apache-zookeeper-3.9.4/0040755 0000000 0000000 00000000000 15051152474 015317 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/.github/0040755 0000000 0000000 00000000000 15051152474 016657 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/.github/workflows/0040755 0000000 0000000 00000000000 15051152474 020714 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/bin/0040755 0000000 0000000 00000000000 15051152474 016067 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/conf/0040755 0000000 0000000 00000000000 15051152474 016244 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/dev/0040755 0000000 0000000 00000000000 15051152474 016075 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/dev/docker/0040755 0000000 0000000 00000000000 15051152474 017344 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/tools/0040755 0000000 0000000 00000000000 15051152474 016457 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/tools/ci/0040755 0000000 0000000 00000000000 15051152474 017052 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/tools/cmake/0040755 0000000 0000000 00000000000 15051152474 017537 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/tools/cmake/Modules/0040755 0000000 0000000 00000000000 15051152474 021147 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/tools/sonar/0040755 0000000 0000000 00000000000 15051152474 017601 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-assembly/0040755 0000000 0000000 00000000000 15051152474 021137 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-assembly/src/0040755 0000000 0000000 00000000000 15051152474 021726 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-assembly/src/main/0040755 0000000 0000000 00000000000 15051152474 022652 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-assembly/src/main/assembly/0040755 0000000 0000000 00000000000 15051152474 024471 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/0040755 0000000 0000000 00000000000 15051152474 020576 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/0040755 0000000 0000000 00000000000 15051152474 024275 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/0040755 0000000 0000000 00000000000 15051152474 025720 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/0040755 0000000 0000000 00000000000 15051152474 025064 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/0040755 0000000 0000000 00000000000 15051152474 027017 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/ssl/0040755 0000000 0000000 00000000000 15051152474 025076 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/0040755 0000000 0000000 00000000000 15051152474 025437 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-compatibility-tests/0040755 0000000 0000000 00000000000 15051152474 023331 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/0040755 0000000 0000000 00000000000 15051152474 033020 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/0040755 0000000 0000000 00000000000 15051152474 033607 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000156 15051152474 032534 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/ apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0040755 0000000 0000000 00000000000 15051152474 034566 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000163 15051152474 032532 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/ apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0040755 0000000 0000000 00000000000 15051152474 034566 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000167 15051152474 032536 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/ apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0040755 0000000 0000000 00000000000 15051152474 034566 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000176 15051152474 032536 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/ apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0040755 0000000 0000000 00000000000 15051152474 034566 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000210 15051152474 032523 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0040755 0000000 0000000 00000000000 15051152474 034566 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000226 15051152474 032532 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/zookeeper/compatibility/ apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0040755 0000000 0000000 00000000000 15051152474 034566 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/0040755 0000000 0000000 00000000000 15051152474 020760 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/0040755 0000000 0000000 00000000000 15051152474 025666 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/0040755 0000000 0000000 00000000000 15051152474 026455 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/0040755 0000000 0000000 00000000000 15051152474 027401 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/0040755 0000000 0000000 00000000000 15051152474 030322 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 031111 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032332 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-fatjar_src_main_java_org_0100644 0000000 0000000 00000000157 15051152474 032522 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/zookeeper0040755 0000000 0000000 00000000000 15051152474 034256 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-fatjar_src_main_java_org_0100644 0000000 0000000 00000000164 15051152474 032520 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/zookeeper/util/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/zookeeper0040755 0000000 0000000 00000000000 15051152474 034256 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/resources/0040755 0000000 0000000 00000000000 15051152474 031413 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/0040755 0000000 0000000 00000000000 15051152474 026604 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/0040755 0000000 0000000 00000000000 15051152474 027566 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/0040755 0000000 0000000 00000000000 15051152474 030355 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/0040755 0000000 0000000 00000000000 15051152474 031337 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/0040755 0000000 0000000 00000000000 15051152474 032626 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/0040755 0000000 0000000 00000000000 15051152474 033414 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/0040755 0000000 0000000 00000000000 15051152474 033416 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/0040755 0000000 0000000 00000000000 15051152474 033556 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/0040755 0000000 0000000 00000000000 15051152474 033242 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000157 15051152474 032670 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source0040755 0000000 0000000 00000000000 15051152474 034423 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000164 15051152474 032666 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source0040755 0000000 0000000 00000000000 15051152474 034423 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/0040755 0000000 0000000 00000000000 15051152474 033335 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/0040755 0000000 0000000 00000000000 15051152474 026222 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/0040755 0000000 0000000 00000000000 15051152474 027011 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/0040755 0000000 0000000 00000000000 15051152474 027735 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/0040755 0000000 0000000 00000000000 15051152474 030656 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 031445 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032666 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000161 15051152474 032543 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034263 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000167 15051152474 032551 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034263 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000201 15051152474 032536 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034263 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000200 15051152474 032535 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034263 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/0040755 0000000 0000000 00000000000 15051152474 031747 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/0040755 0000000 0000000 00000000000 15051152474 033225 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/0040755 0000000 0000000 00000000000 15051152474 034014 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000163 15051152474 032606 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0040755 0000000 0000000 00000000000 15051152474 034335 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000175 15051152474 032611 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0040755 0000000 0000000 00000000000 15051152474 034335 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000203 15051152474 032601 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0040755 0000000 0000000 00000000000 15051152474 034335 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000215 15051152474 032604 xustar000000000 0000000 141 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0040755 0000000 0000000 00000000000 15051152474 034335 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/0040755 0000000 0000000 00000000000 15051152474 027770 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/0040755 0000000 0000000 00000000000 15051152474 030711 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 031500 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032721 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_test_java_or0100644 0000000 0000000 00000000161 15051152474 032576 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034316 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_test_java_or0100644 0000000 0000000 00000000167 15051152474 032604 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeeper/graph/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034316 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_test_java_or0100644 0000000 0000000 00000000200 15051152474 032570 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeeper/graph/servlets/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034316 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/0040755 0000000 0000000 00000000000 15051152474 026604 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/cacti/0040755 0000000 0000000 00000000000 15051152474 027667 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/0040755 0000000 0000000 00000000000 15051152474 030206 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/0040755 0000000 0000000 00000000000 15051152474 030064 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/0040755 0000000 0000000 00000000000 15051152474 025374 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/0040755 0000000 0000000 00000000000 15051152474 026321 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/0040755 0000000 0000000 00000000000 15051152474 027274 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/0040755 0000000 0000000 00000000000 15051152474 026163 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/0040755 0000000 0000000 00000000000 15051152474 027107 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/0040755 0000000 0000000 00000000000 15051152474 030030 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 030617 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032040 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034043 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000164 15051152474 032547 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034226 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000173 15051152474 032547 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034226 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000177 15051152474 032553 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034226 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000203 15051152474 032541 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/filters/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034226 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000200 15051152474 032536 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034226 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000205 15051152474 032543 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034226 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/0040755 0000000 0000000 00000000000 15051152474 027504 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/0040755 0000000 0000000 00000000000 15051152474 027142 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/0040755 0000000 0000000 00000000000 15051152474 030063 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 030652 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032073 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034076 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000164 15051152474 032602 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034261 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000173 15051152474 032602 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0040755 0000000 0000000 00000000000 15051152474 034261 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/0040755 0000000 0000000 00000000000 15051152474 025726 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/0040755 0000000 0000000 00000000000 15051152474 026515 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/0040755 0000000 0000000 00000000000 15051152474 025726 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/build/0040755 0000000 0000000 00000000000 15051152474 027025 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/0040755 0000000 0000000 00000000000 15051152474 026171 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/0040755 0000000 0000000 00000000000 15051152474 026305 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/0040755 0000000 0000000 00000000000 15051152474 027074 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/0040755 0000000 0000000 00000000000 15051152474 027316 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/examples/0040755 0000000 0000000 00000000000 15051152474 030712 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/0040755 0000000 0000000 00000000000 15051152474 030415 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/0040755 0000000 0000000 00000000000 15051152474 030053 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/0040755 0000000 0000000 00000000000 15051152474 026621 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/0040755 0000000 0000000 00000000000 15051152474 027410 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/tests/0040755 0000000 0000000 00000000000 15051152474 027763 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/0040755 0000000 0000000 00000000000 15051152474 027155 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/licences/0040755 0000000 0000000 00000000000 15051152474 030742 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/0040755 0000000 0000000 00000000000 15051152474 027744 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/0040755 0000000 0000000 00000000000 15051152474 030670 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/0040755 0000000 0000000 00000000000 15051152474 031611 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/0040755 0000000 0000000 00000000000 15051152474 032367 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/0040755 0000000 0000000 00000000000 15051152474 033655 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000161 15051152474 032635 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/uti0040755 0000000 0000000 00000000000 15051152474 034377 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000171 15051152474 032636 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/uti0040755 0000000 0000000 00000000000 15051152474 034377 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 032400 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 033621 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000165 15051152474 032641 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000177 15051152474 032644 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000212 15051152474 032632 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000203 15051152474 032632 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000216 15051152474 032636 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000206 15051152474 032635 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/logger/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000207 15051152474 032636 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000173 15051152474 032640 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/retry/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034351 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/0040755 0000000 0000000 00000000000 15051152474 032702 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/0040755 0000000 0000000 00000000000 15051152474 034015 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000161 15051152474 032646 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000167 15051152474 032654 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000177 15051152474 032655 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000202 15051152474 032642 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/categories/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000201 15051152474 032641 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/mimetypes/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/places/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/status/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000167 15051152474 032654 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000177 15051152474 032655 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000202 15051152474 032642 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/categories/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000201 15051152474 032641 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/mimetypes/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/places/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/status/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000167 15051152474 032654 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000177 15051152474 032655 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/actions/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000202 15051152474 032642 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/categories/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000201 15051152474 032641 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/mimetypes/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/places/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/status/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000167 15051152474 032654 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000177 15051152474 032655 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000202 15051152474 032642 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/categories/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000201 15051152474 032641 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/mimetypes/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/places/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000176 15051152474 032654 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/status/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0040755 0000000 0000000 00000000000 15051152474 034460 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/0040755 0000000 0000000 00000000000 15051152474 030723 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/0040755 0000000 0000000 00000000000 15051152474 031644 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 032433 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 033654 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_test_jav0100644 0000000 0000000 00000000165 15051152474 032674 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034404 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_test_jav0100644 0000000 0000000 00000000177 15051152474 032677 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034404 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_test_jav0100644 0000000 0000000 00000000207 15051152474 032671 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/manager/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zoo0040755 0000000 0000000 00000000000 15051152474 034404 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/0040755 0000000 0000000 00000000000 15051152474 020250 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/0040755 0000000 0000000 00000000000 15051152474 021037 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/main/0040755 0000000 0000000 00000000000 15051152474 021763 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/0040755 0000000 0000000 00000000000 15051152474 023775 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/0040755 0000000 0000000 00000000000 15051152474 025617 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/html/0040755 0000000 0000000 00000000000 15051152474 026563 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/0040755 0000000 0000000 00000000000 15051152474 027064 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/0040755 0000000 0000000 00000000000 15051152474 026563 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/0040755 0000000 0000000 00000000000 15051152474 017734 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/0040755 0000000 0000000 00000000000 15051152474 020523 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/0040755 0000000 0000000 00000000000 15051152474 021447 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/0040755 0000000 0000000 00000000000 15051152474 022370 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 023157 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 024400 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 026403 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/server/0040755 0000000 0000000 00000000000 15051152474 027711 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/server/watch/0040755 0000000 0000000 00000000000 15051152474 031017 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/0040755 0000000 0000000 00000000000 15051152474 027362 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/0040755 0000000 0000000 00000000000 15051152474 030706 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/0040755 0000000 0000000 00000000000 15051152474 020267 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/0040755 0000000 0000000 00000000000 15051152474 021056 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/0040755 0000000 0000000 00000000000 15051152474 022002 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/0040755 0000000 0000000 00000000000 15051152474 022723 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 023512 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 024733 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/0040755 0000000 0000000 00000000000 15051152474 025702 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/0040755 0000000 0000000 00000000000 15051152474 027514 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/generated/0040755 0000000 0000000 00000000000 15051152474 031452 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/main/resources/0040755 0000000 0000000 00000000000 15051152474 024014 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/test/0040755 0000000 0000000 00000000000 15051152474 022035 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/0040755 0000000 0000000 00000000000 15051152474 022756 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 023545 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 024766 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/0040755 0000000 0000000 00000000000 15051152474 025735 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/0040755 0000000 0000000 00000000000 15051152474 023001 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/0040755 0000000 0000000 00000000000 15051152474 030641 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/0040755 0000000 0000000 00000000000 15051152474 031430 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/0040755 0000000 0000000 00000000000 15051152474 032354 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/0040755 0000000 0000000 00000000000 15051152474 033275 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 034064 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000163 15051152474 032616 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034405 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000175 15051152474 032621 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034405 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000205 15051152474 032613 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034405 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000220 15051152474 032610 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034405 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/0040755 0000000 0000000 00000000000 15051152474 032407 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/0040755 0000000 0000000 00000000000 15051152474 033330 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 034117 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000163 15051152474 032616 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034440 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000175 15051152474 032621 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034440 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000205 15051152474 032613 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034440 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000220 15051152474 032610 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/ apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0040755 0000000 0000000 00000000000 15051152474 034440 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/resources/0040755 0000000 0000000 00000000000 15051152474 034421 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/0040755 0000000 0000000 00000000000 15051152474 020752 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/0040755 0000000 0000000 00000000000 15051152474 026205 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/0040755 0000000 0000000 00000000000 15051152474 026774 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/0040755 0000000 0000000 00000000000 15051152474 027720 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/0040755 0000000 0000000 00000000000 15051152474 030641 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 031430 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032651 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_main_java_or0100644 0000000 0000000 00000000161 15051152474 032526 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034246 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_main_java_or0100644 0000000 0000000 00000000171 15051152474 032527 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeeper/recipes/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034246 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_main_java_or0100644 0000000 0000000 00000000200 15051152474 032520 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeeper/recipes/leader/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034246 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/0040755 0000000 0000000 00000000000 15051152474 027753 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/0040755 0000000 0000000 00000000000 15051152474 030674 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 031463 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032704 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_test_java_or0100644 0000000 0000000 00000000161 15051152474 032561 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034301 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_test_java_or0100644 0000000 0000000 00000000171 15051152474 032562 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeeper/recipes/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034301 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_test_java_or0100644 0000000 0000000 00000000200 15051152474 032553 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeeper/recipes/leader/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeep0040755 0000000 0000000 00000000000 15051152474 034301 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/0040755 0000000 0000000 00000000000 15051152474 025333 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/0040755 0000000 0000000 00000000000 15051152474 026122 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/0040755 0000000 0000000 00000000000 15051152474 027046 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/0040755 0000000 0000000 00000000000 15051152474 027270 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/include/0040755 0000000 0000000 00000000000 15051152474 030713 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/src/0040755 0000000 0000000 00000000000 15051152474 030057 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/tests/0040755 0000000 0000000 00000000000 15051152474 030432 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/0040755 0000000 0000000 00000000000 15051152474 027767 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 030556 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 031777 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034002 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000165 15051152474 032507 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0040755 0000000 0000000 00000000000 15051152474 034164 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000172 15051152474 032505 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/lock/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0040755 0000000 0000000 00000000000 15051152474 034164 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/0040755 0000000 0000000 00000000000 15051152474 027101 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/0040755 0000000 0000000 00000000000 15051152474 030022 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 030611 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032032 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034035 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_test_java_org_ap0100644 0000000 0000000 00000000165 15051152474 032542 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/recipes/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/r0040755 0000000 0000000 00000000000 15051152474 034217 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_test_java_org_ap0100644 0000000 0000000 00000000172 15051152474 032540 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/recipes/lock/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/r0040755 0000000 0000000 00000000000 15051152474 034217 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/0040755 0000000 0000000 00000000000 15051152474 025527 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/0040755 0000000 0000000 00000000000 15051152474 026316 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/0040755 0000000 0000000 00000000000 15051152474 027242 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/0040755 0000000 0000000 00000000000 15051152474 027464 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/include/0040755 0000000 0000000 00000000000 15051152474 031107 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/src/0040755 0000000 0000000 00000000000 15051152474 030253 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/tests/0040755 0000000 0000000 00000000000 15051152474 030626 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/0040755 0000000 0000000 00000000000 15051152474 030163 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 030752 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032173 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_main_java_org_a0100644 0000000 0000000 00000000156 15051152474 032523 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034176 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_main_java_org_a0100644 0000000 0000000 00000000166 15051152474 032524 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/recipes/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034176 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_main_java_org_a0100644 0000000 0000000 00000000174 15051152474 032523 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/recipes/queue/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034176 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/0040755 0000000 0000000 00000000000 15051152474 027275 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/0040755 0000000 0000000 00000000000 15051152474 030216 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 031005 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 032226 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_test_java_org_a0100644 0000000 0000000 00000000156 15051152474 032556 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034231 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_test_java_org_a0100644 0000000 0000000 00000000166 15051152474 032557 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/recipes/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034231 5ustar00rootroot0000000 0000000 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_test_java_org_a0100644 0000000 0000000 00000000174 15051152474 032556 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/recipes/queue/ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 034231 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/0040755 0000000 0000000 00000000000 15051152474 020626 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/0040755 0000000 0000000 00000000000 15051152474 021415 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/0040755 0000000 0000000 00000000000 15051152474 022341 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/0040755 0000000 0000000 00000000000 15051152474 023262 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/0040755 0000000 0000000 00000000000 15051152474 024051 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 025272 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 027275 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/admin/0040755 0000000 0000000 00000000000 15051152474 030365 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/0040755 0000000 0000000 00000000000 15051152474 030403 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/0040755 0000000 0000000 00000000000 15051152474 030044 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/0040755 0000000 0000000 00000000000 15051152474 030553 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/0040755 0000000 0000000 00000000000 15051152474 030565 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/compat/0040755 0000000 0000000 00000000000 15051152474 030560 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/jmx/0040755 0000000 0000000 00000000000 15051152474 030073 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/0040755 0000000 0000000 00000000000 15051152474 030743 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/0040755 0000000 0000000 00000000000 15051152474 031704 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/0040755 0000000 0000000 00000000000 15051152474 030603 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/0040755 0000000 0000000 00000000000 15051152474 031673 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/0040755 0000000 0000000 00000000000 15051152474 031544 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/0040755 0000000 0000000 00000000000 15051152474 032221 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/0040755 0000000 0000000 00000000000 15051152474 032766 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/0040755 0000000 0000000 00000000000 15051152474 032334 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/0040755 0000000 0000000 00000000000 15051152474 032066 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/0040755 0000000 0000000 00000000000 15051152474 033127 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/0040755 0000000 0000000 00000000000 15051152474 032133 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/0040755 0000000 0000000 00000000000 15051152474 033074 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/0040755 0000000 0000000 00000000000 15051152474 033725 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/0040755 0000000 0000000 00000000000 15051152474 031560 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/0040755 0000000 0000000 00000000000 15051152474 031711 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/util/0040755 0000000 0000000 00000000000 15051152474 030252 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/0040755 0000000 0000000 00000000000 15051152474 025056 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/0040755 0000000 0000000 00000000000 15051152474 025645 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/0040755 0000000 0000000 00000000000 15051152474 027066 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 031071 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/zookeeper/version/0040755 0000000 0000000 00000000000 15051152474 032556 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/0040755 0000000 0000000 00000000000 15051152474 024353 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/0040755 0000000 0000000 00000000000 15051152474 025121 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/0040755 0000000 0000000 00000000000 15051152474 022374 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/0040755 0000000 0000000 00000000000 15051152474 023315 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/0040755 0000000 0000000 00000000000 15051152474 024104 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/0040755 0000000 0000000 00000000000 15051152474 025325 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/0040755 0000000 0000000 00000000000 15051152474 027330 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/0040755 0000000 0000000 00000000000 15051152474 030436 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/cli/0040755 0000000 0000000 00000000000 15051152474 030077 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/client/0040755 0000000 0000000 00000000000 15051152474 030606 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/0040755 0000000 0000000 00000000000 15051152474 030620 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/compat/0040755 0000000 0000000 00000000000 15051152474 030613 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/metrics/0040755 0000000 0000000 00000000000 15051152474 030776 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/0040755 0000000 0000000 00000000000 15051152474 030636 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/0040755 0000000 0000000 00000000000 15051152474 031726 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/0040755 0000000 0000000 00000000000 15051152474 031577 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/0040755 0000000 0000000 00000000000 15051152474 033021 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/0040755 0000000 0000000 00000000000 15051152474 032367 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/0040755 0000000 0000000 00000000000 15051152474 032121 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/0040755 0000000 0000000 00000000000 15051152474 033162 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/0040755 0000000 0000000 00000000000 15051152474 032166 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/0040755 0000000 0000000 00000000000 15051152474 033127 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/0040755 0000000 0000000 00000000000 15051152474 031613 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/0040755 0000000 0000000 00000000000 15051152474 031744 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/0040755 0000000 0000000 00000000000 15051152474 030307 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/util/0040755 0000000 0000000 00000000000 15051152474 030305 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/0040755 0000000 0000000 00000000000 15051152474 024406 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/0040755 0000000 0000000 00000000000 15051152474 025317 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/0040755 0000000 0000000 00000000000 15051152474 027463 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/create/0040755 0000000 0000000 00000000000 15051152474 030726 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/create/version-2/0040755 0000000 0000000 00000000000 15051152474 032552 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/set/0040755 0000000 0000000 00000000000 15051152474 030256 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/set/version-2/0040755 0000000 0000000 00000000000 15051152474 032102 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/0040755 0000000 0000000 00000000000 15051152474 031322 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/version-2/0040755 0000000 0000000 00000000000 15051152474 033146 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/0040755 0000000 0000000 00000000000 15051152474 027627 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/0040755 0000000 0000000 00000000000 15051152474 031453 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/kerberos/0040755 0000000 0000000 00000000000 15051152474 027133 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/ssl/0040755 0000000 0000000 00000000000 15051152474 026120 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/upgrade/0040755 0000000 0000000 00000000000 15051152474 026746 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/embedded/0040755 0000000 0000000 00000000000 15051152474 026137 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-specifications/0040755 0000000 0000000 00000000000 15051152474 022323 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/0040755 0000000 0000000 00000000000 15051152474 025114 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/pic/0040755 0000000 0000000 00000000000 15051152474 025667 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-specifications/system-spec/0040755 0000000 0000000 00000000000 15051152474 024577 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-specifications/system-spec/zk-3.7/0040755 0000000 0000000 00000000000 15051152474 025530 5ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/.asf.yaml0100644 0000000 0000000 00000003035 15051152474 017030 0ustar00rootroot0000000 0000000 # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # # https://cwiki.apache.org/confluence/display/INFRA/git+-+.asf.yaml+features github: description: "Apache ZooKeeper" homepage: https://zookeeper.apache.org labels: - java - apache - zookeeper - consensus - coordination - zab - database - key-value - distributed-database - distributed-systems - service-discovery - configuration-management - distributed-configuration - hacktoberfest features: wiki: false issues: false projects: false enabled_merge_buttons: squash: true merge: false rebase: false notifications: jira_options: link label worklog commits: commits@zookeeper.apache.org issues: issues@zookeeper.apache.org pullrequests: notifications@zookeeper.apache.org apache-zookeeper-3.9.4/.gitattributes0100644 0000000 0000000 00000000743 15051152474 020213 0ustar00rootroot0000000 0000000 # Auto detect text files and perform LF normalization * text=auto *.cs text diff=csharp *.java text diff=java *.html text diff=html *.py text diff=python *.pl text diff=perl *.pm text diff=perl *.css text *.js text *.sql text *.sh text eol=lf *.bat text eol=crlf *.cmd text eol=crlf *.csproj text merge=union eol=crlf *.sln text merge=union eol=crlf *.vcxproj text merge=union eol=crlf *.vcxproj.filters text merge=union eol=crlf apache-zookeeper-3.9.4/.github/workflows/ci.yaml0100644 0000000 0000000 00000006174 15051152474 022200 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT 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 workflow will build a Java project with Maven # See also: # https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: CI on: push: branches: [ '*' ] pull_request: branches: [ '*' ] jobs: mvn: strategy: matrix: profile: - name: 'full-build-jdk8' jdk: 8 args: '-Pfull-build apache-rat:check verify -DskipTests spotbugs:check checkstyle:check' - name: 'full-build-jdk11' jdk: 11 args: '-Pfull-build apache-rat:check verify -DskipTests spotbugs:check checkstyle:check' - name: 'full-build-java-tests' jdk: 11 args: '-Pfull-build verify -Dsurefire-forkcount=1 -DskipCppUnit -Dsurefire.rerunFailingTestsCount=5' - name: 'full-build-cppunit-tests' jdk: 11 args: '-Pfull-build verify -Dtest=_ -DfailIfNoTests=false' fail-fast: false timeout-minutes: 360 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.profile.jdk }} uses: actions/setup-java@v4 with: java-version: ${{ matrix.profile.jdk }} distribution: temurin cache: 'maven' - name: Show the first log message run: git log -n1 - name: Install C Dependencies run: | sudo apt update sudo apt install -y libcppunit-dev libsasl2-dev - name: Build with Maven (${{ matrix.profile.name }}) run: mvn -B -V -e -ntp "-Dstyle.color=always" ${{ matrix.profile.args }} env: MAVEN_OPTS: -Djansi.force=true - name: Upload unit test results if: ${{ failure() }} uses: actions/upload-artifact@v4 with: name: surefire-reports-${{ matrix.profile.name }} path: ./**/target/surefire-reports/ if-no-files-found: ignore - name: Upload integration test results if: ${{ failure() }} uses: actions/upload-artifact@v4 with: name: failsafe-reports-${{ matrix.profile.name }} path: ./**/target/failsafe-reports/ if-no-files-found: ignore - name: Upload cppunit test logs if: ${{ failure() }} uses: actions/upload-artifact@v4 with: name: cppunit-logs-${{ matrix.profile.name }} path: ./zookeeper-client/zookeeper-client-c/target/c/TEST-*.txt if-no-files-found: ignore apache-zookeeper-3.9.4/.github/workflows/e2e.yaml0100644 0000000 0000000 00000005737 15051152474 022264 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. name: End to End Tests on: push: pull_request: jobs: compatibility: strategy: matrix: jdk: [8, 11] zk: [3.5.9, 3.6.3, 3.7.0, nightly] fail-fast: false timeout-minutes: 360 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.jdk }} uses: actions/setup-java@v4 with: java-version: ${{ matrix.jdk }} distribution: temurin cache: 'maven' - name: Show the first log message run: git log -n1 - name: Install C Dependencies run: | sudo apt update sudo apt install -y libcppunit-dev libsasl2-dev - name: Build with Maven run: mvn -B -V -e -ntp "-Dstyle.color=always" package -DskipTests env: MAVEN_OPTS: -Djansi.force=true - name: Cache ZooKeeper ${{ matrix.zk }} id: dist-cache if: matrix.zk != 'nightly' uses: actions/cache@v4 with: key: apache-zookeeper-${{ matrix.zk }}-bin.tar.gz path: apache-zookeeper-${{ matrix.zk }}-bin.tar.gz - name: Download ZooKeeper ${{ matrix.zk }} if: matrix.zk != 'nightly' && steps.dist-cache.outputs.cache-hit != 'true' run: | curl -O https://archive.apache.org/dist/zookeeper/zookeeper-${{ matrix.zk }}/apache-zookeeper-${{ matrix.zk }}-bin.tar.gz - name: Extract ZooKeeper ${{ matrix.zk }} if: matrix.zk != 'nightly' run: tar -xzvf apache-zookeeper-${{ matrix.zk }}-bin.tar.gz - name: Test ZooKeeper nightly server and ${{ matrix.zk }} client if: matrix.zk != 'nightly' run: tools/ci/test-connectivity.py --server . --client apache-zookeeper-${{ matrix.zk }}-bin env: ZOOCFG: zoo_sample.cfg - name: Test ZooKeeper ${{ matrix.zk }} server and nightly client if: matrix.zk != 'nightly' run: tools/ci/test-connectivity.py --server apache-zookeeper-${{ matrix.zk }}-bin --client . env: ZOOCFG: zoo_sample.cfg - name: Test ZooKeeper nightly server and client if: matrix.zk == 'nightly' run: tools/ci/test-connectivity.py --server . --client . env: ZOOCFG: zoo_sample.cfg apache-zookeeper-3.9.4/.github/workflows/manual.yaml0100644 0000000 0000000 00000005345 15051152474 023061 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT 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 workflow will build a Java project with Maven # See also: # https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#manual-events name: Manual Build on: workflow_dispatch: inputs: buildRef: description: Ref to build (commit, branch, or refs/pull/1234/head or refs/pull/1234/merge) required: true default: refs/pull/1234/merge mvnOpts: description: Maven options required: true default: --fail-at-end goals: description: Maven goals required: true default: -Pfull-build apache-rat:check verify -DskipTests spotbugs:check checkstyle:check javadoc:jar jobs: mvn: name: mvn (triggered by ${{ github.event.sender.login }}) timeout-minutes: 360 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.inputs.buildRef }} - name: Set up JDK 11 uses: actions/setup-java@v4 with: java-version: 11 distribution: temurin cache: 'maven' - name: Show the first log message run: git log -n1 - name: Install C Dependencies run: | sudo apt update sudo apt install -y libcppunit-dev libsasl2-dev - name: Build with Maven run: mvn -B -V -e -ntp "-Dstyle.color=always" ${{ github.event.inputs.mvnOpts }} ${{ github.event.inputs.goals }} env: MAVEN_OPTS: -Djansi.force=true - name: Upload unit test results if: ${{ failure() }} uses: actions/upload-artifact@v4 with: name: surefire-reports path: ./**/target/surefire-reports/ if-no-files-found: ignore - name: Upload integration test results if: ${{ failure() }} uses: actions/upload-artifact@v4 with: name: failsafe-reports path: ./**/target/failsafe-reports/ if-no-files-found: ignore apache-zookeeper-3.9.4/.gitignore0100644 0000000 0000000 00000003726 15051152474 017314 0ustar00rootroot0000000 0000000 # Java hs_err_pid* zookeeper-server/version-2/* # Git *.orij *.rej # SVN .svn .revision # Eclipse .metadata .classpath .eclipse/ .idea/ .project .revision/ .settings/ .externalToolBuilders/ local.properties .recommenders *.launch build/ target/ out/ # Intellij *.ipr *.iws *.iml # NetBeans *~.nib nbbuild/ nbproject/ dist/ nbdist/ .ndb-gradle/ .cproject .buildpath nb-configuration.xml nbactions.xml # vscode classes .vscode # Other *~ logs/ *.out *.log *.bak *.tmp tmp/** tmp/**/* .DS_Store .gradle *.patch *.swp .ant-targets-build.xml # C tags .cproject .project obj zookeeper-server/src/main/resources/lib/ant-eclipse-* zookeeper-server/src/main/resources/lib/ivy-* zookeeper-client/zookeeper-client-c/Makefile.in zookeeper-client/zookeeper-client-c/aclocal.m4 zookeeper-client/zookeeper-client-c/autom4te.cache/ zookeeper-client/zookeeper-client-c/compile zookeeper-client/zookeeper-client-c/config.guess zookeeper-client/zookeeper-client-c/config.h.in zookeeper-client/zookeeper-client-c/config.sub zookeeper-client/zookeeper-client-c/configure zookeeper-client/zookeeper-client-c/depcomp zookeeper-client/zookeeper-client-c/install-sh zookeeper-client/zookeeper-client-c/ltmain.sh zookeeper-client/zookeeper-client-c/missing zookeeper-client/zookeeper-client-c/.deps/ zookeeper-client/zookeeper-client-c/.libs/ zookeeper-client/zookeeper-client-c/Makefile zookeeper-client/zookeeper-client-c/cli_mt zookeeper-client/zookeeper-client-c/cli_st zookeeper-client/zookeeper-client-c/config.h zookeeper-client/zookeeper-client-c/config.status zookeeper-client/zookeeper-client-c/libtool zookeeper-client/zookeeper-client-c/load_gen zookeeper-client/zookeeper-client-c/stamp-h1 zookeeper-client/zookeeper-client-c/build zookeeper-client/zookeeper-client-c/core.* zookeeper-client/zookeeper-client-c/TEST-*.txt zookeeper-client/zookeeper-client-c/*.la zookeeper-client/zookeeper-client-c/*.lo zookeeper-client/zookeeper-client-c/*.o zookeeper-client/zookeeper-client-c/generated/ # Python *.py[cod] apache-zookeeper-3.9.4/Jenkinsfile0100644 0000000 0000000 00000005131 15051152474 017500 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pipeline { agent { label 'Hadoop' } options { disableConcurrentBuilds() buildDiscarder(logRotator(daysToKeepStr: '14')) timeout(time: 2, unit: 'HOURS') timestamps() } triggers { cron('@daily') } stages { stage('Prepare') { matrix { agent any axes { axis { name 'JAVA_VERSION' values 'jdk_1.8_latest', 'jdk_11_latest' } } tools { maven "maven_latest" jdk "${JAVA_VERSION}" } stages { stage('BuildAndTest') { steps { sh "git clean -fxd" sh "mvn verify spotbugs:check checkstyle:check -Pfull-build -Dsurefire-forkcount=4" } post { always { junit '**/target/surefire-reports/TEST-*.xml' archiveArtifacts '**/target/*.jar' } // Jenkins pipeline jobs fill slaves on PRs without this :( cleanup() { script { sh label: 'Cleanup workspace', script: ''' # See HADOOP-13951 chmod -R u+rxw "${WORKSPACE}" ''' deleteDir() } } } } } } } } } apache-zookeeper-3.9.4/Jenkinsfile-PreCommit0100644 0000000 0000000 00000003452 15051152474 021401 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pipeline { agent { label 'Hadoop' } options { disableConcurrentBuilds() buildDiscarder(logRotator(daysToKeepStr: '14')) timeout(time: 2, unit: 'HOURS') timestamps() } tools { maven "maven_latest" jdk "jdk_1.8_latest" } stages { stage('BuildAndTest') { steps { sh "git clean -fxd" sh "mvn verify spotbugs:check checkstyle:check -Pfull-build -Dsurefire-forkcount=4" } post { always { junit '**/target/surefire-reports/TEST-*.xml' } } } } post { // Jenkins pipeline jobs fill slaves on PRs without this :( cleanup() { script { sh label: 'Cleanup workspace', script: ''' # See HADOOP-13951 chmod -R u+rxw "${WORKSPACE}" ''' deleteDir() } } } } apache-zookeeper-3.9.4/Jenkinsfile-owasp0100644 0000000 0000000 00000003357 15051152474 020637 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pipeline { agent { label 'Hadoop' } options { buildDiscarder(logRotator(daysToKeepStr: '14')) timeout(time: 2, unit: 'HOURS') timestamps() } tools { maven "maven_latest" jdk "jdk_11_latest" } stages { stage('BuildAndTest') { steps { sh "git clean -fxd" sh "mvn clean package -DskipTests dependency-check:check" } post { always { archiveArtifacts '**/target/dependency-check-*' } } } } post { // Jenkins pipeline jobs fill slaves on PRs without this :( cleanup() { script { sh label: 'Cleanup workspace', script: ''' # See HADOOP-13951 chmod -R u+rxw "${WORKSPACE}" ''' deleteDir() } } } } apache-zookeeper-3.9.4/Jenkinsfile-s390x0100644 0000000 0000000 00000004445 15051152474 020373 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ pipeline { agent { label 's390x' } options { disableConcurrentBuilds() buildDiscarder(logRotator(daysToKeepStr: '14')) timeout(time: 2, unit: 'HOURS') timestamps() } triggers { cron('@daily') } stages { stage('Prepare') { agent { label 's390x' } tools { maven "maven_latest" jdk "jdk_11_latest" } stages { stage('BuildAndTest') { steps { sh "git clean -fxd" sh "mvn verify spotbugs:check checkstyle:check -Pfull-build -Dsurefire-forkcount=4" } post { always { junit '**/target/surefire-reports/TEST-*.xml' archiveArtifacts '**/target/*.jar' } // Jenkins pipeline jobs fill slaves on PRs without this :( cleanup() { script { sh label: 'Cleanup workspace', script: ''' # See HADOOP-13951 chmod -R u+rxw "${WORKSPACE}" ''' deleteDir() } } } } } } } } apache-zookeeper-3.9.4/LICENSE.txt0100644 0000000 0000000 00000026136 15051152474 017147 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/NOTICE.txt0100644 0000000 0000000 00000004044 15051152474 017040 0ustar00rootroot0000000 0000000 Apache ZooKeeper Copyright 2009-2025 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). This product includes software components originally developed for Airlift (https://github.com/airlift/airlift), licensed under the Apache 2.0 license. The licensing terms for Airlift code can be found at: https://github.com/airlift/airlift/blob/master/LICENSE This project also includes some files with the following licenses. These BSD licensed files: ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.h ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_itr.c ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_itr.h ./zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_private.h ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/yui-min.js ./zookeeper-docs/src/main/resources/markdown/skin/prototype.js These MIT licensed files: ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/date.format.js ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.bar.js ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.dot.js ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.line.js ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.pie.js ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.raphael.js ./zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/raphael.js This Apache 2.0 licensed file: ./zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java apache-zookeeper-3.9.4/README.md0100644 0000000 0000000 00000004437 15051152474 016603 0ustar00rootroot0000000 0000000 # Apache ZooKeeper [![GitHub Actions CI][ciBadge]][ciLink] [![Travis CI][trBadge]][trLink] [![Maven Central][mcBadge]][mcLink] [![License][liBadge]][liLink]

https://zookeeper.apache.org/

For the latest information about Apache ZooKeeper, please visit our website at: https://zookeeper.apache.org and our wiki, at: https://cwiki.apache.org/confluence/display/ZOOKEEPER ## Packaging/release artifacts Either downloaded from https://zookeeper.apache.org/releases.html or found in zookeeper-assembly/target directory after building the project with maven. apache-zookeeper-[version].tar.gz Contains all the source files which can be built by running: mvn clean install To generate an aggregated apidocs for zookeeper-server and zookeeper-jute: mvn javadoc:aggregate (generated files will be at target/site/apidocs) apache-zookeeper-[version]-bin.tar.gz Contains all the jar files required to run ZooKeeper Full documentation can also be found in the docs folder As of version 3.5.5, the parent, zookeeper and zookeeper-jute artifacts are deployed to the central repository after the release is voted on and approved by the Apache ZooKeeper PMC: https://repo1.maven.org/maven2/org/apache/zookeeper/zookeeper ## Java 8 If you are going to compile with Java 1.8, you should use a recent release at u211 or above. # Contributing We always welcome new contributors to the project! See [How to Contribute](https://cwiki.apache.org/confluence/display/ZOOKEEPER/HowToContribute) for details on how to submit patches as pull requests and other aspects of our contribution workflow. [ciBadge]: https://github.com/apache/zookeeper/workflows/CI/badge.svg [ciLink]: https://github.com/apache/zookeeper/actions [liBadge]: https://img.shields.io/github/license/apache/zookeeper?color=282661 [liLink]: https://github.com/apache/zookeeper/blob/master/LICENSE.txt [mcBadge]: https://img.shields.io/maven-central/v/org.apache.zookeeper/zookeeper [mcLink]: https://zookeeper.apache.org/releases [trBadge]: https://travis-ci.org/apache/zookeeper.svg?branch=master [trLink]: https://travis-ci.org/apache/zookeeper apache-zookeeper-3.9.4/README_packaging.md0100644 0000000 0000000 00000006762 15051152474 020612 0ustar00rootroot0000000 0000000 # README file for Packaging Notes The ZooKeeper project publishes releases as tarballs. For ZooKeeper packages specific to your OS (such as rpm and deb), consider using Apache Bigtop: http://bigtop.apache.org/ ## Requirements - you need maven to build the java code - gcc, cppunit, openssl and python-setuptools are required to build C and python bindings (only needed when using `-Pfull-build`). Cyrus SASL is optional, but recommended for a maximally functional client. On RHEL machine: ``` yum install cppunit yum install python-setuptools yum install openssl openssl-devel yum install cyrus-sasl-md5 cyrus-sasl-gssapi cyrus-sasl-devel ``` On Ubuntu (in case of 16.4+): ``` apt-get install libcppunit-dev apt-get install python-setuptools python2.7-dev apt-get install openssl libssl-dev apt-get install libsasl2-modules-gssapi-mit libsasl2-modules libsasl2-dev ``` ## Package build command (using maven) Commands to clean everything and build the tarball package without executing the tests: `mvn clean install -DskipTests` `zookeeper-assembly/target/apache-zookeeper--bin.tar.gz` tarball file structure layout: - `/bin` - User executables - `/conf` - Configuration files - `/lib` - ZooKeeper JAR files and all the required java library dependencies - `/docs` - Documents Beside the binary tarball, you can find the whole original source project packaged into: `zookeeper-assembly/target/apache-zookeeper-.tar.gz` ### Building the C client (using maven) To also build the C client, you need to activate the `full-build` profile: ``` mvn clean -Pfull-build mvn install -Pfull-build -DskipTests ``` Optional parameters you might consider when using maven: - `-Pfull-build` - activates the full-build profile, causing the C client to be built - `-DskipTests` - this parameter will skip both java and C++ unit test execution during the build - `-Pc-test-coverage` - activates the test coverage calculation during the execution of C client tests - `-Dc-client-openssl` - specify ssl support and openssl library location. Default value: `yes`, resulting in the autodetection of the openssl library. If the openssl library will not be detected, then a warning will be shown and the C client will be compiled without SSL support. Use `-Dc-client-openssl=no` to explicitly disable SSL feature in C client. Or use `-Dc-client-openssl=/path/to/openssl/` if you want to use a non-default / specific openssl library location. - `-Dc-client-sasl` - specify SASL support and Cyrus SASL 1.x library location. Works similarly to the `c-client-openssl` flag above (`yes`, `no`, or path). Please note: if you don't provide the `-Pfull-build` parameter, then the C client will not be built, the C client tests will not be executed and the previous C client builds will no be cleaned up (e.g. with simply using `mvn clean`). The compiled C client can be found here: - `zookeeper-client/zookeeper-client-c/target/c/bin` - User executable - `zookeeper-client/zookeeper-client-c/target/c/lib` - Native libraries - `zookeeper-client/zookeeper-client-c/target/c/include/zookeeper` - Native library headers The same folders gets archived to the `zookeeper-assembly/target/apache-zookeeper--lib.tar.gz` file, assuming you activated the `full-build` maven profile. apache-zookeeper-3.9.4/bin/README.txt0100644 0000000 0000000 00000000350 15051152474 017560 0ustar00rootroot0000000 0000000 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 apache-zookeeper-3.9.4/bin/zkCleanup.sh0100755 0000000 0000000 00000003672 15051152474 020367 0ustar00rootroot0000000 0000000 #!/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 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi ZOODATADIR="$(grep "^[[:space:]]*dataDir=" "$ZOOCFG" | sed -e 's/.*=//')" ZOODATALOGDIR="$(grep "^[[:space:]]*dataLogDir=" "$ZOOCFG" | sed -e 's/.*=//')" ZOO_LOG_FILE=zookeeper-$USER-cleanup-$HOSTNAME.log if [ "x$ZOODATALOGDIR" = "x" ] then "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.PurgeTxnLog "$ZOODATADIR" $* else "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.PurgeTxnLog "$ZOODATALOGDIR" "$ZOODATADIR" $* fi apache-zookeeper-3.9.4/bin/zkCli.cmd0100644 0000000 0000000 00000002133 15051152474 017624 0ustar00rootroot0000000 0000000 @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 ZOO_LOG_FILE=zookeeper-%USERNAME%-cli-%COMPUTERNAME%.log set ZOOMAIN=org.apache.zookeeper.ZooKeeperMain call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%" -cp "%CLASSPATH%" %ZOOMAIN% %* endlocal apache-zookeeper-3.9.4/bin/zkCli.sh0100755 0000000 0000000 00000003050 15051152474 017475 0ustar00rootroot0000000 0000000 #!/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 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi ZOO_LOG_FILE=zookeeper-$USER-cli-$HOSTNAME.log "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ -cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \ org.apache.zookeeper.ZooKeeperMain "$@" apache-zookeeper-3.9.4/bin/zkEnv.cmd0100644 0000000 0000000 00000003422 15051152474 017647 0ustar00rootroot0000000 0000000 @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%..\logs 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 @REM setup java environment variables if not defined JAVA_HOME ( echo Error: JAVA_HOME is not set. goto :eof ) set JAVA_HOME=%JAVA_HOME:"=% if not exist "%JAVA_HOME%"\bin\java.exe ( echo Error: JAVA_HOME is incorrectly set: %JAVA_HOME% echo Expected to find java.exe here: %JAVA_HOME%\bin\java.exe goto :eof ) REM strip off trailing \ from JAVA_HOME or java does not start if "%JAVA_HOME:~-1%" EQU "\" set "JAVA_HOME=%JAVA_HOME:~0,-1%" set JAVA="%JAVA_HOME%"\bin\java apache-zookeeper-3.9.4/bin/zkEnv.sh0100755 0000000 0000000 00000007553 15051152474 017532 0ustar00rootroot0000000 0000000 #!/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 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. # Or you can specify the ZOOCFGDIR using the # '--config' option in the command line. ZOOBINDIR="${ZOOBINDIR:-/usr/bin}" ZOOKEEPER_PREFIX="${ZOOBINDIR}/.." #check to see if the conf dir is given as an optional argument if [ $# -gt 1 ] then if [ "--config" = "$1" ] then shift confdir=$1 shift ZOOCFGDIR=$confdir fi fi if [ "x$ZOOCFGDIR" = "x" ] then if [ -e "${ZOOKEEPER_PREFIX}/conf" ]; then ZOOCFGDIR="$ZOOBINDIR/../conf" else ZOOCFGDIR="$ZOOBINDIR/../etc/zookeeper" fi fi if [ -f "${ZOOCFGDIR}/zookeeper-env.sh" ]; then . "${ZOOCFGDIR}/zookeeper-env.sh" fi if [ "x$ZOOCFG" = "x" ] then ZOOCFG="zoo.cfg" fi ZOOCFG="$ZOOCFGDIR/$ZOOCFG" if [ -f "$ZOOCFGDIR/java.env" ] then . "$ZOOCFGDIR/java.env" fi if [ "x${ZOO_LOG_DIR}" = "x" ] then ZOO_LOG_DIR="$ZOOKEEPER_PREFIX/logs" fi if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then JAVA="$JAVA_HOME/bin/java" elif type -p java; then JAVA=java else echo "Error: JAVA_HOME is not set and java could not be found in PATH." 1>&2 exit 1 fi #add the zoocfg dir to classpath CLASSPATH="$ZOOCFGDIR:$CLASSPATH" for i in "$ZOOBINDIR"/../zookeeper-server/src/main/resources/lib/*.jar do CLASSPATH="$i:$CLASSPATH" done #make it work in the binary package #(use array for LIBPATH to account for spaces within wildcard expansion) if ls "${ZOOKEEPER_PREFIX}"/share/zookeeper/zookeeper-*.jar > /dev/null 2>&1; then LIBPATH=("${ZOOKEEPER_PREFIX}"/share/zookeeper/*.jar) else #release tarball format for i in "$ZOOBINDIR"/../zookeeper-*.jar do CLASSPATH="$i:$CLASSPATH" done LIBPATH=("${ZOOBINDIR}"/../lib/*.jar) fi for i in "${LIBPATH[@]}" 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 for d in "$ZOOBINDIR"/../zookeeper-server/target/lib/*.jar do CLASSPATH="$d:$CLASSPATH" done #make it work for developers for d in "$ZOOBINDIR"/../zookeeper-metrics-providers/zookeeper-prometheus-metrics/target/lib/*.jar do CLASSPATH="$d:$CLASSPATH" done #make it work for developers CLASSPATH="$ZOOBINDIR/../build/classes:$CLASSPATH" #make it work for developers CLASSPATH="$ZOOBINDIR/../zookeeper-server/target/classes:$CLASSPATH" #make it work for developers CLASSPATH="$ZOOBINDIR/../zookeeper-metrics-providers/zookeeper-prometheus-metrics/target/classes:$CLASSPATH" case "`uname`" in CYGWIN*|MINGW*) cygwin=true ;; *) cygwin=false ;; esac if $cygwin then CLASSPATH=`cygpath -wp "$CLASSPATH"` fi #echo "CLASSPATH=$CLASSPATH" # default heap for zookeeper server ZK_SERVER_HEAP="${ZK_SERVER_HEAP:-1000}" export SERVER_JVMFLAGS="-Xmx${ZK_SERVER_HEAP}m $SERVER_JVMFLAGS" # default heap for zookeeper client ZK_CLIENT_HEAP="${ZK_CLIENT_HEAP:-256}" export CLIENT_JVMFLAGS="-Xmx${ZK_CLIENT_HEAP}m $CLIENT_JVMFLAGS" apache-zookeeper-3.9.4/bin/zkServer-initialize.sh0100755 0000000 0000000 00000010717 15051152474 022403 0ustar00rootroot0000000 0000000 #!/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. # # 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi usage() { # the configfile will be properly formatted as long as the # configfile path is less then 40 chars, otw the line will look a # bit weird, but otherwise it's fine printf "usage: $0 Optional parameters: -h Display this message --help Display this message --configfile=%-40s ZooKeeper config file --myid=# Set the myid to be used, if any (1-255) --force Force creation of the data/txnlog dirs " "$ZOOCFG" exit 1 } if [ $? != 0 ] ; then usage exit 1 fi initialize() { if [ ! -e "$ZOOCFG" ]; then echo "Unable to find config file at $ZOOCFG" exit 1 fi ZOO_DATADIR="$(grep "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" ZOO_DATALOGDIR="$(grep "^[[:space:]]*dataLogDir" "$ZOOCFG" | sed -e 's/.*=//')" if [ -z "$ZOO_DATADIR" ]; then echo "Unable to determine dataDir from $ZOOCFG" exit 1 fi if [ $FORCE ]; then echo "Force enabled, data/txnlog directories will be re-initialized" else # we create if version-2 exists (ie real data), not the # parent. See comments in following section for more insight if [ -d "$ZOO_DATADIR/version-2" ]; then echo "ZooKeeper data directory already exists at $ZOO_DATADIR (or use --force to force re-initialization)" exit 1 fi if [ -n "$ZOO_DATALOGDIR" ] && [ -d "$ZOO_DATALOGDIR/version-2" ]; then echo "ZooKeeper txnlog directory already exists at $ZOO_DATALOGDIR (or use --force to force re-initialization)" exit 1 fi fi # remove the child files that we're (not) interested in, not the # parent. this allows for parent to be installed separately, and # permissions to be set based on overarching requirements. by # default we'll use the permissions of the user running this # script for the files contained by the parent. note also by using # -p the parent(s) will be created if it doesn't already exist rm -rf "$ZOO_DATADIR/myid" 2>/dev/null >/dev/null rm -rf "$ZOO_DATADIR/version-2" 2>/dev/null >/dev/null mkdir -p "$ZOO_DATADIR/version-2" if [ -n "$ZOO_DATALOGDIR" ]; then rm -rf "$ZOO_DATALOGDIR/myid" 2>/dev/null >/dev/null rm -rf "$ZOO_DATALOGDIR/version-2" 2>/dev/null >/dev/null mkdir -p "$ZOO_DATALOGDIR/version-2" fi if [ $MYID ]; then echo "Using myid of $MYID" echo $MYID > "$ZOO_DATADIR/myid" else echo "No myid provided, be sure to specify it in $ZOO_DATADIR/myid if using non-standalone" fi touch "$ZOO_DATADIR/initialize" } while [ ! -z "$1" ]; do case "$1" in --configfile) ZOOCFG=$2; shift 2 ;; --configfile=?*) ZOOCFG=${1#*=}; shift 1 ;; --myid) MYID=$2; shift 2 ;; --myid=?*) MYID=${1#*=}; shift 1 ;; --force) FORCE=1; shift 1 ;; -h) usage ;; --help) usage ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done initialize apache-zookeeper-3.9.4/bin/zkServer.cmd0100644 0000000 0000000 00000002333 15051152474 020365 0ustar00rootroot0000000 0000000 @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 set ZOO_LOG_FILE=zookeeper-%USERNAME%-server-%COMPUTERNAME%.log echo on call %JAVA% "-Dzookeeper.log.dir=%ZOO_LOG_DIR%" "-Dzookeeper.log.file=%ZOO_LOG_FILE%" "-XX:+HeapDumpOnOutOfMemoryError" "-XX:OnOutOfMemoryError=cmd /c taskkill /pid %%%%p /t /f" -cp "%CLASSPATH%" %ZOOMAIN% "%ZOOCFG%" %* endlocal apache-zookeeper-3.9.4/bin/zkServer.sh0100755 0000000 0000000 00000026640 15051152474 020246 0ustar00rootroot0000000 0000000 #!/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. # # 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi # See the following page for extensive details on setting # up the JVM to accept JMX remote management: # http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html # by default we allow local JMX connections if [ "x$JMXLOCALONLY" = "x" ] then JMXLOCALONLY=false fi if [ "x$JMXDISABLE" = "x" ] || [ "$JMXDISABLE" = 'false' ] then echo "ZooKeeper JMX enabled by default" >&2 if [ "x$JMXPORT" = "x" ] then # for some reason these two options are necessary on jdk6 on Ubuntu # accord to the docs they are not necessary, but otw jconsole cannot # do a local attach ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain" else if [ "x$JMXAUTH" = "x" ] then JMXAUTH=false fi if [ "x$JMXSSL" = "x" ] then JMXSSL=false fi if [ "x$JMXLOG4J" = "x" ] then JMXLOG4J=true fi echo "ZooKeeper remote JMX Port set to $JMXPORT" >&2 echo "ZooKeeper remote JMX authenticate set to $JMXAUTH" >&2 echo "ZooKeeper remote JMX ssl set to $JMXSSL" >&2 echo "ZooKeeper remote JMX log4j set to $JMXLOG4J" >&2 if [ "x$JMXHOSTNAME" = "x" ] then ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=$JMXPORT -Dcom.sun.management.jmxremote.authenticate=$JMXAUTH -Dcom.sun.management.jmxremote.ssl=$JMXSSL -Dzookeeper.jmx.log4j.disable=$JMXLOG4J org.apache.zookeeper.server.quorum.QuorumPeerMain" else echo "ZooKeeper remote JMX Hostname set to $JMXHOSTNAME" >&2 ZOOMAIN="-Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=$JMXHOSTNAME -Dcom.sun.management.jmxremote.port=$JMXPORT -Dcom.sun.management.jmxremote.authenticate=$JMXAUTH -Dcom.sun.management.jmxremote.ssl=$JMXSSL -Dzookeeper.jmx.log4j.disable=$JMXLOG4J org.apache.zookeeper.server.quorum.QuorumPeerMain" fi fi else echo "JMX disabled by user request" >&2 ZOOMAIN="org.apache.zookeeper.server.quorum.QuorumPeerMain" fi if [ "x$SERVER_JVMFLAGS" != "x" ] then JVMFLAGS="$SERVER_JVMFLAGS $JVMFLAGS" fi if [ "x$2" != "x" ] then ZOOCFG="$ZOOCFGDIR/$2" fi # if we give a more complicated path to the config, don't screw around in $ZOOCFGDIR if [ "x$(dirname "$ZOOCFG")" != "x$ZOOCFGDIR" ] then ZOOCFG="$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" >&2 case "$OSTYPE" in *solaris*) GREP=/usr/xpg4/bin/grep ;; *) GREP=grep ;; esac ZOO_DATADIR="$($GREP "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//')" ZOO_DATADIR="$(echo -e "${ZOO_DATADIR}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" ZOO_DATALOGDIR="$($GREP "^[[:space:]]*dataLogDir" "$ZOOCFG" | sed -e 's/.*=//')" # iff autocreate is turned off and the datadirs don't exist fail # immediately as we can't create the PID file, etc..., anyway. if [ -n "$ZOO_DATADIR_AUTOCREATE_DISABLE" ]; then if [ ! -d "$ZOO_DATADIR/version-2" ]; then echo "ZooKeeper data directory is missing at $ZOO_DATADIR fix the path or run initialize" exit 1 fi if [ -n "$ZOO_DATALOGDIR" ] && [ ! -d "$ZOO_DATALOGDIR/version-2" ]; then echo "ZooKeeper txnlog directory is missing at $ZOO_DATALOGDIR fix the path or run initialize" exit 1 fi ZOO_DATADIR_AUTOCREATE="-Dzookeeper.datadir.autocreate=false" fi if [ -z "$ZOOPIDFILE" ]; then if [ ! -d "$ZOO_DATADIR" ]; then mkdir -p "$ZOO_DATADIR" fi ZOOPIDFILE="$ZOO_DATADIR/zookeeper_server.pid" else # ensure it exists, otw stop will fail mkdir -p "$(dirname "$ZOOPIDFILE")" fi if [ ! -w "$ZOO_LOG_DIR" ] ; then mkdir -p "$ZOO_LOG_DIR" fi ZOO_LOG_FILE=${ZOO_LOG_FILE:-zookeeper-$USER-server-$HOSTNAME.log} _ZOO_DAEMON_OUT="$ZOO_LOG_DIR/zookeeper-$USER-server-$HOSTNAME.out" case $1 in start) echo -n "Starting zookeeper ... " if [ -f "$ZOOPIDFILE" ]; then if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then echo $command already running as process `cat "$ZOOPIDFILE"`. exit 1 fi fi nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \ "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null & if [ $? -eq 0 ] then case "$OSTYPE" in *solaris*) /bin/echo "${!}\\c" > "$ZOOPIDFILE" ;; aix*) /bin/echo "$!\c" > "$ZOOPIDFILE" ;; *) /bin/echo -n $! > "$ZOOPIDFILE" ;; esac if [ $? -eq 0 ]; then sleep 1 pid=$(cat "${ZOOPIDFILE}") if ps -p "${pid}" > /dev/null 2>&1; then echo STARTED else echo FAILED TO START exit 1 fi else echo FAILED TO WRITE PID exit 1 fi else echo SERVER DID NOT START exit 1 fi ;; start-foreground) ZOO_CMD=(exec "$JAVA") if [ "${ZOO_NOEXEC}" != "" ]; then ZOO_CMD=("$JAVA") fi "${ZOO_CMD[@]}" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \ "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" ;; print-cmd) echo "\"$JAVA\" $ZOO_DATADIR_AUTOCREATE -Dzookeeper.log.dir=\"${ZOO_LOG_DIR}\" \ -Dzookeeper.log.file=\"${ZOO_LOG_FILE}\" \ -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ -cp \"$CLASSPATH\" $JVMFLAGS $ZOOMAIN \"$ZOOCFG\" > \"$_ZOO_DAEMON_OUT\" 2>&1 < /dev/null" ;; stop) echo -n "Stopping zookeeper ... " if [ ! -f "$ZOOPIDFILE" ] then echo "no zookeeper to stop (could not find file $ZOOPIDFILE)" else $KILL $(cat "$ZOOPIDFILE") rm "$ZOOPIDFILE" sleep 1 echo STOPPED fi exit 0 ;; version) ZOOMAIN=org.apache.zookeeper.version.VersionInfoMain $JAVA -cp "$CLASSPATH" $ZOOMAIN 2> /dev/null ;; restart) shift "$0" stop ${@} sleep 3 "$0" start ${@} ;; status) # -q is necessary on some versions of linux where nc returns too quickly, and no stat result is output isSSL="false" clientPortAddress=`$GREP "^[[:space:]]*clientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` if ! [ $clientPortAddress ] then clientPortAddress="localhost" fi clientPort=`$GREP "^[[:space:]]*clientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` if ! [[ "$clientPort" =~ ^[0-9]+$ ]] then dataDir=`$GREP "^[[:space:]]*dataDir" "$ZOOCFG" | sed -e 's/.*=//'` myid=`cat "$dataDir/myid" 2> /dev/null` if ! [[ "$myid" =~ ^[0-9]+$ ]] ; then echo "myid could not be determined, will not able to locate clientPort in the server configs." else clientPortAndAddress=`$GREP "^[[:space:]]*server.$myid=.*;.*" "$ZOOCFG" | sed -e 's/.*=//' | sed -e 's/.*;//'` if [ ! "$clientPortAndAddress" ] ; then echo "Client port not found in static config file. Looking in dynamic config file." dynamicConfigFile=`$GREP "^[[:space:]]*dynamicConfigFile" "$ZOOCFG" | sed -e 's/.*=//'` clientPortAndAddress=`$GREP "^[[:space:]]*server.$myid=.*;.*" "$dynamicConfigFile" | sed -e 's/.*=//' | sed -e 's/.*;//'` fi if [ ! "$clientPortAndAddress" ] ; then echo "Client port not found in the server configs" else if [[ "$clientPortAndAddress" =~ ^.*:[0-9]+ ]] ; then if [[ "$clientPortAndAddress" =~ \[.*\]:[0-9]+ ]] ; then # Extracts address from address:port for example extracts 127::1 from "[127::1]:2181" clientPortAddress=`echo "$clientPortAndAddress" | sed -e 's|\[||' | sed -e 's|\]:.*||'` else clientPortAddress=`echo "$clientPortAndAddress" | sed -e 's/:.*//'` fi fi clientPort=`echo "$clientPortAndAddress" | sed -e 's/.*://'` fi fi fi if [ ! "$clientPort" ] ; then echo "Client port not found. Looking for secureClientPort in the static config." secureClientPort=`$GREP "^[[:space:]]*secureClientPort[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` if [ "$secureClientPort" ] ; then isSSL="true" clientPort=$secureClientPort clientPortAddress=`$GREP "^[[:space:]]*secureClientPortAddress[^[:alpha:]]" "$ZOOCFG" | sed -e 's/.*=//'` if ! [ $clientPortAddress ] then clientPortAddress="localhost" fi else echo "Unable to find either secure or unsecure client port in any configs. Terminating." exit 1 fi fi echo "Client port found: $clientPort. Client address: $clientPortAddress. Client SSL: $isSSL." STAT=`"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.log.file=${ZOO_LOG_FILE}" \ -cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS org.apache.zookeeper.client.FourLetterWordMain \ $clientPortAddress $clientPort srvr $isSSL 2> /dev/null \ | $GREP Mode` if [ "x$STAT" = "x" ] then if [ "$isSSL" = "true" ] ; then echo " " echo "Note: We used secureClientPort ($secureClientPort) to establish connection, but we failed. The 'status'" echo " command establishes a client connection to the server to execute diagnostic commands. Please make sure you" echo " provided all the Client SSL connection related parameters in the CLIENT_JVMFLAGS environment variable! E.g.:" echo " CLIENT_JVMFLAGS=\"-Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty" echo " -Dzookeeper.ssl.trustStore.location=/tmp/clienttrust.jks -Dzookeeper.ssl.trustStore.password=password" echo " -Dzookeeper.ssl.keyStore.location=/tmp/client.jks -Dzookeeper.ssl.keyStore.password=password" echo " -Dzookeeper.client.secure=true\" ./zkServer.sh status" echo " " fi echo "Error contacting service. It is probably not running." exit 1 else echo $STAT exit 0 fi ;; *) echo "Usage: $0 [--config ] {start|start-foreground|stop|version|restart|status|print-cmd}" >&2 esac apache-zookeeper-3.9.4/bin/zkSnapShotToolkit.cmd0100755 0000000 0000000 00000001734 15051152474 022233 0ustar00rootroot0000000 0000000 @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.SnapshotFormatter call %JAVA% -cp "%CLASSPATH%" %ZOOMAIN% %* endlocal apache-zookeeper-3.9.4/bin/zkSnapShotToolkit.sh0100755 0000000 0000000 00000002541 15051152474 022077 0ustar00rootroot0000000 0000000 #!/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. # # 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi "$JAVA" -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.SnapshotFormatter "$@" apache-zookeeper-3.9.4/bin/zkSnapshotComparer.cmd0100755 0000000 0000000 00000001733 15051152474 022415 0ustar00rootroot0000000 0000000 @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.SnapshotComparer call %JAVA% -cp "%CLASSPATH%" %ZOOMAIN% %* endlocal apache-zookeeper-3.9.4/bin/zkSnapshotComparer.sh0100755 0000000 0000000 00000002536 15051152474 022266 0ustar00rootroot0000000 0000000 #!/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. # # 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi "$JAVA" -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.SnapshotComparer "$@" apache-zookeeper-3.9.4/bin/zkSnapshotRecursiveSummaryToolkit.cmd0100755 0000000 0000000 00000001743 15051152474 025541 0ustar00rootroot0000000 0000000 @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.SnapshotRecursiveSummary call %JAVA% -cp "%CLASSPATH%" %ZOOMAIN% %* endlocal apache-zookeeper-3.9.4/bin/zkSnapshotRecursiveSummaryToolkit.sh0100755 0000000 0000000 00000002616 15051152474 025410 0ustar00rootroot0000000 0000000 #!/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. # # 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then # shellcheck source=/bin/zkEnv.sh . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi "$JAVA" -cp "$CLASSPATH" "$JVMFLAGS" \ org.apache.zookeeper.server.SnapshotRecursiveSummary "$@" apache-zookeeper-3.9.4/bin/zkTxnLogToolkit.cmd0100755 0000000 0000000 00000001744 15051152474 021710 0ustar00rootroot0000000 0000000 @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.persistence.TxnLogToolkit call %JAVA% -cp "%CLASSPATH%" %ZOOMAIN% %* endlocal apache-zookeeper-3.9.4/bin/zkTxnLogToolkit.sh0100755 0000000 0000000 00000002551 15051152474 021554 0ustar00rootroot0000000 0000000 #!/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. # # 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. # # use POSIX interface, symlink is followed automatically ZOOBIN="${BASH_SOURCE-$0}" ZOOBIN="$(dirname "${ZOOBIN}")" ZOOBINDIR="$(cd "${ZOOBIN}"; pwd)" if [ -e "$ZOOBIN/../libexec/zkEnv.sh" ]; then . "$ZOOBINDIR"/../libexec/zkEnv.sh else . "$ZOOBINDIR"/zkEnv.sh fi "$JAVA" -cp "$CLASSPATH" $JVMFLAGS \ org.apache.zookeeper.server.persistence.TxnLogToolkit "$@" apache-zookeeper-3.9.4/checkstyle-simple.xml0100644 0000000 0000000 00000005205 15051152474 021465 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/checkstyle-strict.xml0100644 0000000 0000000 00000042120 15051152474 021501 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/checkstyleSuppressions.xml0100644 0000000 0000000 00000003020 15051152474 022625 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/conf/configuration.xsl0100644 0000000 0000000 00000001027 15051152474 021640 0ustar00rootroot0000000 0000000
name value description
apache-zookeeper-3.9.4/conf/logback.xml0100644 0000000 0000000 00000010717 15051152474 020373 0ustar00rootroot0000000 0000000 %d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n ${zookeeper.console.threshold} apache-zookeeper-3.9.4/conf/zoo_sample.cfg0100644 0000000 0000000 00000002237 15051152474 021076 0ustar00rootroot0000000 0000000 # The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/tmp/zookeeper # the port at which the clients will connect clientPort=2181 # the maximum number of client connections. # increase this if you need to handle more clients #maxClientCnxns=60 # # Be sure to read the maintenance section of the # administrator guide before turning on autopurge. # # https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance # # The number of snapshots to retain in dataDir #autopurge.snapRetainCount=3 # Purge task interval in hours # Set to "0" to disable auto purge feature #autopurge.purgeInterval=1 ## Metrics Providers # # https://prometheus.io Metrics Exporter #metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider #metricsProvider.httpHost=0.0.0.0 #metricsProvider.httpPort=7000 #metricsProvider.exportJvmInfo=true apache-zookeeper-3.9.4/dev/docker/Dockerfile0100644 0000000 0000000 00000002006 15051152474 021331 0ustar00rootroot0000000 0000000 # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT 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 maven:3.8.4-jdk-8 RUN apt-get update RUN apt-get install -y \ g++ \ cmake \ autoconf \ pkg-config \ libcppunit-dev \ libtool \ openssl \ libssl-dev \ libsasl2-modules-gssapi-mit \ libsasl2-modules \ libsasl2-dev apache-zookeeper-3.9.4/dev/docker/run.sh0100755 0000000 0000000 00000003617 15051152474 020513 0ustar00rootroot0000000 0000000 #!/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. set -e -x -u SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) export IMAGE_NAME="zookeeper/dev" pushd ${SCRIPT_DIR} docker build --rm=true -t ${IMAGE_NAME} . popd if [ "$(uname -s)" == "Linux" ]; then USER_NAME=${SUDO_USER:=$USER} USER_ID=$(id -u "${USER_NAME}") GROUP_ID=$(id -g "${USER_NAME}") LOCAL_HOME=$(realpath ~) else # boot2docker uid and gid USER_NAME=$USER USER_ID=1000 GROUP_ID=50 LOCAL_HOME="/Users/${USER_NAME}" fi docker build -t "${IMAGE_NAME}-${USER_NAME}" - < apache-zookeeper-3.9.4/owaspSuppressions.xml0100644 0000000 0000000 00000007000 15051152474 021622 0ustar00rootroot0000000 0000000 CVE-2024-6763 CVE-2018-8088 CVE-2021-37533 CVE-2018-8012 CVE-2016-5017 CVE-2018-12056 CVE-2023-4586 CVE-2019-3826 CVE-2021-29425 CVE-2021-28164 CVE-2021-34429 CVE-2023-35116 CVE-2022-45688 apache-zookeeper-3.9.4/pom.xml0100644 0000000 0000000 00000130064 15051152474 016635 0ustar00rootroot0000000 0000000 4.0.0 org.apache apache 23 org.apache.zookeeper parent pom 3.9.4 Apache 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. http://zookeeper.apache.org 2008 Apache License, Version 2.0 https://www.apache.org/licenses/LICENSE-2.0.txt repo zookeeper-docs zookeeper-jute zookeeper-server zookeeper-metrics-providers zookeeper-client zookeeper-recipes zookeeper-assembly zookeeper-compatibility-tests scm:git:https://gitbox.apache.org/repos/asf/zookeeper.git scm:git:https://gitbox.apache.org/repos/asf/zookeeper.git https://gitbox.apache.org/repos/asf/zookeeper.git release-3.9.4-2 JIRA http://issues.apache.org/jira/browse/ZOOKEEPER jenkins https://ci-hadoop.apache.org/view/ZooKeeper/ User List user-subscribe@zookeeper.apache.org user-unsubscribe@zookeeper.apache.org user@zookeeper.apache.org http://mail-archives.apache.org/mod_mbox/zookeeper-user/ Developer List dev-subscribe@zookeeper.apache.org dev-unsubscribe@zookeeper.apache.org dev@zookeeper.apache.org http://mail-archives.apache.org/mod_mbox/zookeeper-dev/ Commits List commits-subscribe@zookeeper.apache.org commits-unsubscribe@zookeeper.apache.org http://mail-archives.apache.org/mod_mbox/zookeeper-commits/ Issues List issues-subscribe@zookeeper.apache.org issues-unsubscribe@zookeeper.apache.org https://lists.apache.org/list.html?issues@zookeeper.apache.org Notifications List notifications-subscribe@zookeeper.apache.org notifications-unsubscribe@zookeeper.apache.org https://lists.apache.org/list.html?notifications@zookeeper.apache.org tdunning Ted Dunning tdunning@apache.org -8 camille Camille Fournier camille@apache.org -5 phunt Patrick Hunt phunt@apache.org -8 fpj Flavio Junqueira fpj@apache.org +0 ivank Ivan Kelly ivank@apache.org +2 mahadev Mahadev Konar mahadev@apache.org -8 michim Michi Mutsuzaki michim@apache.org -8 cnauroth Chris Nauroth cnauroth@apache.org -8 breed Benjamin Reed breed@apache.org -8 henry Henry Robinson henry@apache.org -8 rgs Raul Gutierrez Segales rgs@apache.org -8 rakeshr Rakesh Radhakrishnan rakeshr@apache.org +5:30 hanm Michael Han hanm@apache.org -8 gkesavan Giridharan Kesavan gkesavan@apache.org -8 akornev Andrew Kornev akornev@apache.org shralex Alex Shraer shralex@apache.org -8 thawan Thawan Kooburat thawan@apache.org -8 hdeng Hongchao Deng hdeng@apache.org -8 arshad Mohammad Arshad arshad@apache.org +5:30 afine Abraham Fine afine@apache.org -8 andor Andor Molnar andor@apache.org +1 lvfangmin Allan Lyu fangmin@apache.org -8 eolivelli Enrico Olivelli eolivelli@apache.org +1 nkalmar Norbert Kalmar nkalmar@apache.org +1 enixon Brian Nixon enixon@apache.org -8 symat Mate Szalay-Beko symat@apache.org +1 ddiederen Damien Diederen ddiederen@apache.org Europe/Berlin maoling Ling Mao maoling@apache.org +8 full-build zookeeper-contrib zookeeper-it fatjar zookeeper-contrib zookeeper-it apache-release org.apache.maven.plugins maven-assembly-plugin org.apache.apache.resources apache-source-release-assembly-descriptor 1.0.6 source-release-assembly-tar-gz initialize single true ${sourceReleaseAssemblyDescriptor} apache-zookeeper-${project.version} false tar.gz posix m2e m2e.version 8 org.eclipse.m2e lifecycle-mapping 1.0.0 org.codehaus.mojo exec-maven-plugin [0,) exec org.apache.maven.plugins maven-antrun-plugin [0,) run com.ruleoftech markdown-page-generator-plugin [0,) generate org.codehaus.mojo javacc-maven-plugin [0,) javacc com.github.koraktor mavanagaiata [0,) commit jdk-release-flag [9,) 8 clover false clover ${project.build.directory}/clover/zookeeper-coverage.db org.openclover clover-maven-plugin false true ${cloverDatabase} 50% ${project.build.directory}/clover true false ${project.build.directory}/clover/zookeeper-coverage.db ${project.build.directory}/clover/zookeeper-coverage.db ${project.build.directory}/clover true true false true org/apache/zookeeper/**/* org/apache/zookeeper/version/**/* **/ReadOnlyModeTest.java **/KeeperStateTest.java clover-setup process-sources setup clover test clover sonar ${project.build.directory}/clover/clover.xml org.sonarsource.scanner.maven sonar-maven-plugin org.sonarsource.scanner.maven sonar-maven-plugin ${sonar-maven-plugin.version} reuseReports clover ${project.build.directory}/clover/clover.xml target/surefire-reports clover ${clover-maven-plugin.version} 1.8 1.8 1755632956 false 2.22.1 8 true 2.0.13 1.3.15 0.12.0 1.48 5.6.2 1.6.2 4.9.0 2.2 1.5.0 4.1.119.Final 9.4.57.v20241219 2.15.2 2.14.6 1.1.10.5 2.0.0 1.75 4.4 4.1.12.1 4.0.2 8.39 3.0.0-M3 2.17.0 0.25.4 4.4.1 3.7.0.1746 yes yes org.hamcrest hamcrest-library ${hamcrest.version} org.apache.commons commons-collections4 ${commons-collections.version} org.apache.yetus audience-annotations ${audience-annotations.version} commons-cli commons-cli ${commons-cli.version} org.apache.kerby kerb-core ${kerby.version} org.slf4j slf4j-api org.apache.kerby kerb-simplekdc ${kerby.version} org.slf4j slf4j-api org.apache.kerby kerby-config ${kerby.version} org.slf4j slf4j-api org.slf4j slf4j-log4j12 org.bouncycastle bcprov-jdk18on ${bouncycastle.version} org.bouncycastle bcpkix-jdk18on ${bouncycastle.version} org.slf4j slf4j-api ${slf4j.version} ch.qos.logback logback-core ${logback-version} ch.qos.logback logback-classic ${logback-version} org.jmockit jmockit ${jmockit.version} org.junit.jupiter junit-jupiter-api ${junit.version} org.junit.jupiter junit-jupiter-engine ${junit.version} org.junit.jupiter junit-jupiter-params ${junit.version} test org.junit.platform junit-platform-runner ${junit-platform.version} test org.junit.vintage junit-vintage-engine ${junit.version} org.mockito mockito-core ${mockito.version} io.netty netty-bom ${netty.version} pom import org.eclipse.jetty jetty-server ${jetty.version} org.eclipse.jetty jetty-servlet ${jetty.version} org.eclipse.jetty jetty-client ${jetty.version} io.dropwizard.metrics metrics-core ${dropwizard.version} org.slf4j slf4j-api com.fasterxml.jackson.core jackson-databind ${jackson.version} jline jline ${jline.version} com.github.spotbugs spotbugs-annotations ${spotbugsannotations.version} provided true org.xerial.snappy snappy-java ${snappy.version} commons-io commons-io ${commons-io.version} org.burningwave tools ${burningwave.mockdns.version} org.apache.maven.plugins maven-compiler-plugin true -Werror -Xlint:deprecation -Xlint:unchecked -Xlint:-options -Xdoclint:-missing -Xpkginfo:always org.apache.maven.plugins maven-jar-plugin ${mvngit.commit.id} org.apache.maven.plugins maven-source-plugin org.apache.maven.plugins maven-javadoc-plugin 3.1.1 true none org.apache.maven.plugins maven-assembly-plugin 3.6.0 org.apache.maven.plugins maven-release-plugin org.apache.maven.plugins maven-scm-plugin 1.11.2 org.apache.maven.plugins maven-surefire-plugin false ${redirectTestOutputToFile} org.apache.maven.plugins maven-antrun-plugin org.apache.maven.plugins maven-dependency-plugin org.codehaus.mojo exec-maven-plugin 1.6.0 com.github.koraktor mavanagaiata 0.9.4 true org.codehaus.mojo build-helper-maven-plugin 3.0.0 net.nicoulaj.maven.plugins checksum-maven-plugin 1.8 org.openclover clover-maven-plugin ${clover-maven-plugin.version} com.github.spotbugs spotbugs-maven-plugin 4.0.0 excludeFindBugsFilter.xml org.owasp dependency-check-maven 12.1.3 org.apache.maven.plugins maven-checkstyle-plugin 3.1.1 com.puppycrawl.tools checkstyle ${checkstyle.version} checkstyle-strict.xml checkstyleSuppressions.xml UTF-8 true true false false true maven-remote-resources-plugin process-resource-bundles none org.apache.felix maven-bundle-plugin 5.1.9 org.cyclonedx cyclonedx-maven-plugin 2.7.9 com.github.koraktor mavanagaiata find-current-git-revision commit validate org.apache.maven.plugins maven-antrun-plugin set-hostname-property validate run true org.apache.maven.plugins maven-jar-plugin Jar Tests Package package test-jar org/** META_INF/** true org.apache.maven.plugins maven-source-plugin attach-sources jar org.apache.maven.plugins maven-javadoc-plugin attach-javadocs jar aggregate site aggregate zookeeper-server/src/main/resources/overview.html *.recipes.* net.nicoulaj.maven.plugins checksum-maven-plugin artifacts SHA-512 true com.github.spotbugs spotbugs-maven-plugin org.owasp dependency-check-maven ALL 0 owaspSuppressions.xml org.apache.rat apache-rat-plugin **/README.md **/findbugsExcludeFile.xml **/checkstyle-noframes-sorted.xsl **/configure.ac **/Makefile.am conf/zoo_sample.cfg conf/configuration.xsl .travis.yml excludeFindBugsFilter.xml README_packaging.md src/main/resources/markdown/skin/* src/main/resources/markdown/html/* src/main/resources/markdown/images/* **/src/test/resources/embedded/*.conf **/JMX-RESOURCES **/src/main/resources/mainClasses **/Changes **/MANIFEST **/src/test/zoo.cfg **/src/main/resources/webapp/org/apache/zookeeper/graph/resources/* **/src/main/java/com/nitido/utils/toaster/Toaster.java **/TODO **/acinclude.m4 **/aminclude.am **/src/hashtable/* **/include/winconfig.h **/tests/wrappers.opt **/tests/zoo.cfg **/tests/wrappers-mt.opt **/c-doc.Doxyfile true org.apache.maven.plugins maven-release-plugin false clean verify -DskipTests antrun:run@replace-cclient-files-during-release scm:add@add-cclient-files-during-release scm:checkin@commit-cclient-files-during-release clean verify -DskipTests antrun:run@replace-cclient-files-during-release scm:add@add-cclient-files-during-release scm:checkin@commit-cclient-files-during-release false true true release-@{project.version} org.apache.maven.plugins maven-scm-plugin false add-cclient-files-during-release none add false zookeeper-client/zookeeper-client-c/CMakeLists.txt,zookeeper-client/zookeeper-client-c/configure.ac,zookeeper-client/zookeeper-client-c/include/zookeeper_version.h commit-cclient-files-during-release none checkin false Prepared ${project.version} org.apache.maven.plugins maven-enforcer-plugin ${enforcer.version} banned-commons-lang enforce commons-lang:commons-lang false We don't use commons-lang anymore, so do not depend on it directly. banned-commons-lang3 enforce org.apache.commons:commons-lang3 false We don't use commons-lang3, so do not depend on it directly. banned-json-simple enforce com.googlecode.json-simple:json-simple false We don't use json-simple anymore, so do not depend on it directly. org.cyclonedx cyclonedx-maven-plugin makeBom package ${project.basedir}src/main/java/resources **/*.* org.openclover clover-maven-plugin apache-zookeeper-3.9.4/tools/ci/test-connectivity.py0100755 0000000 0000000 00000003610 15051152474 023117 0ustar00rootroot0000000 0000000 #!/usr/bin/env python3 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 argparse import subprocess from pathlib import Path class Server(): def __init__(self, binpath): self.binpath = binpath def __enter__(self): subprocess.run([f'{self.binpath}', 'start'], check=True) return self def __exit__(self, type, value, traceback): subprocess.run([f'{self.binpath}', 'stop'], check=True) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--server', help="basepath to zk server", required=True) parser.add_argument('--client', help="basepath to zk client", required=True) args = parser.parse_args() server_basepath = Path(args.server).absolute() server_binpath = server_basepath / "bin" / "zkServer.sh" assert server_binpath.exists(), f"server binpath not exist: {server_binpath}" client_basepath = Path(args.client).absolute() client_binpath = client_basepath / "bin" / "zkCli.sh" assert client_binpath.exists(), f"client binpath not exist: {client_binpath}" with Server(server_binpath): subprocess.run([f'{client_binpath}', 'sync', '/'], check=True) apache-zookeeper-3.9.4/tools/cmake/Modules/FindCyrusSASL.cmake0100644 0000000 0000000 00000004065 15051152474 024544 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # - Find Cyrus SASL (sasl.h, libsasl2.so) # # This module defines # CYRUS_SASL_INCLUDE_DIR, directory containing headers # CYRUS_SASL_SHARED_LIB, path to Cyrus SASL's shared library # CYRUS_SASL_FOUND, whether Cyrus SASL and its plugins have been found # # It also defines the following IMPORTED targets: # CyrusSASL # # Hints: # Set CYRUS_SASL_ROOT_DIR to the root directory of a Cyrus SASL installation. # # The initial version of this file was extracted from # https://github.com/cloudera/kudu, at the following commit: # # commit 9806863e78107505a622b44112a897189d9b3c24 # Author: Dan Burkert # Date: Mon Nov 30 12:15:36 2015 -0800 # # Enable C++11 find_path(CYRUS_SASL_INCLUDE_DIR sasl/sasl.h HINTS "${CYRUS_SASL_ROOT_DIR}/include") find_library(CYRUS_SASL_SHARED_LIB sasl2 HINTS "${CYRUS_SASL_ROOT_DIR}/lib") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CYRUS_SASL REQUIRED_VARS CYRUS_SASL_SHARED_LIB CYRUS_SASL_INCLUDE_DIR) if(CYRUS_SASL_FOUND) if(NOT TARGET CyrusSASL) add_library(CyrusSASL UNKNOWN IMPORTED) set_target_properties(CyrusSASL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CYRUS_SASL_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${CYRUS_SASL_SHARED_LIB}") endif() endif() apache-zookeeper-3.9.4/tools/sonar/code-coverage.sh0100755 0000000 0000000 00000004554 15051152474 022650 0ustar00rootroot0000000 0000000 #!/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. usage() { echo echo "options:" echo "-h Display help" echo "-u SonarQube Host URL" echo "-l SonarQube Login Credentials" echo "-k SonarQube Project Key" echo "-n SonarQube Project Name" echo echo "Important:" echo " The required parameters for publishing the coverage results to SonarQube:" echo " - Host URL" echo " - Login Credentials" echo " - Project Key" echo } execute() { SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" MAIN_POM="${SCRIPT_DIR}/../../pom.xml" mvn -B -e -Pclover -f "$MAIN_POM" clean install -DskipTests -DskipShade mvn -B -e -Pclover -f "$MAIN_POM" test -Dparallel-tests -DtestsThreadCount=8 -Dscale mvn -B -e -Pclover -f "$MAIN_POM" clover:aggregate clover:clover # If the required parameters are given, the code coverage results are uploaded to the SonarQube Server if [ -n "$SONAR_LOGIN" ] && [ -n "$SONAR_PROJECT_KEY" ] && [ -n "$SONAR_URL" ]; then mvn -B -e -Pclover -f "$MAIN_POM" sonar:sonar -Dsonar.clover.reportPath=./target/clover/clover.xml \ -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_LOGIN" -Dsonar.projectKey="$SONAR_PROJECT_KEY" -Dsonar.projectName="$SONAR_PROJECT_NAME" fi } while getopts ":u:l:k:n:h" option; do case $option in u) SONAR_URL=${OPTARG:-} ;; l) SONAR_LOGIN=${OPTARG:-} ;; k) SONAR_PROJECT_KEY=${OPTARG:-} ;; n) SONAR_PROJECT_NAME=${OPTARG:-} ;; h) # Display usage usage exit ;; \?) # Invalid option echo "Error: Invalid option" exit ;; esac done # Start code analysis execute apache-zookeeper-3.9.4/zk-merge-pr.py0100755 0000000 0000000 00000052517 15051152474 020043 0ustar00rootroot0000000 0000000 #!/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. # # Utility for creating well-formed pull request merges and pushing them to Apache. This script is a modified version # of the one created by the Spark project (https://github.com/apache/spark/blob/master/dev/merge_spark_pr.py). # # Usage: ./zk-merge-pr.py (see config env vars below) # # This utility assumes you already have a local ZooKeeper git folder and that you # have added remotes corresponding to both: # (i) the github apache ZooKeeper mirror and # (ii) the apache ZooKeeper git repo. import json import os import re import subprocess import sys import urllib.request, urllib.error, urllib.parse import getpass import requests try: import jira.client JIRA_IMPORTED = True except ImportError: JIRA_IMPORTED = False PROJECT_NAME = "zookeeper" CAPITALIZED_PROJECT_NAME = PROJECT_NAME.upper() # Remote name which points to the GitHub site PR_REMOTE_NAME = os.environ.get("PR_REMOTE_NAME", "apache-github") # Remote name which points to Apache git PUSH_REMOTE_NAME = os.environ.get("PUSH_REMOTE_NAME", "apache") # ASF JIRA username JIRA_USERNAME = os.environ.get("JIRA_USERNAME", "") # ASF JIRA password JIRA_PASSWORD = os.environ.get("JIRA_PASSWORD", "") # OAuth key used for issuing requests against the GitHub API. If this is not defined, then requests # will be unauthenticated. You should only need to configure this if you find yourself regularly # exceeding your IP's unauthenticated request rate limit. You can create an OAuth key at # https://github.com/settings/tokens. This script only requires the "public_repo" scope. GITHUB_OAUTH_KEY = os.environ.get("GITHUB_OAUTH_KEY") GITHUB_USER = os.environ.get("GITHUB_USER", "apache") GITHUB_BASE = "https://github.com/%s/%s/pull" % (GITHUB_USER, PROJECT_NAME) GITHUB_API_BASE = "https://api.github.com/repos/%s/%s" % (GITHUB_USER, PROJECT_NAME) JIRA_BASE = "https://issues.apache.org/jira/browse" JIRA_API_BASE = "https://issues.apache.org/jira" # Prefix added to temporary branches TEMP_BRANCH_PREFIX = "PR_TOOL" # TODO Introduce a convention as this is too brittle RELEASE_BRANCH_PREFIX = "branch-" DEV_BRANCH_NAME = "master" DEFAULT_FIX_VERSION = os.environ.get("DEFAULT_FIX_VERSION", "branch-3.5") def get_json(url): try: request = urllib.request.Request(url) if GITHUB_OAUTH_KEY: request.add_header('Authorization', 'token %s' % GITHUB_OAUTH_KEY) return json.load(urllib.request.urlopen(request)) except urllib.error.HTTPError as e: if "X-RateLimit-Remaining" in e.headers and e.headers["X-RateLimit-Remaining"] == '0': print("Exceeded the GitHub API rate limit; see the instructions in " + \ "zk-merge-pr.py to configure an OAuth token for making authenticated " + \ "GitHub requests.") else: print("Unable to fetch URL, exiting: %s" % url) sys.exit(-1) def fail(msg): print(msg) clean_up() sys.exit(-1) def run_cmd(cmd): print(cmd) if isinstance(cmd, list): return subprocess.check_output(cmd, encoding='utf8') else: return subprocess.check_output(cmd.split(" "), encoding='utf8') def continue_maybe(prompt): result = input("\n%s (y/n): " % prompt) if result.lower().strip() != "y": fail("Okay, exiting") def clean_up(): if original_head != get_current_branch(): print("Restoring head pointer to %s" % original_head) run_cmd("git checkout %s" % original_head) branches = run_cmd("git branch").replace(" ", "").split("\n") for branch in [x for x in branches if x.startswith(TEMP_BRANCH_PREFIX)]: print("Deleting local branch %s" % branch) run_cmd("git branch -D %s" % branch) def get_current_branch(): return run_cmd("git rev-parse --abbrev-ref HEAD").replace("\n", "") # merge the requested PR and return the merge hash def merge_pr(pr_num, title, pr_repo_desc): merge_message = [] result = input("Would you like to squash the commit messages? (y/n): ") if result.lower().strip() == "y": # Retrieve the commits separately. json_commits = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}/commits") if json_commits and isinstance(json_commits, list): for commit in json_commits: commit_message = commit['commit']['message'] # Remove empty lines and lines containing "Change-Id:" filtered_lines = [line for line in commit_message.split('\n') if 'Change-Id:' not in line and line.strip()] modified_commit_message = '\n'.join(filtered_lines) if modified_commit_message.strip() != title.strip(): merge_message += [modified_commit_message] # Check for disapproval reviews. json_reviewers = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}/reviews") disapproval_reviews = [review['user']['login'] for review in json_reviewers if review['state'] == 'CHANGES_REQUESTED'] if disapproval_reviews: continue_maybe("Warning: There are requested changes. Proceed with merging pull request #%s?" % pr_num) # Verify if there are no approved reviews. approved_reviewers = [review['user']['login'] for review in json_reviewers if review['state'] == 'APPROVED'] if not approved_reviewers: continue_maybe("Warning: Pull Request does not have an approved review. Proceed with merging pull request #%s?" % pr_num) else: reviewers_string = ', '.join(approved_reviewers) merge_message += [f"Reviewers: {reviewers_string}"] # Check the author and the closing line. json_pr = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}") primary_author = json_pr["user"]["login"] if primary_author != "": merge_message += [f"Author: {primary_author}"] close_line = "Closes #%s from %s" % (pr_num, pr_repo_desc) merge_message += [close_line] merged_string = '\n'.join(merge_message) # Get the latest commit SHA. latest_commit_sha = json_pr["head"]["sha"] json_status = get_json(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/commits/{latest_commit_sha}/check-runs") # Check if all checks have passed on GitHub. all_checks_passed = all(status["conclusion"] == "success" for status in json_status["check_runs"]) if all_checks_passed: print("All checks have passed on the github.") else: any_in_progress = any(run["status"] == "in_progress" for run in json_status["check_runs"]) if any_in_progress: continue_maybe("Warning: There are pending checks. Would you like to continue the merge?") else: continue_maybe("Warning: Not all checks have passed on GitHub. Would you like to continue the merge?") headers = { "Authorization": f"token {GITHUB_OAUTH_KEY}", "Accept": "application/vnd.github.v3+json" } data = { "commit_title": title, "commit_message": merged_string, "merge_method": "squash" } response = requests.put(f"https://api.github.com/repos/{PUSH_REMOTE_NAME}/{PROJECT_NAME}/pulls/{pr_num}/merge", headers=headers, json=data) if response.status_code == 200: merge_response_json = response.json() merge_commit_sha = merge_response_json.get("sha") print(f"Pull request #{pr_num} merged. Sha: #{merge_commit_sha}") return merge_commit_sha else: print(f"Failed to merge pull request #{pr_num}. Status code: {response.status_code}") print(response.text) exit() def cherry_pick(pr_num, merge_hash, default_branch): pick_ref = input("Enter a branch name [%s]: " % default_branch) if pick_ref == "": pick_ref = default_branch pick_branch_name = "%s_PICK_PR_%s_%s" % (TEMP_BRANCH_PREFIX, pr_num, pick_ref.upper()) run_cmd("git fetch %s" % PUSH_REMOTE_NAME) run_cmd("git checkout -b %s %s/%s" % (pick_branch_name, PUSH_REMOTE_NAME, pick_ref)) try: run_cmd("git cherry-pick -sx %s" % merge_hash) except Exception as e: msg = "Error cherry-picking: %s\nWould you like to manually fix-up this merge?" % e continue_maybe(msg) msg = "Okay, please fix any conflicts and finish the cherry-pick. Finished?" continue_maybe(msg) continue_maybe("Pick complete (local ref %s). Push to %s?" % ( pick_branch_name, PUSH_REMOTE_NAME)) try: run_cmd('git push %s %s:%s' % (PUSH_REMOTE_NAME, pick_branch_name, pick_ref)) except Exception as e: clean_up() fail("Exception while pushing: %s" % e) pick_hash = run_cmd("git rev-parse %s" % pick_branch_name)[:8] clean_up() print(("Pull request #%s picked into %s!" % (pr_num, pick_ref))) print(("Pick hash: %s" % pick_hash)) return pick_ref def fix_version_from_branch(branch, versions): # Note: Assumes this is a sorted (newest->oldest) list of un-released versions if branch == DEV_BRANCH_NAME: versions = [x for x in versions if x == DEFAULT_FIX_VERSION] if len(versions) > 0: return versions[0] else: return None else: versions = [x for x in versions if x.startswith(branch)] if len(versions) > 0: return versions[-1] else: return None def resolve_jira_issue(merge_branches, comment, default_jira_id=""): asf_jira = jira.client.JIRA({'server': JIRA_API_BASE}, basic_auth=(JIRA_USERNAME, JIRA_PASSWORD)) jira_id = input("Enter a JIRA id [%s]: " % default_jira_id) if jira_id == "": jira_id = default_jira_id try: issue = asf_jira.issue(jira_id) except Exception as e: fail("ASF JIRA could not find %s\n%s" % (jira_id, e)) cur_status = issue.fields.status.name cur_summary = issue.fields.summary cur_assignee = issue.fields.assignee if cur_assignee is None: cur_assignee = "NOT ASSIGNED!!!" else: cur_assignee = cur_assignee.displayName if cur_status == "Resolved" or cur_status == "Closed": fail("JIRA issue %s already has status '%s'" % (jira_id, cur_status)) print(("=== JIRA %s ===" % jira_id)) print(("summary\t\t%s\nassignee\t%s\nstatus\t\t%s\nurl\t\t%s/%s\n" % ( cur_summary, cur_assignee, cur_status, JIRA_BASE, jira_id))) versions = asf_jira.project_versions(CAPITALIZED_PROJECT_NAME) versions = sorted(versions, key=lambda x: x.name, reverse=True) versions = [x for x in versions if x.raw['released'] is False] version_names = [x.name for x in versions] default_fix_versions = [fix_version_from_branch(x, version_names) for x in merge_branches] default_fix_versions = [x for x in default_fix_versions if x != None] default_fix_versions = ",".join(default_fix_versions) fix_versions = input("Enter comma-separated fix version(s) [%s]: " % default_fix_versions) if fix_versions == "": fix_versions = default_fix_versions fix_versions = fix_versions.replace(" ", "").split(",") def get_version_json(version_str): return [v for v in versions if v.name == version_str][0].raw jira_fix_versions = [get_version_json(v) for v in fix_versions] resolve = [a for a in asf_jira.transitions(jira_id) if a['name'] == "Resolve Issue"][0] resolution = [r for r in asf_jira.resolutions() if r.raw['name'] == "Fixed"][0] asf_jira.transition_issue( jira_id, resolve["id"], fixVersions = jira_fix_versions, comment = comment, resolution = {'id': resolution.raw['id']}) print("Successfully resolved %s with fixVersions=%s!" % (jira_id, fix_versions)) def resolve_jira_issues(title, merge_branches, comment): jira_ids = re.findall("%s-[0-9]+" % CAPITALIZED_PROJECT_NAME, title) if len(jira_ids) == 0: resolve_jira_issue(merge_branches, comment) for jira_id in jira_ids: resolve_jira_issue(merge_branches, comment, jira_id) def standardize_jira_ref(text): """ Standardize the jira reference commit message prefix to "PROJECT_NAME-XXX: Issue" >>> standardize_jira_ref("%s-5954: Top by key" % CAPITALIZED_PROJECT_NAME) 'ZOOKEEPER-5954: Top by key' >>> standardize_jira_ref("%s-5821: ParquetRelation2 CTAS should check if delete is successful" % PROJECT_NAME) 'ZOOKEEPER-5821: ParquetRelation2 CTAS should check if delete is successful' >>> standardize_jira_ref("%s-4123: [WIP] Show new dependencies added in pull requests" % PROJECT_NAME) 'ZOOKEEPER-4123: [WIP] Show new dependencies added in pull requests' >>> standardize_jira_ref("%s 5954: Top by key" % PROJECT_NAME) 'ZOOKEEPER-5954: Top by key' >>> standardize_jira_ref("%s-979: a LRU scheduler for load balancing in TaskSchedulerImpl" % PROJECT_NAME) 'ZOOKEEPER-979: a LRU scheduler for load balancing in TaskSchedulerImpl' >>> standardize_jira_ref("%s-1094: Support MiMa for reporting binary compatibility across versions." % CAPITALIZED_PROJECT_NAME) 'ZOOKEEPER-1094: Support MiMa for reporting binary compatibility across versions.' >>> standardize_jira_ref("%s-1146: [WIP] Vagrant support" % CAPITALIZED_PROJECT_NAME) 'ZOOKEEPER-1146: [WIP] Vagrant support' >>> standardize_jira_ref("%s-1032: If Yarn app fails before registering, app master stays aroun..." % PROJECT_NAME) 'ZOOKEEPER-1032: If Yarn app fails before registering, app master stays aroun...' >>> standardize_jira_ref("%s-6250 %s-6146 %s-5911: Types are now reserved words in DDL parser." % (PROJECT_NAME, PROJECT_NAME, CAPITALIZED_PROJECT_NAME)) 'ZOOKEEPER-6250 ZOOKEEPER-6146 ZOOKEEPER-5911: Types are now reserved words in DDL parser.' >>> standardize_jira_ref("Additional information for users building from source code") 'Additional information for users building from source code' """ jira_refs = [] components = [] # Extract JIRA ref(s): pattern = re.compile(r'(%s[-\s]*[0-9]{3,6})+' % CAPITALIZED_PROJECT_NAME, re.IGNORECASE) for ref in pattern.findall(text): # Add brackets, replace spaces with a dash, & convert to uppercase jira_refs.append(re.sub(r'\s+', '-', ref.upper())) text = text.replace(ref, '') # Extract project name component(s): # Look for alphanumeric chars, spaces, dashes, periods, and/or commas pattern = re.compile(r'(\[[\w\s,-\.]+\])', re.IGNORECASE) for component in pattern.findall(text): components.append(component.upper()) text = text.replace(component, '') # Cleanup any remaining symbols: pattern = re.compile(r'^\W+(.*)', re.IGNORECASE) if (pattern.search(text) is not None): text = pattern.search(text).groups()[0] # Assemble full text (JIRA ref(s), module(s), remaining text) jira_prefix = ' '.join(jira_refs).strip() if jira_prefix: jira_prefix = jira_prefix + ": " clean_text = jira_prefix + ' '.join(components).strip() + " " + text.strip() # Replace multiple spaces with a single space, e.g. if no jira refs and/or components were included clean_text = re.sub(r'\s+', ' ', clean_text.strip()) return clean_text def get_remote_repos(): repos = run_cmd("git remote -v").split() dict = {} for i in range(0, len(repos), 3): dict[repos[i]] = repos[i+1] return dict def check_git_remote(): repos = get_remote_repos() # check if all remote endpoints' URLs point to project git repo name = PROJECT_NAME + ".git" for url in list(repos.values()): if not url.endswith(name): fail("Error: not a %s git repo or at least one remote is invalid" % PROJECT_NAME) if not PR_REMOTE_NAME in repos: fail("Error: PR_REMOTE_NAME (%s) environment variable has not been set!" % PR_REMOTE_NAME) if not PUSH_REMOTE_NAME in repos: fail("Error: PUSH_REMOTE_NAME (%s) environment variable has not been set!" % PUSH_REMOTE_NAME) def check_jira_env(): global JIRA_PASSWORD if JIRA_IMPORTED: if JIRA_USERNAME.strip() != "" and JIRA_PASSWORD.strip() == "": inform_pwd = input("JIRA_USERNAME set but JIRA_PASSWORD is not. Want to inform it? ") if inform_pwd.strip() == "y": JIRA_PASSWORD = getpass.getpass('JIRA PASSWORD: ') if JIRA_USERNAME.strip() == "" or JIRA_PASSWORD.strip() == "": msg ="JIRA_USERNAME and/or JIRA_PASSWORD are not set. Want to continue? " continue_maybe(msg) else: msg = "JIRA lib not installed. Want to continue? " continue_maybe(msg) def main(): global original_head original_head = get_current_branch() check_jira_env() check_git_remote() branches = get_json("%s/branches" % GITHUB_API_BASE) branch_names = [x for x in [x['name'] for x in branches] if x.startswith(RELEASE_BRANCH_PREFIX)] # Assumes branch names can be sorted lexicographically latest_branch = sorted(branch_names, reverse=True)[0] pr_num = input("Which pull request would you like to merge? (e.g. 34): ") pr = get_json("%s/pulls/%s" % (GITHUB_API_BASE, pr_num)) # Check if the pull request has already been closed or merged. pull_request_state = pr.get("state", "") if pull_request_state == "closed": merge_hash = pr.get("merge_commit_sha", "") merged = pr.get("merged") # Verify if the pull request has been merged by the GitHub API. if merged is True: print(f"Pull request #{pr['number']} has already been merged, assuming you want to backport") cherry_pick(pr_num, merge_hash, latest_branch) sys.exit(0) # Some merged pull requests may not appear as merged in the GitHub API, # for example, those closed by an older version of this script. else: pr_events = get_json("%s/issues/%s/events" % (GITHUB_API_BASE, pr_num)) for event in pr_events: if event.get("event") == "closed": commit_id = event.get("commit_id") if commit_id is not None: print(f"Pull request #{pr['number']} has already been merged, assuming you want to backport") cherry_pick(pr_num, merge_hash, latest_branch) sys.exit(0) else: print(f"Pull request #{pr['number']} has already been closed, but not merged, exiting.") exit() if not bool(pr["mergeable"]): print(f"Pull request %s is not mergeable in its current form.\n" % pr_num) exit() url = pr["url"] pr_title = pr["title"] commit_title = input("Commit title [%s]: " % pr_title) if commit_title == "": commit_title = pr_title # Decide whether to use the modified title or not modified_title = standardize_jira_ref(commit_title) if modified_title != commit_title: print("I've re-written the title as follows to match the standard format:") print("Original: %s" % commit_title) print("Modified: %s" % modified_title) result = input("Would you like to use the modified title? (y/n): ") if result.lower().strip() == "y": commit_title = modified_title print("Using modified title:") else: print("Using original title:") print(commit_title) target_ref = pr["base"]["ref"] user_login = pr["user"]["login"] base_ref = pr["head"]["ref"] pr_repo_desc = "%s/%s" % (user_login, base_ref) print(("\n=== Pull Request #%s ===" % pr_num)) print(("PR title\t%s\nCommit title\t%s\nSource\t\t%s\nTarget\t\t%s\nURL\t\t%s" % ( pr_title, commit_title, pr_repo_desc, target_ref, url))) continue_maybe("Proceed with merging pull request #%s?" % pr_num) merged_refs = [target_ref] merge_hash = merge_pr(pr_num, commit_title, pr_repo_desc) pick_prompt = "Would you like to pick %s into another branch?" % merge_hash while input("\n%s (y/n): " % pick_prompt).lower().strip() == "y": merged_refs = merged_refs + [cherry_pick(pr_num, merge_hash, latest_branch)] if JIRA_IMPORTED: if JIRA_USERNAME and JIRA_PASSWORD: continue_maybe("Would you like to update an associated JIRA?") jira_comment = "Issue resolved by pull request %s\n[%s/%s]" % (pr_num, GITHUB_BASE, pr_num) resolve_jira_issues(commit_title, merged_refs, jira_comment) else: print("JIRA_USERNAME and JIRA_PASSWORD not set") print("Exiting without trying to close the associated JIRA.") else: print("Could not find jira-python library. Run 'sudo pip install jira' to install.") print("Exiting without trying to close the associated JIRA.") if __name__ == "__main__": import doctest (failure_count, test_count) = doctest.testmod() if (failure_count): exit(-1) main() apache-zookeeper-3.9.4/zookeeper-assembly/pom.xml0100755 0000000 0000000 00000013442 15051152474 022460 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-assembly pom Apache ZooKeeper - Assembly ZooKeeper Assembly full-build false 0644 0755 true org.apache.zookeeper zookeeper-docs ${project.version} pom org.apache.zookeeper zookeeper-jute ${project.version} org.apache.zookeeper zookeeper ${project.version} org.apache.zookeeper zookeeper-client ${project.version} pom org.apache.zookeeper zookeeper-prometheus-metrics ${project.version} org.apache.zookeeper zookeeper-recipes ${project.version} pom commons-cli commons-cli org.eclipse.jetty jetty-server org.eclipse.jetty jetty-servlet com.fasterxml.jackson.core jackson-databind jline jline ch.qos.logback logback-classic io.dropwizard.metrics metrics-core org.xerial.snappy snappy-java org.apache.maven.plugins maven-assembly-plugin bin-assembly package single ${project.basedir}/src/main/assembly/bin-package.xml apache-zookeeper-${project.version}-bin false posix lib-assembly package single ${project.basedir}/src/main/assembly/lib-package.xml apache-zookeeper-${project.version}-lib false posix ${skip.lib.artifact} com.github.spotbugs spotbugs-maven-plugin true maven-deploy-plugin true apache-zookeeper-3.9.4/zookeeper-assembly/src/main/assembly/bin-package.xml0100644 0000000 0000000 00000006465 15051152474 027364 0ustar00rootroot0000000 0000000 bin-package tar.gz true src/main/assembly/components.xml *:* org.apache.zookeeper:zookeeper-recipes org.apache.zookeeper:zookeeper-client org.apache.zookeeper:zookeeper-docs false true lib ${rw.file.permission} ${rwx.file.permission} true ${project.basedir}/../zookeeper-docs/target/html docs ${rw.file.permission} ${rwx.file.permission} ${project.basedir}/../zookeeper-jute/target/apidocs docs/apidocs/zookeeper-jute ${rw.file.permission} ${rwx.file.permission} ${project.basedir}/../zookeeper-server/target/apidocs docs/apidocs/zookeeper-server ${rw.file.permission} ${rwx.file.permission} ${project.basedir}/../zookeeper-server/src/main/resources/lib *.txt lib ${rw.file.permission} apache-zookeeper-3.9.4/zookeeper-assembly/src/main/assembly/components.xml0100644 0000000 0000000 00000003410 15051152474 027373 0ustar00rootroot0000000 0000000 ${project.basedir}/.. . NOTICE.txt LICENSE.txt README.md README_packaging.md ${rw.file.permission} ${project.basedir}/../conf conf ${rw.file.permission} ${rwx.file.permission} ${project.basedir}/../bin bin ${rwx.file.permission} ${rwx.file.permission} apache-zookeeper-3.9.4/zookeeper-assembly/src/main/assembly/lib-package.xml0100644 0000000 0000000 00000004575 15051152474 027362 0ustar00rootroot0000000 0000000 lib-package tar.gz true ${project.basedir}/../zookeeper-client/zookeeper-client-c/target/c usr include/**/* lib/* ${rw.file.permission} ${rwx.file.permission} ${project.basedir}/../zookeeper-client/zookeeper-client-c/target/c usr bin/* ${rwx.file.permission} ${rwx.file.permission} ${project.basedir}/../zookeeper-client/zookeeper-client-c LICENSE . ${rw.file.permission} ${rwx.file.permission} apache-zookeeper-3.9.4/zookeeper-client/pom.xml0100755 0000000 0000000 00000003524 15051152474 022117 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-client pom Apache ZooKeeper - Client ZooKeeper client full-build zookeeper-client-c maven-deploy-plugin true apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/CMakeLists.txt0100644 0000000 0000000 00000020410 15051152474 027027 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. cmake_minimum_required(VERSION 3.5) project(zookeeper VERSION 3.9.4) set(email user@zookeeper.apache.org) set(description "zookeeper C client") list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../tools/cmake/Modules") # general options if(UNIX) add_compile_options(-Wall -fPIC) elseif(WIN32) add_compile_options(/W3) endif() add_definitions(-DUSE_STATIC_LIB) # TODO: Enable /WX and /W4 on Windows. Currently there are ~1000 warnings. # TODO: Add Solaris support. # TODO: Add a shared library option. # TODO: Specify symbols to export. # TODO: Generate doxygen documentation. # Sync API option option(WANT_SYNCAPI "Enables Sync API support" ON) if(WANT_SYNCAPI) add_definitions(-DTHREADED) endif() # CppUnit option if(WIN32 OR APPLE) # The tests do not yet compile on Windows or macOS, # so we set this to off by default. # # Note that CMake does not have expressions except in conditionals, # so we're left with this if/else/endif pattern. set(DEFAULT_WANT_CPPUNIT OFF) else() set(DEFAULT_WANT_CPPUNIT ON) endif() option(WANT_CPPUNIT "Enables CppUnit and tests" ${DEFAULT_WANT_CPPUNIT}) # SOCK_CLOEXEC option(WANT_SOCK_CLOEXEC "Enables SOCK_CLOEXEC on sockets" OFF) include(CheckSymbolExists) check_symbol_exists(SOCK_CLOEXEC sys/socket.h HAVE_SOCK_CLOEXEC) if(WANT_SOCK_CLOEXEC AND HAVE_SOCK_CLOEXEC) set(SOCK_CLOEXEC_ENABLED 1) endif() # Cyrus SASL 2.x option(WITH_CYRUS_SASL "turn ON/OFF Cyrus SASL 2.x support, or define SASL library location (default: ON)" ON) message("-- using WITH_CYRUS_SASL=${WITH_CYRUS_SASL}") if(NOT WITH_CYRUS_SASL STREQUAL "OFF") if(NOT WITH_CYRUS_SASL STREQUAL "ON") set(CYRUS_SASL_ROOT_DIR "${WITH_CYRUS_SASL}") endif() find_package(CyrusSASL) if(CYRUS_SASL_FOUND) message("-- Cyrus SASL 2.x found! will build with SASL support.") else() message("-- WARNING: unable to find Cyrus SASL 2.x! will build without SASL support.") endif() endif() # The function `to_have(in out)` converts a header name like `arpa/inet.h` # into an Autotools style preprocessor definition `HAVE_ARPA_INET_H`. # This is then set or unset in `configure_file()` step. # # Note that CMake functions do not have return values; instead an "out" # variable must be passed, and explicitly set with parent scope. function(to_have in out) string(TOUPPER ${in} str) string(REGEX REPLACE "/|\\." "_" str ${str}) set(${out} "HAVE_${str}" PARENT_SCOPE) endfunction() # include file checks foreach(f generated/zookeeper.jute.h generated/zookeeper.jute.c) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${f}") to_have(${f} name) set(${name} 1) else() message(FATAL_ERROR "jute files are missing!\n" "Please run 'ant compile_jute' while in the ZooKeeper top level directory.") endif() endforeach() # header checks include(CheckIncludeFile) set(check_headers arpa/inet.h dlfcn.h fcntl.h inttypes.h memory.h netdb.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/socket.h sys/stat.h sys/time.h sys/types.h unistd.h sys/utsname.h) foreach(f ${check_headers}) to_have(${f} name) check_include_file(${f} ${name}) endforeach() # function checks include(CheckFunctionExists) set(check_functions getcwd gethostbyname gethostname getlogin getpwuid_r gettimeofday getuid memmove memset poll socket strchr strdup strerror strtol) foreach(fn ${check_functions}) to_have(${fn} name) check_function_exists(${fn} ${name}) endforeach() # library checks set(check_libraries rt m pthread) foreach(lib ${check_libraries}) to_have("lib${lib}" name) find_library(${name} ${lib}) endforeach() # IPv6 check include(CheckStructHasMember) check_struct_has_member("struct sockaddr_in6" sin6_addr "netinet/in.h" ZOO_IPV6_ENABLED) # configure configure_file(cmake_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/config.h) # hashtable library set(hashtable_sources src/hashtable/hashtable_itr.c src/hashtable/hashtable.c) add_library(hashtable STATIC ${hashtable_sources}) target_include_directories(hashtable PUBLIC include) target_link_libraries(hashtable PUBLIC $<$,$>:m>) # zookeeper library set(zookeeper_sources src/zookeeper.c src/recordio.c generated/zookeeper.jute.c src/zk_log.c src/zk_hashtable.c src/addrvec.c) if(WANT_SYNCAPI) list(APPEND zookeeper_sources src/mt_adaptor.c) else() list(APPEND zookeeper_sources src/st_adaptor.c) endif() if(CYRUS_SASL_FOUND) list(APPEND zookeeper_sources src/zk_sasl.c) endif() if(WIN32) list(APPEND zookeeper_sources src/winport.c) endif() add_library(zookeeper STATIC ${zookeeper_sources}) target_include_directories(zookeeper PUBLIC include ${CMAKE_CURRENT_BINARY_DIR}/include generated) target_link_libraries(zookeeper PUBLIC hashtable $<$:rt> # clock_gettime $<$:ws2_32>) # Winsock 2.0 option(WITH_OPENSSL "turn ON/OFF SSL support, or define openssl library location (default: ON)" ON) message("-- using WITH_OPENSSL=${WITH_OPENSSL}") if(NOT WITH_OPENSSL STREQUAL "OFF") if(NOT WITH_OPENSSL STREQUAL "ON") set(OPENSSL_ROOT_DIR,${WITH_OPENSSL}) endif() find_package(OpenSSL) if(OPENSSL_FOUND) target_compile_definitions(zookeeper PUBLIC HAVE_OPENSSL_H) target_link_libraries(zookeeper PUBLIC OpenSSL::SSL OpenSSL::Crypto) message("-- OpenSSL libraries found! will build with SSL support.") else() message("-- WARNING: unable to find OpenSSL libraries! will build without SSL support.") endif() endif() if(WANT_SYNCAPI AND NOT WIN32) find_package(Threads REQUIRED) target_link_libraries(zookeeper PUBLIC Threads::Threads) endif() if(CYRUS_SASL_FOUND) target_compile_definitions(zookeeper PUBLIC HAVE_CYRUS_SASL_H) target_link_libraries(zookeeper PUBLIC CyrusSASL) endif() # cli executable add_executable(cli src/cli.c) target_link_libraries(cli zookeeper) # load_gen executable if(WANT_SYNCAPI AND NOT WIN32) add_executable(load_gen src/load_gen.c) target_link_libraries(load_gen zookeeper) endif() # tests set(test_sources tests/TestDriver.cc tests/LibCMocks.cc tests/LibCSymTable.cc tests/MocksBase.cc tests/ZKMocks.cc tests/Util.cc tests/ThreadingUtil.cc tests/TestZookeeperInit.cc tests/TestZookeeperClose.cc tests/TestSASLAuth.cc tests/TestReconfig.cc tests/TestReconfigServer.cc tests/TestClientRetry.cc tests/TestOperations.cc tests/TestMulti.cc tests/TestWatchers.cc tests/TestClient.cc tests/ZooKeeperQuorumServer.cc tests/TestReadOnlyClient.cc tests/TestLogClientEnv.cc) if(WANT_SYNCAPI) list(APPEND test_sources tests/PthreadMocks.cc) endif() if(WANT_CPPUNIT) set (CMAKE_CXX_STANDARD 11) add_executable(zktest ${test_sources}) target_include_directories(zktest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(zktest PRIVATE -DZKSERVER_CMD="${CMAKE_CURRENT_SOURCE_DIR}/tests/zkServer.sh") # TODO: Use `find_library()` for `cppunit`. target_link_libraries(zktest zookeeper cppunit dl) # This reads the link flags from the file `tests/wrappers.opt` into # the variable `symbol_wrappers` for use in `target_link_libraries`. # It is a holdover from the original build system. file(STRINGS tests/wrappers.opt symbol_wrappers) if(WANT_SYNCAPI) file(STRINGS tests/wrappers-mt.opt symbol_wrappers_mt) endif() target_link_libraries(zktest ${symbol_wrappers} ${symbol_wrappers_mt}) enable_testing() add_test(NAME zktest_runner COMMAND zktest) set_property(TEST zktest_runner PROPERTY ENVIRONMENT "ZKROOT=${CMAKE_CURRENT_SOURCE_DIR}/../.." "CLASSPATH=$CLASSPATH:$CLOVER_HOME/lib/clover*.jar") endif() apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/ChangeLog0100644 0000000 0000000 00000010367 15051152474 026053 0ustar00rootroot0000000 0000000 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 draftapache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/INSTALL0100644 0000000 0000000 00000022310 15051152474 025321 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/LICENSE0100644 0000000 0000000 00000047314 15051152474 025310 0ustar00rootroot0000000 0000000 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. =========================================================================================== === The following part contains the dual OpenSSL and SSLeay license === === for OpenSSL versions 1.1.1, 1.1.0, 1.0.2 and all prior releases === === (see https://www.openssl.org/source/license.html) === =========================================================================================== LICENSE ISSUES ============== The OpenSSL toolkit stays under a double license, i.e. both the conditions of the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. OpenSSL License --------------- /* ==================================================================== * Copyright (c) 1998-2019 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" * * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please contact * openssl-core@openssl.org. * * 5. Products derived from this software may not be called "OpenSSL" * nor may "OpenSSL" appear in their names without prior written * permission of the OpenSSL Project. * * 6. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the OpenSSL Project * for use in the OpenSSL Toolkit (http://www.openssl.org/)" * * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY * EXPRESSED 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 OpenSSL PROJECT OR * ITS 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. * ==================================================================== * * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). * */ Original SSLeay License ----------------------- /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written * by Eric Young (eay@cryptsoft.com). * The implementation was written so as to conform with Netscapes SSL. * * This library is free for commercial and non-commercial use as long as * the following conditions are aheared to. The following conditions * apply to all code found in this distribution, be it the RC4, RSA, * lhash, DES, etc., code; not just the SSL code. The SSL documentation * included with this distribution is covered by the same copyright terms * except that the holder is Tim Hudson (tjh@cryptsoft.com). * * Copyright remains Eric Young's, and as such any Copyright notices in * the code are not to be removed. * If this package is used in a product, Eric Young should be given attribution * as the author of the parts of the library used. * This can be in the form of a textual message at program startup or * in documentation (online or textual) provided with the package. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * "This product includes cryptographic software written by * Eric Young (eay@cryptsoft.com)" * The word 'cryptographic' can be left out if the rouines from the library * being used are not cryptographic related :-). * 4. If you include any Windows specific code (or a derivative thereof) from * the apps directory (application code) you must include an acknowledgement: * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" * * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. * * The licence and distribution terms for any publically available version or * derivative of this code cannot be changed. i.e. this code cannot simply be * copied and put under another distribution licence * [including the GNU Public Licence.] */ =========================================================================================== === The following part contains the license for the Cyrus SASL 2.x library === === used for optional SASL support === =========================================================================================== /* CMU libsasl * Tim Martin * Rob Earhart * Rob Siemborski */ /* * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/Makefile.am0100644 0000000 0000000 00000013067 15051152474 026335 0ustar00rootroot0000000 0000000 # need this for Doxygen integration include $(top_srcdir)/aminclude.am AUTOMAKE_OPTIONS = serial-tests if SOLARIS SOLARIS_CPPFLAGS = -D_POSIX_PTHREAD_SEMANTICS SOLARIS_LIB_LDFLAGS = -lnsl -lsocket endif if WANT_OPENSSL OPENSSL_CPPFLAGS = -DHAVE_OPENSSL_H OPENSSL_LIB_LDFLAGS = -lssl -lcrypto endif if WANT_SASL SASL_CPPFLAGS = -DHAVE_CYRUS_SASL_H SASL_LIB_LDFLAGS = -lsasl2 SASL_SRC = src/zk_sasl.c endif AM_CPPFLAGS = -I${srcdir}/include -I${srcdir}/tests -I${srcdir}/generated $(SOLARIS_CPPFLAGS) $(OPENSSL_CPPFLAGS) $(SASL_CPPFLAGS) AM_CFLAGS = -Wall -Werror -Wdeclaration-after-statement AM_CXXFLAGS = -Wall $(USEIPV6) LIB_LDFLAGS = -no-undefined -version-info 2 $(SOLARIS_LIB_LDFLAGS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) # Additional flags for coverage testing (if enabled) if ENABLEGCOV AM_CFLAGS += -fprofile-arcs -ftest-coverage AM_LDFLAGS = -lgcov endif pkginclude_HEADERS = include/zookeeper.h include/zookeeper_version.h include/zookeeper_log.h include/proto.h include/recordio.h generated/zookeeper.jute.h EXTRA_DIST=LICENSE 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/zk_log.c src/zk_hashtable.h src/zk_hashtable.c \ src/addrvec.h src/addrvec.c $(SASL_SRC) # 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_|allocate_|zerror|is_unrecoverable)' noinst_LTLIBRARIES += libzkst.la libzkst_la_SOURCES =$(COMMON_SRC) src/st_adaptor.c libzkst_la_LIBADD = -lm $(CLOCK_GETTIME_LIBS) 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 = $(AM_CFLAGS) -DTHREADED libzkmt_la_LIBADD = -lm $(CLOCK_GETTIME_LIBS) 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 $(SASL_LIB_LDFLAGS) if WANT_SYNCAPI bin_PROGRAMS += cli_mt load_gen cli_mt_SOURCES = src/cli.c cli_mt_LDADD = libzookeeper_mt.la $(SASL_LIB_LDFLAGS) cli_mt_CFLAGS = $(AM_CFLAGS) -DTHREADED load_gen_SOURCES = src/load_gen.c load_gen_LDADD = libzookeeper_mt.la load_gen_CFLAGS = $(AM_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 # These tests are ordered in a logical manner such that each builds upon basic # functionality tested in prior tests. e.g. the most basic functionality is # tested in TestZookeeperInit and TestZookeeperClose and as such should be tested # first as a foundation with more complex test suites to follow. TEST_SOURCES = \ tests/TestDriver.cc \ tests/LibCMocks.cc \ tests/LibCSymTable.cc \ tests/MocksBase.cc \ tests/ZKMocks.cc \ tests/Util.cc \ tests/ThreadingUtil.cc \ tests/TestZookeeperInit.cc \ tests/TestZookeeperClose.cc \ tests/TestReconfig.cc \ tests/TestReconfigServer.cc \ tests/TestClientRetry.cc \ tests/TestOperations.cc \ tests/TestMulti.cc \ tests/TestWatchers.cc \ tests/TestClient.cc \ tests/ZooKeeperQuorumServer.cc \ tests/ZooKeeperQuorumServer.h \ tests/TestReadOnlyClient.cc \ tests/TestLogClientEnv.cc \ tests/TestSASLAuth.cc \ $(NULL) if SOLARIS SHELL_SYMBOL_WRAPPERS = cat ${srcdir}/tests/wrappers.opt SYMBOL_WRAPPERS=$(SHELL_SYMBOL_WRAPPERS:sh) else SYMBOL_WRAPPERS=$(shell cat ${srcdir}/tests/wrappers.opt) endif check_PROGRAMS = zktest-st TESTS_ENVIRONMENT = ZKROOT=${srcdir}/../.. \ CLASSPATH=$$CLASSPATH:$$CLOVER_HOME/lib/clover*.jar nodist_zktest_st_SOURCES = $(TEST_SOURCES) zktest_st_LDADD = libzkst.la libhashtable.la $(CPPUNIT_LIBS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) -ldl zktest_st_CXXFLAGS = $(AM_CXXFLAGS) -DUSE_STATIC_LIB $(CPPUNIT_CFLAGS) $(SOLARIS_CPPFLAGS) zktest_st_LDFLAGS = -shared $(SYMBOL_WRAPPERS) $(SOLARIS_LIB_LDFLAGS) if WANT_SYNCAPI check_PROGRAMS += zktest-mt nodist_zktest_mt_SOURCES = $(TEST_SOURCES) tests/PthreadMocks.cc zktest_mt_LDADD = libzkmt.la libhashtable.la -lpthread $(CPPUNIT_LIBS) $(OPENSSL_LIB_LDFLAGS) $(SASL_LIB_LDFLAGS) -ldl zktest_mt_CXXFLAGS = $(AM_CXXFLAGS) -DUSE_STATIC_LIB -DTHREADED $(CPPUNIT_CFLAGS) $(USEIPV6) if SOLARIS SHELL_SYMBOL_WRAPPERS_MT = cat ${srcdir}/tests/wrappers-mt.opt SYMBOL_WRAPPERS_MT=$(SYMBOL_WRAPPERS) $(SHELL_SYMBOL_WRAPPERS_MT:sh) else SYMBOL_WRAPPERS_MT=$(SYMBOL_WRAPPERS) $(shell cat ${srcdir}/tests/wrappers-mt.opt) endif zktest_mt_LDFLAGS = -shared $(SYMBOL_WRAPPERS_MT) $(SOLARIS_LIB_LDFLAGS) endif TESTS = $(check_PROGRAMS) clean-local: clean-check $(RM) $(DX_CLEANFILES) clean-check: $(RM) $(nodist_zktest_st_OBJECTS) $(nodist_zktest_mt_OBJECTS) apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/NOTICE.txt0100644 0000000 0000000 00000003651 15051152474 026021 0ustar00rootroot0000000 0000000 Apache ZooKeeper Copyright 2009-2023 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). ---------- include/winstdint.h is included only for Windows Client support, as follows: // ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// ---------- apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/README0100644 0000000 0000000 00000017351 15051152474 025161 0ustar00rootroot0000000 0000000 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://zookeeper.apache.org/ and our wiki, at: https://cwiki.apache.org/confluence/display/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 zookeeper-client/zookeeper-client-c. Skip to step 3. 2) unzip/untar the source tarball and cd to the zookeeper-x.x.x/zookeeper-client/zookeeper-client-c directory 3) change directory to zookeeper-client/zookeeper-client-c and do a "autoreconf -if" to bootstrap autoconf, automake and libtool. Please make sure you have autoconf version 2.59 or greater installed. If cppunit is installed in a non-standard directory, you need to specify where to find cppunit.m4. For example, if cppunit is installed under /usr/local, run: ACLOCAL="aclocal -I /usr/local/share/aclocal" autoreconf -if 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 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" Alternatively you can use the CMake build system. On Windows, this is required. Follow steps 1 and 2 above, and then continue here. 1) do a "cmake [OPTIONS]" to generate the makefile or msbuild files (the correct build system will be generated based on your platform). Some options from above are supported: -DCMAKE_BUILD_TYPE Debug by default, Release enables optimzation etc. -DWANT_SYNCAPI ON by default, OFF disables the Sync API support -DWANT_CPPUNIT ON except on Windows, OFF disables the tests -DWITH_OPENSSL ON by default, OFF disables the SSL support. You can also specify a custom path by -DWITH_OPENSSL=/path/to/openssl/ -DWITH_CYRUS_SASL ON by default, OFF disables SASL support. You can also specify a custom path by -DWITH_CYRUS_SASL=/path/to/cyrus-sasl/ -DBUILD_SHARED_LIBS not yet supported, only static libraries are built other CMake options see "cmake --help" for generic options, such as generator 2) do a "cmake --build ." to build the default targets. Alternatively you can invoke "make" or "msbuild" manually. If the tests were enabled, use "ctest -V" to run them. Current limitations of the CMake build system include lack of Solaris support, no shared library option, no explicitly exported symbols (all are exported by default), no versions on the libraries, and no documentation generation. Features of CMake include a single, easily consumed cross-platform build system to generate the ZooKeeper C Client libraries for any project, with little to no configuration. EXAMPLE/SAMPLE C CLIENT SHELL NOTE: the ZooKeeper C client shell (cli_st and cli_mt) is meant as a example/sample of ZooKeeper C client API usage. It is not a full fledged client and not meant for production usage - see the Java client shell for a fully featured shell. 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 To start a client with read-only mode enabled, use the -r flag: $ cli_mt -r 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) apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/acinclude.m40100644 0000000 0000000 00000027137 15051152474 026475 0ustar00rootroot0000000 0000000 # 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 of 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 ]) # CHECK_CPPUNIT # ------------------ # Check for cppunit presence. AC_DEFUN([CHECK_CPPUNIT], [ ifdef( [AM_PATH_CPPUNIT], [AM_PATH_CPPUNIT($1)], [ifdef( [PKG_CHECK_MODULES], [PKG_CHECK_MODULES([CPPUNIT], [cppunit >= $1])], [m4_fatal([Missing AM_PATH_CPPUNIT or PKG_CHECK_MODULES m4 macro.])] )] ) ]) apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/aminclude.am0100644 0000000 0000000 00000011175 15051152474 026557 0ustar00rootroot0000000 0000000 # 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 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/c-doc.Doxyfile0100644 0000000 0000000 00000143152 15051152474 026772 0ustar00rootroot0000000 0000000 # 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 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/cmake_config.h.in0100644 0000000 0000000 00000011372 15051152474 027461 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CONFIG_H_ #define CONFIG_H_ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ARPA_INET_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H 1 /* Define to 1 if you have the file `generated/zookeeper.jute.c'. */ #cmakedefine HAVE_GENERATED_ZOOKEEPER_JUTE_C 1 /* Define to 1 if you have the file `generated/zookeeper.jute.h'. */ #cmakedefine HAVE_GENERATED_ZOOKEEPER_JUTE_H 1 /* Define to 1 if you have the `getcwd' function. */ #cmakedefine HAVE_GETCWD 1 /* Define to 1 if you have the `gethostbyname' function. */ #cmakedefine HAVE_GETHOSTBYNAME 1 /* Define to 1 if you have the `gethostname' function. */ #cmakedefine HAVE_GETHOSTNAME 1 /* Define to 1 if you have the `getlogin' function. */ #cmakedefine HAVE_GETLOGIN 1 /* Define to 1 if you have the `getpwuid_r' function. */ #cmakedefine HAVE_GETPWUID_R 1 /* Define to 1 if you have the `gettimeofday' function. */ #cmakedefine HAVE_GETTIMEOFDAY 1 /* Define to 1 if you have the `getuid' function. */ #cmakedefine HAVE_GETUID 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H 1 /* Define to 1 if you have the `rt' library (-lrt). */ #cmakedefine HAVE_LIBRT 1 /* Define to 1 if you have the `memmove' function. */ #cmakedefine HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MEMORY_H 1 /* Define to 1 if you have the `memset' function. */ #cmakedefine HAVE_MEMSET 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETDB_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_IN_H 1 /* Define to 1 if you have the `poll' function. */ #cmakedefine HAVE_POLL 1 /* Define to 1 if you have the `socket' function. */ #cmakedefine HAVE_SOCKET 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H 1 /* Define to 1 if you have the `strchr' function. */ #cmakedefine HAVE_STRCHR 1 /* Define to 1 if you have the `strdup' function. */ #cmakedefine HAVE_STRDUP 1 /* Define to 1 if you have the `strerror' function. */ #cmakedefine HAVE_STRERROR 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H 1 /* Define to 1 if you have the `strtol' function. */ #cmakedefine HAVE_STRTOL 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKET_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_UTSNAME_H 1 /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H 1 /* Define to 1 if IPv6 support is available. */ #cmakedefine ZOO_IPV6_ENABLED 1 /* Define to 1 if SOCK_CLOEXEC is available and wanted */ #cmakedefine SOCK_CLOEXEC_ENABLED 1 /* poll() second argument type */ #define POLL_NFDS_TYPE nfds_t /* Name of package */ #define PACKAGE "${PROJECT_NAME}" /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "${email}" /* Define to the full name of this package. */ #define PACKAGE_NAME "${description}" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "${description} ${PROJECT_VERSION}" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "${PROJECT_NAME}" /* Define to the version of this package. */ #define PACKAGE_VERSION "${PROJECT_VERSION}" /* Version number of package */ #define VERSION "${PROJECT_VERSION}" #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/configure.ac0100644 0000000 0000000 00000017313 15051152474 026565 0ustar00rootroot0000000 0000000 # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) AC_INIT([zookeeper C client],3.9.4,[user@zookeeper.apache.org],[zookeeper]) AC_CONFIG_SRCDIR([src/zookeeper.c]) # Save initial CFLAGS and CXXFLAGS values before AC_PROG_CC and AC_PROG_CXX 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 CHECK_CPPUNIT(1.10.2) fi dnl OpenSSL AC_ARG_WITH(openssl, [AC_HELP_STRING([--with-openssl[=DIR]], [build with openssl (autodetect openssl library by default) )])], [], [with_openssl=yes]) AC_MSG_NOTICE([configuring SSL using --with-openssl=$with_openssl]) saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" if test "x$with_openssl" != "xno" && test "x$with_openssl" != "xyes" ; then CPPFLAGS="$CPPFLAGS -I$with_openssl/include" LDFLAGS="$LDFLAGS -L$with_openssl/lib" fi have_openssl=no AC_CHECK_HEADER(openssl/ssl.h, [ AC_CHECK_LIB(ssl, SSL_CTX_new, [have_openssl=yes]) ]) if test "x$with_openssl" != "xno" && test "x$with_openssl" != "xyes" && test "x$have_openssl" != "xyes"; then CPPFLAGS="$saved_CPPFLAGS" LDFLAGS="$saved_LDFLAGS" fi if test "x$with_openssl" != xno && test "x$have_openssl" = xno; then AC_MSG_WARN([cannot build SSL support -- openssl not found]) with_openssl=no fi if test "x$with_openssl" != xno; then AC_MSG_NOTICE([building with SSL support]) else AC_MSG_NOTICE([building without SSL support]) fi AM_CONDITIONAL([WANT_OPENSSL],[test "x$with_openssl" != xno]) if test "$CALLER" = "ANT" ; then CPPUNIT_CFLAGS="$CPPUNIT_CFLAGS -DZKSERVER_CMD=\"\\\"${base_dir}/zookeeper-client/zookeeper-client-c/tests/zkServer.sh\\\"\"" else CPPUNIT_CFLAGS="$CPPUNIT_CFLAGS -DZKSERVER_CMD=\"\\\"./tests/zkServer.sh\\\"\"" AC_CHECK_FILES([$srcdir/generated/zookeeper.jute.c $srcdir/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 # Check whether to enable gcov (coverage test) AC_ARG_ENABLE(gcov, [AS_HELP_STRING([--enable-gcov],[enable coverage test])]) AC_MSG_CHECKING([whether to enable gcov]) AS_IF([test "x${enable_gcov}" = "xyes"],AC_MSG_RESULT([yes]),AC_MSG_RESULT([no])) AM_CONDITIONAL([ENABLEGCOV],[test "x${enable_gcov}" = "xyes"]) CXXFLAGS="$CXXFLAGS -std=c++11" 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]) dnl Cyrus SASL 2.x AC_ARG_WITH(sasl, [AC_HELP_STRING([--with-sasl[=DIR]], [build with SASL support via Cyrus SASL 2.x (default=auto)])], [], [with_sasl=yes]) if test "x$with_sasl" != "xno"; then saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" if test "x$with_sasl" != "xyes" ; then CPPFLAGS="$CPPFLAGS -I$with_sasl/include" LDFLAGS="$LDFLAGS -L$with_sasl/lib" fi have_sasl=no AC_CHECK_HEADER(sasl/sasl.h, [ AC_CHECK_LIB(sasl2, sasl_client_init, [have_sasl=yes])]) if test "x$have_sasl" != "xyes"; then CPPFLAGS="$saved_CPPFLAGS" LDFLAGS="$saved_LDFLAGS" fi fi if test "x$with_sasl" != xno && test "x$have_sasl" = xno; then AC_MSG_WARN([cannot build SASL support -- sasl2 not found]) with_sasl=no fi if test "x$with_sasl" != xno; then AC_MSG_NOTICE([building with SASL support]) else AC_MSG_NOTICE([building without SASL support]) fi AM_CONDITIONAL([WANT_SASL],[test "x$with_sasl" != 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 # use SOCK_CLOEXEC if available and wanted AC_ARG_WITH([sock_cloexec], [AS_HELP_STRING([--with-sock-cloexec],[build with SOCK_CLOEXEC flag set on the connections])], [],[with_sock_cloexec=no]) AC_MSG_CHECKING([whether SOCK_CLOEXEC is available]) AC_TRY_RUN([ /* is SOCK_CLOEXEC available ? */ #include #include #include main() { #ifdef SOCK_CLOEXEC exit(0); #else exit(1); #endif } ], AC_MSG_RESULT(yes) has_sock_cloexec=yes, AC_MSG_RESULT(no) has_sock_cloexec=no, AC_MSG_RESULT(no) has_sock_cloexec=no) if test "x$with_sock_cloexec" != xno && test "x$has_sock_cloexec" = xno; then AC_MSG_WARN([cannot use SOCK_CLOEXEC -- SOCK_CLOEXEC undefined on this platform]) with_sock_cloexec=no fi if test "x$with_sock_cloexec" != xno; then AC_MSG_NOTICE([building with SOCK_CLOEXEC]) else AC_MSG_NOTICE([building without SOCK_CLOEXEC]) fi AS_IF([test x"$with_sock_cloexec" != xno], [AC_DEFINE([SOCK_CLOEXEC_ENABLED], [1], [Define to 1, if SOCK_CLOEXEC is defined and wanted])]) AM_CONDITIONAL([SOCK_CLOEXEC_ENABLED],[test "x$with_sock_cloexec" != xno]) # Determine which libraries we need to use clock_gettime saved_LIBS="$LIBS" LIBS="" AC_CHECK_LIB(rt, clock_gettime) CLOCK_GETTIME_LIBS=$LIBS AC_SUBST(CLOCK_GETTIME_LIBS) LIBS="$saved_LIBS" # 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_CANONICAL_HOST AM_CONDITIONAL([SOLARIS],[ case "$host_os" in *solaris*) true ;; *) false ;; esac ]) AC_OUTPUT apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/proto.h0100644 0000000 0000000 00000003074 15051152474 027235 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #define ZOO_NOTIFY_OP 0 #define ZOO_CREATE_OP 1 #define ZOO_DELETE_OP 2 #define ZOO_EXISTS_OP 3 #define ZOO_GETDATA_OP 4 #define ZOO_SETDATA_OP 5 #define ZOO_GETACL_OP 6 #define ZOO_SETACL_OP 7 #define ZOO_GETCHILDREN_OP 8 #define ZOO_SYNC_OP 9 #define ZOO_PING_OP 11 #define ZOO_GETCHILDREN2_OP 12 #define ZOO_CHECK_OP 13 #define ZOO_MULTI_OP 14 #define ZOO_CREATE2_OP 15 #define ZOO_RECONFIG_OP 16 #define ZOO_CHECK_WATCHES 17 #define ZOO_REMOVE_WATCHES 18 #define ZOO_CREATE_CONTAINER_OP 19 #define ZOO_DELETE_CONTAINER_OP 20 #define ZOO_CREATE_TTL_OP 21 #define ZOO_CLOSE_OP -11 #define ZOO_SETAUTH_OP 100 #define ZOO_SETWATCHES_OP 101 #define ZOO_SASL_OP 102 #ifdef __cplusplus } #endif #endif /*PROTO_H_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/recordio.h0100644 0000000 0000000 00000005665 15051152474 027710 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #include /* for int64_t */ #ifdef WIN32 #include "winconfig.h" #endif #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 zoo_htonll(int64_t v); #ifdef __cplusplus } #endif #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/win_getopt.h0100644 0000000 0000000 00000045553 15051152474 030261 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * DISCLAIMER * This file is part of the mingw-w64 runtime package. * * The mingw-w64 runtime package and its code is distributed in the hope that it * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #ifndef __GETOPT_H__ #pragma warning(disable:4996); #define __GETOPT_H__ /* All the headers include this file. */ #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ #ifdef REPLACE_GETOPT int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #undef optreset /* see getopt.h */ #define optreset __mingw_optreset int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #endif //extern int optind; /* index of first non-option in argv */ //extern int optopt; /* single option character, as parsed */ //extern int opterr; /* flag to enable built-in diagnostics... */ // /* (user may set to zero, to suppress) */ // //extern char *optarg; /* pointer to argument of current option */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #ifndef __CYGWIN__ #define __progname __argv[0] #else extern char __declspec(dllimport) *__progname; #endif #ifdef __CYGWIN__ static char EMSG[] = ""; #else #define EMSG "" #endif static int getopt_internal(int, char * const *, const char *, const struct option *, int *, int); static int parse_long_options(char * const *, const char *, const struct option *, int *, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; static void _vwarnx(const char *fmt,va_list ap) { (void)fprintf(stderr,"%s: ",__progname); if (fmt != NULL) (void)vfprintf(stderr,fmt,ap); (void)fprintf(stderr,"\n"); } static void warnx(const char *fmt,...) { va_list ap; va_start(ap,fmt); _vwarnx(fmt,ap); va_end(ap); } /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } #ifdef REPLACE_GETOPT /* * getopt -- * Parse argc/argv argument vector. * * [eventually this will replace the BSD getopt] */ int getopt(int nargc, char * const *nargv, const char *options) { /* * We don't pass FLAG_PERMUTE to getopt_internal() since * the BSD getopt(3) (unlike GNU) has never done this. * * Furthermore, since many privileged programs call getopt() * before dropping privileges it makes sense to keep things * as simple (and bug-free) as possible. */ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } #endif /* REPLACE_GETOPT */ //extern int getopt(int nargc, char * const *nargv, const char *options); #ifdef _BSD_SOURCE /* * BSD adds the non-standard `optreset' feature, for reinitialisation * of `getopt' parsing. We support this feature, for applications which * proclaim their BSD heritage, before including this header; however, * to maintain portability, developers are advised to avoid it. */ # define optreset __mingw_optreset extern int optreset; #endif #ifdef __cplusplus } #endif /* * POSIX requires the `getopt' API to be specified in `unistd.h'; * thus, `unistd.h' includes this header. However, we do not want * to expose the `getopt_long' or `getopt_long_only' APIs, when * included in this manner. Thus, close the standard __GETOPT_H__ * declarations block, and open an additional __GETOPT_LONG_H__ * specific block, only when *not* __UNISTD_H_SOURCED__, in which * to declare the extended API. */ #endif /* !defined(__GETOPT_H__) */ #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) #define __GETOPT_LONG_H__ #ifdef __cplusplus extern "C" { #endif struct option /* specification for a long form option... */ { const char *name; /* option name, without leading hyphens */ int has_arg; /* does it take an argument? */ int *flag; /* where to save its status, or NULL */ int val; /* its associated status value */ }; enum /* permitted values for its `has_arg' field... */ { no_argument = 0, /* option never takes an argument */ required_argument, /* option always requires an argument */ optional_argument /* option may take an argument */ }; /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, ambiguous, match; #define IDENTICAL_INTERPRETATION(_x, _y) \ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ long_options[(_x)].flag == long_options[(_y)].flag && \ long_options[(_x)].val == long_options[(_y)].val) current_argv = place; match = -1; ambiguous = 0; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; ambiguous = 0; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else if (!IDENTICAL_INTERPRETATION(i, match)) ambiguous = 1; } if (ambiguous) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); #undef IDENTICAL_INTERPRETATION } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; if (options == NULL) return (-1); /* * XXX Some GNU programs (like cvs) set optind to 0 instead of * XXX using optreset. Work around this braindamage. */ if (optind == 0) optind = optreset = 1; /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. * * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or * optreset != 0 for GNU compatibility. */ if (posixly_correct == -1 || optreset != 0) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (*options == '-') flags |= FLAG_ALLARGS; else if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; if (*options == '+' || *options == '-') options++; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = (char*)strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } //extern int getopt_long(int nargc, char * const *nargv, const char *options, // const struct option *long_options, int *idx); //extern int getopt_long_only(int nargc, char * const *nargv, const char *options, // const struct option *long_options, int *idx); /* * Previous MinGW implementation had... */ #ifndef HAVE_DECL_GETOPT /* * ...for the long form API only; keep this for compatibility. */ # define HAVE_DECL_GETOPT 1 #endif #ifdef __cplusplus } #endif #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/winconfig.h0100644 0000000 0000000 00000000543 15051152474 030053 0ustar00rootroot0000000 0000000 #ifndef WINCONFIG_H_ #define WINCONFIG_H_ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #define inline __inline #endif #define __attribute__(x) #define __func__ __FUNCTION__ #define ACL ZKACL /* Conflict with windows API */ #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/zookeeper.h0100644 0000000 0000000 00000354027 15051152474 030104 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 /* we must not include config.h as a public header */ #ifndef WIN32 #include #include #endif #ifdef WIN32 #include /* must always be included before ws2tcpip.h */ #include /* for struct sock_addr and socklen_t */ #endif #ifdef HAVE_OPENSSL_H #include #endif #include #include #ifdef HAVE_CYRUS_SASL_H #include #endif /* HAVE_CYRUS_SASL_H */ #include "proto.h" #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 query with the watch * flag set. If the client is ever disconnected from the service, the watches do * not need to be reset. The client automatically resets the 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(WIN32)) && !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 */ ZNEWCONFIGNOQUORUM = -13, /*!< No quorum of new config is connected and up-to-date with the leader of last commmitted config - try invoking reconfiguration after new servers are connected and synced */ ZRECONFIGINPROGRESS = -14, /*!< Reconfiguration requested while another reconfiguration is currently in progress. This is currently not supported. Please retry. */ ZSSLCONNECTIONERROR = -15, /*!< The SSL connection Error */ /** 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 typedef for setting the log callback. It's a function pointer which * returns void and accepts a const char* as its only argument. * * \param message message to be passed to the callback function. */ typedef void (*log_callback_fn)(const char *message); /** * \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); #ifdef HAVE_OPENSSL_H ZOOAPI zhandle_t *zookeeper_init_ssl(const char *host, const char *cert, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags); #endif ZOOAPI void close_zsock(zsock_t *zsock); /** * \brief create a handle to communicate with zookeeper. * * This function is identical to \ref zookeeper_init except it allows one * to specify an additional callback to be used for all logging for that * specific connection. For more details on the logging callback see * \ref zoo_get_log_callback and \ref zoo_set_log_callback. * * 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. * \param log_callback All log messages will be passed to this callback function. * For more details see \ref zoo_get_log_callback and \ref zoo_set_log_callback. * \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_init2(const char *host, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags, log_callback_fn log_callback); #ifdef HAVE_CYRUS_SASL_H /** * \brief zoo_sasl_params structure. * * This structure holds the SASL parameters for the connection. * * Its \c service, \c host and \c callbacks fields are used with Cyrus * SASL's \c sasl_client_new; its \c mechlist field with \c * sasl_client_start. Please refer to these functions for precise * semantics. * * Note while "string" parameters are copied into the ZooKeeper * client, the callbacks array is simply referenced: its lifetime must * therefore cover that of the handle. */ typedef struct zoo_sasl_params { const char *service; /*!< The service name, usually "zookeeper" */ const char *host; /*!< The server name, e.g. "zk-sasl-md5" */ const char *mechlist; /*!< Mechanisms to try, e.g. "DIGEST-MD5" */ const sasl_callback_t *callbacks; /*!< List of callbacks */ } zoo_sasl_params_t; /** * \brief create a handle to communicate with zookeeper. * * This function is identical to \ref zookeeper_init2 except that it * allows specifying optional SASL connection parameters. It is only * available if the client library was configured to link against the * Cyrus SASL library, and only visible when \c HAVE_CYRUS_SASL_H is defined. * * 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. * \param log_callback All log messages will be passed to this callback function. * For more details see \ref zoo_get_log_callback and \ref zoo_set_log_callback. * \param sasl_params a pointer to a \ref zoo_sasl_params_t structure * specifying SASL connection parameters, or NULL to skip SASL * authentication * \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_sasl(const char *host, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags, log_callback_fn log_callback, zoo_sasl_params_t *sasl_params); /** * \brief allocates and initializes a basic array of Cyrus SASL callbacks. * * This small helper function makes it easy to pass "static" * parameters to Cyrus SASL's underlying callback-based API. Its use * is not mandatory; you can still implement interactive dialogs by * defining your own callbacks. * * \param user the "canned" response to \c SASL_CB_USER and \c SASL_CB_AUTHNAME, * or NULL for none * \param realm the "canned" response to \c SASL_CB_GETREALM, or NULL for none * \param password_file the name of a file whose first line is read in * response to \c SASL_CB_PASS, or NULL for none * \return the freshly-malloc()ed callbacks array, or NULL if allocation * failed. Deallocate with free(), but only after the corresponding * ZooKeeper handle is closed. */ ZOOAPI sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, const char *realm, const char* password_file); /** * \brief signature of the callback function for SASL password. * * This callback is defined by user to decrypt the content of password file with * context into the actual password. * * \param content the string read from the password file. * \param content_len the size of the content in bytes. * \param context the handback object that will be associated with the password * file. The object is not used by zookeeper internally and can be null. * \param buf the buffer where the resulting actual password is saved. * \param buf_len the size of buf in bytes, which is also the max allowed * password length. * \param passwd_len as an output parameter of the callback function, passwd_len * points to the actual length of the password stored in buf. Its size must not * exceed buf_len; otherwise, SASL_BUFOVER will be returned. * \return SASL_OK, or the possible errors defined by the SASL library. */ typedef int (*zoo_sasl_password_callback_t)(const char *content, size_t content_len, void *context, char *buf, size_t buf_len, size_t *passwd_len); /** * \brief zoo_sasl_password structure. * * This structure holds the parameters for getting the actual SASL password. The * user-defined callback is a processor that decrypts the content of password_file * with context as a handback object helping the decryption into the actual password * as is described above for zoo_sasl_password_callback_t. * * All of password_file, context and callback are allowed to be null; however, once * callback is defined, password_file should not be null since the content processed * by callback is from password_file. If callback is null and password_file is given, * the first line of the file is just the actual password. While both password_file * and callback are null, the password would be read from terminal with prompt. * * zoo_sasl_password is defined by user and used to generate the actual password in * response to \c SASL_CB_PASS. */ typedef struct zoo_sasl_password { const char *password_file; /*!< The path of the password file */ void *context; /*!< The handback object used by callback */ zoo_sasl_password_callback_t callback; /*!< Callback over the content of password file */ } zoo_sasl_password_t; /** * \brief allocates and initializes a basic array of Cyrus SASL callbacks. * * This helper function is similar to zoo_sasl_make_basic_callbacks, except that * the actual password is generated by zoo_sasl_password object rather than just * read from the password file. * * \param user the "canned" response to \c SASL_CB_USER and \c SASL_CB_AUTHNAME, * or NULL for none. * \param realm the "canned" response to \c SASL_CB_GETREALM, or NULL for none. * \param password the object defined by user to specify how the actual password * is generated in response to \c SASL_CB_PASS, should never be NULL (otherwise * the behaviour is undefined), see struct zoo_sasl_password for details. * \return the freshly-malloc()ed callbacks array, or NULL if allocation * failed. Deallocate with free(), but only after the corresponding * ZooKeeper handle is closed. */ ZOOAPI sasl_callback_t *zoo_sasl_make_password_callbacks(const char *user, const char *realm, zoo_sasl_password_t *password); #endif /* HAVE_CYRUS_SASL_H */ /** * \brief update the list of servers this client will connect to. * * This method allows a client to update the connection string by providing * a new comma separated list of host:port pairs, each corresponding to a * ZooKeeper server. * * This function invokes a probabilistic load-balancing algorithm which may cause * the client to disconnect from its current host to achieve expected uniform * connections per server in the new list. In case the current host to which the * client is connected is not in the new list this call will always cause the * connection to be dropped. Otherwise, the decision is based on whether the * number of servers has increased or decreased and by how much. * * If the connection is dropped, the client moves to a special "reconfig" mode * where he chooses a new server to connect to using the probabilistic algorithm. * After finding a server or exhaustively trying all the servers in the new list, * the client moves back to the normal mode of operation where it will pick an * arbitrary server from the 'host' string. * * See {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1355} for the * protocol and its evaluation, * * \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" * \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 * ZSYSTEMERROR -- a system (OS) error occured; it's worth checking errno to get details */ ZOOAPI int zoo_set_servers(zhandle_t *zh, const char *hosts); /** * \brief sets a minimum delay to observe between "routine" host name * resolutions. * * The client performs regular resolutions of the list of servers * passed to \ref zookeeper_init or set with \ref zoo_set_servers in * order to detect changes at the DNS level. * * By default, it does so every time it checks for socket readiness. * This results in low latency in the detection of changes, but can * lead to heavy DNS traffic when the local cache is not effective. * * This method allows an application to influence the rate of polling. * When delay_ms is set to a value greater than zero, the client skips * most "routine" resolutions which would have happened in a window of * that many milliseconds since the last successful one. * * Setting delay_ms to 0 disables this logic, reverting to the default * behavior. Setting it to -1 disables network resolutions during * normal operation (but not, e.g., on connection loss). * * \param delay_ms 0, -1, or the window size in milliseconds * \return ZOK on success or ZBADARGUMENTS for invalid input parameters */ ZOOAPI int zoo_set_servers_resolution_delay(zhandle_t *zh, int delay_ms); /** * \brief cycle to the next server on the next connection attempt. * * Note: typically this method should NOT be used outside of testing. * * This method allows a client to cycle through the list of servers in it's * connection pool to be used on the next connection attempt. This function does * not actually trigger a connection or state change in any way. Its purpose is * to allow testing changing servers on the fly and the probabilistic load * balancing algorithm. */ ZOOAPI void zoo_cycle_next_server(zhandle_t *zh); /** * \brief get current host:port this client is connecting/connected to. * * Note: typically this method should NOT be used outside of testing. * * This method allows a client to get the current host:port that this client * is either in the process of connecting to or is currently connected to. This * is mainly used for testing purposes but might also come in handy as a general * purpose tool to be used by other clients. */ ZOOAPI const char* zoo_get_current_server(zhandle_t* zh); /** * \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 occurred while attempting to send request to server * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details */ ZOOAPI int zookeeper_close(zhandle_t *zh); /** * \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); /** * \brief returns the socket address for the current connection * \return socket address of the connected host or NULL on failure, only valid if the * connection is current connected */ ZOOAPI struct sockaddr* zookeeper_get_connected_host(zhandle_t *zh, struct sockaddr *addr, socklen_t *addr_len); #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 occurred while attempting to establish * a connection to the server * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZOPERATIONTIMEOUT - hasn't received anything from the server for 2/3 of the * timeout value specified in zookeeper_init() * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details */ #ifdef WIN32 ZOOAPI int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest, struct timeval *tv); #else ZOOAPI int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, struct timeval *tv); #endif /** * \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 occurred while attempting to send request to server * ZSESSIONEXPIRED - connection attempt failed -- the session's expired * ZAUTHFAILED - authentication request failed, e.i. invalid credentials * ZRUNTIMEINCONSISTENCY - a server response came out of order * ZSYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details * ZNOTHING -- not an error; simply indicates that there no more data from the server * to be processed (when called with ZOOKEEPER_READ flag). */ 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 string 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 value the value of the string returned. * \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 (*string_stat_completion_t)(int rc, const char *string, const struct Stat *stat, 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 exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, 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 mode this parameter should be one of the Create Modes. * \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 mode, string_completion_t completion, const void *data); /** * \brief create a node. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, a unique monotonically increasing * sequence number is appended to the path name. The sequence number is always fixed * length of 10 digits, 0 padded. When ZOO_*_WITH_TTL is selected, a ttl node will be * created. * * \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 mode this parameter should be one of the Create Modes. * \param ttl the value of ttl in milliseconds. It must be positive for ZOO_*_WITH_TTL * Create modes, otherwise it must be -1. * \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_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, int64_t ttl, string_completion_t completion, const void *data); /** * \brief create a node asynchronously and returns stat details. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, 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 mode this parameter should be one of the Create Modes. * \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_acreate2(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, string_stat_completion_t completion, const void *data); /** * \brief create a node asynchronously and returns stat details. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, a unique monotonically increasing * sequence number is appended to the path name. The sequence number is always fixed * length of 10 digits, 0 padded. When ZOO_*_WITH_TTL is selected, a ttl node will be * created. * * \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 mode this parameter should be one of the Create Modes. * \param ttl the value of ttl in milliseconds. It must be positive for ZOO_*_WITH_TTL * Create modes, otherwise it must be -1. * \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_acreate2_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, int64_t ttl, string_stat_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 gets the last committed configuration of the ZooKeeper cluster as it is known to * the server to which the client is connected. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param watch if nonzero, a watch will be set at the server to notify * the client if the configuration 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 configuration node (/zookeeper/config) does not exist. * ZNOAUTH the client does not have permission to access the configuration node. * \param data the configuration 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_agetconfig(zhandle_t *zh, int watch, data_completion_t completion, const void *data); /** * \brief gets the last committed configuration of the ZooKeeper cluster as it is known to * the server to which the client is connected. * * This function is similar to \ref zoo_agetconfig 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 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 configuration node (/zookeeper/config) does not exist. * ZNOAUTH the client does not have permission to access the configuration node. * \param data the configuration 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_awgetconfig(zhandle_t *zh, watcher_fn watcher, void* watcherCtx, data_completion_t completion, const void *data); /** * \brief asynchronous reconfiguration interface - allows changing ZK cluster * ensemble membership and roles of ensemble peers. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param joining - comma separated list of servers to be added to the ensemble. * Each has a configuration line for a server to be added (as would appear in a * configuration file), only for maj. quorums. NULL for non-incremental reconfiguration. * \param leaving - comma separated list of server IDs to be removed from the ensemble. * Each has an id of a server to be removed, only for maj. quorums. NULL for * non-incremental reconfiguration. * \param members - comma separated list of new membership (e.g., contents of a * membership configuration file) - for use only with a non-incremental * reconfiguration. NULL for incremental reconfiguration. * \param version - version of config from which we want to reconfigure - if * current config is different reconfiguration will fail. Should be -1 to disable * this option. * \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 * \param data the configuration data that will be passed to the completion routine * when the function completes. * \return return value of the function call. * ZOK operation completed successfully * ZBADARGUMENTS - invalid input parameters (one case when this is returned is * when the new config has less than 2 servers) * 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 * ZNEWCONFIGNOQUORUM - no quorum of new config is connected and up-to-date with * the leader of last committed config - try invoking reconfiguration after new servers are connected and synced * ZRECONFIGINPROGRESS - another reconfig is currently in progress */ ZOOAPI int zoo_areconfig(zhandle_t *zh, const char *joining, const char *leaving, const char *members, int64_t version, data_completion_t dc, 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 atomically commits multiple zookeeper operations. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param count the number of operations * \param ops an array of operations to commit * \param results an array to hold the results of the operations * \param completion the routine to invoke when the request completes. The completion * will be triggered with any of the error codes that can that can be returned by the * ops supported by a multi op (see \ref zoo_acreate, \ref zoo_adelete, \ref zoo_aset). * \param data the data that will be passed to the completion routine when * the function completes. * \return the return code for the function call. This can be any of the * values that can be returned by the ops supported by a multi op (see * \ref zoo_acreate, \ref zoo_adelete, \ref zoo_aset). */ ZOOAPI int zoo_amulti(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_result_t *results, 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 occurred */ 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 gets the callback to be used by this connection for logging. * * This is a per-connection logging mechanism that will take priority over * the library-wide default log stream. That is, zookeeper library will first * try to use a per-connection callback if available and if not, will fallback * to using the logging stream. Passing in NULL resets the callback and will * cause it to then fallback to using the logging stream as described in \ref * zoo_set_log_stream. */ ZOOAPI log_callback_fn zoo_get_log_callback(const zhandle_t *zh); /** * \brief sets the callback to be used by the library for logging * * Setting this callback has the effect of overriding the default log stream. * Zookeeper will first try to use a per-connection callback if available * and if not, will fallback to using the logging stream. Passing in NULL * resets the callback and will cause it to then fallback to using the logging * stream as described in \ref zoo_set_log_stream. * * Note: The provided callback will be invoked by multiple threads and therefore * it needs to be thread-safe. */ ZOOAPI void zoo_set_log_callback(zhandle_t *zh, log_callback_fn callback); /** * \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); /** * Type of watches: used to select which type of watches should be removed */ typedef enum { ZWATCHTYPE_CHILD = 1, ZWATCHTYPE_DATA = 2, ZWATCHTYPE_ANY = 3 } ZooWatcherType; /** * \brief removes the watches for the given path and watcher type. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param path the path for which watches will be removed * \param wtype the watcher type to be removed * \param watcher the watcher to be removed, if null all watches for that * path (and watcher type) will be removed * \param watcherCtx the contex associated with the watcher to be removed * \param local whether the watches will be removed locally even if there is * no server connection * \return the return code for the function call. * ZOK - operation completed successfully * ZNOWATCHER - the watcher couldn't be found. * ZINVALIDSTATE - if !local, zhandle state is either ZOO_SESSION_EXPIRED_STATE * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZSYSTEMERROR - a system error occured */ ZOOAPI int zoo_aremove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local, void_completion_t *completion, const void *data); /** * \brief removes all the watches for the given path and watcher type. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param path the path for which watches will be removed * \param wtype the watcher type to be removed * \param local whether the watches will be removed locally even if there is * no server connection * \return the return code for the function call. * ZOK - operation completed successfully * ZNOWATCHER - the watcher couldn't be found. * ZINVALIDSTATE - if !local, zhandle state is either ZOO_SESSION_EXPIRED_STATE * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZSYSTEMERROR - a system error occured */ ZOOAPI int zoo_remove_all_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, int local); /** * \brief removes all the watches for the given path and watcher type. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param path the path for which watches will be removed * \param wtype the watcher type to be removed * \param local whether the watches will be removed locally even if there is * no server connection * \return the return code for the function call. * ZOK - operation completed successfully * ZNOWATCHER - the watcher couldn't be found. * ZINVALIDSTATE - if !local, zhandle state is either ZOO_SESSION_EXPIRED_STATE * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZSYSTEMERROR - a system error occured */ ZOOAPI int zoo_aremove_all_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, int local, void_completion_t *completion, const void *data); #ifdef THREADED /** * \brief create a node synchronously. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, 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. 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 mode this parameter should be one of the Create Modes. * \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. This parameter may be NULL if path_buffer_len = 0. * \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 mode, char *path_buffer, int path_buffer_len); /** * \brief create a node synchronously. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, a unique monotonically increasing * sequence number is appended to the path name. The sequence number is always fixed * length of 10 digits, 0 padded. When ZOO_*_WITH_TTL is selected, a ttl node will be * created. * * \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 mode this parameter should be one of the Create Modes. * \param ttl the value of ttl in milliseconds. It must be positive for ZOO_*_WITH_TTL * Create modes, otherwise it must be -1. * \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. This parameter may be NULL if path_buffer_len = 0. * \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_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, int64_t ttl, char *path_buffer, int path_buffer_len); /** * \brief create a node synchronously and collect stat details. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, 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. 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 mode this parameter should be one of the Create Modes. * \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. This parameter may be NULL if path_buffer_len = 0. * \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. * \param stat The Stat struct to store Stat info into. * \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_create2(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, char *path_buffer, int path_buffer_len, struct Stat *stat); /** * \brief create a node synchronously and collect stat details. * * This method will create a node in ZooKeeper. A node can only be created if * it does not already exist. The Create Mode affects the creation of nodes. * If ZOO_EPHEMERAL mode is chosen, the node will automatically get removed if the * client session goes away. If ZOO_CONTAINER flag is set, a container node will be * created. For ZOO_*_SEQUENTIAL modes, a unique monotonically increasing * sequence number is appended to the path name. The sequence number is always fixed * length of 10 digits, 0 padded. When ZOO_*_WITH_TTL is selected, a ttl node will be * created. * * \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 mode this parameter should be one of the Create Modes. * \param ttl the value of ttl in milliseconds. It must be positive for ZOO_*_WITH_TTL * Create modes, otherwise it must be -1. * \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. This parameter may be NULL if path_buffer_len = 0. * \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. * \param stat The Stat struct to store Stat info into. * \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_create2_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, int64_t ttl, char *path_buffer, int path_buffer_len, struct Stat *stat); /** * \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 gets the last committed configuration of the ZooKeeper cluster as it is known to * the server to which the client is connected, synchronously. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \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 configuration 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 configuration node (/zookeeper/config) does not exist. * ZNOAUTH the client does not have permission to access the configuration node. * 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_getconfig(zhandle_t *zh, int watch, char *buffer, int* buffer_len, struct Stat *stat); /** * \brief gets the last committed configuration of the ZooKeeper cluster as it is known to * the server to which the client is connected, synchronously. * * This function is similar to \ref zoo_getconfig 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 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 configuration 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 configuration node (/zookeeper/config) does not exist. * ZNOAUTH the client does not have permission to access the configuration node. * 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_wgetconfig(zhandle_t *zh, watcher_fn watcher, void* watcherCtx, char *buffer, int* buffer_len, struct Stat *stat); /** * \brief synchronous reconfiguration interface - allows changing ZK cluster * ensemble membership and roles of ensemble peers. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param joining - comma separated list of servers to be added to the ensemble. * Each has a configuration line for a server to be added (as would appear in a * configuration file), only for maj. quorums. NULL for non-incremental reconfiguration. * \param leaving - comma separated list of server IDs to be removed from the ensemble. * Each has an id of a server to be removed, only for maj. quorums. NULL for * non-incremental reconfiguration. * \param members - comma separated list of new membership (e.g., contents of a * membership configuration file) - for use only with a non-incremental * reconfiguration. NULL for incremental reconfiguration. * \param version - zxid of config from which we want to reconfigure - if * current config is different reconfiguration will fail. Should be -1 to * disable this option. * \param buffer the buffer holding the configuration 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 * ZBADARGUMENTS - invalid input parameters (one case when this is returned is * when the new config has less than 2 servers) * 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 * ZNEWCONFIGNOQUORUM - no quorum of new config is connected and up-to-date with * the leader of last committed config - try invoking reconfiguration after new * servers are connected and synced * ZRECONFIGINPROGRESS - another reconfig is currently in progress */ ZOOAPI int zoo_reconfig(zhandle_t *zh, const char *joining, const char *leaving, const char *members, int64_t version, 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); /** * \brief atomically commits multiple zookeeper operations synchronously. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param count the number of operations * \param ops an array of operations to commit * \param results an array to hold the results of the operations * \return the return code for the function call. This can be any of the * values that can be returned by the ops supported by a multi op (see * \ref zoo_acreate, \ref zoo_adelete, \ref zoo_aset). */ ZOOAPI int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_result_t *results); /** * \brief removes the watches for the given path and watcher type. * * \param zh the zookeeper handle obtained by a call to \ref zookeeper_init * \param path the path for which watches will be removed * \param wtype the watcher type to be removed * \param watcher the watcher to be removed, if null all watches for that * path (and watcher type) will be removed * \param watcherCtx the contex associated with the watcher to be removed * \param local whether the watches will be removed locally even if there is * no server connection * \return the return code for the function call. * ZOK - operation completed successfully * ZNOWATCHER - the watcher couldn't be found. * ZINVALIDSTATE - if !local, zhandle state is either ZOO_SESSION_EXPIRED_STATE * or ZOO_AUTH_FAILED_STATE * ZBADARGUMENTS - invalid input parameters * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory * ZSYSTEMERROR - a system error occured */ ZOOAPI int zoo_remove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local); #endif #ifdef __cplusplus } #endif #endif /*ZOOKEEPER_H_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/zookeeper_log.h0100644 0000000 0000000 00000003371 15051152474 030736 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 LOGCALLBACK(_zh) zoo_get_log_callback(_zh) #define LOGSTREAM NULL #define LOG_ERROR(_cb, ...) if(logLevel>=ZOO_LOG_LEVEL_ERROR) \ log_message(_cb, ZOO_LOG_LEVEL_ERROR, __LINE__, __func__, __VA_ARGS__) #define LOG_WARN(_cb, ...) if(logLevel>=ZOO_LOG_LEVEL_WARN) \ log_message(_cb, ZOO_LOG_LEVEL_WARN, __LINE__, __func__, __VA_ARGS__) #define LOG_INFO(_cb, ...) if(logLevel>=ZOO_LOG_LEVEL_INFO) \ log_message(_cb, ZOO_LOG_LEVEL_INFO, __LINE__, __func__, __VA_ARGS__) #define LOG_DEBUG(_cb, ...) if(logLevel==ZOO_LOG_LEVEL_DEBUG) \ log_message(_cb, ZOO_LOG_LEVEL_DEBUG, __LINE__, __func__, __VA_ARGS__) ZOOAPI void log_message(log_callback_fn callback, ZooLogLevel curLevel, int line, const char* funcName, const char* format, ...); FILE* zoo_get_log_stream(); #ifdef __cplusplus } #endif #endif /*ZK_LOG_H_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/include/zookeeper_version.h0100644 0000000 0000000 00000001746 15051152474 031646 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_VERSION "3.9.4" #ifdef __cplusplus } #endif #endif /* ZOOKEEPER_VERSION_H_ */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/pom.xml0100755 0000000 0000000 00000017056 15051152474 025623 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-client 3.9.4 zookeeper-client-c pom Apache ZooKeeper - Client - C ZooKeeper c client org.codehaus.mojo exec-maven-plugin autoreconf process-sources exec ${project.basedir} autoreconf aclocal -I /usr/share/aclocal -if configure process-sources exec ${project.build.directory}/c ${project.basedir}/configure ${project.basedir}/../.. ANT --with-openssl=${c-client-openssl} --with-sasl=${c-client-sasl} --prefix=${project.build.directory}/c ${c-test-coverage-arg} org.apache.maven.plugins maven-antrun-plugin generate-sources generate-sources run build-c-client compile run replace-cclient-files-during-release none run -SNAPSHOT -SNAPSHOT -SNAPSHOT c-test-coverage --enable-gcov cppunit !skipCppUnit org.apache.maven.plugins maven-antrun-plugin test-cppunit test ${skipTests} run apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/addrvec.c0100644 0000000 0000000 00000013462 15051152474 026643 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #ifdef WIN32 #define random rand /* replace POSIX random with Windows rand */ #include /* must always be included before ws2tcpip.h */ #include /* for sockaddr_storage */ #include "winport.h" #endif #include "addrvec.h" #define ADDRVEC_DEFAULT_GROW_AMOUNT 16 void addrvec_init(addrvec_t *avec) { assert(avec); avec->next = 0; avec->count = 0; avec->capacity = 0; avec->data = NULL; } void addrvec_free(addrvec_t *avec) { if (avec == NULL) { return; } avec->next = 0; avec->count = 0; avec->capacity = 0; if (avec->data) { free(avec->data); avec->data = NULL; } } int addrvec_alloc(addrvec_t *avec) { addrvec_init(avec); return addrvec_grow_default(avec); } int addrvec_alloc_capacity(addrvec_t* avec, uint32_t capacity) { addrvec_init(avec); return addrvec_grow(avec, capacity); } int addrvec_grow(addrvec_t *avec, uint32_t grow_amount) { unsigned int old_capacity = 0; struct sockaddr_storage *old_data = NULL; assert(avec); if (grow_amount == 0) { return 0; } // Save off old data and capacity in case there is a realloc failure old_capacity = avec->capacity; old_data = avec->data; avec->capacity += grow_amount; avec->data = realloc(avec->data, sizeof(*avec->data) * avec->capacity); if (avec->data == NULL) { avec->capacity = old_capacity; avec->data = old_data; errno = ENOMEM; return 1; } return 0; } int addrvec_grow_default(addrvec_t *avec) { return addrvec_grow(avec, ADDRVEC_DEFAULT_GROW_AMOUNT); } static int addrvec_grow_if_full(addrvec_t *avec) { assert(avec); if (avec->count == avec->capacity) { int rc = addrvec_grow_default(avec); if (rc != 0) { return rc; } } return 0; } int addrvec_contains(const addrvec_t *avec, const struct sockaddr_storage *addr) { uint32_t i = 0; if (!avec || !addr) { return 0; } for (i = 0; i < avec->count; i++) { if (avec->data[i].ss_family != addr->ss_family) continue; switch (addr->ss_family) { case AF_INET: if (memcmp(&((struct sockaddr_in*)&avec->data[i])->sin_addr, &((struct sockaddr_in*)addr)->sin_addr, sizeof(struct in_addr)) == 0) return 1; break; #ifdef AF_INET6 case AF_INET6: if (memcmp(&((struct sockaddr_in6*)&avec->data[i])->sin6_addr, &((struct sockaddr_in6*)addr)->sin6_addr, sizeof(struct in6_addr)) == 0) return 1; break; #endif default: break; } } return 0; } int addrvec_append(addrvec_t *avec, const struct sockaddr_storage *addr) { int rc = 0; assert(avec); assert(addr); rc = addrvec_grow_if_full(avec); if (rc != 0) { return rc; } // Copy addrinfo into address list memcpy(avec->data + avec->count, addr, sizeof(*addr)); ++avec->count; return 0; } int addrvec_append_addrinfo(addrvec_t *avec, const struct addrinfo *addrinfo) { int rc = 0; assert(avec); assert(addrinfo); rc = addrvec_grow_if_full(avec); if (rc != 0) { return rc; } // Copy addrinfo into address list memcpy(avec->data + avec->count, addrinfo->ai_addr, addrinfo->ai_addrlen); ++avec->count; return 0; } void addrvec_shuffle(addrvec_t *avec) { int i = 0; for (i = avec->count - 1; i > 0; --i) { long int j = random()%(i+1); if (i != j) { struct sockaddr_storage t = avec->data[i]; avec->data[i] = avec->data[j]; avec->data[j] = t; } } } int addrvec_hasnext(const addrvec_t *avec) { return avec->count > 0 && (avec->next < avec->count); } int addrvec_atend(const addrvec_t *avec) { return avec->count > 0 && avec->next >= avec->count; } void addrvec_next(addrvec_t *avec, struct sockaddr_storage *next) { int index; // If we're at the end of the list, then reset index to start if (addrvec_atend(avec)) { avec->next = 0; } if (!addrvec_hasnext(avec)) { if (next) { memset(next, 0, sizeof(*next)); } return; } index = avec->next++; if (next) { *next = avec->data[index]; } } void addrvec_peek(addrvec_t *avec, struct sockaddr_storage *next) { int index = avec->next; if (avec->count == 0) { memset(next, 0, sizeof(*next)); return; } if (addrvec_atend(avec)) { index = 0; } *next = avec->data[index]; } int addrvec_eq(const addrvec_t *a1, const addrvec_t *a2) { uint32_t i = 0; if (a1->count != a2->count) { return 0; } for (i = 0; i < a1->count; ++i) { if (!addrvec_contains(a2, &a1->data[i])) return 0; } return 1; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/addrvec.h0100644 0000000 0000000 00000010356 15051152474 026647 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 ADDRVEC_H_ #define ADDRVEC_H_ #ifndef WIN32 #include #include #include #include #else #include #include #endif /** * This structure represents a list of addresses. It stores the count of the * number of elements that have been inserted via calls to addrvec_append and * addrvec_append_addrinfo. It also has a capacity field for the number of * addresses it has the ability to hold without needing to be enlarged. */ typedef struct _addrvec { unsigned int next; // next index to use unsigned int count; // number of addresses in this list unsigned int capacity; // number of address this list can hold struct sockaddr_storage *data; // list of addresses } addrvec_t; /** * Initialize an addrvec by clearing out all its state. */ void addrvec_init(addrvec_t *avec); /** * Free any memory used internally by an addrvec */ void addrvec_free(addrvec_t *avec); /** * Allocate an addrvec with a default capacity (16) */ int addrvec_alloc(addrvec_t *avec); /** * Allocates an addrvec with a specified capacity */ int addrvec_alloc_capacity(addrvec_t *avec, uint32_t capacity); /** * Grow an addrvec by the specified amount. This will increase the capacity * of the vector and not the contents. */ int addrvec_grow(addrvec_t *avec, uint32_t grow_amount); /** * Similar to addrvec_grow but uses a default growth amount of 16. */ int addrvec_grow_default(addrvec_t *avec); /** * Check if an addrvec contains the specificed sockaddr_storage value. * \returns 1 if it contains the value and 0 otherwise. */ int addrvec_contains(const addrvec_t *avec, const struct sockaddr_storage *addr); /** * Append the given sockaddr_storage pointer into the addrvec. The contents of * the given 'addr' are copied into the addrvec via memcpy. */ int addrvec_append(addrvec_t *avec, const struct sockaddr_storage *addr); /** * Append the given addrinfo pointer into the addrvec. The contents of the given * 'addrinfo' are copied into the addrvec via memcpy. */ int addrvec_append_addrinfo(addrvec_t *avec, const struct addrinfo *addrinfo); /** * Shuffle the addrvec so that it's internal list of addresses are randomized. * Uses random() and assumes it has been properly seeded. */ void addrvec_shuffle(addrvec_t *avec); /** * Determine if the addrvec has a next element (e.g. it's safe to call addrvec_next) * * \returns 1 if it has a next element and 0 otherwise */ int addrvec_hasnext(const addrvec_t *avec); /** * Determine if the addrvec is at the end or not. Specifically, this means a * subsequent call to addrvec_next will loop around to the start again. */ int addrvec_atend(const addrvec_t *avec); /** * Get the next entry from the addrvec and update the associated index. * * If next is NULL, the index will still be updated. * * If the current index points at (or after) the last element in the vector then * it will loop back around and start at the beginning of the list. */ void addrvec_next(addrvec_t *avec, struct sockaddr_storage *next); /** * Retrieves the next entry from the addrvec but doesn't update the index. */ void addrvec_peek(addrvec_t *avec, struct sockaddr_storage *next); /** * Compare two addrvecs for equality. * * \returns 1 if the contents of the two lists are identical and and 0 otherwise. */ int addrvec_eq(const addrvec_t *a1, const addrvec_t *a2); #endif // ADDRVEC_H apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/cli.c0100644 0000000 0000000 00000111146 15051152474 026000 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * cli.c is a example/sample C client shell for ZooKeeper. It contains * basic shell functionality which exercises some of the features of * the ZooKeeper C client API. It is not a full fledged client and is * not meant for production usage - see the Java client shell for a * fully featured shell. */ #include #include #include #include #include #ifndef WIN32 #include #include #include #include #else #include "winport.h" //#include <-- can't include, conflicting definitions of close() int read(int _FileHandle, void * _DstBuf, unsigned int _MaxCharCount); int write(int _Filehandle, const void * _Buf, unsigned int _MaxCharCount); #define ctime_r(tctime, buffer) ctime_s (buffer, 40, tctime) #include "win_getopt.h" // VisualStudio doesn't contain 'getopt' #endif #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 const char *cmd; static const char *cert; 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_READONLY_STATE) return "READONLY_STATE"; if (state == ZOO_EXPIRED_SESSION_STATE) return "EXPIRED_SESSION_STATE"; if (state == ZOO_AUTH_FAILED_STATE) return "AUTH_FAILED_STATE"; return "INVALID_STATE"; } static const char* type2String(int state){ if (state == ZOO_CREATED_EVENT) return "CREATED_EVENT"; if (state == ZOO_DELETED_EVENT) return "DELETED_EVENT"; if (state == ZOO_CHANGED_EVENT) return "CHANGED_EVENT"; if (state == ZOO_CHILD_EVENT) return "CHILD_EVENT"; if (state == ZOO_SESSION_EVENT) return "SESSION_EVENT"; if (state == ZOO_NOTWATCHING_EVENT) return "NOTWATCHING_EVENT"; return "UNKNOWN_EVENT_TYPE"; } void watcher(zhandle_t *zzh, int type, int state, const char *path, void* context) { /* Be careful using zh here rather than zzh - as this may be mt code * the client lib may call the watcher before zookeeper_init returns */ fprintf(stderr, "Watcher %s state = %s", type2String(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; ctime_r(&tmtime, tmtimes); ctime_r(&tctime, tctimes); fprintf(stderr, "\tctime = %s\tczxid=%llx\n" "\tmtime=%s\tmzxid=%llx\n" "\tversion=%x\taversion=%x\n" "\tephemeralOwner = %llx\n", tctimes, _LL_CAST_ stat->czxid, 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_string_completion_free_data(int rc, const char *name, const void *data) { my_string_completion(rc, name, data); free((void*)data); } void my_string_stat_completion(int rc, const char *name, const struct Stat *stat, const void *data) { my_string_completion(rc, name, data); dumpStat(stat); } void my_string_stat_completion_free_data(int rc, const char *name, const struct Stat *stat, const void *data) { my_string_stat_completion(rc, name, stat, data); free((void*)data); } void my_data_completion(int rc, const char *value, int value_len, const struct Stat *stat, const void *data) { struct timeval tv; 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, " create2 [+[e|s|c|t=ttl]] \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, " wexists \n"); fprintf(stderr, " myid\n"); fprintf(stderr, " verbose\n"); fprintf(stderr, " addauth \n"); fprintf(stderr, " config\n"); fprintf(stderr, " reconfig [-file | -members ,... | " " -add ,... | -remove ,...] [-version ]\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; } rc = zoo_aget(zh, line, 1, my_data_completion, strdup(line)); if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } } else if (strcmp(line, "config") == 0) { gettimeofday(&startTime, 0); rc = zoo_agetconfig(zh, 1, my_data_completion, strdup(ZOO_CONFIG_NODE)); if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } } else if (startsWith(line, "reconfig ")) { int syntaxError = 0; char* p = NULL; char* joining = NULL; char* leaving = NULL; char* members = NULL; size_t members_size = 0; int mode = 0; // 0 = not set, 1 = incremental, 2 = non-incremental int64_t version = -1; line += 9; p = strtok (strdup(line)," "); while (p != NULL) { if (strcmp(p, "-add")==0) { p = strtok (NULL," "); if (mode == 2 || p == NULL) { syntaxError = 1; break; } mode = 1; joining = strdup(p); } else if (strcmp(p, "-remove")==0){ p = strtok (NULL," "); if (mode == 2 || p == NULL) { syntaxError = 1; break; } mode = 1; leaving = strdup(p); } else if (strcmp(p, "-members")==0) { p = strtok (NULL," "); if (mode == 1 || p == NULL) { syntaxError = 1; break; } mode = 2; members = strdup(p); } else if (strcmp(p, "-file")==0){ FILE *fp = NULL; p = strtok (NULL," "); if (mode == 1 || p == NULL) { syntaxError = 1; break; } mode = 2; fp = fopen(p, "r"); if (fp == NULL) { fprintf(stderr, "Error reading file: %s\n", p); syntaxError = 1; break; } fseek(fp, 0L, SEEK_END); /* Position to end of file */ members_size = ftell(fp); /* Get file length */ rewind(fp); /* Back to start of file */ members = calloc(members_size + 1, sizeof(char)); if(members == NULL ) { fprintf(stderr, "\nInsufficient memory to read file: %s\n", p); syntaxError = 1; fclose(fp); break; } /* Read the entire file into members * NOTE: -- fread returns number of items successfully read * not the number of bytes. We're requesting one item of * members_size bytes. So we expect the return value here * to be 1. */ if (fread(members, members_size, 1, fp) != 1){ fprintf(stderr, "Error reading file: %s\n", p); syntaxError = 1; fclose(fp); break; } fclose(fp); } else if (strcmp(p, "-version")==0){ p = strtok (NULL," "); if (version != -1 || p == NULL){ syntaxError = 1; break; } #ifdef WIN32 version = _strtoui64(p, NULL, 16); #else version = strtoull(p, NULL, 16); #endif if (version < 0) { syntaxError = 1; break; } } else { syntaxError = 1; break; } p = strtok (NULL," "); } if (syntaxError) return; rc = zoo_areconfig(zh, joining, leaving, members, version, my_data_completion, strdup(line)); free(joining); free(leaving); free(members); 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++; rc = zoo_aset(zh, line, ptr, strlen(ptr), -1, my_stat_completion, strdup(line)); 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 ") || startsWith(line, "create2 ")) { int mode = 0; int64_t ttl_value = -1; int is_create2 = startsWith(line, "create2 "); line += is_create2 ? 8 : 7; if (line[0] == '+') { int ephemeral = 0; int sequential = 0; int container = 0; int ttl = 0; char *p = NULL; line++; while (*line != ' ' && *line != '\0') { switch (*line) { case 'e': ephemeral = 1; break; case 's': sequential = 1; break; case 'c': container = 1; break; case 't': ttl = 1; line++; if (*line != '=') { fprintf(stderr, "Missing ttl value after +t\n"); return; } line++; ttl_value = strtol(line, &p, 10); if (ttl_value <= 0) { fprintf(stderr, "ttl value must be a positive integer\n"); return; } // move back line pointer to the last digit line = p - 1; break; default: fprintf(stderr, "Unknown option: %c\n", *line); return; } line++; } if (ephemeral != 0 && sequential == 0 && container == 0 && ttl == 0) { mode = ZOO_EPHEMERAL; } else if (ephemeral == 0 && sequential != 0 && container == 0 && ttl == 0) { mode = ZOO_PERSISTENT_SEQUENTIAL; } else if (ephemeral != 0 && sequential != 0 && container == 0 && ttl == 0) { mode = ZOO_EPHEMERAL_SEQUENTIAL; } else if (ephemeral == 0 && sequential == 0 && container != 0 && ttl == 0) { mode = ZOO_CONTAINER; } else if (ephemeral == 0 && sequential == 0 && container == 0 && ttl != 0) { mode = ZOO_PERSISTENT_WITH_TTL; } else if (ephemeral == 0 && sequential != 0 && container == 0 && ttl != 0) { mode = ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL; } else { fprintf(stderr, "Invalid mode.\n"); return; } if (*line == ' ') { line++; } } if (line[0] != '/') { fprintf(stderr, "Path must start with /, found: %s\n", line); return; } fprintf(stderr, "Creating [%s] node (mode: %d)\n", line, mode); // { // 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)); // } if (is_create2) { rc = zoo_acreate2_ttl(zh, line, "new", 3, &ZOO_OPEN_ACL_UNSAFE, mode, ttl_value, my_string_stat_completion_free_data, strdup(line)); } else { rc = zoo_acreate_ttl(zh, line, "new", 3, &ZOO_OPEN_ACL_UNSAFE, mode, ttl_value, my_string_completion_free_data, 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; } rc = zoo_adelete(zh, line, -1, my_void_completion, strdup(line)); 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_free_data, strdup(line)); if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } } else if (startsWith(line, "wexists ")) { #ifdef THREADED struct Stat stat; #endif line += 8; if (line[0] != '/') { fprintf(stderr, "Path must start with /, found: %s\n", line); return; } #ifndef THREADED rc = zoo_awexists(zh, line, watcher, (void*) 0, my_stat_completion, strdup(line)); #else rc = zoo_wexists(zh, line, watcher, (void*) 0, &stat); #endif if (rc) { fprintf(stderr, "Error %d for %s\n", rc, line); } } else if (startsWith(line, "exists ")) { #ifdef THREADED struct Stat stat; #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) : 0, NULL, NULL); } } /* * Look for a command in the form 'cmd:command', and store a pointer * to the command (without its prefix) into *buf if found. * * Returns 0 if the argument does not start with the prefix. * Returns 1 in case of success. */ int handleBatchMode(const char* arg, const char** buf) { size_t cmdlen = strlen(arg); if (cmdlen < 4) { // too short return 0; } cmdlen -= 4; if(strncmp("cmd:", arg, 4) != 0){ return 0; } *buf = arg + 4; return 1; } #ifdef THREADED static void millisleep(int ms) { #ifdef WIN32 Sleep(ms); #else /* !WIN32 */ struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; // to nanoseconds nanosleep(&ts, NULL); #endif /* WIN32 */ } #endif /* THREADED */ int main(int argc, char **argv) { static struct option long_options[] = { {"host", required_argument, NULL, 'h'}, //hostPort {"ssl", required_argument, NULL, 's'}, //certificate files {"myid", required_argument, NULL, 'm'}, //myId file {"cmd", required_argument, NULL, 'c'}, //cmd {"readonly", no_argument, NULL, 'r'}, //read-only {"debug", no_argument, NULL, 'd'}, //set log level to DEBUG from the beginning #ifdef HAVE_CYRUS_SASL_H // Parameters for SASL authentication. {"service", required_argument, NULL, 'z'}, {"server-fqdn", required_argument, NULL, 'o'}, //Host used for SASL auth {"mechlist", required_argument, NULL, 'n'}, //SASL mechanism list {"user", required_argument, NULL, 'u'}, //SASL user {"realm", required_argument, NULL, 'l'}, //SASL realm {"password-file", required_argument, NULL, 'p'}, #endif /* HAVE_CYRUS_SASL_H */ {NULL, 0, NULL, 0}, }; #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; int flags; FILE *fh; #ifdef HAVE_CYRUS_SASL_H char *service = "zookeeper"; char *serverFQDN = NULL; char *mechlist = NULL; char *user = NULL; char *realm = NULL; char *passwordFile = NULL; #endif /* HAVE_CYRUS_SASL_H */ int opt; int option_index = 0; verbose = 0; zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); flags = 0; while ((opt = getopt_long(argc, argv, "h:s:m:c:rdz:o:n:u:l:p:", long_options, &option_index)) != -1) { switch (opt) { case 'h': hostPort = optarg; break; case 'm': clientIdFile = optarg; break; case 'r': flags = ZOO_READONLY; break; case 'c': cmd = optarg; batchMode = 1; fprintf(stderr,"Batch mode: %s\n",cmd); break; case 's': cert = optarg; break; case 'd': verbose = 1; zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); fprintf(stderr, "logging level set to DEBUG\n"); break; #ifdef HAVE_CYRUS_SASL_H case 'z': service = optarg; break; case 'o': serverFQDN = optarg; break; case 'n': mechlist = optarg; break; case 'u': user = optarg; break; case 'l': realm = optarg; break; case 'p': passwordFile = optarg; break; #endif /* HAVE_CYRUS_SASL_H */ case '?': if (optopt == 'h') { fprintf (stderr, "Option -%c requires host list.\n", optopt); } else if (isprint (optopt)) { fprintf (stderr, "Unknown option `-%c'.\n", optopt); } else { fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); return 1; } } } if (!hostPort && optind < argc) { /* * getopt_long did not find a '-h ' option. * * The invoker may be using using the "old-style" command * syntax, with positional parameters and "magical" prefixes * such as 'cmd:'; let's see if we can make sense of it. */ hostPort = argv[optind++]; if (optind < argc && !cmd && !clientIdFile) { int batchModeRes = handleBatchMode(argv[optind], &cmd); if (batchModeRes == 1) { batchMode=1; fprintf(stderr, "Batch mode: '%s'\n", cmd); } else { clientIdFile = argv[optind]; } optind++; } } if (!hostPort || optind < argc) { fprintf(stderr, "\nUSAGE: %s -h zk_host_1:port_1,zk_host_2:port_2,... [OPTIONAL ARGS]\n\n" "MANDATORY ARGS:\n" "-h, --host Comma separated list of ZooKeeper host:port pairs\n\n" "OPTIONAL ARGS:\n" "-m, --myid Path to the file contains the client ID\n" "-c, --cmd Command to execute, e.g. ls|ls2|create|create2|od|...\n" #ifdef HAVE_OPENSSL_H "-s, --ssl Comma separated parameters to initiate SSL connection\n" " e.g.: server_cert.crt,client_cert.crt,client_priv_key.pem,passwd\n" #endif #ifdef HAVE_CYRUS_SASL_H "-u, --user SASL user name\n" "-n, --mechlist Comma separated list of SASL mechanisms (GSSAPI and/or DIGEST-MD5)\n" "-o, --server-fqdn SASL server name ('zk-sasl-md5' for DIGEST-MD5; default: reverse DNS lookup)\n" "-p, --password-file File containing the password (recommended for SASL/DIGEST-MD5)\n" "-l, --realm Realm (for SASL/GSSAPI)\n" "-z, --service SASL service parameter (default: 'zookeeper')\n" #endif /* HAVE_CYRUS_SASL_H */ "-r, --readonly Connect in read-only mode\n" "-d, --debug Activate debug logs right from the beginning (you can also use the \n" " command 'verbose' later to activate debug logs in the cli shell)\n\n", argv[0]); #ifdef HAVE_CYRUS_SASL_H fprintf(stderr, "SASL EXAMPLES:\n" "$ %s --mechlist DIGEST-MD5 --user bob --password-file bob.secret --server-fqdn zk-sasl-md5 -h ...\n" "$ %s --mechlist GSSAPI --user bob --realm BOBINC.COM -h ...\n" "Notes:\n" " * SASL and SSL support are currently incompatible (ZOOKEEPER-3482);\n" " * SASL parameters map to Cyrus SASL's _new/_start APIs and callbacks;\n" " * DIGEST-MD5 requires '--server-fqdn zk-sasl-md5' for historical reasons.\n" " * Passwords are obtained via the obsolete 'getpass()' if not provided via '--password-file'.\n" "\n", argv[0], argv[0]); #endif /* HAVE_CYRUS_SASL_H */ fprintf(stderr, "Version: ZooKeeper cli (c client) version %s\n", ZOO_VERSION); return 2; } if (clientIdFile) { 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 zoo_deterministic_conn_order(1); // enable deterministic order #ifdef HAVE_CYRUS_SASL_H /* * We need to disable the deprecation warnings as Apple has * decided to deprecate all of CyrusSASL's functions with OS 10.11 * (see MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also * for covering clang. */ #ifdef __APPLE__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif if (mechlist) { zoo_sasl_params_t sasl_params = { 0 }; int sr; if (cert) { fprintf(stderr, "SASL and SSL support are currently incompatible (ZOOKEEPER-3482); exiting.\n"); return 1; } sr = sasl_client_init(NULL); if (sr != SASL_OK) { fprintf(stderr, "Unable to initialize SASL library: %s\n", sasl_errstring(sr, NULL, NULL)); return 1; } sasl_params.service = service; sasl_params.host = serverFQDN; sasl_params.mechlist = mechlist; sasl_params.callbacks = zoo_sasl_make_basic_callbacks(user, realm, passwordFile); zh = zookeeper_init_sasl(hostPort, watcher, 30000, &myid, NULL, flags, NULL, &sasl_params); if (!zh) { return errno; } } #ifdef __APPLE__ #pragma GCC diagnostic pop #endif #endif /* HAVE_CYRUS_SASL_H */ if (!zh) { #ifdef HAVE_OPENSSL_H if (!cert) { zh = zookeeper_init(hostPort, watcher, 30000, &myid, NULL, flags); } else { zh = zookeeper_init_ssl(hostPort, cert, watcher, 30000, &myid, NULL, flags); } #else zh = zookeeper_init(hostPort, watcher, 30000, &myid, NULL, flags); #endif if (!zh) { return errno; } } #ifdef YCA if(zoo_add_auth(zh,"yca",p,strlen(p),0,0)!=ZOK) return 2; #endif #ifdef THREADED if (batchMode) { processline(cmd); } while(!shutdownThisThing) { int rc, len; if (batchMode) { // We are just waiting for the asynchronous command to complete. millisleep(10); continue; } 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 (rc > 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 (!processed && 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; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/LICENSE.txt0100644 0000000 0000000 00000002727 15051152474 030647 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.c0100644 0000000 0000000 00000021510 15051152474 031112 0ustar00rootroot0000000 0000000 /* 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, hashvalue); 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. */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable.h0100644 0000000 0000000 00000016530 15051152474 031125 0ustar00rootroot0000000 0000000 /* Copyright (C) 2002 Christopher Clark */ #ifndef __HASHTABLE_CWC22_H__ #define __HASHTABLE_CWC22_H__ #ifdef WIN32 #include "winconfig.h" #endif #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. */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_itr.c0100644 0000000 0000000 00000012420 15051152474 031770 0ustar00rootroot0000000 0000000 /* 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; } /*****************************************************************************/ /* 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. */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_itr.h0100644 0000000 0000000 00000010100 15051152474 031766 0ustar00rootroot0000000 0000000 /* 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 */ static 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 */ static 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. */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/hashtable/hashtable_private.h0100644 0000000 0000000 00000005621 15051152474 032656 0ustar00rootroot0000000 0000000 /* 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. */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/load_gen.c0100644 0000000 0000000 00000017367 15051152474 027013 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #ifdef THREADED #include #endif #include #include static zhandle_t *zh; // ***************************************************************************** // 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 || state == ZOO_READONLY_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(LOGSTREAM, "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(LOGSTREAM, "Failed to get children of %s, rc=%d",root,rc); return rc; } for(i=0;i #include #include #include #include #include #ifndef WIN32 #include #include #include #include #endif int zoo_lock_auth(zhandle_t *zh) { return pthread_mutex_lock(&zh->auth_h.lock); } int zoo_unlock_auth(zhandle_t *zh) { return pthread_mutex_unlock(&zh->auth_h.lock); } int lock_buffer_list(buffer_head_t *l) { return pthread_mutex_lock(&l->lock); } int unlock_buffer_list(buffer_head_t *l) { return pthread_mutex_unlock(&l->lock); } int lock_completion_list(completion_head_t *l) { return pthread_mutex_lock(&l->lock); } int unlock_completion_list(completion_head_t *l) { pthread_cond_broadcast(&l->cond); return 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; } #ifdef WIN32 unsigned __stdcall do_io( void * ); unsigned __stdcall do_completion( void * ); int handle_error(zhandle_t* zh, SOCKET sock, char* message) { LOG_ERROR(LOGCALLBACK(zh), "%s. %d",message, WSAGetLastError()); closesocket (sock); return -1; } //--create socket pair for interupting selects. int create_socket_pair(zhandle_t* zh, SOCKET fds[2]) { struct sockaddr_in inaddr; struct sockaddr addr; int yes=1; int len=0; SOCKET lst=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); if (lst == INVALID_SOCKET ){ LOG_ERROR(LOGCALLBACK(zh), "Error creating socket. %d",WSAGetLastError()); return -1; } memset(&inaddr, 0, sizeof(inaddr)); memset(&addr, 0, sizeof(addr)); inaddr.sin_family = AF_INET; inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); inaddr.sin_port = 0; //--system assigns the port if ( setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)) == SOCKET_ERROR ) { return handle_error(zh, lst,"Error trying to set socket option."); } if (bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)) == SOCKET_ERROR){ return handle_error(zh, lst,"Error trying to bind socket."); } if (listen(lst,1) == SOCKET_ERROR){ return handle_error(zh, lst,"Error trying to listen on socket."); } len=sizeof(inaddr); getsockname(lst, &addr,&len); fds[0]=socket(AF_INET, SOCK_STREAM,0); if (connect(fds[0],&addr,len) == SOCKET_ERROR){ return handle_error(zh, lst, "Error while connecting to socket."); } if ((fds[1]=accept(lst,0,0)) == INVALID_SOCKET){ closesocket(fds[0]); return handle_error(zh, lst, "Error while accepting socket connection."); } closesocket(lst); return 0; } #else void *do_io(void *); void *do_completion(void *); #endif int wakeup_io_thread(zhandle_t *zh); #ifdef WIN32 static int set_nonblock(SOCKET fd){ ULONG nonblocking_flag = 1; if (ioctlsocket(fd, FIONBIO, &nonblocking_flag) == 0) return 1; else return -1; } #else 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); } #endif 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(LOGCALLBACK(zh), "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(LOGCALLBACK(zh), "Out of memory"); return -1; } /* We use a pipe for interrupting select() in unix/sol and socketpair in windows. */ #ifdef WIN32 if (create_socket_pair(zh, adaptor_threads->self_pipe) == -1){ LOG_ERROR(LOGCALLBACK(zh), "Can't make a socket."); #else if(pipe(adaptor_threads->self_pipe)==-1) { LOG_ERROR(LOGCALLBACK(zh), "Can't make a pipe %d",errno); #endif 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); pthread_mutex_init(&adaptor_threads->reconfig_lock,0); pthread_mutex_init(&adaptor_threads->watchers_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; #ifndef WIN32 return write(adaptor_threads->self_pipe[1],&c,1)==1? ZOK: ZSYSTEMERROR; #else return send(adaptor_threads->self_pipe[1], &c, 1, 0)==1? ZOK: ZSYSTEMERROR; #endif } 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 */ #ifdef WIN32 int zookeeper_interest(zhandle_t *zh, SOCKET *fd, int *interest, struct timeval *tv); #else int zookeeper_interest(zhandle_t *zh, int *fd, int *interest, struct timeval *tv); #endif int zookeeper_process(zhandle_t *zh, int events); #ifdef WIN32 unsigned __stdcall do_io( void * v) #else void *do_io(void *v) #endif { zhandle_t *zh = (zhandle_t*)v; #ifndef WIN32 struct pollfd fds[2]; struct adaptor_threads *adaptor_threads = zh->adaptor_priv; api_prolog(zh); notify_thread_ready(zh); LOG_DEBUG(LOGCALLBACK(zh), "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; zh->io_count++; 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)){} } #else fd_set rfds, wfds; struct adaptor_threads *adaptor_threads = zh->adaptor_priv; api_prolog(zh); notify_thread_ready(zh); LOG_DEBUG(LOGCALLBACK(zh), "started IO thread"); while(!zh->close_requested) { struct timeval tv; SOCKET fd; int interest; int rc; zookeeper_interest(zh, &fd, &interest, &tv); // FD_ZERO is cheap on Win32, it just sets count of elements to zero. // It needs to be done to ensure no stale entries. FD_ZERO(&rfds); FD_ZERO(&wfds); if (fd != -1) { if (interest&ZOOKEEPER_READ) { FD_SET(fd, &rfds); } if (interest&ZOOKEEPER_WRITE) { FD_SET(fd, &wfds); } } // Always interested in self_pipe. FD_SET(adaptor_threads->self_pipe[0], &rfds); rc = select(/* unused */0, &rfds, &wfds, NULL, &tv); if (rc > 0) { interest=(FD_ISSET(fd, &rfds))? ZOOKEEPER_READ: 0; interest|=(FD_ISSET(fd, &wfds))? ZOOKEEPER_WRITE: 0; if (FD_ISSET(adaptor_threads->self_pipe[0], &rfds)){ // flush the pipe/socket char b[128]; while(recv(adaptor_threads->self_pipe[0],b,sizeof(b), 0)==sizeof(b)){} } } else if (rc < 0) { LOG_ERROR(LOGCALLBACK(zh), ("select() failed %d [%d].", rc, WSAGetLastError())); // Clear interest events for zookeeper_process if select() fails. interest = 0; } #endif // dispatch zookeeper events 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(LOGCALLBACK(zh), "IO thread terminated"); return 0; } #ifdef WIN32 unsigned __stdcall do_completion( void * v) #else void *do_completion(void *v) #endif { zhandle_t *zh = v; api_prolog(zh); notify_thread_ready(zh); LOG_DEBUG(LOGCALLBACK(zh), "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(LOGCALLBACK(zh), "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) { #ifndef WIN32 return __sync_fetch_and_add(operand, incr); #else return InterlockedExchangeAdd(operand, incr); #endif } // 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); } int lock_reconfig(struct _zhandle *zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; if (adaptor) { return pthread_mutex_lock(&adaptor->reconfig_lock); } else { return 0; } } int unlock_reconfig(struct _zhandle *zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; if (adaptor) { return pthread_mutex_unlock(&adaptor->reconfig_lock); } else { return 0; } } int lock_watchers(struct _zhandle *zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; if (adaptor) { return pthread_mutex_lock(&adaptor->watchers_lock); } else { return 0; } } int unlock_watchers(struct _zhandle *zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; if (adaptor) { return pthread_mutex_unlock(&adaptor->watchers_lock); } else { return 0; } } int enter_critical(zhandle_t* zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; if (adaptor) { return pthread_mutex_lock(&adaptor->zh_lock); } else { return 0; } } int leave_critical(zhandle_t* zh) { struct adaptor_threads *adaptor = zh->adaptor_priv; if (adaptor) { return pthread_mutex_unlock(&adaptor->zh_lock); } else { return 0; } } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/recordio.c0100644 0000000 0000000 00000022007 15051152474 027034 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #ifndef WIN32 #include #else #include /* for _htonl and _ntohl */ #endif 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 zoo_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 = zoo_htonll(*d); struct buff_struct *priv = oa->priv; if ((priv->len - priv->off) < sizeof(i)) { int rc = resize_buffer(priv, priv->len + sizeof(i)); 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 = zoo_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 = { ia_start_record, ia_end_record, ia_start_vector, ia_end_vector, ia_deserialize_bool, ia_deserialize_int, ia_deserialize_long , ia_deserialize_buffer, ia_deserialize_string}; static struct oarchive oa_default = { oa_start_record, oa_end_record, oa_start_vector, oa_end_vector, oa_serialize_bool, oa_serialize_int, oa_serialize_long , oa_serialize_buffer, oa_serialize_string}; struct iarchive *create_buffer_iarchive(char *buffer, int len) { struct iarchive *ia; struct buff_struct *buff; ia = malloc(sizeof(*ia)); if (!ia) return 0; buff = malloc(sizeof(struct buff_struct)); if (!buff) { free(ia); return 0; } *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; struct buff_struct *buff; oa = malloc(sizeof(*oa)); if (!oa) return 0; buff = malloc(sizeof(struct buff_struct)); 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; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/st_adaptor.c0100644 0000000 0000000 00000004273 15051152474 027373 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) # define USE_STATIC_LIB #endif #include "zk_adaptor.h" #include #include int zoo_lock_auth(zhandle_t *zh) { return 0; } int zoo_unlock_auth(zhandle_t *zh) { return 0; } int lock_buffer_list(buffer_head_t *l) { return 0; } int unlock_buffer_list(buffer_head_t *l) { return 0; } int lock_completion_list(completion_head_t *l) { return 0; } int unlock_completion_list(completion_head_t *l) { return 0; } 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++; } int lock_reconfig(struct _zhandle *zh) { return 0; } int unlock_reconfig(struct _zhandle *zh) { return 0; } int lock_watchers(struct _zhandle *zh) { return 0; } int unlock_watchers(struct _zhandle *zh) { return 0; } int enter_critical(zhandle_t* zh) { return 0; } int leave_critical(zhandle_t* zh) { return 0; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/winport.c0100644 0000000 0000000 00000022431 15051152474 026731 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 WIN32 #include "winport.h" #include #include /* for int64_t */ #include /* must always be included before ws2tcpip.h */ #include /* for SOCKET */ int pthread_mutex_lock(pthread_mutex_t* _mutex ){ int rc = WaitForSingleObject( *_mutex, // handle to mutex INFINITE); // no time-out interval return ((rc == WAIT_OBJECT_0) ? 0: rc); } int pthread_mutex_unlock( pthread_mutex_t* _mutex ){ int rc = ReleaseMutex(*_mutex); return ((rc != 0)? 0: GetLastError()); } int pthread_mutex_init(pthread_mutex_t* _mutex, void* ignoredAttr){ //use CreateMutex as we are using the HANDLES in pthread_cond *_mutex = CreateMutex( NULL, // default security attributes FALSE, // initially not owned NULL); // unnamed mutex return ((*_mutex == NULL) ? GetLastError() : 0); } int pthread_mutex_destroy(pthread_mutex_t* _mutex) { int rc = CloseHandle(*_mutex); return ((rc != 0)? 0: GetLastError()); } int pthread_create(pthread_t *thread, const pthread_attr_t *attr, unsigned (__stdcall* start_routine)(void* a), void *arg) { int _intThreadId; (*thread).thread_handle = (HANDLE)_beginthreadex( NULL, 0, start_routine , arg, 0, (unsigned int*)&_intThreadId ); (*thread).thread_id = _intThreadId; return (((*thread).thread_handle == 0 ) ? errno : 0 ); } int pthread_equal(pthread_t t1, pthread_t t2){ //Is there a better way to do this? GetThreadId(handle) is only supported Windows 2003 n above. return ((t1.thread_id == t2.thread_id) ? 1:0); } pthread_t pthread_self(){ pthread_t thread_self; thread_self.thread_handle = GetCurrentThread(); thread_self.thread_id = GetCurrentThreadId(); return thread_self; } int pthread_join(pthread_t _thread, void** ignore) { int rc = WaitForSingleObject( _thread.thread_handle, INFINITE ); return ((rc == WAIT_OBJECT_0) ? 0: rc); } int pthread_detach(pthread_t _thread) { int rc = CloseHandle(_thread.thread_handle) ; return (rc != 0) ? 0: GetLastError(); } void pthread_mutexattr_init(pthread_mutexattr_t* ignore){} void pthread_mutexattr_settype(pthread_mutexattr_t* ingore_attr, int ignore){} void pthread_mutexattr_destroy(pthread_mutexattr_t* ignore_attr){} int pthread_cond_init (pthread_cond_t *cv, const pthread_condattr_t * ignore) { cv->waiters_count_ = 0; cv->was_broadcast_ = 0; cv->sema_ = CreateSemaphore (NULL, // no security 0, // initially 0 0x7fffffff, // max count NULL); // unnamed if (cv->sema_ == NULL ) return GetLastError(); InitializeCriticalSection (&cv->waiters_count_lock_); cv->waiters_done_ = CreateEvent (NULL, // no security FALSE, // auto-reset FALSE, // non-signaled initially NULL); // unnamed return (cv->waiters_done_ == NULL) ? GetLastError() : 0; } int pthread_cond_destroy(pthread_cond_t *cond) { CloseHandle( cond->sema_); DeleteCriticalSection(&cond->waiters_count_lock_); return (CloseHandle( cond->waiters_done_ ) == 0)? GetLastError(): 0 ; } int pthread_cond_signal (pthread_cond_t *cv) { int have_waiters; EnterCriticalSection (& (cv->waiters_count_lock_)); have_waiters = cv->waiters_count_ > 0; LeaveCriticalSection (&cv->waiters_count_lock_); // If there aren't any waiters, then this is a no-op. if (have_waiters){ return (ReleaseSemaphore (cv->sema_, 1, 0) == 0 ) ? GetLastError() : 0 ; }else return 0; } int pthread_cond_broadcast (pthread_cond_t *cv) { // This is needed to ensure that and are // consistent relative to each other. int have_waiters = 0; EnterCriticalSection (&cv->waiters_count_lock_); if (cv->waiters_count_ > 0) { // We are broadcasting, even if there is just one waiter... // Record that we are broadcasting, which helps optimize // for the non-broadcast case. cv->was_broadcast_ = 1; have_waiters = 1; } if (have_waiters) { // Wake up all the waiters atomically. ReleaseSemaphore (cv->sema_, cv->waiters_count_, 0); LeaveCriticalSection (&cv->waiters_count_lock_); // Wait for all the awakened threads to acquire the counting // semaphore. WaitForSingleObject (cv->waiters_done_, INFINITE); // This assignment is okay, even without the held // because no other waiter threads can wake up to access it. cv->was_broadcast_ = 0; } else LeaveCriticalSection (&cv->waiters_count_lock_); } int pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex) { int last_waiter; // Avoid race conditions. EnterCriticalSection (&cv->waiters_count_lock_); cv->waiters_count_++; LeaveCriticalSection (&cv->waiters_count_lock_); // This call atomically releases the mutex and waits on the // semaphore until or // are called by another thread. SignalObjectAndWait (*external_mutex, cv->sema_, INFINITE, FALSE); // Reacquire lock to avoid race conditions. EnterCriticalSection (&cv->waiters_count_lock_); // We're no longer waiting... cv->waiters_count_--; // Check to see if we're the last waiter after . last_waiter = cv->was_broadcast_ && cv->waiters_count_ == 0; LeaveCriticalSection (&cv->waiters_count_lock_); // If we're the last waiter thread during this particular broadcast // then let all the other threads proceed. if (last_waiter) // This call atomically signals the event and waits until // it can acquire the . This is required to ensure fairness. SignalObjectAndWait (cv->waiters_done_, *external_mutex, INFINITE, FALSE); else // Always regain the external mutex since that's the guarantee we // give to our callers. WaitForSingleObject (*external_mutex, INFINITE); } int pthread_key_create(pthread_key_t *key, void (*destructor)(void *) ) { int result = 0; pthread_key_t* newkey; if ((newkey = (pthread_key_t*) calloc (1, sizeof (pthread_key_t))) == NULL) { result = ENOMEM; } else if ((newkey->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES) { result = EAGAIN; free (newkey); newkey = NULL; } else if (destructor != NULL) { //--we have to store the function pointer for destructor, so that we can call it //--to free up the user allocated storage-- newkey->destructor = destructor; } key = newkey; return (result); } int pthread_key_delete(pthread_key_t key) { int rc = 0; LPVOID lpvData = TlsGetValue(key.key); rc = TlsFree (key.key); rc = (rc != 0 ) ? 0 : GetLastError(); if (key.destructor != NULL && lpvData != 0){ key.destructor(lpvData); //we take control of calling destructor, instead of calling it on thread exit. } free (&key); return (rc); } void *pthread_getspecific(pthread_key_t key) { LPVOID lpvData = TlsGetValue(key.key); if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS)) return NULL; else return lpvData; } int pthread_setspecific(pthread_key_t key, const void *value) { int rc = TlsSetValue (key.key, value); return ((rc != 0 ) ? 0 : GetLastError()); } int gettimeofday(struct timeval *tp, void *tzp) { int64_t now = 0; if (tzp != 0) { errno = EINVAL; return -1; } GetSystemTimeAsFileTime( (LPFILETIME)&now ); tp->tv_sec = (long)(now / 10000000 - 11644473600LL); tp->tv_usec = (now / 10) % 1000000; return 0; } int close(SOCKET fd) { return closesocket(fd); } int Win32WSAStartup() { WORD wVersionRq; WSADATA wsaData; int err; wVersionRq = MAKEWORD(2,0); err = WSAStartup(wVersionRq, &wsaData); if (err != 0) return 1; // confirm the version information if ((LOBYTE(wsaData.wVersion) != 2) || (HIBYTE(wsaData.wVersion) != 0)) { Win32WSACleanup(); return 1; } return 0; } void Win32WSACleanup() { WSACleanup(); } double drand48(void) { return (double)(rand()) / RAND_MAX; } #endif //WIN32 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/winport.h0100644 0000000 0000000 00000010520 15051152474 026732 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 header file is to port pthread lib , sockets and other utility methods on windows. * Specifically the threads function, mutexes, keys, and socket initialization. */ #ifndef WINPORT_H_ #define WINPORT_H_ #ifdef WIN32 #include "winconfig.h" #define _WINSOCK_DEPRECATED_NO_WARNINGS #include /* must always be included before ws2tcpip.h */ #include /* for struct sock_addr used in zookeeper.h */ /* POSIX names are deprecated, use ISO conformant names instead. */ #define strdup _strdup #define getcwd _getcwd #define getpid _getpid /* Windows "secure" versions of POSIX reentrant functions */ #define strtok_r strtok_s #define localtime_r(a,b) localtime_s(b,a) /* After this version of MSVC, snprintf became a defined function, and so cannot be redefined, nor can #ifndef be used to guard it. */ #if ((defined(_MSC_VER) && _MSC_VER < 1900) || !defined(_MSC_VER)) #define snprintf _snprintf #endif #include #include #include /* for int64_t */ #include #include typedef int ssize_t; typedef HANDLE pthread_mutex_t; struct pthread_t_ { HANDLE thread_handle; DWORD thread_id; }; typedef struct pthread_t_ pthread_t; typedef int pthread_mutexattr_t; typedef int pthread_condattr_t; typedef int pthread_attr_t; #define PTHREAD_MUTEX_RECURSIVE 0 int pthread_mutex_lock(pthread_mutex_t* _mutex ); int pthread_mutex_unlock( pthread_mutex_t* _mutex ); int pthread_mutex_init(pthread_mutex_t* _mutex, void* ignoredAttr); int pthread_mutex_destroy(pthread_mutex_t* _mutex); int pthread_create(pthread_t *thread, const pthread_attr_t *attr, unsigned (__stdcall* start_routine)(void* a), void *arg); int pthread_equal(pthread_t t1, pthread_t t2); pthread_t pthread_self(); int pthread_join(pthread_t _thread, void** ignore); int pthread_detach(pthread_t _thread); void pthread_mutexattr_init(pthread_mutexattr_t* ignore); void pthread_mutexattr_settype(pthread_mutexattr_t* ingore_attr, int ignore); void pthread_mutexattr_destroy(pthread_mutexattr_t* ignore_attr); // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html typedef struct { int waiters_count_; // Number of waiting threads. CRITICAL_SECTION waiters_count_lock_; // Serialize access to . HANDLE sema_; // Semaphore used to queue up threads waiting for the condition to // become signaled. HANDLE waiters_done_; // An auto-reset event used by the broadcast/signal thread to wait // for all the waiting thread(s) to wake up and be released from the // semaphore. size_t was_broadcast_; // Keeps track of whether we were broadcasting or signaling. This // allows us to optimize the code if we're just signaling. }pthread_cond_t; int pthread_cond_init (pthread_cond_t *cv,const pthread_condattr_t * ignore); int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_signal (pthread_cond_t *cv); int pthread_cond_broadcast (pthread_cond_t *cv); int pthread_cond_wait (pthread_cond_t *cv, pthread_mutex_t *external_mutex); struct pthread_key_t_ { DWORD key; void (*destructor) (void *); }; typedef struct pthread_key_t_ pthread_key_t; int pthread_key_create(pthread_key_t *key, void (*destructor)(void *) ); int pthread_key_delete(pthread_key_t key); void *pthread_getspecific(pthread_key_t key); int pthread_setspecific(pthread_key_t key, const void *value); int gettimeofday(struct timeval *tp, void *tzp); int close(SOCKET fd); int Win32WSAStartup(); void Win32WSACleanup(); double drand48(void); #endif //WIN32 #endif //WINPORT_H_ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zk_adaptor.h0100644 0000000 0000000 00000025773 15051152474 027406 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #ifndef WIN32 #include #else #include "winport.h" #endif #endif #include "zookeeper.h" #include "zk_hashtable.h" #include "addrvec.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 #define READONLY_STATE_DEF 5 #define SSL_CONNECTING_STATE_DEF 7 #define NOTCONNECTED_STATE_DEF 999 /* 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; int lock_buffer_list(buffer_head_t *l); int unlock_buffer_list(buffer_head_t *l); int lock_completion_list(completion_head_t *l); int unlock_completion_list(completion_head_t *l); struct sync_completion { int rc; 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 45 /* connect request */ struct connect_req { int32_t protocolVersion; int64_t lastZxidSeen; int32_t timeOut; int64_t sessionId; int32_t passwd_len; char passwd[16]; char readOnly; }; /* 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]; char readOnly; }; #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 pthread_mutex_t reconfig_lock; // lock for reconfiguring cluster's ensemble pthread_mutex_t watchers_lock; // lock for watcher operations #ifdef WIN32 SOCKET self_pipe[2]; #else int self_pipe[2]; #endif }; #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; typedef struct _zoo_sasl_client zoo_sasl_client_t; /** * This structure represents the connection to zookeeper. */ struct _zhandle { zsock_t *fd; // Hostlist and list of addresses char *hostname; // hostname contains list of zookeeper servers to connect to struct sockaddr_storage addr_cur; // address of server we're currently connecting/connected to struct sockaddr_storage addr_rw_server; // address of last known read/write server found. addrvec_t addrs; // current list of addresses we're connected to addrvec_t addrs_old; // old list of addresses that we are no longer connected to addrvec_t addrs_new; // new list of addresses to connect to if we're reconfiguring struct timeval last_resolve; // time of last hostname resolution int resolve_delay_ms; // see zoo_set_servers_resolution_delay int reconfig; // Are we in the process of reconfiguring cluster's ensemble double pOld, pNew; // Probability for selecting between 'addrs_old' and 'addrs_new' int delay; int disable_reconnection_attempt; // When set, client will not try reconnect to a different server in // server list. This makes a sticky server for client, and is useful // for testing if a sticky server is required, or if client wants to // explicitly shuffle server by calling zoo_cycle_next_server. // The default value is 0. watcher_fn watcher; // the registered watcher // Message timings struct timeval last_recv; // time last message was received struct timeval last_send; // time last message was sent struct timeval last_ping; // time last PING was sent struct timeval next_deadline; // time of the next deadline int recv_timeout; // max receive timeout for messages from server // Buffers buffer_list_t *input_buffer; // current buffer being read in buffer_head_t to_process; // buffers that have been read and ready to be processed buffer_head_t to_send; // packets queued to send completion_head_t sent_requests; // outstanding requests completion_head_t completions_to_process; // completions that are ready to run int outstanding_sync; // number of outstanding synchronous requests /* read-only mode specific fields */ struct timeval last_ping_rw; /* The last time we checked server for being r/w */ int ping_rw_timeout; /* The time that can go by before checking next server */ // State info volatile int state; // Current zookeeper state void *context; // client-side provided context clientid_t client_id; // client-id long long last_zxid; // last zookeeper ID auth_list_head_t auth_h; // authentication data list log_callback_fn log_callback; // Callback for logging (falls back to logging to stderr) int io_count; // counts the number of iterations of do_io // Primer storage 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[41]; // the true size of primer_storage /* 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; // Watchers 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; #ifdef HAVE_CYRUS_SASL_H zoo_sasl_client_t *sasl_client; #endif /* HAVE_CYRUS_SASL_H */ /** Indicates if this client is allowed to go to r/o mode */ char allow_read_only; /** Indicates if we connected to a majority server before */ char seen_rw_server_before; }; int adaptor_init(zhandle_t *zh); void adaptor_finish(zhandle_t *zh); void adaptor_destroy(zhandle_t *zh); #if THREADED 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); #endif 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); int zoo_lock_auth(zhandle_t *zh); int zoo_unlock_auth(zhandle_t *zh); // ensemble reconfigure access guards int lock_reconfig(struct _zhandle *zh); int unlock_reconfig(struct _zhandle *zh); // watchers hashtable lock int lock_watchers(struct _zhandle *zh); int unlock_watchers(struct _zhandle *zh); // critical section guards int enter_critical(zhandle_t* zh); int leave_critical(zhandle_t* zh); // zhandle object reference counting void api_prolog(zhandle_t* zh); int api_epilog(zhandle_t *zh, int rc); 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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zk_hashtable.c0100644 0000000 0000000 00000032504 15051152474 027670 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; const char* cstr = (const char*)str; while ((c = *cstr++)) 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. * Don't clone the watcher since it's allocated on the heap --- avoids * a memory leak and saves a clone operation (calloc + copy). */ res = add_to_list(&wl, wo, 0); } 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)); } } } /* If watcher is NULL, we return TRUE since we consider it a match */ static int containsWatcher(zk_hashtable *watchers, const char *path, watcher_fn watcher, void *watcherCtx) { watcher_object_list_t *wl; watcher_object_t e; if (!watcher) return 1; wl = hashtable_search(watchers->ht, (void *)path); if (!wl) return 0; e.watcher = watcher; e.context = watcherCtx; return search_watcher(&wl, &e) ? 1 : 0; } /** * remove any watcher_object that has a matching (watcher, watcherCtx) */ static void removeWatcherFromList(watcher_object_list_t *wl, watcher_fn watcher, void *watcherCtx) { watcher_object_t *e = NULL; if (!wl || (wl && !wl->head)) return; e = wl->head; while (e){ if (e->next && e->next->watcher == watcher && e->next->context == watcherCtx) { watcher_object_t *this = e->next; e->next = e->next->next; free(this); break; } e = e->next; } if (wl->head && wl->head->watcher == watcher && wl->head->context == watcherCtx) { watcher_object_t *this = wl->head; wl->head = wl->head->next; free(this); } } static void removeWatcher(zk_hashtable *watchers, const char *path, watcher_fn watcher, void *watcherCtx) { watcher_object_list_t *wl = hashtable_search(watchers->ht, (void *)path); if (!wl) return; if (!watcher) { wl = (watcher_object_list_t *) hashtable_remove(watchers->ht, (void *)path); destroy_watcher_object_list(wl); return; } removeWatcherFromList(wl, watcher, watcherCtx); if (!wl->head) { wl = (watcher_object_list_t *) hashtable_remove(watchers->ht, (void *)path); destroy_watcher_object_list(wl); } } void deactivateWatcher(zhandle_t *zh, watcher_deregistration_t *dereg, int rc) { if (rc != ZOK || !dereg) return; removeWatchers(zh, dereg->path, dereg->type, dereg->watcher, dereg->context); } void removeWatchers(zhandle_t *zh, const char* path, ZooWatcherType type, watcher_fn watcher, void *watcherCtx) { switch (type) { case ZWATCHTYPE_CHILD: removeWatcher(zh->active_child_watchers, path, watcher, watcherCtx); break; case ZWATCHTYPE_DATA: removeWatcher(zh->active_node_watchers, path, watcher, watcherCtx); removeWatcher(zh->active_exist_watchers, path, watcher, watcherCtx); break; case ZWATCHTYPE_ANY: removeWatcher(zh->active_child_watchers, path, watcher, watcherCtx); removeWatcher(zh->active_node_watchers, path, watcher, watcherCtx); removeWatcher(zh->active_exist_watchers, path, watcher, watcherCtx); break; } } int pathHasWatcher(zhandle_t *zh, const char *path, int wtype, watcher_fn watcher, void *watcherCtx) { int watcher_found = 0; switch (wtype) { case ZWATCHTYPE_CHILD: watcher_found = containsWatcher(zh->active_child_watchers, path, watcher, watcherCtx); break; case ZWATCHTYPE_DATA: watcher_found = containsWatcher(zh->active_node_watchers, path, watcher, watcherCtx); if (!watcher_found) { watcher_found = containsWatcher(zh->active_exist_watchers, path, watcher, watcherCtx); } break; case ZWATCHTYPE_ANY: watcher_found = containsWatcher(zh->active_child_watchers, path, watcher, watcherCtx); if (!watcher_found) { watcher_found = containsWatcher(zh->active_node_watchers, path, watcher, watcherCtx); } if (!watcher_found) { watcher_found = containsWatcher(zh->active_exist_watchers, path, watcher, watcherCtx); } break; } return watcher_found; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zk_hashtable.h0100644 0000000 0000000 00000006103 15051152474 027671 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; /** * A watcher deregistration gets temporarily stored with the completion entry until * the server response comes back at which moment we can remove the watchers from * the active watchers map. */ typedef struct _watcher_deregistration { watcher_fn watcher; void* context; ZooWatcherType type; const char* path; } watcher_deregistration_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); void deactivateWatcher(zhandle_t *zh, watcher_deregistration_t *dereg, 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); void removeWatchers(zhandle_t *zh, const char* path, ZooWatcherType type, watcher_fn watcher, void *watcherCtx); int pathHasWatcher(zhandle_t *zh, const char *path, int wtype, watcher_fn watcher, void *watcherCtx); #ifdef __cplusplus } #endif #endif /*ZK_HASHTABLE_H_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zk_log.c0100644 0000000 0000000 00000011532 15051152474 026514 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) # define USE_STATIC_LIB #endif #include "zookeeper_log.h" #ifndef WIN32 #include #else typedef DWORD pid_t; #include /* for getpid */ #endif #include #include #define TIME_NOW_BUF_SIZE 1024 #define FORMAT_LOG_BUF_SIZE 4096 #ifdef THREADED #ifndef WIN32 #include #else #include "winport.h" #endif 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* zoo_get_log_stream(){ if(logStream==0) logStream=stderr; return logStream; } void zoo_set_log_stream(FILE* stream){ logStream=stream; } static const char* time_now(char* now_str){ struct timeval tv; struct tm lt; time_t now = 0; size_t len = 0; gettimeofday(&tv,0); now = tv.tv_sec; localtime_r(&now, <); // clone the format used by logback ISO8601DateFormat // specifically: "yyyy-MM-dd HH:mm:ss,SSS" len = strftime(now_str, TIME_NOW_BUF_SIZE, "%Y-%m-%d %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(log_callback_fn callback, ZooLogLevel curLevel, int line, const char* funcName, const char* format, ...) { static const char* dbgLevelStr[]={"ZOO_INVALID","ZOO_ERROR","ZOO_WARN", "ZOO_INFO","ZOO_DEBUG"}; static pid_t pid=0; va_list va; int ofs = 0; #ifdef THREADED unsigned long int tid = 0; #endif #ifdef WIN32 char timebuf [TIME_NOW_BUF_SIZE]; const char* time = time_now(timebuf); #else const char* time = time_now(get_time_buffer()); #endif char* buf = get_format_log_buffer(); if(!buf) { fprintf(stderr, "log_message: Unable to allocate memory buffer"); return; } if(pid==0) { pid=getpid(); } #ifndef THREADED // pid_t is long on Solaris ofs = snprintf(buf, FORMAT_LOG_BUF_SIZE, "%s:%ld:%s@%s@%d: ", time, (long)pid, dbgLevelStr[curLevel], funcName, line); #else #ifdef WIN32 tid = (unsigned long int)(pthread_self().thread_id); #else tid = (unsigned long int)(pthread_self()); #endif ofs = snprintf(buf, FORMAT_LOG_BUF_SIZE-1, "%s:%ld(0x%lx):%s@%s@%d: ", time, (long)pid, tid, dbgLevelStr[curLevel], funcName, line); #endif // Now grab the actual message out of the variadic arg list va_start(va, format); vsnprintf(buf+ofs, FORMAT_LOG_BUF_SIZE-1-ofs, format, va); va_end(va); if (callback) { callback(buf); } else { fprintf(zoo_get_log_stream(), "%s\n", buf); fflush(zoo_get_log_stream()); } } 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; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zk_sasl.c0100644 0000000 0000000 00000041470 15051152474 026701 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "config.h" #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include "zk_sasl.h" #include "zk_adaptor.h" #include "zookeeper_log.h" /* * We need to disable the deprecation warnings as Apple has decided to * deprecate all of CyrusSASL's functions with OS 10.11 (see * MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also for * covering clang. */ #ifdef __APPLE__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif /* * Store a duplicate of src, or NULL, into *target. Returns * ZSYSTEMERROR if no memory could be allocated, ZOK otherwise. */ static int _zsasl_strdup(const char **target, const char *src) { if (src) { *target = strdup(src); if (!*target) { return ZSYSTEMERROR; } } return ZOK; } /* * Free the malloc'ed memory referenced by *location, setting * *location to NULL. */ static void _zsasl_free(const char **location) { if (*location) { free((void*)*location); *location = NULL; } } zoo_sasl_client_t *zoo_sasl_client_create(zoo_sasl_params_t *sasl_params) { zoo_sasl_client_t *sc = calloc(1, sizeof(*sc)); int rc = ZOK; if (!sc) { return NULL; } sc->state = ZOO_SASL_INITIAL; rc = rc < 0 ? rc : _zsasl_strdup(&sc->params.service, sasl_params->service); rc = rc < 0 ? rc : _zsasl_strdup(&sc->params.host, sasl_params->host); rc = rc < 0 ? rc : _zsasl_strdup(&sc->params.mechlist, sasl_params->mechlist); sc->params.callbacks = sasl_params->callbacks; if (rc != ZOK) { zoo_sasl_client_destroy(sc); return NULL; } return sc; } void zoo_sasl_client_destroy(zoo_sasl_client_t *sc) { if (!sc) { return; } if (sc->conn) { sasl_dispose(&sc->conn); } sc->params.callbacks = NULL; _zsasl_free(&sc->params.service); _zsasl_free(&sc->params.host); _zsasl_free(&sc->params.mechlist); sc->state = ZOO_SASL_FAILED; } void zoo_sasl_mark_failed(zhandle_t *zh) { if (zh->sasl_client) { zh->sasl_client->state = ZOO_SASL_FAILED; } zh->state = ZOO_AUTH_FAILED_STATE; } /* * Put the handle and SASL client in failed state if rc is not ZOK. * Returns rc. */ static int _zsasl_fail(zhandle_t *zh, int rc) { if (rc != ZOK) { zoo_sasl_mark_failed(zh); LOG_ERROR(LOGCALLBACK(zh), "SASL authentication failed. rc=%d", rc); } return rc; } /* * Get and format the host and port associated with a socket address * into Cyrus SASL format. Optionally also store the host name in * provided buffer. * * \param addr the socket address * \param salen the length of addr * \param ipport_buf the formatted output buffer, of size * NI_MAXHOST + NI_MAXSERV * \param opt_host_buf the host name buffer, of size NI_MAXHOST, or * NULL for none * \return ZOK if successful */ static int _zsasl_getipport(zhandle_t *zh, const struct sockaddr *addr, socklen_t salen, char *ipport_buf, char *opt_host_buf) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; int niflags, error, written; niflags = (NI_NUMERICHOST | NI_NUMERICSERV); #ifdef NI_WITHSCOPEID if (addr->sa_family == AF_INET6) { niflags |= NI_WITHSCOPEID; } #endif error = getnameinfo(addr, salen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), niflags); if (error != 0) { LOG_ERROR(LOGCALLBACK(zh), "getnameinfo: %s\n", gai_strerror(error)); return ZSYSTEMERROR; } written = sprintf(ipport_buf, "%s;%s", hbuf, pbuf); if (written < 0) { return ZSYSTEMERROR; } if (opt_host_buf) { memcpy(opt_host_buf, hbuf, sizeof(hbuf)); } return ZOK; } int zoo_sasl_connect(zhandle_t *zh) { zoo_sasl_client_t *sc = zh->sasl_client; char iplocalport[NI_MAXHOST + NI_MAXSERV]; char ipremoteport[NI_MAXHOST + NI_MAXSERV]; char host[NI_MAXHOST]; int rc, sr; socklen_t salen; struct sockaddr_storage local_ip, remote_ip; if (!sc) { return _zsasl_fail(zh, ZINVALIDSTATE); } if (sc->conn) { sasl_dispose(&sc->conn); } sc->state = ZOO_SASL_INITIAL; /* set ip addresses */ salen = sizeof(local_ip); if (getsockname(zh->fd->sock, (struct sockaddr *)&local_ip, &salen) < 0) { LOG_ERROR(LOGCALLBACK(zh), "getsockname"); return _zsasl_fail(zh, ZSYSTEMERROR); } rc = _zsasl_getipport(zh, (const struct sockaddr *)&local_ip, salen, iplocalport, NULL); if (rc < 0) { return _zsasl_fail(zh, rc); } salen = sizeof(remote_ip); if (getpeername(zh->fd->sock, (struct sockaddr *)&remote_ip, &salen) < 0) { LOG_ERROR(LOGCALLBACK(zh), "getpeername"); return _zsasl_fail(zh, ZSYSTEMERROR); } rc = _zsasl_getipport(zh, (const struct sockaddr *)&remote_ip, salen, ipremoteport, host); if (rc < 0) { return _zsasl_fail(zh, rc); } LOG_DEBUG(LOGCALLBACK(zh), "Zookeeper Host: %s %s", iplocalport, ipremoteport); /* client new connection */ sr = sasl_client_new( sc->params.service, sc->params.host ? sc->params.host : host, iplocalport, ipremoteport, sc->params.callbacks, /*secflags*/0, &sc->conn); if (sr != SASL_OK) { LOG_ERROR(LOGCALLBACK(zh), "allocating SASL connection state: %s", sasl_errstring(sr, NULL, NULL)); return _zsasl_fail(zh, ZSYSTEMERROR); } return ZOK; } int zoo_sasl_client_start(zhandle_t *zh) { zoo_sasl_client_t *sc = zh->sasl_client; const char *chosenmech; const char *client_data; unsigned client_data_len; int sr, rc = ZOK; if (!sc || sc->state != ZOO_SASL_INITIAL) { return _zsasl_fail(zh, ZINVALIDSTATE); } sc->state = ZOO_SASL_INTERMEDIATE; sr = sasl_client_start(sc->conn, sc->params.mechlist, NULL, &client_data, &client_data_len, &chosenmech); if (sr != SASL_OK && sr != SASL_CONTINUE) { LOG_ERROR(LOGCALLBACK(zh), "Starting SASL negotiation: %s %s", sasl_errstring(sr, NULL, NULL), sasl_errdetail(sc->conn)); return _zsasl_fail(zh, ZSYSTEMERROR); } LOG_DEBUG(LOGCALLBACK(zh), "SASL start sr:%d mech:%s client_data_len:%d", sr, chosenmech, (int)client_data_len); /* * HACK: Without this, the SASL client is unable to reauthenticate * with the ZooKeeper ensemble after a disconnect. This is due to * a bug in the JDK's implementation of SASL DIGEST-MD5; the * upstream issue is: * * JDK-6682540, Incorrect SASL DIGEST-MD5 behavior * https://bugs.openjdk.java.net/browse/JDK-6682540 * * A patch has been committed to the JDK in May 2019, but it will * take a while to appear in production: * * http://hg.openjdk.java.net/jdk/jdk/rev/0627b8ad33c1 * * As a workaround, we just "empty" the client start in DIGEST-MD5 * mode, forcing the server to proceed with initial (re)authentication. */ if (client_data_len > 0 && strcmp(chosenmech, "DIGEST-MD5") == 0) { LOG_DEBUG(LOGCALLBACK(zh), "SASL start %s: refusing reauthenticate", chosenmech); client_data = NULL; client_data_len = 0; } /* * ZooKeeperSaslClient.java:285 says: * * GSSAPI: server sends a final packet after authentication * succeeds or fails. * * so we need to keep track of that. */ if (strcmp(chosenmech, "GSSAPI") == 0) { sc->is_gssapi = 1; } if (sr == SASL_CONTINUE || client_data_len > 0) { rc = queue_sasl_request(zh, client_data, client_data_len); if (rc < 0) { return _zsasl_fail(zh, rc); } } return rc; } int zoo_sasl_client_step(zhandle_t *zh, const char *server_data, int server_data_len) { zoo_sasl_client_t *sc = zh->sasl_client; const char *client_data; unsigned client_data_len; int sr, rc = ZOK; if (!sc || sc->state != ZOO_SASL_INTERMEDIATE) { return _zsasl_fail(zh, ZINVALIDSTATE); } LOG_DEBUG(LOGCALLBACK(zh), "SASL intermediate server_data_len:%d", server_data_len); if (sc->is_gssapi && sc->is_last_packet) { /* See note in zoo_sasl_client_start. */ sc->is_last_packet = 0; sc->state = ZOO_SASL_COMPLETE; return rc; } sr = sasl_client_step(sc->conn, server_data, server_data_len, NULL, &client_data, &client_data_len); LOG_DEBUG(LOGCALLBACK(zh), "SASL intermediate sr:%d client_data_len:%d", sr, (int)client_data_len); if (sr != SASL_OK && sr != SASL_CONTINUE) { LOG_ERROR(LOGCALLBACK(zh), "During SASL negotiation: %s %s", sasl_errstring(sr, NULL, NULL), sasl_errdetail(sc->conn)); return _zsasl_fail(zh, ZSYSTEMERROR); } if (sr == SASL_CONTINUE || client_data_len > 0) { rc = queue_sasl_request(zh, client_data, client_data_len); if (rc < 0) { return _zsasl_fail(zh, rc); } } if (sr != SASL_CONTINUE) { if (sc->is_gssapi) { /* See note in zoo_sasl_client_start. */ sc->is_last_packet = 1; } else { sc->state = ZOO_SASL_COMPLETE; } } return rc; } /* * Cyrus SASL callback for SASL_CB_GETREALM */ static int _zsasl_getrealm(void *context, int id, const char **availrealms, const char **result) { const char *realm = (const char*)context; *result = realm; return SASL_OK; } /* * Cyrus SASL callback for SASL_CB_USER or SASL_CB_AUTHNAME */ static int _zsasl_simple(void *context, int id, const char **result, unsigned *len) { const char *user = (const char*)context; /* paranoia check */ if (!result) return SASL_BADPARAM; switch (id) { case SASL_CB_USER: *result = user; break; case SASL_CB_AUTHNAME: *result = user; break; default: return SASL_BADPARAM; } return SASL_OK; } #ifndef HAVE_GETPASSPHRASE static char * getpassphrase(const char *prompt) { return getpass(prompt); } #endif /* ! HAVE_GETPASSPHRASE */ struct zsasl_secret_ctx { const char *password_file; void *context; zoo_sasl_password_callback_t callback; sasl_secret_t *secret; }; /* * Cyrus SASL callback for SASL_CB_PASS */ static int _zsasl_getsecret(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret) { /* Max allowed length of a password */ const size_t MAX_PASSWORD_LEN = 1023; struct zsasl_secret_ctx *secret_ctx = (struct zsasl_secret_ctx *)context; /* The extra 1 byte is reserved for storing the null terminator. */ char buf[MAX_PASSWORD_LEN + 1]; char *password = NULL; size_t len = 0; int res = 0; /* The extra 1 byte is reserved for storing the null terminator. */ char new_passwd[MAX_PASSWORD_LEN + 1]; char *p = NULL; sasl_secret_t *x; /* paranoia check */ if (!conn || !psecret || id != SASL_CB_PASS) { return SASL_BADPARAM; } if (secret_ctx->password_file) { FILE *fh = fopen(secret_ctx->password_file, "rt"); if (!fh) { return SASL_FAIL; } /* * The file's content may be the encrypted password with binary characters, * thus use fread(). */ len = fread(buf, sizeof(buf[0]), MAX_PASSWORD_LEN, fh); if (ferror(fh)) { fclose(fh); fh = NULL; return SASL_FAIL; } fclose(fh); fh = NULL; /* * Write the null terminator immediately after the last character of the * content since it would be used as a null-terminated string once it is * the actual password. */ buf[len] = '\0'; password = buf; } if (secret_ctx->callback) { if (!password) { /* * The callback takes effect only when password_file is provided. */ return SASL_BADPARAM; } res = secret_ctx->callback(password, len, secret_ctx->context, new_passwd, MAX_PASSWORD_LEN, &len); if (res != SASL_OK) { return res; } if (len > MAX_PASSWORD_LEN) { return SASL_BUFOVER; } /* * Append the null terminator to the end of the password obtained from * the callback function. */ new_passwd[len] = '\0'; password = new_passwd; } else if (secret_ctx->password_file) { /* * The file's content is the actual password, which must consist only of * text characters (i.e., without null terminator). The first line would * be read as the password once there are multiple lines in the file. */ p = strchr(password, '\n'); if (p) { *p = '\0'; } } else { password = getpassphrase("Password: "); if (!password) { return SASL_FAIL; } } /* * Any password, regardless of its source, is always null-terminated. */ len = strlen(password); x = secret_ctx->secret = (sasl_secret_t *)realloc( secret_ctx->secret, sizeof(sasl_secret_t) + len); if (!x) { memset(password, 0, len); return SASL_NOMEM; } x->len = len; /* The extra 1 byte is the null terminator. */ memcpy(x->data, password, len + 1); memset(password, 0, len); *psecret = x; return SASL_OK; } typedef int (* sasl_callback_fn_t)(void); sasl_callback_t *zoo_sasl_make_password_callbacks(const char *user, const char *realm, zoo_sasl_password_t *password) { struct zsasl_secret_ctx *secret_ctx; const char *user_ctx = NULL; const char *realm_ctx = NULL; int rc; secret_ctx = (struct zsasl_secret_ctx *)calloc( 1, sizeof(struct zsasl_secret_ctx)); rc = secret_ctx ? ZOK : ZSYSTEMERROR; rc = rc < 0 ? rc : _zsasl_strdup(&user_ctx, user); rc = rc < 0 ? rc : _zsasl_strdup(&realm_ctx, realm); rc = rc < 0 ? rc : _zsasl_strdup(&secret_ctx->password_file, password->password_file); secret_ctx->context = password->context; secret_ctx->callback = password->callback; { sasl_callback_t callbacks[] = { { SASL_CB_GETREALM, (sasl_callback_fn_t)&_zsasl_getrealm, (void*)realm_ctx }, { SASL_CB_USER, (sasl_callback_fn_t)&_zsasl_simple, (void*)user_ctx }, { SASL_CB_AUTHNAME, (sasl_callback_fn_t)&_zsasl_simple, (void*)user_ctx }, { SASL_CB_PASS, (sasl_callback_fn_t)&_zsasl_getsecret, (void*)secret_ctx }, { SASL_CB_LIST_END, NULL, NULL } }; sasl_callback_t *xcallbacks = rc < 0 ? NULL : malloc(sizeof(callbacks)); if (rc < 0 || !xcallbacks) { if (secret_ctx) { _zsasl_free(&secret_ctx->password_file); free(secret_ctx); secret_ctx = NULL; } _zsasl_free(&realm_ctx); _zsasl_free(&user_ctx); return NULL; } memcpy(xcallbacks, callbacks, sizeof(callbacks)); return xcallbacks; } } sasl_callback_t *zoo_sasl_make_basic_callbacks(const char *user, const char *realm, const char* password_file) { zoo_sasl_password_t password = {password_file, NULL, NULL}; return zoo_sasl_make_password_callbacks(user, realm, &password); } #ifdef __APPLE__ #pragma GCC diagnostic pop #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zk_sasl.h0100644 0000000 0000000 00000012010 15051152474 026672 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_SASL_H_ #define ZK_SASL_H_ #ifdef __cplusplus extern "C" { #endif /** * \brief enumerates SASL authentication states. Corresponds to * org.apache.zookeeper.client.ZooKeeperSaslClient.SaslState. */ typedef enum { ZOO_SASL_INITIAL, ZOO_SASL_INTERMEDIATE, ZOO_SASL_COMPLETE, ZOO_SASL_FAILED } ZooSaslState; /** * \brief zoo_sasl_client_t structure. * * This structure holds (a copy of) the original SASL parameters, the * Cyrus SASL client "object," and the current authentication state. * * See \ref zoo_sasl_client_create and \ref zoo_sasl_client_destroy. */ typedef struct _zoo_sasl_client { zoo_sasl_params_t params; sasl_conn_t *conn; ZooSaslState state; unsigned char is_gssapi; unsigned char is_last_packet; } zoo_sasl_client_t; /** * \brief allocates a \ref zoo_sasl_client_t "object." * * \param sasl_params The SASL parameters to use. Note while "string" * parameters are copied, the callbacks array is simply referenced: * its lifetime must therefore cover that of the handle. * \return the client object, or NULL on failure */ zoo_sasl_client_t *zoo_sasl_client_create(zoo_sasl_params_t *sasl_params); /** * \brief "destroys" a \ref zoo_sasl_client_t "object" allocated by * \ref zoo_sasl_client_create. * * \param sasl_client the client "object" */ void zoo_sasl_client_destroy(zoo_sasl_client_t *sasl_client); /** * \brief put the handle and SASL client in failed state. * * This sets the SASL client in \ref ZOO_SASL_FAILED state and the * ZooKeeper handle in \ref ZOO_AUTH_FAILED_STATE state. * * \param zh the ZooKeeper handle to mark */ void zoo_sasl_mark_failed(zhandle_t *zh); /** * \brief prepares the SASL connection object for the (connecting) * ZooKeeper handle. * * The client is switched to \ref ZOO_SASL_INITIAL state, or \ref * ZOO_SASL_FAILED in case of error. * * \param zh the ZooKeeper handle in \ref ZOO_CONNECTING_STATE state * \return ZOK on success, or one of the following on failure: * ZINVALIDSTATE - no SASL client present * ZSYSTEMERROR - SASL library error */ int zoo_sasl_connect(zhandle_t *zh); /** * \brief queues an encoded SASL request to ZooKeeper. * * Note that such packets are added to the front of the queue, * pre-empting "normal" communications. * * \param zh the ZooKeeper handle * \param client_data the encoded SASL data, ready to send * \param client_data_len the length of \c client_data * \return ZOK on success, or ZMARSHALLINGERROR if something went wrong */ int queue_sasl_request(zhandle_t *zh, const char *client_data, int client_data_len); /** * \brief starts a new SASL authentication session using the * parameters provided to \ref zoo_sasl_client_create * * On entry, the client must be in \ref ZOO_SASL_INITIAL state; this * call switches it to \ref ZOO_SASL_INTERMEDIATE state or \ref * ZOO_SASL_FAILED in case of error. * * Note that this is not a "normal" ZK client function; the * corresponding packets are added to the front of the queue, * pre-empting other requests. * * \param zh the ZooKeeper handle, with the SASL client in * \ref ZOO_SASL_INITIAL state * \return ZOK on success, or one of the following on failure: * ZINVALIDSTATE - client not in expected state * ZSYSTEMERROR - SASL library error * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory */ int zoo_sasl_client_start(zhandle_t *zh); /** * \brief performs a step in the SASL authentication process. * * On entry, the client must be in \ref ZOO_SASL_INTERMEDIATE * state. This call switches it to \ref ZOO_SASL_COMPLETE state if * (and only if) the process is complete--or to \ref ZOO_SASL_FAILED * in case of error. * * \param zh the ZooKeeper handle, with the SASL client in * \ref ZOO_SASL_INTERMEDIATE state * \param server_data SASL data from the ZooKeeper server * \param server_data_len length of \c server_data * \return ZOK on success, or one of the following on failure: * ZINVALIDSTATE - client not in expected state * ZSYSTEMERROR - SASL library error * ZMARSHALLINGERROR - failed to marshall a request; possibly, out of memory */ int zoo_sasl_client_step(zhandle_t *zh, const char *server_data, int server_data_len); #ifdef __cplusplus } #endif #endif /*ZK_SASL_H_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/src/zookeeper.c0100644 0000000 0000000 00000540471 15051152474 027243 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 !defined(DLL_EXPORT) && !defined(USE_STATIC_LIB) # define USE_STATIC_LIB #endif #if defined(__CYGWIN__) #define USE_IPV6 #endif #include "config.h" #include #include #include #include "zk_adaptor.h" #include "zookeeper_log.h" #include "zk_hashtable.h" #ifdef HAVE_CYRUS_SASL_H #include "zk_sasl.h" #endif /* HAVE_CYRUS_SASL_H */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_POLL #include #endif #ifdef HAVE_NETINET_IN_H #include #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_UNISTD_H #include // needed for _POSIX_MONOTONIC_CLOCK #endif #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_GETPWUID_R #include #endif #ifdef HAVE_OPENSSL_H #include #include #endif #ifdef __MACH__ // OS X #include #include #include #endif #ifdef WIN32 #include /* for getpid */ #include /* for getcwd */ #define EAI_ADDRFAMILY WSAEINVAL /* is this still needed? */ #define EHOSTDOWN EPIPE #define ESTALE ENODEV #endif #define IF_DEBUG(x) if(logLevel==ZOO_LOG_LEVEL_DEBUG) {x;} const int ZOOKEEPER_WRITE = 1 << 0; const int ZOOKEEPER_READ = 1 << 1; const int ZOO_PERSISTENT = 0; const int ZOO_EPHEMERAL = 1; const int ZOO_PERSISTENT_SEQUENTIAL = 2; const int ZOO_EPHEMERAL_SEQUENTIAL = 3; const int ZOO_CONTAINER = 4; const int ZOO_PERSISTENT_WITH_TTL = 5; const int ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL = 6; #define ZOOKEEPER_IS_SEQUENCE(mode) \ ((mode) == ZOO_PERSISTENT_SEQUENTIAL || \ (mode) == ZOO_EPHEMERAL_SEQUENTIAL || \ (mode) == ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL) #define ZOOKEEPER_IS_TTL(mode) \ ((mode) == ZOO_PERSISTENT_WITH_TTL || \ (mode) == ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL) // keep ZOO_SEQUENCE as a bitmask for compatibility reasons const int ZOO_SEQUENCE = 1 << 1; #define ZOO_MAX_TTL 0xFFFFFFFFFFLL 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; const int ZOO_READONLY_STATE = READONLY_STATE_DEF; const int ZOO_SSL_CONNECTING_STATE = SSL_CONNECTING_STATE_DEF; const int ZOO_NOTCONNECTED_STATE = NOTCONNECTED_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 SSL_CONNECTING_STATE_DEF: return "ZOO_SSL_CONNECTING_STATE"; case ASSOCIATING_STATE_DEF: return "ZOO_ASSOCIATING_STATE"; case CONNECTED_STATE_DEF: return "ZOO_CONNECTED_STATE"; case READONLY_STATE_DEF: return "ZOO_READONLY_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 #define COMPLETION_MULTI 7 #define COMPLETION_STRING_STAT 8 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 { int type; /* one of COMPLETION_* values above */ 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; string_stat_completion_t string_stat_result; struct watcher_object_list *watcher_result; }; completion_head_t clist; /* For multi-op */ } completion_t; typedef struct _completion_list { int xid; completion_t c; const void *data; buffer_list_t *buffer; struct _completion_list *next; watcher_registration_t* watcher; watcher_deregistration_t* watcher_deregistration; } completion_list_t; const char*err2string(int err); static inline int calculate_interval(const struct timeval *start, const struct timeval *end); static int queue_session_event(zhandle_t *zh, int state); static const char* format_endpoint_info(const struct sockaddr_storage* ep); /* deserialize forward declarations */ static void deserialize_response(zhandle_t *zh, int type, int xid, int failed, int rc, completion_list_t *cptr, struct iarchive *ia); static int deserialize_multi(zhandle_t *zh, int xid, completion_list_t *cptr, struct iarchive *ia); /* 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, completion_head_t *clist); static int add_completion_deregistration(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, int add_to_front, watcher_deregistration_t* wo, completion_head_t *clist); static int do_add_completion(zhandle_t *zh, const void *dc, completion_list_t *c, int add_to_front); static completion_list_t* create_completion_entry(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, watcher_registration_t* wo, completion_head_t *clist); static completion_list_t* create_completion_entry_deregistration(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, watcher_deregistration_t* wo, completion_head_t *clist); static completion_list_t* do_create_completion_entry(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, watcher_registration_t* wo, completion_head_t *clist, watcher_deregistration_t* wdo); 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, const char *func, 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 struct sockaddr_storage *addr_rw_server = 0; static void *SYNCHRONOUS_MARKER = (void*)&SYNCHRONOUS_MARKER; static int isValidPath(const char* path, const int mode); #ifdef HAVE_OPENSSL_H static int init_ssl_for_handler(zhandle_t *zh); static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error); #endif static int aremove_watches( zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local, void_completion_t *completion, const void *data, int all); #ifdef THREADED static void process_sync_completion(zhandle_t *zh, completion_list_t *cptr, struct sync_completion *sc, struct iarchive *ia); static int remove_watches( zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local, int all); #endif #ifdef _WIN32 typedef SOCKET socket_t; typedef int sendsize_t; #define SEND_FLAGS 0 #else #ifdef __APPLE__ #define SEND_FLAGS SO_NOSIGPIPE #endif #ifdef __linux__ #define SEND_FLAGS MSG_NOSIGNAL #endif #ifndef SEND_FLAGS #define SEND_FLAGS 0 #endif typedef int socket_t; typedef ssize_t sendsize_t; #endif static void zookeeper_set_sock_nodelay(zhandle_t *, socket_t); static void zookeeper_set_sock_noblock(zhandle_t *, socket_t); static void zookeeper_set_sock_timeout(zhandle_t *, socket_t, int); static socket_t zookeeper_connect(zhandle_t *, struct sockaddr_storage *, socket_t); /* * return 1 if zh has a SASL client configured, 0 otherwise. */ static int has_sasl_client(zhandle_t* zh) { #ifdef HAVE_CYRUS_SASL_H return zh->sasl_client != NULL; #else /* !HAVE_CYRUS_SASL_H */ return 0; #endif /* HAVE_CYRUS_SASL_H */ } /* * return 1 if zh has a SASL client performing authentication, 0 otherwise. */ static int is_sasl_auth_in_progress(zhandle_t* zh) { #ifdef HAVE_CYRUS_SASL_H return zh->sasl_client && zh->sasl_client->state == ZOO_SASL_INTERMEDIATE; #else /* !HAVE_CYRUS_SASL_H */ return 0; #endif /* HAVE_CYRUS_SASL_H */ } /* * Extract the type field (ZOO_*_OP) of a serialized RequestHeader. * * (This is not the most efficient way of fetching 4 bytes, but it is * currently only used during SASL negotiation.) * * \param buffer the buffer to extract the request type from. Must * start with a serialized RequestHeader; * \param len the buffer length. Must be positive. * \param out_type out parameter; pointer to the location where the * extracted type is to be stored. Cannot be NULL. * \return ZOK on success, or < 0 if something went wrong */ static int extract_request_type(char *buffer, int len, int32_t *out_type) { struct iarchive *ia; struct RequestHeader h; int rc; ia = create_buffer_iarchive(buffer, len); rc = ia ? ZOK : ZSYSTEMERROR; rc = rc < 0 ? rc : deserialize_RequestHeader(ia, "header", &h); deallocate_RequestHeader(&h); if (ia) { close_buffer_iarchive(&ia); } *out_type = h.type; return rc; } #ifndef THREADED /* * abort due to the use of a sync api in a singlethreaded environment */ static void abort_singlethreaded(zhandle_t *zh) { LOG_ERROR(LOGCALLBACK(zh), "Sync completion used without threads"); abort(); } #endif /* THREADED */ static ssize_t zookeeper_send(zsock_t *fd, const void* buf, size_t len) { #ifdef HAVE_OPENSSL_H if (fd->ssl_sock) return (ssize_t)SSL_write(fd->ssl_sock, buf, (int)len); #endif return send(fd->sock, buf, len, SEND_FLAGS); } static ssize_t zookeeper_recv(zsock_t *fd, void *buf, size_t len, int flags) { #ifdef HAVE_OPENSSL_H if (fd->ssl_sock) return (ssize_t)SSL_read(fd->ssl_sock, buf, (int)len); #endif return recv(fd->sock, buf, len, flags); } /** * Get the system time. * * If the monotonic clock is available, we use that. The monotonic clock does * not change when the wall-clock time is adjusted by NTP or the system * administrator. The monotonic clock returns a value which is monotonically * increasing. * * If POSIX monotonic clocks are not available, we fall back on the wall-clock. * * @param tv (out param) The time. */ void get_system_time(struct timeval *tv) { int ret; #ifdef __MACH__ // OS X clock_serv_t cclock; mach_timespec_t mts; ret = host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); if (!ret) { ret += clock_get_time(cclock, &mts); ret += mach_port_deallocate(mach_task_self(), cclock); if (!ret) { tv->tv_sec = mts.tv_sec; tv->tv_usec = mts.tv_nsec / 1000; } } if (ret) { // Default to gettimeofday in case of failure. ret = gettimeofday(tv, NULL); } #elif defined CLOCK_MONOTONIC_RAW // On Linux, CLOCK_MONOTONIC is affected by ntp slew but CLOCK_MONOTONIC_RAW // is not. We want the non-slewed (constant rate) CLOCK_MONOTONIC_RAW if it // is available. struct timespec ts = { 0 }; ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts); tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / 1000; #elif _POSIX_MONOTONIC_CLOCK struct timespec ts = { 0 }; ret = clock_gettime(CLOCK_MONOTONIC, &ts); tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / 1000; #elif _WIN32 LARGE_INTEGER counts, countsPerSecond, countsPerMicrosecond; if (QueryPerformanceFrequency(&countsPerSecond) && QueryPerformanceCounter(&counts)) { countsPerMicrosecond.QuadPart = countsPerSecond.QuadPart / 1000000; tv->tv_sec = (long)(counts.QuadPart / countsPerSecond.QuadPart); tv->tv_usec = (long)((counts.QuadPart % countsPerSecond.QuadPart) / countsPerMicrosecond.QuadPart); ret = 0; } else { ret = gettimeofday(tv, NULL); } #else ret = gettimeofday(tv, NULL); #endif if (ret) { abort(); } } 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 != 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; } void close_zsock(zsock_t *fd) { if (fd->sock != -1) { #ifdef HAVE_OPENSSL_H if (fd->ssl_sock) { SSL_free(fd->ssl_sock); fd->ssl_sock = NULL; SSL_CTX_free(fd->ssl_ctx); fd->ssl_ctx = NULL; } #endif close(fd->sock); fd->sock = -1; } } /** * 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 (process_async(zh->outstanding_sync)) { process_completions(zh); } if (zh->hostname != 0) { free(zh->hostname); zh->hostname = NULL; } if (zh->fd->sock != -1) { close_zsock(zh->fd); memset(&zh->addr_cur, 0, sizeof(zh->addr_cur)); zh->state = 0; } addrvec_free(&zh->addrs); if (zh->chroot != NULL) { free(zh->chroot); zh->chroot = NULL; } #ifdef HAVE_OPENSSL_H if (zh->fd->cert) { free(zh->fd->cert->certstr); free(zh->fd->cert); zh->fd->cert = NULL; } #endif 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); addrvec_free(&zh->addrs_old); addrvec_free(&zh->addrs_new); #ifdef HAVE_CYRUS_SASL_H if (zh->sasl_client) { zoo_sasl_client_destroy(zh->sasl_client); free(zh->sasl_client); zh->sasl_client = NULL; } #endif /* HAVE_CYRUS_SASL_H */ } static void setup_random() { #ifndef _WIN32 // TODO: better seed int seed; int fd = open("/dev/urandom", O_RDONLY); if (fd == -1) { seed = getpid(); } else { int seed_len = 0; /* Enter a loop to fill in seed with random data from /dev/urandom. * This is done in a loop so that we can safely handle short reads * which can happen due to signal interruptions. */ while (seed_len < sizeof(seed)) { /* Assert we either read something or we were interrupted due to a * signal (errno == EINTR) in which case we need to retry. */ int rc = read(fd, (char *)&seed + seed_len, sizeof(seed) - seed_len); assert(rc > 0 || errno == EINTR); if (rc > 0) { seed_len += rc; } } close(fd); } srandom(seed); srand48(seed); #endif } #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: // ZOOKEEPER-1323 EAI_NODATA and EAI_ADDRFAMILY are deprecated in FreeBSD. #if defined EAI_NODATA && EAI_NODATA != EAI_NONAME case EAI_NODATA: #endif return ENOENT; case EAI_MEMORY: return ENOMEM; default: return EINVAL; } } #endif /** * Count the number of hosts in the connection host string. This assumes it's * a well-formed connection string whereby each host is separated by a comma. */ static int count_hosts(char *hosts) { uint32_t count = 0; char *loc = hosts; if (!hosts || strlen(hosts) == 0) { return 0; } while ((loc = strchr(loc, ','))) { count++; loc+=1; } return count+1; } /** * Resolve hosts and populate provided address vector with shuffled results. * The contents of the provided address vector will be initialized to an * empty state. */ static int resolve_hosts(const zhandle_t *zh, const char *hosts_in, addrvec_t *avec) { int rc = ZOK; char *host = NULL; char *hosts = NULL; int num_hosts = 0; char *strtok_last = NULL; if (zh == NULL || hosts_in == NULL || avec == NULL) { return ZBADARGUMENTS; } // initialize address vector addrvec_init(avec); hosts = strdup(hosts_in); if (hosts == NULL) { LOG_ERROR(LOGCALLBACK(zh), "out of memory"); errno=ENOMEM; rc=ZSYSTEMERROR; goto fail; } num_hosts = count_hosts(hosts); if (num_hosts == 0) { free(hosts); return ZOK; } // Allocate list inside avec rc = addrvec_alloc_capacity(avec, num_hosts); if (rc != 0) { LOG_ERROR(LOGCALLBACK(zh), "out of memory"); errno=ENOMEM; rc=ZSYSTEMERROR; goto fail; } host = strtok_r(hosts, ",", &strtok_last); while(host) { char *port_spec = strrchr(host, ':'); char *end_port_spec; int port; if (!port_spec) { LOG_ERROR(LOGCALLBACK(zh), "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 || port == 0) { LOG_ERROR(LOGCALLBACK(zh), "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(LOGCALLBACK(zh), "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 (addrs->count == addrs->capacity) { rc = addrvec_grow_default(addrs); if (rc != 0) { LOG_ERROR(LOGCALLBACK(zh), "out of memory"); errno=ENOMEM; rc=ZSYSTEMERROR; goto fail; } } addr = &addrs->list[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(LOGCALLBACK(zh), "skipping unknown address family %x for %s", addr->ss_family, hosts_in); } } 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) && // ZOOKEEPER-1323 EAI_NODATA and EAI_ADDRFAMILY are deprecated in FreeBSD. #ifdef EAI_ADDRFAMILY ((rc ==EAI_BADFLAGS) || (rc == EAI_ADDRFAMILY))) { #else (rc == EAI_BADFLAGS)) { #endif //reset ai_flags to null hints.ai_flags = 0; //retry getaddrinfo rc = getaddrinfo(host, port_spec, &hints, &res0); } #endif if (rc != 0) { errno = getaddrinfo_errno(rc); #ifdef _WIN32 LOG_ERROR(LOGCALLBACK(zh), "Win32 message: %s\n", gai_strerror(rc)); #elif __linux__ && __GNUC__ LOG_ERROR(LOGCALLBACK(zh), "getaddrinfo: %s\n", gai_strerror(rc)); #else LOG_ERROR(LOGCALLBACK(zh), "getaddrinfo: %s\n", strerror(errno)); #endif rc=ZSYSTEMERROR; goto next; } } for (res = res0; res; res = res->ai_next) { // Expand address list if needed if (avec->count == avec->capacity) { rc = addrvec_grow_default(avec); if (rc != 0) { LOG_ERROR(LOGCALLBACK(zh), "out of memory"); errno=ENOMEM; rc=ZSYSTEMERROR; goto fail; } } // Copy addrinfo into address list switch (res->ai_family) { case AF_INET: #if defined(AF_INET6) case AF_INET6: #endif addrvec_append_addrinfo(avec, res); break; default: LOG_WARN(LOGCALLBACK(zh), "skipping unknown address family %x for %s", res->ai_family, hosts_in); break; } } freeaddrinfo(res0); next: host = strtok_r(0, ",", &strtok_last); } #endif } if (avec->count == 0) { rc = ZSYSTEMERROR; // not a single host resolved goto fail; } free(hosts); if(!disable_conn_permute){ setup_random(); addrvec_shuffle(avec); } return ZOK; fail: addrvec_free(avec); if (hosts) { free(hosts); hosts = NULL; } return rc; } /** * Updates the list of servers and determine if changing connections is necessary. * Permutes server list for proper load balancing. * * Changing connections is necessary if one of the following holds: * a) the server this client is currently connected is not in new address list. * Otherwise (if currentHost is in the new list): * b) the number of servers in the cluster is increasing - in this case the load * on currentHost should decrease, which means that SOME of the clients * connected to it will migrate to the new servers. The decision whether this * client migrates or not is probabilistic so that the expected number of * clients connected to each server is the same. * * If reconfig is set to true, the function sets pOld and pNew that correspond * to the probability to migrate to ones of the new servers or one of the old * servers (migrating to one of the old servers is done only if our client's * currentHost is not in new list). * * See zoo_cycle_next_server for the selection logic. * * \param ref_time an optional "reference time," used to determine if * resolution can be skipped in accordance to the delay set by \ref * zoo_set_servers_resolution_delay. Passing NULL prevents skipping. * * See {@link https://issues.apache.org/jira/browse/ZOOKEEPER-1355} for the * protocol and its evaluation, */ int update_addrs(zhandle_t *zh, const struct timeval *ref_time) { int rc = ZOK; char *hosts = NULL; uint32_t num_old = 0; uint32_t num_new = 0; uint32_t i = 0; int found_current = 0; addrvec_t resolved = { 0 }; // Verify we have a valid handle if (zh == NULL) { return ZBADARGUMENTS; } // zh->hostname should always be set if (zh->hostname == NULL) { return ZSYSTEMERROR; } // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); // Check if we are due for a host name resolution. (See // zoo_set_servers_resolution_delay. The answer is always "yes" // if no reference is provided or the file descriptor is invalid.) if (ref_time && zh->fd->sock != -1) { int do_resolve; if (zh->resolve_delay_ms <= 0) { // -1 disables, 0 means unconditional. Fail safe. do_resolve = zh->resolve_delay_ms != -1; } else { int elapsed_ms = calculate_interval(&zh->last_resolve, ref_time); // Include < 0 in case of overflow, or if we are not // backed by a monotonic clock. do_resolve = elapsed_ms > zh->resolve_delay_ms || elapsed_ms < 0; } if (!do_resolve) { goto finish; } } // Copy zh->hostname for local use hosts = strdup(zh->hostname); if (hosts == NULL) { rc = ZSYSTEMERROR; goto finish; } rc = resolve_hosts(zh, hosts, &resolved); if (rc != ZOK) { goto finish; } // Unconditionally note last resolution time. if (ref_time) { zh->last_resolve = *ref_time; } else { get_system_time(&zh->last_resolve); } // If the addrvec list is identical to last time we ran don't do anything if (addrvec_eq(&zh->addrs, &resolved)) { goto finish; } // Is the server we're connected to in the new resolved list? found_current = addrvec_contains(&resolved, &zh->addr_cur); // Clear out old and new address lists zh->reconfig = 1; addrvec_free(&zh->addrs_old); addrvec_free(&zh->addrs_new); // Divide server list into addrs_old if in previous list and addrs_new if not for (i = 0; i < resolved.count; i++) { struct sockaddr_storage *resolved_address = &resolved.data[i]; if (addrvec_contains(&zh->addrs, resolved_address)) { rc = addrvec_append(&zh->addrs_old, resolved_address); if (rc != ZOK) { goto finish; } } else { rc = addrvec_append(&zh->addrs_new, resolved_address); if (rc != ZOK) { goto finish; } } } num_old = zh->addrs_old.count; num_new = zh->addrs_new.count; // Number of servers increased if (num_old + num_new > zh->addrs.count) { if (found_current) { // my server is in the new config, but load should be decreased. // Need to decide if the client is moving to one of the new servers if (drand48() <= (1 - ((double)zh->addrs.count) / (num_old + num_new))) { zh->pNew = 1; zh->pOld = 0; } else { // do nothing special -- stay with the current server zh->reconfig = 0; } } else { // my server is not in the new config, and load on old servers must // be decreased, so connect to one of the new servers zh->pNew = 1; zh->pOld = 0; } } // Number of servers stayed the same or decreased else { if (found_current) { // my server is in the new config, and load should be increased, so // stay with this server and do nothing special zh->reconfig = 0; } else { zh->pOld = ((double) (num_old * (zh->addrs.count - (num_old + num_new)))) / ((num_old + num_new) * (zh->addrs.count - num_old)); zh->pNew = 1 - zh->pOld; } } addrvec_free(&zh->addrs); zh->addrs = resolved; // If we need to do a reconfig and we're currently connected to a server, // then force close that connection so on next interest() call we'll make a // new connection if (zh->reconfig == 1 && zh->fd->sock != -1) { close_zsock(zh->fd); zh->state = ZOO_NOTCONNECTED_STATE; } finish: unlock_reconfig(zh); // If we short-circuited out and never assigned resolved to zh->addrs then we // need to free resolved to avoid a memleak. if (resolved.data && zh->addrs.data != resolved.data) { addrvec_free(&resolved); } if (hosts) { free(hosts); hosts = NULL; } 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; } struct sockaddr* zookeeper_get_connected_host(zhandle_t *zh, struct sockaddr *addr, socklen_t *addr_len) { if (zh->state!=ZOO_CONNECTED_STATE) { return NULL; } if (getpeername(zh->fd->sock, addr, addr_len)==-1) { return NULL; } return addr; } static void log_env(zhandle_t *zh) { 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(LOGCALLBACK(zh), "Client environment:zookeeper.version=%s", PACKAGE_STRING); #ifdef HAVE_GETHOSTNAME gethostname(buf, sizeof(buf)); LOG_INFO(LOGCALLBACK(zh), "Client environment:host.name=%s", buf); #else LOG_INFO(LOGCALLBACK(zh), "Client environment:host.name="); #endif #ifdef HAVE_SYS_UTSNAME_H uname(&utsname); LOG_INFO(LOGCALLBACK(zh), "Client environment:os.name=%s", utsname.sysname); LOG_INFO(LOGCALLBACK(zh), "Client environment:os.arch=%s", utsname.release); LOG_INFO(LOGCALLBACK(zh), "Client environment:os.version=%s", utsname.version); #else LOG_INFO(LOGCALLBACK(zh), "Client environment:os.name="); LOG_INFO(LOGCALLBACK(zh), "Client environment:os.arch="); LOG_INFO(LOGCALLBACK(zh), "Client environment:os.version="); #endif #ifdef HAVE_GETLOGIN LOG_INFO(LOGCALLBACK(zh), "Client environment:user.name=%s", getlogin()); #else LOG_INFO(LOGCALLBACK(zh), "Client environment:user.name="); #endif #if defined(HAVE_GETUID) && defined(HAVE_GETPWUID_R) uid = getuid(); if (!getpwuid_r(uid, &pw, buf, sizeof(buf), &pwp) && pwp) { LOG_INFO(LOGCALLBACK(zh), "Client environment:user.home=%s", pw.pw_dir); } else { LOG_INFO(LOGCALLBACK(zh), "Client environment:user.home="); } #else LOG_INFO(LOGCALLBACK(zh), "Client environment:user.home="); #endif #ifdef HAVE_GETCWD if (!getcwd(buf, sizeof(buf))) { LOG_INFO(LOGCALLBACK(zh), "Client environment:user.dir="); } else { LOG_INFO(LOGCALLBACK(zh), "Client environment:user.dir=%s", buf); } #else LOG_INFO(LOGCALLBACK(zh), "Client environment:user.dir="); #endif } /** * Create a zookeeper handle associated with the given host and port. */ static zhandle_t *zookeeper_init_internal(const char *host, watcher_fn watcher, int recv_timeout, const clientid_t *clientid, void *context, int flags, log_callback_fn log_callback, zcert_t *cert, void *sasl_params) { int errnosave = 0; zhandle_t *zh = NULL; char *index_chroot = NULL; // Create our handle zh = calloc(1, sizeof(*zh)); if (!zh) { return 0; } // Set log callback before calling into log_env zh->log_callback = log_callback; if (!(flags & ZOO_NO_LOG_CLIENTENV)) { log_env(zh); } zh->fd = calloc(1, sizeof(zsock_t)); zh->fd->sock = -1; if (cert) { zh->fd->cert = calloc(1, sizeof(zcert_t)); memcpy(zh->fd->cert, cert, sizeof(zcert_t)); } #ifdef _WIN32 if (Win32WSAStartup()){ LOG_ERROR(LOGCALLBACK(zh), "Error initializing ws2_32.dll"); return 0; } #endif LOG_INFO(LOGCALLBACK(zh), "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] == 0) ? "" : ""), context, flags); zh->hostname = NULL; zh->state = ZOO_NOTCONNECTED_STATE; zh->context = context; zh->recv_timeout = recv_timeout; zh->allow_read_only = flags & ZOO_READONLY; // non-zero clientid implies we've seen r/w server already zh->seen_rw_server_before = (clientid != 0 && clientid->client_id != 0); 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 (zh->chroot == NULL) { goto abort; } // if chroot is just / set it to null if (strlen(zh->chroot) == 1) { free(zh->chroot); zh->chroot = NULL; } // cannot use strndup so allocate and strcpy 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(update_addrs(zh, NULL) != 0) { goto abort; } if (clientid) { memcpy(&zh->client_id, clientid, sizeof(zh->client_id)); } else { memset(&zh->client_id, 0, sizeof(zh->client_id)); } zh->io_count = 0; 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(); zh->disable_reconnection_attempt = 0; #ifdef HAVE_CYRUS_SASL_H if (sasl_params) { zh->sasl_client = zoo_sasl_client_create( (zoo_sasl_params_t*)sasl_params); if (!zh->sasl_client) { goto abort; } } #endif /* HAVE_CYRUS_SASL_H */ if (adaptor_init(zh) == -1) { goto abort; } return zh; abort: errnosave=errno; destroy(zh); free(zh->fd); free(zh); errno=errnosave; return 0; } zhandle_t *zookeeper_init(const char *host, watcher_fn watcher, int recv_timeout, const clientid_t *clientid, void *context, int flags) { return zookeeper_init_internal(host, watcher, recv_timeout, clientid, context, flags, NULL, NULL, NULL); } zhandle_t *zookeeper_init2(const char *host, watcher_fn watcher, int recv_timeout, const clientid_t *clientid, void *context, int flags, log_callback_fn log_callback) { return zookeeper_init_internal(host, watcher, recv_timeout, clientid, context, flags, log_callback, NULL, NULL); } #ifdef HAVE_OPENSSL_H zhandle_t *zookeeper_init_ssl(const char *host, const char *cert, watcher_fn watcher, int recv_timeout, const clientid_t *clientid, void *context, int flags) { zcert_t zcert; zcert.certstr = strdup(cert); zcert.ca = strtok(zcert.certstr, ","); zcert.cert = strtok(NULL, ","); zcert.key = strtok(NULL, ","); zcert.passwd = strtok(NULL, ","); return zookeeper_init_internal(host, watcher, recv_timeout, clientid, context, flags, NULL, &zcert, NULL); } #endif #ifdef HAVE_CYRUS_SASL_H zhandle_t *zookeeper_init_sasl(const char *host, watcher_fn watcher, int recv_timeout, const clientid_t *clientid, void *context, int flags, log_callback_fn log_callback, zoo_sasl_params_t *sasl_params) { return zookeeper_init_internal(host, watcher, recv_timeout, clientid, context, flags, log_callback, NULL, sasl_params); } #endif /* HAVE_CYRUS_SASL_H */ /** * Set a new list of zk servers to connect to. Disconnect will occur if * current connection endpoint is not in the list. */ int zoo_set_servers(zhandle_t *zh, const char *hosts) { if (hosts == NULL) { LOG_ERROR(LOGCALLBACK(zh), "New server list cannot be empty"); return ZBADARGUMENTS; } // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); // Reset hostname to new set of hosts to connect to if (zh->hostname) { free(zh->hostname); } zh->hostname = strdup(hosts); unlock_reconfig(zh); return update_addrs(zh, NULL); } /* * Sets a minimum delay to observe between "routine" host name * resolutions. See prototype for full documentation. */ int zoo_set_servers_resolution_delay(zhandle_t *zh, int delay_ms) { if (delay_ms < -1) { LOG_ERROR(LOGCALLBACK(zh), "Resolution delay cannot be %d", delay_ms); return ZBADARGUMENTS; } // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); zh->resolve_delay_ms = delay_ms; unlock_reconfig(zh); return ZOK; } /** * Get the next server to connect to, when in 'reconfig' mode, which means that * we've updated the server list to connect to, and are now trying to find some * server to connect to. Once we get successfully connected, 'reconfig' mode is * set to false. Similarly, if we tried to connect to all servers in new config * and failed, 'reconfig' mode is set to false. * * While in 'reconfig' mode, we should connect to a server in the new set of * servers (addrs_new) with probability pNew and to servers in the old set of * servers (addrs_old) with probability pOld (which is just 1-pNew). If we tried * out all servers in either, we continue to try servers from the other set, * regardless of pNew or pOld. If we tried all servers we give up and go back to * the normal round robin mode * * When called, must be protected by lock_reconfig(zh). */ static int get_next_server_in_reconfig(zhandle_t *zh) { int take_new = drand48() <= zh->pNew; LOG_DEBUG(LOGCALLBACK(zh), "[OLD] count=%d capacity=%d next=%d hasnext=%d", zh->addrs_old.count, zh->addrs_old.capacity, zh->addrs_old.next, addrvec_hasnext(&zh->addrs_old)); LOG_DEBUG(LOGCALLBACK(zh), "[NEW] count=%d capacity=%d next=%d hasnext=%d", zh->addrs_new.count, zh->addrs_new.capacity, zh->addrs_new.next, addrvec_hasnext(&zh->addrs_new)); // Take one of the new servers if we haven't tried them all yet // and either the probability tells us to connect to one of the new servers // or if we already tried them all then use one of the old servers if (addrvec_hasnext(&zh->addrs_new) && (take_new || !addrvec_hasnext(&zh->addrs_old))) { addrvec_next(&zh->addrs_new, &zh->addr_cur); LOG_DEBUG(LOGCALLBACK(zh), "Using next from NEW=%s", format_endpoint_info(&zh->addr_cur)); return 0; } // start taking old servers if (addrvec_hasnext(&zh->addrs_old)) { addrvec_next(&zh->addrs_old, &zh->addr_cur); LOG_DEBUG(LOGCALLBACK(zh), "Using next from OLD=%s", format_endpoint_info(&zh->addr_cur)); return 0; } LOG_DEBUG(LOGCALLBACK(zh), "Failed to find either new or old"); memset(&zh->addr_cur, 0, sizeof(zh->addr_cur)); return 1; } /** * Cycle through our server list to the correct 'next' server. The 'next' server * to connect to depends upon whether we're in a 'reconfig' mode or not. Reconfig * mode means we've upated the server list and are now trying to find a server * to connect to. Once we get connected, we are no longer in the reconfig mode. * Similarly, if we try to connect to all the servers in the new configuration * and failed, reconfig mode is set to false. * * For more algorithm details, see get_next_server_in_reconfig. */ void zoo_cycle_next_server(zhandle_t *zh) { // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} lock_reconfig(zh); memset(&zh->addr_cur, 0, sizeof(zh->addr_cur)); if (zh->reconfig) { if (get_next_server_in_reconfig(zh) == 0) { unlock_reconfig(zh); return; } // tried all new and old servers and couldn't connect zh->reconfig = 0; } addrvec_next(&zh->addrs, &zh->addr_cur); unlock_reconfig(zh); return; } /** * Get the host:port for the server we are currently connecting to or connected * to. This is largely for testing purposes but is also generally useful for * other client software built on top of this client. */ const char* zoo_get_current_server(zhandle_t* zh) { const char *endpoint_info = NULL; // NOTE: guard access to {hostname, addr_cur, addrs, addrs_old, addrs_new, last_resolve, resolve_delay_ms} // Need the lock here as it is changed in update_addrs() lock_reconfig(zh); endpoint_info = format_endpoint_info(&zh->addr_cur); unlock_reconfig(zh); return endpoint_info; } /** * 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 == NULL || 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; //ZOOKEEPER-1027 if (strncmp(server_path, zh->chroot, strlen(zh->chroot)) != 0) { LOG_ERROR(LOGCALLBACK(zh), "server path %s does not include chroot path %s", server_path, zh->chroot); return (char *) server_path; } 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(zhandle_t *zh, 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 = zookeeper_send(zh->fd, b + off, sizeof(nlen) - off); if (rc == -1) { #ifdef _WIN32 if (WSAGetLastError() != WSAEWOULDBLOCK) { #else if (errno != EAGAIN) { #endif 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 = zookeeper_send(zh->fd, buff->buffer + off, len - off); if (rc == -1) { #ifdef _WIN32 if (WSAGetLastError() != WSAEWOULDBLOCK) { #else if (errno != EAGAIN) { #endif 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(zhandle_t *zh, buffer_list_t *buff) { int off = buff->curr_offset; int rc = 0; /* if buffer is less than 4, we are reading in the length */ if (off < 4) { char *buffer = (char*)&(buff->len); rc = zookeeper_recv(zh->fd, buffer+off, sizeof(int)-off, 0); switch (rc) { case 0: errno = EHOSTDOWN; case -1: #ifdef _WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) { #else if (errno == EAGAIN) { #endif 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 = zookeeper_recv(zh->fd, buff->buffer+off, buff->len-off, 0); /* dirty hack to make new client work against old server * old server sends 40 bytes to finish connection handshake, * while we're expecting 41 (1 byte for read-only mode data) */ if (rc > 0 && buff == &zh->primer_buffer) { /* primer_buffer's curr_offset starts at 4 (see prime_connection) */ int avail = buff->curr_offset - sizeof(buff->len) + rc; /* exactly 40 bytes (out of 41 expected) collected? */ if (avail == buff->len - 1) { int32_t reply_len; /* extract length of ConnectResponse (+ 1-byte flag?) */ memcpy(&reply_len, buff->buffer, sizeof(reply_len)); reply_len = ntohl(reply_len); /* if 1-byte flag was not sent, fake it (value 0) */ if ((int)(reply_len + sizeof(reply_len)) == buff->len - 1) { ++rc; } } } switch(rc) { case 0: errno = EHOSTDOWN; case -1: #ifdef _WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) { #else if (errno == EAGAIN) { #endif 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; if (lock_completion_list(&zh->sent_requests) == 0) { tmp_list = zh->sent_requests; zh->sent_requests.head = 0; zh->sent_requests.last = 0; unlock_completion_list(&zh->sent_requests); while (tmp_list.head) { completion_list_t *cptr = tmp_list.head; tmp_list.head = cptr->next; if (cptr->c.data_result == SYNCHRONOUS_MARKER) { #ifdef THREADED struct sync_completion *sc = (struct sync_completion*)cptr->data; sc->rc = reason; notify_sync_completion(sc); zh->outstanding_sync--; destroy_completion_entry(cptr); #else abort_singlethreaded(zh); #endif } else if (callCompletion) { // Fake the response buffer_list_t *bptr; h.xid = cptr->xid; h.zxid = -1; h.err = reason; oa = create_buffer_oarchive(); serialize_ReplyHeader(oa, "header", &h); bptr = calloc(sizeof(*bptr), 1); assert(bptr); bptr->len = get_buffer_len(oa); bptr->buffer = get_buffer(oa); close_buffer_oarchive(&oa, 0); cptr->buffer = bptr; queue_completion(&zh->completions_to_process, cptr, 0); } } } zoo_lock_auth(zh); a_list.completion = NULL; a_list.next = NULL; get_auth_completions(&zh->auth_h, &a_list); zoo_unlock_auth(zh); a_tmp = &a_list; // chain call user's completion function while (a_tmp->completion != NULL) { auth_completion = a_tmp->completion; auth_completion(reason, a_tmp->auth_data); a_tmp = a_tmp->next; if (a_tmp == NULL) break; } free_auth_completion(&a_list); } 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; } } /* return 1 if zh's state is ZOO_CONNECTED_STATE or ZOO_READONLY_STATE, * 0 otherwise */ static int is_connected(zhandle_t* zh) { return (zh->state==ZOO_CONNECTED_STATE || zh->state==ZOO_READONLY_STATE); } static void cleanup(zhandle_t *zh,int rc) { close_zsock(zh->fd); if (is_unrecoverable(zh)) { LOG_DEBUG(LOGCALLBACK(zh), "Calling a watcher for a ZOO_SESSION_EVENT and the state=%s", state2String(zh->state)); PROCESS_SESSION_EVENT(zh, zh->state); } else if (is_connected(zh)) { LOG_DEBUG(LOGCALLBACK(zh), "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); LOG_DEBUG(LOGCALLBACK(zh), "Previous connection=%s delay=%d", zoo_get_current_server(zh), zh->delay); if (!is_unrecoverable(zh)) { zh->state = 0; } if (process_async(zh->outstanding_sync)) { process_completions(zh); } } static void handle_error(zhandle_t *zh,int rc) { cleanup(zh, rc); // NOTE: If we're at the end of the list of addresses to connect to, then // we want to delay the next connection attempt to avoid spinning. // Then increment what host we'll connect to since we failed to connect to current zh->delay = addrvec_atend(&zh->addrs); addrvec_next(&zh->addrs, &zh->addr_cur); } static int handle_socket_error_msg(zhandle_t *zh, int line, const char *func, 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(LOGCALLBACK(zh), ZOO_LOG_LEVEL_ERROR, line, func, "Socket %s zk retcode=%d, errno=%d(%s): %s", zoo_get_current_server(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(LOGCALLBACK(zh), "Authentication scheme %s failed. Connection closed.", zh->auth_h.auth->scheme); } else { LOG_INFO(LOGCALLBACK(zh), "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 = {AUTH_XID, ZOO_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 != NULL) { rc = send_info_packet(zh, auth); auth = auth->next; } zoo_unlock_auth(zh); LOG_DEBUG(LOGCALLBACK(zh), "Sending all auth info request to %s", zoo_get_current_server(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(LOGCALLBACK(zh), "Sending auth info request to %s",zoo_get_current_server(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 = {SET_WATCHES_XID, ZOO_SETWATCHES_OP}; struct SetWatches req; int rc; req.relativeZxid = zh->last_zxid; lock_watchers(zh); 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); unlock_watchers(zh); // 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(LOGCALLBACK(zh), "Sending set watches request to %s",zoo_get_current_server(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 = zoo_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 = zoo_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)); offset = offset + sizeof(req->passwd); memcpy(buffer + offset, &req->readOnly, sizeof(req->readOnly)); return 0; } static int deserialize_prime_response(struct prime_struct *resp, char* buffer) { //this should be the order of deserialization int offset = 0; memcpy(&resp->len, buffer + offset, sizeof(resp->len)); offset = offset + sizeof(resp->len); resp->len = ntohl(resp->len); memcpy(&resp->protocolVersion, buffer + offset, sizeof(resp->protocolVersion)); offset = offset + sizeof(resp->protocolVersion); resp->protocolVersion = ntohl(resp->protocolVersion); memcpy(&resp->timeOut, buffer + offset, sizeof(resp->timeOut)); offset = offset + sizeof(resp->timeOut); resp->timeOut = ntohl(resp->timeOut); memcpy(&resp->sessionId, buffer + offset, sizeof(resp->sessionId)); offset = offset + sizeof(resp->sessionId); resp->sessionId = zoo_htonll(resp->sessionId); memcpy(&resp->passwd_len, buffer + offset, sizeof(resp->passwd_len)); offset = offset + sizeof(resp->passwd_len); resp->passwd_len = ntohl(resp->passwd_len); memcpy(resp->passwd, buffer + offset, sizeof(resp->passwd)); offset = offset + sizeof(resp->passwd); memcpy(&resp->readOnly, buffer + offset, sizeof(resp->readOnly)); 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; if (zh->state == ZOO_SSL_CONNECTING_STATE) { // The SSL connection is yet to happen. return ZOK; } req.protocolVersion = 0; req.sessionId = zh->seen_rw_server_before ? zh->client_id.client_id : 0; 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; req.readOnly = zh->allow_read_only; hlen = htonl(len); /* We are running fast and loose here, but this string should fit in the initial buffer! */ rc=zookeeper_send(zh->fd, &hlen, sizeof(len)); serialize_prime_connect(&req, buffer_req); rc=rc<0 ? rc : zookeeper_send(zh->fd, buffer_req, len); if (rc<0) { return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "failed to send a handshake packet: %s", strerror(errno)); } zh->state = ZOO_ASSOCIATING_STATE; zh->input_buffer = &zh->primer_buffer; memset(zh->input_buffer->buffer, 0, zh->input_buffer->len); /* 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); static int add_string_stat_completion(zhandle_t *zh, int xid, string_stat_completion_t dc, const void *data); int send_ping(zhandle_t* zh) { int rc; struct oarchive *oa = create_buffer_oarchive(); struct RequestHeader h = {PING_XID, ZOO_PING_OP}; rc = serialize_RequestHeader(oa, "header", &h); enter_critical(zh); get_system_time(&zh->last_ping); 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); } /* upper bound of a timeout for seeking for r/w server when in read-only mode */ const int MAX_RW_TIMEOUT = 60000; const int MIN_RW_TIMEOUT = 200; static int ping_rw_server(zhandle_t* zh) { char buf[10]; zsock_t fd; int rc; sendsize_t ssize; int sock_flags; addrvec_peek(&zh->addrs, &zh->addr_rw_server); #ifdef SOCK_CLOEXEC_ENABLED sock_flags = SOCK_STREAM | SOCK_CLOEXEC; #else sock_flags = SOCK_STREAM; #endif fd.sock = socket(zh->addr_rw_server.ss_family, sock_flags, 0); if (fd.sock < 0) { return 0; } zookeeper_set_sock_nodelay(zh, fd.sock); zookeeper_set_sock_timeout(zh, fd.sock, 1); rc = zookeeper_connect(zh, &zh->addr_rw_server, fd.sock); if (rc < 0) { return 0; } #ifdef HAVE_OPENSSL_H fd.ssl_sock = NULL; fd.ssl_ctx = NULL; if (zh->fd->cert != NULL) { fd.cert = zh->fd->cert; rc = init_ssl_for_socket(&fd, zh, 0); if (rc != ZOK) { rc = 0; goto out; } } #endif ssize = zookeeper_send(&fd, "isro", 4); if (ssize < 0) { rc = 0; goto out; } memset(buf, 0, sizeof(buf)); rc = zookeeper_recv(&fd, buf, sizeof(buf), 0); if (rc < 0) { rc = 0; goto out; } rc = strcmp("rw", buf) == 0; out: close_zsock(&fd); addr_rw_server = rc ? &zh->addr_rw_server : 0; return rc; } #if !defined(WIN32) && !defined(min) static inline int min(int a, int b) { return a < b ? a : b; } #endif static void zookeeper_set_sock_noblock(zhandle_t *zh, socket_t sock) { #ifdef _WIN32 ULONG nonblocking_flag = 1; ioctlsocket(sock, FIONBIO, &nonblocking_flag); #else fcntl(sock, F_SETFL, O_NONBLOCK|fcntl(sock, F_GETFL, 0)); #endif } static void zookeeper_set_sock_timeout(zhandle_t *zh, socket_t s, int timeout) { struct timeval tv; tv.tv_sec = timeout; setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval)); setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); } static void zookeeper_set_sock_nodelay(zhandle_t *zh, socket_t sock) { #ifdef _WIN32 char enable_tcp_nodelay = 1; #else int enable_tcp_nodelay = 1; #endif int rc; rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &enable_tcp_nodelay, sizeof(enable_tcp_nodelay)); if (rc) { LOG_WARN(LOGCALLBACK(zh), "Unable to set TCP_NODELAY, latency may be effected"); } } static socket_t zookeeper_connect(zhandle_t *zh, struct sockaddr_storage *addr, socket_t fd) { int rc; int addr_len; #if defined(AF_INET6) if (addr->ss_family == AF_INET6) { addr_len = sizeof(struct sockaddr_in6); } else { addr_len = sizeof(struct sockaddr_in); } #else addr_len = sizeof(struct sockaddr_in); #endif LOG_DEBUG(LOGCALLBACK(zh), "[zk] connect()\n"); rc = connect(fd, (struct sockaddr *)addr, addr_len); #ifdef _WIN32 errno = GetLastError(); #ifndef EWOULDBLOCK #define EWOULDBLOCK WSAEWOULDBLOCK #endif #ifndef EINPROGRESS #define EINPROGRESS WSAEINPROGRESS #endif #if _MSC_VER >= 1600 switch(errno) { case WSAEWOULDBLOCK: errno = EWOULDBLOCK; break; case WSAEINPROGRESS: errno = EINPROGRESS; break; } #endif #endif return rc; } int zookeeper_interest(zhandle_t *zh, socket_t *fd, int *interest, struct timeval *tv) { int sock_flags; int rc = 0; struct timeval now; #ifdef SOCK_CLOEXEC_ENABLED sock_flags = SOCK_STREAM | SOCK_CLOEXEC; #else sock_flags = SOCK_STREAM; #endif if(zh==0 || fd==0 ||interest==0 || tv==0) return ZBADARGUMENTS; if (is_unrecoverable(zh)) return ZINVALIDSTATE; get_system_time(&now); if(zh->next_deadline.tv_sec!=0 || zh->next_deadline.tv_usec!=0){ int time_left = calculate_interval(&zh->next_deadline, &now); int max_exceed = zh->recv_timeout / 10 > 200 ? 200 : (zh->recv_timeout / 10); if (time_left > max_exceed) LOG_WARN(LOGCALLBACK(zh), "Exceeded deadline by %dms", time_left); } api_prolog(zh); rc = update_addrs(zh, &now); if (rc != ZOK) { return api_epilog(zh, rc); } *fd = zh->fd->sock; *interest = 0; tv->tv_sec = 0; tv->tv_usec = 0; if (*fd == -1) { /* * If we previously failed to connect to server pool (zh->delay == 1) * then we need delay our connection on this iteration 1/60 of the * recv timeout before trying again so we don't spin. * * We always clear the delay setting. If we fail again, we'll set delay * again and on the next iteration we'll do the same. * * We will also delay if the disable_reconnection_attempt is set. */ if (zh->delay == 1 || zh->disable_reconnection_attempt == 1) { *tv = get_timeval(zh->recv_timeout/60); zh->delay = 0; lock_reconfig(zh); LOG_WARN(LOGCALLBACK(zh), "Delaying connection after exhaustively trying all servers [%s]", zh->hostname); unlock_reconfig(zh); } else { if (addr_rw_server) { zh->addr_cur = *addr_rw_server; addr_rw_server = 0; } else { // No need to delay -- grab the next server and attempt connection zoo_cycle_next_server(zh); } zh->fd->sock = socket(zh->addr_cur.ss_family, sock_flags, 0); if (zh->fd->sock < 0) { rc = handle_socket_error_msg(zh, __LINE__, __func__, ZSYSTEMERROR, "socket() call failed"); return api_epilog(zh, rc); } zookeeper_set_sock_nodelay(zh, zh->fd->sock); zookeeper_set_sock_noblock(zh, zh->fd->sock); rc = zookeeper_connect(zh, &zh->addr_cur, zh->fd->sock); 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) { // For SSL, we first go to ZOO_SSL_CONNECTING_STATE if (zh->fd->cert != NULL) zh->state = ZOO_SSL_CONNECTING_STATE; else zh->state = ZOO_CONNECTING_STATE; } else { rc = handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "connect() call failed"); return api_epilog(zh, rc); } } else { #ifdef HAVE_OPENSSL_H if (zh->fd->cert != NULL) { // We do SSL_connect() here if (init_ssl_for_handler(zh) != ZOK) { return ZSSLCONNECTIONERROR; } } #endif rc = prime_connection(zh); if (rc != 0) { return api_epilog(zh,rc); } LOG_INFO(LOGCALLBACK(zh), "Initiated connection to server %s", format_endpoint_info(&zh->addr_cur)); } *tv = get_timeval(zh->recv_timeout/3); } *fd = zh->fd->sock; zh->last_recv = now; zh->last_send = now; zh->last_ping = now; zh->last_ping_rw = now; zh->ping_rw_timeout = MIN_RW_TIMEOUT; } if (zh->fd->sock != -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 && zh->state != ZOO_SSL_CONNECTING_STATE) { // We gotta cut our losses and connect to someone else #ifdef _WIN32 errno = WSAETIMEDOUT; #else errno = ETIMEDOUT; #endif *interest=0; *tv = get_timeval(0); return api_epilog(zh,handle_socket_error_msg(zh, __LINE__, __func__, ZOPERATIONTIMEOUT, "connection to %s timed out (exceeded timeout by %dms)", format_endpoint_info(&zh->addr_cur), -recv_to)); } // We only allow 1/3 of our timeout time to expire before sending // a PING if (is_connected(zh)) { send_to = zh->recv_timeout/3 - idle_send; if (send_to <= 0) { if (zh->sent_requests.head == 0) { rc = send_ping(zh); if (rc < 0) { LOG_ERROR(LOGCALLBACK(zh), "failed to send PING request (zk retcode=%d)",rc); return api_epilog(zh,rc); } } send_to = zh->recv_timeout/3; } } // If we are in read-only mode, seek for read/write server if (zh->state == ZOO_READONLY_STATE) { int idle_ping_rw = calculate_interval(&zh->last_ping_rw, &now); if (idle_ping_rw >= zh->ping_rw_timeout) { zh->last_ping_rw = now; idle_ping_rw = 0; zh->ping_rw_timeout = min(zh->ping_rw_timeout * 2, MAX_RW_TIMEOUT); if (ping_rw_server(zh)) { struct sockaddr_storage addr; addrvec_peek(&zh->addrs, &addr); zh->ping_rw_timeout = MIN_RW_TIMEOUT; LOG_INFO(LOGCALLBACK(zh), "r/w server found at %s", format_endpoint_info(&addr)); cleanup(zh, ZOK); } else { addrvec_next(&zh->addrs, NULL); } } send_to = min(send_to, zh->ping_rw_timeout - idle_ping_rw); } // choose the lesser value as the timeout *tv = get_timeval(min(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 && (is_connected(zh) || is_sasl_auth_in_progress(zh))) || zh->state == ZOO_CONNECTING_STATE || zh->state == ZOO_SSL_CONNECTING_STATE) { *interest |= ZOOKEEPER_WRITE; } } return api_epilog(zh,ZOK); } #ifdef HAVE_OPENSSL_H /* * use this function, if you want to init SSL for the socket currently registered in the zookeeper handler */ static int init_ssl_for_handler(zhandle_t *zh) { int rc = init_ssl_for_socket(zh->fd, zh, 1); if (rc == ZOK) { // (SUCCESS) Now mark the ZOO_CONNECTING_STATE so that // prime_connection() happen. // prime_connection() only happens in ZOO_CONNECTING_STATE zh->state = ZOO_CONNECTING_STATE; } return rc; } /* * use this function, if you want to init SSL for a socket, pointing to a different server address than the one * currently registered in the zookeeper handler (e.g. ping other servers when you are connected to a read-only one) */ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) { SSL_CTX **ctx; if (!fd->ssl_sock) { const SSL_METHOD *method; #if OPENSSL_VERSION_NUMBER < 0x10100000L OpenSSL_add_all_algorithms(); ERR_load_BIO_strings(); ERR_load_crypto_strings(); SSL_load_error_strings(); SSL_library_init(); method = SSLv23_client_method(); #else OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); method = TLS_client_method(); #endif fd->ssl_ctx = SSL_CTX_new(method); ctx = &fd->ssl_ctx; SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); /*SERVER CA FILE*/ if (SSL_CTX_load_verify_locations(*ctx, fd->cert->ca, 0) != 1) { SSL_CTX_free(*ctx); LOG_ERROR(LOGCALLBACK(zh), "Failed to load CA file %s", fd->cert->ca); errno = EINVAL; return ZBADARGUMENTS; } if (SSL_CTX_set_default_verify_paths(*ctx) != 1) { SSL_CTX_free(*ctx); LOG_ERROR(LOGCALLBACK(zh), "Call to SSL_CTX_set_default_verify_paths failed"); errno = EINVAL; return ZBADARGUMENTS; } /*CLIENT CA FILE (With Certificate Chain)*/ if (SSL_CTX_use_certificate_chain_file(*ctx, fd->cert->cert) != 1) { SSL_CTX_free(*ctx); LOG_ERROR(LOGCALLBACK(zh), "Failed to load client certificate chain from %s", fd->cert->cert); errno = EINVAL; return ZBADARGUMENTS; } /*CLIENT PRIVATE KEY*/ SSL_CTX_set_default_passwd_cb_userdata(*ctx, fd->cert->passwd); if (SSL_CTX_use_PrivateKey_file(*ctx, fd->cert->key, SSL_FILETYPE_PEM) != 1) { SSL_CTX_free(*ctx); LOG_ERROR(LOGCALLBACK(zh), "Failed to load client private key from %s", fd->cert->key); errno = EINVAL; return ZBADARGUMENTS; } /*CHECK*/ if (SSL_CTX_check_private_key(*ctx) != 1) { SSL_CTX_free(*ctx); LOG_ERROR(LOGCALLBACK(zh), "SSL_CTX_check_private_key failed"); errno = EINVAL; return ZBADARGUMENTS; } /*MULTIPLE HANDSHAKE*/ SSL_CTX_set_mode(*ctx, SSL_MODE_AUTO_RETRY); fd->ssl_sock = SSL_new(*ctx); if (fd->ssl_sock == NULL) { if (fail_on_error) { return handle_socket_error_msg(zh, __LINE__, __func__, ZSSLCONNECTIONERROR, "error creating ssl context"); } else { LOG_ERROR(LOGCALLBACK(zh), "error creating ssl context"); return ZSSLCONNECTIONERROR; } } SSL_set_fd(fd->ssl_sock, fd->sock); } while(1) { int rc; int sock = fd->sock; struct timeval tv; fd_set s_rfds, s_wfds; tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&s_rfds); FD_ZERO(&s_wfds); rc = SSL_connect(fd->ssl_sock); if (rc == 1) { return ZOK; } else { rc = SSL_get_error(fd->ssl_sock, rc); if (rc == SSL_ERROR_WANT_READ) { FD_SET(sock, &s_rfds); FD_CLR(sock, &s_wfds); } else if (rc == SSL_ERROR_WANT_WRITE) { FD_SET(sock, &s_wfds); FD_CLR(sock, &s_rfds); } else { if (fail_on_error) { return handle_socket_error_msg(zh, __LINE__, __func__, ZSSLCONNECTIONERROR, "error in ssl connect"); } else { LOG_ERROR(LOGCALLBACK(zh), "error in ssl connect"); return ZSSLCONNECTIONERROR; } } rc = select(sock + 1, &s_rfds, &s_wfds, NULL, &tv); if (rc == -1) { if (fail_on_error) { return handle_socket_error_msg(zh, __LINE__, __func__, ZSSLCONNECTIONERROR, "error in ssl connect (after select)"); } else { LOG_ERROR(LOGCALLBACK(zh), "error in ssl connect (after select)"); return ZSSLCONNECTIONERROR; } } } } } #endif /* * the "bottom half" of the session establishment procedure, executed * either after receiving the "prime response," or after SASL * authentication is complete */ static void finalize_session_establishment(zhandle_t *zh) { zh->state = zh->primer_storage.readOnly ? ZOO_READONLY_STATE : ZOO_CONNECTED_STATE; zh->reconfig = 0; LOG_INFO(LOGCALLBACK(zh), "session establishment complete on server %s, sessionId=%#llx, negotiated timeout=%d %s", format_endpoint_info(&zh->addr_cur), zh->client_id.client_id, zh->recv_timeout, zh->primer_storage.readOnly ? "(READ-ONLY mode)" : ""); /* 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(LOGCALLBACK(zh), "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, zh->state); if (has_sasl_client(zh)) { /* some packets might have been delayed during SASL negotiaton. */ adaptor_send_queue(zh, 0); } } #ifdef HAVE_CYRUS_SASL_H /* * queue an encoded SASL request to ZooKeeper. The packet is added to * the front of the queue. * * \param zh the ZooKeeper handle * \param client_data the encoded SASL data, ready to send * \param client_data_len the length of \c client_data * \return ZOK on success, or ZMARSHALLINGERROR if something went wrong */ int queue_sasl_request(zhandle_t *zh, const char *client_data, int client_data_len) { struct oarchive *oa; int rc; /* Java client use normal xid, too. */ struct RequestHeader h = { get_xid(), ZOO_SASL_OP }; struct GetSASLRequest req = { { client_data_len, client_data_len>0 ? (char *) client_data : "" } }; oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_GetSASLRequest(oa, "req", &req); rc = rc < 0 ? rc : queue_front_buffer_bytes(&zh->to_send, get_buffer(oa), get_buffer_len(oa)); close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "SASL: Queued request len=%d rc=%d", client_data_len, rc); return (rc < 0) ? ZMARSHALLINGERROR : ZOK; } /* * decode an expected SASL response and perform the corresponding * authentication step */ static int process_sasl_response(zhandle_t *zh, char *buffer, int len) { struct iarchive *ia = create_buffer_iarchive(buffer, len); struct ReplyHeader hdr; struct SetSASLResponse res; int rc; memset(&res, 0, sizeof(res)); rc = ia ? ZOK : ZSYSTEMERROR; rc = rc < 0 ? rc : deserialize_ReplyHeader(ia, "hdr", &hdr); rc = rc < 0 ? rc : hdr.err; rc = rc < 0 ? rc : deserialize_SetSASLResponse(ia, "reply", &res); rc = rc < 0 ? rc : zoo_sasl_client_step(zh, res.token.buff, res.token.len); deallocate_SetSASLResponse(&res); if (ia) { close_buffer_iarchive(&ia); } LOG_DEBUG(LOGCALLBACK(zh), "SASL: Processed response len=%d rc=%d", len, rc); return rc; } #endif /* HAVE_CYRUS_SASL_H */ static int check_events(zhandle_t *zh, int events) { if (zh->fd->sock == -1) return ZINVALIDSTATE; #ifdef HAVE_OPENSSL_H if ((events&ZOOKEEPER_WRITE) && (zh->state == ZOO_SSL_CONNECTING_STATE) && zh->fd->cert != NULL) { int rc, error; socklen_t len = sizeof(error); rc = getsockopt(zh->fd->sock, 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__, __func__, ZCONNECTIONLOSS, "server refused to accept the client"); } // We do SSL_connect() here if (init_ssl_for_handler(zh) != ZOK) { return ZSSLCONNECTIONERROR; } } #endif if ((events&ZOOKEEPER_WRITE)&&(zh->state == ZOO_CONNECTING_STATE)) { int rc, error; socklen_t len = sizeof(error); rc = getsockopt(zh->fd->sock, 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__, __func__, ZCONNECTIONLOSS, "server refused to accept the client"); } if((rc=prime_connection(zh))!=0) return rc; LOG_INFO(LOGCALLBACK(zh), "initiated connection to server %s", format_endpoint_info(&zh->addr_cur)); 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__, __func__, 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, zh->input_buffer); if (rc < 0) { return handle_socket_error_msg(zh, __LINE__, __func__, ZCONNECTIONLOSS, "failed while receiving a server response"); } if (rc > 0) { get_system_time(&zh->last_recv); if (zh->input_buffer != &zh->primer_buffer) { if (is_connected(zh) || !is_sasl_auth_in_progress(zh)) { queue_buffer(&zh->to_process, zh->input_buffer, 0); #ifdef HAVE_CYRUS_SASL_H } else { rc = process_sasl_response(zh, zh->input_buffer->buffer, zh->input_buffer->curr_offset); free_buffer(zh->input_buffer); zh->input_buffer = 0; if (rc < 0) { zoo_sasl_mark_failed(zh); return rc; } else if (zh->sasl_client->state == ZOO_SASL_COMPLETE) { /* * SASL authentication just completed; send * watches, auth. info, etc. now. */ finalize_session_establishment(zh); } #endif /* HAVE_CYRUS_SASL_H */ } } 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->seen_rw_server_before ? zh->client_id.client_id : 0; zh->seen_rw_server_before |= !zh->primer_storage.readOnly; 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__, __func__, 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)); #ifdef HAVE_CYRUS_SASL_H if (zh->sasl_client) { /* * Start a SASL authentication session. * Watches, auth. info, etc. will be sent * after it completes. */ rc = zoo_sasl_connect(zh); rc = rc < 0 ? rc : zoo_sasl_client_start(zh); if (rc < 0) { zoo_sasl_mark_failed(zh); return rc; } } else { /* Can send watches, auth. info, etc. immediately. */ finalize_session_establishment(zh); } #else /* HAVE_CYRUS_SASL_H */ /* Can send watches, auth. info, etc. immediately. */ finalize_session_establishment(zh); #endif /* HAVE_CYRUS_SASL_H */ } } 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; } //#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(LOGCALLBACK(zh), "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(zh, WATCHER_EVENT_XID,-1,0,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); lock_watchers(zh); cptr->c.watcher_result = collectWatchers(zh, ZOO_SESSION_EVENT, ""); unlock_watchers(zh); 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; } // cleanup completion list of a failed multi request static void cleanup_failed_multi(zhandle_t *zh, int xid, int rc, completion_list_t *cptr) { completion_list_t *entry; completion_head_t *clist = &cptr->c.clist; while ((entry = dequeue_completion(clist)) != NULL) { // Fake failed response for all sub-requests deserialize_response(zh, entry->c.type, xid, 1, rc, entry, NULL); destroy_completion_entry(entry); } } static int deserialize_multi(zhandle_t *zh, int xid, completion_list_t *cptr, struct iarchive *ia) { int rc = 0; completion_head_t *clist = &cptr->c.clist; struct MultiHeader mhdr = {0, 0, 0}; assert(clist); deserialize_MultiHeader(ia, "multiheader", &mhdr); while (!mhdr.done) { completion_list_t *entry = dequeue_completion(clist); assert(entry); if (mhdr.type == -1) { struct ErrorResponse er; deserialize_ErrorResponse(ia, "error", &er); mhdr.err = er.err ; if (rc == 0 && er.err != 0 && er.err != ZRUNTIMEINCONSISTENCY) { rc = er.err; } } deserialize_response(zh, entry->c.type, xid, mhdr.type == -1, mhdr.err, entry, ia); deserialize_MultiHeader(ia, "multiheader", &mhdr); //While deserializing the response we must destroy completion entry for each operation in //the zoo_multi transaction. Otherwise this results in memory leak when client invokes zoo_multi //operation. destroy_completion_entry(entry); } return rc; } static void deserialize_response(zhandle_t *zh, int type, int xid, int failed, int rc, completion_list_t *cptr, struct iarchive *ia) { switch (type) { case COMPLETION_DATA: LOG_DEBUG(LOGCALLBACK(zh), "Calling COMPLETION_DATA for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); if (failed) { 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(LOGCALLBACK(zh), "Calling COMPLETION_STAT for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); if (failed) { 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(LOGCALLBACK(zh), "Calling COMPLETION_STRINGLIST for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); if (failed) { 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(LOGCALLBACK(zh), "Calling COMPLETION_STRINGLIST_STAT for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); if (failed) { 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(LOGCALLBACK(zh), "Calling COMPLETION_STRING for xid=%#x failed=%d, rc=%d", cptr->xid, failed, rc); if (failed) { cptr->c.string_result(rc, 0, cptr->data); } else { struct CreateResponse res; const char *client_path; memset(&res, 0, sizeof(res)); deserialize_CreateResponse(ia, "reply", &res); client_path = sub_string(zh, res.path); cptr->c.string_result(rc, client_path, cptr->data); free_duplicate_path(client_path, res.path); deallocate_CreateResponse(&res); } break; case COMPLETION_STRING_STAT: LOG_DEBUG(LOGCALLBACK(zh), "Calling COMPLETION_STRING_STAT for xid=%#x failed=%d, rc=%d", cptr->xid, failed, rc); if (failed) { cptr->c.string_stat_result(rc, 0, 0, cptr->data); } else { struct Create2Response res; const char *client_path; deserialize_Create2Response(ia, "reply", &res); client_path = sub_string(zh, res.path); cptr->c.string_stat_result(rc, client_path, &res.stat, cptr->data); free_duplicate_path(client_path, res.path); deallocate_Create2Response(&res); } break; case COMPLETION_ACLLIST: LOG_DEBUG(LOGCALLBACK(zh), "Calling COMPLETION_ACLLIST for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); if (failed) { 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(LOGCALLBACK(zh), "Calling COMPLETION_VOID for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); assert(cptr->c.void_result); cptr->c.void_result(rc, cptr->data); break; case COMPLETION_MULTI: LOG_DEBUG(LOGCALLBACK(zh), "Calling COMPLETION_MULTI for xid=%#x failed=%d rc=%d", cptr->xid, failed, rc); assert(cptr->c.void_result); if (failed) { cleanup_failed_multi(zh, xid, rc, cptr); } else { rc = deserialize_multi(zh, xid, cptr, ia); } cptr->c.void_result(rc, cptr->data); break; default: LOG_DEBUG(LOGCALLBACK(zh), "Unsupported completion type=%d", cptr->c.type); } } /* 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(LOGCALLBACK(zh), "Calling a watcher for node [%s], type = %d event=%s", (evt.path==NULL?"NULL":evt.path), cptr->c.type, watcherEvent2String(type)); deliverWatchers(zh,type,state,evt.path, &cptr->c.watcher_result); deallocate_WatcherEvent(&evt); } else { deserialize_response(zh, cptr->c.type, hdr.xid, hdr.err != 0, hdr.err, cptr, ia); } destroy_completion_entry(cptr); close_buffer_iarchive(&ia); } } static void isSocketReadable(zhandle_t* zh) { #ifndef _WIN32 struct pollfd fds; fds.fd = zh->fd->sock; 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 fd_set rfds; struct timeval waittime = {0, 0}; FD_ZERO(&rfds); FD_SET( zh->fd , &rfds); if (select(0, &rfds, NULL, NULL, &waittime) <= 0){ // socket not readable -- no more responses to process zh->socket_readable.tv_sec=zh->socket_readable.tv_usec=0; } #endif else{ get_system_time(&zh->socket_readable); } } static void checkResponseLatency(zhandle_t* zh) { int delay; struct timeval now; if(zh->socket_readable.tv_sec==0) return; get_system_time(&now); delay=calculate_interval(&zh->socket_readable, &now); if(delay>20) LOG_DEBUG(LOGCALLBACK(zh), "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.xid == PING_XID) { // Ping replies can arrive out-of-order int elapsed = 0; struct timeval now; gettimeofday(&now, 0); elapsed = calculate_interval(&zh->last_ping, &now); LOG_DEBUG(LOGCALLBACK(zh), "Got ping response in %d ms", elapsed); free_buffer(bptr); } else if (hdr.xid == WATCHER_EVENT_XID) { struct WatcherEvent evt; int type = 0; char *path = NULL; completion_list_t *c = NULL; LOG_DEBUG(LOGCALLBACK(zh), "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(zh, WATCHER_EVENT_XID,-1,0,0,0,0); c->buffer = bptr; lock_watchers(zh); c->c.watcher_result = collectWatchers(zh, type, path); unlock_watchers(zh); // 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(LOGCALLBACK(zh), "Processing SET_WATCHES"); free_buffer(bptr); } else if (hdr.xid == AUTH_XID){ LOG_DEBUG(LOGCALLBACK(zh), "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 && cptr == NULL) { LOG_DEBUG(LOGCALLBACK(zh), "Completion queue has been cleared by zookeeper_close()"); close_buffer_iarchive(&ia); free_buffer(bptr); return api_epilog(zh,ZINVALIDSTATE); } assert(cptr); /* The requests are going to come back in order */ if (cptr->xid != hdr.xid) { LOG_DEBUG(LOGCALLBACK(zh), "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 api_epilog(zh, handle_socket_error_msg(zh, __LINE__, __func__, ZRUNTIMEINCONSISTENCY, "unexpected server response: expected %#x, but received %#x", hdr.xid,cptr->xid)); } if (hdr.zxid > 0) { // Update last_zxid only when it is a request response zh->last_zxid = hdr.zxid; } lock_watchers(zh); activateWatcher(zh, cptr->watcher, rc); deactivateWatcher(zh, cptr->watcher_deregistration, rc); unlock_watchers(zh); if (cptr->c.void_result != SYNCHRONOUS_MARKER) { LOG_DEBUG(LOGCALLBACK(zh), "Queueing asynchronous response"); cptr->buffer = bptr; queue_completion(&zh->completions_to_process, cptr, 0); } else { #ifdef THREADED struct sync_completion *sc = (struct sync_completion*)cptr->data; sc->rc = rc; process_sync_completion(zh, cptr, sc, ia); notify_sync_completion(sc); free_buffer(bptr); zh->outstanding_sync--; destroy_completion_entry(cptr); #else abort_singlethreaded(zh); #endif } } 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 watcher_deregistration_t* create_watcher_deregistration(const char* path, watcher_fn watcher, void *watcherCtx, ZooWatcherType wtype) { watcher_deregistration_t *wdo; wdo = calloc(1, sizeof(watcher_deregistration_t)); if (!wdo) { return NULL; } wdo->path = strdup(path); wdo->watcher = watcher; wdo->context = watcherCtx; wdo->type = wtype; return wdo; } static void destroy_watcher_registration(watcher_registration_t* wo){ if(wo!=0){ free((void*)wo->path); free(wo); } } static void destroy_watcher_deregistration(watcher_deregistration_t *wdo) { if (wdo) { free((void *)wdo->path); free(wdo); } } static completion_list_t* create_completion_entry(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data,watcher_registration_t* wo, completion_head_t *clist) { return do_create_completion_entry(zh, xid, completion_type, dc, data, wo, clist, NULL); } static completion_list_t* create_completion_entry_deregistration(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, watcher_deregistration_t* wdo, completion_head_t *clist) { return do_create_completion_entry(zh, xid, completion_type, dc, data, NULL, clist, wdo); } static completion_list_t* do_create_completion_entry(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, watcher_registration_t* wo, completion_head_t *clist, watcher_deregistration_t* wdo) { completion_list_t *c = calloc(1, sizeof(completion_list_t)); if (!c) { LOG_ERROR(LOGCALLBACK(zh), "out of memory"); return 0; } c->c.type = completion_type; c->data = data; switch(c->c.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_STRING_STAT: c->c.string_stat_result = (string_stat_completion_t)dc; break; case COMPLETION_ACLLIST: c->c.acl_result = (acl_completion_t)dc; break; case COMPLETION_MULTI: assert(clist); c->c.void_result = (void_completion_t)dc; c->c.clist = *clist; break; } c->xid = xid; c->watcher = wo; c->watcher_deregistration = wdo; return c; } static void destroy_completion_entry(completion_list_t* c){ if(c!=0){ destroy_watcher_registration(c->watcher); destroy_watcher_deregistration(c->watcher_deregistration); 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_head_t *clist) { completion_list_t *c =create_completion_entry(zh, xid, completion_type, dc, data, wo, clist); return do_add_completion(zh, dc, c, add_to_front); } static int add_completion_deregistration(zhandle_t *zh, int xid, int completion_type, const void *dc, const void *data, int add_to_front, watcher_deregistration_t* wdo, completion_head_t *clist) { completion_list_t *c = create_completion_entry_deregistration(zh, xid, completion_type, dc, data, wdo, clist); return do_add_completion(zh, dc, c, add_to_front); } static int do_add_completion(zhandle_t *zh, const void *dc, completion_list_t *c, int add_to_front) { 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); if (dc == SYNCHRONOUS_MARKER) { zh->outstanding_sync++; } rc = ZOK; } else { free(c); rc = ZINVALIDSTATE; } unlock_completion_list(&zh->sent_requests); 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, 0); } 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, 0); } 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, 0); } 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, 0); } 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, 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, 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, 0); } static int add_string_stat_completion(zhandle_t *zh, int xid, string_stat_completion_t dc, const void *data) { return add_completion(zh, xid, COMPLETION_STRING_STAT, dc, data, 0, 0, 0); } static int add_multi_completion(zhandle_t *zh, int xid, void_completion_t dc, const void *data, completion_head_t *clist) { return add_completion(zh, xid, COMPLETION_MULTI, dc, data, 0,0, clist); } /** * After sending the close request, we are waiting for a given millisecs for * getting the answer and/or for the socket to be closed by the server. * * This function should not be called while we still want to process * any response from the server. It must be called after adaptor_finish called, * in order not to mess with the I/O receiver thread in multi-threaded mode. */ int wait_for_session_to_be_closed(zhandle_t *zh, int timeout_ms) { int ret = 0; #ifndef WIN32 struct pollfd fd_s[1]; #else fd_set rfds; struct timeval waittime = {timeout_ms / 1000, (timeout_ms % 1000) * 1000}; #endif if (zh == NULL) { return ZBADARGUMENTS; } #ifndef WIN32 fd_s[0].fd = zh->fd->sock; fd_s[0].events = POLLIN; ret = poll(fd_s, 1, timeout_ms); #else FD_ZERO(&rfds); FD_SET(zh->fd->sock , &rfds); ret = select(zh->fd->sock + 1, &rfds, NULL, NULL, &waittime); #endif if (ret == 0){ LOG_WARN(LOGCALLBACK(zh), "Timed out (%dms) during waiting for server's reply after sending a close request, sessionId=%#llx\n", timeout_ms, zh->client_id.client_id); } else if (ret < 0) { LOG_WARN(LOGCALLBACK(zh), "System error (%d) happened while waiting for server's reply, sessionId=%#llx\n", ret, zh->client_id.client_id); } return ZOK; } int zookeeper_close(zhandle_t *zh) { int rc=ZOK; if (zh==0) return ZBADARGUMENTS; zh->close_requested=1; if (inc_ref_counter(zh,1)>1) { /* We have incremented the ref counter to prevent the * completions from calling zookeeper_close before we have * completed the adaptor_finish call below. */ /* Signal any syncronous completions before joining the threads */ enter_critical(zh); free_completions(zh,1,ZCLOSING); leave_critical(zh); adaptor_finish(zh); /* Now we can allow the handle to be cleaned up, if the completion * threads finished during the adaptor_finish call. */ api_epilog(zh, 0); return ZOK; } /* No need to decrement the counter since we're just going to * destroy the handle later. */ if (is_connected(zh)) { struct oarchive *oa; struct RequestHeader h = {get_xid(), ZOO_CLOSE_OP}; LOG_INFO(LOGCALLBACK(zh), "Closing zookeeper sessionId=%#llx to %s\n", zh->client_id.client_id, zoo_get_current_server(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) { LOG_DEBUG(LOGCALLBACK(zh), "Error during closing zookeeper session, sessionId=%#llx to %s (error: %d)\n", zh->client_id.client_id, zoo_get_current_server(zh), rc); rc = ZMARSHALLINGERROR; } else { /* 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); /* give some time to the server to process the session close request properly */ rc = rc < 0 ? rc : wait_for_session_to_be_closed(zh, 1500); } } else { rc = ZOK; } LOG_INFO(LOGCALLBACK(zh), "Freeing zookeeper resources for sessionId=%#llx\n", zh->client_id.client_id); destroy(zh); adaptor_destroy(zh); free(zh->fd); free(zh); #ifdef _WIN32 Win32WSACleanup(); #endif return rc; } static int isValidPath(const char* path, const int mode) { 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] == '/' && !ZOOKEEPER_IS_SEQUENCE(mode)) 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) && !ZOOKEEPER_IS_SEQUENCE(mode)) || path[i+1] == '/')) { return 0; } } else if (c == '.') { if ((path[i-1] == '/') && (((i + 1 == len) && !ZOOKEEPER_IS_SEQUENCE(mode)) || path[i+1] == '/')) { return 0; } } else if (c > 0x00 && c < 0x1f) { return 0; } } return 1; } /*---------------------------------------------------------------------------* * REQUEST INIT HELPERS *---------------------------------------------------------------------------*/ /* Common Request init helper functions to reduce code duplication */ static int Request_path_init(zhandle_t *zh, int mode, char **path_out, const char *path) { assert(path_out); *path_out = prepend_string(zh, path); if (zh == NULL || !isValidPath(*path_out, mode)) { free_duplicate_path(*path_out, path); return ZBADARGUMENTS; } if (is_unrecoverable(zh)) { free_duplicate_path(*path_out, path); return ZINVALIDSTATE; } return ZOK; } static int Request_path_watch_init(zhandle_t *zh, int mode, char **path_out, const char *path, int32_t *watch_out, uint32_t watch) { int rc = Request_path_init(zh, mode, path_out, path); if (rc != ZOK) { return rc; } *watch_out = watch; return ZOK; } /*---------------------------------------------------------------------------* * ASYNC API *---------------------------------------------------------------------------*/ /* make an attempt to send queued requests immediately without blocking */ static int nonblocking_send(zhandle_t *zh, int rc) { if (adaptor_send_queue(zh, 0) < 0) { if (zh->fd->sock != -1) { close_zsock(zh->fd); zh->state = ZOO_NOTCONNECTED_STATE; } } return (rc < 0) ? ZMARSHALLINGERROR : ZOK; } 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 = {get_xid(), ZOO_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(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } int zoo_agetconfig(zhandle_t *zh, int watch, data_completion_t dc, const void *data) { return zoo_awgetconfig(zh,watch?zh->watcher:0,zh->context,dc,data); } int zoo_awgetconfig(zhandle_t *zh, watcher_fn watcher, void* watcherCtx, data_completion_t dc, const void *data) { struct oarchive *oa; char *path = ZOO_CONFIG_NODE; char *server_path = ZOO_CONFIG_NODE; struct RequestHeader h = { get_xid(), ZOO_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(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } int zoo_areconfig(zhandle_t *zh, const char *joining, const char *leaving, const char *members, int64_t version, data_completion_t dc, const void *data) { struct oarchive *oa; struct RequestHeader h = { get_xid(), ZOO_RECONFIG_OP }; struct ReconfigRequest req; int rc = 0; if (zh==0) { return ZBADARGUMENTS; } if (is_unrecoverable(zh)) { return ZINVALIDSTATE; } oa=create_buffer_oarchive(); req.joiningServers = (char *)joining; req.leavingServers = (char *)leaving; req.newMembers = (char *)members; req.curConfigId = version; rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_ReconfigRequest(oa, "req", &req); enter_critical(zh); rc = rc < 0 ? rc : add_data_completion(zh, h.xid, dc, data, NULL); rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), get_buffer_len(oa)); leave_critical(zh); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending Reconfig request xid=%#x to %s",h.xid, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } static int SetDataRequest_init(zhandle_t *zh, struct SetDataRequest *req, const char *path, const char *buffer, int buflen, int version) { int rc; assert(req); rc = Request_path_init(zh, 0, &req->path, path); if (rc != ZOK) { return rc; } req->data.buff = (char*)buffer; req->data.len = buflen; req->version = version; return 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 = {get_xid(), ZOO_SETDATA_OP}; struct SetDataRequest req; int rc = SetDataRequest_init(zh, &req, path, buffer, buflen, version); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); 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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } static int CreateRequest_init(zhandle_t *zh, struct CreateRequest *req, const char *path, const char *value, int valuelen, const struct ACL_vector *acl_entries, int mode) { int rc; assert(req); rc = Request_path_init(zh, mode, &req->path, path); assert(req); if (rc != ZOK) { return rc; } req->flags = mode; 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; } return ZOK; } static int CreateTTLRequest_init(zhandle_t *zh, struct CreateTTLRequest *req, const char *path, const char *value, int valuelen, const struct ACL_vector *acl_entries, int mode, int64_t ttl) { int rc; assert(req); rc = Request_path_init(zh, mode, &req->path, path); assert(req); if (rc != ZOK) { return rc; } req->flags = mode; 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; } req->ttl = ttl; return ZOK; } static int get_create_op_type(int mode, int default_op) { if (mode == ZOO_CONTAINER) { return ZOO_CREATE_CONTAINER_OP; } else if (ZOOKEEPER_IS_TTL(mode)) { return ZOO_CREATE_TTL_OP; } else { return default_op; } } int zoo_acreate(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl_entries, int mode, string_completion_t completion, const void *data) { return zoo_acreate_ttl(zh, path, value, valuelen, acl_entries, mode, -1, completion, data); } int zoo_acreate_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl_entries, int mode, int64_t ttl, string_completion_t completion, const void *data) { struct oarchive *oa; struct RequestHeader h = {get_xid(), get_create_op_type(mode, ZOO_CREATE_OP)}; int rc; char *req_path; if (ZOOKEEPER_IS_TTL(mode)) { struct CreateTTLRequest req; if (ttl <= 0 || ttl > ZOO_MAX_TTL) { return ZBADARGUMENTS; } rc = CreateTTLRequest_init(zh, &req, path, value, valuelen, acl_entries, mode, ttl); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_CreateTTLRequest(oa, "req", &req); req_path = req.path; } else { struct CreateRequest req; if (ttl >= 0) { return ZBADARGUMENTS; } rc = CreateRequest_init(zh, &req, path, value, valuelen, acl_entries, mode); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req); req_path = req.path; } 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(req_path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } int zoo_acreate2(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl_entries, int mode, string_stat_completion_t completion, const void *data) { return zoo_acreate2_ttl(zh, path, value, valuelen, acl_entries, mode, -1, completion, data); } int zoo_acreate2_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl_entries, int mode, int64_t ttl, string_stat_completion_t completion, const void *data) { struct oarchive *oa; struct RequestHeader h = { get_xid(), get_create_op_type(mode, ZOO_CREATE2_OP) }; int rc; char *req_path; if (ZOOKEEPER_IS_TTL(mode)) { struct CreateTTLRequest req; if (ttl <= 0 || ttl > ZOO_MAX_TTL) { return ZBADARGUMENTS; } rc = CreateTTLRequest_init(zh, &req, path, value, valuelen, acl_entries, mode, ttl); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_CreateTTLRequest(oa, "req", &req); req_path = req.path; } else { struct CreateRequest req; if (ttl >= 0) { return ZBADARGUMENTS; } rc = CreateRequest_init(zh, &req, path, value, valuelen, acl_entries, mode); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req); req_path = req.path; } enter_critical(zh); rc = rc < 0 ? rc : add_string_stat_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(req_path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } int DeleteRequest_init(zhandle_t *zh, struct DeleteRequest *req, const char *path, int version) { int rc = Request_path_init(zh, 0, &req->path, path); if (rc != ZOK) { return rc; } req->version = version; return 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 = {get_xid(), ZOO_DELETE_OP}; struct DeleteRequest req; int rc = DeleteRequest_init(zh, &req, path, version); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); 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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } 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 = {get_xid(), ZOO_EXISTS_OP}; struct ExistsRequest req; int rc = Request_path_watch_init(zh, 0, &req.path, path, &req.watch, watcher != NULL); if (rc != ZOK) { return rc; } 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(req.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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } 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 = {get_xid(), ZOO_GETCHILDREN_OP}; struct GetChildrenRequest req ; int rc = Request_path_watch_init(zh, 0, &req.path, path, &req.watch, watcher != NULL); if (rc != ZOK) { return rc; } 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(req.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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } 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 = {get_xid(), ZOO_GETCHILDREN2_OP}; struct GetChildren2Request req ; int rc = Request_path_watch_init(zh, 0, &req.path, path, &req.watch, watcher != NULL); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); rc = rc < 0 ? rc : serialize_GetChildren2Request(oa, "req", &req); enter_critical(zh); rc = rc < 0 ? rc : add_strings_stat_completion(zh, h.xid, ssc, data, create_watcher_registration(req.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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } 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 = {get_xid(), ZOO_SYNC_OP}; struct SyncRequest req; int rc = Request_path_init(zh, 0, &req.path, path); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); 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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } int zoo_aget_acl(zhandle_t *zh, const char *path, acl_completion_t completion, const void *data) { struct oarchive *oa; struct RequestHeader h = {get_xid(), ZOO_GETACL_OP}; struct GetACLRequest req; int rc = Request_path_init(zh, 0, &req.path, path) ; if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); 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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } 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 = {get_xid(), ZOO_SETACL_OP}; struct SetACLRequest req; int rc = Request_path_init(zh, 0, &req.path, path); if (rc != ZOK) { return rc; } oa = create_buffer_oarchive(); 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(req.path, path); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s",h.xid,path, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } /* Completions for multi-op results */ static void op_result_string_completion(int err, const char *value, const void *data) { struct zoo_op_result *result = (struct zoo_op_result *)data; assert(result); result->err = err; if (result->value && value) { int len = strlen(value) + 1; if (len > result->valuelen) { len = result->valuelen; } if (len > 0) { memcpy(result->value, value, len - 1); result->value[len - 1] = '\0'; } } else { result->value = NULL; } } static void op_result_void_completion(int err, const void *data) { struct zoo_op_result *result = (struct zoo_op_result *)data; assert(result); result->err = err; } static void op_result_stat_completion(int err, const struct Stat *stat, const void *data) { struct zoo_op_result *result = (struct zoo_op_result *)data; assert(result); result->err = err; if (result->stat && err == 0 && stat) { *result->stat = *stat; } else { result->stat = NULL ; } } static int CheckVersionRequest_init(zhandle_t *zh, struct CheckVersionRequest *req, const char *path, int version) { int rc ; assert(req); rc = Request_path_init(zh, 0, &req->path, path); if (rc != ZOK) { return rc; } req->version = version; return ZOK; } int zoo_amulti(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_result_t *results, void_completion_t completion, const void *data) { struct RequestHeader h = {get_xid(), ZOO_MULTI_OP}; struct MultiHeader mh = {-1, 1, -1}; struct oarchive *oa = create_buffer_oarchive(); completion_head_t clist = { 0 }; int rc = serialize_RequestHeader(oa, "header", &h); int index = 0; for (index=0; index < count; index++) { const zoo_op_t *op = ops+index; zoo_op_result_t *result = results+index; completion_list_t *entry = NULL; struct MultiHeader mh = {op->type, 0, -1}; rc = rc < 0 ? rc : serialize_MultiHeader(oa, "multiheader", &mh); switch(op->type) { case ZOO_CREATE_CONTAINER_OP: case ZOO_CREATE_OP: { struct CreateRequest req; rc = rc < 0 ? rc : CreateRequest_init(zh, &req, op->create_op.path, op->create_op.data, op->create_op.datalen, op->create_op.acl, op->create_op.flags); rc = rc < 0 ? rc : serialize_CreateRequest(oa, "req", &req); result->value = op->create_op.buf; result->valuelen = op->create_op.buflen; enter_critical(zh); entry = create_completion_entry(zh, h.xid, COMPLETION_STRING, op_result_string_completion, result, 0, 0); leave_critical(zh); free_duplicate_path(req.path, op->create_op.path); break; } case ZOO_DELETE_OP: { struct DeleteRequest req; rc = rc < 0 ? rc : DeleteRequest_init(zh, &req, op->delete_op.path, op->delete_op.version); rc = rc < 0 ? rc : serialize_DeleteRequest(oa, "req", &req); enter_critical(zh); entry = create_completion_entry(zh, h.xid, COMPLETION_VOID, op_result_void_completion, result, 0, 0); leave_critical(zh); free_duplicate_path(req.path, op->delete_op.path); break; } case ZOO_SETDATA_OP: { struct SetDataRequest req; rc = rc < 0 ? rc : SetDataRequest_init(zh, &req, op->set_op.path, op->set_op.data, op->set_op.datalen, op->set_op.version); rc = rc < 0 ? rc : serialize_SetDataRequest(oa, "req", &req); result->stat = op->set_op.stat; enter_critical(zh); entry = create_completion_entry(zh, h.xid, COMPLETION_STAT, op_result_stat_completion, result, 0, 0); leave_critical(zh); free_duplicate_path(req.path, op->set_op.path); break; } case ZOO_CHECK_OP: { struct CheckVersionRequest req; rc = rc < 0 ? rc : CheckVersionRequest_init(zh, &req, op->check_op.path, op->check_op.version); rc = rc < 0 ? rc : serialize_CheckVersionRequest(oa, "req", &req); enter_critical(zh); entry = create_completion_entry(zh, h.xid, COMPLETION_VOID, op_result_void_completion, result, 0, 0); leave_critical(zh); free_duplicate_path(req.path, op->check_op.path); break; } default: LOG_ERROR(LOGCALLBACK(zh), "Unimplemented sub-op type=%d in multi-op", op->type); return ZUNIMPLEMENTED; } queue_completion(&clist, entry, 0); } rc = rc < 0 ? rc : serialize_MultiHeader(oa, "multiheader", &mh); /* BEGIN: CRTICIAL SECTION */ enter_critical(zh); rc = rc < 0 ? rc : add_multi_completion(zh, h.xid, completion, data, &clist); rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), get_buffer_len(oa)); leave_critical(zh); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending multi request xid=%#x with %d subrequests to %s", h.xid, index, zoo_get_current_server(zh)); return nonblocking_send(zh, rc); } typedef union WatchesRequest WatchesRequest; union WatchesRequest { struct CheckWatchesRequest check; struct RemoveWatchesRequest remove; }; static int aremove_watches( zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local, void_completion_t *completion, const void *data, int all) { char *server_path = prepend_string(zh, path); int rc; struct oarchive *oa; struct RequestHeader h = { get_xid(), all ? ZOO_REMOVE_WATCHES : ZOO_CHECK_WATCHES }; WatchesRequest req; watcher_deregistration_t *wdo; if (!zh || !isValidPath(server_path, 0)) { rc = ZBADARGUMENTS; goto done; } if (!local && is_unrecoverable(zh)) { rc = ZINVALIDSTATE; goto done; } lock_watchers(zh); if (!pathHasWatcher(zh, server_path, wtype, watcher, watcherCtx)) { rc = ZNOWATCHER; unlock_watchers(zh); goto done; } if (local) { removeWatchers(zh, server_path, wtype, watcher, watcherCtx); unlock_watchers(zh); #ifdef THREADED notify_sync_completion((struct sync_completion *)data); #endif rc = ZOK; goto done; } unlock_watchers(zh); oa = create_buffer_oarchive(); rc = serialize_RequestHeader(oa, "header", &h); if (all) { req.remove.path = (char*)server_path; req.remove.type = wtype; rc = rc < 0 ? rc : serialize_RemoveWatchesRequest(oa, "req", &req.remove); } else { req.check.path = (char*)server_path; req.check.type = wtype; rc = rc < 0 ? rc : serialize_CheckWatchesRequest(oa, "req", &req.check); } if (rc < 0) { goto done; } wdo = create_watcher_deregistration( server_path, watcher, watcherCtx, wtype); if (!wdo) { rc = ZSYSTEMERROR; goto done; } enter_critical(zh); rc = add_completion_deregistration( zh, h.xid, COMPLETION_VOID, completion, data, 0, wdo, 0); rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa), get_buffer_len(oa)); leave_critical(zh); /* We queued the buffer, so don't free it */ close_buffer_oarchive(&oa, 0); LOG_DEBUG(LOGCALLBACK(zh), "Sending request xid=%#x for path [%s] to %s", h.xid, path, zoo_get_current_server(zh)); rc = nonblocking_send(zh, rc); done: free_duplicate_path(server_path, path); return rc; } void zoo_create_op_init(zoo_op_t *op, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, char *path_buffer, int path_buffer_len) { assert(op); op->type = get_create_op_type(mode, ZOO_CREATE_OP); op->create_op.path = path; op->create_op.data = value; op->create_op.datalen = valuelen; op->create_op.acl = acl; op->create_op.flags = mode; op->create_op.ttl = 0; op->create_op.buf = path_buffer; op->create_op.buflen = path_buffer_len; } void zoo_create2_op_init(zoo_op_t *op, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, char *path_buffer, int path_buffer_len) { assert(op); op->type = get_create_op_type(mode, ZOO_CREATE2_OP); op->create_op.path = path; op->create_op.data = value; op->create_op.datalen = valuelen; op->create_op.acl = acl; op->create_op.flags = mode; op->create_op.buf = path_buffer; op->create_op.buflen = path_buffer_len; } void zoo_delete_op_init(zoo_op_t *op, const char *path, int version) { assert(op); op->type = ZOO_DELETE_OP; op->delete_op.path = path; op->delete_op.version = version; } void zoo_set_op_init(zoo_op_t *op, const char *path, const char *buffer, int buflen, int version, struct Stat *stat) { assert(op); op->type = ZOO_SETDATA_OP; op->set_op.path = path; op->set_op.data = buffer; op->set_op.datalen = buflen; op->set_op.version = version; op->set_op.stat = stat; } void zoo_check_op_init(zoo_op_t *op, const char *path, int version) { assert(op); op->type = ZOO_CHECK_OP; op->check_op.path = path; op->check_op.version = version; } /* 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; #ifdef _WIN32 fd_set pollSet; struct timeval wait; #endif get_system_time(&started); // 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 && (is_connected(zh) || is_sasl_auth_in_progress(zh))) { if (is_sasl_auth_in_progress(zh)) { // We don't let non-SASL packets escape as long as // negotiation is not complete. (SASL packets are always // pushed to the front of the queue.) buffer_list_t *buff = zh->to_send.head; int32_t type; rc = extract_request_type(buff->buffer, buff->len, &type); if (rc < 0 || type != ZOO_SASL_OP) { break; } } if(timeout!=0){ #ifndef _WIN32 struct pollfd fds; #endif int elapsed; struct timeval now; get_system_time(&now); elapsed=calculate_interval(&started,&now); if (elapsed>timeout) { rc = ZOPERATIONTIMEOUT; break; } #ifdef _WIN32 wait = get_timeval(timeout-elapsed); FD_ZERO(&pollSet); FD_SET(zh->fd->sock, &pollSet); // Poll the socket rc = select((int)(zh->fd->sock)+1, NULL, &pollSet, NULL, &wait); #else fds.fd = zh->fd->sock; fds.events = POLLOUT; fds.revents = 0; rc = poll(&fds, 1, timeout-elapsed); #endif if (rc<=0) { /* timed out or an error or POLLERR */ rc = rc==0 ? ZOPERATIONTIMEOUT : ZSYSTEMERROR; break; } } rc = send_buffer(zh, 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); get_system_time(&zh->last_send); 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 ZNEWCONFIGNOQUORUM: return "no quorum of new config is connected and up-to-date with the leader of last commmitted config - try invoking reconfiguration after new servers are connected and synced"; case ZRECONFIGINPROGRESS: return "Another reconfiguration is in progress -- concurrent reconfigs not supported (yet)"; 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"; case ZNOTREADONLY: return "state-changing request is passed to read-only server"; case ZEPHEMERALONLOCALSESSION: return "attempt to create ephemeral node on a local session"; case ZNOWATCHER: return "the watcher couldn't be found"; case ZRECONFIGDISABLED: return "attempts to perform a reconfiguration operation when reconfiguration feature is disable"; case ZSESSIONCLOSEDREQUIRESASLAUTH: return "session closed by server because client is required to do SASL authentication"; case ZTHROTTLEDOP: return "Operation was throttled due to high load"; } 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; // [ZOOKEEPER-800] zoo_add_auth should return ZINVALIDSTATE if // the connection is closed. if (zoo_state(zh) == 0) { 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 (is_connected(zh) || // When associating, only send info packets if no SASL // negotiation is planned. (Such packets would be queued in // front of SASL packets, which is forbidden, and SASL // completion is followed by a 'send_auth_info' anyway.) (zh->state == ZOO_ASSOCIATING_STATE && !has_sasl_client(zh))) { return send_last_auth_info(zh); } return ZOK; } static const char* format_endpoint_info(const struct sockaddr_storage* ep) { static __thread char buf[134] = { 0 }; char addrstr[INET6_ADDRSTRLEN] = { 0 }; const char *fmtstring; void *inaddr; char is_inet6 = 0; // poor man's boolean #ifdef _WIN32 char * addrstring; #endif 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; is_inet6 = 1; } else { #endif inaddr=&((struct sockaddr_in*)ep)->sin_addr; port=((struct sockaddr_in*)ep)->sin_port; #if defined(AF_INET6) } #endif fmtstring = (is_inet6 ? "[%s]:%d" : "%s:%d"); #ifdef _WIN32 addrstring = inet_ntoa (*(struct in_addr*)inaddr); sprintf(buf,fmtstring,addrstring,ntohs(port)); #else inet_ntop(ep->ss_family,inaddr,addrstr,sizeof(addrstr)-1); sprintf(buf,fmtstring,addrstr,ntohs(port)); #endif return buf; } log_callback_fn zoo_get_log_callback(const zhandle_t* zh) { // Verify we have a valid handle if (zh == NULL) { return NULL; } return zh->log_callback; } void zoo_set_log_callback(zhandle_t *zh, log_callback_fn callback) { // Verify we have a valid handle if (zh == NULL) { return; } zh->log_callback = callback; } void zoo_deterministic_conn_order(int yesOrNo) { disable_conn_permute=yesOrNo; } #ifdef THREADED static void process_sync_completion(zhandle_t *zh, completion_list_t *cptr, struct sync_completion *sc, struct iarchive *ia) { LOG_DEBUG(LOGCALLBACK(zh), "Processing sync_completion with type=%d xid=%#x rc=%d", cptr->c.type, cptr->xid, sc->rc); switch(cptr->c.type) { case COMPLETION_DATA: if (sc->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: if (sc->rc==0) { struct SetDataResponse res; deserialize_SetDataResponse(ia, "reply", &res); sc->u.stat = res.stat; deallocate_SetDataResponse(&res); } break; case COMPLETION_STRINGLIST: if (sc->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: if (sc->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: if (sc->rc==0) { struct CreateResponse res; int len; const char * client_path; deserialize_CreateResponse(ia, "reply", &res); //ZOOKEEPER-1027 client_path = sub_string(zh, res.path); len = strlen(client_path) + 1;if (len > sc->u.str.str_len) { len = sc->u.str.str_len; } if (len > 0) { memcpy(sc->u.str.str, client_path, len - 1); sc->u.str.str[len - 1] = '\0'; } free_duplicate_path(client_path, res.path); deallocate_CreateResponse(&res); } break; case COMPLETION_STRING_STAT: if (sc->rc==0) { struct Create2Response res; int len; const char * client_path; deserialize_Create2Response(ia, "reply", &res); client_path = sub_string(zh, res.path); len = strlen(client_path) + 1; if (len > sc->u.str.str_len) { len = sc->u.str.str_len; } if (len > 0) { memcpy(sc->u.str.str, client_path, len - 1); sc->u.str.str[len - 1] = '\0'; } free_duplicate_path(client_path, res.path); sc->u.stat = res.stat; deallocate_Create2Response(&res); } break; case COMPLETION_ACLLIST: if (sc->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: break; case COMPLETION_MULTI: sc->rc = deserialize_multi(zh, cptr->xid, cptr, ia); break; default: LOG_DEBUG(LOGCALLBACK(zh), "Unsupported completion type=%d", cptr->c.type); break; } } /*---------------------------------------------------------------------------* * SYNC API *---------------------------------------------------------------------------*/ int zoo_create(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, char *path_buffer, int path_buffer_len) { return zoo_create_ttl(zh, path, value, valuelen, acl, mode, -1, path_buffer, path_buffer_len); } int zoo_create_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, int64_t ttl, 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_ttl(zh, path, value, valuelen, acl, mode, ttl, SYNCHRONOUS_MARKER, sc); if(rc==ZOK){ wait_sync_completion(sc); rc = sc->rc; } free_sync_completion(sc); return rc; } int zoo_create2(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, char *path_buffer, int path_buffer_len, struct Stat *stat) { return zoo_create2_ttl(zh, path, value, valuelen, acl, mode, -1, path_buffer, path_buffer_len, stat); } int zoo_create2_ttl(zhandle_t *zh, const char *path, const char *value, int valuelen, const struct ACL_vector *acl, int mode, int64_t ttl, char *path_buffer, int path_buffer_len, struct Stat *stat) { 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_acreate2_ttl(zh, path, value, valuelen, acl, mode, ttl, 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_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_getconfig(zhandle_t *zh, int watch, char *buffer, int* buffer_len, struct Stat *stat) { return zoo_wget(zh,ZOO_CONFIG_NODE,watch?zh->watcher:0,zh->context, buffer,buffer_len,stat); } int zoo_wgetconfig(zhandle_t *zh, watcher_fn watcher, void* watcherCtx, char *buffer, int* buffer_len, struct Stat *stat) { return zoo_wget(zh, ZOO_CONFIG_NODE, watcher, watcherCtx, buffer, buffer_len, stat); } int zoo_reconfig(zhandle_t *zh, const char *joining, const char *leaving, const char *members, int64_t version, 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_areconfig(zh, joining, leaving, members, version, 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; } static int remove_watches( zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *wctx, int local, int all) { int rc = 0; struct sync_completion *sc; if (!path) return ZBADARGUMENTS; sc = alloc_sync_completion(); if (!sc) return ZSYSTEMERROR; rc = aremove_watches(zh, path, wtype, watcher, wctx, local, SYNCHRONOUS_MARKER, sc, all); if (rc == ZOK) { wait_sync_completion(sc); rc = sc->rc; } free_sync_completion(sc); return rc; } int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_result_t *results) { int rc; struct sync_completion *sc = alloc_sync_completion(); if (!sc) { return ZSYSTEMERROR; } rc = zoo_amulti(zh, count, ops, results, SYNCHRONOUS_MARKER, sc); if (rc == ZOK) { wait_sync_completion(sc); rc = sc->rc; } free_sync_completion(sc); return rc; } int zoo_remove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local) { return remove_watches(zh, path, wtype, watcher, watcherCtx, local, 0); } int zoo_remove_all_watches( zhandle_t *zh, const char *path, ZooWatcherType wtype, int local) { return remove_watches(zh, path, wtype, NULL, NULL, local, 1); } #endif int zoo_aremove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local, void_completion_t *completion, const void *data) { return aremove_watches( zh, path, wtype, watcher, watcherCtx, local, completion, data, 0); } int zoo_aremove_all_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype, int local, void_completion_t *completion, const void *data) { return aremove_watches( zh, path, wtype, NULL, NULL, local, completion, data, 1); } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh0100755 0000000 0000000 00000007337 15051152474 027256 0ustar00rootroot0000000 0000000 #!/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 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. # # determining the domain name in the certificates: # - use the first commandline argument, if present # - if not, then use the fully qualified domain name # - if `hostname` command fails, fall back to zookeeper.apache.org FQDN=`hostname -f` FQDN=${1:-$FQDN} FQDN=${FQDN:-"zookeeper.apache.org"} # Generate the root key openssl genrsa -out rootkey.pem 2048 #Generate the root Cert openssl req -x509 -new -key rootkey.pem -out root.crt -config <( cat <<-EOF [ req ] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [ dn ] C = US ST = California L = San Francisco O = ZooKeeper emailAddress = dev@$FQDN CN = $FQDN EOF ) #Generate Client Key openssl genrsa -out clientkey.pem 2048 #Generate Client Cert openssl req -new -key clientkey.pem -out client.csr -config <( cat <<-EOF [ req ] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [ dn ] C = US ST = California L = San Francisco O = ZooKeeper emailAddress = dev@$FQDN CN = $FQDN EOF ) openssl x509 -req -in client.csr -CA root.crt -CAkey rootkey.pem -CAcreateserial -days 3650 -out client.crt #Export in pkcs12 format openssl pkcs12 -export -in client.crt -inkey clientkey.pem -out client.pkcs12 -password pass:password # Import Keystore in JKS keytool -importkeystore -srckeystore client.pkcs12 -destkeystore client.jks -srcstoretype pkcs12 -srcstorepass password -deststorepass password ############################################################ #Generate Server key openssl genrsa -out serverkey.pem 2048 #Generate Server Cert openssl req -new -key serverkey.pem -out server.csr -config <( cat <<-EOF [ req ] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [ dn ] C = US ST = California L = San Francisco O = ZooKeeper emailAddress = dev@$FQDN CN = $FQDN EOF ) openssl x509 -req -in server.csr -CA root.crt -CAkey rootkey.pem -CAcreateserial -days 3650 -out server.crt #Export in pkcs12 format openssl pkcs12 -export -in server.crt -inkey serverkey.pem -out server.pkcs12 -password pass:password # Import Keystore in JKS keytool -importkeystore -srckeystore server.pkcs12 -destkeystore server.jks -srcstoretype pkcs12 -srcstorepass password -deststorepass password keytool -importcert -keystore server.jks -file root.crt -storepass password -noprompt keytool -importcert -alias ca -file root.crt -keystore clienttrust.jks -storepass password -noprompt keytool -importcert -alias clientcert -file client.crt -keystore clienttrust.jks -storepass password -noprompt keytool -importcert -alias ca -file root.crt -keystore servertrust.jks -storepass password -noprompt keytool -importcert -alias servercert -file server.crt -keystore servertrust.jks -storepass password -noprompt apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/CollectionUtil.h0100644 0000000 0000000 00000015074 15051152474 030545 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/CppAssertHelper.h0100644 0000000 0000000 00000003045 15051152474 030653 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/LibCMocks.cc0100644 0000000 0000000 00000024225 15051152474 027556 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 // needed for _POSIX_MONOTONIC_CLOCK #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 DECLARE_WRAPPER(void*,realloc,(void* p, size_t s)){ if(!Mock_realloc::mock_) return LIBC_SYMBOLS.realloc(p,s); return Mock_realloc::mock_->call(p,s); } #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){ #ifdef AF_UNIX /* don't mock UNIX domain sockets */ if (!Mock_socket::mock_ || addr->sa_family == AF_UNIX) #else if (!Mock_socket::mock_) #endif 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); } /* * Recent gcc with -O2 and glibc FORTIFY feature may cause our poll * mock to be ignored. */ #if __USE_FORTIFY_LEVEL > 0 int __poll_chk (struct pollfd *__fds, nfds_t __nfds, int __timeout, __SIZE_TYPE__ __fdslen) { return poll(__fds, __nfds, __timeout); } #endif // ***************************************************************************** // gettimeofday int gettimeofday(struct timeval *tp, GETTIMEOFDAY_ARG2_TYPE tzp){ if (!Mock_gettimeofday::mock_) return LIBC_SYMBOLS.gettimeofday(tp,tzp); return Mock_gettimeofday::mock_->call(tp,tzp); } Mock_gettimeofday* Mock_gettimeofday::mock_=0; // ***************************************************************************** #ifdef _POSIX_MONOTONIC_CLOCK // clock_gettime int clock_gettime(clockid_t id, struct timespec *tp) { if (!Mock_gettimeofday::mock_) return LIBC_SYMBOLS.clock_gettime(id,tp); struct timeval tv = { 0 }; int res = Mock_gettimeofday::mock_->call(&tv, NULL); tp->tv_sec = tv.tv_sec; tp->tv_nsec = tv.tv_usec * 1000; return res; } #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/LibCMocks.h0100644 0000000 0000000 00000026422 15051152474 027421 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/LibCSymTable.cc0100644 0000000 0000000 00000004747 15051152474 030231 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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" #include // needed for _POSIX_MONOTONIC_CLOCK #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 _POSIX_MONOTONIC_CLOCK LOAD_SYM(clock_gettime); #endif #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; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/LibCSymTable.h0100644 0000000 0000000 00000010251 15051152474 030056 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #include #include // needed for _POSIX_MONOTONIC_CLOCK #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 _POSIX_MONOTONIC_CLOCK DECLARE_SYM(int,clock_gettime,(clockid_t clk_id, struct timespec*)); #endif #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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/MocksBase.cc0100644 0000000 0000000 00000002230 15051152474 027607 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/MocksBase.h0100644 0000000 0000000 00000002146 15051152474 027457 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/PthreadMocks.cc0100644 0000000 0000000 00000007676 15051152474 030347 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/PthreadMocks.h0100644 0000000 0000000 00000036171 15051152474 030201 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestClient.cc0100644 0000000 0000000 00000153716 15051152474 030036 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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" #include "ZKMocks.h" struct buff_struct_2 { int32_t len; int32_t off; char *buffer; }; // TODO(br33d): the vast majority of this test is not usable with single threaded. // it needs a overhaul to work properly with both threaded and single // threaded (ZOOKEEPER-2640) #ifdef THREADED // For testing LogMessage Callback functionality list logMessages; void logMessageHandler(const char* message) { cout << "Log Message Received: [" << message << "]" << endl; logMessages.push_back(message); } 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(testLogCallbackSet); CPPUNIT_TEST(testLogCallbackInit); CPPUNIT_TEST(testLogCallbackClear); CPPUNIT_TEST(testAsyncWatcherAutoReset); CPPUNIT_TEST(testDeserializeString); CPPUNIT_TEST(testFirstServerDown); CPPUNIT_TEST(testNonexistentHost); #ifdef THREADED CPPUNIT_TEST(testNullData); #ifdef ZOO_IPV6_ENABLED CPPUNIT_TEST(testIPV6); #endif #ifdef HAVE_OPENSSL_H CPPUNIT_TEST(testSSL); #endif CPPUNIT_TEST(testCreate); CPPUNIT_TEST(testCreateContainer); CPPUNIT_TEST(testCreateTtl); 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); CPPUNIT_TEST(testLastZxid); CPPUNIT_TEST(testServersResolutionDelay); CPPUNIT_TEST(testRemoveWatchers); #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(watchctx_t *ctx, log_callback_fn logCallback) { zhandle_t *zk = zookeeper_init2(hostPorts, watcher, 10000, 0, ctx, 0, logCallback); ctx->zh = zk; sleep(1); return zk; } 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; } #ifdef HAVE_OPENSSL_H zhandle_t *createSSLClient(const char *hp, const char *cert, watchctx_t *ctx) { zhandle_t *zk = zookeeper_init_ssl(hp, cert, watcher, 30000, 0, ctx, 0); ctx->zh = zk; sleep(1); return zk; } #endif 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; struct String_vector str_vec = {0, NULL}; zrc = zoo_wget_children(zzh, "/mytest", default_zoo_watcher, NULL, &str_vec); CPPUNIT_ASSERT(zrc == ZOK || zrc == ZCLOSING); } /** ZOOKEEPER-1057 This checks that the client connects to the second server when the first is not reachable **/ void testFirstServerDown() { watchctx_t ctx; zoo_deterministic_conn_order(true); zhandle_t* zk = createClient("127.0.0.1:22182,127.0.0.1:22181", &ctx); CPPUNIT_ASSERT(zk != 0); CPPUNIT_ASSERT(ctx.waitForConnected(zk)); } /* Checks that a non-existent host will not block the connection*/ void testNonexistentHost() { char hosts[] = "jimmy:5555,127.0.0.1:22181"; watchctx_t ctx; zoo_deterministic_conn_order(true /* disable permute */); zhandle_t *zh = createClient(hosts, &ctx); CPPUNIT_ASSERT(ctx.waitForConnected(zh)); zoo_deterministic_conn_order(false /* enable permute */); } /** 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; char buff[10] = "testall"; char path[512]; watchctx_t *ctx = NULL; 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); CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_wget_children(zh, "/mytest", default_zoo_watcher, NULL, &str_vec); CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_create(zh, "/mytest/test1", buff, 10, &ZOO_OPEN_ACL_UNSAFE, 0, path, 512); CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_wget_children(zh, "/mytest", default_zoo_watcher, NULL, &str_vec); CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zrc = zoo_delete(zh, "/mytest/test1", -1); CPPUNIT_ASSERT_EQUAL((int)ZOK, zrc); zookeeper_close(zh); } void testBadDescriptor() { watchctx_t *ctx = NULL; zhandle_t *zh = zookeeper_init(hostPorts, NULL, 10000, 0, ctx, 0); sleep(1); zh->io_count = 0; //close socket close_zsock(zh->fd); sleep(1); //Check that doIo isn't spinning CPPUNIT_ASSERT(zh->io_count < 2); zookeeper_close(zh); } /* Checks the zoo_set_servers_resolution_delay default and operation */ void testServersResolutionDelay() { watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int rc; struct timeval tv; struct Stat stat; CPPUNIT_ASSERT(zk); CPPUNIT_ASSERT(zk->resolve_delay_ms == 0); // a) Default/0 case: resolve at each request. tv = zk->last_resolve; usleep(10000); // 10ms rc = zoo_exists(zk, "/", 0, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Must have changed because of the request. CPPUNIT_ASSERT(zk->last_resolve.tv_sec != tv.tv_sec || zk->last_resolve.tv_usec != tv.tv_usec); // b) Disabled/-1 case: never perform "routine" resolutions. rc = zoo_set_servers_resolution_delay(zk, -1); // Disabled CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); tv = zk->last_resolve; usleep(10000); // 10ms rc = zoo_exists(zk, "/", 0, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Must not have changed as auto-resolution is disabled. CPPUNIT_ASSERT(zk->last_resolve.tv_sec == tv.tv_sec && zk->last_resolve.tv_usec == tv.tv_usec); // c) Invalid delay is rejected. rc = zoo_set_servers_resolution_delay(zk, -1000); // Bad CPPUNIT_ASSERT_EQUAL((int)ZBADARGUMENTS, rc); // d) Valid delay, no resolution within window. rc = zoo_set_servers_resolution_delay(zk, 500); // 0.5s CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); tv = zk->last_resolve; usleep(10000); // 10ms rc = zoo_exists(zk, "/", 0, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Must not have changed because the request (hopefully!) // executed in less than 0.5s. CPPUNIT_ASSERT(zk->last_resolve.tv_sec == tv.tv_sec && zk->last_resolve.tv_usec == tv.tv_usec); // e) Valid delay, at least one resolution after delay. usleep(500 * 1000); // 0.5s rc = zoo_exists(zk, "/", 0, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Must have changed because we waited 0.5s between the // capture and the last request. CPPUNIT_ASSERT(zk->last_resolve.tv_sec != tv.tv_sec || zk->last_resolve.tv_usec != tv.tv_usec); } 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 stringStatCompletion(int rc, const char *value, const struct Stat *stat, const void *data) { stringCompletion(rc, value, data); CPPUNIT_ASSERT(stat != 0); } static void create_completion_fn(int rc, const char* value, const void *data) { CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); if (data) { const char *expected_value = (const char *)data; CPPUNIT_ASSERT_EQUAL(string(expected_value), string(value)); } 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, ctx5; 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); //[ZOOKEEPER-1108], test that auth info is sent to server, if client is not //connected to server when zoo_add_auth was called. zhandle_t *zk_auth = zookeeper_init(hostPorts, NULL, 10000, 0, NULL, 0); rc = zoo_add_auth(zk_auth, "digest", "pat:passwd", 10, voidCompletion, (void*)ZOK); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); sleep(2); CPPUNIT_ASSERT(count == 1); count = 0; CPPUNIT_ASSERT_EQUAL((int) ZOK, zookeeper_close(zk_auth)); struct sockaddr addr; socklen_t addr_len = sizeof(addr); zk = createClient(&ctx5); stopServer(); CPPUNIT_ASSERT(ctx5.waitForDisconnected(zk)); CPPUNIT_ASSERT(zookeeper_get_connected_host(zk, &addr, &addr_len) == NULL); addr_len = sizeof(addr); startServer(); CPPUNIT_ASSERT(ctx5.waitForConnected(zk)); CPPUNIT_ASSERT(zookeeper_get_connected_host(zk, &addr, &addr_len) != NULL); } void testCreate() { watchctx_t ctx; int rc = 0; zhandle_t *zk = createClient(&ctx); CPPUNIT_ASSERT(zk); char pathbuf[80]; struct Stat stat_a = {0}; struct Stat stat_b = {0}; rc = zoo_create2(zk, "/testcreateA", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, sizeof(pathbuf), &stat_a); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); CPPUNIT_ASSERT(strcmp(pathbuf, "/testcreateA") == 0); CPPUNIT_ASSERT(stat_a.czxid > 0); CPPUNIT_ASSERT(stat_a.mtime > 0); rc = zoo_create2(zk, "/testcreateB", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, sizeof(pathbuf), &stat_b); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); CPPUNIT_ASSERT(strcmp(pathbuf, "/testcreateB") == 0); CPPUNIT_ASSERT(stat_b.czxid > 0); CPPUNIT_ASSERT(stat_b.mtime > 0); // Should get different Stats back from different creates CPPUNIT_ASSERT(Stat_eq(&stat_a, &stat_b) != 1); } void testCreateContainer() { watchctx_t ctx; int rc = 0; zhandle_t *zk = createClient(&ctx); CPPUNIT_ASSERT(zk); char pathbuf[80]; struct Stat stat = {0}; rc = zoo_create2(zk, "/testContainer", "", 0, &ZOO_OPEN_ACL_UNSAFE, ZOO_CONTAINER, pathbuf, sizeof(pathbuf), &stat); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); } void testCreateTtl() { watchctx_t ctx; int rc = 0; zhandle_t *zk = createClient(&ctx); CPPUNIT_ASSERT(zk); char pathbuf[80]; struct Stat stat = {0}; rc = zoo_create2_ttl(zk, "/testTtl", "", 0, &ZOO_OPEN_ACL_UNSAFE, ZOO_PERSISTENT_WITH_TTL, 1, pathbuf, sizeof(pathbuf), &stat); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); sleep(1); rc = zoo_exists(zk, "/testTtl", 1, &stat); CPPUNIT_ASSERT_EQUAL((int) ZNONODE, 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); } #ifdef HAVE_OPENSSL_H void testSSL() { watchctx_t ctx; zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); zhandle_t *zk = createSSLClient("127.0.0.1:22281", "/tmp/certs/server.crt,/tmp/certs/client.crt,/tmp/certs/clientkey.pem,password", &ctx); CPPUNIT_ASSERT(zk); int rc = 0; rc = zoo_create(zk, "/ssl", NULL, -1, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); } #endif 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); // first test with a NULL zk handle, make sure client library does not // dereference a null pointer, but instead returns ZBADARGUMENTS rc = zoo_create(NULL, "/testch1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int) ZBADARGUMENTS, rc); rc = zoo_create(zk, "/testch1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); rc = zoo_create(zk, "/testch1/mahadev", data, 7, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); 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); //ZOOKEEPER-1027 correctly return path_buffer without prefixed chroot const char* path = "/zookeeper1027"; char path_buffer[1024]; int path_buffer_len=sizeof(path_buffer); rc = zoo_create(zk_ch, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, path_buffer, path_buffer_len); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); CPPUNIT_ASSERT_EQUAL(string(path), string(path_buffer)); const char* path2282 = "/zookeeper2282"; rc = zoo_acreate(zk_ch, path2282, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, create_completion_fn, path2282); waitForCreateCompletion(3); CPPUNIT_ASSERT(count == 0); } // Test creating normal handle via zookeeper_init then explicitly setting callback void testLogCallbackSet() { watchctx_t ctx; CPPUNIT_ASSERT(logMessages.empty()); zhandle_t *zk = createClient(&ctx); zoo_set_log_callback(zk, &logMessageHandler); CPPUNIT_ASSERT_EQUAL(zoo_get_log_callback(zk), &logMessageHandler); // Log 10 messages and ensure all go to callback const std::size_t expected = 10; for (std::size_t i = 0; i < expected; i++) { LOG_INFO(LOGCALLBACK(zk), "%s #%d", __FUNCTION__, i); } CPPUNIT_ASSERT(expected == logMessages.size()); } // Test creating handle via zookeeper_init2 to ensure all connection messages go to callback void testLogCallbackInit() { logMessages.clear(); watchctx_t ctx; zhandle_t *zk = createClient(&ctx, &logMessageHandler); CPPUNIT_ASSERT_EQUAL(zoo_get_log_callback(zk), &logMessageHandler); // All the connection messages should have gone to the callback -- don't // want this to be a maintenance issue so we're not asserting exact count std::size_t numBefore = logMessages.size(); CPPUNIT_ASSERT(numBefore != 0); // Log 10 messages and ensure all go to callback const std::size_t expected = 10; for (std::size_t i = 0; i < expected; i++) { LOG_INFO(LOGCALLBACK(zk), "%s #%d", __FUNCTION__, i); } CPPUNIT_ASSERT(logMessages.size() == numBefore + expected); } // Test clearing log callback -- logging should resume going to logstream void testLogCallbackClear() { logMessages.clear(); watchctx_t ctx; zhandle_t *zk = createClient(&ctx, &logMessageHandler); CPPUNIT_ASSERT_EQUAL(zoo_get_log_callback(zk), &logMessageHandler); // All the connection messages should have gone to the callback -- again, we don't // want this to be a maintenance issue so we're not asserting exact count int numBefore = logMessages.size(); CPPUNIT_ASSERT(numBefore > 0); // Clear log_callback zoo_set_log_callback(zk, NULL); // Future log messages should go to logstream not callback LOG_INFO(LOGCALLBACK(zk), __FUNCTION__); int numAfter = logMessages.size(); CPPUNIT_ASSERT_EQUAL(numBefore, numAfter); } 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/4; 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); } for(i = COUNT/4; i < COUNT/2; i++) { sprintf(path, "/awar%d", i); rc = zoo_acreate2(zk, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, stringStatCompletion, 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); } } void testLastZxid() { // ZOOKEEPER-1417: Test that c-client only update last zxid upon // receiving request response only. const int timeout = 5000; int rc; watchctx_t ctx1, ctx2; zhandle_t *zk1 = createClient(&ctx1); zhandle_t *zk2 = createClient(&ctx2); CPPUNIT_ASSERT(zk1); CPPUNIT_ASSERT(zk2); int64_t original = zk2->last_zxid; // Create txn to increase system zxid rc = zoo_create(zk1, "/lastzxid", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // This should be enough time for zk2 to receive ping request usleep(timeout/2 * 1000); // check that zk1's last zxid is updated struct Stat stat; rc = zoo_exists(zk1, "/lastzxid", 0, &stat); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); CPPUNIT_ASSERT_EQUAL((int64_t) zk1->last_zxid, stat.czxid); // zk2's last zxid should remain the same CPPUNIT_ASSERT_EQUAL(original, (int64_t) zk2->last_zxid); // Perform read and also register a watch rc = zoo_wexists(zk2, "/lastzxid", watcher, &ctx2, &stat); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); int64_t updated = zk2->last_zxid; // check that zk2's last zxid is updated CPPUNIT_ASSERT_EQUAL(updated, stat.czxid); // Increment system zxid again rc = zoo_set(zk1, "/lastzxid", NULL, -1, -1); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); // Wait for zk2 to get watch event CPPUNIT_ASSERT(waitForEvent(zk2, &ctx2, 5)); // zk2's last zxid should remain the same CPPUNIT_ASSERT_EQUAL(updated, (int64_t) zk2->last_zxid); } static void watcher_rw(zhandle_t *zh, int type, int state, const char *path, void *ctx) { count++; } static void watcher_rw2(zhandle_t *zh, int type, int state, const char *path, void *ctx) { count++; } void testRemoveWatchers() { const char *path = "/something"; char buf[1024]; int blen = sizeof(buf); int rc; watchctx_t ctx; zhandle_t *zk; /* setup path */ zk = createClient(&ctx); CPPUNIT_ASSERT(zk); rc = zoo_create(zk, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); rc = zoo_create(zk, "/something2", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); /* remove all watchers */ count = 0; rc = zoo_wget(zk, path, watcher_rw, NULL, buf, &blen, NULL); rc = zoo_wget(zk, path, watcher_rw2, NULL, buf, &blen, NULL); rc = zoo_remove_all_watches(zk, path, ZWATCHTYPE_ANY, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); rc = zoo_set(zk, path, "nowatch", 7, -1); CPPUNIT_ASSERT(count == 0); /* remove a specific watcher before it's added (should fail) */ rc = zoo_remove_watches(zk, path, ZWATCHTYPE_DATA, watcher_rw, NULL, 0); CPPUNIT_ASSERT_EQUAL((int)ZNOWATCHER, rc); /* now add a specific watcher and then remove it */ rc = zoo_wget(zk, path, watcher_rw, NULL, buf, &blen, NULL); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); rc = zoo_remove_watches(zk, path, ZWATCHTYPE_DATA, watcher_rw, NULL, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); /* ditto for children watcher */ rc = zoo_remove_watches(zk, path, ZWATCHTYPE_CHILD, watcher_rw, NULL, 0); CPPUNIT_ASSERT_EQUAL((int)ZNOWATCHER, rc); struct String_vector str_vec = {0, NULL}; rc = zoo_wget_children(zk, path, watcher_rw, NULL, &str_vec); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); rc = zoo_remove_watches(zk, path, ZWATCHTYPE_CHILD, watcher_rw, NULL, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); /* add a watch, stop the server, and have remove fail */ rc = zoo_wget(zk, path, watcher_rw, NULL, buf, &blen, NULL); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); stopServer(); ctx.waitForDisconnected(zk); rc = zoo_remove_watches(zk, path, ZWATCHTYPE_DATA, watcher_rw, NULL, 0); CPPUNIT_ASSERT_EQUAL((int)ZCONNECTIONLOSS, rc); zookeeper_close(zk); /* bring the server back */ startServer(); zk = createClient(&ctx); /* add a watch, stop the server, and remove it locally */ void* ctx1=(void*)0x1; void* ctx2=(void*)0x2; rc = zoo_wget(zk, path, watcher_rw, ctx1, buf, &blen, NULL); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); rc = zoo_wget(zk, "/something2", watcher_rw, ctx2, buf, &blen, NULL); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); stopServer(); rc = zoo_remove_watches(zk, path, ZWATCHTYPE_DATA, watcher_rw, ctx1, 1); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); rc = zoo_remove_watches(zk, path, ZWATCHTYPE_DATA, watcher_rw, ctx1, 1); CPPUNIT_ASSERT_EQUAL((int)ZNOWATCHER, rc); rc = zoo_remove_watches(zk, "/something2", ZWATCHTYPE_DATA, watcher_rw, ctx2, 1); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); } }; 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); #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestClientRetry.cc0100644 0000000 0000000 00000010135 15051152474 031047 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "Vector.h" using namespace std; #include #include "Util.h" #include "WatchUtil.h" 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, "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); apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestDriver.cc0100644 0000000 0000000 00000012011 15051152474 030031 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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() << endl << "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 #include class Zookeeper_logClientEnv : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_logClientEnv); CPPUNIT_TEST(testLogClientEnv); CPPUNIT_TEST_SUITE_END(); static void log_no_clientenv(const char *message) { CPPUNIT_ASSERT(::strstr(message, "Client environment") == NULL); } static void log_clientenv(const char *message) { static int first; if (!first) { CPPUNIT_ASSERT(::strstr(message, "Client environment") != NULL); first = 1; } } public: void testLogClientEnv() { zhandle_t* zh; zh = zookeeper_init2("localhost:22181", NULL, 0, NULL, NULL, 0, log_clientenv); CPPUNIT_ASSERT(zh != 0); zookeeper_close(zh); zh = zookeeper_init2("localhost:22181", NULL, 0, NULL, NULL, ZOO_NO_LOG_CLIENTENV, log_no_clientenv); CPPUNIT_ASSERT(zh != 0); zookeeper_close(zh); } }; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_logClientEnv); apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestMulti.cc0100644 0000000 0000000 00000055772 15051152474 027715 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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" #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; 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; } 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; #ifdef THREADED class Zookeeper_multi : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_multi); //FIXME: None of these tests pass in single-threaded mode. It seems to be a //flaw in the test suite setup. CPPUNIT_TEST(testCreate); CPPUNIT_TEST(testCreateDelete); CPPUNIT_TEST(testInvalidVersion); CPPUNIT_TEST(testNestedCreate); CPPUNIT_TEST(testSetData); CPPUNIT_TEST(testUpdateConflict); CPPUNIT_TEST(testDeleteUpdateConflict); CPPUNIT_TEST(testAsyncMulti); CPPUNIT_TEST(testMultiFail); CPPUNIT_TEST(testCheck); CPPUNIT_TEST(testWatch); CPPUNIT_TEST(testSequentialNodeCreateInAsyncMulti); CPPUNIT_TEST(testBigAsyncMulti); 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; CPPUNIT_ASSERT_EQUAL(true, ctx->waitForConnected(zk)); return zk; } FILE *logfile; public: Zookeeper_multi() { logfile = openlogfile("Zookeeper_multi"); } ~Zookeeper_multi() { if (logfile) { fflush(logfile); fclose(logfile); logfile = 0; } } void setUp() { zoo_set_log_stream(logfile); } void tearDown() { } static volatile int count; static void multi_completion_fn(int rc, const void *data) { CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); count++; } static void multi_completion_fn_no_assert(int rc, const void *data) { count++; } static void multi_completion_fn_rc(int rc, const void *data) { count++; *((int*) data) = rc; } static void create_completion_fn_rc(int rc, const char* value, const void *data) { count++; *((int*) data) = rc; } static void waitForMultiCompletion(int seconds) { time_t expires = time(0) + seconds; while(count == 0 && time(0) < expires) { sleep(1); } count--; } static void resetCounter() { count = 0; } /** * Test basic multi-op create functionality */ void testCreate() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz]; char p2[sz]; char p3[sz]; p1[0] = p2[0] = p3[0] = '\0'; int nops = 3 ; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_create_op_init(&ops[1], "/multi1/a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p2, sz); zoo_create_op_init(&ops[2], "/multi1/b", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p3, sz); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT(strcmp(p1, "/multi1") == 0); CPPUNIT_ASSERT(strcmp(p2, "/multi1/a") == 0); CPPUNIT_ASSERT(strcmp(p3, "/multi1/b") == 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[1].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[2].err); } /** * Test create followed by delete */ void testCreateDelete() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz]; p1[0] = '\0'; int nops = 2 ; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi2", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_delete_op_init(&ops[1], "/multi2", 0); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // '/multi2' should have been deleted rc = zoo_exists(zk, "/multi2", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); } /** * Test invalid versions */ void testInvalidVersion() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int nops = 4; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi3", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, NULL, 0); zoo_delete_op_init(&ops[1], "/multi3", 1); zoo_create_op_init(&ops[2], "/multi3", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, NULL, 0); zoo_create_op_init(&ops[3], "/multi3/a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, NULL, 0); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZBADVERSION, rc); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0].err); CPPUNIT_ASSERT_EQUAL((int)ZBADVERSION, results[1].err); CPPUNIT_ASSERT_EQUAL((int)ZRUNTIMEINCONSISTENCY, results[2].err); CPPUNIT_ASSERT_EQUAL((int)ZRUNTIMEINCONSISTENCY, results[3].err); } /** * Test nested creates that rely on state in earlier op in multi */ void testNestedCreate() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz]; p1[0] = '\0'; int nops = 6; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; /* Create */ zoo_create_op_init(&ops[0], "/multi4", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_create_op_init(&ops[1], "/multi4/a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_create_op_init(&ops[2], "/multi4/a/1", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); /* Delete */ zoo_delete_op_init(&ops[3], "/multi4/a/1", 0); zoo_delete_op_init(&ops[4], "/multi4/a", 0); zoo_delete_op_init(&ops[5], "/multi4", 0); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Verify tree deleted rc = zoo_exists(zk, "/multi4/a/1", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); rc = zoo_exists(zk, "/multi4/a", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); rc = zoo_exists(zk, "/multi4", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); } /** * Test setdata functionality */ void testSetData() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; struct Stat s1; char buf[sz]; int blen = sz ; char p1[sz], p2[sz]; int nops = 2; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi5", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_create_op_init(&ops[1], "/multi5/a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p2, sz); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); yield(zk, 5); zoo_op_t setdata_ops[nops]; zoo_op_result_t setdata_results[nops]; zoo_set_op_init(&setdata_ops[0], "/multi5", "1", 1, 0, &s1); zoo_set_op_init(&setdata_ops[1], "/multi5/a", "2", 1, 0, &s1); rc = zoo_multi(zk, nops, setdata_ops, setdata_results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[1].err); memset(buf, '\0', blen); rc = zoo_get(zk, "/multi5", 0, buf, &blen, &s1); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL(1, blen); CPPUNIT_ASSERT(strcmp("1", buf) == 0); memset(buf, '\0', blen); rc = zoo_get(zk, "/multi5/a", 0, buf, &blen, &s1); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL(1, blen); CPPUNIT_ASSERT(strcmp("2", buf) == 0); } /** * Test update conflicts */ void testUpdateConflict() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char buf[sz]; int blen = sz; char p1[sz]; p1[0] = '\0'; struct Stat s1; int nops = 3; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi6", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_set_op_init(&ops[1], "/multi6", "X", 1, 0, &s1); zoo_set_op_init(&ops[2], "/multi6", "Y", 1, 0, &s1); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZBADVERSION, rc); //Updating version solves conflict -- order matters ops[2].set_op.version = 1; rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); memset(buf, 0, sz); rc = zoo_get(zk, "/multi6", 0, buf, &blen, &s1); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL(blen, 1); CPPUNIT_ASSERT(strncmp(buf, "Y", 1) == 0); } /** * Test delete-update conflicts */ void testDeleteUpdateConflict() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz]; p1[0] = '\0'; struct Stat stat; int nops = 3; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi7", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_delete_op_init(&ops[1], "/multi7", 0); zoo_set_op_init(&ops[2], "/multi7", "Y", 1, 0, &stat); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); // '/multi' should never have been created as entire op should fail rc = zoo_exists(zk, "/multi7", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); } void testAsyncMulti() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz], p2[sz], p3[sz]; p1[0] = '\0'; p2[0] = '\0'; p3[0] = '\0'; int nops = 3; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi8", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_create_op_init(&ops[1], "/multi8/a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p2, sz); zoo_create_op_init(&ops[2], "/multi8/b", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p3, sz); rc = zoo_amulti(zk, nops, ops, results, multi_completion_fn, 0); waitForMultiCompletion(10); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT(strcmp(p1, "/multi8") == 0); CPPUNIT_ASSERT(strcmp(p2, "/multi8/a") == 0); CPPUNIT_ASSERT(strcmp(p3, "/multi8/b") == 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[1].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[2].err); } void testMultiFail() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz], p2[sz], p3[sz]; p1[0] = '\0'; p2[0] = '\0'; p3[0] = '\0'; int nops = 3; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0], "/multi9", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); zoo_create_op_init(&ops[1], "/multi9", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p2, sz); zoo_create_op_init(&ops[2], "/multi9/b", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p3, sz); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, rc); } /** * Test basic multi-op check functionality */ void testCheck() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz]; p1[0] = '\0'; rc = zoo_create(zk, "/multi0", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Conditionally create /multi0/a' only if '/multi0' at version 0 int nops = 2; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_check_op_init(&ops[0], "/multi0", 0); zoo_create_op_init(&ops[1], "/multi0/a", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[1].err); // '/multi0/a' should have been created as it passed version check rc = zoo_exists(zk, "/multi0/a", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Only create '/multi0/b' if '/multi0' at version 10 (which it's not) zoo_op_t ops2[nops]; zoo_check_op_init(&ops2[0], "/multi0", 10); zoo_create_op_init(&ops2[1], "/multi0/b", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, p1, sz); rc = zoo_multi(zk, nops, ops2, results); CPPUNIT_ASSERT_EQUAL((int)ZBADVERSION, rc); CPPUNIT_ASSERT_EQUAL((int)ZBADVERSION, results[0].err); CPPUNIT_ASSERT_EQUAL((int)ZRUNTIMEINCONSISTENCY, results[1].err); // '/multi0/b' should NOT have been created rc = zoo_exists(zk, "/multi0/b", 0, NULL); CPPUNIT_ASSERT_EQUAL((int)ZNONODE, rc); } /** * Do a multi op inside a watch callback context. */ static void doMultiInWatch(zhandle_t *zk, int type, int state, const char *path, void *ctx) { int rc; int sz = 512; char p1[sz]; p1[0] = '\0'; struct Stat s1; int nops = 1; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_set_op_init(&ops[0], "/multiwatch", "1", 1, -1, NULL); rc = zoo_multi(zk, nops, ops, results); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0].err); memset(p1, '\0', sz); rc = zoo_get(zk, "/multiwatch", 0, p1, &sz, &s1); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); CPPUNIT_ASSERT_EQUAL(1, sz); CPPUNIT_ASSERT(strcmp("1", p1) == 0); count++; } /** * Test multi-op called from a watch */ void testWatch() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int sz = 512; char p1[sz]; p1[0] = '\0'; rc = zoo_create(zk, "/multiwatch", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, NULL, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // create a watch on node '/multiwatch' rc = zoo_wget(zk, "/multiwatch", doMultiInWatch, &ctx, p1, &sz, NULL); // setdata on node '/multiwatch' this should trip the watch rc = zoo_set(zk, "/multiwatch", NULL, -1, -1); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); // wait for multi completion in doMultiInWatch waitForMultiCompletion(5); } /** * ZOOKEEPER-1636: If request is too large, the server will cut the * connection without sending response packet. The client will try to * process completion on multi request and eventually cause SIGSEGV */ void testBigAsyncMulti() { int rc; int callback_rc = (int) ZOK; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); // The request should be more than 1MB which exceeds the default // jute.maxbuffer and causes the server to drop client connection const int iteration = 500; const int type_count = 3; const int nops = iteration * type_count; char buff[1024]; zoo_op_result_t results[nops]; zoo_op_t ops[nops]; struct Stat* s[nops]; int index = 0; // Test that we deliver error to 3 types of sub-request for (int i = 0; i < iteration; ++i) { zoo_set_op_init(&ops[index++], "/x", buff, sizeof(buff), -1, s[i]); zoo_create_op_init(&ops[index++], "/x", buff, sizeof(buff), &ZOO_OPEN_ACL_UNSAFE, ZOO_SEQUENCE, NULL, 0); zoo_delete_op_init(&ops[index++], "/x", -1); } rc = zoo_amulti(zk, nops, ops, results, multi_completion_fn_rc, + &callback_rc); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); waitForMultiCompletion(10); // With the bug, we will get SIGSEGV before reaching this point CPPUNIT_ASSERT_EQUAL((int) ZCONNECTIONLOSS, callback_rc); // Make sure that all sub-request completions get processed for (int i = 0; i < nops; ++i) { CPPUNIT_ASSERT_EQUAL((int) ZCONNECTIONLOSS, results[i].err); } // The handle should be able to recover itself. ctx.waitForConnected(zk); // Try to submit another async request to see if it get processed // correctly rc = zoo_acreate(zk, "/target", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, create_completion_fn_rc, &callback_rc); CPPUNIT_ASSERT_EQUAL((int) ZOK, rc); waitForMultiCompletion(10); CPPUNIT_ASSERT_EQUAL((int) ZOK, callback_rc); } /** * ZOOKEEPER-1624: PendingChanges of create sequential node request didn't * get rollbacked correctly when multi-op failed. This caused * create sequential node request in subsequent multi-op to failed because * sequential node name generation is incorrect. * * The check is to make sure that each request in multi-op failed with * the correct reason. */ void testSequentialNodeCreateInAsyncMulti() { int rc; watchctx_t ctx; zhandle_t *zk = createClient(&ctx); int iteration = 4; int nops = 2; zoo_op_result_t results[iteration][nops]; zoo_op_t ops[nops]; zoo_create_op_init(&ops[0], "/node-", "", 0, &ZOO_OPEN_ACL_UNSAFE, ZOO_SEQUENCE, NULL, 0); zoo_create_op_init(&ops[1], "/dup", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, NULL, 0); for (int i = 0; i < iteration ; ++i) { rc = zoo_amulti(zk, nops, ops, results[i], multi_completion_fn_no_assert, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); } waitForMultiCompletion(10); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0][0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[1][0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[2][0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[3][0].err); CPPUNIT_ASSERT_EQUAL((int)ZOK, results[0][1].err); CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, results[1][1].err); CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, results[2][1].err); CPPUNIT_ASSERT_EQUAL((int)ZNODEEXISTS, results[3][1].err); resetCounter(); } }; volatile int Zookeeper_multi::count; const char Zookeeper_multi::hostPorts[] = "127.0.0.1:22181"; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_multi); #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestOperations.cc0100644 0000000 0000000 00000123256 15051152474 030737 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "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(testUnsolicitedPing); CPPUNIT_TEST(testTimeoutCausedByWatches1); CPPUNIT_TEST(testTimeoutCausedByWatches2); CPPUNIT_TEST(testCloseWhileInProgressFromMain); CPPUNIT_TEST(testCloseWhileInProgressFromCompletion); CPPUNIT_TEST(testCloseWhileMultiInProgressFromMain); CPPUNIT_TEST(testCloseWhileMultiInProgressFromCompletion); CPPUNIT_TEST(testConnectResponseFull); CPPUNIT_TEST(testConnectResponseNoReadOnlyFlag); CPPUNIT_TEST(testConnectResponseSplitAtReadOnlyFlag); CPPUNIT_TEST(testConnectResponseNoReadOnlyFlagSplit); #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_; }; class AsyncVoidOperationCompletion: public AsyncCompletion{ public: AsyncVoidOperationCompletion():called_(false),rc_(ZAPIERROR){} virtual void voidCompl(int rc){ synchronized(mx_); called_=true; rc_=rc; } bool operator()()const{ synchronized(mx_); return called_; } mutable Mutex mx_; bool called_; int rc_; }; #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, &timeMock.tv); 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, &timeMock.tv); 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, &timeMock.tv); 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==ZOO_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, &timeMock.tv); 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_); } // ZOOKEEPER-2253: Permit unsolicited pings void testUnsolicitedPing() { const int TIMEOUT=9; // timeout in secs Mock_gettimeofday timeMock; PingCountingServer zkServer; // must call zookeeper_close() while all the mocks are in scope CloseFinally guard(&zh); // receive timeout is in milliseconds zh=zookeeper_init("localhost:1234",watcher,TIMEOUT*1000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); // simulate connected state forceConnected(zh, &timeMock.tv); int fd=0; int interest=0; timeval tv; int rc=zookeeper_interest(zh,&fd,&interest,&tv); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // verify no ping sent CPPUNIT_ASSERT(zkServer.pingCount_==0); // we're going to receive a unsolicited PING response; ensure // that the client has updated its last_recv timestamp timeMock.tick(tv); zkServer.addRecvResponse(new PingResponse); rc=zookeeper_process(zh,interest); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); CPPUNIT_ASSERT(timeMock==zh->last_recv); } // simulate a watch arriving right before a ping is due // assert the ping is sent nevertheless void testTimeoutCausedByWatches1() { 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, &timeMock.tv); 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, &now.tv); // 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)); } // ZOOKEEPER-2894: Memory and completions leak on zookeeper_close // while there is a request waiting for being processed // call zookeeper_close() from the main event loop // assert the completion callback is called void testCloseWhileInProgressFromMain() { Mock_gettimeofday timeMock; ZookeeperServer zkServer; CloseFinally guard(&zh); zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // issue a request zkServer.addOperationResponse(new ZooGetResponse("1",1)); AsyncGetOperationCompletion res1; int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // but do not allow Zookeeper C Client to process the request // and call zookeeper_close() from the main event loop immediately Mock_free_noop freeMock; rc=zookeeper_close(zh); zh=0; freeMock.disable(); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // verify that memory for completions was freed (would be freed if no mock installed) CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); CPPUNIT_ASSERT(savezh->completions_to_process.head==0); CPPUNIT_ASSERT(savezh->completions_to_process.last==0); // verify that completion was called, and it was called with ZCLOSING status CPPUNIT_ASSERT(res1.called_); CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res1.rc_); } // ZOOKEEPER-2894: Memory and completions leak on zookeeper_close // send some request #1 // then, while there is a request #2 waiting for being processed // call zookeeper_close() from the completion callback of request #1 // assert the completion callback #2 is called void testCloseWhileInProgressFromCompletion() { Mock_gettimeofday timeMock; ZookeeperServer zkServer; CloseFinally guard(&zh); zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // will handle completion on request #1 and issue request #2 from it class AsyncGetOperationCompletion1: public AsyncCompletion{ public: AsyncGetOperationCompletion1(zhandle_t **zh, ZookeeperServer *zkServer, AsyncGetOperationCompletion *res2) :zh_(zh),zkServer_(zkServer),res2_(res2){} virtual void dataCompl(int rc1, const char *value, int len, const Stat *stat){ CPPUNIT_ASSERT_EQUAL((int)ZOK,rc1); // from the completion #1 handler, issue request #2 zkServer_->addOperationResponse(new ZooGetResponse("2",1)); int rc2=zoo_aget(*zh_,"/x/y/2",0,asyncCompletion,res2_); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc2); // but do not allow Zookeeper C Client to process the request #2 // and call zookeeper_close() from the completion callback of request #1 rc2=zookeeper_close(*zh_); *zh_=0; CPPUNIT_ASSERT_EQUAL((int)ZOK,rc2); // do not disable freeMock here, let completion #2 handler // return through ZooKeeper C Client internals to the main loop // and fulfill the work } zhandle_t **zh_; ZookeeperServer *zkServer_; AsyncGetOperationCompletion *res2_; }; // issue request #1 AsyncGetOperationCompletion res2; AsyncGetOperationCompletion1 res1(&zh,&zkServer,&res2); zkServer.addOperationResponse(new ZooGetResponse("1",1)); int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // process the send queue int fd; int interest; timeval tv; rc=zookeeper_interest(zh,&fd,&interest,&tv); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); CPPUNIT_ASSERT(zh!=0); Mock_free_noop freeMock; while(zh!=0 && (rc=zookeeper_process(zh,interest))==ZOK) { millisleep(100); } freeMock.disable(); CPPUNIT_ASSERT(zh==0); // verify that memory for completions was freed (would be freed if no mock installed) CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); CPPUNIT_ASSERT(savezh->completions_to_process.head==0); CPPUNIT_ASSERT(savezh->completions_to_process.last==0); // verify that completion #2 was called, and it was called with ZCLOSING status CPPUNIT_ASSERT(res2.called_); CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res2.rc_); } // ZOOKEEPER-2891: Invalid processing of zookeeper_close for mutli-request // while there is a multi request waiting for being processed // call zookeeper_close() from the main event loop // assert the completion callback is called with status ZCLOSING void testCloseWhileMultiInProgressFromMain() { Mock_gettimeofday timeMock; ZookeeperServer zkServer; CloseFinally guard(&zh); zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // issue a multi request int nops=2; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; zoo_create_op_init(&ops[0],"/a",0,-1,&ZOO_OPEN_ACL_UNSAFE,0,0,0); zoo_create_op_init(&ops[1],"/a/b",0,-1,&ZOO_OPEN_ACL_UNSAFE,0,0,0); // TODO: Provide ZooMultiResponse. However, it's not required in this test. // zkServer.addOperationResponse(new ZooMultiResponse(...)); AsyncVoidOperationCompletion res1; int rc=zoo_amulti(zh,nops,ops,results,asyncCompletion,&res1); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // but do not allow Zookeeper C Client to process the request // and call zookeeper_close() from the main event loop immediately Mock_free_noop freeMock; rc=zookeeper_close(zh); zh=0; freeMock.disable(); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // verify that memory for completions was freed (would be freed if no mock installed) CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); CPPUNIT_ASSERT(savezh->completions_to_process.head==0); CPPUNIT_ASSERT(savezh->completions_to_process.last==0); // verify that completion was called, and it was called with ZCLOSING status CPPUNIT_ASSERT(res1.called_); CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res1.rc_); } // ZOOKEEPER-2891: Invalid processing of zookeeper_close for mutli-request // send some request #1 (not a multi request) // then, while there is a multi request #2 waiting for being processed // call zookeeper_close() from the completion callback of request #1 // assert the completion callback #2 is called with status ZCLOSING void testCloseWhileMultiInProgressFromCompletion() { Mock_gettimeofday timeMock; ZookeeperServer zkServer; CloseFinally guard(&zh); zh=zookeeper_init("localhost:2121",watcher,10000,TEST_CLIENT_ID,0,0); CPPUNIT_ASSERT(zh!=0); forceConnected(zh, &timeMock.tv); zhandle_t* savezh=zh; // these shall persist during the test int nops=2; zoo_op_t ops[nops]; zoo_op_result_t results[nops]; // will handle completion on request #1 and issue request #2 from it class AsyncGetOperationCompletion1: public AsyncCompletion{ public: AsyncGetOperationCompletion1(zhandle_t **zh, ZookeeperServer *zkServer, AsyncVoidOperationCompletion *res2, int nops, zoo_op_t* ops, zoo_op_result_t* results) :zh_(zh),zkServer_(zkServer),res2_(res2),nops_(nops),ops_(ops),results_(results){} virtual void dataCompl(int rc1, const char *value, int len, const Stat *stat){ CPPUNIT_ASSERT_EQUAL((int)ZOK,rc1); // from the completion #1 handler, issue multi request #2 assert(nops_>=2); zoo_create_op_init(&ops_[0],"/a",0,-1,&ZOO_OPEN_ACL_UNSAFE,0,0,0); zoo_create_op_init(&ops_[1],"/a/b",0,-1,&ZOO_OPEN_ACL_UNSAFE,0,0,0); // TODO: Provide ZooMultiResponse. However, it's not required in this test. // zkServer_->addOperationResponse(new ZooMultiResponse(...)); int rc2=zoo_amulti(*zh_,nops_,ops_,results_,asyncCompletion,res2_); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc2); // but do not allow Zookeeper C Client to process the request #2 // and call zookeeper_close() from the completion callback of request #1 rc2=zookeeper_close(*zh_); *zh_=0; CPPUNIT_ASSERT_EQUAL((int)ZOK,rc2); // do not disable freeMock here, let completion #2 handler // return through ZooKeeper C Client internals to the main loop // and fulfill the work } zhandle_t **zh_; ZookeeperServer *zkServer_; AsyncVoidOperationCompletion *res2_; int nops_; zoo_op_t* ops_; zoo_op_result_t* results_; }; // issue some request #1 (not a multi request) AsyncVoidOperationCompletion res2; AsyncGetOperationCompletion1 res1(&zh,&zkServer,&res2,nops,ops,results); zkServer.addOperationResponse(new ZooGetResponse("1",1)); int rc=zoo_aget(zh,"/x/y/1",0,asyncCompletion,&res1); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); // process the send queue int fd; int interest; timeval tv; rc=zookeeper_interest(zh,&fd,&interest,&tv); CPPUNIT_ASSERT_EQUAL((int)ZOK,rc); CPPUNIT_ASSERT(zh!=0); Mock_free_noop freeMock; while(zh!=0 && (rc=zookeeper_process(zh,interest))==ZOK) { millisleep(100); } freeMock.disable(); CPPUNIT_ASSERT(zh==0); // verify that memory for completions was freed (would be freed if no mock installed) CPPUNIT_ASSERT_EQUAL(1,freeMock.getFreeCount(savezh)); CPPUNIT_ASSERT(savezh->completions_to_process.head==0); CPPUNIT_ASSERT(savezh->completions_to_process.last==0); // verify that completion #2 was called, and it was called with ZCLOSING status CPPUNIT_ASSERT(res2.called_); CPPUNIT_ASSERT_EQUAL((int)ZCLOSING,res2.rc_); } void testConnectResponseFull() { CloseFinally guard(&zh); Mock_socket socketMock; HandshakeResponse hsResponse; std::string hsResponseData = hsResponse.toString(); CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(41)); zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); CPPUNIT_ASSERT(zh!=0); int rc, fd, interest; timeval tv; rc = zookeeper_interest(zh, &fd, &interest, &tv); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); socketMock.recvReturnBuffer = hsResponseData; rc = zookeeper_process(zh, interest); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); } void testConnectResponseNoReadOnlyFlag() { CloseFinally guard(&zh); Mock_socket socketMock; HandshakeResponse hsResponse; hsResponse.omitReadOnly = true; std::string hsResponseData = hsResponse.toString(); CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(40)); zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); CPPUNIT_ASSERT(zh!=0); int rc, fd, interest; timeval tv; rc = zookeeper_interest(zh, &fd, &interest, &tv); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); socketMock.recvReturnBuffer = hsResponseData; rc = zookeeper_process(zh, interest); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); } void testConnectResponseSplitAtReadOnlyFlag() { CloseFinally guard(&zh); Mock_socket socketMock; HandshakeResponse hsResponse; std::string hsResponseData = hsResponse.toString(); CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(41)); zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); CPPUNIT_ASSERT(zh!=0); int rc, fd, interest; timeval tv; rc = zookeeper_interest(zh, &fd, &interest, &tv); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); socketMock.recvReturnBuffer = hsResponseData.substr(0, 40); rc = zookeeper_process(zh, interest); // Response not complete. CPPUNIT_ASSERT_EQUAL(static_cast(ZNOTHING), rc); CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE, static_cast(zh->state)); socketMock.recvReturnBuffer = hsResponseData.substr(40); rc = zookeeper_process(zh, interest); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); } void testConnectResponseNoReadOnlyFlagSplit() { CloseFinally guard(&zh); Mock_socket socketMock; HandshakeResponse hsResponse; hsResponse.omitReadOnly = true; std::string hsResponseData = hsResponse.toString(); CPPUNIT_ASSERT_EQUAL(hsResponseData.length(), static_cast(40)); zh = zookeeper_init("localhost:2121", watcher, 10000, NULL, NULL, 0); CPPUNIT_ASSERT(zh!=0); int rc, fd, interest; timeval tv; rc = zookeeper_interest(zh, &fd, &interest, &tv); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); socketMock.recvReturnBuffer = hsResponseData.substr(0, 20); rc = zookeeper_process(zh, interest); // Response not complete. CPPUNIT_ASSERT_EQUAL(static_cast(ZNOTHING), rc); CPPUNIT_ASSERT_EQUAL(ZOO_ASSOCIATING_STATE, static_cast(zh->state)); socketMock.recvReturnBuffer = hsResponseData.substr(20); rc = zookeeper_process(zh, interest); CPPUNIT_ASSERT_EQUAL(static_cast(ZOK), rc); CPPUNIT_ASSERT_EQUAL(ZOO_CONNECTED_STATE, static_cast(zh->state)); } #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 #include #include #include "Util.h" #include "WatchUtil.h" #ifdef THREADED class Zookeeper_readOnly : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_readOnly); CPPUNIT_TEST(testReadOnly); #ifdef HAVE_OPENSSL_H CPPUNIT_TEST(testReadOnlyWithSSL); #endif CPPUNIT_TEST_SUITE_END(); static void watcher(zhandle_t* zh, int type, int state, const char* path, void* v) { watchctx_t *ctx = (watchctx_t*)v; if (state==ZOO_CONNECTED_STATE || state==ZOO_READONLY_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); } } FILE *logfile; public: Zookeeper_readOnly() { logfile = openlogfile("Zookeeper_readOnly"); } ~Zookeeper_readOnly() { if (logfile) { fflush(logfile); fclose(logfile); logfile = 0; } } void setUp() { zoo_set_log_stream(logfile); zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); stopServer(); } void tearDown() { startServer(); } void startServer() { char cmd[1024]; sprintf(cmd, "%s start %s", ZKSERVER_CMD, "127.0.0.1:22181"); CPPUNIT_ASSERT(system(cmd) == 0); } void stopServer() { char cmd[1024]; sprintf(cmd, "%s stop %s", ZKSERVER_CMD, "127.0.0.1:22181"); CPPUNIT_ASSERT(system(cmd) == 0); } void startReadOnly() { char cmd[1024]; sprintf(cmd, "%s startCleanReadOnly", ZKSERVER_CMD); CPPUNIT_ASSERT(system(cmd) == 0); } void stopPeer() { char cmd[1024]; sprintf(cmd, "%s stop", ZKSERVER_CMD); CPPUNIT_ASSERT(system(cmd) == 0); } zhandle_t* connectReadOnly(const char *address, watchctx_t *watch) { zhandle_t* zh = zookeeper_init(address, watcher, 10000, NULL, watch, ZOO_READONLY); watch->zh = zh; CPPUNIT_ASSERT(zh != 0); sleep(1); return zh; } void assertCanRead(zhandle_t* zh, const char *znode_path) { int len = 1024; char buf[len]; int res = zoo_get(zh, znode_path, 0, buf, &len, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, res); } void assertCanNotWrite(zhandle_t* zh, const char *znode_path) { char path[1024]; char buf[1024]; int res = zoo_create(zh, znode_path, buf, 10, &ZOO_OPEN_ACL_UNSAFE, 0, path, 512); CPPUNIT_ASSERT_EQUAL((int)ZNOTREADONLY, res); } void testReadOnly() { startReadOnly(); watchctx_t watch; zhandle_t* zh = connectReadOnly("localhost:22181", &watch); assertCanRead(zh, "/"); assertCanNotWrite(zh, "/test"); stopPeer(); } #ifdef HAVE_OPENSSL_H zhandle_t* connectReadOnlySSL(const char *address, const char *certs, watchctx_t *watch) { zhandle_t* zh = zookeeper_init_ssl(address, certs, watcher, 10000, NULL, watch, ZOO_READONLY); watch->zh = zh; CPPUNIT_ASSERT(zh != 0); sleep(1); return zh; } void testReadOnlyWithSSL() { startReadOnly(); watchctx_t watch; zhandle_t* zh = connectReadOnlySSL("localhost:22281", "/tmp/certs/server.crt,/tmp/certs/client.crt,/tmp/certs/clientkey.pem,password", &watch); assertCanRead(zh, "/"); assertCanNotWrite(zh, "/testSSL"); stopPeer(); } #endif }; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_readOnly); #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestReconfig.cc0100644 0000000 0000000 00000053772 15051152474 030355 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 extern "C" { #include } #include "Util.h" #include "LibCMocks.h" #include "ZKMocks.h" using namespace std; static const int portOffset = 2000; class Client { private: // Member variables zhandle_t *zh; unsigned int seed; public: /** * Create a client with given connection host string and add to our internal * vector of clients. These are disconnected and cleaned up in tearDown(). */ Client(const string hosts, unsigned int seed) : seed((seed * seed) + 0xAFAFAFAF) { reSeed(); zh = zookeeper_init(hosts.c_str(),0,1000,0,0,0); CPPUNIT_ASSERT(zh); // Set the flag to disable ZK from reconnecting to a different server. // Our reconfig test case will do explicit server shuffling through // zoo_cycle_next_server, and the reconnection attempts would interfere // with the server states the tests cases assume. zh->disable_reconnection_attempt = 1; reSeed(); cycleNextServer(); } void close() { zookeeper_close(zh); zh = NULL; } bool isReconfig() { return zh->reconfig != 0; } /** * re-seed this client with it's own previously generated seed so its * random choices are unique and separate from the other clients */ void reSeed() { srandom(seed); srand48(seed); } /** * Get the server that this client is currently connected to. */ string getServer() { const char* addrstring = zoo_get_current_server(zh); return string(addrstring); } /** * Get the server this client is currently connected to with no port * specification. */ string getServerNoPort() { string addrstring = getServer(); size_t found = addrstring.find_last_of(":"); CPPUNIT_ASSERT(found != string::npos); // ipv6 address case (to remove leading and trailing bracket) if (addrstring.find("[") != string::npos) { return addrstring.substr(1, found-2); } else { return addrstring.substr(0, found); } } /** * Get the port of the server this client is currently connected to. */ uint32_t getServerPort() { string addrstring = getServer(); size_t found = addrstring.find_last_of(":"); CPPUNIT_ASSERT(found != string::npos); string portStr = addrstring.substr(found+1); stringstream ss(portStr); uint32_t port; ss >> port; CPPUNIT_ASSERT(port >= portOffset); return port; } /** * Cycle to the next available server on the next connect attempt. It also * calls into getServer (above) to return the server connected to. */ string cycleNextServer() { zoo_cycle_next_server(zh); return getServer(); } void cycleUntilServer(const string requested) { // Call cycleNextServer until the one it's connected to is the one // specified (disregarding port). string first; while(true) { string next = cycleNextServer(); if (first.empty()) { first = next; } // Else we've looped around! else if (first == next) { CPPUNIT_ASSERT(false); } // Strip port off string server = getServerNoPort(); // If it matches the requested host we're now 'connected' to the right host if (server == requested) { break; } } } /** * Set servers for this client. */ void setServers(const string new_hosts) { int rc = zoo_set_servers(zh, new_hosts.c_str()); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); } /** * Set servers for this client and validate reconfig value matches expected. */ void setServersAndVerifyReconfig(const string new_hosts, bool is_reconfig) { setServers(new_hosts); CPPUNIT_ASSERT_EQUAL(is_reconfig, isReconfig()); } /** * Sets the server list this client is connecting to AND if this requires * the client to be reconfigured (as dictated by internal client policy) * then it will trigger a call to cycleNextServer. */ void setServersAndCycleIfNeeded(const string new_hosts) { setServers(new_hosts); if (isReconfig()) { cycleNextServer(); } } }; class Zookeeper_reconfig : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_reconfig); // Test cases CPPUNIT_TEST(testcycleNextServer); CPPUNIT_TEST(testMigrateOrNot); CPPUNIT_TEST(testMigrationCycle); CPPUNIT_TEST(testAddrVecContainsIPv4); #ifdef AF_INET6 CPPUNIT_TEST(testAddrVecContainsIPv6); #endif // In threaded mode each 'create' is a thread -- it's not practical to create // 10,000 threads to test load balancing. The load balancing code can easily // be tested in single threaded mode as concurrency doesn't affect the algorithm. #ifndef THREADED CPPUNIT_TEST(testMigrateProbability); CPPUNIT_TEST(testLoadBalancing); #endif CPPUNIT_TEST_SUITE_END(); FILE *logfile; double slackPercent; static const int numClients = 10000; static const int portOffset = 2000; vector clients; vector numClientsPerHost; public: Zookeeper_reconfig() : slackPercent(10.0) { logfile = openlogfile("Zookeeper_reconfig"); } ~Zookeeper_reconfig() { if (logfile) { fflush(logfile); fclose(logfile); logfile = 0; } } void setUp() { zoo_set_log_stream(logfile); zoo_deterministic_conn_order(1); numClientsPerHost.resize(numClients); } void tearDown() { for (unsigned int i = 0; i < clients.size(); i++) { clients.at(i).close(); } } /** * Create a client with given connection host string and add to our internal * vector of clients. These are disconnected and cleaned up in tearDown(). */ Client& createClient(const string hosts) { Client client(hosts, clients.size()); clients.push_back(client); return clients.back(); } /** * Same as createClient(hosts) only it takes a specific host that this client * should simulate being connected to. */ Client& createClient(const string hosts, const string host) { // Ensure requested host is in the list size_t found = hosts.find(host); CPPUNIT_ASSERT(found != hosts.npos); Client client(hosts, clients.size()); client.cycleUntilServer(host); clients.push_back(client); return clients.back(); } /** * Create a connection host list starting at 'start' and stopping at 'stop' * where start >= stop. This creates a connection string with host:port pairs * separated by commas. The given 'octet' is the starting octet that is used * as the last octet in the host's IP. This is decremented on each iteration. * Each port will be portOffset + octet. */ string createHostList(uint32_t start, uint32_t stop = 1, uint32_t octet = 0) { if (octet == 0) { octet = start; } stringstream ss; for (uint32_t i = start; i >= stop; i--, octet--) { ss << "10.10.10." << octet << ":" << portOffset + octet; if (i > stop) { ss << ", "; } } return ss.str(); } /** * Gets the lower bound of the number of clients per server that we expect * based on the probabilistic load balancing algorithm implemented by the * client code. */ double lowerboundClientsPerServer(int numClients, int numServers) { return (1 - slackPercent/100.0) * numClients / numServers; } /** * Gets the upper bound of the number of clients per server that we expect * based on the probabilistic load balancing algorithm implemented by the * client code. */ double upperboundClientsPerServer(int numClients, int numServers) { return (1 + slackPercent/100.0) * numClients / numServers; } /** * Update all the clients to use a new list of servers. This will also cause * the client to cycle to the next server as needed (e.g. due to a reconfig). * It then updates the number of clients connected to the server based on * this change. * * Afterwards it validates that all of the servers have the correct amount of * clients based on the probabilistic load balancing algorithm. */ void updateAllClientsAndServers(int start, int stop = 1) { string newServers = createHostList(start, stop); int numServers = start - stop + 1; for (int i = 0; i < numClients; i++) { Client &client = clients.at(i); client.reSeed(); client.setServersAndCycleIfNeeded(newServers); numClientsPerHost.at(client.getServerPort() - portOffset - 1)++; } int offset = stop - 1; for (int index = offset; index < numServers; index++) { if (numClientsPerHost.at(index) > upperboundClientsPerServer(numClients, numServers)) { cout << "INDEX=" << index << " too many -- actual=" << numClientsPerHost.at(index) << " expected=" << upperboundClientsPerServer(numClients, numServers) << endl; } CPPUNIT_ASSERT(numClientsPerHost.at(index) <= upperboundClientsPerServer(numClients, numServers)); if (numClientsPerHost.at(index) < lowerboundClientsPerServer(numClients, numServers)) { cout << "INDEX=" << index << " too few -- actual=" << numClientsPerHost.at(index) << " expected=" << lowerboundClientsPerServer(numClients, numServers) << endl; } CPPUNIT_ASSERT(numClientsPerHost.at(index) >= lowerboundClientsPerServer(numClients, numServers)); numClientsPerHost.at(index) = 0; // prepare for next test } } /*-------------------------------------------------------------------------* * TESTCASES *------------------------------------------------------------------------*/ /** * Very basic sunny day test to ensure basic functionality of zoo_set_servers * and zoo_cycle_next_server. */ void testcycleNextServer() { const string initial_hosts = createHostList(10); // 2010..2001 const string new_hosts = createHostList(4); // 2004..2001 Client &client = createClient(initial_hosts); client.setServersAndVerifyReconfig(new_hosts, true); for (int i = 0; i < 10; i++) { string next = client.cycleNextServer(); } } /** * Test the migration policy implicit within the probabilistic load balancing * algorithm the Client implements. Tests all the corner cases whereby the * list of servers is decreased, increased, and stays the same. Also combines * various combinations of the currently connected server being in the new * configuration and not. */ void testMigrateOrNot() { const string initial_hosts = createHostList(4); // 2004..2001 Client &client = createClient(initial_hosts, "10.10.10.3"); // Ensemble size decreasing, my server is in the new list client.setServersAndVerifyReconfig(createHostList(3), false); // Ensemble size decreasing, my server is NOT in the new list client.setServersAndVerifyReconfig(createHostList(2), true); // Ensemble size stayed the same, my server is NOT in the new list client.setServersAndVerifyReconfig(createHostList(2), true); // Ensemble size increased, my server is not in the new ensemble client.setServers(createHostList(4)); client.cycleUntilServer("10.10.10.1"); client.setServersAndVerifyReconfig(createHostList(7,2), true); } /** * This tests that as a client is in reconfig mode it will properly try to * connect to all the new servers first. Then it will try to connect to all * the 'old' servers that are staying in the new configuration. Finally it * will fallback to the normal behavior of trying servers in round-robin. */ void testMigrationCycle() { int num_initial = 4; const string initial_hosts = createHostList(num_initial); // {2004..2001} int num_new = 10; string new_hosts = createHostList(12, 3); // {2012..2003} // servers from the old list that appear in the new list {2004..2003} int num_staying = 2; string oldStaying = createHostList(4, 3); // servers in the new list that are not in the old list {2012..2005} int num_coming = 8; string newComing = createHostList(12, 5); // Ensemble in increasing in size, my server is not in the new ensemble // load on the old servers must be decreased, so must connect to one of // new servers (pNew = 1) Client &client = createClient(initial_hosts, "10.10.10.1"); client.setServersAndVerifyReconfig(new_hosts, true); // Since we're in reconfig mode, next connect should be from new list // We should try all the new servers *BEFORE* trying any old servers string seen; for (int i = 0; i < num_coming; i++) { client.cycleNextServer(); // Assert next server is in the 'new' list stringstream next; next << client.getServerNoPort() << ":" << client.getServerPort(); size_t found = newComing.find(next.str()); CPPUNIT_ASSERT_MESSAGE(next.str() + " not in newComing list", found != string::npos); // Assert not in seen list then append found = seen.find(next.str()); CPPUNIT_ASSERT_MESSAGE(next.str() + " in seen list", found == string::npos); seen += next.str() + ", "; } // Now it should start connecting to the old servers seen.clear(); for (int i = 0; i < num_staying; i++) { client.cycleNextServer(); // Assert it's in the old list stringstream next; next << client.getServerNoPort() << ":" << client.getServerPort(); size_t found = oldStaying.find(next.str()); CPPUNIT_ASSERT(found != string::npos); // Assert not in seen list then append found = seen.find(next.str()); CPPUNIT_ASSERT(found == string::npos); seen += next.str() + ", "; } // NOW it goes back to normal as we've tried all the new and old string first = client.cycleNextServer(); for (int i = 0; i < num_new - 1; i++) { client.cycleNextServer(); } CPPUNIT_ASSERT_EQUAL(first, client.cycleNextServer()); } /** * Test the migration probability to ensure that it conforms to our expected * lower and upper bounds of the number of clients per server as we are * reconfigured. * * In this case, the list of servers is increased and the client's server is * in the new list. Whether to move or not depends on the difference of * server sizes with probability 1 - |old|/|new| the client disconnects. * * In the test below 1-9/10 = 1/10 chance of disconnecting */ void testMigrateProbability() { const string initial_hosts = createHostList(9); // 10.10.10.9:2009...10.10.10.1:2001 string new_hosts = createHostList(10); // 10.10.10.10:2010...10.10.10.1:2001 uint32_t numDisconnects = 0; for (int i = 0; i < numClients; i++) { Client &client = createClient(initial_hosts, "10.10.10.3"); client.setServers(new_hosts); if (client.isReconfig()) { numDisconnects++; } } // should be numClients/10 in expectation, we test that it's numClients/10 +- slackPercent CPPUNIT_ASSERT(numDisconnects < upperboundClientsPerServer(numClients, 10)); } /** * Tests the probabilistic load balancing algorithm implemented by the Client * code. * * Test strategy: * * (1) Start with 9 servers and 10,000 clients. Remove a server, update * everything, and ensure that the clients are redistributed properly. * * (2) Remove two more nodes and repeat the same validations of proper client * redistribution. Ensure no clients are connected to the two removed * nodes. * * (3) Remove the first server in the list and simultaneously add the three * previously removed servers. Ensure everything is redistributed and * no clients are connected to the one missing node. * * (4) Add the one missing server back into the mix and validate. */ void testLoadBalancing() { zoo_deterministic_conn_order(0); uint32_t numServers = 9; const string initial_hosts = createHostList(numServers); // 10.10.10.9:2009...10.10.10.1:2001 // Create connections to servers for (int i = 0; i < numClients; i++) { Client &client = createClient(initial_hosts); numClientsPerHost.at(client.getServerPort() - portOffset - 1)++; } for (uint32_t i = 0; i < numServers; i++) { CPPUNIT_ASSERT(numClientsPerHost.at(i) <= upperboundClientsPerServer(numClients, numServers)); CPPUNIT_ASSERT(numClientsPerHost.at(i) >= lowerboundClientsPerServer(numClients, numServers)); numClientsPerHost.at(i) = 0; // prepare for next test } // remove last server numServers = 8; updateAllClientsAndServers(numServers); CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers)); // Remove two more nodes numServers = 6; updateAllClientsAndServers(numServers); CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers)); CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers+1)); CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(numServers+2)); // remove host 0 (first one in list) and add back 6, 7, and 8 numServers = 8; updateAllClientsAndServers(numServers, 1); CPPUNIT_ASSERT_EQUAL((uint32_t)0, numClientsPerHost.at(0)); // add back host number 0 numServers = 9; updateAllClientsAndServers(numServers); } /** * This tests that client can detect server's ipv4 address change. * * (1) We generate some address and put in addr, which saddr point to * (2) Add all addresses that differ by one bit from the source * (3) Add same address, but set ipv6 protocol * (4) Ensure, that our address is not equal to any of generated, * and that it equals to itself */ void testAddrVecContainsIPv4() { addrvec_t vec; addrvec_init(&vec); sockaddr_storage addr; sockaddr_in* saddr = (sockaddr_in*)&addr; saddr->sin_family = AF_INET; saddr->sin_port = htons((u_short)1234); saddr->sin_addr.s_addr = INADDR_ANY; CPPUNIT_ASSERT(sizeof(saddr->sin_addr.s_addr) == 4); for (int i = 0; i < 32; i++) { saddr->sin_addr.s_addr ^= (1 << i); addrvec_append(&vec, &addr); saddr->sin_addr.s_addr ^= (1 << i); } saddr->sin_family = AF_INET6; addrvec_append(&vec, &addr); saddr->sin_family = AF_INET; CPPUNIT_ASSERT(!addrvec_contains(&vec, &addr)); addrvec_append(&vec, &addr); CPPUNIT_ASSERT(addrvec_contains(&vec, &addr)); addrvec_free(&vec); } /** * This tests that client can detect server's ipv6 address change. * * Same logic as in previous testAddrVecContainsIPv4 method, * but we keep in mind, that ipv6 is 128-bit long. */ #ifdef AF_INET6 void testAddrVecContainsIPv6() { addrvec_t vec; addrvec_init(&vec); sockaddr_storage addr; sockaddr_in6* saddr = (sockaddr_in6*)&addr; saddr->sin6_family = AF_INET6; saddr->sin6_port = htons((u_short)1234); saddr->sin6_addr = in6addr_any; CPPUNIT_ASSERT(sizeof(saddr->sin6_addr.s6_addr) == 16); for (int i = 0; i < 16; i++) { for (int j = 0; j < 8; j++) { saddr->sin6_addr.s6_addr[i] ^= (1 << j); addrvec_append(&vec, &addr); saddr->sin6_addr.s6_addr[i] ^= (1 << j); } } saddr->sin6_family = AF_INET; addrvec_append(&vec, &addr); saddr->sin6_family = AF_INET6; CPPUNIT_ASSERT(!addrvec_contains(&vec, &addr)); addrvec_append(&vec, &addr); CPPUNIT_ASSERT(addrvec_contains(&vec, &addr)); addrvec_free(&vec); } #endif }; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_reconfig); apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestReconfigServer.cc0100644 0000000 0000000 00000036153 15051152474 031536 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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 "zookeeper.h" #include "Util.h" #include "ZooKeeperQuorumServer.h" #ifdef THREADED class TestReconfigServer : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(TestReconfigServer); CPPUNIT_TEST(testNonIncremental); CPPUNIT_TEST(testRemoveConnectedFollower); CPPUNIT_TEST(testRemoveFollower); CPPUNIT_TEST(testReconfigFailureWithoutAuth); CPPUNIT_TEST(testReconfigFailureWithoutServerSuperuserPasswordConfigured); CPPUNIT_TEST_SUITE_END(); public: TestReconfigServer(); virtual ~TestReconfigServer(); void setUp(); void tearDown(); void testNonIncremental(); void testRemoveConnectedFollower(); void testRemoveFollower(); void testReconfigFailureWithoutAuth(); void testReconfigFailureWithoutServerSuperuserPasswordConfigured(); private: static const uint32_t NUM_SERVERS; FILE* logfile_; std::vector cluster_; std::size_t getLeader(); std::vector getFollowers(); void parseConfig(char* buf, int len, std::vector& servers, std::string& version); bool waitForConnected(zhandle_t* zh, uint32_t timeout_sec); zhandle_t* connectFollowers(std::vector &followers); }; const uint32_t TestReconfigServer::NUM_SERVERS = 3; TestReconfigServer:: TestReconfigServer() : logfile_(openlogfile("TestReconfigServer")) { zoo_set_log_stream(logfile_); } TestReconfigServer:: ~TestReconfigServer() { if (logfile_) { fflush(logfile_); fclose(logfile_); logfile_ = NULL; } } void TestReconfigServer:: setUp() { ZooKeeperQuorumServer::tConfigPairs configs; configs.push_back(std::make_pair("reconfigEnabled", "true")); cluster_ = ZooKeeperQuorumServer::getCluster(NUM_SERVERS, configs, "SERVER_JVMFLAGS=-Dzookeeper.DigestAuthenticationProvider.superDigest=super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is test */); } void TestReconfigServer:: tearDown() { for (std::size_t i = 0; i < cluster_.size(); i++) { delete cluster_[i]; } cluster_.clear(); } std::size_t TestReconfigServer:: getLeader() { for (std::size_t i = 0; i < cluster_.size(); i++) { if (cluster_[i]->isLeader()) { return i; } } return -1; } std::vector TestReconfigServer:: getFollowers() { std::vector followers; for (std::size_t i = 0; i < cluster_.size(); i++) { if (cluster_[i]->isFollower()) { followers.push_back(i); } } return followers; } void TestReconfigServer:: parseConfig(char* buf, int len, std::vector& servers, std::string& version) { std::string config(buf, len); std::stringstream ss(config); std::string line; std::string serverPrefix("server."); std::string versionPrefix("version="); servers.clear(); while(std::getline(ss, line, '\n')) { if (line.compare(0, serverPrefix.size(), serverPrefix) == 0) { servers.push_back(line); } else if (line.compare(0, versionPrefix.size(), versionPrefix) == 0) { version = line.substr(versionPrefix.size()); } } } bool TestReconfigServer:: waitForConnected(zhandle_t* zh, uint32_t timeout_sec) { for (uint32_t i = 0; i < timeout_sec; i++) { if (zoo_state(zh) == ZOO_CONNECTED_STATE) { return true; } sleep(1); } return false; } /** * 1. Connect to the leader. * 2. Remove a follower using incremental reconfig. * 3. Add the follower back using incremental reconfig. */ void TestReconfigServer:: testRemoveFollower() { std::vector servers; std::string version; struct Stat stat; int len = 1024; char buf[len]; // get config from leader. std::size_t leader = getLeader(); CPPUNIT_ASSERT(leader >= 0); std::string host = cluster_[leader]->getHostPort(); zhandle_t* zk = zookeeper_init(host.c_str(), NULL, 10000, NULL, NULL, 0); CPPUNIT_ASSERT_EQUAL(true, waitForConnected(zk, 10)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_getconfig(zk, 0, buf, &len, &stat)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:test", 10, NULL,(void*)ZOK)); // check if all the servers are listed in the config. parseConfig(buf, len, servers, version); // initially should be 1<<32, which is 0x100000000. This is the zxid // of the first NEWLEADER message, used as the initial version CPPUNIT_ASSERT_EQUAL(std::string("100000000"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } // remove a follower. std::vector followers = getFollowers(); len = 1024; CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(followers.size())); std::stringstream ss; ss << followers[0]; int rc = zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(std::string("100000002"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } // add the follower back. len = 1024; std::string serverString = cluster_[followers[0]]->getServerString(); rc = zoo_reconfig(zk, serverString.c_str(), NULL, NULL, -1, buf, &len, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } zookeeper_close(zk); } /** * 1. Connect to the leader. * 2. Remove a follower using non-incremental reconfig. * 3. Add the follower back using non-incremental reconfig. */ void TestReconfigServer:: testNonIncremental() { std::vector servers; std::string version; struct Stat stat; int len = 1024; char buf[len]; // get config from leader. std::size_t leader = getLeader(); CPPUNIT_ASSERT(leader >= 0); std::string host = cluster_[leader]->getHostPort(); zhandle_t* zk = zookeeper_init(host.c_str(), NULL, 10000, NULL, NULL, 0); CPPUNIT_ASSERT_EQUAL(true, waitForConnected(zk, 10)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_getconfig(zk, 0, buf, &len, &stat)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:test", 10, NULL,(void*)ZOK)); // check if all the servers are listed in the config. parseConfig(buf, len, servers, version); // initially should be 1<<32, which is 0x100000000. This is the zxid // of the first NEWLEADER message, used as the initial version CPPUNIT_ASSERT_EQUAL(std::string("100000000"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } // remove a follower. std::vector followers = getFollowers(); len = 1024; CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(followers.size())); std::stringstream ss; for (std::size_t i = 1; i < followers.size(); i++) { ss << cluster_[followers[i]]->getServerString() << ","; } ss << cluster_[leader]->getServerString(); int rc = zoo_reconfig(zk, NULL, NULL, ss.str().c_str(), -1, buf, &len, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(std::string("100000002"), version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } // add the follower back. len = 1024; ss.str(""); for (std::size_t i = 0; i < cluster_.size(); i++) { ss << cluster_[i]->getServerString() << ","; } rc = zoo_reconfig(zk, NULL, NULL, ss.str().c_str(), -1, buf, &len, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } zookeeper_close(zk); } zhandle_t* TestReconfigServer:: connectFollowers(std::vector &followers) { std::stringstream ss; std::size_t leader = getLeader(); CPPUNIT_ASSERT(leader >= 0); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(followers.size())); for (std::size_t i = 0; i < followers.size(); i++) { ss << cluster_[followers[i]]->getHostPort() << ","; } ss << cluster_[leader]->getHostPort(); std::string hosts = ss.str().c_str(); zoo_deterministic_conn_order(true); zhandle_t* zk = zookeeper_init(hosts.c_str(), NULL, 10000, NULL, NULL, 0); CPPUNIT_ASSERT_EQUAL(true, waitForConnected(zk, 10)); std::string connectedHost(zoo_get_current_server(zk)); std::string portString = connectedHost.substr(connectedHost.find(":") + 1); uint32_t port; std::istringstream (portString) >> port; CPPUNIT_ASSERT_EQUAL(cluster_[followers[0]]->getClientPort(), port); return zk; } /** * 1. Connect to a follower. * 2. Remove the follower the client is connected to. */ void TestReconfigServer:: testRemoveConnectedFollower() { std::vector servers; std::string version; struct Stat stat; int len = 1024; char buf[len]; // connect to a follower. std::stringstream ss; std::vector followers = getFollowers(); zhandle_t* zk = connectFollowers(followers); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:test", 10, NULL,(void*)ZOK)); // remove the follower. len = 1024; ss.str(""); ss << followers[0]; zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_getconfig(zk, 0, buf, &len, &stat)); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } zookeeper_close(zk); } /** * ZOOKEEPER-2014: only admin or users who are explicitly granted permission can do reconfig. */ void TestReconfigServer:: testReconfigFailureWithoutAuth() { std::vector servers; std::string version; struct Stat stat; int len = 1024; char buf[len]; // connect to a follower. std::stringstream ss; std::vector followers = getFollowers(); zhandle_t* zk = connectFollowers(followers); // remove the follower. len = 1024; ss.str(""); ss << followers[0]; // No auth, should fail. CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat)); // Wrong auth, should fail. CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:wrong", 11, NULL,(void*)ZOK)); CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat)); // Right auth, should pass. CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:test", 10, NULL,(void*)ZOK)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_getconfig(zk, 0, buf, &len, &stat)); parseConfig(buf, len, servers, version); CPPUNIT_ASSERT_EQUAL(NUM_SERVERS - 1, (uint32_t)(servers.size())); for (std::size_t i = 0; i < cluster_.size(); i++) { if (i == followers[0]) { continue; } CPPUNIT_ASSERT(std::find(servers.begin(), servers.end(), cluster_[i]->getServerString()) != servers.end()); } zookeeper_close(zk); } void TestReconfigServer:: testReconfigFailureWithoutServerSuperuserPasswordConfigured() { std::vector servers; std::string version; struct Stat stat; int len = 1024; char buf[len]; // Create a new quorum with the super user's password not configured. tearDown(); ZooKeeperQuorumServer::tConfigPairs configs; configs.push_back(std::make_pair("reconfigEnabled", "true")); cluster_ = ZooKeeperQuorumServer::getCluster(NUM_SERVERS, configs, ""); // connect to a follower. std::stringstream ss; std::vector followers = getFollowers(); zhandle_t* zk = connectFollowers(followers); // remove the follower. len = 1024; ss.str(""); ss << followers[0]; // All cases should fail as server ensemble was not configured with the super user's password. CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:", 11, NULL,(void*)ZOK)); CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat)); CPPUNIT_ASSERT_EQUAL((int)ZOK, zoo_add_auth(zk, "digest", "super:test", 10, NULL,(void*)ZOK)); CPPUNIT_ASSERT_EQUAL((int)ZNOAUTH, zoo_reconfig(zk, NULL, ss.str().c_str(), NULL, -1, buf, &len, &stat)); zookeeper_close(zk); } CPPUNIT_TEST_SUITE_REGISTRATION(TestReconfigServer); #endif apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestSASLAuth.cc0100644 0000000 0000000 00000033257 15051152474 030201 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 THREADED #include #include "CppAssertHelper.h" #include #include #include #include "Util.h" #include "WatchUtil.h" class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_SASLAuth); CPPUNIT_TEST(testServerRequireClientSASL); #ifdef HAVE_CYRUS_SASL_H CPPUNIT_TEST(testClientSASL); CPPUNIT_TEST(testClientSASLWithPasswordFileNewline); CPPUNIT_TEST(testClientSASLWithPasswordFileFirstLine); CPPUNIT_TEST(testClientSASLWithPasswordEncryptedText); CPPUNIT_TEST(testClientSASLWithPasswordEncryptedBinary); #ifdef ZOO_IPV6_ENABLED CPPUNIT_TEST(testClientSASLOverIPv6); #endif/* ZOO_IPV6_ENABLED */ CPPUNIT_TEST(testClientSASLReadOnly); CPPUNIT_TEST(testClientSASLPacketOrder); #endif /* HAVE_CYRUS_SASL_H */ CPPUNIT_TEST_SUITE_END(); FILE *logfile; static const char hostPorts[]; static const char jaasConf[]; 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 || state == ZOO_READONLY_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); } } public: Zookeeper_SASLAuth() { logfile = openlogfile("Zookeeper_SASLAuth"); } ~Zookeeper_SASLAuth() { if (logfile) { fflush(logfile); fclose(logfile); logfile = 0; } } void setUp() { zoo_set_log_stream(logfile); // Create SASL configuration file for server. FILE *conff = fopen("Zookeeper_SASLAuth.jaas.conf", "wt"); CPPUNIT_ASSERT(conff); size_t confLen = strlen(jaasConf); CPPUNIT_ASSERT_EQUAL(fwrite(jaasConf, 1, confLen, conff), confLen); CPPUNIT_ASSERT_EQUAL(fclose(conff), 0); conff = NULL; // Create password file for client. FILE *passf = fopen("Zookeeper_SASLAuth.password", "wt"); CPPUNIT_ASSERT(passf); CPPUNIT_ASSERT(fputs("mypassword", passf) > 0); CPPUNIT_ASSERT_EQUAL(fclose(passf), 0); passf = NULL; } void startServer(bool useJaasConf = true, bool readOnly = false) { char cmd[1024]; sprintf(cmd, "%s startRequireSASLAuth %s %s", ZKSERVER_CMD, useJaasConf ? "Zookeeper_SASLAuth.jaas.conf" : "", readOnly ? "true" : ""); CPPUNIT_ASSERT(system(cmd) == 0); } void stopServer() { char cmd[1024]; sprintf(cmd, "%s stop", ZKSERVER_CMD); CPPUNIT_ASSERT(system(cmd) == 0); } void testServerRequireClientSASL() { startServer(false); watchctx_t ctx; int rc = 0; zhandle_t *zk = zookeeper_init(hostPorts, watcher, 10000, 0, &ctx, 0); ctx.zh = zk; CPPUNIT_ASSERT(zk); // Wait for handle to be connected. CPPUNIT_ASSERT(ctx.waitForConnected(zk)); char pathbuf[80]; struct Stat stat_a = {0}; rc = zoo_create2(zk, "/serverRequireClientSASL", "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, sizeof(pathbuf), &stat_a); CPPUNIT_ASSERT_EQUAL((int)ZSESSIONCLOSEDREQUIRESASLAUTH, rc); stopServer(); } #ifdef HAVE_CYRUS_SASL_H // We need to disable the deprecation warnings as Apple has // decided to deprecate all of CyrusSASL's functions with OS 10.11 // (see MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also // for covering clang. #ifdef __APPLE__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif void testClientSASLHelper(const char *hostPorts, const char *path, const sasl_callback_t *callbacks) { startServer(); // Initialize Cyrus SASL. CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK); // Initialize SASL parameters. zoo_sasl_params_t sasl_params = { 0 }; sasl_params.service = "zookeeper"; sasl_params.host = "zk-sasl-md5"; sasl_params.mechlist = "DIGEST-MD5"; sasl_params.callbacks = callbacks; // Connect. watchctx_t ctx; int rc = 0; zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL, &ctx, /*flags*/0, /*log_callback*/NULL, &sasl_params); ctx.zh = zk; CPPUNIT_ASSERT(zk); // Wait for SASL auth to complete and handle to be connected. CPPUNIT_ASSERT(ctx.waitForConnected(zk)); // Leave mark. char pathbuf[80]; struct Stat stat_a = {0}; rc = zoo_create2(zk, path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, sizeof(pathbuf), &stat_a); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Stop and restart the server to test automatic reconnect & re-auth. stopServer(); CPPUNIT_ASSERT(ctx.waitForDisconnected(zk)); startServer(); // Wait for automatic SASL re-auth to complete. CPPUNIT_ASSERT(ctx.waitForConnected(zk)); // Check mark left above. rc = zoo_exists(zk, path, /*watch*/false, &stat_a); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); stopServer(); } void testClientSASLHelper(const char *hostPorts, const char *path, const char *password_file) { const sasl_callback_t *callbacks = zoo_sasl_make_basic_callbacks( "myuser", NULL, password_file); testClientSASLHelper(hostPorts, path, callbacks); } void testClientSASLHelper(const char *hostPorts, const char *path) { testClientSASLHelper(hostPorts, path, "Zookeeper_SASLAuth.password"); } void testClientSASLHelper(const char *hostPorts, const char *path, zoo_sasl_password_t *password) { const sasl_callback_t *callbacks = zoo_sasl_make_password_callbacks( "myuser", NULL, password); testClientSASLHelper(hostPorts, path, callbacks); } void testClientSASL() { testClientSASLHelper(hostPorts, "/clientSASL"); } void testClientSASL(const char *password_file, const char *content, size_t content_len, const char *path, zoo_sasl_password_callback_t callback) { // Create password file for client. FILE *passf = fopen(password_file, "wt"); CPPUNIT_ASSERT(passf); // Write the specified content into the file. size_t len = fwrite(content, sizeof(content[0]), content_len, passf); CPPUNIT_ASSERT_EQUAL(len, content_len); CPPUNIT_ASSERT_EQUAL(fclose(passf), 0); passf = NULL; zoo_sasl_password_t passwd = {password_file, this, callback}; testClientSASLHelper(hostPorts, path, &passwd); } void testClientSASLWithPasswordFileNewline() { // Insert a newline immediately after the correct password. const char content[] = "mypassword\nabc"; testClientSASL("Zookeeper_SASLAuth.password.newline", content, sizeof(content) - 1, "/clientSASLWithPasswordFileNewline", NULL); } void testClientSASLWithPasswordFileFirstLine() { // Insert multiple newlines and check if only the first line is accepted as the // actual password. const char content[] = "mypassword\nabc\nxyz"; testClientSASL("Zookeeper_SASLAuth.password.firstline", content, sizeof(content) - 1, "/clientSASLWithPasswordFileFirstLine", NULL); } int decryptPassword(const char *content, size_t content_len, char incr, char *buf, size_t buf_len, size_t *passwd_len) { CPPUNIT_ASSERT(content_len <= buf_len); // A simple decryption that only increases each character by a fixed value. for (size_t i = 0; i < content_len; ++i) { buf[i] = content[i] + incr; } *passwd_len = content_len; // Since null terminator has not been appended to buf, use memcmp. CPPUNIT_ASSERT_EQUAL(memcmp(buf, "mypassword", *passwd_len), 0); return SASL_OK; } static int textPasswordCallback(const char *content, size_t content_len, void *context, char *buf, size_t buf_len, size_t *passwd_len) { Zookeeper_SASLAuth *auth = static_cast(context); return auth->decryptPassword(content, content_len, 1, buf, buf_len, passwd_len); } void testClientSASLWithPasswordEncryptedText() { // Encrypt "mypassword" by subtracting 1 from each character in it as plain text. const char content[] = {0x6C, 0x78, 0x6F, 0x60, 0x72, 0x72, 0x76, 0x6E, 0x71, 0x63}; testClientSASL("Zookeeper_SASLAuth.password.encrypted.text", content, sizeof(content), "/clientSASLWithPasswordEncryptedText", textPasswordCallback); } static int binaryPasswordCallback(const char *content, size_t content_len, void *context, char *buf, size_t buf_len, size_t *passwd_len) { Zookeeper_SASLAuth *auth = static_cast(context); return auth->decryptPassword(content, content_len, 'a', buf, buf_len, passwd_len); } void testClientSASLWithPasswordEncryptedBinary() { // Encrypt "mypassword" by subtracting 'a' from each character in it as binary format. const char content[] = {0x0C, 0x18, 0x0F, 0x00, 0x12, 0x12, 0x16, 0x0E, 0x11, 0x03}; testClientSASL("Zookeeper_SASLAuth.password.encrypted.binary", content, sizeof(content), "/clientSASLWithPasswordEncryptedBinary", binaryPasswordCallback); } void testClientSASLOverIPv6() { const char *ipAndPort = "::1:22181"; testClientSASLHelper(ipAndPort, "/clientSASLOverIPv6"); } void testClientSASLReadOnly() { startServer(/*useJaasConf*/ true, /*readOnly*/ true); // Initialize Cyrus SASL. CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK); // Initialize SASL parameters. zoo_sasl_params_t sasl_params = { 0 }; sasl_params.service = "zookeeper"; sasl_params.host = "zk-sasl-md5"; sasl_params.mechlist = "DIGEST-MD5"; sasl_params.callbacks = zoo_sasl_make_basic_callbacks( "myuser", NULL, "Zookeeper_SASLAuth.password"); // Connect. watchctx_t ctx; int rc = 0; zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL, &ctx, /*flags*/ZOO_READONLY, /*log_callback*/NULL, &sasl_params); ctx.zh = zk; CPPUNIT_ASSERT(zk); // Wait for SASL auth to complete and handle to be connected. CPPUNIT_ASSERT(ctx.waitForConnected(zk)); // Assert can read. char buf[1024]; int len = sizeof(buf); rc = zoo_get(zk, "/", 0, buf, &len, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); // Assert can not write. char path[1024]; rc = zoo_create(zk, "/test", "hello", 5, &ZOO_OPEN_ACL_UNSAFE, 0, path, sizeof(path)); CPPUNIT_ASSERT_EQUAL((int)ZNOTREADONLY, rc); stopServer(); } void testClientSASLPacketOrder() { startServer(); // Initialize Cyrus SASL. CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK); // Initialize SASL parameters. zoo_sasl_params_t sasl_params = { 0 }; sasl_params.service = "zookeeper"; sasl_params.host = "zk-sasl-md5"; sasl_params.mechlist = "DIGEST-MD5"; sasl_params.callbacks = zoo_sasl_make_basic_callbacks( "myuser", NULL, "Zookeeper_SASLAuth.password"); // Connect. watchctx_t ctx; int rc = 0; zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL, &ctx, /*flags*/0, /*log_callback*/NULL, &sasl_params); ctx.zh = zk; CPPUNIT_ASSERT(zk); // No wait: try and queue a packet before SASL auth is complete. char buf[1024]; int len = sizeof(buf); rc = zoo_get(zk, "/", 0, buf, &len, 0); CPPUNIT_ASSERT_EQUAL((int)ZOK, rc); stopServer(); } #ifdef __APPLE__ #pragma GCC diagnostic pop #endif #endif /* HAVE_CYRUS_SASL_H */ }; const char Zookeeper_SASLAuth::hostPorts[] = "127.0.0.1:22181"; const char Zookeeper_SASLAuth::jaasConf[] = "Server {\n" " org.apache.zookeeper.server.auth.DigestLoginModule required\n" " user_myuser=\"mypassword\";\n" "};\n"; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_SASLAuth); #endif /* THREADED */ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestWatchers.cc0100644 0000000 0000000 00000075102 15051152474 030370 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "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); apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestZookeeperClose.cc0100644 0000000 0000000 00000052460 15051152474 031543 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.data)); // 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->sock=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.data)); // 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); CPPUNIT_ASSERT_EQUAL(ZOO_NOTCONNECTED_STATE, zoo_state(zh)); 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.data)); // 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.data)); // 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.data)); 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.data)); 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.data)); 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.data)); 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); apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/TestZookeeperInit.cc0100644 0000000 0000000 00000025411 15051152474 031375 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 unsigned 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 != NULL); CPPUNIT_ASSERT(zh->fd->sock == -1); CPPUNIT_ASSERT(zh->hostname != NULL); CPPUNIT_ASSERT_EQUAL(EXPECTED_ADDRS_COUNT,zh->addrs.count); CPPUNIT_ASSERT_EQUAL(EXPECTED_HOST,string(zh->hostname)); CPPUNIT_ASSERT(zh->state == ZOO_NOTCONNECTED_STATE); CPPUNIT_ASSERT(zh->context == (void*)1); CPPUNIT_ASSERT_EQUAL(EXPECTED_RECV_TIMEOUT,zh->recv_timeout); CPPUNIT_ASSERT(zh->watcher == watcher); CPPUNIT_ASSERT(zh->addrs.next==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 unsigned 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(unsigned int i=0;iaddrs.count;i++){ sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs.data[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 unsigned 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(unsigned int i=0;iaddrs.count;i++){ sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs.data[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 unsigned 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(unsigned int i=0;iaddrs.count;i++){ sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs.data[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((ENOENT|EINVAL) & 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 unsigned int EXPECTED_ADDR_COUNT=COUNTOF(EXPECTED); const int RAND_SEQ[]={0,1,1,-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(unsigned int i=0;iaddrs.count;i++){ sockaddr_in* addr=(struct sockaddr_in*)&zh->addrs.data[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); apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/ThreadingUtil.cc0100644 0000000 0000000 00000004701 15051152474 030510 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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) { #if defined(__GNUC__) return __sync_fetch_and_add(operand,incr); #else int32_t result; __asm__ __volatile__( "lock xaddl %0,%1\n" : "=r"(result), "=m"(*operand) : "0"(incr) : "memory"); return result; #endif } int32_t atomic_fetch_store(volatile int32_t *ptr, int32_t value) { #if defined(__GNUC__) return __sync_lock_test_and_set(ptr,value); #else int32_t result; __asm__ __volatile__("lock xchgl %0,%1\n" : "=r"(result), "=m"(*ptr) : "0"(value) : "memory"); return result; #endif } #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 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/ThreadingUtil.h0100644 0000000 0000000 00000015135 15051152474 030355 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/Util.cc0100644 0000000 0000000 00000002702 15051152474 026661 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "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; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/Util.h0100644 0000000 0000000 00000007570 15051152474 026533 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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(LOGSTREAM, ZOO_LOG_LEVEL_DEBUG,__LINE__,__func__,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 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/WatchUtil.h0100644 0000000 0000000 00000007335 15051152474 027521 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 WATCH_UTIL_H_ #define WATCH_UTIL_H_ #include #include #include using namespace std; #include "CollectionUtil.h" #include "ThreadingUtil.h" using namespace Util; #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; 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; } 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; #endif /*WATCH_UTIL_H_*/ apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/ZKMocks.cc0100644 0000000 0000000 00000041121 15051152474 027263 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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) { unique_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 = zoo_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 = zoo_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)); offset+=sizeof(req->passwd); memcpy(&req->readOnly,buf.data()+offset,sizeof(req->readOnly)); 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=zoo_htonll(sessionId); buf.append((char*)&tmp64,sizeof(sessionId)); tmp=htonl(passwd_len); buf.append((char*)&tmp,sizeof(tmp)); buf.append(passwd,sizeof(passwd)); if (!omitReadOnly) { buf.append(&readOnly,sizeof(readOnly)); } // finally set the buffer length tmp=htonl(buf.size()); 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 unique_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==ZOO_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, const struct timeval *last_recv_send){ // simulate connected state zh->state=ZOO_CONNECTED_STATE; // Simulate we're connected to the first host in our host list zh->fd->sock=ZookeeperServer::FD; assert(zh->addrs.count > 0); zh->addr_cur = zh->addrs.data[0]; zh->addrs.next++; zh->input_buffer=0; if (last_recv_send) { zh->last_recv = *last_recv_send; zh->last_send = *last_recv_send; } else { 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; } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/ZKMocks.h0100644 0000000 0000000 00000036440 15051152474 027135 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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, const struct timeval *last_recv_send = NULL); /** * 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)),readOnly(0),omitReadOnly(false) { memcpy(passwd,"1234567890123456",sizeof(passwd)); } int32_t protocolVersion; int32_t timeOut; int64_t sessionId; int32_t passwd_len; char passwd[16]; char readOnly; bool omitReadOnly; 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 #include #include #include #include #include #include #include ZooKeeperQuorumServer:: ZooKeeperQuorumServer(uint32_t id, uint32_t numServers, std::string config, std::string env) : id_(id), env_(env), numServers_(numServers) { const char* root = getenv("ZKROOT"); if (root == NULL) { assert(!"Environment variable 'ZKROOT' is not set"); } root_ = root; createConfigFile(config); createDataDirectory(); start(); } ZooKeeperQuorumServer:: ~ZooKeeperQuorumServer() { stop(); } std::string ZooKeeperQuorumServer:: getHostPort() { std::stringstream ss; ss << "localhost:" << getClientPort(); return ss.str(); } uint32_t ZooKeeperQuorumServer:: getClientPort() { return CLIENT_PORT_BASE + id_; } void ZooKeeperQuorumServer:: start() { std::string command = root_ + "/bin/zkServer.sh start " + getConfigFileName(); if (!env_.empty()) { command = env_ + " " + command; } assert(system(command.c_str()) == 0); } void ZooKeeperQuorumServer:: stop() { std::string command = root_ + "/bin/zkServer.sh stop " + getConfigFileName(); assert(system(command.c_str()) == 0); } std::string ZooKeeperQuorumServer:: getMode() { char buf[1024]; std::string result; std::string command = root_ + "/bin/zkServer.sh status " + getConfigFileName(); FILE* output = popen(command.c_str(), "r"); do { if (fgets(buf, 1024, output) != NULL) { result += buf; } } while (!feof(output)); pclose(output); if (result.find("Mode: leader") != std::string::npos) { return "leader"; } else if (result.find("Mode: follower") != std::string::npos) { return "follower"; } else { printf("%s\n", result.c_str()); return ""; } } bool ZooKeeperQuorumServer:: isLeader() { return getMode() == "leader"; } bool ZooKeeperQuorumServer:: isFollower() { return getMode() == "follower"; } void ZooKeeperQuorumServer:: createConfigFile(std::string config) { std::string command = "mkdir -p " + root_ + "/build/test/test-cppunit/conf"; assert(system(command.c_str()) == 0); std::ofstream confFile; std::stringstream ss; ss << id_ << ".conf"; std::string fileName = root_ + "/build/test/test-cppunit/conf/" + ss.str(); confFile.open(fileName.c_str()); confFile << "tickTime=2000\n"; confFile << "clientPort=" << getClientPort() << "\n"; confFile << "initLimit=5\n"; confFile << "syncLimit=2\n"; confFile << "dataDir=" << getDataDirectory() << "\n"; for (uint32_t i = 0; i < numServers_; i++) { confFile << getServerString(i) << "\n"; } // Append additional config, if any. if (!config.empty()) { confFile << config << std::endl; } confFile.close(); } std::string ZooKeeperQuorumServer:: getConfigFileName() { std::stringstream ss; ss << id_ << ".conf"; return root_ + "/build/test/test-cppunit/conf/" + ss.str(); } void ZooKeeperQuorumServer:: createDataDirectory() { std::string dataDirectory = getDataDirectory(); std::string command = "rm -rf " + dataDirectory; assert(system(command.c_str()) == 0); command = "mkdir -p " + dataDirectory; assert(system(command.c_str()) == 0); std::ofstream myidFile; std::string fileName = dataDirectory + "/myid"; myidFile.open(fileName.c_str()); myidFile << id_ << "\n"; myidFile.close(); setenv("ZOO_LOG_DIR", dataDirectory.c_str(), true); } std::string ZooKeeperQuorumServer:: getServerString() { return getServerString(id_); } std::string ZooKeeperQuorumServer:: getServerString(uint32_t id) { std::stringstream ss; ss << "server." << id << "=localhost:" << SERVER_PORT_BASE + id << ":" << ELECTION_PORT_BASE + id << ":participant;localhost:" << CLIENT_PORT_BASE + id; return ss.str(); } std::string ZooKeeperQuorumServer:: getDataDirectory() { std::stringstream ss; ss << "data" << id_; return root_ + "/build/test/test-cppunit/" + ss.str(); } std::vector ZooKeeperQuorumServer:: getCluster(uint32_t numServers) { std::vector cluster; for (uint32_t i = 0; i < numServers; i++) { cluster.push_back(new ZooKeeperQuorumServer(i, numServers)); } // Wait until all the servers start, and fail if they don't start within 10 // seconds. for (uint32_t i = 0; i < 10; i++) { uint32_t j = 0; for (; j < cluster.size(); j++) { if (cluster[j]->getMode() == "") { // The server hasn't started. sleep(1); break; } } if (j == cluster.size()) { return cluster; } } assert(!"The cluster didn't start for 10 seconds"); return {}; } std::vector ZooKeeperQuorumServer:: getCluster(uint32_t numServers, ZooKeeperQuorumServer::tConfigPairs configs, std::string env) { std::vector cluster; std::string config; for (ZooKeeperQuorumServer::tConfigPairs::const_iterator iter = configs.begin(); iter != configs.end(); ++iter) { std::pair pair = *iter; config += (pair.first + "=" + pair.second + "\n"); } for (uint32_t i = 0; i < numServers; i++) { cluster.push_back(new ZooKeeperQuorumServer(i, numServers, config, env)); } // Wait until all the servers start, and fail if they don't start within 10 // seconds. for (uint32_t i = 0; i < 10; i++) { uint32_t j = 0; for (; j < cluster.size(); j++) { if (cluster[j]->getMode() == "") { // The server hasn't started. sleep(1); break; } } if (j == cluster.size()) { return cluster; } } assert(!"The cluster didn't start for 10 seconds"); } apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/ZooKeeperQuorumServer.h0100644 0000000 0000000 00000004631 15051152474 032114 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * 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_QUORUM_SERVER_H #define ZOOKEEPER_QUORUM_SERVER_H #include #include #include #include class ZooKeeperQuorumServer { public: ~ZooKeeperQuorumServer(); typedef std::vector > tConfigPairs; static std::vector getCluster(uint32_t numServers); static std::vector getCluster(uint32_t numServers, tConfigPairs configs, /* Additional config options as a list of key/value pairs. */ std::string env /* Additional environment variables when starting zkServer.sh. */); std::string getHostPort(); uint32_t getClientPort(); void start(); void stop(); bool isLeader(); bool isFollower(); std::string getServerString(); private: ZooKeeperQuorumServer(); ZooKeeperQuorumServer(uint32_t id, uint32_t numServers, std::string config = "", std::string env = ""); ZooKeeperQuorumServer(const ZooKeeperQuorumServer& that); const ZooKeeperQuorumServer& operator=(const ZooKeeperQuorumServer& that); void createConfigFile(std::string config = ""); std::string getConfigFileName(); void createDataDirectory(); std::string getDataDirectory(); static std::string getServerString(uint32_t id); std::string getMode(); static const uint32_t SERVER_PORT_BASE = 2000; static const uint32_t ELECTION_PORT_BASE = 3000; static const uint32_t CLIENT_PORT_BASE = 4000; uint32_t id_; std::string env_; uint32_t numServers_; std::string root_; }; #endif // ZOOKEEPER_QUORUM_SERVER_H apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/wrappers-mt.opt0100644 0000000 0000000 00000000153 15051152474 030440 0ustar00rootroot0000000 0000000 -Wl,--wrap -Wl,pthread_mutex_lock -Wl,--wrap -Wl,pthread_mutex_trylock -Wl,--wrap -Wl,pthread_mutex_unlock apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/wrappers.opt0100644 0000000 0000000 00000000266 15051152474 030027 0ustar00rootroot0000000 0000000 -Wl,--wrap -Wl,calloc -Wl,--wrap -Wl,free -Wl,--wrap -Wl,flush_send_queue -Wl,--wrap -Wl,get_xid -Wl,--wrap -Wl,deliverWatchers -Wl,--wrap -Wl,activateWatcher -Wl,--wrap -Wl,realloc apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/zkServer.sh0100755 0000000 0000000 00000015315 15051152474 027613 0ustar00rootroot0000000 0000000 #!/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. # This is the port where zookeeper server runs on. ZOOPORT=${ZOOPORT:-"22181"} # Some tests are setting the maxClientConnections. When it is not set, we fallback to default 100 ZKMAXCNXNS=${ZKMAXCNXNS:-"100"} EXTRA_JVM_ARGS=${EXTRA_JVM_ARGS:-""} if [ "x$1" == "x" ] then echo "USAGE: $0 startClean|start|startCleanReadOnly|startRequireSASLAuth [jaasConf] [readOnly]|stop" exit 2 fi # ===== # ===== cleanup old executions # ===== case "`uname`" in CYGWIN*) cygwin=true ;; *) cygwin=false ;; esac if $cygwin then # cygwin has a "kill" in the shell itself, gets confused KILL=/bin/kill else KILL=kill 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 # [ZOOKEEPER-820] If lsof command is present, look for a process listening # on ZOOPORT and kill it. which lsof &> /dev/null if [ $? -eq 0 ] then pid=`lsof -i :$ZOOPORT | grep LISTEN | awk '{print $2}'` if [ -n "$pid" ] then $KILL -9 $pid fi fi # ===== # ===== build classpath # ===== 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" CLASSPATH="$CLASSPATH:${zk_base}/zookeeper-server/target/classes" for i in "${zk_base}"/build/lib/*.jar do CLASSPATH="$CLASSPATH:$i" done for d in "${zk_base}"/zookeeper-server/target/lib/*.jar do CLASSPATH="$d:$CLASSPATH" done for i in "${zk_base}"/zookeeper-server/src/main/resource/lib/*.jar do CLASSPATH="$CLASSPATH:$i" done CLASSPATH="$CLASSPATH:${CLOVER_HOME}/lib/clover*.jar" if $cygwin then CLASSPATH=`cygpath -wp "$CLASSPATH"` fi # ===== # ===== initialize JVM arguments # ===== read_only= PROPERTIES="$EXTRA_JVM_ARGS -Dzookeeper.extendedTypesEnabled=true -Dznode.container.checkIntervalMs=100" if [ "x$1" == "xstartRequireSASLAuth" ] then PROPERTIES="-Dzookeeper.sessionRequireClientSASLAuth=true $PROPERTIES" PROPERTIES="$PROPERTIES -Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider" if [ "x$2" != "x" ] then PROPERTIES="$PROPERTIES -Djava.security.auth.login.config=$2" fi if [ "x$3" != "x" ] then PROPERTIES="-Dreadonlymode.enabled=true $PROPERTIES" read_only=true fi fi if [ "x$1" == "xstartCleanReadOnly" ] then PROPERTIES="-Dreadonlymode.enabled=true $PROPERTIES" read_only=true fi # ===== # ===== initialize data and test directories # ===== if [ "x${base_dir}" == "x" ] then tmp_dir="/tmp" tests_dir="tests" else tmp_dir="${base_dir}/build/tmp" tests_dir=${base_dir}/zookeeper-client/zookeeper-client-c/tests fi # ===== # ===== start the ZooKeeper server # ===== case $1 in start|startClean|startRequireSASLAuth|startCleanReadOnly) if [ "x$1" == "xstartClean" ] || [ "x$1" == "xstartCleanReadOnly" ] then rm -rf "${tmp_dir}/zkdata" fi mkdir -p "${tmp_dir}/zkdata" # ===== initialize certificates certs_dir="/tmp/certs" rm -rf "${certs_dir}" mkdir -p "${certs_dir}" cp ${tests_dir}/../ssl/gencerts.sh "${certs_dir}/" > /dev/null cd ${certs_dir} > /dev/null # GitHub is providing us hostnames with more than 64 characters now. # And there are no cppunit tests do hostname verification currently, # so we could set CN to arbitrary hostname for now. ./gencerts.sh tests.zookeeper.apache.org > ./gencerts.stdout 2> ./gencerts.stderr cd - > /dev/null # ===== prepare the configs sed "s#TMPDIR#${tmp_dir}#g;s#CERTDIR#${certs_dir}#g;s#MAXCLIENTCONNECTIONS#${ZKMAXCNXNS}#g;s#CLIENTPORT#${ZOOPORT}#g" ${tests_dir}/zoo.cfg > "${tmp_dir}/zoo.cfg" if [ "x$read_only" != "x" ] then # we can put the new server to read-only mode by starting only a single instance of a three node server echo "server.1=localhost:22881:33881" >> ${tmp_dir}/zoo.cfg echo "server.2=localhost:22882:33882" >> ${tmp_dir}/zoo.cfg echo "server.3=localhost:22883:33883" >> ${tmp_dir}/zoo.cfg echo "1" > ${tmp_dir}/zkdata/myid main_class="org.apache.zookeeper.server.quorum.QuorumPeerMain" else main_class="org.apache.zookeeper.server.ZooKeeperServerMain" fi # ===== start the server java -cp "$CLASSPATH" $PROPERTIES ${main_class} ${tmp_dir}/zoo.cfg &> "${tmp_dir}/zk.log" & pid=$! echo -n $! > /tmp/zk.pid # ===== wait for the server to start if [ "x$1" == "xstartRequireSASLAuth" ] || [ "x$1" == "xstartCleanReadOnly" ] then # ===== in these cases we can not connect simply with the java client, so we are just waiting... sleep 4 success=true else # ===== wait max 120 seconds for server to be ready to server clients (this handles testing on slow hosts) success=false for i in {1..120} do if ps -p $pid > /dev/null then java -cp "$CLASSPATH" $PROPERTIES org.apache.zookeeper.ZooKeeperMain -server localhost:$ZOOPORT ls / > /dev/null 2>&1 if [ $? -ne 0 ] then # server not up yet - wait sleep 1 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 fi 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 apache-zookeeper-3.9.4/zookeeper-client/zookeeper-client-c/tests/zoo.cfg0100644 0000000 0000000 00000000607 15051152474 026727 0ustar00rootroot0000000 0000000 tickTime=500 initLimit=10 syncLimit=5 dataDir=TMPDIR/zkdata maxClientCnxns=MAXCLIENTCONNECTIONS localSessionsEnabled=true clientPort=CLIENTPORT secureClientPort=22281 serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory ssl.keyStore.location=CERTDIR/server.jks ssl.keyStore.password=password ssl.trustStore.location=CERTDIR/servertrust.jks ssl.trustStore.password=password apache-zookeeper-3.9.4/zookeeper-compatibility-tests/pom.xml0100644 0000000 0000000 00000004170 15051152474 024645 0ustar00rootroot0000000 0000000 org.apache.zookeeper parent 3.9.4 pom 4.0.0 zookeeper-compatibility-tests zookeeper-compatibility-tests-curator Apache ZooKeeper - Compatibility Tests Module for various compatibility tests org.apache.maven.plugins maven-install-plugin true org.apache.maven.plugins maven-deploy-plugin true apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/pom.xml0100644 0000000 0000000 00000005551 15051152474 034340 0ustar00rootroot0000000 0000000 org.apache.zookeeper zookeeper-compatibility-tests 3.9.4 4.0.0 zookeeper-compatibility-tests-curator Apache ZooKeeper - Compatibility Tests - Curator Module for Apache Curator compatibility tests 5.0.0 org.apache.zookeeper zookeeper ${project.version} org.apache.curator curator-recipes ${apache-curator-version} org.apache.zookeeper zookeeper test org.apache.curator curator-test ${apache-curator-version} org.apache.zookeeper zookeeper test org.junit.jupiter junit-jupiter-engine test ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-compatibility-tests_zookeeper-compatibility-tests-c0100644 0000000 0000000 00000000271 15051152474 032532 xustar000000000 0000000 185 path=apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/java/org/apache/zookeeper/compatibility/TestApacheCuratorCompatibility.java apache-zookeeper-3.9.4/zookeeper-compatibility-tests/zookeeper-compatibility-tests-curator/src/test/0100644 0000000 0000000 00000007237 15051152474 034576 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.compatibility; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.CuratorCache; import org.apache.curator.retry.RetryOneTime; import org.apache.curator.test.TestingCluster; import org.apache.curator.test.TestingServer; import org.junit.jupiter.api.Test; /** * Make sure minimal Apache Curator APIs work correctly. As it's a widely used ZooKeeper * client library we should not break it. */ public class TestApacheCuratorCompatibility { private static final int TIMEOUT_MS = 5000; @Test public void testBasicUsageOfApisAndRecipes() throws Exception { try (TestingServer server = new TestingServer()) { doTest(server.getConnectString()); } } @Test public void testBasicUsageOfApisAndRecipesInCluster() throws Exception { try (TestingCluster cluster = new TestingCluster(3)) { cluster.start(); doTest(cluster.getConnectString()); } } private void doTest(String connectionString) throws Exception { RetryOneTime retryPolicy = new RetryOneTime(1); try (CuratorFramework client = CuratorFrameworkFactory.newClient(connectionString, retryPolicy)) { try (CuratorCache cache = CuratorCache.build(client, "/base/path")) { client.start(); cache.start(); BlockingQueue paths = new LinkedBlockingQueue<>(); cache.listenable().addListener((dummy1, dummy2, data) -> paths.add(data.getPath())); client.create().creatingParentsIfNeeded().forPath("/base/path/1"); client.create().creatingParentsIfNeeded().forPath("/base/path/2"); client.create().creatingParentsIfNeeded().forPath("/base/path/1/a"); client.create().creatingParentsIfNeeded().forPath("/base/path/2/a"); assertEquals("/base/path", poll(paths)); assertEquals("/base/path/1", poll(paths)); assertEquals("/base/path/2", poll(paths)); assertEquals("/base/path/1/a", poll(paths)); assertEquals("/base/path/2/a", poll(paths)); } } } private static String poll(BlockingQueue queue) { try { String value = queue.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS); assertNotNull(value, "Event poll timed out"); return value; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } } apache-zookeeper-3.9.4/zookeeper-contrib/build-contrib.xml0100644 0000000 0000000 00000022405 15051152474 024237 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/build.xml0100644 0000000 0000000 00000005150 15051152474 022577 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/ivysettings.xml0100644 0000000 0000000 00000003341 15051152474 024070 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/pom.xml0100755 0000000 0000000 00000005256 15051152474 022305 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-contrib pom Apache ZooKeeper - Contrib Contrib projects to Apache ZooKeeper true full-build zookeeper-contrib-fatjar zookeeper-contrib-loggraph zookeeper-contrib-rest zookeeper-contrib-zooinspector fatjar zookeeper-contrib-fatjar org.apache.maven.plugins maven-compiler-plugin -Xlint:none org.apache.maven.plugins maven-checkstyle-plugin checkstyle-simple.xml apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/README.md0100644 0000000 0000000 00000001000 15051152474 027131 0ustar00rootroot0000000 0000000 ZooKeeper Fatjar ================ This package contains build to create a fat zookeeper jar. Fatjar can be used to run: - zookeeper server - zookeeper client - distributed load generator for testing (generateLoad) - container that will instantiate classes as directed by an instance manager (ic) - system test (systest) - jmh micro benchmarks (jmh) Use following command to build fatjar ``` mvn clean install -P fatjar -DskipTests ``` To run the fatjar use: ``` java -jar zoookeeper--fatjar.jar ``` apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/pom.xml0100755 0000000 0000000 00000011002 15051152474 027175 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-contrib 3.9.4 org.apache.zookeeper zookeeper-contrib-fatjar jar Apache ZooKeeper - Contrib - Fatjar true true org.apache.zookeeper zookeeper-jute ${project.version} org.apache.zookeeper zookeeper ${project.version} org.apache.zookeeper zookeeper ${project.version} test-jar org.apache.zookeeper zookeeper-it ${project.version} org.slf4j slf4j-api commons-cli commons-cli org.eclipse.jetty jetty-server org.eclipse.jetty jetty-servlet com.fasterxml.jackson.core jackson-databind jline jline io.dropwizard.metrics metrics-core org.xerial.snappy snappy-java ch.qos.logback logback-core ${project.basedir}/src/main/resources ${project.basedir}/../../conf logback.xml org.apache.maven.plugins maven-assembly-plugin jar-with-dependencies org.apache.zookeeper.util.FatJarMain zookeeper-${project.version}-fatjar false false make-assembly package single ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-fatjar_src_main_java_org_0100644 0000000 0000000 00000000203 15051152474 032512 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/zookeeper/util/FatJarMain.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/java/org/apache/zookeeper0100644 0000000 0000000 00000010634 15051152474 034261 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; import java.util.List; import java.util.Map; /** * 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 Map cmds = new HashMap(); static List 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); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-fatjar/src/main/resources/mainClasses0100644 0000000 0000000 00000001062 15051152474 033574 0ustar00rootroot0000000 0000000 ::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 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 jmh:org.apache.zookeeper.BenchMain:Run jmh micro benchmarks apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/README0100644 0000000 0000000 00000004767 15051152474 027477 0ustar00rootroot0000000 0000000 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://zookeeper.apache.org/ ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/Makefile0100644 0000000 0000000 00000001644 15051152474 031230 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/setup.py0100644 0000000 0000000 00000003247 15051152474 031303 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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://zookeeper.apache.org/', 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/") } ) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py0100644 0000000 0000000 00000001420 15051152474 033442 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py0100644 0000000 0000000 00000002271 15051152474 033036 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py0100644 0000000 0000000 00000001421 15051152474 033167 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py0100644 0000000 0000000 00000017046 15051152474 032673 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py0100644 0000000 0000000 00000002117 15051152474 033547 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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", ] ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000167 15051152474 032671 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_0100644 0000000 0000000 00000016513 15051152474 034430 0ustar00rootroot0000000 0000000 ‰PNG  IHDR‰ÊXÓÔ pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFvIDATxÚìšyeU}Ç?çÜåí¯×éîYzöEg¦Yf6g"&`…D(À˜˜ˆ1q‰! I™JH´F#I aŒ‘DÅA‰Í0³Àl Ì>=COïݯûõ[îv–üñšaZe¬J¥8U÷÷ªî÷þî÷üÎçœß¯®°Öòóqõ§ŸýV±à-–’f‹Õ Ö)[›ªÇÿ—J_{ú«ï ßTä†ûvìyä3kr#UãÀXK¢ ®_ß>Éó;úŸ:Õçæ ×ÔßHDJ!²¥ºuŽŽZŽhöžJȧ\Ö.kfýÚîwæÚ›>ùf‘H ÂvÁw¾#Hû’‘É€e-pëEMtweo~3@ð°¬#H»’Ÿ®,ˆ”B)CÏÒN>òõ}5a¬˜ Õ©¾¾pc\*}ñ鯾'p- 8RN»d1Æ% ¿{Å,F«††W]Ùi¯–|}ûäÝÏïè_³þÛnڼᚪ´LãnG€ïJ´¶LÔ-oâÕµ¹ö¦/4<±¸€ÄJ´QgŸÿi¯\î´WÇK‚-Ç#ÊÅs¸Â:N¤ª@ókÞÈ+RB œéhÜ$Žjž,äšÒ Ö‚!*¯zeN{%¬Å‚WVŒ«´¸åóÏ͈M`h(šn]7W‚cIøqá•uçnúüÅWŸÍ÷ßû|¢r§SéuÃXF42ö •u܆Wg‘ª"Œ§3öl£„%OZ_çvdcUü¦"QlößtÏŽ% »ÀÖ^ñjÚja¬®6å¼L#êÿ(IþÆ["¿ ‘7Ì“}ŽÝ®”¾YkM¢ôÆK/XùOoœÝgäÉÁ£'ß®´î7ÆV•ÒCsfΘa­eçÞƒ£¹lº+Q:Ÿ$jö5W^rଯsðèÉ”Öûµ6‡’Du*­uœ$ĉ"å{:Qº3Ž“C‰Rû¿½é'¼Nä¥#'>›Í¤ïèžÙ!›‹ù.mÌ¥tgÅa„±¶3IÔ‘¹³;»ÞqAlk)Þñåÿí³¯ñ$I”-Yk­ˆE{kSN Ò!KÌI¢rQœÐ?4Êæí{mÆòuž<·÷à}MÅügÂ(ÆC¹Rãåþa|ÏeÞœ.\ÇAiMFì;pìówÞvÃéHÄîý‡ŸOÕ¡Éu´·´Ôê!å©*Ï<¿ÿÙZ=ü³tʧ¥©pÏꞥ—X “Nñôö½q¢j‰R#ŸøÝ›V»I¢Î[8o–œ(W˜ªÔp]‡á± ¤”v÷ßöŸ?úÃc?êšÑÊD¹ÂEç/o™ÑÖÜò·|k€L”&NJi´6X …|–æbþ´ûI¢ÐÆ`¬ÅC¢õ $¨×ÆöîÙwàXGKS!·òm ZÂ0ffGã¥ò=}û‡¯<àž®­Äq‚”’þp¢Vª©$y±›ž|ö¾®­ŸQZãH‰‚á1´1ÌìhCkCFÔƒ'·îúüýŸûÈg_“'ýhë]‰RŸ.r8Râû¹lš¹³;éžÙA&"å{¤S>óætá¹Î§ï¼ëþ»^“'q¢Lg{‹È¤}öáè‰þš#evÝ…ç üË#?´Ž#ë—¬^‘kom¢½µIì۷ϼ.OþjÃ7Ÿ˜º­V«´µ¶¬*ä³»®¾ìÂ.¥4|ã;CQX_e­ÝÔªIýó£}ùƒ¯[;Ÿúðo~Xéû©eŽ#‡­µN¥Zg|¢L…0ìºÞ2`å™oJûOÜ»áö±Ñᛣ0@±ñ_ý«Ÿ o1öÿí–q÷εgL:(«–æ½â¯æ½æK}™ž›Øx¤žTvW’Ò÷r»°âõy²xÑçè±#ú®íçs~áÞ•mk~kaÓ¼Ö¢Ÿ!ã¤ML-Žé¯ ™=£;þ}<úOzG_#rþ¯ÏæW>6§Ð™ùð•s麶lŠÄ”Q&Ä¢‰+||§H-vÙ|jËþcåƒ78Â9tÚ“¿ÖÒVð›|ç¼+®+¦#ªñ)B]Cƒ6m,‘ލÄÃH9Æ;ç^²rN~þ£¡Næjc"¹Bªsuç¹ïË{ŠZ2‰²„ÀK Œ0h«§/K¨4¬™yΊŒÌÜk•“7¬¸òªYùêIc)yðþù¯'0F⥬hðÕ“:EßeN~æÍXñ. ¦Go"Ol–Œ“â¥Ý%ûæοdW¿=µâ¥ q˜`k:rM/O¥®sTÂ\e´ÕX,ž…|!K6SãОG÷O²xE;Wüêz.Í#œ˜ÄƤ<‰\ê6 Ai§¢ ±VX4Zøx^ŠL&ãJ´²T&4¥ñ„j EG8$:A Ôa)ЩöX'X«Iƒëø¸Ògfw3«®ìbùÚ ^±F-˜Äžt™ŠÂ$9Ñx«÷MÅÕ¥Žp0F%š]­,9¿ƒž+28ÍeÂx€ZÕ"„ÄZ‹+¦&*Saý ·qØ7O×BsCSÖ#6šr8Î;nî„ì$Õx]±ˆéefÑxÒ¥+NNŽ|;6ú»àÀ؉÷ô|ÎX’Z2ɤ8ÌDm„0NPF“˜„XÇh£ñŸ†úF†*ŒëñÓŒýȽ·-Ì^zü;-]ÜctB=ÐÖœ®Á¤¤\Ÿ¬Ÿã…Á—Ç÷?¦~ãÇ_yñ?F'†_=ø…‡½ƒýÙN½óðýç,ï:§-ŸCB6êe¬ GìÅ5WÜŠŸ¾_-ÎÝ\Ejmr2“ý“Ü¢·w'ãä/¹†ÜyëÙnsnk'™žKH/]…žEz™ùKÏ{€kà]©–Žë…Öè †DR¼ü}$ýÇ0cØzoÖB ¿üÛØ8¤¼õû8QHª©‘Ê|xJ"ëÜt&câa áÔõýÛp;çRø¥›ð–­"Õ@HJ;Ÿ"ˆl5š©ÔåHK…0X¥ÉJp,”¼—Öëî »î½x³0öèÆ~ô¯d]Lck‚®†ˆ”Ž­M׫x®‹LgއîkÐhºß‘<ûr¥Aµ0Dú>fšRi{B' 6 ¨×ªh×Ãí¹˜Ìeïê„©ç@Û‡¿€; ÐQ€‰C”Ò%)õD=IŽ$Ñš¨R¦õö»qgÌæå‡¾Èѯü9[¾¿p%-¿s•r¤µ(ÞÙ8)ûÝjµ~c1]|·0šjiœ$¬3ôý‡9¶ñ~£8ô÷Ÿfîø(¹ù謂 Í9Ÿ ‰‰ãä±ñ¼Òq/[Üê¿-ëçËå¹p%õS'Q•ŒDÆ":æb•¡˜÷84Z¼TKÞ{óž pÎkøý¥…›V ïx ˜ÏæêÕ¼Æ1cÁ p3.¾çp¤¬¶lôW¼ÿ[?Þ6àüÖ-·0gÎlyˆ¦#c•0,DSç·ål6ãáyßwpÓ.©ŒKÝ vW½m›ZV¢Ö2ûDP«˜±±1ë Œ1"I’â í+7õÖgZ ¬_jF/Í;fž+E:ÒL &©^LÍþÉÀŒY»E&7‘ÅdS¾¿Ueü"Ïöw?æŸþáH‹F!Ö3]צÜâÛ¤pó±®õÖ¢ÑÍA\ÛäHÙkô« Ý?Oüê†n±$Z­i/trIçÚëg6-v ©\ǧM^0^¼±wôùÛ±A[õ)^mG¹¶kÞ½bÖ寞ÿ®vßSÄjŒXk‹ãøÌinbnËõ+ç·»aë‘]V‹'ÿØžq$#uÙò®õ/ZtU»±§¨½Äº†Ÿ–ø+ªÑ0Õè0s[;¸lÙo|Ô%÷Å0´rºõ¡òÅ_:oþÅmõøe"`…$I${vTynëÃÇi´˜§¢St49wÞå3Æ^;-"¯{ûìU=BÔItÔèˆ MuÊð¹?:ÀÇïx-ONàgÚhŒ±Ô£1u,¢)Óy'€Û”i¹±5W$Jj#H§Ó¤3.I((6¥1ZϧÉfsS¯Ehã{>³Zæ\ ়üù¾ï LB&›æÉï<÷ÌŽR:›Ólþ1ì­áù·|d6Ò‹Ñ6¡˜+æd³n³6ŠHE¤HÑ׳óéqòEŸBÑ'•‚¡þ„㇠űêFˆ%ÀÁiLq›r˜DÅHE8AÄœy-\¸V#]ËЩ*Z[ºçç)6eðS>Æ¢(Âw ' ‘ñruo=Šº¥„JµÂê«<.½¦•¸ÖÂ_êSåˆËy뮕TªõúJ<F&&98^~dp|Ò:²QzLÖ™¦B¯Aû(H]÷¶Ù«{ Q¯ÐÞP­(þò®ç¸ëŸeûÓ#xiù*íà »’“Ý Ú玛s⤎µ Ú§Ò:6›2XÓ }.›Ãj— ¡TŒï{t5ÏmÐÞwŠçû¾K¢²Ù4›Ÿ˜d÷ŽAG ¥C¡)ÃÖrhÏw¸éŽÅà$«(f›^¡½wšöiRô¿°kÛ0ùBŠ|¡Aû‘Á“Ç*ä i-±6Fk‹œÎU7NT9Œ£b¬bjAÄìîVV_¼Ç…á)´¶Ì™W XÌž¦}˜DxŽ%x…ö¥ÊÔÞà ÚŸ³¶…5WÎC…¾tÏn*S!ë®ZÌ%We™ªÖ ê%ÔôÇ £“Ó´©Œ=2\š´Ž”$ZQ®1R:A*¬vÑJ ‡±ò c}qí4íö5ho|õè΃/~jÖŒËNÓ^ ^ȯÝÖƒRš–‡zm¬lÀ:“áÅc' †§iÿã°6ØËÇ·¿tp*›J#§i?œ 3ó0…îc„ÎK„Q€±†l:ÃÀx‰mÏ÷~íä6síËWüàÐn{û“;÷MIá“;ƒöÕzƒöéTŠl:GïÀÿç éãËÏBûÁÌPÿPe²w¤o¦•¦AûL†´ŸB)›bçÞã;žÿŽ;¼ìÍh?ï{õƒõ-ϼ<¾Ü)­MùNƒöڔгKN¶mÏ‹ù¿Ú¿U ü_ùïÚÚyøÌ²¨ðIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000161 15051152474 032663 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.0100644 0000000 0000000 00000010516 15051152474 034377 0ustar00rootroot0000000 0000000 ‰PNG  IHDR77¨ÛÒFsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ‹P`õtEXtCommentZooKeeper Á±IDAThÞÕši´]E•€¿:ó½ço’—‘ 22 FTVÓ@ZZAh‹­ˆ²lYžHkƒ#¢‰ˆ4m¤[TT!@2½ä½ o¾ïÞwç{æêY¸3¼¨µÖ]÷Ç©½ë|gWí½kW þJmÊ1§*ÓçvÓ„cÞOTZøRJPUÒMS;wïܰº.•^½òŽ«äÁŽ!Ž4Ôäió…žjù·R|=A¥8„¦ÇP5MÕÐt ]s쉠¹µãéJ­zÖýÿq™0c©G.U×¼XŠØ#§‹A„ðòÌŸ9•]Û·24œÃ«èëÞÈ@ïîÉm“Ooœ0ÿg{^{&:б´# çX~JVj¡$V±Ç“‹MÕÆG?¿”ñϾŠZƒÍëï§½>Æ#«^Ʋ¬ÓêÚŽú póß¼å*™ûÚÛ§x;ÕigÕÌi@ÄÂé‚Úà+¼´~+sþñlþeéÇè)6ѵñ)ˆ‚JPý}¦çþŽš3»I—ò¹å/o>O~é’Se½‰„ý¿[v§¤ùÃÎôcNùÂßCy½é†XsKç5'ùüE¸¥fTé³cÓVV>µ‘Ÿï権³ŸÿÝŸ>åïnõŠë䪭½ôlÛÉÌy³øòågóÊ–=<¹êEÚ“t95.˜Ú‚O­8èwTþZp nßÛ»u;?¾ù z·íà¥63|’µ;z™7mZ¾À¸ŽÖ½‡2Æ_ nËž½KÏõí¥S\'¸r¸'û´?}Î×a°àQð"võ“Ò5æ¶´,KÑÓQu~òöZxdàª5ƒ„»ú;ç`Y±N¢ð›½ÝÒ–`e×ËL˜:¯\¡g× qE#£ªäb:ªgoŒ]îûÁ½W÷üicW[·bz~íŠÆÊÞ½Œ* öµÇ®=Yú=õõ—–ò.mëî)ñ\¹Ç×o`Ò”̘Ä!ù‘·0ïP©â».VÊœ«ézçÕ·/~Ø×œÜ±rf±äÜœ0ÔÍ­zaf×Ö=¬¨sÑ‚ó?<"ÄñÛÞIöã·žÛ4yzÇ©•|­³®.}l±X"ˆB"%¤®!A$#îêZK[S ›wïæ »hK¥ñLdk‚( !аS±‹‡sÙð™Ãj¹ò¾á3,;~¡3¾ªú…[²%lËZξ=R®]üvr_¼çòcZ&$>SÉ?äÔ¼c‡FÈ÷Q*åð½+m`ê²r׺?ñ‹¾ÈB…âà(ª®Q«U©• Ô—rÑE³•Åßxknéo_yíîž¡ïi^ÕJ²g Ãû›‰ ”æú-øþEbÒG7ý™[uÍ™¹|åÓ¦%/È—p#ìèŪC£•"–LùR É:,˜= án£»ÚŒ=ª5?¨QΆ’­­Mõ T«.Mãë¶$Í`É×/ýõða™–Úô°}®ZÏ ÇÏaÑ™¬[¿£§Œ'-Åì(ftÊ×î[þï7Ý?8p~ÓZççöån¦˜5?ò{1㉯_óäë:.ú÷³:TUý¥Å0L]­¡KdÄo×ð¤,b%,bq“∃™0>dacÅ bɱ„¶ìù‡7î<ì¹åүܳ«åÝ'MŸÀ‰WÞùÃ??øéþ?ëØÔÎþÂqOå·•»Ÿûñöî·Pab „Ž%C/DJøÈ¢8¿ýc£¨èºDà:-³1“ lÍ yBãsË>øý'Ç,qŽÇM:êãoív6»W~bU÷ÛÉ>pÛï»?uï‡÷8•ê$ÅŒC$ñj%üPEŠ¥*a,Œü'džÕÈD£­¹üývsò¿Æ4·´m5<àúèŸÛоbO~´H!Ÿ'—ÍS.; %+Ùž¡€Êh ß‘drçL½‘ËfÌe~Ñ ¯È˜á˜Âi©ª[>(°EŸ˜;ÅÔõ)‰ºzlÛÂ4 SÅŒ tK§©ÑD[¨Š@O[Ll¬ÇloÄëïêî|uù5SÆn?g™(AxPp-­©+m;>IK!ñ>µš&$uɤ'ÀŒiTŸ¡f‡á\™)–ŸŽ&r£ùÓ2ïkÀ%c³å±öWN¦5OhºœHEÕ4TMÃ0,ìXšT*I̲1õv2 èÄ #©sߎ- ª¤,›xº;a]<¶Õ/yàx_yèÊé–6AQ2’(RA¨U‡ó”ŠU¤ŒÀÀ«ø.¦"ßÜÜ…WeB]œ–ú$X|ïoî ¤Z¨uÖʦa‚!¾Ï¨›§·»ê%ºÖªŠÔ‚Jb) 9j]A¾<^ÎÑŠ»hOÜ1v–óCÄAÔPöt ïzÔò¦©‚ªà8·B1_&Ò|"Õ¼* Z››Ù2ÇwÕ]<Œçß4¾]¤>Ö?v–s<¬ÌpÃ;/›)×Md¤PÉy(ªÄ ‚ÀÙüª'S!Ù”¤œ-á…%ªåÒ%»ýÂ#“.¾mýØW¿¤$r½YûĦ‹Òã(¨ÄL›°`™jƒ‚R¨†úPÅš&(æªxÂE.žT1£¨­®Éœ¬û‘€èƒ¸ÝËjŠÄ©-ð —Á¾‚Àz\Û¤Šh–B€G€‡b Љ&cñÁ¬óƒò–Þz˺&{‰ª˜Ä“6q[§±Á&T$NÕ#’Eªøµˆ0ŒhhOaa`™qlÍÆð­“ÒKŽXݲàüê¬ñǾ—Þg\{̱†¦ÎÒ º†FêêЋXïè¦@ÕµŸG>H"¬D ˜Lœ4‰ºtŠÐ×èïË7ä?O¾øˆLË‚2ê„ïékN™2u‰_¤ÒÔ'S¤Ò6¹zJEJÐ4…¿ëÞ¡h Aà…†­³k{/ý;ûˆ(JˆR8c슮3”¯"Ñøž"‡âí+*ÔÊ ÛÆ÷C)#4}ÿ+–†øP­"t‰a„d³U„#q£Õ4ï{¸l–ŠçQqÞ=¿¼áÁËç«w³›…TC ÏuÑÔ4… ¢E²À´Ü DÒCX õSÓ¨® R£»EzÌá¼b™\ÕyO%õWþÐU×2)=¯¡-/¸Õ!ˆ"‚ÈG3¶?;¼@ÕT¢j€/5Â/ùEà!¦epÂùs–Hˆ'~²fØ­9Ïõpý=d)~-xÙuü»‡G²ôlï%—%7P Ó§2èP诒l0‹'^4caä›/K"¼"ªe‡Á]%ÏgtO‘áÁlzÆ¢ŽôaµÜ鳯­ŸRo¯ÈT"vö÷½´vGï¢ï1øâøS÷¯+]~û9ËÚ¢½ÉèêWG‹X 4¶ÛÄcÚŠüPey]{|0 ªeÓ4.Ú—©^ß í«·ä±M)#­¯N°<ž0›ÚÊèûÞ¹-™Ý!E UÁsÝÊÏZÒ¼áO$ú|1¬yÕ¡dÌäw=å¹ï¤cR}üœ® ÷ýv÷ŸžgÍ+=래½"4‡ï½â÷o¨/ú×y-«—ÝzÓÀ3k?=R*SóŠ?øzyÛ-_¿fø`âÜ»Þ š7±©Éõƒ%–¥ÛNÍ«oœx”±u[Ž lßZý0jmHÙ¡rðâÛé8{ª¼íÕ½³wïØÆo[Sÿèƒ=Ÿ{å7Ýo:*Ú»n¨Ò¿½ï¾K¯]·Ëe¾ýÓ§3+°öî1;Yf¶ªH)Ñt•¡L‘šWCG^iU6½“ŽqÎð˘٠ãÚqzG߲߅scŸízõ…¦'~ŽÜŽ.ú6/áڻƒC·ˆd„”ص•…ÛØÔ;L(^7¿|ÛòöãW5~ngÆ‹ºŠdG®ÆMˆŸtË“Õþ²ï†°¡%îñ£å÷¢£5{ýÙ3Ÿn{ÀÐü“ÂPAÓÄ ?YSùΊu• ®ikZ*ž÷cô÷ïcÚøz¡!äþkÕH¾åþÅ¥ÿ†òlÛ(Â% dJ2N:î?4ÿeÿsfÄØ–SÉf \v\Œ¾|îÔ–/˜B^7X¬4–œÈTªÐ6µY;ó­tP((«t3F$4„P°â›reˆ‚ýWÐ(¡4ßßn¼éŽVc,8w¸ê&ƒPÁ UÇbÔØ–¡éšS¾ø¦sÀ º~f“ÂE âhŠÂ¬VØØëÞ2\®Ž«ù‘©* Q¨Qq…Êw^ ï î™—_óǵ´|ð ÍBUUÜH€¢€DB!ÐM’É7gîn¨ÅšEÙ‡‚«’)EôºGöŒ8ö¼òôø -€QGaÔ“Ô\ƒ‚ëÙ7FαȻ‚W°;'è+„‡'ˆgžlih@³mÒIÃÔ1M #™ fDZ1tË^ö—r;3†@ªÔŸHüê ÁPå{Â|%Zgh )S‡(DU"Eb[)Ã'¥8Ôj’r$(F‡îP4E¬R ëé)ãN0L4kU‘(BG(±¸…ß:íä¯ûþß pÖ,ëÆ{^r)ùIü5'¢­^?õ VÂEOíŽÈæ\)‰kšæ"%š eWâx!š -¶zxàîyüeùÉó›–Ô

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.

./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000173 15051152474 032666 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source0100644 0000000 0000000 00000002337 15051152474 034427 0ustar00rootroot0000000 0000000 // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT 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); } }); ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000163 15051152474 032665 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/packag0100644 0000000 0000000 00000001617 15051152474 034415 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. copyright: Apache License v2.0 version: 0.1 description: ZooKeeper Browser name: ZooKeeper Browser sources: [Source/Zkui/Zkui.js] apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py0100644 0000000 0000000 00000011457 15051152474 033054 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000164 15051152474 032666 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/client0100644 0000000 0000000 00000002711 15051152474 034534 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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
Host Port Interest Ops Queued Received Sent
${client.host} ${client.port} ${client.interest_ops} ${client.queued} ${client.recved} ${client.sent}
% endif ${shared.footer()} ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000163 15051152474 032665 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create0100644 0000000 0000000 00000002233 15051152474 034520 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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()} ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000161 15051152474 032663 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.m0100644 0000000 0000000 00000002240 15051152474 034433 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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()} ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000162 15051152474 032664 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.0100644 0000000 0000000 00000003102 15051152474 034436 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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
Node Role Avg Latency Watch Count Version
${host} ${stats.get('zk_server_state', '')} ${stats.get('zk_avg_latency', '')} ${stats.get('zk_watch_count', '')} ${stats.get('zk_version', '')}


% endfor ${shared.footer()} ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000176 15051152474 032671 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared0100644 0000000 0000000 00000004077 15051152474 034533 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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()">
./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000161 15051152474 032663 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.m0100644 0000000 0000000 00000005335 15051152474 034455 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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
Key Value
${key}${znode[key]}

Details on stat information. ${shared.footer()} ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000161 15051152474 032663 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.m0100644 0000000 0000000 00000006446 15051152474 034474 0ustar00rootroot0000000 0000000 <%! # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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()} apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py0100644 0000000 0000000 00000002430 15051152474 032672 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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') ) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py0100644 0000000 0000000 00000002140 15051152474 033043 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py0100644 0000000 0000000 00000011150 15051152474 033041 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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)) ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-huebrowser_zkui_src_zkui_0100644 0000000 0000000 00000000156 15051152474 032667 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py0100644 0000000 0000000 00000002011 15051152474 034602 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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') apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/README.md0100644 0000000 0000000 00000010265 15051152474 027502 0ustar00rootroot0000000 0000000 # 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 `mvn clean install dependency:copy-dependencies` in zookeeper-contrib/zookeeper-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 zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources. 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.4 - 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))) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/pom.xml0100755 0000000 0000000 00000010720 15051152474 027537 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-contrib 3.9.4 zookeeper-contrib-loggraph jar Apache ZooKeeper - Contrib - Loggraph LogGraph is an application for viewing and filtering zookeeper logs. It can handle transaction logs and message logs. org.apache.zookeeper zookeeper-jute ${project.version} org.apache.zookeeper zookeeper ${project.version} com.fasterxml.jackson.core jackson-databind org.slf4j slf4j-api ch.qos.logback logback-core * * org.eclipse.jetty jetty-server org.eclipse.jetty jetty-servlet org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.mockito mockito-core test ${project.basedir}/src/main/resources/webapp org.apache.maven.plugins maven-dependency-plugin ${project.basedir}/lib org.apache.maven.plugins maven-surefire-plugin **/*Test.java ${surefire-forkcount} false -Xmx512m -Dtest.junit.threads=${surefire-forkcount} -Dzookeeper.junit.threadid=${surefire.forkNumber} ${project.basedir} true ${project.build.directory}/surefire super:D/InIHSb7yEEbrWz8b9l71RjZJU= ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000213 15051152474 032541 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000001663 15051152474 034270 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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); } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000204 15051152474 032541 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004043 15051152474 034263 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 java.util.List; import org.apache.zookeeper.graph.filterops.*; public abstract class FilterOp { protected List subOps; protected List 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 + ")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000210 15051152474 032536 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000007201 15051152474 034262 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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()); } } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000211 15051152474 032537 xustar000000000 0000000 137 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000017055 15051152474 034272 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class JsonGenerator { private final ObjectMapper mapper = new ObjectMapper(); private final JsonNode root; private final Set 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 JsonNode txnEntry(TransactionEntry e) { JsonNode event = mapper.createObjectNode(); ((ObjectNode) event).put("time", Long.toString(e.getTimestamp())); ((ObjectNode) event).put("client", Long.toHexString(e.getClientId())); ((ObjectNode) event).put("cxid", Long.toHexString(e.getCxid())); ((ObjectNode) event).put("zxid", Long.toHexString(e.getZxid())); ((ObjectNode) event).put("op", e.getOp()); ((ObjectNode) event).put("extra", e.getExtra()); ((ObjectNode) 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 = mapper.createObjectNode(); Matcher m = null; ArrayNode events = mapper.createArrayNode(); ((ObjectNode)root).set("events", events); long starttime = Long.MAX_VALUE; long endtime = 0; int leader = 0; long curEpoch = 0; 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()) { JsonNode stateChange = add("stateChange", e.getTimestamp(), e.getNode(), 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) { JsonNode stateChange = add("stateChange", e.getTimestamp(), leader, "INIT"); events.add(stateChange); leader = 0; } while (iterator.hasNext()) { int dst = iterator.next(); if (dst != e.getNode()) { JsonNode msg = mapper.createObjectNode(); ((ObjectNode)msg).put("type", "postmessage"); ((ObjectNode)msg).put("src", e.getNode()); ((ObjectNode)msg).put("dst", dst); ((ObjectNode)msg).put("time", e.getTimestamp()); ((ObjectNode)msg).put("zxid", m.group(2)); ((ObjectNode)msg).put("count", count); ((ObjectNode)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) { JsonNode stateChange = add("stateChange", e.getTimestamp(), leader, "INIT"); events.add(stateChange); leader = 0; } if (src != dst) { JsonNode msg = mapper.createObjectNode(); ((ObjectNode)msg).put("type", "delivermessage"); ((ObjectNode)msg).put("src", src); ((ObjectNode)msg).put("dst", dst); ((ObjectNode)msg).put("time", e.getTimestamp()); ((ObjectNode)msg).put("zxid", zxid); ((ObjectNode)msg).put("count", count); ((ObjectNode)msg).put("epoch", epoch); ((ObjectNode)msg).put("epoch2", epoch2); events.add(msg); } } else if ((m = exceptionP.matcher(e.getEntry())).find()) { JsonNode ex = mapper.createObjectNode(); ((ObjectNode)ex).put("type", "exception"); ((ObjectNode)ex).put("time", e.getTimestamp()); ((ObjectNode)ex).put("server", e.getNode()); ((ObjectNode)ex).put("text", e.getEntry()); events.add(ex); } } JsonNode ex = mapper.createObjectNode(); ((ObjectNode)ex).put("type", "text"); ((ObjectNode)ex).put("time", ent.getTimestamp()); String txt = ent.toString(); ((ObjectNode)ex).put("text", txt); events.add(ex); } // System.out.println("pending messages: "+pendingMessages.size()); ((ObjectNode)root).put("starttime", starttime); ((ObjectNode)root).put("endtime", endtime); ArrayNode serversarray = mapper.createArrayNode(); ((ObjectNode)root).set("servers", serversarray); Iterator iterator = servers.iterator(); while (iterator.hasNext()) { serversarray.add(iterator.next()); } } private JsonNode add(String type, long timestamp, int node, String entry){ JsonNode stateChange = mapper.createObjectNode(); ((ObjectNode)stateChange).put("type", type); ((ObjectNode)stateChange).put("time", timestamp); ((ObjectNode)stateChange).put("server", node); ((ObjectNode)stateChange).put("state", entry); return stateChange; } public String toString() { String jsonString = null; try { jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(root); } catch (JsonProcessingException e) { jsonString = "{\"ERR\", " + e.getMessage() + "}"; } return jsonString; } public static void main(String[] args) throws Exception { MergedLogSource src = new MergedLogSource(args); LogIterator iter = src.iterator(); System.out.println(new JsonGenerator(iter)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000206 15051152474 032543 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002544 15051152474 034267 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000207 15051152474 032544 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JSource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000025550 15051152474 034271 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; public class Log4JSource implements LogSource { private static final Logger LOG = LoggerFactory.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 log 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 log 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); try { iter.close(); } catch (IOException ioe) { System.out.println(ioe.getMessage()); } }; }; Thread t2 = new Thread() { public void run () { LogIterator iter = s.iterator(starttime, endtime); System.out.println(iter); try { iter.close(); } catch (IOException ioe) { System.out.println(ioe.getMessage()); } }; }; Thread t3 = new Thread() { public void run () { 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000204 15051152474 032541 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogEntry.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002717 15051152474 034271 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; import java.util.Map; public abstract class LogEntry implements Serializable { private Map 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); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000207 15051152474 032544 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogIterator.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002013 15051152474 034256 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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;; }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000205 15051152474 032542 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogServer.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004322 15051152474 034263 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000207 15051152474 032544 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogSkipList.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000005271 15051152474 034267 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; /** 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 = LoggerFactory.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; } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000205 15051152474 032542 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/LogSource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002536 15051152474 034270 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000215 15051152474 032543 xustar000000000 0000000 141 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MeasureThroughput.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000006172 15051152474 034270 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; import java.util.Set; 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; Set 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(); } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000213 15051152474 032541 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/MergedLogSource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000013030 15051152474 034257 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MergedLogSource implements LogSource { private static final Logger LOG = LoggerFactory.getLogger(MergedLogSource.class); protected List sources = new ArrayList<>(); 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 { List iters = new ArrayList<>(); 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.clear(); 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()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000222 15051152474 032541 xustar000000000 0000000 146 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/RandomAccessFileReader.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000020501 15051152474 034260 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; public class RandomAccessFileReader extends Reader implements DataInput { private static final Logger LOG = LoggerFactory.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()); f.close(); } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000214 15051152474 032542 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TransactionEntry.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004005 15051152474 034261 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000210 15051152474 032536 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/TxnLogSource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000026405 15051152474 034271 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.TxnLogEntry; 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.slf4j.Logger; import org.slf4j.LoggerFactory; public class TxnLogSource implements LogSource { private static final Logger LOG = LoggerFactory.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()); } TxnLogEntry logEntry = SerializeUtils.deserializeTxn(bytes); TxnHeader hdr = logEntry.getHeader(); Record r = logEntry.getTxn(); 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."); } TxnLogEntry logEntry = SerializeUtils.deserializeTxn(bytes); TxnHeader hdr = logEntry.getHeader(); Record r = logEntry.getTxn(); 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000213 15051152474 032541 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/AndOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002267 15051152474 034271 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000211 15051152474 032537 xustar000000000 0000000 137 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/Arg.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002271 15051152474 034264 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 + "]"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000216 15051152474 032544 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/EqualsOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002612 15051152474 034263 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000223 15051152474 032542 xustar000000000 0000000 147 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004515 15051152474 034267 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000220 15051152474 032537 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/LessThanOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004344 15051152474 034267 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000213 15051152474 032541 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/NotOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002345 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000217 15051152474 032545 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/NumberArg.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002012 15051152474 034255 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000212 15051152474 032540 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/OrOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002271 15051152474 034264 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000217 15051152474 032545 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/StringArg.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002022 15051152474 034256 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000217 15051152474 032545 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/SymbolArg.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002015 15051152474 034260 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000213 15051152474 032541 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/filterops/XorOp.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002431 15051152474 034262 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000217 15051152474 032545 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/FileLoader.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000003463 15051152474 034270 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; 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 file = request.getString("path", "/"); ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.createObjectNode(); try { this.source.addSource(file); ((ObjectNode) rootNode).put("status", "OK"); } catch (Exception e) { ((ObjectNode) rootNode).put("status", "ERR"); ((ObjectNode) rootNode).put("error", e.toString()); } String jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(rootNode); return jsonString; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000207 15051152474 032544 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Fs.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004730 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.FileNotFoundException; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; public class Fs extends JsonServlet { String handleRequest(JsonRequest request) throws Exception { 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()); } }); String jsonString = generateJSON(files); return jsonString; } protected static String generateJSON(File[] files) throws IOException { ObjectMapper mapper = new ObjectMapper(); ArrayNode fileList = mapper.createArrayNode(); for (File f : files) { JsonNode node = mapper.createObjectNode().objectNode(); ((ObjectNode) node).put("file", f.getName()); ((ObjectNode) node).put("type", f.isDirectory() ? "D" : "F"); ((ObjectNode) node).put("path", f.getCanonicalPath()); fileList.add(node); } String jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(fileList); return jsonString; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000216 15051152474 032544 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/GraphData.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004173 15051152474 034267 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 org.apache.zookeeper.graph.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GraphData extends JsonServlet { private static final Logger LOG = LoggerFactory.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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000220 15051152474 032537 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/JsonServlet.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000006305 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; 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 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) { ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.createObjectNode(); ((ObjectNode) rootNode).put("error", e.toString()); String jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(rootNode); response.getWriter().println(jsonString); } catch (java.lang.OutOfMemoryError oom) { ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.createObjectNode(); ((ObjectNode) rootNode).put("error", "Out of memory. Perhaps you've requested too many logs. Try narrowing you're filter criteria."); String jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(rootNode); response.getWriter().println(jsonString); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000216 15051152474 032544 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/NumEvents.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000005003 15051152474 034260 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.zookeeper.graph.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NumEvents extends JsonServlet { private static final Logger LOG = LoggerFactory.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(); } } long size = 0; LogIterator iter = source.iterator(starttime, endtime); size = iter.size(); ObjectMapper mapper = new ObjectMapper(); JsonNode data = mapper.createObjectNode(); ((ObjectNode) data).put("startTime", starttime); ((ObjectNode) data).put("endTime", endtime); ((ObjectNode) data).put("numEntries", iter.size()); if (LOG.isDebugEnabled()) { LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", numEntries=" + size +")"); } iter.close(); String jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(data); return jsonString; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000222 15051152474 032541 xustar000000000 0000000 146 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/StaticContent.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000003630 15051152474 034264 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } try { while (resource.available() > 0) { response.getWriter().write(resource.read()); } } finally { resource.close(); } // response.setContentType("text/plain;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_java_or0100644 0000000 0000000 00000000217 15051152474 032545 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/servlets/Throughput.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000007066 15051152474 034273 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.util.HashSet; import java.util.Set; import com.fasterxml.jackson.core.util.MinimalPrettyPrinter; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.zookeeper.graph.*; 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); String jsonString = getJSON(iter, scale); iter.close(); return jsonString; } protected String getJSON(final LogIterator iter, final long scale) throws IOException { long current = 0; long currentms = 0; Set zxids_ms = new HashSet(); long zxidCount = 0; ObjectMapper mapper = new ObjectMapper(); ArrayNode events = mapper.createArrayNode(); 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) { JsonNode node = mapper.createObjectNode(); ((ObjectNode) node).put("time", current * scale); ((ObjectNode) node).put("count", zxidCount); events.add(node); zxidCount = 0; } current = inscale; currentms = ms; zxids_ms.add(cxn.getZxid()); } JsonNode node = mapper.createObjectNode(); ((ObjectNode) node).put("time", current * scale); ((ObjectNode) node).put("count", zxidCount); events.add(node); String jsonString = mapper.writer(new MinimalPrettyPrinter()).writeValueAsString(events); return jsonString; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000160 15051152474 032603 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/loggraph-dev.sh apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/loggraph-dev.0100755 0000000 0000000 00000002635 15051152474 034335 0ustar00rootroot0000000 0000000 #!/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/../../../target/` LIBDIR=`make_canonical $BUILDDIR/../lib` WEBDIR=`make_canonical $SCRIPTDIR/../web` 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 CLASSPATH=$BUILDDIR/classes:$WEBDIR:$CLASSPATH echo $CLASSPATH java -Dlogback.configurationFile=org/apache/zookeeper/graph/logback.xml -Xdebug -Xrunjdwp:transport=dt_socket,address=4444,server=y,suspend=n -cp $CLASSPATH org.apache.zookeeper.graph.LogServer $* apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/loggraph.sh0100755 0000000 0000000 00000002364 15051152474 034113 0ustar00rootroot0000000 0000000 #!/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/../../../target/` LIBDIR=`make_canonical $BUILDDIR/../lib` 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 java -cp $CLASSPATH org.apache.zookeeper.graph.LogServer $* ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000216 15051152474 032605 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/logback.xml apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000002640 15051152474 034336 0ustar00rootroot0000000 0000000 %d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n TRACE ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000233 15051152474 032604 xustar000000000 0000000 155 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/date.format.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000007455 15051152474 034347 0ustar00rootroot0000000 0000000 /* * 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); }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000225 15051152474 032605 xustar000000000 0000000 149 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.bar.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000032372 15051152474 034343 0ustar00rootroot0000000 0000000 /* * 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; }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000225 15051152474 032605 xustar000000000 0000000 149 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.dot.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000011364 15051152474 034341 0ustar00rootroot0000000 0000000 /* * 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; }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000226 15051152474 032606 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.line.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000022100 15051152474 034327 0ustar00rootroot0000000 0000000 /* * 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; }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000225 15051152474 032605 xustar000000000 0000000 149 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.pie.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000017552 15051152474 034346 0ustar00rootroot0000000 0000000 /* * 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; }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000231 15051152474 032602 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/g.raphael.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000054244 15051152474 034345 0ustar00rootroot0000000 0000000 /* * 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; } }; })();./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000231 15051152474 032602 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load-big.gif apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000003604 15051152474 034337 0ustar00rootroot0000000 0000000 GIF89a67óÿÿÿxxxØØØTTTÜÜÜÄÄÄHHHŠŠŠ!ÿ NETSCAPE2.0!þCreated with ajaxload.info!ù ,67ÌÈI«½8ëÍ»ÿ`(Ždiž%’D‚ §Â΄“M¿w¸ê¬D/¤ AÅÙñó —\‘w,X†F¦³- Ú@{Q©Á` ]u“€Ö&MáÙ@”Ä‹Eo4q%s3u y,'jl$oP–—˜™š›œž‡–¡…,¢G¦¨Š{–®°²—µª¬=¹¥kºC¤ŸÂÃÄÅÆÇÈ›Á޾$¼rt#·%Ó ÕÒ"Ï„Ñ#ËrÍÉžßÅÛÅ×ÃèÂæåáâï=!ù ,67ÎÈI«½8ëÍ»ÿ`(ŽdižhªA…Zƒë±,¶¶kè¢^Úˆ ^ï‡ôj¶Áá‚H¸ˆ¦äÀ3L- £b$ &„Ñ• zh’uAo¥õt' òz!o6q#sFuƒf†#jkmZaBc’UW‘—œžŸ ¡nŒ—eg„.@©«}M±´³=²­¯2»¨¤’¦¼¢ÄÅÆÇÈÉÊ'Â)ÍdpÌÑ"¶%Õ|¸'×¾&Ý"ÏÌÀËžáÇ߯ÛÄê¢èÆæäñ'!ù ,67ÎÈI«½8ëÍ»ÿ`(Ždižhª®¬0 [ DMIJ8ØÄë<ßD³á† ™+j†Iy$v¶4T¬UEOÁ̚ĶrMHB×DÝ›)Ì#Ã__Ñ»>0;a~…†‡ˆ‰Š 5 ‹ h TCn‘qy)Žn |=?›o¦l,©Ÿ?¢h¤±x™›’”b–’»ÀÅÆÇÈɘ(Ìp< DÐ#¬'Õj§&×\Ó&²#Î޸ʈáÅ߯ÛÅêÀèÆæäñ&!ù ,67ÍÈI«½8ëÍ»ÿ`(Ždižhª®¬0 [ DMIJ8ØÄë<ßD³á† É4)Î”È›ŠŠ5kgg–¸5/Ü;‘ÅÉê {#=Sï¦|N¯Ûïø¼ª`¨ q?g€lZ)}<gCŒ6A‘’ˆC‹<Ž™RHgz‹¤¥«¬­®' 5 ¬ ’ #‚#—µTF%²— !$¾5Ç•ÉÊ!š$Ä’ÆÐ %½—Ày·Œ¹H»±³Û?Ñ«ÈèÍ«çìׯð+!ù ,67ËÈI«½8ëÍ»ÿ`(Ždižhª®¬0 [ DMIJ8ØÄë<ßD³á† É4)Î”È›ŠŠ5kgg–¸5/Ü;‘ÅÉê {#=Sï¦|N¯Ûïø<>Žä_ØZ*€gH…‡?‰ƒC~?‘z”•–—˜™L5nR'g !#<@]&«6®a°±Wj%ª<­W¢ 5 gC ± ƪÉ?¶Åy¶ zÔ5ÛÜzرÚyÓ¶Öy˫͕ÁÃç"!ù ,67ÌÈI«½8ëÍ»ÿ`(Ždižhª®¬0 [ DMIJ8ØÄë<ßD³á† É4)Î”È›ŠŠ5kgg–¸5/Ü;‘ÅÉê {#=Sï¦|N¯Ûïø<>Žä_ØZ*€gH…‡?‰ƒC~?‘z”•–—˜ 5 – <5 ”¢6Ÿ3R"œ§ 3j ®6_A!´5¶]!­§°n²¦®©n«"¡¢¤5H›ÆgҔϢ•´ß®•Ü<Þ”g–Üê™(!ù ,67ÌÈI«½8ëÍ»ÿ`(Ždižhª®¬0 [ DMIJ8ØÄë<ßD³á† É4)Î”È›ŠŠ5kgg–¸5/Ü;‘ÅÉê {#=Sï¦|N¯Ûï"D¢–@à+ <5 ‚6~?q{ˆ ‹j6?g•5—AŽˆ‘9lZ‡Š¢R‚„…†Ž}®²³´µL5³g¼QK"¹‚N“ •&˜!Ê_!Ä<Æ3Ègk©#ÄÚ¶Œ²£´Ì²è®æ´äâï'!ù ,67ÌÈI«½8ëÍ»ÿ`(Ždižhª®¬0 [ DMIJ8ØÄë<ßD³á~ˆD-и`C€‚WSD9ª­9{?J-!1ã};b[ig~Òµu0Ö’IÅÚ›æâ½#STVWXaL…‰Š‹ŒW5Žl5”CO{‘TœfiC˜=¥b§s <¢?y7¨Q ¹•ÁÂÃÄÅÆÇ»(Ê"³žD¤@m'·#Õ&×!Î'Û#Ì&ßȉáÂÝÄÙÃèåÑÅäâï%;./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000225 15051152474 032605 xustar000000000 0000000 149 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/load.gif apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000001241 15051152474 034332 0ustar00rootroot0000000 0000000 GIF89aòÿÿÿÂÂÂBBBbbb‚‚‚’’’!ÿ NETSCAPE2.0!þCreated with ajaxload.info!ù ,3ºÜþ0ÊIkc:œN˜f E±1º™Á¶.`ÄÂqÐ-[9ݦ9 JkçH!ù ,4ºÜþNŒ! „ »°æŠDqBQT`1 `LE[¨|µußía€ ×â†C²%$*!ù ,6º2#+ÊAÈÌ”V/…côNñIBa˜«pð ̳½ƨ+YíüƒÃ2©dŸ¿!ù ,3ºb%+Ê2†‘ìœV_…‹¦ …! 1D‡aªF‚°ÑbR]ó=08,Ȥr9L!ù ,2ºr'+JçdðóL &vÃ`\bT”…„¹hYB)ÏÊ@é<Ã&,ȤR’!ù ,3º Â9ãtç¼Úž0Çà!.B¶ÊW¬¢1  sa»°5÷•0° ‰»Ÿm)J!ù ,2ºÜþð ÙœU]šîÚqp•`ˆÝaœÝ4–…AFÅ0`›¶ Â@›1€ÂÖΑ!ù ,2ºÜþ0ÊI«eBÔœ)×à ŽÇq10©Ê°®PÂaVÚ¥ ub‚ž[;./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000231 15051152474 032602 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.css apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000005066 15051152474 034343 0ustar00rootroot0000000 0000000 /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 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; }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000230 15051152474 032601 xustar000000000 0000000 152 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000017752 15051152474 034350 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); }); }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000234 15051152474 032605 xustar000000000 0000000 156 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.log.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000003415 15051152474 034337 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000237 15051152474 032610 xustar000000000 0000000 159 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.server.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000022323 15051152474 034336 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); }); } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000240 15051152474 032602 xustar000000000 0000000 160 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.session.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000013076 15051152474 034343 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); } }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000236 15051152474 032607 xustar000000000 0000000 158 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.stats.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000003575 15051152474 034346 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); }; ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000233 15051152474 032604 xustar000000000 0000000 155 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/loggraph.ui.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000026310 15051152474 034336 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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); }); }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000226 15051152474 032606 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/main.html apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000004212 15051152474 034333 0ustar00rootroot0000000 0000000
Edit Filters Add logs
Log view Servers view Sessions view Statistics
./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000227 15051152474 032607 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/raphael.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000414206 15051152474 034343 0ustar00rootroot0000000 0000000 /*! * 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; })();./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_main_resourc0100644 0000000 0000000 00000000227 15051152474 032607 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/apache/zookeeper/graph/resources/yui-min.js apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources/webapp/org/ap0100644 0000000 0000000 00000041576 15051152474 034351 0ustar00rootroot0000000 0000000 /* 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"]});./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_test_java_or0100644 0000000 0000000 00000000223 15051152474 032575 xustar000000000 0000000 147 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeeper/graph/servlets/FileLoaderTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeep0100644 0000000 0000000 00000010414 15051152474 034315 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.apache.zookeeper.graph.FilterException; import org.apache.zookeeper.graph.FilterOp; import org.apache.zookeeper.graph.LogIterator; import org.apache.zookeeper.graph.LogSource; import org.apache.zookeeper.graph.MergedLogSource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; import java.io.IOException; import java.util.List; public class FileLoaderTest { @Test public void testHandleRequestOK() throws Exception { String[] files = {""}; MyMergedLogSource mls = new MyMergedLogSource(files); final JsonServlet.JsonRequest jsonRequest = mock(JsonServlet.JsonRequest.class); when(jsonRequest.getString("path", "/")).thenReturn("/tmp"); FileLoader fl = new FileLoader(mls); String s = fl.handleRequest(jsonRequest); Assertions.assertEquals("{\"status\":\"OK\"}", s); Assertions.assertTrue(mls.getSources().contains(new MySource("/tmp"))); Assertions.assertFalse(mls.getSources().contains(new MySource("/tmp2"))); } @Test public void testHandleRequestERR() throws Exception { String[] files = {""}; MyMergedLogSource mls = new MyMergedLogSource(files); final JsonServlet.JsonRequest jsonRequest = mock(JsonServlet.JsonRequest.class); when(jsonRequest.getString("path", "/")).thenReturn("/tmp3"); FileLoader fl = new FileLoader(mls); String s = fl.handleRequest(jsonRequest); Assertions.assertEquals("{\"status\":\"ERR\",\"error\":\"java.io.IOException: Message\"}", s); Assertions.assertFalse(mls.getSources().contains(new MySource("/tmp"))); Assertions.assertFalse(mls.getSources().contains(new MySource("/tmp2"))); } private class MyMergedLogSource extends MergedLogSource { public MyMergedLogSource(String[] files) throws IOException { super(files); } public void addSource(String f) throws IOException { if ("/tmp3".equals(f)) throw new IOException("Message"); sources.add(new MySource(f)); } public List getSources(){ return sources; } } private class MySource implements LogSource{ private String file = null; public MySource(String file) throws IOException { this.file=file; } public boolean equals(Object o){ if(!(o instanceof MySource)) return false; if(((MySource)o).file == null) return this.file==null; return ((MySource)o).file.equals(this.file); } @Override public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException { return null; } @Override public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException { return null; } @Override public LogIterator iterator() throws IllegalArgumentException { return null; } @Override public boolean overlapsRange(long starttime, long endtime) { return false; } @Override public long size() { return 0; } @Override public long getStartTime() { return 0; } @Override public long getEndTime() { return 0; } } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_test_java_or0100644 0000000 0000000 00000000213 15051152474 032574 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeeper/graph/servlets/FsTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeep0100644 0000000 0000000 00000003641 15051152474 034321 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; public class FsTest { @Test public void testGenerateJSON() throws IOException { File[] files = new File[2]; final File file1 = mock(File.class); when(file1.getName()).thenReturn("testDir"); when(file1.isDirectory()).thenReturn(true); when(file1.getCanonicalPath()).thenReturn("/tmp/testDir"); final File file2 = mock(File.class); when(file2.getName()).thenReturn("test"); when(file2.isDirectory()).thenReturn(false); when(file2.getCanonicalPath()).thenReturn("/tmp/test"); files[0]=file1; files[1]=file2; String output = Fs.generateJSON(files); String expectedOutput = "[{\"file\":\"testDir\",\"type\":\"D\",\"path\":\"/tmp/testDir\"}," + "{\"file\":\"test\",\"type\":\"F\",\"path\":\"/tmp/test\"}]"; Assertions.assertEquals(expectedOutput, output); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-loggraph_src_test_java_or0100644 0000000 0000000 00000000223 15051152474 032575 xustar000000000 0000000 147 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeeper/graph/servlets/ThroughputTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-loggraph/src/test/java/org/apache/zookeep0100644 0000000 0000000 00000006216 15051152474 034322 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 static org.mockito.Mockito.mock; import org.apache.zookeeper.graph.FilterException; import org.apache.zookeeper.graph.Log4JEntry; import org.apache.zookeeper.graph.Log4JSource; import org.apache.zookeeper.graph.LogEntry; import org.apache.zookeeper.graph.LogIterator; import org.apache.zookeeper.graph.TransactionEntry; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; public class ThroughputTest { @Test public void testGetJSON() throws Exception { long scale = 1; Log4JSource source = mock(Log4JSource.class); Throughput tp = new Throughput(source); LogIterator iter = new MyIterator(); String jsonString = tp.getJSON(iter, scale); String expected = "[{\"time\":3,\"count\":1},{\"time\":4,\"count\":1},{\"time\":5,\"count\":0}]"; Assertions.assertEquals(expected, jsonString); } private class MyIterator implements LogIterator { int index = 0; List list = new ArrayList<>(); public MyIterator() throws IllegalArgumentException, FilterException { for(int i=1; i<3; i++){ long timestamp = i; int node = i; String entry = Integer.toString(i); Log4JEntry le = new Log4JEntry(timestamp, node, entry); list.add(le); } for(int i=3; i<7; i++){ long timestamp = i; long clientId = i; long Cxid = i; long Zxid = i; String op = Integer.toString(i); TransactionEntry te = new TransactionEntry(timestamp, clientId, Cxid, Zxid, op); list.add(te); } } synchronized public long size() throws IOException { return list.size(); } public boolean hasNext() { return index" -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://zookeeper.apache.org/docs/current/zookeeperAdmin.html#sc_zkCommands apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/cacti/README0100644 0000000 0000000 00000005056 15051152474 030552 0ustar00rootroot0000000 0000000 Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/check_zookeeper.py0100755 0000000 0000000 00000031401 15051152474 032315 0ustar00rootroot0000000 0000000 #! /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) if not values: # Zookeeper may be down, not serving requests or we may have a bad configuration print 'Critical, %s not found' % opts.key return 2 values = ' '.join(values) if critical_state: print 'Critical "%s" %s!|%s' % (opts.key, ', '.join(critical_state), values) 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') stat = self._parse_stat(self._send_cmd('stat')) if data: mntr = self._parse(data) missing = ['zk_zxid', 'zk_zxid_counter', 'zk_zxid_epoch'] for m in missing: if m in stat: mntr[m] = stat[m] return mntr else: return stat 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('Alive connections: (\d+)', line) if m is not None: result['zk_num_alive_connections'] = 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 m = re.match('Watch count: (\d+)', line) if m is not None: result['zk_watch_count'] = int(m.group(1)) continue m = re.match('Ephemerals count: (\d+)', line) if m is not None: result['zk_ephemerals_count'] = int(m.group(1)) continue m = re.match('Approximate data size: (\d+)', line) if m is not None: result['zk_approximate_data_size'] = int(m.group(1)) continue m = re.match('Open file descriptor count: (\d+)', line) if m is not None: result['zk_open_file_descriptor_count'] = int(m.group(1)) continue m = re.match('Max file descriptor count: (\d+)', line) if m is not None: result['zk_max_file_descriptor_count'] = int(m.group(1)) continue m = re.match('Zxid: (0x[0-9a-fA-F]+)', line) if m is not None: result['zk_zxid'] = m.group(1) result['zk_zxid_counter'] = int(m.group(1), 16) & int('0xffffffff', 16) # lower 32 bits result['zk_zxid_epoch'] = int(m.group(1), 16) >>32 # high 32 bits continue m = re.match('Proposal sizes last/min/max: (\d+)/(\d+)/(\d+)', line) if m is not None: result['zk_last_proposal_size'] = int(m.group(1)) result['zk_min_proposal_size'] = int(m.group(2)) result['zk_max_proposal_size'] = int(m.group(3)) 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') for typ in [int, float]: try: value = typ(value) break 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()) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/README0100644 0000000 0000000 00000004156 15051152474 031071 0ustar00rootroot0000000 0000000 Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 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. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/ganglia/Screenshot.png0100644 0000000 0000000 00000330717 15051152474 033041 0ustar00rootroot0000000 0000000 ‰PNG  IHDRø™ * ¼sRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚYù IDATxÚìwXTÇþÿ‡¦ , [E@@¤E  RTÔx%{L,`¢¢^5(Ø5Š%ì^ýùÕpÕ¨X¢bÄ IkÄ„ªt¤7EÊï7›m,˲õýz|öYçœ3gÎ{ϼ™93ó9mmmP4!ttE[ÆçKÏÈè >Øõ·”ñ1¢ @¥@G:º:º:º:ºÐÑÐÑÐÑÐÑí8ì?ú»ºô`çœø?³xWtÿØþŸß4¿ÿ[cÎÍSì¸mxÖ v&M¯~4 ÁèôþxËö‡^‹V]tþÏÌÞZøaèè‚vÐв6ê'iæØV•°ÿx¦}è§6ÝÿJÑd|¼ïä$-RyKØ]È5ñ“)t Át™óó£½Çžù‡é Ð. 1u¹%ÿÔ—ÁNC; ì4ØÙj€•㜟*Û!M…±{—N5zÜèqî®Ó¾úîÁ›w\‡ŠÞú÷J¢çº9Y °³è2ò«û5„BØù1»}<Ü}¬û7÷€5ÿyRÝú~›Ø˜4ÜÙj€Ãâ¯í]8n˜ƒÕ‡aŸn¸”ÙÐ&òÀ悳Q“r§Ÿtkë¿Ü¬ØY9Ìý¹ö…äÚí‡'w¶}âjoe?ì£5QÖ7ÜÙ<ÍÕÞjÀ qËΤ¾åœMx9Ç~~ã-)81cÈÁN#¦ì¼{B`b2~œí6Èf€ÕôKoZ9yþ¼{ñÄánîãF÷œöÅÖs+Z!m —Âg3vô„q£ÆM \w4¾¤E@?·&åüƒ·cÓÿÕÐÒêÄ€±No·‘ŒÒÛ?¦6 ¾”¥£KѺåvJêã'©ýï»ILBú¹f¬‘FKY̪é‹ÿOwÑ¥{qbï l>±pzhB%Õ)½•-ÆØ 9÷÷û6!åé¯ß1 ¤¥ôvÈ'Áç´¿¼ÿ¿¸;ßÉÜ6kÁ±Ì&"|Sw‡¯¯ÿïÇྤ!n÷Y­?$¦f<øÏÌæW~ósy«ˆµM§þù⺄¼þaóOÆ_¾v}ÿLs]Þ¾ßß»åü¸7É:äÔµ‹[ÆTDoürãæM Ö!§®]ÚæÝ³mÅÿ½¢:ó¢ÊrbOÒgá…ÇŸ¤þvuíø™  iúÉÙ?;ˆë‰@éíéKÐ[|9.áqñ1ߎÎ;÷퉗„4>ß·ðß¿ ;p+îÁÏq 烙 ÇΧ7òÿ’ì׉©lmó ñýæÂ;¾˜ø‘÷è±ãfï¼–V÷Ÿ°›™£)©|œò uÊÒÑÕ0øÙš&!¤¥,6lÕ¥2ÇÐCƒýÇ©í×ʾZéÛ§!Ýûù¯Ybûæ¿ÛÏe½#„ˆÞÊEkõËֿžqbç‹nuÇþ8µóF•í’Õ-º¢©?hîšI=~ÿ>òa­¨Mï1ó߸ÈÕ´›¦käÒ°YfU7ö\Émq VO“>½º„°Y_„/örîïì½lï–¹öº<Ýñ÷»½cÍ]ù©«½Ý°©Á ú“Ü;ÅSBÝìí>œºla’÷¢ºMô%ðwôÅ+i|ybûJǯB>2ïFÑ2² dœI7MBZ«ÿ|\Ôjla¢«AÑ1ó\±öKï>:³ì’¬2¢ob(v?WCSW¿»îà¯þߘ±?ý¬Gôê)3½¨ÿ{ÜZKŸi¨EJÓKšPaJÒÑÕ¤}0ñcûž¤åÍÍk®ÕÝû¹½.!ÍÅI …„á23 VÛdè†äUlrYK;[¹z¹ÉçÏ:m°jãËn“úá&ï)ëöaAj“ã³ë„oz?~©m3Âê}Q×ÊÍ¡IYó®ý‰•»³‘&!„h1†Og.¬3h1ÂŽŠÌ¤eÔÇ€óШÿöíEÈÛŠúV‘—Ð(\jÑh.þ߃bÂô·¤†^‘1‡\{Í^ƒº/ì¼Uú÷¨®f7=R_ÙЊ £—¡@ei.¸ºþß÷Þ ß3—êAµÔ¾©%D×°×jO=#=BêJêZ‰±È­ä¯Ô†ø°o*l ëã·yê¾nÈ_‘}[jKê)¿4ÇãÎ{Úšë54i%5MÂ7µüõ`@G¯ûß´{ôÒ%mµåõïDHѦ+N°[Ï¿‚ khhjÒ­çûóijBÚÚÚÚD^B !ÂÎ"ºÍµ¥u<’þ]&û s—Y‡¾;}isðÙÍ„ñáÌÕÛÿ=ÅN7;M BH[›¤w†¾ý(+r'-ñ5û–ÞûÔ¶6¢¡©ú PªŽî»œ¨a4ÇìÚ;£ï_¥Ò¢™Ð©¬®ÿ{€¶­¡ªK_³­ì—þ¿33jøO=²×÷úª««Ecéb6ï¿wÖ~Ч³—ó‡°M„&By×Àþ{l±¹¾²‘hÐè=t„çI‘ú`¤–ÈÓÕKv7Иú„TW7´£'^/E FÅÎ<µjÛC=Ÿ]Û&›iBš²¯K¨dºéCÊž>+ßñj~óøE5éç9Œ¡E´MEm}^o;Óî=]‚ö|iQpjõÁGTÈbêØ’ß3j9ÐÖŠÿíþúÛ„ Má›Þ'4g§ä²ÿúÞø:ñ&b7ÖÑ@§ý¥úˆ¢ÓQƒ¿„´Õç<|ð{…Xqœ´ÍÜG›’²gÏ+ÞKÚVûÍÌàè¢æ–²»»vÜ.i!DÛ¨ÿø/¶øº)zžý–¯?ÛÍÌÁ”ÔU‰»žöí/+½—Þ.ÿ[¢úìäfR}Tö«Ÿ.&–jÙÏ_7Ùøƒ{o4BØ97öÉ4™±þ3BHw‘[ÿFÏA_ïþÜ*ÿ?+¾{ò¶:ö›)½R¾Ý{· ‰ÒÖóÓ¶ —jØhŠØô>»šø½gRʛۚËÙüEFWMµÐ&b(MDžNÛ¸Ÿ1©.¨x×V÷èàŠÐÛÅ-âåé¸pÝD£—ßí»SðŽÒRž¹-V×cK›4æ%EEüßÓªBi*z’òF£ÿhßtâîîC Zó_‰9ªúáþýñÔþÍ%nŒ®îýiÈDSÎÓ vþóBÒÛíCFtb Ñ&ùjJIHÏÈÐѹ;{TЃÒÀ«·­±Ž6÷ÇÛë»7Þ;¸eÿÅßkµuÞµþteèRSNOVØÖæ¼ós?û6©¤¾è˜yê?^¿ŒzU×JˆcøêsÿY`¥ÓTûý¶}QO«uºi´t·¿ð›õŸØ÷Ô ²²©)}ÏÇOÚí?:ôÎöSñ–51]>ù÷¶o>±ýk­ªàë‚'¯¸SXûŽèè0F†_:æÇÐù­ý{7šåÄcçnÏ[ôCFuÑÒïíya^ü‚/Ï¥W±‰VOS—Ofj ¿„–¢Ûço¼YÚà;Ó3lçèë Wó ¥øÊç[‹kß}ÓQ›þ{ôS-vÞÝÛö_ú½¶[wMMãç…n˜çb¨ÙÖqí`Ĺ˜'oZºk5¿m24mÅ–à } ²Ö§„{Íx´0öÊ|«¿æ ·–ßž¸!¹®±º¶‰=C½Þ3Žþ÷›z„–ªg—Ož¹x÷áëFn­õõ:ýÆÎüzí¼Q,άúÆ›}§ÇO¿~;¸?†tP>ìú[ªcGW‰ :ºöÿI9äÖ÷«š‹£¿œ¼‡µÿÆWÃNjys#È÷[£ý×¶1Ò„¶ £Û>è:©£m:)â»IlXõÿ2:÷¥µæIdð¾†/oð@/ nŸé£Ùkäêÿþç?—7¶½N¬¬m}[o³ú‡ÅÃY:Ð .˜º,>ì?LŸ{â÷j6!:=û||üúnWL_Ñ`.tt;>P)ÐÑ€Ž.€Ž.€Ž.€Ž.5FÖ¯ºŒèP)´ev¦§OŸBn€ÂR]]=f̸@y] Œè©®®ÎÎΆ¸¨Ú2>Ÿ­­-D(/^„kTõŒè@G@G Ú ܾ}›'Å×ײàZ ’`DW)¡Ñh¸ •/˜jüÊŠƒ¯¯¯¯¯ïøñãÑX„[Ê·<øi\ ºŒè¹Q[[  ,ihhHMMÕÒÒªªªÒÖ†û¹Õktt\ ÐÑ h4š\ú„ò:/ŒW¯^egg÷éÓÇÊÊ*99ÙÒÒš¸€k:º ÄTTTŒ5ªG„///€k€ ƒ5º ž^¾|ééé™™™ÙnV.\pqqa0~~~¹¹¹œü:deeeffÔØØ(:ÚtàÀ“P‰oß¾ýâ‹/ÌÌÌÌÌÌ-ZT__O¥/\¸ð믿¦¾õÕW_|ñÏuñ_o@@“É õòòb±XGŽ]~*ž [ZZ6mÚdccÃb±æÌ™ÓîHHCCÃÒ¥KÍÌ̬¬¬<(ŽnÏ+la¤¤¤Lœ8ÑÌÌŒN§{xxDGG·«Àû¡£×«ž”––&$$ÜæšÀ-ÅwË?üð·ß~£¾ÿöÛoÆ £¾³ÙìeË–™™™õë×/22²3s’¥âr4-::zðàÁÆÆÆ#FŒxôè'ÿ3gÎŒ9’N§;99={–J=zô½{÷¨ï–––¥¥¥¸àZ€Ž.èZj¹ [µj÷Ö¤¤¤   3gÎØÚÚ¶›ÕÝ»w/]ºTPPàãã³téRNúÇ=z”ššZ[[»uëÖvÓ !‰‰‰wîÜIKK»{÷.•Êf³SSS_¼xQWW·qãF*}ß¾}±±±×¯_¿víZ\\ܾ}û¸¯K`9ƒƒƒ¯]»vàÀÐÐÐ7npºËÏɇ£•¾gÏž'OžM}¿}ûö°aØL&n'¸¨mmm²9ÓÓ§O !â42?;wîliiùæ›o¨ÿÒh´¨¨¨U«V%$$t´QÒØØhaaQRRB哚šjaaAÉÍÍõòòJKK‘NmJKKëÝ»7wž¶¶¶±±±Ôþ999&LÈÈÈ 6%''B.\¸ðá‡rÅ¿êŒF£UTTBŒ©/&&&Ôå–££ãÍ›7ûõëG)--1bDvv¶Mú÷ïïÞ=Nù?øàþ~¦8ç½??öööŸ}ö™§§'5,£©©)Za÷CG¯Wm¡ÂºTVV6,%%e„ œM/^$„ÌŸ?®·X‹SSSÿõ¯½|ù²­­ÍÑÑñÇttt$„ 0 &&†Z<™››ëää$Î”ŠŽº‡ø.G£Ñ²²²X,!„Íf›ššVVVBœ£¢¢œœœxÎUWWçààðøñc&“9sæÌiÓ¦ýë_ÿÂ]¤¼®€Öè*[·nÕÖÖæ´Û(:d``ðóÏ?Ïœ9³Ý’““CCCŸ>}Ê™TÌ¡OŸ>œ/Üí*a鄞^.!¤¬¬Œ{ÿ²²2ΦaÆYYYihhðôr…¡££ÃýåÝ»w¢Ë/¢¢¢þý8GCCôþ¥¥¥ÜåG·Žê,+W®8p`Íš5™™™=zôˆˆˆ˜6mš÷CG¯W=AX¸egÜÒÉÉIWW7%%¥­­­GT/—òæÍsss~÷è(Rt9ª—KéÞ½{ss3õ=??À€üùèëëOž<ùüùósçÎMJJ:yò$î"¥v-êñ(&ÕÕÕcÆŒ‘Ùé0uYÑ ×ÕÕ]»v-OúåË—/\¸°eËÎX«fÏž=oÞ¼ÌÌÌÚÚÚ¢¢"îMœ/ £ÝtÐétîýét:gÓÿûߺººÚÚÚ .H,‚ˆòSðLLèÝ»wQQg¦_MMèüY,wù%>o»ûóàààpäÈ‘ÄÄÄ‚‚‚}ûö­\¹R²û¡£×«žPa]ìíí †———4[vÈm¦L™rõêÕ«W¯N™2…“hbb’——Çï¢ÑÖÖæŽ} —ëÓ§°Ë_°`ÁéÓ§¯\¹âëëKÅ=p-èŠ^®Œ§bDW¡ e0Ë—/çߤ§§giiyäÈ‘ ܽ{Wt뤡¡ÁÈȨ{÷î¹¹¹Û¶mãÞ¡¡¡±qãÆéÓ§·›.I“&mܸqß¾}mmm¡¡¡“&M¢ÒKJJÖ­[GM¯š1cƸqã$[ý%¢üT75&&ÆÛÛ›3¦1wîÜeË–mÞ¼ÙÔÔôÉ“'{öìÝÍþä“OÂÂÂöîÝK•_âóŠÞŸÿ   777 ·oß¶ÛÄv?tôzÕ“ÒÒRž(;Xó·ìÛL:õÓO?%„ü÷¿ÿå$Θ1cíÚµÔÑÑ ³´|ðÁ¹sçæÍ›§¥¥%— Z²dÉwß}÷ÁïÙ³gÿþýÔ¦¡C‡êéémݺõСC¸‘Tõ°à €P=Y‚]…æÀ6lGÔÃÃcþüùíŽ=ztݺu&&&~~~ÞÞÞÜ›\\\\\\tuu9A¤D¤ dË–-ZZZŽŽŽÝºuÛ²e •¾råÊY³f :tèСŸ}ö§œt†… nذÁ`ìÚµËÏÏOéÊÏf³O:…™ p-P1ÐÑU„ÍÆØ¬|õÁï¢87Àˆ#8ÿ½}û6Öè ¤R+GíååU]]íëëK½ú[¹j=ƒÁ=z4Þ*×€Nrûömžùº:ºªƒM(a‡¨dL^…-ªísçÎ;w®òÖzxR§«—€)Ú3ÎÓ=ø‰šCuk›ššbcc}}}ùû½èè€ô‘»Û\ ¨$TÿVœð¢@åihhHMMÕÒÒªªªÒÖ–sO]€ZÀ3y-H\ 5!W»ŽW¯^egg÷éÓÇÊÊ*99ÙÒÒ]JLEEŨQ£¨÷Õ{yyɽ---›6m²±±a±XsæÌáŒ7Òh´C‡YYY™™™566r‰ŽŽL&344ÔËË‹Åb9rDÄþÔ!ppp0119pà'ýÂ… ... ÃÏÏ/77Wôu¥¤¤Lœ8ÑÌÌŒN§{xxDGG·+é™3gFŽI§ÓœœÎž=K%¾}ûö‹/¾033333[´hQ}}=§}úŠ+ÚÍ'88øÚµk ½qã"^Äþ„êöKKK»{÷.'ñîÝ»—.]*((ðññYºt©èëš9sæÂ… ³²² wíÚuþüyÑû9räøñã‘‘‘ÅÅÅ·nÝúõ×_©ôÐÐP6›ššúâÅ‹ººº7¶ûÓÔ‡ú¥¸8ÜÃÒÅ×××××wüøñD1žÍi´µµÉæL*ðóf6û?“&Bæ_¿®Ý½;îf”ˆ{÷î9;;3 ªùøûï¿{zzr¶ |‰9\ ×â°sçΖ––o¾ù†ú/F‹ŠŠZµjUBB“Él÷pGGÇ›7oöë×RZZ:bĈììl*ŸÔÔT BHnn®——WZZ•ž••Åb±!l6ÛÔÔ´²²Rt>„cccꋉ‰IEE…°ý©CÒÒÒz÷î-¬Ì%%%œþµ¯öööŸ}ö™§§'5h¬©ÙΛ³³sTT”““Oº­­mll,¥CNN΄ 222øÏÈù¯0}„•H*Ueeå°aÃRRR&L˜ ÚµºŒèv€ä“'k j ’Ož„(ïkàZ@ZP­œ^.Å¡C‡ ~þùgqr(**8p 5™ÖÚÚš{ô¾OŸ>œ/ܽJªGéÞ½{sss»ùèèèèèèp¾¼{÷Nôþ„þ^nrr²‰‰ Fc2™ ¢¯ëÊ•+………kÖ¬4hÍåË—EŸ?`Àþô²²2nÄ™Ý PÐÕ¼zõ*))I__ðàÁÉÉÉÔ³ 9‚`Tÿ@Ä3žš‚‚‡'NPßž8áäïoð¾ÊŸ¦§§?~ü˜¢¯¯?pà@•¿d¸p-ÐÕ„‡‡ëëëóLZ&„\¾|¹¤¤ä£>2dˆÀÎO—ò·ß~Ó××çßTPP@õ ¨á}Éò‘Êþ³gÏ ›8q"F«««333ûG§B[»±±QWW—“âàà@-nnn¾qãÆòå˧M›&"ÿ>}ú¤¥¥ñèÒétnèt:çŒõõõTè£7oÞˆÿ«µµµáùQW€`T]HDDgYy^^ž§§'N÷ôôÌÏϘÒ!îmÛÖÌfSß›Ùì{Û¶án@‰`2™nnn>>>>>>nnnâÌ%“;QQQƒ 266vwwOHH訳Áµ€k.%44ÔÈȈ¿—KÑÓÓ³´´KKKkk먨¨vƒQ-Y²dÞ¼y_~ù%‹Åòòò2d•¾eË---GGÇnݺmÙ²…Jß¿ÿ¥K—úôéãëë;yòd1µððð   D]–:>>>TH*¹›«H0*6›íáá±råÊ… Rs---“““Y,VIIÉðáÃ_¿~͟Ÿ5u¹¬¬lΜ9<;lÙ²eÆ RQÃÅÅEZÂJ+«Ú3ghsç*Z© >Ķ IDAT´’J©”:¬KAA³³ó›7olmm;êlp-ÔDh×J Â&å"))ÉÜÜÜÜÜœ’ŸŸŸŸŸïêê*ÚµºÑݼy³½½}@@'¥ªªŠÁ`x{{Óéôªª*)IKKó÷÷_½z5O/·¥¢¢öÔ©ÖÊJ’’¢¯¯ßÉÏaÆI%}}ý¥’IIaΘ¡€¥‚VЊ»Tºlví©SM/_Jö Qú”——.^¼XGG§CÎ×BM„Vp-ÎÎÎEEEwïÞ½{÷.êL¾åQ‘]CCÃÖÖÖ¿ŸÅÖÖZZZ¦¤¤0™LîqžOΜ‹‹‹oܸáèèȿÖ-[víÚÕùgeeÙØØHåÚ¥˜U]B‚¾‡‡¢• ZA+žRÕÕÕIpliiizz:u¬¾¾¾÷‚7…yúôéìÙ³§M›¦©©)³ÁµP¡\ (Â&ÊŠ³UÉ] tJÊíÛ·¹ß¦+û]‰º\]]Íã£GŽŒŒ\»víáÇnj#0E ÑÑÑ> ¸wï'4¹Ô‘Öcéf%­¿ë {ÐJµb2™JÊåìÙ³'Ož¶téÒGM˜0Šqòöí[‰ wj"´‚k@—¢h–¥"S—e†´&$oƒJ4 gþ *< ®\ äÒï•ïÔeMürϰ¡´’o©î.ÔDh×]ù€UIÐ ZÉ·Twj"´‚k@×!bN :ºª žaC+h%ãRÉÝmáZ¨‰Ð ZÁµÄáUBÂ+éý™QVV–˜˜xçÎ;wî$&&–••¡£«Žà6´‚Vò-•:ðîÝ»ÜÜ\êÏLvvv'?©7yv>Ÿììl©äC)17WÀRA+hÅ]ªôôôÜÜÜ¢¢"˜’84³Ù?oÝúóÖ­Íl6Ô@¹xöìYÿþý½¼¼¼¼¼ú÷ïÿìÙ3ttձɘ””$­?¢YYYÒúÓþòúu,´‚VÜ¥B“Q|ttt,,, !ÄÚÚº“ŸTäÂÎçcmm••%•|!¬ü|,´‚VÜ¥²³³³°°033ƒ)‰CòÉ“55É'OB ”÷(Dau¹C ~)rG²ø¥¢AÔe\KîÔügÒ$j,W»{÷ùׯôéƒûe¡´´4==r<}}};;;î—#겺€UIÐ ZÉ·Twj"´‚k)÷¶mãÌXnf³ïmÛMP"˜L¦›››››w/W. £+°–ZA+Y–ê—_~),,”Ù¸j"´‚Vp- ˜’šZÿé§!©©!©©S##;“›¹H1«wii X*h­¤R*£„à6´‚V²-•££ciiéýû÷333Ùp‚» 5ZÁµÔ€7oÞ(`V¿þªÚ­ÔY+£„à6´‚V²-•¡¡á AƒFEùå—_ž={V]] #ÂÝ…š­àZ 'ð§¾¾~III'?™L¦Tò)))6l˜TòÑ×ׯsuUÀRA+hÅ]ªÂÂB‰*T0*ttÿF“͉ð Z©žV3¸PL­ºwïnkkëááÁd2_¾|©>MF‚˜Þˆ­”3V¼º¹–tcÅkkk)EÏÎÏÏ—VLo³ÚZ,´‚VÜ¥’8VüÀÓÓÓcbbbbb222(_KQ‘¨Ë×®]ÛºukVV–““ÓŽ;ÜÜÜòòòæÍ›÷ôéS—3gΘ››ó§ìèÖÖÖŠ8â— ¢£Ëù~áÂ…®;â—v¸r®×®…¨Ërùòå³gÏ®^½zΜ9„õë×»»»¸»»‡†† L‘#¥„VÐJ¾¥¸»P¡\ ®…» ZA+)–ê6ò5mÕð²Ó§OBš››ŒŒ! ºººAAAǘ"Œ²²²9sævQ±* ZA+ù– àîBM„Vp-¸î.h­¤[*___Å1ÕY£K£ÑX,ÖòåËOžžÒÉϬ¬,©äCɾpA*ùÔÅÇ×%$(`© •i5¸¥…úœ1cFiÕ\^^{êT“¬US4ð ZA+ù–J@d¬–‡VÊY@¡P‘5ºõõõ?ýôÓîÝ»ûí7KKË””&“YRR2|øðׯ_ó§ì-;;;߸qÃÑÑ‘¬@<1¨ºn™.V»u¸r®×@\ëöíÛ"FtÕq.踾¾ÞÓÓÓÜÜüæÍ›d²dÉ’¼¼<------êñÃèÑ£###ÙlöáÇnj#0E ÑÑÑ ())麫Æ3lh­ä[*€» 5Q}´š!p-Y6üàZp-h¥zZ)Ô¼e¢8S—oÞ¼Ù½{÷óçχ‡‡KpøèÑ£?þøcSSÓ;wž8q‚²}ûöøøx33³û÷ïoݺU`Š@˜L¦ŸŸßš5kºèz1¿ZA+ù–Jî”Ü]ÐJåµâôx:×â‡Íf¯]»ÖÚÚšF£ ë¬æååyzzÒétOOÏüü|¸j"´’nVÂ^åˆÈ ÚÑÕÒÒjiiyüøñøñã]]]322$Èä³Ï>ûý÷ß+++ûí7oooBˆ……E\\\EEElllß¾}¦ðÃy·ÐìÙ³ãââôôôºèªñ4ZA+ù–JÀj7¬à‚VÔg¯^½:ô9ÔÈhÆŒ‹-êüçâÅ‹UfµÛ–-[xðÀÅÅÅÀÀ€ú¯Ì‚Îwé†u¡þBH%ü†´‚‚”˜›+`© •²hÅÐ¥+´’8¬KYYYbbâ;wîܹ“˜˜HÕe >ˆÉ ­ÔG«§šRkUTTH|¬¢¹ÖÎ;wîÜibbüÝwß l¶á ¨‰ÐJmµ2444hШQ£!¿üò˳gϪ««åëZòŸºrä\ ®…š­¤ÞÖ➺¬ m-­­­ÅÅÅ999T¿W„ku)òÑ¥bÐÿõ#ÕÕõèу¨x­ •ìK¥ñ¢` æ¡@+É^/”C§Kp”ÀÏŠŠŠÎ¼^®…šׂVÂܬë´êüKÑ455{÷îÍÝË• òÑÿüóÒÒÒM›6½~ýú‡~ÀSF<9ƒVJý”‘(Þˆniiizz:u¬¾¾¾“Éý”c#¨‰ÐJ5´RÒ]¸j"\ Z‰p3EшìGtµångÇŽ[¾|y=úõë7~üøƒªÃ3KŒ¼A+h%ãR1™Lî6bg VXÔ××Oš4)--íèÑ£~~~p-ÔDh¥2Y)È]¸j"\ Z)µVrGþS—MMM£¢¢Þ¼ySTTtîÜ9j-ŠÊƒˆmÐ ZɸT·ùèdanÞ¼Ù½{÷óçχ‡‡ÃµP¡•*e¥ Q—áZ¨‰p-h¥ÔZÉm¸Y(,< ‚V*¯Õ?f×HI®N–Ê××—ó"ÊÎ4µ´´ZZZ?~<~üxWW׌Œ u°GÔDh¥>Z)Ȉ.\ 5®­”Z+¹£"ïÑŠŠ4h±±±»»{BB!$//ÏÓÓ“N§{zzæçç L‘#x­ÔG+)Žt¦Tšššµµµ555uuuÚÚ’?æ³¶¶~ôèQ||ü Aƒ´µµ[ZZºÈ(hïá¤ÈÑÙP¡\K:3¢«Œ®ÕI¤Œ*))‰H)|x̘1S}ðàÁ€€€’’’®+0žA+õÑJŠñK;u™CgòILLŒŽŽ®ªªª}̼NbgCMDM„Vrq­ÎG]–ŠkíÝ»Wfc j"´‚Vò-•Ü‘GW*mÄ¥K—>zôh„ Ôè·oßnß¾=>>ÞÌÌìþýû[·n%„ð§„Édúùù­Y³&  ¡¡¡‹®óû¡•úh¥ ktyš‰i5:t̘1†††üQ‘¥ Ïú>&¦³¡&¢&B+¹¸VgÖèJѵ ÜÝÝÿºêê$‹‡"uQ—¡•øQ—¹c/+fÔåN>¡ë<òŸº„ÇI“°TÐJA´š1cÆÚµkg̘QYYIÿóQUU‡öñYQQagggaaaff&{  G‚Ü6oÞloo boöæ&--ÍßßõêÕÜéxû7ÁÛ¿¡U{ŸÜoêî"­$~û7“Étssóñññññqssc2™òmÉ?UYY™ÄShd<6RTTqòäÉììlkkëÎ|&%%¹ººv>Ÿììì¶¶6 ÎçcmmýòúuÇI“­TÐJY´Z´hQeee¯^½x>‡ýûØ1i•ª¥¥¥¹¹YGGGº­Æ…uQ.ðFJ¥¾@hÕ™¬$ŽA¥ ïÑ•¢kmذÁÀÀ`Ñ¢E„Ç766†……uôŒ†††­\3t®Úh÷ÍÞT÷ÛÙÙ¹¸¸øÆŽŽŽp-¸´®oÿVñŽ®z6P=<겑áZ¨ª)ɉ£.Kѵ˜LfAAA·nÝ!l6»oß¾%%%_‘ˆ°É³fÍêß¿ÿÚµkwîÜ™}æÌ‡ggg?|øpíÚµ÷îÝc±Xp-:ÙÑ•:wt}}}©OÎåØÑ•ÿÔåÌÌÌÉ“'³X,“)S¦¨É<Ìï‡V꣕‚¬ÑU´H€p-ÔDhEuYV®egg÷ý÷ßSÁó:Ô¿i] Ï,h1ßìÍd2ýüüÖ¬YÐÐÐ×BM„Vª¡•¦¦fmm­††FMMM]]¶¶¶|›.òïèΙ3gìØ±YYY™™™îîîsçÎU‡&#ÖB+õÑJqÖè*T$@¸j"´"ˆº,+×:sæL\\œÍýû÷OŸ>ݙܸ‡sy†v-,,âââ***bccûöí+úðÙ³gÇÅÅéééÁµP¡•jhennþ믿:88<~üø—_~±³³S÷ŽnFFFpp0aÙ²eéééêÐdÄÓ h¥>Z)Ȉ®¢E„k¡&B+‚]Y¹–­­íµk×JJJJJJ®]»&Å]¸j"´‚V½¼¼,,,ÆŽK}Q÷ŽîâÅ‹:DM§9xðàÒ¥KåXÉâÊøaI×e…'gЪ+²R]E‹¨t &B+¸–tfDW ]Kº/E£FÂ¥òò*i½R«ÄÜ\K­”E+þטu…V¿'l*uéèrÜïß¿?,,¬wïÞ½{÷Þ´iSDDDgrã¤äååyzzÒétOOÏüü|)xʈ'gÐJY)Ȉîm>Ðd$x7,Þ£«êZIöþ[y®]+--ÍÏÏÅb±X,??¿´´4Åt-龊·*•—WeeeIë•Z¬ü|,´R­ø_cÖZuæ¥h ÕÐR©¨ËÜagÏžmmm½nݺ;väääœ>}š?EtA$@„¡àQ—%ˆˆ¨Ë¨ª)ɉ£.KѵÜÝÝýýý9¯ºqãÆƒàZ(»­)fÔeaÿUǨË]DBBBPP®®nPPP||¼Àa”••}üñÇçÎëºâa”Z©V 2¢ PáZÐJö®Õ™5ºR$55õ«¯¾200000X¾|yjj*\ 5ZA+©—Š»[Ëÿ_Ù£²ݪª*ƒáííM§Ó«ªª¦$--ÍßßõêÕÜé-µ§NµVVÖÅÇB:ùicc#•|!&yyRɧ.>^ßÃCK­”H«Á--üŸO55¥XªæòòÚS§š^¾DÏSÆ`Ý)´R­'ê²´prrâ¼^èàÁƒNNNp-ÔDh­ººTrGe;ºFFFååå111åååFFFS2þü¢¢"žt-ccÚ‚š½zéKéägVV–Tò!„¼éÛW*ùè[— €¥‚VJ¤Õ--þO—ÖV)–J›N§-XÐÍÑQ²§Œr¸¨¼à6´R­dDWŠ®uòäÉ{÷îQ¯Š=qâ„bú " ²´’,•tµ’8²€¢¡²ktgÍšÕ¿ÿµk×îܹ3;;ûÌ™3ü)sÈÎÎ~øðáÚµkïÝ»Çb±xvÀº„¡àktyÀ]ÔÙ”d‰ÄktáZѶ¦hktEƒ5º’wq©Ëœ/Û·o733»ÿþÖ­[¦„Édúùù­Y³&  ¡¡¡‹ ŒçýÐJ}´RÌ¨Ëøc‰šׂV2p­ÎŒèµPáZÐJ©µ’;*ÒÑ­ý'„ ‹¸¸¸ŠŠŠØØØ¾}û L˜õeöìÙqqqzzz]T`Ìï‡V꣕‚¼G×÷Ÿ¨C#o¤Ä)¡•d¯ʡӉ”^/TQQ!ñ$@)ºÖÅ‹ ø_Ǩ ý­ •|K%wTjê² Ötš¬¬,iÝFR̪.!AZõD1/ZuiVÂf º´¶>ÕÔ”ÊìªT˜×BM„Vâd%ñÔeʵ¤R*ccã#GŽÈݵ,,,öïßïïﯥ¥×BM„V*ÓÖâ4®¤®¦.…xX‚'gÐJÁ³R]• 5®­dàZRŒºÜI×òõõUü^.æ¡` ´’,•tµ’xJYYYbbâ;wîܹ“˜˜HÕe9‚ÝŽ§Œêöä Z‰‰ˆ‘Ñ•à%æp-ÔDh¥ÔZ)ûˆ®]ëèÑ£555AAA=zô€k¡&B+åÍJÁGtïÝ»çììÌ`0¨Nïï¿ÿîéé)Úµºmäž÷C+õÑJqFtÍ5®­dìZÑ•–k­ZµŠ²yófN '(‰ìá~G\ 5Z©˜VÔ ·"€©ËòÛ •úh¥ Q—É?#»À…PáZÐJ®Õ™¨ËRt­Z>$È$**jРAÆÆÆîîî Bn§¼¼|øëׯù§Î^VV6gΜÀÀÀÀÀ@¸\ ZIÖÜR´5ºü«-Ú,Ð¥`DW>ài´R­dîm>TÞg0óP •R¿GWŠ®ÕÚÚzìØ±–––NKyyy``àâÅ‹ù{¹„ªª*ƒáííM§Ó«ªª„e’––æïï¿zõjž^nKEEí©S­••uññ„N~ÚØØH%BˆI^žTò©‹×÷ðPÀRA+%ÒjpK 糋´j./¯=uªéåK ,‚êÙ*È21Œèv |x̘1Â²ŠŽŽ>xð`@@@II \ 5Z©ªVr]ù€ˆmÐJ}´R¨ËIIIÆÆÆ£F211qrrzñâŒ5®­ºÚµ:uYê®Õ­[·£GN™2E²—,]ºôÑ£G&L Ñh4ííÛ·„ƽÏöíÛãããÍÌÌîß¿¿uëVaY1™L??¿5kÖ444ÀµP¡•jhÅ™·ŒŽ®¬Q¨÷x­ÔG«.ѽTÝ‘ŠÐŠN:>j¨UqqqÏž=y´â?J´Âˆ.\ ®ׂk‰föìÙÖÖÖëÖ­Û±cGNNÎéÓ§%Û§]0¢«Ô­º.+ÙèF89iëêÿüóa hëê¶{,ô)ù¾G]@G×jqBpI%L—´:º¨":ºR‡ÓÑ¥þkØ·ïøuë¬Û›Àrûöm___ê“ó_9vtñòqМ\·nÝ‚Kœ}’žž~þüù66»ùõëß+*VÌš¥ejÚR\Ü™Ï*£wï:Ÿ–©iuf¦¡­mçói).nkhÐÐÓS´RA+¥ÐªO·n<)œš"]­ úöm~ýZ‹Å2äTѶ6"«ÁQtt€ì022*//‰‰)))122’xØÙÙ…‡‡Sß·lÙ²aÆÎ8''ÇÒÒR*×.Ŭ“’t]]­TÐ Z ,5uyØ‚Ã?ÿ\œ©ËŠ‚Q€v'—B…é233SÀ¬Þed¨öB+UÒÊz̘yW¯º‰ÙË¥&*s¦+sÏ[– Ñí°}ûö¹sç8p€ ÁE%òèÖ­›fÕÃÏOµ/Z©’VS##;t¬è`Tèè…ÃÂÂ"..Ž'‘'”·À}:Ê'Ÿ|¢Â2j1¸— •ªjEuk›ššbcc©¨Tò-:º@Qptt„() ©©©ZZZUUUÚÚrîi*è]šppÉòWPSpßNª!€k¸\ E#//ÏÓÓ“N§{zzæçç‹Y‘£¢¢ dllìîîž 0hÅMDDg7õÔŠÿžç(ÙhõêÕ«¤¤$}}ýÁƒ'''[XX £+˜ ¢Cm6›½lÙ²Þ½{÷îÝû«¯¾jjjêh“"::zìØ±L&Sœº×ÚÚêëë+z·Ÿ~úi„ t:½oß¾³fÍÊÉÉVNEh(óÌGR^jkk;-h ¸\ ®ÆúõëÝÝÝ ÜÝÝCCCŬÈ7oÞ<þ|aaappðœ9sæ­8<þüûï¿‘:hÅψs”l´ª¨¨5j”½½=ƒÁðòò²³³CG·«Ú»wï.,,|þüùóçÏsss#"":txtttHHȺuërssÅ©~»wïnwMù‘#GV¯^›››šš:xð`ê½ÏÂÊYËþ~ bÀµ@•HHH ÒÕÕ Š¶Ï¨³gÏ:99éêêŽ=º¦¦æÝ»wb棆Z±Ùì/¾øbÇŽÍGÅ´â¿gøµâ?J6Z :´GŠ£ž*¿^èòåË›6mb0 cóæÍ—.]êÐáûö틈ˆðññÑÓÓkwç‡?~üرc¢w»víšOÏž= /^üêիΗ³¸¸ØßߟN§»¸¸$&&r»ÃÆMLLÜÜÜD왕•åææÆb±ÂÃÃ9•Dà˜Œ° 4¦ððp‹åîîž••ÅïVÜßÃÂÂX,–««kff¦è+å¿¢ŒŒ cccŒŽÇU§®±W¯^®®®œàÑÑÑ~øa¯^½8 ðÍÑ£G-,,,--üñGå&ÿeõ®ׂk ¤TUU1 ooo:^UU%l7ÏËËË/^¬££#f>j¨ÕæÍ›ííí:šJjÅ}ÏðkÅ”:h¥^ÝœœjÄÜÛÛÛÞÞžšq'>©©©ùùù¶¶¶L&ÓÏÏOD‹¤¦¦fÁ‚‡255?ÿM›6M:UD9­¬¬zõêåàà°jÕªêêjaù,_¾<00°°°pÏž=AAA<[³²²† &bÏÉ“'çååq·ZȈ>úúúyyyS¦L i·Ý–——7mÚ´v÷ä¿¢+VLž<¹¨¨ÈÏÏoÅŠ½C¨Ë,++Û»wï—_~I%.Y²äàÁƒ¥¥¥ø¿´kIþùç¾}û6nÜ(q9¹/¨p-¸\ %ÅÈȨ¼¼<&&¦¼¼ÜÈÈHüŸ>}:vìØ1cÆlݺµ3ù¨¼V‡º|ù2ç)ž:kÅsψs”:hÅF[[›lÎôôéSBˆ­­­8;Óh4kÛ.^¼(âo6ÏËÜètzQQ51¯©©©wïÞeee„ë94OVü‡¯ZµjÉ’%ÚÚÚÇŽ»zõ*µà›ÿð ôêÕ‹š¼Ç‰°544,[¶ìÏ?ÿ¼y󦡡¡°rBZ[[©iyyyW¯^xÕ&&&õõõýœ555œ³éëë‹Þ“ÅbeffÔÖÖöîÝ›»¨<‚;‘À¢°°:ÜÖÖöÍ›7üÊPßî)"[ž+b±XYYY4­¦¦ÆÖÖ¶¤¤Dôá}V®\idd¤¯¯ôâÅ a‡_¼xñرcÜSÅDœ(==}ܸqÚÚÚ111†††"ÊIÑÔÔ´²²Úµk×ÿþ÷?¡?¡¦&çA>O3ާÙ!bO±î‰ç}z‡Ÿ~ü¸‡ s¢ãÇ755?~|äÈ‘Tй¹yttt}}=w¸<î=G%:O~† vüøq6›}ôèÑv§Ì™™™%''s§4662Œ¦¦¦;wrutt¼½½W®\¹~ýú?þøƒ»!Èýß!°œÂê \ ®×@I±°°ˆ‹‹«¨¨ˆíÛ·/Órl–ÇoyöéÙ³'>ЊÿO˜°|ÔP«ž={òkÅ”:h¥²]ÏV¯^mjjêìì}úˆXL%ððõë×çææ:::ZYYݽ{÷ĉ/ç¢E‹òóóçÎˉ›òöí[夶 >¼´´Tădfföëׯݠ#÷Œˆˆ¸~ýº¹¹y]]¦¦¦AÄ?!¤²²²OŸ>—.]Ú»w/•¾|ùr{{{ž…œ=÷ìÙÓQ=÷íÛwùòeSSÓèèhΉ„±qãÆÉ“'s—|ÇŽæææ<7Nÿúë¯>ÌI 3fŒd¯ëXNa‚5®ׂk]‡â®Ñ¶ o­è Z[[úé§°°°GI¥Ÿ (RY£ ׂk D®rw­.E[1…@ƒC–Ðh4 +++ÌF®×€   €ÔèhñàZ#š:º:º]Lkkëœ9sTé·™3gŽÌB…àZ°2Ô šp Ž,8÷í¤¢¡ ÖU§£{÷î]{{{™Ý¾¾¾ÜÕ,::zìØ±L&St%g³ÙË–-ëÝ»wïÞ½¿úꫦ¦&a‰„{{û˜˜ü @U‘k l|ÄÆÆNž<™Á`ôéÓgÚ´iÏŸ?'„¤¥¥}ôÑG ÃÜÜ|éÒ¥ °2T› ¢Cf"¬â‹ß³%¬ÆÏO?ý4aÂ:Þ·oßY³fåää+§"tïU&@÷ûoÕ¹«ø µú³®:ÝË—/»ººÊæ\»wïîÖ­·G‡„„¬[·.77Wt=ß½{waaáóçÏŸ?ž››!,‘âêêzùòeü@U‘k 4¥C‡-_¾<777--- `ÆŒ„Ï?ÿ|üøñ¯^½zñâ…‰‰ÉŽ;`eÑf"¬â‹y¸ø-( 09rdõêÕ¹¹¹©©©ƒ¦üMX9k¹ÀO Ô¡ VÖU§£û믿ò<¡ÑhGµ°°°´´üñǩČŒ cccŒŒŒòòò~ýúÕ××sŽª¯¯·¶¶.//v¢‡?~üرcœ”}ûöEDDøøøèééµ{[oÚ´‰Á`0ŒÍ›7_ºtIX"!dÀ€¿ýö¼UEf®%ЋÆ¯§§×ÔÔÔØØhddDIMM ¦ÑhFFFÿþ÷¿£££ae 6â#~ J`L ×®]óññéÙ³§ÁâÅ‹_½zÕùrûûûÓét—ÄÄDnÓÞ¸q£‰‰‰›››ˆ=³²²ÜÜÜX,Vxx8gxMàh›°ñC£ÑÂÃÃY,–»»{VV'‘{Î÷°°0‹åêêš™™)úJù¯ˆçoPGïê{õêåêêÇyÀñá‡öêÕ‹£ÿÑ´û·R´ ü—©> µú³®:ÝÂÂBcccžÄªªª?ÿüsß¾}7n¤RV¬X1yò䢢"??¿+VÐét+W®p¹r劇‡Nx–ššš :tÈÔÔ”“˜šššŸŸokkËd2ýüüD¸@NNŽ!ÄÛÛÛÞÞžšN#0‘B§Ó ðGUE6®%¢ña```ii¹wï^ꯗ³³óÑ£Gëëë«««÷íÛ—ŸŸ+ˆFXÅñ[P`í²iÓ¦©S§Š(§••U¯^½V­ZU]]-,ŸåË—îÙ³'((ˆgkVVÖ°aÃDì2yòä¼¼<î¾–ÀÑ6Ñ'âA__?//oÊ”)!!!í~^^Þ´iÓÚÝ“ÿŠxþuô¡.³¬¬lïÞ½_~ù%•¸dÉ’ƒ–––rDàÿ"švÿVvô2Õª >Ö5d¶˜øéÿgïLâ¸ò¶_Í¢hh¶fYT0(Ƹ!"ŠHLFÞD5:‰¶&¨˜'**q¹"FQud|äqÄA‰â‚£"FL&àˆ±ÙTv›E6…ƀé«ÓKÑôZÝ}ÿ>p5Õ§NswÕ]g©ó¯’‚ ÜÝÝ•”¿••U}}½à-L&óÙ³g#GŽìïï777ïìì$ÂÚÚºªªŠÉdvvvº»»s¹Ü›7o~óÍ7™™™ÕÕÕ .Œ3gŽØ£,_¾ÜÜÜœœ»g2™ä¥Èb±bbbV­Ze``päÈ‘óçψÝÅb566’…ìëë³··oii»‘üìààÐÜÜŒû(Ê&++‹ ˆeË–iŸkñsm: ÔÖÖîÝ»·­­íÔ©S'::º¨¨ÈÌÌìË/¿Ü±c—Ë…• ­®Åd2Å®ÈÍÊÊ¢èi™‰¤ Ÿß£ÊJtw±-(ÑÝÅ6À(ÔÓÓ³víÚGåææšššJ*'é„äc™µµµçÏŸ[kþ4ƒÁ Í™}útr‹££cNNÎË—/SSSŦœ1cužÒ܃(°³³+**ÜÒÛÛkiiÙ××·{÷nþFCCÃùóç¯_¿~óæÍ¿ýö›`OOðß!!¶œ’Ñå€NÝÖµ§£ûÁP¯Ú'INNÎÎζµµÍÉÉÙ·o¹qñâÅÆ Û½{·‘‘Ñ¢E‹†zèÍ›7×ÔÔxyy¹¸¸\»víèÑ£’RnܸÑÖÖÖÛÛ{„ äJ ± ‚(,,\°`n¢h+ªq-±>-ZcggçããóäÉ““'Oò899íܹóÛo¿%¶ÁÊf"é—rwé[PÒóÙgŸÕÕÕEEEñ£=½xñBl9ÉoMLL¦NÚÜÜœ‘‘!)Ï”””ÊÊÊÑ£G*Ilʤ¤¤ .8::vwwëééQ"ý‚xþü¹ƒƒÃÙ³gù·†„„„èèèqãÆ‘!ESîÝ»w¨zнI"...,,L°ä»v튈ˆðôôttt:X,Ö—_~yèÐ!þv6›={ölÙ^2$¶œ’Ñå€NÝÖµgîÀÀÀÒ¥Kÿþ÷¿kÍ%222##ƒoˆå¡–5ºÚçZ°24ȵ(¾x׎’<ÿÒ¥Kñññ÷îÝSȸ~&>4· óm]õkt ´æŒÑÓÓÓ²ö"9ÇÐV´Ïµ`ehè&©&“É`0\\\èö -@@‹oë8ÕP VÀ8ƒ‚gÉ £  £  £  £ ]]€v¨ô=ºYYY )Àµp-ÐDT7£ÛÑÑQ]] ÅÔ^„kàZ ‰0^¿~ h X£ ]]@@KII DÐ–ŽŽŽÙ³gõšëZJ3ºš×^D€e\‹Ìèîîî@7(^– ×h–k) ÌèÐ*ÐÑ€Ž.@W°F€v\¹rEhKHHdÀµ¤3º† “ɤa©._¾ìèè(Z6±[H4Zzþ @Q„„„„„„Ì;]\¸«zË£ ·p-ttiILLÌÌÌìêêÚ.v‹èFC ª¨ééé¹ÿ¾¾¾~{{»žÂêñX ÐPׂiÐ8ޝ¯/t“ÉÔ諦—¨<~ü¸ººÚÁÁÁÅÅ¥¨¨ÈÙÙç0ÀÕ àZèèÐ9x<f½€ÖÐÖÖ6cÆŒ‘#GAp­!G—H„)‚P‚‡VVVšODD„••UlllPPµµuZZùÕ™3g|||,--CCCkjjÈ+V¬øòË/ÉÏ_|ñÅÊ•+¥)§à¡òKYßþþþmÛ¶¹¹¹Y[[/Y²dЉžžžÕ«WÛÙÙ¹¸¸ìß¿Ð|Š‹‹ß}÷];;;‹åï“Ãß%##cúôé,küøñ'Ož¬BJJЧ§§MJJ u½˜Læ\\\ìììØlvoo¯X}ø;¾xñbåÊ•vvvvvvŸ}öÙË—/©KØòKÊ_¨äü™LfNNΤI“,,,¦M›vïÞ=êòk7ÍÍÍW€#Á]UﮈÍ_ÒÕ*É%]õ’\eÖ¬Yׯ_'?÷öö:;;777ãt‚k¡£ `ht #ømaa!›ÍÎÈÈpww4«5kÖ|ÿý÷)))±±±/^äw ¯]»vöìÙúúúàààÕ«W““““oܸqáÂ…ï¿ÿþæÍ›ÉÉÉÒ”“YN6ÔÕe{÷îýõ×_oß¾]]]mjjK~Û¶mÝÝÝeeeÅÅÅ?þøã ù|üñÇ+V¬¨ªªjhhسgOff&¹=---==ýàÁƒMMM—/_ÌŠ ˆ;wî\½z•Ãá\»vmÐzýüóÏ÷îÝ+++ëêêJLL«ߨØXWVVöàÁƒîî8êãJBRù©óË… ._¾\WW÷ÑG­[·ŽºüÚ ‚QÁ]éஈÍ_ÒÕJá®b¯zI®²téÒ'NŸ¯\¹2eÊ+++œNp-±0^¿~_D)))!BšF†.°{÷îþþþ-[¶ÿ2™ÌÓ§OÇÄÄHÓÈ`2™mmmAXXXlllÈ|z{{œœ¸\.ùoQQQDDAgΜyûí·¥)¤¤UaÒo÷òòÊÍÍ=z4AÍÍÍÓ¦M«®®¦8¢‡‡Çõëלœ‚xúôé›o¾If()ŸqãÆ}òÉ'ä´†žÞ†›½½½OŸ>=~üx±…äp8öööÒ”ŸÉd–••‘å©©© âp8éÝÝÝoܸÁ/ÿ¼yó***=®(’Ê/)¡’ðÿe2™UUUÖÖÖAðx<[[ÛçÏŸú;j1===eeeÏŸ?Ÿ2eJqqñ¼yóø_eee±lÙ2¸ÜUî*6ÿ!ª 4 IDAT¹«¤«^’«tww{zzþòË/VVVüñ‚ þüç?ã,Ò\×R*ª[ËDš/Ð“ŽŽŽÙ³gC±$&&ðÛa$011ù׿þõñÇK“‰¡¡¡à‡W¯^‘í­ØØØ’’Á‡fI¦L™âââÂ`0¤l‡)„ÆÆÆ &ðÿe0Ôé›››ÈÏüùœ;w.%%嫯¾ª¬¬9rdRRÒ‚ ‚¨««;v¬¤£HÙÛ*†ƒƒƒ`»S,---‚é[ZZd;®¤òSç/²½KÄðáÃÿýw]¾îŒ îJgw¥È¨î*öª—ä*ÆÆÆaaa™™™QQQ………ÇŽÃY×’]¢££ƒzâN—IHH022úú믅¶gggŸ9sfÇŽ‚s†C%22réÒ¥•••]]]‚_ýãÿèîîîêê:s挒ªf``À_ÅÊïÚ566òŸ¸ëìì´WV__O~æ ÈÇÓÓ3--íÎ;õõõÉÉÉëׯçwå‘QÁòXZZ }+ô‹ÅLÏb±d;¨¤òKÊßÀÀ€ß8~öì™ôÒ©ÇÐȰ.ãÆ³´´ 3f ìîª.wuKŠüÅ^­CuW W\¾|ù‰'Î;BÆ=p-ñç­Š‡Çi4„|œˆkii-úÕˆ#œÓÒÒ–/_~íÚ5ÙZ===fffǯ©©ùæ›oøÛ¹\î¦M›ÈßeáÂ…sæÌQÆ*¬7ß|óÔ©SK—.Õ××'·DEE­]»vûöí¶¶¶¿þúëÞ½{©‚~øa||ü¾}û^¿~-¸äLR>ááál6{æÌ™ ãÅ‹|ÑØlöªU«þú׿¾ùæ›MMM{÷îýî»ïd«T|||RRƒÁˆ‹‹û裄ºåyyyóçÏçÏ¥¼÷Þ{qqqÉÉÉdùß{ï=Ù*©ü’òóÍ78°fÍš®®.¡¹,êa¡òk7ÍÍÍBQv°Rîª.wuKIùKºZ‡ê®®8yòä#F$&&8p'\‹ÌèHJJÊÖ­[)â‚úûû/[¶Œ?39T>¼iÓ&›ÐÐÐùóçó·¯_¿~ñâÅ“'OžmÚ4¼¯\Sèéé¹ÿ¾N£úÇûWbâ¿çñp Y”––zxxyxx”––µp-ttILL$bË–-‚8`bbò¯ýKžœ[ZZÈÏ2‰ÍÇ××÷Áƒýýý·nݺyóæÀÀÀƒÈhX&L Ÿ£vuu:®½½½PþçÎkhhøê«¯&Nœèææ–³‚þ<~ü¸°°ÐØØxÒ¤IEEENNNºPë¢cÇ:ëë;ë닎Ã9€ÆÁø/ºSe¸p-EP@`h$$$ =´LDvv6—ËýÓŸþôÖ[o;V¶ÌY,V}}=Ù©¯¯g±X8Y z{{ŒŒ„Ob‘íbó144ôððÈÎÎööö~ýúuvvö˜1c É®ìO?ýdll,e9===ÓÒÒ‚øý÷ß/^¼½`Áœ4G[ƒQQ¼Åº³¾þç£GÉÏ?=:><Üä¿@ú3a„òòò_~ù… ccã &Àµp-éÁŒ.±±±fff¢½\‚ FŒáì윖–¶|ùr™ƒH½÷Þ{qqqÏŸ?okk‹}ï½÷¿}óÍ7O:Õßß/´—èvIù„„„$&&.\¸páÂ…;vìà/ÔŒŠŠZ»vmmmí«W¯~þùç… R—3<<}zâĉ~~~C_±vý›oøÏþýÎã]ÿæœhVVV3gÎ ž9s¦••ý]‹"\%\ ¸:º€¾¤¤¤lݺ•â6æïï¿lÙ²õë×Ë–ÿŽ;ôõõ===½¼¼† ¶cÇÁo÷îÝûÝwß™™™ Wt»¤|‚ƒƒÃƒƒƒÉí111žžžÁÁÁ6666lX¼x1u9Ùlvrr²³³³««ëéÓ§ŒJ#`0ÁÁÁdHªàà`µ?T#‰M,¹¹¹™™™ kÖ¬Y²dÉPñÁÁƒÊÊ^þ¿ÿ·¡¬lCYÙÊSàÆÆFEÕ]Y½âphX*h­R*º…u‘Ƶ¸•2®…+ZÁµÐÑê¼É ÂßÎO³bÅ ò±^irÚÂd2;ÖÔÔÔÔÔtìØ1¡g‰§M›öïÿ[ôö)º]R>,‹Ë嚘˜˜ššr¹\þ£Ñzzz_}õÕÇÛÚÚnß¾FQH‚ æÏŸùòå–––¦¦¦ .ï(4‡ÉdÖÖÖ ÔÖÖÊ6Û bNž<9~üx##£Y³fuvv¾zõŠâÖòÎ;ïœ:uJy…yöì ³êýñGí® ´Òe­44•£££Í‡~XSSCÝ †káJ„Vp-tt@^¼½½¯]»víÚ52ü˜<}f‚ ^¾|èè蘛›«Ô’·¶¶.Z´èóÏ?'—”‹ÂápÂÃÃ7nܸhÑ"Áíýmm]Ç<NËùwÊ”) ÉÇØØøÍÞ^…äC[-\HÃRA+h%X*#¯ëøñ¾‡e¸ü5.UWWW]]]YY™§§ç²eË$%ƒkáJ„Vp-ÀxýúµjŽTRRBÄ oXZƒ¤3Ùg@±\¹rEðmºYYYA5Ë$¹I%++ëoûÛ×_½aÆ¢¢"™/ê+¢¤¤$22rÁ‚ñññzzzbsðöönjjºxñ¢———h‚;vìÙ³G~ŪªªÜÜÜ"¾³ê.(0ö÷§[© ´*Uww· û677———“û3FpÁÛ\K±7wiîã½½½ööömmmp-\‰Ð ®EáZJQ—²@‡h+úúúýýý¿üòËܹs}}}+**”t “'O;vìèÑ£Ó¦M£H–““óóÏ?GDD\¿~ÝÚÚZI…QÔÍX±Y)ê¾NÛ B+]ÖÊÊÊJí¡\dlsww:tˆâÁ¸®Dh×BGƒÃ,»ººÞ»w/??ûöí¢‘À¥ÿÈùš*Y½z5AóæÍ#ÿmjjzã7ÄÞWBCCÛÚÚ""".]º4bÄeˆñ~h­T\*Q×|EõHãZäWÇŸ6mÚ1É/Â…káJ„Vp-tt@1Y­<ýÞØØØ÷ßÿ­·Þ 9±<m”2À)ù!22222Ryb ZA+Õ—J½mD¸®Dh­4ÚµŒ †Æ‚ .^¼HF‡Ò‘§ô«ªªh˜U÷Ð_/¬Y„VÐJ§xõêUMM ùJ’êêj9ÿ*$Ÿêêꪪ*…äCÄà hX*h­KU^^^SS£À·© £µ…uQŠ ë½ç,SX¡˜yp-€ö¹–RÁŒîÐx\PðXq½•¡À—˜Ïœ93::Z×ÄÌ´‚V*.­ž„káJ„VÐJã\ kt‡Àï<Þ¿ ‚Xvá‚Áðá ¢´´ÔÛÛÛÒÒ’ìô–––Ê–Ueee^^ž® ˆUIÐ Z©·Tg®DhטÑEÇŽuÖ×wÖ×I$ -Šz‰ùÊ•+÷ìÙ#ÛS=𠯰¡´Ro©Î.\‰Ð ®…Ž®R謯ÿùèQòóÏGvÖ×C4ˆ &”——çåååååUTTP¼àqPRRR’““íìì˜ÿEÄ6´‚Vê-•. Ø`Tä°¦BÂ󸹹)*h×Ñ‘†¥‚VÐJ°TF5dh !)))!!AR”ÔÚÚÚ¥K—–””øøøddd8::ò¿:·zuõ­[ü]gÏþààAÜ“ÐÖePðæ@h­d.•2káì•­´Àµ”Šn­Ñ½ÿ~jj*E‚Í›7ûùùåææîÚµ+66öĉü¯ÈníŽ;¶nÝ*IíììR)fõŠÃ1;–n¥‚VÐJ!¥jiiáp8¤q;–\¯ ¤3oÐ Z©²TwïÞuvv¶³³“µ\ g´Ò8­.\8äìPTÁΜ9£®¥C.óx¼•+WîÚµ‹"MAA›Í622b³ÙùùùÊ+̳gÏh˜Uï?jw¡•.kUZZêááäááQZZŠVàÀª$h­TY*//¯æææ[·nUVVòxÜÝÝÝßßßÊÊê¡L|š…bƒQ ÏSUU¥¨ A/\ a© Ý´277ÒßÉff2ì%ö¯………<Á¨håZ:ŒŠìåJ Fµxñb¯¿þz÷îÝÕÕÕB   ËÉȶ`C¨=¬‹¹¹ySSÓðáà ‚àñxvvv2ª¸p-Í®€b›p24ù´#•®¿^HhjwçÎùùùvvv·nÝJLLTÞq1† ­h^A™WhÐV«+"ÈœÕøñãSSS»ºººººöïß?~üx]pK\‰Ð Z©·Tg®Dh% :¬Ñ¥!ºu™Dp:Whj×ÉÉéæÍ›*(ÖB@+šWPS”ôÑ*$$D!Å8vìØúõëwïÞMÄÛo¿}ô¿oØÖnp%B+h¥ÞRœ]¸¡•$è°F—†èúŒ®ºÀÈ´¢yµoFWŒ;677—Ëår¹ÜK—.UÐË“àZ¸¡´p-\‰ÐJ0£‹Ž.ÀÈ´¢yµrFàJ„kA+¸Ql0*2ƾB‚¹¹¹)*”×Ñ‘†¥‚VtÓj¨A¤ž²X„‚‚QµµµÉŒŠVèb0*yPT€„ªª*EÝü˜UwA¢îîô¬ ®i%O$ ¡¾®ÌÁ¨®ºÀµàZÐJ³´‚k B®…+ZÉÖ„óPÔÓËiiiFdcØÐŠæÄŒ.u+â_¸®Dh­”Q*yBè)ÊúH(ÒÔÖÖ²X¬ÀÀÀºº:¸®Dh¥š¬è¹FWí®…Ž®´àÝnx·›&j%Ï[ÔD·ÓA+º=NóÛo¿9R<+¸ ´Ro©Ôjž:ÍæÍ›ýüüêëëýüübccáZ¸¡•j²Â]±àÑå¡w» ïÑ•)Ê9räÚµkÕÞžƒk@gds-ÑÉÁÐñêzt™úÁcgg碢"kkk.—;uêÔ'OžHÊ¡¥¥eÉ’%‹-Z´h\ ¥6ádhò©Ìµ” ftÕFΠÍ+ˆ¨ËbáOhtý—gϞѹ— ׂkA+ÍÕ*äh„Q´··[ZZΟ?ŸÅbµ··KJÆápÂÃÃ7nÜ(ÔËíokë:~|àùóîü|‚ äü[UU¥|‚¨>sF!ùtççwаTЊ†ZMêï—þ¯ÏÀÀÒSü5bÄï­­]Ç÷=|¨é®…Ý¡QF A`F—ÏÝ»wíììÈ`‰¢ indÐH*p-€œ®%Ê•+W4bF·¸¸ØÊÊŠzF×ÛÛ»©©éâÅ‹^^^p-”Ý„“¡É§2×R*˜ÑUÆV4¯ –Íèzyy577ߺu«²²’ÇãÉY êåµàZÐJk´ÒˆIÝY³fŠzzÙÂÂ"--Me®¥Tth.Ùkíééùý÷ßÍÌÌĦ)((HJJ222b³ÙS§NU^a0† ­h^A-žÑ•Ÿï¾ûŽ ˆøøxþ–}ûöÚ¤ W"´‚V*.••••`QÈ•´/_¾|ï½÷8ÎáÇCCCµ¾á‡³ ZéŽV4Y£«@×RºµF—ÉdZ[[GGG;vLlŠ÷¿áÝnÞí¦iZÉù5¡-4ÑJæw»). h·b´‚V*.Õä,LnnîðáÃ333t¡Õ‡³ ZéŽV4Y£«p×’{îË—//]ºôí·ßþôÓO¢ßúþ7¼Û hx®ôhîC€ƒ¾Ô®€†ºù Jþ‹(åy®™™Ykkkll,‹ÅŠŽŽ¶´´ͧp-4ô=º2¸–RQÿŒ.¤þåË—ŽŽŽ¹¹¹J:ЪU«jkkõõõõõõ%­®–òýoòƒ‘3hEó jeÔe¥š˜ {‘È™FeàJ„VÐJÅ¥ÒÓÓëêêb0ÝÝݲ/7suu½wï^~~þĉ úûûéÙ¸Wl0ªÂÂBBAAƒªªªÊèá… 4,´¢›VC "5ÙÌŒPP0* ™ƒQ)е‚úgtÉ鈬¬¬¿ýío_ýõ† ŠŠŠ”Q€ÌÌÌ]»vÕÕÕ3fÇŽóçÏ'D&Cjjj¢¢¢JKKÉ÷¿‰FÆÇ(#ÐÙá@-›Ñ•gnD¬‰ÉÜIt_ê4˜Ñ€þÈæZ>¬¯¯;vluuõ«W¯ÆŽëää$›kegg¯Y³æ­·Þ:w¡¡<®¥àZh茮 ®¥TÔ?£«¯¯ßßßÿË/¿Ì;×××Wy/êøä“Oþýï?þü§Ÿ~"{¹„LïSÆV4¯ ftÅv)ù¡Ã±¥¥åwÞ9uê\ ®­´F+//¯   ''§€€òƒÌY-X° ¡¡áâÅ‹†††„¶GÎÃÙ­tP+š¬ÑU kiIGWS§Q,ˆn­h^A틺,tÁ¦!­bPq8œððð7.Z´Hp»bCè¹¹¹)*¨˜Mm-¡ lÆþþ4,´‚V‚¥’9„žS©=¬ ÚZh?@+Q—5ʵÔÿè²n>Nƒ÷•A+TïÑ¥öb™]²)µ?ºìííÝÔÔtñâE///¸\ ZÑP+™Ãºm‘Ùµ>ÿüóÜÜÜŽŽ~«m-\‰ÐŠÎYiè{tep-¥¢þ]Ý|œ#gЊæÄ{t)²)µ»VNNÎþýû#""¸\.\ ®­´I«äÉçÎ;999ííí4šbƒQ1 BAAƒÜÜÜʈëèHÃRA+ºi5Ô ROY,BAÁ¨ÚÚÚdF¥@×R:÷z!Œ2bäLw´ÂŒ®ô¨þõBB‹{Év§è\ñ =jþ.'Ož<~üø¥K—FŒׂkA+Zi¥v×Zºté?ÿùOZС­…+Z‰f%sË&3º2¸–RQÿŒîíÛ·}||LLLÄ6ì0ʈ‘3h%VòŒç‰n§ƒV22¶´´Ü¹sçêÕ«W¯^½sçy-ËÆÌ™3£££å´”®?"¶é)6h>ä‡ÈÈÈ›7o õrfÞ ´Ro©ä¡ººúÖ­[4ŸÑ…káJ„V²A“5ºtCý3º&Løî»ïÂÃÃÅNh`”#gÐJæ¬0£+Èõë×½½½---ÉNï¿ÿýïÀÀ@êQFI®eccS]]ýÆoh„ËõàZÐJC]‹š¡FÚ‚¶®DhEì0£«XÔ?£[__ïçç÷Ÿº»{äÈ‘„€‘3hEó jå]Æ‘³+W®Ü³g2îp-\‰Ð Z)ƒ.àZ8» •6e…]švtgΜyþüy‚ z{{¿ù曹sçê‚ùâ}eЊæÔ¾÷èN˜0¡¼¼!›Â]\‰Ð Z)£T¥¥¥AAAAAA¥¥¥ê5‚‚6›mddÄf³óóó%%stt´±±ùð\`¹¹ IDATÃkjj¨Äï¼óΩS§àZ¸¡•üÐdF—n®¥¶Ž.?î_ttt\\\}}}}}}lllLLŒ’Ž˜›››™™ÙÐаfÍš%K–ˆMCŽV’ ccc•W}ŒœA+šWPûftüñGOOÏ_~ùåîÝ»cÆŒ!®D¸´¢·VŠŠ¯ÚÛÛ---çÏŸÏb±ÚÛÛ%µîêêêÊÊÊ<==)Vâq8œððð7.Z´Hp{[[×ñãÏŸwçç!ç_777…äC„Mm­BòéÎÏ7ö÷§a© M´šÔß/Ûß==™÷úëÜÜü{kk×ñã}jºk©?•••U}}ý°aÂàñx£Fâr¹J-I}}½··÷³gÏ …¾rvv.**²¶¶ær¹S§N}òä‰P¼ÛM£+ˆ÷èi}zRR’òúº%%%‘‘‘ ,ˆ×wB :Z‰QFŒ2jV˜Ñ¥îÙ’Q©(F%¹Vggçºuërss ‚ MNN611¡m®ׂVp-…PSSUZZêãã“‘‘1jÔ(Bä]AdÌ‚áÇO›6í¯ý«Ø_„¿ËÉ“'?~éÒ¥#FÀµp%B+Bógtep-¥¢þ¨Ëîîîßÿ½ tòäÉcÇŽ=ztÚ´i’Ò£•_ýõ £•Z9„‘3hÅGt|xfffBB\ W"´‚V /Ù@Äã'8»p%B«A¡É{téæZêïè–••}ñÅ&&&&&&ÑÑÑeeeº`¾X ­h^Aí{®Ñ××ïïïÿå—_æÎëëë[QQו­ ]K7ÁÙ­tG+ú¬Ñ¥êïèŽ?žÿz¡ýû÷?^Ì#gЊæÄŒ.®®®÷îÝËÏÏŸ8q¢A?\ W"´‚V /Õ•?‚Ž+Î.\‰ÐJ4™Ñ¥›k©¿£{ìØ±ëׯ»¹¹¹¹¹Ý¸qãèÑ£º`¾9ƒV4¯ ft)ˆ}ÿý÷Y,V@@€î4q%B+h¥âR…ü]ðÅ£b0„‚‚¹¹¹)*”×Ñ‘†¥‚VôÑJ¶ ROY,BAÁ¨ÚÚÚdFE7×RGwìØ±¹¹¹\.—Ëå^ºt‰¶‘¨ ‘5Q+ybîIúV½ZÑ!à‚ .^¼hhhHH÷: -ãýÐ Z©·Tº€¡¡¡“““¥¥%A®®®rþ%ã­ÊŸ««kUU•Bò!º®Ž†¥‚VôÑêùóç2üunm•y_¡¿cÆŒqrr²³³ÓtKQÔeÍ‘¨Ë‚´´´p8r_ccã±cÇ’m)Ä/Ð͵¨k€–›’9ê² ®¥TÔ?£›••åååebbÂü/ºpc ZѼ‚Ú·F·´´ÔÃÃ#(((((ÈÃã´´Tæ¬nß¾íããcbbBþ ו­ •2J%´Â ËtqváJ„V’ Ï]Z¹–ÚmhÆ ß}÷]xx¸¾¾¾î˜/V%A+šWP+×è2 riœ°ÙlÒµtªÉˆ+ZA+Õ— [œ]¸¡•4Ð'ê2­\K… Ñ©^.‘3hEû jßŒî„ ÊËËóòòòòò***&L˜ sVõõõ~~~ÿ©]w÷È‘#áZ¸¡´RF©Œ 1>E×´’-ºÊd33BAÁ¨,,,䉇B+×RÿÝÇwvv²Ùlh,bÝÐ °FWz†´Ú-,,lñâÅŸ~úissó¶mÛžj0*…k¥ üg˜I†ÖeëÖ­&&&Ÿ}öA‡êíí§§§ÁµàZp-ðPT_×ÂÂ"--M6׺~ýúèÑ£GÍ`0zzzÊÊÊÞ~ûmÙ\K³€kÁµtV+™=Ps]K©`F÷Ìš5ëàÁƒ<ïСC³gÏVÞ0Þ­h^A­\£«(ÒÒÒ¾üòK“èèèÔÔTÙ:¨ô ‡+®×úCwQ93ºçØìŽ¡\ÈÓ§Owuu%§FŒ!ÏKÑ‚4Žו­Ô’\K×;ºü6¥`ãR¨•¹sçÎüü|;;»[·n%&&*¯0X ­h^A­º,O1ÆŒ“ššÚÕÕÕÕÕuàÀ2!w§NC†Ç#_Û«^ Å•­tǵ”´Ú­:?ÿDxøÝƒ—îq¾Û·o ºÖ7ÔkÒ8\ W"´RKVp-]ïèvýþFÁ4NNN7oÞlkk»qãÆ¨Q£”WŒœA+šWPûftCþˆ<ÅÈÈȸy󦛛›››Û­[·”ž]úðx---ï¼óΩS§àZp-¸–ü(oîï½½…©©'Þ¿úÖ-)‹o_joGIãH* ê‰+®­àZƒ¢þ× é&X ­h^A®v£§Vòàîîþý÷ß«à@üðx—/_ '‡ÃY¾|ybbâœ9sÈ-ååå™™™¯y¼ßŸ<ùw[ۺŋõmmû›šäùÛnhhöê•üùèÛÚvTVšº»ËŸOSÓëžƈt+´’Y+‡aôO« k×þþ䉾µµ)ÿ¢}ýšPU„;’”®% \ ®­äñ@¸:º4#gЊæÔú5ºñ¢33³ÖÖÖ¼¼<.—kff&)Ù²eËšššlllø[ÆŒ“@~Þ±cÇÖ­[å/ÌÓ§OR/fÕ[XhäëK·RA+h%¶TIãÇMY¾|ê§Ÿi\ÓEG’ÒµDkáJ„Vp-…ƒ`Têk! Í+¨•kt)þ¥'R†ÇËÉÉÙ¿DD—ËU^aìììh˜Õ«Š í® ´Ò&­\gÏ^zþ¼/›-e{‘ÿ à¿4w$•õÄÙ…+ZÁµ3ºê³”ЊæÔÊ]Zun…Bâ‘ñ„Þ´sçΨ¨¨””ŸŒŒ IYYYY…††¶µµEDD\ºtiĈÊ(ð°aÃh˜ÕÈÐPí® ´Ò&­>8xPNËRo«Q¬#ÉæZªW"´‚V:îZè説;…V4¯ V®Ñt[µwzņ\OÊ|"#####E|øá‡Zì¥ú––¸¡@+mÕŠ´¬¾¾¾7n„„„¨ÝµÄ:’ ®5(p-­àZ .KË«W¯jjjZZZ‚¨®®–ó/ù‚)ùó©®®vssSH>ApiX*h%Væææ²ýmkk“ô­zµ*//¯©©ill”§—KhÈ]…àåå@Cééé¹ÿ¾¾¾~{{»®LNÀµ€k)ÆkUEÐ*))!ÂÝÝ]£¼;vìÙ³Gþ|0K ­TPÁ… ʜŌî™3gÔ«Uww·Â/í¬¬,‚ –-[¦}®¥¡ÔÖÖ.]º´¤¤„|ÒÑÑ‘ÿ°7Åû‡OŸ>½k×®ÚÚZ//¯;wúûû‹æ­IJJJHH “é¦V¢çŒ4{©F«ÇWWW;88XZZþúë¯ÎÎÎcÆŒkÁµàZp-mr-¥‚]õ€u§ÐŠæÔ¾5ºWD€љ͛7ûùùÕ××ûùùÅÆÆÿ}:õ^¹¹¹™™™ kÖ¬Y²d‰Ø| Ÿû÷罹¦Rä£ Z‰ž3Ò쥭ÚÚÚf̘1nÜ8KKË   Áö"€kÁµàZp­AÁŒîÐÀŒ®FW3ºÒ£­3º§­­mòäÉB 07B7œ‹ŠŠ¬­­¹\îÔ©SŸ}êìì¬z)0«ÞÂB#__º• ZA+±¥"]ž²|ùÔO?•æÑe ;èP0*ia«l¡¶ ³zUQ¡Ý„VÚ¤•ëìÙKÏŸ÷e³¥ìå’*óW|nh:4£»sçΨ¨¨””r6¹Qhé¶Ø4Ê`ذa4Ìjdh¨vWZi“V<8¤}©ƒQtt5''§›7o m æ&6 ~ø¡K¤oi‰KZi«Vd·¶¯¯ïÆdT*ü4èè‚ ///ˆ€†ÒÓÓSVV¦¯¯ßÞÞn`÷ÐZô .P[[Èb±ëêê‚`þнNŸ>=qâD ??¿‚‚±ù@+A’’’øÉtS+ÑsFš½T£ÕãÇ 'MšTTTää䤨ü™’© ¨­šT•'NQ]¾^àZp-¸@Gh6›7oöó󫯯÷óó‹%¢««Kì;¸ÉÍÍÍÌÌlhhX³fÍ’%KÄæ­øÜ¿?55•"]ÐJôœ‘f/ÕhÕÖÖ6cÆŒqãÆYZZ3Fá‡ø³8†Ô&àñxk×®µ·····ÿâ‹/úúú†Ú¤ÈÉÉ °²²’¦Á100BìÒ¥KóæÍc±X£FZ¼xñÓ§O%•“ ei.U@JÛA[ÀµàZp-ÐÑÕi Øl¶‘‘›ÍÎÏÏ—Ò_Nž<9~üx##£Y³fuvv¾zõJÊ|tP+·råÊ]»v 5-ÓJôœÕJt/Õh5yòä‘#GÒ¼Mðí·ß644Ü¿ÿþýû555IIICÚ=''gÆ ›6mª©©‘¦Íñí·ßu,--mãÆ555eee“&MZ¸p!E9»€ñ eÀµèèÚÑÞÞnii9þ|‹ÕÞÞNqÝØÚÚºhÑ¢Ï?ÿÜÐÐPÊ|tP«íÛ·7.""b¨ùh¥V‚猨V¢{é‚VR’½mÛ6KKKKKËíÛ·Ÿ={vH»''''%%1bÐÄ?ÿüszzú‘#G¨“}ÿý÷ÁÁÁo¼ñ†‰‰ÉçŸþøñcùËÙÔÔÎb±|||îܹ#8~gcc3sæLŠ”UUU3gδ¶¶NHHࣈ“‘t ±CW ÖÖÖ~~~UUUü‚ øŸããã­­­}}}+++©k*Z£ŠŠ  ÿŠ¡¿y‹¬£¹¹¹¯¯/?tbNNÎÛo¿mnnÎW@ô5‡vrrrvvþç?ÿIQNI‚ˆVÀµàZp-¸@G¨33³ÖÖÖ¼¼¼ÖÖV333éw,)) ˜={vbb¢<ùh½VÈÎÎæ{´.k%tÎH³—.h%%OŸ>%Ÿ©ž?þ¸qãÈ'¬¬¬®®ÎÝÝÝÊÊ*44”¢EÒÙÙ¹|ùòØÚÚJŸÿ¶mÛ>øàŠrº¸¸˜››{zzÆÄÄtttHÊ'::zÑ¢E {÷îe³ÙBßVUUM™2…"å† ÂÂÂjkk[-b'd¨$„±±qmmíûï¿¿aÆAÛmµµµ ,4¥hÖ­[ÖØØºnݺ¡ž!d5[ZZöíÛ÷—¿ü…ܸjÕªýû÷777óEý0è Õ£G’““ãââd.§`5\ ®ׂkµÃxýúµjŽTRRB„»»;DW=‹/öððøúë¯wïÞ]]]-é5ÂBœÐJð^B&ÓM­DÏiö¢ƒVYYYA,[¶Lf×b2™b×¶eeeQœ9Bç‹Åjll$Ìëëë³··oii!Æ¡…²Ý=&&fÕªUGŽ9þ<Lt÷åË—›››“ï f"é@===k×®}ôèQnn®©©©¤r100@>X[[{þüy±µ¶±±yùòånB Fgg'ÿèÆÆÆÔ)­­­+++MLLºººìíí‹*$ˆ¤‰ý!ÈÝÝÝÝŸ={&ª ùYlJŠl…jdmm]UUÅd2;;;ÝÝݹ\®4~ÂçêÕ«[¶l©¬¬ìïïç×èÝwßmmm ˜Üâè蘓“óòåKÁ€ê‚)g̘A§(S¦LIOOçñx‡ô‘9;;»¢¢"Á-½½½–––}}}»wïæo444œ?þúõë7oÞüÛo¿ 6ÿbË)IׂkÁµàZ]ÿi^ˆ>)°qãF[[[ooï &888P,¦»ûæÍ›kjj¼¼¼\\\®]»vôèQùËùÙgŸÕÕÕEEEñ㦼xñBl9ÉoMLL¦NÚÜÜLñzJJJeeåèÑ£ :"6eRRÒ… »»»õôô(‘þ@A<þÜÁÁáìÙ³ûöí#·$$$DGG7Nhé8?åÞ½{‡ªgrrrvv¶­­mNNÿ@’ˆ‹‹ ,ù®]»"""<==…N‹õå—_:tˆ¿ÍfÏž=[¶×uˆ-§$A\ ®ׂk‚5º ˜Õn’¾Â[+”ÁÀÀÀ¥K—âããïÝ»§<~&ׂkÁµP±k)ˆòƒ‡*a2™ ÃÅÅO£ׂkÐÑ€:Zü¸ÚÖè@G@GwH0%ƒßL•¿‚BðtÒÑ”ÍÀÀÀ’%K4´ðK–,QY€=—Z ôÀµàZ £«þ,Ž!õdx<ÞÚµkíííííí¿øâ‹¾¾¾¡v„rrr¬¬¬¤é& „„„P'»téÒ¼yóX,Ö¨Q£/^üôéSIå¤C÷^k–HóÎz]èÁJɵkׯ§ì£HyŠM)i÷qãÆåååAjiüMJ©Åº–$ÓÓ,ýšîZb Jl êÖ­[––––––sæÌ¹}û6\ tt5¸'óí·ß644Ü¿ÿþýû555IIICÚ=''gÆ ›6mª©©‘¦§ôí·ß6Œ:MZZÚÆkjjÊÊÊ&Mš´páBŠr ½Õíëë«ì£HyŠM)iw__ßììlH=¨¿I/µX×’dzš¥?@Ó]K’A‰¶ –,Y²víÚšššššš5kÖDFFµ@GW³íxÛ¶mäèÝöíÛÏž=;¤Ý““““’’‚ƒƒGŒ1hâŸþ9==ýÈ‘#Ôɾÿþûààà7ÞxÃÄÄäóÏ?üø±üåljj g±X>>>wîÜáog2™qqq6663gΤHYUU5sæLkkë„„þÀ§ØÁTI…Éd&$$X[[ûùùUUUñ7 &àŽ·¶¶öõõ­¬¬¤®©h***üýý-,,üýý+**†z†u477÷õõ½yó&€ãí·ß677}“»”̇vrrrvvþç?ÿIQNI‚ˆVSÅüøãBöL&sÐJµ¶¶Ž=úåË—ü½^¾|éêêÚÚÚ*çå)6¥¤ÝÇŽûÓO?iŠG©Fj9=S¬kI2=ÍÒ é®%É DqvvÖ××g0 C__ßÅÅ®:ºÌÓ§OÇŒCÄüùóÇ'øÄ4”••ÕÕÕ¹»»[YY…††Rô£:;;—/_~àÀ[[[éóß¶mÛ|@QNsssOOϘ˜˜ŽŽIùDGG/Z´¨¡¡aïÞ½l6[èÛªªª)S¦P¤Ü°aCXXXmm­`_Kì`*õ„066®­­}ÿý÷7lØ0ho³¶¶vÁ‚ƒ¦­ÑºuëÂÂÂCCC×­[7Ô3„¬fKK˾}ûþò—¿W­Zµÿþææf¾¢¨iooôèQrrr\\œÌ嬦Šihh°°°j¥X,–¿¿ÿ¹sçø»œ;wÎßߟÅbÉyyŠMüllT IDAT)iw‹U__¯)¥©è™|×’dzš¥?@Ó]K¬A‰mAýïÿþï–-[¬­­­­­·lÙò÷¿ÿ®€ÖÃPÙü’’‚ ÜÝÝ¥IÌd2Å®ÈÍÊÊ¢èi0™LÁoY,Vcc#ùd]__Ÿ½½}KK !0{&”•èî111«V­2008räÈùóç Äî¾|ùrsssò1?ÁL$¨§§gíÚµ=ÊÍÍ555•TN‚ ÈkkkÏŸ?/¶Ö666üQRƒÑÙÙÉ?zcc£±±1uJkkëÊÊJ“®®.{{{Á¢ "é@bˆ††rwww÷gÏž‰*C~›’"[¡Y[[WUU1™ÌÎÎNwww.—K½»ÐqõêÕ-[¶TVVö÷÷ókôî»ï¶¶¶Lž<ùwÞ9r¤¤Ý%åÙ³g#GŽìïï777ç‹,ZN±‚ˆ­¦Š±²²ª¯¯|$UÊJݼyó›o¾ÉÌÌôð𨮮^¸pallìœ9sÄ…â´—&¥¤Ýûúú𛛇Z묬,‚ –-[&³kÑVjéO`ŠEȵ$™žÌúàZ2¸–XƒÛ‚úðçL™²zõêׯ_§¦¦‘o¸êu-¥¢Í3ºÎÎÎåååäçG9;;ó»ÒLÍ988¬_¿ÞÌÌÌØØ˜Íf?xð@ÒîYYYGŽ|À•â@åååsæÌ100ÈËË#íXR9 ‚ÐÓÓsqqÙ³gÏ?ü ñ'ÔÓãO? u>…:K)¥:WdÚ?’Â`0úûû ‚|xi¨Õh¨Ã4BéW¯^Ïårù_;wnÛ¶mÇ—¡dßX__ŸŸ§ØrR¢Æ^.yæ‹>N&M¥ššš<800ššÊår†zyJ™RÒî---šâQª‘Z~Ïu-I¦§Yú4ݵÄ”ØTAAÁºuëLLLLMMׯ_ÏoVÁµÐb´¹£»`Á‚„„„ÖÖÖ–––„„„>úhH»‡……%%%utttww§¦¦z{{KJ)ó€¢ ™™þÕW_¥¥¥ñ§ ©ËÉårwîÜ9uêTþ¡e¢sæÌILL|ñâÅ 5›Ò××÷àÁƒ</==]†Ýʼn ˆôôô¾¾¾ôôôéÓ§“[srr^¾|™šš*6åŒ3¨óeÊ”)ééé<ïðáÃ>èkggWTT$¸¥··×ÒÒ²¯¯o÷îÝü†††óçÏ_¿~ýæÍ›ûí7Á¹à¿CBl9% ¢v¦OŸÎápd¨ƒÁˆŠŠJIIY¸parrrTTƒÁáòúéŦ”´;‡Ã™6mš¦x”j¤¦@©Åº–$ÓÓ,ýšîZb Jl ÊÍÍlÔuvv&''{xxÀµ@GW3.hãÆ¶¶¶ÞÞÞ&Lppp X*v÷Í›7×ÔÔxyy¹¸¸\»víèÑ£ò—ó³Ï>«««‹ŠŠâG{zñâ…Ør’ßš˜˜L:µ¹¹9##CRž)))•••£G4T’Ø”III.\pttìîîÖÓÓ£Dúñüùs‡³gÏîÛ·Ü’=nÜ8333±)÷îÝ;T=“““³³³mmmsrrø’D\\\XX˜`ÉwíÚáéééèè(t2°X¬/¿üòСCüíl6{öìÙ²½dHl9% ¢v>øàê`c•Z¼xñ°aÃvïÞmdd´hÑ"ŠÝ¥¿<Ŧ”´{aaá‚ 4ŸT#µ¤hj?þø#ŠBj±®%éXš¥?@Ó]K¬A‰mAeddܽ{×ÃÃÃÝݽ°°ðĉp-´ú®Ñ•ôÞµ£ .]ºïÞ=…Œ;àgÒè“aéÒ¥ü@šEdddFFÈFzÔ²ÚM½R‡††þÏÿü¿¿?ôÀµàZè k)z n’*a2™ ÃÅÅ…nÏе §§§¡½\‚ Nž< ©¥$77WÇõÀµàZh1(|Xã5‚G5 £  £  £  £ ]]€v¨ô=ºYYY )Àµp-ÐDT7£ÛÑÑQ]] ÅÔ^„kàZ ‰0^¿~ h X£ ]]@@KII DÐ–ŽŽŽÙ³gC ft4¯—‹Ë@ft€ wwwˆ xY.Pƒ]èèèè*kthÇ•+W„¶„„„@Ìè &“©A¥½|ù²£££h™Ån!Ñh¹4ë×Ò2wî\tq]€ "11133³««Kh»Ø-¢5-¨¥§§çþýûúúúíííx ÐÑ Ûp8___è MŸÖ…ìÇOš4©¨¨ÈÉÉ ç-HFh!<3`@£ikk›1cÆÈ‘# ‚ ‚ 0$0£ à0EJððáÃÀÀÀÊÊJê|^¼x±råJ;;;;;»Ï>ûìåË—üü8àââbggÇf³{{{ÉíýýýÛ¶msss³¶¶^²d ÿY\&“™““3iÒ$ ‹iӦݻwOšò ~ª—”:H*$zzzV¯^mggçââ²ÿþAó)..~÷ÝwíììX,–¿¿NN—ŒŒŒéÓ§³X¬ñãÇŸü%ý^’ŽKØòSœBŦþÝ%•_ûhnn.((¸"Ü ÐÑ #]ÄÇÇÇÄÄ~[XXÈf³322ÜÝÝ©ó‰åñxeee<èî‹ãõóÏ?ß»w¯¬¬¬««+11‘ܸwïÞ_ýõöíÛÕÕÕ¦¦¦±±±üô.\¸|ùr]]ÝG}´nÝ:iÊOˆ,¾êZ\ŠòˆeÛ¶mÝÝÝeeeÅÅÅ?þøã ù|üñÇ+V¬¨ªªjhhسgOff&¹=---==ýàÁƒMMM—/_ÌŠ ˆ;wî\½z•Ãá\»vmÐz‰ÕYHþ¾¿—ØãJBRù©ó‹Øß]Rùµ£y`¼~ý*€(%%%A ÚÓbvïÞÝßß¿eËò_&“yúô阘˜‚‚++«Awwww¿qã¹°ðéÓ§óæÍ«¨¨ ó)++#·×ÔÔq8‚ ¼¼¼rssGMDssó´iÓª««ÉôUUUÖÖÖAðx<[[ÛçÏŸzt&“)¶ $ývIå‘„‡‡Çõë×ùõ}óÍ7É %å3nܸO>ù$00œìÕÓûÏг··÷éÓ§Ç/¶ÇÞÞ^šòKÒYRzI¿õqE‘T~ŠóA°$ü©wI¿£–ÑÓÓSVVöüùó)S¦Ï›7ÿUVVAË–-ƒW€XT·~‰l2=éèè˜={6tà“˜˜h``Àïå’8pÀäÿ³÷¦aM]kÿÿfRT”@dA['´"Â98bkO뀵¶U‚-íe-2(Z=G¤TÁ =åá*G-J-ŠZñ'pj±E•I$Ì3"ð±ÿÍ/'!Ù ;É÷ób_agíµ×ú²÷{M÷24üõ×_?øàƒs¨««³²²"?[YYÕÕÕñ¾â?_[[K~~ùòå„ xi´´´xŸÉÖAÇóærP‘p¹\þz ˜Ï™3g¿úê«’’’‘#GÆÇLJ„„QYYéìì,î.R¶6%è,ÃÿkP÷W~Éù‹dHþïôáÙ³geeeVVVvvvwïÞµµµ…¯€¯%=˜º Dss³äÁ:M#&&F__Ë–-ç322Nž<¹cÇþ±Aq0™Ìªª*òsUU“Éä}ÅžÅbñšR/_¾äÍGmiiQf•uuuy«Xe+›Íæ¯×€ù¸ºº:t(//¯ªª*!!aãÆ¼F 4òJƒHyLé’ðÿâÊ/.]]]ÞzÝW¯^I#µŸ’F£rqqa±XNNN0MøZƒøeWr 5y €¶“Idd$‹ÅŠˆˆþjĈ¶¶¶‡Z½zõåË—Éx°â˜?~TTTBBBddäüùóy_EGGÇÇÇkiiEEE½ûî»äÉÐÐÐõë×oß¾ÝÂÂâþýû{÷î=yò¤Òj=~üøüqÕªU:::²•gÉ’%ÑÑÑûöí#ëË;/.Ÿ… r8œ3fhiiµ··óÄäp8ëÖ­ûþûïÇ_SS³wïÞï¾ûN¶J‰Ô™×,¿téÒܹsy#Ìþ_ƒB\ùÅå?~üø„‡‡·¶¶ Ì Ü­ P~õƒËår¹\þ3R®Ô…¯€¯E`D€‰‰‰Û¶m“uÙÏÏï£>â@ŠcÇŽ:::®®®nnnÆ Û±cï+//////WWW}}}^P¢Í›7»ººš››oÚ´iùòåÔÖK\4f²=fllÌûj°å‰ŠŠÒ××wss›8qâ”)SxçÅåÃáplmmíííOœ8Á FµnݺU«V}úé§l6; `Ò¤I2×K¤Î$111ÇÐÐw‰„ÿ× W~qù÷Ýw§OŸ¶²² Z°`”w.¿ú¡¥¥H†¤ Tã&=(ÄŠ*mæºh‹È°.°Z BCÂAg 'ùùùÖÖÖÖÖÖATVVVVVz{{ÃjÔÉ×R(º€nxxx<~üø?þ ÂØØ˜?¤4t€ ";;[à ö¥¤qG=–¨i÷Åÿ]3_®©S§ò1X-|-4t¢~t)tbäÏçiÁµBšÚׯ__½z5((HØÙáí£ä…ª·^…¬B‹ « àÿ¨}5±ü¨º¯…†.Pa'ŒÌA#‘jéìì,**ÒÑÑijjÒÕ…õÐôÇU÷Ó|-4tP*è¡ÔLž={VVVfeeeggw÷î][[[h 3SþÐî…þ!|-4t@†††éÓ§“[•@5öµ°.C?ö÷÷/))0Ÿ÷ßßÌÌ,222 €Íf:tˆ?áô™™™'N455:ujaa¡ÌU8yò¤——‹Å .//篗pz{{cccØlöÊ•+yýyÊ“šš:mÚ4&“éîîž––Fžôõõ½rå ù¹««ËÖÖ–Ëåâq¢ \.7777›hP!‰J…üŸöööO>ùÄÒÒÒÒÒò³Ï>ëèèàå/p;ÉþIAAÁ¼yó,--™L¦ŸŸ_ff¦äôü‘þLgggXX˜¥¥¥Ýþýû…uÒOëîî^¿~½¥¥åرc“““¥YSÆ`0]]]ÍÍÍ%ûcëÖ­#óOJJâ×M¤žâò¬žâÒUñµÐÐ"hå#::zóæÍüßæççs8œÔÔTivê ÿùçŸ###ùåÒŒ’9‹Lîܹ .TVV¾ûî»6l¹ —/_>}útUUU```XX½ø+Hžß»wïýû÷oܸQVVfdd)¹<‡:zôhrrrMMÍ… nݺEž_µjÕ?ü@~ÎÎΞÞÅÅÅ—/_–ìmß¾½½½,çÍ›7ÌY\>ƒÕS\z *¾–V¿rî„MÌU‘Ý»w÷öö~óÍ7äŸ ãĉ›7oÎÍÍ•¦ Ç`0‚055%?˜››“Qk! Fii)›Í&¢»»Û¢±±Qš»HXSÑÕÕeccS[[+!½››[VVÖØ±cɾ¨©S§–••I(‡‡Ç‰'ÜÝÝîÕÖÖæêêzïÞ=33³>ø $$ä½÷ÞÃSDÈ “'O.((˜3gï+‘›˜«ÕzÓÝýïùó ‚øèÜ9ÝáÃñ  B\¹rÅÃÃÅb‘Þß~ûÍßß_í­–zø?ãÆ»zõª A/^¼˜3gÎÓ§O‰Â û'...~ø¡¿¿?98©­­-›ÿ#Οqtt¼rå ¯œãÇ—\qù8;;_ºt‰\Y^^îîî>àbWƒQ\\|ø›7od+ùÝ»wÍÍÍ †™™Ygg§äô/_¾œ0a9YÅÞÞž¿×\dy*++…ó100X°`AzzzSSS~~~pp0ž"úðìÙ³üü|ƒ‰'Þ½{—ü½T{î;ÖRUÕRUu÷Ø1<¨Z)TËÿ©««³²²"?[YYÉ<æÌ™êê꯾úÊÓÓÓÁÁ!##CfÿG¤?ÃårùË)M‘DæóêÕ+kkëAåC„@+W‚?V[[;¨rŠËg°z6= ›¯…`T@4111“v‚ÈÈȨ­­ýÛßþ6iÒ$‘½!xˆuu»ººôõõygV¬X=oÞ<ƒÑÖÖfii)pI?¿ß0zôèÛ·oHyG++«ââbá]‚ V¯^½fÍCCà  r->  Œª¥ªêNJ ùùNJŠûÂ…†Rû€!g„ Ož<¹wïA&L€&ªâÿ0™Ìªª*Ò˯ªªb2™<¥££ƒü%zõê•ð…þ‰««+ÜäÍ›7¿üòKDDDHHˆ„ôú?­VþrÊ,š¹¹yEE…œùˆóÇÌÌÌD–Sœžâò¬ž¦4÷µ0¢ Dill,lå ‚1b„­­í¡C‡V¯^Í ®0´Œ?þÇìííåéìì466>|xyyù¦M›„Íú¥K—ø'퇆†®_¿¾¢¢¢§§çÎ;K—.•|G‡³nݺ¼y󦲲2""‚÷Õ[o½5bĈ¸¸¸E‹áA¢êŒêĉžžž¦¦¦>>>¹¹¹Ä‡NH|eçÎ7ÝÝäç7ÝÝWvîÄc€ aff6cÆŒÀÀÀÀÀÀ3f èƒ ù?óçÏŠŠjlllhhˆŒŒœ?>Ïc9pà@GGÇ«W¯FŒEú' .¼téR{{ûëׯÛÛÛ:Ð…ÓKö„Y²dItt4¯œ2ë¶téÒ-[¶p¹Ü¦¦¦˜˜™óç-Y²„§ç¶mÛø=@‘zŠËg°zJNèïk¡¡ D˜˜¸mÛ6 ´ŸŸßG}´qãFÙòˆn'gi÷îÝûÝwßó²:|øð×_mnnP ¡ ÿ CÿÙÛÛæäälß¾]WW—?â·¢ #‚·ýzMM……AÇŸ:uê1En–‹QJhEó 誺VÂV‹lÔØ×BC¤EÀA”ÇGFF.Z´hÒ¤IÊ­-Ü­´ŽmŒRB+šWPýFtѲh¬¯E Fƒ#$$¤ººú—_~ÑÓÓ#4f…!4)̪-7W½+­¤ÇÔÔT½µ€†. ÆI F)¡Í+¨–#º_ ]%ñ,7÷u½¥QWW———wñâÅ‹/æååÕÕÕɜՌ3"""4M@ŒRB+šWPýFt5¶?¾ðµÐÐU6oº»‹û5.îMw7Ô@µxøð¡££c@@@@@€££ãÇeΪ¤¤dçÎô¯rOOOyy9ù3SVV&ç‘ÜÉSþ|ÊÊÊ(ɇ ˆZkk– ZÉ£•‰‰‰ldž†áótÐêÉ“'ååå/_¾„†¯|-e‚}tÁÍääü¤$‚ ¼9œéaaxšP!®\¹2a‹E—Ëýí·ßüýýyßjGÊÈÈHmmí¯¾úÊÀÀ€þÇ>º*]Aì£+=ê·®dÔu]øZh”¯¥PuYZZªªî¤¤Ÿï¤¤¸/\hheYP&L˜ðäÉ“{÷îa`` Ï–³‰‰‰A$$$ðÎhB<*¬;…V4¯ ÖèÂר¯E 4u™ñÒTTTøûû3™LÿÊÊÊÿê¢Ø¹“7‹æMw÷U˜¸àaff6cÆŒÀÀÀÀÀÀ3f˜™™ÉœU«š ÖB+šWQ—éÀÏ?ÿ>>UUU>>>‘‘‘ü_-NNÞTTÔñl**ÚTT´89YžÂP¸V‡Â¬zŠ‹iX*h­()Ý$ÈÆ‰'<==MMM}||rù|ñøøxɽxòƒQJhEó ªÙˆîÍ›7«««•¶¾Œ*222ÒÒÒª««¿üòË•+WŠô¬àkÁ€VðµÐÐrss9޾¾>‡ÃÉÉÉQÜ^½zEìºnÝRï B+MÖŠnd#+++==½ºº:<<œt" ‚xôèQRR’¢oQJhEó ªÙˆ®››—˽~ýzIII·ê„eúᇜûúúÞ¼ycll,Ò³‚¯ÿZÁ×BCwhjjb±XsçÎe2™MMMü_õ64´?ÞרHÈyœ22’ÉdFDD°X,¡TªX·nÝÖ­[Ùlöùóç¿øâ‹òòraÏ ¾𵔃ÆE]æÿÀ›ÌÃc×®]999–––ׯ_‹‹S\a°‚ ZA+%—*[™³rwwOJJ"»'÷ïßïîî®4;–––‘’’«­­MDss3ÿˆ ¬ÞDÕ Q—%`oo_XX˜““ãéé©««ÛÛÛ« ùúúþýï·°°Ø½{wJJŠHÏ ¾¬´‚¯¥4nDWNÐËÀ#[/cvvvPP¸o56R\\¼qãÆ»wïñöÛo'$$8;;+§î}s555£Fâ}%²¡ «TŒèJÏ ¬VFFFxxø¤I“Μ9£§§'yv|-€Ò|-…‚`TCzƒ ´ÚRɃ³³sVVVmmmmmíùóç•ÖÊ%„VáòZ¹„‚‡sñtA+úW#º ©®®þå—_ôôô”`.d¦§§§¼¼œÜ’¤¬¬LÎc~~>%ù”•••––R’AÏ£a© ´â/Õ“'OÊËË)Üýh¨Àˆîà@/#CÎèÂj  0¢+=°Z5ðµ Ft‡ô÷C+h¥äRI°¼OÞD5¨ Ft%·ŠÝÜÜ «…7ZA+µ÷µÐЕj§Ó»KQ2ÁÀÁÁªiµÖÖ4,´‚Vü¥¢ÃtQC\Fì ­h^A5ÛGWyºlÚ´)..®±±‘Ã!X-¼‰Ð Z)´Thèª zzz666,‹ {{{9äŒqùó±··/--¥$‚ Ø••4,´‚Vü¥rrr²±±±´´¤‰eøã?FŽ© ÝsXÁ­”£•‰‰‰lGSSSáóXíÆ?Ò¢£££QžFÞ ´ÚR¡¡«¡ 7ZA+UìeØ¥ŒÁ`Ìš5kýúõšÐ=çííMU‡…ƒƒUÝ(nóçÓ°TÐJ­e;644œYºté–-[–.]:´ZÉÜ=Gá.ß|óMRRRGGͽ#̞Ì0h…Ùs‚`Tƒƒª ¥¥¥T¹ìfÕ–›KU›„ž„Vê¡Õ‡u¡ùΰZxUW+y‚Q™ššŠ›½<بTô´ZQ^kµÎhÂöBx¡´¢¿¯¥Pt ¡¢¢bÕªU<ðòòJMMµ¶¶NóóÏ?GFF–——ÛØØìܹsþüù * FÞ ´Rf©nÞ¼ikkkiiIvÄ<]xÕ¯‚j¿FW4dQ.ÞDh¥öZIè ‡ÃÉÉÉ™ÆÆÆF[[[KKKGGÇÎÎNq…AÏ´‚VÊ,•‘‘‘§§çôéÓ ‚¸yóæÃ‡›››e.FIIÉ¢E‹ÌÍÍÍÍÍ.\XRR¢4;–•••žž^]]¾råJ‚ 222ÒÒÒª««¿üòKò ¬ÞDÍÔJýFtëêêòòò.^¼xñâż¼Š‹<ÿ–±1AXñTùZ” AÁ¨lmm ÌÌÌjkk§L™òüùsá4“&MÚ½{÷Ì™3srr¾ù曂‚THÈîƒu0*33³ªªªaÆÑÝÝ=f̘ÚÚZe*P__¿dÉooï]»v…™155ÍÌÌôòòâïžë8{vßï¿Gø¡Á¬Ym998âHóã·K–Ü×Ñ™ØÛKíñëŸ~yÇÕÉÉâ®:FU½ô=<¸?þ8|Ú´annƒzÓ¯\¹2aÂ2p:—Ëýí·ßüýýe³Z&&&555Ç'­–¥¥%…à”_ qHZ£;Ȩ{4ñµÐÐ¥†åË—;::nÙ²e÷îÝeee©©©Âi¬¬¬þýïÏš5+77wÕªU••• 2¾ˆn­ •Ì¥ò†î¶mÛLLL>ùä‚ >ÜÚÚ«4SöàÁƒ+V„„„DGGkÿ5U©££ãüùóÿú׿nß¾ «…7QuµRrÔeq·óêëÛzúô[-.—ûäÉòZ'''333Ù¬–OHHÏjeffÞ¸qCíºx¡•úiEs«E·†®E]ÞµkWhhhbb"u™<)°MÈ÷ß¿aÆÊÊÊ1cÆìß¿_q…ÁZh­†¶TòðÝwßÍ;³oß>B)AªÒÒÒŽ;–’’2uêTò̺uë¶nÝÊf³uttºåž.hEó R8DI‡ÕnA˜™™ñ·låáØ±c7nܽ{7Ao¿ývJJŠ&8~x¡•æhE«E74h®Íµk×®^½:fÌò¤€cºdÉ’¢¢¢æææßÿ}ñâÅŠ+ VpA+h5´¥’‡V1(áÖaaa………sæÌa0 £½½Ý××÷ïÿ»……ÅîÝ»ê¼âé‚V4¯ …kté¿” ˆl!dÎÊÙÙ9++«¶¶¶¶¶öüùóÎÎΚàøáM„Vš£M¬º€âÎôœA+h¥94­Gõá‡þöÛo·oßž;w.¬ÞDÕJýFt‰¿¶ Ò½…¨ FEnæIIxªBÕZ[Ó°TÐJU´Œê“Ia©ä FE+4h.%`݈JWZ©‡VC¾FW°Zx¡•ÌYa.?—.]š>}z~~þôéÓµµµoݺ5gÎX-¼‰°Zš¬Öè Œè¢—=gÐJ•´¢ª—QžI€š F)¡Í+¨~#ºÖÖÖ·nÝruu½wïÞÍ›7œœèo(Nœ8áééijjêãã“››KDEE…¿¿?“Éô÷÷'c| ŸÕ‚Õ‚VêaµèækaDwp —QÓzΠ µ¢¤—1;;›* 4c#¼1r;~06‚7ZQ’Ft¥‡žVkÅŠ[¶lqpp8{öì–-[ž?¾bÅ {{û¯¿þúÛo¿}ñâÅ?ü |V V ZI‰jY-i|-…‚Ý¡=gÐ Z)¹TòÇtá÷ •ƒJ60s+TQ+q+Ð<644ˆûVEW» X* —ò­VZZš»»»¾¾¾¯¯oKKKOOOnn.‡ÃÑ××çp8999AŸ¯ÿZÉ MFt©ŠŸ‡†®jƒèvÐ Z)¹TAÿ&Ø===‹E„½½½œGrúüùØÛÛ—––R’AìÊJ– ZÉ£Ucc£lGSSSqߊ»»¸ô¶õõjåäädcccii)§×¨BƧ¾¾~Ù²ek×®ÕÓÓkjjb±XsçÎe2™MMMAŸáÑÛÐÐzüx_cc[NArKKK)ɇ ˆ²“')ɧ-'§-7—†¥‚V*¤ÕÄÞ^á£W_…¥zS_ßzüøëÇUÝ×ÂÔåÁAÕt€ì-ç!šº «€¢‘gê²;uYÂ%Ê´ZfJU¬ÖƒV¬X­­­mkk[PP`ffV[[;eÊ”çÏŸ ŸÕ@~#I«%ƒ¯¥P4hDWšàÝÝÝ[¶l±··'÷¨T\a0ò­ ÕЖJCuaµð&jŽVê·®€™R «•––‘’’«­­M„¯¯orrrww÷ÁƒgΜ)ò ¬¬´R«E7_Kƒº[·nõññ©ªªòññ‰ŒŒ™fÇŽ7nܸpáBKK‹B±`-´‚VJ.U]]]^^ÞÅ‹/^¼˜——G®\•3fDDD ‰Žh*|V o¢fj5بËFè¿T% +,,œ3g9`ÐÞÞ¾k×®œœKKËëׯÇÅÅ!|V V ZÉ M¬…¾ºƒCšà'OžÜ³g³³3Cq ç ZA+%—êáÃ‡ŽŽŽŽŽŽ>”9«’’’;w‰ËÊÊJOO¯®®_¹r¥È3°Zx5S+õÑ¥IjŠ›³Öúߌ5ÊÆÆæÚµk W¯^3f AÂg`µ`µ •zX- }-4t‡„ଈ,_¾ÜÑÑqË–-»wï.++KMMNóþûï¯^½šlè~ôÑGˆ QÄPª E]ˆh*ò ¬PQuYh¿T«EúZªòÀj ƒ‘TQ«¥Pè2u9++køðáééé111 º…Èà=»wïÞ½{·¹¹yxxø÷߯¸úb-´‚VJ.U¶ªø 'ÑTø ¬ÞDÍÔ ktÞDh¥±ZÑÄjÑÍ×Òr3¤££ÓÛÛ{ïÞ½Ù³g{{{?}úTA7"ƒœèà;v¬pE€\Ð Z)¿TAAA¼-ÝT´¡FÄœ9sÈ?kjj„ÏŒ5 V o¢j¥~ktëêꊋ‹y“Y,–lY©Ðp.¬¬´R]«E7_kèGtííí srr<==uuu{{{5Áø¢ç ZA+%—J[[»µµUKK«¥¥¥­­MWWön>¥Å/é­ D4>«…7Q3µR¿] ã—¡ÕÔ£ÊÏÏ'( ÏSZZJU(£ÇçÎѰTÐJU´Œê-cc K%s0* }-Jú5ºááá“&M:s挞žÍ—‘`ÝCŽlëF?~\UUåìì\VVÖÓÓãììlccÃûVžÕn°ZP}Öè”.x“Íj]¹re„ ä(.—Ëýí·ßüýýaµ€‘T´ÉR¦¯¥P†~D7$$¤ººú—_~ÑÓÓ#4fv úû¡´Rr©ÜÜÜlllfÍšE~9+##£úúzò3—Ë566†Õ›­†<+õÑ0a“'O.]ºtéÒ¥§OŸN˜0V o"¬´¢³Õ¢Ð×R“†®ª€½Ý°·´¢ƒV2O§X("Ϻ‘yóæmÛ¶­¹¹¹©©iÛ¶m O7°‚ ZѼ‚ê·F×ÌÌlÆŒ3fÌàߥV o"¬´¢¡Õ¢Ð×R“†îÚµkÇŒchhÈø zšK===r‘½½½œGrƸüùØÛÛ—––R’AìÊJ– ZA+þR999ÙØØXZZÊf) øÏþ³»»ÛÙÙÙÅÅ¥§§çŸÿü§&tÏa´RŽVâV  x455•ùZ‚–«Ý(Œ_ª*V‹Z0J ­4G+úÄŠ§ÕöCßÐÍËËËÌÌljjâSA/#zΠ´RD©‚ø'CCÃcÇŽÕÔÔÔÔÔ;vÌÐÐPºç¼½½©ê°ppp ªÅmþ|– ZÉ£Ucc£ldž†™¯865Q¨•<Ýs¤±Ò«…Ùs˜­d Fõ‚É$h0{ŽB_‹†>ÕªU«~úé'þ3š ¡´´”ª†…YµåæRÕ&¡g¡•zh¥´MÌÅY-ÕV o¢ i%O0*SSSªf/{õõ=ÐÖ¦$²‹>^Ñ3¨1ò­ ÕЖJHûÐÑÑáïïomm••Ehxº Í+¨~kt)äÆ^^^¼\Ú.ƒÕ‚Õ‚V°ZjÕнÿþÌ™3ŒŒ½FwëÖ­>>>UUU>>>‘‘‘â’=zô())IѵÆZh­†¶Tò“••5|øðôôô˜˜¥Ýôĉžžž¦¦¦>>>¹¹¹¤Ãªœèxº Í+¨~Q—yó–å/‡ÃÙ·oŸÒ&ñѼ‰ÐJs´¢Ï]4tÿ‹V!t£ÜÜ\‡£¯¯ÏáprrrD¦éîîþä“O¾ýö[E×=gÐ Z m©äAGG§··÷Þ½{³gÏööö~úô©2[×éééÕÕÕááá+W®ä™P%ÜO´¢y1¢+²—ÿÿo<´µ9Rºx¡•æh…]š6t•FSS‹Åš;w.“Éljj™fûöí...ï¿ÿ¾ðW½ ­Ç÷56¶åä!ç±´´”’|‚(;y’’|ÚrrÚrsiX*h­øKõ¦¾¾õøñס1±··/,,ÌÉÉñôôÔÕÕíííUÚ­ÓÒÒÜÝÝõõõ}}}[ZZzzz”vkô÷C+šWPýFt© ¹LÄŒ3Ξ=KDWW×Î;gÏžMOW ±â+ZÉŒŠ&±âéÆÐ£R¶¶¶fffµµµS¦Lyþü¹p##£>¾Ÿ7áqª$d÷ׇ:UFFFxxø¤I“Μ9£§§Ç`0”B¯¾¾~É’%ÞÞÞ»ví"ψ,CoCCÇÙ³û~ÿ=úà fÍjËÉÁGš¿]²ä¾ŽÎÄÞ^u:~yä÷LJO›6ÌÍMž_ž`T555×®]ÓÖÖž={vbb"žÀ×@"öQ?Où¾ºÔ°|ùrGGÇ-[¶ìÞ½»¬¬,55UBbqž+"ªt¡•zh¥áQ—ttt ptt|øð¡ÌY áFaaa………sæÌ!#-···óB.+:ö2ž.hEó Òs®<¥X—+Ï2ÝS§N¹¹¹*-N;áP ÒÅ›«­†ÊjÑÄ×BCW…AÏ´‚VÊ/•Ö_ÈYŒ!ܨC Fý¨Q£”µO´¢ÕrD—ª`T›6mŠ‹‹kllT‚­@8¨„à Ôþtpp *¢yEAQ€F??– Z©V{{…´µ),•>þرceeeööö8âHóã–-[LLLÕé÷æÍ===KKËÁZ-3joµàk Éë;(ÜJWi¾–BÁˆîЀž3h­†¶Tš€žžž ‹Å"ÂÞÞ^Î#Ù+*>ööö¥¥¥”äC»²’†¥‚VòhÕØØ(ÛÑÔÔTæk޶õõ”äC–ÊÉÉÉÆÆf°­\²Y4vìX##£Ù³g«ÄöBòwÏaöaÐJüÑÄÄDäñ-cc‚³çèFtÑË€Š1T#º&û)3„)¬Pcä FE[Nž<)›Õêìì,**zýúµƒƒCEEÅøñãõõõeµøGGGWVVò¼>X-ÔÏHbDW Ñ­¨¨ð÷÷g2™þþþ•••"Óœ8qÂÓÓÓÔÔÔÇÇ'—ºa(a0ò­ •’KEšZy†DxÛrDDDDEEUUUUUUEFFnÞ¼YL(ž.hEó ªßÝüü|SSÓéÓ§›››»»»ÿþûï2g5„Û ÁjÁjA+%dE“5ºòûZhèÊÈÖ­[}||ªªª|||"##E¦ÉÊÊJOO¯®®_¹r¥â ƒèvÐ Z m©äáСC_|ñ…¡¡¡¡¡aDDDRR’Òn-Ü'Mž.¼‰¢.E]>Ãá4æ%6mš½½=¹åˆ#&L˜ OI‚‚‚tttMo"´Ò­hu™nhPC777—Ãáèëës8œœœ‘iÒÒÒÜÝÝõõõ}}}[ZZzzzTôœA+h¥äRQÐÉÉ)))‰9pà€£££Òì˜pgœ4]xxºð&Ñ Ñ-ËÉùaá›ÉÉoºº¤¹öÆüVëêÕ«2ƒÜ^¨££C£ºx¡•æhEϨËhè*¦¦&‹5wî\&“ÙÔÔ$!e}}ý²eËÖ®]«§§Ç;ÙÛÐÐzüx_cc[NAr(ɇ óŠ JòiËÉ1ðó£a© ´â/Õ›úúÖãÇ_?~:Í%%FãÆÉŸOoMMg§Öˆt+´‚Vü¥23æÍóç:l¶Ñ¯¿’¯°‘µõ쯿¶Ÿ5K3¥Q›¢Áׂÿ­$#.•W_ßmmJ‚Q)Ù×R(º„Æàë뛜œ¼eË–ƒΜ9Sdš´´´cÇŽ¥¤¤L:U¡…A?´‚VC[*Õݨƒ¿3Ž ccãúúúK—.ÕÖÖ“iœœœbbbx.ã¶mÛä¿ï‹/lmm)©…Yuåçë{{Ó­TÐ Z‰,U¼»»®¾þäÕ«§|ü±._üd_ þ´’§•KÐuîûZ4uy×®]999–––ׯ_‹‹#O ÌÞ +,,œ3g9±§½½]A…ÁZh­”\*µ"tX:"iii)))±±±ä”² ¯»»[B%Ȱ ¨²êyúT½+­ÔI+û™3W=ëÍáHÙʈ_*ËX\\Ìf³Ùlvpppqq±&8~øM„Vš£}ÖèÒÊ×Ò ]›k×® œˆ°¯´€ûè9ƒVÐJù¥RÑÆ-?aaaAÌ™3‡ü³¦¦f×®]¡¡¡‰‰‰^^^©©©Š»õ°aÃh˜ÕÈà`õ® ´R'­''Ëi²dnë~üñÇ .üßÿý_‚ <¸fÍš7n¨½ã‡ßDh¥9ZÑgD—V¾–èÒ ôœA+h¥üRÑ*@‚l´þ7£F"»ð®^½:fÌáK–,Y¢Æ¶T‡Å ´RW­Hc5{ölBî€EEEŸþ9oS´¢¢"z*Om0ªüü|‚¢ð<¥¥¥T…2z|î K­TE+qÁ¨Þ26¦°T2£¢›¯¥AÁ¨(ª Ù[ÎTH@úT—ÎÎ΢¢¢ÆÆÆÉ“'ðfs Öjùøø„„„|òÉ'A>|833“ÎpB1U IDAT#ºðµ‰„5º$”£R²¯¥P0¢;4`ä ZA«¡- 9þþþL&Óßß¿²²’ i¶E9qâ„§§§©©©Onn®È| ?ñññ¼dš©•ð3#ÍUÊÑêÙ³gùùù'N¼{÷®ÌY;vìÊ•+ä¦hW¯^MII¯…ßDh¥NYÑd.Ý@CWZ°·övƒVtÐJæé4ÙBÀ¬Ñ™­[·úøøTUUùøøDFFMÛ–|UVVVzzzuuuxxøÊ•+Eæ­xš •ð3#ÍUÊѪ¡¡aúôé...,+ ÀÉÉI欜³²²jkkkkkÏŸ?ïìì¬ fëN¡•æhE“5ºtóµ0uyp`o7•® ´R­ä™NS\\ÜÐÐðÖ[o Ä­ÁÔeºakk{÷î]6›-°ó9ƒÁào üÉ£ªªÊÃÃãÕ«WãÆ™´êîîöóóÛ¸qãš5kÈóâòÑ­øŸ===­„¯¢ƒVØG¿‰ð4M+ S—iµî |-…‚Ý¡=gÐ Z)¹Tõõõööö=êêê‚!¢3MMM,kîܹL&³©©I\2‘­ÜúúúeË–­]»VOOOÊ|4P«íÛ·»¸¸¼ÿþûƒÍG-µâf„µ¾Jå´˜Ç.Í´ö!³ç0# ZÉŒê“IÐ`öÝ|-Œè¢—=gÐJÅ´’­—ñÊ•+cÇŽ;v¬––ßåí·ß–ÜˈÝ!ÄÖÖ¶  ÀÌÌLò(¥Èßš+V„„„DGGkkk‹ËZõñ­éjmmÕX­ži®¢ƒVƒ²ZRN…€¯ÿZÑ9+úèÊàk) Ñ•&t„ÒBq`ä ZA+9Ku†ÃiÌK:mÚ4{{{²#vĈ&L@c’Îøúú&''wwwMÐJø™ÖJø*•ÓjÞ¼yÛ¶mknnnjjÚ¶m[pp0Íý1ü&€VƒBAQ—UÝ×Ò ©ËÒ„Ž0 öv`È!§ÓÄ»»“3ûë¯íTÉÎÎ "¼?yßbê2@µ”ÕjiiÙ°aCVVAÁÁÁ †††´õÇàk å_K¡èjÎÃÁ qáÂq¡#D¦yòäIzzzw÷›çÏkhذ|¹Ž…EoM<Ç&==ãžùóѱ°h.)17Nþ|zkjú;;µFŒ [© ´â/•á˜1ož?×a³xïm?¡¬;PE ;Fg ¾üh5àÑjØ0ÉZmÞ¼¾–†6tëëë/]ºT[[+nuŠÈ4NNN111¼^ÆmÛ¶É_˜/^ØÚÚRR/ ³êÊÏ×÷ö¦[© ´Yªxww]}ýÉ«WOùøc]}}8² ºþ|-øÐ ¾åhÐ]iBG(-¼„¥¥% ³êyúT½+­ÔI+û™3W=ëÍáHiyÉÉ3¼)4üsiÐÊÃo"üh_K~4hDw×®]¡¡¡‰‰‰^^^©©©äIû"Ó( ”i’ÕHê¢Sг‚ÐJ´Zœœ<¨k³³³EšcCîÁׂÿ­àk¡¡+;666×®]8)°œÈ4ü,Y²D%Òa±ð ­ÔU+ÒÔ¾~ýúêÕ«d¤ük€&þ|-­àk¡¡;ĸ¹¹ATrïr¦¦&]]X?€¯P[_KÿM@xsvÆ_H¸êĉžžž¦¦¦>>>¹¹¹„7yW9­HâããyÉ4S+ágF𫔣ճgÏòóó &Nœx÷î]jógˆ&Hi(AmåüC•ùðà¾|-øZ*ák¡¡ D ¼9{kk«À´ma²²²ÒÓÓ«««ÃÃÃW®\I(q“w•ÓŠ ˆG%%%IÈG´~f¤¹J9Z544LŸ>ÝÅÅ…Åb899Q~‹÷D1¨–Lww÷úõëG=zôèÏ?ÿüõë׃meffΚ5ËÌÌL¡¯¯/((Hr²óçÏÏ™3‡ÉdŽ3fùòå/^¼WN:4ï¥yUU)ÍZ°Àׂ¯_K£|­A¡Õ¯¬=‘Ämb”€¸ÍÙbq üÉ£ªªÊÃÃãÕ«WãÆ“¼É»ÆjÕÝÝíçç·qãÆ5kÖçÅå£ Zñ?3zzzZ _E­Dnb>(«Å`0D6kO:%áÇ[@œ¸¸¸û÷ï>|˜ ˆ5kÖL:õ믿–|SþË3337mÚ”””äçç7bĈ˼gÏž¼¼¼k×®I(á‚ 8ŽOooïÑ£GOž=¨Ëâãã¥iåÞ¹sçèÑ£GŽ‘œìçŸ 5j”¡¡áÚµkŸ={&9kjj.\Èd2½¼¼òòòø}š¨¨(ssó3fHHYZZ:cÆ 6›Ã´9’,îF"{111l6ÛÇǧ´´”w’?ïstt4›Íööö.))° )P£§OŸúùù™ššúùù=ündMLL¼½½yá|233ß~ûmžÂ$søða[[ÛŸ~úIB9Å "\MàkÁׂ¯_kÈACW#àmÎ^__ÏÛœ]sçÎuqq!ç KOQQQeeå¸qãÌÌÌ‚ƒƒ%´£ZZZV¯^}àÀ éó]¼x±„rÚÙÙ™˜˜¸ººnÞ¼¹¹¹Y\>Ë–-«®®Þ»w/‡Ãø¶´´tòäÉRnÚ´iÁ‚üm-‘ÓÞ$ßHƒŠŠŠE‹mÚ´iÀÖfEEEHHÈ€)…k´aÆ ¼|ù288xÆ ƒ}BÈjÖÕÕíÛ·ïÓO?%O®[·nÿþý\.—'‚ð‡©?ÿü3!!!**JæròWøZðµàkÁ×BC(Ù6gOKK‹ˆˆHII‰ÕÖÖ&”¸É»ÊiÕÜÜÌïYj¬VÂÏŒ4Wi‚VƒåÒ¥K-+)×ܶ´´<{öÌßßÿã?wyDDD`` ðîvânÔÙÙ¹fÍšüüüþóŸâÊÙÚÚúüùóúúú .tww‡††Š+çµk×>þøc‹µxñâ²²2þ¯¾úê+ƒï¾ûNBÊüüüuëÖ >œ×Ò“áFÂ|öÙgÇÿì³ÏnÞ¼)eÊ[·n ø¯¨Ñ;wÈË×­[wçÎÁ>/^|ûí·™Lf@@À«W¯È““&MÚ¸qã¶mÛNŸ>ÝÑÑ!Ãó¶~ýú‘#G.\¸°¢¢B¶r TøZðµàkÁ×BC(ƒ]»våääXZZ^¿~ìû9±MÀ» +,,œ3g™¦½½]8h%}>š •ð3#¬•ðUš •”ØÚÚ>yò„üüçŸÚÚÚòÚÒ ÍYYYmÜ¸ÑØØØÀÀ€Ãáüþûïâ.?uêÔ‘#GøŸm 7zòäÉ;ï¼£««{éÒ%### å$B[[ÛÎÎnÏž=ÿ÷ÿ'ö‡G[›7üØÒÒÂÿ•”)¥ú…“ér^è --­ÞÞ^‚ dk=ЬÑ`ãb¤ ‹ŽŽ®­­}ùò%ï«3gÎÄÆÆššš?~|áÂ…2räÈ‘AèèèðòYN ‚TøZðµàkÁ×r°“¤F ¼9»HYà¤pšQ£FIÞä]cµ>/œ´w•&h%%!!!111‡îïy÷Ýwuù‚ âãã9ŽŽŽÎ‘#G<<<Ä¥”&2 IzzúŽ;vîÜ"e9kkk¿ÿþû)S¦ˆ»Å;ï¼÷õ×_5JrD¦ôööNNNÞ°aÃÑ£Ge¸\\­=~ôèÑiÓ¦‘g¬­­333ƒ‚‚øƒ|ò§œ>}ºä<…™«¬¬ åÍjnooYNò[CCÃ)S¦p¹ÜÔÔTqy&&&–””Œ;vÀùØ"SÆÇÇŸ;wÎÚÚº­­7kK¤ Ò߈ ˆÆÆF++«Ó§OïÛ·<áââ"°œ‰—rïÞ½ƒÕ3!!!##ÃÂÂ"33“w#qDEE-X°€¿äß~ûíûï¿ïêêjmm-ð00™Ì/¾øâàÁƒ¼ógæÌ™²m2$²œâh¶j¶÷önQ}}}çÏŸŽŽ.,,¤¤ßÿ& iV †Üj)L] @3I™0 ---;;;Ì¡ºÔÊ»ÐO¨X£ ] ÝAÁþgÊü/¨Á-ø'õMÑôõõ­\¹RE ¿råJ¥ØÓp©Õ@ð+_ ¾|-8jü[OßÝ÷D1¨·«»»{ýúõ£G=zôçŸþúõëÁ¾œ™™™³fÍ233“æÕíëë ’œìüùósæÌa2™cÆŒY¾|ù‹/Ä•“?9j³h­µµUþºhÎÿåË—]\\}é_O‘)Å]îââréÒ%H= Ù‘Þ¶¨±þøZðµàkÁ×Rãßzm5~»þõ¯UWW?zôèÑ£Gåååñññƒº<33sÓ¦M_ýuyy¹4oï¿þõ¯aÆINsèС/¿ü²¼¼¼¨¨hâĉK—.•PÎV>ðc ”FFF†···¢ï"ýë)2¥¸Ë½½½322 õ€fGzâÆúàkÁ×ðµÔø·^[½Ÿ¡ØØX‹Åb±¶oß~úôéA]ž8bĈß¹sçèÑ£GŽ‘œìçŸ 5j”¡¡áÚµkŸ={&9kjj.\Èd2½¼¼òòòxç FTT”¹¹ùŒ3$¤,--1c›Íމ‰áu¤‰ìÝw#a FLL ›Íöññ)--åäOÀûÍf³½½½KJJ$×T¸FOŸ>õóó355õóó{úôé`Ÿ²Ž&&&ÞÞÞ×®]ãýè¾ýöÛ&&&<„?HæðáÃ666¶¶¶?ýô“„rŠD¸šJæÖ­[½Œ cÀJÕ××;¶££ƒwUGG‡½½}}}½œ¯§È”â.wvv¾}û¶ªØ(åH-ÒìÈiHÕC|-øZðµàk©ño½:7t_¼xáääDÄܹs]\\ȹ+ÒSTTTYY9nÜ833³àà` ïvKKËêÕ«8`aa!}þ±±±‹/–PN;;;WW×Í›7777‹Ë'""bÙ²eÕÕÕ{÷îåp8ß–––Nžü Þy縸¸ööök$2¥··wrrrww÷Ñ£Ge¸\d‘‚8zôèëׯ=:mÚ4òŒµµufffGGGRR’ȔӧO—œ§0“'O>zôhww÷áÇœ|biiy÷î]þ3]]],ëõë×»wïæÔÓÓ›;wîÆ·nÝúÇð[þ?…ÈrŠdÈ™6mZqq± •ÒÒÒ MLL\ºtiBBBhh¨––– ¯§À¿^dJq—O:UUl”r¤iv$ 9úàkÁׂ¯_KëÕ¤¡+r û—_~iaaááá1aÂ+++ ËD^¾uëÖòòr777;;»Ë—/§¤¤È_ÎÏ>û¬²²244” ½½]d9Éo §L™ÂårSSSÅ噘˜XRR2vìØ—ï‹LîÜ9kkë¶¶6mmm ‚H#‚ ­¬¬NŸ>½oß>òLLLLDD„‹‹‹±±±È”{÷ ™™™¼‰#**jÁ‚ü%ÿöÛoßÿ}WWWkkk‡Éd~ñÅäçp83gΔ-ð½ÈrŠdÈY¼x±ä*µ|ùòaÆíÞ½[__Ù²e.—þõ™RÜåùùù!!!ªb¸”#µH³#}Ø5Ö_ ¾|-øZjü[Oß5ººôðcC9}}}çÏŸŽŽ.,,¤ä·ÿ&•~V­Zõ?ÿó?ªXø+V¤¦¦òÜé’5º*-5µú”oµàkÁ×ðµ†Ü×R(ºôT¯®2a0ZZZvvvt›×†mmmÕmz¥¥¥Aj耯_ ÀÀo½.5@ùO~;àk0„`‚4t4t4t4t ¡  ¡ Ð¥î£ÛÜÜ|êÔ)ˆP`µ°Z Š(oD·¹¹¹¬¬ ŠTÈ_„ÕÀj€*¢Õßߨ X£ ] ]@ (/êòƒ 7€¶477Ïœ9V  ºV ŒèB•`µ@­ÐUòýÆÑtC¶“°ZÕ²ZŒè@C@CP º  dgg œ ‚,X-PK0¢«q0 º¯ŠÖN½å‚‚‚‚‚‚fÏž gVVÀj€Úƒ]( µµ"ПÎÎ΢¢"¦¦&]]X?¬ ¡ ÀPÀ`0І”ðìÙ³²²2+++;;»»wïÚÚÚB+« «hè€ ÓÐÐ0}úô‘#GA°Z Æ`®ÚÂB ÁãÇýýýKJJÌçÀvvv–––§««‹÷ÕÉ“'½¼¼X,Vpppyy9ï|jjê´iÓ˜L¦»»{ZZšäûöööÆÆÆ:88°Ùì•+WòFxe(AAÁ¼yó,--™L¦ŸŸ_ff¦4R$&&ºººš››'&&J¾¯¸òK.§°¼o¿ýöíÛ·ÉÏ·oßž~AwïÞŒŒ|ðàAGGâÊÊJggg‘ùˆ¼ïË—/'L˜ðÿº^´´$æÌ™3‰‰‰_}õUIIÉÈ‘#ãããCBB¬‚@+WÂ}Å•°åtww×××/((èïï9r$ÙÊ•—Ëå×.å ¬ ¬œºZ9qé%Ü—lå1|øð7oÞà)R«EvÏ=innž9s&º€bbb ¦ó‘‘‘Q[[û·¿ýmÒ¤IâÜ5~ªªªÈ±Žªª*‹Ež\±bEttô¼yó F[[›¥¥%ÏM,..ëwßÑ£Gß¾}ÛÀÀ@ÜÝûûûùý3WW×C‡ñæÍ›_~ù%""Bš†®È¦¯ÈûŠ+ÿ€åfÑ¢EgÏžíïï_´hÑ€ù°Ùl~ñôRºÀÊ©«•—^«%²<V (iå–••¡¡ ( 22’ÅbEDD5bÄ[[ÛC‡­^½úòåË䨢££ãããµ´´¢¢¢Þ}÷]òdgg§±±ñðáÃËËËwîÜÉKÌápÖ­[÷ý÷ß?¾¦¦fïÞ½ß}÷„û†††®_¿~ûöí÷ïßß»wïÉ“'ù›—.]š;w.ÏëZ¸p!‡Ã™1c†––V{{û€…‡¸ûŠ+¿ärŠdñâÅÿøÇ?‚øÏþ3à}—,Y½oß¾þþ~þ¥t€*¸\.—Ëå?ƒ5o°rêaåÄ¥—Áj‰,P9«…B.¸P&F¥¶$&&nÛ¶MBúhãÆfåååååååêꪯ¯Ež<|øð×_mnnø`Íš5¥¥¥ÕÕÕ{öìIOO'ϯZµê‡~ ?gggOž<ÙÌÌLÂ}:tôèÑäääššš .ðª . L‚‚‚‚‚þ?öÎ<®©+ïÿ—­b›°˜D”MÀ‚K+* `¡¥K[x:¡ŒmÕè×q¡ŠJE§¢cUÔªL§¼|ÔA©RGA,Ô Z±b ”}ß þþ¸OóËd#$—ä&ù¼ÿÈ+Üœ{rî‡{>9ë÷¿ñÆ=Ææô^¼x¡šoÒ‚‡˜?çóÿñÎ;A||þ¼á¨Q¸›Ð ®^½êîîÎf³Éæã¯¿þ øTâCÌáZ¸ Š;v lܸ‘ü“ÉdžìرíííA899]½z•̧²²òÕW_•uYZ>“&MÊÎÎ&7aVUUMžwв²²;wî?«Úq]2žy×®è@bb"ƒÁY´LDFFFSSÓ›o¾9}út‰7jkkÉžCmm-9ÕOĸqã~þùgƒ!ga,,,„óQø¢,--«««'L˜ g>®®®‡"âùóç.\ˆ'?Š‹‹[´h‘‰‰Ipp0ÇHvo¿´´T|FWFþ@e •¦rõË/Ÿóùäûç|þÕ/¿„&hÇÛÛ;(((((ÈÛÛ[žub#Ê÷ßïááaffæááqþüy‰iª««X,V@@@MM \ ¸Ð8ÌÌÌÄ{¹AŒ=ÚÎÎîСCqqq½½½Cfµyóæ¶¶¶öööM›6½ÿþûäÁ˜˜˜åË—WWW?{öìÖ­[²3yï½÷6oÞÜÞÞÞÖÖ&¼¡w¸DDD¬[·®¹¹¹££#11qÈôaaaÙÙÙOž<éïïòä‰p‡öµ×^=ztRRÒ‚ †Ì‡Ëå.Y²äîݻϟ?¯©©‰2 2ŒJSy75uUIIïÿüϪ’’U%%簾*“[}}=U£0«g¥¥4,´‚V””Šna]–.]º{÷îÆÆÆ]»v-Y²Dbš 6øøøÔÖÖúøø(Ðk¡&B+¸P;{÷îýâ‹/dD]öõõýøãW®\9dVS§N:uª«««±±ñ¦M›Èƒ«W¯vuu ²´´\µjÕG}$;“M›6»¹¹M›6ÍÓÓSp\P6‰…”Øg³ÙîîîÓ¦M›={ö{b¹\îž={ììììííOž<),*66¶»»ÛßßÈï]²dIllì§Ÿ~jaa8}útyòªAOO/((ˆ I¤ömèèª‡ÆÆFfõT(øžV^ ´Òe­Š‹‹œœœœŠ‹‹Õk¶¶¶úúúzzzäÒ/qòòò¸\®±±1—ËÍÍÍ•Ý ~ë­·¾ûî;¸j"´‚kZÑ-†à¸ Í¢E‹Èe·²ùüóÏ«ªª:4zôèÿkÊëë¯]»öÁƒmmm7nÜ Ï_øÏÑ£G>ÐÖÖ–6ØÞN1 %_g̘AI> ãÕ§O)ɇ(*âDDаTЊ&Z틉‰‹‹Û£^­Œùüî´´þ4ݵaÑ¢E•••Ož<Ù¹s§p©áÂçóÓÒÒ]t ¹¸»»×××_¹råÊ•+d˜4õ–Û¶mÛ¹s§òùðx<JŠDaV=yy __º• ZÑ$+Á¶ŸS§N©W«žžÎmnn.++#Ïe0ÎÎÎÂÞTÿ ŽéÓ§ïØ±ÃÏÏ/77wãÆEEEâiìì슊Š8NSS“§§çãÇ%Žžº»»744\¸pÁÍÍ ®ׂVp-<^Hs‘6ÖÝÝ-#ô º8~üø—_~ÙÙÙ¼oß>å—}ÕsæÌ9wîÜK/½„{@›¸té’ðÓtUÿx!D]VTý®S›U¿ë´½@h¥ËZq8Z…r!4’K—$¦™3gNjjêºuë<èçç'-«ÌÌÌ[·nEFF^½zUð0¸j"´‚kCF‡n½\‚ bbbbbb”/$ / hXº¬x< ³êÉËÓî „Vº¬Õ%1Ôk_ýõŠ+8N||ü¾}ûȃ"cáÛ·oÏÍ͵²²º~ý:ù Fi­áµk×FFFöõõÁµP¡\ TÝ, 3ºêcØÐ Z©¾TÂëgÔÎ{ï½÷Þ{ï‰Õ¶µµÍÉÉ‘à”èèèèèh¸j"´‚k€Z±,<^Hc£"¢¢¢BÉׂ‚Jò©¨¨àñx”äCăóçiX*hE­ÌÍÍÕ®UYY™ÂÁ¨€2`æ ZA+õ– À°@0ªáAUX4…úPŽba]D""ˆ Åa]àZ®×µ£ú`T˜Ñý/Tö¸'ŒaC+h¥âRa î.ÔDh×Òåå=¢î_P---ùùù—/_¾|ùr~~>¹VhIG—Ïç¯[·ÎÞÞžÉdJë¬VWW°X¬€€€ššõ»’ ´Ro©î.ÔDh×¢!Ïùüÿ$%ý')é9Ÿ5Ð,Š‹‹œœœœŠ‹‹ÑÑ¥€m۶ݸqãâÅ‹]]]Òb”oذÁÇǧ¶¶ÖÇÇ'!!A½Æ6´‚Vê-•.€Èˆ,­Y@ã(}JI>DQ'"‚†¥‚VÐJ¸TÆ|¾ÂÛÄŒJ´ÉØÝÝ}úôéüãëÖ­[µjUaa!mÝ™ªE€<ªŸd ³êÉË£êמ­”ÉJáE€”k¥dX—ÁÁÁ†††ÊÊJ²I2¬E€´r-ÙKáZp-¸\‹n¾4äV2¸\ Z©]+Å\ Á¨D100¸sçÎo¼áååõðáCzvq©}P9ÎAÉ£ ¨zÌC“ K­è£•bê V+åÔ¡¯¯?nÜ8áö¢Z\ Oÿ¦ÌRB+]ÐJy×¢rn%ÃÝ…š­4N+£eœ>}ú¡C‡>ÿüó­[·9;¡^0ʈ‘3ÕJkæF$2¬¹J\+!!!'''--ÍÙÙYÚ Ÿ!C¶˜Ñ…kA+¸–®¥2D†íHw±©ªªª˜˜˜âââ©S§?~|üøñp-¸´¢›Vй֥K—DŽËv­ÅPí]Ç„„„ LŸ>ÝßߟÐ0­ •zK¥v×:uêTZZÚ$™1óòòRRRÈ-žžž2RÊxú7î.ÔDh×RÇÝDÚÚÚæää ­…š­´R+áž­ÚQÿÒåðð𺺺 .I³Híq&¡´Ro©ÔîZxú7žh ­ðôo€¶ÚÐ m­‘CKž£«2ðDJ ³hô)‡Dõ‹ñôoàZšåZhk ;(¼tYÆŒ.ž£K_¨ FUPP@Pž‡ÇãQÊèÁùó4,´¢VŠ£¢V+åƒQÑ<ý›š_bŒ÷C+Ñ àîBM„V4׊Vë– ‚ ^¨Š_~ùå—_~éƒ Òµk×¾@'ùàÔ^JêòéÓ§…ÿLKKKKKS¥kÝ»wïõ×_744´µµýî»ï$fURRòúë¯͘1ãÁƒóœ’ššúúë¯755Áµ€ki(mmm%%%qqq/^¼àñxxÅ+^ÕòZZZZRRRVVFm—èZ# ]ž£K¾ÿí·ßüýýi;:ˆH€}ÐJ™¬´,~©Èꞣ ×BM„Vp-Mq-Í®ׂVj×Je.FuF]Ä ¼yùå——/_NèˆØ­ •ŠK%ò®…» 5Zi½ké2¸» ´Ro©ÔŽ:÷èŠ/ÕkllLHHÐóÅú~h­T\ªàÿ®…» 5Zé‚ké2¸» ´Ro©tº£«Ë`4ZA+õ– àîBM„Vp-Ê©®®`±X555Ó|ÿý÷fffçÏŸ‡k¡&B+íÐê§Ÿ~ª««SÙ®XÍèèjʃsuQ—¡•6E]VfzDG÷-ư¡´Ro©4bRwÆ >>>µµµ>>>ÒV»,]ºt÷îÝ»víZ²d \ 5Zi‡VnnnÍÍÍׯ_///çóù´°$µG]þá‡ôôôèuyÈoGüR€ø¥¿ôÑ£G—.]:sæÌ™3g.]ºôèÑ#…ã—Òʵ¿¯x•öúÁ|úé§|ð†Æ/¥ÐµTƘ1cx<^ww7Çc±XÓ¸»»gff¶¶¶ž;wnÊ”)hk@C6–––_~ùåüùó?þøcMMz£.«F—ËåîÞ½›’iî””A„qäYNƒ¹ŒœA+mÕª¸¸ØÉÉ)00000ÐÉÉ©¸¸˜®5¢ÙÚÚ²Ùl‚ ìíí•|%¯Wù|ìííy<%ùaQSCÃRA+úhÕÞÞ®v­œmmm­¬¬ÔèZ*£££ƒÍfÏŸ?ŸÅbuttHL³oß¾˜˜‹ûõ×_ 4ÐÖÖ–6ØÞÞ“›K„’¯<’|‚¨8uŠ’|zrs{òòhX*h­„Kõ¼µµ;-­ÿÁL`Ô¨QŽŽŽ¾¾¾çB9Pˆú/Äb±êëëÉa¿žžÅÔqïÞ½ 477K[Umoo¿~ýúäääÊÊÊo¿ýV<ðSC$BUÈ{4…ÔA9Š…¼¿zõª‡‡Ùëknnþõ×_ŸëAT¹–j€k¸\KeØÙÙq8œ¦¦&OOÏÇ‹§™>}úŽ;üüürss7nÜXTT×€nhÇã…Ô?£ëíí}îÜ9‚ ž>}úå—_¾ñÆ dÂçó?ùä“äädiòòò¸\®±±1—ËÍÍÍ•‘²¥¥å­·Þúî»ïFîª1ó­ •ŠKåááQVV–ýðáCõº–Æ» ZA+Íu-•1gΜÔÔT>ŸðàA???‰iÈ‘A===ƒ††¸j"´ÒV­ÔŽú;ºß|óMFFÆË/¿NírªXVW-{`øúÒ°TЊ&ZM¼ªQ+…—Óp8ooï      ooo‡£^×Ò8gZA+—ŠB×RÛ·oÏÍ͵²²º~ýzRRyPd[Ù×_½bÅ ‡?¢þ‰» ZA+õ–Jí¨é2%˜šš þ”¸üXžå4L&ÓÝݽ¡¡áÂ… nnnâ ¨ZNÃãñ¨º(̪'/ªzBÏ „VÊd¥ð"@ʵRl9Í¥K—Dއ0¥ç"@J€kÁµàZp-¸j"´‚V*p-ÙèâÒeJèìì*1<Ëi‚ÈÌÌÜ·o_dddSSÓÈ£AÐ Z©¾Td188X#žÒ!'2"ðÁµP¡\K—ÁÝ­ •zK¥vÔßÑýç?ÿ)xßÛÛûÙgŸPûOârq8NHHÈÚµk###ûúúF誱¾ZA+—J__¿»»[OO¯«««§§ÇÐЮ…Xñ¨‰Ð Z©ÀµHŸéíí °±±ÉÊÊÒ…Ž.î.h­Ô[*tt‰C‡9s† ˆ_ýÕßßÒ¤IÊä&<+2µkkk›““ÓÖÖvíÚµñãÇË>=:::''gôèÑ#tÕ ‚VÐJÅ¥²±±¹y󦫫ë;w~úé'gggµ»Ö½{÷8 #Á† |||jkk}||ÔëÕ¸» ´Ò\×"ÉÊÊ5jTzzzbb"=[¥Ïž=«ªªjii!¢¢¢BÉW===Jò©¨¨ppp $‚ šllhX*h­„KUVVVUUU__¯dß¡bÔ¿G·±±ñí·ßöóó»råÊáÇgÍšEçìÑè „VÊd¥é»Ýd3¬Ýn”¸ŸÏ÷õõ]¹rå¢E‹¤m¸°³³+,,´°°Y€|>ûÂ… £¢¢D¢èÁµàZp-¸Afff­­­ ,+>>žÍfˈʉ¶j"´‚V”¸Ö¥K—†Œ,0¢¨F×ÒÒòìÙ³/^Œ‰‰¡s/£Œ9ƒVæææš;Ê(2¬¨Ì(#%®…Xñˆ­+^e®eooûöíÜÜÜ)S¦ :Ö @+h¥âR]úoÔnj›Ñ•±-MÚäF1r­Ô˜â—RëZˆš­àZ#íZ222–-[6}úô³gÏ‘+AÐÖBM„VЊZ×71õÎèj‚Ϋ0­ •êK%ì¶ 4RëZ‚žªìXñëÖ­2Vü­[·"##¯^½jaa×BM„Vp-ÂÃÃÃÃÃu­ †» ZA+õ–Jíè@ b´‚V*.•ÈÃ9èù¬ÄŠGM„VÐJ³\KybÅóùüuëÖÙÛÛ3™Ì}@î.h­Ô[*µ»–ú;º"§²gBªŒA+h¥ÞRÑǵ+5ZA+­ikÉ+~Û¶m7nܸxñbWW׈Î-ãî‚VÐJÅ¥jiiÉÏÏ¿|ùòåË—óóóÉØF:ÝÑÕ¨ FUPP@Pž‡ÇãQÊèÁùó4,´¢VŠ£¢V+ªBÞƒá‚1lh­Ô[* //Ëås¹ÜÜÜ\‰iN:µsçÎI“&‘áZ¨‰ÐJk´*..vrr trr*..ÖõŽ®©©ikk+ù¾¹¹ÙÌÌŒžÞmdddkkËf³ ‚°··WòÕËË‹’|ìíí(ɇ ·wÞ¡a© }´jooW»VÎÎζ¶¶VVVp-ƒ1lh­Ô[*p-ybÅ755]¹rÅÒÒÒÕÕõܹsÂ!V"ÑiÜ]Ð Z©¸TÇÛÛ;(((((ÈÛÛ[¸—«ÔßÑ5119vìXCCCCCñcÇLLLt¡ÉˆõýÐ Z©·TºàZˆ,€ÈÐ ‘TìZcŋľڱcÇŽ;,--—-[öõ×_£­…ö´Ò­.‰¡æ–Ð UñË/¿üòË/ÝK[[[III\\Ü‹/x<^ñªk¯|ðÁ§Ÿ~úÁHK#ûSª^KKKKJJÊÊʆ[…=ztéÒ¥3gΜ9sæÒ¥K=þ4-----MË\‹díÚµ/ÐI>øµ—D±Ê ×h–k>}Zð*üF†k(êߣ›““óé§Ÿ666 JBçç˜c߈F_ ´R&«!w»‘ Ä?¥É¾‘«W¯º»»“ñäZZZ~ýõ×€€Á§ÃÚí×BM„VØ£«Y®uúôéÍ›7×ÔÔÀµP¡´9×ÊÎΞ={vAAÁìÙ³õõõoÞ¼9oÞ<Ù®5¢ªÝÎ6nÜxâĉÀÀÀúúú;vØÚÚª±0L&S5ÖõýÐ Z©¾TT…¤•k© Ü]Ð Zi®k­Zµêïÿ{XX˜¡3àî‚VÐJÅ¥²±±¹y󦫫ë;wž={6iÒ$õš€ú÷èþöÛožžžúúú£FZ·n`Gǰ8yòä”)SÆŒããã“'e‘zuuu@@‹Å ¨©©QïUc}?´‚V*.•‡‡GYYYvvvvvöÇ=<<ÔëZî.h¥}ZEDDf}µÛµ‚Ö©^.j"´‚Vª/•››[`` ­­­¿¿?ùF×;ºÏŸ?×××wppÈÎÎ...Vlä2+++==½®®nÙ²e .”˜fÆ >>>µµµ>>> ê½jŒA+h¥âRQ ×¢ ‘(/p-ÔDh×gãÆèíí¥y×”Úz¤9STÌÁÁªlM664,´‚VÂ¥R8„žHô)µ£Rÿ]rµðÅ‹¹\.ŸÏÿÛßþ¥ð·ÔÖÖº»»766‰|dggWXXhaa!ãÙndaZZZ.\%^ìÑè „VÊd¥é{tÅÝ688Xð~X»Ý(q­“'O&''WWW»¹¹mß¾ÝW’DÕÕÕ±±±wïÞ:uêñãÇmll¤¹–Œ/‚kÁµàZp-ñ1ìÑEM„VЊ®5¢¨F—´Ú7ß|³¢¢¢¶¶V™^nkkkTTÔâÅ‹Å{¹Attt°Ùìùóç³X¬ŽŽi™”––†……­Y³F¤$mmÝiiƒíí=¹¹A(ùêàà@I>AXVWS’OOn.Ã×—†¥‚V4ÑjÚÀ€àUÚY?¥V«ç­­Ýiiý(`¤Û Û®º\ ëP(³”ÐJ»µ¢Ð®›a IDATµD tÜ]Ð Z©Åµ¨ÝÔ?£KaþÑÑÑááá›7oÖחз³³+**âp8²gtÝÝÝ.\¸àææ†QFŒœA+Z07L¾ þ”=Ê8Ò®E‚u(¨‰Ð ®¥Y®5ÒÀµàZÐJíZ)æZ²ÑÅÝòòò XZZZZZ†……•——+ɉ'âãã=ºeˉ½\‚ æÌ™“ššÊçó<èçç'-«ÌÌÌ}ûöEFF655ÜUc4ZA+õ–Jí®E‚u(X[­°EÌ™3'--m$Z®"Èø3%%e¤C à7ZA+ÍmkQ‚úgt½¼¼ÞÿýO>ù„ ˆÃ‡Ÿ={6??¸™‹xeCCÃ+¯¼"²u­ªª*&&¦¸¸˜Üí6~üx‰ù§œ8q"--í‡~=z4F1r­ˆáÏÒ§-[FÃQFeæF(q-ëPP¡\KU®•››ûü#''çwÞ‰ŽŽž5kÖ5ö¢££íííׯ_Ÿœœ\YYùí·ßJLvïÞ½ 477K\D ׂkA+µk…]jxøðá²e˘L&“É\¾|ùÇÈDdçÉ+¯¼BˆZ°µµÍÉÉikk»víšÄ^®ð)ÑÑÑ999"½\ Áh´‚Vê-•Ú] ëPP¡µ’øØ!Íu-ÿãÇÿòË/ƒƒƒ_}õUgg'å_”——Çår¹\nnn®Ä4|>ÿ“O>INNé«FM„VÐJs]KK:º‹/Þ¿?ÙAÝ·oß’%K詵!ï Š‚€óx<ªB“?8ž†¥‚VôÑÊÜÜ\ÆY⟒G¨ÕJ™÷¨ݵ–.]zûöíy󿑿'Ožb‹S¶oßž››keeuýúuOëåp8!!!k×®ŒŒìëë!Ä“¡´Rq©(t­ÞÞÞ'NDDD\ºtiÉ’%_}õÕýû÷CCC)7 y6\lݺÕÅÅ%22Rü#j7\ðx<ª– Wœ:EP´\¿'/†¥‚VÐJ¸TtØpA ´x¼Äôô HÕr4…ž:u*""BÚYŠ´AÕ½®\K³\ËÖÖÖËËë£> "#<þÜÜÜœr×’gÃ…©©éàà  ç„k v°t™º¥ Ýwư¡´Ro©àZ¸»P¡•î¸Ö;wNž<ùöÛo âÞŽ„kɳᢳ³Sà™#Ꜹ» ´Ò\×Ò’Ž®n‚õýÐ Z©·Ttx¼\ 5ZA+Õ¸›ÍVQHÜp1ÒÑ•qwA+hE­ZZZòóó/_¾|ùòåüü|rË':º:Fƒ ´Rq©Dv¸)¹á ®…» Zi½Vʇ¤¢‰k‘]ÍÞÞÞ€€›¬¬¬2 ‰?¥MÛŽôBÔDh­T\ªââb''§ÀÀÀÀÀ@''§ââbtt5jƒQééé…çqpp *”Q“ K­è£•bÁ¨¨ÕJá`TÄGv§ ŒaC+h¥úRQëZYYY£FJOOOLL„káî‚VÐj$J¥÷t0õ£Ò,ðl7¾@h¥LVÊ<‘2nÿ~Ùç«TŠH‰ã¢LX¸j"´‚ki–k™™™µ¶¶&$$°X¬øøx6›--$2\ 5ZA+…]«¹¹¹¬¬Œ<—Á`8;;s8Ù®5¢`FW=`4ZA+—Jd{›.ìÑÅ:¬CVʬC‘}® Ö¡PèZööö·oßÎÍÍ2eŠ¡¡áÀÀÚZh?@+hEy©8Ž··wPPPPP···p/W- £«°¾Zi·VÂÛ P]ÙÚÚ’Ahìíí•|%—ÿ(Ÿ½½=Ç£$‚ ,jjhX*hE­ÚÛÛeœ%üéºuëGH­dŸ+©œmmm­¬¬Ôè ,`±Xþþþº3<7ÒÏ–Wì•ÚgËSU*h­„K¥ððÜ%1Ôk)Xº<<ðl7 ³ k pG—|"¥ìs‡7Ê è"@‘#Xº \ ®×PèZÁÁÁ‚}òl¸Q0£«0K ­´L+±Ii¢UpppppðĉMMMßxã <^5®ׂkÁµP¡´¹R©ttå»Ý°Û ZÉ¿Û|O«Ýn}}}EEE­­­ööö÷îÝ{úô)lmX`W´‚V*.\ wj"´Òh­Ô–.DÔè „VÊd%{!ŸÈ§ÂS%4‰_zõêÕ‰'Nœ8QOO¯¯¯¯¤¤äõ×_|ŠE€¨‰p-¸\Kyª««cccïÞ½;uêÔãÇÛØØˆ§9yòdrrruuµ››ÛöíÛ}ÅnK¸\ Z©]+Å\K,]¦ÌXX,V@@@MMÂiTFƒ ´R²Tg¹ÜÎáTäY³fÙÛÛ““Þ£Göðð€káîBM„Vp-jÙ°aƒOmm­OBB‚Ä4YYYéééuuuË–-[¸p!\ 5Zi«VjGK:ºò«‹Åêè葲µµ5**jñâÅFFF‚ƒmmÝiiƒíí=¹¹A(ùêàà@I>AXVWS’OOn.Ã×—†¥‚VÐJ¸TÏ[[»ÓÒú<®ÿ7jw$-Ù£kjjÚÞÞ|ñâEiÞ*O‰”••¥§§¿àóŸ?~ük[Û”‰ ÆŽhhPæµÃÈÈìÙ3åó1;¶³¼ÜÔÑQù|^ôõéM·RA+h%\*“ñãŸ?~l`aaúŸÿü_Õ¶±ycýzû¡©@È{¸j"´‚Vp­aaggWTTÄápššš<==?~,­M¾yóf}}ÑIìÑÕè „VÚ¡•vìÑ5$´33³ÖÖÖìì즦&333…ÓHÄÙÙ911Q`¾_|ñ…ò®¬¬´³³£äÚ)ÌêiA±—ÝJ­ •ÄR¥Lžlhl<#.ÎóÏ646†kÁµP¡\KíÌ™3'55uݺuôóó“˜æÄ‰ÇŽ;zôèÌ™3G´0X[­ •zK¥öI]-YºL+ŸÏ—a¬ò¤QVVV4ÌêÙÇÚ}ÐJ›´²÷ó‹=w΋˕³½(XHó…k¡&B+¸–f¹–Ÿïëë»råÊE‹‘Ç¥å£ Z ß3FFF"Z‰ŸE­èùDJ¸\ ®×ÒA×Âst5ú¡•vh¥ÌstKKKÛÚÚ^{í5‘ªŽ.ftu‚ŽŽ6›=þ|‹ÕÑÑ!-™ÄöbkkkTTÔâÅ‹ŒŒäÌGµÚºu«‹‹KddäpóÑJ­„ïq­ÄÏÒ­\ ®×*{)¡´Rq©úúúŠŠŠZ[[íííïÝ»÷ôéSõš:º:™™Ykkkvvvkk«™™™ü'Þ½{×ßßßÏÏ|žÂùh½Vû÷ïÏÈÈ ·º‘¯:«•È=#ÏYº €kÁµàZ@Å`/%´‚V*.UAAÁ˜1cfÏžmii9yòäû÷ Fœ9s椦¦òùüƒúùùÉyÖ‰'âãã=ºeË}}}…óÑ­:;;›âÈWÝÔJüž‘ç,]Ð ÀµàZp-]@ž0]* {†™7h­”,ÕY.·s8•tÖ¬Yöööä6æÑ£G{xx¨×‘°GW'¨ªªŠ‰‰)..ž:uêñãÇÇ/“låˆoèNÓÐÐÐÚÚ*’´Ap\<Ôª¡¡á•W^ÑJü,:h…=ºp-¸\ ®¥<ÑÑÑöööëׯONN®¬¬üöÛoHƒ=º}ÐJ;´"÷è¦Lžlhlìùç?ψ‹346ò\ñèSÂORý]tt]\‹ä Ó5dª:ºÅ{ÎttÉ?MÇcýzû¡°\ºt)88˜|ü©ÆŽ.>(@¦ëâÅ‹ÒÂtILSVV–žžþ‚Ïþøñ¯mm+>úÈ`ìØ†e^;ŒŒÌž=S>ƒ±c;ËËM•Ïg ¡áE_ŸÞèÑt+´‚VÂ¥2?þùãǦ‚zû⡪ÉQtt½„éjjj’¦KbggçÄÄDòý¶mÛ¾øâ å SYYiggGÉuQ˜ÕÓ‚c//º• ZA+‰¥"—.ψ‹óüóŸåYºL7Œ P€Ÿ¿|ùòqãÆ7î/ùKÿp›™™™þþþGžº788,;Ù?ü0oÞ<‹5~üø>ú¨²²RZ9éÐPYk¤¹tww+-h ¸\ ®Ɔ |||jkk}||ä¬ÈYYYéééuuuË–-[¸p¡Ä| •€{÷î8p@F>º •ø=#ÏYªÑª­­möìÙ...l6;00ÐÙÙÝ‘j|õÕWuuu÷îÝ»wï^UUUJJʰNÏÌÌ\µjÕúõ뫪ªä©~_}õÕ{Ê:´fÍšªªª’’’iÓ¦EDDÈ(g·øý@Ë€k€6‘——Çår¹\nnn®´d"#P'Nœ˜:¨ŸÏÿä“O’““‡›–i%~ψk%~–j´zíµ×^~ùeú¨§ÍÊÈÈØ²e ›Íf³Ù[·n=sæÌ°Nß³gOJJJPPÐèÑ£‡L|ëÖ­#GŽ|óÍ7²“}ÿý÷AAA¯¼òЉ‰ÉâÅ‹=z¤|9ÂÂÂX,ÖÔ©Sóóó…ÝaÓ¦M–––ÞÞÞ2Ròx~\uòÍÍͽ¼¼1233_ýusssâodsøða[[[;;»ÿûß2Ê)Mñ˺\ ®×@Céèè`³ÙóçÏg±XÒ’IlmmŠŠZ¼x±‘‘‘œùè V[·nuqq‰ŒŒn>Z©•ð=#®•øYº •nut+++Éóùó绸¸+î䧤¤¤¦¦ÆÑÑ‘Ãá„„„Èh‘tuuÅÅÅíß¿ìØ±òç¿eË–wß}WF9'L˜`nnîêêºzõêÎÎNiùÄÇÇGEEÕÕÕíÚµ‹ËåŠ|Êãñf̘!#åªU«BCC«««…[-'dd‘ £ººzÁ‚«V­²ÝV]]>dJñ+Z±bEhhh}}}HHÈŠ+†{‡—ÙÒÒ²{÷îO?ý”<¸dÉ’}ûö577 D3¤%ýþûï{öìÙ´i“Âå¾L ;ÀµàZp-433³ÖÖÖìììÖÖV333ùO¼{÷®¿¿¿ŸŸ_RR’2ùh½Vû÷ïÏÈÈŒâé²V"÷ŒrrrZ·nÝŽ;***¤=FX„'N;vlçÎ3gΔ´÷FÝÔJüž‘ç,:h%ѵFmžÑµ³³+++#ßÿþûïvvv‚œ<ƒÜÖÖÖ+W®433c0\.÷þýûÒN?}úô7ß|#¼TLÆ•••Í;×ÐÐ0;;ÛÔÔTF9 ‚Ð×ן0aÂÎ;üñG©ÿB}}Á@¾H3N¤Ù!#¥\÷ŠB§ FRôôô‚´;@䊆;L#’~éÒ¥›7onjjª¯¯|töìÙ-[¶Œ3&---,,LB’­LAžË)C´u¸\ ®€†²}ûöÜÜ\++«ëׯ“ól7ˆì&XºtéíÛ·çÍ›G¦yòä‰x>ÐJþ|tA+ñ{F\+ñ³tA+Ýê膇‡'&&¶¶¶¶´´$&&¾ÿþûÃ:=444%%¥³³³§§çÀîîîÒRŠÄ_‘ÑMOO [»ví¡C‡ƒî²ËÙÔÔ´}ûvOOOáq,ásçÎMJJ"ïrÙHLéå啚šÊçó9¢ÀéÒœèÈ‘#ýýýGŽ™5kyÄÆÆ&33³··W8\žpÊÙ³gËÎSœ3f9r„Ïç>|xÈ%sVVV………ÂGž>}Êf³ûûûwìØ!8hdd4þü•+Wnذá·ß~n ÿ9,$–Sš @—kÁµàZh(¶¶¶999mmm×®]?~¼ˆÓ lVÄoEÒ¼òÊ+âù@+ñŸ0iùè V¯¼òЏVâgé‚VZÛÑ•8´fÍš±cǺ»»{xxX[[ËØL%ñô 6TUU¹¹¹M˜0áÊ•+GU¾œŸ}öYMMMLLŒ nÊ“'O$–“üÔÄÄÄÓÓ³¹¹Yƃ½{÷–——Oœ8qÈ #S¦¤¤œ?ÞÆÆ¦§§G___† òAíííÖÖÖgΜٽ{7y$111>>ÞÅÅEdc€ å®]»†«çž={222ÆŽ›™™)ø"ilÚ´)44T¸äÉÉÉ‘‘‘®®®666"7‹ÅúüóÏ<(8Îårýüü{\‡ÄrJèp-¸\ 9è»GWÚGxjÅH088øÃ?lÞ¼ùöíÛ”´àñoš%{táZp-4ȵ@í®5¢ÒS48T “ÉÔÓÓ›0aV£ׂk@ 0„€ò:Zü¸Ôˆ>$€Ž.€Žî388¸páBÝü/.\¸PeAÅp-¸ZS:G•ÿ-ø áÛI;DC@;~Áµ§£{åÊ—‘þ–k×®…††²Ùlkkëððð{÷î‰ÿT ·ÂË“§A¥¥¥o¾ù&›Í¶±±Yºti__™ƒ‹‹Kvv6~-€kÉøM 8ŸÏ_¾|ù¸qãÆ÷—¿ü¥¿¿_y×""33ÓßߟÃáò¹~ýº¿¿?›Íf³Ùsçνqã\ úð$†Õ“‘h&Ãê‰û†üV&‘~øaÞ¼y,küøñ}ôQee妧Æî½Ö~þ­.÷`iÕ¿zJL)ít úמŽnFF†——×HËþýûãã㫪ªJKK####""„«·ÈC®å¬ðóÎðÝwß]±bAþóŸßxãGÝ¿ßÒÒ299™ÌÁËË+##?ØÀµ¤ñÕW_½ôÒKÂÖÕÕÝ»wïÞ½{UUU)))Ê»VffæªU«Ö¯__UU%ÈjáÂ…Ë—/¯ªªªªªZ¶lYtt4\ mêÉH4ùO—èò[™D:´fÍšªªª’’’iÓ¦‘%­œâí7´¦ õ”˜RÚéô ®=Ý›7oŠ 0™ÌÇÛÚÚÚÙÙýûßÿ&>|øÐ××w̘1¾¾¾>lmm8qboo¯à¬ÞÞ^{{ûÖÖVi÷åo¼1zôèþþþ§OŸš™™Qr¯ËÈóþýû?ýôÓ§Ÿ~JDIIɲe˘L¦™™Ù_ÿú×ÌÌL2ͤI“~þùg¸p-‰ÜºuëÈ‘#ß|ó°ílÙ²…œhݺuë™3g”w­={ö¤¤¤=ZÒÎÎÎÀÀ@OOOOOÏÀÀ`„ p-´¬½®Œ™Hô ù­L"ßÿ}PPÐ+¯¼bbb²xñâG)_Ά††°°0‹5uêÔüü|aÓÞ´i“¥¥¥···Œ”<ÏÛÛÛÂÂ"11QöÒ?i_$“ÉLLL´°°ðññáñx‚ƒÂ ï7oÞlaaáååU^^.ûJůHä7h¸wyæææ^^^999‚Ž×_ÝÜÜ\ €øÙ ù[)[ñËÔÖn‹œ·½Ä”ÒN× _píéèÖÕÕ3Fä`GGÇï¿ÿ¾gÏžM›6‘GV¬XZ__²bÅ ‹åëë{öìYÁ)gÏžõõõe±X2j¬‰‰‰ÝîÝ»ÿò &˜››»ºº®^½º³³SÏ“dÛ¶m+V¬xùå— ‚pww?|øpoooggçž={jjjÈ4,«¶¶?·ÀµÄéêꊋ‹Û¿ÿرc+++ ‚˜?¾‹‹ ¹´OI×*))©©©qttäp8!!!d;ã_ÿúׯ-,,,,,6nÜøÏþ®€6¡¤™Hô ù­lH¶lÙòî»ïÊ(§œí·øøø¨¨¨ººº]»vq¹\‘Oy<ÞŒ3d¤\µjUhhhuuµp_Kâ4²ì/Á`TWW/X°`ÕªUC:vuuuxxø)ůHä7h¸wy™---»wï&§m‚X²dɾ}ûš››"ˆ¿‘Í¿•ýL­lÈ_=%¦”vºý‚ë©l3ñÝ»w ‚ptt¡ü9Nmm­ð‚&“ÙØØøòË/ ˜››wuuaaaÁãñ˜LfWW—££cSSSNNΗ_~™žžîääTQQ‘0wî\ß588X]]½k×®¶¶¶ï¾ûNpœÖ¯®®>w r.†‘˜çíÛ·£¢¢Š‹‹GEDiii|||aa¡™™Ù矾mÛ¶¦¦&‚ úûû­­­›››ñ‹ €bœ>}š ˆ?þXû\+..ÎÜÜœ\q$p$‹U__O~uÿ¸qãZZZ”t-‹µzõê%K–~óÍ7çÎËËË{ï½÷f̘±téÒ/^8p °°ü…†k v×b2™wäž>}Z† ˆX„43ôèD²?]Ü7$ž.ÑÊd|Q__ßòåËÿý÷¬¬,SSS¦'±ý&‚¥¥¥`MOO4gòÛëëë †ì”ååå&&&ÝÝÝãÆ.ªˆ Ò¾Hâ?¢®®Ž<ÝÑѱ±±Q\ò½Ä”2²¹"ñß Ù§‹ü#._¾¼qãÆòòòÁ½ýöÛ­­­þþþ¯½öÚ[o½ENçÈÿ$ço¥4A$^¦ŠQM@Æm/OJi§+ü .ѵFí™Ñµ¶¶Ÿ¸'kŽ ?/Þ±÷÷÷ohhHMMp-‰¿+ß|óðª0‚ ìììÊÊÊÈ¿ÿþ;îNIײ¶¶^¹r¥™™ƒÁàr¹÷ï‘-³˜ IDATß'"//oÅŠ&&&¦¦¦+W®Ø#\ í@š™È¹ñU¢oH<]¢•Iû¢²²²¹sçfgg›ššÊ6=yÚoúúú‚éG‘ΧHgIFJ9}UÓ?zzzA¯n."W4Ü)1‘ôK—.ݼysSSS}}½à£³gÏnÙ²e̘1iiiaaa RžßJÙ‚¨±—«²€ü¿õSJ;]ƒ~Áµ§£;kÖ¬ÒÒÒ!“͘1ãÈ‘#|>ÿðáÃär==½˜˜˜½{÷FDDìÙ³'&&FOOOÚé~øaQQQkkkrròäÉ“…?mjjÚ¾}»§§çQòäyóæÍ‡Šïëë»|ùòªU«>ÿüsòHiiéÌ™3ñs \K‘P+äkxxxbbbkkkKKKbbâû￯¼k…††¦¤¤tvvöôô8pÀÝÝ ò`WWמ={œœœàZhÃ5$ú†üV&‘ôôô°°°µk×:tH0U(»œâí7Ó›;wnRRÒ“'O†¼"‰)½¼¼RSSù|þ‘#G8]b‘‚8räHÿ‘#GfÍšE±±±ÉÌÌìíí=pà€Ä”³gÏ–§<¿A2°²²*,,>òôéS6›Ýßß¿cÇÁA##£ùóç¯\¹rÆ ¿ýö›pOOøÏa!±œÒÑ‘€ŒÛ^ä_/1¥´Ó5è\{:ºï¾û®ì]û${öìÉÈÈ;vlffæîݻɃ}ôÑK/½´cÇcc㨨(§GEE­^½ÚÊÊjêÔ©?>qâñǾyOOÏæææãÇÒwÕß¼ySàG2ò$bÛ¶mëÖ­322¾)™L¦­­íöíÛ¿úê+rÛ Aáááø¹®%'kÖ¬;v¬»»»‡‡‡µµ5¹kKI×Ú°aCUU•››Û„ ®\¹rôèQ‚ Ž?þÓO?9999::|ûí·p-4‰!ÑLä?]¢o(ÉgŸ}VSS#ˆöôäɦ'Ò~“ÈÞ½{ËËË'Nœ8d¨$‰)SRRΟ?occÓÓÓ£¯¯/Cù¿ˆ ˆöövkkë3gÎ~ããã]\\D"› RîÚµk¸zJü ’ƦM›BCC…Kžœœéêêjcc#r3°X¬Ï?ÿüàÁƒ‚ã\.×ÏÏO±‡ I,§4At¤ õ”˜RÚéô ®={tccc‘NhKHHÈ_ÿúW___ 󌎎>~ü¸À:ÃE-{táZp-ÔèZ2ºxÖÎyþ?ü°yóæÛ·oS2Fß Ñ ö\õ{t µæŽÑ××׈Û%++‹ò<“À¸\ è&©&“©§§7aº­¡hhñ/¸!n5Fʇ0NÀ`Ùtttttttt £  £ Е>G·³³óôéÓ )Àµp-ÐDT7£ÛÙÙYQQÅÔ^„kàZ ‰è½xñ*аG:º:º€ P]Ôå»wïBnméììôóóƒkàZ ×Q0£ U €k€V¹–¡Š¯ÐÑÑÿfÝñØI¸®#çZ#fthèè@G +†  \ºtIäHpp0dÀµ@+] 3º:“ÉÔâb_¼xÑÆÆFžÄÌ?ó8Ðt‚ƒƒƒƒƒßxã 4áx(€k¸Ì h½k¡£ ´Š¤¤¤ôôôîîî!SvwwKL&í8ÐúúúîÝ»g``ÐÑÑahˆõ,¸h­kÁ4VQZZêåå€8=ª¨¨°¶¶ž0aBaa¡4TÁd25z€LÓË×® àZèè-‡ÏçcÌH¤­­möìÙ/¿ü2A×-v-,]ÖZ˜bˆ$xðàA@@@yy¹ì|ŠŠŠÞ~ûm+++‹åëë›™™IŸ3gÎÕ«WÉ÷OŸ>µ³³knn&¿733sÚ´icÆŒ™9sæíÛ·Y?~|Ö¬Y,kòäÉ'Nœ  lÙ²ÅÁÁÁÂÂbáÂ…‚ñ¹¾¾¾¥K—ZYYM˜0aß¾}r^¯ð’'Ož|òÉ'VVVVVVŸ}öYoo¯bzJÌgêÔ©>NöðáéS§Ê¸.²„{÷îuuuµ´´Ü»w¯lU477çåå]šÀñDQ£ ‚8uêÔÔ©SÙlvHHHUU•Dçþ^‰ŽG„4‡”†Ä|¤9›È… þ”æÌ2ÊàZ@#ÚiÒò‘V륹‡´v—Œö \ ÐܵÐÑÕZº…ؼyóêÕ«…?-((àr¹Çwtt”χ~¸hÑ"WWW·sçÎôôtòxllì·ß~K¾¿téÒŒ38ùçùóç/^¼XSSóþûï¯X±‚ÒOšCJCZ> 8›Äï•V~ךÒN“‘ÄZ/Í=¤µ»d·gàZ€Î®¥÷âÅ Õ|ÓÝ»w ‚²ºÊÙ±cÇÀÀÀÆ#a'Ož\½zu^^ž k*—?ýéOä`ž¾þÿ Žôôô¸ººÞ¹s‡Ãá|øá‡ááá|ð™?dz°° ‚Ïç;¶½½ ww÷“'ONžï¼óŽð§¯¾úêwß}700 r–øqiù'%%EDDDDDlÛ¶M°!AÚu) 3 „uãÉãxÒjt__Ÿ™™Ù¨Q£ªªªV­Z%r–……Evv¶ðV iŽ7\¤å#Í‘^}õÕýû÷÷öö666ŠÌíÈ@¼ü®4ŵdä3¬v‘´v×pÛ3p-¸‚QgïÞ½_|ñ…Œh~¾¾¾üñÊ•+‡4¬={öØÙÙÙÛÛŸ}úÌ™3ÿû¿ÿ )Í)((°±±±±±!¢¦¦¦¦¦†Ü{)¸“ „uÀµ@…®5¢`.P>ŸŸ––6äØtÀÝÝýÁƒ¿ýöAfffÐ×mu-ttAˆ=­[€ìÐíl6{Μ9äS…è\NÈ›gæÌ™‚?/]º„çRÂñhâ$p6×p-×BGŒŠ“êí  Ð>'³àZ £ Š€€¥¸èŽk¡£ Ð DÏ  €k€»/@«@G ‹ ¦ ®ZìZXº<<åå1Ñ×R Y´´´”––öôôÁ`0&MšÄf³áZ¸\  •®…Žî0xÎçÿ')‰ ˆÏŸ75 ‚ A»»»“†ÛÒÒR\\×ÀµàZ­t-,]…ÇŽuÕÖvÕÖ;5Ð8ôþ®€kÁµÚíZ˜Ñ•—®ÚÚ[G’ïo=:9,ÌÄÚ² )xxx”••ݹs‡ ƒááá×ÀµT“É”öV¸p- ѹÝ””&“)íÓêêꀀ‹PSS#üÑÕ/¿|Îç“ïŸóùW¿üw3‡Ãñöö òööæp8ô/óÉ“'§L™2fÌŸ¼¼<¸p-5Âçó×­[gooÏd2¥5¥d8’<Àµ€k¡£« ÷îÝ;pà€Œ6lðññ©­­õññIHHþèÝÔÔU%%½ÿó?«JJV•”¼›šªLIêë멺( ³zVZJÃRA+hEI©ZZZòóó/_¾|ùòåüüü––ú[VVVVzzz]]ݲeË.\×BM„Vp-5²mÛ¶7n\¼x±««KÚ”¬ G’¸j"´‚k¡£«|>ÿ“O>INN–‘&//Ëås¹ÜÜÜÜ‘+Lcc# ³zzó¦v_ ´Òe­Š‹‹œœœœŠ‹‹éïZ'Nœ˜¥$¢¨ˆAÃRA+…µÚ§ØkÁ×_‹¡‰VÆ|~wZZÿƒ ˜€††uimmŠŠZ¼x±‘‘\ ®­(Õp=psn.\Kœ¦¦¦+W®XZZºººž;wn¸í(aJKKÃÂÂÖ¬YׂkA+ù_… m­¡ óâÅ Õ|ÓÝ»w ‚pttT×¥ššš þ”¸êÆÎή¨¨ˆÃá455yzz>~üX$Á¶mÛvîÜ©|ax<žƒƒ%×EaV=yy Š[GÏ Ô5­"""Îj̘1mmm"cù4ÑŠ|>Ûpinn.++<ÛÍÙÙYxëÈéÓ§ ‚øøãiåZd¢££ÃÃÃ7oÞ¬¯¯ׂkA+²®NÜpæ \K„ñãÇ=ztîܹùùù±±±••• 8AL&ÓÝݽ¡¡áÂ… nnnâ àZp-h% a7n˜in[kDÑ¡¨Ë‡•¶·dΜ9©©©ëÖ­;xð ŸŸßȆªBmV êÎNÏ „Vò#ÒËÕ­8ŽF æÄ‰ÇŽ;zôèÌ™3¥¥kÁµ åYÝÕ×§C©èæZ^^^‚÷Ò¦kät¤ÌÌÌ[·nEFF^½zÕ®…š­ÐÖ tý9º"a·oßž››keeuýúõ¤¤¤‘û^Gìz¤„uÕš „Vò3fÌ-Óê’ô7¨¥K—Þ¾}{Þ¼ydŒÓ'OžÀµàZÐJYMZÿ×°cÇŽ;vXZZ.[¶ì믿V¦ÅápBBBÖ®]Ù×××BM„Vhkºø]áé\‘©][[Ûœœ”£AЊæ¨}3ºAk®YÁµàZÐJeYÑdF—n®5qâDq·QÀ‘§DGGGGGõP¡•bDDDV/£­%]ŸÑU ‚V4¿@í›Ñ¨‰p-h%'4™Ñ¨‰p-h…¶:ºšFΠÍ/P+gtj"\ ZÉ}ftugÏžUUU‘ÏÞ¬¨¨Pò•ÜH¬|>”äCD“ K­4E+sss‘בЪ¬¬¬ªªŠÂÇüª ŠºL ˆ¨Ñˆ¨Ëò£}Q—eCÛ¨Ëp-¸´R>+ º ×BM„kA+Ùn&h›in[kDÁŒ®zÀx?´¢ùbFW ÀÜæF •´9Ù¯•,¹´µÐ~€VÐJÃAGW=MÆ‚‚ª~Dy<U ŽçÏÓ°TÐJ­†Û¼¼Ž3FÚòõj…&£üÙÚÚ²Ùl‚ ìíí•|%—ÿ(Ÿ½½=Ç£$‚ ,jjhX*hE7­ÚÛÛ‡õj×ÚJa©œmmm­¬¬`J*{)¡´Ro©Ô–.ª–Ó ”Yº,ŽÂK—)‹áZŒ´Rëxp-¸#áf#×6ÃÒe 8 ‚V4¿@D]¨‰ÐJgµBÔe¸j"´‚VZ:ºêëû¡Í/PËöèþôÓOuuu*[À×BM„V¢.õPQ¡ÚZèèj$ ‚V4¿@-›Ñussknn¾~ýzyy9ŸÏ‡¡&µ • è0£«›®…x(ˆ‡­{¼â¡Ðе°Gwx`ßÐ °GW>Ÿ_]]]SScnn>aÂSSSÁGÃÚíÆd2»»»{{{ßyçÒÒÒLJ„„ÀµÐ&3¤É]ª\ m-´ÒÍh¸Gw¸®5¢`FW=`¼ZÑüµrî¨Q£}}}9΃”,LVVÖ¨Q£ÒÓÓéé3˜ÁÜ´R,þükff=bÅSëZhk¡ý­ •fµµ”D‡ft¿ÿþû¤¤$7yòääädoooñ4ÕÕÕ±±±wïÞ:uêñãÇmllD`”hî°Ÿ’ ê²33³ÖÖÖ„„‹Ïf³;::h{Àµ@ÔeÍ®€œn†¨Ë²Ñ¡ÝŒŒŒ'NÔÕÕ­Y³fáÂ…ÓlذÁÇǧ¶¶ÖÇÇ'!!aä ƒÑ hEó DÔeØÛÛß¾};77wÊ”)†††º`¡¨‰ÐJw´BÔe¸j"´‚VZ€¡îøÝ·ß~KD__ßóçÏÍÌÌ$¦ÉËËKII166ær¹žžž#WDlƒV4¿@-‹ºL- ,˜>}º¿¿¿îX(j"´Ò­èu™Z4+²j"j"´’á ÞSÉE×¢ÝÚ£Ëd2-,,âãã;&1AGG›Íž?>‹ÅY‹8ÐÖÖ–6ØÞÞ“›K„’¯<’|‚¨8uŠ’|zrs{òòhX*h¥°VÓ~3z´Èšhõ¼µµ;-­_­»>ÂÃÃëêê.\¸`ddDDww·.ø'ư¡•îh¥­3ºˆ,€ÝòÐJû¢.Ó-²­Ð¹¨Ë½½½?üðÃW_}õóÏ?‹jggWTTÄápššš<==?~,’ûF€=ºòƒÝnÀ GÎñY®ÀH»™†ºÖˆ¢þ]&“Iö?lll²²²Fè‹–,YR]]m````` mˆbΜ9©©©|>ÿàÁƒ~~~#wÕï‡V4¿@ìÑ•Á7¦NúÿØ;ϸ&¯¿ŸLÂöވ⬠Š›V[ëèßÝ*ÕJÝ£îUg«RÅ=kE[ÜÖQhEqV”½Ã ’äyE„€Œ!\ßù@¸ï“s®ÜçâüÎÔ××/obj"´j9ZiÞˆ®Zí,Ðhž‰š­àZÍ×µ4$ÐUÐÓizõê5tèP33³M›68p@©Ûnذ!$$ÄÜÜüöíÛëÖ­k¸òb-´RójüÝ«W¯Öù^??¿mÛ¶5Út˜:£ÚI€ …¨hZ”£££ª¦eZY©a® •ºiUÛã…¸\æJU“ëãZЏ\nýwغuk5‘jRR’··7—ËõööNNNnZDûZµ­Ôsgú¸–Jhú©Ë-s:M\\œª#&Ux玪gΜàà`*•Ú¯_¿íÛ·+’…k¡&B«æ•”šïº\×jPš~DW%Óišè9ƒVj^@—Ë%„deeq8œ&´P¸\ Z©<)Å]•ìçgdd´gÏž&w-''§ .j"´jQ®¥G…®¥š~n#O§áóùãÆ›>}zå(—’——ÇãñÈåróòòÊÿIš“#8tH–›[B©ç«£££JÒ!„˜&%©$½޽Õ0WЪÎZuJëüj›•Uá5Ѫ”Ï:$~ù²¶u¿Â •÷K¨9ŸþùŠ+òóóóòòV¬XáããÓ„ ׂkA«ªrUg|F¥ÖÇ?+x©:¸Vs§ãôohU·ã…Ôäôous­¦?^(66vÞ¼yaaa„nݺmݺµábÝgÏžM˜0aĈ«V­¢*â·µµ}üø±±±qfff—.]âãã+\ ª-ïÑs­¡€õލ<¢[çmëU®Uݶ¼¯~'ÀšÔQFAAÁܹs/]ºDññññ÷÷×××o8ód³ÙÕL`†kÁµ UUIÕÙU¸ë²bD·É]+::zÞ¼y="„tîÜyÛ¶m®®®jîµàZЪ*ª²µö2ÙÒS§šc[«Aiú©Ë6æØ±c|ø‰'!»wïž:uê¿ÿþK4ÔDh×j¾m-•ЂŽš1cÆ“'Oú÷ï¯ØÅ´¨¨ˆTÚ.uÆ !!!æææ·oß^·n]Ãe»ÛA+5/ æíº\a›ûfqÂPÙ–Ëå÷^†kÁµ UC'¥ÂÕnõñRºVddä¬Y³ÊŽŠŒŒl ?ÔDh×jám­¦Ñm´é4JçþUxÓÆÆ&88¸Jž3h¥æÔÈst›p-ÔDhÕ$I©Ïˆ®ªhݺu@@€¯¯/!dïÞ½­[·n Šš­àZ-¼­Õô#ºS¦LñòòŠíÝ»÷Ô©S[‚ù¢ç Z©y5oDW…4Ú9ºp-ÔDhÕ$I©Éˆ® 9xðàÍ›7Ç ÝºuëÀp-ÔDh×Rç¶–†º-s: zΠ•š#º5äÕ«W:::p-ÔDh¥IIiÞˆ®««ë¥K—233333/_¾¬¶;Qa×eìº ­šõ®Ët+¢˜N£8^hÇŽ-d: zΠ•š#ºJ©°h–Íf{yyÍœ9³%4ïÝ»§ª¢qqqªjp¼¼xQ s­ÔG«ª…Õ¿vâpê|o…W###i26 ÃÆÆ†ÇãBêùª8X¤þé888ÄÅÅ©$BˆIr²æ Z5­rss•¾Úòù*Ì•‹‹‹¹¹ys·”¦?^¨üÝÏ>ûÌßß¿%ly@#PŸã…ª¡Îç ©,r®Ó–÷ÕS«ƒ:ª?ﮀf{`<³É]+((hÕªUÉÉÉe­¾&4±OZ(\ €:Øšj›gæZ JÓè¶Ìé4èï‡V£U}F!ªúkÓj…±‘¦s+ UËÑJóÖèΟ?ݺu¹¹¹‚÷Ô!‘“'O¶k×ÎÈȨgÏžwªxœ’’’¼½½¹\®··wrr2\ 5Z5;×Ò¤5ºM?¢Û¼@/#ÐŒn¿úÐLGt³³³£££÷êé鹺º*fÇ)¨ÕØ\ €–ìuðÌ&w-›¨¨¨zn(0a„ŋ;::ž;wnñâÅñññJ¯qppX²dÉÆŽ9RùŒèж¦#ºup­¥éGtƒ‚‚ÜÝÝõõõËÖ¼µ„Ç=gÐJÍ ¨yktŸ?îìì<`À€8;;?þ¼ÎI;;;õP¡•¦&¥&#º*t­eË–×§,ÇŽkݺ5‹ÅêÕ«WAAD"©|Í;wüüüX,–ŸŸ_HHHõ â¡C‡?~®…š­ÔʵԤ­¥!®J¦Ó4;°''´Rójä®Ë”÷Ô3Ë–-;vì˜\.OKK›={öÖ­[áZ¨‰ÐJ“’RŸ]—UåZ ,X»v­©©iý»çø|þ¸qã¦OŸÎ`0*ÿ5//Çã 8ËåæååU•HttôðáÃ.\8nܸòïKsr‡Érs CB!õ|uttTI:„Ó¤$•¤S¢×»·æ Z5#­:H¥•_ŸQ©*ÌU)Ÿ/8tHüòeº–Jhú©Ë*™NÓh¨j:M\\œªþ%«0©Â;wTÕR϶4­ê3mÏÈȨªöYmçÆ¨\«ºM§ÉÊÊŠ‰‰)›Nãââbll\ö×ZM444äóù†††ÙÙÙ"‘ÈÝÝ=11®…š­Ô-©:{`{™LU±®‘‘Ñž={šÜµTØ–›0aˆ#V­ZEU&‘­­íãÇ333»té¢tz3›ÍöððHOOÿûï¿ÝÝÝáZp-hUsª²µö2ÙÒS§šc[«A¡7y#L1ÆÏϯ…œE©ýýÐJÍ ¨y#ºÆÆÆåݶ>”––R©TGGÇk×®q85é¶„kÁµ •ÊÂ9õÑU¡k©„cÇŽGW…®e``°sçÎò-¢:$2cÆŒ'Ožôïß_Ñš***ªœÔ† BBBÌÍÍoß¾½nݺjZÃ>>>‹-3fŒP(D[ m-hUOÔd®ÊÛZõ¤Åíº\ý^ŸÜ-;‚fv]®`¾ƒV¼–ýZö×ZMäóù\.·¹<p-lZê¼ë² ]‹ÍfÛÛÛ0à—_~¡Ñhj~8\ €:Øš:ìº\×jP¨xbÊSóÝ›°³¤á’BÏ´*CóFtUHçÎ'OžÒh…êj"´j9ZiÞ9º„ˆˆˆo¾ù¦ž{/õP¡•f»–&£‹@÷#ªÙ-;ìØÜ´Rº/_ _m³²ªùkjUŸUEDDDÿþýþùç¶mÛnÙ²%==½%Ø#VpA«–£•ú캬BŒŒŒ.^¼hdd4pà@µõÕ.¸Pl  ’‰ñŽŽŽªš®Ÿie¥†¹‚VÍE«ªJ$p¹£GVU®ê¼àBÝÀÔåøänØ °Y».תI¤a».W >“ðùü€€‹¥Îÿ àZp­«UsßuY…®U¾ñ³eË–µk×ÀµP¡U³Kªš]—ŸQ©*™½ÜÈm­#º¡Ø-P$U¿[`ýA?´Rójä9ºªÅÁÁá×_1bDxxxK°GÔDhÕr´Ò¼Ýò]ü ,Pç(®…š­šÖµÔª­…@·¦”m¹\~ïåºíX°Z©y5~×åúgæõë×óçÏ?s指‡GK°PÔDhÕr´R“5ºjµy)\ 5Zµ×R«¶Ýš"ø˜²7Ë_cccœ““sëÖ-kkë†Ë zΠ•šPóFtL}²Q\\|üøñ±cÇž>}zéÒ¥ÿý÷ŸzšV»aµ´ªÛ±@ \.QÑñB999u^í¦B×j™ ý­4L«j–c¨Éˆ®º¹V‹[£[O°n¤YktkÖèVãZ½zõšzô¨£‹šׂVµE=×èbP5®­ÁµÔ¤­¥°UÓ€µÐJÍ ¨‘kt«ùµV´À(5Zµ(­Ôg®ª\«¹ Ú÷îÝ#*š§ªéú//^TÃ\A«æ¢UU %:q8*ÌU\¨›kaêríPÕtúL]®•LŒ©W{½NÓi*»-&lþúë/¸\ €Fð4¶Ð͵Œè6 èï‡Vj^@ÍÑ%ï‘BM„kA«ªP“]¸j"\ Z5¾kiR[ nMÁþ¥Ø¿´9jUŸB«úkÓjUŸýK«ù|¬à‚V-G+5Y£ ×BM„kA«Æw­zV®…©Ëµ;6ëb×嚣ñ».W“QáZš§•†íº ×BM„kµp­šÝ®Ë5q­#ºMzΠ•šPóv]¾Z ÷ÌCÁ<hÕ¬ÏÑm®…¶ÚZЪn¨Éˆ®º¹ݦk! •šPóÖè*–‹ØÛÛôëׯ%Ld0666<âààPÏWÅôŸú§ãàà§’t!&ÉÉj˜+h¥>ZåææÖáՖϯó½^ŒŒ\\\lllÌÍÍáZÜ=‡„¡v]nä]—Õ͵0u¹v`'@ÐŒÀ®Ëå …‘‘‘b±ØÑÑ1))©M›6,«ì¯˜<°¨ó®Ëp-@­<­Éw]®ƒk5(Ñm0J ­Ô¼€š7¢{ïÞ=##£îÝ»›šš¶nÝ:""F„šׂVJQ“]—áZ¨‰p-hÕø®¥Im­è&%%y{{s¹\ooïäää:_£°Z©yÕîY?¿üÚTÒnݺ988(jkk·mÛ®…šׂVJi ]—›»k©•#¡&µ U¹–&µµZP »téÒž={¦¤¤ôìÙsùòåu¾¦É;K.)ôœA«2ÔD÷MHÈ‘áÃïïÚUZRR“{ÿý÷ßò»#ܺu ®…šׂVJi ÝæîZjåH¨‰p-hÕ@®¥Im­èÞ¹sÇÏÏÅbùùù…„„ÔùuëÂAÏ´jˆ¤šÅ®Ë¥%%÷Ž|ùå›Û·kr»bS59Ä®…š­HË;G·Y»–Z9j"\ Z5kiR[‹Þrݼ¼<7pàÀ+W®äååÕüš˜˜˜ÀÀ@¹HTÿ"'gîøñ433izz}^ó ŽDRÿthffù±±NNõOGšž. )ÚÚê–+hUg­,™Ì†ÈÕ‚ šP+}këÒøxš‰‰AY½•ËIcmª×BM„VÍH«:{ jµš?s¦f¸V©&ÀµàZÐꓯÕxšB«Ú¶Ð4¾­Õ‚]‡Ãçó¯]»–™™Éápj~‹‹ËêÕ«?ÿüóÏ+V¬¨flmmUR.&UrïËÓSÝr­ •Ò\mmÝšÎbuþî».S¦ÐËíéׂk¡&B+¸–Z9RM€k¡&B+¸–ÊiAS—{õêµk×.‘H´{÷î>}úÔù•P‡óô!)Éëך]@h¥IZ9ôé3ùÜ9O?¿:oÙ\šò¿Âµðt¡&B+¸Vst$<]¨‰Ð ®õIZÐˆî† &Mš´}ûööíÛ=zTñ&›ÍÕ_Ó0™L5LJÇÇG³ ­4I«¯víªÕ½W¯^UjÇp-<]¨‰Ð ®Õì Oj"´‚k!Ðý€Mppp…7Ë»sU×”ç믿Ö`‰h<ÐJCµRX­X,¾uëÖàÁƒ+{1\ Oj"€k5´#Õ¸€Vp-ºM€»»;D ™" ###i4Z^^ÞRÜ®\ ®h®EÅ÷Ѩ|€;û=ÕÜuòäÉvíÚõìÙóÎ;DÍ‚W+­lݺµì²–©Uåg¦&w5ŽVoß¾½wïžžž^‡=zdccg€kÁµàZp-ׂkÁµ4Õµè¶*à.*L7ªÌ¥K—SSSüñlj'5;^­´"„„‡‡T“NKЪò3S“»G«œœœîÝ»»¹¹ñx¼¸¸¸ÀàZp-¸\ ÀµàZp-Mu-м±ÎDzöì!ÄÉÉ VØøØÚÚ>zôÈÄÄ$33³K—.ñññŠ÷+ì!Qá×2RRR<<<222œœœ”¦­D"QïÞ½çÍ›7uêTÅûU¥Ó´*ÿÌ0Œ ZU¾K´ "„|ûí·p-¸\ ®×p-¸\«q\«AÁˆn‹ ìw.—[ÍîJ—Ïç7núôé £†é´@­Ö®]ëææ6f̘ڦ£‘Z•f*kUù®– €kÁµàZ®ׂkÁµº-‚²Üù|~­pöì™——WŸ>}Ö­[WŸt4^«;wž9sF±hDñÚbµªðÌÔä®– €kÁµàZ®ׂkÁµèS·Ü;6gΜ¬Y³†J¥5;^­´ÊÏÏ/[^¢xm™ZU~fjrWKÐ ÀµàZp-ׂkÁµàZ Öè¶'MšôüùsÅîÖÖÖöµSøEå¥å¯IOOçóùÒV({¿r:-P«ôôt]]Ý ZU¾K´Âj7¸\ ®×p-¸\«‘] . Ép-€F¹Vƒ‚©Ë4 ºètº]tÕ vÕà;kÌoA>¢ü㤢542™lâĉÍ4ó'Nl´#ÓàZp-¸\ ®àZp-¸ÝZ3Jµª]"‘hæÌ™³f͋ŵ­œçÏŸ÷òò266®IÕ•Édƒ®þ²Ë—/÷ïߟËåZ[[?>!!¡ª|ªÃ¿¥‡t7GAýËÒrþñ_¿~ÝÍÍ­¡?¥æÕSé•UÝîæævíÚ5¸\ ®×jÂv<\ ®ׂk©ƒkÕ¼~5#×Ò@·þµë×_MMM OLLܺuk­n?þüüùó—,Y’˜˜X“Úû믿2™Ìê¯Ù³gÏÂ… ###;tè0zôèjò)( ±8s挧§gCJÍ«§Ò+«ºÝÓÓóÌ™3p-¸€k5a;®×@\«æõKS]‹ªÙÏК5kx<Ç[»ví©S§ju»¿¿ÿÖ­[ ¤­­ýÉ‹>|¸ÿþ}ûöUÙ…  ¤«««¯¯?}úô·oßÖ?ŸéééÇçr¹íÛ· -{ŸÍf¯\¹ÒÔÔ´GÕ\×£G“Õ«W—õô(íݬêƒ*Ãf³W¯^mbbÒ³gϸ¸¸²7Ë_PöóªU«LLL<==ccc«/iå½~ýºwïÞFFF½{÷~ýúumŸE ===ƒƒƒËLá³Ï>344,S òÕ³wï^[[ÛÓ§OW“Ϫ©\ÌF&,,¬B/#›Íþd¡ø|¾½½}qqqÙ]ÅÅÅ|>¿žÕSé•UÝîêêúàÁ¸\ ®×R¹kÕ³"õàZp-¸V#»VÍë—¦º–&º ...„º¹¹)æ®ÔœÈÈÈääd'''cccŸjêvAAÁwß}·sçN33³š§¿fÍš¯¾úªš|ÚÙÙ¶jÕjÁ‚ùùùU¥3gΜqãÆ¥¦¦nÙ²ÅÏϯÂ_ãââ:wî\Í•óçÏ6lXRRRùú¯´×§úª€žž^RRÒ—_~9þüO:`RRÒˆ#>yeåÍ;wذaiii>>>sçέ[çtvvö¶mÛ¾ÿþ{Å›?üðÃŽ;²²²ÊD¨üCõäååEEEùûû¯\¹²Îù,_ÌF&55ÕÈȨ¶…âr¹½{÷>{ölÙ-gÏžíÝ»7—Ë­gõTzeU·s¹Ü””¸\ ®×R¹kÕ³"õàZp-¸V#»VÍë—¦º¥Ñ?{öŒâääTÃD‚‚‚ªyúÙlvù¿r¹Ü´´4űXlaa‘]¾G§BR•o_°`Á?ü@§Ó÷íÛwîܹ;wî(½ý»ï¾344T è—O¤ª …3gÎŒŠŠºté’AUù$„Èd2ÅT¤¤¤sçÎ)-µ©©iY×…B)(((ûô´´4==½ê¯411‰Õ××å³ZAª>H鑚šª¸ÝÉÉ)##£²2ŠŸ•^YM²JdbbÇf³ œœœ233«¿½ÂñÏ?ÿ,[¶,66V*•–•èóÏ?çóù^^^:u:t¨ŽŽNU·Wõ):::R©ÔÐаLäÊùT*ˆÒb62ÆÆÆ)))åç†Õ°PÁÁÁëׯ tvv~óæÍèÑ£—/_Þ·o_¥ŸRÍc_“+«º],[ZZfeeÕ¶ÔAAA„o¿ý®ׂkÁµjõ¥ÀµàZp-¸–Ú¶µ”Ö/µr­E“Gtmmmcbb?GEEÙÚÚ–ï[údE²´´œ7o‡ÃÑÓÓóó󋈈¨êö   }ûö•ŸtQÍÅÄÄôíÛ—N§_»vÍÀÀ š|B¨TªÝæÍ›ïÞ½[åWH¥–u‰U0Ä ¸š+kô¬Ôéö²ž …"•J !åg\Ô– %ªm7M…ëg̘±jÕªÌÌÌ´´´²?={vÍš5FFF‡>|x2©ðkV–¦Ò|V#H:¯âɯ<¦&…òòòJOOßµk—L& ÈÌÌôòòªmõ¬á•UÝžmii ׂkÁµàZ*w­zVd¸\ ®×jdתª~µ×Òä@wĈ«W¯æóùÙÙÙ«W¯9rd­n6lØÖ­[óóó <<<ªº²ÂNÕØz``àðáÃ-Z´gÏž²î«êó™™™¹aÆ.]º”ïò)Aß¾}×­[WTTôÉ)½ÒÓÓs×®]"‘hÿþýu¸]i–!û÷ï‹Åû÷ïïÖ­›â++«óçÏ(½²{÷îÕ§Y™Î;ïß¿_$íÝ»÷““OÌÍÍ=zTþ’’'‹7mÚTö&ƒÁ8pà¼yó–.]úêÕ«òîSþ×Z¡4ŸU ÒätëÖ-::º…¢P(“&MÚ¾}ûèÑ£ýýý'MšD¡PêP=+|õJ¯¬êöèèè®]»ÂµàZp-¸–Ê]«àZp-¸\K ]«šúÕB\KC]¥KØ.\hffæááѶm[KKËj–%(½}éÒ¥‰‰‰îîîvvvׯ_?pà@ýó9mÚ´äääI“&•í@PTT¤4ŸŠ¿êëëwéÒ%++ëèÑ£U¥¹}ûöØØX{{ûO.ßWzåÖ­[/^¼heeUXXH¥R«¤æDÉÍ͵´´ç7n3fL«V­¬¬¬*< \.wöìÙ»wï.{ßÏϯOŸ>uÛø^i>«¤Éùꫯªß£šB?žÉdnÚ´‰Åb7®šÛk^=•^YÕí÷îÝ1b\ ®ׂk©Üµj¾Y\ ®ׂk©ƒkÕ¼~iªk©ïÝjºôP52™ìòåË«V­zòä‰JþâkjÖÃäÉ“ÿý÷æ˜ù &=z´¬QsT²Ú ®×p-¸€k¸VÍ]«A¡«§‚¨º ›Í¦P(vvvê6¯4 T*µ™:/!䨱cp-¸€kÁµàZp-×Ò<×Ò@4ëuøß €k\ €&„ Ðètºètu£QÏÑÍÏÏ ‚è€æ\ ×€æHãèæçç¿yóŠšQ{®€k@s„"—Ë¡kt Ðètºètº].º].€@ø”BšèFEEAšèÊår¨@cÀ]tº].º].€@@  .€@@½)ͼzpÓâ}}}}}§Í]ùÛÉ»‰Åòú§+˾¹ÆoÓ¼4ýò ¿-Ï„Ÿ¼Côúè\ß—ÒKße,õÂ2_ߥçSÞý.M¿¼Âwîá‘Ò›K^îš³ìlri5M¿´Â·"BóëVVIüÉÅ ÄŠê¡$1hɼ}Q¢ú^k*{È __Ù½zÎ÷¾¾¾¾3mþã^ºX©RrÁ“KvE*û™ êÒ®U³}}}}§/ÜüÇ}%)Èeñ×R¦wqbïþ¤í×k.”(ž>iFö°NQ§³ë˜ýâ§ñ]û%>+©¦€Òû›_µv:â„XZ«¤KŠ×ö}:â‚Döá-ið´ÿÚ.׬fDüÑ©ª‹KŠ×_ôTc€æ @S#ÍÝóó‘8ÛA£~œàj¦#ÍK >õÇÆ8ÁŠEC,ªù šA»ll>š–eGg­Ðð¸Â¡f ‘æ¼xšEˆüé©éÙ» IDAT þç–¦4"/|û"“é8ÊJ«y¡š [¼À‹WÖÕJ¡±t)šùÕÖXvåˆßžßw1»Ç»èKRClùýˆ]«E= ËõRËŠ’^¾xõüöÅð|×~JÉü'‡.öÿqÓRg­ÌG'ÿïðoLóµ£í˜å.I:÷Å‘×<Û3ÛuxDy=}ù’è,ÝÖÿçÝð‰8ô–¨ËÎv‡ûÑ`è€æ ²‘儬_÷¼×0öõ?ŸYÎÜ4–Üãæ[)akžj½á;¶~fÖW£ò¿ù‘~`Sžë.Ýž5Ї‹îÅõù…5£sáîÀÂlBo7Òj뮣–,ö\ÒÂmüg|¹aîH3aBdo/$/òç?Σݗ–¿ý¤{kÆëÃ)¥djdîz·½CÅ• +—ÜÜ¿æxAR ÑuàÌÙd7¥ /uS— ‰)‰¿EÚøt5)mQ9ÆÏöígF—ç?þýØ£¯WþßîÝþË|Xƒ.¼Bä…—^ÚLÚ°{÷¯~mÓ¯ü–-N»¶óÀî׫¶ïòŸÝ5÷FX¶ÒÄU%ø! Õuìd)yž&&ò‚è¼nݺw3Îxð*_F$/ÞH,:9±)D^þ»ÿ© ©wl™ÛGv#`WH–”I΃0â½ÀÇ–e_™DÛ~æM gË #·é½p$çѾ€¥½go ØùËŒ.‚K;=ΗU.ò#Z¿9³½¹œsþ±³>©"?•…ÆŸÛ~ô…ÉW˶íðŸß<ú/ï#¡(ú­z;–<KBäůBµïç¢[MúŠl+={è6mðâßvïÜôÓ›ÓÇÂrÊM}•ñïÖ¨PaÙåçËÒ ]Úµ2Ó*ÛHš÷™v¢]ômŠ6¯ÎI‘ÂÉ@½Àˆ.4-2Q^~©®«þ»1UQô¡åÛîçÉ!„i?zÕO}ÛMYÓJ—ÇÑ"’Bº¶6M"T,K¤™ôÖÃZ›J´]:9èD§>Í´1§«……èxŽü<øéUeŸGÑ­"Áa޾[“aQ9“”)œN“x&²Ï ÿ‹t·‰*àutãP‰\uã«ïOCZ1qì7vXèÊЗù_ªVÛ1#:[èQˆ^¯oF=\z*,u¤£ýGsw¥)-þWY(æÈ—ñdëšMÕ·Ð"¤ƒñ¿ìgóågG9[ì¦Í¡ŽU)(¾t±èô¢¨ã¡¬_n˜~åVêèÈø7ë?K³«ãõŒé„t1]õ¿ì‘ r;9…ùõr˯ÝkÙ5ÏÒ_²ÀÈÕCÞêE9}ü³/fçåt·^ù•Ž…p¿°Z~!w !TÇÿ‚>Ç’¡Cd¹ ›.|¼†9OIa 2gÊåYFªD`§=`…ûýBŠ!f^]åÃJ¦›*HËËmB˜£–¬ÿB*'ÂÈ›ƒ¥rBDÉ÷O^ ‹/ Zú\±˜ð÷Ñtyzï×B‘ 2ŠX¦<–"†¡êš±ª˜KYE‚åþ5pÛxpþÍ´zž ×n$—N'í;°¯‡½Î$Ï3ÙîmŒé„H‹²òEÙ7~]pçݧHEĸ@(#4¶…Ñ»¹ÊK¿¨”]ªÉй³z¾/$T›C%y„Ð , è„”eÑ8æúï GçXrÈs~‘L¯R‘?ê/Pž)§Ò]²Âì]KãwêPXÈå%by…-͹›ø¿)Iã¤R•Å „JaP)D&}œs-¢Ëu@Àˆ.4½›zÍ\Źrêï -—ò¥„¶îÐ÷ËEóR÷!ýß ²wûœÛT]#û®ƒ†vŽ:¾wGÈü®JZþ6ŸÏšXx pŬ}4ãŽ_x·Ò¹C­<ÄTE‚?/ðâÑÊz¶í(Ïœ;[¿[¼©eÕÕ]çîÓÏìuÞMŽætžâ—sìøö'…„Îuë;e†·)-†hYî!¾´~öÞо£×”YC­k{¶͸÷ôrœøuÞI a™w:sbgµ@饆NöÔ½ÛN]?·«²üÆT¾IËaÄì %Oÿ<ÿ(Ѷéé3ÔæFV%™t{¹Óÿç0º-‡ZMy ^•Œ\ÏI£_<¾jöB\·>ßNêd@!™µ/TùÞ kŸÇ ŽœX=gŸœèX}öÅìq9ÔÒÄûï{,im§§<-Í~‹ÿ:¡¸(ûàÚÿ>üÕzì¦å}¹HŠÃ8§ó©öÆÿM*'Dφ=b½Û¢aL!Jc6š‰á¶ñÒ5±]7J “é9Ö~Ïx–.ÃüÐ/Òù›^µ_"§òtÇ­sgM)-–Jï·Èºç ËÎÍTÚø [ó¶‹­ùlƒŒè²}ï¸qƒšYe‡H›™®‡µ“¶nŽÞ“%'„bÒšã»ÇnzÇw»39C¤£»½Í#tv‡géiiY-¿õUç„ÝŠ÷ã÷:‡“‰q«y×Þ~Û=‹ªÏlÿ…錡‚•ób]°ÿ¬-Åï»ðœ_Úüþ¹’ÂZˆm—?‰_ùų¹„°,Ùc7Úá"[÷ölo·^mèÚp5hj(r¹*€ Ézñ$Ù¨]{Km !ò¼ÐËny®]æÅÅÔÐÒ(º×sµÖ­ZkA h¡ ýš‚0æÂÁ#·â‹åD&ˆ¾ñO ¯£‹\-L] aí3ýëœÃÛç_šžm·q? 0‡É %‚©Ë4 Lj€@@  Ð5¤47òêÁM‹ôõõõõ6wåo'ï&«`¿YöÍ5~›ä¥é—Wømy&üä¢×Gçú®¸”^ú.c©–ùú.=Ÿòîwiúå¾sLj”Þ\òrלeg“K«N¾$r× ßrÌX¼ítxž´V…ÅYðÓñ·b•(/I Z2o_”¨ ýòÃÿÚ}6¦Xu[OH3®®šµõqa SEš¿èøÛ‚˜c›Ž¿.i ‡¯¦ÏFÈ __Ù½zÎ÷¾¾¾¾3mþã^ºXiéä‚';–ìŠTö92AÔ¥]«fûúúúN_¸ùûJRË⯥LîâþÄÞýIÛ!¯×\(Q|/ÒŒìa¢Ng×1ûÅOã»öK|V¸réýͯZ»?qB\»'µ¤xmß§#.Hdå¾ÿàiÿµ] (®Yõø5¢SU—¯¾è© î 6ä€ÚÅ>Ù¡{~>g;hÔ\Ít¤yIáÁ§þØ'X±hˆC5ŸA3h7b‚ͧSÓ²ìè¬W8ÔŒC!ÒœO³‘?}ÁÿÜÒ”Fä…o_d2GYÕã,Q*½Õ÷k}ÝX„Èű7íÛsØj㜮µûb$i·Ž\×þj¦‹ŽêòV’•ËieŪYŠ¥™/c%–£Mõ­;þqûÐÙ][õŬñ³¡ñÛóû.f÷øqãb}Ijè‘-¿±kµ¨§a¹noYQÒ˯žß¾žïÚOI ™ÿäpÀ¥Âþ?nZꬕùèäÿþi¾v´³Ü%I§ã¾X#òšg{f»H"¯§/_¥Ûúÿ¼¾Õ!‡ÞuÙÙîp?Ü è¨rÁó“'_ZŒ]=«¯‰Â@9†ý'Ù2Ž>â—Z˜É3îÿuälhl®„Põí=¿ž:Ö“Wô例O=‡Ý=w7¡p܆L™6ÜMOšóôÌÁ7còäný½9ÿÝÕÿaeß÷¡† âÜ/¾jׯˆ.©œ I¹ GÛ®³=íè‰ÂYÞËÇ9¶>ƒ%×Fä4åQK’Ÿ%QlÇÛjBäÂÄÛ'] ‹/i™µôÍŸÖZ„Rþ“ÀÛþ}# z6ž_}7¶—¥V… ŽÂÐÖc³µ !„ÝqÐÛàóñ¹¥] J3&Í‹¸x4ðzD–˜Æqî3úÛ‘ËÅY÷lþ³À{î¬Áv,R9?ìeBéJÒîŸ<|:ômLÏγ‡a©ò\¥©7ŽÞ³ó“ S.É|záÄÙàˆ ‘¶]Ÿ/\ãnüoí¨üÿ[~ɶ¯UôÝ'‰ù˾߲ˆ¼pùÁ›\™Y¯Žo§O•D_:}ýÑÛ|)äÝç“'4x¸}ã‰(!õÿN½E³zä=»ðÇéÛ/Ò‹ ѶøìË©“ûZ3E)÷Ož¿“Sʲñáñ:×ÛšE˜&öZI¯²$îŠxTžÿ`ÛÒ “¹ë'8i"/ß³ä ™²az[’Ê"ä…lX÷¼×0öõ?ŸYÎXêxòÖ«l1a™u6er;írφ,ÿå¥c'®?O‹ŽƒÇM䢣TC½ß)Õ°óø™Þ®n\‘Ê™:ÚL¦ŒEûø+—æÇED§‰´Ù¥“¾D)cig vçjbÝmø ë+o¿ÌùÚÎŒV lÜR豺õo_3i„Â0ûÎνðoAn_C!D.{s&~è!þ«bÒÅl›¿ECJáˬ5«ÓÎFH¤:,ïo­7N×7¦É3þMûi]Æí$ÝToìB»¥CËuÙˆE§EÿÌ7ùc™‡nYÞÅúÅìJ’ÊDN\êvx$#ç^ú’õ7ßJ [{ðTë ß±õ3³¾•ÿÍô›ò\wyìö¬Q<\t/®Ï/¬ wfz»‘V[—pµd±ç’nã?ãË ÛpGšÉ“"{{!y‘?ÿq†ŒÐí¾´üí'Ý[3^N)%S#s×»íÂ(®\X¹äæöø5Ç ’Jˆ®gÎ&»)mh.ÔL]€ZP/Š´ñéjR>Ú¢r:ŸíÛÏŒ.Ïüû±'F_¯ü¿Ý»ý—ù°]x#"„È #.½´™´a÷î_ýÚ¦_ù3,[œvmç'ܯWmßå?»kî°l¥3ˆ«JðCªëØÉRò6kÅþ¶£Æ¶×ÿ8 bXx}3iÒøÏÝõ•6´œÆ®Z5Ö‰ù®K&!:‡aj¡WîRaTÎ=±ÞäþÌr$µãÂV7ý ¹ŠËJ„Ïu·]ïìØ7>ãç‹bInþbßä·}ìî<îð`¿±ìXÜ´“"aBæ´ÙÙÓÝþ{Úþò\æå¥oƒRÞÍ‘–+r !4昗6ôA;[ÿ>’IRù3ÌŒrzø´Ãý†ü½qó¯Jd„Èóò·^ ùíu]Ñ¡£¾Â˜ô#ÅÆÜmÿè/K«à„)»Šs_¤~»2ßv®ÛãGíÇ“¿oHĄȲr¯Êµ˜ß*üyǧ™é]Mþí%ó»}ÎßZ2Fh½ƒ(+ly‘i'ÚE?ñØÖ¡hóêœ)ìêFt æÈDyù¥º®úïÆTEч–o»Ÿ'#„¦ýèU?õm7eM+]G‹H éÚÚ4‰P±âfÒkXkm*Ñvéä ]Tœú 4Ó~Äœ®:¢ã9òóà§W•}E·Š?DúnLN„EåHLR¤p:Mâ™È>3ü;,FÐÝ>&ª€×ÑC%rAÔ¬¾? ieÄ Ä±ßØa¡+C_æE¨ZmÇŒèl¡G!z½¾õpé©°Ô‘Žöå§ÅÊ$áÛgù–ýªÝvüÿµ)J3&É|t'ÙrØÝ,u©D·ë˜i$BJ“Bˆ$ëÞ·#Í&odÇ¢"/TšŸöÔJB&…>,lõ¿1=lØÂî6rTØ£c•e*Mÿ÷jªËÿ< (âø+A/í'®ý²…‡nÝÌÿv³Ö*xùVdýõä!.úTy¡•n–2Ò˜;Ö Ç×V "ã+ÿRØ„n`a@'„h»O˜5òüßg·-ÚY¬gãáé3êóެ÷Á™´(»ˆÆ1×÷!tŽ%‡<çÉô*iøQì'L“"3w°Ð¡RèzÖÝ}z]Þð_\ásNm¦ÉÊ%™OÏ>~;׶߈¥SºXit3CŸÁ*g•Âúð¦$%ïDé;šcA¡Ó­ß/ò¦Bˆ¶TÇFë]iŒ.ø„âd"É)¸BL†ð²vþ^ì³PW§êlæ%—ÒMµyïZ:TS çKi„Áp1®õŒ6†!Ëêý¢k=[m}0.^Êé õî P­èôBˆ4ê|òö‹Eلʵ`ˆDĪB®”¶”ÒÕöЂÔÛ_w÷+5r79ÍjæB]@  „¶mwrèʃ¬ÖýLè„Â`ñØD^ø<&],'rÁó£ûBt'/[׉ˠ”¦œ_õKb³—U’#’;2)„ÈŠ³rJä&JZê5IaÖÞ•õoXhH*³õS!Ĭc[í;wîÊ“Î>æLBU›£«eÝgåro•"—䧦•òøG¤‚Ô÷A»\˜*Աѯøo¢eÀ56V4ð‡|q÷ÆÞ§©"waåŒQµ Y¢è¡œhQ‘彸œÛþó®„Ðô»Oý~ŒÍÑÊíG®u[þ…]i~ÌO* ¥Ëc¥d–¼Ë£ˆŸ.VŒläE /óy},´æ&°ì Þ•A”ôà¿|ã/Ì©™×Ò¨ÖýytBˆ,ÿmœÐl9“"&¿Êã¶·bÜÌg¶þqËœvÚ„"+ÎL-Ð63Þ>Yd:ÀœIˆ(.è—É}ç,øÖ^JD±¿/O¥:é?±\Þ‡”âäû¡Y]ìÙTBd‚·1ÅfƒË¯¥¦èºöq¿e“Ïë3ΔNˆ\¹ô‡‚Òì ¹óðY}ÇQ$ù ÷·ï?dºqa×w¥ééHÃÓÒvÚ4BHi~JÑ5Ò¡T¿½µ4çá¡m/}6þäi@!„ÈKKJ)L­ÚT¥Áÿ·ñ2}ðô\ô•ܪíbÔ•öæàUQÿo´Þ?M²ˆ#‰«þåuÅ!JBp Û”®ãj|锉5BDYÂØ"ºü\^ñC±@Ft¨„H%Á3ӽ͇Bçr·oµv.0-aÿ0·Ù®U…¬sziˆ_jÀ¦Bd™¯%rSŸ*%„P*öTŠ.“ñeÀe²ì|9ÓŒB!DNˆ$·$EHiòÂx»k.#M°JêÖè@- tøßHçä›Î=ˆMÏr’#nýî,ZJ£"•Èzlm:E&Lyx54S$-Uzv üKWƒØÓg§ K‹SÂÎ\JT~îHdZ}æ(‹¾!wûÌ’I! ‹Îî´W×#ÅvŸÙ°mëV^ÎüK'‚ãò%¥Å©oÜpô™@Nˆ\þç™'éÅ"AÂÝÆvïf^i;_i‰°¸¸¸¸¸¸H}çúk©¹‹1CYÆhf]ºñâÏž K)–3žœùýï—•BèZZTŠN«ã;\>’^Ze~Hå²uï¤ûòÏ“¡‰%…©OEJ*]&-Ê*bsYBKÝÜgŠe2aʽãûnæ8Ûè%¼.4nc¡E!%©|N+km !Dœþ2CÛÑÑ@ËÈÉ’ú6$,±°T"H¼÷ûúu‡ŸÈEi‘Ùl'K"—潎ÊÕuv³Ð¥Hòâîþ·ÀØÕD×ÐB;ûÁíH¾X"H¼ûÇÞ«|-{7ƒY^dXŽ}OgÝòQ EÛ©w+ñ½ÀÓñ–ý:qiÕ})øˆÀ-ÛN>J-–Q :!T:íC|Få´ém›uåDÈ[D’}íDp‘«—«Þ'þ«ÓMÚwâÄ_8÷8µH,äG] -qíí¬KŠ“ž?ü/¾°ÊÓoä.(Ž:{>ÉeäˆN†¢œ¬¬¬¬¬,~^Iù©FKçèE¬™¹/ïE²„Ÿ!¼}8~Æq©×}·IÑl×è†/ ’^ ”’þú‰Iµ‘{Êâ› „0¥)2¬xíµÆ½O?S”Y hr2«jGƒ¥ÚI¤äÄ%’wü#KL·#Æ$ï5’IuHÑFÃïVÕ³šIÒbS—(¯[ØäW‘zMgº!浪¨u¯~Êb“UãÙVP´ä±/B4u†sø˜ q¹Wª.[êYèS$àÒ ¿FdF¿ùiAœÑTu !¢='<0Ó<4p®,AØr'Ä+â~×gGå×\Øêw¬´»ñçí^ûsF8G½½sÿ¤I k½Á~°ÇÃ/¥’7t@nMz¨‡wÔ½–w/­¶æÅlð û¹Ž8÷³X,ß“å}ó+lÎe±Xéõ8ê)9ºÞó@ÑÀ âW§mÙ”\ñ­ÏgEãV$ÿø÷ÓUƒ¥‚à5æ¦ìö[Ãb±|ÃNæ4ðˆá7 º b¶þ!„ˆöì]¬Ox-év§Çn÷f±X,߉wk{à'ð…‡|À#©9øYÈ‘Ûö¢ðfZrx?x£•"ù‹(Óh©«–õ«Ê>ù žÔ cÃH*…"FÆc¤Åµ¬ýk鎳 Yü­•)ø«%œ.×q²Õ "B$„± ³*œ&L‘è-þ_=̮Ǿªeæ×¦Ç¤”è±vnœ„J.8“®â8†:TrwmñÓ’'wÓ‹:&Î{›viS2·—˾’ý‚dô¬ö°Ÿð¡¯~m'ÐIü¦¼óÉ—s*Úøˆ$­cîèîl®Dá5ä¥%]Ì©êÒµÍgË∂ˆ®gWN=u0Ò—aßëß‹]ÐúèRÂÙ;åíSÏÆZ¦0[ÚsÇ_ú–i":RèJŠß¿÷¾zŠœW^8±âÆ/ˆ¡3çûUËf©ˆá"âweí{baǸ}î±úºóò3i™%-~[ÏÏèéñó FîÁ³ßÝJ!3Lh¥YåìÉFt !„7ý;—­7[½¬|è“oÊ8pðÎk6ŠÞÎ^¹ÍS÷Qøûl¯ß³’~?-5=¿ºS(®bd»ÌuÑ&‰àÔÜíwPt4/Sz§ˆÓUÄ9üa9ðæÜ{ÎuZûn˜¯ÝçURüº{ùíãì•%1dlï0>ÿBNÝê 4:*Ÿ•5p%Øû1’Ä™òнe.ûYÊ×îc9wOÕ*αÒacªý2“ü#YÕNãTˆX#¶.À¨¢ÈÍTí.+y#áëìcq¸¥Ïþ¸Ø½ëft]M,$Ož&Ûü¸’M žš'c——¾Æ¿©¨R 3CGâí·ïëÅZn»ÙçeØxã\þû «¢>ìtÖò2³}wz›Jc}²í¤\–ýS“{xlÜ~_+aFÜá¬fœ]Ôÿ `€h„¨Óuðít‰ª\„Nc ›9NMC!LRM—É®yÕgwñ@ ¨js—¹¹-_ 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': 'pps', 'value_type': 'float', 'format': '%f' }, 'zk_packets_sent': { 'units': 'pps', 'value_type': 'double', 'format': '%f' }, 'zk_num_alive_connections': {'units': 'connections'}, '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'}, 'zk_last_proposal_size': {'units': 'bytes'}, 'zk_min_proposal_size': {'units': 'bytes'}, 'zk_max_proposal_size': {'units': 'bytes'} } 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'}) while True: for d in ds: print "%s=%s" % (d['name'], metric_handler(d['name'])) time.sleep(10) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/README.txt0100644 0000000 0000000 00000006371 15051152474 031566 0ustar00rootroot0000000 0000000 Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 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 } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/Screenshot-1.png0100644 0000000 0000000 00000600074 15051152474 033051 0ustar00rootroot0000000 0000000 ‰PNG  IHDRz×e•sRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ!Êi IDATxÚìwX××ÇÏ™í,° ,½ ‚XADDcï½kŒ-Öt£i¦jÔ$Æ$¦S£±w{îØA&–eÙ¾SîûÇÀŠˆ%ySL~÷óøø°³sÛÙÝ™ùÞsï9„;Á#@ ÿ(b@ @ r‹@ @ ˆÜ"@ "·@ @ä@ @ ¹E @ Dn@ È-@ @øÀ<äy¡º/ÅL¸µÇPcwœª—3÷öŒ×)_Sóß5r±‡®îÕ ! E‰§ÕK}Fùî¶ç?BFFùåþ̘1cïÞ½Ä@ þa¹%>å×$.­Rç ª+5Ä·á­BQ” 5µ @þ1ñp·n¹[ÌXhðÌ„µÖ?%À*ÊÊȇ@ @øäV­/«FtQ¥R©•J%Çqƒçy„ðÝ^, (J©T€ 6›M,îúߥµð£a q2™Lãá©V«)бÙlƒÁd6ªßÇúŒÿŽîÕ•Xˆ³‹@ @ üq¹…BDDD „”JettL£FŒFã‘#‡NŸ>k±˜êK)Œ¥RiLLLRRÃ0&“É`0dggççç›L¦GP*ˆ=W(Ý»wïÞµ›F£¨ªª:uêÔ¡C‡ô†Šß[a½ÅŠuíöpá¢Eä'Dø×±páÂêª*bÂ?=gçÇo/Z¹ëTvµªÀøÏx÷q±ÒžjÓÁQ=.¿–võ)16@ <²r˵VP¡PôíÛäÈ‘†a/­Vk±Xüü¼ 222\›µ\eýüüF=zä(…Ba³Ù,Ë… Ö®]›|ü˜Ýn¿³™ººçö|×9¨Î©ºØÀ‡††Ž9r@¿2™ clµZu:]ZZZee4è¾B÷è3Œîv‰ýnÄ¥˜®‘J¥Ò§Ÿ~ÚÓÓ–.]ª×ëÿqÕúÆìÙäWD ¿¾të“ ÃÖ›cN~}r¨š-¹¼{Õâ‰ñ‡ó/»•âO¹½{Åöèé©"±á‘–[PëöQ©TÑÑÑñññ*•Š¢€¢(Š©”‰‰‰Îɹɲ¬ €kwx{{'&&K$ñ````UUUúŒüüüGmœØgŸèèhO­'‰ÄÓÓS"‘üS½ê,ã|ê©§ÒÓÓ;tèpùòå©S§~ñÅõ…+@ }¸‚Íï­×G¾}!eAœR<ôö+GûàýÏn¤Eÿÿ&±onÝG,M ÿ8Ôµ–ø?ÆX"‘H¥Rš¦išF#„…CÂ{õîÑ6±›ZIQàÒZ —Ë=<Þ¾2…R"‘¦œNgµ©Ê`0äçæ]¸páÔ©S¹¹¹¢rÛ²e 4ä:û§ÁÖì=ß}õóÞóf HØº×øOõPþñýÞBåž§‡|öôºåOü±u-|ñ¦É£¿¼éûøò5ÏFÝóÉÁ‘½iÞß/´ƒGÒœU {xýuBˆ-üõÃ×>½ÔfÉšYM¬ÿßÑ„5kçh¹Tкÿ”Öý§¼ù[zdÁ€ž ž™9¶ßÖþ¶²JŠ–$¹/¹£OŽžƒ…§‚®9è?e¸ïòk¯¿×\¶yõ ¯Áß'ºßy}¶*íàë]#-÷iˆÜ"„¿En¹žéU*•+î…Kuˆº âZ5pàÀÌÌÌãÇó¼àt:Y–E ø¶‡‡¦i×z9±OOÏ„ÄÄAƒµi›¤u÷ i$€ fU¦(ŠçY»Ý^PPдY“U?­¾~ý:Ç u{¢V«GŒ1zôèV­Zyxx0 #6×¹sçöíÛïß¿ß××wĈbT¼¼<©TúÓO? `&>>~„ ±±±âA‹Å£×ëÏž=[ÏC¥P(ºuë6yòäf-š;9ÁétÊd2Á`pØí‚ ˜ÍÕW®\Ù½{÷ñãÇõz½XƒhC†aZ¶lÙ§O¯aÇDFF* Š¢ÑÑX@ÀñNA6{×®]÷íÛ·fÍš´´4žçëI¬GDqasÊâ ÷VÓþ­:µóâ /ž<¿õÃçMšuo'ýá2HêÛºS;/ßÅ_êb‹o>^h÷ê:iL|ts÷¿¸­ãÉ·ª6ßèÂ#‰õäS:üÒá·[›ûzÖ“øv™õfÇ÷‡]=[è¨USöܦÕcëÎÇ ™oSùÝ9IT±“F|·qÝåÇU?ßÐ û±[½«©ÌÃS %%&¾ö¦ÏWœ?x 7m¢¹w+@ø{ä– »Ýn±XA@ˆÆøv¾ÚÇ} I¥Ò„„„äæßÊÍÍeUÍj:AÜîÄ»‚gˆ¢E&—wéÒeêôéñ mÔj5Ã0RD  –ç8޳XL èT*wwitt´B¡°˜m?þøã­[….ýƒjݺõã?Þ¡C—NÔ62™Ì/À¿‹Æ34<Ìn·Êår12bhhh×®]>j4yž—Ëå£""7R¹)0¥P(bcc###/\¸ JÇ«1Lx£ÈÞ}ú%µLå®FøZwH©v“P”¸%-*ªIxx¸J¥Ú³gOee¥¸¼a˜-Z<ýÔ´Þ½{ûûûÒ4`ŒY‡“ã8F*cFÊH@.•µhÑB­VWTT———?jBK„+O½V ùÜ—Ÿð§A(?¼pÎÏ7«3JÙ$™`¸°ú£ÏÖ˳P»éåÍ䫟÷}iËþm*v/¢Ý$¬™õ·|õÓ‘R`s–O˜¸¼¨Ù«ß¸|:-,ö9€ÂÖ›¿}µxùžÔr'¨‚“FΚ=±­Ý]ó½é 廦ÿèFÌøñ~Ç7Î1Kü'¼=w´âçñSÖ–€þðН*æöî_vrÙgßm>‘c™®Eω/>70JÅç×ímÀðWã}¼}lT;ý×*Á;îñé}оSJ8MËQo̶–¶›;?ûàÇý@œ4úµW;_™ùò1€cëô^ç§}9ºÎè€-;ùÓ]íBƒ}ßÒ(4á_Ž"º_;Õ÷ÛÞùê\‡·Ükî,BÅÉm©‚´U ? ¸·ì× fŸ+ X<<\Àå­š2y½nöO-ÃdwU§lõäãÁK×þ)•é7ü§Öª»ôXÓ¾Íá·í¿åÌmÙD lΪɽ_Óüœóݽ[q#Ÿ@ ü9%§VΞ·§(ÿž5ß áúÆ‹OÌ]0µ¤äÌ‹w•j:=9&N ¾}§Î”ùõó¯¯~föæîèíà¡20?Âõxåý§ÛÈ*.þòÁ'ç#&ÏŸÝ_g¸²îã57Àf¯yë“=Ê®S^|~l"}ëÔòÅ»­­G I“¡OKÐܶ\¹O»õû\È‘Ëðoyõ]¼¸—òü;mCãMíÝ÷æ¿ùÜð¶ÑVécß\4PG$âÉ'êN?×eäÜ•»öïY÷ÑÄžO®:/mÓ\Óà][Þlü¡yë~É =)Vy÷Äjè˜÷'ù_{£÷ïÿòÛžÍ_<5øµ+Sç jü»Z!ÂáÁ‘ €À²lyy©Ù\-“I¢\J ººZ.WºüEQMš45jTAAT*E < @bÚ* Æ5Z„0ÆÇ9l6¥R‰0¦hZàÙ¼œÜ_Ý}æÌ™¼[ùz½ž„èèèàà`Ñ9Ö´ió.]º=›’››+Š7___q aÝ®Ûíö¬ì›W¯^u8qqqQQQb—8g(„1¶ÙlÙÙÙ………âp8Ž«¬¬´9¬¤°€…À@ÝÞ€äïï?xèÎ;·j"z¥A(((¸rþÊ•+]»v —K¤4‚2™¬]»v%%%ׯ_¿víš(2ccc}}}B Øb1ïܹsÍêµz½ÞÍÍ=!!¡k×®Mš4‘H$ÅÅÅEEE§N2™Lwï×zTvpÉ£¦~ýßÊŸ·<½ãÅý—òë:sÑ[C‚JnK ꙹ³†úA?ïô¡ Îl9[ùº¨\¿ýÖÌfrKã.êÓûìHŸFïÜWn=Gµõ¤Ž¹ôHõÅMGÍ 8wþ -äü „¶¬~‡ßº»f}þ>÷yNðôâ¤ÞÑR+³uJ²>×èöD¯^-V­½h è4|hËó¯Î/·nï5·;â‡N½äÊšÍY}‡ÞÑ[GÆWÑžú˜ÉùmÊU·.ÏNîÓš =·ì×͆[$“Wnë[AiÜÌ9—*è3ÕÆÜxx—°%;o©búïÓĺçöèίÚÖP»žh¨Ï‚Éf/áߎ4â©×B?Ÿóáò]+?ÞÉ(ã{¿öó»o=ÑR @y÷ûîÔ¶ o-™4p>€:¢û ?oÿ`T}kp̸ñ‹°c&´h(¤ åÕçÛÓÛž{ý“±ý«@Ù¨×K›w-èæ‰~W+@øóå–Ë‘e6›/_¾œuó†F“èJRl6›ÏŸ?ïîîÙ¼ys¥R.>úK$’V­Z :4==]LÔ51új"õá: ƒËÊÊvíÚ…1nÔ¨‘Z­vwSó,·ÿþµk×çææ:X'ÆX©R…‡‡ó¼0!2™L«ÕŠÞ1…»»»¯¯¯ÖO<èp8.^¾´jåOÉÇ ‚ЩS§)S¦´lÙR*• PEEŹsç\+ô{Þ­\½^ïëëOS´PÑåûŽ5*>>ž‘ÊÅÈãìììõk×íÚµ+77×ÇÇ'''ç‰'žˆ‰‰‘1 aš¦e2Y\\\‹-233‡T*U©TbjLqg±XŒÕ†òòòÂÂâììì“'OFDD0 STT¤×ëËËËM&“«'uwÐ= _#ÁQ­7©ÛLzÐ d¯È¾rjç·Ÿo=üÙÂØvŸEUÀÏFuú¬öìòì Vàá#nÆV5ÑÛgߦ#ÛÎ%Q‡ à5tDsX]ÕóæÒ*À#ГZÛ£€ýò®jÖ³à#»wGÝ|Ü( $ €À u­Ço•²¡-ÂTh¯èÆY™[YãNrõVüÔTZ%ˆ‘1à¦SÓÀHÃØžwàÇO–ï¹ZÆ‚›Fvßs÷l—}pŸ ¿YHŸÙ+û̾÷ûaƒßÛ1ø½»Ž«{m½;±¬ùü,<ÿŽÓºo0Õ9M2轃Þ{èVð÷È-ŒkY–½|ùòáCGCBBƘã©×®è+ jµ*::Ú•PËK£íÖ¥k €‡ÚaÀ‚@Q €Œ°¨( Qc§Ýq<ùØô FãîîééîéééEE%J¥›§L&—Ë}|u‰‰‰áQ ( 0fY¾ºÊd·Ûë$)F ãz°ÞP±k׎mÛ¶•––…ŒF£‡ÆË×?ÐßßhDñ[m6X­VÀ¤ÍnŽ¢€ 8íÖẻ»ÇÇÇ7‰n*Wº‰:«Ùtôð‘Më7dfe‚Pm¨Ú¾e«ŸŸ.0Ð_«Õ „1M£ßæÍböïW;NžÇv»Ç¤|Ô×××ÛÛÛËËK<Ž õÖzÉårq£TÍŠ\ukfY¶°°°¤¤„¢1‰±Z­îСCl\\pp°L&Óxi›7oîÚ‘e6WgegêõzWe¨Ý†,ˆ«ûNŸ>]ã¹pYYÙùsgòóûûùé(Š@ŠÇ€0 @bÎbžçÅ ð¸õ«´´´°°PŒ“áææÖ(2B©vcLQ4pUeå¥óòòòÄ`Œ RΞíÛ·¯——ÆR©T‘ûøøèõúªªªœœ<£ÑèååEQ ƈ¦$Í›7oÔ¨QµÉTRR–››k³9œNgnvÎÁƒoܸa0îÎ`öÈ@ù<6¦ã7sxw̤] aJÓͳò´º†*ÝD-ÿöÆ/˶3ÜÒÖ/?\â=téªáµš¥¶ Ipï‘M~\œ^ Ðxxï @o rÑÙíâáƒs_‘ôkb;³õh—0q怨5õk^;ËSòGÜãÇöóNÙqè­çømÕywe‚$nÂðHŸR¯· ,œÓa*8wtÍ)3€»“J¢”Tžüémÿ¾Þn²ÈŠ@ á¿,·\[ƒBr¹ÜÃÃC&U  ±€(ŠÆâX!;;{ûöí5êÚµ«L&£( !ЦiµZÜI…ÍPÀ‰±Ý1€P#0¦)Ú?0 ""c\^^®R©Ú¶m;hЦM›zzzbŠ¢¤R)CS0ÙíÖŠŠ –ePcÌHiªÖõ `„m±Ø £  ð<_PPPxë'“ÉÄH‰ #­”(`  ªªª¤¸ÔÉ 4 `Œ±Á`ÐëõÏB´„Q(bÀEªvC—¹ÚX\TàtØP­x¾¢¬Üj6SMׄe¤(???oooš¦õúòCö·jѼC‡J7PaL»©ÜÕn~~M›6EˆÆ¼PYYѶm›ƒ;v,##Ãjµa  ‹òê2ç›7¿_²zÏ…”£9MDûaŸŸ§BH5jÑ"Ë¢¯6oXzïæƒ^ŸýLsEÙåúUкΣÛ|9/EˆÙÕ¿þ¾Êó±7¾|ÕmñŠÝ{6¤‚*¬Ã䙯õ Ò Ôüÿr‹ŸõõBíWË·ß²ž—èZ zmæ3>ÿwÿ¬¾<íâ‚;Þ¶/0±W—ê×›7Ò+èC†6;¹êÚÉ]G“†>°]('(@ „ÿ´Ür!“É:wîüøã7mÚT\4Èó¼Ífcñ¥¤¤lÙ²ÅÏϯU«Vâ[EÕz±(šFQ€1€`6›EG(·2yëÖ­ûжm[©\VZZª”+ÂÃÂd2Çó6›Ùb¶¹©•ZO BQXÀX¡Pøûû*ÝT6‡ #‘xzzÖ T(:©î%FSHh!ŠBÀ ¼LªpW{Ò4Íòœ¸T¯´´<''Çn·Ë%R@ˆ¢(­VëííÍH$Ç9Ž’’›Í&•J¢Â ,ËŠQÅ]d‚€1æY–åYNô›‰Ý³‡IdR1áÙ³gW¬Xáp8b[ÇI¥R‡ÃAÓH­V+•JZÂÈår„) ôöönÑ¢Åc=¶~ýú}ûö™L–Gò‹„á}^ü´Ï‹ }Ç|’¦}’4íŽc!ãÖ$«'©´Ý?=ܽîë>ß'÷qÕ¯Œ8û›wîr ¨¹®‚óñSòˆš>–%¨}Gûæ¯ÇßÿŽ˜¾>yzí¿Sßë0µ^Ïîì­,úù-ÉÏ×¼P=¶$9¹öÆÏmL~®æï–?Þ8ÑU敚ŽMýf·«öäÁpßvïÝg@ ÂEn‰:D«ÕöìÙ»k×înnîƒ%‚ç±Õj0MûöðôÔ*•ÊÈÈH‰D& *A( 05üWUôúržçD=eÊ”^}zët:DSÇñ,wóæÍ={~cYÖj· ÆÄÄ„®»èt:„0öðT7kãëãmÐW`@ac^Dçåé®ñÖú…1Æ `†a‚(ŠÂ<‡)€rw÷ U*•bL,€¨(qƒ¢°¯¿_@P 8Þªªªëׯ———»¹¹Ñ4ˆáï)ŠR(da€PÀ¨J‰„FaA@5ùÇøÊÊªÊ ½¨B«ª¿íÙ_p+)©—ôX‡ IDATƒ\.Õëõr¹<**ª]»vÑÑÑ2™ !AlT.—‡„„xyyÙíöôôôëׯ‹QC@ ¿^nÆØËËËßß_©T€¸ƒ!ärR!„Š‹‹·nݪÓyOž<ÙÛ['¬]ª(Šâ8§T*U*•Æ@Ót@@@\\œŸŸMÓ<¤RiQYùÆwìØa6›yÌÙl6‡Ã‘šz…g¹û¹»«1`š¦}||4 Bcà8îÖ­[iiijµ‡k> >>þÊ•Keå…:vìØ(,œ¡ÄœÂ¦(¥Rîãã#•ËÀE XP©Tîîî øÂ~ÈårF#“ÉœN§ÝnOKK»•—FQ ‚F£ ’Ëåv›]t§Éä’ à§'BXÌ r8ÅÅÅz½!$³Õr6å\zF&Ã0«‰a?ï©S§ºuëªÓé$Z4¸J¥ŠŠŠ NOODb¿áï Qˆ @ þ]<8ͱøh1¦iÚúÜÝ{]„[·níÝàêµ4§ÓIQ”DBSP  DõB1O­—··Ž¦%°„¡MIå21À(piiù©SgRSÓ²³³ósó+ÊÊvkNvVÖÍV«UÔÇVUªªª\²#''oëÖíçÏ_t:9„E!·Ï!CÆŽÛ¥Sçnݺ7nà€>ÞÞ„ ‚KÆ`hÞ,¦¹R®  f˜4¢¤Œó p|EE…Ùlfjcá#„¼½½Û·o‰hÀ€‚àР¸øX_1BMüŒJƒñƬêêjAÄxöÞ:]hxxXD£¨˜&ááႠܼysÛ¶m .œ7oÞÊ•«òòò\Ÿ‚ r¹’a¤Ñ$D@ ü)8ÓæG¡ñž|ÜÎô÷› „PÈ+ì5%øÒSCBQ-_?f¸wfuÇÕ·#B(hfŠMyòdddTãÆÑÁÁA¢lÕ¢¥Wqq1Æ888ØWç-‘Jc€1F( U+·@¡µnݺnD{QmŠÙ·jn ¼`µZyží€ EçÎKKK÷îÛ]^^®Õj;vìØ¹s'•JéµÓé,..NÏȰZ­!©\ß­[·˜fMÝÜÜÕjõ­¼Ü½»w9t¸¼¼<+++??!ºS§N¡¡¡â¢D„Åb±Ûí.L „¿ŒCaSʆýx B§nßÿ~Gͽ§,eaÝÚz¼Ÿm,<~¾‚oLƒ3ÿà¡ñ½ŒßR*_ò¥À–y0•€¦ý[¹ß»/|Ѫ~ÍŸLæc>ü+/þlþº)Çüœ[{ÝL^ùjÏ­¿MÙþT¤ô¯4öß3@@xÄå–Õj5™L·ï9 ‚ ×ë+**xžwù»ÊKË8˜ߦOŸ>b슚Èï”+A®U2 Š"Œjóa iIppp¿~ýd2™Éd HHˆì±ÇBBB<<<$ ð…1ÅÓR$“Kä#ŒÄÅH¯7œ9{v`~~`p€x«”Ê%aaáÁÁ¡EQœ“œ³_˜ü¸YßMž ï®ø]+lÉ¡¯æ}}¬Œ˜Ð~¯¼;5A–k¿Ì[°%ˆ¼Ú?3ÿ¥þõzÌWY0kŸoßo§bo.Ÿ2ye®+·rÄ k— ¬™0+=ôÅœ¯NTã•0iÎË}ƒê÷“+ÜúÒÓ{z¯ø¦¿7%TžüfÎâýER63ç‘QJtûãû䪢ÛÅ$±ïlú4bó¸».é!Ï®gÛ>ò=ÏŽùäzÝtÐòömý¨ê÷¤Ž,¹äîDÚdöyà \íMÈ|îÝ®½—Ü Û¾<3^UÿÇœóÓø^sR8ïá?Y:З¾oݪ¦ýš£u'púþë–§‚Ý…ò“»2j¯O—w]³<á«æJΞ*€àníîWfÍÕü_l 6û‡'Æÿœ {fé'Ïv a3w-~aÖš,㞟Ý8h÷Xú¯küï @ ücPu%Ęçy§ÓéÊm%>î †ŠŠŠºù AHKKÛ¶m[ff¦èªW!ºJ¹GÊeN޳:쀀h½¼ÆŒ3gΜ ¼þúëãÆMhÙ²¥‡‡‡„a@@ˆÁŽ1‹|4>ZO C‰ÑÚkšN¿~=ùØ‘ŠŠ ¨Ý`†1Ð4Åq\iqÉ…””ÂÂBž­ÝoH.“øøøÔ<°ò|ii©Ùl¥£8ØÚeƒ5á×+**¶lÙòý÷ß_½zÕn·»rˆùûû'%uèÓ§Oûö탂‚$©¸äOÔZ§NÚ¹cÇ­‚<¨ Q¨”ÉU*•L&“Ð (77·Ö­[?õÌÓóçÏÿàƒæÏŸ?eÊq‡˜ ,Ëæåå%''ߺuëQÍtüûg3«³.ç˜ñ£Z?W¸íý†Ÿíüž®ç¤»ë©>³di^¿%ë7o\2¸è‡/OU׳{kÛÇ+®ÙÄ£ŽÌ_¾<3íæ­Ë§¡õŸí+»c%•4pIJýÉǓſê©A äï¶m¾×ïON>ðU?ßȧ×J>ž¼ï¶ÖzXƒÔ³Ü×h‚x”©=5"îì‰w&.¹ ƒWnz¥Åi%xýþWzMÚnPvûüȪ aœ?¡´ñ½"Àr19Ï`¼¸õ2ø&5¦Œ§öÝ´˜ÓößUBÏH9[´gÞЖ:)B!¹_ܨ”óÀå-M{þÀõ×£¥Ê¡ûL¼þÔ—“Ú‡(BHê7òƒ%ܽ­<±xtK BHÚí¥-y\hOývÑ À}ØÚ=_MíÖ<"2®ÏÌ¿}šä–Ð3Êaâj硲w,x"1X…¢<£z<÷Ãùªšß•õätBH>pOµøúô3~!ºÇN#¸V9†ÎØ´úÕÞÕ!Yp§ë³Ðð á?ÄCE&eƒÕju8âR@1"Ÿ···¯¯oZZÇq®“Ífsrrr‹-´Z­¿¿M\ \³ßH¬cŒñ<ÎÎÍÏÈÌ  £%RQŠ¢u¾:oooŒ1BØn·ggåèõú¦Mšxºk8DZ¬Óbc£’«jeFX06 ¿íúUë©éÙ³»¿ MÓ‚  †«—¯œ={¶²¼räÈ‘¾º@JBSFÆ0R ÛíöK—®\ºtÅÇÇW*•ŠÃq8N§SÌ},*ÏìììuëÖéõú!C†$%%z{{Ó´@2‰ç½¼¼Üb±¨T*‡ƒ=sêôÚõëΞ=Í:œ€±PV^räè¡Æ7oÕR­TÓ Â€e2Yhhhpp0æq›Çq‹Ýf³¥§§ïÝ»799Ùf³Õ•»2Øž³í£÷I­rXíîmŸû辺ÊCŸÎùá\/H†¼6Ícͦ7s> ÿü­.^÷Uý–3oÿ– óà±£JhõÔ»ÓÛjøÂ=ŸÌýîœQ®‹ÔU ÍïnîµV)Kkë½UÖŠ_(dAÜsÆÛSÚjï.^uàÙÇ· [·´§gmö6{µ]âí¯•R@©"½ö†¶X̧^›°sÄÏ´UñEžy]ÿê² •ó&­d™ÒrC¥Ðì©^ë®»Ç °ùÔkS¶,f‚Îz¡y ÒLçØa´—]=Ë'¾¥B´ªý€àïw¦[{´w«Dù»7Ýh6ã5ë§GîJù›ˆxáû87T;À/bÎÝ]Ï»kÚPr ¶›ŒÆMA£ºÖpæn^|¬Ù”î¥{ÄçG©›ëí,XGÉeÔùn4hÛû›Ê åwÚ+;wo£õpÞ\6öñŸJ üù-+ž¾ãæˆ-W>òô’,|ö×Í3š)¦Jihç$ drŽ\6Ù;ÎØü‡½úâ¥aÏŸÊ=pªŒoR~øŠš÷oîÎë‹ô}÷"£kÔd—^ÚøÆ@®Eþ†X  Ù¥H5¾ü”õÂüníç_©Ö›©,½´é­žiö‹)óck›>1}à E@˜Ÿ¤ª$ÿðg£'4Ë>4%ø>î)®(yo>¨zLï¡sý®%g,žUçš’½lh«é{ÍµÓ ™¿žÞæèÍã'vðx¸‰Àü%#ÇkãôéEmßwãØ’1OD·=þ´ô®’¯#@ø_’[®Àf³ùĉ­ZµŠW*•c§ÓYVVfµZÅ\cœŸŸ¿nÝ:HJJÒjµµ+ôÁXuâĉ¬¬,§Ó ,Ï¥§§oÞ¼Y*•ÆÅÅiµZ‰DBâluuuyY™ÅbÊÉÉ9v4™eÙÁ‡…„³V‡Ãn·šÌ'NŸÌÏψÍR€Ç… ªªªÒÓÓ[´háááa·Ûoܸ‘|ähfffhPh÷îÝE…(AÀLí¦,±ÿYYY»wïÖét2™Ìjµ^½z5==Ý¥'ÅmÛ¶-77·´ttBB‚L¦ÀÓ4òððP©Ô7nÜØ±cGff†¿ ÇqçÏ¥\»vÍdçë0B`µZ÷ïßo·9ûöíÓL£ñ ò—ËåE±,‹08Î’’’´´´âââ‹/ž?¾¼¼&ë­Ë¥ö(«-Kê¦ÒI߯렱_ýtÊ'{ :w=²êZ»~™jMùaé<ô¹ÛÊØw^€Ö'j-Õþ¾š++Úð«ߧþ0¥xñ÷åC¾^ßß«hÃóÓrh®¸Çøšú;3§ç.Éï÷ù/=t¸pëË/}qö‡çõŠrï0y+7÷Û ÒF£ft~óÝáC½[´IhÛ¾kÏN­½sf}3–i_õnþå”vtšøðë)ïY–R…·ï€­Êk1¡©ÌxÚâæÁH<½™ªr«n5–“„?>÷ °œ¾pçG`<»l«dÔWqj5䳋Ée¶KŸ=ÿæ¶Ÿ'>oª¤¨µ†ãæºÏNµ™¹ bË¡š¦BNj:íÅ!•œU7vigï;><¶pÓÔž›j^(Z¾þó—½:8 !ÛÞßKðP©oÎXto£wü\Ìgçyú€€iûáöºÕß’Uðí¤95ØwÓÖ%þ¡ô–2¦o+jÕ!mÿª¸C‡ËÜÚ~¬{P8•žºë’a@Ù±[Ñ+Á›âEö¨.qTð›;WŒð‡[?t™~Æ|ñX×ÜÖãÆ„°ç/AÌüÓ—gGÑeëû~x@ÖéË‹{ž¡n~Ó³é³Ç²·®¹úzl‹Ú¦ã>¸tüõV²‚»6šzŒ;½åªeJð}v‡9Ëo”è¢ýd÷4RåÞ×^ÞkœðËÑo³žúpH÷·_[4ý«‰ߊy8K+ûý’ºcŒ?*[ß'øñýÎ »®YžPo€dÑ/@øß’[ ®gs:'OžÄ·k×N¥R1 c6›Ï;—––Vo7‘è »råJeeåîÝ»½¼¼‚ƒƒƒƒƒiš.,,<|øp^^^Mt [LÆ{v—'¶OjÚ´©ŸŸBaaaZZZVV–Åb)...*(–ÐLfæMOµ»Àa ƒÍj½U˜«¤€Å…(Di4µÊÝ`0\¿žQ\\êáá¡T*YÖa0L&Ïr>^^NÖ.`c Ep¬ jE ¸ªªê×_­¨¨hÒ¤‰ÆK[YY™röÜ… X–E…DOˆÛ±JJŠÂÂÂFJQ”èžòõõ¿zõòáÇËÊÊ”J¥ ‹Åµõ `„¡²B¿g÷¯×R¯„„„ùûû·hѬqãÆ4Mëõz«Ýb³Ù23²Î;W^^nµZ«««Å•™ÿžïrk3ó=õécWäe§ž®°utÒ>qqÎ×f>Ù¥S§n“G6–•ž½»ÔõÚa\»Ø•öjÝ%Z…tm¤¿žÏËÌÍóïÛÁO‚Pp×÷–6Іš…@ŽÜ—ò/^~Ü2àm6ï좛õŠP Ÿ€;WQšvÏ}³}B~ê¹³gNm~ã§Ã—|1¼¡‘*ÂÚ5V!¼BÝÙÖßõÝ¿¬`I_÷æÛ'ãßú¬³]ís{äAu '6¤E=þªŸ8ŸP3@;4\"vÖ»¦œÿâ©y_¶ýùÍ8¥xrÆ/ŸOzå“péÚJ+|üEá°å»žˆpœûüù÷¾í°â¥æ·#ßHG,[9£I§5ÖÜÀA€l;1RöÀ!ý!ƒü£þGáËv=?øýTð²rÓËõVºÐµOâNžª¼±pÊçcϼÙLö?qM\ïh8rÝpîØ…Ã¿fPqC[y…zvó{+½$å·ó+ÓÀ³]×pÐ>Ýß]×ý]ÁZœvlÓ¶£{Öfتlw]T¬×wu0gŽQ €È©»JÆ+µn ÔÄ@„ØÉcš)€wl?8Và¬2:ð}ƒÚ œâmùžK',—Vï7P?\4:B@ÑþåÏŸù¶ÍgiöÌŽñ{([7ÞÙ—udsì/`««$:@ rË•b«ªªjÿþý§OŸ¦iZW‹ÅjµŠz j·K‰E8ŽËÍÍÍÏϧ(J.—+•J1@ŸÉdƒÖÄÚd4T8qâêÕ«^^^Z­PRRb4-KM˜ !„*NTPEB‚ óÀ#„0`ˆˆˆ6lXd£ˆŒÌ¬'N¤§§WTTpœÓápÔŒ“¢ýýütˆAï¤  `µZkö¤!„ÄpöÅÅÅjµš–0‡Ãb2sW» Æb–gŒ±è:ËÊÊrmS( …Ên¯ +ât:Åíj® ‡®»X<++ëæÍšFzkµZ™LVQQÁòNžç­f›h:‹5žÆCdB¡üÀœiË ÷ðÎq}cdKn±/®ÚÐÿܱcÇvÏ›ôÛÄïfÝ%ìåR{±µv¿4o.·JÔ”øPc6ÞɃ!¨ ¯M!¾üÀ¼;›»m",H"&|þÃè@°ÃPaS*o~P¯xC_‡—®²±Hl·Ønƒ{®˜úæo9j|©€9gm²iЦQólaòž¼è¾í}A1ˆ‚‡/ ‚áì×//Êè8÷ó‰-Õp*kI5~4°Uœ‡êþ^ÁxyoAÔ¨Øz«ô˜»êQX3O\¤b;D¨(÷fÝZR_dø8% ÎüC®_*ÖçGÌ9œüö‰³à›©éyÞ½fF*)JÛ?‘úèJ9×<˜ù}߆mûx=Eö'„ù½F#üoRwáæåcîñ…ö²üÔ/}/Œ‰¹Ýtyî´ïG}!òÁ^IpÇöÞp½âÆæ•?¥@Ì D/J¡Ø^õõ–²}«Öð&ªUÿf*ÀÕ–>ó䛿\WBˆ[ÃÊBÅW—˜@¡u¯Ù>&q×ÖïŠÚÏ@ŒŒ©‘Q÷ï¨W#o€2(K/v@¬k…-8°õ²W§±~2ÎÊâjðŒU×zÖýšú€áV~®›[m?¹»§ŸJ+^Ô]#‘v ŸçÝ=Q¢8¢išã8»Ýn±XŒF#Ïó2™Œ¦iš¦år¹›››¿¿@@€¿¿¿···ès:UUUz½Þd2qWoå!ðWe0dß¼y>%åü¹”[yùÕUFžåŽÇ¼ `Ì À Ç ¬ƒw²˜ãDéƒ)…Lž0~üØÑcF½ðÂs³g¿:cæ S¦M0h`hh¨R©T(daá!=Ö><<”a(1‹±¸9*??¿¤¤@¨q[‰ë$ CYIiu•‘ç9p­ß0` PŸçyÑûd2™ÊËKkC8ÖÄ` X …‹‰¸\ÖxžeY¶¨¤85-õâÅ‹yyyÅ…%e%åf³YlÔõԵأÿSxö†{ÿg¦ŽìÓV[p±ÈÎcGú×S^Ü+M8饙}Ê󌜭3Ú»EkæôÊ׿ª3~[sšŽóa€×Ÿß{Ù(`KÆÞã\lBXãÇÂJv+f-9¹7ËÖPs€(±~iHbxÙÎ]錭iËg<õùe>´~qÁV^T^g™V{9O~÷ù¦KevAp”_=r¶Zá%•»ã²›z; Ï»uÿ½ç\é‘•ßí͵;Ë®]3z{J~GYpæ®{㣜~Ÿ|üdKñ¹†ÑÅ' SûÒ-_vrg^h·HÅjÈ?[¨ë¥ª}T«`õ8omûä“_óX¨NÝ 5ñ¢Å“™¨gÖDõn­AÖ3o xá—«¦Ño=“c¬Ü5\C£ú^)Zí«Sa±YôH™.¯þjÙ¦ƒ—K¸ÎôÜﲃ$¨KW°þñp¹ëJhNùxÒè­ýC§3H½Ý êfž©æ gIZ €6TÃÔÎq6§P3cVanà™£Á.ây&ÿiÊ»%‘H5j¬ÓéX–-++«ªªÚ‹Wv¥Réïï¯Õj]ñ߇^¯/--uyil6[IIÉ­[·ìv{]_˜MKT5áz2ÏÕj‘P«dÄë4Ƙa™L¦P(T*•—¶CÇö‡£¸¸øÔ©3™70ÆM›6éÔ±ƒŸŽ¦iŒyÀˆã¸œœœää䢢"—†Ûýu®¦ÅŽÜ6¸bÙcQT¡Z䔸fÍ(0®ç½¨·öÒåõªÛ¨k€¢kñ^ÅÙËÆv_&vÐoèÒ†õzgæøÃÞ¿fM"¨b=D {"ôY£6KJÓî™ù1þÅ­ð¬gžsÿñë±5±Ú¥Ÿœ7ñãE¯üªZ@î‘=&¿?¥‰ €$‘”¬ž1v±•i4ä­·›(Ô_}îʼcÖ0šð@]¿9Ò¼¦þ%¯½ž6ÞØÐ}^›ßÆ]Ë×+¸úÄœÉw„ÊP´xvÑÔ/?ž7f©žE`â°WÞîíãÆíõú´¡›µ‰qÁòû?e5rBðkO÷XÆ{·™ôAG­Š~ø²¶k«~N-³¤Nìõ9€¼õœõ_tûzïy Æ·{Âô÷»û<À¹e),€àþµsÐuQ¯FÒé•™—ç¾4j-O»7=ïfr\} ž5\OJ>Ýg?wmÞŒ+JÖÿõ¹ñªºgܱM (ÿ߬ÐÀÁÕ/4`[ê/0ˆ4âw𿇠ß÷â÷S@Õ{éŠia¼Ù|[!P2¥kƒ–ÂËSŠ@Òxú’Ÿ·ÿâ–mÿÌ™[ûné÷€ äÛ§ H÷ÇúGÉ€òj7¸)¿>u –ðú+gŠ òÉ·_Ò\æ¼±!Ýr{‚’Жr“Óaab&H×ìwžX´äDßù)®/{vòK'Yå ·¶õø£¦PÆÍx)þëWÏWoÓÖ7Ÿ<ÝÉ×tiÝܧ¾,Ý€q­T¸ùðŽŠµ¿Ú޽1{]â7c­'?ùM´Ó+¬òR€pãðuó $YÁ¯ËŽÚ~æ·î±T%%Œ@ ü‡xÀ%M|âã“Ör¡lˈÆÃ·VxOø-}Eßû±ž~6"é›`º­/:0Ê€-eVTÂç éµ¹xÏ0/¶³/6Nü²d±ã^ì-;³âÇ£e }°h]77ãî!þý¶Û˜NëŠü2©Í‚ÔÚ›8höÎ…³óãPÊÌÆÅti IDAT _B§M•G‡kì—߈‰ý0’V—ž«»ÿ ß™½rl‡I›Jꙺ+åÛþ>48®Þ«õ¬dûB©ÕÛ'ÍOT#0™Ô¨ëÊ ðA·ò-ž[%PÝwTèa;†ºm7äqWÇê ðÈho¢·Â‡‡šîÕét mÚ´‰ŠŠòõõ lÒ¤I|||bbbÛ¶m“’’Ú¶mÛ²eK1±\.—Édr¹ÜÃÃ#00°U«VmÛ¶MLLl×®]RRR||¼þÏ•ׯ_ß²eË¥K—8Ž$`à€aw¿€ F*£ƒ1½a%%%¿ýöÛÖ­[sss]Ž)òm ÂýošºÍK@P±júœcÆ­4PDõŽ÷X5œ ©uŸG÷ï Æ Œé+ÆPW$,ØþÅèæŽK«?ú:YúÄWõP˜NïͲ¸wœ»`@˜8ñsût|Û)©_üè…ΉSþÿF&môäÚ«§¾¡os)íÕ¤Ç3KOœ¯ÑZ ‹yq÷åÍsF· R¨#º=·,åè¼Dq¨ºÓâm‹†5u0VJ^^{à½Ö?rçɼ'@øOñ`ïƸQ£F#GŽìر£\.¯®®¦iZ\4èZ;'‚˜áª¼¼¼ººZ,èæææíí-†wE‘Éd:sæÌ¦M›.]º$ëûP+“¼½½‡ 4|øðæ-šªÕjÀF`±ØŠJJr³³yž ôÑy©TªÂÂÂÝ¿íݾ}ûµk×]±4þ=Q(þr5ïðçB¼[@ þ6*ÍqQQÑŽ;RRRB&“‰aOOO™L&îqÃôqg4Åx ð,ëîî®Õj===Ť½<Ï;ŽÂœœWÖà?Eˆ2©¢¢bÆ iii­ãcu:T"çyÞ`¬ÎÈ̼•—g·ÛuÞ^AAA:îæÍ›)))¥¥¥®-QDká÷—üâóÝE -GÊSßš-#]%"·Bn9Îëׯgdd¸ÖÝÑ4Í0b$"ârÅ•ûd2™eÙ;CPü9 Ç%–ª«ÍgÏž½|å"MÓb¢Yž³Û휓€t™L&‘Hl6› ˆ’宅|ððpe‡—~¸8»Á÷èž_~„4Ì¿¨«ðãÁ{¨ÄäW®œZâ–eív»Ãá°Z­6› A¨—$Ê¥aÄ3ÅôVQLó:ù¾x‹Ùn2YªªªÅÇw;Z¯Ãá0›Í®¤Ãu…ù*ü`³—÷õ gÃ/†/ÚðÔ“?d70×*TþbþÆ\öÿ³ÇV¸ïãéƒ 9îùÅ Y,×V¿öø°aƒ‡O[t ˜ýÛ¬ü· ™@ <²ïÝÄ÷€Û7Àƒt•@ ‘wË¢½îÅÙ%Ãêþá:¿.u Ö­ð/ò&a ƒÀ×6$`Ì øÞÔUk„G¾:ërŽùÑøH„²ý-Éîòé¦-뿙¬Y¸­sf¯Y¸çÿØ»ë°(º¶à÷lïK#ÝHª ‚…­Ø(vw‹bc·bwØ>XØŠ¢€Šˆt#K,Û;߀â£~¯¾ïý»¼ž‡9çÌ™ÙÝsÏœ9ÇpŹk× -<¾ûy!>ÕB!„êFÃ*@¿?xŘÃce)âÉ[L[;ÕQU–qÇš#¡Å,-s­B¹)L¾¹mãÅO2$@7è1wå$Gµo6¯¯¶òó·žÓ_ ?±¾Q¶qòÙ6>+Unl¬ž#xÙ„3RNz„¸§÷¤Ìþ]ŽìvU§#wN9ÒtçžþZq^t®VWAaYu0Ì:ù©°maˆ¬í⦠UÁ©ŸÁQÿ˜òîNŠEæ<ÙµúX(O&§»-_3ÚJr¼fùåy/®;˜-‘±mG¯]ÑŸóúà†ƒÏ³$À²¸då{ZèŠ §i&´œ¼¢B¹í´mžÝ´äµw¹v.6 xs!„BèÏ„ó~¢ßJÆ/Ñ»qÇî}ëœÃ÷ýÄϲóhžÛA¿Ëg¼ìÅ’ÿéê#Æ„£¾Wÿ¹¼¹U´ïý,ÍÞ³†65¾nYgÚ›ÝûÒúì¹èwåÂÖŽ;}BJòjoþYüªæÊoimúÇÝ‹ä“Psï“nߦÑ>5S+åjÏó½w~¦c;7»Ì;!…rÄÝy¯ÕÏI£â£ÁвÑÊ}ü$ž/—ä…Þ/(Î+.Êä+ê*Óè*4^^yÕí-Éç»g#Ûmºèç{r¾Ùç÷é¹µŠRZüÆgOL‡­ç¯\=1™vçô½ë[|\¶_ºvýÜ2ÓGë÷¾ç“ +Îåºo9tò¸w«Oç屢¾©1qÍ\2Dx–!„~=ìôBn¡?Õ;d’dÕ©EUoåb©@M«#ê]j|PªNogm:Á0èÒÏ‚ @(¶™¿a<ðÊ©}>§ßä øâ/éˆR^}H ;2{´ûð1‹Î§ ²“2koõ¯ÌSjÕÏ$îÞ§2~ô½(ƒ¾vÅAµV(”Á1mßT‘B¥R›¹µÊ½û:¿4êöG½þŽª•Ÿ Šf÷¥s-^y wóX¤f¯Ë¢S*÷÷Ëžý“¦Ù²¥øÒüéË}næ·›8Ì0»v‘²SB“4zt6dEµÓšË "?ë»u7bEÁªwntp†¶q; êF\qa¹ µö.ÓkæbÁ³¡¿/–$ÝÙ2Þ¥© ºª‘ðçÞþà˜½ò¬Sm ÂjcLÍeËžQ%¸îOÊþmá$Ÿoyöá—¥BèWÀ΄èWDí,e†0«¼ª© +Ë+gs(dÕc}2± Øñõ’)…J!dy¼§‡žC:·ìmÍŒÝ'ªvI•”ÓÍÆî96B¤¨(_Àá$nªµ9Ô¿²:¡ÒßìÈÝ·oé1†ýæ(ÃëÚ+ÐÞ4½2ŽÕ€¶E=׌68[ùËu™˜b=~ÏõEL‚, \:!I_MT(Ï.‘6$¼|©²¦BÕÊ„¢ý¼³—û†ÞóžpwÄ\Z9*fìÿ²wÒ’ŒŒ"Iõ *•¨ÇÕÜåZ¹Œ;¹{ ÏA„þòü'^®=·…ѬúM\9Í’+H ½}fÓØ«G.zyeºÕú YòzÇö»â“x{ !„~M;«ý<ªF³V´7§ý£‹¥¤´$öî…7Tû–š4¼ /–“üØ€—R{c‹ÆÙ÷³$ É Hˆ3Bâ¸}gLÖËQ-=,S(# P@*” ö&¹þ·cø$Yurî´=á2£Ú›ñíÊå$¡Ô¢ŸEôÑÃMúÚ+1¿]¡æN°,úvä;e9 ÷k 'Œ9¶Ð; [JŠÓî_LjÚÇFY«µñúA _.Ë òO5êjþå&›(æà¤y ‡þÎﯙ—ÅmS+G©žƒqþ“ L ÈyÁ»û|¶ÒOÿçiºH~\Àã‹6:ôÚUË2®½ËµrIåIñDè/"˾:Ùm[˜Åüû)þÖ,š»`压¯“Ã}zˆnÏpÛ&À*B¡ÿ*õÞÝÂÁúþ.ÿá±ìã½Çmߺ¤ÿþ9Á5ï>qã$+&€:=ûü\å4S·+­ØJKf}ôž;òMÕÄP•À²2H}Õü1O5Tµm­Ì(Y"Š¡] rÁŒYÜû<—E­óö*ª^/Ïum¸j²Z›½p Ö¹ÖÊŠ€R³¾6¥¡ò>-” ñÍ \s'ŒºvTyPÔÏV±ÚB…S§ê-Ÿ9ì´œ¡ïºhM'U UÙcYOïõCÀu˜º±›æ—kLóÁ£ŒV-~N£¨¶›±®3¹,±zŽJ*ò… >x/é+#[Œ[ëÑ“aTè½ÐýP™Æ½—¯h­H„Ö>°ê.µv¹v.Mq¶„þ"â¸kn•š,»°¹g“¯¿À„bóY'÷Ý4›à³þñÂkÃFèöÊÜ|¢ƒ¿÷¾Iå4mÇÑkŽîÖBé_}Ï “om˜¿âÐíÈB9ËÀiÔÒÝ;fTô—–fÝ_?}ñ~ÿÈBT,{MÙp`ÝPã¢Síu'†ÀD]ê¶M1á^– S%öwŒä+ëò‡zN …éOlÝ&XîåªUÑI†, Þw µÏ>¿Ú…wæÏØûÚykçªé³ÄI6ß7\qn« ÿÁò¹»Ÿ·ÚÒ]­òBƒ$íŠ÷)¡Çá+]Xá;&ï¼ìrb²)ýϯlºµñCÏ:„þ>”&ƒŽ^_œèº£¯Éã“Ý],UÄïnŸ>û2[µ×¾›kÛpJYfLÛY›†rŸÝeDáÆIŽ ©··yͶ^v«ŸÕ¶—)oú´! J—ô5§e<öÙÉt9æ¤J€@I•OnßÃrëÞN§QG¡ßn1LúïýÇËU£Ú7¸†Q»ÁãÛY‡ºú¥a¼…X&†rnl¾êrÈÃøËBR˜|sÛÆ‹Ÿx¢r!×qÖ6¯Þº°sõ×<¦¦¥•$™ëyjfÙ®9מÛhrpÃÁçY`Y \²rŒ½BÞ“]«…òdrº±›×«æNŽ_1þ\KåòB%·5sf§¯ºÙq^s6ÁuZ{¡ …E'…¥Å"š"ûëƒèÒÜÐYÛÅMª‚S?ƒ£þ1åÝ*æÖ’å¾~!£&-gîÞP®Ö`ÃBZä•D_`ãÐD¾"ücÛæ£1ù÷£ni¼Ïb5~y™éˆ¡ìŠœÄâ.^#Ôhâ²b ia0pªº‚Tq4ñ]œD*³Z˜¡© ˜ã aššBi9kj³ü”k÷µF/f§žO|,’E³‡‰«—YXðtwzZ)IÒØÍfš·1¯ª©(¾æš”ð¸‹×äŒìRi'kQ´„šéwµÀB¿/àRïºíu\çÝk·8»æŸ *¶r]vzñ"G_Ý?ƒª;â|(Õlîª]cúñ€¡×näîÀs ãÉWïåÍXè3{ð^ÐµÛ ßõØg‚ @Áqá¼¶/¶ú,J*stk×ABýæp‹®ïq`™«”¾Ø²zõñÐ4>ÉÔ4ï<Ü}R{Qeg¦N÷9sæq¶Õ¢ƒ¼$áÉÍí^'¦K€ÝlSèwuȾ~ú¡Z·Á. ’œ@Ÿõó÷¼/’5jÃäë/i ›>¬iæÒ!ƒ.ȺoÙäÕ»©¡:d%ïïømö¾þ¡»<ü §’n¿%££ço»ÑÙgPU´ÅÿtõcÂQ_gUaÄ®I;î§w·óHîÀCWûkf]Ÿ?9ºr¬bå=Þê“à²çÒPCiÄ‘ék÷Ú÷ˆ8ÙnÓÅ Fåoz™(qž¥I{rqIF:i1XÍÒëh—˜)i^9á Ac1vÏ^~3YsÔ›¯÷ˤř|Ece]EƒÆË+—ƒ"@RRÌPz¾a’×»\†ÍàÅ+Æ6¸LS+ùË8ik%~r…Q&@Í|'ÓwW¶(Ïø‘åÌêÑŒ €Öj§…>¥ìŲä¨lµ…Ùqtý>*QéÓe)1YêvÉɯ ´†ÐRÌ;îUVuû, Jzžª6ØG[UV´2á……]›ØŒì–c‡²Åé/? ¥æ uóÍšÍ]@^^Àì±×J—I ^ÅÔ•>Bè·!ئý–Ÿí·¼îw•º].­>0†R÷«eu ”AÑ™LNøf±bçsEÕ×f™ ÝzgèÖ:B1­n+¯…¯ü6]ÕÎ[ÞmÁ£„B¿Ä÷ŸÝ¢›ôÝ’€¢Ë+çî NãËHQ^üƒëGŒ¾™. jôÝêè"ká‡[î=OU4ï>öˆß$»jói?ÆQ™z“Ž‹½VLÕØˆ Mf­Z>ÌJ­¢œóvv†ô¼O¡?&–P¹z#žÙZ‘.úÏc¸-.=·ýNfåS„b›ùɯœÚçsúM¾€/$¦èõï¬K']ݬ¾iQjPš¾[w#&P¬z÷àF窴l)¾4úrŸ›ùí&µ¨ˆ(*ŽÓ¼—ucˆ•ëœz“m¿àÄmÿíöϼ÷†×zýëYRmFhR.)‹ Üé÷ÏÙ…z7ì}ßðCdT5GŽà}9ÿs±°…Žn1/¿¨,‘§h¦OûÁ唚4T5Ùt†ª¢¼\H2›õq%“îe¼8‘Z$Ido…*ÝÔ¸4 ê¨73û²­¼ðP¥‡º*¶‚uZÎ1ÛFYê}yGjD‘JÛ> U½ eEµ×Ê€`ªhr€B•ד>B!„úýáSßV@î]B*¸œ}””Tñ/îñ8s:0­†/íÇHØ5w×Þƒg6yù¦€iïA–_¯’g˜eoÑËÞio €zk{5*4jÃòÐ;WïX±d£Ï«¹4ûʘþ6Và œ=jàÔY7K@©Mk}œóþOA7ºtPé©rdò¼G«'¬¹›AÑiÙ{Xw=:Y9± A!ê ”I (Eûyg/ïœÔ^%ûž÷„UúDPHžŒBøŸyõrlqÚËOj-t*C yYü«ÀD¾€ÂµíÚœ’_T5MYO¡<»bd. /_ª¬©PuÞÓ”š¨8»6S¡RU›¹:0SyU[ñ_-péЩS‡N·E «]}PWÍ*L~[®ä¨ÖTMZÀ3P×`þðòBJe,Xñß²Wñ}r‹ ¦~gí¦M*–}©4 @•F0mŒFï³n×’Vú,áâ–Üj£‘Õ^¨tJUZH!„BýŽp« AP€ ²¾Þ{¢±iAQ¶k¥`¾øæ¥Ç¯||G(¨~í©ÈÏ.(—ƒ47.¾€Æ¢Ш 3ø9výÒù;$)gXÚuÿŸ˜ÌŸ>ùpãBÍ6#úÏcšòì[pùNŠÄ!qܾ3&ë娖–)”‘,“Ž&Y÷^eKA’ýÒ?NXuè˜Fí Òÿyš.’ð¸Ä¢ZâÁIóý',œß_³ ´ÙH«g>MÍjÁ"”–ïè›u|oJ·‘Í8U9‹?ßܱãNšˆ”—|zø°±V§Êy™y9M«µñúA _.Ë òO5êjΆʷèzN­ä!ÏÊIRüú£ÌÜêËÃ[ λŸ½|ñâå‹[ž¶_Ïy‚£hÆ-~Dš1µí æz©R;Æ/¹¼,G,©£¬¼8¼œåbÔ®†‘Š0#WN’Tõ6ì’çE¥2åñ>¥È«*¢ÖŠÅ{TX,²¼<&HªÑŒž>âz Ű›¾ËMÅ@R‘ UµöšÕ{3Ö—>B!„úYßvKœ“ NF@kÞ߆û0äñð¦T­‘wonl^Õè£UÄl)'ŸúÚ‹gT»'PµP&®v¹ý6V³©çO{|~àçû4IÒnÚòjxÿ4,ËÑˇ?žLË!ƒÔWÍóTCUÛÖÊŒ’U Qïºh껕ӆždkiqŒÊ¡¨šÝ—͉ñ^è~ ¨LãÞËW´VW4e´jÁðktEµÝŒuÍlÔ×L¹wtÛ‚“B*•”’Ê-†z.í¬ý%j ¨uZ¶–µ×¤Bø×,êN!„Bý¬zÛU$I}½èF3.-±€²WÛ×®>þ&™¯Qn%Ÿèérâ³í§w‡6IÈêI'”“tµVC{j¾¸Ph]1âEÂF÷>ûÓ¤œÛߢRù’Ö²ÑPõ'ž|±ÑÄA›-~VBoêyñþ=Ⱦ<°ýž¢Ù7ëÿùz°~Qj ¯›Ÿ› ›·nõZºôGÒ–¿÷»YÖeT'mjÉ›ÍÓ/µÜ»»>,ô"ŒÌþÈWkíȤ”ñ®ÌÒ_cm£ú¥ÿÇ᪨”ðx°nÆ€€<ÃB!ôû4bdBIêéY{ÿ™ë¤¨è¼dÇã%)T›ñGqqç£ÞÛº+8®;>'->ŸÐ°0Рeé~- °¡„pCYqbJ)X(1œv^Þù–§Ù¶³¿¿ EQ_;wõÜû ¡ØÜceWõ¿,Öòqþåœöºê¯èÁsú6¾mñdEUGÊIo}#„Ðo ·Ѿã;ÄŽš7zDïVÖÚL Ó¹)‘oß>ºò0C@f_<¶pÆ´™#[Zi‚¬øsèCÿgYß»É$ý± K_úÌÙ«¶eVkíöOn¾¨è9Jáß…ªÕeÉá.oùça‚j6ƒB¡z’²ºç”@!„áúÿ"ŒÜ1lüÙTÉïÌC–yyÚøcI_óà{ ö|Ãÿî¸8îÀèiײ~ª)Rè9|y0ÿ7ç‚úBAÒ?ëGµ5P ‚¢Ò´û¬cïx_烼Þ;ÁÉMÁÐn9lÓ£ìÊ®õ’ÌûÞƒšk1‚ –vËá[žåÕû +|µsDsU‚ XF]^O7&wYÁË]£õÙA]Ó¶ïÒëÉ"iêãÙ¢—Y28ƒ”âQD¡CÃ*@)v‹…ûL•ØÿÙÏ¡ûöM M¨?‘„(ó}†j+#ÖïÍ!ôŸ'I:>¨ÅÔ€²Êà§8þñÁ©mž'¾|µÙY™€ò÷ëº:­û 5 Za·«+zD ÃÞ®³§Fïì×{mMËÔ”HOÊùpÅ«¿´YÚµ¾uv|5µÿ+¶®±6—öt÷ˆ±¶IO&PÌ]žqadE… mßÙŽ[ö"âî¶!=)ïÂ74QÖ×g~HCµ‰©6/Ò"„†[èÏhWd?=¸áàó, °,.Y9Æ^Y–°sõ×<¦¦¥•$™ëyjIÓ‚ÚK¬ÁÇ×û<ÊÝ ÇÜ•“Õd÷w¬9ZÌÒ2×*”ÛUËA¾k凇øÏ>M3¡åäÊm§móì¦U–Âä›Û6^üÄ• ¹Ž³¶yõÖ*|²kõ±PžLN7v[¾f´BÕo›§žRuÖNKJ,0™±sY'Õò(myß IDAT¿MÛnÄ HÃbøª•#š²ñ7¶m½#`è:u¡¾Iw?º@²aÚ•ž'×ißݱãz¼ˆ”rZOóžÛE‹Røæàꃡ%ra6bíòš‰µ“&ÜÚ¾ùbx)Û¢kël™EWñ»u£wÓœÍy‰q)D§‰K^'$¥sGìÞ!%yOvÍs;èwùŒ—½ ¸ÎÞŠ²â\®û–C'{·útþ^ú—uHþ§«Žú^ýçòæVѾ÷ÓË?ß=ÙnÓE?ß“óÍ>¿Ï}ICZ›™G8ÏY¿sç4åIBiÖËïÍ—œºìçwĽÈïRŒ $tϪÛ: Ž_:ç3¸àj§¥!‘‘Îm¦±kÕmí9GΞ9³»gÒƒïËäyÏÏ„X¯D]~.-¾" uœïaÍ&€i>ùvv©D±Ý‘CÕì¶Ö÷éûÃÎWmð> à äugd?q¤-‡ª†}móŠEäwrgê·2€ämm•u[ö¿õ£ÍÄå« ±U³Bè§Õ{w ‡%D?A˜”¦ï¶Æˆ Óªwîòस¬½þuéaÐÕÍêê%ÖZ"Lyõ!-,zöèã 4’2cSuz;kÓ Â K?‹€œºNU¶q; äêF\qRù—¡Øfþ¥7WN¥&}z“/è(¦j¶l)öœ?=Þ¥S§®‡Y|íÀ'úü¡´ùÄá6Š„$)‰Ç5V§S5Ú¹uzðòô¾G‚¼iLG¥ÒЃ!†ãÏØ(„ܲµ±š¹’(õS¹AÿÏ×ÿ1™xÚN fÕ”q?£ ì[tà÷œWæ1y²}]èâ,Y¤DŸÎ½n2æ¨"…Â4³Ô1$L„ŸÃËšOj£ùiìNÓºëÑ%)‰…*Mµèå©ŸÊ Æp³¯Ö*!žeýUÄ…Y% bf¤Ty¡“®m£ EŸyRYIv)°Õ¸ŒÊ7¹jUŸr²äýã—_Œ¨xnªb…úçÀRÒæÒ“@ÊÉïåÎl¹Ðo_äð…Wâ%ÙÎ}8·c1·Óºþ+ÛrñÈ!„ÐÏÁîHè÷#+_îÔÊ`¾öRN7»ç¢¯ïeßKOŸØ:Ä€PuóŽB­§uA¡R‰ŠK5Ëó­ž°ænE§eïaÝõè h?ïìå“Ú«dßóž°äŸ/ƒOH²#²ÔÛZ)Q€,OýT¦×L“ÿrÓÜ1\;g×ýZ±™&-4ézÏy¦jb¨úCwt˜–C©¯š?橆ª¶­•%«Ì2Zµ`ø5:¢Únƺ¦Ìʲg„ç¨Ûë1ä%‰qr³îª,£>Ct= Ò5´0WUbqT•ö³Æ>ðš<ÌO×X5—ce§!ËøP ÕÎ@»ãü‰oÖÏw‰NQl6zóÜæ*týY×m;šÎ$ضWõ²}û¶fR\‡)Co,Ÿ8êºe Cº¦MSYƇ\ {=&@yZD‰ž[:È ’£ÅÆSÔ¥ ´Úiä×.!SˆÐ_†¢Ùoû¦N¼H?ëÑô¬GÕÒ+ÍlÊB«ßæ¥v×ú´©“Æ&¢ò¾¾í¢µÝ45?9ëÁ»Œ„­c& {2ƒOˆ(Ë+“ýÀpW æÎté¨2çº0{e ³k,™oƒX]gôÑ£•k Ï†w‚´m}:ùf=¡7¼BèõZGâãCß—–Ðh mm3KËv¾¾ÞX_ÿ ¾{Í4666?76oÝêµtiãR•¿÷»YÖeT'mjÉ›ÍÓ/µÜ»Ó9íJÍ%»ûhü©·[Éòèk~)޽ ©¹÷—Î v|U[Elrü¸**%<¬Û°! +ýF°%–­v¤A§«…χ¨Ywcëê­'ýCÒËAɬëX¯'´T®üâ“å¿Ü»pÑ¿L10´[š·Ígq×&4 KÞí›:~µß§bP²è1i^OK< Œ<Ãb¶ZEM¿Oâ| ŸŒª¼@îem¿%ÚŸÏyå¡E@ƒ¹Ë‹ÃNz-Þvñi|1  dÚÉ}ÉÎmSÛ¨PȲ°]#/¿"¦ =ÿÆw„.^îA¡Ÿ ·fÎ<üüùÅȤÐ À à(f¨vï>ñæÍXený«p d¹Ow­>ôº ŠÍ=Vzö7¡çÕ^ÂúsiöÃm^GÃÊ4:ÎñžÝIn!„B}GâãC#“_À*€¹ ðÊæßš9óðÁƒÓTºøEot…L¶™ÿ¤(Z}®†¯´¾×lY`Ö*ªU«Ë’Ã]j,úvÉüIÑî±üT<Œ!„BèÔî»5}ú„„·Ð`QU¬­–B^ajzz4VB!„Bý›pK$â—”€CÕÌ_t©‚¤¤¤ðûIL“A .„xì¶|VPÌëÍ3†Úv÷NbVл[SÛp)tÝžÓN>HÌ Jʸë¿o°µ>ƒB!„ú‹ÕîLH¡Ðh4:ä³b‚F«94ÕñxlP­Ù-§Ÿ;8L· pç¼DßY '®9+ýÜç4((òž^>#7Ñ©ó’UÌãG®ÞàNì8γǵñ±ÃÏŸ£ú`ïÄyŸØý¼ÎX¼7)¬Ïîd ¢?ØæÍ›±B!„jl¸¥¨¨ª«Û4çA2|°¯Z*8¹²¶¶YÍÕóŽÝO¡`>|B;U†IïNº‰G¹œF¼×tsjÚ«“ñÅ€„;û|ü Úvr–_òÙ¦Ev¼ÒDYGŬi7cp{ʵ2i㶺,ÀpëV1ÞB!„B¨Qá–Ïw÷5 wÞ–Î)€%r. MMƒkË’®ì8X1TFÛÊp«Y9m}ƒÜ‘2©ìKR *ÒÍŸr>»bîYR˜ÇÇãƒBýH9¬„úép |}½ÝÜßÊ‘&åˆ )Á‘)[X8´o?x÷î±ßKPœrÿeöìaæÓfM/xIé5 3 0EÜâ;[Ý}ž1k„áÄé ®¿Éa˜tèÞ©øÀè5‘:Û»/ë×ç$Ê=×±*ýn¼ÓÎçïfÿú¼ÈòÂ7;ž<Óíæ9Y¥ìñx³î×Úž¹5FŸ†G!„þ]¸7oîœ9óPffBÅ4ÇššFššˆµÊß7Ÿ¾iYß…»:‚8÷íÙõ^›£Ú-¾·Õ!IâÕKú[¹Ü@˜忱{þ™öîÝ‹•€~mó_¡ÿdÑc¯…÷ø tT!JÿúÝ‘•½sêÀRo9ÊÎó'Ý6Ùþçz«áˆV!ôoÃ-8xpFCÛñžŽÐqúòJž{w°ÎݪW‚x¿mÃü¶ÕX?ùbO‹•?÷²Õ©ü“d´é‘Ê¿Óîš|÷’¿•Š“ü"„P$IgWœ/óÙíØ¸EÊ…¥Õ `ÙŽ›ÔtÛê +Ϭí¾ÀœŽÇ!„þu¸…PÃfÍš…•€~ÒÞ½{1tGÿ]D1§÷½0>Øœ ªºHs†úú||û¡TD0uœ­¬èص9³ò&‘LwúùÙÃ1q™R qŒ{¶³Á¡™d9É×—?x”_"`pŒ][Ûäh«^÷Í%9/Ãzàuÿ|>ƒk;®ËÌšôïæN–†¼;íýö͇2 UM½¹{‡IK,´(%}ŽžˆÈØtbä.óánVnÃÍVoxðlìÌuvL<â!„áúm8€•€~†L&ÃJ@ÿU$)w®%¨vèiöM$"Ϲxs‰gа2ôe½üp¤wzöÍ‘£˜H’w_^¹;h*l*¯<åNàúxé¶{ÎÆÔÿ±×®| qšAAjyÊíÀ­2ÍCÇLê*B¬çX–¢¦…Ÿ[yÌ¿å”5#•( æNfGù¸?û([%AÊ›ü°ƒ·6RÆl÷Tàè(Ñ#K%@g))0(À4íᬺ!1áêí”Uv–x !„¾£¡a†fÎ<0F"/áKL LšÛ, ±/hƾm: ü”ž'®§ v·š~àí”ém)òø»y¢ïå.Љ‹:zÞ±êÚ¸=—ÚwݶGsªŒ¢ØñÄȱ¶zK<?éaÍ`µ5€¸G‘%$q„úZ=Ö‘øøû÷T •¡­mæî¾Æ××ë !„ª¤ !S @×2P®ÝKVý± €¢ï±Â² €¥Ûß»ÅÃÞï âbÃ3Õ3“$4ý¾ƒÕ€qóeá¶ :ÀpØ!ÃarIQ\ú›ûŸ?Ü,ŠÄòº/™»[é³€mÜ\‚K¥%b Dƒ¹÷Ði¢‰Yi!Ë­";5ëdÚu®yS=Zݽ©*š4if\¾ÔñöBýp¸5sæáçÏ/D&½€nùõ !þvˆ›Û¢›7wb•¡¯düÀ©/’'tÓQñ«,ÿ´×›p›Vzå¶ñü­Ú?Ü«ŸŸu|x¢Óå6ßv‘—¼Xø –Ùv—KÛ_Ó –äeßÙËo»ÜŒûîÍžë?V`Ù總££ò¾~’4ܯuh®Ò˜-ËCf>¼Sý¢0ÕjkïÄÛZe7T*YJìþå¢!§šë3¤IÑûWˇŸ´Õe4v¯5±+1B¿š\T&†ó›@HÊ+WEC±2Š¡ji¨€° D./ã €ÅbW†/¶JU¤8ùôÃ[¢ÓÊ b‚¨oX@¶³"Ô£V‚$ÉïåN·k³`CÁîµ±YR~ʳ¨”gQþëÖK†-›§Ã®# S ñÅr<ä!ô/­øøÐÈä° `.@E«÷=”Í+ ¾5sæáƒ§¨tñ‹ÞèPÕð+J 8utûéEڃ܎c·¶Ë½>¸ÝŽ‚F­Ã´ùèÑh½ä=]NÄ‹ñ i•c7óÒ¿@à¬Ã!@–}/‹Ö£¡}Š5ñK³’ó #su'ßjiü릓‘—–¤¤ÈH`´°ŸböæjO;coÄúáJs<2Ð@òñýî=JYhP+‚«Ú+þ»R5~¯B¿áKQ• ârñ71šª"@PÂË/#C€47ŸÀÒP¦PÊ8l~YO‚¤8õzdKÝÜAO;ýÅ–•ÑEÀuZÕµ__CäûÓGƉ åG†ao8w hÆúïÕ5õURسÔð‡qQiâèÏBF¹wV¯ã[D\.uè!„~8Üš>ý@@ÀQè °àKC¯ÀRÈššž]sõ¬;ÿù̵ì9ÆÅ}ÃáöF3¬ /ýË›qÒ¬»Wq Ãó¤xz|¡ìl¢y:5©XÇN@Xüö5£ÕEYxð±ëÆó7©¦|ëÿˆ/ŠFæÃF3ƒ¦¿g¬ëì¢'|=íÑ›Î]çŽQ(¹x¾Ä~Æn­Q¼ï4E‹^^R(’ÛÚŒ›¯ô~cÌç,ÉÕ¥L÷ÆE'ßÝ{Æ—M×Í~˜‡%ô;ÓrVF¤g‡©q‡NB–ˆ—-`:››d'§••éZŽó6V—–„l{ÿ"R$åËXmíÆ-ÑHÜŸ”™(óÝÊõèžvâ†ñüܸƒ5R¦¾­Y O}å†Òòˆƒõ–mÎlÕ†{ÝH3Rý&}üœ%åº4»HŸþáØuã9 ¥«£ã‹I s—·élMûNë›2°òÓo}Ia‰5çBå^OôÒU¶B¿ö7UÝÒ„ ‚œTž ¸5>`L˦֬˜÷Âô‹cÌ·Xk 2n¯ /+ë:&ÇÌŒýQ’qóT†ýb}F|Äñ…Oc¥ô6§§LÌÉ*cÛ!Í ²,ÿBTÞ³j¼s'³ÏßÚ¾3%[½í–;íumÖoÔ«EÝ^g“¡ˆŠÑCEb©XBÒé49/5G À6µÒÀ{ä!ôÃá–HÄ/))‡j±V… U””ÖX(û|çè™'%pèÂØ›Ï¦[N™;ðØÔóé4·™¼ú·7`0'äâa¯õ’…4]× k½ÜºX©PÉ’ø;ÇæíW?|¼aØÆvýîð,§>xRùw¡Ñ¨;A³-x'¸´V—¥<÷Ùa2ÞcHKU^ÈéicŽ¿-¡êöœ¸~åsEB΋¼~ÜsÙx­‘w‚f[ðÂÆëº84¡å„ì¿üp¤úTÿ­í@kðõ¤Á¢€ŽóaÅ5nV* (ùxv¯çÎ4·šë8y›M[7Û"ÚçÉ­(Éüv_>ëԕׇòÿÍ»Õ&í,bÞ†‰l»0Å1)‰F.Ú¤Á ¿f4颾2ɳèÕ휚Ë"Å”y±¹”²·Eå£h©ARÓ) uý`“ü\z‡³M©Ew&xŸÛ¹ór˘E¥ý·YsŸKЛ|ÉTCZ0ãím›®ýA^–Ër©½1›¼%Ë 8ζ5ä%ì“L?Öešÿá”·ai “ÂV3.i+ þ™–¥ï2Û48O>l©®Òû4(yv»fÊj#]·‹Ñ×B²ì”#=S€ª7£Ëtwþ7)T/å{•))¡¶>Õc 3ÿú¤ˆ÷éºNòü»qéíÛ.¯$z}÷]™ÄZåËGS–‘tÐ5éëöM,ÈoöÂ¥sDµ>’}g›†Tì5ÆZýrŠ6=màŸwÉAÉ‚†Š5®N©›Y¦±6½àêyWïT}‡jÙa¯CBÃlÔ,{ò?ïõôe&yË6Ã:p¸±zjS˜²o±¬#ëÙ¥|€rPö£ 7˜;Uæ¤ÏY/É}¹¸}œ•)µ(<+€îlßJ›*C]‡¥ùGÎ9¡??Ì݉‘ü:¬{Z+àG¡ï·–k¿¦Ðh4:ä³b‚F«ûê¼(鹘v4c²[N?wpX{NèÎyw½`:N\sÖËZµù” gÆw7˽ºqÛÊrô«¦©‚"ïéå3Aå ãÎKVµáݽz#TÇyöPåØM>z\«¤“ûNy¤Èvèâ½S+  JeWùçM®h¯*Ë»»æT<”ož¼xÊÎØr¹0íÙ•õ3¼fÎ=|·Ü ãl¯9ÍJk¯S­uî »Î¼ˆÿÙ3ˆi6H%ïn_&M¿] 6@»êþ4ïUA~XÔ±ÑvŒyõ"UV”,Õè¬RT\šœSîhnT”“Ï‹,Tµ5®»ÕÏ0ÑÖU ³4¹²2Á—`V–Tª1POƒ „‚jëŒô¾ –™¶®"A¡0 5›p€¢ÄᨩêPBWæÈ…b`·i>j}%æŽOl\TX»¯4÷›”¥õ ¢3a€Û¦çn›ž÷Ÿå®HmDÙÆ4×3U'¶¢º¼*/ªrKMÉ¥W‡W| ÎoÒm˜JõË T=Ó™* à¶î”¥£Îú(ÔŸBè£êöpoPôæiªè›÷ô&Ùq¬½“½€ÑĹåô{îÃ[V<ûJ7]8bÝZó&Tº‚逎«ýÚ›°Ñ¢ƒç:+%Yʵo(6vnÆ(Ë Où¡> åN3måu§§«³*;77úuV6EÙڣǺãÍ4iÀ´^Ø¡µ¨Z¢ÔgÁ<ëa]uñš B}_í+cŠŠªººMs$Ãû/M\€3À‘+kk›Õ“Ne¯’d˜ôî¤ xôÀ‘ËiÄ{M7ש¦½ºu!;¤ìñ^½?Ypñ …i5¹¡r%ÜÙçãWÖ|´“³4ø’Ïþ0-²ûà•&Ê:*fM»€ëÜS®•ë·ÕeÞ¬ÜÊg·_¶…‚[ÿñ†jŠaÚû˜‹òŒÐ'A4Nê4Ö}tSu-MPÒV”|~WcšIµûÒÉøbIyQ¡ðµó!»™‰É¾ø„ zL ·íÖ—À“”S´Ç:Ï®@ Ä[¤§™žó²T¹“¹MahT`z‰¡v=Ï>Q¨ðý–¬|\œÂøú…^u‰¨‘BÉ£' å`]ÓÞªô¸I#SnL1]¶ï„®ôo,ûfóüŒ3£î†î¿k5{—±*õÇÊP+…óñ{¡ß‡n2|¶ÓÒiA×>¯°5gj Y<æË›]§óü>ÎõDl«)}6MéS{9Á0Ôoç¤~_—Œ¯ztª§¯=þÝâñÕ~ß¼¦\öªžN¹»f“/7«ó‡™e×fé›6_®L%]º™à0k„JˆBiàÕzíã3ÁÒ²R¦:Ìø  `-ÀA°°pÐÔ4¨3†q§¾æòاñ¢ª¦võ0Œ (Ie_¢³ÊÛ4:µþ¦(ùuIÉŠö3A¥@Ú¡ù=Ý»9»wsvï±ð=¿úVb¡ ®62UÏ}‹ï¶‘ÝXáç·ì=ðRØØ&k}©ùV½yý/QPsh%xs!!ËÊÄ„ûµ¶5Ú*û§¤ó,/z<ﹸÕlTrŸ?¦XXq Ú@ØÙ|•½ªé¤”ïŸY ’_öX¤Ó†Ó¸èCVÂãô±ë1ÌÐBŸœ)#å@H¥²ªsQë›”iÿOek€<ýÐÓ³þVýçsóJøÒ-+»V òê{úÅhýÇj@Ä‘ qÿ£-‰ã/ 5u£ŒñÁ-„jÔï·‹|}½ÝÜßÊ‘&åˆ )Á‘)[X8´o?x÷î±5›v†fN°W0ïæÑÅ nŸ–î¿Ìž=Ì|Ú¬é/)½F˜d<~zÒ§¸›-XµZâ)3êÚ—ujI|1Ø ž9‘È´é¤×èoú”»Ï3f0œ8}BÁõ79 “Ý;º¾Æª¨X Õ²_'vd\© $99­”ÓU‡ ùfÈ÷åÕòªc_SÄ-ð¼©ÕÄ×íפdêgó}êìj‘§’KËÁÑo}ÇÜ—EݵåÈÖt‚Bšt`–dk¨R•[i0NÛÙÑ0V%¸Ý[ö~{rdõâå..ªNmT{-d}P¿¼•ô¸êF“ŒzzˆÊ¶hf2VQqé¢Ò¹'÷p8Qg(ÁДNX'P `½+¨UZµÞ|%P^±ƒ©! åÞzegUggÕ)è<”>Sê2”ÛßAÍuí»3m‹S™‹¨¶m¥Ú9#KV™oI#v­á2Ô¨œ·´kU{š'ÃS!„BÃ-ôÇ jH›Å¬l¦àê“¢ ç¥íÃY7Òå_ÞÒ¤Ö*‘|•îRúð)ïÖiòkj} ­‰Ô‚F/ü¦´oäƒêË7¥Þé¤ÌÍ+;:N•äø+nˆŸx\x¿ÔÂ_q¢ K¶âqá½¥RÖ÷ªWÈ#ç½~Vfĩʷ¡]§²7«,€m?v‚ Ž2P’ª§ðúÔ[!„Bè§à@ðè'vy{éÖ³´Âž’8?ºÁÈrm*TÜöI|LK¦y¸²@VNhÅÆ=¥ùhyú ^GAëHz\ù _:Þ¼zôFv^VüzõýKFà3öŒžì1‹§0+ßä:ó÷+Ób'ÆÑŸçÝEdýç/©ÝN"š¬<Nz"¿{e­¾fF$>¥*3eÁ’ @ΤŠÛ¢¥Ä– m¬EíµHŠLÖB‹ˆãWæÛÀ®1Œ×o—7cˆc9çu•¢Öµf Yt<‘B!„~Kc«ý$å6B‡Ï¬ TúåÙ¨Žä—SŠ”ƒåÌ’OxOžðî#S±›¦ÒŸ<¢j÷÷nB}ø‘j!²¬œdÝ嬾B%•eŽ}‹¶—œO\¿òõîMŽ¿â€¹ÌTª¼íA?£e  "!A$PräßyV2υ̸¦8p+½žnr’Z<Ȭ4ê(-½ùþ€Ê$9¿8•A~ó%¹k ”¡v R<‹B!„0ÜB&Eéˆö”óGX1Í„mU¾.6î$ËòcF”ɧíóP^JWÚCqôtl&ká ·Ðu{Ë«…ÊZ”g;΄PrfÓ k’A!!$$‘HSV¾`¼¨ƒ&-8 dr *ß [NfÒò$ Je¸1ë"Kñc¤ˆ,£Þ¼MØ8í9ùo5j×,ƒ<¶f E²ÊjD!„B¿v&D?´.ÉÄp¾$å~½kCjö*Û®¸ÀUU ¤‘[™IPÀ±»<']b¯:í¤œ½´^-åÕïópZó,ଜ¯²5ެãhþv79C$iCrG¸“V 5p{ß•kèKí¬ #B1«|ëæÁÄ&ÜÁíYšV’¶Æ@Z˜Îæv=GÒ(¤ËÒRÛjI‰ÙýmÙly›åÇ—H9œoJKÖ›/ÙèÎwMúó—TœÐ TÒbpÙÖö$ø³µÝÀ®5® r®IšP*«ñ²À û"„Bý:VªÝœ'¿s›#66–J¥Îš5ëÀX]ègÈd²Šs) k!„Bÿ}ðîú)êX ÿ5âã °B!„0ÜBØ@G!„BèO‡Ce „B!„ÐoQÿ¼E$ŽSö7!| !„B¡? ¥á<ú+üÇN)õÄÕi(_BsQÇÕU!ø¡RÇ)ô²›jYb Õ +_–>ãvœñÔä…ô ؉ՆD—$púvçDTMÚ%ŽåôvUˆAÙ‹:Êöíæ?D’Ìîi¡nñåŸ ÷Naã¶”Q. Q³¨¾­…ÚÔçÄ·…¬³ØÕ®Ž±¾ CœÔ­šªÛ9)/¿JüàÕ’ï¤B!„~y¸…ÐwÐä½FÊ#ü輪Æ}ì5c€Ð¾ÿÂ)çߥIRB/Òu\ÈGç~ õ/+¦…&€¼1Ç¡Ž²5~óúÐõD~ âã âã â£Júª5n3ª|äµÂøø‚—„ºVå£ âã v&Yì¯ao*Ës/uØÙ¨¸‚7'E™;”N$üXÞpú!„BÃ-ôÿÔî&4‹b€€vå9¸¿\ªà±’V.#wr]]Tº¸¨LØEÏÿ?öÎ<®æìÿãïÏýÜýÞîRJ–”劄¥Òf)[–HƾdfŒÆ:ömÆ®ˆD(‰¸$$}¢D\i»u÷ås?¿?Ú÷ßïoÆœçã>ft?Ÿó:ïó>ç~>ç}Îùœ ß>ˆ¿??9ÌØs®,ëè.\[AÑP@ zBŒŸ©6¹ÇT ‰*« ‹5rÅóênìñ+ýôVÊ Öü¥ô¢‹• ÿÔ¢ô#nîWOÞGÔóeÉÕ”«ëønNB''á´½4iÅ\ÖÓ®ÌN§@E]3€wVRgNºÚ¥6PÕõyV›ÉX4Tس«Ðk]BÔdvV–jLE7X˜’n;ÍúMJ{~ ¤ŸýA0'#@A[ÒŸÉñ_AUê)×y}]…ÎN‚¹'qU5@  ·ÿ;ðF:üÜŠ@‘̸o¦м8 À b¸ëÓ5ÇoHoEËú=æ>$Ý»cw1¢•@ž˜*%àI ÖË W̽ÍL¶ÔºvÔz7¡¡”õð«ªÝÅHÀò$Äò›ùW—kÇ,SÙ´Síܨ5Å+F,,ïŽ%‹ô: a½Ö”Á2BÙIn²è[ÒðÕúŒG0¨4¹!’˜ª=|³ îšLt™ø B.¿è\Æêߣç |μßDãÞ¸NϦ6 TS£èEÀµÖuà`¨É4º¥*슲#@û’=lnùú:ó^:ÍTþèMí¼‰z–„Vϼ¹…[•·bÆ$F%gÅáÚ^å¹ðº©>±b?k,ÎQ-}•&+¨Í4!Wå]Yå]ÝRõÂi¯q0#)ÑÙ {¥ k6»<ê%])¼? r—ËšåÉ,ëQ§ÇŒ4¢íÌÄ"==Œjé£4¦Ð ó.n6TÑ’ 8S³=\ûèAUW‚†Ú<@ DÃ;Ëÿÿ&p{ȧKÄ÷V[3þž‚ˆ:áÛ«»¿gŠ3ig“‰±ÎdY“" `Pt=F#Š(<8žØh[eÒbnàæžÚñèhz¦Hc]4€>‹ô{(èi/¶…¢Le†•>ƒT]€3ȯÚ'„4ꡈˆ-šïF~8Ï6…™¥¯ñ,(Þ…¤<ŽÎ×…rá =$‰ðs0|Á§ºT}àt’R¯Ù%s˜$’½êNò‰ƒU‹¶Ÿˆ…ÃUuz èF[SÃîÑÎ'>¥e!ËÃV¬0ÏU× €@ „æù†a×;¢ ò¼°>cwW ðö_j¿AF²ë#ؘͦWºÿI±´oƒ§v`†µ˜qOª@ ¾I¸Em:9Fœ.§KnjT207ö»t7]"N—ˆ/Žiô—‚6R'•ÈriN‘žªùøât‰8q—û›"þë³Þ×rê óEGuOAù×V.„$„‘,RAÝíÏ_—€Oßߘ~(œ; |/­é@‚[žËŒ`J\dwç?z”ÿèIÁNWJh0®0TW«X¹˜Ó5´¾±äMü HG_åêUšÆÙ”¢89ÙÚxB«RŽ_¼‚Ù8UZèí}´yûØOmÕÝùõ´Âz¥¾œªfç—t¯H¾%v+çøCŠÊêOÔ¨8¬I{¢mÌ`ýX¤éYZËÞÄçú{-òik'³ßØÖ©€@ €âÆñ‡E•¢­¬Èc‰ÿÔÒ¨žn[|ø©É˜µ»nšÜމª@ K}ÉÉ4‡¹ƒ­Â¿ÑÝzØÌîßhfL‘ø‹£Ç/ßòæö­õ¸Fë²GÐNëyås7¤éù†$îB¡HËáòŽ$Fý ÙY:;hÒKÏÞEÐÅPžB‹_¼€¹¬Ñ·, é:C»~6+Á µ¸r pS=Éóõ#/žUµ® †l?NÕj¯ÏI’J!Ý–È:YPJ’)–=ãNêËœyË79TÊ­´ýLy£õFõåÑØ«©¯ðs³;”öuØÝ²W,lÊÁ€M8Slnh uy øÝÔ ˆQ: RØG±ò¡Ñä~,3ØÏ–Oñ&ÛfWV@ø"Zônþîú±„"Ï~¼ÒhëCä±ç:šä&ÿË£/’AÛY+™ÓžŽj@ D­Ý'’$K^èDm:ùzèŠöÅ_ç ½òî´+ô䨒›Ç³‡zŸÎ5`ì–C—ÌíÓ£ªì§WC·¬9s?‡VÇ ýLàSرhã¾Þn]vÜÎu v<)  ìèëõ~ƒö)†_¹¼¹K;^íëÓïT&­匽ëN›ùÌôiûqÉÈçÌÖ• îy§¯;w¼Q׿ý8mp+>Qò ÛÒ© N{\þHýO©ÅòÊúFjuŸðòåKÇgÏž½wïÞÛFû–9q6å—Pe'ºz|‚(nKQQQȈï Íó5¶¶ëšlþ)÷çCÝb2OºÑ™ÚmwXj¼kuá†IK­é Ï¾¹uÞO¿‡%~Ö³­Ü¦¬Ûÿ›;6 »9ºÙàœº‡¯Û®dYyüxðÐøüíÓ:p'K+è4aÛÙ“­Y²ë#{¦ÌÜ;êŶíWÓÕBÛ ¶î]êÙ¤xà©Vñë#̽ò׬5Ù³âÂ;á ¤‹c›–?VªÎ\°|ÿ•çù¦…ãØ%¿oÕC¨MüɺëÖw%§tÝÿîáL U4@ÔÇ—Ìn©UÀ4ñž×k÷fÀ027*¿ÔÒšŒ>rx£+@ñ.%‡cceç=;¨«É¨þ»Ÿ”î.gî=q¼²H ´ÆÎ‹×þxÛge•†Ô~ˆOx×±{ *€ôÕýg…ª×ïU”ú•[Î^¹ >Ön~͹Ó,§œÜµØŽ„šöhÇAâK‰Lþ…¥ùo ¶ÒÒòP›G ¾zËáþÖ›~?‘(wwáñáÚÑd‹1ÛÚÄî*¹ëIoÿèÐowÛâ}û6úpeã¢ñ½ÞÂÂÆ5§©‰ósÃñûû,2öù_ïi»[4lãñLJ¸ ë†Ož6Ù¹ÿ©€W¿Ï>9"ðÈ[ÅÍ- Wè§O|ô««qí%ÛÜæoÞÝDßԩ®®„äÜxûÑ¡ÜkýÖƒýöòæŸçôzœóX¼Úæç˜뙽§¥M¿`kÙÅZñÃ-ޏ®ô›º` Ý©\£Mó)ÛUŽi3z¾+@y}ªo@D>ÃvzDôD++ŸEýNŽ/¶–ž=pU¢ÒÒï’x^;0éfgŒ?úXé]>¤ìþú‡ÃÖv€ÂèÓæ=U0;ϯWY™paÿå7ù*ÍçûE0«Ñþs§ÛùM³£’7L³'M×ú‡È¸­Q»@ýoø0Z+Z±÷T’ÂʼnÄǨcÉc·aÄÖgÿ´;£Å.oéÉ—9m¼–l~2rW ôÞvdÉ &è°p~×ßgfzÿ±?ÀÕ@ôËÔõA;n¤©¦v»-Ñg[Ó97ÿ,¸cýíÏõ“Ö/¼mB“ÊOhžïY*íµ'!dv+xöµÑµë»qÉåY×|[[[ñ©Àlܦ};+´˜@ Æ—l®&K>$hé7ºhâ]’TØÎÂkgk~5¾À zsï^à­{4-»&+>å)  ÿü*M@eR°®Aʯ‡ü#ìô©ˆ›oTµ­„«)w ¿­µ À‹“gß(Ií€@ ˆooµöÓæóå d%!¹~4©™Ÿ¨l YRx"4æcÃÔëõz½ÞÀïé׃ö1úæ»’M ›÷´RpŽ À²g«âM¤p¶ ´ mÉv¢¶ãG´.¾#bÆŽ\9²øéêzÅ[8uVí¹¯g€ÝÄ¡–%Áb‚^? i¤}ùR…j@ þ»á€æUÐÙçPp)èNNï:"kügåS-ñ¦6@¹A25æNhÑ.†øöñ–h”o«OáÁÏUħëG’š-*{G ¡øœ¯‡»x´R̼¯ë /#¯d<“ÉgV¼Oãt¼d˜²Òh%Eh!([¬B6çƒ"W®­Wœ%`U[HÈ>Ë"lÂ+?‚ó› @‘§ Pe"Ä;ÜÝ»¨M»nÞ¼vyûîgE•ÂCÑ‹ÿ|h=°—Àjãädú×ñ¿ìÅ"$I,û¦Ê5`(zû^ÐfDÿ&tŒÂmj†žÝúôøá¡Â7(emA“ÂöðàÄG9O£É¿T œi,™TøO¿ý¶!ðc#¿¿¬UA—Áò¬˜ /"ÿ/™*¿SµàÕ¿©ÜÚ±—g8#MÚµ5±uä/ ÅU_õ×£@ þñV[ŸÑV/œN~}ôiS?Ÿòh p–±¬æ†Þ}P‘øëË:•M€5ähP¨ˆò.OüfFÄ«ƒ™qÁP )*®ôÒ÷`df„žÕB ˆ¯úe§¤w7®¼[|InQéˆ:åì®;Ã7¸°=þ½ýò3Óº… ¤ŸÝ~Sj€æ_ÒE.JO“A'#zŸu#žg<;ûËêo¤\ªg#>÷÷3c¹þúbÛßâµÏÿ¨æc0Æp8„&í«b/ÏÓéCev½É Nä¿6 7Œ9Ÿ?@õˆ3`­áø•õ¿^Z3MÈUyWÖ·1U~§ê‰ìîŠ ëZ]¡Ïdþ¼ s"ÿ\Rù‚9g¢ÑáÎ…sD_rÕ­@ þñ–õhËßBöí'6õÛ×– ,=Äë4¨,IÈnºmdKZñUáÄ”É!fKŽw²b|A/£ §57Æ@ùâÊÝBc·þ-<ÖWˆãzx´‚ߎ]Êœ\üìY˜pòjÍÎCÄ@/6F ˆ/æÛ…:IÈäÉ‹þx˜^HofÝÂDx~ÏØaûŸÈ¿t‘žêÑÆßN>Ê#€Ö¢‹]'+.N|+å eOÇl}&%@ñöVhB¡Ak æ?ó¾êÖ)Œø‚⪣ž‹%½½ ºŽÿ ª’Àâ¶ñ<Üîn‚IÛi¹*|û þþt üä0cÏ}¸°¬ü¡»ðºg)e±Fî£x^Ý=6PÕU4 ß1êãc4c,¸‡Ðk ]Bhñ Kø½„¾ì[¹úô#nîWOÞ¤úÞÕ«£\]Çws:9 §í¥I‰ÊÔçm&cÑPaÏ®B¯et ÊŽÿ jQ-jLE7X˜’n;ÍúMJ{>@µ2–H?ûƒ`N,F€‚¶¤?/8’ã¿‚ªÔS®òúº sOâªj âÿf{ß‘ïŽo‹7÷õi[)СµžøÛff»ù¬>v%úÚ™Í?ôŸxâ1ÝÞVøewgyØ´éûî¿Ïÿ”xb®ßöÏÎkV8}¥8£Ãì #ùæ¸û®;uaß¼C}j¿hÃ34‰@ _C¦ôôqoùîI^¿µî;÷dø†ÑM¿pá^—U?ò µœ“‹™ÚßswçLý«Åñ¦¾§ðÖóVn?D ôf½Æü·un.ºˆ Ä+ÜúÎaÙ­9~¤Vø)GÅ43P@të³5ŽA18øë7 æ{ê^…Ð,Æ(Íñ’u2onR3â©þ, ”˜Ù+ÌÊSŸDÍiN—:«º=§½Ê&¯çê'¶©?®µ®pÀªiRôÖÀélŒCköJAy} oæ­kFÌJ;ʆ!Ò¼—N3•?:EÓßC;o¢¾âÊAjÕń؛[¸ÕXy+&`LbäPrV®íUf@ýpÚkÌH At6Ã^)Jæ±j5'])¼? r—ËšåÉ,ëQ½Œ¢rŒ4¢íÌÄ"==Œjé£4¦Ð ó.n6TÑ’ 8S³=\ûèAUW‚†Ú*ñ¿„ÑaM¹¦ìNc¿#‹ÜQvP8òvù>¸ «a—†VS0ê{VVa2ÞxÔ {çR[ÎO"ç€Ç% °öçÃ5˜Q›xiªšÃÃÖ£6EŒÚTý€Q¿P9Zû@ (Üú"ˆ‚””ö6MÍÍùú¼Œ»¡'6n¹‹¢­÷Ww_ÏgBL21vY¶Ü„4€u@QØd‚  Î£(9ZÛ*“s7÷TõÏåFGÓ3Eë<7…3J&gªkÒ…Zyꦬ/€•˜QED¬æ^4=ú4šÜE`ûÙò)ÞdÛìÊ ¨‘"@üPk7Œ$Ñ€ø?ª"±oÖ£®·ê_¾|‰ãøìÙ³÷îÝ‹<ø+QÜ–¢¢¢7@|PÿÝwÄwŒHd‚œð÷'--9@ âon!¨@ @üÐVø—¡y¾F„aFë½7½lïeüì&†q¼£e;ƒÕ‰?Yb†¹ž/@µ‡@ ÿ,êšÝ 8 Ñ()*›ÍÛ¹s"r@ ¾'ô÷–. :ÊG¾@ ÄJ-ÖÁþý§\»vðÒ¥W®ìºsç´Ÿßjä,Dµ® ~x¨pÆ JÙÞš¶‡'>ÚÈyMþµªr1×^$ |Vò§,–ç<«5C>-p!ëM…Þu¯Ùƒû±“Õ%j_²zpR4 ¿SƒmÕ“ÿWÑe°ñOBrhæ¶$UÍ—#Uú¥uc{Zp0 £Úö›ýÇc©¡¦+PÞÝíãz4ga†a4Óƒ—„ehJj2×i/À0 ã¶ê3÷øäÅ?YÙÍÑF†uøyûô®< Ã(†aÖxú=E‰jÖaG †a-=ViR6÷±éСýø¨Ââ ïïšäØ‚…aF7ïâ³áÆ'}=Ù€&óâÏÖ Ã¶£·] ÄÄ0¬ËÞL}uc,§ÄÕU|¥xº†aL¯kEÅ?˜eŽaÞïr!(â&š`Öfñé½ÓšÒ1ŒÒ¨ëøÝ ¥‰ët@ü­´´„çw`%À€U»‚AnWp€f1ã¶8]RåsÄ×¼aO‚ñœƒ%âô¤Åv,F‡€8‰8]ŠçÕÝØãWúé ¬”¬ùKé9D=+8þ+¨Eé+F ÜÜ®ž¼¨çË’«)W×ñÝœ„NNÂi{iR@‹_ü…ïè tn4o`eb…|7PU*‘ËFüqŸhõÃD[VåËØ“_û8Î?vÿ½šn܈­Ë~º¼ß_ŸªêÌŽ”Æþè2bóõt%0³ßŸ[]sü†ôV´¬ßcNàCÒ½;v7#ŠðX 䉩RžÄ`½Ü‰ŠAkîmf²¥Öµ£Ö» íh ¥¬Ç^Uí.F–'!–ßÌ¿º\;f™Ê¦jçF­)^1ayw,Yt×aëuyÏËe'¹É¢oIÃWë3Á Òä†Hn`ªöðÍ‚¸k2ÑenàJv$g»D}æNÁµßu9YÅS@¥ù.Ñëž°.3T¡1âÛ2‡$æ…·H?“Ó^<¡Rœf2ç…\(œÉ¼^iúHÿá۩Ķâ:€ìËU²®PÀ%zf}¡–bÞJïÇÊmÅìb3ªó>^³1Dªåð½½&r7þI˱Ñõ2#ëòðRm__CJUF‚â#ÑBÓÛ0i,'0Y{0ª 6\E e_9_]ø[Ó~Þþ9Íñ²…>U ó£~^%h6!øµÒ Ï½Ø› ð|Óô=/´•ÎTüç|œ åÏáq·¢o?}q}åäKgÚ3ô$hR÷.>• `9?öcÖ[Ifð0(®-ßüHYá*ÚkÇ •^žyvÆøùØ9áGŸÈôYWþŒ›iã*ßà Ÿ//ýíÃeWŠ47'?mŸ3 ýBP²²ŽìIøªƒï¨=7%æH>æ¾;ãÇ®a¢®Ô˜3Ã¨Ñ ,~0ûJIßJx¿Ì@}kíÖÇʺ܅@ ÿŠpK£QåAw€*ó6½AÏÑå ýcóúý»Î¿}êåë÷mÞþïñÛ­ÈÅ“]/CŽ/wjLZSé‡nE¦IÄé¯E]°)jS/0óK§ëÉãwšñëÜU³;ò(ÕNþcdëÆ=kÒ¬¶ÝøùŽ4^ŸîµtMàŽùÃæ“¥ãøÎ‚bÓ%1‡6QLF\¸›.‰ÞéÂzëˆÓ%Gü›}GBR þúôPj>%…Ð,ÆhË9s“šÏö÷ôñ4:ôûð ³òÔçÆPs^ѥΪn¹´WÙ´ë¹z6ÔJL0µÕ(m#:éæmH9NÏ* h)ÕÔ(z®µ®ðZr§[ªÂ’Kì=¿¢jS¡ÇbÞK§ùƒ?z'8[7o¢¾tä{s ·«iÅŠ1r(ù,·ðÕYЀa¥Ó±¤;P–/ÏI±gÜ8ÊÚð+ûögL®!€ÕJ×–8ß lDtm€“M¸˜LS©+A­°˜ðÂT‚VSÖÚú XN{ƒIáͰÜÒZŠ €“®¿Þ¿[¸Ò‡ ¿dÍòäí{Q‡4¢fbö,Œjé£3¦dÞÅ͆j[2o¤Ù.où¤âï fä¸j¯¯@viÞÊÛãÅÓSÑ2Šóo›|[³0ÜÄqÑŽYÍ åìõ¬JM›Ñ¼k ÈØÜ“ß´Ë€‰›žÙL^¶ráÈF‘#Žx Mx6•x/1tÞðéFtfyÈÒmšˆ‰s̱LÜf{ñr/y$Ó¿¿tøt™1ºuåÛ±2õÊC-Õy{Œ6S¯|’éTÉ[z0êÈNõòZ@uûiJg.4‹a?OjQÝ!eÆI -~Ãq޳¼[Ò0#» ?´€Owî~ÔÕî.ÔÄ¿#Ü¢P¨T* r«˜˜£Riu‰ÔïbÏ­›µ4`ÞH¥…óœ¥sí˜ÌNÓ‚ŽOì×úsèúÍ+Ö_ÏnÂO<šòøSOÛöRYq$¬ÊÉÍE¦”4k»²7²³1 ’n>‘’@*^_¿/`µ·g<>rOL'¿N| ¿»Ÿ ò®®·zêØ]ÓL<=à`A‡Q‹wM·*¹¯5é;ÜŽþ95)|ÿÝê +n̬AÄ4õüS Ð:ö³d ;èFà8zµâštp±H¹—møžÚß^Ýý=SœI;›LŒu&Ëši뀢ë1Ò˜iTDáÁñ„ÀFÛ*“s7÷ÔlŒGGÓ3Eë «dôYô §ØÓ@AO{á°-e*3¬ô™¢êj4œA~Õ š4ꡈˆ-šïF~8Ï6…Ysç„âw}“¥¥4³²|³/s‡Îcd↞#UC,KÒK€‘_X—fÝð–gZG1Kï1I${Õ9œä=«m):> ;‡«êô0&ж¦†Ý£O&| ”ÒJ)ëµfâ¹êˆ¿7¸é Í[û2²ÿœµõi…mÚ|IZ[•ü¶hæ6æ凌Ã-›Cvûˆh†OO£Nn]<¦·¥¹Ûºø"’(”|<8¨m‹-ZXÚNº¥€œ×9ekYMš•f€ œF™ä]>òàù¥#O0‡Y#,«Œ1EŸdÀ2æ•Ü®h`¨®¶¶‚ ˜Ó5ôn%oâïj$?>EéØžP Óa:’líN¼ ¡¿õP[iñ‹W0›åDk"+ŒþÑSmö‰vî9Vy4{Gø-œH2X‡Þí×ÇÑ5dMÿ«í¤j1óuÐ’oF‰Íia)÷·7`Ÿ©QqX_¢-³.f°÷Ñ®cƒreéÖh–½‰ÏÇèï}Ô-ä´µ“™­Ç’Ê5îL¢kÄßþ6ØÂ÷šÝ6KŸfÝ|PákºI3@‘ôM¦ÌàÌ¡€öSÊ'cKaå['fd7çìË©Ÿ’b""£n\¿z;£èÎêŧƸšrÌ|wîñkQ6ü€1[t(7¤sèå¿n÷c›Þù!rû¶ÇO¨nCšUÝÆHe$rp)@Ê’‚Ž'0ÛwïíÀ¯=;üi#.€¬è½DIÐ}~õ¹š/Ê©·øz•¶øªGÈs«ïm¤Ìx•Oô7Ât¹orxMxTÀ˜µ¸ëö„&è] â;¤ê¥mçÎIÖÖ½Œ>šÀ\€K/Ö쑨»©©Ec„Íü~;³yL_fÒ©ßví½«.éã ôD]ÁPYŸ¹êÉ”¦5hÖ‘›”š@íÜ׎°[õëÅP¥&|ÒÊS‚/æ»ÇÔY~ŽLxs*2MS’Ý»ý <üú:ùõuòëÿãEÕÖ`sYþÓð§­úŒŸnÏzqéàÇÚæ}'MèĄע?ê¾·VDv­ËûŽÑóÊ» ¤éùêB¡sîÓ>òåŽ$F!{ô3d3uv&Ф—ž­4 èb(O¡Å/^À\&—,T é:C ×X E5¨Ul|¦:{’éë×ÀÍÜÉöãT­®ðú¸ @±¬5cR_¡ûî‹ò•†Æƒ 1}\„æ0.IÇ+|‚Ú4„7ЋÿÃfŠm;øCùêñÙÆ^U²þëJÕb–uðØÝW:Y›tôä$öPlnh\§‡€ßMݹb;JWZˤ°b¥ cr?¡û6C1eb= ÄßFû9YUù’m;Ò™`ˆ[ºäLš’ÔçÞÛ¾`tãѬb¸¥}}p¤m»çajŸ)ËvžŽº´¬ ¹–ÒØ¡o øœRغÿ°áÃÜ=ؾfÓóÏzÙÍ£T¼³:Mþ¡%@Þµ“)LÙžÕ· bÛxu§À½M»ïå¤òùŸ“çNóº*A]Gv¬öƒì(†;ë¶ÞÎ#HÙÓKHªß…ËŒ©§ø8Ç„†W·Rå$èÞGüy»†­.7®¹ø^ Ú÷á›ÿÌ€fîNM µ»‹ƒ49òìéÓ¡·?èP³D ß 5w‡†_žSðNÏÖbzŒMðE¢îÞû÷Ϫ˜V8|ÿãýô1‹ºúß—­ýÒÓóš~:³~I8ÙgíŠÚêbÆš'r-Ò¯¹úyÐæËÏ Ë>ƒ™‡§ßžx‡'+ãÈü½7Ÿ?¢|InàqÑÑ'«{âŸÎsØ‘¬þ[·‰º"bxùò%Žã³gÏÞ»wïwÿ )¸Ï:-ÓNó p)m©«gP‘·ºn|3‚(nKQQQȈÿšçklm×¾—ÐüÛ#…`È¿6ÑzàÉ\`¸þ)¬¿€&u‡G×…w*]°)Wˆã~íYé9#í«}lÞÓ¥i—Þ"úÇGñ¯Àìs(5jšda3አÀ¨eséó´€Fþ—RNx™*nŽnÚïœ\0.öýI×òi~íË]Ú-K£‘™ç 1uâOÖ]·¾+6˜þxU/ûuÿ)½‰“V>yøkzíÙa…wævvÝ›YžŠ  è²÷mB€¥²º1u_;©•û±\à[´ÀÞ¿S(:©Ò÷Rþ /¾"nb —㥯¤c %€Õÿ”È©V†:Ü¥¯XL!j§âû æ}'.^ܰÿãÇ×2Y•J75µ45µøý÷ uJé^Û{ºïŠ1~Ë÷u½§/ ƒ”‰ü'ëÖ.>fÕ/P½½v Wúüà®Gv‹í'ïÜêuv¶ÇŽr‰ê'oýS|ºWUÍÚMÈ:5aŠê—¹³|Ü'/À@ññaоë.¾Ph^E†¼·¨ ñç£> |²ßŠvÕO^>+–ù¨?¥\^Ÿ_=ê +R£ˆÉ­iÐV91×?¨óáIг+äÝŒy£þ®Hdò/ø™°/ýW˜“ÉtÙh0iiyÈ ˆãþp~üµ 3ÚÏ¿šÔbÓªMG.?ÌR‚Që>–n]?©KÕ=èmçEÄs—.Þ|+ñÎG£V.Ó~Ú¶yŠš;ó[¾à×£ÑiÏeÀyL]·'pˆií‹æè­ýfv[6ï1è-¬qH”Ómõ­8á‹¶†<ü¨ºy·ó7ï\Ü…]wv|—­1!ÔÉ‹÷ß~¯kÔcê†Íw ]ý\FÍã®ußÈeÛÅMEÓׇ¥æÓú/:½¢å6×9ª\9‡;ÒñäO[o¾Çu¿öÝ“­¨P‡»Ðî:â{-öATÍn!þg Ù-âta/z°}õ™~Së~'ô6à yãDCNüo½?åÆý¶™•Ìn•Í"Ä¿*r@ ß7ž¾sKÀÚÕ+ÚZ±¤¯ß|Ö˜žÖ•ƒ¼ƒ@ ÿMÐ.@@|ï0»þ}n‰Wç&,õÇW/Þ|&Û Xt:öwZå‚@ ÿoáV@À)S¶O›¶kþücÈSˆÐ㇇ gÜ(ߎO“ÂöðàÄG9O£É¿VU.æÚ‹„Ï|þ#çi4I,ÏyV ™•×rþ§;FÎÓhEù´À…uíj¨}Éî+2iokÒ±£IG[㎎ü_¯SˆoáBCµ¬u,O‘‰¨ìcËÈo˜A9=ÒXT1­Èxúm¬6ÔZA$öò g¤£I»¶&¶Žüe¡¸ê ÷[¬G@ü¿\­GývééG%I’$IêóR¯nõ³fÿ7‚-Žó±<’$h%!@@m‹ ¦¥=¼ví`ñVææ­ýüVŸ9³ù Q¹ùŒ1¡IûjŠŸè~yžN*³ëMu"Ù_§IR‚iMÜÈ'i ¶èòD»»"Èšd§64‡Šçÿ›QÈLx ¾uô–êó—6 À>]ãz²†9+:³þª ‰Bjõ¬iÍ4!Wå]¿T7Œ9Ÿ?@õˆ3`­áø•µ8ø©Åµљ̟wácNäŸkC*_0çL4:ܹpŽè B®ºõ@ þ=Pjеܾ}÷pºMbΜw’Q¯•QW®ì>|ò¢JldÞWÝ:…_*ê¹XÒÛË Kàø¯ * ,nÏÃMàî&˜´–«Â·âïOÇ€ÀO3ö܇ëË:Áº ×VP4PƒžãgªMî1ÅìÒ‚9± Ú’þ¼ ïðà…|w7¡£½pÀÌ÷:PgWlªêQмaÍñ¸: ü·ÐòˆJç+8þK§6°R^°æÿ ž&œN1€Šºfﬤ¦B@^¤Sª”‘ù#/Þ¤ ¼‰£ù?l¦å:ÊÕu|7'¡““pÚ^š”Y¬‘û(žWwc_é§‹³^JÏ©{²¬‘ Ôzw»Ôf2 öì*ôZF—”z è#}ÅH›»ÀÕ“w + §5¦¢,LI ·fý&¥=¿†’–H?[¹Ž‚#9þ+¨J=åz ¯¯«ÐÙI0÷$®ª¦€@ ño ·ÒÒžgÜ•gVì¹]A||x@ÀÒ³¨fÎãvD^L‘ˆÓ%âçOƒNmôhNûz;â$âtñý«Ò ÜC$âô’OÜã»{6Lé$Äk?Ÿç,§'-¶c5È$j˱QqzŒo ´·HeðF:üÜŠ@‘̸o¦м¸ßŽÄp×§kŽßÞŠ–õ{Ì |HºwÇî&bD+<1UJÀ“¬—;Q±Îso3“-µ®µÞMhGc(…tò5¤„Qe$(ž1-4v™¬Ë UhLø¶Ì!‰yám¥¥0²'5Õ‘¾‡ c£änpv&U[:C#¼—©lÚ©vþ¦2^ÿî=—åsæý&÷Æ"– æ0[‘ÈDdm<í¹p—ÊšUËx#dR"`_ѱ`¹û#ÎöD,û270U{øfAÜ5™è27ðF–'!–ßÌ¿º\;¦8ëZÓ ÍUÿáÛ©dAàˆ?q@]"KôÌúªI-żÿ”Þ•ÛŠÙ¥Ã2BÙIn²è[ÒðÕúŒûxYÌÆ©–Àô6öšÈÝø'-ÇF×ËŒ¬©¤¥,Õö­\G½M“Ær“µ£ bÃU´Pö•ó5ø @ âû¦jô0sæÞ¨¨CÐ`@Y/¸+ÀÈ•™•U²`‹Þg§ˆ ï®î?”,ç´êîć ýcûÞŸÇŒuð:ž¦ZSχ㮿‘ˆÓ?D^ÞíÝžƒ•L =Y÷ëÑàÿ|ˆ¸ôànº$æ'ÌØkÇK‰8q=ßiƯsWÍîÈ£­©ÇôC·"Ó$âô×¢þÙŠ^ƒl ¥!ÞG:¾cղἰœ6oXs¼“ø-f\ÞÔ Ì¼ÃÒÅ©Çz zþv+òEñäØËãËS«d¢Vøöêîï™âLÚÙdb¬3Yæ-ÒÖE×c¤11Ò¨ˆÂƒã ¶U&-ænî©¥ü· IDATØަgŠ4Ö&õYô §ØÓ@AO{á°-e*3ì5† t£­©a÷hç“ 2ç2wèLÉ^u'ùDÁªE[ŠŽOÄÂÎ᪚JZf@å:*ñYJb…™x®º&_!@ü«Â- …J¥Ò ·Ú‰9€é0*µ¸ƒD%Ç¥€…Ïâëii¡¡§¹s¤r˜¶SOû¡kú‘Ƀ§,è0jñ®éV%[“¾ÃíèŸS“Â÷ßUÓɯŸÂïîgGƒ¼+ÇŸË*dÅì4-èøÄ~­?‡®ß¼býõìæ"‹.5ÈÖÑWӤ߾üZ9·fÔ`Òvôê£i ß8uñ´m/•õ»Øsëf- ˜w RiáÎô(ÿÑ“‚®”Ð`\…ì}ô϶±‹4=ùð.Ž*ðQ.œ¨émJD¥é!JGsŸÐ^«4ø­{Xw¢ú‚P L‡éH€ö>Ú¼}ì§¶êîüš‹Km®^>Ž<È|«#«—‘ø|ŸžP¤Œ~º÷&Ú»oCèoµ@Êñ‹W0§J‹'+f]G€ÕºN‘¯KÞÄŸptôU®^¥iœMÉ/™š&ùf”Ø­œã)*¨?Q£â°&퉶5•´‚X¥:*Ʋ7ñ9‚þ^ †|ÚÚÉì7¶u* @|Tíyr¹Â¦MÛf_Ï€§v¥ßÇmà››·.þB—4n`AÀ¼á]lšq›vúC×>-µ½V¿óêkóŽz”ö¹{6e\€ÔC„ÐØ«.‘¯«µöãT­æðúœ$©Òm‰¬Cé»›âàBöŠ‚M9° çqŠ-à ¡ZI+À/¯#PÂ>Š•&÷c˜Á~¶|Š7Ù6»²úé ø·…[;wNòó[ý:â‘lnüà °D¢î¦¦Åa£v]š¿»ºaò• €ó:Œ8ùc/®u·¦´÷8Þí_0íÔ§â'žHuŽ‚QyÉ—<%øbÞÈI=¦Îâuf›S‘išŠ‡1 §¡/ݹŒ¨Q¶öRÑ­\·0¼¼•¦ÁœkLk_±³ÝÌï·3›;iâƒ7þö†ÿª®8j_ÓVq;­¼B¸.²8€þË‹ú/¯ÔÅo>¡0utRÜûO•jÓ/Š.¨ø§»üÞÃ’Ç£’—O±mUÇbU¥§gZœ.Î tµ­€K¥ó‹®ˆÊ_Qô½¥>eh~éXiê…n­¼z½üOЉfV[¥Œrf[Õ¡?*laO!¯)¼¦‚œ[Qœ[isçè˲.oP]‰­æbZ]"U`Ù+n_†êÕ¸ÁïlAEŸ¬¿ ­!=F¶÷Sœ÷«ò 3T­Í*•×QyXQØ¿BÙªµ@ ˆY¸gά>|Q||xŽï;=[‹é16Á‰º;8xÿþ{qO™"t]pi÷áÅØ/ °6t€Ü„ØLÅÛÈÛfû¶˜+–ù¨?¥\^Ÿ_}c?Í«È×ãµ"þ|Ô‡ªïßQ&ðŸ¬[»tø˜U¿P@õöÚ÷ñáÕek ‰Ì‡ÎŸ`(LüÇ­'R•$@M&éoîzd·Ø~òέ^gg÷ÿmïé¾+Æø-ß×õ~œmDˆzÌ@ ˆ;jµ¤R©J¥R«¥L¦€Åb1Ki zzQ’¬g ƒ—/_â8>{öì½{÷"w!þ A·¥¨¨(ä @ “(K"ÉÈÈÈPKÕÒ‚‚µT--PƒT,&@ 6iÒ²}—.-[¶¬W ½µ@ @ ¤Ÿ>eH2 ÔjµZ] `²@­•J­uJ!ÉHL}Ò¥KWGGGÀ¼AôJ)Ä_@*œqƒR6¦Ia{xp⣜§Ñä_1–ðŽK;á¶ä’IWÝköì5íæ`ȧ.d½ÑüŽ‘ó4š\ƒ+l×IðóN^C²®šü/ø@—Áò™ˆÊ>6¼ˆü†¥$(§G‹*¦O¿U7©#IìåÎHG“vmMlùËBqÕî°þ×€@ ñOG­V§¦¦fH2€Åb2 L”¬T Ô VƒŠÿ«*Šcb>œ˜x¿xÙaÔ5»p@£QR(T6›·sçDTˆjÍÇ0`ŒápMÚW#Ä^ž§Ó‡Êìz“AHö×i”?–²‡*ÛÕ¹ –(¤&¼_ØÝAÖ$3Ÿ-ÑŠ—wJVQýYWIÎþkn 5Ó„\•wýÒÍ5pØóùcT8ÖŽ_PYQ‹ƒŸª'Öm¤>“ùó.|̉üsmHå æœ‰F‡;Î}AÈõMœ€@ ñµ™L&“)(žÑR1Õ’‚OŸ$ª©T­V«UÀd²Jº¨¥=U‰$ã|P££d·wÊ”Z­ƒýûO¹víà¥K;®\ÙuçÎi?¿Õ¨Õ Íûª[§0â‹wWQÏÅ’Þ^]ÇUI`qÛxnw7Á¤í´\¾}:~r˜±ç>\XÖ þÐ]xÅé+f[õxcÙ‰J_‚ŽrußÍIèä$œ¶—&ÕàaX)/Xó—Ò3ã9þ?1·-f?Í¢ÿ<•ý Žã¿‚ªÔS®òúº sOâJ¼ïî&t´ø‘ù^U9ù ª²Š>ò;F}|ŒfŒå÷z-¡KФ¯)ps¸zò$Õ÷ŠÞj‚²X#÷Q<¯îƨêúܪÍd,*ìÙU赌.!@™Àñ_A-ªÅB©è S’Àm§Y¿IiϨâüŠÒÏþ ˜‹‘  -éÏ Ž¬Áiªj @ ß+©Å]Q¡PÀ,((ˆ‰ÇÄĤI$Ÿ222$’©º@-UЧ»¤jµX“ššØÐp+ àÀíÛAÑw§Û$æÌy'õ:QuåÊ®ááš@To¤ó·ÁÏ= ÉŒûfêÍ‹¬ †»>]sü†ôV´¬ßcNàCÒ½;v7#ŠðX 䉩RžÄ`½Ü zEEªÁ'PM; ~[¾Köen`ªöðÍ‚¸k2Ñenà#Èe*›vªµ¦†~æ¥]KõÎÃÊNLÀ¤±œÀdíÁ¨‚Øp-”}í&ë2CS ¾-sHb^È2xWLXUý P 1L>Xx1\Ö+‘}á-–ÊNr“Eß’†¯ÖgÜÇ+†Lú ßN% Gü‰ëªü#Ë“Ëoæ_]¢¯w#µóþSz?Vn+f_(ñC­0Dªåð½½&r7þI˱Ñõ2#«:ÿn–jûúR¨2ωšÞ&58íÊùê @ ß!Ÿ>}’JŠgÔ’D±8,,,55Q*•fd$ŠÅ1OÄ©âŒTñ±8QùD,NÍȪURµ Ô*P«¤Ò‚°°óR©´AáVZZÂóŒ;°à À*€]Á ·+ˆ8Pr’À=D"NÏÚч@1&§güæÌmp±xÎÁqzÒb»êK°ŠÅK>qïîÙ0¥“ÿ*©².i‡€8‰8]žF‡Þ`^aVžúÜjÎ+ºÔYÕ-—ö*›v=WïѦª$½¥zý$8°‚ñ¾d³ìÍ-Üj¬¦(FÄÈ¡ä³8\[§Q™wq³¡Ú–LÀi¶‡Ë¼‡(öŒƒGY~eßþŒÉ5U‡õÙ"ÝÐZ€å*Hó^:Íüѳ8ÁÙºyõÛµ™&äY^ZZ^ZZÞ…©­ƒ¹Öº<ÀðmN{ƒIáͰ\E‰µµ€“®¿Þ¿[¸Ò‡ ¿dÍòäí{A©æ|о‚ÆQ 3±{FµôÑSª;MÞòI @ ßjµ:#%EjuJ*U‹cÄ‘‘‰$#55õ‰Ø±IFä uL Z¼\-^®Ž\¬^4XÝ^ð)åIbFJ*¨ÔêÒOFJjdäùêúUŽ™3÷FE‚>‹Ê&º,œQ™YY©ÿòK.l»ôžgí9ÞÍ/ð€ƒå¬¡«“d_;À®—Üù}U/?)õ¿5|{u÷õ,q&Ä$cWeW&+5M«u×Bb‹ÇâŽZÒ¡.”FëÉ+§»»¶R's1þpª’!Ö¬#»èØVŒ•æ—Ål¥¢cJÏr!Ïco¡±êuIZœÝЩ•´M{+ê÷–V> JK'¬a>ìWzU@Þ¤«ôõ1ÎëBÈs‚ÎSƤJ¨"ªè+áwÃŽÒ8o !KW\ᬺ?HQspþ¬±µÙ‰Ê@•]½bþœòY²ž­×}Fn3$ßý î{V¥ÙþÕ ÃoE}¥r ÖõM*‡î³Ä20ïØ¡·(=#i³ _¦©(™Å`ÉÓÍÊxV•6šò+“*s ‚ ‚øõ1 #Œ2Œ8“ÉÈÌÌà‡—Ì1(ÅØÁÉ£a·7Ñv«qW40кZLÉ_ ` Î`ø|~¹M”Ýb±Øl¶">V(K*¨"ŠÍ.{‚[¡ÝÞ¸ò9rͦøù ÖO»µÙí&Õ×u¶³ÇaIòØx7¯I-s®ùNÛ/T44·kœæqÀ>`/7bíÌ1‰q⯷BàÆ¹„)Í›6îÔD餯˜#Gi…y9»=ç:,ò™:×Kè<Ùã€ÓÑ/YåË&^?±ÊS¦ØÐañ”>ÓM´<ìNÿyË?‹Þàt8*Qÿ2|#×î•»æ‰ê,{- ä ûçzÚÈ)Úu—½O*2­ ½öe/v/3Ù×|¸&âõã8Ã.@]ǼÅOUÇuãBAÎsÊ]o-g3Erõ!CåG¦UR-»¼e÷Õœ»s¥”ÌÂ5w±1gö4õÞ!²:õ%&-œÊb5)½º¼bþÔryÊŒFŠOS·ó“³Yò. rZVyV%Þú»üµ(·ÍÛ5KyéLÍõ©”¥Fæmì/«‹ _¶m™6YjÒAEêò*k´ñNòfïËæ@öt‚ ‚ þsñVf†HÌ0 éwïD4É0Ba¦žüùТ±) u £££.D2Ö† ÉÅ@3x‡‹ô è⸡0†ašþr¤XþÊÍíÀÍ›Qù¡L?-•PÙ¦9`À¼#G–€f×c1«-ñ1tÏ%a(•¦Žk¯ÅÜÓÊ=Ííèùiz¯Öíí•H5{ñ֤ƉGÆni¶{[;²"ž=|¼?øV¶U@ÜúöN;µßô¸\°Uœ¹ôþã™áÙ€bƒ©áóš„ ài³÷ ýÒi¥7æ™Obí*[³í°Q£û¶nfP[§¡®–BQøð>S^÷»ÀŸÆ‹ñìb,Å`øç׉äÚŠÇõòj†1âââ\]]½½½Is‡T*-Þ—BCCIkAñöîÝ»Sþû‹D™B¡Í€ÆÄb 4Mkeffïü]a¦ 0~b2u Ìiàós·´ô47mÚ§©ù劯ò£[žžã†õH¸ð0gzæ€÷€?àÏR[Û ì±’àÄ&Ÿðl°túXk¯Uú]öéÐd©gݡŽr´03ïèdÕÑ©¯¶£Ã³¶§‘mߦ€,îZ|Õ‰ qç̉GÞJr&5¥’+Ôº.pC낈€µëž+p_ÚYìIÿ¯6i„ßD|<#&‚ â?%#C”Éd@ ™™4h4h##£â˜ “ɈEÂ3ÝÌ¿FÀP 2ÅX€ðÚØÜ ÃÐtq°E™"‘¨ªp @`àŠþýçDD§I”(RJYªÁãYZ[;mÝ:ºº2¾¾tûÝ´ÁM'»NI»Íê5¤ z3aÙ4ݨÿý!gm<¼5¤•9(È:fŽ6Ü/î¿È*ÿ\…¸Œ3UiÚmDW#àåvÏs"1Bn$»ià‹ÅVVV÷ôK>¢¹î‹ â›°µ[·ª›º)ÿùö±ó¨:úO3Qú²Tò&p™ßÇ& WôÕaU\Gš÷1*µU>‡L,®¦ …”´<) ð­Éj\T‚ ˆÿš¦õtõD¢wåb­O3e@Ì0KÐǦdéRˆJ.¤µ¸4­©EÓ´(&¦dš .mTê2ªÂ-—]ññ÷/]ÚU9º¹Òþ%ÝÜê¾}åæ»©ä`ADiòœûþ»s‡úEžÓV½Âå}9OCbд·Î×¢"E;ûúwüî¤_(ÈÄ N¤mš(O2‚ ˆß“®®nŸ>i.ÍPv~ C###s3#c3Cc#CC#Cc3-==†a„BaŒPXk1ŒWküx×’H¬êp«  /;; –@¹¡˜Ž¨eg§W_^JÉpÀ,ÿûá_ <¸º§!MA©žÃ Ïk/ù_pæÄJ‡9ç×· ãtZÀ9h¥J©˜L\ψ®Þº&ñ£¼LÕ ‡‡Šø‚ÈU+<ë?µ©jÅœ9F“®‹ø‚¾:,”~Í.^7nßå "þˈuc¹¥È”MG¹Ù("3l’ã¢åmsë7ã ¬V£fv2èå] ßÝSUËq[œˆµÃBŠú='ï»öJÄ$‡œÛîd¤B±K—0ùÂÙ{·+®¨®ÑzòÊéî®­ÔY@%™¨vÞsU ºq°·Ū=àÌmè²§­ 8Mfßã DûGÔc‘ïAÄRaŒ—óšhnß,ùø¶õŸl=ú<·8**HŒxY¨Ô¼]ýr' b}Fõè3+,Mn›é‹mÅ'†öŸw $ìÄú‘%ðf¬è­Íú®dA¿+333;;š« ºäŸ/0Ä 3¢ ‘H”Qjîx®–æˆñÃÍÌÌ*͹üµà,›ÍVÄÇ SAQl¶b™… íöÆñË%äšMñó¬Ÿvk³ÛMª¯ëlgÃ’ä±ñn^“Zæ\ó¶_¨hhn×87Ìã€}À8^nÄÚ™'bã`1uïÊn:…qÇÖ…$62¯q©«õºõ§R>ĽÌ5žäç3¨lÎo«nºœû;7·ñ—‹­ûV‡ð¾'%kìƒ IDATK†˜ê˜×¤O®FfÊÈóÂîæŽuR5²Prß'Ï®k‡¡­5.?·jªˆ´ó‡^HL&98J+ÌËÙí9×a‘ÏÔ¹^‚¨~A¥Jó$Øe¸Î¶ÜŠ9hö¹4te™Œ8õ¸ÀÁºU÷†Üûu´e*6ŽUã mÑa·Þ“ß?‚ ˆÿf´%8ã îðÊ…¥—[ÔþóOU 0õåh÷¯[þZtiæ³ËW. aä€bãI§¯æ¹¸®uî»Êíï]ÙNå{“Aü¶èáÃG™‡„œFG¡º8Ìâ–DZŸþ”ŠÆh˜››?ÞÌÌúkù–·TUµôõ›½â1`ú¹cAY¦¡«Û¤lò¡{. @©4ýs\{-àö¶Õ^íöÞu<‘ŠÔîo?©q/ÛzÑy…€Z×a3•Ÿ=|pÒ›ÿ,[šðò“„ó‹[,l¯¼Ù¾ÒÃSÈéÚÎm°þ—Äx:ØK”pŒ—­˜s£ÓUF#ÉÁg¯'^)ì3qS—V¹'sʼ/—Wòë’qêRF×¶m²ÛptñèS‰áìnØÏ8`_’¬‘•¾RPéªæ-Ø–]QŒ/UQ4t¬$“‚#—"%ÖÖí-Û7kËN {¬kߥs«ëÍÁÙ»"r "AÄ„b“ÙÏä³?ÿÉi±$V¾¤ŠôjÝOæV)·ßùN¾óÓ_ µ;Î=öd¬ò¢Aü—™™™‘¡axxxHø© Q&Cƒf_cÈ€á¢ä@SOoà@§>}–{ÐV5ᖧ縡C=.<Ì™ž†y@'à=àø€Ç³ÔÖ6(“Z*8±É'<,>V%áÖ§0Fö)œYêY÷Áò¡£-ÌÌ;:Yutê«íèð¬bi$…Òj‡rÊæü¹Š _ŸÑ–RäÒ @A¡äË:ÒObÒÑ´V›n¦wnfɡܸ{{U@óà]anR@PÚÀqí&LUoCãÕ‘øЧÀ¸sæÄ#ïŠ# 9“š§Ô°Ì–r£+¬X¦ Te™ä¤«?†µ…ݨIºÜØ}»|Ûvñí6n4M#åÒåòè‚ ‚ ‚ø?…\šš}œœŒÌÌ¢¢îÆÄD‹Dï22DÈdš[<¤¥©«§§§gndffcchhXm†•L,¸¢ÿ9Á©C%Ê…”„R–jðx–ÖÖN[·Ž®.ÃÂ×—n¿›6¸éd×)i·Y½†4RBïq&,›¦â¿?䬇נ†´2Yb@ÇÌÁцû":ö^L®›~“i‹–Ó‘òv­9_ëªÎùVÂG^c'g*ÅØ¶^¹•´[÷ÄÐöÎíYÈ ¿ø"ÿË;ùQ~žw{­°î¹'HÑï’H«Ë€ú?÷ó¼“ ÚÿbÒ¸‘V£LØ#gÞAú:äF²ëÎSÆ¥¾÷žcر»m–÷؃å¶ÇTX±tVšÉHØ»A/aṈ̃Þl¹s7-JêÑÝx* ÑAAAü_‡R™™™"‘H(‚aèêéé–ΪtzŒjÂ-AA›]\v¦¤$?æX[»¡¶¶A b-Èô3Sq;³·tBᇇ‡W-Zû<Ï>]ÇyÂfg¤>½¼féî'¥»¼šÎµpöÜäxܵëÏٻ묙Ðf¸[ýçO¤$Ryõ9Gç$oðé»Ã¥åˆe£ÞH/ÊL³¤e2jeW}µ¢¤kû,¸“Qzì¬(éÈèñâ…Ó§îê<“B^Ê}Ÿµ«‚b(xr,a䜦Fœ M–ÈÜ9b|¡û<ÇÁK˜wÑçV§W¼Ð¯âŠe«PI&EŠ®]‰G3RÃÃ’™´¼à'°2GÚÕðW ÙÛ ‚ ‚ âß¡©©©©©YñiZ5÷ÓÉi¹Œïž&‚m8ü/ƳKñ]UDUz?AAAß•ü,¡èú¹Zh²Yæëð­^‹/f)ù‚ ‚ ‚øuQ¤ ˆrÈèAAAüUn¹¸øä³XleeuOϱ¤±‚ ‚ ‚ jŽúJ µ+>þþ«W‘ÅSeèê6iÞ¼}`à Ò^¿2ºEAAÿT¸åââ{ãFÀ ÁMt,@T“µºww ÚLšŒ„[AAAÔ«â¢øø/„7± Ü/ ¹¦Á..¾%‰4»ñIÛìÔ€¥Óç´ˆ/®ë¤Zã-«w ñOæšrÿ­º³u:Ü-â Düý¬µ¯¯Hv ‚ ‚ ‚ þ™pkÊï„„‡°æ*Ÿ–š šþ&))æ?SsN‹1~Ç]þhÃ\Û¹{ÃFÿ 1 mz™ë°É.AAAÄ?näeg§Áà”}£#$*EÙÙéÕgI)˜å?\ â „gW÷4¤)(ÕsXáyí%_ â ΜXé0çüúötœN ø1­T)“‰«BãùÑÕ[wÂ"~”—©ºáðP_¹jå€çoý§6U­˜3ÇhÒu_ÒW‡…Ò¯ÙÅëÆíñ»|!Aın¬1·Ô¥“”š±@rÄi¿“{¶xÏ6ÜÚñP|!ªÉ0ÆgÏùsñ"~ìݵSÿ¾!äÂ+ÿQð$ uVµ (M«u×BbE|ˆ/ˆ;vhI‡ºìOÙ×1é|Ð[Qøž^(­Þ›¢Eü¨–jdþH‚ ‚ ‚ø„[,›ÍVÄÇ SAQlvÙ‹íÚíã Dü„'KM?-ãšMñól­ü`³Ûê-7•Ú9{^Ô²Éàå^“,k=ô6jÁ¬õ×SsÃ<ÄÈX;aîÄÍq°˜ºwe7'îØ:߀GÙe¶¢×­¿)çCÜ›\ãIr6ªîRÄœû;7.ôy!k`ë¾ÕÁàËà•<ûÙ­hƒ÷ðÃâãOžÜ5±«Jf®´ºSQͼvü?ŸÓ¨ó¼e™!'ϼ‚V»1ó{h±ªM c¯ŸX5u‘Ë ß|ƒNÓM7¥ËÔ1öiðŽðLЭê°5mÆXÒxr÷Ór/AAAü‚Ê_<§ªª¥¯ßì}˜Ï!”8e™†®n“²É?†î¹$,¥ÒôÏqíµ€cØÛVxµÛ{×ñD*R»¿ý¤Æ½lëEçj]‡ÍT~ö4òÁIoþ³liÀËO~Î,Vl±°½ðfûJO!§k;·Áú_6ãé`,QÂ1^r´bÎNWù0ääˆà³×¯ö™¸©K «ÆÜ‰9%ïÅûìá2£o[ãzªúæŒ1·3,l¿øZÕ –pa»ç±ô¨Ö#m:H"ŽzîˆÒ‘wwZj¨¡§¢P]*ÿM„ÀvôÐ‘Í jëhPÓUS@Zé:´qýù=ÆuÔ£•Bߊˆ9S@vS‚ ‚ ‚ø•Ýòô×¼y{µ”Ú˜œ2€X`9àÏR[Û Lj©àÄ&Ÿ «}6n»ò¦l>ò’ ¨x’;YêY÷ÁnO^yKñ::MuèÔ„–J•”FR(•UWâ²9¾~ťȥi¡äË:,µfõ/®qžÐ©i'Óî[îªÍÛê³k’¡\*ù< &•ÈQa†ÇÊ(Ôº.pðnô“#ë¼¼o3•gÍÄú%‚2q[3Éš-½¿+,±ˆì¥AAñKªdjˆÀÀýûωˆN’(Q.¤$”²TƒÇ³´¶vÚºutu¾¾tûÝ´ÁM'»NI»Íê5¤ z3aÙ4ݨÿý!gm<¼5¤•9(È:fŽ6Üѱ÷brÝô›L[´œŽ”·kÍÄ5ÊùVÂG^c'g*ÅØ¶^¹•´[÷ÄÐöÎíYÈ ¿ø"ÿK¸¥ÕyæYwÕûA×ïÅeP ¬9ÀÇ×ßIÕ>T•á߈lÕ ê(ÙBab޲ TH Nž|6ov+ӺȽ¾;ô£”ì¤AAñŸ ·mvqÙ™’’Pü˜cmí†ÚÚ5ˆµ ?ÒwÌLÅ5 ûÎÞÒ …^µhíó<ûtç ›i@’úôòš¥»Ÿ|”îòzh:×ÂÙs“ãq×®s¸'M@”CF·‚ ‚ ⇨jtËÅÅ·  ŸÅb++«{zŽ%EAAQsÔW­]ññ÷_½Š,ž*CW·IóæíWöúÑ-‚ ‚ ‚ø§Â-ß7^n¢` | š¬Õ½»sPÐfÒd$Ü"‚ ‚ ¢&XÅÇ?x!¼‰e@ àxÈ5͈ˆvqñ-I¤Ùõ˜ˆ/HÚf§,>§E|p]'ÕoY½S€ˆ/x2×”ûïÅš´¾ý¼Õg£® D|èÆÃ›^k'˜hVh¥–.·D|­ûi»®æm'¯œîîÚJP4oø’s±|èZØ:sÙƒ€,+êÀ¬¾­uiŠ¢(JI§¥½«oDºôo…‰š×¢(ªó©ŒÿO±åy±ÇçörØ^ý3jžò‹¼[ckSEµõ}[mKI߇.±7äREq8›úo<%£LøgAéÞl—"Ë|ì·ÀÉÂ@•¢(JA«içq›®$þ>’¢W[ZQEµÞ& ÿˆò£[,›ÍVÄÇ SAQl¶b™… íöÆñË%äšMñó¬Ÿvk³ÛMª¯ëlgÃ’ä±ñn^“Zæ\ó¶_¨hhn×87Ìã€}À8^nÄÚ™'bã`1uïÊn:…qÇÖ…$62¯q©«õºõ§R>ĽÌ5žäç3¨lÎo«®`Îýƒ[ŽøËÅÖ}«Cxߟy¬ mÖª ‰8u9ªzõµ¹,oï½üÒ‡|ÚnjÁ—CÚÄyÏæ¾ ŠN®?'h4d~.Ù‡ˆß^AôÆî– ~þê¦F_ö™z9\x÷þºöjÔ¯ZliÊá>&coJÖUw7cÍS~oQ_xM]sù5 X·Q½:ºuUXÿ÷ÖúÇëHéÞ`H’8¾Ó0¿Ä/±×«›çõ8sÕ÷aðä¦äd9ñÿUþG[UUK_¿€ǥhCP–ièê6)›ücèž#¾;Žì:pïÓéŽao[}àÕnï]Ç/ìZu\Ôëe[¯(¯Pë:læ4ÇNz)'½ùÏŦÈO~οý,¯~·ö:À›í+=÷3Š zwm¼õY¹tÛ‰#ÁÂB²DÞÃ-›Jà ‡$¦g§¿¾³c@m±›¿ÿ‘Ã0JÆóã_¼ˆñë©ñÿ(¶¼(7»fgpkžò{Ò^§ ‚â„ ÷7[ý ÷‹–«ãý,‚ ÝûìRŠ{†òK8fS÷\}–yq눦².¹¹œIÉ'Kü»á–§ç¸æÍÛ«¥ÔÆtà,ÄËðx–ÚÚeRK'6ùlXí³qÛ•7å~šK¾…Å“ÜÉRϺv;xòÊ[Š×Ñiê¬C§&´Tª¤4’BiµßÞ²9ÂVTøúIJ‘K+ÒBÉ—u¤Gg –ýíêæÞþkéܵw³kÐ^ q&##ûA”|™²ßçà6ëÚÍÌ@KM«¡ÍdOß¹#bÆ—üª¼ ^â`¤IQ¥ÚØnú¡ç¹Å_Æœ«ªQÕrþ–IæêE±(Š¢(ªî¤;y%™'í³aQÕ`Î#qAô;ã–-F…f€4í®×8›\Š¢(Ž®Ùà5WÞ}º®âk›«y±%o¼-M{ 1 ›s”„å (åÒŠ­u8EQ­köçºë©RT–²F(Müpf}Š¢Î8yd^OžEQJ¶3Ž €‚ËyjvY˜ó}5)­Q7r! ήne BQK³Yw×=2K:¥òMÚpüÉý¶EQ6Ûýåd¢EQ”’A—9§ã_žžoßD…¢(µæýÖÜJ+^½¦uüXö³¨¢¤@¥9·T©×?XÄ$^5qÀÔ­GâËÝ»õlõä¡ýÜö Dü§{;i•Ž.õ‡ ˆø‚¸}žË]Wî|.â ¢f¶¢?­ûi»¥ÿTë¼ü±ˆ/ˆX:ÚÁn̶S‚ÿâÌ„r‚ø&’¤CÝŠOŸpÛ]ìuìFlZa™ïƒFÖÐz ë°@¥×¾×Er¹<ûÊàÒÓ™Ò]·né¢@{ü͹\.—¼ñµ€ÆKŸ2rqäÜ`{2].Ï{äÞºx%N­:%ßAãeQùUn®æÅ–$ù9Ô/9)ÄѪÛrÊÍôèµfÅ'wt7®[<¬®:à|zQ…”Y5+@îÍ1µÀ|g¢$ÿ[½’6¨ÕÖa}3E ¬vŠ ã½{7­]ÜwQµë7¶^x?ëÕîžfm¹àv¦¬’&ív4úX§’?TMzîÝâó5át‹ž{4U€:Î7räò‚×ñcéÏ¢°ªòTQ5òÝ!Ò½ÿÝ{Ñ«m­@ÅéRÖ×ëVeÇ•v¢¦ÝfÍS~­ƒ•U×OJ?^[·dPG_ýsa[m}U$—$êA€®içî¶­jx 哽÷gòÕcn—ýûÏéÖmlÏž“FŽ\=kÖá2oW1<Åå ™"ê†@ļ :¾¾w.[·ß¢à¨pˆ/ÝŒ]1ÁRE©˜Îôº›ÄˆøžfÊì:ÝW켟Ì$Ÿ;"ñll¥V6ì©,g ,ÍËöD‰ø‚Ä3Çýö=/n%ß~ɈnÜ o]»üP¥ÜÀaÉúg·"¾@týÁ­žsÚk³« ·À®ÝmåÞ(_ <x2˜„[!—Ë ßž˜Ø¢Ì÷K©I¯yÇâòdr¹\ÎËÇwªu'óóÊ¥¬q* ·,KÚ!ÿá¬ú`ãÿAV>Üʹ:D«“Ÿ¨¤ÍÄ%©[o{UTI“– h¾‘[êOµ?¯äÈårùÇãÀ`öCqëXö³¨º<ÕT Ò½ÿàî=?IçK(S¹j:®šw›?¤ƒ­²Ÿ,iHv·“‹;ÍüûÓõ?‡[ÌóåÍJnÒ5í9j·ßä“ÎõgóÓÄ"Ý S?W M6 Ë|¾ÕkñÅ rAüñyQö‡ädQ·©ÃôõL_Iúó“Ëó‰Í^sL8hZ(Rvõi¶«Ôz© ©E¨_üš«WO­ägœÒìà2HûؾÔsûï½h»? ¬§hÈ.CÁ»ÜZê%“å(ª×*¾„CšUÅæŒ85.öì¦ån•gGzO»8àYNñ¢âÒV|UM Pi?¨RK™[‰]rò£B¢ÂtQ6Í& ?µ™¢®±.„Œ·™hWhÒÊZ\((*€Jíâù Ù…â-A^ã:~Kyô¾¡jAîýïw×>àC¬¨¦ô§ÅEIWÎ<©mÛÝTW‰ª®ãÒ¬q·ù#;ØÊûIYnZU½:tqZÅZ†µ”’ÁFãÙǶ¿øsö‰ø¢wCý‡úmš«n»2ìÜR+uòˆÄŸÆOnÄ{:öñüqùI„=õÈLÿ´Œ  -¢Úïz}{R€]ËÄÉux3÷—H¤ItµU@gˆçŽ¡ KaiùùW£Âùò³ j9yx½}žÉ![6?z °»¸8ÔS(·Qµºj@fN²(WUä9Oü= ,;ZkT¿¹šåf ÎXä0=@„C6zÎdÇ‹w6°?•Çb+PÅ3œ~)šjÍ P)V ž Ì©]OÈÎ|õ&GÖI… ð]ô;¨ÕP‹]i“–üª— ¿(… Ûªq¿£<5ªA¤{ÿûÝ»bý.]õ-Ê¿¶ïZjÏþÚÅ]_îÃã†x%¡îø› {;լ㪾Ûü1l•ý¤‚z]u +÷CF! @’ó®Ôän”šé´ãqÞ= ¿z%ìÜÉÂì›sƒ&Ü­Ç"ûðO¢ªOÂÅÅwüø-'z¹¹$-ED¥4­FvRä÷܆, Œx•’"Œ ^;cÓK¬¦–…ºÖÝøÕ¤G¿þýºÖ¹·eùzßSO¥Vå?TÜÖÎc ´K~ÑmïÚS·BG¥lìhÉpgýö;iRyþ‹½.ÎÓ'þÃýSƒÍÕ¤ØXÅg)óRs ²EO"DšŽ]:½»FtjTl>Ñ”NÉhµ¯Q¾›²ÉÀN\@vkÑ‚Àø|¹äã-3w&h5̾Þ׎jBšö´†uÌ+”ky‚ Ýûÿ©{‡²ÙŒÙm) ûÔ°¾³ÞŒÄÝ?½bð^ItF¶Qù:®ª;Ø*ûü}Íɽ!É@–qwßÑÏÓ&ìh¢§¢lµm7~±çÑг‹›çå’ ~z..»zô߸±™¶v=½¦ff=‡õ Íò› —ØßHül½µb%»R‹ùül™\./z{ØA  fØ’§¨3âìéç{©5G^Ï)cAìšâ»¡6ðBú§«ÐËL]•ûp™ÉçŸó’ÿ·\™Wåæ¾©ØòÌ~ŸÕǶ=paz=P29ÁøÎ:%År5§|Ê@Qb Pɽ[”]pñüâÇ €õ‘÷îݒ˙譶åGÊXm–ÞË.53a©&-¹— þ¬‡ùr¹\žÔô¦E”þ³þ¬‡ùù3jZÇ·J}U–§šªAº÷Þ½Ëå¯ Ò­XîΧ¨²ãªq·ùc:تûIYö9Mê-»Ø™i*m«­¯Šäq[;(Kß̶³US ív Éܯ?V¥ƒZ7nø_¾½O`•:-Q4(!*?ôüy¯þýçP„ ˆ h“y—Ÿ.r²0P)Y Ûª÷ôÝwÖX«QØõGÞósëÁSϾˆÏPçÙÏäïqÐþúð §ÉÐ)m V—ŽZ•^¼¡ÒÖãÚ­-£Úés98ºm‡¬½zÕÝL¹æ›«®ØPïä±Ê¡[O‡ÛfE°ç‚ÇG6øÜä ß±¡;ȹšÀ”K ™þ·Ö÷)<Ž\; IDAT¹]|rÊ}H»úÊ ÖÄÎuïÃ+¬Ôþæåz\ËU5­c¡ìÿP‚ H÷þ]Ý;Ài<öè³»»§÷6Ñæ€BíݧzßyäÛW[áß鸪ì`«D©Ù¬½~~I¯ÆÜì׊Û{;àðyNlN³"öLµã©‰¢nÞˆH`5¶¸óÖ©ñȵ?“Jvª=Æ_¾³Kʼn‘€ô^50`®Ï”âÓ›:†.^4ÈÞL‡Äï…Q-tK*úÞÇ–.W®Œ¬'Ü׳˾øÂŸ¸½T› X0Ýe°Ec –<'ù~ðѵ«‚žfÿàI=þÝÖ w°AAÄQþÅ)S¼<8—iõÛ€OWŽ@Ðs(§®Ža\\N‹q'.L¶©›vÙ÷Ø™ë±9:¶§.¸-’|oA¤âŒ÷¯ï…?ŒŒNe~Ú£}Åz£ü¯í×@-éÖ‘£IZævÝmÿì" =þ8MúC7ô¯¶ÆòåËɃ ‚ ‚ þ¾òã¯yÙÙi°ü4Cåg!Q)ÊÎNPjÆV<ɧýNîÙâ=ØpkÇCñ…õ{NÞw+안/H9·ÝÉH…*yV䪕ž'_8{ï¶@¾»§:ª–ã¶8?j‡…ºFëÉ+§»»¶RgPÔ·Ÿ´ûZH¼ˆ/H¹ºg`cN%Ù‚R1™¸*4ž/]½u'¬ø‰ÃꥷõÖjSUóüï‡ D|ðLàꞆ4Å1štýÓÓK¿.)gÜ¿ËDü—ëÆsKÿ)›Žr³QDfØ$ÇEËÿÚæÖoÆAX­FÍìdÐËÿzÅJ©þ¨Ö(Ÿ‰jç=W¢{kP¬ÚÎ܈.{Úª€Ódö=¾@´D=2 AAAü|á‹Åf³ñ±BÂTPE›]|ä<ûÙ­hƒ÷ðÃâãOžÜ5±«Jf®´É„#ǘ ö;÷ä²+£å ¹^“•nzÝú›r>Ä< Þy;t‡¡­5X–CM‘vþЋœR›¢[Oô?4¶{“'WoXº:ì}}žY%ÙjXLÝ»²wloÀ£ì2¥-ÞVÜ›\ãI~>ƒ­•lv[½å¦R;gËŒ¸Õ´IÎýú¼5°ußê`ðåâW…:¦Æµé“«‘™rò¼„°»¹×ÈBéÑþ;+%ù!­QI&Ú1§@±U÷†\­6Ú²ÇÆªµ[Ú6¢Ãn½'O,#‚ ‚ ˆ_ù;éTUµôõ›½â1`úi©8e™†®n“âEñþ#{g¸ÌèßÛÖ¸žª¾ùcÌí Û{$:vkÀ~Æû’UYé+b<ì%JUó lÚ:´mÈlÃAÒÅ£OÅÐÿ\Eþv ×ÛV¸ï>JÆ ý+dÛ°™R{àÍö•žBN×vnƒ¿dñi[ã%GõW»½wO¤"µûÛOjÜ˶Ñé*£‘äˆà³×¯ö™¸©K «ÆÜ‰9eÞ¯ôÖ¦¬ˆS—2º–­”ÄpöhÊš´àÈ¥H‰µu{KãöÍÚ²ÃëÚwéÜêºA3@pöî÷_ÒIAÄ×ÅÅÅ‘F ˆŸÇŒ3BCC±pËÓsÜС æLOÃ< ðð|ÀãYjkXj-Ìê'^\ã|~ Ô[ð ™Ý^µy[}Å· ,‰;gN<ò®ø˜_Τæ)5,³Ü耠´ãÚM˜ªÞ†Æ«#!ñ¥ß¦(©äÓíPr ’l³”G€¤PZíPŽ\ö)P*SuE…¯Ï>C)ri  PòeéÇ'1éhZ«M7S;7³äPnܽ½* Žyð®07©B¥(Þkr™ä¤«?†µ…ݨIºÜØ}»|Ûvñí6n4M#åÒå”"òå#‚ ˆìðŽ4Aü ¼¼¼~‰rV2Od`àŠþýçDD§I”(RJYªÁãYZ[;mÝ:º8ÜÒê<ó¬»êý ë÷â2¨ÖÆàãƒëoò^‡ÜHvÒÀyʸ´Ó÷Þs ;v·Íò{°Ü˜hÿ‹IãFZ2bœy[T¦…¯/\OrÚdÖ2÷¢s/¤ íúÒ›*ÉÖåZL®›~“i‹–Ó‘òv­9€¸|U __ºýnÚও]§¤ÝfõÒH ½•ð‘—ÀØÉÅ™J1¶­Wn%íÖ½1´½s{rÃ/¾ÈÿòN~”ŸçÝ^+¬{î Rô»$Òê2`€>äÏý<ïä¨P)éiÊ2é{7è%,š9¶Ã›-÷bî¦EI=ºÛïBC$Ú"‚ ˆ··7i‚ø×I¥Ò_¢œ•OË´ÙÅegJJBNN›ÍÑÖn¨­mð)Ö Ë|ræ~¿. iǤYñWü—xì»›-—Gî1¾Ð}žãो‡Ì»ès«Ó+^ÚVð2äXÂÈ9M!8š\¾¥ò£|G8­XÔ˜ûBį/ù¾®˜mÚmÏٻ묙Ðf¸[ýçO¤$Òr×ùåGúŽ™©¸faßÙ[:¡ðÃÃë­Î+HÞàÓw‡KËËF?¼‘^(—^GËdÔÊ®újEI×ö-Xp'£ôØYQÒ‘ÑãÅ §OÜÕy&…¼”ûþ>kWÅ2•W*ÿ‡´Fe™A*ºv%ÍxH KfÒò‚ŸÀÊiWÃ_1äÛ÷ÍÈÅ!AüB~‰‹g‚ ˆb¿òS(9fö©ûñ]†L«í(çAÆÒ드&œËøîi"؆Ã/ð§ñb<»ßUõ»ú Ÿ»G.!â—àååE­ý÷‚ŒnÄÏ@*•þ’÷nýR¡"Ý S?W M6 Ë|¾ÕkñÅ 2%ñÝÈÏ'A¿Äái„_WUUš¦“>~$MA¿_9Ü*ˆ÷tìãùãò“zê}‚ ‚ ˆï‰¦Àå~ÌÍ­øVýúõùþþÇöíÓÔÓø×_¹ŠŠ¤¹‚„[pqñ-(Èg±ØÊÊêžžcIcAAT”zã†èîÝÈð zFæãýýË_ÕQU==wndx8 ˆÆÚ4§%K¤ v#ˆß7ÜrqÙÿÒ¥]ÅSeèê6:Ô#0pi/‚ ~QC›òþoÛ Lˆ' N¿Q¬zaéRhÑL#ŒŒ<ý×_Ný•””Tü®ª‚‚÷ˆB!#ƒË¥†a˜Í›{zz’¦#ˆß4Ürqñ½qÃÿ…à&º–ÀGˆÂâÏßïßNPÐfÒdAAŸ‰¢¢™Í‘§Oë5èÜ€Bjê¾9sDB!€ .h=##Ònñ›`U\ÿà…ð&–€;à ×4#""ØÅÅ÷sœ¦Óiä¶ h_ â¿xìd­}ý¿q²RK—["¾€?žÇù©£Ó[k‚ ‚ ~u6sæÐ4]üºøáÞÞE‰‰ ©©û\]323KÞ A4­ehgg7wî â²<aõ;ô¤ˆï ·¦LñNHx;` òi©9°©éo’’bŠpZŒñ;îòGæÚÎÝ6ú_ˆQhÓË\çoÌ»!ÝÜê¾}åæ»©?ñ ì?¼ÖQ¶×NfIì*ß‘'AñMríì>…Z Äb&3ÓîÜWW0 F“ËÕäjirµ´ôôì\Ç;mÚôe:\ʼnƵy¼ÏÿÖê:\åX4U<Ó²ä ÝÇDãøûo(L‘ÛÓDãtjÙ… Ê=L4Î¥ÿÐj°7ÐðŒþÎr~_E¾áÀòIN=Ù®nÅ«Ík¡q,¥ò¨´´ÿD³ü_+NüCáVAA^vv,r£L!Q)ÊÎ.þ¦RjÆV<ɧýNîÙâ=ØpkÇCñ…õ{NÞw+안/H9·ÝÉH…bñ‘«Vxž|áì½ÛQøîžê¨ZŽÛâDü¨ê­'¯œîîÚJ@Qß~Òîk!ñ"¾ åRèž9•d JÅdâªÐx¾@tõÖ0ˆåeª^z[oý§6U50Ëÿ~¸@ÄÏ®îiHS£I×E|AH_J¿.)gÜ¿ËDü—ëÆsK²øj­«É0ÆgÏùsñ"~ìݵSÿ¾!äÂ+ÿQð$ uVµ (M«u×BbE|ˆ/ˆ;vhI‡ºl”iϤóAwn Dá{ziP ´zoŠñ£vXª‘S-»Ÿwgß’ÑöF<×ÖÞy͹Wâôd/6ì]PñÂä«[§:¶oÁãñxFí&oº”ÈÈ«^¥Ú<‰²?¥(8A;DQxD!ªKÁä²ð’Â#Mäàã<¢ð¨9²Þ}C\œuÇcÐŒ ©Ò¼'Û‡[ñx¼CŽ¥|S,Ç_ÐÏ¢×ÌrÀÂñå÷FYê©&½v %Õ­U$ôíi2ètjµOÍç?õtl?éFîw´'# ^>¬“ Ç3¶î7kï£,Éûë['Ú›ñx<^ë.£V_|û•Sž±bà´àwä4ñ+±[º”¦i0 €a #3SÌ_éê0nß‹™{BnÞº¼ùO勳L=žô¾ÊÄéY…•õòñ»'¸÷\y,4<<ôØÊ>ÒcÓ‡­~˜+ÿú*ÕæI”Åx!Ú ÀnžPLGÆjDwF^N¹Ïé£ñ&h ÞmhèÖ8úÈ}´uå‹^³zjKÃNEê,8}ûÎîz ßPDñ“MWEwÞÁ~qk³ÕÓ•“½ž‹ÿ©µÈÅ‹¶#6û{º’Âø=Sß2ZuùÅËØ»»ú$nsY)–&Ÿp›~”3þè£ØØÈ“3jŸŸ5qO|¥{'¥fá2®È{Uh* ¸ˆ_GñWIÄ È3˜ŒŒâ?išÖ¤i»¹sµ;w®t¯§kËôôdúõ¤<³Â)[³g5`o÷bçÒTÎêÙÊw²€"…sîLjóxµÚ9ªxJÉÈ!8£2¼K­æ¼Ú<^­^®Ü§Å½–”ºç©nÛ²6ÏDkÄjNrQ™e=æÎî§Õ‚W»¹…æô½ì,YùÓO•æ™~Ÿëö‡–¯vs Í™‡Ø9E¬ã“ÕN§³.Œ×¨PP\ÎLÖ•)µ¬æ+æ•tAlµÆ_dɪÝhÅüe%ñLän5ûÖµy¼Z=§Ó±ù_-å« –NO¯Õúå{™_iƲŸE*_ÙµO­æ¼ÚÍÛjNÙ¡˜šÏÞ2X}w2»Y}àrvÁçÓÙÒ2- ©¬´Õ||¥}%AÅÊ%(÷²ÔXê¢Ù‚W›Ç«Ý¶Bk}ÞèWš¥ÚÏ‚øÇÃ-UU-}ýf—Z*AY¦¡«Û¤d¯ˆ÷Ù{ÍÞ‹ÑÉy€ª¾ùc<Ï{Ø×V2tìÖаŸqàÂnŸ©†Yé+¯ãé`9È®ûâ}§Ž_Ê×Ö¡mÃÖƒm8Hºxôié#Eþv ×ÛV¸ï ðÞ2ºÏÖT»ŠÙ6lÖ­½ðfûJÏ~g„e~¶K¶µüQ«úÀ«ÝÞ»Ž_ص긨×˶QÕ·‡%GŸ½~Òó¿haÕ¸Tpö•Z³ªiã„ Û=ý¶yGùG=wøy”†žŠBu ¨ü7ÕvCGºÍìo­ @MWM¡l8sìÌ;°;êѪýðŠˆ9CF3þyæÍ5«oèÏ÷÷™ÑÇÌPO·q— ›÷η’<}’*EQÂŽ&CÎ¥Ë/C ℽ,‡-u±kÁ3r\<§û§×÷ Ҝݯ] ¯¹Eßé{eÉP$ôícñçªe£í;ÛX´¶è=ãpLvòñÉSN§g]o7&0¹ÌA¦XñF½û´ñöm4nm?eã¦Ñ­ŠÞ¤3eW‘‹g<†wiÓœÇãñÌ{¹î}šY&¸²bKŠ’Î¹ÿÙÁ„Çãµhç8óÀÓ€"}³«ž2¿ÉÇ„7s!tÂ`¦—`’}KÈ!q¾DEÈáqPmÐìÔµk¾irІ°œt_nO{z5¯Ywå%–€DázVáþéÏäk©NóSøÜ™Hß)Í«œÜ3÷NLÚ#±lúÌ Véop¥y½Sš5‘›í”}76=·@°QmÓ3ùŸ»rœjÉúîË<4TZrjš’Y*”_Wzœùϔ ŠFÙÈäÕmTZ1ÿÇTqr5µpëõ´Gò n+/¿Ä’T–UÑ×VäE 窯Jföûå·×¬¼ËD=B¥)é¡Ù÷cÓa¨Ãj·äÓNdOЇõŽŒÛÛ‹Ô>'Uý½óŽk"yø³%! „¦QBSQDñl¨ˆzX°!vEDÄrvÁÞË©ˆˆbÇ.ö‚QÅ‚åTP8@£"jêfß?(Ø×~¯e¿Ÿûœ!™yæ™ç™™gfg·¶êh«¬×}5b`ªêœEˆð‡æe7E¶Ë°:ÖFëµjƒ €ænmÝ:ÁÚº£Ö=˜p  `)@8Φ¹´lše_Yí;©‹e—v½6?p­Û3 €ìÁ}:{÷ììݳ³wïYÉeµ—€^9›œ“¦z»²àÏ×Ój ©H…BAT/´…اb2¢á{b*S5æ8SÇÂ`³0B¦øšG]­ñÆ$¿Ö Hà 0ïµÇÖìÉz~xí¶ jf¿’´c‡²i=cõäN8ñp×µl9ݰÿâ7ï—Z ík¦ò k¿Ý‡Wõ¯gC‚=~h¾âfâmÃ̪Ï[º<ýmüæÜ>á÷RRŸDUîö >û‘ Šž&ð‚NÆ%>Œ_o—´nù ÆÐ]^º¼þQq¼MjÂuãÁ¸ìÿìHŽèt[±v¨Ûd¸Jeæé‹ïÚ®¸ñ2í틛ˬ’6-¿ŽQI@yưèîòÐ8Áê[)oSïýÞéå–eç>¸aߥák‡7gþî&C™ +ö¬º*µ¡É @ʃ¢òüˆü!ã0Ì/–Þ_)@þîÂátÁà:l»Y'L2fu KHØÞ••yÀßo¯Ì{ßÃÔÔG‡Ç!'ûF¦Ëj6$¼ºqý™VÖ¤­9§BAŽikƒ’ÔôüŒCÓfÓyîyê£}Ëî¾WÔn’T¹Š yÖͼž[â_<¹´Ð4aÍÒ«µî,d˜zï¹{cçÔNúªÍF!zœÀŸq"îÞí=½Þí ɺçzâý³“ÄGÖͨ¹M…àž&úåìXÇ6]Æî(ñœ7ÎZƒÝ&`˺Q-˜dùÛ«· Í»”-8vžJÎF7r3ކæÛØà" ‡êgfT]"±…£ƒƒ—WãW¸ú$”!å*W&Î&±/xÌqfâŸdûù¢ç§%Í0ÀM¥‘—‹çtV¢åÈ'i …ç!J`(çI-9¤¦@6ÇWùæ SXÙ= ŸãY‰š’E¾r=¸Ö²™cɇ‡ŸU¦Û”2?'²žèHæŽ$øÉo/Ž<^<¾%µêZŽ’> ÆÑ' ÈÓ£Lè)qæAƒ…¨“fÈZé‚¶•|¸ò1Í¡ÅLIP“]ÞX¬=3A¾eo¹O­U;¯œXÖksñ"#µm¥‹§é'52y›Hmåõº¯FVªjí†ý&„M7q]kW×…Òª ú‚æ¿€bvìØ²Aƒf'%Ë‘­àÈÂ!xs§N^[¶Œ­<ÿàM!bÖÉŽ ðåQü»²¬Ë·s§0óõŸóàÓâ—^]‹vŒß_;>x}%gÂh—1íRŸy/¯¡‡,ëR|Î4ï–3—„È/¼"ÌÝú³Žl¤p+¥t†qËÀKYÉd{&@Y–u5ácà0Ë)ÓüóP->ÄÞMÿ"(;¯_äƒ]W“Z™ ìû•°Ü};¢PwåUù×pKM­å„Öçúþƒ`XÛT(ÎÌÌ.ḱ()yÆ©Sü6«M»¦Pû…¾jþ!Š’¼2LÏTûkÙ0Ápo\^„U}F„7£5‡žðuÒì½fŽx8þ³=n5bx; ëس²1«@ímŠ˜û†+1×Oœ¸xrõžù™E‹®Ã§Í à¨F츩wäe/]S}´<ÿ“3Ð"SóJ\‰ÀÙÚØ—1Ç/àî]œæ_{Ž¢ǵ—ÅOãnåG °æ«:UÃTÀd…@T8¦ >Ÿ¨¸Úàc謴ÑÇ#•¢g·r ºYÕ:P©È‰=þÚÜÿŠO`;|ñôc½ö]ÈèÆ¨nHªs/i©4´4*5D5¸Le\?ÿÆ|âïý-Ø´8gê¾kGkÎØ¨r•I•¸å„ƒ[éâ Ûw¸ÝÊÕé_äÐDC¥çÑ42®˜+Öl.–C½ìµQ„´ì`ƈk=¼£`lo¥õ¾¢×Aõ=÷%¤˜íèkráôTËŠž¿=<ù¨~Pô„–ìg”-XL‹ÂïçÊÛ[ÒO¥ùN(yñâubbÅßU¦< )½¾wß!Ê OŸÆIBŠ„p•Z*£¦KÙþ•œ°hŽßV®œ§ð *[6Z¡¥DžGkn>‰ç"dK[‚[;ò(GÙœ_±ZLêµT"Eh1Qùga6J|f iÃú*Ú +T@õb"B!)þ€º„NE·„€a+€bÖÀVŒð$ÇeˆÈ£ Ðw\à]…"ETòå©Ï­ÔÇT•þhÖ;Šì &|P˜•1b’.}HL¿vþda.‚z•0É3W2JÐ’FΫêhÛ€ûШsàdÓŠ‚¨¬]5 )­úº¡@ó? ·àìÙM;?|H¯xͱ¹iU¬JÑ“Ëgì>`D€(J»½(4ê~1I&ï5Qò›ç°Å ½$__XE1”¾½|<}ôlK ’NÇæÖnÅåO#FùÊ—-42d> ⬫ï“ÎÕ›Ÿ°uV¤þêIm}f4{ùœ…”¨uOLyrĸ`Æêùýgmî²Ï®X°æu™4w}xÿ°€V£–Œ}|»@ ÀQÍ£ÓzÌòÆZòœ[QóæÝ+TBõ.¯OàßGž¶ÇÑž‹Gz/ w¼W¡vBNäÄH Þè‚æÅ{ ¢mýS|m2UXB€ÊX§ç}–ñ›òjMýT¬k5åb5?Ë ³ ˆÏ‡†´9¤Ò¯e*´cóÙ-Š£ T‚z·29ÇÊÃ/ÄÀ(Ï}v!lþ<Ÿ<þÍÝT¶Ÿeñóè%›O&å"MZÚZsKê“X­¶¦Kèþ•Ûâ7ùm+çÙy­\6ºús¹m ‘„Ъº“BPÐ*—b½@°Þ;CÙZøà Í\½çgæ)uLuju¶òÂÜ"Ì EÕ ŽñÌ›2J„% ³ ©Ê½\HKeJ ”Ò2h°%DHµ`ŒoÞ”4œ‹«¦©Ï­j~8B*×i`šz\GP¦&³RŠIÙÚ дò ¹kÐÅÛB?Ksd¹×ÖúÏ‹m6ûè¾Q6lÔ¶@”ÓÔP£ðÏ<9ÐáÍw–}yÎ4ˆ$5C­Ê$¢èÅ‹'iÙÛ7,K†^»‚év–™}íä±+µ‡)CÄhòIMÿUšgzõºÇ£\S8ЂD ìà ÞáŠKŠJ¸€å ˆžL¯¸`ž‘’a!=©¼âÊ’bB…Òäër òáL]™¤–! X¡ 1@^dß·”Lp¨s£X‘éd]»F>àIÛ6¦Pjù£(„SŠÂo3àBíìcL˜ŠûŠ»\åýº˜×¾¤·>P™±x¬iu†ðIâ Z -Q*çÉxõ„"ZzC*—R±¢žT•mRÄV-·Žµ¿Ji÷3Ä IDATU÷|Aó? · <|j=˵ÅÎ|”²“Ⱦ¼sÒå5¿<ÒÇèHÍ™FÖŽ.®;Tg$™ªidï¯ìô½RSH]±LS­²g{—\-Tê´ãÛ¤ÜÉ)Ëü£FY¤8íøúaÇ××ÔGto…ŸÃ 5¶x{ÄÇýx¶â¯ÔZI-T+u{A«ªŽ£l×è»êX†:Aö¢^·Õ–[ÇžL};Sàý‘})åt£þǰ¬úuæž=u-gøxóª+D–y`|ß=6ûã79#(„œ P–})#¨»]pž‘6ÃbØùKÓ,@Ê s„ ½&¥Ýk“¢k™§¹íV˜À8&í‡N÷=z6ò‰PÞͼ: ñáÌì91MVÅ<hÁF‰œƒƒz®˜WˤP›,f–Úú‡Yˆˆs“O®ð_µðL³cM®5.Ì 41(M€7 ™; PŸƒ€=ÆÐ‹ ©æËáõBøäüçÀmìsŸ’¬à|cñ$£@ᦅ!ÊÊy­x8eKÀø–VÜüWï%Ђ✗_´¬¬Í-tɤ¬|hãDÉÇ| Ùp®–ÚØãÿØ¢dáÍà![L¶šÛšJY¹l¢,y¾Ëb9vç…iu1PŠëk$ ôâÍwk™4I\(’T†W_ÇIuÈ5mš×Ê•fOË %ù¨PH‚)ùˆÝܧ¹ý£|eÁùºS¢DÍI”ËŽ—jN铸Ég!²R„`ú|%´óì]©³ Á£Žá½¦)X…øžƒ¨ã4Y¬b7ƒ4t“¶ÛÀYsD¶q´Bó3cý8­Ë.%×WÈ«_ýC)SÏUâ¸\sóIÙ¦‘„ò9;d£F›}b H&Šfg¡yöJÕ%K éÖÚÕˆ‰¿¨%³Q…êS˧€RÔ•©j²#ÀÂI‹Ñ%gy‹V17ÉdfTígHS©õvöšŠu#Xsͬå8Ys¦º ê«øõ¶QåR& ®ìåq5ˆk[û«n”V5iÈ4ÿÉÂîw¬;Â2ë2pÖªykÖLö2Ή۲`þ•Ÿòñ*¿óèáÆOŸN§mýÕv5Ç%}õØá±Ï³>}ÊþãúΙ6e;ÏræÎ77‚·g¯¼-–¦œÛ~èO…úµ-ÌÐͧÝǽk޼($H™ðöºîÞÛ^©9„‡1Q駬ì<Õø m¿œk³üW‹þ633õñÅ!¿§4uëaÆTÉ"—•– ž>_%JÓÎoÙ•ªK¤ŠL*µÉ¢Ä5“&„žËËÀHŸkèòY(Ë2îÆÆ§þ,¯ëBLÁl ŸûÀKWøÓ^5áS€Ö`6¥*ðÁe°g€¡5À;ÈœDãb\¯E´ð]a­k“aê1Ì:kך¯‹¢4õÔšé-½ú©;0ǶÑ_óÎÆÈ_ò¼Ä]´=GÚ[ööiûaφSoËIε­;^Ê‘«çpÙ¸½;¼æð‹"Bž—´{åÉÒÎãÜô>ÆÌ.·}õPség¡P(üX . lÊòB™®¥½µEóÍ#¯Œµ kÜEÈb‹%T¼Þ$QÔœ9òìì:‹¶Èý¹ü®]uºvç÷¥y¾\ºîTÉ0“a€ÑÀÒµýÑ-žºÖVº}×£ƒ6–ö5 ͆•M³fø»è¶íÈ[üH8TYœ‚•‘€r«4ÍÁ½ùn4óF”lõRV/®c&’ð½bî)­N6z­Ü´ž:–í¯:Õ¦–)1’nÛ-fÖv²Ñíèϰ\X<ב e_âEØv¬Æ|œðI”—*FôW2U(`ÆTò)GJ*Q¼ú³kã׉u¯h®ŠG›R˜±Vô"Ù)…ƒÚÎ6zN£ÙJŸ’¨É„Ú“Ìê,P'*3j¨\Ê ÚÒÚõ[µQ2iþm¾çwôJÓ¶zöÛúïÉ«¹ÃöAŠn.¶7¢ó¿y]˜yGœÕÚ¶6"tä–|9 Ú¿xmŒ™9ÀœznK»ÏÜèÙ~ۢϔqÝôÎÕ³äi2,|oIè²IV¸¾ýÀнó4??¡êµ[ôõ0õ ñöîLìüÖÕû(¯óÒ“‘Fk¶…M?ðI€éÚôðÞ=Í üšåÊÔÐi sü]Zἦ6½Ç}´1%»Œé¢"³®Ú¨ÑÀ k_ÿ¶ÖÓ~žØ&®>×ö5@AþéêŠéÑ]NÝ\bÿ“ìs±çB+䮃¢û @€ïÆ!ÀÖ(ª™”†QPð È" g˜7â ÊoÛ£YÞ”¢@ZcPŸ°;R¼dåhçД'è1&"j²€™K-…ã0'rQèÜÀÎE$ÏvÐâݳìÙ2|{Ä—…¡Ã–Èô†ömÅIlD.ÈúÏ퉙 Ý™¿8t¬Óº2L·µçÜC!uÒ×o»[*mc{o«Jç°éÁ!ª N»ûžçÒÙ„·h¾mô1lG` X,® ±$ ‹Ï²pu 33SRª·ºÄÀIuä¥W¾ûu¾ÚÑÈ\rùe…¢hQÿК?óäAû ƒ¾þ- Wž×9VeY~ýeåG¾“x뵡AÕÈñŽ«5s!J×Ñ«Ê9T•žÆÞEo¼k¤­¿PЩ+ßBûõq9¤Û|·ŠÑ’JEö¯¦¶øjjå¯f¬9§2ø¥<âjÍ[…pżÛT>BkX€J[E}îS…I€Ân*®¤´¶J›¡2K#|Aóï/CÖó[@@„TZŽ¢8‡£½uëxÚX? uo{úáyóæMPPÐŽ;hïÿÀx[ þgeKOSù‹xhÄÐã.îó4@i?4ŽÒûs=B›î¾0Û–>RP‚ ‚‚‚bcciS| ã…>—»Ã͵PXXš°X,ÛþýGmÜ(*, 2$33³êvB ðùýçÌé0b„ª@@¶*Íÿ/iiùtøŸ‚« ´v¥¥=¼zuWÅ£2 [z{‡;¶Œn‘4444¬Ù 9½¬‹Éò˜Ò‚Þ¬iÔúùæžÇö3Ž[ѱÍ÷@e0%à³tì솬\™óå ž>½qHa¦Ø|¾¡£×´i?ý3]š¿nDܾý*ãôpøÂkéi4ûìÙM´ÉhhhhþˆV‡ÙKZMÚzÝks?úa» A–&ï:È ÜîAÛŠæÛçKi©Û¢E—6­’ €oa;qÏž/¥¥¿–Ä´èÓ™‰‰®®FFFÀbåääÐF£¡¡Ã-HK{ô*ó,¨8=— ¥3 “’ÎD„‡ûWdlÒÅ{á‚¡îMXâO™O¯˜r-çï>©A£UÀ£M2£útJ“}»öBXƽ§O ôéÜÚ /H{~ý`亽/EÊFU‡ÛcÃã#Å':=£!HCó¿¤æ ~ÿcPn+Ow£Ð¸n–ë¼ä´3mšïƒnÝæ¸¹EÍ ,ÌNŒŠ*%jpð¥HöV‡††††††æó… ®Û m š:Ô>¸-•–烳J¬UÁ/ Ð”¢eç"€Ü¤˜C§voÞ1w¤O'Ïi2`÷™u÷ÚŸÂÄŒÜ˶{Ùj"¸…O¬01#yÅò}G^æ^:ÿ !CÙGD×ó÷7ÂħaNÚ<û)˧‡Lk£ÃØ}rä­ËiÂÄŒWcwiÁ¤ ˆfk¿±i‰›wï]Ë&>ÝÖN[µ¬÷ÑS-¹ƒgF?ŒË&fdž9¶ª aÚNŽ&f\îßÕÏ•z¾Ù}èú¥taâÛ¤µãíj¼6”í0n^oM<œÛÃ/($rËòU£\‡ŒÛøT¤¼f¹ÓÛ­Âø,ºš˜!¼um­#“nt444444? @ ü×Yhhh~œp Eqg@Ýï<@äŽWœó&‹ÿ¸ûL‡íN¼––vêÔ.¿š¢RX­'Þ?Î1c¯oÿÉ» [ ³mrÕ‹dŒzjÇüœòüÜ΄2`uö¶ç¡ó^^CYÖ¥øœiÞ-g. ‘_xE˜»õgÙH!6àVJé ã– –²’ÉöL€:¯È–e]Mø8ÌrÊ4ÿüÔcDK€±wÓ¿ŠÀÎ+Àù`×Õ¤V&û¾C%,wߎ(”Æ]y¥òÁòäý›îôZÙµÓ¦[Û/½÷åà [Xž“ø²8ÈØjÂxŸœD¤W *=ihhhhhh¾O&VþKV-êV]"R½£UeIBÅÿÓÓ*;)˰w ¥üiø 5§©¥üöìÙMƒÏþÕczO‡ñîüþ­G1;wN­ö»èÉå3¥‚#‚øO÷nþéFôô›ï“åÉ;GM<—i8lñÂ߷ϙҙþGA݇óIß^>ž@$ŽÍ­ý Ÿò§£|Å¿72õ²A–Yï“(Äæ'lùü‹f[ŸÚrPH‰Z-±<9b\ðÙ'ŠŽ³6/î.{|pŸ5¯Ë„ñëÃ_a–£–Œí¡/•Ö*^§õ˜åófôåçÜŠš2ï^¡êž”üÃÑ ãƒÂî¦Ê¬<§Lšíg+N~áðƒõ>~°äÞöÙ»_™õ[º}jßf8Ýæ¾ÿ#7³ˆø' ~ŒjÒÐN¡Û Mc¨Žµ@5ÖªÚû‚2F@=@å?{ï,鿬†<“ݧ5/&ï¯^Øèé!z‘(åLGžÎéÝšw¡ r¤ø†Á¼­¯ÿ&ÿþ+[U¡xÇêךwâÓ_ÌF"Ï·k»ô6¼ãþ‚éþfqÿŒÿ—B¿_ÔÎþÃçÖsñ?>:{ðQªŸdÙ—wNº¼³æ—Gú©Ù³vtqÝ¡ê¶LÕ4²÷Wvú^©)¤®X¦©VÙ³½K®*uÚñm Ò?îä”eþQ£,Rœv|ý°ãëkê#º·ÂÏa…[¼=âSqÆŒòZ(Ͼ¸jÞÅUu~¨¡­êäß ™äB7¶ï YîÍ+?~+5Ÿ\ϺëÐÀE}ÌXH_mì'\vã÷N5zC „È=<¸çž®gcçØhP·Â²ça~“·=*ÀWÆaü¼ V¥Šw{ô¿<þæÉáM€—ÎlÙvèÊÃÌ"ã™;õöž4¶‹ª¦òó©a—5ݱ]{‘ß‹)7ÎO2Ç©†ŸÿÒV 5-yfįž7¦Ü:áe€þ?˜ö«z¬Ú¶jš²²—×içC÷¶täÐ Í òuk«v‡M’‚$'';ÔŠµTw·Òu»hgW”räÃ#Ö‚ îtSEŒÁøq–‹‚"ò§V á™ví4Þd^ÑùÁ„Žî_Ð3-ÚL4çýO•ý)ôûýŽuGXf]ÎZ5oÍšÉ^Æ9q[Ì¿RHŸ¢ù7º¼´ÈI‡Å}–‹‹=¾¼q|úÈUKI¥¸ ¨Þ÷p7˜à_P/ûÚéä&óbîE6úoB •Z`=mþ­3ïGˆµ>]žó«Ï–4«IÛÎÞº{ë\ØÔ6¹‘¾ý¦}×ð ÇPŒãLŘõœûüç ÿ}ÓúwÔûlECó?çéÓ§õ|_ßîVEOË"9’Ã!¹<¥U/±¿3¤ÝÃ$PôŒ=k Ž@ÏÚ‰?}^Tk¢#Ç.„ð:·Öt;xjí{Tü^ð=c€Ž­@ÏÚ‰|/QVNÜ“#µÜíõÝ>ÓY©åêä#®iŽë®g%Ðë6ó „ZYT[^õéI '¦hÅ —&òÇÃAÕ%v?'ÞŠ%ÚîÝtœìuû±S¨s!9Ç´í[ñd!HÎ1í¶í´O¼ÃT¨µ ÿÏl¥&{U3]×~ç¨ö˜’—È™ÖO×Z gÝžïÆÈ+Ç7ÓŽÌER7iYŠK‘¿`:"¹jç^‘ÚÆÐ`k©[/y:ÇÙ·8€o#ÐôÚÏe”UHã¡u'^AåÕ…ª±@í–·¾¤i[=ûY¹¶0rµ´õ™´þÁçöNaEæ‘>F®-ÜÔnmÑü,ˆ3“Þi÷ œèÞ¶…©i {wÿ Ƕ‘¿+䞘âSPti¢Û¸c¹)Î8êÓ½­µ@ 8zLÛóBT#8=¬wë HeÞé!­="3òœ !Ã;·6<ƒ÷½(©§ ’gFôs¾bÉX÷n®NöN}ƒ¦¾Ú­Ÿ“µ@`ÝÞÃ?,!yz˜‡óÈÅn6[Ï%óÜG®X2Σ»kû¶®#×ÇÆí èæêØÚyÈŠ¸< ¡ZHón®šµá^@UV] ËIg„»[u^ñBò-y•,¾¿>ä²nðÑý‹‡»Ú43lfÝqȼ¨ã‹Z>X½ôjÞW/ÈÞÇLwµ°áHÕ3ˆ¦¹µQ3SóÖ&zÍ-u”îòjg[‰@ ôÚúV%¢RýëÛß{µvêåI¿9 :…<€â]”§ƒÏ¹°jØ ”Áf®ÓÍŒƒƒÃ×Þ†$«Ÿ“QñýÓäd¨»»¥fq'?™µÿ)~!XÄG™ã9¹}Jï¥ä?‰+wkŸEU£…¢»œÐ8bõ­‚·©¢ß;a[–i| €ø¨1Ó]ìU|?µ )Bš±Akã3¤b¶}3O¶%>ÿÉ¥2ÓÎÒ«¨‚J¾4KcÚ, Ý™…ÏS ö Dî¾oøÚm@OL9|W‰—®²”è€77Z“ZqQ„'ðÄ'ã Æ—Ú%±—SçBŒ†•.sÅ6.ÐH£1kÖmMésBU¬–æÿÀVruÙH9vq¾öŠ\ÉÞCåùµw ?–Ì»øajÁ£Ãä –ï2ðdñ$cèV˜°]®õWL§¬·1@cZ •å þмìf¢èÂv¯ñ¬ ükRùW%Z¯‰Fx“·* ˆ˜8q³Ÿß¶3öÓ=)ÍO×aŒãB°hDLü³w"9¢ÓmIÄÚ¡fl“á»"¼tyý£âx›(3L_|×vÅ—io_Ü\f•´iùu|ˆJÊ[u‹î.¬¾•ò6õÞï^nYvîC½=Qô4t2.ñaüz»¤uËoŸ<2ɘÕ),!a{WV濽2ï}SS‡œì™. D𝏙xcÛ0sDô8?ãDܽÛ{z½Û’3tÏõÄûg'‰¬9š!“7ºrueÕÒðjž7ì»4|íðæßÔK½%o/Ü.¶õdÉRù’Ñ|ÐäŽÊ¤˜§Å•££ðâ|Ÿ¹ÃöšÝ‘_£{dÙL\:¨¹±Û’MÓ[³-§Ä<Ìø¬2jàlû‚Çg&þI¶Ÿ/z~ZÒ ƒ‚DÖÉÜ‘#ùíőNjÇW¼Ç'&̵Òm+ùp;äc:šC!Ÿùô:ëyyp%#[ ,ŸjÓà=xHƒzÖ¦QšÔ‘€#†+x ºŠž-OYh.e.PX]Ú-3|8G8 tye=·güC[¥$¨É®@o,Öž™ ß²·Ü©öwHN,ëµ¹x‘‚‡‘Ú¶ÒÅÓ‰ô“¼}¡ŽbòzC¬T Ôú#†{+L ›nâ> ÆÑ' ÈÓ£Lè)qþZ#„Ò€¹=üèàj­]ii¯^ÝUR’ãLCÖÞÞ¡ÇŽ-£{TšŸÌÀ}Õ˜ë'N\<¹zÏüÌ"ŽE×áÓæpT‡SÜÔ;ò²—®©>ZžÿI„h‘©y¥ î–ãlmì˃˜ãp÷.Nó¯=GÑ– q«ÃÛñ0]Çž-YªÏžQäÄmîŧ °¾xú±^û.dtc&îíbb€ËKQÀ-‡zÙk£iÙÁŒ×zxG ÀØÞJ3ê}!;4²jÊêÁ¦ÐibáÚËâóª¬ §×7Ö®9ø MLµ‰gŸJ Å狽ã_wÜ}m†¯vh€°›wrhÛI_e‰2ûä¬)Gõçž^Ø™‚R…F îܳWSÊÝ Á}l›¤[ÉùSøO.f5àj€áÊú¢,H<öDÇûäH{>ÐÞ?ò¸›¸ pË 3·ÒÅA·ïp»•«Ó¿È¡I€”Ù‘œ£jÚÒ_n–ulEjês™\}MúIA4?WVµUùü÷zÏn9­*^߉”åáGWr®é‰çÉL™…Ù(ñ™5¤Ê2‘~mgåE“‡Ý/Ù¿’ÍñÛÊ•óžAeËFER—Щ¸ä0lE€¼#õ¹Z‘8¤’R>–•… :ÊÊì˜Ò¼)4´½ERÊ)T€ÚÞ§Im ÉgWåBA©„u¹ äã< öé5LÁ¯o¥ùg¶B³ÞQdWˆ4áƒÂ¬Œ“„téCbµÌ•‹`„^eŸHòÌ•Œ´¤‘ÑHŪÑt)«ÓZh ÔYp²iEAlÅOrüQ†È<š}÷È5ªú|’Ò€¯ÿj{ø 퀀ˆÛ·£_eÜžÎ_@x-=íâÃAƒfŸ=»‰î@i~™œcåáâá@”ç>»6žOÿÆæn*û5ˆ²øyô’Í'“r‘&-m­¹%J ë]ü¬ììB÷¯Ü½Éoë\9ÏÎ3hå²Ñm´ê 06Ÿ]ñ3Z1º¨"/Ì- ZTuÙϼ)£DX¢0\«)·ªWÃ4õ¸( (Ž LMf¥4ÈF×BA]Ñ¢^ ¿!ÚM4ÙŸË”ÀU17)û’[‚é6å¢Ù@|JøàdVv;&© Ký†¥(qïòÌ‘§},ªFã„°­ú»b¿ÝH˵½™ßjò`Çìs×ÿÈÕ=ÿÆèוM1@õ;…(úP@êšW o,ÃVöUóM}n•/p„TRº‚2»8Y][ªÓ†j–uç3Lm]®½»Eó•\uß»…€£#ÅÓ2TÞ»ÅnB˜š’`J,ŒRJh &/ï’šàÏHɰž¿Tnɨèô1¡B©§”¸L©¸þ@¯˰‡)CÄhòIMÿUšgz÷1$¡+T€!È«ƒìû–’ÑFŠSÊW^bO°|hãò1H@W„JäK™êEO­§I­Åžzƒ¶FIht®òìÅg•½z Çæ±œ)·ç¨S€Ôúg¶Âo3àBíìcL˜ŠûŠ»\åýº˜×¾¤·~ CðIâ Z -Q*çÉê;"ݨ[±±+­ÓËâ IDATÆš~õU‚¢žTõjRÄV-×zˆLo$ëÚ5òOzضF¡”tÿ[Þü¡þÒҽʼKŽ„l8¥í “’ÎDTÇiMºŒþýòÙ×ÂÄ aâ«gч׸7chw9"LÌx>§[}™IÓ üÇ…‰ÂÄÄåvlÜlÒÞ ab†ðèÔ– ºç§ù'+’¢k;Ç‹*z(ŒcÒ~èt_›ÒgO„r•Þ‡øpföœî´˜§¯’ï\<ÚÏ«œVw‹Š!¯x=²ìK@– 3KmýÃÎ$¼|4ÈèΪ…grÿöþ:Î7æyU; „(K(ç™ððÆ÷Ί†kQ5¿¦.ëûY©bÛî¦ýòàé4ÕeŠœKQÀyp;-€i³`ßÞ=³Ìn.^÷¥A§Hÿ<pÖtÑžù®ª»X±à,»wéêÅl‹>mZvú…÷úì™ã/õûv7ÆnZ˜–! ÞVú¢üÕÁ{óÝŒ(³iQú—Q· ÿ Ê5ÐÕÑÖ ÏnÑü°±VÕ«·’““qtt¬¸‡099öìqtt¬¸¹°þ³[¨Ž|Ávq“;š³ r ݤí>²ÖÁ ëFð¼·áД°±!ll+B‘¤9i‚æ¹LX¤‘>‰k|©ï*q,`m>‰ˆ(™²Q#Gƒ¤ì­)åëô–´ýÀÚp +# ç{ÇKp¾Â°³Wðb šrŽ}èÏwCRÊ©y|—d¢è§,4¯Œº'h„„Ææ—â[‚Y丒›K|䬙[ñRR­ÿÐVšê²#ÀÂI‹Ñ%†ŒE«˜ù5n!M=¤ÖYì5'ð"JS™kv`-½dêï¿oÀtÕˆ)CC ey†…t„ ¾v5Óp˜´e =©›ÉßòæOnùûïHO n³4«¾u˜yïrrRªfãÐVrkgäú Ñ—R°¶ŽMþ?î19¦³,Ë1Smè>ŸæßÑvñÀ¹6ËÕ±øço33S_Üò{JS·fLŒ‰J?eeç•Ée¥åƒ§Ï×@‰Ò´ó[v¥*äRù5óÍàíÙ+o‹%…)ç¶úSY”¸fÒ„Ðs™b`é³q ]>ëoÏB¦ì³v­9ñºˆ JSO­Ù‘ÞÒ«ß_92E6X‹Ê¢\¬R–eÜOý¶ÞÁ„ð:Ï[ÚçË–Q×}œù)?/ûùåmSG„¾rœ»´o  gZŒ^h»hÕ­üún %¾Ä-õÝ7|çï#j™»±B4[lûù`X²AÏvºìæÝd×vÜÖìݳ£N!õ]G8œØ|òu1Aˆ’÷…l<—£Ántä‹Re×nNå_Ë&uÛ°Ú(®N›©nÛÅǯ ßÉEóc2±rw«"¦ªþÚÑÑÑÑÑ`OÕ{öÔˆ¸ÔõíÊ·*“WkíOÌD¾WÌ=¥ÕÉF¯•›ÖSDz½óåš*‰–®ínñÔµ¶Òí»´±´¯`ÆÒm»ÅÌÃÚN6ºý– ‹ç:RGw”òy&’íREÏÁFwøApoE"ˆžlÉbYá6^û6ü·”ãºÕ8Õ ž ¡ìëA¼áÛŽI­‰fƒ«GusÍ#þØ u•l 4¹Š õâ€ÖæG©Fj«ú³kã׉u¯h®ŠG•5¢ÉîH)Ôv¶ÑsÍVú”DM&ÔÝ ™®z@§l Ð`kiŒåqÂc$Q^ªÑ_YkOƒÒ€Ï›?0µ#$©´¬¸8œj¹ýPhÊ‹‹+žŠhÙ¹ 7)æÐ©øÌRv.oÖQšN¹°®#4ñŠÉð’ÆÎì ‹Î„²á3 øýÝƒÛænÊT3MçumcâÆ›=]Õñ×K"ëÉת>f˜üºpîì‘ÎæZeŸž\:aÅóÒº—·kÀ@£§ïG0V™ðð]ÖÔ*wݽ|SŸK‰‚âW÷²;µÖQfßY=aÙ×búÝ4µg£¼ÎKOF­Ù6ýÀ' ¦kÓÃ{Gô4€²E_S¿ÏaïÎ\™:-aŽ¿K+œ×Ô¦÷ØÀ¡6¦d—1]ªÄÎw[²Ø}æFÏö+Ø}¦Œë¦wP£Ö¾þm­§ý<)°M\}6®íûޒݘ°;R¼dåhçД'è1&"j²€™Ûxfèť‘õ–õþ&U0òéêŠéÑ]NÝ\bÿ-í~a†¿n¾ÐäÈöðSûý&R`Úf޽Æî¾<¡[3&|=z¤a5~Ý”s^ Wy:ntÓ¥ö ‘w3ìÔ)ìí´¯ê»vîG;4^¢Ýn`;ìFFçŽM1`Z»Û3Î¥÷toμA§ÄÎ÷Ú¶ûËâÐÑNËJPVî™ëȬF˜";¡ð¯Þ¬NV7!h¡¢^ëa8ñùæ–ÐCíú´ûG÷4ÐÐ|£TìnMªŽ©Ô’–Ö HÕ³[šòðµßl7CôvFåg¾“xë±Z¡L¢hQÿÐÚ_븈w\­™ËBû²úÒí@¾[ŵK%_ßµ<òzyÕ_•Ï·ô.½à]Zõe¹@ùõ—ÓUº†ˆ^U¾ƒTÑxMTÆ»ú»G绫˵¬0µz’èT_ñw Uø7mE‘Ý\rùeå^ÛN|5µn¥Hƒ_Ê#®–ל’+æÝÎÿ[¦STGÝl-¾³üêÙ Œ½‹Þx«(«RG 4Ø~¶…üÚk47_¼¸ýÓðLØV«“¤=ÒÓeÂ{€!sæÎT»ŠŸJ?$Ç]?°ùàÅ·d³ncö (MZ|2%ûõýwæSf:=ÏÈg˜ÿºÐ¿ŸaÁ‘£wiSMóD1ü E¸u÷Ù•°¡UÉ­ˆ{3Žn—.º_Xñ{OYå\øèªÈÙƒu=:¯ç(Þ;Üî]õÞmøelDžÝÔZåz]šïu)1PPü`Ë‚óÂV£V´b¾ØÒ½ÿIú±ïµûò§‹@ß¼y´cÇÚû444ß8AÅÆÆÒ¦øÆ àvU¤µG5¬nSÅZP•K@¤§¥€@ G[•æg --ÿ§íkïnq¹:ÆÆVŸ®eÂ3€vÕµ8%Ïаâñ– O‹Ý·0 hPß®v&\cÇãÝ,dÆ&§æÊsÅ%>à”Ñu¬÷h+S½& eÈ•¿R# Ó–Z3yY™ @«ÇÈ`Î/’ RX7 çž‹Àz­ë=ÊHùhalVP÷®Cù»ÚåjaPáåܤsçã³oÈúùmìnãÒ‚}2»„¾hhhhhhhþ{¨f–Ýêw¦!AÚ6¥¡¡ù¦¨}“ËÖ­¬­;j}Ѓéç R–„ƒ@àl``Z‘KËÆ¡Yö•Õ¾“ºXvi×kó׺½q­à 3ñ^{lýÈž¬ç‡×nÛ‘Pï9œÕØiSæ6cÿ©ïÁ/^Sg8=©Å)eÞµèØb€Ò;‘WóˆF—‹0Ø, €)è[ ihhþ3¤©á#»w®C·a[^I~ôÒih~*ªã«ú-Õˆ‹6 ÍOÅÓ-Ž[6hÐ줤sy#²¢@8O pîÔÉkË–±á–N·àó!܇gã¼)DÌ:Ù1¾<Š'¥´H ÐÄáWOWö«·%¦ú@qffv Ç͈ €ši^=}.;¯_äƒ]W“J-VK‚ Ÿ^ŽÞ{ù¼kè¶¡æ,“ꄲ(i^×!ë@$,2w­Š!µ)Ë­ÀÀ¾ïP ËÝ·# ¥qW^•Óm€††æ¿BÃ&àh|ÀÏY: ÍOe5>Т¡¡¡Ã-€³g7ìüð!½â5Çæ¦U±(EO.Ÿy8°û€p¢(íFô¢Ð¨ûÅ$ ¯vm{ÜnŽ“ïÖž'¦õ^»ãhÏÅ#½…;Þ¿«¨:#UV#M™ñëÃû‡´µdìãÛR€²8» ‰ï¤M¾,EÞ‹ë«G>§>q§,ÿ$,€¯ïR“¿ÝOUn:­Ç,ïa¬%Ϲ5oÞ½B%Ýhhhhhhhþ,›5“…,#£ôœÚ44?'?Ñ{Pp ŸK‰‚”­ÝÝÓǨúQ4444ß,ô£2¾©ñ¢†¥ôìÉws“dfZ¬\™c`ÐL.êêÚI(LÏÉi†a™sæ°lmA$"‚ƒå3fXDEå”–6ãr…7þþ´=ih~†þ¥]ECCCCCCCó÷໹óçM›&Š‹aT”Åœ9£¢@’’Â22²X¼˜£Q£DQQ 3š8‘6 ÍOÂOn)2ô1rmáFomÑÐÐÐÐÐÐü;ˆ’“±ýû…QQ|ËfÍ@$2œ=»0&¾XY±mmŸººbk×Z6kÆ÷òž>]†å0´éhh~ðz~ ˆJËQçp´·nO‹†††††††F–……Q``NiiAèÇÄH„ÂÌÀ@‰Ph!—ç0¥}úhõ题˜/:ð]]aíZn¿~¥´áhh~òp+ `WZÚëWwU<*Ãа¥·wè±cËh{ÑÐÐÐÐÐÐÐ| ·ttrJ+£'á¥KŒ­[ Û¼<áŽú^^…GŽ€¡¡$%…5g”–MœøÔÍ­SXØÿã“3”eØ;…Ò‚G¿ ç;¶9íÄï ”*ÖŠ¸};úzBT†ÝÓ¼ÀláÐô§å±/n4h6m/eYnfAÛ†††&‡ ˆùó«ÿd,«\˜Î10 ‚ƒ¿˜™Y„…MœÈغµ"$ËÑÒ2xô¨V¬%Ëen™Êïh£'èÙväOÙÈÌVÿjûZ˜˜!L|õ,úð÷f í.G„‰Ïç´c«¯AcÒ4H•ž‰ËíØ¸Ù¤½ÂÄ áÑ©-黫>JoûÙ T°rè9nÕ•Ycò’eÏ·û¸›Ç?ß[MÛõðYrüui}ïG¾Ú0xØÖ×ÒïÖ»dù‹­ž'ß®\ŒV|Šßâçî öÝǬºò^ªürv˜ ½ÃÒå5„ˆÓNÌèd%X9ž2M\{…±Á”ÊÔ¹ê(¦¶ Âëǻ٠«v½|7Æ}T4Fþ?×–2Wƒ¢ˆ÷û=UMÛo¶âoŠ¢¡ù¶u¿LÏÉÉ!ˆz€ ‹œ¤uX,[~\':¾\F×¹ŠQú¯6xÌ@¶hsyg€)(ú]K_ ýÉQgsví4Þd^Q½âÁFÿ†ÀÿWËÓîþ á–¿ÿŽôôÇà0@³ê[G€yWð.''¥2Ü☹´ad^?·?ê|\×Òmôƶµ¥ÏlÚµm÷ÝÏ&ŽÞ+#bBÛjÕ|þ<ÓfÜ¡ÚJníŒ\¿!úR ÖÖñ þÿ`Ñc:ë À²3Õ†n?÷•Áè´þÊ;wîܹ}ëÚÉ= Í :Ö˜§¬È²¯Nn2/&á^ä`#ì;ªéíø›—O¬Á[<Ìï`¦úÐR).(’}¯n%Å™WVŽ–Z\9P¹'gL?ÊœxôIjjò© ½‹3ývÿÉí³ëîJn™aÏ6ðÛ Õ…ñó~+^w K|ýêî&—˧l{Yóµ€ & R¦FPB¡XšŒ*)Š[2+šâé›7OûBtðükùʆäÿ ÚRæjPYò:þ½Éø7+ |÷€· þ÷DÑÐüˆ±¤wÊ^b÷¶„©)aï^¾a£¸-P¨l€==DÏ#U<تݵ•ž µÎ¨UÌ\9È3ÙýµW,Ñöè®Ó¾­ÎÈõ̸]ZÝt[ë YÁÌ#€Èc®šÅ¹—˜¢S€^šÈw S]”g²û¶ã-˜ÃëÛŸß§;ôjr2Îhút×µè ºÓØ/J䨅^çÖznO­}/%PYY¨½á¯ë2—QVY_<´³îÄ+¨ è{Ö@žµú¼¨ÎR_ÁCöŒ:¶=k'~ð¼DYl$Gj¹Ûë º}¦³RËÔˆ¢Î^êÄL×µÀy ªQ¢<ãáÌ[À·èÙ‹å×KUSµžBÍ¥øæaÚ‘¹Hê&í!Kq)‚ä%r¦õÓµèY·çû‡1òˆZÊT DU*¨ Riù"5ΪQaêuí©¢Œž@ ×¾ŽCåÕ…ª±|ƒîþ‰Â-©´¬¸8œ˜5øšòââ‚ÊW^ì˜ö>cgíŠ?zèJ.S ÔNE‰÷—"ü²pGÄs¿ ÍT§¡ˆ–‹r“bÚ½yÇÜ‘><¤)M§\XךxÅd$¦ìwÑ⻬½u9µb¯ìÍñ‹:7eÕN£k;9^˜˜q¹˜*ŸAÃä×e[o½MÌ&f¤Ÿ9Ò–KùÊ1n×€FzG0Vѯn¹8à>±ÂÄŒ7»]¿”.L|›´v¼¡‡Œ¥ghdddddÜÌ¢í¯ŽäÛ{Yeѳý³v°¬úOßó¤H òô0ç‘‹ÜlA둹Dê¦ÑC–>—"D^bø´~NÖu{ÿ°„%$IÅ4x¡JY~?¸]ím»¡1yJjmeiÛzYu]ûJªF[Ê\ù Š’üyû‚™î?ÄsШ™Ûâò4XØ_E÷L4?\ù¸¬ÁІ‰ä Ó­ÆUI~Ô˜9ž“Û§ô^Jþ“h±r·vðYT5$>jÌôc{ßO-HŠflÐÚø ©ˆnæÉ¶Äç?¹TfšÀYzUP‰’«Ë@ʱ‹óµWäJö*ïȯ3iáÍËn&méÂø­ŽØÂzjJ=›Pcs Ŭ“Å“Œ¡SXaÂv9+SÃß%ó.~˜Zðè°9¨å‰ÉT”Ù6LY¹ )UâT©ê‹Ò–2¡Æ5Uʈ.l—auŠÖë8¢!wÿ\áŠâ8΀/uæ"Gp¼êê'•„3ó ðäè¾i¦¥÷ÂgïÎ’ªí0x梀¹‹æ.šâ׫ö•4ãö…thÑ¥¥†êjgñw_€é°Ý‰×ÒÒNÚå×CST*Ë»º/ J“ÖLšã·éM¹R’rÅÔA—ËM».˜Þ¦¤V5ƒ1jºš‰´ š<·;÷]üüê¨N¹íXU¿•<ܹa~ø+¥Y×-¿šâô¸ñ#"ÏsìX8›²ˆçfŽßœÛ'ü^Jê“è±ÊÝ~Ág?„èñCó7ããïÇL2fu KHØÞ••yÀßo¯Ì{ßÃÔÔG‡Ç!'ûF¦Ë¾&¾±m˜9"zœÀŸq"îÞí=½Þí ɺçzâý³“ÄGÖÍÉ3L_|×vÅ—io_Ü\f•´iùÕÏQô4t2.ñaüz»¤u˯æ)>žŸéQìµû~jjRDïŒ S7>«Ó¶>4,Üû7/INÈ–Eé×ñ!»"¼tyý£âx›()Õà û. _;¼9ó›s$ÃÔ{ÏÝ;§vÒ¯¾RÙm¶¬Õ‚ @–¿½z»Ð¼‹@³z‡)fÉö+f8ÕÜ'D¦•5ikΩø–cÚÚ $5]å8[ƒ ¨•Ó©QŒ*—–Ë‚õ>®àhcÓ®ÿ’Œ¾Bºðª”çÕ·&Õàá¾_õIjmQã_—‡¯R1å«+²Žo³ß6 Jññqò¬‰Ã¸ÇNî ²~¶bÔô39ùEŸ¤ù÷H÷ ¢˜Årì¥ÆêI|g;]÷‰šçÒúv¥ŠÀyRK©)ÍñU¾9Ã*pb¨¡‚¦¥ÜŒ¡ô.3À@ÃXa¥‰¼/l”"Káï+ç¡€p>ãˆ7§Ÿ¤‘—‹çtV¢åÈ'i …ç!›Ä¾à1Ç™‰’í狞Ÿ–4çúòk/å(éÃ`}‚€<=Ê„žg|Žg%jJùÊõpàZËfŽ%f|V¹Æ YOt$sG|Œä·G/ß²2hœ0CÖJ´­äÃíéh…(fJ‚šì ôÆbí™ ò-{Ëxj$o…‰Áx¢QWÃ|újúwArbY¯ÍÅ‹|<ŒÔ¶•.žN¤ŸÔÈ}UÆDK}î:©¾ù½~¿¨K Ö5UÊØt×uhu](× »ljÇ\®Ž±±Õ§k™ð  ]õäàp”¾Eó#"“ƒ•‡8ÄC å¹ø…0î<.ÿF‰«º¾Œ£lίœOéµT"Eh1¡ŒÔã’€ € ¤& E ‘× ÊV6­ZmâP„ÉœhÍÍ'ñ\„ü?öÎ;,Šã}àïî^åîh "(ÑPƒŠØPcDÅ‚X°€½ÄFÔD„ØP F؃#*ìEA x*ÂÝÑn¯ìíÄ$ߟe>Ïã17å-s3óÎîÎ6q¡„…4:ï^n²%ÎdJ„P#Òú/­5eJ¬x¦ž¯õó¡Ç`ËÛЮBßÀ«,œzÇò5¯BB¦…² S¼Æh Ê\¿PÅÀ¦š|‚¶–ªÏb­CUøËW ŵr ¿úZëPÌŽ¿uíM3'@×ÒŒÕj7Myÿ´ вŒ°¦,KWå´¨‘Ž]ˆjuåÂÔ±W1H9ÌÎÂkÉ 3âš a˜ZvÍè¸Çµ¸û ·""&øû‡=?s»pf|Ðà-@@4H$í¬­íK=kVßJóîmÖýKYpSÞ1p¨·y[W[öÕDZc¿Š5¨N\ϰrNão¼›èž^L7\à¦Îmf]9ñôJ ÌZ ŽI˜ÛQèÔ¶+ñU¥Þdç¿úàZWÕý«V?äŽ éfÜE,6Qi¤Ë=:Œí?ÆÇ½[ß]|½­}¾‰~Tõi]nR\â"7o™µNNÒêi Î;ðó(çÒ›‚iÅŸÏ(»®éoWÍZWÈU‘Z@€NU¬®‹Õ=Ã?¬ª¿Þ]9xÛrôñ‘®b@«Q!“xï;ÿz؇jr+o-ôók¥ë›¿Å¶ýiu433«µ*ŒÍ/_ƒqlÙ²zÞ IDAT\l°„×$ïêB >äIf]è"/yŠLì´Cg’ŽóîH16`ýoV‡½/.au*\FSd㘥ڜõw~FòR%xvþ¹},÷N[ЪSÂàxÝŠxÙ@G§ˆ½ƒDûJ¤D‘‹r˱bL‰ß="˜¶Bp¬{Á0œ!q¤AåNCÔ–#xIIôu‘jŸ `"[ÛQuòLIS¶~Ê$¤Z]ÅÍO´© ù„L 6`öò¯5%G3)ÁXë2NU->Æ€£ýáç‚®çDýC„Ém {Y3³„ùÄ£jšŽÀ˜=U«ÍË¿7 ©;x¾LY€É_â‘Z¤w+ö7œX%“’ÉYcí+ÄcÊ èÁ¤x=ß°Ýj­h”Ñq^µ¸ûs_TVO:xpIãm_4eùq )`0ÁFqëF½:uòݸq¬>×5 áσqÑ߇® Û½ÓÛ@víÖ_ÆžŸ'̘0wÙŠc‰3š<ÛqJj¸EŽ›w›}òÏ££fÍ5iBæ€÷·.½Ò€N¥PÔkÓßÇ£S ss{+  33«ÐÄÞ¶,>®”GD¼S@sßOšú]i&v³‹ƒZ]‹Ûµâ§£¯€gÂÁ™.i(n,øfÈ7]–]’•Kˆ›1¶«ÇÚµïÐAë‚:âP”|öQ š8>M#GGGÇÆÖ•m±³D¶flÇÙg§§§§§§?{|3ùÂÁ@'.ãØÇ7Q¹e5Rò—RÈNĪû@©}}l^p¼0 þÞ£»)§÷†õ³%€9 a™Úˆ ÿ•¬´­’G{Ãw¦*„5Hktÿ2él–Eço8”ÑÖK…§ê,ÞÇŒ®ðþÖ LëµõÔ–ÑÎ&åŽ)þóØuº£_{quWâ¦Í„yþ*Ý5Rf?|oÚ¬‰ˆ¨{†"Xõ9Ç*îÀp:ŠfòÏuÁ¡ß’+q&¼‡9냤eÔQâàTsUºü¤ÀîÞ+”Þö­~óä-nã"qù€ªˆÏ³Ê&ܹÓL^b=ËÄÓns¢BùOê«»;Ð,±ÖˆãgY$þäÿ—Xé@@²b²Ši òY;÷ân#ÕõþÆOƒæàøÛ—xnqµQEÅŠŠàd“P”Æ]‹·©¶ 1ŠM[‰iœÂÒOò·¥a&OLž 8‘‰¶µ¢Y\Z̦ÄJÃÛQågÇZ½’c3LÕ„´§ªõÞªý,j){ŸÈ?’e¸oåAºåó6aP˜ü.?4œ›Í¥e¬J`¬8<í8º0І½h'Ïè ̾aÒÔ¨§jµyYû>*§—üU‡Y ŠÒ8«¢ˆ&¾jã7å×ZaY0_›_½Y«k˜Z!£ãìêPç—nÀñãëž×¿ÏÌmÆ{uš2xð÷ݻٺuzÅ2PúÇÕL~ëÁƒÇOèínúöö/«Fÿx»ÐèBËfÀ¬)“»5ÞÙÿÃÿµOJ*åÔÉï$»©’ ð úaÚLÿÆo‹›9põŠm‹¼ý–rœ¾iª}Ö6þ‹¢—ô¨¯-»hY)ÏWl饵ÑDÓQ‹Çv·R•]¾Òdå×ë7yýÏvE2yp~åäí÷™óÒ•¼•f¿Uìÿjž1¶«Ç¼å˜¥ fõg_ŒýnÁï2š8>óÌÆsdë7»Ví £hµôò?/ÿÈGFÆ ¶}ŸaN/·­:üXAQEiGWE=oâÛïï<ÒD«‹J(¶ÈJÌÅ©¢ô“·¥i5*Æk¨¸•‡Ÿ[þá GP”üîÏ¡á'²¹B»:IK‘yo¤R©4'ëÙs[çNÙü¦óÜ­MŒ¶NppÕÛ—Y¹Åæ ºâŒ+‰—Ò>§l¨œøYã£Uã6¯ÚHõN*•Jßä+u zuõ>)éÕ²â©-]qÆ•s+(~s?oAJøöëﵚÜÔmáWÍ|F´àÿ F"¬ Ó¬ZŸ´î2¬Õûýëþ)§¨¢gÇÃwf9ùö`|1Æ5oÐоvÖ|˜´EÏS/=-06ª1–²¬¥*\Ô¢‹mÖÁÕq”&÷zÌOU^Ó{6úºÎU¡Aññ‘••µ`Áý!ï^^^›6m"¢îp‘æ§#…“¬Ù[fм½Ì}F γɨ8eÀ,Õ‹CÔ²HQۯų.êÆuÓé‹á&T³tÁà^bÏ‚\¿Â_Ýßxrœ«ëÛ‡z*¶™¨z‹¡k¥âmmÙv$5±0rå8¬8À‰=­ƒE«Ž¢[ÚÀ¡º‚'¸Ù€¢ÕÞøF §f}×âƒÂ‹úZÓ¶«'VÙ¤úŒ JŠ´~Þ:ý Û„½K)J²³³{÷î Æb-0–៯8ÿÐÕ-…[ø‚›w;ÿ£« øøÐ?”UýsÍ™ÓÓÓ?*-ØŽÊćèEâ ·@ >&>¶À @|làÈ@ ÿ¬„†VVÈ ·@ 3š"+!ó1  6̺|9Ê×7vÒ$¡Fƒl…@ p @ Q'r/_~°zux¸I“ª_¿² ッÖ¯WÊåÒÌ̸À@"+  @á@ ¢¶X+1ñLpðÝä3¤ŒÌ¼{7~ùò† –+$ˆ¨Q£î&$2I’ —“RiÂúõÈn ·@ µ ½w er îÆÇ§ÆÅé¿"rscG’>~LÊd@’¤RI‚l]\Ýn!@ jÁcÞ<§ÿ¬ÿ/9*J“•E俯ÈäòÒ/xÀàñÌ===ƒƒëR³®˜ÈT`Ÿ„>!Q¿LƒP¸…@ @Wœ“© >¸Èÿ¤B|*= 9øW(b³›{z–…Z$(•¤\¼? H’¤˜ÏóÍÅ|ss[[Ï€I¾ááʋ«s8§‹;:[J$–.Åß…s²HP±Ö E<þL` ªö¯_KÑá·‘tÿ…HšL~ï–¢øÜÿ­H4v³Y‰¥ÄYtèõ:ˆQòÿ¯}„]…[ÄÿÍ›ßcõrw‘H$’¶^Wžz¡¤ÿÓ¹êѺÁÃ"«Œ|]r;¤“DâvWÉ\¤–âÿ‘TŸ"tɃŸŽS/W,u¨7‡Gz…Ý+·,]üÇZ/—ž›ž©k g¿‡ :“[ÔÊyì…BƒorâÜ)ôeÍ,¾:ãkI%\¿ñ=þRoï¢ËSš~׺ûÈŇéþ-ßÕ–.¼±dHà‰7(C jÇ3$„ÇãI @’ T‚\®$IàÏãñÅ¿ü“pkÆŒ˜I“6L™9kÖn4’"¾$´9GúOˆ}ç>{GBÊ• 'Ö79;wðôÃÙÚÿp®Ræ+Œ.ïiùµ]çù½{²ÎĤä똊ÔXü?’ê µ”™g—±%­À`ÖÑåß8“ëÖ߉_C]_;{G¦¶Ö5]tgãÒG}æô¶&êÐ2£c{l¾zÿþýû÷ïß»ýû™ 4GçÏÜ›YzJ4Îî´ölJJÊåK¯õ'‡ ›²7Sýïø®¶ ˜©ûŒ š¨e‰¹(àB jC«4âH’TÊôÏk<OÌãy[wëVµ¤’¸ñJ×3PéÕŠ²·§\½JÖ…+¿Öàù$~ø;Óø|üÌ$ñ¸ƒXnªI@? '‰¥S[ñ´-ì\ 4™ü¾­E?‹úz‹{+½’ûºÊ ó4dŒüÖÂIb)‘Xô à?(ȿɟ5ÀÜEbéä.ž½‡U¨3š¨øƒ?w ¹³ÄÒÉ]ÌJ1*¢!N…Š:·´”H,Úû˜þüÓs"¥IŽÿ6Í¢Ã|vq©åYa-&ÅuLjVÑ8 Ãîn7õrµ”H,zÏ䥕0[Ìhñ²*~¦…ë“ër#U쑱6 3Ûžƒ¥­7òK…1u‰ç&}Ú‰Bfˆ%Cv᥋ª’ƒ´L’Se^«E0š¡«Tc)‘X¶­fyMy£uî~ŸU¸5cƶ^½&;·íäÉM§OG¦¤ð÷Cƒ)â –§¬\q¹Áü¸è ~mmmš;yýÎù´îçR y¾¥WK¿Sù4€þ"FË>Û3•Ï·ôi7"d†§³ÄÅçÇy=Ë>Ù•‘÷Çî¹Û;K$NîÞ3wÞQè@“ÓÏ}ø²Åc½ºy¸»º÷ Úû¤ çðwÓâóg&yŽ;˜SmqK½ýmgªùÐÀÙcl®ÇœÖ}”A‘¸›*×)˜õn7bÙâq}¾õhÛÊcÄÚÄäm==ÜZ¶²,9—MfLßÖ~Ñ×Û§÷·=G¯Lz­¬,•&#Ú«YçeÈOÕ±ª´MC¯}ýMP_K¶¡»oŸÊùz€‹‰ÞÔy—ÿx³Ã8gNmµQ9Ç×&9LömĪCÓL,ßÞã™èŠê5ë9eZ;<ý÷—dù—–6¶¶¶ ì$m¼¦müyŽÃíÍ‘· «N~Ù§B‡wn)‘HœÛûÌþùA¡Žªì;Z™q,lä·­œ$‰Ä­OÀÎò*]Ž¡ÏÖžS:<Ú´û© D­x._Î77€Ò \¤>èR*I ô 6mßž¡˜P3¦œšmûô!×€y·’˜Õ*¾nø¶B_ w¬|?¥ËäN›ÂSûÜLË¿µÄöšNÜN¨tŬÛV%OÉÏ.jsE0s¡­ºÌ a»,“?LÏ{p¡¸Ù þÒs¸æ wÎ~oÁµ´ü1ªŒu¦á`”±Äñ&9½‹~’w'N©Ûa6û8NUoô æk ªá˜H)XWEÊ#ɲ›—Ššßà/=‡«_rçpÌg)î§ÉâFAB*^}³ºRJ&EdWLÂ’©•óŸ¥É7u"6.ᾦ@Á”X¶‡¤ë0FM_âþQPò'7I¥ã¡£¨Y¡“qôÇ…\õÆKywÎÛ_5ùé®eªJc¬8­!N/4[–Cîú¥¤£¸Fါ{¤`rè´Evu³†g¤KPrÖÍFÅR‘Ãt¥¾ *õ%“äåL-2É`¬«” #?µYMT³<^£…©Úüòi‡[3fÄ\¾wþjlFó{¹YÒ¡Ïï•$ž>9hÐ<4˜"¾”OO_+j6´¯ƒÁšœë4eǾ޶ƯcPòÛ7-»ú[ä0¬ìóÆ®÷¾¿!§wôïOÒîÄÕí˜2ûø €RÜ»* :’œzóÒÚæ7Ö,ý=t[Œ¯…È;6y¿]ÕF´Y'w?i6~¤é€‰®»âž@Ø //2ªýƒâðæÄ¦FµòÛWų'ÿ~ygÏW;C³‡î<ŸzíødåþU2Ô +~rÛjîÁS§Î^×æÊ‚™µ¾†R±lúþ½zxcΧêX¶½ÿÎ+¿mÞÉÊ`5@Ü=™é< …@%„.y68|VamÏk^Ú÷\2¸½yé J]›æVqߟ“ÇÂT-d@›w÷ðî{´¤Kcã÷\G/ïÆ…w¯f©ªL~KÃ’%+/>y–öû¦N7.9ñ º‡¿.sÏÌ+.Ë~{˜þìÁ…%Ín¬_zž5¤}Lšû´/<÷P‰Æ¢6Š(ʱM›ò33Ê‚.9€ÒÑ­M__æbíµN¢!rWN·kná5Ip"«|ËNä=n¤\4R+"h3UÈLêùn† 0žvÚDL¨9Žzz”m¸8fÙ«¶'wÖá%Ø[9mmŠäbïRywÌÉù#(1A‹Û*·*ßò™ß]⥠ÈE5–,:©çŒ¥oîc¿£˜5vï‹ò®€[h{|…½}‰g&ñž4&ç¦LÚÑ»dnºúh[½þw¶ Š`|šxÏŠ?ÄI}A·](¿ÿ+ÙSb9¦ndo6ûÀŒìÞô Û‰ŒªY£qôÚM˜¥nafÍ4ÛcožãÙ Uqž\5R\‹ÿb6çªfã®wÔ"<Ô¥K¨j¸¿ÖΚ²35ÞY«I^~Y´V3µa™0ÎÝ”Õ-_®Ëßê~ŸI¸•ž~ëQf ,8  °ŠZËnÜ81cFL¹µëu½)áøcij†4õÑqûVy5d¸Ü3®HS3R'I>l)'î~HššQö/ýÏ]&9›ÖíÁ4–ãÈDijF²Ÿ«–DÄ‚¶0·˜°´7#þ^1B2Ü¿ƒµ]CS¢ì³ ûN\ª`袉î–,Bèä;g¬ÕÍ}—ÞQ¬f~Ã[‹À-Üz|…½}™_ÃmŠä“ûßtšÒÛ– êõ˜Ú½ø××kº›zwé#6êëj†c‚¦íØõû ïhM·k3â/¯Õ´‰mE8`Bבã?=z¡Ò Š™8zôìêdöÉ>ñ‰ l˜Võjáý“Ïš hm†hÿ:²`õ»1ë¿knRëáM:ùs¬Û73-ˉ»†=[Ι}s$xÃi ÁÍÒØëcÂàxÝŠxÙ@G§ˆ½ƒDû€6µ¡!ŸiÁ†ìÑ^þµ¦Ê~ ‰¤—­Ží¨:y¦Dÿó×È©VW¯ˆÇÐ(«\Ôš¡Å´î!!×O°:x#Åuõ«tUë§U§ª+%R¢ÈE¹åX1¦ÄïL[!8Ö½`Î8Ò r§!j˼¤$úºHµÏ0“švÜ ™™,FŽf:©‚±*Öe6œªZ|ŒGûÃÏ]ωú‡“Ûö²%“FcíFº ÓG,L%êâ ¬V˜2(z0vß°Ýj–¯hÔ´ÎÝÏÀ/ŸÔvo•¿Uªâ‚‚a£ÎÜ3þ› ¯`8”†Ò?»Uü¾˜bË0`‰lÍØŽ³Ï>NOOOOOöøfò…ƒNÆ ¦¢ËKÙùk`tÒ¥‹ÉÉÉÉÉ/ì™Øà‡ÒÕŒ-þýF+Ípo² ôêhrŸ½¥-×yýTQ>9ýÀÖ§½9êÌûïæÞXÒ³¹DҬݬ۪¬(ow¿¸£÷-`4]‡3Ãjñ àüzvööMÜü»á›´5cfÿšcl×Iý2él–Eço*Ï:¥4³ÈeÚ–cW>¸t È6eÅÇôr—úŽz}l^p¼0 þÞ£»)§÷†õ³%Jw êÔghh4, 5ÇZû'OVÊädixUúO4|ÙÑòØ€€¬Ë—«”5ë `Â;Íäà%Ö³L<í6'*”ÿ¤¾º» @spüíK<·ìû¨œ^òWf)((J㬊"šøªsT¬¨N6 EiÜÕ±xë‘jƒíuF±i+1SXúIþ¶4L£KÒ-Ÿ·á«€Âäwù¡áÜl.ÔgH¤í+ eONlþå…ÖøÐEØxŽlýfתýd­–^^ãçåùÈÈ`AppÕÛ—Y¹Å•Е&îº.ì7¦‹£m)íGŒ’díßu»®T¤ü3üF«ÌpFE$e“TQÚ‘Õ±ÒÖ#=mXMèŠ3®$^JûœÞÓD¦'ܶðîh‰pšN>p)¹”c‹[²lÇì<±y‘kˆ,˯êá²W²Z/è×ìÁʘy×6O®—6oÙÉ„@‘ø=0Ç IDATyo¤R©4'ëÙs[çNÙü¦óÜ­M*O¢òÔU“'„ÈTÏÚÖŠÏâZˆy˜A—Ш‹J(¶ÈJÌÅ©¢ô“·¥i5*-]§>£+y#U[4µFãaMi¬%«t!< @ê_o¬qHylp°&+«Ò_¤ùéHá$kö–™"o/sŸÑ‚ól2*NÙÆ€«ëÛ‡z*¶™Ð9’;¶«`¯Y;gK÷Ñ|ÝÈÂØ©]+hk˶#y¬‰…‘Ãtƒí0¬8À‰=­ƒE«Ž¢[ÚÀ¡º‚'i«ŠÜ¡äì3sw¶è8ÝôÇ‚ùn4Ñ€)ÑŽŒÞ¥5íälÙÂÓôž[ñ®…c£¢Ö|[ÛQ½Aõv½¨•³…ÿðp¤ñêwV­ŸrdP7P´ÚßècáÔÌ¢ïZ|PxQ_kÚv`õÄ*Ã7ÕgUR¤õóÖé6£j–‹Ãd扜©*QÍÅ¹Ôø5J‹³‚—ðúµ _É’Fº#uuPídÌP« -_³…ëTç'BÕ› …Bó š½MÊ„?Z—/ö€‰NdcÓ¤tpIÝW6#hPßošÛ ¸ çæé¨î–åÓ£1xýìUZ´q‡Üãð$¢¿×¡,-€ÐÍyÁ7C¾éß¶>̃Ùgb¶ÉŸîÞ:}G†6ˆ?ß…ƒÌqÓÈÕ1a#6æi3sìâ?g@#6Xz.ñšîÓvß±÷wãºYž¨aËÓnXô®Â°%“;-—Q,+×a»º ÞÝa ¿êÛÇ~J¨Ï°WǶ,½® ~ñëî‡õ|–9ó „³ï7¦uøÒ˜ Á»ºW™oX¼ÎV·A+UìÐÖA2“&=&ÇD³#0aEµóLÎ-›×õè…Å®ÄçágÕ‹³×LûN¨O°ÌêÛ™•N7¦V<‚mfkWO`LS\ܪ{ÃÜ”'Š@‰uM³ÕìÁ-VU†0Aë ˆÀ”A+çíö80Å@§¹6¿ï7¥Þ1oê1`ÍѹƒìªŒÛ„íÀu«¿ÚÇu øv#ÃW÷µÆAWÑ%ÎN ¸<­C –¨¾s¯±Co…?É*æt¨¥Ï(Ó¯ü%êÐÙ…[V¨T*KC,’$ybž£‡§\š™ùä ”Å`JàYyU‚Ó@=s³z&Ã@£ó•? -ý˺KI̹’J‘°tž?*¶l42R‰4A»eA{L#ôÿwPF«zŽ9S¢Ø]qJY‡F EÕ&<Ô«­L¬8g‡öŠËóÐæ²^Ú*¾RÌÅ(üàPîŸõ誋ÑêõQÄ;Lá]åälÅØˆ,  ¿â©-jÖnÇJÚyîÉóÔOæLU17‰ß\y.­ô[á+-´ .ç•7Z½K@Ó’ó»B%1I^î5ª8ÌlXM˜*–g¡ŽÝïS]VVù;"b‚¿Øó3· gæÁ÷]ÞÄDƒDÒÎÚÚ^ï4Sç6 ³Î®œxz%f-Ç$Ìí(tjÛ€ý@ÖÖÙSö½ÑßC“¹ÅÜF•Ú(z¼ÿxÞ í'O7kŃûÒ+ÚFà@iËöÐiÆjÔÉÜ6kçý‚’7ÿ|U¢«®ž¾lé3 ÷a8N`Z-EW•§¥è YÒÒ€{2tÛŒ{·.¾ºøz[û|ý=Jö©€ñ¿ê¿`{ÿLßñ›ú‡Ÿò/û3` @àù‡¥zÓŠÏ„Ø}JÄ©)•Çèi‰§•ýaæ÷PÈ{„&”O«eƒZ³ óOƒªE~‡Óü žAÃâÜšõ9ð°Oég“nÝM&ËÚóÇÃ[6†"Ãjg$=›ñéû–çzõ¾þc‹g2y_ÜïÀÃ~5×ÂvðÙôç_¯çõ÷±xDÞO«<±Yù5mÔìÁè«ô®æ³Î<›¥ÿÜmÇãô:©Äièv êä‡Wò]Ðî1&ލKŸ€¢Ço‰|w4ç£1`„Ï ɲ [$O,vñö.—ɶ ’™™ $ À’±Ø;8˜íà`XDbùEXàa¹à®÷¿ÙèC±óŠB)ÄÇOzzÞçnÀÁƒK šwãÆ‰\¿,­‰Ób&”H"iש“ïÆcõÓ¸y·Ù'C…7_ºþT†9tjÎxëÒ«â— —sü&N›ý-DZKÏoQãwWi|w6{ÂècZ¤í;ö—¦’ê—g.eø7™³8TsêÕÈÓ›·?œ¡ÚÑaª¼Tî­s“ Œ)«~yîê›ÀaM¿ ˜–wïã×àuâ•—êVõZõ¢äöšàÁ‚’äÄ'%¶¥ê¤TÕµ˜ÓŒÅA6÷âv%œô‹ÚˆgÂÁÑÏøl  îµgMüË>ß}õ¹^ú¡Þ]ØyÛuÖ¡f\äoÂ(úçµô—˜gÞ¼ùå˳߿€À_ â-Í”_,¶qkî ÿê_Y>j2ùý}8ß]TøZÿïÔý§êðÛ;!1œ…ÀµÕ .Zà£ãaÿÏJ!ÿëp Ž_?cÆÖׯŸæ±XkëFÖÖöe±èäwŽÝøí¿ö,J‘þ[Ü¢°Øk4}wë¨IêÐï}†…üè@¾y|jà תg ‡žž×¨¿&V{½ä^̨‰š%? ºåËs1Ý8Q½Ú8±än̸Ùì• ½çnè êw·÷.ûaÕc¥M+3çQ˾mhª}}y×ü¹—ß›ù”ª“RU†“‚¬üz'¯ŸÈÐæ>8¿2dû}ôæâ33m?oq‹Éç}7ô³&>C颻Ûöò7÷ù,µC þÞy.Ztfý R*±£Ë¤;ßé¿-¢¨€¸_3SS=cëâ6).®Êõ++¡0>8ønr2@I’q¾‹Q•(D (Üb ´¶¥§ßüÙ­ôô[2S`1ÀA€P€H€ýPÔZvãÆ‰3be@| xÌ›ÇãñôŸõÿ%GEi²²ˆÜÜØ€™\^úx<ž¹£§§gpp]jÖ™ tˆô¿ ²*âÓ·¦M‹zþü6xÌ”¥º,€ÜüWÙÙOô ì&cÏKS3.tãðÝÜ’¦f\ÝØø)©,Ç‘‰ÒÔŒd?‡éÐDa÷uiÒÔ{‘­ÍþNÍå¥LPA|ÈPž“© âK ˆÍnîéYj‘ T’ry\pðþ€ Ib>_Ì7óÍÍmm=&ù†‡—¿› Ô9œÓÅ-%K—ŽâïÂ9Y$¨Xë‹"ÿËÒj2ù½[Šâsÿs³h_ñúµ~û‘yËÀªŒ¦øû#µâÿ7ÜR©Š ò §ò]@+Ðä#Ë">]èüS~ÎíÞþÐ×,ª­<,ⱊqš{ó{좱^î.‰DÒÖkâÊS/”´~ÐéÝrh|®îCGêýZú~k$Æ£å #$ xüt;­Æ‚ŸÚ·—6Nñj#‘H$®ßŽYqö/ÐÊôà º7“Hšµ¼ðHº²âµstɃŸŽS/—.ŠjÈYSíÛK&÷jÝL"qé4,ôä+Õ?”ªR‹êÞVÕ¿½¶<×Ô*!£5·U½ˆ/ Ï$ $I‚R r¹’Ôß<Èãóx|1Ï\,ö pè=°ÒËŽÕÄöɦû”ꥇäÉÉòCKÕÔ!Ó+ØE4€ËW|ÂoÂ$¬Õ‹6”t}dbÕfÕÿ±?R[!þÃ-g±Xl¨þÞˆ\À4‹Uû;þJ¯b=‰ÚvòØSiê‹?6MomRqe×¼õ´Ø Ò1q‡ÕÒ¤©ÒÔŒ§‡ö,ê\Ÿe¼Œ'¹èTZj†ôbÒj7Cû콿‹½’ôBšš‘“pj³¯‹«C)¢.C¹2_¡f\îç è?!öûì )W.œX?ÜäìÜÁÓgkÿk‘03Ϩ+)))))IÑĸó‚øK)))))'æºÚ÷X´áûÎ"â³w •sdÖÌœIÝ=dyzΔéjåýð)ËwÛ’úøÑ•õ,ý.ò¡R˜dž]>jÄ–´‚²YÙXÎr3¨Ò¢'Ì~Ro¸s¦ð | ®¥å߈Qe¬3 ÿ3V'â3·""&89u4}m 3NÈÒ~ˆ‰¤µµ}]+z~&bc\ôƳY„…•°|‡ýÉæ®ûá‡cÙeétÉ«Âöþ£gÍÔÉLmL #•°úvwø+ziȦ#ûNdV»ÐÀvôéÑ@äôó™íÑÓ q‡FÍj)…ø¢Ñdî™rÅeÙoÓŸ=¸°¤ÙõKϽ“]Y–,Yyñɳ´ß7uz¸qɉ×`7|[Œ¯…È;6y¿á5#åÓÓ׊š íë`p½ë4eǾÞ¶ú|š—r{l¼ôàΙí¯®úé\®ö͉9ã7äôŽþýIÚ¸±ºSf£ysrΔ˜ß×ÒÒnÄôÊX7=üÒ¥8­‘ž^8rYΰ]¿Ìë(þÛ¿n­üöUñ¬ÃÉ¿_ÞÙóÕŽÀÐì¡;ϧ^;>Y¹Õ 5Å$ ŲéûSôêá?þ«Áü¯gl\3ê+]òìÜeY£®_i_¤×kÕ¨ôʺ‰}KëÂ´ç Šmï¿óÊo[§w²*{Ò“’3ç¬XK0exšñçƒB[ç÷{gììææ1øÇÄ;;þRUĪIX!INüâͯû,›ånŠT8ˆYMóêH•ÜÊ`¼Gµè…@|Q”c›6ågf”]r¥£[›6¾¾ÌÅÚk<>DC<䮜,n×ÜÂk’àD:Vùv7,;‘÷¸‘rÑH­ˆ Í\T!3©çG¸*ÀxÚi5"0¡vä8êéQ¶AH`´°µ TMMhD>ü<Šƒ— 4ïÆ¹~YZ5¦ÅL(‘DÒ®S'ßÇêóèTÅ*XŠ9Jà˜[@U¬ª| ”V“¨vH M«JÓ ;ÿÕ׺ªnì_µú!wThH·ª÷>VB(夑k­Fàµuö”}o´¥m)ãj.…ø²*öþÛ|-ì­ð’¼·rÂÚ”NË-šï¯Ç:Åòêê¾0é>Ž×pÔ‘¶0·˜°´73~Û«é„Yƒ[X°À¢ïðæËW>›­þ%U0ôðDwK€“±÷]|¢;~ÇÜÿÈW1ÐvÚöCžÊ†hßýâéqÇI³ÜE´ôe5êëj†ctÓöìä–Ã;Z \› bÿ’©ßÝ©.Ì¥wý=z:~R®¤•ÏöÏžzÀ*(nÂWº8¸¦ÜRká\!ÔÅ*·±m _,•R1æ¬9CAþ»bòQôŽ‹–ì™ÇÏ<±púîÔ^1É)Ó°èúÂÚ~#h+a™`,  –€&¥LñZ2Èj†Ï BÙ•=Zñ£-(s½ÖØ´ à±‘:m ô³ûÜÃ-8~|ýŒ[_¿~®ͱµu#kkûòX ¨·wϦAsçÎkb¹­Ft夺ûþoG丙½P™™UhâiËÐϬÉN}XÔ Ù„ñ#³S±ž_q*߇£~™p9'ÀÏaâ´ yñ×ßr»ôüF5=¹æRˆ/LWp?nñ†#7r°zM\œ„…: AÐ!l÷òÍ[âÖO‰˜¯5÷ Z¾dôׯ6ÑØb;3:MZHiÅ©Sæ¾S‹ë‹Ø„ÀJ¨Èq £uYV>õî—!_ÿb0Âf¼|•O[4*‹y6-\@+ Þ^}íîP|9þF~×ÞV2K!ÎÂpŽ€S* Ž ŒÂd}rý:'iõ´‰ çøy”3_'ò@U¤Öú "5p…\† ã2ä$üоçqá¾hvõ ¦B˜õùq¸» iÀ²)gÄ'z¸sþ }©[nìë-ú`©bIÅŸÏ(»®éoGÔI…Òj+ PkK‡[]¯+oÇ4¶Cs?âK…ÈÊJRNVµJÿ#åq!!“lmM]]«”'™uY ‹¼Xä)0±ÓI8λ#ź5*߉ÆÄ hꞯS`ò—¸F¤@•àÙú3¢±Üg8m¡³¬¸ƒÂh) —‘BÀÙ8f©æåï]”[ŽcJüîÁ´‚cÝ=lulGÕÉ3%MÙ!Õê,ò‰GUs É¢j‰#k¬Ä´î!!×O‰:x#Åuõ+3Æ„w¢¶ÁKJ¢¯‹Tû\1ÉYOÁ×G,LcX¦}¥”A©±öñSf£UÆ©ª šÚÐOÈ´`Cöh/ÿZSÒ‹©N;.úÙ}2üÓ»?¢£§?¾þ…݉‰Û÷í[dkh2wŒYsá%ËcÀ´ÀˆÌ䈓c^iþv;šg»£|pé@mÊŠé×bÊyÍúu¦M2<C¹gü7=^+bÞ Ùš±gŸ}œžžžžžþìñÍä g¹7Aþ+Yi-%ö†ïLÍ£€ãüÃÏ»vÎu¸²<ùý¿~“Á$L Ó§4Üë ïo0ðÇ´^[Omíl‚â¦Í„yþ*])³¾7mÖ„éÜÆœ-:8¥çä:ï6Õ38Õ¼‘ e“3FpŒ¶ž[^jugá?‘ª:Å»Nwôk/Æê¦BQ]€Za[:W׋n&D|ɱÖþÉ“•29Y^•þÓ _vt†<6 ëòå*eÍ:(˜pçN39x‰õ,O»Í‰ å?©¯îî@ÐûÏ-û>*§—üU‡Y ŠÒ8«¢ˆ&¾êÆ\+*‚“MBQwu,Þz¤Ú¦b¬ –"Y±YÅ4Pù¬{q·‘î Áä ‚™ðh[+šÅ¥Å<°ñTµ~Ã[µŸ%£@-e¯ñùG²Þ¤2ä”3$Ö|Y sèK:¿äo8I”P•$Øt*0"<ØŽ*?;Öê•›aª& å4þDq¹UkîjUŠ)ÔE˜j*TÈfåAºåó6aP˜ü.?4œ›Í¥íþž‚ˆŽÿ<¨Pg§¬²¶Zº6soÛý¥¼ØÛËvoégcéÒ‹‹z^\Tµ#™!ïBèä6¡U2ÿQ‘ÔY [''l­’ƒ©â ¯JòÞH¥eî vQQ ÅY‰¹8U”~rã¶4-§»Vžºjò:«%‡ÖjlmkÅgq-Ä< €àબ—Y¹®kAż‡™yÌ îÐ?l쬒§ökmƒ¿{p&fÙ†¬v¡Ñí„ ¯@!löSB}†½:–¸°¥Áå–ƒÌqÓÈÕ1a#6æi3sìâ?g@#¶‘»c »aÑ» ÖLî´\F±¬\†íZè.¸Eîx6Ú}I!nÞbà;ç»™@VùDÒlüšïNøþ¸ÂÇ-ÜÓâ_ ¸…P™ç–ÍŒëzôÂb×{±­Jû%òJ ‘c{E–¥µYýOðöEaó;Ç(h‘Ë s]Ä5&mjÉÉ”Ã:/Ù»teÈ=Z¾Óðv¹Ùßžõ/JUUKé“<Óf…~§ÞV8ˆAB ŠŠŠBÞG û^E%&&"S| ó…•Påé!“–¾<†àñx.ÞÞ£ÂÃå2Ù–!C233KÏ I‹½ƒƒÛûUº¤"‘X"«">€ôôµðÜTÔ‰_d¸5cFÌåËq2R @;€÷ Mzž~úæ AóŽ_L†@ ¡Çº[·`OÏØà@Y¦tRllUé`XŠÍvèÖ¨rQ @|90!*YB²¯‘øøàC„ì§,ŸTv!;ÙŠ’-ûNE)"Eš¦uÖ;÷÷ÇT¦º)>>ßçùð@·sÏy½ßçÞsÎûžsÏýýÌÚŽÐÀãxFRÊnGšÆ‰Ì:ý÷îM —­¼vM5™ªIªAËwxö6jZ3-š‚Tè·÷:ö"'éåÝu“Zqª¾xÉ4é;uǵ¸W9IÙçb6{ØjbåÓ_)Û"ÎĤå$¥Þ\=Ísäºsg_å$Ý?5ÅA‡Q]ÿ’ÖZèmN@ wåÊ“5k‚{uŽš4©æü•–Öñ¹sÏmØ ‰r23£fÌÀ³²Óˆ_4Üb0‚`BÍÆy€É1‚¨:KŽwˆ|‘”‘“”þ8 MÅ1N[Ÿ}Û†uâÞÝ0k寫&îý³EÓaKC§´×»6cÌüÙkß3Kâw¥@ÉíÕ“çzoxAó¦'ƒÝÀÌ̶GÏ÷8½´|{rã^ƒÚ°>¾xSÒ|J‚lËW&ò •cvœ}O4êºh£»¹Ú‡cØ-'ïß=Î>cçD·)¾Û Z :¥qyh©©%º|xOR«q?;ˆÎ=ñ t;Œ›×G¿Q5ýl.];@ òbcÏÎû ᬤ@’ùàÁñ+5jTù[-ß:jÔƒsç$‰D"INι èexâW¡úËLZZº&&Vâ2á@eEì®’gdÔ´jòO±2¥€i6óœÐQ€eéÒÝàUøÖ퇳°†ƒœ§4é×Ý4¹T ýÛîÓ'îÝšô´ˆÌ”eßMHzD»­f·êR4Ho^¾ù©TaO éï¥`5_t°fA¼:¥™ÑÂyM7ë–œ™2-Ý{5g¿]Îå‡;šhœ€ô³›C¢…íFw}0dËÃToKž1—Ì®ªÛý ]<@ çáC ˆØ€ÇÙÚš÷èx^ÞŽßÏ©øú–8À¶±­-òñ‹†[!!¼¼ÓÏÞ+ž™tø° ‚ö††fUR“G‚·%£«cy¸U¥,ÿ—Pæ^2ŒòãîÐÖ¾«‡cW7C÷þOëÒ–¹}Vä㢲Üä§O²Ê”µlZ_µ ªñƒc ùùw†3 ëoïý¹ Õ™’¼R ‹Ï’ŠÊM…HPy§— cVÕß}Ûs)º~øÕéüûïΞU}z‹ [·Ž²´ds8;¦O/ÿ$»ü/6[×ÒÉÉiîÜÊýâ^”¥ø…Ò’Gýàyþ·…!~|höû;thY¯^ã_5#†³ `Ž˜æ&~‹>:ylÚ4¶® e¯/\Ïh6uºÏ÷i‹‡7x{‹5y±_ë’›Q;W.=úØ\H Å ÚöwïÜ©¯™™w÷Âå n?Ê*SÖ· k¯eРµË3×ÏêL@ÙÕØ”2µ³Î]É0Ÿè3ÁÅ®•½Ã¿¦6©k¦µo ý tñ €&³¹“SE¨%±X"EÍ{`útHØ ás8|Ž.Ÿ£«klì4}’GpðçX«„éÝ\_ ¨ü£÷ÛHÍèdL5òQ¼a»¶äþðb䙜¾-yÇóªLçöiÉ‹~W³¥ÄúÁ¼äª:)ìñfG¾À†u“óµâéóüލɋ~ÿÂh=ü¯¨­‹ÿ—BßýÎè'OnðõýûýûtÕgŽ - Íêk”=çÏ\µÀmÎÆn ûxooП«Ÿ•: Lœ¼a"@‘÷ä⪀ðÇŸÈí¡÷ÚÌu˜ì~xúo³–}¥tº‚’ÅF­tlFõl¤­xeç¼9W>éx©õ÷¨I²%¸ Xè ÉMŽY)TÔõD£(«†~1ºxþÛÈsoìݼ=:önf¡t,» ™¹hvÿ¦ ä™aýÝ/M½|ØÃð[bnÅ›ÈnçÆÇñlˆÓõ¢s#ÛϺWã¸á¨£ñK[sP½üsÊîôŒŽ_fÏ©»Æ¡ö‰ó~:²Wd÷“±³µyö nžøWwÊMtÈÇÇöZg ÿ£ã„‹’ª§³û캵­«&ªâ?‚S@@rB‚D$`«Ö €˜Í° À6°;OŸnØ£Gõ3¨Në ×v¤(%VúÐ FˆÏ·¤pCÙ¢dcÞi³–Oõ|Ö)Ã㎠æžLòÙX㊯O›çwDMž®Þ7 £åÿ¥¦~èË¡Ú_QcÀOýrå/^¼ðóóÛºukeL”}ÔwàÂgmgøº·5Æ Rã¶-^{µéŠs‘ÃQÿj¸J‰ðc”< öœñrêÑ07¡i` ÃD÷ë?¿¼EñÓû®dØ+n‘‹âÂúê1ê¬qEm5¢[íÑ+²ûÉØ¹V%¦þ6Z—Þ¯2ǬÝ.ûz;?ÇìŽo§É¢À˜ Õv`epôðY¨B_I’~~~±±±È?Fñ™;ÉçÎI$`ƒ*º`³9Øl>› gÌÕîСúi%LïŽ:Ø6ax÷ŠÎWŠoÄßaS”¸IÎyÃà¦1>¾ÐS ÒZsœø(¥tmäÓV–Œ³£dœÔ ѸŸ)jê,^·Flû‰ÓßU£õå­ÌLÙaDéºy²o¸®ƒ˜~W Ýõ ðgY û\2ƒâ‘Î>%+&*xj½™<“3p«“ uí*.,À {Š7®Ûr±¼$ÎÒìKiè¿M( ò&/{óÝÄ€©ì¼¤h{'¦‡›Æøs¥ïfèü‚A5ì]3¥ß„w8ËV°ãRJé2³$hŒB«!~¥,ÕW=ÏøBφ5JŸ&çgÑédÒy©Ò6)±qØgyç7+$5³Íäº`:´ÇŽ^d´øS=QIÉ8<á³±aެAníG‘w¢Y™bªI¿²µ’fy_¬)õ—Ó'¨Y/ÜŒJ18  3¸èê:¹&ˆ‰ÀÞ:ïD7g ú‚‡uÔ5jÿg %qDÍùÕU+¯˜Ì‹ÚæçÚÖÒØÈ¼yÏÉ"ç9*ž<ÎS½Ø§,|îël'ìúÎÜ›ZF( íž3°ƒ@`íà63ò~¡HáðYmk7ÿ=O‹Õ×ÅÊÞŸÙÙnÀú["õ£ ¶ž‘±±±±±‘¡&±tU?bwæ8´ž+TuÇòô-ÎmFø(É si3àϹ#\ÜÜûöì=zUÜ{9Ћ‘gls¶êôDòkT?\ŠLÒ:Ìѭ°3ïõ¨ñÚjD=úeèwä¹µ'1¯âÍOÙ«cû3¬Ç{ XåÕª_~^9 Q¬…@ü×pZ±‚£« HT$b±XÐÇZ´h(ÝÈâ¬,µWÄ ¯qÈU—…/SEuÂ7-ÓxO‚<“33€i$z––ÿ$¾Ôê6gù <±P¶ó®ðщ2ÞI­ûðÊUu@áñÿƒøÅ™›%VC]ÌÕ¶û×°öŽØ¿ÒÍX5#%Ÿ×kSâ“ûgš]_½ôBž"÷Ôìñ³ûn»‘’z?j¬2ÂÛÿd®<÷ôlï°"ˆ›©©·Ãúd¬Ÿ\± '%Ï9³`dPö°û~ïȯÏm¨Ýv”3÷îÁ›B%È^>ù©í¨î8€²4åžÁœC11άo{mþÌýo¤tbHÂÈeé¶5žY¿rÕ*²NïN±?HÐlÀD»ŒQ)’zÖxhµ7ÄðñÞØònUüìÀ±üŽ“]Mpt?!¿ %$iÙ¶-›Í.ÿ¹<èˆ-íÛ¶õð¨ÿC?- J1õ7× …"ŽG³’^Q툓4Â0“†Ÿ+šÛEÉ(Ã>ˆ(Cm¬(SS1c¾´—ÒÈæNT¾8ÁÊ)·°‰ì$MÉ¢‰r}´¬e³ÇRwö3?VƒäpO†ž¢WìÃkFf,;ÙB¼h¤‚‡S:¶Ò€™dú Y=ÍÁ„Iìûº’y#H>Nñۉã‹Æ7­]| ÞÑ–.­©£è¼T›ªwµ…“ž^ SCÒT»v›rÂ,Y =б’{6ÇrÓò/ÖT•SéÔZ/blzˆû2™ïc`² —¤=¯׫®ÿˆž…¯o˜TZÆ`\®NHÈxäSÄO?&/Î+ÅõÍtj)Í&ÌÜB=Ïæ+V¥x'Û—¤9ôðD}ÀÚcöØ÷_NQž¼¯ëud„hçí$nÄ€â㥯ÄäŽq³ê=¯ÏméåÊ}èF¾Ë´“§EwÖc@`ìÖ>ÛñZv#Ç5Þµïâ#Ö¹b?ô²ìÜÛò×®YIÊÁ¹õ5Æqè5å·Õs"nù†ôäau×xÝhX ÛloTLæßfDÑݽçIçÐîúŒŠðúš·½@-µ¡ÇøµíÑ»xĉâ'O’“’T¾«µ; )%ŸM¸Ùv‡aß¾õŒO s0ÐRj«µIšŽ¥»Wp·Dq½C´ä<…»_é²Ñ m%ö8Jsã"£šÚ’ZŃ«lÌW­… ô›*±BFQùÍTAƒüÈÒŠý9ks¼@U-áŸSžž`€R ÙnHê—)ž…’YÌ(VÔÓ1Tá{ŒÒ#uU§c`Ô‚2hÅ×ô}é$YC'Æ¥õ}7KÕb”ª¡V]oUà”VEÑDå.Ùµ×£ŽµÕËg1ÅpwjüA¦¨-uð:¸DÊ5_ôpr}êñã‡[¾¾ÛÓÒî\¸°]µ£†‘QS/¯ÀC‡–!·"~j˜|S*5§˜µ¾N)Îû(ã7ä1pM-UÛÉ ŒRÊ ²„äÇ}CZíSkÔ2^¿RzÍÛ¨…(Dä‡ëïÌK¯¿-ìÖ× ¾íÛfØ@σ×>õjr䜸ëÊ| ä Nƒ†š*1¸–¡6¾Ę́)& µ°@?Øs<»°pF§æ €R(HÆßrº 7ùr×sÑaÖr»Ç^Nú£áµ]Wµ‡Dµ­|W ÃÛ]íX¹3Fh±Ñ†@ü‡À³²ÎÍ‘¤j¨UþD0ÉØXÛήî¼dŒ¸ó¸^™ùçÕ˜8/±o9QЉŽhú¬Ô<ñ[aïZs+W/hI1H|ï Þ~Uó%eH´+|ÇÀôeº„ª›ÀxÆJ¦¥ôôÙ²fLyž£PšÖ±A3Å7¡Èû ¡´ ÀD¯ržŒGÔsLÛˆ!^ #°ç{97›IúeÑ‹¯‘g-¥Óõht^*kF;aµ…Ñi€ZÖHT§ÚªìEW/ 9êåZ‘é`ÇÅQ·xÒý¶u{Øù[êñ¯ðí‹ }}î\‰ºx}GFó‡y3²r†¦?,‹=s&tРߑ[?5l+×.Z©GãÞ©=¹“eîׂ߽›ô_I!xÆ:LKÿóÉiiiiii/“ï$Äšå`Áᛂò\Êžï ŽLÊ'€eó箑sÌãV$|ªÿ¼>«éà¡ÆÉ‡ã®‹Wôi_¾Ê,Ë}W¤ÊDž÷ò¥×ÔÒ¬¦˜Ö¿| «Ì¿y‰¸-.ñrBBBBÂåø=MED§É¾¡Æi[Sƒž“zŠOzœqq÷ãÆã†[©¹œÁ5²°üLc36zu ø/ÅZ&Oˆ$åáUùÕÖðUÀ%í˜>=ëÊÚxF’ÏÈÉaädã/ï³þž£³9W>Ǘ䪥%iNž y*6el@ŸÉJ0’Ið)‰¥ælOÅäR @Bì8D”R@ ‰È½ û‘²åÁ eä$m“Ë^}€( A–Ã\;œçJÔõN/fÖOjýš³ú0QHBI*kõV¼©‡¬1‹b1^3òJ¿ÜžQ%öBöÆ#D‰‰p–k¼Ó ôâiò¤/®S£óRmóTT-FÕjEýŒ­[]¨O½0-¥ÃM‰5«XFäMYu{Øô[êñƒ…[iiwŸg^…Ň–„€’6·oŸòõ +¿2šŽ½˜“”ñlj¿eŽÃü»9IWF7fÖš-a926')#a¸9ñ},Ôúm}jNÒÃÐ6:_“såY\tüz`:çÌuL_5vÖ¶ØÇ¯?|ÈzzñïÙ6dµŸå×^‹¾«5rÙ&wçêO HJ–seípg¯ÐÍÎÃí…‡7I."Iу]K‚O½Óàà€1ÙËrôêF±‹V^ÎWÖWÓÂm¸Eò– ð>^­*gJ¤O·†Ä½“%©GÖìÈi3²O›>5Å<—(K3®Å&¦þªË¶ÉœØ·´\Çtµ¬Ø­Â¼ÃˆQ‚¬;ïÃW×x-Nû‰ý™q›6î|Óf’«©ú£W¥$?7GܼºÕˆÿòòX« Ê*B6Øl €Dµ<H$¢sçʳ²j< ÂnÎãwï®Û½'ßm”æé2éÚ£ÅÃLÕÇè”ñÀ’5nŒMîzÖVz.냂K\ )óa¥Ó­™>Žz­;òî*f U¥à¥0¸¤Ušæà>|§šyËC<”•ÜT²m§Xë¨v'ýNÚíKw.×ùM ¦¥$"\ {uÚÛè;Œæ(Gï˜B²4”.ýÈ'KøÃ6ãÒ/‡£&ÒÐ1k¿Žƒ^Gf³…EóìkϢɓ¾tº‚è¼ô•FÕ–º¾ÆRuj MP¯z!È~#ȲÅp7%³þ¶ºFü|cLãã³566œ~¨¼6íæCÞÐ7ïÞ¥ Ï"~êûÂÜ+ì¤vèš°À›òå€éXvõ>>{€@NÛ—˜Û¶³8pÙäN+ HÂÀn`àÎ ÞÓÃ@½F)òî×îU’šzŸŽg‹Vu ?78¾uÆ ±X\bH$6ŸmÙÙI”“™™’Rñ .$•‘—ZòˆäüZ[} ɹgª,H·ÀB·Àª¿æÉývø}þY2Î×ü6h³²‹ÏÊÿËw‡ÄˆkBıÏ*K9Gå;€aײ° Õ¾ªì¼Dô|‰êÿŠ ŠùWòkˆ]GñÖ âúˆ§Ë“¢)½4^ª5}–GŸ­š£ªÀ¨b¬zÑN{òª¨%ëÐÀ¢O@S/5Ęx¾ð¢½<è<\W]#~ôpK*--*ʇöj±–Š® Ð”ÕýÝrÂräÙ¤ÑËi&=Û7$>Ü ¿0ìQÅE¯ÛÆgǠΜÊã|ÇÕ'ÙðYPôöÚÞÐykoä›ÑgBalÁˆß7.qkÁ“æf)Y5®5¦I߉ACz6Ó”¢çÇ#ç-8‘R¦Q×Yˆ_ŒÓ¤ÿüðþóéº"ŸØg>?i;íyê¤jüCb¼«EaºŽÓ¶^˜Vå˜ÅäsÏ&—Λû_Hõ¯mäÞfùÍÔí½–±‘že×ÁêëÔC§…‡·lRÝã|1M|ã^úþ²ʲò»øÂ¯z”l2üpêð ‡×VãµÖn:útêèªWGŸói>UÏÓìºíiº¥ˆÿ&€DR1±%`óù¶nn£‚ƒE[† ÉÌ̉€  ðùnsç2ÍÍÕ3ô‘ÿ3ÒÒò‘~šp‹Á ‚ Ÿjü"09Fõ~"¬©]ríèß¹CýÜ;ÌYÔ)Ú3¾HuÜÈÖâÚØ»úƒÛ—/PJ²…dä3-ú/ôqñçÌØ¡Kóé3)k91bƒ›¹<ýèÚ˜ŒÆÃç™WߌÝròþÝctãB'ÎzÆéÿç¶isC3z\rùòYÄÿ#ÊÒ÷oÞ¾<r»Ñ¨ùMYÈñc z_Kqñٺ͛Y±âݧO0ãØ±à!n9™9Àáóì›{LŸ®úþ"(ܪ--]«q™ð MÅQ`p•<#£¦õÍ(ýlȦè\æ ÷ñæzZ8”‡[)ÿœk]ÒþÒ˜FzZ8ËÞÜÎè>Ök´•™~CÐ6ÒÆ!Ÿ6¦Ëoæo·-ø+õ[ÿá&UKeZº÷j Î~»œË5v´°b~ù,âÿYÆžI;˺ÌßåiF w ÄÀ§’§E‹ÎnX)É)¾¥í¤ÈÈO%å{ì”äô¨c™II–;›ýîÝ;ä4…[õ"$d‚—W`úÙ{Å3óá€n¢¶@ÐÞÐÐL•L)-•€¦>Ÿ –®ž&HK¥U÷ d9Ðì°IIJ¥åÇqS¯5‡ÖÙIoX½æ™Æ¨%=ª¿~¢ž Žc IjÙƒÃpdýíï½?WQ^V¡æ¸/Ÿ…@üÂnõgBÚŸPýÉúÊF@ü¯1ìÑc®“ÓŽ¹3 2s&íØQBVÙˆd2Í{ô ªMj!nÕÍ¡CË úýöíSyó\¦À¸$O hß©“ǦMcË[™ΧBs›.kw,8òZèÆHyðé«7Gcè˜àE™™YÅ\'cv-¨¿KzVägb5aüÈwIXï2ÉÑ IDAT&5ÞÂ’½>w%{úpó‰>òßúÀ²ìÚ»{áÖi _> @ ¢:ŸHràÚ ‘/Äw ·àäÉ ¾¾¿Ÿ®ú̱¡¡…¡¡Ye¬ ÏŒ³@sϧ>]@)ÊL [öFþÕË_îÞz°WÀ¯EÛìo^SÔ±ƒrñÍ¿G4 öv]º¹õû4E•=ø{Ô$Ù’?܇,ôä&Ǭ ë: @ @ êúÒ&¢:Eýj&¿xñÂÏÏoëÖ­¨öÄI’~~~±±±È?oѪU+xúô)ò'ñ+´‡ TU@ ÿ*íÛ·¯üÏÓ§OQ¬…@ü: p @ â_ ´Ú·oooo¯Š¸îÞ½‹|‚@üR|é %_ß0©´ŒÁ ¸\ñÈY@ õ¡U«Vl6ΉD®|~Íì犻”¥ø…Ò’Gý²úoxyõQK µ½OŸI.l?}ú¯3gB¯^=è刜…@üx­vf!‰ìB ˆ*ÐjÕªU‚DrN$RÅZööö`ooß­[7èÖ­›z¬%ËfmšÆïh£/èÛväO feIþIÊ86D¿_8CñM’ëóB’8W+Þ°][òø÷KRóÀçB)ìñfG¾À†u“óÕJhóDü"á–¯oØ•+Q¯ïÈhþ0oFVÎÐô‡e±g΄ô;òâ—Až{cÇ¢±Î¶@ÐÎy⪘Wb @žÖ·åÐãyßøy6Å›H×–‡?ÔKP˜á6íÜ«Çg¤Ï×’,ý_ù„Ì=<Ò9ðae%W¼› ÔhóÛÈÅÑÉ%ÿèãujvÕ鮯§ž?ÐÁJ °j?xÁ‘4qõ牊‰›¼Û ]Ï1+Ï¿•Öv°Î¬jqW½NªìIˆ{Ç)WTßJ¥$™§–ŽèÖR 4ï4pväýBe-)ÿÿå¼cCZö ÏT &ñ«FYª@+AB0q8¸víÚçC2<|²ö~±ly´(!A½\FFkXÉ,ù¡&<”˜°ðGœÁ e‹6–uáýO=ð¹PwŒh0¿ðú¢¡vÒ¯VB›'â ·ÒÒî>ϼ ‹,8%m nß>åëVžˆÿ[tNRFNRò!Wc€i6õJRFNĵ¼ ¦Óí@NRÆã¹m8ßG7a926')#a¸yíË!1­¦A]J½ž‘“ôê呃ë=ìt¾ø®Ú÷‰øiQdÞÂŽþç®^‹?µÁ“{~Îài‡ßýXƒH¥XX(û'¼}6Ͼ¿uõûƒÁì´îüÕ«W¯$ÆŸ;¼n8?!`˜÷ÞLÙw± 7ìµhã]xøwÐ/~ì”ÜcKRòókŸ,Ÿú¬J(Df™5ó kÒÁû©©Žú韙í‘&¦;(«+«ZÝUŸ)qæù£FlI-*ï†ei> ¯Ù]|þ2õæv׬¿|W?Ó¦D ÿ{*Ÿ6©¢¬š–jIaíM~û²÷ ±skÒÌŒ´s.[,n%g…8sêÚô­ø3#‰B%È39®¼ Å:Î=tìô\ü8)e€½Ó×SßJ ßc÷VM› ¼Ã™5@×V oíÀ÷ßC+AžÎíÓ’#T5Ysbr³ÙÖg>5fmGhàq<#)ew/·ˆøŒœ+»]xCð‰ë9Cºk«éœ[I9;G5âXžu'!#')#óÄ¡•}-ÙXùtÖƒ å»<{åÛ´b|ÁÐu ‰ÉÈIº²Ò¯¦‚Ûf̬ÎLÅMqÿs銿f ôÛŒVcü»h±l§$æ$eœskÀ€Ïÿg™Ué¨Íw\sù\jNRFNRÆ‹è=‹º4$0Í–ÞA±iI9ñ×nÄeä$= míi²&¦ý—…\~™”‘“”‘~âÈ’ÖZè#g? âgn–X u1WÄjX{Gì_éf¬z´ Ÿ×kSâ“ûgš]_½ôBž"÷Ôìñ³ûn»‘’z?j¬2ÂÛÿd®<÷ôlï°"ˆ›©©·Ãúd¬Ÿü¨|hMÉsÎ,”=lç¾ß;òëÞ Tž¹gfÀ5Û KÏÒ^>‰_fu{Ãò‹Äíaz<· {¼LN @>¼Îó;’t'q]óÛk—_ÈSШ"ÚŽræÞ=xS¨Ù«Ó'?µÕÝ êcªèÁéL›-4ëÒªaéìÖ¸øÁõ¬Ú9’¢{w,‚â“.;\žóE»Ôz =>Þ;e^»î¤¦ÞÝ?Û;ebxºŒÎ@%aä²tÛÏÆ¬je¾J+mÐÚ‚«º¹f- ‹SÓÕ_ã´òÝ´vTUöò•‹nCšƒMueU‹»êÖÀ4óмvéïi >ÏÜc—§Éøtr¬}«nc·»Ïg­AŸ²Ê€fy`‚`Õå”—©7þêôlÓ²SïI ë}¡Ößá²×û¦Ï9¥7ûÔãÔ»»–^{«¨­tâ¿MBÅß UT- T'.....®âå¼^t<@Ž?ÓX5™ß¾¹žó$ÍSið1‘¤)Y4Q®O€–µlöXêÎ~æG€ ‡{*x80ô½š`^3Þij_X”ù»)98Õd`Ù4›ê³[Â$ö}]ɼ$§øíÄáÑEã›Ò[Ap(üq<š•ôŠj·@ôø˜¤Q•Î{ËN¶/©àᔎ­4`&™~D#C @C=Hh6“›3•ýD}wfõžz¸®þãñ±ŠX‚ÝròþÝctãB'ÎzÆéÿç¶isC3'N Üåqp‚ äöjÿ#)YÉ)¦¤ý;µêmÁ¹c0¸@³³{­4ËîÉq÷Œ§ìÛ6Ì$ÿÚ†YW1·és&îU¼uÝ Æ½aï?¾x™'U]¥ÌFÇzêKonš¸üžÚ$nЦ¹ù8þˆª4=îfÉx-Û Y§h€y窈|Q¦´ÈJ<’‘Ï´è¿ÐÇuÆŸ3c‡®Â¦E.ïÕ@ö"z͹¬¦Ãÿh¢5MÎx<9oAè”Å—ÃfìÌdZÚ;Yr™èrûIPç•âúf:µÏÚÍ&ÌÜB=Ïæ+V¥x'Û—¤9ôðD}ÀÚcöØ÷_NQž¼¯ëud„hçí$nÄ€â㥯ÄäŽq³xõúaæ~ÎCÏÌ€Q–ÿA„jS©yêïG‘k Hüh@X ÷lÃÃôì{5Á‚_ç€C5Uq5¼\y£ÝÈw “vò´¨ÃâÎzÕ„?>ý²é€6:u?5`hhAi~Ùž¼áO/GSC‚â ?7´v»>×É»Øèd Ÿó#[ñp[Ï€™‡zïŠÉøSÃ@¡k`Ù¹·eùAi‰4´5ÊÍbhh±@V*¥h§_ðŸrÐÀ/jBåsõƒM”QufEë®úh`h›@Í×>î» ¤þ}䌉¦1Ǧ5«-eÅ€Fÿtëxt áÜÍaAÜc2'±ž*öî`=þñMÁ±“þr³äàÐdàÜi»âÒ—Ž@ü±–SEÄåTñ÷µd´ er°ê'^ÒO eÙDÌ­ù#µø—Š ³äGöVj§˜ã pŠÏQ5Á¥DÙ¦«ÔUép¥EC¨zÇa…ï1J,O€Q äy4†h:–î^ÁÝÅõÑ’óî~¥ËF+´ÕÚ±‚l 7$õËÇÏBÉ,f“$à”¾0¥ÉÂ(``@Õ£ˆzcª¡ÀQ w§ÆdŠÚR¯ƒK¤\ "ä h L®%Ocõ3é ¬opC¯¤ŒÇQšÙÕÔ–Ô* _Ž}Á½UubhùOnii隘X}ˆË„Gm*Çr{€«äU}¢,º¹fC\ŸΣçŽ."1-Ý{5g¿]óä;èçE¦æʲï&$=#÷ÂE§NÛ7ïhÕŽÈŠ{däܳG«D3+€ŒÓ÷ÙÎkL^…oÝ~8 {`8ÈyJ“~Ý(H éï¥Âräx°²Î„K>ž?ýØ+ºGéUój¬¥Ó§$oT ÜÎè>Ök´•™~CÐ6Òf›;vlðfóòÀLÖof 3`јìh‚G—Ê´áÏ}úäÁÝ£Ûî ;ã'É7Õ¡RsŠIÐþÜ$+Åyeü†<&®i ¥23£”ò‚,!ùqßVûÔÚôŒ×o„”žEE›Ë6ja ùáú{óÒ+Ço »õ5¨ÏZ\LYô8jñÆ#·³±Mm­µŠ•U[j€¬…à>§B*”J¹è=*›a <^ûÔ«É‘sâ®+;ð«Ý(¥ÏO'›¬G´daNh5Юn˜šbB»¡^»Ô Ì.Ä ›Tô@8Ï¢!³8§˜lRÃÀZ2LC‹ Ò™¥´TZ5ì‘eÇ­ñ™Ûè÷ƒ»FÙp0ºƒÊœ:³¢wW}5ÔbŽhZyøØ>èÌ•ïf_úh:î^±yKÔïyr^sw¿ËF[ÕûB?¨¯Ã¢l¦kV1Îã[4ä`ô¥·ÒFzDüçHKKl6{@€DRwAÕ9®j 0NŸ> ¢8®ó•¡—Kœø\SÅЙ’ƒ'Ù÷s°áÆJ¦¥ôôÙ²fLyž£P6(¡ ØtÍ)* ÏW€@b¹ùÕZBJÛˆ!^ #°ç{97›IF7 @^¾HûTJ`e9Œ[ñ–¥˜˜ñàˆ¦ÏJÍ¿5¨=a|мÏ*@›Lôš!çɾê[q^£ˆÂ^´ÆrÔnÖCdú#ØqqÔ-žt¿m•ö‘Ö@gºK¬È÷'~Ÿ{\kúñ‡Ï\=³7ÐÕ/^°/˜A÷¦"¡mD«ŠÕtðPãäÃq׎Å+zŽ´×®Þ;¦œybìÞA·ãeÙë¸óYz]zš³0¤œT½»Uú©”¬úÜ£N»>ß|™—!,—MŠ^çÈy¦_ÓáüfVZùÏß–ßÊâwÏ>i[5­šƒ²øñß.LíówÌ–Ñ6åKþj¬;«ZÜU 4}Aü,§þëž•Ÿ¥”•ÉÉaÖÑñ+Å9™%¶>[N\ö$ñ ŸñÕ• OdC½/ÔBíú:œÐ5×£>½ÎW¥$‹só%Pe4¥£Õ„ˆÿlÄõôéÓ§OŸ®`³W°ÙÁ5>±U“Ó§O«b-ÐqàjÌñáJ$^f2Rï±¶.á¤4”ýfN9IÛä²W Hå0×çy…´Ú&}$­ß³×ÅKIxÇÙú¬zaÐYb/doõNƒƒÆd,ËÑ«gÅ.Zy9ŸfñY–Ÿ›SÉÇ‚’’2’É3àk0È’´Ó›¶§*äR8‹!ýð:+¯è<§ëTµ¨bZ¸ ·HÞ²áÞÇ«Uõ´$içîé¹uÔ§¿†I‰JmvÖËûþžã½9·Ëß6\‚oa /OžY$)H9µyß+EÕnŠ’ÕaWåiÖo˜õëí«'’dIêÑÕ[Ó›z¸Ò÷@ÊÒŒk±‰©Õ¿ÛÅi>ÜMójpø­O y^Òöàë:î#Zp”¥×.\N.$Èìã³Æo“ŽÛ¼j¨…ôcNNNN®P,§;¨A›U=ÜU§úëѦ³ñ›ý«÷?)$åy·#V)é2Ωa]Aš(iõä §2ÅÀ646àz|6³ÞªNãú:œ0é3²õûÈõG_–’’wq![ŸÉ¨BšÒÑrBÄUÐu÷îÝ`>ÿËA—@ øÜ(óäKO2dn™ÉssÖu­y‘)Ù%nËÜT²m§Xë¨v'ýNÚíKw.kÒ?!–l“*vðÚÚèyîçTµû 7‘†FˆYûulô:ú0›-,šgOaú²Å²‚P^»VüY—•ãz(q Œ–¬qclr׳¶ÒsYÇ\âb ¡téG>Y¶WZJ"Â¥°W§½¾ÃhŽrdñŽ)$ëkE[D½Œ%È~#ȲÅp7%³ÖßUƒ::Õ<ð寯Ð*1V:Ýšéã¨×º#/à®bÆPeQ ^ʢɓùÝ‹ø¡_Œròä_߿߿O/.Î'–¡¡…¡¡]¬UþÜïQ^çF™«âœš$[ò‡û°€…^’Ü䘕Byéëí¡÷ÚÌu˜ì~xúo³æ\¾”VÈKˆË–ä—žz ŽöŸðJeÂÆù3W-p›³±È>ÞÛôçêd±QkÚÂoÏ-Zá~qy‹?6‹p ½2*“¿Û?v’xÁÌiÃ~›èAéû;QÛVL•ä$®Ûæ¶Å·Å¨Åcï]JÊ÷+}®.²Ïš­{ŒðZ´Íþæ5…êÉ-Ut=dN¸ÁªÉ­GÎjôì1 )Igò'¡\Ø`âä ÙŠ¼'W„?£ëí§¹/̽ÂNj‡® ±)_˜ŽeWàã³X0äô]ݰm;‹—Mî´¢€$ ìî\àÀÓ´ø8ÚaY1C·ÅÀ…‘óì¹UqކոµSOy,\énìTõM)²øü çó•?r]vŸ œ~}®c ‚×Ð¦ÏØCï§d•²]ú™y/qöæDìšïÓõ4ª€hÔw„`õ’¼I›šW­[úêüMm— µŒð•ò›ó\º—ç®Û¬ó€µGç 2%ÓwZà<;ؽ]DzïÔq=ôOUóó°:ì:;¢²š.^¼btûÀbOðÛ˜°S¬·ñtÑ߇ A3£º_lWE0·íÜðEóft +¤x¶ƒ"æØq@‘q!h澎G–Ûfì ½V"б}B+Ni»jGÛÍ5n¸íN“U}ÜU‡úPà¦CCÃóÇ:¬-ÅõZºÏÛ·d Q]{¸ñÀõk’ÿXãn7_ ÓÎ#ƒ×¸2p¨ï…ÊÄêëpÜÔssا…žmË †º´à&ƒ¶tÔ² ~TŸ0îÖ­›j“ŒI™™;,--srrT“Zª%ˆiiiªô,ÙÌͲ™tYñÄ!1UG–âØÏ œ£òUï1ht. ¿X¹;xiͬtÅ[/Tˆ4ó*‰ñª\¶Qæ n…nÕžÔ);/=_Rþ“aײ° eµªâÉ>«qåïT—Â"iŠ 5¶YÙÅgU˜x¾ðRëE,$ç*¦þi ¤É³JdEïLAÝŠŠBó¯ä×K Oî·»ÀOí©œªs£Ë“ªÃ½j:?Ís|䂯ƒe6Øßµá§Ü¥n»1‡6'§xLŽ)ø/mÇIQ¿ÜKf/^¼ðóóÛºuë¯}qS…‰3œW5Û{f¶5z’†@ü¨$éçç‹\ñ³ôª=ßÕÃ-u}äUÄ7––œð³´‡ªª¯ŒOÙæÝNwàJÑë„M¡ Ï O ~v”¥ïß¼}y2äv£Qó›¢X @ ¾ª=ß  ŠµÔ÷É@ƒfâW…[_‰4-ÄÝ5ùñßB–±g’Çβ.ówyš¡F@ ¾;•ñUeÐUs¦ @ p @ü7a·ú3!íOäøG_…[@ ˆï€úž„•[e ˆÿ6h«(@ ˆÿªoÞ¡X @á@ âû£¾<ø@‹ @ þ•ȪòÿªKuDõ7 ºn!@ ¾‘Ê«2²B!ñ ‚"@ü+ ¥ƒ_…²Ï,Ä ·Dý[êìÌBù@ ~ÍXKõ·úªÂšÈ²Y›¦ñ;Úè ú¶ùSƒYY’ZË39}[òŽç}moÄ86D¿_8CA›g:·OK^Œð{æù-H‰õƒy!ÉŠ7l×–¼Ã¾G¶öx³Ž£@_`Ë~ÿzhÝþ=EÖ›ÿ—B(ÜB ¾7Å—ÇÛÚL=•û9P*ŠÕ¢KÐÉ×õÏ×’,¥ë\ã7Msïh#¶ûO ¾%¡jï\Ãú¶z[Òå_VXŸ³h½Q§]¢|ÂÊÝk ¶ððÉÚûŲåÑ¢„Qôr­=b%³„úõü¥Ä„…åfㆲE˺ð¾G¶2<îÑ`~áõEƒ¿Q-ßSd½ù)Â-â_¡4aѼ#oÿÑC;¥XX(£kúÓÂ'ûî÷]›½Ü•Œž9b彟ªsUdÞÂŽþç®^‹?µÁ“{~Îài‡ß)~TÅdö‘Y3²&¼Ÿšúਟþ™ÙÞi2ñã`ï ä[’’Ÿ_ÛàødùÔÐgbU`’y~Ũ[R‹*j¥¶”•Ð&¦n›03ÆÐÿäãÏ..±¼´À?:Kñͪª†NU2ôún¿vµœø³ì8†þnÉü²BiÝgÑz£.»D}ã·ß({Ï;·&ÍÌH;ç²õÁâVr†P ¼Ã™5@×V oíÀ÷ßC+ËÀáÚÎvú^ß™ìÔ2€ÂGœ9umúÖü™‘D¡°÷qšãzê[ ô{LàÞ*  ÚDÝô]>êÐä €å%q§»êY ô­Ûñ}¶0óÄø_½õ‡ePÆüÃA¿ÓB |‡»ÞÈS i&ÇÕ´Xǹ‡®ƒž‹'¥?Ú=g`ÀÚÁmfäýB¥âÝ¡qv-<÷¼–(Þ׺̈́ÃoÞžês\Xxv’Ó¸CÙUfjÄ™·ßèôž1ɹu3³&vÎ>ëƒÇ¶’¿*€Þ Ÿ5ÀÑV °vpóßó´¢s-|îël'ìúÎÜ›ZFÑÉùû¸µãzÚY Zö˜ð×­ªMdÑLUÕ7Ÿ*ÃoÑÕU+¯˜Ì‹ÚæçÚÖÒØÈ¼yÏÉ"ç9*ž†¸šßhJë: è½Q‡]Ä/NålqÝIµäcúAŒ¿v`;ñ.’ƒn²°5Rs&¹³½9EE7S…·Ã¤뵃aª±{|žlSbþý³¥f×¹K/0¹³Çs³û–ÜHÉ¿%VFèøŸdH_kLŸ£¡7»àqªp×@ìÚÛºßA"éòQïtyÊ35|¼Ù2¯¢;©Â»û%Ø^퉻±½ÉôóD1ÒLÖÝBÈ¿ÍÊ–™GœÏ&;*d!q'>’Pp'±¤ùmÎòKÔÐíÅzJ·¢=^¤úNn ó:¿ìpBÁ•HÙ›í%螺 nž”ˆpf`uhÖPÌ9R4Ù:m)¸¾Yή)5—"âŽEi|Raè0eyѸҳšžn¯ì¯ ¯qÈU—…/SEuÂ7-Óx_µÛ¤MP›ò 1¢˜Í2ë§?È_ÇçõÚ”øäþÙ…f×W/½§ ‘‘+}½oúœSz³O=N½»k`éµzLΑߔøÅ™›%VC]ÌÕfA4¬½#ö¯t3V™Z?ÁòÚì ä9gŒ ʶsßïù Œ\–n[ãÙ˜õ­5Êiå»ií¨&,ªìå…+Ýš(^¥•6hmÁUB¸f- ‹SÓ I¦™WäµKOëdPÑå“"ú”ŸI—àEÆÓ'ÅÆ6ŸöÎØÅÞ¾óà…±e¦¦šŒoTUõú¬®ð³’ìã‹7¿ï4ËA5¿}Ù„Ú΢-‹Ì^‡]Ä/ÎW|˧œ×‹ŽÈñg«&óÛ7×sž¤y* £„Iìûº’y#H>Nñۉã‹Æ7‚œ0KÖBt¬äžÍ±ÜtÆ»Dv’¦dÑD¹>ZÖ²Ùc©;ûY/²_X”ù»)98Õd`Ù4›:WP`iòa~üÜø`Ùñ5óÄÞÆ²“-Ä‹F*x8¥c+ ˜I¦aaNr­ç¬”2ì}ÎâVùÌùXÁ}×¥ Ë­î©àáÀÐSôj‚}xÕÚcäPR‡šÍäæLe?O™!& +MìmÔ¥¹Šïh¤jdÈpÒÓKajHšj×îžn¯\vMp(üq<š•ôŠj·@ôø˜¤^õTºµ*¯cÓCÜ—ÉŠâ¼R\ßL§v+ë%ørŠò$½"ÅÇK^‰É#âf9ðײsoËïQ±”øåÿ) ü¢&4QFI@C[£®Ÿnì!Âc˜¿Žçü¤Lεê罤Ÿ7Y–ý(fË‚ù#óø—Öš¾Rz­'Û¨…]Eçªi ¥Ê”A¥”ÓÉÈ|ýZ„éšUt„|‹†œkµîäóaòMu¨Ôœb´?7áJqÞG¿! POÁ¯ßÐØ«®¿w0/½rü¶°[_ƒïØMȲãÖøÌmôûÁ]£l8Ê-6HKdJ”ÒRhhiÐ I0 𔸓?;ô>Y€;,ò¯™@[‹K€N¯€…žšÍ¦yŸp<.æYä¼Sª³·ÜÞß—÷ͪh…·ww[Û߯— ªl¿pýb–FM»®}ÓØõè|Í÷µDq:]ç+C/—8ñ¸¦Š¡3%O²ïçP6Fñá€=ß˹ÙL2šn³ž±’i)=}¶¬SÕ²ã9 ¥ò,“ºç+@‡ ±Ü|  @^¾êûTªÞ)`´ù˜j|N kNQIÕò¤x&yŸ!T€6˜è5CΓñtÚcAgY¯²HVŠf]©'YÑÏ(—J@^këòÕÐêÒ\%w>­T«µèzéÁÄ9x‰­xˉRLÌxpDÓg¥æ‰ßŠÆš}ö-]‚Â^tÊrÔ˵"ÓÁŽ‹£ný{çÅñð··w\áàŽ¦"‚¢EŒŠ¢($Ñ`D‘`ÁöžAT$j%vØP ¢ö`Á– *öFPT”€ ”»ƒƒÛ+{ûûã(ì bò‹‰óýøù$ì½yóæ½ÙÙy3[Š}޵*5¢ëžïá Äÿ&ÞýÁ´¶em§Ôéû´ïëdç÷íæÇjþ}˜€ÓfØôþmP÷B|ð "üüÇã°¸)•Ï/1–Æ,ÛYgÓµïšzš~ëÒŃAölª"í—°ãܯ¿bœ•VAé©)IÒ”®½f%K´28ϪÛð“dîækŒZ ä•¸rñªâñžˆ©ÅtwК1³§È”*zY¬-N–Úõ@ H©}v«¼¨œl¢Ý3Ìn ;?óh’î‹1”Ù»'~ÙwþuúÕúŠ\Zëi¯Ã¿îÜbs1lù¥¢¿ê6MÙÃ_& ^Ùï—„-cx.loÇ/~ügåÆ<÷Q‘‘];MÎ@+éäz AËɵÞÎõÚvhÍês\ 7À1œß%¤ºÔjwþ‡XUŸò?ŽÝ zúõbk‚Ví;JÑ/·™9Ôo ÝLˆ@4öy-Œ]åƒxìÞÁdæÓlF惨ÅÜŒæÊ¯lÀÜèZÂY„YJb’{ÜÅì\6E;´ðPt)à¬ÚÏ“ Ìg­ñøG2Mú_sÖÅËIÈMâF=€)T[~ü,³”`dœàî}Žéœö­Ý}þ–4:)› û—ÜU‡™Rd™«¢ðv¾Ê6TÇÁª·{x÷,”]L¡Mµ2‰wÙPÑ·Õ;6‘(ãÍKFaù{Ì(²¹–~kzS?ÔIªáÔI†'²1àP–æ“M 9TC²œe«ð³b®^iÐb„¢]-;)ÚîaõÞ@|ôéËÔÚ\ñêás @©$™gþz[F»Õà¿\{~íù“Ø_ €qíÆ.N¸êfZŸ)½LY­&ï¼¶Ù™Àvšžœ¾²1,Û™—âBY[W­£¶ÿËîkn?NHÜ>¤ ‹×kI«Yψ‹G““~èaçnÖ Mïfä2gÓ·Fgi'ýx Ñ] v®ÚŸ&&)eþå5~žþ‘å²»fí¤&lŒX¿a´*vö¦û2 p†âÍËœÂZfì:m/)$`ÅÁä‡O³³3Z¼1£¹ÇW6s7¿®%‡×I/%Iɽ_GœÈesé.®´f<1é7ºóëk>-'‰Ü¤MQ”À¶¶„§ÇÏ>-%Ä'6ï}®Æš¤§–WŒÝBB]Ÿ­?skâ×oÞäüqþ—Ù“ÖåtŸÜOëGÚŠ2 õµcq˜¶cWµH\¸â÷b €¦üÅ•Ää̦Ō̋Ÿ9q«bÂæ•Ã[+Þæççç”ÈÙü¼ S"bn©U…©Û"®ûŒrâÒçÒHv4mÞÎA‹]+‹ÏhT5ï6¡?+iUTJ¾‚”¿HˆÜõºãð/¬[Ö”â|UuQ¼ºúõëhTc¿qõ©}g)ú "M»Ü-ÐÖâ“G»©õ^C úéHÙ Ö–oOŸ±†çYDTœÜ™xKEäv¹Á>cÓž¬ö Jçv¥ÏUp+bëN9ÿ¨Q/3'£û]ËwÎW ¬ˆÍÑ u¬ÀÙÁtäðt¢0ÌL¹(L)ŽtûL8óẅ́޼!=†µêëdÙÛc°Ç¸»ƒ™ËX®ftYì·¤€qELÝÕÍqàÚ+;±À²¯ê]à²5^È´Å›ñÆZ¢A›kg/ô¦~˜=”å`ÙjoÆS{;S¯ŸC"d^µ$he9“0Ь©ý¼5ufÉ´Ýã½¼ø¿ÑÄ› åéÇâr·í¹ÒörʣǤß8Ÿró¥œ`ðÂû‡ä ý6ž˜ÿÕ®kÇŠìÇmžc9pÈéN×·E­JyK6¶«ëæåÊUíf˜òHÔƒ“¾ñU½½ežÃ²ëj/ø 6‡q ë(Ì{nt°Å~¯®‰…À³q¶© *®‡o¹Ù÷›m}ç$—àù×¶Ebëβð%S{-“LóNƒÃwγûc­Ï^Æ„CÓ?3äAðÏ£“Ɔ¬ï{r‘K[¯ÖÓûŒxu,q~GvÍÅÕý§#1–«"·ÌØý†ÀM¾òŠ tæð|#·……uYRÆ0q¼`ÇÜ®¹¼ôÉ“'ÁÁÁQQQ(úâ#‡$ÉàààÄÄDäŠízÑÈcÔ+e†¼Šø›—ŠÑxøÏòË‘¤ìù•¤çW’X;fî91Ö‘MçW IV/Ã0¨xq>«Ù¯ûšHÎϸXîô§GËâ ;Ê5ɧQH¤ý2¸Ï•>ýº»~¿àÑw_­¹ŽÎ-@ ÿ×ImV“J#×!ÿmšø8ÏeJx@¯v&0ŒÚ|ÞË8ïVÝ£xdÑ­[¥Ý÷nføÙ𾂇×^Èe$Š]ç Rž¼_"}tìÙg3DZoÜ.iÜ#õ^/jïÖ…™un×Î%³£¯áíÛa@Êe`b%`†ax ×I3½ 1o@ 9ïû† Ò­P½ý“üüû¸{3ž_L»µ¼oƺi›_оt]‘¾{ÆF2ðÜÉ[i‡×}–Pº…@ ñA^¾œ¶zuD_·¸)SZ™›×MÃøüøÐÐ3ëÖÉ%’üì츠 <'9 øDhâ› ccc‘ïþ8::º¹¹!? ññæZ‰‰§ÃÂÀ„Cˆ‰ì{÷â—/÷]¾<77Wû+ǣƌggr9p¹‚ â̺uý7mB®C >ÐîÖGk9::"? ñ1“ÿ>@ˆ%@Ü‹O‹Óþ„ÆŽ“ŸžNˆÅ@„\N€,Ñõød`~HáÉ“'#þ}H$ä@ >rÜæÌ¹wú4AÀ€KQQclm9\nl` ö¸ö‡cbëááZ$“}:.Ò”ã¯Ô[…š†øA»[Äß1øæeKÉ¿¶Ht"âÿ€ŒÅêàáQ•j —I\hèþÀ@ B.WÈ5rML,-=§øFDÔäZ2Ö´f"Qõ?Ó¯FJÇ4 ~ÅØQpøÍ{£Êæöï(ˆ/¬}ð¯_GABÉ?ä síPÁ¦ôF™ÚÔë.ã·afbò¦µ”Ân6v™‰‡^7±i´ÍiB?œ¤RJ·ˆ÷uKüºÏ¿#oêuåñÚ¡#6¥+êÿ¢Ì»¸á{Ÿž"‘HäØó›ï"ÎåÔ»‹4¨Sß·àZìÂñž.Ž"‘HÔÍsòÊ„çrJ{=ˆîßqx|¡æÓ ¬úMò†ižÎ"‘HԩϸgÿT%Ï:ú­ÌŠ™:}Ÿ¼ÿÒC‰—.%Z:<4cÔŠ;2J‘uêÉ+òŽ~3)ö­Ë¬ígR®\<±n$ïlÈÐïçª?á°yGfÎ8`0åÀÝÌÌ{GƒÍNÍž¶=K)1mYzï-©é¯¬sM[ú]ä#¹61É>»|̨-™¥U×Q}’ÕÐ (2·Nš‘`1ëøÃ'Î/¶½0Ö¡u“­ª:Õ¶aÚÛ•”J.îŸÙ‰k1è?[VÒÐ@]Ð`»ˆO íWeÆ!kŸ× 8ŽÃñ µèÝ›®4Æ1ÓXZjZZ‘"geÀ†ÒÙ6ÌÍ‘Ì2²Ð`Eïš@…',¸w4‰L{øýš†i€‚Ç G÷1µ™‰D¦¹ieÚ‘»±ÉøK'3QG“1+ òj¿v^ú€2ØÄAdfï"œ±ƒ)­·øVW€d\0uË*¯I˜áî¦SÎ24tªTÙÜ.‚e‹Œ={›¸t2õ æf”⇿3Š/aœž"œp'ë¥+÷bŒ<;™‰D¦ýgp2+èE£¶°×I†ú˜Ù‰ÌzOâÝS 4Dªg¼ÝaÓ…"Óa;•£–‚¹~„qL–¹ÎxØOL†¦òšÚ‹Ìì» ¶° I=I†nÓÔtÍi ‚ºèxg+ÌD"³nõB£ª® äwæ G‘™½‹pÖnf™¦Q}Ò-⟀’¿8>ºOg{‘H$ê: pGZªÜ„Å#Ý;ŠD"‡>³~M+Óy‡¿ ˆ/‘žžâ1á`^­ëŠ<ûæ+㯃¦xvnkmݶ“gÀÚˆñŸ©^•µ‹Ô¯HRG§Fú`WÈà"‘½‹÷Œwë ””$eåŠË-çÆm èlkÙ¦CŸ©ëvÌuU§=,Ô¤‘Þ‹™îÙI$uê?cOfEÛ:Uvô@—‘Ë÷ìíæÒÉÅ+xOF Î?¿zìN"‘£›ÿ•\=V?VК¤z±ÕÓÎ}Yñ‘¬]–ˆ-Μ3ÔÁÇìNö2˹ö¬èÑþI¯ÙS\ÍYÍ¿üþwñ±ýå ÈÜ8lèϯ¿ ö2«JWäô’:á¥(y´÷`ñ×Ë~؆Ë`·ò Ûw`I3F­Ò…ÆB×´…¥¥¥¥¥¥YE⪘âá›~êgëDÖB µbGëÚ…@|‚x,_Î51€Ê .B›tÉå@¸…õèÑ(El§7YvÏ GgÓXz…~‰\ù{ÉÓLÉÆ^ø†%ì×$¨²¹3ÂXŽË$²ŠÓ.–ÛÝä.=Ç @'K•;o—<8V!8ÎÚ‹W¯†ìÙyyýe×2ŠïÆÉ5Ûggè^ªhN@÷qJ*™ý   âv’B5ÎMCéQEJ™Wò#—Ä·’enr—^ †o+ó5ÕxÇJvû“x½ãb¡rCrñÝÓåÖWy?c(ô4ª®Ús åKv`Ût¶øafɯƒ±+bðî†hÍ“0oµ.¿˜*¡©|[r¤tjKèµE|u³Š“͘ÆQú—ÞÊ,¹½ÀöMŽÁ•´qÍH¦1隣ygk%‡t µB’°Y‰× C× Ó¸¥¾¥×3KnF+^¬5Šx€5Ø(ÝB þTÙ»g„]q\váQÖÓ´‹Kìn®[zî­øÊÒðK¢•¿g<ͼ¶±×£ KN¼«‘Û¢}MÞ±—vû[Õº®ðÇ `%Ì ŽO~ðJ¢ÂLz/Š^=܆[«ˆ¦~Eç™Ãt àÄì‰ëI(F¬ IDATóúo½–‘y7n¼fû´YÇ tJù“S×evýlt66ØöÓ¶ï[ám©5Hõòbaß ÉiwO/°¾ºê§s… ºÖ‘¤ôþUAð‘K©·’îpsÍÒs…Ê—{ƒf7™yâa潸1ª3©%j ­IÌ^?m]=²ÁÇAîgÓ7¬ÓÖ€ªxzõmÕϳʛunÍÓ^¤yÖ-Ê2ŸII–µÿŽ+~ù¾—yÕÛ„H ½dÍ•NàÉ‹?ÒÊ,ŠöÌìÞµ«ÛЉVV†Œ&Z¥ÛšúÖX’¿hóëËfºa5Q o‚ªC}D†µbGãâÇ ´ ø‘‘¤­³3‡Ã©ü»2é’Èm»:;ûú6~yoNA9V¡³”ÆäRx3þAêsªÛ|ÉÃ߈V80­1gJCÝ5Œ 섲0ÂJ 1 °ÔAóíy”¡H:Yóä˜A~e¾…½Mæ¤ '«Ì˜À·WÎOÝÚÇz«3¼Ð Tt&ú³Xîb`÷@_¢»ôªb’~#Õ¦ê¾m±7/±wm}3ÉI3•N¦`l§Ù+xÆ ô5ª®ZÆ«‹œ'­+fyk¸8ÕvpÅ÷º»[ú[Š“#ýÕV¤•­AXn"'½µ|áhµ§Œa3ÈgGØ/” ¤oŽê¬U”N ÁV8ô–×Mu[JR9wMˆ¹£H!N »Éc•Nl öÄ_¹hÔ©bísÆ×ÔÚœQQüF‚[Q™…2¬•1^t#þPÓó —ùI ì*p ϵgãÏ>|êÈÊó³¥<Û/GΟ5HÄêHçšK¾MÞ›j8üðd3&€½ïìñ±ƒ÷%¿ìoY5p«Ë Ëq3kc\cÚOš9ÔÉ” ¦^#;,_ù¬ˆòÑS)ÓÎod`Úµo[,âåÛì¢Cm6mÏÃÁÖ;4dïéM@¾Ñc’­Û×¶_()ùÓý³¾=`7©­&޶»2Q`°ù ,WPŒ–-µ“¥êB ­ä»JKÞ–·nwZ¸d÷nö‰å¡S¾åž94± ³)VÕZ)3¬ka÷£#Šv`¯* êw7AÇ€v, Ó‰]ýºHYqcÛ…@|:”¥¥¥§¦Qû¼$€KÒO_ºîkÑ¿ã4aÒ| ø#QÜе|×rÞ–8Þ´M|•@í\¾d¬ÚHƒ=Œ3\„™‡QíI~ÀàiÚµç6eÖNƒI¥dåŸâù–3ì3Nj\¬†ê+½KáçCM<À’8S®‚ו!À+zUà”[Y“ öÎg›(s~•0( `zUO-Hò0ÌDc¢wpMëæ s1ÕÛR`RÍùï°ˆça¸iV9œQ‚ÖV£L­i¨ }sˆ £†[ÁU×MU~KI_c”)Yé" Z8‘ÞP@ t øgÀ4¥ã­?r3kÖÎÑž_¦ ]Ãw-ß¼%nÝ´MsU‚>ÁË—ŒýLÿ²©TñìL[<`Y‘÷ aËüy£ …Ö÷6h "ÔâœòíÞaŸíÕ(stJ–ÐʘÊÌ/#AçZ­‘¾U › X¸¡9_;Æ3˜LŒÒPz+ŹBn•$4eñ‹ÌÌF¨UÌà·´2b4¤e^Òê€y‰­æøuŒW“Ïç€B¦Ôà Q”+ÍgÓ$Í›FOû±Ç×ÇŸËÂYõŒø<&÷ [0ÒÅ }à²i§Å'%<Ú1÷„¶”ë–›ûú šlÍDAzó×Óò/Ö|c…7ª •jkÐ`M˜»~»®¼ׯ ]­Ÿ*xNΙÐ@ $DíT«ò?„$.,lŠ¥¥Q§N§IgqSw¥MÍ¥“çã2Gù–c嘜qïˆaÀ Ãc_I¿¾Æ׬ˆ¶¥$¾gˆ`ŸvÀW0Ä0i.3Sš0µ#9&°Ô°l'OW´g¨Äx¾ZcÅ®©HŸ€f˜Òl')‰º!PìsÔ+ÙLÆÑ3l4Ö“¯Ñ7ª>&6•Š«Á˜ @bźWK=æI¹ ƒ [Rä]F‰Œ˜€I^2T¥€‰é-ب¦ÑF°t¼uÉ´!îÛˆVØ× MM¥F-((ÁÅjhöx÷z{³>€øëAw |}lNhc²ƒìÁÝ|•NÚŠ(¦ÀÒ˜e;ëlzVVVVVÖÓô[—. ²×(9vÝù™G“t_Œ¡ÌÞ=ñ˾ó¯Ëß:Ú5<¦ÐF¨)ΑT>V^/Ó4¤MÙÃ_& ^Ùï—„-cx.loÇ/~ügåüHžû¨ÈÈ®€&g •tr= åäZoçúm;´æaÕ®ÄpÃù]BªK­vçˆUõ)ÿãØ ª§_!Ö¸&´àõ h–™Cýv±Ðåñ)çZû§N•‹%DezUùOûjøªWgHbs._¦](!ŠùùŒü<üé]ƒ_BŒ7¨B¦“< IªáÔI†'²1àP–æ“M 9˜R†‘,Ê\H1H,ë$w[&¦R3ö ³œ²„¹c£ëhe³Ê„já¡èRÀYµŸ)&A™ÏZã'ðdêîäë`Ù*ü¬˜«W´¡hgÐUµhÀ`¼yÉ(,ox„ÑÛ¨z´ìGt~ÍY{/'!7‰õHWù{™WËTë û—ÜU‡™Rd™«¢ðv¾Jý·Å7¶it¤€Æ´¢^hjl3w#º–pÖa–’˜äwq;—MY5Ñ3”n!u~UQ\_Í[±LVA²æB6ƒ”eܰ-S­R¨%©«¦N ?‘-Ž…¥9—É6r0Ü€¡xó2§°\÷VhÌØuÚ ^RHÀŠƒÉŸfggÞ9µxcFs¯l tЍ”tQ5ÐÂct—‚«ö§‰IJ™yŸ§äc¢VMn!¡®ÏVŽŸ¹5ñáË7orþ8ÿËìIërºÏ îΧ¿Zè©´>6^#^Ƭ?ù¢‚”ç$mÜxWANo’¦üÅ•ÄäÌåcad^ü̉[6¯ÞZñ6???¿ DÎîàçm˜s£H­*LÝqÕØg”—®8—F²£ióvZìZY|F£ªy· ýYI«¢Rò¤üEBä®×‡aݲ¦·àƒ¬ª‹âÕÕ‡„¨_G£êë¾Nèšàd@çMC±3¦i—»ÚÚB|’¨*s-q­»9àp8Ußã‚Ɔªrrê­a×ç ¿üÒäË>Bï1†'+kŽ–°Òƒ)ËÁ²ÕÞŒ >¦öv¦^?3†Dȼ,(›åö¬WÓÎ=a·ÕAÃ5¥x9 i—e8´ŸÐca¡_Ù&_Mõ½L¸±u§œÔ¨—ƒ™“‡Ñý®å;ç« uSG}LrÀ(²B¦öóÖ°§ª¶Æk™¶X8b3Þà7#ô5Š&˵"6G+Ô±gÓ‘{ÀÓ‰ÂÓדl‰í1 ØcÜÝÁÌe,W3º,ö[Ro¶ÕئÑF°–­@£ZQ/45Nh©ˆÜ.7Øgìâ`Ú3€Õ~AéÜ®T“=ƒh2XÓŠÅÆÆÀäÉ“‘ÿ>$ ˜h_sô„¢>¹/£?yò$888**ªÒ% þ®!÷t%x^»ÎŒ½»0t[j!SÐܡ߸!Ô®ˆ¢° [:¤®þaõ‘; àZ¹^°zާ%K#M]>jÚÞ,‹)ÇçwÔÝãQ¾þ}ÛªÈCÉÞ¸©ÃWþ3ÿ¶2"g¿¯Ø>³^EÑ®+ªu:Êïì _²=1SL2Í; ž¹b‘Ÿƒaís™’¿8¹:úØÕ'Å*ÀŒm?÷ üqö Uvô7>¾ûý°¯ ìÒ·%ŽÏ}_^¿Ò³?¾7¨Z²4iŒÛJ磉¡íK’~ž³tïí7”…˰>Å¿¥~},q®IÙ[½Ä}qôâ¢Nœ>ÈŠÇ«û ‰Í¯uÌyÝC>Ü'‡ÂçFœÌRÇ!?¬[2RT}3‘¶ôëq®¸¶½7¨ŠL½’Z§Ó Pò¬c+ÃÖývï­ŠÛêó ákfõi†UºÔ¶ ô·0ë=¿/ïZ•©^èD¡¾…6/Öа¯ã¡oêÆ®Ž7ÞÙ.Äß¹p@’ÁÁÁ‰‰‰ÈÃõÂÇ£¼½åb1•ÙA!ÇÖÍC’Ÿ‘QõÔ#!×&]NØo¿±ll'ˆOgÍE (ÝB  œŽÁg, ›è²® Ø–.#VÅL·Gï3B Ÿ(E2™ÇÂ…§×­ òÅ ´uœ²cG‘¬ò­D2’ Œû-;5ÕÖÍÍÒÒ8œÜÜ\ä4¥[¡†ÐåÛ¨sß"G €EïÞ¡±¡Aâìü)±±2²Ö+fH˦wo Î¦@é@ ¢aŠHrðšMÐà‹XÍù|‡ƒR/¥[ÂÑÑQû.@ „9Ž—[}3¡.­ZµJ‹;+´´¶|¹ŒÅBîB >šøÝ-GGGGGGä>@ ˆÂË—ÓV¯Žèë7eJ+sóºiŸzfÝ:¹D’Ÿ„×ÿúøÒÄÝ-777ä;@ ˆÂÄÄÓaa`Â!ÄDö½{ñË—û._^ýV >ŽG#ÎÎ&äràr9AÄ™uëúoÚ„\‡@| 0 @ šLþýû!–p/>>5.Nû^X;fL~z:¡ý²\N€,Ñ-BJ·@  â6g‡ÃÑþ¿ö?—¢¢T99xaal` Xû ;€ÇÄÖÃÃ#4ùMM9ž-Åþ?¥>Ë(ÝB 8øæeKÉMÕߪbŠ@|²ÈX¬U©r9!‘Ä…†î ‚à!är…\!×ÄÄÒÒ#pŠoDDÍë4d¬iÌD¢ê¦_6<”Ži@ýŠ3°£àð›÷0F•ÍíßQ_Xûà3^¿Ž‚„’F”§°‡›]Ef"Á¾ncK5Iµ,±%\„´\•@„\¬}^ €àp8BÇ#4Ô¢woºÒÇLci©iiEŠœ•JgÛ07G2ËÈBƒ!¼kRž°XàÞÑL$2íácôk¦ ^3ÝÇÔ^d&™䦕i׎°›Œ¿t2u4³Â OU«2énÈ`‘™½‹pƦTw|S0×0ŽÉÃ2×û‰©¨Î¶è*Res½º~ xy û÷Ž]É~­ªÛ®×I†ú˜Ù‰ÌzOâÝSô¨‡¿3Š/aœž"œp'õXXr‹;s‰£ÈÌÞE8k7³¬v)Eµ£+Lå4µ™Ùwla’ Êæt,[dìÙÛÄ¥“©W07£â­¦ÓðŒ7 » lºÐAd:lgÕYË5h°{1FžÌD"Óþ38™ EP=õ½¡cŒ™HdÖm.«¼rg†»›N9ËPÕx£žß4 õJ·ˆ JþâXøè>íE"‘¨ë€Àie¨rtï(‰zøÌú5­LCæþ. ¾DzzŠÇ„ƒyug§,»¯ãƒe!{^ÔÏÇÈÂÔ­]ìE"ûn¶\-TÔR¥(¼¸"díµ·¬1*&Ì\7vØO+Š.®Y{MJ%·bfru‰ì]¼gíþ£LCg³„N§”¤©Uvô@—‘Ë÷ìíæÒÉÅ+xOFª[=íÞµ#÷1ó.ÿ%¹“÷Ù Gž6o.ìH54k\‹ѧri42ïøÏI6S}[3é}õo‰©n¶õh‚¤×ì)®æ,ƒæ_~ÿƒ»øØþÇrÀ-<¦¹>޸뉄><–/皘@å¡Mºär Ü‚Bzôh”"¶ÆÓ›,»g£sÂI¯ðÂ/‘+/yš)ÙØ ß°„ýšU6wFËq™äQVqÚÅr»›Ü¥ç$¨ñd©rçí’Ç*ÇùA{ñê!Œ,`ÏžÈËë/»–Q|7N®Ùn<ë8£æRÅV‡)Úzm_ݬ2ªNôT¤)gÞ1¯8˜ 9wJæ|ÅpÆ>\w¬T¿d†°Mg‹f–ü:»ò'Fo@ ÛVækªñŽ•ìö'ÎBU{ö4n©oéõÌ’›ÑŠk"þ Fê”b֘ʘÆQú—ÞÊ,¹½ÀöMŽÁ•¤”yU ?rI|+YÖá&wé9†FO«9ú4H˜·Z—_L•FŽÐTV‡kêÚ Â/*7$ß=]n}•÷“N-´¬•Ó è‹W•1’„ÍJ<™ý   âv’B5ÎMÃÐ w¿=Àè”n!ÿªìÝ3®8.»ð(ëiÚÅ%v7×-=÷V|eiø%ÑÊß3žf^ÛØëц%'^ƒÕÈmѾ¦ïØK»ý­êm1°¬‡ÿ¼ìóç?ÏÜž!¯«?`ÚN¥ÿ¯·23oíùvòNùU•ƒ;»CÈ‘ýS[rzm¹zuó—F5CêÉÙÓ¢K}·_Ï̼ÝïÅÚï#Èil>ÏV_'mí1Ï”¤ôþUAð‘K©·’îpsÍÒs…f ¯Ÿ¶®ÙÆà_F½þ JïÌväd¨&äœÜ•a7qˆ¨ý É^ìŒË I/U¯ö= ía ó•âßSRò<«¼YçÖ<íš6Ϻ£EYæ3) ÀëàÓ£ìxÜ#9 =ÈHÒÖÙ¹úUI—@nÛÕÙÙ×·ñË{|s ʱ ““É¥ð"fü!ƒÔçT·ù’‡¿­p`Z+bΔ†ºkØ ea„•b`©ƒæ)Úó(C‘2t²æÉ1ƒüÊ4{›ÌI5$NV™1o¯œ=žºµõ¶¡¹¶¾Š0Ž:`²JÀŒ¯=|r”¥“B`y9OZWÌòÖpqªíàŠï¨Æ@'`q•sׄ˜;Šâ”°›<æPéÄvô¦æ&rÒ[ËŽV pÊØQ6ƒ|v„ýBÀ$ýFª80LÕ}Ûbo^bzn3Áè5(pr¤¿ÚÊ‚´2z‡§ÈI3•N¦`l§Ù+xÆP½3‚µŠÒ èuW•1½åýY¬w1 °û  /ѽæB¬$•ÆoM눉\€@4êT±ö9ãkjmΨ(~#Á-Œ¨ÌBÖÊ/º(éù…Ëü¤‡ FÃ÷¹3[|³buòÀ37»û¡mõ"`nâ¡ôÖgG&ÀG†Í8øõ¯ /FÑÈksIêÁ»&þGFuâÝbyÈ[0y46ÓÝ2 §ö¯¸À´óÙE€˜víÛ‹xY¢ÆšÙº}mû/$­ÿ ìáɧíu1ƈŒû z-ìo‰ãÐ÷Û¯V…l¿1}SA­ðj$~ϳèmgTy´®¯Þf¿ý—ÄšU%Ï”BFÛˆ]¹Ç`ó @Y® €cÛËZºõzžª[{ÊÒÒÒSS¨½8C!–¤Ÿ¾tÝ9Ö¢ÿÆi¤ùð5F:ÓqC×ò]Ëy[âxÓ6ñUµOpù’±j# ö0ÎpýfFµs$ùeƒ§i#Ôn\Sfí4˜”QJVþ)Îao9Ã>ãÔ¨¶ÁÅj°|ç Ș¾Š¸šæ†•2| ¤ ‰ l*UQÒ< 3јhgš¸¦usÀôP3ŒÑ 0^¾Â(S²R-œHšåLœ‡á¤Yåô–´Ö°Êe$ 8%äV:ÉÓóÄE¯A­&ÕœßÐf8eίª… ”¦¡2Яc¸j?jâ–Ä™:p¼v¨ ª2IJúšÆoéMê”n!;˜¦ôaÜ¢õGnæaÍÚ9ÚóË4@¡kø®å›·Ä­›¶i®JÐÁ'xù’±Ÿ5¤ 7ÿ:|Ý0ï)³"¾Ü׳ò˜Jœ'Å-ÚV︠usVY~YcŸñRK_—P¦­«†TN §N ›i‹Ó×N¶œ+äj¯ &4úü« ó?@ùã“é6ƒÂ1 ÊîíŽÏ“Jƒzu``”ZM2~9—ÿ…_KÝ‹‘º8»PcbmR5ŠÖõÕ¿1¦›Ï…L©À@£(W›ÏÆ€ÁkÞ‚-~^¨”n!4KNΙÐ@ $DíT«ò?„$.,lŠ¥¥Q§N ëR2’Îâ¦îJ›š»0y>.s”o9VŽÉ÷ެ0<ö•ôëküÐxÍŠxñ`[ŠAâ{†öiƒ 1ÀLšËÀÌ”&LíY ,5,[ÅÉÓÚY%ÆóÕ+vÀ×Çè+"+¹¥†€>eP¦³šñ3±¡¨T¼X ÆL+( À˜Þ&T.\Ñ[ȼ̂\¬†8`÷p¯·'&9Ô]㤄-)ò.£D FLÀ$/*ò}žfÅè501mÆBW¢Qjé"X:Þºf¦ q_:o4“ru뵦4ÅIJ¢nûj}Î 3jAAI]¿y6¥ >t3!ÑÈ×Çæ„Æóãï?¾—rjOø@K€¬ÈÏ–9l9võQZò`Ë” Ži×j`ðe?ÿqã8,.dEŠX»Ï¶…/Jª¿•¼ÌW ¬ÌFŽãL£(y%®,^ñxOÄŽÔ·¹46Stæáôµÿgºêûä§Ò,}´÷jŠSv\`Þš”üû¥K—.]úýâîÉ-l?”¥¬¥(½ (þ/Œ).loÇ/~ügå„Qžû¨ÈÈ®]U)Šú/¤ÛÄß’kíŸ:U.–•éUå?í«á«^!‰ ̹|™NE3òóùyøÓ»¿„o.P…L'y:’Té“ OdcÀ¡,Í)&›r0¥ #Y”¹bXÖIî¶LL¥ fìAf9d sÇF×ÑÊfUgq E—ΪýL1 Ê|Ö?$³ÁgqõV¤`Fm2È%@–É^Ëè2ZÙ¢f¡Zö#:¿æ¬=Š—“›Äz„é7€2`0Þ¼d–­€¡ѵ„³þ³”Ä$÷¸‹#عl ¯)U3ZPØ¿ä®:Ì”’ Ë4X…·óU¶y\‚¢× ÷zh¡‹ Õ4&^,[…ŸsõJƒ#íjÙI™ÓùͪI}Ò-â¯Ï¯*Š ò«y+–É*H–À\Èf²¬“¶eªU µ$uÕÔIá'²åÀ±°4ç2Ù¦B€0o^æ–ë¿3r™³é[£ ‡³´2,ë#ì_n[u8]J’²Ì£«¢žµóØÆ ª0s7¿®%‡×I/%Iɽ_GœÈe³Õt6S4æé©öú¤)q%19ó_þ§ºþ'²ÎÜ1õîiÆ2?qç þÀqŸÛZVbÓcÔQÎþwÊt¯L³¶ÍâWb•ž: þ51ÕÛÁÏÛ0%"æF‘ZU˜º-⪱Ï('.€¦¢ _iÚÞmm!µQUæZâZwr8Àá„öóÆÚ†Ä††ªrrê«Øõ¹Â/¿4ù²Ð{ŒáÉ Åš£e#¬tÇÊr°lµ7cƒ©½©×ÏŒ!2/ ÊfDy =+ÀÕ´sOAØmuÐpMi^NƒGÚeí'ôdXèW¶ÉWS}/nElÝ)ç5êå`æäat¿kùÎù*Æ2}®é¬àïbÖm4‡9¹,r„FwI·"6G+Ô±gÓ‘{ÀӉ˜´°5^È´Å›q5€ ¥"r»Ü`Ÿ±‹ƒiÏVû¥s»R SJQ“{Ûc°Ç¸»ƒ™ËX®ftYì·ä{=lü~èl õ!]kIÐ 4*^LrÀ(²B¦öóÖÔ¡q:¿5© >t3!A›m• ò<[ý'Ïk×™ðÀ«¡®NLAs‡~テߎÈÈ5\»:ý‡Õá )J IDAT>æ)€kå6:bµ—4m½XO[ì3âÕ±ÄùõÍo1^§ M³Sm(®ß'm‘/Z>¶{xC új\tì·"†´ZÕéQï\[mé¹½(,|¬Ë’2†‰Óà;ævpÚ×·9§ÜÀµ¾NúÚÿ¼Hç™7ç–͈ûâèÅEþÕ»_µü¯x~öº‘פæ8(Ÿÿ¶ëQ3Ÿe\Òzà¸.K£/†º©Z$†°óW­ S2¤A" =×ìILuá9‡Æ, Ÿä-¥ŽC¶‡tÒ:BžuåO«»J·Ìq<*(H.—W¦XAp„[7I~vvFTå`ràQyéÀWmO/Ö;KkMœy¤UAz‡K½Ãkÿ,Pï×üMh€³ë=ÕÔ¾âü£ÊÿºÈ7%èë [=ïr¥=¬šR4©²¹ÀÔx,nÙ ß?n1ç«ß¹^®ßÛbÉãÅU&ÐYhâ*:Wû ¦[J]å(ÊâóŠèsµ$må‰5/ú¡<ãŠ=õ·š^ƒŽkÁ¨eƒn-»‹=jFÁZktô4îªgLKéÚnCç·ûâ/Ÿr  ê®ÀPŸÜýBOž< ŽŠŠBÑG¼WRþç^¿á&œúÕÇâ?£€ìúÜáÍ·'ÌqD7øÿÓÝŽ$ƒƒƒ‘+>†ë…9Ÿåá&ÎWf!ÇÑÛ{LD„D,Þ2lXvvvåË3„BïÐÐ~~º E"3äUÄ?NVV1ÿ>Ðî@4 ¼ÕÐ~»×Ä¿ð]Ûÿö¦ùöâŽ;f²C¹QÊdŠrL:t¶|ynQýö[Ä0ïüì|à …-ºvð ÔþôoŸæ€*›ûÁw¿K}-P'@ Pº…@ ˜Q9‹œ¦n:ï»~ Å÷º”ìÞ¶=œ Í,Ðk‚ˆÚÉd ž^·‚È€ÐÖqÊŽE2™öWIÆý–šjëæfii Nnnî¦í¬Zwè!”n!Ä_ä÷òßzÿ׳J~÷E¿uGÁF h±èÝ;ÔÃ#64Hœ?%6VFÖz ÉbÙôîMÔÙÔB (ÝB @4LI^³ Hä Qô"x@ @é@ @ü{@7"ˆOÿö¢ÿ[]Ÿe!‡#ÿU"##IÝ6ˆ@ Pº…@ ñ—Œœ€@ Pº…@ ñ׃>0@ Þôì@|ºhÊó²¥ä‡ @éñ>yû9ô‰ÈT4(IIÎŒÑàöÓôèþ‡ÇjtÅÕ¯v ìè{øM£&¯Ê¼‹¾÷éé ‰DŽ=¿ù.â\A5¹MïU5 ò;ó\ê¶Ò}YZÙ³-ý:ú%”Pèò&™§~“¼aj¿."‘Hԩϸe§^6Í?%ðƒ»äÞ¨J0®ÂC N…ÊàQ 8/zÀ} îbpß^„Qþ~IôZøðàÓ…$%Ï:ÛqÙqCÒï'›_sä• @þh{ijA?²l¼5ò©òƒ>žsÑÈeú$UÔ²ÄB”p!âß’nMŸ=eÊúiÓ"gÎÜ…<…øDQå&,éÞQ$9ôð™õkZYíUzÇ´…¥¥¥¥e C&f`¢ýÃÒ˜…h¤÷b¦{v‰DúÏØ“YA‘…W„¬½&%R ϾùÊøë )žÛZ[·íä°6bügªW%jÐHì ÜÃA$²wñž±ã®Tªg[t6ÝÃA$‰DÝæÞ¨Lä÷ÃÝ»L9[¬ª®È’[13¹:ŠDö.Þ³vÿQ¦¡×YBË®j_e+˜XMJ!q,|tŸÎö"‘HÔu@àŽ´2J•íÕeС£¼¼}ú÷ùzìʤ×* =XíUvô@—‘Ë÷ìíæÒÉÅ+xOF Î?¿zìN"‘£›ÿ•\=V?.-)µþfÎâ±…LÜØaPÀЖoî<-ýM”§½ê( ráU(hš%AÇ›Ðþt| -»ur¶Cã’_2ïøÏI6S}[3åö'HzÍžâjÎ2hþå÷?¸‹í¬›oÑ Ó–b²Y”JIR*¹œbr˜˜úå¡•É.óǵ3hlO&ó_"==ÅcÂÁ<²~%µî$´]‹æDÐ×[z½%¸…Ç4×Çw=Q q @ {º5}ú¶~ý¦œ;·íäɧNE¦¤ð÷GÎB|‚H¯, ¿$Zù{ÆÓÌk{=Ú°äÄëÆ¯«^^,ì»!9íîéÖWWýt®æ†ª†ÕòÇ `%Ì ŽO~ðJ¢ÂLz/Š^=܆dÁ‰Ù×çõßz-#ónÜxÍöi³Ž¤äέÖË.¦&'löÆ“=(¨øãP’â‹qn¦Õ§:Yprö´èRßí×33oF÷{±öûˆr}:ß#-ÍÞ=#ìŠã² ²ž¦]\bwsÝÒsoIMyÆóƒ çN­u¾2oƾWjºƒ*Ý9»ôþUAð‘K©·’îpsÍÒs…Ê—{ƒf7™yâa潸1ª3©%jÃÎë×T&Ê?cN”vÖݤÉöfÀå± Þ¤ÕN î@¹àKhÑ*sKch¶ ‡@ݘìNõ*aß3ÑÐ& Rò<«¼YçÖ<­&žuG‹²Ìg:FÑ <ÍyJSJn3*|4±aÔ7~5þ~ň–%çÖj1+ÐÙktO«‘Û¢}MÞ±—vû[iêGðKx|z”«³cˆ@ ÄG–nMŸ}ùrÜù«±/:Ü/ ÊÉþì~Eâ©S‘C†ÌAþB|j0¹ÆxÑøC ©Ï©nó“þ6¶UãïÔb¶Ÿ4s¨“)רÎkd¼àY‘ªñjq ϵgãÃ>ÇY9µ_÷ΞSVžÈª €|›¼7ÕpøÂÉ.fLœoï;{¼ù­}ÉoI\4ÒßÕÊÂÊ¡÷Øþ¬+îJ)(»àwè;¶» zN•¤¼kâ?wT'!Ž »ÄÚ:±®WgíÔãîìvUn9 ÜüTç.3¦µÌ™ƒ¡î挊â7܈*-”i0Nç€ÉÝ ÀøFOhóäèÅ×jšƒµ7bÚùì"ÀaÚµo[ìÍË·ÙI‡2ÚÌÚž‡sm½CCœ tr ª"}ûä¡K ÇìØ2²UÓßµÚ¬£x=ä:³uMPxÀuK¬0@ ¤ºaÅɃßó,zØa@)d°Ø•ªl¾(Ë5{d´e¥¥t¥0“ßGÅŸ9{êàÚñNš[‘[+&ÌrJ[;¶_Ÿ¯Oÿå¶„|Ïž¬/‚ÕÑo¨“h躖í‰@é««Q¡'ß资cÛËZzëzž ]øh&“õeeÝ~œ‹‚ àÈfŠoÞ<1}zôÖ­•~Û¡sgŒèÞ^È¢8ýò™M?ÆœÏÿîš7þbÿ“5=߯ûö¬\ZE šŒ¡kø®å›·Ä­›¶i®JÐÁ'xù’±Ÿ5r 74çkEL&Fi¨÷QK*U<»Ó˜@Vä=HØ2ÞèBá…U9%äÛ½Ã>Û[#k“#Vó€iÔœp?óóáO°ñû.<Œeý]šžÐÌ7þEjÆ.W>Ûê›%›~šú"?õųcGwæc(îˆÆæE’ÔUS'…ŸÈ–ÇÂÒœËd› 9Øß¯3v6ˆ—°â`òçÙÙ™wNE-Þ˜ÑÜã+n Ñ] v®ÚŸ&&)eþå5~žþ‘‰Ú°lùYÝ^½òz‹¾µ_€™»ùu-9¼þHz)IJîýº8âD.›oÕïn‘RVA²æB6ƒ”eܰ-S­R¨)Püµ))— e™GVÇæwíÑ‚Iwð]y‹×H‡—1ëO¾¨ å9I7ÞÕ¹ÿŽiÞ}¨O'á_ó’UÌl6×1k°YÀÛþðÈ žûÀãV #Ø|é L³¶ÍâWbp;øy¦DÄÜ(R« S·E\5öåÄÕ”¿¸rî÷t)©GÀŒ¶TUêRxþçý&3‚\ŒpÖ6œÂ'o”ŠüÇEÆíš4¦Ëá Å›—9…å*=¬€†:ƒ®kqñ÷ê- =®ßMEA¾Ò´½ÚÚB ÄG›n1L&“Eõ SaL¦ö"†›;;™¨o¿\ ]¦*žßºþZÍuØ»uD/Þíu3W¬Oa÷˜¾çǪ¥uC¾ä÷ûS+ Úôþa‘‹äÌÑcÏÁ¤Ç„¹ýªn×/Àë8uß® ]_ìœìýíômb§á¡‘ßVm¥É®ý%á Ö¼GÈÂ^&dá™ð_³@vsÕÔÐië²LFüùmwÓ;ÑAãæÍ^“üšÃCbD£Á-¯]í]´Á§“½“×Ï%C"V{Y|øÌžÑ Z†Àý§#1S,în™1ÜÛÓÓgì²ó,ÿ¨¸yÎ<À­FlÝÀ?:µ—ƒ“Ç÷»†ïœïbXwŽßjÀ(û ™ÈÏÛ¦N‡Ç[úFnŸb°o¬‹ƒCÏ€óíì˜Û•×(ïÌ)lF„Ú_ puêÜó›°ÛNAÃ-K3rÊ)¼egEìð.ÝFïaNŽŽaÅ=õò¸uý7oÖywvpöß n¶8£úÆ>yZ䌛Óþ²û† ƵG­#ܹàô˜ôõuœ¥C¡C*ð ¶°óW­ odH5ÀsYèp9Èݱƒ{ð•aÛC:q|snÙŒ³•úhjGÞ²;›7‹GÏígðº…üè|aZ¯ ›Ÿ/ êÌmDOf·õ`¶ØgÄægÍi#hP-¡n¨“Ðv­÷ë- ½þî*Ϻò§ÀÕÝ òøh¨»8;sæ¯))ïW$ÂA€.UGI€p0Ü(:ô‡}û0[ìú=¼­:yžË¨+¥:• œ ²|¾Êß+2k?ñì•oÛæì<¾4"9H”±©ç¡’/×Þ‰sWŸžé:õ~³ÀÝÃl³Vø9ýùñÔwŒ 5Zs"¸¥®äåzüØú`U©Ñ·I—&Ú¤oêÓÿPO÷Ù-†ÅÐõW¶ö0é³›¤Ý»}bç‰+¹Jøw ½íé“âÉ“'ÁÁÁQQQ(ú-ªìèo|.|÷ûa_d’öà»QÞ¿]вkÇæl €,88¼_Üà3Ç'Z¿×GÄüÛ‹þo ?ø,Kw¸ús¯ßð Nýêc¾tøž¼_èe×ço¾=aŽ#û?ì’$ƒƒƒQ÷@ ˆu/þ›6M²·ïiôÚ fœdü°D¢îÖ•WÀ´§RfÁ_µÐjÀ¸¶Ýz¶¬\¡¦4UóöúSyR]}+?©¦ê'|t†3 ç—YýÝýûºû÷u÷ïrO÷{¤”’PÑ$ )<¹xÄÌ]G/ü‰‰>÷ý~öîߦ:±Q܈՜;?á‡ÑÓýCJjˆ¼”í±O[|Õ³þ¯1o5$´_ÞÎø—è}ykèÉ·wÜé4s¼âñ§[pðà’¾}'Z>oÏô3€ö€¹b†„]Z÷ëÕËwÆñZ™Š;{¶Þ#ç¶ábô¦ÁK£c®¤®Ùšzyîj@ûïFù|¿È¯ÀÿØ»ë°*–ÿàïSt—„€¨Ç [±1ÅÆDQQ±½&Xب€ aw]±@¯…÷˜ØR ‡î“¿?<ŠußëõózxeÏììÌì첟Ý9©¡á‰?:˜$H¼t+0qpš2мe[‹!.K=fÔÿì_TIY^  ×f°M—ÎÍuš:¯riUx÷è>÷Õg’((ÉÑ fB~) -\vÎÑšlѤqËþn¯ºm pnü+]R3T;,\Õ<Ìój¦˜væ?µë¥…ý)Ì^9@—EÍF!ä_DöAAÎλSSã ²Øl9]]S]]ã±Vy´wì´‚åsf okãÐ¥Ï/œ>Ÿ (N÷³ŸÇÙ°ÔzÁöîd<:´nÙÆW%ú­~°”ÅwŸ*p]l3jår; 4íUˆ{ög¿ð¦(ÒßëQëEžÛlN¹Œ»ž­ç0ÍÃAe>¿ºaeÀ3šžÿ3§Ð—N_³°L ‹é>W¦ÿPaª?à÷ÿŒ©ÙcýŸ=¨G|GË}í®g¨´_õg{j0B!ÿ64I©‰ÞÝ"„-zw‹B~-ô\!„B!„P¸E!„B!¿Ž/}Û³³_YY1“ÉVRRóôœLEùuýï&‚'„B…[5-ÿØØW®ø—O•¡¯ßÀÎÎíĉ5Ô^„B!„òýá–³³ß­[G#ão£Ðx~X\ì…¶¶ ƒ‚<¨É!„B!äkÈxw+6öadÂm¬N®€p …­sîßvvö«H¤Ñë$Ÿ_ñqg׆©æš?ýÛNÔºãóâŸ-j­òÍÃù¼xÞT®í8B~=’¢”„<úæ)B!„üÆá–““O\Ü#ôÊ•KÛ 3;éÝ»¨êÉùç<ü½Ã3ŒÚÚ­÷;ëÖJõŸœX^Ä¿½ÃÕ{­ÇÝLí8òO§Ò¤ç¶è²¯K.HùkÇL›NM¸\.·i§Á3¶]I.ýìdú¿þ-FžÍ”|c¤’ùçˆdö}aÜ®~-Æ„dK¿3ŸÂ[ŽÍ¸U´î5nÕÉW…’ŸÛªe‘[‡ò|U@”´gP‹á§Ò¿;ô¥ßÜ1­_k.—Ë5ï9qÝ…ÄÒøö‚lÄ0ÁÀ»{•tÏx6 - EÙ9ÄwÀ"xb‚ø•(-¢ã„B!ßn••åçg¡=Pc©DÊÂüüìêW¥o/ÜéºÜv€_ `êè2´. y³aó>¸ÏçÅ'œ;áÞßLA¥Gà_ñü[ª3˜ÚÃÎ݉ç_õ´T†\ƒ÷xñü}öÝ&„òyñQ>þçÏÅðyožîœÙZ‰Á1ž²¹½ágãyQ:ª©›ÏX;ÇuVK5&ØfãBù¼ø(ßÀ !±|^ôÝ3GÛréâ>/"xº…àöŸ±7<ì ŸŸr)Ä{xSeú’1òÄLs>RÒíÉÐë×CO®$>9g¬û£Â_ëÛ˘œÎ[.ß¾}ûÖÍ¿.Ú2FãúÊQއ?s’’켊 Yº}Vl_ÜUý;ÄEoÏžuP8öàèèˆS³´.-˜RÆý˜t{Ë ¢J½ðj8r‚ÝêýÁÉFŽ;^õ@Qõ~B!„|c¸Åd²ÙlÞ’0 !ƒÍæÈÌ¥,þVH€úÝÈ+¶q:ì;ª³ÒC¹îÛoËwpp;´ÌôõŸOËÀiÙ×TQ³Õ°vL@¹‹M}íæ–¦À«°;iPV- ?³;$Q§Ã‚5Å™—ÜöÇ(¼¿qÚ"G˜âO7¬¬’{ãÔA^±\½‹WYä^:sî 4;Ø/é§©ÔbÚ‘ömã÷9XOwöÏi>r‘×ôzÚáä üYŒ^·j’U.æ]EW¤Jî'©õ=ÕªU}cãúæVN[·Mj)LÊg?˜;¤cS.·±…õ¼ƒ/ ʇK$yœ­Ì¹\®yÿ9‡¢‹¥€$ïéC;4árÏÙ‘' L ÛlßÓ¼·E);ïåˆQc KÆPÕ×æó –‚¶¾¡‘ ·•ÓŽýóMy{=,q&ÏwÖ ‹Æ\nãvœvÝÉ,y½³o‹QgÒÅŠï/¶àvv}R@”´×¦Í¸à̲O-?åÔ §³Ùy§ö¶?‘R–ù—û‚­牅 ~ÖíÇ®[e? g—v­ºŒÝzÝöÐÞ]Ú¶h?bÝõL±ì•eç^è:¡•›¥ÖdˆÓ0ÃôG¯ó|0î56£F6ÒwHZ  †÷Ñð Z¤Â°=¤H„”ŽB!„|S¸¥¢¢ihØaÀÓ*KÅÀA(IÔõõ|&©´â9³–†À›ÿSý׊Œt×zq屚Ú7ëÔ¿;9ì©@»g–m»6âÏßå—_þÅ]ôÜqÔwÇåd€¥¥£Â,}û8: @qÊÃë¼;/re\$Æ]ôö<¼Óç‰(¾Üs×aŸã CÝ@£MŸz€º•Ëþ‹¾3ÍÔëh¨@;œ|;qÞ“;ê.§¯óÜÜÒìþæµWª?¨ÒfâNÈ<'7¿³7Ÿ&å š=VùmiÂ8íü|G¿üáw££ïûõ‹ß:sÛÓ&þ•ÙgÇÍç—ßÙ¸úJ¦(-xþäí)ý}ÿŽŠŽ8:Iè8/(­,ñð¬ÁZóƒŸE?Ü?´(ümíÑŠR>7³²®WðøNr„ ÷ ìö?ˆŽ~xÄžqhºÃa¾:q—ŸHQ–pãa²îóR„gÞ»œÂÖQ‹ùi£]ãŒô÷®¥n½÷úA;£ªSôˆrÝј{êúß·öôM œíúnäž«¼»AÓJŽm</Y#…V³¶ožØ@oC‚óÛh¯ùƒ_"¨ E%”®CúóêMúE"Àú}P1>®½u`Å'!’ÐñA!„o ·<=§4nÜI5Us€ó@ ¬|Áå¶×Õ5–™‹\=K놀$æFlÅ=RIµ0L’ý4ø)P¿÷ÄéŠÑçýý"uûL™d®€Ô+WS«>±$” |ËsR±èC$&I0À`°˜’wÏëßÕ®OW»>]íú-xLo[ïÁn4ftku˜ZmûÔg¤'fW‹WXºV[/Ÿ]Ùõòô†iýÚ7kc5uCpl±’lÞ‰M»%cÍ5X,vN'}'—‡ì†Sæk®¥¨Öhàèf¬´¸ôw7ó”G®p°Ðf³TŸ?IçÁ‘kO®þc:užµ™"K±þÐE3›Ô::+Éø9ù`ªè¨ («X"zzò•©ÓŠq-ÕY,µ¦£WÎáÆcôî©y%ªX”ÊãÁjRˬ³D9ë é¢Ëª½Ñª5oÑÃÍÕ˜ å†L8uŒî¤Ë‚¼¡y#å¼·9Y5º™QqÀK‹_: [›9~Ï®ÑuÙ?¸—ëÀØ, u2JJª´i¤«XUN– =°ä@Lï‘B!äË’Ÿ.:qb­íÂû÷ƒ3Ç$‹” CI¬Îå¶ïÜyøŽ“ª%e™ qžÒZ¹aŸñ½š¯½=Cø%¸r'mö¨†3f9eÝaÓH O,É. z ‹F6´ý^Ôݬ'b·¾–@Zhh¼Ÿ»z=d&P IDAT,Ë+ôÚ ¶é¢ù¸øëë%H¼t+eÖ§)Ygï¥Ë™uëk™ç3Á-²„ö9ùV,E Åòkm&› ‰¤Æ#dbP©ÑG׎€¸8åiÈ®¥ŒËÔ¸¶Ù(5[ªeªY~1ô››f,e•Ê Ù ©D˜“œ-Î8<¢åṚ$$&æ24+Vgi˜ÖQüä.DgÙ¾;ŸO‰óøùPÑSe sRòXºõµ+N,uÓ:œ¾°þ¨.¬Å×bSšþ•Õ|ú°¶ÉÁW_¤h1¼¾ ÒÚ­jó*k«00˜lSNY®b-&RÙ5JÎÁ€ˆ’Žýá•=åôYç&?eàZn$LN!áwARy/ŠY @±ÌʈKʇ€˜,:<!„òဠ gçÝ©©qå_s¬«kª«k\3Öý!sI^|ı@¿måoµ<ö³ŸÇÙ°ÔzÁöîd<:´nÙÆW%òo\‹E#.2¯‡¥”f?CǶÈúëú›ÒÏ—®(ÒßëQëEžÛlNͲÚù +~¼{üTëb›Q+—Û¥i¯Bܳ…´ÃÉO&Í ›Úíe¯»zk0–’Q»‘sŽDð%MôÕ‘”#‚> @qä!ß» §Lqs­n Æ1uþ⬆RaÎ;¾H]rñ4"1K56 .HË*•`0 ÅR€IÑû"ñwæSÛýаËÉZ]{šÈ±Ó ÕÅñÙ¢ÞªlâÜD¾P½¹ºZÓ!íë.^y“l6¼eƒ†ÝÔ÷;ùRgàRC6ðÙí›g«aɪ‘¶‘<@T\fÔc„U}…Ÿw:ÔôEö ä*Ã-–”Y(¼ƒ´k¨k€<¤¯„P …[„BùŽp €¯ïÌ/­—{cŒA—Ï\–ÄžÜ2êä–‹EñGú©ü-ý„u—>K8ÖßàXÅÿßêgpèC¼õt§KçQ֓ɪ®rkYóÊKØ"ÿ õý+þŸ|i÷´K»i“C­£ã¥É œÜ—:Ù´5U“dE†ù팪3ØÍDAGmLÛµ›·Ÿì1¶±äÙ~×mÁ-÷;˺>gé÷×zëÖǺo›ÐR9ãÖû™—:ºì4®Õö[ÏôÜ:Ê$'ÌÓç¥@y ئxtùußÑuR.z~#Rý®|>!.ÍJãó•!¤½úkÿFï´®ë[+m<`Tcoÿ§ºlÓ˜{f£O\ûíõä”ë m•á´+µÉ‚µZŠ:=Û&øÜªç¼¨îžTdÉ1Ë’“3͹š_}‚’Y£«ëÚ+`ë´fÃÐ`þÌÝi oD:|œ0ƒa wD-EFäv†’6Jo¢´h“ ÙN !„òá!äk0Õ»®>`°Ñkלƒé¥K«I/;Ÿ£³Ú(Jý߯t›`±¦€©Ù|èò=KÚ*!QV b4Êw_Ûši×çˆÙ:æCÝö-µPWnëí÷~¹Ûè6«:#6Wâ íÞ«VZÍßfÓn¢Yÿö=´ƒ¿+Ÿš$»KZVä¢Ù°ËÍgرp̦”¬Z?¡½[SÛk¢ßÞé\9€ÓzhkÖµø®ê° ×ØÊœ×ǪÞ¾‚\¾þÀÆŽ®6£’Î]ûµÍ+³FßXòÜkÎæîˆC}Tâ•›ã#Hºþq‰â4ç"e3òî"`@à †®PT¥îO!„ZÐÍYR“TúÛÍnãâââããC{ÿ?Ì®!÷ÿm['âb©ÁÉ?D,»¸¸„††RSBÈ/IM@!„B!nB!„BÈ/ƒÞÝ"„üè?B!„üÿ£Ñ-B!„B¡p‹B!„B(Ü"„B!„ ·!„B!„P¸E!„B!nB!„B…[„ŸIR”’'þ6J!„B¾3Ürvö›:u»££×ܹ¨¥ÈïCš2¦Iû¥J¾sý²È­ÃFy¾*«¹¼è޳y3‡›U"¤Ì?G4éìúô«¶$ÉüsD‹ ¢oÚ(ùH”~s‡£U.—Ë5ï9ÑýòÛ2Ò’ØS µhÄå6j?lééØéÇŽPüÜÓ¦Óô[…¿~>å—ˆÒonŸÖ¯u#.·içQ®ç“Ê~°TÕ¶X¥„’÷A£¸5ôÛ'¬­„ÒÒ„àÕc»·àr¹Í:¿'"Oò™ã¢è髦}w¾àkêU³ûæýí6Òåb¦¸ö«­ÏËXK˜à׿Åȳ™’/Ö_פßÚþásQÁý5#f§ÑmBùÏ…[ÎÎþýúM½rÅÿüù.xݾ}ÜÎ΋¯")ÉÎüý¥ˆSNÏs\nêñˆèèÇg\´/Ìw Œ”<Ûæ¸îU]¼W‘៯áõ²¤üâ<áòúñcwEçW^•.å2”EûN™¢;/èYÌË«®f×–Î;™,úîRUª—©Õß?üv…¿ŽÍ5WÔ²xŒ§– b–‡7]w5òuô]ÿAÉ;7>.‘.ÝÛ2/0ATѵիfa #v¬0¿¿.«¶«ÕÿçZµî©¸Ó2T-œ§}Ö…fRÀE!ÿ¥pËÙÙïÖ­£Wïìoö$sv2dÜ“âÐ ¼lmR{‘ß—´$þœÛ¸ž­s¹\nÛ³öW’ªW–7ó”G®p°Ðf³TŸ?IçÁ‘›%³.}»7V£éq¤%¯Í›~\ÇÅcJ}Ia)äUå+…)¯"AQ™”©l`¨ZmWJËd§ür‚ü쌢ÒHß@¾åªƒ^Ó;ËoêôÉ¢ï,UµS÷'%¬TüÄÏëw–Ku0>ì÷/U©c³ÿqÌÓ‹ë]™í'`Té-¢·§ÿØ”1ÑcF³Š`f}m½HrŸÞHÑíÐH•Z›4/þªŒ>_ûŽì†Sæk®¥¨Öhàèf¬´¸÷¯[käps5&C¹aN£;é² ohÞH9ïmޏ¶þÓ@±¶3€¬£5')ìdT=§%Ã*±ͬ-h#Ç€è]èÉW¦N+ƵTg±ÔšŽ^9‡w:$^@Á¬³qÞƒ»)B: BÈ"Ü*++ÊÏÏB{ Æ=ðn) óó³«-'Ÿß}`Çò5îè˜7Pæ˜ åóâ¯[»ÿØË·Gg6T16ÿèƒëñ|^|¹îýÍÇÐjzÀK±|^|ê•ÐÀõå8†ýgì {Ãçŧ\ ñÞT™y£Ák‚¿<­;·è0lá¡’/nl¦T,ªrÕ,–‰Áä0Ë;SQ¯NÅKEWyÉI‰¹ McÍòñ –†iEÆgJR…('9[œqxDËòÙš Ü™ ÈKÎÑ>AJغÑc|Y³ïŸÑL‘)¯¢€²BAÅ^“” ¯"/ãÀ•’õ|Y‡ŠI)šL¼)ú4ªŠj}V.mÑЬy¿Yë¢Î†…,ú°Ö„м)•ŒX ïþþ‹%Ý:²ÊøR¶ ‹ÁTn4|ÞXýØ ·ø{KÙ›‹¼D3·MnXåvCNþÓz…§æÙYQVB¦äC?þryKSeôùÚkÁXÊ:*ÌŠÍJ¤_¹–¶ ƒÉf0å”å*r`2 …´¶þèõ ëhfÅg3´M4Ê÷SÅÐH• aNJK·¾vE#±ÔMëp øbL¥:úò9o2)Ü"„_SÍçP˜L6›ÍÁûOf‚!d°ÙÕo42ôŒ›êöÝ@泸¢Š¿N}l©1¯ ›M?ì;Ò0+Ücîm†õ¬n‡Do‡üÙóèÁ ¦Â×'Ý^HëÌ5n3ÍíÀDÍ0/‡¹//ó¹È+þÙ´Ì¥^Ó›Üð›½/cÖ¶·™Šé¨ÙÕ—(q¾¦ŠÊª…ágv§t±é°`Eç“cþ¾äºgð‰iÍKo™sèöóRÅ6sûŽªQH+·Wôä©zk!õÜÂEgõÜÏ>j¦È¿;dÛïÄÅü„¦N»Î-g”¤<>½ÎÉ}ù¹^A“t™WÆ­zš’ËQé¾j]·,åyªD{¸6ââ´wùb(³aæët©VïõŸI'f‰ ÆÄiY¥RˆSÏ-ù¤$RàÃFÙêj³Qç/ÎjÈ æ¼ã‹´äi'J žù;MÝ+´;dV'-–FÃF*Y‘oKQŸ äÝË÷ª¨Ë5’™²y×ùÇC¦ Ë/šµs¼wÖHФ~3S%ƇËvKŽÅ`©´^p[Ô[• @œ›Èª7¯ÜéR™±!„_BÍ¿**š††<­z© „’D]_¿Aõ¿¡æ®¼xÜ©“bÙ3ïµÞ/*ŸË‹òÜ~dᆱ#Zv5ÞøøŸºè¿îT<`4 O¯á½MÄk\wóÙ>iÐŽÌÞ}êêV.û/øÎ4P¯£!«¨H¨ö;o¶MwƒÔ3¾…5—ä|Í ¸‹ž;Žú °´tT˜¥oGe Ò´ˆë^å°ÍZ~RHËß÷RÙé‹³Òødä‹9ê:òLqaìùþÑ"a™(—·qÚ·à„(èè(²åµ4KŽY–ž˜œYTýn¿œÙ !¦‰»m8Í‹JHzóâÖá5Ë϶ݫNÅ5UÙ ϰw¥âÂèÓ›öò[ë×và¸V©{¶žy]$.}æéóRH²J"ý¸Qè÷×:m߯cÏsÄRÿÖæ1Vv^‘¥’¢øðЛѿï7s‰SÎÎì[fï½a¤iYŸÏç§e—È7c­|{[À½÷"a&ÏÛ5›±Íe­®(#e ­: š”kTW·¥Œ¬ê´³ïÏ Ûès›_&.‰ñ:ÚbdwcÃk)¦ýP©j*Kºó¬”Û¯ÅÇçöªìwYUh®ß¤‹AÒ‘Gžç‰…™÷ן.ìjß»£r-¹†ÓŽß¼^áܪlƒ‰{‚½muü´^]u?²µëë1s’r„_Ѥæ û}Úçk]«¹Rm ó}kÕÚ²eŸ>y´2ŒŽn’°ý||±¸$9lçΈ2)8ÆF5NôßxêUžX\}f£O\ƒáƒêɧñZ u9tZ&„_RÍÑ-OÏ)vvnqÌÉÂb ;|Áå¶×Õ5®ž<ÁîžgùÅi¯^ÔmßR eq•usŽv?ó×*sÖï¸GË¢{…–ÂkR?¯Êem<î´Y°ÂmÉì®~yRõ¦¶+˜&®QjSKJY Œ®k­Ý°rYŸBźÝì½¼íŒÙ?±T5kÉÊRmTOåã4qúÇý.«„,ÆH¯€¬•n“,6±´ZØ,9ì:TŸ%Žÿ°–Z#µŠ³£ªŽ‹£f`¤§ÌÆ—ëUý®žF«^u3oGåÍæê2kk1ãÓ>ÿ;"±¶–ù¾µjÝS‡eªü…•y´ æOöÝž±p­u«%R]‹=ÍX<&“c6%0 dÕú íÝ ˜êÜ^ýöNçÊ@Ilø[õŽ](Ü"„_“ìøÃÖváýûÁ™9É"%CÄP«s¹í;w¾{÷ÌŠ½NF¹·?˜ÖlÞõü*[ÌÆ]äÍæFyö´:™,‚R»ù×.ŒÒ¾}Ã怙ó¬4R‡œëtÉ®niäÑ-!‘bÓÞÖ Ç¶ ÖcT}bëÙ{érfÝúZæ¬}?~¶þ“Káñ9ª]ܼFšf\XvT³¯zÕ%gGtÚö¤ò™?9“ζÃ/ZÚKGµÇõè…à›¯4ì>†o:éÒ§åSì¼/Ò£gÑ¥1íÖ?,–]È~®‘¿íÄÒß/䌉‰qqqñññù–A˜à7ØæÚŒ§†ëÒdä¿v÷âíá1#¯Ù_ØoC½€(óÉÃ4ö-êÈ3qÚ‰‘ý޽4ÙXö}»K¸Õ YØ´âÖX,vqq ¥–$„_‚ìÛ‘AAÎλSSã ²Øl9]]S]]ã;&}kîÅýìçq6,µ^°½;­[¶ñUvIìxáše¶c]—2Q’xÅïíýàñS®‹mF­\n”¦½ qŸ-ÌÖs˜æá ˆ2Ÿ_ݰrw¸¡Ó¸ÕU—<û1”ÛÚoZjœ¦Ó6º0ÿýªàû I/nBÈOªk»¨ßÁÍg̨Oc4ñC6áè>Çòi·÷¾ÖØIï3cÎ⌿ö<2Ÿ{²½|I!¿*š‚ÔD£[ÿ4ºEþÓ$9·\§íâ·}.‹#÷Ñž+ýÂÞ@ÞÀbÔ¢ ØÈžWZøp½ýæÞ^à ?¶nB…[„Â- ·!äAá!„üZè>:!„B!„ü#Ø_øÌÙÙ¯¬¬˜Éd+)©yzN¦Æ"„B!„ ·œýcc\¹â_>U†¾~;;·'ÖP{‘ÿ*///±XLí@!„B~"†¬XËïÖ­c‘ñ·Ñh¼ ’¢Ù·¯CP5ÙÞoøîVÿþýi¿B~!ôî!„üÂáV¿~S¯þ½+@𘠃7 ‡ [äëëT~ïVå*â÷Ñ!;·¸í.|ÍFå¹ö«|Wõn Œâk‹:Oäüë[IÓvwÄnóÊ_3öõ³þyÙ¥vnB!„ò¨9U†““O\Ü#ôVÆZÚ 3;éÝ»¨êÉÓÎï ð>ð0W§‰íú- Z}݃(7w^Þ»b™m»¶K,û¢Ç—nºñ€0jÏ2¯óo…ÿ¡ÚB!„Bþ_­²²¢üü,´äªÐ "ea~~vµ…âäó»ìX¾ÆýóÊ p ûÏØö†Ï‹O¹â=¼©2ƒm6.”Ï‹¼níþc/ù·žÆìª0ÍF.šÖ¹8WÀ76ÿèƒëñ|^|¹îýͪ¯òö¨K_ûP>/>Ê7ðBH,Ÿ}wãÌÑã¶\ºø†Ï‹žn¡ÆdhtÜtãR4ŸÏçÅÇœ<¸¢k6*2‰òñ?.†Ï{ótçÌÖJ àZM¸q)–Ï‹O½8¢¾œŒbW­¨ ùîŸçž½P”põÔõç9Ù™sŒg\ª½vÔí!„BùÃ-&“Ífsðþ“„™`l6§zj=cã¦=n óY\‘|‹iGØ·ßç`=ÝÙ?§ùÈE^ÓëUn}l[Ëe¼ztðý‘Joq\êý¼T±ÓaßQ•zÌuß~[¾ƒƒÛ¡eM«®“”Y&e•ܧòŠåêõX¼Ê"÷Ò™so ÙÁ~I?M¦¤4ùæéu3—9»ø]*6î>{ÙœÖ …TV- ?³;$Q§Ã‚5P0wW;êxÿF®+WR#B!„6Üäáì¼;55®ükŽuuMuukÆZ²?Þ=~ªÀu±Í¨•Ëí€Ò´W!îÙ¢ZVñ³ŸÇÙ°ÔzÁöîd<:´nÙÆW%úßn cøï³r¬Ý ß¶wÃE¢Z ùÄo¼ƒpÍ2Û±®K™(I¼â÷ö~ð§Å®±1”ÛÚoZjœ¦Ó6º0ÿýêëæw—];êw„B!„üh–¸rÅ¿|ª }ývvn'N¬¡ö"D&IqZŠH×XEMA!„B>`ÊŠµünÝ:zõÎÞøfO2g'óGÆ=) §tÊ IDAT½pÁËÖv!µù”<Û:´›å0—¥µ$¼östÚ' J91±ïôLÉlW˜ñèäæyãõ¶ìfi9`Â"ïkI¥R&ß×éröwfþ3Êö]2Ã@Ä'?Yi@vÅGïîU®wðŒgÓPµ”%«Á@„ò²¾˜­&â\!,Ÿš4±lD(!¿à« óÉGÏ;!ƒúºa祠W|^<Ÿùôè‘Vu926À6ÊçÅ_cò³r¨ôÚÍç=ñj­ô™Us–oîÎçÅó¦rå~¨9Êf¶kw^¾Ïç½y}úØÛ–jôzÛoJšÿøD¸bÏn¬¿ŽÜÏýr„")Í)¨øú–VWW'‹ïï6"þÅUö Odµš¶åÀ™³Ç÷ºZ+ÞXë¸ì_ôë7©Ú8hOªü™…*Çvº=Š?÷ÍâÈð¯øOÚŸ2‚µ‰Ð™m;( ·IA?ZµqÐ µvÞÇÛnH{NG!„B~R¸åää÷½…€råÒ¶ÀÈÌNz÷.ª|\û眇´*½±;`ËÖ££X­´ÕûßMs(âßÞáê½Öãnæ\“r Çî߷ݱƒiæC>§.&ëtš¸äì©ñ\yê'¿!IføñõA“§ ×}|äêÇ`G’ûô¨ëÔ!=---­íÝÎD ÓB–.»œ›ÿ×b»¹çÓÙ{­õ{”Ÿ¾làà OŠËW*ô>`á $‘§×Lµéaiiim¿êø‹|Iïþ®]÷ô<ݧôja¢§gÄí2vÕV§6¢¨¨lqEŠGWŒµ²ìfi5Þõϸ)dçùI9«nH˜zÙÕ¶ïTÿÇùùW=fÚöµìfÙËzÊê“ÑEÂäƒzØzF—ýÄÕ…A ê¬üñ‡²Z•O_#a3dFµ¢kÈÎËr@á6”Ö(“ vÃtêGƒõPöæ+Æ£¾P]Âì¸Ñt EÚJˆh„‹B!?%Ü*++ÊÏÏB{ Æ0Q7ˆ”…ùùÙ†j³Ž\)÷Ï>¸ÝgÉØqmÆ p ­¦ܸËçŧ^ Q¿<ÍÖN{¿äóÞ<Ý9³µ£<¸é?coxØ>/>åRˆ÷ð¦Ê 0¸ãV„Dóâù7Â6µ-_U®éô›|^ü%k=fµÿWÅP7Ÿ±vŽë¬–jÌÊQ¯(ÿóçbªn‘¡ÜÂq]h,/žÿWøßañŸ )šŸ×]yW§Y¶zýι¶.ÓÀje?¯³ÊçË £ß”˜ºà¿”èÝÕÓqõGõ7«×Ï®iòÉ ¸²Š ,lõG l ¾q#d£e²ß ÿ ›Mj¨õÙzÂsˆ~åË[êmFt–Þ ‰,€’è[‚#-4¶za`Z÷ ë7/{_²:¬Ú³}¥oþŠ(ªoÝ˨ÊP±\ƒq›½—ôÖ+ÏZøîNV·5§Â®œmðÐÇãf–HVžÂOËùªâ‘H©(ýÚÆ9;Ó{ìtl«Æ,xàéÁ3[zêzø³kÛÅîKlÝÞóÝ—Ùȱþ^™à;"Ѿâ'y[•àJŠJ(]‡ôOÇ‘DÈÙ  ëƒN@,2îUO þL$ME¢â–ua8þ+¾Oð …ùxZâ(’(£ï&'„BÈO ·˜L6›ÍÁ{W' !ƒÍ.¿ú’æ¿ÀxT /,6öÌÇ^ʹ…b(˜;=8¹oƒŒ3î[Vº‡¥×åê–yé75Í{ú°Q§Ã‚5Ph1íÈû¶ñû¬§;ûç4¹Èkz=ÕÖÍ•âÎlö=t7ç‡j¦¬Z~fwHú‡-*YÌܳ¶W.æä&¿cùŸ¬ÀÒnÝR?½ö$G @Zv·PjÞ±ÎçQ”Y ÎÏHLþ·Êâνo;¾‡‹©Óm\ç’ËÇ#ò¥€$'âüsõ¡3‡6Qg±Ô[NØìë>ÊDö>TiaÛƒó øEE/ÏóÐeX+Uéû»g) š3Æ\“ÍT®?`ú­'gᆵr¥/.Ê*fiª~~Ê v½1Sû7ÒPP1ëiÃef&½O“‘'/öÁgÊ)κ³uöê‡mVosh¥ÊÀRTae?¹tþÚ£d´t>zÕ˜ `(·ëÖ±¾ÊÏ}”6ÿ²UüdCòaȨŒýÀR'£¤z`#AÆ= %´[Ac˜@öf«ÇFù‡ñ~²N¢T¦$…?V˜ª—X Ó1A!„ïQóù?MCÃFéa x ´þp „’D]_¿AùaìÑ sœ]lZ63R1l;ľmo3A'·dëÞ¦@âÎ5®»„À1_&§žD_¶àdZãÂö×&ÖÕÒQaqêØô©ÀÊe¿UÅFêu4mÄée¼õ]»rgœ\¯óÆ~Íâ.zî8™ÆU¶µ™l¢¥£Ââèõé¤$y¯uóLëÕa3˜l&©DòáZ®üLÖçî•sÌdÔÂPáÊ·$F‚ºá¿´èÅ™Ëüü‚¶}˜ @"I˜‡oet´Ñ*HË•j©—7 y½FM@æT M†öQšü,¿;ø\¯-M• z—š+~vz¿³“¥æŠ WÐ³Õ T¥qé…(Œt$¥Ù諸j`*iU|Äb³R±(WFžÉïRd”STˆ3d˜ß»ô$¯cM& ÔzþŽEû÷.Ù·A¨Æí7yñÂáMTþ‰7uÑ8*ŸySn$LN!áwARå>P‘7J†üi`ƒˆ/#'z *Sh¡q2T”!-DñAÄÎFâH(?†ÂæcG(€€2˜4å$!„B~J¸åé9ÅÎÎ-î⣂9YX tÒ£€/¸ÜöººÆ¦j“6u“/op¸°,µæÃü.-è¤Ò¸!ç-‹ @,ª¼,•V½e,--*ÀÀ`°˜’wÏs<’&ªø4OÙž $·Tƃ=lëÛŸ¼“ J…[¬ ˆ?3ë(+2®õU[Z¶PåÝ-BÁ¤G{ ,&"C(» 2k‘Y¤ÞüSü÷‘äÞ?~‡ÝßýÈŒÆåí·§Ì?’8ÀAMW ¹)yRÉë3‡"ÌFi†ŒÎ)WÐ@­ÙÁ·Â™U­¼¸ €XUO•cl½ÿ€}=©0Ÿ!Ò4¨2x*_¿—…RèÅÛ|›QF•§0ùÌü‰Çî<µÊ\FaY²òTgߋĵšå¡pÎÞ¶­Ã-û­ž›»v×DiFrwÂúÀٌҴ=—zo¹Ü%p”!ëÿÿT¤é‹ìÈ;|·²~¤1ÈŽù˜6Ý:[kÍ3T Ôr@I„‚Ú­¯#¸€R€Ù ŠtXB!ä{ȸ‰}âÄš>}&¼iÈ#‡†`td(ïÐhmÚ¯sçá;vL*_K³Ç¼ó/Μð™;ÏeüÔ)}šÉïÞL*J¼xóÐ`þ*×™Cì¦Ï ^ÔQöýcAâ¥[)€‰ƒÓ”æ-ÛZ qYê1£n&ïe>ÐhÊäqƒ{ZñÚ—(?#@³áÎæN·4úΚŠRïE f/[½ÄñYæŸ>XôðȾ(@ÇÆÿØsfLr;´ÝɈ;éu;ÿ3eY‹ú¬oILÓpüûˆÓožx¬ÜkD{½ F­†3K :þ¼DÓ¦enHÀÅØB‰8ïå©ma©r L°8Œ²Ì·©ÙÅÕž9ã[Ùè?óÙ¡;x€)K¯ËÐæ™'½ƒ£òÄRaÆ}ßYœ÷¿.­3´›>£uâ®y®‡n½z—™™~xÍ‚€ÔVSÌ•e–VfžqJ²Ë 0Øò,9“a˦èÝÞäÍË‘ ?ÂgÑÂíaÉ¥×ÖÓRdË©«+0 )Nzp‹Wð3猯þºT¢²ã«ÇKÆ0ñ®vJ!¯L[´’ í¤hûò€` s+eWd›01ÝQ0{T™f°iÓ?n4icå¤_(LùG“ð¦"L軃MoYB!ä»ÈžL0(ÈÃÙywjj\ù×ëêšêêWÆZ$¹—Î=ÚsȘl@œ{íè ·½wó¥Ò'~ã„k–ÙŽu]ÊDIâ¿÷"Ùo¶?Þ=~ªÀu±Í¨•Ëí€Ò´W!îÙÙ{/ ¬»ÍqÐjïV"*Ê&áßÜâk½Ë¹ùøU“ÝÊ.”¾§¦Òü;ž t6Lk5nnÝ—ÏÄDeâjïk”Åí=»ÌÝeê !ó-àíÿeyQ|¦ 2kQö-‰éIÂaòåÓ¯µû.nXe|„mØkD3¿‡ï8m¸nsöæí.w1ÔõŸ½Õ¹…"¤Æ½z^ò˜2ã]àAÛª‡—A¯¡ |·‰†ô®˜ú‚¥?Ø}[¡ÇÎ%C¼òÄlͦVó<œÍ«÷g¶Ñ{UöûÞ>kOŽ “öƒV8ö3â2{‹Ì¦®à°<Äj#_Üt¨©~]aX5?ât„þvèvƒ B!„P¸EÈOÃà´[¾wE[%@*.ÍŠ Úºn›A#ßQF?tȰ´ºº¸6ª«öÝñŒˆq•Ö˜–“]¶,h®ÇÊ‹»}hÛZÇø’ƒ[m þýª6œí§%@Tñ[º=´CIYÖjÈð¯øOÚŸP›^3øQ›96¤%(>¼uH2GÑßU˜²±¥½Fþ}¼íñS˜Ó!A!„ïA"ûÐ×ÔÕÓÓÓÓ«c`Ò¬ïäI-ÿè]¤¥IW¶ÏÝßÒÒ²›åÀ +¿*’BÈ¿ê1Ó¶¯e7Ë^ÖSVŸŒ.’2гÿöZë÷(?'|ÙÀÁž—oª4Òcø€…7r$Dž^3Õ¦‡¥¥¥µýªã/ò%ÕÊ$Í¿¿k×=}'O÷)½Z˜èéq»Œ]µÕ©(**[\‘âÅÑc­,»YZwý3®D ÙyJrŸu:¤§¥¥¥µ½Û™èª¦^vµí;Õÿq¾ä3UK>8¡‡­gtÙ7µ¨. Qï`å?”Õª|ú ›!‘b^Cv&X– ·¡´ÆVµ`°¦ûPï8¬€²7þHata³cà>DÓU€i+!’ÒA!„ ·ù's^]>ãÖ†ò·gVm}À]tì¯Û·®_hö4pçÍ÷y<=xfKO]¿qvm»˜ÀaébÈZXI½ÍˆÎÒ»!‘EPrKÐa¤…2ÃV/ Lëátýæeï‘âãKV‡eV@JßüQTߺ—Q•gåŒÛ콤·«¼¨ïîdu[s*ìÊÁÙ}ä¯Ðdh¥yÁÏò›±ƒÈõÚÒT ¢w©¹â÷g§÷;û±F©¹"èÉU®jªÒ¸ôB ”?†’Òì÷U]50•´*>b±Y ©X”+#Ïäw)¹R #õò£Ÿ!¯×¨ Q Î|anT|ïÒ“¼Ž=4™øLÕ¾oP\¡¢$ûC¹‘09…„ HÜI•a÷"o” øÓÀ(_FNôT¦ÐBãd¨(CZˆâƒˆÄ‘P~ …(ÌÇÓ`Ä”ÁdÑ!A!„ŸnÑü/„bãSI×ÈØ¸ÆÔ âô+ë×]Öþ#ಕ±SÌ?=müYHJ3’‹¸ÖÎf”¦½¸è¹Ô{Ëå.þ6ÌO«_Ô4Pkvð­pæcU+/® VÕSå[ï?`_@*Ìãgˆ4 ä>®#_¿—…RèÅÛ|›Sv“ÏÌŸx¼áÎS«dÍæÀ’•§:û^$®¥äUr%¯ÏŠ0=Bà4œ½m[‡[.ö[=ÿ6wí® YU eÈú'NEš¾È¾¼óÀ‡p+ éG@ƒì˜iÓý ³µæ`CJý!”$A(¨-Üú:‚ (˜  HG!„B¾ëš’š€¯l…ÅÅb¶ª¦šSR”puÏÑ7"‘@”á³háö°äRÈkëi)²åÔÕù²VÍ‹cle£ÿÌgW„îà¦,½.C›gžôŽÊK…÷}gMpÞÿº´ZDÑnúŒÖ‰»æ¹ºõê]ffJtøá5 R[Mu0—9§Ÿì<ã”,lZæ†\Œ-”ˆó^žÚ–*§ÀÀ`˳äL†-›¢w{“7/GÙµ'=¸Å‹+|SãU]*ÑÙñÕã%c˜xW;% ‚W¦-ZIÐNŠvR´}y@°…¹•‰²+²M‹˜î(˜= ðaتiÓ?n4icå¤_(LùG“ð¦"L軃M·ž!„ò]èÝ-B¾áx14òƒõK‡ôe«ê4ì>b²õ3¿8¾ªýŠå¯Ý}¬ÜPÐog»rY/-6ÇêÓ…L¤TÍÌ ×оÛDCzWL}ÁÒì¾­Ðcç’!^yb¶fS«yÎæÕŸwc Ù¸We¿Ïáí³öäÁP1i?he€c?# ”oÉÊSU©åºÍÙ›·» ÜQÄPoÔöVçŠH­\GÎlô²ñaŽ›½ú^%³Â÷7½\ϵ÷?9·‰ü·4_µ×¥†#´4ª-‘›ã#Hº^l!ËR@kîÇh‡Ñu:!ùÒ‚¡:¨2ÛãsP°Éžjovœ¨²v0˜òù¨ÖüH®+êl‚nKêù„BùNŸ½g+•JéaÂ_ÅÏÝY¿á£‰111...®+WR_ús»[þ†µö ¿]þ==—ÐÐPê „Bù€&$„B!„ ·ªRéà—À‹çóþvk,O»‘B!„òïóïn± ÂάlZuQIÊÿW{yüïÊ1õ'^9»¦9òN;wuyZ @*ÌåéŠ2óE4+!ÿ«#„B!_nU!ŠÀa³ hÔ~ðêÓ™—^~/ùß¼èÉÒ.VKiB!„Bþ­¾åaÂ'î]ëYr û Ýñ”Zt7“yƒ¾‹6„<»Ïçŧ\ ;ìÔ¯.[nxÉ‹çóîï·í|ðk>/>îØ.Ç&* `(™ ]³=ìÕx>/>þÜYŸñuYU×úkåÀi^ûñÜlÌù³kš€ú(ß—|^ü­ õÔ*’…Í6aWY‹ç3}Íñ“/ø¼øäs¶Õ¬ü~ –N[ç=Gžðyñï.^8³ïŸÏ¿èn¡@=€B!„ò/·Ê18Ê*åÓVg'f‰ÀÒ±Þµ?`aϦ¥Oƒ^¾•¤Ò°ï$ÿ“S[T dt j…wQ|)”ë Z»uYG%p FïÛ»cz§†š%ɯ³ë´>ëèùYmU>N¯g6kÕòQM´À„Trÿa²ûúîí‡×ï¼-ùÌ šþðÉ;¨3pêt_´fAà˜N=ìµèÿØ»ó¸˜Ö?àŸY›¦š6¥¢Ù#KÖûÙC´ t‰+\k¶k )\»”›d‹r-Y†²/!¥ˆL‹ö½Ù~´×pmwûù¾_½^êœç|Ï3Ïœ3Îwžó2S¾ºy3ͱ¡«q'nyºõ><ÐoOYŸØêŸYÅüäô$©/csÑ\ l›¦zÓfÚðâðñWrpä4¹!„B!ä_•n=XÓuT̘³‡~nÍQ®ö¾w>3Ù¥·#¾Þ7oÿ£‚Š«$)º¨ææ ¶2 •HËò*iÏ÷Í€äÒií¥ÒšÅB!„òwù²±[ò¢¸=³~{ €aâ¾Ë¡5_–õìi ÔkÍO¸rñÌÉ‹gÎÞKR6Ðá2jlËkÜkt+x{ëUÒó§ÉÐx`M& Ü¤{w]’¸È÷%Šw,—J@Y[P+ðŸ’å¼~› M†÷Õç2˜ªº4v‹B!„òïJ·Euß’M·ÏkÍzêïùG>Àé´êУG¡—£âÏmm¦Q¹ÉŸ?nž¼1« (º³ópBγãÞ׊~¿=AW®…;6|Ë¥,ÅS`Hsâcsp{­9ç³}º¬/¨sáãs©”­·½xwãÞñzôΓ?!Íz¼q΄=¬,¬z7{ݱ¨T1ˆLè3ý|Æ_þIRÀÄ>.gÒdÿù¶LC ÷jý¤'e«ÞÝ.++»G <š‚ª/»pî1pO€ìôO†ÕDœÄ¥· g#–{|ää~vej­zÜ©BPŸ8!„Bþ¾t (ŠÞ±jûKh0cÕüŽy'¦Lš²!ü~b>KǨy CÍü·w‚‚¯Š$•—JqIÅÚuù@vtøÊáK‚ÞI!:;»ï‰ŠÏæÖkf¤]”òàÄŽñÃvÝÏûØ…MáÝuëßM—‚cÔάMCUÖ—ôqÉsþ:ncÐã,9€ü×W‚îd€´˜î-$ɵҮ¬q˜µ'¡Ñ¸Õ¿þ¶fbódÿyžz'þû*ÁÒêîæ1½£€ùÓ¬‚ñОTþã¿rUŠ ò?²Y.RýÊ~I>¡ ùLD'hÛA¹Ù«ñ&ä[+#­qt€8o-ü˜ÎB!„|¥f-r¹œÁ`|SleÓµwüì´·ÆnÐŽDÉ?šUj˜4Pzû:¥H†R“);.¬jÅDô/í¦ýž,û?x¿Ã›U=ÚvÄÄĸ¹¹y,]ZÖywVÛÍ5fßîI”Êˈßþþ“ÃnÕÅ뛞wr<×b`Ýûî§2t͆Í\2½‡>D½Wí<ý¡„¥ÞØzÒ/óG7WaB–}bË–C—c³åjÆÖöóæ5H•+X(K pr¸<æ˜ï€Bç7;ôTŠŠ|“™]¢ÝÕiù‘&ÊÒÔë~«·?H“k™è«tãzãÍG\xÇœ&ž4÷ œÝ\éooÆÎL˜†]äé Ùk¨ò«¯Ê@Œ6ò¼eh± ¸'–Àd˜î-û6HrOF€aÖ5”˜ å(+U «…f‰PU€â­x:¼õhù ÙˆÕF&)¨}^ej¬’£`ÈGšl IDAT9ž¯Ó¦§ÀþÈV13a]]77·°°0ú…B!•yÈñ2•ÍV¼•píÁƒ`á³Ká«Z1ÜУWRetÚŠã/ÞÎmbÛ¿aÕŒ…S¿ÿ„v²ç£óH’nåöÛröJøGµ°ÕKƒßeFyy ¿|ýJðª1{¶†§H!K _á¾'ÙzMÈå«ç·’[°"<-[QÉ\E «’åFßQsö äKNÇý*³`ÜúØÇ ·Ò]UYBOÿÁ²îÜÖõèE&“Yê­à»üA»Æ¶Ñd¸Œ p ¾•éR»$ ”,¬Ö!Ì6¶ÒRÀ4L-Œ¾oÓÓÏÄÕ·_9 ¡2†½\¦ž¸ô†²a ÃkÃæøWùÃîåß÷Ô…áB¼š„÷Žl¨¶‰<©·Sh·{.ÞŽAÆX‚SåÍÉ9\å ¤:å}[eª¾ç°äB*¥Ó‚B!ÿ¶t«ðÉâÖÝÿ+^fÑ£5Ó-ÖÐÛM>ï¬P«Ã—¼ûP ƒJ•+pyIFr>K½Ž +‘ÁÓ5”ÎÖÂÑj ‰ÜT™éÏ[çíß²gÁ¾µbI_Çùî#e½Ï’~vé\¤Þû’ÖµK6W5S°WµNLeuåÒÊ0ÙLÈeâÌ·YÐ2(«SEO_õßÞU­ðþ½rÜQ0:Ž„³x½²*ÝîùÛQ0J šF€ô<2 [ñœòò› åy(8ˆØ™x= *÷Áû†ÊT¾ç¹P“E§!„B¾“š€xMúwQ}tþuq•…Ñå€ûh3 ¥ /ÉÉ*)í•æˆršÜôÄ|û_÷œ¸~|§“îíÏ'CMWc8ùÈ¥k7®]»q-âÊ™€ÀëeÕ.).J­½ðÓý)lº<ã}Ni)YaZJþ»³– M¨« èt•ûúÒ‘rä1È8„ô ”¾Ü_÷þ1TÁï.€7—|ŸJ•œEÀìž2„BùÞé–œüGÐqü}1Ô:ºþl±w–û®°G‰i™éIÏ®ì_üÓ–˜ÖÓÝ{Öa(Ž8õ²@YÖÝc'RMm»(=Ü9Ï}Kxb”´uµ”Ù\uu[·Û°ViÛO=Ï–ÊÅ©‘>?Ù»î™v¯vIFŽ¢…ŸÎNêõÒøÝÑÝ eEI×öîyR" +x!ŒËý7¦^Õ‡K½vFF|õf7„ÑöjI%!ÈÎÓmeè G9Ú?†P²yYå…2ÊÂ&ŒCŒ% ¦u•i ‘ìR¹Ó7ëP6é'*Sºj^Y#z.À„ޚΓA!„òi½™ð;ÎtGÈ K·Ï²ƒuNí;tx±ãšl Sµž©åÈ ‡ÇtÑç@ 0”%xMŸURÂij»vÕ@}ué’Å/×ìtî·¦<½¶KõÔb²0dÍæ<Ïm †zgKÙš-úÍñt5kÈ©]’Íé§`s$}ªŠÃQë<>¬òrì»V®Õf`7CÖ]ÒW½=þ©™ ÿTµáRc*´4ª-áN„á¼¹\šl!Ýr@kve¶Ãhº]xɧ 6¨<ì±Ê<í­6²+7 Ê:@ßéã•Q«¹ŠÛu×CÇ”ÎB!„|%Ê©HM4ü‚$=úQš®i3.¥ž6þD¿ƒûFüƒƒŒ>:üÿ5šžB!Ÿ@c·ùO’¦^üuÖ’À9RYqòmÿxîíêЄ„B!ÿ*Ÿš™ÐÕÕ·¸¸€Édóù//Gj,Bþ=”š9¯tܸqî¿|puÛ Y¸Á¡1—š…B!ä?n¹ºúÅÆF]¸à—››ÎfsõôÛÙ-XIíEÈ¿SÐf¯G&ü‹jTq[!„Bùhºåêêq4:þzæÀˆÂãbÏFÙÚº‡„xR“B!„BÈçP0v+6öNtÂ5,ÀðGžYfdä)WW_àN‹Æ‹jüì«÷}F‚©öÜôB$|àmƧ÷‡B!„òŸU³wkúôaa»Ñ p*F‚´~AÚ¨7ïÞ=iÎÝû} 8ÊÍ;ôÑ„èê¾ïJJ’¢óéP„B!„ò‘t«¸8?''æUr­R¨ˆsr2@–}oÿž{`hÚ¶qè£)y~fÛš[yÛx|ØË™&¢KG5ÑG¾³Ç”SM&­^:²GU†,+:xï‚…'cuÇÎ4Ézp1Ö ‡y]vJÔfÇž ä žÉ8÷-ƒ[©''ʸ@!¥zC/pgÞ@ ÈO¹wh…ÓêGy”Óò¯ôƒOO!„R[ÍÛÿ˜L6›ÍÁ‡ZÓÀ3ØlΟ‡ÔïmkÆMy“k2ùȇöñûœ»¸úe¶5ÏÛ¥aY§¢–w=h×™FÝNs—tÕd€×ÚyçàVü¸  >‡ne–ÖÅ`ô os­»¾3'þòó†«ïy|½c„B!„ÿˆš½[ªªšMSÂð0+_*‚/S×Óküç!Ÿ{ é˜(á´Xtl€~nûû•­iØÙ@)wÎkk`²‰Š­£‘VU§ÎÀžFÀ[ŸUK·Åq{vš3Ö@I~~  ÖsÜþ“Ç÷ïùÜˤ®-B!„BÈ4Ýòòr²³[wînî¬tÌ,à(àsÃÏŽÌ`°˜wÍ™z$Y¥å+5¨(!/)`‹ÅP˜U$«Œ ûpÚc4Çn¢MÇví-Ft¶1XÇÆÊ'º˜Þ5òo$+HN’è Xÿò˜„B!äo£`.Á€€•½{;ê¿jÂËE0:3T¶j˜5è۵눭['}vä’סI€‘ót§mLÛwê¶ÐsZ#%Å…Åï„Os€¦NŽã‡ô9¬¸M]—¹µÍ»utßšAoðø\&½e䯗/ü¹‡U¿Eá©ÒŠEy×ÜzÛz½øh²_òÒwêô½q%$Iû¸œI“A^ðlÿ¬!VV=]OÞ?Z¶ðó)Œù_”†îÕúIO2ÊV½»]žaÞÀ#MAÕ—Z¸ ÷¸'@vú'Ãj"ÎâÒow²ËÆ=>rr?»2µV=î‚T!¨OB!_MñcŽCB<]]w½Wú˜c::†_’k@Áý]&—xÌ·½t±P”üìÌš ÉG çÞÜî¾§þ橃Vlou¯´VÒœÄ ]ç)žÎ<@’öøâÚ¥»Ò[Fþ&77®9×Ús¨ûsJËŠ2sËŽn–Vw7¦õLˆ“"Î?Õž±{ÿ€zêJYõ=ZÔ|É cþ— ƃSÑš<ðø@ù'BŠ´îƒ¯¢ðÓ©~e¿$Ÿ€À¥´G¼J؉à²!/DÁid¯Æ›6h2ê«*S\¹Š-‡ä%r"ñÖÒ‡ÐoC'!„B¾_ºÀÇgÆŸm+Ï ™Þ(¤òoI‚}ÿ*JCwM ÝU}«*e^ꫨ|yú%)í¹ªsE·,-|Å/GòlÝ‹ô8lå¡ ÐÔ×bf¥ä°´Ud¯Ò ¤ 6ÿÈÞ»ñÀ6¶ÒRÀ4L-Œ¾o³ÑA••ñ ôô,ëÎm]^d2 lØÁÂðjÑœªß0p/ÿ¾§. âÕ$¼w„`CµMä1H½ ˜B»-Øsñv 26ÀÀœ*ßå®üY²¼o«LÕ„K€\H¥t>B!ä{§[®®¾ÅÅL&›Ïxy9Rc‘ SÓâçeæ¯ôíìÕ¾<áÉNÎeji”:,A=v^jîç^ŽKs“³äõÔK7g(é6m%¹ÏOzî>÷ Ú LñóärÅó3(Þ{žÔLeuåÒ|ÉfB.“+›ý¼uÞþý!{ì[+˜ôuœï>¢¹ê?ØŸ­ƒf¯kÝ¿WŽ; FÇ‘p¯w@V¥Û=;ŠF DSÀ(žGft+žI¡…f‰PU<;¯GAå>xßP™Êd/R*`ÒÜ„BùŽé–««_llÔ… ~¥Seèé5¶³[°’Ú‹üh —ºùÌ#g­Ü.V—•e8uÕdOÞfI Ê Íy—*Qk*`ñYc{X*:d%eK ËPø2èÐ=ã‘=’~]}^û—Ýçûò˜RÑïS&CÔŽùѽ×Î SóMìÝ3“Q”üäœ×ÂíÏwÛ3Ú€õ¯ý(ÒôAÆdŸ*Ò­t¤y 2b*˦ø¢Î¦šQ Uðûƒ ¾¸äÏÒ­ÏSrE³ xÊt.B!ä«®&åZ¾G/Þø-¾åƒ´™‰¢Qq ÂΞõ¶µu§ö"?†JÛ©+&¨Þ8“PÚÅ1°ÜèÝ‘gcs¥²üW¡;¾n0°§!—Åa§½}ŸQðé~.¦VGÓ¬3»ÏſɤÙOoÞþž«$-(²Õ4\¦,?áâÞ£¯$’)P;¦â½+‰%Ͻ·sžû–ðÄ"(iëj)³¹êê<do¢"„q¹ÿÀŒòÕ‡K½vFF|õ–6„ÑöjI%!ÈÎÓmeè G9Ú?†P²yYå…2ÊÂ&ŒCŒ% ¦5xÝV…Hv©Üé›uÈÿ¬2¥«&á•5¢çLè­¡y2!„òýÒ­ØØ;Ñ ×° <oÀyf™‘‘§\]}«–ä™ÍŠ„ñ"áewã/y¯ÔÊõºH/œlÂÛx|˜Hy¬ÑGîmTXàcª.‡ÀÒ_$Œ4ÏŒ¾œ&_p)·pZ9Ťü‚›c4fÓú‘8á6¨gA³ƒd¶k77æp {ö0xîé4m\É'£±ê\½ÁŽì6°GO›E×ÌÜäÚZÍ`ÐÏŽï,Ú§ïP§š:ÖÍK*PSñÞÚºý–,ǹŸ•u2û/]ÔS‹ 釫ÞÏ%‰ÿ‰–ÌñGú¡òŸýÈM©Y€;†½*’-¤{BhήÌv-P· …äS•ÃÊ9†ôCÈ@~2x6h²¯ÚȮ܀Ê~8™ìÏ+“ãôÃȺNwF@Ï”NB!„|í•d¿§Oß¶;¡Õ#œ¸UVœ{gP×Ó§½Ê)·ßø{ÐD-x{x°Å®ç%_°c–Vëa#MBN>Ëk0þœp¦És¯ý%ŠÓ­Ú>!IP¹Ïdž ý‹‰‰qssóXº”Þýo1ÛÒê|Õ^ׯ•þRGW×ÍÍ-,,ŒŽB!„T¨Ù»U\œŸ““óê¹ HTÄ99•KT›Û ÑBÑ‹Kñ€aÿ‘-”ÀÔpôj¼èòîþZ6ÛbDÂ;:ªit^%ô…H/ÆÇ\Ò½. õ6ÓVÍòøÉ´Æƒ[Š —Ñ6ŸsôDŒHøòÎÆÉ¦ÊÌE¨XÎ5œvfCº#‚ã…Ïöyèr¼èòžê 04n~&>Øa®F· B!„Bþ†t‹Éd³Ù|¨U0 1ƒÍ®¼kI­ƒm?M\Ú·e÷s@×fœ‰²,ûÖ¾›ùàu·k£ÎT7·3ã ýìÁèñ€jŸImÔEÏŽž^ç‰f<¼8rò­La“˜Ë7Ôÿôãp>YX·íÀ‘CgmšÝ‚kaÏ >ãõÉŠ³ ÝvClºum¥Î‚8>(è  cVy×w‡}Ò!@!„BùÛÒ-!!žÃ‡»0«w;Ç~]§>¿gω»vÍ(ßHËÚ¡#…×}ƒÏ^¸~!âÔ¾ G€J¯á5Å/Cã@y",I ˆ_Øy,ºPÏn‰ÏÊÞu%’OÖç“…Í'¬^8g°öûˆ}.s#>|NïV~´Ÿ÷Ý©±³×æm.”黫#eÒ‚ngѰ-B!„BÈ_åÇ›•×|éÍ}Îo½zÚ{½ [  ‰àÉס‰ài"xB!„ÔÀüÑÒKîöc €'â(×"„B!„ü…Ø?Øë•g]ZÚFŸÞwB!„BÈ?šn¹ºú0™l>_àååHEÈ·“$'It ,j B!„4Ýruõ‹ºpÁ/77Íæêé5¶³[°’Ú‹ü î,æ~½Æ#Ù”,·œZÓ‰ÿm‘K^úN]â~le{åê{¼·lø¼³^ÝUÊÓ²ôs3Fù5Ýqܽï+»ôs3Ƴڿ·û%Ç)Q“ƒvöÕø‡Æj¦!Fyµ7A›‹mäuo¡~ÝÀK`2L÷VÞë\¸ Ï~ÔÐ$êÚ«õYhàÈF¬6r¸0I@íó*깊Óz[ ÓíäJ!„¿,Ýruõˆ8 ½sàDáq±g£lmÝCB<©ÉÈÁ2[¸Ï££J•%PÞ)к¾Š¢Ír‘êWöKò \j&?‚‰à²!/DÁid¯Æ›6h2ê«*S\¹Š-‡ä%r"ñÖÒ‡ÐoC§!„B¾†‚©2bcïD'\Ã2 ð¼ä™eFFžruõ­Z’g6G(Æ‹„—Ý9¥Ù›ñø0‘0þòX£Ï&°ô ãÍ3«}!«Ñ3P$Œ·­—˜ºƒ‚EÂø„õ–ªPjåz]$ŒN6á~aLB>ûÔPÒÔ­FGÀAÎõE‡¬}PöÈ·¢hÏܯdÊ Ëþ}ådk++«ÁËŽ=É‘AœxdâàÛ6Ï?zøÀ~ƒí—Åæ%ŸY¸è|VÎ¥ùv³O'Á3ßäEo.l™9¦¿•••…Õ@û¥ÇžåËÄ/”â”k>³Çô³´ê3Òý·ûÙ²Ùž‚z¾>`?dæÆ%vVV½¦½¹ÏÞÚÖëEñ_Ñ¢:Ð߃†Ëü "¨²ö%6@¦h;ÉÈHË \ o3ŠjTN ú»Ð`Cã_ øäßRèï±?Lî Å2@Žä¥Ð3#!„òUjfEÓ§ï Û^€;P‘Ì´~AÚ¨7ïÞ=¯RV¹åø>z€†c4Ù¾ëyÉßTi‰èÚVLAÆ£4 ½ƒäoÄ´ÙU¾òLt~;s¾8QÒiQG ¤…­pßS0~sˆ_knâ¹5s¬Ð<´®%d¹ÑwÔvø¶Q͹ùë$­7zìX¿îñ˜Å%+jÞLÒ{‹ZÔH%š€ømвMQÖùoí¤%Mº´fÚÚmWûl7­?¢‡WëK‹W….øíb”ðõ3×JÔ«&[iá êÙ’œÇëm9R_ÎU“4_ÓJ³>ç¯h½4ˆ¦Vv(1MQ^ù÷=ÚP.Dáj¤ŒBÝšg;27BèyBiÞÜFêm4°®R ¢àr ÏGþI > &|ƽŸ¨LåÛ e{ðV£( Å…`óé „BÈ7§[ÅÅù99é0¯’k•²€DEœ““Q¹Dµ¹Ý-½¸ô¾yïFýG¶Ø÷ë£_;s ú;¯^:²GU†,+:xï‚…'cuÇÎ4]:ò¨ñˆõ$RŽ€îˆàøÅa?›;Fæ}ÎE¯z›i«fš<÷º|êY§ÞÅ ÜÇ™7PòSîùÿv»Çâ.Uc:=Ô±uýu‘MWCŠR¢ü}­O(æ·ž²ÐsAoÕ¤xi½FªÙ¿»Zomù{EÝúÈ}†ï2ڴض¹@ÎÛ뇼l¸™n8þœp¦IÖÃK¯ {tЖ¾ŽðÚúÄØqÂÈvšYQ¦MÜ{7GFGÕÿ¹8ò—jÉöÀí‹Úª¶¶µæ,>õ$·cæÓÓBt[ÙVMþáJð]þ ]cÛh²F\F8ßúÐ`Û i)`¦F ß·ÙŸìÑb¶˜ã³¤}ùE½<3bÅÏ'JÏRý¡ ÐÔ×bf¥ä°´Ud¯Ò d¨?ý]vh|½q+{ò˜hÐwÚ¤À맪d[n)ªçB€el3ÌLO‹ , ÿ²FÍñ¯ö‘bà^žáÔ…áB¼š„÷Žl¨þ>Ä õ6` í¶`ÏÅÛ1ÈØKpªäF9‡«4bÈò¾­2Õòk°äB*¥S‚B!ß#Ýb2Ùl6jLCÌ`³+¿õVë`ÛOçöm¹îÔ{} ›q&žžV}”¯õ”#&j†{;Ï~ª»{fì€M“Lò"×ÍùýybLaí ²:í~â Õ`ô o—V¹W|gîKà·ïÕ(/|ùþ~þN1åí¦öm~Ýsö5ÆàŸæ:/?$y;ìì ½«zë–Ä®MlÅí¹±’wﳤ‚]úWn^ï}¶DLeuåÒkx&› ¹ìÓfðë6hذbª µh^Y' Cžûü¤çîs’¡ÝÀ¤?¯üIÔ5ãK²E¹ÐÐ×(=­Y‚ú:JUâK²ד–Jþ_?S¢š½†êGúˆ¸£`t gñzdUîrÎߎ"€QÑ0 À¤ç‘™ÝÆå%´Ð,ª*ç¡à bgâõ(¨Üï*S™ìåB @LšH’B!ß%ÝRUÕ40hšž€‡€YùR)p|™ºž^ù5C­ã$ ÄÂK¢ü8aZ4±fºúéýÊHc›Þ ôsÛ߯lQÃÎJ¥éÖs¯!ý%€ÀÒ|L ’î\>,TXÃa{.$ƒ¡ÒdŒSÍZ«Åùù%€ZÏqsøOß¿´Sø$GšŽŠ˜Ü–K¬ €W»wúOdÜ×±íçÒh@okY]àÍöU˽¸=;ÍmP±¢nlñÝ'ÙÙ75ÔÖÕ ¦§ÆBiºwn»W`ƃ6öݺK"yíx +ï3b©±º¾ ”ný`òuêÖžºÛhÐ@­™§"®3ï«õó6áR5]5ŽáàýrÈÅÙ¢T‰füŠ“ª/žäNšrá×ÕçµÙ}¾Ÿ!)ý>eB°âÑIlzê¸÷6SU6 ËOͬÚáÌR\ÏÜ@ÿüÔ{lhú ã ²OéV:RŽ€<1•eS|Qu/ƒ IDATgSÍÎ(†*øýÁ ß@\Þ÷¨TÉYÌ.àÑ@PB!„|—tËËËÉÎnyܹ»¹³Ò1°R€£€LLÌutÊî3bh¶·ï¥  Û–ÃWKiYë°íþÛ*W•,&€Ä]s¦I.c%/JËWjð…—šñ¿oö¹œ¦î Î Ò-YÚiÑ»‰6Ûµ·ÑÙbÄ`›!Oj‡‘—å@Õº$%Ò§F¬zvë6¶)Žô_·þ©Ò¥Ö5¿ß–K%wI%òÃ%+ù~dÅ™©©©Ug&d«hj©°Á1ìg£wl癞ýO 8XºÝ†µòõÝ~ª“ÇðæüôHŸ¹‹/›m=4FñqÅa¿{û>£yC­ÏíP’‹ ¤ì†š.S–ŸpqïÑWN7©¢|‹U×Ò¶åž½>¡Ý– 1ÈŽØw0FÌïQ¹Vq=«Nâ'+xs÷N’Vû.MԘ߽E«— ‚¥ÐÒ¨ÒÀ†0ÚŽhçÊ 3JB¦-LƒÁf€ü ¢Û x/ò– ln‹ ˆ¦‚üÅWQ0­Áã¹€B$» £üÖhFsÔ›üñʨUYÅ‚ô ²¯Lè­)Û;!„BÈ·¦[VÚÚºGFžJ›(á—0$ ¾TÝÄļk×[·N0µ,‡[ðPxÓoáÞ„b0TͧnvmÔgRõµaJ^‡F$ý4ÖÈyºSzðí®±E«ìŽj^Ófºí†ØtSŽŽŽŠÎþÂ!œ¦®ËÜô„ÝzºÛrïQ x|.ªÅ|ráFòÌÑM¦ý4=ýsÀØÆÀû°ËÂݼég.ZÁ»/ïÔ† ÔêZc ë°€œ„„Ä\~/} ¦Ãå"—>\ï0¢Ú"½ ¿™aÂ[¿ç°Æ>›%C{Õ+½¹–¥7dÍæ<Ïm †zgKÙš-úÍñtmÃÿ  í×°gƒPO§iïöý©÷óNRƒA?;Fýºph¶Z&–#?òK*è¯(ßÒ³Y½6cý–ý7‹5Û êÕTùnµµŠê™ú Ê·®z{œ4÷ œÝ\é/hÔjÃ¥ÆÔjéîDÁ›Ë¥!H÷„К]™í0Z n$ÞFò)¨ *{¬2ÏF{«ìÊ ¨²ƒÐwúxeÔj®âvGÝõÐ1¥óB!ß/Ýâéêºëýû¸ÒÇëè4ÐÑ1,ϵ¦–µCG ¯øŸý#W°žhNt]ж×ðÎ>*‚Üß5ar‰Ç|›ÑKÛEÉÏάɨ9•`~´Ÿ÷]³y½6Ûÿ©çìò)¶?—,'1C×yЧ3¤=¾¸véîG¤ÕbÎñu˜ÃY»pðÜ-–(I½{hõ¢uÏRÄ^sw×Y;¥íøÙõŸ>’×è.¿<°óXï¥ãì–ø´¿u]Bs þHøæë.^ûøjfÝ¡»® ­¶D½ÍøUûÆW+edôûò?T­¼ÿ°tœsðÊœÚ{ì°:ìJõhö»2¸ôw™ó– çÊucm ãkutÚè_žRÀàð‡ciN¡ ž +Ö#‡#ß½=uÐìã£Öª­b£Î%Ô)ÿKÿôk}béÜ‚ŽÂmkP‡‰ä3öøÙ«!„B¾Âz‹ ×pøœAu?$gÊ4;LtÕRzÕeÄ”3™4ì €\þÃ]rÆÄĸ¹¹y,]Jïþ·˜miõ¾j¯ëe™y]]77·°°0:!„Rýƒ¾nÏÈrØO5Ø€,ëõå­Þ‹ÏS®E!„B¡tëÛÇzÙ ò¢÷Ÿï§¢Ÿ‡B!„üyºåêê[\\Àd²ù|——#5!„B!„|kºåêêuá‚_éTzzíì–¬¤ö"„B!„¯O·\]}#"ŽFÇ_CoÀøQx\ìÙ([[÷Oj2B!„BùÊt+6öNtÂ5,Ü€Ò§¼ÞGÞìÌÈÈS®®¾>>Ó+JòÌæ\>?Fx½yBÏ1À6N8Óä¹W~‰Ÿ3wºÀÒ?fC—Ôà]6?¬ñè+žÏטK£¦´œSú˜ã GKÍŠ®9˜.ŒjàúÇöõ~ëßã·Ø’/‰IùËüà3B!„ÔÆ¬ñ÷ôé;ãâî¢à^žkhü‚´Œ7ïÞ=¯RV¹åø>z€†c4áþ}•–ˆ®mõؾÊóV= ‹B!„ò_I·Š‹ósrÒaÔHŸ, QçädT.Qmn7D E/.ņýG¶PªœcÐÚo×Ã_‰„ñI¡g¶h¡Â` ãï¯^µßÿ©(âá³ ]莎>?ÐYõó*ÍPo3mÕ,ŸLL@©Þ•^W^ ãEÂø¸“¿¯â~¶zL†’ñðŸF]Ž ãN¬éoÌ«^wgCn^]Þ3@†æÀÍÏDÂ;ÌÕtxB!„B¾_ºÅd²Ùl>Ô*˜†˜Áfs*¨u°í§‰‚Kû¶ì~èÚŒ3Q®¾¯õ”#ÚÇïsìâê—ÙjÔ£»òïxÎ^³åšR'ç凵P®ZOí¸œžµsç:lnæ<¤í~œ+§ÃƒüódÉos¤ÏV„B!äûª9vKUUÓÀ iJxù8¾L]O¯qÙ†ZÇIˆ…—DùqÂ8´hb3ÌtõÓû•‘8Æ6½èç¶¿_Ù¢† ”BϽ†”îXšo‚IAÒËÂŒ³ú¶çBB1*MÆ8uѬµZœŸ_¨õ7‡ÿäñý;A;…Or¤é¨ˆÉm¹ÄÊxµ{§ßñDÆ}Û~.X5ôÏ©V ^Ëú ú:YŒêkÊ܃çAþÏ‹éàøq=^3jæùœjËêŒÜ}dØí)S¢&íì«ñU]Ÿù·ç]pK\ñ7_¿mŸ‰n3†˜¨0?¶IÉKß© RÜ­l¯ü;ª²•$)ÀÉáò˜c¾6:̬=Ó£‹¼Z‹Š ÍEŒ6ò€º·P¿ Ènà‰%0¦{+¿ *\†g¿jh’uí‡Õ€ú,4ð‡ d#V9\˜¤@ öy•aÕ\Åé ½-Ðéêê&„BÈ÷I·¼¼œìì–Ç»›;+óK 8 øÀÄÄ\Gǰ,ÛÒloßK@·-‡¯–.Ò²×aÛý·‘ @â®9S$—ޱ’¥å+5ø² JãßìS:UFgé–,í´ÇhŽÝD›ŽíÚ[Œèl1b°ŽÍ'µÃÈeeÿ*î²*Š 8œè4¿õìµ::li”_x¢˜Ž³Ùœ=k-Ô+®³Y*ZJi·¿5*ƒÓañoKÚóå2qAú«þÛ6N]´ë#Žâò²¢ÌÜ/¡Xe+–Vw7¦õÌC› ƃSñ‘Ô¿¸hÝ_EÑf¹Hõ+û%ù.5“ÁDpÙ¢à4²WãM4õU•)®\Å–Cò9‘xkéCè·¡3‚B!ß#ݰÒÖÖ=2òTÚØD ¿„!að¥ê&&æ]»ŽØºuR镨–åp  oú-Ü›P †ªùÔÍ®úLj£¾¶"LÉëЈ¤ŸÆ9OwJ¾Â5¶èc•½Óñ@ëÂâìB@·Ý›nÊÑÑQÑÙ_xÿ§©ë27½¡G÷…žî¶Ü{TŸ‹j1Ÿ\¸‘ìúë’¶Õãˆãƒ‚žÌŸkjVyWw‡} Û°~x ®º®®®zÕ+{qZEú^ô&ÌgÓ¾°ÇÉ…2¨4´š´x‘IúQ'—+­,”£cóå…EZV3–N³ª[3b*iêèꪀ^=ã冲)û<äÑQ²Üè[¶º›-W3¶¶Ÿ7”NÄÂEç³r1ß.k¶ß–¡º5 Œ50eYmÙx-6K¦fÜÓiÑüaWªlµ±ÃMïU¥½[òŒ»G7o¼‘ ÕÝÆ¸-˜h®–tÄyÆÍ=•¢"ßdf—hwuZ¾p¤ ûíA§‰'Íýg7WúŽ-ªý=PåW_X1ô%6 Å*ÛIþ@FXV`]CÞf9@¹jµ´ ¿ ª*P¼Oç¢øäø³þ(…•I«¾JŽ‚åx¾ÉK¡s lêá"„BÈ—SüwHˆçðáîCÌêÝα_שÇÏïÙsâ®]3Ê7Ò²vèÈAáußà³®…_ˆ8µ/è Òkxç*7YÜß5aòáË z£—.Þ¶}Þ4knÜ“Œš_ÔçGûyßM‘;{mÞæÒè˯íd9‰ºƒ¦xîß²ÏÛ–ÿøâÚ)»}¨Svß×aNÈ=I—¹[ÍéQr÷Ðj‡uÏjß·(}wõ`¤ @ZpÀí,¶E>Eü6hÙ¦(“yþ—®EüqÌÝøážmW?ÈyAì#Í©»öï?²ië¨uKO&ýIÏ·¾u¯úùO¢Þ—@–¾Â}O²õšËWÏo%=¶`Å%Öàõëjzo ðªÇ¨] >3>žã|a\5WI ®_:* ¡úGË—$†îšº«úÆþýõý+ÿ’ç?ÜæÖu›¢½d]«ß­rŸ©¡#ôCËþÈ«äÔºa§ÖÕØ´FÌØÀ£7V+‘P½¸uZ2·þûŸÐqA }²ÒÆreÙ,c§ý{ +Ný¡ ÐÔ×bf¥ä°´Ud¯Ò dC©åı¦jL@¥ÅðQõOÜH9¶ë{añµTPU “}¸|—?hר6šl Ñ—‘NÁ·>X6¬<ëÆJëóÖ\˜Úoðé^¤§0K’ˆ"ÎÄÖ·?2¬¹€˜ qs:=îø‰Ýx`Û i)`¦F ß·ÙRFÆA›æT=ç,`à^þ}O].Ä«IxïÁ†j›Ècz0…v[°çâídl€%8U¾)Ê9\å ¤:å}[eª&\°äBJýÝ„Bù¾éÖ†¡ÑÝ~Œð ðD Û"˜MÚ¹¢› ´Ÿ„ÁQ¯[å\aÈsŸŸôÜ}îA2´˜4âçÉK2xÚ:üÒ‹v&_[y)9|2Ý’æ¦æAE[•%ÉzŸ%ýìÒ·Ê÷õÞgI+Ò-…ß%eÉ5ê©—Ö¡¤Û´9)H·²“s™ÚFe¯‚%¨§ÃÎKÍ“©¬®\Vg6r™ü¯êÛÕA³×µîß«øºcŒŽ#á,^J·{þvŒˆ¦€Q =Ìè–ÏÚ-4K„ª äy(8ˆØ™x= *÷Áû†ÊT&{¹P“Eg!„B(Ýúzò¬KKÛèS;Ê”J©N}CÃêc·Ês¤” ¿®>¯ýËîóý yL©è÷)‚! +LKΓÏÄéñäVŸ>ÃÄï"®&itìZÃÊÒUãÞÀ¡!€\œ-J•hêsce5`©)( Î¾?’²%Ðå(|tèžñ˜±­À¨y;KPWMöäm–ªlÒœw©µ¦Ö¿è£HÓW}¨H·Ò‘rä1Ȉ©,›â‹:›jvF1TÁï.Pøâ’?K·>OÉYÌ.à)ÓA!„¯ñ©ùÊ\]}'OÞ2uª÷ìÙ¨¥©HÎÅR¶š¦€Ë”å'\Ü{ô•DR"•(yq`_„¨X–ÿêÜÎÀÔVúéÖÌgdÅ™i©©©©ÉIñO®Zµ`jG—I-•ÁÒí6¬UZàöSϳ¥rqj¤ÏOö®û_Åa§½}ŸQEâømL³Îì>›'“f?=¾ywø{.Y¹UÅ=pëÁÞÙq66W*˺ãàë{*ž%+x!ŒË•}ßf«>\êµ32â«çK†0Ú^í#©$Ùy`Ú¢­ äè GûÇPJö"/«¼PFYØ„qˆ±D!À´¯¢ÛªÉ.•;}³ùŸU¦tÕ$¼²Fô\€ ½54O!„B¾û#‰–_llÔ… ~¹¹él6WO¯±Ýò€€•Ô^„lƒA?;Fýºph¶Z&–#?òK*è°ê¶,t°<›× û¸u«‡è±jej÷ÖNQú;K½a‡¾Kü¦ö×c`é Y³9ÏsÛ‚¡ÞÙR¶f‹~s<]Ûð¹¹={„z:M{·çèO ¨ñMWoÈØ°ÅmàÖ|†zÓþ37¹¶V†Ü°b«ƒ¶åù–јMë‹6y» Ú’Ïw±vóxcÎû› ^ŸôÃUoï?3aáRc*´4ª-áN„á¼¹\šl!Ýr@kve¶Ãhº]xɧ 6¨<ì±Ê<í­6²+7 Ê:@ßéã•Q«¹ŠÛu×CÇ”ŽyB!„|%†¢\Ë7"Â?:þzæÀ ªIš}ú8‡„xR“ýߓ˸‰cbbÜÜÜ<–.ý– âÄ#ŽÎ7ìû Ôbþ˜GÎlK«ðU{]¿VúK]]77·°°0ú !„BH×…±±w¢®axÞ€?òÌ2##O¹ºúV-É3›# ãEÂËîÆ¥7%±Ç‡‰„ñ—Ç}æ 0¥¿Hÿhž™‚¡l]Kûm¡!ÏDÂx‘0úáÑ#ëú}ï©© !„B!äïJ·¦Oßw½w@¥|i{à¤e¼y÷îy•²Ê-Ç÷Ñ4;  ÷;׌ÛÜáðqסm‹®ìÚ½qÓÑsÏYm´×¥©=!„B!ÿ5Ó—ââüœœt˜5Ò' HTÄ99•KT›Û ÑBÑ‹Kï›÷nÔd‹}¿>*®¾ Ç ¿óê¥#{4QeȲ¢ƒ÷.Xx2VwÜ9áLÑ¥#PO"å莎Qö³¹cdù#sj-;›HŠ >t5!OŠ]«ê×e”€ÛÂ%ü²£Ñƒ5]†œËjVù{Fƒñç„3M²^zeØ£ƒ¶ôu„×Ö'ÆŽF¶ÓÌŠ:0mâÞ‡ÚvŸ.pi¾îärÛæ\9o¯ò^°áfºáøÊ ÷ĽQoÓH|Éi¸Ë…›nîëV|b¶Õ¬;¹ô`äÇÈþèö?r TÜVG!„BJÕìÝb2Ùl6jLCÌ`³+oæSë`ÛO—ömÙýеgRã~@^ë)G8´ßç<ØÅÕ/³Õ¨yÞ. Ë’8ýÞ¶fÜÔgw.< /rÝ”yS=c +·–ç<¹þ €áè=ÂðØØ  ¿©=U²òþôa£*ªYWŽpZÏ_Ö1+4èä+hvrXÐW“ù§dE‰W_=c‘«›oh¡åÌE³ÌÊg“.­ð‹Ç§v\ÎÏÚ¹s¶F7sR‚v?¦\‹B!„R[ÍÞ-UUMƒ¦)á x˜•/•Á—©ëé•?[”¡Öq’…bá%Q~œ0-šØ 3]ýô~e$ޱMï†ú¹íïW¶¨ag¥Às¯!ý%€ÀÒ|L ’î\>,¬VqìQû™®n¶­ZÖS5h?Ô¡}/ã’.‹¯|úÅÛî˜ñ }·î’Èc^;èÊûŒXj¬®¯Âú³Œ‚7‘ñV“ìì›jëêPÓSc!½z…y-ë/èëd1ª¯)kpwžù?/¦ÃˆB!„òçé–——“Ýò¸swsg¥c>` ¤G˜˜˜ëè–e[šíí{)è¶åðÕÒEZÖã:l»ÿ¶"ƒÁbHÜ5gê‘d @^”–¯Ôàó*ÆTkÞ®~âùµÎgׂ%h5Ü7tnÕf ØWJc±9¬? G.•TtƒI%rÔšQQV=»õÛGú¯[ÿTi‚ÇRkE€-Š 8œè4¿õìµ::li”_x¢˜Ž¢ÿÞÞÞÔ„B!ä¯M·¬´µuŒ<•66QÂ/aH|©º‰‰y×®#¶nTš iY·à¡ð¦ß½ Å`¨šOÝìڨϤ6êk+”¼Húi¬‘ót§ôàÛ)\c‹>VÙ;Tß™¬8»Ðm7Ħ›rttTtvyÄÔ´žsÚC5*äêí˜L†Q×–\àëoÄRµÔl-G¸:3Þ·´ª÷Ýš‚)0¬Ãrsù½ôy€ÂDJôdþ\S³ºÈ»º;샔¢ÿ>777jB!„òw¤[BB<]]w½Wú˜c::†å¹ÀÔ²vèÈAáßà³äÊÖ͉® ÚöÞÙçQE‚û»&L.ñ˜o3zéb; (ùÙ™5’{Êöó¾k6¯£³×f›ã?õœý  <˺z2jX¡c;±ivìG—,ÿíVŽ\žwu£Ïà®­&,›t7"£àŸ¦ÇØy¬÷ÒqvK|Úߺ.‘|¬œôÝÕƒ‘s6wf¦Ü΢a[ÿèYI„B!ä¯À &øb¼æKoîs6xëÕÓÞëÅÿá­„?àcŽ !„Bù+0© ¾4AÕèn?Æxx"ކmB!„B>žàN‹Æ‹öŒÔýÈÜK‘0þÑ<3ePjåz]$ŒN6áP2qøõbœ0^$|z¸[j«>ϧ‚B!„BÈ?£æØ­éÓw†…íF/À¨HWÚ¿ mÔ›wïž×(ϳž³¬ÿíYá_¶W‰èÚVLAÆ£4  ÒÊuq¯ÆÊ A›Ï=}ö:_”S¹ê«T N!„B!ÿšQÅÅù99é0¯’k•²€DEœ““Q}©P°ÁÕB³J†’ñðŸF]Ž ãN¬éo¬f4í̆.tGÇ Ÿè,Po3mÕ,ŸL\Ãi¡;† ¦ñ¨ySºd‰+V1p ú¹ì¾+Æ¿¿¶gdãº×_ }!Æ‹„ñ1—t¯Ë3ühp¦¢Êðlãña"aüó~§Oƈ„¯n›aÆgPª7d¥×•—Âx‘0>îäïmUiâFB!„BÈ÷J·˜L6›ÍÁ‡ZÓÀ3ØlNµ…ïή ΄ö uóÛTd&Êí¦öÝ•Çsöš-×”:9/?4¿ÎååûcäE®›2oªgLAEiZ¨ÇÞhE÷7N]¸ýqQÕð¼6StìÓ85hÍÆ¥kÂSê›è0‹¯þ¾zÆ"W7ßÐCË™‹f™æ†~,¸ÂÊ,j¡\ºNE-ïzЮ3)Œºæ.éªÉ`Œ^áíb®u×wæÄ_~Þpõ=Ï¡„B!„òµjÞL¨ªªi`Ð4%<³Š¤8¾L]O¯qµÒ²œ[ë=ÃûþÚÏ~ž½¬t×x •ðj÷N¿ã‰Œû:¶ý\õïÄöŽNL ’î\>,Û¸<‚¼èíýçib (ùÞå¨g`kTDçîÕx½m¥ÇŽ1àïÃd°âx«IvöM µuu¨é©ŠßÞ{¡8¸ÂÊ °jèŸqç¼¶&›¨ØÚ8iÕQe¡0?¿Pë9nÿÉãûw‚|îeÒœ„B!„¯U³wËËË©Y³.jïµ1 8 d/€€LLÌut k”—¤Düºúõê‘äeÙ×·LrÇ`°˜¤iyP¦Ýú€ãzÿ¯½û¢êâ|fû¦o)$aIÐ;$J MDÚG±!M(*(*MÁ.E@Pé(A@@j–Ð’©¤múnv“í;óý‘BÊn”ò{ž˜½;sî½3wæL[ÉÕM_-[qF_ÏÕ gÔ›¨ôlÞ¾¹Ã§ý¼ãh:üìÉïþ²s|s1Öx@émÛöi¯^c¼“‚#DDLgÆ~‰K›€>aaC–,y­FqKÆö…Kb+þkL=t&›(èÍ)“F œ ¬NݳgQdäYY‰¥?sìáàáág-×*MjR6¼¿yäÁ—ý‰ˆH½êõw„_|øÂ{‹»’1÷Ò†}§ÓÛ­^v©ÍŒc—.øë”¾ßÕ+8m̪—Çš>ýè¥Qs?ä‘.õЪ…k£¶v™=j䬕íÎ6—¿y°$ÖÖÌ­ãÕÚÚÒXuZ¡çØñ‹ÆJˆÌy×þüböš«:¬!pŸðê=¨?s ð@àn9¤[A-ŸEF®2´<žÀÎÎiéÒ1h,€úcl$Z«.$%E—¾*ÃË+°I“.Û¶}ŠözàÙ-€‡•nEF®:yrKlò)åÎ(€ IDATêEÔ‘(Ÿè9dÊz÷»gÏ"4Ò-¨+Ïn%$\ŒM9Esˆ¶Í%ZF´…ŠÛ(ÏŸß¹ª¬KÏ튨äJn¬íhÿÏÃqêºE•|uFiJ—˜ñ]„z ÕŸÝš4iÅáÃk(‚h:‘¨|j;¢™”7ìNFF|Õâù‡<”b "¶øz¦Í P®úÕ-ƒ¡D­. Ž•r­RÏ’ÙÞ¤VV™hIþmáÊo>_ùÍç«Vþ®íµùD²âøš~N 1®¿»¥ˆŠù¾ƒ }ú½¹îô‘$ETræÁýˇ„Ú3ùèʨäø«÷í¾¥ˆJºòÝä6vŒÐïÍý_w!"Ï!»’£âîìPgøŒX>øÝÍŽ'+¢’Svoû¼Ÿ\Âcßb‚à QÉŠc§ÏIVDÅ,kc‡®€ÿ6ÝâñòkÌ#ÆÄÂ*ùÖÞ*»™pÛóì¹õgKHòÌÈVÎ<çŽ#Û©à÷_bÍ-ÆoúùõvÉëǾ01rµ²ù°Ë&6*Kåì‹OïøaÓ Ó{³Âd–¼ƒó~J ¢âó_ŽŸ1aÑ-]]ÑKÛNÚ¸rx˜ÝÅEÓ>_|JÜiì¼ …ºv˜¼v~¯`Ñ­í_­ÚrY>xq,€§/Ýrpùø„Т+•¦Zˆ~!;ÖÙË+°jñüÃ?nZõý¦U߯ÛqÛPt~ç!%I» hÐjx¸ˆ2þØzÍ,Ø«‘sß·:°fåd95êì#.ývâ¥K6¯\òGßÕݧO¾Y@DÚ̋ǣÎ\WYê^$¾›QÒš«=°zÁ¯ÉD¾ÏõêÞ¿‹'Ñåóç-ýmãîÜâw±E1?½ûB+/ Ã0 #ölÞwʪó…–¼}Ìû Ã0Ýw*\äª+géàçÀ0 ×ucáÑÇô>~®äæ¯3ž°<Ñ„žtÕŸÝZºô‘#ç%¸¤y«€Þ'êJ”C´™h%wôðð«RÚ’üÛ•Çï^@ŠÛ²§`èÆOvj-¡¤M L0ŸGDi?¼3aS¶¹tÕ畈F½‰l½‘¾~£6[öwå³df£…E÷@†¸o{wüðREveÌ‹ûsåä?§œ»ðUGæÑ Ü”¶m\×QÓîæ^I§~~¿Ïîc«.í}3HôXu‚%kCÿcNYB¿ÂÕ-xòYy3á¶mŸöê5Æ;)H0BDAÄtfì—¸´ è6dɒתå7>#òƒY‘Ìšù¢ôq›ÿÈ IçWÛHèæ¦Ýé&2¦<™Iä?vÒÏ·jٮËo¸èÍÆb[á°†"‘gÛÃÚ;ók¨´ÄÉ/¸g:“Môæ”I£Nž3"(ëðñ¨“ñÅDS?úäƒ 3§´¡“ TɥŠ/Yˆäã7\L+T¦žý~°Ý\ôáÞœy’NÜìƒãq±±ñû9?d+ùÇѯnL#µüã±ë‰ Ñ,y9ˆˆŠM‹üMay¼z3«-XàéM·ˆhÏžEƒOðÜ[½ÚŽé6aðà÷{ö|õ‡&×(èÞoÂ+“¦¾2iêk‘/úŠˆ ·nO$"²œßy8ÓBDÚè^·ñxŠ×ðÙ·|Æ›ÝE‰× Í6w„±«—]ʱÈÇ.]øÝD«YÙÝ%NàeŠ^õú;{.›»¼·ø£wz/mXðú—q9g–¾·æj¾}ëÑÓ^lmg!"³Á‚h@uކˆ¤!={µõ“9ÊÂß\ºjÆËÇhËÓ•e†;{g ua†qhñÖ/7ŠKw!šcÿsd¦ù‹'¶sb†Ç0 Ã0 &ž-)›yƺpÃ0þÓ/ë qßD4kÞ<ôÕÃEDD–‚sËÞ÷—2 ȼÚÿâhvùžÐÖâ*ÓßXõõY3‘Ó­‡¾Ñ"0¨ísïütpq˜W£Ž}B š²yqºä} Fwö³g†çÒ{Ê—Ue9dáŽn Ã0áË·6¤…Œa±_é»nïú o =Ã0ŽM}qº€½§’µ¯»ôNC†aÞÞ±éý~ÁŽ Ãˆýº½½=Ù@d¾³¢c£©Wˆˆâ?l"²|Dc)8³ø•N ¥ Ã0ŒÐ£ù 3w¥°²<²D~ƒ?xsÒØA#ÆŒùæØ©dÅ_ëÊxh–zãžTæŒ_z•žÊ5Žóñ²í'o«ÈÙóJ""’x¸óˆˆìŸ[—jâ8N}txåæJz.YÜCHDãNi8ŽãÌwVu&"j<ûšžÓEÏð'"ê¶£ãJ.ÏmU¶ƒru/{Sn³91ÚZW‰)é»VDDöCÙ®›1iM¿oôm>óŒŠå8®à·®eSZ""¯6Ý{wkéFDDÁ^Öbu€'“˜.ˆƒ§í?x[•¬ˆJŒß²öƒ.žäPH·8ŽãŒé¿MhZåœ8ð¹÷·ß*a9Žãô×çL;QháLé[9‘Wä¹’»é¿Ëw7uæâœxµOKO’};¢ãèm™²äEH$"Ÿçúùg¦+ØV/µeýóÎÝ7¶Ÿ0mŸóõµfc?žóîÐæò‹Kê7ÙÀÓ‚3©sRã¯ÄªåÞúzÑk9†‚ë[#›‘zïÛSL–"…šˆ(kuÿÿ€oüe"¢¼Ä¼Š7–K½}ËöŒË3‘Ã<ˆ ö¯ÿ;vßúh"&lòà€jwTXÔÙ"’º:•¥aB'WQ}GD$tkìND”{SQù‘&SÆÑ_Äd8""c¡BMD.åÑ ½šy)ÓUé–LÊ#"â ùDDönö<""ˆODÄqÄÕ»¤¹Á3ö®v<""F ”~¯f¾(nöÞöåÃ…Dlö•ÃÎõl€WçÕxꞸ͞Êÿó|ØÄtYzf¢?ŸHàÚbÈ”Ñ!+çÞ¦‚ä3ßËÈÈsÄÒïGúW<µÄHü›KÊÿ#²ݽìâÐñÍѾë–f\¼èò"AȾÕߪËwlàH¤Òd*ŠYrà§¹ºù—‹’Ўφ9×½8""aÃ=½)N¡ýkÝ_yý^ò(ͧŠ/}ûƈeÔ`ܩĵ]En¾NDjUÒ ÛÕžGDÆì¸l""×YžžáU9ËÊðy¶.!ÕY’ïPàyVç_mãØf꯷Æg_=~ààá£Göï8™¢>5oÆžñ'_óÆIaxìa €§…KçWº ‰¸¿§øpÛù¤¬¬”è½_¾½ð6ñ‚:ú‰ø ÂzùQn\Q`ŸA/ êéþ÷âO¾^µóšEijž‡H[}]NTphc‘¤ï”~^5ö«vÍvÑÙ¯—Ÿ-°pÚØµ‘cßš0üŹõõX\é,Ú¾ý^{†H½sÔ ïþ|*>ùÖ…]ŸqYyx¥µ=‘]‹¡]¥DìéfnKÐræü³‹ßù!ƒˆZŽêëû0N­òë¼µcÒKf%y£A·jh o{»Îëã>^ºõ𾃈ˆ+)6âò<‰ðD#<¹t׿Zùš~¥f9Ž3¥oàHDDŽòæÁ2""ry_®¥âU.¯œÐTž£áæ¥/“ Ç¡ Ë_ïPå̈́ŗ洨ÈÖÊþn>'º¤ÖÅUcHúi˜W͸ýÇÿžk.-¡[ÒMR=¯i=ûou¥76|÷’–ã8Nµ§‘÷Ôó•ÿÛðÝKÚ{(YKðe¯Ê`"öªJ›ãʇˆˆÂ6å°Ç©’–E(è¶áìâg„DD<Ÿ¶Ýºw²'"’D¬I1au¼*àq"iñþŸW·}4¤ƒŸ}Ù¯–Ï¿µæüÙ/Â"4|eÛß§õ vÒ¤Ä&(‚û¾·-êǶ÷–¢À‘“Ú¹¾ù¬Ìê½yöíçýuzñ«|DD‰¼Úøòر¹míîeq¢Æc¶^?·æ­ç[xˆˆˆønM{O^qöòª<Êî^‡Nûãêι#:5´#"r Œ˜²öÒÉO;?´WNÜ{[•sê:oÁ€F""x{ŠM=pþÇÉÁŽŠ˜S'Ï'òw›ðÃéãáqx"à½?`åêàŸÃÕ-¤[H·nÒ-¤[H·à³ù#‚øñ¥Ç Ãà'Ô Š””"Òëõ …¢Y³f^^^h€û(½–t Gð‹ÛYÈ´àîN¢êØ‚ñáq1{öl©TºsçN™L1dÈèE‹>7n\tt´\.o×®R/€‡MµäZ•S,†aq=â†*‘HˆH*•öïßêÔ©¥™Uhh[¹\¦×ë’Ë•¡¡¡h.€‡¾3µõ®n=Fpu RºUm@°:Ô`Ðxtdgg§¤¤„††º¸¸èõúƒº¸¸Èd²P¹÷ÁãÇCCCããSˆô¡¡^›7”J%DÒéÓ§—¦g€t nÀ¿”hÕs@¨y þ¬†M4ñB\m¥[.))*"¹\N¤W*•z½>&&^¡P„‡· k»hÑ"oo‰‹‹·\Þöøñãr¹¼ÿþÿIÀOg7ÀSo&ëT}’¨ºÊp§'1LQd”b,ûSgU³¯-¦:'B½åÚ¹W¬7©R© "ooooR*SŠx½^/‘H† éîââÓ¿"o¹£äËԃ曲Íd¼¥oé®™ö¦¦Y£"™cQ·™Æý_•´ ,r‘ª:½mRXê.`%TÖZ¥TìÚ‹Éã¶?_Ô{ k¶Òê.’)t)»ÇªâÂ×ExïÚö€þ€^¥W©T))ñS§NݼysékâCCCÃÂÂjv©'ÏÏç߈×zÍ_µT†­²˜Ì–Ÿ{(Þ3kK'ßÖµ—¨Fm4—˜ÙøŸ‹=©0ô+s‰µ‰ÆJ33ÄëZˆUo²8®$Ñ0ܵ°óZ‹¹r›º¦TØôCS™³ä_ö(|ög‹1ÝÐÏQ¾À”c䊮é{(ûþl¹0]åôœ1Ÿåt—µTÈ4ÕÅ8sš¾³³zmz•˜Ó ½”}—˜òM\þim‰jòYÖpK×Q¬ì¿Ì\`â”1úAî…­?7«K—þ‘IiáŠN–xS¡ïD£ÂÄébuÍ…ªy7Êó]Àl-T­µJYŠM¯{(Gc­öűcÇâÇh Ñô²ýýšE£ŒNŽKŽŠŠŠŠŠš>}ú”)SÆ׿ÿþýûWŸE‘±¿D9à*óýTÅøk/ëlÄ« –¨7çZÿÔ\)`ëêêèŠùÊÿñxuSá>³oÉAǚأïªì;é“MižõÈ—„þ¯.ŸÚIJDªãŸ½Ó3¤kpÈð—?Þw#Ï\~L>èÓÅGâÎ$+¢’“wïZñr˜ŸˆHÚò‹QÉŠ¨¨?ݺýº"*9m÷/ÓÛÉøD<·a,éHDd÷Ü×—³¢~{É…)/dªéûéùîí"×nŠQD%gø}Çú늨dÅÏ;H*nöÛ÷Z>ݽ÷oÏ(w^¥å›ýüøeë/)Îì{­ŸïÞïÛ5'nœ(Íný¶}ñ°6Î<â¹ Û·ëÓæDDÎÃWÞPD%Ÿ|¥‘SõHî§Žíœ{}ûŽÕ“>º«ÆU™‚†ò|Þ‹3…Å?ëÝÙg¼$ž?‚oÇgš¾,ý¸•͉UöEÅûo8~Ù‡Ç+f3 ÈË™TÙœ¥z!þ„ñW>ñ</6a²nswê9Š–Lx É©¥ø³·x'¿79 :E¯SÚQ3DÜ1Ç•KùgŒ·ƒE½ªþUî1Ãwñדnr{F²ÿ¬ý´¦”²Óx%HòÝ$¾«€\Úˆ¿›Ç[g¼e òß#pá‘c3AcoØ—€$þ‚Ž\réźZ dY 5Ëb¥Rµ?Ò&%©JIkÇ}FDÒ©R"’J$CD$“Ȉ(%%E¯×ëõzN—’’2{öìút±“C¶ØB6ã$"ªãÓú¨WGÓcÖM{FcúyñèMΚób9~ˆà©Q÷æ.lÜgTs"¢Â­³ßY]ÌQ湟¾²ËÃNË’Ðûë×}Ù]BT’—g߬Q›!S6·sÖgytùèè5dÌ«Zµ–ˆ„ ºÎøô½“Ãç\6fþ}!¹E§ÆB¢âÄ¿£W²LÕ÷Õ€q—ÍhÃ#"K ùtjju“O™óqéàND<û .-ü…™7.°®ZúvõÞ/vÝ"c3Ï_LkÙÑ_@¤º}îZ‘.1]WíÿýÕñ’«<ÖWý¯œ[IÏ8îìRCj!7Ô[ÉgˆX2Yè‹Ò~˜ÂTŽqç»—ÞŽ.`‚}†ÈêÄ*‹`¹ +µ³Ö™î0¼¦møN*²r?Ÿq³¯È¼ˆe¹¼$Μeèlg¸[&Õ6õ”ìe[ïãÚ͇'jv_b=¶XüG }«î•wXÎWÃ4l' â¢RY¾7ß³üvz× ž¨ˆU™ùÄg<‰ˆñxä fʃ"®ü_µÈ·j¾‰©Q)ª½WÂ"ÂÎßÕNA*Ò­ÕÅÇŸ[±b\Ä8eŠ^¡RH$ŽJs­Ò¤«ž¿w¬LçÈ™ç,àlÆIDdëÓŠs¶ ÿ^:š³nrìawdµ~þJÝ€y¬Ñ•?zžýÊ©|g¼­ àéP÷¶.ö õ&"2];x»¸Òàa.ÊS›HÒìÓºKˆ´Gƈè5º[ŸŸS‰¨Ñðé½]*f­X7¥Mðsm—Ý$"rk߯•ÏiÎ}¶`Õ ""ÃÙGÌüæBIõósmFNhÃ#b¯ñzë¦/tŽXtuÓ^ܽhîÂYï¾ô¬š5gÿöêÀfM‡¿8hêèA§ìQ‘c‡ö …šsŸ¹îQÑŸßM1mü¬“9UO£Ýg±rÀcë>~θZy6×´p/½²Û)5Ù9)É9)Åùè{¼¿¿1Ä÷@†Ë±ä–žü·pé¹Gdubei¿”¼ú3͹è¢W9_;j÷?¿zÕCæÇˆB¤±ÇÉ8NfÈsN<-iåÎÝ•þÜnÜ–ÌÒ‘Ñ—¹²Ñðã%Þðþ¼j$;7äQ>›_v‚‹^¦[xŒsàY–¼ò‹ ¬QÆs”¶[ kó#ë¡Jîy>D¤PéC½½È…ˆT2™$^í¥—«T*½‹ŠH’’¥ÓéˆH¥RÉårooïºÒ`ÙõëÑ[(fjÓæ§å3uVó>:úÑ漣4‹ºdÇ%g]±ó©Ox¼[òK*†¤[µ´¼ûu§¦-¼ˆˆ’ÿ8¯d‰tIgÏæ?°“¨¼PIv–%sîí ‘@"¨ÇM*<ç&nDD77þš¤åˆîýñ¤¬#ÛWÿ¸kë¦Ç’tÇŠš ^|hßͬS7nl[ñ’_È«;”‡WG€'!1³º—HÛiøËI4µ/ßÏWú§Û$qó$â3œßKâÎiú™?Y4.e—î³ËDDþÖ&V9à×pf¯+óp±[t__#“žêÜ14 î’¡Ÿþƒ9ßL†tÓÏuýÄ¢%¦Ã+ü¬eú³^Â.îLð Ã.ýAGá‹òê_oÐ[žg˜µÎ¢²PA”aòÇÆ %E)•JK/pyyyÙøcN›Ë¦§³éwØg_¼\üi†ðóY‡ºâ´þi¥€ë¬æ}tô£ßM…Çtýûi7ßâÈŽç×€J7; H·Ê·DDÂVýC*íRùNnNwoº¨4Ú9‹ÑrZŒ¶ßÈã×7©‘´œ¸éç±ÏµrÏ9²ýÛ¾üboá½Gòðêðh)½™°B…«_ 3X~þÎì=ZܺÒ1¥°±è­.Üö/Ly~âßöHÌ 5ÎUørfp;†!4²2±²À±vsZ™y*¨'žâÏËS^e5uå[‚Fâ]‡$N?{ •’@mT¸Ý ŽD.]D]œG¡¯€ì[Š:ŠÈïE+Çâñ¯¿KÄ+4®eƒAÆÐÅ_‡3¢&’ß÷Kh¹ÆM¨tëi°Lvøc&ÿŸÿjˆ­P­íÒøÃ†ñ.NV‡}j±zÛº\î­Ô©"¼Ã¿ï¿pèΡ)º”^z¥Ä§HIªT¦èõTz'aLL̺uë¬GÃÒñ×ÕþþEþŠZöÐn.þü·ý¸FuÇiýÓJ›ëªæ}tô£ßM¯Øÿ4‚ÝFÅç)›Ïd_Ýh?Ü Ã ÀÓ¢îg·Œ‰Gv%¾þN¹ŽZ°øöÜùcœGëG<3xK¿©oÞȦæ^ø|Ùþß ÅAÏ<ãIDæÄóYFjøcÕ©éjêHAƒûxÚžmïãy÷Ù-ά7IÈ«…”²uö2Ç:nÝã»wìäODƨyo-=¡†x¾öq¥#‹…ˆHêæ$bHËÕˆäaÕà‘θJÿQ9ݪøä:¾,ær[öIõ‘˜7!J6¡ôß½%ûoW\T)ËÉX›Xç*øäˆs¥yJ&U- j"‰×U||Âepi¹®’íWª_ÀaÜE‡ å7(¸ ÿ0ÈlUއdW\õ¯{÷•ì­6±òÒ]E§tå3wlÎw) ¤®äV3T•ê½ÜI¿Üfóët:•Šd2]¨%¦rÀV{¤®Ž–Íßãnñ½ãˆï1® ݲžo¥®{{]ï}ãZd½ç-ï=¯âƒØ­Dú¸_—zé‹nv}ÜqòV®¤‰¿%ÿºø˜Šýg©ˆîÚž¹}FzJ»/ÙqsQÕëpúŒ³7Ù;ñ\G¦G.ããéXÇÌ,EI© v…/úuÑ%•Gç>T''h¨•£(bÁž±)×~ýð³‚Ê_~xux”UK®*ò®Ê ˜­Ô‹a”hÀ%+¶’EDD¬[·.::*""œô’èéÑã·Œ•ËããSˆ¨4×JII‰ˆˆ@ß=j}OeºE\IÌúÝ'Ìxyhïæ~Ž ‘63úüï·üUÈ’Y±}ìXÃÌiSþסqÒçÄøíÛO¶Gs$ýgÃæÊg£¾,™8´• S’ú×á¼6Ã:ÊÈb0sdÉÝ?ýë°§½ÔÔÎIZôûòãþ“G¶ªµ*š3KßZæúÕ”ö^­Ûß½j‹Ã£+.äë.}ùÕÆ€÷FwpóoÛÆQ}ÏTI·Èô°êð('Z5s­ú¿?‡’ÿ­qãÆ©Tª;7ëõúp½þûþŸÅÇÇ«Tz…"…ˆJÓ­Y³f¡ïú.Õö¹|Èï¨IDATžòžwåA㹈ÓSsô1â ñßšßœG±3Û¾ù[6~ý!vÖƒýÑdx2- ©ìììvêõúðð"å”)³õz½J¥š5kÖË/¿lã=ðT¤[ö=6ÿ±>‚)ÊÎÓI<½\xD¤9øq¯ 'ò‘m!Ý€‡œnaXxÂ’®èèã11ŠãÇ~öÙgaaah¤[’Ö³¾[ñZ3'™ RÎìØðå·GJ°×Gº€t n<­xh€‡¡¶×ùá*À}Ãí‚€4à¡ÀÍ„H·µ=»¹Ê`Ðòx;;§¥KÇ ±à ¶fÕª'¬F'MztêõHóÔVái®ø¿S‹G¶­åN, à©K·"#W'$\8thµFS ˆ¼¼GŽœ·mÛ§h/€ûO·"#W<¹96ùõ"êH”OŠ#‰ ¿_xé¥é{ö,B“Ô‡•g·.Ʀœ¢9DÛˆæ-#ÚBÅm”çÏïŒ\EDÄs²ûL²â쎗=ùDD$nùÖYETòå·Ûwˆ<­ˆJŽ,zÁ1‡Ì_r$þL²"*9eÿï?OîãÍ¿Çyˆƒ_ÿìÏĨdEÔáîÍdxµ¨~ukÒ¤‡¯¡¢éD9I;¢™”7ìNFF<[xjC´¹K‡¶#»xlÛ—mëåM”±ó`\²dÉ\¥SáÕ<ó?Mèûò/ë燋Hwàçë*÷Ö/ ôüêŸÿTèîa&öÍ#?Ž”¦ìXxàF\j‰BýÀÂ(ÄTb4q¨ZUx¤ª€kÀ£—n %juu¬”k•z–Ìö&µºˆˆØ‚S»Î:ôh? »çïÛ•_èI”¾sç“óð7çO Ž_z|oœŠúô»`öÐA «ŠÝµöƒx~·÷§‚Sc_|ã°à¥{uÑíñÒ´¿½Þ;µqjÀÍ9ÆoÎdKh׿ÕwÂE¤=÷NÄûû²Y"æËÀŽ­u&bÄò—"?ûh`˜Ÿ„ô9¶¬úhÁ‘tïQ¢¦«bþLðéѱ çÂÂ1¯Šu›xðûAND$6c¼ãÈ=[œ{Ü ³o1þÃEô vÐe&[|;ýùÌÛW´X)žZbçý|«OÕÅIUè9[!>mBÜK¢)L–{:zøt u©1]¯¼tVÒËÇI“sîDöAÜ0Mƒ:‡ɬ¹z4=ßX÷ñUEÕÌB—}`HŒÄÓ-°©ÌÓEÈ#Τ.κ›œe`½#LÛûxW±síÔÛËAWpáXŽFXmµçŒ*uêìŒB WÇ™4š´ëŠ;ùîþW…ºV­ûßĬ}Ñövwñ˜BmùO“ûkŸa4xˆCÀS¡úÍ„<ž@ R~‚yĘ@X6ÄÆl9¡'¦ùðw»&ýú{Ýþ}²©ò7$-ÆoúùõvÉëǾ01rµ²ù°Ë&zÄï¼b aËÞRYëÁíyDöá;¸5ï@wätNÅ®ŠïÞ¶¹+‘ù➓٥9mÒ…sYfiÛIW³»¸hÚç‹O‰;·á£Pié—ì‹OïøaÓ Ó{³Âd–¼ƒsׯ‘>ú› .¿¦¯ž]‡Ékç÷ ÝÚþÕª-—ÕX œ©0£HQñ'USRËÁ$_ìî-âßÇB8¶D¡Td)2ŠõDD¬ZQ¤È(ÊÊ(1š…Šâ¼\£åÁVK õ  ùëþ‘ûû®Z]ÇyÒ¿Î]<½\øº‚’B¥‰ïäÐAÞ²a=Bú—Ùl'  ÖWûìLu¡Š¹8‡<ÐȉW[É uA+tt ììã!¾ÿzs–ºÖöû^­~±–íŽý¯“ûkŸa4¨Ô’+€'Zõ«[2Ÿœ#)t…¨MùT Ñ/dÇ:{y–ê ΕôëÙvd÷®×"¼ˆnnù+­J¶%”ìÕˆˆú¾ýSß²I:û6Š6‡…u騬KH{AÚ‘+^}{toyÂ/„(yß9ÅÝ{ü†ÇqTudÉŸïæC”´fÅê_Ó˜h—úNlü\·F[ÔDD‰–.ÙžlÿÒÀ1þ®î<}Zt|ž‰HŸ}ùø…8- îžÐú÷êâItgùüyKSD=;Mˆˆ %ÉW³ŠªM0Y‡¾ÞŽªüT­£ŸXÀš ngÆ&š}»ù{ð‰ø²Ž/ا\(òìä!-TŠ]Üí93Çsú©ÄÛ*–øÒ^r?QIìŸiÙ†ÒQ-JP_ÚÔÝÁWbȸžUzÚ›‘¸¸z;8iJãˆïÒ ¬›“]P vö’ H_’WD~~BÆ KIOÌ5sD"w÷¦-Ý<ùœQŸs[q;EWãžFèáÞ@L¦ÅÍÑ9PfŸ–[Ì–¦²Ž}¼‹²Ïž.Ô³|×N!m½,gÒ mîV-)*¿ô¨Ë«µ¼¡¿Th1åßΈMЙ‰©£¼¥GC/‰X}aQêõœ¬"–Ü;Gx–7…ñÎÉäJ£¸cpsRO§$+YŽHØÀ§s‰§T”i2pÖgÈ«G;ðî·­Ìâº;7ñxÒP…;Å\=V{Ʊi`§I@¨}Æy©–’Œ¨Q @G;&×ÀY‰Ê¾êjp:Míë`/åYLªôܸEz¾¨bm'FèÙܧJx]ƒ®•«¬¸þ³î³±Ý1BŸg›…ʪo°qQÅþÏ6°+,Ì4;øxŠøF}VlÆít£¥^ÕÁ„çÒ¨SS W¨8wVÅ4nÔ¥¹”+Ⱦp•ZDxÕ\[©}˜›Uš°zk˜¨FÞ(Ò±õ ˆˆïäÜÊÓÛUÀ#‹6GyëZ‘SX•¡ànÙÚü­Ž– ÚúÅðª~îjéÒ7š4éâ˜åFoí#RÝ$ú„h%wôðð«8ᢾ¸ç˜š˜“>}ŃèæÖƒŠªÏC1 ŸGDi?¼Ó½žÙ뙑}Þ‹Ö^Ù{…¨qÄ«;Hoî[½ê²±a¯7^k%¡¬CfUÖÍy×n : êéÅ+;›(oßÅGP~~ªìgÔ›ˆ¨~çÚÌF ¶t¨Bl߸µo³vešJ+JÆÅUV’{)·º5õò±c “UZ"bõñy…Žˆx®ÎvE…)y±×µ <ŠyD<' ±ùÊBãýÜ#òrb2×ãJX‰}@;o™:÷ú•A,õoãæÈ'ÆÞµu˜§‡13>7C-ôjм¡ úÈx9ðÉ”›“ª$²wñs­õgî9KÕª‘‹—°$5^¥á ÝC½½íJƒŸñ“{‰ Y…wRuŒ«¬i·€†öe /o eNñÝ–ïä$) 2Tli[˜r²Î¸}>Zmà¨öÖÞ÷ÝVõé\¥ž¤*ÔŠ+NWi‰ø.ÒZ.*1ŒÐÙÁEJDœÉÂYŠWu5(tðjÑØžÉʉ½”•”˸4ò jP圯ÐÛ»zw¦rTBÙê;g.Hµ²ÁèK7j™KIA\tn>'ñi(cêµÑYLXMbf‚ŠW¯fÍ<›‡JKÉÍh¥–%« ²rÜVÞžyœKÍÄ5Û°Á=ŒŒØ©Ù3>¾®\aRnJºIÒÀ½M[mÍ¡ ¬Ãj[Õkô#¬OlOCºEDÛ¶}Ú«×ï¤ ÁÓ™±_âÒ& OXØ%K^»[N»íP‘ÔSDÜ¥=G³ª]X6¦<™Iä?vÒÏ·jٮËo¸èÍÆbKá¹=·‰ä;Iïüñwü¹#1–½»9Sö™ÃUïEÔ^Ú°2ÚBváKŽ­ZúùÛóW­9õùÿ¸ÔCg²‰‚Þœ2iÔÀÉsFe>j¼×Š›³þŽ/& œúÑ'L˜9¥^U® ½Ëÿøz ïsê•·oåg¦å±DB{±E¡.±uŠä¢¢Ò1L¯ŠÉ¾u=¿@QXÈ’ÄÛÉžÇØû8Iˆ-L-1ÞßÓªü¤TM^šJCD&uâ­¢üLe¾H,ñ;™CšØŒ[ ù —³UÏMî ªzÃ8¸øËˆ4JE‘)/©„%AƒÀêeª¥[•«VvÃ¨Š‹ÉMKÈNÊf‰„bFèîÑÐŽÌYéÑ—²¯¥]ŽÕ#mÔXRÖbeMQ ©´à‰„"‹Ál¶Ö¶fÈ«»î¿­Ên…«µsÕ•®\<U¨ƒÅd&"ßú±°Ø¥Ã Íz íÖÍËM@–ü¼ô"²UÙ=je«Aa1Ç#"‰›ƒÌ‰§KM;ýû­ëUÎÓ2 ¯fC¥*¨Íî.­îœ1×Ê[v\¢-ˆUæfäÇ]/áHèé'u¨ÇFgs0a é—J–q tsâYrc²²uåáU_ÄÊQZy{šX‹o¥‘«U¾–Ñ@à&ó’ñNæõØüä«i1—3®ÝTÔ 곪Wë‰Õþ5ãù/xXÿ™ã={EFþ••Xú3Ç~Ur-""íµM' þ7ÈØ‹¢rjÜÆ«þáåqƹï>ûã‘Dúì¸ýŸšÈ¢øëh…SÞñ#™ú‚’½W©s;*8v³ô½5î_Œo=zZÃW-Dd6X°ÙT—ŽÖ¸™°l\2™9"â,fŽˆÆÚѨ^_úg(ÉÈaݼ8Y|„dRgÜïóf³™#bYŽˆL&KDËC íDäØ*8¢Uyy‰X\å0ç,wµ#⩼­ñy¿»—T“Véi÷ÒÊðj¹èe0š,DÄY,ñxŒÈAÈé æÒ«ô&’ËÒË›¢2Öl²”/ð*s‹«@§Ò,6gÈ«G;Üw[‰xÆúvî“R…:ð…|"2³6vЦÂ,­‘%ÖlÒ)‹³2´FŽq±¾Z*­œQ¡ˆ¿ã`ïbïCDf]ÚÅôDM¥„Çje¥ãû‡Yw묰lÃ/Í×,z“™H(ˆyunt6"â´êT…§Ì—O¦âÌ<Wi„©º c{„±ÚL¶µ6¬ò:æZFJù ‘©ÄÌkVeª‰ˆbk Vת^½GÈzÿâUÑðÔ¦[D´råä:¿¬½øuGﯫ¤léç½¥"aJ;øÃøƒ?TÿZò¦~Þ›Êÿ“³í…ðm¶viê[Û>œºíÃj“u Û¿¾ý›*Ó*/7iCï eÿÖœën=<‘Ô±äÊú9‡”¬¬ý«c[áú© Ö¨ÃCÄÕz»2Ç•ÝaFUŠÆäíììn±'SºReºÏÅVˆµÚÁ¯ÖBÄ×ħÆf™‰'HÉXb,©|„'²÷÷åãàèåP1Uâ'—dÆ–?:Áçñ8"†ÕzÍ«jœ±ÄÌ‘Xê&¤˜LĈ]$B"ƒÚÄ‘¸jSTØ5*ÖÉÍÅ­¡‹:YÉ1?ß6mìùê쨓…¶fÈ֣´Çúvî“R…Ú×riG{"‹J£µz~ÀP’S=…°ãRy5à ©$37æ–‘uºz»Ê¥þ¡é*gyV ¤E™ÿ­º[Û`KÓ-{;;¾Ro&¡ƒXHdÔš †º6ºÚ½\=šúò‰ˆ„Ρ¡ªó×Ê¿Z}A,k{„±Ú:Ÿj¶a¾ªž£IgáˆÄÎ">͌Ȼ•§Ì\|'ÑjFÄÙ^ÕV{ÄzÿVŽ àiK·ž‚Ã&‰×AS:¸ˆXUêñ%Ë>þC‰ç¸ ôq‹»'_XSaB^ŽÅfba‰$òæî EùÑrÅ)UažÁÙÇۉȜuGûpÞåÅi³”š¦ ƒ<|XµEæ.÷SS£®Ý=B’x¹» È¢H;{±¸4ãã9ztéé!õws¾Qh2jMäèàÔ”S ¹X«ZºõØMyù ½½_ûÊB“¸A€„XmJ²ÁÂs¨ÖwÃÕ«â\e-¤ò®Áîy:#_ìê*dÈœ¯ÖsÄÙ˜!+zˆmuîvÝ›•PðT!OÇÕ±ÚsŒÀNêæ&$NŸWlúgQý\y‹`$ };6“Xò Ó ¥‰»Ic2—%46 X8*¯‚›B¡Òç?ì¾ZÔÜ`Ë2¡sË.\Fã(%2e§ë‹Íultµ &ÙFi`{W)™Ò/äZ7ônäœt³Øê‚ eg.ªåã6ÛüÎm«mXÏÑ@Y Ì3Ù{úú¶áTjžCC1”$[¬¶6–¤Öºlô/Ò­'—!aéÀþK± @uB׆•/ÇÌÞÉϱõûê}FªÎ-Xêà¬-¨ñ“fCf¦Ù§±€ô𬢇uXÁ^=ÏmáêßÜž8SQjV|l¥ÔŽ{J²ä$k+ŽžÙUšÒ£‰Ì±‘—@™®M¼®´k)sö´Ë.HN†ø3ÕªV’]h}Ù¦’[gÒ Í=|½\ýxœ¾P=W¡åÈ¡¶xK’ïœ×¹…ÈÜ=âŒjMæÍìÔ3g{†<ÑÃl+¡¸ÎÎ-¾SøT¡¶tëîjÏé Uiq9îFe–ÚUi´¤ô+|¯ ¹k“v ±fMfÎÍZ ß¹öf ¯RR¯žçýãî«%ß²±Á+3õvþÁbžQ—›™¬b9ªu£«e0I+4ù6”’>%39[ÇpEî}ÚzåŸ×[]Iî©ÍKJXkmx£:îl–¹µ‡wC7gbµÙ¹·®*õf¾õ¡ÀÖæ/±±á'ÕÀ“ ¯„š'q® žFkV­zc¬T.o)1$§œ»¡û—©fâ¤I­^w0OmžæŠ×¯Õ7ز߃V)ΜV¸‡ØV÷º '©Kcx‚ Ð#ÑÊ\zÙyJˆ ™izü*(6X@ºGbÇFÁ<‹1÷Ffš°ÁÒ-x@LÙiíC3<Æ,§-<¿¯ð_Xú¿¶ ø÷áÙ-¨1èãÙ-€‡&xp3!Qbb"à_„F€'®n Ý@º€t ˆØ¼Ý£Bƒ«i:díÁ¹={g¨RVm~žŸ]×?ô î{Aúkók†ýÔÑÅ­˜øîåù¶Õ:z°Þê.}Øõ¹e Æ{\hùük_j–Çxð àUDDÄó¼5~0é¢g÷š¬ÿìJD7×(+n¹qã)~èAýk z2ÓÎ\ÌòÿOs‡:zðáDxw¡êzÍÿ^ËÀ=_  jÇi®þ8¹×.m[…øâDž…Èpk嫯ýpÛ@eÔ’1ýzôŒèѭǰwß©tñÁÊG\IüÖ™C#zôŒèÑsÐÛk£‹X"]Ìœ='Œï×¾EØ«Žè4`uЉˆÈ’¹iXØø?²âËD¬úêOï¾Ô£[ná݇Ï;i²:·ÚÃ&³âÈW¯?×3¢g·ÎíÇÎ;œm¡Úª ¿6?"bÊûFÖ¿{Ÿϳ[½nÚÀn]{ôèÚcÈ›c‹«,·fœÖÊë¯Íè9yú„Ñ#E„õ™´b󩯎Ü;¼ß;;ÒLu|jµâVBµ(öÎùúrqô—¯ÏØóŒ­:Ö³M¬´’Õv¨QýŠUÅÊ‚*Ex Gm¥7-…QKÇ÷}¶[ÏÞ'¯ÓV΂L Ëû…¿ULÄx½Y³×rDš3ï<;tc†¶t¡Ú»ó?˜c±²>”3Ô( ׂ éÀ¿Á¤ÌvŸ°ùÄßÿx›÷Û·{ÒÍwSªìßná½·ûøñ‡W½`ºp.ËTËG%—¿ŠüÉùƒÝÇÿ:~|ßß_ßšNÑ!+£ÝÒ3×άÿèîÊ»“ŒD¦´ýÛ³»ŒyƵ|ÖF;ueÂÖã'N[Ý+~éwç².YŸ[-aëâ6®»ÖáÛÇþ:uð}éþ¥ûÒ̵TˆÈu'ðýõÛvü¾cºd÷¢=éf®èÔ¼Èõ¢·wüuâÄñÍcJ–OXðw¥åÖˆ3*ÍFyƒâNÐûë·ïÙýeȹï¶:N[»m÷žE-/¬þ5ÉXû§¶›±j¨œ÷ 3Û;´ûè—oÚ]Zd«Žõk})F«íP³›ÔœíÝpaôokTŠ-:3ÿ½]Þs÷ŸøëðúWrÕ•3 a@Ÿ~1$èHsýP‚Tpóp\ iãö]iðbo~iþÝù÷oÀ·½—«TþO>6|€7ÔAèÛs`[ŸÈ­y W}®ÆL^åǧnû5^8}ÐÿööèÚ­çø™Ý ÉæGlüÖS鯙ƒòˆˆ3© bsÍ툄¾]»5’òøäÜöÕçÍÓ~‹Ÿüxç®âÞ_·wbJçfL?}ÞÜe~Oo‘ é¤_áÆ—sjÎíGa-a7j5㧯.þµw݉´;·.Ü,1´3°|o›U "zw ÷—‘£cGc¡Ö¢/)ëµãéÀá‹Lé¿âªÜ:'i2ië¹A7Ξ<}毯†/ÚñÍËžsç[ÿhÏ 9G|½E7G"bµ99zçBî*1‰ ô*–$täP»±[ÎGHòýÐ\J ãXŽ˜òërRr-z³•¹Õ6«:=û¥w㻼ñ¿g: êÞªèòâ:ª@D _X1Æú#= ÃÜýO8ÍFÎZyŽXP^’áU¿ÕÂö§k­éFm¡Ú®cýÛ¤Ú”ÝoXk‡Õº—Eb}AµW*žˆ¸ŠFò™*Ë7îßf9­»êöüäç2wÍ8|œ=ï<`¼€Ì÷¸ÀÛ î›)iÕо³c}{Žœ2÷‹w:SrrÅT5?ÒùGtœYµ;QÇ‘9ûàû‡|]R-Ê_åuê‹OËF Ý.òëÚ÷÷¯gò,Dú„“FÍ=/ëYçܪ1¦Ÿ<«i7õ£ÈQ»É•g/ç›L,WK¬’ön§;¼étž…È”ulÃI¶}±í8/¸FÔRþ>ˆåõ¯8Ï™´Fƒí:Ö³MòªOÑXiÇÕ?¥bkYPE„"k•öëd>¾õï| ±ªèÝGÒ«½–P4àÍ–¥‡=:ú4êÙQ³uá’þ¾+-Pÿäê^Ë@pu ྠ˜1ཹ».pâà1óGË…6?jìê4{õ»óæŒéµ–Ϙ¥­Æ-ŸÝÅ‘©öjn¾w¿WC¾œi^ð|ÕÃfû­œ0göÐ_ðˆçóü‚Eôª9·Z£•4yeJ·É³_è·ÔÓYÒ%B®KÎÒ zÚ¬‚UŒs·ù+ÆÌúdp÷Ï…|ƵCäêyáNL-q†4ˆ¨YÞøݱ‹•f´>C»à¾­Óf÷ñöº÷œÿÌZëÙ&!Á\õÞôp²Ò¼êÕà™wÍö‚¸fö¥)رnõ»ŸW­>wùkÏÜs¾È¡ahC·ê# ø,³édø3>©Gß¶Ì–[ý{û ¬µÀÌOZÕ#Ë-/_°ë·È¶p€ŽA@5‡wÃÓ(11ð/ B#À“ 7 Ý@ºð”ó[PžÝx pu éÒ-¤[€t éÒ-@º€t é Ý@º€t n Ý@ºH·n ݤ[H·nÒ-¤[H·éÒ-¤[€t éÒ-@º€t é Ý@º€t n Ý@º÷æÿ8c­cšKáIEND®B`‚apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/Screenshot.png0100644 0000000 0000000 00000477476 15051152474 032735 0ustar00rootroot0000000 0000000 ‰PNG  IHDR|Aê§ÁsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ/¤IX IDATxÚìwxÕúÇß3egK¶f³é $PC¤ˆ(ˆEQ±]{;^½Ø«`A@AQ@¤(Jï(Fzßl²›ÍÖ)ç÷Ç$KÊkAóyxò°³sÚ;sfÏwÞsÞƒ@B¢Œ±d ‰ë‡ÃP[[+—ˆ¡(J<¾cÇŽÛn»måÊ•“&MR(!ÉV×!„d ‰¿<ÏsÇqœïMñøñãW­ZµhÑ¢]»v¹Ýné ²Äõ %™@BBBBBBBBâï"º|øœ]Ó§Ow»Ý ,X»víÈ‘#†‘ü]×Ò)q éå„„„„„„Äõ‰8½°¤¤„iF&“¥¤¤ˆ'¸\.½^¿mÛ¶¾}ûúô˜„Äu‚tGJ\™œœÉÿ/^¼k×.É_DWeeå¡C‡æÌ™³`Á‚>øà¾ûîÓjµA0 £P(Z:Á$$$Ñ%ñw¢¶¦F2‚„„„„„„Ä_ Çqf³yêÔ©6›mÒ¤IwÞyç /¼@QÔˆ#”J%Bˆ¦iš¦%CI\oH4$$$$$$$$$þ”——Ož<9%%¥[·nï¾û®L&[¼xñúõë1ÆjµZ£Ñ( ‚Æ·×W^ÓÕf%¢¸ì§ùjq¬Õù¡6 „.Áp»âÿ´åDb }Õk¿Ôa@ñX B<­e[üÔýá hoÏ?‚ŽŠÈÉÉ=]¯.[&õ‰¿¯¾újƒÕ /¼ô’4½PâZaks Éø.zòwLr yþ% ‘¸.×t…„„ 4èî»ïv:wÜqG~~>AÑÑÑëÖ­ËÈÈËån‘z±ÄÕB]ÍX¿¥,ñ)–QKÁ!~%­ A‚ 4å€aøËb6´—í%€¸nKøU\Ž kÏ“?.u ‰ëlþ|°é¶Ã†9ûK>¢°};R?éGåÔ=•›G©S^Þ‹ŸÌ»áÞŸGíËYÑOÙªû¹5O=üʺò¬6~ø¬§ÞzõŽjÔq’+æyÕ•Ÿ ›w¼ÝñEG/^c¡BÕš¡wK\–ûËc¥ùY¿w§ì°¿ü–QFFÆÜ¹s“““†é™öî;ï<óì³)={ÅuË›À0Œ^«S«ÕA¹\®úúz{£PÛ:ú©3þ3ª×RhýUŠKBBBâÿ#ž‹ß~•ëÒŽjûp¶îY4|î: ›>e\(›¿oû+æ ­3å­ØA’+æù@Ê„›îº=Á \õÏw‚ºÿ´É]”ˆ l¢ÿ°B%$®y0s™þru“ ‚X²dÉøñãcccu:<ýè¼I3Ÿùñ§ý™™™Ó¦Nýk%õb‰ßâŠo‚ KOOOOO=zô¼yóžzê©x`ذt•J…1öy½|N0š¦{ôè1kÖ¬;î¸cúôé7ÞxcRR’Z­nyÚuôÀ EFFÆã?þôÓO?þøã>øàرc ×Òƒú#ÚØRhIŠKBBBâÄ[ºý¹IÝ !¤Œ|ç§mx‹·<<"V…š/ú<¿!ûå´1[]Uï÷Wu{5Ç{)‡òÃG-)oþðÃWŸoÜväô¦9ýSbëN•Ø['É®;ûÁœ>Á4B!m× /üT‘Ùò„—ö¼Ý !”º¢˜€†]“…ÝÜå§2î–¿ Ú°fÍš5«–Ž €Ä»ßýtÍš5-›Kûk÷Jµªå[è²¥KHüÖ×Qñ_óãËSµ!2(õÖåGëðæ¼Ú ¡€!ó'ÅQÉur¤Tv³Žº'11Q§({¡+IRƒ/èUáN×þsÞ‡^|uôè ¡à‹…ƒ"M·ôó»«8¿9_U­¤^,ñ{C]fˆ/þG¡PŒ7~úôéz½ž¢¨  @ƒÁàp8BBŒeee999í¥THHÈŒ3fL¿Y¡P¸\.‡Ãqúôé 6ì?xÀín}µÛ-ò³< ‰P™ôûê®èèèéÓ§O¸áF†a0ÆN§Ód2eeeÕÕÕ‚_Wê Î0jïûíj˜ DA+~”Édwß}·ø^gÅŠ‹ErvIHHH\™º_¼sN( à-:ã{hↃ¦ßøNÓ}ú ôYŸ¸úîÙžó{gþr×­Ë÷…ß±|ý8}áÆ'žYùðS#~~äá;×.\•Ëô¹gÉý£L—~7™Ø‰7',}å׺'n<~äС£–î=­"@¨çZ$RýæÔ»×æÇÏ\úLšã»—_ßñü¢OG¯~øÎ.—ò´åï§ÉüM»ÊŒµñ&Ó½þ[wvóèË×jõŒ3wü¥KHø¥ÃþÀæ¿7qô3Çä½:…Ùóêò‡GÖå~•à8øiöÔE÷‹š¶oò;7n(xi`?Ï©5çƒ|ÜCS;9Ö@Ш±S§Å°{g÷¿{³-rüƒKSë·þ{Í 7Þ}á}û­írÞ>+œ”z±Äõ#º yu–J¥êÚµkïÞ½U*AA2Õ³gÏnݺ^dYVÀ·j ŒFc¿~ý"##iš†‡‡[­ÖìÜœ’’’ëM*ˆu êÚµ«Î €¦iN÷†ZLì\¸pavvö Aƒ~ýõ× ¼ýöÛmå«„„„„„J÷lXÛþ÷Ͳ÷¥ÿnÆW?o¸Á€¸…ÝÒâ:¸ìÝ3û{y€¢Ÿ¿û1rbƢ埧„Éا%>¸*WÛwÎâÛÓZ.¢Pözáà‘è_~ÃÎÏßÚ÷ù[ÏŠ™öÞ¶Oïéž6óR’~Jþ×ʹd°Öš¹¿ªõ½ÅR$ô¼ÿÒ }jWúýX•¹2´nEqþË—­U…ýJ—è€ûK—ÒM+ñúú†wE£¹a'Ãf}ÿÞ÷U@ÈÝkÖ¾Û_ `ë9U¿sýÆO¾’D~¸¾t·>4ÚDn%€Tjõ:û×+¿¶éÎ ›–RqweŒÙ×ÝÓòå?9ÏœIuI½XâÏ‚¸ŒâÿŠÓe2I’$I"„ˆÀÑ1‘£ÇŒêÛ¯O€ZIàS\ —ËÅ-ê|3ÕjujjjTTTÛ žøÒ?Ô‘ÃÏ"*ÜT“–Οß–çìŽF—å±À ¼—c«Í5—#À~ïÏmUgü;¸¹ µçc¬ÓéBBB¶mÛ¡R©¤ (€³•”Ú…ë-«?¡2×Um%$®o sö76ývX¿Ñü`å,¹%^€èA‰THïÔ@€ª¬ý”ÿ®^X¸ç£ï¿%#5Ü4àÑ=f¾£·c®º*«~Ôó_ÿRíu”ÿ²ëÃ{{ÑE›ïûI×êçÂqaÃswŒˆÐ˜º ˜¿ú<×ñ0Küõà9±{Á¿¥2-´®ÊûjuÍ¥KHüÖþâ2_¬€3÷ÆP‘!³åç+Åû5<9œí Å³Ã¡vËÊÝ{V~Y ¡·Ý?HÛb€ÆYKjxc| ¨ô™3' Œrvœ³Ô‹%®ÑÍk“ˆf AÀN7iÒÄXxáw–uvì\þð­c‡¦NO2næË¿»èÄWU™ß»¶ÿ/¡Œ#h€âCY ¸ªSg,¡ÝCy›pË«»ËËÏÿ¸éíû(íÇÞxæÛ*¡Õpª¾l͘ˆø„žwl©ä eXÊè9÷NíejØ#0¶`Õì«Ñw›gcË6MÑ µ¢!’—Í-ðöб{c×å*s ­ “]m­þ—Ò%$ür™þ1Hyíà…œœì³¿Ûùóé}OõÃÊT²¦±ª²çÂ…]ÁöÕ¿Ù\ñwÞÚ*v¥‹2‘ågŠœÀ¬¹gÆ~VÜqÎR/–ø“x.£Ç0‚Œ1Çyyž%cž@`À$I5Š¢(‹År`ÿ!¯·Ic3 #“ÉŠô¹¿0°èæjÆÐ¥=¿šrnrfaDâ~Y˜£–QéÛÊ*ßG±‚ @Àc -eŒ/Š}»÷¸ª¢ò³5k÷ýð£Z­–É䧤¤¤±±±}Ä|„AŒ¾ˆ`X¬nO¿ÕÖ^í"ï‹ÅŠm!I„!Öü’7Oh3£¨¨Èáplݺ5***))é:˜[È–o{zÞ¿:@Ü/˜/=“yxý’_ ½k_kú3ö`+î/õ¨útðuņWÖŽüha‚üÏê÷7ž|sñ«»ÈДôþ\ù™Ã§¾~í>»~ã3´W¬ÌŸ^[ ‰"È0òñ;Âö¬Ú4%»k´þ¦տ3ì©ûÍŸ¥xì´zè#ËîìIºÄ¨)J-¨Úþò£!óŸxrr¤8×€ Ÿôè¤Çf|óí-]SVg$©ëݵ/ äÆi r ˜æ$C'°ng}Þž-¯í°._:!dþ½}ã5p*çíGž—¶}½ìdSo/ùl–ŸÊ×ܺqø µjñ¬¹ÖÒ%$ür™þ¢‰×-H}þñ3Ëžý€š¢;öæ’/‹Ã<·¸y Õœ…¬ËìÅiÏÝ{¢ õ¾Û€ò_½-]¬6€ëÌËã§ßôÖ[sxõ“sÆ¥§NϸéÞ׿ÉumÚõ~®ÇWÚ‘7nœž>á¹Ý•-êÈ™Ïg6ÄßûÎ[/-YòÚ«—ŽKŠmÈ©vµiWÃÅo_™?iøàôôÁéãf>þé™Ú–'¬9¹Ù¯­ú齦Nœž>8cÚ=ÿÙ[&¹Å$$ÚŒht#ß=ôͳ7%š¿yoùêƒä »><úÍÝqª®nÛùü¤èóËÍš}ߊ‚n3ÿ³ãáeêÝ‹è¡tûêÍÇÌ—¦ð¡Ó×þtqFWêÂÞ¯¾øz_¡ºÇ„Ç6í{˜Á¥$ßX&¿÷âØ¨ªoé;tñ¾˜iÉÔ9YI·ÈÓ=àÕ• zÌß½öü§åÞŸ)Ž0ýWæÚ[G_¹V—žT×^º„„ÕÕq‘u}hÛö§Æ|ë_‹—leÜõñî×øñDÝôp@ }`zL[·4éãŸ>\0@yríòw·Ttž¸tÇÆ¹ñIWÈYêÅÞïÎåA`ŒÃÃß}öÙÙ³g+ŒÏ]ƒ1F‹+­8ŽËËË{û­w7mÚd·7@ú°aË—/ONNö¹Å0/œ;wnÉ’%;wîdY i¡Ã0QQIIIÉÉÉ]âãM&“Ëᮯ¯gYEQz½^­VÛí¶’’’³gÏ<´¿¨°„e¹6ÕFi4š´´´¡C‡&%%…††Êår§ÓYTTtþüùÇgggGFFŽ5ªS§N2åt:ÏŸ?¿{÷îÂÂbžçÅæ„‡†?¾W¯^EyX¯Åb9vìØáÇ­V+´‹—h0RSS<^Îávéõúˆˆ æš–eÎÆ¢¢¢²²2Çãó°ùæ ’$i2™† K8¨DDD1˜Q(išLx½Þ»µ¾¾¾¤¨øôéÓGŽ)**ƒj´”Áíi¿”ê ÜœœÚšxuÙ2qsd×é%“üÑ‘ôä7+Ƶß^C¨Þº`úr;?´ùÃ)!¤ãØ““9¤žúÁÆûT_Þ1ëÃb "‡Nîk3}y~‹É·æ¿“%Kš2']vbݦÓl¿gÖ¿–zìž©¯gƒ<ù¶Gf'TozuõIgì¢uo¥þ²ú…7¶•Ò SæÝtÃÄÑÝ|ïm~ºwÂs¿jºÅy/\twZðÉ·8–O½ï;¿bËã=¨òÍ‹nk]ÄSÌón~©)«‰Iõë_ú¢aÌÛ›Ÿß³pÚb®ûtZã;·Üý•0iå'?Þ}ß—Ut§á“ú«‹ö|{ÒL'?°fùdaS«vMįÎ}¿0áÁÞóäêLª÷âÕ¯M‹aZÈûá‚kK@‘:d@ïÔž}ú¦%„È ì¶ÿ××®Œ¤ÒWV>jþ´÷ѵë5Äܹòqíw¯4Ÿ0¶ŸëÍyËÛÙyÃܪ§§<Â8îþ»ú«+~øïªýxøKŸ.M×ÿ¿}·¥Ñé¬VxᥗvíÚ%=÷%$$$$$$þd.2^‚«T*_TŒ–ãþ&‡/â)R×åÆoÌËË;xð Ï ^¯—eY„€_>$I¶òP!¤ÓéÒúõ›8qbŸ¾i–$I ‚o9ϳn·»¬¬,1)aíšu.\à8¡eMÔjõ´iÓf̘‘’’¢Õj)Š MMM:tèÀ÷ìÙPq yägž~0IlɺÙâµs³èrÏóM ŒÙS^<öÕñºšøÀÜ1]eNjϺ“û-EVÅ­£†Å¼»­TÕmÜÔ±ÝÚÏ!4 yè¶ÀÇ_>¸ú•/ûÞã;Ê–ÿ°µm[³žx×ՄÎU±öÔþÂÚÔýù4@ñá,sßÒ#ÐŽt~íÖ*ñò{Ï÷× ~Rô]3Þ=»~Kþ¸)­ÚÅ•oÈYõD¶ƒþÊÒ©­È»,xeȧŸ}õÃÑ=_žÙóåj@!Ã\öôäØn-Ú•(>ÝzC-¡h,ü¥.Œ<Ö`«Â§^:!Á¶Õ¯žãª~9rÊ48mò¿·>o”IÑŠ$$$$$$$$®?ÑåÃív;Ag¶ÙŸ—@2™,--m„ E%¥EEE¢¸taA\Å· ­Œ\>lذwÝÕ;­Z­¦(J†HADÂÀòÇq‡¢(•J¥ÑȺvíªP(®>ú¨´´Ü§‚B½zõºå–[ $ÎÇ!‚a˜°Ðaz]tlŒÛí—Ëå⤼èèèáÇïÛ÷³Ífãy^.—wîß¹“*@…BѳgÏøøøÓ§O‹²É^Û)~ÌØ ¬Ò¨¾ÙéDRªh‚—ªué’«R©¾ÿþûºº:Œ± EõèÑãî…wŽ3&44˜$IŒcÌz¼ÇQ2†¢(E€\ÆôèÑC­V×ÖÖVVVšÍæëCn5žVkO]­S¥¨»Xó¹óÖ°Äø@¦õÒRo]á ¸ Yû¼µÂ ¹ËoN_Þü•¹ÀÂÅ@Ð @à…+Z€0{è‘}§^8¼ú•-ݼ2è¸ÖäKFG ¼vÃѶd±ªÁwÌ\u`ßÁ}•• 3:Ö›UÍD÷ˆQ! »vÖB^]Qç·]ØÁ@ãÉÝçúÒ¶§‚§ÁbW÷™ûòÄÅÈ][pöȶÿ¾õõ¾å¯öì¿rª¶Eîâ½½ññ÷çjXÐ3‡jcgdòÈ^Z±ýäö5'·€2qæK¯ÝÓG'Íâ–¸¾D—o&¡(ºZ¹J€ð-Y2™L9yù[¶lQ(4M‹ç ‚@’D{Á`2™FŒ1`À€óÂÀ 0®³ÔUUUUÕTWUUUUUèõúáÇ‹ Æ¢££G••U[»Ãår‰¢K«Õöë×/99™a˜–q,xž÷z½<äryçÎA IŠ ŒyÉdZ­V­VûšÉÈå´L†›µƘa¹\N’¤8 CdtÔ ã& J¢T€^@9N‡ÃÁÐ2­V ¢ƒN§8p x<žï¾ûÎét"„222ÆŒãó¹±WQQqþ\VCCC€F#—ËÅ&ˆëÐärùõvßÈã†'ÓNdmú&wؼ%®r÷«-;鎻wãªabôO£W@ÎZkë5W”œBí?’Ú @CüÂKÓuˆµV˜CDt”ì "šd/ò¯7ü@|ø¡Ý§_9z(šD—ÿ"bey—²’EbܰyóG•Õ+cÔhzýžŸ>Ú€A5r\µ¶!ˆ‚âªsEŽ›B4ˆ·ääÙ‚b)(ôÓ®€ô'_êñíÃ+ö¾½îæÞ÷&^º†‚ùûM}3[9hɆ—Gãû޽){Ó×¹%5Eõh}íâ*¶½ôÚ¶¼°i¯nœß?0÷å)ìm±2Pœ«ê×ÎØãÀÑ#.ŸM—ž;¹ÿ‹¶dnXuèæ^ãƒ$Õ%ñ““#Aâ¯eñâÅÿà™ÃR“øgtùËE/@˲fsuccÃÐ5M2äy¾¡¡A.Wú|G 7ß|sYY™L&C < @â6WŒ›C´„ÄÅ`—K©T"Œ ’x¶¸°hÇŽÇŽ+.-±X,¼ tíÚ5***22Rt”%&v6lÄñã'‹ŠŠD},Î*lYu·Û_pñܹs'55µK—.b•8§„1v¹\åååbs8Ž«««syœzd°€ÀÐ"òfhhè¤)“‡š’œ%z¨A(++;}òÔÙ³gÆ+§e$‚ Ãôïß¿ªªêÂ… ™™™¢ÔìÙ³gpp0Bc;Û¶m[¿nƒÅb Ф¥¥ >ùs×Q¯ IDAT!­xÓœû榪`_»ƾþÐåË>ùvÉ»ÃûG츘›]KNhQÛéþìLEMíM÷VlùòÍ õ]FÝ÷È©*éi'!!!!!!!ñqU›GEEÜu×]sî˜&÷ëêê>ûì3Kmý­·ÞÚµkWß\‚………999IIIá¡c‚ @„œœ Ï-y~ÇŽçMÓ&“I¯×k4:F ÙÙÙUJ¥RÆ0r¹<(Ø”‘1rÖ¬Yñ;‘ ;®Ÿoxåµ—‹‹‹ÅLúôéûæ›o4!QኪÊ÷ßÿãÕŸTWWBBBfÝ~ǽ÷Þ $°Àff^xî™gwìØ)ðÀƒÓþû×S{ö¢( a!TPP´téÒ/¿üÒår.\¸ðþÅš‚8N  Ân·}þùÆ÷Þ~'/?GdllìÂE gÏžm0H„HD`ÌÛ¬Ö•+W.ë‹Å’¸téó'N”Éd<ÏRÅržÌÌÌ'Oææææääåååy½MÓ öššŽã µSëÕZW½PBâo„½ð%''Gz /ñ×2f̘öôB©‹IHüºü•i`Œ«ªªöïß?hð€àà`’$€çy‹Å²oßÏÁÁÁF£QÜK”ÑÑÑFC \.ƒõ5í“K¢³«eÎ,Ë–——WUU%î¦V« Ô35522’a} ¡{÷ÄØØXßVW ùy‹Å·í²¨èÄo â|¿£G6y±\SSsêı’’ñ!!&‚ € ¡Im!@âNÇ<Ï‹áã q=Uuuuyy¹E#  S|œR Ƙ Hak]Ý/§N‹À—••<~|ܸqc |8Ã0A D$©V‹+¬âA )81 <šÄÆ$A††‡ÅÅÅaŒÍf³J¥êÛ·ïĉ“u: B&“Q$€ó€Ûí¬­­eY0Æ”Œ$šÃ!D:®úz›  ð<_VVV^Zʦ¦2 #FS¤(Ys4EV«µª²ÚË $ `Œq}}½Åbáx"iJ¡PˆA‰æ…^ ¶ÊŠ2¯ÇåÛ5ZàùÚ³³±‘ H²)t#A„„„F’$-ó{÷¤ôè>hÐ e€ „0&Tu€6$$,11!óB]]mß¾}~øá‡äää8.Œýì°,!!!q]1f̘ÿ%ù;ï¼#¾Ôÿóù;–þ×ò÷j»¯¶Rûgßáÿ€.yÝ6AêòבèòÁ0ÌСCo¹å–ÄÄDq!Ïó.—Ël6[­Ö“'O~õÕW!!!)))âWA4{´’D€1€ÐØØ(:mDÑ¥`ä½zõㄾ}ûÊäLuuµR®ˆ ‹`†ãy—«ÑÑè P+ :=BXÀX¡P„†+T. EÓ:N¡Pø„¢è°j%NŒ& I!‚@À <#ShÔ:’$YžãÎWW› Ýn·œ–BA £ÑHÑ4Çq§ªªÊårÉd2„„0Â˲.—K äH„ `Œy–ey–}hb5ÄÝÆhF†1fYöøñãŸ|ò‰ÇãéÙ+U&“y<’DjµZ©T’4%—Ë& <<Üh4öèÑcðàÁ›6mÚ½{·ÝîÆs×?S§L¹®òù;–þϸ‚×óúÛ]åÀmyÝ6Aêò½èÕˆÁ`ÈÈ3|øÈ€ Æ Æàyìtºl6ûîÝ{u:ƒR©Œ§iF”U‚  š¢ü¶6Ô[,fžçD)Òµk×ùóç;Æd2!’à8Žg¹‹/~ÿýw,Ë:Ý®ÊÊòúz[¿~iÇ3™L`¬Õ©“ºw 2Ö[j1 „Œ°€1/¢#ˆÐiôFCc ¦(*",<,,Œ Ìs˜ F­T*ňXQA0Æ€.¶×jµ^¸pÁl6$ˆ±ò ‚P(„0À‚€ T %M“!,¨i¿2¾®ÎZWkµ¨µÁöÝ÷;KÊJ $—Ë,‹\.ïÒ¥Kÿþý»víÊ0 B‚X¨\.ŠŠ t»ÝÙÙÙ.\àyi†¡„„„„„„„„„Ä?HtÆ800044T©T€¸²HÜ}Ë7ù°²²ò믿6™ŒóæÍ3›ö™mž¼‚à8¯L&S*•Æ@’dXXXjjjHHI’<d2YEùË/¿üöÛoy̹\.ÇsþüYžå&ÜxƒF£Æ€I’ Òëõ!Œã¸ÒÒÒ¬¬¬¸¸8µZ+F¥ ëÝ»÷Ù³¿ÔÔš ……… 2¤SL,Eˆ; ˜ ”JyPPL΀ ‚° R©4 EQ¾  r¹\¯×3 ãõzÝnwVVViqqLL A€ z½>""B.—»]nѵÆÈéˆÈ0½N‡÷’Fy<žÊÊJ‹Å‚@£Óqüä‰ìœ<Š¢N;EQ!¦à#GŽŒ1¢K—.ÑÑÑ&“‰¦IÑà*•ªK—.‘‘‘ÙÙÙi”øV¸3ߘýoÓ«n¦¯6‰`=úî;¥“žœÓœ„-X=÷9ïs/ê"óóÑ?|Å‹ž²=¾êÎNôóÿMÉ».n{ëµO~Ì©c)CÂèOräØùóY‚À!Œ¡ òó/æ:Ã45A^¯ÛZWoµZ1ÆbèÅÂÂ⯿þ&,,bàÀÁ à &cÐäÉ“½^÷™3g(ŠêÓ§Ï&À€°à“…€±:ÅÄ&uë®”+@âtA2Š7n8¾¶¶¶±±‘jŽš2êéÏ–%:v?µxùϽ^e¸¤l™[ßxaÅ>KÚX:nÞÚŸçvç||ß+‹Æ…‘> XÐ6Ÿ´Ì¶e=Ö½yçfwÉ/®¡oì|±Yû`ÛŽO…O[ýé⦅x,ðs+ÛàǶì› à>ûÚmoE½ýá­Ô5·¶ƒŸÆ¶0š„„„„„„„Äõ'º|¸\.§ÓÉqEQ¢ã8–e/wy>++kïÞ½];w !-ü2¢ãHÜ_Ëÿˆ™çÅ€„E±,–Ëeáá¡ÉÉÉ©½R†®ÓéÄ}™‡m ŽÆÆæ`¸±±ñðáÃññ]:wîE¥ôH ¬¬¬ÄGFF›Œ´L#Œ1B@@ÑD³è…‚éÕ«WËØ÷âJ-q·®¦a /8NžçE; …B1tèÐêêê]»wšÍfƒÁ0dÈ¡CÓU*¥¯Õ^¯·²²2;'Çét"„dr&µwï#FtKJ ШÕêÒâ¢];wþôã>³ÙœŸŸ_RR‚™žž-NSD9·Û-V麽“°»pëë/~Þêqº5}ï}ýÉq¦ºÿóܪV^ c&?v§výæÜ‹žç^‹}ëéa— ;Ž==û¿lŒ–Ç«²pÉ]}õ|ù÷o<ÿÁ ›Üoªº·/î±”“+šó"%ÿ“ßÞ[Ι±ø™ù} í“[÷.ºå«›6®ÈÐ5Gaq7¸ic¨AF¡ŠŸøØ“†J5j<òØíÛ¦}öJ__ñÅ=OX]}{ÝÒ¹ŸR±Tµ¹¾NHZøúc#M¤ÿV4ylÁÕ¦ÅTÄÝß=œDE%™<ßÚÜ5çŽóýþÕE…HÕÀ ‘nËvŽЬlJvnÎMZü˜ó??µ*’-ÙüæÞ¸û?L @Í |»Û‰6ù4h[ç³Æ°ºsöÂġë;û©Ç§vnW1îî ¿¶½üÝtUI3·¶Ws¢c£IHHHHHü½At /+­5ù§Š.§Ói·Û/†0Áb±ÔÖÖò<ïÛ´×\]óãÞÒz÷;v¬Ù¢)F<Ñ´¥o ׈ңf¯ ’¤###o¸á†aìv{DDXZZïÁƒGEEiµZš¦'Œ ž”!FNËF‰Ó‘ÅRìøñKJÂ#Ã!É阘ØÈÈh ÎË ^ž I@€' ÔëµUUˆ¢dz½> !ZDnÁ×F›Ívþì¹~i}cccI’eDFFΚ5«ß€¾V«U«ÕÆÄÄ„›H’&‚Íf;vìXNNÇqã`SèŒ3oºé&AOI’®”äÎqq‘ᇶÙlƒ¡ÿþâ¬K±2.—3//¯¤¤D r½ê.ì8¿y¯lî‡éÝçþ3ÿïˆÿimfÿW>Ÿí<¹jÅY<åÞi[kØgŸ¸‚â5¸£!ôö÷ìÉT|qÿ£ž_5¿òÍÍ“ßß4>°â‹ûî,òS\å¨ÙMù¥Ž>ÿnÉ o}>ʄ˿~äá·¯ºÏÖ&9 Í >N Ð\úË:ݼxèSK¦N1öè“ÖwàðŒô^TcžŸºÙj ·¬]’Jf¿3ÿõeés®~†e‡i UìÀ!Ø™óåšâ·'2¶£Ž€-@댔Õì  Értì-Ï? Ž£§[_ÛñÕ_Ó7¿—ªFÐÔ@¾ ¢M>nÅÀ6e)}ÖàÊùð>“ç?zS¬uïK÷=ûß„µto{r+•W¾yAÆæ¦Šä'>{gŒ¿ƒüÙö²†º:ƒ´µg«èØhòÐ@®ë3:L㱞Ü[aãèð˜ôÞJ¶¤èà/Náê3b´}Æ„k[Üç^kCÑùª²º?c…ëïÖŠÿ½öê#?YœWj3 0öaRÕ—8`ó^k&YžµÛKÎU×þ/–'4=âÒb©Ú£¹gÔ¿¹Jíì© Œ×êi@p¹kËj r< U`¿‘Á*kÅÁVOGæ%äq#:Å(=÷Ù1Ъƒià+Šžtp€âúu‘5žÏ;^píƒük¹÷~ï.yUÖø“¦À¸½IG€Ù†ÆŠÜš‚ pyë–ÝÅè8ýC%G_‹U¯p-¤.ÿ—‹.Œ1Ïó^¯×·–øŸúúúÚÚZqÿ(ÑC%BVVÖÖ­[ãââ’’’D%&®×¿õ9‘ “3^ŽszÜrF.JC`àÌ™3 äv»õzmhh¨F€1¦) „¼³(HdÐé)‚`ñ>@‚ d_¸°ÿÀO]ºÆÆæð‰ˆ$ Ç[o©+.,  h @r†ŽŒ ºpሻr566êtQ+Šq2šnS„ ¶¶ö«¯¾âyþÖ[oMHH`Z<j4aÌ‹I€8ïQ›Í~äÈ‘mß~[ZV Ía •Œ\¥R1 #F¸èÕ«WHHÈØ±c‡F£‰ [Áó|qqñþýûKKK¯cÅ( Ïƒ/©øò“â‚óGk]C¼dPjª÷±ïΖž>bÞôÎLõñö© e‹0mNöÖU…L}Òd;N燎B#9|Bç]Õ~ŠÃÐÃÒStè—’3x—ËXPq1§MrB¦hýdÐ÷¿wå7·—œ?qüØ‘-O®ùrê»oOõ×RELÿÎ*B`´Æ[ðÛÆ]—O+8²7>õÌáÞO/ªGšo¤æ›ðJy µ‡¾ÈêrË£!dÓoqP˜À þóiYÐl¤…ÿNÕЙé¿r¬’íK·=ùtûé…~ø±íœxæŠMº&ƒü£Iüm`ëÊœ^„d*•A§í2˜¡~**l$»üI–D«UZM\?äÜ[Vó{Œ[1ï­«lô8½¼dãkýÙUvŠêÛ]Iv[õ^¤ Tš:G™O6Û¯&Ác±ð1J&PGÛyB­ÑÓâ°FE:l©1ÉX‹…ÃÒ…þ]/œ":²oJ‚Ãâð´N¯Žî£R¾x¶¬­¸me=’1†ÊHÖñ[Uêò‘èE… N§Óãñˆ“Eia4ƒƒƒ³²²8ŽóÜØØ¸ÿþ=z †ÐÐЦ¨¸i$f‚1Æ€x•ääåGFÇ´ŒD€ HS°Éh4bŒÂn·» ¿Ðb±$&$è4zÎÅq,ëu¸(D©äªfñ†Œm¶úï¶ï0èô#CCÃI’¡¾¾þܯg?^g®›>}z°)œ  AÀ`‚¦Š’ØívÿòËÙ_~9,“ÉÄæx<¯×+î˜,êÏ‚‚‚7Z,–É“'ÐÏh4’$ È(€#›Íf‡Ã¡R©<öØ‘£6m<~ü(ëñÆB¹ê§Ÿìܹs÷”dµRMRf&:::22ó‚¸|Žã8‡Ãír¹²³³wíÚµÿ~—Ë-\pׂyïsw®†1S‡¦ŽëÆä¼ëèùÀÚ/ÆŸ8pàÀÎ¥s¿›óÁCíß²ieîJgsÿãÍNYŒ’À‚¯“ò @èÒR6‚$oÞ»´uq—Ì‚:îö·Vͧ{êk]JåÅWÚ$o_³oÅZçÔÆGõÕsĤŒO<õ]áiÐ$ 1çm~×C$ò3¤gË÷_ÜuÜÀ`@"àêÓ€PüýG–å yþ­9Éj8m¸ÊYÕÀC ¬µ–Ó©.ï±l¿î*ërsÏ6óö(ù´)ë’êNmÛO›ØSG JN¡O¾züÛö–6ºìw0õ[&ñ× }G…(ëêʹ€0“Œôº+2ËrK;þ1ö8 ~_l#uB\ß.òènª²cv)Ô±=‚"Bä4î:[ѹêŠ*jh|¼ÊþëîÒZމ×IÅ•íÏϱ‚>µs¯H¡üD­:-Tm­-rª#ÃJ`-¹å™yÎßú2Éõ}FûɇSû0)ê¬uŒÎ¨òÿ\PBz©IìuWçVæºXLD‡&&hÔ àÕ–ì3µõ^ 5ºÎɦPEאַÏùÕ\/F2!)±Q šgksË2ó\øÉÁÚô:DÝ7(}Újµfgg÷èÑC«ÕºÝîÜÜÜý?ýœ——=räHAàDiD B0Õ¼XK¬~~þÎ;M&S\\Ã0N§óܹsÙÙÙ>U)6°¢¢bëÖ­EEEÕÕ3ÒÒÒF1&I¤ÕjU*unnî·ß~›——ÎqÜ©'333íâ+'Œ8Î={ö¸]ÞqãÆuë–¤×kÃ#Bår9A,Ë" ^¯·ªª*++«²²²¼¼üÌ™3§N2›ÍMãPẽí¼åÇs5ã—/˜…K6¯¯pwÅžì÷½øüÛ3æ¦u‡Ì§Šm‚8wËiìÑ‹úôÓm–Þ” ræ~·þ(ÙsJð–S»~µ¥ô£rväz>Ó9,¦jçÊaƒÌ‡wå»zø)Açf±,ª_lÍ'Û³'Ü•„²>^ülõýŸÞ7¸MrÁe®j  R4= Hu ÷ðò·ÂÃ’lD–s?o0M”É5¸æ¢…íKW;Pêíy9 pÕ?}úAQ·^·È33mÚ :ú7¤oÑÆ'_/¼áO#&R¦Þihéîì;Ⓡ·GˆW\á”/7 ê¢jÖ\Í lŸO»²|'¢†Ã¯³'­˜ç:±å“¾,DhòoÆ¿méßß ¿Ùh-„A¯+¬Ê:M†&™ÂR£ùÆüÜú+êÜXjuv1)u ÒΊ΃"#•¸±¢®ÂË„ÄèÒåÄÅæ o|‚2XCXœJ£ (Cì„ÉD‚§¡ªÔHgЛ+3OÒ‘=M !aå…ÅŽky¥Õ>Ÿ’¦Öi•ue5¬ëR˜4‚«ü‚] ŒìM³ùçlúä­ÂÑPë"ƒÑÁ¦„hû±&qP˜‰fk/ÖØešèHcÏ^ÞÃâôY¥.„®-ºà Iл…†V–“~r¨€½ÑV[MwÖ‡õŒæíyy®VÚ×ç×ÒË.×DˆÖè€YûÏÁÜ5xmR“àjóÌ6JKvÊ_ZMR?S ÉÕæYl”:&N É­æç^«%[jŠj›=Q¬»,§!¦¿F¡”‰¢ êøÈäZ¨­:uÚêjgxÎÚÐ*^¥ 8c0 ¼ýb6‘¢2RÅd€†l±7r@h:¼Ù|÷yu#èü_Ä?ûBwÔ%ÛXÃMëýž\ì•ÿ¡OR£ÑSõ–2« ~ÇVWÜÑ\ùÖO*Ö5¿‚®+°†&딂»,ÇRçAA¾ö2šöÏ#™´ŸKVzõF”ºüŸîég¸y½ÞÇcŒû÷ï¯R©(ŠjllÐPWWwòø‰Ó§O³,‹‰^;—iUUUÄÄÄP”Œ ÑUzîܯûöí«©©Q*•‚ 8ß’01ì ÂPWkù~çŽÌóg£¢bBCC{ôHêܹ3I’‹Åév¸\®¼œü'N˜Íf§ÓÙÐÐ †¹oOÁêÛF®¯|È”«¦N |öÁÙûŒú¤„8¢Òq7ÝýìC7o¡)Bßÿžº…V¦à‡î¹WóÑû·5Eu—u¾céœ/{ôÆ÷¤‰5ïåù ˆ¦«Ö-¾íM'ÕiòÓÏ$(Ô½÷ìÒÅ3×SúØ(= ïÚ¶8Õ½)ÿw{"ë…¥·M〠ûØ }4¾MrÀ ‡ž›×*†¢Ç¢e Þù÷Ò™+,,(ÂûÝô¯gÆp3g>qç”-†¸~©‘òËÚCÑýŽÛ#»{ÔjÞØgî+C *òêÓº2×~v¾Æq~Îè·ä½žÛôöˆÛž³ôÅÛ¦º@“v×Ë#ƒ®àèr”—AäxŸ+êRãÚäã9¿²mYoõ9Õtò=üËsݼÓ½ïXz[,—ùB»ŠÒ£K޽˷€¶ò“ ~®»ßm‰?À ²¸ßd4‰¿è ½ïØi¹Yo ^P é£2EÊ/Ö»®ÂuÊr@‘Ú¡®¢ôôÉFP¹#v`’"¦SYÔàJ0¦V­ÀÊ`9SÃ`Kl<à®ÏͱÙ‡ ¡´JฦV´ÏÇ#·fž©jà‘ªs' {fYN %¬ztx`luÀ|l·M†9ŽbôŒ›”’”êƒhð—ŸËt „µ®FI¹\¨¼Ö¬35VŽpè4=Cé„:?94ÝñNó¹3æFj<òA) S„²²^‚3º>ãu—l_k.µ2ÞOL­_Ÿ©¹p IDATk Z¡7À–”¿àä‘Í­î’dìÀ¦íÒRr¶¸ìü'V§²K`©5Ýe$`]lËÉ‚‡eH𢠠7%ë Ä6œ=YçðçGÆW­4*•Né×6ÔšQ¨4¡*R2öJ€:¾Ùšîs@rýÕv«?æB_®KŠí[[Ñܱš?ö©BÈh €÷p—›µyɪͽó e Ž$’uUØìô¥^Iù{nxØö—ìªÞœJ]þ¯]¾-¹¬Vëž={Ž=J’¤(±‡Óél^=Õ´—˜„㸢¢¢’’‚ är¹R©ƒøÙív1à¡(u [½õСCçÎ 4 €„ªª*›Íæp8š‚p!T{¨– AÀ˜!„@\\ÜM7Ýß).'/ÿСCÙÙÙµµµçõx­"õ¹Ÿkk„î³ÿ½ivÇ×@Õÿõ-ý}- ¼áýÍ7´|äk`@›|ü•å;Ù4ì¡ÿ{èò'7AwZ°iÿ‚v‡»ù;~l+ÞHÉOlùØ‚ý ii"à F“ø³À €|O>D4ÍWoñó²¢ë›w³­ ®jL@Ò$p‘,€F.‹‡V7 rZM£Áì2Fk‚ä ÄÙ/V)ãB5Aa29ðU¥n±tæ9ÜüÒ_[+Úå#Ž'Ü^€VQ Nî<"Ùwß3 Ív RÊpAœuL+HÀ:8 g-o$¾±ñxY0Ïc ¤,¸m¾¡ŒW,šu°((9Ñòõƒÿútø‚‚­«pz8ÖUßXQæôb¤ó—ƒŒðúµÅ ðØ9°àläÀØAÈ×öiå$ðØYAôÈ7rLJ«i4ìå1­¤i¾7CËÀ˳X¼¢Z¢'-ÕþÂ!ÞúZTòÐh¤°W8Y2ÛA«×…Q €·¶žÃ@2ßlM÷ùºÃït¡ÿ§.ÙÚüG?UŽåAAÊ)AÓ%A”Î@¹¬nßU/+6ý>7€dÂ:ºdW@êò…è5ƒo!I’b˜xŽã8ŽC1 #zºhš–Édr¹œ¢(Q°,ëñxX–õz½¾ b*Ÿ–õÏqÖúz›ÕZXP Jª6’‹ÒJ„æ­Ã@ñ(¡3iii³gßU_o.>&îåt¤°*kÐf=E0ù@Æá½xõ\^‹Õ bf ƈ5÷M•o‹ª°æ‹¥¶¶ÚØ\ €1¸®µ…8€&Kg¢梨ZçVöõmSó³ÕU´2¥ÍÖSîB‘+À›g¡iPd›Á—ç,0Z7t½´³•ôóW¸ÃÛjèÿâ’­Qô‚ʘÞñ]…R©(‘L,s«•ÆqunØO¨³Co* UZõ¥¸ªºohsM’6ÙËA.ÿgº˜L¦³³³———«««ÙlÎÍÍU©TÖЭñÇstt”J¥¥'ÅÆ‚‚‚œœœÒ½^Ÿ––f0ÊÏÛXß¾UÛÐ¥ñF¥$/í£ üs‹â×4³Ùl.—Ëçóe¶Ò6íZƬ¬¬;wîÅÅÄÒ4]§Žûvmlì‚ ihÌb±$%%ݺu+33³4’±–[zci(ï:4”½0JúIù·H—np+Ö‚¦+˜Pi5fé XùBK,=+²Êì1ü%3B¢¦]†¬ÒΙãÓÉ[žk´092)À”Ÿc*û…eÚÔoI§`2o.€9;ÍøÂƒ4Ø|¯g#1x\™Œ ´!9Rc€¼ü,ßÉɵIS¥Â̶wç¥KJ4’@k35&/ ƒ¢L½¡Ô”H‚™£R›ˆ·ªÅ‹B­ÿê2•EþöB['JMJäžNLSròõ)=Îäóí}… 3æež™oçìÜV©q‹ ´I/|Y1*J(ùaKZ`éJLîÍ0e§›((]á\u}BÿyýWðU-áNì b…¢€äÉÝjå)6>vØë¢,Ê _îâ@©Š˜†‚®²fЫc¢%Íks]ZúÊ”Z ãÉø|€:?:ÙH³Ú¢‚‚Bm¢¬©·ÔÏ]–hªÂÈ…EŠ@§QèiÐekL¾Vñ£Wu¶·Ñ"oÐÐÿÅ%+ZãQfÕ©Ìïø®BÔq‘RI=®g;_yžÞD°¥R&–ì(µ®t÷¨<(')ŽÀ³®<+,o½çïÙyU4Ù¿îkÈåßyÐe÷;88ôêÕ«[·nnnn"‘ˆ$É¢¢"ëV¥ÒƒàÙl¶ ŸÏg2‹Ÿ¥Y,N§T* ƒ5¢ÐjµaaaüñGxx¸u‘aÉì Yú.{nB¿èç«| £L&Sttttt¬««+pXl.›r¹ÜÛÛW­Vã8.ð¹<6A4EF(òNý~òÏ«—µZ-†a4•Ì$U9§ôœW·,]ùÈ­²u©DÇ‘/Ñúù0ýÒ ˆš0$$óŸ&Gš¼\øb{˜ÕšŒ˜¬¤Âr712ÃÀsóeã&}FDF¢ê%ëi˜R›â‹B•™“^dEÒÆü•f¬këì uÅiƒBõ,7KG©.,0IY¦<¥…6ésŠ@bC)R –WKo¨Å+ehOïµëIÝêò6&gFEètxV¢ÜÅÓÃ¥™«I‘®ÖÐb ‡eRDþi °ut‘Ù¥ËÎyª4`⪇Ü)UIÀ,Y9©”‡/‡0êÓ#2’ ËÅ\/¨Ïâ\µ&ûCÌ¢È;9µÙ:Õw±U¦'==Y¯[Qy?¯nC¹ÌMÆÍS$e1¼qô+X: ÐÄ¥ÜÕȽ}mä€2òS 1… `e­A«ãsrÝ]ìjÛÛe¤Ued}žD0åYßcBjÔJ³Äž šLƒåUííÜ-^¿¡ÿ«K–YÃV¡yAÔõŽï*@kSîéå>µ$r[´I]”œcyEÜHÒ“õ2_®ÜÝF›­¨`½çîJÊ(­Üdlì_€\þm½<èjÒ¤IPPP×®]Åbq锛СK°X,EYÓXÏ=·n³¦Ñét¡¡¡Û¶m»zõjén«ÿZu ¡P8hР Æ0Y`@Ð`@˜)š&Í$I²™ À(ÇIÒ’››wñâÅm[w]ûç¾ú«/º-Ü¿¦Žöò‚  7ÿ¯«EŽ@¼Ù/z…—Ït½‘œ«Å§Ü‚ï ŒçâPÛ_,æa`)Ò¤Fd&çZ½­ª6=üuú#pÉj«rùt™L¦¨¨¨˜˜˜Òã"‚`0P²ºÏºk«|ÐRzà!°ÙlëâC³Ù\ñ€Š·甆LjµæþýûOÿyLŽ10Œ0“ƒÁ`1™ €Íf3™L½^OQUq_2ЏÞ"Ú{ Gm3{Ø´QELø~|s ™qqÝÒí 9v>v ªmH:½våÁp•Qg5Ÿ²vN@Øæã± Æ%ÿóüy^@üžåÁW2ÌÀtí´èëæÒç²—‹”÷v•K<Æ'lÎÄ[Cv/o)ÄtW~³¯ið"ñ©•奱îÍó«…—þ̸ìëÌug:mßÐ]†"ÖÛ^kýϽípS^T®]玞 çø·uËú%\ÑBqŸlñ]->Fð[÷rÝq&Z×µµÀZKεŸ–ì| ")¦G¿KGú›îïªX*ïÖ–6ßÎ6“ܺ#¿_Ø›wgËŠ-7³ÌÀñí;{Ѩ†Œ Çìex2rò” ªî„µsºØQ•U®\J>†º¢&Cë÷þP ; ^ÝSÒ³¦g!C †F žð‘]Öx©ô\Ö+ÖSã­ ­Á•õpˆJÛ½¬á5$ƒrg¾õ™¥rï#-æ0 ± çÆÒ½d¥5Oæ<À÷©U;~¹iFCvæÑi³w„ïü:ký޼~[Žô”e:.€Ö†¿Â³ãp‰áÙO_¯»˜ÕuÔ”A§sÍ‹çu`Ü]º1õóŸvµ£3N};+øþΩ…•²—µ|áß*$Û9½·Ç® Ú-ñè áN=‡G/® m÷l°(sæ^À}X½íï+ºöàÇž{d×k´Ü:yŲ«c—ûûµ¸Ï‡{ë\|ZPè[¨ÌÑ sLÙÇýû÷¿çš+•ÊXú‡¥fé^¾¶ÈÅjŠ}š.YmU@.‚.DMø^¶gð²ç¾`{[³¶%¿,¡C—o·v©d䆣#K>O€Ó»jýä1mS»iåËy>{içµkW)1ð›-;{î… Z­=Ùªì/sî³V‡Éþ¼Šbí»ÎÝݵ¢®õFýxdTUUpè2wg…ÚU.·m´©]PÙ….3·v™Y.A‹Õ'[§ì³õ<¯råR@ 5ièŒL€ø$¡Wföüò¨Ã˜á~è` @ Ä{ºhD¢Zõ'~‹Õ'ËOsUãž/íºáòµÃ‹:ÊÑö(â}`åxæ ÿ8Bì°ïâYŠúaðð– uŸu2Õ\³ìS£+ÿI)U-¡3!©ªß—¯}4 ]³j^ýk‹\þMƒ.À5ÔƒDÀrÒÿßDh¼“qïíaŒÚ4ã˜OÈ_O—÷¿ygs©dŸ]ùOJ©êè\w!©-(’ÞŸ¯}4 ]³j^ýk‹\þß]@¼E0pýØl!ð]Á”ÿÖK0gܸNvÐ@ˆ1œºðŽ<ñ´¨æ˜§FWþ“RªŽÄ¡ øÁ ’¿7_ûhºfÕ¼ú×¹< ºïâ&³i@ÇÛ£ Å~–¼÷«/w&¼ÞD²öÞüsîj_Ò&\ ž4uúô  iÓ—î¾eªð½%çÚú  0düê 馊Y#~›3lÀ€¾Ç­¹’U±^Æ”s«Æìß§ïé¿nÀ¬³r_f$[¾#³Íwë7‡„„¬çqÍÚ˹¥þK«ïmÜœòùÆ#'Žm웹3䎺l{›)ñÀê‹n ÷Ÿ8¹ub׆›Š²ÐÊœzlÙÈmÇNÿ6×ùÒú£É5cÉ1³¶ÇÀñ"&êuˆ Aø+HìßQ e‹®kàúë]ùOJ)äkSC׬šWÿÚ"—!èÈxÄ¿„ãÙgïÔêã·Žð(½H’N¯]y0\eÔDͧ¬ßà 2.­_²ùŽŠmëçoNÍÙ3YóÓ´}÷¯ô¸¿eÅ–›Yfàøö½hTC~ÞµŸ–ì| ")¦G¿ùÓ?kôMÜÂÑûíýlt a¿¥Ó¦¦/>Ñnz.&jýý¦8‡IŠ —(unKîƒûd‹ïjñ1‚ߺ—ëŽ3Ѻ®­­ïâ"sïÜ2užÝΖC£ÉVè¤/=Gâ ŸÈœY§™½ùé§ÿ´h0¼?[{1ò¬ÒÁõQçõ¯k¼†âZK2=‰=x“2LšB3íëÚw¼ŒoÑ?Û‘ð0ÖlÑSœ·>“lmÀ½+þöc3CÊ··è8ãë·Õ&Ÿ¸h7ò;nÊo Ý3’€ÛvóìÞOÄV\ßžZDÓ nýÉ>M}J¬`1ÆUL‰?=x‚beYÚ×1œ_Q~g_tžâc¹q߀ø8¶}'?gR‘6]I‚+LŠl³ÔQPsºu®ü'¥òµ©¡kVÍ«m‘Ë¿‚š?ÓÅ­¿*<41+ôòT7A¾_çrê5{$óèÚS饓E´6üøÖ˜‡ÿqtuã¨ÃÓù×ÖoÏí»õøñKšdJf£È¼«k‚ã;þxèÄÉýó¼®,y¤L=¿/¢åªƒGÿ2Ã;-ô¯¿ÌmZØ2(“:#ÝìRÛ™ËqoW/ÿqfiiƒÃ6>ÙðÍ€¯w»ô«S6wf)ÌÔ œlL±œ¡ÊÓ•Lu™ ’ YpsÅ׃zö¹ìR>‹õR`°½ü©”X ¥Ó&àÚg#iÉ|Hº4·ñ}£ëØåï;úFã9u‡¯öq̦͌M±Ù±L—¡ÁÆm¬åŸEiC“þ.°²¹Ñ—ÓDÚ\ªÄh´&4ñfŠ´op£¯ò’ÿp+¬¼‘‘ÝÈ÷Ë€/ÆðTáKIÏ¥$(]»CH³Q#øÆªå#ï*âÇìâ»2ÚwÄ®¢¦,™þçÛ§¿æ˜§FWþ“R ùÚÇÔÐ5«æÕ¿¶ÈåÿsÐEHê Zò¿Ó®Äg…&f…F=;tpóø%(ÂA°\ûÍbÙÿã¹’@4±¢?uûØžÁ{ïæëµ&}ÒídçÞœ˜˵s?ÿÒWbSBS]úuugÎ÷ïÑMu/Wܨ‘éÐŒ‰ ‚Oç·;È—mí âæ–ÍëÂ2 lXUÆÜ gî>{æÇ†7–…<ÕUüªÊ nš2kâB}×ùcß,ç?W„ç—¬?$••SHÀXnb[àõùÄ;D»´iVbðƒØCo¿v©ÇµiØ0p“Í·kú9Ö¤g¬5ºòŸ”RÈ×>¦†®Y5¯þµE.ÿr^:±<{‡ü1¿»¼œAåî-ŒnY»ðA÷#©dÀO¦Ç ¹ý¯ÎZw¹5 R*ïÊ’q» p`‡F=j³c6(}—†¿hy/ Ž Nßw´çƒÛ·o_X6ælïá’Äûyup ÃX½úÑ Æ4êú \0¸8° 4qwã ÛxóqQÝÎ ðà8%ÙˆG0lœùºl5 ˜Uù[~I`ÁÚK]eÝë‹ êwoÆ^Ÿ "›ò íß3{Î}HHúl=2§nq|Èô”I²IazasïÂø„*W[9û¯WxÔG„Ö5Ç> µe.øŒÄ @9£/1Æ®ã>r£mêEâøƒ7œ‡/´U§‚‰—Èz ùÄ[ExšxÇeà6M§º7½f¨FWþ“Rªz"Ÿ´÷èkMC׬šWÿÚ"—¹y^:¢v±y^w9ÝúßÌŽ>m½ÛÔnðÕÄd‹×k±»~·êÌÓ›‰Y¡‰/ïŸØÍ… P¶ê/tóøe‡Ž<Ë ML=õë·Ó¢›3 IDAT%¥#ÂWe¼º¨Ç7!¿„eýõÇ—ö!üqÇð‰Y¡‰Y¡q1ÇŽü4¨¡ zB_kxŸÓ³àè¹d˜2îÇŠzNúfðgÍ¥é3 $Íñlç™uáïl ˜³ÿ:k(à³Ý[¹¦ÿq=Ý ´6öÒUµoSi–¯§_b5ë=f֌޶Eõ¿ð¿|ÕóÛ)̦õ‚u=³v…$wù¢>¯¤dSÚéuëÎ¥iJþç¬NmAéó2óôîI3ìÎåh-E憞IqïìÅ⯘έS÷oÄëhZŸtçÒÇ¿tS¿Í†ݺõ×­ßK#.ÀxoQáÃPÌÍ›íP¢O [òYo~(J“c2WqX"UøTÇéèÞòs¹»Ø‘KÑ4!kÊUßT‘@æ©Â“©£áÒÆÕE¡h.:Ô"¯ÏÌÿíÙÉÛ¸[—Žclz½ÙZ !©œ²üúÆÉG @¼^6ÓÅôì6²Ê£‹‚BX‡3æÅ]Þ¼üòfëàMÞsÓž½DTêƒßä‹[vèúåv/èÓe[x‰‡£GéÔ:`Ú·ûnÙ¬›ƒ‡^'£ç”Å p¾OËznÌŒð”Ô£·s³/fýÊKo?én!jà Çoä‚!W'ß¶ßÀþ²Å3F]—Kêú{ãYfYçoÇ?\4aÐ/\¹»Å*>è°í:oZô²YÃÁöè±`a™Àu¸ûâ™CN0¸¤å¤êב-waÇÚ™¿‚¶Ð6ƒæÌíàP;àÒößÍxºtÖC$!ª7tÙüºZ}eÉØ“oîæ=b^à²å#êAÔlüÊ.¶8­ú»ø«Úãw[µjÔ -Írÿ|Î÷õ_uŒ"ÎpnÊÔä‹mpA=!ó¸Î·½ùuZ£¼0/»Ap]ÏÊ`vŸÙó7Dýv—ŵØ»ajÅkíÙ:<îÈ”t¦„+ä`Dñ&hãÕ>!þTP¸´ƒw÷zL¶£“tCÔÞ‹Ž3=†ûÚš”'æe7®ëW9%OËŠäW-@ ñÞƒ.¶K]óÓ3Qjšßqßï¿t+žf°Dnÿü³_Ój ™ÛKÿSÐOòhÆ¥çáán^=úûí -’µ{J%uîÃþ òY“†R",“áÿꌺ§¶žIPè¹wÔ”EulTïÝ)`ð¤NVìØÜO$lÚÄ……‚®ÛoêÁÝ¥Q¯Þ”#·¦€ßÈ GG–|1€*|t.»íê õÝÕùÊ™|ÿÕ'[t™¹µËÌrBºÌÝÙ¥|)ßž³Öö|qçµï4s[§òÄ]·^ìjÍZoÔGFUýUÝá«~þúºb¢õ¦õooöÿËë˜H6x  a­± ­á«Þòpê¿Ñ©$»;mˆÈS7­5v2רþ\”e+ÅYÞŹj­[kl¹ÚÙʺ¬’•û»¤`WNÙÈol#ë'Ú£¬$ui@ ˆt•"10‚S¶æŠÁe`nS¯± €Ïw§}W–ÞÆµÜ)Úì–ÜØ¸"ðƒÃÀàµ2f^>²}gé®1&˯ÿOÛûtn /;N¡ÜDu¸8ä. º Ã1Aƒ‹:ËjØø>xؽTrê’OQ“îýùŽå×9ÜuVÄ[!$$bÞŽ¥Rùµø€¥ðüduÿt\¬ÆµòGÐ-«­ Èå«EÐeʌ΄ÖîÀhлŽèÏûW‡´õ"ì¾8zeƒ’5Ã:€Nþå»=eGÇѦŒCeY4i*÷Vê7ÈœúãÛ; íò‘Ã×Í-',è+EW# ì:ÍÞÖ©æÖ:ŠFˆ7ÁÏϯZÉ©‰¥-ˆ¨Î TãZù#è–ÕVäòï—M;˜þ< ´bóÌÖž¢J‰)Udx€s=^ÒÕÓž9õ癳3¸N¶¬—Ï@½iFBÞ¬¹˜B—N Þº÷â­D=j9@ Q#xéòBsÊÞ)!mþj-´™½îêl³˜eßž\¥ÇÚ®üæ?ì{:-5.“ûºÊÙÛ»ž¸¤x™à7ÌH&$¯ÕzýÑõa*ÛœQË!@ ˆÁ+6Øè£n;åû=w¢²L†>79ìüñÿýðg†À’}ü›/¿YsùQª–°uó¯í*Ѧ=8~òFÖ«^àõ†‹þ žò0›I@C_òþ¶ƒÙ¨å>,†ˆuƒGïK1¿Ë2ÈÌ£FïL,+C{oþ€9wµTqSìæ‘Nd‘ÿE†úöœ! îißq)@ ˆêBËùhšÆ0tBEÍàí6VÙs+“Ÿ› «×¬™?w®5èõ£Ýº_º3ßeÐ5yAáÜã¼J^‡lÈË*:Úr>àY”¡ GË·—ý‡:£ƒÇnp^¿eñKA”C$«U*øaÅŠK—.!ƒ¼]‘œØµ‘‹!‡Ë3P³!Þæìë[Vl¹™eŽoßÙ‹F5´!3.­_²ùŽŠmëçoNÍÙ3»VAå+µõ÷v-¾’a¦k· E_7—’×-Ýþ cçc§ ê•+Aÿô§i'únxfê^†'#'O© êNX;§‹]IðB’N¯]y0\eÔDͧ¬ßÃNqí§%;¨HŠéÑoÁÒ‘uø%ªáÉêñ{$mÒc <'­Ÿ×^¢‹<²jí©8=M²|‡,^4´WwjíšcÑz–SëNÄÝôa;fšWL8øËç×­;g¤-¼&–u²Ãw·,Ùò@M1ï¡ß/èk›PY”!þ÷W|ZÄõíÜ$›ôí,6=üaäFUBl2Ö~l;õ­{ñ‰é¢¡V÷?\>áXà–QWª¡ Å`ˆOä—/&&ö#&((=þø8—ÈU‘«"PÐ…xÏPyW×ÇwüùÐ 7˳í¿©»gbÁúí¹}·ïm›urÆ7QB ò¯Uº…oؘúùÏ»Úѧ¾|çÔÂõ;òúm9ÒS–ytê¸äªÊ" s¥Ãö}߈ˆùzí…ôö_O·ÑÚðãWXcvn#1<ûéëuÓ;tº±/¢åªƒcÜua;7?Ê0Ö©UüÞKAtLf^«6+—O nNÿòr¢¡µÓŸùÌÞ³².7ÿÜ”ñ‡¢ûÌ´ü¼ø¬ãâ]Kk›ÿ^1r!oÒ,çtº¨¾äÙO‹Ï:,Ú¾¸>;çôŒi[5[â~ó×ûµíšâ£¹¾bÉ©Èúž'*ŠšÍØºô”lî/Çêé¯Ì±Ên®+‘w6¾Ú~7Ï+~Õ¹þò¿Ÿlî7ü÷TS/ã³tQg]å¶—ñPGC|2 µäª ºˆJRBS]ú-ugÆöïÑM´à^blV²sïNL síÜÏÿø0&Ý®tÅü÷“ÔÇQSGîR¯—'f&Ĥ8öhãÀÄ0×N½|/åTQÆõhéËÇ€’¹‹L‰:ªôº éŒ»·íII ¿›¯og"l52Í™11®cûöÇö-{Óœ1íIQƒ±Cê0sb¢Jä!cò–ýÚ_þkïÆ+ú¼RÙÍ…E¶Üwýk>†Q~M<\¥>BcJ¸ÎµwÚÉ?<Çî­ÇǤþµX34Ð0 ­hלéšߌ™·±Ó”EVe ßÇ~ÔŽzg{û9ºa^|CÚSMƒ1ƒê ?5ƒÛ~BWg¦99A!®eÇÔ¥„ë\G‰²Wª!êe@ 5´^ ñ ñr{Ä0¼xãYå+4Åôþò烇=|èàÞÝkº2J6–áÄ Þ~Pi7•weɘ¥ç3pÇF=wuf€ áô}G×ÝJœ}aÙ˜Ù”MaÎ~–%ká/ÄÖ¥„kœëÛjÿZ´9ZT¯M÷>½sÙž¶út•ÀÛ‘‹€1#¼Ð¹‘–ñ$GVO£ø8q1 4É ¹#[S(°áÈOCeV™w&úf%Qö†´Ž·› µ©±÷vtö?Y²æ~B Œé.­<8úÔgjçztÆ“YC{U¥Ú£  @ ⣠ºhD ¡Út$Ž{+×ô?®§›ÖÆ^ºªömêY«gÖ…¿³-`ÎþëL¬¶çsWÜZxæž9­¥i]ä/A~~Jº·õȾp;Ë æìÐKñoôB6SÆýXQÏIß þ¬¹4ýq¦¤Ñ[¾ž~‰Õ¬÷˜Y3zÛæ¥¨ŠOǤµÉ‘zw?9À”ù$[ÖØ™™Äk?¨W›Wýß§bm\ùbwYѳ¨’ÖÇÜzƒpg&ÄÒÞ~î®â¢¨8”*lï¾ÜÖƒý ŽÌœ¼3†ðê8vÖçô'O+‰âòl…E± *Š*úçÀöG‚F®,MR”ÑÝ_ÆK~d ·Ž;SÖ?YÒWVaB,ííÃL­TCº[!@ÔPªX^ˆŽ.D¼ÆÄ]#ºì²v‡þ[~›:oZô²YÃÁöè±`a‘˜üvüÃEý•»ÛñX,‚w®t—v˜3/ò‡e#Y€pþlÎMERrö”–}q€!ñt“¼ÑìÛo`Ù⣮Ë%uý½ñ¬ð0Ü}ñÌ!'˜ \ÒrÒµØÅuÏxš#kèÌ Ô ±”wW ÇýóNs¾êäæë#rx,BÜjÊ——ç3øˆ“‡$—ç_ONf<)°kéêÐnÆØ»Ë§}uˆ‰ ê\Ô@Ìt™Òî‡U_Žd²1nݱ‹?«VQ”¨Ù¸A§Œ~Ò/Ài[§–˜Ìx’+oèÌÐ¥>S;÷³gUeò'³d<)°k)ϯ\Cu9Ä'Ë'~z[HHˆuÛL͵C© äªÈU‘«~² ø QÆëÿP…ŽœÖtÞÞPß]=ñP£õmRU¼²ásyu]àJë¢NIn>¢‡‘{qnÐýÁ»· w©™ #ãk111åøöïÿÉZ£C§N¥#¹j‡RJy"rUäªÈU?5ÐAˆw.pqÈ]4t†c‚#u–1˜T¥+ÕxK!Æ’Ød­ývÄ/ òvÓ–5E@ âm]“'o3u8ÎàñDÁÁ£‘™ÿ®Óìm*\zþJ5v ‡n ötC͈@ xÛA×äÉÛãâî_¼¸½¨¨€Á`98x¶ôðáeÈR@ ñ_ƒ®É“·Ý¼y "ñthY—ããÎÞï×ïÛÓ§×#c!@ Ä ºââD$Ý‚ÅA|xšéÊ{÷~Ÿ?þtQCí¯êÏ»­AöüÈY½z52øà`ZY^2þ‚«£pûVdϽ‰‚V†Å=Ê´ †@®Š\ñƒ®‰7_º´:| À*¹Ú`.ä JIOBÆBX€C ˆ:ŽcØÖ³cl ‹€’ ΔHp}A 8«ìs…¼,‰˜ Ô…j3àL¶Ì™'÷wñΫ4,{É@Ž%µÁ€Òçk*¦/_zõ·˜óSU8Ç^,eƒ9_•§ ÌJõ/rU䪈wtZµºš•‹¸¬´ ߬V+^£W±=ûM^1¿w+WrîÜ6ùå4Ç/Î…NõU=¹šàÚ±‰ŒL¾¼á™çèIT÷÷Nµ+LM8Ž]¾h`GF©"Nîš3ïT”–Fmƒ@ ˆçaÚÚù9à¦Ì|µ­\Îãqpµ™À˜)@«-2Óå?WÈÌâHùMÂ?Y 3¶¬Z l\6†KÚ¶Sé¹9\©«Äw=E%³ó­%–ð0 Íê´üèU‘™ÆØ\1ÀL¸¶®`àtêø°Ì4U¡DŒyØûùÚˆ8mÔg„§Çe˜ÁƾU[¥Ê&ùv2&nÐÄ?)d{Ù:Û±£6þnZJ!Û±®£§+K˜Mù Y‘qÚwaªPóc{vKÙæôgY‰E4`lÏε»ðu—’³€±…õ:»ÚáE‘L^-e]Q¾…'´N“ð0#UIÒU©I½‰ äªÈU‘«~ŠAŽ3 &ä?—$03Æ`T|M-Ñ|WLh¥„ÜF÷oìTp{ýô[XÏ)³Æ.ÝgIû|/ðªëG5~5¶u‡Ù‹Ù»¶?%7 ùWsº3ä·½£$—CÆNçöš¿eÒw!‰?ßdFSm BF@ ÕãW‹ëÑ@Ì2> WqZÈå6>ŠŒ¸PÈ µzªÂçòà>(Òh,Æ v2/9 Ï/¢Ùö\&¸È¥Yªäg…j{×fõx˜A›¥!ÅR7ÇF„åÎÃ"ZÈç7fæÅ*Ä>ž¢Z 7òe%â‚ZnMý¹–|ElŒEäkçÒÄU¯Jʱáq@ÀÐ=ʈѻÔqø6g¤=Í W9Ôâ»y°ÓŸxþnu<ELF¼–!óµsòµwÍNzv°‚YǵfJOÐæ" |&ŸžövLÐDå)ÙŽ`áêð´T–¸NC±Oc™âz>íó¼šÉJ×V\¹*rÕO3è$NNµr.'Á€†%WI€_GÙ88xW̘içÅ$#`|Ÿ!cZJXž=Ú;$ìØ¼ýh*öȶ_÷ñ^Ÿµ÷8¨ˆ?·1øˆâqƒ‘­ÛXî ÞôØŽî:`‘§£Ø»Vè´§{±hN@AW5"$$ñò°½?ñ^Àø^Žn<2÷AvkH°a‰xx¶‘À¸r.@k¢ã—}®+ã1@âÔ®·SÉEZ•©Å$v,ÐÅ¥<ˆÒSL~½n< q§¦ji`8¶îö6B†Ö"çâ@+Ÿ¦E¥ši\ÏqðrðùŒ¼´D–ÀÛ—‹™ Ãd+ÌÀ4 šòlÅL- €V>ËLɦ„¶4hc3âÒLo³Ž¤cr œ+f³ Õ‰·¢£Išp}ûv(×  H£-^©Dë•f°g ¸8føx³À¨ŠM2q°hå?™ÉÙf³¤{‹}„<>Ÿo_…š Í몀@®Š\¹ê§t6liü¹°¢i0 @À€-àëÛÌÖ¶bC‰ÇÖm±¤Ñ¢8è*ñ‰âþB?¿@&-¥KPI `@êÖã~˶n ¤ yh‚³ºA2øÀã8žØß”w°÷·¦À„| ”!”3Ì •….ÿ¹âL`Ë}¶Ja¤iвMêÜ¢|•…ƹ!`HKÖS8—'b¨•yÖçʆc€a!”3ôÙ¹Öß0 Ç(’¤ËJÄy\!@Ø4êaSnÐÄ‹ }vž…Æ À”—m¢çËYd‘ÒL­ŒÌLc;¸ÚËýìå~´1óajt¦ùØ¡X GÎcèó % šhc¡‘GÀÉí¤­xš«"n"À˜¯´Ð€ã,ëÂ.¯ 5iRõz* «"WE®úé]pøð²~ý¾½wï÷¼¡©ž ³`<ÒÆ×·Y«V6løòU¢LÉÿÊž:Øg”‰០õȼt;ÙðŠ\çofLê6v☂“wsXžm»¶/Ü—¤|NÍ•¹ðìU* «"WE®úI‚! ÊœÐU;\LL AS¦LA3]ˆJ$iíè Ä[$&&ÆÏϯôÏÀÀÀýû²ÖèЩ“Õïà ^­Î®Sò­ÄUò‚Z,ûïØˆê¿«PÚ èŽ\¹*rÕOî‰2â °»ˆînëJYãuc$¯÷ lùlü»£† ;Í‚7”gŽçõ›ÿ;««Ï0ÅðúNÃ֟ѺÝöØ_Y ¥`®ZÎøb­Þ›ùoênNâöêÎK,ý›iþù/uOé¿´DÑ ÑçGôG‡r‡Ñ_ØZ\OÍ­*ªýrþ£RħC—>u>A­¯þù!ìÀòhïïQîo‡6þoOrUäªÈUQÐUÌäÉÛŒFŽ3xû‚Ú}„©êb”`1'X¬>E ÛÒм·W¯™ö€_ed!ãA< ýÓßLgã‘ šÆÜjdÑÿ®ñÉŽiÞ&8Ç«½§§3æç…?ÈWY×91nr³*ìJf!Ú¿‰@®Š\ñƒ®É“·ÇÅÝ¿xq»õ ïaÖ>¼ Y ´Cƒ÷&ö=¥ñ3)€žqì=àÊü@0b¿áÂ6Ë㟅ËÏàf>Ú'Qû ¸›Ô“Üñýl~ ,<;™ÊÙ'š¬Ò"Y/-F÷€?b¿áÌ2lÝ4Þ_  YÔ+uâíÜÈh˜>Ÿüu¥%ìGáš‹¸ ÎpÍÍÄmaŸM˜ …aìW”&8Ú£po_ ×3¾ïÏ«³G=ÄñÅ%™ñ ÿ{¨þê?X8VµõÄ¡‚q£Díõ;×Ä/gJaÛGð$ì>ÓîXnr¤*Ëä°¾¯J©]3aËô’ëk‹&Ðhí/xP†ÄQ‰•ïš§gÑ@®ŠxÛA×äÉÛnÞ<‘x º4ȇ¬Ëñqgï÷ë÷íéÓ둱@ÈÍ#êðŽÝÅ»N韱ïØ&¹Ð˜òš`e¢ñ×+FGš80Z¸â¾ú«fØºÇØx q# Bª ¦G×°–ßVˆ¸L)ÜõËÍ=9•ž‹%ç=íXôç4RÊ[ èeúÕ&ê¬hE”ißU½§™±n°pECÕÀ ²ÈÕWÕÍø ýÛ²n+¿—AÁ¹ãhœb_&Û’ÁÚ øØÖzsUG¿!g/UzzÑ#î¶þø5“LÏXÖGp*Ù8æö1¨°gU[9Œ…}ø§’MŸV’_8þa•J™µÛlÊ®ß! .êm ÷³#; PEv@|dAW\܃ˆ¤[° €@3]yïÞï“'oÛ²e"€¸Ó‘¨•ÍŠsÊ„§—öìøqï?Êê6›)jw0fMËÜ“Z®{¢­4캓¯\霴;°ãî8ê/§Z°¬ÙÇPšc0]¿Ð9 €„«Œ¤{Œݹ@ê0»XÌ#Ð’€‘çÂRµÓ7‰`ÆæÐ—ó-£}*Èc¹ëOVÜÓUú•CK³ñ›!‘ÆnÝMA£-Ü,ë¶',á:á1\ãÅŒCìCOºM˜Z‚ÀÏ\W€¨‰¡U6÷F®ÑõÃ}¨N†—ëî•—¾BTYOi£Ýdü²‡›˼™‹u}ñ?üÚÆVv4N’vX¬N>.í\¥RteeQOC J(¿±Ù@]ÙQ#GÐ¥Ÿ&Nܾ-‰¸ 1À\ÈS¤¤§GU̘ujýö·s[±íäÒaÍ_ eɺµaÉÆÖßɳ Žñ2lâ†n¾ IDATšš¥qBS˜GŸ‘ÃÛÑ¥}ˆ¦Ào²úò5ÕµkªKç ·"ÅuL^)ÌkW‡@S{âÏ?Y)¾F¿×'hasí¹êé錂¾_sÒ«l0 €`—¬Ç㛇¶ÇOeyJkEᯯØó¢JÈ9#èÄN!¨õ½Ü_&ƒ`ÑøKå¿H©Ê×Ñšl@ ˆ…²™.£Q«V@3€J»mÚ‚…oV«.’içvüzM [|yúÆD¿qA}wŽÿ-áÙoòŠù½[¹rÀsÿà¶ùË/'NÝÇ|?¿_'1A«ãÎ휾I¶íâh·Ç+[ö:§òùZñg…ûðs¡S}UO®&¸vl"#“ooxæ9zÄÀFÕý½Fí SNc—/ØÑG€Qªˆ“»æÌ;g÷ŹЩ¾ªÇÆ9ulfÏȹ¿nô‚m²ñgÖ´»'/Íl>žZÚÏ_ÌuÚí}!sÖ§ö«˜¦õ2ï ?Lõ ¾ö{¤Šf?¯KšcUe=Ñ}r‡ ,C[ák¶sÔõõ?”ÛÛäÑžÌÚÈ~6D×cl!Ì\¨ú©¥›”rŽš8– hË63›¯Ð½öÉ~س56!rͯ_ëZ7‚'“q%IcfÌLÓÞÈä#¬äîqú,Vg!ɪÕl*ÎËê¢YnóŠ0ë•¢¬5I½ÍVÏMRIÜ©Pïuc}^>³ÆfuJÁ³56«Ê]W˜Á…@7( 00ðSV?$$¤ôêš®¹*rUäª(èg0LÈ.I`fŒÁ¨ú@kcâÍ3ñý|¼Úy³OØŽÛ¿e°SÁíõÓoa=§Ì»tŸ%­Ï‰Ž~énŽ=²rÇ3Úë³^¾¶LÅËjĨ®ýÕøÕØÖf/fïÚ~ü”hÜ€æ_ÍévbtÌßöŽ’\;=œÛkþ–Iß…$>î{ÚšK¨¹}|kö  ÞÍg-ludèßç—îphŒ¯æÞêÇ¢Rct”{êc˃ ˜î½Lü|êüi—Gn¯”¼KkÁm4ñy]>ß[UYC®*>¹¨‹®;ÄœÓŸÕæET6%DÛ~¦YõT0³»Ä´{?MpkáyW*'ÝÜPŽ--¼Æg¨×ž¥kÔ{MuÞO3pºãÜ¢®xSZ4t}ú€vÁ?‚1]¸@о4kZÑØí 9Ù^¦®2vÁ‹ðUeØ÷~…¨b}¿4ØNõ8OÉ],õü!#§_+"¢Ÿ“O‰<«VêØ½×̲ëu9èî„@”±ÿþOVw¥RYÓíP^rUäªÈU?õ K 89ÕʹœO–\%~eãààý¢a%]ü?–göN ;6o?šŠ=²í×}¼×g]:ÑÜ’^¶dS’ààœíÿÍËjncðÅã#[·±Ü;¼é±ÝuÀ"OG±w­.Ð=hO÷â´-œØ§‹so8’íËï×{´›T.À ©¢ |u®…>Ñ0à^bû/‡¬å*³³¡ƒÀœö°B†gi%ªÔ¥½ÇAue øô–#rêioÆiË:Oû¢Ûíº-Tw[X¡{¸|Yõ%4Ðþ^YÓGwîJÙŸ,?Ý…ËP"på©ò»ë¨E—‹Àûûžߗû¦£úvDz¿LYŒ'lã¼ú¢a¦§þìç4a’=_*Ê ¯ž~ïÒ­VÅ­ÉÌå—ÚjØQëë9ùΦ)Uñ:@ ⣠º‚ƒÇ ¶4þ\XÑ´˜ Ð àÀðõmfkëZe~–Gûž>TÌõ8#´ ©òÁ†á8²4F+3˜Ä‹§<è² @ZhÀÃR·Î÷[¶5Ì¡ yZvÙÚd0@b çaÿ;¼¶ñÞÁÕÿ gX²¨ÃëÍSTÐ¥âW/,ë#Ç×WVSª:°A >".®Ýž@ >¶  ^֯߷÷îýž74ÕÂ3aŒGÚøú6kÕjÀ† _VŒbÜúLÓïÓeD§Ú±ƒÏdéáâ_ÙSûL˜2±à/ü³¡Þ™—®^?éã†yÏ\¼Ä|&‚tïÜ“³gv\!Ô0y,–Y§½óëVÕ”|þfÆ”¡nc'Ž)8y7‡åÙ¶kûÂÍ£÷¾ 9e,ÔØ5êÕ»57"¶ÈUN¨“’R‹x9æçÒD<Ò•+« ]n'›PAÁøT Ô7NœòK„·ïºxךnÌOGsúï‹'üp!ÓB1|¾øqûÜŽr´›\µšaÉ>3uЉÁöv"Ÿ­ÁT>híôéõýûÛë³i]îÞj\ÿþ³;uµuë¤ç2:ô™>nê7œ²œ?nØÚ( ºGÛ¾šqú¡¥å¬ŸæÏèh Û·ü«Õ‘ŠÇÛFŒÝ#Íí‹%óV-ëç£HÎM½±vKD!á3bñ—äFãk×U÷h눯÷_Kr¼hÁÏ¿›ÐÿìÅ+û´ÛCÂrHϱÁë~wó¡½Ã°…[–u±·XªJãÅ©PVºèQA ˆOcÔ¦Ç|Bþzòèܸ¼ÿÍ;›K}t*ÒÊ+ ¾ìôÁã'¡[œûvõ#ôC‡@®Z½.ÅÃ]“zO:•jA>[Óa<iË–I/Ë¡º>Ô±õ îÞú¸#kY[ñª)íÂÖ±¶–¿”°|\£åÏeO:èx°øóÍùuK"£í#½¶N=¿õ›ó[+f+—+a_7Ç}¥QדŸƒZý\–na×ë Ÿ+³bšÇe¢ *]’^T@ >2Ì7®“~l ÄÂn#¼Wþö´¨7›,èbxŽZµ¼™ 0¦O'ã~… ½&\µÚ`J8²ãYã•ë5sÏ ŸýP P©Tz½Þ`Pq8b.—Ë)áõåàÈ”ˆ7ÀBìî#™p/ÝäfŒäuïο÷§°Ý8¦æ_ôãp~{ÉúgÅ{ãÌñ¼^=xÑU'A)˜+frÌ ¹%l7Ž©1;†Küˆç‹^§èÊÙÿËÝ=‰è+ó-ý¯ŽèœâÍ$hBM}%+þ)þ³è†¨Ý$fÖ Q»Ie3Çózvå=3”Üucx=ºó#_c^¸TSñïwʵÐ]Ê`I˜™Úîýy¸Ð¿[1´æéŽ ñÍG6¢†G W­F°ü&mžÔªt !òÙ÷k%%E]»vþÚùó'8qâÀɃ»Ožû‚Ú}„©êb”`1'X¬>E ÛÒм'“ÄwÎçö<®óéòñ †ÒÀk¦=àGsì?³Ì¿ÝÓ4<]ýê¢+eçý730G.hÿ»GK4þà Ó±#}e?sÆfÁÛn¢RMÄÛ ì¸$ì£=9‰*z²e̘?Ûm<ÖÓ=ŠE WE>‹UvvRV’R©4 ƒÀÀá‚Áz½A¥Þ”•ô8êQ£F[·n-;¼\&j*Ä›E ] Þ‘ì{ÖãÐõŒc7è½)óþˆE ‰Ý^/êÞQÜ©£xÌOÌ|=ñÓç6[1 ‰ý}¥[3`éûlú„å§²8µ £øìû*\3~a¹MÇ6’6m$ã63UFâä*nd4wú|VÊ=þˆÙœõßñž¤³æ|û{›?bCgÁ/¯ué i×F‡{NL)O¥GDÓ¡»^Páliú†¿ÙrWBÙµ©Ô£–ßDn:£¼x"×ó¨å77 ƒç«½›©ƒ¿+´çfºÉ?øH4Á› Z±Õ9‹oî®?©·ãmö`¢! üê`*—•1J…aìúìC‡s;DšL`äûÌowË=}Vux±N~…[þ0Ò%™ kU2¼pð¯Ü¢ª¾ÊÁd*ô_œÉús®®|^Æyö®ka×–…Nü-aœ†@>´,ÙJóÂ8­©È\åÈÊ×óú•ìiQþjëO©¾³7æ¯}·r ºäÓ;b=ßkañúí¤öáºO> ܳûÓö"úA@èP¥c–2®èèH@ò"–eYVJ¥Rž¢KQ¤¤(UšR(ù£PÈ÷ïÜZÓåŠ/y>CǺe¿ñ²ú=ÜËwù¨À‘‹âÉöãÏðäá¼}„ôŒÃCÆ­¯.c'/½¡@ÕEýÖ}þÃTã© ÝèÆ•‹H4KÇF.0ó/™ê„‰?Ëužç΂aõCÞ3N¹È-VS¥]â:¼—/aÁeµ?ÖLW7þ_[„ñùçÓ˜^Zc¥–n¢üN0÷,ò¶ôó0ßèØ¡H;ÞæÃ(mï>…3FëÊ%äU^hªÂ`Ù´¨¹5*\oÓsÂvñÜóìÆn†¶ ß×Ô«f§®êÇ Z²PcþþtÆÔ†8õzT¬§¢x¦ÜšêO© 3ïi+†NžÚÙ'â®ó· rzý®žçß\|#9熿Û ìrcÿ`;†>{B‡*³o$¹<ÀŠÅb¥R©T*¤ry44(N±ž.)bEB1X ˲B@T&ñ’xyùRÒEê†MMÛ¥Bé#„ÝÕ_`|z¡Åh@Ó œcõ<@“É)°0ˆ4…îøaqûª{gXž>-xä©mjâ翱ñ'y#ŽÙ,:\¨C•™^ÏŸm,[‰É~ÄIºa>a Þÿ¸°ý5{ßò<¥åsxÆòÛ±j—üœöòiÁéý–ïïSúMÓ°–§ñÒ¹fÆJ×% vÞbââDí¿t(ÈgÄiÇ¿ð'aÃT®ç_¾tý !%טlÚÌÜ>ó5ÞC‹NëbS×Ñ'MèP}µYõÜÙ“ŽÙ¿[JJŠJ¡dÅB@£T*"¥ÒÈ+ …B£Ñ(r¹\!fK¦ŠT*!fÁŠEb'‰D"AéàC•F}àÀþ©S%"‘ÈD;¥“çf©Ö‘³c=û ¥¦}¹F忝Wì5»›c>ï—6__g`­ë]O°á8º´Ô·îŒÃkøÎïêMOÁêÇ«IÛl¯`ôè®OØ+H(„1{èãÝYoÆ€)bŠª¹3Éõm}ÚqÁ“B²øKÆZn>Ä -øßhíÛö¼ðÇÐÀT\½jù‚*‡ÆÝe64vV°x‘¶~*'«¦ùµ(óè8«ðϽt3ëÆ¬Êஜ}»¸Ïóˆª2ÄT¬§R_²§ÏSB!„7—F£‘GE©¡Ñ(Õ*•F&=ªP(äÑÑÑÒNNòК°o4Ò/4Ò/4¡s4³ûk¼D)Q‘ò¨h¨5šÒ?ò¨èÐÐý&7A=]ä›X”:XÐy·Îº¬+ÇhÿNÞ··-ÿ×G¬ƒÑuP^p'#ÃA»^†ÔÄ"[8uЙ¯â½ãk¨®×JØB½lŒà£ÓP`þü;–cz Á5zä-ëhäiŠÚ­‡wL3Qqü…׬ÆöêC›©yó½ŸN³~7Ô`×P×¢’Ò9ò««–Ï\®T¦Ák¤Ú}šuíFÇØmnnó'W4QàÅ* rdü¿,ªW|­ƒcì:©péTáõ6Ïú*2XK*Ô³• §xOÿX¡vÿ_mëO!„òÆ’Ë£ª¬†Õ¨RäaÒ0 4•R®¯á@¥†\'|%ð•`Dì Ó,=íèä–Eéàð°P__¿ªƒ i()—»Mw$ÅÄÄp¹Ü©S§®Y³†¢DÊÓëõÅmãäÉ“ RWbbbš6múôŸ}ûöݾ}û ¥RYÿnžîÂÓ”Ît¨Ò¡J‡ê«C£Ñ„…†ª4%“JKî(” òh©D¤Ú9 ,…9» R³¬P³i:JJÒ°~ËV"ñò-~ˆrñ%îýú7µÒV¨§‹<OO[ ÂQll&üG­ZµJ©TR(„š(ÅüMY—J©Pk4`Ù”…R©d•F#—«œ¼°S 1‹ŸBÖQ"‘DEEÐ']"!†øbM˜ÂIâÅgo,ÈåцeYJºýv'„üg”¿òJq „š(ÅÔ-•F£T©44J¥R¥†˲H$ zS˜€,ËŠr¹X¨Ò©4Y"ä‰ËÍp €”%]„B!„RB©T¨4J¨@¥R²`°`½¼¼JÒ(F¥Q+äÑ¾ŽªoF@"• v!,šõö“@£Ñ°lñM], Tª E¥9 iöBB!„BÈJ¡P¨ÕµF£Ô¨ ~ú¤!MÉ¡QiÔQQ‘$ª5ãJ2®+r YŽ0¹ÈÛÏ‹…°x1€F]¼šJ¡¨´JºÈóÐç_÷ço— ŸN¸¡‹½÷óÈû²‹W—ÍMѾ@y 1ÁÃï$–®©“E¯y?¹ðÙ+U)ǾŠOוIŸr4bí࣠º^4øüÐÜ"£©Åj(áYtq÷ìzh~ñŸž'Öýð(USëê•Ò†_]67%ûê•e_¤=˜þÉÃOKîzh~ÏKwTÏIíÍ_w=yì¾äV(r’x+d°,˲*Mq—še…ŽŽNÏNºª›7œ¼š柜÷Ÿ±é,±ßúH–íÔBh²o\øýd©¿¾ñ€Û¬oÅ7ß8úW¾»Þ­†Ž4“NŽ|Õµ[Í•I]íÚcÆ(‹œýwäøLcýŒt_WpwíÍ?ÏåëÁsä3t„›‘xxQtl¶|ëvŸy™ï”%Çë÷,³;ÏÙŠ ma¡€­gËaÀ˜yH>g³„+JûÔ2꧈ ÷µº|=Û¾Å'ŸÙů.ykx÷G›C=f-u0Ó\žz%wN·>VвmÍoÓÕ‹W5Ä<[Û·g{FN4É®©P[~Ç?¶Œ^]Ívç5²¸}kã·Éƒkì*Aà\¿úËV›”YÔ÷íéÓÄü§ÇlvZø=aÇ%Î×ÇÇeÛ7·yÎOµÖÑþdœáì—"C ù;Ð|â„СJÈ?F,vËBSyü’¦\ïÕšP¬ +{Ã×× Ë Y°,Ë Å, ¨T%«°"‘X\‹¤ëÿO^Ü?Ÿ!sÄõ;x>¸©mÞݬðAB¼k7G‰˜|éíc]ÇíjhcÌ¿:ûò±[þZéOÞ/ô·QŤqòn( †óIuî,Ê7;}’lmYÙ¿ë{Æœ¿"Å5¿ÛÝN—yrÊcÞݺÞ}˜Ø±ýìÑVڛѡwŒý§¹_K7 [’qà¹yôï¾gÈ ›–öm[öqv¯Çqæžn:×™wûÖmA³)»-5™GÆÝŽT4ìVú–ÕÍGwÑZn[7óмD&;ñ8v¢ú¼'©*ƒKdÅèþAuÛ}Òðíj«OIXß7Àm0¥ûä@‹üÊAè1†¼4ó!»;º ™r#ƒyáòÄ®ƒ›Ùéíc.KÕ^ï «ûø^.ÚŽÚç*E†K!!„BþËD"‘XÈ*T•3.h`z’¦$ã±b!˲B  Ñ¨‹Wtrrª4‹hx!yþ¬ËÌc°èÏýéùþŽ©Ç2ë½çmÃE!èÒ/gfDª6ŽŒ`Pë­ä:»®¢¼CÙ¹N©í»>LIÍàßÏwv«ðCÛÀ=hK«†f “E¯^dôéÒ\»÷ÛØ™1¿Õ[°ýšÚ¢“}ÑÜË!±ÎÞþ { ÒÒ*WŒkÖ4Èþ¨¼G×Ób¯Ê¶ŒïøËÛþ¥Ù’°M«áV©ÑŒÊ33Ͻ­ƒe=oQÚù3Ïfæ.mù[†¨›¨Ó+ëJp|a7wW ŽmÕ¯~»µV5æ¦R?CfXBvû¶Ÿë·ðX¿…Çßù¨CáCªçÉîjíZE†B!ä¿Å××˯SÇÒÙ ‹_Ó<ý) D–þQÏNÈB–±j¥*:: ¥3p (~ÀW%ÔÓE^×y@ýœ‰Oÿb+,×íbÕÍ7 úÆžQ' àØöñýè->Ã1JÞ6ËI±ssmüì[²[´à×â~Aƺ—oÿè›?Š—[ÿ·>ð3ºxÚ/ºüÓ†Ãa›MnëâTàf¼¼~šÙ´µžö<´lññø;—œþ3Ë¡…ç`ß¡}…\m=7ãåõÓfº[}uiåY¡¥S=gF™e`\ë•”ðsã·íÂ× Š·ò¨ïÞpWØ–s¹4Q÷èá/½+tëÝ|ô$[ ¨²ãœê·k¬í1W5|æz•¥Šò"O6ùŸƒEñõŽY“á °P&oUûOó9¢=y‘§ýW"c¤ñ…„BùÏc{ôè/—GË£äU3.±˜Ý$Å&iÙÒŽN,¨5òèè•R­TexùùŒaú÷VÕ—ŒFã?4‘†e»»+û°P„Œé±$Fû øÊ«Û«ºi9bbb¸\îÔ©S׬YC%)O¯×· šÃŠB!ÿi‘‘W6­Y­Q«Ôÿ%XÉã»4Ų,+ Q*•ÍÓûfÄb§o~úÉ×××dá5^uç9=µo€¢+Ó‡|¼/C€[?ðàþoÛrÜùô½€Ý†Þ3c‘J‘—o¯KÏÑðG8°¤9²ÿê<ãVÁËHþó»¢ bßžDA „BÈëÁ××·SÒ°05T¬†Õ°eyWI ¦µZ£Vj4oûŠE#Æ ¯.ãB­‡ò;Nïïvx[|MߟܶŽîËü¼SŸÏë0Tu^ ¡ßÙ„B!äÀ>ÂËË/4t¿<*eyWé3“Ë=;™}:—< ??¿qãÆùúv¬¡èZ§OM>í#cÙvJ@£*o2æ’÷—ü|*ê’L!•ÉX3¢£=„-¿½'•)¤Ò5—ìÞ{W!•=>¸m¶_É\p¥ïžšÖˆÇ±ýàÈ%ÍÀfèÚ{ ©ìüH7þ³K>³àÝñ«6ßP\:òq}nùŸ¹u€kçôëŽH…T–xüؾÍwR™âøÒ6,µ8B!„BÞÀ¼Ë××wöÔ9CƉD,X°,[üdVÈ‚… Y!‹â?N§iÓ¦~óÍO5g\¨mO—F Ö6`F‡_æ²Aï[CŸ«çZ•M¤Æwúpó¦ïº²@þã¨t o7Ÿ€©;ýl?èýKDéMöŽ£Gäà×ï2gɧç‡.¼Qq&FcaRøõÇ-Û6⪇Wîd«ãž¨9Ï.Y2uá|@rõÕ7½u¾ë¸í«æøpèóáÜ؉3 IDAT®™5³­ZµJ¯§i!„BÈkx‰Dý¼|}##¯DGG))J¥*†wΈœœœü¼|};u’H$µ)³VIWþå½ýF¿Ókü¬¹‚Ž|Do<ÁŸ0´ñÓjy8³+ œ?,èx–Y‹‰ÇOvs:»×öQ¥·Ö+6M}wQdkàéŒf°}˧÷Fr…ßïÆÜ+K¿ÛÔåÀ’æÈ>½rÂŒ[Ûzæ3K.¸~pÝÑø,µ6íJŽ&ëorëŸÀ >Àp÷Û1­Ž-òø$ôâ$jeÕãr¹B!„ò&H$Å •J¥R(r¹ '‰“““D$•%iì³ÊÕ*é2æÞݲSñÎ4Ià‡€6|ÃŤ OßäX7ká²?Õ@ùrúh7{®G;gAij”Ÿ’Y`€.íal.šYÇòj1ã^­JN>µwýÆÇ% V“4šØ:ǦIS[x°ý÷ø#øFšz£Z3fÌ  B!„7H$‰D&Ÿ¾U{µœHCûpçï÷§Íl(ì¼î>ÉTjfò¯Ñ¾Èð´Z”\«bLn]_H3> ÍN!„BÈ «íDEO.[uæÌ‰£?ÿr'§B’bÈyp/<Þí æÂÆ;;ÐÅ…'>_Rd,¾cHhk-`ê´d 9 Or ñàÞN†céì@÷tB!„B꯶ T—¾[x À­8}¡&ê÷U}ëoÞgã¾ó1ilÓF¶d¿ÿ|Fe@Ã程>G›‹VV‚_:~_~ç÷Ï×Qɦ¨ï:žÖ;ÐAØužËŸcGB!„B©½ºH5Š{ÇŽ½ñš,[Рi#[MjäþÕÃß_‘÷¼ÃöÔ7¾û~ûL=ø|}Z¹YrõuU² ÆÜ[ß|ôþ;*#€ü„³û®+@¯¥Ñ†„B!„ºcb> £ÑÈ0̰«Ù“„TŒYãñ«O|Õœƒûs}'ý‘bø¯ìCÝ~XFšM„B!„ºÆ{ƒ÷]èóå¶Í=˜ì”t5ëà(âÈ Ýy6Í@Í‚B!„RWÞä;™ôʨ¨ä££££ˆ£Ë”Ÿ[¿äƒç3(ç"„B!„Ô7yxá뀆B!„òŠ£9û!„B!äodúž.êñ „B!„:Aà %Û„B!„ühx!!„B!„PÒE!„B!ÿM¦ïé Ñj 8ž¹¹upðh !„B!„¼¦Jºµ>6öZ||Dnn&'ptôhڴÞ=K(Roº§‹B!„¿7é 9~×}ÙôÚÀ)X&‰{õ{èÐr %]„B!„çUáž®ØØë÷å°Ø,V»ç£ ?|—Iç¥2E¥?›‡9ÖîÞ0ë.»RÙí9>B0ktQ!•IÇy j[]ždøI…T6¬Q•q‘Œ¥GÀ×+ÿzpI¦Æ?üc÷­¬ëþ޵¨3!„B!äMV–»Lž¼æäÉ èÌžf~À\¤ð(11ô97¶n qæ ›õÿ¤—Šs›÷'&ÝÏ‘âŠEJë¬Û麗Þ~ƒ‘Û6/éÄ×Å]ømGJ½nï9ç@këþ·Æhë2^uYgB!„BÈ ¬/H«ÍÏÉÉDÛrW±·¡³(ÊÉÉCöÍ-XºnÕþ'tÑGW.]ûÇïsÛ}6ôAqÇWÌÞm_t®ÏÀwî3qÃÙÐX…T–|âä–YËN.ëÀ!à€L½µ½µM«I_M_4µ¥5§Ê‡xÔooªLÓÌ}FÍìćêÔÄó¾üfåÌ÷glU€ÓrÔ¬..ïì<'S„mèkÍ€©7peŒB¹º%øÎ}'mºx*^!•%…ý%ÀË‚)éF‹øú«-»î%?rõRÕ+×¹r!–]7ž‘)Îo}׆áØ>xI¦8ìoǧW¥2Åæ hºHB!„BÞÔ¤‹Ãáñx|dTY$LÃãñk*Æ y|î¯§Ì šZàÒeÚ¼é>,ÛjÂÎm£{y¤í[úÂ¥§Rl®³%@^øwãçLXSP®€Ê 7ô´ç˜(³šÍsí|¼ëúÛg"TFÆü¸SWò¡W³››/çƒíØÊ†cÓ6ЇÌcÛîëZŒß±õ?Ùæ±ý'­W6ÿ`Ϊ‰n%ɦSÏA>‚´èÛ‡×]ªºbnù:›(Ä>zÿ--ø-{¹ Å­¿Å,: t·´mîï Dº˜j 6G!„BÈ¥¬óÈÒRììÜ$õ”·ŸÒWõÀ6˜l=j(ÅXð(\æÿqàÈ&.¶ö¬­XI‡®@ÂÊ%‹VË‹€]k9ŒUçÀ³ éz˜ô–<ÉÓø’þUæZU.“‹ÌšvÆä<ÙáûO(»ñð–+gh'ÿÜ}G'ù´§€>3¶ô)YÌ­½³Ù!@tð€>{ëK¿fsý+®¨†s¹:4Qˆvlj]ÇŽÚzwhòïñ©[Ž}ºumyÎ¥ ;rEAƒ !„ByÔõtiÚ´ƒU²-¦G%ðøX O϶öö.ÕÂmøýž>êÉÞÞñýª5—4†árèuúšR¢Uæ8›(³:úŒÛÑY¯uOÌÝ{u°ÔÑ×S ó¢vÊ„y»ñS;±ˆß«-ÙÜãu³úvìÙ9°gçÀÞŸFäW*ÕÄŠ&ê\©Ü¬[‡oî=FMl#|pd}È͆=Ç|ÜŠEò‰ÓÉEÔâ!äf4ë|ÉW°ò„âLù×’.{ö,éÙs´S|cÞ0ƒiÏX¬ù¸öîØ1`ÅŠk(ÄÚÅŽ äÈåsÍ]œŠ&?—xüoá¢)ïNœ¾áðœöBm¶pð0°SÇæ6ܲª.<·w³ªeV« r{ð•"ˆûn<ôõ‚¹S—ïë ã½íÁ—óMÔÎ?Á¶åÃâÁŽƒOŠP˜z> h4vò˜w[µôkóތϗOr7«\jÕË«¦}Ö•CÉÀvÂG^¾r*R_¿—¿ R.”QÎEùï3dGnù_ÿVŽ,Ã0 cæÐ¼ÏÔð,}oEù™+Ã0L×ýÊ¦ÚÆü¿ÏygÀ/qÏZôù·K5ÎJÈPÝ_¿êFª^26ø§•ÝÙ YS¥…J5QfõUHÜññ¸ÿÛt=ѹûØY#{ª®íü)àƒßh@û0toèÃ÷ŸLÒ(ˆX7bÜö0¹ãÐóWþ2gRWAÜݬªÛ¨ºb…LÏT!EÐ+Îþ éa§’4™·ß€Ì3añjo„ÿ:mÔ½ÚŽ]z7µøGmazÔéµS:tž5×ø®¶>ù·~-†-?)×´b¦ùÿý<ÑÏšaNñдú/—ÜR«OÜÔ‰Ã0L£Ù7ÕÚ¨zx7oî5êd6è3¯¬Ó©‘aFàè;ôÛ¿RJG'T·¹ÚW[÷hM[·i· úó¦óÁ§rQ”|bÉàV†a†uôýðûsézT]2U:Ñavà‰œâª«S†áö:ZRïK?l×PÈ0 Ãðí›÷Ÿ{@^}÷ Wh#׳«ïâÕulðáu]x€^ºæyQ {ZûʨÕrzÎzZ‹Äåˆ,ùLñ¥;µìÈ×ÃÛ»X0 Ã5é5uãMUɤ½•?}×qa9•ËÕDÿöë€Ûé«/Û1@ÒŽµá¹Ï%ÆêëY](LÄùù‚F!„PO!¤vt‰Ûzß+pï1zþª½çdV¸ê¯K=4²>€urµã€Å;›ŠŒFcÎ_C-ËqØî+~îÆ`?îB®Ñh4ê…´÷w4»2òo.jU¼’ žyñß¼FÔ¸¹ÚW[—¸}@Ã’ÛzâúÍ'_ÈŠúÎÀspw¯_ü@ËÁÇ²Šª,™vy‚=³fF£1ÿÊäú8=¨ŒF]â¶Þ,8útíåßÒàùùÍ‚jzºš|¥-«Õ£µ¾ÀësXYC`k_yCµEµ­§Éž.¥*¿$Ü…ñúZVþbi>÷’Ê`âÓï¹;¥rfþ•©N½v'%îèÁ`9øh¦áEzºÜ>»œò´† »»qÊ÷tÕTÏjCQ%Îgcj4B1ÕÍ'2nÜÏ&¬š9s+¥"”tBÈS…Oþ˜Ð¬Â( 3w>Û“o0FÍÝEÀuæ¹,½±èÉ®÷­8]É/ûÙÍí°òZ——š^~p 9»1çsÆ¢„5mÀû»Ú ¿°õ©{ú˜ù¯Š*05±k»ð¶Åœðüš6÷<Õ®4rL—ö×âaÝ|ßõG²ÎhÔ=ÞÐÜæFª«Œ1˯)éÊ>ñ @òá¹£QŸ~jáØIó–ï»—c¨EÒU:*¾kéj lí+_MÞRÛz–&]™>•c4 ™G‡X@ƒwÅt—¿y›ïo¢´&>}uå‰9g?± ì Ý`Hßß_@Ðk×ÓQ«Ï“t™R’tÕ\ÏšBQ1ε !„ƪà ƒ‚Ö÷î=îĉõGެ'%€°žuI2Æ·®gÉ€Úl®6Õ®ºŸ9«G´Y8·ì>4èËMáYÀpªÿ9]6rD¯+Kn̼?ÝûËPO>`H¹urûOs>zÛÕ±Û×á9µ™ýNŸ›œ©Àµm`ͫힾP埻žnŸßR—x ·€Â,E‘‡kéÇËwôvå•Îħ_!#lõ±\¹ûû×ã0 GÜo_.ãÕÕ{⟂vSi”¨¹žµÅK}¸„7<銽~_~ =À"`° y>ÊððÃAA!¥KñºŒ\z(J!•)¤÷oíÜñ]Ÿ†ü¯Yó ‹ ©L:ÎSðB닺ïUHe%.Þ¼´úÛq­ÄÜê—·î²K!•Ýžã#¬U•x’á'RYذF9Yqø»?=ÀiÜÖEÀ­ß±g#iQÙ½ßô~w»«?¹,dÿ½àé× Ã)¹OØjì' óÄö(€í3µ¯c•~sïm./ûår¦ÞXpÿ× ±Ó' }oÑuM-6W›jà𹟞[¨ÍQÜWh]Úl)¤²kÿë)±äDz¡“ß¹ï¤MOÅ+¤²¤Ð£¿xY0%ÝD_µe×½¤ãG®^’)Â6ôµfÀÔ¸2F!\ÝF\¡+‰ïÜg↳¡± ©,ùÄÉCÜ&Š­ø û^…T–¸²‡5˜5ýøO…T¦Ø4Ò…k¢J6&Ú-&ÞÚÞJÔþû³¡Š_‰Ù»í‹Îõyz·Þ¨ž.ºÇ‘ò,ê»Ë:šÜÐìÿ¤9£ÑXôä·V+IsO1ÀnÄ‘4ýÓ©D#Ïå–/Qûà[ïâ2¬†Ï2˜˜5Á˜wca‹§¿ÚKþ¿ùˆü7÷\Õ6ªBß/ÿÀóßr|z0óùsÇuu(©Þ°3¹•—Ü“ž}v´]ñ¿l\‰¾ˆƒÒ‰4´1+:ó€ãìëßµ}c `{l¨üä­Ò­BÐ釻%³áÕ´§µ®|5sQÔ¶ž¥iT^XövÔ ¶ò¥ÝÖ ®æ”›½°ò§_<ËÉÃZ€Mà©ìò¯\ŸÕÄ#ÿÊ®ÃçtÕTÏCQ!ο]þ¹VA#„Jip8<Œ*çüt0E Wüme̹{1 €ËÐÒS±±ûö­ŸÐÝB•§ÛbüŽ­ŸøÉ6í?1h½²ùsVMt+0èÔs -úöáu—òÁvleñièÃGæ±m÷Ë?ƒƒm5aç¶Ñ½<Òö-ýaÁÒS© =]|M[Ã`F­ìüÑ8î]<ÌLTiœùéÅ[bä…7~΄å1Íãs|=e^ЌЗ.ÓæM÷a)'„j°->;}{ϼ€6.%C"XÇ–ïNß~ùÛŽV ^Ñ{®nŸÙÛÓ:W~?ViíÙçÓ=Òì«*¤À#pò[PoPÐÛb“£,ÞZ|öâÏ£Ú9 #Žo ûîÌ™E¾æµßܳª ë.‹¿à&Àsr¶^r8xX í­?¬½ ¾ú‡^B ÷êÉ8M¥%Qhá¿üвok ;‹ßvöñ+Ûµ&3އoœÒÃÓJyá|xÇݺ‹ûǹ=û*žyÃ6,ÜwçÔœ%?ókÚÓÚW¾šàÅëYž™×Ì?oï_4¬]Cs°òè1õ×ç—´ÖÐmÌŽ wXõþ¤­uù7„-F~è@ypÕÙ̺ë?ª©ž5†¢BœnÓê"h„7EÙ™pæÌ-.ì‰,8‰=€Oé«z`1,VŠþlÇŽ/г3‘O¿ ƒÞõ÷nPüÝ•waJ‡Å'î<>ù|Ñú󟵛çºG:Í3:¸[Ÿ½u€¥ß×V1»0¡WèGaß÷ÈØ1àíµ‡/Yæ€Åg»ÏpNøqDߟåE3ïÏw«R¬_àåÜò=]ÑKÛê¯÷ž–ðM ÛóYcí©ÀÁÁ~­Z%¿‰œõ1Ë:¤èðÓ-5À½õѨû·jâbëàê(æ… ï79áýÒ*íMvyZ½½_÷ц5 ?!„B!„¼² 2ÁÁcÇ¿‘;=Ÿ]€T`'°žžmíí]Š3.«f¾ ÿùíØcß‚kÝ|pHè§,›¾åÌÂåx¼nÖ„)ʉQ“žoæZakyQ»eÓnüëÖ,âw„ÆV¸9–a¸z]éô°FÀd±ÕïÀÍ¿cÀs6VËt1¹n›r‹s~¿ç‡VÚð]ß}ÏlÄ¢]¹Ô&!„B!KÒ`Ïž%ƒÍ?œ>ì±Î¼Ñ1æzO϶;¬XñqqÒ%î:ëÈ"Ëk‡Î]Q2:z €Œëçå'„žOš:¬ÑØÉc2\MHÞî埽fôÖJ›ÓDíü3qÌÈö£|€;>)ªPƒÂ„ãç§züoᢢ£÷õ®=ú³»~2QìÈÅ÷Õ• æ6z/hŒEãž#º{ >ªPÃT•Fþ¨ÍV¾vÞ˜ëbÇräòǹæ=œX ˆÚ!„B!äïJº:´<(h]rr\nn&'°·wµ·w)͸T7C^{¿Û{ÃÚñ}vì_;¿X¼éJŽÑ±nĸÂEŸ º`~  I‰:º4«êp<íÃнq#g7†>|ÿÉ*O¤,ˆ 1¶hɼA-úœu‰'ᇫk*1r|oæÀ-»¹kcÈO¿ESU*ÊOX¿ê†Ïœ6cƒøûÔÞ߯ÙÝsÁG_¬õ»rQG“B!„Bê=8”¡{º!„B©s !„B!„PÒE!„B!ÿI¦'¢Õp8âŠEJë¬Ûéô.B!„BÈ?®¬§K«ÍÏÉÉDÛrW±·¡³(ÊÉÉzvaŒ™dðÿv^ “)¤2ùÁ=KûJXf , >ûP*SHeqÿøjÀìcË:p8 “Fomo¢‡Lÿäø†m+ÍôNH à:aÆû ¹ß¹ï¤MOÅ+¤²¤Ð£¿xÙ4št´BQV¢öߟ }PÜQ³wÛëóÀØ´šôÕôES[ZÓL„B!„\YO‡ÃãñøÈ¨²H:˜"†ÇãWx‘Ûî×i¥…¾“·¯êœyqùÌ Lÿ©ŸŽ]ü›.itìÌU›çž ™¶YΗøõpÏ;µxKŸ]c<ó¿›õGôãuõ•ÓÊΛܴ±{³}6ŸìØ:J|jÕØ™÷„æ­2g•|ì¤Å[v—U`p}|e™|×ó'÷›6oúɾ̤O™B!„ò $]––bgç&©§ä¸ø”¾ª¶ÁÜ`ãèèQqÅŒ“Oȵ`,8¦ƒ’wýø kÖÿþ˜‰°Ôg¢û;þ ¢ò «îÍ2¿{'âú¾5Ò»9úLÀ³ éz˜ô–ºæê•Lì`4 ${ºè3cKŸ’÷ÜÚÙ¦ÿú BQ<„Ëü?ÙÄÅÖÁ€•£”tB!„B^…¤+8xL`àâ¸ã7r§gâ3  ìÖÂÓ³­½½K…õô²?~Z–ŽC¿ö%IWižd(Ÿ/Ò,j 5°¯ßÛíßèo?pÀÝZVNàæß¿1`ˆ9«eºp<^7kÂŽ”⛳Œšô|´)·8·Aà÷{~h¥ ßõÝ÷÷ÌF,ZЕK0!„B!äßUá>§={–ôì9Ú)¾1o˜Á´g,Vˆ|\{wì°bÅÇÏ*ª0áÄ¥ ñ¤©“?8eá0 ùäUÁø…3Zç]Ù¹yé—û`ÍÐf«ß;ulnc"1â6z/ḩ_/=x2ÈxøKðQ…:!ô|Ðhìä1ï¶jé׿½Ÿ/Ÿänf(_”XìbÇräòǹæh™üÎ IDAT.N,}º„B!„]å©ÓZ´.99®øáÈöö®öö.µÈ¸  "ä“Yüo?ïÿéÏ]P˜vã·¯ç}w/¿O–ÃØñËDz€.ýÎéol¸¡_¿ê†Ïœ6cƒøûÔî3# *—äøÞÌ €![vsׯŸ~‹.0ëFŒ+\ôÙÀ¡ æ𔍣K³ŠòÊÕûû5»{.ø(ð‹µ~W.êh²BB!„BÈ¿¡§èáÈ„B!„Ô9šFB!„B(é"„B!„ÿ&žÉWƒ‚B´Ú‡gnn<šÂD!„B!/†©’n­½Q<‘†££GÓ¦öìYB‘zÐ=]„B!„ü½IWPPÈùó»îË. 'ÐÈNÁ2IܫרC‡–S°(é"„B!„<¯ ÷tÅÆ^¿/¿€…À`° Ø…<exøá  ’…DÝ÷*¤²Ä•=¬€ãÐï€B*“ßŲÖÛ´î²K!•Ýžã#ü÷rMK€¯Wþõà’L!øÇîZY×ýímf̓.*¤2é8Oµ4B!„BÞPe÷tMž¼æäÉ èÌž& ~À\¤ð(11úõÙi~ƒ‘Û6/éÄ×Å]ømGJ½nï9ç@këþ·Æhër;:Å…‹”ÖY·Óé™a„B!„¼©Êºw´ÚüœœL´-—q{:‹¢œœ¬gƘIÿoçµ0™B*“ܳ´¯„e`Ö`À’à³¥2…Twð¯Ì>¶¬‡€2iôÖö–ŒE‹ _ŸŒ•Êg.^>%SH#WùXK†ŸTHe_µe×½';§4¶¬Z²Àkâ9…TÚ߃ò篳qûéãq éÃðïG{ Ë£4÷5³ªSÎûò›•3ߟ±UNËQ³º¸¼³óœL¶¡¯5¦ÞÀ•1 iäê6–à;÷´éâ©x…T–zô—/ †W¾†IÇ\½TuEk›V“¾š¾hjKk`¢Ë®ÏÈç·¾kÃpl¼$Sœö·€ÀãÓ«R™bóˆ4·$!„B!¯QÒÅáðx<>2ª,’¦ˆáñø^ä¶û5F*SHãn/ð)}Mè;yûÚ¡ͯ/Ÿ¹ôç fíÆ.þm^s¡_®šØ¶Þi£æþoÙ¹d~Þ©Å[bä…7~΄å1h3åׯzz bö~²ëfN…­8õä#H‹y”ç=±JÉ^Ïœ˜{mÝŸ¯½ohä¿hÅ—².=®w=@ûL„ÊÀ˜wêJ ôjcvsóå|°[ÙplÚúð‘ylÛ}]‹ñ;¶~â'Û<¶ÿÄ õÊæÌY5ÑMP¾†Ñ·¯»TuÅÜrµaMb½ÿ–ü–½\…âÖƒßâº[Ú6÷w¢N]L5P%„B!ä¿®,±´;;7I=%Ç-ài"¥¶ÁÜ`ãèèQqÅŒ“Oȵ`,8¦ƒ’wýø kÖÿþ˜‰°Ôg¢û;þ ¢ò «îÍ2¿{'âú¾5Ò»9úLÀ³ éz˜ô–šßìóÀ£_¾Z,to7s¨sÙF¢ƒôÙûX'ðþbwÕ’ÝÔ˜“$…>rîñ_…ý&üÔ­Y{wás+¼orÒˆìðý'”݇øxË•3´“‰î¾£“|ÚÓ @Ÿ[ú”,æÖÞÙìPù–~ÍæúW\Q²]áKš(D»ãD„®cÇm½;4y‹÷øÔ-Ç>ݺ¶<çÒ¹¢ A‰„B!„¼NIWpð˜ÀÀÅqÇoäNÏÄg@ Ø ¬…§g[{{— ëéeü´6,‡~íK’®ÒdÆPšÔ€!ýÈ¢¡ÆÀQÛøú½Ðþí€þöÜ­Z]¡þ™Ý:K~º|.Sí* _Èrm¡®l}Æíè,4®×º§Íå ÙF˜»÷ê` ¨£¯§æ%î:”9dL»ñS¬[³ˆß«e<¹×Íš°#¥82jÒóÍ\+l)/ªÊŠ*˜*$7Ëòð-tlÓcÔDGáƒMëCÞêÒsÌÇ,‹ä§“‹¨yB!„ò:%]öìY2hÐìððÃéÃëÌ c®·ñôlÛ±cÀŠ?«¨Â„—R¦ m–téwN»`Ãí ýúU7|æ´üÓÀß§vŸüé»oÇ·>³á½Ûz:­Þøì’£òµI?¬í¿:¨ùˆ…ß8Ÿ¥Ì˯#n1ê«îÎVE‰g7Í{YY¾­(qÇÇãÔŸOŸ2´ûØY ò“¯í\ûÝׇh@û0toÜÈÙ¡ß2I  b݈q…‹>8tÁü@@“utiVÕ¡UW¬¸ & )‚^qö¯X4ñDzØ©$MfþáÛhï‡Ì3añj·„B!„¼˜¿ —Á³úÕÏHQÄoû·þÜÄ€ñG•/<‰O2ü¸tšgtp·â»­H­ÕæáÈÿÊE¸‰^7Dw©aÉA“CÖ¾Ì5‰ÿÊÅÅ¿5ä¿ÛZê¶a¼WÜÿ±#ëõˆm‡ë?:î!&d“^IɶQ—÷?]:÷»ï&8'†­˜÷ùŸJš¶”ÿV{ú¿ ¡†A¤pQ!ÿ9¯@Ò¥ د‰S'w§N½†ÿájÚËuOéä»ú:urïAÝ\¯É·ZñuÄ Ñ]Þðï6 ¡†A¤pQ!¯UÒ2nÜÏ&¬š9s+ňü;¥€ÏÔô­VìMþn£P¼6­ºÖ ¼N £nv–ެ=æ¯F¸ÊïÚßÚ´^Ùï¦èôB§Bê¯Jºµ>6öÚ‰ë‹'Òptô \¼gÏŠ)cfÓ¦o›"ÕÍSÉ*=ð]»¶³0$?ºp#_ÿœgQg_×&vùgEúϸV¢Æ-ìí<ºB哌‡Ñª¼—è2eXQ›>ÎÖ¹©WÎe©é¼Vªm-ÏlÕ¯X³¯ñ+ÂÊç][CNäÉĬ;jÚY†u°õh&vñ90åä%?L“%k uqà0¶í{Ö·P%_º¨Ò¾š‡^ñ‰´ì߯BUN½”Ä,ýËÖ·\Ìuüÿè)ˆ±lÖ¸}>t¹·ÿz’Qhª9þÎãèß>HÿÝÓ c^¯]/GKuæµ3©¹ÕÞïÁ±toÐÊËJÈEQjRøµì¿ñ@{š4!{º‚‚BΟßyúÒ&™wdú´ÇŠâ" N;¶jРÙ)ò÷|±˜Ù9 ¸¦Þ1yƒòß{A‘oÙ¬³sC{¾.37%97G/KœýZ[½Ì¥5£¾0K‘—žVø2ßÚÿB(ÈË´–ê[õ3›ýkØ0ªÝYFèêÒ¾ƒƒ£ˆ«ÎÌÏRq­­\ÛHZ6üç®e¿,ÊJÌNIÊÉR"›&o»ºYsê0æur úÂź¸ò€géæ\®I”oNut½ŠßM¯À饿ù+!W—“*K((4þC§‘:lÒ„üÃ*ôtÅÆ^¿/¿€…À À¼™ÊððÃAA!k×NQ÷½ÑKÛê¯÷žUüpä}·øh.|Òòó‹yµÛ¦u—]1Ë:¤èðÓ-õ¿³×]çÏû ¯ ¨Så‘nû|Ñ©Dzqa„V’–ö Y> š¬ì„»©ÉÙ#ÃwhîÜØÕBÈôEª'iQÑêú]Ùs®¸m‹¸°øGyÆòßa& úzu³B½ð$c¡ÐFä¤Þ¼’¥1€a­½ÚÚYòÍXNn‘;»f-mí­¸ÆBMêCÅC¹Zga×¾‡ƒ0K•e&²³0ê S”p..>ÇŽ™¤»‡»…úÁe='KëÜü¸(àZ‹<[98Õãq /HUÆÜNÏÒ«–\d4½ËÿX(È‹·®¶ùV-Ï´uônfmeÆú‚ÔÌ·ól:•- »’eßÑÑ*;åòÅ,[¯]_G}â…¸˜lnåãå^¶Úðê4 Ž¥«S…ýŠÌP=Ï1.°òlnɃ6á¢\¦4~}çöm,Y¡ ©¨°ô—–ckIÃFB¾¾(ãaâýXµ0y¼T=²”åNøv­$­ÝøEŠäë7Tjã+vdióe·“³õ«fíš°®^‰á¹:¡¸mo§ª ã¡®^¹ÓNᣠ)6õkh`ñÒŒò§ “'g°â6}œ¬T V.Îf£U?‰|—¦ƒy½v½ͳ²’t–În¡&ù~âÃ$¸øÿ›ßM¯Âé¥ìÃ0Ù<â n=Ü ðšÖÜT%›lZ–å¿% oçÛ·×ü‰«ž[þ¶&MÈ?ýËáéß&O^w=€Ù¥?`.Ò³%&F¿6û,höÉö߃Þk­9»nÃ?î<ÍmýŽŸÃó^гpóiàí×ÀÛ¯A3¶ÜëBÏÎ.G3mrÖ£5SOÜÌßµ¡ÃwrjánÁ$§Þ¿‘ŸÆˆÜÛ1Y2Uƒ&1:]©}¾íOY29¨ òsSókÇÝ]›7·sɤòkW3rõ`,êµîè`o^˜–˜ÃwláÚ¼!¯øàáÔ³1ÏÎJ”§ß½gßÙ•å\k±³ ©™垷ƘY{wvnPϘŸ&RÄÖ·óñ³Zš(ùy¯ô×y(ÈK´}ùVÍÚ¶jmc¥Ë•ÝM}”óúÍ1Êr dVÓìM/õ_¡†Á±®²_®Áóã\kk1Pf&ª Å?„ŠR“/‘S6HÉ\äÈÏOˆVårøv^NNŒÉ#Ñä‘ÅËȱjìÒÊoÈH¹Q!ãzõŽ,cÞUÀY ŸÕOQzÚQ¦1âšXVùVÍɹä%ª'Îϸ#-ËÀ·mæèlþÜ=Žu.Ï¡±%EI÷S”€…È¥^ñé¶ÜÁõ åatÙnªøâª £äßÑšITÜÊ7°®~N✴»·TZ3a#[+îÓxŠEù™QiFÖÙ×ÕCdøw¿›^ÁÓK•æ¡O‹ÉPê)¢Sžä³«oZO›kJžá™Ÿˆ‰s˿פ ©[e©†V›Ÿ““‰¶€ â"oCgQ”““U‹‹Sf’AAßÌØÑ……&õÚ®y_Ÿ’Ìÿ¿ÙµuµòSoîÚtµÛüÈ´'ÿ×vÌ=·ñŸ/ÿ¿žž–ê$™¾»eöA]Wxÿ!æ©8³ã¶G@/ãšî“N´œP©ä$É„Sa£E.í0สéħÏr~\:Í3çþåÇÎ[ˆ /|;fɶ¨§ß¶Œ•w{OIá¶ï;'ÏÓcÝW ë3…xM¬©@Õ­3ñ.ÝÞ²Õ'œ^qW2zÄ_±êÚÖI£~½eXó79m¿;¸xP3‘@Γ‹¿­ú¿e—3]†º½­ãÙ´r/:3fðÄ9¢w¼¼¹“vÿLÿé×s_Ý 3|Ûò÷#<}Õξ¡9tÉO"näIÊ—tj.tsg3”¬­¥X›•ðøâMM¡àåä7™©²ìœ}¬€^#»$×ü?{gÅöð3ÛÀR’’"‚ *¶b €"ØŠíû=[TìD l}ŠÝ(¨ˆ +ˆ­4,ÍÒ Û1¿?YØÄzê»ßsçÌ=gÎ9sÏ™;ÖºÆÆê†–j†– .ø›.R1ÒÖÀ <6;1] ™bu'c :¹¥±oó8R’ZA;º¡±¶z¼Ì4i ÍcVˆ@£&Øt´õÈ ÊÈùË“JK TI|!ÙÈDQ2%ûW} Ѹ·9Ùµ¼::´”‚K$$ª6U„ë«T° fMƒrжÒñ7FP/¿NÍÉ)Œ-«¥—*‘ÀoBŒ(d€T(‘4àê¢Ò¸·¥WKî9™N%¨ê(F¢ºšLC!²ø\@[¿ƒ6s>¼*áþúO#IÅ /¯?§€º'¢Žƒ‘µMιɕ2ÙI‰eå2n¬Û¬9YÀýẁѵ̴ÊÙ¬2±$•km¯f`IO-æˆpiMp¥–p0ºŽu¥šiË–Jé(-JM/ å6jZbNJbé´Õ2¡R)Ÿ§yÅñ±ìr°ej}íÕôMÈ©qÿêµéL/u܃"ÉÈæ”Yëj“Ä…i%¥zf픹+­–»µ =#JrËïïÒDÝ¢‹@ ‘Hd(RhR˜#‘ȵþIìv"‘Q§¡J§¹çÜŠ#÷,ŽÀœç/óZV’3-yñþÙ¶åOŽ,8•F¶è<°eEèúÓN¦[UDo[r%>3ìçðuÔ%^ÚœiéñwKzĿޣ°Ü‚Ĥ ›ÙçÆÖ–œåÔ°jå/ïºe;q³·ƒŸK˜ó•êäqÎÇÈ8hgcê~œá¹ožÙ{ö®È ËS£—>¹|F8Õ«W¿¿×QO½zCc–[·©Ë_›ô¦‘ã 2ïlòg“Í]VϾ`Õ±ŠåtLøpë¤Ølïà~^ÝuÅt›Ú•ùÇ>”ÿÊ#o……4*/—:à %¸°T Y$ŽcÅgY™«Y«€„Ÿ“•Âþ¦.TN(~ž>üÆhŒH¤d%‰Ù9Ÿ€H£jhµêÐL¿ƒ1» ½Bê¬v¨nM£R RAõåJÂÏÎ’¶Ô0Ñ/lj ,É*‘ÉyFV!bb®™¤4‡€i™)• Bé¿i Ä7xKZ¶H>UR Z7·0S¥a ÊKcUy¸ê/KY¼Jpø5C‰^˜˜Õ„—IÄRP!ÒHD ªŽÀHZÍHüRAó Eb)àR)^i²²H¤«)F`UÏk0 «j‹ó¿fŠŸYD2$2ùŽÖvŒÏ#àê´óÅÖ@r®’-KpÀ¥0 Àÿ s4-š©à˜ŠE'# p¢®¡JyfýË&•§èÊÌ,‘à2b±X¸LX~"qåƒgRX@V!|ã=‘o´Ã¯˜^”¸Gã®…ÕqWø‚3ò‹¹4ñCŠ.:]ÛÈÈ:?4 ÞØ}žn8ª2MCCËÚ;…&L­Õ¸é=´(ÃŒR:z9{£7ÊivË¡Æq\€ú€ñKT?~xsõã#GZ `Åˉ c¼ã“Û¬ì¡qÀw½e@·ÅîF5‰÷wqº”)¡Ø¬¹¨(¹Åõgfr¢oÝÏ|$>kwÿ6Ý[ª\É,¯.’' c{/5ÌÁƘnÔyÄÔÎ-D=V?iØT)÷ø_*yÛaR¯Þ’è‹þßêãƒÜÖZh6W#6ÖãeD3¦xN²6ÕÑ×uCu"Ëë@³1Y>xzŸ±ƒÛ{“!þê…xáoèQ¸ˆ+Áª¢C%¥‰Å€Qµhd!G‚‘›Sð6Q$£«4k®ci¡bÖ–žÉøÖ©}~ãÛCTóÞiÒ¼ì1¡@P’É.°l¦N'Ñ)ÀæIˆåñé±¹ h* âŠx˜ŽW=2N›ÛRO߯'?‰]!¹¢ ó¥8U“B‘£4ï ¯-á •Hþº)ùïh Ä7x 5£"L¥…q[sŸ™ÉHäŠ4ŒúôÖT~Õ'8†Q(Xõð[Y¼}Í-ÐàJõBSb\Æ)/•ièhé˜hq˜lF35¶³S#ròOKªËÅë¨+V‰"Mc…ȪÈʬܡ<>†kÜÓР½^fQç—Ž,LÅ@] @ZZΓÖëŸ #Ûâ` %g±¬r$€aP9(ÅþÕœLQ33&FW7¬™ƒ¥™ZÐrbùõ@¥ŽÁ'ëþ%ãl5UU"[ 2Jñd8|ëRßb‡_1½4äõºÔÚWIhìŒüŠ.@|ÿ¢Ëߺ§çú”{¯ÊÃß}òÀʪ«žži­ý¤Ì+»*Òè^UtULþb)+¼íãŽ{NvµïÔ¹[÷>nÎz®.û!I½¹][òg ÈÄúà #«ÐˆB‘ÜA½M'“Ìû[½în¢†íè#ÁËzÐ[w1"=‘4.—J>'ýª¹“Æ=·íì Œ¾°mû'êDŸµý”esArйÌé·[¼UO$}y44ó÷\ØC\XĨ™v±g—ˆ©æ4ñÒ˜ЉEWš´¨8%SXy Ååb)Rna«››\\ÈÿšY¨:sŠ_0¯¨ÐÖDÏФg?nq…Œ¬¡¦MTäWÈxvyõVzF2ŽT[׈,JObV_A>Ÿ}ni[¯6AV¦HVÛ=$ÅìB±š¾±±^Ê!ÐMŒ¨PÌMûÈ.·ª+™ñá+kÑïe Ä·x àøg¯.Å €d55+u€˜ˆár rÓø<1¨Ó›µjƒ—’Õ[T?8C31V/ÿŽcÕZt46¬>º¬¼4·2ÁÖÖ«¾>+q\ÀIŽk¦ÝNÅ¢¯•n!_D¤6kFÆ@’ÏÔ«&ÎËU‰/•EVpË‹‹Ë¸L{Ëf­ÍK^1¿r‰µYTµ–…8FRUÑÑ!.H«€X¤Ô1j®Õ#ᆌ•%m,9 e ò}uúsÑ uuI ee>©W]³õz ÐS1ÓÑLÊ.‘ÉÅ“_ý»‹UZúuSt敬پž]ŒéXªˆó²„R\å_¼6ý^é¥×’èu®’Ï=ü’.@|j=²´ÑÑqZóÔV$ ´¬;¦æ§eg>¸gO7?¿)‰¥?x–ÐjÎü¹ã]ç­ó°È ‰¢Ì\·¨cÅ‹ÀS[6\Íš*„e|ýN.®½zÚªåGÅWX.Xµaù¬ó;P¾TrdJQAظy{ž1ÛÁ¸ÎNz†¹h×¢¨»Ë«QY»ß’Û¯Z¼dÑÄÓm(E1áb)§Aß`d S]"'--³\Õ´9­¾¬Å¼zõ#€žTD )ú]—CsŸe¥±„dÃf¦æª²v|dV.ç¦f½K,çk4kÝÙ¨u 2/'ÿÝ'žD*ÈN独k®©A©·Ø­oµ(ùkÛ·wrbŸåf eêjúFêÚjxE~ñÇçyl à%ŠE*f¶Í-šCYzî›Xž’ÂH&)brexQI¾ÂErâžçæ²e&:¦Fd~^Á»Wl>G‰äNýÏ1â[¼j¼ZƒÌb1 ÄÔ&]{jó985iT™œÛø)Ùåb’®•¾ǬžkQ/ÿšcµ5››Tý7§ˆ2õ¢JÒšã8—™STXt=ºN3’˜Sžö2->¿¡gœ”G¢²È’«ÜpNJ~4ÚèSÁÈ"73Ñlnª¡§C•”&=ÏȨ|¬\ÂSêuìÁÏhÌÁÈ'ç¯èô2F5´TÁ@šÏä}VXÆ-ÍdÕ[’0yw"Õü®.b+:FÞ‚¬`çT̬4Õ%üœ·ÌRü«×¦_-½|õuÿ+Tÿ·\ø Ô]³ïæÍ=ÞÞ‡ssS*?ެ§g®§gúïÍ‘©KÈ[W:/ÛÛD¯ÎnZµíשDßkæ/€¤ðÃík½/’ÝÿÊîö^þ»]/Ïð—ÿ²cº[gvœ°ØäÓ{)H„u'\”IŽã sv8ô¶¸nÊ«§%BUù}´ÛMö`¤.Î~rrÅŠçìšì++}|ãåÈþ#<º‘¤eÉ׬?ù‚ƒãá ü†l”üÏ¡‹ŽkÇ{® èü"RRof“f‡Ÿ‰^²»;¡ðzPTé/œ5„e¯n—ÉÿC’—ñø¶\éÁ+gÆ”3ëæRqqbVqb]¥ËâÓ"~™¥1¥œÒ¤¥IJ/‚ùïò j_§‹¢nÕm–“ñ$GîoAiÌíRyùñ‘¥ñJFüÔï-µ½º(9íó–÷•·ˆ€/ß Œõ2›UýGAÖ»ª0R/?Iù»;qJ·¤E)Ó«i1.ã± >°”8íÀÁKß$>~S~”Å‹’ÈGÝ.®þó18î×O¤um­Ô1j¥\ÌW~"äm~›ÝHr°cjÚÔ²öÏŸÌH—^×MÄÙ‘qÙŸÃE^µRù8RpŒÒüg·ó?{ò›ÛŸ}@ü0®r©¬r¢W"ÈzÍJyõ ]›þÝô‚óJ¢o—4æ‚ÔGq© »Ví«¤ô ÎÀŸåÒ„ü´Ò¿ßŠéè%à ŠòØ2í.“½ÆÚHÃg»Í¼ÃþꕺHî1XÅû÷¯|Wê7‚ÖfíóS^FYþ&ù'ü âxã•Þ±#Gþ°˜=ðô ° IDATwî©y RÙ™¥‘1j³nƒ 饬g‘ì}ÍXäē͔W(¿@ÝG3ë;r¾½ @Všæ·õ}¶ì¿xš0­Þ“Ƽ½t-}§@ øCøŠ.a²¿ëpÿï'O’vaHó ¿á¹ÀK¯íÐù$@ ?åº+ÿ@üH”?^èí}D(ä$UU ÿiÈLÿ•ËŽ^?E @ ~pÑåí}49ùejê›Ê…4 -[·î´Y ]@ â[‹.oï#OŸ^ˆeF€#@W€"€P çhäuóæd,TtÀóÈÈ?LëÞ}ûþ‘z!~œ·ØÿéßÚ|8 ”pYÄײRj}§+99&6-Öøì¸vìèè[ÞÞÕËéh ¸Äb0³÷ Ô è¿Îb0Ó¶÷¥qw4ú^`1˜ïÿg÷ï}¼£9ý½åöÛp&‹Ád=}±ÛÌvZ„ºÍ¨¶Þ‘,“1êöçÃèv%°o÷Û©"çB @  R³ÆÜ¹‡BBŽÁ@€¿>×V@áØŒììø?Hi·cgv Vƒò„»Ç£Ò$zö®ÃsöS©¨VC +Âχ­Qò¾P‚\@ @| 57w„B.‡S ]å*®Jú€DMÌá|Áò>ÕbôÒÀ—aLƒ™v#hË Tc—þO’Lƒ™r㊯Ë_wwô}·ëLFü?Ýé˜Z»Y›B’LÖãÈç¡Lãí~; ‹ !,óÍ&ßÓ>eÎkEW”Li;;œÅ`;ë@þwR徉ÇÏ=¼—Âb$EoŸf£"ÿ0J§©+«àåò³ùóóÝ2±×˜©»ß–ʪ÷­>îüÎ]æø.ô™ß^ƒ€Ñ¬&¬¹“À`²ž„nïLA¾Ó ²Ru_Ýôßjé{izÖiÏhÿªŸ˜væ²x8ðÙ×¼âSù afœœðž‘Zõ9ivNàÿ²Š$8ŸûéDÜi¯˜CSbŽ,ŠÁ£—ãþ °¡`| Ã"|èQ²ð±Dx!dÙ@‚/Hß`ˆóÂüæu3j”Çâc1l€Œ{~¹§›ÛÈ1³võž#ûÊ>€’ΈSOMéçàЧúgê•i=i@²”uÕ«ZÎ@¯ó™âF»ÑX–làX¹—çL;Îׯ—£q£W¹-¯ÞXwO…Î+ZõëüáeãZšðßîX|²ê[*’œ‹\çÜ+úƒ?-#J>2¶ÆiðÛ…<ÑØhâùº/:ÓÎNÀztO ­•㯎°ŠDuTÆîIˆé»ì:ÿ¬ýÔÅÚœÀ¤L)ò?!ä ‡"5°Î…Î"°Z…Ο × r‡wX­"á7U/‹ò?˜:ÈïÒµë¬#vˆ˜Û˜­9wíúá±%'üž–Ȥe±×6Ì÷}Īz$@Y¹ášÂVqæ•§\¹y~…qȞ˵fk¾°5Y»3dK¯³O#"žE<}t|j‹1‹‡ëïIC’™ïøývßxñ,ìÔ$3rÃj6>°nÚîuܠѨÛy’¢U¿Î~Äգަ Ì`d·ìaBqÖÍ]§cùüx’b>ùÌãˆg‘¡GÇò.l»œQ{À¸=(µÎè]’wwí²s"çí—C?|pi£ëðÊc rçHœ~qùòÛ*ãün<Š»µw,åæŠå—2kË8ïŽþµ5aàöÝSlé„oëáWŽRÞ§Uüœ“«€﮼m¿Üo븊[wÓD²¢§çzM²×¨÷ ^²™ç±à‡>|ü(äâbƒ'ûÏ'Š G}ò,"âÑÁá­æ…E<‹ÝÙC 缿õÑ §ê³ëŸ¸ß#K(n&_ØÏhë{ñÚS³°K~¡ÿÉOÞþ.E®mdd ¡ïä#à ¨Ê4 -kïXrüü‘ƒçžŽª®?(ÃŒR:zùÞÑM—™ÆCŒÅ\€ú€ñK¸ömž{õããë„bàåÄ„1ž}äš8öÐÈ8à»Þÿʹiµ´ñþ.]Ç´áuûÞŠ’[4|³)'úÖíð«þ§b€6Ý[Ê—h•Kˆt\ÿ¹üøyÐãçAa·§µ¦Ö9îê+ÙÒšà6À +Àwí¾+ço¥‰þË^#¦„lo5bdk÷™fë¦ZíŠÅp톯¥û¸Ö#\ÚLòÕÎPîÒIJÕñÙ®Q,&E³˜0ÎÚ}œÕ²ãôíØÔVg31R®Í°™x–*Œu­åôÓ4Q!bRäªÖ‹CI2¨î™lq§àç­^€QUZö òR…òÅÙTßN%ÿÑ}¾ü?¥ù¯XÚNÓuuUS±4pœeÐ -ùÇO+0 € {¦@• `1 ò·B•o”ËÊ]ÀÊç÷­¸'™¸.]8ؘ ˜ª™­¾0¯LPóRÚ}µFÔïåbššÀqæý«I¶‹–w׬ŒP‰’6rC…­å/"D'÷Õ#Ô;yûmQS}qj²SÎÔüÿêžG– ½:Ñ1¼ôѼ¡ÞKÅ r8Š’«ãâüÌò´c³F9Õçj"oXÍFQ¶»8çÁ¶™£ÝÜÇÏ?ð¼N VW/©R£‰rîúÎpwæ2uGX\žRè¼¢U%Üè5îsÖ._ ¶P#A­å …>®¥Aç>U:.Î;³bãÛ^›÷Lm¼âRÚC¡ í†ïL÷qcG¸¸Lò½Ÿ+Fì_èáîîæ6eÝufÕm7iqäŽI¶=-³£-›0ÎÃ}ܤeÇ_–ˆrﺚ”zÙg{x±Âíî'þ ÜÝÆŒr›ø÷™we2¨ãxbå~(+¼¿dèÀý«~GlxÅ«?  .JE\!FÄ_ n6a˜Ñ}ÍV&âUðedUrýv“•½¾þÑ`̤QÚ1W_—ÉJÓ: õŸNÃ1. %U®3 D÷÷aÄ—]þþÓ[·î¡ž« n°6€•UW==ÓÚ#æ•Ý;·ìÚ÷(£ÎUº*T*²ÂÛ>î‹ÿ¹ú( ³êã6oé™k3m©Ê®?¢ÆÀ©-¹™XÿP#«ÐˆR‘¤fiÑ»86±ë¨Ï6¯ýß¶œ/°‘ˆ¿TðŸŸ:ÀŠÂŒŽ¾šèÃ-fqÀ¸ŸtQ %Þ¾œÙ9^÷K™Öv¼[Ó*.¹I>]}D™~,èêíËÛ:Ç=È—EùïKè³ãü•«'g’î}ÅÁç¼?±êoòžÿ9P¢üdßwáÒ•À}?îñ£1lþXkËq¾+ûëÔꃬðñÿ”þ».^»~neËG›ö¿agÖr¼ôå~HжïAXXxÕÏãÛìUëOSªvÓGñO¯ÜÊè0Û™~>gg{5¬Áù ¯A}úôñ÷C½Y¾“,ë½ +‰º™`2¬G›Þà >\z.ÿplÓ:)«ÑÜuºÍóÅ£\†Ýš;hf?]B­³¤<º¹ß?„õSkH´qÔ¨¿¢£ozdJTE˜S•jZYuíÙÓÍÏoJc¢Déžå-po5gþÜâg„¡–¹!Q”™ë¾ <|»×úýcÍiª–ñô;¹¸öR‰KˆŠ¯Xld¹`ÕÚ¼[ ÿ‹$G¦Y•€›·–kã`\g'½ÃÆ hN^=Pv?Vn ’÷æŸ=ƒ6;ôÜóäÀ‹Ï“ª_0“!Îf|â,2²ž>mB6ÔRY?ÿ+EWZ$ÍØ5Ûˆ ˜iÙ¨6ÍÞN·Ïݬ®yE?ƒ©UDè+¨*BéÏU3ߪ,˜¤R>A—I4uà±oª7×àt+ꤞZ$(á»·Àé´:Bpzû’Îú/Š8FwU]óµüøUš“8)02I¯»É'¯už‰FŽÃ‹¯+4¯ °êiYvÀûë"€Ò¥õŒ¿µÐ‹08Pg²½òꌃ, ¸£Á²ý"û9y7!hõZF—5~ý´±øUªžPž(l#¿—Š+’Âù/­Çߘ¿yûs>öjߣŸ÷.z~9ÎÚóoÃÊsAPÑ3RÔÓÏÚÿ©j `;g—-¨õïpjk4˱©ÝhÄJÂtFFóa½ Éf:ÀÅ*$¿áIH£ý˜ªÕ {]"Aff«#I•«U:/ngA®eU€fÞµ¥*F”hj[µ7¤QÝ@U’*”Õ.(¼NDË—ÌMîïà0ÐËÝŠö#ü¯ìC±çÿÔ…‰gü^÷üßn JÒ È3ÎMu<EÛªßÌu£MH²ZSÜÓQ+Æ=\¸ë^÷Ÿâów_øo·N\ü H Ú{×mÝÕª³F¨ã¦8Y |Løí‹è°¾NÍó…=4ÕRY²Y=*òÊé æ§¨"~_‘ '†©;xT‡õ'¸Ñ«ö¼Ú³]TëKS4‹+vÀ+^ï=ßb⺒Kë·2ÊuzN_:¾½º¢IÈfž§Ny·']Zõ÷}kÇ–jõšMVøüVŠ™ëZr³žC®…å:y˜¾ª“õ&Yqø^ÿ·Sw'X cö-Ø|¤÷éeíj"²žè&êýøF(/ºàæÍ=ÞÞ‡ssS*?ެ§g®§gúWe1sdêòÖ•ÎËööQÁ«³›VmûÄu*Ñ÷š¹Ç‹ )üðpëÚcG÷¿²ûŸ½—ÿn×Ëóüå¿ì˜îÖ™',6ùô^ ¡o\rW˜³3Àù ·íÄuS^=-ÔZÀ]»ÝdßFêâì''W¬x^ë}_qîÅéÓ8Kç{{örÓ@Tœòþñͨ¼—(,~à¯ã&»g ßp ãËפÿ¸ß|¾ÛHÀ+|d2ë SÞiX5ÑHXkh€[NI;î!"Ùd¾ªXCÈ1ËÖxþŒªß¯Ðm©™mQjIà ™Ö¢ÊÑt葆^¼päÉO¸g@46š¸ÃTWþôÖ­­±fÃ,Ú3âŸD6“ h-èÔë¥E|mc‚‰w§EÞ `ÄAßYùÓ¡ô8 B¨T_£„:™„6`±T¸PÞ2.€åäßú«^2öË€¿v$ö]¿ojuH4Õxy)A\Z$ÑÔSgÚà/–ö_ñZ =âð… u·jh43Õqj¯E$B{§®Ô=©9³WWµ¿´Ü–Òô>Ôµ¿ɶg§Ž5ÒOMm^}’¥%¯ïDû°«ü¶F¢Ñ´šØÆ¬¤JøœY‰„†½†¤^×h¥Ò@$UÞc¯³¯bçIXÝ3 XõîFø"ºÝâ³—c"##ïoœ<õ”ßÈæßy–çÆ3D]f6“d^{ÿ.ÖГ¸D(’ÞšºNû9ëþÐ C6Ÿ|æÔ,Kù·éë¾ Dn1vÅèÇËv‡ö’B3‚¦µ-ý,#…×ÛNU¥Óêë«ñ²Ç '„|O‘tm­ ÷_çˆ:µªšå¼NY×%çQÍ&ùÌîoÁW}=ÝwÇC›mCô«O#÷ùRç5ÁH«¿‡²ÂG>³NÀ1ý: kKM< —}>º„““ÏËHÆn[Ü’üöÝrÞ?—‘-§ì;îaL\È.â«êò¿Ä##O©ô†î½7ô ðsüгîq\—ë¿Ùf¾èÀ°wmy8Ðß­>Ç&¨Y»¯š1uó±>g—uT~kLÂzz#¶<=}šó!)ÏãÜÏp«¶Ý×uRq¹€‘¡ë´¤•* jçܰóC¡¤Ýç ê‹nìLJ0¢þ¢ æ5´Géæ½j‚¬ Ø­yðçñiò¥î—vÖjkÛÈ[ÛêÈx·oQÏ}5Ó5êÜw§Ö=`Ë´»LöjÂÙÜ´Cš_‹1e’¡ôù¦Y6Õ£RÒ… N—2ë©£p^æÝ-+înQØvAþ¸’Z?ö™ÙÉù nÑWÀº¯žçÀÖ-T¿“D4BÎKU ç´™îB=`Tz¼ðφÐ t¹,wM W€ú@, †4ãS?Šû€®Å益(=hÕδá»w± a$ý.]±¡ ÓZÙrw2̶R\ÒH±^o¿ðgŸeRêlÕ4Öè,Ûž2µU[,íÅi«¦ríÓ/4½µµÈ|™£ßÛúópHÆ/Ì« ê)ÈQ××U\ÕXã0N/·=4É’síÕa‡‰‘¤iÝhÌJÚ-ú´È»Éê?B¯’Âoßà˜Ü¸W£5#Ö»ª¦ÐyCYÝ3ûuþ LðÞ¯³Þßcz×v»:£Tß{Ä&Ha°mG(äyçb÷L?Øæ€Ÿ³.á?ž†¨––;?šs>³ùd ¶ò˜b>Ów«Ùšy.Œ(åé¯î<Ì•š÷ŒTl'yhÎß°ßb³÷€4AFøá 7ÔܶWå=ÁHAÃ~þ*‡i>ÛîuØ5¢yå`JM>xöŽœ—IÎ~3ÝÍðÌ«¹‚Ö8Ťk‹¢+Œ\7#n´ßÿ.µž@£u´wîºpOˆÃšî§ï&¸Ì¶ÅâN-Z—¿ðì#H ËÿÒÌ{šfß~’í0ÚD”ò˜cµ°YjÀŒ5Ž—µÆC£&ùa½iDV}6ÚvÊvì¡„L§‘(ªdi™TÊ+ÈçjêÑ”ù©ù°Ån—½÷ßv?2ÞŒ¬¸]œýønA÷7v Ð&ÈØa+'ì¿™8ù¯v4hz'ëM_úe…!/s]F‹Óž¿š{7û‚Xü !Œh°èúÙ`4³¾#çÛk‘d¥éa~ûWßg£%W~å¢Kw`îì×fsÆê«è õUe¢´õ˜bu“Ÿˆµ ym,UL ˜ñ:âóæKNÈYg²qb ÈŒ‡fûÚK1f×GR˜Çm§ëw檜V`+Á…à ¥˜—öÕÒb»ðè¿TØ´4<¼øÊ+ Rm—µ%]Î [™YÊ¢–ZË-ÜGj‘§üÙPÀà–A’!Hd@î w@¯à/äüÄÌÝ!u2ÐÀö[>lÊ={îS÷ÓT§}´Î>—üN\9d㦉cø ÑuöG=%cŠeCm·’uf­¼uëä±\œb>|ù†ö*ßÞ9dÜœl0uþüxÎyîãuÝ-èÐ`%=©û¼´ºqßÿ-{ç³|Ü5œ¬ÓeÚÆ‰dЬIÝhÔJ$rÿ¿çظh| IÛÂL»‘Bk«`´˜z§´ëv^ë[ת[Õ¿Æö¸M0_·tÜ52‰ Ýcž¯õwÛJ˜Áȶp0A µ7h=iõ¸ÇÞ•ÏV ]6 œ8¸{æ¡,.uZ÷qùëðÄÎ5áD6·}'å ÿ’Q[JeÔfÖã·ïcAÅÖ¸ŠFçy«L[µùf§}cM›fujë1£uÖ-™üDWÛж%U,Òèµlé»ËÆI1zÇ©œô_²é¨eÎw—øxô+»oÞ8q¬ˆÆC—ûÚÓI‚vñ¥óækœ ˜Ø¢æè½A+&l\æyˆÔÃV¯é¢C7­åxÃ[Hj¢Ö“¦DÌ×±1ë-(è9Îjýæ%O)ãÖTDÌ÷º;þòÁÊçiÖózÜÙqøÅÐ- oBˆ3B”w_Ö­òF3´{L°íÕ÷sl»Ó±¦v²&#ÕÝD&;®˜»qÑØdj ç•뻨}Á| µÕa„|ŃL€¨)§¾`ɽ瑑eotoVp&8ˆˆú¶¹ºög ÿaK\ˆ³u–®!{ͳùa÷îÛ·R/äˆ/ôû?=s¾J8È,Ȉj*böŸ"Ì\ÔE™ñE¬åwº¼½…<¤ªªáï? Y!óCÃãÕÞw³Ýúƒ8ÛIžE|ú¡Gñ÷ŒþŽÒuG®‚@ Äo ˆg?¦'ª¸ßI¡Ü:šœüòÁƒ£• iZzz® Úˆ,…øÌ.~þ-P™„@ ¢.´ýz!+ ¾kÑåí}äéÓÀXf8t(VhJòÝ—£Fýuóæd,@ h*µÞLìûüe[€æú 8Sn o‘˜ ”6ӯܛÓË øá‘K7Š(-úôÀž~Æ’|m¤|v~zTØ«7q…‚_vÌO6žxvÛH3õìÈó£³µ;ä0®¿$äò»béw=пj 6 @ @ ¾/5+[ …\§ºÔYÚ¸HÔÄN `ê6Ý­ 'úú¹«Ç÷Z>~BO×3É"  ™s224•Å`æß9àÖV #YLa1˜o6ùž¾ð)çÞí¨gLVر!`Í\÷%²oÚkhv˜ã»Ðg~{ œf{œÌb0s„Ó’¢D,`jífm If0Y#Ÿ‡2YŒ·ûí4ä•8¯ÝbôÒÀ—aLƒ™v#hË Fi;;œÅ`;ë@þ÷ª~&?÷ð^ ‹‘½}šŠüM@U»É‹{‘¡4t¶ëª ›÷-¹èÚO^Ò×th`¸¢RôïeºBèýŽ?f²žþ3L#茾ñŒÉzèï ËeQ &ëÔDcòi@ â-º‰D†"…&…€‰1©ò‹ 8çcd˜ºg„&'_½ztÖµÒ )ÐÚÍ<ÿÏÔÎÌS^γ½²mÇþoÿìUå[sÇQv”‚ø÷·?ã­·gM‚fWO;2ß=[.w(Z‡Yg¦ ²,¸ºeçÚ-¡ù&V¦”ˆÕ´ŸwÂ×ÑŠ’xiû‘ ¯9µz[y¬ÄŒ ›ÙçÜ{ªÆìY¼eoµ›×ú³«Ú6öéÊò—‡Â—ÍU IDATw­ ˆ•™9øø¹˜Ö¼ïFÔµ³i }ÿøM)87%ôE€J[{êëSÏ•’|k(¢íÈ홫hwÝ… Ö˵%]ÇÖÁ .42}á @ øµ¨),ètm##ëüÐ4x`Wý_)ÀP•iZVþCœ8iÛ{Ѩa6Æt£Î#¦vh!ê±>ÓÕ±8-:íTµk‹îFÔ›ïïât)S@ïÜf…×.æ÷^ȾñŒ>wlá<Ð }ßFŸƒib€ T›• bÍ­©=ô2ø®÷O£ è¶Ø½FDõ±(6k.¤;tôr&öFo”Óì–CZ\o°&ɉ¾u;<ó‘hø¬ÝýÛto©r%³¼Öv¥¯<•E_{ÀP[)‰Å²ïa e&žðFÒ³g®6=¬»2Cß:õï×>ÜÔ€yûÅ×?ä‰@ @ ~tÑåï?ÝÓs}ʽWå ‹áo€¾ù`eÕUOÏêm:™dÞßêuw+5lG ^ÖƒÞº‹9‹H€ÌÃKfÏ«ùã‚B.Õ¼ÖÑ*â.Ü,3½ÛÌyiz>8Y(¿ÈJª_“”ˆ-S I½­ƒËªË¥ZJ“‰õÚ#«ÐˆB‘¤fiÑûøhÕ¬££æóˆ2T[êAàÇÇä‰*²”¬¾Ÿ5ê)/¡ßz=íNžm¨’pòè‘.ý8NŸB£A¹bäÑ@ Ä/[t@PÐÆQ£þŠŽ¾Uè‘)QaLUªieÕµgO7?¿)•E—v¿%·}è/o†G%²1³ž6€¢˜ð nzðÓœùf^s§_ʧXôäPvhÚ?u'ˆ ¼Ÿ=}R÷Év çod‰kõ@”~/<{¾§åÒu>â;±Róδ »•ˆõ~_±ØÈrÁª ´7x·~]½Déžå-po5gþÜâg„¡–¹!‘)EVe`ãæí…åÚ8×ÙI¯Ã°±š“WT„ÝåÕlá½=çÿbèÆžCŽß$Ÿ{ÀÒî?z´àŸÎù?¯¥¤ßÇÊ„LZŸðâfØ[»vƒŒ½Qñ/ŠßJ×rÈ a¢š @ øµ‹.¸ys·÷áÜÜ”Ê#ëé™ëé™VW\ +}|ãåÈþ#<º‘¤eÉ׬?ù‚ƒãoOœ!òùÛÕ}íjOA^Ü-%Š» “‚/¥Lú«H£¯…äÔ]ø÷öÈD/ñÆU£Æû¬$?ýÁ‘¬è[Šb‹Ÿù/;¦»ufÇ ‹M>½—€D(­óäïÍ‘©KÈ[W:/ÛÛD¯ÎnZµ-Ž+ÌÙà|ÐÛvâº)¯ž–j}^\»ÝdßFêâì''W¬xΖ¿&Î>?eåÂyî¼–`ÀÍ}°mÓÍr¥xßÅÊ„ˆAÊzò(¬­ 0,4GP̽õºw†âÇa©äÏ@ Ä/ö[öšb:zÉpƒ¢<¶L»Ëd¯±6ÒðÙn3ï°¿z ’Å„{ŒVñþý+ß¶ú¯‚¾Ó…@ @|wH¿g©H3ë;r¾½ @Všæ·õ}6Z¶@ @üŠå 2â3_r§+%%åÓºU«V¤^ˆç-özæ|8 ”pYÄײR”ßéòö>"ò’ªª†¿ÿ4dAÄï‰+‰™@ ñoŽKÊ­£ÉÉ/<8Z¹†¡¡¥§çú  ÈRˆßìa‚(øuyR.¨Qˆª* Ò÷1¤ök'ÆÑ·Ì@ ÿ^Ñåí}äéÓÀXf8t(VhJòÝ—£Fýuóæd,į+æß{-¹ñJ¹É— Ózcø°ôI+]r/kd(@ ÄÏ£Êÿ‘œ›ë‚|ö\€ ;vtô-oï#Ÿë4ý¾“öߌc1˜,Fì»ÀóÛœL¾á.ª­w$‹Ád̰¢üÒÕé÷Õñ# K |ÅyÉɈ$á ;ìU'Õò1íñ)ÝññÝ\-ÞuÕ¬¸òB°å–,1Ù @ ñój‰Ï¿Í{($ä ø àsýÓ`ŽÍÈÎŽ¯ü¥ÍÔs—gXÉ2ï>ö±B­e׾ÆvÖ÷ ÍþÚóJX~>l’÷…¿ðc_ß]kÄ‹Jãe%˜ANkZ3s³n6- LŒ¨TàV”·)(UMÔx”"ý'\¶zI]Y @ ñ¨¹Ó%r9œbè*WqUÒ$jb§¤rX«nÓÝ r¢¯Ÿ»z|ï¡åã'ôt=“,²Ñ9'#CSY fNðnmÕ0’Å„ƒùf“ïé ŸrîÝŽzÆd…¢ÖÌu_"‹ñö ½†f‡9¾ }æ·× ÙÈiö±'ÁÉ,3÷AÈñ1-)JĦÖnÖ¦d“õ8òy(“Åx»ßNCþXYóZÑ-F/ |Æd1˜i7‚¶ ± a”¶³ÃY f°³>ä¯êgâñs殺IÑÛ§Ù¨È-OV¯ÖŒ8~÷N2‹‘ðbÛ¼qvßKe1^ßšm¯Ah´¦Õ}û“àƒÉb0/YÓÛ€µì™}÷æóH&+ìøPM 0ía»ãXŒ·»ªÿW¤ÄòË‹=ˆé¢™ÙËpДqÃÝ]¬mmtµtU‹Ú2²Ž®®µ­Á|èh%‰éi(ø@ ?»è"H$Šš&ÆH¤ÊgépÎÇÈ80u?ÎMN¾zõè¬j¥R µ›yþŸ©™§¼œg{eÛŽýßþÙ-ªÊ·æŽ£ì(ñïo~ÆZoϚͮžvd(¾{&¶\îP´³ÏLdYpuËε[BóM¬L;)«i?£%ñÒö#^sjõ¶òX‰6³Ï¸÷TÙ³xËÞj7¯õgWµUiÄå/ïZ+3sðñs1­¹ X¯Ö F/}rù ƒGiÑïïuö¥ÁWo¤‚v·©Ëkm d†_Ù4o•÷¢#Á<Ó¾ V-´£ÕÒ1áíƒa¥@ëçÕ]—¤ÕkjWä_=ö¡üÇßXõbÇšá×îXµcE^“vÇKóîú¦6|/Tú8¶”$Xjõî×[W_ðR!§TÄáJy¢Ê6 Y½­9+ÆAùwÝdì¢HßðmÃîø ¾»sé§Ø¼ïÿù7E]D±ïvNOüú;½RÞó9a™èKu?6LƒtxC„=  @ö>v/„,Hðéï{rÄ9ÁÆõïÝ·w¯Sö<+–ÈÊß™9¨wï^}G¯¹Ss³_Zð`éPï§•¤¼Mu˜)n0¯.Ù·W¯ý§øÇ”ɾ² ´3 —fŸuµª¢­ëQ¦¤…áÛÆ÷ëÕ»W¯!Þ§c¹x¹‰þÃÚXÕàúO¦´I¶m@ iÖ?c\üEMÒ«"bNßYáʳ„‚¦ÙP9’Ü`Ÿ1½û:ôw[y=]Øè¹ûê¬VKS^ôšÉû*í!É œØÍýjÁŸòdeέÛ§CÛ6­Ûõ»âì»2ÿíºþ.L1€´$Âwh·qß•7hˆòð™}çEV4ÍúÊÝï0JýN[ߦ†½¥i™äÛ*æ1%Ñ„øÏ]tº¶‘‘5„¼“÷8€3 *Ó44´¬r›äÀIöž¸—àu1Õÿîz'ª…«c M§E§ï ˜g-ºQ+÷‰÷wé:và Õ'¯]~À—.æÜ{Q ûþÅ|¹ƒ‘-œš¤ïÛèsðæ…C{§ ÷+¨(ÖÜÚ±‡>@ÆßõþWÎÝH«•ªŽµáuûÞF©Ç½|ïè¦ËLã¡-~m,'úÖíð«þ§b€6Ý[Ê•hõhMhĺ)÷øŸÛwè­€}Ñÿà¹CÓ0ÍæjÄÆ`¼Œh&½›ç¤ÅKFõÔuCubmWŸ¾qéFúŒܾDŽÞdˆ¿z!þÏ [Y9'=],k°žäÞzùѳéÜ¡EË*_%±*øú*\C±­¦‘¦®Œ¬­¥©D„óä/?¶õ¾åêûÀÉ£sÉõ• y¢AÄo€ò†C‘XçBgX-‚BgÈOkP¹C€;¬Ö‘ð›*‰—†oÚ–èrúIä³Ð=¶!kýÞó…IÇVÝh¹#äYäå©E~B eRö»³K=–Þ­c(k#g9…­bæ?K÷óg_‰x²Õì¦ÏédQÓûPsѪә†„ó˜/ùCN¼INNNNŽ¿3§%Y|ó“Žþž=»½ ;µñ6KZcµÖ‹ï'$''''}¸1¿½õ”5n¦Ä&yPƒVRþ5A/E4mâÑ«ÎEÙÓ-;RÇž|raBöžÍOKñ¦uãˆüu4AÊ“ ë~æ§]X{ðïÏ ¢”“3g^R™~†ñ)!îÅé©” 3gžbŠäJ²—{§/ÿ8üè o;uÂ&éÖï´õljÄ[š”I¾M ’<¦Mèºú*ºüý§·nÝC=WÜ`$l+«®zz¦•íÕÛt2ɼ¿ÕkfßV}ííÐ[w1"cDd^2¤·§coOÇÞžƒ—½áÖ9ZEÜ…›Å Úmæ<Ï^4H=œ\«FÀ*…H%Õ×9@‰Ø·•b%¢Æ§ñªµ?úK"ë#«ÐˆR‘¤fŸú´&}‰@¼FJpPø"µ²DcÏíA;Ç;ÒÞŸß¾ÿÐ3rÑ‚ä s™€µ[¼uvO’ôåÑÐÌŸùŽ™„÷qäN·ÛÜ9[Ä“Hù±"w ÝáöøÂuŽHÀ‰ö ßå²Í%ØoSf‰ÜINÆ¥¡[†Ø™]&U"J–Ÿ}cÎÞ¡;'G…({}™›š´#·¼þ™dY…P¬M361©:g\±P" S×Ò’?ý":U 8‡ÃS’.?%¾Z¸M7Ф)æãíÝ=›‘drwö¤¼çs?H‘ ¢¢vÍep½³÷ »\îw±T˜|üù^Ð]NŸ(¨‚0:z÷¼˜³‹žœº[v™÷æ tQ4®hL1÷Ͷðín¡{çHd£dý³‘2 € {¦@• `1 ò·BÕY-– ”»€•Ïï[qN2÷ðY3ÒŒ˜ZK»æÂ6Ÿõ,RêàjKLj͸[$ÜúT"æõ3±×mqЮÌm%mäœ[ak+‡^C!Óà¦Æ¼EÒ”Lß&P1‰¢ CÖÿPÑAA§5OmEò @+Àºcj~Zvæƒ{ötóó›RÙ^»ß’Û¯Z¼dÑÄÓm(E1áÜôà§9f^s§ëо³ýˆE+÷ÌiIU¨âïg­ûd;$œ¿‘U»F¥ß ϰ\ºÎgÞÏÙ ÝZ¤ÿTQ¬IqT|€å‚U–ÏZ1¿ƒ²ÛW¢ôÏòZÍ™?w¼ë¼u–¹!‘)Ee`ãæí5zÆlã:;éu6vä¢]‹z "ì~lͰ¼>­ÅRNƒ¿á¼h˜ê8ii™åª¦ÍiõåQæÕ«ôì  "òXH‘ôçyŽŒóèíÝã釮<×ÉàÑë»ożï&4ŸrÎéï6ÄàÄOÏSßSÚÌ»8dÕåî–q)o³jjd1‡ØeçàÕ—»½Nz“-U%, NÊîÙý¯ Á –h}ÀÛ/hidié¹ÂH½þ™džQÃÒLN¯Êp…eeqi"€ÕJd”\N^s*•ª8¾¬H©À[êj^”’¤Úf¨¡.­ž¯@Õù¢Ë’yÚäšßµÄŒ÷w3Íg\pú;°gÛïîÆˆqÀ¹ä>Ûú-8ÑÕ26ùM­sãºàж|bÖ6¦´<ìÝÃ"‹9—­Ò–¡g 6’;÷#Ú€· T@þÈýóëŠ tëƒÛiç~üç³³GGZif¹ºie”P´ Èì<®”b5ÃoÇŒn:Õ-VÒF.k)leç%³i²|¤C×nC—ÝÌ£PMîCM„×錨@A8¦é¸ÿÎ~GML“˜÷™°ùú³ç—gËŽ.Øõ–Oié±°ãã©=ºÚ;¬ÈvY2DŸØ@ãÊheGø] x-è¡ÙÄ“¢ÊYÁ>~ù‚ÂÃïìèÎe×¾<~^¸ ”ìv"ìE¸;FÀ ¹jI¡óe6””èÏ>zéæÙiÜ‹i Ž]ºqÜ9ãÜ ¹,€‘Th˜ #ûŽØ+t™ØAM±’´«‡ß÷?òðIØ­õ­Ó^dšêtu5DŨ´UA© O{ùxµ¦ýáÙ¥ðcØô0­ÜÐZôíqï $Üø“s&ù'uœ9é‹+.æßY½9~ØÉÇÏ"l³ºó׿(çÍÙ;Ô…WÃ"Oô|âFº  ^÷ÃËë4ÎÔèíÑ*îú»r¸®¿5kûÁw sì™GOž<<6èµÏæg$Å,ã5ßÜ_ÑN¬ìX{Y‚""øõ,Ìæ;lc PzÐxbQ¶©qo7%“|“@Å–(¾¥"L ¾” ¾’S·Fà½=2Ñë\x–ÙxŸ•[7ŽjU’ž­Dlñ3ÿeÇÞ©uœ°xDGU)H„Ò:Ói¼7G¦.¹ùZÒcÙÞUKú‹^Ý4u[—¾3 ¶ŒØjâº)t…uÅÓn7ÙwÅâaZÙONÎYñœ]3Ž­Wkiÿqò?‡.Æò =×lt4Ôûº4;üL´ ¯EýÔ{Ó²F¹îHc]*`jÚ]S²_ròc8ƒt©@hÖÜãD×ÎŽ'ŒÆã¯$ÜóOL*–D5Ý£¶2n©ƒa*tSY_RXW_­“žøâó#k>D8ºk}É׈R‚ªýóŸ$ºª´˜'Õ¸¸b~~ n­¯¥ìñBŒð¥&Äh–†FtŒ@”ÿ]Vð¼¸èmÜñI¡»'?Ȳӄ2Š…¡‘™¦§!­àɤ á’í;Ô6¦4ÿE¹öPC-LŒ»XQ.ûÙ÷€p€:V¯à Kn/° Y A,ýt•U|<á½àI¯[œt19U°zK†ÛÈoÅ¥âò¸ÇBÏÓO¢îo0»ý¿-Ñß©€Ë…U L T‰ Úéïãû¦¶W'Ò̆ÌBx‘‘ó`ͦŒÉ7_ż~q¬ûÿw¿å×ß8KÒ‚'§ßÛÌp6þÊ”×Bú$ÕØÍјŒQ[ kÓð;ÈÊôL­­KO}"AÍ¢£¾¸Hn©Ðùl‘2ª´ìm­†5´um;›P¨Ùœ.)Ö™ÓQíºávôË“Ýî/ÝS¡Ð ¡aîÂãSÆÍó½ßoÑ4•o÷¿’WÄv‚OÇ6¾è¿~º5û/¤Bíá†a€ƒ ;ôcç]{œ>ùú†~iz¤>Im1Á¥% êÆŒÐú™Eë½þà$Ù£Óû·ú|ZÀ«òSêu?L£nchÖsœUܵ·œò×ß™»wf‡½L‹Þ=ÑiàÀ!Ó¥òr’Š$€Ñ[°Õ ‰…ÝyÉ“Í<†˜’1j çñíUø©$T‡Þ'ùMüØ/õ–/Ì$üOßA |#(D]Xÿx”Ü# ˜×ÐýWÿ}QÙ&Qfðá™Á‡kÿóÂæjWé‡úö:$?¥“&ßF”uÿ°×ýÚBÅRLÕ¹ïN­{À–iw™ìÕ„#²¹ik ç'_Úé~igíþ”>ß4«Ó¦z¬ta‚Ó¥LIS´–)òJ=]eÛ¼êWîÑI-*XFyƒÌ5ƒž¬©+WÁž]S@Ö…Óñ¼óxÍ‹J8?‡ËþðéÌYèäfÔr˜69é£|õC c„†Da4»ö‹/™§DæÆÇ n3oIã]P+isän¦éªJ­ *8eÍtu«Ò_f‰èÁ{VûfÍtT›é6SL”ê­µˆ· KÄzÍ+ov‰8‘›“Uævþ?{gWS÷5ðuîÔmžG *‘2Td(RQÆHŠ)s2{d !³”Ç!c™‰ÌC³¨’Òp)ÕmÐ<Þéœ÷æ{o%¿—ìïÇ9gݵ×^gí}öÚ{ŸszCãUœÛP ÔØ6ý“Tfš/™,Nà”Ô°Åèä·@"ö³¾%ÊŸÆùŸnîL¢)?Ä02 mKø_CpXµ Ú0ëÉz Ш4 郶7ˆVAE_È Ýð'_^QÔ× l|λõ—&WVS²2·” ]ÈÀ.ÉçȨH¤T"bV…18€¼óÕ'óøÏÊȪ)tUo"G&C¿ñCD·¦d>öwXR/¶Ý˜Öqšzw)~åÅ<³:y^aÌ•§”Q“È‘0ŠÁŒÍPš°E_œD0É‚´éu×D›"\˜JÀKâogÎ ÝñK,è% RS£&SÚiÔÂêÕ È4 ©å°„/*/!èC¬áç˜Ð./O‹ÃXëK’eŒÇô'mÿX&ÎoF‰˜ëæ{áNÏŸ°n½ªŒIÉWåÈÐ555…‚ôuоÜ:“_Âà²2.$„gŠu‘Ç0:È/¯à7;ÿcvS‹$ù’e!_²«€¨.y¶<"äWÐd k·.d‡ˆ–Åñ9“¢$&&Æ_žªÕo×ý³3ã3^ì*wi去U8àUŒ{;WJÌ\ÔO¨"T HÒæëvÛ&o_w-»qžX²Y+ã˸èºV:_®<øÂ¢2)øn‰¹R^Ô§µ+g; Q̌ͪá᢭‡;‹_“êïdðþÀ¾×zN¥é]-ô˜W®$VDÕ»ÃÓ'moþ,ŸàÏEõlºgß Íå'ç鵤jaê›^MF êeN™ ‰^UÞ×üü{ \¡á×…ý˜`kRDW§çÏ|••æk7Æ÷¿Ó×rµí‚(}¶©êÿϘSj„ñØäWSÓ€LVÕÏÑ„&}íÞ½:;-Ç(]gšŽÕc^ñŒ>&*¡*§¦‹•ãD+=Š€*Q =EÏçûob$]‘©†juW⹿»ˆûQ=ÅVb–ª® –ɵfRzÖ{D^^©T²Ó²D¨Ô"G”.«¤¤¯ªÞêšÔ°=&œý‰‡G¿`YÆH{ÒÞª"ú݆(ıÏÔUÖQo£É`’–Æɯ.ÏxˆIÞÖxj?*öJ@HNN°.\îâôD IDATF²ÏðúïËÚ[,_Êç:UJG²¥3%¬ŒF¿?=5•,+¥ KB}Ùÿ(‡i¤ªª (‡€b/ ^4ë^‡ƒ–d̉P ÓþÄJV¿=z,Y‘0¦ÏvÑA>açÇ,ð¶_¹fäj5_}d¬²V-Ò£-Á³4Å•ûÆ{xŒZIˆè:zû˜ˆý¼ ˆöåWN”…,³»èzÙÎÆkkÜÒù–šÂ`wßÝEéÚÞëÞ®˜1ì0£wsÚup°$´*LÀ+³2¡«“Ô•ü@¡ÞáñjÅŒáþTy=mùv"F°^ð¼õù ãEp~²?µßí+ŒÜî¿lŽÕIIÆØÕo·±¨(ð™!'¥6_Ç}–õ*…¤`éágø“Ï_Õ¦‡ѳѢý]Ý U{¶ÿ)š·×ÌÁÿãtECÛ¹þ.ÝEØ M×BzðÚ=£Ç-^8ðÂ,mjÛêÈÊv»6¼_9gøI Óõöî¬D—qQtŸ1ú¾²‚ºq/}RN![¡Õð£Îäf@—îçØ·,šçØ_ #a£vz¿[µÒv((ZöÞ¾fRXT?ç(ŽñZùb‰“…¯¨’®ª„LRÐÐÔôä°Öº¢øY½Löû¼ø‘»Ýµy—FICû åÚŸIéˆÂ%Á~lkB÷ÕNð¨rs;ÎbU“H11)_ßÙÈM ÑþGééé¿§ñœ3QìË/Šf™hLјVq¹œ¢ÒR›àñj©"T%E’@ÊÕ­[·ß¹^ˆßŠºhé|?ç+ P£ø¥!„|‹Üò;/yp©bô|Û.äÒˆõN§žuPú,ó”?ßî‹­Úl&þÛ*Dtž†,Š@ºåŸ–÷ð¡EE…BSQÑuvÞrùò6äDD#†ÿíaßÚ)š£imX¢Ä½O߬z+ª*×Ç7…ª¬ øû×ëW;@ ˆß’TW5æÒéý1&ÕoáþÑÿ›u5UCfÿ":;XËŒëxDDP#†˜|x 9²#F¸@ÎêôüÑ+]P|ç%õdnßOz®uGç$ÐÄ3âû£­t!~2„o‘[ˆNÜ…Ï74ÿOZZ|ÒçHØ pÀÀ *Jbco»¹o¢( u9t?ø#3†ÁŒIzxq—­:õÇ1t‹bÆ0bæêýÞ{¶1ºší?;ï$„3˜1 fÄ«H¿]ózɾ·:VûR˜1 ~Fb(2cL3 ˆàו§C‘7@ ¿ MÛ -:òèÑ °X И0˜x@¡cfvvýSþ4ýY®ÎÕó;‘X)®c:tô(%¯ÇÙ?ú†".3ÒdzDªø]!÷wö“²Ã‰sûlÄ¡"åîÉ—Ÿ¹ŠýíFO™o~îü‡RöXN ‰BV´PTÆæ½IQ“ÚS–FnA @ü?Sÿb±ªÊË‹À´YÆUÇàŠsÊË‹“4¨9±7/\?yðÈÚ©ÓÛKcUmäÂÓQ3˜1Œœû!‡zŠcíi˜1Œ7Û½Î}ȹwçe4ƒzb¤˜œÝ¡O̘„ûKI÷YèµÔsIo)PÕlœ»ŸÆŒaä>|tr’MˆZÀÄ{Íßþ(-†Á|õü1ƒ“àg$Õ¼¬¯‹»IhO\Ê`Æ0>ߺ¼s¤6£õ\ÎŒaÜ«D‚æ×Ûùéä…'÷Ò™1©±»gˆ6ß?$j<ËÃFjãÖZÍ_æyÂÇkçt³I³ö'”â@iYî“~MÕÁèzÓ6†¤Ä0˜aw›ÐP¸ýz”UµÝíÙ󇼥•æ¾O©F¹/@ â·IºH$ …B…o"…€q0 ¥n!Qžõ4œNÆå5\öéÊîãA¯Ë[X[WÖ§ÌJƒŽ: ‹?°|çÁH‘®[ίïÙÞû8+âŽí[w4 ×´ðô§Ñ´ HV4î­À½ñ$$TÕµu4µ5D¾¾L-æñ—[Èjúd½—ëÉc ÅÒ¯ï9zþE жÿQLcX/£>CÆêbcÊ£ ‡ @ þi’JHȪ©uÏüÞ5åœ1\ZEE·î'-Ðet‰Û2ûÑ]$ÔLÆÏ2±ÖfÚ’e7¼+Ø.;c[ÿÓ®ÕDêÞ—ì;ÎöJ@ÂDßÃb’Ÿ~Z$'3d?¸ô¾ÔM jµÖørh›ç¿Ÿ9AGE Ö ¨Õê.2H ó°×ßÏ4«ËšT4”E3ØxI ãÄÿ«YØE{Û:£,ºÞlóÊ9±·ï„g=e™¿ßR Žèµ¬ÆŒàî­#$y»³Wwö€¼«JnQ.P´§Ín¨Žæh+M€¯G½6J§Y X1E­DL¢z!þêÞ3ÑéAù¹9@ü÷I—¯ïgç-é÷^U,-‚†ä==SEE Iê«g=ðv½ë d)ÉÇï¯$Ñ£Ÿõ+™YÇVÌ¿˜W·£‹¨-¬ÑjQZåÇ à¢IsÌ[,Õ—ï§±šŸÆ02 x܆õ#@ˆÚ2±™\6o¯zD½DË·òQ¨äÖß=†QEéd›ÛôÞ··K@GÖÔÞZysôŽMkŒÇz®,Õ^éd25¥µ8 4@ %]pùò6{ûÕ±±· §dqÅØãIëé™ìàã3³.é’¶âާD\pøËO%˜æ`À·øð̪/÷#r–LÑt]4§èæË|šöeGfŸå+®öcàƒì9.g¤\¼õ•ÓÂö—{áÙKœuWnöä„$ñ´¬ÇÒƒö Që–\¹\M×}ýVúb@@ ½Ø_Fç¹;u[¸dQQ4iÔ]€ÜGQéßôÊÀÀÁÍË5°èÂ÷#Å>£ké¶®ƒHPú ©ºéLõ›³"Gì°| ìpȥ穤ïÙµÆÉŽùP¾L­ûœÙÓ²c°:ÂìDü.<ŠBN@´‹ùСð×¼25Š_BÈ·È-È D'nÈí']|ÀÍíXnnzÝÇ‘µ52.ÀK_ß¿7Árü”^YÚÓÀ[N¿('ˆ7ǦÏe{þcç´iƒ3@mÞÇÅ‚/1`¥Þ¿’ðbo<Êáñ­N8>Ý•³m½ýTÏu$¨ùòðø×ØÛ‚j‹¢}WPðž×wÚrõïxÀeñø¶ûT¿9>kÕ{ÝØU‡»àÕùíëw}¬båì=:ö_7Ãé›g¾Š(f´x»l¯^Vj’œì°ÓÏKš¯Oqr/Í™]¾r‰›³™Ýžì¢ôwÏ‚_æµùš†Šç‡WŸTß?ÌÖÃ}ã^£‡‹@ ˆ¿‘?s¾–¦1qÅåoy%¸l¿®Ž¼ðóBJ~xE{Ú½w½d_˺gÏþV¾çãÈ4¹ˆøÐJâ? !ä[ää¢7d¡þÌT‘®9tª»v-pPËõY¿îA zn ñóàeå¯85'þ߯ÎlÏJ/üï³P¢¼4âp^óU`Þ×ì «² 8Âÿ‹ø})‚Ùð^Þáý (x €¿€Ä>PË á«¤xïÏíž8y¡>‹&ÙÛOY~"¾À«’.®uvp˜0iþž§ÌÆ8å} ß:cã˪º†$\¦¡™ žeeÞóv4qü„ÉËÞ•ã?h5¦ å<æuW‹!C,,†X»^Ìâ^sÄ}’½Ã„‰.믦V vÜœ[ËìÞû† 5¬cÝMëµàå^]8û$ƒÓ¡zUÅ®wXÛpR(-ŒÏy¼oÁøq\Ü<ÍéPÓký£´¬)@Mžå§Ó9µè¤p§\\ަ²þKÔdÜÙ5¢Å0K뉋vßûRK‡qj²EC[X ±îþ¤„Í85ÅÂÂr„Íp›á#†Ÿ¸Ø'²Â5´ˆÊŠäÞ‹&ÚZ[Xް_ä}=©¨MÚï4û|&/õ™a·èlReÛà'ìw”¾ôõºö…•/Ö:lˆk­•µ}ö'Ú‡Ð~cGü–ü™I+Í×nLwU3U³n=§ÍÛû²àç–§¸ŸƒFªšéXÿÕË\`WÇíJKSPw>aê~ÖhT¯Šg{³¿ý×÷ ¼²:÷+@ÞþÓaAÞø&ÝsÁ„ zË p,ä§4(†Ü‘P5ô6Õ½èO„({éûoÆŸ+7nnî¹ïäÇZ6#p×CÍnÜ<æX|Ê'¢à•%ÝØºÄë)³¾&Ó¬ œåd]Ûv¦vúñkÁ=º<:põ §ã64 OøŒiKymÖÛšaûDFGFF‡¸hRYiA~1=½.ݸ0»âó¸€?ÿû¼ïLR ÑŠaëoÚô’à°«õjeÜÌx¼àÉÞà ˃×o^96—¸+8ç;o~»Ö?6Àä+X™1Ù:ƒÔ©|µøëàæïWþ>Q'ÚwÕáŒDñ‡!üA#7·ã,V5‰D“òõÜ„ø+r®ÔÜD¶òD' PTíºÙÊU“q`%| |¦â²FšÆc%x~ªžß«IzÐ œ–WÁµèéÔ;÷JÃßÓ]è9WÓ#ž×ò“ÒÕÖIšò>5è&GaW–q= s%¾\È+ÈÂè&'ÞæXÿVæ“UAÑÞnÝúëY-•“ߥ6š1u +ª¹p72†®è/½Ç@A 躀è4Ð~éÞ ·Ê€9*ƞ矛qAQ·[¹´W*`MC%Ö²Ú‚Ä8ÞÀ5ÝÅ1²¸Ù8!)Õ#L \O5\¶¶ú`xÝȰ ^@ÆL¢qÜ(p¶¢ç×H¶õ?C)$0vóÙQ-Gî° ú9Y-á¼àWN”>us¾épùˆeq"£âó‰ù¶VÉ ˜±ÁcRM‚NÕrœÃâ’è"$h¶‘ÁØ_nˆ2œ;<ÿ¡pÃ:4¬æ%Ó²‡û·øÇ—Ñ•º)ã½ZdLí× s×kî¤ÜyËUûÖX+5sdKãÙ…ÉJÖ–ÚtŒD×¢É øP>:ûÀœL›^TÀ,5ŸlTüâí—¯%]fìß6AÚšBja©wÐód|)§vµß°ÅÅ@¼c}ÀaÆ¥)›Í¦ó×⯯-¯¥*¨ÊÑH@ï6~íz9¦$åíþŒ]]YƒSU©¤V44PýáâÕŠ Ý­»R@\gÄRÏŒÙÿ\ø0Ò€¨úxný¶³gµq °I]äê~TùbíÌÇ ÞÄy¹W¯+úçÔ|©è1£ÏŽ;µÝ÷i¨6Ë6Í@‹]7çW,;‘=êÐ)'æ¡Æ-Œ<êu$*Ã5tÙº\åø‚»-J™ñͳQÉ‘~ç6ü}ÖMåmËâDâ7Î9KѦä–ㆠ÷®êùòÈõÔ –çníÖDyÔzÇVþ>¶ò¤Ú¤óý»8d§ÔÌ#œ¼°£;ŽF09@×›ðϦFÒ¼œG<¼(Qì¡Ïù,µöÌ?Ý‹øô¬‰mi†/§µ¡æÝÁ¥7&ŸâÞÜεÛ7yÄt¹¹ù§¥Å=|è_÷" ]gç-—/oCžBtvˆêÌÐP”jlía"Àv«.±ñÓWÎûÆ¿ öëôÈ\E‡Còë½wrÄ»ÞÖÔQLè©“*#×}þXhØo†Jba»¨Œ €( ÏÉ3Ö›é(ÊNÌŽþP[Y’˯¼É ¼ä£¹0·›8]Ò_ 7ÀDDšŽÐG, òÇ÷$йõGg\@×6Z×:>];—Ù{¦HÙË*‰®ÒªŒ¥´°§j;oYU/ß4ø¦,W@i‚gË ¿”Ñ$#vÌ]ÿº€fà°fã,ÍÛШŸßN‘€rŒfîÐWB ãæðºô·ŸûƒvéÓî›ëŸ_m`7Ç`þrûgbÜj¥éG†)0¢^X—}^ô_±]÷fh+†‰u(‚üPYzàD¡ýÑ+cås¯ºÏÿÒBü;ê@°Ê)£}/ï¢'ìq=ü {è,­†n€ßxš’RÁíд1Ót«ã¾+*Ó«äÉðÊ¿)»\ØnXrÕuæ‘SÇÔ³OÎÛöàë˜ù:ÔVÌr53?Oä4G«úÕÉ#orXÝé :þ"xEoÞI˜O¨Å_Mgò²a¶Nš¨Ð»¿é3+ y §89×çÙ\¯í³î‚ßHNÖe×—ë~§jßÊ_Æ++s>¿Õ˜œâ7wÓ–8p6¯³”u0Iã ½NÅ-žzïÒ¸Ù Ík†>Ûã›nyè’£&7ÑÑV?Ã3‹ŠøL8vÝN‘ysżdIÀ¿…ò²ç>-Í8é^Öz‡ÐÐ6ZØ™mÑÔä¿Òåæv<""0‰ ÃL¾óqzÚÝ8{ûÕÁÁ³ ÀøÎ9YŒ¦)£(V·=·ño¢èuEIRÕõU_¯ÅÅ¿rM ªË*Šà4Y ¼¨@膢YÁ€`ÒÜ=ÉW¿Èë8F´<€_9®ÑX4©¥0ʸ~}ŽNðÍ)Ö]BðÏP5´»Ã×¥ q ¨üÔ#^•ryæ˜~}†ÉbÉMU­k5­¶‘6ešŸ%xœÊ´Ö’¯l!/Ùá×û‚gñÿ paÊÕD î3ñaS-¼csò Núæ8ܦˊ?ä¾ã¸ù™U½DÕDj?ù¼¼f¿6-µUÃ~ Ãn^ Ö—˜LÕÑæ*T Ó°§÷(:V¯Õ€‰éè¯@&ᚆòÜŒêÆ~‚Æ“Gx,û¸}ýä«Ô®fVFjt*èZ¦:b™«,-«×[…dIe1n ïH-(ŠÆÆìµ+¥YZXX»:éÑ>þÊÞ¿$^#ÉútNè%蜷£Ÿ%ˆú‡AH²ƒ–»=3ëC|\ì‹ëÏ]›tØw ¨]O]¦ßlˆS TM瀷nœÔ+ëÿyÐ}¸µ®8© ³º‰4–ƒ‘0þ#ÀbF¦XlõÔ8ìs(²ÏKùv‚]x€µ6m3šy>o³’Ý]N¯¦FQÌÕÆÄtw— ‘ÚJ€±sâ 6îš"@±ØrÚ¢òÅÚ^mPÒüïÚ/Ïù‹ãéb¢]é‰c€ËkI±Õ8´è0‰Þö&§_|¢v÷}»-›~mfL–ºý-ÀDôGÛHmˆe¤2¿t±¦FÅ0 k{ýëoõ9Šïˆ ¹ŸÚíìDüæ™Íÿ“–Ÿô96\ðð‚J£’ØØÛnnÇ›‡ÒP—C÷ƒ?2c̘¤·wÙªS¤†1cïÖ´µÉô{dÚEÆê 3†ÁŒ‰ñ2 hÎ `0cÌK‹uÑPñã79qqrvYyã!NÍkŸŒ¤|ƒÆÏk㼆Ï©M·¦¦¿qLÁA¦¯Ñl_£™{{%B ‘ê‡$X« •Ìâ²? ^ÅæPÉ4ˆh¹î9ȘRž´» œ#DycÑüÂÞ.à ¤$ÈxþÅÄ›Q$Íáê–s%Šjé½ù•7·®©AIׯî:ƒ²0V@mµW€q”Ö¨€arÐ%j–AÑç?¸šì/—×ïýyöäîÑ ]z{œó«Äå7¬cý@-d»éš÷ ŠÉN^Ì£ôš6.¬^­Î1žrrÕ¶Gy\‚õ0ˆÑ}Œøõšµ ¥»üÍÔnΪvŠ…™¥?û…Úô˜Ã~Êaµ uÒî…¬ÐÛ„òòlHr—à–ºø’ld¬H!Kʳcü][P‹ã¬ÂÄð¸r%]ùvÇ_ÕÑËh÷üî|å×Ð8]-jè2EúöV¿g_ªqÀ«3Co½%î4£·F¡QHRý—¬·H÷Ýu¯é5'âÍZwÓºfmëF¢KE‚õ• Àj3L©þÚ!wSª¢úcÀ²…‡ÞU7« «•Q7íú-4&—xi¬Ïš=Ïk$[–ÒjóÐÈ_œ°7 ¸µœ¦t½±CK/ÿØc|_)¾|‡®5X#ûNX6ˆªÔGÏÊõúkwªÍ|ð< œ¼èÔZ D´Ž˜ÁÓê@‡€øCÆ™M̪*//SZK‘!À甗מ¤Á@=ȉ½yázøçJóRWÆp…!{€’ÃM†ëÑÊ+`ã­-öú24(ÿuÞoí,û–2æ{úÞ ­™°sи{¥=¿uÎöwBÞ#aá6A5á«Ëµf Cfà.¾r÷Ö 7È!ZcDû{èpOfÍùÄ&H’†Ê6ëÔiºªÆ2©—å‹iJ««¶±G¨3"=ýáª0i í1½ÉØ{!iÉ.Dʵ-”©^jruí*6`y—PÿO'Îs Œ¢`®>ÎI‚ 8RMÎ'ùìCŒD¢v¦§ÕFdµPïšîb|Š4t914PÒ*HU.TPÅ^@¼hÖ½-'Ș¡@ÿ#¯HMÒù  ª>̲=@7ñ¼âk=}ÝÈmÛ§Oª)Ó;‡+ ÷ÒtÛ’eñ =Ç^ŒÚ9Tˆ†¦öCÕœ¼{/í_ßö;Kq¹îSwMc'5Ýk¤Lo°š½~G°ñ!G j¬ñ”hÏ©Sä×ÍŸxCNw ±@¤[˘dN¬ËðÚ6Ý‘ ä.£Özõ—€ØÆ_ó 7&m¶jåÛm«¦^æa}gmµÆ!ž5/¥Õ 4¹ak×}lQ&ДÈr½ú+/‘ò›Ñp‰´¬‡Ê<.g(! Pqĺ¥)ÛV9_²zï ½ IDATH×Ñ6ö“’á­^ðzÓBÇQ-%1LV°æ;"h†”¯â é1wîÁ»wçOþ ~-EÒë‡ 8çéÓ ê͸¹Ø îTeî›Ð'çž¿›J¨›qêÒ½ÊØ]+®%g}|‘©µp¥IÙ;FUk܆EcTŠƒÆ»ø‹;5—yÍü@HÒõ€2Í?zŸaEØñõŸ©Ú&ÖÚq[7¾(iÌd¬®$ï4-‰Xj:Šþ$°pøtéÈH K ùÌ}Cf/4XÌW®ãÖ"‡{1îzå/}ÖßaNßáfH{ïc9özA<èãÈDÛ ##þ“B¾EnANø£ádß\¹1ÛÍ™Aû-âeo®WZM³P!—¿Üµè’±ßó¬k-øŒQ !¯v¦†,”¦•. Y5µîù?Ã[£†£<€s †K«¨èÖÇYZ Ëè·eö£- ºH¨™ŒŸeb­Í´áÑ›”"½êœøÐ˜·5ˆeXÌtvé®!¯¤’*œ¯¯[ÈÐz¶ÊUUlI«©+Äß¿‰¿~ôu‰`.Àɹ}œÓ7YËS¨8ß”qý4%]¾¾sœ·¤ß{U±´þ pôôL5êBGRßX=ë·ë]o KN<~Õ ‰ýÔ(2›«%wqÞ}yoVlЮÝD¦{nÖú¦, µå…ðÂ;žNTçvýM†8 â0VÑÎâhÿ³¢xáãÀGMÆ’"O<,4[ö½åbTQ:€Åæ¢Í…”¨ è$¹>Gtàd%«Ž[µ8$xñW%]pùò6{ûÕ±±· §dqÅØãIëé™ìàã3³.Ôd‡­¸ã)þòS ¦9Ø€ð-><“8«¬@Éxœ™hRj…† üóç¬ 1kU:ZÊ$½*-(7W,×À¢K½Ôîn›—©$Ü ¸Çl‹Ÿ£]Œ&l /‹õ°˜´J™eZf -AJh¹u(öíXK·uD‚ÊÐIÕèê#@ ˆÿmÒÁÁÜÜŽåæ¦×}YQQKQQ£!ã¼ôõý[q,ÇO@à•¥= ܸåô‹r°$¿WFkú»úî·»ºÄf÷‘KÃ7MuÞxÔäE·áÙ©ª2V+Ã÷û¯›áôÍ3_E³Äðò¬b%×y\éÜÂ÷O¼7x'ü­-xu>³š–w9©g…•[‡l¯^Vj’œì°ÓÏKÐ÷ Íhc.ÁÇ+ P£@ ß"· ' ˆïç¯x³?E{Ú½w½d_KÛ+èåmð7¿H@ øUùréééÈ ˆvéÖ­ü5o/Dâ×…ò-r rщòß›tq?T Bq€@ @ þ÷OºÜÜŽ³XÕ$ELLÊ×w6r@ @ü$tË߯fîÇþwîº{×/2ò’³óä&Ä_ÁË ystbÈ&ËÛž#nÞ¯àÂò¾|òö>»á3\Fò!—¤\6òà_F ̆÷ð† ïAÁKü$öZ6Q_ Å xîk{89÷·N¶4jnf5ó@t¯x{|Þss³¡7†d7¾–Wðpå(·ˆJhU¦!gk××Njf6Èr¦o|þƒ6€cØŸ|Gëë5aw6‹×†%mhæeŸ·kÐÒÓΟÁnî}ÏIæC-,ÖÝüÂê¨oÛ*ëëÙIã|>±[¯—§UF.:?¼²¹YÓ8]/À€ug“ƒ™©™¥íÔ-!YßÛ{ýçNh­œ5¨ŽÝ8ãP [ öfTýé²Ç$³ÞúÝ{ô2sÜp=½†~“þïØ;këÇþä3ÚvÏÇ–—/{ÁÃqHŸžú=z™;zœ[†Ô$l¶w”ÁàGz0ùß·íùT¨ªß¡Ž­ÆgÛqõ=±¬ä½–ˆ¹Ó†k©5Ÿl†í7Lá´Ù+þ„f¼8|Çʳœïëð[\±·>‹ö¼ªìðÃÿm•Øáó†.Žªü zTnVÐ2»ù¼ß5érs;ø$ú4à ¡Ð=‹é˜žPýèî]?{ûÕh|…èôà9Ÿ¯°úûŒÙ>~ã~õÒñQ_p¡‘¯À‚¼1ðMºç‚ ô–AáXÈOi&P ¹#¡j2èmòŸúÝK¢4|û®Oã΄EE?>`øh“Ï»Vê‰õ·tö<ŠŽº:ë›ÏÖG…8¯äíù•SVÞmH„É4óœÀYãìJ¿š×"£yk{žIcw܆¦!SKch=–?HIKKKK}kIïî37:h[·¤-ÍÕŒ¸š‘§Þ¤¥¥¥¥%‡,Ô¡”EìÜ“áx1<*,hZö¥‹´í%Á¡ Ÿ“ÛtšðúsЦßV¼¼ ÞŸFD>½¶‚zÂãòw¾cê¿vÂ÷ÔjÓÃ2»Ó¢ñÕ¢ÓÂÍ ZëWät>îcjòË€ñ¹û—ŸNç=ؾ.vúéyó®ˆÎ9ó!åã‹3³hAóæ0ØÍò¨¸ƒsÖ&Žñ?åf$Iú)Uÿoul5>©mÇÕ÷Ä^#4ÝÅ!Iiii©I~¶_÷ÌóŽ«ø¾(3õ Ü1P¬£ó]nàßÝÀÊRâÓËqâ»:ü]<øiälc ì¿+ñ?¸%µÑ£R4ígJ\ô}Qöût-ZXZZ|ÒçHØ pÀÀ *Jbco»¹¯“Á$ —Ÿ?“Ã`Æ0r>t³U¶GQÆê 3†Qÿ/êuô¿ÞsûÈ ùB2Ei¨Ë¡ûÁ™1 fLÒÛÀ‹»lÕ©RCƒ˜1ŒwkŒD[·ý{dÚ¥ÁÎ/QŠæ¼3†Á¼´X—Š–WÒÅb³it9y˜ˆ®¶Ãº]¥0¡‘¯¼((íƒ@— ƒè4ОùÞP?§VÌqP1ô<ÿÜŒ ŠÖÏ4i€‰ë©²rJj˜ÑQ< ;C Œ¬jå¤rûC%°7Ï%õݼÓB¶®ip…È4k œ-c†?f]ÛŠ«JA³[ë=Z€ÑûÍñZ(ùèìë !5VF »ÝðaæVÓ÷Eñ :Þsú¦ØªšÔ •öV–fýMG­ºù•#Ä'ÍC@°7­ùð¾ž4zo2K°,AþRj¿Þô>ÿ1åìòõÏø„k:Ûf¿úÝÙ;âVJda®«_`åèjgÚ×Öû}-'çÁö©–æCÌÍmç‰+e5•XXÛò€›÷xÇ´áÆ 5½ô§¶€@;·$N[=*ˆõv0þpòéï³ØÕÔÈ-:’žþ ¬Vˆ75ð€ÂâÌììäúpÓØ›úùÉí³§ï„~‘èfí²ß³oë<óÖ¿“Q]Lœw¿¹¥¯dËH¦éϺpÕm|ßÚ°c'öî ¼—Lî;ÊDéÿ£7U™:Ã\z·‹õÑ€òï„ÒUwì°ŠË“^ýêþåÜŠîª:ò˜ÐƒÀËaµ Þ0,xð`Ï9Ÿ ÐÞ¿ n<€ˆˆ4¡H òÇCîP^òGg\@’èneÓKš DUâÙ# “)}é¥Y’²Tš¬2µ$¯ŠGÓ›ë³gîù†Y5Ž™fÙÀÙ’¼´:ÛêøÌ®n¥÷6ZPî¥GùšVÌVS‚UNŸrúqø£}žnömXŠ©xs>DdéõÐȘˆãƒßºõ…>iž#´Õ+6õlBËâóJ‹RrT6Ì4Пí»k”"¹Ý_¢© Éx':´Wý ^ÀuÀ-bvÙøìÍò ;’GŸ~õp—^Èê¯d&Ö—hƒßoyêeyIøö‰#üE„ß^I½þïÍ ë[ ð­&¶a¡ðûŽHס:™wß–ÿ.k]MW‘Ū*//SZK‘!Àç”—ׯ>šÑoÚÌUþÇ^ºð ˜)Å­îKà}½wâÜ!Ï ö£ŽК¿l‚zó‹ŒI Ô€œØ›®ŸœØöÂrËœî" å‘•ÇíÚhðl±•ÙÈ¡ 4Ũ$Õ1ï.Ž‘¥Õd MÔE€,­*Á­`5í{ü'°U dœ9y±WPþ°e³ D>þŠ_E’­Œ¤jjщoF ÃÖ]}´Ù©/íÓ…Å#§M©~€¦5ëfbZIwݺ‰ôT¤–ãu À€Úìlj&ûØ~ðòz\Èû¾NO¸*Šâ Q†PTÍÌEÞÅd$?‹û»º­µõÈ9'2ªsR¿qÓf ‰MQW†õ­Š÷Ö±½øl5ujiv»Cwà&CHMÉòfS-”Ș„á8+jÂËܺ\JÊ|Ë¿.øÓ3~Þ^ÿFTW²pôI; \H -‹¯“i½”v;üf9B^Z…œº¹5×a=¬ ¥ÈdrmFXF×iãtè@’ì3i¼Ìû¨ÆGFO¥¥E§*©M²‚íÁÛ»µß2Zùmû ïÏ© ]Å S¿ý.Ÿèm2—D¢P(Tø& R£P¨‘ÀÃÉš®G_¾¾tf‰Fåó£«O~aIÊxâÊnk7º­Ý¸pþþÅ*#"$t†ê6o+DybÔGÐp:ó8-íúuÿùV⥕ìÂû[Τ@eì®ykæøT×f…_Û¾x½Û²ã÷«5†º¯_Ú»‚O¦¦• ª9mõ[`*÷ê¸û •{ÂsébBv –Ä?üŒ-[¸b­¥DfxdQã•(רq†­"îØ¾uG“pM OŸqèƒgþ(º,ì}ð½*BRBÛZÇvùN¼7÷ʾ 9XÎAÞúë¡ x ¬fÓø¬§}€J’>h{ƒœH¿€Ì øÃ>áEížîfzðüÚÁ2$ ÊjJVæ–rØ%ù YaA"bV====½A›¸gedÕºo"G&Ë÷?D”‘’ù¸I¾æ‡lhy‹.‰¿iè4€o£’ …V5ó c‚.Å7 X0Š¥2ùÙÓ” €,c<¦?)åcQGRl!µ 5Æ F¦ÚÎ,(RüN+æiRË‘GÝ øþÝwq‡&öëmê|)óõú1³¯1Ù,R異^ņ^ZoTU+ßU†XÃÏ0Lؼú÷8¡XlÀæ{ág–[ÊçÜX1aîåìŸw *ÃXfý¸BjÁ뜽 y»çµLBZ{ÀØ™«÷]87»úæµäÏB¦¶·HQ62 }ˆÉjZbeÅ$=ú(Q®³èÀêQã6zKؼ1¤¹3+B[4ÀvT)SêŸ×!x,.FÀi=Ü.< }öèÞmÿ]©$JÝ\ì¿®cñImg¯…ÙØ÷'óœü÷i ­¯@%„Ô£Ðê·“ÈdŒT×’ñü¥ã—ÝÈ$«œ4{œ €~Ÿ4w;G°ãõ‰/Áeqˆú:¶,‹$D†¯”– ¿0_g+LX¸ë0²Uˆ‰úÄ„ŸoœÓá–ef±‰ö~Ûš…­Ý~£k6,!!«¦Ö¼m^;€s †K«¨è6?šwgëìÍÛ¯3%ÌÝ.0—%aR&—º»,rwYä>k¾"UØ ß%NZ ËhïS>æTH¨™ŒŸå{w‹­ûë›”"¨Î‰‰N,åUgÆ2$8»,_a?X$U$8ü2­µª*6€¤ÕÔîvCUs¯}]"8âäÜ>þ†«j3½Äû?j\ã%Ë•l¸çÄÞ¾~Ý÷L @ Ž( þp0QyüÓ‰÷1okØ8Á),úÇ’î&*!ä Œ¼õ×C Ê ÀXµ8Ô^ÆQPZd †ÈA—¨YEŸÿàj²ÒoL›pzIiEÅ|)ìvbÎc†]ÍÐÓSpw¹ Œ²õ¹Oiiiii/·K œ•ѲLD?L©"ˆê´ðW\ýÞÚ¶Mò¢?dCËZ|ŽÌTµ6lÜäÀ«Îÿš_ÍÔ#%Ds½0F* óÛy%µ†à}‹¾&jc©†3‚<=¯3j ¼ôÍ8¬oÅŽô ‚¥ËëÐ˹ñ$›œœÐÛ)5mgýNShµtõ?¦|HLLŒ¿i{¼à£}ÿe[Ï.”úö+TžÏì.ßYç[¯35£g™HˆtRSî·7é5¬Ì°ç<ÓÁuϲ²¢>È8­]9ÛaˆbflV°WÚ6w»´`§ŠÊ¹Ÿ 9+3âiý‹BùË¢ ‘pFÆ85HÂm³G¥»dQfÃP» ×Ñu­t¾\yð… DeRðÝsu‘úOuÓ¢Wp/ô+ðâÈm®Û?÷éÊ÷[Z;·¤6ús±º«Å¨Rê®ø»,Œ4Ùáë;ÇÙyKú½WK‹à€¡ùGAOÏTQQ£¾¡H)+p ò³Þ…gA\é wDZ²ýú¨R£?žž©sº™b¥W««ÅØnø§°´æs$I}cõ¬Þ®w½,e8ñøýUƒ$zôS£<ÊlÑ8»8ï¾¼·+6h×î"Ó=7 k½§¥P[>ÌŒÞñt¢:ϰëol2Äaà‡±ŠvG“øC/|øh£ÉXR䉇…f˾·\Œ*J'°ØÜÎý*¥¿dí¢w¯™óÞßÚöäA1¢âzFŠÒIB’¾"o¡på ­‚Tàâ@5åPìÄ‹f½ÑpÐr‚Œ  ô?rjõۣǘ cúläv~Ìoû•kF©YóÕGÆ* éEz´%#x–¦¸rßx‘C+ ]Go±Ÿ·¡y÷^™• ]6ÆQöl™ÝE—ÐËvB,á?B7Ûxm[:ßòASìî» ;®³Ý3~Ù«“<’Œ±«ßnãŽÍ½ øB½ÃãÕŠÃý©òzÚòíDŒh_§=ï@éV¯º»xŠÅa\¤ë¯C¶ ¤šï˜joß ²¢*óuÜgY_ RH –~†?ùüUmzø=­¿k·X¿uþ+·oZ1|O!Ä´†ºlßg¯"Irü¥=]TíÙþ§hÞ^3ÿSŒÓ mçú¸ta'4]VéÁk÷Œ·xMàÀ ³´©VU“‡Q©¹þ.6[*©=¦íÛÛ[LÚp§÷»U+m‡r¢eïík&…=ÿ•ul5>iDñ£úö+'¸ÚÁgv;í—qÌÎð€¨jÿ §þ1Ll”`MÉâôæ»°«*¥FîÞÛ[¢@Ôp¦‹¢ûŒÑ÷•Ô{é“r Yµc \Œ=ÏUy¡ÃàsŠúÃv­³–¿,1– Œ@¾«hÜŸ˜9ÅY6øŸ°°Îvœ-dIÈ9¿ªŽËjýUçF¯ýSÞñÍýzçBÍŒfÒ¿Ï,)zë^‡¡hO»ã®—ìki{%‹Û©ª†Vºˆ¶A+]ˆÿ$„o‘[ˆNÜ…‚¦@ â‚VºMüÍ+]@ Ä/­t!@ Ä/}Óx…œ€hó¡Cá¯y¦ 5Š_BÈ·È-È D'nÈBA+]@ ñ AI@ @ ¤ øÕà¬Äq¾Î±ÍþÅßIà 奇óŠ…}-€•ð)`»á¿¼/_Ï4Óvvo.³½ª¤Q³á½¼!ÃûAPð‰} – @ÂWHñÞŸû?N^¨ÏbG‡IööS–Ÿˆ/Á𪤋k&Lš¿ç)³ñ39¼oá[gl|YUך„Ë4´5Á³¬Ì{Þ®“&ŽŸ0yyÀ»rüm¡Æ´¡œÇ¼îj1ÄÂbˆ…Åk׋Yàä<Þ·`ü¸‰N.îžæpÚ6CðçësZ¯/÷êÂÙ'œÕ«*v½ÃÚ†“BáæÜZf·ðÞ7ðâ˜#î“ì&LtY5õ{{&~'p2f³hp‚…Ŭk9¼ÿ"îZÖ &aÏòÓéZtRØiÇ›¼j;e]à‡JœÃ85ÅÂÂr„Íp›á#†Ÿ¸Ø'²°ÎÛxEò ïE–ð IDATm­-,GØ/ò¾žTQç^IB×|ûQ6Ö#F9­8‘Ç j2îìš?ÑÆb˜¥õÄE»ï}©myõ…j«MÚï4û|&/õ™a·èlR%þ—O0ÒÚk¿JºˆÎÝDzï°üòÀ%[%µ4f\~Ùt¼1YÈH©²:÷+ï;‡(ä.jÓ/\~yà²s½Œj™OïÕò«;,ÈßÄ¡{.˜°AoŽ…ü”fÅ;ª&ƒÞ& ÿ©-QöÒ÷ߌ>WnÜ ÜÜ=rßɵlFஇš/ܸy̱ø”OD1À+Kº±u‰×SfýT„0™fJ³œ¬kÛÎÔN?~-ø¢G—G®~át܆¦ñŸ1m)¯Íz[3lÿƒÈèÈÈèÐMrÁ“½‡–¯ß¼rl.%pWp· 3~NíØÀºM/ Žc;P¯V¦¯ÁûÎ$ÕÔõ]¬´ ¿˜ž^—nÜ ˜]ñy\€ÿP<ðt]ÏGDFFGF<=9K_{ÒòÑjäŸN¸øj ¬Ì˜lAêTþZtbhZ3Î=‹ŒŽŒŒzìïX´ëj&€ªé|âþ“gOž<{úèÒrå0¿‹Ÿj8_.­]{Gt²Ï­§‘¡·:Ò‚=Ö^Éâ+ýÜÏ0åy'î<}tÕÓ$iǹt6ps‚wž)±ó  Ùg“bË¥æÁÓš¶Æ;`ù[ÿÕÞ)Ö»÷Ï4” µù#­ƒ‘@tЋ4ˆ6á²Ò.fDDzx@R´Ñ¶µɸW…?8.:q®hÆ©Œ×©n Nï«9~±"½u5˜ˆ¨Î ‘Ø7¬š„¯×nà´¼ îÿ±wžqQ+]?ÉVØ¥÷â"‚ÄŠØPAQ»Ø+(V,(ÖkEP±cG°+6¼ˆ "*""Hï½—…mÉûÎ.(^ï}^5ÿØäÌÉ™“3“9“IbÞsæ Jzkå¶’b87õjJx4 Ê¶,Ëєܛ)/_× ‘ªmi/E/+}ᙓUãd±ÞË»KW„¶úiÒ ÏºÑ™¼Iwñ™ˆŸ‚ ŠÊuè4± ó Rö‚ì2¨„üIP=Xî¿nÆ8YÝfͪ^j@Èš†Šœ•õEqofëô‰1x‚Æ™ÀDöhÓâ'·’ 7°„6´™¢h!™ÁÌæ%´·ºgvwäúa dú-÷ÜÍ–%uÙ†fý¼¬¶ÆŠ"Û+Ç+‚—;Þ±ó?aQ—V~fѤµÒfoÞ8E«8¡Hq¤…AéúC5ó}?WMa|XáxÇÎÿøàöfðy…íŠ÷`t¡u‰ò’iåÓCÛOGWÒ»+–a½ÚdLß®7÷á·ãóêä,\®©ØÊ‘܌ۇà Œ*|Ú0ËDeÒñÒzŽñ8|”NC¡6jË<_D‡^Z”_,6dšQYäÇŒìrµÙ‡<&©S::üfónÖ]u¦á†q?]!À(Ú¶›·Ï2`t­Ïi_Sàå¿MV<.T‹?„®5l„Ê…ÈBžEëÉ?.»¦£¨PP`¾z£zÒ‘•#µ)Àè6z•{ê¼õW>xcë9ßL‘’½§»oQH¥b8V_UO‘W‘¥¢€2ºOÜà&›/ÑrŠ:Ò6Ö¯ýrÉÍ#fðî#s ™(47¥1ÒHG§O(Ò:ëj£¶Ì>ÅÓ–àœ ¬ï’‹P¢69]â‹çÄqǽ¸D&Âg·ÏË|ÐY“Öom$…‡ùì<^ÀˆÎÚ±e’Lìù]^Á¹< hŒqÞº`€dq›ht”‹òlœúÜ·ç:“ïrôIÁS‘ö2SÖÎKYFP±5%ŒÕgôlå¸RÜr©,9!=‰¢îà%-Ω~±)#1_Ψc=XMMR8‡iJ'Æ.¥ñÖW¥áì׉í”çd†¦ÊL>¢"SW´/ç+>ä)Ø•câœO{^Æöš[Ð5gª7.çÕçº~ëŸõ5åyaÉ×ó»3(ÄIý)Éx4€Ðh-[ècœÇ¨Â‰PÝîþÒ  ÁÃpö×›—2{Ï1 U¾©ejK‘(ÒòäŠb6FÑqÜîµo>4ù¦2OHšçÄ…÷VgTR%^î^àö¾ˆj`·nË\Í.ÛЬ¿½1¼R!åuÈNß¾LI„Ÿ+P3±]°ÞN§"x÷Êm§ô/Ì1P,º’l5C—ý4¶´’UƒI6£bÒíÌççµ+~ym/ú÷GjŠBŸ)¶õ °–Ë»±rQFñï¨Ω"÷òßGÙ?ÿØ“œasµšš;'Õß3Òdõ.Ý;!ú´lœ ¹Ø>ç³gž.âi‚ª¥YWv–ߘ?çíœïIõœ³ =žd[-êFéà\ˆ7$c•oÏÝ¥L;ÞO^öãËñ÷ú9i±ß=ñ!—c GïRе¯)€ ôC,sÈ$qáZüó;U‰ÁÏò‡©Ñà3/Ëþh ªô·hç,]*?/!ïn¥ÚÒ³Ó4ô„£±IÚ鸎•JóvŠòñÊݦ9ß¼cÊdùÞ&¦cn,×<ä—‰Ö_¼.åºÛ•G馻ö6ÞãB[ÒÉéŠ4,½³þAP[¥2çøj#ZÞUëÏ|>¿øåEÊnþúŠáe¯óJ±8z}ª&?îôÒÞ†—œë½Ž&=rÕA“¶kÝ¥`*æŸeuÔo´"ž{w­«×ÛSsZGcF ¯õÏœ¢ÒkÇ:“ïzôIÁ_ïÊß×K‘“¡ŒžCÉ?Ö F5]‹zkY1*Óžä–eUg– º‰ZÝ#ÈÍ»6+ YÁL}¬%I@¨šÒ â€&¤œ]Êc3‡jÊP¡ÊŒûK*÷ÔÇòøÚ[®Ù€ÕcŒlž¸ 9ÝrfV ZJëŸbU¾ÕË×ÏÇÚ­£jxà€¥CídÐуìUÀ¼Ò¯^W¬6ÑóÖˆþ[<‡Ë -U@:œî\¦õ^\À«IŽà¬8°9¶b·wï+î&ŒŸaà˜(å ªb†Kcøtsß½QEÎs6:Ùå6íE{ð#U:…¨X£p{3PT¦]ñ|^/.6±ÖµàdDdªŒ¢LAXA…еz­Dœ5ÚDž„bš†rüTvóª­ú¯~žï­;¤CMjªHYèA¯\;߇3t9ÑGWî>5亖i7q„ÄW’’aõV¦IBIœŸÊÁ:>(VòúÆ=ÇõÊ$ +ôëÇݰzi²…¹ùÈùö¬>fÅ*?½! Z'Áùz©]-~gx™W掺U†5|á¶Éêä| h:úú.ïÎK p[ÿDoÔH] |m×AAä*LTfàŠ“÷çd}Ž~yÛíÒÍ)Ǽæv§5÷l¢´àÀÉK4ßá®qÌóhXŸír(´m¢ÏžP¤ÛYû%É[ô` Š&¦ÔGï xúˆx·AzL”ì̈,uÛíZ4@húãÇHnŽJOç¦ÉY©I”f¾ýü€Ø¿¦eÅ$®œuuuòiU²m¢±'3¡õOÍÏY1 ˳ˆŒ‹€HºþWí6W£š×Éþ7 çX9õá rZ¦¨7kIMuæ~ ùVíŒ@¢ ˆh倷 oðúÂz6‘·Ów´¦£üJ.OŒ"FÕšuL!+º,-4Å/TmÆ–Ö?U†Ë|S^Q’DœÈŸu ÀàÔƒXÓe™ Ð(T@õAg/ˆÕBu_ÈôÝÙ¿ôW½°ò·>k÷¶ýèÜ>(ð¥Ôì‚*(“€WQ—R`ÝÌ# Éà‘k,6¾Çd&žô›Ñ~¯¤´’¬†œeoi z[šÒ§æ†][¼¹Q>`ƒ!µë6´#Ñ^y…À„A”½ #YL4’n˜µ'ÓÉí9ïèµ4¯ ß蔦.IîÐ"ŠwåD {Iœ̓d”„v®LT½´Dn°¡mYnVHpÂÇÂEãÎã|Wpî8¹01SÞruwq7²6C|*æë"MÅý®xhHŠbƒrô¦5¬QC˜F.—oXG‡‡‡?ñpz<××s’Ê?êsðÚ„nÿ…²ü¬ÛíkqÁÓZñw}0¢5û’ï"ÝVI|ó´Êгw›6w÷™¡—]û2ä õÐ'ïs¹ýºSÏwîûDL×J¯WåqL>ϤQ 7Ýo×5É¥ë$¾Ìžâb­i4RÓhä¤1n~œîè¬O w¤MžR@Óœå¾ØB§Nü½ÓÎýì«ø'–›Ó.ÒÊí:m¿MA\À€‚B¦R:¾b6§•üªÜœ²zŠîœ£gÔÈ€sÊKêÄå¤ií¢±õOgùoÊÿÃè% h;ã!rëòå§,8²h‘·‹ËEÂG0$czEpY%p6;1‚/ß›FB8V˦[h ´’×’®Ï-ÂpüŸ+“ëC¯‰¬¨ä^Uº7£¼›XÍóâB6àu5ovƇ&ð ¯ÆÝ G5G©[8)0Kk3.µþYOïݹ|]ñŸÕu%yH[ õÕÔ@š(º (€ ² æ uÎPšþ W“›áïv ÝêÐÁy#l²bS$òYb-&(ŠÌÔÙ]xž[XFaˆgè«°°Wa÷72„öJ© 6ÆÞ†¦°q¼.=ò“ »¾†y‹<ý‡lh» )—%auÅyÅu‚TEø L­Ç±òèÛ‘4óÁÊüij®A|œ›õÔ/MÏÊ€®Q¼+³˜ÂµÑª]ð$<Ÿ¼‚ˆ ”ºÎÇä"êÕá Þ²kÏ_ÿýüï‡>“Ôzo¼äi­®l¨Tö68;ýu G‹%Kú‘xhÈéÞæ*Ñk|ø…“è³À%ˆjjãäºÚF¡8³‚ÿc°>%¢Ü°¿YD-ÿØW‘UÆ»ØQy?Èæ˜á,©û;¼Ÿg°1ÀØ™!ÇvÜeØÏî-«?m"õþ_¾‘ùx¥ï¯ì¿š©ÜCž&!Ç8}ôÖÇ¢z ãÇ…¾­RÔ•kNî:Ò&€©dTÒd…›yŠ×¾GùüæÖÑÉô”bûHSTé¬ý JßÅVbxí× W|#ÓÖmŠ®5H#çÁ‹àµIAÏ«X&Ú:¦Ú%!y<À*¢<×Íì¡Uø0±ÇÙ_|—-OhIo¼[ÿÌ—4ÑéTþŸG/AÛ–+”nNN~ûôééêêR2™ª¬¬ëè¸Ýß߃ðÁŸ sH7óÔ”»Îù@Be‡ëZö"¡ 5<ñævòT'%†wÂÕ7T1¦’&RUuõm BÊÉb¸Îˆ„”{.ùBR³cGåËSžºÆ`€H™ëXõ¦ÐUUe=.>EP”¢=ƒeЭº¸ÕO-Õ™¼•8¥? *(ê IÊÀÇ€b J Ð ðÈVÝë(в‡ÔÙÀ ú/éùºøËW>Õ~žky€nìà5r榱»fN©IÓÅ{F)ˆzªng2Â{)r‹¶Ù»wöÔZœªeµaGo±nCËH­g{åxU°ûü;vþ'Æ [çúÑ}ôÛ8E®ÿ<™:šÆâÅj›—Û_Ĩê–k·›Ë xÅk÷ùwìüÉ ›aѾx×"¨½È‹õ+>y8O¿F–ÑÑ”ù†6ázAt& Fm\ïá<õ"†Ò´­7mïÏ@¢(FKÕææ€†uSFën7CkÛši·)dTfà²z´ƒœÌˆsubetûó¯ç°l`àþ“‘ãö˜kNûëõ¸×jÛ=MVÏ|ú_‡¦èPtfíßÌ=xÜiÜ&6NW2¶u;8—Eè½|ÿBïƒÓO”ò@LÍÌnÝÖ±­šE´6n|KðH/Û™ÛõòÒ#ö˜6ý”$=°ýBbõw|ø¬3ãQÄJV‚—…e@¿³^ŽQ¿}]ü͈M@@@@@@@ðÓ’t-]z"(è ŒX мÆ`#OÍÌÉIhØ@ÕŸ{寖õä䙸F7ÓaãÇ+îüñ¤‹Ÿæé^.Y[ü2œ‚GdJ÷›1ÏÔv÷Êö1âì Á/Ãlßâ:LðûÒ²”–é­ª*ÓVWCÏàUU•"a`ƀܨ;Wn=rbÃôƒl.%s€¢:vÉùðg©ùi¹Ùõd dAùiví¼à÷9÷у7¯ÒòCÎŒ•D‘µ9ú5?"渉¤TŸ%;W¹¯è-‰EÕrñ™“ó#ÒòžÒ*B­ˆz²œ¼è¹ÙcÏ;ï£Ë ´>töµeÝ™:“×\{’–‘–~×ÏXz“9ÓÕ×nÍHŠ>° ·X[íW*ÁçìÃÀäüˆÄÈ}˦Í8ðøQj~Äûû‹M$Q ©Mððz‘‘–‘–r÷¦{_&ñu=‚?•–;](J&“)P"$R !“cÅ«â¿@/ û³öP“÷!äïKG.?½×«gË<óžïòYl‚›Ï²uÞi1“î€Ê([$¯(!öþTç/ó!Ž}¤þþlêhDÒ‡—â«A¯ùPô>‹®]š¥ÅK Øs&ï6nK£ßÂíBj­<ÓÛßWCéŠ=FL3€âØ”ÚÆ‡“ý5©Æ`ñŸ©ª¥á‡]Âë®ó·_æg[]i,&ð|õ„¥ÖC·± ]Ö¢µ³J1˜/n\âÌ?xøúm´s§oÝ•\d7`î†1w]Åvx/6¬~qj¥o:EÇx¤Ž8ñðÿ:YƒK@ÐŽw€‚€ð-á ?’t1™2ªªz…ÏÒá#€QÓVÀ%Ǥ”•u6ð’¯Í_¾ÜÙv¼¹SÕxâ\ã‘:ÜÛ³lFi€¥óËÆ¢Úfª´†ü$ÁkBÃóQLcýæSÌ'ô×BíS!çÉõOu ÚlEÇz¤@ÆQ÷ãé<?šÁ¦kÂjé ”t!}ö'öØÎcqÐl}hªÁ–목gNœ¾‘…|P°µ\Ümœ¹¶_@êcïCYŒ4CË¿†õ #Öò!uN*•òè˜W@YLŸYƒ‡ð£®{QÄGÛmÕ‘RäÖr$FL_-÷éCô-Ÿ÷å8i|Òåååäè¸=åÑ»êU¥°`@!À5`±L4•Ðï§žõdïü‡{$i8ùÔc×ÌýU)Ù$²N®^tµ áá,¼¾¸–¦Õæh5_üî•Nq°p™d_:¤^}œÜæÁ+!¡ à7}K©VDEÒO»œ‹­b|‰û”ÅÆ:øêsóG÷„“ EIŸ/ÀÛÛÓI¥ð[AÀǼø»=Åq¶I?ã¡vfCí¬lÌ}â‰GÌþ“’’B8à›tïÞþ˜·âß !·„['üÆ ùIøû{ØÚ®Šº_ìÅç"|D\ Åb™dçé9§!é’¾ú;óí½Ð7_ËÍAT€’èÐÌÚŒÇ/sW8hÎ_êTzçM!UgèhóÊó.¶;\ý—kOrœf™Í6H¼z7›×ÆnƣМŽºk¶¹óãZ#­é~‡D¨µ=¾ýg#ÅÑO_„TuTMnÆÓW+í»/Y±´ô:ÎA /(<ƒÛ@±ïø)u´1NƒÉÀ J`«´”ú®JµƒÒcù6gå˜Ç×|?¼Ý{ª]œŠAH@@@@@@@@@ð‡Òþ†Ð½{‡—/?™——Òðqd-¦Œ °Š÷ï¾d1Ña@P™|mËöó‘U8þáäÌ\÷õ6ö[7;Ô| ÜS&ü>BNÒ〔Yk»ƒ êvP® Ý^vÌ©™óyn¶ÓÝ7¡P—ñôTvÔ}aµ?ð¢Dö‡SsWSön²v=2 ¸Eï.ïrÛ÷¥N¹/€¤þÌ]êü¼—¾\_–H:¶*õ]•j V•U¦8ááùt~ñ§¿÷n=[GDÁ ñZ=‚þä#§¤¤Ô…¿Þ·µõ— ˜–WG ®Š=zHÌél2`…œßçŽ4o¶ ]ÔÝKÎûw\Kû=AŸôcfpã?6®ÃÓTQðÈ»Öl³n'2ÿm––CÑ(¸ü: ›‚òQPX$Ä/Ö; S/†œáP묭 ¥Z–òrïYsàï|NÖ²ËsõP9¤úã™5ë®$°QÅŽí°QoxQ èéº9loù g`¢e±·>í–û¯WÅéœø¿Æ/)]aŸp É-äÄ6®é0N~&Õ¡ ­æ>99¬ÝQ°²Ð½»2¦˜§ûOƒ©M½uîÛ pº3C•ÿ/²Hˆuoˆ ²ó¥íÞ—¶{_X/œÊdö×í­Ò2+W—<[÷!gÄÀ¹d\€óÒï—I $¹Sö¯>Á‡UWedð0âí,ÿ[8P`% ÐËc.°œ¡Ø [ ”AÞX¨öëe\müÐ]û¾N¸ð"üճÆA[=cë8IgÜîvÛô*üÆÜÏAÅ€ üãå5k6}®Q”L+Ï íå¥]\ã]·øfØ« ½š÷Ü/$s»nCK¾ÒÖj—'‰ÉÉÉÉIŸî®è­7g‹©cK:ÓÌN{[7ö܇äääää„À%ÝȆu-‚:õÒ?î¬Ê?Ëž“öáõ)/2õ†kQ€—î·õøGöoÞÿqSÎ/\ æt)âsâ—È s©~ ú¦q;ÈQt:“Á¤ÑÿÏfT&F§TýŒ«Uû:"Ä=‚_"é" h?Ь þàÿRfòVm¹¦[IxMù‹ÑéƒÌ›++†vT¬(ê³Ø YÚÌw©)•XýG×g×Þpq`ßšù:¦ &æ¯û&\u}Þß÷â¿ësàx}UÔÎЃAû&<öÜ•UVÏþp<-/5Õ^5Ÿ“|öõ‡g‚/œ+ª'*êвèËÎ/Ïzæ½/§R ¨‹?~hÚ³ývÏýn—|t :ÿŒƒ@}Ùƒ9¯¢‹ˆÔíGPT:G€. @± ³ ÷Bãí…JÈŸÕ€åþëf\€“µÜ·LÒ¤Âèf¤ÂÉ-¯Ë.0·1d"$•ö:‰÷?×7íÎ¥ø¾Ûö˜Ë4 ƒø"dZÞ[™úŒc½l´2•2ÛxÙÇQ“ÜeZF«íŒi‚—vÙ=Po³³™$‚—:9–ñ„ôTknƹyï“*’Ø ê?ÐÚåÒç\ذ.­|å%næÝ¶C‡ ·tØý¢Dð anæÍµ‡››Úl~”/:B«*ׄ-1eÅâYÓl,Ìv]8ì<×ÁÚ|ô²ë¼ÎÁÏ{´uÊ(‹ÃÇ.:[óu"Î /çÕÕ,1Nʥό\¬•ïÛüµN]¨œ~x³µ.“$ =›-GÊ}O~h|‹˜ øo7KËA…'¢ÜϤ¿©AßÍT±xïÙã-----G d±Ì¶~¨Y¤U«Kò[c;ÂÂ|°‰é8×;Ùœâs-V†VâPûf㘹÷r3îmš8xа¶Ëœ­Fo‹épr¡½ªºì;{/I¼èâö´˜W~x®¥ÅˆcŽ„— &lùH{—%3¦ÚZZØl|˜/à<Û=cÔðáÆŒ_u9êÁó9÷‹1¨‹Ù1n^˜²qS©(M‚F¢1iÄ–àOJº–/?µ`Á‘E‹¼]\.®$ø=à§§^?Vk²«¯¾lã%©« w{œ.;lr‡^•ž£¦ÕC_ÅX¡ìuD†ÒºÛHä?)¯Ç“˜¥ª©ô1öY‘Î’›c׸ËWçó¿ÓžúÏi±Týe×ǺÝ0Óý’“O7^ÙMUW×q£*)2öa–Ö?Ëõ×õŒûø0š‡^[DºoøÊs¦ºñÉr0vdÜÃD•9W,ן3 =IÇFÈ–>*¬7)3UQK_ž˜,üÑ8‰0­e },@ ðx5P8ò>ƒÒŠ_:㔩7bL/)àµqO¤;ô¥WdUKhÈP¨2J”ò‚Z•µÀsÿ‚Íkëx"dZ¥B{Ë ’Ëé´a’¹é€q®÷ ¨mfë¿ÏÔ`Ñ´ IDAT†fñöÆ46Ïò0O?êü•¥Dj”w ÷()¾žÊR!ÍX£0Âgóµ†ÎØ}çÕ닱Ó+Æp„ ëÒ.a?Tç?v÷,œá¸ß¬¶M 'Ò¥x}Åî\Hd¨W¯Ÿ»íR¾6UàW).>pïò¼Úë×é+ÏÜ=kyånËÍç%åÖÉX‹S¿¹¿½GzdfýtÂgPPô&Zb¤!ê}w¼ì>¿ý7ï]ŠãÁ` µ¥wÑÖ¾|,ä`•ÑG–`/¿°{¬R“$†îÜo¥i²qÿ5†¡ó•'ÏžÝÙo¥¬m»Û¥/]t‘æ8©þp9¶êVHXÄËSƒbÏÝÍ’âÐýËÕ8Ô~º£a§µãHþ ÿ°°§žƒŠsØÌÑ´W•£l·yŽþ<¯}cÉ¡{Ò¦^ ~ñâï3£ß»ï~U…¿<_jþé›÷î{Œ9}7ƒ[ºkwÜèÓA/CﯡÜöã[õ˺^"vü­H•i§ìnª# ¡IJIHJÒˆëÁ/ÆÎ-_~:9ùíÓ§§Þ·¡¬¬ëè¸Ýß߃p(Á/ ^[úÌ=™º`بžä–YÖüüÜa¦Ó4â½òµÜU%D®ÔÔ%Ü«R˜¨È¤ÐzŒg<½•[nÙ]ÖX[ñlVV,ùi™œµ~íóx9U2 êêôÓ2¾Ï$1“>3$ n&¥•%•ò{rñ¦V+(z]ZSqvÖÀêé¬Pu”Uº‚¤ °NP]%9¦—< PšŠÃ9`—¤_LH,U–}T*gcÀ$& 8Tp€v‘Ð18`éP;tô {0¯å—諉;¿bå‹Á.YÊ#ŸZª €t8ìé\¦õ^\À«þòœ³ÑÿÅüÍ^Çu{úÌü6´ŒÚ_\ˆ5X°[­á\Ä•4Äê:°³í–Fa€~ëÏökáŽ]8ÖkSX6×XÚÆ°hN­UŸú"UÍn”A´ÇO5¸›ÿ —"Œž)’PN_E^R­€Òa•ĺ Ñc $¾ªŒ¼¡±: HR*L~ëø•fœ…s¦};ÆÒÚyžØÿ*þÊÞ…‘F씬ÿ|Â#Òb×9=Ú—?¡{AÛ†‚ €Cõk÷Åa2‹îŽU£tðO¶º†õ¼>ZÂ;/‚HÙ~\*4ø‚wjRÌË"öhÈšÆ:r;¦Ê˜z磖ýªÒ‡4<5(¢m=½÷å莃YHÖtµªO}þ6=êÓLKO°ÙŠI%üˆ8k¸]iNRm]æ«$ʼn›uè@¢[¹o µo£ -¥qó–ÃZ¹Öþ 1Äó\¿ÜDæe\§^¾¼ö÷«ói1Å+³ò§¦Ä°ƒ>ô¶µ]K8”àFPÿåpt\O£i6ÌÖ9ESoÚbÕ¾«úéż\'òA¬ ÿÍnÖ±=ž?]ÁMIû!’ò¦º•Þ¿O”01¦"­ÞT‚~ïzt¼*øí±™¥¨x·ñº}ÔÚ6X Už3Äõºåºë–k.X̱cPRkÕ8´¬¦Çërkk(2Ì81Ïó£$ó„?u À{à´šüçô P}ÐÙ ²ž  ™~ð‹¯à”†ÿ5sù Ó#—7 ’F"£)Q“WÁà–ò¤•™B#aüåÜ,‹Å¸-†/´WZFU^{ÔDcYI®ÿÄ¡bi‰™ÏZäë~Ȇ¶ ¥<ú~¦¡ý)ävJËw¨YPáwýmYÓ-5„,NAÛÖ%DÔmŽ„DF‘Î…I$*m›)u\e¤IAÐï²GBjÀ¶G¡\,äro¯ž´À?Gð¿I@ªã^p›ÈóÓ?Œ}{trÿÞ¦Ž×3ß»YÍ»™/ø={²’‘ú9"«åá`NVDÞ£'kÍ:á®sßãzz‡«Yëϯú«bá1#&®šè|;“¤n6eÞ-*ŽH›Më‘xçuÄí8]ûA2hË• A;›á¡ªå,bÔ˯< yôèþéÙÚ”LBZÍWáXóÕ•_™™YJéí`^z÷aP@,ËqL› E âĤ!ÁŸt%'Gǧ‡Á6wo?¨1*Šº¿|ù)§¿&XIàûš3V«´Áád”„"¡`µI%ßûû|áQ4V’Qifºé¡Õ¶‡VÛ›>ûî^¡h[K眉ÏÔÖê&AR*Yþ´ R‚‚‚wÉß¹¼+{[!nÕkŒ½&K®6=O€c€ ðù•3“¨ ÌÈ©œ]þÜåe`,_È2TÎT¢*¤ ŒxEáƒu1 娊µJÍ•ø¬ÚÚÄIÿ]ç P’‡´ÕP_ €A}¤ù€¢€ j¾Pç ¥é¿p59)¾Ë¶$Oñ=¿Â¤á…‚då!CÑ÷ãª1Aþ‹©ºV=Å…‡ŒB2J#/}MNNNN~³«Ÿ„Ð^i­ƒðWOkqœúޝß[DzE^ì‡lh[‹ô°L•‘†Ì¦ñ¢€]˜]Èë‘¡¹QA+_xï HªÃ%¯®¾c¡&hoX×ÖBÇ’cfåÞþ;‡¼Üû‰u w±Ê?b)nÿ¤9w©ƒVmwwT*Lù‘¯µüêCJ«’i†ž}Iüí?]«ÿ¾ÇíU~×[âý–Í—½¾f÷ÃÔZ °Ú´G{Ö\cÎYÚŸˆ˜¦é`G÷ø¹ws"//÷¬ ÐÞqØQ»ñ¾VçE¸Y៥í7¬™g7T!3*«N€ ’&öŸ|ϲ7“cÑ˹’Ç^nðÍøN–ŠP… $„WÇÃiÚæ¬ü€€¸¯=6sÊ®háGiZCYEB²¹€•…yÌß\Jíi?ºÔçàÇ^ަí&LȪ]l5ˆ÷÷ür*]-°t鉠 30`-@ó‚cc€P<53''¡qÒLwÎãWKuKL5ýëCˆ™l œ$—ä3rôÕŒ:n²ÎŒG+Y ^–YüŸP7戃ïü†ÔÝ\>ÜÓàæwkn.5Äù#›?;ánquFñÉñIM›è&ÇÆLh¹ª#bƆSG¼¸²'½ÛÑnò­›¯&æ)Wo"£aä…Òôf¨Á¶´ô%²z½55ª‹0+9:‚ #Æ|x{Ò>"'!-Ž’PÑã!~Z‚ç¨Æv$ck¾lJ7 ÷WG_ˆ1UdUu‘ò2 Ñ’ÕÆ_Ÿ^I[éÝÏ.áÿì§ r–ý¦÷§ ïÚ)Cćôµ‰}wqF †µç 0RDÈÒêÒÙ5Ö²tâ¤ÿ¨ ¨+$)Š1(‚B/À#[õi£@ËRg3èÔ_±’ì>'cò«c¬úìèùâ²Õâ½¶kÖÊ™!kOX+‰÷Òzt&#¼—ª°æàÄÇ«ÁiºS÷z‹ÿsZCk²2AÛ^²I ¯|îlsuVˆ¿KÚoÁËš„ÇìÜñvÕ"‹+8U~ÐJ¯Åzü®B†Mûþ,GÈdêøÝß­ž=ê4EŽ¥#GíÜiܯ]¨òÙ#ŽÎZÔmåÜ‘W(dTÞb£·áÿ¤Ë¨O Í`Ñ¢þYÝ EgÞésÔ½;ç Z_†Ñ -œö¥G«+hÜ;ÝcêÍE»ŸŽ8n­Ðöó3oÿQ,·u  .^ŠtZ„n8g–ÂÊÙã+É«÷ë¥æsp KõŸÚ·ò•`ª‰$BB¬v®‰\aoî%¦¨«Â¤QIŸU¨n?|Žƒ£Ì=¿={c]×XãYËv¯×`Iäuû«•ÌÈÛÞ®™?ú¼‘0Yá5I…D“0Zî~é´~í3ËçëgÌ ñ·‘%–küJt9^œÕœàÈ-=o;ÑRK¬y»´Ù¾»Ûmõ¥©P•~Ù{Ãþ×¥¢•à5}íwkC)NAFmzVºõ ’êØù»¶N±èÎD°Šø;ç6lº›À¦}«ÁïÏæá÷þýƒ|þ»é¿v÷oýï*ë36 ö¾´%Î;ÁŸ?çΖÍ7³ZO|Ó –yn1—édy,*©­š¿j樽ŠHö_rh çþÆ9]Uò#É^q Ó˜ Ù–žþ†DÆEð»Ð夋ɔQUÕ+|–Œš¶ .8&¥¬¬û½Š5á·NLu¶àºePÀ´çU Û•{j…¿Š–›lÚ¸½«Ï ½¹Ë+­”¢5aóR«•n«‚¦î(­„ÝkþÙÃÖš¼”[ûÓ´6h¶o¬ô^ ¯^œ-óÌ{¾Ëg± n>ËÖy§ÅØï¼ÁŸ‘ü¶>u»ý—íºZФbµë–U« ¬PÒuP›Kmˆ“Fðg']^^NŽŽÛS½«^U ë†\ðËTAAã{¥<òò (`1lmæiÊÊ3IИt%\ws (èQc<[]VžI‚2vfTšùÇYzrŠ ¡,A‚R‘J(òãGhdûìÜz4…:bÀjÕ¶G¥èØŒÒKç –›´Í´ô(—" ø¯’.ð÷÷°µ]u¿Ø!‹/ÎEøˆ¸@ŠÅ24ÈÎÓsNƒ Æ©åCNš PTYpjÛ}çÖó@Ä“ex}-§q;IÍñ/ÿ}8Q~ûþúL›é¾u¸Ð·ýZ)!‘¨«¨ÇDÛŽ $²N®^tµ€ßx¬JÆÜÎKüþt²—€ ï'á[Â-„þݤ îÝ;¼|ùɼ¼”†#+(h)(h4g\ (üð$ ô‡ì?¿éf ô>Œø¡¤ËŸÕ@%5äIUééYÕâ#Uè¼¶–—ñ¹ÊYUÏiÞŒœdt7¡§³¸_æ®pМ¿Ô©ôΛBªÎÐÑæ•'–…t^Š€€€€€€€€€€€à¿MºÀÇgYg»yéggobüµtúȉK‡V‘âuÊýT&¯Ë‡ä%]·Œ€WU„®}ãv Gè+‚윋mÊFßzžoÈý˜äûW÷?«QÌWßC•ßs8¬²êý±Ï眢Ï~waWVJñßóÄ«*^+(ãÿSEóà>àÓ@(z€EB\¨çàÅm‰;Aðë¾¢‡Wâ¹lªÝ[[—3ÑåVuƒ£Ý¤)‹öç7?b+( Ý1{˛چx-ÓmÂ{9™öΟ2yâ¤i.¾±UØÚ"éD¹ ÿÖ|ó¡ææCÍÍ‡Žœ5‹XYĉ•Slí&Mžåv#‰w*,lX—褂¼KæMãu"\åf·¡©Šíà†^bg7ÅnÚâ}Or¸ÀË}vpñÄ “íg­<œËûçþË´=ƒu1û]Χ4XÀϽël³äQÉïýâ+¬:áöÞ¥“-Gš[Œ¶]º÷V|5ÖáIç&pœ¼çñùy7¿c·Û×Yœ|7?EÉ÷S¹ÁnóÛÿæpXů73xm|ˆÞ›?fÁÝBâÝjÿIÒµ|ù© Ž,Zäíâr‘ðÁÍT¥¿™‹¿™‹_ÿ)ãé⽕YŠ-«Èðšª×{Ó õ˜4™IÕnHJJŽ—Í\4ø›NJþe– qÙo÷%'Ë«;ž1]yÑh\¯êçrJ~t„…Õ°ó²¿ÿBUXA ôòÀ˜ ,g(¶†ÂÄVe7j§k+~ÕÙ-¼ò×ñÔÑž·ï^Û¦vðì—znÚµ}O5·\¹}çäÔ²sž/Ë0Aeüí+vç7¦Ú¢dZ…›Ð^^ÖM õ3Oݼwu£ZÐá¼®ÛÐ2\ogLgÊë³>Ö ?ô$ìUXØ«ßYšN²ŸwDÏ×oßõ]„x>+Â:& Öµfש—þ0^uìD¦Õ±€Û7MÊ;ëY%(úûÀ±4‹#·îœ\@¾¶ï^.ÿ§[ø®vg8™9ݪS€—}ïà…øºß¼‡áe\ß°áØ4Ï»Áa!÷L¥ÞÛ¸! «£N¡ˆÓhââ *MœB̤w1ÚªRbÓkp>$ž¾!øù…Ò­ÓÉÉoŸ>=ÝðZBee]GÇíþþ„§þðš×iO¢˜£ö)I“›Þ•É®y{0%טek':ã=dù˜tÅ—“Àq.ï©9ÎQŠ À/,zº)£ PÀ¨=i¡ágÞHyùº^ˆÌPmK{)ò§¤k7AŽÆ«.æRû«¨U”çæÖ³•Ô&­V”FÛ “b“üncÔ‚j¾yϤ„3©ï“xü:ŒÞWsâ2zÃE¥¤ô¹gNV5Ž“Åz/ïnÒÔîbÂMÊ‹ã*M¶—f’€¬bÓÝR–MÂøœä«©¯¢8@ÆèXÚJ¢±_¯=WžµNŠ*àĸe/êeZ‘âw‘%sk*y8KcÒfÆ•‚¢,ìÉ)±ÉËe¿ï@EåÀ:t€Ø Ðù){A¶áC•? ª'Ëý×͸'«Û¬YÕKYÓP‘ó ²¾(î­Àl!1OÐ8˜ÈmZüäV’¡óö‘PàE É f6éÞ[Ý3;Œ;rý02 ý–{îfË’ºlC³~^V[cE‘í•ãÁËïØùŸ°(‹K«N?³hÒŽZé³7oœÒƒJeÒñÒzŽñ8|”NC¡ca–a]º½+ÊK¦•Om?]IWì®X†õêTx ¸¹w.8ŸW'gázpÝHÅF·!’ƒw\3Aé¼¾º’CfŠ‘xÅ EŠ#-tèJת™ïû¹j|Îa'_D‡^Z”_,6dšQYäÇŒìrµÙ‡<&©S:<¨…îÛ#îg£+EÛvóöYŒŸ?2m€—ÿ6Yið<:7ãöápã ŸþÖ!öç«7ª'Y9R›Œn£W¹§Î[åóèÑ A]~Èå$¶ÈkÃpPzõé]ÃêIë'¡„%]pÙù¼\P‹Û^'²Hëy²ò¨s»¼‚sy@Ñã¼uÁZô–Ù§xÚRœSõ]²cñ*"¢Ô¼Ž"ŠÔ^˜µÉé_<'Ž;îè……ô'öø}®à°ë%¬8à6^²!þ Ctlüâ0Ÿ' x1ÃY;¶ØˆGúìöy™Ï:kÒú­³HQæN½²wCwcÙ¦Òõçæ”y8]$ë ‹ËË0Ã%\{¾9q+)•ãþ—Îî1}z×5ø‚P™4•A%ò.‚3éZ¾üÔË—×âÓÂ`€)@ ä?KI~øÖÖví½{‡ güó^Ù/Õn2Ô‘jÚÄa¿?˜÷%GÊj]g— °ÐNaãºÄèÃ,n Ít‡¶³#12©÷0~ jàÞ׆V¼1óKlŸ¼ô°<»£rLœóioÂËØÞ#¨/§šÖS®Ê¿¾¶ˆ¼¯—½ 'Ò-%!_¾_0`ìRÚo}UÎû’‘DQwð’çT¿Ø”‘˜/g€W„æôcÍ™*ÆËyõ¹žßAi;°fgÖ†‚dsg@¦é §@MXÚËLY;/eAuÄÖ”0V Q¯+%f©£5a›Ò¿öŸ­WŠ[.ý3.àGÖ²…>Àx<€(œ%ñÐíî/qÊÐ<¬!H¾Þ¼”Ù{Ž­òM-S[Š @‘–'W³1ŠŽãv7¨}ó¡É7•yB2ÀD;Ü[YœQI•x¹{Ûû"ªÝº-s5»lC³þöÆðJ…”#Ô!;}û2%~®@ÍÄvÁz;ŠàÝ+·Ò¿¼ÖÀÆÉ`‘‹ísq>[qæ‰áò(‚w,Ü«aâ]Š !?Ô…>Slë`-—wc墌N…Ù˜2Ω"÷òßGÙ?ÿØ“œasµšZ6B¦Óê>z®Ü|/]aÆQ1jbÑýd«ºìè§±¥•¬´ ªDiÖ•]†å7æÏy@;ç{R=çìB'ÙV‹ºQ::S™_/ÇÜëç¤Å~wöć\Žý§]û3 (ýË2I8©þž‘&«wéÞ ù­¯Bü²„¼»•jKOMÓÐŽÆ—XàU±çÜN°g{nmIŸ˜¦k·ÀˆÍ}ÀÉëªàµñg\öÍ]`@=(²Hsÿ_ùÚóX–ÕQ¿ÑŠxîݵ®^oϯGµU*sޝ6¢åÝXµþÌçó+¡}°Ùf‰(ÕADŸ–+$ üò"e7ÿ}Å .æp0ÕéŒÿ™ú¸# =Í= €—ý¸ó`Ã+ßxMz䪃&;l׺‹OxU—R,Ž^ŸªÉ;½t‡·áå5¢.Ô•E²Ž—wô#%z/8ð¤àÌŒSïñ¶m²Ð¢X´ø°–)Á ’.‚Ÿ~Mmý#99:>= ¶ø¸xøAQyTÔýåËO5 IÈHË9:RU´º“‘–þ×0æwSr˜_~DZì:#1¡] Ê›þ’ã|,Зø¾Q YgFP~DZˆƒ&ù :œØ¯«ŽðÌ'OÓ3ku;ˆ_T^¤§;Ö´6ôBymÇëkÚ,/¼h`(Àè%«(@¢jöA >× ¨Úr2BS‘ÆÙõ‚Ò÷ÕåñÙ·\?^\›ð.«Êæá5)91@4º”¸Š (I‚Žq¸˜°0USZAPBë­ee‰§=É »˜›Y.à4-E‘0â&Ü8”W.mfÕ.ã@pá«‹ ü}½ô9* bŒžCÉ…ëE~f¢.£ @¡Ê01vý*8@»áKƒ qÀÒ¡v0èL†ìUÀüuÅj¯»mè¿eÃp¤UU‚§£RË´Þ‹ x5ÉœI‡\vUû{·÷‡ÚŸdà˜°rTLAUA 1Ã%wLÕg 4µáÓÍј¨Ü‚Ѓ^¹v¾>¹¿¿_ØîSñõ 7<ãÔÚ°Ȱ[ׂ“‘©2~ˆ2¡jŒ˜Àû–KqÖhy*®i(Ç/c·ë›ÄŒÖœxÐ(ÔÃ;¶^aôFgÖk·i¶3wDÈ©Ò)$ k™vGHJR2¬ÞÊT I(‰ó…žXm}P²B¿~Üë«—nöºW2p¾=‹þßÄ_å§7¤A†œ¯~žï­qÐùýÇÂ8Š´ï§ æÝá ×*†Ï®Bédî°äÅÁ‘¦[\‡Ê¢ß(ÂÉxý1+æôÊYŽÓf¯½šYWV&’œ±EdESê—÷¼öÁÆUª£ˆªæ"Þm%‘P¦ÉêÝ“±ð›Žy]|SRWËmX:JVüF°qs£ÓäÇ ×¤*c¾ýü&øluÛÑZ4@úãÇH&DåŠzžÓÈb @•Ó’ä µšæË¹¸¼œ¬‰üdZr‘¥KO‘k¨M[6BñÔÌœœ„ÿЪ‚GdJ÷›1ÏÔv÷Êö1âTüû×v^ê¹”¤î:ÓGÓ[w¶dUÕ±ÓeåêIÙRž¿bN0§~ÿØ ÇšG~t@Im‡ˆ"o§ïhMGø•\žýÒJoljÒxq®yìzŽ•SÎ §e6-uB¨Z³Ž)dE—¥…¦ø…ªÍØ¢(ÙæZ‚0º1HÁ•U|Iù†þ€W÷þx}†–d›š4 ¿+€ šÒ …ö£à?ê@€ À©±¦±' P¨€êƒÎ^«…ê¾éº³iÏ`åo}Öîÿ:lûѹ}$PàK©1ØUP&¯¢„/¥ |G“,$ƒG®±Øø™xÒoFû½’ÒJ²r–½¥I$èmiJ;œšvmñæFù€ †Ô®ÛÐbŒD{å @Pö>0Œd1ÑH@Èt¬0&SÞruwq7²6C|*æ÷Ò ‹&#íÓµ‹¯P-ÄIM*¡¤6cn²È*“È ޶‹/¬&925¢Ë@% GöA½’˹z¤žóŽÞYKCðªðNiê’d@šŠ ú]2%\.ß°Žâáôx®¯ç$•}`Š×&Dpû/”ågÝNøX¸hÜyœÏá îÏ]<­ÇêdyC=ôÉû\n¿îÔ¦ã}"¦k%OþLV³Ûc—äyô¾µ·ƒ¦è¼«>Õßýx•£÷Cµ€w^Ç(ºsŽžuP#Î)/©—#l¹r ¸C¡`UªÃˆ!ü!S)Vì¾èŒ2¼ßøž´¯Ç8a|#Øpk~²_•›[ÎÃÛ]°æ Îç6=hŒ’fT;«AÅäääĉçã~6-1ÅáÔVU•‚i«Œ«¡ÀgðªªÊ¾cÎŽ¦3y͵·!iùiéwý÷ŒÕ¡#@S›ØÊ IDATàáõ")"-?"-åîÍÖ>Ü?íî¤E$\4q‡LõàäEÏÍ{Þ€|]¥á†Õ‡];/ø};SÄ3]}íö×üˆ¤è z‹µmTÕ±K·?KÍHË}xÌ®'i¼–àsöa`r~Dbä¾eÓfxü(5?âýýÅ&’h{ûÝû2‰οiÊUœò"GÞÚI¦}WKBP„)9l™LÉÅÔ/E]x„›ý¹$·Çëjßáj}„³5TÆH¬æyq!ðºš7;ãC°Žµ‹n3˦[h ´’×’®Ï-š>º†^»ŽjŽR·pR`–ÖÕ Ý{!ë*÷¢߬¨âðyÙwÓÞåÒeÈ2ÆôŠà²J>àlvb_¾7L#ã¥ìZ> *SóEÏ"(àÁoÿ& t(ÉCÚj¨¯À >Ò|@Ñ H@5_¨s†Òô_¸šÜ ·éV‡ÎkL*ÈŠýM‘Èg‰µ˜ ("0Skdwá2Â2 CÕ~žky€nìà5r榱»fN©IÓÅ{F)ˆ÷Ru;“ÞK‘[´mÌÞ½³§ÖâT-« ;z‹ýsZzþží•ãUÁîóïØùŸ3lëG÷ Ónã¹þóy8O¿F–ÑÑ”¡tî4nZ‡²æëVÇnwv]@’ìåàáfHg`‹«m^n£ª[®Ýn.ƒÖÿˆ…bˆÝ ­mk¦Ý¦Q™ËvêÑþýädFä蘫ÿa^Šæ´¿P{­¶ÝSÑdõ̧ÿuhе®¨a¯†­«õà ޡƒv’küüÜG>9¥2æMÆ€¦7sÒiTvø†M_vz̜ʒڸ ;M˜H4B¡\užy˜Mîf»e«>¢Ûwó¢Ju8/, Q-¹S)“å¶­žýB^FÙP_Í/å ´îß6Dj°ëš®Óý³ïÜ3ÇRµÊ<\¯‰¦=~ó–þLqît¹M‹&ß–Õ5ë§!z1,I¶W_|Ͳ’ç}fjSš§0_ïZõÀÞÿÄib–àgÒO..ÂÂücØAà`Ô<ØŒ£Ò“'¯¿zu €ôˆ€„=¦Ptöi:F÷iNeêÃæöv/u¹þp¥Jê>ÇñÞYH÷yOÂw˺:ïˆÞ™£¨P™÷éCô}ßûáUf~_÷,ºc7ðÐÇv)W£ò–î6öغùûßWkÎx±’•àeaŧl>ÐåIsª…6É0Ì/Åý5 "[Xÿ¬aãÆúëœUÛŒ’_®à¦åߤ¹Ìüà»kCø\ÌÆ(®¸ô|«Nòžéór×¼ðik÷wÇÿØ–ð:<üçß%ø˜tõ©â¬MÒT¢§ù]høÒ¼Éï¾”òàÿR£ h!·„[þß:¡6ÊmöíIW dqI@ð£ Y$-Ñ^^NŽŽÛS½«^U ë†\ðËTAA£M9AÚÍC>!U€*Z™9 ”i=poZ Œ`ÅÜíqÇÙ6&ýŒ‡Ú™ µ³V°™÷-ƒÓO»œ‹­b|‰û”ÅÆ„_l/â@mSI%!|~«5NBB ëäêEW nSãõŵ4­…~ówðâîö”¶ö›ûĘýŽx9Fý ZËO:þ¡þtñ7#‚Š€€€àÿ üü§ö?Ìi½NÊš»}•™JXN@ðÐ&ñ÷÷°µ]u¿Ø!‹/ÎEøˆ¸@ŠÅ24ÈÎÓsηTq3ž¾*Xiß}ÉŠ¥¥¯ÐqºyAo¨ ·­TŽy|Í÷ñƒÁÛ½§jÑÅ©À©¬Pì7Áf°X|üÛøÊö‹ŒÅÑO_„Tué@áܾŠ}ÇO©£qLvHP[¥¥Ôã—¹+þ½³Ž‹ªùø¹ÛÀÒÒ‚" (©" X¨Ø˜˜…X(*"v·¶b¢¢ ‚(¢‚ÄÒݱÀæ}ÿÜ]ãyÞ8ßȽsfΜ©sæÎ½ë¤áºhNÉí7m«aƒ*ŽÎ¾Ðv¥ÈÝݶ,Ðq$ ѱ>ÕŽøa2‰»oìš#)耻w}ÜÜŽçæ¦|ÿqdEEMEEõvD\5±'f­${ý3zÕk`Æ\Ú±a÷g¦]©’ë<W·èÓ3¯Í§âŠy'Å­éïê·ßáú›j~RiQ}­Ué ÕcúŽ!]%¹¹áçÖ­ /–rn&u|ú\¶ÇZ‡É›7:Ôå ÚUú£×€ù•™Bú×¢nƒ@ @ Ú zGÑÄßüN@ ñ/ŽÊ!@ Ä¿ú %%ñCºuëÍ× Ñ ø÷º²-2 2щòŸºÜÜN°X5I\\ÊÏo624@ @ü™ ËÍídrò»'ON~ÿÞ†ŠŠ®³óÖ€€mÈ @ Ñœ_y§ËÍíDxøÕg¯Î2z}(Zš™7)åCÍÓ¿Ñq©xí1øîƦÏòpö—{ç|+âðË ‚\¸XVÇ”å$~Ú=øî‹U ¿€„lú¶àW¾MÒ¼ÐÖÀËólO-â¢vûÿ£ gÃ':ÄᓾàGA¼!Ô±ð"Èê‰ÛÇï°uää<òœ2ÄÒÚÒÂÆÅçU €_õñļa––Ö6esò Ÿ¸p ¯€VÓÔ#ânãæºqÖfC\ü¢+ø¿¨ˆP†ýÍod½&.dòÚÒD¨.M™f_rhÈ¥§ÃIØ™÷7;Z˜Z ±›º5(“ý³¶m£¼¬ Çø~c·‘¸úåBëùaÕ"sVµ=KqsyLdi=hˆã?·ÓÿËß§l‚š·›fLünnæÕé&ß,äwêÙ…_ñéòúIV†={tïm9iý¥üVõeÏP«µ·üÆ \òš)p¯­~ò/P6Ïzq„pqüÒ°îR9¿_B‹ÊòrýÇõçŸËCË¢Ó]ÉÉÑ_Ò^€€CþPmTööí=7·ßÓu]žåE2>ÿc" Ö}t^$#|†¹ÕlIÚÓžæE2Bœ4þЋft›}‰y‘IýLÎRâ¨wüeˆY[nï>Þ+tô¼Itz?Ý>ªM¯îàUÅÁkb³mÌfÍ”¥‰7D¥:<¯ì{Äc&„Ô’©ÿâ‡jøU•éé>úÞäÿ,ÈÅ Ÿ &lÐ[E£¡ ±Y‚RȵæÐÛ ÄŽúÅ"¼ÝYÒGße“£:±uM„ëÒD ã]­ý™Øäääää„ …:„ü ^ßFž{ù:äÆJò©õ™?·Ò¶•~'±ªäv)$(Eªßå:éJXD¨ÿ´lŸáåÿÍt#¢ êRB3ôkR€“æ¿ùÈÇšN>ó±SÎΛ(6çbäçįQçgQüçÍ;Çh-®'P$h4ºJ“ø_ý!Q^EbtJåŸX°+‹¡¯p#:qеhÑÑ”” °@¢áª Àz(*ÍÈÎN@6EtpðŠç±á²6kÉ7DéxuYèúè4ó³gɉµ2hˆjª=øy ù8ðóó(j:2x]åÛíaûœžîóÈwGf)—_p5ÌË=£Œ‡W¿ñ^˜\ØŽrÁLêjb0rSS¼s«¸¬äÓ¯8ïsz~þLa5Xoßî_}iyø‘Á‡vgWðxµ_GìŸìíøÂÿVñÇ Oϳp¨+½ïò*º…n?ïCDBahšĦö|(ð‚ú× ÈUc@Ï£ãF\€“4<6Ó &¡c¤ÊÊ)«Í{Áä`@Ljª6“µï}®6ãöÅ/}·ì$ûÝûáŠHÓ„ðÝŠ¼°`ÖèÅÃTHéë/sn±=Ö>š¼Ueà0.yéo\>P ÃKƒœŒœ‚J9í¨ 4$ÆÙ¹ï“Ê“8š÷3½ââçjœ—§:z„žFî3T'ûqlÙÏ<{e%vÆõã­,Û9í -æý 1;ãÆê±ƒš˜:l|˜×,¹ªPýÒÍfâ’3¦8 ä´ã¼ÏòYN£ [|-Ó†&=ÔïÉÙ)dœ]YVK–'bÜ܇›'Ú±2Ø~þ‰¸êeâÑ‚œìW_ÕlôÄX)=ƒVŒVéÜcÆž8_1Õgãh]:ˆ’ú›8•;[ÿ‹WôlƒÝú§õNV1îßÏ´WßfÕø_Íigggggk¦§7pslH‘&Úèxm’¿ûx›!ƒ,ú›ŽXu;‹ÃN>9ÁÌåz—oõÄS)­<ý¬Íºíuékâ…žqŠ#|fÙ ±±b?ç@D1ª_º ¼bá´Iãí†8¬Çàæïœf;x°µåÈe—ÞÞ_8Èå^j?xŽ˜ýRŤ¡²U’J¤Ò©èC܈Nt±XÌÊÊ0 ´¼a\ Neeés¨¢•pôäý;ßò"S?\l$Þ´<Ê-:{ís³ë˜ÌÀ=¡ó"y‘Œo7Y*“ZÏ£éMÛ”ÉÈ ÞcBQ>YÍ~áÙˆàÔ¼HFΣ ÃŽ=%°vH!þ¸i©×3ûïèÛC®¾Wbµ^=O“³žÐjÄ@–èmÁûUü<2Ÿdò$c³ªÄG÷ÒîŸe[lÔUjG‡Ì$f²TGMW×y½1*îA¦æ\»µWÍ{Æ|ÍÁg’­v^zÆT÷Krl6¿&*þA¢ªËe»µgz§ñmäJT󀔑ª¤ÙCíþ|?‰0*µé Íàp8ÕP0r?ƒò’q®o3¼·4pfü…£ §¾´òÌ*IuY2EV™\–ÏäQôæúzÏ ßð‰#"M³èGènY~r ž®7ÈtÀˆUwó)-vëÛ§CcrAeêÀ²—¾þ×¥fÒ&m{(è­4·uiLŒqk¸šVÓvÞ~õúúþÉ¥û>ðTTó<üZÅã俺]X–_ý3A—°ªòyøL  òÈ,ã´™˜É¼®œìx&$*̯wä±;Íâ'!Uk¸å…J NÞ½4›yímé©À;§Gg\¾Óìá‰)Œ$FcE{޳{€5fº¡;íæñ¸!'ž…†ÜÛÚ=-*£îßètÂ-È+|-9Ô@êÏy†[x¸v§uòÙ¥(>z™©7- 4-kCøú±€ À¯ˆ>°poÛùöÊ 6’´Úî=J£ÿzï‰]$ –_~üô¶÷(­ñ;Wô¥‰i^\+}ƒU{)ˆºìfÈËÈðæqgî¤cz³vϨ8âåÅÓ·|ÖÞÙݨ¢c.AÁlÇ.½zÌöÛmO Û¶‹1éâóÐÐg§†½÷ØùªnYž´ëÉwï2ûpòN:»E[EM€LS”âU×òJ¢+¥†«)P §êtÆÔÄJ[·(#±„õ°DÞ¡>8Dü\WÁÜ—ï=~0-@{d-Ngx×€_Æmi¨ÅÞ]võñy“‹ƒµêí´¦ù]œÇ©úú‚å|>ôÍcOûkv½­þC:4’Ðóq½æŽîò½ÉˆâÊêÊâDhW> ‰Åמ>8«$‘¦a?Ïžðîe®ì˜Ý›z½Xlca¿2Da€†8ùRóÒëRCS»8Úv!cT­‘“z‰ýȤ˜DÏ1æJD‚„v_%Nq³ÀSHÕ,6€˜Ž¥¾F”V“U00éJ¢´*[Åâ·- nêyÿí»³»ïŠf«˜ dv™²x»Áàå³E¨ø¯ô¿Ò˜—D#©ºÏ§¶E Ù:G¿ó»Ø8@hÙ0 ª^{,8U:|¾}r[þñæUaVû¶S$ÂEZ鸔åÖ#3øÏÏòÚ~$¼°¦šÅZ¹^’·ï̶ÛE·µýBL„`Ãþaê‹wio÷O·:Ô~ΩԚœ¤b.`âzƒ{Ib@QÔ•a3k3^%)µ×¦QÁîÀ=?ÇaÓÍóo†VÄ݈Ñt²–on¢„’¢¢­HˆNtÑé²jjú ð±ù¸â|iÝöf”òÐÏ÷ê1ßÇ™D9zã˜I¸¶aÕ¾ îd7\Çk2Þ2èœg¬X9Þ\$U$‰­dBÖi£ulûæƒ7®ÜK:·EÖv°Õ¶[~þá©c‹µ@k ¦þ¤¼º¯>Ññ=¦8ЛÏád ý) Ôú.3ÖÿòñÎóÚ66³I]ôjr¾æ%wéVÿ  ¯|þî°gF A\g¤®a—ï#gåV±hÄꤪºv9å"3itI*.–«®Ù­¹fç~~ˆ‹£€@l~·¦£ôxm³š,;` ë˼è©&h“ðW ˜¼V³­~ÖsC S€Ð´½@Τ£ Ã:øÙM^IÄžén¡¦.­3—!YVC²:·œ À.+àȨЅá4xø¬îzzzzzf[>p…îÊȪ)hÙŽ5‘#åûµc$f7¥¯ý%Z‘²è{“Hc?г|xE‘þ×Þ•6ŒWŒ$Næ³}–]Žˆyrmƒ³N^Kægμ‰(ÐØW0"‰€ýPU"…DhŒµ¦*°†ÄFhWIÕ /ž'Vñˆ2Æ£ú¿–ŠØò0ìüŠ!ò9·VŽ›ý_ì)àUñ¡,‹þ \Æ£qïNè×ÇÔùZÆû £fßÈë¤ßO )õ"|ŽÌl:¼ÇÊŒŒÇ»*“q’挣Ú÷¶]Kkõ£µ‰g—í)Ÿwx…€Š´Ö7øAËÆ.¿•Aì:pâì1š€Wɨ+ù’RÙªõy¢š“Oéîv98$$$äÅÓ‡÷NÎÔ"HD¬ÙÆÎo\f¹%ä>NƒJîHeòÏd<Üå~•Ÿ`b¦Îsñ3žwrx"‹ð]˵<}œ¾[öC‘VÇMfÄg™ÉëÜg;Z)f¼Í¬åñ_êå1õòe§´Ý»ÃK[q™ØÂ‚FÄ8µœª5H//00¾Ç™q‡§OÜ-üf UÓJ¯ðaHø¥/·¹n|^Bé9yXɱ}{;› ìœÔÆ®¯N¢cl¨ü‚L@À¶ñãW¿}{¯È)“+ÎÆ¸˜8OZOÏÔÜÜÑ××¥a£#öq"ôêaé}öŸ ïTk@bPlñOoL¤Ôˆ•ii™UâCUimLóœìÈÏ•ËÕôçÌž–‰ Ó¡Ô¶œ Ò…ç,qÒp]4§äö›жհAG‡´-…ø àÕ$Ü)ªJ/:>2©á­ÿáácšVuLÌÄ`’Mèå]i:uD¢ÒPEÞí²>¦4B}'"ªMÔ‘ôxu0TŒ®*§¦‹•×&݉ϵ6uì!‹»«Ýþ%©·qw)Á.#Á×¶þ³4²ã-Ȥ”iÊiá¯O.¥.=dì˜0ó òvÆSû‘±AwJܲ¯C\Ì…i)|Œ¤å2ÀH #Étí%“U=Zކšþ× €rVA’ pù@6å Pì xT³éÕ4'CêL ‡­C¾*ZóñØñyUFî3ó ½4j×x÷5öV5 k¹úèhQïˆP»·•Fø.EÑ}ߨõëí­«qªî$/_ñßס¹Z™Z“¥Rá/–;\™àÐýÇù4%¾ÝóݲùC.ãó¥~ ô)4Õ«,vt˜OÕ·ý ÂÏmb ÙD¹s}ÌÊ™¶'ÉòzÚò”¶ÆþÖê¢)/¤*7ÿÇK­¦³Ã#zù›Ó<‚Œ±ë¡=Ʋb*óu–Îz™L"( YÈà?˜>êRÂÒõ†kþe¯Z“µgŸO|ÅÈ‹LMºqmŸ£¡T›o¯ýi%@ B0èZ´èhJJ X ÑpÕ`=•fdg'HÒ¯Üb/÷¿øùNr—ÏíŸ7@£èÕ¥£×f* œ±æöM—îTÔÜ@ âÿ/èb±˜••%` ðËïVÀ•àTV–¶¼Ê ðv³jñƒ d5û…g#‚Só"9‚;ö”ÖXämJŽ·‘ lGŸ~ÁÈ ¿0R#ÈO¸óŠ‘÷ÌoPtW½‰dä›ÞUL{‚ûÕw!Œ¼HFÚ€]öÚ4¬þÑVìŽíçý?g]uÓ%7è.;Ô/ˆ‘¾«óŸD7š¹Â‚ åÁ 6xî<¸bÜò y@è3s¥%ÒsAX^$ãÑh%4ýŸ¢ÞRÉ’2÷„>JÌ‹däE2¾^Üd©LÀ$zÏßñ49’‘÷"âu0#/òÃ!#qá*K`@í2f›_hR$#/’‘rç†G_:ú2G€xÄù­_Ó¿¸è\œ“œv~mN)¯,[ýþú­j_P–›š~ÆùíÅ[µ¼ú xyP¼Ÿsü§b`L:·§œÝš—Õxeyøáü¶Ó þ#Ê p6|¢C,>™Aá~ÄB/‚¬^¸xü[GN~ˆïâIŽÇwZq*ºŒÀg~¹²ÎÙÑqÜÄùÞÏó¤‡Wæ9sÓ&´š¦wY½\'N;nÊŠsq•ü_ÔD(ÃI=ç2xÐ «†³näðÚФœ¹!> ':NY°ûq6û‡jÿ6Êâå^_8û4ƒÓFbæÛ ŽëêíýãVk7-Úñ7+ø´T öƒ÷г)ßíÁ͹³ÜaáÃb~§ž]øU ·¼M°:hȰñ‹¼n~©â·Úè줣Îv=:;{ìÆ˜{mõ“Ñ~Ùê¨uŽß §ä—¿ñÛ~#óûöhQG~Á]×ásïtî.€èÔA@"‘ÈP,”¤0F"‘[\Ì~°ûvÈÚ½Ö°1¢ õžwåÂ,Æ9×Ñ ÜN–LZsh®ø³­ç“ úíîykæû¼së# È}†iŠÉöÐ aá C—7¤ ð58FuÁåc“ÍÅ£}Vì:ð’:Àuë¥ =ëOü©ÚŽ7¢~Ë(ªwxÉ]¶š"ÏŠòuÝSÞ4ôˆ F½äxq/bËqÀ™)ÁQÕb=(SDÏîEZ(ù­†_—vcÇâ nËO<ªQ·^ºa™M¼ÿâ3Ûmõ(ß÷œð_ù]”*\å:š“=-0•‹9±tæzwï°\š8u´ŽÕ´ÇÒ€+®ðï7q$M¼ŠžRS¸ŒWW¾öb˜w7NuX•@%Õ¾-«üñXŒ(‰BrO­‰ ¤þTàW×äfñÐOþbtÈÅ Ÿ &lÐ[E£¡ ±Y‚RȵæÐÛ ÄŽúÛxÅ¿#©Ã|oݹºEÿå¾Ó_ëØŒ«»Ÿhlº|ëöñI¥g|ÃKù¼Š/·<—lžW¿ *M3ïIè.'óƶóuÓOܸ{e}—§>×[øhíÔ¡iBo© Y×õRøË—¯^†??=«‡öÄ#Õˆ­kÒzÎxåÛÃG3F¼uãð¸ÜÓ‡¢*ñ6Õn‡Ù¦•~#±‹µ3ÒhÇ߬à/\j+#2[Ǭ+8Yw÷ÿRÛÉç@Núµuëî‹Mñ½óüeȽ“(wׯ ÌlÍþYœJ— PÅÉr®ë»êðÚßzë‚W™—Vý'K¸Žh3Ññhz/ŠN—USÓ/NƒF#à"ˆó¥UTt[º~•Q{|‚‡ï´›±fFýäOÖv°Õ»åçíêSi /:“X W“ù±ùOb¹ææf¦½Ìôû‘2ƒ?ªØ Ü'L]€qÿ=Ín@ê©£'¯gb±Šãí茤å_ à7Æ.0“ $íi³@{¼ß^5q^Üú%·RY¢–ß„heˆâuY±-”¼e rqž¡¯.¯¤’*’4fJ‡·oõK£Ø X1Y €"¢ÊÕˆL6€¤ÍÔ•âñŸb£o{_†|äåhV¿f<~K·Ý­,C‚úU®¦úݾ”½ñŽ¢#. *ÉjãeŒbÕ~*¿¸ŒA–ë*U œ„ô[O”&(¼u “#±«+8¸žú¸òôvœÍÅYµñ§Rß'q¸µ|Z_± ¤².çfòŸ›°HªøfJøë:`²VZv“¥IŸ’üŠvê•”ˆh6ŸGP§¥ŸúÕ¬÷k2ƪݘ¥¸±goyÔÜ¿êLDBaè@lh‚/[ 7ªÆ€žGǸ'uup_Ö» 0’†ë~E]aü;ÞÀ5úQÂbŒú© Äša¦Eo&,_Ws ¸…ÑBi,è y ß­ê™õ’=t­µ"‰Æn¾;kZœ[oŸùs2[*ÓèÅfÞôy®»ì”1ÃËŸ»9ßv ðë)¨Ie Áœm{½qs¾ípt¸…çÕþ¯«ª`‘èbD~aTjÿQV2­x²ëÉè šR7¥R~ï6›ó`û\Ÿ/¹µòCVí[3T‰ØšÅ¸Ì·›fŸÃ´i%…yEb–SŒJ£>¦g•u™¹Û¸®dhÅt<á r BxœŽ.çñÉZã7nÑKâÏ{¾"Z“÷.YÙb6 €~Ë'Â`®mÁ“N=½Ô|¾r½jÜ¥CµÈ ¡3l™Gêìµ—?ö½eJ"ö¯8Οï·n°"ÈŠ½ ûTëõ¤K*ó“ίØþ¢„8¯*+wðY#R¤‰6ú†lÜe·Æ˜´ôI›TPTVÊ7X¸w­±5g*íÎÞ]þŸËY5uR–ì]×7æèͤT–ÇíƒÿôM9¿ÃïyÈê×ož;€½iŽ@¶xÑËcÛFäsxbÓÝ­cv=´9ék'O¨ûâ3ÿdçÞ}´¾×‘ŒQèT"E‚‚â.D£Éðó›Ó½»™d®<,¸Pà p ôôLÕWŒ‚ð;>p€,]Ÿ† y|¥½¥³­¥³­¥óðU±›ù¥ï}Ð:sA±Äû'O¼gwµãbHƒÜ'ÏêÏWàü†¸© Åi¤Œ$ gNÒlù®¯8.¡€Ô×ÖH×fF¨MˆÎ¯?âE"ÛªÄ.Î{öNµ¥Å]Ùsèè«{„\v‹ƒB¢ª“~ßcòŠ 7ŸgazVŽ‹Ý/Þšg€^'ë@ÎtVþ£‹u«´´¥wYkÞïKx“M7±o5â ÓôLø)±,>àUïËI2-×–LÖLÛÝM=)÷k~»ÎE°“ò“È]üŒçÖïš’ŸXHî9SEICuä"9⇴—¹ŠŽæøöÐù–ÇËÀk?d„¥ÊŽ=`4g:!¬7£W„–×ð“V˜-¯¨-‹Zû×áFµÙЦÙ|  ÆBîgP^Ò¡#. Hh[Xw—"^óíÆÅŒ>½¨¹Lºš4 €,£@*/ªá“µ·np6’iðĸ"Ò4³œÐÝŠ¢ô „ïœ;i´ÃŒmO‹)ÂOëÐ4 ”©_M*Þ¹Cž2ÛXÀ¤,·ŸÛf%Åʧ²L(g¼>1‰Fe}ôç8÷ Ëv|/1NI[jÿ¸ •^]âsªhü±Àë7ÕVpÚL\ÜUIépÿ†g÷gsZ·˜8¯²X~Æîcg}'×Ü¿Oí}üÌ^ۜ۳š•"h:á ò²]úbæåpn¥nVlëßètÂ-È+‰£[ê‹+5À7ªÿJ'ÝN¾¤rKRðn&jM$¨êzBÒ—b.^wfÃÑš™>kšÂ'ºéêM6]úºm­"®?ÇïÊ•+WOnªØÕ~íÜ^TÑ"ͽõ ^E¡”óžãçÎl3ù|¥YOŒ¹˜Ÿo>§Ì9póþõÝ& OòG.™¤¯;eû?ƒIo|gŽ:èx㪷u¼ß»j\(ÛŠ7~­¼¯Ü¸yvéÑ]žAîÃw¥|¨Mz«4føÈµ u$bº¤„¤$ º7耀€m¶¶³US»‘œ(Ð °˜„¯Œ‘æpssG__ᚸß÷K“˜þ(<@ÃuÑœ‘†}Lú]þÏB*ŸUQ  d<ÆÁÂÜ@šÈ+º› í0@,ãñ›„¨à<åaƒ¤!ÿÕSFuú“WùÝ.Y4Õañ']€Ü§é"߆I¸¶n¶OëýÏ–Ùz-ŽnÕ|¸ìÅYûÓwwl^¿Äç–Ÿ«àŸ/û½®æUV@/G7× s êÒ 6WRVV]P™––Y%®®Jû> æ¾I¨Ð]ºÁsÝüõK )­V¹{·-ËûVG]=·ËófÐÄ)ÔÓ:ÈÆ~mU¤oiŠþÀnM9·°¬P_×Þ”v¾ŒÙV¬D Ã}SQÍf¥Å`ú}È ¹«¬¢™"Kç×´ï¼µæ(;œñ8çå…œŒ2«i±ã—¼¯*û’usÕÇ «brø•Y\°^ŧº•œ,0iÙ{ôzöWîZR”VÆÍ©’¶•GÝòwº  à¾|ooøiÀ´í µ 8¼NPW>3ñچ͑ý6­,‹5«ê÷Ͷ֤ÚNÓü.ÎãT'G²ÆùÞ¿´ªË³‡b™H‡Féâ×׿ê;Ûª|o2‚˜¢š¢¡5=[^i‘XÌÈý샠}FaÛÅ1ù?Vû‡4/‹•™¡:ÒR…ŒQÔmÆè‰ýȤ˜¸Þ°þ D‚¸†<·´†ßºÅ@Ó4ÕLj’ÊÒ²z}T(@”TçŠx?µYª [ÑØ˜}må¢~w‹Í\'ëÑþ›þWñé ÑÜ@’õÍß÷½¹»“vç÷µq0‚@-ë»guŒÏº«åƒ§ VmãÌ:¯8tßö(ÓM«¬äíùQßÀÄ´Ìô$0 ÈkJ±…zZ“†ôþ+wNàGÜ8Øï›âZ&»q眕þúc懓Kg8O™¹úJFm>£”'˜mmN4Caø` *dm=»m¤Õ“ÂGQÅU_|êâ0 Åˆâ òrbD@ :pÐwïúL˜°z̈e¶Æ³íÌçO˜°ÖÆfæñã‹EK³Ó.­½šÙíÄŸ>÷rHšÊäÍ^³p0%%¾”ÃüròPLOÛÕoÿÁ:Tàå…>O( Ω+ùx/ äEHjÔÄž˜µòî{®ÙªVaÇ\Ú1k÷×ÚÖö‚²mÚù…Gè±öðänÍßÖâd_q™»îlt¶šëÊéôÊß]Ýï8éRbðòÂöûRAì6}‹‹«i—®…’Ꙏ^ûR«â¼éØ6[eî÷cåxå+¿U§âŠ%úN[1¶¯8¸,ž¨*—f–*šçsþÀ¹CãÅ?=óšw*®õ´á[rRϤ$uÓ1ŒÖ|2'©©ÙO•ë•”öâ» '…¨&¯U[ÂH)O%É«Ë:z‹?¢úu²¿_aFí:XE_¹¥Spìáâg4ÛÏÈeoï±#¨á"štÅë X5dz#NÂë²Ï©â} Èh“ðw ˜¼V³à™õÀÈ ôm/óé(Èð‡~¸˜_öîØ²MQFÝL¤@’î"Q“_Éà”s¥%„‚wá4x”û«Aƒ¬Ûû…'tWJFYNÝÒ® ‘(ÛÇΔš‘šó²)}Ý/é àµÇ=ÍÖc$‰ý@OiÙVsæW'¿ŽHeòRC )ÉUâ‚j—ÿLˆ-\º8{ ØÂç]e"‰„‰žQ,XCb kß~ IR¨‚bF+.]÷™k.“ÿxÛœµ÷óþ‹=œ™Éî×WŽ›ò<áã™ù#†q»—ï=Ëýaa'ýIÁ@Ÿô>§iÇ™ó>‘¯ÛS„“º8îZ©þôà½Vßð‚ºÔ#•Î;] $0ø¡Èûø}²íý ~Ñs9[åTGNÖ… Í&?œOÖu9èp=àšÿ…³Þ»’„²Åù鹕99eäc–=~~?A{œ‰t ÅbòòòhßÑñƶð¥cÇ·%Qê¤jÑøW͇£CT6Î ™ŽÏ{t\@âãÁåæ›ý͸b¯z¥á‚€ÑMã²69pïäÀ½-äÓüíUý›b­æžž¯wZÔ$]rsóŠ››…ç„ò×;æï–` (¹iXè&A'KN’ùñÜ–'e|Ù~3]{+þe6Kt•w»·õ­ç\–?O ÍVpð”œÊ‰£KY/–õ÷MýÚ£Go¥V– MÏûèjÔ°žt"û÷Uªˆ«¡ éi6йùï ùJ8`Ày<ŽÉ‰Uß,*°QWÁªßlO®r1*´”ÊÒª—W U–®)óÊïêѳ§\íöÌê~݆ÒQ‹ÿÞ~•9(+c%èîšÔÝÆ1Pz Ä*2`€t9 S Ä ´;j5Ùéö¦Ú¿o¢6  )õ3Ŷ'ÎîfÀŒ ÊÐÚMøŒpEKß°WyRîJw‘2áïK™Õ­'–õ‰×m¨ú §féÓý^‡–µÈ|—£d©ßøþ¿¶(¿š®¢(”¤’‚PÎõ‰åÙYw÷ßÈQ?8YƒõùÙG¬× ‚€Ú?õR—p-dµ¬´òGä «Xù4¥¶O[‰Å€ÑþVû5È],*(‘|lî!ù­~NsL{×å\Pýן6Ô¥D–LP&QÈ‹¯¾X P÷ÅgΑ‡}G+tV¯[Ì`†“ôÏCÚ;Ýl´huaÇ=ïHL>ÒG¼ð&M­o‡QÎÁË|ž>0F…(¼½9¼éúêcoëa?ùc³Eî»$©Ñ¾ó&kà™7¯æÖuÇ#€[ÇÁ)µ Ï?H³Àûznù–‚e—Ö-Ÿ]MµŠoDæŽqTc¾õ]hìwpÄhëòå'Š,·®•jÙ‘‰*v®ö²$´!:~Ð… FÓ°·¤¿ €_žâ{hãã2ô½ÒÎ\Y“]8'·q 0ðì;˜Ø¬h 7:ZØu‹²Œè¡C³â?eêR0ø• ‹›•}yFö÷ÿKÙ8P–ðM¸ò†"¦HWÖÀ*Ëù˜šd<ñÆVÒÔ­:ÃRRž¬úÀLzö¨>Dì“P—í§m“rwE#vqÔë!D)9]Éâ:zÍðw¡€rVA’ pù@6å Pì xT³éÕ4'CêL ‡Ò+YûåÒåÏ…ÌϳìÐL<ý†NÿÇ~ÛŽékAÊtÁ.[E~/E·­4ÂwÉòó· ÷òš9‰‰S4G­óì#öû:4wC™9Ù >Z²!^ùÚÃõ¶cÀÑá"4¼‚—7$´feÜÖUS®ñˆR½¶m0 Ñ  µÛуK'‘‡¬]òiÛò©WI²Ú²ä¶ÆfüD«yIþJóÓz TP†®Põ§ÏÀ~ òÇ êOœ ¿eåÌPYƒº„¼A£w_Ü}ñ©³‡×ýóuû¶é“¸@ì2bÝöþt,ZpÁ’¶XåþqÛª©<ŒÞw–§½"3ÔZ&¸lŒ]p?òõŽe÷'.ƒNl :T$L€hÇÿÞÏ,¾Žˆø+BËü‚;>µV;µTPÔõKXZ[@ÿÎ>sÆþ÷ Šÿ—.„l‹Ì‚ŒðC8Ù·Ý7e»\Þ‹†Œè`Y$èIQŸóÛ¿¤¦³ `EÀ@Ôèñ—ÀÍ{²×ûA6·Ù%ŠÞ¬­ËJþãLÚ¿ôùjg¯¼AGº£ˆ ÑY@AQ BÑi>ÕŽø_Ȥä†ùC†èT ¿ @ Ä¿z§ ÑÄßüN@ ñ/žt!@ Ä¿z§ HIIAF@ünݺÁ_óõB4(þ½.„l‹Ì‚Œ€@tâ,ô¤ @ @ þEPÐ…@ @ (èB þ}j#^{ ¾»±éßó°,œýåãÞ9ߊ¸üò‚ —G'.–ÕñEˆãUeQ»Ã÷ØßÛl´{QläWöü, /+É·¹n¶¯>•ëíïõùo¢¼7²Ú‘ 7å˾¦žß›QP÷»Š}×A¸t¼<ÿÁöÔ"î¨;¿¬8b{Øî‘AÃìuÿü%ŸÿËYýA­Ê p6|¢C,>™Aá~ÄB/‚¬^¸xü;*89<§ ±´¶´°qñyUÂàW}<1o˜¥¥…õ„MAÙœÆ.ZøÄ}„[xõ÷6¦¡E…ïÖ1n®gmaa6ÄÅ/º‚ÿ‹:€eØßüFöÐkÂáB&¯ M„ËE™W§˜|³/²â?׳[¯/ëÂÄ1¾ßØm$®~¹Ðz~˜èb¹¹<&²´4ÄñŸÛé,àe_rh0AO‡“ ÎOL>Büõ*ÿr¡5o7Í<˜ÈÝ~ŧËë'Yöìѽ·å¤õ—>Vð[mtÖ—=C­ÖÞò3pÉk¦À½¶úÉ¿ +éÔ´þ= íÖùͲž–6ÏzqÄÏi"<~žÖ,À/ Ûé~!•óÿÔ¾õ¥·h;^®ÿ¸>ãüsyÈCAÑɳ¶Ü>Þ+|¼Wèèy“èô~º}T±f1UqðšØl³Y3eiÂã†S±.*š¦;÷öØÏG¸N"¼]õ*óOzD­…OÇ{}×ð…•¡ PúÍ_«Hù©L4õ—=ï>~ûuSƒÜ/Ž×ý^hØšüªÊôtÿ÷ãNVeèšwñ*=Ýî9lbçdRzûŸÄü_]‚ÿ˜VÀ‚üQP,ú¹`½åP4 ›%(…\{`N½Í@ì¨-^¶c÷·1çC#^û<ÝìWËJ:µáŽŽ÷ÓW×gûz>-âðÊ>^rwrÐ8ˆJÓ¸Ëa\p?T»àÆËWO½4îzœOfÿ¼MnZKe(ÝW,IÍX¨Ð]LPV¼8ûžGBrd©ûÖç|=ó8¢–Ç%ªÏ0uPýzâ"Ÿ–S±·Z`úíôm­EŽ'šWÓ•žp„‘›Ê ð–r]+“~òýã0&Hjã&OW D¿9|¡^|Édæ£æõ$a‚†ýöŽ¥=g޲4(šSûOV¬$ñ¸5ñDzò Ò]¹K‰Ê«y½$ªjÍ›’˜†]%—Ô¨Õ5Iâoõ^$–Þ QĦö'Hñ¹ÅPyã j èyt܈ p’¦“Ç& `d#UV@Ym^loÐ:F¤ÛLÖÞø¹zŒeÁí‹_únÙÅÜú¸y¯„ÒØH5ä)|·Â0-˜5zç0®¿tŒ©@úióg3Z*Ó.1.yéo¼=P ÃKƒœ‡^™r¥žCËâà¥Ï‡^™à ‡±R.z­{·í²Ú…(+Y–ÝÙ²ÂçU™¸jÕbžQ›‰‡Œö¯ IDATqcõØ­³k”FxžÚ1Zµ¾KcÒCýžXbbd¼¶²¬–,)NÄØ¹ï“Ê“8š¯ª’³Zì½Ë¥7D¸9øazbEyÙùâ¶s…½KM+ÑX|æÐT­ÆˆL¸‚BW¸¹=—xUÊÃ)zS÷ú.ìKÿ]ŸQ„U9Ù¯¾ªÙ,l‚N 3öÄùŠ©ç7ŽÖ¥€¤¾Ã¦‰cæupø>û=Û<}õe/{e"UŒû÷«ìÕ—f&£Æÿrhæš<^eZ&îtv‡H‘æC#çñž5ÞO²8 ÞkÚÎ}‹^»ÚÇÖ•åáu¥xÿ5V[ËCqÄÁÕ;‚29@Ñ»yß kÚk·±‡Éz䜂âbžñšS^cT²e%œXsèc6sÝ<…³¾­¿1õúôïšf¥ <³3‚”LAËŽgPýlÏ*ïg9l®¸Ñ¢ƒ[ºú8åÒ3Þ³&\ö·::ýòÔ3¯Oi^©ZϽ.}M¬[±Aÿâ®>1ûZX€±`ì‘zñû i‚c7?¸YY{èaÞâóFÎz|ÜšÎ˺0eAáλnEî-̵­ïˆҌíßù½í(ª$•H¥SÑãt!Ü´Ôk‡™ý÷ê!Wï<`µ’Þ§+MÛ):âÀ«+@OC®iû˜¨0@•UÁë*%ljј¯—E=û2ľ"îA¦æ\ÿ®Ò8óÍê×¢mÆc8³lui°±ìá¼±Ùj6š"Šáå§Ÿ´Oÿžy—Å6‹œéÍ`F fëdFnÛû!(È(“² ÊùêdÛ$$e›\=[’õ>áÑûêÊ’¤‰ªs.wS`æÝX—”<‰_](>ñš¹–Ɖ©W E5mÆ.Õy[ÄŸ¼^ù ¥Ë¼k: Ü’§‹côêâü¢KšÄé)Óò¡^RëhÉ6–$Þc„8^ùäƒP¶"Ú¥…aóª×êw#.àF•Út…f°8€j( Å_@çN‡Ž¸€@×·þÝ’ñŽ2LÜúÒÊë$»}oЬ2¹,ŸÉ£èÍõõ†êð· N\y¦P"¶z·,?¹Œ&ótݸE‘yT£™Û½Ý´Z‡Æü•©ï e/}ý)®Wͤ1¶=4@Rš›$¨I˜@Yt¬>1u‰ç<Ã-<w¿òÚ*«Û¡*oÁ´€°ÉŠ™ç§OHm31“ׯ+';>9N{»iì®;évnº ã#‰Ñj¢=û'«,¸l(Ü®¦Õ´å;fè•=Xë¼t_ŸÇk[^ØeÁ“#Æ%çǸF»¨•î;Þýc¢{wJ«¼ÂN»yd-ú5 ;ú°àWÇŸ]²4ÔbïE;ìSSU°V݃¶Ó4¿‹ó8U__°Ö„Àßx9¯ÙÕï©ýOèÐäÅ…žë5wg—ïmAWV¨mEÏæeê>º-jÈŽ3úÔ¯Ò¶ÍK¯K MíâhÛ…ŒaZ#'õº“÷“b=ǘ+ <í¾Jœ$& ¹×+nêyÿíÊÈ]ÝwY?ñ¸ö´ñw÷Ü~ž½ß?/³Ø& ¦c©/¹j² &]©@”V¥s“X?u(š¬b65ÏeÊWûáv£—Ïî%öoô¿Ò˜—D›íRuÿNüO®D8¡å† †a€CÕk/eçß±ïBn«Ã?Þ¼*ÌÊ÷Ú0Eˆh[¤.54Ukš¯ 0šáı2‹#2Ù$E³t @ÕÂ’z325!÷]ÚÛOÓí|€WS£”TÌ펉ë î%‰OQW†%ÔýZ£.õ…PVƒ¥ZJ²R‚°T…B‹Ž§[¸'IiìFmivîÙU…ÍÝ×ÝÆ@ŠØlö¨TÛ¡u ˜atAñ&õ2^5/Ë2r™½Ö´1-ÅEŒtAsIŠ\æ%”%ˆ€@AWÓ$«>zá®McŒ•ÈD"·8áÝåõ^gr­žò³ÞùGÞÖ¤8ñfQ„ý¼«9| (ºñÒî耕!•í¥iOrÕ‹9œÎF‹øIxu_}¢ã{-qhq4¬¡?ešr)uNÌç²3ì…waR½d°GE¥l•ú¸WS:šR¤2¨?3ŽóÙ8&À'¨¸X.™"Aà”Õ²ÅiÄ@ þî9n\8ÛV·¸¬èýe Â*IPû¬ÔL‰Èýú(úÈ£nãº6Æ=xmN “  APáÕlÅ Æë=‡FqšQó‚z,= %ÛBoL²» ñ^Q)GQõû²Ì®ŒØ™,¶ÈPVT¶€×Ïmxä÷ Û3€óÀª±†0˜õÀÈ ôm/cBU_ÈðÝ™ú>¯$ÂÛuãçá¾—ÜúK€+«!Y[Î….D`—pdT„ϱ’…Òàᳺ/ŒäÈ;_6O𮌬š‚–âX9"úµóLÌ>鸤>}ècÊÏëвo–EßË0˜5@ûžt¢`}-Æ£qïr'ôóÃ9u,ÞµQ³ñ ó“UÃ;Q:¡qœaDR‹&RU RH„–ÁØ÷êV&„¾Ã í!I”1ÕŸ°ãka>/*”4bÊ9"F'kÀ°_}"‹IØò0lòëgÏžÝZ9îæÒ»—¦výÃ.#^ʲX©Àe\úÃMð¿ë˜)õ"ÜŒÌd ìQÿ,•wŸ¨LŽ%iÎ8:ãë¶m×&û»j‹sjÏ.ÛS>ïÒ>#:Õ€·CDh.m˜Ïy,.&À§tw»|ÛUƒx]IA„ùHDLD÷ûáR%”•ÈMÌ–AXJŽÖ¼ãÍõÔjÜ,àVdä”±H%·¶HñX\L¢íE !Þó›—•QÒü›Y8`õá1€sYœú»í2ABQQQ.ì¼ütÛ’´'Ùgé6ɨ›­¾Ó‚Ã_¸4âÿKAÓšèf¯EE-‹øéíÔâ ÷÷Ó5¦­Tðàpˆ&©8êÕ¼C±1y"ž¼Pzë[ɤÊ.ªÁÏ- ‰¸K6Ÿ.G¼*6+­Ç™å_óµL¥”JV¥g3¯){±"<(Žû'Þã%(üd¶¼ÒÒ(¿TöM ‚¼ ,;ûxèÙ§]‡+´¤Šj©F’•!ù¥ÀË î¯ùPŽ‹pŒZTSŒ„aÀåòp¢¢…dqPn pfŇ,ÕþâͬËkYP%S軂äº(éwΔ±¸¬ÔËÂ2Ļȓ„³%Ó(xAe¸Ù_³Eý «×êOXÜ”€±êªøPŒc ´ˆ@ ƒ.ç v9”¤uàQÁJ9·xSòÄsg—ÔG $K+Bè½ø*>//ôzªî¨žÂ/¨ §Qzñ[rrrrò›Æ’Bwe4mÌñWO™8^“ÃíÑGÛ®)½Ø/éвi/3T‡4¾mÄ«)È*¨á‰ÈG¨¬†Ä$ƒuÁ_?ÇÇÇGLÕì·ûÑ…ßu÷…K—צ—sëY689!÷kÛLÜV•Ù ›Œ:œ_{ÿÖ×P‰\zhW`R-Î+~u%Tlø.è”K]¼÷8—; §e[=œ• RJÿü÷5jCJŒ,ÔHÔ?Þÿ»ˆ/v•»æ¾óA*“|&ãá.÷«t—Eý$Ó0µpö˜‹Ÿñ¼“#jšã—†ïZ¨åéãÜðn^Û"4]ôÀÇélÀ«¿Ü}PÖ˲+¸…Qw£ËøxÕç{/8¦VÝôéåÆWã83îðô‰;¢«u¥jý8+šÐ@–*ùÔ¢ãåHÔ+|’Å~éËm®Ÿ—?°¨T FÄ8µ\¤Úò25­š—µ#ÍPK@œ&&ƒç~+âଌðçé¬V£ï¥ Ì jcWŒWGGÐ:/?ݶd9uVF\j58å‰0yj×q»Žïc¨ÂM<;õž—å|LLúZŸÕ”ˆxuüÝÍË.e?h|xø²<·§7ºzšm «ÕZñt'qÉì 혪3<ÿ†¥3sÿ–…ær2”Fž]謺mé@y½7«Ëß®›°ï“þª°û=÷õ[x«“]ñÛðjîU¥™Ô8·ö?<| ±Yÿ31˜dzyWšÎA¡C¢[î6'ùtfÂ{&Ÿ(©¯f½ÛÜL‡È)"©6|åó{L‚ʸ~“»Åôbf>áAÞÎxj?2óûÚc’C„²•Ž›‘txXMLk¸Áì…òŠ ,U\COÑãõþÛ@ë±ÈTÏ c.LKác$-—F ñ‘B9·¬& «“ÓÂ_Ÿ\J]êg<:!æÜÔd •Gô›dBÆ¢›„Ô&´(HMx»„"5ØÛ„³?þðÈ(eŒ´'îí®JÅ`˜`¶TN7+…·GǧJê*ët=³ar ZÓSüÍ…ÊA@XI*ÀåÙ”ƒ@±7àQÍ:…-hN†Ô™@¥#ŽŠšÇŽÈ«ú0Êp€˜™oè¥Q ¼Æ»¯±·ªYËÕGG+‹ð{©ÝÛJ#|—¢è¾oìúõöÖÕ8Uw’—¯‰øïëÐÜ ­ÎÌ­Éo•á/–;\™à ¨ 냷`Y-"ëËýáÝE!;(#w®Y9Óö$Y^O[žÒ¶ÑØßZß‚±ßá½|ŽÍiAÆØõÐc IþvÏwËæ¹ŒSÌ—ú-ЧpóÿÌ6gÏóu–Îz™L"( YÈ€ö§û`]JXºÞpMÊßµ‘µgŸrKTl(ýú‚Æ·áø¥/ÖN œõ/Ì6ˆÿ~¾]Åþ½»‹bkþÎ6ËÒÒ¡ˆ (¨„ŠŠ]ˆÝŠ v^Û{õªv·bv# "ŠKw7›óýAï. ^õ»zßßÃóø8{ÎÌ{Μ‰wfgÖÔóÂz Ï>F„zùàYHB9©ÖëBÈÔÈK¶ÞNU¹óú¸G^Íi1ÝÿŒ¾o¿µ·2Xí–Øc°{°¯Õ©]‚Ù½OóŸš ørÞøN]·Ûé²)´¬Î"8öØÙ‹Æ«¨¼&Ca²(¯¦ZÎÞ\b†{ÆÝs~¬ß÷²­o£_ö>*‡ÓíÜóì=- jwœ0R=øðí¨RWv“Hò¿ÛK±±±?äFAÈËW çoÑþ½o¾þG𠦦¦`û»¿Õ÷5?n£ø«BØ·Ø-ÿÚN(y6£ï©Ñw9s~£¾ý-…þͲL_±·Ó…×Èwp’ðHÜ(ö-v vBèËá- Të¿ü" „B!„~ü9„^b' &9vî ÿ™·âFñã†ö-v vB¿ñ†,¾€!„B!„~ ¯¾ÓuäÈ쵟ÀÂÂÂÁÁû!„B¡_Þéú—f\Ø?/4j÷¨ŸÚ¿÷¡i¤ &þØ’Ôt‚¬—â`o |2’[AÔz‰Ù6 2{{ sêê:rÞÁÐ|1€¸4âôÒQnnƒ‡NÛò0½æ=H¢œ§kǯ|Y –©"ãS^â­ÍîC‡ 62²OM•%Ê Nº½å³x}«vº’ÿˆKÊÒ’ÉÖÒû÷¦!º…áP}ý[«SuuFnÑפYçw"­¥•¡6óÇÉþYKÿ59:dôƒ¼v`ž,y(?Üþ@¾šyÖJG€Ù* þªW·È—>»ã\|ÎmÎÿ¸wÆúC'ç³ÎüyWå©-­JﯘëÐî/U²0âÚ¶õ{žäÚõ©<+áJ—©íéO»–\\w¬bìþ‹ÝXï·MÝ~ÁùÈTcúWÆP3Qý`è&î'Ü€¬ø|töæϾºÔ†#‘nK#a,P¼ÔpØMk¼—š,lßèYm­øºuýøÍ —tð¼Ä cçæt$_ûûXD¹vïßúh$H8·té}óÞW åʹ÷yý±T°×wìÒÍdòÙò &›þCö5¢¢Ø÷ñ‚A_<ý ÉY-Üe¬ ÷o] ´Nº·ñtè']ý¾È’Ü;!œj)Ó ê"WYÉ«¿cSÛ™¹ºÉʸøÑiïJ5]'¨©Ò€ªæhÐ;éÃkÅföéi[¬ÄñÞz}.s7Sº”‘•$¾³_nðò•OJR1IÒäÚLo.w½jú)rq‡ãÞD „åb–•þ  ¹Ñg/ª4~I¡€4Ó<]Cò"Å}ÒÔØ …  ˆL¸|WsxŸ¬Ë’%+¢Ç¾ÐT嵄e¬émº›QM‰Ä‚RIcR)baâ…Ø€" Tœ { Wb‹x‘GbC"Et¶Zi™Â¬Ö¶ù1gê6pZkG}±D-V^îo-­×ž¦¶¦Ô_èØ% ‚¬|0Û,&€Ü0ú±›AÕ !}03¯_7ã’Ö|à‚9­›Ñ é[jònVd…¿u\l.OPåèô*s±Ë¾s)ÚrîÒ²O+¯@d…J•qàÔ\Ÿú´Ø"ù¿û’Î4 Øxzo,S¥~u 5ó$Õ¦æ,6éÒö‡&sÚp²à¡ç¨+n~>¡M¶ª ïv ƒ—ÑHØM’ÕKv…w·­9ZÈÒ4ÕÌ·n´°=ø©7×OÙ‘V®æ¼ðïÅÝ5©ÐP'”†¬œt”0båf¥gË9Ž°Î ~—œßlü¶uƒ›Óêg f>Þáu(´@$¦º®X3®ÕøLkPþ*FËa €Ÿpy{ å”™wëƒPÙÇÓŠï˜ÝÝòÆ.s¼â&-9õÑÅ¥rmÞ>ñ4Ÿ¥]5¨@×hݶM‰™ÓFAK}lÞúG¹b EÅÉ©äÀí‹eV©ÛáOönÜ.–Ùà%«Æ[SC–NðvjsyQÚe¹ ×7;v):Žçõ—Ѷ©âk«ÀÒiJgª¦{/2ܶéìÇ^Y…b‡Y[—÷U [9ù8͈–™Ÿ'¶œ±uiMqªÄð.¿cÎåÁû‡úÏ–*yo»×žà¦F‹–‚xťǖX²$º¨$xÙäBvJ8¿ÏÎãžÚïoðy˜*º^Ϲ«¦tP¦ÞÙ±îØûr95Sãâ8ÍU‡Ç笪ר%‡=LJCê×RÌ®3¼ë´®^«W¬RÛÛt‚ÁaRò Ì»&]}ÃÉtrÆí–Ë,”j®²–½ù;íSŠR¿Å²3.²,¶”4ÐPªÝž(*V8VRjW¿ •i1^;<—ì5S…-<ÃÆlÂ09~xÊó(h[5]•Mo>ÒG™Í+~²,!*]Í <—Ön»YsJɳeñŸ2T,ãâƒò´†ïÒÏL¿´¢Þ÷I¤K¾ÈÕ±G““•qeyIC÷®DiéçÆ§WF®ØJÝ~®:*B㟥i¸íTã¼›#Þ·î\Ì Ê×±Cƒ™qiy‰‚¬YU¼‘¬Õ!.UVKë÷ÀÇ ¡©<ý×'ÂPk`ÖéPVo€¹ ”@æ ȉ㫿tÆy#‡Î•#üóʼnm&´b¾,åVŽtº²:­ »LL7µf9”¾ «î›Â4©2À¡4øiavB!C!`ã”åo²­Ü¯œ¨ÿÕ1ÔÌ_2˜ª-´ðÕá«ô»mPt\ÔŠ£(â¦5Ù ª Sä”% ç>o$ì¦GT?”d=Þ~0ÛuïùþjifOKh´p™X›äÑúúøýÉz»Å}הΠª7  *ÊÑwjƒeþ÷ 7˜‡îkžrhêº;Éý¦Uݼ’îgéê$ß>ÑióÙÉe¯í Kåµ2g}÷A'¼(7ì=Çq0xq~ÞÁ¶ó7˜\yü[…„y‘±¤i?ÝÚ"S¯ƒìŒÈq ‹Þ^¾§l¼÷ªÚô‰c·h%t[a“}NO²4âà¼MY§´bü-³JÍe¶ìG[|bwž¦/ ?0s­¯åÉE¨º}g »–%X½Ì±ìô´ÚðR5½+ëý¶‡ŒÉýU*ÂwLÙv7Åe$ˆ ³TG\kCò²õNŠSÿ¨††·Œ’²ï»4P#ýÊü©‘ ôR~–ör¿­VrPòbí®¤~;Ϻh’©W-ôyuhvÁ¶C9CözqöÔOš²ŽÚ…/¼ë×Ú?1ò¤¬ÖÕouX*m\Þ.å(È+(`Ò…0éBè«/ì—y§ÓF´êX箋0+?Ëδ·NâÓcùºsUåežF“@40½§•’pKä…5“jûÉÓr «Ž®m úÉrï¤æ%'拌ôæ*rb† Gœ[!Ê +Wꪬ@BGÍÒ0§¸îKý’¹¯+”{¨*Ò€ÐQkc’‘Ú@4T]Ñ[ô5¥o·E}2R3×§ Î}SœQzia2ˆ+Äòɹ‰ ŽÊ*ÚªmŒ²òeÌIº–€-»¥’=@ÿµ† qúBT­qq<”#sHžœs@§þê›…¸4ÊoŪ ö+½»ª‘µM <Ùh¼LÝOI‘ $&ˆ7k÷ù5äÛ]³6ú¶9åe+ÿ=b¨©óâÂ'óQK´+×ENCW ¾d>U…e„ñ¡éBŸ@ IDAT°›Tw鼄 D¾ŽÚt‚Ðë6Àì^f]J°Í\lÕ©±¾¥š0®¬‰§JXvÆl‚*ÔRR1k£Íª‚['ù|j \hcÃ_:fŒs—.Ý݇›±~Îø+üð’j¿X÷ù„÷ûÅیѿý‘ˆ‚"1«†gÉëíKC”ÆêªÓÈS”óäïõÁv^{œTáUãU*ƒ’š»®1`ÁlÙ·§âŠT~»Ï5êf櫪¹íü //Kä~|™SÞ™Or†Ìä «(ò¹eå‰ o‰’eñ ÍvÕ¥„^w×–—Þ5°í°íÍ9*T$¼x—ô6rö¸Ã */Wç¦qc“´\:jPÒ¼Û ó›I2ªó¤j©Ênd«ë {*[]MUŽ }Ñ…L섪킸ñѦF}\Xuw¡4]ÝÞ£U[L06ŒŽô\æ&y3_T(¨sž^Jês8ôÊÓs‹Dõê0[ŒÛeÑɆVü4öì_YEÕŸ–¼ˆ9ë“UH0›wÕ6ת:îR(U'\„T6G©l–*Yû“×”¦¾zN°å­g6g<Œ ú,"@L¨»µœàc=ÉÇzÂÖÖƒúÈQ Ä$A¡Pª—"Ñ@ÉZ,¹ZÚPüÞ¯¢Î1ü!@[ 3€ÒŒ6ƒª7(CâYøÅq\œÿjÁÖ^;=Û)R€¦ÔL¾,£r] r„JÒW!¤ËÁ œºtqê2xk„HêSEe-U=Ç^m”©T•6½ì˜‰q©ÏjËW|S gíï菱°V šˆ³ñùH†¡ vÁ× a饳©µÛ5…Zo»–*•F#ê'clÜÕ…‚ò­ ”³žwòÂö)öÊwÖM^r#ýgl³did¿½•ª0éñÃÈw‡§õé9Àózjø–‰ neý¦¯/ ©[šS¢ß¤Ö¾‰Ÿú&Jlb¡N#iÍÜ6Í×»·ózRƒ¯¯¨ˆóóÚ]4j£»¥<_R¥îÕC¢v¯N ù"²ÞµBÙ@œýÐkòšÛ©›¾Ã]šÑ+2…Zyí’:lI oé’5‡­ªpdiƒ^u¥KL7™°ó¬Ÿß¿sgÙ2TA W |*QïPUÝ(éZʲ[×P««.ʨ©©±ñTaÒ…ÐW\<Œ}’¢Þ²Šä”JPŽbg•œãqŸ²dœEÓÍul2ïÏÍ+'A,Ê N¼sŸfíÊa2idnY©D…qéb (@ŠD"Rœy:üJ E¿GsçÉœÜòr²jzáû2–³A§~êÊ©YbRÆÒ¨ª¶rEùEBeD&4râCU³•+ È/(»àc‚¸É³3Цz×Þ”ˆYBBÅZ®äQvfå%/×G<$Umå‹ò … ÎÎÿ/B²Òµ„ µTrú/•tQìAK¸ó¡¢@ ç»4—è@ªÐì(”Ï…Üø_x«à'ø-ßßoÛß“Ú*PhšííˆàûQ¥bQV¢AwSéÇà¥Ëh8z?}þìÙóg×—ZÊK}ªÔÌ¡øÕÓØ2’,þ 2m©×¥¶<ë›b¨ßФW©šŽæ5ω˳ӲËÅ_6ŸªÂÒ]A— û«ê’^ºŠ¡“aÆÀt2‚îÅ–7ZXîǯkéÊÇì2ïÃnàä…ójd'üŒwVÄå[¶×¢1Ì=Î< xøàу›{7kóÇ ïþš¿ëŒœå¸‘J××ú>J(ƒ¸,ññ®µWå‡oÂ¥ke;pþ(òÜö{"™²—»VÞÔ[´ºæi½Æ«° ìõRn¬›;ú MÅH_…Þx§ñ¹?a]K4P™£5Æ`õ‚—é4ŠJ'õæÌ?y‰A)F]šÓÿ[Ç#ºþˆ¿¶2vûÌwÝT fªšwý×¶¡FŒò¬ÊOõ\ö¿¹Ô÷©ýÆjõ­0õÖ^ÿ”\•­“†ˆ€i>vÑhŠ†Ë²9QëŽ:T¦aß+ÛsØüÑ#Õ–MrYÕ¤£ ¨ª­­È³®c°®vXè¤WNß=ˆÚºù㟨«h[¶4¡¤çòHɳKB­áá-qEK½û¢éoVÍvTNÝ@“Í`4ñŠ%Šj×¥Ë>­_7v˜¨Íú,]o«¨J.ZøqÍÂQçiÊÚJ,*ÈYH4Jº–š¢~½á]Ó:‰V×öâÂæÜî·§§2>Ö…šöÕ£¤òÇ‘ñ•ñ?Tå+ãUTT~òrI’üÏöù‹ÀÀß±YdEDƇRÕö˜”’‚«Ò›¯±hõ]†UÍ» ÿ[wË;wÛßýÁ¯ü}7ŠÅ¾ÅnÁNhè*IaØùk%ÝÆtѦ½üsæ9_ï~êßz ©yW¡·2ô37d™ðNBU|F…ü¦-Kª¹ûéòà;ÎxYèëï7³y~q"„Ð"L¿»uËÍ”ºßŽc˜M\3§£Ò¿êÚ…Ó\;ËkîÈÝ…à´»ª ÿþæùÿþ°j&]áI?B¡ßý„O§ÏŠ}þýqR5»-Ù߭Σo›ª;âàI\õè_¯ „B!„ЄÏtýá3]!„Bý6ðNB!„Bý@øLB±±±Ø ¨I¦¦¦ðŸy{!n?naßb·`' ôoÈ2á.„B!„ú0éB!„B¡YÒeaaQ€~$—ÿå/¼º^[Qû÷ði2Éx·uòçl!€¸ ÓÂíý'ò+ÄõkŠÊ^θ{&T@òÞÏ¿¾bìç,!€0ú㎉Ÿ2’Ë" 2n®ËJNç…¼ÜòG¯©PÅù9ëŸþÙ×ß«çÍ­ >Fdˆ¿¹Õ E‚š–Y“à¨ð¡d½Cx[¨àÙÜ ¢ÖƒHü˶Qz{ígÇÎŽÝ&lž+¿Û?ÕÅÑÑ¡ó•þ)5C[”uwAÏ€’Ê*»Lõð•þ´‚{iéàΜ'ø„Š¿1 ÿ³Oß–fµO5‰ô²œsSa7©‘Vˆ’àý™ßHá’g3:O{Zòµsn„0í¶×Ð.Ž»8»-»’Àûç üê÷3@YÈÊñ;£*ûC˜tfl‡á—²Ä¿õÞE\øáÔÜÚZ´lÑÚqØ'ߊ\鼈¿º;-¹ì3 ã¬¥Ÿ56N¾6¤¼§“Oßq_@½öŠÒÎn3ølšHè7Lº,,,,,,°ãÐïG®³ãú×Í®›ŸôŸ:ŒÃioÒF§öѲ8çþâ°”n&ŽWaIl7T–=+¬Te‘ÙŠzÂLn @ʵÖR¥KŸ%$Äßö¶H^ѓůµ-<¯\·×ÈvyW–Eeð¿õ0öO"ù/ãAF?È‘ó4hdz¹Ý2£êȃ´ÞP:ÌVõWýJYðtߟ{øüþvË{«¼ß—ó¢.¿j¼åÞóÀ s¼×Þˈòß\0rÁÍêÓ{YeêôœÔ§îñ¾åÓ/>{~o³þ5¯c1ü¯¡öt½~0ŒóîDÅÄÄÄD¸:«ù„•nzÔ#I/«á9C£aÁj´—~Záš~. Ø´%nØé§OÎŽIÙ¾1 €ü‡ üæ„K¢Ÿ "öI¢yWâÏ®Úý®ì7ßcñcLz^nò‰ QŸ‚Mdœ:õ(·¡þ§0äY,Ž<‡É’güÀ}¨0*4¶èÿx°¨@²½Äïþ-úm|õ‹4°×Ðo,|æ 2ä ¡ *rdIþ“¡ñö&MT•“qP£¨Û©”‰”X Y¦=>¿ŠàwÔ&2^òt\éE![ÞEð„¥"VÇÖ—¨Çíæ¦Å‰ü¶(º/UI:ðúN`¹HHÕg7P„©‰ç§|HN*:·°¨¹UêŒêãçW<£É“µ”èÀ0m;\£ˆ&–…ï}sçi©hº®ÖÃǪSBƒ}üMæoÒdŠÊ^Ì .^ìÜ-÷õ®ãMzYQOlÙjâBÕèšH–ë*Pqíñá?²òÁl°˜rcÀèÄnU(„ôÁP<̼~ÝŒ HšÁH¯•íô@Эux~ùåéa¢.,9•Óm¸Ñ¶óK8f^9aµzSéš» L.U¦›bõ<¥?-lŸ×£‹6ÿ8¹·TöÕ1ÔÌŸÏ­L5÷¤—¿ùŠ+ 2ÏT÷ÓãŸn#§³ä²dÞÃQÝO{ì7#1gQúÓFÂn’¬^rÌ¿ºzÞöçùl–:9"ëF ;SŸxqÑ 5ïRÊ4û¬=¸¡¿µ‘ÂËúfrÙé)ì“;d?}Ÿ«ïqØw´aÕ%!B©»Ï]GBŽN–å—ÓØT±t…i·ÖÎÙñp厨S÷… X9ô²¬û·xѩͽµ¨@×¶±m_ÔÊŠÕIYWá;~ñÍL€¨(>‰ydƒÌ*5½µÃu©ÜîËY§ÜzîíÓÓ óähÏ‚§ÁåK½Íã•”+vY~h“]Ðæ“Ÿ¢*æ-7?±Ùúíß ·»“@(õðõ÷í¡$”šO™œä²8Õ… ©9ó³ »IÒýPœ~ÛË;sŒßÓ§þ[:–æ -\*²¢€îvøqðSŸÖA{¯&-,,ÈÒœ~àüµ““JÏcÍ>xþê¡þ‰§®Ö½yBÐäX¼Ðµƒ;ÚÁ0¶­¼t…ñ—ö½wÞÿàÉãëkZÄ'VüˆA'½EY/Cº[ÊCEÔѵ^î-X¿ùÞ%;< ZuÒcÔî] ;·…Oï2…âÂÐ3¶–yÛX›>)8­ßÒOßö-C›É[Î=uçþý{W¶ôÓ6tÝ8ÏŠ%»Juot·«xþ6_Tþ4‘ô¡@”ö¸¬S7=^ØIæœKŸì·øjŠ¶ÛŠ ­ZNòù³7=pÃÆp—÷ž^_@¿´ïE¸¡X»<­[e÷•SË7Fö=òèyàÝ?Íüm|Y,ëh%ÌOWr?pñÚußNo\M­  qךöR ¦¢’‚¢"ov!Lºú¥¥¹÷½bS:ô° ÕìÂéé©­Ûè\àï“^ÜÀ÷Æ ¶²…z 7¹4þ-aÞ–Ån©«ÌÍÌHÍMek(r¶mÇ !#/FÝòù+¬à×_D¹¡EŠ=uÕ™@QÕyض•Á4mf¬Fr=5qI¹Œ#A1Y”T¬>¸™:y•ö=)¯JeË0ÒÖ• ³4E2ç¾h¨§/•#†q<”:€ÑHž‚ßáAqIøaÏÙO¶nê¥NÔi*Ñà©Nãeê~JŠÅŸñF{òòÎZý‹7…”|§jÏÚŸ{ßjJÿf•«ŒÊÖÒÓbSгî²(õ ×â¦ÃnRÝ¥WÄ=‰kæÖ£`öÖJ®©.%ä-ØkR)òFVš‚ÉÍ]²]rÆŽæòUIWEݲ]s&P•t8ÂbžÄe¶ÝÚ!¯Žt¸³`Sh‰TyÚ:òMá±þlf×¹“d„øCÆ_ÞëgÔnÖŠ® v^3Ùü÷?¿&IJýÓ3‚ €„â^ÓæõœÖ»½±gÕ§N¯qÑ BãUäL{·Éyü);:  óäö9Ñ™ïçXõ2•St\³{œøá1ßÍëwd••Ô^âóhÍA½X@UïµãúNJ£CQºÊ_F︆c³€¢Ðvè åI|™ÇU³®­`h˜(órJØ™Rå554äñ¢!¤ ¡_‰¨âÓöÐp ë9uwàt}óÓu­æØ˜G¼»ú°\öãT¦A{"áufd¹š‰A(«¶`çGeЬ5”¨dÑÃW»Ö&æRØÆ}MÚ6«»Ñ‘Pûy²<µ´D:ÑèfI(´P¦ÆgçÕ\Läz½yV?}"«ŽÑ@VN «ËS¨øý÷ï€Ñ à ðê\êç=h tPZ‚ÑfPõ¥`H< ¿x^+Ê ük¬ç»'—Ú+S€®¢¯P’V àçg ”µ9Rg;ÒeÈ€‰-ÌÌÌÌÌ:­~+”úTYEWݰǠvªTªZûANrܨÄûµåË¿)†úgíù¡×-‡wP"šˆ“C•\V#hŠ’aç}MŠ-c锚±BPi¢©PÊ QêçW &ª 2÷0â¢ÈG£ŠÅTe›~¶”¨O…ò’ ÌgwX}ëé±yÎj©—çžâ—ò3®)ÅáOx¶êBîí›ï_íҾݨs‰o–÷›t1ý7}yM˺åcPRí+•xIAád‹¶Zt’f0n—Ñõuçâ|AJyÔ‘9LÝ5ÏšC@Uä[õ1N¼óøa¬vï~}µb<¸™hÖ§…œ(ÓΠ¹—©Í;4À€QçRIŠkƒÂÂÄÄ\>ÙÀP„FªH­ˆš£)äUKƒB£Ͷ²¼††Of&]ý:Ä9þon$虯#qGÒ(T~ËtÒ}Ã^§Ë<‹¦¨Ø*—úÇå™ii0¨r&íDïn–ëÚ+Ð@œ÷ª€Ý¯uÏáúfj¥ñi"R A€P("ijv E3ò@dÞXüöSþœ¬µ4éÀH¸z,3Ÿ äÅzû4‘ÝL¦á ãŸ–+²´ðí#žŽ-›Îb™EÅB¦d~jàü¨:_¹ë´-uà·Šb1Tœî^Ð\T A¨B³£P>rãáfòbz¬ŒzôÈ,[% MÛщòäzx±X”þäBœI? ¶ô)£T­î'>ÇÄÄÄļÜ`£ õ©²A7{òùݨR’,‹yúZزQ¯ÚòrßCýVÄ?KÔénYóü‘¨,39³L$c>R˪),#ï– [ýk®¶K/]ÍÌÅ,õòƒR_*o´0ûkæÌþ’€øÜ³^^—¸¤¸ ìÆ+ªm3cÉr>m<á*Ãaäœ5^£´2có?a –G=εvÐ¥1-—Þÿõ1<<<Ôo´Aû?o®ó»ÞÞ`Ûx¸«ž[°ñf\©Ä¥Ü[›œáL˜Ù^9};‡Q^SÈÃk¯¦ÊÚ§‹ó6Í9o¸vû¨š§õ¯B(Z÷T}qðµs›æVŽp}ÏKݾ­9ÀO ü¨<|é‚InN‰!Iå"1•” H–“YÖ­ÇÉ|ç=[ç¾âan“lj*âÛ&œ¿“À²$âÚÍüVŽÍYrÊdÚçlÉK x˜ÀkàhU€Äx×4ÏUýT° ª<µŠ¼š]œ½¯otÍqÂvWϵGuB®å°nONmŠ7Þi,ýÌ<Ã@[­ Yì¤D ªwR^©hiLÝ¡Æ ^Ïw>‘ãè¨êšùybÂ@Õ|q`6s¶¯ÕÀ÷¯‰4à l4>5*C±ë–v‚má»úó€ªlm4tk &.6ý#_TªVŸöÃÚÑ™S'õ=®q &ZÆÍeoì„ju${Í4pðå å”…­ B1ÐÛ–?h´2¸Îîµ ‡¸ñÀy ,ƯØÈ²w{÷½M/~Û¯í¹NÞONö›¾ÙuÁâÞNe â¸hO-ç½Ì•‘þ”¡±àïAüÑ»s É4¶Ù»ûŸÇP÷4´$) ‡+V—" ÍxzÜc¿’‘ðÞn‘\Ö ‡ ªÂª’—Ûå¬ »iRý@côÝøÇëùã{ «™©1ï4þç/Ÿsc…k.#¨÷Þà:wr·C"в»ï_6rr Ñ@UEÝiƳ'v?E§QÔÿðµü ÏVUÄ>M0ëiÀøoí^èF“fl^?Á~Iž˜¥aÙkÊ£ãÌ™åUŸŽ^7ìâ´w»íî¯Qð /ÿu>![mÕG1 À²œ>“h¢ Eµƒ‹jfJGk5–N';¶od%Ë ã4fï{[K½¹Më–”ÔlÅÄÆ–œ0r”ʵ³kW¿ZàîrDD(ØÎò¬½åxí!Tº×«2}Ó${Á䇀Ê2sÛºÅ^Qž?Õ]k†›ý –];Êþâ*U£:€ ÓMè5Yæ£%cÎO”µ…"ôoƒcÕ"ÉÿîýŽØØX¨I¦¦¦`û»ï9_‰ÅBØ·Ø-Ø ýƲLxe¡*+º^û/7s€+Ž„ú] S®¬\q1©î—B™­<¼WvQ¡ürKAèW„IB˜u „úÝOøš»m9éö{,¡_^x@!„B¡ŸéBµþËÏt!„B!ôƒà.„B!„¤ !„B!„0éB!„B!„IB!„BaÒ…B!„B˜t!„B!„¤ !„B!„0éB!„B!LºB!„BaÒ…B!„B˜t!„B!„&]!„B!„0éB!„B!LºB!„B“.„B!„BßMæTOÏý<^…Bc³}|&a7!„B!„з!¤Ò­11¯â⊋si4†¶¶I‹üüÖaOý$‰€B!„ÐLº<=÷œà>ƒv9÷“ªâââ~íÚvì,LºB!„B_«Þ3]11¡ñÏ`5€€€/ÀY(±Î ¹î鹿6Qã»­÷¾ùœ›Ä÷¿yÜ£§õ_ÑÅÎgÓƒ¸ï[ËášE!„BýË’®™3÷ÄÆ¾†î‹ä«§¶ø²óSR"«¦Ð›=qtÛ´Ž¦dÔ­ãçÏ<ÌÖí=¸¯!»!„B!„Kºx¼Ò¢¢\°H œ@(/(*Ê«üÛzü|”Ïï>}ÎrŸÕÓ¦89y]LÁ4²àÌ«ÇÜô nüU¿M½XÍh̽ô näÞC7ýcÒƒ¢‚ÿô1fëí[qéAo®O·U¤4Y€®Û{Æ‘ÀûqéAÜÔÛþ»Ü,äkf»çÀ«ŸÓƒâÞíô°ft½þ[:€¦ÛnPäñŽf³ë|žDqÓƒ¸±W/zYq\ã!„B¡ÿSÒE¡Ðh4:äHÉB@Ðht ªÛXªC¯dˆ€,‹{œ&”³™yjïp{vèöy›vŸa&ï:p’¾ª:‡R‘• `V–ú8è]9Eô” Ðmô|vø‡°ÐK{ßäã{"B!„B?Wí.ŸÉ-ZtRHSƒ97ò¢Öì33; =fˆ. uÜM»².!gÔ¾“nUòFŠ«þ•ÎnH‘PT“Ê Iz]½¬A¥@Ò¾ù½GõpÕÃqTÏ…a¥ukñ+Òógßð>ïø¥‡É„™“›Ç‚—§Z2q#„B!„þOIøù­ëÑc’Nœ)m$LèHÈ{+[ô´·wóöžPY¦ìõɽa"`;x?Úï³iîúýƒ60 î>Ï01kæè«Gš¤Ý LàÿÃðø ·RôÝgNîÛ¶M;ÛAs—mŸaÜ`ê$æ–hÚ è`o©ná¹z®UI𙣛Ö^J›AÁ5ŽB!„ú©hÿ¿vm»§ç¾´´ØÊGÖÐ0ÐÐЫɸ*ó #£§¯˜3íÝ@÷ÖP‘õáæÅñü²ÌýçÓ7/ë¿pGgàg½>¹aùŸŸÊµ­þa|eaûÆNá{-8|ÕŠQŸü7å *]qÀ÷µõb[wŸm/Ìó8OÓ}êvw€0ûÃͫ¾/Ç5ŽB!„ú©ðu~¨þ82B!„Bß~ß!„B!„0éB!„B¡_MæTOÏý<^…Bc³}|&a7!„B!„з!¤Ò­11¯ââÂ*_¤¡­mÒ¢E'?¿uØSÿøLB!„B?6éòôÜp6‚û zØäÜNªŠ‹‹ûµkÛ±³0éB!„B}­zÏtÅÄ„FÄ?ƒÕ~^¾g¡Ä:?$产çþªBÊÝΧq«þß<ß½yJ[ê÷ŽK±óÙô îûÅÖrLKÏÀô nÐ3®2„B!„Я¤ö™®™3÷Ü»wº,¨ÉmÚüÙÃSR"ëWL¿ºýF²b‹ÞãGmÜooà1hÍûâv›D˜þÌÛ+_1ï}¶WB!„BèWR{§‹Ç+-*Ê»:W%'Ê ŠŠòêM%ß:xb§× ×>û?L›;¸9¦Ñg^=æ¦qã¯úmêmÄât=ôˆ›p¼¯AQrõ97ýOy`˜,|ÄM?:Ñiܽô näž7®~NŠ{·ÓÚMÐõføoéšnW¸A‘Ç;**µ±~Ž×¬6Š ¹—ÄÜ{è¦LzPTðŸ#Æl½}+.=èÍõé¶ŠºnïGïÇ¥qSoûïr³ÇŸ#C!„Bý¿“. …F£Ñ!GªH6‚F£Ë¬ÏãøÇ€qg¦œÍÌS{‡Û³C·Ï۴㳃ûš“Ë ¢/¿ã½‹œŠÕöy‡Æ5Ë.Ÿî?ÏÈ+”^ÚçŸIhuX¸Ò^E”}{ͱ( ùsêâiÛ?—I/XžSð䉠2†a×%«m n_º*&.í©Ân=õôñ‰í¸GÝûO÷À»:Ÿ‹N[¬¤­mÒÀªÞ½@’ £¾]tâî9páÖ ¸ÍútV ¿&•Nv­:õnOKºÿޝæÜµM;GsîàtÄÞòñ>³×ûNUUC©H‹Ê€²ÔÐÇAÏà DÒ‹½µËçÔÎ=oùe!ç|vŸÚs.€PÒQ6ØÃ@©×Üc·îõ0ÃŽº,\Õ!„B¡ÿ‡Úgº||&µ&öÖëâ9¹° 3@&À€½`ff§¡¡'³>ðKSñç'1&’@AP)´oþ´Ó•€‘Ù¥¸ªB!„Bÿߤ üüÖ¹º. ¹ž=2IÈæB‚-R23³³·wóöžP¯UçdkyÓc»YDïòñO/‡»Ï3f71kfîsJŸ‘&i÷ÊóJ®Eƒ­ùÀ¸ãedpî[Ñ—.÷îq Ó@\b^a9€¦Í€rae_Þ"~Âí€ÔY#õÝgNνò2“aääÒ¥pϸ5帶B!„Bÿߤ ®]Ûîé¹/--¶òÇ‘54 44ô$3.íAó¦ˆ ¹oÎÚ¿ídd ¶â|úæeýîè ü¬×'7,ÿóS9éOÆ€¹d?¾ŸZ‘[zý=tl¹ÇU4WiÄß×Ö‹mÝ}¶ ¼0«×ίhRYؾ±Sø^K_µb@EÆ'ÿMy\Õ!„B!„þ¿H„úER/޶p½]H’$IòÎ{Úè6×Õ6ê¾ü^¦°¶XYÈlóu¼ª üÏ[-lö& ¥ç)ʺ5o´÷'žäôÂ;›÷»UØè”¦‰‹?ìŸd§#G%(,ŽSŽD”ˆ¿®É_»ÐŠ+ëíã]Ngˆ¿¬Õ_»¬ò·K j—£ÛiÊáðñ—ϧàfßæƒïþ£çÝ¡Íʸi „þ(˜i „ú¥s^îã4æ\|å—ÈÜÛs}}Ÿ›œòùt»+ÓW…ÔûJ:]^=óòõøÊgˆ þçSä䨲ç›÷þYDø ~s’ÓyßÓƒ]9_´ vßÄu™SfóÅ‚Ì;c“–Üþ‰ÿUÍþ†…2Mç…–Õñycµˆ/kõ7,‹e±â}I’âŠÄ C¸Kú- ($¿e>!ô[¤ !„Ð/…÷ù„OXGŸC}Ô+3’n:m×α&L ZtÒ«HÌ©—Ì0Œ]¹‘(A¢ÿ Ö0g e÷iol¨¯£¦Þjìqn)÷Ä"ß·vŽœr)½"åêü®¦zÍ,‡ï/%{dœ¦²ºÕÔóÉB( ôpžvwˆ‰ýÈA];µoih=Ù/YÀ‹;ån££­gÜÞuDk³™ÁµéŸ¨,¯ŒÙÌH[ŽE«é¯î¬B€(óÞŠž-Œ,z¯¼—)‚Â[;õ±RWh1ÿÆážFÃnç“Püd’¹Ë¾ 3œ§”êÇ&”œCãŠ$b.¯muÔêE/|•èá<= D*º)¤ßõÏܸ°]m£Ç6ô†*º®­%û1»òV$/Z²–°0ÏxåÕ»÷. œ³2¨¤2ç’\#±gî‘{"(ysô±Þ”ŽaóêÇ“›zÁcUªG`|ÂûcÎ%¹|Ü8B˜t!„B?ޏèõßC†Þìqâ ›–Ä¡e>´_ùÅ;ÉåI7¯1GöÔ¦Ê=w_š%¾î½váÜõwÒJŠ*ÄÕå+bïÔ;´…PµÜ΄ù V§(Xî¡K£(´è¨ËÏ,®¹£Bp,ûÚ(ÀÔi©V‘Y\q-Âdš›1ƒ`™œi+_/ ªVßmA驾S:0?ìrµè²é}nä€ÏO–;·41±è½%²$1ÜÝZ7„&]!„Ð"ʺ·ØyÈ­.gmí®&}`cµÞ§øüÍ—7®3GôÒ©¼C&J=;ÜfıXª‘ó¤ù£L™dm ®IÀy±±Y<’Ê S¤J’R• T'$Yõ#•@Pˆº……Éæy‰%UZt9wóÉÇç—?ú±LÄl»êqT\\\\lTxØ9æ h,FeMBÍyj›÷Ç<<öºå”îêY±e–1¤æÐÔ_"æ:IWÍ¢«Z#^ ®¾á ÷Öõ×L86Ú¦ïx¡ìEðS_E@ +-zål•¤j‘Uϑ !A­ŒDÖQ°›Ö'ëĹË_YNï®NHwY½Ú*Jà–€¤ !„ú!*>mw1ùîÕN*²jr#zåîüã eD煮 *âî½Q²eÃü‰½´cžÆ• Å@P¨„ ŒOÊ™ö´L;ïÏå8ûîœ>S¯g‰¿,¶åÖñÇýÀO¸~$¬´N@SÑ©¸µ|Žo@r™X\žòâò½<=+ãÖ}-“z]D’Å!ëœ;Î{Vÿ))B¥óT›ÐË^XNéªZ93‰ØæD¶·hl_ ºÕ2zÍ\2¼ÌÐ%ízœdºL[³{×ôf©‘9²~…Ÿ¼{®wÙðyU_ó,-YK˜þèT`Ž˜,|sú¿K/CFk€m=epö¦e!í§wQeKÅ#´l™xüZ<ø 7O¿/Åm!ô¯EÃ.@!ô++y¹isprapkù¹ìngãÖ¬׃ÕjT/þt¨T™ µ›;KgXË ºZ†ö¶V”„ôrjK‡ÎdG'µ7{|Ÿîc¶MH(w^í7®ùÒ/ûµHªîˆ}¹Ù믑׵ÐSd1iµQpœ¶ÝØ0oÆhÓ%é|à˜öžµÿäxýfpèpÈØ1-ô@7pø¼‹2q·~ÖåèÞ!ÿ¾È½³ Å„úÀz±-ž(ß&­þêæ?±>vlŸêÿQŒæ÷‘:Юjõ㕟P´‡J„§£ÚriË¡=Mv3è­~[/ØÈÕ–æEn¶bm`ë9ÿûÖ@€œÕl‰Zž Fâ_ÝÌ=‹m˜¤KÅ~‘Ptwå®™·ú)~Aa^Ìî^n±‡ììÀÆžCýÂðNB!ô]P”Í ’‡w3]DPe§?NÐÁŒë%±g{™OŠr6°f\¡_ÞéBµðNB!„Bß¾H!„B!„0éB!„B¡_“ìgº<=÷óxe ÍVôñ™„Ý„B!„B߆J·ÄļЋ +.Î¥ÑÚÚ&-Ztòó[‡=õ_€Ït!„B!ôc“.OÏýg#¸Ï €@À}प¸¸¸_»¶; “.„B!„Ðת÷LWLLhDü3X ààà pJ¬óCB®{zî¯*¤Üí|z·ú/&üèŽ)-þŸ†*®û¹µ!][Õ¶‘ŸGdšMÜø 6ˆ›ôñ”ƒ®„B!„ÐOKºfÎÜûº,¯žÚàÈÎKLI‰¬_1ãÆÎƒ»Ž‡¨·tݸu¡ÕÿñgàÉÒ°Ëþz’‚ÈÃË}o$ ,+oé¹¢»‰\ü¥m»·žMàáúG!„Bý´¤‹Ç+-*Ê;Fý"N ”åÕ›(Jº±ï¸÷Šu›^€z[yºÑ˜{éAܰ ëý˜|ÆÃ”c4dÁ™W¹éAÜø«~›z±ºn¯éŸÜŽIâ¦Ý½wh¨1ƒ®Û{Æ‘ÀûqéAÜÔÛþ»Ü,ä `6°ÎçIt7=ˆ{õ¢—‡%5¥Î÷"ùIÁ—¯¾Ï€ÒøÈÓ*ƒ‰ÜsàÆÕÏéAqïvzX³ ºÞŒÛ»+PŒ†-žj_VÀ'˜²‚D!„B¡ï¦öí… F£CŽT‘l F¯Ÿ¬±4õô,4º°€ì÷±¥Uéôp%Ò²>G—´š~jï0ÝÜÀíóžýg-t_sR˜<è²ó™ã Ñç7 'û 0Ó³™ºæøx•û¾îó>Ê X¾×c±/÷ýÔìe¾Ó-‹ŸìŸ}4žnÔ®»Ç`øìúSØô/iœ¼BIà¥}Ãæì°p¥ýù‘/n{à7Õ²"l뜓Ï>TÈÙÌ9µw¸D½Ö|*ÇqB!„úîI‡£¢«kžy?ÞXWOœ¶XI[Û¤^=¢íæ‡ç€÷~×ú]á<ЀHŸ½Î' ­VžÓˆ;¸çÀ…$"Lõ×tã>=º‘] v®óÚ/8»—ÙjÙCè5÷X¯ªvÔ¥ž/å(t=Ÿþ!,ôÒÞ7YvÃ%¦äÉboùxŸÏ0“w8I_UC©H ‹ÌTd¼yüêS£•g© »núÉÇB!„úÞI—ÏäQ£ÖÄÞz]<'–tÈ8°ÌÌì44ôêWŒ?0ïðû¢²ŒOá’ÊÄ üà)®ú—  DBQõÇT $í›?ít†°rZEvRæûáôQãÚÚ´srëèäÖ_c`7¯áóêOé²7âËÉ"ù~7~CA"„B!„ÐIºÀÏo«ë¢ëÙ#“„l>!$Ø"%33;{{7oï õꉲCï>y\ÔÐlù wŸgÌn:cÖÌÜç”>#MÒî=zrR¦2Y°ÚKà!2èÞŸuv[@ꬑúî3'ç^y™É0rréRxp}ÎØÙÚooŸ9zû†Ãßa,åÖ³W»(ÕÂfÔy_"CßÞuˆ•:ÈõÑwýé§/j»Ì ð6B!„Bè%]píÚvOÏ}ii±•?ެ¡a ¡¡'™q}²°ýçÓ7/ë¿pGgàg½>¹aùŸŸòÊcƺ Ö-wíµŒå w÷'‡\;…ïµdàðU+FTd|òß”“'ÈÓtŸºÝ Ìþð`óª}º3Ǭ­;åàûÚç®ùvÿZÖ€n1õϹ”°ŸŠ¿=H|  !„B!ôáËúP-üqd„B!„¾; vB!„BaÒ…B!„B¿$™/OÏý<^…Bc³}|&a7!„B!„з!¤Ò­11¯ââÂ*_¤¡­mÒ¢E'?¿uØSÿøLB!„B?6éòôÜp6‚û zØäÜNªŠ‹‹ûµkÛ±³0éB!„B}­zÏtÅÄ„FÄ?ƒÕ~^¾g¡Ä:?$产çþªBÊÝΧq«ÿbÂî˜ÒRáK cšMÜø 6ˆ›ôñ”ƒÂ¯”ª¸îçÖ¶÷Úª¶Ìߨu!„B¡Ÿ˜tÍœ¹'6ö5tX _=µÀ—˜’Y¿bÆw-PoéºqëB+æ-MÞÒsEw¹øKÛvo=›Àûú‡, ;±ì¯'9 ˆ<¼Ü÷F²à7jB!„Bè'&]<^iQQ.Ø0êq¡¼ ¨(¯ÞDQÒ}ǽW¬ÛôÔÛšÈ@×í=ãHàý¸ô nêmÿ]nòÍh̽ô n؆õÇÎ~Lx÷y÷ÿØ»ó¸šÒÇàŸs»·}Jš !û’Y¿”mh,ÃÈN×XÆ2à ƒ±ŒŸPH1c41 Ù5#²“%kÅT7RÝH¢}¹·ûû£¨È:Ì0>ï×¼^ÃsŸóœç<çÌë5Ÿ×óœçô5$ÖgsÈyP èX÷ÿr×ÙP…RTÄïó_ÚÃZ·ü!·wMu¬ÑÞ›ŒQŠ×Ï,ŸôùЕA‡ã”â…ýn­Œ%B¥6+Ž]/ž‰º±{û¼öÕ¤(i$zƒï}7”bÜ¥µ“lõYvwÛt,(F)*’oþ¬¶vÝ.{¡ g~Ýwù€ìøß÷„^I/ª¸q™Õ„ _8""""¢7tI$R©T†{OUI…P(H¥²òÇéš[Y5ìÜçóÆR/Çfë4·sÛ(;Å×ÞnrßôÆgy¹Õ*‰oÕûÙjß:¿ýë­‘ò"VŽŸ³îJž^‹‰;¼9èŸó˜¶tõ Ö® šÛP¯ì!7n¥æ€áƒc{¶‹9Úµ:Ï^ÐêAPÀ¾8˜¶õU7SIQ^Âñ_–Lš+Ÿê”cÕñ‹¹SluK:i`”u2`ãÁ;BµÖ3æ9˜ Ðm6~×öÑNuî,]9iÈm¬ZTÐmÙˌܫSƒÜxÙ«#""""¢Fé–ñ††¦––õî„Äã`û¨T l‡~‘‰…ErÇ Í–ý±yÝâuW‹¬¿r¬ ûÔ­ÝKªÔjc©ˆöìÓ}w‚ 0rhºÈK¹z6*G»‘¼“%·iƒïž!¬_w·Ú=;ÕòË({ˆÔzèh±‡×yî¾±ÙðvíUá?{®¿h®q0ßÚ¤ºs+\Ñi¤ËðzVUÌÍYi! {ØsÍîƒ~ΣkT®j¨%«Ö»kMàæÚEîëã ?oFsv=Ým]ľpäžh\’—Zø¢«[]À§Žˆˆˆˆèƒ ]žžc\\Æ>Ÿ9% ³ŽÀ`à {33«òÆûNûárFNJÔÕ+ 9EЮ®%°qúø)*€&/5[§æ { )*ù÷ 7ÎÓ¨UêÇaP¥ @ë#—þ+›å‡û-_qMg˜ûüÎZOUWˆâmAK ´ Pa·_zøÊ4þw¯Žˆˆˆˆˆþã¡ €¿ÿ¢~ýf†‡ïOœ Ò/T‚¾ÚÄÆÆÞÁaÀš5#˧N=wäXhÆã¿Ü Kš<¸†ëÄ1i{ÿ¼£mÝÁ©Óà £·=çÔ7œJùbPÝ “'¦’ô\H>y³ ù«ô_blUU ȈOÈÔïZ](|ÞOœìRçËî…#Õ5»öÖõ[UA·‡/ŒÌ}tŒv ‡~ý›W``Ýíó®ùûG½TÇžuu|䈈ˆˆˆ>àÐ 0ÐC.ߘœ[üqd3³šffVO&®ŠäDl6¶À}¶ó ù߸y)Q—ÞW½àŸQÓeËæôž±º# îžÿiÉÜåQ¹¯º c¶møÙqþ—yÞvgNªT/èäEŸa®…‹æöâ>G‚Ü›G|n‡ïºÛer›``7jÅœf k8nùTIÄé¨Ì—êYÅWÇ'ŽˆˆˆˆèÃýô¨?ŽLDDDDôÆI8DDDDDD ]DDDDDDï%i…¥r¹O~~ŽD"Õ×7öôÍa""""""z=ÂSqË7&æl\\DñFuê×oë￈#õ!à;]DDDDDo7tÉå>aa~‘Špì{@ “Lœ\=8X ]DDDDDôªÊ½Ós.2þþ€;àø!Ë6=<|¿\îó¨–Ô¼ãðµAQJQ¡#/íÚ¹¼ûDz š–Z VŠŠÐÁ5¤¯Ö'Ã.ÿw])^ô²ÕF…²-ë4–ŸTŠ q¬ößKŸÖý¯ýýú)…RŒûë¿•ýšó…7"""""ú»JóÐĉ‚ƒ7¡+0x`쀯‘:ðVbbtqvƒQ;öŒµ)Jøm㦫Yµí;öêig¾8$±ðß¹•òÄ÷tãû—SU£™å­[¾ë¨£Š=ñÓΔÊÿëÛgÄW{›}ÒwGL>"""""z}¥“9ùùÙi°/“¸Šu€Ê 0#ã>@0jÔÆ@RøÞ›WoøjÈPçí1d–ÝÝ6 ŠQŠŠä#Á›?«]ÜŽ©íľ¦ã.­d«/Gœ~<§IA× hh @е:ïàuQ¡<²Â®øPí†nÇ•¢"¨·¹¤ÜŸËLšMX<Å}rScÉ£°è ¾öÝ({FÁ Éø%Á1¢ByôäéÅSÓhz͆M糖‡¿»}:÷ÛïÖNë7u{ ´ššî`øì>Tp¯T™чº$©T*ý§ª¤B(¤ÒℚŒ«'£X Ú,†ÄÄøŽïbð K ÝfãwmíTçnÀÒ•ó—†ÜùØÆ¬xÍ¢a͇Á粄j­gÌs0 ÛdÜÎm£ì[\{»É}ÓœååV˨‰ëfÞõc¾÷þéLúߺ&£¬“Þy|FýV“~Xìh£}c÷ ¿ O UŶiU@}é‹éš¬Ø3Y€~ã6Õžµh±Â«½‰ÊDDDDDô_Rº¼ÐÐÐÔÒ²Þx\l•ªíÐ/2±°¨S\P³kx¯tùÔ~½:5úÈÐÒîÓQv]­ Ú.Lèݵ&psí"÷õñ…€Ÿ·DVË¢ž;cwJý,û?F|\¹ª¡–¬š³c-ݧní^r’ZmjÖ“u©ÝÕÞ† IDATÜö^<m¬v—ÖÓ[¾þ5Åö\³;ÅÆ Ÿóè•«jÉÌÛš·Ö-^è¯Ý¥õ´AO4.H¤𢢢GEÅ’h=k>Jf]ÁUXêy•ʈ/äHDDDDôá„.OÏ1.. cŸÏœ’†Ù@Gà° ð†½™™@bÔ ÅÇ ¿-s=´ ZÆûûÍhkX¿¥¥ì¶–€Z¥.i®üFxš¼ì|ï•(Z §ß™¢*ùõ¡Á(@¢ ú(Ózõµxš‚¼B”ÛQU .ª¸®*-26µšvjb$žÉÔ@·Fg{C ÿÆ…»…÷¡Â«HÍ6iü •ùô}P¡ €¿ÿ¢~ýf†‡ïOœ Ò/T‚¾ÚÄÆÆÞÁaÀš5#‹C—içéÜ ÏÿóFºPá‘6pïÜñ[Ù7OœìRçËî…#Õ5»öÖÝþMREg,¸–4yp ׉cÒöþyGÛºƒS§‡&…^˘jYoÌ衉¢àT[ÈTwh4@î*$7êôÑk^£*ùÏè¬i–u¾˜û­n„¦u³âÆËÊ>·sKt÷i }ý4›%Wv<ª»ÛëD†ªr…}¨ø*Fnz…ÊÃFæò$""""ú B€À@¹|crrlñÇ‘ÍÌjš™Y=J\Š\Úw¶ïÿ>ÜZ ¨Æü±kÞÂÏdh4}†¹.šÛoˆû roñ¹§ªø­¥œˆÃƸÏv4ÿ /%êàÒû÷O¯›¹ùãUã?ùv]ó³JzU¤<¾Ò»÷zyãa Fž»Ÿè¿Î5j2NyÎØTuÙ¸æC§}|í²€*_]î‹Tù±ë?ÿ"éÔ±Ÿ|úe+¸}Äwî×;¯æÏèC…W‘ÿ*•¹¶ˆˆˆˆèCðaì¡§mÕú'Õ™¶á:°‘ú¸Û€qÓ+Xm(˜:ï9àÙA¸±ÝsSø½”³§Î$}@áˆG&""""bèz-:6Ó<'·ª$ŠÜ<¾Õë›ÕÞ­ø»^‚^ƒþ+|&9×7²4.èÁ‡Dºˆˆˆˆˆºˆ¡‹ˆˆˆˆè}"á1t1tCCC1t1t1tCCC1t½›¤Oi4ŽË{DÑûºøÿñï&d""""¢w—1t1tCC×s¶ö‰JñôÂú:¼DDDDDô‡.©¥k¨¨P–ýçèÉߌ´3ú粚ÔbÄ¢B)^ô²Õ/.Ñ>Pfeg=HÍPqç>"""""zÇI_²žºP™T zÙ÷ùö—J©mæüv¯èßéröÅ9íºÏá­#""""¢÷ÁËMY]\Ú¾V'KǾkn€~“ŽÖÚ SÝiÖ²ƒ—ÃJQ‘t$dÇÄnË@¯é²k¢B)†ÿ8}Õý)EE¬ßúñ ô­û.ZuJ¡Š}{7 s0Ó*{ÔÑù½Æym9¯c‚ÍbÀè­M²jg-šÑBd5ÇîðšÕ»¶  Î†eë¼çDDDDDôÎ…®³Ž\ùíÊÍ#~ã-Än^ûËM•NƒÏ¿îc Ä®žºÚË{û²¹þ j÷ê_f{‹¸ã?ùtr¿ÿ}¹/@•ÞÃë›6ú|Zg] 'dÜஎC;uÛv@­A3*=îJι}î«æÍ^êy*éôÒå?Þ€‡¿¯?xÚ¸yawÔ÷Qùãd[›ž¶í¼®@•–¶•µ=[—ñ¶ èê²QÍônÓÕ7Ž÷œˆˆˆˆˆþA/ùN—N¥*%Q*ý—/-<ÿP#1ob÷1Ôøó¬Òš&V¦¥mjÔ  ÉŠ;­@ÿ–0¶¶ü¨Q Püž^äÆ>:º–™VÖ–ÚÁ%G%‡ìöÝ\2¿©ÉK^IvJZNTwÿŠÉD#Hu¥$&õêW€ë;öÄåh Ópë """""zC×Å¥o|~è§/Ât |¸Ï¥ Q…iñÔÔÍ-³¶^Îy³ ’"óž<\êiÔ*uIê)~ÞlÒ¨ *˜ Sp§C"""""ú7¼ì;]š¼ØÍS~ü €`3sã¨&úE¢®Ý€šèÇ üýà¾ßº¤gi¦-]?ë[[´ëÉLmžuf×–¥ßÜ «¯-áÍ"""""¢÷ô‰¿zÈå““c‹?ŽlfVÓÌÌêUäDl6¶À}¶ó ù߸y)Q—ÞW=£ræéu37¼jü'ß®k~öBqÔ ÷Í]Çy¸êªÔ+¿/›¿ér.o½=Æ#½q\´GDDDDDÄÐEDDDDDô~’VX*—ûäççH$R}}cOÏÑ&"""""¢×#<·|cbÎÆÅEo¤aaQ§~ý¶þþ‹8R¾ÓEDDDDôvC—\îæ©8GÀ¸„À0ÉÔÉÉ50ЃƒÅÐEDDDDD¯ªÜ;]11ç"ãO`à¸^€²lÓÃÃ÷Ëå>ekêÚN•¢B)†Î´–½â)uËO*E…8ÖFRë¡ÁJQ:¸FÅë+®ð¬ʖø£ŸRT\že«Ç»LDDDDDÿšÒ(3qâ†ààMè Ì´•Ú_#uà­ÄÄè2Gé5êd¨5¸gÝu£ ^á”*å‰5îéÆ÷/§ª^³ÓÏjáï·LDDDDDôf•Îtåçggd¤Á¾Lâ*Ö*ƒÂŒŒû¥%† \úTFÞõ£ ÀªÇg u ©Ôs×q…2tScBeçµ7”âÅõ­Œ*µYq,èºRT(EÅÝÛ絯&…`ÒlÂâ)î“›—ß:Q¨¨r‰*öÓwýzC)þun娦z’gµð¸\ÛjÂÁïÛ0°W!FoïñÙO¡ eèæž&Ó^«¢”âÅõöFüJýc¡K"‘J¥2Ü{ªJ*„BA*-]EhÔ²_wSäݲzS4`î<ÄF¯èá™-§³¡ÛÞ¥™‰ÄÄÞÅV†´CÛ#³ŠòŽÿ²dÒ\ùTŸ «Ž_Ìb«û̾<§r%³¢‹<œ,ý¸Ã¼ÕÎ5´^tYêÔ …[cd…/7küªð?6ÝήmªJ+µe¯‹;›®dò &"""""úÇB—¡¡©¥e=„—ʦ`;ô‹L,,ê””F­Fv0FᥣÊì(10sîÛÔ@ó0ü×#éÐëÔ§eÍfƒÚi#ñ·Ÿ¯äjrn…+ [» Ÿ6½Ÿƒ# £g¦çUŽ òZµmåL¯“…@ýÖÖ/|MK“w;âz€œ¤s¡â©«ž Ø—i‡Ýš¶Ú^†è¿è|Þ~"""""úçB—§ç˜úõÛ%WÁà\¾¼accoffU’¹Lí†wÕdíVï8~`l]•;ii€¬(¿À4è·7É¥.âvÅäk}ä²ÂåGÝË;Wxm8•÷Üž¼¸² ‘h €Z¥~ª¼ÿ šL[ææ UŸõ I(äÝ'""""¢0tð÷_äè8ºz\]é`mÔ…ÐF0XSɶf7‡kÖŒ,®_¹cÿºÈ=í;mÌœ‰cæÎòV†N#›™yQ»~K„n›¶º¸¾sßíBHŒ­ªjññ ™úVÕuŸß“çU6oÞë³O§üß´vR䜎Îy‰++ʘ ˜·èãÜΡ±‰ W3ÛjÈ:¹)øžš7ŸˆˆˆˆˆþáÐ 0Уÿ™}zNql1º»Ãøþýgwé2bãÆIªWî<ª• ¹'}ö:r"äHØþ-—ƒ®ýÛTòÿ Ú êð_ƒ“Ô@á_Û6ü™ká2Ï{‘c5Õó·|neãÖ̙޻JrØ·a÷^f¦+;Ò×ëüµµ«çªµnµuuâñíáER÷úÿù€¯sÑ?áCÚ¿O·ÁüÓ[\-o{vîy‹ +À#½q’'^Vj?üsKàâî_c™¸ˆˆˆˆˆèŸŠ"zŒ3]DDDDDoœ´ÂR¹Ü'??G"‘êë{zŽæ0½᩸ås6.."33M*Õ¶°¨S¿~[ÿE©gºˆˆˆˆˆÞnè’Ë}ÂÂü"'àØ÷€&™:9¹zp°ºˆˆˆˆˆèU•ÛH#&æ\dü ,üwÀ ðC–mzxø~¹Ü§lM]Ûé¢RT(ÅЙÖ2€Ôzh°RT„®!}¹3wôSŠŠË³lõžú©R—ÝJQ‘¸¶«1HÌ?Ù«ñ+:B§±ü¤RTˆcm´_±M"""""¢7tMœ¸!6ö<º3ƒG¥vÀ×H½+11ºÌQz†:Yj îYWûŸë®JybûºÅgRU¼wDDDDDô^…®üü쌌4ØO„¨PfdÜ/-1làÒ§2ò®UV=>k¨óT³2Ë~<§IA× hh ”L…E,Y¼Õïš2ìRÔ÷m˜Ø«£·µ1|¹î &Í&,žâ>¹©±Ðù¨Ï"Ïc‰ ¥¨ˆÝ÷Ëâ>3•oSбîÿ害¡ ¥¨ˆßç¿´‡µ®Á Éø%Á1¢Byôäé…R¼èek\¶o·wÉ[uXq,èºRT(EÅÝÛ絯&}4•í½ùÐÁ¥xýÌòIŸ]t8N)^ØïÖÊX‡‰ˆˆˆˆˆžº$©T*ý§ª¤B(¤RÙ㣖ýº›"çè–Õ›¢sç!6O¬æÓm2nç¶QvŠ-®½Ýä¾éÎòr«Uåª;ö³Õ¾u~ûœŸbd…/7k¼Çܧ»¦Õú‡¢B)Æ^žo[QÏ-}ëåf_ù¼Ï#¾þòûãɲ¬…[˶©i1q‡÷ ýsÓ–®>¡ÓÚuáOsVn5é‡ÅŽ6Ú7v¯ðñ»Q®Éâ¾Ý¸•š›“pü—%“æÊ§úåXuübî[Ý’:†ŽíÙ.æh×ê<{A«Aûâ`ÚzÔWÝL™ºˆˆˆˆˆèi¥/`šZZÖ»KÀ㔣¶C¿ÈÄ¢NI‰`ÔjdcŠG•Ù±b,ÖuîÛtɵˆÒ6eÖÎŽµtŸºµ{IQ­6–:€hÏ>Ýw'¨ãŽöÿ›œ¤s¡â¥Ü ûv/xó‘ø|u?ÓÖô©Ÿ ³³ £.C¦ë_½q.`ƒx5C†Çmj7š×Ɉ۴ÁwO‚aÖ¯»[힎‹Úš·Ö-^è¯Ý¥õ´A–¥->î›´RKEû‘.ÃëYU17`da¤…4@ìáuž»ï_l6¼]{UøÏžë/škœÌ·6©n AZ("""""zVèòôãâ²0öðùÌ)i˜ tî»oØØØ›™Y•d.S»á]õ´[½ãxqQåÎCZ®¸ý¸%AÐ’HØ8}üΔâw¯4y©Ù:5_­kjÅ/«¼C3 1ÿ¤M¡«(õ€û ™ËçV-ì: hÓa@o3ç>WŸnFS’„Êm̧*P?; i}ä²Âe³üp¿å+®é sŸßYëÉ6Õ*õãnª4àG¦‰ˆˆˆˆèÊ­‰ó÷_äè8ºz\]é`mÔ…ÐF0XSɶf7‡kÖŒ,®_¹cÿºÈ=í;mÌœ‰cæÎòV†N#›™”ÆŽ‚›AaI@ ׉cz5kj×êÓ©s<&Ô~òů¢ü‡¹€y‹>Îí›h½rÏeõä ¦6Ï:³kËÒonÐÕ×FÙ6õŽœJêN˜ê³ÈóØ_¢B)*b÷ý²¸ÏÌCåÛt¬û¹ël¨B)*â÷ù/ía­[¾‰‡OŸT(C7÷4 ˜öZ¥/®·7➎DDDDDôÆC—D"•Je¸÷T•T…‚T*{\`Ô²_wSäݲzS4`î<Äæ‰Õ|ºMÆíÜ6ÊN±Åµ·›Ü7½ñÀY^nµJ¢\uÇ~¶Úw£ÎoŸóS €¬ðåãf÷¸‘ût×´ZÿpCT(ÅØËóm+ê¹å o½Üì+Ÿ÷ùbÄ×_~ŸÁ îçcÚš>õsavv`ÔeÈtý«W"Îl¯f¨Óð¸MíFó:Yq›6øîI"Ìúuw«Ý³S-¿ŒrÝÐmôñWÝÆtØ­©Vïö2DøEçó± """"¢7º<=Ǹ¸,Œ=|>sJf;À.À66öffV%™ËÔnxW=íVï8^\T¹ó–k#n?nI´$6N¿3¥øÝ+M^j¶NÍWëšZñË*ïÐ HÌ?iSAè*J=à>Hæ2¹U »ÚtÐÛ̹ÏÕ§›Ñ•ü»â髼ÿ cf7™¶ÌÌLª>ë’Pȧ‚ˆˆˆˆˆÞ˜r»úû/rt]=®®t°6êBh#¬©d[³›ƒÃ€5kFׯܱ]äžö6fÎÄ1sgy+C§‘ÍLJ߃*¸–Ôp8¦W³¦v­>:ÇcBí'_ü*ʘ ˜·èãÜΡ±‰Ö+÷\VO¾`jó¬3»¶,ý6à]}m”mS/áÈ© î„ɇ8OZ0¸|òfÁí*®f¶ÕurSð=5 """""zsžÜß=0ÐC.ߘœ[üqd3³šffV ©ÜyT+rùì=ôG¦кj:BþUó®ýÛx_~ÜHNÄÆac Üg;šÿ —upéý'·ÌŽôõ:o;«•«ç*ç=“»L»˜ój=/ÊH¸oî:ÎÃUP¥^ù}ÙüM—ï©Ëµ9ÝgÔtÙ²9½g¬îˆ‚»çZ2wyT®Eó'R'ß>}UIê^ÿ?ðu."""""z“¸O Û`þé-®–·=» ÷¼þA/.äÇ‘‰ˆˆˆˆÞ8 cg¥öÃ?·.îþ5–¯sÑ›ŽzŒ3]DDDDDoœ´ÂR¹Ü'??G"‘êë{zŽæ0½᩸ås6..¢x# ‹:õë·õ÷_Ä‘úp¦‹ˆˆˆˆèí†.¹Ü',Ì/RqŽ€=pa’©““k` ‹¡‹ˆˆˆˆˆ^U¹4bbÎEÆŸÀÀp¼?dÙ¦‡‡ï—Ë}ÊÖÔµ.*E…R i-H­‡+EEèàÒ—;³qG?¥¨¸<ËVïéߤ懯 ŒRŠ ¥yi×ÎåÝ?–ñfÑûº&NÜ{]™€Á£R;àk¤Þ¿•˜]æ(½FC,µ÷¬«ý†û¤Ý`ÔŽ=òO›çÛ¸iåÿí:­Õ¼§¹”7‹ˆˆˆˆˆÞçЕŸŸ‘‘{à‰Õ*ƒÂŒŒû¥%† \úTFÞõ£ ÀªÇg užjVfÙcÂ'C┢")èຠ„’©°ˆ%‹·ú]S†]Šú¾-ó{bô¶6†¥Ç FÚØH ß»÷h{æÚIDAT#`óê _ êà¼=¦Ú ÝŽ+EEPosI¹?—´í½ùÐÁ¥xýÌòIŸ]t8N)^ØïÖÊXò B¥6+Ž]WŠ ¥¨¸±{û¼öÕ¤(×áÄC§O*”¡›{šL{­ŠRŠ×Ûq÷G"""""zÉÐ%‘H¥Rî=U%B¡ •–.ï3jÙ¯»)rŽnY½)0wbóÄ AÝ&ãvne§ØâÚÛMî›Þxà,/·Z%Q®ºc?[í»Qç·Ïù)@Vøòq³Æ{ÜÈ-=Z“qõd«A›Å˜˜€ßñ] d©_t)†ŽíÙ.æh×ê<{A«Aûâ`ÚzÔWÝL%/¬P”—pü—%“æÊ§úåXuübî[]”íðõ+ûׇ>€ng×6U¥•Ú²×Å€MW2ù=Wé¢=CCSKËzwBâq °}Tª¶C¿ÈÄ¢NI‰`ÔjdcŠG•Ù±b,ÖuîÛtɵˆÒ6eÖÎŽµtŸºµ{IQ­6–:€hÏ>Ýw'¨ãŽöÿ›œ¤s¡â¥Ür}*ŒÙ5¼Wº|j¿^}dhi÷é(»®Öm¿9öüK‰=¼Îs÷ý‹Í†·k¯ ÿÙsýEsÓ€ùÖ&Õ ´^TAȹ®è4Òex=«*æfŒ,Œ´V¾Ãº>þªÛ˜»5ÕêÝ^†è¿è|>@DDDDDô|¥3]žžcê×ok”\S€@:pøð†½™™UIæ2µÞUµ[½ãø±uTî<¤¥A™6AK aãôí]Û»8¶wé6#"û¥»dÔ ÅÇ ¿-s×±nG[§Õ†õ[Z>ʇR™Ö³õiÔªÇSbj•OmŠ_Q­\Vø¯â¨{yç ¯ §ò*n:/ÆG„&Ó–¹9HÕg}C ùüÑ ”ÛžÂßQ¿~3ÃÃ÷§NPé*A_mbccïà0`Íš‘ʼn¨rÇþt‘{ÚwÎñù íǯ’×vÙÌdÙãf n…%M\Ãu☴½ÞѶîàÔéá†ÑÛÊŸ¹(ÿa.`Þ¢s;½Èȳ‘e!‰içéÜ ÏÿóFºPá‘6pïÜñ[…j£»4 w’uúèÍ%Oc«ªZ@F||B¦~×êº@…qªPpuöŒ¦¶Õu|Sð=5"""""z¥Ð 0ÐC.ߘœ[üqd3³šffV ©ÜyT+rùì=ôG¦кj:BþUó®ýÛx_~ÜHNÄÆac Üg;šÿ —upé}ÕgÊŽôõ:o;«•«ç*ç=“»L»˜ó(=¸´ïlßÿ}:¸µP?Œùc×¼…?žÉÐh²Ž¯ôî½^ÞxØ‚‘çÃîçúof c¶møÙqþ—yÞvgNªTϪ§N<¾=|úª6’Ô½þ>àë\DDDDDôbÜ}ïUè6˜z‹«åmÏ.Ã=¯ÿòãÈDDDDDoœ„CðòµRûáŸ[wÿË×¹ˆˆˆˆˆè傇€ãLÑ'­°T.÷ÉÏÏ‘H¤úúÆžž£9LDDDDDD¯Gx*nùÆÄœ‹‹(ÞHâNýúmýýq¤>œé"""""z»¡K.÷ ó‹Tœ€#`ÜB`˜dêääèÁÁbè"""""¢WUn#˜˜s‘ñ'°ðÜ/ÀY¶éááûårŸ’J•ºìVŠ ¥åÿIu) ³š&*”›?3ÆžÆý”¢âò,[=Ði,?©âXm:6£¾û=VT(Åk;ÚU-÷ÓËy^ãDDDDDDÿ¾Òwº&Nܼ ]™ÀãÐb|Ô·£Ÿ8R·óô=þœòjçS)O¬qO7¾9U4–ÓµŽ^|ÀªÃ×¢nf+3Jz-å'"""""z”NOåçggd¤Á¾Lâ*Ö*ƒÂŒŒûåK ÞßË;˜–™àt¬û¹ël¨B)*â÷ù/íamTcÂÁïÛ0°W!FokclÒlÂâ)î“›k[MZß×Xœ5Î!çAáãŸ$d–ÝÝ6 ŠQŠŠä#Á›?«S­ÍŠcAו¢B)*nìÞ>¯}5]«g6.©¨3º‚Ôzh°RTDoð=°ï†RŒ»´v’­¾@ç£>‹<ý%*”¢"vß/îÍ ¹­#½ÙÐ%‘H¥Rî=U%B¡ •ÊÊ&Z¾7U>Y>»Ùã|¢×bâïAúç<¦-]}B§µëŸfW ]¸5@Vøòq³Æ{ÜÈyÜ‚:5Èý‡Hy+ÇÏYw%¯lóºÍÆïÚ>Ú©ÎÝ€¥+ç/ ¹ó±™$/áø/K&Í•Oõ ʱêøÅÜ)M3ƒžÕx…™ÛP¯ø7£¬“Þªµž1ÏÁTXúÖË;òyŸ/F|ýå÷Ç“uõe|4ˆˆˆˆˆèM(]^hhhjiYïNH<.¶£°úE&uÊW”qf…GH·ïºŸ5¼¨¸HÛºW'K nÓß= B„Y¿înµ{´–zE¦69IçBÅK¹Z?jA“w;":µÈK¹z6*ÒJ[—Y÷îZ¸¹v‘ûúøBÀÏ["h*:t^ÏªŠ¹# ÃÂÛ®WÜx…éÙ©–_Äö\³;ÅÆ Ÿóè•«j!7;»0ê2dºþÕ+ç¼/¤sG """""zJgº<=ÇÔ¯ßÖ(¹ ¦€tà:ð-à {33«'ŽTÝ ûnÉÅBÈLÊï ¡)É`g#y3/ÿa.`Þ¢s;‡Æ&Z/Õ«‚›‡'u¾\à>éS·)›öÝ­AU- #>>!SߪºnIÅ¢g5þŒÎTx:Y=ù‚©Í³ÎìÚ²ôÛ€[tõµ%|6ˆˆˆˆˆè >ñ÷À@¹|crrlñÇ‘ÍÌjš™YU”¸Š£MüO³w¹ «ȉð5]¶lNï«;¢àîùŸ–Ì]•›§ïëuÞvV+WÏUÎ{&w_ûRÝʹè3̵pÑÜ~CÜçH{óˆÏªÄŸÛÎâ2ÏÛîÌIÕ£Ý ³#ŸÕxűh^ÑÙŠ2î›»ŽópÕT©W~_6Óå\>DDDDDôp“>*Å#½q\EGDDDDDÄÐEDDDDDô~’VX*—ûäççH$R}}cOÏÑ&"""""¢×#<·|cbÎÆÅEo¤aaQ§~ý¶þþ‹8R¾ÓEDDDDôvC—\îæ©8GÀ¸„À0ÉÔÉÉ50ЃƒÅÐEDDDDD¯ªÜ;]11ç"ãO`à¸^€²lÓÃÃ÷Ëå>%•*uÙ­eþ¹öƒ½ÁßïˆqG?¥¨¸<ËV著ŠÏ˜¸¶«1ï½gJßéš8qCpð&tfÚJ퀯‘:ðVbbtùïo>Ÿ (ëjR’ˆˆˆˆˆ¨"¥3]ùùÙi°/“¸Šu€Ê 0#ã~¹Bµâ—UÞ+—z¯\êã}(Çq×q…2tScBeçµ7”âÅõ­ !³ì1áÇ“!qJQ‘tpÝ€†‚Ôzh°RTDoð=°ï†RŒ»´v’­¾ ³špðû¶ÌìUˆÑÛÚ¾°ã‚Žuÿ/w U(EEü>ÿ¥=¬uMÆ/ ŽÊ£'O‡(”âE/[}Þd"""""zB—D"•Je¸÷T•T…‚T*+W¨Õú‡%Ë ý{Ùr:ºí]š™HLì]leH;´=RÕdÜÎm£ì[\{»É}ÓœååV«$Ðe ØxðŽP­õŒy¦êÔ …[cd…/7k¼ÇÜõ[¯ÅÄÞƒôÏyL[ºú„Nk×…?ÍmX¹Õ¤;Úhߨ½ÂÇïBï.ýëJ—šZZÖ»K€í£R5°úE&uÊøxya¾â¯ü‡Ê_¤wù¬SŸ–5%ƒÚi#ñ·Ÿ¯¨¬g8ÖÐ}êÖî%ÇÔjc©ˆ=ì¹fwŠA?çÑ5*W5”ä%D\Olr’Î…Š—^¹ mÝ«“%·iƒïž!¬_w·Ú=;µ5n­[¼Ð3^»Këiƒ,yƒ‰ˆˆˆˆè] ]žžc\\Æ>Ÿ9% ³ŽÀ`à {33«rÇ©¿¬ò-LŠò LûlLëq“Œ›ë"ngPL¾`£%°qúø)*€&/5[§æãc4y…xz×úW )*ùwÙM÷Tê"ÞX"""""z×BÿEýúÍ ߟ:8A¥_ ¨}µ‰½ƒÃ€5kF–;N«ö YòV%iØ| 9j×o‰c†·a \ß¹ïv!Ô7ƒÂ’&®á:qLÚÞ?ïh[wpêôpÃèmÏèHQþÃ\À¼Eçvz‘‘g#ªŸ¨P挙—ø9•òÅ º&OL;%é9¸*†™gM´¬óÅÜou#4­›i¹¼ÃDDDDDôî„.rùÆääØâ#›™Õ43³z2q@Õã‡ÿ)çð…í’³ÿ Ú;|f]¨Ã NRȉØ8llûlçAó¿qòR¢.½¯zVG²#}½ÎÛÎjåê¹ÊyÏä.Ó.æ<ûŒYg·Ë}FM—-›Ó{ÆêŽ(¸{þ§%s—GÝ)ôœ±©ê²q͇NûøÚe5U¾š_ž"""""¢‘ðŸºm«þÓ?©v/%½È´å×ÔÇÝŒ;˜ÎÕ†/‰G&""""zã¤ÿ­©[£cßÉ­*I¢7C×x}óý«1…C@q¦‹ˆˆˆˆè“pˆˆˆˆˆˆºˆˆˆˆˆˆºˆˆˆˆˆˆˆ¡‹ˆˆˆˆˆˆ¡‹ˆˆˆˆˆˆ¡‹ˆˆˆˆˆˆºˆˆˆˆˆˆºˆˆˆˆˆˆºˆˆˆˆˆˆˆ¡‹ˆˆˆˆˆˆ¡‹ˆˆˆˆˆˆ¡‹ˆˆˆˆˆˆºˆˆˆˆˆˆÞMÒ§‹4 Çå="ˆˆˆˆè} ]üÿø÷2Ñ;ŽË ‰ˆˆˆˆˆºˆˆˆˆˆˆºˆˆˆˆˆˆˆ¡‹ˆˆˆˆˆè¿º$Æ ÆøìˆPŠ ¥xý÷Ñõ*·ö‰JñôÂú:ÿîx¾3=!""""¢$tiÔz¼Çºß£N(”¢ByT Yé>º…™ìïœTVòw úÖ©  7»Ð>Pfeg=HÍPý£›ñI-Fü!*”âE/[ýâ’«'DDDDDô%}þÏz ‡n=üEk½Ò‹¦F/7 šðëÝ¢×<§`XÏÁ@Α¯;ºžL×ÀœvÝç¼ ã‘}ñ]é ý'^þw`d5-”® RURæ¼Gç÷çµå¼òÔ‘Õ´´ªöø¿MǯW(E…RŒ¹ñËîÕmM$Tx`ï¢Æ`2ÈûšRT„ ¯eüdO^版ˆˆˆˆ^&tÉjwÒîÿ<º÷Ù[™jufÒ™­+ttÿ=½²êŸoùq[Ûº¦¹ Q7ÓôªÙ˜¼ëÀd;ÃÒ+[ =¢µ‰@V­ã¬E3ZèBSôçYE! +öÏ'Ï%>¹OVsì¯Y½k›êlX¶n`ðŠWe=yÁ7ƒT.¾:‰AݶMjÈR¯»r%.CËø#û!3¶ßÚIáçŠóヿΜ8zêvî³w¯wDDDDDD/ºt>jX ¯ý•U&©¦fB·ÑçÓ:ë9!ãwuÚ©Û¶›j šéTéq£Ê'ÛÚô´mçuª´´­¬¥É<óÝŸkzƒÛà¯WžÍ~â¼z¶.ãm%@ÑÕe£š7èݦ«oÜ+^Uι}î«æÍ^êy:£H•òËçF }Ú÷‹¡}Ý&f0jÕòcY晥˼_;~ð´qóÂî¨Ëµóš×ÈÇŠˆˆˆˆˆyî;]‚ <;­7hbŠßÂÓ‹€Ü¸Ó§SG×2ÓªÓÚR;¸¤RvJZNTwÿŠÉD#Hu¥ÂKä@“zõ«Àõ{âr4i^yK‹äݾ›K&±™výþ«}?íÚ¬ªöãZ2É‹»òö®‘ˆˆˆˆˆ>Ï›é*Pþ¥Y³Oê•YO-ã*Æ¥a­L"zV8Ò¨ Ô¯Ñ7uÁ³w”h½l´Ñmê¶s›kÏfUï„ìþ¿¹Ë—í¿ÿê=y{×HDDDDDrèŠ Ù •‡,Y=¡åÇ-ýjv._î[âTׯ¥@^mM%€^ÝöíͨbÓ þV—Š2nÞ΀ºý»U×$†–æ¥ïtiTy`ÑÄRÐ205zÁb>­ªö­k(NñܸíÈ En™¤¤Q«@¯Š±¶PQOÞÖ5чâ¹Ë nþ8õG§c›HM®sZøø‡ÈŸ¼¨=^'ú-ë¤ß}s@Ø»ºõkT Ø³úèƒ"|üwú”{%ððÝn.æz×\÷( óO_/ú´µ¤òÐõ§þwW°47zAcê‡q73ac¤ÝÎcÇùfm:TæÇ EL&šiw]x82þÊž9ߥ•=øí]#} žÿqdMöÅ-ƒ;Ï]»ïÚíÌâuu9IÇ|¿\{ì~ •»]]gn>«x¨ýQýUòî\üuýо#²þîW…5™—¾²2àÊ €ì›ÇÎ¥€:_¥úîÁ™ß^Ï`¬÷0lÿÕ ZË<å9ÅëBЦÍmmÔg}üRÊæ»óËWì8Ÿ¦†¬F Ûfµ Ÿ\´øÖ®‘ˆˆˆˆˆ>,ªÓh4ÏÛA㉂•ljêܾy'OA§î¸õG7– òë~I)â={{7K£a˜$""""zäïd¯ôl¿Ý¾¥«ð0%5W×Ü¢’@fЮcw™¸ˆˆˆˆˆè=#y'{¥NŠJÎИXXXT’¨Òâû.85ì3½oÞÍå…ô²¸¼ˆˆˆˆè'á½=¿ÓÅ"""""¢7‚ˉa›ˆˆˆˆè-âòB""""""†."""""¢÷SÅïtÉå>ùù9‰T_ߨÓs4‡‰ˆˆˆˆˆèõOÅ-ߘ˜³qq™™iR©¶…EúõÛúû/âH}øNÑÛ ]r¹OX˜_¤â{àÃ$S''×À@C½ªrïtÅÄœ‹Œ?€?àx~ȲMß/—û€¤ê€}§ÊÓÃ̵:M§œVŠŠ S[¶’ŸTŠ q¬ö›È‚†µ,^}J¡ñm›Ô­ºÖ+¶¡c3ê»ßcE…R¼¶£]ÕÆo²{DDDDDD/©ô®‰7oBW`&ð8™Ø_#uà­ÄÄh(ºâ§UÛV-\ÚšùHQkÛ t¬$þ¥Ð]ãžn|ÿrªêowJöѰí[·ÓFzÔámWTmÞ»Oß^¾Û~Wæ¾B#åßt­£°ê𵨛ÙÊŒ7Ö="""""¢—V:Ó•ŸŸ‘‘û2‰«X¨ 32îŠÒNì=•¡eŸÎæèÖîçlÜþõ×[…&Í&,žâ>¹©±€Ì²Ç„O†Ä)EERÐÁuvÞ|T¡ ÛÖËDTé¿ï”Bù»g'h×™ñ§¨PnöQiOômGLo§œ3Ó»ºM™ë¹`üØÜ¹UAǺÿ—»Î†*”¢"~ŸÿÒÖº‚Ôzh°RTDoð=°ï†RŒ»´v’­¾ ³š´¾¯1 ±8kœC΃rÝ šŒ_#*”GOžQ(Å‹^¶ú|ˆˆˆˆˆè­†.‰D*•Êpï©*© ©TVü·ÿoï^c³ªïŽÿ(H +Tj'—rÍD`”ÊEnTÇÔtD"X·©£C® ÙØæƒ[–`SÍJ!îVcºeäb'ˆƒXÃ¥V21Ad±t‚v¥ôi÷¢L[æáŸÏ»ç<Ï99ç<çÍ7ÿsþ§åÔëk¶5Dbè´I=Ó†L¾õꈃ/¬?|®í]®›ùÌoîÍ9ü«ÂÛfÍ[U7ôŽï=1+cÿŸªÏF§a7õMMqû¨]oÈïß­ÇЉ}#öUl?Þü¿µ;ö9ôʈ¦]å¯Ô¶.l©?´³²¦)u䜧K¦KÛµâåÿõ²Ñ…•-ùbjëJ]/ÿ`ûW®?žè5zÑÆ¥'Ol|ð©7#¢¡êç÷-~roCÛÝKËûÔ²¼Aÿþ‡Ÿ•®ÙsÚE\ŠèêÖ-=+kpTDT·ù>ñÛHkîž™9àü’–Ó;Ë*?ŒÄÈ‚'LŸ”q`ÍËGÛ5W§ìü¼~Ý¿ºà×V—ÌÍŽˆ~c²ÎîÙ\Õéc¯¿vìäQ)G+ª{|ùÆa9ãG^Wyìã»þ‰‰ˆˆ–h?«Cçì[&fEZý‹UÏnXõ資#¾ðµ‰ýZÇåÞÚP\ô»’¢MG#:^Ù³[‡†Ví?q.¢¡vÏÖûêšÛî^Ÿ¼±WE¼óä²‡ŠŸ{úÏo7º €K]ÅÅ3† {yM¸?b]D]Ĉ‡#JbРë32zÇGÕµ«|ËéHäÎyä[~¿ñXû礉Ž"âèÊ…“Çä/È_pó¢ª3§ªŸ¯Žè?éîY¹©Ö­*ÝÓxMÞŒ{†w‰šÍ©iSmM'ö|?"eô”¯d¶î^"5{ÔØ¬óŸµœ¨O˜h¯¥±á\üÿ4øŸ¬©1Ùìÿ.YtEÄÚµäå}ûêCSîì#1&ѵèŠ/õ½yܸ©EE÷|ü»3o®Ýü~DêU£ewùK5ÉöÛl<²ñ•w#úΙqËða9¹__°xÅìþ—%OU–ŒÈÎúΦ×öWV¼žìuÓÄîQ»ãÅöw'Öï.+©JFÚ E[J‹—/XVºzû«Ë§÷m9²yGmÄÀÙóçÜ•?÷'wˆ¨yqû‘O=PÕTóÚþ"|wÉÃ?¸ï‡ó‡›Î¸xR.ø\^¾bÞ¼•55oµ¾9#£oFFïvÅQ¿÷™m'§OéÍ»Ê^=ž¼p£õU+¿ùÆ¿Ÿ?íÇK "j÷­_~ê\$½üÒ?bð 8±µâ݆“>ÿFŒÉ‰“[¶j¸°Ú~y×Ì3KïŸ=5'¿ðºhxoï Ï­{»±þxé½ ;=¶ø¶EOˆÆ÷v—=ºä§ûþ“9âSrËéÅ‹V÷|læˆos)ŸËÒìÒg”ù¹W¤D4ÿûÈÖ¢'–nR\ÀÅé§€é€Ï\§@tˆ.D€è]ˆ.Ñ º]¢ @t ºD€è@tˆ.Ñ€è]ˆ.Ñ º]¢ @t ºD€è@tˆ.Ñ€è]¢ Ñ º]¢ @t ºD€è@tˆ.Ñ€è]¢ Ñ ºD¢ @t ºD€è@tˆ.Ñ€è]¢ Ñ ºD¢ @tˆ..Žÿ8hyCêvâIEND®B`‚apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/hostgroups.cfg0100644 0000000 0000000 00000001654 15051152474 032765 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/services.cfg0100644 0000000 0000000 00000004100 15051152474 032360 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/nagios/zookeeper.cfg0100644 0000000 0000000 00000002540 15051152474 032546 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-monitoring/test.py0100755 0000000 0000000 00000020003 15051152474 030130 0ustar00rootroot0000000 0000000 #! /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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/NOTICE.txt0100644 0000000 0000000 00000000366 15051152474 027120 0ustar00rootroot0000000 0000000 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/ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/README.txt0100644 0000000 0000000 00000004566 15051152474 027102 0ustar00rootroot0000000 0000000 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" apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/SPEC.txt0100644 0000000 0000000 00000031241 15051152474 026665 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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?... apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/build.xml0100644 0000000 0000000 00000016666 15051152474 027231 0ustar00rootroot0000000 0000000 Tests failed! apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/README0100644 0000000 0000000 00000000347 15051152474 030155 0ustar00rootroot0000000 0000000 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.cer0100644 0000000 0000000 00000001123 15051152474 030736 0ustar00rootroot0000000 0000000 0‚O0‚¸ LZ 0  *†H†÷ 0l10UUnknown10UUnknown10UUnknown10U Unknown10U Unknown10UUnknown0 100805111421Z 350327111421Z0l10UUnknown10UUnknown10UUnknown10U Unknown10U Unknown10UUnknown0Ÿ0  *†H†÷ 0‰޽vÿäÉ÷;#T³_DZiЩ¸} >1·úòœ$¨ºN7D¼"°Zù`²U'm؇|ãÌ'ïY;³Á#Žjÿœ¥LÝï¢BCâ4¥ÒPÕ‹äoÀ© ìÝ¿’Ü4 4‹Úa¸½jnÉáù„Œ‘^«Ò—¸ øæòã0  *†H†÷ #Gº‹Du¦ïý¾ªœ=ÇÛò}# @ÑÇüPC×/ß s¸™\¼µ>‡Y¯"9¬!@0³X7ÀçìæÞbwårQIñå·«–ŒWî2¸0ÞBâÏøò`y+‘o:Üògx2ƒð*é4½Š:j¬yŒÈRù\¢[²eapache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/keys/rest.jks0100644 0000000 0000000 00000002523 15051152474 030761 0ustar00rootroot0000000 0000000 þíþímykey*Aõ}îº0‚¶0 +*‚¢ˆð…m}´¾M.ÎnêŽÛ×ÛWͱZÓTÆe ŠaB” ÈúŒüS¯W‚Ô³–eM,l *ÁqÝþt”:ÃÊ×å Œé›B€S½ã‰£…„Xþ“6·Ì¡’ `Ú»ÿ®Ë£Zx³Àþí(ꨶÊ@Šf¨ì«ó÷Vš-=аh €ÛwvÊ„òZ-€U÷Ÿº6”9Pä ‚d(`,3·"‹Ó¢#…Ú TÜNr|Þ, ?;Ÿº3ÄòD²vD[ûm92Þïô¾“²),\[WM>mG(‰vÍûðúȳ½þw¿Ä˜z«{Mè[Ú@Ås›™¦kÌOrq ÿ‡aiwÊ‹Ò||#wsÔ2ÉšÕ’-–CKŽHÑô±ƒ>Z“í1 [_Ó@™9ä¢Lo~G‘Ö?‚@4£ß xÎAñ›’«“VøaÖtñn”õÙåÊòœ†X„ߙڷ`(\½¡•̾˜{?ÿÖ+&§…¹ÕX¡rô¯4¼Ü/Imh¹¯ 3ª8Âò\?饕Žç«„ð+ˆÃ%5/ðÉòÎÉè_À{h¾çêaW±Îª¨ª7•у—š¯Hj©W†’¡€• ·QÔ©ržžÌO'Vº;U%7¥Ä,n`"9…ØbàÃƔòa‡F“•J¯Ð‡ÓjûŠX.509S0‚O0‚¸ LZ 0  *†H†÷ 0l10UUnknown10UUnknown10UUnknown10U Unknown10U Unknown10UUnknown0 100805111421Z 350327111421Z0l10UUnknown10UUnknown10UUnknown10U Unknown10U Unknown10UUnknown0Ÿ0  *†H†÷ 0‰޽vÿäÉ÷;#T³_DZiЩ¸} >1·úòœ$¨ºN7D¼"°Zù`²U'm؇|ãÌ'ïY;³Á#Žjÿœ¥LÝï¢BCâ4¥ÒPÕ‹äoÀ© ìÝ¿’Ü4 4‹Úa¸½jnÉáù„Œ‘^«Ò—¸ øæòã0  *†H†÷ #Gº‹Du¦ïý¾ªœ=ÇÛò}# @ÑÇüPC×/ß s¸™\¼µ>‡Y¯"9¬!@0³X7ÀçìæÞbwårQIñå·«–ŒWî2¸0ÞBâÏøò`y+‘o:Üògx2ƒð*é4½Š:j¬yŒÈRù\¢[²eÚŸ­ü±L3ʸæ$bŸKRʳ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/logback.xml0100644 0000000 0000000 00000010717 15051152474 030450 0ustar00rootroot0000000 0000000 %d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n ${zookeeper.console.threshold} apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/conf/rest.properties0100644 0000000 0000000 00000003773 15051152474 031423 0ustar00rootroot0000000 0000000 # # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/ivy.xml0100644 0000000 0000000 00000004034 15051152474 026723 0ustar00rootroot0000000 0000000 ZooKeeper REST apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/pom.xml0100755 0000000 0000000 00000010223 15051152474 026707 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-contrib 3.9.4 zookeeper-contrib-rest jar Apache ZooKeeper - Contrib - Rest 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. 3.1 1.9.8 1.1.5.1 org.apache.zookeeper zookeeper ${project.version} org.apache.zookeeper zookeeper ${project.version} test-jar test org.slf4j slf4j-api ch.qos.logback logback-core * * asm asm ${asm.version} com.sun.grizzly grizzly-servlet-webserver ${grizzly.version} com.sun.jersey jersey-server ${jersey.version} com.sun.jersey jersey-json ${jersey.version} com.sun.jersey jersey-client ${jersey.version} test org.junit.vintage junit-vintage-engine test org.apache.maven.plugins maven-surefire-plugin true apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/rest.sh0100644 0000000 0000000 00000004351 15051152474 026705 0ustar00rootroot0000000 0000000 #!/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 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000210 15051152474 032537 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/RestMain.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000011654 15051152474 034234 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 java.nio.file.Files; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 = LoggerFactory.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(); String zkRestResourcesTempPath = Files.createTempDirectory("zkRestResourcesTempPath").toFile().getCanonicalPath(); gws = new GrizzlyWebServer(cfg.getPort(), zkRestResourcesTempPath, 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)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000220 15051152474 032540 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/ZooKeeperService.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000017225 15051152474 034234 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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 = LoggerFactory.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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000217 15051152474 032546 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Credentials.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000002751 15051152474 034232 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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]); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000214 15051152474 032543 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/Endpoint.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000003735 15051152474 034235 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000214 15051152474 032543 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPort.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000002653 15051152474 034233 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000217 15051152474 032546 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/HostPortSet.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000003067 15051152474 034233 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000213 15051152474 032542 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/cfg/RestCfg.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000006172 15051152474 034233 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 { try { cfg.load(io); extractEndpoints(); extractCredentials(); } finally { io.close(); } } 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"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000225 15051152474 032545 xustar000000000 0000000 149 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/filters/HTTPBasicAuth.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000005466 15051152474 034240 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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() { } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000216 15051152474 032545 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildren.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000004525 15051152474 034233 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 + ")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000222 15051152474 032542 xustar000000000 0000000 146 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZChildrenJSON.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000004312 15051152474 034225 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 + ")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000213 15051152474 032542 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZError.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000002330 15051152474 034223 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000212 15051152474 032541 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZPath.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000003154 15051152474 034230 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 + ")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000215 15051152474 032544 xustar000000000 0000000 141 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZSession.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000003037 15051152474 034230 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 +")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000212 15051152474 032541 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/jaxb/ZStat.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000005744 15051152474 034237 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 + ")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000235 15051152474 032546 xustar000000000 0000000 157 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/JAXBContextResolver.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000005157 15051152474 034235 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000237 15051152474 032550 xustar000000000 0000000 159 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/KeeperExceptionMapper.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000005472 15051152474 034235 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000240 15051152474 032542 xustar000000000 0000000 160 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/RuntimeExceptionMapper.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000003606 15051152474 034232 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000232 15051152474 032543 xustar000000000 0000000 154 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/SessionsResource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000011435 15051152474 034231 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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 = LoggerFactory.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()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000226 15051152474 032546 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZErrorWriter.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000004305 15051152474 034227 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_main_java_org_ap0100644 0000000 0000000 00000000227 15051152474 032547 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/server/jersey/resources/ZNodeResource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/main/java/org/apache/zookeeper/s0100644 0000000 0000000 00000036504 15051152474 034235 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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()); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/README.txt0100644 0000000 0000000 00000000461 15051152474 031200 0ustar00rootroot0000000 0000000 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_master_election.py0100644 0000000 0000000 00000005550 15051152474 034241 0ustar00rootroot0000000 0000000 #! /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()) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/demo_queue.py0100644 0000000 0000000 00000005370 15051152474 032210 0ustar00rootroot0000000 0000000 #! /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()) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/test.py0100644 0000000 0000000 00000011722 15051152474 031035 0ustar00rootroot0000000 0000000 #! /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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/zk_dump_tree.py0100755 0000000 0000000 00000006551 15051152474 032555 0ustar00rootroot0000000 0000000 #!/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) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/python/zkrest.py0100644 0000000 0000000 00000016073 15051152474 031404 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000204 15051152474 032575 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/JUnit4ZKTestRunner.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/J0100644 0000000 0000000 00000010733 15051152474 034213 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.Arrays; import java.util.List; import org.junit.Test; import org.junit.internal.runners.statements.InvokeMethod; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The sole responsibility of this class is to print to the log when a test * starts and when it finishes. */ public class JUnit4ZKTestRunner extends BlockJUnit4ClassRunner { private static final Logger LOG = LoggerFactory.getLogger(JUnit4ZKTestRunner.class); public JUnit4ZKTestRunner(Class klass) throws InitializationError { super(klass); } @SuppressWarnings("unchecked") public static List computeTestMethodsForClass( final Class klass, final List defaultMethods) { List list = defaultMethods; String methodName = System.getProperty("test.method"); if (methodName == null) { LOG.info("No test.method specified. using default methods."); } else { LOG.info("Picked up test.method={}", methodName); try { list = Arrays.asList(new FrameworkMethod(klass.getMethod(methodName))); } catch (NoSuchMethodException nsme) { LOG.warn( "{} does not have test.method={}. failing to default methods.", klass.getName(), methodName); } } return list; } @Override protected List computeTestMethods() { return computeTestMethodsForClass(getTestClass().getJavaClass(), super.computeTestMethods()); } public static class LoggedInvokeMethod extends InvokeMethod { private final FrameworkMethod method; private final String name; public LoggedInvokeMethod(FrameworkMethod method, Object target) { super(method, target); this.method = method; name = method.getName(); } @Override public void evaluate() throws Throwable { LOG.info("RUNNING TEST METHOD {}", name); try { super.evaluate(); Runtime rt = Runtime.getRuntime(); long usedKB = (rt.totalMemory() - rt.freeMemory()) / 1024; LOG.info("Memory used {}", usedKB); ThreadGroup tg = Thread.currentThread().getThreadGroup(); while (tg.getParent() != null) { tg = tg.getParent(); } LOG.info("Number of threads {}", tg.activeCount()); } catch (Throwable t) { // The test method threw an exception, but it might be an // expected exception as defined in the @Test annotation. // Check the annotation and log an appropriate message. Test annotation = this.method.getAnnotation(Test.class); if (annotation != null && annotation.expected() != null && annotation.expected().isAssignableFrom(t.getClass())) { LOG.info("TEST METHOD {} THREW EXPECTED EXCEPTION {}", name, annotation.expected()); } else { LOG.warn("TEST METHOD FAILED {}", name, t); } throw t; } LOG.info("FINISHED TEST METHOD {}", name); } } @Override protected Statement methodInvoker(FrameworkMethod method, Object test) { return new LoggedInvokeMethod(method, test); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000204 15051152474 032575 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/Base.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000005663 15051152474 034272 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.JUnit4ZKTestRunner; 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 org.junit.runner.RunWith; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; /** * Test stand-alone server. * */ @RunWith(JUnit4ZKTestRunner.class) public class Base { protected static final Logger LOG = LoggerFactory.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 { 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 { 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000212 15051152474 032574 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/CreateTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000013532 15051152474 034264 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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.Assert; 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 = LoggerFactory.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 { 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); } Assert.assertEquals(expectedStatus, cr.getClientResponseStatus()); if (expectedPath == null) { return; } ZPath zpath = cr.getEntity(ZPath.class); if (sequence) { Assert.assertTrue(zpath.path.startsWith(expectedPath.path)); Assert.assertTrue(zpath.uri.startsWith(znodesr.path(path).toString())); } else { Assert.assertEquals(expectedPath, zpath); Assert.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) { Assert.assertEquals(data, this.data); } else { Assert.assertTrue(new String(data) + " == " + new String(this.data), Arrays.equals(data, this.data)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000212 15051152474 032574 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/DeleteTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000006053 15051152474 034264 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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.Assert; 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 = LoggerFactory.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); Assert.assertEquals(expectedStatus, cr.getClientResponseStatus()); // use out-of-band method to verify Stat stat = zk.exists(zpath, false); Assert.assertNull(stat); } @Test public void testDelete() throws Exception { verify(MediaType.APPLICATION_OCTET_STREAM); verify(MediaType.APPLICATION_JSON); verify(MediaType.APPLICATION_XML); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000212 15051152474 032574 xustar000000000 0000000 138 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/ExistsTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000005065 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.Assert; 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 = LoggerFactory.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) { Assert.assertEquals(ClientResponse.Status.NO_CONTENT, cr.getClientResponseStatus()); } else { Assert.assertEquals(expectedStatus, cr.getClientResponseStatus()); } } @Test public void testExists() throws Exception { verify(MediaType.APPLICATION_OCTET_STREAM); verify(MediaType.APPLICATION_JSON); verify(MediaType.APPLICATION_XML); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000217 15051152474 032601 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/GetChildrenTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000013170 15051152474 034262 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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.Assert; 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 = LoggerFactory.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 { 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); Assert.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); Assert.assertEquals(expectedChildren, zchildren.children); Assert.assertEquals(znodesr.path(path).toString(), zchildren.uri); Assert.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); Assert.assertEquals(expectedChildren, zchildren.children); Assert.assertEquals(znodesr.path(path).toString(), zchildren.uri); Assert.assertEquals(znodesr.path(path).toString() + "/{child}", zchildren.child_uri_template); } else { Assert.fail("unknown accept type"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000207 15051152474 032600 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/GetTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000011143 15051152474 034260 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.jersey.jaxb.ZStat; import org.junit.Assert; 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 = LoggerFactory.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 { 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); Assert.assertEquals(expectedStatus, cr.getClientResponseStatus()); if (expectedStat == null) { return; } ZStat zstat = cr.getEntity(ZStat.class); Assert.assertEquals(expectedStat, zstat); Assert.assertEquals(znodesr.path(path).toString(), zstat.uri); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000215 15051152474 032577 xustar000000000 0000000 141 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/RestTestSuite.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000002547 15051152474 034270 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000210 15051152474 032572 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/RootTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000004471 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.jersey.jaxb.ZPath; import org.junit.Assert; 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 = LoggerFactory.getLogger(RootTest.class); @Test public void testCreate() throws Exception { 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); Assert.assertEquals(ClientResponse.Status.CREATED, cr.getClientResponseStatus()); ZPath zpath = cr.getEntity(ZPath.class); Assert.assertEquals(new ZPath(path + name), zpath); Assert.assertEquals(znodesr.path(path).toString(), zpath.uri); // use out-of-band method to verify byte[] rdata = zk.getData(zpath.path, false, new Stat()); Assert.assertTrue(new String(rdata) + " == " + new String(data), Arrays.equals(rdata, data)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000213 15051152474 032575 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/SessionTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000011577 15051152474 034273 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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.Assert; 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 = LoggerFactory.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); Assert.assertEquals(ClientResponse.Status.CREATED, cr .getClientResponseStatus()); return cr.getEntity(ZSession.class); } @Test public void testCreateNewSession() throws JSONException { ZSession session = createSession(); Assert.assertEquals(session.id.length(), 36); // use out-of-band method to verify Assert.assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); } @Test public void testSessionExpires() throws InterruptedException { ZSession session = createSession("1"); // use out-of-band method to verify Assert.assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); // wait for the session to be closed Thread.sleep(1500); Assert.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); Assert.assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); ClientResponse cr = b.delete(ClientResponse.class, null); Assert.assertEquals(ClientResponse.Status.NO_CONTENT, cr.getClientResponseStatus()); Assert.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); Assert.assertEquals(ClientResponse.Status.OK, cr.getClientResponseStatus()); Thread.sleep(1500); Assert.assertTrue(ZooKeeperService.isConnected(CONTEXT_PATH, session.id)); Thread.sleep(1000); Assert.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); Assert.assertEquals(ClientResponse.Status.CREATED, cr.getClientResponseStatus()); Stat stat = new Stat(); zk.getData("/ephemeral-test", false, stat); ZooKeeper sessionZK = ZooKeeperService.getClient(CONTEXT_PATH, session.id); Assert.assertEquals(stat.getEphemeralOwner(), sessionZK.getSessionId()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000207 15051152474 032600 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/SetTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000013205 15051152474 034261 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; 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.Assert; 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 = LoggerFactory.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 { 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)); } Assert.assertEquals(expectedStatus, cr.getClientResponseStatus()); if (expectedStat == null) { return; } ZStat zstat = cr.getEntity(ZStat.class); Assert.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) { Assert.fail((data == null ? null : new String(data)) + " == " + (this.data == null ? null : new String(this.data))); } else { Assert.assertTrue(new String(data) + " == " + new String(this.data), Arrays.equals(data, this.data)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-rest_src_test_java_org_ap0100644 0000000 0000000 00000000210 15051152474 032572 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/server/jersey/WadlTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/java/org/apache/zookeeper/s0100644 0000000 0000000 00000003022 15051152474 034255 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.junit.Assert; 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 = LoggerFactory.getLogger(WadlTest.class); @Test public void testApplicationWadl() { WebResource r = client.resource(BASEURI); String serviceWadl = r.path("application.wadl"). accept(MediaTypes.WADL).get(String.class); Assert.assertTrue("Something wrong. Returned wadl length not > 0.", serviceWadl.length() > 0); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-rest/src/test/zkServer.sh0100755 0000000 0000000 00000004361 15051152474 031315 0ustar00rootroot0000000 0000000 #!/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}"/zookeeper-server/src/main/resource/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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/Makefile.am0100644 0000000 0000000 00000000112 15051152474 027751 0ustar00rootroot0000000 0000000 ## Process this file with automake to produce Makefile.in SUBDIRS = src apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/README.txt0100644 0000000 0000000 00000004307 15051152474 027425 0ustar00rootroot0000000 0000000 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 d. boost 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. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/build.xml0100644 0000000 0000000 00000003750 15051152474 027551 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/configure.ac0100644 0000000 0000000 00000003566 15051152474 030223 0ustar00rootroot0000000 0000000 # -*- 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_MSG_ERROR("We need log4cxx to build zkfuse")]) AC_CHECK_LIB([thread], [thr_create]) AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([rt], [clock_gettime]) AC_CHECK_LIB([socket], [socket]) AC_CHECK_LIB([nsl], [gethostbyname]) AC_CHECK_LIB([ulockmgr], [ulockmgr_op]) ZOOKEEPER_PATH=${BUILD_PATH}/../../c ZOOKEEPER_BUILD_PATH=${BUILD_PATH}/../../../build/c AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER_LD="-L${ZOOKEEPER_BUILD_PATH}/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_BUILD_PATH}/.libs"]) AC_SUBST(ZOOKEEPER_PATH) AC_SUBST(ZOOKEEPER_LD) # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h]) AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/utility.hpp boost/weak_ptr.hpp],, AC_MSG_ERROR([boost library headers not found. Please install boost library.])) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/Makefile.am0100644 0000000 0000000 00000000411 15051152474 030542 0ustar00rootroot0000000 0000000 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}apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/blockingqueue.h0100644 0000000 0000000 00000007642 15051152474 031531 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/doxygen.cfg0100644 0000000 0000000 00000144045 15051152474 030660 0ustar00rootroot0000000 0000000 # 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/event.cc0100644 0000000 0000000 00000001710 15051152474 030141 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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' */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/event.h0100644 0000000 0000000 00000036503 15051152474 030013 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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. */ boost::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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/log.cc0100644 0000000 0000000 00000002271 15051152474 027604 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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" ); apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/log.h0100644 0000000 0000000 00000006636 15051152474 027457 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/log4cxx.properties0100644 0000000 0000000 00000002175 15051152474 032225 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/mutex.h0100644 0000000 0000000 00000007537 15051152474 030041 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/thread.cc0100644 0000000 0000000 00000002434 15051152474 030273 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/thread.h0100644 0000000 0000000 00000004746 15051152474 030145 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.cc0100644 0000000 0000000 00000067350 15051152474 031021 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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, NULL ); 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 { if (len == -1) { len = 0; } return string( 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' */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkadapter.h0100644 0000000 0000000 00000062233 15051152474 030656 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkfuse/src/zkfuse.cc0100644 0000000 0000000 00000433424 15051152474 030342 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/Changes0100644 0000000 0000000 00000002772 15051152474 027226 0ustar00rootroot0000000 0000000 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 0.36 Mar 27, 2011 - Fix zookeeper version check, but only warn since we haven't been enforcing it in a while - Look for zookeeper includes in some sane places by default apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/LICENSE0100644 0000000 0000000 00000026136 15051152474 026740 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/MANIFEST0100644 0000000 0000000 00000000451 15051152474 027054 0ustar00rootroot0000000 0000000 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/Makefile.PL0100644 0000000 0000000 00000005070 15051152474 027677 0ustar00rootroot0000000 0000000 # 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 $ZOO_MAJOR_VERSION = 3; my $ZOO_REQUIRED_VERSION = qr{^$ZOO_MAJOR_VERSION\.\d+.\d+$}ismx; my @zk_inc_paths; my @zk_lib_paths; my $with_sasl2 = 0; my @sasl2_inc_paths; my @sasl2_lib_paths; GetOptions( 'zookeeper-include=s' => \@zk_inc_paths, 'zookeeper-lib=s' => \@zk_lib_paths, 'with-sasl2!' => \$with_sasl2, 'sasl2-include=s' => \@sasl2_inc_paths, 'sasl2-lib=s' => \@sasl2_lib_paths ); my $zk_inc = (join(' ', map("-I$_", @zk_inc_paths)) . ' -I.'); my $zk_libs = (join(' ', map("-L$_", @zk_lib_paths)) . ' -lzookeeper_mt'); my $cc = $Config{'cc'}; my $check_file = 'build/check_zk_version'; my $check_out = qx($cc $zk_inc -o $check_file $check_file.c $zk_libs 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"); } } chomp(my $zk_ver = qx($check_file)); if ($? >> 8 != 0) { die "Couldn't check zookeeper version: $zk_ver: $r"; } elsif ($zk_ver !~ $ZOO_REQUIRED_VERSION) { warn "Net::ZooKeeper requires ZooKeeper 3.x, found $zk_ver!"; } my @inc = ($zk_inc); my @libs = ($zk_libs); my %mmopt = (); if ($with_sasl2) { push(@inc, join(' ', map("-I$_", @sasl2_inc_paths))); push(@libs, join(' ', map("-L$_", @sasl2_lib_paths)) . ' -lsasl2'); $mmopt{DEFINE} = '-DHAVE_CYRUS_SASL_H'; } WriteMakefile( 'INC' => join(' ', @inc), 'LIBS' => \@libs, 'NAME' => 'Net::ZooKeeper', 'VERSION_FROM' => 'ZooKeeper.pm', 'clean' => { 'FILES' => 'build/check_zk_version.o' }, %mmopt, ); apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/NOTICE0100644 0000000 0000000 00000000317 15051152474 026630 0ustar00rootroot0000000 0000000 Net::ZooKeeper - Perl extension for Apache ZooKeeper Copyright 2009-2020 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/README0100644 0000000 0000000 00000007273 15051152474 026614 0ustar00rootroot0000000 0000000 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. For details see the ZooKeeper home page at: http://zookeeper.apache.org/ INSTALLATION To install this module type the following, first install the zookeeper C client, then: perl Makefile.PL make ZK_TEST_HOSTS=host:port,... make test make install If the C headers and library are installed in non-standard locations, specify them as arguments to Makefile.PL: perl Makefile.PL \ --zookeeper-include=/path/to/zookeeper/client/include \ --zookeeper-lib=/path/to/zookeeper/client/lib 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. If the C client supports Cyrus SASL (ZOOKEEPER-1112), it can also be enabled in the Perl binding by passing a --with-sasl2 flag (and, optionally, non-standard locations): perl Makefile.PL \ --with-sasl2 \ --sasl2-include=/path/to/sasl2/include \ --sasl2-lib=/path/to/sasl2/lib 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. The ZK_TEST_SASL_OPTIONS environment variable, if defined, provides a JSON-encoded map of SASL authentication options, enabling SASL tests. E.g., { "host": "zk-sasl-md5", "mechlist": "DIGEST-MD5", "service": "zookeeper", "user": "bob", "password_file": "bob.secret" } 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. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.pm0100644 0000000 0000000 00000134114 15051152474 030170 0ustar00rootroot0000000 0000000 # 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.36'; 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. 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, 'sasl_options' => $sasl_options); 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. If a C<'sasl_options'> option is provided, it is used to automatically SASL-authenticate with the server during connections (including reconnects). Here is a brief description of the recognized keys; please refer to the C client documentation for details: =over 5 =item service => VALUE =item host => VALUE =item mechlist => VALUE These map to the corresponding fields of C from the library. =item user => VALUE =item realm => VALUE =item password_file => VALUE These map to the corresponding parameters of C from the library. =back 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/ZooKeeper.xs0100644 0000000 0000000 00000206056 15051152474 030213 0ustar00rootroot0000000 0000000 /* 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() */ #define THREADED #include #undef THREADED #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; } if (watch->next) { watch->next->prev = watch->prev; } watch->prev = NULL; 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 || (alias != NULL && strEQ(alias, "ZOO_OPEN_ACL_UNSAFE"))) { acl = ZOO_OPEN_ACL_UNSAFE; } else if (ix == 2 || (alias != NULL && strEQ(alias, "ZOO_READ_ACL_UNSAFE"))) { acl = ZOO_READ_ACL_UNSAFE; } else if (ix == 3 || (alias != NULL && 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; #ifdef HAVE_CYRUS_SASL_H zoo_sasl_params_t sasl_params = { 0 }; const char *sasl_user = NULL; const char *sasl_realm = NULL; const char *sasl_password_file = NULL; int use_sasl = 0; #endif /* HAVE_CYRUS_SASL_H */ 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"); } } #ifdef HAVE_CYRUS_SASL_H else if (strcaseEQ(key, "sasl_options")) { SV *hash_sv = ST(i + 1); HV *hash; char *key; I32 key_length; SV *value; if (!SvROK(hash_sv) || SvTYPE(SvRV(hash_sv)) != SVt_PVHV) { Perl_croak(aTHX_ "sasl_options requires a hash reference"); } hash = (HV *)SvRV(hash_sv); hv_iterinit(hash); while ((value = hv_iternextsv(hash, &key, &key_length))) { if (strcaseEQ(key, "service")) { sasl_params.service = SvPV_nolen(value); } else if (strcaseEQ(key, "host")) { sasl_params.host = SvPV_nolen(value); } else if (strcaseEQ(key, "mechlist")) { sasl_params.mechlist = SvPV_nolen(value); } else if (strcaseEQ(key, "user")) { sasl_user = SvPV_nolen(value); } else if (strcaseEQ(key, "realm")) { sasl_realm = SvPV_nolen(value); } else if (strcaseEQ(key, "password_file")) { sasl_password_file = SvPV_nolen(value); } } use_sasl = 1; } #endif /* HAVE_CYRUS_SASL_H */ } Newxz(zk, 1, zk_t); #ifdef HAVE_CYRUS_SASL_H if (use_sasl) { /* KLUDGE: Leaks a reference count. Authen::SASL::XS does the same, though. TODO(ddiederen): Fix. */ sasl_client_init(NULL); sasl_params.callbacks = zoo_sasl_make_basic_callbacks(sasl_user, sasl_realm, sasl_password_file); } zk->handle = zookeeper_init_sasl(hosts, NULL, recv_timeout, client_id, NULL, 0, NULL, use_sasl ? &sasl_params : NULL); #else zk->handle = zookeeper_init(hosts, NULL, recv_timeout, client_id, NULL, 0); #endif /* HAVE_CYRUS_SASL_H */ 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: %zu", 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: %zu", 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_ "%s", 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 && buf_len != -1) { 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: %zu", 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_ "%s", 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; struct timespec wait_timespec; 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; wait_timespec.tv_sec = end_timeval.tv_sec; wait_timespec.tv_nsec = end_timeval.tv_usec * 1000; pthread_mutex_lock(&watch->mutex); while (!watch->done) { struct timeval curr_timeval; gettimeofday(&curr_timeval, NULL); if (end_timeval.tv_sec < curr_timeval.tv_sec || (end_timeval.tv_sec == curr_timeval.tv_sec && end_timeval.tv_usec <= curr_timeval.tv_usec)) { break; } pthread_cond_timedwait(&watch->cond, &watch->mutex, &wait_timespec); } done = watch->done; pthread_mutex_unlock(&watch->mutex); if (done) { XSRETURN_YES; } else { XSRETURN_NO; } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/build/check_zk_version.c0100644 0000000 0000000 00000001752 15051152474 032521 0ustar00rootroot0000000 0000000 /* 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 #include #include "check_zk_version.h" int main() { puts(ZOO_VERSION); return 0; } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/build/check_zk_version.h0100644 0000000 0000000 00000002500 15051152474 032516 0ustar00rootroot0000000 0000000 /* 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_VERSION) /* ZOO_VERSION was introduced by ZOOKEEPER-3635 (3.6.0-pre). */ #else /* !defined(ZOO_VERSION) */ #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 #endif /* !defined(ZOO_VERSION) */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/build.xml0100644 0000000 0000000 00000003750 15051152474 027551 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/10_invalid.t0100644 0000000 0000000 00000043236 15051152474 030311 0ustar00rootroot0000000 0000000 # 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/i, 'set_acl(): invalid ACL array reference'); eval { $zkh->set_acl($node_path, {}); }; like($@, qr/acl_arr is not an array reference/i, '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'); apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/15_thread.t0100644 0000000 0000000 00000006312 15051152474 030131 0ustar00rootroot0000000 0000000 # 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; } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/20_tie.t0100644 0000000 0000000 00000021361 15051152474 027440 0ustar00rootroot0000000 0000000 # 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'); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/22_stat_tie.t0100644 0000000 0000000 00000026521 15051152474 030500 0ustar00rootroot0000000 0000000 # 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'); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/24_watch_tie.t0100644 0000000 0000000 00000017006 15051152474 030633 0ustar00rootroot0000000 0000000 # 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'); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/30_connect.t0100644 0000000 0000000 00000013721 15051152474 030312 0ustar00rootroot0000000 0000000 # 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 ## ## Test disabled because it breaks with current ZooKeeper servers: ## $zkh1's connection gets closed as soon as $zkh2 connects, which ## causes it to reconnect, which kills $zkh2's connection, etc. ## TODO: figure out a way to test this. SKIP: { skip 'does not work with current ZK servers', 4; 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'); } } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/35_log.t0100644 0000000 0000000 00000004660 15051152474 027451 0ustar00rootroot0000000 0000000 # 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_DEBUG); 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; $/ = undef; # slurp mode. 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/, '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_.*close/, 'DESTROY(): generated log message'); } open(STDERR, '>&', fileno(OLDERR)); close(OLDERR); } } Net::ZooKeeper::set_log_level(ZOO_LOG_LEVEL_OFF); apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/40_basic.t0100644 0000000 0000000 00000022546 15051152474 027750 0ustar00rootroot0000000 0000000 # 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); } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/45_class.t0100644 0000000 0000000 00000024722 15051152474 027777 0ustar00rootroot0000000 0000000 # 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'); } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/50_access.t0100644 0000000 0000000 00000027200 15051152474 030121 0ustar00rootroot0000000 0000000 # 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 => 40; use Storable qw(dclone); 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'); my $zoo_read_acl_unsafe = ZOO_READ_ACL_UNSAFE; ok((ref($zoo_read_acl_unsafe) eq 'ARRAY' and @{$zoo_read_acl_unsafe} == 1 and ref($zoo_read_acl_unsafe->[0]) eq 'HASH' and keys(%{$zoo_read_acl_unsafe->[0]}) == 3 and $zoo_read_acl_unsafe->[0]->{'perms'} == ZOO_PERM_READ), '_zk_acl_constant(): returned good ACL'); my $zoo_creator_all_acl = ZOO_CREATOR_ALL_ACL; ok((ref($zoo_creator_all_acl) eq 'ARRAY' and @{$zoo_creator_all_acl} == 1 and ref($zoo_creator_all_acl->[0]) eq 'HASH' and keys(%{$zoo_creator_all_acl->[0]}) == 3 and $zoo_creator_all_acl->[0]->{'perms'} == ZOO_PERM_ALL), '_zk_acl_constant(): returned good ACL'); $no_read_acl->[0]->{'perms'} &= ~ZOO_PERM_READ; is($no_read_acl->[0]->{'perms'}, ((ZOO_PERM_ALL) & ~ZOO_PERM_READ), 'assign: altered default ACL'); 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'); # The test is not running as ADMIN, which means that the server # returns "redacted" ACLs (see ZOOKEEPER-1392 and OpCode.getACL in # FinalRequestProcessor). We must do the same for the comparison # to succeed. my $redacted_digest_acl = dclone($digest_acl); $redacted_digest_acl->[1]->{id} =~ s/:.*/:x/; @acl = ('abc'); @acl = $zkh->get_acl($acl_node_path); is_deeply(\@acl, $redacted_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'); } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/60_watch.t0100644 0000000 0000000 00000021644 15051152474 027775 0ustar00rootroot0000000 0000000 # 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); } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/70_sasl.t0100644 0000000 0000000 00000006504 15051152474 027630 0ustar00rootroot0000000 0000000 # 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 => 7; use JSON::PP qw(decode_json); 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 $sasl_options = $ENV{'ZK_TEST_SASL_OPTIONS'}; if (defined($sasl_options)) { $sasl_options = decode_json($sasl_options); } SKIP: { skip 'no sasl_options', 6 unless defined($sasl_options); my $zkh = Net::ZooKeeper->new($hosts, 'sasl_options' => $sasl_options); 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 $acl_node_path = "$node_path/a1"; my $sasl_acl = [ { 'perms' => ZOO_PERM_READ, 'scheme' => 'world', 'id' => 'anyone' }, { 'perms' => ZOO_PERM_ALL, 'scheme' => 'sasl', 'id' => $sasl_options->{user} } ]; $path = $zkh->create($acl_node_path, 'foo', 'acl' => $sasl_acl); is($path, $acl_node_path, 'create(): created node with SASL ACL'); ## get_acl() @acl = ('abc'); @acl = $zkh->get_acl($acl_node_path); is_deeply(\@acl, $sasl_acl, 'get_acl(): retrieved SASL ACL'); SKIP: { my $zkh2 = Net::ZooKeeper->new($hosts); my $ret = $zkh->exists($root_path) if (defined($zkh)); skip 'no connection to ZooKeeper', 1 unless (defined($ret) and $ret); my $node = $zkh2->get($acl_node_path); is($node, 'foo', 'get(): retrieved node value with world ACL'); $ret = $zkh2->set($acl_node_path, 'bar'); ok((!$ret and $zkh2->get_error() == ZNOAUTH and $! eq ''), 'set(): node value unchanged if no auth'); } my $ret = $zkh->set($acl_node_path, 'bar'); ok($ret, 'set(): set node with SASL ACL'); my $node = $zkh->get($acl_node_path); is($node, 'bar', 'get(): retrieved new node value with SASL ACL'); $ret = $zkh->delete($acl_node_path); diag(sprintf('unable to delete node %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); } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/t/util.pl0100644 0000000 0000000 00000003566 15051152474 027512 0ustar00rootroot0000000 0000000 # 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; apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkperl/typemap0100644 0000000 0000000 00000002326 15051152474 027330 0ustar00rootroot0000000 0000000 # 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/README0100644 0000000 0000000 00000014206 15051152474 027165 0ustar00rootroot0000000 0000000 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/Git (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. (On ubuntu 18.4, install python2.7 and python2.7-dev.) Python >= 2.6 is required. We have tested against 2.6 and 3.5+. By default, the extension assumes that the C client library was compiled with OpenSSL enabled (--with-openssl). You can disable OpenSSL support in the Python binding by setting the ZKPYTHON_NO_SSL environment variable to a non-empty string before executing Ant or setup.py. E.g. setting up python and python devel on ubuntu 18.4: sudo apt-get install python2.7 python2.7-dev sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 BUILD AND INSTALL: ------------------- To install, make sure that the C client has been built (use `mvn clean install -DskipTests -Pfull-build` in the root folder of zookeeper) or that the zookeeper C libraries are installed in /usr/local/lib (or change this directory in setup.py). Then run: ant install from zookeeper-contrib/zookeeper-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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/build.xml0100644 0000000 0000000 00000007303 15051152474 030126 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/ivy.xml0100644 0000000 0000000 00000002620 15051152474 027633 0ustar00rootroot0000000 0000000 ZKPython apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/pyzk_docstrings.h0100644 0000000 0000000 00000065760 15051152474 032736 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 occurred\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 occurred while attempting to send request to server\n" "SYSTEMERROR -- a system (OS) error occurred; it's worth checking errno to get details\n"; static const char pyzk_set2_doc[] = "\n" " 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_init_ssl_doc[] = "This method creates a new handle and a zookeeper SSL 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" " cert_str: SSL certificate string e.g. 'server.cert,client.cert,client-priv-key.pom,passwd'\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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/c/zookeeper.c0100644 0000000 0000000 00000143451 15051152474 031472 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 ZINVALIDSTATE: return InvalidStateException; 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 ZCLOSING: return ClosingException; case ZNOTHING: return NothingException; 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) { #if PY_MAJOR_VERSION >= 3 PyObject *s = PyUnicode_FromString(sv->data[i]); #else PyObject *s = PyString_FromString(sv->data[i]); #endif if (!s) { if (ret != Py_None) { Py_DECREF(ret); } 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" ); #if PY_MAJOR_VERSION >= 3 acls->data[i].perms = (int32_t)(PyLong_AsLong(perms)); PyObject *tem_utfstring; tem_utfstring = PyUnicode_AsEncodedString(PyDict_GetItemString( a, "id" ), "utf-8", NULL ); acls->data[i].id.id = strdup( PyBytes_AS_STRING(tem_utfstring)); Py_DECREF(tem_utfstring); tem_utfstring = PyUnicode_AsEncodedString(PyDict_GetItemString( a, "scheme" ), "utf-8", NULL ); acls->data[i].id.scheme = strdup( PyBytes_AS_STRING(tem_utfstring) ); Py_DECREF(tem_utfstring); #else acls->data[i].perms = (int32_t)(PyInt_AsLong(perms)); acls->data[i].id.id = strdup( PyString_AsString( PyDict_GetItemString( a, "id" ) ) ); acls->data[i].id.scheme = strdup( PyString_AsString( PyDict_GetItemString( a, "scheme" ) ) ); #endif } return 1; } /* 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(); } Py_DECREF(arglist); if (pyw->permanent == 0 && (type != ZOO_SESSION_EVENT || state < 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(); Py_DECREF(arglist); 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(); Py_DECREF(arglist); 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(); Py_DECREF(arglist); 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(); Py_DECREF(arglist); } 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(); Py_DECREF(arglist); 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(); } Py_DECREF(arglist); free_pywatcher(pyw); PyGILState_Release(gstate); } /* -------------------------------------------------------------------------- */ /* ZOOKEEPER API IMPLEMENTATION */ /* -------------------------------------------------------------------------- */ static PyObject *pyzookeeper_init_common(PyObject *self, PyObject *args, int ssl) { const char *host; const char *cert_str; PyObject *watcherfn = Py_None; zhandle_t *zh = NULL; int recv_timeout = 10000; 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 (ssl) { if (!PyArg_ParseTuple(args, "ss|Oi(Ls)", &host, &cert_str, &watcherfn, &recv_timeout, &cid.client_id, &passwd)) return NULL; } else { 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; if (ssl) { #ifdef HAVE_OPENSSL_H zh = zookeeper_init_ssl( host, cert_str, watcherfn != Py_None ? watcher_dispatch : NULL, recv_timeout, cid.client_id == -1 ? 0 : &cid, pyw, 0 ); #else fprintf(stderr, "SSL support not compiled in (called with ssl=%d).\n", ssl); abort(); #endif } else { zh = zookeeper_init( host, watcherfn != Py_None ? watcher_dispatch : NULL, recv_timeout, cid.client_id == -1 ? 0 : &cid, pyw, 0 ); } if (zh == NULL) { PyErr_Format( ZooKeeperException, "Could not internally obtain%s zookeeper handle", ssl ? " SSL" : "" ); return NULL; } zhandles[handle] = zh; return Py_BuildValue( "i", handle); } static PyObject *pyzookeeper_init(PyObject *self, PyObject *args) { return pyzookeeper_init_common(self, args, /*ssl*/0); } #ifdef HAVE_OPENSSL_H static PyObject *pyzookeeper_init_ssl(PyObject *self, PyObject *args) { return pyzookeeper_init_common(self, args, /*ssl*/1); } #endif /* -------------------------------------------------------------------------- */ /* 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 existence 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 existence 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; 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 < 0 ? 0 : 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 == ZINVALIDSTATE) 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 PY_MAJOR_VERSION >= 3 extern PyTypeObject PyIOBase_Type; if (!PyObject_IsInstance(pystream, (PyObject *)&PyIOBase_Type)) { #else if(!PyFile_Check(pystream)) { #endif PyErr_SetString(PyExc_ValueError, "Must supply a file object to set_log_stream"); return NULL; } /* 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); #if PY_MAJOR_VERSION >= 3 int fd = PyObject_AsFileDescriptor(log_stream); FILE *fp = fdopen(fd, "w"); #else FILE *fp = PyFile_AsFile(log_stream); #endif zoo_set_log_stream(fp); Py_INCREF(Py_None); return Py_None; } /* 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 }, #ifdef HAVE_OPENSSL_H {"init_ssl", pyzookeeper_init_ssl, METH_VARARGS, pyzk_init_ssl_doc }, #endif {"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} }; #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef zookeeper_moddef = { PyModuleDef_HEAD_INIT, "zookeeper", NULL, 0, ZooKeeperMethods, 0, 0, 0, 0 }; #endif #define ADD_INTCONSTANT(x) PyModule_AddIntConstant(module, #x, ZOO_##x) #define ADD_INTCONSTANTZ(x) PyModule_AddIntConstant(module, #x, Z##x) #define ADD_EXCEPTION(x) x = PyErr_NewException("zookeeper."#x, ZooKeeperException, NULL); \ Py_INCREF(x); \ PyModule_AddObject(module, #x, x); #if PY_MAJOR_VERSION >= 3 PyMODINIT_FUNC PyInit_zookeeper(void) { #else PyMODINIT_FUNC initzookeeper(void) { #endif PyEval_InitThreads(); #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&zookeeper_moddef); #else PyObject *module = Py_InitModule("zookeeper", ZooKeeperMethods); #endif if (init_zhandles(32) == 0) { #if PY_MAJOR_VERSION >= 3 Py_DECREF(module); return PyErr_NoMemory(); #else return; #endif } ZooKeeperException = PyErr_NewException("zookeeper.ZooKeeperException", PyExc_Exception, NULL); PyModule_AddObject(module, "ZooKeeperException", ZooKeeperException); Py_INCREF(ZooKeeperException); PyModule_AddStringConstant(module, "__version__", ZOO_VERSION); 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); #if PY_MAJOR_VERSION >= 3 return module; #endif } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/examples/README0100644 0000000 0000000 00000000315 15051152474 031566 0ustar00rootroot0000000 0000000 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 ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zkpython_src_examples_wat0100644 0000000 0000000 00000000165 15051152474 032676 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/examples/watch_znode_for_changes.py apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/examples/watch_znode_for_cha0100644 0000000 0000000 00000014620 15051152474 034623 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/setup.py0100755 0000000 0000000 00000004226 15051152474 032133 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 import os zookeeper_basedir = "../../" zookeeper_macros = [("THREADED", None)] # Assume the C extension includes OpenSSL support unless told # otherwise. if not os.environ.get("ZKPYTHON_NO_SSL"): zookeeper_macros.append(("HAVE_OPENSSL_H", True)) zookeepermodule = Extension("zookeeper", sources=["src/c/zookeeper.c"], define_macros=zookeeper_macros, include_dirs=[zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/include", zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/target/c", zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/generated"], libraries=["zookeeper_mt"], library_dirs=[zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/.libs/", zookeeper_basedir + "/zookeeper-client/zookeeper-client-c/target/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] ) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/python/zk.py0100755 0000000 0000000 00000004753 15051152474 031424 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/acl_test.py0100644 0000000 0000000 00000010505 15051152474 032221 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/async_test.py0100644 0000000 0000000 00000002410 15051152474 032573 0ustar00rootroot0000000 0000000 #!/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 = getattr(zookeeper, 'async')(self.handle, "/") self.assertEqual(ret, zookeeper.OK, "async failed") if __name__ == '__main__': unittest.main() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/callback_test.py0100644 0000000 0000000 00000015171 15051152474 033222 0ustar00rootroot0000000 0000000 #!/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: getattr(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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/clientid_test.py0100755 0000000 0000000 00000003313 15051152474 033257 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/close_deadlock_test.py0100644 0000000 0000000 00000003046 15051152474 034417 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/connection_test.py0100755 0000000 0000000 00000014313 15051152474 033625 0ustar00rootroot0000000 0000000 #!/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, sys if sys.version_info < (3,): range = xrange 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, "/") @unittest.skipUnless(hasattr(zookeeper, 'init_ssl'), "SSL support not compiled in.") def testsslconnection(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_ssl(self.sslhost, self.sslcert, connection_watcher) cv.wait(15.0) cv.release() self.assertEqual(self.connected, True, "SSL 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 range(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, self.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 range(9) ] 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, self.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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/create_test.py0100755 0000000 0000000 00000010112 15051152474 032722 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/delete_test.py0100755 0000000 0000000 00000005470 15051152474 032734 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/exists_test.py0100755 0000000 0000000 00000004737 15051152474 033016 0ustar00rootroot0000000 0000000 #!/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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/get_set_test.py0100755 0000000 0000000 00000021054 15051152474 033120 0ustar00rootroot0000000 0000000 #!/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, sys if sys.version_info < (3,): range = xrange 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_empty_node(self): """ Test for a bug when instead of empty string we can get random data from buffer malloc'ed to hold node contents. See ZOOKEEPER-1906 for details """ NODE_PATH = "/zk-python-test-empty-node" self.ensureDeleted(NODE_PATH) zookeeper.create(self.handle, NODE_PATH, "", [{"perms":0x1f, "scheme":"world", "id" :"anyone"}]) (data,stat) = zookeeper.get(self.handle, NODE_PATH, None) self.assertEqual(data, "", "Data is not empty as expected: " + data) 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) stat2 = zookeeper.set2(self.handle, "/zk-python-getsettest", "set2", stat["version"]) self.assertNotEqual(stat2, None, "set2 call failed, return should not be None") self.assertEqual(stat2["numChildren"], 0, "set2 call failed, numChildren not 0 in set2 call") (data,stat) = zookeeper.get(self.handle, "/zk-python-getsettest", None) self.assertEqual(data, "set2", "Data is not 'set2' as expected: " + data) 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 range(1024*1023)]) self.ensureDeleted("/zk-python-test-large-datanode") zookeeper.create(self.handle, "/zk-python-test-large-datanode", data, [{"perms":0x1f, "scheme":"world", "id" :"anyone"}]) (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() apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/run_tests.sh0100755 0000000 0000000 00000003136 15051152474 032440 0ustar00rootroot0000000 0000000 #!/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 set -e # 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 ./target/ -name 'zookeeper*.so' | head -1` PYTHONPATH=`dirname $SO_PATH` LIB_PATH=../../zookeeper-client/zookeeper-client-c/target/c/.libs for test in `ls $1/*_test.py`; do echo "Running $test" echo "Running LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test" LD_LIBRARY_PATH=$LIB_PATH:$LD_LIBRARY_PATH DYLD_LIBRARY_PATH=$LIB_PATH:$DYLD_LIBRARY_PATH PYTHONPATH=$PYTHONPATH python $test done apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zkServer.sh0100755 0000000 0000000 00000005051 15051152474 032223 0ustar00rootroot0000000 0000000 #!/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" exit 2 fi if [ "x${base_dir}" == "x" ] then PROJECT_ROOT="../../" else PROJECT_ROOT=${base_dir} fi WORK_DIR=${PROJECT_ROOT}/zookeeper-contrib/zookeeper-contrib-zkpython/target/zkpython_tests TEST_DIR=${PROJECT_ROOT}/zookeeper-contrib/zookeeper-contrib-zkpython/src/test if [ -r "${WORK_DIR}/../zk.pid" ] then pid=`cat "${WORK_DIR}/../zk.pid"` kill -9 $pid rm -f "${WORK_DIR}/../zk.pid" fi which lsof &> /dev/null if [ $? -eq 0 ] then pid=`lsof -i :22182 | grep LISTEN | awk '{print $2}'` if [ -n "$pid" ] then kill -9 $pid fi fi if [ "x$1" == "xstartClean" ] then rm -rf ${WORK_DIR} fi CLASSPATH="$CLASSPATH:${PROJECT_ROOT}/zookeeper-server/target/classes" CLASSPATH="$CLASSPATH:${zk_base}/conf" for i in "${PROJECT_ROOT}"/zookeeper-server/target/lib/*.jar do CLASSPATH="$CLASSPATH:$i" done for i in "${PROJECT_ROOT}"/zookeeper-server/src/main/resource/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) mkdir -p ${WORK_DIR}/zkdata rm -rf ${WORK_DIR}/ssl mkdir -p ${WORK_DIR}/ssl cp ${PROJECT_ROOT}/zookeeper-client/zookeeper-client-c/ssl/gencerts.sh ${WORK_DIR}/ssl/ cd ${WORK_DIR}/ssl/ ./gencerts.sh cd - sed "s#WORKDIR#${WORK_DIR}#g" ${TEST_DIR}/zoo.cfg > "${WORK_DIR}/zoo.cfg" java -Dzookeeper.extendedTypesEnabled=true -Dznode.container.checkIntervalMs=100 -cp $CLASSPATH org.apache.zookeeper.server.ZooKeeperServerMain "${WORK_DIR}/zoo.cfg" &> "${WORK_DIR}/zoo.log" & pid=$! echo -n $! > ${WORK_DIR}/../zk.pid sleep 5 ;; stop) # Already killed above ;; *) echo "Unknown command " + $1 exit 2 esac apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zktestbase.py0100755 0000000 0000000 00000007344 15051152474 032614 0ustar00rootroot0000000 0000000 #!/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): SERVER_PORT = 22182 SERVER_SSL_PORT = 22183 def __init__(self,methodName='runTest'): unittest.TestCase.__init__(self,methodName) self.host = "localhost:%d" % self.SERVER_PORT self.sslhost = "localhost:%d" % self.SERVER_SSL_PORT self.sslcert = "./target/zkpython_tests/ssl/server.crt,./target/zkpython_tests/ssl/client.crt,./target/zkpython_tests/ssl/clientkey.pem,password" 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) def all(self, iterable): for element in iterable: if not element: return False return True apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zkpython/src/test/zoo.cfg0100644 0000000 0000000 00000000540 15051152474 031337 0ustar00rootroot0000000 0000000 tickTime=500 initLimit=10 syncLimit=5 dataDir=WORKDIR/zkdata maxClientCnxns=200 clientPort=22182 secureClientPort=22183 serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory ssl.keyStore.location=WORKDIR/ssl/server.jks ssl.keyStore.password=password ssl.trustStore.location=WORKDIR/ssl/servertrust.jks ssl.trustStore.password=password apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/Makefile.am0100644 0000000 0000000 00000000112 15051152474 030644 0ustar00rootroot0000000 0000000 ## Process this file with automake to produce Makefile.in SUBDIRS = src apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/README.txt0100644 0000000 0000000 00000010161 15051152474 030313 0ustar00rootroot0000000 0000000 ========================================== 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 ../../zookeeper-client/zookeeper-client-c/target/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=../../zookeeper-client/zookeeper-client-c/target/c/.libs 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 12. ./src/zktreeutil --zookeeper=localhost:2188 --dump --ssl=/path/certs/root_ca.pem,/path/certs/node.crt,/path/certs/node.key # connect with ssl params to the secureClientPort apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/build.xml0100644 0000000 0000000 00000003773 15051152474 030451 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/configure.ac0100644 0000000 0000000 00000005256 15051152474 031114 0ustar00rootroot0000000 0000000 # -*- 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}/../../zookeeper-client/zookeeper-client-c AC_CHECK_LIB(zookeeper_mt, main, [ZOOKEEPER="-L${ZOOKEEPER_PATH}/target/c/.libs -lzookeeper_mt"],,["-L${ZOOKEEPER_PATH}/target/c/.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 dnl OpenSSL AC_ARG_WITH(openssl, [AC_HELP_STRING([--with-openssl[=DIR]], [build with openssl (autodetect openssl library by default) )])], [], [with_openssl=yes]) AC_MSG_NOTICE([configuring SSL using --with-openssl=$with_openssl]) saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" if test "x$with_openssl" != "xno" && test "x$with_openssl" != "xyes" ; then CPPFLAGS="$CPPFLAGS -I$with_openssl/include" LDFLAGS="$LDFLAGS -L$with_openssl/lib" fi have_openssl=no AC_CHECK_HEADER(openssl/ssl.h, [ AC_CHECK_LIB(ssl, SSL_CTX_new, [have_openssl=yes]) ]) if test "x$with_openssl" != "xno" && test "x$with_openssl" != "xyes" && test "x$have_openssl" != "xyes"; then CPPFLAGS="$saved_CPPFLAGS" LDFLAGS="$saved_LDFLAGS" fi if test "x$with_openssl" != xno && test "x$have_openssl" = xno; then AC_MSG_WARN([cannot build SSL support -- openssl not found]) with_openssl=no fi if test "x$with_openssl" != xno; then AC_MSG_NOTICE([building with SSL support]) else AC_MSG_NOTICE([building without SSL support]) fi AM_CONDITIONAL([WANT_OPENSSL],[test "x$with_openssl" != xno]) 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 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/Makefile.am0100644 0000000 0000000 00000002246 15051152474 031445 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 WANT_OPENSSL OPENSSL_CPPFLAGS = -DHAVE_OPENSSL_H endif AM_CXXFLAGS = -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ -I$(top_srcdir)/include -I${LOG4CXX_INCLUDE} -I/usr/include \ -I${XML2_INCLUDE} -DTHREADED $(OPENSSL_CPPFLAGS) bin_PROGRAMS = zktreeutil zktreeutil_SOURCES = ZkAdaptor.cc ZkTreeUtil.cc ZkTreeUtilMain.cc zktreeutil_LDADD = ${ZOOKEEPER} ${XML_LIBS} ${LOG4CXX} apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/SimpleTree.h0100644 0000000 0000000 00000011057 15051152474 031633 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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__ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.cc0100644 0000000 0000000 00000042142 15051152474 031616 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 #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 #ifdef HAVE_OPENSSL_H if (!m_zkConfig.getSslParams().empty()) { mp_zkHandle = zookeeper_init_ssl( m_zkConfig.getHosts().c_str(), m_zkConfig.getSslParams().c_str(), NULL, m_zkConfig.getLeaseTimeout(), 0, NULL, 0); } else { mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), NULL, m_zkConfig.getLeaseTimeout(), 0, NULL, 0); } #else mp_zkHandle = zookeeper_init( m_zkConfig.getHosts().c_str(), NULL, m_zkConfig.getLeaseTimeout(), 0, NULL, 0); #endif 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_NOTCONNECTED_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 existence 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' */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkAdaptor.h0100644 0000000 0000000 00000027157 15051152474 031471 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; * @param certs ssl parameters to initiate SSL connection; */ ZooKeeperConfig(const string &hosts, int leaseTimeout, bool autoReconnect = true, long long int connectTimeout = 15000, const string &sslParams = "") : m_hosts(hosts), m_leaseTimeout(leaseTimeout), m_autoReconnect(autoReconnect), m_connectTimeout(connectTimeout), m_sslParams(sslParams) {} /** * \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; } /** * \brief Returns the ssl params */ string getSslParams() const { return m_sslParams; } 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; /** * comma separated ssl parameters to initiate SSL connection. */ const string m_sslParams; }; /** * \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 existence 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__ */ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.cc0100644 0000000 0000000 00000060305 15051152474 031762 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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, const string& cert) { try { // Create an instance of ZK adapter. ZooKeeperConfig config (zkHosts, 10000, true, 15000, cert); 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, getSslParams()); std::cerr << "[zktreeutil] connected to ZK serverfor reading" << std::endl; // Check the existence 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, getSslParams()); 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, getSslParams()); 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, getSslParams()); 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; } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtil.h0100644 0000000 0000000 00000021655 15051152474 031631 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 cert certificate file path * @param a valid ZK handle */ static ZooKeeperAdapterSptr get_zkHandle (const string& zkHosts, const string& cert=""); 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; /** * \brief Sets the ssl params to be used for SSL connection * @param cert ssl params */ void setSslParams(const string& cert) { sslParams_ = cert; } /** * \brief Gets the ssl params * @return the cert */ string getSslParams() const { return sslParams_; } private: ZkTreeNodeSptr zkRootSptr_; // ZK tree root node bool loaded_; // Falg indicating whether ZK tree loaded into memory string sslParams_; // Comma separated parameters to initiate SSL connection }; } #endif // __ZK_TREE_UTIL_H__ apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/src/ZkTreeUtilMain.cc0100644 0000000 0000000 00000023313 15051152474 032565 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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'}, {"ssl", required_argument, 0, 's'}, {0, 0, 0, 0} }; static char *short_options = "IEUFDfx:p:d:hz:s:"; 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; std::cout << "\t--ssl= or -s : " << std::endl << "\t Comma separated parameters to initiate SSL connection." << std::endl << "\t e.g.: server_cert.crt,client_cert.crt,client_priv_key.pem,passwd" << 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 = "/"; string cert; 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 's': cert = optarg; break; case 'h': usage (argc, argv); exit(0); } } ZkTreeUtil zkTreeUtil; if (!cert.empty()) zkTreeUtil.setSslParams(cert); 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); } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zktreeutil/tests/zk_sample.xml0100644 0000000 0000000 00000003425 15051152474 032473 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/NOTICE.txt0100644 0000000 0000000 00000001424 15051152474 030675 0ustar00rootroot0000000 0000000 src/java/com/nitido/utils/toaster/Toaster.java: This java file is copyright by Daniele Piras ("danielepiras80", no email known) released under the Apache Software License 2.0 It has been downloaded in december 2009 from the CVS web interface of the sourceforge project http://sourceforge.net/projects/jtoaster/ . The web interface to CVS is not available anymore on sourceforge. The icons in src/main/resources/icons are taken from the Tango project downloaded from http://tango.freedesktop.org/releases on 2011-09-06. The Tango project is public domain. Distribution packagers should not include the icons in the package but rather depend on tango-icon-theme (Debian package name). ZooInspector will then try to get the icons from /usr/share/icons/Tango rather then from its jar file. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/README.txt0100644 0000000 0000000 00000013406 15051152474 030654 0ustar00rootroot0000000 0000000 ========================================== 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 - Pluggable DataEncryptionManagers to specify how data should be encrypted and decrypted in the Zookeeper instance - Browsable tree view of the ZooKeeper instance - View the data in a node - View the ACLs currently applied to a node - View the metadata for a node (Version, Number of Children, Last modified Time, etc.) - Pluggable NodeViewers interface - Ability to save/load and set default Node Viewers Maven Build Instructions ------------------------- 0. Pre-requisites - Run "mvn install" in the root directory of the Zookeeper project 1. Open a command line 2. cd into this directory 3. mvn install 4. To run ZooInspector run zooInspector.cmd (on Windows) or zooInspector.sh (on Linux) (these scripts will find the built JAR automatically in the "target" directory as long as you don't move it) Ant Build Instructions ----------------------- 0. Pre-requisites - The main zookeeper build script must have been run before building this module 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 associated 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 down 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.apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/TODO0100644 0000000 0000000 00000001510 15051152474 027637 0ustar00rootroot0000000 0000000 - replace JToaster with standard notifications, see: http://www.galago-project.org/specs/notification/ http://stackoverflow.com/questions/857154/freedesktop-org-notifications-in-java DBus and Java: http://bolta-gecko.blogspot.com/2009/06/using-d-bus-in-java.html http://dbus.freedesktop.org/doc/dbus-java/ (packaged in Debian) - properly respect http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html - Rename classes to avoid redundand "ZooInspector" prefix. - Ant build file has hard coded log4j dependency. (ZK will move to maven anyways...) - make directory for config files configurable via commandline parameter - Clean up the code! :-) apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/build.xml0100644 0000000 0000000 00000013754 15051152474 031005 0ustar00rootroot0000000 0000000 Tests failed! apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/ivy.xml0100644 0000000 0000000 00000003545 15051152474 030512 0ustar00rootroot0000000 0000000 ZooInspector ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_licences_Apa0100644 0000000 0000000 00000000173 15051152474 032553 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/licences/Apache Software Licence v2.0.txt apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/licences/Apache Software Lic0100644 0000000 0000000 00000026136 15051152474 034316 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/pom.xml0100755 0000000 0000000 00000007673 15051152474 030507 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-contrib 3.9.4 zookeeper-contrib-zooinspector jar Apache ZooKeeper - Contrib - ZooInspector ZooInspector is a Java Swing based application for browsing and editing ZooKeeper instances. 0.6 18.0 src/main/resources **/* org.apache.maven.plugins maven-assembly-plugin 3.1.1 jar-with-dependencies org.apache.zookeeper.inspector.ZooInspector make-assembly package single org.apache.zookeeper zookeeper ${project.version} org.slf4j slf4j-api ch.qos.logback logback-classic runtime true org.junit.vintage junit-vintage-engine test com.google.guava guava ${guava.version} org.apache.rat apache-rat-tasks ${rat.version} org.mockito mockito-core test ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000205 15051152474 032634 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/utils/toaster/Toaster.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/com/nitido/uti0100644 0000000 0000000 00000023355 15051152474 034406 0ustar00rootroot0000000 0000000 /** * This java file is copyright by Daniele Piras ("danielepiras80", no email known) released under the * Apache Software License 2.0. It has been downloaded in december 2009 from the CVS web interface * of the sourceforge project http://sourceforge.net/projects/jtoaster/ . The web interface to CVS * is not available anymore on sourceforge. * */ /** * Java Toaster is a java utility class for your swing applications * that show an animate box coming from the bottom of your screen * with a notification message and/or an associated image * (like msn online/offline notifications). * * Toaster panel in windows system follow the taskbar; So if * the taskbar is into the bottom the panel coming from the bottom * and if the taskbar is on the top then the panel coming from the top. * * This is a simple example of utilization: * * public class ToasterTest * { * * public static void main(String[] args) * { * // Initialize toaster manager... * Toaster toasterManager = new Toaster(); * * // Show a simple toaster * toasterManager.showToaster( new ImageIcon( "mylogo.gif" ), "A simple toaster with an image" ); * } * } */ package com.nitido.utils.toaster; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JWindow; import javax.swing.border.EtchedBorder; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.Insets; import java.awt.Rectangle; /** * Class to show tosters in multiplatform * */ public class Toaster { // Width of the toster private int toasterWidth = 300; // Height of the toster private int toasterHeight = 80; // Step for the toaster private int step = 20; // Step time private int stepTime = 20; // Show time private int displayTime = 3000; // Current number of toaster... private int currentNumberOfToaster = 0; // Last opened toaster private int maxToaster = 0; // Max number of toasters for the sceen private int maxToasterInSceen; // Font used to display message private Font font; // Color for border private Color borderColor; // Color for toaster private Color toasterColor; // Set message color private Color messageColor; // Set the margin int margin; // Flag that indicate if use alwaysOnTop or not. // method always on top start only SINCE JDK 5 ! boolean useAlwaysOnTop = true; private static final long serialVersionUID = 1L; /** * Constructor to initialized toaster component... * */ public Toaster() { // Set default font... font = new Font("Arial", Font.BOLD, 12); // Border color borderColor = new Color(245, 153, 15); toasterColor = Color.WHITE; messageColor = Color.BLACK; useAlwaysOnTop = true; // Verify AlwaysOnTop Flag... try { JWindow.class.getMethod( "setAlwaysOnTop", new Class[] { Boolean.class } ); } catch( Exception e ) { useAlwaysOnTop = false; } } /** * Class that rappresent a single toaster * */ class SingleToaster extends javax.swing.JWindow { private static final long serialVersionUID = 1L; // Label to store Icon private JLabel iconLabel = new JLabel(); // Text area for the message private JTextArea message = new JTextArea(); /*** * Simple costructor that initialized components... */ public SingleToaster() { initComponents(); } /*** * Function to initialized components */ private void initComponents() { setSize(toasterWidth, toasterHeight); message.setFont( getToasterMessageFont() ); JPanel externalPanel = new JPanel(new BorderLayout(1, 1)); externalPanel.setBackground( getBorderColor() ); JPanel innerPanel = new JPanel(new BorderLayout( getMargin(), getMargin() )); innerPanel.setBackground( getToasterColor() ); message.setBackground( getToasterColor() ); message.setMargin( new Insets( 2,2,2,2 ) ); message.setLineWrap( true ); message.setWrapStyleWord( true ); EtchedBorder etchedBorder = (EtchedBorder) BorderFactory .createEtchedBorder(); externalPanel.setBorder(etchedBorder); externalPanel.add(innerPanel); message.setForeground( getMessageColor() ); innerPanel.add(iconLabel, BorderLayout.WEST); innerPanel.add(message, BorderLayout.CENTER); getContentPane().add(externalPanel); } /*** * Start toaster animation... */ public void animate() { ( new Animation( this ) ).start(); } } /*** * Class that manage the animation */ class Animation extends Thread { SingleToaster toaster; public Animation( SingleToaster toaster ) { this.toaster = toaster; } /** * Animate vertically the toaster. The toaster could be moved from bottom * to upper or to upper to bottom * @param posx * @param fromy * @param toy * @throws InterruptedException */ protected void animateVertically( int posx, int fromY, int toY ) throws InterruptedException { toaster.setLocation( posx, fromY ); if ( toY < fromY ) { for (int i = fromY; i > toY; i -= step) { toaster.setLocation(posx, i); Thread.sleep(stepTime); } } else { for (int i = fromY; i < toY; i += step) { toaster.setLocation(posx, i); Thread.sleep(stepTime); } } toaster.setLocation( posx, toY ); } public void run() { try { boolean animateFromBottom = true; GraphicsEnvironment ge = GraphicsEnvironment .getLocalGraphicsEnvironment(); Rectangle screenRect = ge.getMaximumWindowBounds(); int screenHeight = (int) screenRect.height; int startYPosition; int stopYPosition; if ( screenRect.y > 0 ) { animateFromBottom = false; // Animate from top! } maxToasterInSceen = screenHeight / toasterHeight; int posx = (int) screenRect.width - toasterWidth - 1; toaster.setLocation(posx, screenHeight); toaster.setVisible(true); if ( useAlwaysOnTop ) { toaster.setAlwaysOnTop(true); } if ( animateFromBottom ) { startYPosition = screenHeight; stopYPosition = startYPosition - toasterHeight - 1; if ( currentNumberOfToaster > 0 ) { stopYPosition = stopYPosition - ( maxToaster % maxToasterInSceen * toasterHeight ); } else { maxToaster = 0; } } else { startYPosition = screenRect.y - toasterHeight; stopYPosition = screenRect.y; if ( currentNumberOfToaster > 0 ) { stopYPosition = stopYPosition + ( maxToaster % maxToasterInSceen * toasterHeight ); } else { maxToaster = 0; } } currentNumberOfToaster++; maxToaster++; animateVertically( posx, startYPosition, stopYPosition ); Thread.sleep(displayTime); animateVertically( posx, stopYPosition, startYPosition ); currentNumberOfToaster--; toaster.setVisible(false); toaster.dispose(); } catch (Exception e) { e.printStackTrace(); } } } /** * Show a toaster with the specified message and the associated icon. */ public void showToaster(Icon icon, String msg) { SingleToaster singleToaster = new SingleToaster(); if ( icon != null ) { singleToaster.iconLabel.setIcon( icon ); } singleToaster.message.setText( msg ); singleToaster.animate(); } /** * Show a toaster with the specified message. */ public void showToaster( String msg ) { showToaster( null, msg ); } /** * @return Returns the font */ public Font getToasterMessageFont() { // TODO Auto-generated method stub return font; } /** * Set the font for the message */ public void setToasterMessageFont( Font f) { font = f; } /** * @return Returns the borderColor. */ public Color getBorderColor() { return borderColor; } /** * @param borderColor The borderColor to set. */ public void setBorderColor(Color borderColor) { this.borderColor = borderColor; } /** * @return Returns the displayTime. */ public int getDisplayTime() { return displayTime; } /** * @param displayTime The displayTime to set. */ public void setDisplayTime(int displayTime) { this.displayTime = displayTime; } /** * @return Returns the margin. */ public int getMargin() { return margin; } /** * @param margin The margin to set. */ public void setMargin(int margin) { this.margin = margin; } /** * @return Returns the messageColor. */ public Color getMessageColor() { return messageColor; } /** * @param messageColor The messageColor to set. */ public void setMessageColor(Color messageColor) { this.messageColor = messageColor; } /** * @return Returns the step. */ public int getStep() { return step; } /** * @param step The step to set. */ public void setStep(int step) { this.step = step; } /** * @return Returns the stepTime. */ public int getStepTime() { return stepTime; } /** * @param stepTime The stepTime to set. */ public void setStepTime(int stepTime) { this.stepTime = stepTime; } /** * @return Returns the toasterColor. */ public Color getToasterColor() { return toasterColor; } /** * @param toasterColor The toasterColor to set. */ public void setToasterColor(Color toasterColor) { this.toasterColor = toasterColor; } /** * @return Returns the toasterHeight. */ public int getToasterHeight() { return toasterHeight; } /** * @param toasterHeight The toasterHeight to set. */ public void setToasterHeight(int toasterHeight) { this.toasterHeight = toasterHeight; } /** * @return Returns the toasterWidth. */ public int getToasterWidth() { return toasterWidth; } /** * @param toasterWidth The toasterWidth to set. */ public void setToasterWidth(int toasterWidth) { this.toasterWidth = toasterWidth; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000220 15051152474 032631 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/ZooInspector.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000005200 15051152474 034345 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.IconResource; import org.apache.zookeeper.inspector.gui.ZooInspectorPanel; import org.apache.zookeeper.inspector.logger.LoggerFactory; import org.apache.zookeeper.inspector.manager.ZooInspectorManagerImpl; /** * */ public class ZooInspector { public static IconResource iconResource; /** * @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); iconResource = new IconResource(); final ZooInspectorPanel zooInspectorPanel = new ZooInspectorPanel( new ZooInspectorManagerImpl(), iconResource); 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); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000251 15051152474 032635 xustar000000000 0000000 169 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/BasicDataEncryptionManager.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000003120 15051152474 034344 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 { if(encrypted == null) { return ""; } 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000244 15051152474 032637 xustar000000000 0000000 164 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/encryption/DataEncryptionManager.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000002557 15051152474 034361 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000224 15051152474 032635 xustar000000000 0000000 148 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/IconResource.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000010205 15051152474 034346 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.io.File; import java.net.MalformedURLException; import java.net.URL; import javax.swing.ImageIcon; import org.apache.zookeeper.inspector.logger.LoggerFactory; /** * @link http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html * I tried to take icons that are available in the Tango icon set. * * @link http://tango.freedesktop.org/Tango_Icon_Library * The Tango icon set can be found under the "Download" section. */ public class IconResource { public static final String ICON_CHANGE_NODE_VIEWERS = "categories/applications-system"; public static final String ICON_TREE_LEAF = "mimetypes/text-x-generic"; public static final String ICON_TREE_OPEN = "places/folder"; public static final String ICON_TREE_CLOSE = "places/folder"; public static final String ICON_INFORMATION = "status/info"; public static final String ICON_SAVE = "actions/document-save"; public static final String ICON_UP = "actions/up"; public static final String ICON_DOWN = "actions/down"; public static final String ICON_ADD = "actions/add"; public static final String ICON_REMOVE = "actions/remove"; public static final String ICON_START = "actions/media-playback-start"; public static final String ICON_STOP = "actions/media-playback-stop"; public static final String ICON_DOCUMENT_ADD = "actions/document-new"; public static final String ICON_REFRESH = "actions/view-refresh"; public static final String ICON_TRASH = "places/user-trash"; // better: actions/help-about, but not in tango public static final String ICON_HELP_ABOUT = "status/info"; private static final String DEFAULT_THEME = "Tango"; private static final String DEFAULT_SIZE = "16x16"; private static final String FALLBACK_ICON = "face-surprise"; // compare http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html private static final String[] DEFAULT_XDG_DATA_DIRS = new String[]{ "/usr/local/share", "/usr/share" }; private String theme = DEFAULT_THEME; private String size = DEFAULT_SIZE; public URL find(String name) { String iconPath = buildIconPath(name); URL iconUrl = findInPaths(iconPath); if(null != iconUrl) return iconUrl; iconUrl = getClass().getResource(iconPath); if(null != iconUrl) return iconUrl; if(!name.equals(FALLBACK_ICON)) return find(FALLBACK_ICON); return null; } public ImageIcon get(String name, String description) { URL iconUrl = find(name); if(null==iconUrl) { ImageIcon icon = new ImageIcon(); icon.setDescription(description); return icon; } else { return new ImageIcon(iconUrl, description); } } private URL findInPaths(String iconPath) { for(String dataDir : DEFAULT_XDG_DATA_DIRS) { File file = new File(dataDir + iconPath); if(file.exists()) { try { return file.toURI().toURL(); } catch (MalformedURLException e) { LoggerFactory.getLogger().warn(e.toString()); } } } return null; } private String buildIconPath(String name) { return "/icons/" + theme + "/" + size + "/" + name + ".png"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000241 15051152474 032634 xustar000000000 0000000 161 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/NodeViewersChangeListener.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000002702 15051152474 034351 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000217 15051152474 032637 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/Toolbar.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000006230 15051152474 034351 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.event.ActionListener; import java.util.HashMap; import java.util.Map; import javax.swing.JButton; import javax.swing.JToolBar; public class Toolbar { private final IconResource iconResource; private final JToolBar toolbar = new JToolBar(); private final Map buttons = new HashMap(); private static final Button[] buttonsToToggle = new Button[] { Button.connect, Button.disconnect, Button.refresh, Button.addNode, Button.deleteNode }; public Toolbar(IconResource iconResource) { this.iconResource = iconResource; init(); } public void addActionListener(Button button, ActionListener actionListener) { buttons.get(button).addActionListener(actionListener); } public JToolBar getJToolBar() { return toolbar; } public void toggleButtons(boolean connected) { for(Button button : buttonsToToggle) { buttons.get(button).setEnabled(connected != button.enabled); } } private void init() { toolbar.setFloatable(false); for(Button button : Button.values()) { JButton jbutton = button.createJButton(iconResource); buttons.put(button, jbutton); toolbar.add(jbutton); } } public static enum Button { connect("Connect", IconResource.ICON_START, true), disconnect("Disconnect", IconResource.ICON_STOP, false), refresh("Refresh All", IconResource.ICON_REFRESH, false), addNode("Add Node", IconResource.ICON_DOCUMENT_ADD, false), deleteNode("Delete Node", IconResource.ICON_TRASH, false), nodeViewers("Change Node Viewers", IconResource.ICON_CHANGE_NODE_VIEWERS, true), about("About ZooInspector", IconResource.ICON_HELP_ABOUT, true); private String toolTip; private String icon; private boolean enabled; Button(String toolTip, String icon, boolean enabled) { this.toolTip = toolTip; this.icon = icon; this.enabled = enabled; } public JButton createJButton(IconResource iconResource) { JButton jbutton = new JButton(iconResource.get(icon, toolTip)); jbutton.setEnabled(enabled); jbutton.setToolTipText(toolTip); return jbutton; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000237 15051152474 032641 xustar000000000 0000000 159 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorAboutDialog.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000005701 15051152474 034353 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.File; 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 { private static final File aboutHtmlFile = new File("./src/main/resources/about.html"); /** * @param frame * - the Frame from which the dialog is displayed */ public ZooInspectorAboutDialog(Frame frame, IconResource iconResource) { super(frame); this.setLayout(new BorderLayout()); this.setIconImage(iconResource.get(IconResource.ICON_INFORMATION, "About ZooInspector").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); try { aboutPane.setPage(aboutHtmlFile.toURI().toURL()); } 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000256 15051152474 032642 xustar000000000 0000000 174 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorConnectionPropertiesDialog.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000031345 15051152474 034356 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 Map components; /** * @param lastConnectionProps * - the last connection properties used. if this is the first * connection 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000245 15051152474 032640 xustar000000000 0000000 165 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersDialog.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000064044 15051152474 034360 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.HashMap; import java.util.List; import java.util.Map; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.DropMode; import javax.swing.ImageIcon; 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 JList viewersList; private final JFileChooser fileChooser = new JFileChooser(new File(".")); private final Map buttons = new HashMap(); /** * @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, final IconResource iconResource) { super(frame); final List newViewers = new ArrayList( currentViewers); this.setLayout(new BorderLayout()); this.setIconImage(iconResource.get(IconResource.ICON_CHANGE_NODE_VIEWERS,"Change Node Viewers") .getImage()); this.setTitle("Change Node Viewers"); 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); final JTextField newViewerTextField = new JTextField(); for(Button button : Button.values()) { JButton jbutton = button.createJButton(iconResource); buttons.put(button, jbutton); } 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(buttons.get(Button.up), 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(buttons.get(Button.down), 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(buttons.get(Button.remove), 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(buttons.get(Button.add), c6); buttons.get(Button.up).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); } } }); buttons.get(Button.down).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); } } }); buttons.get(Button.remove).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); } } }); buttons.get(Button.add).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); } } } }); 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(buttons.get(Button.save), 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(buttons.get(Button.load), 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(buttons.get(Button.setDefaults), c9); buttons.get(Button.save).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 viewer configuration from file.", ex); JOptionPane.showMessageDialog( ZooInspectorNodeViewersDialog.this, "Error saving node viewer configuration from file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } } }); buttons.get(Button.load).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 viewer configuration from file.", ex); JOptionPane.showMessageDialog( ZooInspectorNodeViewersDialog.this, "Error loading node viewer configuration from file: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } }); buttons.get(Button.setDefaults).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 viewer configuration.", ex); JOptionPane.showMessageDialog( ZooInspectorNodeViewersDialog.this, "Error setting default node viewer 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) { JButton removeButton = buttons.get(Button.remove); JButton upButton = buttons.get(Button.up); JButton downButton = buttons.get(Button.down); 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); } } } public static enum Button { up("Move currently selected node viewer up",IconResource.ICON_UP,false), down("Move currently selected node viewer down",IconResource.ICON_DOWN,false), add("Add node viewer",IconResource.ICON_ADD,true), remove("Remove currently selected node viewer",IconResource.ICON_REMOVE,false), save("Save current node viewer configuration to file","Save"), load("Load node viewer configuration from file","Load"), setDefaults("Set current configuration asd defaults","Set as defaults"); private String toolTip; private String icon; private boolean enabled; Button(String toolTip, String icon, boolean enabled) { this.toolTip = toolTip; this.icon = icon; this.enabled = enabled; } Button(String toolTip, String icon) { this(toolTip, icon, true); } public JButton createJButton(IconResource iconResource) { ImageIcon imageIcon = iconResource.get(icon, toolTip); JButton jbutton; if(imageIcon == null) { jbutton = new JButton(icon); } else { jbutton = new JButton(imageIcon); } jbutton.setEnabled(enabled); jbutton.setToolTipText(toolTip); return jbutton; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000244 15051152474 032637 xustar000000000 0000000 164 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorNodeViewersPanel.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000010214 15051152474 034346 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 org.apache.zookeeper.inspector.gui.nodeviewer.NodeSelectionListener; 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 ChangeListener, NodeSelectionListener { private final List nodeViewers = 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 nodeViewers * - the {@link ZooInspectorNodeViewer}s to show */ public ZooInspectorNodeViewersPanel( ZooInspectorNodeManager zooInspectorManager, List nodeViewers) { this.zooInspectorManager = zooInspectorManager; this.setLayout(new BorderLayout()); tabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT); setNodeViewers(nodeViewers); tabbedPane.addChangeListener(this); this.add(tabbedPane, BorderLayout.CENTER); reloadSelectedViewer(); } /** * @param nodeViewers * - the {@link ZooInspectorNodeViewer}s to show */ public void setNodeViewers(List nodeViewers) { this.nodeViewers.clear(); this.nodeViewers.addAll(nodeViewers); needsReload.clear(); tabbedPane.removeAll(); for (ZooInspectorNodeViewer nodeViewer : nodeViewers) { 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.nodeViewers.get(index); viewer.nodeSelectionChanged(selectedNodes); this.needsReload.set(index, false); } } /** * @see org.apache.zookeeper.inspector.gui.nodeviewer.NodeSelectionListener#nodePathSelected(String) */ @Override public void nodePathSelected(String nodePath) { selectedNodes.clear(); selectedNodes.add(nodePath); 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(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000231 15051152474 032633 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorPanel.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000023121 15051152474 034347 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; 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 { /** Control how fast the scroll bar in the tree view window moves */ private static final int TREE_SCROLL_UNIT_INCREMENT = 16; private final IconResource iconResource; private final Toolbar toolbar; private final ZooInspectorNodeViewersPanel nodeViewersPanel; private final ZooInspectorTreeView treeViewer; private final ZooInspectorManager zooInspectorManager; private final List listeners = new ArrayList(); { listeners.add(this); } /** * @param zooInspectorManager * - the {@link ZooInspectorManager} for the application */ public ZooInspectorPanel(final ZooInspectorManager zooInspectorManager, final IconResource iconResource) { this.zooInspectorManager = zooInspectorManager; this.iconResource = iconResource; toolbar = new Toolbar(iconResource); final List 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); this.treeViewer = new ZooInspectorTreeView(zooInspectorManager, iconResource); this.treeViewer.addNodeSelectionListener(this.nodeViewersPanel); this.setLayout(new BorderLayout()); toolbar.addActionListener(Toolbar.Button.connect, new ActionListener() { public void actionPerformed(ActionEvent e) { ZooInspectorConnectionPropertiesDialog zicpd = new ZooInspectorConnectionPropertiesDialog( zooInspectorManager.getLastConnectionProps(), zooInspectorManager.getConnectionPropertiesTemplate(), ZooInspectorPanel.this); zicpd.setVisible(true); } }); toolbar.addActionListener(Toolbar.Button.disconnect, new ActionListener() { public void actionPerformed(ActionEvent e) { disconnect(); } }); toolbar.addActionListener(Toolbar.Button.refresh, new ActionListener() { public void actionPerformed(ActionEvent e) { treeViewer.initialize(); } }); toolbar.addActionListener(Toolbar.Button.addNode, new ActionListener() { public void actionPerformed(ActionEvent e) { treeViewer.createNode(); } }); toolbar.addActionListener(Toolbar.Button.deleteNode, new ActionListener() { public void actionPerformed(ActionEvent e) { treeViewer.deleteNode(); } }); toolbar.addActionListener(Toolbar.Button.nodeViewers, new ActionListener() { public void actionPerformed(ActionEvent e) { ZooInspectorNodeViewersDialog nvd = new ZooInspectorNodeViewersDialog( JOptionPane.getRootFrame(), nodeViewers, listeners, zooInspectorManager, iconResource); nvd.setVisible(true); } }); toolbar.addActionListener(Toolbar.Button.about, new ActionListener() { public void actionPerformed(ActionEvent e) { ZooInspectorAboutDialog zicpd = new ZooInspectorAboutDialog( JOptionPane.getRootFrame(), iconResource); zicpd.setVisible(true); } }); JScrollPane treeScroller = new JScrollPane(treeViewer); treeScroller.getVerticalScrollBar().setUnitIncrement(TREE_SCROLL_UNIT_INCREMENT); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeScroller, nodeViewersPanel); splitPane.setResizeWeight(0.25); this.add(splitPane, BorderLayout.CENTER); this.add(toolbar.getJToolBar(), 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.initialize(); toolbar.toggleButtons(true); } else { JOptionPane.showMessageDialog(ZooInspectorPanel.this, "Unable to connect to zookeeper", "Error", JOptionPane.ERROR_MESSAGE); } } catch (InterruptedException | 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.clear(); toolbar.toggleButtons(false); } } catch (InterruptedException | 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); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000234 15051152474 032636 xustar000000000 0000000 156 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/ZooInspectorTreeView.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000061544 15051152474 034362 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 com.nitido.utils.toaster.Toaster; import org.apache.zookeeper.inspector.gui.nodeviewer.NodeSelectionListener; import org.apache.zookeeper.inspector.logger.LoggerFactory; import org.apache.zookeeper.inspector.manager.NodeListener; import org.apache.zookeeper.inspector.manager.Pair; import org.apache.zookeeper.inspector.manager.ZooInspectorManager; import javax.swing.ImageIcon; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; public class ZooInspectorTreeView extends JPanel { private static final String PATH_SEPARATOR = "/"; private static final String ROOT_PATH = PATH_SEPARATOR; private final JTree tree; private final ZooInspectorTreeModel treeModel; private final JPopupMenu rightClickMenu; private final JMenuItem createChildNodeMenuItem; private final JMenuItem deleteNodeMenuItem; private final JMenuItem refreshNodeMenuItem; private final JMenuItem addWatchMenuItem; private final JMenuItem removeWatchMenuItem; private final Toaster toasterManager; private final ImageIcon toasterIcon; private final List nodeSelectionListeners = new LinkedList<>(); public ZooInspectorTreeView(final ZooInspectorManager manager, IconResource iconResource) { this.toasterManager = new Toaster(); this.toasterManager.setBorderColor(Color.BLACK); this.toasterManager.setMessageColor(Color.BLACK); this.toasterManager.setToasterColor(Color.WHITE); this.toasterIcon = iconResource.get(IconResource.ICON_INFORMATION, ""); // Set up tree to display all ZNodes this.treeModel = new ZooInspectorTreeModel(manager); this.tree = new JTree(); this.tree.setEditable(false); this.tree.setFocusable(true); this.tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); this.tree.setModel(this.treeModel); this.tree.setCellRenderer(new ZooInspectorTreeCellRenderer(iconResource)); // Set up right click menu for individual ZNodes this.rightClickMenu = new JPopupMenu(); this.createChildNodeMenuItem = new JMenuItem("Create Child Node"); this.deleteNodeMenuItem = new JMenuItem("Delete Node"); this.refreshNodeMenuItem = new JMenuItem("Refresh Node"); this.addWatchMenuItem = new JMenuItem("Add Watch"); this.removeWatchMenuItem = new JMenuItem("Remove Watch"); // Add various event listeners (all implemented as inner classes on this class) TreeEventHandler treeHandler = new TreeEventHandler(); MouseEventHandler mouseHandler = new MouseEventHandler(); KeyEventHandler keyHandler = new KeyEventHandler(); ActionEventHandler actionHandler = new ActionEventHandler(); this.tree.addKeyListener(keyHandler); this.tree.addTreeExpansionListener(treeHandler); this.tree.getSelectionModel().addTreeSelectionListener(treeHandler); this.tree.addMouseListener(mouseHandler); this.rightClickMenu.add(this.createChildNodeMenuItem).addActionListener(actionHandler); this.rightClickMenu.add(this.deleteNodeMenuItem).addActionListener(actionHandler); this.rightClickMenu.add(this.refreshNodeMenuItem).addActionListener(actionHandler); this.rightClickMenu.add(this.addWatchMenuItem).addActionListener(actionHandler); this.rightClickMenu.add(this.removeWatchMenuItem).addActionListener(actionHandler); setLayout(new BorderLayout()); add(this.tree, BorderLayout.CENTER); } public void addNodeSelectionListener(NodeSelectionListener l) { if (!this.nodeSelectionListeners.contains(l)) { this.nodeSelectionListeners.add(l); } } @SuppressWarnings("unused") public void removeNodeSelectionListener(NodeSelectionListener l) { this.nodeSelectionListeners.remove(l); } ///////////////////////////////// EVENT HANDLERS ///////////////////////////////// private class NodeEventHandler implements NodeListener { @Override public void processEvent(String nodePath, String eventType, Map eventInfo) { StringBuilder sb = new StringBuilder(256); 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()); } } toasterManager.showToaster(toasterIcon, sb.toString()); } } public class TreeEventHandler implements TreeExpansionListener, TreeSelectionListener { @Override public void treeExpanded(TreeExpansionEvent event) { ZooInspectorTreeNode expandingNode = (ZooInspectorTreeNode) event.getPath().getLastPathComponent(); // This whole chunk of code before the "refreshNode" call is to deal with the fact that when lazy-loading a // node, we first give it a single "placeholder" empty child node to mark it as having children, but we don't // actually load those children until the user decides to expand it. These "if" statements figure out if the // node has a single placeholder child or not. if (expandingNode.isLeaf() || expandingNode.getChildCount() != 1) { return; } ZooInspectorTreeNode onlyChild = ((ZooInspectorTreeNode) expandingNode.getChildAt(0)); if (!onlyChild.isPlaceholder()) { return; } treeModel.refreshNode(expandingNode); } @Override public void treeCollapsed(TreeExpansionEvent event) { } @Override public void valueChanged(TreeSelectionEvent e) { ZooInspectorTreeNode node = (ZooInspectorTreeNode) e.getPath().getLastPathComponent(); String selectedPath = node.getPathString(); for (NodeSelectionListener listener : nodeSelectionListeners) { listener.nodePathSelected(selectedPath); } } } private class KeyEventHandler extends KeyAdapter { @Override public void keyReleased(KeyEvent e) { if (!tree.hasFocus()) { return; } switch (e.getKeyCode()) { case KeyEvent.VK_D: deleteNode(); break; case KeyEvent.VK_N: createNode(); break; case KeyEvent.VK_R: refreshNode(); break; default: break; } } } private class ActionEventHandler implements ActionListener { @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == createChildNodeMenuItem) { createNode(); } else if (e.getSource() == deleteNodeMenuItem) { deleteNode(); } else if (e.getSource() == refreshNodeMenuItem) { refreshNode(); } else if (e.getSource() == addWatchMenuItem) { addWatch(); } else if (e.getSource() == removeWatchMenuItem) { removeWatch(); } } } private class MouseEventHandler extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { ZooInspectorTreeNode selectedNode = getSelectedNode(); if (selectedNode == null) { //If there's no node currently selected, see if the mouse is on top of one and select it int selectedRow = tree.getRowForLocation(e.getX(), e.getY()); if (selectedRow == -1) { return; } tree.setSelectionRow(selectedRow); TreePath selectedPath = tree.getPathForLocation(e.getX(), e.getY()); tree.setSelectionPath(selectedPath); } boolean shouldShowPopup = e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e); if (shouldShowPopup) { rightClickMenu.show(ZooInspectorTreeView.this, e.getX(), e.getY()); } } } ///////////////////////////////// BUSINESS LOGIC ///////////////////////////////// /** * Initialize the view by creating a completely new tree by removing and refreshing all nodes. */ public void initialize() { this.treeModel.init(); } /** * Clear all the existing nodes from the tree view. */ public void clear() { this.treeModel.clear(); } /** * Start the UI workflow for creating a new ZNode as a child of the selected node. */ public void createNode() { ZooInspectorTreeNode parentNode = getSelectedNode(); if (parentNode == null) { return; } final String newNodeName = JOptionPane.showInputDialog( this, "Please enter a name for the new node: ", "Create Child Node", JOptionPane.INFORMATION_MESSAGE); if (newNodeName == null || newNodeName.trim().isEmpty()) { return; } this.treeModel.createNode(parentNode, newNodeName); } /** * Start the UI workflow for deleting the selected node. */ public void deleteNode() { ZooInspectorTreeNode nodeToDelete = getSelectedNode(); if (nodeToDelete == null) { return; } int answer = JOptionPane.showConfirmDialog( this, "Are you sure you want to delete the selected node '" + nodeToDelete.getPathString() + "'?\n" + "(This action cannot be reverted)", "Confirm Delete", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); if (answer != JOptionPane.YES_OPTION) { return; } this.treeModel.deleteNode(nodeToDelete); } /** * Refresh the selected node (i.e. deleting all children and re-fetch them from Zookeeper). */ public void refreshNode() { ZooInspectorTreeNode nodeToRefresh = getSelectedNode(); if (nodeToRefresh == null) { return; } this.treeModel.refreshNode(nodeToRefresh); } /** * Add a Zookeeper watch to the selected node. */ public void addWatch() { ZooInspectorTreeNode nodeToWatch = getSelectedNode(); if (nodeToWatch == null) { return; } this.treeModel.addWatch(nodeToWatch); } /** * Remove a Zookeeper watch from the selected node (has no effect if the node does not have an existing watch). */ public void removeWatch() { ZooInspectorTreeNode nodeToUnwatch = getSelectedNode(); if (nodeToUnwatch == null) { return; } this.treeModel.removeWatch(nodeToUnwatch); } /** * @return The node object corresponding to the selected node or null if there is no node selected. */ private ZooInspectorTreeNode getSelectedNode() { TreePath selected = this.tree.getSelectionPath(); return selected != null ? ((ZooInspectorTreeNode) selected.getLastPathComponent()) : null; } private void showWarnDialog(String message){ JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE); } ///////////////////////////////// BACKING DATA MODEL ///////////////////////////////// /** * An implement of the backing TreeModel data for the JTree in the user interface. Controls what data is actually * available to be rendered in the UI. */ private class ZooInspectorTreeModel extends DefaultTreeModel { private final ZooInspectorManager manager; public ZooInspectorTreeModel(ZooInspectorManager manager) { super(new ZooInspectorTreeNode(ROOT_PATH, ROOT_PATH, 0)); this.manager = manager; } /** * Create a new ZNode in Zookeeper as a child of the given parent node. * * @param parentNode The parent node to create a new child ZNode underneath * @param newNodeName The name of the new child ZNode to create */ public void createNode(ZooInspectorTreeNode parentNode, String newNodeName) { SwingWorker worker = new SwingWorker() { @Override protected Boolean doInBackground() { //runs on a background non-UI thread return manager.createNode(parentNode.getPathString(), newNodeName); } @Override protected void done() { //runs on the UI event thread boolean success; try { success = get(); } catch (Exception e) { success = false; LoggerFactory.getLogger().error("create fail for {} {}", parentNode, newNodeName, e); showWarnDialog("create " + newNodeName + " in " + parentNode + " fail, exception is " + e.getMessage()); } if (!success) { showWarnDialog("create " + newNodeName + " in " + parentNode + " fail, see log for more detail"); } else { //extra logic to find the correct spot alphabetically to insert the new node in the tree` int i = 0; for (; i < parentNode.getChildCount(); i++) { ZooInspectorTreeNode existingChild = (ZooInspectorTreeNode) parentNode.getChildAt(i); if (newNodeName.compareTo(existingChild.getName()) < 0) { break; } } insertNodeInto(new ZooInspectorTreeNode(newNodeName, parentNode, 0), parentNode, i); parentNode.setNumDisplayChildren(parentNode.getNumDisplayChildren() + 1); } getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }; getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); worker.execute(); } /** * Delete the specified ZNode in Zookeeper. * * @param nodeToDelete The node to delete. */ public void deleteNode(ZooInspectorTreeNode nodeToDelete) { SwingWorker worker = new SwingWorker() { @Override protected Boolean doInBackground() { //runs on a background non-UI thread return manager.deleteNode(nodeToDelete.getPathString()); } @Override protected void done() { //runs on the UI event thread ZooInspectorTreeNode parent = (ZooInspectorTreeNode) nodeToDelete.getParent(); parent.setNumDisplayChildren(parent.getNumDisplayChildren() - 1); removeNodeFromParent(nodeToDelete); getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }; getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); worker.execute(); } /** * Refresh the specified node in the UI. Refresh is equivalent to removing all existing children from the UI * view and re-fetching them from Zookeeper. * * @param nodeToRefresh The node whose subtree should be refreshed. */ public void refreshNode(ZooInspectorTreeNode nodeToRefresh) { SwingWorker worker = new SwingWorker() { final LinkedList> childrenToAdd = new LinkedList<>(); @Override protected Boolean doInBackground() { //runs on a background non-UI thread //Make all the network calls here (to get children and their child counts) and collect the children, //but we can't add them to the UI until we're back on the event thread in done() List children = manager.getChildren(nodeToRefresh.getPathString()); if (children == null) { return false; } nodeToRefresh.setNumDisplayChildren(children.size()); for (String childName : children) { ZooInspectorTreeNode childNode = new ZooInspectorTreeNode(childName, nodeToRefresh, 0); int numChildren = manager.getNumChildren(childNode.getPathString()); childrenToAdd.add(new Pair<>(childName, numChildren)); } return true; } @Override protected void done() { //runs on the UI event thread nodeToRefresh.removeAllChildren(); for (Pair childPair : childrenToAdd) { ZooInspectorTreeNode childNode = new ZooInspectorTreeNode(childPair.getKey(), nodeToRefresh, childPair.getValue()); if (childPair.getValue() > 0) { // add a placeholder child so the UI renders this node like it has children childNode.add(new ZooInspectorTreeNode()); } nodeToRefresh.add(childNode); } reload(nodeToRefresh); getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } }; getRootPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); worker.execute(); } /** * Add a Zookeeper watch to the specified node (which will display updates in a Toast) * * @param nodeToWatch A reference to the node to place a watch on */ public void addWatch(ZooInspectorTreeNode nodeToWatch) { this.manager.addWatchers(Collections.singletonList(nodeToWatch.getPathString()), new NodeEventHandler()); } /** * Remove a Zookeeper watch from the specified node (has no effect if this node has no existing watch) * * @param nodeToUnwatch A reference to the node to remove a watch from */ public void removeWatch(ZooInspectorTreeNode nodeToUnwatch) { this.manager.removeWatchers(Collections.singletonList(nodeToUnwatch.getPathString())); } /** * Reinitialize the entire tree by refreshing the root node. */ public void init() { refreshNode((ZooInspectorTreeNode) getRoot()); } /** * Clear the tree by removing all children from the root node. */ public void clear() { ZooInspectorTreeNode root = (ZooInspectorTreeNode) getRoot(); root.setNumDisplayChildren(0); root.removeAllChildren(); reload(); } } /** * A representation of a single node in the TreeModel. Keeps track of a node's name, the number of children * it has and its full Zookeeper path. */ private static class ZooInspectorTreeNode extends DefaultMutableTreeNode { private final String name; private final String pathString; private int numDisplayChildren; public ZooInspectorTreeNode() { this("", "", 0); } public ZooInspectorTreeNode(String name, ZooInspectorTreeNode parent, int numDisplayChildren) { this(name, (PATH_SEPARATOR.equals(parent.getName()) ? "" : parent.getPathString()) + PATH_SEPARATOR + name, numDisplayChildren); } public ZooInspectorTreeNode(String name, String pathString, int numDisplayChildren) { this.name = name; this.pathString = pathString; this.numDisplayChildren = numDisplayChildren; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ZooInspectorTreeNode that = (ZooInspectorTreeNode) o; return this.pathString.equals(that.pathString); } @Override public int hashCode() { return this.pathString.hashCode(); } public boolean isPlaceholder() { //A placeholder node renders as "Loading..." on the UI and is identified by having an empty name and path return this.name.isEmpty() && this.getPathString().isEmpty(); } public String getName() { return this.name; } public String getPathString() { return this.pathString; } public int getNumDisplayChildren() { return this.numDisplayChildren; } public void setNumDisplayChildren(int numDisplayChildren) { this.numDisplayChildren = numDisplayChildren; } @Override public String toString() { //NOTE: Don't mess with this; it's actually used to construct the TreePath entries; if you want to //change the name on the UI display, use the TreeCellRenderer below return this.name; } } /** * A class that controls how a given tree node is rendered in the tree on the UI (this is what's responsible for * drawing "Loading..." for a placeholder node or render the # of children for a node). */ private static class ZooInspectorTreeCellRenderer extends DefaultTreeCellRenderer { public ZooInspectorTreeCellRenderer(IconResource iconResource) { setLeafIcon(iconResource.get(IconResource.ICON_TREE_LEAF, "")); setOpenIcon(iconResource.get(IconResource.ICON_TREE_OPEN, "")); setClosedIcon(iconResource.get(IconResource.ICON_TREE_CLOSE, "")); } @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { ZooInspectorTreeNode node = (ZooInspectorTreeNode) value; String text = node.getName(); if (node.isPlaceholder()) { text = "Loading..."; } if (node.getNumDisplayChildren() > 0) { text += " (" + node.getNumDisplayChildren() + ")"; } return super.getTreeCellRendererComponent(tree, text, sel, expanded, leaf, row, hasFocus); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000215 15051152474 032635 xustar000000000 0000000 141 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/about.html apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000001724 15051152474 034354 0ustar00rootroot0000000 0000000 ZooInspector v0.1

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

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

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

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

./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000250 15051152474 032634 xustar000000000 0000000 168 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeSelectionListener.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000002143 15051152474 034350 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * An interface to be implented by any component that needs notification when a new element * is selected in the UI JTree representing the set of available ZNodes. */ public interface NodeSelectionListener { void nodePathSelected(String nodePath); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000240 15051152474 032633 xustar000000000 0000000 160 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerACL.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000016555 15051152474 034364 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000241 15051152474 032634 xustar000000000 0000000 161 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerData.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000011721 15051152474 034352 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.ZooInspector; import org.apache.zookeeper.inspector.gui.IconResource; 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(ZooInspector.iconResource.get(IconResource.ICON_SAVE,"")); saveButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (selectedNode != null) { if (JOptionPane.showConfirmDialog(NodeViewerData.this, "Are you sure you want to save the node '" + selectedNode + "'?\n" + " (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 | 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000245 15051152474 032640 xustar000000000 0000000 165 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/NodeViewerMetaData.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000017532 15051152474 034360 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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(formatValByKeyType(entry.getKey(),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(); } private String formatValByKeyType(String key, String value) { if(key==null) return value; String formatedVal=value; switch (key){ case "Ephemeral Owner": try{ formatedVal = String.format("0x%x", Long.parseLong(value)); }catch (NumberFormatException e){ LoggerFactory.getLogger().warn("parse {}'s value {} to hex fail",key,value,e); } break; } return formatedVal; } }; 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000251 15051152474 032635 xustar000000000 0000000 169 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/gui/nodeviewer/ZooInspectorNodeViewer.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000010122 15051152474 034344 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000230 15051152474 032632 xustar000000000 0000000 152 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/logger/LoggerFactory.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000002362 15051152474 034353 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.inspector.logger; /** * Provides a {@link org.slf4j.Logger} for use across the entire application * */ public class LoggerFactory { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger("org.apache.zookeeper.inspector"); //$NON-NLS-1$ /** * @return {@link org.slf4j.Logger} for ZooInspector */ public static org.slf4j.Logger getLogger() { return logger; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000230 15051152474 032632 xustar000000000 0000000 152 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/NodeListener.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000002472 15051152474 034355 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000226 15051152474 032637 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/NodesCache.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000007464 15051152474 034363 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.inspector.logger.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; /** * A local cache of ZNodes in front of the Zookeeper server(s) to help cut down on network calls. This class will * return results from the cache first, if available, and then fetch results remotely from Zookeeper if not. */ public class NodesCache { public static final int CACHE_SIZE = 40000; public static final int ENTRY_EXPIRATION_TIME_MILLIS = 10000; private final LoadingCache> nodes; private ZooKeeper zooKeeper; public NodesCache(ZooKeeper zooKeeper) { this.zooKeeper = zooKeeper; this.nodes = CacheBuilder.newBuilder() .maximumSize(CACHE_SIZE) .expireAfterWrite(ENTRY_EXPIRATION_TIME_MILLIS, TimeUnit.MILLISECONDS) .build(new CacheLoader>() { @Override public List load(String nodePath) { return getChildrenRemote(nodePath); } } ); } /** * Whereas getChildren hits the cache first, getChildrenRemote goes straight (over the network) to Zookeeper to * get the answer. This is the function used by the cache to insert entries that are requested, but don't exist * in the cache yet. * * @param nodePath The full path to the parent whose children are to be fetched. * @return The list of children of the given node as a list of full ZNode path strings or null if an * error occurred. */ private List getChildrenRemote(String nodePath) { try { Stat s = zooKeeper.exists(nodePath, false); if (s != null) { List children = this.zooKeeper.getChildren(nodePath, false); Collections.sort(children); return children; } } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred retrieving children of node: " + nodePath, e ); } return null; } /** * Fetch the children of the given node from the cache. * * @param nodePath The full path to the parent whose children are to be fetched. * @return The list of children of the given node as a list of full ZNode path strings or null if an * error occurred. */ public List getChildren(String nodePath) { try { return nodes.get(nodePath); } catch (Exception e) { LoggerFactory.getLogger().error("Error occurred retrieving children of node: " + nodePath, e); } return null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000220 15051152474 032631 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/Pair.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000005354 15051152474 034357 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000237 15051152474 032641 xustar000000000 0000000 159 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManager.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000011373 15051152474 034355 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.inspector.manager; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import javax.swing.JComboBox; import javax.swing.JTextField; /** * A Manager for all interactions between the application and the Zookeeper * instance */ public interface ZooInspectorManager extends ZooInspectorNodeManager, ZooInspectorNodeTreeManager { /** * @param connectionProps * @return true if successfully connected */ public boolean connect(Properties connectionProps); /** * @return true if successfully disconnected */ public boolean disconnect(); /** * @return a {@link Pair} containing the following: *
    *
  • a {@link Map} of property keys to list of possible values. If * the list size is 1 the value is taken to be the default value for * a {@link JTextField}. If the list size is greater than 1, the * values are taken to be the possible options to show in a * {@link JComboBox} with the first selected as default.
  • *
  • a {@link Map} of property keys to the label to show on the UI *
  • *
* */ public Pair>, Map> getConnectionPropertiesTemplate(); /** * @param selectedNodes * - the nodes to add the watcher to * @param nodeListener * - the node listener for this watcher */ public void addWatchers(Collection selectedNodes, NodeListener nodeListener); /** * @param selectedNodes * - the nodes to remove the watchers from */ public void removeWatchers(Collection selectedNodes); /** * @param selectedFile * - the file to load which contains the node viewers * configuration * @return nodeViewers - the class names of the node viewers from the * configuration * @throws IOException * - if the configuration file cannot be loaded */ public List loadNodeViewersFile(File selectedFile) throws IOException; /** * @param selectedFile * - the file to save the configuration to * @param nodeViewersClassNames * - the class names of the node viewers * @throws IOException * - if the configuration file cannot be saved */ public void saveNodeViewersFile(File selectedFile, List nodeViewersClassNames) throws IOException; /** * @param nodeViewersClassNames * - the class names of the node viewers * @throws IOException * - if the default configuration file cannot be loaded */ public void setDefaultNodeViewerConfiguration( List nodeViewersClassNames) throws IOException; /** * @return nodeViewers - the class names of the node viewers from the * configuration * @throws IOException * - if the default configuration file cannot be loaded */ List getDefaultNodeViewerConfiguration() throws IOException; /** * @param connectionProps * - the connection properties last used to connect to the * zookeeper instance */ public void setLastConnectionProps(Properties connectionProps); /** * @return last connection Properties - the connection properties last used * to connect to the zookeeper instance */ public Properties getLastConnectionProps(); /** * @param props * - the properties to use as the default connection settings * @throws IOException * - if the default configuration file cannot be saved */ public void saveDefaultConnectionFile(Properties props) throws IOException; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000243 15051152474 032636 xustar000000000 0000000 163 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImpl.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000071756 15051152474 034370 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.inspector.manager; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.Perms; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager; import org.apache.zookeeper.inspector.encryption.DataEncryptionManager; import org.apache.zookeeper.inspector.logger.LoggerFactory; import org.apache.zookeeper.retry.ZooKeeperRetry; /** * A default implementation of {@link ZooInspectorManager} for connecting to * zookeeper instances */ public class ZooInspectorManagerImpl implements ZooInspectorManager { private static final String A_VERSION = "ACL Version"; private static final String C_TIME = "Creation Time"; private static final String C_VERSION = "Children Version"; private static final String CZXID = "Creation ID"; private static final String DATA_LENGTH = "Data Length"; private static final String EPHEMERAL_OWNER = "Ephemeral Owner"; private static final String M_TIME = "Last Modified Time"; private static final String MZXID = "Modified ID"; private static final String NUM_CHILDREN = "Number of Children"; private static final String PZXID = "Node ID"; private static final String VERSION = "Data Version"; private static final String ACL_PERMS = "Permissions"; private static final String ACL_SCHEME = "Scheme"; private static final String ACL_ID = "Id"; private static final String SESSION_STATE = "Session State"; private static final String SESSION_ID = "Session ID"; /** * The key used for the connect string in the connection properties file */ public static final String CONNECT_STRING = "hosts"; /** * The key used for the session timeout in the connection properties file */ public static final String SESSION_TIMEOUT = "timeout"; /** * The key used for the data encryption manager in the connection properties * file */ public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager"; /** * The key used for the authentication scheme in the connection properties file */ public static final String AUTH_SCHEME_KEY = "authScheme"; /** * The key used for the authentication data in the connection properties file */ public static final String AUTH_DATA_KEY = "authData"; private static final String DEFAULT_ENCRYPTION_MANAGER = BasicDataEncryptionManager.class.getName(); private static final int DEFAULT_TIMEOUT = 5000; private static final String DEFAULT_HOSTS = "localhost:2181"; private static final String DEFAULT_AUTH_SCHEME = ""; private static final String DEFAULT_AUTH_VALUE = ""; private static final File defaultNodeViewersFile = new File( "./src/main/resources/defaultNodeViewers.cfg"); private static final File defaultConnectionFile = new File( "./src/main/resources/defaultConnectionSettings.cfg"); //package visible for test DataEncryptionManager encryptionManager; private String connectString; private int sessionTimeout; //package visible for test ZooKeeper zooKeeper; private final Map watchers = new HashMap(); protected boolean connected = true; private Properties lastConnectionProps; private String defaultEncryptionManager; private String defaultTimeout; private String defaultHosts; private String defaultAuthScheme; private String defaultAuthValue; private NodesCache nodesCache; /** * @throws IOException * - thrown if the default connection settings cannot be loaded * */ public ZooInspectorManagerImpl() throws IOException { loadDefaultConnectionFile(); } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorManager#connect(java * .util.Properties) */ public boolean connect(Properties connectionProps) { try { if (this.zooKeeper == null) { String connectString = connectionProps .getProperty(CONNECT_STRING); String sessionTimeout = connectionProps .getProperty(SESSION_TIMEOUT); String encryptionManager = connectionProps .getProperty(DATA_ENCRYPTION_MANAGER); String authScheme = connectionProps .getProperty(AUTH_SCHEME_KEY); String authData = connectionProps .getProperty(AUTH_DATA_KEY); if (connectString == null || sessionTimeout == null) { throw new IllegalArgumentException( "Both connect string and session timeout are required."); } if (encryptionManager == null) { this.encryptionManager = new BasicDataEncryptionManager(); } else { Class clazz = Class.forName(encryptionManager); if (Arrays.asList(clazz.getInterfaces()).contains( DataEncryptionManager.class)) { this.encryptionManager = (DataEncryptionManager) Class .forName(encryptionManager).newInstance(); } else { throw new IllegalArgumentException( "Data encryption manager must implement DataEncryptionManager interface"); } } this.connectString = connectString; this.sessionTimeout = Integer.valueOf(sessionTimeout); this.zooKeeper = new ZooKeeperRetry(connectString, Integer .valueOf(sessionTimeout), new Watcher() { public void process(WatchedEvent event) { if (event.getState() == KeeperState.Expired) { connected = false; } } }); if (authData != null && authData.length() > 0){ this.zooKeeper.addAuthInfo(authScheme, authData.getBytes()); } ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10); connected = ((ZooKeeperRetry) this.zooKeeper).testConnection(); } } catch (Exception e) { connected = false; e.printStackTrace(); } if (!connected){ disconnect(); } else { this.nodesCache = new NodesCache(zooKeeper); } return connected; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorManager#disconnect() */ public boolean disconnect() { try { if (this.zooKeeper != null) { this.zooKeeper.close(); this.zooKeeper = null; connected = false; removeWatchers(this.watchers.keySet()); return true; } } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred while disconnecting from ZooKeeper server", e); } return false; } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# * getChildren(java.lang.String) */ public List getChildren(String nodePath) { if (connected) { return nodesCache.getChildren(nodePath); } return null; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getData * (java.lang.String) */ public String getData(String nodePath) { if (connected) { try { if (nodePath.length() == 0) { nodePath = "/"; } Stat s = zooKeeper.exists(nodePath, false); if (s != null) { return this.encryptionManager.decryptData(zooKeeper .getData(nodePath, false, s)); } } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred getting data for node: " + nodePath, e); } } return null; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getACLs * (java.lang.String) */ public List> getACLs(String nodePath) { List> returnACLs = new ArrayList>(); if (connected) { try { if (nodePath.length() == 0) { nodePath = "/"; } Stat s = zooKeeper.exists(nodePath, false); if (s != null) { List acls = zooKeeper.getACL(nodePath, s); for (ACL acl : acls) { Map aclMap = new LinkedHashMap(); aclMap.put(ACL_SCHEME, acl.getId().getScheme()); aclMap.put(ACL_ID, acl.getId().getId()); StringBuilder sb = new StringBuilder(); int perms = acl.getPerms(); boolean addedPerm = false; if ((perms & Perms.READ) == Perms.READ) { sb.append("Read"); addedPerm = true; } if (addedPerm) { sb.append(", "); } if ((perms & Perms.WRITE) == Perms.WRITE) { sb.append("Write"); addedPerm = true; } if (addedPerm) { sb.append(", "); } if ((perms & Perms.CREATE) == Perms.CREATE) { sb.append("Create"); addedPerm = true; } if (addedPerm) { sb.append(", "); } if ((perms & Perms.DELETE) == Perms.DELETE) { sb.append("Delete"); addedPerm = true; } if (addedPerm) { sb.append(", "); } if ((perms & Perms.ADMIN) == Perms.ADMIN) { sb.append("Admin"); addedPerm = true; } aclMap.put(ACL_PERMS, sb.toString()); returnACLs.add(aclMap); } } } catch (InterruptedException | KeeperException e) { LoggerFactory.getLogger().error( "Error occurred retrieving ACLs of node: " + nodePath, e); } } return returnACLs; } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# * getNodeMeta(java.lang.String) */ public Map getNodeMeta(String nodePath) { Map nodeMeta = new LinkedHashMap(); if (connected) { try { if (nodePath.length() == 0) { nodePath = "/"; } Stat s = zooKeeper.exists(nodePath, false); if (s != null) { nodeMeta.put(A_VERSION, String.valueOf(s.getAversion())); nodeMeta.put(C_TIME, String.valueOf(s.getCtime())); nodeMeta.put(C_VERSION, String.valueOf(s.getCversion())); nodeMeta.put(CZXID, String.valueOf(s.getCzxid())); nodeMeta .put(DATA_LENGTH, String.valueOf(s.getDataLength())); nodeMeta.put(EPHEMERAL_OWNER, String.valueOf(s .getEphemeralOwner())); nodeMeta.put(M_TIME, String.valueOf(s.getMtime())); nodeMeta.put(MZXID, String.valueOf(s.getMzxid())); nodeMeta.put(NUM_CHILDREN, String.valueOf(s .getNumChildren())); nodeMeta.put(PZXID, String.valueOf(s.getPzxid())); nodeMeta.put(VERSION, String.valueOf(s.getVersion())); } } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred retrieving meta data for node: " + nodePath, e); } } return nodeMeta; } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager# * getNumChildren(java.lang.String) */ public int getNumChildren(String nodePath) { return connected ? getChildren(nodePath).size() : -1; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#createNode * (java.lang.String, java.lang.String) */ public boolean createNode(String parent, String nodeName) { if (connected) { try { String[] nodeElements = nodeName.split("/"); for (String nodeElement : nodeElements) { String node; //for case parent is "/" and maybe other cases if (parent.endsWith("/")) { node = parent + nodeElement; } else { node = parent + "/" + nodeElement; } Stat s = zooKeeper.exists(node, false); if (s == null) { zooKeeper.create(node, this.encryptionManager .encryptData(null), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); parent = node; } } return true; } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred creating node: " + parent + "/" + nodeName, e); } } return false; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#deleteNode * (java.lang.String) */ public boolean deleteNode(String nodePath) { if (connected) { try { Stat s = zooKeeper.exists(nodePath, false); if (s != null) { List children = zooKeeper.getChildren(nodePath, false); for (String child : children) { String node = nodePath + "/" + child; deleteNode(node); } zooKeeper.delete(nodePath, -1); } return true; } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred deleting node: " + nodePath, e); } } return false; } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager#setData * (java.lang.String, java.lang.String) */ public boolean setData(String nodePath, String data) { if (connected) { try { zooKeeper.setData(nodePath, this.encryptionManager .encryptData(data), -1); return true; } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred setting data for node: " + nodePath, e); } } return false; } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * getConnectionPropertiesTemplate() */ public Pair>, Map> getConnectionPropertiesTemplate() { Map> template = new LinkedHashMap>(); template.put(CONNECT_STRING, Arrays .asList(new String[] { defaultHosts })); template.put(SESSION_TIMEOUT, Arrays .asList(new String[] { defaultTimeout })); template.put(DATA_ENCRYPTION_MANAGER, Arrays .asList(new String[] { defaultEncryptionManager })); template.put(AUTH_SCHEME_KEY, Arrays .asList(new String[] { defaultAuthScheme })); template.put(AUTH_DATA_KEY, Arrays .asList(new String[] { defaultAuthValue })); Map labels = new LinkedHashMap(); labels.put(CONNECT_STRING, "Connect String"); labels.put(SESSION_TIMEOUT, "Session Timeout"); labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager"); labels.put(AUTH_SCHEME_KEY, "Authentication Scheme"); labels.put(AUTH_DATA_KEY, "Authentication Data"); return new Pair>, Map>( template, labels); } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorManager#addWatchers * (java.util.Collection, * org.apache.zookeeper.inspector.manager.NodeListener) */ public void addWatchers(Collection selectedNodes, NodeListener nodeListener) { // add watcher for each node and add node to collection of // watched nodes if (connected) { for (String node : selectedNodes) { if (!watchers.containsKey(node)) { try { watchers.put(node, new NodeWatcher(node, nodeListener, zooKeeper)); } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred adding node watcher for node: " + node, e); } } } } } /* * (non-Javadoc) * * @see * org.apache.zookeeper.inspector.manager.ZooInspectorManager#removeWatchers * (java.util.Collection) */ public void removeWatchers(Collection selectedNodes) { // remove watcher for each node and remove node from // collection of watched nodes if (connected) { for (String node : selectedNodes) { if (watchers.containsKey(node)) { NodeWatcher watcher = watchers.remove(node); if (watcher != null) { watcher.stop(); } } } } } /** * A Watcher which will re-add itself every time an event is fired * */ public class NodeWatcher implements Watcher { private final String nodePath; private final NodeListener nodeListener; private final ZooKeeper zookeeper; private boolean closed = false; /** * @param nodePath * - the path to the node to watch * @param nodeListener * the {@link NodeListener} for this node * @param zookeeper * - a {@link ZooKeeper} to use to access zookeeper * @throws InterruptedException * @throws KeeperException */ public NodeWatcher(String nodePath, NodeListener nodeListener, ZooKeeper zookeeper) throws KeeperException, InterruptedException { this.nodePath = nodePath; this.nodeListener = nodeListener; this.zookeeper = zookeeper; Stat s = zooKeeper.exists(nodePath, this); if (s != null) { zookeeper.getChildren(nodePath, this); } } public void process(WatchedEvent event) { if (!closed) { try { if (event.getType() != EventType.NodeDeleted) { Stat s = zooKeeper.exists(nodePath, this); if (s != null) { zookeeper.getChildren(nodePath, this); } } } catch (Exception e) { LoggerFactory.getLogger().error( "Error occurred re-adding node watcher for node " + nodePath, e); } nodeListener.processEvent(event.getPath(), event.getType() .name(), null); } } public void stop() { this.closed = true; } } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * loadNodeViewersFile(java.io.File) */ public List loadNodeViewersFile(File selectedFile) throws IOException { List result = new ArrayList(); try(BufferedReader reader = getReaderForFile(selectedFile)) { if(reader == null) { return result; } String line = ""; while (line != null) { line = reader.readLine(); if(line != null) { line = line.trim(); if (!line.isEmpty() && !line.startsWith("#")) { result.add(line); } } } } return result; } private void loadDefaultConnectionFile() throws IOException { Properties props = new Properties(); try(BufferedReader reader = getReaderForFile(defaultConnectionFile)) { //If reader is null, it's OK. Default values will get set below. if(reader != null) { props.load(reader); } } defaultEncryptionManager = props.getProperty(DATA_ENCRYPTION_MANAGER) == null ? DEFAULT_ENCRYPTION_MANAGER : props.getProperty(DATA_ENCRYPTION_MANAGER); defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ? Integer.toString(DEFAULT_TIMEOUT) : props.getProperty(SESSION_TIMEOUT); defaultHosts = props.getProperty(CONNECT_STRING) == null ? DEFAULT_HOSTS : props.getProperty(CONNECT_STRING); defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ? DEFAULT_AUTH_SCHEME : props.getProperty(AUTH_SCHEME_KEY); defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ? DEFAULT_AUTH_VALUE : props.getProperty(AUTH_DATA_KEY); } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * saveDefaultConnectionFile(java.util.Properties) */ public void saveDefaultConnectionFile(Properties props) throws IOException { File defaultDir = defaultConnectionFile.getParentFile(); if (!defaultDir.exists()) { if (!defaultDir.mkdirs()) { throw new IOException( "Failed to create configuration directory: " + defaultDir.getAbsolutePath()); } } if (!defaultConnectionFile.exists()) { if (!defaultConnectionFile.createNewFile()) { throw new IOException( "Failed to create default connection file: " + defaultConnectionFile.getAbsolutePath()); } } FileWriter writer = new FileWriter(defaultConnectionFile); try { props.store(writer, "Default connection for ZooInspector"); } finally { writer.close(); } } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * saveNodeViewersFile(java.io.File, java.util.List) */ public void saveNodeViewersFile(File selectedFile, List nodeViewersClassNames) throws IOException { if (!selectedFile.exists()) { if (!selectedFile.createNewFile()) { throw new IOException( "Failed to create node viewers configuration file: " + selectedFile.getAbsolutePath()); } } FileWriter writer = new FileWriter(selectedFile); try { BufferedWriter buff = new BufferedWriter(writer); try { for (String nodeViewersClassName : nodeViewersClassNames) { buff.append(nodeViewersClassName); buff.append("\n"); } } finally { buff.flush(); buff.close(); } } finally { writer.close(); } } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * setDefaultNodeViewerConfiguration(java.io.File, java.util.List) */ public void setDefaultNodeViewerConfiguration( List nodeViewersClassNames) throws IOException { File defaultDir = defaultNodeViewersFile.getParentFile(); if (!defaultDir.exists()) { if (!defaultDir.mkdirs()) { throw new IOException( "Failed to create configuration directory: " + defaultDir.getAbsolutePath()); } } saveNodeViewersFile(defaultNodeViewersFile, nodeViewersClassNames); } public List getDefaultNodeViewerConfiguration() throws IOException { List defaultNodeViewers = loadNodeViewersFile(defaultNodeViewersFile); if (defaultNodeViewers.isEmpty()) { LoggerFactory.getLogger().warn("List of default node viewers is empty"); } return defaultNodeViewers; } /* * (non-Javadoc) * * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager# * getLastConnectionProps() */ public Properties getLastConnectionProps() { return this.lastConnectionProps; } /* * (non-Javadoc) * * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager# * setLastConnectionProps(java.util.Properties) */ public void setLastConnectionProps(Properties connectionProps) { this.lastConnectionProps = connectionProps; } private static BufferedReader getReaderForFile(File file) { //check the filesystem first if (file.exists()) { try { return new BufferedReader(new FileReader(file)); } catch (FileNotFoundException e) { return null; } } //fall back to checking the CLASSPATH with only the filename //(for cases where the file exists in src/main/resources) InputStream classpathStream = ZooInspectorManagerImpl.class.getClassLoader() .getResourceAsStream(file.getName()); if (classpathStream != null) { return new BufferedReader(new InputStreamReader(classpathStream)); } //couldn't find the file anywhere return null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000243 15051152474 032636 xustar000000000 0000000 163 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeManager.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000002507 15051152474 034354 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.inspector.manager; /** * A Manager for all interactions between the application and the nodes in a * Zookeeper instance * */ public interface ZooInspectorNodeManager extends ZooInspectorReadOnlyManager { /** * @param nodePath * - the path to the node on which to set the data * @param data * - the data to set on the this node * @return true if the data for the node was successfully updated */ public boolean setData(String nodePath, String data); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000247 15051152474 032642 xustar000000000 0000000 167 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorNodeTreeManager.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000003032 15051152474 034346 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.inspector.manager; /** * A Manager for all interactions between the application and the node tree in a * Zookeeper instance */ public interface ZooInspectorNodeTreeManager extends ZooInspectorReadOnlyManager { /** * @param parent * - the parent node path for the node to add * @param nodeName * - the name of the new node * @return true if the node was successfully created */ public abstract boolean createNode(String parent, String nodeName); /** * @param nodePath * - the path to the node to delete * @return true if the node was successfully deleted */ public abstract boolean deleteNode(String nodePath); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000247 15051152474 032642 xustar000000000 0000000 167 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/inspector/manager/ZooInspectorReadOnlyManager.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000004144 15051152474 034353 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.List; import java.util.Map; /** * A Manager for all read only interactions between the application and a node * in a Zookeeper instance */ public interface ZooInspectorReadOnlyManager { /** * @param nodePath * - the path to the node whose data is to be retrieved * @return the data for the node */ public abstract String getData(String nodePath); /** * @param nodePath * - the path to the node whose metadata is to be retrieved * @return the metaData for the node */ public abstract Map getNodeMeta(String nodePath); /** * @param nodePath * - the path to the node whose ACLs are to be retrieved * @return the ACLs set on the node */ public abstract List> getACLs(String nodePath); /** * @param nodePath * - the path to the node to parent node * @return the number of children of the node */ public abstract int getNumChildren(String nodePath); /** * @param nodePath * - the path to the node whose children to retrieve * @return a {@link List} of the children of the node */ public abstract List getChildren(String nodePath); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_jav0100644 0000000 0000000 00000000216 15051152474 032636 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zookeeper/retry/ZooKeeperRetry.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/java/org/apache/zoo0100644 0000000 0000000 00000023404 15051152474 034353 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.retry; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.inspector.logger.LoggerFactory; /** * A Class which extends {@link ZooKeeper} and will automatically retry calls to * zookeeper if a {@link org.apache.zookeeper.KeeperException.ConnectionLossException} occurs. */ public class ZooKeeperRetry extends ZooKeeper { private boolean closed = false; private final Watcher watcher; private int limit = -1; /** * @param connectString * @param sessionTimeout * @param watcher * @throws IOException */ public ZooKeeperRetry(String connectString, int sessionTimeout, Watcher watcher) throws IOException { super(connectString, sessionTimeout, watcher); this.watcher = watcher; } /** * @param connectString * @param sessionTimeout * @param watcher * @param sessionId * @param sessionPasswd * @throws IOException */ public ZooKeeperRetry(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd) throws IOException { super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd); this.watcher = watcher; } @Override public synchronized void close() throws InterruptedException { this.closed = true; super.close(); } @Override public String create(String path, byte[] data, List acl, CreateMode createMode) throws KeeperException, InterruptedException { int count = 0; do { try { return super.create(path, data, acl, createMode); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); if (exists(path, false) != null) { return path; } } catch (KeeperException.NodeExistsException e) { return path; } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public void delete(String path, int version) throws InterruptedException, KeeperException { int count = 0; do { try { super.delete(path, version); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); if (exists(path, false) == null) { return; } } catch (KeeperException.NoNodeException e) { break; } } while (!closed && (limit == -1 || count++ < limit)); } @Override public Stat exists(String path, boolean watch) throws KeeperException, InterruptedException { int count = 0; do { try { return super.exists(path, watch ? watcher : null); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public Stat exists(String path, Watcher watcher) throws KeeperException, InterruptedException { int count = 0; do { try { return super.exists(path, watcher); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public List getACL(String path, Stat stat) throws KeeperException, InterruptedException { int count = 0; do { try { return super.getACL(path, stat); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public List getChildren(String path, boolean watch) throws KeeperException, InterruptedException { int count = 0; do { try { return super.getChildren(path, watch ? watcher : null); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return new ArrayList(); } @Override public List getChildren(String path, Watcher watcher) throws KeeperException, InterruptedException { int count = 0; do { try { return super.getChildren(path, watcher); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return new ArrayList(); } @Override public byte[] getData(String path, boolean watch, Stat stat) throws KeeperException, InterruptedException { int count = 0; do { try { return super.getData(path, watch ? watcher : null, stat); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public byte[] getData(String path, Watcher watcher, Stat stat) throws KeeperException, InterruptedException { int count = 0; do { try { return super.getData(path, watcher, stat); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public Stat setACL(String path, List acl, int aclVersion) throws KeeperException, InterruptedException { int count = 0; do { try { return super.setACL(path, acl, aclVersion); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); Stat s = exists(path, false); if (s != null) { if (getACL(path, s).equals(acl)) { return s; } } else { return null; } } } while (!closed && (limit == -1 || count++ < limit)); return null; } @Override public Stat setData(String path, byte[] data, int version) throws KeeperException, InterruptedException { int count = 0; do { try { return super.setData(path, data, version); } catch (KeeperException.ConnectionLossException e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); Stat s = exists(path, false); if (s != null) { if (getData(path, false, s) == data) { return s; } } else { return null; } } } while (!closed && (limit == -1 || count++ < limit)); return null; } /** * @param limit */ public void setRetryLimit(int limit) { this.limit = limit; } /** * @return true if successfully connected to zookeeper */ public boolean testConnection() { int count = 0; do { try { return super.exists("/", null) != null; } catch (Exception e) { LoggerFactory.getLogger().warn( "ZooKeeper connection lost. Trying to reconnect."); } } while (count++ < 5); return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000157 15051152474 032653 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/about.html apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/about.htm0100644 0000000 0000000 00000001724 15051152474 034527 0ustar00rootroot0000000 0000000 ZooInspector v0.1

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

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

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

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

./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000202 15051152474 032642 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/defaultConnectionSettings.cfg apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/defaultCo0100644 0000000 0000000 00000001703 15051152474 034531 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #Default connection for ZooInspector hosts=localhost\:2181 encryptionManager=org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager timeout=5000 authScheme= authData=./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000173 15051152474 032651 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/defaultNodeViewers.cfg apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/defaultNo0100644 0000000 0000000 00000001707 15051152474 034550 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerData org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerMetaData org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerACL ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000206 15051152474 032646 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/add.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000000503 15051152474 034455 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿabKGDùC» pHYs × ×B(›xtIMEÖ ”Dx¡ÐIDAT8ËÍ‘½Á`†ÒÅ`·rÒºÆ`6[;tqDÒÄd±Z &ƒ„èg7Z,Viâ§ß1Šú‹ïvò¼ùžçÀ¯“¸÷`9=0£ò»+n/ù Üô\ϵ‰½UðR>.0î0Ç9‘ÈxrbD™[Õ"[-$€mxÞ÷\9X¯wÆæÍVëù2@´´@°ÂPØiAkÈeRñ€jt&åR€ÁÈ¿&ROåXNO†Ó…4û3¹âÿòóÙôkgŒ‰ªµO¶›=TrK ‹²wŽIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/document-new.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000000735 15051152474 034464 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿabKGDüéO4×± pHYs  šœtIMEÕ&Ó¶)jIDAT8Ë­OK[QÅsïV$}%‚ÐÊ[ÜÔî\f©ó ²qY¿EÞ²Ýw#Ä‚ßÀM7Ý*…RJ²RJIZ„ÿEb›ÞwÇ…&Æ“Tz63 sæœ3R.—·kµZ‘˜ýËÜ\›gÑoòËŸßå¢oáϾdÃ¥RIÇáüxS/Oßhç¼ —'+‹Æâ®¶²ëÚ23®«Òl6 ! "=õŒÙcÚ~!ã¾"f ëL&ØöjÇ·[SÎW{ÉÝjäkêˆy…¸`1*FÓ_‹à^ºþ¬ƒäë^o: L8 ‚n4Y:OÐçØPEÓi@д¢Þ›zƦ÷Ð织¯ñá'à±Zï_èéYëÓÌü÷ƒ‘Då)]Cu4ýbã›ï$y,%_FDDdàsZzÓ’ºöD/HÏ«/Òõô­´žº.ï‡åwY–%©Ž¤è†î³´<ÆÄÄá’r´À$…âÐ÷q³Dçûx†÷ŸX²¨BAAºç‚(ÁWŠí'nÌÛ±wp×ó¹Øó’‹=/x|vÈl_P¢P¾¢ëP#í»wÒ˜XÇÌŒM4¤Ñ”XÏÞÖíìi^ò"2"‘0e1.XÇÍ»ˆÇªÐ  Q_Í­{hÛ\ÃŽ¦õå,ËÂ4Mª£µ9HãªåÔÆ*¹|û!m›kص)iš8Ž“³¨(¥°mÓ41M“»ÚCÖõÙßROû¶-hšÆÔÔ¶m …æ†aPYY™Þš† <»²Û¶q‡éééy!ëýýý¸®›×ð<]×sï?‰Dò3ðåûùÂá0¥¥¥D£Q²Ù,@.íx]¥ñº:”R,‰Fs–©Žäà4ÿ§Î_“;3LÕ"bmIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000207 15051152474 032647 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/down.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001253 15051152474 034460 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿasBIT|dˆtEXtSoftwarewww.inkscape.org›î<=IDAT8…“MhA†ß™ìækÀ`ÒCüÁªõ"I´àÁ‹ñ"/*âAê¡Uñ*Ò<©P„œ!¢RS[r±ÖUµ-{¨U´TÍš®ý é&³³Ÿ‡Ö›ßã7ß<ó¾óÍ0"BµâÝj„0ê‰ÁÐ{DsuI©i"„o]HÃvlG`É*`É2wݨ×0Æ›y޲]B¡8ƒsch?xµ®)^¯XÊ£øënlˆînP—UÄÖí»`9$½’lH²±\6Q½ïR× 2Ž7 9HEB-Ñ‹'¯û— "Ç+I‚˜¸DQ z9¸ÂpþÔ5/¢‚t6Uš_œK1"B¢[M·E'D“ž÷ùW\@¢Œ{Óó9h-GÛöÓ)ON÷½îí|-rÇ˱ŒñŘ¢Hp/ˆW—BeªÁíãP}.D#Ga.˜4ùaÜ Â™õKÔ5Q!ùpðžååMøƒ(ÑO”]‹p{9Ü>¶mÙƒ¯Ù¡ŒE’º&ŠMA×ÄDE¬t>¼SÚ8›• zVOÞ¼)€ý¡cèË>. QéÔ51QwŒº&zgO/†ÇDk0®0(nŽCáȽ{[ùa~ë×5ÑÛðƒŽ¡‘鈴@»›bØåÁüºL¹Q=ïÈÕÜ º&Š$‘ÌdŸY[±A§OûŸXŽü“»Zlãoü­x—z.Ò¼ó6Ìæ?_Úhý¿8|E½Ã7ÅÙõüsJ*†hËIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000227 15051152474 032651 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/media-playback-start.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001224 15051152474 034456 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ $!ÎÙ­^!IDAT8Ë“Kk…Ï<2É$M&1™ibZI0#ºJÑÔHRlŠÁgt'XÁB¶Ýù\ôH—‚/ºr£ˆ . BM±Xl JúФ3M23¹.Ħb)±wuîæƒsï9@wx`¸]:À  €z°»—üøØë`0˜àëÀüŠ,>•úÆ0LDzÌ/åŸn¶Z­ÏµZMïÉB4“¼>©øìéï‘<þjue²¿_é0ôf 4P¯Õj´/ ©ª’(:Š× 7ØHd€8?Áõ¹ÝáõõÛ¢SL:E×»dòXcy¹²7`8•’8Ž)^½r®>7ԤʥO§—SLü¨ÿœ4-kuhp¨²´´ÔüËf¥¶Ù.^ºx™ÝÒë0Œ6 £ »ÃŽx"Áåóã"º@0S‰xü}æL¦Q*•Ìßd ­Ðt °ñ6x=>褃Èbü‡ü†×xŽuhîEFõûoº¾›ÍŽÃÁ0,ÓÄììkñëâf,½£BoZ»;5eü•>Y–É&Ñð xžÇã'¬… F¬,´»ÝÝćý«­ŠOÌd2_æ7øëezöÆ/so€`o4±½³uiŒ‘Ÿ2ÏÏ.Æq›uŽÁ``NŽO¿MÞÛßõªJÇDM`Õ)"Õã·wWU ˆH8<:øv$Š"ç:@;ÊI’Ô£ªãPÙÁTB*¿¹® ¯ùë)lu¸ÚE‡ÎIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000211 15051152474 032642 xustar000000000 0000000 137 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/remove.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000000367 15051152474 034465 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿabKGDùC» pHYs × ×B(›xtIMEÖ ;"\„IDAT8Ë푱 ƒP†¿iÜÀ:+è#­mF ˆ­sÄtˆÔ¶"DóîO!ʳ³3…ÿwûãæ&ÍÊH6暺¸¦§`˜áøþ?ñ¬S-ÛF䀨IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000205 15051152474 032645 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/up.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001214 15051152474 034455 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿasBIT|dˆtEXtSoftwarewww.inkscape.org›î<IDAT8•“OhAÆ¿7»3Ù…zhLÿÑ‚íÁ@*¨ÐÕŠhDbE-rkJÉIbÁ«ÇR‘ EO¥BñâM*^DPÐC)´*•”4‰¦š&ÙÙQ¬!©ößûæÇ|ß›!­5ÚÕÉ›|–f½évÖnà¸<ÝßIöuL:.Oï à¸<û²WÎLÇF“Á€f—Gÿ ฼„ʼnsÓÖ–—Ç–·‰øÈ% „EÇåÿáÑèp¢§74HŸKð±ô:Ñ ÂÃ]ŽËÓý"‰³GÇùJþ Æa2ŽWk ˆ ¡ý=‰æ<ØŽÃQÁíl*áÚëåehò „‰:}Ç6ñ.ÿN_ š¦ø+¶Ó÷ÔXÆRä¡"Kà(ËuÛ@A®¢¨VqþÔE‹ØŸ<Øoßñcã݇úb”¯¬Áä87PQT¯6ƒ°–¿½@¸7DG‡‰5ò ‘Œ™èŽdoLܱ@A‡‡×¹¼-<ƒd#$2ÐZC) ^¯áñÓùJ±´9cCêSnÅž¹w¹á‰£z÷ú«,sðmpÁÀL‚Á snWeM[¿ì‰!e.ÍzNÓ&´Ö ¥ú0ƒ`ph@dM[/ç<Úõ€Ò>~Ô‹+û²® Uë?c¶júJ¢ZÛ†25 |Ið½=¤/¼b1ö lܺ?n%&ÂFsï'0÷Å۶К"IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/actions/view-refresh.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001620 15051152474 034456 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿasBIT|dˆtEXtSoftwarewww.inkscape.org›î<"IDAT8mRMlTe=÷{ï•ù­3m[¨¶ †¦¡¢3јF7Ä`X¨¼DŒð³a M ta¢à£1L%˜47ü!’Ô«qÑÐ’ÔÒÆF;´èÌ´óÞû¾ë¢ó’Yp’›ÜÅ=''ç\bføHe²Ï4èÚÞô¤jÕ„˜'¸ãÊ.Ø–éÖÝp‚|t&»EÓÄ@O×*}]g³ÑÖ˜Q(V02v¿|gú¡'ï¶-ólzW6ÃŒïĈ™‘Êd׺ÙÚ׊ǂЉÐ Ð 3ÅEÏW ÅŠ 5ô>.W f43ãÕ=gOõ¬kû40ĭѪ®C×äšÕ±Å¾ôóáÞõ­¤ ‚„_®M¨·^l¾¾ê8®lÑ€™?¼3]®§X1/2ã3Ç•gƧg“÷æ^ïhîÚ·uS( áãw»D- H©ÂUÇó’/<ûƒ”ê€AÛ2ó¶e^ùãÛ^,ìÝ}ìî?,az¦ © ,¯ªé©Hc$ðUgûÊnSuiëº&vn{'év>E¢q4A¾Ökwñ_?ß¼€'ã´'Õk‡'«Wí©%ß¼ãªö[¨0j®›ßìà>AX·-óž/à?ú¾˜íÔ©óå¤T=¶eëY‡³c«J奎ßGÆoˆé@îþW†!'ÿ$_Ô“S™l˜ˆîön\{&?3— ¨ D‰V·„ñåþ·©­%rì•=?ßJe²éšÆö†®çò³ò…âëšF®m™®Xv@x\qqahŠ=Ѿ2ŠÃ;Þ}З|¹µ9r-½kÀ5tñT2>÷hMAÒ„ÿ‘¨ÿÇ¡¥‰ÜÜä¥áÉÎÌ–MáD,ˆîµ z®=*–]ü;[‚ãx 8wy´âIu–I1MäæNß<¹-ù °ðIÿ÷7æÜ, ßÎ#?[FÕ•D(Ì—ä¹Ë£‹R©Œm™cà·ð€í¶er-0À{ †¶W1oRÅ ]›!ÂPÕ‘‡lËüÛ÷.UvjeÌÆIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000231 15051152474 032644 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/categories/applications-system.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001114 15051152474 034454 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÖ q_ZÍtEXtCommentCreated with The GIMPïd%n°IDAT8ËÅ“ÁJQ†¿X±N’–ŽÑm%¥0€‚Å¢Ð@é2–:OÐ(¤ô¤y‘Ž!ä:‚Ù\¸É&8‚±‹™&Œ-3Þ(-vÑÎtbM7.zVçÞsù9ç?ß…[FbTáùû½«0ßÝÞùnEKóòùÌ}zN~ß…‚E´[ž-›è¨_Í#p܈Lö!ÛÛó8Ž‹|¯Š( il¨Ý P¸¸N@Ô…-¤šÒ$ºÈâçI2ÙW45ž@¬`Œ!ˆÂ½€À?€ï× ”ƒrkã(­©ó(\šw"Ö—4îojË(å`­ÁZËêÚõ5Þ1ª÷¢ãø%ÞÏ¡u‚XC"f/@‹ÁZƒˆÇßÉ~{F”òù¸ð”¸d£1ˆÖB€lf=im;ék‘DÐ"h- «¤šNcŒ`Œ%Ž Ù¥¯ àÄqIßzüdävè É  ‡Çû‡¼ñàIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000206 15051152474 032646 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/16x16/status/info.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001537 15051152474 034465 0ustar00rootroot0000000 0000000 ‰PNG  IHDRóÿabKGDùC» pHYs  šœtIMEÕ 2:Pœ÷5tEXtComment(c) 2004 Jakub Steiner Created with The GIMPÙ‹o«IDAT8Ë•’KHTqÆ¿ÿÜ;ã½:×G&YjfR>E"Ä¡%Ô¤¢lbÊpa´²EË1ªEM¢‹ˆ %ÈIt&JJÍŠ4•Lqtœ¹w®÷õoå 5~«sàû~œs8T\ÙÔL)%) „˜ˆ1Ëú´ÿekãŸ^v}STÞ”Á0¤ïbmeZ¹½qU7àñS{z?\!&æ,¥zIWë׈–%oïµÔoß“¹c,RFg}’ZUYEÓ£m%ùYééÉ{[n=vˆ_Ë0kEIÕÕÛõç–Ìù¬¨º›·˜D§ ªN ïJh–‹æÍs;u&%srüm'˜Â$†=Wn/@ ¤Ž±,C-fÆ „PPšn(”Âèœì.*ÌÃr§ÖraÀªBã+YÑWUÍH43¦ ‰±œsW²àÈß½Õ%:åý<Ï! ©\m]ƒià !DÑtH²j2 *&X£àõK.Q 1+AIž–†E7©ª±áˆa@ ÏJ^_ F”UËŠ¤ú3’c1úÃ÷^Tt:·$)îo‹ÁjIðúƒˆxµ£­Yܰ‚™ÑzÞ ƒbs'~-‹ðÓ'ê'¼þÞá™…%Q6WìO«rÁÊaâ¯lBÇ;»úå™éùì´$ë¡kí7Ïκäþ2bX’è´gUû—r܃#ªÀúJë¯ÿƒc§«d*ùŒ:6ÉËþƒ¡Àý»‘›é©ilZN§Î̾2Òý4úÂØìPU}òz ÄYXXx—û(’é7VL6^|ŠIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000206 15051152474 032646 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/add.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000000602 15051152474 034455 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÖ ,ôû+¼IDAT8Ëí•1KA„¿;OPl´¬,¬ìïJ‚þŒ4!… þI ±ñÛü[-,lr½…µ…Šâí s!1»Á Xd`afç=v†·°À¼…²V¤Ãc>¸jf¾;q`i·Ý Ón0V á™ñ?„57ñ5)ªçw)C“*£.Où2"Ç1o\P˜@ú‰ÕYï&õv ðúéxz~G3agÂΉ„ìl­†=wz·Sœ6÷É_¸»¨ÒɽÂUáÏZ}E“œ¨¶T¬¯-cšG܆fÕ%¼Tn¶7WØÛÝž5‰GØ•æ_§Fé/Ó-[¥˜->†`|ð^4lée'IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/document-new.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001264 15051152474 034462 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;bKGDüéO4×± pHYs × ×B(›xtIMEÕ%0ÊC& AIDAT8Ë­•Íka‡Ÿ™ÝTšâM›5ž<ˆxÄVððÒƒ'oâÕ³b/¢âÇ?áÁ‹xÜE5¥ z²¹Ô("1¦ÙdÇÃ~fSíj}!dwØ÷yg~¿™]aŸkÔY ø®W·8.ñÅý‡wo«U ¨P[ìᨱ3ÔÇ/4¯¹^½ïfž[½qýfa¨™þG‚þK|«`”ÙþúéŠá¼uVžºù ÍfÇq03D‘°¨ì5%iQÖ8¥YlæâÌshá5<»,öm]óàCÌ Ì03,0ÌÆ¨ôÙÁdtPà0.‡÷#@SAD9HÀ"2Úÿ †ƒb?6€íß‚U5üADÓÃDQq8Ê€e†v†k¸þs6?o!O€–»XUS¬Æ*3˜ç˜SÌΕhl|çÌÉÖàçTÆkëI³2Ù'?â*Lç0ç82sš^¿„ëÕû®W·)ð¹³KÔjµœ¦¤r¨D÷Qu¢aU“cÁ®R´Ûíp“i(CR>I‘§DL-÷O]abIvù^NA)N6™j ¢Ñ}’ ã¹qÏÞ½;•‘H(E Íj,± {——ÎS­VÓÖÈD42NÕl—h1»ÝîD»1ñžä€le…4NOÑŒyxÄIô/ N²ÑÔ¸ˆ[™N£I¦ fœ¸6“Æ…UP,cÏóø+ApçÁ£{·öû²Õº:õÍË«ðìäcú #û“£M˜¶àIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000220 15051152474 032642 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/document-save.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002176 15051152474 034465 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÕ 4Her IDAT8Ë­•]lUÇ3û9m»Óe—ÝÖeY>–"Ú’V‹X0B4F|ÐÓø5HURú F‹ilD4Š’È‹h 16ƈ4AT¬%´µ6ÅÖݶÓv¿ggw|hwl©PLü'7çæžsþ÷Ï=3W`ž{£ýý?•Ʀ´Š…ë’ÍHú\ñ¾•òom¯~ö·¡8éú ­öìá‹«#³¡•>1žUeNñϸÆT:ËôÕóC©dÆVúÛ»_;ýñrÄ–"éùþÀÙU[pôÛ‡‡m#¿Š#“ØJ+PœvœÞuÊtºPv=æÜ¹ëÏ¥‹ßü:p+bà‡kÎãw¬°'¾ºÚè½ðtOÇ>ñ¾PïÎÜÌõÙÑÁ+Ft6N<™@ñ}AÇ/±­Ÿ¾ôú.yYb5%­³ä3÷¯}èè‹ï}pìàG_6G$TUHÎN¡eRø‚ëE¯¬LháîeküÔ¡>;r?Ϫ…à IàÛ©«7Õ/Jr§û˜økœÊÂô|íow°Ëâ_¤&íÝÇŽ¾¹dçÛº–¬eÅy‹óë ©K8tø­¶‰ÛƒPœþãU»@Õ.º¤á?·qÏ —¹Ç¡Pÿkß `AX¬‘ÚZijR5- ^„x_{"‚qÕOÜÐxËHtR<ƒŒ¡JD|,âñx¼Ô,u[n쎮955j—€½“±žîGŸÞ³ýÍbD8±±ó/´G‡‚÷ ·ˆ À+»ÞˆEÀ˜*'Æfoýy°ÎSy­†£½›Ö?¹ÇàËÅ ×¾¡bç±T©ú;ÌU¯‘ØA"ëN¶öìæäÔW —¯ÏìŸ5ï¶Ôxê=3öë•Æ¿=w,Ù¼®Ÿ TUæïê Vß!Š5±&Š5ÅbÌÖõ»8ñlr寥‰<ô.0ÀÚ’šþåëï¼ôÙ¸¶ŸÙ´„êðD0Š:‾ž”®^¶?ý~îçʼ{±™£Z•Í–}ªS©èÂög_è ªè‹óÇ BJÁã«¶Q™Ó21yôº3ö‰éƒ2×ÌhYngÈmmÌà䉣ÿÜ*6¬é'ìPD͆‡û(ºÕ|yü‹ÛBðL+h[0À©1¹jS»íØä§åîhë|¬f…Íþ|¼\MÍÎ3’ßÚÅ·”"?F«;×|¼ûù—"Å'Ÿ}X¾9{sßéÑôƒåâ–€•R r¦Ý÷Z°÷‘ÞÞá  Ti¦täìûîuÀçÌNDìpŒ¨5L˜óƒlÖO½¼ºëü!3”¦˜ h3¨L6[ÀÔÁQØì7¯MbÚ¬ÓzKׯÐüO¬_Sg–õ]C‚æ§@*"þ®×Nç̯'«Ã].’ƒý†\ îHIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000227 15051152474 032651 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/media-playback-start.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001701 15051152474 034456 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;sBIT|dˆtEXtSoftwarewww.inkscape.org›î<SIDAT8µÕßkWð﹓ÙÙÌ.Ûëê*m“ÝM­Kß*B!Ôl †ZcÔ‚õ¥ê‹ø AÁÚ.k[jbZ³ýñ”Ú&ñ¢IVmA+èS)™M²›dv“ììÌŽs|ˆ»ìæ©Õî}˜ÏÜ{ÎÜ;ÄÌxQVóe«Ñ²µ9¾îíµ;øŠŸ™+焇5N¥u¶m›´yÿ¡}™--›ûêêj×x€úÔðà°ÆC‰8OL޳mÛ|ç¯;ÎŽ?Ê4}°1Þ° €k6¸¤Æn·{fý˜ÌLÀÈxmÅ :÷M[ùÅ®ß6)nå½”®GzoôÿìóùÆ„£º®?*´ª¸µµµ¡ÕkVÝû)ú‹’M€ž4³¤Û’ UõÀ2M´w´½}7cÉÔaMÓn !F‰(‹Å¸dÅ¡PÈÉ%IJ`f†ý(‡1}.YÁ®Ov—7mlzùÌ­m‹^¬ìÏLf÷Ñ€AQ ƒA'ålk*sSišYX– Û¶á8²f¦™E`i6¼ëÎÙÖ[>Ÿº4 20í;…BÎp2 ! ˆ@DQ~ÙeÊÝ*´¸†#‘#†ž»öÒòåGÃá•÷LÌ€«««½×¤²2HB@ BˆdY†™5ñÕ×g[·cZå •¶6o¿*„HÕ×ךW×ÔÔj,ˆ žÀ’$A.“Ý=ÝÖ¾7<ïñ}{vªªš ‡Ã¦E \UUU€ÇA–%¸d<|àD"Ÿ™’$]ØÖ²ýÓ†††!™éର¢(…±Õ =•Bkëi#ècMÝ¡={öæëè`ž˜y å_â*Gg´Óè¹Ô“,Yr í»óÝÆäæç—¯ôØç;:²^¯çäç§"í~¿?ÀX8'¬(î{Ñ£7¸åDccã °íÙ¢äHX @`j»iÿérŸvaê?Õï¨8²;ÀïR*MIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000226 15051152474 032650 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/media-playback-stop.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001001 15051152474 034447 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;sBIT|dˆtEXtSoftwarewww.inkscape.org›î<“IDAT8Õ•ËŠÔ@†¿sª’Žm_è}c±Áq'ˆ;_OÄWPñe¼4"³”A˜™Ôq‘IÒIÚ˜{amRuR|õÕ_¹ˆ™qЦ'¡žìEDî ²F¦ÒÌXDVÀô¾`àÊÌöÅ eœ$Ƀç/ž½Su»¾D ¶ÿðþãÓÃZ ¼ÙlLÕ½yý–(ŠzÏ/^žM§ÓÐ žÏçe6_÷ŸÈB>¿z^x´} Àz½®eÚ/—Ëre¡ï9¦iZ3n=n‹Åâ¯Þ˜ÃÂãÉdb?/¯çz­·5ì Š8Vƒg_ö_W5Öâϕ־`0AU±VˆÛ~óf$××wøFfŒÐˆÿFmŒ ãASEì y‹¥™^B +8o0NˆÛ)²6wuWȲ9)dk͸ šBš(I¤hèJLQD "²J¤Ë€€ï|»ÎU8Ð;ŠJб ",ߤéÊÓ Ä@$"q6w¹6u@ýäÛÇËk˜z&"g $¢ª™â<4³d¦ã½¤$S ´¶ª¦ÿy6;IlÎ3¨ílIsð$WÍÁþíI â, èIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/actions/view-refresh.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002524 15051152474 034462 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;sBIT|dˆtEXtSoftwarewww.inkscape.org›î<æIDAT8µ•iLgÇÿï;3;»³K9–[äk‘ E¨hš&­1iÚ‚š˜¦ÖO¶M“&ýÒ415iÒZ$5¶€¤4iµ±Gbý¦5ØÖ3LNUÂÐxO&Öç§3Eæ°,ÙžÃkÓÌã]žzC×M/ >€›5Þöúж+5åå†nmüýJ_û‰;‚ñŽÌ$ —nB‘8,!Xd¡1 ¨šCÊÌH®Õì `¼Ï"›wáÄ´ÕUté†yh* K1Cã!v…òˆ)B0ÛcKˆ`¢ËñeŒÃþ³®«)ÂNÅ,MžZ.ªj\¥ÈÒÙê·ŠÔ¤x i‰Nl.H†ªH01›µìHKÏ9‹ËâÌÞ7 rÅ¢ù.ªjHcij,ÒŽ¶´GHˆÂô^²éV@s>⪦1¸ÑÁǸØþÄæîqùFO«… ¶ŸÚ;J’çkød‡l“9Æ€°nâãºÖÈý‘É÷¯ßÝ­‹LÛ×)@¼VZPvÇèñÝÿ>,}\G„Äy­³Ûd\¸5Bi^šÎ߉ Œú޶ßÕ°ÐNÎIÇT‡ï§É°Hšðû«l²lÍœŸ3nÓ–†)‘èÐÝ?Fœ±UÅï4/]ôÜž³,±aÒÜ}ûÞà‡¾@8›qܽñ¬0ˆ1krâ08Ä—ãÈ-ö¦ó=ÛÎ]éï)©núJXÔb\±d"éˆCUâzîm¶)¹c5Ã;ª_ž–f†tä/‰EÄ´ÐyÏ‹W 3¥âüt­ãÎðžÇ¾°gÌÇÆ'‚NÉÆâÖ—“ŒÄ8'kü¥+¬™zã"0gŒÝ4…C5—&8ß±i¹m˺,»7`Â" 0/M.°R]ŒM÷ÏÓ˜ðGÐøë ¿e/žÜž•$"0Ƥõ• F¬ÓèﺸÃÓqæQî–Êî%¹Õ«ŸOËÈÏMQ2Sc™ÓaCÄ0L “]½qõæC=81r¸ûôÁaa"ÒgÀöuû¼~Ïí²ÞsŸýÀ=}íÎ5)+·•+ZüZp¦:UÅFL‰ߟ£}­Ç®6uE$¢à ˜­Þ}ìÍ› û/°PçD‰F’œn%!£ !èöFû¼ôè\G¢P€Ñ¢¥‰1&ÍÍD aú7oEcD£0‰ÈšËù)i\šrÜß`IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000231 15051152474 032644 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/categories/applications-system.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002720 15051152474 034460 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÖ *@•Í]IDAT8Ë•]lTÇÇ3÷Þý^{×Kü±Ä&65¸$ÁÁ^' !˜Ð¤i•5DM¥ÄM‹ˆ¥~¨éSúVYMžèSU©j%+´.‹ÒVm ¡/Å mj ¬0Lƒ˜…õ®×»^ï÷½»÷ö!»ÆN«ªçitfÎïüuΜÁÿa;_þi\w›×¸â‘Ñ¡Öÿ£~Ö÷DF‡&ë>Ýl~éÑfòÅzAg|®Ôò™óGWÖÆÈuÐ׎|=nS³ë\h8|`}J“Ñ$FE_††ÃÂ(üåA¿Ù œ¬‹«ÐƒcM2ûá¯Úæ£q~yjaùÅíl (œ:{‡º6ð§‹Ù{{‚M÷ˆ¦Šœ¼ž_‰Œ5®8œùú®voWÐ/>Â2Q¥ÅÍØ ©å<‘™[!ÐT…æ&/¹•þÀÉ,]mM´xíJ©BåñÈèÐä½R¨¶ïÿùôÕôŸ°XΖI¤KLÍÜfzöMáihR!“)b ét‘VzÅâí3 fºNñ§5þm(à0ÏØ%’é—`q1Ë –i’Wuhݲ™¥#…BŽÆ'¥‚ÁµÅYYÑ©VT|{5žº0kÅb‹,¥ò¤Òyª»MEHÍ,¡á°o-Øåõ?¾mëèå ±Ø2·Ä2óV<~×P‚}/ô\Šë_¼[$rs™==-XU MQð5xp’Sg•P°ÿÅw§Þ-íÜÿãÍO†zþøXÿƒÚhšbÞÀëqpø½ñüßâvÛÅ]o®»nϽðú>ûæÇ¿¼­MH!BðHo;ÿ˜šáäĬåuÙE_wýÏ?‰”‚óçn"… ¾´ÀØ™èÌÙ±ï<ü#]{+>ØtîùJ_§,ät¤!¸¿ÝOkK#R ò¹2ÕŠ @ôvMS8y†÷>®d-«ºwòíoOÖ*¸ÿµ#§|.u×w÷> Ã&-ÓZK)jkPɦΠˆÞJ!… ŠÎô'Q~71ÌJù‰©_Œ¨ý–¹û[Ot·æ‘†YÁã³#…¤T0>ír .… ½T¤µ­Ó*ãu5bYvšÈjB5ô’±Ž/´õ>÷…¹xnóžÏ·‹|)ËÈûW8?=Z\É9úºƒÂ¦¨¨Rb³I¦æ¢ÖÈÑ }ür4çÒ°Ùœ19=Sý8YüEÕ(]M\¿«Ôê¬Ä.œ8Ö¸å©í‘ùìÖkñKéÔGc?ø^®ñ!þzKï}q  ,@rìÂ1;3ñ³+ÇG޽¾Kó‹Ý—æ“áé?¼q(qeüP­7PÜ@[Ͼ‘=òê¯ÎƒÀ3À³¡á°5w=aENbÍ]OX¡á°< < ìú€Ï­@`«MPQ4giöýŸ¼œ€½–ÿFϺ¬)ÓSÕ˳÷çw^ŽÆC=´Fã¡Æ¼BzÖÕj…çy¨*"‚Èâì~<¶”R­Vw8° ˜ÍfHº:ªšy¿Ül6) Ãpgü”ú\°ˆ0Ns7¬ÛíbŒqcPUÚí6Åbñ٠ɀ¿ÿø†2@D˜L&¹ß÷ Ã0|òú”J¥Bôz½§Õl}¶ãdEˆËå2Wq«Õr÷8nµZm¯bÏó’ç‚ÓKŒ7Óž0½t'+¾|ýÌ›Á[D„ù|î7§Tø§J¥›Í&±Â“¨¶-ˆûQ¹y¼^¯‹ÅË»êõzæp¤}wVloÆ>ûýþórlW£ÑHÔÅ ÓOUu‹[¹\~‘«)GQtuóéúüدŸ¿ßeî¼ÔdGÀñ¶¯x€{àp ÜmÇáö;ÿÕ°3ôÓqIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000210 15051152474 032641 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/places/folder.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001520 15051152474 034455 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<ÍIDAT8µ”1o#E†ŸÙ]Ûg'·J)’4'N9‰2H‡!„ ˆ)mJ~åQñhȸšÂ „d¥! »&AJr’Ü%!Žïzwö›ù(âø6Ä1o3Í|Þygæ3Íf“ÿC@ÇáòòòýÛ6ÍÍÍõÛí¶Ÿ¼²²òÌóÐI›ò<אַ®>ÞÙÙéN‘w777qΡªxiNU1ư½½½´··÷åúúú·åâÃÃCŽŽŽ²ÛÀ*"¦Óé "xïñÞÁ‹‹‹õýýý¯»Ýî7W…ªJÇWãÏ»»»ŸÜ;çÔ{oDçÜ5°ªb­emm­>??ÏÌÌ Õjc Aà½gkkë£[*RàœÃoÀexn…þE‚1€ ÆðÜŠ‰~>{’6äÇ/l³Ýö€µE!œ Ow„ „£s¿q¢ÿ¸_UV>6ˆ9§ ï=:iµž,3vÎq|áùð½|öÁ#´T?29Q¥¥0µÂ“ï~Xh6?ÕqÆ"Bš+ õǽŒ—gÃÛit¯òöý A J‹ŠIïÌÔ@! ‚©À^!·ŽÀ½1¸(.//- q£†*DáÝÁI&Ô«!Ö & ;_FáH-Ì6*Dwà:¯¤¹Cœ§^­ -ÀiÙ1Î9²À`Åßxeô+/çV<^¯¿^’£^_—{ahë”AæHrÁŠŸ˜Gr|ž"ÞÿyÍq!‚uJwPÐ9âü¿ÃÊêWÎzY¡ÎÁ€OsG­r>dJ(@^(ÝAn1æ¯2˜AæY|+f®1{oº§C’ä¢^Ï¢V«mllèÞ«œÓ^ÆO¿L pê$iU’î‹+ǵ4M¿ÿí“/ŠF-ºHn´Ö;ȨªJÞõËó§_ü>ê` X ÍúÀa³ÙÌ"@€—Àk®õ”©¥#xð7ÂÎ µ ÊIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000214 15051152474 032645 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/places/user-trash.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002175 15051152474 034464 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;sBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<úIDAT8µ•ËkÜUÇ?÷wùÍL^Mj„ZE‹.Ä*ºÁ m‚Z7nÜèª@ Å]èF•ZWŠu'î ­`¶ Å4iZ33Édf~ù½î¹×E¦1VñÀá^ÎïùžsÏ9W9çø?Ę:W«C­¯¯=¨í)åIU?ȲÞ[_}‘—>@¼ÿØ£‡'^yù„ªD5¶frgVçKÓ¶>qjúØÂÂOÏÔä‘p,¢Å÷Ný<ù.í•e<Ïû“jíÝaÓñ=}èÕºpúÃWçÏ~ÚÜïií{äà„ítr.^úa;Uè3îÞwá¡]£SG£ç¼0Œßxö™×ãóçÏ#b¶ ¬µ¦ÝnðÂÔÛõzmä¸/"Õ••/NO399‰¯5›tnã~ûü¶N§‹XÙ¡¦ŽD/yZŸù/l·Šï‡Ö˜â°rÎñÚ›õæÄÄS£Q!â0â±”¹%/„<Š\XO ë=C𖤩 ŒX¬8ÄBiRâê+Ÿœî=àoÄQ«eÁhk”'øOùˆ“~ÙγŽZu/ÆXv> ø\þå[jAS6 C­cj£ô’*­öì€ ÆÝ* Aëp³Vε­«ÔâÝ„AëK‚:=8Â'kÔkw1<´‡z} kÆØë›Œ±7òÜ ½èøƒÄÑŠ íÎÌYV|€o¾,*“GüæÜììž{î=€é¶ípˆl¨µ°üqÄZnï{crf~¾D‘«kÀ>¥”òoÄÍÅò3ŸŸ;."CÛù­ÕW®Î÷‡.WÎ9”R ê@ú¯Q7$  ãœ+~1cZW‰z4IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000206 15051152474 032646 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/22x22/status/info.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002264 15051152474 034463 0ustar00rootroot0000000 0000000 ‰PNG  IHDRÄ´l;bKGDùC» pHYs  šœtIMEÖ / ={®HAIDAT8Ë•]lUÇÿwî|ìNgÛn÷ƒRªU[PJ!i@DjÔjR#å} áAM !êƒ$úà“1’Õ`Ô„•BbÐ4F%> ˆ!C¡eÁq¥@»ýØï™;×ת»ÿÉÉÍÉÉýÎͽ— †¶ïØ%äEBå§9çËáÚaÒ,!$Ιõ-À?‰E#N­ý¤ôaÒçë7nZص¤[Ñ4Š,A/™HeòøóÊEsôôÉש¼‹FNÜxûŽ]íßoتÚÔƒøDš`œ¹Ûq\—ð¥í~±½I¦Ã‡–““7žˆE#Çë‚·ïØµ‚içë-—“®Îe°ªËïj‘qp‡s¸©BÅŒOd­€Ï£mZ¾PÛûþžœi”ºcÑHòf–pkaçà–mM)#~m³P“b6xÄ’æ•K>¯Tlª•ŸœNÊú…yãñA DØ9ß±p+Wêo†éØÕÚòM)1 !eA %JH‘RRD¡Ð ˆ)Ÿ*ÍMçŒ|罜Pqó|°xsÂ]g‰ÇãÁlþÁ[àCˆA 1 !VN¯¤5¯¤»NÁ°„¿1Ì™³´.‚˜ÌŠªl8.ÏÏäÊf«_µ%J ÆyN7ì¤å°¢Ï+ËãD ”ÊfE tªî(~"“žå š½–G¢¥UÁ–P“GÌè•KYÝ:× ˆ©Þ®`ÏCˬ7-F5:=3k8Uß1³"c'ÚúäÐóâÅëy‹ „…›ÕŽŽÖÀu9Ê–Í&îiÓ=÷„Û»÷C×uìçƒéÍɹÑS3÷/í Sð•}½ËŬ^™4*êX"³ÇqÜ‹¦} Q•Vë¦s\•<§GNYgFGïß·ç£ÛŒ€ë¼6úÛÈ•Ì̤—¹Ä1*lJóŠSͪlˆƒ‹mÙTÒ:rä»i—9¯ü×Íû8Øœ9Cß KAŸ·uü÷?…Ì\ ˆsm"ˆÇ/üL‰°öàƒ-.c[cшyGà*r?w ?z ;v;qD{E~8t:uL‚O‰o­4i©,b¥#Vž½Ù¢{³Et°IDAT(Ïc` :`ôF4 _?ÿ€À àt‰Ð°ðˆÈ¨èì±X$|ãÂ#â£1$’’SRÓÒ3P$˜€ŽaÎÌÊÎÉÎKdrX ¬lùI~…EÅ%¥eå•Uì0-œ\Õ5µuõ MÍ-­Ü<ÃxùÚÚ;:»º{º{ûølîŸ0qÒä)S§‰ˆŠ!;J\Brúä3gÍ–’–Au®¬Üœ¹óæ/X(¯€ŠŠJ‹/Yª¬‚¾ªjË–«k ŠijiëèêéëéêhkÁ„MtôLÍÌ-,­¬m,-ÌÍLõtL4A¶v@q{G'gg'G{ Œ+X‡-T‡›»D‡‡'ª%^^Þzºv¶žšx“`P öm}O%tEXtdate:create2011-06-17T12:27:10+00:00›õ1%tEXtdate:modify2011-06-17T12:27:10+00:00pÆMtEXtSoftwarewww.inkscape.org›î<IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000227 15051152474 032651 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/actions/media-playback-start.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002130 15051152474 034453 0ustar00rootroot0000000 0000000 ‰PNG  IHDRשÍÊàPLTEGIE222WYU]_[TVS WZU²´°Y[XJLI‘”Z[X=@;svrYZW//+ÏÑÏab^RTQ³¶²Y\XJLH ***XZV’”‘[^YJJJ000222333MMMuws]^[SSSTTToooX[VÀ¿XZUaa]vvv’’’Y[Vopn_b]uup™™™™™™³³³Y[W’“_a\ppmººº¼¼¼ØØØº»º\^[gigÑÑÑÛÛÛøøøØÙØdebghe¸¸¸ÿÿÿÿÿÿ|}{cd`””ÿÿÿÿÿÿ ¡Ÿ^`]wwtÿÿÿÿÿÿY[V¿À½^`]jlhéééÿÿÿ`b^dfb¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿ÷ø÷ÿÿÿúûúòóòèêæñòðüüüåæãéëçôõóéêçëíê÷øöúúúìíêìîêðñïôôóîðíïðîïñîûüûäåãéìçêìèçéåçéäýýüëìéìîëîïìïðíýýýãäãñòïóôòõöôö÷õüýüõõõðñîóôñõöõøùøþþþüüûìíì÷÷÷\'>YptRNS &xLøø®7øüô‡'÷çc"öÊF þö¥3%úò~&#øöáY!!õÀ3øýúKøö¹6 ÷öÜPùðqýö™! öÁ7öáU÷ûóz Ì¢&K(Ø„bKGDW} Ù pHYsHHFÉk>IDAT(Ïc` 021c—`aecçÀ*ÁÉÅÍÃˇE‚¿@@PHXDS¢°¨XL\BR S¢¤´¬\ZF–IC¢¤¢²L^AQ‰QIB$QUQ]S«ª¦®¡©—ÐÖ)¬®«‚†¢F]=}C#¨„±NaSsKKKS}Esk›‰©™9T²°½£££¤¤¤³«²ÛÊÚÆ*ag_ØÓÛ×ßÒÒ_9a¢ƒ£“³ TÂÕ­°iÒä)S ¦Mw÷ðôò†[îãV8cæ¬Ù&øúù"97È­°oÒì9Á!¡aá(Œê˜;/2*:&–]b~\|Bbz F$§¤¦¥g`{DfV6º)`‘˜‹5jóòˆ‰¸L”&U"K%tEXtdate:create2011-06-17T12:27:10+00:00›õ1%tEXtdate:modify2011-06-17T12:27:10+00:00pÆMtEXtSoftwarewww.inkscape.org›î<IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000226 15051152474 032650 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/actions/media-playback-stop.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001353 15051152474 034461 0ustar00rootroot0000000 0000000 ‰PNG  IHDRשÍÊPLTESUPTUR!!!UWS333555IIIHHH^^^ZZZlllooozzz€€€›››ŽŽŽ®®®£££¶¶¶µµµ×××ÑÑÑãããÛÛÛðððWZUWXUòòòÿÿÿÿÿÿÿÿÿUWSúúúÿÿÿãäààâÞáãÞáãßäåáûûúçèåäæâãåáãåàûüûÝàÛÜßÙÛÞØÚÝÖÙÜÖØÛÕ×ÚÓÕØÑüüüàâÝßáÝßáÜÞàÛÝàÚÝßÚÜÞÙÛÝØüýüéêçæèäæçäåæãâäßýýýïðîìíëëìéêëèèêççéåñòñïïîíîìêìéèéæg dÏ'tRNS& Þä%ù#" Ûâ  $ <¹bKGD$´ù™ pHYsHHFÉk>ÂIDAT(ϽÑk_QÀኑ$$ÑEavp¹Të’ËîÚ–P}ÿo’ýÌñü_Îójf4íhùÔ”ù‰_-Àr>v =x;v;vy:v:sœÅušÅrœÃvr¯:o­5–Ám–¿p…¹RZ¡Vžy³CŽ»d”Âic¦#^£Y Tƒ¸P¢Ê}l¬1f¨(a¥!\¢TžO›¾_­Ñw±@o­4k¬-h«'a¨S¡ Q V¡•ÃjµÕ˜„¸QyµAwµ;s´4n³,\ªW¨ W§ V¦ ^¨œÈr»× ¿Û¥¾Û£½Ü }»Ay¼:e³\° \¯ ªÕ©Ó€¨Ñ€ Ëy:sÀÞ¢‚ÁEr»*`¶a¸b¸a·¬Ù‚Â᥇ÇJfºe½g¿gÀf¿¯ÜƒÄã¦|Ã8iÃkÇlÈkư߄Æä©tÁ)oÌqÐmʱà„l½f¾jÄmÉnËlDZ߄Å㩯څ®Ûƒ°Þƒ¯ÝƒÌr}rFtRNSš´yôõ™Sõôk4ôôDîñ' ÝýþåÁúûÌœ÷÷¦söõxLöõN¿÷÷À  ë "# –f pHYsHHFÉk>IDAT(Ïc` /`dbÆ*ÎÂÊÆŽMœƒÓÍ‹Sœ‡×ÃÓË›]\@ÐÇ×Ï? PHU\DT,(8$4,\\BYœYJ:"2*:&6.^FVIB^!!1)9%5-=#SQ !®¬’•“›—_PXT\RªªW×(+¯¨¬ª®©­­«ohlÒÔ‚Jh777ë´´¶µwtvuë9zH¶4÷ôöõO˜8ir3š?š§LíŸ6}ÆÌY³çLš9wÞü(ú†Í³.Z¼dé²åÍF†0ac#Óæ+W­^¾`ùšf3S#c}„¹…‰©™e3XZe,¬Á:Ì:Ìllíììílm€âŽNH–9›¸¸¸š8[˜;éãgÑ€Srãð‰¹%tEXtdate:create2011-06-17T12:27:10+00:00›õ1%tEXtdate:modify2011-06-17T12:27:10+00:00pÆMtEXtSoftwarewww.inkscape.org›î<IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/actions/view-refresh.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002675 15051152474 034471 0ustar00rootroot0000000 0000000 ‰PNG  IHDRàw=øbKGDÿÿÿ ½§“ pHYsHHFÉk>ÖIDATHÇåSkLW=÷ÎÌÎìì‚àò. ¨±HP‹Vj«5McÒ‡ÆljZ‘Ú_}¤‰IÓþiš4š˜T[b[-`£MÚjlmb Šè"HQB} qYö=3÷Þþ`1°²IõO¿ädî$ß=ç;çÞ üojóæcÒ¿é{¶êhîêwŽÍú'ñKw^¬(ê' XgY,“ (%~JÉãâ,ü8ó×jwšS{V½Û˜Î©¸d1¬k;T~/Ž€ e»~ªÈÖ矙g[’—&;*(%0 ‰{¼¬­{00<ÁW~ªìsJ…AÝŒóÅ‚ðœkµoõÍ*P¶ëè÷¹™I[¶½²L˜ ãþ0tºª@¢¡?ASëÝð•†à\S”³ £wØôŽó¯ÔUô€<¼¸òÈ*‡¦¼±¡tþÍq7õ)¥gB+Ãå ­*Ìv=! „Lu% µ•ÙZïýG /­Ì!×»‡,ƒK|Š“NÐd¥*Ñ©:ê~k“Ø5%Ì8?v¥¶œ†(Ò‡ýN5÷üüÅwÍÁ«]C¡J áQÀBIAQd Æ8d-<»^îòWR‚gQnF›aX€ï€ŽêrOË¡ò«—ªßÜjì¹?/ö¸þÒL¶SÌOÕÑÔ1E¢`œ“H<‚CÕíêÀüì´š9ºVB{˜°ybÏÉ}°üºaZ»'†”`W00 )Œ ¢pù±ÀŒ3`œSœö¯ìÚIÃPŠ9œ0/m"V ¸²~©Lɉª×Wª©É:2SX[˜U‘`qAl,Ž€=3+ïÎñÛ7rÄ©âÊ3‰ §úãîˆàB“_Î…Í`Çûg\ӒʆQ8g#>Bù†G6W—Ó7l7€" º¿=ñ„IõŸo”m2@@6,|ZÛ¹;ìýðjͶöh_dÒM$ëe…EaW‚‘Üþ÷ý²þ‘‡µI™5"A4›Œ³à (ËOEã™[‘¾ßwME}ì´2w¤K*¯Ví¾_½až:î÷ï´É2›ÞsM'§6-Žì€öÞQA YZ²ýè¼'òÈ_íõ+:ïôì „s ÅíCÌ0A€åyIè bÀC±÷ýµÆ3]ëO]ìí*ÝÙ°Ÿ3ÑAú¨ÂdÎÉ^»ª$uÝXkS$ᚣ›žñP]\IÁ‚§æ b1´Þñà…¢ùRiA–Þrkè£Á‡¾ð਌’ Ý¥#?/ )IÒðûõ°n q(!äîƒ ìþ¶iœÐkÙ^,ÎÑ< LEù™r!Ëp2™­É,‹cÜAÃm~fš4ý°=<3Åië¬â×>»|£?ÐwóܦîÓû÷Ý$^èô,ôúÂE–h¢ÃFœvBŒsø‚Z:ûùÉó]ߨƒ=­Gª¢œ‹}ZñŽz°sK÷©/o°G¡e­Ø´<}Éú­Šž¼”¨U1ƒKàß…‘žæê¾ËíBQ£˜!@–UT¿úWý{çhÔiP¢$‡K™›]87èòFz<Œè»ˆDÉ}ѵˆˆN‰cŸ0£0XS±ü§õÛé68˜\¹%tEXtdate:create2011-06-17T12:27:10+00:00›õ1%tEXtdate:modify2011-06-17T12:27:10+00:00pÆMtEXtSoftwarewww.inkscape.org›î<IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000231 15051152474 032644 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/categories/applications-system.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000004617 15051152474 034467 0ustar00rootroot0000000 0000000 ‰PNG  IHDRàw=øsRGB®Îé„eXIfMM*JR(‡iZHH   ® pHYs  šœYiTXtXML:com.adobe.xmp 1 LÂ'Y?IDATH …U PT×>÷ýìÛ}»ì²,²°å/d!™‚¦Iqmš10£•5L;Ô´ÔX¥NÛX“Q6Ö1¿ÇNCM¢µiÚi ¦Ô”ÉXãüÁÔŠ‚Hù„…ýaa÷í²»o÷ÝÞ·T:íôÌžwϽïìwîùîyçü‡ÔÖÖÒv»’—e[Í…5Ëmõ/ã]{…^ØbÇeßÛ"¯—•5°ò`§¬V;C $Ïþ—,z)±XjóΫÞn>Ž[NÝœûCÛyœšVÙ$¯[­›” ›y4±¹æ sœ1æ®;ðôúÅíÜýúïñÖ}ŸDÞ|¿›2kŽ,~_¾æ[ßo#¹.›_ÿ:ˆL ŽKJÖêK_sdÛæï¾X^‘·á‹S£`JX¹}kãÚ&Sf6=î ÐKt*©° ¥Ô-èr½·œ©ß¨nØÖXýëo¯YU¬Ò(WÎ ÉŽ€oÀC îk€ä‰Jm~ûà®Ã5ëž“\n5:<ãã“PøP>£‚7œ¿1 –Ü4È2 @ ùÓ,М )ÉQ)2£¨ßøÓßÜ|iaã´Ãá ¶,ž±,ó£Ïeff¦ a,¥—àœœ‰ã5àö ¨oÐ rº!cN?ð*•dX’Ÿ)|wØÆžé¾tyêr×øn€‘ûòÁ8p¢Jäjéíí®]íè3çÿ ¨(¼3AŽˆÔШ u^ù'Á(ЃãFKA0ASžåž¡Š23ܺƒÞÚÿéËý ¯™°B’—'CCzùPâD³~aNÁR*§ÀD€W²—I²Yj@ƈe˜œšÅÃc^¤R2 Æ$P«8c`ÌÕø¦, "*S/ăRüêç§ŽìO5šânï ‹ Óò¸|éRÇÅ H]XdѰœúî:­†=\`dÖÖÛÛÀÙVG# ¨äõ3+Τ””üägu¯=_·AŽ„ÉchÌ‹ ZµöYÿÇ¿}ãGäOç‰.³,ßxxï+Ûžöƒ¤â(J¥T€^§Æ>ï=téê?FFï»Ï<7pñÙª‡ÿˆ^ܺ×TWB8‡Tƒô)zpyü‹Åçy8}úLä@Ó–µøLKK m³Ùd*Ó¸ÙÞQ÷¼­Àí¤ÜœTŠãrð O‚@2î»söìÞ\A噗“åGŠŠŠÂš¤d<åš…áÜîsbCÏÆåtϵ´`\ª­Ý©"óɸKÖ©ÉG„1KüR]W¯ÝÃÓžp<7Ç/±·¢zn y;;.pÙi¥ ¢$*á EÑŒ/­äˆç6Š7440­­çÈ<•f’2š‚<³ÅÈAÏElÎÒ¢HÔC~ò¤¿åÓvoZ-ÐJuöÙw:®ýÕŽ¼ÚøˆÙœ§ôûàT0%\ŸÍøçTõ÷v]ëîîøæ2Ð/onÚµ©Ü O–HÅQ®É¦i„.v]üÒÆ¦ÁÉØs§o¼). ,ª"c±íÐ{¿Ü±ƒ¦’$礗¢Iê™&ƒÄqqêï_tø¦¦&nñ|Rfå3+³Ó’FÍQÑh Ü®`\§WÐÞm>õ·ÖƒU$ïÉßÛ­fŽß…Éêò½ûÞë²>½Šž˜ðcB/ÂN-¥¤ð”L_$"‚(J–$ÊhÔ‚Ë娨žVTU}çUïÒÆùînÁ³KTkkkܱ$(·Œz¼¾C>;ŽfHk@„*¢sQÊíôãÁ·45á'¥ISééZƒK"Æb8F³¤'­®¶U¶»ûƒÐB ‰V½½¤s¿¡nçǶõëò¥(OÒ*±)#hÄH’Šc9X4A¯×ÄuÉ<(U,%†ã OJ–Ì9i9vF8 @~¤›R2E_E+o¬x¢Ìj)Ì5†BúÊõk”VÏÐ&2™ôÀ’ªÑªUž®“-ѯ\¥v5½áš ¹(u’²2—AÍJó&‚Gn:$oä&'Døq£)õ©ŒtÃÒžÛ=P_¿ÿH{û™ã×ïôÅ"‘€ÑR”ÍñÊ$à”´4æ¡ñNÏ;¯ôAoOOó_:oBIñÑóºf?<¼¯–€ ]T@ó7™4ÿ"íªáÉG ­( 26®«,&ί=z÷v;c;úq]ýÏGÈÚúªªÒ<2æ=µ¢ÄLÆíD«1NÜçòýœCn­$ÈÐÀ±c{^èìlcÛÚ|z…Z«s¹HŸèu¹§}Z=¹Øýå•þžÒÒì‰ÛýaƒZm õÜu öíÛ?še!;¡çßÍnžÿD0™3tâĉX `F÷£ö…)Vóxœá”ÔÜJ“)5ãÞý 8vüì—ìàõO¢Z½äóŒúx^!ÜŒŠÑ¨[Y¨Èèÿ{È<& A“µºˆØ«ˆZëë—»Õj•˜§—XÿEþab³…À IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000223 15051152474 032645 xustar000000000 0000000 147 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/mimetypes/text-x-generic.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001132 15051152474 034454 0ustar00rootroot0000000 0000000 ‰PNG  IHDRשÍÊ¥PLTEˆŠ…‡‰„†ˆƒkliˆŠ…ÿÿÿÞÞÞáááàààâââãããäääåååßßß¶¶¶···çççæææéééêêê­¯¬ïïïìììèèèíííÂÂÂîîîñññðððëëëòòò®¯­óóóöööôôôõõõøøø÷÷÷ùùù-4ÉÐtRNS‚‚ƒb*6ACFE@7& X„GbKGDåØù£ pHYs × ×B(›xÌIDAT(ÏÒI‚0@QçyÄÄ Qï4#6Té¿äѽé´ZÿÔ6uÞ`˜Œ vÖÞBÈÆ;ŽƒåºÇ"Bìm„(¥6É`aì–Q½›—8QZM6‰Ÿçy8Ä?— €ú¬X†RFS^("¿pa¼Z•$ˆ©8×#BÿŸ^ø\}&²:¨H'ãkvK!¨r"Ko÷:a'úó£6ñåPÝæi{ÕÕûƒáh<™Îæ‹åj½éo½'+ÇÊ—%tEXtdate:create2011-06-17T12:27:16+00:00bKÀ %tEXtdate:modify2011-06-17T12:27:16+00:00x·IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000210 15051152474 032641 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/24x24/places/folder.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002234 15051152474 034460 0ustar00rootroot0000000 0000000 ‰PNG  IHDRשÍÊ+PLTEUUUppp€€€zzz{{{{{{yyyxxx–––ppp€€€```rrrtttooonnnmmmnnnooogggkkj`acUj•@`Ÿ€€eee5f¥6g¦ÿaaa5e¤^^^7h¦ZZZ9i¨UUU:k¨RRR:k¨MMM9i§JJJ5g¥EEE^‰½4d£EEEM}¶4e¤Br¯3c£FFF6g¥/X’<<<9Z…4d¢3c¡,U‰vvvÓÓÓÐÐÐÏÏÏÍÍÍÌÌ̳³³¯¯¯­­­«««ªªª¬¬¬¨¨¨§§§¦¦¦¥¥¥½½½ÈÈÈÎÎÎÑÑÑÉÉȦ§©¤¥§¢£¥ ¡£Ÿ ¢žŸ¡¡¢¤ÄÄÄ‹˜¨6g¤8i¦8h¦7h¥6g¥5f¤ÁÁÁu‹¦s™Ä¿Ôê¾ÓêÀÔê¶Îæk„¥€¢Ë©Å⎳ٖ¸ÜªÅá¹¹¹`¥­Ò©Äâ’µÛ”·Ü£¿Þµµ´Vz¦›·Ù¨Ã▸݋°Ù”¶Ü¹Ú±±±Lt¦§ÁÞšºÝ—¸Ü±Ù‡­×¼Þ•³Ö®®®Do¦§Áߢ¿à•·Ü”¶Û’´Û²Ú‰®Ø†¨Ï;i¥¡¾Þ•¶ÜŠ¯Ø„«Ö~§Ô|¦Ó’µÚp˜Å¥¦¦²Ùt ÐqžÏ„«Õ›Ÿ£¶¶Û9gÏý|×ývN¸wÏ~†‡vŒNN‡/ô Â0~ûùçÞŠOŸ>ˆ¹k¥­VW^þ V«ŽêÏ@D*++u^šbrr’@)úvnÝÞØoÃ×nw+ÛúºHßùâ«7Oü›¯ßŒ ­1Å{ý!|·Ú˜˜xf4 #DF"[òBÈ3¡È…µÔ°Ö5¤©&M1`ÄbÅ!´I‰+W.|},y¨_Á:¼U]0Ç Ï‚’ø˜ÈR6 £-Î…x”I³„,M)´¥È…,²Ì`E‘¥«þìWów ½†Ö<à{â¤çs8Î:ª•ÝcÙ1ú(pþ÷¨–ÊÝ }”ЍŽÒM*4[³Kyý CŒ»V‚Rá¦kÖ:”ªPw–j\^:C©T㑇§ð(…UjÕ{ÚE­6†µcìå-c¯ä¹AùР G#x”iµç±¢Û¶RƒËñü€r8ˆ1RŠ{¨˜¸„¤”´ŒBBVîÂEyE%eU5u Y„„æ¥ËZÚ:ºzú†FÆ&¦ ³+WÍ-,­¬mlíìÎ×\\ÝÜ=<½¼}|ýüAÁ!¡aá‘QÑ1±qñ‰„Ĥä”Ô´ôŒÌ¬ìœÜ<$÷æ—”–•WTVU#{°¦¶®¾¡±©¹¥µ­½Åë]Ý=½}ý&NêD ¬ÉS¦N›>cæ¬Ùè¡;gîõ7oÝž7]bÁ»÷îÝð3B-|üäé³G˜‹=ñòÑb ñ%ž¿zýfé£eKP„—¯X¹jõÛwk­]·rÅz$‰ 7mÞòvëÛmÛwlÚ¸YË®Ý+öìÝ·ÿÀÁC;V }‘Õt %tEXtdate:create2011-06-17T12:27:18+00:002t»V%tEXtdate:modify2011-06-17T12:27:18+00:00C)êIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000206 15051152474 032646 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/add.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001131 15051152474 034453 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆIDATX…í–±kAÆ»›»½H$D+A°«X¥Ä?ÁB[C´ýÄÂF+AA›Ø\aai#v"‚‚‚ÁB,LD"hîlr¹yŸÅÌí…MvÏÝ‹Zx ;¼÷Þ÷¾™7»0ÁücDUæÛ³‘z?v{÷êþ…Êñ¦ª:¤dóÖ•3évÛÕÛOë„"®å°²Úaeµ3Nˆñìö„€©¾o­3A Àd‡À‰Ë홆¹9™¦äó{!ÿÉKæŠ|ßÞ=ÿ½S—w÷·z"â(‘…ò‰²-Øg¬íË™Z´|©w®Í›šà÷ÜO˜‰K§1Ñ’<'!$®ß{V˜£”€3ñáK—8òäÍ„ œùÀNÞöæÓÎô²u}'Žl•¥('°ÞÙäýçÐçAq)ò•åm€´ÝæŸû§Ë/Ç‘‡páØÔ0VíL¼þ¸qüð &o3 sÂð ŒêÐR7~1ª–jŠ>,ó‹íÙ$Úúj¦t ³ mPçcTû&t¦l¯«V%õ~~‹3”ø# Äø­jiwí]š (0¼‰[@ 4€äwê‚$Hó^w½IH|ôÐ4N†† ¤€ ÃróZ´c'2Sºüäe™_T0/]X¶&Î-OV +·0JOhåC3ÂoŒ?ƒ þWüóLæ¨ðv›IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/document-new.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001760 15051152474 034463 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆ§IDATX…Å—KO[G€¿3sm Æ.L %›ªRÙdÑ ‹îºé¶€»þˆn»ìŠî*õôTj¥.*¡†FAjC"â’¸5„—Ü™Óŵ‘±ïøT=Òhf4×s¾óšñÀÿ,ò_lªÊ'…ònŒ`Ãm[gª¹/B;`}}ý³\.÷­ˆ”Œ1cðÞã½' Cœs±}w<^xËô;ÇLO™oWxïqζæçj_üÑËïÆs)Qú¬µß¬­­•D"¶0 ‚ c™öYª7ÇúÑßÝÝ­!4¢5)ŽýðãÅçH®Šø ÕÖcêaN‰ÕjÁ9G±X¤ÝnÓ…êï£q›¼Ý"l˜rÁ>Vj¨D0ÒfñÁå¡À™¹Tmü<еJD®[ï< ÀÊ=$°äì>Öœ#ö{/êÌU¶@òKàÏÁ×8Ÿ|1à½U˜§Xó#u¬Ô»²„˜Åè[^csgÀßÁî© Íû©Ãz¢ëb‘&Fš ã 3ˆyöÃÎÆ‚˜— åzUFdoK±qaˆs/@Rh¢B»™¼×båjÀ9—ª<1¤€j¥€úKÄÔ£JpÝ_£þ/ÐÓܪ—n° â’æ7­A™F™ÅSÇúW‘ÝrØY?Æ]ý¸WàÀ×8i½1i›››±qÍså^ßÅùEœ/‚Û÷¤Ó¶9&P?‘ °2Å‹ßùô÷Åæ–H”©è—arfqL£ã9Bô$7 üòë÷ÈäÅ“Þ=‡®‚,å7K/@© Z‰¦ Ê—” ×¬òLU½i&@ÿ­×•Ûž i·g"@†±J³€âúehÄ)Î:FH<ˆ6660f0q†4ÆdæÆÀîa^Çjee%õbJRœä¡8I AÿÕœæ‰ø3![RC0ŒÒ¬µ,˜Ø*H*ÃQÀºÇz’Œä»”[ÝY2ŠçFè­Œ,UMë(cNUµ\.—‰ÞAüë¼ÈçógÀ

éTœd¢¬ü½þ¡ë³½yîSy ¾ëÉr+_ì¶,1Ç7zÍüîñó«~zWº:ñË{×/¨)(1†št‚_= ÷áðè¢O 9¾cæaNZÝ1Ù8@ߟ·ývppô÷Çû2ʶ$~ )úgÔçó­ ³ù“‡I˜±æÑ Úˆ–›+Þ~Öó‹H$²Ä²¬ Ã~œÑkndùâvój,Ê¢’g_|™/TîWJ)ß÷Ÿw]÷Þ={ö'Øã;Z[§¥þ¡µþÙ¦M›–vvv"¥ 74Æw‹Ü¶åi¤/Ðhmõ©I§vœ;wbŒ¯Ö3ÏË–-‹F"‘Í—X²d‰-„¸¯½½|>mÛcPJ*ðùÁæùDñ9Ùw†›fO#+¤•‰(7µÔðòÁwè\0ƒŽ–©(¥ÂÓ#¥Äu]:::¶lß¾]^N§×,Z´¨6‰P(RžçQ,)‹hðè7“Ësvà#æÏžF¼,­sëyíí^ê«cÜqëŒp½çyA€mÛxžG2™dΜ93>¼â"!Ä×V®\I.— Œ1á&Æ´ÖD,Ã6·süäi³9ÖÞ2“Þ÷Ï‘Ëó`g3žçáû>¾ïRJ”Rh­q]—Å‹cYÖ– ëÖ­«­®®^ÕÐЀã8(¥Èçó”——‡ÅH)…RŠÊrÉïùoîåoož çhwµ` Ch­(++CA.—CkM¡P ©©‰ÊÊÊή®®ºÀ¶íûÖ¯_o»®‹"ÔÑÑQ|ß'‘HljF£!°,‹Úê[ïšCÏÑSlýJS«D£Qâñ8Éd’d2‰çy8Žƒ”)%‘H¥ .´µÖ÷Ã…: „¸»½½×u©­­%›Í222‚‚ ¶eY$“I`ìüßœNó»Ÿ4PQn#¥DkÖz,_´ß‘R’J¥¨®®¦¿¿ŸŽŽöîÝ{ðãR!úL2™ddd„³gÏ¢µFʱô°,+4¨µó¡ÔÙàûþÏÅb1„øÏ=gŒ!ŸÏã8Zk¦L™‚Öú†Ð@Ìó„ @8œÿ·\‰ ZÆ%¾š>¥´ä/ñoXž]ÿ¤ÚxIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000207 15051152474 032647 xustar000000000 0000000 135 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/down.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002243 15051152474 034460 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆZIDATX…í—KlTUÇçÜ{§-ˆPÀ4j¤¦J…&1 #â#M”èÆQnи%î4AcbbÐÄ­˜@â£<Ú‚ò0B©<¡´´ú Ç½çœÏÅÌ´Ó2-#a×üsgæœ9ÿß÷ǽîÇ=§Ó¢u¾ø^€ˆ`¹í“LYÃzÒ·ëçÇøtÍ×8g±b°Î`ÅàœÁ:‹+ø-²!ßÔ×…ý)Sÿ#À Û‰LšÈeˆl†È¦‰l†Ð¦‰lHÆ$Y6û]$î˜:nG•›-¥nµ|Û÷Îâ„ÃÝ=ÉUUŠd™o‹³8ÿ3@ÎA©|›»›"‚—Ë6/†€"—ªÄ]ÈZ9D®@KGƒwlE¶á¢u~6 E @ !Ÿi@P‚Ò‚ˆa0ºŽ(‹Þ %%Öï^ô&Ç^ÐTgnÙB·ž¢>XX³ôóå‹W•CÈf*"X±  ‡VBhè »Ïâ{H$JXóú:Ä "¿|^ëíø¤XŠÅÏì}¹rñû«Ÿ¨¬NœèhÀØ#!Ž+!)ÛÏt›q.ˆâ@œ x©jÇOþ™n=¬þàFûf1¯¢k Ýg?ÜѸåDOo­œ:‡mA[zÂ6ºRç0*…hü@á'^ †¾{æÙÊZ®vw¸Sç_ô'Ú·‹ùŒ pô+¢È¤k·þüYß=E*&W"Úr=s™~Ó‰À tÖ8¡sŸ5^μºbeºœ†C»Ä™WÆ»)¹ š6Ñ50xsÙw{¾È̘<‡„_B_xÏ×xþp¦~"[l%4=ø3˲ko}*2æ¦:.åàרvÈ]™:o ³÷æõ¥s«Ÿ :’­8e3õ5žŸ½jO¡pîèVóžÄ~ÎÃcøÔS `(ãQJäTÒuVö«G:k“É䔦#“g÷„oõ] U`–Ï^0ò±i¼ ŒÖÐ 6…ã~OOèX}íŒ}çÒ~9ØvÔµPf4Àxï:XXÆÂìòPùQf6g˜—£ÈZˆõbRз˜  uçO¨÷ã^Ä¿ë5/v”mÛIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000227 15051152474 032651 xustar000000000 0000000 151 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/media-playback-start.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002004 15051152474 034453 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆ»IDATX…í–Oh\EÇ¿¿™÷Þ&˜ÝMºY³fw›ÍÖ4ÁCØb¬Æžmã%5顊 ‹[©$¢‡j4¹¤-ö‘,Z©`Š( ^Z(B¨ÊfK’nömÜ4Éû·3ãa7Š6ۘƃùœæ½ù}Þw~óìiOÿ±øóÍxå±ë‘£}=ºf.-™€ØM€P2Ùz¡½½ã¥æèc­Ëù?f×××€Ú€'OOM}Æ[ZO,.Οˆ47aa~qN!Qräá CC¨žtÃл¤Çþºôüü €"÷¡ Ãu(%ÑÞÑÁ9ê_ÊåúCûêŸSJ̚沋möGÕžWúH)%8gèî>ÌS©ƒ±L&3Ük~4·”»cÛ6Ê rG@(þ`CR ø~ôõõóýñøS93{"ÒÔdåóËw}>DZÿ5@2™ ƒþÓCÃÇQE0"±¿R Ä¢1êaÀT7A¼X þZ[S»ÞØØ(LÓ¬ØTi²§§§˜œ½zõ X¶¢Šƒ3Žû««ÿà¼7—Î|ßZ{›I–v]wifffSGX¥£Ñh‰’8çàŒƒsÆJ(¥ ¥€Wô`;6VV ªˆÑÑwôѱ±ÃÁººïõu¯%‰øÈÈHÍ„Ãa€R–mÁv,ضÇuày„(BH (FkðùjÀÇÂü‚´, œX:cmmmÓ(×*Äb1Üþåˆ4®± ¨´ PåPÔ5™Ì&&Ç]Çq?}öÐ3~ÿ¾…|>oNOOoz2*D£Qº"úk c ÆÀˆ°ÑBœs LNNxwç2?'û϶µ=ù[0ÌöööVLË-Ðu€1c ¬Ü œsXë®]ûÒ»~ýël¸1|æÔ©WB人ºV*­]½†@* ’ Ƥ¸uó¦¼üÉE»¾¾áógÎMÕÖÖæ#‘ˆIDUQU(¥ D±ô‚¦#{ï.^úØU Ÿ¿|ò•ñÎÎÎ,€,=ðåT ÀRš¦C …+W.{éôï·¦ž~sppè€Ue÷¶t]ç~úñFñÛ™oÌx¼åÜûç'n(”‹Wm÷ö ï¾7fûñ_xãõ·.…B¡UlÓîm´¶¶Žêzþ«T*•`Qa' o¨b¸+¥ ”–…rñýÜRJ)])åÛÕ¢{úßéOz*|•é^ÙIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000226 15051152474 032650 xustar000000000 0000000 150 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/media-playback-stop.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001073 15051152474 034460 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆòIDATX…í–íŠÓ@†Ÿ¤IÛ“²¡ ±‹½"¿ðæDAoÈ Xd„íâÝÌyý‘¶Û¢ÛFþ±/ ™!sæ}2çÌ8餓þwE=æœù¯ÀÅ}/“ dOž?þ”$ÉêOÛ¶xÿñÑ¡9GʲÀÛ×ïÆîŽ»÷2OÓ”g/žR–¥®¯¯‡Hâòê3®~çnãÄÇ:;;@ð€Ë‘÷lkÐMü_Ìçs‡nþD¾ž¿‰¿OGS0›Í$IÄQŒï"k¿s7Ô¶Vf³ÙAò£EQèæÛ$â8&ÒþÉÕÆvǦ -ZE1  ®ký¸øÀh4Ú¦B!´wBhiCÀCKœÁ¼!MSêºe™›îâ¶m ¡EÁ [èn´Q<"Ž;HI˜Y– «Åb¡«/—DqDDW$±FÝö«K‚´éwæ¢X,Ãv ª*™M‰"H’1’¯¿ؘéÎtÓG`6¥ªªaeYº™“&)î¾cäû; À̘L&ÃÌLfFAwgPŽÓ]#Z·ýToj ªªa5Ð4Ì IŒ'“mÎ÷„õ±¸3£iša;ç¹Ì¦¼zóòØÔ_d6%ÏóƒGÿ$À¸¯ér¹Ü¯V«Ûº®oúÆŸtÒIÿ\?–ù;:`oIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000211 15051152474 032642 xustar000000000 0000000 137 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/remove.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000000475 15051152474 034465 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆôIDATX…í’!NCA†¿}‚kÐ8Õ pš pE‚¨ªâ„3 HªI’¢Èë̘RÒ¤o+‚™ÏìæÏîÎ7“…$I’$ùgÊ_>~rþ°Û »}€ç»‹ugvú.Ÿ^N´­@)5Ng®=Íö Ü^­ÍH¸„KÈc/ÅÚJj¯ï{kTÌÅô퓦„¼{5SdæÂ L07-ÏÍMj%êï³/^^g˶£ó‚û•Œ˜ÆoëÁ^ý›UFÇC´˜†ûj×.a&Ücä?™›pb›>RUàfü´É/I’$I¶æ2o—dë£IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000205 15051152474 032645 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/up.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002251 15051152474 034457 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆ`IDATX…í–[lU†¿5{OO{jCÑXåD©$JZM¡¥1Tâ%¥DLDÑD¢1ÁKO ÆŒ$ÆÂÁàƒQ á QÒêƒÄ i¤´´¶z¡íé93{ù0ç”rz5>°’•=—=ëÿÖÚkÏ Ü´ë´…;È)ßb«n4Žw½ö™=¾ä)¯õ_ÿÏ*6û¯Ì¼}þÚWŸ~›[ò Þ-¯µ+þ3€Š-¶rJÁ­»ª—=ï .²úáçò­õ?_²)v÷¿°ìMfù&çÐSlˆ_¸rŠãg>#0 ª–¬.ðlðUé6¦LÀLtbéâ&´uk–¿8-·ÒÐò-NCZ»ÿ`Áô‡ÄšX~[{SùâénC:Ѹ­€äöÚË=1gÖs¼†Ö〠"8 ùþÜ!-Xœs×´ùeÍs쮉ŠÃ+PQkß*ž]òÂʲ5¹'šŽ0êÃi˜ö€dÐOGo#Kï{Ò?Ûtêþ;JMõúÓ?P^k/š:c÷3Uã¿¶ÕÓh5<îè¤?ÕCÙ‚•þoçNVÍ\ÌÑÆ:×<^ü1— bkìÞX,þéúU›â-=§éìÿkÌ`^ú…ÖÞÓ<º´&_=—meæu”ncŠºàèó«ÞȤ ]¿_s_ûìj¿©:~nþ› •%+ }µGJwŸ4ÀÚµ˜˜³_VW®/*š:]N·Ÿ@U#QQÄcÏ‚g1J@•E©?{Ù3æšâ¹ÌËíµ `Ä(|Ì~ø`ñ²ê%5±“­ß £àN5!Îs¨¤H¸naxÏħ-]g©\Xm[Û›gݶèŠßX玤5Œli­Y7£hÞÞ5;ó!öã1žáØ™} ¸$=©ºm8 Q@¨*5ÅÛPŽÕ •ý‡?îïè\Wÿ~x0[Ïf_p*Ÿ´tœgûÞ ¡àTñ¶¿ôbAéJ^ 7¸Œø`ð`PPðŒ°{ß;ˆ’Â#$‚Ëä‹‘P·+ÏK_ÆØ$ž‚q\ì?GB;±9’Ž—wN¢„ahï±~¢ @ „$FZ‚aÙöÃRå›”KçIhÆ$½ÞQ†‚:EœàyaÊ&.÷]ãÅ c)MжalTfÏ€¤+ ª¸PPoÔ†¿1€¾ Ï€g$‚°‚—®€s‚Hô>ÉéO éúðm ¼ÔU©I¨Q#:cÇß €¢‚ ‘ˆ0à2Â2iý Ø4AzG™j(„¸èV˜¾¦W_Ì?ˆ¶T0Rà± àq¬äP5û54°Kgde›2&ÓâÉl±ÑÌË‚°CD3ÂC¿¦nÈP˜ŒhÃÿ'Ó´™f¤g<’9¿iÿoûúâã”|~ÕIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000217 15051152474 032650 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/actions/view-refresh.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000003750 15051152474 034464 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆŸIDATX…Å—ilT×Çÿ÷Þ·Ìæ¶Ùq ij„¡,\5ä(%%Q‹BZ〚㦊µR”–¶Rû¡ùP©­ÔFE¡,i°I¡ÊÒª qC Mc–ˆ± x·g³gæ­÷ôÃÌÐçÛßz¤«7÷͹çÿ;wÎ=ï ð66§ÕO7.óùÕï(;U8®[Ä9sX– Ön»ò#×uÞ1“¾3mÇ·[SÅZ[ä[þ´óÄŒÖÕ­Ò5~0ò-ݰ²¯X„ƒtM#%LÃÆpÊ@Ï`ʽ|s }·?EŒÓAӢ߷¬ë¯ú™ÆÍàx¯åÀS÷tï °®¾é»~]ùÃö¯U—..eD€$I‚K)s~€3@á ©ŒÖ}öéó¶Cn“#±·eÿŽ!Xû½Æ‡Eœ´]70-Àº†¦m]=òü·kÅad^@ ™q$ˆ %A )¢«®$|té¶}º¥Óp€&éºü_[7U…Þ8y^e¼øú].áŒjx¢:0+âƒKÃrKdaX!PÔ é lWÂr$HlI0m‚3<¼ºB­¬(SÿròÊx2xbÓ2Z87¸=6Yͬx¶)êwù³‚³Ÿ|eå"ßÊÊyÊ`ÒD8Sá'Ÿ<&º"Fù¹»: ÷&ôxù…¯û\ñH"Ø®„”¹BêèMb߉ó)Ûr«§onîðmܸØ(ÌÛ‹­o2â…'q>ÙBI„Ë ´vÄq¹3kwRˆšˆgpàÍ–´ã:›/Üùù”é17ª–7· ÌýŸ¹@®‰M 0Þf…4¨‚áÇÎIÃvë Ýp*ûÅ/Á®ßÍ>, «N]í ¹¹YQUmkî[cO¡2”iÈdmÄxmõž£¥ÓtF.…ãi÷«_¦pªj‹ÏÙH$w0XBŒL à}f7{G 9Çw>„ kÊ· ŽöuõGž¯©kœØÜó––ö£ªÂª8£e‰´µöøéëMEá"Á9¹º¯.^ð›´%8c˜_êGO, Û%ÄF,ÄF€%‹ÊÔYÑ"õò§½/uvýj}ÃѶ#_Ó œÿ¤é©Tõ‹ïGŠ„ï¹Ñ´óâòŠPêí³wæž¹Ô± ð‘/àS‰Ô¯Ö¤Çðw?ÚŒ¬å"‘Ê"ò£/‘E÷pý ®”ÐUhP…”„®ž˜{·'–Ždt!q”vKÇA ¨»ÑhXÑ~ÜéêÉšæ.¾²£qÊ nÞIÈÃoµðËK²[6<èWc1¤ B0D£a1»,& Œ¤ ¤ ŽKàœ1®hpI*Ž ¤’iX†1jÆÕc^­ñ nõ&Ðø÷Ö‘áÛçµS›:zb?_³t¡º¤b¶’LÛˆ¥-ØÁr$L[BÝ[Î5Bæú†åº1d2Y öõgDÏ®¶ã/0äjO€ðˆsú‚êm?»r£?=tûRí­ws«¿í½ÏÌlòo#|ö¼ÏoÇúus¢!î×0†ü{!@2w•$óyäv+>œ”½ƒ™Ì`{CÛ‰½gó:9@²{è€@«ÞݘÊ&n?sí½Í…{…QZõÈ’ùË«õEJ)eiiÔ ø˜¦©cp$`š6Ò†…d2í&c‰¬mfþÓ}ñ­Ÿö_y§€5n˜Ì“½@ÿríË{®¼þÜQú8€{sÍ)*[¾eeñÂ5zQÙ—¸¦—¸BŒ 9*³×ˆ÷}8tãŸï\?ÕYó 篆÷L*–ê¸Ïj¾~„w;8lÏŸñ˜ì˜Þ"´1öX‚ºùÀN~aAX`l#£¼¯×ßö¬óBùë¤L ?‡Š±ÙŽ.S!F¸=HAø^¯îUKä… Üà/x!¼ÂNþޛѻÞ$k¼£ î…˜øä>ö_„=ˆm2svIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000231 15051152474 032644 xustar000000000 0000000 153 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/categories/applications-system.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000004760 15051152474 034466 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆ §IDATX…­W{pTåÿ}ßwï¾7»yíæEÞ$$AbªòH@‚¼¦N;2"J;­âLǶÓZ§´"Õj™©±*N¥V4¨ (¨L òÜlÞ»Ù;ïÞgÿÈBcXÊôÌœ¹³ßýÎùýöÜs~ß½7aKVlà „ š¦@ió'ûçßL.îf‚c¶Ÿî|&“Ñ Gð÷”ÝL ß7 ¶v{ã8¢Š×| Æëhººº­éÿ7µõ?P»ò¡‘ÅË·ß:ýžÌ´¹iéiêØx1AÄèx)6»¤®búÞ%+¶ß¿dÅ6mÙÝÛü6lú²{þÙd|®®®ÆæîÚ6£d®Ð×Õüy]ÝÖôÂòêG4M{yöœJNãMdÜ#ÕYŒÇ{_Qéü¨³¬²}f^µ¤²úmkŠeçÚõõ\W·ëîü‚[Ô¾îæÏ¦ã‘©?êVnŒ0úüÓ»~E&BÁþú«ÒÐðXœcŒ›=§œŸU9‹•”¡ñÓËöL  7Ë•¡­½Mç[ãýýš¦ª†…‹ª•;îªa€ »Ï=Û Êý§šŠù&T‰ªYMÍn³¢¥cfƒO<ñßÒ|‰/)-„Ùl{ćs­½ðøBà9†á±þÝØ‚‚œtlÞ²NY„ Æ ;y¶Œ2¬[Q¯SãRÜñ ¯«å\áÌù«%Qήš]F|Á(&‚1äçç"1ê "‹KðMDÀG'Û(Š¡Çí¤øC":{< ”bñm3ñþñ¤±1Ï‘“ï¿ü‹é¦¡” |Ö=o^…Ñj2"*ˆ € ŠáÌ…nè!„ q% „$öðCUyF†Ühk»2®D¹nè@$™‚O÷K¢ôá‘#i¹Yv0JÀ(¥^Ë™‰n—'‘`ÔÄŒœTT–:±ÿ•ÂÛå¡4¤©ª5¿°[ïßhp¹'Ð?èÁ$‰‘ÑÊŠË“—Ë»]îM^ŽÇ`ÕªGõ9…••E¥·.†Š×6Ý»’YRlâ2(!˜ÆÐ70ŽÂÜTìþSCÄï5ȳågíÛ•^¹ð/qÿ„ãü¹ÖÊëju>š”; iªfÍàÏ~ñÕ†9óo.*;wFÁÜŒüÒyã®î– ©­hXUUgFzª”ã$¥ÅùÜúõu¤ÀIV@¸‡ý°§˜ðáŸÄÛ/_>ðá{{6ýŸ,_½£aÎìªïYU«ïéó ª2€ ¤0ÁPÝ=n´_éÕ¾nm“]ýƒâÇ'X8MS/5ü0Æé"Q¢(c`pŠ¢"@@ë´Ãn3£ãÊY„ÝÉšIˆ »ÛÛÚ¶nÛºAß~e^oÙ΀¸Üã fWT‘;jð¿}òÙ8pªªQ«Å ÷ B\šìJBàñ†ÐÕ;Ù=‡ºÅĸÁŸIFÀŸáœA¯ã`ÐóÈH3ƒR UÕÐÛç…•`6éÀ(EJŠŠ"™Äž>}•—e‡Õb@šÝJxFÁ1 EÑ ,VkÔ0U%#à˜ª¬))QEVQVâ„Ѩ¥n·U¹˜UžB£¸xù"|KÔÈQÆ{ãÍ£eU©ã¢Ýd4jOÿñq.—Ás ±ÇÂòº¥–cÇÞß[]½ãö öJ×À««wðý×Çãn[P„†—^uöô^UEù I“:yÂϤzî©òââ²G¾Õ 0xÈ’‚®®1deÛÐ~õ Þ~÷½cG_\“t ¯ÙðÕ ÒŒ¢[DAî^¾ìvÞã O*!£ „ ŒaÍ=wòF£É×ä'Gú¦úKfÜ·e5ßÓíAfº”RøÆ#ã ŠŠ2qèðјgÜ·ËÕÝ|q:„ÈåŸ.Wÿóǘ›cÇØXpRçA Å\º8ˆ9³*È]5ÕVŽƒ$*bèîôÀjу±Iu„E82¬ðx½èwõ³q[èp²Þ¹á,8qⵈ¬¨§ß9Ú¨ÎÈKƒŽq“:ŸÐxJ(¼c!tuŒ¡ýÒ0º;Ç )JKÈʶRIRÀ@ ©„˜EÕté‹ó"°tåöRÏ-ÿὫé`¿/ÌçXÒCGÇqPqA%“—*ià9QPàLwbóúzÕh0F’³ç=0{Ë]šfùtÓúzg^V>™ðGa6ëQRêÏ1È¢Jéä{@bD£Pe Ѱˆ›‹ö4ŒFĨŒxT‚ê ÒÔÜš™á˜êínþò[+à ÙÚR¬¥+ëî$>oNg ²²Í8~â¤&ˆTÌÎAn^*ì6#t|¢ŒÁb6 +Û†L‡#v>¾Kþ۾ș¹f˜,z(²† kVèeUÝó((žg%eý5óYqqÚ:®à™=ûâ—Ú;[Ï~Ñd¾ÜÞ­é 7³4™6D‚q˜L:”¦ãÂ×qðÐ;â›ÿ:.ŽíöùüþOÎ4•,\XÉQFðÌž}‚(Å~äêim›ŠyÃ3Yº|Ûã£þ)Gfózú¯î¸Üz²Íd²§”Wݱ6-ÕY/+ZÍ{ÿ wf«ýxaï!Õï|²·óü©PÈ -\´n©-#k$JFM‘~yúäë DrÒ @_OKSVnYo8™8ÿùÑÜý—Æð’$‘ÁΞޮæfU,ÚüƒÛo5¨"…É¢ÃÙs_©--_íoi:~DcJ"•64ØÑ¥oQýòÌÛïPàÚ5¼dŸfòg§p€€>ATM¸¦ªJÿÐÐhUAv! &]=.)ôv'âÕ @éë¼àîºÀrâÞuKöe¤ˆˆN¹F„ŒEÃ-î¡Q-3Û P®AÎïhš²72-8ì†Hb Ÿp· fãZ³Å¾(e2 FO5¨ÀK|Í¥d ß—À·¯Mñïeÿ"z8 †®USIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000223 15051152474 032645 xustar000000000 0000000 147 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/mimetypes/text-x-generic.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000001350 15051152474 034456 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆŸIDATX…å—Mj1Ç3RÜ–àb(MÛø+N cÈ5zt‘]îÐm—]yð z,¼µ± Ò] M±;ukª‘5_n»êƒa$Íø½¿~ïi$Ãÿn;0ßJ)G@]A†ÄqŒR*¹¢(Jîv;o ¸‹¢èÝh4údÇ“[Š‚àêâ⢾iERJ´Ö©÷ì~Évýòòò È ”jÜÜÜJ)ö÷÷Y¯×F AðœÝöõƒƒÖëuÃ}¶%@k1ÎܶȶÄqœ jZ,Éó2A…´Z­ÔäJ 0\!ÍfóßPJ¥Úv!Ðn·“~i¾àAÐjµv"`ÆK 0|E8ŸÏQJ•Æ.„ Óé$ýJÆã1§§§‰ƒÓ·*ŠÄT`Šp0¤Öÿl6«L ÛíV`Çqœ Ðn·3¿Eb€­Î`› ddsÇ„&ýÊ)pgj Ê·B²DÙVŠÀfçòÚt:M9)Ú„ôz½jÜeh_Ýn·Ô>á3­õî5eY©ð‡ ²vÄétšZ†EÛ²‚£££-!¹ì¸¾¾¦ßï'}7¾Ëã XŠ€±³³3V«UâÔ°ƒäá—Rr||\]€QnpçeØïÿ1wV“É$s³òýf'¦|H 0 KQÐZRÈMk†@Vþ¥”œœœT:e¦À>–³k ‹‚{,/¢PéSl›qêŠ,Sxyêa~×Z?o4ðJ)ööö*;6f ¬ÕjwÀkà°rÔ—···ÎÏÏßÏvŠ˜-äa¹\~ÞøÄ6¿ð x4€'›ÿ†=ð8Ù{à+ðøž?§›ÀO7‚ªôý?yľ’|þI„"’ûÉIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000210 15051152474 032641 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/places/folder.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000002230 15051152474 034454 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆ pHYs × ×B(›xtEXtSoftwarewww.inkscape.org›î<IDATX…í—=oEÇ3»w{¯ö9‘0 V” !Sn•‚‰Z:>@$>@J„D…h¡CH§@ÉŠŠÒEÂÆÄ(Æ÷ºwÞ—™Š{É:wŽrR(øKvvv÷y~ó<3³»" C^§äkþ_ðnܸá+¥~6ƼwÑBˆÜZûùÇ¿\@š¦[•Jå›7oúI’àœÀZ 0?Ÿ¶ý;wîÜÞÞÞþþÑ£GÇ+PJeÕjÕÑétpÎ-5!›››ùÁÁÁGÀí•h­Ó,ËÄ¥Kc°Öž \Ì„‚jµZ?<<üb{{ûÖ¬™œsÚZûñÞÞÞϲ,˼N§K§Ó™Ì޳ Å@[[[õF£A³ÙD)å¼E÷îÝ»<àòåËi¿ß÷66Zh­–•b¦8ŽæE‹ãcLí¼às€ÝÝ]uíÚ5Ùëõæ–‚̲QÔlÄÅàRÊ@åBcŒm6×dšfÜ?´ÜÝ?ä¼:?«Ìä™Î]¹ÎÛèâ=ž'¾]ÿý›OÎ8çt·Ûõ»Ý.; >ýð]®¿µQp6=>/úÈiÏ<ΓΘ¯¾»ÿþ켘ÝhÔ«ZkÔ’fµÌþßc¢8ŸŒléhÏ# M4*>*ÓAgvy¾ct¿? Ûíg–zµ„5“äÔÁ³†;ÏÜS³³Ö‘d†4ËÁ‰ùV,AV©´Z-2#¨%¬Sxr5¯‹qšSoúŒ†9Öº' yž«(ÑïÐæ ªsOž›ç“ƒQªÑ¹%ð%qª­1ùÑ€1&+—KT›¬ÕÊ8@ ±¼ø/(ã£T“[‡ç ‚²Çi’)!ÄÉ€µ6ÆëõKèܾÒè3m‰3NàOý”=ÉèTå‚§“°˜Ô÷}¼  I™LÙ ƒLæ˜ÃºÉ>aìÄTn—­HœƒÁ(µ¹s‹ÖÚd<ÓøTZe¢D“ééKÈE‡Î-¹™nË/™•D†ãÄÒ[Z‚T ”J>£D%šT”¶/l™â,ç4Ñ>ç” ŽãSzQ…J ŸÄ c½ÔÑ¿‘†±BSúõê^/|ü €µ6õkd¹%Š5J_<^TR N† RÈ8ÜÝ;>3’$!J W¯T ¼ºoV)iªR ‹ýÅ$šëUziU*çý$AX;Xh·ÛbggÇD±bpêøódˆì¾â¸DÇÇC›$ûív- ÃxxQýôË¡øL­ãÿ¶ÿÊ»gäÀX?øáëâ51û5k·ÛÍ`míÍZÐÚt8)…\ÉÈ1*Kk¹y>:é…aØ[˜BH `’™|S@ ¨0 Ï,-ñÿÏéëø¼2¥—Ë7+IEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000214 15051152474 032645 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/places/user-trash.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000003374 15051152474 034466 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôsBIT|dˆ³IDATX…Å—[Œ\G†¿îs›™ËúncÇLŒH%@ŒCä „‹p €,Ùð@$‚@D„xä A–€ DxâÞ¼‚;çf„[v²¾a¯wwvwfç\«š‡sf¼ö&QP²¢¤Ö©îÓ}ꯪ¿ºûÀÿYÌPyàKõmvë©4Yº»<\ c¾ÆýüOÎþp46T:íÖ¯>uß#»|î{^Ž­†}¯Õü“/~û¡CK§þô‡ä×PEàƒl¨í ?ûé¿£Çû>k:MŒ1Xk0Æ–º1`l5Kš%SÎ)ÇoÔ­µÕ˜¡Ýnsïž½„QÌã¿xøìS¿¹ À„¦~øÃwÝ/““O3;;Ë`0XÙâñ¨ÅÄIü?yïyEžpÇíûˆjMú¨Õ›ßùøþ¯5ŽNE{çb¾LŒ1DµˆóSç¸ÿ36Æêk~`¿p0¼Ã³vÛíïÛÃ+§ÏàV €ï—të÷çÙ·÷+¶ô³f½õƒÚ#{÷|58~ìiš¡²zètÚ8µÜý‘Ïk@ëÖóƒC{î=¼xòd5iuxžéݹk|ú“_oAôMóС±ä—Ÿ‹¢huJï$M—øÖ£;R¿=79ù»{îºó€ù^ñ`yß-¿ùÝ[êß´ö…—þì°þ¢9ðåhgEÿÈòdürî-IÔº±¤÷€‡¿=¸qÃú'÷ïûhçÍe™’&Bš YZ¦B’ É@ˆã² òLQçPU‡ªÃUº1ë_îoéðožHÿâˆÚé,ɱÆG]ñºÆsx¢Èâ†ZÍCÄ!êÊgQq%°¤Ç9É  ËçÀ÷Câ '¢ÓP^¡sIZXkT^@ b˜RKµ‘B˜ØvîÂyÎMÇ9‡ï{Ôj–±f€Š#Ï•¢€fc/Ÿ~ÅJæÍ@^î„™Mg²,­}ãCphÜ9ÚÎ{oï ^_Çå«'°6`íø:­[ؾu7AиÖµͱµlÞ¸ ·’K^«Ûäjé ðû'èæYXófGu¼Æ(sóSÄIç`ÇÄ~ÆëI’%aؤןF–„T­š+IìùäyعŒ53YZlk†!ªùp”À#ÏûDá8Q¸uŽzmIzUGwá<*®ò´¬‚ZØ&ŽûˆdˆTeèQ)Î1³€ª½–eÖ†c ü&AÐ †0ìàœ#I©Gk™Ó×PU´òPT‰¢ÍÆ&Œññ½93¯"âFü1Ö’'1"zy™N“ŒZ¸ßk`ŒÁªB= I:ÏÔŧñ½&*Ų+ÎYÒl@kÌ¢ª„Q) DÊRpÎaGšÄ"+™Lbƒ…ÄÕÅÄ¡ª âiT…+3/PHF–ÍŽr«ª¬é¼›,K˜íž§;‰0hÑëÏ"Z’*ÖzÄI¯@¸°€¸k½ÞBfŒ Õɵª"’1Û=EQÄl\ûT—®<¨c¬±™-›îdfîEåqš æQ¹€1–Á`)-TWr@œÎ-,.fº €Vª:fçÏЈ6” weiÕÂ5l×.À±nüVÖt&p ¯]xž8í—ùgY ¬GiQ¬Úk+Ë{u¡;¯K €ÉT•B2ú:C‹Ɔ¨ …d’ãy¶Œ–W¯å⥓ˆ¨Ã%c<K}rWž7ÈS_Xì1܈œ»ÎpUʼn#Í,ý‹¢"^–ÍpæÕ¿±óÖOpêÌQæ®"¢Õû2‚ËoXžç±¼™ÿ˜„rÖ!€àòy´Ùêù‹:©r¯¨hYçË6éýÞNŸû+yž""å_.k}’Á vê¥Ì€Ù!€ÆÙ—óÞ-i˜åKXãSh¼¬ÎuÄ‘eý Xwþâõzw7\œFÆëµqÜGËæç‰€&¸^LÕþóÙgOìÞµk—5ØÊ€CeX Œ€,¿lèpÎð"îGšÆœxîxÞ_d²Ê‹òáÏ©¬x¿óƒ ˆï¿Ù‡·+ÆÚX2óÌ3ÇâõºLÓÀ‚¹i^ hTOÿæ¼M)ª–K€üÕ—‹‚¾\üžIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000206 15051152474 032646 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tango/32x32/status/info.png apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/icons/Tan0100644 0000000 0000000 00000003566 15051152474 034471 0ustar00rootroot0000000 0000000 ‰PNG  IHDR szzôbKGDÿÿÿ ½§“ pHYs  šœtIMEÖ38¥fƒ¢IDATXÃÅ–[lT׆ÿµ÷ÙgnÏŒ_0cCp 8£$&R,RUm¥U+«%´@ EMU©ZE}©”¢¾´$ÊC%çÒ6¢J«m‘ riJœÛ¤Øåb<_æ>s®ûœÝ‡ <U´´¥£}Öÿ­_ÎþÏÕ3yà©§B‘ ¶2ܦ úàÉ&×rö=ëµjþ6´¿yǾ¿çg{Áø¯ZW­×S]½‘¶ÖV„Ã:4Fð=‹‹‹¸tñ¼qibÔVžüÅ+/ýzðŽ <£GÖPc²åK½ý[# Èä«ÈU ¸ÒȇЀå‰0zR Dð÷ƒo™é+GËùàÀÐÐ3έòk·Œ&ì¡e«Ö?²áþ/þ5gÂSy¬j¸gu#bá Î}Ë‘ªPuüÉLNIWñoo =öîW'Ξðuêfü–¶ÿàéF[W>¹þÇBŸÎ˜èNiØ´6жDÀ†„“œ“Ô9÷"!á·7…ÕBÙ²Î_ɹ?|p:3¿²kÕšì¹³ï× °k×O㤋7{Ú»º(±iM­:41rAA2"D€(/ÞPWæÊÕÙœ¡ÝÜ9súdÿÆ }ƒ££§¬ÿ¦ÃnÀt¶½¹scØöÚ“„–„$H¹Dä0N6'²ÁæKãdrFVX×ìxCÀ(›N¡lK»¯s˜élûMunê? ìlí\ÊU,¬lÓáÃ÷ˆ)—@A9D°9#‹1f2N,3ƒs2‚U‚:«^ΔŠ}÷ݧé;ëß„¾×ÕãZ¦Š€ E.ã°‘É9Ä”d €ï3RÊW°]ÏcDÅ ®… U›V4§š¡¼Õu(åE5¡ÁS6LÇs‚Bs‰àÈaŒlÆ`A O|WùT¨¸ ÁWŒÈ7]ºà¤|?Z71­e'“˜^¨ZñˆîÌ!‚͈,²8#›sA*_ùYÓw¤´ƒ ‘""®Ÿ1ªÖ½ˆøX¹”ESTw –‘-Ze6lÎÈd&çdh Gz³ó%ó¼%åÌš‰¦ ®U‚Œ™Ë›ÂZ®X¶Ó&ëð<ó·×.Ž–W67z\#çòb5¿ñ®D*¤Ï•­t±ìN-V쉹¼9\1ÜqÁY6bÏ¥27ùh@h†®3ã m‰åŽŒT¥r_«û={jlÝÚ ßi]ÑkoY昮,´'BH%#-íMÁn®\<›ûv9ªÁ^"{K2í)”«Sl<ŸŠ7Þ­sÕyà¿—Ü/îqë;†€"×Þ:|ô¯¶å`WK<2<™7/;~µð˜*-‹…»:Û’&ãÑʃkç"Íw ¦šbk›¢úÆžŽÛÒÛ988h|îŸÑŽ=?é é¡|sÇnC‰È䊦€"Î3†éŒEBZ>à% C»xÐ/YÑ€þ£}ûž¥bñÉW^üÍŸo•ŸÝàÕ—žÿØu­ïž|÷ˆH6†™œuÕ6 FJ Î ƒ˜k_¼8‘t=çÓ†€èž7‹…òàíÄ—äÓé¹ôµXXðTž•ž™-gçÒIbL2]¹ï;ÚžžÔ5QáÄÚ®\™&yv)¹—ðòËûsRúÏ\›¡e ÁÕ†S‰}ôᱯ]úç{Û.~rtçØøÉ'V¯ŒêJpb=§N}Ó>XJnm©‘íš{ÿòúßÛ³cUª%©/L 8vñn¦€5Ý+E¿è:UuðíÃ××zqð¹Ù;ÞnßñøÁX¢ç±‡·lAOÏZ4F#à\# ›[À;oÆøøÄåùŒ¾îСì;ê´6çÚ²Ù†¯ÞxãOÈ.”àJ ÍA>7åä/x‡MØKÍY@z¦Â?»€]»¾û€+=ä ˜Ëd ”… ãY½vÕ““Õ3Ùóä³ óŽz@G"Cª£]Ý)œ8qcSp]÷õÿç´ïÞ{;00ðch\GâÂ…«èííÅúž.„Bâ[uå¬grwwóÓš¦„aL¡\6ALC¡X‚Àè¹05yÙ\ñR<îÿnfÆ”·ê†ë9Z-DÇºÆÆÐ^!O*Ì9ƒR>”bPŠg•2Ÿ9“ùåü|±üY“ €¬îçq@¨EpzºdNNf‡;;{esswßîÝ?×6mzÓÓe;®8rdäÕjյ喝Ä׃jPK ªÿ Bétzº££ãž±±±ÖÑÑQ?ŸÏ?~üžçyüšÐuTí_s£®% š¨^E Dhoooö}Ÿg2™bMÀ`×BÖÂ`Ýl ê¹ ¯[ªÕœ»Ñ^ÜPåõJ¯»qËøoMJ<ålㆈIEND®B`‚./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_main_res0100644 0000000 0000000 00000000160 15051152474 032645 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/logback.xml apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/main/resources/logback.x0100644 0000000 0000000 00000002212 15051152474 034467 0ustar00rootroot0000000 0000000 %5p [%t] \(%F:%L\) - %m%n ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_test_jav0100644 0000000 0000000 00000000216 15051152474 032671 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/LoggerTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zoo0100644 0000000 0000000 00000003360 15051152474 034405 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; public class LoggerTest { Logger LOG = LoggerFactory.getLogger(LoggerTest.class); String testMessage = "a test message"; @Test public void testLogStdOutConfig() { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); PrintStream realStdOut = System.out; System.setOut(new PrintStream(byteArrayOutputStream)); LOG.info(testMessage); System.setOut(realStdOut); //log to stdout for debug LOG.info(testMessage); String bufferMessage = new String(byteArrayOutputStream.toByteArray(), StandardCharsets.UTF_8); LOG.info(bufferMessage); Assert.assertTrue(bufferMessage.contains(testMessage)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-contrib_zookeeper-contrib-zooinspector_src_test_jav0100644 0000000 0000000 00000000247 15051152474 032675 xustar000000000 0000000 167 path=apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zookeeper/inspector/manager/ZooInspectorManagerImplTest.java apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/src/test/java/org/apache/zoo0100644 0000000 0000000 00000007612 15051152474 034411 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager; import org.apache.zookeeper.retry.ZooKeeperRetry; import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.IOException; public class ZooInspectorManagerImplTest { /** * test create zookeeper node operation, * no easy way to create a real zk server so use a mocked client that only validate path * * @throws IOException * @throws KeeperException * @throws InterruptedException */ @Test public void testNodeCreateRoot() throws IOException, KeeperException, InterruptedException { ZooKeeper mockedZk = getMockedZk(); ZooInspectorManagerImpl manager = getInspectorManagerImpl(mockedZk); boolean createSuccess = manager.createNode("/", "test"); Assert.assertTrue(createSuccess); } /** * test create a normal child node * * @throws IOException * @throws KeeperException * @throws InterruptedException */ @Test public void testNodeCreateNormal() throws IOException, KeeperException, InterruptedException { ZooKeeper mockedZk = getMockedZk(); ZooInspectorManagerImpl manager = getInspectorManagerImpl(mockedZk); boolean createSuccess = manager.createNode("/parent", "test"); Assert.assertTrue(createSuccess); } /** * create a mocked zk client only check path validate * * @return * @throws KeeperException * @throws InterruptedException */ private ZooKeeper getMockedZk() throws KeeperException, InterruptedException { ZooKeeper mockZk = Mockito.mock(ZooKeeperRetry.class); Mockito.when(mockZk.exists(Mockito.anyString(), Mockito.anyBoolean())).then((Answer) invocation -> { String path = invocation.getArgument(0); PathUtils.validatePath(path); return null; }); Mockito.when(mockZk.create(Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any())).then(new Answer() { @Override public String answer(InvocationOnMock invocation) throws Throwable { String path = invocation.getArgument(0); PathUtils.validatePath(path); return path; } }); return mockZk; } /** * create a inspector manager instance from zk * * @param zooKeeper * @return * @throws IOException */ private ZooInspectorManagerImpl getInspectorManagerImpl(ZooKeeper zooKeeper) throws IOException { ZooInspectorManagerImpl manager = new ZooInspectorManagerImpl(); manager.zooKeeper = zooKeeper; manager.connected = true; manager.encryptionManager = new BasicDataEncryptionManager(); return manager; } } apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector-dev.sh0100755 0000000 0000000 00000001644 15051152474 032770 0ustar00rootroot0000000 0000000 #!/bin/sh # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. java -cp ../../build/contrib/ZooInspector/*:../../../build/*:../../../build/lib/*:lib org.apache.zookeeper.inspector.ZooInspector apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.cmd0100644 0000000 0000000 00000002332 15051152474 032335 0ustar00rootroot0000000 0000000 @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. @rem Get the path to the directory containing this script SET SCRIPT_DIR=%~dp0 @rem Get the path to the uber jar for this tool @rem (Requires "mvn install" or "mvn package" be run first) set JAVA_LIB= for /F %%f in ('dir /b "%SCRIPT_DIR%\target\zookeeper-contrib-zooinspector-*-jar-with-dependencies.jar" 2^>nul') do ( set JAVA_LIB=%SCRIPT_DIR%target\%%f ) java -jar %JAVA_LIB% apache-zookeeper-3.9.4/zookeeper-contrib/zookeeper-contrib-zooinspector/zooInspector.sh0100755 0000000 0000000 00000002111 15051152474 032202 0ustar00rootroot0000000 0000000 #!/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. # Get the path to the directory containing this script SCRIPT_DIR=$(dirname "$0") # Get the path to the uber jar for this tool # (Requires "mvn install" or "mvn package" be run first) JAVA_LIB=`ls ${SCRIPT_DIR}/target/zookeeper-contrib-zooinspector-*-jar-with-dependencies.jar` java -jar "${JAVA_LIB}" apache-zookeeper-3.9.4/zookeeper-docs/pom.xml0100644 0000000 0000000 00000004656 15051152474 021575 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-docs Apache ZooKeeper - Documentation Documentation pom com.ruleoftech markdown-page-generator-plugin 2.1.0 process-sources generate ${project.basedir}/src/main/resources/markdown/html/header.html ${project.basedir}/src/main/resources/markdown/html/footer.html images,skin TABLES,FENCED_CODE_BLOCKS maven-deploy-plugin true apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/html/footer.html0100644 0000000 0000000 00000000714 15051152474 030746 0ustar00rootroot0000000 0000000

 
apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/html/header.html0100644 0000000 0000000 00000014606 15051152474 030705 0ustar00rootroot0000000 0000000 ZooKeeper: Because Coordinating Distributed Systems is a Zoo
 
apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/2pc.jpg0100755 0000000 0000000 00000035506 15051152474 030263 0ustar00rootroot0000000 0000000 ÿØÿàJFIF``ÿáHExifII*1&8Adobe ImageReady„ †ØÖÿÛC    $.' ",#(7),01444'9=82<.342ÿÛC  2!!22222222222222222222222222222222222222222222222222ÿÀüî"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ô ø_LÔô kË¡tó˼±R(áÈ赿áÑ?¹wÿ’ÿñU'‚ÿäR±ÿÿèm[ÔÎÿ¢rïÿ%ÿâ¨ÿ„#DþåßþKÿÅWEEs¿ð„hŸÜ»ÿÀÉøª?áÑ?¹wÿ’ÿñUÑQ@ïü!'÷.ÿð2_þ*øB4Oî]ÿàd¿üUtTP;ÿF‰ýË¿ü —ÿŠ£þû—ø/ÿ]Îÿ¢rïÿ%ÿâ¨ÿ„#DþåßþKÿÅWEEs¿ð„hŸÜ»ÿÀÉøª?áÑ?¹wÿ’ÿñUÑQ@ïü!'÷.ÿð2_þ*øB4Oî]ÿàd¿üUtTP;ÿF‰ýË¿ü —ÿŠ£þû—ø/ÿ]Îÿ¢rïÿ%ÿâ¨ÿ„#DþåßþKÿÅWEEs¿ð„hŸÜ»ÿÀÉøª?áÑ?¹wÿ’ÿñUÑQ@ïü!'÷.ÿð2_þ*øB4Oî]ÿàd¿üUtTP;ÿF‰ýË¿ü —ÿŠ£þû—ø/ÿ]Îÿ¢rïÿ%ÿâ¨ÿ„#DþåßþKÿÅWEEs¿ð„hŸÜ»ÿÀÉøª?áÑ?¹wÿ’ÿñUÑQ@ïü!'÷.ÿð2_þ*øB4Oî]ÿàd¿üUtTP;ÿF‰ýË¿ü —ÿŠ£þû—ø/ÿ]Îÿ¢rïÿ%ÿâ«7Ä>Òôß j—öËr'¶´–hË]Ê@eBFFîyÚTWVÐÞÚÍksËÈc‘£) þåÖ–:vžš¼Ú£j76öV°Ü.ó$rãh]Ø+•>¹ªäVúLâ"Ñ53,ÒD-ÚþDEVf Ì20ê:u8ìMwÒéV¼Í5¤Ràòî$…#¸ùçUÂú+ÙGhÚ|Fä2¨$îF ÝœäŽ<Ž(‰ˆør]%¯ÿ³µ%U²¶¼(oŸ8™Ùýìd9úÔÐAáûfâÆ=/Vx¡y¢¥Ô¤4‘ °Æî:y#¶EtÖ¾ ÑbÓ¬m.m#º6p%ºI Á*§*py‡¥h®…¥®¡5ð±‡í3$}¹Ýã¦Hß禛{ƒÛiW(óê i4SjRnZ6|*Ãúg­v?ð„hŸÜ»ÿÀÉøª¹†t[x´ø•eqœ‡Q…9Îxn+Z€9ßøB4Oî]ÿàd¿üUð„hŸÜ»ÿÀÉøªè¨ wþû—ø/ÿGü!'÷.ÿð2_þ*º*(ÿ„#DþåßþKÿÅQÿF‰ýË¿ü —ÿŠ®ŠŠçáÑ?¹wÿ’ÿñT¢rïÿ%ÿâ«¢¢€9ßøB4Oî]ÿàd¿üUð„hŸÜ»ÿÀÉøªè¨ wþû—ø/ÿGü!'÷.ÿð2_þ*º*(ÿ„#DþåßþKÿÅQÿF‰ýË¿ü —ÿŠ®ŠŠçáÑ?¹wÿ’ÿñT¢rïÿ%ÿâ«¢¢€9ßøB4Oî]ÿàd¿üUð„hŸÜ»ÿÀÉøªè¨ wþû—ø/ÿ\ß|9a¤hÐÜX½ÜRµÂ¡oµHxÚÇŸa^\Äù­ÿëíô  /ÿÈ¥cÿÿÐÚ·«Áò)XÿÀÿô6­ê(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢«jöÚ^›s¨^IåÛ[DÓJø'j(É8žY¢ ³»ƒP±·½¶}ö÷¬±¶ܬ2±©è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®?â?ü‹Öÿõö¿ú×a\Äù­ÿëíô  /ÿÈ¥cÿÿÐÚ·«Áò)XÿÀÿô6­ê(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+Œø«|lþjÆ7\_ªØÀƒ«¼¤&àIü+®¹¹‚ÎÚK›™’"RÏ$ŒT¤“Ò¼÷My~#x¾×]òÝ®§óø§ÄšÆ¶ÇïBg6öÿ„qãùÐæ±ãÏ è$®£¯YE ëÉæIÿ|.OéXg⫬þïÂ~Ô¯Cp//ÇÙ-Ǹ-ó0öº=#ÁþÐþËÑ,mXËD„oÿ¾?­mЀõOÏ׎õe¾:i6Jc³B:nÏÍ&=뼊(à‰"‰8ÑBª ÀP:; }QEQEQEQEQEQEQEQEQEQEQEWñþEëúû_ýë°®?â?ü‹Öÿõö¿úЗ‚ÿäR±ÿÿèm[Õƒà¿ù¬àúVõQEQEQEQEQEQEQXþ,¹šËÁúÕÕ´­ðÙLñȧX! zØ¢¸¡s®èVóÌIb¹¹µ·´‹Q¹2»¾×bÈËÊàdœƒÓ57ü%÷K®ÉbðZùi<–»‡ÎL…ç™ÆßÄóŠëè®ËÇ—séOq$:{JÐYÌ äÃÚf%lq´ò}G¥-ߎ®í´Û[Ÿ"È-ÄsÌe&2!p»˜«g;€;q‚Zîè®RÝOâ÷ÒEœbÙ.سH™y›ñžW¶1ïžÕÕÐEPEPEPEPyïí-n-ูŠ)n¬*ìÈG$.zŸjóígìÞ,ø‹=†©4iáß CÍÔr°Xå¹›Éય8=Ï=k¢ñÿ…SÆ>½Òø;|ÛWo˜F0#8ÛøƒßÖQ\>¼“HiÚÖÐÝ›˜`’‘yŠXoºÃŒ­ƒ‘Òºí Q“VÑmo¥‰"’U%•:ðHÈ#¨8Ïã@4QEQEQEQEQEQEQEÇüGÿ‘zßþ¾×ÿ@zì+øÿ"õ¿ý}¯þ€ô¥à¿ù¬àúVõ`ø/þE+øþ†Õ¼NOJ dœ ¹×Ș­´hñŽ7¶~cííUõM[í{ ·?èã†qÿ->žßγ+)O°ìkÂAsÿ¤c§UTEDPª£@À– KËi.å´Kˆšæ Hƒ êBG\ZžŠ( Š( ‘™QK1@É'µ ÊŠYˆ I=«˜Ôõ6¾o.2E°?÷ß¹öö©”¬©¼@þk}ž$1ŒùË{Ôð\ÿÏ¿ZÉ¢²çc±­ÿ Ïüñ‹õ£þ ŸùãëY4QÌÂÆ·ü$?óÆ/ÖøH.çŒ_­dÑG3 ßð\ÿÏ¿Z?á ¹ÿž1~µ“EÌ,kÂAsÿ×›xˆÃþZ{}?szOˆ`_ ÛX,Ž¢2ë.#c“½¸È*OíK?ùèÿ÷åÿ®sè(ªÚ–óÑÿïËÿ…Ú–óÑÿïËÿ…f2åOûRÏþz?ýùð£ûRÏþz?ýùð  ”U?íK?ùèÿ÷åÿÂíK?ùèÿ÷åÿ€.QTÿµ,ÿç£ÿß—ÿ Æ×|AåÇök"ÁÜeå*T¨ôïïI»ºþ¿äï²²Þô’QüÃßùW%E ÜgQðûþFø?딟ʽÅÚ¶³£hr]èzj÷cþX¬¡6ïc«}5矿äoƒþ¹Iü«Øk¢ÂK>>Ô¼eâ¯xºÊÃ]ΆíûIöXØî»;ºÓ-“Í{úø¯â(UøfT`­@•oøŸÀÞñtcûRÅMÊÿ«»„ìš3؇ñèr=«zÚ7†Ú(¤™¦t@­+ :œq“Zˆàÿá-ø…ÿDÓÿ+Pÿ…ð–üBÿ¢iÿ•¨½ŠóïøK~!Ñ4ÿÊÔ?áGü%¿¿èšåjð¯A¢€<ûþ߈_ôM?òµøQÿ oÄ/ú&ŸùZ‡ü+Ðh  ? êšî©o;ë¾þÆ•ãûb\yƒœ¨â·(¢€ ¥¨ê1ØEÙ¦o¸ŸÔûSu}R"Ëígæ`ˆ',ztúã%Ömæ•¥–iFê|—ÿ••´rYdšV–V-#u?çµ6©ÿjYÿÏGÿ¿/þjYÿÏGÿ¿/þˆË”U?íK?ùèÿ÷åÿÂíK?ùèÿ÷åÿ€.QTÿµ,ÿç£ÿß—ÿ ?µ,ÿç£ÿß—ÿ ¹ESþÔ³ÿžÿ~_ü*9õ›8 yKHÛFp"aŸÌb€&¿¿ƒNµ3ÎÜtUXú ൠB}JèÏ9öTz ]CPŸRº3ÌqÙtAè*¥fÝÀ+Ü<,Hð–š@Éû:àzׇ׸øSþEM3þ¸-kGpg.|[ñ ?òMòµøRÂ[ñ þ‰§þV¡ÿ ô+ “Ï¿á-ø…ÿDÓÿ+Pÿ…ð–üBÿ¢iÿ•¨½ŠóïøK~!Ñ4ÿÊÔ?áGü%¿¿èšåjð¯A¢€<ûþ߈_ôM?òµøW•ü_Ö|L.t}n÷òxoR…š(náÔÒW•zíÂ`àsÓæ#½}+\έàM_ñZε _½¼b;kiŽa‹œ“·ø‰=sžƒŽ(Ïþü@ñ¿‰ Vú¦ˆ×ÚxùNª1ß®x“þƒ^ÍMŽ4Š5Ž4TEUQ€ ê)•³ ’OR׬øŽÚââ[A#¬Q9G6;˜sЙJÈ –¨×ìc‹+j²{Ÿoj¡Tÿµ,ÿç£ÿß—ÿ ?µ,ÿç£ÿß—ÿ Å»”\¢©ÿjYÿÏGÿ¿/þjYÿÏGÿ¿/þ€¹ESþÔ³ÿžÿ~_ü(þÔ³ÿžÿ~_ü(åOûRÏþz?ýùð£ûRÏþz?ýùð  •Êkþ Þ^ÊÉþ^’ʧ¯û#úš5ý|¸k;2ʤ~òB “žÃ<­s=*%.€õÿRÿï/õ¦S×ýKÿ¼¿Öˆ|HÓø{?Ù¯ÿ_ÿèU«ÍsšV‘o{hó=æ§ yÒ¶÷!†<ãiæ®ÿÂ;iÿA=oÿGÿTÖ¢5¹£šÉÿ„vÓþ‚zßþþ&øGm?è'­ÿàhÿâiXf·4sY?ðŽÚÐO[ÿÀÑÿÄÑÿí§ýõ¿ü üM[š9¬ŸøGm?è'­ÿàhÿâhÿ„vÓþ‚zßþþ&‹­ÍrÞ!ÿ˜ÿ®cùšÓÿ„vÓþ‚zßþþ&±5k8ì¯1Ïs0Ø}̛۩ã8R{FŠ(©Ô|>ÿ‘¾úå'ò¯a¯ø}ÿ#|õÊOå^Ã]4~XQE¨‚ІÖîÚúÜ\Z\Eq $ "pÊH$‘èA…;íý§ìÞjyû<Ï/pÝ·8Î=3Þ€$¢Š(¢Š(¢Š(ñüƒ­?ëíô®_šèüun—z5´/,ñ+]&^Øãån‡ÃÂ;iÿA=oÿGÿXTø†nhæ²á´ÿ ž·ÿ£ÿ‰£þÛOú ëø?øšÎÃ5¹£šÉÿ„vÓþ‚zßþþ&øGm?è'­ÿàhÿâh°ÜÑÍdÿÂ;iÿA=oÿGÿGü#¶ŸôÖÿð4ñ4X nj–­Ÿì›Ÿ÷?­Vÿ„vÓþ‚zßþþ&«ßè–öÖ3L—ú´Œ‹“]Fú£"‹ÏQE ÷ È©¦×¯¯qð§üŠšgýpZÚŽâfÅQ]… —vÑ]Ck%ÄIq8cLà4q¸¨êq‘œtÍ>i£·…æšEŽ(Ô³»œ’Iì(ôR+PÊAdÞ–€ (¢€ (¢€ óY3öÛßúû›ÿC5éUä×Ú5µÞ«;^ê°³]K”‚è*ŽÞ+*½‹üÑÍdÿÂ;iÿA=oÿGÿGü#¶ŸôÖÿð4ñ5†ksG5“ÿí§ýõ¿ü üMðŽÚÐO[ÿÀÑÿÄÑ`5¹£šÉÿ„vÓþ‚zßþþ&øGm?è'­ÿàhÿâh°ÜÑÍdÿÂ;iÿA=oÿGÿGü#¶ŸôÖÿð4ñ4X ]kþBóÿÀôT*Ö£l–—òÀ’Ï*®>yß{žäàUZ†0§¯ú—ÿy­2ž¿ê_ýåþµPø™ÓxþA¯ÿ_ÿèU«Xú$±Á£Ï4Î(æ™ÝEPI'òOþ„¿è=oÿ~äÿâjšm»é(®oþ„¿è=oÿ~äÿâhÿ…á/ú[ÿß¹?øš9_a%CJÖôÍrÞIô»ÄºŠ&ØìŠÃÇ U ¿øfÂòkK­f®!r’!I VG JÌ ê+›ÿ…á/ú[ÿß¹?øš?á`xKþƒÖÿ÷îOþ&Ÿ+ìI\¿ˆä$?ë˜þf´4Ïh:ÕçÙ4ÝR+›…üµG©å@¬ÿÿÈH×1üÍL•€É¢Š*Fu¿äoƒþ¹Iü«ØkǾÈßýr“ùW°×M„–Ÿ®ý¼èw‰¥¦ë爤`6³pŸLçð­ +Q§‡u½IÔ4«% içÛÏÙÉfåYãR[*Ä!lädÈpGfÍ¢ëÇU°¿µ†ê;x-öÍo5Ò¼Ó'Ÿ¸FdÝBüÝH8 XòOEQEQEQEs¾1ÿu§ý}¯þ‚ÕËWSãùZ×Úÿè-^ªx§BÑ.Å®§©Åm9A FW'iÎ Gj¢¼†z+›ÿ…á/ú[ÿß¹?øš?á`xKþƒÖÿ÷îOþ&£•öÒQX¾7ðÅíÜ6¶ÚÔO3ˆã@’e˜œÊúÖŽ«¬éº²\êw‰k ¿–¬êÇ-Œã€{ V`^¢¹¿øXÿ õ¿ýû“ÿ‰£þ„¿è=oÿ~äÿâiò¾Àt•OVÿMÏûŸÖ±ÿá`xKþƒÖÿ÷îOþ&µ/çŠçA’â Ë ÉŒá”àƒÏ±¤Ó@r4QE@½ÇŸò*iŸõÁkëÜ|)ÿ"¦™ÿ\¶£¸™±EWA'­èzÝï‰\¶ŠÚ|°-œM÷äŒd͵·aw‡eä¦êzV·u.»Ay3ÜÅr–ò‹À¶æ&‡lqùdýíÝð;Øâ»š(Ú•ŒÖÚ«´× )"ä·Ë2ÂäìÛ÷vôùsÎkrŠ(¢Š(¢Š(¯5“þ?oëîoý ×¥W–ê¶Úwöåäë´72´’0$(ó1ØԊʯA¢z+›ÿ…á/ú[ÿß¹?øš?á`xKþƒÖÿ÷îOþ&²å}†t”W7ÿ Â_ô·ÿ¿rñ5³a©Øê¶þÂån-I`$@@Êõà€iY -Ñ\Ùñÿ„ÔuÛpGyrñ4ÂÀð—ý­ÿïÜŸüM>WØ’Šæÿá`xKþƒÖÿ÷îOþ&¯é^%Ñ5Éä‡KÔ¢º–4óQ\¹<ÜŠ9Xº×ü…çÿ€ÿè"¨UýkþBóÿÀôT+1…=Ô¿ûËýi”õÿRÿï/õª‡Ä„Íx> Õ€;.ÿô¯~Ésÿ>òÿß¾šðñ#Nr íò?Þ­6Oùèß™­Tù[òwÙ.çÞ_ûàÑöKŸù÷—þø5õ›'üôoÌÑæÉÿ=ó4ý¯Xó_ƒqɇ51"2v¿xcø+Í|io;øÛZe†B¦òL‡ûÕô“37Þb~¦—Ì“ûíùÔ©Ù¶>Oû%ÏüûËÿ|>Ésÿ>òÿß¾±ódÿžùš<Ù?ç£~f«Úù øEÑøØ³Äê>É/,¤vé^!ÿÿ®cùšêŒŽF ±„×+âù úæ?™¬ç.mFŒš(¢³Ô|>ÿ‘¾úå'ò¯a¯ø}ÿ#|õÊOå^‰âmP×"·K^èæ&bíjªL™ÆϦ?Zé£ð’ÍÚ+Ïÿá_ø‹þŠ6¹ÿ~ãÿ ?á_ø‹þŠ6¹ÿ~ãÿ ÔG Q^ÿ ÿÄ_ôQµÏû÷øQÿ ÿÄ_ôQµÏû÷øP Q^ÿ ÿÄ_ôQµÏû÷øQÿ ÿÄ_ôQµÏû÷øPq}4öÖÏmnnfŽ2ém¦BvƒØž•ÎxSâ7†ü`¢=>ôGz>ý•ÇîæSÜm= Ö-Ï‚5Ë;Y®n>%kQA $vHÀU$ž=+Ã4†>)ñ¶¹>§d³ÛXKpÒ¦¥|<¶pXáG%^8ÏzúâŠÆðÆ‹w hØ^ë7z´ÉÖâ羃ãêI÷­šç|cÿ ëOúû_ý«æŒË/Œâ1Äî>ÅÊ©=Ú¾˜ñüƒ­?ëíô®`Hà`;è c)rÊã>Oû%ÏüûËÿ|>Ésÿ>òÿß¾±ódÿžùš<Ù?ç£~fkä>gð…´ëã=´€/¡É(¾+Ô~1ÆòxZÁcFsöÜáF€×£y²}¿3H¬Ë÷X¡©u.Ó (}’çþ}åÿ¾ d¹ÿŸyïƒ_Xù²ÏFüÍlŸóÑ¿3Uí|‚ÇÉßd¹ÿŸyïƒ_HY‚¾±FAíò-t>lŸóÑ¿3T5rN•tI$ìê~¢¦sæ@‘ÆÑE‰A^ãáOù4ÏúàµáÕî>ÿ‘SLÿ® [QÜL»¨êv:E“Þj7pÚ['Þ–g £ñ=ë¾7°ñÍóhðÍ&hÂ3{"ìYd<•@y N:Ž+Ë~'ü ñV·y&«§ësk*2VÎíÂÖn#ŠUw…Ò=²rTñÐô®æ€ ñˆJ[ÁÞ&U“+àŸùnµíÕæ¬Åo¯H$µÍÐÿ¶k*ŽÖcGÊd¹ÿŸyïƒGÙ.çÞ_ûà×Ö>lŸóÑ¿3G›'üôoÌÒö¾Acäï²\ÿϼ¿÷Á¯zøfŽŸ WFVó'àŒµÛù²ÏFüÍ4³1ËO¹¥*œÊÁcåK‹K‘u(6ó¼ÿõ¨þÉsÿ>òÿß¾±ódÿžÿ}<Ù?ç£~fŸµò '}’çþ}åÿ¾ zOÁ˜eÄ™’'Aö.¬¤ËD¯hódÿžùšBîÃØBi:—V vµÿ!yÿà?úªZÿ¼ÿðýU À §¯ú—ÿy­2ž¿ê_ýåþµPø™ÓxþA¯ÿ_ÿèU«Y^ÿkÿ×Ä¿újÕKvET€QEQEW/âù úæ?™®¢¹ÿÈH×1üÍ š(¢¤gQðûþFø?딟ʽ†¼{á÷üð×)?•{ tÑøIaEV¢ (¢€ (¢€,QÏE4k$l0Èã qO¥PEP;ãùZ×Úÿè-\µu>1ÿu§ý}¯þ‚ÕËW=Oˆh(¢ŠÌaEPEPTõoùÜÿ¹ýjåSÕ¿äsþçõ 6Š(©W¸øSþEM3þ¸-xu{…?äTÓ?ë‚ÖÔw6)žT~wå§›·ný¿6:ã>”ú+ ¢Š(¢Š(¢Š(¢Š(¯5“þ?oëîoý ×¥WšÉÿ·¿õ÷7þ†k*»!¡(¢ŠÀaEPEPEP!­È^øþ‚*…_Ö¿ä/?üÿAB¤aO_õ/þòÿZe=Ô¿ûËýj¡ñ!3¦ðÿüƒ_þ¾%ÿЫV²¼?ÿ ×ÿ¯‰ô*Õª–ìŠ(©¢Š(¢Š(®_Ä?òõÌ3]Erþ!ÿÿ®cùš4QEHΣá÷üð×)?•{ x÷Ãïùàÿ®R*öé£ð’ªjzŒN™qs»É70A–>€äœõ«uSTÓ ÕôË‹ ÞL鵊2ú{pGÒµRÏX¸e¹mWM“LH#d•^2œçç1Èúrhÿ„ŸEþÏßÚ}˜Ê Î|ÃÈLc9>˜î*£xf[Ë;ø5m^æôÝÛý›!V%yä(ãvNI>€c‘xTnY®¯åžìßÇ},¾Z¨vDØ«´pÐ=ó@ ñ^ƒukss©nð[D&š@Ü,g?7Ó‚3ê§YxŸEÔ/¥¦¡— ¸ÆAÊòG#¨uÁÏJÊ_[.5ŸÛfÛ-±-´d.ö}ß_›…_ÃQG«Cí2’]ìÀÆ^>€ дïÙjÖÖ)»í3Ù­êìùãØN8qÁäVÕ`é>M"êÆho$u¶°EÎn žÇ­oPEP;ãùZ×Úÿè-\µu>1ÿu§ý}¯þ‚ÕËW=Oˆh(¢ŠÌaEPEPTõoùÜÿ¹ýjåSÕ¿äsþçõ 6Š(©W¸øSþEM3þ¸-xu{…?äTÓ?ë‚ÖÔw6(¢Šè$¼ñ Ñj6ÖZMÍòYíûT‘:‚…€mª¤åØ)ê:ž*ôÚÞ™©›-ìIy.6ÄO'9Àö'õÁÅgßxn[›Û¹­5k›¯‚‹¸âU%Èw#QŠ€ ƒ<Ò]xV;oíâöd…æ†y­‚©IÜ; p¹ñõÈ’øÇÃÐM$Rê°+ÆÌ®x*Û[·ðž§|T·ž(Ñ4ûÖ³ºÔaŠáp g$‚FTp:SŽ+>A=¬ðÙ€š øI 8R ÿÀqëÞ¬ÍáxfÕZüÜÈ® ºØc1!@>‡9 ^x«M³²kï5f³[¿BÛ™£\}Õï÷ºÖÌ3%ÄÍ%$PÊH#ƒÈàò+š ¶`±7“llúví£;d –úŒWI$&tÞÿkÿ×Ä¿újÖW‡ÿäÿõñ/þ…ZµRÝ€QE QEQEËø‡þBCþ¹æk¨®_Ä?òõÌ3C&Š)UK0U˜œI5#:‡ßò7Áÿ\¤þUì5Æx+ÂHQ¨ßôç\$yâ%>¾¤þ•ÒjzÞ•¢¤oªêVv+!! Ìëb:ãq®ªQj:’ËôW?ÿ ß„?èhÑ¿ð:/þ*øNü!ÿCFÿÑñU Ž‚Šçÿá;ð‡ý 7þEÿÅQÿ ß„?èhÑ¿ð:/þ*€: +Ÿÿ„ïÂô4hßøÿGü'~ÿ¡£FÿÀè¿øªè(®^ûÆžº°ž|c¤ÛK$eRhï¢Ýc†8àטxsãø³¾“JñtHaÅý¥§èø8ÜTu®Wò w¢¨iÖ™¯Ø-ö“}å³p$…òô>‡ØóWèñüƒ­?ëíô®ZºŸÿÈ:Óþ¾×ÿAj嫞§Ä4QEf0¢Š(¢Š(ªz·ü‚nÜþµr©êßò ¹ÿsúÐETŒ+Ü|)ÿ"¦™ÿ\¼«Ã>¸ñîѺ;HÏï¦ÇOöG¿ò¯fµ¶‚ÂÎ+h$0 UèoF/rY=ÏÿÂwáú4oüÿŠ£þ¿ÐÑ£àt_üUn# ¢¹ÿøNü!ÿCFÿÑñTÂwáú4oü‹ÿŠ ‚Šçÿá;ð‡ý 7þEÿÅQÿ ß„?èhÑ¿ð:/þ*€: +Ÿÿ„ïÂô4hßøÿ^wãߊá]jËSÐ5/[Òî—u§¥Ê;D㣩RJ†s‘•éÍ{%Âø;âφÕÝP^k'ü~Þÿ×Üßú¯J¯5“þ?oëîoý ÖUvCBQE€ÂŠ( Š( Š( CZÿ¼ÿðýU ¿­È^øþ‚*…Hž¿ê_ýåþ´E“Ê‘D$ŽBª(Écè+­Õ¼(4 Ãqu‡¾žå€xv¹Ú=}ÍiN-Êâdÿkÿ×Ä¿újÖW‡ÿäÿõñ/þ…Z´KvET€QEQEW/âù úæ?™®¢¹Ú¢€ &5ÉäÐÀʱI8I¯SðgƒWLTÔµ zÃ1ÆzB?ø¯åMðgƒœSÔÂ3G¤>çý¯å]½mNµbl*†§¢iZÒFš®›g|±’P\À²'®7Š¿El#Ÿÿ„Âô+èßøÿGü žÿ¡_FÿÀ¿øšè( þOЯ£à _üMð‚xCþ…}ÿbÿâk ¢€9ÿøA¦¬ ŒƒÚŠ+¤“Ÿÿ„Â?ô+èßøÿGü žÿ¡_FÿÀ¿øšè( þOЯ£à _üMð‚xCþ…}ÿbÿâk ¢€9ÿøAׄ’â:†•¯ÿßüÿÁÿ€íÿÅÕÿÿÈ¥cÿÿÐÚ·©8D.r_ðˆÝÿÏüøßü]ðˆÝÿÏüøßü]?Åy&¿£Ù[I{åËËÉ¥ÂÂÌWËÁ$öôË¿\iúüZZÅ CÚÀâyŸ)˜í ƒ£ÆIï†éŽW³ˆ\?á»ÿŸø?ð¿øº?á»ÿŸø?ð¿øºÉ·ñ=õŒ&òidºXloæ1ÀfŽè"’qÀ Æ{ Õ»ßÚéÒËök;™£¼6 öò“³4AáÚ} °C×g¹oþ¿ùÿƒÿÛÿ‹£þ¿ùÿƒÿÛÿ‹ªgÇWSèÖ·ÖÖQyvm¡y»#,å²G;ÕÐ ŽŸ…jèþ ¾ÕµX`û-¼6çN‚òSæïpÒ™UÇ~ï9÷£ÙÄ.Vÿ„FïþàÿÀvÿâêΕálµS©]È—7 ¡aÄ{V?|r}ûWIE5  (¢¨Š( Š( Š( Š( Š( Š(  ÍoJ}ZÎ8Re‰£”H“p8cµ‹ÿßüÿÁÿ€íÿÅ×[EKŠ{ÉÂ#wÿ?ðà;ñtÂ#wÿ?ðà;ñu¹â å¶ðÞ©q †9b´•ÑÇU`„ƒY>&^µÔ x¯n ÛÆÄHVªîr'=¿ ^Î!r/øDnÿçþüoþ.øDnÿçþüoþ.³µOÞéÚÿe³ó¶\É$fRC¬$PŒœŽ§ œaª]kÅ7§XŽÎÖk;xcÔìí˜4ß¿˜HQ‰UÆ Û}ðÜŒQìâ.Â#wÿ?ðà;ñtÂ#wÿ?ðà;ñuEÒi6X5½´®ˆÒÄňÜO$0ý)ávøŸþ}t¿ûòÿü]{ñ «”RêV#‘ž¸ü…1­ {„¸hci’”¸=Ex'ü.ßÿÏ®—ÿ~_ÿ‹£þo‰ÿç×Kÿ¿/ÿÅн­¼ ÷aŒpG :’?Í$v–Ñ@GoD„2ƨ©Îrny¯ÿ…ÛâùõÒÿïËÿñtÂíñ?üúé÷åÿøº÷§³µ–ÜÛÉm ÀNLl€®sž:óR,q«—TPÄ$p:§'ó¯ÿ…ÛâùõÒÿïËÿñtÂíñ?üúé÷åÿøºúŠùÿþo‰ÿç×Kÿ¿/ÿÅÑÿ ·Äÿóë¥ÿß—ÿâèè +çÿø]¾'ÿŸ]/þü¿ÿGü.ßÿÏ®—ÿ~_ÿ‹  (¯ŸÿávøŸþ}t¿ûòÿü]ð»|Oÿ>º_ýùþ.€>€¢¾ÿ…ÛâùõÒÿïËÿñtÂíñ?üúé÷åÿøºúŠùÿþo‰ÿç×Kÿ¿/ÿÅÑÿ ·Äÿóë¥ÿß—ÿâèè +çÿø]¾'ÿŸ]/þü¿ÿGü.ßÿÏ®—ÿ~_ÿ‹  (¯ŸÿávøŸþ}t¿ûòÿü]ð»|Oÿ>º_ýùþ.€>€¢¾ÿ…ÛâùõÒÿïËÿñtÂíñ?üúé÷åÿøºúŠùÿþo‰ÿç×Kÿ¿/ÿÅÑÿ ·Äÿóë¥ÿß—ÿâèßÙUУ¨ea‚È"«C¦X[Å$PØÛG£"D 8÷s^ÿ ·Äÿóë¥ÿß—ÿâèÿ…ÛâùõÒÿïËÿñtïafñGÚ@ÑÅþ­ `„ãÜPlmT”ÚÀdŒG1Œ¨€a^ÿ ·Äÿóë¥ÿß—ÿâèÿ…ÛâùõÒÿïËÿñtï?cµ 3 hwN1)Ø3 éózþ5=|ÿÿ ·Äÿóë¥ÿß—ÿâèÿ…ÛâùõÒÿïËÿñtôóÿü.ßÿÏ®—ÿ~_ÿ‹£þo‰ÿç×Kÿ¿/ÿÅÐÐWÏÿð»|Oÿ>º_ýùþ.ø]¾'ÿŸ]/þü¿ÿ@@Q_?ÿÂíñ?üúé÷åÿøº?ávøŸþ}t¿ûòÿü]}E|ÿÿ ·Äÿóë¥ÿß—ÿâèÿ…ÛâùõÒÿïËÿñtôóÿü.ßÿÏ®—ÿ~_ÿ‹£þo‰ÿç×Kÿ¿/ÿÅÐÐWÏÿð»|Oÿ>º_ýùþ.ø]¾'ÿŸ]/þü¿ÿ@@Q_?ÿÂíñ?üúé÷åÿøº?ávøŸþ}t¿ûòÿü]}E|ÿÿ ·Äÿóë¥ÿß—ÿâèÿ…ÛâùõÒÿïËÿñtôóÿü.ßÿÏ®—ÿ~_ÿ‹£þo‰ÿç×Kÿ¿/ÿÅÐÐß-<Ï3bù˜Û»ãÓ5à?ð»|Oÿ>º_ýùþ.ø]¾'ÿŸ]/þü¿ÿ@÷öh ÏÚ|˜üý»<݃vßLõÅ'ÙmÂ2"ØÉ啨0WŸ—éÉãÞ¼þo‰ÿç×Kÿ¿/ÿÅÑÿ ·Äÿóë¥ÿß—ÿâèÞÖÚŽ(ÒÕ"9B··SM{;Y HÚ†2 FÑ‚«Ž˜±^ ÿ ·Äÿóë¥ÿß—ÿâèÿ…ÛâùõÒÿïËÿñtïË+³ª(gÆæ“Ž™§WÏÿð»|Oÿ>º_ýùþ.ø]¾'ÿŸ]/þü¿ÿ@@Q_?ÿÂíñ?üúé÷åÿøº?ávøŸþ}t¿ûòÿü]}\Äù­ÿëíô¯/ÿ…ÛâùõÒÿïËÿñu™®|T×µ»$¶º‚ÁQdL@#»ZÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/bk-overview.jpg0100644 0000000 0000000 00000362463 15051152474 032041 0ustar00rootroot0000000 0000000 ÿØÿí,Photoshop 3.08BIMíHHÿáa@http://ns.adobe.com/xap/1.0/ image/jpeg Illustrator 2010-02-08T16:44:01+01:00 2010-02-08T15:44:03Z 2010-02-08T16:44:01+01:00 256 132 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAhAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FWJ/mZY+cN S8urpXlUtBfX08cdxfrcmzNtbpWR3EyCSVWfgIx6cbH4vDfFWAalB/zkLc6lYXFvFLbG0s3PopcW P1Jrr9GyxEyjl68rteFWjDBowCpIBBxVQt9E/PtLkzW11qMAnnmDy3dxps8qQPcO6FoeUtor+nwH 7tKDoKDCqceS9c/NuX8wINL8yQXsdhFa875/qtuNOJNnblPSuo0LNN9aaf1FEnH7PEUrgVLUt/8A nJdtPuPVuwl2J7qSARR6Wp4pau1vEOZlUwvc8FVjSSleXHsqutfKv5yQt5jgS8vYrSa4nu7BVm09 DcTT3isCJowJ40W2/Z5J8VR9mgJQttB/zkvHLbSXhFzB+kSbqCH9F28n1WifCruJ19D7VGp63iuB L2zFXYq7FXYq7FXYq7FXYq7FWP8A5g2WvXvkvV7Ty+0ia1NblbF4ZfQkEhIoVl5R8T78hirzyy0T 899J1HTbKyvVn0tNUlfUZ5nW5SS0Z4OID31xcXkcPpepQB2kElR9gq2KpTcW/wDzkteaZLbXYuY/ VmkKNay6VDcKp9Exo8qMg9FCJhyj4yt8PuoKo6G1/wCcgtNgu5YpZpobdbw21pM1lc8+cd/LE5cl 7hmWYWiovOlGpSgPFVF6fqf5z6r+WN3dad9dt/MralxtDqdpZ296LBQlT9XlW2tuXLl9qlRWnbAq C1zRPz71Owv7O7uJJYLmH0zb2p02JeUUVlJyhZgJB6s31pKSMRx8NjhVOtOb83o/MukWl7f3D2+o 3F7Nqkb2lsYbKztrstarHcwQmNmubdQjI8hccuQoQaBXqeKuxV2KuxV2KuxV2KoDVPMOgaS0a6pq VrYNKCYlup44SwXqV5staVxVAf4+8jf9TFpn/SZb/wDNeKu/x95G/wCpi0z/AKTLf/mvFXf4+8jf 9TFpn/SZb/8ANeKu/wAfeRv+pi0z/pMt/wDmvFXf4+8jf9TFpn/SZb/814q7/H3kb/qYtM/6TLf/ AJrxV3+PvI3/AFMWmf8ASZb/APNeKu/x95G/6mLTP+ky3/5rxV3+PvI3/UxaZ/0mW/8AzXirv8fe Rv8AqYtM/wCky3/5rxVH6X5g0HVvU/Reo2t/6PH1vqs0c3DlXjy4FqV4mlcVR+KpD5s1W9079DfV HCfXNUtrSeqhqxS8uS79K064qn2KuxVA3uvaHYy+je6hbWs1A3pzTRxtQ9DxYg0xVD/4v8qf9Xmx /wCkmL/mrFXf4v8AKn/V5sf+kmL/AJqxV3+L/Kn/AFebH/pJi/5qxV3+L/Kn/V5sf+kmL/mrFXf4 v8qf9Xmx/wCkmL/mrFXf4v8AKn/V5sf+kmL/AJqxV3+L/Kn/AFebH/pJi/5qxV3+L/Kn/V5sf+km L/mrFXf4v8qf9Xmx/wCkmL/mrFXf4v8AKn/V5sf+kmL/AJqxVNI5YpUV42DowBVlNQQehBGKrsVd irsVdirEb23t7j8z7FLiJJlXRbshZFDAH63bb0OFWR/ojSf+WKD/AJFJ/TArTaVo6KWazt1VRVmM aAADudsVYTdeZ4b2zuL7y9otguiWys0nmXV2S1sCqfakhVUeWdB/P8CH9ljhVVtPK+qalBFeS+aL RLa4RZom0fTrKOMxvQqyPdjUOSkMKN3xVRt/KPla7vbiwTzZd3V5a1+s20c1gskZXgWLrDbxlaeq tQdviG3TG0Kl95O0zTAHl84XNi55cfrS6S6t6aGVhxmtK/DGpY8SDTfpileuj+bbSGK4srnQ9ftW owjurX6jK6svMcbmAzxfEpqP3GKEx0DWNE1K9fSr/RRo+vQp6r6bdRRHnECFMtvMnKOeMMQCVNV2 5KtcUsg/RGk/8sUH/IpP6YFd+iNJ/wCWKD/kUn9MVeY+YdXXQ/zcktbSNIY73SNNLqihQSl3fitB T+bCEF6lYymW2Rz1IwJY759/6Zz/ALbll/xvhCspwK7FWF2Wk6VqH5h+ZDf2UF2Y7PTBGZ4kk41N 1WnMGlaYVZB/hTyt/wBWex/6Rof+acCoXVNH8jaVp8+o6jp2nWtlbKXnnkt4QqqP9juSdgBuTsMV YbfajaSxwTWnljR9C067lS3s9S8xrFavPLJXgkNkiGUluwkeNvbChHL+Xl7MOd7q+mwdeS6do9lE gPWgN0b07A9/bFVDSvInlvV4PX0vzZJfQ1C+pb2vl6RasiygVXTTvwdW+RwJWXHk7TLS7is4fM9g buYoILbUNO0mWRzIWC/BbxWTNyKMBTw9jhQiW0nWdNYtd+V9C1+2G5bSoo7S74jqVtboyRt0O31k fTilPNAt/IOvWbXOn6XZn0XMN1by2ccU8Ey0LRTROgdHWvQj3G2+BUz/AMKeVv8Aqz2P/SND/wA0 4qlfmjyv5Zj8s6u6aRZI6WVwystvECCImIIIXCrFfyf8y3OoaRYxSHaOCNB8lQD+GJQ9SwJdirsV dirEbuaKH8zreaZxHFFoV48kjGiqq3VsSST2Awqlel/np5H1SLT5LJb+X9IiPgFtJGWKSSWGH0pZ V5Qqym5QvRyF7mtAQqM80ee/Jb+UYDq93Pp9l5nspltWFvPNKI5Iwrk/V0mCMomB3P6jirGD5k0X zF5Ah0bzR9b0vRp1jiGvWMEsdjcw2cwUjm8Zks+ZhIZJkSm/FmFCSrH1/wCce/J3mAPNovm0Xtsi 2q6e0ZtrwJDBKHZZWQ0mXh8CCgUbFg1MVZHH/wA42eTY1mCahqJMsUEI5tbuoFuIApZGhKSV+qrV XBG526UCrn/5xv8AJjXVzdfX78TXLMzN/olFEkc0TKq/V6Cq3LdtqKR0GKsd1v8AKf8AJewE1rfe Zrn61cPzWzimhurot6M0QKW8UMs7lTcFgeJIoo6ADCr0bS4dX8weYtM1m40+XSdH0VJhp63vFb66 lnjETPJEK+jEqVorHmzUJVeIqFZpirsVeIfmP/5Oq0/7ZFj/ANRd7hCC9j0n/eGP5YEpF59/6Zz/ ALbll/xvhCspwK7FWK6H/wCTD80f8wml/wDY1hVlWBWPeeNG1LU9JgbTFilv9Ou4NQtrW4JWGdrZ +XpOwrx5fstQ8WCntirCPOS+RPPT6fYeadQvvK2pWPrG3066ZLJvrEiqoljlkVobj0+J4GKRl3Nc KpXp3/OM+ierp91PrtxciKeW6u3hjij9Uu6vD6TAN6ZXj8b7s3YqABiqcW//ADjt5QhhtYlv79lt TVS5tnrW2t7fYSQuqNSzQ81AbcitMCoew/5xp8mWbwsmpak3pClC8Ck0MhHFliDJ/fv9gjt3GKpZ Yfl/+UnlHzFp17ba9d3uuac6vbaZatFd3LlTcHh9XtYS6I5uTWgVfhFe5wq9H8pWGrS6vq/mTUrT 9GPq62sNvphZXljhtBJxluGjLJ60nrnkFJ4qqrU0wKyjFUr81/8AKLax/wAwNz/yZbFXkn5D/wDH Ptv+Ma/8Rwoe34EuxV2KuxVhGvtaL57dry0N/aDy3qBuLFYvrDTx+vb84hDRvULr8PCh5dMKvO9L 1jW7jVbB778q7C01ZrqMapftpdwWF39agEt1FLHayxcaNI6Obg/Z5FxQBgqO1nXNCstD0lT+XFjq t5cmaXUdOmtlsRa3dLWGb0obu3fl6v1iFAyfC5oOVcKvSfy5vor/AMm6feQaND5fgnEjw6VbMjRR qZWPNDGkSESmsgIXflXvgVI/zc8seUB5C8y6tc6RZHULfTLt7W/+rRm4jn9FvSdJVX1FIkpuDtiq ZWn5dfl/cWsNzbaeRBPGskTLNdRVjdaj4ealQVO6kexw2hjfnP8ALjyrDqXlb6rZQrBPq6Q3tvOn 1pJ4jbzOY2W4MihT6fYdaHtirKtb0/U9C0bj5D0bT0vCzcrb00ghKrDI6CkbQbvKqJWu3KtNsCWL Wmsf85An93caHpiM1s8xnJVkS5/eskAVbxWdfhjXmeP2j1pXCqtqutfnpHdWbafoVjNB9Xtjdxkx b3MgH1gcmvIyiR8TxYBjuBRvtBVMPKGr/m1PqtlbeZtDtrXTvqp+v30TxcjdD4l4KlzMQhB4t8H2 gaHjQkKwv8x//J1Wn/bIsf8AqLvcIQXsek/7wx/LAlIvPv8A0zn/AG3LL/jfCFZTgV2KsV0P/wAm H5o/5hNL/wCxrCrvzJPn0aBG3kdVfWVnDMjtAitEsch4sZ1daGQICBRiNgy/aAVjVrrP58XN1ZW9 7oVpYwF7dr27tWgkHH9yZ0Hq3TMq7y/EI2OwUD/dhKsk0aTzLqlrpdpr+jqtv9UdNeW+W2kL3ix2 7I8QhlliMbs8wPw9V/ZH2grHvOfk3yLpuo+Wxa2NtpL6jqyW84s2+omWNreYlf3DRdZAm43r88IV ko/Lvy0uyTarGg/3Ums6skf/ACLW6Cb99t8CsVH5feXX/MprC6gS+0Y6QLhdPvTNdgTC44cy9xLL XYkcacadq1JKFXWU/NPRtZnsfJHlzRofLa+j9XkCpDIzcoTJyRZol40aUfYBAFRU0BUoqy1n86p5 r159C0+1t/Tn/R0ErKZfU9CR7b13iupVoJkRJAq786gjiahUms9f/wCciwt7Pd+W7BpSix2FoksC xBx8bSO31t3Kn4kry/kPBfixVnWoy6rL+X11Lq8MdvqsmkyNf28J5Rx3BtiZURqtVVeoG5xV5j+Q /wDxz7b/AIxr/wARwoe34EuxV2KuxVhmqw3c35iJDZzi1vJPL98ltdMnqrFI1zbhJDHyTmFbfjyF fHCrE9L/ACG1XT7jT1Hm2a50/TJIxZ200VzyW0hngnS3Pp3kduafVgK+hQk8ipIWgVONQ/LrzK2g 6bYN5sOm2uiJIguLNLu19W2Cw0M/o3sXxosUo5VI+IMApG6rXk1vP975csrXTbpItNVWKeZdVSe4 vL0O7P60NnJMzRxty/dmacnjT4AtBirH5vN/5cXllqEuveedYvrW0n+oagpWSwt/UkWX92EtLe3L Iwt5BuzdKVqRhVN9T/5VLp7WI1G41UzahbC8hE13rc8qwEfC81ZXaIEjivqU32xVDwH8ktTtorqD zDPZiB2ntjLq9/bsjxQCR5UhuZ+NVhm3bh0qPHFUdpMi6hf3Fn5M/MBtSu7SJLiWzvUh1G3COzKv 76FbeXdkIP75uPh4qppZ+dtY0/VotF81WCWV7OGayvLdzLZ3Sp9r03ZVZHUGrRuK+BYb4oZnBPHN GHQ1BwJVMVeIfmP/AOTqtP8AtkWP/UXe4QgvY9J/3hj+WBKReff+mc/7bll/xvhCspwK7FWK6H/5 MPzR/wAwml/9jWFWVYFQGua5p2h6ZNqWoSFLaHiKIpd3d2CRxxotWd3dgqqNyTirGLrUfP8Ae2M2 oyfVfKmkQxPPJ68Z1DUfSRSxLRxukELcRXjWX78KsFHmP8rvMDaTLqfnHWdYuZ7nnpMJha2b60jp CDBHbWsDq4+sDia1pU164qnuqXP5V6ZfX1lqGta2brTgn16ZdT1wxI7tGoQvDMIOY9eOqDcA1I64 FQ9uPytmCa5Zee7uwna2jpcy6kjTLDL+8jDx3yzUDVBCstOm2+FCZaJf+ab61uL7yl5osfN1laTt bzRXUYt3MkYDMqXlsPRaqsKH0OJ/mpilPPLnnyLUbifT760l0zVrNgl7YXHHnGzCqkMpZHRhurqS D9+KGWKwZQR0OBKWea/+UW1j/mBuf+TLYq8k/If/AI59t/xjX/iOFD2/Al2KuxVQu7+xsxE15cxW yzyLBCZnWMPK+yRryIqzdlG5xVhfn/QfLevhJZJoGv7d3sIZVn4SLM6iQ2/wOvx0UNw603whBeTR /ljdXjzNY6jJPHDI0MphuZHCSJ9pG4yGjL3Bwqo6p+Vt3Z6dc3WoahLBYwxs1zLLNIEVAPiLcnpT FWaeS/zg1TSdGs7TzZo94bWOMJaazbxcmkgQUjkuLWoljZkFfhDV/lXpgpW9K0P/AJxsvmuTDdWR e9uVu5re9vJ4HaVTIRSG6kjagNw/whaVPyxSyy68l/ldqsOmmV4biPS7ZbG0kS+kFbeMUSORklHq hDuvOtDviqS3v5Y/kPaKTefU7YelJEzS6jJHWOWL0DUtMKjgKLXod+uKoOz84/kV5Ju7m90K7a5v blHjli06S5vllUzPcdeT2+0kr8WLDrQHfFXl35l/mRrf5iarYaVDYyaTpMEpmsI3as083Eryd0+F WVGaiKehO57KphpP5V65NarI1zcp/wA9ZR/xvhQqw/lxLPHbyQau0sd3y+qOl07CXgCX9MiT4+IU k06UxVO/KH5fw2uq2V616kz3wX6pK0vqGdEBkHpMzNzUKxb4e2+KvebKIxWyRnsMiljvn3/pnP8A tuWX/G+EKynArsVYL578i2mqTy6jbmeDUpY0iknt7ieEssZPAMsUiKePI9sIQ8zl/LTzVzPHVdQA 7f6Xc/8AVTCqmfy8852c9tqFtqVzLeWE0d1apdSzTw+rE3JecbuQQeh7+G+Ks/g/N3y5d2c2k+cb G40Ke4je3uuayTWcgdeLiO5hFVBBO8iocFJQmg/l1+QNxBB+h5LK7EJLwSxak80iv6kUnIN6zMrq 1ug2oQBTucCsq1f8q/I2rahe399p5kuNQC/Wws88cbOpjPqemjqiyH0EDOBUgUOKsfu/yq/JPSat fRW9kUSCMtcX8sVFt0RY685h+yiV8aDCqX2n5jfk35C0+ew8ryyaiJX9YWOnPNeRlwoSqzSsYE5c RWj79d8VeI+bvOur+b/N7a7qXqaKFEVhDbQPIphh5O8RllQpyDMznmRTttTFWe235bebGhU/pW/F QDtd3P8A1UwoVG/LDzRIrRyapfvG4KujXVyQQdiCDJir0b8t/J0uhwxo2yoKAU7DAVeh4EuxV2Kv Jv8AnIGby3HZeXv03LegfXy0C2MqRelxUc71+ccob6sD8I2+11xV515Dl/Lo+aPLQ0z9Ly3H6YkF j9amtODKbWI+vKFi5cjsGRfirvI1aDCh6P8AlRe+UtI0vzdqCP8AVNPs9TkF3cz3cN0gEaKoVPRS Mjj9kAhix6O+Ksg07SNQ823sOs+YrNrLRrZ/U0by/NTm7A/Bd3y9PU7xw9I+pq9OISo/m3rei+Wf LR1W8smvpJJktre0jYI8jPVmoSrfZjRm6dsKHkGp695B1PUWs7OxmnSW/g0+1mLRBJDLOsDyEGpU KZKqv2mG9FFSCqXXvkzR9QvNXsLaxMM+j6ppllLKQp5C61JbShoo4E8CQtTVCp74FZrD+RNspp6I ArXZVG/3YVTix/JW0QguD+H9MbVkkf5S+XJrF7O7iLRvQh0PCRHU1WSNx8Sup3Vh0OC1pRF++m2N 95S823RtXntLhdN8xRBYxdW6xNzbcFI7uFPiZKUanNdqhVXk1lpX5FTW+iSR63rUUM8cq2dpIsMp tV9S4okqrDJx9aYS8Y1r6m1QV6KXo+gnyRE35Z/VLfUZZFsj+gLxoYSTA9mUK3jL8WyNyPp/CrUL bEYq9UwKxbz7/wBM5/23LL/jfCFZTgV2KuIB64qt9NPAYq0YYyKFRTFXjyfmZ+U3mWOU3Es2nSDm ypdQ1LrHF6zMpgM67IejEN2phQkUHl78tfM+ry2OjOdQmS0a+lljgZkCIY6p8SBzLSdTwC1Fd99s VYPJ+XWh6jZ6lqWj20cNui6Z6H1i2Qlfrd3JbPWPahqvjirL/M/5T6Z5X8vXGrLYLOLZoUW3QLHy aedIQAQj0+KSuynFUh0HzP8Alx6EUl7bXkM/qwQzRIsciq9wrMKHkjMECfF8IbwU4VZTJP5c1ryD f+bNF02SK40i8FjaySGFxJDMsJcXcRIDQSCfjJDUsD/K26hWTeVvNv8AhZpdM1iF7XRrN0iuYJWM sujySGkavJ1m06X/AHRcfsfYelPhVerKsTKGWjKwqrDcEHuMCVwUDoMVbxV2KuxVplVgQwBBBBBF dj1GKoHV9U0fRdPl1PUpY7W0twS0rDffbioA5MzdAq7k7DFWNaPoOo6/qMPmDzDAbSxgcT6J5cYA CFx9m7vKbPckfZTpF7vuFWaYqxT8ypfPKeXlXyTbrNrklwipLJ6PCGMBndz65C/Fx9Px+LFWILqn 56T6jU6DBZ6fNqVt8H+iu8FklwnqUPrNyLwci7kEg/ZVa1BVN/NQ8yhtTGp8G0n9O+Wm0ZkREqp1 W09YNRndmVgAS30ADAr0OgxV2KuxVLPMvlrRvMujXGj6xbrc2NytHQ7FSPsujdVZTuCMVYNoflzy tp2rx+XfNGg6ZJqMgYaPrX1O3SPUIgp5IwCBY7pU/vEGzD412qFKs3Tyn5YSfT7hNKtFn0qMQaZI IU5W8QFAkJp8C+wwKmuKsW8+/wDTOf8Abcsv+N8IVlOBXYq7FXYq7FUgb8vvIjOHby7prMI/SBNp CSIynp8Ps9OG1PDFUVpflPyxpNybrS9Js7G5aP0Wnt4I4nMdQeHJQDxqo29sVee+ZLGysbvzTa2U EdtbR/4a9OGJQiLy1J2NFG25JOFCfyfnF5Lh1i90u/eeyeyuxY+vLGHjluKkcIxC0sn7PV0UYKSv 0782Py9vre1kju2iN41qkUMtrOrGW9QSW8e0ZUu6MCApO3tiqf6Xr+majquraRbxSrPpDxJd+rEY 0YzIXUx8qFhRftUp4VxVL/OXk9taWHUdNmWx8xWCutjeOvOKSN/7y1uo/wDdtvLSjL1H2l3xVhPl PzjL5aM1leWstvollIItU0xyZZ9CkfcMCN5tMk+1FKP7sdfhFEKovV/z80DRvMup6VqFoxs9Pkih S7tpVmllM1ulwJPQKxgRBHPxCRjUfZ3wKr2P57aBcWQuZNK1BaSxQTCIW8yxy3E00FvGxEqnlI1u R9mgJAJw0rLrDzObrzfq3lxrN4TplraXaXbOpWZbtpV+FFqVCGAj4jU+FKEhUPrvmLX7fzBa6Hou m2t7cT2k17JLeXklmiJDJHFxX07a7LEmX2xVT+vfmd/1Y9F/7jF3/wB4zFUh1Py3551PzNp3mC90 bS5p9KVhZ2bazcG1WRq/vjGdKP71eXwtXbt3xVPvr35n/wDVj0X/ALjF3/3jMVUvLHnqTU5ryy1K 2hsdSsbqW0nggna4jJip8SyPFbsQa90GGlTfzTrx0Ly/d6utv9aa2Clbfn6fMu6oBzo/HdvDAqWf pn8xP+pZs/8AuKH/ALJcKpL5ssfP3mTSo9Pm0SKzWO6tbsTWurlJOVpOk6iv1Xu0f0Gh6jFU6/TP 5i/9SzZf9xQ/9kuKob/HGt2Ot2Wm6/o8OnpqEc8lvcQ3n1ne39PkrKYYaV9Ud8aVltvcRzxh0NVO BWKw+bfNl/d6jHpOgW9za2F3LZGea/8ARZ3hpyPpi3koPi/mwqgfMkHnbzBo8+mXnlmzCSisUyao RJDKu8c0TC1qskbUZWG9cVVtDuvzQ0/R7OxvNHtdSubaJYpL+XUuMkxQU5uBbH4iOp7nfFW9b83+ etG0a/1e88s2ptNNtpru4Eep8n9OCMyPxBthU8V2xVb5r1m01AeXTA3Kmt2f/G+KEZJ5q80XGsat Y6PoUF5b6Rcx2ktzNffVy8j2sF0aRiCXYLcqPtYEr/0z+Yv/AFLNl/3FD/2S4Vd+mfzF/wCpZsv+ 4of+yXFVO41/8wYIJJ5PLNnwiVnemqGtFFT/AMeuKpn5Y80WmvaVZ38S+kbuCOcwk8ihkQPxrtWl aYFWeZ/Ml5pE+mWtjpx1K91SaSGCESpAB6ULzMxdwR9mM4qgv8Q+e/8AqUv+5hb/APNOFDEtS0Dz vf6vrGoSaJME1ZdOH1X6/amOI6dP64K/Dv6hAG/2fip12VTGXR7ma7mvJvy2sJbqeUXE073FkzvM taSMxQksOR3xVrQIfK1zrDade+WodCv9CuLe6htYJEMTStCVhlIg4I7Rx7JyB49qYqza8bT9LtdS 1pLZPW9Ez3TooWSYW8ZKhm70UUFemBLH7PzZ52vLOC8t/KdYLmNJoidQgBKyKGWo4+BwoY95v0vz 7rk9lqFh5eGk63ZNxTUkvLeVntWNZbWVKxh4pPAnY7jfFUV5cs/OegNdwWflflpUzrJZae99bEWn X1I4npy9ImhRD9jcD4aAKorVfM17b3eknzL5Uit7WS+VbS7a4t7n0br05ZEkVAtQ1Fb4h0rirNbK 8t7pPVioeQFW707frwJY9N/5NGy/7Yl3/wBRdthVjPm3yt+cM3nO71fyxrMNrpjwxpb2lxcSlAQq iQLA0U0CuSCQ5VsVQ8/lr8/576GWfzDp3owzfHHbiSFXhfaQBRFt8H2eZdgwJDCo4qsl/L3SvzK0 9rmLzfqdrf2qQ28dgLfk0gaOMLKzu6I7Fj1LsxY7/D0wK800O4lX81fMsQb4P0lMaf8AA5JD1D8y z/zoOof88P8AqIjwBSs/NLQPOOt+XoLXynf/AKP1FLpZZZRcS2vKERSKV9SEM323RqHbbAljb+X/ APnIF7P6u3mHTKtJKskqoVcwsqhCGEAZej0UHmrEMZGA44VdoPln89bTV9OmvvMFjNYKYE1aMmSR pI4ZJORjDx0QtCyqaUYtQsxpUqoD8/7iSDWfKLxmhP18V9v9HxCC9D8lStJo8bMamn8MSoQv5f8A 2fMX/bbvf+NMSlhem+Ufz7067u2tvMFm8FxeCRBezT3YEPqSH7MsZKp6bKpjjZSSAQy0NVVk3lH/ AJyEuZeUvmSxiEsNvFL6byAK8NzLKZKRwwjeNlVhGIy44hjRTyVT7WrXzhbfkr5oi83XcF7rQ0jU zJPbALHwNtJwGyQioH+QMCvO/J15PcQ6F6jE01m06/TkkPYPJ3/Hd86/9tqL/uj6dkUsMPlL89Lb zNqt9pmv2i6bd3zS29veSzXKrbGaR0URPGyxhY2VWSMry7OtKkqpP5U/5yAnmWa48w2VHhWCeGKS SIVWSR/UHpwxqDuv2VVitFLfCearM/L1p52tfKepx+cL62v9TP1hopbROCCD06KtOEf7QYjatKVJ OBWAfkNdyyaXaoxqqxoAPYIMKHoXmf8A5TDyb/zF3n/dPnxSlP5g+WvzI1LzJpGoeVNVjsbO0Qi6 hmnnSJpPVVuUkEalJl4ArRj92BUjuvLn/ORN1aLEfMGmQuBCyyLyVleO7SU8zFBFzrEpVqcUZfh4 V+PFU58jaF+b2n66ZfMms2l7ocqzSPaqTJOs0khKAO0Ueyin2SqDoE74qw7zNezQfnXqESMQrw2Z Yf8APE4QgvVddYt5I1Qnr+j5/wDky2KUG1jq9/8Alctlo1x9U1a50ZIrC55tH6c72wEb81BZaMeo G2BWH6T5b/P6w0ya1/TtjKRCRaGWRp5Vka6V6NPNbyOzCDmvN+Q3UcKjmSqHk8p/85DRSSSW/mKw aT1pZ4mLy8FWZ25RiOSKRdhx4c+SoNlWtWKqL/PJtQtvImgG8lWTUI9RtRdTRjijSi2mEjKNqAtW mIQyT8sp5JtGjZzU8V/ViVCr518s3l7dQ6vpuoXen6lbW8lsj2rRgNHIyyFXEiSftRr0xCvOLi2/ N9ZSI/MF6VHQkW//AFTwqpGH84gKnX7wAe0H/VPFVKKT82ZbVbuLzLcyWroJUuFNsYzGRyDhxHxK 03riqYeQPJ2qjVpNYu5jeSX7fWZLwsjeqZKHmClFII8MVes+Y9Ch1vy9c6TPLLBHcIoM0HASKUYO pX1FkStVHVTkUvLPML/m7ZXTR2Hme+kiHQyWmmMf+Fs1w0i0o/Sn55f9TFd/9Ienf9kmNLalBrv5 03EC3Fv5nuJoHHJJY7XTWRh4hhakHGltBTaV58803WnX2t6pNq8FsrPYv6VqkfGcKWZWtoYuQYIt Kk4Vt7z5UspLTS0icUIHf5YCoYD5t0jzjodxfT+WNevrSK/uXu5bQQ2E0ayygcypltZJKfD0LnFW GS+YvzujVnfzHcrGgLM7WenAADckk2uNLaxvNH51Lbm5bzNcC2CeoZjaaaECAcuXL6rTjTeuNLaj qF/+cWsaXc6de69dXGnajA9vcxi1sFEkM6FHAdLYMOSt1U40tp15d0C70yLQROhWus2YFRT+bCrK vOmkebNLv9R1Ly1rd7YnVJ0ubq1SKxmh9VLeK25L69tLIKx26VHOlcFJtgza9+eIJA8w3VP+YPTv +yXGkWpzeZvzrgiMs/mW4iiWnKR7TTVUVNBUm1p1xpbWXnmH85hbSC88yXEds49ORntdORaSHhTk bUU5FqDGltnH5QeWrrSbdIpUYKgCgsN6BQB4YlWY+d9CvdQSwvtPvpdP1DS5ZJraaJIpamSFoWDL Ksi04ue2IS8qv9f/ADqhnZIdbkdAdibKz/hDjSLQc3m786oIjLPrhiiWnKR7OzVRU0FSYqdcaW2p /N/51W8Zln10wxAgGSS0s1WrEKoqYqbkgDGltd5e0LzbqPmxtb12V7u+mCLJMYkiHGJSqjjGqL09 sKvdLjTheaBPpzsUFzbvAzjcgSIVJHyrkUvLtSl/NfQ4oLDTdXSe2to0iiL2UVeMY4ruD4DChLH8 0/nYiM76hCqKCWY2UYAA3JJxpbWL5w/OZrYXS6pbm2KeqJxaRGMxkcufKtONN640tpfrSfmZ5stb S21m7S50+KZLuIQ2yR8mVGVTzXtSQ4VeweQNLmsNKSKVSCFUb+wwFQykqGFCKjAlh/m3z55f0CSS 24Jc3sSepc8pFht7VCCVa7uGqsXOh4LQsx7U3wqlf/K2PJyMlprltc6SblVRbswvPZs8gH7tbmFX TmOQryC74qlFr+QXlHULG0ceYtS1G2gtI7GzmSa1aMQW8gKIvpw+myjjxYEEN+1U4qp6P/zjjpFl qT3Fxrd5LawzWsthBDxiZRaepxE7nmJGPq05KqkAfDQbY2r0ryr5Z07y1oNroun8mtrVSBJJx9R2 YlmdyqoCxJ60wKq6zeaLpdhLqOqSpb2kIrJK/iTQKoFWZmOyqoqTsN8VYX/ytLylAZZNStLiztEK ESrG1w8cbqGDXUMAeS2+19lxWnWmFaY9oP5O/l35i0+zvNM80X2r2drA1taPFPbMiRcyyxsiQgbM TyDj4v2q0GKroP8AnGzR/rsv1nWrxtNWK0itIIaRTL9UiSEGWUl1eqx1A4Di260oMVeneV/LWn+W 9Hj0ux5NEjySPLJx9SR5XLszlQoJ3pWnQDAqYXEFu6lpgOCirM3QAdzXFXmuu+efKl4bjS7a1a50 66imglv0kSKSRTHR3sYpAWuVXlTmvwlqAcq4VphmkeRfym81zyWDeaNWGsfVIrWeyv1js7hY43Aj Uwy28cZooVQFBFfj+18WKsl07/nHXSkupptR1m6nX6+b61jtlWAKpqeEgb1Q5q2zAAgbd8bVkOte XdO8v6N5Q0mwUi3stWsIUZgvJ+IervxCgs5+JjTcnFDOZreGUUkUEYEsE1nz55Xs79rSygS+MMqw 3VwZFghDknlFDI44zypQ8kj+z3IO2FaQFz5k/LXzpZ3fleS/n0fULhamC6hazuVEbhxJH9Zj9NhV R0riqlN/zj35auL17661fU7i6luYbyd5HgIeWEdSoiCjk2+wHEfCvFdsbVL9E/5xs0a1tQmpa3e3 NwBcIGt+EEYjuIlh+w4n+MKtWavxH7VaY2r16G1t4beO2hjWK3hRY4okAVVRRRVUDYADpgVJfMWp eW9Dgim1E0kuZPRs7aNec08pFRHGg6n3NFHViBvhVhnmHzV+W2s6VJoWt37+XW1H4IbqcJGnOKQE +ncsJbaqulPiPj4HFaUf+VBeVNRkOqDXNQvZrmeG9N0ZLeSKWSEbNxWMIQzb/DTiPhUqu2KoXR/+ cbtGgsIIdT1q8ubiBLmESWwSCP07heH92wmo4HVgfi79MbV69b28Ftbx29vGsUEKLHFEgCqqKKKq gdAAMCoXVZ9KsLGfUNRkS3s7ZDJPPIaKqjucVYFe/mP5SmtbyO9s7qDTJYaCeKNpboQy1jM0toiv NCndS67jemFaY/oX5T/lt5s05JtM82Xurxx2cWnl45rUskNvKskavD6A4UCKtGXfqRy3xVE6V/zj do9tfTXF5rV5NCt2lzZQw8YyqIXIWZm9T1G/eU50UgCgoDTG1eo+XdBsNA0Sy0exBFrYwpDGzceT cRu78QoLuasxpuTgVGXa3DWsy2ziO5aNhBIwqFcg8SRQ7A4q+WrjQ/MlpcadoN/p7peWRW6axFwz l5WWKCWdpVjJXm9QsjpMnxfajOEi0gvQdDn1GbTZrPypoy2UjTzpqlxq8pcRzsxMpIjaU3TcuYBE iqfFcnHGa8kGaI/LbQ/L1l+YN69orapepaxi41LT4YYNNtrkeok6SJAVjEzKsY4n1HFSagVyMhSA 9hyKXYq8R/OVvM9n5pGpmGVtKitoo9LukeNo4Z2E6zskTtG/1ghxxaNlagUipWmEKkHkaDStJkth e6fqLayn1g2AG8VwS9GfmBa8pVcAH60oam+43wxjZ2TxVzR3mTQp19LX9YFt5SuHvbdYE0j49Umj knjWdDPEImkk4BjwiRu1GPE1JjQY3b3myVFs4FQSBBGgUTlmloFFPULksW8eRrXrkEq2KsD/ADl0 3zBf+V4I9HtpL0R3aSX1nDKsRkhEbheRZZAyJKyOV4npWhpTCFeMadLDcX95rGv2F3fae5jiluLR iFQJN6UQQDiJYucBBW3lXxaFaY1bIFn2qWF3rsFdStNL0XylbLX65qSxXU/on7LJHLxhtgKmjMzM DT4cn4ZHNiZ3yZb+TVvaw+RrP0I7zk/xT3F/XlNJQcmiqT+5H2YyqqpHxAb1yBVH+ff+mc/7bll/ xviFd+ZtjrN95LvrbSI5ZrtmgZ4Ld/TllhSdHmjRqjd41ZT4iowK+f7aOW51B5NYsbu40/TrV7W6 trJ1pbxxqvq+m6okTMvrD1PSaGU1PwPWuEi0gvQ5rO817S4LHTdKsbbywiB2vtYQXJ48fj9Ozk3q D1aWVe+xyfhnqgzHRP8A8lLXTLfSNTjsLi7vo0vZYk1CY1tJIY5HEAs+B9Ap6dGYxKBVqGpG0Cr0 fArsVfMnni58yJrOtRakl1Bczzzq15M6KPqa3Es0UEEgMrpamGNSwaP03OzMhwqyDy1Jo9tptzYa fpF9q+rSLFbXmg3CQwRQhIlVVZWP1NIdgwdOfLxJwxiejIyCM0DRW0bztoP6Rv8A9BPqRmlPl3RH u2t5pYTH6IlQUXhxf4j6KL8FK8ScMo0wt7fkEuxV49+dLeZbTzBp+qRwPPoVpa80cmEwRXiTEszx zMoeUoU9LdTUHi3UYQrDPJMOl6dc28mt2movfyTSz2lxV3S5kNFk9UotvM0iMjf70grXYM5FcRGz sni70283aPqdxYXHmLWI7TynKjxm0uLVo31Z3LgMpuFMSeowNUjjL1+yGqaiZhQYmVvcdDEI0ez9 H6z6PpL6ZvTI1yVpsZjKTJzPU89/HK0o3FWFfmz+YknkLy3b6xHZR3zXF5HZ+nNMbdF9SOR+ZcJJ 09KlKd8Veea3+ffknWWhsdS8vXky20dzdS6hHJCnoPaJOeVnMHVi0j2rqtShZex+zhVR8tpp9tbe arGd9b1HTdGtm1ubSJkt4WlF3D9ZWGW5tZHaYy8mb4QoqGrXueI1SKZt+XX5p+VtQ8v3H+hQ+X7L RoEkuFieM2UcclzNboYSgjbgzQcgTGB8Q64Es00HzT5e8wRzSaLqEN/HbsqTtC3IIzqHUH5qa4FT GeT0oZJaV4KWp0rQVxV4NZ/85OaLqXluebXPLTXCXCyBLG2kjvInCeivpziVYqFpLlFoEfxxVKE1 +1n1nR9X8tjUbK3a4j0aGynit7+3S51KCK4KW8guYXPHkjcpQR8PwkCoycZEIItlWjec/Lmifmhd aHd6NcXesCeKwXzFcypJcF52hApHII1gt3+sfuxETy4N8JORJtXptv548o3OrnRoNWt5NUWZrZrM P+8EyiUslP5h9Xk/4HAlPMVeT+cPz5g8r+f5vLN3pYntYUtyJ4rhRdSPdcQixQSKiNRnHKsootT7 YqwjXvOfk/zpDfNpWlalpOrXIe7SXTzZu1xFa2sV/wCtd2zyxKSRLtxbkxWnLbiCFZQmoadH5c8s ebdSsJvMn6Tv7W1smvFhtrazWe4FvHcR2itcDkynmm7n/KXDKRKAGdWn5qeSpNG07VL3UI9MTUrZ byCC6dPUETNwqfTMi/a22bIpd50uILm38sXNu4kgm1mxkikXcMjhyrD2IOFCH87fmronlHXdM0e+ s7q4l1JGlE0AiMcUSOqM783Rjx5VooJ8MCWC+ZvOn5P+crq4trO81Cy1e8t0gOr2NlcB5fWYRRWz xtHymL8xx+D7J+F13wqtttU8kweTV1bXNQuPN2m6ddQaZFYRWTWUD3XBHAkt7hwkrrWrl34g7Urk pSJ5oAepeWfOXljW9Ci1XTbgQ6bX0U9dTbBWSMOUCyBR8Kfy7U6bZBKeQzQzRiSF1kjNQHQhgSDQ 7jwIxVKvN/maz8r+W77X7yKSe2sEEkkUIUyMCwWi8yq9W7nFXnWr/nD+VWu6TAurwXcd/VDb2ggJ vIJJ3SJWhnjLRcysysQkhqhoQd1wqknk2eC685y2Oleajc2mvolzaSz6bcw3xhhWRHEbvHHbFKW5 Hq0+0PsknJCZApBDJ/yr85flrd3txY6FbXVvqs/OWe8v4zJcXaRqjmaS6BlHEiQFUdlp2UZEpek2 2o6fdMVtrmKdgAxWN1chT0Pwk7YFRGKvPvNf5v8AlPRPNE/lXWbO4eFbUT6heGOOW1WGVHPF4+Rl cMF4kLGevhirzq41ryJqfmOzm8na1d6aNOnKrp02mXNzaMHiZ5xbRqI5k9BEZir/AALQcQFyUZEI IZlY6/8Al7pmlWfnhheeYri5uU0+DWWtpCY3klEDegsojjt4fUbj8O56VamAm1eg6Z5l0TUtNtdS trpRaXgLWzTVhZwr+mfglCP9sgdO48cCUxjkjljSWJw8bgMjqQVZSKggjqDirCPzg8x+atA8r295 5a08ajeyXscM0TWs16EgaORmk9KAq2zKor03xVhLfm9+YwuEQ/l/K9lBDdPPcmK6UzvCtwIEjj9A mIzPATvzFHAqK1wqyLyz5yuptQ84R2vl210y+022S8v722L3XrX0tuJUiliiihmkeP4lda89qU+L FVXyF+YHmG/0S7u/M2gz2xtLeKZTb2V0ksyyXE8SxC1kQnnGsSlgjt9qv2aEqsx8ueYbLWorh7Sz vLNYHVXW9tZbQszIGqolVeVAaEjvgVNJ2dIZGQcnVSVXrUgbDbFXgeifm/8Am2mhtNdeRXvr+T1P RSGzu7MRSA26IrI6zepV5g1eSbKd9sKpyPzH1e4u9AfVvJNqupT6hb2Gkvcu8MyzT2sMs9zDFPbe okMYfiWUkjjxNNsVZXq/mvzjp/n+LS/0Gl5oNytmIb6FJ2dDPOYpHllEbRgxAFuHgQS4riqdW3m3 TJ9cbS107UEuVnMBuXsZ0t+aiY8vXK8ChELUatPiX+bArIcVeQ+c/wAw/wAwdG/MWXTtP8tnVdFR LUW7JZXRaWSYqrf6bGsscYiZuRrGelOu+FUH/wArQ83X2m3iaz5FhisJIJp2j1D6xbW8dtHZRTrH NLNbNEzySO8S/Z3WlO5VTbVfP3meDyb5S1LQdES1XVpEH6M+p3V5FBZ8h6JZrVU9FRDRv7s70CrQ EhVkNr5+hXRNMvNU0HUILy8tEuZLO1srm5ENW4emW9JGDd+LKDTFVXznOk9r5XnRWRJdYsHVHUo6 hg5AZDQqR3B6YoYX+dV3qcPmzy6sPlO11+x9N3nup9Mmv5EZZU/cpLHUQ8xvUg064pSHSPMOqS6/ YAflLaaeC8K2Vy9tMhhneSL9+1wtoBEkCFeXw1BXY9gqj7HWI5vya1O40ryxbm1TUZI7ewmjuNfW 9lEgM9wQAjPW45UkLEUWvgMVZJpN95fuPJwm8xeTRCn1y5B06002eZXkFq5luRBJBHIgmj5oOS71 C1JOKs58snSjoludJsW02wrJ6Nm9ubQp+9bkfRYKV5PVum9a98CpT+aUt5F+X+tSWemx6vcrCPT0 2aBrqOY+ovwtAnxPQb0GKvF7XXNRj0aK3P5UWcsvGMSXg0ueGEhpITKTamFpFCxMdxIxLJWlNsKs w8ia3FceftJgPk+HR7+XSZXUJdSsdPsxM/KJrNoYUgeWYRHYDkGrU74qjfKktxB5r1Cx1HyVZ2cK XOpiG/sbOQL6At7R2IYw0mN0ZCpb4OXCgDUOKsh8iyeSWvJP8P8AluXRZ/q6l55NMewDRfDSPmyJ yI2+H2wKzTFXhX5m6hqdv+ZzFvItp5g0+GyRkuX0ua4nuGKSViF4qyInE06ofDCqjoWv6lJrU/q/ lnZ6GximEWqcZ7E28CQTtcStfi2QJ6lGVGHAjlue+KoweZLxfyn8sy6D5Ygi067uwtros1rd6uq2 8M/OOeRolDR/GnqcirncUBPRVllndeVZdB0GXV/JzQTSRv8AVdPh06S5WzAvIhx/uUMfKX05aFV+ yW/ZxVmmgHTzolgdOtTY2Bt4ja2bRG3aKIoOEZhIUxlRtxptgVH4q7FVqelVuHGvL4+NPtU7074q uxV2KuxV2KtHhyWtOW/GvX3pireKuxV2KuxVp+HBudOFDy5dKd61xVvFXYqxbz7/ANM5/wBtyy/4 3whWU4Fdiq2L0vTX0uPp0+HjTjT2piq7FXYq7FXYq0OHM0pzoOXjTelfxxVvFXYq7FXYq5uPE8qc ab16UxVy8eI40402p0pirsVdir//2Q== uuid:9C2B057E6316DF11B6CEECB3A855DE09 uuid:9D2B057E6316DF11B6CEECB3A855DE09 uuid:88CB376B6016DF11B6CEECB3A855DE09 uuid:5C27E1965D16DF11B6CEECB3A855DE09 ÿîAdobed€ÿÛCÿÀ-FÿÄÒ   o!1AQ"aq2‘ ¡#ðÁB±ÑáñR3$bC4%‚ rS&c’D5¢T²s6ÂÒ'E7Fâòƒ“£³dU(ÃÓ)8ãóGHVe*9:IJWXYZftu„…gvwh†‡”•¤¥´µÄÅÔÕäåôõ–—¦§¶·ÆÇÖ׿çö÷ijxyzˆ‰Š˜™š¨©ª¸¹ºÈÉÊØÙÚèéêøùúÿÚ?ßã›üsŽoñÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷ §«¿R?Ñÿ¥î½ú Ï¬­•ú•qœÇ[Bd5tØ}ËMC<Ú¹üºx½®àsÜ÷=ʨü?~³þ&}ë}/ª¾œåþŽú©è`ÂaÎý8Ë´x¦D2¦nÀ)ñü»‰LkÅ*bz¤ù•`Óú0E‰*¾ç¹î3~$?÷£Iý4õm‘ú!ê'&gO[žžðжÀúOŒa™ž»-É™éfØ=^)†% •ªPÔQbIP í ¥yî{žá“ÆF~žº èÓ šNˆfz1lÁUšÖª(å_åxf]£®Ä*v L#§&&`²mb=ÏsÜ^zUü@}úÞÅ:—ƒzQëÖÖÜC£±e麗 #ÂW5RÔÕá‹,µtÐÆÒJ”S‡š'’PŽ óÜ÷=Ä?¬ŸÅÐGáýŒå,³êãÔVҜ՞)&ÄrÞPL/0æ,b\.¤I+å Ê´•LJÐÈ¿54iÑÆû«[Ü÷=ÌNüQÿ®tó Ý[ê_ª¼­•º]êvšº¯¡=B’z©ðlÃImQŠ®’ bˆÄ’¨e™‘·Ÿ.Þ`+ÏsÜ÷ ·Y¿ïÃï,ú õ5ë3 =Z¡õ¦úŠLé§Ëf,«ŠÍ›1ƒ4xNU?„EYIkÓÍåU=)‰„Rmf(À{žç¸ éÏãÉøoÏéKÓ7©>¶úŸË½/‡Ô‹ÔCNh33ь˓0L?͸])þ_$ŽølØ„p¢ŸzveX|× sÜ÷=Å·§ŸÄ{õ ë=圛×Γcþ‘bè¦_ê¾A¤J à}P¦Ãñy©¥8æ7&g¢¢Á`À䦙ž ¬µ*J´Šqç¹î{‚¦ÆðÖõ—Ö:Ž€úmõYƒu#«k}N•˜°ŒÓám"Õ˃UcØu.*ˆdg42Ì#?ÙV#Ü÷=Ä^Aür N§õÓôã‘}d`XïT³62ù{*Çü«4Óeì_IÅ(¡Ãó%^ W<’°H’ ×2±79î{žå°sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Í~o^‰vNüLý?~߇¶\è\øÏWúWŠuÍf£ÏíK¸F7]‡Ï U9#GUh©Ð¢5#’Å®àXsÜ÷}þ#¾´ó¯_Q?‡â5’:C„u“¢Ý7¡ê•Wú7ŠfI2PÀfÅ)pɨñ(óŒ’URÕ¨¯ŠUóZ?Ñ«¾Â…ûžç¸n};~2?†g«¹?¦ÿO¾­p.¢uŠC^0<µ`ÃéqÏåbVªþI‰bØ}-,"X]ØÐTMî)² ç¹î{‰|SñÄü)ðoP'Ó%냫˘×(IJ0¬Ñ&^3™Ö˜á™bÃJ”‘‚H\ 6b=ÏsÜTz´ü^?ÏGJ¬èZ½N`ÔØ3â¸vCª£Çqhh<Ê9qªÌ"ŽzL*™ãÚõõ¬¤5˜î{žåtz#ü^=Jz ôÝø9õ¯>æn‡t›4úùͽLÁ:ÅÓÌC êDøÕ&NÍØŽ_¢§Èi‡Ï‹Áh†$«8ÅW”K^2>Êûžç¸wz«øíþ=Ì9ç(õ+Ö^ €fž˜æìS#u.Å‚fìJ·̘-záu×C…a5/3”1,z£-SWÑÓRaRÏ)zÊI£ò Ácy Ô¸÷=Ïpk£üPý×zaêG¬¸ýEá”þ›:?ŽÉ–:ŸÔŠœ;£bо |6®‚®†:ô©Yñ t1|¾ï|Zçžç¹îúÈ1 *Júmÿ-]sSù‘K›%@ëº9•ÇU`ì@<÷=ÏrW=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ×Cþ%Ñ¿T^¸²¤Ã;ÓŽNÇèr¿«óüÏÔ—¨DÊø®3’r^OÈ”ãŠvªÒ’7¬®ÍK•1´ÒRˆ†²-ýÏsÜ+^=#þ$¾€?Þ“zƒë¾z›ÖÇN¿œŸ‹dQ}Zé÷KjòÎ ‘ë²N‡I•ë±ú<¼µ”´Èž:z¹Z5´¡£÷=Ïr±sO=Dô ð¹üR Óønuƒ®­:ƒÔLÙ›rUòçNëñüœðæÿOùŠ ½×œ¯Ó¼' ùÃʸšPçJSÓ,ÅEŠQà5U4¢dSES+H<Ï-\nÚ9î{žåSzpèשFy7þꯨþ‘º‹ÕÚoKÙK­c¬^“ð<­UYÕ¬©Y›q¬V*<~“'âBš¾hÑk#j™R5ò£Dfa¾;ûžç¹d€~aÅóŸâ•ÿ Ï×C1N¹Û4ô;nã ‡EŒak‰à¹¿ñ(ð™êi⯫Žt««…%s²²3óÜ÷=Àñ)é©_NŒgY½bÉSê èO¨î`ys§=OôõÓ¼·Ô¼Z“Ëí 5Y?¤ÌxF5ò0W¼7ª‘G$’(%öÊb÷=Ïp=é—áÓÕ®”á?ð›,­ƒúfê„y#¦]]ê6}ê¾IÍXm.h¬éu6c’ƒ¢\~¯)á4X~©CSÍ &)£"2¯¹î{‹OWÞ=LgOQðªZœ…éÃ8bX/¨~”ôZ>‚b´™[L;=bx.A¢—§Ëõ˜CŠUÅcÏŠúuÐ</ô/,`ôù±â”x–\Ã%Ås]\ÉR²C42O3©’wßOšÞç¹î]7MÕODˆÏãA‡õKÐWS}[áÿ‰Ö—ªý+u&d©sNRÓe:ì«)ãuìÆ,"(fªT”Õ2 †3¨O'¹î{…Ѥ?V9OÓ×ü%Í>˜:‰–±ŸO]Të¾#×Ü'ÉY–Ž«#áøÆyÄêè*sUhød5QH²A%PdRI{žç¸d:ké®”Þ…¿áT&-é7AÔ/R=lëö#Ñ §&c+‹çܘ«Àj°(%£ó±Ji'¨y(¥¥Y‰Œ’yî{žâ Ó ×ÃëÖ ¯R@ôaÔÿS˜.|ôÑž”b}>È^<ÁÔ•ç ÂdªÃñ잎££¨’ŠH¤©ª0Ƴ;£R[{žç¸x¿á*ôØ…'¥?Ä. W§ñtŸOW½ZZÞ—ÓËM=>\™2îSI0¸e¢&#£u0+ÄJžîœ÷=Ïr¨=Zúvõ;éëÖ?ã”söWõA›2w¯ìY1ŽŒa½ éÖQÎYOªø;…Ôa§-ãØÞaËØôØD˜P«4¤3(Ž-ó$ZÄe÷=ÏrÈ==úêŸC?Ï :Ž‹ç|W¤ž™="b9Wê†=‡Œ~‹)f?šÄ⃯Ìx%XHÄ)©ªÍ2™7ǪŒ ÷=Ïr±=Cú4õYMèñ° Àý)çùj+¿ _¨ý>Éty/LG0ôö—5Á"ãXÊ£â’ÁÒzUx¼°Îb±ç¹î[žÓÚ¯Zï¦oZ8Ǥ|òž˜ñGØ…›:‹Ó|g ÂsOùÑÌ”kC\¸Õ+SÒâ3áÕo4TÒ¸•©gY6ípyî{žåxz—ô©ÔúÏÇŽ£ðÉÁpô“ÑÇ®.ªäYIÃÐÂé 6IÁñ¸sE b­Ï‘Žc¸ly66,ÇÜ÷=Íåyî{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žáõ±øpú_õö:Y‰uÛÇ0ìûÐêÊúîŽu{(æ\g(fìµ6+ tõËE‰àsÁ*ÇR –6ܧh6ÏsÜ÷ýþ~™?ü«ž²ß§|¯‰Rb}Vņ;Õž¡æwÌù£4ã3Õbxž3<óJʬÛTE,Ä(gb}ÏsÜ:ü÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ïrµýbþ^’}lõO)uã©Tù¿§½wɘ$¹g ëGOs®aÈÙŽlµ5SÖœ.ª§¨ˆTR‰¥ydRT±±‘ÏsÜ÷ _¤?G^ž½ ôSôÿé—" ƒÓl&®³šëk±:ìCÄdÕ×WVâRÏQQS3¹äs`Vʪ£Ü÷=Ã;ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷?§¿Ã_Ò禿S=xõ…‘ðŒ1ú‰õ k Î]GÍ›Ìu45n+üé°l%1)ž:5'c‚%÷UÚŠ¹î{‡ãžç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹îtX{žç¹ÖõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜöõöóÜ÷=Ïo_o=ÏsÜì0><÷=Ïs¾{žç¹î{žç¹î{žç¹@_ð¡ŸùÑ_‡×ÿ&@òó]ÏsÜ÷/÷žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹îtHÏ=ÏsÜëzûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·¯·žç¹î{zûyî{žç·/·žç¹îrç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{•{ø°ú÷ê/áïÐN–õ+¤½¡ë÷Qz½ÔÜŸÓL¡ÓÌG9jšlG8|ä4Ò5q¦ªT´Ôèžú…³XÏsÜ÷ ¼¾°¿áA¯öŽŸ§ÿ {žç¹{8&x¡Äöùs‡¿a~{žç¸!ÓT,謦àóÜ÷=É\÷=ÏsÜ÷=Ïr€¿áC?󢿯þL€ÿåæ»žç¹î_ï=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ#¿‰® «øqz2물ë’1£åžŽW¿™ä¼.¢š’¾·úÚ°Ì«•-X1¯—&&²5ûª5#žç¹îV¤ß‹×¯w¿•ø õõ~œ['þÊŽ{žç¸Üß‹—â~ÏàCׯ¯Êõ?žç¹îaÿ‡püAÿîD]yÿÇ®Qÿ©ü÷=Ïsßðîˆ?ýÈ‹¯?øõÊ?õ?žç¹î{þÃñÿ¹uçÿ¹Gþ§óÜ÷=Ïø~ ÿ÷".¼ÿã×(ÿÔþ{žç¹ïøwÄþäEןüzåúŸÏsÜ÷=ÿáøƒÿ܈ºóÿ\£ÿSùî{žç¿áÜ?û‘^ñë”ê=ÏsÜ÷ü;‡âÿr"ëÏþ=rýOç¹î{žÿ‡püAÿîD]yÿÇ®Qÿ©ü÷=Ïsßðîˆ?ýÈ‹¯?øõÊ?õ?žç¹î{þÃñÿ¹uçÿ¹Gþ§óÜ÷=Ïø~ ÿ÷".¼ÿã×(ÿÔþ{žç¹ïøwÄþäEןüzåúŸÏsÜ÷=ÿáøƒÿ܈ºóÿ\£ÿSùî{žç¿áÜ?û‘^ñë”ê=ÏsÜ÷ü;‡âÿr"ëÏþ=rýOç¹î{žÿ‡püAÿîD]yÿÇ®Qÿ©ü÷=Ïsßðîˆ?ýÈ‹¯?øõÊ?õ?žç¹î{þÃñÿ¹uçÿ¹Gþ§óÜ÷=Ïø~ ÿ÷".¼ÿã×(ÿÔþ{žç¹ïøwÄþäEןüzåúŸÏsÜ÷Þµþ;þ«½7ô·7õ³®‚ßZúkÒœ… 5ÇЍ¨cyZ9]€i§DÈç¹î{—ã‘úÏ„g, Ç©ŸåâǨék"¦fãZ¨V`¬F„€Ö<÷=ÏpiñX«T25ïØóÜ÷=ǰn/ÏsÜ÷;ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{ž:}œ÷=Ïs^ÿøP¼ßóf¿ˆoÿ_{Ð3o£­ç¹î{›sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÀÔ?ªoMþ’²CõÔÏ\r¿BòXóE&9™±š,)k%†?5 £Ž¦E’ª}½¡^FÐ$óÜ÷=Êaªÿ…ä®¶WU࿆· Þ¸~"ÓG$Ñõ3ʵ9+¦²M ²\Ë›¢‡É;‰ý .=ÏsÜÌýFÿ…/uåÚLŸéÃÓG ¼·\„ËIžóveênl¡ n¢rJŒ.YVà6øöH=‡=ÏsÜ•ÿ éøáu&G©êçã³NðêÆl‘ÓŽ‚dŠá2»ÉÅñJ£[eÈ»”î{žç¹ þþ ³&ïó“øæú¹Å>q”bÕ|ëƒdÍÐ¥¶¬ËðÚ!ýѹÖûµ¸ÔóÜ÷=ÎðÁüZ_ÿýQßûÏsÜ÷=Î'ðÌxYJÜ«øÙzì£Æ"6Šl_®Ðã´[`Ô•HÆÇÝ$èuïÏsÜ÷8Â;ñÉ–“¢ß¿Zp*ŠB–6yÉ™#©1¨&Ašúÿ&kù+Õ—ó6ÜÞü÷=ÏrÝrv&kèa›ïP~ñÏsÜ÷ÃP·žç¹î{žç¹îPü(gþtWáõÿÉÐü¼×sÜ÷=Ëýç¹î{žç¹î{žç¹î{žç¹î{žç¹î{šø¤f·àkër?ñžšéÿ‹*ž{žç¹°=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ :Ã×.‹úzÉuÝFëÏV²ßFr¥vrÍÖa¨Ç²|Æ'4(]ŽŠ€îc óÜ÷=ÊUÌ_ð£F9³®Éž‰z?ÖOć:ÐLik)z;Ó|sÁ¨ª¶«Ú·ÆÓ ‚8‚°&h„ª½µç¹î{këSñûë„rMÐßÂ+!údÁ*c æ¾´õ~škÛê0\‘ 5´ÄwÊv¸:Ü÷=ÉQô;þ§ÔI ¹Û×7¦ŸNb “%7Oºkš3„tà4’Ïo 5ìˆwŸ²IûBíî{žædü6¿ŒÁåËžÿáBXÅ•wÁ‡eÏMý-ÀRži,] ñVH󯶲oP|{’9î{žæOøkŇþê êþ©~šÿÔÞ{žç¹Å¿ Æ )Y•?áB™¶‹ˆÚ9±Oý/Çh¶0*Á©**bFkt“¡×žç¹îcÿeoøQ7Oö¾QüVº=ê ©ÉÅŸ:M”Ö¡QÆÖ”äz™ ™‰pš)U {’=ÏsÜŠýzÿ…&t]¼Î¡ú ôëë6†g™J:›Žôú²¥3± ÔØå^ÛTû–Ý{¤[Ü÷=Ì2€0>ˆ$©ø‡~ ý RP1¾¨b2\óÓšRlÇ”Œ¾nÒ{¥)¸×ÆÜ÷=Ïr×=,úôôgëklÁéKÔ®Që„B*1,Å |k…˜(jì& Å_GrE…Dyî{žá·ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{”ƒÿ Bÿ³%z÷ÿÁ/ÿìq‚sÜ÷=Âcé+Õd¸ÞYÈÔF¸¸‹ ãۻü±¯=ÏsܾÞföÇ0êYL›÷ª›ý#žç¹î˜rí±ç¹î{™¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žçG±ç¹î{šñÿÂ…›þm/áì¿øw}ÿËÕg=ÏsÜØwžç¹î{žç¹î{žç¹î{žç¹î{žç¹îQ¯oÄCÕiõ>Ÿ…ïá[”0¬×ëáT˜Ï¨oQ–Ÿ"ô;-âq¤”µx„j’ ¼V¦)´´…YlÈÍÊÌ©î{žã‡¦À;ÒgM³ªzƒõbÿ‰_¬\HÅ>aõÕöí-DneXðL¹Võ~M Ù©ãušHHýжQî{žàÛê ñôéÖ'Gÿާg*ìµÖΦÿT¨r–‡`.^Â[6bR`¸E5ul,Ñ\ĬU€ #›)ÓÜ÷=ËV¯Ä(0ªIkñJèpÚ6ùõµ¤1&滞B¹ žçžç¹îz¿ Â¨ê1Rº7¤]õuõ¤0ăMÎòª>$óÜ÷=ÂoÕÏ_^Ÿz'ê× >‰ó­^2zñê[ÍÿJðz"zÚ*š ¡„Wcx€š¦&´ry|¾ZX—k©ç¹î{‚?§¯SùÔgDp~¾à¸eéfKÆ'¬€a}AËõÙ'¥j*÷ÃKUPc‹ °¬’'è™À¥Hî9î{žàû_‹axV.-ŠbTønNªóâuG :#¡šIP O=ÏsÜ.^¢ýdzpô¥—úW™zåÔºl§‡uÇ4àY+¤ x”Ù‡3fY„8}"a±NO›}æV´H€³º®¼÷=ÏqË&z˜ÊãÔ_Xý3á¹'9ay¿¢T8=~bÎØ–UÅpü›‰ÅPÓWÅŽTF)kå‰j•gHX˜Ü2¶ªyî{žà÷†bØ^5Hµø6%O‹P»:%m4ÑÏxØ£¨x‹ «¾‡žç¹î¯T‹½zã˘‡N½Rô(uæ‹ ‰aòñzi±œjQݦ1×áò:³’žhÚÄØêyî{žågïKÞ¿¿|&¿­¾€º™}sþy4Ë_Ö/Ã×=×¶/œrn_…<êªì‡È¦vŽ•›ù{¡A%*$c$~ç¹îl#éoÔÏH=cú}éW©®ƒfAšzSÖ *W+bD"TDš ŠJ¨ÑäURO”õ1n;%F[›sÜ÷=Á÷žç¹î{žç¹î{žç¹î{žç¹î{žç¹îiÿ LÄeÂÿÿÃîª(㥹Ùn=ŽÅÏsÜ÷ W œÃUWü«|¥‰ÙÜŸ‡=ÏsÜÙ»¥’—Â).nv/=Ïs܇aÏsÜ÷;ç¹î{”ÿ ÿø}ò`tÿ/5Ü÷=Ïrÿyî{žç¹î{žç¹î{žç¹î{žç¹î{žæ¼¿ð©6ÿªúÕ_ktÛÿfÞVç¹î{› sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Êõø•úë©üÏøjþÙ;Ïž¥²,Iê£ÕŽhYfé×DéêØ$Tó¤QȸŽ6ë¹£¥–6]­ÅjsÜ÷º!øúaË ëßâž³â‹ê«jI‰u'ªÕ [”ðùˬ²A‚døäl.Ž„º´Ó-@[YJ‚Wžç¹î ¹ ñrü9òÿ¯? Žã”úõ—*êpL#)á™q0Ü›!A–ÿ¬óaôÕ´¾]2K£1*Ó÷¬¹î{–ÙYˆPaÿ+óõÐÐüôÉOEçJ‘yÕßdI¼ÎÖ6Q©ç¹î{ž¬Ä(0å§|Bºªš*zWšTˆIQ;ˆã‰ ‘wv6U“ ç¹î{•ãÔÿÅÓ/K=GuÇÒ~%‡ç,Û×?Oý5N«gl•–òž#TÔåI1L7_åɇ¬ÍŠÃþnÛ¸þéç¹î{‡G"uW*羚tϪh*òfÕ|+ÅrÖ ™idÀq˜žáñâ4ôU”8‡—-=j¤›e¦q½2‘u<÷=ÏqgŒãØ]¤†`Æi0*ë­¬¨†–#Bï•n@6ç¹î{€V=^úzè—Z½=útê?P¢Á:×êž·¡èoO’–ºª³|±,BRÔ°É<4ð­ÚYÝ’K9 ÏsÜ÷2ôÕHõ OÕê¼»”3~B¥è¶gÅ2®g©Îy_Ê‘ÖÕa*­5nزF+0ö xêã¼mcc§=ÏsÜ0”•tµ ”50âXm|a骢tš ¢\2²¬¤ãCÏsÜ÷* Õ/àøjz¾Å+³õI)ý?ú€Â*¥lÔ×G«S!glEÚÕ-S€§¨ªPÛI­§™€6ÐsÜ÷ƒú®üBÿ¼Í•òïâCfõáølæ3éÿ/D«e.ªÔÖVÑSãutpÖRüÕ”œÆ‚Ot¤¢#³Ý÷=Ïq)ëÓwZ(=YúôÇê_Ó>qõ…×ü/ðåƒ.fŽeZ±‰cÔYŸz¿–=Ag_:žº «‰cù5áø]P³½çŽ:ê:/pºï F¼÷=Ïr½:oødúÔ鿤_ÁîŸÕÏ >¢ú¡ô¡éË묾ª} å‡VÍMŽf¬K9k› ¦ÄhZ¦=óÓÊ’Ç+R9CmÚOsÜ÷¿­?IýwÌ߆çáKÐ,ø¢ú{¨Ï¿­™ƒ¤´iU¸d“aÒf¬ŸE‰Éˆ-USˆ±YþN‚;Âé•‘}Ïsܬìcðvõ{KøYz:Â:Ùè[=u{¨¾“=Uc×Na©&#™›¡ô48ž)MƒÑR×CôuÕ´ú4 ÈÅ·"yŒ=ÏsÜ=þ­ÿÄ#ªÝküus‡¤ŽæÎ’e_SÝôó…zt¥¯’—­Ç°Œ³–²\Y‡+E;ÖÉåÖÅEAWEãî{žàiÑßãÕÖ'éïñ¤oN>…ú…éÓ¨^‹ä\«ÑG9¬ÇM˜3T0j"ŸÅi0·Ä+ kæRÖ¤Hëç¬èÃPñÅî{žáèü ÏPþ‹¿ #¨ø×A³OKú9ÔF=9¤ë>kű ìBë‹›WŽST¾![Tâ¶b¯5´Q(uŒ*è}ÏsÜÛKªOéïE:uz·ÕŒßCzkÓ¬6¯ÎùϘSÐá¸uFi¦•Ï‚¨ÐK sÜ÷=Ê6ÿ„Ùå|VƒÐ'Qú™I”ªº{Ñ¿Qýlê§Pý.tú®•“é¦?ŒEN”À(†&4³K¨ ÈáÖêàŸsÜ÷6ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{š6™4üP¿Ãìé~sÿËô\÷=ÏpÌ~&ç ?ñÙÏsÜ÷6éGü’iâ+ü9î{žàÚ;{žç¹ß=ÏsÜ /øPÏüè¯Ãëÿ“ ?ùy®ç¹î{—ûÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷5âÿ…JÙ=jĺmÿ³o+sÜ÷=͇yî{žç¹î{žç¹î{žç¹î{žç¹î{žæ¹ß𙪌­†z$ëOOs#Ǭ܉֮¨Çëî†rÃ~¢UfºÂ•ukPæ9°ø©’)°fŽ@²¸ç¹îl Ÿs!“ò6s͸NX¬ÎØ®VÂqG ɘpÅê(hä©ŽŠ˜5Áš¡Gÿy‡=ÏsÜù°à„ã×”ú}‘ÿóÐl£Œu× õU×J®”ÅSG×i3v>”µ´ÕQµ9ÂÆ RÔb¥àù¦”Å&溓÷=Ïq³ñè/Sýaþ$~DèO£^ u¿Ô®oƽ>Wt7`SGD½(ÇMˆÕVã±¶#J´í>]U‘iܶçŒ6ãî{žäßÄŸÓgX=Aþ)?Œ7C0K™ÇÖªz܉骗§9Ï'L¢“'gÊL“¾k1b1Ë[EåÓÍK| VbCæ”)&yÜ÷=Ë;õ™ør~!çÔׯÜÕQÑ<ªØR}厘Qu.„CSGšú¥C˜25N#CK,’«I<†ÕÍv¤ßžç¹î¾¡oRVhô5š}a~HüF=9åD8égL}8äìb|:£§½lÃp" ñŒ6WØYSMQØ’A:¯™–V€"ûžç¸9~1þ‰ýRúŒ“ðúè†Pü.³;“2—BñŒ+ÌñflÉÕêΚçLC¬Ã)òÍe^1œ0L>Mb£zŒ~ºž²v;M2ÎÔèî{žábƒð»ëæ\ÿ–xúýê'ÐQº÷QÐÜ3dßZÙC¥©ÅsfO—ó%cdhëhÎ'IåRaâ§ÏŠd‘c&×fý7¹î{ŠLÿøg~"Rú9õÅ–“ÓNnÌ™5zø­ê¯V}:áóÁCõS¢#{J0ÕJ¨\rÌbu§Þˆº!ÏsÜ÷9ûðÒüAs'á-øˆdNˆzIϽ(éßZýJ`çП¢¬FHcÎ3%Ñâ•5OBµõ# Þ$¦_—óË+ÂÌ7+,î{žåòþ~„:•è[Ö÷ã;—cèö;ÒoIùÿ8ôê»Ò‹âuÕØŽŒÒÒÒfUŪèê1:ºº‰¤-=7Ÿ,Ì]®—&ÂÞç¹î\'â ˜z •}z±ÆýP͇ÅÐ|ƒ™¢ê|x™O–¨Ãê°¹i>Y2—¨žIR*dCæ4Ì‹¾Wžç¹îÀ_ê^ø<~˜gV£¬ƒ7ÅÓú)©ë÷ ˜°ŠÊšŒ ` ¢áOJ#R4@£Ãžç¹î[—=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsܤøRý™+׿þ ùÿcŒžç¹îjçèû¹Px)?éóÜ÷=ÍÌý+ÿÉ ÿˆ'ðç¹î{–GþE>ÏsÜ÷%sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÎcÏsÜ÷5àÿ… Ψü=¿ù.ú ÿ—ªÎ{žç¹zýcêž[è‡Jº…ÕìÝ]n]éÆ]‹âX^sâUÂŽ•)(¡šHVZª— ¼S°2—J²ö rµCñlÓŠSÇEKHl7U@µ·Óu½ÏsÜ p¯ÃW×Çâ‹›rÇR?Œù„tÇÓvV¯§Å²GáiÓ,Jirõ]]4¾m<¹ë0ÓÌíŠM U&žšV€° ‘¿¹î{›àx –p\#-å¼"—/åÜ¿KOC€`4ñRQPÑRD°AOO ‘ÅQ DDU@9î{žã§=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜѯþËÿgAü?ð—ç?ü¿EÏsÜ÷ Çáñÿ8ŸøÙÏsÜ÷6ŽéGü’iâ+ü9î{žàÚ;{žç¹ß=ÏsÜ /øPÏüè¯Ãëÿ“ ?ùy®ç¹î{—ûÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷5âÿ…JÙ=jĺmÿ³o+sÜ÷=͇yî{žç¹î{žç¹î{žç¹î{žç¹î{žåzãü+:«‹zˆ—ñü1ºÍGé[×ÒPÃAÔœ#¦’«¦½bÂ)Dxnn ¦Vu˜,H‘×Ä­"ªwzÇ,^ç¹î¹ñì§ôûŠ`Ý)übý(g˪³Ê”Puq°ÚÜãÑœÇUs— ̹z:Ï$ÊÊ\Á2¸Y|ÉΤ{žç¹x]õ ÐOQ¹e3§§þµeN¶å6[1eLÁ…f D2.à²I…Ï:£ÛºµˆìG=ÏsÜrÊÝè¾FêPzµ’ºE•òUzµò_çW©˜^„áùƒ3-ˆÁIüÛ¤‚:šß—BV/>GØ4[{žç¹ÖÑ‹ån¨gž·ežå|¹ÖާÓÐÒu+«ÔGš3 .O%8ž+OUÕÇOÇ Í#TUP€=ÏsÜ9î{žç¹î{žç¹î{žç¹î{žç¹î{žâ4æÌ­‘ðK5g\ˇäü¯ƒFfÆ3&)YM‡ÐRD½ÞjŠ·Ž8Ô{Y€ç¹î{”‘ÖÿøP¡Œ¯›æèǤh³?âOêR ÉÑŽ‡àÕY¶1XF&¬Ç Oå‘Q†¿›<3Oå¨,ɶÄûžç¸eÿç×gâ›Ô¼“Ö߯[º=é›§ØœïH Ì‹¶)ƒÉ_O*ÍKWŸñºr#Åêa¶ÓOh¹O$<ÑIî{žæÉ”ôôô”ðRR@”´´¨±ÓSFª‘Ç(UUU°·=ÏsÜÍÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷)þ…ÿfJõïÿ‚þ_ÿØãç¹î{š¹zí•ï“þ‘/=ÏsÜÜÓÒ¿üpÏø‚{žç¹a´äSè÷=ÏrW=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜèö<÷=Ïs^øP·üêÃÛÿ’ï ßùz¬ç¹î{—ê“ÓÚúÉ;¥ØÆj­Ë,ß–³R᫱\#Å0ì©^3'‚UQÕPÈØ­ ’M¡¼”‘Áç¹îW6#øPç¬ .uÿ§ý-õAKÓžºån£tÿù>rÁswÄð,›Ô,^·6Ëòx–#˜cžJè1Lo-Q˜O ÒoÖ“dÞç¹î =.ü<³÷NzƒéÒ¦~±àxïIý7u[?õg+á'+VÇ›kñn¤eë€â8}f3.14-IKSœæ’›e·‘|¸y}Ïsܰ>†úµè¶|õ'™º5›²bË^ ú…Iœipl{Ç¿™`¿Ì0|·”«¼É¨ñzxjR–›’­cU¤6Œ2“»žç¹î%úès©Ø÷ªž£ú¾É=GʸwQ3ŽPÆ:qƒe,s)7Ë´ÙB³ŠJ:ªØ’¦ êñX±¸Ìµy‰FV„¢´iR=ÏsÜ/X7á™2~Mè/L2§Zp¹ò£¼ÝYž=6WbÙfž³Äqz¼Å†f1G›ê¢xS¢Y!­FG »µQ+U‡Å+ûžç¹wœ÷=Ïp†zŸü/??YÓâ—©ŸGÙªY—]•Ùò| ?3ºmÚùæò˜ˆv [Þç¹îW ÂvúÒæ†_E~¹=Kz¦ÃÕ— É'«nQŒØl+2E^Ó"Ùl8÷=ÏpŒzõÊWá‹“: œºsøËMê-õ»«Y¥xFAÏýÈ­>SªÞ– F£¤yë*R• $Apf`C›óÜ÷=Ãê½;ÿ…7å2Nê+ÑÿV¾MÏʌӔúŸ|êH?1ýYfòÌ{®‚>ûFã©ç¹î{™쩯ü0þØ®{žç¹Å°ÿøTŽ*naô•ijó±ü2—¯•µÔáH{Å%ú-m§`IÏsÜ÷¯Ž¹þ5žˆzwÓn¢ú´üVúé§êþzËY¨È½\ËQA.`Ä#£ªÄV<óSúL.‡ÏÄ*Y•ˆ1¬}¤{žç¸xWðnõ£ÕÈ\ú£üy=Aõ Å“v!MÒÊ\«Ñzz”–ÒŸÕè±%Xœ“î.…ÎÜ÷=ÏpFégü'7ðžÈ’ùžúˆz¤êR,kWÔ®fœÁŸ«*Äd·úE-UüºK³oImO†œ÷=ÏråòNº}Ò¬¯‡d~—ä\¦ù+aCÃ(°|.”1¹ÒaÑC{íQÏsÜ÷\÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsF¿øS/ýðýÿÂ_œÿòý=ÏsÜ3‡Çüâà?g=ÏsÜÚ;¥òI¥ÿˆ¯ðç¹î{ƒhì9î{žç|÷=Ïr€¿áC?󢿯þL€ÿåæ»žç¹î_ï=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ׋þ)ÿf@õ©ÿé·þͼ­ÏsÜ÷6ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{Œ9Ÿ+eŒí€b¹S9åÊ Ý•±ØŒÞZÅ(éëðúÈ Ç==ZIˆHk)÷=Ïr˜zÃÿ áü,º™››©Y¢X¤ž¬©Ñu?¢ùé¾!He±&ž“”a±ÊZ¸öiÏsÜ÷ÿ ŸÄ³£NG¤¿Çªø~ JQhò¿Y²fQë;ÏMm°Í‰âß!:1¸Ý4q†°µµ{žç¸PºïêŸþCè·Ô/£oM8æ~ô¿êŸ3zÐų6 ÓlkËó+É NRÀâÌ5ˆ>]O ?›´q4qL.£r ’=ÏsÜ8ãÔgü(ã+í8÷áÁÐ>©Šd­\³ÕüC5û®éŽc§)Rà°–ä€l"Þç¹îdÿl¿øP?ýɧÿýQycÿ8yî{žçê§þE»+ð¦èö@JP<åÇzçMŠš¢äØÂpJEÙ³o½æ n-ØóÜ÷=©ÖÏYð ^œõ{Ò÷@:˜ýzcÏ^±1œS.ôj†yz­™1šœSÀªqÚ™ndZ$‚ëOF»ƒ1©žPÂOwÜ÷=Ã7Qøt~5½di¯ßŽ<½4Ëui²³"tƒ£™[/L§ÍÜ^ÁˆT6!m¶ÃákXî÷=ÏqË-ÿÂo½ã¸îœ½^g®°~"9ëeš›1õ«ª‹0ÅFÛ1†“ ƒÊî)–E ¡½¯ÏsÜ÷.ƒ¢ÞŸúé¿(GÓÿOÊÝÈñ¿›ýSÊx—ð÷›hC+Á…C<„zF‰<÷=Ïp]ç¹î{žç¹î{žç¹î{‰øófV›4ÖäXs..vðúl[ÉËYLجUmLôtõ’Ró–ži©fŽ9JífÔU€÷=ÏqAÏsÜ÷=ÏsÜ÷)þ…ÿfJõïÿ‚þ_ÿØãç¹î{š¹zí•ï“þ‘/=ÏsÜÜÓÒ¿üpÏø‚{žç¹a´äSè÷=ÏrW=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜèö<÷=Ïs^øP·üêÃÛÿ’ï ßùz¬ç¹î{–±ë׬ƒ£¾šsm^FŸ‡©ÝJ­Áò?KdÁ¨åÄqªlk8âpàC ¢%z™pªyçÄÌJŒY)ŸCÏsÜ÷*73~!ž¡ñÿCýeéöJ­¢Ê¾¡=;t?©øŸ[úŸžjñL­˜¦Æ2n'‘¢ªÃ©))–J\J°ÑEŒÉ,‹¶˜Uѧ”ÿ2/sÜ÷œ§ë ¯]_ë?áÄÔ¶“zC›ºéÔ¾žç«7‰6wÇ[¦Ýêu-DØÕ””ï‡Vb˜M>% dîEÄÈj“î{žáóô½Õ\{Í»åê>sórçJúË]eJ¬F¢)p|>ždüMi’I6*D*kæ“Þ?jC¯=Ïsܺ»ës¯yg®=Yô¿Ò®‹á믹*ƒÏ*ÕVâ´øÿK¨2"ÕaøŠÉm²²·7H0,ªªõØF}ÏsÜ 1_ÅW2âSëN:k…fÞ…zˆÄó&Kô?œ%«Äij3Q©SÂòö‰'”Â’ŸÅ_ŒÈâƒ{Æm©î{žåÒ `ª†pö¨&ÚØm÷óÜ÷=Î\÷=ÏsÜ÷=Ïr€¿áC?󢿯þL€ÿåæ»žç¹î_ï=ÏsÜ÷=ÏsÜ÷=ÏsÜÖ×þ‘Ñn’u¥_‡¾iϽ8Á³Æ;ªåhk1Œ>ŸTË™“Ÿù¾±V¬‘­6 )¡Z¸ÂÚuMÊŠ¹î{›dœ—•úq“²·OòF^ɹ'¤Â²ž_…¤h(0Ú–šš/5™„qFŠˆ·Ñ@AÏsÜ÷÷=Ïs]ÛׯŃ¥ý¬Áó]~™º§„uÄs¦`Ë3ÑPÐg3£Àòæ^®¢Ê=.Î3džIK•’Å"RÕÖ¼²ü½|)Â=ÏsÜUôÏ«~°:cPu?uC¨XugWºñ…çì¿&>j8dUô¸–3ÓȰ¹°Œ¯[á•+_+"Y¢§ýd€{žç¹—§]`üF³ÇKi1ÌÅÔLç”sV5™}0å™"ƒ eX§ ÂóþNÈu¹ïŽ,C.Ì>b’²¿÷åF§¤È²DËG¹î{†4õƒÕþø|æ|åýsŰN½tÿ©]@Ë2fÚŒ¤Ë˜sFRȽdÌS¨† ')æ *ìwÂi¦†´àSQ»Î pƳE$~ç¹îüw«¾ªr{õK‰Kœzó‡âýbÇzc7F2µf_Á*pÜ‚bÝ(¤Ä*êª*0Þœgxé£\j‚« ¨†††¯ý>H¯yÏR}ÏsÜAtÿ®~½°ŒÇ–=Au75õ/Åz“ÓFÙ·ª} ‹%®'”pJLk1¦Õ_å”)—êêé«0õœµmYÅ©2Ý'Ws¥&KÅóãYIã\.Ÿ-aøl~JWá•ñÈb–u¯j­±ûžç¹w÷=ÏsF¿øS/ýðýÿÂ_œÿòý=ÏsÜ3‡Çüâà?g=ÏsÜÚ;¥òI¥ÿˆ¯ðç¹î{ƒhì9î{žç|÷=Ïr€¿áC?󢿯þL€ÿåæ»žç¹î_ï=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ׋þ)ÿf@õ©ÿé·þͼ­ÏsÜ÷6ç¹î{žç¹î{•§øŸéWÓ?N™g#DqK;õ› àøŽX|׎äª,j†ªJÇ–†·ËtÕµpSÈcRþ\\¨ºŸsÜ÷+C¢Ÿˆ'©~ŸTt nK‹cRÍ"Õáùß2å*±Œj«®9³&æ E˜3¶vÀñ*Ù²e.˜dtØ~#UU/ËÏS ,ˆµ>ç¹î,râÛÔþ£ç õ€a¹Ç'eNŒÏŠô¶Z_UxþAA‡ô÷êÔ‰ê2áTYº¾:J¨kr- …m-U5F)®¦FŒÄþç¹îNÉ}kÌY;ðàéVúÖêzdõ‹˜Žxë*ÖÕeœ·,ÕzÓÌTÏ:¶'_Pi°jŒ<¨Š ª¤iJÆ]Ð\ûžç¹uùc©õ1”svÒž°aÝDˈÌy£$fd•é¶’:ÅŽW.Ôï§ ™^–EWVR¤©ç¹î{€oá•›3V}ü6ÿœõž³.!3¶tèIñlãœqjÚœGű\G!áU•u••u$ÓÔO4$²Èʼnf$’yî{žáßç¹î{”ø£ÿÙØá>ßøP:Ñÿ²Ö.{žç¹¼÷=ÏsÜ÷=ÏsÜ÷=ÏsVŸÆgÒB:Óø·þx—R²ö'˜+ú±šú‹‚æj¸³.eÃ$¦Ã²–NLÇ„®ØE})æ¦Ä\Õ­M'—;KbÒ0HÂûžç¹´”QùQGö“ËP¾c›³XZäø“ãÏsÜ÷+G×.Uõ‡™ú—•_Òî+Žac.tG¯˜Ž[Z,J<;«ëU™ ƒ**&† ýÙ±y †¤ü»>ñîV÷=Ïp‘t롤qî§ú-êŸT«:—›rKz͈U.Sj¾¦àøÎR Æú]Y„±fÆ35}^3„¾<”í#O$ðÀ³ÔÄ£ù|ŽƒÜ÷=Æ^á?‰qÉÝÈyÖ“ªÙV¿'doLYÔ^j—4Sã9³ÏuxoT±z êJÙ'–:¬1ÖGÅ#ØòÄVHœH—OsÜ÷ Ç@r7«|©êC)fÌ׌çœW$fœ÷ê ϸ>9ŽVâ˜E¦ÌrWä)©è«'–*vǶž¦5I­Ìè°¬^ç¹î :—ƒúãÁ³w¨ü ð,ퟺCÒìB¶.”âxfdưüC7á½aÎx=S^Y­ÃñY§´i‰ÒSÑÒÕE5E;G.²xýÏsÜ-]9é÷â%—*²ÇY3öXÎXÏPòFÑü¨øµqØ3UÉ~§3ŒØ­B˜•sUÌÙJ¦šªxZ²yj"r<Ú‰Üyžç¹î;Ç„~(9§0åCų·K2Î3˜z‹ô§«£ÆñúÌÄýDg\W ¤ÍvSÇéRz²|˜ =$¦‚8Dê9V&>ç¹îl]ÏsÜ÷)þ…ÿfJõïÿ‚þ_ÿØãç¹î{š¹zí•ï“þ‘/=ÏsÜÜÓÒ¿üpÏø‚{žç¹a´äSè÷=ÏrW=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜèö<÷=Ïs^øP·üêÃÛÿ’ï ßùz¬ç¹î{—ÇÔœ[§yC*â}QêrÐQåŽÓb9¦«5WR­OòpÜ"­k1HŽI"x襨Fx†óº † ûžç¸æêF™¯£GÕnsÈÙ79t‹Ágê¤=P¬Ê”xÁd™b©qøQ¨gªyäÂib]é™âDŽÇj¯=ÏsÜv®‹ÒŽGê/FsÕFHËU=Gâðeþ‘g¨2¬#Æñ\?!ãÙžvÄ)¨LÐy8‰Þ¦HÑc ;¥XßÜ÷=ħù‚ô9×ìûÔ¼ÜÉê™4Ðá}SÏñeª:<ÉdÁ Âs$4•x¬tÐUÔ ‚J3,fg†X[ÉR†{M‰M  Üë§=ÏsÜÕñÍêßà—ø‚e¿OtY×ñ)é¶JÏýÏÙwij¾SÌÿÖ J·#œR”æ,*Œ–˜›,íN⢉ʷ•U¢™ˆ÷=Ïpèå_øR?à“²Ö\éŸDº×ˆãÙ?§t¸6 •r‡Jú—&á˜d)CGO¶kŽ=ùe—j{-sÜ÷×þ•èJ±~gé\ó.)'Ǩz1œ¤£«Œ´”ñ±Sm. ûG=ÏsÜàáK‡&æ åCÕ®V°I‡ã}ÏpMQ$y‘­-@*"äyî{žåKþ.ŽÏá륲·AýMÅ™3L½Iôc:çì¿+gL¾ø^\À±J¹±¹%ÌE.´¡Çš"‘Èø‹sÜ÷=Í›:Yø¬þ]kzj~—zÛénqÄjʈ0*lñ—W%ÓÌÐÏUH¸¼c±ñÞç¹î:ã„bðUQÖÇSMR‰%=Dn¯ˆêYYInç¹î{‘bô²ý™AúÇ=ÏsÜpJˆÞÛX{žç¹˜{{žç¹ß=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜѯþËÿgAü?ð—ç?ü¿EÏsÜ÷ Çáñÿ8ŸøÙÏsÜ÷6ŽéGü’iâ+ü9î{žàÚ;{žç¹ß=ÏsÜ /øPÏüè¯Ãëÿ“ ?ùy®ç¹î{—ûÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷5âÿ…JÙ=jĺmÿ³o+sÜ÷=͇yî{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žã–²þ[™1,§Ã1 ã\¸žk®†%IqA0ú\)j*k$‹KE Ír#{ª÷=Ïqá¤E'ëç¹î{ÕØÎ†ÓTÖ×ÖEEEEÍYW+¬qEj]ÝÝÈ ª$“`9î{žæž[j+ÿ¿Â‹ª}1üE:CÓž’t»1fçÍøÆ;˜2 Õ}/«¨ËGŠâ")êâz´Ä°Êy"¢†©$ ^ˆ›ŠÎ‘sÜ÷.Cü}ÿܤ•bŸˆ&AĤÃaWªž†¾|\ʺšu‘ÏŠÆ ¾–ðç¹î{©ÿ…&~ c·®|ÿâ?óÍÏsÜ÷°OøQWàÝüÏÈúîÊ|¦Ï7çi33u¶1Ã`ßöMöÞÚ^×÷=Ïrµ=sþ%žƒºóø˜þ™ï¤¾®úwò—IóÇU꺕ÒfÌå²ý6+ÓØé©%Ä^iÐR¤Ò)]¡œð·=ÏsÜÚO'u›§BÃÎ-óÆp¥6lKĨñ*pw2[Ì£’EîŒ;÷ÙÏsÜ÷qc´r &=ÏsÜp޾ >Ì€óÜ÷=ÉK"0¸?_=ÏsÜÉÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷)þ…ÿfJõïÿ‚þ_ÿØãç¹î{š¹zí•ï“þ‘/=ÏsÜÜÓÒ¿üpÏø‚{žç¹a´äSè÷=ÏrW=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜèö<÷=Ïs^øP·üêÃÛÿ’ï ßùz¬ç¹î{–ÕëC£ÝIõÒ|£==Æ¡ÊxvxÍ™aº§œ¤JJ™0ܧâK™«R*:è冬â†Å†ÉˆÊb©vae${žç¹OCü4ýNâý#êǧÜÙ’r穞ŸE“º¥‘}0b˜Ö9K€ÿSjóv-W™0œÎ´PRIÓÁ‹(W¥U¨§‰aM\î¾ç¹î })ôÖÌ¿êCÒ—Q³ÏKòö)šz Õ¼ÿ3תë ÏŽcù4ô×>å\½—€Ó,»01˜è(<‚(ã¦ó`Þjj6{žç¸eò^£ý7õÕýe'¦šÞ§å¾¸uZŸ5tû5a™‹†©1¬³”²j-e34µ´â Œ>ij%Bl ½ÏsÜ=Bzfê~qõCê?®˜w \‰Õ¬qrD™[ÓÞ7Œ>G^gÄq,&•1 W8г}m=>II(e‚–šM’©¬eƒÜ÷=À{(úUË~Ÿ³o¤ÌùÕOIg¤ÞŸ½åú¼k6ú‚Ç1~–a¸Üº¯ħ©Å±ÊúY©ã£Ápñ]W‰Å‡Ð7˪ÄÙ:ÐÄ’{žç¸˜Æ?.¸úÀÌ×OÿÿD8ç¬JL&²\;õ¤— ôO ¨‰„rIK[ˆˆk1“ ‚%§§Ék¦îı$’nyî{žá§¤¤¥ ¥¦¡¡¦ŽŠŠŠ4ŠŽŽ$XâŠ(Ô"""TÀsÜ÷=É÷=ÏsÜ÷=Ïq›ºwÓìÿLhóæEÁ³µ!O,Òâøe%—¼I·mdR nÛÛ¯=ÏsÜ!]Xüÿ ζÁQPÿ¾“ÖTÕ«­N5†dÌ.â’ FwWåÈhj‰FÓæÝvÜ÷=Ïpƒbð›ŸI½;jœWп©¹þØú±“ ¡éïRñºÜ´Ò´†B+°œÒø‘ª„–$Çó ±¿p}ÏsÜqn˜ÿÂ…½ ,˜¦WÏ];ü^z3‚’¯/×QÅÒ^¯µ,d¾ÊW¦3`³”ŒyI¥km]Ç_sÜ÷ £OÆ·ÓÇ©œý_Ð á†fKž®2Ê…Î>”:™…Í–su4˨”ް,uÑ켊ac'•i4R9î{žåË`9óÅ#CB¶ë[QÏsÜ÷:z¸çPÊמç¹îKç¹î{žç¹î{žç¹î{žç¹î{š5ÿ™ìè?‡ïþüçÿ—è¹î{žá˜ü>?çÿû9î{žæÑÝ(ÿ’M/üE‡=ÏsÜGaÏsÜ÷;ç¹î{”ÿ ÿø}ò`tÿ/5Ü÷=Ïrÿyî{žç¹î{žç¹î{žç¹î{žç¹î{žæ¼_ð©Oû2­Oø—M¿ömån{žç¹°ï=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜJg|õ“:i”³}êlÃr.HÊt²ÖæŒáŒVÓa¸^G î’zšªÇŽ(£QÝ€÷=Ïr‚3㯘}JæÌw¦ƒ÷¢ŒãøˆcØ CQfPÉOú5„T†ØÁ³&b \‘Y˜ÁI樼2I}=ÏsÜ¢_ǃÕz.!ê¿ñ8Êž‰2†%w­è—§œ˜+±†CîÄÙ·8:×ÓO}¦§ó#,M®ž{žç¸¤Ãá6?‡öjšõeŸzÓëÛ1Ó8š\s«]ÍØ±iôbD9~£Að#ŇsÜ÷?NÿÂK¥ÐÓA•¿¾’Uüš*STã¹7Í5 ³Ë³yùž,BRãÊ9m×¹¿¼×÷=ÏpÚåÏGÞ’rzÒ&Rô¹Óœ¬” ŠòFY¡T+‰±Šj$Úá€!†·×žç¹î Ÿæ£¥¿öírÿþ9°ïú“ÏsÜ÷¸ç§Ž€fw¦“2ô7'f£ )»,àµf äj)Ÿmì/og=ÏsÜ-9ßð­ü3: ‘ç_Ãç¢øì²(Oæ/Ó<j s&Ôª§ÃãbI â÷7ïÏsÜ÷†zÿ„ÒþY¦½óMô݈ô:j<ñÓìñ2Õu0Ý¿ô0ʼnËD§pÿ.{g=ÏsÜ ñ/Áw×oA̸ ÏÆ£«85ßCÓ¸áø?X°z”‹ˆVÇEQCþËEŽª,.}î{žç¸„Åýqþ3ƒÔþ ‡å©®ŽáA?zžôÑ]]˜ :=vPÇü¼Sj TT/•cqè÷=Ïrνþ'”=räŸëצî±a½BÃèü´Ìx4”xÞ 3–Q%†W,Ut®J0S$a\ÈYu>ç¹îX^š(ñFŽemÞ óÜ÷=Å\s,€n=ÏsÜËÏsÜ÷=ÏsÜ÷=ÏsÜ÷)þ…ÿfJõïÿ‚þ_ÿØãç¹î{š¹zí•ï“þ‘/=ÏsÜÜÓÒ¿üpÏø‚{žç¹a´äSè÷=ÏrW=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜèö<÷=Ïs\ŸøQ¾`À2Aý fÜÙŽQ匭–=Wô?̹—©†‹Ãðú,Rº¦¢¦¦¢¥’8a†4g’G`ª ’@÷=ÏrÎ?áØ¿ /û™g§ÿý\:ÿÏ¿=ÏsÜ÷ü;áeÿs,ôÿÿ«“§_ù÷ç¹î{žÿ‡bü,¿îežŸÿõrtëÿ>ü÷=Ïsßðì_…—ý̳Óÿþ®Nçßžç¹ît/ÂÈ ?¾€_Ãþo'N¿óïÏsÜ÷))°<¥ÿ üC:Ï”ó?U¨ú‡øHþ8†Z¥Â:i•±ªz¼­]NÄðÆÅdÄ1,C™…F†+c$*äVIG=ÏsÜÚO)e §2Î’²&WòVMËÑQ嬥„QSa¸fGìŽ jZ4Ž(bA¢¢(v÷=Ïp­~"HÎýü>ýuu¦xü™S¨ý*èßS³'OóDQÁ4¸n7äœK¡ªHê’X™¡žp®Œ¤‹Fœ÷=ÏsHÂSñ|üHúë[ðÎËÝmõ£œ3oJ}WI™°î±à}Sé¾Xɹ­¦yé0ê—˜°Ølb¶i„ †1Ú–XXH®Ãžç¹îޏÿÂŽƒÔ?Xž”(ýBbY7QؾE궯-àX­?K:a†fj¬!(h¨iè6ÔÊþS;½@2yP”Œ³ÈZ/sÜ÷7üÏýGÅý!~}Në\Yú¿ÕF9éÛ¤Ù›;AÔ,Vl9ë3åN]Êõy‘j$|zPµÍØ)‘QQ€M<÷=Ïs^ïÂn³ñaõ)”=~"@üXð,S.ú¦Åó=gW½æÌ³RฎZÂóF0ܦ¸`¦+¢‚•ä3%¼§hüÁ2 _sÜ÷ ?âßë èo^óQ=Eg*Šü½øˆä¦t÷Ñ'PòžIëïYisƆÐaÔy½*tT54UOQ3PÔi#R‡Ëc$ñ]w{žç¸/dŸÄ«×viÿ…u ðñ«èl)éS ô¾³–†ŸÊÑÖÓÑω`ÍOŸ+*fwªž#,Ë §)*|×$l".¾ç¹îl{ÏsÜ÷+ƒñü/=4~%}-_«x e^¯äøÚ£¡¥0;Ðçü÷=Ïsßðì_…—ý̳Óÿþ®Nçßžç¹îjø÷ú•ôáê‡ñ1ô/Œúkõ’=B`¹c¦ù—2ã6`9²—ª—Žhá©—ªªHdd•\‚F [žç¹îXgáÿK,g ,¶åÿ.{žç¹´7JTŒ"”ÿª¿Ãžç¹î c°ç¹î{óÜ÷=Êÿ… ÿΊü>¿ù0:ÿ—šî{žç¹¼÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ïs^?øT ÿªúÕ?ëtÛÿfÞVç¹î{—»þuú[ÿo+/ÿãçÿ©Ü÷=Ïsßç_¥¿öò²ÿþ>pïúÏsÜ÷=þuú[ÿo+/ÿãçÿ©Ü÷=Ïsßç_¥¿öò²ÿþ>pïúÏsÜ÷8¿VzX Ÿó•—ÿñó‡Ôî{žç¹¬§Mú1‰‚½Su£­Þ¥ó-tÿ…7£Î âY#Ó?¥L>®z3ª™¿(H!Å3^fz.JªšP”P€P„ 8©÷=Ïsh,‘‘²WLò–_È=9Ê8fBÈÙN™(ò¾MÁ¨ipÌ/¤‹ìCMKD‘Åbú*(÷=Ïp‘þ+^£z—éðëõmêW£•Tt]OèîS¨Æ2]V!H•ôI[T)šÊ‰Ò ç¹î{šÅþ ¿7­¯Zˆ7¦ïM]Hë·K½CtϬÝ<ÆsXépœ«ˆtÿéæc Ã++“¦8é¤Ý\SSÁÂj"0Ìó+Œ=ÏsÜ$8ÿ…Nzêê¥/O}%๧!dÏW}_ëýNSoP“.ü¾IÉÝ5ª86 …©)da5T¸Œõ¯Q;é1´™Çî{žæó^£:á‹ú*ô?ÖoP½FĤë.eôÝÓ¼k3fŒA))°S™ñ\¿‚KZá)é7ÅH•sŵUK•µ/´“î{žå"~jüy½PÒúAõ©Ôž¦t[¨Þ½SSbx¿U:˜5nUÅú}€ÿ0jJ 0LB’ž¾£«’53yUnc÷L-"–YÓÜ÷=ÂÑ_ÇÕfjènOϽYê}.TÌ9“×¶!éÛ“é¾ Åˆd¸p|¶ŸªJüÁ‚Š'w­—~#ÌH¢Öùî{žåËIþ„ÿÚËýš©Ùóú•þs?ÌïûSÿ,Á¿¨_çÎùo‘ÝüÏù‡ÊùžïÎ|§—ûÿä¿IÏsÜ÷ õ£þ¯è¿¢}kê_E±þ…u—=êÍ/HºÓÕJ<§ƒK“²ö5_=E5%d¸ŠcMºž©¨êŒkPé­ä¨$÷=ÏqEé‡ñê÷_ÿ¯X†ö/é2dîzuÃÖ-çÉ0 µñâtÔ‘×Ëf*—Å$Š—ÅSþHFê"–%ØdÙ¹î{›óÜ÷=ÊüP¿ì¬5øÇ®Ãúxý/þ%½7†£Ëyç/$XvÔæ¥O˜|5ÑFb¦¬Â1 ÕJ»Ã—‰B¯¹î{áÉøªd¿T^žúMÕ¼ÁáÙ#2æì< ã”*+á°Ìn‚¢L;§U©H#Z˜$ò‹€Æ=¤yî{žåÞdP}.Æ)aõ'À¸lcÆ^{žç¸+ÇÕ®–ºßüå`øùÿêw=ÏsÜÉþuú[ÿo+/ÿãçÿ©Ü÷=Ïsßç_¥¿öò²ÿþ>pïúÏsÜ÷)?þaÔNŸã?‚ǯ 3ÏX6+‰Uà¥Ãé±:)çŒí‚± R³1 ç¹î{šÖz§G”ÎÝ~^“þ‘/=ÏsÜÜ·ÒºŸä8n‘/÷sÜ÷=Ë ¤ÿ"Ÿ@ç¹î{’¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žçG±ç¹î{…{ÔçA:5ê#"¾Eë§HrÇZò\1bÙC6àNcÂ㯧ŽHâ©Z\b ˆ„Ȳ¸YîˆSÏsÜ÷5µõOø[z4§ùÿ꟣.–åûnòÿ—ô÷)Òmú>^-ÏsÜ÷)c<~}#LBqAÐL«K'jE–pxÔ¡)‡=ÏsÜ ›ðÏé·îôK-ÿê=…ÿç?=ÏsÜãÿ ¡ÓûrYoÿQì/ÿ9ùî{žçü4zpªæÉe»øÆ{ ÿÎ~{žç¸cÿUy»ð'õŸónré½naü<½M&N·Ã—¨jÞšæ"9i)qúz d´”2G3%bF0 ‹¼QG/¹î{›úô+ÔD½NôÓë§®©à}cé†fRp|é—ñ |F‰ÝUYá‘ bbž=ÀK d½×U:sÜ÷=ÌýzèÞQõÐÞ³ú|ê•ä>»e<Å“s´ØtÉMˆ&™ðzŒ°ÓK,r¬sj_ËvF¬H=¹î{žå,ztÿ„Û~^œú£Ð¨Ñfî¬õzOKX¬xï§þŸçŒý65•2®3 bbQVPat´tqC"TÆ“€¤!‘U™XÏsÜ÷¤ü}Iѯ\ Åp<ɘ2Ÿâ›—;õ³­Å0÷Æ(3¼¸í4¸-dX|mJ”Õ3;DŽ%3+nWpÞç¹îXW§OJ=9ôãék"ú?Ã1|kª½"È~l©…®uª¤Æñ¬´ë-g¸vOÍ}}ê?PºÍ× 2‡¿äX:“Ò<ö(lÉu3ÖÓAXp¬N›¤-.5<ËåUQÅ Úâå@7ƒî{žæªÞ¦ÿOÃs+ÅZÙÒf‚21˜±lÑ%­ÿ/±7ç¹î{”—Ô_ÂWÓæˆÔEƒt^ 8UŽÄZ¼]¬/þ½Qç¹î{þÝRmÒˆ¾æ1/üèç¹î{œ?á¬:Aÿnž/üiÄÿó£žç¹îc—ð³é!?æž+ÿÞN%ÿ÷=ÏrëÄ{ ~ƒ5þ¾´j¢é'@ºŸškó7¤Quà×(«1¡ VåÌj±†ÚR¯ËLí´5ÕLv÷=Ïs{JºZúZjê˜ëh«cIhë"u’)b‘C££¡!•lG=ÏsÜ,·=,à¶ý'õÓÒ†iÍU™#/uÏ—ųf 5´K4saަÈÌ v³iÏsÜ÷)ÇÒwü'k"z}õé/¯}Uõ·ÔïS£ÐÆ.és¦xìx€åÊI(d HJáôí<ÄdHÖT»¢ï.€¡÷=Ïp5§ÿ„°ú-“Ñ7]ý"c]@ÅqŒÕÖ~¦Óõ2‡Ô¹ÀðHs~WGK > "•ž†8e®UŠF°j¹\À{žç¹|ÙÓe'éI}Îu¾¢0c#Oz£±ˆc¡Ä³fY„>U5h¢m«QSLäK"w%ʼnÓÜ÷=Ê_ôŸÿ è ôÝÖÿLß5þ RëwC}Ucu^‘½9Wdž`”9pã•ɈM%‰`ÍØœtQ»DÑÄŒcE#É&÷=Ïrnÿ Ôè¶Ól‘Óx}FfŠŠ“ê¢_Tô¸“aXH–|~Z ºƒaF·š?Iv<÷=Ïr/M?á:é'¬ÌÔfIëý]7Có'QkºŸ‰za¯é×Nq·9ƒ‘*'¢LӎᵘœxO˜ƒe$aZ5±IVoÓsÜ÷=É=hÿ„êt[¬ÿí‡üÏÔfhÁÛ ®_­ù—Ȱ™?“â¹cù畆Sy‡ô”òÿ<}Îþðضîyî{žáÍÊ…ýWMÿÞ­þ'½:õ/ŒåºoQX‚zƒôß.^Á+ð¬Åý^Ëe¼2Hqjôº§4Tµ%!]Ï"2´ž\…¹î{–ÁÏsÜ÷(‹ñ¡üozødtŸ3dL˜èzë›;aÒÓôg¡|ÐVÕaUu²Ácñ¡o” §ÿ*©(RWdco™,^ç¹î|ÆúeéÃÌñÓMœ0?ë&bÄçÇ1ie©ó**§‘¦‘‰W[ûÌu¶½Ï=ÏsÜ>Ùðëéf`§ŠYºcÅ€¹óñßèœsÜ÷=Á’ÂϤ€ÿšˆ‰?ô³‰ÿçG=ÏsÜËÿ aÒûtñãN'ÿ÷=ÏsßðÖ ñé<_øÑ‰ÿçG=ÏsÜuÃ? žŒK:,ý%ŠD'Qó8Ÿì¨ç¹î{—·é 8†]¬ÂbZ†QB–k* ¾À9î{žæÑÞ0 °Ì"… •Eç¹î{‡r˜Z5ðÐsÜ÷=É÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ïpõ×îúZèÏP½Aõï9ÅÓÞô®±<ùœ¦¦®­J1*@SáTÔLìò*$pÄîÌ@U$Ûžç¹î#ý'z»ôëë“¢y{ÔW¥Ž¤ÅÕnŽæšœFÍ±áø¾íU…VɇÕE-=ICY ¤± a]Ëg[£+sÜ÷:Aê§ÓÏ©ìÙê§}Ïÿ×\ßé{3Í“:ë„)Æðïäy’fz?7£¥ŠªÁéi^XσóÜ÷=ÂÏÒΨúoõµ…ukôý—¨˜gE3¶;Óž¨Tÿ)ư¯å™Ë-ÇM.%‡íÆèèÚ!k">|ámÞäcosÜ÷+G¢>­?ÏUÝy>žz+ÕôÍEÅ<Ù"YröaÃ0LÛýWÅŒ ½‹â”ôX©¡ec/ÊLþê´‹º5.=Ïsܰö;À队òþŽ{žç¹ïö;À队òþŽ{žç¹ô“”°¬:»Å!§ÃpÌ6gÄqƒ0SÓ†I$–I,¨ˆªK1 ©ç¹î{šÃõO&æÿÆ7©Ù—¢žŒM~Nü;:iW%¨Y0RÍKþpñv´Ùo(IQùÔÂÛjªÀ(A¹¼F5ª÷=Ïqš .§ú+έÔïÃÓ®YËÑ~}¤Žž:Ö1ŠÜ½ $òáþs‚b¯5. ÅfX’̉<÷=ÏpÊô¿ñÿü\}4ÉO€z©ô¥‘ýie|7dsu&bstû5Ͷ´õTµÖaòË{˦§‰HÒã¸÷=ÏrÃzkÿ Áü;q¤£õÒΰzHÅ”‹Öfœ‹QŠà±8>óAY•'ħš!¶iPÿ«ØŸsÜ÷,/¦ßOàéÕhéeÊÿˆgNpµ¬ÙäŒËŠM“y„½s„a¾»À·¹î{žáÅÊÞ¼=ç˜V§$úËéFq§x’uŸ ê&PÄÁ'Ù5&! ØÞ ØóÜ÷=Á{ ëwF1Ê1\«¹_Ã*·|¶#KáUI±Ìm¶Hge6e ØèA÷=Ïqõ)éÏ/V¶õû$àx‚ª»PVf¬ š`ª±Iª•¬| ¹î{žáwΊoái¾k7þ ýÁPñRÉÔì˜õR¡Eº*xq–@µ(†ÃS` ç¹î{„7ªð¦?Áo¦-QDž°¡ê^`ˆ£eÌŸ•sŽb–¤…W´UTx_ÈßÞj¡u¸î ½ÏsÜ­N±¹ú{SÔ^?Î¥u~º@ÉK™sånÓ¼wa4^Cc“Oï´ˆ™µï~{žç¹K¥Æ{Ö•-n\ÄzÓ…z7éž*¦:œ•Ò*:¼?ž í›1bSTâIµˆf¤š4:{œ÷=Ïr¢ñ/@ÙÒ¾ë‡Kj*¿ÎÎ$µ£Å*§¬8üÒóC_-KÍ1ýó` ÖÝǹî{–eøxô³+ú‹‹¤£Â¥Ë}JéíRáýUéur±l®ÙapÃ!Bb” ¬0eç¹îm'éÿÐí%Ÿ…²¿¹ðú9î{žáíýàI`በÿö}÷=Ïp¦çìùèk¥Þ§²£,ýÖl-z›ê|U¤ÓSâY]"õ1Ò>g¦æ4’ŽIU…‡¼»½ÏsܽDe?M¾“º}U}Cfú>™tþlSÁb̵t•µ1OŸå© Ù‡ÁQ&é_@vØx‘ÏsÜ÷=ל§é·ÓÓ|[®ù¾§xwWsvúqUSI[P1LÛÇQ.‡F(`œ¬“­,¤3…A·V_Ü÷=ÅYrA=*t§:õç¯Y·éIúoE&!›³…zÊÑSÁ¸X᥎Yê'Ùb‚ÞY\„ˆÜ÷=Ãyé· éÏRº_Ó>³t³\ÉÓ^¬à6géædµtCÀñì>V‚§åñ ž/:žt}’ÆŽ·³*° {žç¸uðºI °ü¹î{žãÏ=ÏsÜ÷=ÏsÜ /øPÏüè¯Ãëÿ“ ?ùy®ç¹î{—ûÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷±:AS ¡¸<÷=Ïpªu/¤¹•&ßL}ï§=ÏsÜ%y‡Ñ¾ˆTHç S¸v{žç¸’ÿb þ™‹ÿ {žç¹ïö Á¿é˜Ÿòç¹î{‘ê=àÅýö®£üžç¹îïS?†ÞDêVLƲžrÉ4Y§.âhE^YL“DÅuV…ÕÔꮤ2Ažç¹îS¾EÊŸ‰ÇáaY% /TuѬ>G’IýM†£7äX“sIäáòÌß=†ÄY‹0¤’6vÕÜóÜ÷=ËéOü*o>d©ðO^_†îrÉÆžÑ×u3¤ØxÂæmÎØV$ø}E$ ÜÕR¸ƒ9î{žåžtwþ‘ø4uŠA‡ÃëénbŒ]–³Þ ˜ò|Ô¤ÛÝ’«¡Šˆ¶½’¡­ß·=Ïsܲޜúßô]Öiª:Mêë¦N†¬í¦“/çÜ«Œ‡a¾ê?—×K¨òÚã¸Ú}‡žç¹îºzŠzºx*é'JªZ¤Y)ªcexäÔ2²²’ Üßžç¹îfç¹î{žç¹î{F{ëÏCzZ•2u;¬ÙO§1ÑnùÉ1ìŃá ØÄ­¼âm²Æþöç¹î{•íÕÿÇOð‚èjT¶züB:m]%”ÕÑåŒkúóSAöÑ ÉâÒ‰m›w ç¹î{•;Ö¿øW¢ìk0ïJ>›º¥êËE?Ëq±„Ñòœíou_ÇŒµ‘\øœ<Øk¯n{žç¹G§ÿ_Æ+Ö>%–r69€zé~*&‰ðœ†“bÙæZY´òçÌx‘o"UÝŸŽ™Áמç¹îPÞlô½ÕŒŒWõÓ-Òâý\Ä$2Ôõo Äê&Äñìf$55ÑÕLI*owpnZ×±ÔsÜ÷ mNÕŸó;ÒP¾Œ2¤yÂs'[†æ,ƒ>%VZŸ©Ç% °ÔEQI0xÃûÅ’Ê×]Þç¹îm9øiôϤ¾®ýç¹îZÅ'¡üF·ÃAþÏsÜ÷%ÿ± ÿLÄÿ=ÏsÜ÷û`ßôÌOùsÜ÷=É4Þ‰pˆ¤¸bèu÷=ÏsÜ:{é‹ËÓÂñЄ(Gî{žç¸z²nV§Š$h@9î{žàž‹µ@ç¹î{œùî{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žåÂ…=(zâõéé“¢ž}d*s,uƒ¨˜_©ìÿŒcxF€à?/Ì•ð®#OS[OˆUÃ%{ARɇC4Ö¥"Àºn÷=Ïr‡1ÿÂwñ§ÈÞ’?oHx·Gp. ôÓª½kÉhËx/M³^–ò¯P°¼f­¦ÏyK¥Å1(1<2›Þ¡h¡­ŠíG2ÆÒˆMî{žãeàÕë÷è_¨\.z!İ.€çÏX]>ê¾1èJ—¨™>LO9t.‚‚¹jòãbTXìÔêô†ªw¦¨©Uºù‘´‚ݽÏsܾ_ÀoÐG]ýúRõ»Ñ¿P}!—ÓôýfëïR³_J2̘.kžŸ#f,·—ð¼2 WàÕÕÁ ±Ú¡–bcÜè7}ÏsÜ«/Ã_ð¶üAzo×Á÷¤oôÚÝ&éá ]ê6§¨¤Ó4e|Cê0êÎ!UW‚.I‡Vˈ”"Uó¾jž#{ƒ„’ÑŸsÜ÷7EþYMÿ×êç¹î{xåF\Ë8./™3&)I—òî_¥¨®Çñúêˆi(¨h©"i稨žr‘ÅQ¡ww!UA$€9î{žæ­ùÏ;u¯þKŸ³7Dýî{žç¸Vsá?Ò¥w“£LD35 ¡Ze»jt¦Ø-쇇=Ïsܯ¿Y_‡—M:C—zUŒàÝ;‹¨ÎAÊ™zºHä¬ -6+Q,RBÊT û aç¹î{†R/ÂÓ¤” ÅI©YnHó \­ÿ!K#Ïžç¹î/0?Ãg§Tn¯IÑü$Ь­„RJëamXØŽþžç¹î¹sÐÑÃQa²ÄXd-kÅOM @øFª9î{žàÕ–ý b32n›¸¿¸£žç¹î è¢V‡ÌÂ}—÷?³žç¹îž•z¥¦zg› ÒãgôŽ{žç¸Œõqø=õf§Ëž·=âô½:õßÑšP)°Ò#Ë]SÀ)ÀyrÖ;Z‡ ÊÁ%I#OsÜ÷-ª<&•ý~®{žç¹óêõƒøV~8ž¤}GúÙüD²§Ì—ó®Öì—š}0eLÌԧWãÀºM3áy^\²0éÛƒª£ÄR£‚º®9e–”²ªº¯î{žâ—ñü6ÿOV¾¢ý_gÌwÑ·Qú‘:¥šºœý1çtÎØ<?$d7¶eʳáõxõ$ ]OYQxR’]ÒSËR.£Ü÷=Å_©Â÷ñ ê¯âÔ\ó™}/gî«fœÖ®CênBõ3&sÃ_!Ózx£«ª’‹£Â±r"k0µž6*„´H’¦õ.«/¹î{›4þ3^‚²­ÿB½vË4ý¤ëo_2VMÎ3z]ÁjeUj,åŠàRaôõ±ÕÔAIóZ ÓßÊoy¼÷=ÏpÑþ]/Î}ü?} ô©y}ò§RzYÑΙeΠåyd‚ipÜoÉXn_JòR¼±3C<.…‘ÙI‹{žç¸s@·=ÏsÜïžç¹î{žç¹îPü(gþtWáõÿÉÐü¼×sÜ÷=Ëýç¹î{žç¹î{žç¹î{žç¹î{žç¹î{~{žç¹Z(¥¾åžç¹îAl‘»Â>áÏsÜ÷1ÿ ¢ÿŠWî÷=ÏsßÈ(¿â•û‡=ÏsÜé²ý y+÷{žç¸‰Ì>ÃqJy#je!Ú9î{žá ë¥\1GRF­¾ý~Þ{žç¹Sý]ô IU%KÁ„¸¶¡=ÏsÜ­¾ª~.6³AŽdŠ,vp"¬¡‚¥l =§G÷=ÏpgÂ;£u’JÓtg ¥¹ÔQÒµßA@Ðõ{žç¸ÿÃPtï,TOS•2î3”'¨k%Ã1üÁJÓ.àÁ\­a¸°íÏsÜ÷ _¥/KYË©¹K«Õ'ªY÷Ãðñ™òÆ!‡Pç~’’ª‡™`HjR$²JÊŵ ž{žç¹ þš:%ê·ÏÒdüo1fì%㵘&`Â*sf?URSK¾š¨ÇóbðT¢‰"$w^êyî{žá¿ þ‘á­¦é\ìû?1-}eýíÚŠ¹¥¿×á§n{žç¸6eOÃ÷-ào`=9Ãpib·—56K€Ú×ßa‰ø“~{žç¸aòï¢?•1útSWƒã™©êéË ‹•u+$lñº±÷=Ïpæÿ ¢ÿŠWî÷=ÏsßÈ(¿â•û‡=ÏsÜì`4c´+÷{žç¹&<*š3uˆ»žç¹î8$J‚À}\÷=Ïs/=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ­ŸÅ³×´Ÿ†ß¡n¬ú Á2õwêN6ôk§u¢©âÌ9«0bá´”ž]Ç4å¨tƒâ`¦öç¹î{„3Ó㽓 ôÖïRŸˆQ‡£wô·Õ¬s¢ýuè^G£®ÇkfÏT˜§ËaØ~ E-DòÎõQH]§1³Å1l¾ç¹îýÿ…dLWõ‘ž=LôŸÈxIºõ†tGÓOG°¥ŽMÕÕãTõSÒaUøuSñu4’$цŠ5u(.ì½ÏsÜ=?ƒ‡â9ž¿.ú¥ê¶xéýN"èÇ\ó¿Lò6 OEŠáµÓe칅ตF-IŒM4°b,1FZ˜¬vìR=ÏsÜ%üR¿oÅG¢„}Kô/¤TØO]âϘ¶?Ò¾Ÿâ¸î=Ô.åL½Dkpk8b©_YƒÉ3¢CpÓÉïB–‰%÷=Ïsb¼áœr—Or®aÏY÷3áù+%e:ŒC5fìV²ŸÃ0Ú XÌÓTUTÕ¼qEh¥™Ý€Ryî{žæ®µ˜×YáG]OÆrÎRÅóBÿ.—âsPæœÛHÕXhõ3Žau[d¦¤‘–9é2Í<ÑYØÒAý?»Cî{žæÎÝ4é§Oú7ÓüŸÒ®•dü?§ý8éþM…d¼—…SGI‡á¸}$b( ‚(€ ªÒN¦ä“ÏsÜ÷ä,yî{žâ;<æ ¯‘²~kÏyË‹ÊY+ ®ÅóF56‘QáØm+ÖTÎö¿»Q³€ç¹î{šþ_UºÇøƒUKêÔe&{ôßê»êÞ3Ó>ÓáøDX—J«ò¦3[˜0l>y))á•þgÃ¥†f™ÚW’;¶í[Ü÷=ÅþRüc=Cu›Õ¤ÿP]Aèêô ÑP:#×þ«§HðœÉ„æÚìÝ‚t» ÌUzÙ$Â(f¡¯†L ¶Ã+¡'vô_sÜ÷ïC>®½NúÚüPú”º•ÓZ.€zë·¥ù:³ÓþˆáùƒÍÔøŒ5¹â¦ƒÇjk× Ãê)êži䤹Uò·÷}«î{žá{üPóϨ̵ê›ñË]4ë†'Ðl«øuts$u§yO  À¥¢ÍøÞ?ŒaïS.0qj)娥òªÚAª‡×¾ô“Ü÷=ÃøŸtsW¢¿Ã¬˜Ž]L¿Œu¯­~ë±|cd³fˆ_šžÒ{À#JVÇ]5ç¹î{—’þ‰p‚÷þXƒá°G=ÏsÜz ôYƒÆA8Z†Áíú9î{žâëôy‚DWýö%džÁÏsÜ÷œÒ¾JPÿ.AoõôsÜ÷=Á‹è A埑AnÞèç¹î{‚îÓœ.We*®ß€ðç¹î{‚ML $`[êç¹î{”‹ø—~½TÍ=SËÿ‰Gá«Ñôsñ+èÝ“YE6Ú|¯ÖŒ¯N©æålÓø£‘äŽ%Ž’®FR¥QÐ$Òûžç¸j ÿÄÃ¥ˆßJñüO À+:9ê;£•ŸÈ=SzYÇ÷S挃š)Ùàšž¢Ò$¤’HdùjŸ-C…*Á%I#OsÜ÷+»Õ/ãùCéËñBÀ= Ót ,ÝÐ\¹šz]ýDú¥þ~Ôñd¬ãÕŠ\B¿¦’‰håàX)Uå•çM»e€7¹î{‹¯Äãþ é§Ð~/œºCÓZoóÿês¦ù›$`Fé‚Rfl?Àÿ®lg†:¼ÇM„Öa±× Dzˆémî¨ãFG î{žç½lÂ=4úuë—L½2t.›ý¡:å‰uË$ô{­˜Êfl'Ê_Öv¬‚ºtÇN6UˆaòÅ=Nù¨ò¤Ûî{žå‚þ ž ýXú|éöFÆ}(zyËdÅs2ðu?¨Ùß9Ñ伇Ó\±IC-u^`ǪvÏ[%,b;0¥Ê(gcp‰'¹î{‰Âg×®7ø•z!鯫ÅÒ7èÆ1›ëqÌ2¿,¥l˜žW.ŠÍ„½~Y4Ï5CBY F °hîû7·¹î{–IÏsÜ÷=ÏsÜ÷=ÏsÜ÷( þ3ÿ:+ðúÿäÀèþ^k¹î{žåþóÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÂÅë;ÕOôUéo­žªóþ^ij^Nèv .7˜2î)N'WOÑÂc§²ÁòdmÔ|yî{žåLz=ü{½3zÀë®FôûšzÔL9›«=<›ª=*Í9å2ªåìc&ÓPI‹KZõ¸.1\h×å!z…5(‹±s#lW÷=ÏpødŸU¾ƒ:ý”ú™žúKêÓ¦}JÉ=‚J®­fÜ:åÊì7-PÆ’HÕxLm-1X]–yYc`¬C¦Þç¹îŠRÿ‡OSúWÔn¹dßW0̽ é3ÒÅÕ¢ÒçL+/Ë^Å)ŸævÓµK”ë%Œ¬6¦æÓžç¹îßFýkô×øˆãž§hz†ËŒeÏM9»ú¥_Ԩ«ð<ÌÒSµT8Ž S…TÔ¬ÔrÆFí´A·=ÏsÜ+ªI}UüF*ÿ.”áõ}AêN\Ã3-GQsõ$&Ç2­SÒb#=ThÕ5p:m™ Ýlv.WÜ÷=Àðvô–:»Ðï[øvdÀ`ÆðeõÖŒÃ*"Y`¨£‡¦§’  ŽŽU”èA éÏsÜ÷?C½7Ǻuë5áY ±¼»ê«9J R’‚ššeÁð\V“åh·@ŠL>TgÝK n7÷=ÏrîãôCƒ†¸Ãþ@÷=ÏsX?ŃÖ×Y=ú߯½:d¼W%d>žà])£ÏŸÖ<Á‘ó>l©¨Å§Ä«(#¡c—*âùxªÑ'™Q±÷ßÞ÷=ÏpËeÅÿ£] è'¥ìgÕ÷§¼vƒÔ·VúdýWê—Lòõnš‡/dVᔘ¢ÿ\qì=Þ|Fž•+)°È¤š§cÙ»Þç¹î¯Q_Œ§£HxOHóæ5Ю¤çÞ‰uS&å<÷†õÓ Áòý_þGœe§ŽŠë/†Ï‰âŠ€Õ””QÈðwý‰|¿sÜ÷ÿ‰'ã‘IéÉú½ÓOE=Ƴîvôûºc•º»êËÔ•4Ã*³ê½za…£ÅèñTô±HQOå¤É±½sÜ÷6õïÖ^ºz_ô¿žzéÓËz‹êÎZ`ÙUñŒ;°haÂk1±¼V«tÝEB)wI W–f+vgÜ=ÏsÜÕ#кúËôé¢?Ž?¤¼Tú7êzÌ?Šáó•èS Ãäs6%†Kr. h¨ë)©©Uê©c²0 måôsÜ÷,#?tÌ5ÿü'÷7a¹û1uwLCÕW£˜ÃS厶aôr´ÕÊôq|/7Ñ3H¸$y·hdyR¯Ü÷=ËÁü??>€~#ýºíМVji)¦l/ª=.Åi³.IÌ´Ê^ŒRº)âkíkl‘,èJž{žç¸xùî{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žåeþ%…ŸC¿Ü#ÓîEõ#›s]è~oLát—¬Ãó\TÆ‚:lN¦$‘B´óTÄ’hew+"¶Ò=ÏsÜ­êÏøKç¢ ¹“½Kôç ½KÏéç^ñ®žæ¬’pìQ1ºL›úm$Òa¸®>dZÚÙž_¬YÒ®¦PVr§—ÏsÜ÷8×ü'ƒ£Ø®UϕկR›¯Ù¿®_Ô6êV¢Ÿ'IŽáNËTÓCóÐPQá˜kAPõ2LÔÆœF³b„@‡Ü÷=Ëü=<…øtôç®Ý>éßSó?T_¯ýMÍTÍÙ¿666(¹ƒ5Pá´5J¿É¨°ø CùjH?D fo î{žåwúhüúQøXõ7ªþ¸±ŸÄ‹ªe*ìCÏ^ªêóVE§Àóc½Qš£0×Ó`tõ 2T4ñD“ª¬¿anÌÜ÷=ÂËA‚u§þyÔŒ6ç0ôð2é¶'vTÉ•. šýKã˜Maòêkb‚A=Y§šÑ©*Òý?½Cî{žæÑÙS*e|‰–rþJÉ9v‡(dü©GO‡å|«†RÁC‡aÔ‘,SÓSÓ*GQ¢…DE`9î{žâƒžç¹î{žç¹îÝYé^Bë—LsÿFº©€ ×ÓN©aø~ÊíSYI%ƒâ”ÏGWK$´Á2Ç4R2>Ç©"ö<÷=Ïr¸2o ÏÁç:æ^“t£¦%éV;ÔBX6!²†YÆi4dœO «ŠlMpjñ_!IàU•1 w1&þç¹î3t§ð¥ü}1õ“'á:è.HÈnÇpÌè™$Wæ|WÅ«röj¤¨§ÌT”>?‹Uy˜[ÅQ>úX 4Ðy’˜Ò2îO¹î{НK¿†ÿá]è¯Xé«¥ù_¥Þ¥pÜŸˆÃ—ðÎÖ/›ÿ¨Øž0&¨e£Ì8ÅuT˜wÎAµ$(c‰”ÇQuç¹î{˜½vúü)úçúÖ^¹ &Ófün§ÈY;7æ<ãˆä¨³EU^ ئ–êRÂéñ¶’¦––‚©'»)(š{žç¸OáAX†t ðõ£Ã裡¢¦õsÐ zZ8Q#Š(S­EDD*¨€ç¹î{—ôp:;ßÉ_¸sÜ÷=Îc¤¡îç¹î{’ §Q¤cžç¹îfJ8Ph€sÜ÷=Ìë-¬;sÜ÷=Î`ØsÜ÷=Îùî{žç¹î{žå~&†U3_U0ħðÖÇèú9ø–trÉ­¢›m>WëNW§Tór¶i‹|1ÈòG ÇIW#)R¨Žè é}ÏsÜ×·ÑàùèÇñ™é'­n¦uÔ÷Rºkøu©•Øïª.™WÂØ%WHóaÅ~hauYMëx•$.µ°áõòI¢“b¤3A"sÜ÷-§­ÿðœ|w­YÇÔN+UëÿÀ2?ª¬g¥y×­¹!z}ƒÖÍŠu¦”‚‘ñtÄjq_6 wê^ZªÃpœsÄë0aùÿ t0Éi袇 ¨j?8º<›7¹î{1ÿ„Çzѭ駪£Šuߦu;¬˜B³6BÉóÕç K'¶1Ò8ªðêœ'š³ ¾SMVZ—¥ Œ‹SÃU_sÜ÷/‡ðžü9zÍè6úñϽnDz&%zËêQê%Yéý6-I‚e÷¯¥‘ë(<^œ¤u2Ç ÿ(£{*3_sÜ÷¹çðé΃ñuôÿøƒeü/`½$é7J3FEÌ=>ÕÃÖc™‹¯Æšº‚›åŒlÕ¥¥g”;9cbMϹî{…sþÝ„STô3ñi# cõ{×ÄËŒPûyî{žç¿á=ØE5GC?’0Æ?W|A ì¸Å=ÏsÜ¿a€Ñ÷û‡=ÏsܯN¨~þ“ºÏê³?z»ê^ ‹æ¬÷Ôî“â=Îù.¢º˜åj쓊Îõ1µ¥ó…K™óDúË}yî{žåâ?ð™¿C“eÎ`¹w­ýkÈx×F2¾!‘²ïPðŒã—×0ÕäŠün|ĸUV!—j—åéjê¦zY`Hª!V³Ž%Ü÷=Å?_á6¿‡×¨¬ûŸ³ÎrÍ=PÁ¡êvNÉÙ/:äÜ/8RŒ.¶ƒ!ÒPÑ`õLø¦[Z*c\6 Ÿ"YWÍ’&”—>ç¹î*ºÕÿ éô=×.¥õƒ¨¹‡>uK+áýw«È¸ÇT:W—óu”±LÏÓÈV— Çf þW#=i„È’‡• c#È"YvȾç¹î]f}ɸGQr6séö`i“ÏxN#ƒcoNë@¤Å(䡘Äì®öJv’¦ÇÀóÜ÷=ÀKÑǤþ—úôÓÒ¯J}¨ÅjºaÑêJº,¥QÕÃ]Š45¸N-'ÌOO2;yµOb#],><÷=Ïr‡ýSzYë§à××Lùø’þY«¨ž‘z‰TqoÄGðîÂFØD+ïTç\•L¿£§­§ŽïUJ€)PtòÞsÜ÷º±Ð£ÖŠ|ñ×ü:ƒ‡brÏÔP×õç 1¸§ÊwÁ¨_Ì®Áñ¼9Ê ÍNC"Ì|¹<ÝÁŠÈþi÷=Ïrãÿ¯ÄW¡‰'B׫]%j¬©œ²¥S`½wèF4¿-šúšé·GU…b´²,r)Y#&mвª’ºº'¹î{‡ïžç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹îý_êÿLzÓïÖ~³ç|?¦ý,é¾>)³¶)8‚‹¢€{Îí©fbBGîåQ™€>ç¹îk?úq×øQV|Àzçê+Æ:ø0ôû‡ô÷é’tŸ ̽¨Ã%ÝO˜3KG x0O57ÒÒ§ù@}ÒHZƒî{žæÒ˜‚åœËyo¥Ëùw/ÒÓÐà Ž~%£òkh¦ÛO•úÓ•éÕ<Ü­šbß r<‘±ÒUÈÊTª#º‚z_sÜ÷ _áø•tÃñé6bư¼·]ÑßPý®\½ê«Òþ<¯eéþjŒË ”µ1N‘I%,ÒSÊijv(#+*K±§¹î{–MÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷( þ3ÿ:+ðúÿäÀèþ^k¹î{žåþóÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Êÿ„óΊüAù0:ýÿ—š{žç¹ÇþÐëB?wv‰êÿ¯ÅÜ¡$’yî{žåæaýGéæ-YO‡ayóı ¶ÙIAOŠPÍ4¯ßj$r–cðžç¹î,ùî{žçupJ0p ‚º¤iì"Çžç¹îrç¹î{xÞ7‚å¬#Ì¥À0*1|n¶¢*ZJZx—sË4Ó²¢"rÌ@žç¹îHÃñ ?Ãèql&ºO Äኣ Ä©åI©ê)æA$rÅ$e•ÑÕƒ+) ƒqÏsÜ÷&sÜ÷=ÏsÜ÷=Ía½SzYë§à××Lùø’þY«¨ž‘z‰TqoÄGðîÂFØD+ïTç\•L¿£§­§ŽïUJ€)PtòÞsÜ÷3õÃÓÖêà È_Ž?à;Ô¼+õW]DµÙë$C!¢Ê}uÀé%nWÍØq’ƒ€!Š9¦Ù"JÈèˆ=ÏsܵïÃsñ&è·âEÑÊì푨ªúmÖ>œU¶êGÓN=zlÛÓÜÕJÍNˆSLÊÑbqOSåªÈ‚©*K~ç¹î¯ÆñšÎ†G\=#tW(ô%ç™=QQ溙óî{Ïÿæÿ/eöË_$Wæ«äë¢TœU å}ðª.[OsÜ÷™ñ×ôÁÓÿN½ê箎 dþg^¿Ã±.ež™â9—¬xeÁr> 0Êù°ìg*àM ÏÁ£’U•¬HG+î{žä|Oþè’·Õ× oL}6Ä+º–ýz`ðc9?­tØv`‹ ëªÀðF£\&Z‰*kqZcIP$òRŠÌõ.Hç¹î{‚¾Oü{ ÜùÓÃÖì¯ê7¯èžUÄ²æ ˜:¼ý8ê6^£Æ3V![†aôsÕTåØÄ24´/æB¤jѳ0¤ûžç¸}ú'êßÓ稾 úƒéwFsóg\ééc0E•zí@˜>;EK‚fb’o‘ZìJŠž’²XÖ#æŠIe›*YA÷=Ïr²ý*þ0ߪ¿Åo«>poM¹‡¥=(éÏI1. eþ§çL:¿ÌYž³ Ï8nQjš .¨iƒT|ô†–¡ìò˜‹€#uç¹î{—‡ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷ —¬[~™½ô×ëªÞ¥ÁÒ~™ãØÊày¶¦ž®®ŠÉƒâ8äPyTM3<°á“,klÌûT n=ÏsÜ¡Nô#­Ÿ¯X2ß«¯[¹èÿáOÓZïŸôiè[PâT©‰¿Aœ³Å4l7Ѹ÷記”d>íá2K[î{žæÑ˜~‡á}„ÐÆaxd1Sá¸m^Ç*2ôðæñK.UP•BžeYЯ˃ þóÜ÷=ËaôyëoñˆëÖ+Òœ›Õ?Vùƒ4[‘º¯œá†Ÿ¦9W ›#gŒ³cÙw › \¥y+¦\)$e…v¶ÄÃåûžç¸þ£ý~玙~žŠ}<~"XÎWȾ¦±Pz…ÌøOr>1ŒtÒ«(a´ÙξõØþ\òVÈÂz±URûØâ{X8† ¾ç¹îZGZ=l~+yñn¤ü'²÷Yñ 'ëŸV²ORz)׿əÎé£ú½Žb9ËhÛ4¦z:Ü9i)kgçvB ¤AÏsÜ÷oü)Ó¦=×?ÃÔ.dú·òŽѼ±‹ã5="Êu©G†ç¬Aêpô¡ƒHéæž®Ž£y’–6Ey ³’î{žåŸ~Ôõ~ƒ}ÒUÀôµT½"é´u4Ò+$‘È™7VVV†XƒÛžç¹îž{žç¹î{žç¹î{žç¹¬g©ßJýnüúù/Ë$ÕçÏG½G­¯â3øzáÞ8©½^wÉ4jU ®¥t•4‘íVPÜÞsÜ÷¾¨=-à>µpÎ~4¿‚W0,½ëO  ^^Åé)r·[òÄD5nQÍôŒÔûkÂ`Y*6K¨"™£1Ã5/¹î{…·é÷NÿáCùÓÓg¨œ…ê"»Ð'®_ò<ÏõÇÒŽféî[Ï8öRÌy‹å¨ªþ{ÎÍ5Mú#ŠJ‰(Ýw¼±O"{žç¸ycüsžiê£þ©u³Ö´½PÎÞ‘ð·eÜ"¶ƒ¥™7#ÐâØY²•fVenM’‚†á¿:õÇ4îNò¤îç¹î{‰¾†~8§¦Ü÷øXõ ¤ž°e¢Ç? Ü5eLrŽIÒ…Ä´ÓG›hó_óAˆ|Évµ ‹Éò±Ý¿KsÜ÷=Ë ç¹î{ž&Âü÷=Ïrƒ1ïøR¿áIæ,Ó•äê~tÄq<Šb6:ô/ê}4x†Tôu1,ô˜SÆû$Œ‹«ÏsÜ÷þÓøW_ÝÏ@aÿ„“¨ß·ç¹î{˜Oü)«ð³ðÎÝ@·þ^¢ÿ磞ç¹îuÿ-5~ŸöuÿU/QóÑÏsÜ÷+Û×oâ_øþ$³ôwõyŽõo¨½;èµf'‰à] ¥È]QÀòþ%‹b0ÁLµ¸‹`¸E5|²ÓEÇ¥ZFY7£’¥}ÏsÜ;™ þeøHtÏ%原düÝÔŠL«“h Ã²í_Mz­ŠÔCGJ‚(£j¼[©©—b€ªd‘ˆ Ø{žç¸­ÿ–š¿ Oû ºÿª—¨¿ùèç¹î{žÿ–š¿ Oû ºÿª—¨¿ùèç¹î{‚Ï@ÿá@¿‡©.·ôÛÓ¿M3þi=SêÝM]CÁñŽŸç,š¶¢Š‚lNeù¬_§…vÃ7¼ãÀw#žç¹î\­d£ª¶ÉA¿°óÜ÷=Å$3,ª ›ƒãÏsÜ÷3óÜ÷=Êkõ)ÿ ü$ý#u³?ztõêÇüßõ—¦ô¹ç&ÿQ:—ŠüŒõT0bQ/Íà™v²–]ÐÔÆ÷Šfö&àî{žà'ü*Kð8Üõ²_ÿ¯W¿nVç¹î{‘ü*Oð>ðõªÇÿ·Vÿ÷ç¹î{ËRŸ‚ýÆ£ê¶êßþâÜ÷=Ïr¤¿ïÄ›þ«ø±ôó¡C©þ¶FtC>`y†‡4ÅÓ.®-mFV–¾3>“Ç•‹Ä˜•]F•ÀÇÝVç¹î{–«—¿áNRÀ0<«•½]Óå̳–hé°ü¹—¨z_ÕjZ* (Všžžžrª¤qE*"(T¹î{žãÇüµ)ø Üj7þ«n­ÿî-ÏsÜ÷=ÿ-J~÷ÿªÛ«û‹sÜ÷=Çü©ÿ nüs®gË™;-úÈ8ŽcÍ•ôxf_ÿÍßUaùŠÚú„¥‚?2£,¤i¾Gs°QÜ5ç¹î{—Gš(ªˆ *›üyî{žâš ”™AV¸ç¹î{’¹î{žåyzÁüV¿ïA×-tçÕÇ©3¢ù×8áCËX n˜k¤«ÂÍ\Ô0¯„aõqªù´î¶fNÖ·=ÏsÜ'Â’…ûºð?ñÎß·ç¹î{‘þ›ø/Þ¹°3ÿˆþtÿÏ7=ÏsÜáÿ-'~ ÷¸þ£ùÓÿ<Ü÷=Ïs^ïYž£?žµþ#]üP½;þ,™O¢ÝqéF5•1LÅÓIºmœSÎôÙrr’Á‹âM5>}U#½1«’ Ýb!Ú©³Ü÷=Í„å¤ïÁƒþã—ÿÔ:盞ç¹î{þZNü?î9p?ýGó§þy¹î{žç¿å¤ïÁƒþã—ÿÔ:盞ç¹îv?áI¿‚ùïë—ÿüéÿžn{žç¹n½/ë—OzÉÓì…Õn›æ(³GOº‚áy‡"fX’h£Ä0lj†,JŠ¥¥"‘X&G ê¬/bÓžç¹îRÿü(:¶:®„þeu½_tÚÿö9®ç¹î{—ß™ó6“rÎaΚ½p¬·•hjñ,ÁŠ:»­5 ;ÕO+,JÌBFŒÄ('Mç¹î{”«/ü)ðQ_±ëËsà_Îÿùåç¹î{ÿ KüÁ!}t`L<Ëù×öàÜ÷=Ïsÿ…'~ àÿÕòàúçOüósÜ÷=οå¤ïÁƒþã—ÿÔ:盞ç¹î{þZNü?î9p?ýGó§þy¹î{žç¿å¤ïÁƒþã—ÿÔ:盞ç¹î{þZNü?î9p?ýGó§þy¹î{žç¿å¤ïÁƒþã—ÿÔ:盞ç¹î/KŒ¯áÏëO©sô{Ó©œ3«I¦Ã*±™òµ.˜¨¥\2Šh ž3éb²=D`÷×@uç¹î{–WE˜)j­²Poì<÷=ÏqC‚@7±ç¹î{™9î{žålæßÆ#ð´È¹›1ä¼Ýë÷¥9s6äúúÌ/4eÊÌåƒAYAˆáõISMgž0 òÖòPsÜ÷ Ïá§ëðÁô+ÓßRY35~(Ïu=që_Qº§…Öáù³¦Ž†ƒ;×SÕÁC(©œ–š ëî›éÏsÜ÷,sþóð™ÿ¹†ô—ÿSLþ§sÜ÷=ÏÃÞ~?÷0Þ’ÿêiƒÿÔî{žç¸"t£ñcü9:çÔ ·ÒžzÔéÇRº•œe–«‘°lÓ†Wbx„°SÉW"A³¹X¢w ÀŸ{žç¸ ég ÏI½2õeÔ¿\k'â8·©®©áõX>1Ô|s3f<ÀpÜ·þmQ‡axÅmE.K%Oé<ªXX-•˜sÜ÷”r+€G=ÏsÜÉÏsÜ÷ Vÿ/B=ÎØ—M:ãëW¤ê><¸¿Oó_Qòv]Ç)b¬j y¨q|FžxÖX] ¤poÏsÜ÷y?ÿÂÝŸ‰A[èëOOðŹî{žäcøºþ#·â=Ðsÿ‹{§ÿùöç¹î{û~_÷1ÞƒÿêÞéÿþ}¹î{žæµÀý+ú?üZcõ©é¯ñ|ôµO诩2×õOÑ–Õ| +Ñ`c8àk‡Öb¸UFeÅðù«Ee-%EC'Ê«Ç$1F¢8ǹî{†ëñÍŸ‡]ºÅÓ_]Þ…ÿŸNþ–þ½-&ÕZ¾«ä†Êù÷+y±ÇU—³.‰I%]!…m¡D°NÂ7‡Ü÷=Ë‘ÃÿOÃ%((SüHúSŠ$1 J¢Ÿ«9y*#E˜Ô¬ˆZåT»4$÷ç¹î{“?áÝ¿ ¯û˜ïAÿõotÿÿ>Ü÷=Ïs±øºþ'¿â=Ðqÿ‹{§ÿùöç¹î{‡î ÁG3Y%ëç¹î{QT$¢àÜ{yî{žäŽ{žç¹‚¡ö!>Î{žç¹@ß𛳻ЇW›Û×î¶ýl%ç¹î{›sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ía?¬â¹×?às›ÚO)p\ñÕ×2^Öó2=ó?=Ïsܰn‰z‘‹5IL°I¼_¿=Ïsܳ<Ÿ.'G¯¼ ëÏsÜ÷²5£½þŸ«žç¹îkÿøW?™ø­ÿŸÛÔŒþ]7˜sÜ÷=Íyî{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žæ»¿ð§:ïå‡IÄïoåÝmé÷öy9dýœ÷=ÏpÀt_ÕY¢zdZàûÈýëÞü÷=ÏrÒ2c\^ŽCîÜÞç¹î Û½Îü÷=Ïs^N¨àØgÿ…/z{Ãó IPGé6:ÑVSÃU‘z› ²ue¸ lmãÏsÜ÷/ƒüÔt·þÝ®_ÿÇ6ÿRyî{žç¿ÍGKíÚåÿüsaßõ'žç¹î{üÔt·þÝ®_ÿÇ6ÿRyî{žç¿ÍGKíÚåÿüsaßõ'žç¹î{üÔt·þÝ®_ÿÇ6ÿRyî{žç¿ÍGKíÚåÿüsaßõ'žç¹î{üÔt·þÝ®_ÿÇ6ÿRyî{žáüUúgÓzÂóñ#®¡éöE[EÐ>°ËGYA±KO1wGGHVRƒÏsÜ÷*?ð²õI?¢D¹Sç€l¤ý8 )»±¤Éøu=­ÿÏsÜ÷?žu\ÑéÿðþIæmõoÐGïìÆk?§žç¹î_§«*€=*z”k÷éþqÓÿÚËóÜ÷=ÊßüúqÓÌWðzü?±S!ถ!WhÞ®¾£ ¡ši\×U ÎòDYÄž{žç¹n_棥¿öírÿþ9°ïú“ÏsÜ÷=þj:[ÿn×/ÿã›ÿ©<÷=Ïsßæ£¥¿öírÿþ9°ïú“ÏsÜ÷=þj:[ÿn×/ÿã›ÿ©<÷=Ïsßæ£¥¿öírÿþ9°ïú“ÏsÜ÷=þj:[ÿn×/ÿã›ÿ©<÷=Ïsßæ£¥¿öírÿþ9°ïú“ÏsÜ÷5gü@ó>[è/ü( ÓF5—ðz,±I_郂xh© £ŽI&ê4‡s, €µ¢çÙÏsÜ÷.G¢5ü¶Ú¡'™m7_žç¹îX¾ZÄ…u$R¾à<÷=ÏqS3ìBÞ_žç¹îkøzSô·Ö¾”zþÎ]bôÙ:³›SÕ¯^(âÍ9›'eÜwZ8qªI#€T┓Ê#F‘ЦëM†§žç¹î]ÿü7· _ûîÿê´Éùíç¹î{žÿ†öô ÿp=ÑÿýV™/ÿ=¼÷=ÏsßðÞÞîº?ÿªÓ%ÿç·žç¹î{þÛÐ/ýÀ÷GÿõZd¿üöóÜ÷=ÏÃ{zÿ¸èÿþ«L—ÿžÞ{žç¹ïøoo@¿÷ÝÿÕi’ÿóÛÏsÜ÷=ÿ íèþà{£ÿú­2_þ{yî{žæ¾_‹ßH};úMü@ÿŒñÑN†d΋OWº°3nUË]’º8²54p­KáÔæQŠ'icnçžç¹î\ŸHýESf‡§ X}¿zýùî{žåelaq*X¤V¸p9î{žâÍÚÉ¿žç¹îk Ñ¿K¾˜ýQ~>ÿ‹í©ONyÔ5OȾŸ¥ÊTyç(eüÛ-fWªJ‡¤L~’¨@ÒˆPHc°U½ì9î{žå¿ÃNþ_÷-?Oÿú¦úuÿžŽ{žç¹ïøißÂËþå§éÿÿTßN¿óÑÏsÜ÷=ÿ ;øYÜ´ý?ÿê›é×þz9î{žç¿á§ /û–Ÿ§ÿýS}:ÿÏG=ÏsÜ÷ü4ïáeÿrÓôÿÿªo§_ùèç¹î{žÿ†ü,¿îZ~ŸÿõMôëÿ=÷=ÏsßðÓ¿…—ýËOÓÿþ©¾磞ç¹îRü(óðöô ÐïÁÖOTº)è{£ýênWÿ7ŸÕ®¢ånšd¼¿Žáß;Õ\³‡T|­~†ÓÔCçSÔI ›nGe7V ûžç¹g}/õUI˜ê +ƒï"Þ÷·žç¹îXöA͉ÒÃ2É»x÷=Ïp^ ú"ÞÁùsÜ÷=ȸ‰µ<ŸGôóÜ÷=Ê ÿ„ÛÿÕ†ukÿ ïZÿö0—žç¹î]¨œÉäßO½uÍùf½°¬É•rngIJþ(‹½5u SUª²«))"+À5÷=ÏsBï»ñküOó'¨_ÂË7z¡õUϧ¿X¸ŸQðž âÙÿ%dÑó|¹f²££¡Éµy;|TÖ¬ñÇÏT´ê•>è2DY[Ü÷=Ëšè÷ü)j—2Tto«=lôWÒÏGž¬rç]³?¥~ªaY¾—3ç Ü?Óív!&=— Ãé–†jȨå–*¹BÈU ç¹î½ÿ…õ{Ôßá1ë©ø6JÅ2¾uôÒþ‘Sõ3ÔP2ÖԌݟ±ÇÁ°¼[¡ËÕy7ðê êÒ½–®a2ºíTÛåýÏsÜ3=aÿ…)æ‰õƒèŽCômúŠÃ=?å®”Õõ÷2ÅŽbÇ8bµCË4ãÿ$ðL¥SE;áñUjªz4¨*âE¹î{žæØX}}6)AC‰Ñ3=# SÒ;Ç$Nb™ŠY&Uu6"êÀØ€yî{žæ ßð©ÌZ\¬_„+–8uD£ Ý–p”ý¼÷=Ïr7¡® bF%œµÊx“ìç¹î{›Btf­êpj7s{ #îç¹î{†} ?Aþ<÷=Ïs_ÏÂßñSÿ…ŸüÿÙs?=ÏsÜØ7žç¹î|ßýG~.‹–Cõø‡çœ“ê³=å.ŠúVõMŽt÷ ÍuÙ'#ã=ÊYŒ½1õc%ôo®ž ÿ®Ñf„̹Å%&»Ë œVÐQ4%IjØÞ}ÊÑ€»¶ûžç¹)¿áAX õ0´ô’[Ñ7ûMIÔAÎQ áþt¾XTÿêÂË'@ß7æù¤Ûæ£óÜ÷=Êøü?á@=õ9ŸñoC=QËç¬]fÁÓª¹£=ú‘ÌY³ÉóP`Y~ªGÃ𬽃a¹~µqŠÈiÊ9Wh6¦÷mË'Ü÷=ÇN‘z…ÓEŸ‡Òá Ç}Mú‡õ9•:§ó%žsu«)ðL™ÔlÅ–©iš·§™#­­­l)©é–,&š(Eç»]¦>ç¹îmUèÏÔ?¬Kõ+OqŽ“IÕÌ,Ké–? ÐbØrM%]â¢v&x]RSyˆ€ÀsÜ÷=ÊPÿ…UÎÔß…|5*lÔýY郩ø¦8Íû9î{žånú&êF#‰Wá«-K0,Ÿ¼yî{žæÕý¯’¯¡g$ÝW_«žç¹îKþ‚ÿ¯n{žç¹¯Žk7ÿ…5ôáé7ýy°óÜ÷=Í…ùî{žæ¬ßð¢OX¶=<ú‹ü)º1è÷ªyë"'ª [ª8R2¯N°\¡æÌÄ08²œ”áðç8Z”ÏÏÔ ,jC¶âl¶÷=Ïp³þÿŽO«<'Ò/GúoÕšzoZ?ˆ']ýFç>‹ôÓ&æ ©zmýT¤Ë´8md‘çZü/š›æé>lÉþM0•$XÄ,RŽ{žç¸grŸü(ß8õ¿-ztÈœýÇ™½bu~³«4½DécÏôøRÊŸæf†\G0æH°ŠŸæMW(£RÆ¡˜ù¬ª—sÜ÷+›Õ'ü*'5tŸ¬Þƒ=aäÌ—˜ñ¿K^£z˜qêÿGM[ƒP-OQ(zäÙ¯¨Ãgž*jIp‰‚ÔÅ Y#D o;}Ïsܱޯ~49¯Óo¬.´æŽ±eÌ[éÿKýa]t¬è®SϘhÈ9ƒÏø6^¡‡ ­3ÄgšfÅ"‹ù¡¬ùa 3 2m'=ÏsÜ8„×ã¿~ u;¥ýJô­UÐls'eœ 8eí‡b‹ʸîŒùBz!]å̺Ñâ|•E2"I¤³Äæ5ÜÞç¹îïÅ‹þÍeø–ò?õ“ÿeÖ/ÏsÜ÷4‰ü2z“‰GÑ¿OØ`¨o*‹)ex.tX°jhÇä9î{žå­~(x¼Ø—§O@fG-oU½:ÿÞæ«žç¹îlïêÍ¿éÓ}Hÿàœ5ÿÄv«úyî{žá%ü?ìÍ¿‡·þú?ü®ªç¹î{–§Ÿ1zÌ¿‘³ž=‡2®!‚a8e:ïA55“!e=Æå÷=ÏsD_Ã3þ ø›z„õøyeî±çl¥ôÓÕÖuÆòŸQpük¦¹Ã¡ÃæSÿ Έ \[© ¬TpÆJͶV.}ÏsܾN‡ÿ†½3u«],À«ú1ºké·Ôf-Ô Ó¬<_ù™W:b]3‡ÏÆ¿Ð(+fÄè eè²ÔÓ8»T‡ î{žà_‚ÿ‡rX²^f‹$z}ÎݬëJzÑŸ¿®°æØòíf Ô_óI‚âõ•îô^!SU@ñ6$ËdkæD„ÜMþç¹î_Kð£ìkÔçáÕê;àØF9AêÒIpÜÛêSÔ¥WÊõ¹_ Ìø­sAOG…åŒO0a5x‹Ž†í 81¹3“ó}ÏsÜ0ý_ÿ…:z\ô¹UÓ®õ%f.®fœµÓ~–ç?Qù抿§¹Fª‡üãeÜ/¥NZÇóu˜µiƒŠ®¦‡ 3Šh¤ÊÈ®ëî{žæÍYo1`¹¿.à9³-â ‹eÜÑEKˆà¬a„u4U°-L¨p§kÆá…À:óÜ÷=Í"áA¹†£,þ4^“ñ i R?§êø‹m?WŸù—žç¹îOBùæ·þVe˜µöx“ÏsÜ÷6JéCM„Ò³‹ü9î{žà¥^mNÇ·»ßžç¹îPWü'hßÓÿ¯³íõwׯü»PsÜ÷=Íyî{žçÏ¿ÿ… þ(YÔoV))3ÆP›¦}6õ3[Ò?êÞaéuf‘©ò’æ Œ=k1ž¡CˆSÒÐTÅ¢˜]|¿¦ÕACî{žæÅ9ßñøèIõ%šúQQÐŒóŠúé×XðïOýMõNp•°N­âI-°§Ãf¯LVZjy!xªkRŸËFS`Á£2{žç¹îŽ~?먞—ô»èFy˽ëÿQs7I½=z¾­8elןr¤qµUáÔõïŠÒÓT<©%LÔÁev…Û/—î{žåwþ¿ð¢ÜÛë—£uÝêSijŸ¬<‹Óþ¢õ®]YÀð|½…dü£‚àõ±aŒ0ìG¤©Äj<¶¢*XÙ ÌäAæ´~ç¹î-p_øS/G:é_Ñ®?ÕÌ+õ×N¿ôó1u34bDtë¤Ô°LÙŒåáPhó.k¨¤lB¡ð™â¢Âèk*g©0±[;*sÜ÷6OôÉê#§­}>twÔ¿HªjªzmÖü¿‡f,¢+ Jjøi±ß/WrJ©QŠeWu¤+0³sÜ÷5xÿ…Gcseζ~ØÔcšƒ8uI‘Á±²ÆüÍÏsÜ÷&ú$êŽ#‹Ï†‰jY®Rþ÷=ÏsÜÙ££ÕÏWƒÒ;Ýút¿=ÏsÜçÿ ~ƒÏsÜ÷5÷ô6Å¿¯Æ€“øÁúxÿØn¿žç¹îl%ÏsÜ÷5ün¿¿^~ÿ …é?Òöq‡,ä{ ã©tô}'­êž9S˜¡Ìy£1|®SO4’Å„À’Nnjì6ç¹î{‚‡A?áD˜ÓzDôõ ª=`õG¤yìýxÉ="Ä0 \ +tã$æ,[ªÆª*3(‘üã&â\6 $”U$Ѐ¤F­î{žá‡ê×ü(³ÓVZÃò¶=ÐnõÕ&[‡£ôyë†+—×.á Óî™×ÖAB*k£ÌX'ÍbI7éh©YÙTT3/¹î{„Ë;“0N~%™×¡¸ÆˆõÓ ]rÊ]®ô%Óü­„àøn?bÝWÀ0Ìmjk±\É]†ÒÁLéˆÇaQ*•iZÁÙ}ÏsÜ2™ÿñÐÊ“zµø¯Vú“Ʊ¼å”}æ>ˆå~œôWʹk¨¢ÌNÀ1\Cä)óFc’,N74=EUl+M-°M¸sÜ÷=Ëü1¿þŒ~'™G¬ØMòÍVDÏžóyk«*ls+fºªjiÚ¦–³ DzN#Šax• BÇ"ÇQO9£}6íf÷=ÏpŸÿ£¿ìŹ¿ñYìâÊœ÷=Ïr˜=õÅqL9d«gÞÉݾ<÷=ÏsjÏM¸Ô¸† @îå‹*ûßW=ÏsÜ<@ÿ¢7üDsÜ÷=ΟûÍ'Ñý<÷=Ïrƒá6ßõa}YÿÂùÖ¿ý‹åç¹î{—Ñ™²Þ œ²ÞaÊšq\¹š¨jðÜÁ…»:%M u;ÒÏ4L¬ÆåIRº{žç¹Y>ž¿_§ҷU²Ç\:èÃ,䎪ä©l£œ^«Æ'Ã*&‡Ï¦¯¬Š9‚¹ * u½ÔƒÏsÜ÷ü!? ®‚õ‡2õߥ~“²þ[ê.h§Ç©*e–§Å0J LÒ’EŒA„àX½mV…Å]¯BPRB²FÌŒ 1Sî{žâw¥Ÿ‚çá‹Ñ|'¬X'N})a˜6ל±[’º“N9›qf—'âUWU‚P>1ŠÕ>‡Ë+—jl<Ák·Þç¹î=c_ƒïṘ(±Ì;ô¹…ÿ/Í=Ë}*Í4Tø¾g¢‹È9Cùòl&µhñ(…BÑÿ*¥Í.é­!žç¹îAê¿à×øiu¿ªs¬Môµ‡f eª,µ‡GˆÇŽfÌ:‹£ÉÑA 3†áx­5/ò1ÓDœB ÈTU¾Ð=ÏsܳdEUB"€sÜ÷=Í7¿áXzuð”#¸ÍÝOÿØwç¹î{ç MVjsÜ÷=Ͳú!ÿ$Z/ø‚ÿ{žç¸cêÞvúñç¹î{šþ~?öu/øPþÿ캟žç¹îlÏsÜ÷*O5þŸ„Ž{ë6lõýeŒáÕœ÷˜ñ,Ûœó&)Y˜±1<ÁŒb2âµµU4xŒ”RyÕ;´M—s`¶Óžç¹î Yûðšü;úŸêNÕÎyôÃã½z¤Å0\r|Ðkñê|6»Ë‘p¼N¿¤¯‹­­¤F+ MM$’ Ð7=ÏsÜäß„ïáäþª­#颯:3Oõ¸búáÖµŒ Ç-xÁ¿›\o5ÿ%óýÿ3½ÏsÜ÷!åÂ?ðêÉSt~¯(údÃ2Ö!ÐLo3f.’â´X¶e§­Â1\åå nE¨‹Ír©43ÄVë³k0>ç¹î"ó—à›ø_gÎôW¡Xÿ¥Z(:kéÚ,ÉMÑœ7 ̹ÓÄ0J,ß]S‰cTiŠàXÅ#5-tõ“<ÔÓÔOO¥j?I9/ôK+æDÎ9kÁêñÌÃób@”£¦Ç0zêlZ*ï*$ˆÔ%Xƪ„•sÜ÷ çÁ—ðÉÏ=é§\sÒ~KÒž‚K‹MÒL; Åó>‹àï«®+³À±: ÏEž‚j3•w¥N‹/M±\ÿO†ÑæÌz«0f¬Ñ‰TÐ`Ðü½ ÖfìO¨Š–,±SÄë€,º {žç¸Çø±Ù¬¿ÏþGþ²ìºÅùî{žæ„Ÿ†{°é¯D…ÿç›ËÖÿÇ]?=Ïsܺįþ­ÃÐ'ÿ%_AÿòóUÏsÜ÷6õiÿV—êCÿ ãÿ°íW=ÏsÜ%€Gý™·ðöÿÂGÿ•Õ\÷=ÏrÚ±¼"0`¸¾ˆ«6ÒÔQת6Ç0ÔÄиVŽÖ6<÷=Ïr=<ÿÂuÿ ßMÙû¢9ó*áÿ9§¦ütfž‡ä ÓÔ,ÃŒåL·™’©k£Åhðv–*TªIÐLeŒ€3 î{žà•Ñ¿À·Ð/D:뗺ݕòöjÆðþžâ¿è¿@ñÌÕ_Šô×!b¹ö/#ªÀ0 ÀŠ«cº²HòFºUeB¾ç¹î'ºAøzè¾?Œâù~“=f<§žòGHr?±\cé–YêLu°æ<£ISþðüâb«I#Jà9³joî{žâ{ ÿ„îþys/Ôe|—’3/Oðls¦#Ïô˜c— \Ù”jqY1²qµ§„-ezÕ2H+y·Ž0X„žç¹î)º“ø úêP²ŸR°ì©Ý$Çp\§“2Vuƒ#uʰç\ PÒá˜Xóá»j%–ššŽ8DÔÒÁ!Q«^Ä{žç¹st4tøu&H†:J£†–2ï!Xâ@Š HY˜€;’Iñç¹î{š&ÂIŒ_¤²;ÿ˜Z¿ýq{žç¸pÿ¶,p›ÿ©û9î{žæÑ)ÿ’M/üE‡=ÏsÜñ/÷•ÿâ?°óÜ÷=Ê ÿ„ìÕ¾úúÿä»ë×þ]¨9î{žæÀü÷=Ïr†1øNᣙ³îxÎY¿êmËÝMÏ5Eê/Eë:‰˜—!㙦ººLBJšÌŽX –Ï!P»BJÜsÜ÷¼Ùø zÎ~§kýNc8foã¹ÿê¾lè·ƒ¦ÏSð˜dŠ—4Öåô÷$ÄTÈÎÄH#‘‹y‘°g î{žçº_ø zé©L¹êS*a™¾IòmÌ9û¥] ­Í¸_Mr†wÍ0¤ž7„`h᪘F †wŽ6£D*…}ÏsÜBtwþëøpzŸ£¸ŸGrVcÈ—£XVwÀ¨sŽ˜eƒÌXŠx1: Á:Ãþü¡HêeJa0&ÞáP¯¹î{óWü'ÛÐV5ý:䬣Šõ'£8‡¦L±ŒdœƒÔŒ™ž«pLÏW“qÜb³«Á±*á ¢”Õ×Í*YD,v¸ç¹î{—Òþœežôç#ô³&|ñÊ>Âè°Œ¼Øž'ˆãX‹RPÀ´ñµU~-5EULÌòM4Œî×,I<÷=ÏsQøV+Ô_ÂPÍÝO·þ£¸?=ÏsÜ=ÈÆ« ìSžç¹îm—Ñ’Ñ é±‡=ÏsÜ1Õ?ï;}øóÜ÷=Í}= Ùþ?ü=<ÿì7_ÏsÜ÷6ç¹î{•Kë?ðpôë£Ô]õCÕ¬s¨Y?¬Ù_&ǰÌÓ‘³Î/“Üå”ı,U©d8IGq,¸¬â[µHR÷=ÏpÍÿðŸßéÏGºkÓÜ:z{ èÖUÌ™ ÌÙ:bØ&9Žäœã]SŠcx;[QóoˆÑWTVÏ$‰.£ÌeFT;yî{žâƒ­¿€¯áóÖhºEC€g‡àù'§´=&ư>Ÿg,[-Sfþ™aµPÖSå¼Å´Ï%} –Îìë<‡W•ˆ]¾ç¹îuÖoÀ#ðÍë¾?ÔìÉž:3U+Ô¼«‘2m=NŠKG.VÁ:oO¦]™Q¦ÃdŽ.exžòÆ =Ô‘ÏsÜ÷Aüý õW õaEÔ<77f¬wÖeGN1.°gÚŒÓX¸üyƒ¥Xtøn_ưڸQ -|KS#M R²³6õ*JŸsÜ÷ Ï£OD½3ôE’ónPéæ{Ï}L«Ï˜¼˜ÞoÎÝ@ÍøžmÆjë]JªÊSÓDŠ,±RÁøN¼÷=Ïr±¿áQßöb\ßø¬¿öqeN{žç¹®g¢)\âø]Û»'ñç¹î{›úW'ù¯tKýÜ÷=ÏrÁûÂßñüyî{žçx½<ƒáû=ÏsÜÓðÒüM±ÿá^ôí×ÏÂÿÕÞl̃«½OÌ´yÏ(ôRjü·W…fÍ5e$UãX®$›£‰m ‚¬Àßžç¹îœ[þUéOvü<}^à²'ÛJ¾’åºb>‘6m^{žç¸˜oøW¢6CþªûM2ˆ?žqç¹î{œå®?Cß÷>ªõZåýÌyî{žç¿å®?Cß÷>ªõZåýÌyî{žç¿å®?Cß÷>ªõZåýÌyî{žç¿å®?Cß÷>ªõZåýÌyî{žç¿å®?Cß÷>ªõZåýÌyî{žå~/?Š÷JÿެúÃú 鯬ý0¥èaÎuùïêNÃpJƒÁhéi¾^lÅÁ!èØ?›åVʼn {žç¹d‚pZ˜*p¢ñ•±O sÜ÷=Í­º%\Žà‚o÷sÜ÷=ÃP/üyî{žæ ý4õó~¿Š/ãŒõËÒ·\óæWõœºmˆôË9ä.™b™—ª¥ËùhªØÖ4´‘KR|¶qpÀE¹î{žá«ÄÿáS„ðVdÆ=2ú’ÂY~ÒÔô—È#éóqeç¹î{‰¶ÿ…dþˆv¿Cý@!ö™Òƒùã<÷=Ïsüµ™øqÛ’ëÿþ«J_üýsÜ÷=ÏËYŸ‡ý¹.¿ÿê´¥ÿÏ×=ÏsÜ÷üµ™øqÛ’ëÿþ«J_üýsÜ÷=ÏËYŸ‡ý¹.¿ÿê´¥ÿÏ×=ÏsÜ÷üµ™øqÛ’ëÿþ«J_üýsÜ÷=ʈüm?JŸ‰G¢Ê?Lžžz;Ö?¨˜Ž~É8Üu™“".…% Šyõê)± ¶VÚÞèÙoi÷=ÏpDô+€ÕAˆa{â+fOðç¹î{›rúv“ÃÃEþ÷=ÏpáÈ¿Õÿ2óÜ÷=ÍR½zzÃ蟠Çï ^¢ýGÕãxIfôǘ2Ð̘>[Ç3!\cêVACÓTÈ»£¥v,E†—î9î{žá¦ŸþøWCK]Tò²%M$2 ®=äb9î{žáü—þ3ø7@H›­Ù¶;†é‡Pøá\÷=Ïr?üµGø2ÛöÍ_ú¬ó÷þz¹î{žç¿åª?Á“þß¾jÿÕgŸ¿óÕÏsÜ÷=ÿ-Qþ ŸöýóWþ«<ýÿž®{žç¹ïùjðdÿ·ïš¿õYçïüõsÜ÷=ÏËTƒ'ý¿|ÕÿªÏ?竞ç¹î{þZ£ü?íûæ¯ýVyûÿ=\÷=ÏsYÅ+ñôÇø–~(¾Ÿz¯é/4b™×"ä.ÖeìÍ‹b9{Ëí*3uN%å,xÕ53¸òjQ·(#[^àóÜ÷=Ë¥ôA<_ʉS¦Í-ôsÜ÷=Í :T–Â)ú«ÏsÜ÷œD^•Çú¿°óÜ÷=Í/? Æ·ðÊü?rׯ„úºõ/þiz«Wê­¸õ>Vþ¦õÝ„âìÔóüÎYÀña½éd »ÆÛ•‹ûžç¹jÿòÔwàQÿqÍÿ®Ë¬_ûŠsÜ÷=ÏËQßGýÇ7þ».±î)ÏsÜ÷=ÿ-G~÷ßúìºÅÿ¸§=ÏsÜ÷üµøÜsë²ëþâœ÷=ÏsßòÔwàQÿqÍÿ®Ë¬_ûŠsÜ÷=ÏËQßGýÇ7þ».±î)ÏsÜ÷=ÿ-G~÷ßúìºÅÿ¸§=ÏsÜ×GñÙüU}þ%]_ü5r÷¢¾¼ÿž|W¥y›?Ugê_ê¶tË¿!-€ÐEHÛ³^†,¾cRJ-r»}à./î{žáÖô †ÏNY±N{žç¹µ÷DPŒŠú‹qõsÜ÷=ÃQ­9Ð=ÏsÜÕ úëô‡è{ñéü[qOV}|Àz‡õ%t “WŽM4I‰M†åŠ™*R&).bZˆË^ßhsÜ÷=ËKÿ‡÷üîa=?ÿÆÊïüåç¹î{žÿ‡÷üîa=?ÿÆÊïüåç¹î{žÿ‡÷üîa=?ÿÆÊïüåç¹î{žÿ‡÷üîa=?ÿÆÊïüåç¹î{žÿ‡÷üîa=?ÿÆÊïüåç¹î{žÿ‡÷üîa=?ÿÆÊïüåç¹î{žÿ‡÷üîa=?ÿÆÊïüåç¹î{”Ãÿ ü^ /T?„«¾…ú~õ“ú«ÕÜóýAþ©d,*¦ªJúÿåO˘ÅW”²@Š|ªZIekŸ²§žç¹îWï¢\&¢<[ ºï'ñç¹î{›†zY…“ÃC ‰ü9î{žå€þ„ÃýQüyî{žæjÄß hç¹î{„“¯ùL ¬‰b/½Xvç¹î{šþuÿÒ5f`Äk$ZûËXíöóÜ÷=ÂG[èCyœÿ*csþý÷=Ïrûâ_ôËoùÿG=ÏsÜ÷ûâ_ôËoùÿG=ÏsÜ÷ûâ_ôËoùÿG=ÏsÜ÷ûâ_ôËoùÿG=ÏsÜìz ćüâ˜ÿÀg=ÏsÜYå_C8…-l21€ /îèç¹î{–çésÓ­NW–„½M…tÛn{žç¹y]7Á› é£+·j{žç¸-ʤÄG´Ïžç¹î^¶å'Æðʸ–2ûÕ´·´sÜ÷=ÊõéV¯1×Ö:Й7–×o=ÏsܯSЖ#-CŸåLnÁýœ÷=Ïq£ý‚ñ#ÿ8¶ÿôsÜ÷=ϰ^%ÿL¶ÿôsÜ÷=ϰ^%ÿL¶ÿôsÜ÷=ϰ^%ÿL¶ÿôsÜ÷=ÎÇ ¼Hù%1ÿ€?ÑÏsÜ÷X¡\B ¨Ÿù[ ¤~çÇèç¹î{–ƒé“Ó%VYª¢f¢(¯îÛ·=Ïsܾž’å×ÂpÊX™6ìQáð·=ÏsÜ0;O—o×ÙÏsÜ÷ ‡XðÅpª¸ÕwnVþ÷=Ïs^_U^œkó]s%0µ½Þ{žç¹S¹‡ÑF-5\­ü±½æ?¸}¼÷=Ïq4}c7Ó p?âžç¹îuþÄÏý3þ@<÷=ÏsßìAŒÿÓ1ÿäÏsÜ÷=þÄÏý3þ@<÷=ÏsßìAŒÿÓ1¿äÏsÜ÷(=bé:åmÜ~áç¹î{‡ËÓŸ¥\KÄèdzM¬½×ÙÏsÜ÷ÅÇ(K—=ˆ±v‘ò¶¿ü@óÜ÷=È¿ìAŒÿÓ1ÿäÏsÜ÷=þÄÏý3þ@<÷=ÏsßìAŒÿÓ1ÿäÏsÜ÷=þÄÏý3þ@<÷=Ïs’ú!Æ|-üç¹î{‚†AôcŠÑâ4ò5€ 5Ø}¼÷=Ïró=$ôB¯-/2S ·÷}œ÷=Ïró2ÔXuÿnó ÿÇe/ýKç¹î{žÿ†çÀÿíÞa§þý”¿õ/žç¹î;á‡V][§8cu ¤?Æ>{žç¹jž˜½1b¡w¡)°©ÕO·žç¹îlaÐ|­& …QÆÉ³j¯…¹î{žá¹ ~Y—Çhç¹î{’™w sÜ÷=Ć7—`Ä‘ÕÐ0a¨#žç¹îøßDðœNGg¤V-þ¨<÷=Ïq þ0;ÿÉ9?äÏsÜ÷8³¦ÿLèÿäÏsÜ÷=þΘý3£ÿG=ÏsÜ®Åç,fOO†—¬n´ô¯›$õ§Y>§Êy®ŒFµT5iWbXÌŠÃpF óÜ÷=Â)™¿›z\ü=>úÕÆªpÏSÝt“¦]lå’i1ê/ææüé‚áTõ5Ëa±VÍH«S<ÒL>_s:ùJ˜î{žá_¦üaºÌ½<§Êø§¡Š\#Ôžnë‡Nº)Òlk=AÉý1Ì5}G¤¯«¥ÅÒ£=å\Š:6ÃÌ5tÒQ IרÁO¹î{…ëÒOâiê$d Ì£ž²f\êϨßP^³º£Ñì&tÎá"`Ù_/åŒJL9ñº*Y¥§‚|Q¢¤qK¹ÁgØM£>ç¹î#þ/›1.‡úMÌ*ô‘„ÅÖ_V¹ÃªØUÂkqíš2ö …t­i%š¦Húu•ñ¬cž¸U…Ž:*&X¬e•„^÷=ÏsÜ;<üqýTõ«7~è— ZL×?]ÙK9f S)õ'3ã9f›$Ë‘qšºZ÷›nÕ”•4X|•4’$HÎe‰Hqç¹îmEŹî{žâkÁbÄ•Òû»ƒÏsÜ÷ £˜V*ìÒR+nÿTsÜ÷=Ä,žp2×8z{û£žç¹îcÿgLþ™ÑÿÈ#žç¹î{ý0?úgGÿ Ž{žç¹J~˜z¡Õ~¨þ5Ÿ‰¢LÕ‰QVô3ÓTé®-Òܹ‡ÁUEW™²® ‹V´µ‘D³Îjé9 …€÷=Ïp±gÿÇCÑ÷Oýnf/IX‡G«jr®Kê?GsVÆgʰât¹¾JæÂjjaÊ’OüΣ£«FŠ¢½4R4Fbª}ÏsÜsïâ©Ô^±z·ôyÓßM=Ä:qé“<úœ¯è~të~2ùk‹=6^’ŠVš—u’» _ôÔ–ž~ò! ”tdç¹î =üoºÔ¼{ ¸Æ?é+6ôß Þ¨1>«¯B:óY‹åÚÚ\[/t{›DZ*Œ.ŽC[I,b5O–‘5Þ Höm¾ç¹îgô±ÿ 7ô³ÖΪCþÏX®R|ŸÒ~¢õs¦ôtù¿'æüs é¶_WaX­. <’`x¤ô˜{Í ;Ô¦¥Æ€ûžç¹°á¯êã6zéôëú•Äý6cœ:}ÔE[ÑzL{ÁqlK2åê¬*š¯ù¹ƒi Q,°Ãì%‘#óŠ*H—÷=Ïrì-n{žç¸šÆ°„Ä"teÝ}ç¹î{…Ï6ôK ÆÞF–‘_y7÷Aç¹î{G¥¼G,ØrOmƒžç¹îFÿe|þ™‰ÿ {žç¹NiϽEÈw£¯A-6ëKóFnÎ4’áÑɉ¾)„&8`0Õ– cù|wP5×ÛÏsÜ÷¿Ä ñYôwø~z†Ì¾3—Lqn¡fŽ™`Ffëm9‘°6°Ünx⥧Ã(óf3†Õcx—4s½ në ‰ Ú²ýÏsÜ&ÿ‰×âé†t§¦ž¥h½ ôwÏ8ϧJ“â9ûÔÕmQ’0?ó«67ƒRËC[] tß;‡<в¤'Ëœleßç¹î{†÷6þ*^œr§\³¿K¿Ù{?c}=ÉxÂ}7·^éªãŸªµÕÓÔáæ–§й"¦òÉçY]#o²Å½ÏsÜaÈŒ¡|÷ë3ôŸ…äWRë.%%ŠI/ºC„÷=Ïrà V¹7ñƒ>g>•úfÎ]7èöB¬­Á“«y²ü48¾dÂñ¥«Ã¨iðêúª‡Äc•§dXýý—Þ¬9î{žâ{ñîËàü?$«¾.Ÿf·žç¹îl^ѾóÜ÷=À×4äÚ|f9Xƒ†øsÜ÷=ÂÛútÂ19Þ…±ÿ=ÏsÜG7¥|çýö¡øì÷=Ïsû+à?ôÌOùsÜ÷=ϲ¾ÿLÄÿ=ÏsÜ­ÏÅçÍÞŽÿ/T>¥:;%6Ô®•áXU^UÅêh)kà†Z¼É‡a’§¬I"{ÅRâ̦׿qÏsÜ÷¿Pž«ºaè‡ðÓôµëK¯™N\ýšúñ„ôÛ Á° )ðœ [8çL®1Öóë1†“¤T§©žY˜lµ¹î{žáCÉŒ·§Ž¨ô‹#c]/ôË‹uÔWPzÍKÑ µéÿ Í9Z¯­Ìµt‹]#Mšéä“› ’7 Hsþèˆy¼÷=ÏpôOø¦õ;é—G0>¦úwÅýEz¨õG×~¬äŽtŸ)å90¼»Ó\;ÅqêëjZª¢UUi·~”ûžç¸igÿ…zbȾŸ}-u›(ú{ı<Í꙳õNRÈyƒ9e,•A…`½>Å&ªj± sR¤Õ²BVŠ™šG +lßî{žáëèGãí“ýNuƒÑÇD}(z?ÎeÇý]tÚƒ©ðâ“ãÙk/Ñdü½Qñnœæ'Å䯕÷'Ÿ«”ü¿˜ÕGH^AosÜ÷6)uܤsÜ÷=ÄdÊðbÑH’ upyî{žákÌžŸpœZWw¢V-ÚëÏsÜ÷éc-s†¡'ýAÏsÜ÷8²¾ÿLÄÿ=ÏsÜ÷û+à?ôÌOùsÜ÷=Êü2ú¯=kuƒñ.éïQ2NZÀ°¯EÝnÌÝ3éåNEˆÓT×`ø&)_C ø›W×Ö,•L´ª]¡X’lƒ@=ÏsÜ º'ø³þ=uë¾Ñ¶™«-á™â³5Ðt¿®ØÞ‡QdLÓU’éd­ÄÖ†²FzÈÕb…Ú'¬£%¶Ô%½Þ{žç¸VrWãÒn®ú³è‘zA‹ôïÑ{é¿Vº‡ºÏŸ2•fŒbx/LpÜ^ºl[-¾ŒUC.eYé|û¡r“î{žá˜é¯ãøzæl:¿ͽ?êoBèhzUˆõ¦’\ë“ipÁˆôú“L“ l?®Z¡‰U8ŽˆDX;\1B-ÏsÜ÷^—þ;?‡¤=õÖÌg&õ§úYÁ²^dÏÝ6Ær晚·-çÌ{˘V'†E†ãU”“Có8¥:ίU°î³Æç¹î{—÷é?­øg©NŠå~µ`]0Î=%ËÙÅë[,åœõƒE—óFMY%->"ØzTÔ¼4Õɨ¥óŠJк3F…­ÏsÜ÷ ¹[‹}Ü÷=Ïqàbq:: 5÷=Ïp¶fžƒáXÌŽÒQ«o¿îûyî{žàe7¥œ رÃP“þ ç¹î{˜¿Ù_ÿ¦bÈžç¹îÄïǽ%zõMêG¦4TTùû¤^|_+O]F•”‹UL1,,T:ÚC¥ùî{žàz™éO¦ŸÂGÓ×â;ꓟ›?tÛ¥Øþ?€åúJ8jqŒÓŸ06§å裭žžžÚ¢±äc$¡c‰XûÅBŸsÜ÷ ¦Wüi½ ç.›bõÙ[ ™‹8úƒ£êŽJééß.æ ƒ™Æ7šú…O]UI‡æl¿ŒÕà³ÐÔ 6¢#P•dɰ­Žþ{žç¸zRüSs5Ó¬qºÏéÃê·¨.«ú¥êD½8ú|Ë”ÙgÆipÌ©àÃÓâÕxž%µT?ÍŒsIó%È÷Â){žç¸2fOÆÇÑV Ñ>ˆucèÖaŸ2z„Íó.tç§™‹Èy8W§/N1jì[ÍXÕ&‡Âß4‰L’ϾiO–«»žç¹î~˜~2žœú‰'¢Ì+Ó7¥þ¢u÷>úäÀ³3Ò|…‡G•ðʬ:LTaÝ./6)ŠG 1¤53ùŠÎ.ÒLˆ¹î{›HàXdHˆB=ÏsÜWí÷vóÜ÷=Î|÷=Ïs¢î9î{žçãžç¹î=iëoF=9t÷êÇ^:™‚t“§u±lábØu0‰å#T2™fqypÆG"ȬtãlxÎ.!.# âP€d¢/˜¾6¶¶íãÆ¨ñÌlJl,NÅ Ë@$_4ݾ¶¶¶íãÈ)‰áÒVI‡%lm]¼”¡Æð>xû9SWñ”¦¬Í ›ú…é7t§ÑÖl«’¤þ©1ª”ÃÿšÉ¬QµvŠÑÑAEóµ °ÑS&#=tŒÅ$¤†Hª#…ËŽ¼Ë%迬/J¾¡qÜK(tw®ùg:çܨÌ=3ƒ¦‡4á°•}f Tb¯§PNÒÒB 0+{‚¹î{‚‡Wz7Ó½ôÓ8ôw¬Y2¨=1ê aùÓ%×£=#Fî®ÑJ#*J’ èGn{žç¸F²WàÛø^të%uk§9/Ñ>HÀ²?]pºl«Yi(&’›è«S¦Ža<ÎU ©'†Hʼr*º0e{žç¹Ãüÿ <+¡Y“Óm£œ¬Î9‚ƒ5fŒÎ'5}~bÂÄ©G_6/5[bM5:O*DMO¸’H‹e‘Ã{žç¸ú§üúÔ®‚å~…zG§È>²Þ Ô³Õ Å”ñ.äþªäœÃŽÏ†Ç…U5n]ÍoÃ$AK5Lˆ «X{žç¸Ýé¿ðôGÓD]+ôaê7)Òz¿¤é®?˜sbu/ |·^3hªùŒF|12õ\sa´ò¢Ç¦Š¥••y{ {žç¹`8ô èϤ‡¡y¯¥Þœ²ÆBÌ>™ðLS.ô#Âè~QòÖ ™[§¥0SómQ#Ï#†yÙÙ‹1'Ü÷=Ã{ÏsÜ÷: ãžç¹îq1©ðç¹î{œ|˜ÿÃùq,¡•KÏ}«}M»ÛœK eBÀ3ßb’.m©·:, ª–šûVú›{9Y}cüY}t[Ô§_Ìã7ÅV¸nzÌ9K¨ÌØ>TÆf’‡ ÄŸ i*d¯e¨Y$¥¢§©–ž;IR°£ÆÏËœ¹ß¯OúŸÓ.«agé¾wÂó®šTK‡UÁPÐ6â›føšêF×P~÷=Ïr«ýMþŸ†ß«ŸQGõQÖ~šæjεõap”ÏY›ÏÙÇ/EX˜ G—è×å°\Bš KAª.Ac©'žç¹î 2~>£õGŽz±ÀàÏ39fìÕKžóçN0¢g  æïF…Äòî_ES% ¾\¾÷˜¾Mþç¹î"2ïàkøxå^½Óú‹Áº{˜és®ÔçëP˃;f•ʘ©(õØŽGZ´š×Š&©SoF‚ÑÆˆ=ÏsܪïM¿ðñÓï_Ý*õ!ÕO£ØOCz+˜z­Ç”ò ž)ñ~ œŒ à‰Iá™§ÄpŒ*žš6.ÐaJ°6éD<Ï1=ÏsÜµŽŽþ †º#•z§ÓÜ©Q1Ž—õG#æ~›¯J±Þ¨ç¬k-e¬‘œ#ª‹Âð>»x¨RU¬—lªT,Ì®œ·¹î{–5Ð~ˆôïÓ_F:aБáà=0èö ‡å쇂ÍWS]5.†S­-á[äͲœ©‰œÔ~ƒ’¹/’8z’ôSøUþ,=éŸUóßYs ê3"e…ÀòN^ËÙßéöi‹«ª«Ä®¸s zÉVCS)ó¢2 ³#+sÜ÷}Fü}u2ôû8Åœº±ÓÜÙ“rnsFgÊýIÆðlO;eŒ³4Saðf:ȃÔUM”ªb’)™=ÖrÛÜ÷=ÏzüßTYÿ¬¹÷¨0gì=@á? ëHÀ3Þ5…eüÁ>BH)ð‰zè/£³t/Óöˆáù£Ƴ”ø®!&'Uü˪ùʶó¥U; ý•¶ƒžç¹îUÏü(aBô+ðú·ýÅÿ@òó]ÏsÜ÷/ÿžç¹îpdVî9î{žæ#MýѯÞç¹î`ê¿J:ÓlÙÖ´g|7¦ý4Èôß5š3†)8‚–š2ë h;´’Ë#¬PÃi%‘–8Õ•Nj©ªd‚¡'zfÙPˆêÆ7ïµ€&ÇàyºZ—ž:z˜ç’•¶T¢:±ûí`¤Øü1G<4© É+ÀÛfU`ÅØÀv?O+ ‹ŸA:ÍšqÌ»›zCŸý>P­+âý>ÌùÇ Â– Ï–Ú·ù|œ4X%‰bT)$·S#GNÈߣ{J5ÏÉËËAÊ™›%gÌ—2älφg<¹[þñcøMm6#E5€>äôrIhGcÏsÜ÷T—:7ë ÝDô×׬½6géU)©©3¦M]Y†OQ%|œA*°ù"š2&¦Ctap-Øž{žç¹Xù[þÉø`dΕõ¤{¦9¦—+õ«-b??/P³­V+€ã9:zšŒÀë*±)$êé²]²@@ebŽ Sî{žà©™E¹Ë¡™G¡Ù¾«¨Ù¥²&~§ê~Rë^%Ôìé]Ôœ?–úÈ™C‡«ýé„} ɯÍ9•2:cu‹åªœVÔüÌíWU$S1iMöîÚç¹îÎ{žç¹Ä¨nü÷=Ïs§ŒßÝü9î{žç”„÷QÏsÜ÷®#øúÂ:ÿzgĺéAIÕ±P”†C‡ã/–è1,¸MVcŽ‘°x1(cME-ZÍt܃w=ÏsÜ<M†bT”؆S }b,”•°:Ë ±°º²·½KtËêÏUê:½ÕN†åî¤á8/Mó6=W™Ÿ3χb¸6—ázÜ.W‘©ž êF¦c”’[žç¹î&½6ÿÂtýzdë‹uG'æ¬Õôë ªÌõy?Ó¾'M’Î]Ã6ÓËOWb8 67SON&o’Ž\@ù#KµÛw¹î{‰>–ÿÂrý1ô:“-â9s¬Cë}oG²'VzÐŽ”uÁêòV꞉ÑÕ`x” EˆID²b³³K RNDŒ7H£OsÜ÷+kÐ×ü's­¸ïX³°õÛ“©²'¦öv¯èED¤êæ3ÔÜv¶jÌçýe¤Ä0:œ[£‹ÃpØÕ>Nˆ«ˆ¥¬$Ä{žç¹e¹Wþqé ôÉê;ÓpëWQsŽ_õ+…dì1ç˜!éÎ_Ç0l#æ33PÓa§ʰR﨩Â)þvz¨'’`»®²çÜ÷=Írî I–p .P<’ÐàtÔTrÊU¥h©aX¹EPX… ÏsÜ÷9î{žçECwç¹î{˜M›ÿ¿L=CèÖ3™éú‹ÐιàÃÌø†[ÅãY¥Ãê™*ަ”¹†BPw† ‹ßžç¹îWQÿ„ð~YS¢=]ôó3u9t¯¬> ƒÖåü{¨xö*¸e¼B,S ©À¼æ ‡ÔRMlh×iAå²´eûžç¸¨¯ü}㛣Y³¨ý^ÎøÂgü©¸^1ޤâõù÷Íùf)©ðÚ¼&¾Tùj"*‰Ôô©¸ÌY㉓Ü÷=µê›ðÊgB2ON= 䬡™1Ü®x‡[3T}eÎýX¢Ä$Ʊì*“ ÄæÂ3—O«W ¨’<>ò«P’0ó$¼Š­ÏsÜ÷=%ÿÂrºU“ýúlèwª£â”ž¤ý?æ|õ›rŸ]úAb¹V«,ÕueÂðŠÜBšj)) †'zšpí´• ssÜ÷,û¥Ÿ…O¥.õOÒYòÔy³ê'£|­˜r—Hó;›q|~¦l;4ŠæRbrâòO-\ò5T… p±5HÕP{žç¹daBöç¹î{óÜ÷=ÏsÜ÷=ÏsóÃM µ,0@¬óJÄUQrI>sDðÒÁ5ML«=:³Ï3EÉ$øÌrËI4Î"Š%-,Œl@¹$ò¹}n~&Ýô[‰a=2ª¢ÄºÙê‡;áï]ÒïK™Q`›1ã ÕpátÒTÏVñQá´õ5µPRA-\¨gšE†™'˜ˆˆMŽfLc4‘äöš‚iö´ÕrÁ$Aé$@Ë2Mq ÿøø{8 æ Ófò2þIyðéç*ÓÖËO$BJ)P2Γ\ìC»Owqð·nX¶9‰f0œ´ÒÑË.Ö–¥áxÃÓ:‚%Y/î©¿³qðör‰=Dô˦Ù23ñJÿ…bØ6dϸ=Uz8ü1²Õ}Nc˹R©ÄòÓaQa³W­eÇë 'ÎÏ$bZ ZC§”çAÓì*‰ë¥|[§"‡‘ÅNbbl‹ì¶¿½rN¤Üñ‡We6Îã&Ràø¶zÆðZfƒê=2`Ø%U,Ë¥´Ž‚iÕc$ÇË kn&ÀT2…GU¦&VÇú…ˆåúy¡Î]M ¡†\½—«© gJy¥‹æ*‚ÄÅ¡¦I¤V°arÛT¹;×gã ž²o\½õÛ¥2ôeøsôj“Á²¯¤ ïQ—SÏxGœ0ׯ¨ÇñªŠ:=Û€§Ã£Jª:Z×”F»£Ù<‰˜sfÉÄ®ƒ3AZUh&z˜áš!{!pÌ7\}²G°÷⦟3漃ŽSå¬s§ÍtÕåꣂhFë)uf®>Ù#ØÝø/Åã¹S‹ÄêâÇaª*´’´é‘‹Úî\/Þ6ö¾›þ߃gã—Òú¯VÞŠr&xôÕjVxº’°ÜKÊã‘Ø”‡Ä©àæj7‰„É…7GebÍÄî!aßÅ^Jv‘ðÊè%0OET¾QY–à {”-î›×Ó·¬?eüaå¦i_ ¯§”ÓÔPÕ¯”Vu¸1‡¹Fotؾ¸³Ãs^ˆ3ÂÒ5\R¦¥¨_,‰EÁPÚ©:¿ÃŠ®”z<ÿ…"þÙæW§ž¢«¿ŸMx%–“âYÓ Áó¡„•o:¦RbU±Ä ÃK†ãjÄ€@*ƒSNFÓ¢ÈohËÇo}Ʋ”8ªd7Û`íï`xÿófUs{! núXODÿáC(Ã7äÿÄsÒ§V? Þ¨ÑÖ-|Û‘óV/”qæ”CKUM‹`¸lÆ1#zM—eQ<£ß9Á¾£P{H7±æ`o¨ÔÇ—5éçÕ—§VXFeÌ^šúÁƒõ§-e ªjÚ0 ¤®Â௩£Ž¼RŠÄO"IãŽUóâÙác²PŽ óÜ÷=ÃÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷®«õ¥]ÈxïTzÕÔl¥8Ë(¯çŒÃ‰Ñá8](‘Äq‰*+¤Hìû™ˆU8‹Ì¹ÓÁR²’’x*ñÈcßòùw:¤ÛVÖáÜ~¿™§=a¸u´tuõ™‚‹Ã…¼¾X'@µ¬_[ˆÁÜ~¿Øîh£Â’¦žžhªqX“ttM&ËŸeü[[…îy®ÿUzÛê×ñ¦­ÌÝ6ô™s§ ?Ã''KŒÐõÃ×f$‹”sŸR sMY‡e7¤´xd&9§é®Û”ë ´ò¢hò–`Ì¢Îuç %ihb§kHÈZì#U ¢{’N¼sF-‡`Ã+â½gÍ£+âýx‡)aTpÔO‹ÔM9|¸â¥IR= Úžîë±ðê7R²ONO­ëfuL©ˆç,I(rN KOWYŒWUNѧ—5uìŽêX¬vM×b *ÝõÑÒ+à ¿ÁCðóo\] Êh­õ[êK9¤²dìɕய¦®– üb0s]}]=tTÒ»-<Õ+*ÓÇ,[Þ9•™“6dÊölz‡ùžTg" BçO ÚȬîRæö¹q¯ðà‡S›³nL®yqÚ1ŒäƑョo>x6“Gv(Ko0w¸úpYlÉà5 5|KŠå£#Ç| çIŒE‰_y]v°q{Ü^âÜBfŸ@¾‡œýG›=zçêwá¡ëû¥³Vá•6fYå˵ o5V¸7Y…âøg”häòi©æ¤ RJ”F ¯0ÜÕ‚bsš8êþW]»ðÊ…0T Ãp²Im×­Ç|/8`8µA¢Š³äñ5Û¿ ©SOR7€ÂÉ%·\n·ZÐæ*¾_–J—­ÝA00Ì7 ‹+Ú÷ú_‘püÿÿ »ü4³MÕ¬¹‚~(šð)Êã«*à¹Ç9&ƒpJz:|§TÖ8ýù£ªUbAyUeÇ·ŠkŽ××ÙÇ«o-ƒÓü(ïðåë–wĺCÕÜçˆú4ë_ŠùæWê½Ù3 ùª²@§‡ÇE*,‰ aT]´MâÌ}Îùî]ö^êWNsln§*gü3Sgië2…FŠÐÖ¦+GM³Îž©¥qW½]Lò´é†Å·ÌVeU³Èƒì¨"X uàšq++ÿ#‹ªõ¸†eÍ9¦©¥Àr Hø†/0ýrHR˜@º¶Ò¨ºØ’mÂíÔÞ¦å.—Ë–0þ¥ÍŠæÜ몉ʽ3Ëx|¸Æ?V£ŽyÅ= ,)©”î‘®¨¢ö%ˆR[ý9úóë/T36Eô;ÿ ×ôC–òg£.‡Ç#ÔV}AÃñ \¯Ža1ÔÔÂ"Ãé&©£®š|ZjcZÊéZª`^C(¾9c5Ù³!ÕœHÏ-”K\?w—$¨EU3`uÐpBÄñŒÝ‘¦\r9ÓÉ>ÄÂÜ5<ôà)Q~otÖà‚ÀêlàÁ6/Ž`!1šy>+LņT’žh¶“еW“ºènAî4 ð2Å})~ ߌ×Yó—§ÌS-Oéñé'‹G™'é&”””ØÖ1–¯Žƒ8† "Ï$zŠ˜È/RÖ­OèÕ_‡ç¼ªh©+™°jÙ¢I¢†¤§—$R.àé,lÑâCÙÅæÔ<²x¨«Ù°*ùâIႨÇåÉ‹¹]&‰ž2§þ$=ø¯£ÍØUD±ÓU3auR¢ÉS”Øñ¸ÜdBÈG×̴߇—ü)‡ðÑÇpzÏH^»›ñô÷L>g£y«Ã)s<ØlK¿ÈŠ.¤ÿ4‚š5 V‹W6*ª›´XyÑ\1nÃrê5_hâÛφá|Õ».åÔj¾ÑÅ7›ÀÞ.Fá¯qíå„ô«þ_Ó시(ºWø—zKê§áëšLt´tÝM͹'l£ŽcLRŽpeŪ!†Yyož{î.y‚ ìonÿÇ™ö7·ºüä7±½»òê:-ëÑߨꌭCÐOS¹¬8®t¤¯¯Ë˜]̸V+‰MG…´i[3ÑRLõ%3ÍNe|·uF³2ƒß;ç|4¼÷=ÏsÜ÷=Ïr€¿áC?󢿯þL€ÿåæ»žç¹î_ï=ÏsÜ÷0TÕSÑSËUW:ÓS@7M;°UPÁ¯ŒÅš±Üß4xB©XC"ã5®'‚¢™QÊæDQžãi.}ƒ^Æ=Šcî¸fVaFÞ1*–Å, ŒT–. "Ÿ ÇØ9Ecé[é®fÈ_Š/ü( =Ögþ¸¦8ÿìIø\ä¬FlÑ–òÆ'U OE…a¸<"žTØrÊ0Ä0ÜT¦j¨VµxW‚’7oÝHéȧ¼XxØöâ-âY7ÍÙÇÉÙ†¯Ϙùx¶h‚‚²<»K\ò¬-F•ª!ó“r†­~Ä«rTºk›ú‰ž²FÇk±¼é“BŘ³u6ˆ ¯K‰<ëNøzâÑü±ªˆº‡ˆ»Zö±eu^-ø‰úœë¯§Ü[ÖâSømänþ9Ž|;üÐåšö“ê.[Ãw,t™ÂJˆkhbxÙÚðì1*¢x™aošx¸FyÇpºérönž«Tª¤òX¼ »‹ ›}Ýmû¼_`½@Ì8F!.ZΘCÕâÐ#Ì•”~Cqb›Ô`O»­¿wƒ6›1|>®L1áíQ_´‰QOåÑ*îÜWp@Nšü8åÏÀ‹¤ýp Åýoþ‰Öwô¿g ùåİs™qÌk)b8˜QU=¸¶Qó΂I”NÕ ‰!pênA "á‡Ç¡ó°¬F:°Þ0vÈ£ýdk0úÇÜ3`9†?Äâ¬P.ñƒ¶Eë#Ù‡Ö8´Ãq¬/ÍÃëR Zì€Ù×éV±wNzÏÿ…þxÆ=Cø•z^Æÿ^ˆàÐHp~­t-eÜNJ̱Çå«ËýV|"²’šœþ’ië0idtìd㺺80=ˆ öÓÃK$oª8pok{iáÇ%uo²Á¯ÚÄÚrÛý&þ>¿†Oª¼; ¡“¯tÞ›:«#¥6?Ѭ¢ä,Á†b.¥…!“t ©•‚–AKU)#¸ u¹Ïœ¹m™#¨Y©˜<ù‹§ãê_¥¬¬ÃªqÌ¢ÅhãÄ0éÚ–ª™¦ ’TA*–2w#9î{žâÞç¹î{žç¹î{žç¹î{žç¹î{žç¹î ºÕN™ôW#ãýLëP0n—ôó*Àõ9“;cø•&…ÑA—gš¦ºHãQ`ms¯‡Y§8ÅÒU 6ÅñJvŽ7¢I#¼O)²oPÛÍÏ‚‚~ŽüAæìïGX0¸Å©š8ä¡Ic¼/1Ú›Ð6ósûª ú;ñ-˜34xM=@¡‰qøJ#R«¥ãi —rßq¿±EþŽkž½DúÁüeê±zŸG=l̇oáG‘zž¢zï¨Ãb˹ߪø\³¶"rF+W ØNQþg,›‡,l%p¼8bµ˜v`Ʊ—Ëõ«Æ¢‰¯%@ó@^ñÆ,@Ûµ»\‹ð*—ª¢Çrv}ê&AÌ9ˆü®€½dQUb’y b¼µûJ¡B6¶Þ< ±œçeœÃ’©ú‰Ôœ7 flë/Ée,½U_OOW‹Ôo."Š)Ü-îÊ mHÞ` Mɺ Ìǘª=úü=3_«ïL›¨++ýIúéËr×`'Fhei1LécÅk««/*‰)j(–UgxJo1ÄŠ. bV pœï„¶#’(±ÕåI½àª-µÍuñðWÐu#Áñ3ƒgÜð‰‘A‰Ä’MþøU‰[q êWÇÀq}Iœ«0úÇf¬9°÷bE-j+H²{ÖÈ ÏÅ~áÏc‡÷­> bQú¿ü ¿V·Ä©’ª“ÓfvÄ0¬O(Ké<Ï)ðz*¬"«tÆTIª0¹wËæ9­w/!ðüW Å¢3aÕ±ÖF¦Òl`Jc/u?8(a¸ÆŒBgÂëâ­M¤òØCìuî§à@<\Qb8ŒfZ*¤©EÑö°%O±‡p~rãøÌzÍôGñ”ü>pü£Xjªã~­r®™ ÎÙ¯?ÓáÑVPeÙiñ\Ç$i‡™5ß#÷•`F¨¦ãÇÙÄn;šá¤JŠLâ¯Æ#*‹H_]ÎH÷@ûlÕAæ¹=WÌþ¦?¼0õ7güëø^~ >Ãêq\ÃÔjÌ^‡&õ¬Øe©W6#4u4¯>—~^64õ ç#,€í…=‡d¬c¨ÃqŒÝ‹5>/o&:F1ÎÛ¤i-,©{[vÐmoÀ¯Á0\Ï‘h:¡Ÿs<XËøtu3b5åy7NÔ)+G'``ªŠ¯{ðÍ]FÈ9#:tç*u?<Œ3¨ÝAœÁ’ò¦O[YŠU–vo2eÃbâ„7¹æ•XÇm׿î~!9c®8‡£¯ÂËðŽ>¡¿ ïO”{úê~të)(3>M&] Ç!©­Åke¦ŽžJIªDõ/ÆËA¶A›³NUÄ>W7á¿;„Õ9L7¤Pçs7¸„³(:ˆñ׋¸sÎiÊõ±G›põÅp I‡òLrƒlÁüÓx—ye ö½˜÷ׂ¥6lÆ0™`|jÅ0LGkaÅY’E–Í× ”¯bu=õàA7á¡écñÎ?í1øJþ'Môeêó-RV¶'Ó:Ìå™1ïå“QÔ•˜}]6%ˆSãÔ)¥Š ¸'ž_— ŠZ8†Èˆ…†fL–JjJÕЖYð銡~Ð1ÉfÓÛkpN³NŒÍ%-zŠø–£ ”jceû@Å(VÓÚ¸¹ Ç0¼FG‚ž¨ ¸‰Yhžñ̬½ÁG±ÓcÔÏü*'ðÝÎ4Ô>°z1Eø‹zlÂI†·ª;Ê4YÃ2½5<ІŸ*Tå¬N ÇïIU]…Ïas¶FÜCåøþ=øí~[Ç¥?øQáêo3æ>cýe–z§•«äÃkr'V’,‹%]L)¹þR§’˜‚BùS˜gÜmåvç¹ß=ËžÁsþDÌ•˜~—³¶b¾üß  ¢Ä¨ê¦ªÂ|ñKó±$3=?šByª nÒ÷ç¹î{ŠÞ{žç¹î{žç¹î{žç¹î{žç¹î{žç¹îÝU~£ÐçŸâ¹²ž¡!‹г*ÖÌôôØ«¸¦@}ß;Ë䈹lmlH ‡SÛ¨4y·"b™"®ž¡b‹г,VLôôØ›¸§B}ß;b;Æ^ÀlmlH!6léK™2…~T©†eŽ:øêp ™Z+Ù„." =ß3b3¡mÓí 蕺µèK«ž¿ÿZïÅ>â9R^ˆÖä8º#Õ|‡Ã3ÖGɹn·ÉŸ‹ ‡šš«‚ICG%&%lSTBÑÈ#YxF3ƒã8Õ=ŠÔôë9F€Vä|B(Ä’B®ÎÞT.Þ\©vb$Ùuð$ó¬2» űªz|©éÖp‰®É5ÑFL«´Œ"†FòäK³ð;&¾žfÃñ;Äá¦Ã±²^eE«+VF›ž ÅÏ—¶Ç[“g‰Šýžu×ZZîºúxÉ8×XzmAÿ ü/2œÕòåR}/ÄæÂ}AtpUPQ¬üŠˆÃTeiZjÈ%˜ÆäÆÍ <)L`æÌÓᦟ­„å깨d§RR¬»0r'%‚[Ý _^0çÕžà‚¦Šº—ú·ˆÌÔðPTÒÇqZß}ªI`nPÀ‹ýh0™'A䦪’7¦XÄšA$¾K©¨G;õ;•t¸§M—ñ%¢¡Ã°ÊqE„<¿ ®ÐTȸ®b¨Êå¬F¶qšþ cR6ãÆ^Éy§1PÑUR4Ñ5M;Ñ×Tbm†*a xÍ;]´Ö]ïn8áoÅ驦§2!š§ªš­-BrX’ÇA¥†‡ÆÜ߯«ðøü4pLZ>¿õ²“êÝ *Øw§L«%&;žªÞE’‚9ãJu;–jù`€;YšÊEŒ<žŒ`_Ï*ó-EÑeÅËåDë2ÆcJ¨ýÔ`N±‰ Úú[ƒ† …á”xpÀåÇ+3$÷E“ó$ ,¡|¸ê#û'ü˜µ¾0ì¼”˜ÈÕb‹·ªyd]¦ö”îQðÝÍvýf~"Ÿˆ—âè»8õë§_‡žDô¿éǧ´5y‹õ3êËX¾0¹)X~€ÐçÜ6oæxMDpÿ0PÅ9òŒÍúKñƱ(DUírIúɹ<â‰!EŽ1e^×$Ÿ¬›“ÇôEB ²‰'ï<¹ÏÀ[ð©È>ƒý3dŽ·gl‰]…zÓõ•ðšÿPXž/WES&%Z. Ø&M†AMI‡Ò£y … 2(#ˆãÛÏ™9Ï—ãÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=À¿¬sfºl‰Ôä¼N<§¨Ãž›˜²Å F3Ì*¯höß̸¶ÛßN½[›4Sä\J£'âQá8ì{Ó×ÊH‰bïóV²m¾û‹m½ôâ©cðåé²Íraؼ3Q45’#E‘oÞBµ“m÷Ü[mï§5Küc3çI:¯øÉ~>Š}QãPú\Åð^ ælÁWÍY€ÅýuŲ®;‚åüPcôì’PÉDËG-$É%-T±ÈÌÄÈÃó,8Ä”Xve¦“¤}H’o2: -¨1 ¨Á‹},íúÊÚª·˜³%KCŒ=;$ê;ÍæÅG8è1 ¸Õ¢ßM3~†£r¶¡[ÌÙ]5IE‰=55 ôë;<›Ò’mIYP€Çº Oèæ¸mBàwr†õ™ƒ0úpëF½ç¼¹øöþÕBl;®$ÍÿȺߓ(%ëU†µ,0ÉX"ù>Q¨á£’yÌXª&i*Š£ÍY«ÛE‰PÃevmX¤jkˆ ålÅýÑvï·ÃŽY‡1f<AMŒ`Ôª^I³²Ó»Ó2ˆAD¤d6šÄ›…äÜf²¿˜®'ƒSd„Å>h‰ãòÞO-ÐHÞÆÊÆ×7ÛÉ_…§ÏC¤kk½4ú{üUz¹—}4àfªwü":…T¹k?d¬xb-Šâ4MóSÅóøtSFÍ%"ÒTÀŒÎò1™ÌœÍv-‹M1Ì©õ…4“aøwîÇå1 KnˆYÚÁl§_Æq™ç9¦8ê*q*YgÃq:ÿ?Ý‹Êb«IO‘¬ÊuðâÕâ8Œ²œqRY«`yhëj¼ßu61FU»µ€°:ókïJžŒ}-z!éÚt¯Ò§Cp‰dÉ<©1:l"Œ-f)4*È“â5Ó™*«§PÄ je‘À6ÚqC‚d\b|2:Ú¹$ÊéSʼnWV<~I¢1m±½™¼o¸â÷caâ ùXþ¿ÿáBÿ‡Ç¡z¬C¦ø6v›Õª–J ¿é³¦`ÄÛ¹‰)q*ú_6’„¥VHÙž¥AºSÈE¸ ௕((/`þn9OW—®šW’3ʰFÑ’uF‘Âù#(ñkŽ x•ðœ-òöçc4Õh¦j©æ“å¤f‘bFK›mg`7„~ X^YÃp¼9ðÕUA(›#0b®[gà?øqd¿Gz]Öl÷’à«õ³êW.aÙ‹Ô¯X+1ZÌÅWÏ‹¨Ä©¨~v¼‘ PS¼ ,àEæ¡;¤\äæNsåçóÜ÷=ÏsÜ÷=Êÿ… ÿΊü>¿ù0:ÿ—šî{žç¹¼÷=Ïs²¬1K3†)³0Dy…;V0Y°Iávσ=`½K–Ë-ž:sY„Q8ËÔå$­‚F­©W«‚ 銪齊¤¿ç|o9á]CšŸú´s·Në0ª?ç~ ’VC#UÔ+UA‚ò”HÍìTèu ÖjÅóV¤…p3šr]NMüˇkÕDæ¦ejˆ¢qy €*xm=ù ?¤üíéSÔÎLõ£ë»­ˆèoÖªÇK“zïüÞ ¿ —-ác*ax–<ø|óa”ô WujLB“u\3É,n¯À1æ"³È9ŽŸ1amæ˜êE\3Ãʼn¯²Xå÷•˜ÍÏ%Lµf·ÈŽŸ1á.oŠ`5*~n)à„"ÄIÙ$rûH˜nÍϰª¯8®QÆaÆðòo]„Ì¿éÍXÚû]$÷@´‚㹹㗯Ê|+¨]RèP¼¡™ý3õ'&A†ÃéñÃè pÍÝ*ªæ/Šàòãøe-*ˆC3£‰1K&íª©y̙̔÷4×"ÐaT¯WÉ4‘6 ±¼•å ûÃæÛT4'Ö7›ñÁXNXÁW)V §†<½95ÉfwŒ…Z;\Ú4 ›xˆ¨Ä$ ‚ 7,à1eyb’x©°Zzp*"Ž@³Hb@®JÆ¢æçS~^Ϧà Óß­•t¯¬þ¶=]Gø¾TPÉO_€æ´ÄÖ–ÔÖQ´óSÉZËÕòáEà²ÆÑyhŽ¥Vx¤hÑ•Tb‚Õá˜uñÍD4“Tˈ¿—åîšWwp›‰;NÑcsÄbSÔâí€ÖáXeñ&¨“æ`¢šª\NO+Ëß4²Hîs´í78ãuXÎ*•ÞE\´‘¤ SC%,=ž=‰fœ2³¬žªZÜ!£—Ü’((iC̱Ç寠³9¿{‚>6à}ˆc•ù›©¯™êDù{Éha…á¥Ãhci––QK3ØØ‚ñ篛ÂZ@%‹ù ‡å©¡1SPÑÄb‰b‰÷Ab,×cèWðÒôøuäºü±é§¦Ï†cÙ‚IêsßUñºÙñÜᘫkZª¨Ä1JâÒ;ÓÆòEŽÊF{ÁòþmÆg\O ¨ .2 š¯0¼RÑÉM$wªT(}ÄêRá»›ñGå¼åŽN¸®RlpSÏ[™^)h¥¤–;‰J…¸Jn ÜØßxf ˜±9Ev02bb)jq–éž áÔuï©[îîmÀûñü`½þ™~ª¯Ô×[)aêÓ­NÐl½äãyï»ãò°¸¥åã_eEl@lG™}83PIO/ËáÖi±lZŠQóõôÂUe x䨦5'aº›¿mx>PS‡Š— —3Tb˜­¡«1q cp xÜ"˜Åöê¦ïãÁ† 2¥iiâ©Åfžª%bâ/m6• VÇÇÇãÍ:¿޽ÐúÝè>7êOÒ?à™Óª›âVÂñÿT9Ç(bX-œ±|ru§ÁâÉsaU9CDZVy]ZЦ(¥„ˆä˜,´Àáí7þõȵԒv›x5U2 ®ÊÇAŠá¡…~TÄ!1TÒyä;££$~÷k{º›Âåë—4güåè⃢‰‘Ì^¶ ¼Bº‹éÿâ‹é“ƒŸ§Ãb©¦†·5Ð%n(ÓÐD#3†ªÈk*jž!’hÑb‚•QT1Üx‹8œ«­«Æiq 1Uyq5j& yBEP7?V¼ØsÒ'¡oHþ„:x:cé3 øF2Û¬_Îjpêv“Åä„Iqçœs3TG…áXMÂóÞWYæù³-E|Ô´s¬I½R7 ¨°‡ŠªŠE”vz( ¢ÀvÙ»ð‘ü.:%øfzw²öMÊ2ÒõÓªTF)ê;›$SUá &úLwžš$1¤5aÁ`V2DÌá‹}‚ëÀ_6äì~zÌBŸ6RRgî¤RÕa%÷Òãx4ôñ©D†¨8, ÆH•œ1cï°]xfL±ŒËUYc¦¦Î9)c’£/ºŸÃ%…¢ÇPäY óƒ>ñ^k[›²OôÙë^£é^à#ø’gl¡Êt•q£zDê«K$ôôÑP>^«j(ë¿BžKVB°»¡2PŠÙë¤}b¨Íù³ éüS á’óUWCY‰&-†4ÏÒWégV%ÍÙŸ ÈK"æì›áÕ5TuX‘Y1L8Ã<ñ­-s£2LÖ€2’íe%›¹Fôó¨óf<ÁA”A˜òÎ/C=E-EqˆP˜¥•=[)++Z ÀksÜÙGâø]äö)͈7V¦¢è·â»és§øæuÅ=Yz}j¼‰ý`Î[<0ÕÁXYj ¬o.*ª‰£IÙIÚÉèx„ĺïâýHÌ7É©•2nW’® S¥šŽŠªii*þMžJêÔª „ ¾T 1$*†<Ç7SkçÎ8ÖHÊ4i—2ÎõWÖÓIOKQ,´õ.]ꪖ¢ÈövDÒžÊ á¢ ¢Ã¡PÓ%4]ʨµÏ´žäý<4Þ™? l½ê— ]&ÏŠ?S1?]y³¢£Æ°Ì?Ä1 <±_‡Ö¼8þS[…DÈb­„Lc– y–ŽETÝ#Gì7/Œv¦|>º‡¨‡T5¸ 7ÏSÕâˆ×djϘ˜âU’tzêŠ8u$'|¢Â)4´uT•“GZÕa0üÔ5ÊnTÔy²šÙ£¿fªšš=n’ùtØFéóÒwIjâÁ0Œé·¡=¦z¬DSA‚å «‚ÒFª4¦1IIOC;m®xh²Hl³†Ð'“iZCKƒS­7“KO:(xSÄí`ÇpÕÞåˆé”òÃ`”T‹å&`g0a°ˆ<¨!•F臑J*¬t&ãWnçÜÕcñõ{™Ü7 ôgøDt{1ur³¤Ùã/fZßľ§IJ?Kºy˜òÅzÖFh«ž™§Æå0G-ü¹áŽ}‰" ¼[óÜÙ'Ñ7§~®ztèî—}@z•Ì^«ºÝ‹,su «XäÓÁM=B™$aøTRü•™™A o*…2ܪí÷=ÏpáóÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ïq·ÂhñÜ+Á±f¢Äáxj61Ž@®»w#®ªÃº°ÔG±|.“Ã+ðŠõf£ÄbxgØÅ+­·#.ªÃº°ÔG!bX}6-‡Öaµ5tmÛX£€Â×V]U‡pF ëÊoüS?>þ"£Óÿ_²öpnú¯ô«ˆÇ_Ò®¥¦G‰ÐbøbU-|¹¢­GާžE oVù’û’$²Fà.~Ëøì?Õœ/0åçê^SŽDŠläµ1Pæ<¦j«X¯I2,QF@fB ±g;oÀ;<à8Ü_ÕÌ3À¨ÙZ99sp¨Š‹0áT…ZµzXéÐEdd ›s¶ü sn‹Gü’ƒÁÛ;åôuI3(ž:\k žIÀZ€ÔéˆãB ›]¹¬U'§Ÿúõ/’ðLÙ·2þ ¼±`3ׯK[Q“‰ÐÕÍ»l3¤ª¢qeÿ(»N‡¾œ²ì•ø@ç¯[”9›Õ/X}<Ëø,þ&¹'2ÕÑe/PÝÍ”uÔ¹±R(å¾+àukG,R³y±üÊÔLAó¥t;¬uo)t‡4弫…à ™³f'7ÊI[%EM6IQ;$F8éâ–I¶ÇbÖµ‰½¸åž3^Ré¶aÀðjóaªŽ@õ²MSK4Ì#$QÈÒ;ˆU÷­kmÁ²ƒ+àô²CUä5T Z/=üáOËŽ÷èu?¾„›ñbüIº1Õ©ê#ÕÖ Óntó3c™# ëŸNp±ÌíC‚Í6UQ_C4ðIEW]NÐb³SºSÆ“’šFÝmžhÍY’Zúä5&,w)bï<õ‰ “)ÓÔ¹•ËÆîPY­À£2eln†‚‹ Ã(©óŽOËŲÞ$ÓMT±< ‰!ÌŒ|”E ¸wšÜÕ_Õ÷¦Žz<ëOM3Ÿ¨\‡‰þž¦ŽoÄcéâÓwé_Pñr¤ÊÍ@˜m%yM|RÒº%Œð´Õ* ¿Wiú[ŒK•° ŠÌÍ—¨±ZɘÝLRWáóW,ì­AT¯#KL« ™& * 'ê­?M1irÆ5^dÀ)1:,#ÉøÍDRWPËZ³²š*•y ”ê±Y–]Ö%@arlÝD‡"bR`8Lµ8æ M_K‡bYgš7«¤’¨JÊi'VrðŽÅd½®"ä PÊ…^=ø–ô3f¯Åk%ô¢³®øn)G'¥?^Þž1Êš ïˆå¦¦X~3>,¸|•EASJÍBU‹%=;*»»u««Ôý$Îw$d\©nqÌK‚«É—Tõµ2S*ÒS ¡¼ÌÈXF ©¸ØñeÔ\ç†ô×2àùs(å˜çÌøÈGŽ£Ë’µi ªá K›¤b¥¼µ*‰· u„ÐLj)¨R®ÞY»8XÞцÑîBÄ'áËé—×W­\×^œúµüBó—W=/to<æîœä,ÍK yO>ãc#æLCY±6¦¦qR•”f:–¨­¨¬ó±©e‡Íâr§Æsš¬S2ÕÔË ¡jñ¹ä¡¬Ž‚bJ¬jUFE6ït, [R÷¹jüGc=v5Q4‘HF'+ÒÔ­$§@€ªŒ>š[è%©˜[^<òïý~þ=áÔÐúZôÇ–ºy˜â‹Ê­êd´§Î»–ÒŒwjªöW$±ŒL#¨œ:sÓxéªq jžŽ®qDF©Ç±!$˜µ}LR¥D3Í&$$ªeŽH••XS)7¼$[‚FMɉ Õx”4Õ‚¹TÍŠÖ|B®häI£–G­;*:ˆAñŠÖç¸H?ïÅsÒM²×Q½ e\30úÍõ‡Ö¼#ÊøG¤ž‘»â¾†§¤j3>#‰añÔÅ5)™fÊx¬%XYT°1¢öÛ÷ˆú5àÈ/assâFœ÷oHŸ‰ç§Ž‚dœ7ñõ Ø–°Z  þ—°ÚªU²ÎKÔÞvd̘ÕâU¢)(i®J*tU(»Äk|ïžåøóÜ÷=ÏsÜ÷=Êÿ… ÿΊü>¿ù0:ÿ—šî{žç¹¼÷=ÏsÜKæ §‡æ ¼#’¦£ Ű'fÃqZIDS,r²amÊÊÑËå¨u#ÃB¼McÙZ‡ªÂq *j0ìSvl?¥G2Ç)C,Gr°håòÔ:‘á¡^0ãz¨Ã«^y¨q )™¨± wÊ©!S$fáI6À ,uæ²>³¿.—a=Cõ-êGÓ‡E¢ëÎZõC[…ã^¥}⸮„å<Ëá³UÌqÌI£¤–ŸV¯¨š8Æ%F¦Y$ÛQɳ…Ë4ax­v`ÌýIÃ[-bj1Sƒõƒ*9†¢zO9ïM$Þfõ…JüË‹•¸ À¾æl3¢­Ç³'PðæËµùuFÕœ®æç¥ó^ñTÒÍæoX” t}÷‹€Äõ}%V/Žgj&Á+0DáÝFÀÅ4´þc~ŽzywîXÁPÈÛî;\n"£½,SçÜ…Ÿ½Dô/ðŠÎØdø«WTÅ×À‹Ôü¸¦I•!¬ZZœDe¥Ì¿JŠZ*‰¡(¢)dz˜¶¬î5êRu;ÅòvjÃé«q±E‡b49ÎŽcODÔÑU#…S¾ ‚J ª».àF€ Îéö~‹©ø¦)”sNM[ˆåªRʘX62‘ÒüÌ»Ë õ5•ò4p–±·¹+·eàÓC‡Qa°ù4ëO7r.YÛÚÌ×,~$òÇñÁS/úÑÂzgœ?¤æR9Ç//Å1ž…a™³¢éì8äJï"aË…‚­,Ž¦Žž‚Ò:´.¶<¥ÃdÌU¦›¯Å$“ŒÁ)¾,C„ɧÌÔÖ²â“S\“w4Þ ¢|b¨ÃYW\ï^žîNµiY[}<éªYk¥‚äý¡K°ŽMå´ôç¤Þ—ýtŠ·é†CÉ^˜z#“`jìq0Ê,*`qA ¤•µÒÆ´ñnڃͨ‹7vbuá¬ÈyEò¶AÇEOòí…à 1E Ï$ðF‹*¾_œû½ç,N®Ç^yS/6CI¤t‹>I¡Ž8ãŽ8’W–$U‹EÙæ5õbIÕ~{š¬úåõ‘Õ¯Å7Öo¥ÊÀ«ÍLê§¢¼ÑŠ7R=lË$¸7@)(q¬9°ª¼;©¨>8ð ‘ÇÕãi 1ÕÇ!( q]ÏskoNY«Ý>èþNÀ:ûÕézåÖo”§›©=C4fES‹½4IP˜}IEhèD*cÞG¼ä»ÏsÜ÷{žç¹î{žç¹î{žç¹î{žç¹î&óN_9 Jh1Áñ:)£ªÁ±ˆÑ$zZ˜®luÕ•™OÚFaq{ñ=™p˜0ä§‚½ðœJŽhêpŒ^4Yš¦+Ù¶>Œ¬¬Èê~Ò±¿qìãT+U‡WRʓḒ*»A]Aò•ÕôÍQ,Xé.Ÿ£÷#—ÌÝÙXkÀ#3ÐV¦m ¤Å0ÌC)fJøBá`Ëè>V¶¢ vžHñ +¸+ú?r9|ÍÝ”xcô•I™(éñ Ì·ŽÖDS0e/U40™¤JÊ[½×Ü÷RM÷ýÒ9O¾›2†~ôË]šú!øjuõiÑœ?Ä_ÕàWê:*zŠ‘F³É;e\;1P`$S‡cæÓKOlO=â¬2S«u†õ̸¶óì4™Ì?1SêRJ EéWô€vx%C£!Ýõ½¹“;åÌÓ‰`™>ÓÒfOóÓ+gܤ”UïL¿¤£³Á*Nä¾·¶œãGœ0|r¿ Êy¶*|f¿óS/ç ,«ÒÖ4ßëŠE:2›§íÃwѯã¯Ý=èÕ'â%øOæ<ñøAuÆ:Åønu°×b'¯ƒ’w¬ó°ùV¢« J„‰åŠ(º£Ú£„ÎuêöCÈüd¨i8gꦉq W&Ÿ ¡“åÄ‘‰Šžy$”¡É 31 1 `dzfKɹۧ·:U…f'\44®! t¤†Vyv}Ø™‹n:§†ÓTI[Yþýk¥+º¢d@=ÅÚÄw[÷ˆ&þ<;>žs—âµøÁúHÀóÞ3ÔJoÃ+&u[©¦¨Æò}'ó<Í=Þd-W„ÏU;ÔDì¦JfwžŠzZˆË¤s"Ç$½b«>fjм2¯†*¸TK_M-3SÒÐ@È÷¡—Í b}ìZªÉä¸ëWf¼nZŠñH㨈«‚HZ)"*=æ ŽO0 ­|ñ§üwÅG øO‡á™èû2CÕé O©_PNÕ¸¿¨.¬×¶vÌXœ’™Úµ`®Eà©ó ežE›Û#x*t³Æ)(bª\Z£0ÒGSÔØ½Lä¥LM ´²$$SÓÀ‚Q$iLmÒF½Âï!áx=,sŒBlb&ŠH1 å%fŒÅ$±"ÅQD¡Ã"¬†ßm¯qî?øQçªO}?ô£‚ôV‹¬8®ú\Ñ•3Ÿ¢Î‰á8gLÙ_òî&’áo @’ËJ$ó$Jj¹B –ÛV}­<ùîYWáµÕ¿Ä¯}Âz±ë× ø¥|ÝRÒÃô.‚Iñ jËK’âøT•n´†ªFq`Ó|é]ˆT÷=Ïrƹî{žç¹î{žç¹î{žç¹î{žç¸Í˜0:\Ç…TaUsMJ’´om4žUE<ÐȳG,OcgGPEÁqÆŒw¦Ìdø]TÓR¬¦7†²üªˆ&†E–9#{22‚.ö‚4ãf/…AŒÐM‡ÔK-:ÈQ⪅ü¹¡–'$‘µ™YAí¸å~'?‚ŸM½ju릞µz{WK–=It¿Ä0 ñ”ëè©krÇUòefWA6[Çbªai¢¬–ž:åVhガR„Ï9{©Æð ;3å©s6"† +«X]Tx~b«Dr3Ë(§H$`Šžà:0b@à0 n£À°üË—eÌxHŽ(p¾©áµQÐf 2°FìòJ H$`Šžà?h1 p%ÍX>-6)„Qc¸$˜æ8°þ¡PΔxÍPG-$žJÄ¡]¬'º{’5æè¯I0/F^¨ò×M¬ùšÿ ïYTU’gø¶"ý5묱U|¼2eüÉšÃÒy¬ÑB`«­i¢Xᆮ)RªeLà}] ›ËÝ?Ì³Ž¤à𶝠Ás¡¦45±VPÅÍ m$ÉWˆT-æB¤ÿ‚àž&0ž¨áÕx¦s4‹Ôl2VUaØ>o4抲*º(¡ib¬¥™c*ñŠ…¼ÈTÿ«pOð¾¡Ñ͈àù?”glª¨¡Ã31„ÒUGSJ‘4‘ÕSȨC'œ·•vŸõn,;$~ù¿¬9 õï’ò%gà/øauXÄÙ¾>›æ ×±¨°ÉÅD’ã™ —ä޼¡y)àr¿¿*ÔÜÅŸzÑ„d¤Óôß(åçÇóÌžD5Y‚º:ÌM¨L”«SPÒÑ ’s庱ÔÿˆØ_™sxÁrVx‡(åÜñ¼ÞÂ(¥Ç«–§’4 4QE :‡“Ü`ÖSí6׃^—0Ì>aV"jšÐ¡R®vódDQeU$hÒýþ<½tóñüb=äLûë/Õ+ô¥]\˜:?IUšòÞ+Er«Éˆ4óáØ5E4‰Rò¤ÐogH  òf;æWª\_:8ކkc½BPÔE†¸ouš K ìž}EUH=0æZœSÆÚuÄs+¥ G™ifމƒh¢œað8ýß6iæ ¹iˆ¿ßÃðöV+é«Ó& ‚õ €BÝgÆ–\ÇœerŠ’È¸¦2ÓËMçm¼‘Òy1_²aÁ¯¦y.» ù¬Z)jÜÔTSU.'ŠšÙkëäŽ9©]§–²D§•3Óå`lB°½ÄÜ“–ªh¼üB9*Ζ Ö¶¼ÔÉWVè’@ÆY*]\¯—!ؾDAMˆ 9îWWãñë£Ó#é󨟇¯N3Î`êÏâÕu§‹¡]é6rÎøe¥t­¦¬Ä©â/–JËç2Ôˆ$iiÓr‰P{à±ÏrÅÿ Äßè¶?ÌÁ–ð~¬âT´´øH2½|6(©Qª³ }Ÿ F1,ÐÈò >HèÐHÁc>à‹Ü÷=Ë:ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{€ç_=?t{ÔÿK3/Eúí‘©z…ӌֱ5ÀjZx&§MM5MÔSTÁ"‡†x$I#` °rÅ%Äcªˆ5=L)P‚I!3Ó²T2™™ÎÄ–4ÄïÕ/ éGòœË›1jzµŽÉ\õ‹<`ߺêÏ¡^±ºåYÒþ¤e.¨fœÓ‘(3Õ<8+‹`Øû´Pá­Læž*ʆÚX¦šâóã‰=È‹pjË7Áp˜(ŸÃiêê¨}9Xžš–@ĉ"†©éÒK[ô‘ÀàKZü0<—†aðÒµeUÒÔ°•F†Ý}ñÇp¯þ²D§À“ßžåÎæþ¤þ'ÿ‰ZÔå¯K˜|¿‡§¤Œ}%†·ÔîiÃ18:‘áóE5+¶^Á*®¥”y‹$u­,hñ¬O] •à•ů=Ãè'ðŽôaøy[5t‹%VgÞ½ææ¨›©ªsÍoõŸ¨ùŠ®±‹ÕKS‹UF¦;12ÇL‘#]Y½ãî{žå›sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÄîlÊO>åÜ_'眯‡gL¥˜ ’—Êøµ6#‡WSL†9!¨¦¬I"–6RC+©wàež:U–³¤xXŒ`ž¾›å†n¥‚˜Ö¬AÖ@…¦FÜ·A~Ík¨` àq:a—s‚bB1æJÚ–ª–sX±Y–•ëuìÖ¸ þþ¸?|'ÍÿƒÆhÁzÒ::¿æXÇáïÔJ©a¡04²=]&SÌ è)LˆÊ)á­£dOô¶§hÈoÔž‰K›ú¥–º€”ôx„TXT˜v#KQM¦9Ò ËLKPþXeóží$sZ76Ú›Î0“1g¬7©«›z:È&…$Û*;)‘fb€1®Î²Xî1µ— { Ä·¼@°'è¹å~ Œ¾Eü=3¯oJ¼zMÔþ‡u'¨QÍXéK1¼»c9ŽZÜßU+b zT¸Š >*¸ª|¸â©cRÑæ4_Ë9*ƒòjªQ1 Vòé«ä4”ñ/rT3²©¶¢=ˆ{„^ÜpL³I…ySÌ«W_ ì‚­ÃHñGaî#ÌY”i¨Mª|s¾_^fÉ?‰÷âm‡Óa¸ö?WøeúEÇ…m>eÃp÷jŽ®f¼.¢¤d§JšxŽ‹ÉMg•-˜¥Fà+•·Ü÷,'Ð÷á¿èÛðêȵÒF(2 ¸ÊD3®~¤ÄóVcš?xÍŠbõÆJ™Ér\E¸DŒÇË·=ÏsÜ<¼÷=ÏsÜ÷=ÏsÜ÷=Ïr€¿áC?󢿯þL€ÿåæ»žç¹î_ï=ÏsÜ÷=ÏsÜ÷#ÕÒRWÓOE]KmR4uT’¢ÉˆÂÅY\AÁÁUKK]M=m4u”•*R¦–TY#‘X«+‚> ó E==\ÒÕÀ•TÕ Rzy]XX†V}‡„;Ö¯áÓé××–± ý‚¶EëWMëàÅú5ê_.Raùç(âÔ¢ÑKE]ˆQÕ¬5€––t’“r£(i—:O€å Çü×-°0úš*Œ¤”ðŠbõ5ITf(º•³–M—hàu—º_‚e<Â1L¸W Á†¢Žl¬B)÷ÔU%Q˜H HZêVÎXXÙvn"°<ƒ…åœqq<Ɔ|¤ÔÏ€$QˆwËPµhuË]H³–ÐØX sT_Ç?þ#¤^‰z“é¿Ô'N›Ô¯¦ØñL£äo^&‚¹s]L­ŽÒâR®oËÕrTTB ;J•b­£IÕ7Õ?™åÆ tŸÓµvD¥—™(ᨂ¶­Æ=2ÔÍžß,æ`æ} Øœ)&í!¸â tz¯*A%$‹O±TÔ0ÅQHšX¼Óä±”1–â;{±@¾¬ç‹¾[¿§¯øQD}^t£¦xo¤®‘fïPþ­óN]ÃêzÐÜ/âQC•s ˆ¨ëé«jê’*u†š¢_1jgž &ŠÊõ°Ì霰 f¥Â¨b£IXÉTñƈóJÝä‘”î|Y‰'ÄžXnA„ÀÐPRÇL²1yÙU¤÷w ]˜ø±¹>'žâ–»ð‹ëgâ ŽàBüczÛ?P²>Z1,¥è ã8¦ÓŒ6e*ñ k¡\2laâ ´@AÂY‘êªÓiüpç¹zÝ4é‡N:3‘r×L:G‘0Ž™ôç&Ó%TÈØK…aXu*j#§¥¢HãE¹$Ùu$“©ç¹î{‹ž{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹îp–(æFŠTFÿi\p’8åFŽTFßi\pxÒT)"FûJEÁåuzëü/}*zû‡ Í]QÊså/Pù–—¡~ª2Ím~žò=bÎÕÔuX~!…TSI ¥ª&hà˜´wg°c]G§ü-æÜ6äXÒ èñu¬Ähkª*æX)ª¢ªWvTiî`êŶ€GóƒåìÓf¬•Ã5&*µuôUµR¬ó«ET(į̂ÎsV-´Ê8 ÿš3Ì8Faʨ±KMˆ­Me-TÕ,PJ­@¥»¬á®Á”ÞÀ¼¦_Z>¬ývzôë×^…ú÷èž+êÓ·P²6fʹwñéÎS‰áùzLo/Õà´õY»5Rb¬•0¬Ò—†ÒËSU$›# j:/'¨¡gE,©‹GK6WÄ…Dª€UR˜j‡—K,³+FT^¢‹±Ü°ÍÓLIz¹›óÊÈëˆ$`u¢Y@žãe;Ä唡QybP»…#?Æ#ÒáÒ\c¯Þ¤2æÕΈ{ ë.[’ª*\bJùqiñøëEDTN)¤§Ä¡Æ4¢Ž44ûÉÃ!•ú]—0Z*8«0¸jZ‰üÚJ ± %¸o1) Ž|Ý×"VFvÞ{ðgÀ². †SSGSCíLÞe=%RÅ&‡zÁG™}w•/þ±ïÏpÇg^³~'‰õeŸDðT~>’su MSë31PSTç¬Rš¢zz…—,aULûÖZW–&“ˆ Å'¤ÄæUØÂ<÷g ÂÑ×áõW‹gΟeÌG«¾¤³{Ë?Q=\õ ´fž£ãuUQyU Ø­da©£”hñS,jâÞfö¹î{žå¢sÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=¡êïÐï¥_]½4Ä:Wꛢ¸Uð ék)ðfº†œã™~JÕA%V ‰„ù¬:¦ñ!óiäBv€×]Šô3*¦b¢ÍÙF†“.cÉŠQâ³ü²ËLpM¾hã¹½;L¼ñl7À_è¦XLÁGšò­._Ǥ®ÅåÖT¨H%ß,qÜÞ¥ÞhûØnmJ°Æis^¥§Á±e®¦«ÄÈ$雤D¹ý y“¿ˆ<¤>ºOøª~Ý2DzÎ’1_ÄçÑrá5ØDÝAÀë¦FÂeÃ^š:ꌤ|iéYÉqLDs(_ÑPGyáŽ3Ъꮹçö(R¦ƒ2ÑÑ .¯s¹§• ˆbfXX2F 3o[6‘K¨Tþ%Òº©ú¥™3P¥YéqºjQCSvc ˜¾R¥LlV3u@I“p³iê¥Â9ø~9žú1ødäMþ£ºÍ=©N¸ž+—°~šâh`Í9ÊŸĪ3 áQb$s˜ZºL>8Úe‘¾]=ÀóELµ°\ hgšŠ¼GM˜}Kª¿É¡­:…H¡ÇÉŠ5?áð\©†ákI,”±ÏYF»hæe òê{¬@Hïãå"þ{–…²¿â¥ø¥a´ø6›+? ÏF®Þ`Ãÿ™C֬хÍ$!Ãc«‹«Á‹Úzèé'D™’\>`N.¸©ç¹b~…? ŸF‡&Q®Ë~—:CM–qü–ϽZĤ|c:fiZA;ɉ㻪%(ó<”) ¶©óÜ÷=ÃóÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷5ÿ…zîõ…égÕ—á½ÑÿMÞ¡³ßC²7_ Î©ÕX:u’p,ÿšñ†Ôá+Lø~ ‹ÂZ®xÄò‰&Žá‰$ØsÜ÷=Å÷M?>©z)ô›éê¦ZëW¯¾ªúÒë&-ÓΜEÔLŸ–ºŸé«æ†‚  #¼ôoõr‚yj"Í,îyî{žáYõ{ÿ 5õq? l/Õ'¥/K‡ÓÿS ëî#Ðηϊæ,¹›FNÆp|>‡Jj®¥£Š²lR*ï-*M#ÃLÐÊ6è¤ç¹î{–Éþ1ýy“×·Mÿl›øqcY³Ô>5’zyŸ:ÕJz–é°®œàyžZ3$¸•lTU0Õ&]’¸FZäùÙ¬ oSÏsÜ÷|ÿ Ásž}ÉoHøÆê»2z–Ä}<æ‚Iš©d“¥Á("¯Äó‘®\,yØm2JË+:+:½‚î÷=Ïqqøñz­õÑJ÷Ów¦®«Vt6zøëæLé†qëfI‡Öã8ZÆ+¡ ¬l9q8§Š:–zØ™eÚHTe-qî{žáFü5?ß[Ô¾–:Å–óßFsâCÕ¯N>«ñ¿O¸†uÀ`¤Ã1Øò%-•Pæ¬q0¼>xÜPI †w&ñ,EܸfsÜ÷6²ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{šÎÿ½jz¥ôQé«Òniô­Ö\o¢™—©½]ÃrÆtÌ{ÂqüZ«ªÀ1*‰ ¦¢ÅéçI¦ó!GŽ5ÚÎà.à ç¹î{€GB?­úü<ý_úáõz÷ëR‹¦9Ÿ"à¸Dë/Jð.c4ƒ­þY+á_ËÆ&µtìÕ±¼²:{­QmÄóÜ÷=Ç>¯†=Dcþš:¾“ú¯è©o@TYJº›Í·-ãðÁ•óÔÔÑæ:ø(…< =$ ÒáÔTeP$r’ ÷=Ïqó§_« —ÑÏÂˤ¹£ð÷ÇýJzÜõéÒ3nK˘~~ɘÍUxf`Ægšž‰è0êlbŽ1wFØh¢cŹ sÜ÷½]þ->’ý2z•õ²þ ¿ ,»ŒzÎôs‡ôÊ·¢¹ŽrÞ-™ú§IÔlN‡OäXÝV•´Â…«"Yé7l’1g@­î{žáøüq½lu×ÐÏá-×oS}$‚Ÿú‚¤¢Ê˜f\¨Qãpå¬[4cøvW2|Ì& §£Ž¦o%ž#H¨Ì…n§Ü÷=Ê›ôgøˆzùô«ÖŸÅ;Ò†{9ãñbÌþ2¿C³ŸDrÜ4Øu|Æ©x^UŽP¤øN1– A˜TaávH©¤€wÜ÷=Ͷ2F=‰fœ—”3>3—*2~/˜ð¼>¿ÊUdš¬.¦²’:‰hæ%Rò@Îcotjƒžç¹î*9î{žç¹î{žç¹î{žç¹î{žæ»Ÿð§OXþ¥½ þZ=)õV¯£ÝOªê^XÁ*3m&[3auØV3Q=?—‹SUÅgzhÉ;7{ºü÷=Ïp¢zEüIzßéƒÒ?âë?ÔU½Dz¿Ëþš0Œ‡S†t¿­]À: #IãÕxK¶[‡>#ó§U5[ã´AbYn=ÏsÜZ·ü(“­=Méwâ7‡tÓÐ)Ó½úRèV׋á:åÌVŸÉxþA‹GbpQ­,4«‡aؤX«áÉW,õFôàÅRU9î{žâ¡?÷ªì…èûð´­ëO¢ oÔªŸÄmóîÒZ|7äüs]nW\ðÜHÇIFÔ˜m%SŽŠwYü¶£Šç‘]HÜ÷=ÃEê3ñõËÞ‘3ïªÌê{ÑgOúí飥½;ê&OÊô™ªƒlõxŰŒ³>„×.帶8hä™b‘_ʙ¨>ç¹î^qÏr§ôÑ‹u§½ƒ0zŠ“(.+–½>׿ Z gÍsa‹P¸5N34IGKy/PcPB–^Ü÷=ÏsZï«ñüG«*?ŒÃ꣫õ}ׯG¹ß É˜r’G…A\N3 f•šZ9j>]Z7š9$uŒ3’Ä“î{žæÏÝÏ™“ª}éS³Nqæî£e|Í]$Ř¾+•±,_ ƒ©Â+¢€šŠejyI}ä>èíÏsÜ÷{žç¹î{žç¹@_ð¡ŸùÑ_‡×ÿ&@òó]ÏsÜ÷/÷žç¹îjøñþ+>¯ý~#~“ý2ôoÕM'¥^…õw§UÙ‡¨™ùú]KÕ*º,R—Çc†H𸩥®™g8|å!`#Üeaen{žç¸k:}øÓáÞš=#þÙ¯®˜îwüDú›ë¿1ç̤Ùû!tÊ“!b˜Ö%–ñù0ºzÊØÝV%<3¥,l®Gœì‘ü÷=Ïp§ú²ÿ….gʯA–}Vú1ô»Òbýwë]WIz™Kšh(±”ëðH⭨©þ[¦I±lZ Õ°Öuh@Š£ÎUt@Þç¹îYîøÕa™××^5øyôûÑU³O^²-/O±N¯Íäåê|&à™ç)á¹¢j¼V°bùM†6/OI<[nò—ò‹*}Ïsܽ,þ9>’:µYé/¦]ôÕšò?Qý[u??tê§¢‘á™o Äò^5ÓJ>¯0Wæ(+<´†š†®žRbÞåÛ¤sÜ÷_g­S½Î‡'¥/I=F¤è_Tÿ­Ñä¬OÔV_³T™O/RÉEtÔxV6²ROW#bQó‘“j:ûŒë"{žç¸Xÿ ¯Æ“¯=LôßÒŒ'Ô×Fsw©n·Tz“Çý6玴tó*RǃÒË…É‚ÏmÌ4´-mÃŽ ¨x£US°AÛžç¹îlùÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷5¨ÿ…"þ#©.Ÿz-Ž2uz“¢¯Öî¡Ï–új²–œÍ& (b•ª"ë åx7´‚8lò[`7#žç¹î~›ÿ„ôãè{=ú¾õ]êw0~!™F§«Yo¦Ø+•ú(ý&Årýf3‚I‰2'zGÄ"ÚÌ‘nkˆÜsÜ÷½oÿ…ãz üA½@tÑîqÉž¢} g3#ç®”õ †‚H2ÝF9S55.3ł⑟–‰éd†zx'.³˜Ô3FÛǹî{‹¬»øüÖeì•è+¤y÷ÐÏV:·ëÖwHèóÎÒ< ËK6+$µ˜Mdïó5-e>>/ QhÝ7í{ ÷=Ïp®bž¦¿ïIþ ýYõƒð £É¾³=+u{§Ý5£ÈøvTÉÃ̹¯¨U˜~[Æ24õ©EÕGA-duOåùd€Jî{žå²þ<¾½ú­øpþ\õÑ z4ë3b{.tû®¦‹ Âk±ìN8&®’ž¢É9†™&0«‚¦]…Õ2ŸsÜ÷+kÒßâÕëÒöoüU½:úԣ͉P,{¦C'ç>˜ä  Î9« ê<-çBø))á¬bpñ¦ã¹;Vþç¹îmI‚bΰl#ù9°ïæÔ´õ?Ëê˨ƒÏ‰eòå_]Öaàyî{žãŸ=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜÐ+­?ÿ®šÄ#ñè?ø‘RzF釦þ¢â{¢ø5?§EêÌØŽ)‰S4sT`T~m;R%$ =K1›Ì;OèÛžç¹î^­øQ_Eý u‡Õ§Ìã鋪kÍÞ‰ézuS×Þ£å¬?‹,Á†çÌ7¨‹5x„f*1ºZd†DS$ÒmC±ZE÷=ÏsøÉõã<þ75ô’ý:âµ^—3GL(s6w ¦î"3 ÒVRæi¦ªÄBÀRÇ)¦dHI©-¸2·¹î{€EÿE½UèרÅ7¥Ÿ„No%ôc —TýL®KéÝgÅqt̸~YAKˆ¥OŸ\ôtÕøLÿ3±"FߊÜ÷=Ïrâ½&~(=õ£êKªÞŸ:!”ñÜoèöBéæ{Í]cq‡ÿVÌ}OÀ©s6†DÑT<æ®J“3ƒUØÂäŽ{žç¹UŽïâ?ø’z;ê¦\½é³§T½ôå›zµÓ|}UÖÍ–±ÜC6WfÈ1 ùòî㕆ššl>Vª¯dÞdòÒB$¿¹î{—u€z½þ}ë—;ú%ÿ0Yó þ¥ä8³ÏûHTàÞ_N±7 Ãÿ“Rb{ýüI~Ì0íûÈo§=ÏsÜ8Ü÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ïr«ÿÂÓ_âGÔ.„uW¬Ù÷¨½8ê§±xú[šú{šbÊÕômËK-D¿2(ª¦Y¤@¡ž÷ç¹î{®øú`¡Á}5a9í½fë%_¥±Qu·¥£;çó™ñ„ÍTaCESWˆÐ3¾§‰Å*l!ÞV éî{žâ_2ÿÂ} æoGÝeôW&`êÓn´uz«­øžp¡Ìxt9£ϵtÔ”O>U&%ŒUË€¶Ñ˜äìÇ_™ž³¨Â©rÅnlªšzJ(éä¨Â©ZŸÊ ¹¼â\|¾ç¹î[·¯Ã󡟈¯J2ŸKz׉æLŸUÓ|Ñ…g^“uW&bÑàY»)æ¬J”˜–]5=\qÊ‹;‹I ­Èm»Õ}ÏsÜ(Ùwð*ôe•},tËÒv š:‹I”úyÕº~·ã]Hªœ³Q"£¨ –¿0â @"ªZˆgTš8 ‰XG¬TßÜ÷=ëëÑI½}t›.to¬™4eŒ¯–3F›h+ò–0˜.$ø– • RÎôõ!©ØT·™ѺÃQn{žç¸N:Ñø úNë¯ûdÿ[º‹Õ 7ý¸sFNÍZþU›á£þ]‰dæ_! »“äéäþi/ÌFwï²j6ëî{žåÇsÜ÷=ÏsÜ÷=ÏsÜ÷=ÊïüFÿ ŸO?ŠN:kÓ/Pøæm˘7Ió<9»&âù;§ÀñH1šz Œ>) Dôu–TJ–eÚƒCiÏsÜ÷ e_à#éw1ziõéO©^£:÷Ö®˜z«Éµ™²£;u6\ÑŠa3dŒN§£þM>+‡Ê”b¢ZŸô°#o5R1¦Ðyî{žà¹ˆ~ þŽ1|Oׯ'ŠÉ›käüG2Æ_ʧ|n™b§ ËOòŒ>l EDMêpÅ0|WÇ«1:Œ:q‡b³½&Ž娱,;k±ÒÞç¹î8çü';Ó_¡oC™wÔ}Ëxv^ëiëÖCêèF7„gO‘“ TòTóR$3ɱC!‹¶>ç¹î?M†ŽXôÏêãÔg®˜ºÅ™úÓê Ô¶IÈyW©˜‡á¸fAÊØ^\‹DÁ°øLâ-…,õ!CF#yhªG¹î{•OøPþÝ~Ë¿Š‡­¿Å#Õ¥ø}QõQªÿÙ÷Ó[çœ>φcÙº*ÍÙ‰«2¼G •òa£hûdTHŒ¡cFosÜ÷-óñü8râ•ú$qN§f>ƒõÓ.sóï§Îºå_åòbÙ{0aä}ª|V ŠzŠiv£I(»ÇÝ´2?¹î{…‹¡ß‚Iý;tÒ—JzQêªÙ@zvêÃu¨Ù«Çéèjú³šj«èëk©ó\t”éFP´QÓšdQú3¹gosÜ÷­?H_í‘’úS“¿Ïî|ô÷þks枬=?Æ’bßòJJê_äØŒ›ÍÃj~w|ðé¹£C}9î{žá8Ï„/õ×-zËŸíó׌¹þ~ºÈ_þo‡g/"¯+í¨Ä'þªà¯äŸ—ÀOÏí4ºézû¼÷=Ïrãyî{žç¹î{žç¹î{žåYþ'ÿ…WM?L  8f}ë6sèv3éÃ4¶péÞqÉ3átؤ8ÊÃpËæât•{‰d+ßžç¹î® ~?[};âþœýF~!xõ€ÖgÌŸŸp,לñìÅpZìŸO‰S¥ O†ùqÓUÿ2-P6&4³ ûžç¸!ãß¿§<Çÿüƒ‰õC8M†þ+xõ>dêÍH—Y2Î)CˆTâô‡ÙF·Š:™Ã2Ty…•vî'žç¹îKôéø2dÞ‰uóÑ'©|åêw;õ¿ªþ†ºq‹ô¿§Õø½&[ ¢Å2Î qHiº2†7ó()q1O , ²Ä†Bí¸ŸsÜ÷7ø`ú‰õeÿ Ê~¾úÓéZ›Ó¯¦L´Èøv`|ûfoóÉ›rmV-‡äÜȸN ËS†ÍMMˆA>ÊÔ §Û˜ŸsÜ÷/[×ç¡ÞŽþ#•:›é#®râ4© C Ì8<ÐSâøN%…WÉÑVÒISñ‰"šÜ®…] # 1ç¹î{•±Œ•“ºêÏ#UúÜë¨ZØöSÆúçë?Åðì=Ê™&}ØUátÉ52ÄÒG0³´‚F»‰î{žåŸz±ô«þÕ”ó¥¿óáº!ýn¥Ëôßç³%bÿÊs¥òfƒó)kö>Ǫù& í÷£‘×Çžç¹îÌåøUÿ[ó/¨lÇþÜlÀ?Ú¥ù¦Ê(3o“I•ÓáPXpDòOËã_Ê÷TTk¸Í.ž÷=ÏsܱnˆôÏüËt_¤]þ¹cEÿ4Ù_Ë?ç0Õ|ö?Žÿ!Â`Âÿ˜âu6_:²«Èój$°Ý#1ñç¹î{‚=ÏsÜ÷=ÏsÜ÷=ÏsÜדÿ„û`tž¥}RúèoâAêÒÞmõ{™æÍ=]À2`Àp\:ª°ÕÖVSÄ@ÂÞY"¥lBqšìT;k©ç¹î{Š®¿Â~½<úŒÆ½xf<û×î¡I~ ™W¤ù[«¸ºM€IQGI%ËSÑVѽF!55Ï–aj¶›x&I6÷mî{žàå]øCäì?×g¦?^3õœzeÔ€ôëéfnÉôÔØ ~òn1ž:Zã]HòS=A T=9…C–Á™½ÏsÜBôçðé¢ü3¿ ®«ú/ô¯ÑŒsñÃ3õ† ®Šf\Û•²^)™°üõ"a˜µ7ózÊZl:‚šg’Ò*–U`¬\¨>ç¹î$?á<_†?Qÿ ÏCså?P4Súšë>;.`êå,X”XÑÂ(¨(áÀp,çéžHgJEoÑ<®ˆÌЬ}ÏsÜ<߈—áßÓć§éÇTó¶=‘p¾…u;-uK.×eó‡­Ef1–(q* zJŸæ4Õ+òÒ.&åö{ªÙ†·÷=Ïqy€zBþCë—;úÚÿ?¹óþºd8²7û7Ôã>gN°ï+ Ä?œÒa›=ÌI¾CË3nûH-¯=ÏsÜ :Ýø{ÿžž¾õw®ÿí_ÕNžãý ÿ6{1üŽYÀ¿ŸG<Ö¬2“Ëo'¥óïOUs´ªé§=ÏsÜKô_ðÑÿ3Yô•ÕOöÈëP¿ÙO!âú˜3GÎà™ççßç9’)~o‹ù…£šâÂ(ô÷yî{žåžóÜ÷=ÏsÜ÷=ÏsÜ÷=Í.¿áOEëžë‹ð¢é?Ió_RÎÕh³å>né/Mz•/Lq\Ô`ªÁ¼¨WšO’‚T Û%©ÕA`>×=ÏsÜ]eÏ]>ª¿ _KºgÒ^c˜·W=põþ¿§oýCu…º£aU¸äx^…Éýjʈ࣒i¢s§™¢B와ç¹î{„‡ÖãMø z…ü#[rŽ#’½5õ?"z¤Äú ê1äJÜх͉6…aØÎ09*Þº¦šŽ¥ªæÔ¤öŠ3–I#ç¹î]„߉âw™?,›ø^ä“tKúÙ“ºcÓ®¥ú¥ê}Vu— °êŠÌ+Ϋ—£Ié§­"|GÊÁ’¢8‰Ü¯PB‡Ü÷=À§_Ž×«ÜÙêŸ%þøÏAò-® ½Nc]1Ïø ˜ƒ¢™o/E˜ë³•-;â&¤TÉJíQN$”ÄѦ¢íîûžç¹e?ŽhèE¡L[3ú›õy}!t+%f\ÏØ—OêþS4gÈi¼ñP¥hž9ÜâNÁ¶ÂèAˆHì°¤‡žç¹îT¿áÍԟįÑÇá­Ó¼ã%FWÇë:÷ê:8:QÑÞ»õ:†²R,ÐafG™RGc28½ÏsÜÚߨ}SéH°J\ËÕ~£`=0Ë•ÕaÔ8þaÅðüŠlB¨1†™'Ä¥†6š@²0w5†œ÷=Ïq ú¥ôÇ•?¯ŸÖŸQ™-šÊÊ ?©ÿÌ3~_£þ®b§›òTاÌU§ÉÍSäIäÇ>Ö}´¦Þç¹îü÷=ÏsÜ÷=ÏsÜ÷=ÏsTøVŸU:ÁÒÿK^ŠOG:…›ò/œúß„`Øì9/5Wdü[ ªË¸¦ì²žò:)ì¤sÜ÷=È}iügÿ,õé»ñ­éÄÙC¦õèCÈxþWÏ™F¿3VŒ;&çÚ9kê#†«ŠaWŽRS½8Š SSÀÒ4žâlGosÜ÷}+üT?l¿OF]9È=ëª_ ‡4åΨglo>ÔáëO„åqaئa®Yb¬ž¶L‘ªqdÉX]a©V>ç¹î^¹?UÞ‡½HúÁô{™ú’óÇ_0ü7¤xáÉCI9CMÔÅÏÙž—,bi_Ø”²n¤ži£„SÈ—’Ã]X2ûžç¹°ï]0T¸ç¦üÉú}Íù?#ú¨Äð¼6ŸÏ8î‰Wå3’¢™1*±E=D©&w¤ŠF È#»=ÏsÜÔßðŽõëK¡‡ÿã1Ô<#ªX©ŸT} õ5‹àtëjƒ*å,^dÆrþˆÔVWbø­ 6±OQ==:Õ" ŠD›‹osÜ÷6üÁú™…à}Ë}WëdËù…2þ‹u2É‹ÑC–°Ùªh¡–¡Ó¨œÁò«,„G)”«-ˆc{óÜ÷=ÆUõ;é­Þ¾$õ ‘žL+,C±8ÆlÀ Sdʈá–,zP*½Ì1Ò¦&ZÆ´$:öasÜ÷<¯šrÆx˸.pÉyŽƒ7å,ËMf\Í]e>!‡bs ’)éªiH¥‰Ô‚®ŒAƒÏsÜ÷¹î{žç¹î{žæ°Ÿð­Ž®u[¢ß…–_Îêvaé6m—ªùFŽLÓ–q¬KÄZŽ|’H N4˜£RɺĀHÐsÜ÷=ƒéË®Ý{ü4ý þ%þ²[¡=_Ë=AéÓꌃ’zÿê ëž |öf¨ÂªÚ†,¦(¦ÃM*Õ!™™Çœdˆ #`}ÏsÜ“OøÜþ']_éßâ™ÒéºkÒ^Œu›¡¾—°ŸPžžsž[šk“Ë8ö[ÂóEjWÏŠ%BVct¸F,ÒÑ*QÅJ+¢X¥2Ó±—žç¹î#}?~-ЧG}~ =-Â2¯L½CúƒüM©vIê6vÇ3µKGI‚®ü—Ìu¾dS„ªÆj¥Ä¢§ICSÃtì²6žç¹îÏ[_ޝ¬?Ã߬¾£=3uÛ¢97u¶ƒ¥]3΋Ÿ/Ù)°î¢æÌÑ™0|‘ŽaŸ/[‰O+%6%[Y%4QÊ“zbX±ksÜ÷6ë^š3'££á\ëD~˜sv-ÓºäêÇ^rÍDtPdŠÉ08¾-…ÔâÏ/‘y²A4ŽZ5PÛ÷ Ü÷=ÏsPŸÂw4úŠéu_âêãЧX±Þªúɽ0Ã0OOú‹Ïï‡PõWªø6!…Á‹ç'1b”Ï‚aÇYI$°#šˆ1xž8=ÏsÜÜw¢=WÅñßK=!ë—]ñ¯’1üg§ølë.-„ãtUy/¬¨Ë°c´Ôx¸ª¨¦— §v•¢«ù‡¡Q'˜ÊwsÜ÷'Qzô׉Tat˜w¨\_UŽeêŒÝ‚ÓC›0 d¬Ê”­:Ï@±Õ&¥˜=RÞ%òÞì6›{žç¸#äœõ’:—•°ŒóÓŒã…u%fy0á‚b˜®]r´ÔõtK ª®Œ¤£Gpyî{žå†çE~_ü˜ÿËÍw=ÏsÜ¿Þ{žç¹¦?ãñë+Ö/I?ïE^š:Ö¾ºäî“õ_¦8®1zaéû-aÙϨX¾'CW˜æŽ£ Àñ*ž1Aª¼ËåÒ¤’ ”±÷=ÏpØá_Н\= úbü2²F!éã­~±zåëë7çì­”ðN»T`]êÎŒa™ƒä0ª|o jLRŠj¥ªˆC#TFŒ¤çyo/žç¹î_Ußð¡¿\]Tü7ý$úžô«éæ“¡YꞢj:5׉4à¸É Å0ŠtÄi0 1ñ\*Ûs)|HGþƒò΀HgŽT÷=ÏrÞ2gâùê·ª‰?Q? ®œþÔu9× xM1ßQN¯êÍ 8.XÁs¦MÂs*6Ç—ËÔÔáµxÄttÉLeùµŽI­¼÷=Ïp/ô¹ÿ ĽOuKÒo§Œ#ÑŒÙ[Ô—[ú­Õ.›uû¤“gÓVzUKÒJJ GÆ*j£Ëqüðjzõò`h©CJ­–ãw=ÏsÜ;¿ŒÌøþ é6ƒ<Íø†ÕþéîgÂ1_P}bÁpáSšñÌ«’ p ¬Jd¤Ä+$‘~]¡§ÞEUØSz?¹î{•ÝøRz±üW²¢ŸHX—_=/çW‡Ô—Z*ð<©ù—¥ËÙÏ!t¨Ã!Âóvkƒùm[×Í–¶ Fæ–b"M¬­ÏsÜ÷6}ç¹î{žç¹î{žç¹î{žç¹î{žç¹î{š³ÿŸý\z ô­‘½Áé—¬9ë¤Õbê“åÜþ:£¥Ä³f5…ÍGù<2†¬lª­c!ùX (’RªHü÷=Ïp'é—â•Ö/ÃÐqõQÖÞžz§õ7[šºÍ•:{…ô÷ÔÎZÁ:1(©qŒzÙ+°hcÆa¬£¼[@”Ä`Êd@·>ç¹îcëÏãëëG0þŸŠŽ}鿤ZM¾­|õ„dn STçL9aù[Çkê(9M†%.%UKQJi"#FÆEœ3"¼\÷=ÏpVÉÿÏ­ì¯á§é2—ðóN¹úÔõ—Ñ ÿGçõkÃ0özšø?˜bUcÑňaQÆ$Øá¡–qF#w]çÜ÷=Á'­ð üÃѬú­ôÍú(þaꯢ=béJ:Ѩz‰"ÇÔøº´1 ̧­þ«4”)òT"y µZT͹¿=ÏsܼïU¿©Ü 9î¿ÑÞOʹ×Ô¼‹…ÓtÏθ…n”£¨®Æ)(ªë19ðåù–§¡¤–z£#̗ʧ¼ãžç¹îk9ø;zùüCáü0ýxõë5d<Ëø z¬é/©ÌÙ“ðN—E™=^”b©‡ ©© ÄŽ‚‰ëjj¢¦J}ª¤ª…½Ç¹î{›k`•µ˜– „b8ŽØ6!_KO5~ïæ=$ÒÄ®ð³…]Æ6%I°½»sÜ÷=ÇN{žç¹î{žç¹î{žç¹î{žç¹î{žç¹óÈÏ~¹ÿ£~%Š_I)º÷ëW1ôÛÓßSªð~–äÿL=1Àz›I—pÉñlfOŽ N¦„Ð#%b‚ÅüÝ“ÞÞX¿¹î{—/ëßþIÕC}qõÇéÿ'ú›Ô øwáý(Äz½×*®¥aظ/P¨02a²`³TGUóù‚š–(éÚ 5Þy J›Ü÷=Çì¯ø¡úïêgããÒHù[¤´Ðú7ÍÃsÝ&RlÀSWO‚f?“©þºÖNØ|µ-5’5`±Ì»µœ¹ºí÷=Ïq‹ ð¡Uþ¡ýzŠüH²gác?§ŸOxeeM-OXé¿™cøÎ ÒÁŒÃ‡ˆ2¤’üž„Uùj¦¦ŒŠHT5·óÜ÷=Ë%ôø°à߈o¨Þµô»£Ýh:Ñ\ƒÒü׉z‡|ÄjR»0uC,ÒfÚ\¿¸\hŽ’yLâµŠÈ Cuǹî{•3ÿ ξ®zOÖL¥ŠzoüHóæQõÕlÁ=þ}-Â!Lc0ç³53âøæeµh®Â*õ¨ŠÕÑXçûžç¹{õëõ!„úg믤™ðL•–ºE€f|áëZƒ‰ò¾5Ô—M+–èpq@ ²UUOÿ8Ãd6Ùï\{žç¹a÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=Ïp‰zÆü3= þ 8–CÅýaz|ú݉tÆ B›"UWb8õ àÅ$‚Z¤AƒWQ†óš2w†¶Ý-¯=Ïsܺsø9~}%Ë](Ê;ô«„匷Ðî£CÕ®”áqbÙšTÁú‰>K5TâR3ʱá‹åÈZ/ÑsV¿¹î{އð‡ü8óO§n¨zPÇ}/áuë.v¨ê?Q2âÙ–[ž*£¦†\b*ê|I+ij)#CòÓÆ».¶ÚîÜ÷=Ág¢ß‡ï¤/O=VÂ:åÒŽÅ•ú¹ôû é^ŸåÆs+ˆŒƒƒ= Ñá2>3_V%HŽN|éLv Înoî{žåyú;ü&z­ÓÏÄӯߊ_¬±äþ±uÛ?e¸2_J°\Ÿ“æËXf ‚Ó¥-ó*¶®«¬šlRZ:(©K«Yci3#"Gî{žåŽzÀô!é7ׯMÊÝ=õwÑún´d¬•‰œg,åºÜK ¦¦Å ,”?3·­£/ ŠgE.[hfµ®oî{žáxÊŸƒá’:I„ô'*úMÂ0~“`Yÿ ê–“SÍ9ûÃ$Áèñq$¸›ÌeŠšVŒF\ÆA¹Buç¹î{†×Ô礯Nþ²òÒïS=2¥ê¾BÀ1Ì;2`ùv®«¤Ž o Y’’¨>QM!h„ïe,TßPtç¹î{…Ó©?„·áÝÕïöŒÿ8þ™p¼ÑþÖØæ]Ìž¢|ÜS1ÅýgÆòŸÎÿ)ªŸå±ü¦¦þa>ÕƒËS¼î aosÜ÷,[žç¹î{žç¹î{žç¹î¯WÞ…='z÷Ég§®º7EÖ¬““±Q嬺·¡Ž““P …|®‘Ëy5%™ŠëÚö<÷=Ïp¬dÿÀïðªÈë7@ò‡£ìé¨IòÅOY2Rc9²H1ÉòmeN!ƒ<²ÍŠ¼ÑšI«%uò7;·÷=Ïp|—ðÛôAQ]ê©ôù…UÖzÎÁ0ì»ênIªñy—5àøFpš*yÒZÆX ±ÉN#p@mÛÀaî{žâ ÿ„·áñéŸ1ú|ÎôõVÍ>•`Ì”¾Ÿ±úœËœqʼµM›áª§Å¢‚L­2GQt˶má~m…½ÏsÜ(]Uü&z­êkñgè?¯ÏScÉù£¢^Ž[©ôÁѼ'ÍE˜¤­¬*i0âÕUs$ã ®&ª”ƒßD!P´…½Ïsܽ^{žç¸Ajÿ ¯AUÝõ鿫Ӧ7F½Yf£½Cäc‰câ ÍšN+GŽ0žQ^&‰þj‚ vÁ$itm‰Ü÷=ÃÔMýêï@ñŸKÝFÈ4ù£ Ù‡§ËxÏN¥¨®Šš|’8¡Š”ÍMç¹îü? ÏOŸæ'üÓzk‡/7¦L͘s‡AeªÍÛ|³˜s]&EŠÔÒ6;ŒV6Úˆðj@arÑ)MÈŠÎå½ÏsÜ,£ÿ ž«zÄüR=1úËõÖ<ŸŒúiôO‰TãÞž:-…äù©óe^-SK‡ÕƘî3SW,3AGŠÐ¥]?—÷WfÐÎÒsÜ÷=Ë~ëŸCº[êS¤¹ç¡}lÊ«ž:SÔª?åùã(=]} xœ“˜$› žšpŒÑÁd…ÔÝIÜ÷=Êìé—àSøNtrƒª¸gM=`¹Rƒ­ÙV³$õNž,k6Ì1Œ­_‰Pc¬ZRˆõ8e4…£*÷@H>ç¹î¿önè—û6ÿ²õŸýœ?¨ÿæßü×|ÅwÊÿR?ÿV?•yþÌù!ú þo™mwnמç¹îlð¨ü?2Æ%”1ŒÓf‡bY ¦X—FòRây…M1y1jð5b [ª&FQæ8²ÛÜ÷=ÃOЀôƒÒßH2O@ú’`éÏHºsôÙ+%SOYS5“b¢KˆMQ3nš¡ßßrn}–÷=Ïr˜ÿáC?󢿯þL€ÿåæ»žç¹î_ï=Ïsܪ_\ÿƒg£¯Ä'­}:õ ×i³¶ ÕŽ•à2eÌ•šr~pÄr´ô˜dµUur(|8ÞæºUf .´éÏsÜ÷!åÁƒÑÆ[£ô‰M=v|Î^ˆ³Æ'Ô…ãyƒ:â˜Þ!`ŧ¡¨Ÿçªk÷ÉSN‹d,@Qp;óÜ÷=À÷ü½ã¾ŽæôGœò÷I£ê{u‡/f 75ÔÓæ|?5+Pÿ0¡ÄZ $Q£`¡Ë 8V_sÜ÷ G@>…zfëçX}UôçÌù§ÔO\2vNÉýFêoÌU˜ü¸Õ>C˘~ZÃ*ëLª%\±a½eFíóH]Í‹sÜ÷=ÊÒü1? .¾tƒñõ§ø£zËË]5ÉhõKK…tï¤ý4ıìoÀb©Žùî)-Va££œÄåà ‘Œjuy‰Ú ÷=ÏrÈÿßÃ'ÓGâw‘úyÓT2fŠœÓ,b\.à¹w1U`1É‹=)¢Ž¦¤S+y¯nâ~Æ÷·Úç¹î{€Qü=åLÓÖ^Ÿê&w‡ÓW):×ÒlS2çÌ_Ä)s¥8L‰ªq1ä¡Qƒ@ß+p›‹žîyî{žá¿õ§èS >¾²_Jr¨\;IJ÷F³æÔ|“‰K…ËfË´•ÔTo3Ĭd„&#.èÎŒHösÜ÷=Âqž?Ÿ@BË^ ržcËÙ¢\#ÔÏY®ÝRHs-LRKŸÒ£©S0Œù4»±9¿@.5éÏsÜ÷.;žç¹î{žç¹î{žç¹îW¯â øcz_üLr×Jò¿©ªlÅ%Fq©³@®ËxýV_­¤Å&§Z;ϤR÷P ¥ˆ*Âàßžç¹îDü½ ÔtG1z~̸ïT3÷Os.pÊÙâ©3Q±ÌrºŸÉðbÔš£2´0•Ä¥óc]í'ìŽ{žç¸6⿃ϢŒo)~ y'Ë8ínø˜b©ú›‰±Ú‘$¸µ=UE}=NÊ£äÚ Šƒ*( »•C[©÷=Ïr§ßÁçÒ·§~©úVë^ š:…Ô~©ú8ɸÆAè®sÎY§«ƒ*ãâR¯2–hécÅ$‚‘@U‚Ž4cQÏsÜ÷ñü(}Dúür²·âkê—.tÃ)twÓ.‰`ÝÀ2ž#˜q,ɜ祮œ.bÙ’Ÿ¤‚’ž¦‚“.ƉâW|`÷=Ïsc~{žç¹Pµ‚¡ ¯H~ ½K—ó1èG©®£OÕ.©aã1ÔŒN\ÙS>PòSVy{¡ƒvOh€#C®¼÷=Ïpãú±ôWÐïZ>”³£>¸PbUýÏ4¹~ Ãq0üE¡ËXÍ=D®5fR*0ØK›{ÀãÏsÜ÷ frü=ç¼Ëê6cød“õ?Òü¿Òª¼Y†¢(æÉ¹fŸ ¦¢Ž™Dgɨ ƒSï˜\µ›Myî{žå‹tG¤97ÓçEúCÐNœÁQMÓÞˆe|(dJj¹Úªª<-a0`ÔK4Î’A2r.ÆçÇžç¹î ÷=ÏsÜ÷=ÏsÜ÷=Ïr‰º™ÿ Üü?ú×>¶zˆ©Çº­‘º“ê«Ì}RªÊ½KDzå&!‰ÕÔMTXÁ†ÆÄz‰<µ$íÜmÜóÜ÷=ÁOªÿ§ î´Õz°®ÏØ>lÄë½ie¾œen»V.h«Ik0¾–6ø§rŒa˜»Hg’äÊCßí{žç¸'cÿ„ߦ,WÕ¿§ZøaÏ}:뇦¬¥„dL¯-æÚ¬/ Ì™G‘¥¤Âó *Ä⺚î|Ô ‚]›‚&ßsÜ÷ysðûƒÐgááÔHŸ†ßLræ}lV\Mðž™us0ãReÜZÓQ.5 ~!…A%RÆÔ¯!DT³• ½}ÏsÜDþ_…Ö%øQz  èf{ư|Õ×÷b9«­Y£j¹ð§Äj’,>ŽŠŽ|Fz‰)èè©!KÆ·ÈÁ@m}ÏsÜ÷«?ÀŸÑ/¬ßS¸÷«Þ®cýLÃ:ÛŽa¸~˜îYê1—aÃð¬6‰(£¤¢ `…ö´²"µšGw:±ç¹î{†“£ß†÷¦¾‡ú§²rm>`ªë£t¯èíN?Šcµ8Œ;“ šŸÎŠqïÖ3a4•,Ûœî'í{žç¸Ÿëwámé?Ô_z½êS¨ØF9SÔþ·ôoèN{«¤Æ¦¥£“ fX状iÑލЗÛ87]4Óžç¹î%ú-øCú6èV}%u§§¸&`¦ÏŠr!ÓŽ†ÏUOUMO–q7Äd+"d¢bqIí#Edzžç¹îY÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ÷=ÏsÜ¡/øPDxT½ô1jÚŠ(—Õ×AZ‘é飪g¨ÅnÄq,ôáQ¿yÁb<óÜ÷=Ëíç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žç¹î{žçÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/favicon.ico0100644 0000000 0000000 00000001376 15051152474 031211 0ustar00rootroot0000000 0000000  è( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ™™ÝÐ ™Ý]ЙÝD@ ™ÔDI™ÝÝDLÙ ÝDÝÙ™ÔDÝÙ ÔDÝÙ™ÝDMÙ™ ™ÝDÝÙ™ÝDMÙ™ ™ÔDÝ™ ÔMÙ™™ÝDÝ™ ÔDÝ ÝDÝ™™ÐM™™ÐM™ÐÕÙÐÕÙ;=™»X»X»ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÇÿÿÁÿÿ€ÿÿÿþ?ÿü?ÿøÿðÿàÿÿÀÿÿ€ÿÿÿþÿþ?ÿüÿÿøÿÿøÿÿðÿÿðÿÿà?ÿÿà?ÿÿÀÿÿÿƒÿÿÿƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/state_dia.dia0100755 0000000 0000000 00000005045 15051152474 031504 0ustar00rootroot0000000 0000000 ‹í]YoÛH~ׯ ”W¥Ýû=ÈNœ™A2'À¾ ´DËÜ¡H-EÛñ>ìoßjJŽÅKâ)سÃt³?v³»ê«£K?ÿò}å[÷n´ñÂà|L[n0^°<ûúá­ÿr1úyá9?ÁŸeä¬,¸#ؘŸÎÇ·q¼þéììááù'#ä{whãžý×ñ}ç /F–µßÁ‰smwÕ‰ãÈ»¾‹]+pVîùøÚ™ÿµŒÂ»`1Þ¶Úµ›‡~Y÷Ž>~s“|Æg»nÎRýè{í,ÝëÈuþ*ïÃGë&]¯Ý(Ûíjn¦1׌J&õËWŠ¿~ùüùòׯ?ÿ6ŠÜß¹›xcÁ?wîbД'Дl†4‘|Ð’Ç´dÍ/X8ÑË'`´Z…¶¶_wüƒ nnJV3am„%³ æ„ÙSÂá‚ mjÍà ˜¹Á"R³–.åæÊ VÎ|}˜"÷S--,fN…å6 ¥-»žùn°8¥Q ˆÊ~ ¤p¾|o%lp`x}3¼¢9p¼WÌñTk»,Ýk>´#•™Ø±BÚ†åÙ…˜bTôšÝnl³–p*kIöHñx&CJ©ÐÅ;1Åk>¸—Èðtk†G@—N8"}¼Ã6¹0ÜÈN"(ŸH„_…ßïýÇ« Ë{rùYO‘ÙÃÏzJüòyöéËÕÕÀ{gƒùÅ;ÁG+¦¬·>ǰ Š‚mУ(cŸ2‰,¼^SÖ[ާRŒ·W^šX6͇‰:ðO º¨÷L!üHCBzã4!³ÅשÝÓ$qˆc'ÍóʤÀøfÆÕ$`Û7¯Ó)%þ†c&6ΦÀi`ÉI’B¾ï^¨‹]¬dúg¡®)È(RX3F©-˜æ þn‰X r,JSÝ¢øJ‘סžß¡dÎ1PIÔ-ZRä¨àƒ¨]{¥T›w_ç¥Ðq”mwW¦Ô+Ѧ$LMçD‹YÍËèf> B;Çé‰B¤al€äÎY»™™à°p-[ÈbBw”öÕ!~ê7÷ÃkÁbðÝÅ$¥ŒÇb´ç±tY$Õ©bk²Øä9 c}ÊØ:O«-ìfhEı"ulWFIØÃDµ˜DÖ.Ì”õkòlÔ€Û+{4µKÚʺ“Ô°,b›äðÞE%` µåŽSÆ‘¤’Þ*ñ)xk'ˆë@\â:×bâjw"5Òœ˜|Ò¿'á Lš¢c ”mD”Ò†¿r{à¯í¿–­ïþ*Û$d×þÐAÂ0FÝgò1ŽyQ iRÃíªKÏÛj7¬*‘;ƒ½¿Ùi¯i[ifȇ@ÞÉy Ý.³¥¬§fóÓ%œ(SX„h:ù:©P±t2i}ŒK™ú,:e.¼Ò?³ü?áæ‚Okˆm‘Áo³Fsºá”)ùWøàœ0FŸgðO¯—Än×õ96™ïïo%ä(\¬0¼p b3Œœ >[ixÓÇ~&¹X>Ôú«iQ„‹8íÀ\†$ŸœÈàe3è¤PŸÿÂ’øyÿB÷þNÜñÊ?áI|<ÿ¡{ÿ'n?øåu(Õnt]këHYUãLº³,jΪÒ0_˜ª‚XØvëXð—jA¤Io.Ÿâ½Ô ³K¤ª  !wÛ‡˜€ùQÕ±€zPoøR_?è^ÿÉÛþ9Gü)/‡Ÿô/äíÇÿ®š÷ÄK§ßCÖ—´²ÇÛq…d€ªpûùf ¸ÉëY§çL¹ºÓ4[Ù…¾¢–-,‚ ŒÂåap È ''iÆ2Fz(Â’øyÿB÷þNÜñÊÏÔ>?†3¬ü8¹ŸMÔ¡Úí¦ÉpÏi|vR@ç;ˆb-Ûi;ǦÃ#Krü€rA^o¢[a|wñ ²À-u­*-VEH¶äI<¬âÎäã$·±'Ò(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Íþ›x?á3°Š}ò[ø–ì”yŒ’;B³%Žv7Ìz•<“šôŠòÿ5çÿ?¡—Ïе‰míu¸2M¶Ñ²9Õ•I ¤ã’Ë³Ó ž«x®-åŽh%@ñÉWR2#‚ç4¡äöl–ikqVY!óTsÈ+‘zuËÿÂ|nŸTm^Ûûa¯#»óVĈHž ¾W™¸ådlùÎ; WcEqz—€´µ‡Ô%¼´.×¶÷‚I,CϔѓH_äCåô#qäô:2øYŸÃ·ZZ^…’[÷¿ŽcB9¹ûB‚»¾`ädÕÑÑ@À&[xÖáÒI‚#Æ…›¤’{dãÔÔ”V?‰üO¥øGC›WÕçò­ãáUyy\ôDØàþD’$rqÏ ß´Lɱ»Ãá}’ª°%í!°Þ‡k)ÁìAï^‰\Ã}WWÖüU¯Á%®§â „”Y»míÑH…p¬AÏ8 p®ò€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (ªó_ÙÛÞ[YÍwwW[¾Ï ÈË´e¶©å°98é@(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠŽx!º·–Þâ(æ‚T)$r(eu#x Ž1\ü)/‡Ÿô/äíÇÿ¯@¢€<ÿþ—ÃÏú¿òvãÿŽQÿ Káçý ßù;qÿÇ+Ð( ðïÂßß|Gñ¦“s£o±Ó~ÃöH¾Õ0òüÈK?!òrFy'«°ÿ…%ðóþ…ïü¸ÿã”xKþJ÷Ä_û†é;W PŸÿÂ’øyÿB÷þNÜñʹ¥|&ð>‹ª[êV iwlûâwžY·cµØŒŽ ãƒ‚9»J(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯/ø‘¢iÞ#øà'V·ûEŒÿÚ>d[Ù7m…r¤Èƒ^¡^âßù+ß¿î'ÿ¤ë@Éáø}á øÆMB û¥³ñ.g JXL‹¼òaA9ä‚GñUt—š/xwRðÑGÚ— uk!*U–5Ár7@ yÏè”P=7VÓu›v¸Òõ KèÊ4–³,ªà•$g{й\«ðŸ@î/4»ðÖ©*mû^‘3B1·LjBìÈV!B’W¨äÕyï>%øVÞY&´Ó|_g 5¶lï_Å™0È@ÁP¨ ”õÍz%Åé¿<){pÖw·²hš„h^[-f3i$C#-òd‚Äàç×¥QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQ\ÎkËÉã‚Þ/$²*Üšá¿áI|<ÿ¡{ÿ'n?øåð¤¾нÿ“·ürºhF€–kg–q†ÈÿTÊTàü§ ÷Ínü;²ƒNð]…’iÍcqkÜmj`-8E Ü¿88È8ëÅu4PEPErþ!ø…ᯠ^ Ûÿ7S~#Óí#içwÀ*›T¬Û—ní¹Ï^µ†uÿøžáAÐ#ðö—"0mC[¸…Ê[«e]NòåXIÁô ç†ÖÞ[‹‰c†¼’HÁU ’Ià9ÍyÞ·ñ#BÖàŸHÐ4)üg1•ašÞbÖˆÛÆÓ,®¥’«Ëòg sV,~i×ÇyâíNûÅé)™úVX"bňŽvªŸ”;”íq]Å…ž™g…¤–±çd0F#EÉ$áG$“øÐ‘èŸ |Cý³ý­ 姃b’á.d²Ñ%šV™wnòæÞþP)’£jù›åÆ{´6ñDóI;¢id ÈxídõàèIEQEQ\ž·ñÚ5ÇØ£¹“UÕ º.›¥'Ún !ùÁU8R£$†#…lg ²Šóÿíoˆ¾#ãLÑ,|7bý.µY|û–þë¤)ÂH£’’dØÑÿ ßQÕ>oxã\Ôóû··´e±¶šñÉyÝœ°-H qŠì5=wGÑ<¯ímVÆÃÎÏ—ö»„‹~1œn#8Èéê+ŸÔþ*xHò¾Óâk<Üíû#œcÏ”o^øÏ8èj=7á/€ô«†žßÃVŽì…ºg¸\dF`qž¾¦ºM3BÑôO7û'J±°ó±æ}’Ý"ߌã;@Î2zúšãÿávü<ÿ¡‡ÿ$®?øÝðº¼üÁªÏîýû³·æÆ1÷zçÿÙß´?ß[ëÚW‰ãûòÛÞÙ‹)0¼ì…£%w>HËð^Ù© ø› ÄVž.е/ Îî#ûEʉlŒŒ~T[„ùI+ó@ µüƒN.þFýÙòÖON~FSÆzã­eÝøßC²š¦m@µÆß$Ç¥ÝH²–Mà#,d1Û“€r0sÐ×?ysskÿ \Ù:œ²k‘¤Ö"+7`Å­R-ŽÀb6 œï+€k_PÓn¡ ayM•òù햤鸞Ãq'¹´³m¯i×p^MÒ³Uk$FÑ‚‚A•e;Xc#§^*íµÌW–°Ý@ûá™HÛeHÈ8<ô5Â^èZµÞ§ã9íõJÊ)¶yPÃo-Î-P‰äùOoZëü?økJŠThäK8UÑÆ €Gc@´iZéΞ×–eKa<12är$t zö>§µ:ÛÅ:-æ¹oz$ÓdF³ß–2ÀÛŒœÓß§5ÃÙé"²øYmcÆ£=íý´vIe<)Ùï8v,±ïP©»–Î8êqI>›­ÚZëZ\š(†ÞæêÂúÖ;{ˆÓIJ®í‹‚añŽŒÇ± H²Ô-uå’Ò_1bšHí#ŒU‡#±g¥cZøßB½µ’î/¾Ëpצܤ^ZŒ–Ñ…<ÇžÙªÔ’ÆãPÒîlõ8î%Õ¯$F:lþQV™™[ÍÙ³çurº$wÃáÝÖ’÷#’ïûxFq£´q#ùM…Y<…$öyÎ{Сi¾)Ò5[µµ·žt¸t2GÕ¬¶ï"Ž¥DŠ¥‡#¦zÖÍq 4ž Ô¼0–z~¡ i“‹››»9-‚!ãòÔHªX±qÐ…<ô¦xëOº»Ö4¹f‚;%!™dŽ]2[ôY‰MŒÐÆÁ‰ÚÁ ÏLƒ@ÕæZ6«iicaj/o"ÖìcÓ¯.M»ÆÐ„õŽ %“$«–9Ê <šMGC¼oÞ™Á†Cwi·QèÓ\¼PªÇ…Ždp®àá•€ÎI; P®_Å×öÚ‡ÃËk'˜‘é÷Ð1ÚF4tqÏ£)éÅbXè-k5ž£›$zƒx’íåŸÊ>gÙÞYúœgË ©þï õ9ªÒiÖº_ÿÛG¥ýŠëÊÔžG†!,lfh°û@pôíÎ(¬ð'ü“Ï Ø*×ÿE-tÏøþI熿ìkÿ¢–º (¢£žxmm常–8`‰ É$ŒQ@É$žœÐ•Ïø“Æš…¼¸µ½×Ó`[Ø[©–æv9Ú1ÏÌT¨' ž2+“‡]ñÄ·#ÃrÉ¡øY.)ufâêþ0¤7ÙÔ¯îÆìçpF ²WYá¿è^ó%Ó­7_M“qpÆ[™Øãqy?1PÄ .yÀ lëþ?ñ=Â&ƒ Gáí.D`Ú†¶3p9 ”·Vʺäʰ’3‚Gð¶mMæx·Y×’gÌ–k!´´‘B€¡¡CÔ0Ý•#$ ޹ôJ(ŸÑ< áoy ¤è66óA»Ë¸òƒÌ»³ŸÞ¶\ðHäôã¥tQ@V¡®Þ.´tMŽöî(âá§¹ò"…°@X+’͵°ô‘ÅnÑ\ܾ"Ôä¿]2ÇEŽ]F;u¸»Ž{Ï.(™•FõF,ͱˆGœV|¿à†ãEI4éR;Ù§·¼gc$N‘Àou Ý(´¢¸Ý_Ç¿Ù6/vt·š8µfÓä /̱ª3¼ mç ¤íïëVuO›(µfµ±Žå¬šÑbÍÆÅ˜Ü2¨;‚ nÎps@MÍÜøƒVÓ­àKýÔê7—ÞÊÖÒøÊ%m¬ÌYÚ4ØU‰8<\ ƒWñ‰t}÷S›Ãº{-”\N£Ul2"îù“’x<0^ÜœðÕÑ\õž½©kiÚv§¦ZÛË}ó#[^4ÁV?/®èÓ“æ¦Þùã3Cø‚šÎ‰c|tÖ·¸¸¾ŠÒ[f›&! ÜŽoÌ •=R;PiEsPøUÔe¸›HÑ"ºÓààóå½òžfF*æ4ØAƒ ³.qéÍt´U{ë =NÎK;ûH.ídÆøgŒH‚Êžð«P}ð—Â7’_ØYÏ¢êM—šMÃÛ<\v(;+~^r{œÖyðçÄ_ YÌúŠ ×ã“a­ÛüûI,?~¬äè£qUùÝéPÄØln"´ñv…©xnwqÚ.TKddcò¢Ü'ÊI_˜’]¬ ⻈'†êÞ+‹ycš PÔ¿±îžQ$ºlì_OŸ, æ<nLaWh œ€@¢¹?xâ\_iw¶2i ÓÜ‹½2i²®xtlèAÜv*[¬ ?Æ^ Ô|G¬h𶓝ÿc_i>—/ØÖãwšª§†`ŽAëÛŸÿ—Ä?ú)ÿù@·ÿô (Ïÿáø‡ÿE?ÿ(ÿãGü"_ÿè§ÿåßükÐ( ?ÿ„Kâýÿü [ÿð‰|Cÿ¢Ÿÿ” ñ¯@¢€<ÿþ/ˆôSÿòoþ4Â%ñþŠþP-ÿƽ¼ÿUø±¥®¢4 YÏâ\àù:yýÌ`”žl óýá¸0Åh9ü3ãÛ[yn.>*Ç !y$“B¶UE$’[Îk‹þ×ø‡¬j?bðo§×ü¹|»‹ïìK{{(ppß¾9ÞÃ*ÛQ[*ÙãÚIðóRñsÃ?Ä-V;Ø¢}ñhú`hm#m¥r[ýd‡`II`2+¼±°³Ó,ã³°´‚ÒÖ<ì†Äh¹$œ(àd’ñ;]ǃâºé÷>‘uGоØ÷"Å%Š Ò¢#øWIÕ¼aý³c«}¯Ì‹û2}¾T;‡+’y ðGNù­ù¸_û•?öîÿÉ^øuÿq?ý'Zô (®û⿆¢¼’ËIûwˆo£Á’ßD¶k’¨@;÷ !PJƒ†8,:ฬýg\Òü=§=þ¯•ªäo™ñ¸€NÕY°dœp+‡{‰Þ-Šê‰l|bÙTòH¼¼#ho ),Ä2ípT}N¦›ð¯Â–W y{e&·¨H…%½Öd7rJ21ß&@A =rŸ?Ä«íjÞUð/…µ-eʆþæ?²Ù0Æ +ÈT¹WÀ)ò“†Áã4ø›Ä· ?Œ|U ´(Ë&¢n··`H^BwÈŒªr ¤¾‰Eaèðç…‘WEÑí-!O9St¬¥·Ò6]†qÁ' ô¹EQEQEV^¿â=#ÂÚ[jZÕôv–Âo`X³UA,zœxôµ+Ìü+¦ÃãÿËãÍI¤¸Óí®Z¹(ãBÜíÀ;ÙÕˆ \¸B Yx¯â]½ØÕe»ð¿†ew…,—÷qcÊÍŸ(nt™Y”äaiáï è^³6º™”m÷Ê^LFç9fÆãŒ“Œàq[PEBã]Ò-5ôûVÆÙ1²ÚK„Y=0¤äÐú*„ÚÞ“o©G¦ÍªYG}&6[= [=0¤äþU0ÔlL×P‹Ûs-¢†¹A*î„\gåyí@hª±êV3]%¬W¶ïpð‹„‰eRí8rW³§Åe?ú›‡ºEŽO÷Xœ«\xËÂÖ“n|K£Ã(ŠI}¶î#ØÐÝŠÞ0ðÂM/â=!e+"èƒ0` 7sA¹<þ$ЭuìëkN†û }šK¤Yy)9äÚ€4ê9à†êÞ[{ˆ£š P¤‘È¡•ÔŒAà‚8ÅIEyüß °õ_Àš‡öôØ3Ù:y¶WX,ÁY1ä7!ùW!TdÕÏ xõukˆ´/ZI£x¥QŒ–#*ÌàÉ € ?+u ¸ö•ÍøÏÁ–>2ÒÒ ä’ÖþÙüë B%µ”`†R8ÈÀäé(®_À!¸ñ…ÑõÙ«ØJö’d·1?*œðß.@Ý€N( ¢Š( Š( ?øÛÿ$‡]ÿ·ý(޽¼ÿãoü’wþÝÿô¢:ÍÔí£¹ñŠã¶ÒîfÖ_Pµ‘Û³¬.-à9óÄxêÙ#pãæé@¥EyŽ£ß'ŠÒ[âÐêI¨Í+Ü&3<ð–}ªn·ù~^¸RÜ·#5=¿‡ç·[}B×OxµWñ ã5Á„3€IÆ|² ŸîœƒÔæ€;]GY‹Nº†ÔÛ\\O<3Mp*’ÞX¨É1ÜíêEFž²û`½óµA/™æì:ÎÀsœy~fܳŒvÆ+ÌgÑöL1Ùh:Œzˆðõü:Œ†Ò@f¹h”|ÍŒJìÁðÃ9ìzWK®xyu=S[žãM{†OC«4E±0kƒòÓA”Æ>a¸zÐEyµk¶Þöm_K¼—XšöÅíoM»Hs+æã †óBA$ç9®çÅšZê×^‚[F¹¶MSÌvP‚Þq—ÿgqQÏ w ’Šòëýèx®ñdAnßj´Ë˜tY®UcÂG28HW*á•€$œƒQ­œVúæö­.é5³¯Ê×7Í*ËóŒ½#k®Ò˜PNÜt U¢¼ãñ_øDlÂö /HšÖâY­]#Ž`‘ ˆÃTFA²t]ãìë^ÙÞfË¢m4IÒiÝ€žrì³°aTrq€q@«umg-¤Wl{¹¼ˆFÒw¾Æ|qÓåF9chÝd>RʱÁ°@=°u5-?\×|%­é·±éÖ×W¶rÛÛ¤3<ˆ¥ã* ¹E=Hè¼ZÚþеiÔd—Ê´šI”DZÉ,¸sŒVT^3Ц‚öaw*-©¼¸ZË$8'~ÖPHœ`㊒}"â_èÚŠ¼^M•µÄ2)'q2yXÇ>Cž«—ƒÀZªøUá¹µYtñ z’îm³¤D²8å”– pçÐWa{¯išt“%ÝÚÄг¸*Ü#1UÇ’ÃIÇÔv¾%Òo É'¹6©°I‚QIµ‘”2Š[æ#¢€9mWÁz¥Ö…¨xr(4›­.æY嶸»vY4¥Ø•@Œ©‘¶Êqï]ýeOâM*ÞÑîd¹c\µ¦wi”QPÌx?t„ô¨_ÅÚ$v¶×kw[’â4ŽÞG“ä8}ȪYvžpO\PÝ“áV]sÃ:~§0ˆIsùYÛϦIãñ­j(¢Š(¢Šäü}᫽{FŽïG¸’×Ä[µÖ›qMÆ@¤‰n68ùH$„ä žñ'ü%Þ Òõ×%Ô_½@¸E%hÉùw+c'8Æy­‹ûë}3N¹¿¼“˵µ‰æ™ö“µÇ“€Jâþ A5·ÂM'ŠHœ¤®Ô©*Ó;)ç±R=Á€;Ê(¢€ (®Äž4м-åŨÝî¾›ÞÂÝL·3±ÎБŽ~b¥A8\ñ‘@qþ,ø‰¥øbuÓ­áŸX×eÜ"Ò´ñæM›òàd¢ãàœ€@8õ¶ñÿŽÝeÕ¤“ ¦ákc6u r¨Wt…v7g V꬧;‡iáï è^³6º™”m÷Ê^LFç9fÆãŒ“Œàq@œ>ñ_Íã+ù4}5݇ö™ DÜ@Ü)%Ã)ed\6Ÿ”äÓFÐô¿iÉa¤XAej¸;!Ln ¹Vl–9'šÐ¢€ (¢€<ÿþnþåOý»£Çº'Šo¼_á-[Ãö/6›öÏ2[÷"üÈÕFà§yÈ Ðpqž(ñ'†üaÿ ?x^MþACOxõ6›þz™ 1þï9õâø»ÿõ#äÝG'ÂÆ×žÏùš¸ôû§ŸÊ¦}gö^!ñ•޹shÞ|ð‘e! ,ëöXÆ?¼ÙéÀë^…EdxV «oè_+­äV$âC–Ô6O®sZôQ@U=WU±Ðô»OS¹ŽÚÎÝ7Ë+ôQüÉ'É$’h‹ð—ü•÷ ÿÒv¢¬|5±¸¸Ó¯¼]¨Çåê^$•nÊnʶñåxl';° ݆PqEPEPŸümÿ’C®ÿÛ¿þ”G]޵¥ÅÝÄ1ì–îA,í¸ìPz|¨£Jáþ6ÿÉ!×íßÿJ#§ßx~êâO^ÙY”Õæ}ºu˦¡Ä¹‰LËG#ñÀk}m§[ ‹¹<¸Œ‘Å»i?3¸Ez³øÕŠò&ðìsè÷­Ÿæ[«Ù<º|~šÕIŽáYÜ,Œþdž^ðJçpã'ŠÐ¼ˆÁc®XØh÷ëþ£ese:|ˆ‚–Êsò€›LO•8 Ç4é´W›ëžMBëÄ—2éRMq.µ§y2ùD·’ ‘£8ȹ—û§=8é¼;§ /Y×­íìþ˧™â’Þ4dY1(rƒ§QÎ;ç½%Ô>½ñŒvW2É.±K|¶­<ÞXUm«'—Ÿ,°8Ǹ޴[ÄZé—‹]bÒÞw·–O-¾YO-†1“‡ÈïÓšóákâOí/øJ±ü…üÿõ¯ö¯±ãìûaª}«çÄi¤]¾ð„*V"A+ó ŽG<ŠžïRЮôãª\ÊZÛM»lÈRAåL„Æx'‘ЊŠúÚwñþ‰r°ÈÐG§Þ£Ê•Vg¶* è ÚØð}+½ðîµ/…¼@Ñßjˆ$Õ.dM5-¢+*›’AŒÈA6C}8 R®zÓÆÚí”÷°Íx,à·k—¹—O¸Š#õ*ì[ØIì t5äöz'ˆãðg†ô¨ÍíÙ¹15Íê$ÛEgX·©bª˜läõ GMwM’ÓMºK Ðjl«há÷…¸íÇʤ󎞴£\ÓŽ›{¨ý£ýŦ[‰67Èb$IÆ2q´ôÎqÆk…Ó¬u˜$Ó4ËÍ-â!3Æ`Ý,Ko$38Ä…W!YÊtùGqJ×m…|Y¢6«뙵3®™pÉ v¦$ °äŽ{Ð]mâÍ*êÎêñ~ݵ¬âYn4ëˆTFIÐnàg&°¼Sâ#Uð?ˆímç.G»’8®­e·yDÙ*$U,93U"{‹ßë¶I{â û†Ò%D‚ûIkp­å‘µ›É$ dšÎñËëž×M¿HôÍ:ò{›‹»9-‚°ÍÅ*–%¤Œ/^”ÚxþI熿ìkÿ¢–º çü ÿ$óÃ_ö µÿÑK]Éø¿Á‹¯\XëZl‘Ùø“Kq%•Ûn à˜eÚAh›$r7pX7YEqþøgâ Ë#R·þÅñ¬¦)´«™sÁ`Ñ·b•ÊŽ:ô*ǰ¬?øKHñ]¼ ©C žÕÌ–—pHcžÚLpñ¸äppr2ªH8ÍÿÅsàÏú›ôDúE©@ƒÿŸj/³»¿a@Eqú7ÄÏ júŠirÜO¥jí€tíV¶™I *üß)fܤ(bH#ޏì(¢Š(ºð…Í×…®´Ó× 1ÚØéÌ|<›Z¸ŠÿÇZ¬šÜèâXôèÁŠÂÝÈÄc™ ’ë¹ó¹[ (/ÎŒŸ¹³Úx&Úï{\4Œ’jå8ª1²Ù$œ’UqµƒmôÈ †ÖÞ+{x£†$qÆ¡U à8ÅIYúι¥ø{N{ý^þ +UÈß3ãqª:³`(É8àP…cø‡ÅZ…,ÅÖ¹©Áe}Àä—“µY±¸gã9ñ›§øf+¯õ(nf¼Õ58îâržVrþb˜¶â3·#!Ezakz&¡}«éºž›¨ÛZOeÑbâѧWY6g"`ž§­d§‹u$¸‚ÂæÖØ_Ç«:èG¸£«[´ÊñäðHÙÁÎ9ði|)â«­hïu-!Y`2Üi‘Déufxù_s’Øä±yéVbðqCm<š“Ë|ºŸö•ÍÃD?|þKD(?*…*_»ß9© ðÞ¡>¹g©k:¥µá²ŽD ²òKoXÈK¶î;£<ãŠEÕ&º†Àh÷m DGûD0ÊʨæMÛXÊJíðN9ÓÒ¼;©é1[éÑkQÉ¢Û ‘[Ég™¼¡ÀŒË¿@Àû™ÀëÞªÃàË´²´ÑæÖ|ÝÒHÚ+_³bfHØ4q¼»°P^ˆ õÈZø÷QHMOIkEÔš ©Mz— p²È]â+»AÆsÕTãŒUgÁ)«Ûk±›æ†mJxî ™bɵ‘"HÁëógiÈã!ˆ÷  «ÏÞ®§ejú߇ôhæÒ`¾/¨ÄÍæI#8eOß'hõ<Ö´:®½¬_ßC¤M¥Åo§ºÁ$÷<Ÿh˜Æ®Û¸Ø :Œ’ÜçŽ9að®©k¨[^išµŒm› ƒ­ÞžÓ†–!†ÙSnwtç¥Y>Õmoî®ô­fÞÕ¯ŠIy¶FT2„dŒyŠP«Á,8ù£w{âø¼Oe¥&¡¡¢Þ[Ü\©m6W1Ú!°Ÿ† VY] ¹Ã-ê»|R·€C¥{’«èOUÒ¬uÍ.ãLÔí£¹³¸M’Äý0AÁr`Šááø­xI xÄ2Göoì`™í0X¨ËóÄsž2X…Üz’è”WŸÿÂ}®è?'‹ü}o pÚ–”Eå¶ÕÿY3óÃ`Æ{©­â7ƒµýƒNñ‹ÈòˆR¤òdw8ÀT“k6rÀäñÖ€:Š(¢€ (¢€ (¢€ (¬ýO]ÑôO+û[U±°ó³åý®á"ߌgˆÎ2:zŠÐ¢¼ÿþÞ©~ëº~«â;£òí²´tŽ< šIˆÕŽ~lbzr}—â/Š>Kë‹ i’ýø¬Ÿí7á~ë'›þ­w Ìåù}è ñ'4/ yqj7{¯¦À·°·S-Ììs´$cŸ˜©PN[‹«ç¸Ô®³öBòS,óe‹·A’yÀÀ'$fºŠ(¢Š(¢Š(¢Šóÿ¿òHußûwÿÒˆëÐ+Ïþ6ÿÉ!×íßÿJ#¯@ Š( Š( Š( Šòx[FñwÇXì5Ë?µÚÇá¡2§šñáÅÉå=þu±ÿ Káçý ßù;qÿÇ(«²ßlóõ?7ÌóvRçËÎsžfÝ¿ìãã­ZóÿøR_?è^ÿÉÛþ9Gü)/‡Ÿô/äíÇÿ @¢¼ÿþ—ÃÏú¿òvãÿŽQÿ Káçý ßù;qÿÇ(Ð+Ÿñßü“ÏÿØ*ëÿE5sÿð¤¾нÿ“·ürøR_?è^ÿÉÛþ9@?äžxkþÁV¿ú)k ªö6úfmag—kkC n'j(FO'µb€ (¢€ (¢€2õ¿ è¾#·ò5.Òù:!š Ía†ØÝPœTƒÀô®>?„ðè¯4Þñ.³ ;>øí–Qqh¬T+…þù*:³ppG@+Ñ( ?ÿ‹£¡ÐÄöÿ½eyqŸÎÚOâ«ýãGü,]gOýÖ¹ðïÄpÝ™WLD¿Œ§@Lˆ@ ~\p0{× Q@þ\ÜExŠ0ò8E2[LŠ 8å™QîH½t–>,ðÞ§y‡ˆ4«»©3²/c‘Û“…'øV…õ…ž§g%ý¤v²c|3Æ$FÁeOøW?}ðãÁZ…œ–³x_JHß0[,.0AáÐ;zt ¢ŠóÿøR_?è^ÿÉÛþ9QÁð’­íüoãX`‰Gz°UEÆ(Ñ(¯?ÿ…YÿSïŽðqÿØQÿ ³þ§ßÿàãÿ° @¢¼ÿþgýO¾9ÿÁÇÿaGü*ÏúŸ|sÿƒþ€=ŠóÿøUŸõ>øçÿý…F> ø^êáî5ÛgÄ”TŽMSPwh”p¥6œÄàçÛ9ï/¯ìôË9//îà´µæžA.H,x$ƹ½Kâw‚4«užãÄúk£8@-f œÊǸÇ\c§¨ªv?ü§ÞGu‡ y8Ë$Èr办½Çz×Acá? é—‘ÞXxJ´º;&‚Ê8Ýr8`22 rÿð¸<;yÿ sÄ;×ei²?‘Ÿ»¿~Ünù±ŒýÓGü$ÿµ_Ýé^ƒNŽošÞûWÔj'Pe‚1æ+ÆÐ~V<ð zçÿðŠxç[ç_ñ¿Ø-æÿ]c Úˆ¶cîùw™HV9ÙzUá'ƒ´©^æm3ûZú]æk½U¾Òò–mŘ7É»?Ä~N{Š(ª÷×özeœ——÷pZZÇóO $–< ’ãV( ;â¥â皇ºTw±Dû%Ö52ÐÚFÛC`/úÉ2¤6’¤äÕ‹…ÚlÚ§ö׋.äñ>±°'{¬£p _ 7 îù†á‚Mÿ ´ØuOí¯ ÝÉácaO:Ê%hNÐCÀß!^ÛóÇ$ ¯'Ä=KÂ/ ´¨ì¢•öE¬i…¦´‘¶–Á_õ‘œa@ î!ˆÀ D¢«ØßÙêvqÞX]Áwk&vM‚DlppA…X ²5]}4ÍJËO[ ËË«Èå–(í‚tfì—eïŒsúã:õËkö¥×ŒtIôÉű†Êð4ò[¢šß à2õÃóò÷ŠWñÖœ`ZYßÞ"Û «“K›XÉa™09Ê?Ê»›å–Ö²³æ&t ä#wcÎ(Ôž+-‘tmNK»•y!´U‹Ìh—nd9*¯Î£ CdãVçǺlVwmg¨^Á¾¸{x—ý#»Ã2ß#| 6œRx‹Áðk—V7€X5ͤOö"êFÚNP²À¨Á :ž¹¬½kÂ7¶º¨t‹¸ 7Qµ¸¶·°âRŠå|• û¶;Øc ÛŒó@‡Å–Êþïʹòì®!·m\–•"e#ž˜™sÓ¡ëÆRïŶvš„öæÒòH-¦Ž «ØÑL0Hûv«eƒ¾¹*¤ Ã$sYMàûË»9c‹S[[k絺¹…íwÈ$‰b ¼`  “ך–ëÀ–Òø†ãTŽ=%Öêdže½ÒÖâU`O—&á´ à†ÁÉhuHwØm%¹ò·mß± mÎ3Œg¸{øïSÓ­¯ìþy–·Q$пöìr0N ädÖºÿÉ<ñ/ý‚®¿ôSQàOù'žÿ°U¯þŠZçÿá-ø‡ÿDÃÿ+öÿáGü%¿ÿè˜å~ßü+Ð( ?ÿ„·âýü¯Ûÿ…ð–üCÿ¢aÿ•ûð¯@¢€<ÿþ߈ôL?ò¿oþÂ[ñþ‰‡þWíÿ½ŠóÿøK~!ÿÑ0ÿÊý¿øQÿ oÄ?ú&ù_·ÿ ô (Íæø‡â­3QÒ!×<ýk©jØ-ÇöÄSmyÊ‹“€ íÓ­z$ðCuo-½ÄQͨRHäPÊêF ðAb¸?Šó%Ø×cÿ³× PÝ|/]#TmkÀÚŒšù2["YìnÈÞvÉr —ÆW„uAæ¬Añmâ+iRh“»ˆ£Ôc&[ †'c,C¶×ÆÕ\±®ò«ßXYêvrYßÚAwk&7Ãþ YcÖ,ãxÌ–Á£™‚Êц^2ªF·©ç ö­ãmsK·kaaiqªZjimr‘†Û<›tC9W*¸–ÃÔsI­ø÷Q¶œ¶mk}oö›_,bÓA$LûH?|ˆþ^1Ï ÖÔe¸·¼¸ÔL÷ãR…ÌÞNÑ)´Jйù+ rǃœ“š§§ü>M7Vk¸u&6ãRKØmÌ?ê•c•< ÛºfRAÇŽô­ãiàÓµ«Ë l>Ïkö¶¹™XDzvP]þeȲ0G½X_Ïo O{­¢ë×/s ¥°ÓTÇ–FTUsæIÝ'Žâ  :奦¨#P¹‚âÙd¶Þ¶¢)0o\‚+€@íZóx~ÿSÑç±ÕõWÉÖóØY›sˆÁÕ°ÒI’Aì8ÇzQŒ<¢§RÐÕòH4ùˆœ®Ï8g·Í¸tû¼äai^)׎…á½_S›Lxu™"ݽ¤‘˜ ’FùŒ­¸üª3ߎxêtËMbµMVÞìmÚ‰ogäþÑ˹'è@ö¬Ûo,†´¶¼.ŠÑ1súð¼xÆ~\ïÏ~˜  è¼O®¦‰câk¨lvб´D´C ¬ªŽdݵˆÜ¤®Ñß㕟ÆwÖ¾)×´ÉííÖÖÚö €l¼ËÌÈüóÙÇÕb]¥•¦6³æè6’FÑZý›2FÁ£åÝ‚€ªô@H'®_¬ø%5{mv3|ÐÍ©OÄ,Y6²$I=~lí9d1ô¿¥]=öcy(Q$öñÊÁG²‚qíÍsÐø²èü?Óuƶ†MBüAP))šW¼œ ¶O^«ún­ec¦³SymÕ-|Ó¥\¬lTÎý›BñœîÆ9Î9¬‹?ê‘øvßA¼×-$±µDû4–ú{EÉ –ö^RF»·negbç8þ :à м+­M¬Ç<¬èú’ \>'¡cœ««;ÏiëÅa|ÿ’C¡ÛÇþ”I]— ^A¯Í­jš„Wl-P[Z mÜ‚îY³ß8‡5Ï|ÿ’C¡ÛÇþ”I@ì~1YêvqÞXx7ÆWv²gdÐi‚Dl>#ð«ð´ÿêBñÏþ ÿû:> É!пíãÿJ$¯@ ?ÿ…§ÿRŽðOÿÙÑÿ Oþ¤/ÿàŸÿ³¯@¢€<ÿþŸýH^9ÿÁ?ÿgGü-?ú¼sÿ‚þνŠóÿøZõ!xçÿÿýð´ÿêBñÏþ ÿû:ô (ÏÿáiÿÔ…ãŸüÿöu^ûãž™g%åÿƒ|eik7Í>˜#EÉe‹àd?ôŠóÿ¿òHußûwÿÒˆèÐ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šóÿ¿òHußûwÿÒˆëÐ+Ïþ6ÿÉ!×íßÿJ#¯@ Š( Š( Š( ?ÿ›…ÿ¹SÿnëÐ+ÏÿæáîTÿÛºô (¢Š(¢Š(¢Š(¢Š(¢ŠóÿÿÉ^øuÿq?ý'Zô óÿÿÉ^øuÿq?ý'Zô ã|_¥Í{tòÙhójgÙo©Xß%±¹Â¹2)* Î0ã“ÅeÛhºµ§‰o®õ +[½’w¶qs¦êIÉhå£óÓ?:·UuÉ @©.Ò»”Œƒƒ‚?/¯­´ÛFº»“Ë…YT¶Òyf 8õ PñOþd¯ûìözô óÿŠó%Ø×cÿ³× PEPTõ]*Ç\Òî4ÍNÚ;›;„Ù,OчóG €F«”P™Çà_ø&à\øW’çKG/'‡µ)s.]ŠÁ)ÆI`zž]›­Íâ&—}¨¦«Ã>ƒ®œ°j#gšr0É÷eRäªrÛIØV~³¡é~!ÓžÃW°‚öÕ²vL™ÚH#ržªØ' 0Fx4¡Ey|~ñÏ6Â)ªÿÂC£Gµ²5iÍ-qÜÀlµT ê<=ãý Ä7‡NI'Óõtûú^¥·¹\‚Ãä?{å¾Rp' ¢Š( 'ñ'…´o|uŽÃ\³û]¬~*y¯\PƒÑç[ð¤¾нÿ“·ürù¸_û•?öî½€<ÿþ—ÃÏú¿òvãÿŽQÿ Káçý ßù;qÿÇ+Ð( ?ÿ…%ðóþ…ïü¸ÿã”Â’øyÿB÷þNÜñÊô (ÏÿáI|<ÿ¡{ÿ'n?øåð¤¾нÿ“·ür½ŠóÿøR_?è^ÿÉÛþ9Gü)/‡Ÿô/äíÇÿ¯@¢€<ÿþ—ÃÏú¿òvãÿŽQÿ Káçý ßù;qÿÇ+Ð( ?ÿ…%ðóþ…ïü¸ÿã•Øhš&áÍ 'I·û=Œ¼¸·³íÜÅ,I<’y5¡EyÿÁ/ù$:ý¼éD•èçÿ¿äè_öñÿ¥Wx×¥Äví4k<ŠÎ‘–™WˆH—>™´%Q@Q@·Àñ$³GLþ\AØí‚vS€N`}(†â”g‚håUvŠ0`I §ÁØŠ’¼ÿãoü’wþÝÿô¢:ïe–8!y¦‘#Š5,îç  rI' ®ãS¬Ÿµ·F ¬-È`róã A¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ïþ6ÿÉ!×íßÿJ#¯@¯?øÛÿ$‡]ÿ·ý(޽€ (¢€ (¢€ (¢€<ÿþnþåOý»¯@¯?ÿ›…ÿ¹SÿnëÐ(¢Š(¢Š(¢Š(¢Š(¢Š(Ïü[ÿ%{á×ýÄÿôkÐ+Ïü[ÿ%{á×ýÄÿôkÐ(…ñ‘y/[QNµyhÚtP+éZŠÚë$ŒCfhÉeÇQÖ[CÖ5Mb ~;O*}/öe¤ï5ÆåvöɰùžK u׺¾›¦É wú…¥¬“¶ØVy• ‡ÑA<Ÿ¥=µ %ŠYZòÜG žTŽe\#ä ¤çƒ’=ÅsÞ‚ÿÄúÖ¥«ZNbžæÞkhšíü§Ù c-¾Æ!Ôýàzzb¨Ëá]BîÒÒÚh&‰.o¤hn|§XÏÚá‘Þ¼žzu®Éu}5õ6ÓSP´kô]Íj&S(¥3œ~¶¿£^ùŸeÕ¬'ò¢ó¤ò®Q¶GŒï8<.;ô ûÂú¥®›o¦Å§_^ÚØëkI­o+™áxä%Œ†D;Õßi%`çš¿ö}NÓSÐõ_ësEkÜRÁs}Ó©ÄU‹¼ä;[ø‰é]7‡üC¦øŸH‡RÓ.X¤Ef@êÏ ®¬3ÈÍjІ´ëÛyµ}KQ…-îu;±?ÙÕÃù(±$j ÂdãŽqÎ3[ôQ@ð³þg_ûï¿öJô óÿ…Ÿó:ÿØ×}ÿ²W PEP?ã¿ù'ž%ÿ°U×þŠjæ-®¿Ã¿-ÍäÖZL¶¶Ës¦"‰öbT‚Š\(,ì23]?Žÿäžx—þÁW_ú)¨ð'ü“Ï Ø*×ÿE-sW7/Lj.|9s%㧇`kIĦᘉn°C1%Èí’sÖ©Ë ¦¥‹â—::Ájï{y¨ÈcY¼Ã¾/´d´eÐØ?!#gê4P“\k³Ïk¤~øÙèÄ]+Ïq¯ÏRÌ®¡¼ ÎèAr í Ï]¸®½®u˜~KsÌwººiìÑÏmûÁ#8eÊÇ=Ojé.®¡²³žîáöAm$‚vªŒ“ÏAL´¿·¾2ý™¼²¡‰”|Ê`3ßN Šàï5M ô½fãPŽæå#–yuù¢HŸËvýäÀ–‹8û€ ¶ÑLðýΣªxrk©ŒÒ$¼C!²Çu/Ì@-ò¨ùˆõ#&½"Šñ[[ýB=F¶“RƒN¶ «XK>­%ŠˆmÌ6£ ˜a>F8Æ8;³Z> º3E© _Vš=U/ì’ÚÎ+†X¤„˜ e‹£©c!.FAÈÆ+Öh ?ø§ÿ2Wýv?û=zyÿÅ?ù’¿ìk±ÿÙëÐ(¢Š(¢Š(¢Š(¬?xGEñŽ–öÅ”sŒ±N `'toŒ©Ê¯±Æ#ŠÜ¢€<¾K_ˆ¾ß%…Çü&z"no³]¾Ëø—÷'>gðrw1à*(®ÃÃ~4мSæE§]í¾‡"âÂáLW00Æàñž~RÁI\ñ“]sþ$ð^…âŸ.]FÓmô86÷öìb¹†v”sò–,ÊçœçÿæáîTÿÛºô ó?ø+ÅzÄfÕuÝf=jÍtsaoxTG*…’6 "õ$Ÿ3æËŒ±^™@Q@Q@Q@Q@Q@Q@GðûR¹Òþh2ÚÊI-ÏÙÍèed¼d.Aã€ÜgŒãnyæ€8›-VûQÔ4ËmA§itÿ›ek‘”©±’OŸÉ&=À¹/`2ȪöÚ¶«ÔzFœ/Q./õiÚK!neb—dy œ“€OLc“^‰o£iv¶›gI<åÀ«µöìÜ08m¿.z㎔ۭH½µ6·z]•Ź‘¦1KnŽ…Ø’[cq$’zäšóýWS×õ-Z‹Q¿].[O4‘:Ü;‰C?6÷`Œ0Iäð*O‹?òBµúáiÿ£¢®ê}FºŽî4› ’Ù ÉlŒ#_Ep=…qÿ‘cø=­¢(UQn€Ÿz Q@Q@Q@Q@Q@Îf[yZÞ8äœ!1¤ŽQY±À, g¾= y^Ÿñ[ÅZ¦¹¢Zü<Χaƒqk&µn ÿ ƒrò>eÈù”çæõŠãüoàøHþÍ«i7_Ùž&Ó¾k AG×÷rpwFrF0q“Á•€3ÿá-ø‡ÿDÃÿ+öÿáGü%¿ÿè˜å~ßü+CÁ7ÿ„í:N­ký™âm;å¿ÓØý?y'tg ç'U›° ñ´ÿücá íþߨþÕåþÿûjÞM»dWû¼g;q×½{PEPEPEP›ø’×ÅZgÅ8üK¡øgûjÕ´A`Ãíñ[m<È~ÿ'ÝúñV?á-ø‡ÿDÃÿ+öÿá^Eyÿü%¿ÿè˜å~ßü*9üiãÛ[yn.>Ç !y$“Ä6ʨ d’HÀsšô ç†ÖÞ[‹‰c†¼’HÁU ’Ià9Íy_úgÆ]Gþ[ÙøÖ_xäÖ$Sø„õÈþ÷ú°ƒáߎµOY˨Ëá¿ì½0ea¸{Ï0Îàà…_-~QÎ[=xÃmî*8 †ÖÞ+{x£†$qÆ¡U à8ÅI@Q@Q@Q@âßù+ß¿î'ÿ¤ë^^âßù+ß¿î'ÿ¤ë^@‰¼;©]x†úê8õ+›BÂ;7ŽÁ­7(Rû•¾Ð>éÞPç ät4ù<5©/Š- Š&}g·¾»’IT·ŸU#©,VÈýÑõÕÞkZVŸ8‚÷S²¶˜…"9§Tb¼žJ=p})Ójúm¾¡Ÿ6¡iìÃ1[<ʲ8ÿeIÉü(‰Ò|1©ZëÃzº¬°Á©Í}ÄOh-Évr ù|ýÛ_i|b®øwÃ×Úm·ƒRK5…´ý:H.²üŽÉGœ²“ÆFFk§“[Ò¢¿jvIx\F-ÚáD…ˆ.Üç8 ãÐZ¥â>ÓT´Óa¸¶¹¼šémå.Ì€2³ne?Â:ã¯ZÎð“êº^—¢hZ-ÄFÞÔÃ5ËȆ1å*Ë´¶Cà6ÖÀ'WYY1ø‹N‹K³¼Ô¯ôë3s˜fñ dq’®pAeù€î=jk­wH±ò>תØÛý¡wÃæÜ"y‹Œåryô  )ÖDWF ¬2‚)hÏþÌëÿc]÷þÉ^^ð³þg_ûï¿öJô (¢Šçüwÿ$óÄ¿ö ºÿÑM\šÞ%§Ãχ^}ýÅ•œ¢Ò;™a™¢Ìc̤¹“ÛÈÆGYã¿ù'ž%ÿ°U×þŠj§á-2ÿø*yZEk+[˜‚o³ðÜtÄÓ@}Ʊ|Ò[#ê$xpÍv-onµilÖm­—ºåU™€Ì»rFð¹Ëcž‚Þ;ÝJû¶Z†­<«&™w%Ä–rF—%ZÜ#o]¤ðÄäÔö$òŠñ˽RKï Þ6¹­]Àë `á£ûT¿½Y P@™¾XÁRÎìÖÌ·w­¡ëQGu MSJ‚'ŽR$u³ Ðí‘Ðî9êkÒ¨ 5Ô.ŸNÖgÓ.µ[Û}=N1qp÷²‰ذC9mê†@9Ü98ÈÑxé®ô;·ûmÅäK¨Ü¤3\9f1‰NO$c­V140ÛV]:ie °´aݰNÐ$VVà1ÆžÔºN•§­œ,ƒ{ÈòÊÛžGv,ÌÇÔ’Oaé@¨¢ŠóÿŠó%Ø×cÿ³× WŸüSÿ™+þÆ»ýž½€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€<ÀúÖ•û=h÷vwki0¹Xü÷PÁïö1 ñ¬kgTñ·a«¾…owuxú(~Û[‰Ðé ­ü ЬžAý£Î$¦ðDw¦M¸Èë³ÙïÒ½ 4=1ôÔÒlÁÎæµÈ"cêS?•rçPñ±Ðínne±º¹Õäµ’fHW€C3©`»‘_ä\㌎˜8¨,umgQÕcÑ_Y{_!¯‹Þˆb2\y2¢  ®Á…|¶Ð3µÙÛéZu¤ð[iö°ÃlæH#ŽU‰ˆ •|§ Ã#±>´ÛJ»Ë¹Ó,¦O4ͶH‡˜z¾ûÇ×­y¶ƒ­ßÙø&ú[kˆÕ­<)ä‘©Û.n~lI"ðr8éÉëz5Ûéí«MÌVl.å†1öS,þS²€ ÁCg©"»EÒ4Ô†HWN´IÙÝ+µ¢çä#¯ÌÜtù­G©iq^Ú\$qY‹‰aò|Ë‹a2”Îv²än^¼du  ¯ ^^Ë«ëú}Þ¦u°¹Ž(¤hÑX± °NXöA]-aøgéáë{¡¾Ý¥º˜JâÚØ[€(UTŒµ@_RrIÍnP^ñ·þI»ÿnÿúQzyÿÆßù$:ïý»ÿéDtèQEQEQEQEQEQEqþ7ðGü$fÕ´›¯ìÏiß5† £ëû¹8;£9#8Éà‚ÊÇ‚NèÎAÎN29 «7a\üÿ Ùµm&ëû3ÄÚwÍa¨(úþîNèÎHÆ2x ²°aErþñMlj4ë˜u[ìíwN—ìú…‘`v>£$ùnTž¸8,ãÔPEPEPEPQÏ<6¶òÜ\K0D…ä’F ¨ d’OÎhžxmm常–8`‰ É$ŒQ@É$žœ×•ÿ¦|eÔ彟€-e÷ŽMbE?XA\ï«?Ó>2ê?òÞÏÀ²ûÇ&±"ŸÀ¬ ®G÷¿Õú¤Cko½¼QÃH8ãPªŠpbˆ †ÖÞ+{x£†$qÆ¡U à8ÅI@Q@Q@Q@Q@âßù+ß¿î'ÿ¤ë^^âßù+ß¿î'ÿ¤ë^@¸·oã}f+MûIî4-ð$6o–à ÛÈù9ÆOƒÛ>/jÖ·3Ù]¾«yms5´¦âÍíf(â\Èe_4Ñäl'ƒÆk½»¿ÑôËøÚòîÂÖòè,Q™¤Hä˜)8Qž[ŽûGÖœúÖ•ø°“S²KÂâ1nÓ¨±…Ûœçz@Æ£áëéÓÅ/𙝵IíÛr‚é[óœñµ’LgŽ:Š©ƒªEs¤Yx²Öç¾—P2ǵãÊw»~ãæ( ŽÜd]¤Ú¾›o¨E§Í¨ZE{0ÌVÏ2¬Ž?ÙRr …¼A¢£N­«Ø)· ÓƒrƒÊ ÛInxÃ|§=ø sÃþ¿´o5墩Ó4y­ç%Õ¼©›È rA‘‘ǽsšN¡áï jâ÷JK„Ÿ@¬LñiåùÜ0-‡z‘³qÈÆ:W}}ã/ØA§\KªZ5®¡9‚„¸ŒÅŒÅ‹nÆÑ·å”w¨n®|5sZ®¼š42Cmsq,R*ä ®Àmo—%G äsŠÛÓ‘£Ó-ÁVXP{¢¬Õ+ÝgKÓ‘ûR³µYº4óªQŒ‘“È—ŸqëLŸ]Ò-§¶‚}VÆ)®€6ñÉpŠÓР'-ŸjãþÌëÿc]÷þÉ^^ð³þg_ûï¿öJô (¢Šçüwÿ$óÄ¿ö ºÿÑM\Ý‚Ë7¾Ú%Ýͼw"Ö9¼Í: X©*AÁ*? FºOÿÉ<ñ/ý‚®¿ôSQàOù'žÿ°U¯þŠZã¯ï58|UyfuH,§‚êôä¼Ö&‹Ì€,yÄ\n;ÁbųýÜf– P¶·¥‰5«Ñ­I®M í‡ÚŸbB<í€Åª›Dd6ìç&½B³®m,µ£i0¸Þ,®ÌªapG˜›«uèK8 Šâ|;â ®ÿá²P{F="a LXý¡!‰¹ûû·}îzÖN‹¨ê—vÏâ[K+×ÓeþÐóuYg–ˆv£Qo±²0¤ ø½vŠòF»Óo­´©nîæŠÆÇZ =üZìóÛÖ²|Ép̤ ÅTóò’F~c¿ jQÍd51«I(Åž©,ËV[sòD@!C¸>µèPEPŸüSÿ™+þÆ»ýž½¼ÿâŸüÉ_ö5ØÿìõèQEQEQEQEQEQEQEQEQEQEQEQEä^¼½±ø¢K§\ {§»HVR‰/ö7ƒÃÜÖu/C®\èúmÆ£<–V qª–yݤæ_0§îÆÀ¿»õÉÎ2σ6ðÜüУžå@Ó>Ù0Ü·.Êp{‚±×m¨hºV®b:–™gzb9í0,›¶àq@6£â lÛêz€Õ†Ÿ%–¡if4ñN¤Iä–%ˆ,Xù‚0:MiAâóo¦ /ΟÄWV. ¨- Ip1ŽÊ‰Ï^3žyÙ×<-§kQo6Ö±^ƒËϳ«HŠŽ¯´1×½^þÄÒ~Þoÿ³,¾ØX9¸û:ù…€À;±œãŠâô½{ÄÚŽ¥ö¸ã¹û7ö„ö²Å/ÙVÚ(ÑÎÿ;x*¤ä`äá@Á¬ÍFïT¹ÐdÓu}OS‡UK:y’ÔÄÝ"—…‘É»æùFxÎ}´-!õ#©6•b×ä`Ýt2‘Œcv3ÓŽµ¿‡4;K[‹[mN†Úç‰áŽÕ%ÿx†ühžŠ÷VÓ|S¦¥%Œ×ÚÒHÒÙ¡”ùyÛ.H²nr¸^Jí+2ÓÃz…Ú]Ùèºu½Ê.ÅšTGUÆ0 Ž+N€ óÿ¿òHußûwÿÒˆëÐ+Ïþ6ÿÉ!×íßÿJ# @¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(›ñ/†¦Ôn Ö´[ˆì|Ed…mî\ñç& Àå¢cø©ù—œƒcÃ^%‡Ä6ó£ÛÉcªY8ŠÿN˜ƒ%´˜ÈäpÈÕqÃGp7+‹ñdžµ{»‹?xZâ8òí É=:סO«iÖ·‘ÙÜj°ÝI·d2Lªí¸¸RrrU€õÁô¬Û¯xwK³Öoã¼±‘ì‘®/’ÚXÌ»•B€Ã#æ!UFìt€9»ÿ jRxƒT®«5–¡{ ÒÉföbÆÊ¾h*S#a†’ó_Ñ´òî¯alJ šå*s†äô;[Ÿcé@M–‡¯YÊ÷òi÷—F=|j ’Û åˆÙù$ü…# ½‰##¡<žLöš6¯¦jQj¢½úùº’ý‘eˆ: î|Äq¹‚à¨Ãs‘‘ÁäWkeªiú‘”Xß[][l¢ •öFÁàýjÝqš'†¯tû¯ }ªä]7L¹‚WVb‘ÌUsÉUÔ:Ù¬[}#\Ðl,f³Óµo!¶†;¦ šÙâØíxòe%U›o” $ÏJôÚ(ÃüñKÁº'ü%Ú:Ï“öï]ÞÛÿ¢ÌÛá}»[„8ÎÚ»ø]¿?èaÿÉ+þ7UüOáSÃäÞ5ðTmÄœêú2ðš‚K &'ÎI’Ë'q¡ë6~!Ðìµ{ßkwÊ™ •ÏUlFx Šãÿávü<ÿ¡‡ÿ$®?øÝð»~ÐÃÿ’Wün½Šò|_ð&§àÝrÂÏ]ó.®´ûˆaO²N7;FÁFJ`d‘Öµ,Y¼ ðÞÑ.îmã¹±ÌmæhÙÐXÊÅIR Qù0@5è•ÅÌ–ïqs4pÁÜòJÁUG©'@gy©Ãâ«Ë3ªAe<PG§%æ±4^dcÎ ØÂãqÞ -Ÿîã4Ý*æÇD»€VX >#»”sj2Å }«Ê2#9 ¬ÍÜ@ÜJ’Iæ½ wGºÓäÔ-õ[¬£ÿYsÂ4iõ`p)_ѤÒßTMZÁ´ô8kµ¹Cœ‚ùÀä×½q0ßO¬Á¦*jw¢ÞëÄ×P™!¸t/ÇpÁŸ*「pit±sgýŸxº–£+¿ˆ®¬JOy$‰ä g@…Yˆ8ؤ1ù¸ëŽ+¾šþÎÙ¤YîàˆÇÃÈlc«œôQëÒˆïìåÆ;¸].ûr²ç.3”çæç#µX¢²¬¤õnû[Òt¹¢‡PÔì­%›ýR\\$lÿî‚F á~1_[éšw„ïï$òím|Kg4Ï´¨¢BÇ“€J±ÿ ·áçý ?ù%qÿÆë¹ûu§ÛþÁö¨>Øbó¾Ïæ3ËÎ7íë·=>ßO´ŠÊ&´pªÆŒx!@À!¾lúóÖ›¨hºV®b:–™gzb9í0,›¶àq@wžŸ{âMu+ˆdmgMI­ŠÂѪÊ-ùÙž™A Œ õæ´õ}S_øH¢±¿XäµÔ´ûXËWò„­pF9ϘÝyçŒWS6‡¤\ÜIq>—e,òEä<¯nŒÍ÷ #%}ºRÁ¢éVÐ4úe”P³¤†8àURÊAVÀÈ {`P=þ»a&½?öõÄñiZ¬Å,~õ gUÿ–­‚»qß=+Ðj»ØYH'i Ýd˜4`ùŒ›ŽH  ýÑéV(¢Š(¯?øÛÿ$‡]ÿ·ý(޽«ßXYêvrYßÚAwk&7Ãøƒ¬ÚÝê^·O´x‡OµŒ,$'ix[8K àa÷^áðÝ[Åqo,sA*ŽHØ2º‘AG9  +”ñ¨Ž9t»Ô/¤Úêeè+¹PyRÝÇ÷VB„úp{WWQKs»D³Mm3ùq‡` ¶ Ú3Ôàèh†×µ=QŸHÕ-e¶¼Ò-u%}J{uýÔ‚&v ªå äíàœb±¼FÖÜúåæ›å\è÷ ¦Ás4X0ÜÍö±œÄG¨¸ô½GWÓ4ˆÒMOQ´²ÎÕk™Ö0ÇÐ#4ÛÝkJÓm#º¾Ôì­m¥Ç—4óª#ç‘‚N yuËÜ[Kâ}ì»Í¥øfæ•úÍɉóÜíùOûHÕ¡¤FúG¼= 2·ÙQ..줋æþùãû¬ž•èZî‘aeíæ«coi6<©æ¸DGÏ#kƒøU¸gŠæž RX¤PÉ"0e`zGQ@CáýNïQøhú>â-âù´‰ :Ðm¼WØx'Í?7Q÷>•ßhzÏ…oþÇe¤IfÓAî­£Aæ[(!—ŒöÁÇ¥t”PšJøw©üŒ’ÿéÒ¯.¡¢èÚÿŠSÄÏS^Ì­ÚW?jµòQV8Áûøa (2rsŽk½¢€<{OÓu;;½?Phf:¾ ÙÎ ndx¼ÉÄŸV1ñþò©í]×ÃÉ£¸ð&™W—•ÏDAÝŽäI EsY³ðö‡{«ß¾Ë[HšWÁ¶:*ä€Xœ3É Wá jž'×!ñ¯`ò®#çHÑ›”ÓÐòÁë1À<Ž0 …XÀ,x;F×u­sþo'Ù®š&‹KÒið¶2ÌHÏœÀOƒÔ"zPEPEPEPEPEPEPEPEP›ø5Ok“x³E³ûW‡®~}sM€|èýîãÆìcxÎܶr];Í+U±×4»}OL¹ŽæÎá7Å*taüÁ ƒÈ ƒ‚*åy^«¥_|+Õ.S‚GTx_ÃË©i.…¦ ÿ0Ëö¡i›¿9Ý¿Ͼh—ðž‰áKÂÚ^¥¨[Ú]jWq(¼¹¹ÁšYØbXœžOÌYLgŒqŒVDþÐlì~#Om¢éÐÍh²¥´‘Ú¢´*tø‰@ùA,ÇïZôUд„ÔΦºUŠê­Ð·A)ÿã?­Xk 7K¤kH ÝÿÇÈ1ŒMò…ùÿ½ò€¼öP–Ÿ¬ø·ì:ü0ÜÛC¤[KcmrFÄ—HðXb1žªéšæV Ûû­ ]2i&“M—U—IvŸ:(åˆ*=U“t`úkÕoôm/T‚85 6ÎîŽcŽâ‘Sè8©ÖÎÕd‚E¶„<c…‚ ƧU=Ú¼Aé@ÇÃýBßVÓu]BÕ‹As©Í*d`à…8#±ìEuÕ“>™wj4Ó4äwyfY, ‚Gc’ß$‰‚yÉ9ÍiÂ%X#º<Á@‘ãBŠÍŽHRI=²~¦€yáµ·–ââXá‚$/$’0UE$’xs^gcuyñ_\Žä[ù~±”•IЃ¬L¹Á+ÇîQ€8<¸ œˆëÿ¦|eÔ彟€-e÷ŽMbE?XA\ï«õH †ÖÞ+{x£†$qÆ¡U à8ÅIEPEPEPEPEÏø“Æš…¼¸µ½×Ó`[Ø[©–æv9Ú1ÏÌT¨' ž2( ª÷×özeœ——÷pZZÇóO $–< ’ã\“|Hñ[Ã.ž-øm!Äz‚[iE¡ á–90ý™Pîë…ÇP¥{H>&Cko½¿ÃßÃH8ãÑBª(ø1^‰Eyÿü.ÙÿÈ~Ç\ðöïõ?Úºl‰çãïlÙ»;~\çxWI¥xËÃ:ãÛŦkúmÌ÷ ¾(å|Öw«Îà@É ŒŒã¹\¾³ðçÁÚþó¨øvÅäyLÏ41ù2;œä³Çµ›9$äòyë@EçsxÄzðO‹nâEuÆ™¬7Úm|µa¶$b Äw—$£#«|C›E¸ŠÃÇZTš$îâ(õÉ–Âá‰Àă˜ËíµñµW,h¼¢£‚xn­â¸·–9 •Ç$l]HÈ Ž#œÔ”QEQEQEOUÒ¬uÍ.ãLÔí£¹³¸M’Äý0AÁr`Šó}+U¾øWª[øsÄw2\øVáü½#Y—­©ío9èuº;("?Tªz®•c®iwf§mÍÂl–'èÃù‚#@#PÊ+Íü0ú§€5È|'­^}«Ã×?&‡©N~t~Ö’cv3°œgn9ž‘@o‹ô¹¯nž[-þmCìû-õ+ä¶1·8W&E%A9Ærx¬»mV´ñ-õÞ¡¥kw²NöÎ.tÝI Ù ´~zgçVê§Šêuµ–´t›mRÔnVÝn\Ú˜¢32Œ™$Nr‡¦i·ž.±ÓÞî»{¨®­Ò'Kbªd¸óÔa°Ç~Pò0G<` Oi7÷¼ÓèÚ-í¦¦òÄWU‚ù#ÀÛ“4aòøPW€9ƒ¤ß[ÜiZ-î”~Úe½¸ûr[ˆ¹Ýûç.Ü”R Î}tï|mka¨Þ[M¦j?g²–(n¯Ub1DÒ*²äoÞGι!H%ÇŒma´Žh4ûë¹$Ô¤Óc‚v•7äüî«·÷mÉ9éÅaØèzÖ‹¬mŧ5Ù:„rZ¤Ñ‰§¤ˆY‚çäRG ê1CøkRÔõÔ¯´ôE¼ÕÒyìÚEo&Ùmd„o ᘓÈ\ýà9Á5²þ5µm½ÔZf£4ó_6Ÿö$XÖd™C­¹Âpœ†#‘Š}Ç‹%¶–ÆÝü9«›ÛÁ+%¨kmê±íÜÌ|í¸ùÆ0Äõâ€(ø'DÕômCYSMІ†;ƒ c<1© Ì3ÁJƒž¤Þ»Ä·—EñŽžÂëNŽámç1Ëkl¬ÐJ£U²ÃGPÜÔ³²µÓ­RÖÊÚkxó²#‹““€8’ž¼ßÄþ'Õ j?òÞóÀRûÉ&#Ä´$Ÿ®O÷¿ÖzÅp)â ôÃjrêZf›-”ºM†®­maxäÛN~ÏûÈ8ÏÊ®ÛÀ€`F8âÒÙAâ/ ÛYØØi–?ÙÚœ‘ͤÍ&ë;§U`È •ˇ/y^+µ›AÑî4èôéô›lcû–ÏlýŒ§µ$º>››6“a%„g)jöÈb_¢‘Ôö  ï]ÚÜèÒÃk¦Ã§ ;©m¤¶·pñ+©çË`WŸAÜ`bº*†ÒÒÚÂÙ-¬íâ··Œa"…*`85QEQEæsës|MñºæÚ9M_RŠBŸmlÇ´,9(N7‘Œ®p@ ½}WU¾ø©ª\xs×2[xVÝü½_Y‹­ÑïoèAyº{©OHÒ´«K·Ó4Ëhí¬íÓdQ'EÌ’rI<’I9&€,A6¶ñ[ÛÅ0D#Ž5 ¨ `Æ*J( Š( Š( Š( Š+ËõËíSâV¹{áMIì<9c+[ëz ]¯;ŽÚþLßû.€.<]®øßQ¼Ò|Ð[XÙÊ‘ÜøŽp$v~xàŒ©60w·é¹ôžð6‹á•ìb’âþww¸ÔnÈ’êrìî“ã p022y$+J±Ðô»}3L¶ŽÚÎÝ6EtQüÉ'$“É$“’jåQEQEQEQEðCuo-½ÄQͨRHäPÊêF ðAb¤¢€<îjþ¿—Tð ñ­´÷âûÃ÷.Þ“¶“ “ï·%s…@§ ðgŒì|e¥¼ðG%­ý³ù7ú|üKk(È*À€qpp3ƒÀ ÒWã?_Iª'‹ü!$v¾(¶M¯q£Æb”d àŽ€0¬€åÏø;Åvþ0Ðþß´öwÊÖ×–“©o:ã|g gûòÈQEQEQEgëš5Ÿˆt;Ý"ý7ÚÝÄÑ>%sÑ— €ÀàƒŽ¸ xŸTðƹ‚¼k?›q'F²Ü& ƒ€ŽOI†@äó $«Iéâ i~.ÐæÒ5x<Ûy9V^'f?™H ‹É|jÚˆÒu«ËFÓ¢_JÔVÔ‡Y$b3FHÃ.:Ž´jÚ±ªkkñÚySè³-'xÙ®7(ó·¶H‡È¼ðÊXðj†ë–^Öäžÿ×ҭ¾‰ªÜð9ám¦Çä­ÿ²äGêQEQETsÏ ­¼·Ç !y$‘‚ª($“Àsš’¼Ÿý3ã.£ÿ-ìük/¼rk)ü Âúä{ýX‹«ÏŠúäw"ßË𠌤ªN„beÎ ^?rŒÁà•ÁäGêCko½¼QÃH8ãPªŠpb¤ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( £žn­å·¸Š9 • IŠ]HÁ#ŒT”P™Á­ÍðËÄh:Ϙ<vá4JY ý‰±ÿÓ1ä 9ØNp¸É ž™Tõ]*Ç\Òî4ÍNÚ;›;„Ù,OчóG €F¯7Òµ[ï…z¥¿‡ð‚Ke«ÆÓ]Ú¬º„³FRpIÈ$H\†÷_­zJiö©©Í©,X»šäÜyD,Ê1œp]¹ÆyúUXô 26ÇOŽ×Ö’[Gæ7ÈÉ÷Ns“|Ð^-š[oë“Á+Å4z}ÃÇ"1VF±È ÷®.ëRMÁ’3cH¸Ôä·²K­_T2…2}éQšg µ7·ðò§XÕ#–ÃXÒtÄÓ®bx® ”ŽåYH %:çxc9«òi3\XÏ$¤± -‰cˆ÷.ÒqœŽ2sÔúšátŸË£øf8uCu%¶¸ÚuÍÄSï+Smf`~`Ê#sî}ªý•ýãükö»œÞÿbK/Ú ‡ÌÞ"bw\çœ×HÞÒŸP7æÐ “:\–Àyˆ†5lŒíb:sÆz R´ðN‡ei-¤1ß}’HÙ­äÔ®^/-† „i ŽP±@•ÊKƒ§š+oiòÊÖQ´÷º³É¼<ñ†òÈÊ äm85ÒØÀú/-ô»}Bò{+½>{†‚îáî'ŽHT2»’Øa+dGÊ1Þ¬è¿ašÉÿ´e·˜ d›Uº“X2•Ý!ØAPr¸ ‹ÀÞ.i.®Ý´]T•¿‰J¿]²ªŽs×¹' þ‰YzþcâM-¬/Ö@‰ažÙ-¼«ÊËõWSÐýAÈ$ J+‡ð§Œn\¸ð‰ÿqâ\˜&d¦§ÎÙ£–@xÁÇFTî(®Æþ+Õ-uo xVÛÏñ.¡š²È¿¹±ƒ%Lò0pAsÈä•^ÇüoÿçÙ´&×ûOÄÚËa§©úþòNFØÆ ÎFpy3.§†¼5‡­çw¸’ûT½q-þ£0K™1ÀáQG ƒ…ä€Wðgƒ,|¥¼I%ÕýËù×ú„üËu)É,Ä’q’p2q“É$“ÒQEQEQEQESÕt«sK¸Ó5;hîlîd±?FÌpA‚"¸}ÅÞñ*ø/ŲHL®F‰«Lû…ôYÂÇ#à<8Ž;•/è•â i~.ÐæÒ5x<Ûy9V^'f?™H W›øcÄú§†5È|ãYüÛ‰8Ò5–á5rzL2'œ€I%ZOH Š+Êõ]Vû⦩qáÏ\Émá[wòõ}f.·G½¼¡}æèAî¤ ,O­Íñ7Äè:7˜|hå5}J) }µ±ÿаä¡8ÞF2¹Á‚þ‘ÚÛÅoopÁŽ8Ô*¢€ªúV•c¡évúf™mµºlŠ$è£ù’NI'’I'$ÕÊ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š*ž«¥Xëš]Æ™©ÛGsgp›%‰ú0þ`ƒ‚äÁrŠóýÄ÷ñBx/ÄÓÏ"Ëì]^çíÉû©qç);sÆþ ²ïô Çñ?†4¿hsi¼m¼œ«/ŽŽ‡³ ŸÌ‚$?Ã'Õ<1®Cà¯ÏæÜIÆ‘¬· ¨ à#“Òa9<äI*ÒzEQ@pþ+ñÂë–þðÇïüCuƒ<Ê‚DÓ 8Ý4€ r¨O9êªñøÏÆwÑê‰áG׊.SsÈÜŧDq™e8#8 …ç¨$ª¾Çƒ<càÝ-à‚I.¯î_οÔ'æ[©NIf$“Œ““ŒžI$ @±ðÞ–¶ !̳O3o–âV契ú³±ê~€`¥PEPEPEPEPEPŸü(ý埊ï#ùínüK{=´ËÊM*£teÈ##Ž zyÿÂÏùìk¾ÿÙ+Ð(®ok6Ú5þ«,vmÔäÓm ŠÚWl‹“ ÈûX–À”UÉ#‚3ÜÖshZcé—sÚ«Z\JóIf9ws#09È;ÉaއÆröþ*×.%ƒMX"ŽòâóȆúçNžÞŒBÒ³d`å†Ò¸Ýއ=ªÏ‡üOªjÜuôV`Ÿí‘ VäÛÍ jFIÀ"F$n˜9Öo é/d-$K¹J'Y$¾åG–RûÔà‘à õ5CJðU•­›AuýÝåÄöÏoq*2G+gie!ŽF7H$sž´’|o©Oc¡$1ÅÞ¡g%ܲ®Ÿqvˆ•vˆâ;¹-Ôœ w$ ™‰\ÄO Ëâ}+9üwJ”_i“F>rr#;øÚÄ òB“0u<%âX|Wáøµ$·’Òpí Õœ¤-¦C‡ÇPAäd‚ 8  ʯ}{›§Üß\¾Ë{hšiÑTOä*ÅUÔtëM[O–Æú/6Ú`‘î*g88 ãŽúP”é^*¸›ÂÞ+ƒûx^_Ϣˬ@Ö÷bF³wG •9O-„x·q]¶©$·÷†ì¾Õp¶÷v×Be†vŒ¸ò×*AÈÏr È­ËO¼º‚æâÕXDBr×u#£àäp*®—á}#F¹Vò V?%[‰&ò£È;{‹Àáp8”ÅK§}ŠÓÇ—êz×™¥,‹gæj×."Æ7èÒNçb Éè1©m¯j7ž#𽼺v©a‘Ìdk™"ÙpDY$ŒN?0ÔÉ¡éÒŪDöùMW?lÛ÷¹b=þ_‘TqŽ™ëÍ2ÿNmnlì žöÄj³Ü#·wixsæq¸Œ(ÕßCiw©É šæg ÚL³Ú›kÙ&Ãíe$æ4ÆŽAÏl ·—e¡u|´ÝHÄ’Á 9à“ÀÇR{Ðâv{‹éîtëû…´›OÒç‡Ë•”bKÒ VLê8­¯Ë2[hqD5YõDŽXtû“²¯•)Ú:qÞ*ûx+ÃídÖae¡Ž %ĪUC*!²¸rHÁ²x;HšÍ-e:”ˆ“‹„i5K¦‘$ Tɹxb09 u¬EÖ·áí5ÿá °µ–Ùd·ŸV›ÎfSÒÒ$ÌH8ˆé[ž¸¹ûF¹¦Mu-äZmð‚ ‰›s”hc“k7ñ.FO8Æy©eðn:[ ¤Íldò¥þÕºóT>ÝÃÌó7v¯ãŠÔÓtË-"ÌZX[¬†-µrK19,Iä’z“É  tQEaøÇ__ x;UÖ™£kn͘¬ÊÒŸ–5!yÁr£·^£­Søuáë ü?Ñô{ÆÍÔ1˜`|ŽìÒ2pH;KÈ<ã=ë›Ö ÉñbûýçT𦗠ýëË y§šøŽ£ó*鈗ñ”è ‘²ËŽzô (Îãø×àøžh5‡Ô´KÈŸkYê6,  Aã$l`ˈþ Ô,㺇ÅRFùÀžåaq‚G(ä0éÜs×¥u}á? êw’^_øJ»º“æžÊ9°b2p…lQ^ÿ cÁ¶ÿ½Òm¯´‹åÿW}a2Í®ÒÌÑ•9‚hÿ…â-;þ@µÈ<ÏõßڱǨgwfð»:¶q×Jô +ÏüOÏ%¯‡5ëX>@I%­ÝÐè–ÌH݇Nª8þ'_iï4^&ð7ˆ4à æ[‹h~Ùk[A24©€ $(lë=ŠåôoˆÞ×ö ;ÄV/#Ê!Hf“É‘ÜãRM¬ÙÈ“ÇZê(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯7ñ?†5O k“x×ÁPy·s«èËÂj9.€t˜dž9$K,ž‘EcøcÄú_‹´8u}"6ÞN[‡‰ÇTqÙ†Gæ$NÅpz×/¬õ›¯ø#QKÖ.Þ3wk8Í•è –2(RUÎ~úó÷ºf«ÄkBõt}zÖOø€¹Q§ß6£~Åh¥ ,¡’’ÚQEQEQEQEQEgë:æ—áí9ïõ{ø,­W#|ÏÄv¨êÍ€p£$ã@æþ'ñ>©â}rox**â>5}eyM=޳ÁãfŒþÕñOÄ)ü½Oáï ¬»dÕ%R·wð²g6èËûµ9âCÏ*G*Ë]‡†<1¥øGC‡HÒ ò­ã噹y\õw=Øà~@ðÆ—á#HƒÊ·–fååsÕÜ÷cù±EQEQEQEQEQEQEQYúι¥ø{N{ý^þ +UÈß3ãqª:³`(É8àWŸÇâøïgü"šWü#Ú4›[û_VŒ4Ò)òÛ1CÈ<Á;•‡ñ) DÔµm7F·[SP´±œ"Éu2Ä¥°Nbp DZ®OŠ««<1x'ú—‰K¾×¸Tk[XÈRJ´².´à€aÎx«WÂ}·¼×žïĺ¤I·íz¼Í0ÆÜ±+³%˜ An§ƒ]åyÜGÄ¿ÛÄ5ÍMðü ƒÍƒF·2O"°ùÕ¤‘¨à2nä“ÎXÓ~øRÒýµF½{Pg,nµ™Íð R¼#”‘ëÀÇyEW±°³Ó,ã³°´‚ÒÖ<ì†Äh¹$œ(àd’±EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE¬øSÃþ!Þu}ÆöFˆÃçM™xWÆåêHÁ'#šäãøOŠóMàÿë:³ïŽÙeŠÅB±h_ï’£«7t½Šó{[↷5Ö‰¥xžÆ(ŸiòµµËlÆÕ² 0ÏÉžxÔÓ~*xRöá¬ïodÑ5м¶ZÌfÒH†F2[äÉ0‰ÁÏ®;JÏÖt=/Ä:sØjö^Ú¶NÉ“;InSÕ[á†Ï€4(¯7¸øs¬xzXî|â)ôøâàhúŒ=‹)e,9hó‡$Œ±,@* I£üNk[øti2xkSt%.&‘M”åQKl—8æ?.HÅŽ(Ñ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+?YÐô¿éÏa«ØA{jÙ;&Lí$¹OUl†#<Т€<îOø¿ÃoðW‰c}=þA:èi¢v%PdTP*{Xä‚MñBïB@Þ/ðf³£¦õÝuo²òÖ(Ù‚†yP§v~\Œc;€¯D¢€9=7âw‚5[vžßÄúj"¹B.¦íœÂÉ´‘Ï\c¯¡®²²õ/ h:ÍÂÜjš&›}: E’êÕ%`¹'°'$ãÜ×'ÿ Káçý ßù;qÿÇ(Ð(¯ñÿÂßèŸð‹ÿghÞOÛ¼Aieqþ•3o…÷n^\ã8Œzì?áI|<ÿ¡{ÿ'n?øåvž»£èžWö¶«caçgËû]ÂE¿Î7œdtõÇÞ|dðŒw‚ÇK’û\¿2´BÓJ´y]¶‚Y”œ+(ÚyR}FG5¡¦|+ð6‘æý›Ã62y¸Ýöµ78Æq4¶Þ½±ž3ÐWYÚÛÅoopÁŽ8Ô*¢€ ?›Rø‘â„ ¤éVž³gVûf¥ žéâ,d…ATp îI;°‚ ­MáÞ—c¨¦±«Í>½®Œ·ê'”rÇ÷bPಀ2»ˆÒjº”:>—q¨Ü¬†ÞÙ<ÉŠJF>óã<…\±$€p À1èÚæ—â9/ô‹ø/m[|/¤€v°ê­‚2§g‘@æóxóÄ'Ö.,>iv7–v’ˆ®u­BF›ö±(0Ïü?2ç¯M¤1¹ñ7ZÔ­­ôoèw’ZêšýèµE I$6à~úTÆ* ää #ܽG‡4  x~ÏEÓVAij…SÌmÌÄ’ÌÄú–$ñÏ(“ÿ‹¿ÿR7þMÑÿþ¤oü›¯@¬KiWz“Ø[ýºY’v·g]:àIJ)!—fÎ#;±@×ü]ÿú‘¿ònø»ÿõ#äÝzRŸS†ßU³Ód3]¤6€›sžÚ 3þ.ÿýHßù7Fã}cMñBxgÇv¶6Wטm2úÄ¿Ù.øŒ9zg `ewúaø»Âö>1ðÕÞdJ„Ã+&ã¸;d^AÈ'¦FFAàšÜ¢¸ÿ…šÍæ¿ðÏDÔoß̺hš'’KùnцbI%ˆ@IîI­ø«B𥘺×58,£o¸’ò`€v Ë67 àg'ŠØ¢£‚FšÞ)^ w@Í…K!#î¤ŒŽœ= ©(¢Š(®?Æþ7ÿ„sìÚN“ký§âmGå°ÓÔýy'#lcç#8<€—cÅ^!·ð§…õréwÇip™#Ìrp‰71Qœqœž+›øgáI´Í,ø‹]Oâa××3ç|jyX@ lBå@àŒtUOü=H5üCâ«¿íV¸ɶ*w0qóc$‚Ãn渢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢£ž!<BÄ…‘J’:àŒP&â¨õYbk=+S’ÂfÛ #O%ÿÚvý§³mÇ|ãš–?iÍáH¼FþtVRIJ"²fC¸€ªs–$€Ï&³ü4ÚÞ“¦éº Άì¶Q%±¿Kˆ„ ¡Âîó7Ë·ïÞ°ltÝz_ézø~îÖûL6÷ 5Äöæ d‚E}™IÀm¤·ŒäÐKÿ •ºÝjÙ_iOoj÷ž]Ú!2BŸyÆÌ22¹Ü28æ¦_˜ô½CP½Ñu;l­Úå…ÂźDU,v쑆p:6"²®m5=V‹P¾ðûAkeeq²ºš&{·”(+ò³(MªGÌy,8¬Øô]ôïÚØé·öuΓ-¼×Ë9k†VÇûÇ®1¸G×èúÅÆ«–—CÔtøÊI.š=‡—#œý@­Zå|!i5˜håÒuë6¨gÔµ%¹F#²<›!ÅuTQEQEQEQEQEQEQEQEQEU{ë =NÎK;ûH.ídÆøgŒH‚Êžð«P•Þ麿ÂTPÐZïSðto#ßé2òÙ+6L°1(½Ð“üDŸ˜ºzF•ªØëš]¾§¦\Çsgp›â•:0þ`ƒAäAÁr¼­mWáŸÄÛ(ìRH|+âg0µºn1Zßäm*¡Æ(#ø‰ÂÆ z¥Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Ä? ë#Ó´ì9,RûMÕaÔíÌâ6òÃà€“ÉqÆy¬ÿø»ÿõ#äÝzçÿñwÿêFÿɺ?âïÿÔÿ“uèPŸÿÅßÿ©ÿ&ëæË»{Ã?¯ÓEºŽ YoZ-š2ºÄd2aD` pa£¨Á?cê¶sj]ÅœrZ<é囈³¾5<1BÚûsµ¹ÚppqƒáOøsÁ–á4m:8ç)¶K¹>yäás—<€Jƒ´asÈ€<ÏGÔtÕ.%¶tpDy.äbF,‡äwé^—EQYúæ³gáí÷W¿}–¶‘4¯‚ltUɱ8g’@ Óué×m¾ÙÇy¢Ûk·pÁq›¹`ˆ4F5†6È(C;˜¸Üx?‡¯­ëÿ,n¡¼±¼ÖÆéampË2Lè‡* nU”’1°sœôÁû?áF Ô~\Ìàr’Hò!ãÕYO¶yæ®xŸáχ!Ñ.o"³ƒXÓ庙Å \£;©T‘Žr;W‹âìYì!°½}©²{;[émŒÙ†uv6ÁMˆósߥmk¬¯ŠîÇLÔlm®îD÷ñÝËlð0òöî]¬Ò,Ÿ*Œ·ƒ@Mö­§i…þ¡kh\ž|ËàŒžpYGâ=i­é)}-“j–Kw %€Ü ‘ –+œŽrjö™5ÇŒômGÈW·´³»F‘òHíܼ…~Gõ®cIðÆ¥k¬C êê²Ã§5ôw= ·%ÙÈ'åó÷m}¤r=ñŠêtèþ$±µ¹Ó¯ v¸‹ÍþjP»ƒ*“‚»ÔMÃÔU™µíÚgŸU±Š)Ô¼N÷«"‚*IäÊ8õµÇèz~·¥GáÉ$Ðîd}+N›M™x3!o ¬¨L˜Ùû’9Ãr>Zµáßj6²øNKë4GӴ˨§;Õ¼©]¡Ú<ä+ò?­u:Þ•©Êb°Ôì®äT‚ád!OFÀ=G5[V×ÓL½¶±ŠÂòþöáT·´ ¸" Ä»*€ ¨ë“ž+þ¾Óm¼’Y¬-§éÒAv—ävHò8<唞223Z*°KÖµgðýæ¤Ñ‡òç±»[y 'È„Žp݇€qâÇQŽÂßÃú½ÝÑ´K¹#‹ÈS³2€Þd«óeLýjUñ@›R–ÎÛGÔî>Ï"Eu4KËyU¶¶\ :“°0ë\rhüZ•Þ¯§ëzŒŸÙQ[¼º^¨0‘e•¶È|苬ƒ<‚rkGZÒu ›ó> ê«´%u$¿Eˆ·>z 3! £g†€:&ñ*¶¯qai¥j7‚ÚU†ææÊ…ÙU°w8c…e'jœf›'‹´øìèCtì/¤Óâ·DIæFe!q•ŽI“ŠÃñ“q«Í>¢ÞÚjo,EuX/’8 ¹3F/…pQ¸‘ÙÃú½¬pjÚ,·V:íåòZ™TyðÊe_•³€Ûd 2GLf€4®üuc§ézÝî©Aq§ùF{ äHá—k”`NFC„uâµ­uë+íB Kfi|û!}ÊƈsžsÒ¹=WDÖµë‹íUô×´wVöö’M¤wK,ŽåX ã  ~éîqV¼?áíKGñ½ëWû-Y,e2»äÞbÛœ­»ÚTv  ÍgÄ#I¿²±‹K¾Ô.¯WŽ;CÂÇ·q&GAüc¹§Kâmœ5 [›·U3˜öÄàd#²±0HàŒçâxÇJ»½×´KÈtíNöÚÚ+””i·«m*³ù[~c,d•¸ÓŠ5½çŇ崼³ÒæB÷²]N“N)íÏ–-†-’0¤g&€-¿!hôÖ³ÑõKÆÔ’imÒ‰Y¢Œ¨óùå`êW¹ ¨ßâ•öwµíÂýš •HÑC0–c §ÌÀ9 ST¤ðþ§®j}jÚhE•½Ü7RYÞ4˜˜‚2˜œ6×[on„tª¾ ðm̲ÝZéV^žll-¡L##Ê»28p „9ÏSØ“@ã[XtÝBæ}3Q†æÂXašÅÖ/;t¥B`‡(AÜ9ÝØÔ÷>'{ãS¾ÐuKQ ‘ưHmÚIKº¢íÛ)^¬>ó æ5 ê6Ún¿¦éö·WöײÚ^E1»à²JždFGpÇ e%¸Én÷M¾¹ð}í­ž‹­¥Àº¶™`Ôuî$”$Èí±Œî…<^h£ÓÔ,r)!ÆAi3‚ç&ˆþ)M¦<Éã k: Bø’ñc7v‘©PT´È:–;p à‘“׉Esú'Ž|-â?!tzÆâi÷yvþhI›nsû¦ÃŽ<Žœô®‚¹ýoÀÞñžÚ¶ƒcq4û|Ë($Í·ýêáÇN:W7Â;)æ“Ã>"ñ„ ùÑZÛ^oµIB€£pL€•†nGôJ+Ïÿ²>(éñåâ\ó>ÿö­ƒ[y8é³È'vrs»¦ÑަøK<}cÆ£ðóíÁÿZ~©ùŠ¿yâ„ç ¨NãÀë@Eyÿü-ÝÓ÷šæ‹â=Ôð·Zž–é¿d7Ädôè§šÔÓ~'x#U·iíüO¦¢+”"êanÙÀ<,›IõÆ:úë(ªzn­¦ë6íq¥ê—Ð+”i-fYT6Á*HÎ8÷r€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ +ûÅžÓ/$³¿ñ•iu7Ã=ìqºä2¤ädçï¾0xO¼’ÖoÀò&2`ŠIäâ•={:u âŠóÿøYZçú>“ðûÅrß?ú´¿µ[8N9;¥f!xœôküQÕ?ãËÂú‡åýÿí[ö¹ó³ÓgÜ`çw]à zG<ðÚÛËqq,pÁ’I*¢’I<9®þïjœk_g†Öž{M"Ê;vŒõ ÁË… ’2Ê0zš,þ øF;Ã}ªG}®_™VSwªÝ¼®Û@ ¬”m0>‡#й©|Wð^p¶‰¬Ç¨^H€Ãm¦£\´ÌI ŠPÞHÀŽ£85ŸŒ¼oâš?øM> û#¿ñƤ(fßn£y•I ž„ãMÒtÝÝ­ô½>ÒÆrí¬+–À!@À>®P›Üü<ñ/‰7ÂUã«ãk,NOÑâ[Xо7!~L±•×$s‘’Y x;ÞE]G´´p…<åMÒ²–ÜCHÙvÇžƒÐVåQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV^¥á­Y¸[SDÓo§T²]Z¤¬$àã$œ{š( nûàÿ€u É.¦ðä #ã" d…"0QÓ°ç¯Z¯ÿ —N·ýÖ“â_é+þ®ÆÃUe†/]¡ƒNXäžI¢Š?áñVŸû­âF« ©ù™u;X¯ä/Б#à…À.89=èÿ„wâUúM·ŽìuI“îÙßé 2gƒ¹â;ÆÈÇR<EÅßÿ©ÿ&èÿ„‹âUú5Ï,uI“ï^Xjé2g‘µ%ÆÁÏR EÂ[ñþ‰‡þWíÿÂøYZŸú>­ðûÅq_'úݵ[ÈFyeV¸#8ŽÔQ@ü-?ú¼sÿ‚þÎøZõ!xçÿÿýPÿ Oþ¤/ÿàŸÿ³£þŸýH^9ÿÁ?ÿgEÂÓÿ© Ç?ø'ÿìèÿ…§ÿRŽðOÿÙÑEð´ÿêBñÏþ ÿû:?áiÿÔ…ãŸüÿötQ@ü-?ú¼sÿ‚þÎøNüU¨~÷CøoªÍj>VmNê+ õ Fù%pGÍžNGj( þ߈ôL?ò¿oþhüUÔÿ}g øsE~Co©ÞIs#»ÃB6…9œƒØŠ( Éø¹qû™n¼iŸ#\[Çs$‘ÁtWùY‡P‚Gàtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿGü#šoý>àtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿGü#šoý>àtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿGü#šoý>àtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿGü#šoý>àtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿGü#šoý>àtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿGü#šoý>àtÿü]jfŒÑÌû—ÿæ›ÿOŸø?ÿYzô~ðÞž/µ)nâ·2÷BP ‘ÖAé]Fk‰ø§&—ƒŒºÄi%¤s«nw6ÖÀ¹¡É® ‘Ÿkâß^Ü-½­åÜÓ6v¤z‹³sÐKZoðïüðÕ¿ð2_þ;^¡éãN²¿ñ“ÚC‚&{+$8T\`1îr?©î1v{]ҦѮ¯µµ }Q t…PÅÀ#iq‘œú­>gÜdû‡熭ÿ’ÿñÚ®šÿ„]î_P-mþ¼ ù?wþ÷ïxèz׎'‰'ñج`wŠ ¹5²™¦ˆê@Ãìyä mç‹u½bÆyá½»´’$ŠäDïäò£Ž?Ÿ­.gßúþ˜Ïjû‡熭ÿ’ÿñÚ>ßáßùá«àd¿üv¼[þOØØê«rÒÈÐÛ¬°ÜÏj±0mÊ( çƒí]Žý¤ÚXŸTºŠi§Äª"jÆ¥GËïß“ëG3îJþ1ð$w¿c{ËŸ ´Æu 2Óýo_jÓû‡熭ÿ’ÿñÚùñôŶЮÞm6ÓTÓÌÅÛT³¸U™>aýàOáŒsøÖµ×Š5«›éáÑä-¬íâtÙe2î@ÃÌb~_¨£™Ûp{žÛöÿÿÏ [ÿ%ÿã´}¿Ã¿óÃVÿÀÉøíy¾³âcÄØÛÜCb†Î;™•¢Tän>¿Ê¡²ñF±usg¢™#]T_üR 2ÿõ¸<Ž t™ªwöß"œùsÇŸ*`2WÛÜã¿Ô5§^Q~ö¨MtTpÈ;9P$ñ² 9Áö=Áê¡íÒ¤®äÓWD‰§Æ.µ‡vŽÍFßO1çêÿC­ìÖF‚?Ñ.$#K™N¸;?öZÔÍyµ%Í6ÊCóFi™£5š3QI,q.éQ}XàUí;pom¿ïêÿDªB.Ò’_5þcQ“ÙsFj—ö‡üÿ[ßÕÿ?´ì?çúÛþþ¯øÔûz_̾õþcä—g÷2îhÍRþÓ°ÿŸëoûú¿ãGö‡üÿ[ßÕÿ=½/æ_zÿ0ä—g÷2îhÍRþÓ°ÿŸëoûú¿ãGö‡üÿ[ßÕÿ=½/æ_zÿ0ä—g÷2îhÍRþÓ°ÿŸëoûú¿ãGö‡üÿ[ßÕÿ=½/æ_zÿ0ä—g÷2îhÍRþÓ°ÿŸëoûú¿ãGö‡üÿ[ßÕÿ=½/æ_zÿ0ä—g÷2îhÍRþÓ°ÿŸëoûú¿ãJ5;p/m‰ÿ®«þ4{z_̾õþaÉ.Ïî.fŒÔjêê2ž„ƒKš×rGæ¹ïx^ßÅÚ4zuÌQÉβíwd‡Uç½ofŒÐ–ÙüÓ¬.<û{;E“k/Í<®`A6AÈ&Ÿ§|!³Ò¯E夫:‚šifzàÅz~hÍyt?,-õ¾ŽÖÍfWóy²lVõ Œ~•¡Ã*[é-7_ÇÎ鄜È#‚zW æ¡–òÚ‰n"Œú;I´–¡~§™¿Á»±ŽÌÛÛy19xÿÒ%Êר:T’|"²—M†Á­l„¶èöÈá•S¸ äàwì+Ñ?´ì?çöÛþþ¯øÑý§aÿ?¶ß÷õÆ—4{Šë¹å§Á™g¶Ó,¬NŠÆÞëθåðy I8õ"µm¾YÚé×1ÛY}žàƒ0i‹ã§ÌFxíé^…ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4sǸ]w<æÛàý•­¥Í¬vö¾]Ê…—tÒe nÆ@àt5µ‚ïa…"ŽKUDPª7·tí]gö‡üþÛßÕÿ?´ì?çöÛþþ¯øÑÏáty¤ßôÉïéí-7³oeY¤Oû b§Ô~Yê—"âêÚÌÈ)),‰¸€…=å^‰ý§aÿ?¶ß÷õÆí;ùý¶ÿ¿«þ4sG¸s.çoðßì·ßm-#ŸÉnWpc¢…ÆAÚ’/†Â^]V4´[éWcËæ?#ŽØÇaÚ»í;ùý¶ÿ¿«þ4iØÏí·ýý_ñ§Ïátyô¿ í¦Ó¥°x- ¼³Ù|Ù3æ¬2? b|"³M.M;ìÖfÞGÞá¤rŽwc9ëß½z'ö‡üþÛßÕÿ?´ì?çöÛþþ¯øÒæpæ]ÎgÁ~·ð}Ôò[E,ÈC²»;±È#%»uüë³ÍUŽúÖV Ì.O@²Sæª÷nƒóFi™£4 ~hÍ34f€š3LÍ æŒÓ3UåÔl b²ÝÛÆÃ‚@ó¦“{ì[ͪÛoým?ïòÿÛoým?ïòÿ>Yv2î_ͪÛoým?ïòÿÛoým?ïòÿ²ì˹4f¨li¿ô´ÿ¿Ëþ4li¿ô´ÿ¿Ëþ4r˰s.åüÑš¡ý±¦ÿÐFÓþÿ/øÑý±¦ÿÐFÓþÿ/øÑË.ÁÌ»—óFj‡öÆ›ÿAOûü¿ãGöÆ›ÿAOûü¿ãG,»2î_ͪÛoým?ïòÿÛoým?ïòÿ²ì˹4fªÃ}kpqÌ2ŸD7ò©óRÓ[Œ~hÍ34f€š3LÍ æŒÓ3Fh#XŒAymz£B-åú”?ƒqÿ¦Ux¡Ý°1§˜8î¤7ôªÕÛ†•âÑ2$ÐüJÏý|Üèç­LÖN‚âXëæãÿG=iæ¸å»(~i’J±Dò7ÝE,~‚ŒÕmDÿIJïþ¸¿þ‚k:’q„¤º'ù1Å^Iìírþtãsž@<„ƒüóKšu«ó:•%RNswlú5dIš3Qî£u@ìIš3Qî£ubLÑšu¨ fŒÔ{¨Ý@X“4f£ÝFêÄ™£5ê7P%‚se(•8Œ‘æ(èG¯ÔWEšäî[ýlàoå]Nkë¸r´åNtÛÑZß;žVa¤¤ºÍ¦fŒ×ÒxüÑšfhÍW½™—ˆ6*Ò“–Bޏ÷è?Š8ãˆb4Uõ r~¾´ÛÆÅý¿ýr“ù¥&ê眽æa7ïn£uCºÕ<ÄÜ›uªÔn£˜.MºÕê7QÌ&ÝFê‡u¨æ “n£uCºÔsÉ·Qº¡ÝFê9‚ä’*J»dEuôašuœ¬“5³1e ¾2NN:øqùÔ;©!oø™Åÿ\dÿÐ’œeï"¢õ53Fi™£5Òn?4f™š3@ͦfŒÐ;ÉL·+hªló%*pHÎç¶yü½éñ,P¦È£H×ÑTUer5‰Æxû<_ú•'™ï]ºŽy?y–|Ê<Ê­æ{Ñæ{ÕX›–|Ê<Ê­æ{Ñæ{Ñ`¹g̣̪Þg½g½ –|Ê<Ê­æ{Ñæ{Ñ`¹g̣̪Þg½g½ –|Ê<Ê­æ{Ñæ{Ñ`¹,ÑÃp¸–5BG#è{RØÎ奷‘‹4$aVR8'ߨü*3Þ›däê—\ñäÅÿ¡IQR>ë.Þ53Fi™£5Èn?4f™š3@ͦfŒÐxÿÅ;©ÿפ¿ú¨*]xÿÅ;©ÿפ¿ú¨«¯ Ô™„âZßõóqÿ£ž´³Yzÿ‰k×ÍÇþŽzÒÍrËvPìÕmDÿIJïþ¸¿þ‚j|Õm@ÿIJëþ¸¿ò5áKÑþL¨|KÕ~f6hÍGºÕù‰ôö$ͨ÷S%$NÄ’JŒŸÀP>R`à’Fis^W¦$Z?ö>¢mlnÖga¥c+GqpYþùe³ü\œž1Š–ßÅ^#:k\¼‡7:s]FeHÆû—XG,ÉówŒçAèË.•ýÉ+yé­ÿ­õèrýb+tz~hÍpóêÚÖªÿaK|./.Þ´¸òw“ç| cåO?ß‹õöµm@³$w÷r$R¬"1£òðæF ¨ wíQI+¦­ÓÏú×îeºÑNÍ3Ô3Fk3FcJ¯®þÕq"‡ia<àØtõ«û«ŽK–MocXûÉ2LÑšuªGbLÑšu¨ pÑeÿpÿ*ês\ÁÿF—ýÃü«ªÍ}W Ëß—êyYš·/ÌvhÍ74f¾¤òÇfŒÓsFhñÅõ¿ýs“ù¥3uÿL·ÿ®r4¨·WWi³š£÷™.ê7T[¨ÝQÌEÉK€2N 8# ää[•éê3žTpk˜ƒW¼Ò´M.Ŭ.%ÔLsÇc,S¤¹ßˆ<ͨ¤ã%[§8"ª:¢­¡êÛ¨Ý^s§êÞ(×ðê1Yζ—Ì"ŠO5ÒS‚Ê¿íO U¦ñ¶±gÕ€xâšKk4/Ã)yqæ¹*Ç'¯JvaÊÏOÝFêó/\ñ¥ªéúkß‹|Mt³Hb…å‘"1¬²+üåN? qBÝJWBzhKºÕê7Tó 仨ÝQn£uÁr]Ô¶íN?úâÿúT;©Ö­I?ë‹ÿ4ªƒ¼‘P~ò5³Fi¹£5ÜuͦæŒÐ³Fi¹£4—rØÖ&ÿ¯xÿô))wÔ­^Oúáþ„ôÏ2½ kÜG$ß¼Ë[èßU|Ê<ÊÓ”‹–L¡FX€=Më†ñu¨ÔµÍ.Ý?³îfH¥iú’·“0Ê‚Á€#zöÈ<1éÖ¹û_j[Zé:4W6q·O"Âðϱ£ ˆÒ•SÏo˜ 1šÉÊÛ—ËsַѾ¼Õ5ß_Y]jPÝÅØì­îZÎ(’A32ïpž=úÓ.|Y¬_ÜÛÿfË0³Ô.f²[G “ʉT¾i K>ãÎ~UàSr@¢Ùé»èß^}á½s[ÖõkE¸¾Š("±IæŽÑüö2J™Ü @HóÐõÏoæU-UÉz;·Ñ¾ªù”y•\¢¹k}ꯙG™G(\µ¾§¶u+¯úãó’©ù•>–ÙÔ.ý2‹ù½eY{ŒÒ›÷‘±š3MÍ®¨vhÍ74f€š3MÍ  ZéÿŠ{Sÿ¯IôL£]?ñOê_õë/þ€h®¼/RdG¢ø—7ý|Üèç­ÖfŠâ^ÿõóqÿ£ž´s\²Ý”;5_P?ñ-ºÿ®/üMš­~â]uÿ\ŸùÆ¿ð¥èÿ&T>%ê¿34f£ÝFêü¼úËfŒÔ{¨Ý@X§o¢i6—­{m¦YÃtÙ&háUsž¼žiaÑt›s?“¦YÇöõÛ`QæsŸ›Žy«{¨ÝZ:µò{ÿ2}œ{AÜGpðÆÓF ¤…Ae¨¨ÎåUFÒ’ââátÛ15Ê•šA9¾zÕ½Ôn©S’Ù±¸'Ðz…E  *€À—5ê7TÜ|¤™£5ê7P$ͨ÷Qº€°NÑäÿtÿ*êó\ŒÇ÷º•u™¯«áŸù{òýO#5ZÃæ;4f›š3_Ty³Fi¹£4Ÿ¨Ÿô»÷$þkPn©53þ“oþãÿ5ª»«Î®íQœ•_¾É·Qº¡ÝFêǘÎãol¬õ+f¶¾µ†æ ˜æ@êHéÁª¿Ø:7öoöwöU—Ø·nû?»7zíÆ3ïW7QºŸ;0Ø,í-DbÞÖ|¸ü¤òã µ:íè=©’iº|¶rÙÉclÖÓ1y!1.ÇbrIÁ9ç>µ.ê7RæÈ­´Ý>Ía[[hD ËŽ%_,1ËÀã$ úÕ½Õê7QÍp¹6ê7T;¨ÝG0\›uªÔn£˜.Mº¤³9Ô“þ¸¿óZ«º¦±lê+ÿ\Ÿù­iIþñMûÈÙͦæŒ×¦vͦæŒÐ³Fi¹£4‹¨¶5wÿ®ÿèOPo¥Õ[³×ÿОªùžõêQ_»G Gï²Îú7Õo3Þ3Þµå"ã5 ;OÕbXµ +{¸ÔîUž0áO¨ÏJŠ}H¹´†Ò}.ÊKh?ÕBð)Dú `U3Þ3Þ—"ìÌ‘"‚6vHcVp•P 0õÀªÓéZeÍŒv3éÖ’ZG‚4*Q1Ó Œ —Ì÷£Ì÷£‘0æ µ­³‚Ú˜F±‘…!¢ñØdà{Ôûê·™ïG™ïO”.YßFú­æ{Ñæ{ÑÊ,ï£}Vó=èó=èå –wÕ½³{wÿ\ãþoY~g½_Ñ7wgýˆÿ›Ö8…jlÒ“÷Ñ»š3Mͯ0íš3MÍ fŒÓsFh–¸âŸÔ¿ëÖ_ýÓª=lÿăQÿ¯YôRW^©2+èÇýÿëæãÿG=hf³tƒþ‚ÿõóqÿ£ž¯æ¹e»(~j+”2ÚÍêèT~"š3S(©EÅõvw9€ÜsÁî=)s[ZlWdF1¹ê@È?…VþÆùú_ûõÿÙWÂÖÈ1›P\˽×ê} <ʃäìÊ£5ûÿçéï×ÿeGö3ÿÏÒÿ߯þʲþÃÇÿ'â¿Ì¿í 7ó~ ¡š3Wÿ±Ÿþ~—þýöTc?üý/ýúÿì¨þÃÇÿ'â¿Ì?´0ßÍø2†hÍ_þÆùú_ûõÿÙW ã=g_Ðí/¯t‹kK»; – —•[pb¡‰7*7('<õÃY9»r[æ„ó2ëø3¬Í®À~/º×ä¹›]Ôll`¬+å•RÄž¬NL['Ò½1t†u ·hÊFAäÿ}S–EŽ‹²þhc‡}ŽhÍ_þÆùú_ûõÿÙQýŒÿóô¿÷ëÿ²©þÃÇÿ'â¿ÌÚoæüC4f¯ÿc?üý/ýúÿì¨þÆ~÷Kÿ~¿úôaãÿ“ñ_æÚoæüCi”ˆ”eœíë]Nj¥„V§~KÉýæíô«y¯§É²éàéËÚ|Rü,yìTkÍrì‡æŒÓ3FkÙ8GæŒÓ3Fh†ªh$Çr“霅QÝ[n«"u ¤`ƒY寧.àØ:îÇê+‹‡”¥Íž­)7tTÝFê³ý•/üü§ýú?üUÙRÿÏÊߣÿÅW?Õªö2ö3ìVÝFê³ý•/üü§ýú?üUÙRÿÏÊߣÿÅQõj½ƒØÏ±[uªÏöT¿óòŸ÷èÿñUæ—ÿZ-j ÇE¾{ù¥X–+ؾÎrÇã,qùQõj½ƒØÏ±è[¨ÝUí’¶› éc‚ääÂDd¤ê;©Ï\uSÈ÷'Gû*_ùùOûôøª>­W°{ö+n£uYþÊ—þ~Sþýþ*ì©çå?ïÑÿâ¨úµ^ÁìgØ­ºÕgû*_ùùOûôøª?²¥ÿŸ”ÿ¿GÿŠ£êÕ{±Ÿb¶êµ¦‚×nøán}ÉúPºSgç¹ãý”ÁýI­bH# ÂÖ¶¡‡š—4ºS¥%+²lÑšfhÍw#óFi™£4üÑšfhÍaëŠÉ}¸ù^=¹÷œ~µ›¾º›ˆ"º„Å*îSú{ŠÉœŸ.ðÛ|y?¡ßCÖ]J´dåx™›èßZ?ðËÿ?©ÿ~OÿGü#òÿÏêß“ÿÅVÿY¥ÜÏØÔìgo£}hÿÂ?/üþ§ýù?üUðËÿ?©ÿ~OÿGÖiwcS±¾õnëE¸·´–h¥7ˆYaŽ Èì2àgêkÎôê:ï‹ÃöšCÁ.ò³Isa ÷‹ ôôÈÉÀïGÖiwcS±Üï£}AS[ζº¼Éc3¶ØåòËA)ìLŒ1þë`žÙÆkcþùçõ?ïÉÿâ¨úÍ.áìjv3·Ñ¾´á—þSþüŸþ*øGåÿŸÔÿ¿'ÿŠ£ë4»‡±©ØÎßFúÑÿ„~_ùýOûòøª?á—þSþüŸþ*¬ÒîƧc;}lh*ßé3cåb¨®3ŸçQÇ ÀÍtY{„M¹ürkb4HcXãP¨£ÕψÄBQ剭RŒ¹¤Kš3LÍ®¨~hÍ34f€š3LÍ  zÙÿ‰£ÿ^²ÿè&§ªÚÑÿ‰£ÿ^Òè&¬×^©2)é'ý ÿëæãÿG=_ÍgiGýOúùŸÿF½^ÍrËvPüÑšfhÍ š3LÍ æŒÓ3Fhù£4ÌÖ&»ã=CÂàÛZÙCs-Ü tôlæ[‘$hc<Ž6È·Ýj.4®kÞ^CacqypÛa·¥‘½FIü…ghšy_Go¨B­-Ò¼·‘¸È/),êsÔÅ~€S-¼w¬k·ŸØPiÍiyra†yËãb@²Hß)ä†f@=WšÂÓ|q®éÚcjNc¹´m"Á­í_Í•ÄÓÊñ†gË;ò 8rIÖ¹¯ià½ÇI‡N·¶dŽÞT«!¦ã“‡à÷pk-´ ½ ‹Ú5Ç‘œùÚnØå_w·Ç•'»*«z)ë]“âC[K˜µ+R’ÚM±g[I­’ueVG0Þ¤ƒœôÈ<ÖŽhž±ÕõWƒÎ„ZkVÊv³Ú·‘:ü-»½rËô«ö¾"ÓnnÙæk[¶à[Ý¡†F?ì†ÆïªäRßhÖ·“ý©K[Ð0·vÌLv‚²Àj¡u5Ìím¯éñj6†¸† ãôÒ’>«¸wŠèóFkœ¶Ò¡kt¹ðö±,8Ê*Éö‹vú+쌵7ö–±cÆ¡¥‹˜Çü·ÓÛwâbl0ú)s@¹£5›a­iÚ“´v·Hó'/ e%O÷‘°ËøŠ¿š~hÍ34f€š3LÍ æŒÓ3Fhù£4ÌÑš~hÍ38¬Su>ºvXÌÐi¿Çx‡8ôˆö_úié÷z†Üê3Ýܽ†•´Ê‡l÷L3¿·ûOþÈàub8 E<¥§ˆ¬5¬;]Ú$€»Í3¾÷nä Ød€ Våµ¼6véooÅ *(À.h+Û;}BØÁp›“! U•‡FR9R;Ȫ÷×}ÄvZ¤ž`vÛo{€¢SÙ\O¦vÆvLÔwCuo%½ÄI,2.×G =  óFk2ÞÛTÒî#µ– Ë»';aŸËg’/E“»Gëýïïo²Üÿϼ¿÷Á fŒÓþËsÿ>òÿߢ9V ‚àƒÚ€š3LÍ æŒÓ3Fhù£4ÌÑš~hÍ34f€š3LÍ æŒÓ3Fhù¨.ï-ìmžâêeŠêÌ=É<Þ©_jém8³¶ˆÝß²î[xØ £ûÎÝzòy88 x¦ZéLnRûS•n¯W˜À\EožÑ¯®:±Ëžƒ€Ëís™„Öiÿ–YÛ<ãý¢c_aó2W•&™ám+H×/µk89îãŽ"Šª©"€àgè+c4f€h¢¸…áš4–']V„µ‘ýŸ}¤üÚLž}¨ëas!Âúg!É_÷NW lfŒÐ; b×Pw…KÃw̶³ ²Æ=Hî?Ú±55BÿM´Ô‘Ìg|gtR£’#ê¬9é×½O£ézÃoIî­î­—„ò“gÑ•Wiÿxmÿw½XÍ«ÇG¸;âüÏøU+‹RÚ-n¢g0¤þ[Gå‚’¬ Œ•aÈìh3Fjk«9,öyŒ§vq´úUlÐóFi™£4üÑšfhÍ?4f™š3@5£ÿ-Cþ½¤ÿÐM[ª:Éÿ‰¡ÿ^Òè&¯W^©23ô³þ‰'ý|Ïÿ£^®æ¨i‡ýOúùŸÿF½]ÍrËvPìÑšnhÍ š3MÍ fŒÓsFhÙ«0ÚéFÎ}F%{‹ƒ=³ß»b»sÇ‚x?^ªf ¼»ŠÆÊâòvÛ ´²7¢¨Éý_Ò-ü%h‘[i‘¬i¥M*Fª$ÄO Ü㟽þàgðσ°·û+EšÙlÍ)[z¿y[ßxv5‘áûim4Kqr»n¦Ýqp=%—aø {Z™ &›¤épùzX‘šYYåšY%’FÀ³¹,Ü:ð¦æŒÐ³Fi¹£4›s¢B÷yc+Ø^¹ËË6Ê颕ý2Fà:PÿlÜi¿.·n±F?åúZîìŽTz¶3Fh¥ÞŸ¦ëÆ×6Ð\¦7E!•ÏB¬9Pj—öV£eΙ«HPt·¿tü"Aõ,ßJC²4ú4ÿ`•‰f„.ëyrÑñ‚{•*OrhMwì®°ë0}‚BB¬Å·[È{bN0O£'¶hþܺ³ãUÒ§…GYí3sþ:7Å0=kFËQ³Ôa󬮡¸8-†út>Õ>k:÷CÓ¯æûD¶á.@À¹…ŒRøØöÎ(O4f°þË®XÿÇ¥üWñùc|»è%AüПz?á$†×ZÖãM#¬“Ðý|ÕÊþöÓí@™£5 SÅÀp5RÞÆ{»„¾Õ0dCº U9Žsýçÿk è rXžDúïÏ}Á¦ÿ £Œ<ãÖQÙOüóôûÝJ¡€0:SsFhÙ£4ÜÑšvhÍ74f€8»Ÿˆ¾%³ºšÑÊ»Û ´§ ú‹¼Ÿg8ÇÝ(ˆOoÞÃJòï^ºÖ-…Þ«ºëX[/ØãÝ [iš½œñÓ¹®Õ§ÑGvÓ".ó-Ã1 iT®}X0zŒ q»Ñˇ:rYÀ>JdJF ÿ½‚Fzâ•´¹å¶¾*ñE¯‡´ë[+ë™~Ç C¨½Á[cæ3îÀ™¦uÄk³i)–ç$ç¯|—r‹<¨IFö@ržHÏz[ËO ­¤%¼/kp¶{žÞ³„”$î; O=²j(õ mL5å¤›á•ØƒÐŽNAA ƒÈ ƒUqu%ͦæŒÒìÑšnhÍ;4f›š3@ͦæŒÐ³Fi¹ª—ú•¶4ìÅä;b‰é%oEQÉþ@rp9  ŒêªYˆ I=¬o·Ýk_&’þM‘ûÚ†Þ?éŠC¾~^˜ ÙO¹ÕXK¬HÊièû“ØÊŒÿ³÷GûX [9 ,l-´è VÉ·snwbYänìÌycîjÎi¹£4ìÑšnhÍ;4f›š3@Ís>*Ðu]ZæÎ4\5Um6þh³ûˆ÷$¢CÆ "ä÷q]&jÝ®¥5œF8Ö2 nùÿM\iØâ´oêZóh÷Þ$ÓnÒk«¹¥º·ŠañŽ &ð·zÃ_ê°hm$ZN¡©>‡`³L-e.Ê“°¸BF o/fP0f½cûvçûþGühþݹþä?‘ÿ}n#Žð•ŒÖ6×Ä,‘ØIs›X¾Á%œK„PÆ8¤vuR}qÎH×Cš‡[¿Õ®¼‰mÕÄ[·Û¾WÌ'iî9íÔT°Õíïäx’ ¸Ædµœm‘®2Aí)#Þ›`ÑšnhÍ š3MÍ fŒÓsFh¦°âIÿ^Òè&´+7X?ñ$¿ÿ¯i?ôZU×…êLŒ½4ÿ£Kÿ_3ÿè׫™ª:qÿG—þ¾gÿѯX¿5kÝÁŽ£§MäÝÃåì“`lfESÃ:\²Ý”u£5ÿ ëÆ¿ôR?ò‡ÿGü+¯ÿÑHÿÊ?üU 63Fkþ×è¤åþ*øW^5ÿ¢‘ÿ”8øªØÍ¬øW^5ÿ¢‘ÿ”8øª?á]x×þŠGþPáÿâ¨c5æ>7ñ­Æ‘âWÐ/-æÊëìóEäÞ óGâ W£'¯í¿á]x×þŠGþPáÿâ«:oƒÞ"¸ÖáÖfñê>¡ ^Ts6‰(¹'¿äóŒó@ Ä×6PÏqlÖÒºîhYƒö$qš±šÇÿ…uã_ú)ùC‡ÿŠ£þ×è¤åþ*€63Fkþ×è¤åþ*²õ_ë:–Õ~/ÙYàglúUº±úù?…u™£5ã©â}^û]³Ó|;ñ -Qä`eš÷JŠÊÞ4ï–c¼Ÿ`¿C]qñv­¡/üU:+%°ë©éö›Sîqó úó@¦hÍgéšÆ¬Û 6ö ¨»´N=ˆê±«¹ æšê²##¨d`C+ ‚= &hÍdÿdO§|Ú%ÂÃÿ—)²ÐeïüåÝ5-¶¹\%¥üOaxç Äm”ÿÓ7+ýÍŽ VŽj+›x/-ÞÞææ…Æ92°÷€,fŒÖØu 32ãíãþ\îÜœö$ä£n†ÑVlµ«k¹þÊâK[Ð2mnl˜îW³u${Ð%ðîžeiíM>áŽL¶Oå>¬£åø5Ÿw¨ëºLék ÛksH¥’~Ï0_ï1ÁLg¾Û'ƒ¡u¨Ë=ËØé›á8šfŽß<óýçÇ!±8fÅ„6¸BòK!Ý4òœÉ+z±þ@`Àq@úf¥cß›«K%¾­(ØMì~P¯—É\p8Vbp2NtÙ¨eŽ9âh¥$†r÷“ÿôÜéW7ií ˜~žSe@ÿtï@™£5‡ö½nÇþ>¬bÔ"òÖɶ?ãœ~NOµX³×tëÙ¾ÏÆËœdÛΦ)Gü€8÷Æ(S4f¸Äþ3ñ­¤ø í3îòåþØ…7mb§†ŽAŠÐûgÄOú'_ù[· ›4f¹Ÿ¶|Dÿ¢uÿ•»z>Ùñþ‰×þVíè¦Í®gíŸ?èånÞ¶|Dÿ¢uÿ•»zé³Y—v3Cr÷únÕ¹lyбÂ\03èØ è9c3íŸ?èånÞ¶|Dÿ¢uÿ•»zÔöž¶y<ßfH¤X¦Y†Ö…Ø€‡c–ô ‚ ÒÍy'Œ¼'ñCųÿ¤VVÛBùqß@Îàr¶ñ¸É>äõ–|I·Óíà¸ð¸š8Õc¬[©‘€ÁlsŒõ ¿4f¹½oÆö ¾óÀ°[§÷¦×í~µÌj?®ô¶Ûsáû>–úìÿè°Ôê¹£5çoÅ»K‹h§Ô4kÛX¤@EÂ(Ïñc~"º/Æ>Öv‹ ^ÖWn‘³ìsÿlÒ€7³Fj½ÍÔvïqq*E ³¹À—þ›­}ï:ÃN=¹K‰Ç¿x—ÿÿs€OqªË5ÃÙiQ¬÷(vË3çÉ€ÿ´GÞoö=2Tjk 2;9âIæöA‰.eåˆþꎊ¿ìŒç'$Ïo6–éooE *"àì*\ÐóFi™£4üÑšfhÍ?4f™š3@¯µí#L`Ô5[IYwˆî.6+’3‚Gòªßð—økþ†-'ÿcÿâ«2ËIÓuŸÆßTÓí/ _oXî¡YT0¹ 0#8$gÜ× ÿ àÿú4?üCÿÄÐ#ÿ †¿èbÒð6?þ*øKü5ÿC“ÿ±ÿñU× àÿú4?üCÿÄÑÿ'ƒÿèTÐÿð]ÿ@ü%þÿ¡‹IÿÀØÿøª?á/ð×ý ZOþÇÿÅW]ÿ'ƒÿèTÐÿð]ÿGü žÿ¡SCÿÁt?üMr?ð—økþ†-'ÿcÿ⪥þ»àýJ4[wHfŒîŽE¾^&õV •>à×sÿ'ƒÿèTÐÿð]ÿGü žÿ¡SCÿÁt?üMy6½ã¸ô=ïìºö©¹Œ­´ÐOOŸ»½ÃŒÿÝzš×ð÷Ä=WÐí¯.õK +—\Mo=ÂFÈã®ã¸>†½øÁäÿ¦‡øiññ5_¼ m¾ю㓾Ê6ü²8 Wþÿ ÐŤÿàlüUð—økþ†-'ÿcÿâ«¥½ø}á™áÙkáý Ùÿ¿ý• ŸÈ®+ˆÖþX»ai~ì^¶Sÿ}&Óù“@¶:Ö—©±[ JÎì¯Që&?#W³^n¿³Åõ¤+}wOŸ½dšÉãu8ìÊùÇ*Ý—ÃOŠZ9ÿAñ^›qÿ–Wo#Œz ÈÄ~€; \ÿÄ–ÿþ½äÿÐMj× ¢ë:ž¯á¿Ç«Çj—ÚuÍÍ„¿eݱŒj2FyêMwU×…êLŒ<þâ_úùŸÿF½sü[m[þØÿèä®–ÄþæoúùŸÿFµsÏü[}[þØÿèä®YnÊ=¶‘™UK1¤šZáüMoc©ü@Ðô½pG.•%œòÁm9ýÔ÷*Èe<9X€}Í.¶¸9½âÞ%mNÓ®£ð÷ïlí$Ô†@Þ9°-c¦ÀO+Ð1aZ«ãK⇑<ÈôôÖŽ—öyÙa( Üdó¼ßã.Ò8ÁëGKê”WŒ]k~*¸ðDww~!g¶ƒwvDVÑÄmÚ%F ò¬A$õåq]çˆ-ìî<;¥iú¶¤ò5ıÇ÷1%ôÁ–9Yá®N0ç8 u:º+€ð5ìºoƒuߵȑ¶—{v,<0*ác?y‘A‚0+›ƒÇ$´ûLWWs³{¥’ò deܤoµb-ˆÊ±Æÿ˜zÑ»² •Ïc¢¼ÚëŽÿŒî¼?g«Çeê_gKµ…$1*[$îK³–ûÀàô®k›äð¬Io©ˆî6ßÈ×öñª»‘©Ä SÎÕmÙÀ8àu¢>ð3è‚æÊÒñvÝZÃ:ôı†­q/çÑG‡u_´Ïg=ÄÀ1’ÎsÎ=HíX÷¾)ñŽmí?á!µ¿mJÎÞqw%´alZYã‹p €ÈD„¨lœ§,E ]€ì®>x:åIá}`l³D?˜Ö<ÿ¼3ù‰¢}žNÏos,d~±úV%LjÔ|Q ÞêÖš¡ˆ¡[¯”î•çdž=ë¾ÍCñ·þI»ÿnÿúQ?4üÑšfhÍ?4f™šŽ{ˆm yç•"Š1¹ÝÎRh|ÖꯉÓìТg£å¯™‡h}ý4íÑryY<¹µÞnH4ÃÒ$¸ôÐuTÿc©þ, ©×P¨¡T `8€1 ²Ô4„Zz‹ë$‹w!&Lœ®p¯Éþ<I,kBÇV´Ô ¤.VxÿÖA*”’?ªžqèzÄÕ¬ÕKí6ÓQn"ýä|Ç*1I#>ªÃ‘@³FkÌÕ´¿õŠuKAühÜ ÷^þ£iôV5~ËQ´Ôb2Z̲;]pC!ôe<©ö ¹š¯yci¨CäÞZÃqs¶T õç½Kš3@Ÿ4­I¾èךfµ-¼çæÞâ!=¹Äò»Ã¯Oáp:œéu/Ãákˆ­¼T–¶­ ÊMkt²†3åKø*¿Ö©|ÿ’C¡ÛÇþ”IUcÕü? kÞ0‹Åîî„Ë]Ú˜‘Q#ÈùðC®ÅÏ=¹¥q­‡‰´=NDŠÏV³–fm¢0nÁm¥ÌŽÎô­Zð½RÌ>­²Ñ¤·¸[h…µ£3yð•Óçdx;Á ‰Ðç"²í5_ &åt¿=ÄrAfó“¨Iy$RÉšö“Ý_Ë$Æw¸¹yTË;Ê›˜•Ç e<k®×‘jz”V­vo[OIa3‘Ë~ ùŠîÚO8£§õØjãâ^¯/†åÔ ðêڵΗ=þŸ$·b@ÞP]ÛÔ/0`2w–º½jóW_ ÛÜÃ=–—3ym{s<’Ò»¾½v»†)å—B%®à“iÀääÀ8ŒÖ%ÅhnVäIalfˆ[8KME.@Y¦X°ì«…u, ^~µ×A¡cD»Ò¯µ;íF;¥tynš?0+.Ò ¢(Ç^Ùæ¹ñðÎÉãqs­j×mRÕšò–7Y#*0V\ôç'9£¨ºßxÞhµËOÒ Þ¢—bÞ$k‘8òVWvl ¡ÉÇ­qòüAñ%¿‡£¼XÃê,.ØÛÌcÆú8@b«’U_h àòNN+®áÝ´RÉxšæ¯ýª÷_kþÑfˆÊ¬c²åìØT´©è1ÐR†šAÒÅ‹^êMˆ§ˆLÒ¡“2γ³·ƒ ÇÆr Óp'ño‰'ðóèWSH`µ’iZú5P䯖ÒÈTv(9Î*‹üB¼²A¥áÉ`»¹‚9´ø!ºY~ѾDŒ+6F "g¨ðN+ Ö|1c¯Ãe¤óL–¥Î2«æï‰âmøÕØñŽ*ç5/‡î4™Þ+ûý[RŽ(a³{Ë”‰ HäYÆHð*æVÉUŒÑêwß®tÔšíKØoVÍþÓ¨¶V1y¡šm‡ Aey'œWi¤jÚºE¥ù„ÀgŒ9Œº¾Üöܤƒõšât?jý棬jVW÷WÆå'ŠgÙå,{e?)³‚p òàñ]ž‡£ÛxEµÒ¬Ë˜-“b™ÜyÉ'I=‚…¶¡èxþ„Ñ|ÿaíGù ô:ó½ ÿ£øÿþÃÚòè•×…êLŒK#û©¿ëæýÕÌ|R?ñn5oûcÿ£’º[S:ú\ÍŸÅØÿZæ>(Ÿø·:¯ý±ÿÑÉ\²ø™G¸×?âË¿ Ãe·ˆíỎâLAhÖ¦ååp3òFªÌHHWA\wˆ^]ÆÚ_ˆ%±»ºÓÅ”ÖRµ¬ 3Û»::¹EФÇ©è5Í£[¤Vö­ešíFžÄÄUs,~VÍÊvžT€N{æ·¿°4s«ÿk*ÈêXÇÚü…óqŒ}ìg§N+ͼRº¾¿¦Oeáë»A ½û>ËvYæg ’H eY˜aAçj Òu—ñûÏÅ¥Þ¥»ZYÜH–ÚUŸy"¹G“>af–P™Ëp_œЧñX[#Ù„¼64×Ó†¦ 'q#[‹DÎÆ ÇëY©sࣧ˜þ˧%€Yàmö[!LD$¨Q™qÇñ‘œf¸‹¤}[Ä7š–¥£jòøbmT4°Ie0iµEÌ8ÞÑï·]¤ŠÇ¸ðÍìþ†Ûû R6‘Ázëm,Qý¡ *rKyaˆ’3ŒÒŽ»‡¡ì÷¤Z\iV·Q@²I)ŠÁ ;¶¸‰ Æä ;q‘ßFçÁš?ö-ö¥ÙÚiB÷kÚÚD7`ç ¥J²õКÄñ厥wg ŸÛÎ'‰§h#+ä“g2Æ[#äùŠ›+—NQj[BÑõ‹-/ì–ɬÄÑKn÷2}¢2ê¡ðÒIå‰C2ä¶à2Inƒ¤|<Ь4»›Û-A.n~Ó"ÉgD®*ìFÔF=y9'5Ô[[Agmµ¬1ÁJ8¢PªŠ:W‡MeevÚ¤6Z~¡o¤A¬.Ë[.k˜P}•p%·H¨Xåp>RWÅzσ>Ò<¥ »YmgÐJÎ̘à_æéŽ‘ÐеWÌç>6ÿÉ!×íßÿJ#¥Í'Æßù$:ïý»ÿéDtÜÐ1ù£4ÌÕ+íIm Š6¸¼”*<‘ݘÿ Žì~ƒ$€@'¼¾‚Â6f<¨Š2ÎÇ¢¨îjœsÞΗš˜Q°î‚ЬG³1þ'÷è½³ÅÖz{G?Ûo$ß0Û¿X”ÿ c°õ=[= æŒÓ3Fhù£4ÌÑš~j…î“k{(¸ùà»Q…¹¶È¦z0ö`Gµ\Í  ¯¶êZg„?l¶òõj‡zöâäþ)œÿuEiZÞ[ÞÛ¬ö³Ç4MÑã`A§æ³n´xe¸k»Y$²¼o½4ÿßSò¿â2;@Ÿ¿äè_öñÿ¥W~QX‚Ê ‚GJùóKµñ·ƒtøô˜|`ÖZ<¼‰bÒ¢3;Ë|ËÉ''*?¼:WAü'óD²ÅñdÆU—F· PhÙ)ªç œœ ñÿ+âý?ü¢ÛÑå|Cÿ¢‡ÿ”[zöµ{á}*ò鯴½o½wg!‚Vÿy—þ‘í^må|Cÿ¢‡ÿ”[z<¯ˆôPÿò‹o@€Ö¾$ÓÆ!ºµÖmÿçâùcþº (ßM‹õôQâ½>Xõ‹{RF òˆžØ™IŒŸA»>ÕçÞWÄ?ú(ùE·¤h> º•oˆA”ŒtKr `Sª’)WeXsÁî ;á6~ñnxnl¦®×–ÿÂÌñ‡ýŸü­ÃÿÄQÿ 3ÆôNò·ÿ@_äë¿öïÿ¥ÔY®KÆÞ&ñ‡Œ|!}  ö?µy¿þ׆M»dWû¸ÎÜuïZ¯y>¨íœæ;e%e½ä÷X³Ôú·AÐdçh÷z„­pÖ:r¤—cc¸&;pyðFN9 'Ž@9©¬lc±W!žYå Ë<œ¼‡ß °§ZÚÁen°[F#rpI'’I<’O$žIäÔù fŒÓsFhÙ£4ÜÑšvhÍ74f€š3MÍ fŒÓsFhÙ¬©4a ­>•9°™ŽæE]ÐÈÚø©V>µ§š3@k­5£µˆ›ptßãiö`=‰­`ÀŒŠ‚º•`HÁEedËbwèÓ‹uïi&Zôcÿ€ñÜ©  œÑšÊƒZA:ZßÂÖ7Nv¢Èr’ö£}8ojÓÍ;5÷0ÚÀóÜJ‘Dƒ,îp{š©{©Çhë#Ü]È3¼\±§²¯ûG·\ t“N—z£¬Ó¡Ý(O“ÿd¼ß푟@¹"€¾óYû¾mŽž‹”žaì:ƾÿxÿ³Œ+{xm-Ò x–(aQF§æŒÐ.-໡¹†9¢o¼’ e?Pk7û Úó¥ßÜYc¤Dù°ý66vd+Z¹£4•öýZË‹Ý8\Æ?嵋dýLm‚>ŠXÕ«-bÃPvŽÚåeh\‘~¨ØaøŠ·š«{§Yj(«ykÁNTºä©õ¨>â€.fŒÖ7öeõŸ:v©&ÁÒ ÐgOÁ²~,~”l]Zq©é“D£¬ö¹¸òxüWÖ€6sFj¥ž¡i¨EæÙÜÃ:‚ѸlCŽ‡Ú¬æ€š3MÍ fŒÓsFhÙ£4ÜÑšvhÍ74f€š3MÍ fŒÓsFhÙ£4ÜÑšvhÍ74f€š3MÍ fŒÓsHÎK1@É$ð2eóå·¶òÖQ»ýÕùòÇã[Õ›¦[±f½•J³®ØÕ† §^} àþ½iW¡B±×© +:óOfsqjBÊ~ümÂÉþßóìF¤¢¤¬Àç ʤ‚)Á‚SüðOÐô?†jlÖÜ‘G2•ÐõV©O'+Oúç# ý®Ya_Ùcæ(æŒÕÏì;KŸü —ÿŠ£ûÇÒçÿ¥ÿâª~­1ܧš3W?°ì}.ð*_þ*ì;KŸü —ÿŠ£êÓ ”óFjçö¥ÏþKÿÅQý‡césÿRÿñT}ZaržhÍ\þñô¹ÿÀ©øª?°ì}.ð*_þ*«L.SÍ«ŸØv>—?ø/ÿGö¥ÏþKÿÅQõi…Êy¦K4pDòË"Ç)gw ©'°©¯l4>ÜÏp×*¹  \ÌÌìz*¨l±=€æ©Ûøf+ùΧñÆhlMÓ°ŒƒÎC_èv¯l‘º«H.QÄúßß[é§ø+%Ï׺§·VïÚ¨«*"…E*¨ÀÐUïì;KŸü —ÿŠ£ûÇÒçÿ¥ÿâ¨ú´Âå<Ñš¹ý‡césÿRÿñTaØú\ÿàT¿üUV˜\§š3W?°ì}.ð*_þ*ì;KŸü —ÿŠ£êÓ ”óFjçö¥ÏþKÿÅQý‡césÿRÿñT}ZaržhÍ\þñô¹ÿÀ©øª?°ì}.ð*_þ*«L.SÍ«ŸØv>—?ø/ÿGö¥ÏþKÿÅQõi…Êy£5sûÇÒçÿ¥ÿâ¨þñô¹ÿÀ©øª>­0¹O4f®aØú\ÿàT¿üUØv>—?ø/ÿGÕ¦)æŒÕÏì;KŸü —ÿŠª·ÖZFšåîTÚŠ·33;Šª,}…V—p¹ ðÃuÁqK Œì7Ñùÿj‡†š —3Fi–hº‹¼15ÔwQŒËm5Ì©,cÔ©lãцAìM_þñô¹ÿÀ©øª>­'ÕÊy£5sûÇÒçÿ¥ÿâ¨þñô¹ÿÀ©øª>­0¹O4f®aØú\ÿàT¿üUØv>—?ø/ÿGÕ¦)æŒÕÏì;KŸü —ÿŠ£ûÇÒçÿ¥ÿâ¨ú´Âæ%æa}/Ÿ-¸[€0."c£þ¸?†j¿Ùu‹/øõ¾Žö1ÿ,¯Wk~ þjÇÞº?ì;KŸü —ÿŠ£ûÇÒçÿ¥ÿâ¨ú´Âç9ý¿·¥¬úyd‘wEõó ÷¶Ö¤SG­0¹O4f®aØú\ÿàT¿üUØv>—?ø/ÿGÕ¦)æŒÕÏì;KŸü —ÿŠ£ûÇÒçÿ¥ÿâ¨ú´Âå<Ñš¹ý‡césÿRÿñTaØú\ÿàT¿üUV˜\§š3W?°ì}.ð*_þ*ì;KŸü —ÿŠ£êÓ ”óFjçö¥ÏþKÿÅRÃ90»ÿ¿3·ó4þ­>ás5®cyJL’ÿÏ8Ææü‡ó«¶ºt’:Íx…9XÈÕsíÐ{ñmá¶d$IýÔPéRV´ðê.ïQ6QEtTrÍ ºFÀ'’ǰrO°¦‡½q˜ô›×_Så§èÎéS)Æ;±ØšŠ‹þ&?ô¼ÿ¿ñÊ?âcÿ@[Ïûùÿ©öÐîd´T_ñ1ÿ -çýüƒÿŽQÿúÞßÈ?øåÚÂÌ–Š‹þ&?ô¼ÿ¿ñÊ?âcÿ@[Ïûùÿ£ÛC¸Y’ÑQÄÇþ€·Ÿ÷òþ9GüLè yÿ ÿã”{hw 2Z*/ø˜ÿÐóþþAÿÇ(ÿ‰ýo?ïäürmáfKTµ J;J=ÜÙòm£?3ã©öQ‘–< ŽäVëRÔÚæM?NÑn%¿U Áä‹Ë„îDqÈ\‚ÞÃ,%ÓôÛÛ$‡H¾žêl®$’ Òc ÿYÂŒœ(àd÷$”ëC¸X,´ÙÀ¿Ô$Yï°BíÿWŸáŒ6<·°Â*‹þ&?ô¼ÿ¿ñÊ?âcÿ@[Ïûùÿ¦ªÓ]BÌ–Š‹þ&?ô¼ÿ¿ñÊ?âcÿ@[Ïûùÿ£ÛC¸Y’ÑQÄÇþ€·Ÿ÷òþ9GüLè yÿ ÿã”{hw 2Z*/ø˜ÿÐóþþAÿÇ(ÿ‰ýo?ïäürmáfKEEÿúÞßÈ?øåñ1ÿ -çýüƒÿŽQí¡Ü,Éh¨¿âcÿ@[Ïûùÿ£þ&?ô¼ÿ¿ñÊ=´;…™-üLè yÿ ÿã”ÄÇþ€·Ÿ÷òþ9G¶‡p³%¢¢ÿ‰ýo?ïäürø˜ÿÐóþþAÿÇ(öÐîd´U[‹‹«Ky..tˈ`KI,“Ûª I&LYeõÝmÛiZ…žœã&`Ñ$òE ÿ»_ö¾ñÏxjhw 2ÕÞ¬ßik6uz¿ë>lG{Èݽ”eŽGrc¤¥¼æòêSw¨2í7 lªÆ¿À½8œ ’Fj[K;› e·µÐ.b‰z*ÉSÉ'÷œ’y$òMOÿúÞßÈ?øå/k¬,Éh¨¿âcÿ@[Ïûùÿ£þ&?ô¼ÿ¿ñÊ~ÚÂÌ–Š‹þ&?ô¼ÿ¿ñÊ?âcÿ@[Ïûùÿ£ÛC¸Y_évzš"ÝB£;¢‘IY">¨ãO¸5G:Æ“Ô6­f;©rƒéÂÉÿŽŸf5«ÿúÞßÈ?øåñ1ÿ -çýüƒÿŽRö”û…™ †§g©ÆÏi8r‡lˆAWŒú:2Ÿb«u—£Í¨H³¾‰ Ú GuÐ$¨=7 9윃ÜUe¹ñ—‡QÑ.ïmPd\[Œßð(•Îïªuþè£ÛC« ´Uùµ+U¹²Ó§¸…‰ãš2:ƒûÎî:гÿúÞßÈ?øå?máfKEEÿúÞßÈ?øåñ1ÿ -çýüƒÿŽQí¡Ü,Éh¨¿âcÿ@[Ïûùÿ£þ&?ô¼ÿ¿ñÊ=´;…™-üLè yÿ ÿã”ÄÇþ€·Ÿ÷òþ9G¶‡p³%¦KsDÑÊ‹$l0Êà qMÿ‰ýo?ïäürø˜ÿÐóþþAÿÇ(ö´û…™•ÿݽ·Í¤ÜÜiÙ-Û0ÿߦÊøÞµk¶ñõc£ÿ–¶MåÉøÆç““íZ¿ñ1ÿ -çýüƒÿŽQÿúÞßÈ?øå/iO£ 2•ž¿¦ßOöd¸ò®¿çÚá Rÿß #ÜqZUJòÂmFï‡&¸‹®ÉM» úó%fÿaëV|é1j–àt‚æXnaüš]ã讵Ú+¨Xߢ°Æ¥â õ_ Þ_ùia$sçßË ¼}o­XÓµ‘««› )§dâDY ßôe2eO±Ÿ¶§Ü,ÍJ*/ø˜ÿÐóþþAÿÇ(ÿ‰ýo?ïäürmáfKEEÿúÞßÈ?øåñ1ÿ -çýüƒÿŽQí¡Ü,Éh¨¿âcÿ@[Ïûùÿ£þ&?ô¼ÿ¿ñÊ=´;…™-üLè yÿ ÿã”ÄÇþ€·Ÿ÷òþ9G¶‡p³%¢¢ÿ‰ýo?ïäür‚oÔeô{Å^çtMú&máfKEE é6໕ӇGR¬§ÜEKZ&ž¨èѬóOzã%dhaôU^rÙϰµ¾±tÆ–Gý<Üèç­=õåÍ·&ÙdûèßPo£}Hï£}A¾¡žèEµB—‘¾êçì)7mX7bîú7ÖgÚ¯;[AøÎøŠ>Õ{ÿ>Öÿ÷üÿñ>ÑÎ=ôo¬Ïµ^ÿϵ¿ýÿ?üEj½ÿŸkûþøŠ=¢tiï¬Iµ+^i,ôy|¸QŠ\jAÁHÈg 9U<ĪúŒzž¢ÑÀ|˜l°Lëà %ô]Û~UõÇ'Ôs›°Ëqo pÃekQ¨DD˜…UN£Ú çEÛ +m6Ô[ÚDZ2X’Å™ØõfcË1îI$Õõ™ö«ßùö·ÿ¿çÿˆ£íW¿óíoÿÏÿG´AÎ=ôo¬ÁwyÞÚ{NIÿÐjxnVd$¤2·U>†š’`¤™s}ê ôoª(Ÿ}ê ôo  ÷Ѿ ßQOt° $f;Q«AM+èƒbæú7ÖY¹Ôݵ·ÇûWÑ iÔçÖ×ÿ[ÿÕû)ÏS}ë/í:üúÚÿàKñº>Ó¨ÿÏ­¯þ·ÿ£ÙH9âjo£}eý§QÿŸ[_ü oþ7GÚuùõµÿÀ–ÿãt{):R ¸ÿxvÅWþÚÔ4Ÿ—\µÿ˜…’ë$|´Q¹GRÃ¥;û^çþ}bÿ¿çÿ‰£û^çþ}bÿ¿çÿ‰¬ÿ¶°?Ïø?ò+ê•»~FÄPÝ@“ÛÍÐÈ7$‘°eaêàԛ늞Úhç{½!M»s¹ü¹ C)õ’- ¨*ßíV­¾¯ä'Úm-¼ì|þ\í´Ÿl®Ïz?¶p?Ïø?òªVìtèßXÚ÷?óëýÿ?üMÚ÷?óëýÿ?üMÛXçüùÕ+vüÍôo¬?í{Ÿùõ‹þÿŸþ&œº¼€6ØîRMÄ~ k8Á7nÁÿž²èmo£}VI–DWF ¬2î)ÛëÓNú£œŸ}ê ôo  ÷Ѿ ßFúŸ}gê:6™«2=íœrÊŸêæ,±ÿºã ¿;̱£;°UQ’OaYÍ«ÈIò­^ÅäÚOà®lN2†ÞÚV¹¥:S©ð¢?ìÝkOçLÖ ÄCþ]µ5ó8ôYW>­¾øIe²ãZÒ®¬€ëm.3²ßFúã"ñå’ZZÉ4W3©¶‚k»›x1 ¿˜ ©`[ çq¯­JmI^×O=,UðòúlÀôÿ`x°%FsxÊÁu?±˜nÌb²›ÁîDßóÏ9Î{gÏÍfMñÕ´™ï-tÍ@·Øäº¶ó¢³ªcvrÈÉ#§#4Ÿ/pW;ØÙ"cU@UU€ w›\åö©¨ÿe[M§Xd. #%Ÿ=äãëQxg^›Y±¸’à@^ÞåíüÛf&)¶ãæ\öçO óUË­…}.um@$ÿ‰´8ï™ü1üÍAæûÓ#“:¬?õÂOý *jFÐcƒ÷‘±¾õú7× ÔO¾õú7ÐûèßPo£}O¾õú7ÐH|É1ïæ¿þ„iwT¾yë«ÿèF—u~eˆ¾Ÿ«üÙô´ãî/Eùn£uCº°/5-Vï]›JÒ^Ök K=ÅÌm&K–Úª¡—û¤“ŸN*iÁÍÙt½Ôt»¨Ý\|ž#Õlu-mæ —`2ÆábpÃdg‘‘œãøMôÑæ;Cx¶á%xn!åÜy@—sœàd àã5«ÂÕè¯èGëKuc8¸IÆ[¢â”•Ñ­¦?úû;üxÕÍõ›¦¾,—ý÷ÿÐÍ[ß_£àÝiÿ…~GÏWþ,½Y>ú7Ôèß]FDûèßPo£}E©¿ú{ºüxV~굩>l›ýôÿÐÅgâ7þÕðþ¬ö2õzOÔ›uªÕ™¯jÒé:go Ís,±ÛÀŽÛTÈìn=€ÎOÒ¼(EÎJ1ÝÍ$®ÍÔn®JóQñ&gu=àÓîâH<ÄšÚ0’nc!rXIÓž´áã!·๼Ÿtìég1Ŭ…˜í·I°ÛêÕ¼l×—ô¿9â´z^ê7W,Þ7ÓEÉŽ8/fZ{¨â$óB”$ç8;‡@HïWtmõ‹‹™­%´òn%‡‘ÈF#<n}=zÔËVæ’²”²fæê7W3 x•õÍWR…mü»;u‰í¥?zt}ß>;)ÛǨç½tª*S•9rËqÆÒWD¥ñ$$uóSÿB»¾¹Âß<_õÕ?ô![›ëêømþ槪ü/1Vœ}?R¶¸ùðþ¥ÿ^²ÿèUõ§Îƒ¨ÿ׬¿ú «õø^§™"†”Ø´ÓÌÿú5êöúÌÓ›ÒúyŸÿF½[ß\²Ý”XßFú¯¾ô€±¾©ÈÿéÒ×4þmRoª’¾/úƿͫ Kµ3*ß g}ê·™G™^9Ër–¹¥>ª–²Û]›Kë9¼ëyöà© ¼eH$‘õ¬ãá›»·{GVó®Þh¤ýÜ;"D8DMÄŒ–$’I­ï22Ÿ´v°ùÙÅŸ‡†ˆ-Å“©‚Þ)¤ŸOIeýÒ…ÌlÇ ¹Tlu»†Ä1BŸlÏ•©Ë¨gË뼿É×¶þ¾Ý+_̣̦ëIîÃÚ2¾…a.¡ÙéÒÜ-Á¶ŒD²,{2£…ã'œc<Ñ¢éߨö kçy»§šmÛvÿ¬‘ŸÉé»…Xó(ó):Ü\Åô–ïþ—7ýsOæÕ_̧[·úD§ý…þmZP•ê#JOßFŽú7Õ}ôo¯Hë,o£}WßFú±¾³¯ŸÖçþ™ÉüÒ¬ï¬íEñsnØqú­m‡þ"3­ð2_66¨ùÞôyÞõéòœ<Å][K¸»Ô-u-:õm/­Ñ¢Ý$^drFØ%YrUEe]xB[ë)ëWy.§34Ó¾\ÈUÏʪÀÉúÖÿïGïSì•îW;9äðL1ë­|’YšìÞlQ®7Jù¤ð»¹éžÙ©ÛÂa´K-4ßñm§Íeæy_{ÌU±ž1·§¿ZÚó½èó½é{ÚÖ´w¹C[Ò/5 ßO:ŒQ:²†û9dœ(åJo)8ÈÝÐb¯é\ØX-µÌöÒì8ìÖÞB"`aBîoçGïGïUÉ­ÉæÒÅï6–ÞLêqÿ×þiT<ïzšÉ÷j }"oæµ£jlºoßFæú7Õ}ôo¯(î,o£}WßFú±¾õ_}èÆú7Õ}ôo  íß<¿õÑÿô#Kº¡-‡“þº7þ„hÝ_˜bï§êÿ6}](û‘ô_’&ÝXwúEéÕÛTÒµí.%„C:Mš’$©Àe!†ãÎ{ô­mÔn¨…YAÝ*jJÌç?áÉ´gÔ¥–HJ<’J™i\N³Ô \c°>ÕB‡°À“B—i‘<1Æ&¡á·¼¢¾J-à‰ C¸Š_01†A#ýjî©e¨ÜÍkq§êKk,!•ÒHÌ‘J¼¡‡ Œƒž9õ«Û¨ÝY}bz_§’êW±B¦‡¦.‹¤Cb%2”,Í&л™˜³A’p;VŽê‡u«9ÍÎNRÝ” ¢¬M=ñf¿ï?þ„jÖúϱlZ/ûÍÿ¡±¾¿IÀÿºÓÿ ü—¯üYz²Æú7Õ}ôo®£"Æú7Õ}ôo ê›6ÿy?ô!TwU‹æÍ£¼¿úª;«â¸“ýê?áýYîeŠôŸ©6êÏÖtÄÖ4×´ižܲE2Z9ƒ+ ú*Öê7Wƒ¸IJ;£ÒpMYœôÞÔ5%µMae‘ 0D°[ùq $b¥‰f;@êì*•瀒wÇqfó:3^X¬ê’´€¨,0Ê\Œòq]vê7WDqÕ£ð»|‘“ÃÁîŒ(ü*‘Yß[Gw„¹{vº`‰Qq€@9òûŒô«¶Z8‚ÂþÆêU¹¶»ši6(BJK2’<±äcŠÐÝFêÎXš’VoúE*1ŽÈÆÒ<+a¢ë7:…›J¢X#…bi¤p¡Içæcž €1ÆÞ:šßÝPî£uEJ³¨ù¦îÇQŠ´U‰w|ñ×DÿÐ…lï¬Ù’?úè¿ú­mõõ¼5üž«ò<|ÑZqôýHu—·¨×´Ÿú «õ•«·üIoÿëÞOýÖ­}†©äÈűlC0ÿ§™ÿôkU­õIA¶»¹·n˜Ò§º±Î2EK¾¹¦­&Š,o£}WßFú,o¨¥O0†S‡sÜS7Ѿ¦QRVbi5f3dþ‘ÿßgü(Ù?÷cÿ¾ÏøS÷Ѿ°ú¥/é™{ Ù?÷cÿ¾ÏøQ²îÇÿ}Ÿð§ï£}T¥ý0ö²îÇÿ}Ÿð£dÿÝþû?áOßFú>©Kúaì 3dÿÝþû?áFÉÿ»ýöŸ¾ô}R—ôÃØ@fÉý#ð#þ·WúAõx>ÇwÿLï³ÿÄÑö;¿úcÿ}Ÿþ&¯ï£}[«ý ú¼ c»ÿ¦?÷Ùÿâhûßý1ÿ¾ÏÿW÷Ѿ­Õþ}^±]ÿÓûìÿ…\´·à³6é‚G@=;}ê'ˆ©5fÊ(ÅÝ7Ѿ«ï£}bhXßFú¯¾ôc}ê¾ú7Ðôoªûèß@ šÎ^2=A¨¾Ï?ý3ÿ¾øTûèß^Ul—Zn¤¢îû;”ñõéÅE=‘Ùçÿ¦÷Ñÿ >Ï?ý3ÿ¾øTûèßXÿ«øÏïeÿib;¯¸ƒìóÿÓ?ûèÿ…gŸþ™ÿßGü*}ôo£ý_Àö{í,Gu÷}žúgÿ}ð£ìóÿÓ?ûèÿ…O¾ô«øÏïaý¥ˆî¾â³ÏÿLÿï£þ«m)?3"PIþ•6ú7ÕG À§~W÷±<ËÕ¯ø&ØÐ"ðÀ§oªûèß^ÂI+#…»êËèßU÷Ѿ˜7Ѿ«ï£}LûdBÈ#©µ´ ü¬Œ=I#úTÛèß\X¼¿‹·¶Z¯‘½MJ7ä{}žúgÿ}ð£ìóÿÓ?ûèÿ…O¾õÃþ¯à{?½ÚXŽëî û<ÿôÏþú?áGÙçÿ¦÷Ñÿ Ÿ}èÿWð=ŸÞÃûKÝ}ÄgŸþ™ÿßGü(û<ÿôÏþú?áSï£}êþ³ûØib;¯¸ƒìóÿÓ?ûèÿ…gŸþ™ÿßGü*}ôo£ý_Àö{í,Gu÷ †®B «[ê¾ú7×§†ÂÒÂÃÙÒVG-ZÓ«.i»²=Uó£Þÿ×¼Ÿú ­Êç︇ì‹ËÜ/ÇïÀfº õ0«FÌ$W»³Šñ“r²òާ §Ûü:VyÒoù/â#þš[äþŒ?•Vò§jЮÙWÿóÿmÿ€­ÿÇ(þÊ¿ÿŸûoüoþ9E>Ÿ`»ì«ÿùÿ¶ÿÀVÿã”e_ÿÏý·þ·ÿ¢Š=…>ÁvÙWÿóÿmÿ€­ÿÇ(þÊ¿ÿŸûoüoþ9E{ }‚ì?²¯ÿçþÛÿ[ÿŽQý•ÿ?ößø ßürŠ(öûØe_ÿÏý·þ·ÿ£û*ÿþí¿ð¿øåQì)ö °þÊ¿ÿŸûoüoþ9GöUÿüÿÛà+ñÊ(£ØSìaý•ÿ?ößø ßürì«ÿùÿ¶ÿÀVÿã”QG°§Ø.Ãû*ÿþí¿ð¿øåÙWÿóÿmÿ€­ÿÇ(¢aO°]‡öUÿüÿÛà+ñÊ?²¯ÿçþÛÿ[ÿŽQEŸ`»ì«ÿùÿ¶ÿÀVÿã”e_ÿÏý·þ·ÿ¢Š=…>ÁvÙWÿóÿmÿ€­ÿÇ(þÊ¿ÿŸûoüoþ9E{ }‚ì?²¯ÿçþÛÿ[ÿŽQý•ÿ?ößø ßürŠ(öûØe_ÿÏý·þ·ÿ£û*ÿþí¿ð¿øåQì)ö °þÊ¿ÿŸûoüoþ9GöUÿüÿÛà+ñÊ(£ØSìaý•ÿ?ößø ßürì«ÿùÿ¶ÿÀVÿã”QG°§Ø.Ãû*ÿþí¿ð¿øåÙWÿóÿmÿ€­ÿÇ(¢aO°]‡öUÿüÿÛà+ñÊ?²¯ÿçþÛÿ[ÿŽQEŸ`»ì«ÿùÿ¶ÿÀVÿã”e_ÿÏý·þ·ÿ¢Š=…>ÁvÙWÿóÿmÿ€­ÿÇ(þÊ¿ÿŸûoüoþ9E{ }‚ì?²¯ÿçþÛÿ[ÿŽQý•ÿ?ößø ßürŠ(öûØe_ÿÏý·þ·ÿ£û*ÿþí¿ð¿øåQì)ö °þÊ¿ÿŸûoüoþ9GöUÿüÿÛà+ñÊ(£ØSìaý•ÿ?ößø ßürì«ÿùÿ¶ÿÀVÿã”QG°§Ø.Ãû*ÿþí¿ð¿øåÙWÿóÿmÿ€­ÿÇ(¢aO°]‡öUÿüÿÛà+ñÊ?²¯ÿçþÛÿ[ÿŽQEŸ`»ì«ÿùÿ¶ÿÀVÿã”e_ÿÏý·þ·ÿ¢Š=…>ÁvÙWÿóÿmÿ€­ÿÇ(þÊ¿ÿŸûoüoþ9E{ }‚ì?²¯ÿçþÛÿ[ÿŽQý•ÿ?ößø ßürŠ(öûØe_ÿÏý·þ·ÿ£û*ÿþí¿ð¿øåQì)ö °þÊ¿ÿŸûoüoþ9GöUÿüÿÛà+ñÊ(£ØSìaý•ÿ?ößø ßürì«ÿùÿ¶ÿÀVÿã”QG°§Ø.Ãû*ÿþí¿ð¿øåÙWÿóÿmÿ€­ÿÇ(¢aO°]‡öUÿüÿÛà+ñÊ“|OÍ?Ù¶ þ®h¢aO°]—¬ì"´%÷4“0ÃHýqè;ô«TQZ$’²ÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/zkarch.jpg0100644 0000000 0000000 00000057727 15051152474 031067 0ustar00rootroot0000000 0000000 ÿØÿàJFIF––ÿíPhotoshop 3.08BIMÿÛCÿÛCÿÀ¿ÃÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € üÙý£¿oÿüý§¬ÿeƒ±_Ç_ÚÏâOü(mö†×äøUã¯ÙëÀÚG†<âˆ>-øo§¦¡wñÓâßþԟ^ðã=¾Šu"–×v²8P&òÀ8¯øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-èÿ†òý¸é ß¶?þ$'üÇÿ¢Þ€øo/Û‡þ½ûcÿâBÁ<ú-è›ñ—üƒö¿øáx÷ÅÿðFÿÛGðŸ‚|7®x»Å³|{ÿ‚^®—áß é—Z稵žûWÞj7bËM²¹¹6Ö—W·/*ÖÞyÝ"`ÒÏŸ´þ|qðþ›©hÚÆ_…Ÿ¾+hšF²mN¯¥i<%¤x¿MÓuSe=͙Ԭlõˆmo¥Íůڢ—ÈžX¶HÀ§@P@P@P@P@P@ùC¥Êr|yÿh¡øMÿ­}ñª€?W¨ € ( € ( €?à°¿~;ü)оx/öOýª¼Yðwö®øëâ?|5øðSþ ýž|Oá&š-QÖþ(ü]×¾6ü.ø‘©ø à¯ì÷á«}GÅß|IáC¤µÕ†©ká•ûg‰õÿ 8ïÚGþ iñöŸÂ¿ 5ßø7ö­¿øû"|ý¢?kïz×ÄOþ#ø‹ |Hø§qð þÎ_<#ð³Æ>ø‰ã½cÅþñ7/| gâ/ø_L´¾ð¯´]jãÄ+Ñ ˆÒ~&ÁR|[àÛSRýžìþx[Wø/àÏÚ{ödýŽüñ*óâþ¡¤üfOŒŸµwÃm7â_€|CàO€ãá…ý‹>xjÇ^Ðt¿ø¾óâvƒ¨#·‹µÍEÔ4O‡úõÄ  þÍŸðQÿÚÛã/üËÅÿ²'Å_ˆw_>xWöÕý´¼'ð»âÅÏÿ†÷²~Õ3|½ðü#¿±ƒµ«Ÿ]é^Ox'ÄWŸüiãmgLŸâïÅ>ÆçÃ^ñV…?†|A­\}cðŸþ ¨|Eý£þ?|1ÔeOˆºGÁoƒÑ~ÙvZGÆ-"ßâ–©¬k^%ýˆ¼V¾ø…¤xƒÃ^"øேöÏñâÕ®þXü3øÅñsÄ:{éVWÄ­Àúß‹4m20ðWü;∿coÚ;ö©ñìayá+ïƒiûø—À^×>$ü@ð€¾+x öØø…áOøJî?‰ÿgo…wZ.»ðòßÄ7u-À~1øUªÀš¯à‰Þ$ðψZÒ€=ÅÿðX¿ø?à1ñ%ÿ쯧ßþÑÑ~Þž.ÿ‚{Ý|4ðoÄoŠÿ~ÿÂÒðwÂýoã¡ã]âÂÙÆŸ<]à»hæÆÓKðïìíuâõñÌ—ÞÕ4.x›^ÑÀ=—áü³â·Ç?‹ÿ°×ÂÏþÉÒèûTþÊ^$ý¬¾,Ü|Lø®Þ×gøÇÿ þxÃA‡Ãp|:Õ¥ø£­|EÓ¬¼0 ½ðJkPÉk¬ßG¢é¦óì àª¿jo†V?³w‡¿eïkZ¡ñâ ø‡áß„Z‡ìµ'íYâïxWáÞ¯¯Û§ìñàßÛæÛà×Ä t ytÝ_âW†wÂqsàôÇ„g±™ïõ P|iÿ¹ñ7ÂÙÃö$ñg>øŸöÓñ×Ç?ÙgÇ?´W3ðoÀÿO§¬ZçÆÝ/IñG‚n5p·uŸÛßö‚Öþ<~Û_> ~ɺ7´Ø÷öyðÅ»oø‹ãE烼WñSâ_Äÿ„·ß~|Ò>Û|+ñ-΃{®Ï£k~×¼[}¯ÝÁáÉãÑd‡A×.5{»=æÝ?þ Ñ«~ѼûBüð¾©á/ƒ-ÿ‚ˆÁ<eïxûIñg…5-Oâ׆?h/þÏ|n{ýÅ¿ ¼W¦h>ðoˆ>.x‡àί‡sŒõÍSÀÞ.ŸÃÞ3øa®Ça¨éàLü,ÿ‚Žx‡âOíñŸá䟼¢| øSñö‰ø;aãÏø_Ú-×í â߉¿²þ…áüF_ì§}àÄ:·õ[O¼×<ãÏxŒ¦›§xƒÄ>Ñ|+ãOk@þ {ÿ7ñgü>Þø—ögñÀÏá¯Á?? |E6¥ñ\ðïŽ>üsÓüQ¢iןÄ_‚_Ïü&þO «x‚çÀ6ßþk–ÚÝ…ïÃ0µ´Õ%µýn € ( €>jý³ÿäÏ?jÿû6¯Ž¿ú«¼S@_ü£þQóû ÿÙ›~Ì?ú¤¼@dÐ@P@P@P@P@P@~Pé_òœŸÚ(~ë_|j Õê( € ( € ( þ8þȲwí;wáÛÿÚSö`ýÿh[ï[êVž½øãðSá·Å›¿ ÚkYÍ«ÛxvãǾ׿Ñ-õY´û u(t×¶ŽúK9.–V¶„ µ?ÙöRÖ¿áOcöeýŸµAû=ÙéšwÀU¿ø7ðîí> éº%¤6:6›ð™'ðë§Ã½;H´¶µ·Òì<"4‹M>;KE´†²[ù@«û;ü×~.hõ¿ÿuŽÞÒ¥Ðü9ñ£TøoàëÿŠÚ2˜äÒ´ˆWZ4¾,Ótöž!ig«CÅ4ñ*,w,€^þÍÿ³¾¥0ê?~ êÛüa_ÚÞ+ß…¾ºŽé+NŸ¡Iô)/Œ+;4Ëñ1øÕefkaÉ47†¿g€> ø—ãoøð‹Â¿>%Z-‡Ä_оøoàíâ7,•áZxËÆºnmâO[™-íâÖu+Ôvµµ.ÛAå€|ññ?þ ¿ûüDýüû/h¼ð;á'Åü6ø“ãýàÃï† ÓÅž%øeñcÁ_4Ù ü:„6º’^I¨¦¥mðá¼8|!¤š„²ê ¨E£­âßK%à›í.ÒLѾü*ðæ»á¯x{áŸÃí ÄÞ ðÿ ¼â-ÁžÒõß ü/º¾ÐµK¯†þÕì´Ø5 ÀW:Ÿ…ü3¨Ïàý.â×ÃÓ_xwB»“Nk#O’ÜãGìÿð#öð¤^ý¡> ü)øéà›}NßZ·ðÅÿ‡Þø‘á«mfÒ9¢µÕí´Oi:Æoª[Eqq „6éwWÄ“,sH¬ƒã/ÙWö`ø‹¢|4ðÏÿg/>8ðßÁ‰í.~ø{Åßüâ=áUÅ…µ¥Œ¿t_@¼°ðKÙÚéú}µ©ðݾ›äAac[RÎÜFêO< x£ÅÞ8мáMÆ¿ÃéãÏé>Ò4ïxÙ|'a>—áUñwˆ,ìáÕ¼H¾Ó.®tïfîôhÖÚißf·šH؆Ñÿg_Ù÷þð¿Ãü ø7¡ü4ð?tï‰^ øy£ü1ðN™ào|EÒÓ]²ñ¤²x¦Úþ-qÚø€&û:~ÏÞøÁâ/ÚÃÿ¾h¼_¢Gá¯üjÒ>x?Mø¯â_G%¬«¢kßlôx|W«ieìtòöWÚ´öòÿgi¢HÝtû!¿ ?g¿€ŸbñD¾ |$ø1õ·ñ/ŒáøSðçÁÿcñoˆ\Î[\ñ*xKGÒ\Õ¿Òn1¨jbêéDó” \0°P@P@5~ÙÿògŸµý›WÇ_ýUÞ) /þ Ñÿ(ùý„ÿìÍ¿fýR^ ²h € ( € ( € ( € ( € ( €?(t¯ùNO?í? ¿õ¯¾5PêõP@P@P@P@P@P@P@PÍ_¶ü™çí_ÿfÕñ×ÿUwŠh‹ÿ‚tÊ>a?û3oÙ‡ÿT—‚(ìš( € ( € ( € ( € ( € ( Ê+þS“ãÏûEÂoýkïTú½@ÎGì'ûü"ý°|ñûãgÇOŠ_¶nµãÛÿÛËþ àï7Ÿ·Ÿí“ðßÃzw…¾~Ø_üà½CðWÃßÞðŽƒ¥øÂÚ•£ØÙhú-”ÛYÄ Ë¶¿áͲý?·oþ,¯öýÿ茠þÑû ÐóûvÿâÊÿoßþˆÊ?áͲý?·oþ,¯öýÿ茠þÑû ÐóûvÿâÊÿoßþˆÊ?áͲý?·oþ,¯öýÿ茠¿à¨¿ðNO‚?²çì ûL~Ð?~+þÜ^ø§ð·Á>$ðV½}ÿ ý¸ñ?ǽ_@Õ¡k[Û˜žÓUÓo-HKÂYT€éZ€ ( € ( € ( € ( ý£ÿj€Ÿ²?Ãkß‹_´OÄÏ|2ðMµí®‘cw«½Õæ³âê,ɤøCÀÞÒ-õxóÆšÔªÑh¾ðv­ø“V‘ddâ)J~|Ù|Yÿ‚¢þÙ¸¿ø ðÛÁ¿ðN€::OÅŸÚÇÁ“|\ý¯|c¦9ßµáoÙ[Dñ7‡|ð^ ¯-ÒØüsñÿ‰|_ Ãsªü#Óeͪ€o§ü÷Æþ*Úÿà¨ðSo‰>!º>n©'‚¾;x/ödð¡¸8tO þÌß >¶‹dÌv¯­êR†,^ò\¨P üWÁ¶¿½Ðoø*÷‡ïט¯Çü?ãÇ‹¼§êû#âN­ã σÿ,îôKˆFˆŒ κý—?à§Õ¿f¯ø(&ûPhšo6ÿ?࡟ |u6±g÷å²ÓÿißٛŸ x¯áž—¥|Ð|úŽƒã‰~|bð¦³â ¿øD~ë:W‹-|m¯>¡ðͬ|Aâ/|BðôŸ äžÛâ‡Âê×·÷×>ñtïÚñŸŸäøðÛSøÂoŒßM¿ÒlG€>x2ÛÇ¿uíkè¬Mî™á»½oÃÐ\ØižoÛ5k†Õ"6v1ËpR›ÁðõDuÿ‚eÿÁW@þ÷ü2¿…›ñÚŸÚOüs>ÔŸðõí:uOø'wü{JAË?ü1еý£¹ò¼â_ÜIHa‘ð©4Öÿ‚Î~Ǿ/ø_öÍý#½ïÇÿØöÑøu¡À£––÷ÅW?ï¼'¦Û"åÞûR×m,D`¿ÚJHèÀÿßi‡ºgÅÙïâÇÃÿŒÿ µ‰®-l÷öb#¥\_è÷WQØë:iž$Õ4[ÿ³jºd²,7övò”ë´ùC¥Êr|yÿh¡øMÿ­}ñª€?W¨ò‹þéÿ&Ïñ¿þÒ/ÿ6ÿÖíøó@Ùûfh_´мYû%üðÔVºwýB KâïŠ ¾üI×4»Qâvµf‹y¡.™â ò-g¸2C€|µàŸÚ›þ §ÙóÃWŸoü]%÷ìyàŸüBü(ø¡á?øßãOü3wüiâM>ãÅÚ%¹øKð—Ä)ñ¿Ã~øo®ü1ñ¥•ž±iqâ«ÛÛ JÔ ~ÒzÝí߇¢ý õ_.·qsákMCHøÆþ:ø'aĽ _†÷~Ò>ë7ÚbüPÔ-<©Üø‡Bú¯öUñ÷Ç?ëß5?ŒZoˆ|=£Þÿ‰ñ?à øá.¯ðÛPð¶‹ãÙëáÆ·ã­çQºñŠtÍoRÓþ-?týkÖúöµ¬ü=Ö,ot=gZÕl®´¢óGü#þQ9ûmÿÙ*³ÿÔßÂtú»@ynñ¿àþ«©øûF±ø™à™5O…¾#¼ð—Ä+9|E¦ZËá/éÞÑ~$êzV°nî H'Òü â'Äú£«¼n™q4—ÒÀö:„v nãOk Ö<'¤ø³ÃZ§Š|;µ×ˆ<5§kº]ïˆ4+kÙn ³¸Ö4kk©u2 ¹í.¡¶šöÚç–Úâ8™Þ€t´P@P@P@#~Þ¿´í×ìcû~Ñ¿µŸáñî­ðWᆽã Á³jI´ñˆaX¬<=¦jZž×}?HŸ[¾°þØ»‚9®àÒÖòK8'»XapÌø%—…þ ~Óž5×jÏÚGâ'ˆ><ÿÁN>K/‡>-|4øçà³ðË_ý€5-n$–„ÿf}WS×->x.æÕR×Nøå¥j^3ñÇ>ŸMñg^Óïît]4÷€ ( žÿj/€ß³Ÿíð_Åßjx'Æ?RÊoëïã™âÒ,| Ú¼÷ÑøóIñˆ»Ó5?‡¾ ðœK>©¦x÷@ÖôsÂïê6̓ÆÓÅŸø%OíÛão~×þ=ý…ô?ˆŸ¿kOÙNø㿎²7íÁñ@ŸÃŸ<{áï„ÿ¾|)ñ÷Ã8|U2Â?j?xWWø¯¢[xGö§ðdž.¼Ð|I£Í/Äç¶ÿ„á?elÿù3ÏÚ¿þÍ«ã¯þªïÐÿèÿ”|þÂöfß³þ©/PÙ4ñGÇOø)'ü÷öfׯ|%ñ÷öÐý™¾øÏM`š‡¼_ñ›ÀšwŽìX¨qöÏmÉâ»`ÊÊU¦ÑÑ[p I ž?áùðIsÊ~ÜŸ¦þ~-ÿá-¹µ#ûËw†dµdï½f*G9Ç4âoø-¿üç\ð§Šô߇ŸðQïÙ Ãî|5®Cà­Gâ/Ží4ÿé^-›LºO ßø—IÕ/|3} Úk&Î}bÂ×QÓo/4øîm­oìî$K˜€>ÿ‚eÁп±Wí¡¬Y|ý¤oü7û þÓÐjrxdéþ"ñ®‘âÙóâ^½kvúaŸáWÆøe‹@_í«È–]Añ”šI¿mGNÓ<â_ˆ3Èo\ôóöáý¸|wû.xªîÓžÓüM¡x_öuñ÷í®[IðÏâÿާñ^×,4ÿøE¦ñ·Ã‹{¿üѯì縸¾ø¯ñ^ |áØƒêº·—¥iZœªßjÿðPï…úw‹>0øONøWû@x®OƒÐx„]ëžø¦ë:5O|MðgÂOè~ ¸‹Å ¨GÁþ1ñÖ–š©ñ^›á[;ÝKñ_ˆ¼;u­è¾Õobɺÿ‚”ü±—í÷¾ø°žƒá½‡Ä]Oâžœß|MàM4j?>#~ÑqxHMዾ ñ&¹â(~|+ñ„·:—…|9¯øûfÚÃOµñ¥Ü:Œ7 _^ÿ‚ø+ÂÚÞŸáüøé¥x–ÞÿÇ¿ôf‡àÕóü"°ð«û9Øêú÷u-/ã-ö‰­é—šoíMð›ÄÚ]¿ÂíOâ&».‹y¯ÛßèÚw‰þðÞ½aâ¯è>(Ò…âé~$Ñt½{M]BÆïL¿ÅŒ…˜¾Óoâ‚ûO¼÷‹›Ø!»´›}½ÄQÍ Ú €?>£ÿ‚€ø!?kmCöUÔ4Ï[êöß®¾[Mgñk×ÿÓZ±ýš­iÛßkÿ™i®è àðœ—>osQUñ:YE>•›}&¡d°?ॿ±„¿ uï‹v?µgÀþ¹Ö#×n¼;ðão‰uí?Mðÿ€¬~(k^,“ÁZÃKƲ|?Ó~êzŠ.þ#Aáù| ä0·ˆ´ì`Ö¼Sû^þÎÞ ´Ô/|Oñ-*ßL‰PÜ´¾ñ”î÷þ'xàÏŽ´ë(m|;<Ú–«cñCâ—€<£iZr]j>0Õ|Kgÿm®¿kíŰ íûû0jZrÝx[Æšÿ5ƒ^+øïeáï |7ø•{®Kà?ÄK=aõKi¼%m„üDš÷ˆO xÊxxË÷›L‡^¹°°»ú“À¾/Ó¾ ø#ÁÞ=ÑíµK-'Æþð÷‹ô»=sN¹Ò5«M;ĺEž³em«é7©æ—ªAm{Z†ww6Wk5´è’Äê:ª( ï xöçý‡iÛƒâÂߨ‡Âÿ¶ÃoÚûãöñßIñ7ÁïÚ3áoÀïø;OÒþü6øWiðÿÄŸ>1hþðúÖ™?5-rûâƒñ[P½ñõψ›Uñ%®©ÆÖ¶àÑÿý°íuªÿÁ¿nˆn—‰HøµûkÖ¡ÇQõ·ír¦zHÍ~^kŸµOí7ãÿø*—Åi[þ Oû_Ýêþ4ÿ‚_x3ᆵðŠOŽ?±¿€~ Ùø+Pý£¾/Ë/Ä‹oZþÔPxgFÒu ½^÷ÃzDš/cñö•¯i7²i}Šiº¼àŽ_ðIßø.ü¢ûö®ñÏì]¡þÏÞ1ÿ‚šü;øcñ7ÄþÔµë^ Ðþ1ü(ðLJüW©ønÇQø‰ûXxïĵqk›y=÷‰|CË›g˜Ÿ+ ì¦ÿ‚‰_蟾=è¾+ø«Cðsöz½øÖ<{ñOÁ·ÿ¼e­hø'ðÃþn©â¯xnOøsieâ8¡ŸÂ¾ðï…þ8x×Ç×þ%»Ñ¡·ð…ÆŸ/ˆµ {^³ûsüðÆ£¬éž,Ò~$x^ëB±‚Úñu_4ÞÄy¼àˆ-ð?LM#QÕ&Õ~/ÇáoˆÞ »‹ÃÚ|W:F©}©Þ蚈µMoÃ~*Óô@ ËÏÛö{ð_Äÿ |*ñ™â¿üYø§caâÝgÁZ§ƒí­|m¥ÞjSx×ÀþŸÅº}¨kÞ(¼ñ%×Á?xWþ#ð5Ÿ|=qa០ÜIâ+_ ø›áþ©âü-ÿýžü]cð?PÒïµÕ·øÿãcÀžš_øE'²‹Äº6©ám*ÿYÓ<[¨hZޱ>¥ã¯C…üªø«Æ°ê·‡´Ý;á§Å ¿jx[öéø7ã=cá¾áíâeî³ñwÄ^Ó¾XÂñIâüKðWÄOˆÞøÈ&—Q[]+áF¿àŸ„¿õ»}k[ŸMס›Âwþ¼ðÕ¿Š®tÍüæ¯ø.‡Ÿÿ•ý¸~Êb?ð©mþÎn˜ÿðšxWÉ3ˆÈÅæmóå7m!°huÒà¸Ñþý¼ÿ¤¼ÇÍý”¿kÍ;+û|üoÕ<¬ýß?þ©¶ýÿ³7Ü  º_íÑûPþÏþ|<ÿ‚‹þÍømàߌ=ðçÂþÖŸ³Å}gâçìü¿üo~šW€|ñkÂÞ=ð/ÃoŠÿ'ñη-¯…ü3â[­/Ǿ¹ñn£¥èZ§Œ4i5K‰€=›ãì |o¼ñŒÚ¯ÄKý×ÄŸ´·‚jí. _xoVŸFø£ðãà7‚> xCO»¹× ô:Ç€÷x Iñ7‹¼5–‘wã‹ ß|:ñ·?Ãÿø‡DÕ=kÀŸ²¯„¼ñËSøí¦ê÷ø‹XRjV_Ù:e¬wÒüxÕÿg}SS–óPEõÔž‹ötðæ›¤¼ï#Me«]GpUtûRÐ@P@P@H@,p œ8-êv«68ô€?(¿à¹Ÿò‰OÛ§þȽÇþ¤Þ xý¬`_„¿µ·áŠÖZïŒþþÔÿ ln->~ÖŸu ? ügð5´Òý©ü1«ÝÜÙ_øâÂÍNëpñ'Â/‰ú7Š|­[\ߔҴíVå5{p™,¿jßÛûöBdžÿmÙcÄ_µ_í7ýNý°¿àŸþºñ†«©XD|¸5Œÿ±}î¯qñwÀúóZÁ-þ»}ð3Qøãá)n¦ÿAÓ|7lÑØBÝøwþ Yÿ­×o—ªþÛ>kñô¿ ~К†µû4ø»MqÃCªøSöѾø‡K™(ÑßiÐ6å!wc4ÖkŸðXø%‡¬¤Ô/ÿट°ÝÄ)vCý¨þ xžøHôÏ xÇVÔ¦Hâ´w=”šò«Ÿø+ßÂŸŠ ýû üý§ÿooÞfÛLÔ~üñ_Ã/€vwÒǬÞ-ý¨þ>é > izŠY5 k¾=ÕL í§xS•cû~Ð?¶~³¥øçþ ¡ãßkß ´íFË^ð—ü·à¡¯/커ÞY\Ǩi7Ÿ´5›M Æ¿µÇ‰´»˜l'okú?‚þXê¶2ËÃ/¬É©°汦éÚ?üçözÒ4‹ -+IÒ¿à’Ÿµ>›¥ézm¬:v›§XþÖÿ±e­•……•ªEmgegmVö¶¶ñG¼¤Q"FŠ íoÛ?þLóö¯ÿ³jøëÿª»Å4ÅÿÁ:?å?°Ÿý™·ìÃÿªKÁöM~ ÿÁ6|#à_ø'ÏÅÇÿ‚nüiøUà üWÖo~$|Dý•?k]7Áú%•ïí×ðÖ-kQñ‰Çþ7[­Þ~× ôÝ]!øÉáÿj—šŸŒ¼=gkñ[Âóßx~çX‹Cýê 0øÏàÏü@ø[ãÏøÄÚ'¼uâO kZ„<}¯ø>×ÇÖ>×5{)l,üb<{¨iž%»ðÛÏý±a¢^j¶ýµ¦¡s”³šüqý†?àÜïø&_ìGeã¹>Ü~ÓßÖðëZÇÚ†k‰úûxŽk†Ô.5½Á×vß¼3¨EªK=î™®ÚøZëÆÖeÓí^0Ô®#û[€~Öxƒáç€4²ÒnôÛý.ÛÅ7³i/s®Å§_èº-õÔ¤¹6·š.u Iô­>KpKÃÿ²ì×áŸk´¿‚ÿ “Åzǃô‡–÷’øÂO†¾øoÃÞ°ð/„£]þ“Úž§¥ÞxjÂTÑî-5-F×ìikyÀ¾“ðà^ƒ¢ØxsDø1ð£Hðþ—§ëúN›¢i¿<#e¤Øi~+Ö¼?âOéÖš}¾‘­½‰|Eá? ëþ!µŠ%‡ZÖü5 jÚ’\ßèÚuŸ&§û/~ÍZՇд­cö}ø)ªiž9××Å^4Óµ…Þ ¼²ñg‰–}vëþÚÏ¢Iµ¬5׊|Ss&£¨¥ÅÔ—'ñ$ÒJÒëú»^{Œ0Ãm VöñG¼¤0A ,Pà J(¢‰¤qÆŠ¨ˆŠP€$ €x Ç×þ)ð‹á¯Ýx³Ãv·6¨÷¢i%¶bßÙÒMGáæ©}àí{X¹øWá­{¾ ^ø›ñK^°Óì|S¡xóÃ#Ô¯´½cÆwºn½â­s@øã½/Wñž½i©x¯R¶×‡Ûu™äѼ=&’ôG„ü1£ø'ÂÞðg‡¢¼ƒ@ð‡ôo hpê:®«¯jèúm¥i‘_kšõk7‘ÙZ@—Z®³¨ß꺌áï5۫ɦž@‚€ ( €?#¥Òmuÿø-Å- úKølµ¯ø$wÃ]&îm/R¿Ñµ8­uÚÏã}Äšv¯¥\YêšUúE3µ¦¥¦ÝÚߨÜî¬î ¸Š9Tô?àGì÷ð;ö`øo¢ü ýž~øàçÃO«3Áþðý‡‡ô‘u*Æ·z­úÙÄ—ƽ©4I>±âb{ýsY»Ýyªê—rI3| ÿtÿ“gøßÿiÿ‚›ëvüy Ñ›¯„ß/¼-ñÁ7~Ò'ð§Å·ñ\¿t9#”ØøÂOéCDñkjê%!×´,o¼§‹|&Æù¨‘ñìÕð'Åž'ñ—Œ|IðÓÃú߈~ h·:‹¯u}q­i{¤h¾½kÞeéþ"¿Ð<%á ÿź]•—Н´_x?J»Öf°ð¯‡íôà ~Ëmõ ê©ðãM’ûÂÚMÞ‹c%Þ§â èµ[;ÛÏjsMãK[Ý^âÓâ­±ñÇÚõž¿ãè×ü §Ùè“_Þݾ‘áÝÁÞ)ñ†ÆÜ u üý›¾,øöŸý >+¼-…‹KðG¾A¡GzòE³Æ>;ð^Ÿ©{©Úº€~¹Ð@P@P@|£ûVþÙŸ¿cíÁן|Y㟊> ¹ðÁ‚|ªüJøáñ§ÅÖV-ªjÃo‡Ú ûf©ý¥¯öŸ‰·á¿Ú>+‹Ûm;Ãó]|:ÓbñÖ5ì¾ ñž§âv7Dþ…þ4èß ð _x[Æž7×þ'øí¾øÂ^±Ñ/uýoİøÇ?¯bWñ'ˆ&øGDÕ~2ÛÞÉðïÁ~/ñáø–ûI¸Ô¬µýJð†­¨A¬êºÇ‡µ[Óµ‹mßQŠ #Pxn.-!.æþ'ý¬?cÏxÃÅVÿ~|uø}}ªxÏJ烦?µ×üZý׆?à‡ÃóIÄSüdý¾ÿg ið³pñþøãÝúÆ„îZX]¹„ H4ƒ§þË_·oíOñ«övø¯ûtø§öcøMð×ö_ø·oñûáŸìýû,EñâGмCñZÃÁÞ/ð_‡5‰¿´ÅáüñxoÃzGµÃ{࿇+öiÿ¢ÿÿÅdþÝŸüáèÿ‡Å~Í?ôC¿à£?ø¬ŸÛ³ÿœ=ðø¯Ù§þˆwügÿ“ûvó‡ þû4ÿÑÿ‚Œÿâ²nÏþpôÃâ¿fŸú!ßðQŸüVOíÙÿ΀> ÿ‚ ÁBþþÔÿ°Gí-û=üý¿à¡Z÷ÅŠž ±ð׃tSþ ÍûjxgN½Õ[Å~¿1Þëþ ø'§èºUºZÙ\Ë%Þ¥{ml‚<< ô-ñKâ€> |8ñÏÅ+Ò|ðÛᯅµ¿øçÅúíÇÙ´Ÿøgöêz¾©{(W‘’ÚÒÞFH Žk«©|»[H'ºš(\ó?þ ÷ðãâÇŸ‰ž<ÿ‚¡þÑ~Õ¼ñãׄ­>þÉß¼QoömwömýŠíµUñ…ôïi{¤M#ãí¬Çañƒã<-5Õæ‰øðåç·ÿ„îÕ€?[( € ( € ( €?%ÚÒûþ ©zÞ)….æÐ¿à”ÞÔ> ýµD©¤]êßµ§tßÚ"ûCIw-­þ¡coû8Xk—–¡.Á4»9äò'Ù é'Ńÿ >6øzÏÂ~øGâ„l|C¢ø®/ xïBÓüQái¼CồáíWPðæ±ÞªÜh:˜‡Yц©ey™®Yiºå”pêú^{jùÇÿÅ‚_ø$oíÇmm Vööÿ¤‚ÞÞÒ(`†/ør8¡†(¤QDŠ©hªˆŠ@ û×ã_ìóð³ö…ÿ…ckñoÚŒü?ð·â4ß´ßxƒHðâ]zO†¿þAgã?x›DÖìµOÓ>&jÞ Ó­à]>öÓÅš?†µTÔ ¾Ÿs§ß€|×áïø&÷ÀŸ ø·á7Š4-_Æö¶¿­ü5¥øsÂ7ü9¿ð¤¾øyñSâÅÿ„>ŽÆóáä×~ð÷ÁïüGÔôÿ‡ðøÿÂ…Ÿ†4? hšÎ£­¯‡íî\ÎþÁ0¼¤þÍþø-ñoâÏÄßx£EøE¥ü*×üm§ËðóA’ ßöUñ§ì©ÿ¯„í4†:>›„|+áo‰~;ñ‚®|A¢j¾2jv—þ2ñ‰´ÛQ Ð»x¯ö(ðÇŠ4?ˆ>ãÆßxkÇ^5»ñ妅áíSáݽŸ‚µâõŸÆÿZh7ß µ _^ð÷įZÌ&xSÀÞ m>ëálžÕu½SÁ¿õï x¦ßVðýç†î,ì4]_ÃÚ†¼Si6¿rè¿¿ao†Ÿ ~ |>ñ¯„‰ªøá’†öþ“ÄúìááïÙNÏÄ×qØ|9²ñeµÀø;á/ é)áýÅz‚íu]%5k/ [Iuw À¯öÿ ïíUÿŠäÿ‚Œô*Pÿ«ý€ÿè;ûUâ¹?à£ý ”Ãêÿ`?úþÕ_ø®Oø(ÇÿB¥ðú¿Øþƒ¿µWþ+“þ 1ÿЩ@ü>¯öÿ ïíUÿŠäÿ‚Œô*Pÿ«ý€ÿè;ûUâ¹?à£ý ”ú ð+ã‡ÂÿÚWà÷ïŸ?Œ?|KðOÄZ•§‰¿f GÃß b‡ö=²Ó|y¦xcÃ_/5MçN‹öšð¥¶ão ËâK+YSÅ–~:ð_Šum߯vÚ ·…tßø(O‡üqûSø£NðŰñw‚|ãx3ZñWì³oà{¯‰7ÿ¿gý/áæ•ð—WûgŽüwáØüRÑ~+ØkÉñzï‚Ð[@·ßÙZÕ­ö‰©^€lü*½ÿ‚–‹ßÙ±¾$éþ5ºµ°ø…ñsGøáesmû,hkŸo>6j?ðª|gâíwÞ ñí¥ÿ‡~I¤Hþ øWá»[Ïëë©ÇâxKZ³¶MlÞÿbEý´ŸñoÛöæmZkŸióèß ìtÝ3^]+Pÿ…‹€u?…ºÕýÖ¯ð¢]I´AàSñ/LÓþ%[¼:ìÚõÝý­ö›i¤xWŠ¿å9?í?µþµ÷ì_@dþÙÿògŸµý›WÇ_ýUÞ) /þ Ñÿ(ùý„ÿìÍ¿fýR^ ²h € ( € ( € ( € ( € ( €?(t¯ùNO?í? ¿õ¯¾5PêõP@Påïüsã_Ź~þÁß²÷‰n¼-ûQþÚW>%ÑÓâ6”¢âûöiýš<šh?ÚbXÁ m¯økF×tŸüŠö{õŒ¾9ð‹Û\M‡«Ä€r|øðÃöcø1ðßàÁ ÛxCá‡ÂŸ iÞð†…nÆY!°°Fiïõ;ÙÚ5m\Ô%¼×a?û3oÙ‡ÿT—‚(ìš( € ( € ( € ( € ( € ( Ê+þS“ãÏûEÂoýkïTú½@P@ù%ûZÛüEÿ‚™ÁW~0ø¥ÒëÆŸ ¼Mû0~Çß,/È}OÁŸ´ÙãÁ?´eïölm‡²Ò¾%|Vø÷âÝ~îXÑcÖ…´1#ÌÚ,^Xëm4:hé‘U0.©!uGeÎà®ÑȈÃÜJ¶?)ࣟòq¿ðG_ûI¡ÿ¬+ûhÐêåù1ñ Oý»5Ú‹âÏÄ Xx¯Iøã‡?f_‡>‰ñûûcÂþ!ÐþŸˆ_¿iy¾ë¾ŸÀžÔ$ø×¢üKøW§øþ?ꚯ‹¼=㯃’x‹ÁÑxoßÚ@ÜüJý¾4Ï köþðߌ|K¯è³åõß„-¼iðÛL´ƒÄWö±ö“âÏ |B×õù"Ðî.>;럵ûj? |Eðcìº5¿¡ŸÄ—žðІÃZñѽý¾´[Äñ î«ñ3Ã÷~+ñZ>¿eà½3áõö•£xö‡ý™¬ü+}÷õ­&îí|ð»Ç¿õMf¹ßOºÐ~h–‹§Å$~/—ÇRþÍÞ(øáâ={ã|fµñ>œúo‹/íô Rø£øg ¤^:ø“§ioÃßÙëW¿Äýóᦙð¿Äú§ˆu]a×üI¨µ¶»£¨kŸ þ}Y@~QÁ ÿåÿðNßû5ï‡?úA%~®Ðñ[âŸÃïÿ |uñƒâ¿Šôü6øká}gÆ~7ñv½t–šVáÍÊ[ýOPº™ùo.Y`·…dº¼¹xm-!žêxapàŽ>ñ—¿à?[ǾÔ¼âO‰z¿Ç?Úü¬ÀlõhŸµíñ_öŒðŸ…5{KéºÇ‡ü)ñGDÒµ2]“iúµ­í¤ñC4O `x|aøAð×ãÿÂïüøÅá3Ç¿ þ&xoRðŽ‡Ë–øVÚoþÑc½>)ð×Ä«-2=u?R~þÐ?iï‡/ÅßÙó⟂~/ü7ñŸìïø]³×4ÑuF×zN¤¶ò½_ÓZEƒYðæ·m§ëÚ-Þû-[N²¼ŽHب €?,~5ÿÁIaÖ¾ øƒöcÿ‚|øÏöÑý«4‹ìK¢k¯¦~Ëÿ³5åÎèÆµûQü{Ó­µ- Ã×z`K›Ôø5àS⯞&m>m.×ÂÚw+®Ûz?ì‹ûjü{âÚwö‰ø¹­þÓ?¶×ÄÏ øãã>©g/†<à/‡sëw‰ÀÏÙÓá]µõæð§àΟ¯é:V«uj×ϼ{â 2ÛÅ¿¼U®êËe–î_¶ü™çí_ÿfÕñ×ÿUwŠh‹ÿ‚tÊ>a?û3oÙ‡ÿT—‚(ìš( € ( € ( € ( € ( € ( Ê+þS“ãÏûEÂoýkïTú½@P@ù¿ûD~Ã>>×þ9\þ×±¿í7ì·ûPkÐ| ñ4ë¾¶øµû>þÑþðt·÷> Ñ><ü$}Áº½çˆ<ú–£¦x;â¯Ãÿø3â†ü?«jZú‡ˆ<>lôKP ~%Áj|¢ß^ý“ÿàžv «­|7ý°þ:ü–ïnAšüBý”þ,C£´¤n[H&ÿ‚?øŸþ ÃáO†>Ð/à B_„“j¾>ý«uÛ¹aÏÛ)æÔþ0x#Ÿbøwámé;£Ó´Ë¿ øGâZjë;xŸ@†ÜÛÝ]hôùßûWþݺ§ìÇñOEð,fñ׆O€|?ñÆ~3‡Yø‘eÿ¦…­üJ‹áôóÝÂ'ð3â'€|;¦h°µÇ‰uüføŸðcÀz~—gw.¡â«+ WTÓ€=\ýº~ øNñ6¯}£üLm+Â>%øã¤kwðx1Ú|1û3ø–|~ø©n%ÔašûáÂÏÝYhúþ¿iÚ†¡©^Á¦øcE×õ!=¤ ©~Ýß4Ï|Pøn×>!Ôø‘¢ê~"ð=ÇÄOCmá½G]ÑþørëVø«¦øOþ>ª–šmþªxµ~ øåm¬mô+ÕÑ5}6×CñΕwâ/?‰€1<+ÿýœ|_gðòïLÔuÅÿ…ñN_ƒºp’OÞXé~8Ÿõ ="óÄš7Œµ_kšž«¦üSð&¥¥øgÀ~"ñ.´ýK^»>އ_ßÁ`G|øº>9|6³øŠ<®øí~)øá†ð߈ï4KýZÚO‡Ÿ|Uðò{Ù®|;¨êºQ‹W¸ð´ºµ¬PÞË%½µìV÷ωÉö:þ`?à×ðW«_ø&ì9ÿ cAÿ‚nëŸ OìõàSà#ñ?Å¿´ï…¾!'†Í›=<^¾ðOŒ|7.¸‘ä^I£<6?Í Q’€?HNŸÿÅñû¾'ÿ‚W|(ŠÝË­iÞý¬~9ßØÆøV¸²Ñõü±¹ºˆxóQÆ@žtrG¹Ì~'þÄ áÏx·ö«ÿ‚›þÓoíökðψ>:i´/‡>øEû/é:Âí"÷ÆVšÞ“û0x çS“â·t™4Û™<-wñ÷âgÅ-3E¼– Ûx´w´þÔ·ý ñí›û:øS[øá¯xÓ]Ó¼Ið—Áv>"øy¾üX¸×|/à½Vß³èZýægà{Fâ×ÅøºÓHð„V÷w~0ñ'‡~$ø[Ãú·‰>üSÒ¼ìÿ ~%x?âç„,üuà[íCPðõî©â>­áïøOW²×|â­kÁ.Ñ5 x·JÐüI¡ëño‡uÍUÓu&Æò×PÓnchŠ„wîèóƒãWüßöjøñYøíðÏPø«û~Ò!e“Ä´ìyãÛ‚Þ6ñ´¨òL­ñWÂöö×Â/;§t’I~0|6ñÍæØ£Š »x׿ò~ÎðVo‡6óÏà¿ø)ÿÀ¿Š.²"~Õ_°^…s­¥Œ™ã\ñ¿ìíñïö}Ñ'khP´÷6ÿ´Äu<̤6@?:ÿà›ÿ·wüoþ ¿àŸ>*øûnÁ>¼£|øáãƒ^!¿Ð?a/¾$ñV¡¥è÷O/þ*h7‹?n–ÓÁŸ¼:¹áYµH`š;›M{D¼€ßh™ý ºÿ‚büKøÚ¿fý»?ࡵ'í=á)³§ðGá§ü"±§ìýâ>k'žýžì´‹~6Ñ&)“Dñ¯Ç?i70î¶Ô¬µJ*~‘ü ø1ð“öø |)øð×Àÿ >ø^ßìÚþxgIðŸ†tÄlåƒIÑmm- åä€Üj„±É}¨Ý¼——÷RË3€ze|ÕûgÿÉž~Õÿöm_õWx¦€8¿ø'Gü£çöÿ³6ý˜õIx"€>É € ( € ( € ( € ( € ( € ü¡Ò¿å9><ÿ´Pü&ÿÖ¾øÕ@«ÔP@P@Åëÿ|â¿x/ƾ'ðo†¼Eâ߇kW_üC­èÖ:¦«à«ïYÛéÚÞ¥ák»Øg“BÕu-2Ýt»WM6ú‹is^é«t¶:…ü šÿðQÏù8ßø#¯ý¤‹PÿÖý´hõr€+éš¿ƒ£ÐuMâmÞ·ãíîÃÅž#ñ¯©€Omû þÍøvÖƒ¾[ ø*ûáÖ¥¼WÓhëà»û/i²h:ž5ìš^¿mi§|Iøa£Í¯Újwš ŸÄ/Úè—:|>1ñj@4¿ØßöpÑâ´ŠËáÜ„ZøÈ|@ž[ïøÿV»×¡âÛÝWÅW·ž4¶¿ÿ…gðöóUѼ]>·¢jú·‚ü5­êzuÞ±¤YßDî~ðW†<¤M xCIDѧ׼SâgÓ ¸¼žÙ5¯x—Vñ‡‰®­ÖòâäÚE©ø“\Õu?°Ú˜tû9/$‚ÂÖÖÑ"‚0¦€?(¿à…ÿò‡ÿø'oýš÷ßý ’€?Wh•ñׂ¼1ñ+Á>1øsãm-uÏxÿ¾!ðW‹´W»¾±]cÃ*Òo4-Kkí.êËS³]CJ¿»´k½:öÒúÜLf´º‚á#•þÑßµì‰ñ3öhý™<áO„>2øùmÂßü`ñ_öµ×ü+OƒÞ&Kˆ>)kžÐ4H–ÿÄþ4Ö|*—ž ðæ’5 Ámsâ™a?û3oÙ‡ÿT—‚(ìš( € ( € ( € ( € ( € ( Ê+þS“ãÏûEÂoýkïTú½@P@P@~QÿÁG?äãàŽ¿ö’-CÿXWöÑ ÕÊ( € ( €?(¿à…ÿò‡ÿø'oýš÷ßý ’€?Wh € ( €?&ÿ`Gmø+¿ìý)ÂÛþÔßÿky¿%Äþýª?f†šmôñEÀ6|UøñRÊ“w™umzd0˜ãýd € ù«öÏÿ“<ý«ÿìÚ¾:ÿê®ñMqðNùGÏì'ÿfmû0ÿê’ðE}“@P@P@P@P@P@ùC¥Êr|yÿh¡øMÿ­}ñª€?W¨ € ( € ( ˜ÿjOØãörý´|)á~Ò¦øƒáïøÞˆþ ‚ÏÆß¼¨øwÆÖÞñ„á×ôï|5ñ_ƒüAÊøsÅž"ÒZÕÊ[mVs-³Ê°ÉÇ¿ðäoø&¯ýˆ_ø–?¶ÿ?Ê?áÈßðM_ú"?¿ñ,l?þ”Ñ¿àš¿ôD~!âXþØüÿ(ÿ‡#Á5èˆüBÿıý°ÿùþPÿFÿ‚jÿÑø…ÿ‰cûaÿóü þÿÕÿ¢#ñ ÿÇöÃÿçù@ü9þ «ÿDGâþ%í‡ÿÏò€ør7üWþˆÄ/üKÛÿŸå~‡üø3ðÏöwøQàß¼-oà… ü7§øCÀ~´¿ÕõKoxsKC†™£¯ê®³yº«>¥©^Ý¿YgsÍz}P@Pä‡Ç™ìïÿdý¾ñ>»nÊñÎèÀÍû#þÑþý¯?f‚?´·‚!–ËAøÇð÷Añƒh—EŽ£áMzæßìž/ð>´®‘<^ ð‹­5Ïø†Ý£V¶×4=BÜŒÇ@EP@5~ÙÿògŸµý›WÇ_ýUÞ) /þ Ñÿ(ùý„ÿìÍ¿fýR^ ²h € ( € ( € ( € ( € ( €? ?h_w²·üÓPøÛã/€µ¿Ä…Þ2ÿ‚q|:øW¥xÇötý”þ:þÑZ-¯´oÚkâ÷‹u ë—ÿ|â»] T·ðö¡§êiªMmqöMBÆo+Ë»äöïø{çÁOú5?ø*?þ*÷öÛÿç7@ü=óà§ýŸüÿ{ûmÿó› þùðSþOþ ÿнý¶ÿùÍÐÿ|ø)ÿF§ÿGÿÅ^þÛüæèÿ‡¾|ÿ£Sÿ‚£ÿâ¯m¿þstÃß> Ñ©ÿÁQÿñW¿¶ßÿ9º?áïŸ?èÔÿà¨ÿø«ßÛoÿœÝð÷Ï‚ŸôjðTüUïí·ÿÎn€ø{çÁOú5?ø*?þ*÷öÛÿç7@ü=óà§ýŸüÿ{ûmÿó› þùðSþOþ ÿнý¶ÿùÍÐÿ|ø)ÿF§ÿGÿÅ^þÛüæèÿ‡¾|ÿ£Sÿ‚£ÿâ¯m¿þstÃß> Ñ©ÿÁQÿñW¿¶ßÿ9º?áïŸ?èÔÿà¨ÿø«ßÛoÿœÝð÷Ï‚ŸôjðTüUïí·ÿÎn€ø{çÁOú5?ø*?þ*÷öÛÿç7@ü=óà§ýŸüÿ{ûmÿó› þùðSþOþ ÿнý¶ÿùÍÐÿ|ø)ÿF§ÿGÿÅ^þÛüæèÿ‡¾|ÿ£Sÿ‚£ÿâ¯m¿þstÃß> Ñ©ÿÁQÿñW¿¶ßÿ9º?áïŸ?èÔÿà¨ÿø«ßÛoÿœÝð÷Ï‚ŸôjðTüUïí·ÿÎn€ø{çÁOú5?ø*?þ*÷öÛÿç7@ü=óà§ýŸüÿ{ûmÿó› þùðSþOþ ÿнý¶ÿùÍÐÿ|ø)ÿF§ÿGÿÅ^þÛüæèóöMÿ‚›|(ýÿj¿Ú“ö{»ýœÿà¡V?¿hÿkµïì™áI¿àŸ_µ§t¯xÓȸý±þè_ ®¾Yx²ëÁø§w£|eÓu¿ xPð¶†ßuM VÔ´ÛËm2ÞüôûþùðSþOþ ÿнý¶ÿùÍÐÿ|ø)ÿF§ÿGÿÅ^þÛüæèÅi?ø*GÃ?‰?³¯Çχ~ý’¿à¨7ž)ñ÷Á_Š~ ðÕ¥ÇüöÔ²·º×üSàmwCÑíç½¹ø<–öϨß[E-Õä0#4²º¢1¢ß°W‡¼AáØcö/ðŸ‹4=cÃ)ðÇìû:x{ľñ™{¢øƒÃÞ Ñ~ø;MÖt=sFÔ ¶ÔtcIÔm®l5=2þÚÞöÂöÞ{[¨"ž)#P¬h € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ùWLý¯¾_üY“á=։㽠i~$ø·àþ‹ãÍoHÑ­þx“âo¾êÿ|Sá-+Rµñæ½os¥xÞ$ñš®½á½ÃÓE ßÚ[k_ɧÚßwV´ßìݪØxcTÓ?h‚z–™ãoÍàê6üyaâÏÛßišdþðÝå¾»%¾·âHµ-oEÓŸDÓd¹Ô…þµ¤Yý›í¥„wüûA|øƒñKÇ¿< ãß øÃÇ¿ ô}#Wøƒ¥xg\ÑõïøCäÖ|A⯠[h~#m*þî]İk ×íï´RMBÏ쟾ˆ?˜‘€{-~PÁ]ü1­øGà'‚no‡šmÍïůø'Ä­7ö®Ò ÓP.©â‚z6Ÿ{áÚÏáŸÚ 7dz†»ãû…µH¦ñW†¼#w"oÓâ– ÔøƒFñg‡ô/xsQ¶Ö<=âmLñƒ«Y¹{MSFÖl Ôt½FÕÈRö×¶70\ÀäÑJ¤œPÅP@P@P@P@P@P@P@P@P@P@P@P@PÂ^%ÿ‚s~Ìž+ñ‡ˆ~ ê~¹ƒÇž/ø‡ñGÇž-ñÆ“gá #Ç&Óþ/üñ‡ÀOü3Ö|m§øN]|/ÿ„OÆëzW‡·öŸãOøGÄZ”höNæWÿðK„Z‡†|1á–ø½ñ¾Îøêÿâ-õΔ> iqøÃÅo/ÁáíkÅþ²ø1„uoÁv?³ÿ4_ x’ÓÃÚ‰4Ý÷ÅúZêím¯ÆºpÒß¿e | ø…ã߈:Ž~"x’Oi á½;Ã,›ÀòøsÀ~âWÄßŠë¡øVOøÞ'¾ˆx·â׋öޤ ÆÿÛ‡þ ;û \ü8‹â>&§â‡Þ!øGoªÃûFO¯Ið›J.ô×øk¦éZUÝí¥Ö­®{×õ¯ø)LñüJÐ<á]rÂßüG“Ã^<’_Ù¾ïNñŸ‡¼EûdxkVð?t‹ßkÇâßþÅsx—ðŸŠ>ðŽ©|A··mnoêé¿|Ãx—ßðSoøwLð}ηãÙWÆ?±ÏŽ|%âmwLÿ†høkuáz—Ãßká¯j:惬x×\¾ø›7‰®>è¶ðò|%𮸲øó¾*×tñ'„t`Õï[êV¾ð„:ÔZ¼Zàð΄uÕñ ÐÄM­¶—juidesc,ddscmþmmod(cprt¸-XYZ \¸4 ÄXYZ vµ¨'XYZ #ÿb¢GXYZ óRÏsf32 BÞÿÿó&’ý‘ÿÿû¢ÿÿý£ÜÀlcurvÍcurvÍcurvÍvcgtXAÕ r ö Š)Ç^ù…³Pât™!3"Á$J%Ü'm(þ*Š,"-j.,/Ö253 595ù7:8»:9;Ò>?l@$ApBûEGF©H InJÕL9MŽNáP7QŒRÞT/U…VÓXYjZ¬[ñ]1^j_`•a¦b´cÂdÎeÙfãgìhõiýklmn'o.p4q7r8s9t;u;v9w6x3y1z.{)|"}~ø€ë܂΃½„¬…™†…‡rˆ\‰DŠ+‹‹õŒÛÁŽ©“q‘Q’2““ö”וº–›—w˜R™.š šê›Åœœlž<Ÿ ŸÝ ¯¡†¢U£#£ð¤½¥‹¦Z§(§ö¨Ã©ŽªW««ç¬²­z®@¯¯Ì°•±Z²²â³§´iµ*µì¶­·n¸/¸ð¹°ºp»0»ñ¼±½n¾+¾é¿¦ÀdÁ$ÁåÂ¥ÃbÄÄÜÅ›ÆYÇÇÒÈÉPÊÊÒËŽÌLÍ ÍÑÎÏJÐ ÐÊщÒIÓ ÓÈÔŠÕNÖ֨יØ\Ù ÙäÚ§ÛjÜ/ÜôݼބßKààÛá£âlã7ääÌ噿fç4èèÒé êpë@ììæíºîŽïað6ñ ñàò¸ó‘ôjõCööö÷Ðø©ùƒú]û9üüóýÉþ‘ÿMÿÿXAÕ r  ’(«úz‹¯?ÁE´þ!l"ô$v%ñ'k(à*W+Ê-8.b/Ã152 45o6Ñ8;9™:Þ< =\?b@ A\B³DETF¢GÖHøJ,KrLºNOPP’Q×ST`UŸVÝXYAZZ[h\t]…^“_¡`¯a¾bÌcÙdæeòfþhijk"l)m/n4o9p=q?r?s:t7u3v2w/x)y z{||ó}å~ÖÇ€µŸ‚‹ƒz„e…K†2‡‡ýˆá‰ÅŠ©‹ŠŒhIŽ) êÊ‘©’…“]”6••ë–Á—’˜Z™"™êš³›~œHØžŸe - õ¡½¢…£L¤¤Õ¥˜¦\§!§ç¨®©mª/ªò«·¬w­9­û®½¯}°>°ÿ±¿²ƒ³D´´Æµˆ¶I· ·Ë¸¹MººÎ»’¼V½½Ü¾ž¿aÀ$ÀèÁ©ÂjÃ-ÃñÄ·Å{Æ?ÇÇÆÈ“É]Ê'Êð˼̆ÍQÎÎçϵЃÑRÒ ÒðÓÁÔ’ÕbÖ2××ÙØ©Ù|ÚQÛ'ÛýÜÒݨހßXà1á áéâÂãžä{åYæ6ççòèÔé¶ê™ëì}í‰î„ïtðsñƒò‚ótôtõ…ö„÷vøwù‡úˆû|ü~ýŠþxÿEÿÿ‰ Éo $ ³ Sþ}î{ƒ‚õ] °"#_$¿&'m(¸*+T,Œ-·.ç01V2‰3¹4ç6798R9f;J;ß<§=¾>Ò?ç@üB CDEFGH'I0J9K?L:MCN@ONPMQER,SSòTÕU¶V˜WwXTY5ZZñ[Ñ\³]”^w_Y`6aaíbÊc©d†e]f:ggñhÍi¨j„kcl7m mãnºo’pnq@rrçs»tucv3wwÒx¢yrzC{{ä|³}~Mꀹ‰‚Sƒƒæ„¯…y†G‡‡Ýˆ¨‰tŠ=‹‹ÒŒŸhŽ0Ž÷¿‡‘S’’瓱”y•?––Ì—•˜^™$™êš±›|œD ÓžžŸc * ñ¡º¢‚£K¤¤ß¥§¦r§=¨¨Ó© ªj«4¬¬Í­®l¯:°°Õ±¦²u³B´´ßµ¯¶ƒ·V¸(¸ý¹ÙºÆ»®¼˜½…¾u¿eÀUÁFÂ9Ã,Ä!ÅÆÇ ÈÉÉÿÊûËúÌûÍýÏÐ ÑÒÓ'Ô3ÕBÖT×gØ|Ù“ÚªÛÄÜæÞß!àBáeâŒãµäáæç@è†éÀêðì/í~îÀïùñ<òóÑõ öQ÷¤øêú*ûtüÌþÿÿÿndin6•T¼SF‡e'ž¨P T9s3^¸áG '2?N]n‘¥ºÐç5Pl‰¨Èé .RwœÃì@k—Äò"Q´S¡Ã+Nrì,k¬î2h’¾  š è 5 w ¥ Ô ' { Ð & | Ó , Š êJ« q×>¤zçVÉ<±)¤0¿Qæ~´Rô—<ãŠ3Þ‹<ï §!a""Ú#š$_%%%í&¶'‚(R)&)ü*Ö+³,“-v.[/D001 2344þ5ÿ78 9:;$<<=T>l?†@£AÁBãD EN?j@ˆA©BÍC÷E*F\GŽHÈJKhLÂNOzPØR>S¬UV…WöYhZá\`]å_k`ðbvde¡g3hÑjolm¶o^q r³tcvwÌy…{A|ý~À€†‚F„ …чž‰k‹9Žè¿’’”l–L˜-™ù›Ò­Ÿ…¡c£A¥"¦ÿ¨Ýª¼¬®{°]²B´(¶·ó¹Ø»»½¡¿ŠÁsÃZÅAÇ(É ÊöÌÞÎÅЮҚԅÖlØQÚ:ÜÝ´ßRá&âú䛿:èéäëŠí.ï ðåòô9ö÷ôù¤û`ý€ÿÿ !+6CRaq‚–¬ÄÛó )Ea¡Âæ 1Y…³âAr¥ÛM‡ÃI‘Ø iµS¥úQ­  M  :   u á P à < Â@¼;¹<ÁIÙcö²\ ¼q(ä¤c(î´}Gè À!š"t#Q$7%&&é'Õ(Ä)±*­+¨,¥-¥.¢/¬0¸1Æ2Õ3è56!7A8d9‡:®;Ù= >>?r@©AàC#DjE´GHHI—JêL=M™NóPLQ®STUîWVXÄZ5[­]*^ª`(a©c2d½fAgÏi`jíl‡n o¹qVrñtv2wÓyw{|Á~o€ȃp…†ÏˆƒŠ4‹ê¦b‘’Ò”Œ–H—ù™ˆ› œ¹žKŸÜ¡o£¤•¦(§»©KªÛ¬j­ø¯†±²¦´5µÁ·O¸Øº_»æ½n¾öÀ}ÂÄÅÆ…ÈÉ‚ËÌ~ÍðÏmÐèÒaÓÙÕOÖÂØ4Ù£Û܇ÝÝß4àŸâãwäÇæçèïêUë§ìýîeï×ñ>ò•óëõUöÄø-ù†úÙüFý÷ÿÿdesc Color LCDmluc itITÄfrFRBØnbNOesES,fiFI>ptPTNzhTWfjaJPtnlNL‚deDE˜koKR ¨enUS´svSEÆdaDKÖzhCN òLCD coloriÉcran à cristaux liquides couleurFarge-LCDLCD colorVäri-LCDLCD colorido_i‚rm²fv˜oy:Vh0«0é0ü LCDKleuren-LCDFarb-LCDÎì·ì LCDColor LCDFärg-LCDLCD-farveskærm_i‚r LCDmmodœ¶l>€textCopyright Apple Computer, Inc., 2005ÿÛC      ÿÛC  ÿÀøc"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠóÿŽŸµÂÏÙû/þ_â_Ãÿ‡Ûžoöoü$þ!³Ò?´<­žo‘ö™ÍÙæÅ»nvù‰œnô +À?áì_²Çý·Àü8zGÿ$Ñÿbý–?èå¾áÃÒ?ù&€=þŠðø{ì±ÿG-ðÿ‘ÿÉ4ÃØ¿eú9o€øpôþI ¢¼þÅû,ÑË|ÿǤòMðö/ÙcþŽ[àþ=#ÿ’hßè¯ÿ‡±~Ëôrß?ðáéü“Gü=‹öXÿ£–øÿ‡Hÿäš÷ú+À?áì_²Çý·Àü8zGÿ$Ñÿbý–?èå¾áÃÒ?ù&€=þŠðø{ì±ÿG-ðÿ‘ÿÉ4ÃØ¿eú9o€øpôþI ¢¼þÅû,ÑË|ÿǤòMðö/ÙcþŽ[àþ=#ÿ’hßè¯ÿ‡±~Ëôrß?ðáéü“Gü=‹öXÿ£–øÿ‡Hÿäš÷ú+À?áì_²Çý·Àü8zGÿ$Ñÿbý–?èå¾áÃÒ?ù&€=þŠðø{ì±ÿG-ðÿ‘ÿÉ4ÃØ¿eú9o€øpôþI ¢¼þÅû,ÑË|ÿǤòMðö/ÙcþŽ[àþ=#ÿ’hßè¯ÿ‡±~Ëôrß?ðáéü“Gü=‹öXÿ£–øÿ‡Hÿäš÷ú+À?áì_²Çý·Àü8zGÿ$Ñÿbý–?èå¾áÃÒ?ù&€=þŠðø{ì±ÿG-ðÿ‘ÿÉ4ÃØ¿eú9o€øpôþI ¢¼þÅû,ÑË|ÿǤòMðö/ÙcþŽ[àþ=#ÿ’hßè¯ÿ‡±~Ëôrß?ðáéü“Gü=‹öXÿ£–øÿ‡Hÿäš÷ú+À?áì_²Çý·Àü8zGÿ$Ñÿbý–?èå¾áÃÒ?ù&€=þŠðø{ì±ÿG-ðÿ‘ÿÉ4ÃØ¿eú9o€øpôþI ¢¼þÅû,ÑË|ÿǤòMðö/ÙcþŽ[àþ=#ÿ’hßè¯ÿ‡±~Ëôrß?ðáéü“Gü=‹öXÿ£–øÿ‡Hÿäš÷ú+À?áì_²Çý·Àü8zGÿ$Ñÿbý–?èå¾áÃÒ?ù&€=þŠðø{ì±ÿG-ðÿ‘ÿÉ4ÃØ¿eú9o€øpôþI ¢³ü'âÍ+ǾÓ5ßêz~µ¢kV‘_éú……Â\ÚßÛÊâšP”’7FVWRC$РŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¿8?à¬? ü+ñÇþ 9ÿøð·Æ¿ xÆÕ?ác}·HÖôèu ¿/Cµ–?6Þehßl‘Æãp8dR9×èý|ÿÿ”ëÿÁ:¿î¥ê=o@@çeú6Ÿ€øo4þF£þ;û,Ñ´üÿÃy¤ò5{ýàðéßÙcþ§àþÍ#ÿ‘¨ÿ‡NþËôm??ðÞiü^ÿExü:wöXÿ£iøÿ†óHÿäj?áÓ¿²ÇýOÀü7šGÿ#W¿Ñ@ÿý–?èÚ~á¼Ò?ùøtïì±ÿFÓðÿ æ‘ÿÈÕïôP€çeú6Ÿ€øo4þF£þ;û,Ñ´üÿÃy¤ò5{ýàðéßÙcþ§àþÍ#ÿ‘¨ÿ‡NþËôm??ðÞiü^ÿExü:wöXÿ£iøÿ†óHÿäj?áÓ¿²ÇýOÀü7šGÿ#W¿Ñ@ÿý–?èÚ~á¼Ò?ù“þ=û,Ñ´üÿÃy¤ò5}A8=©7ep?&õÛ£þ  |Eºðž­á€x‚ËR}"k_øQÓ6Ë¥”ÄÑïAC‡n ·¾qÍ}È¿ðIÿÙaÔýš~`Œø·šGÿ#WæÿðZ/Ù£öÑÿ‚’Ë­þÝ_áðÁ_;á¿€fðö«}ý»¬£˜›_ÔZÚÖHrŒJ[BÎL|»?{‡ÿ‚Ï\xþÜÿ´KþÜçâbüR] H?²ói#Wÿkû6d:q±ý×Ú>ß³Ì3ûcµ4çz4fÕÜîôê’”WZŽüÜœÚE«µ$Ñr‡ïêÓNÊ6Zôm´ÜŸòGg;n“›ýuÿ‡NþËôm??ðÞiü^}ûG~Ȱïì•á /^ý þ|ðþ“­k6ž³Ÿþ}…ß}tû ‡m½“²în70 ?ˆŠø×þ ÖúWö¯ì˜¿ðZïøJá@ÿÂt|ý5/ìßøK¾Ëmäh7÷øßçyXã~ïáß^5ñãDø‰âïø7Cá™ã [Ä:$úÆ}:ÓÀº–°’ɪéú#ês¦—4ë1Ì‘•dŒÆ± Zò>gëj‘†Sª©µ}”š÷ Ó…¤ßC4à¦ô¼µéû·4í»ŒmË6¬Ô½ÔºŸ¤~ý˜aŠ´7Šþü:øû?k~8ð-¼W> ³²ø]c5¾Ž%ÿWÅêÙ}™'l 2ù¸ìÀ&½7þ;û,Ñ´üÿÃy¤ò5|Ãÿr°¾ý‡'ø§û'üG¶ÓÆ/ Myã-'Ä’+«|R°¼vhõižGg{ˆæ"Þu.vmL2kã_ø!ÃÝ Zý¾ü/¯øƒã·¢~Ò¶/ªIñcÁr|;ñ@ÔuÇu™LZΫq©Í¥¤q7—$Eo8B– S¥V¤`Ó‚•û½ohïh´ã>°væWvN¤8NmY©ZÏ¢Ò×–×’i䯣²æ­ðéßÙcþ§àþÍ#ÿ‘¨ÿ‡NþËôm??ðÞiü_‘? ¾Øø÷þ %ªÞ~Ñ5o†¿µ ‡Åyî4Í2?‡~)Õõ-cÃqݲÙÃ}m©ÿeǤÍk…g’ÑLc{; >Iÿ8»øi?í‰ûoZøáþ ÂîOøqþ ®…ý¬"ÛZ@$1?ôµyj¸ûGͰ+æÍcJ~Ò§oŽ<ÖZ½é«G¤¥ûÍ®šqiHòT;é(ÝèµçÕö¹~mSŒ®¶hýÑÿ‡NþËôm??ðÞiüGü:wöXÿ£iøÿ†óHÿäjüÂÿ‚èh­âŽôïø)Ç‹#ð/ÂcðáL:Å÷…µÿéQøÌº ðèZœ©v±h^C,xóFÃ’GyûSþËúOÆßÁ7~ü~ñwŠ~'øs[²ñªëWvº‡…õ?Y.¯Ú¡yEå±’4$WHÀ°có[B›ž‰¯ÃMVŽjí­¾ ¤õq’’¾¦jkFî—³s}þ+%×GföRM;uý?ðIÿÙaA-û5~Ïà§þæ‘ÿÈÕòïˆÿðKO_´›ü"øIàßÙoYñøíbÓãømc“'X­¯$±[k—ë…ŠW'àãô6ëC´Ñü&›ce<ö¶&Ú;H_2IÇ´FŒì>b£h,Þ§½~þËß´®Ÿð£ã¯Á߃߲Œ5ߌv…ã«dO€¿~ ñ'ÂèšíÌú”zìhb†[e”ȲùŽ2œœ° ‚UqQ¡ÑÙ.ú»]ïd´¾kïJ VêÞžU¾Ò¿¦‰»t½ú]Åÿ*“Ñ~Ãçeú6Ÿ€øo4þF£þ;û,Ñ´üÿÃy¤ò5~HÿÁN¼ ¥üLÿ‚½xþËöÉøµ«üñšÞhéðKUƒÀ(ñ-âZ xšGÐÛGÔ¡·YÏš³%Å´Ä–# úçųW†¿hŸø8Ù"øÞ—úÐð?Áí Å1Ã{q§Á.©k­Mä\Í 2/˜#gwX¤,€žU°)a—Ö#FONw+õ²Œß“’JÒƒ·,´o¨b°uRוEö»”ãÿn·+©+Ý&íµþµÿ‡NþËôm??ðÞiü\‡¿coØ‹Å_´/ˆ¾h?>Oãß évºÖ«¥ÿ°°_²Ú\³,ùíd!}Æ7QÙ†9Šøóã/ÀÍ]à­šÏìÅw7Ãß>?Ñþ<ÞJCd±±·êVÎsƒæêV:wËŽ“ý1ë·2x¢ø,í†ßA>3_Ú1Ðp¡Ûñ{ö|àþ÷gŠÅÔå¥ ­n¦ì·÷)9;?*ŠTöב½.­£‡ï'I=W&¯o~¢Š¿orQŸ—2ZÙßê/øtïì±ÿFÓðÿ æ‘ÿÈÔçeú6Ÿ€øo4þF¯Íoø ¾ŸðbçâÞ{û)ËñþoÚßÀ'âñÔÚé|1/ˆKñkƒQÍð¹-ä‹L E}Ç;óÀÁ ~èZ×í÷á}Äõ½ö•±}RO‹ “áߊ£®;¬ÊbÖu[Nm-#‰¼¹ ’+xÄj±ºãJõ•+ý–îµNÎJñë({·rV²”]µ0•KR•KlÒ³Ñê“´ºFZÙEï$Õôlýhÿ‡NþËôm??ðÞiüGü:wöXÿ£iøÿ†óHÿäj÷ú+CÀ?áÓ¿²ÇýOÀü7šGÿ#Qÿý–?èÚ~á¼Ò?ù½þŠðøtïì±ÿFÓðÿ æ‘ÿÈÔçeú6Ÿ€øo4þF¯¢€<þ;û,Ñ´üÿÃy¤ò5ðéßÙcþ§àþÍ#ÿ‘«ßè ÿ‡NþËôm??ðÞiü^ÿ6ÿ‚e~;ÿ‚mþÐzïgÏú.·¢ü5ñþŸ¨XxK¶º°¸‹K¹x¦†T·ˆê¬®¤ A÷}xü‹þQeûKÙ*ñGþš.¨ÿ‚NÿÊ,¿fŸû%^ÿÓE­{ýxüwþQeû4ÿÙ*ð¿þš-kß袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯€?à¡ÿòø'WýÔ¯ýG­ëïúøþ ÿ)×ÿ‚uÝJÿÔzÞ€>ÿ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯øûøWöqý ~0|HðF¡â­sã^¥eªk_O ÚZËkn`ŒZ*DŽŠP僼„ž„+Øh¡{­Éok|›NßzOä\ÉEí{üÒjÿsxQEQEQEQEQEQEQEQEQEW€ÁX¿å_´·ý’¯é¢ê½þ¼þ Åÿ(²ý¥¿ì•x£ÿMTÁ'å_³Oý’¯ ÿé¢Ö½þ¼þ ;ÿ(²ýšì•x_ÿMµïôQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÀðPÿùN¿ü«þêWþ£Öõ÷ý|ÿÿ”ëÿÁ:¿î¥ê=o@×ûBükÒ¿f¿€^8øã«}BïDð‡ïüI¨A`‰%ÔÖövÒ\J«º#HR& ÔFXGa^ÿbÿ”Y~ÒßöJ¼Qÿ¦‹ªù¿Âðq•ãß éšïcïÛ¿ZÑ5«H¯ôýBÃáJ\ÚßÛÊâšRø¤‘º2²º’A Ö‡ü?Óþ¬¯öÿÿÃCÿÝ•ôüwþQeû4ÿÙ*ð¿þš-kßèàø§ýY_íÿÿ†‡ÿ»(ÿ‡úÕ•þßÿøhû²¾ÿ¢€>ÿ‡úÕ•þßÿøhû²ø§ýY_íÿÿ†‡ÿ»+ïú(àø§ýY_íÿÿ†‡ÿ»(ÿ‡úÕ•þßÿøhû²¾ÿ¢€>ÿ‡úÕ•þßÿøhû²¾Ÿý‚¿mo ÿÁD¿dï |cø)aâ /Ã>0ûgØ­u¸!‚þ/³^Og'šË,c2[HFÙ*Tœìðüåÿ((øÿqÿýHu:ûþŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š+œø·ñÂß~jÞ.øÓ¯é^𷹿Ôõ+…··¶AÝRpŽX$@sÿ~,x[à׆¥Ö¾/ø—ÃþÑáÏ™¬j0ØÛGŸšY™TqÏ&¾ ±ý²hoø)üÆø&Ö‹ÁŸƒ—<ÂÜñ¾ÓjzÔD02h»w.B‘qyµ1Âdb»…ðC?~ñ\~-ý¤¬uïÚâ?{âOŠƒx‚PrØ,äÒÝ•RT7f€âÏø/÷ì{á=a´ÿø^×oWŸ'Ú}ÿˆŽyÙöód rG#=Fsü'û-« nüSãÛ}:Lˆ5>ø”ZܰÆUiʼnõQ÷O¶~Áðw€ô?‡z4zwÃýIдø†×O´ŽÖÉ :úVµ|‘ðçþ Ãû üPÔþÅ¢|zðF—{¿Ëk¼þ–7ÎÝ®šŒp•lö úŸÂÞ.Ò¼u¡[êž ÔôýcL»]ð]ØÜ¥Ä/ªH„«pk/âWÁÏ|gÐßLøÃá_ ø³M•J½¦³¦CƒAŽee#“Û½|ñþkðûÂzÅ߉ÿà¾(ñoì½ã©š.ür°¯$ öÝ blî#|ª‘“óq@nQ_i?ðS/‰¿°/ˆl|/ÿ‘𭆛áË©£³Ó~2øJ%ð¥ìŽvÆš­¹ÌºLÌvŒ°hY˜íeQšû¯Ãþ °ñf…gªxZúÏSÓ5æÒîÒeš ¨C$‘Ȥ««)0$Ar¼þ Åÿ(²ý¥¿ì•x£ÿMUïõàðV/ùE—í-ÿd«Åúhº þ ;ÿ(²ýšì•x_ÿMµïõàðIßùE—ìÓÿd«Âÿúhµ¯ Š( Š( Š( Š( Š( Š( Š( Š( ¾xÿ‚žx÷Uø/û1GñÃ:¾©¤ÁðÓÄš?‰u±Ý< w¤Ã{j1N,_c–áÌmLjq+èzãh|>ðgÁývëö§Ôü¥x{se¬Í⻋h4y!˜ˆ¼«¦º"’ µømÁpsŠŠŽQJPv’i«ítÓW]V…Á)>Y&ÓºvÞÍYÛÎÛ>›Ÿ˜¿°‡í=ñoâ×íேÿ|eâÉæø«âÈ~9éåõ ˆÍ—ƒ®,¯¤JƒdÛEyo§£C…nv”Á û…?mOŠ´…:Ç‹›ÁÖw/øÓâiK¦CªÚÅac§Zë°A%äQê*—ò´ºj»Ç(òq¶5‘RTú›àŠ~üv¹·ñ§ì³¨ü*ñ”Þ°ÿ„b w·‹iv€¤ŸÙéujXÃV'òùTíàW[cðCÁz]¾—™á ÛÅ¡êSë:jE¥@‹§ßOçîà?u<Ÿi¹ß*á›Ï“$ïlëZ)GÙÁ4ºwJñQwÞþÎ1Mõ|Ï©œfåÍ7nfšò»Œù´íí&åoåJ=ÍŸ ÁRÿi ;öZø<|5¥x_âŽõχW_¼I®Þizn—§ËmÏ”ºÝëÚtVAuäfácÊ7Ù0ا^×.\èÞÔ$Õ´{ |/böºUäŽd’æÚ#Øfg%ÚD‹IÍzhNi©i«mú&ÛIy$ÔmýÞmäÐM^wŽ‹·Ýú¦ÿíî]¢˜W=ñ_⿇>|8Ö<_ñwX²ðÿ†|?n×zŽ£vû ³„uw=€È®†¢½±‡Rµx5b¸‚Qµã‘£B³òwü?ö;ÿ£‡øoÿƒÿÄÑÿãýŽÿèáþÿàÀÿñ5ôçü+oÐEÿÀ¿øš?á[xsþ€/þÅÿÄÐÏÚwüöJÕ¶ý£¾ ¨¸ SÎñM¤$né¸;yÝŒwÅz€?o¯?ßoÂß_ |JÛÌXÒ¼_§ÞàW÷S78e8÷µÑj_³Ãme.Xø{à{µ» 'hV² ƒgvüÇóg'9ëšó¯ÿÁ*¿f_‰êÇÇ_³ïÁ­BVAžÞ°K€£8Q2D¸ð©Í{ÕµÌw¶ÑÍg"K ª2º‘AGz}~[~Ô¿²Ãïø%í=û6êßðOýG^øG?Äωözˆt!âkàÝOG1M>£ö‹+¹®<¨ñ‹gï\`dæ¿Rƒ¨d ‚2èhkàø(ü§_þ Õÿu+ÿQëzûþ¾ÿ‚‡ÿÊuÿà_÷R¿õ· ¿ëÀ?à¬_ò‹/Ú[þÉWŠ?ôÑu_<ÿÁC¿àºÁ'ÿlÂ?¶€µ~øîËí¾ñ§‡[ÏšÚHÂ%ͭ프ohÝ–C,OŸ.xÀ‰˜1®Ëö´ý·~þÜðGÚWÄŸ²¼;ã}-~øŸÏþϹæÉŽtB\Û>%·|vÈŠyé@©ÿÿ”Y~Í?öJ¼/ÿ¦‹ZôÿÚþHŽ?ì_¿ÿÒy+Ì?à“¿ò‹/Ù§þÉW…ÿôÑk]·ÆÛ/àÿìßâ+-#öˆø­ðÛÀZ¶§Ú,ì¼Gâk-.âî-Å7ÇĨλ]ÀGZÃGë4¥FörM}éôêm‡©ì*F­¯ÊÓû™ùyð'ÁÞ&ý‘#øã/…> ü;×5/„íöŒ|©µÅÿÄNÚæÚ×X€i–$–Û-ÀUk¶óT€ëË?cð³öÿøÝ'ìÙã¿øãG€¼E¡ÃªøVÊïÅÚÌ “áì7׆=Vâ{…ðþ›§ ·€£ýžhîe´lµÓ2õ^Òò-BÒ+‹ cž ÐI‘°d‘HÈe#‚ ‚*Jí­UÔ­*hÚÓ·¾æ×MÓåvåÑhqѦ©Ñ…4õKu×ÝŒSÖûrÝ_›w{£òçá×üľð/†|Qãï¶Þ+øoÆøFóÆ—Zf›¾«£Á \O§+Ëmm;¤¼[}’©ç»*¦QÂW¿ðRoÚ/Æ>ø#6™ñÀþ½ñ'ÿ x†Ö_ÝGbßõ[Ù]o!‡N·ðýôÚ‡–km>[bóÄŒÅyªÿ ¾øsá ¾"‡zwö{x¯YŸÄ©ûD³}ªúeE’oÞ3lÜ"Aµ6¨Çd禨§5G%v½ÿíÈ8ÍyóÉó7e{+Æûi4䦖—ç·•ê9EùZ>í¯èìÎcöÄñî±û}x×á¯ü.æþÒ“[Òì¼3á«6{¯ [ä´Ö÷—×6pê6R,ÈYo™õ ÚXcDS&bú;þ 'ã‹ß‰_ðM‚:ï‰üg©xÿVÔü%c6£­ê‘^]Ü]˜‡ŸÓDªH¥ß Ì™ˆù¬ÒocôM4Ÿ³¤àõmA_ü<÷ö÷>ºßÝZ¾Ž¢çš’Ñ''oñrþ\¾š½WÀðk—ü £àgýÇÿõ!Ôëïúüwÿƒy¿à­_³gìÉÿ|øCàß|áOèŸÛ?oÒµ Ãů›­ßÏõÇ¢–7Ì) ýˆ¢¾fð'üoöOø‘©ÇeáOÚá]M Š8®¼ImdÒ¹8 ž{&âIœž+é Z³ñ•÷‡îí¯ì®I ż«,S)èÊêHaî Y¢¾øýðïUø{ûlþÔ>2Ѭþ4Ø_x“áF€¾Õ|1«}÷ö÷:‚46h7Xý¡$m<ù´„šwÀŽ[—n;@ð÷ǯ ø£~зß´o_üWÕ-þ+/‡.õÊÆÑt‹æ†K»]šIa`#eæWÉuŸ!H^kMôôÖ\º>¶ÞZitº¡Ôµ6ﲿáMTüoËuiì~˜Q^ÿÚ>4OاÁ0~Ðoâi|Yb—vWRx‰e¤ÑÃ{XÕ YŠÇœž•ð‡ü¿á™ÿ©¶Ñ¿hïۯƞø•i¥_3øoáN4­á¯‡·(~î©m2£ßjj¤nkˆÄk¸˜Õ£¡¿à¼ò‡oÚþÄû¯æµÊükÿ‚XÜkúö•ñ»þ çâÈþ |z—Kµ…üVÆoøÞ5OÙµÛÂÏž‚åGœ„îùʨoGä*¨ˆª¨ÀP;N¯‹þÿÁ^¬4Ÿ‰6 फ़ÙïãÎèì“U¹ám`¾v«œE rT‹yJL¥ÂaØ_g«PÈAdÐÐÑEQEWÕõ‹OéwÚõÕ½••¤fYî.$Xâ…Égv (¹4‰ü/¦xÛ÷ºGŒôë_IÔ¡kkË+Ût¸·º‰†9#pUÔ‚AR"¿(?lÿYÿÁ¶¾"Ó;ø›á¾³½ñÄ1'ÛåCi'ú-„+òiöƒ´àâØ] ¤^ñ}¯Äèúþ†³¥–¹c¡n³&ÉV9cY:‚pØa‘“ƒ^+ÿbÿ”Y~ÒßöJ¼Qÿ¦‹ªôÙŸþM¿á÷ý‹Zwþ’Ç^oÿbÿ”Y~ÒßöJ¼Qÿ¦‹ª?à“¿ò‹/Ù§þÉW…ÿôÑk^ÿ^ÿÿ”Y~Í?öJ¼/ÿ¦‹Z÷ú(¯”?à´_´ÄÙöÕ|Yû?êSørH5­.ÓÄ>$¶Ò×Tºðž‰5ÚE}ª[Ù¶Vy ÷m`T.ç8 ‘Âø+þ³ð£ã7ƒ´ÝâÏÆ?Úãe¶»iì:¦«ñKR[;øeEex"Óä‚…Õ²ª‹¯Ôõ ºh¯Š¿â_Ù+þ‰ïŠ¿ðäø£ÿ–tÿòþÉ›O“àDÿÂëñ'ÅúŒê}EzWüöÑñü×ö\¸ø½á/Çã­ÂZ…»ø«KŠv‚ù4¹[Ê{›GÁS$RI ²:í1ù„²c4ÿø'ïüGàüËÀ£Wý•¼cg©jð¬Ú—‡ï¶ÖtŒñ‹‹V;¶îù|Ôߺæ¿>à´ðKÏþÏß²”þýм?ñûÆÿ¾'»è‰aãýR…­™@¼¹¿Š[§‹ìqÆêÍÂ%„1ØZ¾rÿ‚]Á¢¼+ã­âíeñV÷àþ¡¥J·vºw¯¼ßÛ°ìÚ‚þâÕºóÚ8ý§ÿ‚þÒÚ·ì—û/ÜøÇÁz·ƒü?vºî‹¤É«øªÝçÑôˆ/uKk9®îQ.m‰H¢¸y9ž1òrÀf¼á?üGð‡…¾1kß~ ü(ø™à†'DŽÓÆßÖ=;IÕ¯5&’1¥âïT¹·Kˆ -3^¬J·*d1s_VüQýœô¿Œ_ ô/ xóW×î ÐuMX[ñ4BöòãL¼·¼…§o+co–Õ<ͨ¹ ÛJâþ/Á>üñÆÞ6ñ±«x£KÕ|m†^m6{h[I½Ñî&¸°Ôm @Çí(ó ùÆX™bE1•ÜBñŒ¯»—ÍFÐÚú_IÛmZæ|º+>[t_{»íÒÖ¾ú_•sYžG¢ÁsþøÇáχµï‡:Œ|Uyâ WSÒHÑî´K»›y´èá–ëmÊêb½";«vHì®ne—Ì"4r޾ñWüëÂþø½yáSÀ¿ÿ²ôOEÒ5ÿeÛG¤è·:º@Ö1ܤ—Kx ½Ì16Ëgò‡›±Hb¿ÿà›–ß~Úx_ÆŸþ5]“%ójÚ…Æ¥¦Þ?ˆ#»ÙæÅuesa%„h¾Zy_fµ ùŒ-w-ÈøSþ OmÇ¿jÞ&ñ—Š-¾GÃwšOƒtýB'Óõ1¢ØÚÅjÚ§j×㸵I‚åVP‘ùÁñ´\læ¹¶¼oÛ§5ºÚß[ß›D¯“ºŽ¥ë{ûžW·ÅÓEm[¶¥¿üßÀ-¡ëºÞ§á/ˆÚ†,tsÄz·qceö?Zèà›ïìÀ·m*º”[¸í¼Õù²‚Ã6ßþ 5à4ð&¯«ø›ÀŸ|=©ií µž‡«Ã¥Y^ëëfA¦ÜC3ê"Òå0̧íWìWUb¡´-ÿà_×I×tOÅŸµ^ø\ðÖƒ¢Ü_Y?ZkûpÓ Ú K°8F»{Ÿ-FÄ ¤©èuÏø&§†¯'Õ.¼#ã‰Õ5Oèkí*úÌöþï¯Å.o.n^Eü©¹Y= %o³¿½éö9|íñ·×E©ÃþПð[Ï„?³‚|«|_±ñ6{ã .ã[]R¸ÒtOM²‚_*Y¤PÔ K¯Ÿ"8ìžêIÔoeBýmáOØøÛÂúnµá™ÅΛ«ÚÅ{i0R¾lR tlʰ8#<×ÍZüƒÂ>ð§‡4ÿ„>ø¥àkýÆûK¾Ö4BÆÚÿÄ—·¯}s É6m®e–E{(íd„ȉx¯§ì­ÂÎ!iY!EL’4Ž@‰faôE-% ,£/üJ_¡Q—-ýûÕÌßÿ ?hŸø·ö¿´¾}GÇ#³ðBŸx3Á¶Ÿg.ƒÝÚ_Ãce ÷7ÐD÷,ëm¨}¥£ž!”â:¡¥h<1ð?áÜ?øóªÁPn/|iâÿ‰úfŸkâ¹^ÓHÓü!­ÜørïÃKcû¡6§&°šoüÀY>¢³ã¬_0ýðo‚ô‡^±Ðþi:f…¢é‘,ôý:Õ-mm#(£QG¢€+Nº'4çÌ»Îßöüùÿ cçãÖë9C™IwµþPPû¬”¿Æ”¯ÐüwñGÃ/Šd޾ñNûKxÃÏðÝ¥ýÕγ‰4Û›VY$x-´ÉÚbÓ˜D„î]=Ò% ,‚¿]ü#öøE4Ïìí²}’/#íþÚ¶l|ï´~ûÌÆ7y¿>s»æÍhÑR¥h(ÿ]%eÑV×r¤¹ªJ§Ãm½Zm÷núlQEHŠ( #öý›<ûV|:ŸÂ_´ƒü?ão ÜH³6Ÿ«Ù¥ÌK"ä,‰¸f9NHa“ƒÍ|¿oÿýþÍÞR¿ôï‰ÓvøâðÏÄ~Æw$’ÑEöÖHø;Bª…¯µè ‹¿áÆ è¨~ÓŸøwµ¿þ?_~ÚÿðJox þ ïûø+KñçÇ[½;Çßðý¶òûâ6©s©X}E†hþÃvò™-7³“Ë#ÌP²~ÎWÀðPÿùN¿ü«þêWþ£Öôùñÿÿ‚%k´Oí)à?‚ðM |@×î´‹í¯xÃÆ>5Õ5Fk’Ú–öi'Ø’Ï"CÌRXJ® Õ|?ÿÎü3ÿ‚eÁ6h?ˆ?-þÝ^'_º÷‡<áýwá]¶…©jþ ð=ψ­n‹ßßn‚Ù“P´†9Ñ%VO<é˜ñ÷½þ ;ÿ(²ýšì•x_ÿMµïõ§´ä®•ôïÍEþ.5%ÉGFùuíË(Ëó‰ùÇñ+övø»ðBÿâÆû7êß.ôχ<%á¯5Áixéqy©5”%¾Í6«ö;h6¸Dgˆ o\òz”ß ¤Ù)ûW§ìÏÿ ;#Å ã÷ÓÆ†ûÂîÿŠlN­ådýüÁäWêE­Iʬ¥)»¹~|Üíý÷·k½ïc8F4ÒŒU’V^^↞—¿ü9ù[ñ׿´ˆ¾ ü>¶ðw‹¿hÏ xz/ hcÂoyá=O_×îu#u/Û·eÒ5‹-* •‹ì{ÿµÒkvŒ¶ÌJ% ¯â“ñ’×ö‡øÃká ïÚfæëS²ñJ6£o¤ê‘Zèyñ.m>Æîwѵ¸AlÚeÕ•Ón&ᇯӺ*eïsyûOüžß•¾kEmoIÙ§Û“ÿ$òì÷¶ºë®‡å§„/¾&?Â] ߯–Ÿ´ïü)ûOˆŒç#z†_¼(­í5>ßîÝ;¾ï^eÙ£)Sæ‡"}¿ ©þ6å}× Wç_üà]Vÿ‚ü¹Õ4}*ææc¯%–Ò7wÆ¿©–#'ô¿E+àø5ËþPQð3þãÿúêu™¡öÄOÙOáÅÝ2[/Šÿ üâkI£òžW@´¼ÓŸ”¬±‘™¸÷>µðÇÇÿÙ‹Mÿ‚6ürø]ñCö–óŸ üuã½/Á^?øt—rË ÜǪL-¡Õ4ûi­¥Ô3´E–-«"0ÁÿIká?ÛïX?µÿüƒö{ýž¼‹½?áþ±Æoˆ!;tëm8•Ò-_Œ¹¾‘\ÆH;-÷a—"€>좊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠóoÚÓö´ð7ìIð3Vø‰ûCêͤøsI1ÄLPµÅÍäò0H­íá@ZY¤rQG©8é5ã>:ý¿¾ü)ý©ô߃|Moá/xƒN‹TСÖÙmhŸ´ïíÅûPÚ.­û:| øUðWÃ7;d²Ÿâî¹yy¬ÝBH;äÒ´¥ÙÈÈò¥œÔŸá?‘ßðu#|hº‡àÏ‚ÿlïüñ¿ÄI线Ѵ¿x_P´ÔtûiU"”<³ÝKæE4ÑÆ¨žXgkv ®Òö“þ Çÿ(vý¡ÿìOºþk_N|6ÿ’u Ø6ßÿE-9Ÿ¼ÿ ðü·ã}—í}Z|OÝ,¾ Ë!ñ=¬x1§¨ÍÄJ 1Þaªñ_ÑWƒ/ÿ²¾iW[<ϳiË·8ݶqžÝ**N4¢ç-–¬¨Åͨ­ÙSãwÀOþÒŸo|'û@xWAñ—†õ‰ôí^Ê;¨ö`®×ÈuÃ)äkäx?à‘¾;ý™_wüöŽñ÷Âý.bð_Šà_x^"¶ŽñÅÕšúe?1zÙÓþ ?㯉ŸÁÿ ¼#á_|~Óä½ðåÞ…ã™õËë-ºsj j“iV‹ ~J2´‘K0Y )pÕÞ/ü/à`ð÷ˆ5kÏ_Ùi¾³‹R¸¸¼ðÖ«j—¶RÜ¥¬w–[Uþѵiå>ÑiçE™îÃv©JNß]:é{߶̔﷗㷭ދÌò”ñGü'áàÙ¬øKöUø—oùgÓ5Íg÷S¨#!áž ˆÑØ ÈTIOÿ ûsÿѱüÿó'ÿ++Ðu/ø)ƒuGáìmï5è|eãIü©&¥k{ ßøfæ-*ïR&êÂòÕnUŠZÆod¤ë"– ü[á‰ðãáþµñWÅzm×´ ODšFŸ«jÖ66w$Iuqr,#k;FšEn/bµRܤ‹–‰uKæ×2·{¤ÚòA/u]íkü¹œuíï&µêyÇ™ÿ ø›"Âbý”¾ØJ6Ër$Ö¼O©EÀù£‹m´dýænGLrkZÁ øñ©ÚêðT/Œÿ¿hÙ­¤YLJ¯<;àä•[z8Ñl ¬…Hó¥”9'>é«ÁEþé_üCáD×5›ÝÃI|. ´ðÞ§4wVßi¹²³»[o³^^G.ÖJóSƒŒ-þ §ð‡PýŸüñ_ºñމ£|BÑ?á"±´¸ðާu¨iö ̺¿‚ÎÞcimuÝw!Ä2²ÊÈÊÆcï.e¶Ÿù75­Þü²·£Vvë¯áký×_yîþð‡ð¿Â‡þèÚ_‡´&oe§i¶©kigè‘E ‹ÉàÖ¾{ÿ‚ÐÊ%hÿû'šÏþ’IIñ£þ ¥ðëáÇÆ? x À’ÜxÃÄž$ñV‘á«“gixºn›ý ‚TvÔÖÙìÞqÇ0´ó–VÃà/ÍKÿ ÿ”JþÑÿöO5Ÿý$’šMÇŸ¥Úù¤›ü$µÛ[n™.IK—­“ù6Ò|_ç³G±þÌÿòmÿ¿ìZÓ¿ô–:óø+ü¢Ëö–ÿ²Uâý4]WÏÿ¼uû|迼™ðïöX×4ã¡Xµ•Ä>0Ö­`û:Y’KýæÐ m;yÀÎ+ˆÿ‚ŒüHý·o¿àžßàø±ð·öwÓü-7ÿ&³u¦øÛS¸½¶²:mÀžKxžÁVIV=åQ™C0i úßþ ;ÿ(²ýšì•x_ÿMµïõù“ÿæø‘ûnØÿÁ=¾AðŸáoìï¨xZ‡~MëRñ¶§o{sd4Ûq—%ƒ,r´{ "³b@$ ׯÜx—þ %¯«6‹á/ØëÈÿ"GÄ^!Ôd„÷“l6q¬ƒžzŒnÍ}¬höž"Ò.´ÿZÛ_X_Bö÷6×,°ÜDêUÑÑ ¬¤‚¤A ×À<=ªÁ$¿à ø)àÛ«Oösý $ÔÛÁš]Äæiþk¶Ðµìö0ùÎq•£O›Ê•—êî?gÿÛÓâ„&/~Ѿ¤ ‰Á ®5I£ÇîÛT½eÌŒcò{Ùcþ _ᯀß¡ø­ñ›Ç?>6|^·´šÊÏÅ2ÔoìX&M™c ¥½’8mˆ\‚Ã~Ö €}GEPEPEPEPEPEPEPEPEPEPEPEPEPEP_ÁCÿå:ÿðN¯û©_ú[×ßõðü?þS¯ÿêÿº•ÿ¨õ½}ÿ^ÿbÿ”Y~ÒßöJ¼Qÿ¦‹ª÷úçþ,ü-о8ü,ñ7‚¾)XÿjxgÆUÖ‰«ÙyÒAö»;˜^âó"e‘7G#®äeaœ‚ yüwþQeû4ÿÙ*ð¿þš-kßëàø…Ëöÿ¢ÿ—ŸˆùaGüBåû ÿÑ ÿËÏÄ?ü° ¿è¯€?â/ØOþˆgþ^~!ÿå…ñ —ì'ÿD3ÿ/?ÿò€>ÿ¢¾ÿˆ\¿a?ú!Ÿùyø‡ÿ–Ä._°ŸýÏü¼üCÿË ûþŠøþ!rý„ÿè†åçâþXQÿ¹~ÂôC?òóñÿ,(ïúøþ rÿ”| ÿ¸ÿþ¤:ñ —ì'ÿD3ÿ/?ÿò¾µýžgŸ†ÿ°7ìݧøànm࿆þ ‚îê {F{ˆt褚[»‰$¹»•äÛæM4„» 0 ™ût~Ù^ý‚ÿfüIø —7éj–ún•f»ïuíBfòílmc¼²ÈU@àncò©#Ìà•_±ÿ‰¾ü8ñ7ÄOÚ–H¯~:|pÔ‡‰üm2rš[Ûi¤[œœAeXTdå¼Â cÈÿeMïþ åû\éß´§Ä«kȾü0¹š×àΉwFºýèo.ãŰª´VÆUAfË~ƒÐEPEPEPEPEPEPEP_ÁB4‹oˆŸðVŸØoÂ~3óÃ±ÝøËÅ¿Ùò`Ã>§¦é¶‹c3)êЛéä\`†ÁÎîøwþ [tÿ øûCÀ->|I²ŸÄ³•º­é:„¹Ý?i¾ ðç‰>i“hÞ)×´Á§_øúÖ}.K)ÖêX%WVyš+­ÒµÆ,rX¸ò«¿ø"¦£û?~̾*ÐgðÒÜZhº&ƒu |7Ò¼;ª}š×W´¹{Í_P34šÒÇn®ä=´R˜‰ò Œõ¿Œ¿´×Ç›tÏÙÞóáßxâéô=>ÞM_FK[Èa–YÖ{Ø£hcG”4pyÒÈê¾Pušu~ EÙ>Vº+*2”oÒÞÊ÷¾ŸÍ®×SšJP•ݽÛnÿk/J¯Kv÷tI"ÿ‚Fx†_Ûßâž¿ñ{ûb YÕ­ìõ æçPkmJÎ{W²’ñõ# Çn“¢Ûù°ª${]%fÞ¸~(ÿ‚[x·áÂ]'Äþ"ø_â]ែÓÀj^*øWkâK:'SÍ•íã­– Š˜óÙî!rÌZÜ®Ô_jø¹ÿ<ð7Áω>ÐõýÆ·z?‹ãÒe·ñ<¶°éjN-ì—1\ܳHч\DˆeòÁÍEáÿø*W€5¿ˆÿ´k½Æv‰ðÑu_íŸOoi.“ i€Ä‘`¹’îЯͱ®íàIö“ H “){ÛeïÓÙ§{ÿ*¨Ó¾üÝn‚î´¹Ö®VÛ¯;RV¶Žî)«/DŽ3Tÿ‚Xkv¿¬ï¾|MÓto†ñxÿJøqá‡ð‚Ky&£em±†øî¢Ž YÚ6ò–Ô”ºÛ?w]oüƒþQ+ûGÿÙ<Öô’J·ûÁQ¾ÿÁCµoéߤ¹‹Rð”6—wK©iZ’½µ×™äL³éW·p©c Ã$‰×ÿRý³õoØûör‚‚6që_þ&jpø;áæ’Ë¼]ëYTšQÚ tßq#.ØÂ’»®Ÿþ ßûi°'ìÁ¤xF¼“\×®&—YñWˆ'ɺñ6µr|ËËùØüÌÒIÂî$¬iäíÍ{…|-¦øÃ:v‹àë]/HÒm£³²³¶ŒG ¬1¨DŽ4*ª€«ôQ@_ûnükÕf¿Ø¿â÷ÄoÛé÷z߀<¬ø“O‚ýKY®,ìf¸‰&TtvŒ¼J+© œ0<ˆ?g¯ðSÚSàþ#xßö´Ñ<áûið_§Šãº†ÞòÚ;ˆ’eGtYJ¡‚»AÃÉúÃþ Åÿ(²ý¥¿ì•x£ÿMTÁ'å_³Oý’¯ ÿé¢Ö€>ÿ¦ÿÕ€åÝGüm7þ¬ÿ.êûþŠøþ6›ÿVÿ—uñ´ßú°ü»«ïú(àøÚoýXþ]ÔÆÓêÀ?òè €?ãi¿õ`ùwQÿMÿ«ÿ˺¾ÿ¢€>ÿ¦ÿÕ€åÝGüm7þ¬ÿ.êûþŠøƒþ eûk|}øãûXþÒ?à VíüOð/þ*ëáìŠX]ÿkYÜÞÏ+HûcŽØ GÌ8Úkíúøþ áÿ)×ÿ‚ŠÿÝ5ÿÔzâ¾ÿ ¼ÿö¬ýžôÚËöiñçÃ?€4¯hWz,òmÜÖþtL‹*ïFÅ]Of@kÐ( •?à‹´N¯ûCÿÁ=ü¿¤ÿ‹ðòKŸøÆ&pÒC«i2›9Œ„7H±Å?Aþ¸q_U×Ãÿ²<'ötÿ‚ÎþÒŸ Añ_@Ѿ/èÖØÀŠã-¤ê¬§ø‹Íodí×o˜3÷…}Á@$ÿÁxÿåß´?ý‰÷_ÍkéφßòN´ûÛÿ襯˜ÿà¼ò‡oÚþÄû¯æµôçÃoù'ZýƒmÿôRЄ|jÿ‚léß~)xïÄQ|Tø±á=?â}ž™â½AŸI†ÃY¶¶‰áXÌóiÒÞÛïŽYÚÞæ&!Î œÌ|lÿ‚4|2øÏñXx® {ƾ™WBÛc¢ÿfxäÑdY4çŽK»®#Xö0$ËòÍ9ß_\QD?vÔ££M5ê¶û¾Ïgµ‚^òiìÓ_'kýöWïe{Ÿ+üOÿ‚Jxâþ©s¨x¿Ç–3_xâoˆ‚%Òn¢ÒuìÀ‚½>hÞ9"b~³I|мF±­?àŒþ Ñ~øWÃø›ñ‹CÒ¼/áCÁŒ,µ5dÕ´[Û¯´Íetï§±Ç Í•:¢äK¼™Ø4RŠQJ)h¬¾J<‰zr^6ÚÍ®¬m¶Üºïóæç¿¯?½~ö}òGÆÿø#_ÃOá-_xÛÂ×t",ôs¦]U“O‘%»±žâ1À 2Àü³DÎw×Eâø%Ç‚<ñ‹YñgÅïxóƶڭž­a‡­^ZËe¥A©ÄÑ]ÅÊZ¥óDc’EŽ®¥Šø‰jíúVŠoÞN/Tù¿òd”¾m$¯Ù+ >åœt·-­¥¹~v·ù÷<Ëöpýšåýžm5Ô~!üGøƒ-ävöÐËâ­BÞeÓ­ VX¡·‚ÒÞWŽéš6žS·Í–M«·Ì¿à´ò‰_Ú?þÉæ³ÿ¤’WÓUó/üƒþQ+ûGÿÙ<Öô’J©IÍÞO_ëúó&1PVŠÓúþ’è´G±þÌÿòmÿ¿ìZÓ¿ô–:óø+ü¢Ëö–ÿ²Uâý4]W¤~Ìÿòmÿ¿ìZÓ¿ô–:óø+ü¢Ëö–ÿ²Uâý4]T”ðIßùE—ìÓÿd«Âÿúhµ¯¯ÿ‚NÿÊ,¿fŸû%^ÿÓE­{ýQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEðü?þS¯ÿêÿº•ÿ¨õ½}ÿ_ÁCÿå:ÿðN¯û©_ú[ÐßôQEQEQEQEQE|/à ?kßø.|Kâ.¼=û(x^ÇÃ^€hS^× 7w÷ˆç¬v"ÖÜöb:ƒº+óÃÇ¢ÿ‚IÿÁL¾'øÏö‘·¼°øûI¾“¨Eã(í¤šÃÁ¾!´´K¶Õ$PM¼7IÍòܼ(bŸ{øâñOÃúßÃsGñx7A¥ÞÇyk8õIbfVê:Ø¢ŠøGþ #ÿÁÑÿà•µáÛ#À áŽmVxãAqz±L˜K«kÛ7ÊÑ1I7DòŽtÄ{•²íÿðV/ùE—í-ÿd«Åúhº£þ ;ÿ(²ýšì•x_ÿMµä_µíéðsöæÿ‚CþÒÚÇìñÃ6¶O„þ'yà±»öÈ"ëâÑöÍö‘×®ÿÁ'å_³Oý’¯ ÿé¢Ö€=þŠ( Î?ø-/ÇoŒ1üVðîû[|D½½øC¢Ÿ‰Þ'Âz¥¥”s¬w*¶vZšÏw\YO®­¾„ò1Ž""lVÏÇø)/|ñÇ~1ø]®xgÄ -¾ øÆžÑn´“[ÍSRžÖ;ËA&ßä"*É*ìÇ–£nÆVy>í¶ð…e¯êÚµž‹¤ÃªkñE§x–q­Æ£A–$ž@»¥T8PÄ…Àc&¹›ÙcᆖÖGLøqà;c¦èoá‹C‡íÚé.rú|xäµb0 FqÊÒ‚ä+×Y?^hÍ4ý?wËÕr¹nÑsjNëM"»ÚÒ„¿T¿F¦–Ë_“3xŸÂ%‡ãU¿Ã‹»=OÀÿ¯tÿøG¬uM"æÖóé§´²Oý¢òywLï*¤-¶à(Èß8û»Á²çÃ/†þ°ð¿Ã¿‡^Ð<3¥ê)«Ùivikagz’ ê+xã¤Êê®$P0Õˆÿg/‡°ÃåÅà?,gÔ-6Ø/“¨J%¿>åÌ€I2ô•†çÜy¥R<Ôç­I_Ö’Œtéi©OçÖÈ)IBp“Z'§)K_4༹ZÙ»ü™ðçöíø‘ãOŠZnám_àÏ„¼'á ?Á‘kpøÎòõu/Í­[¬Œº}ó]&Ec…gŠéî¦ †HÎ^¾æ®Pý˜>jþ=ðïŠõ_‡žºñG„-–ÏAÖ%Ðm^ÿDA ¥ÁÌ·@€±²€ ⻚è«R3rio)?“Ù_ï~W²Ñô¡*qŠnöŒWÍ-]¼ÿ¢Š+Sàø'‡ü§_þ +ÿt×ÿQëŠûþ¾ÿ‚xÊuÿࢿ÷Mõ¸¯¿è¢±ÝYl´{Ã&(VG.ÿòÒGbK`;}Q@íAû:xwö¸ýž¼aðÏâȽ>ñ¶™.•~læòn97Fø;YNÈ##FE|]á/Ûâ—ü7QÓ¼ÿA’çÇ?<Ø´ï |kÓ,tØÏÉ ·Š-cÉ·qò ½ŒÜ”ÞaúU5Ý ÇÅ-Þ›âk;MGNÔ!{{«[¨Vh.bpU£‘u`H*Ag†Ï­éÉ$M“ÉóR- Œã4÷5âÿ à£ß³÷ÆË(çøOñ·áN¼²Ë;OÙ<ÉÆpñy»Ðàg  ãšìá¦~ÿÑAðGþmøåvôW€ü]ÿ‚«~Í? •¾+|xøQ¥MbÖ¿ð’ÚOwò’-¢v•°A)äc­x•÷üïÅßµM§ÿÁ)>xç≺\AãoÙËá?@1*Ü]ªÜÞœ˜á„ÃsöƯŽýœ~ê¾2øïâ=#žÑbó¯u-Já`‚ì2~ó€¨2ÌH@¯‚u§x–Î×QÓï¢h.mnbY¡¸†ÊA ‚0kä_ÿÁ?eý{ÅwZÿ€|}ðÓ_»!¤¿ðˆu 1 ’§É±ž82îÇ~§?bQ@¯üÃÖ¹Möý²ôÛPIŽÖÛã&¦°À ÎÔ“Ž{’kóþ ÉÿŒñ7Ä?ü+ø%ûjÿ´/Ç?ˆþ º“Äߎÿƒ]|ÿóÿ‚lüzø•ûHë’|Bø»¥|,ñ-՗ؤ–×DðüãHºæÝY.œtó&z‘º¿Và“¿ò‹/Ù§þÉW…ÿôÑkGü‹þQeûKÙ*ñGþš.¨ÿ‚NÿÊ,¿fŸû%^ÿÓE­{ý~?Á^|#à/~Ù¿ÓâwŒÿg? x•>hŸðŽÇã¿ Cªxªòãv°Txféµ%³ºÞ"á‚é¼Ö€„%B·ì ‹•švk¯mºéתkÈÒäé}¿ŸéÒÇç‡?kŒþÓÿhMoÂZÞ¹eßøN]#ÃÚE¥ìšÆ¡n$¿¿ºuƒíSÛF’?–_Ë%$Ê®Þ;Æ_ðQßøGàŒ%ð‡ÇMGÅþƒâ‘á¯|\ŠÇÃÇym=ŠÝ_G.£$QhÐ,2¤-á¶”‘ òe—æ¯Ô +Jžr–ɵò÷”š»ïf»ÙÙ·¢aBÊœ`õi5ûw•=;oÚú«7Ì~>|Bÿ‚“xÓã7ìUàé>2üb‡á~‘âOxÚ;ýutÝ2Y|U­é÷¦ÊÏE¸Û4QÏ5±yžhí癳äùAJGýžÿkhÿ>øÅŸ›=3ž‡Â^O Á¬?ĈnìPßÈPÂ׌¨Ê`ó­%Š;C–à:q_§”USš§)JÛ¸·ÿnºÓþ^Y&œRŠ÷[mŠpsI_e$¿íîOŸØ»³M¹?y$‘ùEð;þ µñÇ´Ÿ´M?âTw¶š…V¡k+B–Ò\Mk"0YFáŠÈC§éÔ°$å ÈŽcm˸giõ‡“K n…mÑQI-…'$þ$“Y¨5IBú¨¨§æ¨ºw·_zÕz^)-\¤íI)¹[G'&¼N{|£x&µ³mÞÑQâ¿fˆwßg‡þ,ñ2š—‰ü7§j×k íežÖ9\(컜àzWoEÓZq©RSŒl›m.Þ_#ž„%Jœa)]¤“}üþaMž¹…ã¹E’9«£ «Á¢EdjxÄïø&ìéñ†õï~+ü øA®Þó^^øFÂKŒqç·ãÛ8¯Ã?Ø£öŸý‘ÿh?ø/§Œþê¿~ ?Á?…ð‡¤>²òSS³gòîÕör/¤k”^íºÉxÚk÷þ EðÏâ—ÇÙ Å>ýïôÅþ:ˆh/®êW >±œ»»Q2<¢$qª ù²ÆÄª«0ü1ðŸü½e§þÛ~/øGð£ãN½¦|Aø}àÇZ/‰äÓR;7Ô&½¼ŠDkxßÍ…CZ@ÑȲ³Fw$È÷›á'üÿàOÀK¥¸ø%ðgág„î×é:O…lm. KAØû’k×kýŸî¼iwðSÃöŒ´Ò¬üyŸ:úi“ùÖ2Þ Ù,¶ÌUO“#)‘*²«¨`; (¢Š+À?à¬_ò‹/Ú[þÉWŠ?ôÑu^ÿ^ÿbÿ”Y~ÒßöJ¼Qÿ¦‹ª?à“¿ò‹/Ù§þÉW…ÿôÑk^ÿ^ÿÿ”Y~Í?öJ¼/ÿ¦‹Z÷ú(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+àø(ü§_þ Õÿu+ÿQëzûþ¾ÿ‚‡ÿÊuÿà_÷R¿õ· ¿è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šðø+ü¢Ëö–ÿ²Uâý4]Qÿÿ”Y~Í?öJ¼/ÿ¦‹Z¿ÿ6ðž«ãßø&ßí¡xLÔ5­oZøkâ; ?O°·{›«û‰t»”Šb@^IÙUQA,HkâØ‹þ ªþͱ‡>:ýŒÿnû½oÀ Ѽ7¨Oað‘䵚âÎÆy^{¤vŒ¼LT²) Œ¨<Ôú+àø§ýY_íÿÿ†‡ÿ»(ÿ‡úÕ•þßÿøhû²€>ÿ¢¾ÿ‡úÕ•þßÿøhû²ø§ýY_íÿÿ†‡ÿ»(ïú+àø§ýY_íÿÿ†‡ÿ»(ÿ‡úÕ•þßÿøhû²€>ÿ¢¾ÿ‡úÕ•þßÿøhû²ø§ýY_íÿÿ†‡ÿ»(ïú+àø§ýY_íÿÿ†‡ÿ»(ÿ‡úÕ•þßÿøhû²€ø'‡ü§_þ +ÿt×ÿQëŠûþ¿8?àþ%ñWÇø)?í­ñÅŸ ~0|'ðÏÄÏøA¿°m~!x^m þïì]ÝÎÔrѾÙ#ùr>XËm-Šý Š( Š( Š( Š( ¾'ñꆟðpoÃÝV÷lvÿþ jÞ‡qιҵ{{à¹e†þrGrvâ¾Ø¯ˆà°Ê>üKý–¾;D„Eð·â•¶¬\Ù4z 4«™ þè–k"Gž öõQ@Q@xü‹þQeûKÙ*ñGþš.«ßëÀ?à¬_ò‹/Ú[þÉWŠ?ôÑu@üwþQeû4ÿÙ*ð¿þš-kßëÀ?à“¿ò‹/Ù§þÉW…ÿôÑk^ÿ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|ÿÿ”ëÿÁ:¿î¥ê=o_×ÀðPÿùN¿ü«þêWþ£Öô÷ýQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@x¿ü[ö[öÖý†~)ü-"?¶xÃÃ×6ºkɰj ¾mœ§$«¹Žê>çQÔ{Eà?ðKŸÚÿlߨá—õ6={PÒÇÄM‘-¶­hÍk}©åH¹‚lçiSÞ½ú¾ý‹œþÅ_ðUŸŽ?µ<ÛxOã?ü »"%¸•’ß\³Œ“·p¸XnDk· 36&¾ï Š( ¼þ Åÿ(²ý¥¿ì•x£ÿMUïõàðV/ùE—í-ÿd«Åúhº þ ;ÿ(²ýšì•x_ÿMµïõàðIßùE—ìÓÿd«Âÿúhµ¯ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾ÿ‚‡ÿÊuÿà_÷R¿õ·¯¿ëäø)_üSǶÇo‚??fßð¤|mðGûwû7Rÿ„:ßÄ¿iþÕ··¶—÷73Çí† WæI3çdm( úþŠøþáûvÒEóx{ÿÑÿðý»?é"¿ù<=ÿÇèïú+àøw‡íÙÿIÿÌ áïþ?Gü;Ãöìÿ¤Šÿæð÷ÿ ¿è¯€?áÞ·gý$Wÿ0'‡¿øýðïÛ³þ’+ÿ˜Ãßü~€>ÿ¢¾ÿ‡x~ÝŸô‘_üÀžÿãôü?nÏúH¯þ`OñúûþŠøþáûvÒEóx{ÿÑÿðý»?é"¿ù<=ÿÇèïú+àøw‡íÙÿIÿÌ áïþ?Gü;Ãöìÿ¤Šÿæð÷ÿ ¿è¯€?áÞ·gý$Wÿ0'‡¿øýðïÛ³þ’+ÿ˜Ãßü~€>ÿ¢¾ÿ‡x~ÝŸô‘_üÀžÿãôü?nÏúH¯þ`OñúûþŠøþáûvÒEóx{ÿÑÿðý»?é"¿ù<=ÿÇèïú+àøw‡íÙÿIÿÌ áïþ?Gü;Ãöìÿ¤Šÿæð÷ÿ ¿è¯€?áÞ·gý$Wÿ0'‡¿øýðïÛ³þ’+ÿ˜Ãßü~€>ÿ¢¾ÿ‡x~ÝŸô‘_üÀžÿãôü?nÏúH¯þ`OñúûþŠøþáûvÒEóx{ÿÑÿðý»?é"¿ù<=ÿÇèïú+àøw‡íÙÿIÿÌ áïþ?Gü;Ãöìÿ¤Šÿæð÷ÿ ¿è¯€?áÞ·gý$Wÿ0'‡¿øýðïÛ³þ’+ÿ˜Ãßü~€>ÿ¢¾ÿ‡x~ÝŸô‘_üÀžÿãôü?nÏúH¯þ`OñúûþŠøþáûvÒEóx{ÿÑÿðý»?é"¿ù<=ÿÇèïú+àøw‡íÙÿIÿÌ áïþ?Gü;Ãöìÿ¤Šÿæð÷ÿ ¿è¯€?áÞ·gý$Wÿ0'‡¿øýðïÛ³þ’+ÿ˜Ãßü~€:¯ø-Ã-kÃÿ¼ûD|³–ëÇß³¶ý¶§øsgðfÊÏWð­´ß ´i|E¡_´öËwº˜ËEtf·hYäXÈUFÙ´Û+àøw‡íÙÿIÿÌ áïþ?Gü;Ãöìÿ¤Šÿæð÷ÿ ¿ëÀ?à¬_ò‹/Ú[þÉWŠ?ôÑu_?ÿü?nÏúH¯þ`Oñúçþ,ÿÁ'ÿlïŽ? Åg·Ÿäï+•»föÛ»›>@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|ÓñóöËøà>3ðÀ¿‡ñ5‡ÃÿØø·YÔ¼Eã˹ŠêKô[Å“vŽTi²1y%‰~uX}-_5üwÿ‚[ü/ý¨~>øËÇ?´o…|ã§ñ„¬/6Ú‡…ÓQ{ë›Ý&úÏK?Ùø7ëo©MÚ]=¶|K#Eƒ¼.>|´ÿ‚'iíñÁ¾%ñn½ðßÅZ¶—£øLñ©â?…Ö:Þ±y&‘Ä“é7—³Ëý—ö„DªétxÝFÿ=fë¿ðCíCÇ¿u½{â¯ÅùüC¤k¶Þ)ÒîbŸD¹}^âÇ\†HÚ9/åÔ¤ˆIkº„Ãm B(¼³ $H½Ž^yº[~òß+{=ûë{Ù÷å9ðéòÁUzþï›æŸ´ù§ktõéï_²oü[Âß¶_ÇŸøK᛬dx/CÑu…Õµ=:ûIžüê-{µ…õ¬3FŠ–qȳ|Ë*Î à ·ÐµóŸìuûø×àÆøïãÇÄ­âµâÏè~EÓ<(t, ÒÍæÇ*×·&G—í¬Ìr 2 )TO£*ª¨'joOëÓú︠äÛrVÛÿIWÿɯÿhðßü2Å¿fŸÚ·öoý¥´ ÐEáß§Ão²¢ãA×dH#’bz¥µø´”úhÇž}É_ÿÁÃ?ÒuÿØ+Rø+áÛÛk¿Š¿u]'Ãþ С›7÷7ÚvÓ}±c\ÈÛfœ ¨c\°$VEŸyQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÃ?·ü[ýÿj/ø7þ/ìíð‹JðÇ‚tÿØÃñ KšêÿÄ÷7j)$ŒšÕ– ß3Žʧîjó»ÿٗú§Å?x·R—Pž÷Çþ²ð®§k#DÖ‚ÒÕïY hcÎöþÒœ6æe!S 0weYMź½líå½·MyQpWUü¿úToÿ’ßk>Í3xãþ ©ð»àðÚßöšµ¸ð狼eáM#ÅÖ•­¥£øf;ð+AyyÕé AŽÊ ™UcÜÈ¡—w£hðT_x“ö„Ñ< ¢ø7â,ö%ñ.©á/ÅâÎÈx~÷SÓm®'½\Ýý¤þÉ<[ÚÜ+Ȥ!p®ÊïÿÁ2Lñ¯íûû|8Õ&Ñ> þÙ¿²Ÿ5›yš ô~Ñ?|1ªC:¿–ÐÍa­øÂÊî9VA±£x•ƒü¤nâ€=+á‡í)ðãrK'ÁŽ>.Ç^tïðÃâg‚ü|þt­áMkVÅ»1È\ñœÐ²­Ú·VÏãúž¿¥NG>ïÿ_×éú€Hõ­Lu-P@P@P@P@P@P@P@P@P@P@P@Ölz€ŸÌúþ¤ÿžhñ×þ åÿ‘øÿ¨øgc?‰í‡ÄßÚǺuÝÏÂo:F«Ÿ¨êöÐË-œž4ñƪ!¼ü=±¾ŠK?íV°¼ÔüCªAq£økM½{ wPÐ@?”þÌ_ð^¿ø8P/ÅoŒßîfïØëÅr›Ï éºíω>üÖ|=pÅ¢ÿ…mðKÃrÍâ‹–Öñ¸m7Æßï¦Ó58e»ƒNø‹rÐOaè߀¿àÌïÙsOÒ ‡âíñ÷źøˆ CÀ^ øwðóHyöÍâ‰÷±Å»I5É\¨9“-•ñÏðgV©áU_þÆ·6½¢xûAßøcHøÅá´7þÒˆ;ZÜCñgán£­á©â`¨·6_ µYI‘¥W€G²@ž¾ ÿÁ]à¬ðE?žýšଞñïÆŸ‚:”¾F‡ã¯ßÚø«Çëá»Iíín<[ðãlwi_ôÝ:)à¼Ö<ãÍ^çÅvq§éWºç€î™¬&þå~ü~øUûI|)ð?Æï‚ž5Ñþ ü0ø‹¢Ã®øSÅ:$Îö·ör;Áqoqo:C{¦jÚ]ôZV»¢jvÖš¾‡¬Ùßi:µ¦£eso¹Å qFì~~ÇßÐó×ñ ±Èä:ž€ ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ù öMý£øïÇw›®'·X¦ø÷¢übð®‹ö4·†oøSH»’º¸¸¸œê×k-{Ù7òÖÞ·‰÷¿Ýv“ùÚÿqëþ3x3övø+ñcã¿Ä;·²ðGÁÿ‡ž/ø‘â©¢Ø×M¢x7B½×¯ml#vAq©^ÅdÖze o6óP¸¶µˆ4“"àÇþÓû x£þ §ÿøýÿ1ýºtñã/ƒ ü{e&™ðçSi/üâψ2A¡àƒ‘[\/•uð»à·‚F‡}â tˆx’}GÁöÚìZ͈|_ÐúÛ[[ÙÛÁgi6¶–°ÅmkkmAomo,PÁ1*Ç 0Æ«QFª‘¢ª"…PÔPɶÏìUðöûýŸ€ ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( žkoz_ì³û.þÐß´†±ooið;à×Äo‰ñé77‘X&¿©x;º¦µ¢øf+¹ÙcŠ÷ÄúÅ¥‡‡ôðNéoõ+hc $Š¥­õÛwèµ…ú‰»'ß§«Ñ~'™ÿÁ;ÿgCöTýˆf?€úüF?øá'‡'ø§pú­î¹&­ñ«ÅñIã¾!›YÔ™¯õIüKñsÄÞ4ñ ÷÷gϺ›S’i³•›jïy^O®²w‹’Ñl’_r·â~pÿÁË^-Ö¼)ÿpý«N‰,ÖóxŠïà·„ïî fY Ñõ¿Ž¿cÕâ,¬¿¹Ô´ø¦Ñ®Uƒ¬–ڔѕËîVoüàøKþÑû6kz4G¨üKñgÇÏx²xUA½ñ§Ç/|=·žv3Ï…üá½=™òÁl’5&4ŒÐô@Pð#ÿÙ[ü#ÿƒàœ?üÇž!Òdßëih6Ϫk^ý§ðÇ‹<-ãmÏÄž ñ.âïj1‰tý{Ãίè·Ñ’ÏTÒ®nìn£ ‚ÝH çšñ¿µ·ìÃû,x~ëÄÿ´gÇß„ß4›kgºSñLJõ=AA‡ ÝÞ®½â+ù•[ìºfƒ¦êZ•ã –¶³?Ë@Ňü'þøÿÔnàžÿðHÏ|Iñ¯Åùnü ⯌6¡¤|Cø¢j)%¶¯á¯…þ¹º§ÃÿêcNÞ0øƒãS¡kcÃÒj0Üé¾ Ò-/õLêŸø$/Á¿ÿÁ|=â~Ýß µÏ‚Ÿ¾8x‡Fûgí§%í—¿e{I_—ÁŸ n¾)èVÒü¾Ñ/oµ#ªX|]Òü9¡x»Ä_kÖtßëd>Ótë‹Ã7zv³¦éúÎe«i­•®¥¥êšeÜÚn¥§ÞÄ—wÖÖ²Kkygwo,sÛ\ÛË$3Ã"K «P} í^};÷ã¯çñ€, 9'·OÇŠ’€ ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( 5ø·ñ—á/ÀOjÿ~6üJð?Â‡Ú {õo|Añ6‘á?Y³+´6Í©ëWvvÒß]˜Ú;>–úþ}¶öV÷‘°æÛ~Ü¿´çí]ÿÿø'GìÓu/€/¾XÿmÛKñwÁYÉÌz׃¦ÓNøÿñê ¡Y¿³¯—GøUà+Ë”ˆ¯Ä mdÀ„?à—¾ñ‰´oŠ·gÅ?þßß4[Øõ} ßã-Ž™¡~Íßõxò±Üü'ý”|6ßð©ô  AÛøÇß¾ «ÛGu'Þ|µ~Ë¥[[ÛEiko ­­¬1ÛÛ[[ÄÛÛÛ‹0A acŠ£UHãŒ*FЍŠ( ÀÛ—þ Òÿ‚vþÙzÖµãËk³ŸÅjiïµ/ü¸Ò¼7¥ëÚ¤ÄÈ×Þ*øu©iz§u ®.[½WQÐô ø—Zºž[OÄ7 ü7×àП‹>Õoá'üM‡J»s‘­|ñ„õT·ÜÂ;{¹¼5ñKY¶ÔÞÝ”Üyv 3agn¯´zOÂø3ûÁ0kPk´gí£ã_ØÉp'Ôü?ð§áž•à}Jçç-.|sã?|EóZç¤ŽÞ ŽXþb²Èî0é‹ö/ÿ‚nþÈ?°O†gðçìÍðkAðMþ©k·‰üuz÷$ø—âô‰£”§‰|w®Iy¯]بþÛ‡í.¬|1§Ý<’é:€r€î«¿ hÞ"ÒuÄ:>›¯h:ÍÖ—«èšÕ®©¤jºmì/o{§êzmôWwö7vòI Õ¥ÔRÛϱ²1ó‚óþ ãñ3öeÔ/¼sÿÄø¿§ü‚âòãW×?cŸ‹‘kž7ýŠ"Ÿz OxWKÖ5ÛÆe[]>f PÁðÓß·í}þƒû~Ï©û0|Ô?v?kÛÂúΗâmWL›ä:ÏÁ_ØçNÔtoˆºëKm4Ÿ‡õÿ޾#øA¡Ü¦Ù[Ãí¹0Hé ?à˜ß<5ã#ãíâ_þÛÿ´ž“'Û4ÏŒÿµö›âëO_Êéq8ø'ðsNÓ´¿ƒôèïͧx"ÃÄVèÁ/üQªÊåÀ?Hh\uç4 a‚3ùçéê<óߡϞÉ_'îöÿ?¯Sí@Réjsòƒ×·<÷Æsõï뚀i œí÷Ïÿ^€.C¦*Ÿº¾½?ŸO×ùó@ÐÚ¬`qÀ’sôç·ÿ_PÀ Aéþ'ùtçŸ~ |)ø÷à-wáwÆÏ‡^ ø©ðçÄÖÿg×<ãÏi¾&ðþ  7“<š~©oq 7Ör7Ú4íJÜC¨i·iå…͵ÔQL š¿ðË_¶oìCÿ/Ø?âcþÑ¿4ïÞKû þÕþ<Õnµ éQr4Ùö¯Ö]ñ‡ƒRÚÚ{ áÿÇX¾"øÞ=ðØø«Áv¥^ £¿fÏø(GÀŸÚ#Å׿õ|aðö Ðlï‹?e_ÚDO‡ôËh–S>µá.æîï@ø­à¢!’âÓÇÿ õÿxJâÉ­î'ÔìåŸì¨÷MP@P@P@P@P@P@P@P@P¾~‡šnñïþváê?:Zùƒö•ý³foÙHÒµ¿|?àÍOIJ‹OxÙ5|Tø‰©<†tŸ‡ |%g®|Cñæ¥5ÎÛamáo êžLΟk{x÷H>3ÿ…¥ÿ)ý±¿sð;ᕇü£àf¡Âüjý¦ü;¤üFý­|G¦IȽø{û/iÚ¤ÞøO<†)"‹Søõâý{^´Šæ ›ÏƒÂTh@º~ÏÿðNÙ¿àW“ã>±gãÚ'ö—–ÿíAûMøšo‹ÿ‡.M¿„µmjÞ ü(ÑPHðÛøgá†<áèmv[ÿfÈ©¸€}ï@uëÍFSÐÿÿ^€#éž??çÿ렪ߠ?®E&Ä÷ü¿û*\(è3ú‰ýs@2x•JÔÿŸë@ €>rý¤ÿdŸÙãöºðŸƒ¿h†:Ží4[Ñ«øCÄï´|<ñ mÛø£á§Ä_]i^7øyâ{Y`‚HußkÚ> |¥†i¦¶2@à ÿcÿÁDÿaŸÃWþ!ÿ‚›þËZo/áj>ðÏíýð¿B‡æ“ûÆ7gÃÿ ÿjû6Ò)¤M;Åíð×âæ£4°ZÃâo_I@>Òý˜ÿlÿÙÓö¼Ñõ«ß‚ž>‹Rñ/ƒîWMø‘ð«Å:^«àO õÐBOáÿŠ? |]i¤xëÁœ"[T}cE‡LÔä‚Y´MGT³ tàRÐ@P@P@P@P@P@P@P@0¿§>ÿçùÐd“Ö€Tu9ü‡óÏò ÜŸå‡øPÇ´§ííû0þÊúž•à;ºñÆ?EŸþÎß ¼?¬|[ý¡þ \HŒö°øSàÿ-uMivÃË_êöG„íùš§ˆ, W™>^ÁNlœùk¥ÿÁ0>j_òÒaàïß·oŒ4Yº„ö|ý›åÔ´ëŽ|Öøíã}öcðíüºúƒöiýÿf/ÙgXÕ|oàÞxŸãO‰â+ãÚ;ãˆu‹¿´G&’0—Rx—â÷Ž®u­•ßÞhWš'„mŽNðõ”*‘(ÚP@P@ë@ (¾ãñÿÐl§üþ¡@ÿШ € ( ‹iÏØ#ö{ý©uÇþ%ÒüIðÏãçƒíÚ‡Ÿ´ÿÀß\ü.ý¡|9Ûk¤üCУóõÿÒ-×|wcâïê1Ïpš…Ôò×ü/oÛÃöÿBý¬|ûoþÎwÊŸµ§ìÏàO±~ÐÒ#åµÚöQКäxºÊÂyµˆŸ³Œº•§éóêWÿ´+¸ýøûB|ý¦>i~|Oðoů‡Ú»4V¾&ð^µm«ZA{#]èú½¼l·úˆ4ã*E«xw]´ÓµÝ"à›]ON´¹V‰@=sžŸËüþ´ÜoÌ8nŽ™íƒÏë@ò:óúóøPÃߟóùШ € ( € ( € ( € ( € ( €"fÉÇaþÆ€"fÇ9ãÔÿOñø·áï‰?²wìÞcøúËö4Õ<9yûNø¢óìl~-ü`¶·Ó|¥•S"·ü3àÿ¨w‚ÛÇV;Öæ0ê+þ ûPÁ ¾,x{WÓàŸþðÿÅMrÙµŸŠø‹©oûYø®à2ÝjçÄŸüDÔu߈¼å.µiþ3ñ߆4«›”·T±yÕ@?s"ºWzãê3Ï_Ǿsì(Ú¶@'¿~ßþ¿ÓÐó@£v'ŽßçühZ( € ( € ( € ( €›^Ô $òOùë@œÿ?àœ¿ ¼oñUý¡gŸx¿ö/ý¬5’êþE¥ÚYüEž’kkÚàö©¿ ÿh Ü2½ÊxçDoÃqÇ ø×Ãî‘Ì€_Ái|/ûüP²ýœÿà¨~,ø áoË4úw‡¿h¿Ù·ÇV^=øyâ‰,Бÿ Oö|·ÕµÿÚ#öuñ$¨ÖÑ]Ç©øsÇ ßQ’ëì_-m¡[xÀ>³ð‡ü“þ [ã­R-#Aý¼ÿf诧‘a„x›âà{YewØ‘¦¡ã_øGôöy…Œ ¬ÈJìݹrú/áx_ÆÚ›âx“Bñg†µxΕâ jÚ~»¡êvÄ•~¯¥Ü]Ø_@J°[\H„‚d@BN¯ßÿ¯üÿð  AÏùëô=ûûúŠ•_±ïÒ€$ € ( € ( € ( € ( á¿Äÿü^ð̾2øiâ7Æ>‹Å>;ð[kšAô÷ñ7Ã?x‡á¿Ž´¸fž~Ðþñ¿…=ñ_­#Æ~Ôþ$ø@ð¯Š|gà{^ÎëÄÞð÷Ž. Ýè~ø©ãχª÷¾5ð¦£ã„¶ÃŸ>ËdLúÄŸXêV—Þ'ñU±ŠïÁV¡ae¢^Zx¢ëRÔ¼"ÚÁ0àÖÙóàî¡|cÿ‚‰[ÛþÒŸ´´"ñ¡ð®çW¾¸ø+ðóS¼ay&Ÿ­‹;‹{ÏŒ^(†Y$þ¥â¹<qw5Å—†5¸ìí¼S©€T?~|+ø= [øWá'ÃO‡ÿ |/ipÚøsá׃|;àÚ(”$QÁ¤xkNÓ4ø’4QRÝUT @·ÆoÙ¿ö}ý¢ô ü{ø#ð§ã.sÛ¾›ñ3À>ñœ#Œ³mL¾›O¹‰±%µå„–×v“ª\[Oñ¤Šü}ÁQÿàÙøU±Þ~Ø?ðH­gÇ? þ(ü2¹o¿ìó¤x³_›Vi´rÚ„š×ìï㙯äñ†ã ;Ë{»Ok:Ϊºöél¼#­h÷¶ú_…u°»ÿà€_ðZmKþ àköxý£îlôÏÛàÆŽ—º­÷Ù-ôX¾4xÂæßH¹ñž‹ vÐi>5ðî¥qg¦|EÐl­-´óq¨i~"Ðí­¬õ-ODðÐôÇit$œŽ3’??ëžÝ}r¦ ?ÓÜëë@©È÷ç4ê( € ( € ( € ( ‰É=³@?~Óÿ´çÁØóà‡?h_Þ1³ðGÃ/‡ºYÔuVà‹ÛÛ©äK]#ÃÞÓ#"ë[ñ?ˆµ)mô­F³V¹Ô5 ¨¢8„ÓÄü%øóöæÿ‚ÅÁÄ?|]ð[öÑüGû5þÈ¡ý•âmOOñ-÷t+ ðÈ–—ß´Æ õÍw\×,^K†øAðå/ì¤Ó¤hŸÂ¾-‹H¼ñt ¢¿³×üÅû/è:=•ïíKûQ|jø£âçŽ;CMø9aá…> ·»+Ï`nøU>¥q>£6Ìöëâ j77ºï„d¼¶Y5 {J’=r@ßë{‘ ë‘Áüûû|öé@ûäã9éþ}èelŒ´ú( € ( € ( € ( —?mÏÚÙ/ö@ý¥¿iG]*âûàÇÁoˆ>;ðÞ™­Þ5†™âi¾oxNâí"žXŸÅþ5}ÂöÞLÏ%Þ¯PC4Α³[ë²»~‹W¯¢b“ÑÛ}•û½âÏÀkÿaÖý’` þ×þXüÿ‚|xSÆ7øÿùýõÚïöÀøÁûFë¾$Õ-~&übñÌÞ/ýŸ>5ü7ø{ðNûÇ]jÞ2ëñüoã_?Šu]%¼3«XÊÑFús^OWvÛ»·•Û×£Ó^«Kh¥m•´Mõ»VÒþwÕüý±ÿࣵgì³ÿŒðÿ†?iÝ[à7ÇOÚ›ö§ý­> ü~ø¹¬þÏ?¬¼o¬xöj¶ý¡µk§Õ¾x¯Ã>5ðgËžø= µÞ *èÚÄi W†ÿ@†ëM™½#.­N1M餓½Òþ´µõ¸¯wΓ¶º¦¶~¾w±/ÇoÚ‹þ UðSÄ_µ§ìMáÛOñGÅ_þÖ_ðLŸ ~Í¿µ‡Œ¾ü¼ñ¾‹ð·öûø¯ü=Ô|ñ·ÀÚÃmá6­¨ü>Ö¼w-¶¿ ø7Mñ‹tZë^Žë@šâÃAðÓz)=ýÎe®©©¨»éÖþvNá{´¶÷ù_[ÞKO/ÆÇè‡Àþ×?ÿऺ—ìUñ·ö¢Ö?k„ÿb­Sö”øuãü=øOàOŒ_ ¾"ü2ø¥àŸ…ß¼9sÁŸ‡Þðn±ð÷Æßð±ìüUáG^Ñïu}ûJƒÁÐï‹J¿×¼`µ\¯GvÓõJ÷·g{nÃ~eµ’wò“kæÓW¾‡ÿôý~~ÅðQ_Û_á¯ÃÏ|Yøƒ{âÿÙöø›ñ/â—ÇO‰÷Åo‹_>&kn ë><ñÏ‹µÇE¹Öµ-Â>°–ÛDÓt=ô¸¤´Ò-î'¼šä{Çü.ÿzÚ^R_“{lýTý§>4iŸ³—ìëñÛãö³]éþüHø¯{c$†?í|àýcÅ'MFVW3j-¥­Œ óešæ8âÌŽ™þ1àÔÙ–çöøëû[ÿÁS~<㟉V~>Ö|àOk­Ü‘üVø—k/~8xâ6›sZx†?ø£ÃÒî`cäè¾9ñ]™UIà ûµ € (üõ¿à´Þ·ÿ‚FÿÁrÿf_Ûçàí¯ü"Ÿ¾=k–?|s£i ö>ë^³ñ~ý¨t [k}ˆm|}àØø§R–C!ÿ„«ÇzÍê@‰¢Ð÷ͤ^¤©G"ɪº::²:8 ¬Ž2 ²CA ‘ŠímÛzÃüóþs@S©úŸç@Ð@P@P@P@ sÇ×üÿZ…ÎÐIúý}:þ…_ð\¯‰¿¿à¬ŸðWÏ?ðH_‚>"ŸMømð·Åš.“ã»a-掿u ·~,üNÖlá’Ý5h> |({ÍIÓ.9íµÛ?iöW‘¿‰€Pí»öTý–> þÅÿ<û:|ð§„>|>Òb±³‚4…õmV‘µ¯ø³RŽ$×<]â{å“T×õ›„Wº¼˜ÇVÖ0ZYÛ€}@üÿÁÂÿ°öµÿ»ý®~ÁZÿaûX¾é¾-ø¯ñï‡ô O±økÂ_cµÔ5ñ}™b`·Oüv𥟋lqÑà°ß(üMOÚÃOÓ~(|Jð‡À/jÚ'ÆŠ_>jÿ¬4^?†Þ Òÿá<ñ'‡üKñ?ÃÚV¥-ÄÞ ð‡¼Eâ Äí¢»itÞý÷µûõôóhWêôWkñµ÷þŸ]Ý]ÁSÿkŠÿ´Oíaðƒö&ý€¼)ûJü7ý•þ|ø™ÿ ¯Äµü)ë/|vøà޾ø{ðÏÂw_³ïŒË|B×¼=âëµÒÓSñ8ð­–‹k¨øÏÄ^ºñw…4}LNö·Tž½ž×õéøè7¥üŸÝw÷_]ÿ_ÄV>ÿ‚•øöoý½|_ûUxÿà÷üJ×ö^ñ_ÅO|ð¯ÅŸˆ³>µñKS¾²“Wñíñ³áoÄŸ Kyà‚Ú‹â-i:Öƒc¡üGÐï|I'ŒÝþÃÿh-GZÖ~0øßö¸Ö4yþ øŸÆ:׋Ö?ˆ:ºjº¤¾6ŸÂº‡Ämµùü|5ý™igà¥ð¬)o.×ÓîÖÞWÚúîé}ÿݯçnÚ²ÔÀ( € ( € ( ý£g~ÓÞÑ>|BÕñJx‡ÃÞ"ŽçÁ:ß‹¼ /Šìt¸ôoPÒ`šÆÃ_ÒijtÓÖêßë·£}u©ßg]ÿ]}QòïÇßø&Ï‚þ5|ñí)áÚ[ö»ý—~%øóá“ðGâ¬ÿ³Äïx#Jø©à_jÚž­á©[É®HÝíäšOÉ»þz÷·ówjïV´¾¶ŸÓ¿çOÇø"ͦ‰ Á6ÿeßÙ«Æÿ´Ï„?g€µÿǯ&øµàÿ‹ toŒÿ³¾™ãOüLñ·… ð?‹|Ká9/uÝ|fÕôŸÛêøCâg‹®ŽÐþøGAý¡¾$þÒ¶š—‰%ñ×Å„Ÿþ øƒJ¹»ÓÂv~ø/⟌^/ðµþc±o¯_ê_|U¿u{®ê}ÕŽŸáøôí/J¸µÔ®µc·’kïwþnÿr±òWüçÂúÏŒ¿à˜Ÿ··‡ô¥¸Õ§ý•>4jÖÐ×i¡xW×îìíãUvšâöÏL¸µ†S%ij$)óºšüWÿƒ<¼káý[þ ×ñËÀ–“@¾&ðoísâÍk\±FSqý‘ã/…? ÃÚ´êåKëŸ x‡M„²€ßØRí-µ‚€YÔP@ßðy§ˆôïøÿþ Óð‹CŒj~=²Ò¿hoɤÛ.¤4ïˆ×Á? øN8!S½†¹¬ø'ÄVІK>—²2JÈö‘ð÷MºÐ¼+ámò´ÝèþÑ´««€Å¼û;M¶´žpÄ)a,°»‚Bä6p:P±ÙSŸî@9!Ï»cõ  h € ( € ( € ( œäãÓúâ€+\œ)äŒ? ?úôüÁmíþ ÿÁÆŸðSøØ-׋ô;/Û[ĺ4w›MÆŸ­ê?µŸ€<-=¦à|£§x{]Õ4(â‡`†ÇPhP¥|´P@~ÁËžðÿˆàŒ_µ½Þ»ã·?|KáÛ¹‚™,ñ_Ç F’c¹›G_ŒÞ6Õœ»Eo}©ßÚŸ*(RöÅ*€}íâ?ØÓãÄŒŸðQŠ:‡Å?|5Ö¿ioÙGá÷ìû*üGð#¸ø›ðÁ¾ð?ÅÝ[_ø…­]Ã?ƒî!ñgü/Ÿú]+Â,·Ý¡|>ð=ññF¯\ÍŠuôVù¶Û¿à–¡Ñë«{ö²²üÛ~ºw?~ÿÁ¿jßMÿá†O‚ðJ/‚W±ǯٿâÅŠ¿l~(êß´/í9að»PÔíx³Iñ†›kájÚˆüâ˜t=;S×4 ^h—ú.aâ/Äê)Eé­£¯[_–ÿôÖáñsk¼žÚµt¯ÿ÷Õzßoö‡ÿ‚3~ÝÖÿ ¿àŸß²—ìÓã_Ø¿âÇìMû|0ÐõOˆÿjíCã¿€¼/ûNþÔKâkšçŽ~.x৆|Oˆ¾i^ Ô´ˆ~ øiªxòëO“ÅW'о#Ú|@ðýÌw++_vÛ·[¶Úô»õó[ôI%%k¿?›ëÝŸ¾±ä¶•¯Ã Z×öç°ý”tÏŠ6ž2¼µðu‡ìy7Åy¾Zü,·ðß…âСտápXi¾"‹ÆQx•|aä]¢xv ¯…ÒÌ›ñªaþ]úº€ ( € ( € ( € ( q†Çáùþ½s¾$Ñôßhú®‡­X[jš>µ§ÞiZ¶›{Og¨iš…´¶wÖ7p¾Rkk»Y¦·¸‰ÁY"‘цÐùÛþÉ?5ø6ãþ 'ñ“öxøÑ>©'ìoñÚM/O¼ñbmJK„ú¶·«ê_þ5ýŽÔK{©êŸ &¾ñ‚¼c r]›;Ÿˆ76Ÿ¬ßZxv+ÐôSð¿Š<7ã è>1ðn¿£x¯Â~)Ò4ýÃ^&ðî¥g¬è:þ‡«ZÅ}¥ë6­§Íqc©išœÐÝY^ÚO5½Í¼©,2:0b»@y'ÇoŽÿ f„¾7øåñÏÇ'ÿ…ß4[wÅ>*×®<›[Kh°–övp {½[[Õ®Þ 3BÐtÈ.õwW»³Òt›;½BîÞÞ@à ö@oˆ?ð_¿ø..³ûlxÃÃZŽû/~Ì׃|S¢h:ÊÇq‡üðÿSÔîÿgÏ…·~\—6’øÇ>3³Õ¾&xëNY¯t†Uø…gouö9ôh¦ÿ@]*•àôç§çþ~h¿³L(÷s×>ô}ybÖ€% € ( € ( € ( €"prO­V»)ÿøþ½ŸÇKÿUÿƒ—®¿hOÁs¤~ÍŸµ_ˆu¿ø›ÄKæÍþþÒ=¿Åbg€H¾|t¶¼ñ¦«¥[ ­Foø{L–;%“]ÓÔ€¡¨éú¾Ÿc«i7Öz¦—ªYÛj:n§§\Ã{§ê:}ì)sg}cylòÛÝÙÝÛËŵͼ’C<2$±;£« ”P@ÆŸüÇûrhº7Á/…ðNŸ‡—ç_ø¯ñ³Æø›ñ?Ãú.ýGPÑþøWPŸþߨZïœë¾#.¬h6Q¤÷dð Ì“ZFšÎqpû¥ÿ³ý—oÿcoØ'ö_ýu¨ÛÅ^ømgyã«hÝ$Ž×âŽ5=OâÄ(§Fauã?kv÷@ââÚÚcFXÐôßOSù~}èpÿÓúš|QGaqǽÚYh©¾Yd‘öºGn]ÎY$“@Å<’$q¬“3H¨ªò”Pˆd`9D±%T%P@P@P@P@ƒâ_x{Áú±â¿ëÚ7…¼/áÝ6óYñ‰,êMÖünñõ¾ðoLš Bo i¿§†ÔP~Òð@OØ_ö€ý›|Gðc[ѼP¿µ½bãÇ~Øþ*Õdø…ûOk?%±røÓâÄ/4zŽô}Q-µ¯‡Ï£ø=3m·„tŸ_Xèz¶ŽüÇøWWÿ‚îÿÁºZ¾©áÂòþÓŸ±=–©{¨ÛF4_üOøo¦Ü]Isu¬XÜè³ZüBý5Û¤’{BÃP›Hð]çˆnî/¦±ñÙŽ-FpÒo…¿ðy‡ìÏ©höÍñ¯ö9øéàÍ|D‹yÂßøân$êª$–ÚçÅ·? oaŠVß"[Ëk;À¥ak«’¦vå~3ÿÁå¿ £Ó&ÓÿfسâWŠüQ|>ˤßügñχ<¦Y^͵-¦›Ã^‡â£âfaö]·‰ø/G±]„?5åñoŽ|Iucq:xoKo>*þË/a³ƒâof>ݬxfHí~$ü6‘®tøzØXm_€~™F¨¨=xî9ýh§ðÿé@P@P@P@P@¿cþÏ £zôýð?Ïå@?ðXø%Ãßø*Gìß'ýFöÃÁßþM¨ø£à'Å«Y'‹Ã(»µŠOÃ^#±½õÇ€¼q–Ÿâ{kEšæÂêËDñ=•¦£}áÛm6ðùhÿ‚ÿÁi?k¯ø"WŒâÿ‚zÁQ¾ üEñÁïHš_ÃÿÚ,‡ÄO†^ûL–ÖÕµ Ë üiø#˜ŸþØ,5ëkï Z-Þ™ k7všE—l@?±¿Ùãþ Åÿáý©tk _àßíð+Vº¿†)WÂ>(ñÆ•ðãâ©‘WtwŸþ!ËáCåHÞCÜ ìž`V ©•‘Øè~زoà "o|Hý§¿gÏhDÓɪøÃã/ÿXˆÕCe.5oZG+0*#Ž"òJ̉»º©þnࣟðuì¯ðKÃúïÿØ.øj_ŽwÐÜiZ_gÒ5­'àO‚u9‚-B[­B#Äu YÞ9,t_ ZZxcTÉwñÜfìûÀ?àŒ¿ðHoÚ'ãoí/ügþ ‚ÖH¾|96tú,–6§ý“á½Ã^Ó5pí+L´åxzgêyïõýMw–qí§çùô  Ë {uöÿõPÔP@P@P@P@P@~Õ?·OÂ/ÙrÿÃ¥°ñWÆoÚCâ47ÂÙkà΋~5üFxÙâmXhŸj¶Ó¼ðÿN•%“ÄŸ¾ ê^ð‡ìíod¹Ö¦½‚=:àåÿþÄ??l={Gø±ÿ@Ö|;­x[LÔ¬¼Gðãþ ëð×WºÕf_‡w–“¥î©ü|×ç·Óï?jï‰úSGjfÿ„OÓþ ø{TŽÿþoê˃Kÿcøþ¾Ç¿®=9èlôí¤~8Ïÿ¯§×¥x¯í/û&|ý­þÇðóã/‡.¯“JÕm|Qà/xkU»ð§Äÿ„¾<Ó™ üGøQãý¡ñ|q \ª\iúÖ‘t‹p‹&™«Úêš%åþ™vðÇ…?jÿ°_‰t‚ÿðQzü×5k |ÿ‚ŽXiú„õ Bâ;á÷í™ éˆ4Ÿƒež[m7Jø½f–Ÿ¾$<°I{7¼ýÓó “jØïöný°¼ 'ÃÚcàׂ~0øI^y´û_é{õOÝÝD ›R🉴ùl|Qàý^HTBú¿…õ#Shs »13!þj>7ÿÁ¢¿±g‹õKÝWàŸÇ_ÒòY%_ë+á‹Ó77îíô„Ô¬¼-âÕµxƵãníÛ“z€<;Â_ðg‡Á«-N)|yûküQñ&޲ƒ=„~xOÁZ”ï’-[YñwíaÇ•¾‹:«æË ·ß±Wüþ éû êš_‹¾ü_üRÒ9,>/üeÔâ7´ë¨qåjº²°ð‡ƒµD̃ûWÁðÞ£$sI ×R@DJúùg¦}ÞÇœgO|Ðaef=;Ÿ§¯óˆ¸Àü‰8üÿÇ=(tÿÏÖ€$ € ( € ( € ( € (  ½o[Ñ|5£ê¾"ñ¯¥øÃúŸy«ëzî·i¥hú>•§ÛÉw¨jz®§,Z~Ÿck·7—·sÃmmo“M*FŒÀòOPý¯hÛÂþûÀßðM›[?ü ŽòçHñŸüâ—…%Õ< 2ÛM%–©aû|,ÖE„¿´‰-îRâÚ/о%þÌø£]ØÜµÏÄiL ö?ì­ûüý‘ì0xȰ'í,Ýzõ~ÿg¯§è€'K%…ýsúõëõ  k§Aõÿõ\óþEH§$õÿëÐ/Š|%áxg^ðg¼9¡ø¿Â>*Òo´x[ÄÚUŽ»áïhš»ÚêZFµ£jp]iú¦›m,–÷–W¶óÛ\Bï±²1òo†ß´/üÆYu¿ÙóCñ÷íMÿò‚I.|Gû/ÛÝ_xÇöŒý‘´°ÆKoö_Ô5k©µŒt»}æûö|×õø:ÖÞþkZ¶Ÿö¯ ê/ÁŽŸ¿i/†^øÉð3Çþø—ðÓÆ6fóAñW†îÍŤÆ71^é×ö³$މ¯i7K.Ÿ®øw[³ÓõíT‚çKÖtëBÚ{hÀ=b€ ( € ( € ( € c®yî;zÐ$gëŒsýÇ­V’ÝdíÉíùô'®;ó“@siÊ„óüºgäþ´Iô¡Ÿ»ž?Çé@Ç¥—çüŸ_zÓ†È'8Çÿ'ßÛéÒ€4ŒýO·°ý}ø  Usô  ºP@P@P@P@PÆßµWíÉðgöR“Ã>×#ñ?Å/x¾~Ì_tã_Žß¯!2,³è>·¸‚= Â:sE4ž#øãKï|?ðÍ­½ÄÚLjíæH­§ùCDý‹>:þںƗñ/þ w©hpü8²Ô,õÿÁ9¾x‚ãWø áy­.#Ô4Köžñ¼1é×?µ7Ä6xí$—ÓÚiŸ´ FÎS¥øOÅæãûuÀ?[4ý>ÃH°±Ò´«=3KÓ,í´ý7MÓí¡²°Óì,¡Kk;;dŽÞÒÎÒÞ8à¶¶‚8á‚Ò(‘QU@Ê(  ¢€SÓõë@—¶¶áûwÁsÿG/ûQpæÀi¯­~¢dû~Cü(FãÓôÀÿ pCžOÝ•<: Z(òÛãìKñ#áoÄï~ÖÿðN­sÃ_ ¾:øšóûsãìõâ©n´ïÙ—öÄòP}¦_iZ\3ÿ¬øé$Wôƒ´ãªO|FŸñ/HñŽu%ˆ¾þÉ_¶×ÃÚ®øHh~%ø7ûE|)’×Nøïû/üS†×Iø¹ð—X¸U]ÜÙ[Í6Ÿã?‡šñawàoŠþ ¹Õ¼ ãM*k{;R†ÿíÚM€Ù”P@P@P@PJƒìÏ_󚌩:üfÿƒ‚ï¾'ißðHoÚâóàÝß,>$Ãÿ þ»¿†W!µñÄ^gí9ð^-cû ºkÑùšš¬z—ØÒý.³d× €sðnÅ÷Å=Oþ 3û<Þ|k¼ø…¨|F—ÄŸFµuñFãÄW~5’þ7xú=$j“øµä×^$Ò–Í4ïµ¹EÓÖÙm¿Ñ„B€?oNÀçÜ J!ïüÿÏøÐÀ¥-P@P@P@PSTÓ4=7PÖµ­FÇHÑô‹½OUÕµK»}?MÓ4Û$º¾Ô5 û¹"µ²±³¶Š[‹»»™c‚Þäši4f’Z¿í—ñóöàÕu/‡ðL« #EøOkw¡øëþ =ñCÃsjÿ´‡µ¸’ÃVÓ¿e‡—ϧ\~Ó~7°¸Žò¼ouq¥|е ôøé¦]À>¸ý•b‚ß²jx›Ä>_üFøÝñ! ºøÉûK|_Ö[Ç~/jPùf&ñgï …¬|;`bŠ?x–~ð…í¡ŠÃ6 çK0ØtP@P@–ÿ¶¿üŸ‡üÏþÎcö¢ÿÖý¥¨õ"€ ( € (âŸÚ×ö!ðíC7…> é>&ñ'ÀŸÚáLwS| ý©¾-•§Äÿ†÷W¦ŸAÔâ»´Ÿ‰? µé™ ñ·ÂC©x;Å:uÅâ}ŸKÕäµÖì€<[à/í½ãÿüOðçì‡ÿð׆þ þÒzü²éß¾.xa¯ ý™ÿl[{5éß|C¬Hóx+âÊE²_~Ï^3½ÿ„¿NšXõßø×ÃwqßY€~ŸÐ@P@P@P@y?ÇŽ_¿fÏ…þ'øÕñßǚÅ^ þÅÿ„«Ç>'š[mDÿ„Ä:O„ôO·M7§öŸˆõÝ#H¶Ù î¼Ô VÚ¬]@+| ø÷ðgö›øg¡üeøñ ÃßþxšãXµÐ|mák‰ît]RãÃú½î­Ek4ðÛÊϧk:uöŸp%Ûqm*‚À ¿µ}?¯ó P@P@P@P@PȵWí»ðOöJ·ðÖã¼IãߌŸäžËàßìßðD“Ǿ1jЇó-¼à;¢ž=ÈÆï¯øçÄ·Z€¼/n?ˆ¼M¦©‰eùKýŽhÛŸRÓ¾ ÁL/tÏ ü·¾µÖ¼ÿàø]âyuo…V†Öt¿ÒuÚçâ^šº|ÿ´Œ,î#´ž_‡zTz_ÀMþÁÑþ <²jÌúݤiV¥iºƒ¦iú&‰£XZiz>¤Y[iºV•¦XA­Ž¦éöqÃicaemVÖ–v°Åomqà i*€ ( € ( €?-ÿmù>ÿø#ŸýœÇíEÿ¬ûKPê>Aîã@ @P@ã¿¿gÿƒŸ´÷Âÿüøñà âGßÅÔ¼?®C.mïmX˦뺩g-®±á¯h—[oü?âŸ_éž ÐuâÔ4JÎò$™@?1í~.þПðK»› ~Õ!ñ¯í5û¬ñXx;öʹ²¸ñ'Ç?ÙŠÅäK}7Ã_¶6—¢Ú¿ˆ? í"h­4ÿÚwúgö®ŽÐˆþ1èq­ì>2`ØøÃþ/Ð4_xO]ÑüOáév:߇¼GáíNËZÐuíS¶ŽóMÕôm_NžçOÔôÍBÒh®¬¯ì®'µº·–9 –HÝX€lÐ@P@P@PåçüwöPø¹ûpÁ5¿iÙ{àM¦ƒ}ñWâqø=ÿµ¯‰õ¸¼9¡Ëÿ_Ç¿…ߵϷk3Ã%Ñc·ñ‡Åox»F6ºÄÛÅrï£ëV/p«ù , Y£$€~¯Ð@P@P@P@!`:šà¾%|QøwðsÁ>!ø•ñcÇ>økðûÂv/©x—ƾ8×ôÏ øgC²B¸Ôµ­bæÒÂÑ]Ù!…e™^ââH­àY'–8ØðÿßðWûø(¿ÄÝoöbÿ‚Pxëá·ü$ZEÅ÷ÄïÚ£ãtPXhß ¼3ý£w¥>©ðKöp×5 â—ÇÿI¬º–›¨jú?…>xcÏЧñ—ˆµI5hü50éOì§û|ý–.|Kã]*ëÅ¿h?‰@ßjŒšªxËã—Å ˆY;OÄÒÛÛÛø_ÁZkÅ ^øgà]?Ã_¼5iki™áô¹Ž[Û€´ƒƒ×óúPè € ( €qÖ€*]^[ÙÛÜ]ÝO ­¥¬2Ü\Ý\ÊÛÛÛÂ$×Í+$pà jÒK,Œ©+;°U&€?•ïÛgþ mÿÝoø)Gü›ÂV´&¯è³wÆÿŽ>&ø×ñwÃ:v¡âOƒ¾ ><ý–þ2|ðÎŒ|o¡Å‰/gñ§4õMWÂ6z÷„¼;¦µý爿çÞ€$ € ( € ( €>/ÿ‚…þÙÞÿ‚|~Çÿkïx+[ø‰áï„ð€ÿhx;Ú•†‘¬ëðŸ|Oð_ÃO±ê:œsXÛÿgÞøÒÛT¸óão6ÖÊx#ÄÒÆÀ7þ Íûpxcþ -û$ü;ý­<à]{á¾ñSñΛgáêš~±¬iÏàoëþº–çPÒâ†Êe¿ºÐ%¾bŒ ¸Ž) ‘¸¨ € ( € ( € ( ¥•cVf`ª€³»UAff'€$ðI4üÒÿÁMàæoØçö*ÿ„‡á—ìý6û\~Ñ6ëN›Kð^»|ð&­ø$_üL°[ÛmoPÓ®9¹ð—€Y½iínôm_ð]ðI€á/Ã_ø'gü{þ ñ·‡þ;~ÝÿõïÙËöTkô×|¤x“@Ô<9¢Å£ÝPüýýÅÃÞiÒÇooñOâUõ­Î±¤\[ßAâÏ-™Ó˜ìOÛþ 2½øo¦ø_ãüÇö„ø…áo? ì4Ûø<ñWÆ‘hšç‰üK¢[(ÿ„§á߯?iž—á÷Œõ aiãÒµ]1<5>«zímâŸé%°ò?Ù#þ9ý²?`ï‰Q~É_ðYOßîo¼2ÖÚdŸÿá‡Ãÿ´4ÈÖv:ߊ|6>Áá/Œž™!ó- ¼óÃæØOãK†¸'Ã]o6Þv¿ñuå®­y§I-ï†|=ây-ä´ Éf­ñWþ ÿ(ø“Pð·Ã ÿfØR}Zk e­5{Á¿M‚ä¥Õ—ü~l£ñíâ«EŽ$½ð—‡´Û¯ XêÑØ_Üø;ÁÏ&¬ ¶ß ?àÒø'W‡¿g[áwÄ¿üZø‰ñÓ_†Öòãö”Ó|E7ƒ5o k¶°L‰€¾[Ï«ø×ÂRË1kýÆÖ¾;Ö¯×s/Šl¦N“LüMñ·ìÿ±ÿƒs >Öï}½ß…<öµñ§í%âë5ŽV´ñ³}yá-;[†öÊx;•Ó”ôöŒÿƒ>>Û|ð¥×ìqûJx÷@ý¥¼§-Þ¡­üc–ÑþüQñ¼íÕ»ø/GƒÄ¯-¯cÑu+þ«{+ ->ÒÿO½ÕïŠæøóà7üÓþ ‘ÿtø•£þË?ðV‚ß~-|8²o±xÆþ"žÖçâÜ^±–+w×¾|\7Sø+öƒðíª¸2Ûøƒ^—Ä)w4:}÷Ä ¬›EÙ¿ìeÿý”?oo‡©ñöbø½áïˆZ}¼6§ÄžIIñ÷¯níÓ|oà}L[ø‡Ã·>rM ­ÝÝ‘Ñõ"kTÕ¬6^Hö¢JÝ<{þ¿ãô=O4`€hh € ( € ø×þ ûøOþ û"üZýüqãxÂÿÿáþÔño…-tÛíIÿ„ âoƒ>&Ù}‚×WŽ]:_·ê> ´Ón¾Ñl²¼¸’,N‘°Îÿ‚wþÄ>ÿ‚v~Éÿdßx×Ä¿¼3ð÷RñÆ¥câ¯Úiv:î¡'Ž8~Üÿtïë÷:‚xsQñw…ÿiÈ4OiÖ7V‘ø‹F´ƒÄš„·ÕMÎ…ªÊ¶ñjöö+-œÀè¡áÿhÐôŸ xWCÑü3á­OµÒt/xL²Ñ´=J±…mìtÍ'IÓ¡¶°Ó´û;tH-l¬íá¶·…(cDP b€•]YC£¬¬++ 2°9H äphüíàíߨ£ö^ý™~"þÉ~| ð·ÂO~ÑS~Ðïñz/ÛÉ¡xcÅÃçø)qáýr?ÙɆt=nI<}â7×5 KÓ$ñ ÷1^ëK{¨Eö·þö~ê//€| –,O„<1ÔäÿÈË<ç'§=ÿ{TG;O¨þŸã@( € ø×þ !«êšìûrëº&§¨hÚÞû~Ó¦¬iW—v©¥jšÁ]éú–›¨YÉ ÝŽ¡aw 7Vw–²ÅqmssÃ*JŠÀøÀÿƒXà˜?±çíiàŒ_µŸí)ðÙ>4xïáGÆ«oøÂ>:¹M_áfœ±x;Þ-“ÄúÇÞÙlücâ oµë‹aŒn5ß CoµÄ>MZ5Ô€úZÚÚØÚÛXØÛAgeg6¶––°ÇokkkoÃommo ¤PAH‘C H±Åª"ª¨zñOÚöpøûTü6Ö>~Ñ_ ¼ñáÆº3{áiêVÐ]¬rE­£^+Tðçˆ,VYLñ‡ï´Í{J•Ìún£k6$çÕðƒö`øyÿõÿƒ¦>~Ì¿³¶­ã}#áO†üe¤.Ç]ñ<Ú–¯ýƒñ'öWÿ…—«xKSÕí Óe×<;§kºûÚévúÌw·’iºf’u›Ý_U¶—U¹ÿE-ÿÏU O'sßó錧JêPó׌gùP´P@P@~[Áij¯Œ±/üCö“ý§~êz6ñcá™ø;ÿ¦¥â ËĺM·ü&~|?×~×¢ê ÙÞùÞñ^±p?f¹–¸ÿ{Ãÿ‚"~×??noø'ÁOÚWöÕtMkâ§õß‹vþ£áíÇÃTÖþø¯ã h«o£iÀZ[4Z6cïÍÄé%ÄŸ¼‘¨õ–€ ( € ( € ( €º¡ Ãø8ÿþP¹ûkûéõ¥> šóø5ËþPáðþÊÇ¿ý[þ+ è^€ (üýÿà–‡ðs÷ü„çñ^~Þ?úÑz5z^o”zîŸóìh°Ÿûçü(j( €!¼Ïjþ ÿàŒÿÁÐðS³ëâø(7þµ‡†(ûÚ € þÿàõ/øõÿ‚m×Çíÿ¢ÿfþľ ±>ð'¿„¼7ÿ¦[çÖ€>†€åSè§ñÉÍ\ € (â?ø)AÿuþÞöfµëðCÇB€?ø3kþL“ö§ÿ³¨_ýTž ì€ (øý¢Î?àñOŸú™þë èýÕxmÉÙÏ?Sšô„þ÷Æ€& € ( € (Ë~5|øKûF|3ñ/Á¯Ž~ðçÅ…ž1þÆÿ„£À¾-±—‡õÏøGµý+ÅZ'ö…™dfx‹CÒ5‹_˜ywº}´¼ìÁƒà‡Àƒß³gÃ}àÿÀo‡~øUðÇÃsê÷Z‚<`4ÝK¸×µkÝwYšÒÍYÄo¨ëö£tw2ææWãv(Öh € ( € ( € (¡úü?ÿƒŽ-nnÿà‹ÿ¶ÄV–óÝJš'Á«§ŽÞ)&‘m¬¿hÏ„—·,‘«0‚ÎÎ îî¥#˂ڧ•’(ÝÔó7þ kÿ‚ŠþÈ)ûü5ý…5ÿ‹ú‚¿i¿ øÛâmý—Ãï0ðÀñ퟼u¬ø£Fÿ…o®j.š'‹õAe©¥½ç†,o“ŰÜZ^ÜG Ï¤Å§0õç@|ïûMþÖ³ìkðÏPø½ûMü^ðÂØy±Ã©x£P+¨ë×ñDgþÄð†ìc¼ñ'Œ¼C,*ÒÃáÿ i:¾±,K$édaŠYø1ÿ‚|XðÇíÿ~Ú?´È5¹¾ü\·ý®þ(øVçXÓOÔáð¯Äþñ†¤Ö죚î-.þçOÔm„Ör]Hb¹/n$wC@èAáõùGûÊOóþ¸ Â?ý—ü(j( €!¼Ïjÿ6Ÿ¶wà”ßðqwíáñ§ö³ð7Å/ ü>øñöºð¬7Ú„.޵¦ø_âçÇõñŸ‚~*Aáý]ô›ÿøRÒ4u5 ÿhÝjš&§­áËMu£ŠÆìý þþÑŸ¿j†Ú7ÅÿÙß⧃>/ü7×—>)ðV±§k ÒÇ·N±iû½OÃÞ ±Y£]SÞ ²Ó5í&gjZu¤àÆ=¦€9?xóÁ <#¯øÿâOŒ<1àøWO›Vñ7Œ¼g®éžðLJôÈ1çjλ¬ÝYéšm¤e•Z{»˜£Þ苺‚þo¿ðr7ü¯ö|ÿ‚¥|fý•þ þÄöž?øÅqðTøË¢KâÍÁú§ögÅ|ZŸáM¦™¢ü*ðÿ”þ4ñ$zdŸná“Q¹ðq©ÛÁªiÊš•Èúü#²º±ð_‚ìîàšÚîÓÃ:µÍ´èÑÍoq‘iðM€é,R£G"0 ¬¬È8÷èGËÑGäM[ € (â¯ø(ý¥Õ÷üÓöë³²·žîòïö7ý§mmm-¡’{››™¾ xâ(-íà‰^Y§žWX¡†4i$‘•Y˜ þ=àÓÏø(Ÿì‡û<ü;øËû'üuø»¢|$ø­ñcã}§>MãÃÿÿ¼YgsàŸ xUt_^:ø{KñYÕ4YV×EñÞŽÚȽ°¶ðüú¾£,ö6àÞò²²†V ¬+)YHÈ`FA‚怀<ÏãÆ„ß³ÿÃýâ·ÆïˆÞøUðãÂöÿi×|gã­{Oðîƒb7‘nouáKBöEû>›¥Ú KT¼xì´ëK«¹b…À?Ï'Â?µ'Â?Û‹þ¢øûF~ÎÚŽ·â¯„Þ,ñÖƒaá¯j^Ô´ ^ÏáçìšÞÖuäѵ8¢ÕôýïYðÕýΙ&³g¦ê¥Ëiq¨iÚuÄÏiúømHÙÁõýrhÒSø?Ýÿš€ ( € ( ÆOø8?Føâø$'íq¤|Ò¼y­üH»??áÓ>ØøƒRñ½×‘ûN|ºÕÿ±l¼-ÚôÞN…§q©}†&òô˜¯åºÅœw 9¿ø7SDø«áßø$Çìó¤üiÒ> è-üKñŵ3⇈ôÏAÿ¼}>”ÚŸ‹"ƒ\Š)´¹-&ÓÍÔj’X=´–Ù·h‰ý¿ € ( € ( € ( €2õ+]JÊóN¾µ·½±¾·žÒòÊîîm.í.#xn-n­æW†{yáwŠheGŠXÝ£u*Äåþ gÿÂ~Ì¿´­Î»ñ_ö;ºÒdÏŽSË>ªþÓ,'¼a«’Ó§Úü'¤Ä÷ß ïgœD«¬|<·} ÉVYäø}©ê/|€”?à±_ðVŸø"?Äölÿ‚›ü$ñ·ÇÏ0Íý™á_ø£UþÓñ¼~±d„j>>æû@ø¡¤éön—rx+Ç·—ÚþÓtµÏ‡q#Y Ýß¶çü·ðÄxsAøÿÔøCâï‹ÿ<{¦ipÛxÃâÇ„uM#Ãþ×|A QÁá½#áÆ“{?‰þ%øûN¼œéÒZÛ_i~‹W[Yô[ÇÚt’YÊðìûÿ7ÿ‚…ÁP¾&ØþÕŸðWŽÿã@t?_ð Ð@ƒ¿ÔgùZøköÚý€e_ÛÛáëü>ý§>xw╬WCÃ>'h›HøàkË•µøçL6þ!ðôÆT†{»+kÖÑu†·†ß_Òµ[%{Fþ/þ9Á?ਿðG‰zÇíEÿ¢øÕñâ¿Ã{6ûoˆ|áë{kŸŠ‰áËe¸M âOÂm?‚þ>ø~Õ¬W~ЦñwSM©Øø ÃÍdšÊ€}-ðÿþ³Ñ¿g½~Ëãgì­ÜþØ>hô=;Lðoˆ ð÷ÀÿjH—0j ñ$ž"ŸQøƒðêM"ò$K¿Ziž;›R™ZüY¡‰ä:XÄZì¹ÿœÿƒ…¼a¡üYý­¼}©~ϲ3j1ëžÒuMUð¿Ã»M.mÆŸ‚ÿ†¥o¬øóQ6—l~%üEÕö^é÷ÅgñVKUÑ@õ‡ÿüÿ‚IþÇŸðO= þü:‡Røs`,¼IñÃÇŸcñ'Å,ªîõãgmká]ï &ðß‚ôÿè—" yu+MBú&¾õÃDÓL[IÁÿÏ'©þ|PmÆ1ÐéïÏ_ç@Ð@ZUÞO©ÀÏçüÿ:¯çßËõ?œø)×ü£û~Üïâ‰4Ë_Ù{ö‹Ô~Õ'ć:¯ü þ5Ö%/+Kñ3Ἒn‘ª]_\<²Þø«ÃñeÍåÁÔu½GÄËo(óÝàÛ+þ Sÿìx«CøUûMø6÷ö•ýŒaÔ Ñ|,u½oYñOÃS¤ÆÅ`³ø1ñÀi·> øUª½¼-ýðçÇú3é[Cyuaðæ´nÔßÚ[þýý•tþÕÿd߃Ÿ~%þÐ~1Ñäy¼ñ_Kÿ„7ÁõòZ˜¼m¬hºž¡wãë8Ž÷LѾ]-†µ¥È£Rñ—„u]új~Y|:ÿ‚iÿÁ\?à¹?´?Úþ ?ñoÆþµÇö§„<1âM&MU·Ð¯HYüýŸã{ À¶ö+9<}ã¸m5í^ÐéºÜñ|EUy˜úñý†àš¿²wì ááoÙÇá^›áíRúÒ _üI× ~ ø©ã–ˆ¤'Š|muoüÖ¯rŸk‡ÃÚLz?„ôë§–M#Ãúw˜à€~›h¶&%RF1œ÷÷üzô µ:ñÐ ŸÖ€þ¿¯‘-P@P@5û@þÐÿe„^-øóû@xëNøkðÀŸØ?ð–ø×V³Õïì4_øIüM£x7Aóí4-;VÕfþÑñ7ˆtm&/²Ø\ys_Ç,þU²M<`¿gÚ?àŸícðŸÃßÿgŸé¿¾x®ç\³ðÿŒ´‹-gO°Ôî|7­ßøs\Š+MMÒ5HÛOÖô»û MÅ„JòÛ;ÂÒÂÉ+€{uP@P@P@®rzú_óÿ×  ‰0!†I'< ä篙úô øáû<ü$ý ¼¯|-ø×ðç¾xŽ+Xð´+=wG¸eW÷Áyµ†©bÒôÍcO{]SJºÙw§^ZÝGÈùÿûÁaØ;Ä÷‹ÿg¯úv—ã­sQÕ.cñÿŒµ KÇþ6ðî“©K)_ øCÄ~*ŸP¾ðLJ--ey‡H{}SY´ŠâS_¼‰nÈ鞟áìÊc¯%yõ=^ ñ@­ž•eFpvúsœŸóý@6ãŸNãß úñ@@ÇJ( € Bë@Þ0x# ÿŸN¸ý=p(óIŽ`ØQžyÇøgóþ\Ðç÷Äø&ì?ñ7ãæ‰ûNø÷ö]øE⿎žW6>=Õ|+o5ÍíÙh×Yñ&ˆ¬žñ‡‰t¦µ·þÀñG‹4mgÄ^XQt-SN }‡iáÐ|?ØÆè08ýF8ë@¥†Ž‘,º÷럧o­tQ¨0£ôþx~|÷Í[ ¯­-P@ uÏ?Ÿ½RšÙ&0É=ÈRúçëÎ@<ÏÇÿ ¼ñÂúï‚üuáoø×Á¾'ÓæÒ¼GáOèºwˆ¼7¯é—#~±¢j¶÷zn§e0QæÛ^[M RS*0ùQû?Á¿àœŸ²×ÆŸ|zøIû9è6Ÿ5]]5 Íâ›íWÆÚ'ÂëŠf? ô/Ýêv –kÅ–ú-FÚ;oJ73iš©¥hÕ;AÙÆ%}{ÿœŠí¬txâ°aο_LšßŽ ¸ {}MZ-P@P@ÁHÿbËOø(wì]ñ›ö=¾ø‰sð¢×â÷ü+¿7Çöž‹Æwü ¼ ñA<¿ M¯øb=KûVO&ŠûµË±¦¢×ãí-j,î2ÿà™¿°ÕŸü‹ö9økû#Ø|J¹ø¹kðëTñþ§/<'‚n5cã¯x‹Ç ørø¦;!¦?ˆMY·uöµµe-ÌÆÞ ½è € ( € ( € çüYâ¯x¾&ñ¿‹µk-žðþ³â¯뺔Ëo§è¾ðös«ëZµýÃYiºm¥ÍåÔÌBÇ2;)£wê Ù6újr¾+øcãÇÁß…?<³oàÏŒŸ üñOÂ0ø‹ON¿†> øgLñf€šÖ–'ºnª4­ZÓûBÃí3ý’ë̓ΓËÞDï¯õëóÓ( ƒþ=è‹Û?ýlþdsïŠùËà7í9ðö ›âèøãý#âEÀïŠÚ—Á_ˆë¿Ú|EÑ|5áëZ&¯,_Ù~#G²ñn—c¨êZ%ÕîŸo¬Å©i_hk­>áTën¿æÓþ®o|øóð×ã–³ñÃFøqy«¿ìûñŸUøãýEíV-+þO‡¼à?x“LЯRyF©oáÛˆ:W‡u«“«Yx»JñˆÐ³é&yŽ­z~*öõ³üC}|ÚûŸâ{@:þ_Ï=s@PÌßl?ÙÓö†ø¹ûDüø1ñ+Kø…ãÿÙO]ð…~:[øzÛP»Ð<âÏÚøŠçOð¬~-ƒÃ> ñ‘'…uÝ/Æ:_‡5]VãÁ>#Ó¯|+â¥Ò|Ma¨i6‡Vº¤›ùßñÑ‚w×¥í~þ‡Ó4P@'û=~Ð? ÿiÿ…Ö>ê7šÏÃí_ÅŸ|%¡ë—v©i½7¯‰¾/øS®kšFË‹”¼ðÖ¯âjÚ‡…µe‘FµáÛ3VÛ‹ÑgWäÚù­ÿ=n¤û«ýç¶P@²g8ü¿ÃÓó·€?j/€¿~8üký~xÿIñÅÙÛLð£ñ›BмëûOÜ|H›Å±øk@Õõ¨#}%|T#ðf¯u¬xn;·Õ´;i´É5[kWÔ!J?]D¶v»·V÷zéswáïÆï|Lø—ñßá_„F½sâ?ÙËÅ^ ð?Ä«ÛÍúÃÃpø¯Æÿ ü/ñgMÑ$øúÎ/ž(Ö<ðâWþ|xøuðC㯌´ÔF«á…_üuðçÃÿ »ðó\»K¤Šdµ¶/«[Ú×õjöù]Kúþ «þ új9#–4–Ib•Hä•ã’7‘ÑÔ•tu!•”À‚ €<[öý¡>~Ê?~)þÑŸµ¶Ð>ü ð†§ã_BͨOmb©Ž‹¢Z\\ÙŨø“ĺµÅ‡‡|1¤µÕ»jþ!ÕtÍ29£’í4¯øþ±7m}>öì¾öÏ’gOø(ͧƾýŸ>*~Ê¿´çìñ/â WãÏÁ;ö„Ñ>A§üQøm¡j:Ÿâ}?PøsñÇÃÂßü ¾&Ð5|"ñ´>ñ¿ƒ´ýZÂçW°As´ßnÎö}ÚÖÖßm~ÿ›ïÝoÿ×SôŠ˜P@P@P@P@P@—¿ðWÍsTÔc‹ïÙÏÂúôžñÏíÕñkà÷ì1á}BÏûFxÖÃÃ?õ(tØo þͶ¿|f×ó°´Ò¢ðÜš•à{kYQ‹ÙIé{Y_¼½Õø»üµÒìO[.í_®‹W÷¤×Ìümý¸uïˆ>:øËÿ%RÁI>6é_²çßü;ðŒ³¿íSáø'Oìoû Þø+àvŸãMû?Çw_´… ø÷ñ'GšþóâÄ=sÄŸü]‚´ CߥÑn4-3Â÷êZÍFÚZ6®Úí}šíÖý ÝJWêõßU½£Û¿wµ´¿yðžo‰_·Æ?ø#_‚þ4þ×?´Ÿ<3ñ'þ uûGüYð§Â/Úâ7À»Úsâ¾™'ìý¦=‹µ†^,ðvµ{©ÍoãøÿU½ðÍÅŸ‰ÃÃZµ­µå†u­m>Ì<ç5~¶I4¯ÿ׫¸þÜ¿ÁåvÝßü7—Cç ëþ&üCø#û x‹þ ûOCð£á¿üŸö¼ý’áøÿá¾)ðgƯˆ_~~Ê¿ü=ðâwŽ Äö:ÿŽüe§üE×/¾ \ø£W»þÛÞœžÓ4=_GÑ4-½¿ÅO[mÌ¥Í_GøÚêħwJšwåq³~«·µgêì‰ðÚÓZý¦ÿ௿ðLψß~)~Òÿ²VàÙËQÓ<ñƒâß~!üGøI¡~׿ ~*éß~jõ¯_|[¾ð½þáÄ^†óĶš×…<7âaF÷[Ô¯¼U¯§£V{ÆïÉÝ®·ÝkùuĤŸI%뢗àôüÙßþÂú/ÀØy¿à¯‘øKžø1û;~Î_´å‡gð÷„tˆ4Ÿx?Á>ÿ‚sþÇÞ:ñ†£o§Ù ó.%Xµ¿ë²™µ cW»Ô5MFâïR¾¹¹˜·½m´†¾·Õßñ¸ïîÝôsü=˯|ø«ãÁþüMý§þ>~Ï·?¶G†¿à¢_·íiá€?<û2ø‡ãïÅŠ÷?k;߃þ(>ñF®•ðŽ¡ñ[Ä>»÷Þ2Ñþøgźw†õ? j÷Íâ­o{5§<›ºìîã¿’K_Ì-²nöƒ¿K½/uêÛ=zŸ«_ðKÿøûÃ?ÿॿ±þ£ñSâ_ǃŸ²Wí ðÎÇàoÅO‹ÿn¾*üAÓô¯ü;ñOÇ?õˆšµæ¡âŸÙü ñž£{ h·¾4Ôuišf³†umjìèÙé¯í5²å‹·¯7á¢Óõî¯ýé+÷µ¿ÿ­ë¼áÿ‰žñ§ÃÅ©Oá_ˆñ‚|M¯k¾Ö&ðÿŠô{Í Y‹Jñ?…õ-ÄÞÔ¤Ó¯îRÇ^ðëº=Ñ‹PÒ5+BÞÞê& ]5ßCñþ kðàçì½ÿÿ‚ºüøðÿAø]ðá߇ÿà›W„¼áÈî„Oû;xîkíBúöþâ÷W×üE­Þ´º§‰|WâGUñ?Šu»›ÍsÄz¾©¬^ÝÞ̾×ý¹ý* “ø¼¦ÿôˆ?ͳ÷®˜Â€>1ÿ‚ˆþÐ:·ì·ûþÓ¿¼/1‹ÇþøOâ;/„°*ç]}OãR/|УÑlÙ.õi|CñwÅ Тӭ&¼“QX#ug ijöW“¿d¯o½~b•í¦ïDüÛµþW¹øYñCá7ˆ~übøû| Ÿþ ûPÿÃÁ8>øWRýšbïŒ>ý‡þh¾?Õ5 cÃ+ûAüý§íðkS×þ!üMµ¹ð7ÂIté¾µÑüWã{+-müEâí4ͬ£{j›jÛ»êü•ï¥÷ëÝîåm•£}­¦šuv¶½ºooøñãçíSû ÿÁ>|Sý±ÿi…|hý³ÿl…?~'x+ö Õ!ø«ñÁ?¿á¥î¼ð·Åß>jÞ±ø¥¨xŒ|=ð¯Ã/ˆiy¼G}eâ¿ ¯ü$ZUÉoHÏ§ï •õi4î¯ýkm- ·”:Þœ›è›MYÛ§ç¿Qÿ´Çˆ>1üñ×íÝû ü?ý½ÿi/ üðÇí™ÿ‡ÁÿuOÞ5×>7þÉ~ýµ¼qâüdømwñ×Å~2¿ñæ³à+Ã~Ðü}áMÆúìšnámP[øˆë3jz¿ˆüBÞŠoGû¾m~Ë犾¯ª×¦—°¯v—jœºnÓ„Ÿ£³Óóz©?|)¯þÊßðW-OöAðgí#ñçâçÀ¿ŒðO Sö†ñÂß¼}ñçÅŸ ~%|6øãà„ÚoÄ øïâwŠüEão øgâö•ã/.·á› ˆ4ë¿h:ޝc:é:n‡ øImÊÓÖîëÓU/¿M¬=Ü—K'÷¶šûµÖìèbÙ‹öjý‡oÿÚûá7ìëðÓÂ?~øköý…jß ¼=㯈úÿŠ>'E៉~ðæñA<1âÿkqèú½Þ­¡*ÇÄÎ5G´¢»ÆOÕ¦•ü¯w~ïä5´—f—Þ¯oËúw°Þ9ðaà¯xKâ&“¡kßüSáoxwÇZŠmí.ü3­x;[ÒnôÏé>#µÔ°¹ÐµêúÏW·¾Òm>kˆîA…ž˜=öê>~ ±°ÿ‚®|Rý’4Ù“À)ð£þ 3ÿÚø÷ðóâ·Ã¯¤"óö¥øçû-Gªø7áGÃïÙƒE½Y~Ãû1ü$»[½7Ç¿õ;YOÄ-RÎãáߢҮ<5â/Æšå]ýçó½½_Wøÿ1¾¯äºö»ôþºÛú5¦@~[ÿÁ[Í÷Ž¿g‡ÿ²f7Š­õÛÇö•øû&_Ýx:KHu›„Þ&ñ,¿¿i»é'½®”Ÿ²ÇÂïvz…Ò#\ ¾†2·×¦†ì¤úÚËÖ^ïà›wò¾»4õik½Ý·´}çùYúùŸÏçü[Iø+ñ³ö€ÿ‚ÁøÎOÿ±·‹õ_>ø_ðŸÇÿ¿à¦µ?ˆ,GÁÝ>ßàŽ¥áï ~Å_³gÂÏ„š‡Äÿ†z>­¬j:†­áŸÚü@ðö­ñŸão‰µãð÷^Ô.gÖ¼=`íi%¿»o²”µõÖúö{[v¯¤ŸW&¿¾Ünºé¾Ë¯[ën×áwƒ?dÚ#ãüVËöù¿ðOŽt=Wþí.¿àMãf±kàïŠmöz‡MÐüqoâI&Ñ|qvÞ›â‰mô?Ésk«øŸÃú}Ôñê͵¥Òûíí*~JËÓn«¢ëb¾Ü¿Áÿ¥]ù½ÿ|·á~ÅÞ1¾ý—¾ |Oñ­¾»ÿºðïüCöÆð?ìåªø×Æ:Œ¿ .¾Ú~Ë0x‹Áßü 㫲ºw‰ÿg ÏÚ·P×| ¢kÖúÞ±á¿i—ˆìõŸjv×:¶µ8þÙéÏNþM©9ÚËmwìïrcw(·¿-K_ªR+óùÞû3öÃþ ±ðÃököÉÿ‚Æ~Íÿ³Öá/~Á_þÍ–Z·Áý:ÇLñwì¹á¿ß>üMÓ?j…ßtoìû:uþ¦ü7OŒ?tû­OOðoˆ.àðUÞ“ é¶–^ÒGkè÷½êÛKnñßÏq­S¿I{¾–Mëå#è¼ û,ßÿÁn~0jz<_ßÙóö…¶ø‘ xnÂÖÆ_ ü0ÿ‚d~Æþ&¹Ñ´ .Ò8- c¦h¯c¤éöÑGšÖöÐFU§ö­Ó–%g¸w|·ÝóOæï§Þ~6|@ý”g-â7ü{ðWþ Aw¥ê_ð”xöðøÏñWCøá¬iVñÿí›ñ¹?gϾ$ð_ÄËÃö?j–zLj^(ƒIðõ寇¼q¯i¾øsymâ=3Ä-áÃmÙëïJï£JWii¶¼±ü/Ô-f–¾ìZÕõ\·×ï~oî?R?àŽ?ðø{ãGü7áWì¿©hš—ìð»ö±ðFû5ÛøG\Ô—Öÿ;Zë 'xüåoM-귳뾻¿ÕoÚ£ö‰ð/ì—û=|Vý£þ'hž5ñÃß„~“Å^2Òþøe¼aâÇðüW¶vZ•íŽö«®l´›{ÖÕõëËËë=;GðýŽ©¬êwvú~Ÿs2?ø/îÔ/ù¥÷»~ºŸ‚g^ðÞ•ÿâýƒ¾(üý°¯?mÝö¼øûcÃâ?x“Äžø¥á¿Ù àü#ºÆ‡Ÿ~œ³ð–‘ð·Â?ü_ᯠüÑüeñDñf·ãïé~!е?xËÅWpj~Oì¿ïmêß“]ßK®‚Z6·Ñ¶÷¶º+ëòôó?¦ºc ( € ( € ( € ( € ( €<ÃǾ|Gñ§Áÿˆ3Ю5Ÿüñ~¿ãß…Wg_ñ%†Ÿá¿x—áÿ‹~êºõχôÍ^ËÃÞ$½>ñ׋t=8ø«JÖãÑ?¶îõ=?Vò¯£<¼ïóWÿ?×tƒ­üšûíËó>;ø»ÿžý€~;|ZñïÆÏŠ×Äž9ø¯'æø·mÄÿŒ¾ø{ñvãá´k.>,üð¯Ä=áÄÛ "/ölÞ9ðF¿"º¤³4ÒÇ©¿à¾KeçaY~¿>¯ÕþzïsåO‰ÿðE¯€¿ÿioØúûQð`ƒöDý¿e/ˆÿþø7Fý iü^ðĽWÇÿ5/‡šÏ…|{áé<»ðç‡|¥øçï6¹ñbâKUÕtÝ:/ÝÚZØÝé‡D»JR;|ïu!ý©7Ö1KäÝÿb×íÿ`ý¾'è_ðOoÙãá×Â_xCö0ý”þ2üBø‰ñáU‡Ä‹ñF§c­|&ñ¶•áWÂ~8ð^§Ä=gÇ6Ÿ5O x¿\ñ.¹ñ'D×/¢Ó®.ïõÝmÚm2ìû-u”¢ßËš÷ù;!}¤ú(É|Ý­oš×õ?H?fÙöuý¼!âO~ÎbðãOkŸ¼q¨_x—Æ^=ñ—޼yâYþÑ­ø·Ç?þ#x‹Å¾>ñ–»}/ÿľ%Õ%¶‡ÖfÞÙVyõ×õêûúûÅñì‰û>xëÁß´×€¼Kàk»¯ ~Ø·×—í§Ùxßâ‰?Äk«¿†~ ø9y»TÑúËN¸¸»µ›PÖu»ÍHë~­[åf¿'¾ýw in—¿Î÷üZ>ÿ‚ƒÁ1´ÏÛËö¶ÿ‚uüDñö•ágöwý”×ö ¿øá´ø“ñSá_Ä”ñ'Äo ü+o‚>"øK­ü%:±oªø+âÃkMkQºox&MÚ+Ý-õ«”:pO§këégúÛ·¯s­üŸ]nÚ§â{džÿgŸþÂß <3ðþ ³û(~Ïzõ_øëÆßìþ.þÓ_þë×>4ñÚÉã-OÆ¿ð ¿j|eñgŠ;ë ÄŸ¼G¥ëZáhZmæ¹¢­‡…Ÿ~íÜ;i¥½=?]|CøYã¯ÛÛYñæ…¦üiý™¿eo‡ß .µ?á%ñÃÛ3âÅiN¨Ï£ÿcxıÂ_û~¿•¦j?lø‰áïì½*öûZ·þÖºÓ Ðõ0cðgÀ/„Ÿ~-üjøéàÿ .‘ñWö‡ãŠÿ¶üI~Þ1_„~¼ðÃÅ:6§¬^ø{A_ø{P¼°Qá'E™¯5Ÿíð·Jyù[åvÿ6þðïæîýl—ä‘ì4P˜|Xø5ðëㆇáÏ |NЮ™¿âMÔxóàÿ´_ˆÿõ]M<9«é^°ðÿ¼9¡ø€øo^:—†5k½2Ñ5½R·ˆCG=þûþŸ¦Í‡gÙßó_©óíÿÒý‹k‰püaøãðR×þ%¸»ø=ªx¯Â~4|&½ñoÂ{ýTëw¿ ¼Â/ˆž°ø‹à‹½QšæçÃ^:´ñ“:³ZËjönöìÁ_'º¾â²û÷óóׯCâßÚ3þ«û:|M‡ö#ø'ðïáo…|+ûüý¥¾3||ø³ð‚Š¿<#z.¾"ü8ñßö+|Ôü)¯ø;û/ãN·áÏÃá ø×áß…<;ggzºÙ%¸Ðï²×YJ-ö²M=û§m?àí'ÑFKÎí¦¿i~Ô?ðF¿€^3ý‘ìÿd_ÙÏᇃ|)ðëÇ¿¶'Á/Úö´ñ÷Äo‹š§‰~/øWFø«£x£ã„þ%ø¹©jž7ø¿âˆ-ð|¾•¡j·Œlî­®nb†ÃÄþH¡º„ÙO¼£dÿíäõòZÿ–¢jî=£+¿šiüÝÿàŸtþË_°7ìûj>×?go…³øKÅáÇþ5ñGĉÿ<âkèðxÂ:%÷¾1xÓÇÞ3O øcEµ¶Ó´/ Úë–þÓ … ¦›»¤bßä?ÕÝù¿>ç¢øçö_øñRøã¬xÃÁ“êZ¯íð7Ký›¾2jÞ+ñ¦qâσ:(ø 4¯ÀÚ/ˆ´õð¼ÖGã7Ä–‹ÅMÅìþ#W—_‘´méGgÛo¾ÿ˜tk£Õùôþ»õ>ÿ‚–ÿÁ2t?Û_àoìEû1øSŸ-ÿgßÙïö¼ýŸþ |NøeâoxßÁZ^¡û0ü/øqñ/ᯈþøRð.Ÿâñ<~ñ†‘cá{Õ¼-i,S­Ï‹ôwŽ)¥VÑ%ÒËäž«[ôÿ‡êV›×Vß«Oõw=ŸÁ¿²‡û xWÆðNïÙ“áOŠ~#|cñükÖ?hÚã_‡¼Uã;M#AÕ,´oÄ?¼Yðßö´ø¥ãK ܰ–>Ñ!ñ/Šõ½"÷KÔ5 ZÛ_}Þ·ÓîWóþ¿3妺ùßñ¾¿Ó©Ù~Ú¿4_ü ý¦ÿcïÙÏöxøÇáo|/ø×/‚oþ)ñd¿ üyáÝOÂþ0²Ð´¿aï„—7÷úŽ‹ª]éñ‹?‰ž¿·[§»°ñ›{ýÿðúÿŸ˜o¹á ÿàŸðIσ?>|Pøiû-ê>ñ¯ÂOøKâ'ÃÝI?hÚ—U°ð÷Œ< â;ø_W×>6ê~ÕOñi«M¦ëZ>£¤ê— *êö7ðÝ]E0õö€ (“×<àx‹Á~/ñ'ƒ|+â|7Ôu_á߉õ¯é:§ˆ|ªø‡Ãú„õýOÁºÍõ¤ú†5 sÂÚ¾«á½^óE¹²¸Ô´NÿH¼’m>òâÞCþ箿‹ûØu¿]ÿ#ȼeû!~É¿¾'Xüløƒû0~Ï;øË¦.˜ºoů|øo≚zè¨bÑ…—Žõ¿ _x¢×û""cÒüQŸ2Zy*ÌÉßÿà•?²çÄßÚ;àÅüýž9Ó¼}áM'S†oøBëÃVž×4];Gðw’EâÝNx5ë(g½±¾:%µ¥)?>dºz«·ßï­ït—¥›wült_´gücà?í­þÅ:.£à_ƒ:oìùû øûÆþ,oÙŸUøàÏü!ñÖƒâƒþ3øi¡ø>ÛÁw-eà i¾Ö¼Oaã]4¯ڮ£¡Ú-Ž—z–ú¥¹}ï$ïéÎÿðáö”»)/¾Úü¬}£ð§àçÂ/´øyðCág߃ž°»¾Ô,¼ð¯Á>ø}á KýNcs©_[xoÂzf“£C{¨Ü“qwšÜ^NL×2I!-@õï‚?4Þ&£ñ@×<ámWEø±¨E h>ŠÿâV™}¥Oeã«Øü-áo xj;¿Cª\&áÍGYŸ£éööçù[åµ½- o§þw½þýO?kßø'GÃÛ3öŸý‹>9|_‡áÇŽ>þÊZgí7§x›à'Å/ƒúÅ_ |^ÿ†ðgü7¤\O/Š5S¡øbçáæ³à{ÚÏwáÉ«M"ÚÚxS)ô×g]ýo×õ·ò·ÞÓ¿áùž¿âïÿ~ø+áçÃØ'Yý“e‡ž_Ç©ø Ų—ˆ|{àÁ±g«i‹ðÿÃÿhOÙŸDðŪÜx¯Sñ2Ïaâ‘âmC]´½€èW:~£&¾Ã[ù[çåòÜÏømà?ÛÆigãÏÇÿÙâ?ÂY`Ö­|eàŸ‡±ÇÅ¿†¾/×íot-NÏN¶Ó#XÑ-µ-Òîµ(5½4Ò¾ ~É¿²ÇìÛ{¯ê_³¿ìÕð à.£â»{+O_üø=ð÷á…ïˆìôÙgŸN³×.¼áÝ}VÓOžêêk[çž 9®n%·Ž7žVp h € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € (ÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/zkperfRW-3.2.jpg0100644 0000000 0000000 00000121734 15051152474 031645 0ustar00rootroot0000000 0000000 ÿØÿàJFIFddÿáwExifMM*bj(1r2‡i¤ÐB>'B>'Adobe Photoshop CS4 Macintosh2009:06:29 18:14:21  ô ^&(.AHHÿØÿàJFIFHHÿí Adobe_CMÿîAdobed€ÿÛ„            ÿÀp "ÿÝ ÿÄ?   3!1AQa"q2‘¡±B#$RÁb34r‚ÑC%’Sðáñcs5¢²ƒ&D“TdE£t6ÒUâeò³„ÃÓuãóF'”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷5!1AQaq"2‘¡±B#ÁRÑð3$bár‚’CScs4ñ%¢²ƒ&5ÂÒD“T£dEU6teâò³„ÃÓuãóF”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö'7GWgw‡—§·ÇÿÚ ?õT’U3ÿizgöx¨Ù±à d þßIÛ›»Úß·g¿þ %6ÒXîÿœž£¶Š¶K¶Ì~ó¶ÿà[ÿöKþî _Ô>±¿¨þÍÁf ²ŒJ22_’l’ûŸ‘NÊÆ8Ùìû·Æ$§}%…ÿfþ/ïÈKþÍü:_ß’Ô– w×€àÐΘA—î¼µ»cs·îþÆÄÿöoáÒþü„”¿ìßÃ¥ýù œï¯#nÖt·Iû¯nÑÝÿEÛÿ¨’›¶¹í8–—:ÄCN×VÉk£óý_Ñÿ‚õ¿Cë&w^­·ú#%â\Ö½•‚ÒæÌ·ví¬Ýù­é?Òú^•Š·ý›øt¿¿!/û7ðé~BJtp:“3]kE6ÒêH´ ݺ6í.ýÅq`½ß^ZÒXΗc´†î½³¯ïí~Ý¿ÔRÿ³üÕÿìÂJwXÛõÒšÝmŸ³60;hÉq©ÚÆ÷ªOëßX˜C>ÑÒ Ûé²reÅíÕ·kïÒ5Ì÷5ŸôÒSÖ$¹:z÷ÖžÚ뿤ºÇ9¬ÚÓðÿ ßUŒØæ¹Ísk·èXŽþ¥õž·šìÉè¬{L9®²ÐAð-%"@Ý1Œ¥òƒ/!oJ’æhê_Z²nôqíé¸I>›¯tí™Ûü§­O«ÝK#ªtšó2XÊï6]U¨’ÍÔ]n#Y°5ûèo÷¤‚$îÿÿÐõT;r1èu¬¨At½Áºw»úíDAÉÄ£) \ €@-sš`–— Õ–»Ý³Þ’˜ž£ÓÁƒ“H:é½½‹š{þõVÛk;ͳë^mŒ!ìwMÂ-sL‚ ÝH´‡ÞVÐú[¸Òwk¨{Ç%¯ìÿÞ®Ÿý‡Åÿ¸¸þ•í¥¡–þeOúLÿ¦±,Í{rëœì-ÏôfßD›Þ]S=õ1­ÿ ;èþsü_ði)lLÓfElû^ÿOú:*;xÙ ·mŸGg¥ƒgüóŸà©ßvKCœ>ÓK`‘‘úU‰šlÈ­ŸkÀéñÿGEGo!¶í³èìô°lÿƒþsü;yÖÚ({+µž¥‡Ò¬.çfï§þ v÷¢;ßÐÒÙ˜r— xðþHzež½æác,œ·½Ö~óÿ;Ûô¿ÁªßStƒÿr³¿öó)hâ±¢Ë×±¬®¶†‹GÓ³ó^³>¤1•ý]ªºÚ[2sZÆ4@k[—”Ö1£÷ZÔe¡ü{üÞ¤c®½e¨×ô¤ÿÿÑô·u ›ÔÓË꽞£]íÚZ$;ó½Onßèö{êÿKZY×åÒÉÆ¤]íq:ê 6{??ÚlüåNÛC~²ÓH±­u˜Åƽ7¹¬sýßÊmo±Ÿ™ÿ]§ù¼d”ãþÒë{£ìkßÁÅ»yýÍîßÿuÿîö?¤ ²õ·#Ô¤·ÔéXoµòcÅÙñO¦O«úMöÅú?¤úu­õÿŠüÿý7aç„’I%)$’IJI$’R’I$”¤’I%0²ª­ae¬mŒ2 \FÓ£¿’TF.0³Ô0Y§¿hÖû£óZŠ’JD11[SX‚×hаm­ÜƒhÚÄ["“ù›‰`ñkGºßûwÒÙÿ©Qr­kZX]µ±º×Í`ø~uŸAŸù‚ Ûcqßk†Û®º™ûwµŒöî÷7ùËôu£Å/§œÿô™a7/êÇSçÿ §Æ;«6óê’ñæ>øX±þ£¹îú¹SžÏIîÉÍ.¬âÒrò·W½žÇìú;Ùì[߇S}W3F0r\cÛ]u³Ýcôþn¶¬©Îú»[žÓ[ŽNis¥¤ååK ~âD‚Jñ" ïõÿÒô_Oü⦷SºãŒ\ËàûZC˜_»Óý&ïæözŸñ¾Ÿg¹Ÿ†ÌìK1^÷VÛ@Ì0á;IŸJñc¾±cƒSMM¤¸Zkipwéüó›êWô¿1ÿŸÿ¯få7Ì—1Ö Ä–²$ÉÛùÅ­þ²Jsõn‡?yÈ·’7»Ÿ»þ¾®_ýÍÉAÀÅe?[2Zóèt¬:€ÜCH7uséoègèÿFí¿¢ý/§üê3¾²cµûMòDû{8×ÝßëéfÜ,ÀÊeß[2\Ö¼zý+Ö¤´wPö¾æþ¶~“ômÝúoÒúÍ$§y$’IJI$’R’I$”¤’I%)$’IJQ±í­…îàv“Ù£ùNQ¿"Œvz—ØÚ™1¹ä4O‡¹fÛ™“Ô-m]=›j‰9V‚ÐÝK7×Gèï±ßI”ïô*úwz–zÐn—ŒeZiz KHÿ(§k˜K²2^Úé©Ócœ@i°¿IÞßKè3þôŸ˜„üœœûXÜ6zt´— ›š@${f¬c²ë6~ûý ÿÑúÉÛƒC^ ÜëŸLM®üÍ!´âT͵Ð÷7Ûº¦zÞŸøMêýLÚ$ˆq@àôXßê§Ñ÷ETGékúR?½%ÇÛÆ#ë™ÖR—ÉÿsôýÏõŸøR*0)¥æ×]áµÙ|Ínݬ¥Ÿðt2ªÖWÔ†ìú»[sƒ2sZò\ãyCu–;Ýcÿ}îúkt˜à°¾¤;ÕÚÞš“šà×´µÂròŽÛ+wº·þûô@­‘)JFämÿÓï2?ó¯Åc b¾ ?¤5î>¦Öëímžæÿ¤ý5Ìä쪭éô·¨;<9Þ«™°´í-û¤³ÕgölاEù–ÓwÙ®±»YxnâÙüí»™ÿV’“¬|üWçÿé» ÿ?u$ŸÒz£¬.A͸íñ«‹ÿÒþï³þ¾ÿûƒö``Ñ}[rï.ô¬6XÈ=þ¶~ÛÝs}=–{wþ“Öý/óu¤§}$’IJI%½ŒisÜÑˉ€’™$ª~ÔÀ&+·×=Å uÑýo³¶Ý©}³%ÿÌáØg‡Ú[[µî}íÿ¶âïËVOj}cÃýÿÕÿÓm¦$I0¤•WÓêvý;kÇiåµ4Øñý[®Û_þÊ _ÓésNk™qÖºí>¡$~ux¬ ¥»Ò2„¬ô1„uœÇ”ÿ¹‡üäÇ©Òý1üÃãHŸþļ³û,³Ôÿƒ@ºìÇH¶æc‰Nˆ{§ÁÙ7·Ó÷¢f/­þ‰nnOÒýV¯Ýl?ö±J¶bÐb¦›-n„s‡òw} ¿©ú4„%-Íyoÿ ÿ/Õ¨LŸæáÃýiþ²d¿Wñ”tàçú¢¸qÓÖ»sßóJã{Ûÿelÿºªàg¤M4’m³Ýe®‚@ú;Λ7eLÚ‰䊛àßs¿Îw±¿æ(ŠØI©ŸC›\L—¦Òãô½¿II# ¯Ïüi,‘áæC ³ÅÄ­/Ý^–4†¹¢+oóc™M¤Ï»r2I&“hˆ­ÍÈë#ûÒRÂú—ÿ 7ÿ gíæRÜüÔv½¿W*kßê½¹9¡Ö\F^Vë63ØÍÿKc=ˆ%ÿÔõT,œŠñ©uöNÆjv‚ãÌhЊ¡m5\Ã]Ìm•˜%ÀÁÜßk¿uÁ%4Ö˜´½àëùŽìæ×áûÖãÿìM 9B±õ³$ÔŽ¿¥áØÆ9í¬ìmýC{¶ZZÿg«^ý­ýÝ8xdÉ¢²I$’ÆòKË»Ã]ÿnÛþ‘c5ŽoÖ¼Êéö5½;5­€õ´ÑZ• 4«­tÿ HÿÜ:lÌv€cW=ÍÅçþÛmuÿçÔ§%ÿO:¶øÀ?}ÏÉoý-"]½ÇÇ}£´ýë­/³Ï,íïcŸåô­’>Ñë?Ãþöi«ibQ÷éK*'7IαçÎïKðÆû;S4t@àêênEƒ‡6·\ÿûwmŽÿ9êË)s~ˆ,þ£ko‡ïnRØóϨ~. ñÿD—·¤ÏþrŽl›{¦»Büß•ÚrüÖ;€ì_þ†æ¦pÌ"lskoõ ìµ»¿öaГ«‡òžç~ 7n­ml>LÿkQá‡]|ÿôûÖ?Oõçôàÿ¼ŠF—ÿ9{íñmeÄÑ6Ùÿ‚"SMtîû-…ßJÇèOõ§uÏþÚ>Çž^~@$½&rFïëü¿½#`>ŸúE cŒýb?îÃ_üãÍßÉ`†ÑÿѶ"d±­­£€uì·ÛÿMM:FJ®Õen­ù9®cÚd9®ËÊsÓû®jÞXS ô“ÿr³¿öó)%?ÿÕõT’I%#Èõ½ =õ¶ŸNz=¼¬ èëµõ•ƒf!Êɦ¬ŠrEº_‘`}vcþ‹ôŸk·ôð?£üõÑ¡œzMã °zÁ»öÎí©)Æÿ³—÷ä%ÿfþ/ïÈ[©$§™ÈÎúÝ}lºÞ™[œ V[’C¤«ßCkßgüé­ôë«ÔVìßÃ¥ýù Vì ;íõ®¥¯²ÝÄNw¨ÆÿžªÝÕì§%Ô¿ !ì6 ª¶¶‡kœâ fÆ7wÓÿŒÿGbJjÙ¿‡Kûò2òþ¸c:ëºU2â@#!åá­}–VÆ·ß»c7înÿø·­טàâÜ,²@'Óo½§¹¾å[ö¿Jêv¶«º}ö¹µYcM´IfŽkê™/§"æ7ôu9k?IüÖÄ”¦»ë³šßÙE®7AKþÍü:_ß­~Ùk)eŸbÉôÞ핊ë ‡9¬wÐw¿ÓÙêoc?á*CgÖ§Ó{¶röTç´èÇ{6nsöúú R¢ú:ž=µÛC…d–>»[£šgiý×µìINN6G×+뚬éwް} K€÷;oòþš/ý›øt¿¿!lÓM ,¥­®%Ä4@“ÉDIN¶ýs¦·ÝiéL®¶—½Ää@kFç;„o«·§·¦Û}wf2Ì‹nôšö°:Ëí¾Æ³Õ—ì­÷ìf÷{ÿœZïc,c«xcÁkšu(uaâÕiºªšË]»sÀÔïw¨ù?ÊzJÿÙÿíºPhotoshop 3.08BIM%8BIMícÿócÿó8BIM&?€8BIM 8BIM8BIMó 8BIM' 8BIMõH/fflff/ff¡™š2Z5-8BIMøpÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè8BIM8BIM8BIM08BIM-8BIM@@8BIM8BIMC^ôresultsô^nullboundsObjcRct1Top longLeftlongBtomlong^RghtlongôslicesVlLsObjcslicesliceIDlonggroupIDlongoriginenum ESliceOrigin autoGeneratedTypeenum ESliceTypeImg boundsObjcRct1Top longLeftlongBtomlong^RghtlongôurlTEXTnullTEXTMsgeTEXTaltTagTEXTcellTextIsHTMLboolcellTextTEXT horzAlignenumESliceHorzAligndefault vertAlignenumESliceVertAligndefault bgColorTypeenumESliceBGColorTypeNone topOutsetlong leftOutsetlong bottomOutsetlong rightOutsetlong8BIM( ?ð8BIM8BIM ] pàÒAÿØÿàJFIFHHÿí Adobe_CMÿîAdobed€ÿÛ„            ÿÀp "ÿÝ ÿÄ?   3!1AQa"q2‘¡±B#$RÁb34r‚ÑC%’Sðáñcs5¢²ƒ&D“TdE£t6ÒUâeò³„ÃÓuãóF'”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷5!1AQaq"2‘¡±B#ÁRÑð3$bár‚’CScs4ñ%¢²ƒ&5ÂÒD“T£dEU6teâò³„ÃÓuãóF”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö'7GWgw‡—§·ÇÿÚ ?õT’U3ÿizgöx¨Ù±à d þßIÛ›»Úß·g¿þ %6ÒXîÿœž£¶Š¶K¶Ì~ó¶ÿà[ÿöKþî _Ô>±¿¨þÍÁf ²ŒJ22_’l’ûŸ‘NÊÆ8Ùìû·Æ$§}%…ÿfþ/ïÈKþÍü:_ß’Ô– w×€àÐΘA—î¼µ»cs·îþÆÄÿöoáÒþü„”¿ìßÃ¥ýù œï¯#nÖt·Iû¯nÑÝÿEÛÿ¨’›¶¹í8–—:ÄCN×VÉk£óý_Ñÿ‚õ¿Cë&w^­·ú#%â\Ö½•‚ÒæÌ·ví¬Ýù­é?Òú^•Š·ý›øt¿¿!/û7ðé~BJtp:“3]kE6ÒêH´ ݺ6í.ýÅq`½ß^ZÒXΗc´†î½³¯ïí~Ý¿ÔRÿ³üÕÿìÂJwXÛõÒšÝmŸ³60;hÉq©ÚÆ÷ªOëßX˜C>ÑÒ Ûé²reÅíÕ·kïÒ5Ì÷5ŸôÒSÖ$¹:z÷ÖžÚ뿤ºÇ9¬ÚÓðÿ ßUŒØæ¹Ísk·èXŽþ¥õž·šìÉè¬{L9®²ÐAð-%"@Ý1Œ¥òƒ/!oJ’æhê_Z²nôqíé¸I>›¯tí™Ûü§­O«ÝK#ªtšó2XÊï6]U¨’ÍÔ]n#Y°5ûèo÷¤‚$îÿÿÐõT;r1èu¬¨At½Áºw»úíDAÉÄ£) \ €@-sš`–— Õ–»Ý³Þ’˜ž£ÓÁƒ“H:é½½‹š{þõVÛk;ͳë^mŒ!ìwMÂ-sL‚ ÝH´‡ÞVÐú[¸Òwk¨{Ç%¯ìÿÞ®Ÿý‡Åÿ¸¸þ•í¥¡–þeOúLÿ¦±,Í{rëœì-ÏôfßD›Þ]S=õ1­ÿ ;èþsü_ði)lLÓfElû^ÿOú:*;xÙ ·mŸGg¥ƒgüóŸà©ßvKCœ>ÓK`‘‘úU‰šlÈ­ŸkÀéñÿGEGo!¶í³èìô°lÿƒþsü;yÖÚ({+µž¥‡Ò¬.çfï§þ v÷¢;ßÐÒÙ˜r— xðþHzež½æác,œ·½Ö~óÿ;Ûô¿ÁªßStƒÿr³¿öó)hâ±¢Ë×±¬®¶†‹GÓ³ó^³>¤1•ý]ªºÚ[2sZÆ4@k[—”Ö1£÷ZÔe¡ü{üÞ¤c®½e¨×ô¤ÿÿÑô·u ›ÔÓË꽞£]íÚZ$;ó½Onßèö{êÿKZY×åÒÉÆ¤]íq:ê 6{??ÚlüåNÛC~²ÓH±­u˜Åƽ7¹¬sýßÊmo±Ÿ™ÿ]§ù¼d”ãþÒë{£ìkßÁÅ»yýÍîßÿuÿîö?¤ ²õ·#Ô¤·ÔéXoµòcÅÙñO¦O«úMöÅú?¤úu­õÿŠüÿý7aç„’I%)$’IJI$’R’I$”¤’I%0²ª­ae¬mŒ2 \FÓ£¿’TF.0³Ô0Y§¿hÖû£óZŠ’JD11[SX‚×hаm­ÜƒhÚÄ["“ù›‰`ñkGºßûwÒÙÿ©Qr­kZX]µ±º×Í`ø~uŸAŸù‚ Ûcqßk†Û®º™ûwµŒöî÷7ùËôu£Å/§œÿô™a7/êÇSçÿ §Æ;«6óê’ñæ>øX±þ£¹îú¹SžÏIîÉÍ.¬âÒrò·W½žÇìú;Ùì[߇S}W3F0r\cÛ]u³Ýcôþn¶¬©Îú»[žÓ[ŽNis¥¤ååK ~âD‚Jñ" ïõÿÒô_Oü⦷SºãŒ\ËàûZC˜_»Óý&ïæözŸñ¾Ÿg¹Ÿ†ÌìK1^÷VÛ@Ì0á;IŸJñc¾±cƒSMM¤¸Zkipwéüó›êWô¿1ÿŸÿ¯få7Ì—1Ö Ä–²$ÉÛùÅ­þ²Jsõn‡?yÈ·’7»Ÿ»þ¾®_ýÍÉAÀÅe?[2Zóèt¬:€ÜCH7uséoègèÿFí¿¢ý/§üê3¾²cµûMòDû{8×ÝßëéfÜ,ÀÊeß[2\Ö¼zý+Ö¤´wPö¾æþ¶~“ômÝúoÒúÍ$§y$’IJI$’R’I$”¤’I%)$’IJQ±í­…îàv“Ù£ùNQ¿"Œvz—ØÚ™1¹ä4O‡¹fÛ™“Ô-m]=›j‰9V‚ÐÝK7×Gèï±ßI”ïô*úwz–zÐn—ŒeZiz KHÿ(§k˜K²2^Úé©Ócœ@i°¿IÞßKè3þôŸ˜„üœœûXÜ6zt´— ›š@${f¬c²ë6~ûý ÿÑúÉÛƒC^ ÜëŸLM®üÍ!´âT͵Ð÷7Ûº¦zÞŸøMêýLÚ$ˆq@àôXßê§Ñ÷ETGékúR?½%ÇÛÆ#ë™ÖR—ÉÿsôýÏõŸøR*0)¥æ×]áµÙ|Ínݬ¥Ÿðt2ªÖWÔ†ìú»[sƒ2sZò\ãyCu–;Ýcÿ}îúkt˜à°¾¤;ÕÚÞš“šà×´µÂròŽÛ+wº·þûô@­‘)JFämÿÓï2?ó¯Åc b¾ ?¤5î>¦Öëímžæÿ¤ý5Ìä쪭éô·¨;<9Þ«™°´í-û¤³ÕgölاEù–ÓwÙ®±»YxnâÙüí»™ÿV’“¬|üWçÿé» ÿ?u$ŸÒz£¬.A͸íñ«‹ÿÒþï³þ¾ÿûƒö``Ñ}[rï.ô¬6XÈ=þ¶~ÛÝs}=–{wþ“Öý/óu¤§}$’IJI%½ŒisÜÑˉ€’™$ª~ÔÀ&+·×=Å uÑýo³¶Ý©}³%ÿÌáØg‡Ú[[µî}íÿ¶âïËVOj}cÃýÿÕÿÓm¦$I0¤•WÓêvý;kÇiåµ4Øñý[®Û_þÊ _ÓésNk™qÖºí>¡$~ux¬ ¥»Ò2„¬ô1„uœÇ”ÿ¹‡üäÇ©Òý1üÃãHŸþļ³û,³Ôÿƒ@ºìÇH¶æc‰Nˆ{§ÁÙ7·Ó÷¢f/­þ‰nnOÒýV¯Ýl?ö±J¶bÐb¦›-n„s‡òw} ¿©ú4„%-Íyoÿ ÿ/Õ¨LŸæáÃýiþ²d¿Wñ”tàçú¢¸qÓÖ»sßóJã{Ûÿelÿºªàg¤M4’m³Ýe®‚@ú;Λ7eLÚ‰䊛àßs¿Îw±¿æ(ŠØI©ŸC›\L—¦Òãô½¿II# ¯Ïüi,‘áæC ³ÅÄ­/Ý^–4†¹¢+oóc™M¤Ï»r2I&“hˆ­ÍÈë#ûÒRÂú—ÿ 7ÿ gíæRÜüÔv½¿W*kßê½¹9¡Ö\F^Vë63ØÍÿKc=ˆ%ÿÔõT,œŠñ©uöNÆjv‚ãÌhЊ¡m5\Ã]Ìm•˜%ÀÁÜßk¿uÁ%4Ö˜´½àëùŽìæ×áûÖãÿìM 9B±õ³$ÔŽ¿¥áØÆ9í¬ìmýC{¶ZZÿg«^ý­ýÝ8xdÉ¢²I$’ÆòKË»Ã]ÿnÛþ‘c5ŽoÖ¼Êéö5½;5­€õ´ÑZ• 4«­tÿ HÿÜ:lÌv€cW=ÍÅçþÛmuÿçÔ§%ÿO:¶øÀ?}ÏÉoý-"]½ÇÇ}£´ýë­/³Ï,íïcŸåô­’>Ñë?Ãþöi«ibQ÷éK*'7IαçÎïKðÆû;S4t@àêênEƒ‡6·\ÿûwmŽÿ9êË)s~ˆ,þ£ko‡ïnRØóϨ~. ñÿD—·¤ÏþrŽl›{¦»Büß•ÚrüÖ;€ì_þ†æ¦pÌ"lskoõ ìµ»¿öaГ«‡òžç~ 7n­ml>LÿkQá‡]|ÿôûÖ?Oõçôàÿ¼ŠF—ÿ9{íñmeÄÑ6Ùÿ‚"SMtîû-…ßJÇèOõ§uÏþÚ>Çž^~@$½&rFïëü¿½#`>ŸúE cŒýb?îÃ_üãÍßÉ`†ÑÿѶ"d±­­£€uì·ÛÿMM:FJ®Õen­ù9®cÚd9®ËÊsÓû®jÞXS ô“ÿr³¿öó)%?ÿÕõT’I%#Èõ½ =õ¶ŸNz=¼¬ èëµõ•ƒf!Êɦ¬ŠrEº_‘`}vcþ‹ôŸk·ôð?£üõÑ¡œzMã °zÁ»öÎí©)Æÿ³—÷ä%ÿfþ/ïÈ[©$§™ÈÎúÝ}lºÞ™[œ V[’C¤«ßCkßgüé­ôë«ÔVìßÃ¥ýù Vì ;íõ®¥¯²ÝÄNw¨ÆÿžªÝÕì§%Ô¿ !ì6 ª¶¶‡kœâ fÆ7wÓÿŒÿGbJjÙ¿‡Kûò2òþ¸c:ëºU2â@#!åá­}–VÆ·ß»c7înÿø·­טàâÜ,²@'Óo½§¹¾å[ö¿Jêv¶«º}ö¹µYcM´IfŽkê™/§"æ7ôu9k?IüÖÄ”¦»ë³šßÙE®7AKþÍü:_ß­~Ùk)eŸbÉôÞ핊ë ‡9¬wÐw¿ÓÙêoc?á*CgÖ§Ó{¶röTç´èÇ{6nsöúú R¢ú:ž=µÛC…d–>»[£šgiý×µìINN6G×+뚬éwް} K€÷;oòþš/ý›øt¿¿!lÓM ,¥­®%Ä4@“ÉDIN¶ýs¦·ÝiéL®¶—½Ää@kFç;„o«·§·¦Û}wf2Ì‹nôšö°:Ëí¾Æ³Õ—ì­÷ìf÷{ÿœZïc,c«xcÁkšu(uaâÕiºªšË]»sÀÔïw¨ù?ÊzJÿÙ8BIM!UAdobe PhotoshopAdobe Photoshop CS48BIMhÿÿß¿ÿçÿÿß¿ÿçÿÿß¿ÿç8BIMÿÿÿá†http://ns.adobe.com/xap/1.0/ ÿâ XICC_PROFILE HLinomntrRGB XYZ Î 1acspMSFTIEC sRGBöÖÓ-HP cprtP3desc„lwtptðbkptrXYZgXYZ,bXYZ@dmndTpdmddĈvuedL†viewÔ$lumiømeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ óQÌXYZ XYZ o¢8õXYZ b™·…ÚXYZ $ „¶ÏdescIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view¤þ_.ÏíÌ \žXYZ L VPWçmeassig CRT curv #(-27;@EJOTY^chmrw|†‹•šŸ¤©®²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿÿîAdobed€ÿÛ„##""     ÿÀ^ô"ÿÝ ÿÄ?   3!1AQa"q2‘¡±B#$RÁb34r‚ÑC%’Sðáñcs5¢²ƒ&D“TdE£t6ÒUâeò³„ÃÓuãóF'”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö7GWgw‡—§·Ç×ç÷5!1AQaq"2‘¡±B#ÁRÑð3$bár‚’CScs4ñ%¢²ƒ&5ÂÒD“T£dEU6teâò³„ÃÓuãóF”¤…´•ÄÔäô¥µÅÕåõVfv†–¦¶ÆÖæö'7GWgw‡—§·ÇÿÚ ?îI$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$,ŒŠq«õ.vÖÈ^ç8ýꦖÙm¶Å$¤©*Ã?ã;(ØEs½Î…›?œe´ÚÖ]U¬ÿCéú©dçââ.y‚èk_a oó—[è2ßF†§·ôI)²’@‡AupB¹4Òí$;cí s‰®ŸKíkjkþ‡Ú)ýó¶‚IIRI$”¤”k±–ÖÛ+p}oÌpÔ9®˜ö©$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$§ÿÐîI$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJTº›i®æoõ1ì0±ž¾Ò[f3ßfUÙ4úú•cþ±þ…]I%<û°®»£õ'ß_¯nG­m u[»Ð¯ŸCÏVì{f?êÿö§ý'é•Ü›Ÿeî¦Ë«º†VÁ[oé*~UŽ¢ÏI¶z?iûe¦»ÓÇýémZi$§Ü[ké˜\ÃutzMÌ­ Ølc*}.ý {ߒ϶ýžë)ý'«UjøÛ¨¼bcÜÊ]‰œÚ«sßu¿³½qñ¶ú˜ÕÞúïû.%Ÿ¥þwôK¦I%<Ûq/oQsì9.ÊÞÛE{†6ÿÑÓûYÙáW‡öÐÝüïóŸª]j58[EΠ›,ÌËmîsI'ÿµ}ïÝÿhmýOÒÿ´ÿͦ[É$§’f9n6n-¿h£–²§ãÙ°ÝQu—}—6ŸKöN[ò¿¥eåþ‡&¯JßÓÓúEÖ¤’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JÿÑîI$”¤’I%1´XêÜ*pcÈö¸á¿õ½õ,oÚ~—GÀ6ÞÚ²3*¬ ­pkZMuÛ•–÷Û²½õÿƒ¯ü.G¤¶Ô)¦º)®Š†Úªhc$Ã=:Û¹þÿ ’œ,K¾ß‹ÓjûK¶¿Ö]mvÿ´Rܶ_‘KýOR·æ]uÔÙþ¯Ó¨Y•“~!Êu®Ì~ŸN[Ç5÷Ø2m·Ö©›>Ñýº}}JÿXþml?¦á¿¬w0ЍheA¯{Öú—­UŒ¿ÓôGoé?MþJs«u•ϤZs´½”Û]NewÓ½ŸÌßêT’œÝöÇÛ½[=_¶z–óéz?hý“èý“ùèÿ¯úÞ—Ú?á}°Ÿh;(ÛcìÏŸ^·<¹ƒ}7õÑc»ôXßc»ì¿ ®ŸøÒ-ÙØhûNÏÒîßôéú‘éý£ì›þËöŸö£ÑõÒ§§aÑqº¶Cõ‰sœÆo;îû6=u¾¯ø_³WW¨’š{¬þÇÃfO£ëï°W³w§ô›m»÷úw¡Yó£«åOþ ÿ¼)}{ÿ’hÿÃ,ÿÏyK©IO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO"ï®9͹¸îè÷6û,¨¹ÂÇô]?cõôçGVÿÊ,Ÿüÿx—AgOij6¬ç×9T4¶»%Ãk\Çþ¯ôÂÙþ YIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO"ß®9ιØíè÷:úÀ/¨9ÆÆô]e?cõôÑçGVÿÊ,Ÿüÿx—A_Oį6ÜæWW´6Ë%ÇsZÆ~ÏôÁWþ YIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO-ÿ::·þQdÿàŸûÄ—üèêßùE“ÿ‚ïêRIO#G×솗ãô{®kN×:·9à8ƒs«Ãw¿Ü‹ÿ::·þQdÿàŸûĺ Ÿ‰[ªÄ¯ÓcÞlp—:^àÖ=ÿ¦}ŸèÕ””òßó£«åOþ ÿ¼IÎŽ­ÿ”Y?ø'þñ.¥$”òßó£«åOþ ÿ¼IÎŽ­ÿ”Y?ø'þñ.¥$”òßó£«åOþ ÿ¼IÎŽ­ÿ”Y?ø'þñ-n«“„Ç^ÓS(©¡ß¥úWØKÿPÅÙm^û+þvÊïþwù¿ç•̬ªñqÍï—0€çXæQK[½Ì¯ßm¿éSÎÿÎŽ­ÿ”Y?ø'þñ(¿ëgS­Ž²Î‰Æ09Î/kZÖûž÷½ØKxõ +i÷Ü^>Ì=Qé·¿ßsq=&o«ô¿jý/­W¤ªÝÕðrqmiªÛñÝŽl¿h ÙCþÓs-Ým6úÛðò(ô©ý?þ‹JrªúÝÔn¬[OE¾ÊÝô^Â÷5ßÔ±˜Jó£«åOþ ÿ¼KZ¼œŸXÂÆ©å”ìmLg¿{ïõ®¯›o·ùÏN§äÛëÙè×Gøe:ú½6=•2«Mî/˜núÝIÇûC/w­ö¡›EßÏúvÕü×ø4”ãÎŽ­ÿ”Y?ø'þñ%ÿ::·þQdÿàŸûÄ·0ú•ynheV²»lª×úv°·Øúl·ÒúÍe}žïø5qïc½Á£Ä˜ô’SËÿÎŽ­ÿ”Y?ø'þñ%ÿ::·þQdÿàŸûĺO´ã¥gùÍÿÉ%öœoô¬ÿ9¿ù$,.á—i}7ÿ::·þQdÿàŸûĘýiê Ièy xŸSÿx—Hrñ@$ÜÀ¤îj©E´·[eL²×zŽôžlcœ÷z¯²·ÿ9údà}DVÌJ1'†G†&UN0úÓÕO 'OøÏýâBÈúåŒÁfGGºšÉÚc[wÌÝnWKÓÁû9±À‡Z÷XàyÎú+ ëÐÿ"³ÊöÔÞ‚ž’‹EÔ×h,hx†õ4 0p+gýKQÒR’I$”ÿÿÒîI$”¤’I%)$’IJI$’R’I$”òß^ÿäš?ðË?óÞRêW-õïþI£ÿ ³ÿ=å.¥%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IMLœl»,.£!µ±ìØúì¯Ög.ý--õ±ý;ÿIþ×§þ3ðœ0+ÃdzÓ5Œk¬knk™Xm~–M6lõYkú_ælÿ„Tzž#rsƒÜq^( »ÒÊg­Ï÷Ýéî«Ð¯ôŸÚÿMéÿ VmɱÝ*«±öã[sjôØâÖmõ=7?Ÿ_ôjô=J±¿Áúé)¯û4‡Ômcžà×RŠÖÚ(e´ãôç_ú¿ô:­þ—ü÷¯þ›ÓVñ:mxÎ|¹¦º\ÀÆÖßÑ»2ûmôñöSúÃú‡ó~—ý¹ê,ï·æ?f#l¸[ºæØðÜq×T1®£ÏZÏÙ/¡ê[f7óžèê«ôÞ“bäu|¶Ïö_øØŸ¬zÍÚ„°Kf_\}¯+wÛ>Ÿ¢¾ìß´¤û7Sf=?¤þüÍi)­õïþI£ÿ ³ÿ=å.¥rß_?äŠ?ðË?óÞRÜéyùÆÌüq‹~â=0w{}»,úOIMÔ’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%9ZZß]øxùUR7\ò,kçù¼j¾Áïèý/Òÿ:¬Þú>Å¿>¶ŠÜÖúµ8zÍvÆýŸg§úÏéßéUúÒÿ£O“e÷c…O6XÒNç9­sq7üÏõÿë5%ŸM·ã:ºÛ]„–’Ëw84ïþvÒãÛþ‡#ô¾¿àÒSQÖô_±1¦¦;q Çôâ^ï[ü˜Ú>Ñ]”ÿ…ý[ôJc©ôšâÆ=°ik÷±Žtc·×ô_kè­Þ–5;2¿þfÏøÕWö^xÙnàël ¤ß{Cj¸c~ö­lûvG§v­úÅáý/ðUXƒÒ[ê1áž´2§1¥çß¿¨dæÿIuÖúVþÑÿOÿm¤¤×ÛÒé®Ì[ZÏM»Kèô÷4›Ýg¤Æã×[ý{ò.®ßÐUê_þBœÎ’ÆÓè´ÃÙKS… u-Ì¡˜ÕÓëÑúWc}¢ŸKþÏæ•Ft|¡ˆÏUí·2»[ip{êm‚ºÝÓh©Ù8íûMªþ›ôUÿKÿJ…Ó,£)™/ i-»Õk_e§Ô¼ôÿKõŒÝÖ]úú[?Vÿú¿ó‰).ý%ùmc.´æÔjõ›>ëë½õTÌê÷¿ùêl¹h,¾Ÿƒ•\ÿNºÇ5µÖûÇ8¹Ž®ê±²›³¦zu3ú.-¶Õú_ø5£e5Û³x'Óp{`–û›ô~ƒ½é ê¦i 7 ¥¤4Ë Üß{η?ùÿú«ü Ø1vlÚí¾Ÿ£ôßü×ÒÙ»ÔúðÿÏÿÂ'T;ËüOýx¤v9‡1ö[ß\þ¯·ßé£ôo­•¨Ý[Uu–þ’Çn?ïqg«î}•~ú8•ddZ^æzä<º·1Ô~‡ºêeŒýó·ú¶ÿJG³–dÐÖµÓê>ÙÞèÞÿuÞÛ=_ûküø?M8œ– ¿SúJ0ãËÿޱo1/ÜÉÉ’&ß\Çׯù¿ñìÿ©¹të‰úï‘Ô 'Øà`‹ædή~×þ‹ÓÝÿ£e{ ?è”ųþ¥¨È8Ñ(ÿ‹gýKQ’R’I$”ÿÿÔîI$”¤’I%)W· êÙU¸õYUB+cØ×1€ Ÿ¡­íÙ_±XI%5Ρ¸ç¯A‡s*ØßM®ûë§o¦Ï{Ñ,ÇǵÌ}µ2ÇTf·9¡Å‡÷©sÿ›DI%"û.7¯öŸIŸh¾¶Öú›s×Ûê¥^.5v¾êêc.³ùËÖµïÿµ­ßb*I)å¾½ÿÉ4á–ç¼¥Ô®[ëßü“Gþgþ{Ê]JJRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI*ù¹ØØ8îÈÉ~Æ4œèý:™ùö$§?ªúÞ³·P§õsêI›}Oµ}“ùeô~Ûúóþ²;r³ÑéÊ­›ò¬ª§¹±0l}¥þ‹='Ùè2Ëmô?Âú~’TwOîȦËMU—\ú‰h¯ÅßÓ¶_öœ_Ñ]ú·ëÍÝú{2ŒK1‹r¡´2¸»ÒìþnÖ_[ª~?§þ’»S›ûW%Ì®¦}¡ÆÝÏÞðÑO¡·Ôélý{ë™›üõß¡ÿIoè”jê}Vö<²†Um8âçUclÞû\ìüvQ[\ìwÓUß³ýj­¶¿æ¬þoôŸ¢°ê:(Äm®µ­Ç..nO®æ—=ߢ{¿j}£íúŒ«Ñþ•þ ÒÿYéxŽie•R_[Ùxª¯íczm{ÿá3,õÂþ“ýJi[Ömq݌ֺ›_UTÙ±ööWgRʹôâþ“"ªpýÑSþÔý?ú9cu£}õãÛ[Ϭ]kë²¶½”NülL‡Ó~Ëÿhz_¤·ôwWüí¾—é,»¥QŒìWl¦š¿JáêÝV÷?fO¯ê2üÒzž¾¯üIPÞ“Ck¾«+kÅv›wïm®ÇûKý{mÚ}KéÃý5ž­ŸÍ×þ%"éùÙ·dzY-ed°¼Ó²Ê­©Í,fÆÛs¬Çêt~—ú^'¥ÿ‚­+X$5Ïòn§ßµQ§¥2÷ýì}Ì®cm6ú-qÞúk¡öÛö*½V5Mt­”zÇýþáâÖ~ÿòÐíËô©}¦·Ñ>èháÿIû¿àÿðZÕ•ŸÔž×ŠñwUúWµ¯eÍ/kÚïð[ûþ‰À‹¾c©L·¨úc¯é+»­u6—}C0×=m›íú^—ýmÚ÷dÖ}7†‰äyú;ý¯ô¶…ÿ‹W®cî°ÝÏý¸ŒH×N’ëûÈôŽˆ×«§÷'ó§\Çׯù¿ñìÿ©¹të˜úõÿ"·þ=Ÿõ7&%èpÿ¢QÿÏú–£ áÿD£þ-Ÿõ-FIJI$’SÿÕîI$”¤’I%)$쾊êõ챬¤MŽpk ýz®ö$¤‰!Y—‹UMºË«eO–9Ík»ù¿NÇ;c÷§³#§1–ÚÊÝiŠÚç—ŸÝ¥¯þq%$I í8Þ¿ÙýVzñ>–áêGïz?Î$Ìœk-}5ÚÇÛ_Ó­®{?ãko¾´”ó^ÿäš?ðË?óÞRêW-õïþI£ÿ ³ÿ=å.¥%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)UêtÙMË¢¡ºÛi±Œl/{]mÜÿgÓV’INoS®ëÚqÝŒüŒk5ªÑKÛd¿úO©v6ü]žŸýÉÿ„ŵK&Œ—ôÊ鵇*ð*õ}7 œë+5ÚüœgÝúQ™zõUw¥U‹A$”ó㨂ËÍv®»ômû0É-¼c~±“ê7öO¯êÓ•êýŸþÓßÿR.FôÛm70ŠmÅeç6Ç Öuòñ½J«Çþgí˜ÿöžª¿›ôÿš[i$§ŸýŸÔ,ÆfFCIÌm̲ÆVk/}xõ¿ŸEù^¦'óïý§úÇúOðV6lmyµäÙ[ƒHÈ{½GTç×eǦ¶¯f)¢¯Z¼L›?WõÿÂ~±úe´’Jqúf&E9-¥ÕQMN®¿QÕXY.«Ó§#nSðý:ÿKûCôÿÌ-g³{cqo›yRI$£ÕØ%Ö9¡‚I‘ÀíþJ­P{¯c¥ÿ¤ÍÁ±^Éý=¶}?SôŸùñÂl´RÒæ†ÃÞà4"˜Ýÿ£HʺͣÚXxyvàßÒ½ÆþoÊl³þ2Ä¢I$þŽ(Ÿü2^†.Êw¯ ?­?ç?ô¾‘™õÄDˆãnÿ¢ƒŽÒçÛnâC ƒ¡k =ÏÔç“?ÍýôÔWéÒÖw_‰÷=>Ï ñô§‚<@WËsùçóKÑòÿá‰1õëþEoü{?ên]:æ>½È­ÿgýMÉŒ¯C‡ýø¶Ôµú%ñlÿ©j2JRI$’ŸÿÖîgí{ ­héùNuêöwm%¾¶Ïfé,ôŸ¿Ò«ô‹MaäS™vvKp¦  ÿ§úÎ5×¶æUû7©Û_è=¢¾¬Ïµ¢ûWSv5ߤþ‘üòèIO-õòdQý¥Ÿùï)nt³ÔÎ1=M¬nF㯣³Û³óž°þ½ÿÉ4á–ç¼¥Ô¤¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥(]a®²æÎüÖþó¿uMW¬ ¬õŒ3ù’'¿¶ÛdNÃæ—òãY2tˆùçÿ7÷²$ª±S“$—8“:”,®£Õik½g:ÍÌa«vãú7[[ÿIëz^Ÿ©ê)å<¶¢Ö:ÇûZ+-âÚŸ}^·³õSÕEcKXÖ—€7;é:?=ê@8aCôýÀ¢ƒ$‡:ªŽ¡î’5á¾ïÍØ¬*Ì%ùoqÚÛµ¦#ùOVR–” Ú&¤¤Mj‚hYèŠç—8PÇCÝîqÑX>ÿûq±°5¢õ*±Íis‰.yÜAüÙÿßø´´e\ksA¢“1ìpßk}œkqíö³Ò© q²e/OýÆ5±æ?4¶¹ñÙê¸eZßs„Ò×°2ÚàÏV‡?uŸÎ=ŠËÞÇ<ðÐOÜWË2ÆÖ9yòá¾ÿ¢íÿëþIóHvÿ£ãÇûÒÿXmsj%ѹę¯ýµüµaF¶–±­'qI=Ê’l’VÆøEüÒõKûóõÉK˜úõÿ"·þ=Ÿõ7.qß^² ±ŒÝZÇ6˃ê3Ÿæ}¡4šdŒL®¿F&rÿêðÿ¢QÿÏú–£ áÿD£þ-Ÿõ-FEj’I$”ÿÿÐî+ÖnÈ£:dz¼á½¼º]S-¢ìO°[ŠÖ[‘e[ý^¥ú·ø?ç?Ò.©Q·¦5ÙÈ®ç×cåÍk«eÆ¿°}µµ¾½þ¿Ù?Aüï£ÿ’“à´· ®³Ö-©€Ú ‹kO»þùÄt£`ÙíØÍ¬®„”ÝI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)=k¤‰®£,p:9þæ¿ÚßôJWØáÖ@µÿF|óL÷Ó‹SD¶¶’X&{ÎÚ«üÿç,M®)px~oïþ„?—ú¶3ê•~„=SþÿècÿÔŸøS‹]½¸õë?ÝÛé5ÌfOéiúì³ô?ðˆ´ÔÊjeU‚X h$¸Àþ]›ž‡‹SØÂû$[i±»Ýcý¬cëÇõ?ÀûÔ’5é›úÓdR©fÛr6£`@<Ÿçìüßä×þÿRÛq iqà ?%S´’÷èæ‚÷ºê{ÿÂíÿÏô5£—dOä#ü죇_ëúóãò¶Ô.ºª+6\ñ[.q€©;¨¾÷ú{=w {½¸ìÿ®ÿÚŸýR§§7Ôå¼å^8.Òºÿð¶7óuÿçÕüºÿ[ôYý¡ržõQþ~_ú«þ«ÿ…£ûFnn˜­û>9ÿµÒ8ÝLWçÜûma}pë¡û79fËw=îÛwºÇ®½q_]éêb“k¯i鿯cíÛf×þ“Õô÷ÿ¤ÿˆO¨­–K1Ö?ÝéÿµÉþQëðÿ¢QÿÏú–£ áÿD£þ-Ÿõ-FEI$’JÿÑî.O·6ß³Ý’×ØàÛ@Æ5úÍ§í «õú,³íV`ÑWþËþ‘m.kª`Œ¼ÛÝP²±FÓ“?em§Óþq´ý›+êw}—í¶öŸÕýJz kn5V1æÖ=slt=®k\Û^ÚÛU¤ÿ‹­ ÔìJK tšØkaXÍ­ô«sÄd”¤’I%)$’IJI$’SË}{ÿ’hÿÃ,ÿÏyK©\·×¿ù&ü2Ïü÷”º””¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJLç´¹Ú‰?$è›m ÷W.þ… |Çå[#COš^˜ªž‘a€ïswßN¸úÿÑŠ4_iÈ$Š€-¨5áõ][½+™›±­þsü_¤JÒû­mU—5;¬µ…Žl°·~ÕÙ¾ÏÓVådÐÑ 4øŽÖó’¢(WòâRJ•ÝN–¼ÓŽÓ•xæºõ ÿ¿ùšWãçf<3&ÝŒåôS£ßÝÉÊþrû?àjÿщ€Ù¡¯ý¶#„ï20Ãæõüü?ÔÅü±³Êê-²ÓŠÇd¼8+Ñ¿ñvdÿ7Wü'þŒý-iëÁ³,z™Î–N˜ÌöUíÿOþ'þ¹ÿm+¢ªñ±ý:†4@—B+D4È ÄX×åý¬–`&#ˆqÅ~°ÿ;9äý.?ò_õ/ü1MkXÐÖ€ÖN’H,Ræ>½È­ÿgýM˧\Çׯù¿ñìÿ©¹%=ôJ?âÙÿRÔd?è”ųþ¥¨É)I$’JÿÒîfk:±öeÑM×7k^M"ûeáΡ›k¦ûßú*¿íŸÒ6´×9“o§““’ú2qË!ÆÌ1Km}UàÕÔvååÓúo³þ«Wé­ÇÿПZª’¼L|*k݇UuWhššÖ5àeŸ¡kw« ¾~–=[=?N¦7fïSfÖµžŸ®ßç¶¥ÿ¬$¥$’ùc×êZa²€\âãíc+ª¦¾Ûlÿ‹IITÝÕ0[C/6]…Á°Ç¹òÍÞ¾üzëûE_gÙúÇ«Wêÿá”îêtº¶Ùdz 9¤=»Iku¶T××E;ßüõþI)²’­ûCífßú]Û>‹¶z‘êýŸí[>ËöOþÓúÞ²Tõ K®4Öùº%®k_°ì»ì÷ØÆÑ“é?ùß³ÙjJyÿ¯òMøeŸùï)u+–ú÷ÿ$Ñÿ†Yÿžò—R’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’D€$è))Ö6ÇhÆ“ŠÇ¡P®¦¸¾ÇFæ€òÇYÿj­õ]üÕOTßÔè9í*Ð&šiiul~“ÖÿEêêµ pr³_ö¬ç ƒÛ ª k³Ñqõ[‹™“üÿüeUúI° ““æáôãòýÿý&¨b$Œ“ýV9Õq|þ×ùÌXÿ×á_ͧûeÿ ¤}«,Áµ´´ë#cò2œÏÐcúŸð‰}“3/\Û=:ý¦¤‘?øg/ùÛ?ë>’»MãÖ+¥‚¶Í•¶ŠÀn{´kG%2>£wú#åfLQ©ÉêËþùÜO´çceÓgÙ?Wþüå^¥))ÜÁ­•acÕ[ýVWSÛ?}­kXËëˆê¾¥¸Xâ‰ôELIú{[én}{™üÚ°’”¨uZEŒ¡ïõMtÛ¾ÏG«´×‘ìû'ë_ÎdWýôÊúI)ìäÐÜLœŠí¸2¼Š¶íu·m¶Ê.é¿j­ž¥¾¯Øñ= ‹ÁÝoéÐ,ÅÉ£⺷ÙfGO§ŽcKÚËë5[ëZÍÿgþ™]Þµ¾«ÿ8º4’S‡¶èû£g«öÏ_ÕÚïKÑûOío[íŸÑÿ£þ£èzßhÿ‚ô’ÂmÄtìSMŒ³~ÑcšæÖvS~è²ú,¯µÝ}y_«Ùwü?éâI)å¾¾Éá–ç¼¥¹Òðòpñ½œ§fÙ¸»ÕxÚ`íýó—¬?¯òMøeŸùï)u))I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥$‘ :¨ÙÕqCtîÉ´~e#ÔëÙüÍöâºèÂRùA—òý&ò–ÕS í{k`åÎ!£þ’£[#»0«ÿ·îÿÞZÿðeaa‹¶»6é#Õ°ú¡„ÎÿWýn´Ó?ñ¿ïWã‡ó“þoë§þ?ó_ø_ºÌõVØØé~@ÙüÕÿB¯ÿÑ^¢ kÎÌxÛ¶’$¶€}2æ}±îõ­ÿ­Ö¯ŠIù´X4ªï1ßO؇m‚ïÕ¨‡4†ú¦]Xô-7ÔÃÈ¥»w³ü‰FŸÍqÇú_ÝXrX¨À`„¿õ¼ÔÿÃþoýG¸ŠŒ|w?eUµ¸Ô;ˆ}v}¦³üîÿgÚ(ôßÿ ê-ÌhcÁ$4 2týç¹'9¬isˆkF¤ž¤ÝòÇÓ«Rlú¥%¬±µ·s¾w'÷¡Un“m¿Î;·fÍ­ª5µÖ?Ö°þ‡M£÷¬ÿ„úÿ5ê"XòÆÈis‰€‰KoHùÍÿz©ÈB'ÿ—ýÄV°—ÍLv×w#–‚ˆ‹'ó«‰R@žŽ16e/ž_óaúúñI$’ Ô’I$¥.+ë¾Si9‡-ÎÆuŒkq#Ø×méýOSùè?Â.Õs^¿äVÿdzþ¦ä”ô8Ñ(ÿ‹gýKQpÿ¢QÿÏú–£$¥$’I)ÿÔî[/ÈËÊãºæÓµ®?nÉÆq®«ýac×öZÿEmðVÿÆúËus]W# í¶²û+ÆÚEV¾ÚÍ4Y›NMÕbåb3+ÕýKѲ«½OÓWþ%;=.Ç[ƒM†¦S[˜ÓK+{¬h¤µŽ£Ým{?â½?ú⸅g«‡E»=/R¶;ÓÿG¹­£ÿZGIJI$’R’I$”¤’I%<·×¿ù&ü2Ïü÷”º•Ë}{ÿ’hÿÃ,ÿÏyK©IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$;²(¡»î±µ·ÅÄ’&€â'²D–íoÓ öƒÅ¶~†Ÿën·ôÖÿÖéKìY—ÿLÉ!§ü?è™ý«ÿ¤Ùÿ¦ñv_ôY=ªþrQÃýSëÍÿ…CÿRûIò:†&9ÛmƒÔí[}öúÍ{¬@ûOQÈÓPÃþ û¿³‡Oé?íë+V±ðñq„QSkžHãý{?œz—2½ —A!ƒWýÆ$oyáÿ|ƒ“ˆñÿ¬Ïÿªaÿ¯š_²ÛiÝ›sòîéÓÿ°´ÿèßUYßE ôé`%°=*ÀÒß ˜‹­ãéVà -Ýý „Û‹ØN aÛÙ½—¿Jœà}Jç7õ¯Sþ´”c)|£‚=rÍŠy²äÒøqôýü+c]—\ý­k·40–ûGúw¡Œ[· ±h-kØáì¡Í{¶?fSc=ZÙú_Mbƒg©kÝcšóeRvŠ¥¾¤ßGÓõkþsùÿQÐÑhÐñG_ç'ÞH_ÒýãóµÆ3œý×XlÚç–4{²ÁéúÔ×zy^Ÿü*;Ö41€5Öù­jt’2'uÊUÛúÃ…‡ù–™`ýóþ›ýíÏç*I߬8°2Ó?¾ÑÿSýÑÚ¬#òÿþ‚ÿ”\ÿÌQ &mqq{ûýEª>Û΢XÇi?œæÿßÛN¬Öx¿ÉÇù½œ—ïÿêµ$’H2)$’IJI$’R—1õëþEoü{?ên]:æ>½È­ÿgýMÉ)èpÿ¢QÿÏú–£ áÿD£þ-Ÿõ-FIJI$’SÿÕî7V=½Aø/É´=ÙXîÊ4Ð÷ÐZqÌ\+2q¯ý_ü¥þ—ßúç¿HºEGö7Gÿ¸8ßöÍúM%7+aemaqyh½Ñ¹Ð?œ³cXÍïRLÆ2¶6ºÚÆÖµ£kZÖûXÆ1©ÒR”.ºº*}Öµ°8ù5O?#$ÓèÚÊÅOÞæYYµ–9¿ÌomyÌYúoç?ôÿѤ¦.ê”ýŸöUe¿j¯Õ®¶úžžÖße–z–×Wè™máâ}T®ê¸õl!¯±ŽcnuŒmT¼í«&ÿQõ¿Óü­oóŸ£Tqqz–&6†¡}øøîÇ}LÛYg¨Ü7W½÷ä:›¾Ïn¥‘uW~“ùÚ1ÿÁ¨ÙÓrêÆ8•3ÕáU„ûšÑQ¤_S²,õ][ßW¥oó>µŸ¡þi%:?´ªõý/NÏOÔô~Ñ ô}oûüçÚ>Ÿè}_³ýŸ×ý­ê%GRªûZÁ]Œe»½\éݳùÏCe·þ¿´SG­Wé)U>Í™ý ÑýÚ¾ÓöÍÙéúÿ¶}/KÚ~ÓöÕ¿£ú—é=t°ñ³Øq­§Ó¯§†ÜÒÛ¶UwMÆû;+{¯÷Ó“ë_öŠqÿàýD”ç}{ÿ’hÿÃ,ÿÏyK©\·×ÍzEþì³ÿ=å-ΗӛӱŽ;m}฿}§s½Û}¿ôSu$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJIFË+­»¬xc|\CGý%Lõl2vÒ]’ï Zë?ðFþ‡ÿ@7+ãŽrùc)xéo$¨}£©ÛüÎ3i‡^ýöÔÿÏ©¾Å›oôŒÇû”4R?íïÓ^‡`OüÕÞÐ<ñÃü/z_øÇ¸Þ²Úªnë^Ö7ÅÄ4ÓTU¡Çn+–ÿø!ì×ɳe Uôœ;y¨Zÿß´›]ÿƒúŠèà½^ÿœ«Â6ÍýÿÔCüX{¿úSŸéõLç,ngó*ý%±ü¬›DÏúÕ(´ôÜJŸêlõmïm¤ÛgùöýúÚ¶™Îk\âÑÉ:¸Fç_ï æ•+ ?wêÿÇŸÏ“þªºg=ŒÎ “{’«]—µ¤‚*næ·Õ·ÚÇo;[èÿ¥Ckn°ŸM®l›«wÓ©íýbãmôí¡ÿñ•~Å/”zÎOÓÿC`ã'åýy|‰MÏ~Ó¥¸í›=¯q>Ú½ÿ/ýkA­Ï{?Ua’džäÜæØÇlevÑg¥’ú¬«b°ÌZä¾ÒmyØI~­ßXÚËh¡ß¢Æÿ¬£§Æ&ÿÉûÓù?ÁÆ¡ lž)~÷ýêˆÇ?}ÄÚàñcµeV5¾ŽìV‚GI$‰'uêI Ù‡m½ÚhÑ»’æû¶ÿQê#í6A1HÐÇÓwò›þýlG„õôÿyw ëèþòg½Œ÷ƒ¦¨ï¼Ö +ta–§ý ~ŸúÿÖ”Ù[tn~ž÷{§·þÿb*VÞ¯›ˆÛÔz_÷‹5­cCZ!­à 7¸†29÷“û©ì°2Ëža¡=uŠÁRL’y$¥â~Œ½È­ÿgýM˧\O×~˜ÖÒz­asìe~‰?¢×ûÚÏßýJ{ ?è”ųþ¥¨È8Ñ(ÿ‹gýKQ’R’I$”ÿÿÖîI$”¤’I%)$’IJI$’R’I$”òß^ÿäš?ðË?óÞRêW-õïþI£ÿ ³ÿ=å.¥%)$’IJI$’R’I$”¤’HÑ.0$¤¥$ªÙÔº}_O"°|?æ3Þ…û[ÚS]×ÿÅÔøÿ>ÆÔÄ8£Ü2 9N¢áýîGøÍô•¶ç¿ù¬ØÚöWÿAŸhz[zÅŸIôcäµ×;ÿu¡ÅØHýì‘óK?ꑟüÌ>îFú–×X›ÖÑÿIRýkõ¿2û'Ò)où¸Ìgþ|S¯¤ôæÞƒ^ïÞ²mwùÙªW.ßãpâÎSÿeOøù}¯ý$ÅÝ_§‚C-õ\?6 ëOþצû~KÌQ…k¼ì- à®u¿ø¼Ö5ƒk@hðtª]ÿÅ âÄ6Æeþ×'þ©öÖ,ïF8?Ö¹ãÿmêKö~CÿŸÍ¹ÞUí¡¿ø }Oü_I.ÖÏÕ^ô‡Ê1ãþä!Åÿ†Oõ­*úOOc·ú"Çþõ„ÚöaÖ+ € QuŒlîp$ÉV81¼´÷{{Ø•Â=c)濞|_í&•$ÓX÷ÒàCLi»ÛïÛïö¨œ— «Ø \â^æ·nÝßI!0võtq¬÷#Ðñv2›a.ŸaÖÁa m…µ‡?s-;(³ßÐãÛþ–ÏÒÿ6§6çYÁŒcÜ×4}ªs6²ÆlèŸê?ü/¨RýÓöŸ«ÿ›üâ¸Éùc#ýïB[óª­¤ƒ }C­@¹Þ{Ÿ_ü*¯&ç’ñµ²æ½È­ÿgýMÉ)èpÿ¢QÿÏú–£ áÿD£þ-Ÿõ-FIJI$’Sÿ×îI$”¤’I%)$•|»î«ÓmúÖZí zu°¾×[‘{kÈôÙú?ô6~‘%6YŸµ-²ª½ =KíõI¨¿hhÅwÙ³6ÞÊîõXôêÇýé½_ðI­ë¬mÔÕêÒ(fUÏ.Øk¢ÝÞ“ë¯e¾½»)È·Óõ(þcùÔ”ê$³ÿi?ÕÝèþ©ë}Ÿ×ßïõw}“wÙ½?èÿnýOÔûO«êÿ€K©>çÒ_NÌ|¹û5»÷9ð×dWëÑé³ÐûF-VdQéÝ‘ÿ 餧ëßü“Gþgþ{Ê]Jå¾½ÿÉ4á–ç¼¥²ÞŸ¼IÍȰwÚö´ì½l@“ÐZøÆ&ø¥Áþ  ûj¯[Öåßú¥OöF!þpÛiþ]¶ý§_Jéµ™n; ñpÞðmèz»µuaýì’ÿ©ÇÿV¬î¯ÓZc×kσ&ßý·mª?µ73‹‘gÙé·üü—R¯1Œ`†44x Ò©wû¸± ¡9´ÉÿªáÿJ4>ÑÕ_ô1W¶Ïý jíKÒêï>슩Ö^Îȱ_J@å.æ_ôè+ݯ–¡þ»ÿ»óCöu¯þ{2÷Ÿä¹´ý—­‰ÇGéÓ.«Ô>69Öçç½]ÜߘÙXåÃÑÊUµþ>ñ“¤ý¿ö©þ4Ƽlz¿š©•ÿU¡¿õŠ&ÚÄ’öˆÐÉÆÚ„ËÚ6èuY¨c3S+þô™¤ o¤LØÑ¶&\4Ÿ£¹ÜüZ·ð\ͲÑÏé YCsô¾§èÒâÖ™Äo(ý­”•!œl~Ê›Î5µîs[k©uWúmýÏøÏÒZ¡êfZÂá]úKšÇÑ´³n;ú\Ь¿þÛª´j]#/ðÿSÿ¥¿¸Ž?ÝŒ¥ÿ3þ›}Ïc/ph’L!–‡XCC†Ñ¡èþ‘Ûkü䑹Îk*i>˜~ëX6ý®»¾‡¿ÙúÒÆ£6›÷µÎ¼×¹ÁkZÒÇ µcÙôÿ˜ÿIú4¸eÖP‡—ëd¯YýØyz×ݒ醶°Z ¸îpwõÿ¥ßé‰õò9 c›¸VÝÏúËõoÿúD†›-/¸zf—]½Ž;ßêýzŸð¿Î# zI¶]·q.ôý´ïwüø4¸!ÖSÉÿ6*࿘ÊÞ?÷k²Ì"ðÚÙê;s©. /Úê‡é*¾çÿ7ÿ\N,̲¿ÐÒÚ7W¹†Ó;-Ÿè÷ããþfÏôw«i"8Ëþò‚D@Øÿu¢×¼—\àÝí{ÈnÐÁï¡ïÛújnzLÃÆfÒ ayit½ÍõNü·óˆÅÍ>(G+³Ã¿«îìë?3ù Ndià/'`J`4€’ ½ÇèT÷|@hüßôŸ×Hý¥úiS|~“ùÿ¶ÿ›ÿÏŸðhpž´ÂzÔ|ÏýÊW=¬âlÞ×ßù4(u?â±ñHü±éódý_üßçWuÌ4K‰ÓÚ M7@­ŽI—L~â h4I+¾ÔðÈï/¦?B1QÜKž\ á£üÕ&VÆcCG’’IYH„F z¿xú§êþº’I$)$’IJI$’R’I$”¤’I%)$’IJ\Çׯù¿ñìÿ©¹të—úôæþÇkdnõ™¤ëônIOE‡ýø¶Ôµú%ñlÿ©j2JRI$’ŸÿÐîI$”¤’I%)QêTæ^ÊëÆÚYºok¬~;žÈöÒÌœz2­¯ôŸÏçÕy$”æ;1­Ç·ºiº†YO£½Æ–×i©Ûª½¸Ìè¾Çg§öJÿÂÕê†A·¤^ÚF6;˜i·¼+œòZæ×OªÆßCXË}[=,¼ŸÑú”ƒý*ÙI%9_`ËÝöy¯ìhûNùw­>¯íO²úŸ¥ý?þÔý«ùŸÑ},\ ºÝ‹U¦¿³`O¢æ—,ý˜8Þ½O­ŒÇô1/³ü>O¯gú%ª’Jyo¯qû&‰ãí,ÿÏyKc§tšúuÇ2£.ˆïvÝöï³ÔýÅõïþI£ÿ ³ÿ=å.ˆãØ_¸ÞøõhÚÐéý•ÞÏ}þsý"\ îxÇÿ¸[(ƒ½ÿƒ)A³ÏÒ²ÃíÚ}Ñý¿gç¥öjt™0ÒÙ.wÑ;¿•üµˆÐ ¶»`xÕçÝêŸð»6úžý¦ÿ@˜ôüG3cÙ½¾›i;‹50î­ÜïüùĽ¼]uÿþš=¸þìO÷½Jô°Ú[$Ãé¶^~“ýÞ—¹ÿοzN°×eâ°ÒçkWô¦û74¬qšâòÆ8‡¹Ä4öeÎwúJÙþ?«Cd´G ã·þ®Ä†<}!ÅôŠF(ô„Äi‹0ƒú©}Œ×µŽ˜yÛìõv~±ìþcùÕ-Œk¶WˆH®ÆÃ‰k[ï õréÞÿٿ„ÿD¬}ªŽÎ.þ¨.çî·þ /´ˆ‘]‡û'Á¯üý¿¾íæ!þòÆŸ`›ˆÿ…¥îsGÙ«kw¼º]'OèÙ Ù_ý¨ÿÀS}›%Õ[CæI!…àdÿ¦ý'§êÑÿ‚«>¥¤À¨ÄòHnÙ»üÏÒ¨îË#JØÓà\Og~åé=4¸cÒcþ/ýÚ½˜öÇÿ¢vœç“`ïcàVÏÌþysšÿSí?öí_àìR5osÞòë$mvýÍômkÏVŒ}ÿ«úŸÍ¢FIIøs÷ÿŸÒ¸‘7`5£»ü¯ø´¨Wù1åý>Ü?¨…½33a¯x5šŽòçî¬ûlÞïÒÆÿ:ŽÜz;kh'l˜}?æ7;þüˆÇ1­¶"dÍôÿÁµŸñŸñ‰þÍIúMÝ>$žíþ‹Džò‘O _ñb½ƒ—óCvM ^8=ÚG«ùŸðiÆ6;x­¿·éE0ÖŽ=?Ö?óSéþ´¿æߢûMs9Ä0×~÷¥û© ÉÚž~#oïþÿü_þŒ›{f$O„¥§Aø Ê§øÒDlÉ)æñàßÝk¿?ÿ=§i'PÀÙó&7cüþ¥ëTIÀ‘¡Êa}E¥Í$à :þïý%¾ö>ø‡ø_»þO u±ãµ£Áß¿¿óÒû3N{Þ< £¿èö~çþ|Rõ¥»šÇ(ƒÿM':èX$ó¸ñ§ò7%rðø°W¾7·ê±ÿê¸)¸ô4È`Ÿ$óê~wò܈à´¸C€häD“¯þA7¦íÒlqÒ @}$»É$èÎZþ‘øß:EXÆ‚IãÃ_ú”–jKŒ™$ÿ);+c1¡£È!§Š?XzBõ÷?»þm¨÷6kd“Æïoï$Yc¢_¶5!½ÿ´äD’¾Á\óJRÛý\æ0V»h.ãqÔ©¤’¸D €÷T’I$•$’I)I$’JRI$’”’I$¥$’I)I$’JRI$’”’I$¥./ëÇKÅn9ê`;í6XÊÜgÛ·kÿÁÿÖ—h¹¯_ò+ãÙÿSrJz?è”ųþ¥¨È8Ñ(ÿ‹gýKQ’R’I$”ÿÿÑîI$”¤’I%)$’IJI$’R’I$”òß^ÿäš?ðË?óÞRèŽ5 jçã½Þ«÷—;õìÇI ÿÝ–ç¼¥Óz5m Ø Güí8êG’ ¦>Jÿ…£j^á3óßgòÿ‘¯8:\xiqì^÷ÜŒk¬òѧû[3|Qâ=ä®<ß¼?çÿ„× ÁfÖ»Ûµ¬üïø6»q hÜÓ¬4ó?ÕFI+ýïñ‘yNó‰ÿ/þŒ\×C\cù%/WÛ»ÓÂ5çúȉ!c²8gÖ}?FÍ–mµ’Oi?é'/¶@È<™)¤•ŽÁ\ýùÿã_ú­Ý»F·ls=ÿÍN=mÆvìíÌ©¤•øpÞ™ÖÿEEúî-ò€•ûÎH2í°l÷x†äþöôD’¿/±^Øï?ü2hÍN-ØéÓQ ?œ“©k£qq2?w÷¨ˆ’\E^Ü6#‹ûþ¿—ûì 5ˇÿœœUX2'‰*I%gº}¸~ì{åPp!$’Ar’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)s^¿äVÿdzþ¦åÓ®cë×üŠßøöÔÜ’ž‡ú%ñlÿ©j2ôJ?âÙÿRÔd”¤’I%?ÿÒîI$”¤’I%)$’IJI$’R–~7R}Ϥ¾˜ùsökwîsá®È¯×£Óg¡öŒZ¬È£Ó»#þÓZ +.·bÕi¯ìØè¹¥ÆË?Gf7¯Sëc1ý Kìÿ“ëÙþ‰%9_4éû²Ïü÷”·?ltŸû›ÿn×ÿ¥spq3è8ùu‹j$¦D9¿Eì²½–Wÿ[YóCêïýÄÿÁ.ÿÞ„”ßý¯Òîn?ý»_þ”Kö¿Iÿ¹¸ÿöíúQPÿšWî'þ wþô%ÿ4>®ÿÜOüïýèIMÿÚý'þæãÿÛµÿéD¿kôŸû›ÿn×ÿ¥ù¡õwþâà—ïB_óCêïýÄÿÁ.ÿÞ„”ßý¯Òîn?ý»_þ”Kö¿Iÿ¹¸ÿöíúQPÿšWî'þ wþô%ÿ4>®ÿÜOüïýèIMÿÚý'þæãÿÛµÿéD¿kôŸû›ÿn×ÿ¥ù¡õwþâà—ïB_óCêïýÄÿÁ.ÿÞ„”«¾³`×Õ(Âkê}75Î~P¹¾E¢×6«>•¤ô¿Ó×üê¿û_¤ÿÜÜûv¿ý(¨Í«¿÷ÿ»ÿzÿšWî'þ wþô$¦ÿí~“ÿsqÿíÚÿô¢_µúOýÍÇÿ·kÿÒŠ‡üÐú»ÿq?ðK¿÷¡/ù¡õwþâà—ïBJoþ×é?÷7þݯÿJ%û_¤ÿÜÜûv¿ý(¨Í«¿÷ÿ»ÿzÿšWî'þ wþô$¦ÿí~“ÿsqÿíÚÿô¢_µúOýÍÇÿ·kÿÒŠ‡üÐú»ÿq?ðK¿÷¡/ù¡õwþâà—ïBJoþ×é?÷7þݯÿJ%û_¤ÿÜÜûv¿ý(¨Í«¿÷ÿ»ÿzÿšWî'þ wþô$¥Sõ›Ω~ŸS)¥­s2Íôí.9ÕWôký«þžÏæ•ÿÚý'þæãÿÛµÿéECþh}]ÿ¸Ÿø%ßûЗüÐú»ÿq?ðK¿÷¡%7ÿkôŸû›ÿn×ÿ¥ý¯Òîn?ý»_þ”T?æ‡Õßû‰ÿ‚]ÿ½ Í«¿÷ÿ»ÿzSö¿Iÿ¹¸ÿöíúQ/Úý'þæãÿÛµÿéECþh}]ÿ¸Ÿø%ßûЗüÐú»ÿq?ðK¿÷¡%7ÿkôŸû›ÿn×ÿ¥ý¯Òîn?ý»_þ”T?æ‡Õßû‰ÿ‚]ÿ½ Í«¿÷ÿ»ÿzSö¿Iÿ¹¸ÿöíúQ/Úý'þæãÿÛµÿéECþh}]ÿ¸Ÿø%ßûЗüÐú»ÿq?ðK¿÷¡%+¥}fÁ΢Ë/}Xneެ1÷6^Ö†;í õ>Ï쨯þ×é?÷7þݯÿJ*óCêïýÄÿÁ.ÿÞ„¿æ‡Õßû‰ÿ‚]ÿ½ )¿û_¤ÿÜÜûv¿ý(—í~“ÿsqÿíÚÿô¢¡ÿ4>®ÿÜOüïýèKþh}]ÿ¸Ÿø%ßûÐ’›ÿµúOýÍÇÿ·kÿÒ‰~×é?÷7þݯÿJ*óCêïýÄÿÁ.ÿÞ„¿æ‡Õßû‰ÿ‚]ÿ½ )¿û_¤ÿÜÜûv¿ý(—í~“ÿsqÿíÚÿô¢¡ÿ4>®ÿÜOüïýèKþh}]ÿ¸Ÿø%ßûÐ’›ÿµúOýÍÇÿ·kÿÒˆY]w¦SmÌÊ¢×ÔÇ=µ6Ön±ÌkžÊYµÖ:ªÿÍ«¿÷ÿ»ÿzÿšWî'þ wþô$¤Ý?ëNÊë"Û鯲ÀK©}¬ÜÈ.g»£ÿžÕŸÚý'þæãÿÛµÿéECþh}]ÿ¸Ÿø%ßûЗüÐú»ÿq?ðK¿÷¡%7ÿkôŸû›ÿn×ÿ¥ý¯Òîn?ý»_þ”T?æ‡Õßû‰ÿ‚]ÿ½ Í«¿÷ÿ»ÿzSö¿Iÿ¹¸ÿöíúQ/Úý'þæãÿÛµÿéECþh}]ÿ¸Ÿø%ßûЗüÐú»ÿq?ðK¿÷¡%7ÿkôŸû›ÿn×ÿ¥ý¯Òîn?ý»_þ”T?æ‡Õßû‰ÿ‚]ÿ½ Í«¿÷ÿ»ÿzSö¿Iÿ¹¸ÿöíúQsŸ]:†GIexù\ÿY¤¶»÷†ÝïÙ[œµ?æ‡Õßû‰ÿ‚]ÿ½ Í«¿÷ÿ»ÿzS«„g øÔÃÿE¨é˜ÆÖƱ‚ÀÑ൩ÒR’I$”ÿÿÓîI$”¤’I%)$’IJI$’R’I$”çõ@Ç}–«uǶðËú.o§‘eYÿnux•ÿÂ%ÒÃöºªÓ«Ë)èµ¾ž5—ÕWüYÖe×ÿüÚ½mU][«µ²·hæ85ÃùlzUUU5¶º˜Úënc@kZ?Æ$¦I$’JRI$’”’I$¥$’I)æME™}5…¦šïs³¥¹¶ïmLöþ“#í^þ¾Gýǫ쟢ZubQÔ*f mÆu–íáä?¸VþýײÜßÓÿ;r¹OLé´X-£š­lí{+c$lvÛ+fÿ ‹N.6>ïB¦S¼î~Ƶ›ûözm÷¤¤©$’JRI$’”’I$¥ åÞì|k.mn¹ÌÚØ œç~cvVרŒ’Jyz.œLúÞë\_•A¾Ë+¶‘éÚ:U]CúMTú5úOÉý[ùÚðý/ð+c¥µ•œºj´U[K[£ZßKÛ«©¿˜ÆfÙ”®šj!౤[­‚é=­§ôßéC]uÅ¥U5SXª–6ªÛôXÀÑýV1%3I$’R’I$”¤’I%)euÏPÕS6l»!µÚ-“Qaeïo®Æ:¿V¿´²Ðoýcùð‹UFÚªº·WkenÑÌpk‡òØô”óøõc¿&¬ ë¬ãÔü†ìh?f¶ö·ú=[ìÈeL¯/7õOæ¨ÉÇ»ÓZ!Û°sû[S‰™¥–ßV·þꌥØXn c:ŠÝŽß£IcMb?v¾’;ZÖ´5  %)$’IJI$’R’I$”¥Ö˜mÉÆ¬S^Oèïw¥{½:=Ÿfý>ý—~µNÿÕÿGüÕ™Vz´ÿ:¶PîÇÇÈhmõ2æƒ!¯hx~wéSÍݲÌCkÉ}”têmÃuŸÎ ‡}£uÿøi÷UÓ«·þ7Òÿ º„+1±íseL{ª3SœÐãYýêw4Š’”’I$¥$’I)I$’JS- ÒD"Gò½û–ÜÞŠö=î{~Ôúí{¸Òsß•ê=»íª·T=¶:½ô߸¹6»Ô.²íìÿ†{ÿKþ‘%8^•;¾Éµ¿`û§é@ô½?³}§ìÞŸó~íŸÑúÍÿ‚Z] þ¢Ð ±¶\Ú?¡e×Õ‡ÿ²¬¥Xû&'ÙþÍè×ö~=ô¢w1·ÒEkƆ0µ¢Ð ?5­IK¤’I)ÿÔîI$”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’R’I$”¤’I%)$’IM«šüFãí¶¬qu¾›î¼n­ô²rÓá};q«§ùÿðж?Xþp=ìÌ ul¦ÜVÃn²ß[v%mºûéõ±ÙG«w뾇¥þ‰^Ëmn¿½ûÛœklzô3kôw‚ý îÈõ?à=/ð¨]˜ª‘“mt‘h4ºæ‹)õC/ödT÷W_§öo´ÿ…§þß[ÓIK×Õ鲯TÊ­7¼½®ªº£IÇnGÚêú>ÆfÑwó¶zµ3þ @õ¼V°Ûc-e;Kê´´ÞÐYW꾕–[úGÝW£öŠèõ¿Á¡túp™•S©È¦Û/ÜÜv1•¸¹Ý3×wê®±•}›ÓÃýE·å[öŸçD€ìj?fWëæÒöV1¦¨€_Žî›fu—zž½u?Ðÿ‡W¥ý%%:ªÔÚ¬}µ[Sê5µÔ¸4ÛúÃþ͉c= nªÆ[oü7ø;?kM”Ö1ìkŸyǹ¯,¥Í¥ýGs¶]evþƒÓ»ô6YúSü?¥Eµ*£ Ò²2±ÜMØ¥ße©ŒkK/©øt¾¬{r2?\Èý«‘“éSüíu×údwUIÍÜÛÀ·íÁû áê}‰´Û…»{>ŸMýsí_ÍWgè½;›IIֱț*¶ êýZ·µ³s7SŽßBº­¶ßQ÷eãWèä×oé“?­ÑY{.¢êîg¤$1ÖXüƒ{1©£ÑºÚ,ê–ÿ†ô¿áôð«ÇéÌ!Ôå×mídWŅ¿×Àû%½BÛ®ßöïÚŸ`þöz­ý/èhý=Õ—¨ÑSò®oTÊmvˆh³Ò>´§è±ø¾¦Wê¶~µëý§+ý?óTúI)éqòì'c«{×ÖøÞÇC_±þ“í«ù»+³ôVØ«UÕkµÍ ¦ÐËCŽ=„03#`u»1¿M¿ô•3Ô§ífõký"~–ÊYKýi¸9òãŽÆUS]¶¶ú~¿ß³Óë]ÿZô•L:+hÀqÉbù=‚·1Æi»ÑûEî{ýOK¦ý§Òý_þÔµ%'ÅêOý“F~[a× Éemÿ¹e8­®¿Vÿû‘Oø_úÍ?ÑÓÛÕ꨻}6†Ôr -Æõö7+mß¹úK~Éö¯J¿çÙV?ìl*ÅóK>Çéݰþ“e˜nÄýó”ý¶ÆU_ý×õ¿IüÚ«™GL~nA³'¢\ßµ3"ºß`p®¿f¿5쮺ìÄû?ý§Ë§þ»ê¤¦ýZ¦Y·ÑµÕz­ÇõÚézÏ{1=?u­¿ôWYú[½Gü©ë~‰Oöž0ÆÆÊvæU–70=ônê{²6»þããYüß«úE›“KÅAôdaýº·gªsjne_juŸÑÙëÙýùÏûSé!»`b}«7Oeoûisú]‹–ÏÖ,ß{ýjzg­m ÆþoùŸY%:.ëTW½·Sm61­{kxhu¬±ìÅ©ôl¹õÿHºª¬ûE”záý%#ÔŸ¿£Æœ‹CÚýuE•Ý“»ù×WwóöžËªô}_Òz¾•6ÐÇÆÃ‹},Ì?[Ó×M Zæºÿ¶Õê[mÔßú*òO‹Gú/FÕÖÓ\óñúgì«“–N9ÂÃa±•½¥µµù_²ó}?ÓÙúÍÏþþÐý'óË_¦×[+;.¦ã ÐÆÔÝ7m}ŒªËÿIgüg§þŠªÒSg#*œfÜâÖ¹Í` {ž}:Ûú6¿óÐjaËAyiqk}Ísv¾Ïæj¹¯oèl³wøOø?ôŠI”¾ªÃíô€¾’ i|½¶1ÔÓµ›ž·ô~§ø4SQÍÉ}9 k]c}j\ØËƒ*þ‹÷7ùʾÏþ'þ¶’›mêX„¼´z€–¸n®}?R¯oé?Iþ¿¤A«¨ÎVEv_¤+nÒ×î´Yì÷}? ³‹¶æ~”VçzOzøÿeÉÈÞû,õþÝèzµÛüåè)Víªü§edTIô½v¾¢+ =§ÓµÿÍzWz•Ý餧^»cÛ;]Ä‚Óý¦?Þ¤«à´7¦µî±¡º=áÍq›ì¿ôÿöïé„”¤’I%)$’IJI$’R’I$”¤’I%)$’IJI$’SÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/zkperfRW.jpg0100644 0000000 0000000 00000473406 15051152474 031353 0ustar00rootroot0000000 0000000 ÿØÿàJFIFÿíPhotoshop 3.08BIMÿâøICC_PROFILEèapplmntrRGB XYZ ± acspAPPLöÖÓ-applrXYZ,gXYZ@bXYZTwtpthchad|,rTRC¨gTRC¸bTRCÈvcgtØndinì>desc,ddscmþmmod(cprt¸-XYZ \¸4 ÄXYZ vµ¨'XYZ #ÿb¢GXYZ óRÏsf32 BÞÿÿó&’ý‘ÿÿû¢ÿÿý£ÜÀlcurvÍcurvÍcurvÍvcgtXAÕ r ö Š)Ç^ù…³Pât™!3"Á$J%Ü'm(þ*Š,"-j.,/Ö253 595ù7:8»:9;Ò>?l@$ApBûEGF©H InJÕL9MŽNáP7QŒRÞT/U…VÓXYjZ¬[ñ]1^j_`•a¦b´cÂdÎeÙfãgìhõiýklmn'o.p4q7r8s9t;u;v9w6x3y1z.{)|"}~ø€ë܂΃½„¬…™†…‡rˆ\‰DŠ+‹‹õŒÛÁŽ©“q‘Q’2““ö”וº–›—w˜R™.š šê›Åœœlž<Ÿ ŸÝ ¯¡†¢U£#£ð¤½¥‹¦Z§(§ö¨Ã©ŽªW««ç¬²­z®@¯¯Ì°•±Z²²â³§´iµ*µì¶­·n¸/¸ð¹°ºp»0»ñ¼±½n¾+¾é¿¦ÀdÁ$ÁåÂ¥ÃbÄÄÜÅ›ÆYÇÇÒÈÉPÊÊÒËŽÌLÍ ÍÑÎÏJÐ ÐÊщÒIÓ ÓÈÔŠÕNÖ֨יØ\Ù ÙäÚ§ÛjÜ/ÜôݼބßKààÛá£âlã7ääÌ噿fç4èèÒé êpë@ììæíºîŽïað6ñ ñàò¸ó‘ôjõCööö÷Ðø©ùƒú]û9üüóýÉþ‘ÿMÿÿXAÕ r  ’(«úz‹¯?ÁE´þ!l"ô$v%ñ'k(à*W+Ê-8.b/Ã152 45o6Ñ8;9™:Þ< =\?b@ A\B³DETF¢GÖHøJ,KrLºNOPP’Q×ST`UŸVÝXYAZZ[h\t]…^“_¡`¯a¾bÌcÙdæeòfþhijk"l)m/n4o9p=q?r?s:t7u3v2w/x)y z{||ó}å~ÖÇ€µŸ‚‹ƒz„e…K†2‡‡ýˆá‰ÅŠ©‹ŠŒhIŽ) êÊ‘©’…“]”6••ë–Á—’˜Z™"™êš³›~œHØžŸe - õ¡½¢…£L¤¤Õ¥˜¦\§!§ç¨®©mª/ªò«·¬w­9­û®½¯}°>°ÿ±¿²ƒ³D´´Æµˆ¶I· ·Ë¸¹MººÎ»’¼V½½Ü¾ž¿aÀ$ÀèÁ©ÂjÃ-ÃñÄ·Å{Æ?ÇÇÆÈ“É]Ê'Êð˼̆ÍQÎÎçϵЃÑRÒ ÒðÓÁÔ’ÕbÖ2××ÙØ©Ù|ÚQÛ'ÛýÜÒݨހßXà1á áéâÂãžä{åYæ6ççòèÔé¶ê™ëì}í‰î„ïtðsñƒò‚ótôtõ…ö„÷vøwù‡úˆû|ü~ýŠþxÿEÿÿ‰ Éo $ ³ Sþ}î{ƒ‚õ] °"#_$¿&'m(¸*+T,Œ-·.ç01V2‰3¹4ç6798R9f;J;ß<§=¾>Ò?ç@üB CDEFGH'I0J9K?L:MCN@ONPMQER,SSòTÕU¶V˜WwXTY5ZZñ[Ñ\³]”^w_Y`6aaíbÊc©d†e]f:ggñhÍi¨j„kcl7m mãnºo’pnq@rrçs»tucv3wwÒx¢yrzC{{ä|³}~Mꀹ‰‚Sƒƒæ„¯…y†G‡‡Ýˆ¨‰tŠ=‹‹ÒŒŸhŽ0Ž÷¿‡‘S’’瓱”y•?––Ì—•˜^™$™êš±›|œD ÓžžŸc * ñ¡º¢‚£K¤¤ß¥§¦r§=¨¨Ó© ªj«4¬¬Í­®l¯:°°Õ±¦²u³B´´ßµ¯¶ƒ·V¸(¸ý¹ÙºÆ»®¼˜½…¾u¿eÀUÁFÂ9Ã,Ä!ÅÆÇ ÈÉÉÿÊûËúÌûÍýÏÐ ÑÒÓ'Ô3ÕBÖT×gØ|Ù“ÚªÛÄÜæÞß!àBáeâŒãµäáæç@è†éÀêðì/í~îÀïùñ<òóÑõ öQ÷¤øêú*ûtüÌþÿÿÿndin6•T¼SF‡e'ž¨P T9s3^¸áG '2?N]n‘¥ºÐç5Pl‰¨Èé .RwœÃì@k—Äò"Q´S¡Ã+Nrì,k¬î2h’¾  š è 5 w ¥ Ô ' { Ð & | Ó , Š êJ« q×>¤zçVÉ<±)¤0¿Qæ~´Rô—<ãŠ3Þ‹<ï §!a""Ú#š$_%%%í&¶'‚(R)&)ü*Ö+³,“-v.[/D001 2344þ5ÿ78 9:;$<<=T>l?†@£AÁBãD EN?j@ˆA©BÍC÷E*F\GŽHÈJKhLÂNOzPØR>S¬UV…WöYhZá\`]å_k`ðbvde¡g3hÑjolm¶o^q r³tcvwÌy…{A|ý~À€†‚F„ …чž‰k‹9Žè¿’’”l–L˜-™ù›Ò­Ÿ…¡c£A¥"¦ÿ¨Ýª¼¬®{°]²B´(¶·ó¹Ø»»½¡¿ŠÁsÃZÅAÇ(É ÊöÌÞÎÅЮҚԅÖlØQÚ:ÜÝ´ßRá&âú䛿:èéäëŠí.ï ðåòô9ö÷ôù¤û`ý€ÿÿ !+6CRaq‚–¬ÄÛó )Ea¡Âæ 1Y…³âAr¥ÛM‡ÃI‘Ø iµS¥úQ­  M  :   u á P à < Â@¼;¹<ÁIÙcö²\ ¼q(ä¤c(î´}Gè À!š"t#Q$7%&&é'Õ(Ä)±*­+¨,¥-¥.¢/¬0¸1Æ2Õ3è56!7A8d9‡:®;Ù= >>?r@©AàC#DjE´GHHI—JêL=M™NóPLQ®STUîWVXÄZ5[­]*^ª`(a©c2d½fAgÏi`jíl‡n o¹qVrñtv2wÓyw{|Á~o€ȃp…†ÏˆƒŠ4‹ê¦b‘’Ò”Œ–H—ù™ˆ› œ¹žKŸÜ¡o£¤•¦(§»©KªÛ¬j­ø¯†±²¦´5µÁ·O¸Øº_»æ½n¾öÀ}ÂÄÅÆ…ÈÉ‚ËÌ~ÍðÏmÐèÒaÓÙÕOÖÂØ4Ù£Û܇ÝÝß4àŸâãwäÇæçèïêUë§ìýîeï×ñ>ò•óëõUöÄø-ù†úÙüFý÷ÿÿdesc Color LCDmluc itITÄfrFRBØnbNOesES,fiFI>ptPTNzhTWfjaJPtnlNL‚deDE˜koKR ¨enUS´svSEÆdaDKÖzhCN òLCD coloriÉcran à cristaux liquides couleurFarge-LCDLCD colorVäri-LCDLCD colorido_i‚rm²fv˜oy:Vh0«0é0ü LCDKleuren-LCDFarb-LCDÎì·ì LCDColor LCDFärg-LCDLCD-farveskærm_i‚r LCDmmodœ¶l>€textCopyright Apple Computer, Inc., 2005ÿÛCÿÛCÿÀ¨[ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( €>Jøíûo~ÏŸ³ŸÄOü#ø‰}ñwYøŸãŸkÿ¼9à‚Ÿ³'í7ûLx®oø_YÑü=®ø³TÑ?fÿ„ï¼9 Øëž Ñ´£©x’&ÚæûP†ÞÒK‰7„Æ…xbq9–õje2œFg8aq/ ‚§ŸTÍédëŒö_S…lÂyqõ|?·x‰C^£¤©òJZÖ¥,>Н:4©f˜¼Ç—*˜œ|=ø¯ãmcâ—…tŸ‹> ºðŸÃ/k¿³í%§~ÐÞ<ñ5“j{ ø[öV¹øHŸ´Ö³«ZXhºž½scgð’[ˆ<+hþ/•Âï¯&ø•õlv,íXìV]<Þl±¬Þt²º4pµñY†1å_\†_„À,vŽg_,5<¯Š¡€Ì%†ÇU†å‡ÿhÂãq«ý›€ÅÒÀb1yü%až;9SÁ`ðõó?ªRÆâ³© ¯‚–"®i:U£—Ã*UlÜþÜŸ²­§À&ý§.¾.é|M{þæÕåð÷Œ—Åãâü$GÁçáAøN|9ÿ oþ/ü%àøKþ×ü ßð´¿á)ÿðˆÿlÿ¡Q‹O W(¢××jq£.Y?ü/,þzŒ\+äsÉ´!›á–ÅVÅeÒÄáðøl:½z´é`±S¤ð©â–fìðk$Œêg7O#Y8C QÕÏsõ“Ò,v­¹“ÂÓÄQÌ2ú´%Rž; *ÓxCöÚý˜4ËñwÂ~;øâïƒÓXi¾ ’ßâ§Ã?ðÄ¿‡wWz þŸ­èö¾0ðŽs¯èú–›©èqj6Z”óÎ6PÀ`pyj”«`s JÀàq}Zy¬q™›ÆÒËM†ŽW–8ê˜lV¾¢ËgOZºúí<:–K:8}XçPrÊ¥OœqŽ8,S¥oàGí‘û=þÒ~'Ò¾x¯ÅÞ"ðv‹£ø§Ä^ ø­ð“ãìýãûø‹í£ÃÞ=‹á÷Çßü3ñ¶£ðó_“LÔíôOˆ:fyà½^ïMÔm4ývâêÆî(µÄÓx<¿šb§C‚ËkÖÂfÕjâpðžI‹¡‡xʘ<úƒ«õŒ‹°q–68Lâ– SbéÓž.ªÂjuñX\%,E\ÂŒ±]JêWÁçXhU¡B®+!Ì)Fx û F¶/ J¾''Äã¨P©ŒÁ­HK†Uy?ƒ?ðP/Ù/ö€ñΙð÷á_ÄíK\×?eÖ%…P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@~[ü`ý•uoŒÿ·v½ñ“áoíÿ®~Ïÿ¼û*øSá¿ð«à§?fÏ|`ðÞâo‰~.ñÖ‰ã_ß|zðÇ+=/À>Ôô¦Óì4h~øcSÖ/>ÝÏ üLXáÖ´ÅæÊ©b0T8×…Äýj–wðµüôc<—˜ð~E›ÕÁäùÒÃÊž?‰xn5¯›ÏƒÎ¸ »3ÂʤkS©‚ÆSèÌjaª¾Ââð£_-Ãñ¦? :µ«C˜åüMŒàÜaÉ¡)C F9~'‚ðùmLv#ža½¶7AÒÃb#þgþÇÿ´¯ÅïÚ“þ #û øÓãü-ÍÏ€> ÿÁV¾xgÆ^ÒÆ‰àŠ_þ~Ñ߾럼+£jZ„M×~ør]I´¯êz”šeÇü&¶Wá»{¨¢ö8Qᱸ^(â:Xe—ãxÏÀ¸ºŽQ:²¯<£.Î8Éßa0uªÛW/­žÑàÜËRºä–Qá߯ÕÄã?³ñuøx®–#.þÌá·ŠþÐÃðŸÒ‹²nc*thb1ßñ©+fü,ÎÇK1Áeù·eU•Â3¿9­†£…§W…¡á—6>-ƒþ §5ìñ^Çû3ßÿÁx4mîïü#3ünµÿ‚[¼62E¤æÈ\/ÄÃi ÌÛI|e›€ï¯[²Ç‡†Î Á°Ä¸}ið·Ó&· ªŽ._UŸ‰Y5)<+Ö¬SÀaüVú­:-WTÖ}R¬VUW)í⌗ËìÝX`¾ˆ8‘Ó·4hÃ9Ì+Ô§ŠkÜç‹ÄøOS¥¯³­9þþX`ÿ‚¥Øø·þ¯ö¬Ô´(¯GÁì¿ø"í==»Æº ÙÃzxÖá¢×“Pq¤Ë4^ :kë~¤Áá)˜kŽ\Ú  Ü#Ä9*Ÿ}R¯ÒŸò5YÅÒÿXè}ªò:JW”'þµâ<5çtùqs7’ÿgsæ4¡Èøé©pæ`°œ7£ô`ñu×ä·Ö!’b/qm|¦3¶”éø}†£R¥~_{û*X~ËŸ/>"Ëhú;¦ýÂX|Bþ KF:tºH¹†Õ$i-£oÙ8‹ë éT«Íìéå_H·œIsûå5² ^íùÿæ|Q_„©a9½ïí ™g²÷¹Yøÿ ]ýëÆ7ö’˼öJ£Š¬øž>'p$ëûK´ÿµñ$±¾Ó÷œ«4x½}¹ý WÄŸ`~`|;ýª?o¯Ž6¿<]ðSö@ý5†^ý ?iïžÖ>)~ߟ>øïÄ_ðÌŸ´wÅOÙ¿YñN»à ÿÁ7þ+xÂßð”øƒáN«â-3D°ø‹âï°húžŸÖ¯-êÜÇ ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ãú×ícÿ$о>ü4ý/?bØ‚O|Tø?ñÃã_‡µKoø)Ç—ð­Ÿ…~xÓözð/Œl5›éà–pêöþ ÔµÚSÀ·µ²Ðõ:óNÒ¼Y6©ªè×6:=¦¼ìð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íxÿí ûXÿÁI?f¿€_?hÏ~Äÿ±­àŸ€_þ%ükñŽ—á?ø)Ç›ïj^øWà½kÇ^!°ðÕ–±ÿ³Ð´›Ï^i:å¾kªkš6q¨Ém ö«§[<·€{ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ÂÆÿ‚¦ÿÑ›þÀø²ÏÚ+ÿ¥;@ü,oø*oý¿ìÿ‹,ý¢¿úS´ãú/ícÿ$×~>üKý-?bØ‚?|+ø?ð?ã_ˆuKŸø)Ç”ð­ç…~?xÓö…ð/ƒ¬4kè¿à–sj÷ ÓuÙ¯ÇW&µ¾Ðôí:ÏNÕ¼'6—ªë77ÚŦ„ìð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”í|ïñGöÞÿ‚Š|%Ð5ox‹öýŒoô­#ãÿìçû8M6‰ÿøé3]x÷ö™ø½ðkà·ƒ/¬?´ÿà—ZR]x_Ã~,øåáŸêbE½Ót?ÄߨZ_‰5. ÿç²n$Á繇åø<>:…sš9;‰£N–˜Tɲ¬ò¬rÊŠ´êâ¨á0ùÆ Н:4)Ã0†+ MÕ–¬—ËpÿåüIšñŽU€ÂæTßq†³v/JŽ_Í*ðþKÄu£“ÕXŠ•±¸|>Á`ñ¸š˜|=(f”ñ¸N´ðuåa×þ4ÁV4MOÁzu·ìûøŽ/xžã÷ú®ÿ(øÞúgm ð‡ŠüR¾,ñ£êßðK"ö \ÞøfÏÁvòxrËĺÛx¿Åþ‚M-msÄÛåÙe~=ÅUÎ2œ®y.SK3Ãàócã‹âÕ3¬Ÿ(y>D°y~6…LÚ• Ö¾{V9¦#*À,—$Î*C0žc ¿-Ì~–sq•(ªu'í*89C—–’TêTö•y§©·MRN söµi®^G9Âî¹ñ“þ  [Ù\^þÆ¿°4ŸÚ:Ö‹¡ZÅoÿ+ý¡ Ò^ëš¶›nØŸþ Gy6Æá¯o̶V×-o ÍÂÅm6~]_2©ˆ§AÂ?VÀcóÓ¨æ¡~®*¢½8T—=_f¨QN*½ZJ¬éRs«[+ʱ9µlU,3§©å¹žk^¥gQS†+ÁVÆÕW¥N¬ý¥b°Øt੼Mz1­RRµ=¯øXßðTßú3ØÿYûEô§k€óLÝgâßü BÒ5]nÿö8ý€–ÇFÓoµ[ÖOø)_í]m4ûYnîYÿÁ(Kar¡œne#«ƒ­˜cpx 2OŽÅaðt®¢ëbjÂ%')YÎq½£'m“zñ[Áü1Ä|]N¥<Ÿ…²߈ój””%Vž[’eøŒÏ:q«R•9TŽ VPU*Òƒ’Ju!Éc럿à¤>ð»øÓƲÇü—Á‚Ê ýC[ñü㯇t­& ˆ–Qý©ªj_ðJ¨tË6Œ6ÉKÓu`²º€Ç2«†Êž*XìfC V¥:¸ÊµãC û¹Ê¢­ˆöIS“‹”eQA¸´Übî—Òð^;ñdT¸W‡8‡7͸‹‚Æåü;—åuóN!“Ça©bcƒ–Y”hÔ«£ªž"žxªq«*ujÖrñ~Ü¿ðQÏŠ^%Òt„ÿ°/ì¥ñ'Â×îÿoø¹¢~ß?´fð‡G·Jñß[øÛâü»ÁxþÊi;e›á §Äo*âáémã‚öKOžÁq64ÄÒ£•`óLË Q¿i›QÁ¼>QF6mN8ܦ8ø6”y²ˆf6”—7*ŒÜ?`âoør\Ã1ãþ#à^ Ïp‘Õ<>Ìø’9·ˆyg:qž· ðމ*ð–&”':Ò§â#ƒ}¥*St%Zu0Яôü,oø*oý¿ìÿ‹,ý¢¿úSµôgãÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNÐÿ þ ›ÿFoûâË?h¯þ”íð±¿à©¿ôfÿ°þ,³öŠÿéNР~É?´/Å?Ž7_´§„~5ü$øðâoìÏû@iÿ!j~žò}FOxŸÇ?³×Ä…þ*ñ€N¥us©x·WÖ¼&š•Äú‚hëy,“·5<&\uEF§šÃ ßZøŒ§9Ž N_휖»©”æò¡F­l5)æX,Tワ_¥õJõhÏz˜ŠÕha0óÖ]_‰ËkÆ1†;+¯… xʹ^eÇ0ËgŠŽ ë¼'íja°Õgz´)Nücû ~Ê~5øSð·àµßÂ[O x àmÝ¥ÿÁ8þø«Çücð{Q´°»ÒÎ¥ð³âÁŸxâwÃíKPÒõKM×µ xÃH½ñ&Ÿªj–^ ŸS¶ÔëÆN®?2§œb+âVeOW+Ž/‰Ä`ª<¢½&®KR8:˜zur9ÐÀeôžKVÊ”2üV,Ùa…QÂ`±Yu:tªà±¸Ïí<^F–e Fmõ¼Faê¯ö„1Ržy ~3Ž¥·ý«G‹Åb©c!_Z¤ß/ì1û)Oû?Ù~oƒúTŸF¸<\4V×ücÿ bü@_ÿÂ\¿Wâ°ñü-•øÆ¾-ÿа|c_Š#ÅñQÿl¦Ñ‹”±•²šòoW‡ý”r dÿð„²thbp°¡’G%ú„2Œ4°¸Ìn¶.ŽˆÃc±Ô1êÒÆâ¡Uá[¬Í]ãwÓÏnÞx³Ús†›¥ž,çëë8¥ X*4³%ЧBŽ_—Ò¡tð8HÑ›Á¿±ì¹àŸ…?> Ùü+¶ñ_€~7ɨÍñª‹^*ñ×Çüa¸Õ4»}âïâ¯Ä¿Œþ'ñïįˆ—–ú-Ž¥^xÃÅÚÍÖ‡¥Xiú~‹6Ÿgag Œ1øYZ*8»n—Ò§•C™GG2þØÂË+Žt3Éf8|>cS<§(æõqøzÚ˜ÙâhÒ«„©[©™ÐÄbe­‡Ž ¦+‰Äcª¼¶ñTa”'©ˆQÉaG¥–*9Tiæø,Ž;«Zø û~ϳm犵o†ñ,þ$ñ¶£xkÄÞ4ø©ñgãíñRð‡‡Ví|=àUø…ñóÇ¿ñï‰þþϺgˆ„²[ëzÁ?ø ÏT±c§^Ã5‡ú5V_ Bµ uªÔ–#…ʱì]IcózùF ¥ Ø,š¾yŽxŒæ¶M„«„ÁÕÃe1ÒËhTÁ`gK `°®•ciÓÌ1Äâ¡(fxœî4(Æ8L Îñ´«PÅçSËpІ_S8ÄÓÄâÕlÒ¦XúŽœ±xÜT«}›X–|ÿÓÿ“uøÿgÿÿbÿצþÙ÷ýP@š>5þÛZïǯ‹ÿ ÿg­áÀ¿…³ß üFñgí ûW|øµãÿ‡<_ã¼Uª·Ã¯„º¯ƒ¾3|д]áï‡ü/çÅ?‰W!ø„þÔ¼M¤éðòo³Íssãb³:™nAÅ\YŽeü7Äà0|?R¤°ÆO+Èòü÷3Îc™ÖXšYVMVY<ƒ&Ĭ“7Ža›a3zŽ¥(eŸUÅú+O˜p¶C‚Xºù—QÄc1Yž‚Ì0 )fߨyfYS-§<5l׈q¸Šx¬Öy[Ͳ…O'†q¯9æ°«„÷ØKö—ÔloØ÷öyý¨5MðÛTøÕð×Eñ¶¡à¹o'Ô­ôk»ß: †Ò5K›-:ãUðÞ¥%³jþÕ§±´—Tðåþ—~ð#\_iÄy],—3žJþÆX §2Œ1´ã‡Ç`¡›åßû?2£J4s,¯ë¯/Ì©¦•5øcàçÄßxJ=J_øKá'í â‡z?À?‹ž+ÑSD×ÿ†¾|Jñ^· ðÿˆTØ—Ð5…²ñrؼÛN{HÕÊ©çØj¿Øñøì†­*8ˆg¹v[ú¾c™d’Ââp˜ïí|—,3x•ƒÆa«Õôqÿð™V¥,gºðøØå˜ê´?Û0ÙVi!Ið†?CðËâäŸåøÅÛéÒü‹ö˜_ŸÙÎ_Œqê±É£?ˆþ)·Ä%×#“Do YÌZyÌ0õ2ïßG„ÆæWµÿd–{—å´±Œ~aÃÆ{ ñ& †Áã±XŒnEÇ O €Ì12ª¨`1•(¬Íÿd*x¯þ¿Ù­¡oÚ  üý¢¼}£ü²ºð~“ãËsâg‹<ðŸÄÞðw†5? ëV—ÚGŠõÍnÏÃZ½Õ¾³£iº­Ö¹áíMÓZÖoG‰ÀÊ50¹‡G…p˜ÊÒX<^$Ÿa8B9#Æc^ K1–}ŽÃP޵Zu¤³¤¿±)VÌií¡[ˆX_€Xœn&„0™n%Õö*T£‰– VÇæYvô¿ŒÿhŸ‚ÿ|{ðËá—Œ|y§h¾4øÃ |Bñ_Ãí2K-fîÏVð§Â­ Nñ'ÄêZý†›wáß xkÃ:F±¤Ïy¯ø»Vд‰îuM;M±¼»Ôï­¬åÊ¥j4jç´±iá¥Ã9ø“?ž.qÂÐÊrZy®%xÌn'éaè¹fXÚT)á_®U…,n*–xL·0¯…Ê…ñxL£ƒ…Lf>Î2üƒ&žL\ó<ß5ÊóLë/ÁàèáãV½gˆË2lÇ«F›ÃÓT©R«Zñ˜:x™~~Ôß°Ïí3ûD|<ñoÃßë:çÆÍ áÅÿ üñ‡‰¾|{øcቿþ"x—á‹>/ß|ñOÄOx3áí'ákÍkàÏ wRñÇÂMGâZxoLÒ4Û½3_Ñü?âÍQµîÈa±3¡Œ­,=l=L»G˜åøÚSÀgyvV >c˜pþ68|ó–â1há¨f8¼¾Ž ¶*µ5:ò¯Z9e^½ =\=9V¥VÌf#-Âãp•!ʱž|F'*¥œ`¥ˆÊ§šÐ£…ÆU­–,cÇÓ†0rï¨c=…¿ÿÁNÿboˆ~øã¯|Xñ½àŸ†:Õç„ü]ã o‚?bð´~?²ñóü0o…>ñßÂû}'Çï|mðgÃÏŠ>‡^Ós©xvû_ð†Ÿaâ-5_PÐîu 4y—­áê}RŽ:Ãâ0xŒF+ V…ÆÑ†;°òÇeØŠ˜JÕ£…Ìð0ÅàêcrÜS£ÂÒÆa*â0ôéâ°ò©ËõŠk[/š«GG ƒÇψÃ×ÃÔž0–&}oNšÄàqUpXÚ41˜gW SƒÅÐU]l5xCßë` € ( € óÿ‹? | ñÇágÄ¿‚Ÿ´/øJ>|`øã/…¿|5ý§¬hŸð‘xâ‡u/ ø»BþÙðøƒIþ×ðþ¯¨XièZ®™¬X}£íZf¡e{1€zP@P@PŸéß | ¤üSñ—ƽ?Bû?Ä߈þ|-ñw‰¿´õ‰µü ðÄ|YðëBþÆŸP—ÃöðŽøƒãÅ-CûOLÒ¬µ_þ²ëº†§e¢xvÛIô ( € ( € Åñˆt x\ñOˆ/SNм7¤j:î³"»¥ž—¥ZM}rɼ²m –O.$yd+²4geSåg™ÖYÃy.oÄ9Ö*,£"ËqÙ¾iŒšœ£…Ëòì5\^2¼¡N2©?eBIòSŒªM®XFSi?‰8‡'áÏx«ˆq°Ë²Ê3,û:ÇÔŒç W”àëcñø©B”gV¢£…¡V§³¥ Õ¨ãÉNœ£ä·<9ñá_…<ñ•.þ ǧø÷áŸÆy¤¼Õ÷ àøÃ6Í¡‰¯_'áü]‚Ì³Š¸l<ñÙ–c(åx øê+û^•L®ž_Š©Z:4‹PÆÕõö©9þKã—ý¼?ðû’ey_ˆÞ0q/q7xsγz)Á|J¯ñ^WÂù”߇™Ž Žñ¼]‘`òÜË™æ•sþ"áéb¸cœÂ|$ó« }‡áÆ‹á )#ó;3Ü™¼|/ d´1ñ¸ŠslÆ“r§˜çxй®.Œå¼°*”rôÖžÏ.£„¤•íOÞ•ÿGϾ&fùN3†rŒ×á÷ãé†3ƒ<1Êp<ÃùŽ’~ÎŽ¥„Ìø½ÆMÏë|g™ñ>Så”ñröT•?¥kéÅB€ ( €>ýÿäâ¿à¬_öÿõÖ_ðM:ûþ€ ( € ( € ( € ( € øþ §ÿ&ëñþÏÿþ Åÿ¯Mý²(ïú( €? ¿k_hÚçÅOŒ¿oÏØŽoÚ‡ö3Ö|/ðÛÆÿ²Þ¥ðÏö3øÅû`]ê_Fñf…ñCÂ4ŸøcâÍ·Ãoˆº&§%†¡ð»â5ç†>øNÏÂþ*¹†oˆ ®YjRAãº4s¿1§ŠÀןeùîgO‡þ«Í–añ|3Ê2e•ã2Î&ÄUÃa2<î–q<ï,Ï^3>Êœ°´òÌv°QÆâ#èT­‰Ë«e˜œ¿Ja“:M³ž+ƒÏ0î#x¼ J¾+:á¼nQO$Ç`¨á2œâµ<ï,Ì©b”ªWÉðïÛ?à–? ><|ýƒ~ ü<ý¡â×m¾#é0xòïOðŒðF­tu}ŽçF–Â-[W†Æ+¹þŸŠ§WÀáñUp|IŸPàŽÊø£®†Š8Ç ÃãÞ+ê©ÊŽmÃí³?©scœêf’¡Rx™sø9},5<׉«à0ղޯñŸcøS,t¹käÜ-‰Ìg<¯$ª/ª©SUs .W*ÑyfC+k õO«aÿ">ü'øµáˆ³—ÂÿÙËÁ¶7ÃñáßÚOOñ/ÆïØgö¨ø¨|gý‡f¿ Gâßøëâì­ûs|Lýœ|¶š×„›ÄZŸŠ>„?´'‹u=bëćÂï Xéúퟆ¼¬y¦/ÁÃ1­[%¥á×äT8ƒ<¡“Žx#ŠðÇÃYS€•,F8Îêá³JX ÌT!žå˜ÎÅfØégØl»†ÄVô8–ª§_бPÃÁçXî3Ëó >,Ú1Çñÿˆ‡„â,vŸ`y3 ¯‡iæ¹z©Æø˜ÂY.?&âÌ„2¼Ó:¢éb|/á?ìqñ÷Eý•¾Á:þ'x÷þ ‰ãˆþøÑ¤Zx³à烾 þÌÿ?cȼ;á¯ÚDüV±ý ôÿÛ³ÄÿðO?ˆ°7†¾Çc¦ü\ÿ„U?hÿ~ÑZß‹e“ÀZ†c{w6™ìdxÊ8ÜëÂÜâ#J<‚à ndóëeÐáÏøS.Ëñ9N.ÀSÈgÅøjxÜò þÛ/Îrœ]Fsž`£Û‡Ã‰#:1ñ^4åË6Æø“ª96a_‰rßq™Ã ±xœ|³HðÆ/û;;•#Æç±¡þ¯âðÜGå™…\9ÇþÈþÊÿµ {ã¯üS@ø·ðÛÄzßÚ+öŠðŽ‘¦j>(ð¶§£èÿ¾j_±Á/ø‚÷Âú¶¥ameâÏÁª§Š<9q¨èó_é¶z妳¦<±ßÚÞÀŸ=<®Žoá†q5eÅc8³Æšµ©Ó~Ë0ËÖsƘŠùFq 2jµ•pðÂæN2q·§‡Ãb0õ'N9¯c™b2¯²î!ËåCʼ?ð™añm*¸j™¦EˆâœF+Z­.ZRÄag[õü$e´* ­:^Þßž^ý‡k_Úgöiý¸üñ;Kñ?…>5ü.ý‰&ø~¾šoˆZ×Ä¥W†lüMõXŒfÅrÊ3<&*¤04x‹œæ™^,Êó,“‚ËèæUqõp´¾:† àŒ‡„qt~§ŽŽeÀ˜Œ^A“J†?*Àª†=æ?ëC–s‚§‚ÁÊ/.Àà0Ù†3>Íéfu°¸ÏìøRÇf8;Ÿ ÿg½jÏþ á/‡ÿf_ÚXºðÿíñâ—‰|7ð¶_|*ý¨~é~Ü<áoÚCà÷†4¿ j~8ñÏü esáψþðN‘£¾£ñ#ÂSÎtüFotí^ùj’,“úÞ?(¯ø]áÎ__<ÊèÔÇãøC;¥à¤r–'ø*TÇcò<‡W–`(PË'ò1Q‹Ï²×¯˜cp˜~«‡ÌešáÜÖK™ÔÏ1¼>OÃÙ¥l6‹Ë1”«cs¼Ï¾»—åØÜf ŽS<Ï …ýkñ6‡ÿ .ƒªh_Ûîý©jÖ¿Û>Ô?²µí;s+}£KÔ|©þÉt»p³y2aY†Óšñ14>³B­m_íbãí°Õ=–"þÕ*–—$û;>§Òd™§ö.mͳ²¼Ûê5ã_û7;Â}*Ær¦½Ž;í)}b„¯ySö»IßCÀáš?êà?i¯ü:ßýá¯ýZÿªƒ‰¿ðëÿÜÖ¿â6Õ¤ðGÿ/ÿ ü3Gý\í5ÿ‡[ÿ¼4«_õPq7þû€Älÿ«Iàþ _þøfú¸Úkÿ·ÿxhÿV¿ê âoü:ÿ÷ÿˆÙÿV“Áü@¿ü,ðÍõp´×þoþðÑþ­ÕAÄßøuÿîÿ³þ­'‚?øøX?áš?êà?i¯ü:ßýá£ýZÿªƒ‰¿ðëÿÜþ#gýZOñÿð±ìü ÿs£Âaã¯}§S›SþÕøƒâøI5›:ÖÊ×û>Úûì–^V™Ø¾Ó §”Û.îïfóŸµ}Œ¿ýŸFT~¹ŽÆóU•_k˜b>³Z<Ñ„}œgÉ R\œÊvœæïïiù×qOúÛ™PÌ¿ÕÞáŸc§‚ú‡åعmogˆÄ×úÝ|/Ö1>ÓSë>Æ®#Ú.z|5>Eì¹¥ÝWqòÁ@P@P@P@P@P@y¯Ä_¶já/ØÇ Þ'‰üInÞ&Óõ©4‰Ùü ¡Å&©âK›mSs6®“Þ&ƒá™›O¶ºŸJ›Åz›ý-þÓÁñÇÖs pß a)äø¨ñ{AçØ,Ö¦[VRàü¢œó û‡Ê±óus8ÕÄÇ'ÈjËC[.«Ä\Â~Æ}¼?4ñ뙬øKƒ°4² l8£‰pòâl¿;«”Wœø "¥S4â\V$Ìæëg¯†AÃ5å—áqUòšÜQƒÍ'õxaþ³N¿Åß‹¾ø3á{ox–ß[Õîµ}{H🄼#áM=5ŸxãÅúýÁƒHð¿„ôg¹³ŽÿUº s{q%Õ厗£èö¦¿®êZ^…¥jZ•¯ÔfÙ¾&ÃG‰z²­^Ž „ÂÓöØÌv3.Z8\-(*•giNNs…*4aW^¥*ªTô/‡¾ç¾$ç•òl–¶Y—Ð˲¬Ãˆ8ƒˆsüd²îá~Êi*¹Ž{Ä”hâg„ÀPs£†¥ lV;1̱x§*Ác³\~ ˆøþÁÿk¿‹?|Y«éŸÓöSðþ—ð£ÀºEŽ~!jöõÛx›Äþ6Ôõ_øῃ3â\f+&­©ø”ÒÊòNÃåùžc‹ ¨dx<£>žk›ar¬» œg3§ŽáÌÏ™à•,FS9ýÜ0_G® Ì+<Ë0ãOól6„cO†œü1ðÖx‰b1ê¤cœçùnkÇÜG„§ 8:Õi®ð÷VÏcC1¡,<êUéídï‡ÿÅÝ=þ3k,ý¥µø-râ_ŽÚΙâ¯A­j,†]YÑþé)áσ¾»°¹ðæ£5–§áï„–·ÒÏ ƒÜx„]h:b6nÀRà\cÎ%‰âJÙÇer•|þeÔᜳ1­ƒÁÇ-§”`&ªñ$*ÖŽ_õ*µ],ñT1ßVÁ×Àý•¸Ï)ð÷ òìƒÁ\¿7âš™e(xW–ãræ®[ƒÈ*C4˳s g>#gØ|]ç N7ñ¾êbãG(t3\l£÷¥¥¥…­½…­½••¤1ÛÚÙÚCµ­µ¼*(-íáTŠb@8£EDPTs§Œ!£B*1ŒR²Œb¬¢’Ñ$¬‘ü½ˆÄb1uëb±Uëbq8Š“­_ˆ«:ÕëÖ©'*•kV©)T«RrnSœå)JM¹6ÝÎc[Ô-›ÅðûùsxÚçˆDK4OeáÛ;{žä^ÛOs Pj~$ѧ†ëJ’Òú;䳊K“aq{ksîà0ÕVSžfQåT¨,¿,ssÅSœkæuêâ!OV*“©„ʱÔçGØyáå^q¥õšt+Rü§‹s¼üCð·‚jûz˜üÖ§qÌ0ôðÙ/ W*àl¯”bjæÍðÌ~ƒÏøû…±˜\džëe™¾8£•á«f&Ææ¹~a×׈~ P@PÀ±¿üœWü‹þÏÿáÏþºËþ §@Ð@P@P@P@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@PçoÅïÚ×ö´ý®¯¿dÙ“öqø9ñk^ð·ì÷áOÚÇÞ8øßûMøïö|ðÖ…§xãâŒ~ø[š$ý“ÿi‹ïkwóx^Ö'žìxvÎÖÆÕ£ <ûCá–¼Ve(ÅF… 9w æÜ=‘N¼ñ+šg¹FcžT†°ªœ0Ùf ƒxœDñŽu+fxztèZY®Œu:8 6çR´ñ\H¸¶½ Q¡«áp|'W„°õkWļG´•|~'ŠãO‡†Ôé帚µk¯kFߵ/x~ûâ·†<࿈w6FOx_áïŽõ¿‰¾ ѵ>e¾ñç‰>ü$×;|.ð·Š4½nÓBÒæÒ+¸t-sHÕä…týJÊâwƒO1©F–^ž:®&¿ÕpÔðíU1¯íª<7õj0¡Ï*µÿÖ,N!ö0R©ýµˆ£•òýz¬(IãË©T¯˜'£K mZØÏöjTðupXìÊ–.¥Jü‘†¦_–fXøb$Õ)à²üv*3t0˜Š”ý«Wñï|?â_ø3^ñ§„ôO|B}n?øSWñ¦ø—ÇxkLþÚñ~Яo!Õ Òµ-êî-FÆêÚ,^#²éç¯Ee4ÞP§š:°Ytàcšd xç/«Eç™dá˜åª¿´ð3Ž/íðòUª…w˜Ã(Tj¼Ú­\Ò<±S›ÌjVÉ1°yÕ(`­õ™ÕÊ1uèasHF›–Z•Z¥V¤"û¿„~ þÐ~âÀ?Œ ¾7økûÍ.| øá?‰^—SÓcÔ4èüKàÍ_ZÑd¿±‘•/,ÖôÜ[;*Ï1öÖÂâ°Ñ¡7œ`òn*âZø XšTè<¶Z9¾m†­RŽ'•ãpU%åS?8á\£ƾ0gØÊœTθ^ž_áf˜ÏÄÕ8‡ qV†á¬®XUõì>'2\)”G1Ë%‰Y¦q”âò›{|Áø¯Àý/Uøéã¥ý«üma©é¾f¡ þËÞÖ즰¹ðÏÃf;uÖ>1kºEâ¥Í‡Ä?ŒÑCšlööú·‚¾&‹áÆ·ÒuÏüE±½õ2JusÌwúÕ§VžÙÔ¡Ãðp–.¬£í³Šô§iC0ÎRNœjF5pYR£‡å¥_˜Â§ö¿Šì…¼,üáœ^ œ¼n5ñÏŠrÌM‹§ˆ©•a+â(B§Ö‚Tó\ß/£]bc‡†]žkJ·¢WÌŸqöwöú‡Ž5Ë5†7Ú‹ \Éo§s/ˆ®µ+»›k[±nudš84m2{è Úéò¥ÆŸ"Û5Ì 0öëáªa¸/¯)ÓäÍ3|Õ(ÕÅ{^L®Ž*µ¨û_©Êœêc±tðõ‰„©bbë*UåÙVuƒÎ¼_â쮞ñÁ|)†ž>¶#xˆã¬Ç=ÌñÙ~]™, âJx¼>…xœàå™Ã$ÄÒÇdu¡—˃ž&=…x‡ê!@P@|ûÿÉÅÁX¿ìÿþÿ묿àšt÷ýP@P@P@P@ðüOþM×â7ýŸÿü‹ÿ^›ûdPßôP@~ ~×PÁÛö·øsÿ øAð·Ã®> |3Ó|)ñWöÁ¶Ò¦øSñ‡Áú}×/4-öVø·Zð•ÏÆ¯†Úæ·â{-cOøyá? |{·½ñV›>ˆºþ©éú‚ùg†ŽYÅ++úÎ9—ÖÄâ2ܱ×ÿ_1Xú\5Ãø|p¤0N¦}<§‡£€ÉðÕ8o 1â †µ<Ï…Ìc€Äc»ñËR|0³eˆÊ!•ã(àñ˜ÉáWà#;ÆÔ̲^+úì©ä´±·¥ ÞrâŠÂVÈs O¯†Ãæø<»é¯ø$ͯÇ;ø'ßÀØ¿høO&ñ™µñýÏ…m¾%Iª·Å[‚×?|_sðMø„þ$h¼AŽm> KàkMj?NÞ%·¼ŒCâ‹“¯E©8ú~,XŸìü¾žkMâxšÂô¸ÖLgŠãˆpž_.Ž][ V†_õú¹êÆÓ«_ ZŽ Y·Ökaë,4©Ö~^ð•3~*©”¹QáZÜsÄÓàÅŠ§ˆŠ¡Âo7­®Q¥ZÅG*•«â2ZS„êSáú¹e(Ò§à Kñ#à§‹ôŸ øçö9øðgã/ÂßÚÓGðoísq®èŸ±Æ7ïø*?ìªø«Ä?><øŸñÆ¿>6ßišï„¾7Ž|I7Äi¾1ü Â|{¤ÛˆÞ9»ñ6‘w®ù9u,~; ’Ã)Åa1Õpgü;Ãþ"pôk`²ü‡†ðš®YÃÙ7åõèWÊa[0¯“`8R¾UW“æ¸.4ÄáqØLš®m‘B­GŠgGWŽkb)ÕÁÔÌüE˳\çƒñÒö´¸Ç6«â–;Ææ\ ŒSÁfÐÀU«8q¦Elß.\'‚Ä`s Ã+Èñ¸œ5Ÿ¾hßn?d…ß±_įÚÛöqð·ípŸµÖ—ãÿ~Í?aÏüBÿ‚”hÿ´&ûRËã}Oö‡Ô|WªÿÁB>XOá^Þͼ_¯~Ô¯ÁŸ |:¹ø«\Ùé7‹q¦iwþÏTÁbó¯ñ™=7ŒËx?%à§S–[*Ë8[+Àðm,7pÇÖ­S<–AO±™îGÅØLTkf¼Ožf•±YnOÄçx\%^~,ö˜uãW×?ØÿÖÌoa°Us[føŽ#ÂgXìT|=Çpm Ë!œ×ËðáŒwá°²†ƒ±Y æ8þ? ûû1þÏ? þ;~пð_xßÂú-âüoøñà?‚~7×N™jÚÕÿõ/Ø{ས”×îsöMûÅšÞ³¤ÛyÞ]–«{-ì;nH”|ä2üF7Á|¯•bç•f¯½âÛ-­uHÿj_ø4]C¿ü3ào€Ð|=ñž÷y~.Ôí¬µ›™/^Þø³†Îx_‰x÷ —ʆÅÎ!ðë1Æd°ï0ú‡…ü/˜å9÷ˆ.°xYañxø>9Ï|GàÌã+§<%lÖ^`!CÜÄáêÏ.ÃQáÞ-àΖ9<†²âŒËãëáa†­ŸñÃþ g¹­J*®ò°yGcž)Ô¥ƒ£âv!br¨WÃNŠÝý–o×âçÇø%ÿƒ~~Öß²ßÄ{ïÙBÏ\ŸUø;ûþ¾2øwãoÙãᬿ¼Eà|!ý±|qâ¿ø(OÄȾxsW×dÑ4i<â„ÇÇÞ(øÏ០jž6^ñ«áÿ´Æb#‹ã?8ž5©gyv7ƒx·&¯ÄY}zt8+<ÃææEŠáÌ.W£O4£™fùv7…Îøo#Àâ«cäX\öüN[KBµo‡ÃP­ƒàáì] Ù6hø‹‚óŠY&mJ¶#ð9®8ŠüG™U£7•T£C•æ¾]ÅœMÃá°Ù¿öÅaifxÌn/ÄTøðóáŸü‡AÔüO®xïàn“uÿø³ñ{Ä¿~x7Á¾*´øañÁ·÷ÄKÂ?~?økÅ÷vš7‹~ xGPð„l>(ϫëÁ¶V„±éZ?‡\Ð~Jç—åßG*˜<Ç‘ÖÊ<(ðë’ãsjtŸ àñÙϘ©æ8LíKƒxHñ=Lû:ÁeØÜ>+Yq†s•ÖþÔËëWX¨ý^&—ö‡}"ðøœ¿SÎ8ûŒòœË•N¤8—–áø¿$ÃPÿW½•D«âòJXj®'õ|ZÅä9nuBviˆÄýC÷üçâV»ñ/öˆý²5ûÏ~Ì¿´ÖŸáÙØÜÿÁ@c«xKàŸí®èúwÄ=_x‡Àן~7ü,ÿ…Õð³O·¶ºñÇ‹~ø×P³½Ñ!Ì«e¼7žË ^®0©Z¦g›æyëF†o–äíG‡§‚ÌòùÖý£Õì%Õ4ÛÍ>OQÑ¥»…¢MSHk4Ô¬X|û6¿³Ô,ÄËŒses Ìg‚>w2ÁÔÌ0¬,ÕÔÄÒtá˜e²ÃG„m§í°²Æaq¸UUZÉ×Â×…›¼¾®i–ã2ú9¦c’ÕÅÑ•(f¹D°p̰2m?oƒ–aƒÌ0J´mdñ8,M;7zmÙ®B/êQø‘ño쌷B[ ‘âu:“jGˆü¿ GæB"oì°4s£Iýšª¦Sy›ÓóTøKMämñ×ÕþǪêbJÜ8Ö~ž>XßežrpÜ9é*rþÏ_Ù+ŸÔ#º|Sù \™Ò|8åâOˆ¿°+:¸¥V¿ 5Äñy”³aÄ~Ï„iº”U)e%“¼š§ölcUãouÿáÕ³¯ì?áhüH2ÞßÛÞÇ©›ÿhéñÀ·*ÚuƒÂö5°º7÷ ugutZÖÛÉ»„ –|?ÔÌÃê8ÌüD:ö˜¼e \1þ߆>½‚…×RÁ`åþ«}Y`ëºÑ•uˆÂâqXzÏI*ª¯?üCü×û;€ÿˆ¥âOµÆãðøÚyŸÖ87ûG.§B8˜Ë.ÀKýKúœp—ˆŒ±Åàñx·,.Øâè¥YV¹'‚µ/d¼º¼™TÇTÆ/8ö•9ä“Ê~ú•[ˆT±˜¿oˆÄTt*aÞþÔÞ-z1ñ+ânæ‹i’ãÂ^o‡R+É.š×A-áD¶$S U5iœQˆæŽã}ÃܸC*yÄq¬k^•j#_†ý¦Ixª˜‡‡ÉܸfP… °šÂUY„3¯ N a[š¼î| ™Î–Mx—âw‰£ˆÃV†#„}¯–2®.X\…˃åa«Â¢ÁVY¬3jÏJš§^ž#Ÿ>ÇJ°—L°¶²›SÔu‰-á†ÔuV³{û¶ŠâiîZÂÎÂÔÍ;Fg˜Ãifid1Çe#O§Ë°u0:J¸üvgR…*TåŽÌe†–3éÒ…7[,‡uk8:Õ],5(:µ&áNå„~Ë*ÀUË0llÏ1Î*aèÑ£<Ç6–xü\©Q§FUñRÀ`ðG^¼©ºõ % NµZŽ*tÜ)ÃF»D( € ( € ( € ( € ( € ( Å#Ò|á¯x·^¸û.‰áTñ¯s€Lne>¡{"«2u··¢]ï…È&¼Ž Ï2îÈs®$Î+}_*È2¬Ã9Ì«Ù7K–ajãqu#(©J4(ÍÆ<ËžVîÏŠx“)àÞâ.ϱUÉ8_$Í8ƒ7Ĥ¤èe¹> ¾a©ÊPSœpøzŽs<í¦î| iáéhÇð"õCà7ÃoÏã?ÚW[Õµ¹ø¥ñoÄZêüHÓgSú»xSÂ×ZÕ¿ˆ>7ivºÞ‘%Šx;᛫hz¿ôÍ?óÉ¥ŽÊðœ;*Ù–')¡ŒÇñb³Xª8ìÓˆ¸“5ÄqMn¯Fž3J†.Åf’þ×ÀÐÇbp˜l=AF­|ÆS§û÷€ùú2x%‘x‡ŽÄæuükñS ÄœCáµ þ:Y÷äœwœg™¯ø¯š`V;4§“ñ#0ͳ.ðŸ‡ÇbáÃôðYÏexŒ-L‡‚3 gè¸@UU@  8_²Ÿ6Ûm¶Ûm¶ÝÛoVÛz¶Þ­½Ïøh#ð†·©¶›ñ3E¸ñ'ÄÏ‹ýÖ‘ñcPkÿXMuñĶʚh2̺.­ìàÔ~éQHcÓü{áè¡V~ãÄ ÎyÖ±\)ާ•ð§åÔq¼†X|§ <1•Uoù)ËÄ4jש†âœdãωâ*Fäš”¹p«÷r—-x¹×ÄMÇïQ7^¢÷uv¤ÒR¡”\·ðÊôjúŠuØîVæßVøƒãTµxî’î%ƒÃšÌÞ ÁâOYĦO I+[Ú¾ŠÐË$‰©øsF×µmÏáÞ ”eò¤éUÁðÞC*Ñ•Q›©š`až¹MTÊòºó—.kªµ£U#Ë šc²ÿªT_yÆØg—ã2,®T] Øá©×Œ¨KRUsœ¶Ÿ9T\›%ÄÔ—.w*؈æq«BXç2Êþ¡YzE|±ñ‡àýBÛYOk6«'•sâ½wLY&·°…ÜøZí¼'såËgm ×v¿Ú%ô–Óê3^^l•£[„²KK[oo;ÃUÀË,ÀÖqç¥“åø·UÄÎ1YÅœÒæ…zµ)Ñ­õlôð°¡Cš R§*ò­Z¯å¾g™Òãž)Ë¡_êøÿ8LJã_ÉpµkOÜÎ~㽆'*Àáqy–\³¾Íë`1yî'4Í=ž"tiãiå4²Ì»Ø×ˆ~¤P@PÀ±¿üœWü‹þÏÿáÏþºËþ §@Ð@P@P@P@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@PÄ´ÇÆ¿ÚóàºxïÇ^ø%ûk¿¾x6÷ƾ"ø¡ñÏöÚø¿ð+_Ñôh·׌5 sÁ^ýþ?èZf ÛZ]K¥Ä}FãP³„ÜϦ铱#𬣙æyµ:t0˜JT>©V¶7Œ áF4 °Ï ‡šÌñxÊ’ÁárÜ3ÆËUácGR¾+êô}<[S4ÆeynT«âó ÆtðïR•4!Ž­Š«J•*5Þ*¤*ᾯõjõq˜•‚TjTÄSC f#«ý†h_ˆÿµwì³ð—ö‰ø£ðJ/ÙëÄ?´)¼_¦|0Oê?eÓ¼¨ßÝ7õë¯êÿ¾j‘Ëã? ®•âè´]OÁ>©¡ÙëVºn« jp^A×çyKÉqt²úõ*hÓÀàjç:´è¦O›âpÔñ습L6/C‹ÉªÔþÏÌkQ«ìVeCBƒ­Fy—àêýZžw„…|6µ,¿2¯O_+ö´ý¦++úža%Eã>­CÐ<9ûPþÌþ0ø¹â/€ý¢~ø§ãÇ„"¹ŸÅ¿ü9ñoÀßÅÏ Ád¶ò^Mâ/†úgˆ.¼e¢Eh—v¯s&§£[$ snÒ²‰£-ä`ÓÌp•ó ½;|.ð·Š4½nÓBÒæÒ+¸t-sHÕä…týJÊâwƒO1©F–^ž:®&¿ÕpÔðíU1¯íª<7õj0¡Ï*µÿÖ,N!ö0R©ýµˆ£•òýz¬(IãË©T¯˜'£K mZØÏöjTðupXìÊ–.¥Jü‘†¦_–fXøb$Õ)à²üv*3t0˜Š”ý«Wñï|?â_ø3^ñ§„ôO|B}n?øSWñ¦ø—ÇxkLþÚñ~Яo!Õ Òµ-êî-FÆêÚ,^#²éç¯Ee4ÞP§š:°Ytàcšd xç/«Eç™dá˜åª¿´ð3Ž/íðòUª…w˜Ã(Tj¼Ú­\Ò<±S›ÌjVÉ1°yÕ(`­õ™ÕÊ1uèasHF›–Z•Z¥V¤"û¿„~ þÐ~âÀ?Œ ¾7økûÍ.| øá?‰^—SÓcÔ4èüKàÍ_ZÑd¿±‘•/,ÖôÜ[;*Ï1öÖÂâ°Ñ¡üÖÓFøÁñZXa×–›ªÃðëáÞ„’êž0ø¯iº´:mæ•lmlü9a¡]Æ·>.ÕuµÑôæ‚u}_Eü‹Å<Ó4ÄÔáNáŒÛ –ñ/ç˜lEgZœkÔ£Á¼;ˆÃæ\YŒT+`qØJ´«Qú–GR†1a£‹Žs*8lM,EªSpÊr¼Ë5áÄyþQ”ðÕ,ɸƒ?áÜ}\mëÅNà¬ã%Îø¿Ã.XlŸ6¡õ¾(Áb2Îâ¼ß0žUá~âÏ5§›G‰*p¶Ož{·Â¿†>ø9à/|9ðM¥Í¶áÛi•.5¹5-oZÕ5 »S^ñ?‰uyÿÒµÏx§\½Ôgìa0ï‹Âál>â±p댭>ƒ¯V4•lV"~å ='.zõ§îÒ§NZEŸ!'ËJÍò¦íå'e{E-[}Õ½6øAik႟ ¼»ø‰ž‹ðóÃ×7wµ[=Câ¼Z½íÄ_ux.§Ó¥ñ†›=¿‰î-ïgÓâÔ­®Å½Ü¶±¤ÍõŠæ­Š¯KJ?ÍV½HÓ¦¾s’Gç9îs‚áÜ“9â Χ±Ër,«0Îs Ïþ]`²Ì%ln.£ÿ %ò2|"KxcAã’o4«=Bâ9 ±·¸zŒ+vn¢Ó-¬ìÛÜÜJ÷oik2Ü´²¬k¼Šíά³|Êšœg ÊøjR…LEZnŽ£ÃÑT§‹«_èÆ•(FŠ­Z¤áIBo”ù¯ œªxyÁ˜º˜zØlFiÙVwŽ£ŠÁäøls,÷ O9̧˜a¸•d±ÌêãñØš¹•L·.Ááqùâ1ÃÓö­ygÝP@ðìoÿ'ÿbÿ³ÿøsÿ®²ÿ‚iÐßôP@P@P@P@PÀðM?ù7_ˆßöÿðV/ýzoí‘@Ð@ùÿKš?¾7ý‡?àž¾uçö7íƒû@\x§ãe£"¦©û3~ËZ øÏñ;Ã:‹<‘gHøâË?†?µ˜ÚKÍÅz͸†xÚT£‡c gåØŠª50~ðöiâŽ.H:”kg˜Êœ%j4 ’ä§NV„yc¤_òYðSÅúO…|sûüø3ñ—áoíi£ø7ö¹¸×tO؇ãG‚›á×üöÕ|UâŸ|Oø‡ã_‚Ÿo´ÍwÂ_ Ç>$›â4ßþGá?‰>½Òm‡ÄoÝø›H»×tË©cñØL–N+ Ž«€ð‹?áÞñ‡£[—ä8<7„ÕrÎɸÏ/¯B¾S Ù…|›•òª¸|Ÿ5Áq¦' ŽÂdÕslŠh_ÎŽ¯ÖÄS«ƒ©™ø‹—f¹Ïã¥íiqŽmWÅ,w̸§‚Í¡€«VpãL‹:Ù¾\¸OˆÀæ†W‘ãq8j??| Ѿ.Ü~Èÿ ¿b¿‰_µ·ìãáoÚá?k­/Çþ.ýš>~Þ9ø…ÿ(ÑÿhMö¥—ÆúŸí¨ø¯Uÿ‚„|6°ŸÃ:½½›x¿^ý¨5_ƒ>øusð7V¹³Òn/ãLÒïýž©‚Åç^ ã2zo–ð~KÁN¦,¶U–p¶WàÚXn6áŽ;­Z¦y,‚ž1c3Ü‹°˜¨ÖÍxŸ<Í+b²ÜŸ‰Îð¸J¼üYí0ëÆ¯®±ÿ­˜Þ>Ã`ªæ¶ÍñG„αبø{ŽàÚ!–C9¯—á!Ãî Ãae b²<;Ìp †3|~ööcýž~üvý¡à¾>ñ¿…ô[ÅøßñãÀüo®2Õµ«ÿê_°÷Á;{M)¯Ýçìš-÷‹5½gI¶ó¼»-Vö[ØvÜ‘(ùÈeøŒo‚ù^ *ÅÏ*Ì1^"xÏ›åÙ…J5p«ðwö)ý…|eðïÆß³ÇÃY~x‹Á0øCûbøãÅðPŸ‰‘|ðæ¯®É¢hÒxÄ ¼QñŸÃ>Ô-<l¼3â-WÃÿiŒÄGÆ~ qÁ㳟1SÌp™Ú–'ð‘âz™öu‚˱¸|V²ã ç+­ý©—Ö®±Qú¼M/í&úEáñ9~/:§œq÷å9–*Hq.7-Ãñ~I†¡þ¯{*8‰WÅä”°Ô3\Nêøµ‹ÈrÜê„2ìÓ‰ú†7îÿø%ÏÄ­wâ_íûdk÷ž&ý™i­>ÿó±¹ÿ‚€þÇV>0ð—Á?Ú#]Ñôïˆz,¾ñ¯>2üoøYÿ «áfŸomuãü!ñ®¡g{¢xËÂ~ñlZE߆t-×ÚÂQ„8K5—b¸yVñˆq”r¬D§[žKÃü)OÅù×0Ô3,[ˆŽ‘ãèTž#*Æç™FaË1˜Ì\óª”üŒtܸ›,ÿ… .ySýK¥‡Äb¨Óú¾aôp|C™VËxo=–½\aRµLÏ7Ìò)Ö ß-ÉÚŽ+O™åó­ûãoêÞ*ð¦½áÝ Ç(øk¬k|¶Z<ià«ÿxZæB¥5mÏâ7„<àkB ~'ðgˆô¦ÜÞ~™1ÚVòË“ç9vg˜dG`°X˜WÄðî}[>Ãäù½(ß›˜×áŒï‡8‚–¥ï9å9îWŒV^Ï oÛV©Np…Z”%$Ò­IRu)¿æ‚¯Nµ'%ÿO)N=âÎÿáoÅïoûDücҗľÓ|£Ea¢~ÏÒ'ÃïXÛÜCuñ_ÂTø©I{ãýfI£»Ô´ïÉã…±\Z[®‘ð×J´k«[Ÿ¡ÃñfCB9*«á—ãUÄx¬ó4øyõßZÕåÏÄ˵•"¸Ò¼Ww⃋¼&×á5´­q$æ‹2-,¶ ¸1žŠêq*¾3âL*ç9DãÇ3%—ø…¡K…(¸Êtñ™=«ŽªÍVã*°TãL• ²sk‰‡5F*1ÁµN¦¿íPçÂIºîúÆ£©†Ñ[ï-×Ã_Üiy?´Åû·|!á­¯Ùt_€ýã4û~ïÚMûoÁ Ï7âü¿l·ó4]cûWà þʰò¾GæjŸÚQGв*Uçðׂ±WãYqUomñ?^È¥õ{xkŠöC“‚¡ì*òãð_SñýsÏÇòåÂ}Tt*¾oöÌJ¾Ø+G îÕ÷¿Û#|+¾%Ý{²æÂ{«ý—âæ«eð»Ç·–—SþÒ5(mþ Áðºm:÷BýÒÎÿÇʲIûH]¾ðÃPOH¦ ô+ë/ÙÝb‘Í·À[yÂLš×âî­Bµ*~p.¥^:©ÅÐÄÐÌ"â0ÒàZ2j¥<ǯâcœR«â-Z|Ð’Xz©¦ñ¸©%…Xw [ÿ¾¾\"—֞Π¬l"z’CðÃÆÑ¾ŠïûEüd¸]+á4¿o£›CýŸx—Æ[ÛŸõ£À˜$ƒâÌA-ÜZ?‡dÐ>µÅåÊÏð^kU´¶µSâÌ‚qǨøcÀôÞ3Œ¡ÄøyC0ñ)Ë+É#R¬åáÖÚx‡R586¤jFŒñÙœsU:4>;…gZ­f¨U÷Ûq/—è»ÇïÕ²ÿl•°ŠØ…fùaÉ„»wµd½ Â:&§á¿ ø@Ö|_â/ˆ¶¤ØéºŽ<]má+/x²òÒÝ!¸×üAià? ø'ÁvÚ¶©"µÝô>ð‡†´(î$uÓt]>ÔGmÌçXü.i›æY–%Ë8oŽÆâ1X\ƒ%«œ×ÊrjªJtòì¶·fùö{W„‹Tpõ3|ï5ÌeN1x¬~&³•YmN2…8BU'ZQŠŒªÔTÕJ+9ÍR§J’”·j8BïÝŒV‡E^aa@P@P@P@P@Pš|^ø«á¯‚þÖþ x ^]Z馯ÃIд˜–ëÄ>/ñV¹}o£xOÁ^ÓÚHΧâx†ûOÐ4=]~ѨßB%’i£ós|Ó “`+ãñ\ó>Jt¨RJXŒ^*½HÑÂ`°´î½®+ˆ<= iûÕ*FíFò_máçç^%qnYÂYÃP¯úÖ+0Ís ’¡”pöE•ákfYÿg¸µ¬GÃÙF›fØÉF^Ç…ªá •]:SùßöeøQã3Ç_~3|j°Ð/~2xÞËC°Õ5Ý2yµü%§Ü5Ƶ?Á¯jFýíføuðÞɼ)¡ZκN—{â?ˆV_þ _"KãoìÝ3ó> ȳGqGñY•Ë3ÃåywåyÍzØê0ÇT–oÄy±•(<‡:<3¥OêLULó+Ïs õ*Ã0¥G àø“Åy?ø±ŠË¸; Á༠ðK†©xuáy‹ÂÊqNgŰ9ÿ<Ä2§˜â0´±~ g‡Ñy[ÀÑÇp惲f¹~C—U¥ö~¼xçü™£ø)ñFÝ"ø¿$šÇ‚õÏ Å/ÀâoztÞ)³“Ãqk_ æ¸"×Nñ¦ƒ&ªºÖ‡¬Þ±hw–ë7 ÚØÌÚøqMOxJ¬§ÁQŽ >ËóYÃĉMp&Exæ“Àqt)§[æ1Á¼a¡þјPÄËBÕ±g61Û _LKr¥8±ÛëIÔ\œÔ/¢«nxÉ鹞‰šnF‰ð£ÆËkª &àxCSÑtWQÕ|§µÕµKÑ4)æÕu?ü7¸žðê—–B7‰ž Õu ·H­¹MçXL~7†Áóƶ ˆXüœ0xNâštè,% C”W g¸<5)ÖÉ1ØJU0•>ëÃÌqœqÂtj`«fXjYî]Ç`©a«cjbrܳ Ç3ƒ£K…¸æR§ýŸ…ÄʽJ¼Å8j4•JøÜ‡5ÂS¯ƒ­èñF°Å(ddŠ4L²Ëq)XÔ*™gäži¼³I$²6^GgbÇåç7Rs©%)ÊSj…8'&ÛP§N1§N7~ì!Â*ÑŒTRGÈÔœªÔI()Tœ§%N:TÔ§'&¡J”aJœ~í:p8+FŒRK”ñÖ§™áÉüôšQ«ê:†"ŠÞ ©d¸ñf¹§xnö]N› ˆ"}P\^ÇwÖÿa†åæO-g‡ð•1y¥?g(CêXlÇ6œêÔÄQ„iäÙ~+5Ÿï°•)biTšÂ{*£R>±:Jçi?Ë|`â 'ð./ë”±X…ÄÙïx}†Ã`°y>c‰­Œñ'Œ2.Ã?ìüÿ ŽÉ±¸<5N"XÜÚ†eƒÄà–O…Ì*â©{ U%¹UQUB¢€ªªªªŒP0`À ñ[rnRmɶÛm¶Ûwm·«mêÛÕ³ôØB¡ tátéÆ0§NP„!£BJ1Œb”c¤’I%aiP@PÀ±¿üœWü‹þÏÿáÏþºËþ §@Ð@P@P@P@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@Pç¿ÅÛãß¾#þβìëá?þ6øàO|@øï©üIøñuû?ø_Ã0üMO^xÀÔl> üh¼ñïÅOèþÕuѤk:oÃÿ‡ú>™>Œuÿ‰Úe樶pÒÆJY~uŸTÃU—ä9Å~Ä×ÂJ–#9Ì3œQ“çùÆ%ÊkO „ÅQÊ2Œÿ'©ŠÄæÎT±ì–/§Œ• }løštpõòlµâ©Ç8ÏrúÙÖ…xV†[—äk2Æä˜L×:Ìpôñxœ5<Ç:Ë3\ —ey¶;Øå9†2¶•5Ž;éÙ‹ö…ðOíYðáŸíðößWÓü/ñ/ÃãW·Ñ|Cµ·ˆ¼5ªÙ_^h¾&ðŸˆ`²º¾±‹_🉴Í_ÃšÊØ__XKK¹kÛ»F†âOw1ÁÃ^’¡‹£˜`±¸ ¯8ʳ,4jÙä¹î[„Îr\Î:ð§^•<Ã*Çàñ‘£ˆ§OEVöUé¬'çakÕªñÔ1Xià³ «7Îr ÛR­òÀç+ø üe]+ìßlþÓ? ¿ÿ ¸ÓþÉþ•öÓ¡ý›ìß¿ó|¯š–ùŒ1Õ2ÿöúyd§ Êx?ö¨eó§*p©t¨s¬$¡:Ô£8×tÜeVœd“œS1oêTXçõ'öQúßû?×}·´ö?TöÜŸXö¾Ê¯³ö<þÓÙÔå¿$­ËøÓöÐý>ø£Qð?ÄOÚÃöiðt}VãBÕ¼!ãOŽß ¼-â/[´Ð´]èú‡õÏØêÖZ­¯†|A øŠãO¹´Šî \Ò5y!]?R²¸àÓÌjQ¥—§Ž«‰¯õ\5<ûULF+ûj ýZŒ(sÊ­õ‹‡È}Œªmb(å|¿^« xÄòêU+æ àhÒÃCV¶3ýš•<\;2¥‹©R¿$a†©—å™–>‰5Jx,¿ŠŒÝ &"¥?jÕü{à_ø—Áþ ×¼iá=ÆŸ[À>ÕüG£é¾%ñÄžÓ?¶¼Gƒô+ÛÈuO?‡ôoø›kk¢ÚÞ¶•¦§ßˆ-{J U­ˆÃÓ~Ó„ÀÔÍ1T!ïÖÃe”±˜<º®cˆ§ÎŽža˜åø˜º‘ŽÌv *о*Œ'œ!B–*rŒ0ØŒf/¡ˆ””hVÇâðØÜnJ«jLf'—f¼>•jØl6½8J–¼áç¿?iŸÙ¿ãŒ|oðïáíðGâ§Ä†s›oˆþøqñ_À~8ñÃû‘w5·ñ¿†<1¯êšß…'ÖóÙµë Ü3[•ó£tý¯.Žq…ÿiÊgUцi‡ýö]:Ê.£¥u>l4ª¨FStÕW>Tåk&ÊÄÿ±cc–ã?Ù3Su£€Äþã*)R“«-^Zîšj2uR«I·j‘¾•ûaþÉïü&'Cý©g=gþ߆u/ü@þÊøÝðÏQÿ„Áº7ˆ5/ êþ-ñÙÒ¼U£kÔµíkìZUˆ4­KFº»‹Q±º¶‹ˆÃ¬ºyÄ«ÑYM7”)掬]Ä8晞9ËêÑyæY8f9Eê¯í< ã‹Á{|<•Gª¡]æ0ʯ6«W4£O,Tæó•²LE,uJ+}furŒ]z\Ò¦å€ÄV¥G©U©¾ïáÇ‚¿´ƒãø…ðãÂïþšþóK‡Ç> xOâWƒåÔôçXõ :?ø3WÖ´Y/ìdeKË5½7Îʳƌ@=µ°¸¬4hO†¯Bª+†j5)GBR”Uzœb«QrŒ¢ªSrƒ”d”®™ÉOB­JÔiW£R¶PŽ"•:°J©RœkB2r¥)ÓjpSQrƒRWNç¨ÖÁ@P@P@P@P@P@P@2Yc†9&šDŠ‘å–Y]cŽ(ãRòI$ŽB¢"‚ÎìBª‚IIµå&’I¶Û²IjÛoD’Õ¶T!:³…:p•J•%S§¹Îs›QŒ!§)JRiF)7&ÒI¶|%à}ZÛöñt¿µ‰ãi~|$±ñ-çìǤ4rÊž/xt»»?~Ó—6g\§ˆtªx[à^RsûísÇV·¾'ÙZh_O3Ãc)ãø÷3æ|1Ã8 Ë1ÈiF©,]µLɽ’þ#¯†§_ ‘FÒPll%|Ò¡ý âÖu‚ú5xaÄþûJ”xû0É¥Ä?HlÏ <_e9)gù‚X)Bõ–#&­…ÃgÞ'R„©¼Garž­I©šù¯ÖŸ t(ô/éãìz…þ½q©xË]´ÕïãÕ5;_xÓRºñF¹eu¨Eg§Årºn¥ªÜiv^U¤6úm•½¼6öñD¾¯åÊ8gþËŽÁã3šØþ(ÍðÙž.ž?‡Îø«ˆâ ß ˆÆÒÂà©×X vc[/Âû<&•  †£B•éÇøïÃ<ŠžCÁùwûc€Ì3üFgÆyö8ÇÓÍ38ä5ûûSâh$’Ï©_jZº ¶°¹…`ðíšÓ\}¶ ä¶XuCJh.¬<‹Ø¯ ²­ÄqI2IíåØjßÙ¹Þe Fð¸|. Ru±ª:™ž#‘B—°©N5\ðØlb©Gí(J‡µr¥)Æ?˜q¦s—>5ð»‚ñTkb1yöoŸq=*qËòl~>ÉÞ"x¬wö®_ .yðåLc“}O5ÃæNž:޾&~¾¼Côð € ( € øö7ÿ“Šÿ‚±Ùÿü9ÿ×YÁ4èïú( € ( € ( Š¿oÚÙÿcßÙÿÄ^=ð¿…£ø“ñ£ÄêÞø ð›í nÿ¾$CáÝgĉ§4RÅ>àxwB×<}ñ+_ŽH΃à/ ë×ñ;ß‹kŸâLß–`qrËéÑ©ÃåY¾{ˆ«Š…Z˜,§‡ø õüûˆ38Q©F¬°Y~ÑÂá°ñ¯‡©›çù–Gø\Evu„™îän0Ça–cRµ,ºY†W—OêÎ+Ìó¼}¯$ȲéNjk3Îó,E,% µ(Ö¡—aV;<Ì)¬§(ÌkQøóAý¾?hŒ~ÿ‚iü7ø=eð7Âÿ´ÿí×û2GûOøûÆ>?ð—Ž|að{à¿Ãï |>øqâk7Â] âw_Mñ4¸B®E|¯ Ÿà&—Š˜¬& †°Òž"YëȪ`1+ˆãÔPÏhðž6¶Zù–Fo‰_ðSï‹_²Ü_µ?ÁžøQñ?ö¬ø)aû3joþi^/ø1ð—öˆƒöÂø‹7ÁŸ„/wáox¿ãG‰>Oá?‹6Z¦ƒñ&Ûþ'Ä{Cá«;èÚ¼7ZµÏ†ô)ÀƧÃ'Ápý(àó¼w‰¸_ sY¯àrì^'†ðÜm,Œðô2ÌF/*©Á‹;ͱ#Nž:¯ãò:Yž.8ŒkZ±0ŽISÎëJ¾CKÃ~&ñ2ž//ÂÇ ‹x^Ìq6gÂ󥉯â¨ÿjVÎjðæ /ÍZxZðâœ.'–`¡—â#ˆôëßÛ«âÿì•ñgÄŸ?o]cோŒÿ²OÆ/ÚÿÀg¿‡>xr} öw:CükøS¯øâÅúLj´ /Äžñ'„¼q¦øöÖ×Åú]Ƴk¨øÁ÷š5›kÞ~e›`°¹ã0˜l^'6àæ RµTø£+âì~o‘äxŒ%JrÈñ´ø—,Ã䙎]WžQ‚βœÆ†i8˃Âu`2¬Ã›p¼5 'ña”¡iˆŸ g¹~_€Îf`Þ9Þ]Ê1¦*Že «!©F¦E‰£S.©]:¸n_áïíÅûZx þß¿jíö·ø1ÿñƒ¾x;Á|+ñLø‡û1øóâ×Ãý{âOÁÍâÄï|Nñ„~>iÞ%´ÐÏ‚!Çfôø{Û¯—ÖËñÙv33Ìs̳G6ÉáŒÈ³¬Æv7ƒ« —ë¿ø#¯ì÷ã¿Ùþ ½û=ü+ø—‹ŽÎñâ/ˆ4ßGŸŽtË‹ßñÏìsð;àÏÆ_…¿µ¦àßÚæã]Ñ?b o‡_ðTØUñWˆ<~|yñ?â~ |m¾Ó5ß |0oø’oˆÓ|cøA„þ$ø÷I¶¼swâm"ï]ñ²êXüv%†SŠÂcªà<"Ïøw‡üDáèÖÁeù á5\³‡²n3ËëЯ”¶a_&Àp¥|ª®'Íp\i‰Âã°™5\Û"…Z§ÎޝÖÄS«ƒ©™ø‹—f¹Ïã¥íiqŽmWÅ,w̸§‚Í¡€«VpãL‹:Ù¾\¸OˆÀæ†W‘ãq8j??| Ѿ.Ü~Èÿ ¿b¿‰_µ·ìãáoÚá?k­/Çþ.ýš>~Þ9ø…ÿ(ÑÿhMö¥—ÆúŸí¨ø¯Uÿ‚„|6°ŸÃ:½½›x¿^ý¨5_ƒ>øusð7V¹³Òn/ãLÒïýž©‚Åç^ ã2zo–ð~KÁN¦,¶U–p¶WàÚXn6áŽ;­Z¦y,‚ž1c3Ü‹°˜¨ÖÍxŸ<Í+b²ÜŸ‰Îð¸J¼üYí0ëÆ¯®±ÿ­˜Þ>Ã`ªæ¶ÍñG„αبø{ŽàÚ!–C9¯—á!Ãî Ãae b²<;Ìp †3|~ööcýž~üvý¡à¾>ñ¿…ô[ÅøßñãÀüo®2Õµ«ÿê_°÷Á;{M)¯Ýçìš-÷‹5½gI¶ó¼»-Vö[ØvÜ‘(ùÈeøŒo‚ù^ *ÅÏ*Ì1^"xÏ›åÙ…J5p«ðwö)ý…|eðïÆß³ÇÃY~x‹Á0øCûbøãÅðPŸ‰‘|ðæ¯®É¢hÒxÄ ¼QñŸÃ>Ô-<l¼3â-WÃÿiŒÄGÆ~ qÁ㳟1SÌp™Ú–'ð‘âz™öu‚˱¸|V²ã ç+­ý©—Ö®±Qú¼M/í&úEáñ9~/:§œq÷å9–*Hq.7-Ãñ~I†¡þ¯{*8‰WÅä”°Ô3\Nêøµ‹ÈrÜê„2ìÓ‰ú†7îÿø%ÏÄ­wâ_íûdk÷ž&ý™i­>ÿó±¹ÿ‚€þÇV>0ð—Á?Ú#]Ñôïˆz,¾ñ¯>2üoøYÿ «áfŸomuãü!ñ®¡g{¢xËÂ~ñlZE߆t-×ÚÂQ„8K5—b¸yVñˆq”r¬D§[žKÃü)OÅù×0Ô3,[ˆŽ‘ãèTž#*Æç™FaË1˜Ì\óª”üŒtܸ›,ÿ… .ySýK¥‡Äb¨Óú¾aôp|C™VËxo=–½\aRµLÏ7Ìò)Ö ß-ÉÚŽ+O™åó­ûKª[ß]ØÏo¦ê?Ù7²y^N¡öHo¾Ï¶hÞOô[†X¥óbY!ùØló<ÅË É„«‡£ˆ§S…úî<þÓ íêaý¥á(Ç÷ÔÓœ9&ãSEïrò½$Ïs[ CJ®7õü49ý®ë0¾×šœãßÒR©OÙÔ”*û©órr?vMœ:xgâK…‰»ÞO;ÈøC4Eû>ø5ˆáùEÆ&ò'½Òn>|y¿Ø¾Sa5+’¿A,Û†œ©µÂ|±'´öî`ý¯-L§«§x{Jt1”ýÛòý{{ØjWú‰g| åIÇ‚9cgícþ²æoÛrÕ˧SÞt¯OÚÒÃf½Ûòi{HÞX:<Åÿ†~!\$Ëcñ;û=ßíþLŸð…è—~GÚ-üIË5‰³çÔü=sócíðŒy3mMjøÆaón§(ÓÙÔË%ˆÖÛÖiás*zûWž–‡1„ÎøBŒ©¼W}r1ú¯´úËšaý¯±­“OïS¤Ü>·K›Ñ÷oõí¿iNòËpªs^xwÇ“}»ìŸþÅçÿjý‹þ) çìkÿ„ïû3ýlãí_Ù?Ûþ ÿ[·ÿ¿ÿHÛÿ ^¯äåG3áè}_ÛpÇ·öTöÿ𵎥õcþ¯}oá¦ý—×?³³ï‚ÿWÿY?wì|´Ëœp­?ªýcƒ~³ì¾¡õŸøÈs*?[ú¿ú«õï‚“öÚÙlI®ßˆê§F–3ánz”£‡XŠ¿Û˜øýjTée0¯>EO–ÖjàóJܰmQþØöp¼rü;•SθJ4pŸûJ´a…Xºßëg®N• Žž&~ÍRåÃýn¾:ÄrÓmPþßöTï ¯æ·~ñôË0µø“öFy%h›þíãÉåÖÞ8öÉ8yQ_hÐïl4ŸØ^c|ÚÖÂŽiÃpux[Û(Æ kûsOÚIC¥;Æ›qçž>U¤´9V˜J<Å ç„éÊ›¯ÁXQ„Hÿ¬y•/k8Ã,ŒçxRnÒx\Ƨ,tö§"ÓC›¬ÑlõkY"Ö5ŸíË–¸yïû:ÛLòà1Ä«oäZ»Æû$Ieó‰ÞÞvÂ6Ƥøøúødï6k× çP@P@P@P@PÅŸonh߈W߲§‡ádžcÒõOڿĶÐ\hZ¬j~ýt­J3¶|NÓ^=WâwâÿÃn"²Í†§ñ?Âú¾Ÿñ™ÄçÄY„øc RQ˰ʕ^*ÄÂëš…XÆ®‡©TZ,FgMª¹Ÿ+ö˜l¢J»©™ákCúWì5øC ãž{…¥SŒó©ã°>丵 :Y®­\qã?SÞ©”ð>63ÀpGµ‹Âg^!Ñ©ŠÿkÀð>y—âþ‚ñ¼6žW‚>躴þŸX×´Ytø£_z~Šâÿ ´«Û½Oâ7Äx;Æ^ñO|e­x~ïÞ+ñ­¿Š­Ï…¾ø‡Ä^ðŠ<9§i¦§á¿ hß|7cÄ«}Mxõhãñ„IâÕ‹Äp^ØØý×c(QÂpÇ å¹æGÄYFC‘às*9¦O‘UÉêÿkñv[–qe9¦+„Âf™Æ;†3LEN©ÅFX9K$œ²g<®¦ˆæÃŹV­:uhÔ«Vpp©UT^΄çJ•H(ÊP§ÐJºŒ}ïÞ/ii¦–ÇŠ§[¿‰_ ôy$2Àž9ñÃYA¬Ïb÷ÖžѬ|%!¼Ò¬¼m¡ÜkºUçÄ]:ybÕ<㯠iÚÁÐõÆð¿Šaðf§qÅ“Ótx[Œ3B…Ipþ@±04ñ _3Çb3˜*ÌFC˜R˱•èpÆ*ž=áüÛ‚Y†‚Íò™ç˜J¡d4¥C‚øó5xhT§V\-ÂñÅUËib£„Äfù–+ˆ °øüW æ”r¬~# Á¸ÊP©âNÏ1™zÍ0xxç¹^$ÀÑõ ù#áŽ:ÞúÒÿÇš•‚Á#]xgÃ\†íítæ…?á-Ôu&šÎÖôÂÚ´W? Y\ê6Ëø}õå™ÖËò:˜J?ñóÜúx¬«.Íe…Ÿá±±¡á¾SÏrúxÚ3 á¬M\>3ƒ§SØ×†~¤P@P@~Æÿòq_ðV/û?ÿ‡?úë/ø&}ÿ@P@P@ù ûl~ȵ¿>"ühý£>üøÖÐþÊÞ;ø9ð×àïÄߨÿâ‡Æ¯x2ÇÄ:§ñ/þ‡‹< ûe|Ó ñÇÆJÓÚ]ö¯¨|,ñ6§ea០h:|7¶1jöz÷Çq6G‚üMÃK_9«Å9^"•Z9U*¸\ö¾I—e!—p–%›QT^m[3Î}¥ ¥ÕÌ3lÇ ,˘ÒȲ§È+ЫÅÔXŠyU>ϰ¸ç[0­ ù*Ï1Y¢§.(Ì0Éåµ%,»$•,¢…:ùŸ³ËðÏg—âréñ&s*ÿ þÍ¿hÏÙGHÿ‚B~×~üCøƒ§|/ý‚|Aû"þÐøû5|Y¸ø§ðCÃ>ðßÁ?ü$“Å߳Ɠ¯|iøÙã]oÀþ$øm¨ü<ø³ª|=ðÔ:È×õ›/Oð×Áþ³Õô­+ö(ÍpXo|[•JË…㬫(,ÿ+…\nKŒâÞã"Ì0ßVÃàiãëå_p÷æK-­Ìs<» ŽÉ¥O0ÎͰÂ~eÃùv+À†…„Åðwg9í ›1«K™«X &cʳœ»(ÌᆡC/Æã2lε »/Çãr¹<Ûóö]øÓÆ8ÿà«vŸ>5Êçþ ooûFKð< yÇx¿cöý“?áŽï|kÁÉ׎$øƒ0sñz„§J?¥ðløzËÁúgÞo7Ëðä×ǃòüÒÓ¥É9ðcx¢ßÄן¾ÅñKà Ÿ[×4»ÝJ]M÷D×.c‡ªÏƒ+ä¼A¢ñ?]ñªŸgyn[Z†kËø^ÏÂO®ª9mLT*æ³¥ÄüUÄ•ò*Š™Çö^S”àãG/αõ0të:ö\W…Ì8gV\:ð_83šãéc08Z¼oÄÜY†ãË…¥Qex,Gð†_‰Î©Ñ¯•ºœG™V¥‰ÄÐÉë){OíðZÿ‚²|hñgŠ|)ð÷ã¿Â/„¾ÿ‚w~׳^‘âŸÚà‡ÄÏÙãXñOÇ_ÚêÓÁZD.“àOŒ¾ð_ÄM[Ã_ 4¾¡âoXøZjzÞ¹¤éþño‰¢´Ö£´ðó<«‡ÈüOÍ2ùá19®—øq•pn8ª*y›ðÿ‹óŸq™ž/S—“añ™Þ…rl²ž}K/ÇV¥ˆÎñXŒ©a0øLMc*ÎèPÎ|/ÂbðøŠXNã!â¾2n›«õ .gÂø~ Ãd˜LnÔË3lf3.Ï8«‰ÆdŽkƒÁ<§/‚Æ*¹&M¥CñSöÊÑ¿à–_³îµû?~Ñß|kûüdø)ñÛö¹×¾)|ñ§€¾øYý›¾øÃÖðÅÏxEø[ñîçâGÅ[ÝQø ¬üCÑm¼&­¯ëמºŽÊÊO¾Æc²üßÅ ÓÄœYÓáÚY7‰˜œ¦–>”²üÛø—ˇp™ |—*yÆyWžç¹Žm™Oý‹S“a°Ùno˜G2ÃN¯Â`òÜVCáļ=ƨã3¼MNá™brÉÇ”ÿbpGðïc8¡ætbðtpù¾„ð8\«,©SûfÌéQÇe¸u—ãêÐþ„+æO¡?àŸ?²'ŸxkÇßµO~Óö¾6µÿ‚ÿÁM¼M‡á¯ÛwöÐð_Á¶Ô|ÿFý«íô{kŸÙÓÁßt/ÙòóÃ÷‘èVMâÏ Þ|0ŸÂ¾=ž}fëÇZ7ˆî|Gâµ@Ùê( €þ;ÁMc¿Ùûö‹øû#ø£â¦‹âÚ[ãßÅ? ü+Ð> ü>Ö¼+âoˆ~ ¹ñ^‰sâ 7ÆŸ<2Þ#Óõ?x ´è¬ä‹VÕ-ßUÖ?µôÇðÞ‡®[µäöfIÿuS$ʿګaòÎ$Í1øØ5,·.‡ å¬ç‚Æâàæ©æ•èagC—S…\K¯RL\0˜/iŒ¦fÏû&YægþË…­‹ÈpY}‰Ç›Uâ#Âpή[‡—+Ä`ðØìEzØü|¥KBŽ]Ž¡ ElÑa2ì^ŸÆ¯Û¿Ã? ¾/뿾|ý j_оøw¤üYø±áoÙâËàÕÄŸ>øŽóY²ð®³ãGã?ÆŸƒUÆ­âÙ<9âü/à?ÞøÇâ6¹a£^j–É­g¹áY…†Îó:ñ­†ÈxsOžq JR«—`3 à(æõ°Ãá~³œæ8¼MˆÃfÙ<£*ÌŠÁýfTñ8ìÕW R›É°êtgšqÆÔÈòmN†;0Âà1°Uóm‹–+Ëò×™V§•ÑÇæù–_…­XŠTêÊ,uL7Ñ?>;ü2ý§~ ü6ý > ëÏâ_†?ü-§ø¿ÁúÄÚ}ö“y>™~ZßRÒu8mµ'VÓnâ¹Ó5}.ú®ôíNÎîÎtÂÕîæyn')ÆKŠtg?c„ÅQ­‡« ølV 0ÂPÌ2ün¼\.;ŠÃc0Õ4r¡^Q„›Šòð˜¸c)Ö”aZL6;1Ë1˜lM)QÄà³<Ÿ0ÄåY®J_#™`ñX:ê.tÝZ2•*•)8T—°WÔP@P@P@P@P@P@P@P@|ëûC|]×|¥øwÀ¿ ìì5ï_ïu ü"Ð5§¹ÑôÛ»K/µø‹âgâ´’+‹o†ß t¹S^ñUÏŸhÚÅô¾ð&“xž*ñ§‡-î~{ˆ3jø X|[WÏsyÔÂå4*)N9ÂØŒË Ô£–唚¯Š—4iºSX¬n2ý‹Âr®,ÇçSÆØœ^UáG‡˜l&yâmƒ*9Ž3ˆÄû ›‚xbxˆÎ~4ãŒu9å9 O» ߊ³ <²ÎkPíþ |(Ñ~ x MðV“¨ë×¢çQ×|[ã-tÁ/‰¼ãÞKªø·Ç^)º·Ž(®ußë7× qÙiÖík£i6öz6›§XÛväùU›KJ¥LDùªWÅã+ò¼N?ˆ›«‹Çb¥”«âkJS•’…8òÑ¥Q§Nù8û3ñ'‹1¼M˜apyVÐÁå\?ÙZ« “„ø_(ÃÃÃü-‘P«:“£•ä¹u8Z2©9âq••|Ë0­‰Ì±¸ÌU{Ú?ÚµŸˆž'Öe“Ã׺'†´7Â^’Ö6ó^ÒõëË‹cÇð]êˆQÓ­µ+xüèEàŠIt(u+„¸ilͯÍåŸXÍ8Û?Í'<“”äYn†òYáé`qYÆ_œb«VÌøÊŽ'0Šž7CB et©Î¦QK^5L/ÕÿŸ²­g>"ñ>sV§ãrNÉòÎáê˜Z9n3?Êóìf#œxCšAO1˰ٖŸ‡ô§¹Ð¥R®EG3ÄC*¸?ªú-}Áú1æ?&×Ïo´Ÿ øOˆ·Þ,Õ|5àCÃQxñ>É„ügâ-/Ã~=ñJøÅjšwü ~Ô¼Iã£oᤗź°ðáÒ<,±ë·Ö7}gÃ-ÿXhcs^ aòlkŸá³Yð츪3Îr<³špîPòI'„Ŭ\C…Êø{Úæ²†Mƒy§×sw<¿ˆ¥S KŸ²q…'YÔ”)8*¾Ã÷ugV©í>%ì©JumÞK“–Ÿ¾Ó; økEðg†|;àÿ Ù.›áß hZG†´ 9$še°Ñt->ßKÒì–k‰%žUµ±µ‚$òÉ3„Ý$Žå˜øy¾kÏs\Ï;Ík¼VgœæÜ×1ÅJ0„±8üÃWŒ®áN0§[Z¥GpŒ"åhF1I-)Â4¡ pV…8FWnÑ‚QŠ»»vI+·s“‚io¾.êQ‰\Ûxkáæ’»ì~ÛãjÒN³YÃâ‰6ÜÃkà»)#šÿÁvn ¼Û¥ø›PIum/MöjB~ ÂÉÆ>×5âlcæ•TöYƒ9B¼òˆÞ”ê縈Ê|öºu(_”á¥&3öõià áî nUó¾0Ì%Í,=ªý[‡2|)Jž&¦E Ñ«_‰qP<'âbêᯎÈðr†ôšùcâÎGº…¦±7‰µ[T8>%Ô´C;ÛÙFî|0É¡\ÃŬ1Ü]ZÁ«Yê¦6Ôd¸¹†âk¸#‘-Þ½¬ã _ «ZJÿÙX\³\DãídóS•:Õ%Jj˜*ø>e†:U)BIÆUåV¤ÿ2ðç:Ë8§âåÔ¤âøÿ>á)c+`rœ=ZÏÃÙÒàìà G—á(c³¿ĹWºÏ+ã±øLv'3ÂP¯K,¥ÂaúêñOÓB€ ( € ñÿüñû¶ñÖµñƒBÒtßZøšÞóà§í ñ÷ökñTšžªépÛj:ý>%ü+ñ¶­á÷¶Öo%ºðž©âÏ ßj0éZÅöq«èZöœñü—ᧇ~üOÿ‚¢ü:ðž¥ñWðÿ‡oÿ}ƒPø¥ñcâŸÇÜkÿÁ2¿àœší×öïÅ/~2øñ7ÅUî§s™ÿ 7‹µìM=?ú7Ø\{ÅSáÇŒÿ¶WìÕðßâÄߨWö—ðoÃ_ƒþ?øÃûM|>ø}û4h^-øËðƒÄžñþ—àÍ_Oñ·ÅüWÔxŽÅôŸxÛA‚þòúïç35Ë‚ø“õÕ–ðö]ļG“q.MˆWÁUÍñ/Ãøª™ÞuCW ‹§„Ïr,FŽ[šàsœ¯3Ìéäx¼&jŽUF™ëPÄO-â…lº9¶gšðýg c½”ñu0¸\bãÈ2¹Wl4³<6eKëØì“–æ¹RÁñV 1ú¥|v`ç…Êÿ‚(ø‚mcþ ƒû3/ü"ƒt? h>:ðƒeðŽ›®Ùè^:ðÿ‰2ðw‚~0èún¿ªø—\døÇáMøžòÜkÞ!:½çŠgÕ,µ]FÏP¶™þÛq•±XL»:ÇQ¯Ãøü×ÃÞÏ3<š†u«pv/ÁyV.¯à°0Ã{wO†á(`rì¶xIã(a°Øl¿N¾.YÕùŒ¯ qÆ9= cÎp¹Wˆœi—axƒZüLßãq8Üç‰öŸWž/›b³ –.…H`ëã°Ø¬Ua(TŽùð[ÂMð/Æÿ±ÿí}à‚ÿ´ñ³öµ |<ÿ‚þ̾(ñÁßÛ£ã«ñGÄ>Óm>~Ýþ)ü›Ä?þØ5úx_â†,þ0Ūü.¶øskâkøxNÒÛCð²ç˜åxL› Ë0Ô2ìóá&}W(Âe8Š¿ñåLÂj¹þ‰3¬,kaëQÍsW–VâÜË›N!øQáÏMŠ/,­ôÍ*ïTµöxrž .μÂåu%G*Ïòn Ææ8«›3«âFE›ðe,ñœG‰ÅÃ$¥Í±«æÙÆcŸñ¡KsܲY®à±9ø®RÅ/¥ŠÖ^CãìS_7_Ù8~žSÅaü8Åä0ÃÏ3«ƒÂácÆå2hË1ñ™ÊœðxÚüi‹ÀbF~~Á?lÚWþ ¡kñGÀ~ñÄ?|rð·Â¯‡ž?ñ ”zæ±ðjëİçÀäÆ fÔRãþÿ‹›UÖmnõŸxQ4­Äš¥ëךnagÎ`èæx? r¬Ë…«,ÃÄOsœŸ4—*¯Û…üQ§ŸpνvOì*¬Û"ª–#/Æàsl?eyõ9áj¨ÐÄb±Y-\^[†ÆW‡Ö°X|V**¸EÆ{oñ‡Æ¿Ú[ö«øIñÃö®ð.ŸªÁñûþ ¥ÿÜøÅð«I²Ð>˪ɤÁIüw£ëþý¥'Ñ4åW¸¹ñgÁÏü"ÒÇ„–HÖâk?ŒBk}3P‹R·‚çN+Åe«(ãn=ɧ[,áßs xw œëWËñ™'ƒøüø‡â-æ8uˆ©–ÔæâÌŸ‡3LËOˆÉ³Ÿ 3ÈFøÌ©ÓIJʸlû€<6ÍÕ,ã4àÜñ&o<^ý“‰ø—'¥Äžø7'áêØzK4ÅâéæY4³ Õøv•zÙ7 â²|»¥•á2¬>3 æß³ÇìÏð‰ÿà•°øÓBñ?Ã_ÿh?Û“âO‚uÏŠÿ< â¯øOã&§ÿÁAþ&Má¯Ù+ã_Š<-¾+ðìÉñfãÃðø+ZðìzÖ‡ðâ cÄ÷“êzn§ªøÏX°ñ7Éápõ!“x’eØ,»4§ŒðËÃLïÂY¬g ³Ž³ã±òþÙÆ<>2Ž+9áÜ?œp…LÛ ›¼%^Ê2,§-ÄÑ£‚Ë—ÕãñßxÿšcqXü²YñîU‚â\ªÓ̸ ãnÁUÌò<;­F¦†e˜ÓÁS⚘ F¥L&?0Ï1ùŽ Ž\ó éoüòÎ/„ÿ´/í‡û3ê³w‚ÿe/øÂß³ÏÄüý˜>(éß?a+S㫈šl7øáåø+ðGÄ> xöçÃן>xƒÃÅ©iúO‚F:…LåŸXÃà)VÎø.ž:•|š^ʆa„ʸƒ2ÁSÇqWZ…:Ø> …lel› šÐ©W ÄYnM.zµ«d©Óý£Ô"¾žÊæ-6ò ù#+kysfu-åÈÄ’Ù‹«3p dÅÔç>`ÇÏnŠÏOÓí#šûUÖ5;§ƒMÑtm: OYծ촽6ÚæþòÞ 8³à •`qYŽ:¯±ÂàèʵiòÊråŽÐ§N έj²q§F8Ê­j³…*q•IÆ/é¸7„8ƒø§"àÎÀ¼Ëˆ81¡–å¸_kKIÖ¬Û©ˆÆbñ§…ÀeØ*«Ìó,eZ8,·/ÃâqøÚô0¸zÕaῳ×ÃÏÝj~!ý¢~1é’é¿~+éö–v~»¸Šø|øMiw.£áƒÚlЖ´]f3:ø—⾯`Y&ÄUÏsì*ÇcÿÚñ°¹ž?„Ã{HQ§C B† †Âa(PÂÑøß òš¹We5q¹$8w<â­qäðÌ1ù²Àqob«q'`ã™f–Çb¨à³ŒËÁûZxzXl ‚Áàð86‡ï+ì½ÿ € ( € ( € ( € ( €>ÿ‚iÿɺüFÿ³ÿÿ‚±ëÓlŠûþ€ (ˆñÃ?‡o¼©øûáÿ‚·YºØ§„Â`&«ö˜¨à=·Ôp^Úw©õLÖqTÃó{?·­ìak>cÂÞð7›ÄÏàŸxOÁïãOê~9ñ‹ø[ú?‡ÛÅž5Öâ´ƒYñ‡‰›I³´:÷Šuxl,aÔüAª­Zþ++HîîåKhBLca0ØÅGƒ§‰¥„ÁE%„ÂÒÆã±yž2– ¿sBž/2Ì1ù†&¡×ÇcqxºªxŒMj“™%¥%Çøww.­ž9ñ%£Îtˆìd)¡x1ãñž¸º¹½š6‹ÃúÐÒ,¼©ÍcÖ ¯âËD¶Š$š…—ç|~ð9«á®ÄÔÌ¡>.ÏpÓªòØa&á”p¬©ñNl³'Š«MÒɳE–a8[W ORâ<4(S¥í'ŒÂþUâƒË³·Âc*æôêq×á*V–QOQÓȸ*tøÓ¥âø£Äº­Ž…áßh5œÚ†¯®kÚÞ©=®›¤húU…½Åö¥©ê6öV6pMsu‡‹¼-à |MñÕ÷ü'í~ÿiMáKž™¦iºµíž§¬ÛÙëó[éÚ>¢S±Ó®õk}"Þí´Í.7O¶ú.8Îpy¶vðÙ.oÄ™Ç pýpen+ú¬3Œ? a1x¼V …|&­|\ªâ±ØÜ{˰˜ŒU\mZ+‹’–*¶8jr§Nõ)ѧ^«ö¸…C›ÙºÒŒTš”’”ì£óÉEÉE>Xü+ի㎃;N÷þ¼Ö$›ÏþÜñõ[i…ÃÝG&•7µètŠgÕu¥h‡íô½¢ÞüÙŸìû&ÇìúEÔñ…8áóz8ÃÙÿgä|9ƒ«f©J8ºy]<ÅN jÚU1wö˜o·ÖqÜGµÆâ>ÓiG žáòèSö_Ù|9ÂX ÔÝ(М1ôøg*©›*”ã€ËZŸöµlw7µÂ}eéõ¼VaŠöÙ†+sÇ:®•á›ç»óLz•Æ•á¸Ö,n¦{¿jö±Xíu8§Ó5x·½‚{i—1ÏѳFÜ?„­ŒÍ°ñ£È§…§ŒÍ$êTÄQ‚£“`±¶!ʶtñ4R¡‚©ûÚ)Öƒ´©Ô„Ò’þñƒˆrîðÿ7­™ýbT3ìw ðLO™bªæ~$ñ>MáöON†]Ä|fG˜Ny¯`ÓÁfØ(ñˆ4OÙwá.½q üBø£¶½ñÆú[gPø1ð,j ¤ø‡Æ“.åÓüã©bÔ| ðp܆1ø”k¾8K=WJøm¯éòüŸc1X¼EÊkʆa˜ÑúÆc¥üL›#öžËŒƒZSÇãš©Éù¶Äû|j…ZYv"úÂÈø{)Ìüsñ*¥šð‡æ+*àîÇ+a)ñQkŸ%y_ K€Çñ¦S‹‡Ò ðo†>xKÃ~ðV‹cáÏ xCEÓ¼;áÍ MˆAc¥èúU¬vv6vñŒ±A‘ËË3îšg’Wwo£ÁàðÙ~ ÁQ† „£O‡¡Mr•QP„"»(¥«»ní¶Ûgã—[ñW~üOÓ‰ï'Iî­ïõ)4­2÷ô Ãâò¬·ˆ¸÷êÜk„¡ÃÀepž-e42?s÷‹Åpå ß:äu°´qyEÅøê8LºPÌñóÊeN\68Ì]LCIÒÂß '[š¥Zãí\-.UYÓ§{IÆ­\<\§xGÚ&Ó“Œ_º×çÇY“¯jk¢èzÖ²ûJé:N£©¶óR¶s]Æiíb ˆŽã-Õ´xÎùá\Ƚ™vãó ^øÌf šæ½ñáE[’iÞóÓ–•Y_á§7h¾ü«ó<Ó-Ë£~lÃÁ.^w+âñè.UN•zÞ¦œ”+M¿†•IZÃøu¦M¢øÁ:MÄ’Mu§xOÃÖwsÌóÉ4÷i6‰wq4—L÷/4÷"Y¥{‡iÚGf•‹–5èq>.î$Ïñ”ãRÄç9j0‚§tèÔÆV•tãIF”iÓ¤áFœU5¥¢’=^1ÇSÌø·‰ó P…:8Î Î14)Ó(S¥‡«˜b'B•8PŒhÂ*.éÆŒcJ0ŒU4 ’âKËFÕü ÜDÓË«ëRßF†ÓO»‚8ü9aq¬ «•¿Šfmõ´Ákw`±ßÛj2ÙM ñ*ÈYet+¬w˜Óš§ yKÛbhÔœ³LM,£IáçQÕÃOí¨â°õpЯ Ó›q·á|{šå“âo ¸7†©ŒÄq7bsz4^[’fx:8&ÆñDs|3œ6.¦8<óÃë.ÌrhPÎpå|«…Æá©Ó®çØWˆ~ P@P@|ûÿÉÅÁX¿ìÿþÿ묿àšt÷ýP@ò?íeûn|ý4? Þ|WÔ<_¯ø×Çו‡Â¯‚Ÿ¼ âo‹¾/jºBZIªiß ¾ø&ÃSñ7ˆ²×PÓγ«½½—‡4bÚ%í«KÇ,[ž5e¸,&34Ìþ®±³ÀeÔUjØlªèÿhcêÔ,&[€ö±8âó N…Z°«^.™×OÞ¦a‰Äa2üº•zxYæ…u‡ÃZ²¢£9Gôïö`ý­~~Ø>¼ñÿÀßÜëvº®ÞñÇ…×RÔñXJÊ–7RJ–3 ‡¬ý™ÍG•ñ:ÔqXÇ 3Æe¸úÂã°Ôñ>Õa«N…Eïá±.…u…ÆP•l)Ю°øŠ¾Æ§/Ò5ÄuP@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@P@P@P@P@P@P@P@P@P“|lø»£|ð¡ãMONÔµ)´*J¬!Fœ¡FŸÉøq—f™äµ8‡)ʲN*Î0ÿëeÙ7<°n-Ïäó^!£F½J•jc=†eН†ú즾µ«N–”©áéwõ§Üž#ð»^³øâ/üIѼ[àïø*-w\øqà+½ Ã2Ùë½øyâKÁt GÅw®Óø„?Å_êV.—®‰o'…íbˆê7í~ÿ}Źu~Ë8…±Ù6yçÓËòþ(â*9†m øÛÄÙf?àlË “Њ§–8ð~u…ÄAâç[V9½iÏê´ê,4yhMVZñ©J­%9Фá J›£9RÄÂU³ÿh§$ùRŠt×ÄÕÏn¯:6ø¯slÞ¸ðä’§ÛL»åØLdÝOgÉ…Æc0ôq\µn½ƒ©JЕd×"Qœ ­ükøƒþ -ñçâ_Á½gâGÿø+bðŸìIûø+âwˆ~2ø{ ìì¿jßø)·í­øÇâ‚cßø9¼Ö~ ðïƒü9áæøsÁïÛÚx‹J†þ͵›ÕÕ´››šûŒÆöf}™æ™qx‰Ážø[Ã5kTÅá|KÊ2¬&âέ¬U|ni„Ͳ¿øWÂÕ£ƒÉêØ`(Æ´k|æNÖqG"Êx†¯úµˆÎr>'ñÅ΄V¿„™oÖr®ážŒ1žÓêËsjyæcVžcÕâ?g ½Bu+áþ£ý®ü0¿Öµÿx'ž,ðÝ¿…|sâ¿ø?Zñ¶‹;+Ä—¾±ºÕtK™X‰¿°õ ›½6s$BP–Ιö—g™æ_•b~·•á3¬Ò–¥G†£‰–Žn Bu1XL6R«ïÂ×Á%äpæ3™p÷æY¥ašcr,¦¾>‹§*O‰¯„†/…TçyÒ…f+Õ ?ÝÎ¥V×<æß}^IíP@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@P@P@P@P@P@P@P@P@ÓCm ×E¼I4óÍ"Å 0ĦIfšY ¤qFŠÏ$ŽÁQAf iJJ)ÊMF1NR”šI$®ÛoD’Õ·¢Z²éÓ©Z¥:4iέj³:T©ÆU*U©RJ0§NNSœäÔc§)I¤“lø¿àìþÑÿ-ÿj/[8øcáDÕ´OÙ;E¸y :ž‹«Y6—â¿Ú6úÅÖ4‹Xøm5ÿ†~<«<ú_Â.»eslÿuý.Óãrxˈ³ñ>"/û3 ªÑáJ2nÕhÕƒ¥Šâ)Á¤•lÆž,ošT²Žj𔵱£ý)â5Z^ peo2šñ|qŸKšxû™ÑŒLg€ÄÇø7†ÅFS•L»ƒkÓÂç|oJ•,wˆnžU‰£^<”ã±j×ÙŸÍ@cñ1 Öcð§nô[T°ñωí¬u‰ôÛ…´·Òt_ÙÞxÊöçY¸–Æú)4ZÙxJïNU‚ëR_y÷v`Kymð{ 9¤8w„19fc˜`ø»ˆ(a3:¸ÑÃQ˲¬“ ŠâŒ]|ÒµL&.L·2­’á8oŠ£ˆÇG=öT18kTÅPü¿ÄØPÎið§bò|Û5Àq×a°9Å|·&)É8wŒã#ŸGÒ"‡KÓ.uýLÝê÷VZ|QÙÛÜ^Kºˆ•kÄÍñ˜lÃ4Ìq¸ö^ŒÄUÀekŠÇÇ+ÀJ¬¾£–SÆãgS‹¥—a=Ž –#9W«J„'VNmšSŒ£FRç’ŠSŸ*<íïMÆ>ì\åy4´MèoWœYåÚ{üGÔ5E˜Í¡ü=·ŸÃÖ1¥ÄÂñž« ýãGaâ©,în4­[&(üCàÛmKI:ž§'‡õÉluÍ^Ù¾»¿±ø_ ƒtÔ3%«O3ÄNTàêG"ÁÎ¥<º‚ž#(zTñ˜øb1“–[žUÂã Ë FU}Ö.+ àÜ&TÕ<Ó‹ëRÎ1S•*n¬xoR­§âòbhÒÇæpÅæ'”q%l`°Xfù\1Y^_Yz|‰ð§!á‹Û=ZÿÅš•´,¯¿/‡ä¸’ÒœxzÞi+»Uû]í¥¾£>¤‘ÿhÈÓ[]5ô0"[yfOo6Ã×Ááòl-Y¦§–Ã28ÖÄÎ4ÿ´êN¬%:5Ÿ±¡^¦žSú¬T*ÑXyÔ”ªórþaáöo•ñ.uâV€ÂÎ0Ük‰àšØÚùfK…«‹\‚ÂåøŠX|Ï.‡öžm–`³Ì^}JƒÏkO€Ìg›ápt¨à=„«uõâ§…P@P@~Æÿòq_ðV/û?ÿ‡?úë/ø&}ÿ@P@|iûcþɳí'ám/Åÿ´T~&ðÌ¿4¿x«Â¼ñwâWÀ¿ü ±¹±°Ô|]ââOÂïøC]Ò,þÇá6ÿW‚ööóE¸F¶“QÓîc¶ ^Na_’ÇÄձ،žx,¶k™ákV„¿³p²«Š• F1­CJ2©ZT°Õð˜©:•¦°ôýµEO±™‚¥ÃÔ0”sJY®etò¬E UéâsD«à²ú”œùjQÄÃëõèÒ­Fµ%^qœÜ%$4+ýµü=û#xkàgŒÿfßÛ»Äß´7ÂŒïâ¯Ú'Â? ¿oŸØÆ®<ðÛÂ~4¸ø{}ûRxûö†ø௅?þü;¸× øÙñwáÏÅsXÑïí¼Dó_ØI{qyô¹rÇC‰rÎͰ™u*¹~†cŠÆZ Wáˆñë­O"áüN? êðÖ[ÄYÓúæ 䵲ܹC*ø|Ó§_ áäâ(`keÙ¦gÄæ5iâ3LÏ'ö)¾+§ÅXžÂ`sœî¶WƒÅÕŽmž`2:pêØªYÖ"­J›ÀÓ¯[ ˆt¿±mP][EÒ5U¸Ó¯SÒì5»Ñîö“t·–‘\‹.õ’6¼Ó§y¶WF47Í¥¾Ñ–6‡Õq¸¼/³ÄRú¶+CÙbéª8º~Ƭéû"Ì%ÂØyKû:„)×âºðm)akG› ÃШ¶¯šÁûlÖJt2ˆºrQ–g†«éo ¨Óðoƒèøó›Ð¥.2Íq8Ü«À,§Ntsܺ¤°ù猜-[©åœˆŠË¸9Ö§,>iâXc)J½ÎðUþη··´·‚ÒÒmmmaŠÞÚÚÞ$†ÞÞÞXá‚cUŽ(bV8¢U#EUU ¯²ŒcÆ1ŠŒb”c¤£¥e¥¢Ih’Ñ-üÛZµ\EZ¸ŒEZ•ëש:Õ«VœªÕ­V¬œêU«RnS©R¤å)Îs“”äܤÛm“S3 ò¿ÜAâ_‰Þ2×,õÍBâÇÁ? dÐ¥Õ®—mâ[¨´ÿx‹UŠóíÏe­¼úN±àÝ*6]:´+Í'\³ûmÃj6öŸždÕ¨ç¼Å9¾7Æ×Âpž À•2w‡Äaòü>}ˆ§‚â¼ï1§‰úܰ¹´ëe¹Ÿ åДp4ªäø¬»6Âýj´±¸Šoʸ‡â_øÏ=Ág¹†#Àù~ÃJ™ Âb°¹^‰±T²þ7â,Úž3ëÓÁg•+å9ÇeTåºl‡”ç¸?®â%˜bpø?T¯ÐÏÕOÔµ»ünÑ|¢ø³ÅZøkák_ˆ>:ðý¿ƒš_ xçJøŒáÊv„’Ÿ³WÃ')ãÜx_î¯a§Ë«êëVZ:´¿ÚÞ"Õ'‹MÐ4ë™tMÄz†›¦Ýj×V‰¬kãDÔ,|5¤}¿ÄZ¼QèúUüñ{YO7Ͱ˜,F&,”ñ–:sÁÇêyfœñYŽ*”1øü¯ ŠÅRÁÑ­,\ñølFkú¾Y‚œ±¸Ì=9ûÜ1“ÒÏsÌ_ŠÆS˲ùN¦'6ÌjTËáýŸ“àiO›c(ÓÌó<›ÆÐËèb'—e/3Âb³¼Çê¹6_9æ8ü%*“x;ÃßðŠøkIОíõ»H$›UÕ%Hâ—X×u ‰µ-Zš+kyµnòÿT–ÚÎ ki.ÚÞÆÖÖÒ8mãŒï3þØÍq¹„hÇ FµHă”¡‚ËðÔá…˰IN­H`ppøHU¯R®"¬hª˜ŠÕ«Ju%GœogY†i\F"µGhR£F©V¬ßHS„e9>‰6ržšÒ÷ÂZ.¯cjöVþ#¶o y¬4ý2é_Å3ËâZþÓKD³“É©3ê3¯™=Ýéžêò{‹¹§žOcˆ©×¡cðXŠÑ¯W+¬²wVœN.Œ£“Ó†Y†­‹”«¼*Ž,57ËNNt¨Â8þoàÖ/,ͼ2á.'Ê2ê¹V Žòúž#ÇŠÉr>Ì©Öñ‰ã|Dó¬·‡iRÊãŸU¯ŸT©žbáíñ™–k,^cšc1Ùž+Œ¯×WŠ~œP@P@|ûÿÉÅÁX¿ìÿþÿ묿àšt÷ýP@å>økã¿ÁO‹ß¼isygá‹ÿ ¼wðÇÅš|ëmiáÿøcTðƱuerà¤vÖÄÖÒÈ q̈Π‚‡Ä¹e,ã"̰q‹.U({jyŒ£ Ã/ÄàêC„ÇT§VtéU¥ƒÅaèâjÑ«8R­Nœ©Õ”a95ìpök_#ϲlç Ebq^iÇÑÃKšØŠ˜\M:Ñ ÜT¤•ggxÅÍs^)É#øàðm·À?ø‹ãïìÛ«ÿÁq?a/þÖ_´OÃßðNoi¿ Ž¡øö0øyaâÿ x‹áßÃk7øƒwà=göŽø§¬xª'»ÔÆ×ž³ñgÑôk»•±µµúŠ.pv'…<8Žn•JY3ŸeØüÇ<ãiáÜ(ⱘ]„ÂBŠŽV–#*ÂÇ5«õz•ha´x;Høwàü?Ð>Ñýƒào xwÁÚ'Û&ûEßöG†t‹=MûTä)žãìvPùóm_2]Ï»®q™â3¼ß4ÎqjœqY¾cŽÌñ*”\i,F?WYS‹mÆš©VJmµ&Þ矒åtrL›)Épó\>Q–`2º*ÛÚT£—áia)N§*Qçœ)FSåIs7ec«¯8ô€<;ãoí;û5þÍ«ûG~п¿gý3ÅÓižÔ~6üYð«jVÑ ‹?B¼ñÞ¿ Ûê÷Ð@DóZXIqqDHñªÕêÒ­¥–Ò© ¹Ž" ¥ 9ÆxÚÔÝXÐU)abÝzuêBŠ”)Ê.¬ãNüòIêèVXj˜ÇF¯Õ(Ô*¸§NV¥VpHS©^ÞÊ'N•IÆ’”¡NrI¨I­Ÿˆ?¾|%øpŸþ*ühøOðÏá‘iW ñSâÄ_ø3áÃÁ¯Ƈ:xãÄzƛᖋY3B4©¨SQ2Çö6›Ì\˜º”ðˆa1Ó† V¼°Ô°Ø¹,6"¦&1”凅Î'^0„å*Q‹¨£IÆÑmF 2Ì©N¾_cèB…LTë`“ÅR†“µ\DêPö OJ•[Tàþ)"Ë|mø0Ÿ OÇwø»ðÁ~Âb~2·¼(>ù~oü%Gâ!Õ¿áò¿yý¹ý±ý™åüÿjÛÍ^e|žs§›ÿÂUHT£Ftó/ö®!ÁP¥8â½”£R»©MQƒJU\̯ࠥ9ü+û5•›¬êÆöûoµ•8Ö?«{^wJTê*ª7tÝ9©ÙÆVgߎ?>0ü>_‹þ0|-ø¥ðªHõ9“âo߈ñ¿Ãç‹D2 fUñŸ†u}OÃ’a—ûMƤVÀÅ'ÚŒ[„òº+™ÆYvXXc£_„£,DåOªâ=œqNPÄ){)$Ú›I‘…«OVt05!Œ¯Oõ:”p³Ž"¬1žçû,éÑsœq?¼‡îU}ø{¾ò¾wÁÚ+ö}ý£´][ÄŸ³ÏÇ_ƒŸ|; ê­¡ëšÿÁ‰Þ ø£¢èºÚij¶«j¾×5Ë;UXfm>òxnÄL²vN’Ã׆ ÑÁãcRx<\©Î8l\)K’¬ðÕÚöXˆÓ›å©*Sš„½Ù4ô U§^+*Ž;(”â±x9ÍÍB8¬3~Û)ºuUhAÉÂv¿+µ?~Ó_³Åø×á_ÂÏÚ à‡Ä¿‰ÿ ]£ø‹ðãÀüã/øÒèÙ:x×Áþ×õ/øUÖõZÑ—]Ó¬ Ýn@”¬p·Ç`?µp_í™[­õu™aÚ0¿¿û®Rçú߻©û¿iÏû¹éîÊ׊ÿaÅÃÿcÇÔŒ§OŠÿgÅÔ„aN¬§ 5^JÒŒiÕ¥RRŒP©NmòÎ-û}|ÿÓÿ“uøÿgÿÿbÿצþÙ÷ýP@P@P@P@P@P@P@P@à_´ÅÍWá—†ômÀÚL)øÍñCZOüðŒìßd¾ñ=Ü=ÿмHb’9í~ü8Ñc½ñ¿Ä F[„ðö‘6—¤‹Ÿkàçùµ\³ FŽ”qYÎgY`rŒ$¯ÉÀñ¾u™f|S˜Uȼ7àl¶\Qâ/ÒQúÆ#ÃÕ,.E’ûHΖ#‹¸Ë3ž†8KR2£<ã1¥ŽÌ ›.Íq¸^‹à—ÂM3à¿€l|!kªßøŸ\»¿ÔüQãÏëBøH>!|Bñ-Ój~.ñ¶¼Ð"B·ºÖ©,†ÏO¶Xôßh–úO†4;{-EÒìmº2\¦žM€†5jbkÎ¥\V;ZßXÌ3 LÝ\^6»ŠQS­U¾JqJžŒia¨F(Ò„||AÇx•Ř®!¯Âä™^ ÈøW…ò×Sû#„8C% °\=Ã9R«)T–-ÀÂ?XÆW”ñ¹¾g[0Ï3JØœ×3Çb«úÝzÇçÁ@Œ<[eá[;ÚkF×üI¨x7I»{Ô]{Å6w—–:s6c©ÞÛÙ$6WZ†³©Ças‹¢Yj:Åâ K˜|×ñ&‡p¸8J®YÎ{þÄá|·ÇG‡V›Ã®í~Ç^ÖµÉüB!×.õ X>óÄ:0Ë3Ú|+êðF\%ŒÎx,Ï'âŒ~_™æXœgÏ;ç,÷‰Äc§ƒ†mƒ¶]ˆË°}<µÔÀQÃV©Ë„nt{â-‰—·§S ‡Äç9ÞwŠ­‘á¨ÖŒ2œ=JØ|^´¡ 0õåÄá°ô©~òŒréJ´aÔoàðö“àrþ© ¸Œ>IÃùžuRtÒÌñ4(â08¨}[ÚT¯F­`p¸ªÓ«)S¨³*p¥R¤èWkîšðO  üÙø­ð¿IOÚcãoÇÿÙÒóágíûmi³÷Ãw¿³‡ÇÚ'Mø{ðûáÂ-gÄ~5ñL"µÓ¼ð;ãgįjõ&¼þÒ“_Ð?áø§ô­. cÃvþ¼ÔkÇÄPÆ«\kÉð˜LË Å¹Ý,7æ8¯öª¸ZXËhÒãlNw‡¯ˆÃa*b3¬4³F_‚Ì0´(``¹—R¸ÿ‚‘]~ÊwZ\W²e×üSKñ™ð¬úRÞx/ˆßðNé¿iWð…¾mZgØï~?ÅÅ‹Ý!TÙiþ<ˆøZµ¸½Ôä–o#Ãhªø^Äbx®᥌¸MÉÊÃáøwráúØ9]Bœ8o&ãn;ËrZê.L6åys¡_*¼/¥âèÇ%A9>&ËþŠ˜n&‡4§ E!Ìój9ôñt›|Ò졆 ÀØLÛÚ]ãð˜Ž\W´ÂfU©×µÿ=Ôµï þÚß¶_ÁO ÛIoð/ö—³ÿ‚7§í3¥Xؼ¾žçâ‡íâ_ƒž9ÔµûM5!ß©üNøMáá÷‹#y£Ö3'ÂVð³ÆÃÀLˆ˜Œ¶”îe†«Çœ5ÂYö*Œ£<&6ÇOŽ£Qç8…Š×k×Ëò\f{—Õ­G<ÿ‰gñJ„±4+T†%a° d¼9—ãï *ª¾Sq·Ò˱Q”jaéR’ÁΕL¾¥õü{Æ:ýœÿküsû8YI xÇ\ÿ‚7ÁGÅvž²·²K‹/€–_ |Að7[Õ-ì-•#“Έ¼[â;^ß+E¦¯ˆu}Gò?¶®¡ŸäsJ“Ædþ<`1Øš±Êó\‡Á|Ç4ÅNU$èfÜAâ6{Á¹ÞoíT”ÿ´ñ¼ŽÎ£‹¨§õÌv#ÂՔ咄ðÿI‘`°p̼öXj*¦Å(ȲÌ# Tqy;àgñáÅJÞÊ9e~#áî„iF ƒÄbã9EþÏßðoÆ¥ð/I²ðö³áoÚ{öLøgá›ß é&ÇQÔþ|fýŸ¼skñ»J¹· .±w¢xÏDIügãX/.g3ëE¯‹uùn5 o£ýo>”¡ôˆÍ°1¥ 8_øíøì #„¥Ãü/øœã†p>ΜcJ– ‡³ÎᵓEF8|Jt0øXSxˆÆ_“ðõJ¸Ï±¦"¥LndááÃ1¯ÏW_‹óÿx;-ÎsOoQʳÇç™gñF0p’«ŒÁæXÚ û(/è²¾4úÓñ‡þ óñ÷â¶…á¯|0Òÿb/ÚÆž Ÿþ ?ÿ6Ñ¥ý¢ü5âÏØ¾×àÝžâ?ø*7í_6±âËÆ?µß„ÿh6ðÿ€¤Õ/­|Ymið&ëÅW“øsYoøkÆ–Óøzã]ýž € ( € ( € ( € ( € ( € ( € ( € ( wÅþ.ð×€|-â/øÏ[°ðç„ü'£j>!ñ½ªL-ôý#FÒme½Ô5 ¹H%a¶¶†I"¼€‘#ÈÊÏ‹Åá°\F7Zž „£S‰¯UòÓ£F”\êT›í¦ô»{$ÛHö8{‡ó¾,Ïr~á¼³œñfX<£&ʰ4Ý\^a™fá†Áá0ôî“©Z½HA98Â7r©(Â2’ù¯ö}ð‰|mâMcö¥ø«¤^hÞ4ø¢¦ƒð«ÀÚ¼F;ïƒ?$¼WÑü?{dí Ó~#üF¸Nñ¯Æ‰¼Øu;o x iol¾i—·?7a18ÜMn(Í(ÎŽ70¢¨ex*ÊÓÉò75ZŽpmû<Ç1’§ÍìîªÇ €“œ2ÚS—í^.ñKÃ96]àWælˆ¸G3žkǼS—TçÂø“â”0óËóß ‰Š‡×x7ƒ¨ÏÃ^ª‘öupU³þ,§ 6'1Øj?[WÖÏ¡@Ú¾¯¦hV¥®kWöº^£ØÝjz¦¥{2Aga§ÙB÷7wwSÈBE¼¼²»*)5Å™fX ›.ÇfÙ®3—å™fÌ1غ±£†Áàð”§_‰ÄU›Q§FN¥IÉÚ1‹g›æùfA•fYæwÂåy>OÅfy¦e­ >—àhO‹Æb«Ôj¨aèSZµ$íE¶p~ µÕÅ`±Ùvk  2¹ðýL»Â\-ŠËiξ'W<Éòî(ÁñGÔÄ{n&Ëñ>×arü—'Ãàþ±Ì³×ÓkïÓøä—:Ï„ì~[ÃñzÕ~/ëR|4½ñŸÁ[èt?|3Óµx‹V¿ñô¾.•ƒø'NÓmt)4¨|Sb²k6:þµ¡[ø}½{¦ÈŸuáü©`sœGÕ©ÁUŸ`cÅt2.<ÃÏ0Éx¯‚Í2̇!’Á8çØ¬U\Â8Ê™F!LjËp\Ê_ÙÔ1Q—6.ò¦¨¥‰_YŸ°up­B¥Ê”«:øQJ>Ñ{ês‚‡¾âÏg¯…:Lø†ÓžÕ¼Cz†ht»G-RæÂÎ[û¶e†ÃM·»Õnì4Ë{­Nú[}>ÖmFúÊÂ;‹˜šòîÚÜI:zYFY[8ÌðYe *sÆW9V•,Exaè¤çˆÅT£„£ˆÅÕ¥„î&´0¸zø‰R¥5FZ®4åëdY>#?Î2üŸ %N¦;R¯*8¼L0”"L^6­ ^:µ ±uéàð¸œTèѨ°øzÕœ)Ë'áöáï ÙA­\ÏjrÝxƒÅW¹ÔV;Ÿk³¾¥¬}ŽÛTñ/Œn´½*ÒêvÓ´= ?ë:w‡t;=;AÑïHÓlQ;8—2Ãæyµz˜j†W„…,·'¡l+•,«/¦°¸^®*É(âñ•éSX¬Ã1–SÅfy…|Vc ±¸¬D¥èqvm…ÍóÌM\¶’Ãä¸PÊr5°ntrL®”pywÖkà2^¡ŽÌ1),fkšÏ$Ëq™Îi‰ÆæÙŽfÜT¥Úׂ|ÉÇx¦òËíþÐníÍÓkþ%‰á¬tûûh[ÃVÞ,Šúò=@7–×Ú%€±¾²GÔ,u©´««c–÷û™E ÿVÎó5%—eSŒä±œ5Y¬×‡É§B„°Öö’«‡Çâ#ˆ”pØŒ 1”ªóóF•OË|EÍr—x]Á¹– Y…N3ãì5\5å&s€ÂO€rlßĬ>ošPÎÔþ§G/ÎxO&ŽQœejçy?b¸s0À<7°­ŽÂö5៩P@P@?ñ¯â_>øVÃÄ>ýž¾0~Òš½çˆ-tküÖ¾h^*Ò´ë;U¾›Åš…çíñÇà‚dðýÎi£Ý[i~1Ô¼TúŽ»¥Keá«Í&sTÑ€> ÿ‚røËÄ>'ÿÁQ|]âÏ„ÿ>xƒWý¿üý¡ð·â–¥ð³Wñß…þÁÿÊÿ‚ri–¿Ûº‡ÁO‰>\mÙY[x‹Lÿ„kâ/ˆ¼­WÓàÖ²§5ÌŸ»·ŽÇIJé·/pÿ$"3+|ªkæøÃ™â¸g:ÃåÝæ°5!FžºÂã+Ón/†Áb¥(G ŽÅaU|> )Áa±Uh×s‚§Ì½î¯•ḗ Äçp…Lž†s–ÖÍ!V“ÄR–ž2ŒñJ®0©*ô½ŠŸµ ¡7ZÔÔ$åÊÿ‹mkÁ¿ðKÙe˜>|/ÿ‚r~×þÿ‚ÇØx 3ÂZ·ìãûChŸ´Ö…ûgG¦%¬_¼Eû[àðV£á‹-'õÿˆ—ÿ/|ªxHÝjRi²h÷ §µÆUþÑâ,·á\&G•áøƒ*­‘Ê¥ì,Ÿ†xR–i…ž+,âܯ0…zXnŽ++Î2¼^1ÄâëJ®«Š­ ë粸G.ÃT¥âú©ŸN¥ Râ¹B¤³ÜO㮾#„±Ys­QVÌqŠ8ž¯‚–8)KWÙFyÒÛßûÚ|?ð-¯Ä«Kï[x;Ãþ7½Óöý‚óÅðè–Qø–êËj¢ý’ãZ[Ù­¶¢/’é…QÀÓ=©–UÎóй-9Òɪf¹…L¦•E%RžY<]i`)ÔRnJpºQ’“rRNí½O'‡iætx"£ÍÔÎidÙe<Þn¤j¹æpÁPŽ>n¬…G,RªÝH7·ÍÓLìkÊ= ‰>4þÄÄÿŒ3~Ð_>:üxý•¾6ëôï…>9ø‹û=ÉðRMGâWÃSÕõ¯ øgÇÇ‚Ÿüy/…um{Z¼ð¿Šô èž;ÐWT¿°Ó|S—w=Œœøl?ÕµèÓ¯‰þÌâ F dê´¨à±Ù–òê¬+὆m–ærÊÜr¼F7'Ìòêøœ1©W.Ëëaz+×X˜å½ =\^Aý£O'Ì*BsÅ`ðy­\&'–8NrÁãrºØÜ Â9~c„Æa¨cž#‡…*˜ÜkÄpSÿÁ1~hß>x3áŠ>/|ñ¯ìåâøÛáWÇ߆>'ðÖ«ñ®ÃÅ¿eÔ.>4ë¾.Ôþ/ø/â§€þ(Oñ–óT¾Ô~"ØüOø}ã #UÕM†¯§éÚV§¡èz_UIÕŽ7‹ÀTþǧ‚áJ G.Ëhᣕ® ÂK-«‚áǃÅÑÅÁap8¼£.Ì0xÚr†s‡Ì¨UÌ)ækŽÌjâù©Æ2Ãæ”sóš™Ç®3Çã³µ–aS‹aO1ÃQÏáŒËê`*ÒÄÐÀfÙ†U 7¢¦K‰–KˆËkåP¥„†¥ßükàMïÀ”ø/q⟌/âH¾5CûMÅûGÂi§ÃEGûMÛß­ü×ÅgÃGÁð™D¨š*xhü>? Áª¿‡ÃŸø@@ðÍ*’¿ WÊTr p… ÏðËb±ðXö9Êâ<!gÛÎ(ñ$¸‹=ÄgÒÏ%™â3niˆÇÕ¯õêxlNÒJKˆVf–v¸²m>"úüVæ0Èã‘G T呬¢YL²Âü:òzÙË+àªdøZôê¼DñUqYÁ8þêŸ ÿh?†ß|Uñoö‡ÖÿjVðûüqøÇñ_ÄþÓ~,x¥ü ðÐèwŸ|!ðŸÀß aøYqm­ðöÇág€ü§h>"^(6w~#ÔõmVþ+•`ò輑äœGOŒòìnYV»Ìhñ:¹UUÅ3ÆfUs¸ŒÍÇ"ɰžÇíršy^[…ɨ崲x< ª„êCŠÌ1r†i½‹­‰œáå_ ™d™–‰¥>úÛáŠ>ÓÚÇ"ža[)¯¯C^5sLÇ©džß0ϳ ß0äË0ô©â©Óž"5ø?ßðM¯…ß|YðƒXÿhŸŠÞýš­5[/Ù_àÇÅÏø\øgû7Á«xwSðY“À’øgá§„>#ø×PÒ< ¬ê¾ð¶³ñçâÆkÁþÔ/4Ÿ j:B]O$ž<ËñüψžiÄ9žS<‹0â\/ö¾+(­ŒËqø¼%Ha)Uó^O•Wͳzy\3ÜÞxi昵ˆÆ¬O6#†«F-ey,s¯õ†Y[z9MLÚÄUÃV:Ï‹úžŒÅc²Ìšž2&[«O‚ËhK—ýSôJ¸N“àø&Ÿü›¯Äoû?ÿø+þ½7öÈ ¿è € ( € ( € ( € ( € ( € ( € ( € (áÛ›ˆÿl‰ZE¾™ºÿöVø;âuÖµ}`Æ­ þп¼-¨Èš&ƒ£9”¦¿ð“àþ»h$(këߦ“°I}áÿ bRöV2‹xüQªO§XKüµI¤Öa6öÞU­ä-#]¤µ¶{kˆ¡Ã²Æó8áó,ÝࢣˆÄAÔžS„§‰ÄÂxhÛ ^y¶j­^zÔ*(ª*­UÏòê™ÆQ›xÍG†½ŒkçáÒâœDªå9>"8*!q/#ɱ|ê·>{–b«ÇÃÞ*ÂË€X|»5ÂʼóâkåÙ|0ý…x‡ê!@P@P@~Æÿòq_ðV/û?ÿ‡?úë/ø&}ÿ@P@|ûY~È?íG¦ø_RÒþ3|wý>.ü;:Ì¿ >5þÏß5ø§Ã2kÉcý­¦xƒÂ÷‹ªü9øáZM+L}WÂ<#â]*SeúgöF£·PN9añ4q“ÌrÜuL2®– ң˳ % Õq0ù†WŒL-w‡«_S ‹¢°ÙŽuë}[JkB§\+ОXv–7ëÇMsÔÂcp˜¸Ó•ŠÀfXYSÅáªJ”¥Nµ N¶_Œ‡$qØ,R¥GÙþuAûÁfîµÐ5ßø.ö¡'Ñ3%Í÷‡àœÿ³ƒñŽûG.Ài‹ãYu}SÂÚVª q x¦ÏÀ’]3[¥Ôz\3Ë"vÁR¯ÿ#(Õ–ÜðÊ«<µV•¬ê{IÇW œ½÷G.W-#RèKŽ|ôSX EKOg,Ê 0ö I6œbð«Übáb-$ª9OÚÔ§’ý;ý—?e¿ ~Ë Ôü3£xÿã'Åßx¯X&ø‹ñsãßÄ¿|Oø™ñÅc‚À꺮§«M‹áû {;xm4ŸxAð§‚t+Tò´Ù´·2O×_NX|6ƒÃeÙvxŠ˜l&U©'_é&|LÑ/.4íWãÆ«§\Ig­|+øW­YÉÕ—ÃK+¨§Ò¾'üOÒ§ŽÏï€|z¡|Kâ]3â«V­Åõªà°UjPáŠ'G2̨ÎTêçµiɶW•ÖƒS†[ ©RÌó:RRÄIO€šK‰§ý=–å¹wÑÛ.ÀqG`0y§Ž™¦ ™ðOæxj8Ì…8 ebrÞ<ãÌ· ”1\mŠ¡R–?øJt²zSÂñoáeÍ’d˜ï²´]GðÞ¥ø{ÃÚ^¡è:g¤hº.‘go§iZN•§[Çia¦é¶‘ÅkecekVÖ––ÑG¼¤Q"¢ª±£FŽ,>•:(S…*4iB4éR¥N*éÓ§£B)FŠQŒRI$æìË2ÌsœÇ›æøüfišæ˜ÌNa™æY†&¶3˜cñ•§ˆÅãq¸¼DêWÅb±UêT­ˆÄV©:µªÎu*NR“oN´8‚€<·Çºåî§rß |â! xó_Ñ_U:ÄZ|º”žð²j–Zf©¯€`¸Ó­uÉá¹¼‡Á‘kCì7úÕœ÷[j:>§j=ãßÄK€8g;Y7g9T³™ÓÁTÇχ8z9†˜g):5°8|Úµ*øª\-O5_TÆf¸jµç‡Æá2Ì~þYÇÙî74ÄÏÃâ5Ãü{Ÿä“ÍžqK.©™Õá>†i‚Ë3N IÐÄe¸\ò½N6‡ÒÎ×ÔqùÞ¾"¦1Àäùžú&›¦ØéqXi¶–ÖV‘4Ò,–Öö‘î§–îòàÁk0 ï/'žòêD‰ ÷SÍ;ƒ$ŒOÛàpL· OÃÐÂáéʬÕ,= 8jn¶"µLN&»¥‡§JŠ­ŠÅV­ŠÄÎ4âëb+U­;Τ›ý-Ëp9F–-Âa°XJR¯R40˜l>“¯Š¯WŒÄ:ZTh*øÌezøÌ]HRƒ¯Š¯Z¼Ó©RMÞ®³¸ÂñF©¨èž×µ}C—ÄúÖ›¤j7š/†-õ-3G¹ñ.³¬²i>µÕu«›=N»×um¥ZÞê—vÚ}µÅÜrÞO #C)Âaqù¦]‚ÇfÊp8¬n†?6«…ÅãieX•¡fg[€¥_Š£—á\ej:5q5iQœ(SG¸©)F”`êN1“5(ÅÔ’O– Rj1s•¢œšI»·c‘øEà»ø LÒ,ô ¯ \êú‰ü{â?Þx¯RñÍÆ•ão‰Þ)Ö~#øòÌø·V’KÝvÞø§]ŽÆìˆmÁm­´Û; 2 ;+k3ìGq/_2¥›ÒÁáržË3*>‡éã2Ê0‡_2|xPá»»-SYñ–¡mÙ­5« Otö6Kt4M6Öè¤wÖÀÞßÙZßk:Œ.£&ëKÁ¨¥´1C!yý¼ÒŽ# ÈðÕª^°3ZtcˆÄÔ…'ÅV¥Í*Z¡‡¯[ÃT›ÃFÕ¨}ZUªN¤yiþ]ÀyžQÄT¨føË6Î2¬·8âœ÷ ††y]Ï-ÍwK/ÂáðµÝl_a^!úˆP@P@PÀ±¿üœWü‹þÏÿáÏþºËþ §@Ð@P@ÁJu_Ûs@ý•~ ø‡öñ_ìÛàŸŠžÐ<_®x›Å´Ëx–×Ãz/€t¯ø®÷RÔü¬èúÿ‡|9Ὲº~µ…} ø‹âmìŸ ´‹{}FïÅöí§¡’?˜âŒN?¯Š¥‰†,¡€ÆÏZ…|&7†'Ú`ÖYv/5’ÉpðåxÕ‹–e')a•{F} ÐÀâó<&‡©ŠÇbs,¦†[Btq8œ²­J¹…bif¸LºÎ1xj¸yJœ0ùD¡Ž©9Z‹”ùbÿ›Í#þ ‹ÿ ñ¿Á-À> ÿ‚ÈÁ¹ >ØŠ“ÖÅàò|LFX¼|2ogR‡b0î¿×jeÒ£ís¡²=´Kp#Ú%V<™›¦ó,ÁÒÀË,¤ñØ·O-œêTž_Mâ*r`gR²iË P”êÅT“¦å4¤Ú3É]G“e.®cO7ªòÌ©›RP,Ò£ÂÒçÌiªR•%O+â`©ÊPQª”$ãfn× é…P@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@P@P@P@P@P@P@P@ð»â¿þÚ(Õ>ü0Õï´ÙOšÕΉñ“ãu=–¡ñãXÒ/×]ø%ðwX´’)àðµÔè¿>+éS£ê º‡Ã¿‡×ÃPÿ„—Äžøø¼GâªåÙeYÐá\%iQÎsz2”*gµ¨ÍƾI“ÖƒRŽ2Œ¨æù­)'S÷™~~Óë8šÖ™^C”}ò<qÆ_…ͼzϲÚ§‡æT)bpžeÙ†5ò¯|F˱©J·W¡V–eáßæ¥–Œx» ðŸØ¹.m÷V‹¢èþÑô¿x{KÓ´=CÓ¬ôEÒ,íôí+IÒ´ëxí,4Ý6ÂÒ8­l¬l­bŠÚÒÒÚ(à·‚4Š$TUQ÷ThÑÃÑ¥‡ÃÒ§B… p¥F(F*T©ÅB:tà”aE(ÂJ1ŠI$‘ü±™fYŽs˜ãó|ߌÍ3\Ó‰Ì3<Ë0ÄÖÆcó ~2´ñ¼n7ˆJø¬V*½J•±ŠÕ'VµYÎ¥IÊRméÖ‡P/âÏèþ Óí5 aî Ôµ+ÃÚM•¼·ºŽ«­kWiga§ØYÀk‰X´—W,«åÙi¶·ÚÛÅees<?Ä|K•ð¾ ŒÌçY¼~i—d¹v B¦+˜æ¹®&8l …¢¥VµFÜñ‰F<˜LÄÊž ˆ«Oåø·‹òn Ëð™†qðöµ¡é"jÖž ñ–¨ÒÝxƒZ´°·±¶Ì··×Öz”ïÞÆ &¥-Íùµó//&këÛ¶nNÉ3\§-ö¼G˜áó®(Ì%Sf¸|% ÔÅbñxlŸ/Q§ KÈò%­É£Ž©_õ~|N*¬±x¼L¥ÃÀ¼=dyB¯Å¹¶ˆxÏ4•LOçxL¿Ã^®7ŽÁäR…c%Ã|4³F[C2«‰Ç¼/´Æc+OŽÅÊ]µ}YöÁ@!ñOA²ø‹â?ü6Öüáø%5ýâGŽSXñGØ/|%¨ü7×ô¿ü×­ü+k'Û|Oç|Rð¶™}iòG [Má‹›ËÉ.nlí´ë¿¾áÆ¿ å|GÅ8 û6áüýåØîáù`r¬áóœ/å¸Ìƒòê¹Åhû §“„s|^´ðîyXfÔ¨P*Uêâ¨òׂ­:T'JZ\ñ¯Wš¥9Pœjᦩ­gzôÔ•ýÄé¶îÒOÛëàN£Ê¼c+xŸÅþøykqØmÄ9ñìQ\Eö‘áí:òH¼)¢L–ž.Ñ5›8|aâ»)%–y¼5ã k¾ðgŽ÷‡!‡³¾/¯JZ¬êð· Ît§ìqŒÃÂyþgNxŽÌòÜMNÈ10…:T³®Ïò¼ó‰8_ˆrŠøÚ9^a†=V¾<ø  $(,Ä*¨%˜œ$’xI=)¤ÛI&ÛvIjÛ}vÉ”£Ês”aEÊs“QŒcÜ¥)6’ŠI¶Û²Wmœ€¯­5o èúõ”&oÀþ&‡}žŸ<±x†i5x'º·±u-½ÜMqpwOu!7N÷2ÊÇÚâ<=lsŽË±ç­•TYMKb+âiÂyd#‚©N\CuU¡5Nšµ:1µ*)R„ùƒ9¾[ÄÞp·åW…ËøûWÄ /µÊ2¼ˆÃq¾*¿`ñ™Ž'ŒprÌq8,Ï S“ž31®åŽÌjÔÇâ1}}x‡ê@P@P@~Æÿòq_ðV/û?ÿ‡?úë/ø&}ÿ@P@q&»·ø{㻋¯Å è<âi¬þ5Æ‹h¿®£Ñ¯^ßÁ uâFO[JC3ëκ¸ù—î¹±maÖ——ï_+å¶ö;ò¸ÂY–_™ƒÊa,n3ÍkÍåÑu ¥ŽåôËêªõíC÷Ï’ÔýûÈßí+ã?~×¾*~Æ^ÿ‚|øoñçâ/†µŸøC^×?j/ø'›âO‚^9¾³–ßAø­q¥|=ñ%÷Å].÷á­üÐø¶4ð¾•ý½tÚgöu°ÿNpÝØ\'5Æe¬Ÿ:†K_(Ïò\Ýq%5ññÉá“æØLÃUÐÁF¬±^Û …­ƒ©ªžòÃã!S :´ås &IYÕÎr™çN¾ÃÕ^óü>7:0Ë”ñ³£J”1“©MCƶ òã°•)â°ôjGúèøy£øƒÃ¾ð7‡üY«[ëÞ*мáÄÚ奲ÙÚë^ Ó4[+-gV¶´UEµ·Ôu.o!¶TU‚9– ªØÏqx<~wœc²ì<ð¹~75Ì1x-YsÔÃ`ñºÕ°¸z“æ—4èМ)Î\Òæ”[æw»ù®Ác2ÞÈrìÆ¬+æ~M•à±Õ©·*u±˜\ š°n0n+ÓœâÜbÜZn+c°¯(öB€ ( € ( €?àšòn¿¿ìÿÿà¬_úôßÛ"€>ÿ € ( € ( € ( € ( € ( € ( € ( €>"ñGˆußÚÓ]×¾ü6Ö5/þÎþÔµ |iøÃ ÞO§ê¿u]>yl^ÿÂsÿ /‰>üN+_‹+×ÊòÚÕ0ü=‡©S g'*us:´äáˆÉrjðjQ§)QÍójR½í2üþ½õœN]ý9‘å_€VUÇ\i—`ó3|:ð×ü×KàŒ.”1yG‰¾$e˜ˆÎ•|mzS£™x{À…)G4Œ°¼aÅØoõ[û%ãO°¼7á¯ø7Ãú'„ü'¢i~ðdžô»ÃþÑ,môÝEÑôÛxí4ý3LÓí#ŠÚÎÊÎÚ(à··‚4Š(‘QT__†Ãaðxz8L%Xl.”(aðôathѧ téÓ‚Q„!£Å$’?³¬ë7â<ß3ÏóüÏg™Ö;™æù¾gŠ­Ìs,ÇZxŒ^7‹ÄN¥|N+^¤êÖ­VrIÉÊM¶mÖç˜ZòòÓO´º¿¿¹‚ÊÆÆÚ{ËÛË©R [KKhšk››™åeŠ …Y¥‘•#YÝ‚‚k V+ ‚Ãb1¸ÌE. B¶+ŠÄT…> Bœª×ÄW­QÆ*4iBU*Ôœ£B2”šI³›ŒÂeØLVaÄÐÁ`p8jøÌn3Zž „Âa©N¾'‰¯VQ¥F… 0ZÕªJ4éÓ„§9(¦Ï4ði¹ñ¦§ÿ *mRîçÂZ®—¥?ÿ _hÍ¥¶“kä^}³Å÷Ðß«ê ¯x‘/å‡O˜ýˆXxOìvÂÊ ÝSZóþ…Ý~*Çÿ¯•3 M~Ìrüº|âò¹eï.Ãû,KÄñ..–1KóŒö8Ê”ðU_Õ‡>«Cê”qxüÕÕüσ'sOø‰u³L^'„³\¯+Ÿ‡\3ŽÉe•Ë*Âû _×8ÃGæÏø’ê´rêÏêKÂ_SÃ, viûoT¯ÐÏÕB€!¸¸·´·žîîxmmma–âææâT†ÞÞÞi&žy¤eŽ(bZIe‘•#Egv «§N¥j”èѧ:µªÎ4éR§T©V¥I(Â8E9Ns“QŒbœ¥&’M°m$ÛvKVÞÉ-Ûg…|]'Æ÷þ)øýmcðŸV⥗…¬þ|Oøa­ËâÁñöuÑl¯:„óJ˜ŠLV3̰5ð˜jŒ»°UèÒ§é5òÇÅ…rÞ5Ö­¼?á}_RºiˆÆÝ!†ÚæyïõiâÒ´Ûk{[ÖKK«›­BöÚÞ [†ÜK"Ã&UÍzù®e›à°´”[u%ˆªçR­*tðØ:sÆbêÕ­AJµTpÔ*Õ©Z’ç¥ÊqÖ(üëÅž,ÀpO‡|OŸæ3¬ °tr|,./Çã1y×ãpÜ7eø»5©O-̱نw›`0X<»5…ÇbkÓÂ×½:²O¡³µ†ÆÒÖÊÚ(`·³·†ÖÞ hc¶·†xÖ(¢‚Þ±[Ã"¤PÄ«HP+Í­Z¦"µjõgR¥ZõjV«R­IU«R¥I¹Îu*͹ԩ9IÊu&ܧ&å&Ûlû|³.Âen_”à0ØL+Àá2ì„¡€Àá0˜*ðØl6 ……<6 B(RÃá0ð… =Â(Fœ"•ŠÈî ( € ( € øö7ÿ“Šÿ‚±Ùÿü9ÿ×YÁ4èïú( € (Àjؼc?ì½ûGCðóYñ‡|}/ÀŸ‹Qø'_ð~¨xź'‹ÀZúø{UðLJô¨æÕ5¿éú±´ºÑ´6)u CQŠÞÒÊ7¹–5?'ÇqÅÔá †8š˜©eõ:5]âqQr‡¶ÁS–3ÄÑxÚ>Ó ,MN¶¥‰§:I?¥àÉ`áÅü/<Æj˜ñPñÆN<#Ã,}mõ™âe: ©ó:¾ÞQ¢à¤ªÊ4Üšþ $j–:_Äh¿á>}Bi§ñ± kasภĖ6ÿ{ÄX•þ³eøþ¯þ©à0üK”bò™aðÑÂÑᮡ›áªbrÜvS…•L>qì2b2º˜,n4sP¯Œ¢«OGå¸Z£„!þ¸J¦%ö³xÙq~3B¢ÅÕÀâ3‡©•K4ÅÔxˆU© ö7<Þ†!a¨ÐÄÿbŸ°7íqiûX|$ÍðËö ø}â?†Ö>ðwŒµÚ{ö|ñ§ì÷­øëÅ#ÃV²ë)ð¦ã8-îuöþ ™îîì—Ȱ»¹K91&vç´ð•ñ8¼ë-§†ÁåY®w¬³)Ž&Lv[ƒ¡[‰ÃÒÅaã)N‡Ì(áðµäÜ150¸ÈÓoêóg‡ÃÒÆáp9vIšO‹Íò¾ÈžišJi`ó mj8Œ&&¥ mXÅâq–â1¨J1­Jž+V´WÖ ºkÁ>€( € ( € øþ §ÿ&ëñþÏÿþ Åÿ¯Mý²(ïú( € ( € ( € ( € ( € ( € ( € ø—ľ%ñí_âoáÃoTðßì÷á½RûÃß~2øzúãMÖ>%kmÄ–Z÷Áo‚Úõ”‘]YéÖwQ\iþ-é .…*_|=ø{|Ãa}lÀ€èì~”’I$¬–‰-’]úêI$’I%d–‰%²K¢BÐ0 Öˆþ(øÒëÂöð›øÁ? ¼]omãÃâxNóÂ_´>‹â‡wïqà¯_øŽïVÔ_Á:5÷‰´©|e®Áá-?ûW]Ñß¾ñ­‰Þßô<Ë8G"£›WþÀâÿ‹2Zµxue¼CœÑÎ|3Çå¼O†T³ìÓ•ÑÁacŸã°ùN2]S9Å}O/Ç,ã4Ë"ñB«É.|EWM{ZT¨TJ·=*nž23¢ïJnMÒ‹©k5MsN.œ'¥K{užgi§éö–ÖÐYØØÙÁ­¬Kµ¥¥´ ÛÛ[‰ B‰Q"Ǫ(àkםН[‰­W‰ÄU©_ˆ¯Ru«×¯Zn¥ZÕªÔr©V­Z’”êTœ¥9ÎNRnM³©%£’I$’²Ih’KD’Ùañ6Ö'ád‘%Öã’Ã-ž™©Z·Ãý-’mgFÔ¬u¯øŸDº±ñÍÏÙ|§êö6?Ú>ÕþÃ_ü)ñ¡ þϾ×/ü'ñ»ã'†¯î4í_â'ˆt§xƒà‡Á~ÆH®,¬¬%gÒ~/|ZÑî]a}ðëáýòøõ.®Qâ‰N*(âq8ºj9‡‡œ˜Ñ”3zo Æ<]…— K$ɸ×ì økÃþ ðþ‰á? èš_†ü1á½.ÇDðÿ‡ôK}7GÑt}6Þ;M?LÓ4ûHâ¶³²³¶Š8-íà"Š$TUWØa°Ø|Ž F– †¥ |=F4iÅB:tà”aE(Æ1I$ç,ë:Íø7Ìóüÿ3ÇgYæuŽÅfy¾o™â«csË1ÆÖž#Æâñ©_ŠÄש:µ«Uœ§Rrr“m›u¹ææ¾<ñ4é=ŸÃï jsiÞ>ñ¦—®6‡©A¤Å­Ãá; :ɾ×ãnÒkÛ(bÓ¬ï'±Òôã4—÷Ä:–›nº}õ”Z¡¶øN1Ï«F¶‚ò}\qV_›Ë)ÇQ˩洸s¿¬ñ>m†«ŠÂR§Ââ«a2ü´gŠÎ±ø +‹ÂÓÌÍ8÷‰«Â¾ÃÎÍ+e¼ƹV{,2¡”ÒÎèðž.ÁKë|eàëcp4ie¸~Õp^¿ÄÅÚuоG­é>øa⟠Ïá›+Ü/ü!¬kþ*ÓüSusàíK:ÊÒ_Zßxsôü«-ÿPò쳌xƒ`¸‡7˲¾.ð~¥ð¶?%Åb2Ž4–_ŒÎø·(Î)æÕñ99dÞ.Éñ9EYæ>4±XŠõ2L=l>iÅRZœðô¥NT¡9Ð̽¼j%SÏt'MÁ*ÚÓ”ê*Óâ’¨Ó‡ºxsú„<= øKÂÚFáï x_FÒü;áÍHµ†ÇIÐôÆ 3HÒ4ËuH,ôí7O¶·²²µ{hc†5T@çÙ¦g˜çyžcœæøÜVg›føì^gšf8ÚÓÄc3 Lj©ŠÆãqxŠ®U+â±xšµkâ+T”§V­IÎmÊMp„)ÂéÅBãBVŒa£Å-ŠI$¶HÙ®)øcsÿ ZjÿÙÖ[OKðyŽâÊòßþþ•%Ä>Õ-.ôŸx¿B¼·ñˆ–ëÆvZ–u£‹ßë~·Öü=¥ø‹MÕ#o±âÚ_ØòÁp’‹…l†yß5,E ¿ë.24çšá+QÆäù.aB®FáK"¯…ÇQƺ–3«€Ìñ™f+ 5÷ÜqGûYÆ2†#†iÔ|D§G‡­þ·cãJ¦wÄaó ƒ‡³\5ntèpÖ+˜ÐÌ~­›åyÅl³8ÇäøÜ ׫WÇÈø¯R× N—á«X®5]_V²¶¹»¸ùìôZK­[Y¿Š9ฑM¤úv™2F×ÍåŒo40 ‰âö²|._S븼ִé`ðX:õiQ§î×ÌqíFއœ©Ô¥íëSÅbçR2Tð41P©QÓ§?̼Gϸ¿¸k‡¸.Ã㸉¸—)Àf–7÷¹_ð”'_0âN*ÎpÔq˜Ÿ¨_x£áÿÀÏ‹~1ð«è:‰â_ZøƒÃÞ×u.o èþ"ÓõMP×…ý«é–•…æŸ{z–ö÷Ö—VÍ$/ò¼oÅe\+žfXÏ Âàe,>1F”£„”ªSÖq­J´)à°üξ:«¥WØa)Ö¯s8§ô¼ÃæœYÃ9^24êàó ÿ(Âb¨ÖXR­B¾:”ê:5hÕJtêNštêÒ©ïµ “æ_ɯĽþ !ðïþ ‘£þßÖð^/ øï_áwƒ¾-ê<7û+~ÄãÃ~1ÿ„Š×IÔ¯~|9ñXðuö«?Ä”MNOxvæû÷®x®Ö ¿è‹{ ÓÿBâ ¹d<-jôó,&SÅ<&oÀ|Çx\&a‡¥‡Í2õŸÅ9m^Ì=Š„kK¸bžgƒÄU£K[›áå]NЩ/ÖŠòd( € ( € øþ §ÿ&ëñþÏÿþ Åÿ¯Mý²(ïú( € ( € ( € ( € ( € ( € (Ì~'üeøiðsM²Ô~!øªÏD—X¸–ÇÃZÞë~1ñž­rt?x'B¶Ô¼[ãm}àåCð¶«j<¢×ÊGuó3<ç-Éé¦aŠ…ZNjŒëã1•RröÕÅãq &Õ -µZ»å²lûŽðß|GÆâp|!âs8eÔ¡ŠÎ³jµpÙgpÞ¥XÑY§ñ6k[Ãü3”Ƭ£ æ™îe€ÀÆr7_ÚJ1—Íšþû@þÕZDþ½ÒÛÙiú}•º¤0[Á *ªªåŽ]ËHÌÇëpx<._…Ãàp8zX\”(a°Ô!thѦ¹aNœ"’Œb—ÏwvÛ?žxˆóî/ϳ~(âŒß0Ïø‹?Ì1Y®uf¸ª¸ÌÇ3Ìq•e[‹ÅâkJU*Ö«RNR”’´b£¨®–ºO( #Ä~.»Ó5 #Dð÷‡î¼[­j:µ•–£•í­žŸá]*m·º÷Šu¼ã¦ÛG§‰¤Ò,"´»ÕñÝäv#ÃÑâî"§EUÁÕȳÎá. áìû“ø™†£Ä“Áføzù† ”Q¡ÂØ:yVm‚Îq¸LÞ†+ä™\!^Y–g‘òÕœª·‡¤Ý¤ªÒÄb)V¥˜6èóSj2U¯'Rœ©ÆTÜcí&íÉ ¾¯£iqhš>•¢Áu©_A¤i¶:\7ºÎ¥y¬ë‘XZÅiÖ­¬j3\j®¥p‘ oµ+û‰ï/®ž[«©¥žWvøìv.xün3R–…Ln+‹ †FxšÓ­*X<°Ø<-9MÃ…ÃÓ§CIB•(Bœ#ÑòÆ1NMF*7”œ¤ì­yJMÊR{¹6ÛwmÝšUÊQçd¹Ôl4ßÙ%Ø—Ç·Òè:…å·ö¹Ó¼2-&ºñEÒêz|*º}ìÚLSišt­ªh÷Éw¨¥î“x÷öÁ'Ôp´ia±8¬þ¼¨¸píf8j5~©SëY«­ YMa13oBÙCŠ‚Âc°ò£†• m‡ÄJ¤~σ!C‹Æñ>&T98S Û‡­õ¿\ÎÞ" Ž„°Xº­âðÔ³ ÓÇc °9–X|ðÙ†8\\êÃÑ#b"@BF‹Ìä*ª 9gc€2Ìʼnå‰$šù™IÎRœ­Í)9JÉE^M·e¢•ÞÉ$¶I#ã§9Tœ§+9NRœšJ+šM·hÅ(Å]»(¤–É$>¤’­íå¾guví­•¼×W2,RÎÉ ÒJË ,ò°E$G rJçåDf ¨P«Š¯G F*U«Õ…Qs…5*•$£êT”)Á95yÎQ„w”’Mž~ošàr<¯1γ:³¡—eX,NaޝO‰ÅÔ¥„ÂQ|EHaptq¼LãJ”ha¨VÄU’P¥J¥IF/›ðž›®C£ªø¢kyõ½[Q¼¸Ž d‰­ô-H"Ò4 ;¡771[ÚCíü׾ѭÞêw$²[ÛCêg8¬IapyM:´ð,-R©VSUs {‹ž72¯GÚN•)Õ­RT0Ф—²ËðøJU%R´jÕ©ðä<]„¡žq'ˆx¼/‹¸›=Ìñ´px 8i`¸;„ã^8~ଯ1X<6?‡Àå¸Z9¶uŠÇJ]âìÛˆqØ:X<º¶„ë«Å?O ( € ( € ( €?cù8¯ø+ýŸÿßýu—üN€>ÿ € ( €9_øŸKðg‹u?xrËÆ>2Óü5®ßxOÂZ–µ†ôÿx–×LºŸCðõÿˆ¦³ÔaÐlõ­M-´ëf]>ú=2—½{K•„Âü¤±QËño‚£™bF–^8z§?vT«V:°…9AË›šœÔ—º×½s³/†®?OŠ©ÁTÅaá‹ÆÒ¡,M\&Ub«âiáã:r¯R…7*£u%8·uüwü_øAñƒöLÕ<]û~ø“þ ý…,|Eðžâ÷â÷‹ü}àÛSÁzî£à¨ôiäñˆ>*Yü;³ø%÷…¼›_j~ðÅïŠmÚ}[M´žê‘=,‚á¼fM…†=ðæ[ÌòÌ‚Y´• bðù%9ªŽ~×.ÀEc#†œ°Ò¡„Âáç%ˆ©…Á*µaž7‹âꘚ5ð¯>Ì+Ó¯‹YtÜ+>ÅÐSż3ŒÝ:Y¦eŒœe(QÇTlËÉF/«B•Oë;àG߃ÞðeƵðkÀ>ð‘ñƒ\Ô~7x©¼§éÖö3ñßÄá‰9j³çÂ߇¾ñ?‰5ÿøÎoøsà·íâ¯xïÇZ÷„üR4Oi¿#Ñl´¯ßêž#ñn†'Ò­µ.|$ñxèq3…Œò¾ÅåYV?ëòbñ¹îo—âsŠY6Mƒ%C‰ÂepØüØcò¬f™mX¬F"®"–«K‚ŽC ez´ñ|L³ºÙU*t#V¿‡«äø\Ï4Íkyð—üfoŒÞøá¿Ùïö}—Åÿµ7Ç/~Ðõ>*Zü2ðÁ[öMñ+x7ö†×~+ü_ðÏ€þ-j6¾ ð¿ŠæÐôk^ øSãSÇz‡‹ü&±øC‚÷VŸCí§IæUpò ÓÌ2,w‡ùG‰ŸÛXïk–GÃ|AŒÃåY. ¶•Ê¢â|Ã9ž?,†QíV2 ÅO8X¾ž'ÉQË-†qK?¥W™åoÿñåØO3úÿK&Åñ*Ä`quêe”*põÂÃ<Ææ“tñx|6?-ÂÓʱ9ž:†[7ðTËþ]ivÿ/o¿lK?Ú¾×ö›öW¶ø‘¥0~Ñw¾?·º¤ðÒø~øÅ/ÅUølÞ#¶ø}À¹ø]ín| уͧŸêÝñØ~-ÁñvcK›'•G!ÁøˆÌ°\pø’8ž¬&+"Í2Õ”Ó¡–O8§šãó~X M\&qGMÔÿ„ÇÄñâ á%¸^Æbc•)fo9£ÇÕršxèäpÅÖÎq¼hT§šO&Ž]S)â˜ÊÂe2ÅWŸ[ÿ‚¢éÿ|+ûNé´¯Á¿øAiÙ~ÛàåÞ©ð7á'Ä›ŒGÆi{þÏÙöß¼Sà¿‚·þ#‹âÄ4»øw>|>Ôü%â/R}KM¹ðëhþ ÖbÌðÙKáÚUó<Ë6ãª>ÿeãcK-«€âê¸ »:Oˆ£[3¡K‡ fpâ˜ç•ZË#ÀæóÄex|Ï+Åå§NX,]zyÝL>]—ÑàœçÄhæ¸wŠÇáêp§ÖÌð¹ËöO„®³ì?,þΖQT£‰ÆæÙ ,gŠY›ž»ðíïâüIñWÁ¯Ûoá/ÿfˆšGìñâÿÚ«Ãwþ7Þ|{øk⿃? ní,>*ŸøLõ¿„_õÝâÂûSB¹ño…eð-ï‡çÑõý7Vð—ŽXÚŽ; ,»4Ï2üFš¿¯Ó«‚ð—íð°ü+ð^‘ðoÄ_| jwÞ·ðGÄÿŽz}®½ ~ñ^§á­^êÉn}ßìŒV=Çðvi*^2˲lë;­–`ê¬~SZ— Ëþ³åX\ækZ¶}PÇ',»õf¼£¼øþ §ÿ&ëñþÏÿþ Åÿ¯Mý²(ïú( € ( € ( € ( € ( € ( €<ÿâWÅ_‡¼7'‹¾&x¿Gð~‚.­´û{­VvûF©ªÞÈ!ÓôM K¶K[Ä:þ¥;-¾— hV:޳©Ü²ÛØXÜLʇƒ2Írì£ ñyž.އ5ý¤¾<Æ«ð«ÃÓ~Í_ î]–O‰ÿ|(šŸÆ]~Å’)#¼øyðFúæ=?Á 8iRß^øÝ7öå…Äd_ü¾µx®Ÿç¾»Äyê_ÙXypÞ]&ÓÌóŒ*«œWƒI©åù,ä¡‚RÕF¾vý¼$½ü–qjOöõgÁ g'Ç™½?¸ÎŒc(p?‡Yô°>å8¥*‘žŒ(kV«gâOŒ_µ1ñÇeØ^Æc0™e•åˆÉ|:á 8wrŠ­(¬M‡>\Ë5öq:üGÄ5óž'Ǩ©fYÖ6­ê?u¯pü°( €<£_ñŽ©âícÁŸ 5-OèZžaâýkV´Õ/´Ùjv×wÓË[[Ŧx‡ÅpÚÛZ¬^:æqb5­/XÖZ-5£·¿üç9â|Ã;Åæ|-áî?)Ÿe†Yƒâl×1Ãf¼³…0¸úœ]jŽ<wÄt°ô0ñ§Ão7À×Á¬×/ÌóISÀ¸PÆ~OŸñ–iÄXìãƒ<+Ìò:œW‘f™>‹ó¬Ûšc²~ Áfxl^:½ZpÃaéeœEÅ”p¸l,)p“ÏrìFgY^qJ–ZéáóÇÃ>ðç„#ÕSÃúp³“]Ö/µýnòk«ÍGSÕõ}BMóÞêz®¥qy©_IB++%ºº–=;Lµ²Ò¬ÛN²µ¶‡éòÈøfŒrl ÃO7ÌñyÎkŠ«ˆÅc±ù–gŸ5\V?1Ç×Äã±s§MSÂá#ˆÄT†‡Âåø8ÐÁapøzcÃ<ÜO6‡åËS=ÎqÜAã+b±™Žg›ç…NzøÜÏ5ÌñÌËR%K‚Ž+Vž]–apYVl»…ÂÑé«ß>œ(É~(xÄ [|4ð5οáψ¿|+ñ<ñ)~j~ü1Õ¼?¡ÀlüWñWTðÞ‚ÐÛë:ÖˆºƒuiÇŽî¾Ûm¤#iZ?‰õmìøK+Ë/WЏ‚–]špÇ g7,û…Ÿa8k‰8³™f=¾OÃmá3LÉN®–cžá²|n‡¨û ¸Ù,f;(Áfø‰ÏJœáZ½:ÞÊ¿°•j4%iR·½YJQä¥*‘•guv5%Çžм!i~šN•¢Ùj~ Ô¿á"ñ–¯¤h:Fsã?O§iÚf©ããZZAâZÛJÓá¼Ô'YnZÚÊÊÈLml­c‹ÃÎ3¼Ã;­†xÜf?…Ëp¿Ù™ ˜ãs*YIOŠÅá2L²xêÕªaòÜlf&tpÔÜ)*Õñù=µzÓž”éš—,b¥9sÕ”a:µQ•Iò¤œä¢¯'wd•ì‘Ô×h5ÝcVweDEgwv ¨ª ff$U–b@M4¥)(Å9JM(Å&å)7d’Z¶Þ‰-[c)Ê1Œ\¥&£Å9JR“²ŒR»m·d•ÛnÈò_ý—Æ^ ×>+(²»Óµ dð§Ã­JÝln£¼ðŒÉ}{âm?Ro iz¿Øüwâ6–öÐ[x—Åž ×|!áßø³ÃWSëZºÜ}Ÿûl-Ëø9¼EN¬³Ž'ÂÕxŠ3£ÄXˆKC*ÄáVm‹Á{nÊÔ(Vur¬›<Ëó¼ÏˆòlÖzx §ú{~Ê2¾o‡Æ`ëÏ>ã,gŠ¡ áoìfñGˆ´ycÔg‹ÃÞ½»½¼´µòÄÿˆ¡ÓN‰õ]A¤6^˜jªh÷QyÚØÒžIY4Ùí¦ú>"FYŽ„ðÔç™çz4(V«Ìêe¹dÿ}ŠšÂÖÃ({|Ö›Â,6•yò`>¸£,U:°ü{8Ê1^!ñ× âhg¸Ì7øo›fY¶i–åê‚Âq§áRËr*sÜ»;ye<‹‡Ôâ.ÇeX­qlxrµlL©dÌ+º¯ž?a ( € ( € ( €>ýÿäâ¿à¬_öÿõÖ_ðM:ûþ€ ( € óoŒ·z‡Â/ŠwÞ&ñî«ð¯Ã–¼ku¯üNЯmtÝsáÞ‹oá½J]SÇ:6¡{c©ÙØê¾²IõÝ>îëMÔ-íîì"–k+¨Ñ “Äâ\F 渜fc‹ÊpØ|ZÕ³ ž7 KŸ› e]ÔÄM¥JQ©R´çT¡*“Š=l†–"¾y“ÑÂeø|Û[4ÀRÃexµ|.c^¦*”i`±?½¡þÏŠ›Tk7ZŠTç&êÓIÍzùñOŽ| yãÚ/â/ü±âOø&·Ž4‘'޾7xׯ¿³5¿Ãïü Ö£Pñ—ÄŸ…~ð]Ÿí á¿€¾'ðÔ§Qñ.»k£Úk‰ðÿP¹¾¼Òìcšháú|. … Û*˸Ë.˲¬mLã*¥G%â©ûlªŽ{õü?öFUÅœº¼ð™n-g?SÂO ŠÄ¼<×ÙaqxºtÔëGÈúÞ2tñ8Þ ÇâóŠ˜l>.x|׆aí ô¨Ñ©õ¼ RÌèÂy§³ÂljÀâ¨ÐöÕ©ÓXœ µ}•9ÿoÞ ±ð¶™àß i¾M>?iþÐl|“"Í¥ÇákM*ÒßÃé¦Ê¬ë.žºLv‹g"»«ÛˆØ3“®oW1¯›f•óký­[1ÆÕÍ>±JÿÚ515göÐJ<•~²êûHÙrÏ™YZÇ—‘SË(ä™=,–Q–MK*Ëéå2„§8K,†Œp„ê~òqxUIÆU=ù&œ½æÎž¼ãÕ ü.ÿ‚‹ÿÁD`ðwí¢þÀþ ý±¿e¿Ø'UÕ>éß~9~Õß´—Ä_‡>ñ€üâ¿êþð¯‚f|Kñ'†|5㯾*ø‹R¹ñ?‰.5_|(ðíž·«xsÄzÖ»áÝ)üÌ<7bø‚–72ž á¼^[”fXL«F‡qq™åë8«–`+OÚO‡ò\¿'­€žmÄqÃb±ÓÄçXINŽ+ŽÆá=c«’`r\Mº9†oÄQÍñ[ÇÓ«<ƒ&Ër©ÓÀK;ÍáFPžiŠÄf؉ÐÈøz8œ<|òlÛ™b_‡ú¶7æ_i±ìQûHÿÁ=ÿiÏ‚þx»öѾ~Ø¿³Œ¿jé>0xsâßÃ_ü~ø¡ñ+á—Æ™¾!|jý ôíj÷Á~ñ7ÆøkǺW‰üoâ=oMÑÛÆ“h¾òtQªèZ|IÌ6ã¿2Âá¸s/âO ¼9Âx…TžW”<·Ã$â,NÉ*âê[ç–ñ*ÌrüµÄcsÜVAb–#™àqÄx¸ü,ó\›#Æaqx®!Îò¸ƒˆxê¼1ùµzÜuÀ. Ì0xóáðÙV#+Éð˜ÙSÂÑÁäyN”U•<>Qí18¿ð£ÛþÓiÿok‹ëŸÙOø+^Ÿñ üaÿö»uáè¿g¿aÙ?dQûTÎÒYÛ3|$³ø¯p×ö…¿ü#ZOÃÕºø—©©xBåu$æá).‡eÜFžK[ˆr¤…óÍœrÇÃy—‰xÄvK8ð5çˆ4íkÆ7ÞÓõ_íÍÿMXáJ«ƒñ¹'ñ :ù^[›øý†ÎêTÇP©…«•ðŒ<ÅøUG±4+(bpÙ;âN5ÅbðùµZ¥‡Ê¸_2â –ÔÂÖqåÙ dµhcs >øƒ’*˜|^¦¯qå|]•ð‚ÄF» ³¼FÃÊXLV_V­*Ô±_á10¡,EiS÷ÏÛCðŸü§ãlj®ÿbŸŠ^øÁàO†ðKïÛ¯á&·ñcážµŠ~Ÿ_µŽ—ðûß >Eã­í‹Çéþ ×|Aão éÞx‹ÂIÓ-1Xyçõ¥Å3LÃ&sY®UƒÈ³•™à°µpÒŒ7˰Xœ‹Âyð>uFYwb¡áÏÃ%œÖÞ#ƒ¸Û„¸—ˆ3ŠT£'í8w•pŽ/‚ÎÓYfgý¥“ÓÁbªÖÌ0ôçý*WÉŸN~0ÿÁ>~|V×¼5ãï‰ú_í»ûOø/Á0ÁGÿà¦ÚÌ¿³§†¼'ûÝ|¼Ó¼9ÿFý«áÖ<'s®xÇöDñgí¾ñìš]õ׋.m>;ZøªÎë+à_x.Ú[è@³ÔP@P@P@P@P@Pã߈~øYá}GÆ¿|aáÏøKJý¿Ä>*ÕìtM* &.ÚÛíwóC··³·°±„É{tñÚÙÁ=ıÄÜxüÕájcs^„¥oiˆÅV…QrvŒyê4œç+Fœ#yÔ›Q„e&“ú>àþ*ã¼óÃ<ùÏñ=Ïê™>C—b³<}XR=zßWÂS«8a°ÔÔ«bñUT0ØJ|MZTa9Çæ†ø­ñóãŽ/ÙûÁPü+ø{x§?þ>xg]²Ö5;S.ÃwðËö~ž_øÇTŠâßtš‰>+êŸ l#“ìÚžá?h“F×?6ó\û<²È0K+Ëçÿ3Üû ^ªÆöçË2 <>2ª”u§‰ÍjåÔÓå©O £$åûZà/ ü.sŸ‹œMSŽø¿ÿäÕøOåxœ»]C™aøßÅÊTóžÀÔ¥VÐÅäÜã\\áí°XÌÿ…óJsAøkû3|;øâwø‘ªÜxâ¯ÆK‹[›+¿ŒŸõHüUã˜,ïþÎu+ÂQ¥¦Ÿá_†ÔÒÖ[Ï|+ðׂ|)u=¼Wwz=Åð{§ô2ÞËð§˜Õ–'5Î%ByÆkUb±Ñ…K{JXD¡O –aª8ÅÏ •a°XYÊ*s£)ÞoäxÓÆÞ0âìŽ<€¥“p‡kÐÄáü8à ò«‰Â{eƒÇñåˆÆgÜqá#ˆ¯ ?qæuÄÙõ Uªaðù+ý_@~>P@y.µ¨ëž?»×¼ᙼMá ÚÇH’÷â•…µ¤K¨FK ù´o‡ójO£<ú ³Ç{ãKk{­/Ã÷·Zi¿Úzä‚h_›æ¸ìߌ±9Ç ä53þÊèa2Ú˜¯°t0ÔãŒxéàñ•r¾ «Ž¥Zž:­lž¥hbøª…F_’âëÑÃ`~¿›RÆÇ(ü:Ìsß1y÷pÅn'àü›Ê*ã|SÀa°t£˜<ʦ0­’ø}[1¥ˆ¥™W¯UÄSÆñ®ŠÊ¸{ˆ¡„Ë´óÊ„2/MÓôëM.Ö;;(ÌpÆ9i$’{‹‰HK«Ë¹ÞK›ëÛ†mÝõܳ]ÝÎÏ=ÌÒÌîçïpX,6_‡† Mœܧ9Ö­Z£KÚb1XŠÒ|^.¼—´Äâñ5*âq5e*ÕêÔ«9MþŸ—åØL¯ O‚¤éѦ®å:•+â1Z^׌Å×LN;ˆ’uqxì]Zؼ]yN¾&µZÓœåvºÎРø³ñAü0ðü8ð«à¿Ú#Ǿ øƒâ‚Ÿü{­k:6‘⻟Úè£\Õµ«¯hºþ³eá j*ð¤>#Ôí´Ò}GÒb¹¶¿Öl¤uÁ¼%ÙTâŽ"Âq øw>ᬳ8“‡p~7'¥Äu±ï/Áàhæxü·_;ÍpÙ>qS+ÂUÅ.jyv;:UpøDN\Egj¥EãkR­<-Ò”cQÑPç”Ü!9*p•JjrQÞqÓ’:ÿ|?ðÿOН4{k„Õü}â«¿xÖþëYñ´ú·‹5 3JÒ.n­_ÄZ¶¯q¥iº^‰¥i:‡téí´M HÓ¬ì4Ëh£mþ.}Ä™—¬žŽ:­)`øs'£ÃÙŽ-ÀG“a±xÌu*5£–`ðTñ˜ÚØÌÃÌ3jÑÿÂSöÏê|‡·.3âÌ^™Ë›ûCÂôqSŒRVÊóû¤ÜÛyæQ‚Ës˜ÆØn'ÁOû#ôGýŸÀXU\³8ã|v™ÏŸûS-àʪñqŒc²l׉±ym:ŽRâ<‹.Êx‚1QÁñ–_?ì/bŠ( Š8 Ž8`†4ŠbEŽ(¢BGq  q  ˆ *¨  |Dç:“J“•J•%)Ô©99Îs›r”ç)7)JRmÊM¶Ûm¶ÙùÕJ•*ÔZ³Zµg*•*T”§R¥IÉÊsœäܧ9ɹJRnR“m¶Ù%I5¯ë1ÚIa¡ZÝEˆ¼( € ( € ( €<ã_ÃO|T𭇇¼ ûBü`ýšõ{?Zë7:ø)¢ü×¼U«iÖúv«e7„õ OÚ3àÇïÇáûëFÓXº¹Òü¦ø©5 JŠËÄÖzLÚæ—¬€|AÿåðoˆþüOÿ‚¢øGÅŸ> |qñ“ûø3ûCâ—Å-7áf“ã¿}¿þ •ÿäÔí·tÿ‚Ÿ >ü2·þIJ½¶ðî™ÿ×ïùº>“§Ï¬ÿkø‚]W]ÔÀ?Oè € ( ý«´Ï ë_²÷í¤xóÂÚ׎|ª| ø³§øÇÁ~Ôt½#Ä^-ð½ç5ë}{Ú­®^éº6™­kZ\—Zv—¨jÚŽ›g}qÅíݽ´rJŸ+Ç þ©ç’Ç,gÕiàýµJ™{¤±øWB­:Ôñø5Zôç‰ËªÂê4œ*ʵLÆXÈÐý󪜿XöMOÙsòÊ/Uü¿üñíûW~Ç?>ÜÁg`-#öø·ð_Cð¹ªøÀž ðßíý¤|Ö|7oáÍ{à‡Žׯ™~xwâÆ“á'¼øoâß§ƒ?´,uû{ý~5æËcú7`VuÄ•_¼NyÞ9ÆKƒªTÂä¼w‹¥˜PÍðØúr¬ñx¼·,ϱôèãkà2éÊ­*ªå´êR—3‡Æpö6§àèOƒ0Ø®Äe—åX.+Œqøî ¯€•l ê²ÃSÎ*ä.œ?³q8©Ó¥Ž§G ugJ¤*TþµüáÁ^ ðƒ¼*ž_…ü'áÃ>í&óËÐ4*ÓKÑÓílÌn¶éÖ¶ëö’ÌgÇšX–ÉãÍñøÌÓ5Ìó<ÇþFŽaÇã¿wì¿Û1˜š¸ŒWî¿å×ïêO÷cáèg’`pY^M”e™uG[/˲ÌYÕ…gW„ÂÒÃáj:ÔÒ§UÎ…8IÕ‚P¨ßò SÃ?üE =¬Ÿ/ý»›g-C†røÇ=ø‹;§_—ò»§<·+NŽe›µdã:’ʲêМjá³,BN÷oø…~øm ˜Ÿxµ~$¡ÍÉà߆8¼³8âå^ÊQÂñ¿TŽeÁ^B¢Juð¸:\{ÆYf/WpVORQ¯ÇÀ?³„|;âk/‰¼CâoìY¦Óþ"üT›OÔáš_0ÊŸ ü ¥Øé¾øWo¶Y- ׂü9§x—R°òíüMâOL†éû03„Ãâa˜æ8ŒN{›ÁóSÌsGN§Õ$ïu–`iBž+Ž®<ø<=§õ\¡,Vkø÷¶ÅøÇJ?R¯ƒ~çü9ˆ…lu ¼Q“ñ_â±XüFÓËqzÜ=ŒáÞ¡†À{oí8<Ïýy˳ŠPúžEŒÎýoOÓì4›=/K²µÓtÝ>Ú; >ÆÞ+K++KhÖ+{[[h!··‚%Xâ†$XãE ª¿JÁ`°yn —åø\>‚¡K ƒÁa(ÓÃap¸jTèáðøz1…*4iSŒaN•8ÆŠQŠI®eù~)À`ò¼«„ËrÜ» G€ËðzXL †§X|. Bèáðô)F4éQ¥Ó§¨Æ)$‹•Òvp?óèø_#Âç¹¾ šç8~ÈgÃaóž+ÇåùÆe–dlG´k‹Ãd˜Ã0¯V¤hÕ† CϋĨÒu(QöØš9V«*Tå(Suª¨·N„gN«%m#*’ŒRWNRoÝZÙ»&xÁwÞ>*¼Õ¼gã_ø³ÆZ÷‹â“Æ7Z%Èðm†¶šlü…¢hvºo‚¼7e¤ØÛi¶³Å}«j¢û]×õcZÔï/å8ƒ=¡œ¬žŽ"É2 >O‘åÙ,ã’RÇÒþÜÄà%Š©_‰3Éæü¶/>Í+ãqqU©ÏƒÃPx|¿-ÀàpJx©:~ÑÊ­J®¥YÔýã‹öj\©Q§É¨Ò‚ŠQNònóœ¥)6ûêùÃR‹ˆ--纺š8-­¡–âây\$PÁ 4“M+± ‘dz»±TN]:u+T§F”%R­YÆ:pNS©R¤”aE]ÊR“QŠZ¶ÒZšQ£WV• çZµz£F•8¹Ô«V¤”)Ó„Uܧ9ÉF1I¹I¤µg[ÚÝüaxõ bÐÚ|&)zt­õ‘wñ!´ ­¾³âK=9¡±>¿Ólg»Ñ¼;y©kúo¼9âØ®|] èÓi–údÿsVµSÃ`«{~2R¡õÌ|pÔVUª`rºø•/ 8ìÒ†.Åpîg“N–K˜ã¡‹©‹§úEjø£<&]ˆXŽ?SÃ}3Ž‹ÃðcúŸ·­–äØŒdjb—á1¸ªX|Ë9Ã`²œo ç9J<=šæTñµ±Ô½¥cUDUDEˆ€*¢¨Âª¨ÀUP𒔥')7)I¹JRmÊRní¶õm½[z·«?4”¥9Js“”¤Ü¥)7)JRwr“wm¶Ûm¶Ûwc©¥©j6:>Ÿ}«j—PØéºm¥ÅýýíÄ‚ÖÎÖ'žââg?v8¢Gw<œ)À'Šß …Äc±8|Œñ¬]jX|5 kš¥jõ¦©Ò¥õ”ç%»³Éϳܟ…òLã‰x‡1ÂåA–c³œç5ÇTTpyvW–áªc1ØÜMW~Jl5•ªJÍòÁÙ7dù¯ ZÁ«N¾:¹°šÛR×4‹+}9/ín,uM/ÃoJÛK¾°“TÔímu'½¹žçT–Íle» ¦Úê6þn‘núÙ­j˜:o‡©baW —ãqq2ÃÖ§ˆÂbóXÿ²ÕÆañÁá+VÂÆ…(RÂB»ÄB6*¶¯&6¬ª|eØ>%ÅÃÆ,~MŠÀgü]Ã9N"¥eØìŸˆxw€«[?Àpîq“Wâ. ˲ìö¶k˜b±üE‰Êá”bs%K!ËóÜ ÄpΖ²¯ýT( € ( € ( € (àØßþN+þ Åÿgÿðçÿ]eÿÓ ¿è € (  ý¨¼[¤øöiý¡âÖ‡àß‚?ìœrü,¦±xê‘‹<- Õ ¹ãè¸CW1â®ÀPÇVË+ã3Ü« G1ÃÉÇ«[Fœ1XyFQkBRU(ZQ~Ö0÷£ºþVþÁ%ÿj ~|qðì‘ÿÖxsÂüá_‰~Ðm?`¯‰3:'‡üg¤Yx‹HÒ›ÅÖ~2°²ñ î§jö·ºÅ•­•åôSÏio»FƒôlF]ˆá.&žW™âêbq<=,.?•FX ×­–ãq8¬£ÛJ´ðôêJ”êåukJ¤Õ'‡­UÊNWø¬³6ÂñU…Î0Z¸<&i†X¬.VŒVdÝ,.e:P§OûB„mK2¥N…lq!hþÁôkIl4}*Âx´ø&²Ólm&‡I·kM*m­b†H´ËGg{]>7B¶VìîÐÛãfb¤Ÿ'Z8Œf.¼%ˆœ+âkÖ„ñul\ãR¬çbª¤•\D”“­Q$§QÊI+Ø×+ÃTÁå™v´0tªáp8L5Zy})PÀS©CN”á¡&åG EÇ JMÊ6ÚlÒ®c¸( € ( € øþ §ÿ&ëñþÏÿþ Åÿ¯Mý²(ïú( € ( € ( € ( € (Ž©ªiš&}¬kZŽ‘¤é–³ßjZ¦©woa§iöVÑ´·7—××rEmiko´“Ü\KQF¬ò:¨&¢­ZT)εj£J”eR¥Z³:táܧ9ͨÂ1I¹JM$®Û:°8viŒÂåÙn˜æêô°¸,[ŒÆbkMS£‡Âápð©_^´ä¡J(N¥IµŶ‘òaý£üañná´ŸÙSáøñΑ.øäý ¾ CÂÿ³ý’”V[ïO/Œ¾9®d°o‡:}·ÃíYã’ÎãâÖƒ6dO“ÿXñ™´.À}z‹º|A˜{L.A .§„’_\Ï7¼]N8 ­8K6 õ_¿¯xwÃê1ÌøSDxÒãáWÁý8KàÿÜÛ£´V¾)Õ‹~&´ ÖúÄ-J ˆ›áx^„«ÒÇgغ¼E™Qš«B®::y~ªM)eyE;àðrŠmGWëy“‹q©˜TVKÍϼvÌèåxîð£ Àx5ÁyŽX ׸Ün/Œx³)Fr£Ç¾"ã8‹ˆèÖ”c:ùðÿ*±U°|!‚«Í)}Q_P~P@VòòÚÂÚ[»¹<¨bbIdv'jE¬“ÜÜLåb·¶·Ž[‹™!‚)%tFçÅb¨`èTÄâgìéSWmFu')7hS¥J”gZ½z²jaR½z²*4çRq‹åÆcpÙ~®/SÙФ¯&¡R­IÉ»B• aR¾'Zn4°ø\=:¸ŒMi U*Η•i6šÇÄÉt_ë‹â/ ø4Yë1CðÃZÓl¬çñÇãøJ—f™®]™ñA“ÖÊãÄÙ—×' 8œÛ*Kg­øÇJ~·¸ðÞ»áëš-ì­þ«¤ÜiÞ%ñµ–·öv´¹Ó5ì ½Á÷~´Ôü!â¿jßÚVvZGØWÆÐàøWÀdxÇ_ˆªSÍ2ü߈rü} ˜L>I嘬«!¯€öе,^ ûJŽ?;£šVÂgy>yý›ý™ƒú­zøß»Äæ8n†#,áÌ{ÄñUZ9ÖUžñVW™áªà0¸ ‹Éñ™' ârÏl±1Ùö¾3â,>sˆÀñAÄo(þÇÀ}O‰Ì}’¾üà( êøø—Åž2¾‡áûk;ßßL–RØj:¥üfM?²Xê:Uôwð&(×µ[›[« :øGc†YÒúö8>†–û/)¡œî¾xý( € ( € ( € ( €?cù8¯ø+ýŸÿßýu—üN€>ÿ € ( €?žø,µÏìƒâOÚÃþ ½ðköæñ‚>þÎß´ÏÛ Pñ7Å_~Ó_¿e¸¼­x#Á_ õ_ éÖ^9ðGÆÏ„ž ¸ÿ„«Y¼ƒM¹±ñý‹'˜E ¯…?±//5 oü¬§•ãø·Œ0ù•ZT+e¾eù¦Uâœ%šfŽ<Ë2йd𘊳ÂâéSÉñ¹¾`éa0Ôóð®½\TðJØwéã*æXNÁ×ÀQ©^†3Ä®Êsn\$kC—Tà³Xf/J’Å`§Û'ÊòøT©‰Y}E˜Jl-luLº¾ä¯þÅðj×…4ûMŸ¶§ìéáÝ&ÒFk-@ÿ‚ËxßJÓ ’išy>Ϧéÿµ¬‘´ó»K Že•Ùßs±'èðqË—O˜œm F9uGë’•j5i¬–Qª«GÚ*téáU9ÂiªJœ”¹_“5«Îv^Þukb*7Ë*•*·*ÕªÔÑÊ¥FÜêU“s”¯)I½OêïG‹OƒIÒàÒ%Iô¨të(´Éã»{ô›OŽÚ$²•/ä–âKÔ’ØFëvóÌ÷!„Í,ŒåÛ,d±Åâ§‹ƒ§‹ž&¼±TåEaå DªÉׄ¨FãAÆ£’tc*mr(E+.\²ž Ž[—ÒËjF¶]K„§€« D±p«‚… qÂÔ†.U*ËÐP”q«RU“UI¹s=æ;B€ ( € ( €?àšòn¿¿ìÿÿà¬_úôßÛ"€>ÿ € ( € ( € ( € ()¦†Ú®.&Š x"’içšEŠa‰L’Í4²HâžI‚¢‚Ì@Ò””S”šŒbœ¥)4’I]¶Þ‰%«oDµeÓ§RµJthÓZÕgt©SŒªT«R¤”aNœ"œ§9ɨÆ1NR“I&Ùò=çíA}ñóPð×ì¥à–øÕ©ÙÜMañSV»¾ð—ìßá»Ëk±iß™©OñTÓÉ•ŽðDñ´l·m+Äž ðkHoáù)ñ<óÔÃp® ç5a)S©šVL'a§òTO3öU%˜Õ§«ú¾QG¹âéb1;ûHÿAáüÃpn xõÄÑðÓˆ£O…à<¿…ⳬ=z…>xì. Àã"Y·ˆ¹Ÿ ÏêÕ£ÉrŽ$Pú¥WiŸ²´7¿Ó|UûTx¸~Ñ'ÓobÕt_h¿ðŒüðf¡¶wv²ø_à°Ôõ­']Ôt«û(¯toüWÕ~$xÛF¼ó§ðî»áû{‰,ÒáhãjSÅqN/ýaÄÓš«G RÕ² Dã(¼.MíkR¯R”â§GšÕ̱´gyaëáã'c|y­Ã\nCàGÿÄÉ1¸ià3!ÂæÛ~-ñ&¤1+ÓÏv£wmcØi÷,÷3ÇxYçä¼;…Äâ³L\£õL5<]L ŒÍ³z˜zØÊ9}˜L—*Ããs|wµÇbha)¬ ¼¥ˆ« is3渋ø…0xÌnsŽœ^ KW/˰Xüó=­†Äcðù] ¸É0¹†{˜û|ˆÀÒŽ.ÄÊxšôé%ÌÎdk¾<ñ å¢h´ð÷†5 æí¼Sâ«›ÛéÚµå„ãJµƒááÒã’°ß´k ®x‹@‘a­­-®žvž×ÁY¿gxœ4rl ’äÜš¾&\CÄUñT3ìe‰ÁÖYv—¼¾+}SèÕÍmäÓ(J†Ž"ul?ÌÇ=ãÞ!Æa!Ãü9ƒáîÌx‹—ñ^'‡âl»7Æ`+¬« Cç•Ó©_êùP­œ¬óˆò ‘£NX\&:ò¯…K†:LÒøoSñ®£¨|Fñ/…®®µ #]ñBXE†£u?ÚûOðÞ‰g¥xZÎ÷N8ƒGÕN>¿§Z(„k3;K4« ÀuJ™aÅxÜogÜ=ˆÄcrÌ߈#ƒ§OŽÄUöË‚Ȳœ.]ÃØlV ¥K+Ì^Y[9ÁaÒ¦³J³u*ÔX òšÕxo4ãlÇ1ñ‰¸WŠÌ2|÷Š!€¥G/̱UÖ"8컆²LU¸+‡ñt8sŒ¼,­“e˜£^¾q”g¸ü'âqYåiAåØzo‡¡‚ÊܱXÜî¬ð8;ÔÆCÜs£„¯†N5bëañʤ岧R”eB*š¿<Ÿ¶rŸ»n SúÃKÓôŸÛ5äÑÜßL‹ºëQ»ŠÎÓO[íJíË]j7æÆÆÊÑﯦ¸»– Kxä™Ö ù®#‰ÅºX­:ª„%KNNÔ°Ôg^¶&T0´cjXl?Ö1ë,=S£•ªÎ0Nro±EFöI]ݾ²vQ¼žòvI]¶ì–ºëœfN»®é^Ò¯5­jèÚiö1ù“:Asys+»íìì4ûnu SS¾¸xí4Ý'Mµ»ÔõKù­ì4ëK«Ûˆ “·/Ëñ™®2†KÛbqå„eR• PI9T­ˆÄâ'K „ÂЦ§[ŒÅV£„ÂaéÔÄâ«R¡N¥Hú^Wαøl³-¡õŒf*|”ã*´pôiÆ)ή#‹ÅT£„Àà°´£ ð ØÓ¡’ÖTI´ˆ<]â¹´û­Fký_P°}V}G]NËLÒü;âQiâ ?‹ìüÝéñ¹†‡°xŒ—!¯…ÇâñØl5<÷ˆè¼MHÖŒ¥ mL—'†&–|°”ó sÂ×Åã3<©ÖÊsXäµù1ße˜f™o à1\=Ã8Œg̰xJ¦çŽ–M—âpØ usN]­2Á4Û(­ÚB­<ÓJÒ\ÉæÝ]ÜKwy*}®æîh¢–êy¤†ØÜK¤L–°‚Õx1x‰b«Î³J)¨BQ¥J4iÂKØÒ£Ns…pŒêªP•i©U¨IÉ¿«áüž–C”á²Êu'^Pž/Н:Øê«˜æxÌFgšbi,Ë™âðø|FeŒÅWÂà%ŽÄQËpÕ)eøIC †¡NëœöB€ ( € ( € ( € øö7ÿ“Šÿ‚±Ùÿü9ÿ×YÁ4èïú( € (òÿöÐÿ‚xøsöÍý®ÿaŸ‹?<ðâïìýû6èßµ·Å…|%añjÿ|àmáö¡áßx“Á¾'ðN³'†õÏ ÝjZ׈oô[­!MÞˆ5+Ã$Vødø\6?âÌã‡¡ŠŽoÀÉeì©ÖÄåÙÞ²œú¶=ºÑŠÂЩ‘áó<¹b°•jbåSð’ °˜œEhoÅbkävO‚ÄWÃN—düK™¥Z¥67%˸7ò:˜ Š”›Ä×yÏäxú8lE5„pËëâ¥^¼.•B_ø$ÿüÑX2ÿÁ5`e!•—ö8ýƒ+À‡9A ó]”kVÃV¥ˆÃÕ«CB¬+P¯F¤©V£Z”ÔéU¥V3§Vœã¤$¥ ¥(´Òg;JIÆI4ÓM5tÓѦž5º{Ÿ|[ÛÛÙÛÁii6¶¶°ÅommoAooo,PÁ1*Ç 0Æ«QFª‘¢ª"…TÔ©RµJ•«TZµg:•jÔœ§R¥IÉÊu*NMÊsœ›”ç&å)6Ûm¶gB…58l5X|>•:(P§ ThQ¥ ThÒ¦£ t©Â1…:pŠ„"”b’IT…P@P@Á4ÿäÝ~#ÙÿÿÁX¿õé¿¶E}ÿ@P@P@P@Ü\[Ú[Ïww<6¶¶°Ëqssq*Cooo 4“O<Ò2Ç1F­$²ÈÊ‘¢³»Ò”£Ês’Œbœ¥)4£¥w)7¢I]¶Ý’ÕšQ£WV• ªW¯^¤(Ñ£F«VµZ²P§J•8)N¥J“’„!å95¦ÚGÌš§í‘û>Å©_h>ñçÆOé×Oa}ៀ~ñ?ÆýSLÔ–®?³üEqðÏIñ.‹àÙü›‹yoêÞ±. {›¸h™þf¯d ¤è`ñ“Î14äéÔÃd8\NwV•K){ׯ_^¶'Z®#ˆ«R¾#^¤ëW¯^´ÝJµ«U¨åR­Zµ%)Ô©9Jsœœ¤Ü›dõFA@ŒÊŠÎìTfbª’ÌO’I8“JRQNRj1Šr”¤ÒI%vÛz$–­½ÕŠRŒ#)ÎJ1Šr”¤ÒŒb•ܤމ%vÛvKVyÌß<==Æ™ká[m_âê:ÅÆ‹5×€í­5ÍD¸°šÆ=YüEâg¾³ðÆŠÚJjÜ\é—šÊk×q¥ÌzF‘©Ý[Mn¿WÄ–µ|‡hf\k,ng[+«ˆàú|ß+Êk`êá!™K;Ïç‹ÂäT²èciV¯€ÅfÎ14á^n[ÄP«B?œVñG‡±Œ³ ˜lßÄ9æ9Æ#%­Šà<6=ÉrLF¶žoSˆ¸žxì ä²Êa˜QÄbrÌns ûJšy>Q™â°Õ°Ñ¡/‡þ#x¾ÆöÓÅ^"‡À6ϬÁ=¯ÃAîõ™ô+x¯c—MÖ|[â-)!“U’[ ©ßÂúƒ¨é&Ò[+/ޥɼu2^8âl&+ Ä9Ý.ÃÏ4¥W ‡àl±9¥l¢<\*`sN$ÎòŠs¥S1L"´ø(ÉñÙkÃUÂá3¼\k¼Rà«Ãþ#q†„â®#£À8Z™Í ø ø{Wøskâφ^ñ7Ãõñǃ|;ñ#ÄHÓüyâÝ+ž0ðÂhžÓôÛ?xšK‹™Î©®é× ðŽ•â^Ùtçû,“ƒ1Y…&gžã©ð_ æx.(­“q_å\Hò ÷3á|µcq\=“c2l“6–?9Äâ«å¹TiÒ§õL¿›eõ3¬fY‚¬ñQ穈Œ\¡J/Z¢ªP£Rµ¥ óåUªF¥J|´ÔTê]¾iÆœÕ8ÎK•çi¿ãÕüI¢øÛâõÿ‡þ'øËÀ?|{âïƒÚºxHø^Óáfâý2 ØiF•ý¿¯Çªx›KðUÍö©ü@Ô®YÕ__ñSèö~Ðuù<3kÕŠãi`²¼~CÁX|Ë„ò>#á¾Éxß,çûZ·ã²\]<߯ã?³²éá2œ^}K™a8o M`pq˲xã«ç9Ž]Ú´Ç Í8ÕĸW«Fµj˜i{?f°ñ©MF1çŸ5HÒr„«IóKž§*§ ºkÚëàΠ  ­k[Ó|=§Ëªj×+kg–ðï`Yž{¹ã¶¶‚5PKI4òÇôDÜd•ã…$‘;0 ^g‰†IÖ¯8Ô©ÊšJ4èÓ•Zµ$ÞŠ4éÂR{ÊVP„e9F2ïË2ÌnoŒ†/£*ø‰Âµ^TÒQ¥‡¥:õêÎOE t©ÎoyI¥ qIBóý@Õ¼]{aãOXÞiª-tëÏ |;½½‚âß³‹É5xµ/[ir>ªxâýÿäâ¿à¬_öÿõÖ_ðM:ûþ€ ( € ( € ( € ( € øþ §ÿ&ëñþÏÿþ Åÿ¯Mý²(ïú( € ( € (Ǿ$~Ð>ÝÙiŸ~*øÂ:î« ‡Eð¾§â ã\4Fuµðçƒme¸ñOˆ¯Ó‹MH¿¹0+Ì"ò•œyd™LáO2Í0XJõ]¨ájb!õÌDšæåÃàâåŠÄMÇÞä¡F¤¹S•¬›?EàϼOñ ŠÇp_ñGåxº™ž{‚Ê1k‡2ŠJ~Í×Îx’½:9M‡UZ¤ñ¦c„ ªÊ4ÝNy(¿.ÿ†•ñŸ‹äŽ‚ÿ³?ÆŸ[\ L>1ø‘¦YþÏ>„G/’­zŸ_Nø¿$W,|ûK­àψlî,’Kµ¸ öI{åÿ¬˜ÌcK&á¼ç^ØÌÆ”8¬ìœÖjéæíOâ„ðù>"…çͬþëþ · ðô'Wį<5ášÔ]5S‡83ˆñ‹*óÃÚIaeÀqÆøw”t±3or|E,L¡AÑn8™aÚÞ ý°¼}ß|cøkðO¹…Ú?À7ÄŸiÓ<¿ûůvCÁ÷±#’ŸÙ¶ÖS°\;/™öhGƒâüzÿkÎ2ÜŠœ’棑`¿´q´Û^÷&mCê“IénƒÓ™ï˸—èëÂuoÃÞñ¯ŠØÊ5$éf>*ñ2àÎÆSŒÿuõ¯ü3ŬXj’ŠRª¨xÑ^šæt¢Ÿ/¶©f?ØÓà†­2ß|S°ñWíª í.ÞããÿŒµÿŠ:¼°¹ûe•æŸðã\»ÿ…MáÛ‹[À—P¿…|¡¹Š ö™m­Ú*\’U—´Í!ŠÏêóFnYþ2¾iCžç„éåÕçý“‡”'ïÅáp-%oµ„þ’'à)Ë À˜¼‡Â<²ÄaãKÂ^Êx4xlU«bpøÎ3ÊèÄ@Îh×ùP©û‹3G*3«Nü•ª©ý7¥i:^…§Yé&›§èúNŸ ÛXizU¾Ÿ§XÛ¦vAgei6ÖЦNØ¡‰rp£5ô´©R¡NhÓ§F•5ËN•(F8EmB F)vI#ñ~?šc1ŽgÅæ9†.¬«bñØüMlf3Z_\F':•«Õ•½ê•g)>­š¡ÈP@sÞ(ño†|¤Ë®ø·^Òü;¤E4ÆÿV¼†Î .îŸÊ³±·3:µÞ¡{1XØ[,×—· °ZÁ4Ψ|^ âLƒ…2Ú™Çg~I–S«JƒÆfXªXZ3Äâ'ìðØJ¬”±Ü]V©a0t#WŠ­(ÑÃÒ«VQƒùÞ(âÞàœ¢®}ÅÙöUÙE*Ô0Ï›ãhà¨TÆb§ì°x3­8˘c«5C—ác[ÄJ0´+Vœ`ùY|WâÝrïWÓ<#á«( Ò-nôøÃì¶ÞÔu ãc¥iã*åî·ø}.¥u¤jþ8ñ©â}VÃGŸL½Ó,.u?|?Ô'¾ŽößR½¸ð%¾­}g© ËçÓþÉâ›ÿ[ÛÚÅ[¬Wo=ÌÎÔÇb2ÜË‹sÌÃ?Ìpye\+ƒ¯Èø3[ ]v*·QÌqx\zÅa1rÁ}[ˆq™õzp•O:ÕêÖÃÚ¹–+(ÍøçˆóN'Ͱ5|³•à19§x}˜WÇSÇaó,n#€ðù¶;™¬f<»êœS˜q6…¥NxhÒÅω­ßÙXÙi¶–öu®ŸciÃkeeo­¥´)÷b·¶#†×øR4U…}ž …Àa¨àð8l> ‡‚¥‡Âá(ÓÃá¨RÃN 1…*PHB1ŠèÐ0XY„Ãà2Ü/Àá)FŽ‚ÃÒÂa0Ôa¤)aðÔ!NQû4éÂ1]jº  € ùÆÛÅþ'øý XÜü.Ô/¼%ðcâÃÿˆúkü`¼Aà_Ž^ñtzÂøWšïÇ?>jzÆööÞ#ñ>“ã?¤šmüqx;XÐü+âÏ kÚú…\—*ðã2ÄRâì69ã®âNÅG‚ª,·ˆ|?ͲY`^qœeüQÄü+ÆXLÊ–65jåyN7#É%VSÏ09†q“fØ«.%R¦2 Г§†­F¼~²¹ébéÔæötçF…|< ãe:‘«Vñ—îåu)ϘöÏøWEðÄ ºmªµýÕ®‘o¬ë÷‚;ŸxšmJ´Ñ,5/ëÔS(<_bý®0Ä`äòªRö^¶+ ‚ÃWÄguòêðÂæØ\Ó-Ágzu©Çî%ÁRÊ(ˆ.n_o ø‚ïÃ×wsY<âã=ÊŽ>U1.¦ià¨æQÇà°ùÖ"Š£F¶”ð™V*ym*K.Žm—QÌèÑ…xÓtñÌx¦eðž‘䛞2•\[­œñ_C7Žg—axƒ‡ ð´j`rLmL¢å1Ï2œ>q‡ÃÓÄÆ“¥éòÇÅ…Éø“Äòèße³Ò´]GÄÚåýÁ¶´Ótä1ÚÛ2,2Kw¯k2FöŸo ÄS<׌חHâ='OÔ>ÎU”Ãí«ã1ø\«/ÃSUkb±Ræ­UIÔŒ(åØÉbsMYÓœ# P¥$åŒÄá)^ªü×|BÄp¯ö~WÃ|%žøÅùÖ6X · ȩʆ_:xJøœÇŒ¸ª½™/d˜,.7 ‹«‰Í*TÍ3UÉxƒ1qÀÉ‹àû ½nÓĺöÍk\Ó>×$‹u—£C5íì°\iš-Åõõ•¦·ý›qm¦jô`__%£~Áks-€o;ÄÑÀWʲîlìeŽŒ])bñÓ†„*RÅã©áðõëeÿZ§W†Ë§|>U’©õšÔ¡ˆyÃÂì—2âì³øÏØñgpÿö¯^žaC‡xS ŠÍ³\FÃü+ÍóŒ¯-âå‘cp?ñ•ã8£–ÎXG’åÙ†#&_^)úpP@P@P@P@ðìoÿ'ÿbÿ³ÿøsÿ®²ÿ‚iÐßôP@P@P@P@PÀðM?ù7_ˆßöÿðV/ýzoí‘@Ð@P@Eñ ö€øð”¢|QøËð·áäòÉ6öž3ñï…ü9}wq4¾D¶V¶©k{{wq9[ÚZA5ÍÄäC O) |œÃ?Èò›,Ï8Êòù6”aŒÇápõ')>XÆêÕŒç9KÝŒ!JRÒ)½иCÂO<@R—xoÇ\aJœ'R¶#†øS=Îp¸zTáíj×Äâðøl6•/ÞÖÄb*Ó£J’u*N0NG˜Iû_xV-Ã/|zøÏxBý˜ü>ø)ã› _<›¼¨ì¾'üIÓ¾ü ¤ÚK·ü,ŠÍµ - º´–1ñv®™f=Îgö³òlt0ÕÙC3Ì©åùD›êÿ´-ã*Žœ¾æGŽ,À%Wø·Â °é¿l¸»ÄÎÅgxXÆÞÒxžà¼gø‰IC™(ÇýPu1U)áa^¥ D)Aÿ í—ãÓ*è^ø9û>hó[Æm5Ÿ‰¾"Ö>7øò¦ÎPøkðâçÀžÓä³\ySÙ|wñ<7 ÂKXíàV½Ÿ¬qŽ>ê†'áú2Šå­™b+gxôÞê¦[—K‚¦à¶”3ÌTe+Þ*1¼ôþÉú7𢄳^,ñÅÜÆY¬F]Á9>]ᇠT§MþîxN5ã*ÙøÅZÖX´“i¿|uãÄ»247²ÜÙ%½¤ýX«‹|Ù×g™¢o™áhb–G—Çû‘£’CŠ­Gw*y†;§w¹C–ŸøŽX‚ †žøYÀ³…7MgÙžCSÅ.¯w®&¶gâŽ#в·0µ¡OÂ+’誘jtq2­ˆ«ì þüø?oqmð³á‡>‹×yu+ x_Gѵ ^æR{ÍoT³´‹RÖ¯î]D—Z†«wy{u ó.'–Oš½|»%Ê2ˆÊ9^YÀs¶êK …£F¥i=e:õaRµI=gR¬ç9½e&õ?;ã?¼EñµüwÇUů ÓÁRÏóÜÇ2Âeô`š§†Ë08œDðyfŒdãC €¡‡ÃPƒp¥JÐõôφ ( € (‹Õ<á+_Ó¼(nnõ/j°Mug¢hºf¡«Ý-¬2^Û›ÍR{y´ïiÒ^é÷štZ¯ˆ¯t-õ$²ûh¸G~[0ã<‡/Îp\:ñœ~˜Q©ˆÂå9V™â#‡§<]­f°”*`²\ñX,V žcžbòܾXê3Â}iWŒ ¾/5ñ†r¬ÿ.áG‰Åæ\OšáêâðyI–fÆ*8ZU1¸®æ•ð8jÙweÕ1¹~3.¥šñ7(ʧ˜Ð«‚úêÄBp9 üVñu¦›sq%Â(SX¹–ÿL€i~;ñf£¡%½ƒéÑI©È‰á/ j“^R^ÎÖËâ±±‹O—M×­®înRÏÂ¥x— ¯ZxO )C3¯S—ÒþÏã#Çe£ƒ–”ñóŒ8o‡3 ¸§˜SÌðØ|'PxJx*˜æ†"½xá¾nŒ|Wâü&[‰ÄTÀøABÆ*®?+ ²¾<âÜË!§‡Ëç–Ò©™Ô„8G„óJØ×šÑÍðxLˆxg€¥—UËsü./І¦ðÿ€ü3á«Ý_SÓí/.5Mrõ/õ=S[Ö5ŸêSM·ÒÙÅ î¿©Occ§i_E¦i¶k§éÐ\Ë ¬1¹SïäÜd8¬Ë‚Ãb«f¾.Ì~a›fy¦yŽ«V•L]L-:X¬ãŽ­„Â`¾½‹§€À`å‡Á`hשK ‡¥ 8¿§áþáŽÆæùž]„Æb3\÷ ~gšgyÎsÄy•jÔ*ãª`éQÆçøüʾ—ic©e™fx\».¡‰«C…£NN/±¯§>Ä( €?ƒ1|y‘b8Ƭ¸v—d8z•haeÃÇÒ„³Ì^qŠ£W“PÀƤqò…Jð«*¸¨s<]Ú¤Þ!Ç-U‡^ÙѪìåí¹[öjšiÔr·-ì×3I«\üxñÅö;‡Ÿ môoŒíoxÚßö¿Å›ßüÑíå2ßiPèúŸÃ;O†üm©˜¢ÓŽ¥uñ/Hðv ꆙâ kS6‚*^呟·Äq7UÇp*«A` ƒhp爘ê‘äÃã*c°œW[Šøg!ÂsÏõZ<)Ï3*‹ †Åå¸ '׳"ø¹ì¨ÐQÄÙóóbl$SÖ<² ¨V«+ró:ñ§yFr—,Óইs&“wãÏø×ⶥᯊÏÅoê>8Ô´›Cá-cQŠþÏGÐô½+À¼;¨xgÁz^¡6á;_è¾ ÕmÙ ×ucZñ\ð4ÖãÌ}(ã(ðîS‘p~5á,gxlƒ ¬³¬xjøìÃŒâ<È3<6kŸbð´ñYÍl§–àê©TËðXO?ìÔ,,_+­R®"PÄKNUeìå.eF4aJ…(ÉÆš©Éi9Ju9ìµðçHP“‰£ÅløU§Gã-jÛYÒn,Wì_ <¦|Üa‰–@éͪ™7²UøšNž+7Áb(UÊ¥:rÊkÑÆä¸¼&žs</ W—âg€«ÆÒÄ?¾|ò:œÜyŒŸ ºU«{âxÂnŽ7=ËñXz¹$ªR–G‰Ãæ<;ŽË±tx‚¦_ŽÀ×ÅeXÉåµòÜÂŽ)¬¿ "ñAy¾(jMã‹yL‡þù¡6¿bŠH¼Ufmoü.$’ßÅÈtŸ\i7‹âãªi7çDðÖ»ƒ§x‹G¶ÔÔ‡Ï(Q‡ aÕ‡/ü-Â~Û‰§8Ï(®«a³~XÕÉd±™5,e’¬&3õü×/–c‰Ë1ÕpŒ§Ç2%| ‚\/Zš‡üdTê*ü_:©‘bU|&zã Ü?%È(ãðï‡Ö0¬Ï:ʧ›c2|ƶ ú½|qð!@G4Ñ[Å-ÅıÁ¼ÓM3¬qCj^Ie‘ʤqÆÝØ*¨,ÄMT!:³…:p•J•%S§¹Îs›QŒ!§)JRiF)7&ÒI¶aŠÅa°XlF7ˆ¡„Áá(UÅbñxª´èa°ØjåV¾#^¬¡J 4¡:•jÔœiÓ§NrQMœÖ§ñþŽïàjM3H¾E °7"Áww á»mR¬ï¡¾´k†ÒüP‹ªh¢DÛÚꉌ}ÔiðÞ:+ˆ0Åãpî3žCSìãRISY¥\I×ÃÏ]SX¼¢Oq“…ZØI^ÿŒ.+ÆxÓÂÕªxEÅ•²ÍãˆÃá¼XÁä¿]­^ž 3ÁÑÆÏ€ðEƒÃeYÆ7Ë*cgþ"R‡ðœkQŽ/–ñ '³GѬ´K8¬í>Ñ)HmâžúúækýSP{kxí£¹Ôõ+§’óP»òbŽ3qs,Ž4Ù"/ŽÇWÇ×zÞÎ S«:x|=(að˜hÕ©*²¥„ÂÒQ£†£í')*t¡¹JS—4å)?Òø_…r®ʰÙ^YõÜL©a°8|fqœcñYÏg•p*} ÈsìÂ¥|Ï;Ìž BŒ±¸üEj±£J–—²ÃQ£Fž­qŸFP@P@P@P@ðìoÿ'ÿbÿ³ÿøsÿ®²ÿ‚iÐßôP@P@P@P@PÀðM?ù7_ˆßöÿðV/ýzoí‘@ÐÏþ/ý«fŸj±ø{žXx¢o7ìþ·ñ¦‰«øÖìA<Ùx3F»Ô޹T³¼Eã|d*ÅOÀ¾åÙK^§í'O‹¸×‹(gzœþÍQ§SÃzñ© TZ˜yÂ4ª´ü&ý§|N„xÛö¯O $ͺK_Ù÷à‚|ÐÃçG"Ù®©ñ·Qý£.&cn¯g{©[Úiw^l·Úe·‡ç6ÑÚ/ìž&Å/öÞ*XD÷†A’à°m.dù=®uSˆ¤ýÔá:‘…)Jît㇗*ƒ\à†G$øgÀ9ñ©«B¿‹¾'q7F¥Og8( € ( €9?xóÁ>ûñg‹¼5á¶ÕoN›¥G­ëzv™6©¨ªG#ØiÐÞ\Å-õäqMÒ[Z¤³G ¬Ò"Äw×Îg¼aœ1õ5Ä|K‘dRÌqo—Ã6Íp8 ¹†9F–K^Lf*êÓ«:xÔ« SUgÓ|ÇÉñ'ðGg®,âþá¹æØç–eT³Ìï-Ë+f™ŒaN¤ðmf&•l~2*Ô«TÃaaZµ:3j'Îs·~<Ö5câM7ÀÔõ}k@¾‡K7~2´×üàû«ßí ì5&ÓüA¨xzúó]¶Ñż—S^xwEÕt½A 0iú¬²Ë#ÁáâxÃ4ÌžyàÎÇæy®MŒ§—¼Oa³žáœF+ëµ°x÷ƒÎq¹./œPÊÕâ*ârL¯0Ëñ±tèೕ*NT¾sǹÎlø“-ðÿ„3<ã;áüu­âøË ŸðⱿÚ5òüÊYf<=ŽÆgØl™aêbëã8w%Ír¼Â »5«V¬åAïà}g_»¼¸ñ¯‹uCHÔôt›¯è±Úi‚k› õ«¯í8-#ñŽ¥=Æ¡ÛéòϯYÛZi÷_e:cÜ o%¹p–iœâqUø«‰1ØÜ³“ÒËq•C –ðåµðxHf¸¯ÒÃC‰ñõkcibg‚©[8ÃPÃ`±Wx VSÄÔ¹ð6uŸâñ¸Ž6âÜÇ0Ê3<‚ŽQŠà,–ž(á<=|NO:ÅiÐÂSã,ʾ#0¡Œ©—Õ¯Ÿ`ðØ<»õW–O˜Ê½žáý ºE‡ü3£i~дØÞ-?GѬm´Ý6Ê9%’âU¶²´Ž+x|Û‰e¸˜¤a¦žYg´²;·ÔäÙ.Oùf%Èr¼¿%Ê0•<Y•á(`p8XN¤ëTT0¸hS£OÚV©RµW'VµJ•j9TœäþÓ á쇅2|ðÆM•ðöE–Ó,¿'Ép8l·-ÁS©Z¦"¬pØ,:8z>ÛZ¶"³…5*ØŠÕkÕs«Rs–Åzg°Ÿ«júVƒ¦Þë:î§§èº>›o%Þ£ªê×¶Úv›akÌ·7·×’ÃkkoæI§–8ÐrÌ+§‚Æf8ª¿ ‰ÇãqUcG ƒÁЫŠÅbkMÚ¨aèFu«U›Ò0§ JOdÅ)Frœ”cyJMF)um½ólò¯üjÑôvø¦xGÂ~:ø³ãO†—ÞÓüQà‡ºN—ˆbŸâ¶¢}‹Wñþ¹àO^‹-"ñ|KâÆb}ÃÈ/õ(¡{­: ß°Ê8Ž\7‹Î³žà܋аüC‰Êx“‰q˜Éå³§ÃP¬±ÿXÁpÞ_Ä\GAׯÑyVY)dNže™Éá𳨨âªP穊Œ}´iÓ«ˆ«AÒJ4c{Ö·-¥ZT©;EóÏ÷¾ä5•®“5+ŽÞ#»ñ¦—muðûá~‘aâÏ 7Ãïi—z·Å?ø¯Áp[é÷þ5_x;YðßÃÝá÷ˆ/¯Ž£á½ t¿üM³·Ó£‹Ä÷WöäxzÅajø{–Qȱ•hñ/ãq>r¸“$ÅÑÁðŽ[“çµ*bpÙ ÉóÌiĸþ%Ëpô>­šæ/”ðz¸©O)£MФójpå©](Ú£w|‰Ú‡ÀŸ‡¾#“ÄÇö7Ÿ4Ýs⇾%ÙøwâØñÏ…ü⯠}†_ ÜøúÜ7O…aÐ5>ß[ÒãÓmR[}~´'mD­Â¬7ˆ\K–G-|9ˆ¡Â¬¿†ó>¯™ð•Ãù¶w”g_X†oKˆs<éã3Š™–S‹–*´¡W.¶ÓXkÓd°”fçí“ÄFU¡]B»ö´éT§oféBiÆŸ$¢§Uu;ÎüÚžÇ_t…E<ðÛC5ÍÌÑ[ÛÛÅ$÷È‘C1!’Y¦–B©Q¢³É#²¢"–b&®:•jB•(N­Z³:téÆS©R¤ä£BNSœäÔc§)I¤“lÒ•*µêÓ£FJÕ«T…*T©BU*Õ«RJéÓ§ç:“›Q„"œ¥&’M³Ì%øsâuiðÏD“ÅwH·§ˆ5ºÑ< kx4ÝRm>iuém%¸×,%Ô­´˜.$𥎴ÇLÖ ÕìšêÞ7ë!ò×J·ãã“Q“£RYnRÇñ j„†&ËáZ°ˆájã*RŽqˆÀ¥‹ÀÔÁWTjM5÷ø:ŽPèb8Û3†ABRÃÕ–QƒT3>)¯‡úîž.œ2ªxˆQÊñpÁVÌ*цŠËRÇeµrüLhVœZ°ÿc×î>ÙñV›ÆžUó]Øxq¡}+ÀšrÛê÷z†_ÂÝ\Åâ-FÆßûK¨øÆ÷ÄI·¢Câ XxRK©lS8ñD²Ú~ÆpTò.|:£ˆÌÕHã8‡êà¨á±ª9ÅJ4§–a±>½É…É(e’–0©–渌â4¡ˆ–1ã)å4~­Áù}.sª¼åTŽaÅXÉVËðøLÅG?©BLŸŠ­ý¤éàøs “ÎyfiS)Îñyü(SÅKÒcŽ8£H¢DŠ(‘cŽ8Ô$qÆ€*""€¨ˆ *ª€WËJRœ¥9ÊSœäå9ɹJR“nR”›nR“m¶Ûm¶Û¹ñsœêNU*JSœå)Îs“”ç97)JR“nR“mÊM¶Ûm»±õ$…›¨jÖ:`Œ]Jæy¹·²µ‚{íFåDÐC,–ºuœsÞÜÅn×=ܰÀñÙÀÆâéâEêÃ`ñ¾gJ S†•+Ö©O†¤Ü*T„kb«Ê Sª©MQ…J‘z‹ÙÒS¨Ô_ƒñ6OÃêŒs MYc1K›”åØL^ožæã‰Âaq²ü+¡ŒÍñø| Lv®g‰Â`ëPË0“–;0©†ÁÓ«^Æ™¦x‹\këÃ¥Áa%܃Ið¾›syuZqµÔ´éG‰ïL–Ö^"}ZÒüOu£M¦I£iw0Zýž]NêÖ=LúؼVW€XzYL]\DhÅãs|]*jOí°¸¨<¦‚Zùdpu°ÞΖ:¸ã±tªVö±ÂÑ«,!ùïäuÅÓͱÞ.ax{“ÖÌ«®ðï Çæ¹– ’<¿>Èññ 6•l¿)ãªÜI–ç+˜p¦+‡ëp¯ãðYÔqþa—Ñâ w ªŠ¨ŠUPUT`*€àW€Û“r“nM¶Ûm¶Û»m½[oVÞ­Ÿ¯B¥S§Ó§N1…:pŠ„!%BŠQŒc£Å$’I+ H  € ( € ( € ( € ( €>ýÿäâ¿à¬_öÿõÖ_ðM:ûþ€ ( € ( € (ðþ?øsãMwþ Ÿñëâ&…ûCüoøqáÿé_c×~ü?›áN›ðãâÒë¿<¡ËÄíSZøU¯ü^¸Óm´ý^ê1 ø'â¯ü;ªí|I¤k‘!ðÊ?ÙüIð’½_öúÏ<3ÁC/Ƹ`êÉa]fT)á~­[e…­KûN¾a‚Âb0,V †ÅB­zÞ¾+’ñ~ìu)ø{âv.¦? £,n"•ͱQË꾫§xvÛöuØjoÚâãöSfŽîRßuO‹6ëk§ü3fÿ„r÷À-¨|2¶±Óü¶‹p„WÑàÌ_;ÄpöKô’¯WûVÙœ¸“á¿pÿ pF7>§ŽUßbøw,ñ שW3ž"¥j¼+’g8ùã1x*ÏéñWüb±ã œ?|ž9Î èãO ©G,ÿW«x‡™g8^3Åäp1¡<ަyCÃùR•L­á%†Äñ^wWèbñ±ÅBÛKÅÚ‡ìwñëö­ý‚?f¥ÿ…û7þÐoü é~ øSqqðÓÂÿ¦ý¨?jÿþÏ_´ïÂH|,öVô¿‰_<9i£‚ì´m3LñíÇŠtÝ2ëÚΩ}'Iq†7(á¾#©_5Ër¯ð9/&6½\MlÇ„eà¾gâ«àŒUjÒ©ˆÆdµx—ƒ§„Áå5+{)eø¶ãâ§ñûÇ!aÕþ1ëøài¿|â?gP𮳦C…AÒ/ol%ý'G ”øÍ˜ð&W„¥–ðn+&ñ“)¥ÃXh<6Y€«Ày7‰8c8Ãà¡'Fž„¯ÃK ŽÎy^k›ás|â®7ñÕý§æÙv7žøK[óŠï1âºð׌¡žU—6.8Ž-ãŽáÎ ÉèTвáÜ^QÆ8ì&$Šyf\²ü–® FU‚­Gú\¯“> þxbϳsKñ+à?Œÿà©ß>üQÔ?à£ÿðRŸ i²Fñ£öQð'Å«¿øßþ mûR xbóBñv‹®üv¾ñÄ)uÝ+Zð–•ữ êzÌ*ÑbÐí¯í®´Ùn< nOÆâ§Uñm‚Á¾NL]K*¡*pŒý®2¶]ŠÇÉ΢Káñ8^^~Eußë|5â7 pÎC†ÁGÁ¸—‰!õ—ˆâî1Çñök_š®3[ ð<9–ñ–GÂT£…ÂÔÃàÜ3l—=Uþ¬ñqyÅ~¸ØŸöuÔb–/xSÄ–âv¸¹‡ö€ø—ñ/ã¾—,Žc-^ø¯âßx[N±o)ÒtOÒH~úiÚNOõ/‡ª&±ø\NuÍ'),ÿ2̳ÚM¶®–5Åâð´éè¿uJ…:[û—roèâf|bÁÔ…NÏòo %F’£F¯„œÁ^c¡©¨Î¦sÀ,Ì>­‰úÖ& .#.ÂæQÄeø…ŠÁáhUÂb1Bãn9ÍðL_ xWšR©_5¯‚«KÄ. ʸ-Q˨ÑË«SÎÕ,ž—f¿TÆÕ¨ÅåÀ:k團O>£WŠr\‡‰œ!ÃÕø‡¥‰ÏòÚPÆNsÄfþ Ì3<“_€Tè¬48f…,%Ô­õŒm>JK¯“ø¡šÔâl=~4áîËqu)Óá\G ð¤ñœO”P¥˜Nu1Y¦gÅ9®qù–'–*Xuƒ§Áøz9~2U±[Ìi*tãøm¦M¥jºïˆ|oâmCKÑŠçTñf«c¢êâk)¬/õ=oÁž—ÃþÔu]J ™þÑ4ž[kg*ÓNUEZ‡`*ã2ìÇ7θ³>ÆåùSʤó$Ìp™^dªá*àñ™†mÂÙ L—„1ÙŽ:zÞÚ¬òС*åølPŒnŸ†¹el~Sšç¼CÆüM˜åy#É&ó^-Íp9.n«àkeù†ižp_ Õáþ̳\ʆ"¿Ö+Ôáxá°³ªÞU„Ë£F;Þð_„<§E¤x?ÂÞð¾™ ÷QØh=†“j.ïbòñ¡±·öôE½¼5ÍÛF­q,¬¯c á^áL <·†x{%áü*ÕñÁäÙf-Ãýg ±X©SÂQ¥â±J?­bf¥_áW©RI3ßᎠàþ Ë©åÂÜ=ÂÙ]øŒ\0?“åùFc1ŠšÆãeG‡¡ ãq¾Ê“Æã*)bqr§bjÕ’Lé«ß>˜(/[×4_ èú§ˆ|I¬i~Ð4K­SZ×5½BÓJÑô2Ê'¸½ÔuMNþh,¬,m Gžêîîx­à‰IdDRï—ãó\v,ÊðX¼Ë2Çâ)a09~ [ŽÆâñTèa°˜L<*WÄâ+T”iÒ£FœêTœ”aI¤LçFSœ£E9Js’Œc«”¤ÚI%«mÙnÏ%Ô~9h×O¯éŸ ¼-ãO‹ž#Ó¾èÿ|;ƒô+‹/øóEñ<×vþÓ¼#ñÃÅðü ÕuÍsìrj'G·ø‰.­¥øvK?êvúN«¢Ï©ýžÃüu—b¸§7ȸ/,Äñn7ƒóJ™ÞaN¿ðî?)…™Æ':à ŸûKÄ _—ûxá¾½W†aƒÅæq¯•á15q¸<}<'<±Q|ñ¡N®"j„qöpj•XTmSTñU90’œ­ÍÊ«9Fœ’Œ å.·¤|lñ€ñ–“ŠôO„žÖ|#á¨|â Xéþ*ø­á?O$·0¼Ô ñ¦¯|/º¶³ƒìÚg‡!—ÃÞ#¶’à^jš’O [iË oä#ÆTÉñüg™`s¬Ö¦}”çXŒNQÁùÎKN0§’PÃTȱÙwÒ«^§µÅ擆g•Õ'C …•9ª¸©9ÇSÚÅTާJ¥4ªb)ÔzÔrUa: -#á5{ÊWÒ%«ÿ_ µûïßxÛH½ø“Ä[/Xø·ÃŸõÝkÇŸnÓá¼ÿÚ¹Ò>ø–ûPø[áû]s>%¿¿ðwƒÜ&/ê´3ª>ÏŸûc†8«†ù¹ù~¯þ³pöeývܲößRþÑúßÕïKë>ÇØ{zÓÛBŸí'û$ø£â·Ž¾|vø-ñoJø)ûG~Î6þ5Ð|ã|4“âçïxâv•¢é>üPøk§xÿáOˆüIáf øcĺCøkâ·‚5Ÿx·Ã:6«©ydºŽ“©8O…ÎslßV(qK>Ïpª518|nžmG<ʱT],Nxlç"Ì©VžW“Äaã…ÌóŒ//ÅQǵKÍÂaððἫ‡1ʾ&žG˜eyÖSŽÂÕ†„ÍòÜ›1È*ÍB½„òÌã+Íqx\ç/t•|Dià+ás 7.ÂbéüÿuÿÀKŸ…±Ê¿¯­¿kÈ¿jØÿný©­þèQiÇöŽO' Iðv?"ÉðFo…¿n~‰ð’Ü|:Ýß_ÅîÞ1:Ó›Êëpœørø*<#—ñfQC›Jy´sÌ·q™ÎiÆ´¸ŽX)dk_;Î3ºÙÍ*ùd2Šy^c–ä2ÀáéárŠXJšÏþÖŸíûâÿÖÊ|)K,®SËe“Ç?°ŸK‡?´%žÇS&©P¯?í(çqõ³^$úÕ PÏ+Ч6³ÿÆâçié?ioãßÚö¡“àüº·Æï„¿ ¬>i_[öpÖÛÅß³Ì_¾x“ÅßµMßá¯%¾ñôÍ㟉5øŸXÖ?´5+}{ Lˆß.Ãd‹‡ª×Ës,“ŽW‰4óLtéæU1¼],M’Öxì59f§Uá¼ Âï$§Ue‘âsHWÍ1–gŠÌ§\òÅâñ•3ªx|Ç/ÅðNgáËʨ,V O„óšÙÞ/5P«,f2¼sìv?=«˜ÕÎ)N•xÜ«‡jáòÊɨ§wàŸØSÅž-øƒãŸ‹?¶·ÆþÓ;ñOì÷âÙ[D°ðÀÉ>|6ð·Á_ˆ–ZŸÄûuð–µñ_㟉u|OÔ4øÇij|@³ðêéþÒ4Ï øÃ0 Iµ>lË/Ës‡‹²W‡ÄaªqÒÊ©ñ6†-Þ†‡©çˇ°=Jt¥S+Âe8ž%ÍóHTÌ1YÞe‰Íjáq5óQËð8\>˜­JT09Žb°¹„–[•dX<.œRŽ ÚS•./àßü¿âo…5ÿÙ{JøçûRZüyø/û\Oö`ð0ø#oà?ˆ'Ä:g€¼Cð«Á^+ý¡þ*Kñ?ÆÚgÆ/|>øyâ}_IðÅß¾| °¸ÖçOxŸLñµmi-¿¼ó|V+:Ìø·5 Wf¹6k’b3,%—åT©q'-Äñ&k„Ê$ñ˜Œ>}Ä ,§…ÇâVq,ª– œarÜ—Ká¼¹eØl6SO†rUS-áXæù~k<®¬Þ?ðù./ûO‡øzžg/aìøw#Í¡…Ç`°ÏS3«ý•“áñ¹¶*ޱ¿ªåÇÀðM?ù7_ˆßöÿðV/ýzoí‘@Ð@P5ïˆü=¦Þ&Ÿ¨ëÚ5…üOs•î©cky%µ­­ÅíÍÂ[O½iÊ4éT”|\wpîYŒ†_™gù._©B¾*žš`p˜Ê˜l.ŽÅb!†¯^iÐÃà°x¼]zу§G …Äâ*J4¨Uœxˆþ3ü2¼Ó-5­ÅPx«J¿Ô®´‹;ïiúÇŽà¹Ô,ᵸ¹‚&ðvŸ®Xá¾³“íû3‹»qÎfŒ7ÉÃÅ>Äà0Ù¦WÄ4x‹/ÆcñnÂx,Ï‹èׯáiaëW¥N\1‚ÍÜ£ XÌ,ýµ½„Ö&‚…Y:°RøŠ~4øcŒË0yÖKÅX~+ʳ Ë”`±Ü—çyC˜`¨áqš4¥Á¹vzåtqØ*ŸX·Õ¦±xgZnµ5+w¿.cÛhÿþ øƒPðñH㵷Ѭ4+}záµ{‹@Ö¼a«øsB¾Š¸:œ×‡RŠÑ´Ëk©-&»¼HìeéÅñž"Ï(eœÆ™Ö7%å„0ôr¼QC8­,u ©äù¯fYQ‹§JÞ>®)ã©á¥€¡ˆž®'a*uã|AÅSŸar¼Bâ ǹ)ÓÂáòlE†ÏñÌpØ RÈ3®0Íøs"ÇS¡Oó:ØÇ™RÁÏ,Ãbªa+âñ´éàj¾ÿ‰ú†¡¥²xsÂ^Ðn4±q©Ë«x‡PÖ¼Qc«Kê9t=#I¶Ðfµ´iÓɨEã9êÉ{i¥¡† ùê–7ñ¸Ì¾QÈøo#Éëeê¶a<Ë:Ææ¼A„̪CŽS–e´2j¸|4Ö ¬ñ´ø¦«ÅFx¬40ØoeKZèæ(f9†W(pçðæCˆÊ–#4«›ñañF7«O–Ç"Ê2Œ6A[ ƒ¨²êõ3\iYãcSƒ§ƒÂ:41õèÇàjzUµ§‹~'ë×Ñê3^Ý\øLÓ¼ eym%µ”QiF)‰µËk+k‹k«¸¤¶ñ,z„¨M ÕíÌÚ¤<”øK‰qùu 7qþo_OWˆ¯ÂY~„0¸ª¡…§O.t¦óìÚ†Z†#Nt3Øc',mZXœUz4°ð¥ÃO¸¿3ʰØN.ñC>Äc©æU±¸¬Geyo`±˜j˜l *YK¥7ÄÙî…Äa±XºU0ÜKO0©,µ ^7BŽu£øiàU½×5 Úê·^$œ\k-¯Í{â8î™5Õ`…-µëJÚÎÊÓQŽ+Ë >Æk >hak+h<˜ÂúPà>Ž+7Æ×É0ùŽ#=¬«æ’Îjâ³Êx‰C Æ(PÎ1ê\.Nž'‚ÂS¡ƒÁU¥JXJ}œ9}j~p,q¹îaˆáÜ&kŠâJñÄg3â ØÞ$§Š•<­ 0ÃgøœË ƒÁ`ót±˜ ¿G €ËëQ£,ìi(v°Á ´QÁo VðD¡"†Ò(£AÑc¢(쪕õT©R¡NhS§F•8¨Ó¥J§N[F‚QŒWE‘öÔhQÃR§CF–…(¨R£Fœ)R§´iÓ‚Œ!Ò1I.ĵ¡¨P@^¡ñáö•©A£j^9ð–µuá­WÆvš-Lj´ˆõ›ßèF1­xžËI7Ú7º’ÒÄšŽ¯km5…œ“Eó£ÊŠÞî†8“…©ÂäÖ#G5Áäuñô²Ìl°43¬Ã™à2œF5Qúµ Ë¡RXlj°ÄWŒ'*tåI¬¥^ŒeË*´ÔÜ%UEÎ<Μ>*Š7æpýé%e}YÅÙüjÒüOý†>xcÅž<¶ñwÃ}gâ/„üUi£^èÿo’Å ·Ñü5¬xÛU·ŠÄ~#¼¸U±ÒçÓnµ K+{íGP²·†Ùv¿bòŸíŶOÃÕr^)ÀðÆs”VÇañÜO‡–!T©ÍpY¬êfY^WF“xŒe ^­J²tóX¨Ôåö0©UT¡*Ôê(¸Ñv²Œ%VKÜœÛÒ..I')%mk 㟌à‰õÏxoá‰âO‚÷Z6¹á¿é)ã/‰ÞøÏâ‚_x7ã‹[þö«¢x ŦµÐ´_þÏ:´#ñ“â fdÐíÂ:ž¯áþERk/ÉóN5Çå|wK€Í8‹,„ørÞnL—<àœ™¬¸<b+f8ü¯ÄÌ%L³,ö™nœ³ Ñΰ«—Wâ© 4'†q”)GÚ×£ŠŸü¼¥‰©û™FŠº„gƒ’œí9>Eì嵤|ðEõ–¹­A©xãÅq|.Ò¾k)ñ¾£.µ}â¿iÓ}²ïþMÏÁ—:Ljõ"ÚŠ5k é×ÜÂÚÒçþ%Zv—§Xðãxë?Äaëåø ˜^ÉçÅØÎ5Áe >Oâ¡ì(ÿeãäëç´°Y^,.QƒÄæøªx:µ©¶b±˜¬EG I59)U©õxá¥R¬œÝJqw~Ò:Rrœ½ê’TâæìŸº£ê‘ÇQ¤Q"EH±Çj8ã@@TDPU@ +ä%)NRœå)ÎrrœäÜ¥)I·)JM·)I¶Ûm¶ÛmÜè1õïxoÂÖgPñ?ˆ4OX#í{V°Ò,†Y˜Üêð`É!/„@]°£5Û—eY¦o]a²œ·šb[ŠX|»ˆÆ×n¤¹ •,5:µœÚŒW-å'Ê®ÏO*É3œ÷°y&Q™ç8¶à–*ÀbóCu%ÉM*JUª·RmB –ò“åÞ‡"ÿ,nüèü-á¯øÂâ#,`i~¹Ò4É'‰®cx£ñŒÃ^¸1Mgwouö=^éì®`û%ÚEyqemuíG…qy%›æ¹IJ\’[Ìécqq§5JJrÊòEšæ´Ô¡^J>ßF8ŠU=µN…:õhý x'‡ösÏs¾áÚ3P›xìâ†c…)ª3IäÜ:³¼ê’%âlG$ÔáA`x_/…YS«r§‰­!Çc)S©*5f§Êç?eV‚´jÃMSáѼh¥ñ7(”Øx.¬Åît«K²—²ÞGϪÛÙjz…¥Â—ª pʲ,ƒ+ŒÕHûO¨kb¹*,T>±žÔÌÜ¥Œ­Gš„(JpXyVuká0Õ©)qŠÃªÉ8k†2hTU"êfnc=Xã)º[âZ¹Ã¦£Gˆ¡Í†§†JqÂμ«bp8o͸£ÅLHéá8s…¸Ïļê¶/0Ëã”pQ†ÇQÂcòØeµ1XL÷Šs¼Ã"àŽ¯ì³|#G‰xŸ*ÄæZ•±9e u,&.T[©ÚxãSžÝ,5ÂÚz\^ Ö‹NëWv±Ü§½•ÕäÖN‘-͸i/b¹Ò<@¶îV y¥æå^¾A„§VXœ;7ÄÊ•`§ŠŽY€£ZTÛĬEÄã1°¥RÑ¡:XܵÔW©VÒ“Ž Ë<^â f–KÅ-áÖGK™¬Úxl†·qfg—ÐÆÆ9-L§1Í1Y/ ðÎ'‚U+æÔ1ü1ưÁU”0˜,V"òÇS’ëÀ¾Ôïìõ=oK‡ÄúmÍÕÖ™q¯ÄÔi²ÜßϨ+X[]o³¶’ÊIÌ1Û‹ËKH ¶Žçˉjhño…Ã×Â`1sËpغThâéeßìo Xzxf±5ir׫ñ§í14§UЭ^u*Ê—4Ù¾eà÷‡™þs•ñðö³œ‡˜æÿ € ( € ( € ( € ( €?1¿àœ<¶¶øñ{@´ðÇŽµmCFý¼à¬÷³Keá-V×E½‘?à§ÿµö¡†‘â}n='ýõÌW°AXkÛÇ~.ôû»›kÍ;R†Óä1|cB†k™äØnâüË•àêbªÔÂpÞc‡Ê±S††-ÏóXe¹eŒ¯OF8àó:Ô!ŒXŒ"½ N K ð˜Þ=Ãa³¼ã‡ðœ/Çy¶a’à*ãkÕÁpŽk…Éqµ!€ÂfòüŸŠ3ºyG føüM,m ¡€Îká©ãÖ//ÅâpØÌ»2£„ûÊøÒþ {/…Zþž5;¹£ÕmüSâ?é—~²†ú u¾¸‹ÃÚç‹á¾{Ë9&Ôl¬¬®^qeÔΙy(…9)q7c)e°žç8%ÄÕ†cGˆsÎÀbrl%,]+^žI›q5<\±XYÖÇa0¸Zò­ÉGêø÷—âj*Qá£Åük£×ÁxSŸåë3ÆV¥›aø«ˆø3,ÆpþŽ;‡Ž;K‡sÞ0£Ž–3R¾e‚Á`±3Ä*t>«™ÿeã*ª0h›ãíž´§Mømá»õÔ-óoÅ6³›KYîEõεkýðþkmB[e³kK+Ë»x'žæ9µ ˆí"’õ{_qXlÖ/À™28Ü4rJßÚÜAÅxj¹z­ˆXÊù®ûƒ*áñµ(G ,6 ŠÄÐ¥Z¶"qµá‡§S•oq¸<î/-ð׆³æHpåuñGàëeQÄbV;á_ø}_ ˜UÂÇ<&Åáèb+âiÖÌ1ð”ªãlG¡|Cžá$Ô~ i°[·‡ÖÆæÛþ ·ÓÝ|C.’¶·zýν®ø¤Ám¯¿VÑô[Èu´„Ǧê·Úê+Üɼ2~6­ZÆñ¦dË ^†I”0RYÕLµañ9ƾq›ñ £B–eÍ™e™^&–6j|˜Ç›Â2¯>Šyˆ•ñêf> å´0òáøàq8nàŒ>_5Ä5²……ÅçøN}žñS¡†£›óæÙ>KŒ£˜C G“-ͱÙô#¯iVöi¹ºIé΢ãÍ¥îìb/Æß jö~ðçį\j_eøÛáûŸü8ñm·†-ƒO ®ëÚf‹áòš³÷¾ÍpÔê×Î3NÈ©axÞ<™R̸£&«›ey¬#VxüÓÂùN/3âÙð¶URŽ7Š093&©‰k/˱xüÏ›úÔ$Ò§ õ\°ßZƒ…ŠœàíËשPUê_Ý£*±¨—¿8Æñ-§Ž~$ëQéÓhÿµ="-gá*øÞÑþ"xÇÃ>mâ=üpI¥|ñ¥¯„¤ø‹¨é:… –UñŒ|5eã?è—áÿøLäb‰5¸…°ÅCÇ8Llð [ß…76ºö“¢èÞ*ø{áÿ·‚ßìúLJ¼_ªj¾²ñ'ÃM5âÿcjÿÐí|1áMÃ:“ éðÕŒPÁeáïiºU¥¥–‹¡ÙÁoo ®“¦ÃmaoǺ$Hçólÿ=ϱ¹–ežgY¶s˜çY…lÛ8ÇæÙ–31ÆæÙ®"u*b3<ËŒ­Z¾?0¯R­Z•±¸ª•q5gR¤§RRœ›Ö©RŒ!J:p§Nœ!Â4à´P„b’ŒI(Å$­±¹¨j:~“gu¨ê·ÖznŸeosyy¨]Cggiigo-ÝÝÕÕÕËÇö¶°Oss<®‘Áo ³JËnà †Äã+ÒÂàðõñXšõiQ¡‡ÃR©^½jÕêÂT©RŒêT«ZµJt©S„\êUœ)Á9Ê)öá0x¼ÃC€Ââq¸ÌMj8|6 B®'ˆÄbkSÃáèP¡F3«Vµ|EZT(Ò§N­j”éÂ2œãç—ü(Mä[ñî¡fúÕ»ØxGº×”ê: ñõ®¡¤Í®£xWMÔa×¾ø£Â3Ǭëúl:‹×Fðþ­>Ÿâ;ÿ¥£Á™Çîjf’Àpî¼p5c‰â u,º_UÌWVÃca—þ÷7Åa§—qVQSž.ÅO’ÐŽa–æ‹„Š8.À¬5\Ë<Ï3Ê`ëbrìƒ,£•Ñp«ÅbpK>ΪV«‡ÅÑ¡Žâ\¾®&<3˜ahg ᣚåḚ̂$¾×µspȰõéG 'ŽÏ•^*ÅOAdUêÔ–3åáÉáêfVaV–·Õövʳ ™:8q…XÒGöFÛÃVêÊr¡‰Çãpu°‘£]C—%ÅÓ­VtfùhûJܹöÅþ)Ëð0áÜÛ‡<'|N ¾a_3É׈œWý“‰È°8œ^ŽŽk’p— ñ6[Ä5³²X§ˆñ7 Åå¸ >cA:¹”°™t·¾ðæ²¶£ÄöKâÖ³º[è‰JÖ+ák§Û5ÔZ[DšL.dÓ¢½‰"°H¬ï滹±KV¹¡Ä9¦ÖþÉ®òe^Œ°õ²Ü𵧇u±5U)âÔå©TðósÄJuðУK*Ê”Y¾màïñ\2ïøˆ9TÜ×ücá éºÖ³âøsÃz?†­ Ô©âÍ ÏÅ‚Þê_\j4ëVöw·:s\[ÙÝKÐe¾ñÞmK†1X>Ï_Ƹ¼ßÂ9¾3[,ȸ—(<÷ ’çÙœp™>c_&uiC4§†ÇT–­z±J•Zô¡G(®d›WI²=_ã„4¹üe‰ñK_Ô>xcÃÞ-Öí|7ðkâ¶±o¬iÞ)Žêm"ÇÀšì>_ |Fñ'“i$º¿…< ®xƒÄ¾Žki›còÜÁէņ/‡°øÆPÍóLUÃØÙâ2ìÅ·O-þÇ©™âçR-ãp¸N5dç^¢öªjõ!ÂÎŒcYËzp•JÑ´á¼ý¢„–Rzêž(øÓ ñ\~øIáY.,ü§k^ “ÇÃwˆ¾ ÞÅ3ÜøÅ>øñ'Pðv£ÈéxßKÒ,ÁâñxLÇ:Ä,$V꜎Ãgy¦a“fÜ%„–UxLÏ(Äæ8ù×ÄÓÍ3LnªÃAº•ùñ䥇öŒe k›^lDeN©N¼¯nhTŒ#dáÉsæøð§RŽæ?xj_Ç©üÿ…â;o‰Zçˆ>"iž2øS0™uo xãBñ–©­h1—ÄëssŒ5Ïézˆ<_oq-Ÿ‰5MNÉ…¸è‡ˆüc…•)å9¬8rx^9ÿˆ–Vál¿-áœ^GÆ0px<× Ìr<&2ÈᔺT¥’eù^3 –äµi¾W„ÂWN«O‡•ý¤=·6êsUç:Ñ«‡wæ…XU”¡WÚ]ûYÎ2DÚœ¤´=Bð§…¼-c¤ižðÖáÍ7ÃÚ›áMOд};H±Ðü-£C¾á­"ÓO¶··Ót *Þ( Ót{(áÓ¬aŽ8­m¢DU1˜ç¾oˆÆâól×2Í1y–eŠÎ3VcŽÅcq†oŽœªã³\mlMZµ1Y–2¤çSޝ*˜¬Då)Ö«9I·´)Ó¦£BŒ!pPŠŠ8éE$”a¤b¬—Dgë?|áÔšM{ƾÑ–Þ)'›ûSÄ:M‹Gv×#%ÍÜo…´µº¹8RLóÊ2‘;œ qg(G.Èsœs«8Ó§õL³]Js«N„b¥NŒ£wZµ*Z¿âU§ïN)ý.]­•e¹n.–¾#ÅXnhfXYn•ÆJ†AWœåG4àŒÂΟ f™¥zOS³®(k/ÅN’áøÜ?Õ2\£(ÇQÂâqYwá9¡œ}ieiÂõ³> eÿë½Yo¢Õ>$üJ¸¶¾m]L΋ᆴµÕgñÏ•gcàÿø{V¶]Kñ¥¦‰¤_® u˜`ð'µ»½N÷Æ:߉¼AX~'£ƒxyá8[…iÕÃýJ^ÓÇæÊ½l>ç­ˆÃçyžeƒªñØ¼Š¶?†xeN!â % “€Ê²êÂñŽ,-LðUØU—IUÅåÙžyF#K…ùñ8¬'g9Æ»ÌqÜ5ˆÌó$°‹-©WЏ£,ÃàpÜ9ŠË2L¦Ä ¾ýµµï Úë÷æòòú+ßÜê2º²¹¿“Æ-;i“ø®óY“I‰-¾ øÓH´¶ÓÒÛOð爵 XEmá×L)q‡{†Ãæµ²ì?°¡† š–#£ˆ¥‡ŽF©¬]<žŽÉʯ dXÚõqjµ\Ni–a³lLêæq–.XÏx»êë…Ϋå8O«á°µ0Ü?G Ãt140°áÅIc©d|¶ùÊ¿pÖcˆ­XŠØ¼ç'Á瘺•³ˆÏ?A(  *¨0à8t¯›m¶Ûm¶îÛÕ¶÷mõlùÛm¶Ûm¶Û»mêÛoVÛÝ‹HA@ιãOxiüAâ}FÝ=Œ+¨ê¶V²Ü_G W2Y[Ã,Ë5ÅävÓÃq%¬)$é±ÌшÝXúÙ~Ef²”rܧ1ÇrRŽ"¤°¸ 04Öš•Åͬ‚[Xí1˜|›«‡¸¼ž9/.®g”Ãqq€ªëåØÇ–V´âªå´¨à*Â5151v…l-:U¢áZ£öSU9éR:å©S‚Ï|ðã‹0ÊøÏ†éñÆ\ªakUÀq¶?4âÌ¿[ ’á8}O–çØÜ~]^8œ»Këøyá>«˜cêãs\m ù¦aÆb:+-7NÓaŠÛN°²°·‚-à‚ÊÖ Xa‚ÖÞ;KX"ŠDŽkXa¶‚4PÛÅ1ªÆŠ£Ì¯ŠÄâªN®+_V¤êT©R½j•ªN¥j³­V¤çRR”§VµIÕ©97)ÕœêI¹É·÷YNC‘ä8\6#ɲ¬›ƒÃ`ðXLS—`òì.—`¨e™~ ‡ÁÑ£F†–áp¹~…(F–†¡„¡P£NœnÖªP@P@P@P@P@P@P@|ûÿÉÅÁX¿ìÿþÿ묿àšt÷ýP@P@P@P@ùûü(ýŸõ¯ÙÓö–¶øaáísNø±û}ÿÁLnþ#ø{⋵-_ÂÚ¿€ਿ¶]ß…%·ð‰õë¯ x^ÓM]}fQá'D¶Õ/ô›½Xú•­ÍòþÃücâFð•^Äfy~+ƒgÑá|φ²\.7ÂUâ:ªŽq ¹ÖS—RÍszø§™SÀÁæ¸Ü}\&G.Á<6µ,;ëÂðÆeœ*ŸRÉ3<ÖÚ´èÊ8\;JµjUp´)Ò§N”*ÓU#‚¥ìéEJUñXhÊ.­z\ÿ}Éuû/G®øûPEø!s⯈wZ*|Q¸Óìü©x›Ç7¾Žã@ðàñ÷ö|:lj®<º5Æ¡ÿÂB/dÐ_MmOû,–âÕ\hø·<¿‡0²—RÊ8jŽ>\#K_>ÂåÚ5]gôð×'OëràŒþ†O _ëØ¼‹‚Âc)TÃáqJ¿]Æaèañ–„ÅPŸ¶šúž"Ž&êóCÐâƒTnŠë[¾‘öOƒ¼e¬–’G±(Ò´ ÒÓe”˜TUdd°có‹„sÖí:8 >’“úæw‘à’ŒS“›xÌÆ‚P²~ûjIÅIÉ4\x‰µJf¥)?¯ñeÉB1r•I<~m†Q§e+TmAÊ2‚“šhe¯Ä>ÿË6^ø‰*Í$K]xÄÚ.c–m#q,~ °Òf¶Ž$×-î&Šê8.Ö-d fŸEÔᶪÜ-‰Ãóªù¿ ÁÂ3sTx*Ç{ð†:~Î2ËqÈU”倫N£:”\ëà[ª©ãð•*Õ~ ÅáyÖ'=àøJœ*9ªY’f^ü)æU=”'”âñôëN¤²ÊÔi΄êáÝLNZÝhÒÌð5k°xëÄ}ŒÚ|&ø‡2]I§$ÒÏsðëNM9o®|5òßE¨ü@¶½hô«]QÔu§Yê˜9®++·ÂÙM?¬ªüÁô¥B8ÙS§Nbå–Žw:Tðµ0|#_ §¯”àðx7ŒÄa)ª¼E’â1sÂåðÎñ™4–>#ñýè±i~.”.[L’å5Ohæm:Úåü ú”wG¶Ö!›SÓ-µï¢ZÙOs§_ê~KXµ˜´ÿéšÅ¬â2¾ ñ RñžÉbãJXL¡Š«J.8ºü!Á™Ö*Âξ*<k›âpF={jØ<½ ..­ 5_O§,FÏÂ^-ךYobòô«:ŒY}˜y²¶¹«hÑ$WmrÉŒ…§ò.\ªE Î'§‰sUó¬—.P… óc1ªŠn¿µ|Y~9NŠ¥|Båµ?iJ7”æ¢oÄž#ã2:xiežø™ÆU18œÛì8o'Ȱµ(,«ê+ë8šœ_ļ+†£†ÌªcÕ<¦n»ž3ê˜úÎ,6UÛum_Åñª¯‡üo9¸žmÄÖº šFYÉÑŸK°ñUÙ‚y®.mÊ<ÜÄÖHÖíÖ­;Á`²Y9<Ë;«‡¦©ÓšYvU[1¯)J¥xÔ¥ìñxœ¢Š©NéU»Äû)¬L"ª©Â²§KÄþ'Q§Nán 9ÅÏŒÂÎ\iÇùoåT¨QÁåu𙌱œ;“x™KÅcqøN$³ 4òlUz˜áñYtñk«Zøêõ!]Zð΀|ùZéîô KÄÒ½©·´òa´e×ü5 µÌwnón®-¯¡šÛ쪖0JeuX:Ü=BU;›f_»Š£9–*„k*µùçY<»5ZR£õ~J4êáç ÞÙËR o‰2ïsZXZ|-Å~pSúæ"ylÏ‚óï15rù`²ßªá²ÊãNÂà1ôs/퇈ÌqØ ã ŠÀgBžQƒÄËV¥á‹Ý];¿x–Ò5žY^={ Ί[[8>Ë-Äj 7÷Io}ot²_O·Á´p˜\Ú† R—*­)SŒ³ xœw$áZ½OmU11Ã9ÎZt*F­ ”\pôç TêJ¬¦óïsn&£F†eâoe”iâñŠ”xC’ð¯ÖðøŒ·,Âgâ1Ø<š¾wO …Æà±™® ¶7Áfñ¾/ ‰Æâðt040É©ø ÂZä"ÛÄ<>"¶èÛxŠk­vѧk;;-gªOuiåIoc’Ô@-^f¹¹0}¦öòYÞˆ³œ¾£«–ã§–UtÕ/k–S£—ÖPUëâ#jø:tksÆ®"§-oií”*J§²¡Bçˆ<ðÓŒ0°Àq¯ axël³—ñÆ/1ã ¶xº™VW“VsÊø‹˜å¯ _“àÕ|½a]W,~a,#ÇæÙ®'ÒÛXÙY ¶vv¶‹…m­â€a!··Qˆ‘FÞÒÖ¡¶·ˆ|Æ«åUÄb+»×¯Z³»w«VuåR­Y;ÎRwukV¨ßZ•jÍûÕ$ßß`2|§*&W•åÙl9aL ƒ-<. N<¸zT×-<[—`à­háp,Õ¨C¾í¿³×7ÿèÕ?Ø9Ô§˜IåÔðôç(8Õβj8¹N8‡…”ieõscë8Õ‹rö8iòÑ_YvÃþôñ¸:\Q…á 0ã<^q‹ÃÒÅS«—øcâfcQÃâ2jyõ ¸î.ÀðŽ#„òØ×ËëSíï êæRY,³«åé‹á}FßN‹@ñlq ¼íz|9¨[éÖÉukÜkqyx–ñ¬Ê²y6ñù³Ùݤ–×qÃ*€[É*G W<Ë%Š¥R­5Af˜j¸ª²£ZTféP¡*²pn>Ò•YòS¯FQ«FU ÛJ>'á*縇Á~&Wž7 ÆO5—gX,‹K1˨æT)ã³\Ζ„1tá[ê˜ü ¬c2¼Æ|eC ˆ‚Œ’?êRêzu‚xÅæÚö iÂpiš?Úmc¸0ê1Üøª^YíÝÚÒê=#JÕ|»¨¤¼]Ä„²œ,0˜œL¸ƒ%öÔ*U§KœÔÅã}•YRçÃJ–O<)ÕŒUjRÆã0|Ô§¨Ôæ§£âˆâ%£àÿ‰Ï/Ͱy~30âÊÓðÛÃÜ/õü¾Ž9ásÊï°ÜO‰Æ`jÕžY˜Pážâ/a˜aë¨Ê® ÐÆÖpÖ¼H÷úmºø2î;+˜,¦ÔuoDEÓ$¹Ek›Qkouw=ìúsŽå¡ m3*›+›¤rèž*X|UYg´e^•LD0¸jX ÂOR’¥YÖ«F<=j°M¬E*2\¯HñgÕÎr ? s:9N?”b³Ìóż#J?_0¥N¦a—,»˜æxÜÛ‘T•ZéáU,¿Rœ%”ã³ 5]Zp6£ñßé‘CáO 2Q`u«Û¯jpÞØùΣQ]'N¶ð%ô:ÃÙGæ5¿Ûu=/¤FÏf²I,,7 ¬>.sÎs­Áâ…ÂT¡ˆä‹ú´±˜ª¼CB¦ 8‰òª¾Ã ˜Ëy(×qŒ*qTÏ+ž¦s<_5u‚壂¥‡QP_V–)ºõª9N£~Þ’TáìêU”ýÎêÕ<`žq‘û †Ø\ƒÙeRâW[2âŒ~pëO?íÊa•eØ(ÒÃà£MåX¬|¥Kx:ËLˆBu[[¿ëZ¥ýñrµÂiú„>8Ò-ô°Ö>LPÉs¦ë.Ú[‰"’[Wª5¸n8|Lqnw_?iõ:Ôs¼‡ýÌ)bpóáüm\e±ó©X¬5 QœfÝeÍ™ež5â3œ’¶QÆÞåÉ𜧚ã32Í!•ð¶^ž ‹ŽIF•N–+*ÁàçšÃ1Íá[5¯ïtMFâÎæúïÅ3Ëb÷2@‘øãÆVVŒ×7×û^§ëÖšmù‚k—ŠÍ¯­.^ÎÊ;{wŽÒÞRèq? N½*rxCQ©)pþEˆ¬•,=<7îqXœº¶+í!INºÃÖ¥õåWUJµZ“—>kà÷ g¸Ü¯0Íó/±xŒž¶>¶•¼UÊrÊ“ÇçÜí¼Ë"É8Ë,Ès—‚Åcªa²º™ÆYŽ«•åT0Y>¥ ·„ÂÑ~§à/kom&·á}Zk;‹»»_í>ßUX./¯æÔîe5¸EfÔ'’î?—ólkq•IÂñ}€Xà3|ëңF·Ôq5pn¥,>JP”°Ò¥&–œhÉÞõa̪¹óÏ›n ðcÂ~-«€¯Å¾ðTÊñ¹–e—ÿ­Y& ‰)á1Ù¾s‰â v"…ê–:Œ'<ë[2 ¹9pX¯e<pÿWêZí hO"Êú&Ò¤×7)#i¶m"Ü^N÷7— æË5ÕÌ’\\Ê’yäyef‘™̳ÅÂ8üj„©Ò¥(¬UuR¡J7RÎQ:Pk–œ"¡¢’>ž§ðuZÐÄUá>©ˆ¥‹ÇcéW©ås­Ošbêãóÿ € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € (àØßþN+þ Åÿgÿðçÿ]eÿÓ ¿è € ( € ( € ( €35­gJðæ«x‡^Ô-tAÓ/õgU¾•`²Ót­.Ö[íGP¼ÈXmlí šââV;cŠ7sÀ5ǘæ<§/Çf™…e†ÀeØLF;ˆ”g5G …¥:õê¸SŒêO’”%.JpIµËÊM'ÓƒÂbs ^ƒ£3xŸàæ›?Â?Úái¤Ô_öSøÑñ‡Ãü?ðÓö”:‡õÅmà+|NñwÄ¿êz÷‚4­KÆÞÑ~<ü:ø=­xÛÂöš—„´ýhÚ\CµO-Ç}k•cpóË8/Ê«g¸ÎÇ8¼æ†Q„¯€Ããñr– ã2©WÊêæ¹rÎ2Šy¤óÜž8™TÌò¼$0y„°žn#†§CûC Z¦H³ª|=Sˆ2Öêå³jõ1X|)Ô®°ø¹`ó n —åÙÍ,%LŽ …̪Ï2ʾ½ú+\'IðüOþM×â7ýŸÿü‹ÿ^›ûdPßôP@P@PŸê_¼ ¤üSðoÁMC]û?Ä߈þ%üRð†¿³5‰µü ðÄ ü'ñ]þÙƒO—ÃöðŽøƒãÂÛìÍOU²Ö5øJ>Õ¡iú–‰â+$Ð( € ( € ( ?ø³ñKÀŸ¾|Kø×ñK]ÿ„_á—Áÿ‡þ2ø¥ñÄßٚƷÿï>øwRñg‹µßìoéú¿ˆ5ìŸé:†¡ý™¡iZž±ö²éš}íì°[JèP@P@P@§|Rð&¯ñOÆ_4ýwí~ü?øiñKÅÞþÌÖ"þÉð'ÆüYðŸÃ­wûf}>?ßÿÂEâ߬?³4ÍV÷XÒáûV»§é–Z߇nup@ € ( € ( €<ÿâ_Å/|ðîâψºïü#¾Õþ |'ø[§êٚƯöüqø§àß‚Ÿ t/²èZ~§{ü%~ xGÃ_ÚsÛG£èŸÚÿÛ>"Ô4ŸØjz­ P@P@P@|ûÿÉÅÁX¿ìÿþÿ묿àšt÷ýP@P@P@PÆ¿ðQ_øçâwì ûi|;øf.Ÿâ¿e¿Ž¾ðu½Šo½¾ñ³ð×Äv:n›f¥—ý+T¸™4ëv %ºGS¸ øþ=å‡ fš´¥ˆÂeØœŸ8̰ñææÄdù6u—fÙÖ('9{|§Œ¥Ëʤ¹ùaM¨¿¯àÔèñŸN¥Xaù³L= 8ºr`q˜–ðøÂ|ÍEÇ/ÆÕ¡’“Q’ ã&¢Û?(>)ø£Áß´/À?ø7óAø'­èþ³âÚ[öPøÃá› 'YKÛÝ3á—ÀoÙóÇš×Æ=ZIm[YxCO¶“Á!žê·Å:¶á=tXê:¯Ù«ö<ú2—Ò9ÌãV°YwøåÄxìu)Ó•Ü?Æ~áœu9Ò”¡[Ä9ßpìò¹BRÃãiJXÚ©O)Çò.¥S à]\§NXÊpðŸ‚éeø•R–.—ð׈|šç™_±©[ëù[Â|I‹ÌT!*¸<6½YZ”Ôåýׯ\~`|;ý•ÿo¯Ö¿<#ðSö¿ý4ÿ†^(ý ?iÑþ)~À> xïÿðÓ´wÅOÚCYð¾»ã ÿÁH>øÅ?ð‹xƒâ¶«áÝ3[°øuá·èúfŸ=Ö‘ë\É( ¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlTãú×ìÿ$×~>ü4ý£/?mØ‚?|+ø?ñÃে´»oø&÷Ç”ð­ç…~?xÓözñ׌u fÊ_ø*lڽLj4Ý_ökð-¿†®¬µÍ;N³ÓµoCªiZÍÍöy¡{ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@ü+Ÿø*oýGìÿŠÓý¢¿úlT¹ÿ‚¦ÿÑä~Àø­?Ú+ÿ¦Å@?ûBþÉßðROÚSàÇÙÓÇ_¶ÇìA¥x'ã÷Áÿ‰«¡j÷Zæœìð®à©¿ôy°þ+OöŠÿé±Pÿ çþ ›ÿG‘ûâ´ÿh¯þ›ð®à©¿ôy°þ+OöŠÿé±Pÿ çþ ›ÿG‘ûâ´ÿh¯þ›ð®à©¿ôy°þ+OöŠÿé±Pÿ çþ ›ÿG‘ûâ´ÿh¯þ›ð®à©¿ôy°þ+OöŠÿé±Pÿ çþ ›ÿG‘ûâ´ÿh¯þ›ð®à©¿ôy°þ+OöŠÿé±Pÿ çþ ›ÿG‘ûâ´ÿh¯þ›ð®à©¿ôy°þ+OöŠÿé±P ~É?³×Å?×_´§‹¾5ü[øñƒâoí1û@iÿüøkñ?âS´Ÿ~#øáG€üãß»Ý×øÃúâ;^³]³kºùk¢n 2’ôa[Àà?²°OêyZ­õ…–áÙð ¿¿ûÿ©ÒäÃûoÞTýç³çýäýï~W1_íØ¸fßöÌ}8Êñ¸¯öŒ\!(S¥(CWž´c*t©S”c4œ)ÓƒN0Š^Ñssoemqyw‡¯‹Åâ*;S¡†ÃS•jõª=m T¡9ÍÙÚ1lÖ¸šÔ°ô)εzõaFqs©V­Y¨S§NNSœç%Å&å&’M³ó—á7üëàßÅá+Cð³ãç€þ~Ñ~+»ðGìÍûM|DðÏ€4‚?´Ša°Öµ]/KðJèÿ|AñwÂCÆZ_‡5ÍCáÖ¥ñ§á?½#â=½‚¨kòêš,z—¥„Áb±©à1T'”瘌†§axs4µ,ç’ÐÂáó^":ƒ§ŽÀå8¼>qÈqXÜ?`²¿­ã19M:yVqýŸË­Kñõa5˜eÙFwÍó|½ûl»,Îjf« §†­*¾ÃˆÂOˆ¤¸yg™vÃÿÛ50¸7š©fY\±¿¤5ÊjP@P@|ÇûJþÕ> ý™íþ麧„>"|Uø™ñ«Æ²ü<ø1ðWá—á½SâOÄßÙè·‹5›]Ox³À>ðö‘á¿ èz¿ˆ|Mâïˆ><ðoƒ´6Ï:–¿ouwamwŒ*ÕÄf4²œŽÇO-ÌsºôèË Jž#Êk`0ù–qÅcq8L- &šåx*tý´ñØü~c‚Àe˜"Ñá*Ò4ÿÜüXøÍð—Sñ‹®|·Æ/ðÿàΓñCǶ¾¸Óo.ü3Úæƒmªy̰˳ ÷RYo åyž/(Æñ..œêe”ñÙfŠÎ>8Üë…É(æeœf,§—à+ºøJØ¥ŒÂbèQôÿ³±3ÅåÙVGŸfù} ×,È©IËËñxìË+Àb~µ‰X|› ,Ë3Êqø· ˜f¸Lv:¦uðØZ˜4ñGן~,ü?øñð·áïÆŸ…>#µñwÃ_Šžðÿ< âk8î`ƒ[ðljôÛ}[H¿·°ÛßYË5Ìh±¾¶¶¾±¸Ú^ÛÁu ±'¿šåxÜ—1Ååy…5K‚­*5£ ­NZ)B­ ô¥:XŒ=zr…|>"”çGB¥:Ô§*sŒŸ‡–æ8l×G„u=WV©Îzðõªa±X\M‰NŽ+ Š£[ ‰£5ÍJ½*”å¬YèµçÁ@P@#0PYˆUPY˜œÉ$ž€I5j´¨R«^½HR£FœêÖ«RJ4éÒ§:•'&ÒŒ!å)7d“m'&’M¶ÒIjÛnÉ.í³ógÀŸðT/‚¾=ñ®-þüyðßÀ_‹_.~üý®üUῇúìññgâ”z¾©áÍ/Ã^k/‰ÚŸÆ½Nño‰´=_Ã^ñçÄOƒømãÍzÚËNðŒµ‰¼EáoíΜ²…\Ëû.”¡,»1ϲ'ÄÜ=”f)ÒÌs¼Ž94ø•âð±¡õœ6øfœøŽžGœb²Î!­“S¯Œ¡”ÔXLdpóšÊ9L³—RQÅá¸g3y7cprŒ°œ9šÃ8¡Ãµð¸éâ¦6–ˆ1X|—1̲*Y¾Q–f3 Ç0Âý[*¤õ—¨ÏfÏÛàWícñKö£øUðfóÄ:íÿì“ñAø_ñ#Åw:vŸ‚5Ïk:=Þ§{gà^ßX¼¾ñ§…ïtíOÃ^#Ônô"Ò/i·ÖZLº½­»ßQ–ß4ᬿŠðÉÇ*ÍsŒë(ËÝ_sŠþÅÃd¸ÇšS¥xÿdæ˜Lÿ‹Éq2©¹†^Ž*ø,F-cä²ìÿÃxs<G“g¸ÈÓ÷¨áiçxÌóC/­Uòÿ¦®C‹Y¦œjC/«Rž½u™Ð̰8°¨P@P@|ƒûA~ØžøñÀ|9ð“ã7íñãâO†|WãÏüøcðѼYoðçÀ×z6›â¯ˆ^%ñ'Ɖÿ¾øOÂún±â-C³üDÓukšœ:O„ôzúÈ-°£^XœVe‡Ãáñ©d˜,¿1ϱЄ^&ÂçŒ~'XˆóýsŒÍkeY¯Ôr¬— šæÕhå˜ü[ÀÇ…«ˆŽõiC„ÁbñXœ>f™•l§(ÃÕ•G‰Íqø\-+éžûEͯ‹¯tCHÖìôÞ,²µ,Ëû"´êÓË2î"ÎÿÕÞÎ39Æ–]œç;©Ã¡BXwŠÅà°uxš”ø~Žyœá2Ì‚¾g °¡šÔ¡‡Äâ(öchW¡¡J¦iše¹úÇ™d¹d^#3Àå2Ë*g´ªT…Oa…¯¯R–{G#ÁbñYíLª®²ÏøUÊcŽý²½³Ô¬í5:êÞûOÔ-`½±½´š;‹KË;¨’{k«kˆ™âžÞâhfš9cutfVô×£[ Z¶J¥ E µ(×£V§VjSp«J­9¥(T§8Ê„’”dš’M3“ ‰¡ŒÃÐÅákSÄaqTibpÕéIN•zá´kSœn§N­9Fp’mJ2MhËU‘°P@P@Ï_ŒÍ1õ,„ñçu+Tå‚Ò¨ÒŒêׯVN4¨P£ Ö¯Zp¥J©8ÅöeØ ^kÁe˜ /ŽÌqX|‚” ëbqUcF>z²…8sÔœbçRp§ù§(Å6¾#øÿøwñŸÆß <¬üý¡~EûCxo[ñ‡ìÅâߎþøyáÿ ~Ñžðþ‘‰õ+Aொ?ñgƒµïøC.!ñÅ—€~:x[áÄOÂ+¨ëwƒî£ðÿ‰G÷ÿ³1”ñ9¶Y‹§ä8/í,놱!S5˲ØãðyN'*Ø9bòle<»7Ìp^oC-ͱØÜŸŽÁÃ4Âá)âðÕjøóÌ0o Í0•ã˜pþg˜PÊrþ#©G*Åf˜¼.;ÁÂÅ„Ía Ë –f5òœÎ¶YK&Íá…k,̱sÄ`ã‰öÚSö°ð?ìÓ7ÂÏj^øñWâ¿Ç_ê~ø)ðGá-Ÿ„¯>$|Lñ…áWÆ^$]"oˆ1øyðûÃú'†ÅÓ¡<%(eù[ŠË°8ì߈Æâp˜zXj8Üß-ÁÑ¡ µs ÃŒ¥…Ë0XÜG58úS§N†]ˆÍq˜š, v[•F½u^rÄæùÌñʲ¬- - N"¶/õL]U%Iap¸\&/˜bp˜,=lD'ý˜¿j¿~Ôº'Ä ü1áÿ|>ñ×ÁïˆZÂþ(éÚ™ñ'á?Äm7JÒ<@þñBxCÄÞ6ðNª—Þñ‡â ÄÞñ¯Œ|âT¶¼ÐüI¨*Ü;½„*e¹^sƒÄÐÇå9Äs‚Ça£Š§ŠÊ3,VOœåøŠÜ6„ÌrœÓ‰Áãp˜¬5*‘”iâ(ûl+ ‰¯Ç*³¥™cò|^¾ 2Ëè帺ØZî„Ý\»:ÁÇ0Éó<5l-|N€Ì°’•L=ZuœáR–# Ч‡Æa±zM×9°P@P@ð‰ÿà¦ß±Þûf|#ýtŠš/ÄOÚwâ¾£ãí>ïÀ? 5Ÿ ø¾ãá|;ð6§ãí^OŽÓÄ‘_|=ŸRÒô¶²Ð4K>ÿÄú•ýÝ¥ÃèVÚºÖ­k#‹â*¹Ür¯öŒ/äx¼ó1Ìãïe‘X>!Êøj®UCOÚGœ¬Ç3ŸµÂRR§‚§–f0Ìq.xWÁ—3Ãâ¯xÄO‡ä„HÈžjê·v¦=î«¿°ׯx‰‡¯Šàn+£‡¥:õIŽ©,54åSB'[¦£y9ãpÔëa#Êœ¹«.Tåd}Oâ°Ø3áðÃŒ°Y6f«A¸O/Îñ¼UÃY^_R¬¡GŽÌ°xj’y¸GòŒ—‰Ë<ñ‡³*/ ÓË0^WÃ×téÕ‡ÏÅÁÏ 8¹òÓÅѯ–æ|AZ†Ö©…ÊrÜ~e‚†#ö¯úw¯‡>¸( € ( € ü„ý°|ß ÁTà”?üU©A¦ü5ÔtOÛCàv›}¨Þ>Ÿ¥Yülø—ðïÀž#ø¦½Çü{\k^1ðçÿèžÓïžï/mÞÚÊIuiìlî«„­-ñ 8óbóßpØqºu+Ç…|Háüߊp¸zmÞuVYË3zê«IŒÄ¨Ë…ÅÔ¢ø§Áü9‰Šq¡’øµ“cs¹¤åxL瀸ÿ†òLv"QMÑÃa3ÜÆ–SíejRÆq>YÁºm¿ö ÓŸÄ?ðPø,ÆO\Xê? |SñŸöeø[¢ëzeÜ7:v±ñà¯ìááý;â²Ú½™–Êê_j>+м)«_$ïu¿¢jú ôp\è2D«†“¥á®]Î×&uâwŠÜS”(ÉJ5¸ˆáŽ£¥ÊÜVŸp—Ë(7 L)}u7Le%Ä+›°T”ã*Ù/…|‘ç0M¹ászÜYâ7áòüOòã0¼;Äy.®«l63ÂG‘B¢9ߎ^<øÁñ»ãÇíûÁ;<ð¿à߈gŸÂ¯ûyþÞzÿ†´CkðûÄÞ:ð“o£xOÀ~Ðí¬µoÿµ*ü!Ã7VÚÏ57Àÿ <+/‚—ÄZ†¿-Îá5òèe0⬃̫brŸ!›qI:f|YšNtëñž†éJÀdy}Là ñv*•|V#5žk†Êòìvi–âq˜oN¾e>Í2¬u,=<ߎ+dÙFm–ÒÌÞ[äXLËC†±¼K‰æXÜnup¹Ý~áL ©F½8}wŽË2Ìd~¿úSû4~Ïþý”ÿg³wéŸ|ø}ᯇ~Ÿ\ºŽ÷\¿Ó¼9§Ceý«­ÞA¬Zί:MªjÓÛÚZ[K¨^\=½­´&8SêsÜÞ¶{šâóJôháž!Ñ…&Ia°X<&–ê’GC‚Ãáð”]YÔ«*tc*µ'QÊoæò¬¹ex(á^"¶2´ñ8ü~3ˆåU±¹ŽkÄæ™ž2¤`£NÅæÌN!Ó§N—µöpJG¸×’z!@P@p_t]wÄ¿ þ$xwÂ÷gOñ6¿à/h¾¿3e®êžÔlt›±æ2¦mïç·˜oe\§ÌÀd×ÊqÞ˜pOà0\n7†3Ü.•?âVÄWË1T©Q‡^zÓ’§µRšg½Â¸Ì&_Äü9ÇÛê8û(Æc‹šX\6a‡­ˆn IÍ*P›qI¹l“¹üÁ¬úWÄŸø!ü÷à‚o´vøÑ¬ümý€¾é :Ômâ=ã?ì÷û@øûãž“wì5½;XøagðÏâ»ãd¶³–ïÃZ^‘¨\´gNX®%ý{8Çàóü%âLª¬q|Çâ^EJT¿Ô „\C˜×ÅÂ\¼ó¬&' ê¦!RXŒç„Ê1®†;* ápøL^Sá׎ù.mÏ,߇ñ‚±´åz•+qox™ «!¡-âªf™Ži—g8J®^Íå•#aç<8bîßüWý¦m?g»ï þÊ¿ >#|EñÿÄOè~ñ.«ð—Äü?ñágÂý{íð°¾&x~>|Xø1à-[ÇNƒm>‹à{;¿[¶›âýEñ5ÞŸªi:¥§ÜþsÃSÌq¹VW˜áquøkˆ¯SŠ%‚ÄSÃâ19V:ë ¥Ub°ØÌ*â|R¡“c³<6')É«æ¸ü£šQËaWë°5Ö†g˜áê`¶°8/iø\Ê…zøÙÕlN ‡Åã)SÃbhb0Ù*Õóée¸¿c†Îêå´r:õðÔ3*ØÌ7ægüWP¸ð§íÉÿ$øEáÿØïãgìÕðûÃ:ìo¥èúGÄþÌú½§Ã›oþÎðæàïIð{öŽø¿}«x«ÆÚd“xÇIÖ|0¾3ÐçÓíµ9¼{âÏxÎî Pú\«<Ë‚3Lv'ƒueãˆØšpÃP¯‡ÂÉâòOðòɲÜ40”èà£Ãx\\4¡„ÊhàqÙ^ Äæ_VÇÐË~w1¡õ"á¼3ákÊ<].(ð¿‹êÎ¥HG—„— ñw,lÜä•,UÄ8lm,^"þÎ…|ûK¥õ¼­§Fu'á>&•: x©Ã3P„çõ—6Åxqeôå()[žd¹F.–„íí£Âø×Cž¢œKðG}&áþ~Ø,^ÖãáçÇoø)í£ñká¥aqúv½ðæÿâ_ü"~'Ò¾ëÑüMâ/x“[ѯtù®,õ­.öÏ^†yTÍk–Ñ­‚ðãÂ,³ QÇàü>Ž.¶¢j¦ ÄÜYÅ|aQ«¯Jo†¸‹%®ðí³F¼pöN“ŠÇ4œ1}â&*…jxŒ?ö§ eR¯Fj¥*¹¯ øeÀÜ1Ä\•cxVžˆ2ŒË,ÄÕŒ¥7ŒËñ4êZtš?þø+Å_?a¯ø*Ÿí âÚ·ö…øuû\þÌ?¶'íÏâ†~ð÷Çÿøoá÷€ük­üN×>#üð†¿û6höð“ãE·í{âÍPÓ›ã'Ã߈Z߉´ŸYYü>Ôt!a£I§ùù>'—xôz­”a£g™Ž]Àœše¸ûc0ùÖqƒñ¿ ñÇ ‹rÂdØŒ.ž-b±ùE<³=¡S<ûšÏ ,4©û¸¬6 9ñ;ÅLbÖSÃ51_ÛØlÏ 8åU²~Åx{’c²~8–eN4ñXˆeXl=<(c«âr(O…êpü²Ï¯Ã<Ž7úÕð&£â-cÀþ Õ¼_¥¦‡âÍSžÔ|Q¢FÁãÑüE{¤YÜëzZ:É2ºiú”·6ŠË,¡–DŽãôÕ 9Ͱ¹^!âòÌ6g¡—b¥¬±88ª´ð˜‰>X]ÖÃÆGîÇY|+cã8wŽÆðþGÍ):ž/'Ë1YMÒtqØŒ ¸ºN“Ö›§ˆH{7¬-ÊõGW^aìP@ùÑÿvð'Ž>&Á0¿nïü7·½½ñž¿û1üV‡GÓ4ØÌÚ†´¶ž¼Ôu=Æ%"I.õí"ÎÿG·H·Nòß"À)Doˆ±XlºŽI›ãÿäSñ·‡ÜEž]ÔQŽAÃüsÃÙÎyZ¤¨ÆucK •`±xªÎ”'QR£>XIÙ?ªà¸Î·RÁШ©csL·ˆ2\®«i*yÎyÃù¦Q“MÊMFŸ.kÁ¿k9FŸïg8FKãoÚÅý¢¾&ÁÿáFx‡Ã~)’ïãD_´Æ™.ƒ®Ûê-iû?x3öLø•¤x¯Åbê'}Tø}õoø;ÁMu5¬v×Þ$×tÍT{[¹Ú4ýµ FÆÞ1Äâ¤çO!ðçÅøç5ãòMñ†yÂ9 /ܹЗöÎe]f™l=§²ÅeùVc™`e^lšü·(©àfCäœq9Ÿø1•`pÓ„ÕJy¯ ×ÇñáꪑŒ°øœ'áìî†eªŒ&&PË«ÂñŠ”ºOø)Æ›«|Gý·à”ÿ4ÏkŸ³½þ¿ñ/ö‚ø‡áÚ÷ÂÞŸâ<[à_„ËaÀï†ãâN›âŸ„÷~ øñáïx‚-cEø‘ðïâv™«xkÁšƒhþ½Õ¬W\ð÷ÊpÕ*˜®7âJ”ñUð²O ³le,68iÖâüsÅ|=•æ¹EjxÜ.:L«…åK*âÜÆxL,s\..ŽOˆÂæ™Fcjbþ»=©OÀô#WCG8ñ+ƒ²ÊõqN´pÜ1[/Ëx“?Ë3÷<.#ˆ†gšâ²êœ”9ã[5ŸfxlÙUÄeøûÿðJÍSømñßþ }ðJãÄ7ŸmüûSøOÅúÇímâÐ×âÆïˆ¾xW_ño‚¾(Âi¤ü2_|°²ðÇ€ŸÃO ü6ð¾•á{ éðÇÃ:½¶¥¨xƒ·'¯ _‡dÙWoNtxë:˜šØêù·pq–/êë‘T¦ø‡‡rþåÂQÂaéeñɸ/✫ ,GÅK‹«™C†Ìñ³µÌnP@P@~%~ÞŸò—?ø!×ýŒ·ïþ³=_ÿÉkâ7ý˜ªŸúôø 8£þMæ þÏG†¿ú ñýµ¨ € ( € ( € ( € ( ðìÍû7ü?øŸâï¾ýŸ~ø'ã?Ä‘iɳþÝŠ£ŽÆÿ¶cpÔÝ>3þÑŠ¡EÒ§AÒ£ˆ«ÏV•7B*.œcì©S§nHE/n € ( € (  âg¯…ÿ¼«ü8øÉðßÀ_¾x!^ðÄÏø{Çž Öã·™n M_Âþ)Óµ]RH'Dš¼±˜G2,ˆ¨a…|.ì~³‡¡ˆú½zxœ?·£N·°ÄÒ¿²ÄQö‘—²¯NïÙÕ‡-H]òÉ]›QÄb0þÕáëÖ ëQ©‡­ìjΗµÃÕV«B¯$£í(ÕI*”§xM+J,å_öoýž$øEaû?IðàÃüÒâÒ`Ó> ?Âï·Â-: Xµñ… ‡ÃfÐσlâÑuû-sIŽßE4íbÒ×S³ÞÛÅ:wVÅbqükˆÄW¯šex¼.?,Ì«V©W—c°0tð8Ü2r–# ‹ÁÓn\N¥:Øx73‚Ðå£Fކ; B•:lΆ; ™aèÂ4èflÒ”èfx|u(%OC1£R¥uDjSÅÒ©:xˆÔ„¤ŸŽüFÿ‚tÁ>>0x×^ø“ñoöý¾)|EñUÌ7ž'ñ÷ÄoÙ‡à—ükâ;»{K}>Þë^ñO‰¼©ëšÅÌv–0Ϩ_ÜK¥­½²2à h¼T0Ø|,%O BŽœëâqS… P£ âq¸Š¸¼f"Q§ÆUñxºõ±Xš­:•ñªÖ«)Ô©9>е«WpuªÔ¬éPÃá©:µ%QÓÃa(SÃapðsm† J–J6§F…:t©Æ4ᯧ< à|-ð‡¾|1ðW„¾øÂ:l:7„ü àOhþð†4{bÆßJð÷†|?g§èº.›v0Øé¶VÖ±m‘.N{q8¬N2¯·Åâ+â«û:4½¶&µJõ}–<6—´«)OÙáðô©P£ òÒ£J((Š塇Ãá`éa¨QÃÓ•\F"TèR…(J¾.½LV*³…8Æ.®'Z¶'Q®zÕêÔ­RR©9Iõµ°P@P@x†…û3~Íþø»âÚ Ã_³ïÁüzñe¬¶>)øÝ¡|(ð“ñwĶSÅoö~ ø“a ÛøÏYµšKHe·Ôu«˜¤ŠÖÞ7BÆÁ·—áqXúŽ UWÆá0ìØ\]eVuÕlV%,EUZ¤ë*•a9ª³Kóɶb¿Û«`ñïöÊùu'G/¯Šÿh­¢á*n–¥nyái8NptèJpœ£kI§Üø?áŸÃ‡—ž2Ô<ðÿÁ>¿øâËÏüB¾ð…4/ ÞxïÇZ–Ÿ¨xÓÆW:-…”Þ(ñeý†™¦ØÞx[{íbêÏO²¶žñá´#)·G …Ëé?e€À¼SÁ`©û˜L#Çbëfç…ÃFÔpïÄâ1¸§JxŒ]zØšÜõªÎr*þûS[÷ØÚØ|¶2¯ï1Up™m«eØZ˜‰Þ¬ðù~ýŸBSt°´uB4áî–´oøÞ%ñ‡Œü=àÏ è>0ø‡>‰sãÿèÞÑô¿øæçÃZZè~¸ñ†»cg©âiô GÑ%Ö®¯dÒ´´]>Å ´Q)~ãðts„xÜ^dð´¿w‡yŽ>ZXüÁÐ…©µƒD£Rx\E½®SŠJr¥89$”›IC^øzuëB†)SXš©8ÑÄ*Sö”•zjJ•9ûôÕHË’~ôlõ55/ƒ_5ŸøV¿Úÿ ¾ê¿ð¦uKmoàÿö—¼1}ÿ £Z²Ñ®|9g«ü5ûV—/ü º¥§‡¯o4+kÿ ÿeÝÁ£]ÜéqJ–SËõ:õÞc<ÝÖªój¸\ÇS4u&ó˜,ÝRY¶x×/¬Ï ™ªc‡•WK¨ÒX˜Uöp·pô!€YT(QŽV±8jËcNÀ,fYZxŒ·°i}_ë9}z•+àkû?k„­RuhNœå)>[Äß³ì×ã_Šþøñã/Ùëàw‹~8ø.müñ›Äß ¼¯|Wð½·ž-àð×ÄMW@»ñ~… êçÉ‹KÖ-R/´O±WΓvX?øN­ŒÄåÿì8ŒÆŸ²Ì1?öZØúJ1‚§Œ«C’xª|„9+Êqå„ckE%¶+ý» GÿlÁá§:˜|&+ý£ ‡œç“¹éRœêB3œ¡¹N1“nI3Üh € ( € :õ¥(ÆQ”d”£$ã(É'E«5$îši´ÓÑ­Àñ†?³/ìÝðKľ6ñ§ÁŸÙ÷à‡Â?üJ»mCâ7‹>ü(ð€|Kãû÷»žýï|m®øWAÒuO]½õÕÍë\ë·Wó5ÝÌ÷%ÌÓHìé~ã-ŽMG÷9<&êC*¥û¼¶/`ç -…ŒÝܹ*JN—îÛäÐ1íx÷šbÿÚ³9QXw˜â?}xtàÕ‹©ÍˆtS§MªN§"tàù}ØÚž£û*þËúÇÂk€š¿ìßðTøwªj:åßÁ}GàÿÃÛï„×ZÖ±­^ø“VÖ.>Üøv_ͪjž"Ôµ{QÔ$ÑÚî÷Z¿½Õ.f–úêyäʽ 8šyu,MXŠY>ƒÊ)×§ ´ò¼&_F8l.…E(àpøüðfðçàïßü'ø{áèå‹@ðÃOxÀž ÐâžWžhô ø_OÒ´M29§‘æ•,¬aY%w‘Áv$öWÅbqR§,V"¾&T¨ÓÃÒ•zµ+JžŒyhЦêJNiGݧJ6„#¤b‘ÇG ‡Ã*‹BZµLEeF”)*¸ŠÏšµzŠ=j²÷ªU•ç7¬¤ÙÞVÁ@P@P¯ü3øqâ¯xâŠ>ø#Äž=øc'ˆ&økã}š±âx³L]Å2øÄš…Ƴá9 “@½ÓÛYÓl5sjTQo _‰Ã¿a‰Æ`W‹ÄQýÕ|VXñT1Ï.ÄÕ‡-JøŽÃa± VRÃýkCìýµ*s‰W÷øxàëþû n2ޝï0ñÌpTñ4°xøÑé¬n–3O ŠQöô)â±0¥R1¯UK· € ( € ( € ( € ( € ( € ( € ( € ( € ( € ø?ÅðROÙoÂþ'|Uý¥üwãïƒ:þ•á_Š?¿a?Û£öƒÐ|â]oÂÚ´ÍVñÏÀ¯ÙËâ/µ®|+â] [û¯‰..a³ÔíþÑR—1ÂWŽ;Ç D°51Ù®[KS ‰¡B¾;#ÇÕË3jz˜ŠT£ˆxu˜zÓ¢êSæQ”'*u)Î[âðõ08šx'|*ø¡ðOÆð û­6q­ü3øÍàÿ|HðÓ=Åœ²Y§ˆü)¥>£`öº®ž·Z]õ•åÇv# S 8B¬¨JU(añ1x|VOFŠq©S Z¼)b# ‘Ž# VPÅa+ª˜l]8šUiCŠŽ"kÉñö5êaçíðØœ75Jvæ•/¬R¥íè»þïCÚaêëìªÎÎÝís›|ésûW|‡ÆŸ¾Yø‡Å>#ø…û0ø[À¾2øÑàü)ø·ãïx{Aø—¥kZߟÃÞðWµý[âV«â-7ÃÚ¼öžøaiã/Ã% ´¼Ò­¯.m`ž(TXœóJ ¥l >'ŸT¯ Ud©q ,>E‹¯„­MCÚÒÂá°œK“b±yµJqÉð˜\MlF'JŽ0žIÓ•,v-«*Tq™–C‰âl:ÕèÑ…|— ŽÌ²ÊØ¥^¥HááUæN? CR¬3 UzTá…ÂÖx¬/·ú Îê+û;[èå!¼¶‚ê¼³¼Ó¯+ˆ–hÖïOÔ µ¿±¹Tp'³¾¶·¼µ”<0E2 ð·ˆæÕaðÌþ9ø£¥xBóáW‚5Ÿ Y›Â¾ñg´xžÓK¿¾ÐtMBÎÚYÕà“ÌhæØœ/¿‡Éq‘˳ EOöl:Ì¥†Ë1³Ëpu±>Æ–g˜áðYÖUŽÆà2Éã1x?ŒÆÑ¡†ŸµF1¬òšx¦¡[;£ˆÄå´ Õ|E\®/W0¯‡ ê×Àå¯ÅàhæXúxl'0¡,¿ˆ«q¡/w¤@! O@ 'Ørk:µaB•ZÕeËN9Õ©+9rœ\ç+E9;E7h¦ÞÉ64›v[·o›<«à—Æï†´_Ã]âÿÁ¿ÿÂcðëÄ÷>!´ÐüEý‹â}ºã¾%Ö<¯Çý“â­'C×m¾Áâ=VÓ·Þi–éuöOµÙ5ÍŒö×SnéÎ42ìKV¡›dÙ'eóºÿhÊ8‹)Áç™6/–üô¾¹•æLW°¯x¬?¶öª414êÑ„9Åb³Lí96sœpþeNÏý›7ÈsNS›a9íìë}W0Âb0ÿX¡:¸jþÏÚá«V¡8T—«TP@P@P@P@P@P@P@P@P@P@P@P@P@P@P@~~ÌÞý±µïÛ‹þ óqû8üwýš>x^Ûá\:æñ·öMø¥û@kÚŽ»ÿ oû;¿ö–“âO~Ú_³FŸ£i?`kKoì[Ï ëןl†âûûɺO´8z8¥á¿J­jÃK7ŒcN²Ä¯ |.u«J¼%B¥.,Xzt°ë”ªÒœq2­Zxªñ¯´c 8w‡©OÁy?üIð?‚¬(gÓ©Ä2x,çG“Ï É‚¯,~pî#*­šeÕ¸Ã,ÿ—哯—æœIq¶6Íqõ°Æ]Içø ÐŒðœ9—àrü\òï­dõ1˜Œ»‹Åbi”ÿ4M_á'‹?àá¿>?|YÕ~1xOþ éû&xÓÿþ|~øç¡xo\ñgÄ‚ÿüG­üLð…lþ1ø£Â^Ó/um*Û\øEe¥^k6|?©Ýø{á©áÿêú²j¶äòÎÇá08 nEB·Òï-áʹ6c_Ì2ü¢Y¿“Äd¸ìfcJêøçK6ÇdÙÖeˆ§G5Î2¨ÑÈózµòœ.ÂöeŽiÅ~Ë0Ç`x…ÿĺq>qý­„Âá0˜,×€­âN]‚Íha°– Jô°˜lÞž9åsÏ[âxОVy¤ÿZ~ iz÷íÓñ“ö¢Ð¾.üiý¦>øöRÓÿg_†¿ <3ðwö‚ø±û?ʚߌ¿g|hñ7Çï_|*ñw„u/‹š‰¼Aã¶Ð´mâóøçáT:w€foøCµÝcÄï?R†,ã..Ââ©`3(øâÆO—ÊPÃÖʸW'àL~.˲Ùðþ65øs<]X×âlV'6Ë+âe”ç9V ,.[B3Ç|ßâkOáîM^ 2Ãâ|'ðÿ‹sJ•>±õî$Îx·Äx\\ªç˜jôóÈaðLƒ •BžQ˪Ç;Ÿbq•±˜èåÓʾý”¾%üxý¾jOØ›Â_¿h¯ÚCø}ãø%÷ÅO‰ÿü5ð7ãŽ~韼eðëö·´øSோwúÿÀ{Àzç„uøj;OÜßü(Öüu}¥†_T¹ð$—ÞÔ´Á`èf|AͱxZø„þ¼AƒÈa‰ÇÐÂp¶{â'qq*Wúï³Àâ¡S-Yfa[ÅS§ƒ­`ó Ç'Êñ8.ÜÞ­\¢<5•`ëJ¯Õ¼]úEpTó|]</œp¿fU“Ñ̰« [£‡£R9¼0´ñøJÏ3­”VÀ,ï*Ÿ«ðHOŠ_¾%~Ì?4ω^6ñ7Ä]Cà¿í}û^~Ͼñ‡Ž5‹ßxëUøuðw㟋|%à¼iâÍRk_Æ#Ò¼3ka£^x«Zžã]×£ÓíïõÛÝGYšûR¼Î8©æ\á‡â(áãšqW†¹gk…¡‚ÃbsyV̲ìV+€ÂS¥„Á˶–*¾ JŽc+b%†¡‡¡:t)ëŒÃSË8ËÄl‡ *ÿÙyýC(£‰Åbqµp˜ g pÖ{,#Æãjâ1تT1¹Æ28z˜ÌF#O ìhJ´ãFÁÿŒ>"|Iý?lOø)ýÿíûBü:ý¡þk¿¶‡ˆ~xWGøáñÂ>iŸ²ïÄï‰þøyðƒÆ?³5ŸŠ->|K>$±øeÄM[âÿß|@Öu¿jO¡ëþ{Å ùñ5òð³:Ááÿ¶ó.1Ëü%Ïsì.6KG‰'âVgÃØŒË…rê8˜âhä°lö\#•ã8nž]RÆe‘Ìq8Êù­|tjý.Ãqñ—ã§[(Ér>!Î8[ÁÊž“RÈr,$ªñ­\Æ:5óã±t+q£Âgõs‚†O†[ 4²xʽ~çþ íðçF¿ÿ‚»ÁKþ-ê ñSHñ–£ðGöñ~¡á}[ãׯÿøOJ×>/ü/øâ/è×þ×~ _x[°ðö§l¶ÓõO ÜéŸ ¬ÿMø_càÝ3VÕ,®þ+Àáò.㬫/Ä |3ø×ñ'áo<[ñFÚgÄúFâÛ|=ñ/†õQltÝ=tý[DKø4_iFßþ>±ñ?…­F…/…Ã5§B‡ŠùÜé<Ëù÷†¸l‹ Œ¾/ƒy¿ qæ/1t²úª®,kÊèQ–‡ÅåÒ©Qc瀫›`²œ~]ôGï`|=Ëi[ O=ÿˆºóŒ^{ ÄÉãá ëÔÜq85†¯Ÿâq¸lÃS œ`jF¶˜árü×<Âf’Úí;ûnüVý›>(~ÜúÂÏÛ¦Óö‰Ñ>>üVÿ„â>¥û`þÉ_?à>ðWÂÚ/\økcð#Ç_³÷ÅOÛ¿án‰?„¦ð·†nüãïˆ?fø_^;Ö5sÃ0³Þº²ìáx}^—ƒu«j¸Ï ážaÆÏ\39ñ>ÄXåXœçÃ8\LÞYN3…Î~­áî†c—æõ³¯'Âç´ñ˜ÌLj0¸ëâ(S©œø«”*ѧ…àìOe3W*§[W‡q\)b*å¹ÿbªÓËeœÑ©ŽÃG‰8²YÝLׇ0ù7ý‘†Ã`°x†öÏÛŸâçh¿³×ì›û x§Â>øñ³âgìâš÷ÄïÚ?á»ñOÇçÂ>*McÀÆ¡)O®ã · –CżßR¦¶Mâ牒C(crîáÞ È¸O2ÁB¾M™F¾QÄç®#ÆãñXÜó˜Õ©’QÊh`ªàéK,oÀðÞkŠÆäþ`q”èã#žøOü{Ôå«G0â<ã=ϸ‹‡q8'™`g†Ìð« ÃXzþÇ%Æeا˜ñ LElEGK.Žóçö;³ý 5ÿØ»öð§eø©ûOüðÿÃOÚ¯Ä>~Éß¶u§ìmûdkšíÿíqâÁ´Æ‹©x_âïìû7Ä?†ú^ŸaãEñgÀ¾ Ô¼]â f6*¼¶Ñ´í'—R2yV/—æ40˜_þQÊg‘Ôž:—f¸¯ £‹Æá3ÎÆbRÌÿÖ .ÃðäóJýzSáŒÞš§Ö3 u?v4ÞÅx<;S0©ãŸë¸Š(Öã§.ãZô)G.ÎéaT2ìFM›f8\oWÀÇ*L&–¹ãèSöv;úÿ‚lü\ãOìƒðóÅOñ7â7Å}SF×~&|>ñˆþ2øÓá§ÆM]øuñ/Åž¾øñwÂúoˆ¼W¥Kñ/á¨ÑâðŒüU¤k×ú_5¿ÝxâÁã·ñC¡šAJŽA…L·G9á>Íéæù=Ì)ã3'‘JŒE[W)¦½–™çàTèc8‹-«O2ÂUÊx—7ÁË)Î&±9¦EBµhæy~K‹Ìá_K9X ¯0Àà œB«ž?/–¶)Ëõª“û¶¼£Ò ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( € ( &ø}ð7áo¿üdñÿ€ü/ý…âßÚÆÚWÄ_‹š·ö߈µOøK|c¢x+ÃôÍcìί¨éšÙ¼áhÿÙþ³Ñ´¹¿³ÿ´.,eÕ.ﯮj„¥†Êð¹5É–à±ùÞi†Ãi/gŽâ<Æy®s_ÛO›?®cêOìªU=ý– )SO'ŒÌi‰~Óòœ§#uþøKÈþ»ý—…öPå£þËý¡Œýÿ³úÅmþÑZ·³¥È·¾^üoÑhû¯ ù¿ü=ðÇÄ´mø‰>ÇðßÅ'бõl'Õããc”C½¬r,VoÊ•Ü>«ŠÏp¹v 5«zn.¿Ö°ÙN_O“ëS¡õ~|4(Ô«^u>=ñïücöøÅ}7Ç5moÂ?õýwÆ_þÂìý 4ß‚wÿ"…hçÿÙU*âquke²Ì¥‚­W©Z„çŽÅʵߊðO?Ù㈾0øŸÇŸ u«ïÚà­‡ì÷ñ³MðßÅ/Œ>ðÄß…:DWÖº‹âïü?ñÿ†<ªëÞ°Ôõ 'Ÿ&ð÷ü,O h——‡|W¦iµ‘æ8ÆêMέ<ÇŠ2®5ÄÃR¦*?ë^M‹Éq¸<÷ Dê¬aSù#̪à~®³ªY}>s~Ô¥>ª§…–M<,hágÃùNeeÃaèaå…ȳl.cƒÆä®TiÂXœ¥Óͳ¸l»ëàòün&YŽ_G ˜B–&~(þÀ߲߯ wLñ7‹¼ã7]°ðN‹ðÏSÔ¾|qøóðV|6ðïÚÆ‡ðïâúüø›à(¾6øKMGUŠÓÁßãñ¿‡"ƒ[×­Æš`×µˆït¯l^76Çc!KóìuLÏ:Àã)RÅä™®gVš¡<Çø˜UÈqX÷†…<"ÆÕËe‰Ž†« ‡¡Jž8%ý€É²ì§„£Ã¸XಠN¥Jy¶Kƒ‡±k •ç±’Îð4]\= Téá³ q©Ž¡GQKJˆú7‡¿eŸ€ø¯àßžøk¤xgâ/ÃÏ¿ðÍ~Ô¼?{®hú…¾ Gñ4_toéú¥¿€ìô»Mg@Ñç°½Ã_Û:}µŒZeާo¥´¶Rt7ˆñ8‰Ë_4Åä:yUzµ+º“¥S ø‹9rxgGëOÞ3ÛºgG™`°‘Ádt(S§‚áŠÙ®#"ÃÒ^Êž¶w„˰Y¤ãìùeˆúÞ)ÀBKëÆœ¨:´U:Õñ*ï|øð“öuð÷Š<)ðoÂð‡è3ø—ñã‰l?·¼Mâí/ˆ¿|K{ãx‹í^)ÖuËÛ?íïjšöM…Í®…¥ùßdÑ´Í:Æ8­“ž“t2¼‹%¤ùrÎÉðùI†ø¾¥”ákbq0žÚWÄb}l^"~ßW‰—´åiFŒzë·‰ÌóŒâ·¿˜çØøfy¶#áúÞ:ž_Ê¡_ÙC–… `2Üg†¥F‹ö>ÕÓuªV©SÃüQÿõý’.üjÑþxÿâ‹.q¦xÿâ7ìÍ£|DÓÿgOˆ¾<¶»Ñ´KßøM(Ô¯u?í˜ô$ñžwØ¥ÕdÓãŠÑ}FžsK ûª|Aɳ Þ?Öñœ?ƒÌðEkÔ番a3ŒÆ—³Âº4±Yö˜¨V«F„é»õõqâ+æò/ìöž?0qqy†7Û™óU]c›©õÜ6™ºMá%`°´£‡Ãà³ùá ñž2ÂgRÇáÞ ƒÃJ›¡ƒÂÓ£ñþ •ûüYñoÅxûàö£¬ÿÂ÷Ù?Æ¿Û|\øÛ üøµ«Å¥ÙèÐø³â?ìûáψúOÀ¿øö ;NÓ!·ø…â‡wþ8·“KÒ®¡ñ]é–Û𬻲ÚÙC¤ç–ÔÅfêZµkV†UÍ+¼^?0áéU©:œ5˜âq’©Žž?‡ç–c:¾' ñÅⱪõ¼v)ã𙢫˘a0Ø ÅÓ…:U±ùvYí£€ÊóÉS„WåXZ8Š˜Zy^|³,¿ê>Ï,3ÁP£BÍcþ »û kšGÃ-óÀ^;·á7Ã;à熵mö…ý£ü9âÝ{àö“#ËaðâÏŽ<=ñkKñ—Ç…4³ƒðëㆽñÁ³Çsw Æ‹,Ww)/¥Ž­,Ó1ÍóLÆ4qØŽ íú8¬= ùf{_ ‡ž†?:ÉjS–O›fôèÔ¬£œãðŒÛÚWÄWx×_Z¥O?Fv[–eY{«‚Âd˜œ^+"ž½|>? «•9b#f´ªC4ÈðïØaaK•c0˜L5,Ž…*8 (gÁÿÈý‹´¯‡þx;៌>øWà=—Œt„w¿>?~Ñ|}àü@×§ñGŒü¦|Xø7ñcÀß'ð7ˆuù—T¿ð.£ã;ß››-#ÊÑ"DÑÒÆ+ή+^&¾&½ì,‹†q¦':y–IÃ8*y~C‚Ψ:ކ{ý—…¦ã†Äg4ñøµ[ÅN¼ñXüulEaéÑÂÐÄá¨a°°¡‰Î³N!öo †šÁç9Þ&®/5Çe.¥)K%©Ž©ZtëÃ(x/ É€8àiRÃÃëo… ~üð‡þ|)ðž—à¯x^¨´oé+;C š…ýÖ­«j——“]jZ¾µ®k÷ú߈5ýbòÿ[ñ¹¨jÖµ¨_j—×wsoŠÅׯT…Jî¹Ãa0XzThÑÂáp˜,– Áa0ÔèápX… & …£G „ÃQ¥‡ÃÒ§Jœ ³¡†¥‡ö•LV+ŽÅâ1ëâñxÜv6´ñÌn7Š©[ŒÆb«ÔlF+Z­zÕ$åR¤™è5ÌnP@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@P@ÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/zkperfreliability.jpg0100644 0000000 0000000 00000210301 15051152474 033313 0ustar00rootroot0000000 0000000 ÿØÿàJFIFÿíPhotoshop 3.08BIMÿâøICC_PROFILEèapplmntrRGB XYZ ± acspAPPLöÖÓ-applrXYZ,gXYZ@bXYZTwtpthchad|,rTRC¨gTRC¸bTRCÈvcgtØndinì>desc,ddscmþmmod(cprt¸-XYZ \¸4 ÄXYZ vµ¨'XYZ #ÿb¢GXYZ óRÏsf32 BÞÿÿó&’ý‘ÿÿû¢ÿÿý£ÜÀlcurvÍcurvÍcurvÍvcgtXAÕ r ö Š)Ç^ù…³Pât™!3"Á$J%Ü'm(þ*Š,"-j.,/Ö253 595ù7:8»:9;Ò>?l@$ApBûEGF©H InJÕL9MŽNáP7QŒRÞT/U…VÓXYjZ¬[ñ]1^j_`•a¦b´cÂdÎeÙfãgìhõiýklmn'o.p4q7r8s9t;u;v9w6x3y1z.{)|"}~ø€ë܂΃½„¬…™†…‡rˆ\‰DŠ+‹‹õŒÛÁŽ©“q‘Q’2““ö”וº–›—w˜R™.š šê›Åœœlž<Ÿ ŸÝ ¯¡†¢U£#£ð¤½¥‹¦Z§(§ö¨Ã©ŽªW««ç¬²­z®@¯¯Ì°•±Z²²â³§´iµ*µì¶­·n¸/¸ð¹°ºp»0»ñ¼±½n¾+¾é¿¦ÀdÁ$ÁåÂ¥ÃbÄÄÜÅ›ÆYÇÇÒÈÉPÊÊÒËŽÌLÍ ÍÑÎÏJÐ ÐÊщÒIÓ ÓÈÔŠÕNÖ֨יØ\Ù ÙäÚ§ÛjÜ/ÜôݼބßKààÛá£âlã7ääÌ噿fç4èèÒé êpë@ììæíºîŽïað6ñ ñàò¸ó‘ôjõCööö÷Ðø©ùƒú]û9üüóýÉþ‘ÿMÿÿXAÕ r  ’(«úz‹¯?ÁE´þ!l"ô$v%ñ'k(à*W+Ê-8.b/Ã152 45o6Ñ8;9™:Þ< =\?b@ A\B³DETF¢GÖHøJ,KrLºNOPP’Q×ST`UŸVÝXYAZZ[h\t]…^“_¡`¯a¾bÌcÙdæeòfþhijk"l)m/n4o9p=q?r?s:t7u3v2w/x)y z{||ó}å~ÖÇ€µŸ‚‹ƒz„e…K†2‡‡ýˆá‰ÅŠ©‹ŠŒhIŽ) êÊ‘©’…“]”6••ë–Á—’˜Z™"™êš³›~œHØžŸe - õ¡½¢…£L¤¤Õ¥˜¦\§!§ç¨®©mª/ªò«·¬w­9­û®½¯}°>°ÿ±¿²ƒ³D´´Æµˆ¶I· ·Ë¸¹MººÎ»’¼V½½Ü¾ž¿aÀ$ÀèÁ©ÂjÃ-ÃñÄ·Å{Æ?ÇÇÆÈ“É]Ê'Êð˼̆ÍQÎÎçϵЃÑRÒ ÒðÓÁÔ’ÕbÖ2××ÙØ©Ù|ÚQÛ'ÛýÜÒݨހßXà1á áéâÂãžä{åYæ6ççòèÔé¶ê™ëì}í‰î„ïtðsñƒò‚ótôtõ…ö„÷vøwù‡úˆû|ü~ýŠþxÿEÿÿ‰ Éo $ ³ Sþ}î{ƒ‚õ] °"#_$¿&'m(¸*+T,Œ-·.ç01V2‰3¹4ç6798R9f;J;ß<§=¾>Ò?ç@üB CDEFGH'I0J9K?L:MCN@ONPMQER,SSòTÕU¶V˜WwXTY5ZZñ[Ñ\³]”^w_Y`6aaíbÊc©d†e]f:ggñhÍi¨j„kcl7m mãnºo’pnq@rrçs»tucv3wwÒx¢yrzC{{ä|³}~Mꀹ‰‚Sƒƒæ„¯…y†G‡‡Ýˆ¨‰tŠ=‹‹ÒŒŸhŽ0Ž÷¿‡‘S’’瓱”y•?––Ì—•˜^™$™êš±›|œD ÓžžŸc * ñ¡º¢‚£K¤¤ß¥§¦r§=¨¨Ó© ªj«4¬¬Í­®l¯:°°Õ±¦²u³B´´ßµ¯¶ƒ·V¸(¸ý¹ÙºÆ»®¼˜½…¾u¿eÀUÁFÂ9Ã,Ä!ÅÆÇ ÈÉÉÿÊûËúÌûÍýÏÐ ÑÒÓ'Ô3ÕBÖT×gØ|Ù“ÚªÛÄÜæÞß!àBáeâŒãµäáæç@è†éÀêðì/í~îÀïùñ<òóÑõ öQ÷¤øêú*ûtüÌþÿÿÿndin6•T¼SF‡e'ž¨P T9s3^¸áG '2?N]n‘¥ºÐç5Pl‰¨Èé .RwœÃì@k—Äò"Q´S¡Ã+Nrì,k¬î2h’¾  š è 5 w ¥ Ô ' { Ð & | Ó , Š êJ« q×>¤zçVÉ<±)¤0¿Qæ~´Rô—<ãŠ3Þ‹<ï §!a""Ú#š$_%%%í&¶'‚(R)&)ü*Ö+³,“-v.[/D001 2344þ5ÿ78 9:;$<<=T>l?†@£AÁBãD EN?j@ˆA©BÍC÷E*F\GŽHÈJKhLÂNOzPØR>S¬UV…WöYhZá\`]å_k`ðbvde¡g3hÑjolm¶o^q r³tcvwÌy…{A|ý~À€†‚F„ …чž‰k‹9Žè¿’’”l–L˜-™ù›Ò­Ÿ…¡c£A¥"¦ÿ¨Ýª¼¬®{°]²B´(¶·ó¹Ø»»½¡¿ŠÁsÃZÅAÇ(É ÊöÌÞÎÅЮҚԅÖlØQÚ:ÜÝ´ßRá&âú䛿:èéäëŠí.ï ðåòô9ö÷ôù¤û`ý€ÿÿ !+6CRaq‚–¬ÄÛó )Ea¡Âæ 1Y…³âAr¥ÛM‡ÃI‘Ø iµS¥úQ­  M  :   u á P à < Â@¼;¹<ÁIÙcö²\ ¼q(ä¤c(î´}Gè À!š"t#Q$7%&&é'Õ(Ä)±*­+¨,¥-¥.¢/¬0¸1Æ2Õ3è56!7A8d9‡:®;Ù= >>?r@©AàC#DjE´GHHI—JêL=M™NóPLQ®STUîWVXÄZ5[­]*^ª`(a©c2d½fAgÏi`jíl‡n o¹qVrñtv2wÓyw{|Á~o€ȃp…†ÏˆƒŠ4‹ê¦b‘’Ò”Œ–H—ù™ˆ› œ¹žKŸÜ¡o£¤•¦(§»©KªÛ¬j­ø¯†±²¦´5µÁ·O¸Øº_»æ½n¾öÀ}ÂÄÅÆ…ÈÉ‚ËÌ~ÍðÏmÐèÒaÓÙÕOÖÂØ4Ù£Û܇ÝÝß4àŸâãwäÇæçèïêUë§ìýîeï×ñ>ò•óëõUöÄø-ù†úÙüFý÷ÿÿdesc Color LCDmluc itITÄfrFRBØnbNOesES,fiFI>ptPTNzhTWfjaJPtnlNL‚deDE˜koKR ¨enUS´svSEÆdaDKÖzhCN òLCD coloriÉcran à cristaux liquides couleurFarge-LCDLCD colorVäri-LCDLCD colorido_i‚rm²fv˜oy:Vh0«0é0ü LCDKleuren-LCDFarb-LCDÎì·ì LCDColor LCDFärg-LCDLCD-farveskærm_i‚r LCDmmodœ¶l>€textCopyright Apple Computer, Inc., 2005ÿÛC      ÿÛC  ÿÀŸ`"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šù×öý¤<{íeàï‚ß³IðŽ‘¯ëþÔÒÚ)n%ÜC$îò\Eiç %˜ã¯{á×ky©'$Õúr§+íeäÄýÛsiñ|œZ‹NÝyšVÝ»wWúzŠñØKöŸÖ?i_ø®ß➟¦é¾6øuâ«ÿx†=8H¶3ÜÚ²²Ü[,ŒÎ‘M °J™Šï*Y±’äÿ‚†|'Õ~!Iá/ø¥§×%¼ºÒl§}ù4{ýFÝ¥±ƒUh”÷)±÷Aí (ãnTâjN4ãÍ{®^en±i4×]S_zbäÚµ¬Ü_“M¦›ÛFŸÝu¡í´WÇ_°×üÒÏãÁÍÅ´gŒ| áËé>Áã]wF¶Ñ/lÓI…ï. ûgöŒ÷2BÐ7‘°[…2«)bìTzaÿ‚™|µð'‹|G¯øŸUЬ< ¦Å¬ëPë^Õ´«û[_dwicuiÌÐ3|¢X£t$A­*GÙ9)t½þM§ò¼_Üïªi(ÞV²ÞߊM/_yzÝ[F›÷š+äߌ¿ðXï„Þøãï|4›\ñfµà¯ ŸA¡Káý[J—[²-åÅum,ö?½³i0¦ö%–Á ÌšëOü÷á6“áÝ*óÆš‰t»›½×^Ô­×Â:ÕÈðÝ­À%%Õ$ŽÈ:3²B$»ò"3”f•›¿•—Íßÿ‘~–Ô;yßðåÿä£n÷ÐúŠŽÎòFÎ+>Xç‚tG$l$R2Hà‚9©(jÚ1&šº (¢ÂŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Šá>>~Óý—¼?¤êŸ|Eiáë={V¶Ð´ã,rK%ííÃì†ã‰YÙ‰É$ *†f!A#šø—ûu|;øUñZÿÁëøßVñF•eo¨ßXøsÀšïˆŒAÏ&›e:E¼Á.Õfì'¥tþû|ì½lÓ·f˜ìÿ ü›µý/§®‡°Q^)ñkþ !ð‡àoŠ#Ѿ(xšûNÔWMƒX½ñFã iúÏ„/­u='V¶ŽòÊîÚA$7PÈ¡ÒDqÃ+)Ôvm7ÛOžªßƒûŸbn´ó×妿ŠûÑ~Š(¤0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯•ÿà Ÿ|CñâÃÝkIøSuñ3ú%¾£ äžñL¾ñ–q2F"k-@jV(m$ é<&\ŸÝ° ´­}QEL ¥kôÿ&¿R£'Û©ðGƒ?àœ^+ø?ÿ\ø«ð“ÁÚ%ƒü@ø…§kú’èv7Èm­/uíŒwW¡ÄHa‡Î‘Àb…‰Áß|lø ñÀ¿gÏ‹? <'uãMCᾃyáoxfÏP³µ¾žÎöÞØ4Ö’]Í»É ö‘’¯2Fm­úæŠÑÉós­»òQRŠZßG8»ÝÛ­õ"É¥¯Äý\ÜdÞ–ÚQRV²O¥´>wÿ‚vþÏþ+øCðÿÇúïÇ++}Åß|i¨øÎûI·»K±¢%ÀŠ+{Fš?’I#‚Þì„©rÛK 1ñ¯Ù¿àgÆ?…~|ñ' êúg€µû›»ÏkwV7ú]íšËq<šm°¹ûl:«´è7M Çy¬$pT»h¬êAT'ÙåŒmåhùû«Ï^·-I§'Õ¹6üåvüµ¿Ë¥Ç¿Á!~8kŸÓÃÞ ðü~Ôì¾xsI‰eñ0Çw«i¾#¸ÔäÓÚâÎf–ðù`\G…V‘H|©Ç¡ü|ÿ‚{ü@ø÷ðƒã5÷>x³Ãzγàð®mã‰÷~(ñ&§q-ì71¤×:ÕÞŸkd<„Û–»‚ÇË+~ ÑZ¹¶Ûîæþsrrü%k=,¶½Û#')våÿÉyô„ÛÞíëk%ð7ü?öøñï⇊.~xvÞçLÔgísÀörµýµ¼ÚÓÝ[I¦Æ2†HŸmò×,+”ø™û3ükŸÄÞ+ø?ðËÇþ ø®ø7IÓt}wž0Ó,>Ã}m„Åâ»;F[;ø¡“çŽkX.Á#Ç€áM~’QP´Vþµ•Io¾õÚÙ%Þó¡Ë¦É-uZ*qÕlô¥ô»oµ³ü%o©ZxWL‹Æ7]êñZD—ÓÀ›"šp€Hè¿Â¥·;ZQU)s·.äÂ<‘Q¾ÝŠ(©((¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šüšÿ‚¿éŸüKã]OÆŸ¾øÚçFð—|5£ü<»µÔ´9t¸mޱe%ÍáGÔ–åoodEw@«I³ ’V£ñƒQ“@ø£ûOüdø9û@j¿|oáO iš÷‚5i¾N‰%¦žÓÛZêsÜGpnâ˜Í"ùš|Ѩi]VYW~ø×ÀÄ4&‘¯éñÜCv¶º•œwP¬ÐȲE(I¨t‘UÕ±•eFk›ø‹û.|2øÁã=7ľxÅ>!Ѷ?TÕô KëÛ­¹|™åž<7#i<ÔF2bµ“Oª¼`¯m›n-µdÕ­fžŽiÕö²WVŠk£JMÚû¥f’z½ï}?9¾|u²×>~ÛÞ(ý­là𷈼gà½Ä×úF Û4û¿ GQD²ì‚èÜÀ¨…ûÇ“þ ÿ}oðÚËöÆ»M?Vÿ…+à»ùÓW˜›JƒÃò[ÜíÞA*·hGžAžXgôâìÛðëâç‹t}â¿€|â}wìJÔµmÚöïL!ƒƒo4±³ÄCß!€zÒüPý›þüoÖ4CãO€¼âûýO7L¹ÖôKmBm9÷ÝÍŽUNTƒju¡°«¬§ÿ,b¤¢×wï+í{5zéQ›¤éÉ»¸·/W)S“^JðÓ{]hù}ïÍŸø$Ê| ñ¶¸Ÿµ½Òé77³×„/ìÿ¶& "ÚÑx ÈA I‘„°ÏQ_^Áô=SÿðKo‚¶Þ.Žæ“áåž(çÎøí¤–I-”ƒÈ„Ø`v¯kø³û9|=øötÓñÓÀ~ ñ¡Ñ¥ó´ó¯h–ÚØdãç‡ÏFòÛÊàð+±Ž5†5HUQª€WMJÞÖU&Ö²wû¥R_û}½SjËžùR{$¾èBàù¥önÿÚ?þ ûykÿðPŸ cx¯Äö¾ ñ†»ia¢øÀn³s§é¶º”–Ñ– §É)‚3#’IeÜÄ·4ááÿðV¿úþ9ÿá•¶ÿåE{ŸüóþVYý¯ÿîsÿԪʿs+™jmsùÀÿ‡‡ÿÁZÿè_øçÿ†VÛÿ•ÃÃÿà­ô/üsÿÃ+mÿÊŠþè§`¹üàÃÃÿà­ô/üsÿÃ+mÿÊŠ?ááÿðV¿úþ9ÿá•¶ÿåEGôQ`¹üÑøãþ ½ÿEøcýÿ )¾+x{þNJþÓøIciý§6ï&ÒßÌÒ‡›<›lI—m§à×)ñƒþ ­ÿ ýž¼M‹ñûƾ+ð6³uj·°ØxƒáÖ“¦\ÍnÎè³,SéªÍxäPà`”aœƒ_¾ÿðQ¯ø›Ãÿð¢?áZü-о(}»ã‡í5_í? Ë­ÿÂ%`ÿhóµ»,±Om…ÛxùH¼Ó‘ó ?lø$ìõû{üK±ñ‡íeðûþ¿éºdz5µßöî¥cåÚ$²Ì±ùv—1¡Ä—6⥾l€VÏÅøxü¯þ…ÿŽøem¿ùQGüµðSø‘ãÖ鬦¿ðÿ½?S¶†áQ¡i`ÒÙV@’FŠȧ"¿£ÚOölðWí{ðWZøwûDh¿ðø;Ä>Gö†ŸöÉí>ÑäÏÄ_½·‘%]²Ã|¬3· p¿cÿØ‹áìðÒûÁÿ²o†?áðæ¥©É¬ÜÚhÝßy—oP´žeÜÒ8Ìvð®ÐÁ~\’I,;Ÿ‚ßððÿø+_ý ÿÿðÊÛò¢øxü¯þ…ÿŽøem¿ùQ_ÑýXW?œøxü¯þ…ÿŽøem¿ùQGüß¼WáÍKÙ5]á%õ•ÖÉ7òç‡Jd}²#¡Á8e`yVïüûgfxιÑ`¹üàÃÃÿà­ô/üsÿÃ+mÿÊŠ?ááÿðV¿úþ9ÿá•¶ÿåEGôQ`¹üàÃÃÿà­ô/üsÿÃ+mÿÊŠ?ááÿðV¿úþ9ÿá•¶ÿåE~×ÿÁ\|[ð“À¿ðO_ˆ:¯íÑá}wÆ í³¿¶ômVŠöóv¥j¶þ[-ŹnZÝÎ&O•ï}ÖúB‹ÏæËÅðSoø*Ç‚<5¨ë^4°øË¤hÚE¬··÷÷¿--í¬­ãBòM4¯¤…Ž4Efgb’@…ðþ ÿ2ý¡¼5>µðSø‘ãÖ鬦¿ðÿ½?S¶†áQ¡i`ÒÙV@’FŠȧ"¿¡OÛ¿AÑÎ ¬yÚåXw?¼Qÿ6ÿ‚¬x#ÃZ޵ãKŒºF¤ZË{{ðrÒÞÚÊÞ4/$ÓJúHXãDVfv $G…ÿà¦ßðUøkNÖ¼añ—Wѵ{X¯l/ì¾Z\[^ÛÈãšSI+$nŒ¬®¤‚ kúý±õOHýþ*Ýø/ÂÖ>9Öm|«Íaá»Ý5õ;o\-”Æ;¬Ó湎g B¼È¨äÑûj:ž¯û!ü+»ñ§…¬| ¬Ýx?HšÿÃvZké–Þ¸k(L–0Ù¿Ím.Z%…¹Œ Sȧa\üÿ‡‡ÿÁZÿè_øçÿ†VÛÿ•ÃÃÿà­ô/üsÿÃ+mÿÊŠþè¢ÁsùÀÿ‡‡ÿÁZÿè_øçÿ†VÛÿ•ÃÃÿà­ô/üsÿÃ+mÿÊŠþè¢Ásùxðïü[þ âÿ‹rxÂ~5ñ^©ã¸®®,ŸÃvŸ´™õt¸·g„Ù¦šeD"”ºmÊßpN=#þÿkÿ¡ãŸþ[oþTWíÿø$ìõð£öµ¸øéà‡ß`ø©u©ê̺ßöî¥.ë»õ™näû4—-n<Áu8Ú#Ú»þP¸úB‹çóGãø*÷üáö?ü,¦ø­áïøHu8tM+ûOá%§öüÛ¼›K3Jlòlm±&]¶œƒ[¿ððÿø+_ý ÿÿðÊÛò¢¿s?n/ø]_ñgÿá‹ÿè¦è¿ðÿLJüŠŸ¾þÑÿÏûaþ£÷ÿÜï^çE…sùÀÿ‡‡ÿÁZÿè_øçÿ†VÛÿ•ÃÃÿà­ô/üsÿÃ+mÿÊŠþè¢ÁsùÀÿ‡‡ÿÁZÿè_øçÿ†VÛÿ•Ã|hÿ‚×ÁH?fÿìßøhxëÀ_Û>oöü$ ôÍ/íÞVÏ7ÉûF˜žfÏ6-Ûs·zçþž+Ã?mø&ÇÁ_ø(_ü#_ðØ> ÿ„¿þµdÄÞÿOû'Ú|Ÿ;þ='‹~ï³C÷÷cgÉÉ`¹øKáø)·ücÆþÓµ¯Ø|eÕôm^Ö+Û û/ƒ–—×¶ò xæ†TÒJÉ£++© ‚$½ÿÿ‚µÿпñÏÿ ­·ÿ*+ú"ø_ð×Dø3ðÓþøkeý›áÏ i–Ú6•içI7Ùm-âXaÌ‘™ßlh«¹Ù˜ã$““[´X.8ððÿø+_ý ÿÿðÊÛò¢øxü¯þ…ÿŽøem¿ùQ_ÑýX.8ððÿø+_ý ÿÿðÊÛò¢øxü¯þ…ÿŽøem¿ùQ_ÑýX.4¿à«ßðT_ƒ> ½ñ/Æø­áOi»>תë? ,ll­wȱ§™<ÚR¢n‘ÑHË2É¿VàÛßÛwâ‡íïûx«Æµ—‰ÿá+ñ›ã«½Úïû:ÒÇË´M?O™còí"$¸™·-ó`œïüÿ(VøÑÿp?ý>éÕáŸðgïü£OÇöSoÿôդзèýY¢Š)’QEQEQEQEQEQEQE~ ÿÁ">#iàáÛgÅ>>»K@³ñÅýäíüÇâ{68Øãz’@šú{ÃðR/Ûöè·¼»ý‰>èz'…ÓÚC«ÍrI NéîåXª²’«`äsÀ¯Ëkx³ã7ü'öˆø}ð~i“PøãÏh³Æ¤ˆæƒþpÞv>TmmÌGA{Wô½û:ü Ñ?fo‚^ð/ÃØ„z_‡,ÖÙ`W¹½$Î䑞F÷s_-Œ¥‹ÍqÒÂÓ«*TaÌãe)J[$ÚvIjíßÐý³†ñ¹pÅ<ë‚£Ìq5$©Ó¬¥*t©RÒS”£Í)ÍÚ)éh»lÓù;þçûwx¯ã³xÛáí]$ÿ| <’5ÄQÅ5Õ°—Ë•JÆî‚c°à¶HÀèM}Í_˜?ðV?‡z¿ì-ûexö¢ø)k1²¼¼ŽÇÅ6°ecž@›üpâÜ4dž‘+rÌ+ôá¯ÄM'âçÃíź[ÝÄQ_Ù̿NJr;ê ò*ø{V›«–â¤ÝZOF÷”Ã/7Ñù­L'h´G¿áWø:ûû+ûCÄߨsëÙ›5k'‹ý Ý„²ù’¤Q|¤mów×Õ•òŸüëþ§ü;âwü6ü'_ð®¿âUý¯ÿwÙ¶ÿä-eäý›í¹ÿ_äïßÿ,÷ãæÅ õeQ@Q@Q@y¿í§êz¿ì‡ñVÓÁ~)±ð6³uàý^^êO¦[x~ᬦßMxŸ5´p¹YZeæ0…‡"½"¼ßöÇÒ<%¯þÈl>?j—Ú/|«Ûø“Q²B÷:~šöS-Ôð¨ŽBdHLŒ G!%GÈßt€±ÆŸ©é²»Ox¦ÇÇ:ͯƒôˆoüIe©>§mâ …²„I} ãü×1Ìá¥Y›™†<šôŠóØãHð–û!ü+°øª_kž²ð~‘oá½Fõ \êjYB¶³Ì¦8È‘á³d?"ýÑéQEQEQEQEQEóüÇÄ>&ð¯ü×â ÿÁÿ…ºÆŸÁýöOë>—ÄvZÎu+U3N„‡ŸÊŒ¼ãähUÏké ðÏø)?ü.¯øb¿ûÿä± ÿÂ?ÿóÿoöŸù£ǯÚÖÀ~mµîtæÿ¶>¯á-öCø«ñûK¾×< eàý^ãÄšu“”¹Ô4Ô²™® …„‘‘#Â$U"HÈ,>uûÀýŽ5 kÿ²»ÿ€:]ö‡àKßéÓ¯\½ÎŸ¦½”-kÌd™±2HISó·Þ'í¨êzGì‡ñVïÁ~±ñγkàý^k Þ鯩Ûx‚ál¦1ØÍfŸ5Ìs8Xšæ@åG&ØãQÔõÙá]ß<-càmfëÁúD×þ²Ó_L¶ðýÃYBd±†ÍþkhárÑ,-ÌažE¶?‡|[âÿÙ⮓ðKè¼wªx?W´ðÛÙ^‹”Ô¤²™-L7%ÐA ™£Û!upÛ†2Øãþ-ð‡ì‡ð¯Iøý%ôÞ;Òü¤Zx‘ïoEõËêQÙB—Fkî'̲n;9mÇ94nýDñWì=ñ—Kø•¯ÿÂ)áÍKÀºÝ®«­ý†KïìkGÓçY®¾Í?•i<¤!ŸnÑÉ~‰á_Ø{àÖ—ð×_ÿ„¯ÃšotK]+[û –?Û6‰§À°Ý}šB^60²yNK&í§hÕ¨¢Š(¢Š(¢Šù¿þ 5áïxƒþGü+_ŠZÂÿ°ü`ðýÞ«ý§âItOøKlív‰oåƒöÙîr»lß /”r~Q_HWÍÿðQ¯ |$ñgü(øk/k¾þÌøÁáûÿÿfDÒkx’?´}†Êãm¼Û`“3ncå´fTïô…QEQEQEQEQE|3ÿ'ʾ4ÜÿOºuxgüûÿ(ÓñÇý”Ûÿý5i5îðrwü¡[ãGýÀÿôû§W†ÁŸ¿ò?ÙM¿ÿÓV“K©]Õš(¢™!EPEPEPEPEPEPEPágüM·¾ÿƒ˜?kio`Ši,¤ñ¤Öîè¡sâ{D,„ýÓ²G\ŽÌGs_ºuøgÿ<ÿ•–kÿûœÿõ*²¯Üʘ•)7dÞÇñóào‡¿iOƒú÷‚>*Z›½ÄÆÞuRHˆ!’X؃¶DuGSƒ†QÁWæwì­ûEx“þ½ûG_üý¯®çºøU«<—úº°É*Z+±"hÑw°+, ’|ã ’ÿ¬5ä?¶wìQà¿Û›ál~øÃ ÌbÒån¬u2©ya #w”ì¬0ê 2AeT:ʪâe n ¨â)ìÞÒ]a/'Ó³×Cõ_8ã”R¯Ã¼I UÊqM:Š?*‰Z5é^öœv’ÚqѧdÉÛãöÑø¹ÿ¶Öü}ðsH×t/‚¿ /í絸_ݵ™‘"¹™³‰.7HŒ#]ÂažX³þÂ~Ê?­i¿ÙÃÁž<Ò6*ø—LŠæhÐå`¸dñþÄ©"Àj½çì¡àÈeíGá„ô›mÁ÷º4ú2[@¿ê’Te2dòÒnmåÉ,[æ$žkò¯öNý¶uØÏöý¢~øö鬼eà˹¬4(‰EšîCg8„63äȦãŸï“ÏJùÚR­Âø×_YÍV„œŸE8^VeËt—V¾GëxÊ|6²ÎÀ,<òüE8R‚ÖRÃb\i¹Ôj¢¬£:’Ú1{»9?›mŸÚ²Ïâ/üÛ@øáã+}jÿÁ¾ø±¢.™=ˆSK)Ë[FXð‘ZK! –ËÏè®ÆúRÆ:TšÞâ5–)ådV pA¿hÿ„zgìÑÿ øcмŠ)¾jÑuµÚîZÅz'Ï#ÆÌ»/Ï0µs\–1TrêÑÀ4“ætáM*u$öiÎ"šò¾é¿ª¨¢Šû£ù(¢Š+å?ø-×Çøfÿø&ÄïÂàoÿceÄ‹Æ:Wö¦‰}æêÖPÿ¤ÚïO3g›æ'Ì6ȈÜãêÊðÏø)?ü.¯øb¿ûÿä± ÿÂ?ÿóÿoöŸù£ǯÚÖÀ~m´=Ί( Š( Š( ¼§öï×ôO þÃßuO‰Zü%~Ó| ­Ýjº'Û¤±þÙ´M>vš×í1‚ðy±†Í@Y7n^­\7íAã|1ýš~"x—à¾ÿ Œ|=áKSд¯²Kwý§ ¬²[Ûù‘,¾d¨‰åÆC¶ì)ƒ@_°†¿¢x«öø5ª|5Ð?áðæ¥à]ëJÑ>Ý%÷ö5£éð46¿iŸÊŒ¬~k€Ï·qäšõjá¿fx›âwìÓðïÄ¿4øG¼câ 麞»¥}’[OìËù­b’âßȘ™bòåwO.B]vበšîh¢Š(¢Š(¢Š(¢Š(¢Š(æÿø+ìýáŸÚ—þ ëñÀŸ>#h_ |9®ÿg}¯ÅzȈÙi^N¥k:y‚iàOÞIB3*üÒ®2p§é ùOþ uÿ Óþñ;þþ¯øW_ñ*þ×ÿ„;ì¿Ûò²ò~Íö¿Üÿ¯òwïÿ–{ñób¾¬ ö ÿ„×þ§â'ü3gü”_øFu/øEÔÈWì²ýþ>sþ¿Êÿ[û¿ï|¹£ö`ÿ„×þ§áßü4Ÿü”_øFtßøJ¿ÔÈWì±}³þ=¿sþ¿ÍÿUû¿îü¸¬/Û¿Àÿð³¿aþØÐ¼=ÿ u½3ûW[»û&™¦yÚ|ñý¢î|*÷ïy0v¢±ÁÆ(ý„<ÿ Çöø5á¯í Ä?ðxDÓ?µtK¿µéšŸ“§ÁÚ-'Àó`“fô“r2œ â€Û¿þŸøaðº?·áÿ„[þÝþÄò¿´þÁýŸ?Ú>Éç~ëÏò·ìó>Mûwqš?aøFᇾ —þÝÿ„;þ]û ûoÊþÓûö|gû_“û¯?ÊÙ¿Ëù7îÛÆ*÷íñLøMû!üUñW<9cãÃ>Õõ[ýô ¶×-ಚY,æß‹å̈Ѷäq‡9Vö8ø¦|Yýþø«Á~±ðvâoé­†dPÛhvóÙC,vpìŽ5òáGX×j  *ޤQEQEQEòŸüOǾ ðü3‡ü.ÿÂ{ý³ñÏÃ:^…ÿÉô¿øG5Y~Õö}W÷*~Õämôi1›þb0+êÊðÏÛ‹ãÄ¿‚ð§ÿá›<ÿ ü%ÿt_ ø«þ%WZ‡ö&ƒsç}³PÿFuû?•åÅûùwD›þe9îtQEQEQEQEQEðÏüÿ(VøÑÿp?ý>éÕáŸðgïü£OÇöSoÿôÕ¤Ö7üKÿÐ|cû*üøáÏê÷÷ú5¦‰=ιm,rYÛHšÆ›,©2KÅå.LŽª€Z¶àÏßùFŸŽ?ì¦ßÿé«I®\62Ž3™Ñ•Ô[‹õ[¯‘ígé«4QEu(QEQEQEQEQXþ6ø… |4Ò#Ô>#ëš?‡ì&¹ŠÎ;Jò;H^y\GAä` »²ª®rÄ€&°¾#~ÒŸ~x¯HÐ~-øÿÁ^×<@Bézv¯®ZØÝêD°P-á–EyNâå’ V’êíóíêDßÏåÜíh®+â7í)ðçà÷ŠôâßüámsÄ.—§júå­Þ¤KÞdW”î!~Py RüRý¤¾| Õ´«~>ð_ƒïµ×òôÛ}o\¶ÓåÔ[!vÀ“H¦S–Q…’=hZÚÝ]¾}½CËçòîv”WñcöŽøyðû7þŸ<à¿í©"Áq>%Xü-ý•lµ?|-´Ž ¿êWª7œáˆ¸1¶ÖU`Ë ß½dÞÄ*æ?-ñ[£‡¯A¬^Êš×™½œe·/Vúu>ÞÇ1¯ ×,Ì©Ï"iÎx¹Z.ŒcnhU¥~eY](Á/}´Õ“výÔ?à¢ßtבn>0ü9cËyZõ´ ñž 9Ýøf½c¾)Ó¼sá?Zð}í¾¥¤êÖÑÞYÝÛ¸x®a‘C#£ªT‚½|MâŸø ÀÉ>jZ_ƒ4ýf/¾$6:ÕÞ«;˜î¼¼G4«Èß‚T $ qŽ;þkûZMῆ6ø1ûA\dëß$º»E»š=$arÉ'ìónÉèd…®Œ>oŽÃã)á³:pЍŸ+‹ms-\]Òéª<¬Û€8k7áü^sÁ˜ªõ§…œju¡ËÙÔ¼UJjM¥;'}Rwi[Xÿà­·ÏÅ…¿¶Ãχ¿±\—×¾$Ò¬¦ÖuM.ÖÔÝ®®]Y–ÖXË…‚ d!pØ•JÊóŸÚÓöÿð_ü£þ ™ñቶ.N4 äÒmÙ7d·nÝVw±Ç‡|[áÙá^“ñúKé¼w¥ø?H´ñ#ÞÞ‹ë—Ô£²….Œ×!ÜO!™dÝ vrÛŽr}"¼Ÿö Ñt/ ~ߴφ!_xoLð>‹g¥ë‚Éì¶-c°…"ºû4„¼b*¿”ä²nÚy½b­ûòÿü—öáOØ›ö\¾¼ðÕÊGã_ïÒü>†ødeýåÖ9â;‡ohÁá«…ÿ‚%~Ü“þÒßåð?Å©›Çÿlî…Û7Úo¬Á+ í»ægB<©3“•FndãO=ÂÃ1ŽXßïoåä½Z»ô^gèt<2ΫðN3?öXTTÿ¼ÖΧø#77üÎÝöÕÙ¦KtÝ;ª.@Ë “ú)Õ쟞Q@Q@Q@7ÿÁ\h þË_ðO_ˆ>;øÁðçBøµáÍ û;í~ÖLBËUóµ+XÌ3A:~îIRa˜›æ‰qƒ†HWÍÿðWˆŸþÁ=~ ëÿ°½¾»uñRÃû;û-D]jõ·êV±Üyvm ¢_ôg¸'1¶Õ ÜmÈúB€<§öïÿ„gþ{ã/ü.íßøC¿áÖÿ·±<¯í?°gÏö²yߺóü­û<Ï“~ÝÜfØCþŸøaïƒ_ð¥ÿ·áÿ„DþÂþÛò¿´þÁýŸÙþ×äþëÏò¶oòþMû¶ñŠ?nýDð¯ì=ñ—Tø• ÂWáÍ7ÀºÝÖ«¢}ºKí›DÓçi­~Ó/›hüÔ“váÈ~Âþ‰â¯Ø{àÖ©ð×@ÿ„SÚ—tK­+Dût—ߨ֧ÀÐÚý¦@*2±ù®>ÝÇ’hwö ÿ„×þ§â'ü3gü”_øFu/øEÔÈWì²ýþ>sþ¿Êÿ[û¿ï|¹£ö`ÿ„×þ§áßü4Ÿü”_øFtßøJ¿ÔÈWì±}³þ=¿sþ¿ÍÿUû¿îü¸ª¶>Ÿ©êÿ²Å[Oø¦ÇÀÚÍ׃õxl™máû†²˜G}5â|ÖÑÂåei—˜ÂŠ?c?SÒ?d?…vž4ñMŽu›_éßø’ËR}NÛÄ e’úÇù®c™ÃJ³72 y4éQEQEQE|ßÿøwñoâü(ødÛvßûã‡õfkk¥ùž‹ínKÓGö˜èw[ æN0Ž>¯”ÿàª~ðW?áœ?átxûþ/ìoŽ~Õ4/ø‘ϪÂGªÅö¯³é_¹aö_?sÿ¤É˜ãÙó‘_VP\_ÅÿÚ3ÀŸ³ûi_ð»¼Y¢xU5É^)5;•·Šw@ 1°«€Ã%ˆŠí+óþ =¢CûLÁBÿgƒ—rÊl®˜\ê) •t·»»Xå`q€âHôã <øùîeS+Â:Ô¢¥6ã§³r’_•Ïм0á /çÐËñõeOujTœRrŒ)Ó”î“Ót–½ÏÒï ø¿IñÆ“¡à­SNÖ,%û—6W)q ý ó­ü’ý¯à–þ!ÿ‚cév¿¿`ÿxžò? ^%Ö­§_•–H­Ï˜æA<ådF^Ëg Ä{í5ÿ‡ðî¥ÿÆÿ…ð’õ,¼kã4oÛiÉ.éô]DÆ>ÒÄã8†6óÈÃo€7àyÔø•aýµ<ÊŸ²©N<Ö¿2”{ÅÙ_]-½Ï®Æx6óoìüWâþ»„ÅUö<Λ§:5wå­ ÊË—ßSNÎ)½4¿¤~Í_ðS'ö‚ÿ‚‚|HøI¦µ·öO‡­Ut+´ë¨\[1Kñ»?7Í"ì‚;gšúÒ¿ ¼Mû x»þ ƒðËàí ¥‹·ÖfÔψ,\í^pó ¶Æ2¢[O>)3÷\‘ÜcöïÀ^7Ó~&xFñƒ®ïI׬¡Ô,¦%†Tø« ŽÍqXÕV†=rÖ‹R·÷'ïGîÖ/µ¬õ5ñ¯ò^ž 3ኞÓR2¢åÿOðïÙÕo·´´j.’æ”–†µWÉŸ¶OüGá'ìy6k{'Ž|]ùO¤h’,‚Õ»‹‹ƒû¸ÏQ°opz¨ë_AÇá²ê~×QF>§Vü‘ùO ð®oŸŀɰ³­Uôн—y=¢¼äÒó>³® ö¡øñ§þ̳狼yâR†ßÃZt—QÄçæ|m†¬’´h?Þ¯ƒãÿƒ–<%ªãá—‹MÓ ýÚß[•ߺ©ã;síÚ¼ö¾ý±¾/ÁY´ |2øQðgžðö£¯[Éw¨ºÜÞÃ0ÉáÖÚ4†Ë™,ÀÁÈÛ_3ã,ÃÍ`j{J­Z)FOÞ{t²ùŸ³pßÑãŠ)æ˜iñ6a°šuªN­$•8ë;.~fÚM.TìÚoCÅ~*üÔ$ÿ‚ þÓŸ~(¸ñ/Å OJ÷2§ïÙ/\üXø‹%ßÅM â–‡áÏ„~;ð埄¬ÿá×O×.ÛT´7Ú³^Åhmfö¶°‰YÜ´åUšXÅzTVžøÏªÿÁCu¿Œ6ìâm ÎÎ/í}>K;Ë xun­Ç•p‹-¸ó¤žMŒªÂT%€eãï¯_¼+ûDx6/übÒÿ¶4ˆ5MU ûLÖûnm'K‹y7Bêß$±#mÎÓŒ0 xŽ?ðOÏ„´Ä¿|(ú–­si …ùƒW¾±·Ö­¡—ÍŠßQ¶¶ž8uQùܤª2F0H¬}“xyÐoYs«öç4åç+Á«]%4­ºª£^5–ÑåvïË)5%i'{;É&ÕîßçwìEcÆ/Ù;ö¸Ökз¾(†¾±ÔgÔâæÛN†æ2Œª›—¹—#¬Š[ï é?àŸ6 ñ¥?hÿÛ PÖGÁOéÓ¾«óaÒ§ðô—EY‡÷Fᜎ¯<¨ÇÝ??àž¿h/ ⯄>Û¨Ie›x¶º­îŸk«ÚA ’mBÖÖxá¿…eb¹IPrÁ"¥øëû|$ý¤|M±ño²]êi£E•ì5{í)u <8Y^Gg8~ÀŸ ¿h‹Í2ãâw…\Ϥéo¡A&“«_h¬úkcu„ÆÂx|ûC´fÚ]ñ³^©áŸ iÞ ðå†áM3JÒ­ã´³³µ‰b‚ÖÔ*G(UTà[Ô¬ªJ¬­ng{ysU—Þý§ßÍÜÆÜd—Ý pÓÉò}ܽæWáïíSñÇöBÿ‚áþÔ~%ý€þÿÂÐñ÷‰¼Y¦^i_ðêßÙì_YãȰ‘%]²ÃnžcƒÍÁe#úx¯Ã?ø!çü¬³û_ÿÜçÿ©U•~æW4M˜QEB (¢€>oÿ‚xKá'‹?áDÃYx£]ðÇögÆßø7û2&“û[Ä‘ý£ì6WmæÛ™›s( £2§¤+å?ø*Ÿ|à?øgø]ÿ„÷ûg㟆t½ þ'“éðŽj²ý«ìú¯îTý«ÈÚÿèÒb97üÄ`WÕ”ý„­ŒÖº¤1\Û\ÆÑM ¨%Fee<A ƒ×5ÊüøàÏÙÓ³hŸü9¦xgJ¸º’ö[{(ö¬“HrÎÄ’O`pªªª¨¯¢¡Ò„¦ª8®e³ê¯¾¾gL1¸Št'……I*SiÊ)¾Y8ß•µ³jîÍ­.í¸Wãçü·öoÕ¿gÿŽß¾Ks¦i¿l§Ð<@ÖÇjý¡íÚ'YB·Ûûлu ×ìrÿþ xw㿃×Aø£§G©ék{k¨_ÞÛΓFr9t`uRá5ãqN³¼°éÚ{Åö’þšùŸ¡xKâ ¼6â*9¬¡ÏA§ ÐÓß§-Õž¦”•úÅ'£g˜ÿÁ5fßøe_Ø·Á¿ƒÈÕÚÈjZ¸?{í—½‘[ÔǹbÈí¯ÿƒˆÛB´ÿ‚@|Y¾øƒ¡ÿÂAg§ÿeI¨º6¬³>«gr UX®Ó.âù”2änÈûb¼3þ OñÇâ_ìßûøÓÆŸ±ÿƒÿá=ø‹£}‡û#BþʺÕ>ÝæßÛÃ7ú-£¤Òl‚Y¤ùmÙ¸åAÑ£ƒ§‡ÃG ‚1Q]’¶ëTüÖ·Ôùˈq™®q[;®Ó¯R¤ª»¥(¹JNM8É8Ê:ÙÆIÅÇÝi­ ÿ‚<~Ùº%ßü0jŸõxl øGçéz¥ÌíÌv±-»êu"B rÍ'Šñ öªý­¿àªZÖºcs§ü5øgm|öQjÒ8µœ ÁK¬I1—i ßfU ¸)'‚Þâø'üsÿøðá}æ¡¢|?ÔõÈ|G«<`‹+];/5³°HÈ·O&pÒq°”ýœø'ðcÿ³×ÂÍÁ¿ ¬Nдu·¶ˆrÍŽZIøäv%™%˜žõð™5,Ã:¥ %yJ*7„œ]¥RQv²{¨¤•û·÷Oø…ŽáO1µóü®,f;1qÄQ…X)ÓÂÑ«QÊP¿+«9Ê\‰é$ì¶—ÇŸ²ü³Á? üTþ.ý«5›Ÿ‹¾0ž_´1Ô‘¿³ÒCŒ´‘;³]>sóJÅH#÷`Œ×Ý–‘iö‘AapAãŽ5 ‘¨  pIE}¶_•árª~Ï ME=û¿Võ66ñgçœq‰X¼ï*²JÑNÊ]¡¥/(¥~º…~SþÖ^ÔàðQ½'ãGÛ+©>üIíõû+dýݳÈÁ®a ls;rUÐaTšýX®oâçÂ?|vøuªøOâÆ•m¬èÌ& «YÇ :†R9GR+©HEsgy[ÌèÇÙK–¬”%ÚK¿“Ù£ÙðßáÁ™…U¤ë`qt±¯nzrëè§iAèÓVº»g7­þÖ¾Ò¿f›ï‹v^!±Ô¼e§6¤/íd³(àD£‚%/ˆ¼³†vù)àÙWNÿ‚ü$ý ¿ißÛþãÆz‚¬ôJöÒ×ÃSB/ÚÞÎÙå™- ÊÝb‚! ®å·*Miü~ÿ‚JüeøIñoJø1ð_^×uƒµÈoåk}:HQ‹µò(Â)Š‘*ä>HÝšùü5˦i¿hÑt{‘¤›É·D;cLªª¹8¯ÕoÑiŸ¼gs|Û!·Ð¯¥‘°NÕ[w$àrxÛÂíQÂâb¶Z«î‘ó^4Sy–y“Î| ¶º¹SI´~h|Ò®¿à±ŸðSOâO‹mî%ø=ð¦D‡HµH†í‘‰·Œ©à´®æQ‚B¸*kKþ WðóXÿ‚nþÜþý¦þÙÈþñ-çØüUa “ºþù[°©pHâx‹“–Q^‘ÿáYImû x–Iãd[Ÿ]¼lF<Å6 ‘ê7+¨5öÇÆƒý þj~ø¿¥C­xWU[«YY”>פ2ÊÁ•He ‚85åå¹3Ír…‰R¶&£öª}§wþÝKKvoCí¸¿ÄXð7O'•.l£ OêR µSø¯hÞÉÕ”¿yÍ£rI7k³òSã·ÅÚ?þ ¦ø‹\ø¢j>øMà•{ý>É. u{ˆôbóuw• ±¯îâ*;ðÒ}Ûÿý½¡ý·ÿgxŠç|{á0– €vÁÝ…P§<\ÏÒ¾ð†•à Xè¾Ó¬´#L…mí,í!X`¶F¢(@ö¯ÍÚóökø“ÿèý¸ãßìEáMGÅñ4s?‰tZD…ä ÍÇ.!‘öN®ªDnŒ(:Ï‹áڰ̧VU”´­¥ôé(Ål ú+èÙLJâ‡ÅÜ nÃàèàÏ/wJòK÷”«T“´§]$Ôݽø¤ÛÒÿ§õãÚŸíûðsCøéyðß^øáûØ„Z]OäIJ1â´0‡‹~ñ¸qœãጿðpd¿~ Â7û3x'ĺ7ůNº\)(Žê-=¤ùwÛù§›qڊѦî9ÆÖë>Á¿^ñìû#þÖÚ†½7ÅÊu ½NÃR,ÚS¶[ÉÃG;s#¸b[!™»'Äus*Š–IÕ²¼¥+¨¥Ò)ïÌý,ºù|þÂàçŒñ"½L©/gB•% •dÕ¹ªÊ7kØÃmäߺöæýø‰ñSÃ_ü'6½ñG_Ñü=£@{ÝBí-àè¹“Ø “ØùGÅ_ð^ÏÙËÓÍâkF€Ö:$ádù±ò™„yõÏ{ñ^9ðÿþ ä7Þ0†Ú7âÖ½âŸè?»Ñt»Ex%XŽÖ2¼‰n¼`¬@îë¹zWÙ?¿àŸ¾éVvžøOà3ö Åî õß«\N¯#7¹bkX×ϱÎð¥ þûs“ôå²K×s†®Wá Ç—ÄæU«ØEaéÅtRuTæäô¿*Ij¯t|#ãßÛ·ö‡ÿ‚ªëz…?àž¾Ô<à8] ½ñÌÿf¸‘vçl·c+<~ê òs’ÅIǰÁ¿n]_Ǻ.¥ð7ö–ûUÄ¿‡;íaíþ‘¨ZÄÅ7Ï-,'ø£s‡5÷?†ü1¦ø7F‡Mð†c¥iÖÀˆmlàH!‹$“µ$žS_ÁUàŸ~4×~/ø[ãÇì+m*|NÐï ŽþÒÕ’7Ô±\ ä#Ë‘\íxˆÏCpâ2ìÃ)”s5ZUª-'hà÷PŠÙÇuÕëè}6SÅÜ'Ç”jðdòú9vjøjÍ·8WŠv–"´¾(ÕMÂNÉCK-9—·ÿÁ\|=âoÁ=~ Ø|ø¥¡|ñÿÙßdñ–³âI|9e£cRµgó5Ax<ØÃÀ0>v™Pðæ¾¯Áÿø,?ìÛûS]~ÄÏŽ¿o¯‰ oá©u+K½Êá§‹O2´rÍkh«lÀJ‘Fgl¸9ãŸÛoŸ­>8|ð§ŒtM¢×ÄúM¶¦Š?埛¹N{©b§ÜWÐå™·öƒ”gFTä¶SVmi¯mÝš»kKÚèü›ŒøýS…Ø|}])+Jt&¥T¼ýÍm;Z7ŒÜc ûÊN¶íâ/øCöCø««|Žúoé~Õîü6–VBúåõ(ì¦{Q ±GÈfXöÆQƒœ.Óœö8ñ‹|_û!ü+Õ¾?G}ŽõOé~$KÛ!crš”–P½ÐšØ"$4›£¡WhÆûcéúž¯û!üU´ð_Šl| ¬Ýx?W†ÃÄ—º“é–Þ¸k)„wÓ^'Ím.VV™yŒ!aÈ£ö8Óõ=#öCøWiãOØøçYµð~‘ ÿ‰,µ'Ôí¼Ap¶P‰/¡¼šæ9œ4«3s pÇ“^¹ð!ûcé×ÿd?жµKíÀ—¾ÕíüI¨Ù!{?M{)–êxTG!2$&FP#’£äoºOØãHð–û!ü+°øª_kž²ð~‘oá½Fõ \êjYB¶³Ì¦8È‘á³d?"ýÑGöï×ôO þÃßuO‰Zü%~Ó| ­Ýjº'Û¤±þÙ´M>vš×í1‚ðy±†Í@Y7nGì!¯èž*ý‡¾ jŸ tøE<9©xDºÒ´O·I}ýhú| ¯Údçò£+šà3íÜy&€=ZŠ( Š( Š( ”ÿàªð­?ã?á¤ÿá:ÿ’çáŸøEáû/ü‡¿Ò¾Çý¡öŸùqÿ[æù_½û»{×Õ•òŸüOãü)øgø£ü âÿøKþ9øgÃ?ñSi_ÚØŸiûWüL4ÿ~Ï}—û©þm›ßå9¯«(¯Ìï·ü/ïø8ËYºù¦²øc¢8‹ŽË(àlúq¨Ip>•úc_™ÿðC¢ßÿk_ÚGâÄÁ¤ƒXÕ¼‹)Ë—W ŠGeHí½°Ãòùÿïñx /zœÿ*qoóhý¯Â¿øKȸ§;òï¨/ñbªFžž|ª_+Ÿ¥ZŽŸ¯a=¦© W6·Q´SE"†IQ†YOH ×垃ÿ;“ÂðS*[{'»ø%IâˆD™x¡ttÆ™&zŸ4ÆF~ü*Ü’­Õ*+ÒÍ2L.pééÕáŸðgïü£OÇöSoÿôÕ¤ÒêWCõfŠ(¦HQEQEQEQEQEQEQEøgÿ<ÿ•–kÿûœÿõ*²¯ÜÊü3ÿ‚ÊË?µÿýÎú•YWîe(…QLAEP†~Ü_~%üÿ…?ÿ ÙàÿøKÿá/ø›¢øgÅ_ñ*ºÔ?±4Ÿ;횇ú3¯Ùü¯./ß˺$ßó)ȯs¯›ÿà£_þ-üBÿ…ÿ ›q®Ûÿaü`ðþ¯ã/ìÍmt¿3Ãq}£íÉqºhþÓÝëa¼ÉƱÇÒQEQEW†ÁIþ|Ký¤b¿x/ö?ñ‡ü _uŸ°ÿdk¿Ú·Z_Ø|«ûy¦ÿJ´Gš=ðE4"Ûöœ)${|§ÿºð‚¾'Á0>'h´G¿áWø:ûû+ûCÄߨsëÙ›5k'‹ý Ý„²ù’¤Q|¤mówÐú‚ÛÃöZÕÞ¥gei¡q\Ý$J³\${¼µw,Ì“h'펦®QE$’Ø©NSw“¾ËîÑ}ËDQE2BŠ( ¸oÚƒþ_øfŸˆŸðÍŸòQáÔ¿áÿQÿ!_²Ëö?øùýÏúÿ+ýoîÿ½ò滚òŸÛ¿Àÿð³¿aþØÐ¼=ÿ u½3ûW[»û&™¦yÚ|ñý¢î|*÷ïy0v¢±ÁÆ(óËö,7пයOü57ü,MkáΓÿ Snâh<;§ý´·Ù’?Ò ¹ºù:mùH5ú=û_ê±h_²gÅëíÆ?êÓÉ´e¶­œ¬p=p+óö.ðk|ÿ‚´~ÏþºÕ´/¾hÚT:¶…yö½3PþÙö›iö¯›Ÿdfð7#£`gôþ ¨.™û |d’E,Áz¼X¯g*øÍ|nO/g…Ì­Ò­oÉ?Ôþ†ãú_Zθ=5ñàp ÿ¶ÛÔùïþ ï±–Óþ ô’\.Ôºñ%ü±ƒ¹@…3íó#}+î:øÏþ'a%Ÿü‡Ã²M·mÞ«¨Ê˜<à\2sø¡¯³+Öá˜òå8UýÈþGÂøÍWÛqÞw/úˆª¾é5úQ^áù™ÈÞ|ðF¡ñ>ÏÆ×žðëø¾Á]`Ö³âц]­ûà»ÊHäœqŒšë¨¢¢áNü©+êüßs£‹¯ŠPUê9r®XÝ·Ëwe}•ÛvZjŠ(«9Š( ÿàà]wBðßü ã-çÄ7Š4µµÓ¢ûß½‰3ɪYÇÞr)aåLñLSqB@bFüûñÄüOý…Óú„ÆKïê³é»Xå¾Ï)ûDMô̲ ö‹«Õ?à®>!ñ7…àž¿oþü-о4øŽìï²x7Yðܾ#²Ös©Z«ùšt$<þTeç?#B®xC_Ÿžý¦“þ ûtþÓÍ«Éc®Y¾¡áë=‡ÊšòI{8òÒ;Ùƒ6G0àWÉçØÈå8ì.:¬­Kß„·¶©J:m¼}^‘û§…ü?SŽøo<áœ5Qѵf_ý¨"ü:×í%ð²Á$÷w7!:o–»¥K•'÷Lƒ–Œa*A?›~1ñWÄø.Ïí9ªøcán³ªxKöð|Š·W"2P!Ž$tãÍž\‘¹Û(b¿Í̳ÊxJtþ®½­J¿b×½Ö÷ÙE-Ùö<áž/>Åc?µª}O ƒ×V¤_îõ²‚†ŽUdôŒ4mü“ú‡Fÿ‚èüÕ¾=\x-õËë]:6Ãâiíöéd‚¡ó½+ Œòr ß_éZµ®½¦[Þèw6÷¶wq¬°O‚H¦B2I äÅ|ÅsÿkýŸî~Càð\ ¸.šê¾5¿<€ Æï‰$åbà€ÇZ·‡~7Áütš‡…î/~%üÔn@žÞBÊ, 1á€Ü,æ%¸‘G•) 0Ý€¾_ö¦i“þó4§R{ÊýËÿ4^®+ù–½Ï´\Á^ ·ƒàœUZ8è+BŽ)Å,MºÓ¨Ÿ,*Ëg-v‹I6}ïûqÂêÿ‹?ÿ _ÿE7Eÿ„ïþñî‘ãxõmL-›[_[Åp°ÙA†\¸Î~á1€¥Á$}Ïÿñ•ߎ`ØïüEyy¨ê’x“RkÛ«© ²ÜLî²3³±%‰ó$óœ×£…â<&;&\÷ŒŸ2ÛKh»ïÓc䳟³ÞáêÙîsMÐq©Nš¥%ïµQMóI^ðK’É5y^ú%¯Ñ_µÏÄqð‡öYø‹â}ʲh~¿»„1ÀyVÝüµÏûO´~5ò¯üÉðßþØFãZ™G›âß]Þ+÷1D±Û*þ§þ]ü;âø þ Åâø-$h§ñÝŽ’Œ<5ÂK öÌpȹ÷õÅz_üCá×ü*¿Øá>bÈ|;o,a6”’è§{tíŸ|×4ÿÚx‚ ¥*Müç+~Q=|?ü$xS^kIã1ч­<='/ý.¢üOr¢Š+êÅŠ( Š( Š( †àäïùB·Æûÿé÷N¯ ÿƒ?å~8ÿ²›ÿ¦­&½ÏþNÿ”+|hÿ¸þŸtêðÏø3÷þQ§ãû)·ÿújÒiu+¡ú³ES$(¢Š+â‚,þ%øXð÷ˆ%¿‚Ç\³–ÆâK¹-.R9£§ˆ‡‰À'¤y5±E)EI4öcŒœZksñãÅŸ¾ þË¿´OÅ~Ý^?ý¦üa%üð¯¾Áâoêz–šm!̶÷6Þwwö“p­ §åÂa1’~ýÿ‚Oè(ð¿ìðþËã·‰mu˜¢»`½ðÄ«‹útñÊañtïnòkM ,M²$Ga?•Gú¾yɪÃÉË ¥ÝCî³³{ûÍ|Z­o¦–SˆJ5¹{9}ýRÛÝí£ÒÚõ~™ERQEQEQEøgÿ<ÿ•–kÿûœÿõ*²¯ÜÊü3ÿ‚ÊË?µÿýÎú•YWîe(…QLAEPÊðU?x+ÇŸðÎðº<}ÿö7Ç? êšüHçÕ?á#ÕbûWÙô¯Ü°û/Ÿ¹ÿÒdÌqìùȯ«+å?ø*Ÿü+OøÇøi?øN¿ä¹øgþ_øF~Ëÿ!ïô¯±ÿh}§þ\Öù¾Wï~îÞõõeQEQEóüÇÅ¿ < ÿõøƒªþÝ×|gð®×û;ûoFÑ¥h¯o7jV«oå²Ü[‘¶å­ÜâdùQ¾÷Ýo¤+å?ø-×Çøfÿø&ÄïÂàoÿceÄ‹Æ:Wö¦‰}æêÖPÿ¤ÚïO3g›æ'Ì6ȈÜã}YEPEPEP^oûcé×ÿd?жµKíÀ—¾ÕíüI¨Ù!{?M{)–êxTG!2$&FP#’£äoº}"¼§öï×ôO þÃßuO‰Zü%~Ó| ­Ýjº'Û¤±þÙ´M>vš×í1‚ðy±†Í@Y7n@œ_ ´Ÿ |?ÿ‚¾~ËPüÔï|Aà3ðÃI·ðΡx wWÚgöݽ¬óŠ2$h•$aåÇÉÆÔè?@?চ‰ÒÿàŸß¥T»Ã7pàœczlÏá»?…~xø;_Ñîॱ>»ðÏ@_ xwÄ ¼9u¥hFúKïìK9¬¯#ŠÓí2÷T{WÍpö䌓_{ÁZõ4¿ø'/Åym¶–m!a;†Fמ$oÇ káðröxlßÊuþS‰ý-ľ·œðß› „‡Ý‰¨­øœŸüÏOk/ø&_€$f .çÕ%•Ê`ÿßþ5õ½|ÁÿfÑÆ‡ÿÏø[ Hdk{q’1ƒ.¡s&? øü+éúú,‚<™f?ù÷ý%’ø§WÛñ¦wQ=+oOk;~EW¬|QEQEQEóüÇáßÅ¿ŠÿðO_ˆ:ì/q®ÚüT¿þÎþÄ—FÖ×E½]𕬗]ãM‹ý.Ì‹¹K/;°|Wþ %ÿÄ›öÃÿ‚„ü)×äµ›þ2X¼YqUR)–Hã,:I?Ú¼¡ƒ±³¸kÚ¿à®?³÷†j_ø'¯Ä|`ø¡|%ðæ»ýö¿ë"#e¥y:•¬éæ §?y$IÌ«óJ¸ÉŸ¤+ÎÌòʵC¯ÅýÎÿŽÏÉŸYÁœg™p&a,Ï*Ÿ-WN¥;öU ãuçÔãýè«ÝÁÀ_WÇðK¯ÜøWI‚öëáùƒY°±X7#¢+[¼^Zã)å\9)Ü'^·ÿ›ø«Æ_ø&ÏÁmj+;]:T𥖛sgmn-â´žÒ1k,iÿTªð0 Ø`+µý¸|¯|Tý‹¾.xgáUšê>*ñƒ5?D¶g‰÷ÒÙL–˾b#Rehþg!Aäkãø6¿â^¯7ì¿â߇?wÅâojñËwo&ÝÖææ-²Gò|¼\[\“·Œ¿¦+†¤–5§¢J´Z󿆫M­Ë=§ÂS©Ä< ŠMÍËZ56N ž!¨5Í~noh¢Òµ¬äïsì¯Û¿AÑŒ|[â.:Ë0¹ViU8RÖM+J´ÒåŒë5ñÎ0J ½l®îÛaUuÍ ËÄú5ÖâKKmCO¾‰¡¸¶¸‰eŠxØa‘Ñ 8 Õª+Üi5f~iÊR‹³[3ó›þ 'ÿçý–þ~Ï~¶ñY¾øwyªüE±¿ð­Îœ’ÜË«øAyý¦O!·¸h옼¹_Ý*ìȧ–é?àÝ-`j°n­ ÆPéÞ0½·$œï&ÚÎLû­Çá]gü‡Å~ ðçÃ_‚6< ÿ Ïü$Ÿ4-Bí©ôÏøFµYâ¼ú¶!í~FýLG&ÿ˜Œ ó¯ø6ÛPi?c¯Ú•]øÊiAîKØÙ‚?òüÍ|jðyΨǒª²V_eì~í—f˜Î ð9…iU¬±99NNRwU`¯&Ûz+.ȳÿ!ܬ±…"ó¼ž8µ;7`²‹ üœw•üÅ}»ðBÙ,¾ xB#X£‹D²EE]¡vÒ¾ ÿƒ”/cOÙkÀvîØš_yЏ<ªÚNç§×ó¯Ð¿ [½§…´Ø®Q’H­bGV*BA­rÿ{<ƾѤ¿ 3‡Š¿uá¯Gù«ãe÷:HТŠ+êÅBŠ( Š( Š( †àäïùB·Æûÿé÷N¯ ÿƒ?å~8ÿ²›ÿ¦­&½ÏþNÿ”+|hÿ¸þŸtêðÏø3÷þQ§ãû)·ÿújÒiu+¡ú³ES$(¢Š(¢ŠüÊø÷®ø_ãí“ñNý«hégk‡·¶ÐøgEµºÑ´‘«X5”3jÜɨÛÈÚ‚¼ÒKÄ`¨ y$Ÿ®à˜uïÿ°ç¼Kñ+Q©_EuÝ[iãO‡V·Šîh­ïÒØ"0ÇÆÐýï WÉ´¦½ð÷Ãÿµ¿Æ˜¿à©ÿ® ×áHøjÖ¿Ö4›=*KHs6“%•¬ÂÛU_iÝ+”‘w´í"¾Àÿ‚kÜøúïö'ð3þÒ«¬/Š…¼è¶!Xu6³‹&½@ rm¹‘prÙç4a¿Ý—¤>ûJ÷í=¹’Ñ6Ö©+x+[xétý{Äö:mÓBÄ…E<ªÅ V±‚TúRº²}ôùöÚùýç¥Ñ\Œj†õéß>#ø BÔ5øûÁ~¾×_ËÓmõ½rÛO—Ql…ÛM"™NYFHõª³ÛÎß>Þ¾BZíÚÿ.þ‡iEqŸ?h³áyøóÁž þÚ“ÉÓÿ·u»m;íïÇËŸ"ùÈáryØÁ:]B’Û:ÉŠNUäGQIj®Gá¯üóþVYý¯ÿîsÿԪʿs+ðÏþyÿ+,þ×ÿ÷9ÿêUe_¹”¢6QE1Q@)ÿÁTþ8ÿÂÿ†pÿŠ?ÀÞ/ÿ„¿ãŸ†|3ÿ6•ý¡ý‰öŸµÄÃOù×ì÷ÑyºŸæÙ½þSšú²¼3öâÿ…Õÿþ¿þŠn‹ÿ ßüxÈ©ûïíøüÿ¶ê?ýÎõîtQEQEW†ÁIÿáuÃøÓþßÿ%‹ýþÿøðÿŸû´ÿÈCýþ=~Óþ³þóm¯s¯”ÿà·_¿á¤à˜¼ÿ ‡¼ý³ý•ÿßê¿Ùz%•«YMþ“u±ü½þW–Ÿ)Ý#¢ñœ€õeQ@Q@Q@pßµŽ1ð÷†u-OBÒ¾É-ßöü6²ÉooäBD²ù’¢'—Û°¤ w5Ã~Ôñ7ÄïÙ§â'†¾ ëðxÇÄ>Ô´Í Uû\¶ŸÙ—óZ˽ǟ2ÅåÊèþd`ºíÊ‚@ù­ñ§Æ~(ñ—ü»ö*ñÏÅý%tø·Áz ºþŽÖsZ *ögîaòfc,E$»•<¹ d1€Äœ×ןðZÍFm/þ ñB[òÝ¢Ó¡'åS´Fú«0üx¯¿kox£à§íŸû韵¯øH|o¤i¾ðÿˆ56º–ñu+È®­ º¸Ì«,¦I$™üÉfÈf’+êoø.¥ÃÃÿÐñÂÄì«-Ö˜ŽûÃíðÅAü+àg/gC;Šþóûé#úž…?­æ~Tz§ì£ÿ€ce§èw?ðJM:=/þ Ùðš+mÛ[DYŽã“¹äwoÕ} ^#ÿسŽÃöø@–‹±[¶2’~f‰Y>¥‰ük۫벘òàpëû‘ÿÒQøÕöÜKšTþú’ (¢½åŠ( Š( Š( ”ÿà·_ð­?áØ¿á°á:ÿ…uÿ¯íøC¾Ëý·ÿ!k/'ìßkýÏúÿ'~ÿùg¿6+êÊù¿þ ãû@øgöZÿ‚züAñ߯‡:ůh_Ùßkð¦²bZ¯©ZÀžaš Ó÷rJ“ Äß4KŒ0úB€<ßöÇø¦|Yýþ*øWÆž#±ðvâoêúUþ¿zÛhvóÙM—“o’5òáGis Â²ŽGægüKÀìÿ8? |%â{?øWű]]µ*-y>ÏwG››6…áïøH| ¢jÙZ%§Ù4Í3ÎÓà“ìödùPG¿bG“µFN3[¿µü&¿ðÍ??á›?ä¢ÿÂ3©Â+þ£þB¿e—ìñóûŸõþWúßÝÿ{åͳü&¿ðÍ?ÿá¤ÿä¢ÿÂ3¦ÿÂUþ£þB¿e‹íŸñíûŸõþoú¯ÝÿwåÅ}Iø‘Øk:´‘u}ª8ŽÚÊžg?ÀŠ¥˜þüÝÿƒ{´›Ÿˆº¿Ç/‹ú´ø¿_HõÙ!in§ï“wýó_YÿÁO~'„?°Å]cÍ0Ë&….™ ©ù–K²-pÓƒíŒ×žÁ>†ðNÏ4B+¯ÜÝ듌}ï2cl}s šù|oûV{…¥Òœ'7ÿoZ õ?láßøEðË;Ç?‹ˆÃá£éMJ¼þOÜO亟]ÑEõâaEP‚þÞ¿~$|&Ó~ZþΞoÇã‰G…&è¾&ñWüMn´ÿí½ÛÎûfŸþŒö7Ì‹÷í‰ö|Ì0+ä¿ø7VÝt­ãva¹,¬|ElE¸°ŒmxÏ9Â(Éç_1™éœåïʪÿÉbÿCöž j~ñ\I`d¿ðmH¿Ÿ¼¿Ÿðr8mWáOÂÅï5/\y<€¤ˆU0Iÿƒ“¿å ß?îÿ§Ý:¼3þ ýÿ”iøãþÊmÿþš´š÷?ø9;þP­ñ£þàú}Ó«Ã?àÏßùFŸŽ?ì¦ßÿé«I¥Ô®‡êÍQL¢Š(¢Š(òþ ­Üø þ m«þÖoñ™l&ñ´vÚ4šZÄú0ð„º ðÊ–k§‚‚÷ûBCæà˜™A‘úÿÜÿ„÷þ³ÁðÒ‡[>)ò'çZÿ©²ûL¿bûwý=}“ìþn~mû·|Ù¯?jŠ?üCû[|Cø}âŸükðŽ©x–×P¹Õ<9guo§hÓ´³wqu§ÞÛÂH¾¹¼2[¾ÆiŽÕ@ ØðLŸøçÅÿ±‚uÚ!µé|G:]l›\·û>©ub.æ[¯#•í»¾@;˜’2MM0¶ÿ ùr­üîŸ7zЦƒÅë^/ɯÆOO-ðe®¨÷ª(¢œðSO€:l²Wí?­~ÌçÂ%ñãÞüMÔµ/ÁqªxItû{_ôM:Ö]5½¼o,p\ÜÀÑHáÃ>ð¢þ¬|Cð—Œþ5þвWŽþ ë¾_ø~îM[Žæÿ\6öcÝ‹kù º†=&ycº•Õ.AóŠ(wÓß¿à›¿¾0øß]×üyá[Ù®¼VðI¯ÚZx‡S°ÒüDð€#mGM¶¹KKæTfx¤$( Nø­ÿàø3ñ£Æ†¹ã¿Jo5«[{^;ZÔ4»-zÞ aÔlìî#·¿D*­Är¿.6ñYÁJ4ùS³²IïÊÔm¶Ò]“¶›·}5”¡)§%xÝÝ2rŒ¾OMו’Üø‹àŸŽ¼1ûc|6ý¹~#|]Ó’Ù|SàÝ*x Ô‘MÆ“¢KáˆïmãW#„Í;‚ "ê"ÿ‚|Ø/Æ”ý oÿl4MCY<§Nú¬C͇JŸÃÒ\]f#݆r:¼|ò£sübÿ‚vüøõâäÖþ&ø9no~Ão¥ÜÇgª^éÖš­œ !µ¿µµž8/ †V+”• *Ž¿°ÂOÚGÄÐkü+%Þ¡š4Y^ÃW¾Ò—PÓÉ•ävsÄ—–¡Æ|‰Ä‘ò~^Nj´#RiÁYN* ¯*Œf¹—yK›ÞÛyjî*59S”ÝÜ[“ónTåo%KGþÝÑZÇÁŸðH»føÑã½nÚÉT¼µýžü!ccý­fþȺ†õ®ÝKVFŽ/1º’«»¥}sÿ_Õõ]sþ mðVãÆ3\Ü]Â<±C,ç.ö©,‰lIî>ΰà÷5Þ|pý>~ÑšeÇÄï ¹ŸIÒßB‚M'V¾ÑYôÖÆë „ðùö‡hÍ´»âÿf½SÃ>Ó¼áË Â66šf•¥[ÇiggkŬ1¨TŽ4Pª¨ÀºjVU%VV·3½¼¹ªËï~ÓïæîsB›‚‚ì’û¡Ny>O»—±üÊü=ýª~8þÈ_ð\?ÚÄ¿°ÃøZ1¾ñ7‹4ËÍ+þýC[û=ƒëë#Üy$«¶XmÓÌbPy¸#,¤Gÿ³Ž9øgÄßñSj¿ÙÿÛfûWüKôÿ‘¾Ñ}/™û¨>]û毫+æÿø(ׄ¾x³þGü5—Š5ß f|`ðýÿƒ³"i?µ¼IÚ>Ãeq¶Þm°I™·1ò€Ú3*wúB€ (¢€ (¢€ ùOþ uÿ Óþñ;þþ¯øW_ñ*þ×ÿ„;ì¿Ûò²ò~Íö¿Üÿ¯òwïÿ–{ñób¾¬¯”ÿà·^=ðWÃø&Äísöˆðü-Xÿehxgûr}ûO~­d‘¦Û©–/.WŠ_”ÞVÓÃ#êÊ(¢€ (¢€ (¢€ òŸÛ¿AÑñ7Š¿àž¿l>üRо xŽìï²xËYñ$¾²Ñ±©Z³ùšŒ ¼laà;L¨xs_HP›þØÿ4τ߲Å_xÓÖ>1Ѽ3àý_U¿Ð/J mrÞ )¥’ÎmñȾ\ÈnGs•aÁøKþ Ÿ¥iŸ´‡ü_á/įxfÇÁúmŽ›áïYè–! ¶…c}eKeHÑ|¸Õ¼kµb%¨ÀvþØþ"ño„?d?Šº·Àï¦ñÞ—àý^ïÃied/®_RŽÊgµÛq<†ele9Âí9ÁòýGÁ>.ý¨à‘V:WÇ›{áñÅŸ í.5˜¯¬E•Êkm§G4‚keD¸»tAiB®0<¬óõü¾¾o(»zÛOÆÇÜxiŸÿªüW•æÚ4ëAËüIOÿ%l‡Å¾"ÿ†Åÿ‚:ø’ê]kBÐ.<ðŸPµºÕu«ß³iš]Äú\°Í5ÝÆ—R—i$Áڈ͎1^‘ûxþì=ðkÃ_Ûˆáð.‰¦jè—kÓ5?'O‚?´ZOæÁ&Íé&äe8Å|Uÿ¾ø…á‹ßðDÏŠý¡uCEðg…ô¯h¾#¿´R÷z~‘=œ—ÜBr’ÉÍÁP#sº,lnÜøÏÿFømû~ÆŸ ~~ÄZ•çÄ]~hú„ ¨^x,Ʊ»¾A7Ÿ$&¿–ŽÅ†R0@®<.yBžSG0ÄÎÉÅ_»•µIuw¾‡Ðgžf˜Î:Çð®OAÎq­QEl£OšñœžÑŠƒ‹mè¼ÛGÿKøµ®~ßßµ÷†?e¯Ùê鿳tûÔºñeôGtQÈ 3Ç-£%Šçæ•Õ8dúEð³á¾—ðwᧇü'à¨Þ#ÃZ|eš;nq1¬k¹¿‰° “Üä×É?ðF¿ØUý–þêÞ8øõm8ø¡ñFšÿín%¹Óíw—X]òO™#æY9äù`ò†¾Ô¬ø Z^Ó2Å«U«Óù ¾ýÚ¿7®§_ŠùöŠÂð~CQOM9­«â%üj×[¦ýÚz´ ´vh(¢ŠúSñÀ¢Š(å?ø*Ÿ€¼ãÏøgø]>ÿ„ û㟆uM þ$sêŸð‘ê±}«ìúWîX}—ÏÜÿé2f8ö|ÀäWÏßð@ß.Óã×íEko²$^±ò¢\(U ùW°(ãÚ¾ÿ‚x·á'„ÿáDÃYx_]ñ?öŸÆØx7û2Vû'Ä’}£ì7·n!Ýx›rŸ4Ã1?o™à‹’‹?ÛÏö¯´»ÝÉñ·–ÊC šðlŽØ,£õòùÆ™®]/ïT_}6~Õáÿï8‹éÓ¼$¿ðLÏä'ü%\ý®¿e]*FÅ&¿(,¦KÝ5I÷áZý+¯ÍOø, [þ Wû&é÷¬ÆÖM~Ìc~«h¬A÷ ¿•~•ÓɵÌóz ÄOÝðgÑíG/ü 7úQ_N~*QEQEQE~SÁÙ¶‡ü)دJø?ÿ×öŸü/;þ&ÿÚOö'öMþ—wÿþS}£Íó6}øöc??J?àÏßùFŸŽ?ì¦ßÿé«I¯sÿƒ“¿å ß?îÿ§Ý:¼3þ ýÿ”iøãþÊmÿþš´š]Jè~¬ÑEÉ (¢€ (¢€>IÖ?à´_>~Ñ>þÒþ.ðÿí{À:Œ°hêjkó[E:Î…cÄL Œ ÃhnŒ+郿¼-ûA|7Ó<_ðW\°ñ'†u•w²ÔlŸ|!£b§¾~ ×åßíÁûZjÞÿ‚‡K¥|oøÝ7Áý&ÏÆ‘xrÛB͹ðôúóo¼·1tÿnÄjK£h–2…˜gïoø&¯Åüný‰üâoŒ1¡ÖµníicöÕ­£¹–;kñný¦†ã`7€ÞáÕGºå_|ovµ³z]]YÝ%£Qx…ì«r-ßâÕºh¬õ³ºåw÷“—ºQE(¢Š(¢Š(¢Šü3ÿ‚ÊË?µÿýÎú•YWîe~ÿÁ?åeŸÚÿþç?ýJ¬«÷2”FŠ(¦ ¢Š(å?ø*Ÿ|à?øgø]ÿ„÷ûg㟆t½ þ'“éðŽj²ý«ìú¯îTý«ÈÚÿèÒb97üÄ`WՕ៷lj?áOÿÃ6x?þÿøKþ&è¾ñWüJ®µìMçÎûf¡þŒëö+Ë‹÷òî‰7üÊr+Ü袊(¢Š(¯›ÿà®?>-ü(ÿ‚züA×ÿa{}vë⥇öwö$Z6ˆºÕëoÔ­c¸òìÚD¿èÏpNcmª¸Û‘ô…xgüŸàwÄ¿ÚGö+ñ§‚ÿcÿÂñYûöF»ý«u¥ý‡Ê¿·šoô«Dy£ßSGò)Ý¿iÂ’@÷:(¢€ (¢€ (¢€ òŸÛ¿Çð¬a%þÇмCÿ÷u½Oû+[´û^™©ù:|ò}žî 6 6lxò7#0ÈÎkÕ«†ý¨?á5ÿ†iø‰ÿ Ùÿ%þKþ_õòû,¿cÿŸÜÿ¯ò¿Öþïûß.hòïþ Mãø‘û~Ã>4ƒGÑ<5y«èúv¡ †…ll´ý \Xé“{(C&!4ÉØ¨£øø9Û|"ð¬w(ÑÉh®Œ0ÊD( ô5ÒU ÚÉaá: µ),6±Fê…‚GçW«ôšK–^Gñþ2§¶ÄT©ÞMýì(¢Š³˜(¢Š(¢Š(¢Šù¿þ ãá/„ž:ÿ‚züAÒ¿nk¾ øWuýý·¬èÑ4·¶{u+V·òÕmî Ýr¶èq ü®ßwï/ÒòŸüëǾ øcÿÀø®~Ñÿ…¡àëì¯í ÿnO¢iïÕ¬’/ôÛu2ÅåÊñKòƒ»ÊÚxc_VP›þØú~§«þÈm<â›k7^Õá°ñ%î¤úe·‡îÊaô׉ó[G ••¦^cXr(ýŽ4ýOHýþÚxÓÅ6>9Öm|¤CâK-Iõ;o\-”"Kèo湎g *ÌÜÈ1äÕÛ¿AÑaŒàí=+ÜÿàˆÿðLŸ‡ß¿g‡§‡û{Åž2ð®™­Ø=Ü_ºðä7VqN-íÔ³n‘D Ã|Ì€™OØÿµŽ1ð÷†u-OBÒ¾É-ßöü6²ÉooäBD²ù’¢'—Û°¤ ³ŽSÿ‚©üqÿ…!ÿ áÿ¼_ÿ Ç? øgþ*m+ûCûí?jÿ‰†Ÿó¯Ùï¢òÿu?ͳ{ü§5àðJ2ÚGüö®°Ô#xî.u}BíyÚ²°9Ïq2õ¯®?n/ø]_ñgÿá‹ÿè¦è¿ðÿLJüŠŸ¾þÑÿÏûaþ£÷ÿÜï_%Á='mþ ƒûIiwK™o,¯/•ÔåUEí¡û‘p¿‘¯˜Ï}Ün_?úx×ß ´øcûÞ⺠®ó÷+Ó®¡ÿ/Š=Kþ %û/Û^þö(¤²cc®5e`;Ƨ?ìJý(¯Í?Ût¥÷üóà qm™¡Ðl÷¨¶sª?#¶ ú×ée½ŠÌ%ÿOm÷B!â¢öY Ñí‚æÿÀ«ÕÓêQE}9ø°QEQEQEðÏüÿ(VøÑÿp?ý>éÕáŸðgïü£OÇöSoÿôդ׹ÿÁÉßò…o÷ÿÓî^ÿ~ÿÊ4üqÿe6ÿÿMZM.¥t?Vh¢Šd…Q@Q@“Ÿµ—Å„?m‹ºüö–Ôþ]ü,Ôí­ü¡h÷Ö6°Chö6óý¶FšÖcsrÓI(hIB(Ù†S_SÁ!¿à¡¾ý²g?išßÄo øÃân›gs.§oa,Q_MgÛÁo{qiü{¼°ý™Ý0¼¸Àè4¿hø(Á¯†,ñ¥ž·à¯ø×ÄÖ4ÏOáxïgÖ5‹èÌé¶rÊQ'¹HBË"ï4eËdà{ìµñÀ_¾i?ø ¦[YizüN£v˜¶7QáŸì#­¿´ÆŸýsö¶y?»óü¯/ÌòþMùÛÆ*ÇüZôkÞý4‹‰£ÖfGprŒ&˜ŠF:ƒçç9íï_˜q•3Hÿ4(~2±ý§á ç…àš«þ]×ÌŸÝENÿ+mÔýQ¢Š+ôóø°(¢Š(¢Š(¢Š(¢Šù¿þ ãâxWþ ëñÿàÿÂÝ ãOˆàþÎû'ƒuŸ Ëâ;-g:•ª¿™§BCÏåF^qƒò4*ç„5ô…|ßÿqøwñoâ¿ü×âû Ük¶¿/ÿ³¿±%ѵµÑoWf¥k%Ç—xÓD"ÿFK€s"îRËÎì¤(ÊnýDð¯ì=ñ—Tø• ÂWáÍ7ÀºÝÖ«¢}ºKí›DÓçi­~Ó/›hüÔ“váÈ~Âþ‰â¯Ø{àÖ©ð×@ÿ„SÚ—tK­+Dût—ߨ֧ÀÐÚý¦@*2±ù®>ÝÇ’j÷í¨êzGì‡ñVïÁ~±ñγkàý^k Þ鯩Ûx‚ál¦1ØÍfŸ5Ìs8Xšæ@åG&ØãQÔõÙá]ß<-càmfëÁúD×þ²Ó_L¶ðýÃYBd±†ÍþkhárÑ,-ÌažE_ý¨<âo‰ß³OÄO |Ö?áñˆ|3©iš«ö¹m?³/æµ–;{>e‹Ë•ÑüÈÁuÛ•€(ý˜<âo†?³Oÿ |hÖ?á!ñ‡¼3¦éšî«ö¹nÿ´ïᵊ;‹>`%—Ì•üÉvÝ–’+ öïÐtO~Ãßt¿‰Zÿü"žÔ¼ ­ÚêºßØd¾þÆ´}>ušëìÑóùQ–“ÊBöí‘Gì! èžý‡¾ i uÿøJü9¦øDµÒµ¿°Écý³hš| ×Ù¤%àóc '”ä²nÚy€=ZŠ( Š( Š( ”ÿિáwÿÃ8ÅaàoÂ!ñÏÃ>&ÿŠ›UþÏþÛû7Ú¿â_§üö‹é|ÏÝAòïØÿ0ÅxìˆÃßðpwÇ+l¬¿nðýÆ[¦Ðí¦Mú` ÷ÿø*Ÿü+OøÇøi?øN¿ä¹øgþ_øF~Ëÿ!ïô¯±ÿh}§þ\Öù¾Wï~îÞõàh?ðqçÄë{F,—šù›†NNÓ¦ À€ü|¿ûµpÿ§Ñ_|d~Õáï0ÿƒ“¿å ß?îÿ§Ý:¼3þ ýÿ”iøãþÊmÿþš´š÷?ø9;þP­ñ£þàú}Ó«Ã?àÏßùFŸŽ?ì¦ßÿé«I¥Ô®‡êÍQL¢Š(¨¯ï¢Ó,f¹¿qñ´²9芣$Ÿ ¥¢”¯gmÆ­}v?µ/Û ö2ø•ûZxÏâûJøƒÁ3iºÜú¾§]YC>‰'ˆ¥Ò#²_Y!ä—˅¦*¾t,vmå¾øÿ‚RÜø&ûöðEÏìöþ-¹ðµÙ¾¸·¾ñ, §«J÷Ó´÷²ª¸¸˜Ë2•À)"ð:ÿhkâ ¯í“ñgÁfïâ¿Ãí7Æ>/´×uÍoÂZLÖ6V> Ótuš{ûmBÒܼšÍÊ}‘˜<“íŠ(ÕBªƒö¿ü'ľ9ñìGàCöˆmz_Η[&×-þϪ]X‹¹–ÆkÈð¥g{Anïæ$Œ“W‡³Ã&»COûwôwŽêÎ6´KuZÏ]_æÿý«ÿ}Jÿ¼mûÕQREPEPEPáŸüóþVYý¯ÿîsÿԪʿs+ðÏþyÿ+,þ×ÿ÷9ÿêUe_¹”¢6Q^;ûrþØÚ?ìAð&oøƒL¿ñ¯¨_A¢xsÃÚ~>Ûâ=Vå¶[YÁœà±™°v¢;aˆ Xb¢¿1¿hÍCÅß <%cã_ø,'í“®ü “ÄR4š_‚~$6ŸÙ«ÆbI…­Íæ¢Sry’ö)<kµñ×Ä_ðJË /|AñÞ«ûF~Îlbm~}OKŠø.Õñÿ–ÝQu dݺEt(#Uf¡{ÉÉl¾}½F×+Iîõ^‡ºÁF¼[ð“Âð¢?ᬼ/®øŸûOã‡ì<ý™+Gý“âI>ÑöÛ·î‚ø+ÿ ›þ ¿ñ×Yý­¾éRHº×ƒx~oüRø©`ø#ÂðÉåVô®æ–wÿ–V°'ï&”𫌑»#â/Œ~:ÿ…{âûÍ;þ [ûr|Vð‡Ä 8 »Ôto…~›Oðç…|õ S]G¦Üï ’æXÙ”äàRæWK«‹i´~¬W”þÝúþ‰á_Ø{ã.©ñ+@ÿ„¯Ãšou»­WDût–?Û6‰§ÎÓZý¦0^60Ñù¨ &íÃ+åhi_‹ðM}kº·íEãË?ŽŸ³OŒî-­-þ"›8m55ÉUµ“Rû0ÞYJÒF¿jP¬ å¿_ëoÛÄž,ðŸìƒñSWýŸÒúižÕ¯<6–6BúæMJ;)žÔCnQÄîf팣8]­œiÅÙî(µ$šzÿÁIµÅÿðAσ·Ã·…¼7}¦xVóMÑEì—ãD³“LÌ6¦æ@aÐO&¹ø(½Úx»Pý‚l­%Cgyia$S É"c¤.*‘õ5êðR½kÆ>5ÿ‚ø#Xøñ ž9¾Ñ¼/}ât¼±W0ê2Cºó­‚ ü÷pÑ„PŒvíÀñÚ*髿_Žÿè~ÃQEúqüZQEQEQ^)ûy~Ùö±'Áhuã¢ßø·Å~"Ô ð÷„¼3`@»ñ­q‘²Â/ÊÎòv¢1Á8RítWççįÙ×âäuߊßðSßÚÏÆ? t­&ݵ;½áz[éW†bÆj’Ú{AÃ0\‘¹Ø…@ß)8~ý©þ*~Ãß¼-ñsZø“{ûK~Ê-Msâ+ýû?Å~ ´œªE"Çn´ ٗ̉&@A A··K_Ê÷µýlíèû «Zýoo;ZÿuÕýWsÜÿà·^ðWÄïø&Äíöˆñ÷ü*ÿ_ehx›û}oû3f­dñ¡[°–_2TŠ/”¾nãšú²¾Kÿ‚ÓjŸ õ¿ø%wÄ[ßÚ¢]|.¼‹HŸP›Á/hú¼Ñ¶«dÖíh×_¸ Ì`,[¬eöüد­(æÿ¶?‡|[âÿÙ⮓ðKè¼wªx?W´ðÛÙ^‹”Ô¤²™-L7%ÐA ™£Û!upÛ†2Øãþ-ð‡ì‡ð¯Iøý%ôÞ;Òü¤Zx‘ïoEõËêQÙB—Fkî'̲n;9mÇ9'íðÿLø³û!üUð¯×¦j~NŸ<Ÿg»ƒ#Í‚M›<ÈÌ23š?aÂÎý‡¾ x—ûBð÷ü$>Ñ5?ì­Óìšf™çiðIö{H2|¨#ß±#ÉÚŠ£' V¢Š(¢Šò?Û{ö¿Ñb/€W¾4ñM…Þ»¨Mu‘ h6Gž#Õn[˵±·àüò?|6ÕWl¸ \¢¿/~>øÛÆ> ›ÃZ‡ü“ö©ñÿÁMsÇO{£x àöƒ4‰¥CÏ29¯à±½šæDó¢í²2Û‚nQšÚðgíCñWö?øOañ§Ã¼CûM~ËÆI^“_ðÓé6ð•ºHb’ú Ð@×ðDÊÍ*Ë8Q¹ÀX÷Ó”uKþóÐr\)i}|ÿ‚©ø÷Á^ÿ†pÿ…ÑàøO¶~9øgKпây>—ÿæ«/ھϪþåOÚ¼¯þ&#“ÌFx&›nºGü“¨6žZ3ªxt=Ï9¤ÆÐbúv¾‡ý»¾7|IƒÂ¿³®¹ûKªëZG~*øjßÄ—z.Žš´SxRé'{©åc Ÿg¶+öv7Ca@W.î~søŽÃþKðCˆ~Õ Iævù§û"ø Þ¿uà+æ8ŸEƒ—jôÿ¯Ôý£Á~\CO¾Y‹ÿÉT%ÿ¶ýãü/+ø‹þJñm±.ƒáäQŽLÁ´‹søÜã¾õúS_šŸÐë¿ðqÅ{‹Æ!ì¼?”Ú0[=:üþb¿Jèá}a‹—zõ;~å|†—òå¸5ópr‹¸QEôçâÁEPEPEPÃ?ðrwü¡[ãGýÀÿôû§W†ÁŸ¿ò?ÙM¿ÿÓV“^çÿ'ʾ4ÜÿOºuxgüûÿ(ÓñÇý”Ûÿý5i4º•ÐýY¢Š)’QEQE~AÿÁBu»ŸÁCmµÚÍþ3-„Þ6ŽÛF“@‹XŸF—AžRÍtðP^ÿhH|Â@œ!2?B¿à›ŸðžÿÃx#þPëgÅ>Düë_ò6_i—ì_nÿ§¯²}ŸÍÏÍ¿vï›5òíÃìõsûGxÆ?‹’þ×0x©Þ5»“ÁÃŇJÌí6‚Ð\Û€®íÙÍ}+ÿœÐüQáÏØÀ_,üSe­CæIJÍ&³-±½œÛKz'f‘'x .ñ“ò*€M0­‡^Ÿ ¾oK·ÕÊNÚèñoš¼_ªóÝ¿»[[¢ŒôWú.Š( Gæü·öEŸáßÂŒÿu/k¿üqâ9¢Ô4Zë|Ó¬ÒÜ+ÀæìÞmB.n]¬af”ïYUT9<+ûqj_µï¾1]Úx§Pøu iúw€õéY¼3 èjI©iûŽm&–âa1™6¹Ø£õwįø%Ï…> kŸ¿±üsñ3Â~øµr/ýœt+oø(ßÀ¯Úâwíu ¾¥ãxC·ðΡ>Eǃ¥ÿ„j Mï¬ ÚO%ÜæV™ ³yas´bºÙoÃ?ðS›ÿ‹>#ýµ,í5Í_ |#‡ÚíN|7q¡FëR´V?è÷MrêEÊmu*†~Èø¿ÿ³ðÅ?ë—Zˆ|wàm'Æ-Ÿ‡|U¡øfþÞ×NñFŸh¾\] mä–-°æ%´HÑ1BÄc~2ÿÁ3<ñWÅ7š§„üK㿇#[ðÜ×­<'}mkmâ & Â[…žÚfb<‘¬Öí Á$e`ñ¶&Õ•UMró$£ýÉ%4ç¦ÜîJö×w«JùaŸ±ö\îüº¿5zmG]ùT$•ôzmÍ+|UûèÖ¿ðTÏO/íÓiiâåð‡Á_ 6“¤¦D´»Ô¢»{­Z(Üá/ÛË•æÖ5öŸü'⎿ñ›þ ±ð‡Ä?¯gÔõ»­ Aq{9--è‚Y I,Α#–þ"Ä÷©>'ÿÁ3¼ã=WM½øcâ?ü+¹±ð¢xvð}íµ¸ÔtHóåYL.­çG¹öM—:om² ×µü)ø[¡|øg ø?ᆟ•áß ØC¦éÖ‘’ËoHsXà ’I'$’MoR¤f긫)JéÛÕ]þjqùÝm·Ï8ò'­¢—¥¡N-|Ü[ô³Ý´¿š‡¿ðSŸøuüö£ø‰ÿGü'¿Û>&ñg‡?³ÿ¶?²üŸ7_[;Íû<Û±ö=»6ŒïÎáŒèþ ±ûhÃÂÿb¿ü`ÿ„kþøKþÝÿíí²}šþâÓþ><¨·îû6ÿ¸¸ßŽq“ø÷ÿœøàÚþ4ý®4_Þ ð§Žtk[¯ÞÃaâ "ßS¶†á|Oj‹2Å:2¬$‘C†pM~í|5ø_ៃ> ²ð×Áÿè^ðæ›¿ìšVa•®ùGòà…UtŽîpY˜žI5ÍvnׯŸ¶5¼&ÿ‚»þÈz?5¥‡‹õ½6ÝÏî¯5h,íR2ãøgij֩g |^øS­'Š<¨]ƒöY.QJMcu·Ÿ³]BÏã¦U¹ TÐ8ÿƒ€ü-¦^ÿÁ)¾0j÷ºu„Úµ† ½µëÛ£\[Ç%ý¡‘#ʬQ @;W=}Kgá½/Æ?"ÒÕµ ôKÛõŠóGÖ ÎÑu+y{K´Á÷sF„€Aeܹ³_œß´ÿÄÏþзþðŸüóÆ??gˆ_ ®DÖ7žhSÁ>.¾Œæ=ZÎòK ˆÌ»P²Ç)ŒÃ¹” æ¢|Ú(õ{ö]]ºù.¯Èһ˧Nþ^^½;=®¿à‘¿üE¥øsâ¯Å¯‰:׃î>=øÂoØxná|¹ô<Ä‘[˜Ç s*'"õÔAæ ~Ï>ý£?àš¿¶Âo|FÐ~|;·øó«éö>%ÕV#¦hñjZU÷”³ÁƼi­ÔyªHÌÃkmü4ý·F‡£ø—ÀðJ?ˆ?ko‰~)h­ ×ðiø}¬iwzœZ{ÞGáûìé¢yÖÒ"a YqÌhäŠîÄÔU«N¤ofÛ×}_SMÒ¥Kt’üšÿচc»û4¤¼dQ‰<§%—vÓ’+äÏ÷ñø“öÚýˆA#ÛAà‹±ÈxY#Ô5e,q÷¢SžáW>•ùOÿÈÞ0þhÑÿÓÇö¿‚ð\GZU±ïMõË×㽟“?k袊ý<þ- (¢€ (¢€ øÓö’·ƒÅðZ¯Ù¯Oñ©ÿ‰v‡àÏëzoŸ.}PýŽÝÎÞ…ÒÖIH$|»›kìºù³þ Cû&x§ã÷…üãoÙŽöÃKøÉðoX>"ðŒ×¼[j¢1^i—üÝÀLLÃ!@ÐÈðUOÚ›Áÿ¿h?ü<ý£?á/Ѿ|"Ñn/ílcð†¯mãÉhæÙäšÖÒHVÎËr·ïCÌÁËkÚÿà‘>6ð?íÿZ🆵I.ntM/ÁMá¿­ö—skh|õC~È|UãOXøÇFðσõ}Vÿ@½(-µËx,¦–K9·Ç"ùs"4m¹aÎU‡ýŽ> iŸd?…~*ð_‡,|£x›ÁúF«a Y6Ú¼öPËœ;#|¸QÖ5Úˆ0ƒ £€ö ÿ„×þ§â'ü3gü”_øFu/øEÔÈWì²ýþ>sþ¿Êÿ[û¿ï|¹£ö`ÿ„×þ§áßü4Ÿü”_øFtßøJ¿ÔÈWì±}³þ=¿sþ¿ÍÿUû¿îü¸¬/Û¿Àÿð³¿aþØÐ¼=ÿ u½3ûW[»û&™¦yÚ|ñý¢î|*÷ïy0v¢±ÁÆ(ý„<ÿ Çöø5á¯í Ä?ðxDÓ?µtK¿µéšŸ“§ÁÚ-'Àó`“fô“r2œ â€=ZŠ( ¾3ýº`‡ÄðUاGñ9ó4o´øËXŽÚ@<™µ+].ßìŒr0^5šéÐuÊ’>鯳+çø)쑯þÔôMOà§g ü[øc¬Ãâ¿jW@ýo¡ ­ks·“msÉ ‹Ó ¬AÛ‚Ê~Ø?²§ÆÏ~ØøÉû kß `Ôü'áßA¥xÒÂúâÞwº¹ŽidY,å¢%má@Ç~l©â»/ØCö¨·ÿ‚„~Èâ‰>·Ðï&¸Ô|3â}ÏçÚÅsm+Û]$rñ¾Á ú6#5ñ‡Ä_ÛOáÇÆOIí×ñ§öŽý޾$.ššf·àÈõa§èWf,ï¸Ó®ä±ž)QüÎ'‚Xäe+Ç k_Jøë¥ü~ý,ÿfŸø!n‹«ÝÅ&‘®üO¹²º‡BðÅœÎßmž«€²j:Œ›¦ÀN7¾òà ©EòÓp–ÚÙuMÉÉ»öwzk«ém]ozJK}.ú4¢£kwÑkÙ=ï¤ÿðOO„¿?hø$¿ì†ß³çŽn<9>#ëk“ϪÝéï¯øONÕµ;saþûHšÙlñ ¡a ,@§ý¨]tÿø8wàÌ—ºI´‚³ -¤ƒ¹o—ë^¥ûlþË ~|ý‘|­xÝþøá¯Åÿ¯„’M}^Oê‘Ü%®˜í g{œÈÍw&QIaó òïÛÓv‘ÿâý¯¯Q–ÖãG³µÇ;¤7šŠãx3GϽ|¿釡.Õ©?ü™´øïæ¹/çÀc#ÿ”[Óîö,ŒjðpǹoËK%¶tbbÇ+‰ô¸Ç䄌Wé]~kÁ6„¿ðZ/Únå<©eˆ^²Œ3 þЄ2íÊGªJý)§ÂŠøZ²ïV£ÿɘ¼u|¹Þò`°qÿÊ1-Š(¯§? (¢€ (¢€ (¢€?)¿àìÛCþ‡ìW¥|ÿ„kûOþÿí'ûû&ÿK»ÿ)¾Ñæù›>ü{1ŸŸ¥ðgïü£OÇöSoÿôդ׫ÁÍŸ ü3â¯ø$_Äø£Ãº¥â? fbj·VM{£}£ZÓc¸û,ì¥àóca]êlŽ+ÊàÏßùFŸŽ?ì¦ßÿé«I¥Ô®‡êÍQL¢Š(¢Š(ò‹ö–Ñ>øWöÖøÙwÿñÇÇÿ>¹©[?¢ðÞ«âѯ´áanžm™ÓÖHÍѸYÃÀØÁ Dg$Ÿ£à² ÇÂÙûAñßÄ»¯ŠÇ#±º‚æßÅ~%Ôo\ÙÇkIf±¹™£¶¹{xí™Âª•guÂòµóí­ûkø›ÁðPwÑ~1|}Ôþ è–4‹Ã–žIí4û_øG§ÐgŸû}丌›¦ûv#RXÅĨÈY†~íÿ‚o|añoǿػÁ>+øÛûíS†à5÷ØþÅý±o̱[_ýŸÊûL ÆÀo x=0êq裗/]ýæšæÕkum‹ÅÿB]o/ÞßÝM;hôå|Þòr÷(¢‚Š( Š( Š( Ã?ø!çü¬³û_ÿÜçÿ©U•~æWáŸüóþVYý¯ÿîsÿԪʿs)Dl(¢Šb+jº-ž»n!Öí-¯"VÞx–E ‚2Î çÞ§†¶…#·EŽ8Ô*ªŒ€:ŠùOþ §ÿ Óþ1ÃþOþ¯ù.~ÿ„WþŸ²ÿÈ{ý+ìÚiÿ—õ¾o•ûß»·½}Y_)ÿÁTü{à¯ÿÃ8Âèðü'¿Û?ü3¥è_ñ<ŸKÿ„sU—í_gÕr§í^F×ÿF“É¿æ#¾¬ Š( ¢¾°ƒT´x5(b¸‚A‡ŽTßx5-„e¢A¦Ã¼Œ$q D^üÀ¯–?à·^=ðWÃø&Äísöˆðü-Xÿehxgûr}ûO~­d‘¦Û©–/.WŠ_”ÞVÓÃú²¾oÿ‚¸üDø·ð£þ ëñ_ý…íõÛ¯Š–Ùߨ‘hÚ"ëW­¿RµŽã˳heÿ£=Á9¶¨fãn@ÁHQEQEU=[Ãöú ×llïDD”²Ï\n\¢€ óÛWð–û!üU¿øý¥ßkž²ð~¯qâM:ÉÊ\êjYL×PBÂHÈ‘á*‘$d:ýáéæÿ¶>£©é²Å[¿øZÇÇ:ͯƒõy¬<7{¦¾§mâ …²˜Çc5š|×1ÌábhW™•šù×ö£›ÂÞ3ÿ‚½×ìñ¦^h~»øq£ÞøsN¿r×:~–!´–Ú XÉ!2$“$™`~vûÇàø'ö³oâÛÏöLx•H¶ð½Å»£Å+mr}T0ï‚ ~‡|oþÕñ_üæoøcOð&µ?›;»ÿY鯦Zø~qa ’ØÃfùkd…ƒD°·1„ zWæ¿üË[_ÿÁF¾ÁbŒÇBÒ5‹k‚¹; A«J qÇŽýG98¯Ì8©Û>ÁGù½šûª¦iø¹¼.â*‹z_Z—ÊXGñi/S÷’Š(¯ÓÏâÀ¢Š(¢Š(¢Š(®«¡ÙkЬzå­ìhÛ•g‰dU=2ÍXŽ5†5HUQª€S¨ ›ÿà®>ñ7Š¿àž¿l>üRо xŽìï²xËYñ$¾²Ñ±©Z³ùšŒ ¼laà;L¨xs_HWÍÿðW |$ñ×ü×â•ûtx£]ðg»¯ìïí½gF‰¥½³Û©Zµ¿–«opNë•·Cˆ_åvû¿y~ ö ÿ„×þ§â'ü3gü”_øFu/øEÔÈWì²ýþ>sþ¿Êÿ[û¿ï|¹£ö`ÿ„×þ§áßü4Ÿü”_øFtßøJ¿ÔÈWì±}³þ=¿sþ¿ÍÿUû¿îü¸ª¶>Ÿ©êÿ²Å[Oø¦ÇÀÚÍ׃õxl™máû†²˜G}5â|ÖÑÂåei—˜ÂŠ?c?SÒ?d?…vž4ñMŽu›_éßø’ËR}NÛÄ e’úÇù®c™ÃJ³72 y4Göïÿ„gþ{ã/ü.íßøC¿áÖÿ·±<¯í?°gÏö²yߺóü­û<Ï“~ÝÜfØCþŸøaïƒ_ð¥ÿ·áÿ„DþÂþÛò¿´þÁýŸÙþ×äþëÏò¶oòþMû¶ñŠ?nýDð¯ì=ñ—Tø• ÂWáÍ7ÀºÝÖ«¢}ºKí›DÓçi­~Ó/›hüÔ“váÈ~Âþ‰â¯Ø{àÖ©ð×@ÿ„SÚ—tK­+Dût—ߨ֧ÀÐÚý¦@*2±ù®>ÝÇ’hÕ¨¢Š(¢Š­ªhöšÝ·“­ZÛ^B7—$“ía½¸Ûqè#ÄÛ”ù î‰û|ÏÿB¼?ÿ{ý–5+…ó!º½±±P§æ ÚLŸaç©ü }1ÿñ‰¼?ÿ #þ¯ÂÝ â‡Û¾0x~ÓUþÓðܺßü"Vö;[·òÈûöØ]·”‹Í90¯™?à²ôOø(§ìŸ«&×Ù¯ÛíŒñ“§füŸC¼¾c‹½Ü¿Ÿ´é¿üž'í>¯iÅNƒûx|T~ü=GúüA¨ÁMk;» Ò[>¿zDNß›Uº+Ïl€HÇÒ¿JëóSþy)Ö¿l?Ú¯R´F²ëñ¶[—̽ÔYAë…n™Wé].×,Œ»Ê£ÿÉä?¤¹ÆUi%,òÚ“×ï (¢¾ üT(¢Š(¢Š(¢ŠøgþNÿ”+|hÿ¸þŸtêðÏø3÷þQ§ãû)·ÿújÒkÜÿàäïùB·Æûÿé÷N¯ ÿƒ?å~8ÿ²›ÿ¦­&—Rº«4QE2BŠ( ¼¿öÛø»ª|ýŽþ)xãÁ¬øKº–­aç.øÄðÛI$e—ø€eŽý+Ô+â_ÃãÃÝoŸlTðÿˆìfÓu+7vEº·• Id!€eb2<ðk*ð•JSŒ›M.šØÛ8Ó« Í^)¦×u}OÌ=gö;ø›ñ+öŒøCðÓâíã­{Lñ<{y©êš}©é—Ö­f†>imOÙ­ä:‚±Œ#ÈP&¿Igÿ…Ÿ~éñ‹|Iã«Ý1dóµÍzX俾g•ä̆4TwìUUUTv¯˜ÿâ¯ØãþˆŽÿƒOÿ’«éÿ€ß|#û1ü'Ò< ð+F‹ÃþÐQã°ÓâšIRÝ^F‘€yYœåÝÏ,z×[œ\ж­ýîMzrÅÆ>i]Ûc‘FWNNú%D“öóN^W¶§_EV&¡EPEPEPáŸüóþVYý¯ÿîsÿԪʿs+ðÏþyÿ+,þ×ÿ÷9ÿêUe_¹”¢6QE1Q@7ÿÁF¾"|[ø{ÿ #þ6ß]¸þÜøÁáý#Æ_Ùš"êž_†åûGÛžãt2}š¶×#aŒ:矤+Ã?n/ßþ7ÿŸÿ†lñ‡ü"ðˆ|MÑ|Mâ¯øšÝiÿÛz ·öÍ?ýío™î%Ûìù˜`W¹ÐEPEP_7ÿÁ\|=âoÁ=~ Ø|ø¥¡|ñÿÙßdñ–³âI|9e£cRµgó5Ax<ØÃÀ0>v™Pðæ¾¯”ÿà·^ðWÄïø&Äíöˆñ÷ü*ÿ_ehx›û}oû3f­dñ¡[°–_2TŠ/”¾nãšVQEQEQEW ûPÂkÿ Óñþ³þJ/ü#:—ü"¿ê?ä+öY~Çÿ?¹ÿ_å­ýß÷¾\×s^oûcü?Ó>,þÈ|+ãOØø;Fñ7ƒõ}*ÿ_½m´;y즊KÉ·Éùp£´¹ÐaYG Ë~4iþ6Ôÿàšü´“„øŒ¿ ¤›ÅGøš¦“¾ð£~ç™ÖQû¯Ýóòü¸¯Ë/ø •œ·_ðQß¼Ymô­FI~êý—?÷Ó(ükõ\ü'Òüÿ‡ÿ„ÁÞ'²ñf‹£üþ±ñŠ¡¶ÖmâÑ|ˆïa #®ÉQVU #Œ0Ã0äþ_ÿÁ½Ö2]ÿÁAVHq¶×ÃwòÉ“ü%¡N?Zü׊iß?Ëšü$™ýàŽ)Sð¯Œa}¡/üš“è~èQEúQürQEQEQEQE|§ÿºñÿÁ0>'kŸ´G€áhx:Çû+ûCÃ?Û“èŸÚ{õk$‹ý6ÝL±yr¼Rü îò¶ž×Õ•áŸðRŽ?ÿfÿدƞ4ýüÿ ïÄ]ì?ÙöUÖ©öï6þÞ¿Ñm&“dÍ'ÈÃnÍÇ*>ç@oûcé×ÿd?жµKíÀ—¾ÕíüI¨Ù!{?M{)–êxTG!2$&FP#’£äoºOØãHð–û!ü+°øª_kž²ð~‘oá½Fõ \êjYB¶³Ì¦8È‘á³d?"ýÑGöï×ôO þÃßuO‰Zü%~Ó| ­Ýjº'Û¤±þÙ´M>vš×í1‚ðy±†Í@Y7nGì!¯èž*ý‡¾ jŸ tøE<9©xDºÒ´O·I}ýhú| ¯Údçò£+šà3íÜy&€/~Øþ"ño„?d?Šº·Àï¦ñÞ—àý^ïÃied/®_RŽÊgµÛq<†ele9Âí9Á?cx·Åÿ²½[ãôwÑxïTð~‘wâD½²7)©Ie Ý ­‚ ‚A3Iº0ŠåvŒ`_ý¨<âo‰ß³OÄO |Ö?áñˆ|3©iš«ö¹m?³/æµ–;{>e‹Ë•ÑüÈÁuÛ•€(ý˜<âo†?³Oÿ |hÖ?á!ñ‡¼3¦éšî«ö¹nÿ´ïᵊ;‹>`%—Ì•üÉvÝ–’(¹¢Š(¢Š(¢Š(Ã?n/ø]_ñgÿá‹ÿè¦è¿ðÿLJüŠŸ¾þÑÿÏûaþ£÷ÿÜï_#ÿÁuƒh¿µ/ì­ªÚ03¦¿qµXeAŽïL`ÿ¥}7ÿýŸ¼3ñûþGü,¯ˆÚïøB¾0xÅzWö˜ˆÿÂOkö'H·ó'‹÷÷cmÙæ?îÎ#~qó?ü#³ñìõªÚ³%퇈®Ò„ÜÖLN:˜“ò¯˜ãùÕ}œÝ8Ÿ´ýÝøóMí(â#ÿa«/ÎÂÁ¿q¾«ñö’×,qê¾!³ íÍYu98¾>Æ¿Jkó[þ ½’Mká¯ÅÍnð ŸUñ¹’4\"¹Û’N3)öë_¥4poü‰è>üÏ}!´ñ3‡òûÿà4)Gô (¢¾œüX(¢Š(¢Š(¢ŠøgþNÿ”+|hÿ¸þŸtêðÏø3÷þQ§ãû)·ÿújÒkÜÿàäïùB·Æûÿé÷N¯ ÿƒ?å~8ÿ²›ÿ¦­&—Rº«4QE2BŠ( Š( Š( Š( Š( Š( Š( Ã?ø!çü¬³û_ÿÜçÿ©U•~æWáŸüóþVYý¯ÿîsÿԪʿs)Dl(¢Šb (¢€>Sÿ‚©ø Á^<ÿ†pÿ…Ñãïø@¿±¾9øgTпâG>©ÿ «ھϥ~å‡Ù|ýÏþ“&cgÌE}Y_7ÿÁF¼[ð“Âð¢?ᬼ/®øŸûOã‡ì<ý™+Gý“âI>ÑöÛ·ý§¿V²H¿ÓmÔË—+Å/Êï+iá}Y_7ÿÁ\|Câo ÿÁ=~ ßüø[¡|iñÙßdðn³á¹|Ge¬çRµWó4èHyü¨ËÎ0~F…\ð†€GÒQEQEQEå?·ü#?ðÃßátnÿÂÿ.·ý»ý‰åiýƒû>´}“ÎýןåoÙæ|›öîã5êÕå?·~¿¢xWöøËª|JÐ?á+ðæ›à]nëUÑ>Ý%öÍ¢ió´Ö¿iŒƒÍŒ4~jÉ»pä ä>é¾Öà”ž³ø<Úúø*ïá5œ:#k>Oö ÓÛGAnn¼ŸÝ}£Ê)¿gÉ¿vÞ1_™?ðnnž×Ÿ·v·"2iàÛÉXâîÉ0?•~¦~Ê×úÄø&çÃ{¯‡úxOÃßÃ]2]7D[ù/Žg.—ŠÔ]JÏåFËšãsíÜFI¯Ì¿ø6ÓGóÿkÿêf>Ëàémü½¿{̽´lçÿ…_àëïì¯íaÏ­ÿflÕ¬ž/ô+vËæJ‘Eò‘·ÍÜxS_VP›þØúާ¤~Ènüákë6¾Õæ°ðÝîšú·ˆ.ÊcŒÖió\Ç3…‰¡^dTrhýŽ5OWýþÝøÓÂÖ>Ön¼¤Má»-5ôËoÜ5”&Klßæ¶Ž-ÂÜÆ)äQûcøwž/ýþ*é?d¾‹Çz§ƒõ{O ½•è±¹MJK)’ÔÃr] š=²P‡ ¸c ýŽ<;âß~È ôŸÒ_Mã½/ÁúE§‰öô_\¾¥”)tf¹ây Ë&é°s–Üs’GöïÐtO~Ãßt¿‰Zÿü"žÔ¼ ­ÚêºßØd¾þÆ´}>ušëìÑóùQ–“ÊBöí‘Gì! èžý‡¾ i uÿøJü9¦øDµÒµ¿°Écý³hš| ×Ù¤%àóc '”ä²nÚy¯~Øú¿„´Ùâ­ÿÇí.û\ð%—ƒõ{iÖNRçPÓRÊfº‚FD‘T‰# °ù×ïö8Õü%¯þÈ ïþéwÚ/|¤\xoN½r÷:~šöPµ¬1’BdHLjÄÉ!%OÎßx€zEQ@Q@Q@)ÿÁTÿáZÆ8ÃIÿÂuÿ%ÏÃ?ðŠÿÂ3ö_ù¥}ûCí?òãþ·Íò¿{÷v÷¯ÿƒ”ÐAðKáäK«}~àE*’<ÛäàŽœ¢ŸÀW¿ÿÁTü{à¯ÿÃ8Âèðü'¿Û?ü3¥è_ñ<ŸKÿ„sU—í_gÕr§í^F×ÿF“É¿æ#¼WþNƒwì›ài6gg‹Qwc¦lîN3ï·ô¯˜ã%Í“b}æÙ¾u=Ÿˆy;ÿ§’_}9¯Ôƒþ ®°Hÿeÿ].ï6oˆ›ž0–‘ÿ£ô¯ÑÚüøÿƒn­cOأųª,ž7¹FoU[ àY¿:ý«áË“a—÷VeãýOkâs/ú{oº1_ QEô‡ãÁEPEPEPÃ?ðrwü¡[ãGýÀÿôû§W†ÁŸ¿ò?ÙM¿ÿÓV“^çÿ'ʾ4ÜÿOºuxgüûÿ(ÓñÇý”Ûÿý5i4º•ÐýY¢Š)’QEQEQEQE~{ÁH,¼Kð‹Gø±ãŸ‹Ÿ¼Y¢ø»Zxì>xSÁ~'Ô-çšê+xü¸äÒ  ¥<÷…¼Õš;˜Ò2 ãžø‡ã¯[~Ð?ñOí«à_ÚvçÁ^ð_†æ¹ºð'ŠõèZÀ´–}^äGi«Ù›í¯2‡û2]:‹b00{̰_Äß þ×¾=ø¿à‰? î5Ï´6öø£á½æ¯}á½:(‚&Ÿgs»l© mò¾Ø”»Èų€ÿ‹±ÅÝøæÛ¿ît|QÓa±ñ™yáÙu?칸·¹“ÃÏ& £KY“q1H—j®ÛÇÍ’r‡4i+%ÍdÒ{&•µ¶·w~ÒIÝ»YY9=[Œª{ÏÝÙµ»\Ñzzrû‰ôroWÊ|÷qâígöѼý¥üað³â´MàîƒaiðÕô?ßZÙ±£Õ?´ocIö“JÓB¥oËå†w15Gà7Å_ÿÁPu/ˆþ ñ¼sàHü ð×Ã7ž³ðLjn´›huOH:œ×÷)léöÍŒÐÆ°N$‡b¾P–&½—_ÿ‚FÍá+ø{öQø’ÿ< ñÃ:w…|U¤Ë Vêk{+Qd·}Û\ÇöK—³CÉ$w p®0Ílxëþ …¨èž'×®d/ˆ±|/Òüiá x’Æ_®®n-,¢k{k›)>ÓÙoÚG‡ÍuØLD 5u£xT&îâ”[ÝM)©TvþvÓV»NÎË•BV•9TKGï%³56 ¿Â”“¾UwÎÙóÿì{ñÆ¿ðV¿ü,xßÁ–^øIáÝV(|)â­nuíR;—“PœÚý¿àåŸð²¬uÛïøZ:ñÃý+û2¥û=ýÿŠ­ü™n<ÉSl ä¶æMî20Î?¢*üÿ‚:|/ðÏÄïø9gö§ÿ…•áÝ Ä?ðj~-Öô¯í;®ÿ³/áñU§“woæ)ò§{m•0ë¸àŒšýé®x›°¢Š)ˆ(¢Šù¿þ 5âxþGü+_…ºÅ·|`ðý¦«ý§á¹u¿øD¬ív·oå‘ö)í°»o)šr>a_HW†~Ü_ðº¿âÏÿÃÿÑMÑá;ÿù?}ý£ÿŸöÃýGïÿ¹Þ½Î€ (¢€ (¢€ ðÏø)?ü.¯øb¿ûÿä± ÿÂ?ÿóÿoöŸù£ǯÚÖÀ~mµîuóüÇö~ðÏíKÿõøƒàOŒ´/„¾×³¾×â½dDl´¯'Rµ<Á4ð'ï$‰!•~iW8Rô…Q@Q@Q@y¿íâ/øCöCø««|Žúoé~Õîü6–VBúåõ(ì¦{Q ±GÈfXöÆQƒœ.ÓœH¯7ý±ôýOWýþ*Úx/Å6>Ön¼«ÃaâKÝIôËoÜ5”Â;靿¶Ž++L¼Æ°äP_²6«âŸˆ?±·ÃïÚ&+øüm­ø3JŸÄñ^Ù‹ ¥Ôe±‰®Ö[uDIç4¡£›Wjãóþ ¦ÓR__o›Ìƒ@‚½ˆ{€I>ÿ»™¯ÔØãOÔôÙá]§'Ã`ÿÂuÿ ëþ%_Úÿð‡}—ûoþBÖ^OÙ¾×ûŸõþNýÿòÏ~>lWÕ•òŸüëãü3üâw?áð7±¿²¿âEã+ûSD¾óuk(Òmw§™³ÍóædDnqƒõe<§öïÐtO~Ãßt¿‰Zÿü"žÔ¼ ­ÚêºßØd¾þÆ´}>ušëìÑóùQ–“ÊBöí‘Gì! èžý‡¾ i uÿøJü9¦øDµÒµ¿°Écý³hš| ×Ù¤%àóc '”ä²nÚyÛ¿þŸøaðº?·áÿ„[þÝþÄò¿´þÁýŸ?Ú>Éç~ëÏò·ìó>Mûwqš?aøFᇾ —þÝÿ„;þ]û ûoÊþÓûö|gû_“û¯?ÊÙ¿Ëù7îÛÆ({öÇÔu=#öCø«wà¿ XøçYµð~¯5‡†ït×Ôí¼Ap¶Sìf³Ošæ9œ,M ó r£“Gìq¨êz¿ì‡ð®ïÆž±ð6³uàý"kÿ Ùi¯¦[x~ᬡ2XÃfÿ5´p¹h–æ0O"¯þÔðšÿÃ4üDÿ†lÿ’‹ÿÎ¥ÿ¯úù ý–_±ÿÇÏî×ù_ëwýï—4~ÌðšÿÃ4ü;ÿ†“ÿ’‹ÿΛÿ Wúù ý–/¶Ç·î×ù¿ê¿wýß—ÜÑEQEQEóükâ'Å¿‡¿ð¢?á“mõÛíÏŒÒQŠ_܆§ê¾Öö}’ËþŸÁ}ú~¤ðo5¿“ûJÛ6yÞ(¾|íÆÿÝÀ¹÷û¸Ï·µ}Ó_Á­d·ÿ‚rè/2•Yõ}Eã'ø—Ï+‘ø«¾ϫáˆòå8UýÈþF4Uö¼wËþ¢*¯ºM~EWº~bQEQEQE~aÿÁÕ?¶†~ ÿÁ=n¾x¢Ç]ŸÄvb\ÚÃYZÿeêZeÝÇÚ¥WMѸ ±$Ëd6ÑÍaÁŸ¿ò?ÙM¿ÿÓV“^­ÿ6|/ðÏŠ¿à‘oÿ‚~ÏÞøýÿ #þWÄm á×ü!_'Ã`ÿÂuÿ ëþ%_Úÿð‡}—ûoþBÖ^OÙ¾×ûŸõþNýÿòÏ~>lWÕ•óüÇöðÏìµÿõøƒã¿Œt/‹^п³¾×áMdÄ,µ_;Rµ<Ã4§îä•&‰¾h—8`>¢Š(¢Š(¢Š(¯)ý»ôÅ_°÷Æ]/âV¿ÿ§‡5/ëvº®·ö/¿±­Ofºû4d<þTe¤ò†}»G$W«W”þÝúþ‰á_Ø{ã.©ñ+@ÿ„¯Ãšou»­WDût–?Û6‰§ÎÓZý¦0^60Ñù¨ &íÃ(°†ƒ¢xWöø5¥ü5×ÿá+ðæ›à]×JÖþÃ%öÍ¢ið,7_f—ƒÍŒ,žS’É»iäñOø&'üÏÅ?±?ÅŒ:çÄ‹ß ÝÚøöþÞ}!4›‰ä{XcšñÙ&Y!Œ)ÅÄ8 _î·#Œû_ì!¯èž*ý‡¾ jŸ tøE<9©xDºÒ´O·I}ýhú| ¯Údçò£+šà3íÜy&½Z¸«à(âkÑÄM{ÔïËÿo+?Àú,¯ŠqùFYÊ0ÒJŽ-SUWmRŸV­e7úMÖÇò÷ù^Z|§tŽ‹Ær>¬ )ý»üqÿ ÇöøËâ_ì} Ä?ðx[Ôÿ²µ»OµéšŸ“§Ï'ÙîàÈó`“fÇ#r3 ŒæØCÇð³¿aïƒ^%þÇм=ÿ tMOû+D´û&™¦yÚ|}žÒ Ÿ*÷ìHòv¢¨ÉÆkwö ÿ„×þ§â'ü3gü”_øFu/øEÔÈWì²ýþ>sþ¿Êÿ[û¿ï|¹£ö`ÿ„×þ§áßü4Ÿü”_øFtßøJ¿ÔÈWì±}³þ=¿sþ¿ÍÿUû¿îü¸  ¶?Ãý3âÏì‡ñW¾4ñƒ´ox?WÒ¯õûІÛC·žÊh¤¼›|‘¯— ;HÛå”rØãáþ™ð›öCøWá_øŽÇÆ:7†|¤iVýAm®[Áe QÞC²IË™d]®ã0Ì94nÿøFᇾ2ÿÂèþÝÿ„;þ]oûwûÊþÓûö|ÿhû'û¯?Êß³Ìù7íÝÆhý„?áÿ†ø5ÿ _ûwþïøAtOì/í¿+ûOìÙð}Ÿí~Oî¼ÿ+fÿ/äß»o V¢Š(¢Š(¢Š(å?ø*Ÿ€¼ãÏøgø]>ÿ„ û㟆uM þ$sêŸð‘ê±}«ìúWîX}—ÏÜÿé2f8ö|ÀäVü«Nkßø&ÿ‰eV ,õM6bñr‰ø¸?…oÁTü{à¯ÿÃ8Âèðü'¿Û?ü3¥è_ñ<ŸKÿ„sU—í_gÕr§í^F×ÿF“É¿æ#«Ár4õ½ÿ‚d|C‘Ø©´—K”üDêv©ƒø9?…x¼Gl«¿éÜÿô–~‰á_eÆù¿ê*‚ûêE~¤_ðC -?à™¾’lmº¸ÔåLÃQ¸N5õÕ|¿ÿbÑ·ÿÍø] H$/m}q17Qº—†ü~õ^AL³ Ÿüû‡þ’Œ(¢Š(¢Š(¢ŠøgþNÿ”+|hÿ¸þŸtêðÏø3÷þQ§ãû)·ÿújÒkÜÿàäïùB·Æûÿé÷N¯ ÿƒ?å~8ÿ²›ÿ¦­&—Rº«4QE2BŠ( Š( Š( Š( Š( Š( Š( Ã?ø!çü¬³û_ÿÜçÿ©U•~æWáŸüóþVYý¯ÿîsÿԪʿs)Dl(¢Šb (¢€>Sÿ‚©ø÷Á^ÿ†pÿ…ÑàøO¶~9øgKпây>—ÿæ«/ھϪþåOÚ¼¯þ&#“ÌF}Y_7ÿÁF¾"|[ø{ÿ #þ6ß]¸þÜøÁáý#Æ_Ùš"êž_†åûGÛžãt2}š¶×#aŒ:矤(¢Š(¢Š(¯ ÿ‚“ÿÂêÿ†+ñ§ü;¿þKúü#ÿñáÿ?öÿiÿ‡ú7üzý§ýgüæÛ^ç_7ÿÁ\|=âoÁ=~ Ø|ø¥¡|ñÿÙßdñ–³âI|9e£cRµgó5Ax<ØÃÀ0>v™Pðæ€GÒQEQEQEæÿ¶>£©é²Å[¿øZÇÇ:ͯƒõy¬<7{¦¾§mâ …²˜Çc5š|×1ÌábhW™•šôŠá¿jøMáš~"Ã6ÉEÿ„gRÿ„WýGü…~Ë/Øÿãç÷?ëü¯õ¿»þ÷Ëš¡ûj:ž¯û!ü+»ñ§…¬| ¬Ýx?HšÿÃvZké–Þ¸k(L–0Ù¿Ím.Z%…¹Œ SȯH®ö`ÿ„×þ§áßü4Ÿü”_øFtßøJ¿ÔÈWì±}³þ=¿sþ¿ÍÿUû¿îü¸®æ€ (¢€ (¢€ (¢€ (¢€ (¢€>Sÿ‚Ý´ÿ‡`|Nÿ†Áÿ„ëþ×üJ¿µÿáû/ößü…¬¼Ÿ³}¯÷?ëüûÿåžü|د«+å?ø-×|ðÇþ ñ;\ý¢<ÿ CÁÖ?Ù_ÚþÜŸDþÓß«Y$_é¶êe‹Ë•â—åw•´ðƾ¬ 7ý±ôýOWýþ*Úx/Å6>Ön¼«ÃaâKÝIôËoÜ5”Â;靿¶Ž++L¼Æ°äQûiúž‘û!ü+´ñ§Šl|s¬Úø?H†ÿÄ–Z“êvÞ ¸[(D—ÐÞ?ÍsÎU™¹8cÉ£öÇÒ<%¯þÈl>?j—Ú/|«Ûø“Q²B÷:~šöS-Ôð¨ŽBdHLŒ G!%GÈßtŸ±Æ‘á-öCøWaðT¾×< eàý"ßÃzê¹Ô4Ô²…mg™Lq‘#Â#f8È,~Eû í߯èžý‡¾2ꟴøJü9¦ø[ºÕtO·Icý³hš|í5¯Úcàóc š€²nÜ9ØC_ÑèðŠxsRð.‰u¥hŸn’ûûÑôø_´ÈÏåFV?5ÀgÛ¸òM^ý±üEâß~Èuo€1ßMã½/Áú½ß†ÒÊÈ_\¾¥”Ïj!¶(ây ËØÊ0s…Ús‚~Ç"ño‹ÿd?…z·Çèï¢ñÞ©àý"ïĉ{d,nSR’ʺ[A‚f“ta!ÊíÀôŠ(¢€ (¢€ (¢€>oÿ‚x‡ÄÞÿ…ÿ ×án…ñCí߬¢Š(¢Š(¢Š(¯7ý±þéŸd?оñ§ˆì|£x›Áú¾•¯Þ„6Ú¼öSE%äÛä|¸QÚFÜè0‡,£‘éå?·ü#?ðÃßátnÿÂÿ.·ý»ý‰åiýƒû>´}“ÎýןåoÙæ|›öîã4{ö8ø¦|&ýþøWÁ~#±ñŽáŸéU†¿d[k–ðYCwì’EòæDYk¸ÃŒ3O¤W”þÂðŒÿÃ|ÿ…/ý»ÿwü º'ööß•ý§öìø>Ïö¿'÷^•³—òoÝ·ŒW«PEPEPEPEPEP†ÁIþ8üKý›ÿb¿xÓö?ðü'¿to°ÿdh_ÙWZ§Û¼ÛûxfÿE´tšMK4Ÿ# »7¨ ûxgüŸàwÄ¿ÚGö+ñ§‚ÿcÿÂñYûöF»ý«u¥ý‡Ê¿·šoô«Dy£ßSGò)Ý¿iÂ’G¹Ð”þÝúþ‰á_Ø{ã.©ñ+@ÿ„¯Ãšou»­WDût–?Û6‰§ÎÓZý¦0^60Ñù¨ &íÃ(ý„5ýÅ_°÷Á­Sá®ÿ§‡5/è—ZV‰öé/¿±­O¡µûL€<þTecó\}»$ÖïíAã|1ýš~"x—à¾ÿ Œ|=áKSд¯²Kwý§ ¬²[Ûù‘,¾d¨‰åÆC¶ì)ƒGìÁã|Nýš~ø—ãFÿ÷Œ|Cá7S×t¯²Kiý™5¬R\[ù,^\®éåÈK®Ü1$@?l}?SÕÿd?жž ñMµ›¯êðØx’÷R}2ÛÃ÷ e0ŽúkÄù­£…ÊÊÓ/1„,9~Ç~§¤~È í$ü/¸×h»ll]Çû»IÆÑþäì­-/wnny+ÞN6^ë“ÝNÚ2’¼Tµþün¯vV\©ý•+¿{”ùŸà·ÆŸÁH~ümø§©øëÇ>Ô¾x/G“Â6ñÖ§Xêrh0êÓÝÝ[@ëöù§Hü»•–1• ’Mmüø«ã_ø*¥ñÄ"ñ·Ž| >øfóÃö~ñ Ö“m³©éSšþå->Ù±šÖ ÄìWÊÄ×·øçþ GwaqãM/ö]øŽ>x'â‡tÿ x³G>]JâX,텒ϧ]ˆ…ÃÙ!žH®á\ eÍ^ñ×ü QÑT¤ôzêùÛ>ý~1ø×þ Áâ·ÿ…ãOø2ËÂ_ <;ªÅ…?k¿µì ð¿Ç?¤IüE¬èá5)ÑB‹«ˆ${y&Ú æ4%ðì àõ_ø& ÷Ã=rÊÿö"ø…Â÷oÚ|=ÕûÃÃ\Kë @Ëis6þUôBY@™¼ØÈš&À¯ g/€Úì½ð#Â>‹¡ø?L‡L´k†4«àÉ#wl»%·œàÝWdåt¿íê­¿+©CåhísÂ2\œÚµ¿òJi¯?yIúÝý¦~ Á??mß…ÿ°GüûXøÃö²ñ?ü"žÔµ?èÖ×Ù×wÞeÛø’ –?.Ò)f;y›qP¿. ÉþžÄIß±_ýü´ußþ@¯7øñÿ®þÏŸ´7ÇøÿÆž1øËk¬øç]¾ñü6Z¶š–ÐÜ]Ü<ò,*ús2ÆF ˜€I<×)ÿ~þÍ?ô<|sÿÁΕÿÊÊæWFúçÿ'~ÅôZ?òÑ×ùø‰;ö+ÿ¢Ñÿ–Ž»ÿÈáŸñïìÓÿCÇÇ?üé_ü¬£þ ýýšèxøçÿƒ+ÿ•”õ sÿˆ“¿b¿ú-ùhë¿üGüDûÿÑhÿËG]ÿä ðÏøƒ÷öiÿ¡ããŸþt¯þVQÿ~þÍ?ô<|sÿÁΕÿÊÊ5 öâÿ‚ûþÅáOÿ§ÿÂ×ÿ„oân‹®ÿ¨×t/øC¼;þ'¿ñä¿nû.ÿøòçÍózµòŸüþNñ¯ü4¶‡ÿ›øÑÿëþ˜?´⑃þB¿j»óä+aç¨ûÝýߧͺ¾¬ÿˆ?fŸú>9ÿàçJÿåeñïìÓÿCÇÇ?üé_ü¬¥¨ô=Ïþ"NýŠÿè´壮ÿòñwìWÿE£ÿ-wÿ+Ã?âßÙ§þ‡Žø9Ò¿ùYGüAûû4ÿÐññÏÿ:Wÿ+)ê-sÿˆ“¿b¿ú-ùhë¿üGüDûÿÑhÿËG]ÿä ðÏøƒ÷öiÿ¡ããŸþt¯þVQÿ~þÍ?ô<|sÿÁΕÿÊÊ5 sÿˆ“¿b¿ú-ùhë¿ü^ÿ'ÿƒŠÿf~Å~4Óÿg}{Àß¼cqöìÿ xÇÀúµÎ‰«bþÝ¥ûLWðÄÞ\BY“t‹‰"B2@RÄ¿³Oýÿðs¥ò²øƒ÷öiÿ¡ããŸþt¯þVQ¨hðRø9;à¯ü1_?áÝÿ?âñ ÿÂ?ÿÿüÿÛý§þBfÿ_´ÿ¬ÿ€üÛk†ÿ‚?ÁÉÞ ÿ†i×?áìŸ?ââÿÂM?öwüR3ÿÈ+ì¶žWü‚¬<Ÿõÿlûß¼õùv×sÿ~þÍ?ô<|sÿÁΕÿÊÊ?âßÙ§þ‡Žø9Ò¿ùYF£Ð÷?ø‰;ö+ÿ¢Ñÿ–Ž»ÿÈÄIß±_ýü´ußþ@¯ ÿˆ?fŸú>9ÿàçJÿåeñïìÓÿCÇÇ?üé_ü¬£Qh{ŸüDûÿÑhÿËG]ÿä ?â$ïØ¯þ‹GþZ:ïÿ W†Ä¿³Oýÿðs¥ò²øƒ÷öiÿ¡ããŸþt¯þVQ¨h{ŸüDûÿÑhÿËG]ÿä å><ÁÈŸ±•ïÀïäøÞÇâÔÚòCá[ß êð[x™Í»…Óæ’}=¢Hî!f‘Y–dW›ÿÄ¿³Oýÿðs¥ò²øƒ÷öiÿ¡ããŸþt¯þVQ¨h|Ãÿ¦ÿƒ‘.,¿kÏÃûsxÞÇáçìý…xž ð­—…ážÛÃ./mE†Ÿ š^ž³¼vö_h…YÕPˆÁ 6Ñ_¢?ñwìWÿE£ÿ-wÿ+Ã?âßÙ§þ‡Žø9Ò¿ùYGüAûû4ÿÐññÏÿ:Wÿ+)jçÿ'~ÅôZ?òÑ×ùø‰;ö+ÿ¢Ñÿ–Ž»ÿÈáŸñïìÓÿCÇÇ?üé_ü¬£þ ýýšèxøçÿƒ+ÿ•”õ sÿˆ“¿b¿ú-ùhë¿üGüDûÿÑhÿËG]ÿä ðÏøƒ÷öiÿ¡ããŸþt¯þVQÿ~þÍ?ô<|sÿÁΕÿÊÊ5 sÿˆ“¿b¿ú-ùhë¿ü^ÿ'ÿƒ“¾ ÿÃøÓþßñ£þ/úü#ÿñHßÿÏý¿Úä!aöoøõûOúÏøͶøƒ÷öiÿ¡ããŸþt¯þVQÿ~þÍ?ô<|sÿÁΕÿÊÊ5 þ ±ÿ'|ÿ†+ð_üÃìßñëöoõð/›u{ŸüDûÿÑhÿËG]ÿä ðÏøƒ÷öiÿ¡ããŸþt¯þVQÿ~þÍ?ô<|sÿÁΕÿÊÊ5 sÿˆ“¿b¿ú-ùhë¿üGüDûÿÑhÿËG]ÿä ðÏøƒ÷öiÿ¡ããŸþt¯þVQÿ~þÍ?ô<|sÿÁΕÿÊÊ5 þ Oÿ}ýŠÿࡱ_>ÿÃFÂ!ÿ Øâoÿ»¨}“ì×ö÷ñïöh·îû6Ͼ¸ßžqƒîñwìWÿE£ÿ-wÿ+Ã?âßÙ§þ‡Žø9Ò¿ùYGüAûû4ÿÐññÏÿ:Wÿ+(Ô4:¿Ûþ,ý–<_û!üUÒ~ül¾‹Çz§ƒõ{O ½—‡µË”Ô¤²™-L7&Í š=²P‡ ¸c#Ã?àŠßðqg€¼!û!Ýé?ðTï—ÓxïK×^ÓH{ß__\¾•š@f¹±³q<†e»Ý$îÓ¹Ë;©>‘ÿ~þÍ?ô<|sÿÁΕÿÊÊ?âßÙ§þ‡Žø9Ò¿ùYF£Ðî¿jø8‹ö+ñçìÓñCÿ„ïþßíŸ êV?ðŒÿbëº_ü$~m¬©ö¶ý„}—ÏÝåyù^ýÙ£ö`ÿƒˆ¿b¿þÍ?ô?øNÿáþÆðΛcÿÏö.»ªÂ9åZÄŸ`ûoØOÚ¼¾WŸ“ælÝ“šáâßÙ§þ‡Žø9Ò¿ùYGüAûû4ÿÐññÏÿ:Wÿ+(ÔZçÿ'~ÅôZ?òÑ×ùø‰;ö+ÿ¢Ñÿ–Ž»ÿÈáŸñïìÓÿCÇÇ?üé_ü¬£þ ýýšèxøçÿƒ+ÿ•”jçÿ'~ÅôZ?òÑ×ùø‰;ö+ÿ¢Ñÿ–Ž»ÿÈáŸñïìÓÿCÇÇ?üé_ü¬£þ ýýšèxøçÿƒ+ÿ•”j)þÏðrwáíz‡ü/ßñŠÿð“xƒì¿ñHÁÿ ¯.÷û+ýE‡ÛþÿØzþóþzq¾¿Gÿâ$ïØ¯þ‹GþZ:ïÿ W†Ä¿³Oýÿðs¥ò²øƒ÷öiÿ¡ããŸþt¯þVRÔz¿µOüwûj_ð­ÿ°gо/}ŸÇZd×?Ú~Ôm¿á „y»¼Aoö½5üÙí26ÇÙ›Í;`çÕ¿â$ïØ¯þ‹GþZ:ïÿ W†Ä¿³Oýÿðs¥ò²øƒ÷öiÿ¡ããŸþt¯þVSÔZçÿ'~ÅôZ?òÑ×ùø‰;ö+ÿ¢Ñÿ–Ž»ÿÈáŸñïìÓÿCÇÇ?üé_ü¬£þ ýýšèxøçÿƒ+ÿ•”jçÿ'~ÅôZ?òÑ×ù¾ÿ‚ÐÿÁÉßòM¿áÏß?è)ÿ oüR?õçö/ù Ø×ïúŸøðW¹ÿÄ¿³Oýÿðs¥ò²øƒ÷öiÿ¡ããŸþt¯þVQ¨ô;¯ÙƒþNý˜?áš~ÿÃIühÿ‹‹ÿΛÿ WüR:·ü…~ËÛ?ãÚÃÉÿ_æÿªýß÷~\Wsÿ'~ÅôZ?òÑ×ù¼3þ ýýšèxøçÿƒ+ÿ•”Ä¿³Oýÿðs¥ò²E¡îñwìWÿE£ÿ-wÿ(ÿˆ“¿b¿ú-ùhë¿ü^ÿ~þÍ?ô<|sÿÁΕÿÊÊ?âßÙ§þ‡Žø9Ò¿ùYF¡¡îñwìWÿE£ÿ-wÿ(ÿˆ“¿b¿ú-ùhë¿ü^ÿ~þÍ?ô<|sÿÁΕÿÊÊ?âßÙ§þ‡Žø9Ò¿ùYF¡¡ÃÁn¿à·_³í{ÿÀøðïöwøÿ Œ|Cý•ýŸ§ÿÂ9«Z}£ÉÕ¬®%ýíÅ¢D»b†Vù˜gnI÷?ðgïü£OÇöSoÿôÕ¤Ñÿ~þÍ?ô<|sÿÁΕÿÊÊûcþ ½ÿÞð?ü÷àv«à€:¯Šõ}W×fñÓx‚æÞâån$··•Z!QËHÈIÉnH Pº±ô QLAEPEPEPEPEPEPEPäÏÅÏé¿f/ÛGã×Äâ×Ã_k¾ñ9¿ð¤ ƒìVö2ºÙ$!̉Q7Úx`@¯Ôåωü¡êZÄb½CO·¹ž0¥B;ƬÑ‚HÅxÅ/ø%¿~+xÿÅ:–©â/i¾ñþ§i¬ø·ÁÚ~¡oƒâ{»`%¹F·k„Ü!„H°O Ëå/˜­Î~”D XÀUQ€à ){”#ºPVóŒe/û}ëÞÉ7«i:Þýg5Þoå)'ÿÛ‰4ºk¦‚ÑE(¢ŠG$!Ú2qÀõ¯ÌÙáÂþÁ§áÞ¹ûoþÌ? 4ïøÛÇ·–øþÚîÇXñN«jZ…ÌÖOxË÷Q7š!Ya½™;WôîöÎ-FÎ[{øÖX'CˆÃ*êF#Ѓ_3|6ÿ‚RøáÆ£à»Vñ_Ä={Á µVÖü%à­ZþÒ}Ã÷9ŠHŠÚ­Üâ1ü¥¸¸™cÏÊ&)ûDïmµò¿½n©Ûáé}ì–®m:RŽûÛו¨ù5wïu¶×>"øwáû]/þ õðö¡Óâ·ÿ…í⟊¶7Ú§ˆ×-¨êßkï§Í¤É.wµšÚ¸ms±|•!C ×ëÝ|Óá¿ø%§€ü5ñNÔ­üCã» h¾)›Æº_§¿·>Óu™]¤7q"Û‹’Y$•`{†…$veŒcéjÕI{>T­ï6—ò®XEGäã+[K4÷m%SÞ¬çåk÷|ó•þé/Gt´I²Š(¨¨u g¼°ž{‰­$–6Ež ¦HI¡Õ”‘ÔnR29qSQI«« ;;Ÿ—Ún·â¿Ù-¿à£Ú¦ƒãxÇÅÑt½BÃ^×ÞÝïÒfÐ¥™A ,M!ؑĊ¡G’p¾:xRÃþ ¥£ü ñwì}gi¤kþ,øoâ…ñ,Öê]üY5·‡ŸT‡P¿`w]\%Ü!„ònl\2ä~ƒè?±¿ƒôˆ¿üE¨ GWoÑYÁâ=>úHÞÈÅmdl–8QcWUx‰ß½ß$ðTq\OÁø&Wƒ>ø§HÔüAâo|AO hø[Ã6>+¾¶ºµðΙ>k{e†Þ&“|iF[–ž_.5MøÈ9J•9E;I¿gs‹Zwr_+½ÔSÖ„% 5tœ›]ùœöï,ž½l–µò·ÁŸƒÚì©«~Ã>0ø# ¾*ø¬ÃKñ¾£ºñ¬wº$×ó\ê2ç}Ô‘ÜIJ¤’ò÷¤)Å~›WÎ_?à™ž øã¿ k6¾(øâ«O‡W:‚t}Q·¸ÓüÀÛ"Ùˆ­ã–Cåb{™'tŒlVQ_F×mz‘©)8«')4¿•7¤{i«ÓKÉ¥µÎJp”9TÚŠMÿ4•ï.úÝ-u÷BŠ(¬ B¾!ÿ‚‡ü>Õ4ÿÛÇöSñ,þ8ñ}Ö—}ñÙGáyÍ4[6=ó„Xí–âI‰CóMÞ¯;øÛû3è?|sðãÄ/»Õí¯>kçÄzRYËEqpmf¶Ùp6-ˇ8B‡p_›Ž•)ÏùeòRMþ kN¤?š_7—ãcãÚËöu×´ßÛãwÆŒ¿³7Ã/Ÿt¿ èãM¸ñN¥e.¡¥Œ7^ÿeYKeuº]×e&{@æÃ6A_:ð߆¾ÁDt¯Ú·âWˆl­g²ðW†tË_†W2Ä ›À¶?ðÅ©Ás§¦qe9šes$EIòBgjâ¾ÇøÁÿÌð¯ÅoøïXÑükñ#ÁV_`†ßÆÚ7‡µ X¬ÖY­Ý¡{ImÙÐa‰<Õ/‰ðJ‡ž:ÔuUðv¹ã‡ú‰ô+/ ø—Ãþ¾·´Ó|M¦Ù§—½ÊËo$±m‡0-¤‚F‰Š#¦*œ)A;IÆÑË'¼ôß´ZJV”›Jö7U"ªÂm]&œ¿½Ê”uôm§îÞ1Wê½/öø‘¬|býŒ~ø³â ×|Ká +SÔ©R÷ZE$ƒÓ,Äþ5êUOÃÞ²ðžc¥xnÚ+-;M·ŽÖÖÞ!„‚(Ô*"ŽÀ(}*åvbjF­iΠɶÒì›ØâÃÓ•*P„ÚI?’ (¢±6>jÿ‚’|8ð‰ü5àÏþ×Þ6Òô„^Õ¤Õkú¼kàíWᦡ>«£É¡&™4FêX¼‘4ÐjV7Q;ƆAl ŒG8#7Ç_°|~Øi~)üMñ6µ k°x‹ÃÞ'˜hÖ:džï!FEkSe¦ÁnÊUäVIá˜8‘•\¨ûÍ®«O.hI¿9{ª×²²åë¥U|ñQOìÉ_³q©¼—¿vÕÝíüºþ~jú-²ÁE¥ý•`†Þ?€×m¯Ûé˜ôá ðÃêo¤ÔìfíSjÎvíÚq_`ÿÁ7¬"øWûNþÓß üV^ð/‰ô«¿é°&Ë]j\W–È>X¡YAu‰@Tó[+§ø%§€åøs&™7ˆ|vþ-—Å«ã¦ñÑ¿·ÿ„“ûmSÊ[± ·û0Ü¿‘åü¾V3^—û2þË:ì» ëÐøgQ×¼C­x·V—\ñ½®Oú–·zê©æÌaŽ8,qÇGqÆŠ€*Žst_³¤õäQ~nÔUþ\’WzÚÏíJÓZÓ’qZ_î÷ª»úµ8è´ßùcK¢Š*@+/ÇF¡âêöÔÆ‹ª^ÙMž `óŌ̌©7—¹wìbnåÎ1‘Öµ+âG‚“âOÃÍw÷†§¤Ç¯ióéÍ}§H±ÞY‰ch̰;«*È»·)e`¥e^.tå¯tô½¯óéëÐÒ‹å©ݵZÚÿ‡_Có3áOÁ Fý¡>é?ðM këσ¿ðM«ƒÿ t߇qü[ø¯ü1Óô§Ðäð†¥§øi4ëë…¢x'–×F†è†Wbγ¬ŒÄ–bIÌŸà™øaâÏê>(ñÄ?ˆ? íf²ðn‘â‹Ë;‹ Ç,fäˆ-"–áÖ䬗RNèœ+ ’t¬¥8ÔŒ%ñ-íoç½’ø>-"›õ{{ó $ã)Gn—½ö·3ÏšM'mþïÇ~é~ ~Ã?¾¤Iñ+⿉ôË?ëŠKßxÂcN¹¸¼óg}ÈŠEGŒ9a”í¿UkæÏ‚ßðKßüñׄµ=;Äž=×t?‡3]Üx/Âú¾¡o6á).C,h±Û¤òI$Ž?´Í8‰]„{{}'[Õ©ó8«')4¿–-E(vѦôÓÞ1ŒZ’»»QI¿æ’rnOͦ–ºéèQEbhùÓÿdýŽeÓ~|oø«¬xG[øÅâíwIŽ?^ZÅmjßà´·Rní¥–ì\óÄ—LöP´ÎTFÊTn¯ÑjùÃâ—üOÿ¼uãícÂÞ=ø™àK?ŠÉxÏGðíõ”v>")·.ææÎií]àQ½¤°3(ÃucVù4vv}žµIꜣï%¢Ýµ½ Æœ“žªê뺿Üû¤ýÖÖ»#äß…_|%ÿø«ñ×Pøÿ¨Ù|B°øðßÂö^ Ön‘ÓEîŒ×ók6i'6×rÌÈâàm•D*»†® ö Ðí¿à¨žø›â?Û~+ëøYá{Üße¤Ð¥ºÒ$¼ŸVµqy$Þ[ý©6¸òT9û—â'ü·À>+»ÏÃxïá•ß…m|«ØøNþÞÚß^Ñ­X-nEż̥äŒOE8I|Ìñ3þ ià?jO/ÃÏøïᥥÿ…­¼¬XøNþÞÚÛ^ÑíX-.Eż̅äg¡˜$Œ¾fbµU•=9•£Ó–_¼¼ôÙË™_—UyoËl°ÏÙ{=yR¿šý×»¯nIZú=.ýé[£ÿ‚eüR×~6Á>> ø¯âeÄ—zþ¹á; ›ë™ßu)…A•³ÕŸÉî[5îu•àOé? ü¤xsÀ–Péº&ƒeŸ§ÚE–Öñ Hã\ó…UŸJÕ­ñU#Z´êAY6ÚôlçÃÓt©BÝ$¾äQE`lxÇíµû.·íaà=CÖµ+±ám#\‡Zñ‡ ¶Yá4´$a¥HÏl#œ­~˜þÔ²†ûTèþOê^ ðî·àÝb={Ãúö…q:Žzˆñù‘£–VŽI£–9#eb ž1åZ—ü[À~ }GZñg‹>"꿵 zËÄ‹ñkûHüAgygÚù ªYG qM4~GÙ|¦Y_z19©¤¹&äöºvÑÞÒ¥+ùüqzYhÓ”šºž*=l×¥ÔÕ¼¾;©/zïTÔR&ürñï…áÓ-g ü!ðoÀm[Wø»áO üCÒ¼b‹J×ô{ô¸¹ŠH/­ímdlÚÝÄÆàŸ¤?c_ 鿳Çü«ã‡ÂÏ‚¶:Ã¥ð¿‡üUk éÐ-¾Ÿ¢_Ü=å¼âÚÄp –Ö)(, c$“Ù\ÿÁ,¾ëßük¦øÿZñ¯‰|OãíZË]Õ|i{}oˆVúÄ©°šÞ[kx ·û1AåG Ëesg¿ý›¿döq×+ý™tþÓ>ø«ª]êÉâhÚŽ‡em±‹)`½xV• eÙÁ¶iWP2Ù ‘.øoÿ³ðÃOøwP±ñŽõ/ x+Z»ñ…ünþðæ¡q¿|öÈ–ëpû<éÌqÍ<±Äerм`ŒmNŽ[^©*³›]Û©+½›IK¸å+ʤ¥ªwÓ£nšŠ~J ;u³m;Ù|ðmÃÏØóöKý¡<&ÿÂåøñH>)ñ"eµÁ¬ÜÍ å•ܹß4ÆêR%b6ÊT.Òkõ¶¾iøkÿµð'Ã/xsQÓüEãÍGÂþ Ö®¼Eáß_Û¿‡ü7¨\oÝ=²%ºÜ>Ã4æ8æžXâ2¹ESŒ}-[Êqqj*ÉÉÉ/勌‚òN-馽îfÓçM»Ú)7üÏšmËæ¤·Öé­’l¢Š+"‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( âÃM;â4/Üø‚ÒÊå‘Þ]^¾Ñ/«.ì&†t!d†AÈ$WÃ?±?Âë)õŸÚS_ñÏŠ~3x„|%ø‡ªiÚ çżØÛi¶“¥³£j “ òÊwL²7ÍÎpý¯ ø7û¤Ҿ9ZÿÂIý¡ÿ ŸÄÚ‡ˆ·gù_Øÿj²‚×ÉÇšÞ~ß#~ÿÝçv6ŒdáQN*³§ñ:rQÛãæ§Ë¿[)jôµÕõ³Ú›„5SnxßüþWqÑu³è|½ðÏþ âk„Wüø*u}ÀŸ t‰:´ÚßÄ‹‰n¡±¹·žV¶[›‹)¦½ºE¶m¯3(— ]ã ô='þ õÿLrê?µWÃøü áýCáÄÿ4+«fæóM†H•­î û4+mvEͱ “Æ|Â<Ñ´æ?ƒðGïøTŸ |{áoøXŸÚð›ü$Ó>ý«ûÊûØ­níþݳíMæoûfï'+·ËǘwdmüKÿ‚Kh_bð•‡Ä¿Ü]è~øOuðºîÖßOÍz³ýŒ‹øæ2°Ñ¬•–2’ °ù¾\7^!§V«ƒ¼o.W×UZÛöj¯Ýß­°¢’5?îßåìy¾ûÖÛ²µ½ÛÕý”à®:íûDxáÖ®¿ ?µ.ë>*ÿ‚‰|+øQá}KUÒô}'@Ô~"xŒØÊñ>§Gggfí-Oq$¯Gû:)KÐ|ý™>$ø3âN•âÚã%ïàðÞŠÚ.—¤éZ;è6Šn½Ôás‹ëͱ*‰•nr‘)rkŸøíð‡XðÇüCágÅo éÚ¶©£êú£ðïÄŸ`ŽI$Ó"¸’;Ë;ÆüËÏo$O'Dóщ1;sÒäþÿßiû?ü›ÙíóêL9½N}ì¾ïwŸÿ%çß^ÝsöKÿ‚„ê?·´†×Ãðý—„µÕÔ4û›#âoâ¿ ˜·Æ­¥x%°‘ˆP<‰î$¤Œ —^Ëþ ‹ñÇ\øÑû4ÏeñVòmOÅ¿|EªøYÔ&“RŸM¹håú|òÄ!‘¸3µpž ÿ‚sj¿ ~+x+âÆ_ÁãØþ ¦¡{¢Ý[ø=ÇŒue–ÖXZ¦¬.æ—SÛ íÇ-+¤E·•Áîÿà˜¿µÏ‚ÿ³L÷¿læÓ<[ñÄZ¯Žu>c™4Ùõ+–mŸ“óņ6äüÈÔáËï‡^üÜÞïþKí/m6»nÅÖéoæÓü<²æ·]ýž¯WÙj}ETQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/zkservice.jpg0100644 0000000 0000000 00000251406 15051152474 031600 0ustar00rootroot0000000 0000000 ÿØÿàJFIF``ÿíPhotoshop 3.08BIMÿâ,ICC_PROFILEappl mntrRGB XYZ Ð acspAPPLapplöÖÓ-appl cprtˆ+desc´gwtptürTRCgTRCbTRCrXYZ gXYZ4bXYZHchad\,XYZ óQÌcurv3XYZ o¢8õXYZ b™·…ÚXYZ $ „¶Ïsf32 BÞÿÿó&“ýÿÿû¢ÿÿý£ÜÀntextCopyright 2005 Apple Computer Inc.desc sRGB ProfileÿÛCÿÛCÿÀ¹XÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?þþ( € (››{;yîîç†ÖÒÖnn®®eH-í­àF–içšVXá†Õ¤–YR4Vw` šþ\ÿiïÛ«âWíeu«iÞñ6µàOÙ«VQo¡x'FK]/\ø›áèf3Xx£Çþ$ŽØø–ÊÇÄ.°jÖ> ðÞ¯£é«¢ ;OñKëÆïX°Õxsƒ°±¡‡ÇæquëUŠ« ,¿J3áíbÕêÔ³æi¾HÉ¥Ë'güãOÒK>­šç%ÀÕ£•exóËñ9ýÞiŽÄaê¸âÞ´féà°n¤u!Š­MN§´¢ªªtÿ:áž¾Ñø?ÿ†ÓÁü¥¯·þÎËÿèÿ„Ôùùoýsãú+8—ÿ¹§ÿ5ü3×À/ú!ßÿðÚx/ÿ”´geÿôƒÿÂj?ü€®|aÿEgÿá÷4ÿæ ÿ†zøÿD;àÿþOÿò–ìì¿þ€pøMGÿõÏŒ?è¬â_ü>æŸüÔðÏ_¿è‡|ÿÃià¿þRÑý—ÿÐÿ ¨ÿòþ¹ñ‡ýœKÿ‡ÜÓÿšƒþëàýïƒÿøm<ÿÊZ?³²ÿúÁÿá5þ@?×>0ÿ¢³‰ðûšóPÃ=|ÿ¢ðÿ §‚ÿùKGöv_ÿ@8?ü&£ÿÈúçÆôVq/þsOþjøg¯€_ôC¾ÿá´ð_ÿ)hþÎËÿèÿ„Ôùÿ\øÃþŠÎ%ÿÃîiÿÍAÿ õð þˆwÁÿü6ž ÿå-ÙÙýàÿðšÿ ëŸÑYÄ¿ø}Í?ù¨?áž¾Ñø?ÿ†ÓÁü¥£û;/ÿ þQÿäýsãú+8—ÿ¹§ÿ5ü3×À/ú!ßÿðÚx/ÿ”´geÿôƒÿÂj?ü€®|aÿEgÿá÷4ÿæ ÿ†zøÿD;àÿþOÿò–ìì¿þ€pøMGÿõÏŒ?è¬â_ü>æŸüÔðÏ_¿è‡|ÿÃià¿þRÑý—ÿÐÿ ¨ÿòþ¹ñ‡ýœKÿ‡ÜÓÿšƒþëàýïƒÿøm<ÿÊZ?³²ÿúÁÿá5þ@?×>0ÿ¢³‰ðûšóPÃ=|ÿ¢ðÿ §‚ÿùKGöv_ÿ@8?ü&£ÿÈúçÆôVq/þsOþjøg¯€_ôC¾ÿá´ð_ÿ)hþÎËÿèÿ„Ôùÿ\øÃþŠÎ%ÿÃîiÿÍAÿ õð þˆwÁÿü6ž ÿå-ÙÙýàÿðšÿ ëŸÑYÄ¿ø}Í?ù¨?áž¾Ñø?ÿ†ÓÁü¥£û;/ÿ þQÿäýsãú+8—ÿ¹§ÿ5ü3×À/ú!ßÿðÚx/ÿ”´geÿôƒÿÂj?ü€®|aÿEgÿá÷4ÿæ ÿ†zøÿD;àÿþOÿò–ìì¿þ€pøMGÿõÏŒ?è¬â_ü>æŸüÔðÏ_¿è‡|ÿÃià¿þRÑý—ÿÐÿ ¨ÿòþ¹ñ‡ýœKÿ‡ÜÓÿš&ø«û)xÄÖÚ4 ´€ îm'½}jç[ý™¼ñ)5˜&ŽÝl ··¸Ôü(4–³‘.$’e’ôÝ‹„Bù¤äÅå4ê¨}R~§.wS,¡‰çNÜ©':<œºÝÞ\×ém~‹‡¼CÆàjbŸb¸Ãˆ!V4–^;Íò7†œ\ÝYNp¡˜¼JªœbãKÙ¸7yóÚ?#þÏÞ ´Õþ"üYñ'¢ý™¼yû2ü)Ñ5MûâJþÌ_¾XëÞ?ÑæûOŠ®<7ªC¬x¢ßPðwÃë;+Ý3Ä:ôÓ[Ú]x†[­>ÁfMîäù}Ï‹«ˆþÌÄe˜HJÄÿeá𱩈ƒ½WN|õT¨áâœ*Tm'Q¸ÆüŸ£qŽiW’ðî'—åwÄ8ªšY#ãÌç=«ƒÉñ1äËᎡ,6ts,â¥ZXŒ1Jx%Nµg‰§è†^+ý†~-xžÏÂøIà{sYÒ®µï ÁãOÙÒ_ÛxçC²òÞ³à=CÆ^Ñ,|g§AÑ\Éqáë‹õ[IÞ>̯*úZÙ.ª£K B3œ]JJ¾Zðê½8ÚóÃʽ j¼UÓn›–ŽûŸeþ,pî®eâLÚ¶ ˆ§„Ì'•ñªÎ'”âêó{<6oG,ͱUrÊó”eà -Ô‹¦½ûEå¯ÄoØ üf<¿ ¾üÞ+ÿ„ ßÄÚÀèÿÙ«â1ÕÓ€7_»¨úÏûocõl7ñ¾¯íÿ³ÿÙ=¿7'±úß°ú»©ÏîrûKóû»$ñ…e3yîw¦_ý®ò¯õɾ!YW²öÿÚ2áåšÿl,Õí‰uÞ‘aڮ߳÷ñ·~ø'ãÆï‚ºgìçðgâÄ[Y>éß¾èß üi¬kWÞ,ðeþ¹â{ź¸Ñ¦?<=y­Þ½â‹ÈÛI´t²·[»ûËX޽| v;»ˆÄ§„† -9ʵ T«R´ù% r³©VJÐŽŠòjÿM•å~¢öÑšâÕ¥Ž9 ­‘<.#ð¸hR¼Ʈ^©W£¢kÚaêQhó&œoyj®®tTËüZ†{“ðô8ƒ;Ä㸅µ’WÀñ„ñù^iiJ3x<Û ™ÖË«ªu!*uy1-Ò©hTQ”•ßðëÅ?°ÏÅÃ௠ü&𖹩iwºç†?á/ýžÀÚgŽ´=8ÆouŸk>.ð>¥øÛL¶Šh®¤»ðåÞ£Ø_íÃ6‹$ÈaªäXªÊ…,&5%T¥í²ïa𹧇j…x«¦åJRV÷¶Ô3¬‹]<ÓÄYÅ\% E,&?û7?µkå8ºüÞË œarì׈ÊëÔ”eN4ñ´èËÚ¯díQ¨¼íÇß°_‰¼_¥øGKø]à·‡Ã÷‰î¡b Ñ-ïµmz} øfÊ÷YӬRÖ9es¶'ŒÖ– °´èá2ÚÅâ=ƒÅbp”eG•*•9¥AJu*Q•H§RjíìupaÄìøƒ™qf´xw&þÕŽC’ñgK4Îg,vì¨ÕsÅÎŽ T±Øú´°ÕªSÂáê8Åk%×øà_ÃßøCž'øécû,øãÂú=§öíŸt€Þ ø{e¦hV—Së³ø†#©x«I’ uŠ­®ìo ÙÜ¥Är1ˆ°ø >j¸øåUéA{Eˆ§€£‡ŒiÆ-Ôu5h4­u(Éi{­78âÜë7̲ì Öñ)Çâª}R¦QŒâì×9«_Z¥8a#ƒ’£€Ä©MÊq©N­)Ýò8I.c3á÷‰¿b‰^ o hŸü£ësh×þ#Ñ,üuû6Üü=ÿ„Ã@Ò•$ÔµŸMã_èpøÃO°†kyîî«…Ëðµ±د­bãK£8©¬5:sÄÖQ¥NR0üC©~Ç ü/ðÎ_ü)øsâ_ü@𞛯èºïÙÕ|iâÏZ /OºÕ|K¦x Â> ×|Oe G5ì5ÍÕŠ[éïymeq?ÚNÃ%“á©a]l&­lEÔ…<6]íëU\‘”êÃFJ±§y&Ü£hÝE¾m¼ó¼Ã=ŽYÄ9Þ/ÉóøêzOÛ¯´›Øo>xsEÖt]oL”C©è^!Ð5 Øë:·§JUot­ZÊÒö ñ»Â#–7~Œ5§KÛað˜9ÚP•ð”á8N.Ò§RœéFtçñBqR]V§žæž"ðÞ>YnoÄ|GCì©b)J—ã±XlVºr¡‹Áã0Øê¸l^´St±zµ)NÒJWŒ’ôøg¯€_ôC¾ÿá´ð_ÿ)k£û;/ÿ þQÿäýsãú+8—ÿ¹§ÿ5ü3×À/ú!ßÿðÚx/ÿ”´geÿôƒÿÂj?ü€®|aÿEgÿá÷4ÿæ ÿ†zøÿD;àÿþOÿò–ìì¿þ€pøMGÿõÏŒ?è¬â_ü>æŸüÔðÏ_¿è‡|ÿÃià¿þRÑý—ÿÐÿ ¨ÿòþ¹ñ‡ýœKÿ‡ÜÓÿšƒþëàýïƒÿøm<ÿÊZ?³²ÿúÁÿá5þ@?×>0ÿ¢³‰ðûšóPÃ=|ÿ¢ðÿ §‚ÿùKGöv_ÿ@8?ü&£ÿÈúçÆôVq/þsOþjøg¯€_ôC¾ÿá´ð_ÿ)hþÎËÿèÿ„Ôùÿ\øÃþŠÎ%ÿÃîiÿÍAÿ õð þˆwÁÿü6ž ÿå-ÙÙýàÿðšÿ ëŸÑYÄ¿ø}Í?ù¨?áž¾Ñø?ÿ†ÓÁü¥£û;/ÿ þQÿäýsãú+8—ÿ¹§ÿ5ü3×À/ú!ßÿðÚx/ÿ”´geÿôƒÿÂj?ü€®|aÿEgÿá÷4ÿæ ÿ†zøÿD;àÿþOÿò–ìì¿þ€pøMGÿõÏŒ?è¬â_ü>æŸüÔðÏ_¿è‡|ÿÃià¿þRÑý—ÿÐÿ ¨ÿòþ¹ñ‡ýœKÿ‡ÜÓÿšƒþëàýïƒÿøm<ÿÊZ?³²ÿúÁÿá5þ@?×>0ÿ¢³‰ðûšóPÃ=|ÿ¢ðÿ §‚ÿùKGöv_ÿ@8?ü&£ÿÈúçÆôVq/þsOþjøg¯€_ôC¾ÿá´ð_ÿ)hþÎËÿèÿ„Ôùÿ\øÃþŠÎ%ÿÃîiÿÍAÿ õð þˆwÁÿü6ž ÿå-ÙÙýàÿðšÿ ëŸÑYÄ¿ø}Í?ù¨?áž¾Ñø?ÿ†ÓÁü¥£û;/ÿ þQÿäýsãú+8—ÿ¹§ÿ5ü3×À/ú!ßÿðÚx/ÿ”´geÿôƒÿÂj?ü€®|aÿEgÿá÷4ÿæ ÿ†zøÿD;àÿþOÿò–ìì¿þ€pøMGÿõÏŒ?è¬â_ü>æŸüÔðÏ_¿è‡|ÿÃià¿þRÑý—ÿÐÿ ¨ÿòþ¹ñ‡ýœKÿ‡ÜÓÿš¿gOÛcãwì¡öK-3QÕ¾*|Ó]îõ¿ƒþ$¹Ô-ðç|+á¿x?W´×ü'âý Jñ7†µËíg«èZ化•¨Ûy©¢Ë+˜gDš8æ@û%Ž9‘%i¦ÓM4ìÓÝ5º~gú%FqŒã%(É)FQjQ”d®¥®šiÝ4ÚiÝ!…P@P@PçGüƒVÔtŸØ?âçömäÖg\ñwìóàÍXÀÛM÷…ü}ûJü ðGŒtY›ý“Äñµ¡ß+!²ÔnRG!YÕÈé®s–S©8O‡RŒµR^Ñhü€ñW‰ÀxmÇ8ÌiáñX~Î*P¯MÚ¥)¬T§Ó´’nϦëSùίè3üv ( € ( € ( € ( € ( €>:ý³nþ>]xÃþ øðûÆ^-¶ñÖ¹&‘ñCÄ~ñ€¼?ãOü=Š“WxÃÁºü%(LÕbÔeÿ„zÜê±ïbÓ£—ÆÎž`èS¡ÃÖ¬«ÍÃSWN½=½ÿbñ¨GÚÕ¿$f¤ýšæ¹”Sý/Ã|!O6ÆæœWœå™uL§ NC‚Î0Y¾3,Ìó™M¬;Ì–Q–æu¾¡€åúÕ|<¨Çë³ö8~uJUå4Ô<9ã‹?5üQýžõ/|—àg‡þëºßµíoRñ§¼LÛÞö¶>¦\IÁoŒjx§þ´7Šžx¥ÁÙyŸö¢Í§“¼»û2XïaýŠòH×wöÿ]u³’ÃýIÕÐõ/~ÉZ÷Ä/_´ÅH'¼øgã;àæ­û?|fÒ&Ónu; WÃ^Ô´éz–™ox׺§‚u Ùmt¿xS^†ÒÃ_´džÏuÅ­µõ¿Ul¢¦#˜bÓxZ÷ÁÏ/ÆÁÅÍJ• B¬'ܧBRj5iTJ5¥b¤¾.ñ“ppüãO=ʹ8›Æ<1ˆzt*áñÙµN_ˆ¡^tÕ*¥Q©_/Ì0’©[>hU´*N”ü'Äÿÿhß|QŸãWį‚_/u}cÀ^ø]⟠~εü*+¨u¿‡Ú牮ì¼sáétïŠ>°ñWÃïYøœÞ[é~0Õtÿø:þÂ{eÒ.c»šîû‚¶2­ŠxÜNç<=<-ZYnj°mTÃÔ«(צãŠÃÆ®º«uÒj2‹\Ž÷—×`8‚²ì‚<-‘ñO ÒÃa³|f€Çq¯®$§,.s„ÀÓ«•c|‡7­—çM\²|¶…l¿2£Z&œiÒë|yû$ø…¾|Ö|ðûâ¾—}ðßâÄøÓáí+â=+âÞª~)[êvÚî«añÒ×Ŷr_xÒæ´Öîlïü]©A¬kÚDúãd]\íˆÊ*}W/ž‹„°ØœMzøHæucŒ—ÖÔÕIGªÇšº—,Ú•e )Ôƒ©Õù¹Gˆ¸%Ÿñ†8Îx{K;ɲL§+â:Ü‚Äpæût'„ÃÖá:™uUG+•k……JYt±4'‡Âba…VtáÆüTøP¾ ýŒmMð»âïÃÏxóÀþÒ¯£øíñÒçã—‹|E¦ø:{øGä»ÔÛÆß¬´ +Ÿë6i§ÙxŠí®`‘g#òá†xwá¾/×µ¿Kã?x‘|C¯k ü[ð¾Ë@øocá-^ûV§þÜÞøªÓÂ÷DhZ=·ö@¹Œ2ú(ãó*¸HWÁ<<'¶©:îµ ¾Ò¥L-l*§†Êj—ïW*±¤ýÈ.KŸ%…̸O‚òî$Åe|Q/Æñ*˰˜\®9ni‚ú– ŸeÙõ\fwW1ÃÒÃË0ÿ„Ø`¡OSNø¼MO¬ò=|à¯ì©â­O„ÿ ¾'üý£µˆ>ë¾ žïÇmûdøƒXýŸç¸ø}ªYê¾ñ~ð¾ÿâŒÚ¸‰/´};V´ðMÇÃk=;K½Ûaot––é8ó°YMjo †Å`³)¬=J.Uÿ¶ªO/o8ÎhaeŠu-ÍÍPxhÆ2÷SI\úÞ(ñ/Æ."Ïr(àœ4ó¬&g yOüC<Œc â…\>7-Ågôraœ,Ml=LÒ9ÝZÕé^´àêNPzüø{â_øóö­×üG¡®—įÚ/xJüÜé·2ëÞ·ø'ðsÂÖ÷çì7W76ÃâOx¢Æ+Q,ïc’Þâím¥íµÍÏÐ`0õhâ3j•!ʱY‚­FW‹u(¬ ’—ºÛIU§V*3´“NV´“‘q†sÍ2px,[ÄO#àÙe¹JÐŽ2Ÿñ.a:?½„!RRÀãrú²«AÔ¤ÔáMÔu)T„:¯Ž÷zÄ~‡O²øŸ´V‹«Þ›?øµ?Ú•ÓDMèÒþ$ÝiÞ×—íI/cwªX4bAp&Ã×çì”V_ý£ Ë–­l:÷m~n\K*šéË)ǽÏ?„©á¥˜Îµ^/|ŠÃRö¸ÕPÍê^»’Œ©:ù%:øü'îܤªÓÃÖRk‘¨ós[þÊ?|ðëö¥ðç…~ÿÃ-x#âÇü¤xà¼þ2Ò§µÇ¾×5ë1»·øo¬k^ø}iã;aáßÞÙø?Uº™,t™5‰T\´PŸŸYN#†ÍiRÂÿePÅС >׃Ö(Ô•I×k :”°ê²öTd¨Î^ìÞº¯ÏÄ,Ÿ'θ˜gßëökÃÙ®k‰Î8¢f"–S˜á(à°Ùm9çxl.?9«–Mãs:Us,=8ºØ…‡MÚG[ðKà^¯qñÃþ(ñ‡ÀßÚ_šÿ€|9âùt|cý¯5³ñ'ˆ¼3wáëOxZçâŸ^ò [JÕõ8WX×t? Mg VÒ= ¹Ø¶û`pxŠuk`s:U(R¬éÕÆçÆÑZ”*4ž*½Ôá9%:¤ÒI¸ßo?Šx³ ›Ëx¯³ qŽËV3Ã^Pá|ʦ§˜Ò©™cáejœðØŒ5 K „ÅcãVr¨•G ¹ûïÁ_€Ò¿ì9ð×ösø¡¡&…7ìõáφÞ;Ñ#›N¼}#[ŸÁ¶ÚV¿åÝéW7zmÍýŽ®÷7i¨i÷—I½·»‘ŠÎ} ÿaá²ÜU5 <º–Nñ—%GAB§½(¹Fw|Ñ“\Ë™7¹ñüQÅÑŠùçä·‹£2Æç™N*Q­Mbp±ÌçˆÂ^ž"ë•\2…7Fµ(IR—²4¯æïüý£¼gðãöñǬgý›5Ù—á&•ÿ ‡uFñ –—àz-sÇj𵯛¤ÍñwÆÚ–™pñj×ZeÕ•Ž…¦ÿjÛéùœ2†2­†Ìªc¨ÛòÉex8{ZSö‘ œõÔ£7øãoøÅ> мiàýgÂz´þ&‡ZÑ.¼c®hžÕô­JçU¾Òµø·w%‡Ã_xÆÿþ~Ðòü%ø•ákc]Ó\5ZðxLÇꘚNu#RjuèR­¬\jÂU”~EË”ôx3‰² “)â^þÜáÊj¶y‚Ͳ®"â> dY„0ØJøÇnE=EgV•:ôçJ´#V•EiÓšRŒ•ïi'£W;0†7*Æaó »_ŽÂÏÚa±Xz’¥^…K8óÓ©¥rÉ«§{6oÖ‡P@P@P@P@P@P@P@P@~ïÿÁäß>0ÙËøËÿU¿ÂJü/‹¿ä¢Ì¿Å†ÿÔ<9þ®ýäÍpWýxÎõ£ÎÖšù³ö° € ( € ( € üØÿ‚¸ÿɇüGÿ²«û%ë`|¯c‡ÿäy•ØvÿN#óäÖñÿý’¹Çþ¢T?êþ€?Çð € ( € ( € ( € ( € ( € ä|sãßü2ðÍÿŒþ ø›Gð…4¹,aÔ|A¯^ŧévrêwöÚ^Ÿ÷S=æ£yken¤æ[‹ˆ£PYÀ®l^3 €ÃÔÅãq4p˜Z):¸ŒEHÒ£MJJ ΤڌS””SmjÑíðï ñg>á|›2â ÷0•Xà2Œ£_˜ã%FLEXá°˜xT¯ZTèR«VQ§ 5Nœåk&x7ü6ÿì‰ÿGð«ÿ «þ.¼/õÓ„è¥Èÿðç„ÿå§êßñ,ßHú2>(ÿâÄüÂðÛÿ²'ýO¯ü*¬?øº?×Nÿ¢—#ÿÞÿ–‡üK7ÒþŒŠ?ø„ñÿ0‡ü6ÿì‰ÿGð«ÿ «þ.õÓ„è¥Èÿðç„ÿå¡ÿÍô‡ÿ£#âþ!(ÿâÄüÂðÛÿ²'ýO¯ü*¬?øº?×Nÿ¢—#ÿÞÿ–‡üK7ÒþŒŠ?ø„ñÿ0‡ü6ÿì‰ÿGð«ÿ «þ.õÓ„è¥Èÿðç„ÿå¡ÿÍô‡ÿ£#âþ!¨-À  „˜_¦]œeYº«,¯1Áf1 à«K‰¥ˆT¥QIÁTt¥%%8§fÒlø>2ðßÄê`(ñçq/UÍaˆ©–ÒâLŸ“ÔÇSÂJ”q50°ÇQ£*ð¡*ôcVtÔ£ T‚nìõZôŠ ( € ( € ( € ( € ( € ( € ( Ýÿø#¯ü›çÆû9ê·øI_…ñwü”Y—ø°ßú‡‡?Õߣ¯ü™® ÿ¯Ïþ´yÁúÓ_6~ÖP@P@P@›ðWù0ÿˆÿöUd¯ýl€õìpÿü2¯ûÃÿéÄ~oãüšÞ?ÿ²W8ÿÔJ‡ó½_ÐøþP@P@P@P@P@P@ŸðT?ù2ï‰?ö2|$ÿÕ·àšüßÅßù7|Mÿ`´?õ7 k~ÎÏùL¯ìyœë/žÌŸçýyo‡5YÃ7Ú®¥<×ÇQñt’ÈäÉ(´Óuýa#rrD6–é Kžïf¸(Ë6Ã`ð´éÒxœ.KF)BÛ—`œ¦í¢u+U”ç.²”¤õgå<ĵéø}œq&}ŒÅcc”gž&×Ä׫)WĬ· ã.'¥B…>gyG —`iapÔ®”iQ§N6I½Å(nEÃÜX[Cö]Î¥fÐßÜÏóÀ#h¬µú\ kqreD‹ì¯¨#K˜Ð»Ýëâ8BT(ÒÄÖœþ·K ]TÃÒ§¥G5:ø^\]GV•.G)ªË%zJ*í~w“ý#0Øèãªã²l¿ Cý_Çç¹\ð™Î?j¸HÑ–)Ïe[‡ðt²ìv=â)Òüº¦uJx…*åV£¦§rÃâ÷~·5æƒqi¨éhÑGbí{n—¯¯Ý=–™¶MSLÓnaGºFI¥{6‰Sçå†'†)ÒÅeôèæ4ëaq°ÇÎx„¨T•å´cˆÅ^8LV.”ä©IJu6ß,Ô›õ2_±˜ì“‹±Y§ã2Ì÷†kð®”Tžm‚¥›Uã\ƶS:u¸‡!È1øj53 3§ŠÄÔÊ燧O÷Ô*âR”V|¾6Ô´KÏI­Z"\ý»ÃÚeŽœ5ŸM·¸¼Óon¥˜^-Ÿ´‘Û<Ó2ØË A¤õG Âæ|® Ò•/«æ˜¼F)ácOV• U P‹ ëòJ¬gV4àž%AFNNi].#ÅÌû„s^9¯Å™m*XÿíŽÈ2|Ší|fAÇf™m˜âq0Ía•¼U z}\V&pÉe‹jQ¤°“œ¡'§oãùîôØ.-tˆ®oäñ ¯‡Í¼wò¥Œ’]ÛIuݵõÅ„2˪ªÊ$²‰Ñ„£‘CòUáªT1S§[:Xx啳/k<<xÆ…XR´)â*B5“på¯4×#ºRm{ø1¹žC…ÇeÜ1‡ÇgUøß.à§‚¡œb)åëæ™}|~2ÀfØÜ› ‰¯„„)Æž!WʰÕiMb"¡9RŒjú-³\5¼ wPÝ4QµÄPL×Å9PeŽ)Þ(hÑ÷*HÐDΠ1 Ú>Z²¤ªÔT':”Tä©N¤:“§wË)ÓŒêFjÎQSš‹ºR–ï÷l¾xê˜LÏ†Âæ3ÃQ–; ƒÄÔÆá0ø¹S‹ÄQÃbêá°uq4)Õr*õ0˜iÕ‚S• M¸/Úïø#ü‡ÿjûüÿÒߎUý]ônÿ‘oÿØv[ÿ¦1Güÿ~Ú¿ù-<ÿ²_¿õm‘»ý,ˆa@P@P@P@P@P@P@û¿ÿuÿ“|øÁÿg/ã/ýVÿ +ð¾.ÿ’‹2ÿÿPðçú»ôuÿ“5Á_õã9ÿÖ8?ZkæÏÚ€ ( € ( € (ócþ ãÿ&ñþʯì•ÿ­ð½Žÿ‘æUÿaØý8Íüaÿ“[ÇÿöJçú‰Pþw«úÿ€ ç5ïéÞ¹Ñ,nl¼Qªê^$¾¹Ó´M# ñ—5«û«=2÷Y»èÞÐ|CªÇok¦é÷—W³ÚEg ÄK…–XRD¹¥R*t«×«UÉS¥‡Ã×ÄÕ—,e9?g‡§RiF1mÉ¥ÕÝ«ûü?Ãßâ18l“ K[†úÞ%VÇåÙ|)aÝzXu7[2Åá(ɺÕéAS…ITn|Ê1“QÿÂI{ÿDÏöÿÄgý¡¿ùØ×GÕ1ÿô+ÍÿðÑ™ÿó)õñ ¸ïþ…ywþ%)ÿϰÿ„’÷þ‰ŸíÿˆÏûCó±£ê˜ÿúæÿøhÌÿù”?âqßý òïüJ8SÿŸaÿ %ïý?ÚÿŸö†ÿçcGÕ1ÿô+ÍÿðÑ™ÿó(Ä&ã¿úåßø”p§ÿ>ÃþKßú&´þ#?í ÿÎÆªcÿèW›ÿá£3ÿæPÿˆMÇô+Ë¿ñ(áOþ}‡ü$—¿ôLÿhüFÚÿTÇÿЯ7ÿÃFgÿÌ¡ÿ›ŽÿèW—âQŸüûøI/è™þÐ?øŒÿ´7ÿ;>©ÿ¡^oÿ†ŒÏÿ™Cþ!7ÿЯ.ÿÄ£…?ùöð’^ÿÑ3ý ñÿhoþv4}SÿB¼ßÿ Ÿÿ2‡üBn;ÿ¡^]ÿ‰G óì—Ãþ(Ó|I&½ogiâM6ÿÃÌz¿¤ø·Á¾/ð.¹¥ê³hz/‰`·¹Ð¼k¡øXÜh^"Ñu+kı{+ˆ/Cq$‘ΑsûÊuiÎz5hÍB­,E øj°”©Â¬oN½:s´©Õ„Ô¹yZ–´íò¼AÃYÏ â°ø,ï O ˆÅa#ÃÆŽ;˜S«…ž#„UU|»‹ ¿Ú0xšNœªª±t›”e.Š™áÄéþ:´Öc»¸Ð¼ñ—ÄV6Zο I«xkàÇ/èsj¾×5 kÖú~»¡ü<¿ÑõHôíwIÔtÙ/4Ëë»)g´”ÛÜMÙéRÄâ!í(`s*ô¹êAU£–fiJTªN•NJ”ðÒ„ÔjBpr„¥âìÚÔý áoâð¸Lm¯õ|v ÃJ¯pÖ¥L.;O…«*ŒÞ•z^ÛZUNµ*ub¦”á]+ÿð’^ÿÑ3ý ñÿhoþv5§Õ1ÿô+ÍÿðÑ™ÿó)·üBn;ÿ¡^]ÿ‰G óì?á$½ÿ¢gû@ÿâ3þÐßüìhú¦?þ…y¿þ3?þeø„ÜwÿB¼»ÿŽÿçØÂI{ÿDÏöÿÄgý¡¿ùØÑõLý óü4füÊñ ¸ïþ…ywþ%)ÿϰÿ„’÷þ‰ŸíÿˆÏûCó±£ê˜ÿúæÿøhÌÿù”?âqßý òïüJ8SÿŸaÿ %ïý?ÚÿŸö†ÿçcGÕ1ÿô+ÍÿðÑ™ÿó(Ä&ã¿úåßø”p§ÿ>ÃþKßú&´þ#?í ÿÎÆªcÿèW›ÿá£3ÿæPÿˆMÇô+Ë¿ñ(áOþ}™úŸŽí4Kd¿×¼ñ£Ãúk_iztš¿ˆg‹iu¬êvš>›ý£­jÿ¬´½2ÞãR¿´µ7ºå­” :½ÍÄ1‘s«GB­|gF”\TªÖË3 TáÏ%¹Ôž0‚r’\Ò’Š¾­×ð¯Ž0ø|N&®W‚ö8L6#]Òâ>¯R8|% ˜œDãF†qRµWN*“öt©Î¤ùmJM'ÛÔžÁE¼âß~É|1à kž.ñæ»ðÎêÓAðæ™y¬ëpi|#©ê2ÚéÖOwp¶z}¥ÕíÇ“˜íà–R6£ø_òÜ~qÁö[–aªc1ت!‡ÃRåö•d±t&Ôy¥éÊM¹$’m³ú·èEÆü)áÏÒ‹ÂŽ5ã|ïÜ-æ¹®'7αî¯Õp4*pöo†§:ª…:Õ¥í1éR„iR©9N¤R‹¹üêÃ.þÒßôo¿¿ð×ø×ÿ”µügÿ§Ä?ú%³ü ÿÍý,Äÿýèýp‡þÏÿùÌ|ßð×þMwSÒµ›hÆ©^ cÃÕ÷ˆ´Ï±_^;Þ^[j^Ô&‚;qro ËZÜØªMÊN£™¼\ßžåÕêey¾_‡ÁcðÔèПÖ2¼%,Æ”hS…<<£‹t~±xÓ§J¼*»ÁEÂn6gêäq–Uƒã¯ø·6â^Îñ™žk‡YGq;ƒñÕógí}´Ü&›ö—Mží_ 8 Ì)b©ñ#(y~"‹Ê±…ù~‰2Úþ&a±RâL>[—ñ óªüy™fX§ÖÇTËg[˜àkãèã²úØÚÞÇ ÐÄQt°ÑŒ£õJ*#ÿ‚m~ÖñÁw ý‘?iiMô¶—ÝOð¿âl÷ÿj±GŽÖî+ù4¦¼†ê$‘ÐOÉ!RT±^*çá“©BêNk‡…ztèÓÀåôðî–"Qj3ÃÂjJS”#'Npqæ\Ö¾¦4kî¾.þÏŸ´7ƒàñ–›ð† É/À?ŒÏöœ¾ºø¯&¸Š¾ð^®ö¿aOè¬Zõm–¶jg0\ˆ¢<àn1á<Kˆxk9˪cqx˜hË_íaFŽ"5%|q <²œU¦âÝýÔìíþ8~Ô^/áo¤xG˜x?ÄOàøk!âœwZ–6†L°8œÇ1Êkàé8qòŠ•ÝjXjóR¼!Éj’„¥/ÖøI/è™þÐ?øŒÿ´7ÿ;ýËê˜ÿúæÿøhÌÿù”ÿ+âqßý òïüJ8SÿŸcô_iÚi1Xx¯GÖ4ìó«hÞ2ð7|¬ÚE«C-Æ›sý“ãø{Q¸³½Š ̶¶ÓÚ;Á4Bo6)p|ð©:5ib(U¦¡)ÒÄá«áª(ÔMÂ\˜Štäã+;I'ÓWºgÌñ ç¼/S K;ÂRÂÏJ¥l3£˜eÙ:´éTöUµËqxÊP”g£…IƦª\¼­7ÒP|ðP@]—Ž,õS~Ú„¾/xšÓNÕõ} çUðŸÀ¯^,ЛVÐu'Y³´ñ†üªhš‹éº¥¥Õ…Üš~¡u WvÓÀdó"uJ–'S‚ÌqÔêSö¸|»^“)ÊHÆ­,<éÉÂq”%Ë&”“WºgÞà|2ãLÇ„Ì0¹fX\vž+ :ÜAØJ•(VŠ*’Ãâójš\ñjJ5©B|­>[4]ÿ„’÷þ‰ŸíÿˆÏûCó±­~©ÿ¡^oÿ†ŒÏÿ™N¯ø„ÜwÿB¼»ÿŽÿçØÂI{ÿDÏöÿÄgý¡¿ùØÑõLý óü4füÊñ ¸ïþ…ywþ%)ÿϰÿ„’÷þ‰ŸíÿˆÏûCó±£ê˜ÿúæÿøhÌÿù”?âqßý òïüJ8SÿŸaÿ %ïý?ÚÿŸö†ÿçcGÕ1ÿô+ÍÿðÑ™ÿó(Ä&ã¿úåßø”p§ÿ>ÃþKßú&´þ#?í ÿÎÆªcÿèW›ÿá£3ÿæPÿˆMÇô+Ë¿ñ(áOþ}‡ü$—¿ôLÿhüFÚÿTÇÿЯ7ÿÃFgÿÌ¡ÿ›ŽÿèW—âQŸüû3uÚøwIÔµïx#ã~…¡hÖ7Z¦±­ë?³·ÇÍ/HÒ4Ë^æûRÕ5;ï†ÐYiú}•´r\^^ÝÏ µ­¼rO<±ÄŽã:´qt)T¯_/Í)Q¥ T«V¦U˜Â:pNS©Rr¨ÂŠr”¤ÒŠM¶’ðŸoû++&ùiñ/ Ôœ¬›´)ÓÎ¥R¤ŸÙ„#)ÉéÛHîjÎB€ Àñ‰tÏ [ØÜjQkWOªj¶Z—§øwÃ>$ñ~·©êÚ‹8³±Ó¼?á='[×/¦”E+·ÙtùVb’yÚ8cw »JP«Ruf©Ó§FZõjNWj0¥F*IÙ7¤]’mè{Cšñ&=e™>¬d¨ÖÄ{:˜¼ Ž¥iËÄa°ÐP‚½§Z.OÝŠ”šN¿ü$—¿ôLÿhüFÚÿt}SÿB¼ßÿ Ÿÿ2Ÿ_ÿ›ŽÿèW—âQŸüûøI/è™þÐ?øŒÿ´7ÿ;>©ÿ¡^oÿ†ŒÏÿ™Cþ!7ÿЯ.ÿÄ£…?ùöð’^ÿÑ3ý ñÿhoþv4}SÿB¼ßÿ Ÿÿ2‡üBn;ÿ¡^]ÿ‰G óì?á$½ÿ¢gû@ÿâ3þÐßüìhú¦?þ…y¿þ3?þeø„ÜwÿB¼»ÿŽÿçØÂI{ÿDÏöÿÄgý¡¿ùØÑõLý óü4füÊñ ¸ïþ…ywþ%)ÿϰÿ„’÷þ‰ŸíÿˆÏûCó±£ê˜ÿúæÿøhÌÿù”?âqßý òïüJ8SÿŸaÿ %ïý?ÚÿŸö†ÿçcGÕ1ÿô+ÍÿðÑ™ÿó(Ä&ã¿úåßø”p§ÿ>ÅÒ×±Ãÿò<Ê¿ì;ÿ§ù¿Œ?òkxÿþÉ\ãÿQ*Îõ@ãøP@_äã>ÿØkâ'þªŸ×µÃ_òPeßáÆÿê%SöÿäaÅöOÑÿÕæR~øÿâgÄúž,ø§ñÁ |- ÑÛMâ_ø¯AðnÄÙò “Yñþ§$Òío.&¹ûNÕ85ú®/ƒÀRxŒv/ ‚ šN¶/K I7²u+NMô\×gï8\/WØ`°¸œef›Tp´*â*´·~Δg6—Wb_|Fø}ñKÃðø·áŽüñ·ImoâoxŸDñ‡ç¸…"’X!Ö|?}¨iÒÍsÃ$‘%ËH‰4LÊD$Âã0˜ê+‚Åa±”$ÚUðµéb(¶’m*´e86“M¥+Ù§ÔXœ&+UÐÆa±Jé]ÑÄÑ©BªM´›§V1šM¦®Öéö;*é9€ ( ÉÿÿÉÀ~Ó_öT¼ÿ¬åð"¿âù(3úÿ„ÿÕV^~ãOüޏgþÉ*úÓq9^¼ƒñÀ €>¼ýŠÿä€i¿öT¿hßýhïŠõúŸȃ ÿ_ó?ýZãOìü¿þDÜ5ÿd—ÿë3•…7í'û:Ûü@ î>>|ƒâ›]ŧ†“|Sð4 ýÁAðkë«â#w9’1°Ó¼é ˆ²çÓyÎO_Ô%›e«Ì¡õ'ŽÂ¬_<­hýYÕöÜÎêÑ仺¶ç´²ŒÙá~¼²¼Åà¹\ž1`±/ Ê·—Ö=—²åZÝóÙugµW¤yÁ@PË¿¶oü›ŸŽ?ì1ðÓÿV§‚kçx³þIüÇü8ýK g‰ÿ‘~sÿdÿê0>9¯ÊOâ° €>Lý c„ßµ¯Ä?€Þ ñœ~ÖüYã¿xiþ"øN+ _ÙéZÁ‹¾5´Ó帼µº³Öt´ñ„t[ŸìÝfÖò+ìö°žêk†ùÌï€8{Ä NY“ç˜v•zتtñønJxü'.[ŒÅ'B´£-\-zsŒ¡8Eůy³ûèyã牾ñŽyàn!Äá°ÈÖ71áÌ\ç‰áìÞ®6Ëpte˜e²œiÎ¥ž5Q¯FTq§[ÚFª”coÂßÛoþ Ëñûö¹·Ö<‘âï…:¶¥i¥h_ü&×CÃçSÔd»NÐ|Q§jÅáOÞ}ŠW¶´•µ-ðOeo§xŠûTžm6×ù/Äÿ8³ÃŽl}–{ÃÒšŒ3l9©áå?hãO0Â{Õ0òå¦äëAÕÃYÅ:Ñœ¹ý}~šÞøá*YcÉÀÜtÕ£æ¸ÊrÀf®ô ¥‘æµ#B–&­JÕÕ:ymxÑÌ%(¿aOê/ë;þ 3ñ–Ûã?ìðùõkKÿø × ¨ý‡?ä†ÞÙfý¡?õwxò¿Là¯ùRÿ°ìÛÿVx³û ÿ’s†¿ìžÉÿõ‰êúÏí%û;xsÇQ|/ñÇÏ‚ÚÄÉæ‚Þ‡zÏÅ?i~:šâé•m`‹ÂWÚìü“\³¢Áiìó3¨X°ÏµW9Éèâ–¶k–ÒÆÉ¤°uqØZx§)|)açUUn]…ßCéiå9­l3ÆRË3 ¸D›xªx,LðÉFîM×7I%fÛrÓ[žÓ^‘ç…P@7þØ¿òiŸ´ÇýŠÿúƒëuáñ?ü“™÷ý‰ó/ýD¬ta?Þhÿ×ÈþgÄõùü>P\_òS>ÿÙnðþ›õúíÊ¿äs“ØÆŸþš¬~¥áü•Õ?ìGœÿê+?V<]ã/xÃú‹|y⯠ø'ºDk6­âokšg†ü?¥ÂºŽ³¬ÝYiÖQ´Ž±¬—71)vU³eÄbpøJ3ÄbñpÔ)«Ô¯ˆ« 4`›µçR¤£¦ô¼¤µ?£(aëâªÆ†…lEzŽÐ£BœëU›µÚ:jS“µÞ‰é©Ï|7øÁð—ã&•s®ü!ø£ðë⮉e:ZÞk? ümá¯éV—2+Þ*4½›÷pœ7n)b³ Þži•QÉêæLUlm\+R¡†êUkcc:˜ÒÅâ[­ ÇY{9Tç-x3öªý°4/ƒ?·£ã]âõÕ¿ÁßÙ_Çÿ~ |qøÑû2ÃðY>?ðìllìõ{½û+b¸“,ÉhçøÜÒ–20yf70ÀÐËèQ¡G.–c5oS9R¥]b=¬æŸû+pŒcRTŽJXn̳z¹&,«…”ÖaƒÀã+c«Õ­[p3­OÜ¡N5*Qt=œc$¾²¹œ¥Ô2¿ißÛ§âŸÃ¿~Òž(øI¯‰þ|ð×À_„Zv—ká‘â¥ñgí1ûKøËÂÉ¡ßFšc[øƒ\Ó~xÅ~Õ.ü) êúañ÷ŒcÓd¼[ņ]63¾(Ç`ëçUòõøL¦ŽU—œh}ab3¼ëCÙÍ{7Õ¡Â×ÃÎXz5)ûiâT¹’pÓ'á¼*ŽSG'G™Ö̱óœ«:>Ã'Ê(Vö‘|éÓ§,f&…x*õ!?ej<·SÔý‘¾>þÖú÷ÇÍ3á÷Äý ãß>ø£Á'ÖuOˆ?di?f±ðßÇÚTúV‹§j:n­¨è¾%ðߊìnµ[k-NøHôÍCM·’mgQ†í£Žø6â ¹´0˜êY®+_ ^¤ñ™‡¼—êxªN›…(N'N½ ñ•HÂ3^Ú‚n¬Ô¬£=Ë2*YdñX:¹nGFœ0¸ñfÿ[ÃÔUêN„jQ­FQƒ” ý”ã7jpq×»ÿ‚ox—ö²øÛðCàïí)ûAüvðÿ‰tÿ‰ß âÕ øOá/…^ðΕisyÑüWªxÆ)eׯ|E>›c5Æ¥¥i–ú/…àŸZ–ÒÛKa¥ÛÞÜupe|ÿ3Êòìë7ÍiWŽ;ª,B…8¹É{¼ýŠÿä€i¿öT¿hßýhïŠõúŸȃ ÿ_ó?ýZãOìü¿þDÜ5ÿd—ÿë3•¾ðæµû|9ðÿ†¿iÙáÅo„¼uoªê¿µïnüâO‹ýâö†…ñ‡â§Ãø{Mñœ>,ÓµÍcJ¾ñOŠ<+âOjvz¤ëzU²ÚÚÅå|¾^ÁÒ¡œðþ —a1Q©Sˆp’ÃWÄsÕÅóÒÌqø,Mbcˆ…Z•zô+bg‰Õ¦¹R·éØŠ´ø›V¶Sžã08üNÂ&8šT9)ay*`0X¼=YáÝ S„ãF…jT!(IS›rnü7ÄŸÛ‡öÃñŸÄÚûàn‹ñºÏIøñsâGÂ?‡Þð?ìg¨ühøyñ;ZøM©É êòüEø¸¾!±Ö4†ñO‰-5 H-<þÑJ¿ºŸW¼šæ:åÆñ?bqy¼²Êyœi噆3/Âap¼7<Ë ©€¨éTxÌÃÛF¥?oZ3ŠŽSt)ºs“©'$ºpœ;að¹\s*™t§˜àp˜üV'ŸÃ/Åàéã ªÓX\²9ûRŒœ±.§¶©Ï©¤©¼Uñ3ö¼ø¿ûO|7ø9ð×â.™û5øgÄŸ±w…>>üB±ñÂý ÇÞ?ðŽ5¿Ýh3øwIµñ–vº+£kk¯ÃªXZ.™qö]=JïíÖ¾î#Ä9Žw‚˰XÈd´+ðÝ ÛØX¼^Sé:4ãYÆ1©û:¾ÙT„}›å¥Ï.xø´py&Æcñ˜Iæõ¨ñ l³ *XÚ¸l-|=<2ªªÎTT¥(JÜôý“„¥íêrÇ–GŠkÿˆŸ³Åí½àÏ‹º´;ø‘ðkFð¯ÄßÙîDÐôëâGþ2Ø[ø+á†×LÑíí,®õk?ZO‰|wrJöWš)¿™Ù%¿œ¯Ä8Ì¢ þÆÖ¿´¿Ä¿Œ~>ø_ üIñ¾!uÔ´MÀ¾ ÓÓÄn™¥è^ÒíüC¨ãW»mjÉm­b>./?â5ŽÄeTêæJ¶O…Ë©ã1ygG6XÜË¥Œ¬êûôèápÐöЄ)Q‚­?ÞKÚÇ–)úøl“‡Þ†gR–ÑÍq9„ð˜|Ã?–Xðx .6®’¤¹*UÄâ'ì§9Õ«9R…ág+ɯiÖ>?þÚß?áÞžЮ4¯Ù›âOíáŸ3üxÓ|sð¼ëZ†n~h>š=WÃÞ ñ-õ¶£¤j7·s]jþÒuZkKk ~Ïû~=y4õ‚çÒ©›q.7ýQÃÑpÉ1¹ÅÑæ°Å`}¤è< *OžŽ´”éÎrr©FJŽ1…hû_kÉiyÐË8{ø¦½E<ß•UË[<67Ù²ÆÔ¨œ*â)EƤ"­N¬éÁIΔ½“§ÌÚúÇö“ѼMáïÙWÑeR\6}S‡XZ3ȸŽTðê­JÊŒ^G˜Z ­VêNßÍ6äúŸ2׿ñ P@ ð§üœ/ì¯ÿesÆ¿úÌŸ´zyü”Yý…ã?õKšŸ¯x5ÿ#ÌÿþÉz¿úÐpñï¶ÇìýãߋڧÀ_øÁ? ~2þ0ñwˆu¯€ß/Ž™àO‰6^-ðuç…c½SŸ@ñ^“§xËÂ-q%×…ï5¿j:jEªkQHÖÒ͇(ÅfSʱXl.3Yf#V®S™K“ †' ,?7<¨â)ÃC™º«FpµJ©Ù´ÏêͰùrÌhVÅc²ÙãèááG5Ë•ñx*˜|Dkè£V…GF³Šöª•hOš6®“?9´ïŽþý‹>||ðgÀ?Ù‹Åß³ÏíâÿÚàv⿃#KÑþ6xoÂz§ía¯ÙøcâG½;áíö˜Þ9ð×…¾ø¶ÓÂÞ‹VðüZw‰¼=oáá¢xgM’X.~# ŒÀð†[šà2©fXŒïëå4¨ÒÌ0¸jùô±5?/¥‚•?¬Ð­.*T°‘©F0ÄÃÙ{,<\£/»Íêg7~Ðþ+Ñ4ŸÞ{È"þÛ‡GðÇ„øšÃBº²½ÔíY­Lu:•ãF´kQ8JjP’¯ÏG–Ÿ,*ÆT׺׬þÀž&ý¬þ9x?@øûñ—ã·‡¯¼©x›ã7‡´„¾øUá!5=Âÿzt1ج5µñWu=¬}’÷hÆ7Ç™JNs—QÉ2êõ2Ì[V8˜ÑËêÔÇVÆÖŸ%Jø<>&­:8{*~Ê~×â«*“R”œb£…ñ{þN‡ãý‰ÿ?ô‡Åµó¨ý‡?ä†ÞÙfý¡?õwxò¿Là¯ùRÿ°ìÛÿVx³û ÿ’s†¿ìžÉÿõ‰ðL~ñì7àï‰w_ÿd†´'Àm+â/ÄŒ^5ý¥ü/yàwâm¯…üKã]cÇ3ø±ðËâ‡ìµmRøw¥ê i©j¾ñv¿yáÛ9tm"CkäGóë[†0øÙf¼=ÍòªxÌfc‰ÎèK W±U1OÁbéF­iàá>YÔ¡ˆ­%FŒ]:zr¯Ö]z\GˆÁÇ+ϱ™Ve<&‡Ê+,M,$ªÑÃÃ> …«*t£Šœou¨S‹«VJ¥Mns?m¿Ú“Ç_þ=hßâøÙáï|ñ|~ð}§Ã_ØÖ÷öƒðçÄ_XøGÃþ&Ôî¾$xâoi—:“}{¯ÛXXh> ·Ò5Ë WXºÖ.ç¿·¶µÃ0âl÷˜æ´ò¯í:8|¯°˜xà¸rY½exáèש,n)Öƒ£NR¬£ XhÓ« kÚJ¤œÔc¶‡r\6,©™¼º­|ƃÄâ%‹Ïã•ÕÂQ•z´`°˜uJj¬ãNR©]Ô§*—‚„T[—ºë_¿l¯‹ÿ?do…ž ñ—û.ßücý>"|xøÝ¡x»á®ão|>ñW†|[ðC:O‡ô¿Ícö_hÚ§ÄkÿIgâ½ÑbÓîõ+½GE¿Õ¬4¹-ýZ¹æ9àpµá‘Ï1áÌfk™ÒÄ`¡‰Äa+ÐÄe4½*uœykÒ©ÎT”%9NœªB››O/È0~yÄQžsarܺ¥ dðô1T+PÌêsÕ©IJôjC ªT”j9ÆHÂUºíKö­ñŸìÅâ¯Ú¯ÁŸ´_ŒÇvß ~x+öø=âƒáÝ ÂÚ§Ä ø#Ç~þÍðõ¥®˜Úôô{I¡†8\Ágñ;ÃÚ|<$EÑ<û’b3ì6qˆúÔpV7Ë«ûT'‹£ìžG’ŠŒU˜Ó‹I-#Ž£ÚŽ˜C%Ãçr\FUCêÏ™b2¼}mV¼0µ}¯ÖpÕœêÊSöo/œ“oâ–¬ÚNzü—â¯ÚÓöÖƒÅþø{©ü\Ñþ!|6øðŸÇ¿t*æj˜\feŽÂåØ|Ë<–W x,¥OÛóòN¶#ZSu¾©R‹IÓ—Åc=®˜‘ØÆ«:Y-Üå®®'f÷óª8š'œÒÅâÞ; ›4ö¸¹Q¥‡u›Ãâ%èÑJ”9bã Gugy6Ï’ö´+f1«…¬ Õ§ìðʵLB¤”bšöÕ›©>i)M¹låÊ´Hù~¿-?…‚€ Ë‹þJgÀ?û-ÞÿÓ~¿]¹WüŽroûÓÿÓUÔ¼ ÿ’º§ýˆóŸýEgÔ_¶·ÀoügÓ> ø‹áþ…ðûâ§ðCâä_.þ |X»¹°øuñjÐx3žM[Ô!Òÿn‘¡øKâ.•ãm;ãGÄk¯…?þ"i:·ÂÓ ê?4Ÿ øŠ_ZÛxKU_ xš}RÚ]Ú:cíçç1œLòŒ&g |=£?Œ²¨}Z,>2ž&–1à0˜Êu0Êxêtk:ñXyû î¤]4 ªsA„áÕšbrùÔÏ^k’8æSúÄêÖÂO<¿ n+ 8c=¬ps«IQ”«ÃÛQP’¨ÜÝ>S¶ý“~?þÖÚ߯«|Nо Imâo„_?bÍwö¾ø³a¥Ôž.“âNŒÖ¿³¿€F§5¬×úÝÜi~1ñ†¸tÃmª=¦ƒck;Çm4ö·½™ŸÔƒxܱƾ_—pÕ^!ÇÁF2x®Òk'Âó¸¹Òætñ8Š®³µ(ÆMEµ.\»…éÍ,`v?ˆ)dX9I{„©ÍšbySP«e:)óÞ*U%%ï%(ð³—í1ûlj¿þi^9ðçÇ¿ˆ^ø›}}¥üQ‡Æ±n£ðÀÿo jšæ—âÏüDÄ:ÅÞ§áÛmsO³ðÕÆ•ãímKT¶×-5;]ZÎkI­çäÉó¾%žg—SÅQͱ˜\lå rÄðÔò¬.]z5*¾«T•J1«Ñ”1^ÒsXÎ5‹O«6Ê8vv>xj¹fƒŒgƒt8†9ž'jЧ:8Œ+¥´©ÎU”ðüƒ§(J™5ï_±§‰¿k/Ž>"ñßň߼?oð›Á´×í_ð³Ã_ü;ð«Ã0j,ðgÃ?‹ÿþøVøúâWÕ,uÜésX/…ôí1õ+/ZÉâ+Ý^ï[ÔäOW‡+gùl^?šÑX 6wŸàheôpëá°YŽ;AâqmûHΓ§Oa9ÆŒ]iT•Y³Ìâ 9]K ‚Âeµ^;“ä˜ÚØê¸ÚÎqÌ]aðËÜ”jª’çöÒŸ,ªÉR8Ó‚u¾?É×øÃþÍçàGþ¬ŸÚj¾Œ?ä¡ö&Ë¿õ;8?œ|fÿ‘o ØwéŽ9ùÃðP €?wÿàŽ¿òoŸ?ìåüeÿªßá%~ÅßòQf_âÃêÿW~Ž¿òf¸+þ¼g?úÑçëM|ÙûXP@P@P@~lÁ\äÃþ#ÿÙUý’¿õ°>×±Ãÿò<Ê¿ì;ÿ§ù¿Œ?òkxÿþÉ\ãÿQ*Îõ@ãøP@_äã>ÿØkâ'þªŸ×µÃ_òPeßáÆÿê%SöÿäaÅöOÑÿÕæR}«ñÇötðçÇY|5{©üAøÙðãYðšjÐé:çÁ‹>,øi~öÚÛiϨ[ê¶Ú%ßö>¸ŒúU›Zɬiw×:v.NžÑ/¯–ãôŒÓ'£š:ž/3ÁÔÃûEN®[˜b0Sj¯#š©RöuWîãÊêÓœ¡ïr8óÊÿЙnm[-U£ ._‹§_‘Ξa‚£‹Štùù]7Q{JOß—7³œTýÞu.XÛËm¿`ÙÆ?…þ#ø_¤x×^-ñÖ™ñKÄ5ÿˆÞ2Ô¾2ß|PÐá´¶Ð>!EñFMYOc:U%9J|ñ”å+IÏš1k ðïìwðÿFøañ£áN¹ãÿŽ¿´_> Õ¾øóYø«ñwÄþ=ñ"ø[Xð÷ˆ¼3qc᫽nItï HšgŠ5oô­#J·žîá¬çÕP:uŠÁ­.ÂSÀæX ¸¼ÓK5ÂÔÁâªcó øºÞÂ¥Ô(Ê«p Ô+Ô÷©ÓNO•Ôs䲫Ÿâ§ŒËñ´°¹n¦Y‰†/ O£†¤ë­*Êu£M)Ö¼èÃJ“j+™C‘NW÷ù¾øZóáäŸ 5-9u_Oàãà;Ý+Qsp/ü2ú/öÖ7’)¸Ó7C< #33:ílcÖxJÂ< áφxoªJœÝùè:~ÅÆOwÍ ß[žZÅWX¿®Â|˜…ˆúÔg 9k{Oj¥ÒÓÕ-Oœ>þŸ³·Ã?ÙÏÄ?²Õ¯‡5Ÿü*ñ}α{âÔñψõ-wÅ^$¿Ö&¶‘uGÅp¾Ÿ«Ç©èÐéúEŸ‡5->kí ßDÑÛO¸†êÆ;“ãà¸_'Ádõ²8Ñ©ˆÀb%RX…Š­:µëN£Ožx…ÉSÚSP§3‹ŒéF•>I)EHõ±œKšã3jYÔ«S£ ©ÆƒÃR…*4£Ó„h>h8Tr©*Ð’”jº•9“Rhê>þË>ø¯ßø“Jø›û@|A¾ºÐdðÅ…·Æ2ø¤xA’÷O¿k- EÖ¯?³-çivHš½Õ­î»¬sZ&¨¶÷—ÑÜí–dT2º³­ vmŒœ©:1YŽe‰ÆS¥IÎq¥J¤½šw§ªJ2ª¢œUKJJXæ9Õ|Ê”iTÁå˜Xªª´Þ/¡…©VªŒ£ÏV¤;Vœ›„eNMIÚ1kÒþ |!ðÀ/„þø1ðý5(üðÛÃvðÌzÅ÷ö–¨šNš…-–úüÅ»¸ NùŒ1—<íÛ–eø|§„ËpŠk ‚£ IóÏÙÃHóNË™÷vW93~#4Çb³ SƒÄbëJµgN<çžü±»å^WgçŒÿäà?i¯û*^ ÿÖrø_–qü”¿ýÂê«/?Ÿ|iÿ‘× ÿÙ%OÿZn'+×~8Pן±_ü 7þÊ—íÿ­ñ^¿Sáùa?ëþgÿ«\iýŸ—ÿÈ›†¿ì’áýfr£Í“þ ×ðVçSÓÅ=ý¤~!x'F×t¿i¿¾"þП<ñoô¿|uø[}ñRZø› |ø×㯆¾ø®%•¶™.½âÃÚ”6ã]½Ó,ltýGYФÑ5-FÞÊÕ¯ng¹ˆOZâ¸W.Äⱨb3L ±“UqÔrìÏ‚Ãã*ò¨:µéQš^ÖPŒc:”)ÍF.Rm\Ë ÄØü>†t2Ülptðus ¿ Œ¯…¦å)ªTjÕƒ~Î3œå u}¤ ç%“±ô|oñ•¾;Fš§ü'ð®Óàé•õ“J> ²ñUÏŒ`C`ñ³6©ý³u+> × ïlD-#yõÖ]†Yö¢Sú×ÔV]~véýZ5Þ!.Göý¤æÝÚÓÌòÞ?òÿìÖáõo®ËðÚX•‡o™;r{4½Ûi-oÐó/Œ±ÿÁ_Ž_þünñî™­ÜxßàßÚ¼,tÍrãMÑuˆà×4¯éZŒô˜£h|M¦h>+Ñ4¿h–WOV:åœwŸ¼ãnLJ²ÜÓ0˳<]:²Äå’æ¡ÉVP§RÕi×§ M5¥xR¯JéFNÑ«muGf>Ì2ì?.ÃNšÃf1å­ÏMN¥;Ó©FrÃÔnôgVYÑ©(ÝÊœœLŸ‹Ÿ±‡ÂÏ‹?.>*ÇâŒ? >"êÚ.™á¯ø³àÅ_ü1Ô™áí=¿µõ]J]B[ãs}wquqs#Å”alª½ëʦOB¾*•çVNŠ4¨Ux‰Õs©^£…(?iRnn|Ó“““9kŠt³:6¢©æÕ¨VÅ(Q…4§‡«Rµ5FùiÑ‚Y^‚-£Ž?öÍÿ“sñÇý†>êÔðMpñgü“ùøpÿú—@òq?ò/ÎìŸâ/ýQæÇ5ùIüVP|)ÿ' û+ÿÙ\ñ¯þ³'í^žCÿ%EÿaxÏýRæ§ëÞ Èó?ÿ²^¯þ´<}íñ¿à‡~:Ùø~_Æß¾ê^ºÔ.´_|ø¡â¯†ºÜ-ªEm ì7Ï ^G§kVÒ-³C¹§jKe"<–?fk‹“7ê9žUG4%S˜a'BS•:Ùv:¾ ªçQRRt¥ÉR/–-*°Ÿ+W-åèÌ»4«–Ê«§‡Àb¡]AT¥ÁÑÆS|¸¸ª±s¦×4“tçdí.k+y—ûþÎv¿þ#|=×ô¿xý¾-kñÄ/ˆ>#xÇÄ¿üAâOËþ×Óâú¬:þ‡©øâ(î| þŸG·Ð®|éí-Ö[Ëç¹óéð¦O3Vž+ý¡R…lf/Ä×ÇÖ­…iá+,c¨ªÒ©…’Rú.š¥+Ê*ò›—|øŸ6x¼..”ðø_©Bµ,. „ÃÒÁR£‰ºÅRú¯#¥R”Ú®«*Žª²“²ºß‡_²_ƒ>è_ô+ˆÿ¾%Gñ/Â×> ×/¾.üañWÄ+Í?Ãw6š“éþ‡XœéZ$‚-Rì¶¡k¦i\7’//.b·†4ßa°”±”¥ÍqËAáªË1Ìkã% 2ŒàáET|”§+Î0疜ҒŠF¼óŠ©…©&YƒxJëN8,eYJ2æªà¹ê+Á{’Ÿ"×–)¶ß´ø'á¿…¼ðÃÂ?4[iî<à¿h ô›=Vas7…¼9áëO X[jW böy4›8¢¼™¢As#I!wížC ÃåôâÞ …¥‚§ºhÆ„c9iÌÝ8¥'eÌîúžv'[Œ¯©$±ŒM\\åMr¥Z­YV” ®ùR©&â®í¦§ÏaÙÇà‡Áˆ_³Ï†ü+©x‹áwÅ;NOhþ;×/|Oy®éú‡†4_[è3j³}žöAð‡t/ ønÊÞXŸDÓ4«5³.#7ÇËx['Ë2Ì^QF„ë`qÒ›ÅSÅÕ•yU„èRÃ*NnÒT©aèÒ£F1kÙBåjK˜õ³%ͳÇ šÕ¯ XÜŠÃTÃSÒ”kTÄJª‚æ‹©V½j•kJIûIÎ\ɧccà¯ì‹à¯¾&Oh?ÿh_}‡E¹ð;ñG㇎<á/ è·ohÒYèžÖ¯Þɤ,m`µÔu«ê––ñ¼v·Ñ}¢äÍ®[Ãøl²º¯K›ây)Ê8ìÓ‹ÃЧ.[Æ•³qm(E)Õö•"–“ÖWÏ1Ïq9•F®+¡ÏQV­W—a°µëÔ5¥R­8ó[ÞmÆŸ³„›¼¢ì­ë~ø?àGíဓRÂÚ¡âNÁ5{ã©_‹Ÿø»]ñ®²f¼1BdFÖüE¨µ²ykäZ˜mòþVöï˲ü>Wƒ§Âóª§^qö’çŸ6'WRò²ºöµ§Ë¦‘²Ö×8s ~#3ÅÔÆbyj¡ {8òG—B–£wgìéC™ßY]õ>ø½ÿ'CñƒþÄÿ„úCâÚüËŠä¢ÅÿØÿvOÂ|gø¸[þÀ³?ýM‹^ø€P@T~ßòCo?ì³~Пú»¼y_¦pWüˆ)Øvmÿ«Y–&X,Nû?«â±ôs*ÉSŒeõš±TiòrÚ0¦¡‹­zqŠWqµ”R<ÿöýþ þÓ>*øEãŠzf·{«üñü$]\¸ÑìuTmwž)x¾ÎÞ?x^OxÂûè·eaþÕðý…ʺí•eã͸{-Î«åøŒt*ʦ[[ÚÐöueN5?{‡®èâ"´­A×Âáë{9ií(ÂIï~œ¯>Ì2Šì> tãO0¤©Öö”ÕIAªu誴$ÝéVö8šô½¤uä«%×Jß?cï†|o§üO¸ñņ¬¼;ƒ®~ üø™âO†~&×| ýÆ©oá_Í¢\?_Ò,õ»»Ý xÿøA¾ø/Bð†`Ö¯©¨ÿÂ=áÝ6+O‹R½h¡×&Î[™ÚÄ﹌cv+».Ëðù^_ƒË0ªUÀá©a(*’ö“ö4`©ÁNM.yr¥Ìì¯ØãÇãñ–?˜â\~³ŒÄTÄÖtãÉkVnrpß*æz+»wÓo,dÓ–á]gTš ˜l´éd³Òÿá!Ÿ\}5<¹­+¨-î"Yw àòºôëañ™½HP„©á°˜¬Ó‰Áa¡(¸rÓÃÔ›RQƒq‡¶u\7‹RI§˜q/2£R•|.WNuçâ1Xl· Cˆœd§ÍR¼!Ìœ¦”§ì•5=T“M§í¿ >ø?৆õ¯ ø%5(ô­â'ÄÿŠ€Õo¿´.Š~.ü@ñį¼3¡òìÄÞ(ÕM³Úßa±h-<É|Ÿ1½<¿/Ãå´jÐÃ)ªuq˜ìtý¤¹ß·Ì1u±¸†•¢ëרá³Fî×<ìv?˜Ö§[ÈçK ƒÁÇ’<«ØàpÔ°”.®ï%FŒåö¥yY\øWã÷üŒ?ìÞ~êÉý¦«óŽ0ÿ’…ÿØ›.ÿÔìàüSÆoùð—ý‡qGþ˜áƒ‘¯œ? (÷þëÿ&ùñƒþÎ_Æ_ú­þWá|]ÿ%eþ,7þ¡áÏõwèëÿ&k‚¿ëÆsÿ­p~´×ÍŸµ…P@P@PæÇüÇþL?â?ý•_Ù+ÿ[à={?ÿ#Ì«þðÿúq›øÃÿ&·ÿì•Î?õ¡üïWôþ?…ˆ¾ Ö¼ñágÄM'—íü­xž]_DÑõMJÕ ¦½àoøzÞêÖ_ê:V›2Á¨j6Ÿh…¯Ro%ÞH’C-uåØïìÌË xz¸˜Pöñ*2¥–­B¥5$ëT§”¤¹—=ìî“?MðLjònÇçu3¬U\%~Q % ´ðÕq_¿†g€ÅòÎSœbéaêµ;5Ì”]¹®})ÿ ­¬ÿÑ·üMÿ¿áÿ7•ö?ëÝúæ_ø7.ÿæÃõ¯øˆœ ÿCÊÿøhÌ?ùXÃkk?ômÿð¯øCÿÍåëÝúæ_ø7.ÿæÀÿˆ‰Àßô<¯ÿ†ŒÃÿ•‡ü6¶³ÿFßñ7ÿ ÿ„?üÞQþ½Ðÿ¡>eÿƒrïþløˆœ ÿCÊÿøhÌ?ùYærÁM¼+Åè>ËðGân¼ß-|0þ/øF.n<¹'‡dÔã“þ³åÕ"’5µvK™á†êæÚ9౿{^gâ>cåyŠÅh¶Öµ¯ø÷ãÄ _š‡‚âñ÷t]sGеmOCÕuHt½á'ÃÉ=ôþÔ5M2).uêÒAw³H,Í´³Þc|f?ý£˜ãñê…L41U¨Ê«J”ª(ÒÁapíÉÑH.iÑ›IM¾[7fì~CâwåG›dÕòlMLVSËëV©†«…¾%g9Ö>Q…:ÉMÂ4qô=ö’sçKá»Þ®có` €7¾þо&ø5à'øð/ÆÞ&—OñïÅÝrß]мUðÒ/RÒümñoÆþ9ѧ·ƒYñv›©Á Òeÿƒrïþl8â"p7ý+ÿá£0ÿåaÿ ­¬ÿÑ·üMÿ¿áÿ7”¯t?èO™àÜ»ÿ›þ"'Ðò¿þ3þVyGÆïÚ/Äÿ>jßôï8ðõο¬ø*FÖõ¿ü2—KÓ-4OxsÄ…ÕÜZO‹õJeŽÃJ¹òá³²¸šYŒqª|Å—ÌÎx²¦[‰ÀSÊñÔgˆö1UjÕÀû8(b)T”§ìñS›J0zFmÛC_ˆ¼õ Ò,ßZ¶#(Íð”),«zøÌ³„¡9ÁBukCšrvŒnõ±Î×ËËÁ@s·š¦³á_üø‰¤xbûÆ1ü9ø­ø‹WÐ4½KFÓ5[Í3YøAñOáú>Ÿq¯ßišSÍk«x×Jºž‹ëvk¯,ñÇ»à±o/̲ìÁÑ©ˆ†^¥JT¥N5eÙ~7¹iÓ§xÔÄÂRRš÷T­wdÿEðÓˆ2žÎ3&ÁP¾ü±·Ô¾,|;Õ~Y]œZKãŠ4&¼ýê@ZÒÿˆ°Ït‰4±Ç$G"Dò ‘”ºç—âNY‚Š–/‹Ã)lëbrÊ|ÚÛE,roW«IÚúžöEå\OVt8yg™ÕZÄŽ[ÃYÆ-S÷\íRTpò…6ãJ*rNI7ìÏŽGüIû+IáýoÇ6Ÿ>?_ü1ð÷ü5ð÷Uø§i£øEüoâ_hz®¿il³É☵٣±´Ò."ÔÚ-冿{³Ãwm}orþ7üF^tç]e¹Ä°´ëÒÃÏ©á]V­9ÔJÿYç|ª Jкn6MI3ô·À¹Œq˜\ª¦+ K>ÆåXìçT©šÏ€Åáðu*8FrÂFUjba* XµS…nyS)Á}cð£þ ¹ð—㥼×< ©|Cû(|Ô5;(cM>Ie¾Òâê¶)ö®Ã]ÙÂ-î/ µŸË¹*½l'‰™V=7ƒÁbñ6Wj–',”â½ÖÜ¡õÞxÛž7¼Uœ’zè~yÄ9Ž „çq%÷&ç’…:˜þͨЫ&ë(Æ–%Ñxz®W¯(*ueÏ S©h.cÙ?áµµŸú6ÿ‰¿øWü!ÿæò»׺ô'Ì¿ðn]ÿ͇ÌÿÄDàoúWÿÃFaÿÊÃþÿ£oø›ÿ…Âþo(ÿ^èП2ÿÁ¹wÿ6üDNÿ¡åü4fü¬ù¶ÿĺßÄ/Šÿþ%ê~ Õ< câ=+Àº6—£kz¯‡õ]VCá‹-i/¯f jz¾Ÿ ¼Òê‘Glzn[É™¤†5òËü~i˜jæxŒzÃUÂÓ©C F4ëÊŒª7GÚ¹Iû•`¢ÝD£ïóhî–—ü«Äî%É8Š®F²\U\\0LeEņ¡ Í'äXî bÖ÷pïcÔSBÌZ3YaüCÁâ¨RÄÐÊsѯÔ§%W.Ö2W_ó£[5ѦŽüë‹8[‡ólÃ$ÍsLN1Êñu°xºÊ3µ8ÊÍÒNPš´éÎËžœ£5¤ŽËþÿ£oø›ÿ…Âþo+oõî‡ý ó/ü—óaæÄDàoúWÿÃFaÿÊÃþÿ£oø›ÿ…Âþo(ÿ^èП2ÿÁ¹wÿ6üDNÿ¡åü4fü¬òߎ´·Š>,|ø­ð¿JøãÍSø‰ðóÆ> Óõm_Åß N•¦^xŸ@¿Ñ­¯õ!§øÎöüØÚMx—bÊÎêìÁýžÞivFÞ~mÅôó¯1ÀSʱôêcp8¬$*U«öpž"ŒéFu91SŸ$\Ó—,e+'dÞ…Óñ'¨Î5–s‰¨é¾uN9N9Jn7js‚Šrz'&¢›»i]œÅ|™üªP/¯ÜëFµðëź7‡î¼S/þ#x{Å—š …þ—¦ê†§Ã¨Û]Çau­]Øibê1|“*]ÞÛ$‰вy›´ÃâÆ{)×XL\+Ε9SIÁF¤Zƒ«(C™s§ïN7Iësï<9Ï2Îâ?¯æõêaðsËs#­N…LD¡WA“t©'7-Šv½Þ‡Ô?ðÚÚÏýÄßü+þÿóy_qþ½Ðÿ¡>eÿƒrïþl?kÿˆ‰Àßô<¯ÿ†ŒÃÿ•‡ü6¶³ÿFßñ7ÿ ÿ„?üÞQþ½Ðÿ¡>eÿƒrïþløˆœ ÿCÊÿøhÌ?ùXÃkk?ômÿð¯øCÿÍåëÝúæ_ø7.ÿæÀÿˆ‰Àßô<¯ÿ†ŒÃÿ•žg«ÁM¼+¢|VðÁ=OàÄ+o‰^9ð·‰|cá¯?‹¾ý¢ÿBð¥ÆmªÜ ?á;òÒbÚƒÍelÍçÞÛiZåÌÑi4üGÀCG,¯1XšôªÖ¥IÕËï*tœTÝþ¹kûÍÅo% i{¸~ È1\=˜ñM v.¦G”泎YF?’Ž/0…iáà׳æqýÊZ‰rÒ©ˆÂBmKIKÓ?áµµŸú6ÿ‰¿øWü!ÿæòº׺ô'Ì¿ðn]ÿ͇…ÿ¿èy_ÿ ‡ÿ+ømmgþ¿âoþÿù¼£ý{¡ÿB|ËÿåßüØñ8þ‡•ÿðјò°ÿ†ÖÖèÛþ&ÿá_ð‡ÿ›Ê?׺ô'Ì¿ðn]ÿÍÿ¿èy_ÿ ‡ÿ+>tñŒuÏŠ?¼Uñ7Pð&±à*óáÃéún¿¬xgUÔï5ø«ãˆ5[áÿ¾­¬ÙÛØohð[}¢é.e¸†÷6éQÉ7ÉfùŸöÆg,tpµ°´Ö „P¯:©)ÑÄcëNKØU«\U4¯%'%-,“™øÅ9áx ’âêã__;¯ŠL%|,b±ôòhPŒ=ºŒ§+à+¹ÙrÅ8jÜšZUÀ~HPîÿü×þMóãýœ¿Œ¿õ[ü$¯Âø»þJ,ËüXoýCßêïÑ×þL×׌çÿZ<àýi¯›?k ( € ( € ( Íø+ü˜Äû*¿²Wþ¶Àzö8þG™Wý‡aÿôâ?7ñ‡þMoÿÙ+œê%CùÞ¯èü ( € ÎÖ5m;@ÒuMwWºŠÇIÑtëí[T½‚Cg§iÖÒÞ^ÝL䀱[ÛC,Ò18TBIâ¦s8N¤ÚŒ!Nr{(Å9I¿$“lß ‡­ŒÄáð˜jr«ˆÅW¥‡¡J ¹Õ­^¤iR§µrœå¤µmŸÄµÏíwâø)\ÿ´¿Š|3â ï ßüH¸Óæð‚ÇâX|YðšãR´ɪ|1ñF™â´´Ž?šâëPÑÀWËÝIKHS¥ŠÀO7Á©s'í*c«eøzq”·—ï=™ÿ ÇÀÖ¼ø†ø2%ù®uü=ñ¿‡t >÷o5o_h+á-Í›äûF«®ÙÆ’IJ<‘>½AW¢ºÊ¶½:q_Í:Ò§ì`¼çQk¾êçú§›Tÿr–YšIé 9fs•cq•§Öž.¥‹yŽ&ªZòað•dÕÜn”šØÒ>2|!ñNšÅ_†úÛÚˆÚåtøcRku”¸‰§z¤Æ!!G™6‡(árTâáÁÔ¿&/ ;oÉ^”­}¯i»_]ÎlO ñ& Aã8<ª¨(ÿ‚‹øïRø{ûþкΧꦫ«øëÀÖzn˜Ú½ËÍñîÓÀÏ!²ŽX¤0Co¯Í5ÅÄfI,âF»H'hDOâqyaòLÆpŒ§9Ðt#Ç·‰j‡Ãtì•FÛWi^Vv±ú—‚¹M ëÅ ÃbkQÃáðÙµ<ÚµJõÖ 9-:™²Šªã%Ï)àãBVU$Õ78)s/Îø"÷ÅO‰Z§ìï®øÚÚæûRøQâYxÄšu批à_Dº–‡®x3Å—’ý—N×`ÖĶ:®w§Éá½RÒËÃ×1O ë—ˆõýKç8+‰ž]S”¥,%WþÏV. ¥ «šœèU“´j*žÖ3„¢éÍF›½9Ê¥I~ÕôžáüÆxLâs§J‡àRþ×À×§Šž6Ëäèâ°™ž_J>Ò¶xg­‡ÅS¬±Ô*UÆÓ”1xj,%ØõøÛàÍ9„^:[øUpH›â^Ÿ‡tOµ1ÂØA㘮µ‡š†£)k/Å××W±Ás=”W6öóLŸeõê1Ò¿>ÿÔT}œ/üª½å‡”ž¶Œ+IÉ&âšMŸÌï…s:ÉË)–ˆanoøB­,n+ÙîëO*•:9Í1Vö•qu(Rs„*Êœbý/K×4Mq&“EÖ4½]-ÙRwÒõ MA w‘fkI¦³…bªä‘ t¤*]Âpš[¸IJÏÎÍž# ŠÂ¸¬V†sMÁb(Ô¢æ–ÅTŒ\’m]«Úúš•g8P@Z¦»¢hk ëZΕ¤%Ã:Û¾©¨Zië;FȰµÜÑ Y©p…ІRØÈÌN¥:vö“„/·<”oÞ×jçE &+ä°¸lF%Á'5BJÎ W³’§8¦Ó³v½Ž7WøÅðÃÿgþÞø§ðãDû_›ö_íxgMûO‘åùÿgûn©äùÐù¾^ï/Í~<ÅÎ3ÆàéÛÚbðнíÏ^”omíÍ5{]_ÔôðÜ3ĘÞ©ðþw‹ö|¾ÓêÙV>¿³çæäçöT'ËÍË.^ksrÊ׳1¿áxx.ëÃǾ-‰¿y÷…þøóUÑ/ìÏú½SGñ0ðü>×t›b{-GDÖu]FÞx¯,$¹²f¸Xúõü8â+-Ô©a±„£üð«ìÕ*zrÊ’’jQn:_ê¦iOýò¶Q—KiRÌ3̧Š¥U|X|Në’Ça1÷•Z¼5”gR¬¡Ur>\ý¢Ú×1ÙøwÃþÜÉemÿ KâWƒ¼=¨k7»ì–^ð—¯~%øËYÕåò¥Ž ê:'‡§ó¿xÁ[ãŒ|IÁº•2ü¯6¡…ÀÐS«W‡°”ž[^5)©SþÑÂOŠ?vßSžšR”Æoø'×ü·ö‰ðÅíã÷ÅÏ…ºïƒô?†ÝÝï…,|Gài¼g6¯ã-äAauàÍÆþñ冑cq»SÐ<]§h^&³þÒ³³ÔH½±'o‹áîÌhc)æ¼-J0ÂÍÊŠ©A×s­hÉÐ…zXˆÂ/Þ§Z0¨¹’—#ާôߌ~3ð^qؾáÜÿ ™bóì-:Y…\m²8l³z´©æ˜¬«”ÖÄÖ…¨bòêØ¼ _aV¥'‰¥U¸/èãGý£äc:k…õw²0¦­ià/ˆš%ÇŠ¼=%Ç™*Ÿ|>ø—oð¿ÅúH!F™ž§â{˨+N%F_Ò!™<)M¯Ž8|LZmÝþ÷ŠXZÐÑ?w–U[V7sø£Á r¼6/0Ã*¼Ï W8ÉqpËñªOêÎE<ÿ-Å^rƒöî­ :sæ­ƒ‹OÑáxx.×~ÃǾ‰y=÷Š~øóJÑ,,ÇúÍSXñ1ðüÞд«|J÷ºŽ·¬éÖÚ|Ky%µš­ÃtýzŠþ$q–îUp؈B1ë9Õön•8-y¥RqQIÊMGSÄÿU3JŸîu²ŒÊOÝ,¿<Êqªµ_ÇÃ`~¹~/RñT¨ápÕªVœ£NŠ©U¸-]'ãGÁí~Y`о,|4Ö§‚?:htŸø_Q–‹Ëž«3Çv ½ÀRÄ.r@«†7Qµ ^mjÔ1¤Òîí6sâx_‰pqŒñ|;žáa9rÆxœ§0¡JÍòÆUpñR•“vM»]ž—â/ë2hºî«½º£Îš^§e¨< !`2ÚO1\«g «I¶…Zu/ÉR¶ü“Œ­~önÇ—ˆÁc0ª/„Äá”ÛPxŠh©µ«Qu#f®›Jö¾¦ÅYÌPv§«é:, u¬êšv“k$ËoΧ{maÎé$‹Mu,Q´ÍRȱ†.ÉŒTb&s„ç8Á7kÎJ)½]®Ú×Fíäͨa±©ºxj±\Ü(R©Zj ¨¹¸ÓŒ¤¢¥(§&­y%{´y´ÿ< <óYxJïPø—©Á4–³Ù|4Óäñ„Wñ¹ˆéºÇˆtæÿ„GÃz›±ÓÄÞ!Ñ¥†9îàC`²ÝGÌñÔq¢å‰’m5†‹¬£-¹gR?¹§+ô«R _™Ú7g»ÍáÕÌiÑȨN1© ¹íe–N­.e_ ƒ¬¿´q´ynÕLJ.œ«8Ó—æü£âÏÅþÉž"Óom†…¨|Y¾Oh^ð•µÿŠuVÑ.m§¿ñˆ§k{ÍàïÍ}®i ·ˆÂXë°x§NŒE&ÛKk[[!1ÛÆe\ ‹lªxJªJx*ÍC™4ÝÎU!f÷娪ÇÉ%++éô¬áÜ6]Çøn!ÀÔ¡‰së:¶›¤A4žTSjwö¶K.Ö*9.¥‰MŠÏ±X¶ÕfÆ53© jóœ`›²s’ŠoµÛWfô0¸œT¥ 6¾&q\Ò U+J1½¹¤©ÆM+´®ô»¶ìãuo‹ß ´†]wâ‡Ã½;—t·“Vñ¯†´ÔÐt…ï58VWE`ΨX¨ „ñ˜Jvö˜¬4/·=zQ¿{^J秆á¾"Æ9G gX©A'5†ÊñÕÜvNJ• 8¦ÓI»]˜ð¼| uχ¡ñŸŒâošÛPðoÃßx‹@Ô zóIñe–‚ÞÖ,Ã|ŸiÒõÛÈä}É‘£”$}zƒþ¯Yt•=z”俚£OØÍy¤µôg_ú©›Sÿ}–Y–Ii:9žs•`±”gÿ>±9u\ZÌpÕzòb0”䕜¬¥ŸðüEÖ¾_ üÕìaoš-_âW‰|=á:æ ¾[k«=3÷;ñjºÿ¯»Ó¼Eáß _AØö›Ã%´'Ö15?…ƒœV¶ž*­:1}š7^·›Jt¤—÷´GöFI…×0â\5i-%†Èð8ÜÆ¼'gN¥|m<§.iü4ë`±¸úSÝÕ;T‘ÿ¯ÅOñâ߈:o†´¹¿×xᎉ5Ž¢ “‹>óÇþ$½Õu °â4H5 xcÀ:­¤WÞD©|tíGN=–.¯ñ±¥½<,eg¼^"¬§'å:T°óIÊÍK–Q?´8ÿ"ìš¾?‡ŸbãV‡:ø+SÉð4°ôiµÌÜðøì~o‡©(Rç‹¥í¨Vþ^¿à¦_oþÿÁC¾ÿ‘Ñ/tíOöiµðv¦–6çW»›Å~4׿·ñGˆ¯µ{›»iµMnãÄþ¿ð׃u½Væ÷\ŸUÒtÛ]>[Öû0±¶ü·‰ñÒÂqê4å劌”W9ðÆ‘âK[NÝí5-4êV‘Ïq¥ê6Ò Ã¨iwM>ŸzŠ^1sm/“$±l‘ÿVÂbaŒÃPÅSRP¯Jdš”y•ÜdžÒ‹¼eæ›ZŸç÷丞Ï3l‹:U1N?‚©Z„ÕJý…IFŠ3OÞ£ˆ§ËZ“v—$ãÍÊñ^]ŒP@û¿ÿuÿ“|øÁÿg/ã/ýVÿ +ð¾.ÿ’‹2ÿÿPðçú»ôuÿ“5Á_õã9ÿÖ8?ZkæÏÚ€ ( € ( € (ócþ ãÿ&ñþʯì•ÿ­ð½Žÿ‘æUÿaØý8Íüaÿ“[ÇÿöJçú‰Pþw«úÿ€ ( €!¹¸‚ÎÞ{»©£·¶µ†[‹™å`‘CÒÍ4ŽØ q«;±8Už&ÒM·d“m½’Z¶Ê„'Rp§N.u*J0„"¯)ÎmF1ŠÝ¹I¤—VÏÍëoYØü^Ôi Îñ¦é/Ä–ñêéðÛUÿ„ŸáªÏ<†<Ùjºo¨4ê6^Wš/ôËÝ i…õÕÆ·_6©Æ8ÉfN ›–é^)Ma§íp×}§"…iF׿Œ ýææ~ßSV¯ Ñà˜bªJ—·ÄpµÓ–YÞê9ã„R—ïpõø‚Xœº…^k{ô±Q±„p§é2²º«) ¬+ÊFA¸ äúSðöšm=m4÷MoqhP>­áí_X]Ðô}imŒlº¶™e©-»JJÐ Èfh$1í.ghÄN:–ö„í{sÅJ×ÞÜÉÚýNœ>7ƒsxL^' êYMáëÕ æ£~U7Jqæå»µïk»nÎ7Uø3ð]0oáGÃ]`Û ±Õ| á}@Û‰¶DïK˜Ä%1Çæy{wùi»;σ©oi„ÃN×·= Rµ÷·4]¯ÔôðüOĸNªqy†öœ¾Óêù¶>?-ùyýž"<ܼÒå½íÌí»2áC|.·ù4Uð=™ù¤Ò~xÓÇ ô ‹“Ãê^øqâO hwz¼Ñˆmî5›­>mVâÎÒÂÊ{É-4û(mãêUü8N‚ë -zøJmÿ4©áªÒ§)µdæâæâ£&£ºÖüþzâñt3Z»,N{–e\AŒ„:Q§ÎðXü]<–q”eg­¥Eí$ÕЬ­éS"á©Áé8d—<_Åk‡«GK™]{J©V…ù©U§5£þ§?èá~0à—àÿ8ê>­_þ†8Ïü/ÿæþÝÊÿèŒá¯ü*ãþ‹þ§?èá~0à—àÿ8ê>­_þ†8Ïü/ÿæþÝÊÿèŒá¯ü*ãþ‹'øQðëÆu¿Œ<~=üXÓ¦ð5Ë + màbZ'…|Gp|eàçÒm/~ ÝKi¡Øhzü>³‚ÊDЬ¯ü/«hú ¦•§i1èš_& Z*µ¯ââðõçÅCËìª?mG‘KÚ„iÔT’©ÆT§ j0‚„~‹ˆs¼¶´òÜÛýPáÚñÍò¼%jµªb¸±Ôy† Ù™šÄÔ¥ÅãS[ƒžaRubñuhãðøœe\M|L±xWo†^2eeoÚâû+¬­¢|*ÊF`~A‚u®¿ªÖÿ¡Ž3ÿËÿù„ùåŸe‰¦¸3†ÓNé¬_Ý5Õ?õ³sÍuOØëá¼ðÝkãÄ:¾© ±Z’ëEÒõ;{™’[í-ü? èöÞº7]@|3k£A§\,o¥Áb°[¬\Óɰu,ê{IÍ_ßn’oW(ºt੻꽒‚‹·*•½Ì?‰œIƒR§ƒú–6”°ªž+BtãuX…ŒÆbjciò7N_©Šh9,Dê¹ÍË£ð·ìÝáÿZ\Ùiÿhk¨n®>Õ+xŸãÏÄŸÝ,¾ZE¶ÞûÆšî¿{gÔRm-. µ2n˜ÂfwvÒ–[NŠj¬Å¦îý®?]ßm%^¥I%äšWÖ×8s8Ææu!Wðd%NÎ+Â9WMÇ™Êó¥–a0tªNíþò¤'RÖ7*Isšß콩kz¥åë~ÔµN—¥Ý:<^Ѿ øJÇI±)hÓRÿ…y/‹Š®o%A·ˆâ+(7dÜUN4—+vWjײ¹Ùi´#íCãïˆëwwå-Õæ‘¨ø_ÁiŠßÍû,Wwßø7DŸìÆ{ƒÄÚ[ÞŸ´J²ÝH›6†0»XŒMÝ®á*T.•ìœpÔhÁÚîÍÅËWvÏ3ÆŒO$e“äŽ>gNž&†?5ä”ù}¤¡_;̳<\=§$9¡B¥îEÆœ]ÛÙÿ…ðÒóþF}#Tø„‡÷eñ?Å^)ø•¡­ïCªÚx[ÆÚιáM'W ÓC££hzuÕ¥ÝöŸe%¶Ÿ}uk5ýC /âÂxîâªÕÄÓæþxÒ¯:”¡=×4!“”chɧËþ·g´ÿÜ142g²«à0)Òÿ z™†W†ÂæŒ3j3 N.µ:µiÒ­UT­JH÷ú„|'á‘ øcÃÞÞ;04=MÒGÙ!ÛåZÿ [[ÿ£Å±<¸?Õ&ÅÚ£hÇD(Ò¥ü:Tééor†‹eí±ãâó,ÇþýÆã}ùUÿkÅWÄ~òWæ©ûê“÷åwÍ?‰ÝÝ»³¡­  €1u øwÄ"×ô \[a0¶Æ—c©‹qs勽‚qœC˜G·Íò£ß»bâ'NK{Jp¯nxÆV¾öæNײ¿{Xlv7æðxÌVÔåsxlEZnÜŽ~Êqæäæ—-ïËÍ+Zîþsÿ á]¯ü‹ž—áþ~i“áf½â?…V×Ó/ú›bÃáÞ¯á?^¸µù…¤ºí®¤mc’xaÙÅÄró}C ¿‡Máû¬%J¸E'ÑÎ8iÒFº:ŠV»KFïíÿ­üCSý÷³Ž‘yþÄ¥ñC [:Ãã«a!Sz‘ÂT¡í$£)^p„£•«|Òµx¢[üK–{i ¶W¶·¢øÂ+9]LRÉ‹ãÏ ø«ÃSI-»Ënd½Ñ.ž”ÍhÖ÷‘ÛÜÃ3ÀFk\F)´ï:¬“Ù¾JôêÒm«¯z׺´’k£ÅØŒ<¤á“äQ„×-Xa𸬶UbŸ4c,VQŽËñÑŒf£;RÅSSq娧NS„¸ÝSöQ𶸰¦·ã-gXKvw·MSáoì·¨, F…nÿg¹„LáT; RÁ@bp+ å4ª[ÚVœí·>+•¯½¯—»žÄ<Ãäð¹f æ’›ÃñÑsJí):|g$›m'{]÷1¿á޼?oòè¾6xù§±ø_ðÇáF™{7Aw©i_ ~øSNÕ/Õ1 j­Õê[ª[­À‚4cûšþ7AuŽXl$dûÎ8\-(Î]9¤œ­¥ìuÄLÆÏ\g p¾m5¤*çÔ³þ!¯J?óî†#<â ½ MÞN”é9·76äî鿲¶¥¥j7Vßµ7íc.Ÿkwoww ßüDð~©§jÂ)Qæµ¾½Ô~Ýx’ÞÖò$[[ˆô}{JxâÝ-”–—²IvÕªP”d³\ÝÅ4Ý9bhÎ3³»R”ðÒª”–’¤4Ö-JìÊ¿ˆ4qjÓ©ÀFµJs§OK%̨WÃóE¨Ô¥JŽw êR“u ñ8LBr´jÆ¥$©®ÏÄ¿³¾‡âÍ4iZ¯ÄßÖÖ¢â+¯3ßþ x;QóaYêþÕô=XÛ‘#y¶†ôÚÎÁhdh£)µ\º£É ˪NU*pwNs”§9ÏÆ2œç&å)JRâÆå)6ܤÛm¶Û»<·ãgÃχΊ~>|Z¼“Æ×Þøq GÀʼn[â7ˆô¯M¨KŸðbËP»¶Ðí5Ë~þÎÂûOº¸Ó´»ÈãÔtÐZþÛ—†­õiÃûCÝySÃ$á·ûMHPrj8(ɨ*ޤ”e8ÆV”~%ïð¶u–má±_êÒŽUGÎpÄñc“Y.šFŒe[Š*ѧSS µ©V§ øŠmѬíJmø3ðãÅßð‹kvVß~+ØZhÿ¾+èö–öº7À¶O"Ãâ?‰£H×î¥72..6H–ë,¬–¶öÖ˼f [ÙM,~.*œ\PÀíUUw|w{¾—z$¬“âló.þÐÂÕ©Â<=Z®'#áìMIÔÅqb|õrL劧ÅãÉ”!tæãêN¤Ü§/Yÿ…iãOú8_Œø%øÿÎ:ºþ­_þ†8Ïü/ÿæç·r¿ú#8kÿ ¸Ãÿ¢Àÿ…DÓ~òÿâ—Æ Bñù¸¼ÿ„Î=#íw“û3ÃZN‰¡Yä`y:f“cl1•€1bÇÕ/¬±XÉK¬½²…ß~Zp…5éEyúÈ£îÑÈ8j%ðRþÌ–'‘/·Çb1xº¿âÄbkT}fÕƒþí¯ýoŒøq5ð£êkþ‚qŸøQ0ÿYªГ†¿ðˆøS:T¿»¾ñ×ÅûûVÿ[hßüa¦ qó&ot KGÕ¡Ù I?Ñu0§•?m$ÐH}J/âÄc$º¯­V…ÿíêr„Öºé%}ÓiŸë>">õ§†èÔ_ UeµÜo£ýÖ.Ž'.hÞ?¼£;_š³QœOøQ 'ýÞ³§x“Æ™4?ˆ_>#|GðÄî9Žk¯ ø÷Åž#ðÝÝųâk;‹*Yì®.-$†tYõ 3øãV´zÓÄâq8šO³t±ªÒmn›ƒqz¦ž¡þ·g°÷°ÕðYmn˜¼Ÿ&ÉrL|ø£O0Ê2ü:œ&¯°§ˆŒjÁ¸TRƒqzz_ÁOƒZ$²\h¿ >iÍ“,Ú_€¼+a,±nåI%¦•¼{Õ_c1]ÊÕG‚ƒnL,Vn8zQmofÔµÌ1SÄø¸Æ®#ϱ0Œ¹£Fo˜VŒef¹£˜‰%+6®•ìÚ¾§e¤øgÃz Í.…áýE’áU.$Òt› 9çD%‘&{;xZUF%•\°RIkxS§Nü”á ïÉÆþ¶J癈Ç㱊1Åã1x¨Á·ˆÄV® ÞÅUœ”[Z6¬ßSn¬ä (åOÚ[ÀVÞ+¾ø6t{*ׯíñRïPð׈%²¶ûU¿Š<'ð_ã‹<-õÖÅšïLÓü] èW÷:m̯ay ¡¶º†Kwto+3 «Kɪï)R¨Òº«KŒ­AÉï(Ƶ:rqo–IY¦›?BàlÞyu.'úÕ\EL©põ:8ìjÏÙϘñG eÙ¤iS»:õòìf.Œ+ÓŠ­NUyéÉM&} àÿÙøËÃ:7‰lc–Þ-VÐI=ÇšV£’ZjÚ& „+Cªhz¤šF©lê’[jW6ò¢I(ô(ÕU©B¬SJjî/â„“jp—iBjP’é(´|^e€«–c±8²Œå‡©Ë°þ"ŒÒ©‡ÅQw|Ô1xyÒÄКmT£V‹jI-jp…P@~ïÿÁäß>0ÙËøËÿU¿ÂJü/‹¿ä¢Ì¿Å†ÿÔ<9þ®ýäÍpWýxÎõ£ÎÖšù³ö° € ( € ( € ùëö­øiûL~ÏŸþ \_¦‘{âÍ/M¾ð¾·7žÖÚ¼â #Çj*VÅÍk 6ðÉý©T|ª²ZF‹”dÔªBÿM—ÆY /íŒL],Æ¥.¡$Õzx‡R gSƒ³¥GVYuIrʶb¨Ö¢ªRÂW$·ÿ’ù¬Ù ðßþ¦~+¡ÈÂöKÿOÖÿäÃÙKÿÕ^<ÿêî†zŸú?‡…Ìðü(Ô¤ÿY|<¶ëw€n.~XáÖü ¢öÃö2"MªøMÒ®`¹ÖumÅ—VÅöf°ÒÒÚÂK£§ne‡o¤è®hÓ‹³•Á§9³Fkÿ tåžÐ÷ñ¾Îâ ø‘ƹû9秬¥…Ì›¥[U7>m[ à ‡ÅeôçëõØ|ÐP@P@P’xæ ü%âŠö0Ë=Ž›£\xâ%´oquyàØ¦›UÓuÛ;deÎ¥à}Z[ëÅ·ŠE3øo_ñpŽÓTÖB·‹’ºtjGÜcOvåE78ÔŠë*r•–ôêV²œýš_G”Î9Ž ·U”cZ¾*Ü–¬ä¡Nžfã=|%ZÝ…× TœåËŽÁåÍÕÃáž.sõh'†ænm¦ŠâÞâ(ç‚â Xg†TE42ÆY%ŠTexäFdt`ÊH ×Ri¤ÓM5tÓºiêš}Sî|ôá:s”'Bp”¡8M8΋jQ”]œeš’i4ÓORZd…P@P@P@P@PÞÅoñSN±OÞxwáüO5 Wî]üHñy§hº[7"X¼1àÝgTÖµ;IË:‡Šüm8ºÒ."Ž_¿ÅÆ;ÓÁþòO¾&¤%GÏÙQœ§%üÕhÉ;Á¥ô´¿á+‡ëÖwŽ7ˆÿÙ(ÅïO$ÁâiWÅbXË™á°øZ"ù½Ž_™Ñœ]h( € ( € (Çþ0ÿ¢Áðß]ÿV4/Œóv¿ë­?á,Ôføt<3!þÑo çË¥ÛM¶ÕguãÆh°Õ?çÞ3¯Uí¤ðÚ‹Ûò?îÊWÒçÒð×ï'á7úß ç?» _ìêÎß7ÙýÂÊþ·f¯[MFõ}ô¿ uÍWÄ:n›©j¾ñͨø‹EЬnuMSÃ^(ž{‰/üY¦é6±Ýjz¶™â_:Ö?izo‚Š£‚ÅbëC‡Ç`!FŽ]_QÓ¡‡¯å©,#.\E ¿Rz_UÀÒ«êv7ö:¥•®¥¦^Zê:uý¼Wv7ö7ÝÙ^ZÜ – ›[«w’ ‹yãe’)¡‘ã‘:3)ºã(É)FJQ’N2‹MI=SM]4Ö©­ÏŸ­J­ µ(W¥RjS•:´kBTêÒ©ã8T§4§ ÆI©FIJ-4Òeªfa@«j'K°šò; CVº om§èÚL1\júæ­s †‘¡ióÍmú¶·ª\ÚiZd3ÜÛC-õݼs\A4©†+K‡­Š¯.ZT)Ê­G¥ùb¯ev“”Ÿ»Ú¼šWÔõ²“0âLë,ÈrºJ¶a›ch`p°nJÖ¼ÔyêJ1œ£J’n­i¨É”'.Wk?ê‹ö#ý›îeï€:ÃýzòÃTøƒ­jÚÏ~)kMÕåΨø÷ÄòÂ×¶úÚíìY4 h–Z‚ônŸs£xjÇVÖ¢ºñ¡¬êwÿÏ™ž:y–?ލ­,EW%,"”)CMù)ÆoVÚ»m¶ßûÀü+…àžȸ[7V–O ÖnRxŒUYÏŽÄûí¸¬N6¾"º‚´iªŠœ#F1_[× õa@P@P@PÀŸµüçáoí1¨\xïJÕµ/„ß͵¥»üAðÕ­¾£¥øªßN¶’ÒÃNø“à«Ù!Ò¼[konm­á×,n|;ãû;=7JÒ,|ioáÛIt;¯c)ÏsžnXJ×¥'z˜j¼ÓÃÍ»^NŸ2å›I/iÛFÚÐüßÄ 87Ĭ,iq\££.w€T°ÙÆ1U9),[¥QשUœþ§‰l76†ïTðýÄ·65•^>ÆÕŠÔ¨Óï7N½hÎQ³÷TÒæ…Ý›”••“Wg~_ôDá¬Yâæ8ºÊ›Žc2œ¾¾W(Þ´ðµ*JŽ*JŸÖÜ©PRN÷nMlb×?à‹ßüO¥Ýhž"ý ?g½sH½.´ÝSàï¬çò¤I¡w‚ãã4‘ùOsÛÊ– ˆãžIcGZ©Çøš±p©•á' oÔ©$ìî´qÝ=SÝ=V¦O¢O€ÄSÅ`¸÷ˆ0¸šMºuðø%*±æN2Jp¬Ÿ,âÜ'ùg J2N-§ä¾!ÿ‚G~Ü t8økã?ÂÏ:Z† u8#øs«èÿ4-ìöF²Ðí%ñׄ¼G‚ii¸ñ®³>¥©•KñÔââðTj¤ß+©Z£œcehJJ+Ÿ—[NW›Vç”çyË\Ãè‡ÃÚÑÄSâ|Ë9SKO –ác†«ˆæ›ž"• ˜Š‹ í¢à熣%†§UMáiaèJž‡ß°¾'krøOÃ?¶7À«O[ØO«^|2ñ¯ìåñ‡á¯Å‹ "ÚkheÖu…¾)xWâ–ˆ^öÅ Öî|5“w¡§ÝYÞÏk¨YM>Ÿñ±Ÿô.Ãàê¿äpÿÄœpïý™×þp?ü´öøsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÓÅ5ïø#§íkð–Êó^ð/dž<4÷°¾£ð·Bø1®[ê^³®÷Xøk±ñ£G†ù…ìÑMªø#XñEŽýš×¾ ŸNÔ4Èü)âìiñæ.”¥É¡ì¥ª¢êÍœ¯vé>U(ÆW|ÐnPO—ÙªiIOÓÆý¸{0£Gë+ðöz.mn­žžÚxãÛþ"3þ…ØoüWü3þ$ã‡è´Î¿ðÛÿå§«ßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùhßioú9oŸøc|}ÿϦøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–‡ü9÷ö–ÿ£–øÿ†7Çßüúhÿˆ…Œÿ¡vÿUÿ ÿ‰8áßú-3¯ü6àùià#þ ßûAøÓÅW>ø3ûGü ø‹¨ézþ‡ã¿èÿ¼[7ß…ö2Omyeâ=pürfñ'Œ­®¬õ+(¼àëmoQ±×4Ñ¥|CÔ>XêZ܉ø…iÛ/Ã&Ó³uj´ŸFÖ—³Ö×Wî)}xf5iʯç•iF¤%Vœpr©MI9Â59çìå8Þ*|“åo›–V³õß Áÿh_ i§N°ý¦> ÜËqs>£ªêºÁ/Üjúî¯w´ÞkÅÚ|d…noîvGîá‚ÒÎÒ ]7Mµ²Òìl¬­â—b©G–9v¶Ü§9V¨çRrø§6¢¯'ä’ŠJ1Q„cÓú#d™…m[ŒóxFáG‡£–`¡†Âa©ßÙa°Ôå^n¡yKÞ”êU©:•ëÔ«ˆ«V¬ùŸˆŸðFÚ®x4Ÿx_ö„ø«øÏÀ“ß꺊~øÛH‹ÄÖ—öéúï„îµ ¯Œ[¦±c"\érÌÖ–¶þ)Òü;y¨ÞE¥ÚßG2©Ç¸¹Î•OìüþÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÐÿ‡>þÒßôrß?ðÆøûÿŸMñ±Ÿô.Ãàê¿äñ';ÿE¦uÿ†Üÿ-øsïí-ÿG-ð3ÿ o¿ùôÑÿ ÿBì7þ«þAÿqÿôZg_ømÀÿòÓ›ñü[öŠñ§…»·àڿৃ¢»ÕPñ'†m?gïÝø?Ähºë7shñšÚM#Äz”M+ÂC¡_éÒK©y:†½eâ$Žk;žõë œô08z)Ïš¥%RnK»Í¸YrT’¿ï)Ê-ÊÒ©š§í?¢—âp šñ^o™ÊžTpXê¸,5<ËÉMÇ N8¥Zk‚¡._ö<]*ñj8J¸7(Õ‡•\~Àßµ‡çžÓã‹|-àH!–H“Æß¿g‹Ÿ>Þ€ìé8Ö>üGºø…á­>+î/õ]âOÂÏø[GŠÚXîõÿ2[!yÓÿ ÿBì7þ«þG‡ÿqÿôZg_ømÀÿòÓÔþÁ8üyñ‚V…Ÿ·ì«ñèYÃâ+o ü2ñF·¨øfçP[‡²²ñ>—cñÎ]KÃz…ÊÙÞyZ~¹ia|M¥Òýœ5¼ÁøˆXÏúa¿ðu_òø“Žÿ¢Ó:ÿÃnÿ–ž—ÿ}ý¥¿èå¾áñ÷ÿ>š?â!c?è]†ÿÁÕÈ?âN8wþ‹Lëÿ ¸þZðçßÚ[þŽ[àgþßóé£þ"3þ…ØoüWüƒþ$ã‡è´Î¿ðÛÿå§èì¹ÿÝøGû=ë¶_ |Ò£æßSÕõüñÂBqž­• |\ð_‰5 j¾"Ò­¾ØÎ’>gà›W¶KûÐþŸEð÷î>,ü&øëð†î—P»Ö~ë?<eœßMñà±ø—à«-Ú,Ú¤ºÖ¹¬è¶ú.— ’x¶? _Ç6½ø;Æþ ø‰ Ùø«áÿ‹ü/㟠ê›x;_Ò|M ß‰³Ö4[»í>ätba¸|Rxa¢€ ( € ó/‰þü_‡K‹âoÃß øÒ][©ü;©kzE¬ú熮/Òµ Ÿ x=sÃw:…½´6š…Æ‡¨XMdŸb»’kFxXÊáF|RðG??héz]¿:€¾;éÓþО ´ó]Ä!ƒF¿›K³Ò,-ü¡Ûx.pþOÆ/qñ“özפÓzŸ~ÏÚáø×áëEWYð΋ào‹öÓÊ]V×þ‡^7ÓÂCwq«jš(û$7`Áðïâ¿Ã¿Šú}æ£àØkÿÙW e¯é[.ô¯øSS‘]†ã?ëVÚwŠü®ªÆí.…â½GÖ! LÖ((Ðè € ( € ( € Ži¢·Š[‹‰c‚#y§žgX¢†(”¼’Ë#•HãÝØ*(,ÄM|Ó{ûAêþ-¼»Ñ~|.ñ'ũ๟O›â.©wkðóàN•{o+¤qñ]KxâÉŶ¡om{ðOÀ?´èõ›k}/Äz…íoÓX€·ü(OüHýçí%ñ+þýÿ|Ÿþé:§Ã„ p~X‰­¡ñ&»ãï‰ÆÊÖK›ôÏøÕ¾k“ÜÝ|)±Ö´ÿ Ëá ¤tmGðî‘¥øÃúV›¡h:Ÿg¤hº&ck¥éF•§[Çg§éš^›ez~Ÿai 6¶vVÅmkopADŠ J€ ðßü ÐüCâ¾#øù>|g6Ö¶c⇇t{ «ýoM´È‹Ãß4iͽ‡Ä/ ‹oÜÁ¥kò5Þ"[ê>Õt ^ÎÏQ€„~5_Câ=3áÇÆ? Üü9ø©Ë%¦‰{eµ®ü(ñôñ[ÍvƒÀß%Ñtí,kW66÷RxÅI øâÙìõoì½3Äš˜£ €<Ÿâ?ÀŸƒæÒ¯>&ü/ð?µ];Èü;¯ëÞÓnüOá•ÔZÝõáy â > m-…ôº§§ËxÇËËí 4ÿ†_·ðßï~üløûð–Cþ¿L_‰Æ/]Eoÿ 6þÐñü[Ó|¡hÊ÷0Zèÿ ßáÄ6wXêR^Ùé‡G?³¿l_|Ö~&ø ñÏL‹÷pXkþñÀo%¥¯Î·Ÿ‹ô-gã'„|U¯jq³JºwÃ_†%Ùû|p‹Y?³-Àøhxg÷_ÿf?þH?suâZxSã‚nîÇïdþ‡ᯈõ/‹òéÑY‰nSñ_Á¯ÄòÛMcR_O¦A©vÞý¤>üHÕ†¼+ñ;ÂòxÚ0}ðÛ_»—ÁŸôo;{Zü'ñœÄ =ýº®¡¦'ˆ<-¦¾©¤\Yë:rÝiWÖW“€{mP@P@P@1ÝüGñçÆK«à ņà»YæÓüCñ÷ĺ&©wd÷QHË5Á/ jºm–•ñBQeþ&]jÇá^‘uyaýˆŸ¯¬|Uá¥ðWìãðÃÂ:ý¿Žu-6ïâWÅKtx×âÿÅ[‹üIµ‚R.áÝnþÒ;?xvI“ígÁ¿ ´¿x-5nõX¼;©¨j—@ï@P@|·cÿ'­âŸû5¿êÙø‘@RP@P@ ãٻᗉµûÏè6z‡ÂŸŠw¤=Çů„sYx+ÇÚ‹¡i ‹Å—Pé÷šĽ&Öwk» |TБoã-K]VÿÃW“Cms©ø;\“Vðv³uc¦ÜëüúfŸ%°ƒþ"x¯Ã>#Ó>üe´H5ÝFI´ï|Q´KOü]žÊÖ}BK±¶äðwÄhth$Ô5?ßEŽº4ýYðƧ£é½–€ïÔP@P@P$‘"G–WHãI$‘‚$h€³»»ªŠ ³3IPÊÚFž?j[kx…õoÙêå`Ô< à«}GSÒOÆm>R²Ûxßâd:uÕ…ÍÿÃ]O‘íôƒzòÏ¡øŸ@¼ŸVø­£jϪé¾ð€ÔvVVzu¦Ÿ§Ú[XiöÐYXØÙA­•¬Kµ¥¥´ ÛÛ[‰ B‰1"Ǫ(ÍP@sž.ð—‡|wáÝSž+Ó"Õ´-^(ã»´‘æ‚T–Úâ+Û BÂöÖH/´­cHÔm­5][Ó.m5mX²±Õô›Û=JÊÖê "øâß4þ4ø?ãýNmgâÁÛÎçÄW© ®¡ñ À(Ó§»ðÄùtè#Ž+_íé´¯xCVšÕ¦°ºñ·Ãÿ9 X¤Ó¬>€ € ( € (Ä:‹âÍ\ð·‰t­?]ðï‰t}O@×ôM^ÆÓSÒµY²ŸNÕt­OM¿†æÇPÓõ ™í/lomç´»¶šX.!–Ã?eokZŸÁ½Âþ/ÕucÇÿõoà·Ä C[¾»ÔuýWÄŸ 5|1Šõ«ÝFiµKéþ#xjÓÃß´í[UúŽ¿ xÓGñ Í•‹jÂҢ蠀 (‰ñßÃ?‡t‘ üMøàŸˆºga¢øïš‹´ÒlóiÞ °Ô,Ã?—óäå¼´Îv.xšÏLÒt›@¿ÀßcÖ|Ekðóâ?„u_„_¯a»ŸIð§ˆ5-'YѼmi§[És¨ê¿ ­ðÄ×—^ÔôëßþÏz†£s6£t| Úx[Kñ¯ÃKX¹yo/5‡~'Öíï¼?.¯'ö†©ðóÅ•¥Æ¹uà_ê€}/@P@PñÀžø›àÝ{ÀÞ(ŠæMÄ‹ÓX]=Ž«¦Þ[\C¤ëš&£3izþ«ZØëz«úF—¬XY_ÀDÖèh€øãÏxÃÁ—ºe¶oŠß 5ûï†ßÖÚÙ,!¾ñFƒ ¥ÎŸã =62Ñiº?ÄÏê>ø™ éÐÍršVâûM[©¯të½€ã@P@P@.~Ô'þ-Àß³Õ©ÝqûBx°øWÄç;â±øAá:ãÆ_®µ+aÍÖ“â_ é1|%1‘-¿öçÅûFÞ}(ߥ}F0€€ ( € ( —>+ÅûE~Ìÿ"ù­¼ksñ+öoñ'ýÒÖÛÆ^¸øËá/êZ—ï#šMÄßíC¹ŠÝ5OãD·º¬:…•®‹â¨è € ( € (åÏ„ñO~ÑŸµw‚eùeÖï> |vÓâµù´ø´ˆ^½øL†vo-ãñ Ç‹g^j–é ¶£H¸ðÕâjÝ__Xé`QÐ@P@r9ð„~$øvëž7Ñmõí îkK§µ–k»Iíïl.îÃQÓu-:âÏTÒuK ¨£¸°Õ4»Û=FÊeÛ\ÄüЖ|,ñgˆ´jÿþ$j“ë.Ðô›¿xÅ׋ Mñ#áLzÄz4Z…äÑÅeÿ|{y£h_LÓ¢°·xÄ®m‡m4ûP ¨ € ( €)ê:ŽŸ¤i÷Ú¶­}g¥éZ]Ö£©êzÔ6Z~§ÙB÷7·××·/µ¥¥´R\]]\I0CË+¢#0ùÓöKÓµ ƒø™â[Ë/|z ñÃÇIªÛOc¬ZjllµxcRÓî9t§øyàDðŸÃ‹m!¼étÛ? Ao{¨k:˜¿×50¥è € ( € ùnÇþO[Å?ök~ÿÕ³ñ"€>¤ € ( € (æOÚÊÒïJøQwñƒCµ¸ŸÅ¿³¶¡Ç tød¹ÕïôÚ^Mñ3ÂzM¤JN¥¨ø÷á=ÏŽ|¦iSÉmiw­kºT³_i’ZÁªØ€}'iwi¨Z[_Ø][ÞØÞÛÃwg{i4w6—v—1¬Ö÷V׳Ãqoq ¤°Í¼rÆë"3+@,P@P@òåßüPßµï‡þÁû­#öø7ãâ‘sûèÿá=ø¯xxMaÙ5οàoŠ#ñl×âûO½·ð¢Ó_A¿·Ô—Å QÐ@P@PËš/üU?¶Ooî¿Ò­> ~Ï~ð¿…fOôq¤ëßüyã?üZ°¹EòäÔÛSÑ> ~Ï÷ÖsܥŮ’¶—°iÃuªxŽúŽ€ ( € ( —?jÿô |ñQýêxSö¤ýš Ù‘¯¿á`|_ðÇÁ¨öÏó ì¹þ&EâÌS}®-M5~Î÷Ë{jõP@P@|¹á/ù=_ÚþÍsö?ÿÕ±ûqPÔtP@P@|¹ûUÅ#á/ üyÓÿu®üñ–ƒã åoøö½øk®ê–øÓ£jJ<¦’ÝþkÿˆôO2ÿKÓ¬<{áokºõëø{GÕl/@>£ € ( € ùwö×bÿ²wÇÝ9¿ãÛÄ5ßj_ß:7£Oëbÿ–WGHÖïE¬øo³Ü˜§Øþ^Æú…T*ªŽŠŒõÀ  € ( € (å»ù=oÿÙ­ø ÿVÏÄŠú’€ ( € (  ÝkJ´×´}[C¿gM¿Ò¯D/åÊm5YlîDRa¼¹ 3>ÇÚv¶ ýŽ5[½söJý˜õ{ã¼Ô~|#º¹1'—–OèE¶&[jç ÉÇ­}#@P@PËŸ?âWûD~ÈzõÏÍe¬x›ã/ÃU‹æ¸_xà÷ˆ>$XO26ÄM-4ƒ~+†æåe’â=NçE·ŽÎ[{Ë»»¨è € ( € (åσÿñ4ý¢?kÝzÛå²Ò2xRk—•.$Ôíõ«y,â·´µ»¿úŽ€ ( € ( —?l¿ô/ÙûÄ.—/áG޾üxñ,iÍåǃ?gߎŸ þ7xâ×H‰¶Åu¯ÝøCÀÝ·‡lng²²¿×eÓ¬¯µ-2Îyõ`¨è € ( € (å¹пmk/²£ÿÂKû-êŸÛþWÉý¯ÿGÅ#þ·ãþ>?áÿ…ƒãìùû'ü%:×—¶Ë©( € ( € Ãñ?†´/økÄ>ñN•c®øgÅz­á¯èšœ u¦ë:»aq¥êúV¡k'Ésc¨é÷W—p?É4IpÆ€·á»oøFõOêRɹÿ´,×î¡øöHnu¿x?FÖ5[ˆ­-´ˆíícšúòyÞÒU„q"¢¨Ö™W…ŠÊòÜUl»*Øœ½Y,Ç*µ°ôêT’Œk(Å9I»$’Ù+Ì9Ÿ‰¼_†Ì³ 5}ÒÃã±t)Eà0’j*õ!äé7&£›m·»w=g?èæÿiOü9óò®»ÿâð/ý q?ørÇòó‡þ"ŸÿÐÃÿ†üÿ) üYÿ£›ý¥?ðçÍÿʺ?âð/ý q?ørÇòðÿˆ§Æô0Ãÿá¿ÿÊN{Å~ñÇŽ<=©øSÅŸ´7í­ø{Y…-õ=.ëââÛÞCñ]G­Ÿ À,ðE(Ù"È2HÈ'üBÿ¡n'ÿXïþ^ñøÏþ†ü7àÿùIÐçâÏýßí)ÿ‡>oþUÑÿ‡è[‰ÿÖ;ÿ—‡üE>3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”†~,ÿÑÍþÒŸøsæÿå]ñxþ…¸Ÿü9c¿ùxÄSã?úaÿð߃ÿå&÷Ã|_ð×í#û,ØIñûãoŠ4/|aÖ¼)⯠xÇÆ£_Ð5Í ü øËâXío,'Óc;­õß èš¼ðM ÑÜXF7˜žXßà¼Hðû…øs†jæyVµ \q˜J*¤ñ˜ªñöu§%5ÉV¬àÛIkk®Œûøçˆóþ"†_™âéVÂË Š¬á &Œ¹é(¸>zTã-.î¯gÔýÞ¯ç“÷Sówþ 'âÏhQþÍ>ðGÄ_|8ƒÆß¼I¤ø¢ÿÀz¼z±«iOÁ‰ž%³ÒæÔZÒîh솹¤iš„±À"y¥²…^Cøßï¼7ÈrÞ#âjynmFuð’Ábë:p­V„½¥(ÁÁûJRŒì›wW³ê|OˆÖapõLÃ,« 8¥‹ÂÑS*u—%YIMrUŒ£we­®ºçâÏýßí)ÿ‡>oþU×ô/üBÿ¡n'ÿXïþ^~ÿOŒÿèa‡ÿÃ~ÿ”†~,ÿÑÍþÒŸøsæÿå]ñxþ…¸Ÿü9c¿ùxÄSã?úaÿð߃ÿå!Ÿ‹?ôs´§þù¿ùWGüBÿ¡n'ÿXïþ^ñøÏþ†ü7àÿùIÏ'…oþUÑÿ‡è[‰ÿÖ;ÿ—‡üE>3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”ŸsÿÁ9üYãÏøoö—Ò¤)έJÒR­‡…JÔ«)MÞrm&ì¶Z½ðqÏxo ™fUc[V¶.œ)”\ib*S‡¹N1‚´b“ik»Ôý¯†>Äüñ§‹>0ø«ã_í»ö‚øãáÝ3üOáOxÂ^7¢è:>‘áÃeca§[éŽD—7Ë,²K4ÓÏ#É!Èú+ï8[ˆx[ šf˜*Õ±•qÚs© n*Œ\hâgNšötªÆ ÑŠM¥w»Ôüxï‰2.$ÅeÙn.%*J„ð˜jÒR«B3›ç©NSw“nÍé²ÐÎÏÅŸú9¿ÚSÿ|ßü«¯¹ÿˆCÀ¿ô-ÄÿáËÿËÏÿˆ§Æô0Ãÿá¿ÿÊC?èæÿiOü9óò®ø„< ÿBÜOþ±ßü¼?â)ñŸý 0ÿøoÁÿòÏÅŸú9¿ÚSÿ|ßü«£þ!ÿзÿ‡,wÿ/øŠ|gÿC ?þðü¤ç¼%á_xÂÞðW„hoÚ3Cð·„´M3ÞÑ­~(Þ5¶•¢hÖpéú^n×|Ó´6vvðÛÆÓM,¥#ävËþ!ÿзÿ‡,wÿ/øŠ|gÿC ?þðü¤èsñgþŽoö”ÿß7ÿ*èÿˆCÀ¿ô-ÄÿáËÿËÃþ"ŸÿÐÃÿ†üÿ) üYÿ£›ý¥?ðçÍÿʺ?âð/ý q?ørÇòðÿˆ§Æô0Ãÿá¿ÿÊO3øÓâÏŽ>ø9ñgÇ>ý§ÿhÈ|Aàφ~<ñ^‡-ßÄ_·ZE¬xwÂÚ®¯¦Iues¤Imyn—¶p4Ö·ð\FU£vÊÏ|+à¼Iœcpù~"ŒU˜b¨MæÙ¨ÖÃá+V¥'Vq’S„[Œ“RÙ¦™édÞ%ñv38ʰ˜Œu PÅfX5h¬.T«â©R©(ÒR‹p”’”Zj÷Nçô‘_Ê'ôÑòGíÕâÏø'öZø—âø—Wð‰c»øy¥Xø“@ž+mkJƒÄŸ<á½Vm2êh.RÖòM#V¿·†çÉy-Úo:-²¢:ú™Ž7:ÉðXˆ¹áñy¦_…¯)AÊŽ#J•X©E©EÊ’R‹RMÝ4Ï79ÄÕÁå®.„”kárÜv&ŒœT”jÐÂÕ«NN2N2JqMÆI§³M3òë?èæÿiOü9óò®¿«ÿâð/ý q?ørÇòóù—þ"ŸÿÐÃÿ†üÿ) üYÿ£›ý¥?ðçÍÿʺ?âð/ý q?ørÇòðÿˆ§Æô0Ãÿá¿ÿÊC?èæÿiOü9óò®ø„< ÿBÜOþ±ßü¼?â)ñŸý 0ÿøoÁÿò“žÖ¼)ãj^Ö5¯ÚöÔ5/ø†çÅ~»›âà“Cñç…"ñ—ÄoÚG×µMI㱸¸¾ž}7Àž´f›É´ŽÅͼh÷W-/ì^ð†EÅÛÿÛXj¸¨e}[Ùâkáù>µý¥í¹½ŒáÏÍõzVæ¿/+µ¹ÿ)ñ;г®þÄþÈÄS¡õßí/¬{L=üÿVú‡²·¶„ùy~±Vü¶æº½ìÏÅŸú9¿ÚSÿ|ßü«¯Øâð/ý q?ørÇòóòŸøŠ|gÿC ?þðü¤3ñgþŽoö”ÿß7ÿ*èÿˆCÀ¿ô-ÄÿáËÿËÃþ"ŸÿÐÃÿ†üÿ) üYÿ£›ý¥?ðçÍÿʺ?âð/ý q?ørÇòðÿˆ§Æô0Ãÿá¿ÿÊN{Eð§Ž<;©x»XÑhoÚ7OÔ¼yâoø¾îŠ7&¹â+? xcÀÖڥЗO‘â ø3Ã"­ºÃ µÑ홣7 <ÒŸñxþ…¸Ÿü9c¿ùxÄSã?úaÿð߃ÿå'CŸ‹?ôs´§þù¿ùWGüBÿ¡n'ÿXïþ^ñøÏþ†ü7àÿùHgâÏýßí)ÿ‡>oþUÑÿ‡è[‰ÿÖ;ÿ—‡üE>3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”Ÿ¨¿°¯‹"ý†Ò]cÄ^Òµ}NKk+m!-¬íÞöòv†ÖÝ xÊìh Wd^ð^;$Éñ¸œ¿3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”†~,ÿÑÍþÒŸøsæÿå]ñxþ…¸Ÿü9c¿ùxÄSã?úaÿð߃ÿå'<þñÄž,·ñËþÐß´cx²ÓÃ×¾·ÖÏÅϵEáÝCR°Ö/tµ_ìÿ ÛÜjZe…Û³Bf[ YU «ñxþ…¸Ÿü9c¿ùxÄSã?úaÿð߃ÿå'CŸ‹?ôs´§þù¿ùWGüBÿ¡n'ÿXïþ^ñøÏþ†ü7àÿùHgâÏýßí)ÿ‡>oþUÑÿ‡è[‰ÿÖ;ÿ—‡üE>3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”ŸmÁ;|Yãív?Ú[Ãþ7ø‹ãoˆðx'ã†ôŸ ßøóW\Ö4#Vø/ðÏÄ·š\:ˆ´µšK#®júž¡s‰^)ofTE²4þzñ'!Ëxs‰ªe¹UÐÂG„¬©ÎµZòö•c77í*Ês³ii{.‡îÞçY†ÃÐÌ3:°­Š–/EΩÑN¥ÉN1ÕÞ¶»ê~‘WÀŸl~üNñgÅÿþÒ?µ5„¾6ø_Bð_Æžð׃¼j4 DÐÇÀ߃^%–ÖΠ6BZã]ñ&·©ÜO<ÓM-Åüƒx‰"?¡¼7ðû…ø†ify®µ|\ñ˜º2© f*„})ÅArR«]&îíwÕŸ…xÇoþUÑÿ‡è[‰ÿÖ;ÿ—‡üE>3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”œ÷…<+ãø{Lð§„ÿhoÚ3Dðö –úf—kñFñ­ìá–yn¤Ž6ŸOšbyå”ï‘Îç88ÀüBÿ¡n'ÿXïþ^ñøÏþ†ü7àÿùIÐçâÏýßí)ÿ‡>oþUÑÿ‡è[‰ÿÖ;ÿ—‡üE>3ÿ¡†ÿ ø?þRø³ÿG7ûJáÏ›ÿ•tÄ!à_úâðåŽÿåáÿOŒÿèa‡ÿÃ~ÿ”žMñóÅŸ<ð+ãOŽ|5ûQþÒž#ðgÂoˆÞ,Ð.¦øö¸mµ¿ø?YÖ4«‰m.t‰-úÎ $·ž7†eS¨ÈÌkáGar¼ËG.ÄF¶Œ¯JO1ÆÉF­=J“Œ«8É)E6šiìÓGvYâoâs,¿ [BT±ì% ±X $\©Õ¯NJJ’i¸É«¦šÝ;•ÿe/ù5ÏÙ³þÈÁßýW~¯Ñò/ùdßö*Ëÿõ‰ð×üŽsoûãÿõ*©òíÄoÚÓöŸñ·Å‰?gŸˆ? þüøCñ7ÅŸ,¼Qão…úŸÆ|Qñçëïì_ˆwé1øëÀZ7„¼¡øª;ÿ ió-Ö±­jZމ¬^Ë­·Øáo Ìÿ<Åf(Åà2¬»/Çb2è×Äàjf8œv//gŒ’¦±XJx|5*êXx>j•g:u&ùcÊŸ³,&G“áð?Ú¸\ngÇàècåG†ƒÃbãí0±u>¯Š©_R‹Uä­ pJqÖ\ÌîuO‹Ÿ< ñ[ö øUñãáÕÆ½ñ›Ä4O‹W^ Ó5 jvÿþø£Åþ Ô¼&5ë©5_I¨ÿfèÚŽ·§]Mª‹K›CG·Ô/m`ƒQŸª¦ašáqü3€ÆKêæU³ZY„°°©ìf°x*øŒ4ðþÚN¥>Js« :œ²s¦§(¥7Ï Y‰Áq7±j––YSDáía,V.… D+û4¡W“ž¤)Í(s%Ž1mÅ;À¶Ãÿ þÌ_ ¾4ü`ø™aâùü{©ëzƒ©ü=øsルüFñ ¯‰¼Kco¢x á5Ž•«|@Õµk=;D.ôû òæHôCYØšs CÂqŽGÌól1:”©OƒÅ{Lee^´,&_TÆT©R|Ð…9;S]!¨±9*¶qŒËðIPXXS«V¬^Ùá):Te*˜œt§ ,!)ÔN3•H«Î4õž‡Uiûs~Í·?>$üy¹ñ†µ¡øàåÔ_¬üUà_øOÇïn¥ÓRÖÓÆ üG iž:Ю.¡ÕôëûT¿ÐaûN›rºŒKEyWxñFLòÜnk,EZX\ºJø×Ââ¨bð““‚Œq*Ô¡‹¤äªBq碹¡%5xݘˆóu˜a2ÅBLN`œ°R£‰Ã×Ãb¢¹ù¥CJ¬ðõtç8Ôvšpv–†7„¿oïÙËÇÞ-Ò|9{ñFë\ð·õ_‰6~ºøñ“JñÄ?èÒÃ÷‰~èZ§‚,õ_Œp–êÍmŸáͯˆä½¶oiÉs6x~,ÉñSÄS£,tªÐÂÔÆÆŒ²¼Êlf›Š•lº•L,jf0¼¢¢ðq¬åÍÓ¹¥~Ͱѡ:±Á*uñ0ÂJ¬s,éaq5”hãêC(`'e.o­ºJ.2Ri¦fÁ?¿leý´>x+â^£àßø;Æ—^ ðf·ã{mSáwÄÏø ã[ñM•õÌ‹ð»Ä¾?Òmtÿˆžµ“N¸k×¥ÉõŒf2½<.ƒŸ$jâ+SŒ§i8Ź(É«?æüŸ-þÕÌ(àÝ_aIƵ|Mwa„ÂÑ©‰ÅVQºç•:ªJ1ºæ’I´›gøÂ?ðP-/Äžñ7Ä_³§‹¼=©jšoü'¿ t/ƒÞ,ð’øcE¼–?íøA¾'¿Ä/ê Õt(¤‘´ñâOØYë¦ÔCpúA»6¾~Ű¯B¾32ɱg8}kG.Äaý…95í>«ŽxÊÓ­:I¾Om†„jòÚ^ÍËš=تü/:5èárüÖ…XÂVÆÔÇЮëTŠ~Ïë8?ªÒ…:u_Ç챕;Ýsò¸Ë̼ûSxû\Ô.m¼Eã?xTCÿ*ñßì³£ÛÜx/YÕ¤ñw€|=áêÚGƒ,¦Òd¸Dñ}óé‡Rok ;]"îÆâ8îu 0Ü8l÷VrlN…¸Ó‘SO V£Äa(ЯRž.›’§ˆ“‡;ÄÔµ5r‹JS‰Ù_&ÃSŠt°õëß„pÙÍF±à¨bªÖ£ ˜‰*–u(GŸ“êôïQÊq’mFG°üKý½ÿf„þ4×¼âïø©î¼sigñÄÞø_ñ/ƾÝÞÃÌVß>%xSšǾÜGiuk{yеí2M>ÎêÞêùmá•ú8Þ+É0š¸lF"»–QŽ2½ 7ƒÀÊII,v6†¦ÔeIW­ÉJ|©ÜàÂpÎoŽÃÒÄУA,B”°´kc0˜|^21m9`ð•ëCŠNIÆ.…9©I5´iüaý·gÏ‚8Ò~xÃ\ñ†¯ñÄ´ø™áß|7øcñâ·ˆüC૽bÿD懤ü9ðω¯u;k[Í6êM@ÚÃ!°²ßÝùV—6òɦcÄÙFYЧ‚ÄÕÄTÅÖÂÇG ƒÀã1õ«a¥Rt½­*x*å8ÆP“Ÿ*|‘´åhÉ78Í3,5Le xxaibe„«_ŒÂà©RÄFœj{:“ÅV¥9FqQækšWŒo$Òù×ã7üÛá§‚'ýuï†ú?Ž~)|<ý£¾"ø§ÃÚî³à¿‚_ü}¬èþð¿Ã¯‰ºÎ£¥èú?ƒ<3y¨Û|TÓjÚeÏŠ¬|1Œõɼ9‡‡5-_Jñó.6ÁaeÃÕpTñXü&sŒ¯F­\6Y™bêS£C©:téá¨Jk ^:˜:•xPXš®Š…Ô§êåü‹Ä¬öž.¦ŠÊ°”jÒ§ˆÌphT«[ƒ§Ô©ˆª¡,°ÕêNºsTeYЦ«9U„'úU k6Þ#д_Ù[ê––zö“§k6–šÞ“¨è:Õ­¶©g ôú¾‡¬[YêÚ.© S¬wúN©gk¨é×k-í´0Ëý*‘­Jh©Æ5i¤cVœéTŒg$ªR¨£RœÒv:‘Œá+ÆII4|Zr£V¥)8JTªNœ¥N¤*Ó”¡'éÕ¦å m^5!)Bq´¢ÚiŸdÁ5¿ãÏöÇÿ³¶±ÿÖ?ý’+ù Æù.qÿö —ê3úŸÂ¿ù#p?ö˜ê]Sôº¿0?E?îä²þÔŸör;ÿÓO…«ûÁÿù!°ö™êeSùWÅ_ù,±¿ö €ÿÔX|_ø·ñëÇ_.ÿf/Ù—Tðu|=ðÿÄ¿ŒŸ¾"xgRñݧƒtïjÚî‘ð÷žð¯xZ?x“Ä­áOj·×ÚLj´í/GÒt˜Ö(u ËüYý>cÍqY¬²L’x\-L6Ž72Ìq”'ŠŽª•iàðø|$*ÐUk×ú¾"¤çR´)Ó§M$§)û¿;€Àå˜l±gÄ18šxŒU\…­ 4«Ï u1UëâgN³¥F·£Æ)N¤æîáûÜwÆ_‰µ·ìÓû3|Hñ¯Ä?|"ø‰ã/xûàVàx[À:σ¢Õü?ñãGÃÿø¶ÛÅ¿õxšÃNÕ¬´¯ßd_è¾)ºµîáški´ÂulÇŸä¹.3ŒÄåøÌE,^WO Š¡„«†U)bó,&FuëÆ#N´½œé×”[’np÷ú2ü&G›çL>ŽÂáêá³*˜œ5lM:îp¹~+AÐÅB•)N(óÆ¥É(´¤ÔýßnÑ?h/h>-ý°ï>$|Qð…¯€ÿg¿x&Æù$ÐuËðÇGÖ~ü4øw¥x£_ÔXxÏSÖïüU7‰4‹Ï ãƒOñ“áFŽ]{O¹€út³jT±E,f;°¹F' §J¥§S+Ábå õgîbgVuÝjr£t£Z :°hóªeujÐÈ£„Á×–'4¡‰”Z« «:yŽ/ Ñ¥͇…8ÑTgÖnT§^þÎi–þ ~Ø¿¾<ø®ûÀ¾ Ô|o£xÎÓBoÚx[âwÂω?µßøI.à°—ÅžÓ~%ø_Â÷>-ðÌ7—v–÷ׇ“Q°·–òÔO4i‡}eœE•æÕç…ÃOO_X v—Õ­‡æPxŒ<1´(KAJQN­%8§(ó5̉Ì2Ç-¡N"8j˜yUö­ƒÆàñÔéWårT+Ë ^²¡YÆ2j^Y5Y;3ʾÿÁLd?Šº§Ãûø×Æ7:_Å RÃþ ñΧð‹â®…ðÇWñv§æ%‚ágë²ð·gºŠMüjðÇ…n¼áφŸ üU,Þ=ñÇŠ|3„<7ã˯ø§Ä‘éþ¿Õ4=OUð¢xK]ÓtK‹~ÃTÕsȸª¾e›åòÁãèË™ÖÂaªË*ÌèЖŽˆoНAaèâ¥Z½nJ)ÔÃý^¬)J5¡R¥ç\3<«/ÊññÅ`êÇ—ÑÄâ)¬Ë/­]b+c1tRÂáèÖuëa•*4\«F# þÞœê)R”!ôïí7ÿ&ÛûBÙø±ÿ¨¿^—É3Ä_ö"Íÿõ_ˆ<îÿ’‡"ÿ±ÎWÿ©ÔéB¿ƒísâŸø(üš/Ä¿û|ÿÕÝðæ½Îÿ’—‡¿ìy”ÿê~ñ¸þIìûþÄÙ§þ¡W?7õíoMð·­x“Y¸š?‡ôG[ÕnØÚn•g5ýõÁUË0†Ö d rBàs_ÞjÂ*•ªK–*s«R_Ë qsœ¾QMŸÅt©ÎµJt©®j•g p]ç9(Å|äÒ?7~ø›þ ûEøÿ´/‚>"üø/à¯é¶þ1øeð?Åß Õ¨¬Þižcpx¼ö„«áp”òîÂç1¯:qTðØÚ¸œâ•z³…;VÄaéÇFJŒ!“Q•½éÙN]“`ñxlš´hâqSÇñN')•×§‡©ˆÂSÃåuhÓ„çzT+Ô–2ª•YIÂ-ÆúFïè¿‹ŸµÇÁ¿2xKDñÞ«âÝÆ^,Ј´ßü-øcñâÇ.4 QoÿŠ®|ðÛÃ~)ñሮ§XN³ªØÚiâa-º\É5­ÊÅìfA—eoOS[^—¶†‚ÆcñNŒl§ˆ–F½jt½­HƼT›Œ­å`r<~d«ÕÃB…,= ¾Êuñ¸Ì. ªÊî4cˆÅÕ£J¥gg Jv³µš¾7‹ÿnÙ‡Á? >ühÖ¾#y¿¾3x´x áæµ¢øoÅ ºÖül|?âï >ƒ¢è÷ÚþŸâCüG¥aêe¶¦ ý–~9|søA¥ø×Äþ7ø9u¢è·ÃüøÉ xŸÂÞ#ñ, ©xzãâW€o<5 øçÁþÖ4‹[ë»k6šG‡.-áM`ÍqRøù¯`pÙišeðÄ×ÄåÒ§J¦–fT«Ð­Y9ÑxÜ$¨ÑÅaðõ)ÆRŽ"¤iÑnÉT¼’~®YÁøÌFs—eØú˜z8l|jU†/˜`*ѯJ‹äª°˜˜Ö«†¯^IF2¡ Nª÷Ÿ'ºÚûÛá/Å]ã7ƒ-6ƒSWIb0µ*Ñ”—ÚŠ›qzI&}1û)Éêø7þÍsöŠÿÕ±û%×àž=ÿÍ)ÿußý㟶x%ÿ57ýÑ¿÷¬~É×ó¹ûÁøÙûVÿÉêøËþÍsöuÿÕ±ûZWôG€ŸóUÿÝ ÿ{'àþ6ÿÍ3ÿu¯ý䟟¿µ/Æÿü8¸øCð·à¾‹á½kãwíãMO›Æ~<àíÃ^Ô'²†)ô«[;5ñït_xvî3z—Ó_M¢ê–²[ÙIÚRââ(ÖajU–c˜å¹ŽЫ5*9}\¿F¼Rtâ£õ¼]*ôd¹ùÜ*‘j-s&Òxº¹*ã€ÀcðoJ5qÔñÔ+Q“jrrú¶¥±÷yT}¤%y^ÖMùì‘ûNøßã5‡ìmyã¯xF{ãìÿ´GŒü §ø;X³¿Õ/–|°Øzîž]ÄÏ*Ãâgˆ„£ ™Œ£‡©FÊ­JõV5x¯gJ¤%iTé^ý¿¿føÿGø¢øÃÄÏ'‰|Gqàÿ xòûá—Ä+à׌üYo%ÌÃ~øÓ©øVÓáŠ5™îlî­,lô_^I©^BÖºÚghã~êY’b1tð”±5›¯Yáðø¹`±´òÜN!9/c†ÌçB8õ\£(Æ4ñs’å‡3jüu¸c8¡†ž*¥ ?º¤«×ÂÇ„©˜aè´ŸµÄeð­,eiJ2”ªQ*w••ÚÅøÿý•¾xËâG€üGâoÝx‡àö½ƒñ]|+ðsâÏ´¿†âçÃñ„ÿõß x;YѼ3á t/i× âÍ^ú×Bi¡Õmé¹Ñõ8í²ÅñŽE‚Äã0µ«â¥[.ª©cý†]˜ba‚æ¡G«bªáðÕ)ÐúUàþ±RQ¤Ú©kÒ¨£¦„ó¬] &&•,4icé:¸/oÁa狵j´,=*õéÔ­]T£$èSŒªYÓ—-ªA¾'\ÿ‚„øgEý´4?ÙªüGñ?ƒ!êß®¹ŸgXÌÆžO‹Àåy~YŒ©—¼V+<ÇÆÐŒ^+’ŠÄá)ÐÃQœý‚›J³«N¤”ToéýS$Ê0˜ ™®™c³,1ˇÆCC „­),;W‡ÄÔ­ˆ«ûnES:”×4¤åËÖ럿h?‡?¿a†_/~_ø‡ãgÅÏŒ¾ø«©ø'GÖ`е¯ø'àƯ‰¾Õ|5k®ÞÜj>ÔõGÁ¾×¬%»Ö`´’M[H³¾»´h/E\Ã6Áâø_Œ– u³<Ã1Âã熧UR©G ”æxÜ=J1«)N„ç,.ubåQEº”ã)FÒ0†*ÅáxƒŽ.4²ü‚†&¥7V\Fi—àñ­*qP­ÇZ4ä£MÊЩ(ÆW‰À¿µ×€¼3û5x_ã_ƉÚW‰¢ñ~ øDÕ¼ðÿÆpj^7ñ‹ñ;ǾÑüà†6ún§ã¯ø§NÓ¼->“ue¤i7ÓëWÕüEcö<É2Öˆ0”rZžcާ]VÅâð´ªap˜••sðKâþ—ñ?Á†)µÍoனà«9¾4,Ish,­>/‰¯õW½´]2ÚìÜGœðüY“bªW¥BxÙU¥…¯§NYfc ã°¸tJ¹dg†‹Ì×½Xོª9G‘JèÒ¿ fØjtjÕ†4êâ¨àêMf8LjÄ6©ÓÌe D–^î¥Ìñ~ÉC–NmXÄý€?lØ¿lÿ‚º'޵x×ÁÞ2‡G°¿ñlZ·ÂŠ>ð Õέ«øŽÊÅ~ø§âk¥xòÖÞÛ@-­Ká}wÄ ¢ÝÜ[êKf×¶qI— ñ"âL¶–*xlV©Æuý¦„ÂJS©Zú|]8ÓÅÅ*_¼t*ÖöRiTqrŠzqG¾Ì*a£ˆÃâ0AÃÄâ’„)JOG 7<3n¯îÕjt½¢MÁK–LûVÛþK/ì·ÿg!àOý4ø¦¾gÆù!±ÿö—êe3è|*ÿ’Ëÿ`¸ÿýE¨~üWññýT~hÿÁJãÏö9ÿ³¶¾ÿÖ?ý®+ôÿÿä¹ÀØ&eÿ¨uOμTÿ’7ÿa8ýK¤~k~Ó?,ÿgÞ<øÅw¡]x¦ Ûh¶:…l®c³ºñWŒ¼câ]ÀþðÄ7’¤±Ù7ˆ¼gâM F7«h·¦àÅ cVçYœr|³˜Ê”«º œiP‹Q•|N&½<.‚“º¶ÄÖ¥O™§ËÍÍgkÌÙ>],Û2Ã`XÑUIU­$ä¨áèQ©‰ÄÖqZËÙaéU©Ëöœmu{ž!ào ÿÁC,µÿ x³âÆOÙ»_ѵkD>>ø=¡ü ñvƒká¿ Þß[/ˆ—Á_døƒ«êºö¿¡i²]K¤7ˆ< a¦k·¶ñÛÝ*¯´Ûy˜\?F®‹Ì²j´çR—ÖòêYv"”hÑ”—¶ú¶`ñ•*U­J º~Û U”TeìÔ¹—¥‰¯ÂÒ§^†›S© u>«˜UÇЩ*Õ£{/¬`V¥J¬ùTý–&s§uÎÓRó½ ö¦ñö¥¬xúË^ñŸ„<#†ÿà¥Z_ì³áÃwà½gYoøïÀÞñ ˆèò\fø¿\Ô|Cª _j‚ßFÓa‚4¾D[?,ó:˜¨ÕÄáðêÃ#£Í†«SÛá Ua—³æäÄUYòâgËN %;huÕÉp°§†•,=zî·Ï:­Ëˆ§OØâ£‰ÄÒx‡í-ÏBœ)C› Ô›mÅïogø§ûwþÍ¿¼i®xÆ%ñuƧádÿõü-ø™ãï|,P´‡Q¶“â§Ž|áMs“û.âßVœø·WÒ®“<:•ІÊXæoGÅ96]‰«…ÄVÄJx~WŒ©‡ÀãqxlœTâñø¬5 ¸|ä”j?¬T§ËNJr´]Ï?ÃY¾?OB• üË N¾3 †ÄcdâÖˆ­N¾)ó§ì!>i§y&•ÿ‹Ÿ¶ïìóð[Å^ð7ŠüEâkÆ^;ð#üJð?…þ|8øñ[_ñ—ƒâÔ­´ÉuO i_¼9â[íkÉ{¨ï¦‚Ʀ‹HYµgU°†IÅæM”e¸Š\Ejõ18¬+Æáh`°x¼}\NN0s¡ ò«g%&¢›TïQû‰ÈŒæ¹…øš4¨Ó¡†Ä¬&¶/…ÁS¡ˆq”Ô+OVŒi·Ê✚Nv‚÷Ýœ¾7ÁMþøCý”kúkø¢óB‡Æz½§†n¬¼7ªjZW™ñ¶ K!Äàiâ±ølß3ž´¨e™–&¥Tpø¹W¦©aè:”óbhÒ‚ÁÕ‡·•%‰©2 ζ]ÁØÌML†Áâ2¼¾ªQ­˜`0ôêÕ­[ ¨TukÖTê`gBµI<])ûÕxx:ªU¡þ’x_Ä~-ðׇ|U§Úë66&дŸXØøCÕ¼1â;=fÂßQ¶µ×¼5¯Úiú›xnRSCÖ¬,um&ù'°Ôm-¯-æ…>Ê…hâ(ѯÔŒ+Ò§Z1­J¥ ÑH)Æ5hÖŒ*Ѩ”­R•XF¥9^Œdš>Fµ)P­VŒ¥NR£R¥)J•HV¥)S“ƒ•:Ô¥*ui¶›…Jr”'¥ 8´ßØ_ðNù ~Øö\|ÿ¬õð†¿’¼eÿ’Ö·ý‹púMCúƒÂoù$)ØÃÿ¥@ý;¯ÊOÓÁ?ÿÉËþÙ¿öpš?þ³ìý_×~ ÿÉCþÆ9‡þœü·â×ü•õì_‚ÿÒjükø»ñÏÅ?,¿e¿Ù¦÷ÀžñV•ðÚÃâçÅ‹Ÿ|?©xÓKð„üGâ cÂþÑ|5à]3Zð×ü$Þ+ñ†­áŸOæj¾!Òô#GðíìÎ/®î­â‹ë³,Ç3¯šÇ"Ée…Ãâ)ࡘcó e)âiá0õ«T¡…§G ´=¾#R…wz•¡N:2ožRŠ_-—à2Ú9d³œÞ8šôgŒ–ÂÕ†xªô©S­‰©W:u½ëQ^å)Îu*Å{©6p_~"þן³ìµñÏâÄ/| ø•âÏ7ÃÙþøçÃ?5ŸFøëEðçŠ,¼iðþûÅ^)Ó-§°´Ô¡:-þ‰â™b½ûd¦îÂÎ]9ÿ“2ÆqI‘f˜¼f+.Æâ0ßTx)Ñü?ñ7áOÄÏ„š‡‹¼-eqkk}âoÅñ+žÿ„ÏA±žúÅ/u? JÚÔ^Ú<ïw³oey¦!á0ÓÅSĺR¯N–7Ë爡*øU¡Cë4¢ç)ÑçŒy¢ÛI¦ÞaÃùž[‡úÖ"j˜j¨Ô«ƒÆá1Ñ¡ZJRCÂV­õz’Q“Œ*ò9rÊ׳<ÇÁðSÙÇÚ¿„tß ø×Æ3é¾7ñ4~ Ðüs}ð‹â®•ð¼xæ}NãF¶ðF©ñCQð}·€´]ê–æÂÛ@ÔüAm¨Íu5¬+™s ¿x{S 8œC†*ºÂÒÅK/ÇÓÀýjStã†©Žž8Jx‰MrªS¬¦äâ­yFý˜ŽÏp´ë槇¢ñ0ÑÇ`§ŒúºŒg,D0q®ñ5(F2æuaIÁ%'{&̯ÙÓöòÓ>8~Ñÿ´WÀ+¿‡ßtY~|Xÿ„Àþ!¸øñ«AðåÖ‡¦ü ð'Ž5ËŸˆ~8ñ†cðg…µû¯ë^$‡Â6:–£ ¿Šü!ÿv» Øê¶^%Òu]^2~*†iœçL°˜êoú®´²¬Î•R†]…ÅT–3Z‚ÃP­*õk¬<':O‡ú½ZQ©ÔêT¼Û†g—e9ViV¢Æ`¾³‰¤³<¾­eVxìNšÂáéVxŠÔ•tyB5}…oN¬¡*5!¤?jïù5ÏÚOþÈÆ/ýW~#¯g=ÿ‘&sÿb¬ÇÿQ+FKÿ#œ§þÆxýJ¤²—üšçìÙÿd àïþ«¿Ñ‘È“&ÿ±V_ÿ¨”C:ÿ‘Îmÿc<þ¥U>FðÅ·í!ûø¿ã…üû6øŸö˜ø7ñCãÄ/Ž>Ö¾øëá†|eàmwâö½qã/x7ÆÞø­âÿé÷ZT>:ÔuísBñ'‡5­VáôÍitûÝ.,³üýç<;ˆÌha²jùÞ]ŽÌq™¦®¡‰ÃUÌjË‹Ãb¨ãñHJ :µiW£R£têòΚ”.ýÚòÊsê øŒÞ–QÁà0¹v&ž3 Œ­COMaðØŒ=\ LÔÞ4éÕ£Vœ=>xÔå•—uâüuøñ§öø·âß…Ö~ Ÿá׉?hcâ¾…¥øÏFñE§ÃÍ;Æ_ üWá/ÛÝk º4¾"Ôu7¹Ñ-µoøG4«ûM;Yº½f›IµUŸª¶4Çf\)˜b01Ã<lÞ¦>”14ëÇ N‡ÂÆUvëNnT£SØÓœaRRIºqU-,F[ƒËø›C,BÅÑÊ©àªÏRŒ±S¡Ž¡_ãO÷žÊå©({YÆR§»)ÉÁ|«þÄŸ×öiý…o®<ñJóƳ6¹ñ×þƒÿî¾|TÕt‹šæ¸¶º¯‚>)ø;ÆžÑÿá ÐaƒG¼:£ã-/GÖô w\Ó/5+KÀ±”Ÿ æ¿Ø¼-)añòÄä•3O®eÙvnò¬}JY…Z¼µ0Øü>*/mE*röSÄÓ§V•Z°”ã-§§ÄYgö¿Åb0J†qO.ú®;–,Ë ˜tù¡ˆÁ×Ãթ쪷8ûHaêT§V9Æ7k´Ôd_xƒö<ý¯ì¼ðö†ð§Æ/2|1ћß´¯í1iñÿâOŽ4†þ!Ñït‹ÛÏkŸ> xsÂöšuŽ«â‹(teñÕк³±‚YJ4–vQôO‡±¸wˆc†Ê³Š–jð4Ýï:ŽmÅSÁV§:r•z¸ì]„*WЧõ©sF*úòÅsÇ=¡K>Èž#3Ê«à2Ï®TUrŒ¢Y^<])ƤcFž V´§(Q“ŸÕ£iIÚþôŸß_þxÏ\ý±?cðïÚ< ðËÀŸ´þ…ã}v ½*Ö-ãÇZ?Â{X=„×jw±j—Ö’¦XÞÛØ5“=óY¬öÍ7Öã°8мCÃøÚTo…ÁasºXšªTâ©ÁÕ­lN3“ÕÃÓjruV¦9âgÌ¢áV›|ò‹Ÿ7»Ígnþ Ááÿ‹¿ ¿f¿‡ÿ³¿Æƒ>)øi®þÏ~ ðŸÃˆ|]©ø‹á÷ˆ'†±Ô1™Õ¹wf˜lÓ2Çã¨Ôʸª®Y—JŽcRUg…Ç`'™acNTœ¥Fu(á±1­ESr72~¶ˆ0Uð™Ejy‡希·‚ÁU†gÃTó|jà!pÄà±°Àb%R5cV0­ˆÃÊg5r´×èÿ~k^ý³ô_h¾šßá„?a|ð¿ˆïukRîÓ^ð¿Å=oR_ H÷wóxšææ/ ÿdÞ]ë76†ÏPm¢]Bkå–$û,.US Ä”ñ4¨5—áø_ •P­*‘œ£V†>¬ýƒr›¯&¨{9J¤£Ë>³sº>O™ÓÄðý\=JêXúüI‰Ì«RŒ%Ê•l ëZ1Tb~xÆš—4:EGSä'øûD|9ð÷Ãoèßu¿k?¿à¨¶'í"ß ü5⟇šgмUðoãÿíCák~¾ñ?‹4?Åu.“ñÃ^(] ^ñ&…}™Õ•ÜVZ¤Mf¿<òœßG‹§–ÕÅÕ˸㈳—£_ øŒ·2ž{CW:øŠXe' Â…eVµ)*jQ’EÊ{‹3ʱuqxj™<-<d9O×+QÅN~=\E:ñ£F¦"Êx Ô}¥:5"æã(¹Aó²:£y«èZ.­¨èš†µ SIÓµïjòé³êÚåíœ77:&§>ªhój:Tò½ìºV§¨é²\Á+ØßÞZ˜®$ý”åR•:“§:38NTj8:”¥(©JGNu)¹Á·:u'$ùg(ÚOàªÂ4êÔ§ ­Tœ#Zšš…XÆM*U# ŠKš*p„Òkš1•Òû#þ ­ÿ¶?ýµþ±ÿì‘_È^0Ésÿ°L»ÿPéŸÔþÿÉÿ°œÃÿR꟥Õùú)øsÿ%—ö¤ÿ³ñßþš|-_Ø>ÿÉ €ÿ°¼ËÿS*ŸÊ¾*ÿÉeÿ°\þ¢Àø[â·ƒþ8|ý¥µÿÚsà¯Âɾ=xcâÇÃ?ü6øÇð»BñW…¼!ñOÔ¾jþ,Ô¾xÛÀמ9Ô´k–ßbñçŠ4_øYñ‡î¶™©iú…À6‹ôXü>i–gUs¼·óZ† ‚Ìp4«ÐÃã!< JóÁâp³ÅT£†«\UzuèÕ­E¯rpœµŠðpXŒ»1Ê)dùŽ5e•°8ÌF/«Bµ|,¡Œ…bðø•‡LE9saèÔ¥Vª§ïÂq^ìŽ?ö‚³ý¥j_ÙOâo‡n¿fýWáWˆ¯þ"~Ï—¾ðˆ¾"|?×üw¬hžøïðÛÆ¾5×…§kÞðG…?ásüST°Ötûi5 üMªYxVçþ$Z|Réh¸‡ìšpöS5šCŠÊ¸— [ …¯ 8Œ÷‰¥œÓSÄ:Q«G ‡þÒÇ.ZNnµHЗî >f¹sϳH¼¶¦ ™ðþ*–+Fupù7G*¨áAT*¸šÿPÁûôäÔUJ²ýäÚ©dù°¼5û/ügÒ¿à™ß²/Àføzlþ,ü3øûx›Æ~‹Vð²Í Ú|8ý¨~üBø“©¶­°tÉ´Ÿ é^!Ö¯³5[˽XGsk§¦£¨ÝÇk>t2<ÊŸpöR𜹆 Õ±8uR…éGž`±˜ÙûESØÉÓÃÓ­V|•%*žôaÏ9(½*ç9}N0Ï3'ŠRÀâð™ý=w ÍUx¼Ÿ…ÁÑÃÚEN¼éS<"¡u)òÆ.KèÙãÃÿ¾þÒŸµw„üCðgÅ7¿ þ<üy¾øÿá?ŽºOˆ¾Íàm2ÆëàoÁ‡oà¿x~çÅÖŸm|L5¿†:‹Ûͧø/SÐ¥¶Ôlä›V¶Û(¾QG0Ëó¬ú…l¶¼°Y¶k<ÛšS­ƒxZqy^[ƒxjÔeˆŽ65½®n.iÒqœoQYž^kWŽÊ2Jô³ Åå¹drÊùléb–&rY–a‹úÅ*ªƒÂ:>Ïeñ'„u…‘´ŸèLJ5E…‚JÚv¹§Üé—˲¸Y µÔ¡«l¤ îúôaˆ¡[Rþν*”jYÙòU„¡+>–NÌþ-£Vt+R¯NÜôjÓ« ê¹éÍN7òºW?6>øÇöÍý›¾ø;öq¾ýuߎ¿ ´ 7á瀾5øâ·Âo ü/ñg„|7màÝkÆúŒüQ§|EðF©‡í´Ûoiú'‚|sÔ-/n4›‹«yíâ—b8“&Áa²iðý\ÒxPÁás<6?CˆÃЊ§†«Š†&¼1˜ZŠŒað¥†Å.xÉÓ”“Húì~‡ó|f#6Ž{K.Ž6¬ñxœ¿‚ÆÖÆQÄVn¦"žxz2Âb 깺3©ˆÃ·ETJWoý®?g?uŸÛ§Uð߀UºøÏÿ²ðŸÀO&›âÇc®ünµñ_íG«jþÓ§Öµ=&êÚ;H¾!øNH5ýzÓGÐî Õá?Ú =®¥—7dù®>§Ô£„U%™p- « ¡ZŠ\Î5óÊ•0°ugNIEbè5V¬iÒ’¨½û©¨ï‘f¹f 9 Ø®Håüg_2Ä9ÒªåO/• žg…¬:n¥DàýÛ8¹rŸ´ïì¯ñU?i8>>h¾ý§>*øCÆ_þü/Ö¼1û+þÕúÇìÓñ Àþ%øqªø¯Q†óU¶‹â§Â_ ü@𧈠ñŒªÆûÅ3ê^Ô´i§Ó4ùbÖ.ŒüùæEYÌsZXló‡Äåx< ZS%Æa«àªb&¥R?^ËèbèVŽ%¯z» ”Û„-VN[äùÖû!å•1> ‡Ì±xÊu³¬’žo…ÄQÅÂŒ\i·ƒÆÖÃW¤è_Ý¢£Zœ“„mßh_²Ž¹ øOö³ð?Âx[Oø}ûhx»ö…øÁ៉_í~/øÓÁ°ø«à×í/¤ßø·Å^?ñˆõ[¯kº§!y¡–òÛM¿ÕÓ²ŽCV–„ã†ËñT#„â\Fo˜ÐÇfÌq8u_-Ï)ψÅÖ­Qâ*ÏŠÃÊ^Êu¥•¹“’„ê.j¹Ý:µøžXœv´ñ\?C+ÀVÂ`¥ÃâÃ(©0Ô©F4)à ‡¯ûHRŒ©Ò³Q”ãL©ûMþË?þ)ë¿ðSð—… k_ß²çì³à¯……Þ³¢YÙøÃÇ_ 5ŸÚ'Uñf„C_›½&æÒÛÅ ²ž»i§é· ¬Z [Ûˆtý@ØÆw‘fXêÜkì(.\Û"È°Ø Ê¥(ÇŠÀÕÎjb)||Ôå_ z±„$êÆÒjåy>s—à©ðƒ¯]§–g9Ö#¤¥C Œ§•Â…]#i©:5åÉMÊkÙËš)Ê<ߤ_|qâ߈ž ±ñ7þxËàˆ®.¯­nüã½[ÀšÞ¿b¶s˜b¾mGáÏ‹ZœÐ¼¢”¯ü;ø»ûAüYÔ5ÍŲ_Œ~xF/ kÉu®|Kø“ð§Z×õÏo¾“¤øWÿ ¼SãËK&o2ö[ýwÄZ÷‡f·YEp×S½`ó ß0ZxŽÄåXu‡ª¥WÀT­R»IS§BŽ‹Œ©»ÍέjÔZ´R¦ù›Œâ°9V4êQÏ(fUþ±IªxL6(QR½J•ªã(ᤪ+EB•*UT¯&ê+%/‹ÿfoÙGã_†âýŒ´oøZ÷Á¶¾ÿ‚Nxëöaø¬Ç¬ørþü_ñ.½û2µ¯‡4nîmJò/ø¾þ-[Eû~€­¡4m¬$ךzÝüÞIæTW ÓÅPž8NÅä˜ÚŠ­K ˜V­’rÑ^άœä£…ÄMT§ÏJô¿‰yC›è3Œï.«ý¿SZ8‰bxãœáiºub± T³kÕ|ôÒ„\±4"éÔåªý§ðÚŒù| öxýˆüsá“ð7àŸÆ€?¶gˆ­~x›áô×ÿOíýâM{öO¸Ÿá^·§kžñî…ðwUøÑ>¸¶ÑjZ“¯iÿgø?a¦húŽÍ2Òî+XîWÉÊ8gEåyne•q%håõð’ž3ým­[ “ÀT…\>*–]W2u¬§JXàÞ_SŸ¹(EIz™¯a«hæ9~gÃô厣ŠäÂÿ«©g‰ciÊl-\|0¥ÌáVt¥‹XùN¤=ùEÎM øñH·ÿ‚›[ÁëÇíñ‡Å ø`Ïy Ï'¼/wû$üøs¦\‹P”éöÇÇñf‹—ˆ›Lž ­®oÚÚ=:úÞöëëp™^.šã_i‡IæÙzØåI¼MÃù^êo’?Z£ˆ¦£[‘§O•BjRù|Vc…©.äÄ]e˜ ±¾íEõzÑÏ3TÕœW3úµj©s¦¤£w8¸Çľü0ý¡>üDýŠ8|FTËñTªá*T­]b1TcZ„-QNXYשzmœïoCŒÊóxcǾiBX,V †+ˆÆrKCKˆÂÓ©FUÔëÓ­J¬¦£UÓ•&à™ëTžUžárç‰Íieü¿G.¬±˜|]l6*†š8jôj`¨âjB¬h¸Q«J­(źJ¤j{Ü«®ñ_„¾<üYø§ÿûø«â¿„ÖÞ¹øgñ£ã‡‹¾)xrÃÆÚŠGÃÏ ø›öløíðïÁ3jšº&=wWÔõxKOÕìü+e¬[éz¾¯p±]^hº|ÚÑéÄaó\ÃÂXúø…– 2Ìñê1ÄÒ¯õ:²\׆s¨½š«Ru+ááR4#QS©RIJTàê˜Q¯–`p\O‚¡Žx•ŒËòê:²ÃÔ£õªô³|·ˆP‡¾éÓ…:5å W•78A]F¤Õ3áÏþÄßn¿f/Ù.Gð‡Äyüoû;üzý¥¼mã„ß ¾;Ý|ø•â/ü^ø‘ñ~m7Tð'Åx»E°°ñe†‡âOk–>¡âÝLÕôm_[ÐuOKº’kcòõxg5–G7‡Æ6|øa¢ü=Ò´_Úoö¢´øýñǶ¾·×fð½°ñ·ñ[â/†¼§i·~-ñ µ­›xïÉ»Kƒqt-V8#­*pö&¿ñLp¹VuC3Íð4°¥žgͱ¸¨á•WA{j˜üe 4!,EhÆ/i^òå²FPÏhRϸmâ3<¢¶]•ãjb§S'Éå•á0Ò®éªÒöPÁákWœãB”¥/«]5h¹]³ôã¿ëÿµ_ìQñÂþø#á7ü4%¿Žu{k&Ê ÙxÇáu–á;sa=å¶¡yo¨ë6ëg E•êY´I5âZÛªM_]˜à15³îÅУ|.û]bª)SŠ£N4°ë‘ÉNJu#Ê•8ÉE¤åʬϗÀchSÉx‹ ^µ±Õ•¼4$§'Zt1®­ys(¸ÆQ¦Üœ§(¹&ÒrnÇÿîðÿÅï…Ÿt¯ÙçâßÁŸü=¿øo?‡t¿ßø‹áî½àŸ‹všŠ¼_}·àTð·‹µ¯éÖÖzpÓ.u+_xgÂW‘ɬÚAa¢ÖúƒÚrð…,ÕÓÊ3 ¶¾yZta‹•l%\.a×ÄIUÂû E\D#rJqÅPÃÉ:‘Pç´ùz8ª¦˜Ï5Àæ1QÌZ«<,ib©âp2tñ.µ t&å>u a«W‹ömÉÆñæû–ÛþK/ì·ÿg!àOý4ø¦¾sÆù!±ÿö—êe3ßð«þK,ý‚ãÿõ¡ûñ_ÇÇõQù£ÿ)ÿ?ØçþÎÚûÿXÿö¸¯Óüÿ’çÿ`™—þ¡Õ?:ñSþHÜý„à?õ.‘ùûZüÔ¿h¯Ù÷Çß ô-r×Ã^(ÕŸÂ^&ðWˆ5 y.ôÝ#Çß |sᯉ¾¿Õm¡ýüúL2ð~†ú¬P¤>Ÿö•ƒ÷¥+ú§?Ë'œe8¼*±£^£Ã×ÃVšr…<^ G„H­eMbpôDµpæKSù«#Ìa•f˜\mZr­FÞŽ&”]§S ŒÃVÁâ£ôSx|ENFôæµô<›À¿´íwâ-Â~ ñ_ì9â/ê­h¶~$êþê?´Ý o­£ñ>¹à™4jÿ¼KyýöËŸ èZßÿ ýªí­­µ][O‰f¹>~6âÕ¨a«ðÅl,ý­(âñ³Ì²ùåð¥ÏoW éV©Ž¯.NgB•Li8ª• ®ÎìNY‘R§_Cˆébb©Ô– ¿ t긿cKíiC J<öUªSÅVå4¡ »#ækÏÙ£ã|º×ˆn“À· ïüÿÁ¿´½¬ŸÛ~oÁ 3À>Ñï¼tëAÖÞßRÒ5c¢:¯‰œÛ"Ñ^!’OY.fêU—Õ]¥â:‹ö´uË!…ÃS–+ø—²9¯fÿ|íuM¦›ö#›åÞΔ~²¯Äe^ζ™ŒñXš‘ÃVáRöŠôu³©tÒðˆ?±ÇÅO |`ý¥ãÔ>þÙ?<ñïâ¿‹¾&xkXýn­wàGÃØô¯ˆZ}„:Ç‚¾+|2¿øÏðçO±“Fº†óG> ð߆¼j5ï eÇycö‹9,ÛÊÆpæ?˜çJYfxl׈ÇP©“qM\«¡Œ„L6?<Ë:rŒ©ûj1>Ú‡"œoSÒÂçø*ø £—eØŒ³CVžmÃtóýž5‡ßµ·ÁÿøwÁ—oÁ¯…ÿ°RüÐukývË]»Ð|A¥|Fð-Ɖá9nï¯äñ6«|žÐ'{¯Ig%­ÿÙd7š½¹Xdú¼&QWÄuj8g ·ŸÙ4ªN¬jÊ•jxÌ#¥AÊSuêKêô¤åYÅÆ|¯š|Ò³ùŒVkO‘ãéUÄ)æÎ&þÔ«Ó•(Õ¥S ŠU+¨Æ>ÆxŠªÔ¹¹¡Ì¹cÊ›>[ÖÿgÏÚÁÞ±ñžƒðwVñ¾³ðÏþ ¿ñwö­°øm ø£ÀZg‰s®x^ÿQ°·¼¼ðî³sáíO[Ð.5]âi4ÍF}YÕ´yo-¦“LÔïìš©C¡RuhÑ«RL=J”©Ôž«§*´'8)JIQZR©JMÂn•J”Ü¢Ü'8ÚOá+B4«U§ °¯ ujB驪u£ ¸Æ­5Vê¨TIN*¤!5.xFWKìø'ü†¿lû.>ÿÖzøC_É^2ÿÉk[þŸý&¡ý?á7ü’¿ìaŽÿÒ ~×å'é‡àŸ‰ÿäåÿlßû8MÿYÇö~¯ë¿ÿ䊡ÿcÃÿN@þ[ñkþJú¿ö/Áé5„>2ø/ãoÂ_ÚQ¿jŸ‚ Û㦕ㄾø7ñáF•â ø?ÇbÓÀ>(ñOŠþxÓÀ:—Œïôoj·mÇk/ÄeØì£û1Æ,¶xlu\~:5ká¹±ThÐÅañQ¡•रØz”ªÒ¥RÒ„£(ZJKƒý¡ ý¥ÿj¯Ùãï„gý™õ¯… ×Ã{_†¾ñGÄ_‡:ï¼Ký™ñ Ãú÷ŠïüB<+¯êžðµµ†Ÿ¦E&“muË­EbÔ$œÙËö+KŽLÝgYïæØw’ÕÀV«õ8à°µñ˜:¸ºÜ˜ÊUkη°«S AFNœV*¬§ïß•òÅõeO(ÉsܲºÍéãiSúܱ˜š8\U<-|-Zt#KÛS†&³”æÔÛÃSQ¼-̹šóÿ¿±ßÆ–?ðS iV¿ðŒ¯í®~ËÞ!ø?¯Üx´;_\|ðWÃmGZÒeÕ¼1/Š<#i©xÁW> ¼ÕÞßOÔ­!¿—VÒÖâ¢øónÌs8ñ½ qt?µëdu²êο²X‡–ápS«MÔ¡7_ÖÃK *Ž0œTÝH)$›êË3ì].­9{oìºYÍ,}5GÚJ‚Ì18ÈÓ¨©ÖŠ£^P£ˆXˆÓ¼á' Ù¶…ý“gÝLüxðŸÄŸˆ?³çí«à-{á…ü^|;ãOÚgöäÔhß Xëþ*²²ðÖ³¢ø#Âñ£âkÝ[kz-Þ£q'‰uÂ7 "À>˜n§€Z<ƒ(Ÿö­ n/)âl-\ G±Äç|Q<æ„jâ#5)ápÿÚX×%V”¦Ýz”°í{8{œÍr™æiìÊøL.iÃØšXÊô=®'áÈeUåNŒ¥VLEu€Â$éÔŒW²§Rº|ò÷ùoÍCOý—þ3ÚÁ.¾üO‡¦Œ:Äÿ‚Þ'Õ|­ád¸³µð×í§àߊ^&Õ³°…L¶½LqŽ¡˜PÍ)ÖÂ}^‡±ÉðáñgˆŽ1U•l ör¥‡«Mª±rœ-&¼¬Â®䲆aFܧ[[.©KõŠÎ¶kÆF½ Ñ¡,+§XÄæªW¥4éÉF3n7úö®ÿ“\ý¤ÿì|bÿÕwâ:õóßùg?ö*Ìõ±åd¿ò9Êìg€ÿÔªAû)É®~ÍŸö@¾ÿê»ðíü‰2oûeÿú‰D3¯ùæßö3ÇÿêUSßkÕ<À € (  ~ÿ“—ýŒ¿ìá5ýgÚ¿*ñ—þHªÿö1ËÿôäÏÓ<&ÿ’¾—ý‹ñßúLÞÊþD?©ÌOø(÷ü†¿cÿû.>7ÿÖzø½_«x5ÿ%­ûæúM3ó?¿ä«ÿc þ•Pùj¿®å° € ( €>²ÿ‚kÇŸíÿgmcÿ¬û$Wñ÷Œò\ãÿì.ÿÔ:gõ_…òFàì'0ÿÔº§éu~`~Š~ÜÿÉeý©?ìä)¯Ì<`ÿ’ÿaywþ¦S?Fð«þK,ý‚ãÿõ¡ûñ_ÇÇõQù£ÿ)ÿ?ØçþÎÚûÿXÿö¸¯Óüÿ’çÿ`™—þ¡Õ?:ñSþHÜý„à?õ.‘òm`ŸÊ@P@}Kÿáÿ×íÿeÇÁúÏ_kùÆ_ù-kØ·ÿ¤Ô?©<&ÿ’B—ýŒ1ßúTÓºü¤ý0üñ?üœ¿í›ÿg £ÿë8þÏÕýwàßü‘T?ìc˜éÈË~-É_WþÅø/ý&¡r¿U?3 ( € ð/Ú»þMsö“ÿ²ñ‹ÿU߈ëÊÏäIœÿØ«1ÿÔJǧ’ÿÈç)ÿ±žÿR©Ã?€äDðWýŠ^ÿÓ=2a?ÝpßöGÿMÄþŒÅ¼â?ëýoý9#­®ƒ € ( Óø#'ü¥köÿ²…ñGÿYŸã|÷ÿÈ®_õþç#Üáïù¯úóWô?Òb¿8>ðþbàé/ù5_Ù[þÎÉõBüj¯¡áùGþ¼Vü‘âqü‹§ÿ_iéGñ[_£ŸP@Pöóÿ»ÿÉþÑŸö{¾2ÿÖlý–«ó^%ÿ‘µoú÷CÿMDûü‡þE”Ç[ÿNÈþ“ëÁ=“üÐà­?ò“ÛwþËD_ú¯| _¤ð×üŠ©×ÚÿúqŸŸÿÈʧý{¥ÿ¤#óÖ½óÅ ( € ‚ëþ=®?ë„ßú-«“þãÿ°LOþ™™Óƒÿ|ÂÿØMý;ýsëò#ôóóþ Cÿ(¼ý±¿ìšXÿêkájëÀ¿`¿ì/ ÿ§ scÜñö _ÿMLÿ7jýtüÀ( € ( é þ ÿ“ÔøÙÿf»âý[ +⸿|¿ÓùáÏ­á±¾¸ýÎpÕñ‡ÕŸÃÏüåÿ'©ðOþÍwÃßú¶>-×Ùð†ù‡¦óÄ)Å`½qû„þokíO’ ( € (ý"à‹ßò‹ÏØçþÉ¥ÿþ¦¾*¯È±ÿïØßû Äÿé韧àÿÜð¿ö CÿMDý?®C¤ÿ# _øö·ÿ®赯×pî8/ûÃ阘cß1ö_ÿNÌžºÎ` € ( Яø$·ü¤ßö"ÿ²Ñ/þ«ßWÄ¿ò*«ÿ_hÿéÄ{YüŒ©ÿ׺ßúC?Òú¿6>øþl?àèù2ÙÏþÏwÁ¿úÍŸµ5{Ü7ÿ#j?õî¿þš‘ãgÿò,­þ:?úv'ñ _¥P@Pö¥ÿ·ɪþÕ_övOÿªà­~qÄÿò4—ýx£ùHûÞÿ‘t?ëí_ý(þ«çlÿ6ø,ßü¥köâÿ²…ð»ÿYŸàe~Ãò+ý­ù£àø‡þF2ÿ¯4¿öãó>¾„ð€ ( €9/ÿȉã_û¼Gÿ¦{ÚçÅÿºâ¿ì·þ›‘¾ýçÿ_ééÈŸÿÙapache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/images/zookeeper_small.gif0100644 0000000 0000000 00000011357 15051152474 032752 0ustar00rootroot0000000 0000000 GIF89aOpçÿ7(%3%3/ /-!;*.9 !5'275)K8 %5:!*lI ="'<,E 7%%),/`,~ -UD&4)"I&"-8B(#/CZ&L+ G-:u)98Q#=U.Y/ T0K1'E3'H6$+:Sh/P6,`5 E‹/>Ng5+EQ9(g9y3L>6R=14I)p< a>,]@$2M%/O 6GbFEJDJ,QfŽ@$aK?†H @Tt=]-;_(J1fQFjR;‹M_V;‹J1O >d,@g)Ee3—Q¤L+K^2d“Ci1•R3v[=~Y=U Mh=‘W:Do/Sj5Ml5Ll;o_VHn5Kn0¡XOfHq,waJ¨U4_ka‚Or‚$l€P^…Kkz˜]ˆG€}QªqSY‹@±pO`ŠBžwP`¨¦vXžyX˜{ZaKdŒR¨yO`’Gf‘Oi’Ji‘V}‘*s‘Qj–S«„eŸˆc´‚cm™V§‡fkš]‘–µ†ZžŠwsš^oœYŒ“d²‰b¹ŠjxŸc‰•¢v¢_pšÀ¿c½”nµ–r†§^Ê’h¡œ›Å—jÊžpˆ©ÊÐn“§¿ÓžfÆ }¢ƒÛ¢kϤ{Ú¥sÖ§yߥ{à¬}Û®ä­tè­ƒ´ÂTÞ²‰¡¼ÚÉŠ賆㵆ﺌ輒¸Í…뽎¶ÆÙ¿ÅÉøÂ”óÄ”øÉ™ÿÊ›êáýÎÿÒ¡íñeåéê÷ø†ÿÿÿ!þ ZooKeeper!ù ÿ,Opþÿ H° ÁƒTŰ¡ª@ #JœHñà 8‰MÊÈq’ÇIa*ŠIñÁŠ9N¢\ɤ%“0áœIS`7=xXÁ³§Ï-áȬItâÍ H“fÀT§Ó'à -JÕ`œIuf`Ð$“¡& ¶f=ù²ªÙa"•PbÄ‹8ô´lzölvÍqöìQ¶/z–ÕOp?=žu¹V%¯Ê¾fdpà6p*ë´­£2/Z´uó6<ËÉòÙ#F”¸ÃGŠw ’µam]zH`Ú¬€ Hû4(Jp·2ðî]x€Nïʉëv  ÖOþˆ¸Pâõ³Öíݳ'Ï8qÛºáJ2‚()Î÷ 0aŸ½ÿÿ½ÓN;ï3"¡_UÄpƒ>ÓLS>ëÙƒO8Ó˜Í9d!É‚TÕÒIÙ„€Ã µÔ#O;fdŠ4ØMÊ' 5M'2– ´L3à6ŸL@Ç8Ùàð 35U 3¡ÐRÎ70N8õ¸£Î Ì´“ L&IÔ'ÇHòÎ;òØŽ:÷¤©O=ïdJ(^E‡4íÔ£ž7h¦ 84ÆYÔ6å”yži®—8uøù§8í¼S1Ô¨ÎzòÀ—¨¢5a#N9íÈ#:ÿÝS%|VõM3¯,È §îÈSþöxN9ÛˆS*U§¦ª1ï¸ó2ôôÓ=È´3Î6Ûœ•+ˆáY9ßxc΀ïÁiÖ² :€”¨£ Ê¢ªŸ)w,Ž7ø¨Ç^£ÓàRH׊ÛÛИr 5ûL©¡ïŒ‹4ž@R‰¸63Jo¦@“ #ÊP£>çêƒ>Í1ž€R ,wlAÔ©CÆ‹3ÐÜñË/¨P¨:Ä´LË8ØXSH.«¬ÂÈ0wÔô0!›eD2Î8s #¾è’J8ûÞƒ›Æ’S‹-PÛÂÈ/ºAÓÎ=SµEÐÎ$“Œ)–ð‘J*íJ):¬@½Š-©L­‹%4Q#Œ#fÑËu2ÃäÍÇþɨÔS¨=í°¢ #mÞ†%ºÜ¶Ç#3 U1ÂuÐx“ Ãè¢ +ú¨§*ÐÃ˶Լ¶.¶è’ŒÕu¢59yQ Ýu2Å sr*m¤Bu0ùš2L1'·Qº(§óÂË0¦DDÌ>÷¬<È%—DM?;SL1¾ä}2ÕšïbIï¶“Jæ¨Û²ˆ%« Ÿüò!]¨>´Ô1Ã4iÂ.Ҕ랾æšS,ÄLJ·íÁ~àöÐ>Q`yšãEÕÒ‰¤¥ f˜ô”¦ŽŠøÝ£š-H¡1XD­t5ƒ…ŰQ¸PmlÄ#A‰Uè‚l³…BjÁžO…Ãaþf»<$²¼,€¥€" ñQìj° )D1µx¤Ã.l`% ‘‡<¢ÈC¡Ô¢b„Ã?Lš<à¡&b(„^Ä `DŒá~hƒ(`áŠR8Q3R`¢k¸#Æ€ˆFPB”x¤*y˜ƒŸ¨Eç¢7¤©'! óêVA H‚C%(A ?Èá¥XåxÑ c¤B«ƒ.9°AŠhD#‚)Š=d[î€Y'Â}Ù£vú[- b FàN„¥fÁ†ü”ÀD%¡ˆ1~ø…ò~a JøARÄ(ºéË`R‚˜«þ8H8Ì1 ZÔ"òð†<" w‘e7Ø4ª‡Ì•Šð¢ – (”˜a%‘‡GÈAßk§"¤ W@YÂ#IQØ‚uYÕ?›éZÀlò¢=êŒíYn¶€¤"!z Du( Q UB⣶àÕ`ñ‡\¸"y ¨@€ùÈ,ê"!æ(G8€¡ŽiPƒÙHk«Ô“&y¼ƒ· Ú/Ä@ m ‚`B%¸é “æ¡•ƒ-ªQ4]¸ÂV]C*2H€…oK<Úãb,³Ù†4Ò:wxÖãÆÚVáNÅv\$ÆWB`˜þC5üÁKØ¢]dÃjÇð@‚†©ƒÚŽqLcUã 6Ž! lhvØp.Æ"©Í+Ø!ÂÄ&p›P`‚rØ;l Pltµàí_+áRÔébY&‘Šå†ÂCŸx‘4ޱa• WЪAü@Š–’¢¥$;¶.uµ€]ƒ Öp…?¬M„©¦p)„8á •P„"¬ð…ÒSDp<‹º(>¸æ] Q K0âÆ—«õÈ‘Œ!xUÈØÀ—Â{x¢-`‘`À¸cP‚+R÷‹¼¥B$³¸ÄìÌÒ¾U˜pÉv Lçb'ÃX Ö\þ•‹qåŠd™ng‘â—¡hHRÈÖ½XE“ PŒ¡†UVF1†åYÀÙ,†Åh½ìÝJˆ!ù0†(ÌkæÀR–(+œ@’Yº/mæyàq*B˜8…"œ a-¡žh6h1“Y´âÐfQD8 Š5°á®€&0WKάâBã0.M„qkÈÈÁ‘rðn¸¸„-*â×׆QŠ5<×á(B'HÂlBôðÊk³a VØ"$Öð‡§ZûK°²Ýa&flI$Âx…¹ÏÒD÷Ås†ª„1‚‡‰`?ÈÓbæÈi=è kQ$ொj–º:0¶°¹—þCò>º¢Èf+0²Ñ%Š *ãTñƒp¿‡:úÁ½é nÔD‹q¨Gµ 3när›¸é÷D¸ K€OG]. áó{ȃû4‡9Šs¢0bæ'¥/œ¾ô§G ¢HÑ=Ôá o¸ÃÙàºY¢¶tÝQ-N‡:6—ìeWÂo-k6¦!wªÊa·;êø‹Tt<}"‡E.ÑŽVyjVÉ*|Qø_ø"Žzþƒüpéà’<äð]•;ÊA$ÂWäåZc§-¾—7ݰl¨v%N'@É3îÀ`&Gû¢lJ¯²ÿ`±Å1\aµ¤8a.D±ˆÞÁ”&Å'Êþ‘ŠÖžx#\힉èwç/Š‚qªp߯w¸™÷n»Ú%¿©[f¯æaÜ5Á3]G[à^à ¼p ƒ:¶K°Z¥ |wQ³{ ¿Gn£0€#±6 ·p¸Ô€K°DGÆO$ y$Á¤DÁ3o }J V ‡sK's¹Ð€ÿ°D¤ ë³c °E€ J€£ƒD!}N$(Ø;X=øÑg bÐFX “´lKXk`H˜°R\Øb°® oõZ.µ N8C³E àJÈ„5ÑUã$LDzæe…ph »À¢@lþN† ¤„3ƒDåBÚ…<°ˆnøº…AµfèB†Ob¨‡3ñN]å>„ØJ¥Dˆ€Rªd~º€‚¦Ó>ÚU ¡H]å¤ ¨¸Eu$WcÂe ¾3J¤X¼¥o04a¤p:U&W-ˆ¨ä‹•àŠ¯xD«àJkH¸„Ê8§ ÃBš³±Ä‹ðò¿ _ö^~ ð(b]´ݘŒ4a‡c ·°<º“Kç¨D‚@˜àe(´}ö]…OЈŽàß8‹ |pc „Š”ÔX ¥ÐŒ'¶R34CéHn ©4‘G¦Óq`ˆù ‡`¹‘¤ðþb0c a4ah^À3Ñ…È{P´gJ W º¥‘ö„ã]”àH×W­0C@’#±7¾ð ·àPÈCB^¸’W¨1Gi^E¸JŽÔ4ñ”C “#aw2AN”%&[âXаx¹aHš:€–#ˆC‘„^uTGÇøÄ&LsI‡sYGL…iM)}ù—4±,$ }4NèõnHOèKh(bs©20lðJ~6#Q™€™–¤Õ5¸ð¶kpZ(5Jðl"&bk°VÐEy q™¡ dÀ@r p÷T‡d1ƒr°×9mþ b¦!à* ðA  SÀ @ …Ðx o¬PzpŸ…€ñá©.žç™ž ` P  @øPËu ×Cõ@+Ò%üÙŸáy‘@ Z #aŸPC"¡o3±0àéŸ)j¡ª¡:"Q&pVI€@à öLÛp+ñŸ@ÚŸ‘` à¢ZÑ5Ð?`(P@0û ÇB0Z6¤)j}à z¤A5P<p¦Ñ-ðáÀ I`"ñŸ[š¢ ð¥E:¦J`-Àþv ¤50ÐOZB@2*7°¥[ª _à ¦~Z<gð©\À0 `‚z¦<ð¨qjЪg€; ©”j 0:À~ªHB`€¬Â vÀ1pª5ਣ:g0¬Áê40'Öjᙩ, ž»Ú« Á ¬Äjvàæj¬ Ñ@¦Jn`G GàãJ®ãú®q;“J  Àš:¦Þz4®äz°jàg`4pzÀ ¬ k°K®æ «qEP“ªÊ;¦ ‹±æº°Xp‡Šþ Gg`®ö np²(ë` ±ÿÀ±E0«µº«›&›³j°°\°²B`§A< P {³€põj´;¯!»¥`Fº¡™z a³æêG‹´Pp<ÀQz à´1ë©hk®k® ›° º²è*EÀµ@ûµš©`[¸P °ŸÊ°Y˶4@okжGpXÀž:³B¶Ÿº°ËÓš¯RÀµ;¸ ¨«áYžqŒËI»¶l ºë²Bð´•‹XÀ°gÀ‹Ë°º[¹· ºñ_@º)J¡á‰§6€I«»²{»KYÎz&@0›»º ¾ûº+ ±©j p½ÿ@ep¼?ëyʾ6°;б\k Ð @Éjª¦ÚëÚÐ1à¿PÀ pÀ@P \l0 À¾é˵›Áò;ºÆk üÁ |À",Â`¾a ÌÀ Ì¿,ÜÂ<Â"|èk¼Ç;ºüeà RœÀ>ìÃ|¿|ÿ Â>\-Ü ÃþDðÄR@ÃRðÄ;œ\ÀX\À&LÁ;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/index.md0100644 0000000 0000000 00000010443 15051152474 027247 0ustar00rootroot0000000 0000000 ## ZooKeeper: Because Coordinating Distributed Systems is a Zoo ZooKeeper is a high-performance coordination service for distributed applications. It exposes common services - such as naming, configuration management, synchronization, and group services - in a simple interface so you don't have to write them from scratch. You can use it off-the-shelf to implement consensus, group management, leader election, and presence protocols. And you can build on it for your own, specific needs. The following documents describe concepts and procedures to get you started using ZooKeeper. If you have more questions, please ask the [mailing list](http://zookeeper.apache.org/mailing_lists.html) or browse the archives. + **ZooKeeper Overview** Technical Overview Documents for Client Developers, Administrators, and Contributors + [Overview](zookeeperOver.html) - a bird's eye view of ZooKeeper, including design concepts and architecture + [Getting Started](zookeeperStarted.html) - a tutorial-style guide for developers to install, run, and program to ZooKeeper + [Release Notes](releasenotes.html) - new developer and user facing features, improvements, and incompatibilities + **Developers** Documents for Developers using the ZooKeeper Client API + [API Docs](apidocs/zookeeper-server/index.html) - the technical reference to ZooKeeper Client APIs + [Programmer's Guide](zookeeperProgrammers.html) - a client application developer's guide to ZooKeeper + [ZooKeeper Use Cases](zookeeperUseCases.html) - a series of use cases using the ZooKeeper. + [ZooKeeper Java Example](javaExample.html) - a simple Zookeeper client application, written in Java + [Barrier and Queue Tutorial](zookeeperTutorial.html) - sample implementations of barriers and queues + [ZooKeeper Recipes](recipes.html) - higher level solutions to common problems in distributed applications + **Administrators & Operators** Documents for Administrators and Operations Engineers of ZooKeeper Deployments + [Administrator's Guide](zookeeperAdmin.html) - a guide for system administrators and anyone else who might deploy ZooKeeper + [Quota Guide](zookeeperQuotas.html) - a guide for system administrators on Quotas in ZooKeeper. + [Snapshot and Restore Guide](zookeeperSnapshotAndRestore.html) - a guide for system administrators on take snapshot and restore ZooKeeper. + [JMX](zookeeperJMX.html) - how to enable JMX in ZooKeeper + [Hierarchical Quorums](zookeeperHierarchicalQuorums.html) - a guide on how to use hierarchical quorums + [Oracle Quorum](zookeeperOracleQuorums.html) - the introduction to Oracle Quorum increases the availability of a cluster of 2 ZooKeeper instances with a failure detector. + [Observers](zookeeperObservers.html) - non-voting ensemble members that easily improve ZooKeeper's scalability + [Dynamic Reconfiguration](zookeeperReconfig.html) - a guide on how to use dynamic reconfiguration in ZooKeeper + [ZooKeeper CLI](zookeeperCLI.html) - a guide on how to use the ZooKeeper command line interface + [ZooKeeper Tools](zookeeperTools.html) - a guide on how to use a series of tools for ZooKeeper + [ZooKeeper Monitor](zookeeperMonitor.html) - a guide on how to monitor the ZooKeeper + [Audit Logging](zookeeperAuditLogs.html) - a guide on how to configure audit logs in ZooKeeper Server and what contents are logged. + **Contributors** Documents for Developers Contributing to the ZooKeeper Open Source Project + [ZooKeeper Internals](zookeeperInternals.html) - assorted topics on the inner workings of ZooKeeper + **Miscellaneous ZooKeeper Documentation** + [Wiki](https://cwiki.apache.org/confluence/display/ZOOKEEPER) + [FAQ](https://cwiki.apache.org/confluence/display/ZOOKEEPER/FAQ) apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/javaExample.md0100644 0000000 0000000 00000053353 15051152474 030404 0ustar00rootroot0000000 0000000 # ZooKeeper Java Example * [A Simple Watch Client](#ch_Introduction) * [Requirements](#sc_requirements) * [Program Design](#sc_design) * [The Executor Class](#sc_executor) * [The DataMonitor Class](#sc_DataMonitor) * [Complete Source Listings](#sc_completeSourceCode) ## A Simple Watch Client To introduce you to the ZooKeeper Java API, we develop here a very simple watch client. This ZooKeeper client watches a znode for changes and responds to by starting or stopping a program. ### Requirements The client has four requirements: * It takes as parameters: * the address of the ZooKeeper service * the name of a znode - the one to be watched * the name of a file to write the output to * an executable with arguments. * It fetches the data associated with the znode and starts the executable. * If the znode changes, the client re-fetches the contents and restarts the executable. * If the znode disappears, the client kills the executable. ### Program Design Conventionally, ZooKeeper applications are broken into two units, one which maintains the connection, and the other which monitors data. In this application, the class called the **Executor** maintains the ZooKeeper connection, and the class called the **DataMonitor** monitors the data in the ZooKeeper tree. Also, Executor contains the main thread and contains the execution logic. It is responsible for what little user interaction there is, as well as interaction with the executable program you pass in as an argument and which the sample (per the requirements) shuts down and restarts, according to the state of the znode. ## The Executor Class The Executor object is the primary container of the sample application. It contains both the **ZooKeeper** object, **DataMonitor**, as described above in [Program Design](#sc_design). // from the Executor class... public static void main(String[] args) { if (args.length < 4) { System.err .println("USAGE: Executor hostPort znode filename program [args ...]"); System.exit(2); } String hostPort = args[0]; String znode = args[1]; String filename = args[2]; String exec[] = new String[args.length - 3]; System.arraycopy(args, 3, exec, 0, exec.length); try { new Executor(hostPort, znode, filename, exec).run(); } catch (Exception e) { e.printStackTrace(); } } public Executor(String hostPort, String znode, String filename, String exec[]) throws KeeperException, IOException { this.filename = filename; this.exec = exec; zk = new ZooKeeper(hostPort, 3000, this); dm = new DataMonitor(zk, znode, null, this); } public void run() { try { synchronized (this) { while (!dm.dead) { wait(); } } } catch (InterruptedException e) { } } Recall that the Executor's job is to start and stop the executable whose name you pass in on the command line. It does this in response to events fired by the ZooKeeper object. As you can see in the code above, the Executor passes a reference to itself as the Watcher argument in the ZooKeeper constructor. It also passes a reference to itself as DataMonitorListener argument to the DataMonitor constructor. Per the Executor's definition, it implements both these interfaces: public class Executor implements Watcher, Runnable, DataMonitor.DataMonitorListener { ... The **Watcher** interface is defined by the ZooKeeper Java API. ZooKeeper uses it to communicate back to its container. It supports only one method, `process()`, and ZooKeeper uses it to communicates generic events that the main thread would be interested in, such as the state of the ZooKeeper connection or the ZooKeeper session. The Executor in this example simply forwards those events down to the DataMonitor to decide what to do with them. It does this simply to illustrate the point that, by convention, the Executor or some Executor-like object "owns" the ZooKeeper connection, but it is free to delegate the events to other events to other objects. It also uses this as the default channel on which to fire watch events. (More on this later.) public void process(WatchedEvent event) { dm.process(event); } The **DataMonitorListener** interface, on the other hand, is not part of the ZooKeeper API. It is a completely custom interface, designed for this sample application. The DataMonitor object uses it to communicate back to its container, which is also the Executor object. The DataMonitorListener interface looks like this: public interface DataMonitorListener { /** * The existence status of the node has changed. */ void exists(byte data[]); /** * The ZooKeeper session is no longer valid. * * @param rc * the ZooKeeper reason code */ void closing(int rc); } This interface is defined in the DataMonitor class and implemented in the Executor class. When `Executor.exists()` is invoked, the Executor decides whether to start up or shut down per the requirements. Recall that the requires say to kill the executable when the znode ceases to _exist_. When `Executor.closing()` is invoked, the Executor decides whether or not to shut itself down in response to the ZooKeeper connection permanently disappearing. As you might have guessed, DataMonitor is the object that invokes these methods, in response to changes in ZooKeeper's state. Here are Executor's implementation of `DataMonitorListener.exists()` and `DataMonitorListener.closing`: public void exists( byte[] data ) { if (data == null) { if (child != null) { System.out.println("Killing process"); child.destroy(); try { child.waitFor(); } catch (InterruptedException e) { } } child = null; } else { if (child != null) { System.out.println("Stopping child"); child.destroy(); try { child.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } } try { FileOutputStream fos = new FileOutputStream(filename); fos.write(data); fos.close(); } catch (IOException e) { e.printStackTrace(); } try { System.out.println("Starting child"); child = Runtime.getRuntime().exec(exec); new StreamWriter(child.getInputStream(), System.out); new StreamWriter(child.getErrorStream(), System.err); } catch (IOException e) { e.printStackTrace(); } } } public void closing(int rc) { synchronized (this) { notifyAll(); } } ## The DataMonitor Class The DataMonitor class has the meat of the ZooKeeper logic. It is mostly asynchronous and event driven. DataMonitor kicks things off in the constructor with: public DataMonitor(ZooKeeper zk, String znode, Watcher chainedWatcher, DataMonitorListener listener) { this.zk = zk; this.znode = znode; this.chainedWatcher = chainedWatcher; this.listener = listener; // Get things started by checking if the node exists. We are going // to be completely event driven The call to `ZooKeeper.exists()` checks for the existence of the znode, sets a watch, and passes a reference to itself (`this`) as the completion callback object. In this sense, it kicks things off, since the real processing happens when the watch is triggered. ###### Note >Don't confuse the completion callback with the watch callback. The `ZooKeeper.exists()` completion callback, which happens to be the method `StatCallback.processResult()` implemented in the DataMonitor object, is invoked when the asynchronous _setting of the watch_ operation (by `ZooKeeper.exists()`) completes on the server. >The triggering of the watch, on the other hand, sends an event to the _Executor_ object, since the Executor registered as the Watcher of the ZooKeeper object. >As an aside, you might note that the DataMonitor could also register itself as the Watcher for this particular watch event. This is new to ZooKeeper 3.0.0 (the support of multiple Watchers). In this example, however, DataMonitor does not register as the Watcher. When the `ZooKeeper.exists()` operation completes on the server, the ZooKeeper API invokes this completion callback on the client: public void processResult(int rc, String path, Object ctx, Stat stat) { boolean exists; switch (rc) { case Code.Ok: exists = true; break; case Code.NoNode: exists = false; break; case Code.SessionExpired: case Code.NoAuth: dead = true; listener.closing(rc); return; default: // Retry errors zk.exists(znode, true, this, null); return; } byte b[] = null; if (exists) { try { b = zk.getData(znode, false, null); } catch (KeeperException e) { // We don't need to worry about recovering now. The watch // callbacks will kick off any exception handling e.printStackTrace(); } catch (InterruptedException e) { return; } } if ((b == null && b != prevData) || (b != null && !Arrays.equals(prevData, b))) { listener.exists(b); prevData = b; } } The code first checks the error codes for znode existence, fatal errors, and recoverable errors. If the file (or znode) exists, it gets the data from the znode, and then invoke the exists() callback of Executor if the state has changed. Note, it doesn't have to do any Exception processing for the getData call because it has watches pending for anything that could cause an error: if the node is deleted before it calls `ZooKeeper.getData()`, the watch event set by the `ZooKeeper.exists()` triggers a callback; if there is a communication error, a connection watch event fires when the connection comes back up. Finally, notice how DataMonitor processes watch events: public void process(WatchedEvent event) { String path = event.getPath(); if (event.getType() == Event.EventType.None) { // We are are being told that the state of the // connection has changed switch (event.getState()) { case SyncConnected: // In this particular example we don't need to do anything // here - watches are automatically re-registered with // server and any watches triggered while the client was // disconnected will be delivered (in order of course) break; case Expired: // It's all over dead = true; listener.closing(KeeperException.Code.SessionExpired); break; } } else { if (path != null && path.equals(znode)) { // Something has changed on the node, let's find out zk.exists(znode, true, this, null); } } if (chainedWatcher != null) { chainedWatcher.process(event); } } If the client-side ZooKeeper libraries can re-establish the communication channel (SyncConnected event) to ZooKeeper before session expiration (Expired event) all of the session's watches will automatically be re-established with the server (auto-reset of watches is new in ZooKeeper 3.0.0). See [ZooKeeper Watches](zookeeperProgrammers.html#ch_zkWatches) in the programmer guide for more on this. A bit lower down in this function, when DataMonitor gets an event for a znode, it calls`ZooKeeper.exists()` to find out what has changed. ## Complete Source Listings ### Executor.java /** * A simple example program to use DataMonitor to start and * stop executables based on a znode. The program watches the * specified znode and saves the data that corresponds to the * znode in the filesystem. It also starts the specified program * with the specified arguments when the znode exists and kills * the program if the znode goes away. */ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; public class Executor implements Watcher, Runnable, DataMonitor.DataMonitorListener { String znode; DataMonitor dm; ZooKeeper zk; String filename; String exec[]; Process child; public Executor(String hostPort, String znode, String filename, String exec[]) throws KeeperException, IOException { this.filename = filename; this.exec = exec; zk = new ZooKeeper(hostPort, 3000, this); dm = new DataMonitor(zk, znode, null, this); } /** * @param args */ public static void main(String[] args) { if (args.length < 4) { System.err .println("USAGE: Executor hostPort znode filename program [args ...]"); System.exit(2); } String hostPort = args[0]; String znode = args[1]; String filename = args[2]; String exec[] = new String[args.length - 3]; System.arraycopy(args, 3, exec, 0, exec.length); try { new Executor(hostPort, znode, filename, exec).run(); } catch (Exception e) { e.printStackTrace(); } } /*************************************************************************** * We do process any events ourselves, we just need to forward them on. * * @see org.apache.zookeeper.Watcher#process(org.apache.zookeeper.proto.WatcherEvent) */ public void process(WatchedEvent event) { dm.process(event); } public void run() { try { synchronized (this) { while (!dm.dead) { wait(); } } } catch (InterruptedException e) { } } public void closing(int rc) { synchronized (this) { notifyAll(); } } static class StreamWriter extends Thread { OutputStream os; InputStream is; StreamWriter(InputStream is, OutputStream os) { this.is = is; this.os = os; start(); } public void run() { byte b[] = new byte[80]; int rc; try { while ((rc = is.read(b)) > 0) { os.write(b, 0, rc); } } catch (IOException e) { } } } public void exists(byte[] data) { if (data == null) { if (child != null) { System.out.println("Killing process"); child.destroy(); try { child.waitFor(); } catch (InterruptedException e) { } } child = null; } else { if (child != null) { System.out.println("Stopping child"); child.destroy(); try { child.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } } try { FileOutputStream fos = new FileOutputStream(filename); fos.write(data); fos.close(); } catch (IOException e) { e.printStackTrace(); } try { System.out.println("Starting child"); child = Runtime.getRuntime().exec(exec); new StreamWriter(child.getInputStream(), System.out); new StreamWriter(child.getErrorStream(), System.err); } catch (IOException e) { e.printStackTrace(); } } } } ### DataMonitor.java /** * A simple class that monitors the data and existence of a ZooKeeper * node. It uses asynchronous ZooKeeper APIs. */ import java.util.Arrays; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.data.Stat; public class DataMonitor implements Watcher, StatCallback { ZooKeeper zk; String znode; Watcher chainedWatcher; boolean dead; DataMonitorListener listener; byte prevData[]; public DataMonitor(ZooKeeper zk, String znode, Watcher chainedWatcher, DataMonitorListener listener) { this.zk = zk; this.znode = znode; this.chainedWatcher = chainedWatcher; this.listener = listener; // Get things started by checking if the node exists. We are going // to be completely event driven zk.exists(znode, true, this, null); } /** * Other classes use the DataMonitor by implementing this method */ public interface DataMonitorListener { /** * The existence status of the node has changed. */ void exists(byte data[]); /** * The ZooKeeper session is no longer valid. * * @param rc * the ZooKeeper reason code */ void closing(int rc); } public void process(WatchedEvent event) { String path = event.getPath(); if (event.getType() == Event.EventType.None) { // We are are being told that the state of the // connection has changed switch (event.getState()) { case SyncConnected: // In this particular example we don't need to do anything // here - watches are automatically re-registered with // server and any watches triggered while the client was // disconnected will be delivered (in order of course) break; case Expired: // It's all over dead = true; listener.closing(KeeperException.Code.SessionExpired); break; } } else { if (path != null && path.equals(znode)) { // Something has changed on the node, let's find out zk.exists(znode, true, this, null); } } if (chainedWatcher != null) { chainedWatcher.process(event); } } public void processResult(int rc, String path, Object ctx, Stat stat) { boolean exists; switch (rc) { case Code.Ok: exists = true; break; case Code.NoNode: exists = false; break; case Code.SessionExpired: case Code.NoAuth: dead = true; listener.closing(rc); return; default: // Retry errors zk.exists(znode, true, this, null); return; } byte b[] = null; if (exists) { try { b = zk.getData(znode, false, null); } catch (KeeperException e) { // We don't need to worry about recovering now. The watch // callbacks will kick off any exception handling e.printStackTrace(); } catch (InterruptedException e) { return; } } if ((b == null && b != prevData) || (b != null && !Arrays.equals(prevData, b))) { listener.exists(b); prevData = b; } } } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/recipes.md0100644 0000000 0000000 00000046772 15051152474 027610 0ustar00rootroot0000000 0000000 # ZooKeeper Recipes and Solutions * [A Guide to Creating Higher-level Constructs with ZooKeeper](#ch_recipes) * [Important Note About Error Handling](#sc_recipes_errorHandlingNote) * [Out of the Box Applications: Name Service, Configuration, Group Membership](#sc_outOfTheBox) * [Barriers](#sc_recipes_eventHandles) * [Double Barriers](#sc_doubleBarriers) * [Queues](#sc_recipes_Queues) * [Priority Queues](#sc_recipes_priorityQueues) * [Locks](#sc_recipes_Locks) * [Recoverable Errors and the GUID](#sc_recipes_GuidNote) * [Shared Locks](#Shared+Locks) * [Revocable Shared Locks](#sc_revocableSharedLocks) * [Two-phased Commit](#sc_recipes_twoPhasedCommit) * [Leader Election](#sc_leaderElection) ## A Guide to Creating Higher-level Constructs with ZooKeeper In this article, you'll find guidelines for using ZooKeeper to implement higher order functions. All of them are conventions implemented at the client and do not require special support from ZooKeeper. Hopefully the community will capture these conventions in client-side libraries to ease their use and to encourage standardization. One of the most interesting things about ZooKeeper is that even though ZooKeeper uses _asynchronous_ notifications, you can use it to build _synchronous_ consistency primitives, such as queues and locks. As you will see, this is possible because ZooKeeper imposes an overall order on updates, and has mechanisms to expose this ordering. Note that the recipes below attempt to employ best practices. In particular, they avoid polling, timers or anything else that would result in a "herd effect", causing bursts of traffic and limiting scalability. There are many useful functions that can be imagined that aren't included here - revocable read-write priority locks, as just one example. And some of the constructs mentioned here - locks, in particular - illustrate certain points, even though you may find other constructs, such as event handles or queues, a more practical means of performing the same function. In general, the examples in this section are designed to stimulate thought. ### Important Note About Error Handling When implementing the recipes you must handle recoverable exceptions (see the [FAQ](https://cwiki.apache.org/confluence/display/ZOOKEEPER/FAQ)). In particular, several of the recipes employ sequential ephemeral nodes. When creating a sequential ephemeral node there is an error case in which the create() succeeds on the server but the server crashes before returning the name of the node to the client. When the client reconnects its session is still valid and, thus, the node is not removed. The implication is that it is difficult for the client to know if its node was created or not. The recipes below include measures to handle this. ### Out of the Box Applications: Name Service, Configuration, Group Membership Name service and configuration are two of the primary applications of ZooKeeper. These two functions are provided directly by the ZooKeeper API. Another function directly provided by ZooKeeper is _group membership_. The group is represented by a node. Members of the group create ephemeral nodes under the group node. Nodes of the members that fail abnormally will be removed automatically when ZooKeeper detects the failure. ### Barriers Distributed systems use _barriers_ to block processing of a set of nodes until a condition is met at which time all the nodes are allowed to proceed. Barriers are implemented in ZooKeeper by designating a barrier node. The barrier is in place if the barrier node exists. Here's the pseudo code: 1. Client calls the ZooKeeper API's **exists()** function on the barrier node, with _watch_ set to true. 1. If **exists()** returns false, the barrier is gone and the client proceeds 1. Else, if **exists()** returns true, the clients wait for a watch event from ZooKeeper for the barrier node. 1. When the watch event is triggered, the client reissues the **exists( )** call, again waiting until the barrier node is removed. #### Double Barriers Double barriers enable clients to synchronize the beginning and the end of a computation. When enough processes have joined the barrier, processes start their computation and leave the barrier once they have finished. This recipe shows how to use a ZooKeeper node as a barrier. The pseudo code in this recipe represents the barrier node as _b_. Every client process _p_ registers with the barrier node on entry and unregisters when it is ready to leave. A node registers with the barrier node via the **Enter** procedure below, it waits until _x_ client process register before proceeding with the computation. (The _x_ here is up to you to determine for your system.) | **Enter** | **Leave** | |-----------------------------------|-------------------------------| | 1. Create a name __n_ = _b_+“/â€+_p__ | 1. **L = getChildren(b, false)** | | 2. Set watch: **exists(_b_ + ‘‘/ready’’, true)** | 2. if no children, exit | | 3. Create child: **create(_n_, EPHEMERAL)** | 3. if _p_ is only process node in L, delete(n) and exit | | 4. **L = getChildren(b, false)** | 4. if _p_ is the lowest process node in L, wait on highest process node in L | | 5. if fewer children in L than_x_, wait for watch event | 5. else **delete(_n_)**if still exists and wait on lowest process node in L | | 6. else **create(b + ‘‘/ready’’, REGULAR)** | 6. goto 1 | On entering, all processes watch on a ready node and create an ephemeral node as a child of the barrier node. Each process but the last enters the barrier and waits for the ready node to appear at line 5. The process that creates the xth node, the last process, will see x nodes in the list of children and create the ready node, waking up the other processes. Note that waiting processes wake up only when it is time to exit, so waiting is efficient. On exit, you can't use a flag such as _ready_ because you are watching for process nodes to go away. By using ephemeral nodes, processes that fail after the barrier has been entered do not prevent correct processes from finishing. When processes are ready to leave, they need to delete their process nodes and wait for all other processes to do the same. Processes exit when there are no process nodes left as children of _b_. However, as an efficiency, you can use the lowest process node as the ready flag. All other processes that are ready to exit watch for the lowest existing process node to go away, and the owner of the lowest process watches for any other process node (picking the highest for simplicity) to go away. This means that only a single process wakes up on each node deletion except for the last node, which wakes up everyone when it is removed. ### Queues Distributed queues are a common data structure. To implement a distributed queue in ZooKeeper, first designate a znode to hold the queue, the queue node. The distributed clients put something into the queue by calling create() with a pathname ending in "queue-", with the _sequence_ and _ephemeral_ flags in the create() call set to true. Because the _sequence_ flag is set, the new pathname will have the form _path-to-queue-node_/queue-X, where X is a monotonic increasing number. A client that wants to be removed from the queue calls ZooKeeper's **getChildren( )** function, with _watch_ set to true on the queue node, and begins processing nodes with the lowest number. The client does not need to issue another **getChildren( )** until it exhausts the list obtained from the first **getChildren( )** call. If there are no children in the queue node, the reader waits for a watch notification to check the queue again. ###### Note >There now exists a Queue implementation in ZooKeeper recipes directory. This is distributed with the release -- zookeeper-recipes/zookeeper-recipes-queue directory of the release artifact. #### Priority Queues To implement a priority queue, you need only make two simple changes to the generic [queue recipe](#sc_recipes_Queues) . First, to add to a queue, the pathname ends with "queue-YY" where YY is the priority of the element with lower numbers representing higher priority (just like UNIX). Second, when removing from the queue, a client uses an up-to-date children list meaning that the client will invalidate previously obtained children lists if a watch notification triggers for the queue node. ### Locks Fully distributed locks that are globally synchronous, meaning at any snapshot in time no two clients think they hold the same lock. These can be implemented using ZooKeeper. As with priority queues, first define a lock node. ###### Note >There now exists a Lock implementation in ZooKeeper recipes directory. This is distributed with the release -- zookeeper-recipes/zookeeper-recipes-lock directory of the release artifact. Clients wishing to obtain a lock do the following: 1. Call **create( )** with a pathname of "_locknode_/guid-lock-" and the _sequence_ and _ephemeral_ flags set. The _guid_ is needed in case the create() result is missed. See the note below. 1. Call **getChildren( )** on the lock node _without_ setting the watch flag (this is important to avoid the herd effect). 1. If the pathname created in step **1** has the lowest sequence number suffix, the client has the lock and the client exits the protocol. 1. The client calls **exists( )** with the watch flag set on the path in the lock directory with the next lowest sequence number. 1. if **exists( )** returns null, go to step **2**. Otherwise, wait for a notification for the pathname from the previous step before going to step **2**. The unlock protocol is very simple: clients wishing to release a lock simply delete the node they created in step 1. Here are a few things to notice: * The removal of a node will only cause one client to wake up since each node is watched by exactly one client. In this way, you avoid the herd effect. * There is no polling or timeouts. * Because of the way you implement locking, it is easy to see the amount of lock contention, break locks, debug locking problems, etc. #### Recoverable Errors and the GUID * If a recoverable error occurs calling **create()** the client should call **getChildren()** and check for a node containing the _guid_ used in the path name. This handles the case (noted [above](#sc_recipes_errorHandlingNote)) of the create() succeeding on the server but the server crashing before returning the name of the new node. #### Shared Locks You can implement shared locks by with a few changes to the lock protocol: | **Obtaining a read lock:** | **Obtaining a write lock:** | |----------------------------|-----------------------------| | 1. Call **create( )** to create a node with pathname "*guid-/read-*". This is the lock node use later in the protocol. Make sure to set both the _sequence_ and _ephemeral_ flags. | 1. Call **create( )** to create a node with pathname "*guid-/write-*". This is the lock node spoken of later in the protocol. Make sure to set both _sequence_ and _ephemeral_ flags. | | 2. Call **getChildren( )** on the lock node _without_ setting the _watch_ flag - this is important, as it avoids the herd effect. | 2. Call **getChildren( )** on the lock node _without_ setting the _watch_ flag - this is important, as it avoids the herd effect. | | 3. If there are no children with a pathname starting with "*write-*" and having a lower sequence number than the node created in step **1**, the client has the lock and can exit the protocol. | 3. If there are no children with a lower sequence number than the node created in step **1**, the client has the lock and the client exits the protocol. | | 4. Otherwise, call **exists( )**, with _watch_ flag, set on the node in lock directory with pathname starting with "*write-*" having the next lowest sequence number. | 4. Call **exists( ),** with _watch_ flag set, on the node with the pathname that has the next lowest sequence number. | | 5. If **exists( )** returns _false_, goto step **2**. | 5. If **exists( )** returns _false_, goto step **2**. Otherwise, wait for a notification for the pathname from the previous step before going to step **2**. | | 6. Otherwise, wait for a notification for the pathname from the previous step before going to step **2** | | Notes: * It might appear that this recipe creates a herd effect: when there is a large group of clients waiting for a read lock, and all getting notified more or less simultaneously when the "*write-*" node with the lowest sequence number is deleted. In fact. that's valid behavior: as all those waiting reader clients should be released since they have the lock. The herd effect refers to releasing a "herd" when in fact only a single or a small number of machines can proceed. * See the [note for Locks](#sc_recipes_GuidNote) on how to use the guid in the node. #### Revocable Shared Locks With minor modifications to the Shared Lock protocol, you make shared locks revocable by modifying the shared lock protocol: In step **1**, of both obtain reader and writer lock protocols, call **getData( )** with _watch_ set, immediately after the call to **create( )**. If the client subsequently receives notification for the node it created in step **1**, it does another **getData( )** on that node, with _watch_ set and looks for the string "unlock", which signals to the client that it must release the lock. This is because, according to this shared lock protocol, you can request the client with the lock give up the lock by calling **setData()** on the lock node, writing "unlock" to that node. Note that this protocol requires the lock holder to consent to releasing the lock. Such consent is important, especially if the lock holder needs to do some processing before releasing the lock. Of course you can always implement _Revocable Shared Locks with Freaking Laser Beams_ by stipulating in your protocol that the revoker is allowed to delete the lock node if after some length of time the lock isn't deleted by the lock holder. ### Two-phased Commit A two-phase commit protocol is an algorithm that lets all clients in a distributed system agree either to commit a transaction or abort. In ZooKeeper, you can implement a two-phased commit by having a coordinator create a transaction node, say "/app/Tx", and one child node per participating site, say "/app/Tx/s_i". When coordinator creates the child node, it leaves the content undefined. Once each site involved in the transaction receives the transaction from the coordinator, the site reads each child node and sets a watch. Each site then processes the query and votes "commit" or "abort" by writing to its respective node. Once the write completes, the other sites are notified, and as soon as all sites have all votes, they can decide either "abort" or "commit". Note that a node can decide "abort" earlier if some site votes for "abort". An interesting aspect of this implementation is that the only role of the coordinator is to decide upon the group of sites, to create the ZooKeeper nodes, and to propagate the transaction to the corresponding sites. In fact, even propagating the transaction can be done through ZooKeeper by writing it in the transaction node. There are two important drawbacks of the approach described above. One is the message complexity, which is O(n²). The second is the impossibility of detecting failures of sites through ephemeral nodes. To detect the failure of a site using ephemeral nodes, it is necessary that the site create the node. To solve the first problem, you can have only the coordinator notified of changes to the transaction nodes, and then notify the sites once coordinator reaches a decision. Note that this approach is scalable, but it is slower too, as it requires all communication to go through the coordinator. To address the second problem, you can have the coordinator propagate the transaction to the sites, and have each site creating its own ephemeral node. ### Leader Election A simple way of doing leader election with ZooKeeper is to use the **SEQUENCE|EPHEMERAL** flags when creating znodes that represent "proposals" of clients. The idea is to have a znode, say "/election", such that each znode creates a child znode "/election/guid-n_" with both flags SEQUENCE|EPHEMERAL. With the sequence flag, ZooKeeper automatically appends a sequence number that is greater than anyone previously appended to a child of "/election". The process that created the znode with the smallest appended sequence number is the leader. That's not all, though. It is important to watch for failures of the leader, so that a new client arises as the new leader in the case the current leader fails. A trivial solution is to have all application processes watching upon the current smallest znode, and checking if they are the new leader when the smallest znode goes away (note that the smallest znode will go away if the leader fails because the node is ephemeral). But this causes a herd effect: upon a failure of the current leader, all other processes receive a notification, and execute getChildren on "/election" to obtain the current list of children of "/election". If the number of clients is large, it causes a spike on the number of operations that ZooKeeper servers have to process. To avoid the herd effect, it is sufficient to watch for the next znode down on the sequence of znodes. If a client receives a notification that the znode it is watching is gone, then it becomes the new leader in the case that there is no smaller znode. Note that this avoids the herd effect by not having all clients watching the same znode. Here's the pseudo code: Let ELECTION be a path of choice of the application. To volunteer to be a leader: 1. Create znode z with path "ELECTION/guid-n_" with both SEQUENCE and EPHEMERAL flags; 1. Let C be the children of "ELECTION", and I am the sequence number of z; 1. Watch for changes on "ELECTION/guid-n_j", where j is the largest sequence number such that j < i and n_j is a znode in C; Upon receiving a notification of znode deletion: 1. Let C be the new set of children of ELECTION; 1. If z is the smallest node in C, then execute leader procedure; 1. Otherwise, watch for changes on "ELECTION/guid-n_j", where j is the largest sequence number such that j < i and n_j is a znode in C; Notes: * Note that the znode having no preceding znode on the list of children do not imply that the creator of this znode is aware that it is the current leader. Applications may consider creating a separate znode to acknowledge that the leader has executed the leader procedure. * See the [note for Locks](#sc_recipes_GuidNote) on how to use the guid in the node. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/releasenotes.md0100644 0000000 0000000 00000054252 15051152474 030637 0ustar00rootroot0000000 0000000 # Release Notes - ZooKeeper - Version 3.9.4 ## Breaking changes * [ZOOKEEPER-4891](https://issues.apache.org/jira/browse/ZOOKEEPER-4891) updates `logback-classic` to `1.3.15` to solve cve issues and `slf4j-api` to `2.0.13` to meet compatibilty requirement of logback. This could cause slf4j to complain "No SLF4J providers were found" and output no further logs in certain conditions. 1. For library or client usage, this could happen if you specify and inherit incompatible slf4j and logback versions, say, `slf4j-api:2.0.13` from `org.apache.zookeeper:zookeeper` and `logback-classic:1.2.13` from customized project dependencies. 2. For application or deployment usage, this could happen if you custom and inherit incompatible slf4j and logback versions in classpath, say, `slf4j-api:2.0.13` from zookeeper distribution and `logback-classic:1.2.13` from customization. This could be solved by specifying compatiable slf4j and logback versions in classpath, say, `slf4j-api:2.0.13` and `logback-classic:1.3.15`. ## Bug * [ZOOKEEPER-4020](https://issues.apache.org/jira/browse/ZOOKEEPER-4020) - Memory leak in Zookeeper C Client * [ZOOKEEPER-4240](https://issues.apache.org/jira/browse/ZOOKEEPER-4240) - IPV6 support in ZooKeeper ACL * [ZOOKEEPER-4604](https://issues.apache.org/jira/browse/ZOOKEEPER-4604) - Creating a COMPLETION_STRING_STAT would set acl_result completion * [ZOOKEEPER-4699](https://issues.apache.org/jira/browse/ZOOKEEPER-4699) - zh->hostname heap-use-after-free in zookeeper_interest * [ZOOKEEPER-4725](https://issues.apache.org/jira/browse/ZOOKEEPER-4725) - TTL node creations do not appear in audit log * [ZOOKEEPER-4787](https://issues.apache.org/jira/browse/ZOOKEEPER-4787) - Failed to establish connection between zookeeper * [ZOOKEEPER-4810](https://issues.apache.org/jira/browse/ZOOKEEPER-4810) - Fix data race in format_endpoint_info() * [ZOOKEEPER-4819](https://issues.apache.org/jira/browse/ZOOKEEPER-4819) - Can't seek for writable tls server if connected to readonly server * [ZOOKEEPER-4846](https://issues.apache.org/jira/browse/ZOOKEEPER-4846) - Failure to reload database due to missing ACL * [ZOOKEEPER-4848](https://issues.apache.org/jira/browse/ZOOKEEPER-4848) - Possible stack overflow in setup_random * [ZOOKEEPER-4858](https://issues.apache.org/jira/browse/ZOOKEEPER-4858) - Remove the lock contention between snapshotting and the sync operation * [ZOOKEEPER-4872](https://issues.apache.org/jira/browse/ZOOKEEPER-4872) - SnapshotCommand should not perform fastForwardFromEdits * [ZOOKEEPER-4886](https://issues.apache.org/jira/browse/ZOOKEEPER-4886) - observer with small myid can't join SASL quorum * [ZOOKEEPER-4889](https://issues.apache.org/jira/browse/ZOOKEEPER-4889) - Fallback to DIGEST-MD5 auth mech should be disabled in Fips mode * [ZOOKEEPER-4900](https://issues.apache.org/jira/browse/ZOOKEEPER-4900) - Bump patch release of jetty to include CVE fix for CVE-2024-6763 * [ZOOKEEPER-4907](https://issues.apache.org/jira/browse/ZOOKEEPER-4907) - Shouldn't throw "Len error" when server closing cause confusion * [ZOOKEEPER-4909](https://issues.apache.org/jira/browse/ZOOKEEPER-4909) - When a spurious wakeup occurs, the client’s waiting time may exceed requestTimeout. * [ZOOKEEPER-4919](https://issues.apache.org/jira/browse/ZOOKEEPER-4919) - ResponseCache supposed to be a LRU cache * [ZOOKEEPER-4921](https://issues.apache.org/jira/browse/ZOOKEEPER-4921) - Zookeeper Client 3.9.3 Fails to Reconnect After Network Failures * [ZOOKEEPER-4925](https://issues.apache.org/jira/browse/ZOOKEEPER-4925) - Diff sync introduce hole in stale follower's committedLog which cause data loss in leading * [ZOOKEEPER-4928](https://issues.apache.org/jira/browse/ZOOKEEPER-4928) - Version in zookeeper_version.h is not updated * [ZOOKEEPER-4933](https://issues.apache.org/jira/browse/ZOOKEEPER-4933) - Connection throttle exception causing all connections to be rejected * [ZOOKEEPER-4940](https://issues.apache.org/jira/browse/ZOOKEEPER-4940) - Enabling zookeeper.ssl.ocsp with JRE TLS provider errors out * [ZOOKEEPER-4953](https://issues.apache.org/jira/browse/ZOOKEEPER-4953) - Fixing Typo In ZooKeeper Programmer's Guide * [ZOOKEEPER-4960](https://issues.apache.org/jira/browse/ZOOKEEPER-4960) - Upgrade OWASP plugin to 12.1.3 due to recent parsing errors ## New Feature * [ZOOKEEPER-4895](https://issues.apache.org/jira/browse/ZOOKEEPER-4895) - Introduce a helper function for C client to generate password for SASL authentication ## Improvement * [ZOOKEEPER-4790](https://issues.apache.org/jira/browse/ZOOKEEPER-4790) - TLS Quorum hostname verification breaks in some scenarios * [ZOOKEEPER-4852](https://issues.apache.org/jira/browse/ZOOKEEPER-4852) - Fix the bad "*uuuuu" mark in the ASF license * [ZOOKEEPER-4891](https://issues.apache.org/jira/browse/ZOOKEEPER-4891) - Update logback to 1.3.15 to fix CVE-2024-12798. * [ZOOKEEPER-4902](https://issues.apache.org/jira/browse/ZOOKEEPER-4902) - Document that read-only mode also enables isro 4lw * [ZOOKEEPER-4906](https://issues.apache.org/jira/browse/ZOOKEEPER-4906) - Log full exception details for server JAAS config failure * [ZOOKEEPER-4944](https://issues.apache.org/jira/browse/ZOOKEEPER-4944) - Cache zookeeper dists for end to end compatibility tests * [ZOOKEEPER-4954](https://issues.apache.org/jira/browse/ZOOKEEPER-4954) - Use FIPS style hostname verification when no custom truststore is specified * [ZOOKEEPER-4964](https://issues.apache.org/jira/browse/ZOOKEEPER-4964) - Check permissions individually during admin server auth ## Task * [ZOOKEEPER-4897](https://issues.apache.org/jira/browse/ZOOKEEPER-4897) - Upgrade Netty to fix CVE-2025-24970 in ZooKeeper 3.9.3 * [ZOOKEEPER-4959](https://issues.apache.org/jira/browse/ZOOKEEPER-4959) - Fix license files after logback/slf4j upgrade   # Release Notes - ZooKeeper - Version 3.9.3 ## Bug * [ZOOKEEPER-2332](https://issues.apache.org/jira/browse/ZOOKEEPER-2332) - Zookeeper failed to start for empty txn log * [ZOOKEEPER-2623](https://issues.apache.org/jira/browse/ZOOKEEPER-2623) - CheckVersion outside of Multi causes NullPointerException * [ZOOKEEPER-4293](https://issues.apache.org/jira/browse/ZOOKEEPER-4293) - Lock Contention in ClientCnxnSocketNetty (possible deadlock) * [ZOOKEEPER-4394](https://issues.apache.org/jira/browse/ZOOKEEPER-4394) - Learner.syncWithLeader got NullPointerException * [ZOOKEEPER-4409](https://issues.apache.org/jira/browse/ZOOKEEPER-4409) - NullPointerException in SendAckRequestProcessor * [ZOOKEEPER-4508](https://issues.apache.org/jira/browse/ZOOKEEPER-4508) - ZooKeeper client run to endless loop in ClientCnxn.SendThread.run if all server down * [ZOOKEEPER-4712](https://issues.apache.org/jira/browse/ZOOKEEPER-4712) - Follower.shutdown() and Observer.shutdown() do not correctly shutdown the syncProcessor, which may lead to data inconsistency * [ZOOKEEPER-4733](https://issues.apache.org/jira/browse/ZOOKEEPER-4733) - non-return function error and asan error in CPPUNIT TESTs * [ZOOKEEPER-4752](https://issues.apache.org/jira/browse/ZOOKEEPER-4752) - Remove version files in zookeeper-server/src/main from .gitignore * [ZOOKEEPER-4804](https://issues.apache.org/jira/browse/ZOOKEEPER-4804) - Use daemon threads for Netty client * [ZOOKEEPER-4814](https://issues.apache.org/jira/browse/ZOOKEEPER-4814) - Protocol desynchronization after Connect for (some) old clients * [ZOOKEEPER-4839](https://issues.apache.org/jira/browse/ZOOKEEPER-4839) - When DigestMD5 is used to enable mandatory client authentication,Users that do not exist can log in * [ZOOKEEPER-4843](https://issues.apache.org/jira/browse/ZOOKEEPER-4843) - Encountering an 'Unreasonable Length' error when configuring jute.maxbuffer to 1GB or more * [ZOOKEEPER-4876](https://issues.apache.org/jira/browse/ZOOKEEPER-4876) - jetty-http-9.4.53.v20231009.jar: CVE-2024-6763(3.7) ## New Feature * [ZOOKEEPER-4747](https://issues.apache.org/jira/browse/ZOOKEEPER-4747) - Java api lacks synchronous version of sync() call ## Improvement * [ZOOKEEPER-4850](https://issues.apache.org/jira/browse/ZOOKEEPER-4850) - Enhance zkCli Tool to Support Reading and Writing Binary Data * [ZOOKEEPER-4851](https://issues.apache.org/jira/browse/ZOOKEEPER-4851) - Honor X-Forwarded-For optionally in IPAuthenticationProvider * [ZOOKEEPER-4860](https://issues.apache.org/jira/browse/ZOOKEEPER-4860) - Disable X-Forwarded-For in IPAuthenticationProvider by default ## Test * [ZOOKEEPER-4859](https://issues.apache.org/jira/browse/ZOOKEEPER-4859) - C client tests hang to be cancelled quite often ## Task * [ZOOKEEPER-4820](https://issues.apache.org/jira/browse/ZOOKEEPER-4820) - zookeeper pom leaks logback dependency * [ZOOKEEPER-4868](https://issues.apache.org/jira/browse/ZOOKEEPER-4868) - Bump commons-io library to 2.14.0   # Release Notes - ZooKeeper - Version 3.9.2 ## Sub-task * [ZOOKEEPER-910](https://issues.apache.org/jira/browse/ZOOKEEPER-910) - Use SelectionKey.isXYZ() methods instead of complicated binary logic * [ZOOKEEPER-4728](https://issues.apache.org/jira/browse/ZOOKEEPER-4728) - Zookeepr cannot bind to itself forever if DNS is not ready when startup ## Bug * [ZOOKEEPER-2590](https://issues.apache.org/jira/browse/ZOOKEEPER-2590) - exists() should check read ACL permission * [ZOOKEEPER-4236](https://issues.apache.org/jira/browse/ZOOKEEPER-4236) - Java Client SendThread create many unnecessary Login objects * [ZOOKEEPER-4415](https://issues.apache.org/jira/browse/ZOOKEEPER-4415) - Zookeeper 3.7.0 : The client supported protocol versions [TLSv1.3] are not accepted by server preferences * [ZOOKEEPER-4730](https://issues.apache.org/jira/browse/ZOOKEEPER-4730) - Incorrect datadir and logdir size reported from admin and 4lw dirs command * [ZOOKEEPER-4785](https://issues.apache.org/jira/browse/ZOOKEEPER-4785) - Txn loss due to race condition in Learner.syncWithLeader() during DIFF sync ## Improvement * [ZOOKEEPER-3486](https://issues.apache.org/jira/browse/ZOOKEEPER-3486) - add the doc about how to configure SSL/TLS for the admin server * [ZOOKEEPER-4756](https://issues.apache.org/jira/browse/ZOOKEEPER-4756) - Merge script should use GitHub api to merge pull requests * [ZOOKEEPER-4778](https://issues.apache.org/jira/browse/ZOOKEEPER-4778) - Patch jetty, netty, and logback to remove high severity vulnerabilities * [ZOOKEEPER-4794](https://issues.apache.org/jira/browse/ZOOKEEPER-4794) - Reduce the ZKDatabase#committedLog memory usage * [ZOOKEEPER-4801](https://issues.apache.org/jira/browse/ZOOKEEPER-4801) - Add memory size limitation policy for ZkDataBase#committedLog * [ZOOKEEPER-4799](https://issues.apache.org/jira/browse/ZOOKEEPER-4799) - Refactor ACL check in addWatch command ## Wish * [ZOOKEEPER-4807](https://issues.apache.org/jira/browse/ZOOKEEPER-4807) - Add sid for the leader goodbyte log   # Release Notes - ZooKeeper - Version 3.9.1 ## Improvement * [ZOOKEEPER-4732](https://issues.apache.org/jira/browse/ZOOKEEPER-4732) - improve Reproducible Builds * [ZOOKEEPER-4753](https://issues.apache.org/jira/browse/ZOOKEEPER-4753) - Explicit handling of DIGEST-MD5 vs GSSAPI in quorum auth ## Task * [ZOOKEEPER-4751](https://issues.apache.org/jira/browse/ZOOKEEPER-4751) - Update snappy-java to 1.1.10.5 to address CVE-2023-43642 * [ZOOKEEPER-4754](https://issues.apache.org/jira/browse/ZOOKEEPER-4754) - Update Jetty to avoid CVE-2023-36479, CVE-2023-40167, and CVE-2023-41900 * [ZOOKEEPER-4755](https://issues.apache.org/jira/browse/ZOOKEEPER-4755) - Handle Netty CVE-2023-4586   # Release Notes - ZooKeeper - Version 3.9.0 ## Sub-task * [ZOOKEEPER-4327](https://issues.apache.org/jira/browse/ZOOKEEPER-4327) - Flaky test: RequestThrottlerTest ## Bug * [ZOOKEEPER-2108](https://issues.apache.org/jira/browse/ZOOKEEPER-2108) - Compilation error in ZkAdaptor.cc with GCC 4.7 or later * [ZOOKEEPER-3652](https://issues.apache.org/jira/browse/ZOOKEEPER-3652) - Improper synchronization in ClientCnxn * [ZOOKEEPER-3908](https://issues.apache.org/jira/browse/ZOOKEEPER-3908) - zktreeutil multiple issues * [ZOOKEEPER-3996](https://issues.apache.org/jira/browse/ZOOKEEPER-3996) - Flaky test: ReadOnlyModeTest.testConnectionEvents * [ZOOKEEPER-4026](https://issues.apache.org/jira/browse/ZOOKEEPER-4026) - CREATE2 requests embeded in a MULTI request only get a regular CREATE response * [ZOOKEEPER-4296](https://issues.apache.org/jira/browse/ZOOKEEPER-4296) - NullPointerException when ClientCnxnSocketNetty is closed without being opened * [ZOOKEEPER-4308](https://issues.apache.org/jira/browse/ZOOKEEPER-4308) - Flaky test: EagerACLFilterTest.testSetDataFail * [ZOOKEEPER-4393](https://issues.apache.org/jira/browse/ZOOKEEPER-4393) - Problem to connect to zookeeper in FIPS mode * [ZOOKEEPER-4466](https://issues.apache.org/jira/browse/ZOOKEEPER-4466) - Support different watch modes on same path * [ZOOKEEPER-4471](https://issues.apache.org/jira/browse/ZOOKEEPER-4471) - Remove WatcherType.Children break persistent watcher's child events * [ZOOKEEPER-4473](https://issues.apache.org/jira/browse/ZOOKEEPER-4473) - zooInspector create root node fail with path validate * [ZOOKEEPER-4475](https://issues.apache.org/jira/browse/ZOOKEEPER-4475) - Persistent recursive watcher got NodeChildrenChanged event * [ZOOKEEPER-4477](https://issues.apache.org/jira/browse/ZOOKEEPER-4477) - Single Kerberos ticket renewal failure can prevent all future renewals since Java 9 * [ZOOKEEPER-4504](https://issues.apache.org/jira/browse/ZOOKEEPER-4504) - ZKUtil#deleteRecursive causing deadlock in HDFS HA functionality * [ZOOKEEPER-4505](https://issues.apache.org/jira/browse/ZOOKEEPER-4505) - CVE-2020-36518 - Upgrade jackson databind to 2.13.2.1 * [ZOOKEEPER-4511](https://issues.apache.org/jira/browse/ZOOKEEPER-4511) - Flaky test: FileTxnSnapLogMetricsTest.testFileTxnSnapLogMetrics * [ZOOKEEPER-4514](https://issues.apache.org/jira/browse/ZOOKEEPER-4514) - ClientCnxnSocketNetty throwing NPE * [ZOOKEEPER-4515](https://issues.apache.org/jira/browse/ZOOKEEPER-4515) - ZK Cli quit command always logs error * [ZOOKEEPER-4537](https://issues.apache.org/jira/browse/ZOOKEEPER-4537) - Race between SyncThread and CommitProcessor thread * [ZOOKEEPER-4549](https://issues.apache.org/jira/browse/ZOOKEEPER-4549) - ProviderRegistry may be repeatedly initialized * [ZOOKEEPER-4565](https://issues.apache.org/jira/browse/ZOOKEEPER-4565) - Config watch path get truncated abnormally and fail chroot zookeeper client * [ZOOKEEPER-4647](https://issues.apache.org/jira/browse/ZOOKEEPER-4647) - Tests don't pass on JDK20 because we try to mock InetAddress * [ZOOKEEPER-4654](https://issues.apache.org/jira/browse/ZOOKEEPER-4654) - Fix C client test compilation error in Util.cc. * [ZOOKEEPER-4674](https://issues.apache.org/jira/browse/ZOOKEEPER-4674) - C client tests don't pass on CI * [ZOOKEEPER-4719](https://issues.apache.org/jira/browse/ZOOKEEPER-4719) - Use bouncycastle jdk18on instead of jdk15on * [ZOOKEEPER-4721](https://issues.apache.org/jira/browse/ZOOKEEPER-4721) - Upgrade OWASP Dependency Check to 8.3.1 ## New Feature * [ZOOKEEPER-4570](https://issues.apache.org/jira/browse/ZOOKEEPER-4570) - Admin server API for taking snapshot and stream out the data * [ZOOKEEPER-4655](https://issues.apache.org/jira/browse/ZOOKEEPER-4655) - Communicate the Zxid that triggered a WatchEvent to fire ## Improvement * [ZOOKEEPER-3731](https://issues.apache.org/jira/browse/ZOOKEEPER-3731) - Disable HTTP TRACE Method * [ZOOKEEPER-3806](https://issues.apache.org/jira/browse/ZOOKEEPER-3806) - TLS - dynamic loading for client trust/key store * [ZOOKEEPER-3860](https://issues.apache.org/jira/browse/ZOOKEEPER-3860) - Avoid reverse DNS lookup for hostname verification when hostnames are provided in the connection url * [ZOOKEEPER-4289](https://issues.apache.org/jira/browse/ZOOKEEPER-4289) - Reduce the performance impact of Prometheus metrics * [ZOOKEEPER-4303](https://issues.apache.org/jira/browse/ZOOKEEPER-4303) - ZooKeeperServerEmbedded could auto-assign and expose ports * [ZOOKEEPER-4464](https://issues.apache.org/jira/browse/ZOOKEEPER-4464) - zooinspector display "Ephemeral Owner" in hex for easy match to jmx session * [ZOOKEEPER-4467](https://issues.apache.org/jira/browse/ZOOKEEPER-4467) - Missing op code (addWatch) in Request.op2String * [ZOOKEEPER-4472](https://issues.apache.org/jira/browse/ZOOKEEPER-4472) - Support persistent watchers removing individually * [ZOOKEEPER-4474](https://issues.apache.org/jira/browse/ZOOKEEPER-4474) - ZooDefs.opNames is unused * [ZOOKEEPER-4490](https://issues.apache.org/jira/browse/ZOOKEEPER-4490) - Publish Clover results to SonarQube * [ZOOKEEPER-4491](https://issues.apache.org/jira/browse/ZOOKEEPER-4491) - Adding SSL support to Zktreeutil * [ZOOKEEPER-4492](https://issues.apache.org/jira/browse/ZOOKEEPER-4492) - Merge readOnly field into ConnectRequest and Response * [ZOOKEEPER-4494](https://issues.apache.org/jira/browse/ZOOKEEPER-4494) - Fix error message format * [ZOOKEEPER-4518](https://issues.apache.org/jira/browse/ZOOKEEPER-4518) - remove useless log in the PrepRequestProcessor#pRequest method * [ZOOKEEPER-4519](https://issues.apache.org/jira/browse/ZOOKEEPER-4519) - Testable interface should have a testableCloseSocket() method * [ZOOKEEPER-4529](https://issues.apache.org/jira/browse/ZOOKEEPER-4529) - Upgrade netty to 4.1.76.Final * [ZOOKEEPER-4531](https://issues.apache.org/jira/browse/ZOOKEEPER-4531) - Revert Netty TCNative change * [ZOOKEEPER-4551](https://issues.apache.org/jira/browse/ZOOKEEPER-4551) - Do not log spammy stacktrace when a client closes its connection * [ZOOKEEPER-4566](https://issues.apache.org/jira/browse/ZOOKEEPER-4566) - Create tool for recursive snapshot analysis * [ZOOKEEPER-4573](https://issues.apache.org/jira/browse/ZOOKEEPER-4573) - Encapsulate request bytebuffer in Request * [ZOOKEEPER-4575](https://issues.apache.org/jira/browse/ZOOKEEPER-4575) - ZooKeeperServer#processPacket take record instead of bytes * [ZOOKEEPER-4616](https://issues.apache.org/jira/browse/ZOOKEEPER-4616) - Upgrade docker image for the dev enviroment to resolve CVEs * [ZOOKEEPER-4622](https://issues.apache.org/jira/browse/ZOOKEEPER-4622) - Add Netty-TcNative OpenSSL Support * [ZOOKEEPER-4636](https://issues.apache.org/jira/browse/ZOOKEEPER-4636) - Fix zkServer.sh for AIX * [ZOOKEEPER-4657](https://issues.apache.org/jira/browse/ZOOKEEPER-4657) - Publish SBOM artifacts * [ZOOKEEPER-4659](https://issues.apache.org/jira/browse/ZOOKEEPER-4659) - Upgrade Commons CLI to 1.5.0 due to OWASP failing on 1.4 CVE-2021-37533 * [ZOOKEEPER-4660](https://issues.apache.org/jira/browse/ZOOKEEPER-4660) - Suppress false positive OWASP failure for CVE-2021-37533 * [ZOOKEEPER-4661](https://issues.apache.org/jira/browse/ZOOKEEPER-4661) - Upgrade Jackson Databind to 2.13.4.2 for CVE-2022-42003 CVE-2022-42004 * [ZOOKEEPER-4705](https://issues.apache.org/jira/browse/ZOOKEEPER-4705) - Restrict GitHub merge button to allow squash commit only * [ZOOKEEPER-4717](https://issues.apache.org/jira/browse/ZOOKEEPER-4717) - Cache serialize data in the request to avoid repeat serialize. * [ZOOKEEPER-4718](https://issues.apache.org/jira/browse/ZOOKEEPER-4718) - Removing unnecessary heap memory allocation in serialization can help reduce GC pressure. ## Test * [ZOOKEEPER-4630](https://issues.apache.org/jira/browse/ZOOKEEPER-4630) - Fix the NPE from ConnectionMetricsTest.testRevalidateCount * [ZOOKEEPER-4676](https://issues.apache.org/jira/browse/ZOOKEEPER-4676) - ReadOnlyModeTest doesn't not compile on JDK20 (Thread.suspend has been removed) ## Wish * [ZOOKEEPER-3615](https://issues.apache.org/jira/browse/ZOOKEEPER-3615) - write a TLA+ specification to verify Zab protocol * [ZOOKEEPER-4710](https://issues.apache.org/jira/browse/ZOOKEEPER-4710) - Fix ZkUtil deleteInBatch() by releasing semaphore after set flag * [ZOOKEEPER-4714](https://issues.apache.org/jira/browse/ZOOKEEPER-4714) - Improve syncRequestProcessor performance * [ZOOKEEPER-4715](https://issues.apache.org/jira/browse/ZOOKEEPER-4715) - Verify file size and position in testGetCurrentLogSize. ## Task * [ZOOKEEPER-4479](https://issues.apache.org/jira/browse/ZOOKEEPER-4479) - Tests: C client test TestOperations.cc testTimeoutCausedByWatches1 is very flaky on CI * [ZOOKEEPER-4482](https://issues.apache.org/jira/browse/ZOOKEEPER-4482) - Fix LICENSE FILES for commons-io and commons-cli * [ZOOKEEPER-4599](https://issues.apache.org/jira/browse/ZOOKEEPER-4599) - Upgrade Jetty to avoid CVE-2022-2048 * [ZOOKEEPER-4641](https://issues.apache.org/jira/browse/ZOOKEEPER-4641) - GH CI fails with error: implicit declaration of function FIPS_mode * [ZOOKEEPER-4642](https://issues.apache.org/jira/browse/ZOOKEEPER-4642) - Remove Travis CI * [ZOOKEEPER-4649](https://issues.apache.org/jira/browse/ZOOKEEPER-4649) - Upgrade netty to 4.1.86 because of CVE-2022-41915 * [ZOOKEEPER-4669](https://issues.apache.org/jira/browse/ZOOKEEPER-4669) - Upgrade snappy-java to 1.1.9.1 (in order to support M1 macs) * [ZOOKEEPER-4688](https://issues.apache.org/jira/browse/ZOOKEEPER-4688) - Upgrade `cyclonedx-maven-plugin` to 2.7.6 * [ZOOKEEPER-4700](https://issues.apache.org/jira/browse/ZOOKEEPER-4700) - Update Jetty for fixing CVE-2023-26048 and CVE-2023-26049 * [ZOOKEEPER-4707](https://issues.apache.org/jira/browse/ZOOKEEPER-4707) - Update snappy-java to address multiple CVEs * [ZOOKEEPER-4709](https://issues.apache.org/jira/browse/ZOOKEEPER-4709) - Upgrade Netty to 4.1.94.Final * [ZOOKEEPER-4716](https://issues.apache.org/jira/browse/ZOOKEEPER-4716) - Upgrade jackson to 2.15.2, suppress two false positive CVE errors   apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/basic.css0100644 0000000 0000000 00000005661 15051152474 030363 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * General */ img { border: 0; } #content table { border: 0; width: 100%; } /*Hack to get IE to render the table at 100%*/ * html #content table { margin-left: -3px; } #content th, #content td { margin: 0; padding: 0; vertical-align: top; } .clearboth { clear: both; } .note, .warning, .fixme { clear:right; border: solid black 1px; margin: 1em 3em; } .note .label { background: #369; color: white; font-weight: bold; padding: 5px 10px; } .note .content { background: #F0F0FF; color: black; line-height: 120%; font-size: 90%; padding: 5px 10px; } .warning .label { background: #C00; color: white; font-weight: bold; padding: 5px 10px; } .warning .content { background: #FFF0F0; color: black; line-height: 120%; font-size: 90%; padding: 5px 10px; } .fixme .label { background: #C6C600; color: black; font-weight: bold; padding: 5px 10px; } .fixme .content { padding: 5px 10px; } /** * Typography */ body { font-family: verdana, "Trebuchet MS", arial, helvetica, sans-serif; font-size: 100%; } #content { font-family: Georgia, Palatino, Times, serif; font-size: 95%; } #tabs { font-size: 70%; } #menu { font-size: 80%; } #footer { font-size: 70%; } h1, h2, h3, h4, h5, h6 { font-family: "Trebuchet MS", verdana, arial, helvetica, sans-serif; font-weight: bold; margin-top: 1em; margin-bottom: .5em; } h1 { margin-top: 0; margin-bottom: 1em; font-size: 1.4em; } #content h1 { font-size: 160%; margin-bottom: .5em; } #menu h1 { margin: 0; padding: 10px; background: #336699; color: white; } h2 { font-size: 120%; } h3 { font-size: 100%; } h4 { font-size: 90%; } h5 { font-size: 80%; } h6 { font-size: 75%; } p { line-height: 120%; text-align: left; margin-top: .5em; margin-bottom: 1em; } #content li, #content th, #content td, #content li ul, #content li ol{ margin-top: .5em; margin-bottom: .5em; } #content li li, #minitoc-area li{ margin-top: 0em; margin-bottom: 0em; } #content .attribution { text-align: right; font-style: italic; font-size: 85%; margin-top: 1em; } .codefrag { font-family: "Courier New", Courier, monospace; font-size: 110%; }apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/chapter.gif0100644 0000000 0000000 00000000061 15051152474 030672 0ustar00rootroot0000000 0000000 GIF89a€¥¶Æÿÿÿ!ù,Diì«T(;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/chapter_open.gif0100644 0000000 0000000 00000000061 15051152474 031713 0ustar00rootroot0000000 0000000 GIF89a€¥¶Æÿÿÿ!ù,„j Y(;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/current.gif0100644 0000000 0000000 00000000066 15051152474 030733 0ustar00rootroot0000000 0000000 GIF89a€¥¶Æÿÿÿ!ù, „¡k`›‰ÒÅ*;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/getBlank.js0100644 0000000 0000000 00000003105 15051152474 030644 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * getBlank script - when included in a html file and called from a form text field, will set the value of this field to "" * if the text value is still the standard value. * getPrompt script - when included in a html file and called from a form text field, will set the value of this field to the prompt * if the text value is empty. * * Typical usage: * * */ apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/getMenu.js0100644 0000000 0000000 00000003156 15051152474 030527 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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, thePath) { var open = 'url("'+thePath + 'chapter_open.gif")'; var close = 'url("'+thePath + '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) apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/header_white_line.gif0100644 0000000 0000000 00000000045 15051152474 032705 0ustar00rootroot0000000 0000000 GIF87a€ÿÿÿÂÐâ,„™;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/init.js0100644 0000000 0000000 00000004415 15051152474 030065 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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: * */ function getFileName(url){ var fileName = url.substring(url.lastIndexOf('/')+1); return fileName; } function init(){ var url = window .location.pathname; var fileName = getFileName(url); var menuItemGroup = document.getElementById("menu").children; for (i = 0; i < menuItemGroup.length; i++) { if("menutitle" === menuItemGroup[i].className){ continue; } var menuItem = menuItemGroup[i].children; if(menuItem.length>0){ for (j = 0; j < menuItem.length; j++) { if(menuItem[j].firstElementChild != null){ var linkItem = menuItem[j].firstElementChild; if('a' === linkItem.localName){ var linkFile = getFileName(linkItem.href); if(fileName === linkFile && linkItem.href.lastIndexOf("apidocs/zookeeper-server/index.html")<0){ linkItem.className = "selected"; linkItem.parentNode.parentNode.className = "selectedmenuitemgroup"; var title = document.getElementById(linkItem.parentNode.parentNode.id+"Title"); title.className="menutitle selected"; } } } } } } } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/instruction_arrow.png0100644 0000000 0000000 00000000435 15051152474 033063 0ustar00rootroot0000000 0000000 ‰PNG  IHDR Ý"õtRNSøüøZì— pHYs  šœ½IDAT×côðòذi)€éÙÓg_¾|Á%]_[ÿùóg4AFeeqqñ”´555L=î.îÂÂÂS§OuuwEˆ*«(v Sxdø£§~üùñãÏ¢ô@€°°ðœys~üùÁ ©©IRÄÅÇ1$¥$\LLQ1QÏ_=g¸z㪘¸A ›¶n‚ø‡ñÇŸ/ž¿Ø°~çOŸ0•¶6·þùó§ ¨ º¦š‹› * ÑŠ YZY9qM"útzu—êsIEND®B`‚apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/menu.js0100644 0000000 0000000 00000003223 15051152474 030062 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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) apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/page.gif0100644 0000000 0000000 00000000117 15051152474 030162 0ustar00rootroot0000000 0000000 GIF89a€¥¶Æÿÿÿ!þCreated with The GIMP!ù , ŒyÀ€ 26…S;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/print.css0100644 0000000 0000000 00000002325 15051152474 030430 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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; }apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/printer.gif0100644 0000000 0000000 00000001133 15051152474 030730 0ustar00rootroot0000000 0000000 GIF89aÆïï÷ï÷÷ççç½ÇƵÇïµÏï½ÏÞkicÖÏÎÖç÷Þç÷ÞïÿÆ×÷½×÷½×ïµ×çÞßÞcacÖ×ÖÆß÷Îß÷­Ç÷Œ®çsžçs–Ös–ε¾ÆkikÖßïÎ×ïÎßïÆ×祶ޔ®Þœ¶ÞÆÇÆÎÏÎïïïÎÏÆÎÇÆÞßÖççÞ½¾½µ¶µ­®­ZYZRQR{y{sqsµ¾µ”ŽŒççïÖßçÞßçÞçÿÎ×÷Ö×µÿ†cÖ¶9œ¶÷œ¶ç”–”½Ï÷µÇ÷µ¶kïƦ­¾ïÞßï989çç÷çïÿ÷÷ÿœžœ! !JIJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!ù ,¸€„…†‚Ž ™š  §¬ !"ºº!#$%%$##&ÆÆ'()&#*+ÄÒÒ,$ÍÖ-ÛÜ-.*#×á$/0æçæ1áØ*234ð55#,á/*,6789:vð¨Á¢‡‹V@óñH!†YQ$Ñ#Œ(°AÃÆHz$±¨ƆR.a²áÅ A;apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/profile.css0100644 0000000 0000000 00000007533 15051152474 030742 0ustar00rootroot0000000 0000000 /* ==================== 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 } } #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 a.selected { background-color: #CFDCED; border-color: #999999; color: #000000;} #menu a.selected:visited { color: #000000;} #menu { 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;} #footer { background-color: #E5E4D9;} /* extra-css */ p.quote { margin-left: 2em; padding: .5em; background-color: #f0f0f0; font-family: monospace; } pre { margin-left: 0em; padding: 0.5em; background-color: #f0f0f0; font-family: monospace; } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/prototype.js0100644 0000000 0000000 00000606173 15051152474 031200 0ustar00rootroot0000000 0000000 /* Prototype JavaScript framework, version 1.7.3 * (c) 2005-2010 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ * *--------------------------------------------------------------------------*/ var Prototype = { Version: '1.7.3', Browser: (function(){ var ua = navigator.userAgent; var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; return { IE: !!window.attachEvent && !isOpera, Opera: isOpera, WebKit: ua.indexOf('AppleWebKit/') > -1, Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, MobileSafari: /Apple.*Mobile/.test(ua) } })(), BrowserFeatures: { XPath: !!document.evaluate, SelectorsAPI: !!document.querySelector, ElementExtensions: (function() { var constructor = window.Element || window.HTMLElement; return !!(constructor && constructor.prototype); })(), SpecificElementExtensions: (function() { if (typeof window.HTMLDivElement !== 'undefined') return true; var div = document.createElement('div'), form = document.createElement('form'), isSupported = false; if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { isSupported = true; } div = form = null; return isSupported; })() }, ScriptFragment: ']*>([\\S\\s]*?)<\/script\\s*>', JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, emptyFunction: function() { }, K: function(x) { return x } }; if (Prototype.Browser.MobileSafari) Prototype.BrowserFeatures.SpecificElementExtensions = false; /* Based on Alex Arnell's inheritance implementation. */ var Class = (function() { var IS_DONTENUM_BUGGY = (function(){ for (var p in { toString: 1 }) { if (p === 'toString') return false; } return true; })(); function subclass() {}; function create() { var parent = null, properties = $A(arguments); if (Object.isFunction(properties[0])) parent = properties.shift(); function klass() { this.initialize.apply(this, arguments); } Object.extend(klass, Class.Methods); klass.superclass = parent; klass.subclasses = []; if (parent) { subclass.prototype = parent.prototype; klass.prototype = new subclass; parent.subclasses.push(klass); } for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); if (!klass.prototype.initialize) klass.prototype.initialize = Prototype.emptyFunction; klass.prototype.constructor = klass; return klass; } function addMethods(source) { var ancestor = this.superclass && this.superclass.prototype, properties = Object.keys(source); if (IS_DONTENUM_BUGGY) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) properties.push("valueOf"); } for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && value.argumentNames()[0] == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); value.valueOf = (function(method) { return function() { return method.valueOf.call(method); }; })(method); value.toString = (function(method) { return function() { return method.toString.call(method); }; })(method); } this.prototype[property] = value; } return this; } return { create: create, Methods: { addMethods: addMethods } }; })(); (function() { var _toString = Object.prototype.toString, _hasOwnProperty = Object.prototype.hasOwnProperty, NULL_TYPE = 'Null', UNDEFINED_TYPE = 'Undefined', BOOLEAN_TYPE = 'Boolean', NUMBER_TYPE = 'Number', STRING_TYPE = 'String', OBJECT_TYPE = 'Object', FUNCTION_CLASS = '[object Function]', BOOLEAN_CLASS = '[object Boolean]', NUMBER_CLASS = '[object Number]', STRING_CLASS = '[object String]', ARRAY_CLASS = '[object Array]', DATE_CLASS = '[object Date]', NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && typeof JSON.stringify === 'function' && JSON.stringify(0) === '0' && typeof JSON.stringify(Prototype.K) === 'undefined'; var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; var IS_DONTENUM_BUGGY = (function(){ for (var p in { toString: 1 }) { if (p === 'toString') return false; } return true; })(); function Type(o) { switch(o) { case null: return NULL_TYPE; case (void 0): return UNDEFINED_TYPE; } var type = typeof o; switch(type) { case 'boolean': return BOOLEAN_TYPE; case 'number': return NUMBER_TYPE; case 'string': return STRING_TYPE; } return OBJECT_TYPE; } function extend(destination, source) { for (var property in source) destination[property] = source[property]; return destination; } function inspect(object) { try { if (isUndefined(object)) return 'undefined'; if (object === null) return 'null'; return object.inspect ? object.inspect() : String(object); } catch (e) { if (e instanceof RangeError) return '...'; throw e; } } function toJSON(value) { return Str('', { '': value }, []); } function Str(key, holder, stack) { var value = holder[key]; if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { value = value.toJSON(key); } var _class = _toString.call(value); switch (_class) { case NUMBER_CLASS: case BOOLEAN_CLASS: case STRING_CLASS: value = value.valueOf(); } switch (value) { case null: return 'null'; case true: return 'true'; case false: return 'false'; } var type = typeof value; switch (type) { case 'string': return value.inspect(true); case 'number': return isFinite(value) ? String(value) : 'null'; case 'object': for (var i = 0, length = stack.length; i < length; i++) { if (stack[i] === value) { throw new TypeError("Cyclic reference to '" + value + "' in object"); } } stack.push(value); var partial = []; if (_class === ARRAY_CLASS) { for (var i = 0, length = value.length; i < length; i++) { var str = Str(i, value, stack); partial.push(typeof str === 'undefined' ? 'null' : str); } partial = '[' + partial.join(',') + ']'; } else { var keys = Object.keys(value); for (var i = 0, length = keys.length; i < length; i++) { var key = keys[i], str = Str(key, value, stack); if (typeof str !== "undefined") { partial.push(key.inspect(true)+ ':' + str); } } partial = '{' + partial.join(',') + '}'; } stack.pop(); return partial; } } function stringify(object) { return JSON.stringify(object); } function toQueryString(object) { return $H(object).toQueryString(); } function toHTML(object) { return object && object.toHTML ? object.toHTML() : String.interpret(object); } function keys(object) { if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } var results = []; for (var property in object) { if (_hasOwnProperty.call(object, property)) results.push(property); } if (IS_DONTENUM_BUGGY) { for (var i = 0; property = DONT_ENUMS[i]; i++) { if (_hasOwnProperty.call(object, property)) results.push(property); } } return results; } function values(object) { var results = []; for (var property in object) results.push(object[property]); return results; } function clone(object) { return extend({ }, object); } function isElement(object) { return !!(object && object.nodeType == 1); } function isArray(object) { return _toString.call(object) === ARRAY_CLASS; } var hasNativeIsArray = (typeof Array.isArray == 'function') && Array.isArray([]) && !Array.isArray({}); if (hasNativeIsArray) { isArray = Array.isArray; } function isHash(object) { return object instanceof Hash; } function isFunction(object) { return _toString.call(object) === FUNCTION_CLASS; } function isString(object) { return _toString.call(object) === STRING_CLASS; } function isNumber(object) { return _toString.call(object) === NUMBER_CLASS; } function isDate(object) { return _toString.call(object) === DATE_CLASS; } function isUndefined(object) { return typeof object === "undefined"; } extend(Object, { extend: extend, inspect: inspect, toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, toQueryString: toQueryString, toHTML: toHTML, keys: Object.keys || keys, values: values, clone: clone, isElement: isElement, isArray: isArray, isHash: isHash, isFunction: isFunction, isString: isString, isNumber: isNumber, isDate: isDate, isUndefined: isUndefined }); })(); Object.extend(Function.prototype, (function() { var slice = Array.prototype.slice; function update(array, args) { var arrayLength = array.length, length = args.length; while (length--) array[arrayLength + length] = args[length]; return array; } function merge(array, args) { array = slice.call(array, 0); return update(array, args); } function argumentNames() { var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') .replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; } function bind(context) { if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; if (!Object.isFunction(this)) throw new TypeError("The object is not callable."); var nop = function() {}; var __method = this, args = slice.call(arguments, 1); var bound = function() { var a = merge(args, arguments); var c = this instanceof bound ? this : context; return __method.apply(c, a); }; nop.prototype = this.prototype; bound.prototype = new nop(); return bound; } function bindAsEventListener(context) { var __method = this, args = slice.call(arguments, 1); return function(event) { var a = update([event || window.event], args); return __method.apply(context, a); } } function curry() { if (!arguments.length) return this; var __method = this, args = slice.call(arguments, 0); return function() { var a = merge(args, arguments); return __method.apply(this, a); } } function delay(timeout) { var __method = this, args = slice.call(arguments, 1); timeout = timeout * 1000; return window.setTimeout(function() { return __method.apply(__method, args); }, timeout); } function defer() { var args = update([0.01], arguments); return this.delay.apply(this, args); } function wrap(wrapper) { var __method = this; return function() { var a = update([__method.bind(this)], arguments); return wrapper.apply(this, a); } } function methodize() { if (this._methodized) return this._methodized; var __method = this; return this._methodized = function() { var a = update([this], arguments); return __method.apply(null, a); }; } var extensions = { argumentNames: argumentNames, bindAsEventListener: bindAsEventListener, curry: curry, delay: delay, defer: defer, wrap: wrap, methodize: methodize }; if (!Function.prototype.bind) extensions.bind = bind; return extensions; })()); (function(proto) { function toISOString() { return this.getUTCFullYear() + '-' + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + this.getUTCDate().toPaddedString(2) + 'T' + this.getUTCHours().toPaddedString(2) + ':' + this.getUTCMinutes().toPaddedString(2) + ':' + this.getUTCSeconds().toPaddedString(2) + 'Z'; } function toJSON() { return this.toISOString(); } if (!proto.toISOString) proto.toISOString = toISOString; if (!proto.toJSON) proto.toJSON = toJSON; })(Date.prototype); RegExp.prototype.match = RegExp.prototype.test; RegExp.escape = function(str) { return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); }; var PeriodicalExecuter = Class.create({ initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false; this.registerCallback(); }, registerCallback: function() { this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, execute: function() { this.callback(this); }, stop: function() { if (!this.timer) return; clearInterval(this.timer); this.timer = null; }, onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; this.execute(); this.currentlyExecuting = false; } catch(e) { this.currentlyExecuting = false; throw e; } } } }); Object.extend(String, { interpret: function(value) { return value == null ? '' : String(value); }, specialChar: { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\\': '\\\\' } }); Object.extend(String.prototype, (function() { var NATIVE_JSON_PARSE_SUPPORT = window.JSON && typeof JSON.parse === 'function' && JSON.parse('{"test": true}').test; function prepareReplacement(replacement) { if (Object.isFunction(replacement)) return replacement; var template = new Template(replacement); return function(match) { return template.evaluate(match) }; } function isNonEmptyRegExp(regexp) { return regexp.source && regexp.source !== '(?:)'; } function gsub(pattern, replacement) { var result = '', source = this, match; replacement = prepareReplacement(replacement); if (Object.isString(pattern)) pattern = RegExp.escape(pattern); if (!(pattern.length || isNonEmptyRegExp(pattern))) { replacement = replacement(''); return replacement + source.split('').join(replacement) + replacement; } while (source.length > 0) { match = source.match(pattern) if (match && match[0].length > 0) { result += source.slice(0, match.index); result += String.interpret(replacement(match)); source = source.slice(match.index + match[0].length); } else { result += source, source = ''; } } return result; } function sub(pattern, replacement, count) { replacement = prepareReplacement(replacement); count = Object.isUndefined(count) ? 1 : count; return this.gsub(pattern, function(match) { if (--count < 0) return match[0]; return replacement(match); }); } function scan(pattern, iterator) { this.gsub(pattern, iterator); return String(this); } function truncate(length, truncation) { length = length || 30; truncation = Object.isUndefined(truncation) ? '...' : truncation; return this.length > length ? this.slice(0, length - truncation.length) + truncation : String(this); } function strip() { return this.replace(/^\s+/, '').replace(/\s+$/, ''); } function stripTags() { return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, ''); } function stripScripts() { return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); } function extractScripts() { var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), matchOne = new RegExp(Prototype.ScriptFragment, 'im'); return (this.match(matchAll) || []).map(function(scriptTag) { return (scriptTag.match(matchOne) || ['', ''])[1]; }); } function evalScripts() { return this.extractScripts().map(function(script) { return eval(script); }); } function escapeHTML() { return this.replace(/&/g,'&').replace(//g,'>'); } function unescapeHTML() { return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); } function toQueryParams(separator) { var match = this.strip().match(/([^?#]*)(#.*)?$/); if (!match) return { }; return match[1].split(separator || '&').inject({ }, function(hash, pair) { if ((pair = pair.split('='))[0]) { var key = decodeURIComponent(pair.shift()), value = pair.length > 1 ? pair.join('=') : pair[0]; if (value != undefined) { value = value.gsub('+', ' '); value = decodeURIComponent(value); } if (key in hash) { if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; hash[key].push(value); } else hash[key] = value; } return hash; }); } function toArray() { return this.split(''); } function succ() { return this.slice(0, this.length - 1) + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); } function times(count) { return count < 1 ? '' : new Array(count + 1).join(this); } function camelize() { return this.replace(/-+(.)?/g, function(match, chr) { return chr ? chr.toUpperCase() : ''; }); } function capitalize() { return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); } function underscore() { return this.replace(/::/g, '/') .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') .replace(/([a-z\d])([A-Z])/g, '$1_$2') .replace(/-/g, '_') .toLowerCase(); } function dasherize() { return this.replace(/_/g, '-'); } function inspect(useDoubleQuotes) { var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { if (character in String.specialChar) { return String.specialChar[character]; } return '\\u00' + character.charCodeAt().toPaddedString(2, 16); }); if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; return "'" + escapedString.replace(/'/g, '\\\'') + "'"; } function unfilterJSON(filter) { return this.replace(filter || Prototype.JSONFilter, '$1'); } function isJSON() { var str = this; if (str.blank()) return false; str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); return (/^[\],:{}\s]*$/).test(str); } function evalJSON(sanitize) { var json = this.unfilterJSON(), cx = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g; if (cx.test(json)) { json = json.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } try { if (!sanitize || json.isJSON()) return eval('(' + json + ')'); } catch (e) { } throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); } function parseJSON() { var json = this.unfilterJSON(); return JSON.parse(json); } function include(pattern) { return this.indexOf(pattern) > -1; } function startsWith(pattern, position) { position = Object.isNumber(position) ? position : 0; return this.lastIndexOf(pattern, position) === position; } function endsWith(pattern, position) { pattern = String(pattern); position = Object.isNumber(position) ? position : this.length; if (position < 0) position = 0; if (position > this.length) position = this.length; var d = position - pattern.length; return d >= 0 && this.indexOf(pattern, d) === d; } function empty() { return this == ''; } function blank() { return /^\s*$/.test(this); } function interpolate(object, pattern) { return new Template(this, pattern).evaluate(object); } return { gsub: gsub, sub: sub, scan: scan, truncate: truncate, strip: String.prototype.trim || strip, stripTags: stripTags, stripScripts: stripScripts, extractScripts: extractScripts, evalScripts: evalScripts, escapeHTML: escapeHTML, unescapeHTML: unescapeHTML, toQueryParams: toQueryParams, parseQuery: toQueryParams, toArray: toArray, succ: succ, times: times, camelize: camelize, capitalize: capitalize, underscore: underscore, dasherize: dasherize, inspect: inspect, unfilterJSON: unfilterJSON, isJSON: isJSON, evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, include: include, startsWith: String.prototype.startsWith || startsWith, endsWith: String.prototype.endsWith || endsWith, empty: empty, blank: blank, interpolate: interpolate }; })()); var Template = Class.create({ initialize: function(template, pattern) { this.template = template.toString(); this.pattern = pattern || Template.Pattern; }, evaluate: function(object) { if (object && Object.isFunction(object.toTemplateReplacements)) object = object.toTemplateReplacements(); return this.template.gsub(this.pattern, function(match) { if (object == null) return (match[1] + ''); var before = match[1] || ''; if (before == '\\') return match[2]; var ctx = object, expr = match[3], pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; match = pattern.exec(expr); if (match == null) return before; while (match != null) { var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; ctx = ctx[comp]; if (null == ctx || '' == match[3]) break; expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); match = pattern.exec(expr); } return before + String.interpret(ctx); }); } }); Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; var $break = { }; var Enumerable = (function() { function each(iterator, context) { try { this._each(iterator, context); } catch (e) { if (e != $break) throw e; } return this; } function eachSlice(number, iterator, context) { var index = -number, slices = [], array = this.toArray(); if (number < 1) return array; while ((index += number) < array.length) slices.push(array.slice(index, index+number)); return slices.collect(iterator, context); } function all(iterator, context) { iterator = iterator || Prototype.K; var result = true; this.each(function(value, index) { result = result && !!iterator.call(context, value, index, this); if (!result) throw $break; }, this); return result; } function any(iterator, context) { iterator = iterator || Prototype.K; var result = false; this.each(function(value, index) { if (result = !!iterator.call(context, value, index, this)) throw $break; }, this); return result; } function collect(iterator, context) { iterator = iterator || Prototype.K; var results = []; this.each(function(value, index) { results.push(iterator.call(context, value, index, this)); }, this); return results; } function detect(iterator, context) { var result; this.each(function(value, index) { if (iterator.call(context, value, index, this)) { result = value; throw $break; } }, this); return result; } function findAll(iterator, context) { var results = []; this.each(function(value, index) { if (iterator.call(context, value, index, this)) results.push(value); }, this); return results; } function grep(filter, iterator, context) { iterator = iterator || Prototype.K; var results = []; if (Object.isString(filter)) filter = new RegExp(RegExp.escape(filter)); this.each(function(value, index) { if (filter.match(value)) results.push(iterator.call(context, value, index, this)); }, this); return results; } function include(object) { if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1) return true; var found = false; this.each(function(value) { if (value == object) { found = true; throw $break; } }); return found; } function inGroupsOf(number, fillWith) { fillWith = Object.isUndefined(fillWith) ? null : fillWith; return this.eachSlice(number, function(slice) { while(slice.length < number) slice.push(fillWith); return slice; }); } function inject(memo, iterator, context) { this.each(function(value, index) { memo = iterator.call(context, memo, value, index, this); }, this); return memo; } function invoke(method) { var args = $A(arguments).slice(1); return this.map(function(value) { return value[method].apply(value, args); }); } function max(iterator, context) { iterator = iterator || Prototype.K; var result; this.each(function(value, index) { value = iterator.call(context, value, index, this); if (result == null || value >= result) result = value; }, this); return result; } function min(iterator, context) { iterator = iterator || Prototype.K; var result; this.each(function(value, index) { value = iterator.call(context, value, index, this); if (result == null || value < result) result = value; }, this); return result; } function partition(iterator, context) { iterator = iterator || Prototype.K; var trues = [], falses = []; this.each(function(value, index) { (iterator.call(context, value, index, this) ? trues : falses).push(value); }, this); return [trues, falses]; } function pluck(property) { var results = []; this.each(function(value) { results.push(value[property]); }); return results; } function reject(iterator, context) { var results = []; this.each(function(value, index) { if (!iterator.call(context, value, index, this)) results.push(value); }, this); return results; } function sortBy(iterator, context) { return this.map(function(value, index) { return { value: value, criteria: iterator.call(context, value, index, this) }; }, this).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }).pluck('value'); } function toArray() { return this.map(); } function zip() { var iterator = Prototype.K, args = $A(arguments); if (Object.isFunction(args.last())) iterator = args.pop(); var collections = [this].concat(args).map($A); return this.map(function(value, index) { return iterator(collections.pluck(index)); }); } function size() { return this.toArray().length; } function inspect() { return '#'; } return { each: each, eachSlice: eachSlice, all: all, every: all, any: any, some: any, collect: collect, map: collect, detect: detect, findAll: findAll, select: findAll, filter: findAll, grep: grep, include: include, member: include, inGroupsOf: inGroupsOf, inject: inject, invoke: invoke, max: max, min: min, partition: partition, pluck: pluck, reject: reject, sortBy: sortBy, toArray: toArray, entries: toArray, zip: zip, size: size, inspect: inspect, find: detect }; })(); function $A(iterable) { if (!iterable) return []; if ('toArray' in Object(iterable)) return iterable.toArray(); var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; } function $w(string) { if (!Object.isString(string)) return []; string = string.strip(); return string ? string.split(/\s+/) : []; } Array.from = $A; (function() { var arrayProto = Array.prototype, slice = arrayProto.slice, _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available function each(iterator, context) { for (var i = 0, length = this.length >>> 0; i < length; i++) { if (i in this) iterator.call(context, this[i], i, this); } } if (!_each) _each = each; function clear() { this.length = 0; return this; } function first() { return this[0]; } function last() { return this[this.length - 1]; } function compact() { return this.select(function(value) { return value != null; }); } function flatten() { return this.inject([], function(array, value) { if (Object.isArray(value)) return array.concat(value.flatten()); array.push(value); return array; }); } function without() { var values = slice.call(arguments, 0); return this.select(function(value) { return !values.include(value); }); } function reverse(inline) { return (inline === false ? this.toArray() : this)._reverse(); } function uniq(sorted) { return this.inject([], function(array, value, index) { if (0 == index || (sorted ? array.last() != value : !array.include(value))) array.push(value); return array; }); } function intersect(array) { return this.uniq().findAll(function(item) { return array.indexOf(item) !== -1; }); } function clone() { return slice.call(this, 0); } function size() { return this.length; } function inspect() { return '[' + this.map(Object.inspect).join(', ') + ']'; } function indexOf(item, i) { if (this == null) throw new TypeError(); var array = Object(this), length = array.length >>> 0; if (length === 0) return -1; i = Number(i); if (isNaN(i)) { i = 0; } else if (i !== 0 && isFinite(i)) { i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); } if (i > length) return -1; var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0); for (; k < length; k++) if (k in array && array[k] === item) return k; return -1; } function lastIndexOf(item, i) { if (this == null) throw new TypeError(); var array = Object(this), length = array.length >>> 0; if (length === 0) return -1; if (!Object.isUndefined(i)) { i = Number(i); if (isNaN(i)) { i = 0; } else if (i !== 0 && isFinite(i)) { i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); } } else { i = length; } var k = i >= 0 ? Math.min(i, length - 1) : length - Math.abs(i); for (; k >= 0; k--) if (k in array && array[k] === item) return k; return -1; } function concat(_) { var array = [], items = slice.call(arguments, 0), item, n = 0; items.unshift(this); for (var i = 0, length = items.length; i < length; i++) { item = items[i]; if (Object.isArray(item) && !('callee' in item)) { for (var j = 0, arrayLength = item.length; j < arrayLength; j++) { if (j in item) array[n] = item[j]; n++; } } else { array[n++] = item; } } array.length = n; return array; } function wrapNative(method) { return function() { if (arguments.length === 0) { return method.call(this, Prototype.K); } else if (arguments[0] === undefined) { var args = slice.call(arguments, 1); args.unshift(Prototype.K); return method.apply(this, args); } else { return method.apply(this, arguments); } }; } function map(iterator) { if (this == null) throw new TypeError(); iterator = iterator || Prototype.K; var object = Object(this); var results = [], context = arguments[1], n = 0; for (var i = 0, length = object.length >>> 0; i < length; i++) { if (i in object) { results[n] = iterator.call(context, object[i], i, object); } n++; } results.length = n; return results; } if (arrayProto.map) { map = wrapNative(Array.prototype.map); } function filter(iterator) { if (this == null || !Object.isFunction(iterator)) throw new TypeError(); var object = Object(this); var results = [], context = arguments[1], value; for (var i = 0, length = object.length >>> 0; i < length; i++) { if (i in object) { value = object[i]; if (iterator.call(context, value, i, object)) { results.push(value); } } } return results; } if (arrayProto.filter) { filter = Array.prototype.filter; } function some(iterator) { if (this == null) throw new TypeError(); iterator = iterator || Prototype.K; var context = arguments[1]; var object = Object(this); for (var i = 0, length = object.length >>> 0; i < length; i++) { if (i in object && iterator.call(context, object[i], i, object)) { return true; } } return false; } if (arrayProto.some) { some = wrapNative(Array.prototype.some); } function every(iterator) { if (this == null) throw new TypeError(); iterator = iterator || Prototype.K; var context = arguments[1]; var object = Object(this); for (var i = 0, length = object.length >>> 0; i < length; i++) { if (i in object && !iterator.call(context, object[i], i, object)) { return false; } } return true; } if (arrayProto.every) { every = wrapNative(Array.prototype.every); } Object.extend(arrayProto, Enumerable); if (arrayProto.entries === Enumerable.entries) { delete arrayProto.entries; } if (!arrayProto._reverse) arrayProto._reverse = arrayProto.reverse; Object.extend(arrayProto, { _each: _each, map: map, collect: map, select: filter, filter: filter, findAll: filter, some: some, any: some, every: every, all: every, clear: clear, first: first, last: last, compact: compact, flatten: flatten, without: without, reverse: reverse, uniq: uniq, intersect: intersect, clone: clone, toArray: clone, size: size, inspect: inspect }); var CONCAT_ARGUMENTS_BUGGY = (function() { return [].concat(arguments)[0][0] !== 1; })(1,2); if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; })(); function $H(object) { return new Hash(object); }; var Hash = Class.create(Enumerable, (function() { function initialize(object) { this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); } function _each(iterator, context) { var i = 0; for (var key in this._object) { var value = this._object[key], pair = [key, value]; pair.key = key; pair.value = value; iterator.call(context, pair, i); i++; } } function set(key, value) { return this._object[key] = value; } function get(key) { if (this._object[key] !== Object.prototype[key]) return this._object[key]; } function unset(key) { var value = this._object[key]; delete this._object[key]; return value; } function toObject() { return Object.clone(this._object); } function keys() { return this.pluck('key'); } function values() { return this.pluck('value'); } function index(value) { var match = this.detect(function(pair) { return pair.value === value; }); return match && match.key; } function merge(object) { return this.clone().update(object); } function update(object) { return new Hash(object).inject(this, function(result, pair) { result.set(pair.key, pair.value); return result; }); } function toQueryPair(key, value) { if (Object.isUndefined(value)) return key; value = String.interpret(value); value = value.gsub(/(\r)?\n/, '\r\n'); value = encodeURIComponent(value); value = value.gsub(/%20/, '+'); return key + '=' + value; } function toQueryString() { return this.inject([], function(results, pair) { var key = encodeURIComponent(pair.key), values = pair.value; if (values && typeof values == 'object') { if (Object.isArray(values)) { var queryValues = []; for (var i = 0, len = values.length, value; i < len; i++) { value = values[i]; queryValues.push(toQueryPair(key, value)); } return results.concat(queryValues); } } else results.push(toQueryPair(key, values)); return results; }).join('&'); } function inspect() { return '#'; } function clone() { return new Hash(this); } return { initialize: initialize, _each: _each, set: set, get: get, unset: unset, toObject: toObject, toTemplateReplacements: toObject, keys: keys, values: values, index: index, merge: merge, update: update, toQueryString: toQueryString, inspect: inspect, toJSON: toObject, clone: clone }; })()); Hash.from = $H; Object.extend(Number.prototype, (function() { function toColorPart() { return this.toPaddedString(2, 16); } function succ() { return this + 1; } function times(iterator, context) { $R(0, this, true).each(iterator, context); return this; } function toPaddedString(length, radix) { var string = this.toString(radix || 10); return '0'.times(length - string.length) + string; } function abs() { return Math.abs(this); } function round() { return Math.round(this); } function ceil() { return Math.ceil(this); } function floor() { return Math.floor(this); } return { toColorPart: toColorPart, succ: succ, times: times, toPaddedString: toPaddedString, abs: abs, round: round, ceil: ceil, floor: floor }; })()); function $R(start, end, exclusive) { return new ObjectRange(start, end, exclusive); } var ObjectRange = Class.create(Enumerable, (function() { function initialize(start, end, exclusive) { this.start = start; this.end = end; this.exclusive = exclusive; } function _each(iterator, context) { var value = this.start, i; for (i = 0; this.include(value); i++) { iterator.call(context, value, i); value = value.succ(); } } function include(value) { if (value < this.start) return false; if (this.exclusive) return value < this.end; return value <= this.end; } return { initialize: initialize, _each: _each, include: include }; })()); var Abstract = { }; var Try = { these: function() { var returnValue; for (var i = 0, length = arguments.length; i < length; i++) { var lambda = arguments[i]; try { returnValue = lambda(); break; } catch (e) { } } return returnValue; } }; var Ajax = { getTransport: function() { return Try.these( function() {return new XMLHttpRequest()}, function() {return new ActiveXObject('Msxml2.XMLHTTP')}, function() {return new ActiveXObject('Microsoft.XMLHTTP')} ) || false; }, activeRequestCount: 0 }; Ajax.Responders = { responders: [], _each: function(iterator, context) { this.responders._each(iterator, context); }, register: function(responder) { if (!this.include(responder)) this.responders.push(responder); }, unregister: function(responder) { this.responders = this.responders.without(responder); }, dispatch: function(callback, request, transport, json) { this.each(function(responder) { if (Object.isFunction(responder[callback])) { try { responder[callback].apply(responder, [request, transport, json]); } catch (e) { } } }); } }; Object.extend(Ajax.Responders, Enumerable); Ajax.Responders.register({ onCreate: function() { Ajax.activeRequestCount++ }, onComplete: function() { Ajax.activeRequestCount-- } }); Ajax.Base = Class.create({ initialize: function(options) { this.options = { method: 'post', asynchronous: true, contentType: 'application/x-www-form-urlencoded', encoding: 'UTF-8', parameters: '', evalJSON: true, evalJS: true }; Object.extend(this.options, options || { }); this.options.method = this.options.method.toLowerCase(); if (Object.isHash(this.options.parameters)) this.options.parameters = this.options.parameters.toObject(); } }); Ajax.Request = Class.create(Ajax.Base, { _complete: false, initialize: function($super, url, options) { $super(options); this.transport = Ajax.getTransport(); this.request(url); }, request: function(url) { this.url = url; this.method = this.options.method; var params = Object.isString(this.options.parameters) ? this.options.parameters : Object.toQueryString(this.options.parameters); if (!['get', 'post'].include(this.method)) { params += (params ? '&' : '') + "_method=" + this.method; this.method = 'post'; } if (params && this.method === 'get') { this.url += (this.url.include('?') ? '&' : '?') + params; } this.parameters = params.toQueryParams(); try { var response = new Ajax.Response(this); if (this.options.onCreate) this.options.onCreate(response); Ajax.Responders.dispatch('onCreate', this, response); this.transport.open(this.method.toUpperCase(), this.url, this.options.asynchronous); if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); this.transport.onreadystatechange = this.onStateChange.bind(this); this.setRequestHeaders(); this.body = this.method == 'post' ? (this.options.postBody || params) : null; this.transport.send(this.body); /* Force Firefox to handle ready state 4 for synchronous requests */ if (!this.options.asynchronous && this.transport.overrideMimeType) this.onStateChange(); } catch (e) { this.dispatchException(e); } }, onStateChange: function() { var readyState = this.transport.readyState; if (readyState > 1 && !((readyState == 4) && this._complete)) this.respondToReadyState(this.transport.readyState); }, setRequestHeaders: function() { var headers = { 'X-Requested-With': 'XMLHttpRequest', 'X-Prototype-Version': Prototype.Version, 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' }; if (this.method == 'post') { headers['Content-type'] = this.options.contentType + (this.options.encoding ? '; charset=' + this.options.encoding : ''); /* Force "Connection: close" for older Mozilla browsers to work * around a bug where XMLHttpRequest sends an incorrect * Content-length header. See Mozilla Bugzilla #246651. */ if (this.transport.overrideMimeType && (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) headers['Connection'] = 'close'; } if (typeof this.options.requestHeaders == 'object') { var extras = this.options.requestHeaders; if (Object.isFunction(extras.push)) for (var i = 0, length = extras.length; i < length; i += 2) headers[extras[i]] = extras[i+1]; else $H(extras).each(function(pair) { headers[pair.key] = pair.value }); } for (var name in headers) if (headers[name] != null) this.transport.setRequestHeader(name, headers[name]); }, success: function() { var status = this.getStatus(); return !status || (status >= 200 && status < 300) || status == 304; }, getStatus: function() { try { if (this.transport.status === 1223) return 204; return this.transport.status || 0; } catch (e) { return 0 } }, respondToReadyState: function(readyState) { var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); if (state == 'Complete') { try { this._complete = true; (this.options['on' + response.status] || this.options['on' + (this.success() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(response, response.headerJSON); } catch (e) { this.dispatchException(e); } var contentType = response.getHeader('Content-type'); if (this.options.evalJS == 'force' || (this.options.evalJS && this.isSameOrigin() && contentType && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) this.evalResponse(); } try { (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); } catch (e) { this.dispatchException(e); } if (state == 'Complete') { this.transport.onreadystatechange = Prototype.emptyFunction; } }, isSameOrigin: function() { var m = this.url.match(/^\s*https?:\/\/[^\/]*/); return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ protocol: location.protocol, domain: document.domain, port: location.port ? ':' + location.port : '' })); }, getHeader: function(name) { try { return this.transport.getResponseHeader(name) || null; } catch (e) { return null; } }, evalResponse: function() { try { return eval((this.transport.responseText || '').unfilterJSON()); } catch (e) { this.dispatchException(e); } }, dispatchException: function(exception) { (this.options.onException || Prototype.emptyFunction)(this, exception); Ajax.Responders.dispatch('onException', this, exception); } }); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Response = Class.create({ initialize: function(request){ this.request = request; var transport = this.transport = request.transport, readyState = this.readyState = transport.readyState; if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { this.status = this.getStatus(); this.statusText = this.getStatusText(); this.responseText = String.interpret(transport.responseText); this.headerJSON = this._getHeaderJSON(); } if (readyState == 4) { var xml = transport.responseXML; this.responseXML = Object.isUndefined(xml) ? null : xml; this.responseJSON = this._getResponseJSON(); } }, status: 0, statusText: '', getStatus: Ajax.Request.prototype.getStatus, getStatusText: function() { try { return this.transport.statusText || ''; } catch (e) { return '' } }, getHeader: Ajax.Request.prototype.getHeader, getAllHeaders: function() { try { return this.getAllResponseHeaders(); } catch (e) { return null } }, getResponseHeader: function(name) { return this.transport.getResponseHeader(name); }, getAllResponseHeaders: function() { return this.transport.getAllResponseHeaders(); }, _getHeaderJSON: function() { var json = this.getHeader('X-JSON'); if (!json) return null; try { json = decodeURIComponent(escape(json)); } catch(e) { } try { return json.evalJSON(this.request.options.sanitizeJSON || !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } }, _getResponseJSON: function() { var options = this.request.options; if (!options.evalJSON || (options.evalJSON != 'force' && !(this.getHeader('Content-type') || '').include('application/json')) || this.responseText.blank()) return null; try { return this.responseText.evalJSON(options.sanitizeJSON || !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } } }); Ajax.Updater = Class.create(Ajax.Request, { initialize: function($super, container, url, options) { this.container = { success: (container.success || container), failure: (container.failure || (container.success ? null : container)) }; options = Object.clone(options); var onComplete = options.onComplete; options.onComplete = (function(response, json) { this.updateContent(response.responseText); if (Object.isFunction(onComplete)) onComplete(response, json); }).bind(this); $super(url, options); }, updateContent: function(responseText) { var receiver = this.container[this.success() ? 'success' : 'failure'], options = this.options; if (!options.evalScripts) responseText = responseText.stripScripts(); if (receiver = $(receiver)) { if (options.insertion) { if (Object.isString(options.insertion)) { var insertion = { }; insertion[options.insertion] = responseText; receiver.insert(insertion); } else options.insertion(receiver, responseText); } else receiver.update(responseText); } } }); Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { initialize: function($super, container, url, options) { $super(options); this.onComplete = this.options.onComplete; this.frequency = (this.options.frequency || 2); this.decay = (this.options.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.options.onComplete = undefined; clearTimeout(this.timer); (this.onComplete || Prototype.emptyFunction).apply(this, arguments); }, updateComplete: function(response) { if (this.options.decay) { this.decay = (response.responseText == this.lastText ? this.decay * this.options.decay : 1); this.lastText = response.responseText; } this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); }, onTimerEvent: function() { this.updater = new Ajax.Updater(this.container, this.url, this.options); } }); (function(GLOBAL) { var UNDEFINED; var SLICE = Array.prototype.slice; var DIV = document.createElement('div'); function $(element) { if (arguments.length > 1) { for (var i = 0, elements = [], length = arguments.length; i < length; i++) elements.push($(arguments[i])); return elements; } if (Object.isString(element)) element = document.getElementById(element); return Element.extend(element); } GLOBAL.$ = $; if (!GLOBAL.Node) GLOBAL.Node = {}; if (!GLOBAL.Node.ELEMENT_NODE) { Object.extend(GLOBAL.Node, { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12 }); } var ELEMENT_CACHE = {}; function shouldUseCreationCache(tagName, attributes) { if (tagName === 'select') return false; if ('type' in attributes) return false; return true; } var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ try { var el = document.createElement(''); return el.tagName.toLowerCase() === 'input' && el.name === 'x'; } catch(err) { return false; } })(); var oldElement = GLOBAL.Element; function Element(tagName, attributes) { attributes = attributes || {}; tagName = tagName.toLowerCase(); if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { tagName = '<' + tagName + ' name="' + attributes.name + '">'; delete attributes.name; return Element.writeAttribute(document.createElement(tagName), attributes); } if (!ELEMENT_CACHE[tagName]) ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName)); var node = shouldUseCreationCache(tagName, attributes) ? ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName); return Element.writeAttribute(node, attributes); } GLOBAL.Element = Element; Object.extend(GLOBAL.Element, oldElement || {}); if (oldElement) GLOBAL.Element.prototype = oldElement.prototype; Element.Methods = { ByTag: {}, Simulated: {} }; var methods = {}; var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' }; function inspect(element) { element = $(element); var result = '<' + element.tagName.toLowerCase(); var attribute, value; for (var property in INSPECT_ATTRIBUTES) { attribute = INSPECT_ATTRIBUTES[property]; value = (element[property] || '').toString(); if (value) result += ' ' + attribute + '=' + value.inspect(true); } return result + '>'; } methods.inspect = inspect; function visible(element) { return $(element).getStyle('display') !== 'none'; } function toggle(element, bool) { element = $(element); if (typeof bool !== 'boolean') bool = !Element.visible(element); Element[bool ? 'show' : 'hide'](element); return element; } function hide(element) { element = $(element); element.style.display = 'none'; return element; } function show(element) { element = $(element); element.style.display = ''; return element; } Object.extend(methods, { visible: visible, toggle: toggle, hide: hide, show: show }); function remove(element) { element = $(element); element.parentNode.removeChild(element); return element; } var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ var el = document.createElement("select"), isBuggy = true; el.innerHTML = ""; if (el.options && el.options[0]) { isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; } el = null; return isBuggy; })(); var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ try { var el = document.createElement("table"); if (el && el.tBodies) { el.innerHTML = "test"; var isBuggy = typeof el.tBodies[0] == "undefined"; el = null; return isBuggy; } } catch (e) { return true; } })(); var LINK_ELEMENT_INNERHTML_BUGGY = (function() { try { var el = document.createElement('div'); el.innerHTML = ""; var isBuggy = (el.childNodes.length === 0); el = null; return isBuggy; } catch(e) { return true; } })(); var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { var s = document.createElement("script"), isBuggy = false; try { s.appendChild(document.createTextNode("")); isBuggy = !s.firstChild || s.firstChild && s.firstChild.nodeType !== 3; } catch (e) { isBuggy = true; } s = null; return isBuggy; })(); function update(element, content) { element = $(element); var descendants = element.getElementsByTagName('*'), i = descendants.length; while (i--) purgeElement(descendants[i]); if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) return element.update().insert(content); content = Object.toHTML(content); var tagName = element.tagName.toUpperCase(); if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { element.text = content; return element; } if (ANY_INNERHTML_BUGGY) { if (tagName in INSERTION_TRANSLATIONS.tags) { while (element.firstChild) element.removeChild(element.firstChild); var nodes = getContentFromAnonymousElement(tagName, content.stripScripts()); for (var i = 0, node; node = nodes[i]; i++) element.appendChild(node); } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { while (element.firstChild) element.removeChild(element.firstChild); var nodes = getContentFromAnonymousElement(tagName, content.stripScripts(), true); for (var i = 0, node; node = nodes[i]; i++) element.appendChild(node); } else { element.innerHTML = content.stripScripts(); } } else { element.innerHTML = content.stripScripts(); } content.evalScripts.bind(content).defer(); return element; } function replace(element, content) { element = $(element); if (content && content.toElement) { content = content.toElement(); } else if (!Object.isElement(content)) { content = Object.toHTML(content); var range = element.ownerDocument.createRange(); range.selectNode(element); content.evalScripts.bind(content).defer(); content = range.createContextualFragment(content.stripScripts()); } element.parentNode.replaceChild(content, element); return element; } var INSERTION_TRANSLATIONS = { before: function(element, node) { element.parentNode.insertBefore(node, element); }, top: function(element, node) { element.insertBefore(node, element.firstChild); }, bottom: function(element, node) { element.appendChild(node); }, after: function(element, node) { element.parentNode.insertBefore(node, element.nextSibling); }, tags: { TABLE: ['', '
', 1], TBODY: ['', '
', 2], TR: ['', '
', 3], TD: ['
', '
', 4], SELECT: ['', 1] } }; var tags = INSERTION_TRANSLATIONS.tags; Object.extend(tags, { THEAD: tags.TBODY, TFOOT: tags.TBODY, TH: tags.TD }); function replace_IE(element, content) { element = $(element); if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) { element.parentNode.replaceChild(content, element); return element; } content = Object.toHTML(content); var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); if (tagName in INSERTION_TRANSLATIONS.tags) { var nextSibling = Element.next(element); var fragments = getContentFromAnonymousElement( tagName, content.stripScripts()); parent.removeChild(element); var iterator; if (nextSibling) iterator = function(node) { parent.insertBefore(node, nextSibling) }; else iterator = function(node) { parent.appendChild(node); } fragments.each(iterator); } else { element.outerHTML = content.stripScripts(); } content.evalScripts.bind(content).defer(); return element; } if ('outerHTML' in document.documentElement) replace = replace_IE; function isContent(content) { if (Object.isUndefined(content) || content === null) return false; if (Object.isString(content) || Object.isNumber(content)) return true; if (Object.isElement(content)) return true; if (content.toElement || content.toHTML) return true; return false; } function insertContentAt(element, content, position) { position = position.toLowerCase(); var method = INSERTION_TRANSLATIONS[position]; if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) { method(element, content); return element; } content = Object.toHTML(content); var tagName = ((position === 'before' || position === 'after') ? element.parentNode : element).tagName.toUpperCase(); var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts()); if (position === 'top' || position === 'after') childNodes.reverse(); for (var i = 0, node; node = childNodes[i]; i++) method(element, node); content.evalScripts.bind(content).defer(); } function insert(element, insertions) { element = $(element); if (isContent(insertions)) insertions = { bottom: insertions }; for (var position in insertions) insertContentAt(element, insertions[position], position); return element; } function wrap(element, wrapper, attributes) { element = $(element); if (Object.isElement(wrapper)) { $(wrapper).writeAttribute(attributes || {}); } else if (Object.isString(wrapper)) { wrapper = new Element(wrapper, attributes); } else { wrapper = new Element('div', wrapper); } if (element.parentNode) element.parentNode.replaceChild(wrapper, element); wrapper.appendChild(element); return wrapper; } function cleanWhitespace(element) { element = $(element); var node = element.firstChild; while (node) { var nextNode = node.nextSibling; if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue)) element.removeChild(node); node = nextNode; } return element; } function empty(element) { return $(element).innerHTML.blank(); } function getContentFromAnonymousElement(tagName, html, force) { var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV; var workaround = !!t; if (!workaround && force) { workaround = true; t = ['', '', 0]; } if (workaround) { div.innerHTML = ' ' + t[0] + html + t[1]; div.removeChild(div.firstChild); for (var i = t[2]; i--; ) div = div.firstChild; } else { div.innerHTML = html; } return $A(div.childNodes); } function clone(element, deep) { if (!(element = $(element))) return; var clone = element.cloneNode(deep); if (!HAS_UNIQUE_ID_PROPERTY) { clone._prototypeUID = UNDEFINED; if (deep) { var descendants = Element.select(clone, '*'), i = descendants.length; while (i--) descendants[i]._prototypeUID = UNDEFINED; } } return Element.extend(clone); } function purgeElement(element) { var uid = getUniqueElementID(element); if (uid) { Element.stopObserving(element); if (!HAS_UNIQUE_ID_PROPERTY) element._prototypeUID = UNDEFINED; delete Element.Storage[uid]; } } function purgeCollection(elements) { var i = elements.length; while (i--) purgeElement(elements[i]); } function purgeCollection_IE(elements) { var i = elements.length, element, uid; while (i--) { element = elements[i]; uid = getUniqueElementID(element); delete Element.Storage[uid]; delete Event.cache[uid]; } } if (HAS_UNIQUE_ID_PROPERTY) { purgeCollection = purgeCollection_IE; } function purge(element) { if (!(element = $(element))) return; purgeElement(element); var descendants = element.getElementsByTagName('*'), i = descendants.length; while (i--) purgeElement(descendants[i]); return null; } Object.extend(methods, { remove: remove, update: update, replace: replace, insert: insert, wrap: wrap, cleanWhitespace: cleanWhitespace, empty: empty, clone: clone, purge: purge }); function recursivelyCollect(element, property, maximumLength) { element = $(element); maximumLength = maximumLength || -1; var elements = []; while (element = element[property]) { if (element.nodeType === Node.ELEMENT_NODE) elements.push(Element.extend(element)); if (elements.length === maximumLength) break; } return elements; } function ancestors(element) { return recursivelyCollect(element, 'parentNode'); } function descendants(element) { return Element.select(element, '*'); } function firstDescendant(element) { element = $(element).firstChild; while (element && element.nodeType !== Node.ELEMENT_NODE) element = element.nextSibling; return $(element); } function immediateDescendants(element) { var results = [], child = $(element).firstChild; while (child) { if (child.nodeType === Node.ELEMENT_NODE) results.push(Element.extend(child)); child = child.nextSibling; } return results; } function previousSiblings(element) { return recursivelyCollect(element, 'previousSibling'); } function nextSiblings(element) { return recursivelyCollect(element, 'nextSibling'); } function siblings(element) { element = $(element); var previous = previousSiblings(element), next = nextSiblings(element); return previous.reverse().concat(next); } function match(element, selector) { element = $(element); if (Object.isString(selector)) return Prototype.Selector.match(element, selector); return selector.match(element); } function _recursivelyFind(element, property, expression, index) { element = $(element), expression = expression || 0, index = index || 0; if (Object.isNumber(expression)) { index = expression, expression = null; } while (element = element[property]) { if (element.nodeType !== 1) continue; if (expression && !Prototype.Selector.match(element, expression)) continue; if (--index >= 0) continue; return Element.extend(element); } } function up(element, expression, index) { element = $(element); if (arguments.length === 1) return $(element.parentNode); return _recursivelyFind(element, 'parentNode', expression, index); } function down(element, expression, index) { if (arguments.length === 1) return firstDescendant(element); element = $(element), expression = expression || 0, index = index || 0; if (Object.isNumber(expression)) index = expression, expression = '*'; var node = Prototype.Selector.select(expression, element)[index]; return Element.extend(node); } function previous(element, expression, index) { return _recursivelyFind(element, 'previousSibling', expression, index); } function next(element, expression, index) { return _recursivelyFind(element, 'nextSibling', expression, index); } function select(element) { element = $(element); var expressions = SLICE.call(arguments, 1).join(', '); return Prototype.Selector.select(expressions, element); } function adjacent(element) { element = $(element); var expressions = SLICE.call(arguments, 1).join(', '); var siblings = Element.siblings(element), results = []; for (var i = 0, sibling; sibling = siblings[i]; i++) { if (Prototype.Selector.match(sibling, expressions)) results.push(sibling); } return results; } function descendantOf_DOM(element, ancestor) { element = $(element), ancestor = $(ancestor); if (!element || !ancestor) return false; while (element = element.parentNode) if (element === ancestor) return true; return false; } function descendantOf_contains(element, ancestor) { element = $(element), ancestor = $(ancestor); if (!element || !ancestor) return false; if (!ancestor.contains) return descendantOf_DOM(element, ancestor); return ancestor.contains(element) && ancestor !== element; } function descendantOf_compareDocumentPosition(element, ancestor) { element = $(element), ancestor = $(ancestor); if (!element || !ancestor) return false; return (element.compareDocumentPosition(ancestor) & 8) === 8; } var descendantOf; if (DIV.compareDocumentPosition) { descendantOf = descendantOf_compareDocumentPosition; } else if (DIV.contains) { descendantOf = descendantOf_contains; } else { descendantOf = descendantOf_DOM; } Object.extend(methods, { recursivelyCollect: recursivelyCollect, ancestors: ancestors, descendants: descendants, firstDescendant: firstDescendant, immediateDescendants: immediateDescendants, previousSiblings: previousSiblings, nextSiblings: nextSiblings, siblings: siblings, match: match, up: up, down: down, previous: previous, next: next, select: select, adjacent: adjacent, descendantOf: descendantOf, getElementsBySelector: select, childElements: immediateDescendants }); var idCounter = 1; function identify(element) { element = $(element); var id = Element.readAttribute(element, 'id'); if (id) return id; do { id = 'anonymous_element_' + idCounter++ } while ($(id)); Element.writeAttribute(element, 'id', id); return id; } function readAttribute(element, name) { return $(element).getAttribute(name); } function readAttribute_IE(element, name) { element = $(element); var table = ATTRIBUTE_TRANSLATIONS.read; if (table.values[name]) return table.values[name](element, name); if (table.names[name]) name = table.names[name]; if (name.include(':')) { if (!element.attributes || !element.attributes[name]) return null; return element.attributes[name].value; } return element.getAttribute(name); } function readAttribute_Opera(element, name) { if (name === 'title') return element.title; return element.getAttribute(name); } var PROBLEMATIC_ATTRIBUTE_READING = (function() { DIV.setAttribute('onclick', []); var value = DIV.getAttribute('onclick'); var isFunction = Object.isArray(value); DIV.removeAttribute('onclick'); return isFunction; })(); if (PROBLEMATIC_ATTRIBUTE_READING) { readAttribute = readAttribute_IE; } else if (Prototype.Browser.Opera) { readAttribute = readAttribute_Opera; } function writeAttribute(element, name, value) { element = $(element); var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write; if (typeof name === 'object') { attributes = name; } else { attributes[name] = Object.isUndefined(value) ? true : value; } for (var attr in attributes) { name = table.names[attr] || attr; value = attributes[attr]; if (table.values[attr]) { value = table.values[attr](element, value); if (Object.isUndefined(value)) continue; } if (value === false || value === null) element.removeAttribute(name); else if (value === true) element.setAttribute(name, name); else element.setAttribute(name, value); } return element; } var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () { if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) { return false; } var checkbox = document.createElement(''); checkbox.checked = true; var node = checkbox.getAttributeNode('checked'); return !node || !node.specified; })(); function hasAttribute(element, attribute) { attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute; var node = $(element).getAttributeNode(attribute); return !!(node && node.specified); } function hasAttribute_IE(element, attribute) { if (attribute === 'checked') { return element.checked; } return hasAttribute(element, attribute); } GLOBAL.Element.Methods.Simulated.hasAttribute = PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ? hasAttribute_IE : hasAttribute; function classNames(element) { return new Element.ClassNames(element); } var regExpCache = {}; function getRegExpForClassName(className) { if (regExpCache[className]) return regExpCache[className]; var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); regExpCache[className] = re; return re; } function hasClassName(element, className) { if (!(element = $(element))) return; var elementClassName = element.className; if (elementClassName.length === 0) return false; if (elementClassName === className) return true; return getRegExpForClassName(className).test(elementClassName); } function addClassName(element, className) { if (!(element = $(element))) return; if (!hasClassName(element, className)) element.className += (element.className ? ' ' : '') + className; return element; } function removeClassName(element, className) { if (!(element = $(element))) return; element.className = element.className.replace( getRegExpForClassName(className), ' ').strip(); return element; } function toggleClassName(element, className, bool) { if (!(element = $(element))) return; if (Object.isUndefined(bool)) bool = !hasClassName(element, className); var method = Element[bool ? 'addClassName' : 'removeClassName']; return method(element, className); } var ATTRIBUTE_TRANSLATIONS = {}; var classProp = 'className', forProp = 'for'; DIV.setAttribute(classProp, 'x'); if (DIV.className !== 'x') { DIV.setAttribute('class', 'x'); if (DIV.className === 'x') classProp = 'class'; } var LABEL = document.createElement('label'); LABEL.setAttribute(forProp, 'x'); if (LABEL.htmlFor !== 'x') { LABEL.setAttribute('htmlFor', 'x'); if (LABEL.htmlFor === 'x') forProp = 'htmlFor'; } LABEL = null; function _getAttr(element, attribute) { return element.getAttribute(attribute); } function _getAttr2(element, attribute) { return element.getAttribute(attribute, 2); } function _getAttrNode(element, attribute) { var node = element.getAttributeNode(attribute); return node ? node.value : ''; } function _getFlag(element, attribute) { return $(element).hasAttribute(attribute) ? attribute : null; } DIV.onclick = Prototype.emptyFunction; var onclickValue = DIV.getAttribute('onclick'); var _getEv; if (String(onclickValue).indexOf('{') > -1) { _getEv = function(element, attribute) { var value = element.getAttribute(attribute); if (!value) return null; value = value.toString(); value = value.split('{')[1]; value = value.split('}')[0]; return value.strip(); }; } else if (onclickValue === '') { _getEv = function(element, attribute) { var value = element.getAttribute(attribute); if (!value) return null; return value.strip(); }; } ATTRIBUTE_TRANSLATIONS.read = { names: { 'class': classProp, 'className': classProp, 'for': forProp, 'htmlFor': forProp }, values: { style: function(element) { return element.style.cssText.toLowerCase(); }, title: function(element) { return element.title; } } }; ATTRIBUTE_TRANSLATIONS.write = { names: { className: 'class', htmlFor: 'for', cellpadding: 'cellPadding', cellspacing: 'cellSpacing' }, values: { checked: function(element, value) { value = !!value; element.checked = value; return value ? 'checked' : null; }, style: function(element, value) { element.style.cssText = value ? value : ''; } } }; ATTRIBUTE_TRANSLATIONS.has = { names: {} }; Object.extend(ATTRIBUTE_TRANSLATIONS.write.names, ATTRIBUTE_TRANSLATIONS.read.names); var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' + 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'); for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) { ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr; ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr; } Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, { href: _getAttr2, src: _getAttr2, type: _getAttr, action: _getAttrNode, disabled: _getFlag, checked: _getFlag, readonly: _getFlag, multiple: _getFlag, onload: _getEv, onunload: _getEv, onclick: _getEv, ondblclick: _getEv, onmousedown: _getEv, onmouseup: _getEv, onmouseover: _getEv, onmousemove: _getEv, onmouseout: _getEv, onfocus: _getEv, onblur: _getEv, onkeypress: _getEv, onkeydown: _getEv, onkeyup: _getEv, onsubmit: _getEv, onreset: _getEv, onselect: _getEv, onchange: _getEv }); Object.extend(methods, { identify: identify, readAttribute: readAttribute, writeAttribute: writeAttribute, classNames: classNames, hasClassName: hasClassName, addClassName: addClassName, removeClassName: removeClassName, toggleClassName: toggleClassName }); function normalizeStyleName(style) { if (style === 'float' || style === 'styleFloat') return 'cssFloat'; return style.camelize(); } function normalizeStyleName_IE(style) { if (style === 'float' || style === 'cssFloat') return 'styleFloat'; return style.camelize(); } function setStyle(element, styles) { element = $(element); var elementStyle = element.style, match; if (Object.isString(styles)) { elementStyle.cssText += ';' + styles; if (styles.include('opacity')) { var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1]; Element.setOpacity(element, opacity); } return element; } for (var property in styles) { if (property === 'opacity') { Element.setOpacity(element, styles[property]); } else { var value = styles[property]; if (property === 'float' || property === 'cssFloat') { property = Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat'; } elementStyle[property] = value; } } return element; } function getStyle(element, style) { element = $(element); style = normalizeStyleName(style); var value = element.style[style]; if (!value || value === 'auto') { var css = document.defaultView.getComputedStyle(element, null); value = css ? css[style] : null; } if (style === 'opacity') return value ? parseFloat(value) : 1.0; return value === 'auto' ? null : value; } function getStyle_Opera(element, style) { switch (style) { case 'height': case 'width': if (!Element.visible(element)) return null; var dim = parseInt(getStyle(element, style), 10); if (dim !== element['offset' + style.capitalize()]) return dim + 'px'; return Element.measure(element, style); default: return getStyle(element, style); } } function getStyle_IE(element, style) { element = $(element); style = normalizeStyleName_IE(style); var value = element.style[style]; if (!value && element.currentStyle) { value = element.currentStyle[style]; } if (style === 'opacity') { if (!STANDARD_CSS_OPACITY_SUPPORTED) return getOpacity_IE(element); else return value ? parseFloat(value) : 1.0; } if (value === 'auto') { if ((style === 'width' || style === 'height') && Element.visible(element)) return Element.measure(element, style) + 'px'; return null; } return value; } function stripAlphaFromFilter_IE(filter) { return (filter || '').replace(/alpha\([^\)]*\)/gi, ''); } function hasLayout_IE(element) { if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1; return element; } var STANDARD_CSS_OPACITY_SUPPORTED = (function() { DIV.style.cssText = "opacity:.55"; return /^0.55/.test(DIV.style.opacity); })(); function setOpacity(element, value) { element = $(element); if (value == 1 || value === '') value = ''; else if (value < 0.00001) value = 0; element.style.opacity = value; return element; } function setOpacity_IE(element, value) { if (STANDARD_CSS_OPACITY_SUPPORTED) return setOpacity(element, value); element = hasLayout_IE($(element)); var filter = Element.getStyle(element, 'filter'), style = element.style; if (value == 1 || value === '') { filter = stripAlphaFromFilter_IE(filter); if (filter) style.filter = filter; else style.removeAttribute('filter'); return element; } if (value < 0.00001) value = 0; style.filter = stripAlphaFromFilter_IE(filter) + ' alpha(opacity=' + (value * 100) + ')'; return element; } function getOpacity(element) { return Element.getStyle(element, 'opacity'); } function getOpacity_IE(element) { if (STANDARD_CSS_OPACITY_SUPPORTED) return getOpacity(element); var filter = Element.getStyle(element, 'filter'); if (filter.length === 0) return 1.0; var match = (filter || '').match(/alpha\(opacity=(.*)\)/i); if (match && match[1]) return parseFloat(match[1]) / 100; return 1.0; } Object.extend(methods, { setStyle: setStyle, getStyle: getStyle, setOpacity: setOpacity, getOpacity: getOpacity }); if ('styleFloat' in DIV.style) { methods.getStyle = getStyle_IE; methods.setOpacity = setOpacity_IE; methods.getOpacity = getOpacity_IE; } var UID = 0; GLOBAL.Element.Storage = { UID: 1 }; function getUniqueElementID(element) { if (element === window) return 0; if (typeof element._prototypeUID === 'undefined') element._prototypeUID = Element.Storage.UID++; return element._prototypeUID; } function getUniqueElementID_IE(element) { if (element === window) return 0; if (element == document) return 1; return element.uniqueID; } var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV); if (HAS_UNIQUE_ID_PROPERTY) getUniqueElementID = getUniqueElementID_IE; function getStorage(element) { if (!(element = $(element))) return; var uid = getUniqueElementID(element); if (!Element.Storage[uid]) Element.Storage[uid] = $H(); return Element.Storage[uid]; } function store(element, key, value) { if (!(element = $(element))) return; var storage = getStorage(element); if (arguments.length === 2) { storage.update(key); } else { storage.set(key, value); } return element; } function retrieve(element, key, defaultValue) { if (!(element = $(element))) return; var storage = getStorage(element), value = storage.get(key); if (Object.isUndefined(value)) { storage.set(key, defaultValue); value = defaultValue; } return value; } Object.extend(methods, { getStorage: getStorage, store: store, retrieve: retrieve }); var Methods = {}, ByTag = Element.Methods.ByTag, F = Prototype.BrowserFeatures; if (!F.ElementExtensions && ('__proto__' in DIV)) { GLOBAL.HTMLElement = {}; GLOBAL.HTMLElement.prototype = DIV['__proto__']; F.ElementExtensions = true; } function checkElementPrototypeDeficiency(tagName) { if (typeof window.Element === 'undefined') return false; if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false; var proto = window.Element.prototype; if (proto) { var id = '_' + (Math.random() + '').slice(2), el = document.createElement(tagName); proto[id] = 'x'; var isBuggy = (el[id] !== 'x'); delete proto[id]; el = null; return isBuggy; } return false; } var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkElementPrototypeDeficiency('object'); function extendElementWith(element, methods) { for (var property in methods) { var value = methods[property]; if (Object.isFunction(value) && !(property in element)) element[property] = value.methodize(); } } var EXTENDED = {}; function elementIsExtended(element) { var uid = getUniqueElementID(element); return (uid in EXTENDED); } function extend(element) { if (!element || elementIsExtended(element)) return element; if (element.nodeType !== Node.ELEMENT_NODE || element == window) return element; var methods = Object.clone(Methods), tagName = element.tagName.toUpperCase(); if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); extendElementWith(element, methods); EXTENDED[getUniqueElementID(element)] = true; return element; } function extend_IE8(element) { if (!element || elementIsExtended(element)) return element; var t = element.tagName; if (t && (/^(?:object|applet|embed)$/i.test(t))) { extendElementWith(element, Element.Methods); extendElementWith(element, Element.Methods.Simulated); extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); } return element; } if (F.SpecificElementExtensions) { extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K; } function addMethodsToTagName(tagName, methods) { tagName = tagName.toUpperCase(); if (!ByTag[tagName]) ByTag[tagName] = {}; Object.extend(ByTag[tagName], methods); } function mergeMethods(destination, methods, onlyIfAbsent) { if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false; for (var property in methods) { var value = methods[property]; if (!Object.isFunction(value)) continue; if (!onlyIfAbsent || !(property in destination)) destination[property] = value.methodize(); } } function findDOMClass(tagName) { var klass; var trans = { "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": "FrameSet", "IFRAME": "IFrame" }; if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; if (window[klass]) return window[klass]; klass = 'HTML' + tagName + 'Element'; if (window[klass]) return window[klass]; klass = 'HTML' + tagName.capitalize() + 'Element'; if (window[klass]) return window[klass]; var element = document.createElement(tagName), proto = element['__proto__'] || element.constructor.prototype; element = null; return proto; } function addMethods(methods) { if (arguments.length === 0) addFormMethods(); if (arguments.length === 2) { var tagName = methods; methods = arguments[1]; } if (!tagName) { Object.extend(Element.Methods, methods || {}); } else { if (Object.isArray(tagName)) { for (var i = 0, tag; tag = tagName[i]; i++) addMethodsToTagName(tag, methods); } else { addMethodsToTagName(tagName, methods); } } var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype : Element.prototype; if (F.ElementExtensions) { mergeMethods(ELEMENT_PROTOTYPE, Element.Methods); mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true); } if (F.SpecificElementExtensions) { for (var tag in Element.Methods.ByTag) { var klass = findDOMClass(tag); if (Object.isUndefined(klass)) continue; mergeMethods(klass.prototype, ByTag[tag]); } } Object.extend(Element, Element.Methods); Object.extend(Element, Element.Methods.Simulated); delete Element.ByTag; delete Element.Simulated; Element.extend.refresh(); ELEMENT_CACHE = {}; } Object.extend(GLOBAL.Element, { extend: extend, addMethods: addMethods }); if (extend === Prototype.K) { GLOBAL.Element.extend.refresh = Prototype.emptyFunction; } else { GLOBAL.Element.extend.refresh = function() { if (Prototype.BrowserFeatures.ElementExtensions) return; Object.extend(Methods, Element.Methods); Object.extend(Methods, Element.Methods.Simulated); EXTENDED = {}; }; } function addFormMethods() { Object.extend(Form, Form.Methods); Object.extend(Form.Element, Form.Element.Methods); Object.extend(Element.Methods.ByTag, { "FORM": Object.clone(Form.Methods), "INPUT": Object.clone(Form.Element.Methods), "SELECT": Object.clone(Form.Element.Methods), "TEXTAREA": Object.clone(Form.Element.Methods), "BUTTON": Object.clone(Form.Element.Methods) }); } Element.addMethods(methods); function destroyCache_IE() { DIV = null; ELEMENT_CACHE = null; } if (window.attachEvent) window.attachEvent('onunload', destroyCache_IE); })(this); (function() { function toDecimal(pctString) { var match = pctString.match(/^(\d+)%?$/i); if (!match) return null; return (Number(match[1]) / 100); } function getRawStyle(element, style) { element = $(element); var value = element.style[style]; if (!value || value === 'auto') { var css = document.defaultView.getComputedStyle(element, null); value = css ? css[style] : null; } if (style === 'opacity') return value ? parseFloat(value) : 1.0; return value === 'auto' ? null : value; } function getRawStyle_IE(element, style) { var value = element.style[style]; if (!value && element.currentStyle) { value = element.currentStyle[style]; } return value; } function getContentWidth(element, context) { var boxWidth = element.offsetWidth; var bl = getPixelValue(element, 'borderLeftWidth', context) || 0; var br = getPixelValue(element, 'borderRightWidth', context) || 0; var pl = getPixelValue(element, 'paddingLeft', context) || 0; var pr = getPixelValue(element, 'paddingRight', context) || 0; return boxWidth - bl - br - pl - pr; } if (!Object.isUndefined(document.documentElement.currentStyle) && !Prototype.Browser.Opera) { getRawStyle = getRawStyle_IE; } function getPixelValue(value, property, context) { var element = null; if (Object.isElement(value)) { element = value; value = getRawStyle(element, property); } if (value === null || Object.isUndefined(value)) { return null; } if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { return window.parseFloat(value); } var isPercentage = value.include('%'), isViewport = (context === document.viewport); if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { var style = element.style.left, rStyle = element.runtimeStyle.left; element.runtimeStyle.left = element.currentStyle.left; element.style.left = value || 0; value = element.style.pixelLeft; element.style.left = style; element.runtimeStyle.left = rStyle; return value; } if (element && isPercentage) { context = context || element.parentNode; var decimal = toDecimal(value), whole = null; var isHorizontal = property.include('left') || property.include('right') || property.include('width'); var isVertical = property.include('top') || property.include('bottom') || property.include('height'); if (context === document.viewport) { if (isHorizontal) { whole = document.viewport.getWidth(); } else if (isVertical) { whole = document.viewport.getHeight(); } } else { if (isHorizontal) { whole = $(context).measure('width'); } else if (isVertical) { whole = $(context).measure('height'); } } return (whole === null) ? 0 : whole * decimal; } return 0; } function toCSSPixels(number) { if (Object.isString(number) && number.endsWith('px')) return number; return number + 'px'; } function isDisplayed(element) { while (element && element.parentNode) { var display = element.getStyle('display'); if (display === 'none') { return false; } element = $(element.parentNode); } return true; } var hasLayout = Prototype.K; if ('currentStyle' in document.documentElement) { hasLayout = function(element) { if (!element.currentStyle.hasLayout) { element.style.zoom = 1; } return element; }; } function cssNameFor(key) { if (key.include('border')) key = key + '-width'; return key.camelize(); } Element.Layout = Class.create(Hash, { initialize: function($super, element, preCompute) { $super(); this.element = $(element); Element.Layout.PROPERTIES.each( function(property) { this._set(property, null); }, this); if (preCompute) { this._preComputing = true; this._begin(); Element.Layout.PROPERTIES.each( this._compute, this ); this._end(); this._preComputing = false; } }, _set: function(property, value) { return Hash.prototype.set.call(this, property, value); }, set: function(property, value) { throw "Properties of Element.Layout are read-only."; }, get: function($super, property) { var value = $super(property); return value === null ? this._compute(property) : value; }, _begin: function() { if (this._isPrepared()) return; var element = this.element; if (isDisplayed(element)) { this._setPrepared(true); return; } var originalStyles = { position: element.style.position || '', width: element.style.width || '', visibility: element.style.visibility || '', display: element.style.display || '' }; element.store('prototype_original_styles', originalStyles); var position = getRawStyle(element, 'position'), width = element.offsetWidth; if (width === 0 || width === null) { element.style.display = 'block'; width = element.offsetWidth; } var context = (position === 'fixed') ? document.viewport : element.parentNode; var tempStyles = { visibility: 'hidden', display: 'block' }; if (position !== 'fixed') tempStyles.position = 'absolute'; element.setStyle(tempStyles); var positionedWidth = element.offsetWidth, newWidth; if (width && (positionedWidth === width)) { newWidth = getContentWidth(element, context); } else if (position === 'absolute' || position === 'fixed') { newWidth = getContentWidth(element, context); } else { var parent = element.parentNode, pLayout = $(parent).getLayout(); newWidth = pLayout.get('width') - this.get('margin-left') - this.get('border-left') - this.get('padding-left') - this.get('padding-right') - this.get('border-right') - this.get('margin-right'); } element.setStyle({ width: newWidth + 'px' }); this._setPrepared(true); }, _end: function() { var element = this.element; var originalStyles = element.retrieve('prototype_original_styles'); element.store('prototype_original_styles', null); element.setStyle(originalStyles); this._setPrepared(false); }, _compute: function(property) { var COMPUTATIONS = Element.Layout.COMPUTATIONS; if (!(property in COMPUTATIONS)) { throw "Property not found."; } return this._set(property, COMPUTATIONS[property].call(this, this.element)); }, _isPrepared: function() { return this.element.retrieve('prototype_element_layout_prepared', false); }, _setPrepared: function(bool) { return this.element.store('prototype_element_layout_prepared', bool); }, toObject: function() { var args = $A(arguments); var keys = (args.length === 0) ? Element.Layout.PROPERTIES : args.join(' ').split(' '); var obj = {}; keys.each( function(key) { if (!Element.Layout.PROPERTIES.include(key)) return; var value = this.get(key); if (value != null) obj[key] = value; }, this); return obj; }, toHash: function() { var obj = this.toObject.apply(this, arguments); return new Hash(obj); }, toCSS: function() { var args = $A(arguments); var keys = (args.length === 0) ? Element.Layout.PROPERTIES : args.join(' ').split(' '); var css = {}; keys.each( function(key) { if (!Element.Layout.PROPERTIES.include(key)) return; if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; var value = this.get(key); if (value != null) css[cssNameFor(key)] = value + 'px'; }, this); return css; }, inspect: function() { return "#"; } }); Object.extend(Element.Layout, { PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), COMPUTATIONS: { 'height': function(element) { if (!this._preComputing) this._begin(); var bHeight = this.get('border-box-height'); if (bHeight <= 0) { if (!this._preComputing) this._end(); return 0; } var bTop = this.get('border-top'), bBottom = this.get('border-bottom'); var pTop = this.get('padding-top'), pBottom = this.get('padding-bottom'); if (!this._preComputing) this._end(); return bHeight - bTop - bBottom - pTop - pBottom; }, 'width': function(element) { if (!this._preComputing) this._begin(); var bWidth = this.get('border-box-width'); if (bWidth <= 0) { if (!this._preComputing) this._end(); return 0; } var bLeft = this.get('border-left'), bRight = this.get('border-right'); var pLeft = this.get('padding-left'), pRight = this.get('padding-right'); if (!this._preComputing) this._end(); return bWidth - bLeft - bRight - pLeft - pRight; }, 'padding-box-height': function(element) { var height = this.get('height'), pTop = this.get('padding-top'), pBottom = this.get('padding-bottom'); return height + pTop + pBottom; }, 'padding-box-width': function(element) { var width = this.get('width'), pLeft = this.get('padding-left'), pRight = this.get('padding-right'); return width + pLeft + pRight; }, 'border-box-height': function(element) { if (!this._preComputing) this._begin(); var height = element.offsetHeight; if (!this._preComputing) this._end(); return height; }, 'border-box-width': function(element) { if (!this._preComputing) this._begin(); var width = element.offsetWidth; if (!this._preComputing) this._end(); return width; }, 'margin-box-height': function(element) { var bHeight = this.get('border-box-height'), mTop = this.get('margin-top'), mBottom = this.get('margin-bottom'); if (bHeight <= 0) return 0; return bHeight + mTop + mBottom; }, 'margin-box-width': function(element) { var bWidth = this.get('border-box-width'), mLeft = this.get('margin-left'), mRight = this.get('margin-right'); if (bWidth <= 0) return 0; return bWidth + mLeft + mRight; }, 'top': function(element) { var offset = element.positionedOffset(); return offset.top; }, 'bottom': function(element) { var offset = element.positionedOffset(), parent = element.getOffsetParent(), pHeight = parent.measure('height'); var mHeight = this.get('border-box-height'); return pHeight - mHeight - offset.top; }, 'left': function(element) { var offset = element.positionedOffset(); return offset.left; }, 'right': function(element) { var offset = element.positionedOffset(), parent = element.getOffsetParent(), pWidth = parent.measure('width'); var mWidth = this.get('border-box-width'); return pWidth - mWidth - offset.left; }, 'padding-top': function(element) { return getPixelValue(element, 'paddingTop'); }, 'padding-bottom': function(element) { return getPixelValue(element, 'paddingBottom'); }, 'padding-left': function(element) { return getPixelValue(element, 'paddingLeft'); }, 'padding-right': function(element) { return getPixelValue(element, 'paddingRight'); }, 'border-top': function(element) { return getPixelValue(element, 'borderTopWidth'); }, 'border-bottom': function(element) { return getPixelValue(element, 'borderBottomWidth'); }, 'border-left': function(element) { return getPixelValue(element, 'borderLeftWidth'); }, 'border-right': function(element) { return getPixelValue(element, 'borderRightWidth'); }, 'margin-top': function(element) { return getPixelValue(element, 'marginTop'); }, 'margin-bottom': function(element) { return getPixelValue(element, 'marginBottom'); }, 'margin-left': function(element) { return getPixelValue(element, 'marginLeft'); }, 'margin-right': function(element) { return getPixelValue(element, 'marginRight'); } } }); if ('getBoundingClientRect' in document.documentElement) { Object.extend(Element.Layout.COMPUTATIONS, { 'right': function(element) { var parent = hasLayout(element.getOffsetParent()); var rect = element.getBoundingClientRect(), pRect = parent.getBoundingClientRect(); return (pRect.right - rect.right).round(); }, 'bottom': function(element) { var parent = hasLayout(element.getOffsetParent()); var rect = element.getBoundingClientRect(), pRect = parent.getBoundingClientRect(); return (pRect.bottom - rect.bottom).round(); } }); } Element.Offset = Class.create({ initialize: function(left, top) { this.left = left.round(); this.top = top.round(); this[0] = this.left; this[1] = this.top; }, relativeTo: function(offset) { return new Element.Offset( this.left - offset.left, this.top - offset.top ); }, inspect: function() { return "#".interpolate(this); }, toString: function() { return "[#{left}, #{top}]".interpolate(this); }, toArray: function() { return [this.left, this.top]; } }); function getLayout(element, preCompute) { return new Element.Layout(element, preCompute); } function measure(element, property) { return $(element).getLayout().get(property); } function getHeight(element) { return Element.getDimensions(element).height; } function getWidth(element) { return Element.getDimensions(element).width; } function getDimensions(element) { element = $(element); var display = Element.getStyle(element, 'display'); if (display && display !== 'none') { return { width: element.offsetWidth, height: element.offsetHeight }; } var style = element.style; var originalStyles = { visibility: style.visibility, position: style.position, display: style.display }; var newStyles = { visibility: 'hidden', display: 'block' }; if (originalStyles.position !== 'fixed') newStyles.position = 'absolute'; Element.setStyle(element, newStyles); var dimensions = { width: element.offsetWidth, height: element.offsetHeight }; Element.setStyle(element, originalStyles); return dimensions; } function getOffsetParent(element) { element = $(element); function selfOrBody(element) { return isHtml(element) ? $(document.body) : $(element); } if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) return $(document.body); var isInline = (Element.getStyle(element, 'display') === 'inline'); if (!isInline && element.offsetParent) return selfOrBody(element.offsetParent); while ((element = element.parentNode) && element !== document.body) { if (Element.getStyle(element, 'position') !== 'static') { return selfOrBody(element); } } return $(document.body); } function cumulativeOffset(element) { element = $(element); var valueT = 0, valueL = 0; if (element.parentNode) { do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; } while (element); } return new Element.Offset(valueL, valueT); } function positionedOffset(element) { element = $(element); var layout = element.getLayout(); var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; if (element) { if (isBody(element)) break; var p = Element.getStyle(element, 'position'); if (p !== 'static') break; } } while (element); valueL -= layout.get('margin-left'); valueT -= layout.get('margin-top'); return new Element.Offset(valueL, valueT); } function cumulativeScrollOffset(element) { var valueT = 0, valueL = 0; do { if (element === document.body) { var bodyScrollNode = document.documentElement || document.body.parentNode || document.body; valueT += !Object.isUndefined(window.pageYOffset) ? window.pageYOffset : bodyScrollNode.scrollTop || 0; valueL += !Object.isUndefined(window.pageXOffset) ? window.pageXOffset : bodyScrollNode.scrollLeft || 0; break; } else { valueT += element.scrollTop || 0; valueL += element.scrollLeft || 0; element = element.parentNode; } } while (element); return new Element.Offset(valueL, valueT); } function viewportOffset(forElement) { var valueT = 0, valueL = 0, docBody = document.body; forElement = $(forElement); var element = forElement; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; if (element.offsetParent == docBody && Element.getStyle(element, 'position') == 'absolute') break; } while (element = element.offsetParent); element = forElement; do { if (element != docBody) { valueT -= element.scrollTop || 0; valueL -= element.scrollLeft || 0; } } while (element = element.parentNode); return new Element.Offset(valueL, valueT); } function absolutize(element) { element = $(element); if (Element.getStyle(element, 'position') === 'absolute') { return element; } var offsetParent = getOffsetParent(element); var eOffset = element.viewportOffset(), pOffset = offsetParent.viewportOffset(); var offset = eOffset.relativeTo(pOffset); var layout = element.getLayout(); element.store('prototype_absolutize_original_styles', { position: element.getStyle('position'), left: element.getStyle('left'), top: element.getStyle('top'), width: element.getStyle('width'), height: element.getStyle('height') }); element.setStyle({ position: 'absolute', top: offset.top + 'px', left: offset.left + 'px', width: layout.get('width') + 'px', height: layout.get('height') + 'px' }); return element; } function relativize(element) { element = $(element); if (Element.getStyle(element, 'position') === 'relative') { return element; } var originalStyles = element.retrieve('prototype_absolutize_original_styles'); if (originalStyles) element.setStyle(originalStyles); return element; } function scrollTo(element) { element = $(element); var pos = Element.cumulativeOffset(element); window.scrollTo(pos.left, pos.top); return element; } function makePositioned(element) { element = $(element); var position = Element.getStyle(element, 'position'), styles = {}; if (position === 'static' || !position) { styles.position = 'relative'; if (Prototype.Browser.Opera) { styles.top = 0; styles.left = 0; } Element.setStyle(element, styles); Element.store(element, 'prototype_made_positioned', true); } return element; } function undoPositioned(element) { element = $(element); var storage = Element.getStorage(element), madePositioned = storage.get('prototype_made_positioned'); if (madePositioned) { storage.unset('prototype_made_positioned'); Element.setStyle(element, { position: '', top: '', bottom: '', left: '', right: '' }); } return element; } function makeClipping(element) { element = $(element); var storage = Element.getStorage(element), madeClipping = storage.get('prototype_made_clipping'); if (Object.isUndefined(madeClipping)) { var overflow = Element.getStyle(element, 'overflow'); storage.set('prototype_made_clipping', overflow); if (overflow !== 'hidden') element.style.overflow = 'hidden'; } return element; } function undoClipping(element) { element = $(element); var storage = Element.getStorage(element), overflow = storage.get('prototype_made_clipping'); if (!Object.isUndefined(overflow)) { storage.unset('prototype_made_clipping'); element.style.overflow = overflow || ''; } return element; } function clonePosition(element, source, options) { options = Object.extend({ setLeft: true, setTop: true, setWidth: true, setHeight: true, offsetTop: 0, offsetLeft: 0 }, options || {}); var docEl = document.documentElement; source = $(source); element = $(element); var p, delta, layout, styles = {}; if (options.setLeft || options.setTop) { p = Element.viewportOffset(source); delta = [0, 0]; if (Element.getStyle(element, 'position') === 'absolute') { var parent = Element.getOffsetParent(element); if (parent !== document.body) delta = Element.viewportOffset(parent); } } function pageScrollXY() { var x = 0, y = 0; if (Object.isNumber(window.pageXOffset)) { x = window.pageXOffset; y = window.pageYOffset; } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { x = document.body.scrollLeft; y = document.body.scrollTop; } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) { x = docEl.scrollLeft; y = docEl.scrollTop; } return { x: x, y: y }; } var pageXY = pageScrollXY(); if (options.setWidth || options.setHeight) { layout = Element.getLayout(source); } if (options.setLeft) styles.left = (p[0] + pageXY.x - delta[0] + options.offsetLeft) + 'px'; if (options.setTop) styles.top = (p[1] + pageXY.y - delta[1] + options.offsetTop) + 'px'; var currentLayout = element.getLayout(); if (options.setWidth) { styles.width = layout.get('width') + 'px'; } if (options.setHeight) { styles.height = layout.get('height') + 'px'; } return Element.setStyle(element, styles); } if (Prototype.Browser.IE) { getOffsetParent = getOffsetParent.wrap( function(proceed, element) { element = $(element); if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) return $(document.body); var position = element.getStyle('position'); if (position !== 'static') return proceed(element); element.setStyle({ position: 'relative' }); var value = proceed(element); element.setStyle({ position: position }); return value; } ); positionedOffset = positionedOffset.wrap(function(proceed, element) { element = $(element); if (!element.parentNode) return new Element.Offset(0, 0); var position = element.getStyle('position'); if (position !== 'static') return proceed(element); var offsetParent = element.getOffsetParent(); if (offsetParent && offsetParent.getStyle('position') === 'fixed') hasLayout(offsetParent); element.setStyle({ position: 'relative' }); var value = proceed(element); element.setStyle({ position: position }); return value; }); } else if (Prototype.Browser.Webkit) { cumulativeOffset = function(element) { element = $(element); var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; if (element.offsetParent == document.body) { if (Element.getStyle(element, 'position') == 'absolute') break; } element = element.offsetParent; } while (element); return new Element.Offset(valueL, valueT); }; } Element.addMethods({ getLayout: getLayout, measure: measure, getWidth: getWidth, getHeight: getHeight, getDimensions: getDimensions, getOffsetParent: getOffsetParent, cumulativeOffset: cumulativeOffset, positionedOffset: positionedOffset, cumulativeScrollOffset: cumulativeScrollOffset, viewportOffset: viewportOffset, absolutize: absolutize, relativize: relativize, scrollTo: scrollTo, makePositioned: makePositioned, undoPositioned: undoPositioned, makeClipping: makeClipping, undoClipping: undoClipping, clonePosition: clonePosition }); function isBody(element) { return element.nodeName.toUpperCase() === 'BODY'; } function isHtml(element) { return element.nodeName.toUpperCase() === 'HTML'; } function isDocument(element) { return element.nodeType === Node.DOCUMENT_NODE; } function isDetached(element) { return element !== document.body && !Element.descendantOf(element, document.body); } if ('getBoundingClientRect' in document.documentElement) { Element.addMethods({ viewportOffset: function(element) { element = $(element); if (isDetached(element)) return new Element.Offset(0, 0); var rect = element.getBoundingClientRect(), docEl = document.documentElement; return new Element.Offset(rect.left - docEl.clientLeft, rect.top - docEl.clientTop); } }); } })(); (function() { var IS_OLD_OPERA = Prototype.Browser.Opera && (window.parseFloat(window.opera.version()) < 9.5); var ROOT = null; function getRootElement() { if (ROOT) return ROOT; ROOT = IS_OLD_OPERA ? document.body : document.documentElement; return ROOT; } function getDimensions() { return { width: this.getWidth(), height: this.getHeight() }; } function getWidth() { return getRootElement().clientWidth; } function getHeight() { return getRootElement().clientHeight; } function getScrollOffsets() { var x = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; var y = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; return new Element.Offset(x, y); } document.viewport = { getDimensions: getDimensions, getWidth: getWidth, getHeight: getHeight, getScrollOffsets: getScrollOffsets }; })(); window.$$ = function() { var expression = $A(arguments).join(', '); return Prototype.Selector.select(expression, document); }; Prototype.Selector = (function() { function select() { throw new Error('Method "Prototype.Selector.select" must be defined.'); } function match() { throw new Error('Method "Prototype.Selector.match" must be defined.'); } function find(elements, expression, index) { index = index || 0; var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; for (i = 0; i < length; i++) { if (match(elements[i], expression) && index == matchIndex++) { return Element.extend(elements[i]); } } } function extendElements(elements) { for (var i = 0, length = elements.length; i < length; i++) { Element.extend(elements[i]); } return elements; } var K = Prototype.K; return { select: select, match: match, find: find, extendElements: (Element.extend === K) ? K : extendElements, extendElement: Element.extend }; })(); Prototype._original_property = window.Sizzle; ;(function () { function fakeDefine(fn) { Prototype._actual_sizzle = fn(); } fakeDefine.amd = true; if (typeof define !== 'undefined' && define.amd) { Prototype._original_define = define; Prototype._actual_sizzle = null; window.define = fakeDefine; } })(); /*! * Sizzle CSS Selector Engine v1.10.18 * http://sizzlejs.com/ * * Copyright 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2014-02-05 */ (function( window ) { var i, support, Expr, getText, isXML, compile, select, outermostContext, sortInput, hasDuplicate, setDocument, document, docElem, documentIsHTML, rbuggyQSA, rbuggyMatches, matches, contains, expando = "sizzle" + -(new Date()), preferredDoc = window.document, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; } return 0; }, strundefined = typeof undefined, MAX_NEGATIVE = 1 << 31, hasOwn = ({}).hasOwnProperty, arr = [], pop = arr.pop, push_native = arr.push, push = arr.push, slice = arr.slice, indexOf = arr.indexOf || function( elem ) { var i = 0, len = this.length; for ( ; i < len; i++ ) { if ( this[i] === elem ) { return i; } } return -1; }, booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", whitespace = "[\\x20\\t\\r\\n\\f]", characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", identifier = characterEncoding.replace( "w", "w#" ), attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + characterEncoding + ")" ), "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rnative = /^[^{]+\{\s*\[native \w/, rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, rescape = /'|\\/g, runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; return high !== high || escapedWhitespace ? escaped : high < 0 ? String.fromCharCode( high + 0x10000 ) : String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }; try { push.apply( (arr = slice.call( preferredDoc.childNodes )), preferredDoc.childNodes ); arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { push = { apply: arr.length ? function( target, els ) { push_native.apply( target, slice.call(els) ); } : function( target, els ) { var j = target.length, i = 0; while ( (target[j++] = els[i++]) ) {} target.length = j - 1; } }; } function Sizzle( selector, context, results, seed ) { var match, elem, m, nodeType, i, groups, old, nid, newContext, newSelector; if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; results = results || []; if ( !selector || typeof selector !== "string" ) { return results; } if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { return []; } if ( documentIsHTML && !seed ) { if ( (match = rquickExpr.exec( selector )) ) { if ( (m = match[1]) ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); if ( elem && elem.parentNode ) { if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } } else { if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } } else if ( match[2] ) { push.apply( results, context.getElementsByTagName( selector ) ); return results; } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; newSelector = nodeType === 9 && selector; if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector ); if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] "; i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); } if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } } } return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Create key-value caches of limited size * @returns {Function(string, Object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var keys = []; function cache( key, value ) { if ( keys.push( key + " " ) > Expr.cacheLength ) { delete cache[ keys.shift() ]; } return (cache[ key + " " ] = value); } return cache; } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created div and expects a boolean result */ function assert( fn ) { var div = document.createElement("div"); try { return !!fn( div ); } catch (e) { return false; } finally { if ( div.parentNode ) { div.parentNode.removeChild( div ); } div = null; } } /** * Adds the same handler for all of the specified attrs * @param {String} attrs Pipe-separated list of attributes * @param {Function} handler The method that will be applied */ function addHandle( attrs, handler ) { var arr = attrs.split("|"), i = attrs.length; while ( i-- ) { Expr.attrHandle[ arr[i] ] = handler; } } /** * Checks document order of two siblings * @param {Element} a * @param {Element} b * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b */ function siblingCheck( a, b ) { var cur = b && a, diff = cur && a.nodeType === 1 && b.nodeType === 1 && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); if ( diff ) { return diff; } if ( cur ) { while ( (cur = cur.nextSibling) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } /** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } /** * Returns a function to use in pseudos for buttons * @param {String} type */ function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } /** * Returns a function to use in pseudos for positionals * @param {Function} fn */ function createPositionalPseudo( fn ) { return markFunction(function( argument ) { argument = +argument; return markFunction(function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; while ( i-- ) { if ( seed[ (j = matchIndexes[i]) ] ) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Checks a node for validity as a Sizzle context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ function testContext( context ) { return context && typeof context.getElementsByTagName !== strundefined && context; } support = Sizzle.support = {}; /** * Detects XML nodes * @param {Element|Object} elem An element or a document * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var hasCompare, doc = node ? node.ownerDocument || node : preferredDoc, parent = doc.defaultView; if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } document = doc; docElem = doc.documentElement; documentIsHTML = !isXML( doc ); if ( parent && parent !== parent.top ) { if ( parent.addEventListener ) { parent.addEventListener( "unload", function() { setDocument(); }, false ); } else if ( parent.attachEvent ) { parent.attachEvent( "onunload", function() { setDocument(); }); } } /* Attributes ---------------------------------------------------------------------- */ support.attributes = assert(function( div ) { div.className = "i"; return !div.getAttribute("className"); }); /* getElement(s)By* ---------------------------------------------------------------------- */ support.getElementsByTagName = assert(function( div ) { div.appendChild( doc.createComment("") ); return !div.getElementsByTagName("*").length; }); support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { div.innerHTML = "
"; div.firstChild.className = "i"; return div.getElementsByClassName("i").length === 2; }); support.getById = assert(function( div ) { docElem.appendChild( div ).id = expando; return !doc.getElementsByName || !doc.getElementsByName( expando ).length; }); if ( support.getById ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== strundefined && documentIsHTML ) { var m = context.getElementById( id ); return m && m.parentNode ? [m] : []; } }; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; } else { delete Expr.find["ID"]; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; } Expr.find["TAG"] = support.getElementsByTagName ? function( tag, context ) { if ( typeof context.getElementsByTagName !== strundefined ) { return context.getElementsByTagName( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, results = context.getElementsByTagName( tag ); if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { return context.getElementsByClassName( className ); } }; /* QSA/matchesSelector ---------------------------------------------------------------------- */ rbuggyMatches = []; rbuggyQSA = []; if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { assert(function( div ) { div.innerHTML = ""; if ( div.querySelectorAll("[t^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); } if ( !div.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); } if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } }); assert(function( div ) { var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); div.appendChild( input ).setAttribute( "name", "D" ); if ( div.querySelectorAll("[name=d]").length ) { rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } if ( !div.querySelectorAll(":enabled").length ) { rbuggyQSA.push( ":enabled", ":disabled" ); } div.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( div ) { support.disconnectedMatch = matches.call( div, "div" ); matches.call( div, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); /* Contains ---------------------------------------------------------------------- */ hasCompare = rnative.test( docElem.compareDocumentPosition ); contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; /* Sorting ---------------------------------------------------------------------- */ sortOrder = hasCompare ? function( a, b ) { if ( a === b ) { hasDuplicate = true; return 0; } var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { return compare; } compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? a.compareDocumentPosition( b ) : 1; if ( compare & 1 || (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { return -1; } if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { return 1; } return sortInput ? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; } : function( a, b ) { if ( a === b ) { hasDuplicate = true; return 0; } var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; if ( !aup || !bup ) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : sortInput ? ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; } else if ( aup === bup ) { return siblingCheck( a, b ); } cur = a; while ( (cur = cur.parentNode) ) { ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { bp.unshift( cur ); } while ( ap[i] === bp[i] ) { i++; } return i ? siblingCheck( ap[i], bp[i] ) : ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; return doc; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } expr = expr.replace( rattributeQuotes, "='$1']" ); if ( support.matchesSelector && documentIsHTML && ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); if ( ret || support.disconnectedMatch || elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch(e) {} } return Sizzle( expr, document, null, [elem] ).length > 0; }; Sizzle.contains = function( context, elem ) { if ( ( context.ownerDocument || context ) !== document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } var fn = Expr.attrHandle[ name.toLowerCase() ], val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined; return val !== undefined ? val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : null; }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; /** * Document sorting and removing duplicates * @param {ArrayLike} results */ Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; hasDuplicate = !support.detectDuplicates; sortInput = !support.sortStable && results.slice( 0 ); results.sort( sortOrder ); if ( hasDuplicate ) { while ( (elem = results[i++]) ) { if ( elem === results[ i ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } sortInput = null; return results; }; /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { while ( (node = elem[i++]) ) { ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } return ret; }; Expr = Sizzle.selectors = { cacheLength: 50, createPseudo: markFunction, match: matchExpr, attrHandle: {}, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) { if ( !match[3] ) { Sizzle.error( match[0] ); } match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); } else if ( match[3] ) { Sizzle.error( match[0] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[5] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } if ( match[3] && match[4] !== undefined ) { match[2] = match[4]; } else if ( unquoted && rpseudo.test( unquoted ) && (excess = tokenize( unquoted, true )) && (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { match[0] = match[0].slice( 0, excess ); match[2] = unquoted.slice( 0, excess ); } return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); }); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; }, "CHILD": function( type, what, argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? function( elem ) { return !!elem.parentNode; } : function( elem, context, xml ) { var cache, outerCache, node, diff, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType; if ( parent ) { if ( simple ) { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; if ( forward && useCache ) { outerCache = parent[ expando ] || (parent[ expando ] = {}); cache = outerCache[ type ] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = cache[0] === dirruns && cache[2]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( node.nodeType === 1 && ++diff && node === elem ) { outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { diff = cache[1]; } else { while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { if ( useCache ) { (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); if ( fn[ expando ] ) { return fn( argument ); } if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction(function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf.call( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { "not": markFunction(function( selector ) { var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction(function( seed, matches, context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; while ( i-- ) { if ( (elem = unmatched[i]) ) { seed[i] = !(matches[i] = elem); } } }) : function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); return !results.pop(); }; }), "has": markFunction(function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; }), "contains": markFunction(function( text ) { return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), "lang": markFunction( function( lang ) { if ( !ridentifier.test(lang || "") ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( (elemLang = documentIsHTML ? elem.lang : elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); return false; }; }), "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, "enabled": function( elem ) { return elem.disabled === false; }, "disabled": function( elem ) { return elem.disabled === true; }, "checked": function( elem ) { var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function( elem ) { if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, "empty": function( elem ) { for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeType < 6 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos["empty"]( elem ); }, "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); }, "first": createPositionalPseudo(function() { return [ 0 ]; }), "last": createPositionalPseudo(function( matchIndexes, length ) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "odd": createPositionalPseudo(function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; }) } }; Expr.pseudos["nth"] = Expr.pseudos["eq"]; for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); function tokenize( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { soFar = soFar.slice( match[0].length ) || soFar; } groups.push( (tokens = []) ); } matched = false; if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push({ value: matched, type: match[0].replace( rtrim, " " ) }); soFar = soFar.slice( matched.length ); } for ( type in Expr.filter ) { if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) { matched = match.shift(); tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : tokenCache( selector, groups ).slice( 0 ); } function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[i].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", doneName = done++; return combinator.first ? function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } } : function( elem, context, xml ) { var oldCache, outerCache, newCache = [ dirruns, doneName ]; if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); if ( (oldCache = outerCache[ dir ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { return (newCache[ 2 ] = oldCache[ 2 ]); } else { outerCache[ dir ] = newCache; if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { return true; } } } } } }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; } : matchers[0]; } function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[i], results ); } return results; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? postFinder || ( seed ? preFilter : preexisting || postFilter ) ? [] : results : matcherIn; if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf.call( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); } ]; for ( ; i < len; i++ ) { if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); if ( matcher[ expando ] ) { j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[j].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if ( outermost ) { outermostContext = context !== document && context; } for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; while ( (matcher = elementMatchers[j++]) ) { if ( matcher( elem, context, xml ) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; } } if ( bySet ) { if ( (elem = !matcher && elem) ) { matchedCount--; } if ( seed ) { unmatched.push( elem ); } } } matchedCount += i; if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } setMatched = condense( setMatched ); } push.apply( results, setMatched ); if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { if ( !match ) { match = tokenize( selector ); } i = match.length; while ( i-- ) { cached = matcherFromTokens( match[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); cached.selector = selector; } return cached; }; /** * A low-level selection function that works with Sizzle's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled * selector function built with Sizzle.compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( (selector = compiled.selector || selector) ); results = results || []; if ( match.length === 1 ) { tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && support.getById && context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; if ( !context ) { return results; } else if ( compiled ) { context = context.parentNode; } selector = selector.slice( tokens.shift().value.length ); } i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[i]; if ( Expr.relative[ (type = token.type) ] ) { break; } if ( (find = Expr.find[ type ]) ) { if ( (seed = find( token.matches[0].replace( runescape, funescape ), rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context )) ) { tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, seed ); return results; } break; } } } } ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; }; support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; support.detectDuplicates = !!hasDuplicate; setDocument(); support.sortDetached = assert(function( div1 ) { return div1.compareDocumentPosition( document.createElement("div") ) & 1; }); if ( !assert(function( div ) { div.innerHTML = ""; return div.firstChild.getAttribute("href") === "#" ; }) ) { addHandle( "type|href|height|width", function( elem, name, isXML ) { if ( !isXML ) { return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); } }); } if ( !support.attributes || !assert(function( div ) { div.innerHTML = ""; div.firstChild.setAttribute( "value", "" ); return div.firstChild.getAttribute( "value" ) === ""; }) ) { addHandle( "value", function( elem, name, isXML ) { if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { return elem.defaultValue; } }); } if ( !assert(function( div ) { return div.getAttribute("disabled") == null; }) ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { return elem[ name ] === true ? name.toLowerCase() : (val = elem.getAttributeNode( name )) && val.specified ? val.value : null; } }); } if ( typeof define === "function" && define.amd ) { define(function() { return Sizzle; }); } else if ( typeof module !== "undefined" && module.exports ) { module.exports = Sizzle; } else { window.Sizzle = Sizzle; } })( window ); ;(function() { if (typeof Sizzle !== 'undefined') { return; } if (typeof define !== 'undefined' && define.amd) { window.Sizzle = Prototype._actual_sizzle; window.define = Prototype._original_define; delete Prototype._actual_sizzle; delete Prototype._original_define; } else if (typeof module !== 'undefined' && module.exports) { window.Sizzle = module.exports; module.exports = {}; } })(); ;(function(engine) { var extendElements = Prototype.Selector.extendElements; function select(selector, scope) { return extendElements(engine(selector, scope || document)); } function match(element, selector) { return engine.matches(selector, [element]).length == 1; } Prototype.Selector.engine = engine; Prototype.Selector.select = select; Prototype.Selector.match = match; })(Sizzle); window.Sizzle = Prototype._original_property; delete Prototype._original_property; var Form = { reset: function(form) { form = $(form); form.reset(); return form; }, serializeElements: function(elements, options) { if (typeof options != 'object') options = { hash: !!options }; else if (Object.isUndefined(options.hash)) options.hash = true; var key, value, submitted = false, submit = options.submit, accumulator, initial; if (options.hash) { initial = {}; accumulator = function(result, key, value) { if (key in result) { if (!Object.isArray(result[key])) result[key] = [result[key]]; result[key] = result[key].concat(value); } else result[key] = value; return result; }; } else { initial = ''; accumulator = function(result, key, values) { if (!Object.isArray(values)) {values = [values];} if (!values.length) {return result;} var encodedKey = encodeURIComponent(key).gsub(/%20/, '+'); return result + (result ? "&" : "") + values.map(function (value) { value = value.gsub(/(\r)?\n/, '\r\n'); value = encodeURIComponent(value); value = value.gsub(/%20/, '+'); return encodedKey + "=" + value; }).join("&"); }; } return elements.inject(initial, function(result, element) { if (!element.disabled && element.name) { key = element.name; value = $(element).getValue(); if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && submit !== false && (!submit || key == submit) && (submitted = true)))) { result = accumulator(result, key, value); } } return result; }); } }; Form.Methods = { serialize: function(form, options) { return Form.serializeElements(Form.getElements(form), options); }, getElements: function(form) { var elements = $(form).getElementsByTagName('*'); var element, results = [], serializers = Form.Element.Serializers; for (var i = 0; element = elements[i]; i++) { if (serializers[element.tagName.toLowerCase()]) results.push(Element.extend(element)); } return results; }, getInputs: function(form, typeName, name) { form = $(form); var inputs = form.getElementsByTagName('input'); if (!typeName && !name) return $A(inputs).map(Element.extend); for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { var input = inputs[i]; if ((typeName && input.type != typeName) || (name && input.name != name)) continue; matchingInputs.push(Element.extend(input)); } return matchingInputs; }, disable: function(form) { form = $(form); Form.getElements(form).invoke('disable'); return form; }, enable: function(form) { form = $(form); Form.getElements(form).invoke('enable'); return form; }, findFirstElement: function(form) { var elements = $(form).getElements().findAll(function(element) { return 'hidden' != element.type && !element.disabled; }); var firstByIndex = elements.findAll(function(element) { return element.hasAttribute('tabIndex') && element.tabIndex >= 0; }).sortBy(function(element) { return element.tabIndex }).first(); return firstByIndex ? firstByIndex : elements.find(function(element) { return /^(?:input|select|textarea)$/i.test(element.tagName); }); }, focusFirstElement: function(form) { form = $(form); var element = form.findFirstElement(); if (element) element.activate(); return form; }, request: function(form, options) { form = $(form), options = Object.clone(options || { }); var params = options.parameters, action = form.readAttribute('action') || ''; if (action.blank()) action = window.location.href; options.parameters = form.serialize(true); if (params) { if (Object.isString(params)) params = params.toQueryParams(); Object.extend(options.parameters, params); } if (form.hasAttribute('method') && !options.method) options.method = form.method; return new Ajax.Request(action, options); } }; /*--------------------------------------------------------------------------*/ Form.Element = { focus: function(element) { $(element).focus(); return element; }, select: function(element) { $(element).select(); return element; } }; Form.Element.Methods = { serialize: function(element) { element = $(element); if (!element.disabled && element.name) { var value = element.getValue(); if (value != undefined) { var pair = { }; pair[element.name] = value; return Object.toQueryString(pair); } } return ''; }, getValue: function(element) { element = $(element); var method = element.tagName.toLowerCase(); return Form.Element.Serializers[method](element); }, setValue: function(element, value) { element = $(element); var method = element.tagName.toLowerCase(); Form.Element.Serializers[method](element, value); return element; }, clear: function(element) { $(element).value = ''; return element; }, present: function(element) { return $(element).value != ''; }, activate: function(element) { element = $(element); try { element.focus(); if (element.select && (element.tagName.toLowerCase() != 'input' || !(/^(?:button|reset|submit)$/i.test(element.type)))) element.select(); } catch (e) { } return element; }, disable: function(element) { element = $(element); element.disabled = true; return element; }, enable: function(element) { element = $(element); element.disabled = false; return element; } }; /*--------------------------------------------------------------------------*/ var Field = Form.Element; var $F = Form.Element.Methods.getValue; /*--------------------------------------------------------------------------*/ Form.Element.Serializers = (function() { function input(element, value) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': return inputSelector(element, value); default: return valueSelector(element, value); } } function inputSelector(element, value) { if (Object.isUndefined(value)) return element.checked ? element.value : null; else element.checked = !!value; } function valueSelector(element, value) { if (Object.isUndefined(value)) return element.value; else element.value = value; } function select(element, value) { if (Object.isUndefined(value)) return (element.type === 'select-one' ? selectOne : selectMany)(element); var opt, currentValue, single = !Object.isArray(value); for (var i = 0, length = element.length; i < length; i++) { opt = element.options[i]; currentValue = this.optionValue(opt); if (single) { if (currentValue == value) { opt.selected = true; return; } } else opt.selected = value.include(currentValue); } } function selectOne(element) { var index = element.selectedIndex; return index >= 0 ? optionValue(element.options[index]) : null; } function selectMany(element) { var values, length = element.length; if (!length) return null; for (var i = 0, values = []; i < length; i++) { var opt = element.options[i]; if (opt.selected) values.push(optionValue(opt)); } return values; } function optionValue(opt) { return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; } return { input: input, inputSelector: inputSelector, textarea: valueSelector, select: select, selectOne: selectOne, selectMany: selectMany, optionValue: optionValue, button: valueSelector }; })(); /*--------------------------------------------------------------------------*/ Abstract.TimedObserver = Class.create(PeriodicalExecuter, { initialize: function($super, element, frequency, callback) { $super(callback, frequency); this.element = $(element); this.lastValue = this.getValue(); }, execute: function() { var value = this.getValue(); if (Object.isString(this.lastValue) && Object.isString(value) ? this.lastValue != value : String(this.lastValue) != String(value)) { this.callback(this.element, value); this.lastValue = value; } } }); Form.Element.Observer = Class.create(Abstract.TimedObserver, { getValue: function() { return Form.Element.getValue(this.element); } }); Form.Observer = Class.create(Abstract.TimedObserver, { getValue: function() { return Form.serialize(this.element); } }); /*--------------------------------------------------------------------------*/ Abstract.EventObserver = Class.create({ 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() { Form.getElements(this.element).each(this.registerCallback, this); }, registerCallback: function(element) { if (element.type) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': Event.observe(element, 'click', this.onElementEvent.bind(this)); break; default: Event.observe(element, 'change', this.onElementEvent.bind(this)); break; } } } }); Form.Element.EventObserver = Class.create(Abstract.EventObserver, { getValue: function() { return Form.Element.getValue(this.element); } }); Form.EventObserver = Class.create(Abstract.EventObserver, { getValue: function() { return Form.serialize(this.element); } }); (function(GLOBAL) { var DIV = document.createElement('div'); var docEl = document.documentElement; var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl && 'onmouseleave' in docEl; var 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, KEY_HOME: 36, KEY_END: 35, KEY_PAGEUP: 33, KEY_PAGEDOWN: 34, KEY_INSERT: 45 }; var isIELegacyEvent = function(event) { return false; }; if (window.attachEvent) { if (window.addEventListener) { isIELegacyEvent = function(event) { return !(event instanceof window.Event); }; } else { isIELegacyEvent = function(event) { return true; }; } } var _isButton; function _isButtonForDOMEvents(event, code) { return event.which ? (event.which === code + 1) : (event.button === code); } var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; function _isButtonForLegacyEvents(event, code) { return event.button === legacyButtonMap[code]; } function _isButtonForWebKit(event, code) { switch (code) { case 0: return event.which == 1 && !event.metaKey; case 1: return event.which == 2 || (event.which == 1 && event.metaKey); case 2: return event.which == 3; default: return false; } } if (window.attachEvent) { if (!window.addEventListener) { _isButton = _isButtonForLegacyEvents; } else { _isButton = function(event, code) { return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : _isButtonForDOMEvents(event, code); } } } else if (Prototype.Browser.WebKit) { _isButton = _isButtonForWebKit; } else { _isButton = _isButtonForDOMEvents; } function isLeftClick(event) { return _isButton(event, 0) } function isMiddleClick(event) { return _isButton(event, 1) } function isRightClick(event) { return _isButton(event, 2) } function element(event) { return Element.extend(_element(event)); } function _element(event) { event = Event.extend(event); var node = event.target, type = event.type, currentTarget = event.currentTarget; if (currentTarget && currentTarget.tagName) { if (type === 'load' || type === 'error' || (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' && currentTarget.type === 'radio')) node = currentTarget; } return node.nodeType == Node.TEXT_NODE ? node.parentNode : node; } function findElement(event, expression) { var element = _element(event), selector = Prototype.Selector; if (!expression) return Element.extend(element); while (element) { if (Object.isElement(element) && selector.match(element, expression)) return Element.extend(element); element = element.parentNode; } } function pointer(event) { return { x: pointerX(event), y: pointerY(event) }; } function pointerX(event) { var docElement = document.documentElement, body = document.body || { scrollLeft: 0 }; return event.pageX || (event.clientX + (docElement.scrollLeft || body.scrollLeft) - (docElement.clientLeft || 0)); } function pointerY(event) { var docElement = document.documentElement, body = document.body || { scrollTop: 0 }; return event.pageY || (event.clientY + (docElement.scrollTop || body.scrollTop) - (docElement.clientTop || 0)); } function stop(event) { Event.extend(event); event.preventDefault(); event.stopPropagation(); event.stopped = true; } Event.Methods = { isLeftClick: isLeftClick, isMiddleClick: isMiddleClick, isRightClick: isRightClick, element: element, findElement: findElement, pointer: pointer, pointerX: pointerX, pointerY: pointerY, stop: stop }; var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { m[name] = Event.Methods[name].methodize(); return m; }); if (window.attachEvent) { function _relatedTarget(event) { var element; switch (event.type) { case 'mouseover': case 'mouseenter': element = event.fromElement; break; case 'mouseout': case 'mouseleave': element = event.toElement; break; default: return null; } return Element.extend(element); } var additionalMethods = { stopPropagation: function() { this.cancelBubble = true }, preventDefault: function() { this.returnValue = false }, inspect: function() { return '[object Event]' } }; Event.extend = function(event, element) { if (!event) return false; if (!isIELegacyEvent(event)) return event; if (event._extendedByPrototype) return event; event._extendedByPrototype = Prototype.emptyFunction; var pointer = Event.pointer(event); Object.extend(event, { target: event.srcElement || element, relatedTarget: _relatedTarget(event), pageX: pointer.x, pageY: pointer.y }); Object.extend(event, methods); Object.extend(event, additionalMethods); return event; }; } else { Event.extend = Prototype.K; } if (window.addEventListener) { Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; Object.extend(Event.prototype, methods); } var EVENT_TRANSLATIONS = { mouseenter: 'mouseover', mouseleave: 'mouseout' }; function getDOMEventName(eventName) { return EVENT_TRANSLATIONS[eventName] || eventName; } if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) getDOMEventName = Prototype.K; function getUniqueElementID(element) { if (element === window) return 0; if (typeof element._prototypeUID === 'undefined') element._prototypeUID = Element.Storage.UID++; return element._prototypeUID; } function getUniqueElementID_IE(element) { if (element === window) return 0; if (element == document) return 1; return element.uniqueID; } if ('uniqueID' in DIV) getUniqueElementID = getUniqueElementID_IE; function isCustomEvent(eventName) { return eventName.include(':'); } Event._isCustomEvent = isCustomEvent; function getOrCreateRegistryFor(element, uid) { var CACHE = GLOBAL.Event.cache; if (Object.isUndefined(uid)) uid = getUniqueElementID(element); if (!CACHE[uid]) CACHE[uid] = { element: element }; return CACHE[uid]; } function destroyRegistryForElement(element, uid) { if (Object.isUndefined(uid)) uid = getUniqueElementID(element); delete GLOBAL.Event.cache[uid]; } function register(element, eventName, handler) { var registry = getOrCreateRegistryFor(element); if (!registry[eventName]) registry[eventName] = []; var entries = registry[eventName]; var i = entries.length; while (i--) if (entries[i].handler === handler) return null; var uid = getUniqueElementID(element); var responder = GLOBAL.Event._createResponder(uid, eventName, handler); var entry = { responder: responder, handler: handler }; entries.push(entry); return entry; } function unregister(element, eventName, handler) { var registry = getOrCreateRegistryFor(element); var entries = registry[eventName] || []; var i = entries.length, entry; while (i--) { if (entries[i].handler === handler) { entry = entries[i]; break; } } if (entry) { var index = entries.indexOf(entry); entries.splice(index, 1); } if (entries.length === 0) { delete registry[eventName]; if (Object.keys(registry).length === 1 && ('element' in registry)) destroyRegistryForElement(element); } return entry; } function observe(element, eventName, handler) { element = $(element); var entry = register(element, eventName, handler); if (entry === null) return element; var responder = entry.responder; if (isCustomEvent(eventName)) observeCustomEvent(element, eventName, responder); else observeStandardEvent(element, eventName, responder); return element; } function observeStandardEvent(element, eventName, responder) { var actualEventName = getDOMEventName(eventName); if (element.addEventListener) { element.addEventListener(actualEventName, responder, false); } else { element.attachEvent('on' + actualEventName, responder); } } function observeCustomEvent(element, eventName, responder) { if (element.addEventListener) { element.addEventListener('dataavailable', responder, false); } else { element.attachEvent('ondataavailable', responder); element.attachEvent('onlosecapture', responder); } } function stopObserving(element, eventName, handler) { element = $(element); var handlerGiven = !Object.isUndefined(handler), eventNameGiven = !Object.isUndefined(eventName); if (!eventNameGiven && !handlerGiven) { stopObservingElement(element); return element; } if (!handlerGiven) { stopObservingEventName(element, eventName); return element; } var entry = unregister(element, eventName, handler); if (!entry) return element; removeEvent(element, eventName, entry.responder); return element; } function stopObservingStandardEvent(element, eventName, responder) { var actualEventName = getDOMEventName(eventName); if (element.removeEventListener) { element.removeEventListener(actualEventName, responder, false); } else { element.detachEvent('on' + actualEventName, responder); } } function stopObservingCustomEvent(element, eventName, responder) { if (element.removeEventListener) { element.removeEventListener('dataavailable', responder, false); } else { element.detachEvent('ondataavailable', responder); element.detachEvent('onlosecapture', responder); } } function stopObservingElement(element) { var uid = getUniqueElementID(element), registry = GLOBAL.Event.cache[uid]; if (!registry) return; destroyRegistryForElement(element, uid); var entries, i; for (var eventName in registry) { if (eventName === 'element') continue; entries = registry[eventName]; i = entries.length; while (i--) removeEvent(element, eventName, entries[i].responder); } } function stopObservingEventName(element, eventName) { var registry = getOrCreateRegistryFor(element); var entries = registry[eventName]; if (entries) { delete registry[eventName]; } entries = entries || []; var i = entries.length; while (i--) removeEvent(element, eventName, entries[i].responder); for (var name in registry) { if (name === 'element') continue; return; // There is another registered event } destroyRegistryForElement(element); } function removeEvent(element, eventName, handler) { if (isCustomEvent(eventName)) stopObservingCustomEvent(element, eventName, handler); else stopObservingStandardEvent(element, eventName, handler); } function getFireTarget(element) { if (element !== document) return element; if (document.createEvent && !element.dispatchEvent) return document.documentElement; return element; } function fire(element, eventName, memo, bubble) { element = getFireTarget($(element)); if (Object.isUndefined(bubble)) bubble = true; memo = memo || {}; var event = fireEvent(element, eventName, memo, bubble); return Event.extend(event); } function fireEvent_DOM(element, eventName, memo, bubble) { var event = document.createEvent('HTMLEvents'); event.initEvent('dataavailable', bubble, true); event.eventName = eventName; event.memo = memo; element.dispatchEvent(event); return event; } function fireEvent_IE(element, eventName, memo, bubble) { var event = document.createEventObject(); event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; event.eventName = eventName; event.memo = memo; element.fireEvent(event.eventType, event); return event; } var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE; Event.Handler = Class.create({ initialize: function(element, eventName, selector, callback) { this.element = $(element); this.eventName = eventName; this.selector = selector; this.callback = callback; this.handler = this.handleEvent.bind(this); }, start: function() { Event.observe(this.element, this.eventName, this.handler); return this; }, stop: function() { Event.stopObserving(this.element, this.eventName, this.handler); return this; }, handleEvent: function(event) { var element = Event.findElement(event, this.selector); if (element) this.callback.call(this.element, event, element); } }); function on(element, eventName, selector, callback) { element = $(element); if (Object.isFunction(selector) && Object.isUndefined(callback)) { callback = selector, selector = null; } return new Event.Handler(element, eventName, selector, callback).start(); } Object.extend(Event, Event.Methods); Object.extend(Event, { fire: fire, observe: observe, stopObserving: stopObserving, on: on }); Element.addMethods({ fire: fire, observe: observe, stopObserving: stopObserving, on: on }); Object.extend(document, { fire: fire.methodize(), observe: observe.methodize(), stopObserving: stopObserving.methodize(), on: on.methodize(), loaded: false }); if (GLOBAL.Event) Object.extend(window.Event, Event); else GLOBAL.Event = Event; GLOBAL.Event.cache = {}; function destroyCache_IE() { GLOBAL.Event.cache = null; } if (window.attachEvent) window.attachEvent('onunload', destroyCache_IE); DIV = null; docEl = null; })(this); (function(GLOBAL) { /* Code for creating leak-free event responders is based on work by John-David Dalton. */ var docEl = document.documentElement; var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl && 'onmouseleave' in docEl; function isSimulatedMouseEnterLeaveEvent(eventName) { return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && (eventName === 'mouseenter' || eventName === 'mouseleave'); } function createResponder(uid, eventName, handler) { if (Event._isCustomEvent(eventName)) return createResponderForCustomEvent(uid, eventName, handler); if (isSimulatedMouseEnterLeaveEvent(eventName)) return createMouseEnterLeaveResponder(uid, eventName, handler); return function(event) { if (!Event.cache) return; var element = Event.cache[uid].element; Event.extend(event, element); handler.call(element, event); }; } function createResponderForCustomEvent(uid, eventName, handler) { return function(event) { var cache = Event.cache[uid]; var element = cache && cache.element; if (Object.isUndefined(event.eventName)) return false; if (event.eventName !== eventName) return false; Event.extend(event, element); handler.call(element, event); }; } function createMouseEnterLeaveResponder(uid, eventName, handler) { return function(event) { var element = Event.cache[uid].element; Event.extend(event, element); var parent = event.relatedTarget; while (parent && parent !== element) { try { parent = parent.parentNode; } catch(e) { parent = element; } } if (parent === element) return; handler.call(element, event); } } GLOBAL.Event._createResponder = createResponder; docEl = null; })(this); (function(GLOBAL) { /* Support for the DOMContentLoaded event is based on work by Dan Webb, Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ var TIMER; function fireContentLoadedEvent() { if (document.loaded) return; if (TIMER) window.clearTimeout(TIMER); document.loaded = true; document.fire('dom:loaded'); } function checkReadyState() { if (document.readyState === 'complete') { document.detachEvent('onreadystatechange', checkReadyState); fireContentLoadedEvent(); } } function pollDoScroll() { try { document.documentElement.doScroll('left'); } catch (e) { TIMER = pollDoScroll.defer(); return; } fireContentLoadedEvent(); } if (document.readyState === 'complete') { fireContentLoadedEvent(); return; } if (document.addEventListener) { document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); } else { document.attachEvent('onreadystatechange', checkReadyState); if (window == top) TIMER = pollDoScroll.defer(); } Event.observe(window, 'load', fireContentLoadedEvent); })(this); Element.addMethods(); /*------------------------------- DEPRECATED -------------------------------*/ Hash.toQueryString = Object.toQueryString; var Toggle = { display: Element.toggle }; Element.addMethods({ childOf: Element.Methods.descendantOf }); var Insertion = { Before: function(element, content) { return Element.insert(element, {before:content}); }, Top: function(element, content) { return Element.insert(element, {top:content}); }, Bottom: function(element, content) { return Element.insert(element, {bottom:content}); }, After: function(element, content) { return Element.insert(element, {after:content}); } }; var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); var Position = { includeScrollOffsets: false, prepare: function() { this.deltaX = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0; this.deltaY = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; }, within: function(element, x, y) { if (this.includeScrollOffsets) return this.withinIncludingScrolloffsets(element, x, y); this.xcomp = x; this.ycomp = y; this.offset = Element.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 = Element.cumulativeScrollOffset(element); this.xcomp = x + offsetcache[0] - this.deltaX; this.ycomp = y + offsetcache[1] - this.deltaY; this.offset = Element.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); }, 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; }, cumulativeOffset: Element.Methods.cumulativeOffset, positionedOffset: Element.Methods.positionedOffset, absolutize: function(element) { Position.prepare(); return Element.absolutize(element); }, relativize: function(element) { Position.prepare(); return Element.relativize(element); }, realOffset: Element.Methods.cumulativeScrollOffset, offsetParent: Element.Methods.getOffsetParent, page: Element.Methods.viewportOffset, clone: function(source, target, options) { options = options || { }; return Element.clonePosition(target, source, options); } }; /*--------------------------------------------------------------------------*/ if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ function iter(name) { return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; } instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? function(element, className) { className = className.toString().strip(); var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); return cond ? document._getElementsByXPath('.//*' + cond, element) : []; } : function(element, className) { className = className.toString().strip(); var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); if (!classNames && !className) return elements; var nodes = $(element).getElementsByTagName('*'); className = ' ' + className + ' '; for (var i = 0, child, cn; child = nodes[i]; i++) { if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || (classNames && classNames.all(function(name) { return !name.toString().blank() && cn.include(' ' + name + ' '); })))) elements.push(Element.extend(child)); } return elements; }; return function(className, parentElement) { return $(parentElement || document.body).getElementsByClassName(className); }; }(Element.Methods); /*--------------------------------------------------------------------------*/ Element.ClassNames = Class.create(); Element.ClassNames.prototype = { initialize: function(element) { this.element = $(element); }, _each: function(iterator, context) { this.element.className.split(/\s+/).select(function(name) { return name.length > 0; })._each(iterator, context); }, set: function(className) { this.element.className = className; }, add: function(classNameToAdd) { if (this.include(classNameToAdd)) return; this.set($A(this).concat(classNameToAdd).join(' ')); }, remove: function(classNameToRemove) { if (!this.include(classNameToRemove)) return; this.set($A(this).without(classNameToRemove).join(' ')); }, toString: function() { return $A(this).join(' '); } }; Object.extend(Element.ClassNames.prototype, Enumerable); /*--------------------------------------------------------------------------*/ (function() { window.Selector = Class.create({ initialize: function(expression) { this.expression = expression.strip(); }, findElements: function(rootElement) { return Prototype.Selector.select(this.expression, rootElement); }, match: function(element) { return Prototype.Selector.match(element, this.expression); }, toString: function() { return this.expression; }, inspect: function() { return "#"; } }); Object.extend(Selector, { matchElements: function(elements, expression) { var match = Prototype.Selector.match, results = []; for (var i = 0, length = elements.length; i < length; i++) { var element = elements[i]; if (match(element, expression)) { results.push(Element.extend(element)); } } return results; }, findElement: function(elements, expression, index) { index = index || 0; var matchIndex = 0, element; for (var i = 0, length = elements.length; i < length; i++) { element = elements[i]; if (Prototype.Selector.match(element, expression) && index === matchIndex++) { return Element.extend(element); } } }, findChildElements: function(element, expressions) { var selector = expressions.toArray().join(', '); return Prototype.Selector.select(selector, element || document); } }); })(); apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/skin/screen.css0100644 0000000 0000000 00000024762 15051152474 030564 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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("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; bottom: -1px; /* compensate for IE rendering issue */ border-radius: 5px 5px 0px 0px; } #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; } #tabs li { float: left; margin: 0 3px 0 0; padding: 0; border-radius: 5px 5px 0px 0px; } /*background: url("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; 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; } #top .header .current a { font-weight: bold; padding-bottom: 5px; color: white; } #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('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; } img.corner { width: 15px; height: 15px; border: none; display: block !important; } 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; padding-bottom: 15px; top:-18px; left:10px; z-index: 20; background-color: #f90; font-size : 70%; border-radius: 0px 0px 15px 15px; } .menutitle { cursor:pointer; padding: 3px 12px; margin-left: 10px; background-image: url('chapter.gif'); background-repeat: no-repeat; background-position: center left; font-weight : bold; } .menutitle.selected { background-image: url('chapter_open.gif'); } .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('page.gif'); background-repeat: no-repeat; background-position: center left; font-weight : normal; margin-left: 10px; } #menu .selected { font-style : normal; margin-right: 10px; } .menuitem .selected { border-style: solid; border-width: 1px; } #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 .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: 60%; } #footer .lastmodified { position: relative; /* IE bugfix cont'd */ float: right; width: 30%; 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; float:right; width: 35%; background-color: #f0f0ff; border: solid 1px #4C6C8F; margin: 0px 0px 10px 10px; padding: 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('current.gif'); font-weight: normal; } .abstract{ text-align:justify; } 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('../instruction_arrow.png'); list-style-position: outside; margin-left: 2em; } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperAdmin.md0100644 0000000 0000000 00000426274 15051152474 031131 0ustar00rootroot0000000 0000000 # ZooKeeper Administrator's Guide ### A Guide to Deployment and Administration * [Deployment](#ch_deployment) * [System Requirements](#sc_systemReq) * [Supported Platforms](#sc_supportedPlatforms) * [Required Software](#sc_requiredSoftware) * [Clustered (Multi-Server) Setup](#sc_zkMulitServerSetup) * [Single Server and Developer Setup](#sc_singleAndDevSetup) * [Administration](#ch_administration) * [Designing a ZooKeeper Deployment](#sc_designing) * [Cross Machine Requirements](#sc_CrossMachineRequirements) * [Single Machine Requirements](#Single+Machine+Requirements) * [Provisioning](#sc_provisioning) * [Things to Consider: ZooKeeper Strengths and Limitations](#sc_strengthsAndLimitations) * [Administering](#sc_administering) * [Maintenance](#sc_maintenance) * [Ongoing Data Directory Cleanup](#Ongoing+Data+Directory+Cleanup) * [Debug Log Cleanup (logback)](#Debug+Log+Cleanup+Logback) * [Supervision](#sc_supervision) * [Monitoring](#sc_monitoring) * [Logging](#sc_logging) * [Troubleshooting](#sc_troubleshooting) * [Configuration Parameters](#sc_configuration) * [Minimum Configuration](#sc_minimumConfiguration) * [Advanced Configuration](#sc_advancedConfiguration) * [Cluster Options](#sc_clusterOptions) * [Encryption, Authentication, Authorization Options](#sc_authOptions) * [Experimental Options/Features](#Experimental+Options%2FFeatures) * [Unsafe Options](#Unsafe+Options) * [Disabling data directory autocreation](#Disabling+data+directory+autocreation) * [Enabling db existence validation](#sc_db_existence_validation) * [Performance Tuning Options](#sc_performance_options) * [AdminServer configuration](#sc_adminserver_config) * [Communication using the Netty framework](#Communication+using+the+Netty+framework) * [Quorum TLS](#Quorum+TLS) * [Upgrading existing non-TLS cluster with no downtime](#Upgrading+existing+nonTLS+cluster) * [ZooKeeper Commands](#sc_zkCommands) * [The Four Letter Words](#sc_4lw) * [The AdminServer](#sc_adminserver) * [Data File Management](#sc_dataFileManagement) * [The Data Directory](#The+Data+Directory) * [The Log Directory](#The+Log+Directory) * [File Management](#sc_filemanagement) * [Recovery - TxnLogToolkit](#Recovery+-+TxnLogToolkit) * [Things to Avoid](#sc_commonProblems) * [Best Practices](#sc_bestPractices) ## Deployment This section contains information about deploying Zookeeper and covers these topics: * [System Requirements](#sc_systemReq) * [Clustered (Multi-Server) Setup](#sc_zkMulitServerSetup) * [Single Server and Developer Setup](#sc_singleAndDevSetup) 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 ZooKeeper consists of multiple components. Some components are supported broadly, and other components are supported only on a smaller set of platforms. * **Client** is the Java client library, used by applications to connect to a ZooKeeper ensemble. * **Server** is the Java server that runs on the ZooKeeper ensemble nodes. * **Native Client** is a client implemented in C, similar to the Java client, used by applications to connect to a ZooKeeper ensemble. * **Contrib** refers to multiple optional add-on components. The following matrix describes the level of support committed for running each component on different operating system platforms. ##### Support Matrix | Operating System | Client | Server | Native Client | Contrib | |------------------|--------|--------|---------------|---------| | GNU/Linux | Development and Production | Development and Production | Development and Production | Development and Production | | Solaris | Development and Production | Development and Production | Not Supported | Not Supported | | FreeBSD | Development and Production | Development and Production | Not Supported | Not Supported | | Windows | Development and Production | Development and Production | Not Supported | Not Supported | | Mac OS X | Development Only | Development Only | Not Supported | Not Supported | For any operating system not explicitly mentioned as supported in the matrix, components may or may not work. The ZooKeeper community will fix obvious bugs that are reported for other platforms, but there is no full support. #### Required Software ZooKeeper runs in Java, release 1.8 or greater (JDK 8 LTS, JDK 11 LTS, JDK 12 - Java 9 and 10 are not supported). 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. ###### Note >As mentioned in the [ZooKeeper Getting Started Guide](zookeeperStarted.html) , a minimum of three servers are required for a fault tolerant clustered setup, and it is strongly recommended that you have an odd number of servers. >Usually three servers is more than enough for a production install, but for maximum reliability during maintenance, you may wish to install five servers. With three servers, if you perform maintenance on one of them, you are vulnerable to a failure on one of the other two servers during that maintenance. If you have five of them running, you can take one down for maintenance, and know that you're still OK if one of the other four suddenly fails. >Your redundancy considerations should include all aspects of your environment. If you have three ZooKeeper servers, but their network cables are all plugged into the same network switch, then the failure of that switch will take down your entire ensemble. Here are the steps to set 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](http://java.sun.com/javase/downloads/index.jsp) 2. 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. 3. Install the ZooKeeper Server Package. It can be downloaded from: [http://zookeeper.apache.org/releases.html](http://zookeeper.apache.org/releases.html) 4. Create a configuration file. This file can be called anything. Use the following settings as a starting point: tickTime=2000 dataDir=/var/lib/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](#sc_configuration). A word thought 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, for each server you need to specify first a Quorum port then a dedicated port for ZooKeeper leader election). Since ZooKeeper 3.6.0 you can also [specify multiple addresses](#id_multi_address) for each ZooKeeper server instance (this can increase availability when multiple physical network interfaces can be used parallel in the cluster). 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**. 5. 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. **IMPORTANT:** if you enable extended features such as TTL Nodes (see below) the id must be between 1 and 254 due to internal limitations. 6. Create an initialization marker file *initialize* in the same directory as *myid*. This file indicates that an empty data directory is expected. When present, an empty database is created and the marker file deleted. When not present, an empty data directory will mean this peer will not have voting rights and it will not populate the data directory until it communicates with an active leader. Intended use is to only create this file when bringing up a new ensemble. 7. If your configuration file is set up, you can start a ZooKeeper server: $ java -cp zookeeper.jar:lib/*:conf org.apache.zookeeper.server.quorum.QuorumPeerMain zoo.conf QuorumPeerMain starts a ZooKeeper server, [JMX](http://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/) management beans are also registered which allows management through a JMX management console. The [ZooKeeper JMX document](zookeeperJMX.html) 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. 8. Test your deployment by connecting to the hosts: In Java, you can run the following command to execute simple operations: $ bin/zkCli.sh -server 127.0.0.1:2181 ### Single Server and Developer Setup If you want to set up ZooKeeper for development purposes, you will probably want to set up 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](zookeeperStarted.html#sc_InstallingSingleMode) section of the [ZooKeeper Getting Started Guide](zookeeperStarted.html). For information on installing the client side libraries, refer to the [Bindings](zookeeperProgrammers.html#ch_bindings) section of the [ZooKeeper Programmer's Guide](zookeeperProgrammers.html). ## Administration This section contains information about running and maintaining ZooKeeper and covers these topics: * [Designing a ZooKeeper Deployment](#sc_designing) * [Provisioning](#sc_provisioning) * [Things to Consider: ZooKeeper Strengths and Limitations](#sc_strengthsAndLimitations) * [Administering](#sc_administering) * [Maintenance](#sc_maintenance) * [Supervision](#sc_supervision) * [Monitoring](#sc_monitoring) * [Logging](#sc_logging) * [Troubleshooting](#sc_troubleshooting) * [Configuration Parameters](#sc_configuration) * [ZooKeeper Commands](#sc_zkCommands) * [Data File Management](#sc_dataFileManagement) * [Things to Avoid](#sc_commonProblems) * [Best Practices](#sc_bestPractices) ### Designing a ZooKeeper Deployment The reliability 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. 1. 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. 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. For a ZooKeeper ensemble with N servers, if N is odd, the ensemble is able to tolerate up to N/2 server failures without losing any znode data; if N is even, the ensemble is able to tolerate up to N/2-1 server failures. For example, if we have a ZooKeeper ensemble with 3 servers, the ensemble is able to tolerate up to 1 (3/2) server failures. If we have a ZooKeeper ensemble with 5 servers, the ensemble is able to tolerate up to 2 (5/2) server failures. If the ZooKeeper ensemble with 6 servers, the ensemble is also able to tolerate up to 2 (6/2-1) server failures without losing data and prevent the "brain split" issue. ZooKeeper ensemble is usually has odd number of servers. This is because with the even number of servers, the capacity of failure tolerance is the same as the ensemble with one less server (2 failures for both 5-node ensemble and 6-node ensemble), but the ensemble has to maintain extra connections and data transfers for one more server. 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 resources 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](#sc_commonProblems) 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](#var_datadir) 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 and a new transaction log file is created for future transactions. During snapshotting, ZooKeeper may continue appending incoming transactions to the old log file. Therefore, some transactions which are newer than a snapshot may be found in the last transaction log preceding the snapshot. A ZooKeeper server **will not remove old snapshots and log files** when using the default configuration (see autopurge below), this is the responsibility of the operator. Every serving environment is different and therefore the requirements of managing these files may differ from install to install (backup for example). The PurgeTxnLog utility implements a simple retention policy that administrators can use. The [API docs](index.html) 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 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:lib/slf4j-api-1.7.30.jar:lib/logback-classic-1.2.10.jar:lib/logback-core-1.2.10.jar:conf org.apache.zookeeper.server.PurgeTxnLog -n Automatic purging of the snapshots and corresponding transaction logs was introduced in version 3.4.0 and can be enabled via the following configuration parameters **autopurge.snapRetainCount** and **autopurge.purgeInterval**. For more on this, see [Advanced Configuration](#sc_advancedConfiguration) below. #### Debug Log Cleanup (logback) See the section on [logging](#sc_logging) in this document. It is expected that you will setup a rolling file appender using the in-built logback feature. The sample configuration file in the release tar's `conf/logback.xml` 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 shut down (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](http://cr.yp.to/daemontools.html) or [SMF](http://en.wikipedia.org/wiki/Service\_Management\_Facility) (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. It is also recommended to configure the ZooKeeper server process to terminate and dump its heap if an OutOfMemoryError** occurs. This is achieved by launching the JVM with the following arguments on Linux and Windows respectively. The *zkServer.sh* and *zkServer.cmd* scripts that ship with ZooKeeper set these options. -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' "-XX:+HeapDumpOnOutOfMemoryError" "-XX:OnOutOfMemoryError=cmd /c taskkill /pid %%%%p /t /f" ### Monitoring The ZooKeeper service can be monitored in one of three primary ways: * the command port through the use of [4 letter words](#sc_zkCommands) * with [JMX](zookeeperJMX.html) * using the [`zkServer.sh status` command](zookeeperTools.html#zkServer) ### Logging ZooKeeper uses **[SLF4J](http://www.slf4j.org)** version 1.7 as its logging infrastructure. By default ZooKeeper is shipped with **[LOGBack](http://logback.qos.ch/)** as the logging backend, but you can use any other supported logging framework of your choice. The ZooKeeper default *logback.xml* file resides in the *conf* directory. Logback requires that *logback.xml* either be in the working directory (the directory from which ZooKeeper is run) or be accessible from the classpath. For more information about SLF4J, see [its manual](http://www.slf4j.org/manual.html). For more information about Logback, see [Logback website](http://logback.qos.ch/). ### 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. ###### Note >In 3.5.0 and later, some of these parameters should be placed in a dynamic configuration file. If they are placed in the static configuration file, ZooKeeper will automatically move them over to the dynamic configuration file. See [Dynamic Reconfiguration](zookeeperReconfig.html) for more information. #### 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. * *secureClientPort* : the port to listen on for secure client connections using SSL. **clientPort** specifies the port for plaintext connections while **secureClientPort** specifies the port for SSL connections. Specifying both enables mixed-mode while omitting either will disable that mode. Note that SSL feature will be enabled when user plugs-in zookeeper.serverCnxnFactory, zookeeper.clientCnxnSocket as Netty. * *observerMasterPort* : the port to listen for observer connections; that is, the port that observers attempt to connect to. if the property is set then the server will host observer connections when in follower mode in addition to when in leader mode and correspondingly attempt to connect to any voting peer when in observer mode. * *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 affect 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 snapshots. ###### Note >Having a dedicated log device has a large impact on throughput and stable latencies. It is highly recommended dedicating 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 are no more than globalOutstandingLimit outstanding requests across entire ensemble, equally divided. The default limit is 1,000 and, for example, with 3 members each of them will have 1000 / 2 = 500 individual limit. * *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** and **snapSizeLimitInKb**). * *snapCount* : (Java system property: **zookeeper.snapCount**) ZooKeeper records its transactions using snapshots and a transaction log (think write-ahead log). The number of transactions recorded in the transaction log before a snapshot can be taken (and the transaction log rolled) is determined by snapCount. In order to prevent all of the machines in the quorum from taking a snapshot at the same time, each ZooKeeper server will take a snapshot when the number of transactions in the transaction log reaches a runtime generated random value in the \[snapCount/2+1, snapCount] range. The default snapCount is 100,000. * *commitLogCount* * : (Java system property: **zookeeper.commitLogCount**) Zookeeper maintains an in-memory list of last committed requests for fast synchronization with followers when the followers are not too behind. This improves sync performance in case when your snapshots are large (>100,000). The default value is 500 which is the recommended minimum. * *snapSizeLimitInKb* : (Java system property: **zookeeper.snapSizeLimitInKb**) ZooKeeper records its transactions using snapshots and a transaction log (think write-ahead log). The total size in bytes allowed in the set of transactions recorded in the transaction log before a snapshot can be taken (and the transaction log rolled) is determined by snapSize. In order to prevent all of the machines in the quorum from taking a snapshot at the same time, each ZooKeeper server will take a snapshot when the size in bytes of the set of transactions in the transaction log reaches a runtime generated random value in the \[snapSize/2+1, snapSize] range. Each file system has a minimum standard file size and in order to for valid functioning of this feature, the number chosen must be larger than that value. The default snapSizeLimitInKb is 4,194,304 (4GB). A non-positive value will disable the feature. * *txnLogSizeLimitInKb* : (Java system property: **zookeeper.txnLogSizeLimitInKb**) Zookeeper transaction log file can also be controlled more directly using txnLogSizeLimitInKb. Larger txn logs can lead to slower follower syncs when sync is done using transaction log. This is because leader has to scan through the appropriate log file on disk to find the transaction to start sync from. This feature is turned off by default and snapCount and snapSizeLimitInKb are the only values that limit transaction log size. When enabled Zookeeper will roll the log when any of the limits is hit. Please note that actual log size can exceed this value by the size of the serialized transaction. On the other hand, if this value is set too close to (or smaller than) **preAllocSize**, it can cause Zookeeper to roll the log for every transaction. While this is not a correctness issue, this may cause severely degraded performance. To avoid this and to get most out of this feature, it is recommended to set the value to N * **preAllocSize** where N >= 2. * *maxCnxns* : (Java system property: **zookeeper.maxCnxns**) Limits the total number of concurrent connections that can be made to a zookeeper server (per client Port of each server ). This is used to prevent certain classes of DoS attacks. The default is 0 and setting it to 0 entirely removes the limit on total number of concurrent connections. Accounting for the number of connections for serverCnxnFactory and a secureServerCnxnFactory is done separately, so a peer is allowed to host up to 2*maxCnxns provided they are of appropriate types. * *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 60. Setting this to 0 entirely removes the limit on concurrent connections. * *clientPortAddress* : **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**. * *fsync.warningthresholdms* : (Java system property: **zookeeper.fsync.warningthresholdms**) **New in 3.3.4:** A warning message will be output to the log whenever an fsync in the Transactional Log (WAL) takes longer than this value. The values is specified in milliseconds and defaults to 1000. This value can only be set as a system property. * *maxResponseCacheSize* : (Java system property: **zookeeper.maxResponseCacheSize**) When set to a positive integer, it determines the size of the cache that stores the serialized form of recently read records. Helps save the serialization cost on popular znodes. The metrics **response_packet_cache_hits** and **response_packet_cache_misses** can be used to tune this value to a given workload. The feature is turned on by default with a value of 400, set to 0 or a negative integer to turn the feature off. * *maxGetChildrenResponseCacheSize* : (Java system property: **zookeeper.maxGetChildrenResponseCacheSize**) **New in 3.6.0:** Similar to **maxResponseCacheSize**, but applies to get children requests. The metrics **response_packet_get_children_cache_hits** and **response_packet_get_children_cache_misses** can be used to tune this value to a given workload. The feature is turned on by default with a value of 400, set to 0 or a negative integer to turn the feature off. * *autopurge.snapRetainCount* : (No Java system property) **New in 3.4.0:** When enabled, ZooKeeper auto purge feature retains the **autopurge.snapRetainCount** most recent snapshots and the corresponding transaction logs in the **dataDir** and **dataLogDir** respectively and deletes the rest. Defaults to 3. Minimum value is 3. * *autopurge.purgeInterval* : (No Java system property) **New in 3.4.0:** The time interval in hours for which the purge task has to be triggered. Set to a positive integer (1 and above) to enable the auto purging. Defaults to 0. * *syncEnabled* : (Java system property: **zookeeper.observer.syncEnabled**) **New in 3.4.6, 3.5.0:** The observers now log transaction and write snapshot to disk by default like the participants. This reduces the recovery time of the observers on restart. Set to "false" to disable this feature. Default is "true" * *extendedTypesEnabled* : (Java system property only: **zookeeper.extendedTypesEnabled**) **New in 3.5.4, 3.6.0:** Define to `true` to enable extended features such as the creation of [TTL Nodes](zookeeperProgrammers.html#TTL+Nodes). They are disabled by default. IMPORTANT: when enabled server IDs must be less than 255 due to internal limitations. * *emulate353TTLNodes* : (Java system property only:**zookeeper.emulate353TTLNodes**). **New in 3.5.4, 3.6.0:** Due to [ZOOKEEPER-2901] (https://issues.apache.org/jira/browse/ZOOKEEPER-2901) TTL nodes created in version 3.5.3 are not supported in 3.5.4/3.6.0. However, a workaround is provided via the zookeeper.emulate353TTLNodes system property. If you used TTL nodes in ZooKeeper 3.5.3 and need to maintain compatibility set **zookeeper.emulate353TTLNodes** to `true` in addition to **zookeeper.extendedTypesEnabled**. NOTE: due to the bug, server IDs must be 127 or less. Additionally, the maximum support TTL value is `1099511627775` which is smaller than what was allowed in 3.5.3 (`1152921504606846975`) * *watchManagerName* : (Java system property only: **zookeeper.watchManagerName**) **New in 3.6.0:** Added in [ZOOKEEPER-1179](https://issues.apache.org/jira/browse/ZOOKEEPER-1179) New watcher manager WatchManagerOptimized is added to optimize the memory overhead in heavy watch use cases. This config is used to define which watcher manager to be used. Currently, we only support WatchManager and WatchManagerOptimized. * *watcherCleanThreadsNum* : (Java system property only: **zookeeper.watcherCleanThreadsNum**) **New in 3.6.0:** Added in [ZOOKEEPER-1179](https://issues.apache.org/jira/browse/ZOOKEEPER-1179) The new watcher manager WatchManagerOptimized will clean up the dead watchers lazily, this config is used to decide how many thread is used in the WatcherCleaner. More thread usually means larger clean up throughput. The default value is 2, which is good enough even for heavy and continuous session closing/recreating cases. * *watcherCleanThreshold* : (Java system property only: **zookeeper.watcherCleanThreshold**) **New in 3.6.0:** Added in [ZOOKEEPER-1179](https://issues.apache.org/jira/browse/ZOOKEEPER-1179) The new watcher manager WatchManagerOptimized will clean up the dead watchers lazily, the cleanup process is relatively heavy, batch processing will reduce the cost and improve the performance. This setting is used to decide the batch size. The default one is 1000, we don't need to change it if there is no memory or clean up speed issue. * *watcherCleanIntervalInSeconds* : (Java system property only:**zookeeper.watcherCleanIntervalInSeconds**) **New in 3.6.0:** Added in [ZOOKEEPER-1179](https://issues.apache.org/jira/browse/ZOOKEEPER-1179) The new watcher manager WatchManagerOptimized will clean up the dead watchers lazily, the cleanup process is relatively heavy, batch processing will reduce the cost and improve the performance. Besides watcherCleanThreshold, this setting is used to clean up the dead watchers after certain time even the dead watchers are not larger than watcherCleanThreshold, so that we won't leave the dead watchers there for too long. The default setting is 10 minutes, which usually don't need to be changed. * *maxInProcessingDeadWatchers* : (Java system property only: **zookeeper.maxInProcessingDeadWatchers**) **New in 3.6.0:** Added in [ZOOKEEPER-1179](https://issues.apache.org/jira/browse/ZOOKEEPER-1179) This is used to control how many backlog can we have in the WatcherCleaner, when it reaches this number, it will slow down adding the dead watcher to WatcherCleaner, which will in turn slow down adding and closing watchers, so that we can avoid OOM issue. By default there is no limit, you can set it to values like watcherCleanThreshold * 1000. * *bitHashCacheSize* : (Java system property only: **zookeeper.bitHashCacheSize**) **New 3.6.0**: Added in [ZOOKEEPER-1179](https://issues.apache.org/jira/browse/ZOOKEEPER-1179) This is the setting used to decide the HashSet cache size in the BitHashSet implementation. Without HashSet, we need to use O(N) time to get the elements, N is the bit numbers in elementBits. But we need to keep the size small to make sure it doesn't cost too much in memory, there is a trade off between memory and time complexity. The default value is 10, which seems a relatively reasonable cache size. * *fastleader.minNotificationInterval* : (Java system property: **zookeeper.fastleader.minNotificationInterval**) Lower bound for length of time between two consecutive notification checks on the leader election. This interval determines how long a peer waits to check the set of election votes and effects how quickly an election can resolve. The interval follows a backoff strategy from the configured minimum (this) and the configured maximum (fastleader.maxNotificationInterval) for long elections. * *fastleader.maxNotificationInterval* : (Java system property: **zookeeper.fastleader.maxNotificationInterval**) Upper bound for length of time between two consecutive notification checks on the leader election. This interval determines how long a peer waits to check the set of election votes and effects how quickly an election can resolve. The interval follows a backoff strategy from the configured minimum (fastleader.minNotificationInterval) and the configured maximum (this) for long elections. * *connectionMaxTokens* : (Java system property: **zookeeper.connection_throttle_tokens**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the maximum number of tokens in the token-bucket. When set to 0, throttling is disabled. Default is 0. * *connectionTokenFillTime* : (Java system property: **zookeeper.connection_throttle_fill_time**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the interval in milliseconds when the token bucket is re-filled with *connectionTokenFillCount* tokens. Default is 1. * *connectionTokenFillCount* : (Java system property: **zookeeper.connection_throttle_fill_count**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the number of tokens to add to the token bucket every *connectionTokenFillTime* milliseconds. Default is 1. * *connectionFreezeTime* : (Java system property: **zookeeper.connection_throttle_freeze_time**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the interval in milliseconds when the dropping probability is adjusted. When set to -1, probabilistic dropping is disabled. Default is -1. * *connectionDropIncrease* : (Java system property: **zookeeper.connection_throttle_drop_increase**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the dropping probability to increase. The throttler checks every *connectionFreezeTime* milliseconds and if the token bucket is empty, the dropping probability will be increased by *connectionDropIncrease*. The default is 0.02. * *connectionDropDecrease* : (Java system property: **zookeeper.connection_throttle_drop_decrease**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the dropping probability to decrease. The throttler checks every *connectionFreezeTime* milliseconds and if the token bucket has more tokens than a threshold, the dropping probability will be decreased by *connectionDropDecrease*. The threshold is *connectionMaxTokens* \* *connectionDecreaseRatio*. The default is 0.002. * *connectionDecreaseRatio* : (Java system property: **zookeeper.connection_throttle_decrease_ratio**) **New in 3.6.0:** This is one of the parameters to tune the server-side connection throttler, which is a token-based rate limiting mechanism with optional probabilistic dropping. This parameter defines the threshold to decrease the dropping probability. The default is 0. * *zookeeper.connection_throttle_weight_enabled* : (Java system property only) **New in 3.6.0:** Whether to consider connection weights when throttling. Only useful when connection throttle is enabled, that is, connectionMaxTokens is larger than 0. The default is false. * *zookeeper.connection_throttle_global_session_weight* : (Java system property only) **New in 3.6.0:** The weight of a global session. It is the number of tokens required for a global session request to get through the connection throttler. It has to be a positive integer no smaller than the weight of a local session. The default is 3. * *zookeeper.connection_throttle_local_session_weight* : (Java system property only) **New in 3.6.0:** The weight of a local session. It is the number of tokens required for a local session request to get through the connection throttler. It has to be a positive integer no larger than the weight of a global session or a renew session. The default is 1. * *zookeeper.connection_throttle_renew_session_weight* : (Java system property only) **New in 3.6.0:** The weight of renewing a session. It is also the number of tokens required for a reconnect request to get through the throttler. It has to be a positive integer no smaller than the weight of a local session. The default is 2. * *clientPortListenBacklog* : (No Java system property) **New in 3.4.14, 3.5.5, 3.6.0:** The socket backlog length for the ZooKeeper server socket. This controls the number of requests that will be queued server-side to be processed by the ZooKeeper server. Connections that exceed this length will receive a network timeout (30s) which may cause ZooKeeper session expiry issues. By default, this value is unset (`-1`) which, on Linux, uses a backlog of `50`. This value must be a positive number. * *serverCnxnFactory* : (Java system property: **zookeeper.serverCnxnFactory**) Specifies ServerCnxnFactory implementation. This should be set to `NettyServerCnxnFactory` in order to use TLS based server communication. Default is `NIOServerCnxnFactory`. * *flushDelay* : (Java system property: **zookeeper.flushDelay**) Time in milliseconds to delay the flush of the commit log. Does not affect the limit defined by *maxBatchSize*. Disabled by default (with value 0). Ensembles with high write rates may see throughput improved with a value of 10-20 ms. * *maxWriteQueuePollTime* : (Java system property: **zookeeper.maxWriteQueuePollTime**) If *flushDelay* is enabled, this determines the amount of time in milliseconds to wait before flushing when no new requests are being queued. Set to *flushDelay*/3 by default (implicitly disabled by default). * *maxBatchSize* : (Java system property: **zookeeper.maxBatchSize**) The number of transactions allowed in the server before a flush of the commit log is triggered. Does not affect the limit defined by *flushDelay*. Default is 1000. * *enforceQuota* : (Java system property: **zookeeper.enforceQuota**) **New in 3.7.0:** Enforce the quota check. When enabled and the client exceeds the total bytes or children count hard quota under a znode, the server will reject the request and reply the client a `QuotaExceededException` by force. The default value is: false. Exploring [quota feature](http://zookeeper.apache.org/doc/current/zookeeperQuotas.html) for more details. * *requestThrottleLimit* : (Java system property: **zookeeper.request_throttle_max_requests**) **New in 3.6.0:** The total number of outstanding requests allowed before the RequestThrottler starts stalling. When set to 0, throttling is disabled. The default is 0. * *requestThrottleStallTime* : (Java system property: **zookeeper.request_throttle_stall_time**) **New in 3.6.0:** The maximum time (in milliseconds) for which a thread may wait to be notified that it may proceed processing a request. The default is 100. * *requestThrottleDropStale* : (Java system property: **request_throttle_drop_stale**) **New in 3.6.0:** When enabled, the throttler will drop stale requests rather than issue them to the request pipeline. A stale request is a request sent by a connection that is now closed, and/or a request that will have a request latency higher than the sessionTimeout. The default is true. * *requestStaleLatencyCheck* : (Java system property: **zookeeper.request_stale_latency_check**) **New in 3.6.0:** When enabled, a request is considered stale if the request latency is higher than its associated session timeout. Disabled by default. * *requestStaleConnectionCheck* : (Java system property: **zookeeper.request_stale_connection_check**) **New in 3.6.0:** When enabled, a request is considered stale if the request's connection has closed. Enabled by default. * *zookeeper.request_throttler.shutdownTimeout* : (Java system property only) **New in 3.6.0:** The time (in milliseconds) the RequestThrottler waits for the request queue to drain during shutdown before it shuts down forcefully. The default is 10000. * *advancedFlowControlEnabled* : (Java system property: **zookeeper.netty.advancedFlowControl.enabled**) Using accurate flow control in netty based on the status of ZooKeeper pipeline to avoid direct buffer OOM. It will disable the AUTO_READ in Netty. * *enableEagerACLCheck* : (Java system property only: **zookeeper.enableEagerACLCheck**) When set to "true", enables eager ACL check on write requests on each local server before sending the requests to quorum. Default is "false". * *maxConcurrentSnapSyncs* : (Java system property: **zookeeper.leader.maxConcurrentSnapSyncs**) The maximum number of snap syncs a leader or a follower can serve at the same time. The default is 10. * *maxConcurrentDiffSyncs* : (Java system property: **zookeeper.leader.maxConcurrentDiffSyncs**) The maximum number of diff syncs a leader or a follower can serve at the same time. The default is 100. * *digest.enabled* : (Java system property only: **zookeeper.digest.enabled**) **New in 3.6.0:** The digest feature is added to detect the data inconsistency inside ZooKeeper when loading database from disk, catching up and following leader, its doing incrementally hash check for the DataTree based on the adHash paper mentioned in https://cseweb.ucsd.edu/~daniele/papers/IncHash.pdf The idea is simple, the hash value of DataTree will be updated incrementally based on the changes to the set of data. When the leader is preparing the txn, it will pre-calculate the hash of the tree based on the changes happened with formula: current_hash = current_hash + hash(new node data) - hash(old node data) If it’s creating a new node, the hash(old node data) will be 0, and if it’s a delete node op, the hash(new node data) will be 0. This hash will be associated with each txn to represent the expected hash value after applying the txn to the data tree, it will be sent to followers with original proposals. Learner will compare the actual hash value with the one in the txn after applying the txn to the data tree, and report mismatch if it’s not the same. These digest value will also be persisted with each txn and snapshot on the disk, so when servers restarted and load data from disk, it will compare and see if there is hash mismatch, which will help detect data loss issue on disk. For the actual hash function, we’re using CRC internally, it’s not a collisionless hash function, but it’s more efficient compared to collisionless hash, and the collision possibility is really really rare and can already meet our needs here. This feature is backward and forward compatible, so it can safely roll upgrade, downgrade, enabled and later disabled without any compatible issue. Here are the scenarios have been covered and tested: 1. When leader runs with new code while follower runs with old one, the digest will be appended to the end of each txn, follower will only read header and txn data, digest value in the txn will be ignored. It won't affect the follower reads and processes the next txn. 2. When leader runs with old code while follower runs with new one, the digest won't be sent with txn, when follower tries to read the digest, it will throw EOF which is caught and handled gracefully with digest value set to null. 3. When loading old snapshot with new code, it will throw IOException when trying to read the non-exist digest value, and the exception will be caught and digest will be set to null, which means we won't compare digest when loading this snapshot, which is expected to happen during rolling upgrade 4. When loading new snapshot with old code, it will finish successfully after deserializing the data tree, the digest value at the end of snapshot file will be ignored 5. The scenarios of rolling restart with flags change are similar to the 1st and 2nd scenarios discussed above, if the leader enabled but follower not, digest value will be ignored, and follower won't compare the digest during runtime; if leader disabled but follower enabled, follower will get EOF exception which is handled gracefully. Note: the current digest calculation excluded nodes under /zookeeper due to the potential inconsistency in the /zookeeper/quota stat node, we can include that after that issue is fixed. By default, this feature is enabled, set "false" to disable it. * *snapshot.compression.method* : (Java system property: **zookeeper.snapshot.compression.method**) **New in 3.6.0:** This property controls whether or not ZooKeeper should compress snapshots before storing them on disk (see [ZOOKEEPER-3179](https://issues.apache.org/jira/browse/ZOOKEEPER-3179)). Possible values are: - "": Disabled (no snapshot compression). This is the default behavior. - "gz": See [gzip compression](https://en.wikipedia.org/wiki/Gzip). - "snappy": See [Snappy compression](https://en.wikipedia.org/wiki/Snappy_(compression)). * *snapshot.trust.empty* : (Java system property: **zookeeper.snapshot.trust.empty**) **New in 3.5.6:** This property controls whether or not ZooKeeper should treat missing snapshot files as a fatal state that can't be recovered from. Set to true to allow ZooKeeper servers recover without snapshot files. This should only be set during upgrading from old versions of ZooKeeper (3.4.x, pre 3.5.3) where ZooKeeper might only have transaction log files but without presence of snapshot files. If the value is set during upgrade, we recommend setting the value back to false after upgrading and restart ZooKeeper process so ZooKeeper can continue normal data consistency check during recovery process. Default value is false. * *audit.enable* : (Java system property: **zookeeper.audit.enable**) **New in 3.6.0:** By default audit logs are disabled. Set to "true" to enable it. Default value is "false". See the [ZooKeeper audit logs](zookeeperAuditLogs.html) for more information. * *audit.impl.class* : (Java system property: **zookeeper.audit.impl.class**) **New in 3.6.0:** Class to implement the audit logger. By default logback based audit logger org.apache.zookeeper.audit .Slf4jAuditLogger is used. See the [ZooKeeper audit logs](zookeeperAuditLogs.html) for more information. * *largeRequestMaxBytes* : (Java system property: **zookeeper.largeRequestMaxBytes**) **New in 3.6.0:** The maximum number of bytes of all inflight large request. The connection will be closed if a coming large request causes the limit exceeded. The default is 100 * 1024 * 1024. * *largeRequestThreshold* : (Java system property: **zookeeper.largeRequestThreshold**) **New in 3.6.0:** The size threshold after which a request is considered a large request. If it is -1, then all requests are considered small, effectively turning off large request throttling. The default is -1. * *outstandingHandshake.limit* (Java system property only: **zookeeper.netty.server.outstandingHandshake.limit**) The maximum in-flight TLS handshake connections could have in ZooKeeper, the connections exceed this limit will be rejected before starting handshake. This setting doesn't limit the max TLS concurrency, but helps avoid herd effect due to TLS handshake timeout when there are too many in-flight TLS handshakes. Set it to something like 250 is good enough to avoid herd effect. * *netty.server.earlyDropSecureConnectionHandshakes* (Java system property: **zookeeper.netty.server.earlyDropSecureConnectionHandshakes**) If the ZooKeeper server is not fully started, drop TCP connections before performing the TLS handshake. This is useful in order to prevent flooding the server with many concurrent TLS handshakes after a restart. Please note that if you enable this flag the server won't answer to 'ruok' commands if it is not fully started. The behaviour of dropping the connection has been introduced in ZooKeeper 3.7 and it was not possible to disable it. Since 3.7.1 and 3.8.0 this feature is disabled by default. * *throttledOpWaitTime* (Java system property: **zookeeper.throttled_op_wait_time**) The time in the RequestThrottler queue longer than which a request will be marked as throttled. A throttled requests will not be processed other than being fed down the pipeline of the server it belongs to preserve the order of all requests. The FinalProcessor will issue an error response (new error code: ZTHROTTLEDOP) for these undigested requests. The intent is for the clients not to retry them immediately. When set to 0, no requests will be throttled. The default is 0. * *learner.closeSocketAsync* (Java system property: **zookeeper.learner.closeSocketAsync**) (Java system property: **learner.closeSocketAsync**)(Added for backward compatibility) **New in 3.7.0:** When enabled, a learner will close the quorum socket asynchronously. This is useful for TLS connections where closing a socket might take a long time, block the shutdown process, potentially delay a new leader election, and leave the quorum unavailable. Closing the socket asynchronously avoids blocking the shutdown process despite the long socket closing time and a new leader election can be started while the socket being closed. The default is false. * *leader.closeSocketAsync* (Java system property: **zookeeper.leader.closeSocketAsync**) (Java system property: **leader.closeSocketAsync**)(Added for backward compatibility) **New in 3.7.0:** When enabled, the leader will close a quorum socket asynchronously. This is useful for TLS connections where closing a socket might take a long time. If disconnecting a follower is initiated in ping() because of a failed SyncLimitCheck then the long socket closing time will block the sending of pings to other followers. Without receiving pings, the other followers will not send session information to the leader, which causes sessions to expire. Setting this flag to true ensures that pings will be sent regularly. The default is false. * *learner.asyncSending* (Java system property: **zookeeper.learner.asyncSending**) (Java system property: **learner.asyncSending**)(Added for backward compatibility) **New in 3.7.0:** The sending and receiving packets in Learner were done synchronously in a critical section. An untimely network issue could cause the followers to hang (see [ZOOKEEPER-3575](https://issues.apache.org/jira/browse/ZOOKEEPER-3575) and [ZOOKEEPER-4074](https://issues.apache.org/jira/browse/ZOOKEEPER-4074)). The new design moves sending packets in Learner to a separate thread and sends the packets asynchronously. The new design is enabled with this parameter (learner.asyncSending). The default is false. * *forward_learner_requests_to_commit_processor_disabled* (Java system property: **zookeeper.forward_learner_requests_to_commit_processor_disabled**) When this property is set, the requests from learners won't be enqueued to CommitProcessor queue, which will help save the resources and GC time on leader. The default value is false. * *serializeLastProcessedZxid.enabled* (Jave system property: **zookeeper.serializeLastProcessedZxid.enabled**) **New in 3.9.0:** If enabled, ZooKeeper serializes the lastProcessedZxid when snapshot and deserializes it when restore. Defaults to true. Needs to be enabled for performing snapshot and restore via admin server commands, as there is no snapshot file name to extract the lastProcessedZxid. This feature is backward and forward compatible. Here are the different scenarios. 1. Snapshot triggered by server internally a. When loading old snapshot with new code, it will throw EOFException when trying to read the non-exist lastProcessedZxid value, and the exception will be caught. The lastProcessedZxid will be set using the snapshot file name. b. When loading new snapshot with old code, it will finish successfully after deserializing the digest value, the lastProcessedZxid at the end of snapshot file will be ignored. The lastProcessedZxid will be set using the snapshot file name. 2. Sync up between leader and follower The lastProcessedZxid will not be serialized by leader and deserialized by follower in both new and old code. It will be set to the lastProcessedZxid sent from leader via QuorumPacket. 3. Snapshot triggered via admin server APIs The feature flag need to be enabled for the snapshot command to work. #### 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 "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. Algorithm 3 was made default in 3.2.0 and prior versions (3.0.0 and 3.1.0) were using algorithm 1 and 2 as well. ###### Note >The implementations of leader election 1, and 2 were **deprecated** in 3.4.0. Since 3.6.0 only FastLeaderElection is available, in case of upgrade you have to shut down all of your servers and restart them with electionAlg=3 (or by removing the line from the configuration file). > * *maxTimeToWaitForEpoch* : (Java system property: **zookeeper.leader.maxTimeToWaitForEpoch**) **New in 3.6.0:** The maximum time to wait for epoch from voters when activating leader. If leader received a LOOKING notification from one of its voters, and it hasn't received epoch packets from majority within maxTimeToWaitForEpoch, then it will goto LOOKING and elect leader again. This can be tuned to reduce the quorum or server unavailable time, it can be set to be much smaller than initLimit * tickTime. In cross datacenter environment, it can be set to something like 2s. * *initLimit* : (No Java system property) Amount of time, in ticks (see [tickTime](#id_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. * *connectToLearnerMasterLimit* : (Java system property: zookeeper.**connectToLearnerMasterLimit**) Amount of time, in ticks (see [tickTime](#id_tickTime)), to allow followers to connect to the leader after leader election. Defaults to the value of initLimit. Use when initLimit is high so connecting to learner master doesn't result in higher timeout. * *leaderServes* : (Java system property: zookeeper.**leaderServes**) Leader accepts client connections. Default value is "yes". The leader machine coordinates updates. For higher update throughput at the 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 used to connect to the leader, and the second is for leader election. If you want to test multiple servers on a single machine, then different ports can be used for each server. Since ZooKeeper 3.6.0 it is possible to specify **multiple addresses** for each ZooKeeper server (see [ZOOKEEPER-3188](https://issues.apache.org/jira/projects/ZOOKEEPER/issues/ZOOKEEPER-3188)). To enable this feature, you must set the *multiAddress.enabled* configuration property to *true*. This helps to increase availability and adds network level resiliency to ZooKeeper. When multiple physical network interfaces are used for the servers, ZooKeeper is able to bind on all interfaces and runtime switching to a working interface in case a network error. The different addresses can be specified in the config using a pipe ('|') character. A valid configuration using multiple addresses looks like: server.1=zoo1-net1:2888:3888|zoo1-net2:2889:3889 server.2=zoo2-net1:2888:3888|zoo2-net2:2889:3889 server.3=zoo3-net1:2888:3888|zoo3-net2:2889:3889 ###### Note >By enabling this feature, the Quorum protocol (ZooKeeper Server-Server protocol) will change. The users will not notice this and when anyone starts a ZooKeeper cluster with the new config, everything will work normally. However, it's not possible to enable this feature and specify multiple addresses during a rolling upgrade if the old ZooKeeper cluster didn't support the *multiAddress* feature (and the new Quorum protocol). In case if you need this feature but you also need to perform a rolling upgrade from a ZooKeeper cluster older than *3.6.0*, then you first need to do the rolling upgrade without enabling the MultiAddress feature and later make a separate rolling restart with the new configuration where **multiAddress.enabled** is set to **true** and multiple addresses are provided. * *syncLimit* : (No Java system property) Amount of time, in ticks (see [tickTime](#id_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](zookeeperHierarchicalQuorums.html) * *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](zookeeperHierarchicalQuorums.html) * *cnxTimeout* : (Java system property: zookeeper.**cnxTimeout**) Sets the timeout value for opening connections for leader election notifications. Only applicable if you are using electionAlg 3. ###### Note >Default value is 5 seconds. * *quorumCnxnTimeoutMs* : (Java system property: zookeeper.**quorumCnxnTimeoutMs**) Sets the read timeout value for the connections for leader election notifications. Only applicable if you are using electionAlg 3. ######Note >Default value is -1, which will then use the syncLimit * tickTime as the timeout. * *standaloneEnabled* : (No Java system property) **New in 3.5.0:** When set to false, a single server can be started in replicated mode, a lone participant can run with observers, and a cluster can reconfigure down to one node, and up from one node. The default is true for backwards compatibility. It can be set using QuorumPeerConfig's setStandaloneEnabled method or by adding "standaloneEnabled=false" or "standaloneEnabled=true" to a server's config file. * *reconfigEnabled* : (No Java system property) **New in 3.5.3:** This controls the enabling or disabling of [Dynamic Reconfiguration](zookeeperReconfig.html) feature. When the feature is enabled, users can perform reconfigure operations through the ZooKeeper client API or through ZooKeeper command line tools assuming users are authorized to perform such operations. When the feature is disabled, no user, including the super user, can perform a reconfiguration. Any attempt to reconfigure will return an error. **"reconfigEnabled"** option can be set as **"reconfigEnabled=false"** or **"reconfigEnabled=true"** to a server's config file, or using QuorumPeerConfig's setReconfigEnabled method. The default value is false. If present, the value should be consistent across every server in the entire ensemble. Setting the value as true on some servers and false on other servers will cause inconsistent behavior depending on which server is elected as leader. If the leader has a setting of **"reconfigEnabled=true"**, then the ensemble will have reconfig feature enabled. If the leader has a setting of **"reconfigEnabled=false"**, then the ensemble will have reconfig feature disabled. It is thus recommended having a consistent value for **"reconfigEnabled"** across servers in the ensemble. * *4lw.commands.whitelist* : (Java system property: **zookeeper.4lw.commands.whitelist**) **New in 3.5.3:** A list of comma separated [Four Letter Words](#sc_4lw) commands that user wants to use. A valid Four Letter Words command must be put in this list else ZooKeeper server will not enable the command. By default the whitelist only contains "srvr" command which zkServer.sh uses. Additionally, if Read Only Mode is enabled by setting Java system property **readonlymode.enabled**, then the "isro" command is added to the whitelist. The rest of four-letter word commands are disabled by default: attempting to use them will gain a response ".... is not executed because it is not in the whitelist." Here's an example of the configuration that enables stat, ruok, conf, and isro command while disabling the rest of Four Letter Words command: 4lw.commands.whitelist=stat, ruok, conf, isro If you really need enable all four-letter word commands by default, you can use the asterisk option so you don't have to include every command one by one in the list. As an example, this will enable all four-letter word commands: 4lw.commands.whitelist=* * *tcpKeepAlive* : (Java system property: **zookeeper.tcpKeepAlive**) **New in 3.5.4:** Setting this to true sets the TCP keepAlive flag on the sockets used by quorum members to perform elections. This will allow for connections between quorum members to remain up when there is network infrastructure that may otherwise break them. Some NATs and firewalls may terminate or lose state for long-running or idle connections. Enabling this option relies on OS level settings to work properly, check your operating system's options regarding TCP keepalive for more information. Defaults to **false**. * *clientTcpKeepAlive* : (Java system property: **zookeeper.clientTcpKeepAlive**) **New in 3.6.1:** Setting this to true sets the TCP keepAlive flag on the client sockets. Some broken network infrastructure may lose the FIN packet that is sent from closing client. These never closed client sockets cause OS resource leak. Enabling this option terminates these zombie sockets by idle check. Enabling this option relies on OS level settings to work properly, check your operating system's options regarding TCP keepalive for more information. Defaults to **false**. Please note the distinction between it and **tcpKeepAlive**. It is applied for the client sockets while **tcpKeepAlive** is for the sockets used by quorum members. Currently this option is only available when default `NIOServerCnxnFactory` is used. * *electionPortBindRetry* : (Java system property only: **zookeeper.electionPortBindRetry**) Property set max retry count when Zookeeper server fails to bind leader election port. Such errors can be temporary and recoverable, such as DNS issue described in [ZOOKEEPER-3320](https://issues.apache.org/jira/projects/ZOOKEEPER/issues/ZOOKEEPER-3320), or non-retryable, such as port already in use. In case of transient errors, this property can improve availability of Zookeeper server and help it to self recover. Default value 3. In container environment, especially in Kubernetes, this value should be increased or set to 0(infinite retry) to overcome issues related to DNS name resolving. * *observer.reconnectDelayMs* : (Java system property: **zookeeper.observer.reconnectDelayMs**) When observer loses its connection with the leader, it waits for the specified value before trying to reconnect with the leader so that the entire observer fleet won't try to run leader election and reconnect to the leader at once. Defaults to 0 ms. * *observer.election.DelayMs* : (Java system property: **zookeeper.observer.election.DelayMs**) Delay the observer's participation in a leader election upon disconnect so as to prevent unexpected additional load on the voting peers during the process. Defaults to 200 ms. * *localSessionsEnabled* and *localSessionsUpgradingEnabled* : **New in 3.5:** Optional value is true or false. Their default values are false. Turning on the local session feature by setting *localSessionsEnabled=true*. Turning on *localSessionsUpgradingEnabled* can upgrade a local session to a global session automatically as required (e.g. creating ephemeral nodes), which only matters when *localSessionsEnabled* is enabled. #### Encryption, Authentication, Authorization Options The options in this section allow control over encryption/authentication/authorization performed by the service. Beside this page, you can also find useful information about client side configuration in the [Programmers Guide](zookeeperProgrammers.html#sc_java_client_configuration). The ZooKeeper Wiki also has useful pages about [ZooKeeper SSL support](https://cwiki.apache.org/confluence/display/ZOOKEEPER/ZooKeeper+SSL+User+Guide), and [SASL authentication for ZooKeeper](https://cwiki.apache.org/confluence/display/ZOOKEEPER/ZooKeeper+and+SASL). * *DigestAuthenticationProvider.enabled* : (Java system property: **zookeeper.DigestAuthenticationProvider.enabled**) **New in 3.7:** Determines whether the `digest` authentication provider is enabled. The default value is **true** for backwards compatibility, but it may be a good idea to disable this provider if not used, as it can result in misleading entries appearing in audit logs (see [ZOOKEEPER-3979](https://issues.apache.org/jira/browse/ZOOKEEPER-3979)) * *DigestAuthenticationProvider.superDigest* : (Java system property: **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:". Provide the generated "super:" 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:". 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. * *DigestAuthenticationProvider.digestAlg* : (Java system property: **zookeeper.DigestAuthenticationProvider.digestAlg**) **New in 3.7.0:** Set ACL digest algorithm. The default value is: `SHA1` which will be deprecated in the future for security issues. Set this property the same value in all the servers. - How to support other more algorithms? - modify the `java.security` configuration file under `$JAVA_HOME/jre/lib/security/java.security` by specifying: `security.provider.=`. ``` For example: set zookeeper.DigestAuthenticationProvider.digestAlg=RipeMD160 security.provider.3=org.bouncycastle.jce.provider.BouncyCastleProvider ``` - copy the jar file to `$JAVA_HOME/jre/lib/ext/`. ``` For example: copy bcprov-jdk18on-1.60.jar to $JAVA_HOME/jre/lib/ext/ ``` - How to migrate from one digest algorithm to another? - 1. Regenerate `superDigest` when migrating to new algorithm. - 2. `SetAcl` for a znode which already had a digest auth of old algorithm. * *IPAuthenticationProvider.usexforwardedfor* : (Java system property: **zookeeper.IPAuthenticationProvider.usexforwardedfor**) **New in 3.9.3:** IPAuthenticationProvider uses the client IP address to authenticate the user. By default it reads the **Host** HTTP header to detect client IP address. In some proxy configurations the proxy server adds the **X-Forwarded-For** header to the request in order to provide the IP address of the original client request. By enabling **usexforwardedfor** ZooKeeper setting, **X-Forwarded-For** will be preferred over the standard **Host** header. Default value is **false**. * *X509AuthenticationProvider.superUser* : (Java system property: **zookeeper.X509AuthenticationProvider.superUser**) The SSL-backed way to enable a ZooKeeper ensemble administrator to access the znode hierarchy as a "super" user. When this parameter is set to an X500 principal name, only an authenticated client with that principal will be able to bypass ACL checking and have full privileges to all znodes. * *zookeeper.superUser* : (Java system property: **zookeeper.superUser**) Similar to **zookeeper.X509AuthenticationProvider.superUser** but is generic for SASL based logins. It stores the name of a user that can access the znode hierarchy as a "super" user. You can specify multiple SASL super users using the **zookeeper.superUser.[suffix]** notation, e.g.: `zookeeper.superUser.1=...`. * *ssl.authProvider* : (Java system property: **zookeeper.ssl.authProvider**) Specifies a subclass of **org.apache.zookeeper.auth.X509AuthenticationProvider** to use for secure client authentication. This is useful in certificate key infrastructures that do not use JKS. It may be necessary to extend **javax.net.ssl.X509KeyManager** and **javax.net.ssl.X509TrustManager** to get the desired behavior from the SSL stack. To configure the ZooKeeper server to use the custom provider for authentication, choose a scheme name for the custom AuthenticationProvider and set the property **zookeeper.authProvider.[scheme]** to the fully-qualified class name of the custom implementation. This will load the provider into the ProviderRegistry. Then set this property **zookeeper.ssl.authProvider=[scheme]** and that provider will be used for secure authentication. * *zookeeper.ensembleAuthName* : (Java system property only: **zookeeper.ensembleAuthName**) **New in 3.6.0:** Specify a list of comma-separated valid names/aliases of an ensemble. A client can provide the ensemble name it intends to connect as the credential for scheme "ensemble". The EnsembleAuthenticationProvider will check the credential against the list of names/aliases of the ensemble that receives the connection request. If the credential is not in the list, the connection request will be refused. This prevents a client accidentally connecting to a wrong ensemble. * *sessionRequireClientSASLAuth* : (Java system property: **zookeeper.sessionRequireClientSASLAuth**) **New in 3.6.0:** When set to **true**, ZooKeeper server will only accept connections and requests from clients that have authenticated with server via SASL. Clients that are not configured with SASL authentication, or configured with SASL but failed authentication (i.e. with invalid credential) will not be able to establish a session with server. A typed error code (-124) will be delivered in such case, both Java and C client will close the session with server thereafter, without further attempts on retrying to reconnect. This configuration is shorthand for **enforce.auth.enabled=true** and **enforce.auth.scheme=sasl** By default, this feature is disabled. Users who would like to opt-in can enable the feature by setting **sessionRequireClientSASLAuth** to **true**. This feature overrules the zookeeper.allowSaslFailedClients option, so even if server is configured to allow clients that fail SASL authentication to login, client will not be able to establish a session with server if this feature is enabled. * *enforce.auth.enabled* : (Java system property : **zookeeper.enforce.auth.enabled**) **New in 3.7.0:** When set to **true**, ZooKeeper server will only accept connections and requests from clients that have authenticated with server via configured auth scheme. Authentication schemes can be configured using property enforce.auth.schemes. Clients that are not configured with the any of the auth scheme configured at server or configured but failed authentication (i.e. with invalid credential) will not be able to establish a session with server. A typed error code (-124) will be delivered in such case, both Java and C client will close the session with server thereafter, without further attempts on retrying to reconnect. By default, this feature is disabled. Users who would like to opt-in can enable the feature by setting **enforce.auth.enabled** to **true**. When **enforce.auth.enabled=true** and **enforce.auth.schemes=sasl** then zookeeper.allowSaslFailedClients configuration is overruled. So even if server is configured to allow clients that fail SASL authentication to login, client will not be able to establish a session with server if this feature is enabled with sasl as authentication scheme. * *enforce.auth.schemes* : (Java system property : **zookeeper.enforce.auth.schemes**) **New in 3.7.0:** Comma separated list of authentication schemes. Clients must be authenticated with at least one authentication scheme before doing any zookeeper operations. This property is used only when **enforce.auth.enabled** is to **true**. * *sslQuorum* : (Java system property: **zookeeper.sslQuorum**) **New in 3.5.5:** Enables encrypted quorum communication. Default is `false`. When enabling this feature, please also consider enabling *leader.closeSocketAsync* and *learner.closeSocketAsync* to avoid issues associated with the potentially long socket closing time when shutting down an SSL connection. * *ssl.keyStore.location and ssl.keyStore.password* and *ssl.quorum.keyStore.location* and *ssl.quorum.keyStore.password* : (Java system properties: **zookeeper.ssl.keyStore.location** and **zookeeper.ssl.keyStore.password** and **zookeeper.ssl.quorum.keyStore.location** and **zookeeper.ssl.quorum.keyStore.password**) **New in 3.5.5:** Specifies the file path to a Java keystore containing the local credentials to be used for client and quorum TLS connections, and the password to unlock the file. * *ssl.keyStore.passwordPath* and *ssl.quorum.keyStore.passwordPath* : (Java system properties: **zookeeper.ssl.keyStore.passwordPath** and **zookeeper.ssl.quorum.keyStore.passwordPath**) **New in 3.8.0:** Specifies the file path that contains the keystore password. Reading the password from a file takes precedence over the explicit password property. * *ssl.keyStore.type* and *ssl.quorum.keyStore.type* : (Java system properties: **zookeeper.ssl.keyStore.type** and **zookeeper.ssl.quorum.keyStore.type**) **New in 3.5.5:** Specifies the file format of client and quorum keystores. Values: JKS, PEM, PKCS12 or null (detect by filename). Default: null. **New in 3.5.10, 3.6.3, 3.7.0:** The format BCFKS was added. * *ssl.trustStore.location* and *ssl.trustStore.password* and *ssl.quorum.trustStore.location* and *ssl.quorum.trustStore.password* : (Java system properties: **zookeeper.ssl.trustStore.location** and **zookeeper.ssl.trustStore.password** and **zookeeper.ssl.quorum.trustStore.location** and **zookeeper.ssl.quorum.trustStore.password**) **New in 3.5.5:** Specifies the file path to a Java truststore containing the remote credentials to be used for client and quorum TLS connections, and the password to unlock the file. * *ssl.trustStore.passwordPath* and *ssl.quorum.trustStore.passwordPath* : (Java system properties: **zookeeper.ssl.trustStore.passwordPath** and **zookeeper.ssl.quorum.trustStore.passwordPath**) **New in 3.8.0:** Specifies the file path that contains the truststore password. Reading the password from a file takes precedence over the explicit password property. * *ssl.trustStore.type* and *ssl.quorum.trustStore.type* : (Java system properties: **zookeeper.ssl.trustStore.type** and **zookeeper.ssl.quorum.trustStore.type**) **New in 3.5.5:** Specifies the file format of client and quorum trustStores. Values: JKS, PEM, PKCS12 or null (detect by filename). Default: null. **New in 3.5.10, 3.6.3, 3.7.0:** The format BCFKS was added. * *ssl.protocol* and *ssl.quorum.protocol* : (Java system properties: **zookeeper.ssl.protocol** and **zookeeper.ssl.quorum.protocol**) **New in 3.5.5:** Specifies to protocol to be used in client and quorum TLS negotiation. Default: TLSv1.3 or TLSv1.2 depending on Java runtime version being used. * *ssl.enabledProtocols* and *ssl.quorum.enabledProtocols* : (Java system properties: **zookeeper.ssl.enabledProtocols** and **zookeeper.ssl.quorum.enabledProtocols**) **New in 3.5.5:** Specifies the enabled protocols in client and quorum TLS negotiation. Default: TLSv1.3, TLSv1.2 if value of `protocol` property is TLSv1.3. TLSv1.2 if `protocol` is TLSv1.2. * *ssl.ciphersuites* and *ssl.quorum.ciphersuites* : (Java system properties: **zookeeper.ssl.ciphersuites** and **zookeeper.ssl.quorum.ciphersuites**) **New in 3.5.5:** Specifies the enabled cipher suites to be used in client and quorum TLS negotiation. Default: Enabled cipher suites depend on the Java runtime version being used. * *ssl.context.supplier.class* and *ssl.quorum.context.supplier.class* : (Java system properties: **zookeeper.ssl.context.supplier.class** and **zookeeper.ssl.quorum.context.supplier.class**) **New in 3.5.5:** Specifies the class to be used for creating SSL context in client and quorum SSL communication. This allows you to use custom SSL context and implement the following scenarios: 1. Use hardware keystore, loaded in using PKCS11 or something similar. 2. You don't have access to the software keystore, but can retrieve an already-constructed SSLContext from their container. Default: null * *ssl.hostnameVerification* and *ssl.quorum.hostnameVerification* : (Java system properties: **zookeeper.ssl.hostnameVerification** and **zookeeper.ssl.quorum.hostnameVerification**) **New in 3.5.5:** Specifies whether the hostname verification is enabled in client and quorum TLS negotiation process. Disabling it only recommended for testing purposes. Default: true * *ssl.clientHostnameVerification* and *ssl.quorum.clientHostnameVerification* : (Java system properties: **zookeeper.ssl.clientHostnameVerification** and **zookeeper.ssl.quorum.clientHostnameVerification**) **New in 3.9.4:** Specifies whether the client's hostname verification is enabled in client and quorum TLS negotiation process. This option requires the corresponding *hostnameVerification* option to be `true`, or it will be ignored. Default: true for quorum, false for clients * *ssl.crl* and *ssl.quorum.crl* : (Java system properties: **zookeeper.ssl.crl** and **zookeeper.ssl.quorum.crl**) **New in 3.5.5:** Specifies whether Certificate Revocation List is enabled in client and quorum TLS protocols. Default: false * *ssl.ocsp* and *ssl.quorum.ocsp* : (Java system properties: **zookeeper.ssl.ocsp** and **zookeeper.ssl.quorum.ocsp**) **New in 3.5.5:** Specifies whether Online Certificate Status Protocol is enabled in client and quorum TLS protocols. Default: false * *ssl.clientAuth* and *ssl.quorum.clientAuth* : (Java system properties: **zookeeper.ssl.clientAuth** and **zookeeper.ssl.quorum.clientAuth**) **Added in 3.5.5, but broken until 3.5.7:** Specifies options to authenticate ssl connections from clients. Valid values are * "none": server will not request client authentication * "want": server will "request" client authentication * "need": server will "require" client authentication Default: "need" * *ssl.handshakeDetectionTimeoutMillis* and *ssl.quorum.handshakeDetectionTimeoutMillis* : (Java system properties: **zookeeper.ssl.handshakeDetectionTimeoutMillis** and **zookeeper.ssl.quorum.handshakeDetectionTimeoutMillis**) **New in 3.5.5:** TBD * *ssl.sslProvider* : (Java system property: **zookeeper.ssl.sslProvider**) **New in 3.9.0:** Allows to select SSL provider in the client-server communication when TLS is enabled. Netty-tcnative native library has been added to ZooKeeper in version 3.9.0 which allows us to use native SSL libraries like OpenSSL on supported platforms. See the available options in Netty-tcnative documentation. Default value is "JDK". * *sslQuorumReloadCertFiles* : (No Java system property) **New in 3.5.5, 3.6.0:** Allows Quorum SSL keyStore and trustStore reloading when the certificates on the filesystem change without having to restart the ZK process. Default: false * *client.certReload* : (Java system property: **zookeeper.client.certReload**) **New in 3.7.2, 3.8.1, 3.9.0:** Allows client SSL keyStore and trustStore reloading when the certificates on the filesystem change without having to restart the ZK process. Default: false * *client.portUnification*: (Java system property: **zookeeper.client.portUnification**) Specifies that the client port should accept SSL connections (using the same configuration as the secure client port). Default: false * *authProvider*: (Java system property: **zookeeper.authProvider**) You can specify multiple authentication provider classes for ZooKeeper. Usually you use this parameter to specify the SASL authentication provider like: `authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider` * *kerberos.removeHostFromPrincipal* (Java system property: **zookeeper.kerberos.removeHostFromPrincipal**) You can instruct ZooKeeper to remove the host from the client principal name during authentication. (e.g. the zk/myhost@EXAMPLE.COM client principal will be authenticated in ZooKeeper as zk@EXAMPLE.COM) Default: false * *kerberos.removeRealmFromPrincipal* (Java system property: **zookeeper.kerberos.removeRealmFromPrincipal**) You can instruct ZooKeeper to remove the realm from the client principal name during authentication. (e.g. the zk/myhost@EXAMPLE.COM client principal will be authenticated in ZooKeeper as zk/myhost) Default: false * *kerberos.canonicalizeHostNames* (Java system property: **zookeeper.kerberos.canonicalizeHostNames**) **New in 3.7.0:** Instructs ZooKeeper to canonicalize server host names extracted from *server.x* lines. This allows using e.g. `CNAME` records to reference servers in configuration files, while still enabling SASL Kerberos authentication between quorum members. It is essentially the quorum equivalent of the *zookeeper.sasl.client.canonicalize.hostname* property for clients. The default value is **false** for backwards compatibility. * *multiAddress.enabled* : (Java system property: **zookeeper.multiAddress.enabled**) **New in 3.6.0:** Since ZooKeeper 3.6.0 you can also [specify multiple addresses](#id_multi_address) for each ZooKeeper server instance (this can increase availability when multiple physical network interfaces can be used parallel in the cluster). Setting this parameter to **true** will enable this feature. Please note, that you can not enable this feature during a rolling upgrade if the version of the old ZooKeeper cluster is prior to 3.6.0. The default value is **false**. * *multiAddress.reachabilityCheckTimeoutMs* : (Java system property: **zookeeper.multiAddress.reachabilityCheckTimeoutMs**) **New in 3.6.0:** Since ZooKeeper 3.6.0 you can also [specify multiple addresses](#id_multi_address) for each ZooKeeper server instance (this can increase availability when multiple physical network interfaces can be used parallel in the cluster). ZooKeeper will perform ICMP ECHO requests or try to establish a TCP connection on port 7 (Echo) of the destination host in order to find the reachable addresses. This happens only if you provide multiple addresses in the configuration. In this property you can set the timeout in milliseconds for the reachability check. The check happens in parallel for the different addresses, so the timeout you set here is the maximum time will be taken by checking the reachability of all addresses. The default value is **1000**. This parameter has no effect, unless you enable the MultiAddress feature by setting *multiAddress.enabled=true*. * *fips-mode* : (Java system property: **zookeeper.fips-mode**) **New in 3.8.2:** Enable FIPS compatibility mode in ZooKeeper. If enabled, the following things will be changed in order to comply with FIPS requirements: * Custom trust manager (`ZKTrustManager`) that is used for hostname verification will be disabled. As a consequence, hostname verification is not available in the Quorum protocol, but still can be set in client-server communication. * DIGEST-MD5 Sasl auth mechanism will be disabled in Quorum and ZooKeeper Sasl clients. Only GSSAPI (Kerberos) can be used. Default: **true** (3.9.0+), **false** (3.8.x) #### Experimental Options/Features New features that are currently considered experimental. * *Read Only Mode Server* : (Java system property: **readonlymode.enabled**) **New in 3.4.0:** Setting this value to true enables Read Only Mode server support (disabled by default). ROM allows clients sessions which requested ROM support to connect to the server even when the server might be partitioned from the quorum. In this mode ROM clients can still read values from the ZK service, but will be unable to write values and see changes from other clients. See ZOOKEEPER-784 for more details. * *zookeeper.follower.skipLearnerRequestToNextProcessor* : (Java system property: **zookeeper.follower.skipLearnerRequestToNextProcessor**) When our cluster has observers which are connected with ObserverMaster, then turning on this flag might help you reduce some memory pressure on the Observer Master. If your cluster doesn't have any observers or they are not connected with ObserverMaster or your Observer's don't make much writes, then using this flag won't help you. Currently the change here is guarded behind the flag to help us get more confidence around the memory gains. In Long run, we might want to remove this flag and set its behavior as the default codepath. #### 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 unit is: byte. The default is 0xfffff(1048575) bytes, or just under 1M. - If this option is changed, the system property must be set on all servers and clients otherwise problems will arise. - When *jute.maxbuffer* in the client side is greater than the server side, the client wants to write the data exceeds *jute.maxbuffer* in the server side, the server side will get **java.io.IOException: Len error** - When *jute.maxbuffer* in the client side is less than the server side, the client wants to read the data exceeds *jute.maxbuffer* in the client side, the client side will get **java.io.IOException: Unreasonable length** or **Packet len is out of range!** - This is really a sanity check. ZooKeeper is designed to store data on the order of kilobytes in size. In the production environment, increasing this property to exceed the default value is not recommended for the following reasons: - Large size znodes cause unwarranted latency spikes, worsen the throughput - Large size znodes make the synchronization time between leader and followers unpredictable and non-convergent(sometimes timeout), cause the quorum unstable * *jute.maxbuffer.extrasize*: (Java system property: **zookeeper.jute.maxbuffer.extrasize**) **New in 3.5.7:** While processing client requests ZooKeeper server adds some additional information into the requests before persisting it as a transaction. Earlier this additional information size was fixed to 1024 bytes. For many scenarios, specially scenarios where jute.maxbuffer value is more than 1 MB and request type is multi, this fixed size was insufficient. To handle all the scenarios additional information size is increased from 1024 byte to same as jute.maxbuffer size and also it is made configurable through jute.maxbuffer.extrasize. Generally this property is not required to be configured as default value is the most optimal value. * *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. * *quorumListenOnAllIPs* : When set to true the ZooKeeper server will listen for connections from its peers on all available IP addresses, and not only the address configured in the server list of the configuration file. It affects the connections handling the ZAB protocol and the Fast Leader Election protocol. Default value is **false**. * *multiAddress.reachabilityCheckEnabled* : (Java system property: **zookeeper.multiAddress.reachabilityCheckEnabled**) **New in 3.6.0:** Since ZooKeeper 3.6.0 you can also [specify multiple addresses](#id_multi_address) for each ZooKeeper server instance (this can increase availability when multiple physical network interfaces can be used parallel in the cluster). ZooKeeper will perform ICMP ECHO requests or try to establish a TCP connection on port 7 (Echo) of the destination host in order to find the reachable addresses. This happens only if you provide multiple addresses in the configuration. The reachable check can fail if you hit some ICMP rate-limitation, (e.g. on macOS) when you try to start a large (e.g. 11+) ensemble members cluster on a single machine for testing. Default value is **true**. By setting this parameter to 'false' you can disable the reachability checks. Please note, disabling the reachability check will cause the cluster not to be able to reconfigure itself properly during network problems, so the disabling is advised only during testing. This parameter has no effect, unless you enable the MultiAddress feature by setting *multiAddress.enabled=true*. #### Disabling data directory autocreation **New in 3.5:** The default behavior of a ZooKeeper server is to automatically create the data directory (specified in the configuration file) when started if that directory does not already exist. This can be inconvenient and even dangerous in some cases. Take the case where a configuration change is made to a running server, wherein the **dataDir** parameter is accidentally changed. When the ZooKeeper server is restarted it will create this non-existent directory and begin serving - with an empty znode namespace. This scenario can result in an effective "split brain" situation (i.e. data in both the new invalid directory and the original valid data store). As such is would be good to have an option to turn off this autocreate behavior. In general for production environments this should be done, unfortunately however the default legacy behavior cannot be changed at this point and therefore this must be done on a case by case basis. This is left to users and to packagers of ZooKeeper distributions. When running **zkServer.sh** autocreate can be disabled by setting the environment variable **ZOO_DATADIR_AUTOCREATE_DISABLE** to 1. When running ZooKeeper servers directly from class files this can be accomplished by setting **zookeeper.datadir.autocreate=false** on the java command line, i.e. **-Dzookeeper.datadir.autocreate=false** When this feature is disabled, and the ZooKeeper server determines that the required directories do not exist it will generate an error and refuse to start. A new script **zkServer-initialize.sh** is provided to support this new feature. If autocreate is disabled it is necessary for the user to first install ZooKeeper, then create the data directory (and potentially txnlog directory), and then start the server. Otherwise as mentioned in the previous paragraph the server will not start. Running **zkServer-initialize.sh** will create the required directories, and optionally set up the myid file (optional command line parameter). This script can be used even if the autocreate feature itself is not used, and will likely be of use to users as this (setup, including creation of the myid file) has been an issue for users in the past. Note that this script ensures the data directories exist only, it does not create a config file, but rather requires a config file to be available in order to execute. #### Enabling db existence validation **New in 3.6.0:** The default behavior of a ZooKeeper server on startup when no data tree is found is to set zxid to zero and join the quorum as a voting member. This can be dangerous if some event (e.g. a rogue 'rm -rf') has removed the data directory while the server was down since this server may help elect a leader that is missing transactions. Enabling db existence validation will change the behavior on startup when no data tree is found: the server joins the ensemble as a non-voting participant until it is able to sync with the leader and acquire an up-to-date version of the ensemble data. To indicate an empty data tree is expected (ensemble creation), the user should place a file 'initialize' in the same directory as 'myid'. This file will be detected and deleted by the server on startup. Initialization validation can be enabled when running ZooKeeper servers directly from class files by setting **zookeeper.db.autocreate=false** on the java command line, i.e. **-Dzookeeper.db.autocreate=false**. Running **zkServer-initialize.sh** will create the required initialization file. #### Performance Tuning Options **New in 3.5.0:** Several subsystems have been reworked to improve read throughput. This includes multi-threading of the NIO communication subsystem and request processing pipeline (Commit Processor). NIO is the default client/server communication subsystem. Its threading model comprises 1 acceptor thread, 1-N selector threads and 0-M socket I/O worker threads. In the request processing pipeline the system can be configured to process multiple read request at once while maintaining the same consistency guarantee (same-session read-after-write). The Commit Processor threading model comprises 1 main thread and 0-N worker threads. The default values are aimed at maximizing read throughput on a dedicated ZooKeeper machine. Both subsystems need to have sufficient amount of threads to achieve peak read throughput. * *zookeeper.nio.numSelectorThreads* : (Java system property only: **zookeeper.nio.numSelectorThreads**) **New in 3.5.0:** Number of NIO selector threads. At least 1 selector thread required. It is recommended to use more than one selector for large numbers of client connections. The default value is sqrt( number of cpu cores / 2 ). * *zookeeper.nio.numWorkerThreads* : (Java system property only: **zookeeper.nio.numWorkerThreads**) **New in 3.5.0:** Number of NIO worker threads. If configured with 0 worker threads, the selector threads do the socket I/O directly. The default value is 2 times the number of cpu cores. * *zookeeper.commitProcessor.numWorkerThreads* : (Java system property only: **zookeeper.commitProcessor.numWorkerThreads**) **New in 3.5.0:** Number of Commit Processor worker threads. If configured with 0 worker threads, the main thread will process the request directly. The default value is the number of cpu cores. * *zookeeper.commitProcessor.maxReadBatchSize* : (Java system property only: **zookeeper.commitProcessor.maxReadBatchSize**) Max number of reads to process from queuedRequests before switching to processing commits. If the value < 0 (default), we switch whenever we have a local write, and pending commits. A high read batch size will delay commit processing, causing stale data to be served. If reads are known to arrive in fixed size batches then matching that batch size with the value of this property can smooth queue performance. Since reads are handled in parallel, one recommendation is to set this property to match *zookeeper.commitProcessor.numWorkerThread* (default is the number of cpu cores) or lower. * *zookeeper.commitProcessor.maxCommitBatchSize* : (Java system property only: **zookeeper.commitProcessor.maxCommitBatchSize**) Max number of commits to process before processing reads. We will try to process as many remote/local commits as we can till we reach this count. A high commit batch size will delay reads while processing more commits. A low commit batch size will favor reads. It is recommended to only set this property when an ensemble is serving a workload with a high commit rate. If writes are known to arrive in a set number of batches then matching that batch size with the value of this property can smooth queue performance. A generic approach would be to set this value to equal the ensemble size so that with the processing of each batch the current server will probabilistically handle a write related to one of its direct clients. Default is "1". Negative and zero values are not supported. * *znode.container.checkIntervalMs* : (Java system property only) **New in 3.6.0:** The time interval in milliseconds for each check of candidate container and ttl nodes. Default is "60000". * *znode.container.maxPerMinute* : (Java system property only) **New in 3.6.0:** The maximum number of container and ttl nodes that can be deleted per minute. This prevents herding during container deletion. Default is "10000". * *znode.container.maxNeverUsedIntervalMs* : (Java system property only) **New in 3.6.0:** The maximum interval in milliseconds that a container that has never had any children is retained. Should be long enough for your client to create the container, do any needed work and then create children. Default is "0" which is used to indicate that containers that have never had any children are never deleted. #### Debug Observability Configurations **New in 3.6.0:** The following options are introduced to make zookeeper easier to debug. * *zookeeper.messageTracker.BufferSize* : (Java system property only) Controls the maximum number of messages stored in **MessageTracker**. Value should be positive integers. The default value is 10. **MessageTracker** is introduced in **3.6.0** to record the last set of messages between a server (follower or observer) and a leader, when a server disconnects with leader. These set of messages will then be dumped to zookeeper's log file, and will help reconstruct the state of the servers at the time of the disconnection and will be useful for debugging purpose. * *zookeeper.messageTracker.Enabled* : (Java system property only) When set to "true", will enable **MessageTracker** to track and record messages. Default value is "false". #### AdminServer configuration **New in 3.9.0:** The following options are used to configure the [AdminServer](#sc_adminserver). * *admin.rateLimiterIntervalInMS* : (Java system property: **zookeeper.admin.rateLimiterIntervalInMS**) The time interval for rate limiting admin command to protect the server. Defaults to 5 mins. * *admin.snapshot.enabled* : (Java system property: **zookeeper.admin.snapshot.enabled**) The flag for enabling the snapshot command. Defaults to true. * *admin.restore.enabled* : (Java system property: **zookeeper.admin.restore.enabled**) The flag for enabling the restore command. Defaults to true. * *admin.needClientAuth* : (Java system property: **zookeeper.admin.needClientAuth**) The flag to control whether client auth is needed. Using x509 auth requires true. Defaults to false. **New in 3.7.1:** The following options are used to configure the [AdminServer](#sc_adminserver). * *admin.forceHttps* : (Java system property: **zookeeper.admin.forceHttps**) Force AdminServer to use SSL, thus allowing only HTTPS traffic. Defaults to disabled. Overwrites **admin.portUnification** settings. **New in 3.6.0:** The following options are used to configure the [AdminServer](#sc_adminserver). * *admin.portUnification* : (Java system property: **zookeeper.admin.portUnification**) Enable the admin port to accept both HTTP and HTTPS traffic. Defaults to disabled. **New in 3.5.0:** The following options are used to configure the [AdminServer](#sc_adminserver). * *admin.enableServer* : (Java system property: **zookeeper.admin.enableServer**) Set to "false" to disable the AdminServer. By default the AdminServer is enabled. * *admin.serverAddress* : (Java system property: **zookeeper.admin.serverAddress**) The address the embedded Jetty server listens on. Defaults to 0.0.0.0. * *admin.serverPort* : (Java system property: **zookeeper.admin.serverPort**) The port the embedded Jetty server listens on. Defaults to 8080. * *admin.idleTimeout* : (Java system property: **zookeeper.admin.idleTimeout**) Set the maximum idle time in milliseconds that a connection can wait before sending or receiving data. Defaults to 30000 ms. * *admin.commandURL* : (Java system property: **zookeeper.admin.commandURL**) The URL for listing and issuing commands relative to the root URL. Defaults to "/commands". ### Metrics Providers **New in 3.6.0:** The following options are used to configure metrics. By default ZooKeeper server exposes useful metrics using the [AdminServer](#sc_adminserver). and [Four Letter Words](#sc_4lw) interface. Since 3.6.0 you can configure a different Metrics Provider, that exports metrics to your favourite system. Since 3.6.0 ZooKeeper binary package bundles an integration with [Prometheus.io](https://prometheus.io) * *metricsProvider.className* : Set to "org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider" to enable Prometheus.io exporter. * *metricsProvider.httpHost* : **New in 3.8.0:** Prometheus.io exporter will start a Jetty server and listen this address, default is "0.0.0.0" * *metricsProvider.httpPort* : Prometheus.io exporter will start a Jetty server and bind to this port, it defaults to 7000. Prometheus end point will be http://hostname:httPort/metrics. * *metricsProvider.exportJvmInfo* : If this property is set to **true** Prometheus.io will export useful metrics about the JVM. The default is true. * *metricsProvider.numWorkerThreads* : **New in 3.7.1:** Number of worker threads for reporting Prometheus summary metrics. Default value is 1. If the number is less than 1, the main thread will be used. * *metricsProvider.maxQueueSize* : **New in 3.7.1:** The max queue size for Prometheus summary metrics reporting task. Default value is 1000000. * *metricsProvider.workerShutdownTimeoutMs* : **New in 3.7.1:** The timeout in ms for Prometheus worker threads shutdown. Default value is 1000ms. ### Communication using the Netty framework [Netty](http://netty.io) is an NIO based client/server communication framework, it simplifies (over NIO being used directly) many of the complexities of network level communication for java applications. Additionally the Netty framework has built in support for encryption (SSL) and authentication (certificates). These are optional features and can be turned on or off individually. In versions 3.5+, a ZooKeeper server can use Netty instead of NIO (default option) by setting the environment variable **zookeeper.serverCnxnFactory** to **org.apache.zookeeper.server.NettyServerCnxnFactory**; for the client, set **zookeeper.clientCnxnSocket** to **org.apache.zookeeper.ClientCnxnSocketNetty**. #### Quorum TLS *New in 3.5.5* Based on the Netty Framework ZooKeeper ensembles can be set up to use TLS encryption in their communication channels. This section describes how to set up encryption on the quorum communication. Please note that Quorum TLS encapsulates securing both leader election and quorum communication protocols. 1. Create SSL keystore JKS to store local credentials One keystore should be created for each ZK instance. In this example we generate a self-signed certificate and store it together with the private key in `keystore.jks`. This is suitable for testing purposes, but you probably need an official certificate to sign your keys in a production environment. Please note that the alias (`-alias`) and the distinguished name (`-dname`) must match the hostname of the machine that is associated with, otherwise hostname verification won't work. ``` keytool -genkeypair -alias $(hostname -f) -keyalg RSA -keysize 2048 -dname "cn=$(hostname -f)" -keypass password -keystore keystore.jks -storepass password ``` 2. Extract the signed public key (certificate) from keystore *This step might only necessary for self-signed certificates.* ``` keytool -exportcert -alias $(hostname -f) -keystore keystore.jks -file $(hostname -f).cer -rfc ``` 3. Create SSL truststore JKS containing certificates of all ZooKeeper instances The same truststore (storing all accepted certs) should be shared on participants of the ensemble. You need to use different aliases to store multiple certificates in the same truststore. Name of the aliases doesn't matter. ``` keytool -importcert -alias [host1..3] -file [host1..3].cer -keystore truststore.jks -storepass password ``` 4. You need to use `NettyServerCnxnFactory` as serverCnxnFactory, because SSL is not supported by NIO. Add the following configuration settings to your `zoo.cfg` config file: ``` sslQuorum=true serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory ssl.quorum.keyStore.location=/path/to/keystore.jks ssl.quorum.keyStore.password=password ssl.quorum.trustStore.location=/path/to/truststore.jks ssl.quorum.trustStore.password=password ``` 5. Verify in the logs that your ensemble is running on TLS: ``` INFO [main:QuorumPeer@1789] - Using TLS encrypted quorum communication INFO [main:QuorumPeer@1797] - Port unification disabled ... INFO [QuorumPeerListener:QuorumCnxManager$Listener@877] - Creating TLS-only quorum server socket ``` #### Upgrading existing non-TLS cluster with no downtime *New in 3.5.5* Here are the steps needed to upgrade an already running ZooKeeper ensemble to TLS without downtime by taking advantage of port unification functionality. 1. Create the necessary keystores and truststores for all ZK participants as described in the previous section 2. Add the following config settings and restart the first node ``` sslQuorum=false portUnification=true serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory ssl.quorum.keyStore.location=/path/to/keystore.jks ssl.quorum.keyStore.password=password ssl.quorum.trustStore.location=/path/to/truststore.jks ssl.quorum.trustStore.password=password ``` Note that TLS is not yet enabled, but we turn on port unification. 3. Repeat step #2 on the remaining nodes. Verify that you see the following entries in the logs: ``` INFO [main:QuorumPeer@1791] - Using insecure (non-TLS) quorum communication INFO [main:QuorumPeer@1797] - Port unification enabled ... INFO [QuorumPeerListener:QuorumCnxManager$Listener@874] - Creating TLS-enabled quorum server socket ``` You should also double-check after each node restart that the quorum become healthy again. 4. Enable Quorum TLS on each node and do rolling restart: ``` sslQuorum=true portUnification=true ``` 5. Once you verified that your entire ensemble is running on TLS, you could disable port unification and do another rolling restart ``` sslQuorum=true portUnification=false ``` ### 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. **New in 3.5.3:** Four Letter Words need to be explicitly white listed before using. Please refer to **4lw.commands.whitelist** described in [cluster configuration section](#sc_clusterOptions) for details. Moving forward, Four Letter Words will be deprecated, please use [AdminServer](#sc_adminserver) instead. * *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. * *envi* : Print details about serving environment * *ruok* : Tests if the server is running in a non-error state. When the whitelist enables ruok, the server will respond with `imok` if it is running, otherwise it will not respond at all. When ruok is disabled, the server responds with: "ruok is not executed because it is not in the whitelist." 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. * *dirs* : **New in 3.5.1:** Shows the total size of snapshot and log files in bytes * *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. * *mntr* : **New in 3.4.0:** Outputs a list of variables that could be used for monitoring the health of the cluster. $ echo mntr | nc localhost 2185 zk_version 3.4.0 zk_avg_latency 0.7561 - be account to four decimal places zk_max_latency 0 zk_min_latency 0 zk_packets_received 70 zk_packets_sent 69 zk_outstanding_requests 0 zk_server_state leader zk_znode_count 4 zk_watch_count 0 zk_ephemerals_count 0 zk_approximate_data_size 27 zk_followers 4 - only exposed by the Leader zk_synced_followers 4 - only exposed by the Leader zk_pending_syncs 0 - only exposed by the Leader zk_open_file_descriptor_count 23 - only available on Unix platforms zk_max_file_descriptor_count 1024 - only available on Unix platforms The output is compatible with java properties format and the content may change over time (new keys added). Your scripts should expect changes. ATTENTION: Some of the keys are platform specific and some of the keys are only exported by the Leader. The output contains multiple lines with the following format: key \t value * *isro* : **New in 3.4.0:** Tests if server is running in read-only mode. The server will respond with "ro" if in read-only mode or "rw" if not in read-only mode. * *hash* : **New in 3.6.0:** Return the latest history of the tree digest associated with zxid. * *gtmk* : Gets the current trace mask as a 64-bit signed long value in decimal format. See `stmk` for an explanation of the possible values. * *stmk* : Sets the current trace mask. The trace mask is 64 bits, where each bit enables or disables a specific category of trace logging on the server. Logback must be configured to enable `TRACE` level first in order to see trace logging messages. The bits of the trace mask correspond to the following trace logging categories. | Trace Mask Bit Values | | |-----------------------|---------------------| | 0b0000000000 | Unused, reserved for future use. | | 0b0000000010 | Logs client requests, excluding ping requests. | | 0b0000000100 | Unused, reserved for future use. | | 0b0000001000 | Logs client ping requests. | | 0b0000010000 | Logs packets received from the quorum peer that is the current leader, excluding ping requests. | | 0b0000100000 | Logs addition, removal and validation of client sessions. | | 0b0001000000 | Logs delivery of watch events to client sessions. | | 0b0010000000 | Logs ping packets received from the quorum peer that is the current leader. | | 0b0100000000 | Unused, reserved for future use. | | 0b1000000000 | Unused, reserved for future use. | All remaining bits in the 64-bit value are unused and reserved for future use. Multiple trace logging categories are specified by calculating the bitwise OR of the documented values. The default trace mask is 0b0100110010. Thus, by default, trace logging includes client requests, packets received from the leader and sessions. To set a different trace mask, send a request containing the `stmk` four-letter word followed by the trace mask represented as a 64-bit signed long value. This example uses the Perl `pack` function to construct a trace mask that enables all trace logging categories described above and convert it to a 64-bit signed long value with big-endian byte order. The result is appended to `stmk` and sent to the server using netcat. The server responds with the new trace mask in decimal format. $ perl -e "print 'stmk', pack('q>', 0b0011111010)" | nc localhost 2181 250 Here's an example of the **ruok** command: $ echo ruok | nc 127.0.0.1 5111 imok #### The AdminServer **New in 3.5.0:** The AdminServer is an embedded Jetty server that provides an HTTP interface to the four-letter word commands. By default, the server is started on port 8080, and commands are issued by going to the URL "/commands/\[command name]", e.g., http://localhost:8080/commands/stat. The command response is returned as JSON. Unlike the original protocol, commands are not restricted to four-letter names, and commands can have multiple names; for instance, "stmk" can also be referred to as "set_trace_mask". To view a list of all available commands, point a browser to the URL /commands (e.g., http://localhost:8080/commands). See the [AdminServer configuration options](#sc_adminserver_config) for how to change the port and URLs. The AdminServer is enabled by default, but can be disabled by either: * Setting the zookeeper.admin.enableServer system property to false. * Removing Jetty from the classpath. (This option is useful if you would like to override ZooKeeper's jetty dependency.) Note that the TCP four-letter word interface is still available if the AdminServer is disabled. ##### Configuring AdminServer for SSL/TLS - Generating the **keystore.jks** and **truststore.jks** which can be found in the [Quorum TLS](http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#Quorum+TLS). - Add the following configuration settings to the `zoo.cfg` config file: ``` admin.portUnification=true ssl.quorum.keyStore.location=/path/to/keystore.jks ssl.quorum.keyStore.password=password ssl.quorum.trustStore.location=/path/to/truststore.jks ssl.quorum.trustStore.password=password ``` - Verify that the following entries in the logs can be seen: ``` 2019-08-03 15:44:55,213 [myid:] - INFO [main:JettyAdminServer@123] - Successfully loaded private key from /data/software/cert/keystore.jks 2019-08-03 15:44:55,213 [myid:] - INFO [main:JettyAdminServer@124] - Successfully loaded certificate authority from /data/software/cert/truststore.jks 2019-08-03 15:44:55,403 [myid:] - INFO [main:JettyAdminServer@170] - Started AdminServer on address 0.0.0.0, port 8080 and command URL /commands ``` Available commands include: * *connection_stat_reset/crst*: Reset all client connection statistics. No new fields returned. * *configuration/conf/config* : Print basic details about serving configuration, e.g. client port, absolute path to data directory. * *connections/cons* : Information on client connections to server. Note, depending on the number of client connections this operation may be expensive (i.e. impact server performance). Returns "connections", a list of connection info objects. * *hash*: Txn digests in the historical digest list. One is recorded every 128 transactions. Returns "digests", a list to transaction digest objects. * *dirs* : Information on logfile directory and snapshot directory size in bytes. Returns "datadir_size" and "logdir_size". * *dump* : Information on session expirations and ephemerals. Note, depending on the number of global sessions and ephemerals this operation may be expensive (i.e. impact server performance). Returns "expiry_time_to_session_ids" and "session_id_to_ephemeral_paths" as maps. * *environment/env/envi* : All defined environment variables. Returns each as its own field. * *get_trace_mask/gtmk* : The current trace mask. Read-only version of *set_trace_mask*. See the description of the four letter command *stmk* for more details. Returns "tracemask". * *initial_configuration/icfg* : Print the text of the configuration file used to start the peer. Returns "initial_configuration". * *is_read_only/isro* : A true/false if this server is in read-only mode. Returns "read_only". * *last_snapshot/lsnp* : Information of the last snapshot that zookeeper server has finished saving to disk. If called during the initial time period between the server starting up and the server finishing saving its first snapshot, the command returns the information of the snapshot read when starting up the server. Returns "zxid" and "timestamp", the latter using a time unit of seconds. * *leader/lead* : If the ensemble is configured in quorum mode then emits the current leader status of the peer and the current leader location. Returns "is_leader", "leader_id", and "leader_ip". * *monitor/mntr* : Emits a wide variety of useful info for monitoring. Includes performance stats, information about internal queues, and summaries of the data tree (among other things). Returns each as its own field. * *observer_connection_stat_reset/orst* : Reset all observer connection statistics. Companion command to *observers*. No new fields returned. * *restore/rest* : Restore database from snapshot input stream on the current server. Returns the following data in response payload: "last_zxid": String Note: this API is rate-limited (once every 5 mins by default) to protect the server from being over-loaded. * *ruok* : No-op command, check if the server is running. A response does not necessarily indicate that the server has joined the quorum, just that the admin server is active and bound to the specified port. No new fields returned. * *set_trace_mask/stmk* : Sets the trace mask (as such, it requires a parameter). Write version of *get_trace_mask*. See the description of the four letter command *stmk* for more details. Returns "tracemask". * *server_stats/srvr* : Server information. Returns multiple fields giving a brief overview of server state. * *snapshot/snap* : Takes a snapshot of the current server in the datadir and stream out data. Optional query parameter: "streaming": Boolean (defaults to true if the parameter is not present) Returns the following via Http headers: "last_zxid": String "snapshot_size": String Note: this API is rate-limited (once every 5 mins by default) to protect the server from being over-loaded. * *stats/stat* : Same as *server_stats* but also returns the "connections" field (see *connections* for details). Note, depending on the number of client connections this operation may be expensive (i.e. impact server performance). * *stat_reset/srst* : Resets server statistics. This is a subset of the information returned by *server_stats* and *stats*. No new fields returned. * *observers/obsr* : Information on observer connections to server. Always available on a Leader, available on a Follower if its acting as a learner master. Returns "synced_observers" (int) and "observers" (list of per-observer properties). * *system_properties/sysp* : All defined system properties. Returns each as its own field. * *voting_view* : Provides the current voting members in the ensemble. Returns "current_config" as a map. * *watches/wchc* : Watch information aggregated by session. Note, depending on the number of watches this operation may be expensive (i.e. impact server performance). Returns "session_id_to_watched_paths" as a map. * *watches_by_path/wchp* : Watch information aggregated by path. Note, depending on the number of watches this operation may be expensive (i.e. impact server performance). Returns "path_to_session_ids" as a map. * *watch_summary/wchs* : Summarized watch information. Returns "num_total_watches", "num_paths", and "num_connections". * *zabstate* : The current phase of Zab protocol that peer is running and whether it is a voting member. Peers can be in one of these phases: ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST. Returns fields "voting" and "zabstate". ### 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 or three files in it: * *myid* - contains a single integer in human readable ASCII text that represents the server id. * *initialize* - presence indicates lack of data tree is expected. Cleaned up once data tree is created. * *snapshot.* - 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 when the number of transactions written to the current log file reaches a (variable) threshold. The threshold is computed using the same parameter which influences the frequency of snapshotting (see snapCount and snapSizeLimitInKb above). The log file's suffix is the first zxid written to that log. #### File Management The format of snapshot and log files does not change between 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 troubleshooting. Using older log and snapshot files, you can look at the previous state of ZooKeeper servers and even restore that state. 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, all log files following it, and the last log file preceding it. The latter requirement is necessary to include updates which happened after this snapshot was started but went into the existing log file at that time. This is possible because snapshotting and rolling over of logs proceed somewhat independently in ZooKeeper. See the [maintenance](#sc_maintenance) section in this document for more details on setting a retention policy and maintenance of ZooKeeper storage. ###### Note >The data stored in these files is not encrypted. In the case of storing sensitive data in ZooKeeper, necessary measures need to be taken to prevent unauthorized access. Such measures are external to ZooKeeper (e.g., control access to the files) and depend on the individual settings in which it is being deployed. #### Recovery - TxnLogToolkit More details can be found in [this](http://zookeeper.apache.org/doc/current/zookeeperTools.html#zkTxnLogToolkit) ### 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 transaction 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 affect performance. If you only have one storage device, increase the snapCount so that snapshot files are generated less often; it does not eliminate the problem, but it makes more resources available for the transaction log. * *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. * *Publicly accessible deployment* : A ZooKeeper ensemble is expected to operate in a trusted computing environment. It is thus recommended deploying ZooKeeper behind a firewall. ### Best Practices For best results, take note of the following list of good Zookeeper practices: For multi-tenant installations see the [section](zookeeperProgrammers.html#ch_zkSessions) detailing ZooKeeper "chroot" support, this can be very useful when deploying many applications/services interfacing to a single ZooKeeper cluster. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperAuditLogs.md0100644 0000000 0000000 00000021111 15051152474 031751 0ustar00rootroot0000000 0000000 # ZooKeeper Audit Logging * [ZooKeeper Audit Logs](#ch_auditLogs) * [ZooKeeper Audit Log Configuration](#ch_reconfig_format) * [Who is taken as user in audit logs?](#ch_zkAuditUser) ## ZooKeeper Audit Logs Apache ZooKeeper supports audit logs from version 3.6.0. By default audit logs are disabled. To enable audit logs configure audit.enable=true in conf/zoo.cfg. Audit logs are not logged on all the ZooKeeper servers, but logged only on the servers where client is connected as depicted in below figure. ![Audit Logs](images/zkAuditLogs.jpg) The audit log captures detailed information for the operations that are selected to be audited. The audit information is written as a set of key=value pairs for the following keys | Key | Value | | ----- | ----- | |session | client session id | |user | comma separated list of users who are associate with a client session. For more on this, see [Who is taken as user in audit logs](#ch_zkAuditUser). |ip | client IP address |operation | any one of the selected operations for audit. Possible values are(serverStart, serverStop, create, delete, setData, setAcl, multiOperation, reconfig, ephemeralZNodeDeleteOnSessionClose) |znode | path of the znode |znode type | type of znode in case of creation operation |acl | String representation of znode ACL like cdrwa(create, delete,read, write, admin). This is logged only for setAcl operation |result | result of the operation. Possible values are (success/failure/invoked). Result "invoked" is used for serverStop operation because stop is logged before ensuring that server actually stopped. Below are sample audit logs for all operations, where client is connected from 192.168.1.2, client principal is zkcli@HADOOP.COM, server principal is zookeeper/192.168.1.3@HADOOP.COM user=zookeeper/192.168.1.3 operation=serverStart result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/a znode_type=persistent result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/a znode_type=persistent result=failure session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setData znode=/a result=failure session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setData znode=/a result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setAcl znode=/a acl=world:anyone:cdrwa result=failure session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setAcl znode=/a acl=world:anyone:cdrwa result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/b znode_type=persistent result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=setData znode=/b result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=delete znode=/b result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=multiOperation result=failure session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=delete znode=/a result=failure session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=delete znode=/a result=success session=0x19344730001 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=create znode=/ephemral znode_type=ephemral result=success session=0x19344730001 user=zookeeper/192.168.1.3 operation=ephemeralZNodeDeletionOnSessionCloseOrExpire znode=/ephemral result=success session=0x19344730000 user=192.168.1.2,zkcli@HADOOP.COM ip=192.168.1.2 operation=reconfig znode=/zookeeper/config result=success user=zookeeper/192.168.1.3 operation=serverStop result=invoked ## ZooKeeper Audit Log Configuration By default audit logs are disabled. To enable audit logs configure `audit.enable=true` in _conf/zoo.cfg_. Audit logging is done using logback. Following is the default logback configuration for audit logs in `conf/logback.xml` Change above configuration to customize the auditlog file, number of backups, max file size, custom audit logger etc. ## Who is taken as user in audit logs? By default there are only four authentication provider: * IPAuthenticationProvider * SASLAuthenticationProvider * X509AuthenticationProvider * DigestAuthenticationProvider User is decided based on the configured authentication provider: * When IPAuthenticationProvider is configured then authenticated IP is taken as user * When SASLAuthenticationProvider is configured then client principal is taken as user * When X509AuthenticationProvider is configured then client certificate is taken as user * When DigestAuthenticationProvider is configured then authenticated user is user Custom authentication provider can override org.apache.zookeeper.server.auth.AuthenticationProvider.getUserName(String id) to provide user name. If authentication provider is not overriding this method then whatever is stored in org.apache.zookeeper.data.Id.id is taken as user. Generally only user name is stored in this field but it is up to the custom authentication provider what they store in it. For audit logging value of org.apache.zookeeper.data.Id.id would be taken as user. In ZooKeeper Server not all the operations are done by clients but some operations are done by the server itself. For example when client closes the session, ephemeral znodes are deleted by the Server. These deletion are not done by clients directly but it is done the server itself these are called system operations. For these system operations the user associated with the ZooKeeper server are taken as user while audit logging these operations. For example if in ZooKeeper server principal is zookeeper/hadoop.hadoop.com@HADOOP.COM then this becomes the system user and all the system operations will be logged with this user name. user=zookeeper/hadoop.hadoop.com@HADOOP.COM operation=serverStart result=success If there is no user associate with ZooKeeper server then the user who started the ZooKeeper server is taken as the user. For example if server started by root then root is taken as the system user user=root operation=serverStart result=success Single client can attach multiple authentication schemes to a session, in this case all authenticated schemes will taken taken as user and will be presented as comma separated list. For example if a client is authenticate with principal zkcli@HADOOP.COM and ip 127.0.0.1 then create znode audit log will be as: session=0x10c0bcb0000 user=zkcli@HADOOP.COM,127.0.0.1 ip=127.0.0.1 operation=create znode=/a result=success apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperCLI.md0100644 0000000 0000000 00000040002 15051152474 030465 0ustar00rootroot0000000 0000000 # ZooKeeper-cli: the ZooKeeper command line interface ## Pre-requisites Enter into the ZooKeeper-cli ```bash # connect to the localhost with the default port:2181 bin/zkCli.sh # connect to the remote host with timeout:3s bin/zkCli.sh -timeout 3000 -server remoteIP:2181 # connect to the remote host with -waitforconnection option to wait for connection success before executing commands bin/zkCli.sh -waitforconnection -timeout 3000 -server remoteIP:2181 # connect with a custom client configuration properties file bin/zkCli.sh -client-configuration /path/to/client.properties ``` ## help Showing helps about ZooKeeper commands ```bash [zkshell: 1] help # a sample one [zkshell: 2] h ZooKeeper -server host:port cmd args addauth scheme auth close config [-c] [-w] [-s] connect host:port create [-s] [-e] [-c] [-t ttl] path [data] [acl] delete [-v version] path deleteall path delquota [-n|-b|-N|-B] path get [-s] [-w] path getAcl [-s] path getAllChildrenNumber path getEphemerals path history listquota path ls [-s] [-w] [-R] path printwatches on|off quit reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*] redo cmdno removewatches path [-c|-d|-a] [-l] set [-s] [-v version] path data setAcl [-s] [-v version] [-R] path acl setquota -n|-b|-N|-B val path stat [-w] path sync path version ``` ## addauth Add a authorized user for ACL ```bash [zkshell: 9] getAcl /acl_digest_test Insufficient permission : /acl_digest_test [zkshell: 10] addauth digest user1:12345 [zkshell: 11] getAcl /acl_digest_test 'digest,'user1:+owfoSBn/am19roBPzR1/MfCblE= : cdrwa # add a super user # Notice:set zookeeper.DigestAuthenticationProvider # e.g. zookeeper.DigestAuthenticationProvider.superDigest=zookeeper:qW/HnTfCSoQpB5G8LgkwT3IbiFc= [zkshell: 12] addauth digest zookeeper:admin ``` ## close Close this client/session. ```bash [zkshell: 0] close 2019-03-09 06:42:22,178 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@528] - EventThread shut down for session: 0x10007ab7c550006 2019-03-09 06:42:22,179 [myid:] - INFO [main:ZooKeeper@1346] - Session: 0x10007ab7c550006 closed ``` ## config Showing the config of quorum membership ```bash [zkshell: 17] config server.1=[2001:db8:1:0:0:242:ac11:2]:2888:3888:participant server.2=[2001:db8:1:0:0:242:ac11:2]:12888:13888:participant server.3=[2001:db8:1:0:0:242:ac11:2]:22888:23888:participant version=0 ``` ## connect Connect a ZooKeeper server. ```bash [zkshell: 4] connect 2019-03-09 06:43:33,179 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@986] - Socket connection established, initiating session, client: /127.0.0.1:35144, server: localhost/127.0.0.1:2181 2019-03-09 06:43:33,189 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1421] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x10007ab7c550007, negotiated timeout = 30000 connect "localhost:2181,localhost:2182,localhost:2183" # connect a remote server [zkshell: 5] connect remoteIP:2181 ``` ## create Create a znode. ```bash # create a persistent_node [zkshell: 7] create /persistent_node Created /persistent_node # create a ephemeral node [zkshell: 8] create -e /ephemeral_node mydata Created /ephemeral_node # create the persistent-sequential node [zkshell: 9] create -s /persistent_sequential_node mydata Created /persistent_sequential_node0000000176 # create the ephemeral-sequential_node [zkshell: 10] create -s -e /ephemeral_sequential_node mydata Created /ephemeral_sequential_node0000000174 # create a node with the schema [zkshell: 11] create /zk-node-create-schema mydata digest:user1:+owfoSBn/am19roBPzR1/MfCblE=:crwad Created /zk-node-create-schema [zkshell: 12] addauth digest user1:12345 [zkshell: 13] getAcl /zk-node-create-schema 'digest,'user1:+owfoSBn/am19roBPzR1/MfCblE= : cdrwa # create the container node.When the last child of a container is deleted,the container becomes to be deleted [zkshell: 14] create -c /container_node mydata Created /container_node [zkshell: 15] create -c /container_node/child_1 mydata Created /container_node/child_1 [zkshell: 16] create -c /container_node/child_2 mydata Created /container_node/child_2 [zkshell: 17] delete /container_node/child_1 [zkshell: 18] delete /container_node/child_2 [zkshell: 19] get /container_node org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /container_node # create the ttl node. # set zookeeper.extendedTypesEnabled=true # Otherwise:KeeperErrorCode = Unimplemented for /ttl_node [zkshell: 20] create -t 3000 /ttl_node mydata Created /ttl_node # after 3s later [zkshell: 21] get /ttl_node org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /ttl_node ``` ## delete Delete a node with a specific path ```bash [zkshell: 2] delete /config/topics/test [zkshell: 3] ls /config/topics/test Node does not exist: /config/topics/test ``` ## deleteall Delete all nodes under a specific path ```bash zkshell: 1] ls /config [changes, clients, topics] [zkshell: 2] deleteall /config [zkshell: 3] ls /config Node does not exist: /config ``` ## delquota Delete the quota under a path ```bash [zkshell: 1] delquota /quota_test [zkshell: 2] listquota /quota_test absolute path is /zookeeper/quota/quota_test/zookeeper_limits quota for /quota_test does not exist. [zkshell: 3] delquota -n /c1 [zkshell: 4] delquota -N /c2 [zkshell: 5] delquota -b /c3 [zkshell: 6] delquota -B /c4 ``` ## get Get the data of the specific path ```bash [zkshell: 10] get /latest_producer_id_block {"version":1,"broker":0,"block_start":"0","block_end":"999"} # -s to show the stat [zkshell: 11] get -s /latest_producer_id_block {"version":1,"broker":0,"block_start":"0","block_end":"999"} cZxid = 0x90000009a ctime = Sat Jul 28 08:14:09 UTC 2018 mZxid = 0x9000000a2 mtime = Sat Jul 28 08:14:12 UTC 2018 pZxid = 0x90000009a cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 60 numChildren = 0 # -w to set a watch on the data change, Notice: turn on the printwatches [zkshell: 12] get -w /latest_producer_id_block {"version":1,"broker":0,"block_start":"0","block_end":"999"} [zkshell: 13] set /latest_producer_id_block mydata WATCHER:: WatchedEvent state:SyncConnected type:NodeDataChanged path:/latest_producer_id_block ``` ## getAcl Get the ACL permission of one path ```bash [zkshell: 4] create /acl_test mydata ip:127.0.0.1:crwda Created /acl_test [zkshell: 5] getAcl /acl_test 'ip,'127.0.0.1 : cdrwa [zkshell: 6] getAcl /testwatch 'world,'anyone : cdrwa ``` ## getAllChildrenNumber Get all numbers of children nodes under a specific path ```bash [zkshell: 1] getAllChildrenNumber / 73779 [zkshell: 2] getAllChildrenNumber /ZooKeeper 2 [zkshell: 3] getAllChildrenNumber /ZooKeeper/quota 0 ``` ## getEphemerals Get all the ephemeral nodes created by this session ```bash [zkshell: 1] create -e /test-get-ephemerals "ephemeral node" Created /test-get-ephemerals [zkshell: 2] getEphemerals [/test-get-ephemerals] [zkshell: 3] getEphemerals / [/test-get-ephemerals] [zkshell: 4] create -e /test-get-ephemerals-1 "ephemeral node" Created /test-get-ephemerals-1 [zkshell: 5] getEphemerals /test-get-ephemerals test-get-ephemerals test-get-ephemerals-1 [zkshell: 6] getEphemerals /test-get-ephemerals [/test-get-ephemerals-1, /test-get-ephemerals] [zkshell: 7] getEphemerals /test-get-ephemerals-1 [/test-get-ephemerals-1] ``` ## history Showing the history about the recent 11 commands that you have executed ```bash [zkshell: 7] history 0 - close 1 - close 2 - ls / 3 - ls / 4 - connect 5 - ls / 6 - ll 7 - history ``` ## listquota Listing the quota of one path ```bash [zkshell: 1] listquota /c1 absolute path is /zookeeper/quota/c1/zookeeper_limits Output quota for /c1 count=-1,bytes=-1=;byteHardLimit=-1;countHardLimit=2 Output stat for /c1 count=4,bytes=0 ``` ## ls Listing the child nodes of one path ```bash [zkshell: 36] ls /quota_test [child_1, child_2, child_3] # -s to show the stat [zkshell: 37] ls -s /quota_test [child_1, child_2, child_3] cZxid = 0x110000002d ctime = Thu Mar 07 11:19:07 UTC 2019 mZxid = 0x110000002d mtime = Thu Mar 07 11:19:07 UTC 2019 pZxid = 0x1100000033 cversion = 3 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 3 # -R to show the child nodes recursely [zkshell: 38] ls -R /quota_test /quota_test /quota_test/child_1 /quota_test/child_2 /quota_test/child_3 # -w to set a watch on the child change,Notice: turn on the printwatches [zkshell: 39] ls -w /brokers [ids, seqid, topics] [zkshell: 40] delete /brokers/ids WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/brokers ``` ## printwatches A switch to turn on/off whether printing watches or not. ```bash [zkshell: 0] printwatches printwatches is on [zkshell: 1] printwatches off [zkshell: 2] printwatches printwatches is off [zkshell: 3] printwatches on [zkshell: 4] printwatches printwatches is on ``` ## quit Quit the CLI windows. ```bash [zkshell: 1] quit ``` ## reconfig Change the membership of the ensemble during the runtime. Before using this cli,read the details in the [Dynamic Reconfiguration](zookeeperReconfig.html) about the reconfig feature,especially the "Security" part. Pre-requisites: 1. set reconfigEnabled=true in the zoo.cfg 2. add a super user or skipAcl,otherwise will get “Insufficient permissionâ€. e.g. addauth digest zookeeper:admin ```bash # Change follower 2 to an observer and change its port from 2182 to 12182 # Add observer 5 to the ensemble # Remove Observer 4 from the ensemble [zkshell: 1] reconfig --add 2=localhost:2781:2786:observer;12182 --add 5=localhost:2781:2786:observer;2185 -remove 4 Committed new configuration: server.1=localhost:2780:2785:participant;0.0.0.0:2181 server.2=localhost:2781:2786:observer;0.0.0.0:12182 server.3=localhost:2782:2787:participant;0.0.0.0:2183 server.5=localhost:2784:2789:observer;0.0.0.0:2185 version=1c00000002 # -members to appoint the membership [zkshell: 2] reconfig -members server.1=localhost:2780:2785:participant;0.0.0.0:2181,server.2=localhost:2781:2786:observer;0.0.0.0:12182,server.3=localhost:2782:2787:participant;0.0.0.0:12183 Committed new configuration: server.1=localhost:2780:2785:participant;0.0.0.0:2181 server.2=localhost:2781:2786:observer;0.0.0.0:12182 server.3=localhost:2782:2787:participant;0.0.0.0:12183 version=f9fe0000000c # Change the current config to the one in the myNewConfig.txt # But only if current config version is 2100000010 [zkshell: 3] reconfig -file /data/software/zookeeper/zookeeper-test/conf/myNewConfig.txt -v 2100000010 Committed new configuration: server.1=localhost:2780:2785:participant;0.0.0.0:2181 server.2=localhost:2781:2786:observer;0.0.0.0:12182 server.3=localhost:2782:2787:participant;0.0.0.0:2183 server.5=localhost:2784:2789:observer;0.0.0.0:2185 version=220000000c ``` ## redo Redo the cmd with the index from history. ```bash [zkshell: 4] history 0 - ls / 1 - get /consumers 2 - get /hbase 3 - ls /hbase 4 - history [zkshell: 5] redo 3 [backup-masters, draining, flush-table-proc, hbaseid, master-maintenance, meta-region-server, namespace, online-snapshot, replication, rs, running, splitWAL, switch, table, table-lock] ``` ## removewatches Remove the watches under a node. ```bash [zkshell: 1] get -w /brokers null [zkshell: 2] removewatches /brokers WATCHER:: WatchedEvent state:SyncConnected type:DataWatchRemoved path:/brokers ``` ## set Set/update the data on a path. ```bash [zkshell: 50] set /brokers myNewData # -s to show the stat of this node. [zkshell: 51] set -s /quota_test mydata_for_quota_test cZxid = 0x110000002d ctime = Thu Mar 07 11:19:07 UTC 2019 mZxid = 0x1100000038 mtime = Thu Mar 07 11:42:41 UTC 2019 pZxid = 0x1100000033 cversion = 3 dataVersion = 2 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 21 numChildren = 3 # -v to set the data with CAS,the version can be found from dataVersion using stat. [zkshell: 52] set -v 0 /brokers myNewData [zkshell: 53] set -v 0 /brokers myNewData version No is not valid : /brokers ``` ## setAcl Set the Acl permission for one node. ```bash [zkshell: 28] addauth digest user1:12345 [zkshell: 30] setAcl /acl_auth_test auth:user1:12345:crwad [zkshell: 31] getAcl /acl_auth_test 'digest,'user1:+owfoSBn/am19roBPzR1/MfCblE= : cdrwa # -R to set Acl recursely [zkshell: 32] ls /acl_auth_test [child_1, child_2] [zkshell: 33] getAcl /acl_auth_test/child_2 'world,'anyone : cdrwa [zkshell: 34] setAcl -R /acl_auth_test auth:user1:12345:crwad [zkshell: 35] getAcl /acl_auth_test/child_2 'digest,'user1:+owfoSBn/am19roBPzR1/MfCblE= : cdrwa # -v set Acl with the acl version which can be found from the aclVersion using the stat [zkshell: 36] stat /acl_auth_test cZxid = 0xf9fc0000001c ctime = Tue Mar 26 16:50:58 CST 2019 mZxid = 0xf9fc0000001c mtime = Tue Mar 26 16:50:58 CST 2019 pZxid = 0xf9fc0000001f cversion = 2 dataVersion = 0 aclVersion = 3 ephemeralOwner = 0x0 dataLength = 0 numChildren = 2 [zkshell: 37] setAcl -v 3 /acl_auth_test auth:user1:12345:crwad ``` ## setquota Set the quota in one path. ```bash # -n to limit the number of child nodes(included itself) [zkshell: 18] setquota -n 2 /quota_test [zkshell: 19] create /quota_test/child_1 Created /quota_test/child_1 [zkshell: 20] create /quota_test/child_2 Created /quota_test/child_2 [zkshell: 21] create /quota_test/child_3 Created /quota_test/child_3 # Notice:don't have a hard constraint,just log the warning info 2019-03-07 11:22:36,680 [myid:1] - WARN [SyncThread:0:DataTree@374] - Quota exceeded: /quota_test count=3 limit=2 2019-03-07 11:22:41,861 [myid:1] - WARN [SyncThread:0:DataTree@374] - Quota exceeded: /quota_test count=4 limit=2 # -b to limit the bytes(data length) of one path [zkshell: 22] setquota -b 5 /brokers [zkshell: 23] set /brokers "I_love_zookeeper" # Notice:don't have a hard constraint,just log the warning info WARN [CommitProcWorkThread-7:DataTree@379] - Quota exceeded: /brokers bytes=4206 limit=5 # -N count Hard quota [zkshell: 3] create /c1 Created /c1 [zkshell: 4] setquota -N 2 /c1 [zkshell: 5] listquota /c1 absolute path is /zookeeper/quota/c1/zookeeper_limits Output quota for /c1 count=-1,bytes=-1=;byteHardLimit=-1;countHardLimit=2 Output stat for /c1 count=2,bytes=0 [zkshell: 6] create /c1/ch-3 Count Quota has exceeded : /c1/ch-3 # -B byte Hard quota [zkshell: 3] create /c2 [zkshell: 4] setquota -B 4 /c2 [zkshell: 5] set /c2 "foo" [zkshell: 6] set /c2 "foo-bar" Bytes Quota has exceeded : /c2 [zkshell: 7] get /c2 foo ``` ## stat Showing the stat/metadata of one node. ```bash [zkshell: 1] stat /hbase cZxid = 0x4000013d9 ctime = Wed Jun 27 20:13:07 CST 2018 mZxid = 0x4000013d9 mtime = Wed Jun 27 20:13:07 CST 2018 pZxid = 0x500000001 cversion = 17 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 15 ``` ## sync Sync the data of one node between leader and followers(Asynchronous sync) ```bash [zkshell: 14] sync / [zkshell: 15] Sync is OK ``` ## version Show the version of the ZooKeeper client/CLI ```bash [zkshell: 1] version ZooKeeper CLI version: 3.6.0-SNAPSHOT-29f9b2c1c0e832081f94d59a6b88709c5f1bb3ca, built on 05/30/2019 09:26 GMT ``` ## whoami Gives all authentication information added into the current session. [zkshell: 1] whoami Auth scheme: User ip: 127.0.0.1 [zkshell: 2] addauth digest user1:12345 [zkshell: 3] whoami Auth scheme: User ip: 127.0.0.1 digest: user1apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperHierarchicalQuorums.md0100644 0000000 0000000 00000003032 15051152474 034032 0ustar00rootroot0000000 0000000 # 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. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperInternals.md0100644 0000000 0000000 00000051055 15051152474 032027 0ustar00rootroot0000000 0000000 # ZooKeeper Internals * [Introduction](#ch_Introduction) * [Atomic Broadcast](#sc_atomicBroadcast) * [Guarantees, Properties, and Definitions](#sc_guaranteesPropertiesDefinitions) * [Leader Activation](#sc_leaderElection) * [Active Messaging](#sc_activeMessaging) * [Summary](#sc_summary) * [Comparisons](#sc_comparisons) * [Consistency Guarantees](#sc_consistency) * [Quorums](#sc_quorum) * [Logging](#sc_logging) * [Developer Guidelines](#sc_developerGuidelines) * [Logging at the Right Level](#sc_rightLevel) * [Use of Standard slf4j Idioms](#sc_slf4jIdioms) ## Introduction This document contains information on the inner workings of ZooKeeper. It discusses the following topics: * [Atomic Broadcast](#sc_atomicBroadcast) * [Consistency Guarantees](#sc_consistency) * [Quorums](#sc_quorum) * [Logging](#sc_logging) ## 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, message `m` will be eventually delivered by all servers. * *_Total order_* : If a message `a` is delivered before message `b` by one server, message `a` will be delivered before `b` by all servers. * *_Causal order_* : If a message `b` is sent after a message `a` has been delivered by the sender of `b`, message `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 that we achieve consensus in the presence of failures we use timeouts. However, we rely on time for liveness not for correctness. So, if timeouts stop working (e.g., skewed clocks) 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 contain 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 zxid consists of two parts, zxid can be represented 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 include all the proposals that really have been committed?_ The answer is _yes_. Below, we make clear why.) ### Leader Activation Leader activation includes leader election (`FastLeaderElection`). ZooKeeper messaging doesn't care about the exact method of electing a leader as 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 among 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 the 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 `U` 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), after 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 has 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 COMMITTED. 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. ![Two phase commit](images/2pc.jpg) 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 proposal message when that proposal is committed. ### Summary So there you go. Why does it work? Specifically, why does a 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 uncommitted 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. ## Consistency Guarantees The [consistency](https://jepsen.io/consistency) guarantees of ZooKeeper lie between sequential consistency and linearizability. In this section, we explain the exact consistency guarantees that ZooKeeper provides. Write operations in ZooKeeper are *linearizable*. In other words, each `write` will appear to take effect atomically at some point between when the client issues the request and receives the corresponding response. This means that the writes performed by all the clients in ZooKeeper can be totally ordered in such a way that respects the real-time ordering of these writes. However, merely stating that write operations are linearizable is meaningless unless we also talk about read operations. Read operations in ZooKeeper are *not linearizable* since they can return potentially stale data. This is because a `read` in ZooKeeper is not a quorum operation and a server will respond immediately to a client that is performing a `read`. ZooKeeper does this because it prioritizes performance over consistency for the read use case. However, reads in ZooKeeper are *sequentially consistent*, because `read` operations will appear to take effect in some sequential order that furthermore respects the order of each client's operations. A common pattern to work around this is to issue a `sync` before issuing a `read`. This too does **not** strictly guarantee up-to-date data because `sync` is [not currently a quorum operation](https://issues.apache.org/jira/browse/ZOOKEEPER-1675). To illustrate, consider a scenario where two servers simultaneously think they are the leader, something that could occur if the TCP connection timeout is smaller than `syncLimit * tickTime`. Note that this is [unlikely](https://www.amazon.com/ZooKeeper-Distributed-Coordination-Flavio-Junqueira/dp/1449361307) to occur in practice, but should be kept in mind nevertheless when discussing strict theoretical guarantees. Under this scenario, it is possible that the `sync` is served by the “leader†with stale data, thereby allowing the following `read` to be stale as well. The stronger guarantee of linearizability is provided if an actual quorum operation (e.g., a `write`) is performed before a `read`. Overall, the consistency guarantees of ZooKeeper are formally captured by the notion of [ordered sequential consistency](http://webee.technion.ac.il/people/idish/ftp/OSC-IPL17.pdf) or `OSC(U)` to be exact, which lies between sequential consistency and linearizability. ## 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 [slf4j](http://www.slf4j.org/index.html) as an abstraction layer for logging. [Logback](https://logback.qos.ch/) is chosen the logging backend since ZooKeeper version 3.8.0. For better embedding support, it is planned in the future to leave the decision of choosing the final logging implementation to the end user. Therefore, always use the slf4j api to write log statements in the code, but configure logback for how to log at runtime. Note that slf4j has no FATAL level, former messages at FATAL level have been moved to ERROR level. For information on configuring logback for ZooKeeper, see the [Logging](zookeeperAdmin.html#sc_logging) section of the [ZooKeeper Administrator's Guide.](zookeeperAdmin.html) ### Developer Guidelines Please follow the [slf4j manual](http://www.slf4j.org/manual.html) when creating log statements within code. Also read the [FAQ on performance](http://www.slf4j.org/faq.html#logging\_performance), when creating log statements. Patch reviewers will look for the following: #### Logging at the Right Level There are several levels of logging in slf4j. It's important to pick the right one. In order of higher to lower severity: 1. ERROR level designates error events that might still allow the application to continue running. 1. WARN level designates potentially harmful situations. 1. INFO level designates informational messages that highlight the progress of the application at coarse-grained level. 1. DEBUG Level designates fine-grained informational events that are most useful to debug an application. 1. TRACE Level designates finer-grained informational events than the DEBUG. 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 slf4j Idioms _Static Message Logging_ LOG.debug("process completed successfully!"); However when creating parameterized messages are required, use formatting anchors. LOG.debug("got {} messages in {} minutes",new Object[]{count,time}); _Naming_ Loggers should be named after the class in which they are used. public class Foo { private static final Logger LOG = LoggerFactory.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 } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperJMX.md0100644 0000000 0000000 00000015124 15051152474 030523 0ustar00rootroot0000000 0000000 # ZooKeeper JMX * [JMX](#ch_jmx) * [Starting ZooKeeper with JMX enabled](#ch_starting) * [Run a JMX console](#ch_console) * [ZooKeeper MBean Reference](#ch_reference) ## 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](http://java.sun.com/javase/technologies/core/mntr-mgmt/javamanagement/) page to get started with JMX. See the [JMX Management Guide](http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html) 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 initialization 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](http://java.sun.com/developer/technicalArticles/J2SE/jconsole.html) 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 the 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. #### Logback MBeans registration _(new in 3.8.0)_ Logback is the default logging backend of ZooKeeper since version 3.8.0. It can be configured to register JMX MBeans by adding `` to _logback.xml_. More information can be found on Logback's [website](https://logback.qos.ch/manual/jmxConfig.html). #### Log4j MBeans _(deprecated)_ Also note that ZooKeeper will register log4j MBeans as well if log4j1 is configured as the logging backend of SLF4j. 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. In addition, we can specify the name of the MBean with the _-Dzookeeper.jmx.log4j.mbean=log4j:hierarchy=default_ option, in case we need to upgrade an integrated system using the old MBean name (`log4j:hierarchy = default`). ## 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 | MBean | MBean Object Name | Description | |-----------|-------------------|-------------------------------------------------| | Quorum | ReplicatedServer_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/RemotePeer | replica.<#> | 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). | | LeaderElection | LeaderElection | Represents a ZooKeeper cluster leader election which is in progress. Provides information about the election, such as when it started. | | Leader | Leader | Indicates 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. | | Follower | Follower | Indicates 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. | | DataTree | InMemoryDataTree | Statistics 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 | | 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 | MBean | MBean Object Name | Description | |-------|-------------------|------------------------| | ZooKeeperServer | StandaloneServer_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). | | DataTree | InMemoryDataTree | Statistics 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. | apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperMonitor.md0100644 0000000 0000000 00000022044 15051152474 031513 0ustar00rootroot0000000 0000000 # ZooKeeper Monitor Guide * [New Metrics System](#Metrics-System) * [Metrics](#Metrics) * [Prometheus](#Prometheus) * [Alerting with Prometheus](#Alerting) * [Grafana](#Grafana) * [InfluxDB](#influxdb) * [JMX](#JMX) * [Four letter words](#four-letter-words) ## New Metrics System The feature:`New Metrics System` has been available since 3.6.0 which provides the abundant metrics to help users monitor the ZooKeeper on the topic: znode, network, disk, quorum, leader election, client, security, failures, watch/session, requestProcessor, and so forth. ### Metrics All the metrics are included in the `ServerMetrics.java`. ### Prometheus - Running a [Prometheus](https://prometheus.io/) monitoring service is the easiest way to ingest and record ZooKeeper's metrics. - Pre-requisites: - enable the `Prometheus MetricsProvider` by setting `metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider` in the zoo.cfg. - the Port is also configurable by setting `metricsProvider.httpPort`(the default value:7000) - Install Prometheus: Go to the official website download [page](https://prometheus.io/download/), download the latest release. - Set Prometheus's scraper to target the ZooKeeper cluster endpoints: ```bash cat > /tmp/test-zk.yaml <> /tmp/test-zk.log 2>&1 & ``` - Now Prometheus will scrape zk metrics every 10 seconds. ### Alerting with Prometheus - We recommend that you read [Prometheus Official Alerting Page](https://prometheus.io/docs/practices/alerting/) to explore some principles of alerting - We recommend that you use [Prometheus Alertmanager](https://www.prometheus.io/docs/alerting/latest/alertmanager/) which can help users to receive alerting email or instant message(by webhook) in a more convenient way - We provide an alerting example where these metrics should be taken a special attention. Note: this is for your reference only, and you need to adjust them according to your actual situation and resource environment use ./promtool check rules rules/zk.yml to check the correctness of the config file cat rules/zk.yml groups: - name: zk-alert-example rules: - alert: ZooKeeper server is down expr: up == 0 for: 1m labels: severity: critical annotations: summary: "Instance {{ $labels.instance }} ZooKeeper server is down" description: "{{ $labels.instance }} of job {{$labels.job}} ZooKeeper server is down: [{{ $value }}]." - alert: create too many znodes expr: znode_count > 1000000 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} create too many znodes" description: "{{ $labels.instance }} of job {{$labels.job}} create too many znodes: [{{ $value }}]." - alert: create too many connections expr: num_alive_connections > 50 # suppose we use the default maxClientCnxns: 60 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} create too many connections" description: "{{ $labels.instance }} of job {{$labels.job}} create too many connections: [{{ $value }}]." - alert: znode total occupied memory is too big expr: approximate_data_size /1024 /1024 > 1 * 1024 # more than 1024 MB(1 GB) for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} znode total occupied memory is too big" description: "{{ $labels.instance }} of job {{$labels.job}} znode total occupied memory is too big: [{{ $value }}] MB." - alert: set too many watch expr: watch_count > 10000 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} set too many watch" description: "{{ $labels.instance }} of job {{$labels.job}} set too many watch: [{{ $value }}]." - alert: a leader election happens expr: increase(election_time_count[5m]) > 0 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} a leader election happens" description: "{{ $labels.instance }} of job {{$labels.job}} a leader election happens: [{{ $value }}]." - alert: open too many files expr: open_file_descriptor_count > 300 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} open too many files" description: "{{ $labels.instance }} of job {{$labels.job}} open too many files: [{{ $value }}]." - alert: fsync time is too long expr: rate(fsynctime_sum[1m]) > 100 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} fsync time is too long" description: "{{ $labels.instance }} of job {{$labels.job}} fsync time is too long: [{{ $value }}]." - alert: take snapshot time is too long expr: rate(snapshottime_sum[5m]) > 100 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} take snapshot time is too long" description: "{{ $labels.instance }} of job {{$labels.job}} take snapshot time is too long: [{{ $value }}]." - alert: avg latency is too high expr: avg_latency > 100 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} avg latency is too high" description: "{{ $labels.instance }} of job {{$labels.job}} avg latency is too high: [{{ $value }}]." - alert: JvmMemoryFillingUp expr: jvm_memory_bytes_used / jvm_memory_bytes_max{area="heap"} > 0.8 for: 5m labels: severity: warning annotations: summary: "JVM memory filling up (instance {{ $labels.instance }})" description: "JVM memory is filling up (> 80%)\n labels: {{ $labels }} value = {{ $value }}\n" ### Grafana - Grafana has built-in Prometheus support; just add a Prometheus data source: ```bash Name: test-zk Type: Prometheus Url: http://localhost:9090 Access: proxy ``` - Then download and import the default ZooKeeper dashboard [template](https://grafana.com/grafana/dashboards/10465) and customize. - Users can ask for Grafana dashboard account if having any good improvements by writing a email to **dev@zookeeper.apache.org**. ### InfluxDB InfluxDB is an open source time series data that is often used to store metrics from Zookeeper. You can [download](https://portal.influxdata.com/downloads/) the open source version or create a [free](https://cloud2.influxdata.com/signup) account on InfluxDB Cloud. In either case, configure the [Apache Zookeeper Telegraf plugin](https://www.influxdata.com/integration/apache-zookeeper/) to start collecting and storing metrics from your Zookeeper clusters into your InfluxDB instance. There is also an [Apache Zookeeper InfluxDB template](https://www.influxdata.com/influxdb-templates/zookeeper-monitor/) that includes the Telegraf configurations and a dashboard to get you set up right away. ## JMX More details can be found in [here](http://zookeeper.apache.org/doc/current/zookeeperJMX.html) ## Four letter words More details can be found in [here](http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_zkCommands) apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperObservers.md0100644 0000000 0000000 00000015014 15051152474 032035 0ustar00rootroot0000000 0000000 # ZooKeeper Observers * [Observers: Scaling ZooKeeper Without Hurting Write Performance](#ch_Introduction) * [How to use Observers](#sc_UsingObservers) * [Example use cases](#ch_UseCases) ## 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. ## How to use Observer Masters Observers function simple as non-voting members of the ensemble, sharing the Learner interface with Followers and holding only a slightly different internal pipeline. Both maintain connections along the quorum port with the Leader by which they learn of all new proposals on the ensemble. By default, Observers connect to the Leader of the quorum along its quorum port and this is how they learn of all new proposals on the ensemble. There are benefits to allowing Observers to connect to the Followers instead as a means of plugging into the commit stream in place of connecting to the Leader. It shifts the burden of supporting Observers off the Leader and allow it to focus on coordinating the commit of writes. This means better performance when the Leader is under high load, particularly high network load such as can happen after a leader election when many Learners need to sync. It reduces the total network connections maintained on the Leader when there are a high number of observers. Activating Followers to support Observers allow the overall number of Observers to scale into the hundreds. On the other end, Observer availability is improved since it will take shorter time for a high number of Observers to finish syncing and start serving client traffic. This feature can be activated by letting all members of the ensemble know which port will be used by the Followers to listen for Observer connections. The following entry, when added to the server config file, will instruct Observers to connect to peers (Leaders and Followers) on port 2191 and instruct Followers to create an ObserverMaster thread to listen and serve on that port. observerMasterPort=2191 ## Example use cases Two example use cases for Observers are listed below. In fact, wherever you wish to scale the number 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. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperOracleQuorums.md0100644 0000000 0000000 00000026445 15051152474 032676 0ustar00rootroot0000000 0000000 # Introduction to Oracle Quorum The introduction to Oracle Quorum increases the availability of a cluster of 2 ZooKeeper instances with a failure detector as known as the Oracle. The Oracle is designed to grant the permission to the instance which is the only remaining instance in a 2-instance configuration when the other instance is identified as faulty by the fail detector, the Oracle. ## The implementation of the Oracle Every instance shall access to a file which contains either 0 or 1 to indicate whether this instance is authorized by the Oracle. However, this design can be changed since the fail detector algorithms vary from each other. Therefore, ones can override the method of _askOracle()_ in _QuorumOracleMaj_ to adapt the preferred way of deciphering the message from the Oracle. ## The deployment contexts The Oracle is designed to increase the availability of a cluster of 2 ZooKeeper instances; thus, the size of the voting member is **2**. In other words, the Oracle solves the consensus problem of a possibility of faulty instance in a two-instance ensemble. In the case that the size of the voting members exceeds 2, the expected way to make the Oracle work correctly is to reconfigure the size of the cluster when a faulty machine is identified. For example, with a configuration of 5 instances, when a faulty machine breaks the connection with the Leader, it is expected to have a _reconfig_ client request to the cluster, which makes the cluster to re-form as the configuration of 4 instances. Therefore, once the size of the voting member equals to 2, the configuration falls into the problem domain which the Oracle is designed to address. ## How to deploy the Oracle in _zoo.cfg_ Regardless of the size of the cluster, the _oraclePath_ must be configured at the time of the initialization, which is like other static parameters. The below shows the correct way to specify and enable the Oracle. oraclePath=/to/some/file #### An example of zoo.cfg: dataDir=/data dataLogDir=/datalog tickTime=2000 initLimit=5 syncLimit=2 autopurge.snapRetainCount=3 autopurge.purgeInterval=0 maxClientCnxns=60 standaloneEnabled=true admin.enableServer=true oraclePath=/chassis/mastership server.1=0.0.0.0:2888:3888;2181 server.2=hw1:2888:3888;2181 The QuorumOracleMaj is designed to read the result of a failure detector, which is written on a text file, the oracle file. The configuration in the zoo.cfg like the following: oraclePath=/to/some/file Suppose you have the result of the failure detector written on /some/path/result.txt, and then the correct configuration is the following: oraclePath=/some/path/result.txt So, what is the correct content of the provided file? An example file can be created with the following command from the terminal: $echo 1 > /some/path/result.txt Any equivalent files are suitable for the current implementation of QuorumOracleMaj. The number of oracle files should be equal to the number of ZooKeeper instances configured to enable the Oracle. In other words, each ZooKeeper instance should have its oracle file, and the files shall not be shared; otherwise, the issues in the next section will arise. ## What differs after the deployment of the Oracle enabled The _QuorumPeerConfig_ will create an instance of _QuorumOracleMaj_ instead of the default QuorumVerifier, _QuorumMaj_ when it reads the _zoo.cfg_ contains _oraclePath_. QuorumOracleMaj inheritances from QuorumMaj, and differs from its superclass by overriding the method of _containsQuorum()_. QuorumOracleMaj is designed to execute its version of _containsQuorum_ when the Leader loses all of its followers, and fails to maintain the quorum. In other cases, _QuorumOracleMaj_ shall execute as _QuorumMaj_. ## What we should pay attention to the Oracle We consider an asynchronous distributed system which consists of **2** ZooKeeper instances and an Oracle. ### Liveness Issue: When we consider the oracle satisfies the following property introduced by [CT]: Strong Completeness: There is a time after which every process that crashes is permanently suspected by every correct processes The liveness of the system is ensured by the Oracle. However, when the introduced oracle fails to maintain this property, the lost of the liveness is expected as the following example, Suppose we have a Leader and a Follower, which are running in the broadcasting state, The system will lose its liveness when: 1. The Leader fails, but the Oracle does not detect the faulty Leader, which means the Oracle will not authorize the Follower to become a new Leader. 2. When a Follower fails, but the Oracle does not detect the faulty follower, which means the Oracle will authorize the Leader to move system forward. ### Safety Issue: #### Lost of Progress The progress can lost when multiple failures occurs in the system at different time as the following example, Suppose we have a Leader(Ben) and a Follower(John) in the broadcasting state, At T1 with zxid(0x1_1): L-Ben fails, and the F-John takes over the system under the authorization from the Oracle. At T2 with zxid(0x2_1): The F-John becomes a new Leader, L-John, and starts a new epoch. At T3 with zxid(0x2_A): L-John fails At T4 with zxid(0x2_A): Ben recovers up and starts its leader election. At T5 with zxid(0x3_1): Ben becomes the new leader, L-Ben, under the authorization from the Oracle. In this case, the system loses its progress after the L-Ben failed. However, the lost of progress can be prevented by making the Oracle is capable of referring the latest zxid. When the Oracle could refer to the latest zxid, At T5 with zxid(0x2_A): Ben will not end his leader election because the Oracle would not authorize although John is down. Nevertheless, we exchange the liveness for the safety. #### Split Brain Issue We consider the Oracle satisfies the following desired property introduced by [CT], Accuracy: There is a time after which some correct processes is never suspected by any processes Nevertheless, the decisions which the Oracle gives out should be mutual exclusive. In other words, Suppose we have a Leader(Ben) and a Follower(John) in the broadcasting state, - At any time, the Oracle will not authorize both Ben and John even though the failure detectors think each other is faulty. Or - At any time, for any two values in any two Oracle files respectively, the values are not both equal to 1. The split brain is expected when the Oracle fails to maintain this property during the leader election phase of 1. Start of the system 2. A failed instance recovers from failures. ## Examples of Concepts for Implementation of a Failure Detector One should consider that the failure detector's outcome is to authorize the querying ZooKeeper instance whether it has the right to move the system forward without waiting for the faulty instance, which is identified by the failure detector. ### An Implementation of Hardware Suppose two dedicated pieces of hardware, hw1 and hw2, can host ZooKeeper instances, zk1 and zk2, respectively, and form a cluster. A hardware device is attached to both of the hardware, and it is capable of determining whether the hardware is power on or not. So, when hw1 is not power on, the zk1 is undoubtedly faulty. Therefore, the hardware device updates the oracle file on hw2 to 1, which indicates that zk1 is faulty and authorizes zk2 to move the system forwards. ### An Implementation of Software Suppose two dedicated pieces of hardware, hw1 and hw2, can host ZooKeeper instances, zk1 and zk2, respectively, and form a cluster. One can have two more services, o1 and o2, on hw1 and hw2, respectively. The job of o1 and o2 are detecting the other hardware is alive or not. For example, o1 can constantly ping hw2 to determine if hw2 is power on or not. When o1 cannot ping hw2, o1 identifies that hw2 is faulty and then update the oracle file of zk1 to 1, which indicates that zk2 is faulty and authorizes zk1 to move the system forwards. ### Use USB devices as Oracle to Maintain Progress In macOS,10.15.7 (19H2), the external storage devices are mounted under `/Volumes`. Thus, we can insert a USB device which contains the required information as the oracle. When the device is connected, the oracle authorizes the leader to move system forward, which also means the other instance fails. There are **SIX** steps to reproduce this stimulation. * Firstly, insert a USB device named `Oracle`, and then we can expect that `/Volumes/Oracle` is accessible. * Secondly, we create a file contains `1` under `/Volumes/Oracle` named `mastership`. Now we can access `/Volumes/Oracle/mastership`, and so does the zookeeper instances to see whether it has the right to move the system forward. The file can easily be generated by the following command: $echo 1 > mastership * Thirdly, you shall have a `zoo.cfg` like the example below: dataDir=/data dataLogDir=/datalog tickTime=2000 initLimit=5 syncLimit=2 autopurge.snapRetainCount=3 autopurge.purgeInterval=0 maxClientCnxns=60 standaloneEnabled=true admin.enableServer=true oraclePath=/Volumes/Oracle/mastership server.1=0.0.0.0:2888:3888;2181 server.2=hw1:2888:3888;2181 _(NOTE) The split brain issues will not occur because there is only a SINGLE USB device in this stimulation._ _Additionally, `mastership` should not be shared by multiple instances._ _Thus, only one ZooKeeper instance is configured with Oracle._ _For more, please refer to Section Safety Issue._ * Fourthly, start the cluster, and it is expected it forms a quorum normally. * Fifthly, terminate the instance either without attaching to a USB device or `mastership` contains 0. There are two scenarios to expect: 1. A leader failure occurs, and the remained instance finishes the leader election on its own due to the oracle. 2. The quorum is still maintained due to the oracle. * Lastly, when the USB device is removed, `/Volumes/Oracle/mastership` becomes unavailable. Therefore, according to the current implementation, whenever the Leader queries the oracle, the oracle throws an exception and return `FALSE`. Repeat the fifth step, and then it is expected that either the system cannot recover from a leader failure ,or the leader loses the quorum. In either case, the service is interrupted. With these steps, we can show and practice how the oracle works with two-instance systems with ease. ##REFERENCE [CT] Tushar Deepak Chandra and Sam Toueg. 1991. Unreliable failure detectors for asynchronous systems (preliminary version). In Proceedings of the tenth annual ACM symposium on Principles of distributed computing (PODC '91). Association for Computing Machinery, New York, NY, USA, 325–340. DOI:https://doi.org/10.1145/112600.112627apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperOver.md0100644 0000000 0000000 00000031712 15051152474 031001 0ustar00rootroot0000000 0000000 # ZooKeeper * [ZooKeeper: A Distributed Coordination Service for Distributed Applications](#ch_DesignOverview) * [Design Goals](#sc_designGoals) * [Data model and the hierarchical namespace](#sc_dataModelNameSpace) * [Nodes and ephemeral nodes](#Nodes+and+ephemeral+nodes) * [Conditional updates and watches](#Conditional+updates+and+watches) * [Guarantees](#Guarantees) * [Simple API](#Simple+API) * [Implementation](#Implementation) * [Uses](#Uses) * [Performance](#Performance) * [Reliability](#Reliability) * [The ZooKeeper Project](#The+ZooKeeper+Project) ## 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 hierarchical namespace which is organized similarly to a standard file system. The namespace 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 achieve 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 set of hosts called an ensemble. ![ZooKeeper Service](images/zkservice.jpg) 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 namespace 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 namespace is identified by a path. #### ZooKeeper's Hierarchical Namespace ![ZooKeeper's Hierarchical Namespace](images/zknamespace.jpg) ### Nodes and ephemeral nodes Unlike 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. ### Conditional updates and watches ZooKeeper supports the concept of _watches_. Clients can set a watch on a znode. 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. If the connection between the client and one of the ZooKeeper servers is broken, the client will receive a local notification. **New in 3.6.0:** Clients can also set permanent, recursive watches on a znode that are not removed when triggered and that trigger for changes on the registered znode as well as any children znodes recursively. ### 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. i.e., a client will never see an older view of the system even if the client fails over to a different server with the same session. * 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. ### Simple API One of the design goals of ZooKeeper is providing 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 ### Implementation [ZooKeeper Components](#zkComponents) 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 the components. ![ZooKeeper Components](images/zkcomponents.jpg) 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 requests. 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. ### Performance ZooKeeper is designed to be highly performance. 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](#zkPerfRW).) 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](images/zkperfRW-3.2.jpg) The [ZooKeeper Throughput as the Read-Write Ratio Varies](#zkPerfRW) 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](http://zookeeper.apache.org/docs/r3.1.1/zookeeperOver.html#Performance). Benchmarks also indicate that it is reliable, too. [Reliability in the Presence of Errors](#zkPerfReliability) shows how a deployment responds to various failures. The events marked in the figure are the following: 1. Failure and recovery of a follower 1. Failure and recovery of a different follower 1. Failure of the leader 1. Failure and recovery of two followers 1. Failure of another leader ### 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](images/zkperfreliability.jpg) There 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](https://cwiki.apache.org/confluence/display/ZOOKEEPER/PoweredBy) 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](http://zookeeper.apache.org/) for more information. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperProgrammers.md0100644 0000000 0000000 00000224365 15051152474 032374 0ustar00rootroot0000000 0000000 # ZooKeeper Programmer's Guide ### Developing Distributed Applications that use ZooKeeper * [Introduction](#_introduction) * [The ZooKeeper Data Model](#ch_zkDataModel) * [ZNodes](#sc_zkDataModel_znodes) * [Watches](#sc_zkDataMode_watches) * [Data Access](#Data+Access) * [Ephemeral Nodes](#Ephemeral+Nodes) * [Sequence Nodes -- Unique Naming](#Sequence+Nodes+--+Unique+Naming) * [Container Nodes](#Container+Nodes) * [TTL Nodes](#TTL+Nodes) * [Time in ZooKeeper](#sc_timeInZk) * [ZooKeeper Stat Structure](#sc_zkStatStructure) * [ZooKeeper Sessions](#ch_zkSessions) * [ZooKeeper Watches](#ch_zkWatches) * [Semantics of Watches](#sc_WatchSemantics) * [Persistent, Recursive Watches](#sc_WatchPersistentRecursive) * [Remove Watches](#sc_WatchRemoval) * [What ZooKeeper Guarantees about Watches](#sc_WatchGuarantees) * [Things to Remember about Watches](#sc_WatchRememberThese) * [ZooKeeper access control using ACLs](#sc_ZooKeeperAccessControl) * [ACL Permissions](#sc_ACLPermissions) * [Builtin ACL Schemes](#sc_BuiltinACLSchemes) * [ZooKeeper C client API](#ZooKeeper+C+client+API) * [Pluggable ZooKeeper authentication](#sc_ZooKeeperPluggableAuthentication) * [Consistency Guarantees](#ch_zkGuarantees) * [Bindings](#ch_bindings) * [Java Binding](#Java+Binding) * [Client Configuration Parameters](#sc_java_client_configuration) * [C Binding](#C+Binding) * [Installation](#Installation) * [Building Your Own C Client](#Building+Your+Own+C+Client) * [Building Blocks: A Guide to ZooKeeper Operations](#ch_guideToZkOperations) * [Handling Errors](#sc_errorsZk) * [Connecting to ZooKeeper](#sc_connectingToZk) * [Gotchas: Common Problems and Troubleshooting](#ch_gotchas) ## 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 a 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 ZooKeeper Data Model](#ch_zkDataModel) * [ZooKeeper Sessions](#ch_zkSessions) * [ZooKeeper Watches](#ch_zkWatches) * [Consistency Guarantees](#ch_zkGuarantees) The next four sections provide practical programming information. These are: * [Building Blocks: A Guide to ZooKeeper Operations](#ch_guideToZkOperations) * [Bindings](#ch_bindings) * [Gotchas: Common Problems and Troubleshooting](#ch_gotchas) The book concludes with an [appendix](#apx_linksToOtherInfo) containing links to other useful, ZooKeeper-related information. Most of the 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 chapters on the [ZooKeeper Data Model](#ch_zkDataModel) and [ZooKeeper Basic Operations](#ch_guideToZkOperations). ## The ZooKeeper Data Model ZooKeeper has a hierarchal namespace, 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 - \\u001F and \\u007F - \\u009F. * The following characters are not allowed: \\ud800 - uF8FF, \\uFFF0 - uFFFF. * 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, allows ZooKeeper to validate the cache and to coordinate updates. Each time a znode's data changes, the version number increases. For instance, whenever a client retrieves data, it also receives the version of the data. And when a client performs an update or a delete, 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. ######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_ refers 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 entity 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](#ch_zkWatches). #### 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 usual 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. The list of ephemerals for the session can be retrieved using **getEphemerals()** api. ##### getEphemerals() Retrieves the list of ephemeral nodes created by the session for the given path. If the path is empty, it will list all the ephemeral nodes for the session. **Use Case** - A sample use case might be, if the list of ephemeral nodes for the session needs to be collected for duplicate data entry check and the nodes are created in a sequential manner so you do not know the name for duplicate check. In that case, getEphemerals() api could be used to get the list of nodes for the session. This might be a typical use case for service discovery. #### 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. "0000000001". See [Queue Recipe](recipes.html#sc_recipes_Queues) 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 "-2147483648"). #### Container Nodes **Added in 3.5.3** ZooKeeper has the notion of container znodes. Container znodes are special purpose znodes useful for recipes such as leader, lock, etc. When the last child of a container is deleted, the container becomes a candidate to be deleted by the server at some point in the future. Given this property, you should be prepared to get KeeperException.NoNodeException when creating children inside of container znodes. i.e. when creating child znodes inside of container znodes always check for KeeperException.NoNodeException and recreate the container znode when it occurs. #### TTL Nodes **Added in 3.5.3** When creating PERSISTENT or PERSISTENT_SEQUENTIAL znodes, you can optionally set a TTL in milliseconds for the znode. If the znode is not modified within the TTL and has no children it will become a candidate to be deleted by the server at some point in the future. Note: TTL Nodes must be enabled via System property as they are disabled by default. See the [Administrator's Guide](zookeeperAdmin.html#sc_configuration) for details. If you attempt to create TTL Nodes without the proper System property set the server will throw KeeperException.UnimplementedException. ### 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 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. * **pzxid** The zxid of the change that last modified children of 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 off 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 the client handle 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: ![State transitions](images/state_dia.jpg) 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) 1. .... client is partitioned from the cluster 1. 'disconnected' : client has lost connectivity with the cluster 1. .... time elapses, after 'timeout' period the cluster expires the session, nothing is seen by client as it is disconnected from cluster 1. .... time elapses, the client regains network level connectivity with the cluster 1. 'expired' : eventually the client reconnects to the cluster, it is then notified of the expiration 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 1. The ZooKeeper client disconnects from a server when there are pending operations to that server, i.e., there is a pending asynchronous call. **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 been reestablished on a different server. The normal cause of this error is a client that sends a request to a server, but the network packet gets delayed, so the client times out and connects to a new server. When the delayed packet arrives at the first server, the old server detects that the session has moved, and closes the 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 its connection/session indefinitely). **Updating the list of servers**. We allow a client to update the connection string by providing a new comma separated list of host:port pairs, each corresponding to a ZooKeeper server. The function invokes a probabilistic load-balancing algorithm which may cause the client to disconnect from its current host with the goal to achieve expected uniform number of connections per server in the new list. In case the current host to which the client is connected is not in the new list this call will always cause the connection to be dropped. Otherwise, the decision is based on whether the number of servers has increased or decreased and by how much. For example, if the previous connection string contained 3 hosts and now the list contains these 3 hosts and 2 more hosts, 40% of clients connected to each of the 3 hosts will move to one of the new hosts in order to balance the load. The algorithm will cause the client to drop its connection to the current host to which it is connected with probability 0.4 and in this case cause the client to connect to one of the 2 new hosts, chosen at random. Another example -- suppose we have 5 hosts and now update the list to remove 2 of the hosts, the clients connected to the 3 remaining hosts will stay connected, whereas all clients connected to the 2 removed hosts will need to move to one of the 3 hosts, chosen at random. If the connection is dropped, the client moves to a special mode where he chooses a new server to connect to using the probabilistic algorithm, and not just round robin. In the first example, each client decides to disconnect with probability 0.4 but once the decision is made, it will try to connect to a random new server and only if it cannot connect to any of the new servers will it try to connect to the old ones. After finding a server, or trying all servers in the new list and failing to connect, the client moves back to the normal mode of operation where it picks an arbitrary server from the connectString and attempts to connect to it. If that fails, it will continue trying different random servers in round robin. (see above the algorithm used to initially choose a server) **Local session**. Added in 3.5.0, mainly implemented by [ZOOKEEPER-1147](https://issues.apache.org/jira/browse/ZOOKEEPER-1147). - Background: The creation and closing of sessions are costly in ZooKeeper because they need quorum confirmations, they become the bottleneck of a ZooKeeper ensemble when it needs to handle thousands of client connections. So after 3.5.0, we introduce a new type of session: local session which doesn't have a full functionality of a normal(global) session, this feature will be available by turning on *localSessionsEnabled*. when *localSessionsUpgradingEnabled* is disable: - Local sessions cannot create ephemeral nodes - Once a local session is lost, users cannot re-establish it using the session-id/password, the session and its watches are gone for good. Note: Losing the tcp connection does not necessarily imply that the session is lost. If the connection can be reestablished with the same zk server before the session timeout then the client can continue (it simply cannot move to another server). - When a local session connects, the session info is only maintained on the zookeeper server that it is connected to. The leader is not aware of the creation of such a session and there is no state written to disk. - The pings, expiration and other session state maintenance are handled by the server which current session is connected to. when *localSessionsUpgradingEnabled* is enable: - A local session can be upgraded to the global session automatically. - When a new session is created it is saved locally in a wrapped *LocalSessionTracker*. It can subsequently be upgraded to a global session as required (e.g. create ephemeral nodes). If an upgrade is requested the session is removed from local collections while keeping the same session ID. - Currently, Only the operation: *create ephemeral node* needs a session upgrade from local to global. The reason is that the creation of ephemeral node depends heavily on a global session. If local session can create ephemeral node without upgrading to global session, it will cause the data inconsistency between different nodes. The leader also needs to know about the lifespan of a session in order to clean up ephemeral nodes on close/expiry. This requires a global session as the local session is tied to its particular server. - A session can be both a local and global session during upgrade, but the operation of upgrade cannot be called concurrently by two thread. - *ZooKeeperServer*(Standalone) uses *SessionTrackerImpl*; *LeaderZookeeper* uses *LeaderSessionTracker* which holds *SessionTrackerImpl*(global) and *LocalSessionTracker*(if enable); *FollowerZooKeeperServer* and *ObserverZooKeeperServer* use *LearnerSessionTracker* which holds *LocalSessionTracker*. The UML Graph of Classes about session: ``` +----------------+ +--------------------+ +---------------------+ | | --> | | ----> | LocalSessionTracker | | SessionTracker | | SessionTrackerImpl | +---------------------+ | | | | +-----------------------+ | | | | +-------------------------> | LeaderSessionTracker | +----------------+ +--------------------+ | +-----------------------+ | | | | | | | +---------------------------+ +---------> | | | UpgradeableSessionTracker | | | | | ------------------------+ +---------------------------+ | | | v +-----------------------+ | LearnerSessionTracker | +-----------------------+ ``` - Q&A - *What's the reason for having the config option to disable local session upgrade?* - In a large deployment which wants to handle a very large number of clients, we know that clients connecting via the observers which is supposed to be local session only. So this is more like a safeguard against someone accidentally creates lots of ephemeral nodes and global sessions. - *When is the session created?* - In the current implementation, it will try to create a local session when processing *ConnectRequest* and when *createSession* request reaches *FinalRequestProcessor*. - *What happens if the create for session is sent at server A and the client disconnects to some other server B which ends up sending it again and then disconnects and connects back to server A?* - When a client reconnects to B, its sessionId won’t exist in B’s local session tracker. So B will send validation packet. If CreateSession issued by A is committed before validation packet arrive the client will be able to connect. Otherwise, the client will get session expired because the quorum hasn’t know about this session yet. If the client also tries to connect back to A again, the session is already removed from local session tracker. So A will need to send a validation packet to the leader. The outcome should be the same as B depending on the timing of the request. ## 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 lightweight to set, maintain, and dispatch. When a client connects to a new server, the watch will be triggered for any session events. Watches will not be received while disconnected from a server. When a client reconnects, any previously registered watches will be reregistered and triggered if needed. In general this all occurs transparently. There is one case where a watch may be missed: a watch for the existence of a znode not yet created will be missed if the znode is created and deleted while disconnected. **New in 3.6.0:** Clients can also set permanent, recursive watches on a znode that are not removed when triggered and that trigger for changes on the registered znode as well as any children znodes recursively. ### Semantics of Watches We can set watches with the three calls that read the state of ZooKeeper: exists, getData, and getChildren. The following list details the events that a watch can trigger and the calls that enable them: * **Created event:** Enabled with a call to exists. * **Deleted event:** Enabled with a call to exists, getData, and getChildren. * **Changed event:** Enabled with a call to exists and getData. * **Child event:** Enabled with a call to getChildren. ### Persistent, Recursive Watches **New in 3.6.0:** There is now a variation on the standard watch described above whereby you can set a watch that does not get removed when triggered. Additionally, these watches trigger the event types *NodeCreated*, *NodeDeleted*, and *NodeDataChanged* and, optionally, recursively for all znodes starting at the znode that the watch is registered for. Note that *NodeChildrenChanged* events are not triggered for persistent recursive watches as it would be redundant. Persistent watches are set using the method *addWatch()*. The triggering semantics and guarantees (other than one-time triggering) are the same as standard watches. The only exception regarding events is that recursive persistent watchers never trigger child changed events as they are redundant. Persistent watches are removed using *removeWatches()* with watcher type *WatcherType.Any*. ### Remove Watches We can remove the watches registered on a znode with a call to removeWatches. Also, a ZooKeeper client can remove watches locally even if there is no server connection by setting the local flag to true. The following list details the events which will be triggered after the successful watch removal. * **Child Remove event:** Watcher which was added with a call to getChildren. * **Data Remove event:** Watcher which was added with a call to exists or getData. * **Persistent Remove event:** Watcher which was added with a call to add a persistent watch. ### 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 * Standard 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 standard 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:expression_, where _scheme_ is the authentication scheme that the id corresponds to. The set of valid expressions are defined by the scheme. For example, _ip:172.16.16.1_ is an id for a host with the address _172.16.16.1_ using the _ip_ scheme, whereas _digest:bob:password_ is an id for the user with the name of _bob_ using the _digest_ scheme. 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 client 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.) _ADMIN_ permission also has a special role in terms of ACLs: in order to retrieve ACLs of a znode user has to have _READ_ or _ADMIN_ permission, but without _ADMIN_ permission, digest hash values will be masked out. As of versions **3.9.2 / 3.8.4 / 3.7.3** the exists() call will now verify ACLs on nodes that exist and the client must have READ permission otherwise 'Insufficient permission' error will be raised. #### Builtin ACL Schemes ZooKeeper has the following built in schemes: * **world** has a single id, _anyone_, that represents anyone. * **auth** is a special scheme which ignores any provided expression and instead uses the current user, credentials, and scheme. Any expression (whether _user_ like with SASL authentication or _user:password_ like with DIGEST authentication) provided is ignored by the ZooKeeper server when persisting the ACL. However, the expression must still be provided in the ACL because the ACL must match the form _scheme:expression:perms_. This scheme is provided as a convenience as it is a common use-case for a user to create a znode and then restrict access to that znode to only that user. If there is no authenticated user, setting an ACL with the auth scheme will fail. * **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. * **x509** uses the client X500 Principal as an ACL ID identity. The ACL expression is the exact X500 Principal name of a client. When using the secure port, clients are automatically authenticated and their auth info for the x509 scheme is set. #### 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. The node must have READ or ADMIN permission set. Without ADMIN permission, the digest hash values will be masked out. * _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/zookeeper-client/zookeeper-client-c/src/cli.c* for an example of a C client implementation #include #include #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 associated 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 _-Dzookeeper.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 _-Dzookeeper.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. **Added in 3.6.0**: An alternate abstraction is available for pluggable authentication. It provides additional arguments. public abstract class ServerAuthenticationProvider implements AuthenticationProvider { public abstract KeeperException.Code handleAuthentication(ServerObjs serverObjs, byte authData[]); public abstract boolean matches(ServerObjs serverObjs, MatchValues matchValues); } Instead of implementing AuthenticationProvider you extend ServerAuthenticationProvider. Your handleAuthentication() and matches() methods will then receive the additional parameters (via ServerObjs and MatchValues). * **ZooKeeperServer** The ZooKeeperServer instance * **ServerCnxn** The current connection * **path** The ZNode path being operated on (or null if not used) * **perm** The operation value or 0 * **setAcls** When the setAcl() method is being operated on, the list of ACLs that are being set ## 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. i.e., a client will never see an older view of the system even if the client fails over to a different server with the same session. * *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 guarantee is only present with successful return codes. (This is called the _monotonicity condition_ in Paxos.) 1. 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. * *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](recipes.html) 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 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](recipes.html). ## 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 across 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 of the completion callback 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. #### Client Configuration Parameters The following list contains configuration properties for the Java client. You can set any of these properties using Java system properties. For server properties, please check the [Server configuration section of the Admin Guide](zookeeperAdmin.html#sc_configuration). The ZooKeeper Wiki also has useful pages about [ZooKeeper SSL support](https://cwiki.apache.org/confluence/display/ZOOKEEPER/ZooKeeper+SSL+User+Guide), and [SASL authentication for ZooKeeper](https://cwiki.apache.org/confluence/display/ZOOKEEPER/ZooKeeper+and+SASL). * *zookeeper.sasl.client* : Set the value to **false** to disable SASL authentication. Default is **true**. * *zookeeper.sasl.clientconfig* : Specifies the context key in the JAAS login file. Default is "Client". * *zookeeper.server.principal* : Specifies the server principal to be used by the client for authentication, while connecting to the zookeeper server, when Kerberos authentication is enabled. If this configuration is provided, then the ZooKeeper client will NOT USE any of the following parameters to determine the server principal: zookeeper.sasl.client.username, zookeeper.sasl.client.canonicalize.hostname, zookeeper.server.realm Note: this config parameter is working only for ZooKeeper 3.5.7+, 3.6.0+ * *zookeeper.sasl.client.username* : Traditionally, a principal is divided into three parts: the primary, the instance, and the realm. The format of a typical Kerberos V5 principal is primary/instance@REALM. zookeeper.sasl.client.username specifies the primary part of the server principal. Default is "zookeeper". Instance part is derived from the server IP. Finally server's principal is username/IP@realm, where username is the value of zookeeper.sasl.client.username, IP is the server IP, and realm is the value of zookeeper.server.realm. * *zookeeper.sasl.client.canonicalize.hostname* : Expecting the zookeeper.server.principal parameter is not provided, the ZooKeeper client will try to determine the 'instance' (host) part of the ZooKeeper server principal. First it takes the hostname provided as the ZooKeeper server connection string. Then it tries to 'canonicalize' the address by getting the fully qualified domain name belonging to the address. You can disable this 'canonicalization' by setting: zookeeper.sasl.client.canonicalize.hostname=false * *zookeeper.server.realm* : Realm part of the server principal. By default it is the client principal realm. * *zookeeper.disableAutoWatchReset* : This switch controls whether automatic watch resetting is enabled. Clients automatically reset watches during session reconnect by default, this option allows the client to turn off this behavior by setting zookeeper.disableAutoWatchReset to **true**. * *zookeeper.client.secure* : **New in 3.5.5:** If you want to connect to the server secure client port, you need to set this property to **true** on the client. This will connect to server using SSL with specified credentials. Note that it requires the Netty client. * *zookeeper.clientCnxnSocket* : Specifies which ClientCnxnSocket to be used. Possible values are **org.apache.zookeeper.ClientCnxnSocketNIO** and **org.apache.zookeeper.ClientCnxnSocketNetty** . Default is **org.apache.zookeeper.ClientCnxnSocketNIO** . If you want to connect to server's secure client port, you need to set this property to **org.apache.zookeeper.ClientCnxnSocketNetty** on client. * *zookeeper.ssl.keyStore.location and zookeeper.ssl.keyStore.password* : **New in 3.5.5:** Specifies the file path to a JKS containing the local credentials to be used for SSL connections, and the password to unlock the file. * *zookeeper.ssl.keyStore.passwordPath* : **New in 3.8.0:** Specifies the file path which contains the keystore password * *zookeeper.ssl.trustStore.location and zookeeper.ssl.trustStore.password* : **New in 3.5.5:** Specifies the file path to a JKS containing the remote credentials to be used for SSL connections, and the password to unlock the file. * *zookeeper.ssl.trustStore.passwordPath* : **New in 3.8.0:** Specifies the file path which contains the truststore password * *zookeeper.ssl.keyStore.type* and *zookeeper.ssl.trustStore.type*: **New in 3.5.5:** Specifies the file format of keys/trust store files used to establish TLS connection to the ZooKeeper server. Values: JKS, PEM, PKCS12 or null (detect by filename). Default: null. **New in 3.6.3, 3.7.0:** The format BCFKS was added. * *jute.maxbuffer* : In the client side, it specifies the maximum size of the incoming data from the server. The default is 0xfffff(1048575) bytes, or just under 1M. This is really a sanity check. The ZooKeeper server is designed to store and send data on the order of kilobytes. If incoming data length is more than this value, an IOException is raised. This value of client side should keep same with the server side(Setting **System.setProperty("jute.maxbuffer", "xxxx")** in the client side will work), otherwise problems will arise. * *zookeeper.kinit* : Specifies path to kinit binary. Default is "/usr/bin/kinit". ### 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 `mvn compile` in zookeeper-jute directory (*.../trunk/zookeeper-jute*). This will create a directory named "generated" under *.../trunk/zookeeper-client/zookeeper-client-c*. 1. Change directory to the*.../trunk/zookeeper-client/zookeeper-client-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**. 1. If you are building from a project source package, unzip/untar the source tarball and cd to the* zookeeper-x.x.x/zookeeper-client/zookeeper-client-c* directory. 1. Run `./configure ` 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**. 1. Run `make` or `make install` to build the libraries and install them. 1. 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` #### Building Your Own C Client In order to be able to use the ZooKeeper C API in your application you have to remember to 1. Include ZooKeeper header: `#include ` 1. 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 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. ######Note >See *.../trunk/zookeeper-client/zookeeper-client-c/src/cli.c* for an example of a C client implementation ## 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: * [Connecting to ZooKeeper](#sc_connectingToZk) ### 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 Before we begin, you will have to set up a running Zookeeper server so that we can start developing the client. For C client bindings, we will be using the multithreaded library(zookeeper_mt) with a simple example written in C. To establish a connection with Zookeeper server, we make use of C API - _zookeeper_init_ with the following signature: int zookeeper_init(const char *host, watcher_fn fn, int recv_timeout, const clientid_t *clientid, void *context, int flags); * **host* : Connection string to zookeeper server in the format of host:port. If there are multiple servers, use comma as separator after specifying the host:port pairs. Eg: "127.0.0.1:2181,127.0.0.1:3001,127.0.0.1:3002" * *fn* : Watcher function to process events when a notification is triggered. * *recv_timeout* : Session expiration time in milliseconds. * **clientid* : We can specify 0 for a new session. If a session has already establish previously, we could provide that client ID and it would reconnect to that previous session. * **context* : Context object that can be associated with the zkhandle_t handler. If it is not used, we can set it to 0. * *flags* : In an initiation, we can leave it for 0. We will demonstrate client that outputs "Connected to Zookeeper" after successful connection or an error message otherwise. Let's call the following code _zkClient.cc_ : #include #include #include using namespace std; // Keeping track of the connection state static int connected = 0; static int expired = 0; // *zkHandler handles the connection with Zookeeper static zhandle_t *zkHandler; // watcher function would process events void watcher(zhandle_t *zkH, int type, int state, const char *path, void *watcherCtx) { if (type == ZOO_SESSION_EVENT) { // state refers to states of zookeeper connection. // To keep it simple, we would demonstrate these 3: ZOO_EXPIRED_SESSION_STATE, ZOO_CONNECTED_STATE, ZOO_NOTCONNECTED_STATE // If you are using ACL, you should be aware of an authentication failure state - ZOO_AUTH_FAILED_STATE if (state == ZOO_CONNECTED_STATE) { connected = 1; } else if (state == ZOO_NOTCONNECTED_STATE ) { connected = 0; } else if (state == ZOO_EXPIRED_SESSION_STATE) { expired = 1; connected = 0; zookeeper_close(zkH); } } } int main(){ zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG); // zookeeper_init returns the handler upon a successful connection, null otherwise zkHandler = zookeeper_init("localhost:2181", watcher, 10000, 0, 0, 0); if (!zkHandler) { return errno; }else{ printf("Connection established with Zookeeper. \n"); } // Close Zookeeper connection zookeeper_close(zkHandler); return 0; } Compile the code with the multithreaded library mentioned before. `> g++ -Iinclude/ zkClient.cpp -lzookeeper_mt -o Client` Run the client. `> ./Client` From the output, you should see "Connected to Zookeeper" along with Zookeeper's DEBUG messages if the connection is successful. ## 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 existence, you will miss the event if the znode is created and deleted while you are disconnected. 1. 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. 1. 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. 1. 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. 1. 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. ## Links to Other Information Outside the formal documentation, there're several other sources of information for ZooKeeper developers. * *[API Reference](https://zookeeper.apache.org/doc/current/apidocs/zookeeper-server/index.html)* : The complete reference to the ZooKeeper API * *[ZooKeeper Talk at the Hadoop Summit 2008](https://www.youtube.com/watch?v=rXI9xiesUV8)* : A video introduction to ZooKeeper, by Benjamin Reed of Yahoo! Research * *[Barrier and Queue Tutorial](https://cwiki.apache.org/confluence/display/ZOOKEEPER/Tutorial)* : The excellent Java tutorial by Flavio Junqueira, implementing simple barriers and producer-consumer queues using ZooKeeper. * *[ZooKeeper - A Reliable, Scalable Distributed Coordination System](https://cwiki.apache.org/confluence/display/ZOOKEEPER/ZooKeeperArticles)* : An article by Todd Hoff (07/15/2008) * *[ZooKeeper Recipes](recipes.html)* : Pseudo-level discussion of the implementation of various synchronization solutions with ZooKeeper: Event Handles, Queues, Locks, and Two-phase Commits. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperQuotas.md0100644 0000000 0000000 00000006233 15051152474 031342 0ustar00rootroot0000000 0000000 # ZooKeeper Quota's Guide ### A Guide to Deployment and Administration * [Quotas](#zookeeper_quotas) * [Setting Quotas](#Setting+Quotas) * [Listing Quotas](#Listing+Quotas) * [Deleting Quotas](#Deleting+Quotas) ## 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. Notice: What the `namespace` quota means is the count quota which limits the number of children under the path(included itself). $ bin/zkCli.sh -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/count) and `-b` (for bytes/data length). - The ZooKeeper quota is stored in ZooKeeper itself in **/zookeeper/quota**. To disable other people from changing the quotas, users can set the ACL for **/zookeeper/quota** ,so that only admins are able to read and write to it. - If the quota doesn't exist in the specified path,create the quota, otherwise update the quota. - The Scope of the quota users set is all the nodes under the path specified (included itself). - In order to simplify the calculation of quota in the current directory/hierarchy structure, a complete tree path(from root to leaf node) can be set only one quota. In the situation when setting a quota in a path which its parent or child node already has a quota. `setquota` will reject and tell the specified parent or child path, users can adjust allocations of quotas(delete/move-up/move-down the quota) according to specific circumstances. - Combined with the Chroot, the quota will have a better isolation effectiveness between different applications.For example: ```bash # Chroot is: 192.168.0.1:2181,192.168.0.2:2181,192.168.0.3:2181/apps/app1 setquota -n 100000 /apps/app1 ``` - Users cannot set the quota on the path under **/zookeeper/quota** - The quota supports the soft and hard quota. The soft quota just logs the warning info when exceeding the quota, but the hard quota also throws a `QuotaExceededException`. When setting soft and hard quota on the same path, the hard quota has the priority. ### 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. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperReconfig.md0100644 0000000 0000000 00000123216 15051152474 031623 0ustar00rootroot0000000 0000000 # ZooKeeper Dynamic Reconfiguration * [Overview](#ch_reconfig_intro) * [Changes to Configuration Format](#ch_reconfig_format) * [Specifying the client port](#sc_reconfig_clientport) * [Specifying multiple server addresses](#sc_multiaddress) * [The standaloneEnabled flag](#sc_reconfig_standaloneEnabled) * [The reconfigEnabled flag](#sc_reconfig_reconfigEnabled) * [Dynamic configuration file](#sc_reconfig_file) * [Backward compatibility](#sc_reconfig_backward) * [Upgrading to 3.5.0](#ch_reconfig_upgrade) * [Dynamic Reconfiguration of the ZooKeeper Ensemble](#ch_reconfig_dyn) * [API](#ch_reconfig_api) * [Security](#sc_reconfig_access_control) * [Retrieving the current dynamic configuration](#sc_reconfig_retrieving) * [Modifying the current dynamic configuration](#sc_reconfig_modifying) * [General](#sc_reconfig_general) * [Incremental mode](#sc_reconfig_incremental) * [Non-incremental mode](#sc_reconfig_nonincremental) * [Conditional reconfig](#sc_reconfig_conditional) * [Error conditions](#sc_reconfig_errors) * [Additional comments](#sc_reconfig_additional) * [Rebalancing Client Connections](#ch_reconfig_rebalancing) ## Overview Prior to the 3.5.0 release, the membership and all other configuration parameters of Zookeeper were static - loaded during boot and immutable at runtime. Operators resorted to ''rolling restarts'' - a manually intensive and error-prone method of changing the configuration that has caused data loss and inconsistency in production. Starting with 3.5.0, “rolling restarts†are no longer needed! ZooKeeper comes with full support for automated configuration changes: the set of Zookeeper servers, their roles (participant / observer), all ports, and even the quorum system can be changed dynamically, without service interruption and while maintaining data consistency. Reconfigurations are performed immediately, just like other operations in ZooKeeper. Multiple changes can be done using a single reconfiguration command. The dynamic reconfiguration functionality does not limit operation concurrency, does not require client operations to be stopped during reconfigurations, has a very simple interface for administrators and no added complexity to other client operations. New client-side features allow clients to find out about configuration changes and to update the connection string (list of servers and their client ports) stored in their ZooKeeper handle. A probabilistic algorithm is used to rebalance clients across the new configuration servers while keeping the extent of client migrations proportional to the change in ensemble membership. This document provides the administrator manual for reconfiguration. For a detailed description of the reconfiguration algorithms, performance measurements, and more, please see our paper: * *Shraer, A., Reed, B., Malkhi, D., Junqueira, F. Dynamic Reconfiguration of Primary/Backup Clusters. In _USENIX Annual Technical Conference (ATC)_(2012), 425-437* : Links: [paper (pdf)](https://www.usenix.org/system/files/conference/atc12/atc12-final74.pdf), [slides (pdf)](https://www.usenix.org/sites/default/files/conference/protected-files/shraer\_atc12\_slides.pdf), [video](https://www.usenix.org/conference/atc12/technical-sessions/presentation/shraer), [hadoop summit slides](http://www.slideshare.net/Hadoop\_Summit/dynamic-reconfiguration-of-zookeeper) **Note:** Starting with 3.5.3, the dynamic reconfiguration feature is disabled by default, and has to be explicitly turned on via [reconfigEnabled](zookeeperAdmin.html#sc_advancedConfiguration) configuration option. ## Changes to Configuration Format ### Specifying the client port A client port of a server is the port on which the server accepts client connection requests. Starting with 3.5.0 the _clientPort_ and _clientPortAddress_ configuration parameters should no longer be used. Instead, this information is now part of the server keyword specification, which becomes as follows: server. = ::[:role];[:]** The client port specification is to the right of the semicolon. The client port address is optional, and if not specified it defaults to "0.0.0.0". As usual, role is also optional, it can be _participant_ or _observer_ (_participant_ by default). Examples of legal server statements: server.5 = 125.23.63.23:1234:1235;1236 server.5 = 125.23.63.23:1234:1235:participant;1236 server.5 = 125.23.63.23:1234:1235:observer;1236 server.5 = 125.23.63.23:1234:1235;125.23.63.24:1236 server.5 = 125.23.63.23:1234:1235:participant;125.23.63.23:1236 ### Specifying multiple server addresses Since ZooKeeper 3.6.0 it is possible to specify multiple addresses for each ZooKeeper server (see [ZOOKEEPER-3188](https://issues.apache.org/jira/projects/ZOOKEEPER/issues/ZOOKEEPER-3188)). This helps to increase availability and adds network level resiliency to ZooKeeper. When multiple physical network interfaces are used for the servers, ZooKeeper is able to bind on all interfaces and runtime switching to a working interface in case a network error. The different addresses can be specified in the config using a pipe ('|') character. Examples for a valid configurations using multiple addresses: server.2=zoo2-net1:2888:3888|zoo2-net2:2889:3889;2188 server.2=zoo2-net1:2888:3888|zoo2-net2:2889:3889|zoo2-net3:2890:3890;2188 server.2=zoo2-net1:2888:3888|zoo2-net2:2889:3889;zoo2-net1:2188 server.2=zoo2-net1:2888:3888:observer|zoo2-net2:2889:3889:observer;2188 ### The _standaloneEnabled_ flag Prior to 3.5.0, one could run ZooKeeper in Standalone mode or in a Distributed mode. These are separate implementation stacks, and switching between them during run time is not possible. By default (for backward compatibility) _standaloneEnabled_ is set to _true_. The consequence of using this default is that if started with a single server the ensemble will not be allowed to grow, and if started with more than one server it will not be allowed to shrink to contain fewer than two participants. Setting the flag to _false_ instructs the system to run the Distributed software stack even if there is only a single participant in the ensemble. To achieve this the (static) configuration file should contain: standaloneEnabled=false** With this setting it is possible to start a ZooKeeper ensemble containing a single participant and to dynamically grow it by adding more servers. Similarly, it is possible to shrink an ensemble so that just a single participant remains, by removing servers. Since running the Distributed mode allows more flexibility, we recommend setting the flag to _false_. We expect that the legacy Standalone mode will be deprecated in the future. ### The _reconfigEnabled_ flag Starting with 3.5.0 and prior to 3.5.3, there is no way to disable dynamic reconfiguration feature. We would like to offer the option of disabling reconfiguration feature because with reconfiguration enabled, we have a security concern that a malicious actor can make arbitrary changes to the configuration of a ZooKeeper ensemble, including adding a compromised server to the ensemble. We prefer to leave to the discretion of the user to decide whether to enable it or not and make sure that the appropriate security measure are in place. So in 3.5.3 the [reconfigEnabled](zookeeperAdmin.html#sc_advancedConfiguration) configuration option is introduced such that the reconfiguration feature can be completely disabled and any attempts to reconfigure a cluster through reconfig API with or without authentication will fail by default, unless **reconfigEnabled** is set to **true**. To set the option to true, the configuration file (zoo.cfg) should contain: reconfigEnabled=true ### Dynamic configuration file Starting with 3.5.0 we're distinguishing between dynamic configuration parameters, which can be changed during runtime, and static configuration parameters, which are read from a configuration file when a server boots and don't change during its execution. For now, the following configuration keywords are considered part of the dynamic configuration: _server_, _group_ and _weight_. Dynamic configuration parameters are stored in a separate file on the server (which we call the dynamic configuration file). This file is linked from the static config file using the new _dynamicConfigFile_ keyword. **Example** #### zoo_replicated1.cfg tickTime=2000 dataDir=/zookeeper/data/zookeeper1 initLimit=5 syncLimit=2 dynamicConfigFile=/zookeeper/conf/zoo_replicated1.cfg.dynamic #### zoo_replicated1.cfg.dynamic server.1=125.23.63.23:2780:2783:participant;2791 server.2=125.23.63.24:2781:2784:participant;2792 server.3=125.23.63.25:2782:2785:participant;2793 When the ensemble configuration changes, the static configuration parameters remain the same. The dynamic parameters are pushed by ZooKeeper and overwrite the dynamic configuration files on all servers. Thus, the dynamic configuration files on the different servers are usually identical (they can only differ momentarily when a reconfiguration is in progress, or if a new configuration hasn't propagated yet to some of the servers). Once created, the dynamic configuration file should not be manually altered. Changed are only made through the new reconfiguration commands outlined below. Note that changing the config of an offline cluster could result in an inconsistency with respect to configuration information stored in the ZooKeeper log (and the special configuration znode, populated from the log) and is therefore highly discouraged. **Example 2** Users may prefer to initially specify a single configuration file. The following is thus also legal: #### zoo_replicated1.cfg tickTime=2000 dataDir=/zookeeper/data/zookeeper1 initLimit=5 syncLimit=2 clientPort= The configuration files on each server will be automatically split into dynamic and static files, if they are not already in this format. So the configuration file above will be automatically transformed into the two files in Example 1. Note that the clientPort and clientPortAddress lines (if specified) will be automatically removed during this process, if they are redundant (as in the example above). The original static configuration file is backed up (in a .bak file). ### Backward compatibility We still support the old configuration format. For example, the following configuration file is acceptable (but not recommended): #### zoo_replicated1.cfg tickTime=2000 dataDir=/zookeeper/data/zookeeper1 initLimit=5 syncLimit=2 clientPort=2791 server.1=125.23.63.23:2780:2783:participant server.2=125.23.63.24:2781:2784:participant server.3=125.23.63.25:2782:2785:participant During boot, a dynamic configuration file is created and contains the dynamic part of the configuration as explained earlier. In this case, however, the line "clientPort=2791" will remain in the static configuration file of server 1 since it is not redundant -- it was not specified as part of the "server.1=..." using the format explained in the section [Changes to Configuration Format](#ch_reconfig_format). If a reconfiguration is invoked that sets the client port of server 1, we remove "clientPort=2791" from the static configuration file (the dynamic file now contain this information as part of the specification of server 1). ## Upgrading to 3.5.0 Upgrading a running ZooKeeper ensemble to 3.5.0 should be done only after upgrading your ensemble to the 3.4.6 release. Note that this is only necessary for rolling upgrades (if you're fine with shutting down the system completely, you don't have to go through 3.4.6). If you attempt a rolling upgrade without going through 3.4.6 (for example from 3.4.5), you may get the following error: 2013-01-30 11:32:10,663 [myid:2] - INFO [localhost/127.0.0.1:2784:QuorumCnxManager$Listener@498] - Received connection request /127.0.0.1:60876 2013-01-30 11:32:10,663 [myid:2] - WARN [localhost/127.0.0.1:2784:QuorumCnxManager@349] - Invalid server id: -65536 During a rolling upgrade, each server is taken down in turn and rebooted with the new 3.5.0 binaries. Before starting the server with 3.5.0 binaries, we highly recommend updating the configuration file so that all server statements "server.x=..." contain client ports (see the section [Specifying the client port](#sc_reconfig_clientport)). As explained earlier you may leave the configuration in a single file, as well as leave the clientPort/clientPortAddress statements (although if you specify client ports in the new format, these statements are now redundant). ## Dynamic Reconfiguration of the ZooKeeper Ensemble The ZooKeeper Java and C API were extended with getConfig and reconfig commands that facilitate reconfiguration. Both commands have a synchronous (blocking) variant and an asynchronous one. We demonstrate these commands here using the Java CLI, but note that you can similarly use the C CLI or invoke the commands directly from a program just like any other ZooKeeper command. ### API There are two sets of APIs for both Java and C client. * ***Reconfiguration API*** : Reconfiguration API is used to reconfigure the ZooKeeper cluster. Starting with 3.5.3, reconfiguration Java APIs are moved into ZooKeeperAdmin class from ZooKeeper class, and use of this API requires ACL setup and user authentication (see [Security](#sc_reconfig_access_control) for more information.). * ***Get Configuration API*** : Get configuration APIs are used to retrieve ZooKeeper cluster configuration information stored in /zookeeper/config znode. Use of this API does not require specific setup or authentication, because /zookeeper/config is readable to any users. ### Security Prior to **3.5.3**, there is no enforced security mechanism over reconfig so any ZooKeeper clients that can connect to ZooKeeper server ensemble will have the ability to change the state of a ZooKeeper cluster via reconfig. It is thus possible for a malicious client to add compromised server to an ensemble, e.g., add a compromised server, or remove legitimate servers. Cases like these could be security vulnerabilities on a case by case basis. To address this security concern, we introduced access control over reconfig starting from **3.5.3** such that only a specific set of users can use reconfig commands or APIs, and these users need be configured explicitly. In addition, the setup of ZooKeeper cluster must enable authentication so ZooKeeper clients can be authenticated. We also provide an escape hatch for users who operate and interact with a ZooKeeper ensemble in a secured environment (i.e. behind company firewall). For those users who want to use reconfiguration feature but don't want the overhead of configuring an explicit list of authorized user for reconfig access checks, they can set ["skipACL"](zookeeperAdmin.html#sc_authOptions) to "yes" which will skip ACL check and allow any user to reconfigure cluster. Overall, ZooKeeper provides flexible configuration options for the reconfigure feature that allow a user to choose based on user's security requirement. We leave to the discretion of the user to decide appropriate security measure are in place. * ***Access Control*** : The dynamic configuration is stored in a special znode ZooDefs.CONFIG_NODE = /zookeeper/config. This node by default is read only for all users, except super user and users that's explicitly configured for write access. Clients that need to use reconfig commands or reconfig API should be configured as users that have write access to CONFIG_NODE. By default, only the super user has full control including write access to CONFIG_NODE. Additional users can be granted write access through superuser by setting an ACL that has write permission associated with specified user. A few examples of how to setup ACLs and use reconfiguration API with authentication can be found in ReconfigExceptionTest.java and TestReconfigServer.cc. * ***Authentication*** : Authentication of users is orthogonal to the access control and is delegated to existing authentication mechanism supported by ZooKeeper's pluggable authentication schemes. See [ZooKeeper and SASL](https://cwiki.apache.org/confluence/display/ZOOKEEPER/Zookeeper+and+SASL) for more details on this topic. * ***Disable ACL check*** : ZooKeeper supports ["skipACL"](zookeeperAdmin.html#sc_authOptions) option such that ACL check will be completely skipped, if skipACL is set to "yes". In such cases any unauthenticated users can use reconfig API. ### Retrieving the current dynamic configuration The dynamic configuration is stored in a special znode ZooDefs.CONFIG_NODE = /zookeeper/config. The new `config` CLI command reads this znode (currently it is simply a wrapper to `get /zookeeper/config`). As with normal reads, to retrieve the latest committed value you should do a `sync` first. [zk: 127.0.0.1:2791(CONNECTED) 3] config server.1=localhost:2780:2783:participant;localhost:2791 server.2=localhost:2781:2784:participant;localhost:2792 server.3=localhost:2782:2785:participant;localhost:2793 Notice the last line of the output. This is the configuration version. The version equals to the zxid of the reconfiguration command which created this configuration. The version of the first established configuration equals to the zxid of the NEWLEADER message sent by the first successfully established leader. When a configuration is written to a dynamic configuration file, the version automatically becomes part of the filename and the static configuration file is updated with the path to the new dynamic configuration file. Configuration files corresponding to earlier versions are retained for backup purposes. During boot time the version (if it exists) is extracted from the filename. The version should never be altered manually by users or the system administrator. It is used by the system to know which configuration is most up-to-date. Manipulating it manually can result in data loss and inconsistency. Just like a `get` command, the `config` CLI command accepts the _-w_ flag for setting a watch on the znode, and _-s_ flag for displaying the Stats of the znode. It additionally accepts a new flag _-c_ which outputs only the version and the client connection string corresponding to the current configuration. For example, for the configuration above we would get: [zk: 127.0.0.1:2791(CONNECTED) 17] config -c 400000003 localhost:2791,localhost:2793,localhost:2792 Note that when using the API directly, this command is called `getConfig`. As any read command it returns the configuration known to the follower to which your client is connected, which may be slightly out-of-date. One can use the `sync` command for stronger guarantees. For example using the Java API: zk.sync(ZooDefs.CONFIG_NODE, void_callback, context); zk.getConfig(watcher, callback, context); Note: in 3.5.0 it doesn't really matter which path is passed to the `sync()` command as all the server's state is brought up to date with the leader (so one could use a different path instead of ZooDefs.CONFIG_NODE). However, this may change in the future. ### Modifying the current dynamic configuration Modifying the configuration is done through the `reconfig` command. There are two modes of reconfiguration: incremental and non-incremental (bulk). The non-incremental simply specifies the new dynamic configuration of the system. The incremental specifies changes to the current configuration. The `reconfig` command returns the new configuration. A few examples are in: *ReconfigTest.java*, *ReconfigRecoveryTest.java* and *TestReconfigServer.cc*. #### General **Removing servers:** Any server can be removed, including the leader (although removing the leader will result in a short unavailability, see Figures 6 and 8 in the [paper](https://www.usenix.org/conference/usenixfederatedconferencesweek/dynamic-recon%EF%AC%81guration-primarybackup-clusters)). The server will not be shut-down automatically. Instead, it becomes a "non-voting follower". This is somewhat similar to an observer in that its votes don't count towards the Quorum of votes necessary to commit operations. However, unlike a non-voting follower, an observer doesn't actually see any operation proposals and does not ACK them. Thus a non-voting follower has a more significant negative effect on system throughput compared to an observer. Non-voting follower mode should only be used as a temporary mode, before shutting the server down, or adding it as a follower or as an observer to the ensemble. We do not shut the server down automatically for two main reasons. The first reason is that we do not want all the clients connected to this server to be immediately disconnected, causing a flood of connection requests to other servers. Instead, it is better if each client decides when to migrate independently. The second reason is that removing a server may sometimes (rarely) be necessary in order to change it from "observer" to "participant" (this is explained in the section [Additional comments](#sc_reconfig_additional)). Note that the new configuration should have some minimal number of participants in order to be considered legal. If the proposed change would leave the cluster with less than 2 participants and standalone mode is enabled (standaloneEnabled=true, see the section [The _standaloneEnabled_ flag](#sc_reconfig_standaloneEnabled)), the reconfig will not be processed (BadArgumentsException). If standalone mode is disabled (standaloneEnabled=false) then it's legal to remain with 1 or more participants. **Adding servers:** Before a reconfiguration is invoked, the administrator must make sure that a quorum (majority) of participants from the new configuration are already connected and synced with the current leader. To achieve this we need to connect a new joining server to the leader before it is officially part of the ensemble. This is done by starting the joining server using an initial list of servers which is technically not a legal configuration of the system but (a) contains the joiner, and (b) gives sufficient information to the joiner in order for it to find and connect to the current leader. We list a few different options of doing this safely. 1. Initial configuration of joiners is comprised of servers in the last committed configuration and one or more joiners, where **joiners are listed as observers.** For example, if servers D and E are added at the same time to (A, B, C) and server C is being removed, the initial configuration of D could be (A, B, C, D) or (A, B, C, D, E), where D and E are listed as observers. Similarly, the configuration of E could be (A, B, C, E) or (A, B, C, D, E), where D and E are listed as observers. **Note that listing the joiners as observers will not actually make them observers - it will only prevent them from accidentally forming a quorum with other joiners.** Instead, they will contact the servers in the current configuration and adopt the last committed configuration (A, B, C), where the joiners are absent. Configuration files of joiners are backed up and replaced automatically as this happens. After connecting to the current leader, joiners become non-voting followers until the system is reconfigured and they are added to the ensemble (as participant or observer, as appropriate). 1. Initial configuration of each joiner is comprised of servers in the last committed configuration + **the joiner itself, listed as a participant.** For example, to add a new server D to a configuration consisting of servers (A, B, C), the administrator can start D using an initial configuration file consisting of servers (A, B, C, D). If both D and E are added at the same time to (A, B, C), the initial configuration of D could be (A, B, C, D) and the configuration of E could be (A, B, C, E). Similarly, if D is added and C is removed at the same time, the initial configuration of D could be (A, B, C, D). Never list more than one joiner as participant in the initial configuration (see warning below). 1. Whether listing the joiner as an observer or as participant, it is also fine not to list all the current configuration servers, as long as the current leader is in the list. For example, when adding D we could start D with a configuration file consisting of just (A, D) if A is the current leader. however this is more fragile since if A fails before D officially joins the ensemble, D doesn’t know anyone else and therefore the administrator will have to intervene and restart D with another server list. ######Note >##### Warning >Never specify more than one joining server in the same initial configuration as participants. Currently, the joining servers don’t know that they are joining an existing ensemble; if multiple joiners are listed as participants they may form an independent quorum creating a split-brain situation such as processing operations independently from your main ensemble. It is OK to list multiple joiners as observers in an initial config. If the configuration of existing servers changes or they become unavailable before the joiner succeeds to connect and learn about configuration changes, the joiner may need to be restarted with an updated configuration file in order to be able to connect. Finally, note that once connected to the leader, a joiner adopts the last committed configuration, in which it is absent (the initial config of the joiner is backed up before being rewritten). If the joiner restarts in this state, it will not be able to boot since it is absent from its configuration file. In order to start it you’ll once again have to specify an initial configuration. **Modifying server parameters:** One can modify any of the ports of a server, or its role (participant/observer) by adding it to the ensemble with different parameters. This works in both the incremental and the bulk reconfiguration modes. It is not necessary to remove the server and then add it back; just specify the new parameters as if the server is not yet in the system. The server will detect the configuration change and perform the necessary adjustments. See an example in the section [Incremental mode](#sc_reconfig_incremental) and an exception to this rule in the section [Additional comments](#sc_reconfig_additional). It is also possible to change the Quorum System used by the ensemble (for example, change the Majority Quorum System to a Hierarchical Quorum System on the fly). This, however, is only allowed using the bulk (non-incremental) reconfiguration mode. In general, incremental reconfiguration only works with the Majority Quorum System. Bulk reconfiguration works with both Hierarchical and Majority Quorum Systems. **Performance Impact:** There is practically no performance impact when removing a follower, since it is not being automatically shut down (the effect of removal is that the server's votes are no longer being counted). When adding a server, there is no leader change and no noticeable performance disruption. For details and graphs please see Figures 6, 7 and 8 in the [paper](https://www.usenix.org/conference/usenixfederatedconferencesweek/dynamic-recon%EF%AC%81guration-primarybackup-clusters). The most significant disruption will happen when a leader change is caused, in one of the following cases: 1. Leader is removed from the ensemble. 1. Leader's role is changed from participant to observer. 1. The port used by the leader to send transactions to others (quorum port) is modified. In these cases we perform a leader hand-off where the old leader nominates a new leader. The resulting unavailability is usually shorter than when a leader crashes since detecting leader failure is unnecessary and electing a new leader can usually be avoided during a hand-off (see Figures 6 and 8 in the [paper](https://www.usenix.org/conference/usenixfederatedconferencesweek/dynamic-recon%EF%AC%81guration-primarybackup-clusters)). When the client port of a server is modified, it does not drop existing client connections. New connections to the server will have to use the new client port. **Progress guarantees:** Up to the invocation of the reconfig operation, a quorum of the old configuration is required to be available and connected for ZooKeeper to be able to make progress. Once reconfig is invoked, a quorum of both the old and of the new configurations must be available. The final transition happens once (a) the new configuration is activated, and (b) all operations scheduled before the new configuration is activated by the leader are committed. Once (a) and (b) happen, only a quorum of the new configuration is required. Note, however, that neither (a) nor (b) are visible to a client. Specifically, when a reconfiguration operation commits, it only means that an activation message was sent out by the leader. It does not necessarily mean that a quorum of the new configuration got this message (which is required in order to activate it) or that (b) has happened. If one wants to make sure that both (a) and (b) has already occurred (for example, in order to know that it is safe to shut down old servers that were removed), one can simply invoke an update (`set-data`, or some other quorum operation, but not a `sync`) and wait for it to commit. An alternative way to achieve this was to introduce another round to the reconfiguration protocol (which, for simplicity and compatibility with Zab, we decided to avoid). #### Incremental mode The incremental mode allows adding and removing servers to the current configuration. Multiple changes are allowed. For example: > reconfig -remove 3 -add server.5=125.23.63.23:1234:1235;1236 Both the add and the remove options get a list of comma separated arguments (no spaces): > reconfig -remove 3,4 -add server.5=localhost:2111:2112;2113,6=localhost:2114:2115:observer;2116 The format of the server statement is exactly the same as described in the section [Specifying the client port](#sc_reconfig_clientport) and includes the client port. Notice that here instead of "server.5=" you can just say "5=". In the example above, if server 5 is already in the system, but has different ports or is not an observer, it is updated and once the configuration commits becomes an observer and starts using these new ports. This is an easy way to turn participants into observers and vice versa or change any of their ports, without rebooting the server. ZooKeeper supports two types of Quorum Systems – the simple Majority system (where the leader commits operations after receiving ACKs from a majority of voters) and a more complex Hierarchical system, where votes of different servers have different weights and servers are divided into voting groups. Currently, incremental reconfiguration is allowed only if the last proposed configuration known to the leader uses a Majority Quorum System (BadArgumentsException is thrown otherwise). Incremental mode - examples using the Java API: List leavingServers = new ArrayList(); leavingServers.add("1"); leavingServers.add("2"); byte[] config = zk.reconfig(null, leavingServers, null, -1, new Stat()); List leavingServers = new ArrayList(); List joiningServers = new ArrayList(); leavingServers.add("1"); joiningServers.add("server.4=localhost:1234:1235;1236"); byte[] config = zk.reconfig(joiningServers, leavingServers, null, -1, new Stat()); String configStr = new String(config); System.out.println(configStr); There is also an asynchronous API, and an API accepting comma separated Strings instead of List. See src/java/main/org/apache/zookeeper/ZooKeeper.java. #### Non-incremental mode The second mode of reconfiguration is non-incremental, whereby a client gives a complete specification of the new dynamic system configuration. The new configuration can either be given in place or read from a file: > reconfig -file newconfig.cfg //newconfig.cfg is a dynamic config file, see [Dynamic configuration file](#sc_reconfig_file) > reconfig -members server.1=125.23.63.23:2780:2783:participant;2791,server.2=125.23.63.24:2781:2784:participant;2792,server.3=125.23.63.25:2782:2785:participant;2793}} The new configuration may use a different Quorum System. For example, you may specify a Hierarchical Quorum System even if the current ensemble uses a Majority Quorum System. Bulk mode - example using the Java API: List newMembers = new ArrayList(); newMembers.add("server.1=1111:1234:1235;1236"); newMembers.add("server.2=1112:1237:1238;1239"); newMembers.add("server.3=1114:1240:1241:observer;1242"); byte[] config = zk.reconfig(null, null, newMembers, -1, new Stat()); String configStr = new String(config); System.out.println(configStr); There is also an asynchronous API, and an API accepting comma separated String containing the new members instead of List. See src/java/main/org/apache/zookeeper/ZooKeeper.java. #### Conditional reconfig Sometimes (especially in non-incremental mode) a new proposed configuration depends on what the client "believes" to be the current configuration, and should be applied only to that configuration. Specifically, the `reconfig` succeeds only if the last configuration at the leader has the specified version. > reconfig -file -v In the previously listed Java examples, instead of -1 one could specify a configuration version to condition the reconfiguration. #### Error conditions In addition to normal ZooKeeper error conditions, a reconfiguration may fail for the following reasons: 1. another reconfig is currently in progress (ReconfigInProgress) 1. the proposed change would leave the cluster with less than 2 participants, in case standalone mode is enabled, or, if standalone mode is disabled then its legal to remain with 1 or more participants (BadArgumentsException) 1. no quorum of the new configuration was connected and up-to-date with the leader when the reconfiguration processing began (NewConfigNoQuorum) 1. `-v x` was specified, but the version `y` of the latest configuration is not `x` (BadVersionException) 1. an incremental reconfiguration was requested but the last configuration at the leader uses a Quorum System which is different from the Majority system (BadArgumentsException) 1. syntax error (BadArgumentsException) 1. I/O exception when reading the configuration from a file (BadArgumentsException) Most of these are illustrated by test-cases in *ReconfigFailureCases.java*. #### Additional comments **Liveness:** To better understand the difference between incremental and non-incremental reconfiguration, suppose that client C1 adds server D to the system while a different client C2 adds server E. With the non-incremental mode, each client would first invoke `config` to find out the current configuration, and then locally create a new list of servers by adding its own suggested server. The new configuration can then be submitted using the non-incremental `reconfig` command. After both reconfigurations complete, only one of E or D will be added (not both), depending on which client's request arrives second to the leader, overwriting the previous configuration. The other client can repeat the process until its change takes effect. This method guarantees system-wide progress (i.e., for one of the clients), but does not ensure that every client succeeds. To have more control C2 may request to only execute the reconfiguration in case the version of the current configuration hasn't changed, as explained in the section [Conditional reconfig](#sc_reconfig_conditional). In this way it may avoid blindly overwriting the configuration of C1 if C1's configuration reached the leader first. With incremental reconfiguration, both changes will take effect as they are simply applied by the leader one after the other to the current configuration, whatever that is (assuming that the second reconfig request reaches the leader after it sends a commit message for the first reconfig request -- currently the leader will refuse to propose a reconfiguration if another one is already pending). Since both clients are guaranteed to make progress, this method guarantees stronger liveness. In practice, multiple concurrent reconfigurations are probably rare. Non-incremental reconfiguration is currently the only way to dynamically change the Quorum System. Incremental configuration is currently only allowed with the Majority Quorum System. **Changing an observer into a follower:** Clearly, changing a server that participates in voting into an observer may fail if error (2) occurs, i.e., if fewer than the minimal allowed number of participants would remain. However, converting an observer into a participant may sometimes fail for a more subtle reason: Suppose, for example, that the current configuration is (A, B, C, D), where A is the leader, B and C are followers and D is an observer. In addition, suppose that B has crashed. If a reconfiguration is submitted where D is said to become a follower, it will fail with error (3) since in this configuration, a majority of voters in the new configuration (any 3 voters), must be connected and up-to-date with the leader. An observer cannot acknowledge the history prefix sent during reconfiguration, and therefore it does not count towards these 3 required servers and the reconfiguration will be aborted. In case this happens, a client can achieve the same task by two reconfig commands: first invoke a reconfig to remove D from the configuration and then invoke a second command to add it back as a participant (follower). During the intermediate state D is a non-voting follower and can ACK the state transfer performed during the second reconfig command. ## Rebalancing Client Connections When a ZooKeeper cluster is started, if each client is given the same connection string (list of servers), the client will randomly choose a server in the list to connect to, which makes the expected number of client connections per server the same for each of the servers. We implemented a method that preserves this property when the set of servers changes through reconfiguration. See Sections 4 and 5.1 in the [paper](https://www.usenix.org/conference/usenixfederatedconferencesweek/dynamic-recon%EF%AC%81guration-primarybackup-clusters). In order for the method to work, all clients must subscribe to configuration changes (by setting a watch on /zookeeper/config either directly or through the `getConfig` API command). When the watch is triggered, the client should read the new configuration by invoking `sync` and `getConfig` and if the configuration is indeed new invoke the `updateServerList` API command. To avoid mass client migration at the same time, it is better to have each client sleep a random short period of time before invoking `updateServerList`. A few examples can be found in: *StaticHostProviderTest.java* and *TestReconfig.cc* Example (this is not a recipe, but a simplified example just to explain the general idea): public void process(WatchedEvent event) { synchronized (this) { if (event.getType() == EventType.None) { connected = (event.getState() == KeeperState.SyncConnected); notifyAll(); } else if (event.getPath()!=null && event.getPath().equals(ZooDefs.CONFIG_NODE)) { // in prod code never block the event thread! zk.sync(ZooDefs.CONFIG_NODE, this, null); zk.getConfig(this, this, null); } } } public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { if (path!=null && path.equals(ZooDefs.CONFIG_NODE)) { String config[] = ConfigUtils.getClientConfigStr(new String(data)).split(" "); // similar to config -c long version = Long.parseLong(config[0], 16); if (this.configVersion == null){ this.configVersion = version; } else if (version > this.configVersion) { hostList = config[1]; try { // the following command is not blocking but may cause the client to close the socket and // migrate to a different server. In practice it's better to wait a short period of time, chosen // randomly, so that different clients migrate at different times zk.updateServerList(hostList); } catch (IOException e) { System.err.println("Error updating server list"); e.printStackTrace(); } this.configVersion = version; } } } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperSnapshotAndRestore.md0100644 0000000 0000000 00000006533 15051152474 033657 0ustar00rootroot0000000 0000000 # ZooKeeper Snapshot and Restore Guide Zookeeper is designed to withstand machine failures. A Zookeeper cluster can automatically recover from temporary failures such as machine reboot. It can also tolerate up to (N-1)/2 permanent failures for a cluster of N members due to hardware failures or disk corruption, etc. When a member permanently fails, it loses access to the cluster. If the cluster permanently loses more than (N-1)/2 members, it disastrously fails and loses quorum. Once the quorum is lost, the cluster cannot reach consensus and therefore cannot continue to accept updates. To recover from such disastrous failures, Zookeeper provides snapshot and restore functionalities to restore a cluster from a snapshot. 1. Snapshot and restore operate on the connected server via Admin Server APIs 1. Snapshot and restore are rate limited to protect the server from being overloaded 1. Snapshot and restore require authentication and authorization on the root path with ALL permission. The supported auth schemas are digest, x509 and IP. * [Snapshot](#zookeeper_snapshot) * [Restore](#zookeeper_restore) ## Snapshot Recovering a cluster needs a snapshot from a ZooKeeper cluster. Users can periodically take snapshots from a live server which has the highest zxid and stream out data to a local or external storage/file system (e.g., S3). ```bash # The snapshot command takes snapshot from the server it connects to and rate limited to once every 5 mins by default curl -H 'Authorization: digest root:root_passwd' http://hostname:adminPort/commands/snapshot?streaming=true --output snapshotFileName ``` ## Restore Restoring a cluster needs a single snapshot as input stream. Restore can be used for recovering a cluster for quorum lost or building a brand-new cluster with seed data. All members should restore using the same snapshot. The following are the recommended steps: - Blocking traffic on the client port or client secure port before restore starts - Take a snapshot of the latest database state using the snapshot admin server command if applicable - For each server - Moving the files in dataDir and dataLogDir to different location to prevent the restored database from being overwritten when server restarts after restore - Restore the server using restore admin server command - Unblocking traffic on the client port or client secure port after restore completes ```bash # The restore command takes a snapshot as input stream and restore the db of the server it connects. It is rate limited to once every 5 mins by default curl -H 'Content-Type:application/octet-stream' -H 'Authorization: digest root:root_passwd' -POST http://hostname:adminPort/commands/restore --data-binary "@snapshotFileName" ``` apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperStarted.md0100644 0000000 0000000 00000027263 15051152474 031502 0ustar00rootroot0000000 0000000 # ZooKeeper Getting Started Guide * [Getting Started: Coordinating Distributed Applications with ZooKeeper](#getting-started-coordinating-distributed-applications-with-zooKeeper) * [Pre-requisites](#sc_Prerequisites) * [Download](#sc_Download) * [Standalone Operation](#sc_InstallingSingleMode) * [Managing ZooKeeper Storage](#sc_FileManagement) * [Connecting to ZooKeeper](#sc_ConnectingToZooKeeper) * [Programming to ZooKeeper](#sc_ProgrammingToZooKeeper) * [Running Replicated ZooKeeper](#sc_RunningReplicatedZooKeeper) * [Other Optimizations](#other-optimizations) ## 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](zookeeperAdmin.html). ### Pre-requisites See [System Requirements](zookeeperAdmin.html#sc_systemReq) in the Admin guide. ### Download To get a ZooKeeper distribution, download a recent [stable](http://zookeeper.apache.org/releases.html) 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/lib/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 _logback_ -- more detail available in the [Logging](zookeeperProgrammers.html#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 logback 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](#sc_RunningReplicatedZooKeeper). ### Managing ZooKeeper Storage For long running production systems ZooKeeper storage must be managed externally (dataDir and logs). See the section on [maintenance](zookeeperAdmin.html#sc_maintenance) for more details. ### Connecting to ZooKeeper $ bin/zkCli.sh -server 127.0.0.1:2181 This lets you perform simple, file-like operations. Once you have connected, you should see something like: Connecting to localhost:2181 ... 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 -server host:port cmd args addauth scheme auth close config [-c] [-w] [-s] connect host:port create [-s] [-e] [-c] [-t ttl] path [data] [acl] delete [-v version] path deleteall path delquota [-n|-b] path get [-s] [-w] path getAcl [-s] path getAllChildrenNumber path getEphemerals path history listquota path ls [-s] [-w] [-R] path printwatches on|off quit reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*] redo cmdno removewatches path [-c|-d|-a] [-l] set [-s] [-v version] path data setAcl [-s] [-v version] [-R] path acl setquota -n|-b val path stat [-w] path sync 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, see the [Zookeeper CLI](zookeeperCLI.html). ### 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](zookeeperProgrammers.html#ch_programStructureWithExample) for sample code using 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. ######Note >For replicated mode, a minimum of three servers are required, and it is strongly recommended that you have an odd number of servers. If you only have two servers, then you are in a situation where if one of them fails, there are not enough machines to form a majority quorum. Two servers are inherently **less** stable than a single server, because there are two single points of failure. The required **conf/zoo.cfg** file for replicated mode is similar to the one used in standalone mode, but with a few differences. Here is an example: tickTime=2000 dataDir=/var/lib/zookeeper 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 milliseconds 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 _dataDir_s and distinct _clientPort_s are also necessary (in the above replicated example, running on a single _localhost_, you would still have three config files). >Please be aware that setting up multiple servers on a single machine will not create any redundancy. If something were to happen which caused the machine to die, all of the zookeeper servers would be offline. Full redundancy requires that each server have its own machine. It must be a completely separate physical server. Multiple virtual machines on the same physical host are still vulnerable to the complete failure of that host. >If you have multiple network interfaces in your ZooKeeper machines, you can also instruct ZooKeeper to bind on all of your interfaces and automatically switch to a healthy interface in case of a network failure. For details, see the [Configuration Parameters](zookeeperAdmin.html#id_multi_address). ### 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. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperTools.md0100644 0000000 0000000 00000070056 15051152474 031172 0ustar00rootroot0000000 0000000 # A series of tools for ZooKeeper * [Scripts](#Scripts) * [zkServer.sh](#zkServer) * [zkCli.sh](#zkCli) * [zkEnv.sh](#zkEnv) * [zkCleanup.sh](#zkCleanup) * [zkTxnLogToolkit.sh](#zkTxnLogToolkit) * [zkSnapShotToolkit.sh](#zkSnapShotToolkit) * [zkSnapshotRecursiveSummaryToolkit.sh](#zkSnapshotRecursiveSummaryToolkit) * [zkSnapshotComparer.sh](#zkSnapshotComparer) * [Benchmark](#Benchmark) * [YCSB](#YCSB) * [zk-smoketest](#zk-smoketest) * [Testing](#Testing) * [Fault Injection Framework](#fault-injection) * [Byteman](#Byteman) * [Jepsen Test](#jepsen-test) ## Scripts ### zkServer.sh A command for the operations for the ZooKeeper server. ```bash Usage: ./zkServer.sh {start|start-foreground|stop|version|restart|status|upgrade|print-cmd} # start the server ./zkServer.sh start # start the server in the foreground for debugging ./zkServer.sh start-foreground # stop the server ./zkServer.sh stop # restart the server ./zkServer.sh restart # show the status,mode,role of the server ./zkServer.sh status JMX enabled by default Using config: /data/software/zookeeper/conf/zoo.cfg Mode: standalone # Deprecated ./zkServer.sh upgrade # print the parameters of the start-up ./zkServer.sh print-cmd # show the version of the ZooKeeper server ./zkServer.sh version Apache ZooKeeper, version 3.6.0-SNAPSHOT 06/11/2019 05:39 GMT ``` The `status` command establishes a client connection to the server to execute diagnostic commands. When the ZooKeeper cluster is started in client SSL only mode (by omitting the clientPort from the zoo.cfg), then additional SSL related configuration has to be provided before using the `./zkServer.sh status` command to find out if the ZooKeeper server is running. An example: CLIENT_JVMFLAGS="-Dzookeeper.clientCnxnSocket=org.apache.zookeeper.ClientCnxnSocketNetty -Dzookeeper.ssl.trustStore.location=/tmp/clienttrust.jks -Dzookeeper.ssl.trustStore.password=password -Dzookeeper.ssl.keyStore.location=/tmp/client.jks -Dzookeeper.ssl.keyStore.password=password -Dzookeeper.client.secure=true" ./zkServer.sh status ### zkCli.sh Look at the [ZooKeeperCLI](zookeeperCLI.html) ### zkEnv.sh The environment setting for the ZooKeeper server ```bash # the setting of log property ZOO_LOG_DIR: the directory to store the logs ``` ### zkCleanup.sh Clean up the old snapshots and transaction logs. ```bash Usage: * args dataLogDir [snapDir] -n count * dataLogDir -- path to the txn log directory * snapDir -- path to the snapshot directory * count -- the number of old snaps/logs you want to keep, value should be greater than or equal to 3 # Keep the latest 5 logs and snapshots ./zkCleanup.sh -n 5 ``` ### zkTxnLogToolkit.sh TxnLogToolkit is a command line tool shipped with ZooKeeper which is capable of recovering transaction log entries with broken CRC. Running it without any command line parameters or with the `-h,--help` argument, it outputs the following help page: $ bin/zkTxnLogToolkit.sh usage: TxnLogToolkit [-dhrv] txn_log_file_name -d,--dump Dump mode. Dump all entries of the log file. (this is the default) -h,--help Print help message -r,--recover Recovery mode. Re-calculate CRC for broken entries. -v,--verbose Be verbose in recovery mode: print all entries, not just fixed ones. -y,--yes Non-interactive mode: repair all CRC errors without asking The default behaviour is safe: it dumps the entries of the given transaction log file to the screen: (same as using `-d,--dump` parameter) $ bin/zkTxnLogToolkit.sh log.100000001 ZooKeeper Transactional Log File with dbid 0 txnlog format version 2 4/5/18 2:15:58 PM CEST session 0x16295bafcc40000 cxid 0x0 zxid 0x100000001 createSession 30000 CRC ERROR - 4/5/18 2:16:05 PM CEST session 0x16295bafcc40000 cxid 0x1 zxid 0x100000002 closeSession null 4/5/18 2:16:05 PM CEST session 0x16295bafcc40000 cxid 0x1 zxid 0x100000002 closeSession null 4/5/18 2:16:12 PM CEST session 0x26295bafcc90000 cxid 0x0 zxid 0x100000003 createSession 30000 4/5/18 2:17:34 PM CEST session 0x26295bafcc90000 cxid 0x0 zxid 0x200000001 closeSession null 4/5/18 2:17:34 PM CEST session 0x16295bd23720000 cxid 0x0 zxid 0x200000002 createSession 30000 4/5/18 2:18:02 PM CEST session 0x16295bd23720000 cxid 0x2 zxid 0x200000003 create '/andor,#626262,v{s{31,s{'world,'anyone}}},F,1 EOF reached after 6 txns. There's a CRC error in the 2nd entry of the above transaction log file. In **dump** mode, the toolkit only prints this information to the screen without touching the original file. In **recovery** mode (`-r,--recover` flag) the original file still remains untouched and all transactions will be copied over to a new txn log file with ".fixed" suffix. It recalculates CRC values and copies the calculated value, if it doesn't match the original txn entry. By default, the tool works interactively: it asks for confirmation whenever CRC error encountered. $ bin/zkTxnLogToolkit.sh -r log.100000001 ZooKeeper Transactional Log File with dbid 0 txnlog format version 2 CRC ERROR - 4/5/18 2:16:05 PM CEST session 0x16295bafcc40000 cxid 0x1 zxid 0x100000002 closeSession null Would you like to fix it (Yes/No/Abort) ? Answering **Yes** means the newly calculated CRC value will be outputted to the new file. **No** means that the original CRC value will be copied over. **Abort** will abort the entire operation and exits. (In this case the ".fixed" will not be deleted and left in a half-complete state: contains only entries which have already been processed or only the header if the operation was aborted at the first entry.) $ bin/zkTxnLogToolkit.sh -r log.100000001 ZooKeeper Transactional Log File with dbid 0 txnlog format version 2 CRC ERROR - 4/5/18 2:16:05 PM CEST session 0x16295bafcc40000 cxid 0x1 zxid 0x100000002 closeSession null Would you like to fix it (Yes/No/Abort) ? y EOF reached after 6 txns. Recovery file log.100000001.fixed has been written with 1 fixed CRC error(s) The default behaviour of recovery is to be silent: only entries with CRC error get printed to the screen. One can turn on verbose mode with the `-v,--verbose` parameter to see all records. Interactive mode can be turned off with the `-y,--yes` parameter. In this case all CRC errors will be fixed in the new transaction file. ### zkSnapShotToolkit.sh Dump a snapshot file to stdout, showing the detailed information of the each zk-node. ```bash # help ./zkSnapShotToolkit.sh /usr/bin/java USAGE: SnapshotFormatter [-d|-json] snapshot_file -d dump the data for each znode -json dump znode info in json format # show the each zk-node info without data content ./zkSnapShotToolkit.sh /data/zkdata/version-2/snapshot.fa01000186d /zk-latencies_4/session_946 cZxid = 0x00000f0003110b ctime = Wed Sep 19 21:58:22 CST 2018 mZxid = 0x00000f0003110b mtime = Wed Sep 19 21:58:22 CST 2018 pZxid = 0x00000f0003110b cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x00000000000000 dataLength = 100 # [-d] show the each zk-node info with data content ./zkSnapShotToolkit.sh -d /data/zkdata/version-2/snapshot.fa01000186d /zk-latencies2/session_26229 cZxid = 0x00000900007ba0 ctime = Wed Aug 15 20:13:52 CST 2018 mZxid = 0x00000900007ba0 mtime = Wed Aug 15 20:13:52 CST 2018 pZxid = 0x00000900007ba0 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x00000000000000 data = eHh4eHh4eHh4eHh4eA== # [-json] show the each zk-node info with json format ./zkSnapShotToolkit.sh -json /data/zkdata/version-2/snapshot.fa01000186d [[1,0,{"progname":"SnapshotFormatter.java","progver":"0.01","timestamp":1559788148637},[{"name":"\/","asize":0,"dsize":0,"dev":0,"ino":1001},[{"name":"zookeeper","asize":0,"dsize":0,"dev":0,"ino":1002},{"name":"config","asize":0,"dsize":0,"dev":0,"ino":1003},[{"name":"quota","asize":0,"dsize":0,"dev":0,"ino":1004},[{"name":"test","asize":0,"dsize":0,"dev":0,"ino":1005},{"name":"zookeeper_limits","asize":52,"dsize":52,"dev":0,"ino":1006},{"name":"zookeeper_stats","asize":15,"dsize":15,"dev":0,"ino":1007}]]],{"name":"test","asize":0,"dsize":0,"dev":0,"ino":1008}]] ``` ### zkSnapshotRecursiveSummaryToolkit.sh Recursively collect and display child count and data size for a selected node. $./zkSnapshotRecursiveSummaryToolkit.sh USAGE: SnapshotRecursiveSummary snapshot_file: path to the zookeeper snapshot starting_node: the path in the zookeeper tree where the traversal should begin max_depth: defines the depth where the tool still writes to the output. 0 means there is no depth limit, every non-leaf node's stats will be displayed, 1 means it will only contain the starting node's and it's children's stats, 2 ads another level and so on. This ONLY affects the level of details displayed, NOT the calculation. ```bash # recursively collect and display child count and data for the root node and 2 levels below it ./zkSnapshotRecursiveSummaryToolkit.sh /data/zkdata/version-2/snapshot.fa01000186d / 2 / children: 1250511 data: 1952186580 -- /zookeeper -- children: 1 -- data: 0 -- /solr -- children: 1773 -- data: 8419162 ---- /solr/configs ---- children: 1640 ---- data: 8407643 ---- /solr/overseer ---- children: 6 ---- data: 0 ---- /solr/live_nodes ---- children: 3 ---- data: 0 ``` ### zkSnapshotComparer.sh SnapshotComparer is a tool that loads and compares two snapshots with configurable threshold and various filters, and outputs information about the delta. The delta includes specific znode paths added, updated, deleted comparing one snapshot to another. It's useful in use cases that involve snapshot analysis, such as offline data consistency checking, and data trending analysis (e.g. what's growing under which zNode path during when). This tool only outputs information about permanent nodes, ignoring both sessions and ephemeral nodes. It provides two tuning parameters to help filter out noise: 1. `--nodes` Threshold number of children added/removed; 2. `--bytes` Threshold number of bytes added/removed. #### Locate Snapshots Snapshots can be found in [Zookeeper Data Directory](zookeeperAdmin.html#The+Data+Directory) which configured in [conf/zoo.cfg](zookeeperStarted.html#sc_InstallingSingleMode) when set up Zookeeper server. #### Supported Snapshot Formats This tool supports uncompressed snapshot format, and compressed snapshot file formats: `snappy` and `gz`. Snapshots with different formats can be compared using this tool directly without decompression. #### Running the Tool Running the tool with no command line argument or an unrecognized argument, it outputs the following help page: ``` usage: java -cp org.apache.zookeeper.server.SnapshotComparer -b,--bytes (Required) The node data delta size threshold, in bytes, for printing the node. -d,--debug Use debug output. -i,--interactive Enter interactive mode. -l,--left (Required) The left snapshot file. -n,--nodes (Required) The descendant node delta size threshold, in nodes, for printing the node. -r,--right (Required) The right snapshot file. ``` Example Command: ``` ./bin/zkSnapshotComparer.sh -l /zookeeper-data/backup/snapshot.d.snappy -r /zookeeper-data/backup/snapshot.44 -b 2 -n 1 ``` Example Output: ``` ... Deserialized snapshot in snapshot.44 in 0.002741 seconds Processed data tree in 0.000361 seconds Node count: 10 Total size: 0 Max depth: 4 Count of nodes at depth 0: 1 Count of nodes at depth 1: 2 Count of nodes at depth 2: 4 Count of nodes at depth 3: 3 Node count: 22 Total size: 2903 Max depth: 5 Count of nodes at depth 0: 1 Count of nodes at depth 1: 2 Count of nodes at depth 2: 4 Count of nodes at depth 3: 7 Count of nodes at depth 4: 8 Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for depth 0 Node found in both trees. Delta: 2903 bytes, 12 descendants Analysis for depth 1 Node /zk_test found in both trees. Delta: 2903 bytes, 12 descendants Analysis for depth 2 Node /zk_test/gz found in both trees. Delta: 730 bytes, 3 descendants Node /zk_test/snappy found in both trees. Delta: 2173 bytes, 9 descendants Analysis for depth 3 Node /zk_test/gz/12345 found in both trees. Delta: 9 bytes, 1 descendants Node /zk_test/gz/a found only in right tree. Descendant size: 721. Descendant count: 0 Node /zk_test/snappy/anotherTest found in both trees. Delta: 1738 bytes, 2 descendants Node /zk_test/snappy/test_1 found only in right tree. Descendant size: 344. Descendant count: 3 Node /zk_test/snappy/test_2 found only in right tree. Descendant size: 91. Descendant count: 2 Analysis for depth 4 Node /zk_test/gz/12345/abcdef found only in right tree. Descendant size: 9. Descendant count: 0 Node /zk_test/snappy/anotherTest/abc found only in right tree. Descendant size: 1738. Descendant count: 0 Node /zk_test/snappy/test_1/a found only in right tree. Descendant size: 93. Descendant count: 0 Node /zk_test/snappy/test_1/b found only in right tree. Descendant size: 251. Descendant count: 0 Node /zk_test/snappy/test_2/xyz found only in right tree. Descendant size: 33. Descendant count: 0 Node /zk_test/snappy/test_2/y found only in right tree. Descendant size: 58. Descendant count: 0 All layers compared. ``` #### Interactive Mode Use "-i" or "--interactive" to enter interactive mode: ``` ./bin/zkSnapshotComparer.sh -l /zookeeper-data/backup/snapshot.d.snappy -r /zookeeper-data/backup/snapshot.44 -b 2 -n 1 -i ``` There are three options to proceed: ``` - Press enter to move to print current depth layer; - Type a number to jump to and print all nodes at a given depth; - Enter an ABSOLUTE path to print the immediate subtree of a node. Path must start with '/'. ``` Note: As indicated by the interactive messages, the tool only shows analysis on the result that filtered by tuning parameters bytes threshold and nodes threshold. Press enter to print current depth layer: ``` Current depth is 0 Press enter to move to print current depth layer; ... Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for depth 0 Node found in both trees. Delta: 2903 bytes, 12 descendants ``` Type a number to jump to and print all nodes at a given depth: (Jump forward) ``` Current depth is 1 ... Type a number to jump to and print all nodes at a given depth; ... 3 Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for depth 3 Node /zk_test/gz/12345 found in both trees. Delta: 9 bytes, 1 descendants Node /zk_test/gz/a found only in right tree. Descendant size: 721. Descendant count: 0 Filtered node /zk_test/gz/anotherOne of left size 0, right size 0 Filtered right node /zk_test/gz/b of size 0 Node /zk_test/snappy/anotherTest found in both trees. Delta: 1738 bytes, 2 descendants Node /zk_test/snappy/test_1 found only in right tree. Descendant size: 344. Descendant count: 3 Node /zk_test/snappy/test_2 found only in right tree. Descendant size: 91. Descendant count: 2 ``` (Jump back) ``` Current depth is 3 ... Type a number to jump to and print all nodes at a given depth; ... 0 Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for depth 0 Node found in both trees. Delta: 2903 bytes, 12 descendants ``` Out of range depth is handled: ``` Current depth is 1 ... Type a number to jump to and print all nodes at a given depth; ... 10 Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Depth must be in range [0, 4] ``` Enter an ABSOLUTE path to print the immediate subtree of a node: ``` Current depth is 3 ... Enter an ABSOLUTE path to print the immediate subtree of a node. /zk_test Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for node /zk_test Node /zk_test/gz found in both trees. Delta: 730 bytes, 3 descendants Node /zk_test/snappy found in both trees. Delta: 2173 bytes, 9 descendants ``` Invalid path is handled: ``` Current depth is 3 ... Enter an ABSOLUTE path to print the immediate subtree of a node. /non-exist-path Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for node /non-exist-path Path /non-exist-path is neither found in left tree nor right tree. ``` Invalid input is handled: ``` Current depth is 1 - Press enter to move to print current depth layer; - Type a number to jump to and print all nodes at a given depth; - Enter an ABSOLUTE path to print the immediate subtree of a node. Path must start with '/'. 12223999999999999999999999999999999999999 Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Input 12223999999999999999999999999999999999999 is not valid. Depth must be in range [0, 4]. Path must be an absolute path which starts with '/'. ``` Exit interactive mode automatically when all layers are compared: ``` Printing analysis for nodes difference larger than 2 bytes or node count difference larger than 1. Analysis for depth 4 Node /zk_test/gz/12345/abcdef found only in right tree. Descendant size: 9. Descendant count: 0 Node /zk_test/snappy/anotherTest/abc found only in right tree. Descendant size: 1738. Descendant count: 0 Filtered right node /zk_test/snappy/anotherTest/abcd of size 0 Node /zk_test/snappy/test_1/a found only in right tree. Descendant size: 93. Descendant count: 0 Node /zk_test/snappy/test_1/b found only in right tree. Descendant size: 251. Descendant count: 0 Filtered right node /zk_test/snappy/test_1/c of size 0 Node /zk_test/snappy/test_2/xyz found only in right tree. Descendant size: 33. Descendant count: 0 Node /zk_test/snappy/test_2/y found only in right tree. Descendant size: 58. Descendant count: 0 All layers compared. ``` Or use `^c` to exit interactive mode anytime. ## Benchmark ### YCSB #### Quick Start This section describes how to run YCSB on ZooKeeper. #### 1. Start ZooKeeper Server(s) #### 2. Install Java and Maven #### 3. Set Up YCSB Git clone YCSB and compile: git clone http://github.com/brianfrankcooper/YCSB.git # more details in the landing page for instructions on downloading YCSB(https://github.com/brianfrankcooper/YCSB#getting-started). cd YCSB mvn -pl site.ycsb:zookeeper-binding -am clean package -DskipTests #### 4. Provide ZooKeeper Connection Parameters Set connectString, sessionTimeout, watchFlag in the workload you plan to run. - `zookeeper.connectString` - `zookeeper.sessionTimeout` - `zookeeper.watchFlag` * A parameter for enabling ZooKeeper's watch, optional values:true or false.the default value is false. * This parameter cannot test the watch performance, but for testing what effect will take on the read/write requests when enabling the watch. ```bash ./bin/ycsb run zookeeper -s -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p zookeeper.watchFlag=true ``` Or, you can set configs with the shell command, EG: # create a /benchmark namespace for sake of cleaning up the workspace after test. # e.g the CLI:create /benchmark ./bin/ycsb run zookeeper -s -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p zookeeper.sessionTimeout=30000 #### 5. Load data and run tests Load the data: # -p recordcount,the count of records/paths you want to insert ./bin/ycsb load zookeeper -s -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p recordcount=10000 > outputLoad.txt Run the workload test: # YCSB workloadb is the most suitable workload for read-heavy workload for the ZooKeeper in the real world. # -p fieldlength, test the length of value/data-content took effect on performance ./bin/ycsb run zookeeper -s -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p fieldlength=1000 # -p fieldcount ./bin/ycsb run zookeeper -s -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p fieldcount=20 # -p hdrhistogram.percentiles,show the hdrhistogram benchmark result ./bin/ycsb run zookeeper -threads 1 -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p hdrhistogram.percentiles=10,25,50,75,90,95,99,99.9 -p histogram.buckets=500 # -threads: multi-clients test, increase the **maxClientCnxns** in the zoo.cfg to handle more connections. ./bin/ycsb run zookeeper -threads 10 -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark # show the timeseries benchmark result ./bin/ycsb run zookeeper -threads 1 -P workloads/workloadb -p zookeeper.connectString=127.0.0.1:2181/benchmark -p measurementtype=timeseries -p timeseries.granularity=50 # cluster test ./bin/ycsb run zookeeper -P workloads/workloadb -p zookeeper.connectString=192.168.10.43:2181,192.168.10.45:2181,192.168.10.27:2181/benchmark # test leader's read/write performance by setting zookeeper.connectString to leader's(192.168.10.43:2181) ./bin/ycsb run zookeeper -P workloads/workloadb -p zookeeper.connectString=192.168.10.43:2181/benchmark # test for large znode(by default: jute.maxbuffer is 1048575 bytes/1 MB ). Notice:jute.maxbuffer should also be set the same value in all the zk servers. ./bin/ycsb run zookeeper -jvm-args="-Djute.maxbuffer=4194304" -s -P workloads/workloadc -p zookeeper.connectString=127.0.0.1:2181/benchmark # Cleaning up the workspace after finishing the benchmark. # e.g the CLI:deleteall /benchmark ### zk-smoketest **zk-smoketest** provides a simple smoketest client for a ZooKeeper ensemble. Useful for verifying new, updated, existing installations. More details are [here](https://github.com/phunt/zk-smoketest). ## Testing ### Fault Injection Framework #### Byteman - **Byteman** is a tool which makes it easy to trace, monitor and test the behaviour of Java application and JDK runtime code. It injects Java code into your application methods or into Java runtime methods without the need for you to recompile, repackage or even redeploy your application. Injection can be performed at JVM startup or after startup while the application is still running. - Visit the official [website](https://byteman.jboss.org/) to download the latest release - A brief tutorial can be found [here](https://developer.jboss.org/wiki/ABytemanTutorial) ```bash Preparations: # attach the byteman to 3 zk servers during runtime # 55001,55002,55003 is byteman binding port; 714,740,758 is the zk server pid ./bminstall.sh -b -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.verbose -p 55001 714 ./bminstall.sh -b -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.verbose -p 55002 740 ./bminstall.sh -b -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.verbose -p 55003 758 # load the fault injection script ./bmsubmit.sh -p 55002 -l my_zk_fault_injection.btm # unload the fault injection script ./bmsubmit.sh -p 55002 -u my_zk_fault_injectionr.btm ``` Look at the below examples to customize your byteman fault injection script Example 1: This script makes leader's zxid roll over, to force re-election. ```bash cat zk_leader_zxid_roll_over.btm RULE trace zk_leader_zxid_roll_over CLASS org.apache.zookeeper.server.quorum.Leader METHOD propose IF true DO traceln("*** Leader zxid has rolled over, forcing re-election ***"); $1.zxid = 4294967295L ENDRULE ``` Example 2: This script makes the leader drop the ping packet to a specific follower. The leader will close the **LearnerHandler** with that follower, and the follower will enter the state:LOOKING then re-enter the quorum with the state:FOLLOWING ```bash cat zk_leader_drop_ping_packet.btm RULE trace zk_leader_drop_ping_packet CLASS org.apache.zookeeper.server.quorum.LearnerHandler METHOD ping AT ENTRY IF $0.sid == 2 DO traceln("*** Leader drops ping packet to sid: 2 ***"); return; ENDRULE ``` Example 3: This script makes one follower drop ACK packet which has no big effect in the broadcast phrase, since after receiving the majority of ACKs from the followers, the leader can commit that proposal ```bash cat zk_leader_drop_ping_packet.btm RULE trace zk.follower_drop_ack_packet CLASS org.apache.zookeeper.server.quorum.SendAckRequestProcessor METHOD processRequest AT ENTRY IF true DO traceln("*** Follower drops ACK packet ***"); return; ENDRULE ``` ### Jepsen Test A framework for distributed systems verification, with fault injection. Jepsen has been used to verify everything from eventually-consistent commutative databases to linearizable coordination systems to distributed task schedulers. more details can be found in [jepsen-io](https://github.com/jepsen-io/jepsen) Running the [Dockerized Jepsen](https://github.com/jepsen-io/jepsen/blob/master/docker/README.md) is the simplest way to use the Jepsen. Installation: ```bash git clone git@github.com:jepsen-io/jepsen.git cd docker # maybe a long time for the first init. ./up.sh # docker ps to check one control node and five db nodes are up docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8265f1d3f89c docker_control "/bin/sh -c /init.sh" 9 hours ago Up 4 hours 0.0.0.0:32769->8080/tcp jepsen-control 8a646102da44 docker_n5 "/run.sh" 9 hours ago Up 3 hours 22/tcp jepsen-n5 385454d7e520 docker_n1 "/run.sh" 9 hours ago Up 9 hours 22/tcp jepsen-n1 a62d6a9d5f8e docker_n2 "/run.sh" 9 hours ago Up 9 hours 22/tcp jepsen-n2 1485e89d0d9a docker_n3 "/run.sh" 9 hours ago Up 9 hours 22/tcp jepsen-n3 27ae01e1a0c5 docker_node "/run.sh" 9 hours ago Up 9 hours 22/tcp jepsen-node 53c444b00ebd docker_n4 "/run.sh" 9 hours ago Up 9 hours 22/tcp jepsen-n4 ``` Running & Test ```bash # Enter into the container:jepsen-control docker exec -it jepsen-control bash # Test cd zookeeper && lein run test --concurrency 10 # See something like the following to assert that ZooKeeper has passed the Jepsen test INFO [2019-04-01 11:25:23,719] jepsen worker 8 - jepsen.util 8 :ok :read 2 INFO [2019-04-01 11:25:23,722] jepsen worker 3 - jepsen.util 3 :invoke :cas [0 4] INFO [2019-04-01 11:25:23,760] jepsen worker 3 - jepsen.util 3 :fail :cas [0 4] INFO [2019-04-01 11:25:23,791] jepsen worker 1 - jepsen.util 1 :invoke :read nil INFO [2019-04-01 11:25:23,794] jepsen worker 1 - jepsen.util 1 :ok :read 2 INFO [2019-04-01 11:25:24,038] jepsen worker 0 - jepsen.util 0 :invoke :write 4 INFO [2019-04-01 11:25:24,073] jepsen worker 0 - jepsen.util 0 :ok :write 4 ............................................................................... Everything looks good! ヽ(‘ー`)ノ ``` Reference: read [this blog](https://aphyr.com/posts/291-call-me-maybe-zookeeper) to learn more about the Jepsen test for the Zookeeper. apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperTutorial.md0100644 0000000 0000000 00000055613 15051152474 031677 0ustar00rootroot0000000 0000000 # Programming with ZooKeeper - A basic tutorial * [Introduction](#ch_Introduction) * [Barriers](#sc_barriers) * [Producer-Consumer Queues](#sc_producerConsumerQueues) * [Complete example](#Complete+example) * [Queue test](#Queue+test) * [Barrier test](#Barrier+test) * [Source Listing](#sc_sourceListing) ## 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); while (true) { synchronized (mutex) { List 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 responsibility 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 root node). Upon reception of a notification, it checks once more whether the root node has any children. /** * 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 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 structure that groups 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 necessarily 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 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 example In the following section you can find a complete command line application to demonstrate the above mentioned recipes. Use the following command to run it. ZOOBINDIR="[path_to_distro]/bin" . "$ZOOBINDIR"/zkEnv.sh java SyncPrimitive [Test Type] [ZK server] [No of elements] [Client type] ### Queue test Start a producer to create 100 elements java SyncPrimitive qTest localhost 100 p Start a consumer to consume 100 elements java SyncPrimitive qTest localhost 100 c ### Barrier test Start a barrier with 2 participants (start as many times as many participants you'd like to enter) java SyncPrimitive bTest localhost 2 ### Source Listing #### 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); while (true) { synchronized (mutex) { List 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 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 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)); String minNode = list.get(0); for(String s : list){ Integer tempValue = new Integer(s.substring(7)); //System.out.println("Temporary value: " + tempValue); if(tempValue < min) { min = tempValue; minNode = s; } } System.out.println("Temporary value: " + root + "/" + minNode); byte[] b = zk.getData(root + "/" + minNode, false, stat); zk.delete(root + "/" + minNode, 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"); } } apache-zookeeper-3.9.4/zookeeper-docs/src/main/resources/markdown/zookeeperUseCases.md0100644 0000000 0000000 00000065771 15051152474 031615 0ustar00rootroot0000000 0000000 # ZooKeeper Use Cases - Applications and organizations using ZooKeeper include (alphabetically) [1]. - If your use case wants to be listed here. Please do not hesitate, submit a pull request or write an email to **dev@zookeeper.apache.org**, and then, your use case will be included. - If this documentation has violated your intellectual property rights or you and your company's privacy, write an email to **dev@zookeeper.apache.org**, we will handle them in a timely manner. ## Free Software Projects ### [AdroitLogic UltraESB](http://adroitlogic.org/) - Uses ZooKeeper to implement node coordination, in clustering support. This allows the management of the complete cluster, or any specific node - from any other node connected via JMX. A Cluster wide command framework developed on top of the ZooKeeper coordination allows commands that fail on some nodes to be retried etc. We also support the automated graceful round-robin-restart of a complete cluster of nodes using the same framework [1]. ### [Akka](http://akka.io/) - Akka is the platform for the next generation event-driven, scalable and fault-tolerant architectures on the JVM. Or: Akka is a toolkit and runtime for building highly concurrent, distributed, and fault tolerant event-driven applications on the JVM [1]. ### [Eclipse Communication Framework](http://www.eclipse.org/ecf) - The Eclipse ECF project provides an implementation of its Abstract Discovery services using Zookeeper. ECF itself is used in many projects providing base functionallity for communication, all based on OSGi [1]. ### [Eclipse Gyrex](http://www.eclipse.org/gyrex) - The Eclipse Gyrex project provides a platform for building your own Java OSGi based clouds. - ZooKeeper is used as the core cloud component for node membership and management, coordination of jobs executing among workers, a lock service and a simple queue service and a lot more [1]. ### [GoldenOrb](http://www.goldenorbos.org/) - massive-scale Graph analysis [1]. ### [Juju](https://juju.ubuntu.com/) - Service deployment and orchestration framework, formerly called Ensemble [1]. ### [Katta](http://katta.sourceforge.net/) - Katta serves distributed Lucene indexes in a grid environment. - Zookeeper is used for node, master and index management in the grid [1]. ### [KeptCollections](https://github.com/anthonyu/KeptCollections) - KeptCollections is a library of drop-in replacements for the data structures in the Java Collections framework. - KeptCollections uses Apache ZooKeeper as a backing store, thus making its data structures distributed and scalable [1]. ### [Neo4j](https://neo4j.com/) - Neo4j is a Graph Database. It's a disk based, ACID compliant transactional storage engine for big graphs and fast graph traversals, using external indicies like Lucene/Solr for global searches. - We use ZooKeeper in the Neo4j High Availability components for write-master election, read slave coordination and other cool stuff. ZooKeeper is a great and focused project - we like! [1]. ### [Norbert](http://sna-projects.com/norbert) - Partitioned routing and cluster management [1]. ### [spring-cloud-zookeeper](https://spring.io/projects/spring-cloud-zookeeper) - Spring Cloud Zookeeper provides Apache Zookeeper integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms. With a few simple annotations you can quickly enable and configure the common patterns inside your application and build large distributed systems with Zookeeper. The patterns provided include Service Discovery and Distributed Configuration [38]. ### [spring-statemachine](https://projects.spring.io/spring-statemachine/) - Spring Statemachine is a framework for application developers to use state machine concepts with Spring applications. - Spring Statemachine can provide this feature:Distributed state machine based on a Zookeeper [31,32]. ### [spring-xd](https://projects.spring.io/spring-xd/) - Spring XD is a unified, distributed, and extensible system for data ingestion, real time analytics, batch processing, and data export. The project’s goal is to simplify the development of big data applications. - ZooKeeper - Provides all runtime information for the XD cluster. Tracks running containers, in which containers modules and jobs are deployed, stream definitions, deployment manifests, and the like [30,31]. ### [Talend ESB](http://www.talend.com/products-application-integration/application-integration-esb-se.php) - Talend ESB is a versatile and flexible, enterprise service bus. - It uses ZooKeeper as endpoint repository of both REST and SOAP Web services. By using ZooKeeper Talend ESB is able to provide failover and load balancing capabilities in a very light-weight manner [1]. ### [redis_failover](https://github.com/ryanlecompte/redis_failover) - Redis Failover is a ZooKeeper-based automatic master/slave failover solution for Ruby [1]. ## Apache Projects ### [Apache Accumulo](https://accumulo.apache.org/) - Accumulo is a distributed key/value store that provides expressive, cell-level access labels. - Apache ZooKeeper plays a central role within the Accumulo architecture. Its quorum consistency model supports an overall Accumulo architecture with no single points of failure. Beyond that, Accumulo leverages ZooKeeper to store and communication configuration information for users and tables, as well as operational states of processes and tablets [2]. ### [Apache Atlas](http://atlas.apache.org) - Atlas is a scalable and extensible set of core foundational governance services – enabling enterprises to effectively and efficiently meet their compliance requirements within Hadoop and allows integration with the whole enterprise data ecosystem. - Atlas uses Zookeeper for coordination to provide redundancy and high availability of HBase,Kafka [31,35]. ### [Apache BookKeeper](https://bookkeeper.apache.org/) - A scalable, fault-tolerant, and low-latency storage service optimized for real-time workloads. - BookKeeper requires a metadata storage service to store information related to ledgers and available bookies. BookKeeper currently uses ZooKeeper for this and other tasks [3]. ### [Apache CXF DOSGi](http://cxf.apache.org/distributed-osgi.html) - Apache CXF is an open source services framework. CXF helps you build and develop services using frontend programming APIs, like JAX-WS and JAX-RS. These services can speak a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI. - The Distributed OSGi implementation at Apache CXF uses ZooKeeper for its Discovery functionality [4]. ### [Apache Drill](http://drill.apache.org/) - Schema-free SQL Query Engine for Hadoop, NoSQL and Cloud Storage - ZooKeeper maintains ephemeral cluster membership information. The Drillbits use ZooKeeper to find other Drillbits in the cluster, and the client uses ZooKeeper to find Drillbits to submit a query [28]. ### [Apache Druid](https://druid.apache.org/) - Apache Druid is a high performance real-time analytics database. - Apache Druid uses Apache ZooKeeper (ZK) for management of current cluster state. The operations that happen over ZK are [27]: - Coordinator leader election - Segment "publishing" protocol from Historical and Realtime - Segment load/drop protocol between Coordinator and Historical - Overlord leader election - Overlord and MiddleManager task management ### [Apache Dubbo](http://dubbo.apache.org) - Apache Dubbo is a high-performance, java based open source RPC framework. - Zookeeper is used for service registration discovery and configuration management in Dubbo [6]. ### [Apache Flink](https://flink.apache.org/) - Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams. Flink has been designed to run in all common cluster environments, perform computations at in-memory speed and at any scale. - To enable JobManager High Availability you have to set the high-availability mode to zookeeper, configure a ZooKeeper quorum and set up a masters file with all JobManagers hosts and their web UI ports. Flink leverages ZooKeeper for distributed coordination between all running JobManager instances. ZooKeeper is a separate service from Flink, which provides highly reliable distributed coordination via leader election and light-weight consistent state storage [23]. ### [Apache Flume](https://flume.apache.org/) - Flume is a distributed, reliable, and available service for efficiently collecting, aggregating, and moving large amounts of log data. It has a simple and flexible architecture based on streaming data flows. It is robust and fault tolerant with tunable reliability mechanisms and many failover and recovery mechanisms. It uses a simple extensible data model that allows for online analytic application. - Flume supports Agent configurations via Zookeeper. This is an experimental feature [5]. ### [Apache Fluo](https://fluo.apache.org/) - Apache Fluo is a distributed processing system that lets users make incremental updates to large data sets. - Apache Fluo is built on Apache Accumulo which uses Apache Zookeeper for consensus [31,37]. ### [Apache Griffin](https://griffin.apache.org/) - Big Data Quality Solution For Batch and Streaming. - Griffin uses Zookeeper for coordination to provide redundancy and high availability of Kafka [31,36]. ### [Apache Hadoop](http://hadoop.apache.org/) - The Apache Hadoop software library is a framework that allows for the distributed processing of large data sets across clusters of computers using simple programming models. It is designed to scale up from single servers to thousands of machines, each offering local computation and storage. Rather than rely on hardware to deliver high-availability, the library itself is designed to detect and handle failures at the application layer, so delivering a highly-available service on top of a cluster of computers, each of which may be prone to failures. - The implementation of automatic HDFS failover relies on ZooKeeper for the following things: - **Failure detection** - each of the NameNode machines in the cluster maintains a persistent session in ZooKeeper. If the machine crashes, the ZooKeeper session will expire, notifying the other NameNode that a failover should be triggered. - **Active NameNode election** - ZooKeeper provides a simple mechanism to exclusively elect a node as active. If the current active NameNode crashes, another node may take a special exclusive lock in ZooKeeper indicating that it should become the next active. - The ZKFailoverController (ZKFC) is a new component which is a ZooKeeper client which also monitors and manages the state of the NameNode. Each of the machines which runs a NameNode also runs a ZKFC, and that ZKFC is responsible for: - **Health monitoring** - the ZKFC pings its local NameNode on a periodic basis with a health-check command. So long as the NameNode responds in a timely fashion with a healthy status, the ZKFC considers the node healthy. If the node has crashed, frozen, or otherwise entered an unhealthy state, the health monitor will mark it as unhealthy. - **ZooKeeper session management** - when the local NameNode is healthy, the ZKFC holds a session open in ZooKeeper. If the local NameNode is active, it also holds a special “lock†znode. This lock uses ZooKeeper’s support for “ephemeral†nodes; if the session expires, the lock node will be automatically deleted. - **ZooKeeper-based election** - if the local NameNode is healthy, and the ZKFC sees that no other node currently holds the lock znode, it will itself try to acquire the lock. If it succeeds, then it has “won the electionâ€, and is responsible for running a failover to make its local NameNode active. The failover process is similar to the manual failover described above: first, the previous active is fenced if necessary, and then the local NameNode transitions to active state [7]. ### [Apache HBase](https://hbase.apache.org/) - HBase is the Hadoop database. It's an open-source, distributed, column-oriented store model. - HBase uses ZooKeeper for master election, server lease management, bootstrapping, and coordination between servers. A distributed Apache HBase installation depends on a running ZooKeeper cluster. All participating nodes and clients need to be able to access the running ZooKeeper ensemble [8]. - As you can see, ZooKeeper is a fundamental part of HBase. All operations that require coordination, such as Regions assignment, Master-Failover, replication, and snapshots, are built on ZooKeeper [20]. ### [Apache Helix](http://helix.apache.org/) - A cluster management framework for partitioned and replicated distributed resources. - We need a distributed store to maintain the state of the cluster and a notification system to notify if there is any change in the cluster state. Helix uses Apache ZooKeeper to achieve this functionality [21]. Zookeeper provides: - A way to represent PERSISTENT state which remains until its deleted - A way to represent TRANSIENT/EPHEMERAL state which vanishes when the process that created the state dies - A notification mechanism when there is a change in PERSISTENT and EPHEMERAL state ### [Apache Hive](https://hive.apache.org) - The Apache Hive data warehouse software facilitates reading, writing, and managing large datasets residing in distributed storage using SQL. Structure can be projected onto data already in storage. A command line tool and JDBC driver are provided to connect users to Hive. - Hive has been using ZooKeeper as distributed lock manager to support concurrency in HiveServer2 [25,26]. ### [Apache Ignite](https://ignite.apache.org/) - Ignite is a memory-centric distributed database, caching, and processing platform for transactional, analytical, and streaming workloads delivering in-memory speeds at petabyte scale - Apache Ignite discovery mechanism goes with a ZooKeeper implementations which allows scaling Ignite clusters to 100s and 1000s of nodes preserving linear scalability and performance [31,34].​ ### [Apache James Mailbox](http://james.apache.org/mailbox/) - The Apache James Mailbox is a library providing a flexible Mailbox storage accessible by mail protocols (IMAP4, POP3, SMTP,...) and other protocols. - Uses Zookeeper and Curator Framework for generating distributed unique ID's [31]. ### [Apache Kafka](https://kafka.apache.org/) - Kafka is a distributed publish/subscribe messaging system - Apache Kafka relies on ZooKeeper for the following things: - **Controller election** The controller is one of the most important broking entity in a Kafka ecosystem, and it also has the responsibility to maintain the leader-follower relationship across all the partitions. If a node by some reason is shutting down, it’s the controller’s responsibility to tell all the replicas to act as partition leaders in order to fulfill the duties of the partition leaders on the node that is about to fail. So, whenever a node shuts down, a new controller can be elected and it can also be made sure that at any given time, there is only one controller and all the follower nodes have agreed on that. - **Configuration Of Topics** The configuration regarding all the topics including the list of existing topics, the number of partitions for each topic, the location of all the replicas, list of configuration overrides for all topics and which node is the preferred leader, etc. - **Access control lists** Access control lists or ACLs for all the topics are also maintained within Zookeeper. - **Membership of the cluster** Zookeeper also maintains a list of all the brokers that are functioning at any given moment and are a part of the cluster [9]. ### [Apache Kylin](http://kylin.apache.org/) - Apache Kylin is an open source Distributed Analytics Engine designed to provide SQL interface and multi-dimensional analysis (OLAP) on Hadoop/Spark supporting extremely large datasets, original contributed from eBay Inc. - Apache Kylin leverages Zookeeper for job coordination [31,33]. ### [Apache Mesos](http://mesos.apache.org/) - Apache Mesos abstracts CPU, memory, storage, and other compute resources away from machines (physical or virtual), enabling fault-tolerant and elastic distributed systems to easily be built and run effectively. - Mesos has a high-availability mode that uses multiple Mesos masters: one active master (called the leader or leading master) and several backups in case it fails. The masters elect the leader, with Apache ZooKeeper both coordinating the election and handling leader detection by masters, agents, and scheduler drivers [10]. ### [Apache Oozie](https://oozie.apache.org) - Oozie is a workflow scheduler system to manage Apache Hadoop jobs. - the Oozie servers use it for coordinating access to the database and communicating with each other. In order to have full HA, there should be at least 3 ZooKeeper servers [29]. ### [Apache Pulsar](https://pulsar.apache.org) - Apache Pulsar is an open-source distributed pub-sub messaging system originally created at Yahoo and now part of the Apache Software Foundation - Pulsar uses Apache Zookeeper for metadata storage, cluster configuration, and coordination. In a Pulsar instance: - A configuration store quorum stores configuration for tenants, namespaces, and other entities that need to be globally consistent. - Each cluster has its own local ZooKeeper ensemble that stores cluster-specific configuration and coordination such as ownership metadata, broker load reports, BookKeeper ledger metadata, and more [24]. ### [Apache Solr](https://lucene.apache.org/solr/) - Solr is the popular, blazing-fast, open source enterprise search platform built on Apache Lucene. - In the "Cloud" edition (v4.x and up) of enterprise search engine Apache Solr, ZooKeeper is used for configuration, leader election and more [12,13]. ### [Apache Spark](https://spark.apache.org/) - Apache Spark is a unified analytics engine for large-scale data processing. - Utilizing ZooKeeper to provide leader election and some state storage, you can launch multiple Masters in your cluster connected to the same ZooKeeper instance. One will be elected “leader†and the others will remain in standby mode. If the current leader dies, another Master will be elected, recover the old Master’s state, and then resume scheduling [14]. ### [Apache Storm](http://storm.apache.org) - Apache Storm is a free and open source distributed realtime computation system. Apache Storm makes it easy to reliably process unbounded streams of data, doing for realtime processing what Hadoop did for batch processing. Apache Storm is simple, can be used with any programming language, and is a lot of fun to use! - Storm uses Zookeeper for coordinating the cluster [22]. ## Companies ### [AGETO](http://www.ageto.de/) - The AGETO RnD team uses ZooKeeper in a variety of internal as well as external consulting projects [1]. ### [Benipal Technologies](http://www.benipaltechnologies.com/) - ZooKeeper is used for internal application development with Solr and Hadoop with Hbase [1]. ### [Box](http://box.net/) - Box uses ZooKeeper for service discovery, service coordination, Solr and Hadoop support, etc [1]. ### [Deepdyve](http://www.deepdyve.com/) - We do search for research and provide access to high quality content using advanced search technologies Zookeeper is used to manage server state, control index deployment and a myriad other tasks [1]. ### [Facebook](https://www.facebook.com/) - Facebook uses the Zeus ([17,18]) for configuration management which is a forked version of ZooKeeper, with many scalability and performance en- hancements in order to work at the Facebook scale. It runs a consensus protocol among servers distributed across mul- tiple regions for resilience. If the leader fails, a follower is converted into a new leader. ### [Idium Portal](http://www.idium.no/no/idium_portal/) - Idium Portal is a hosted web-publishing system delivered by Norwegian company, Idium AS. - ZooKeeper is used for cluster messaging, service bootstrapping, and service coordination [1]. ### [Makara](http://www.makara.com/) - Using ZooKeeper on 2-node cluster on VMware workstation, Amazon EC2, Zen - Using zkpython - Looking into expanding into 100 node cluster [1]. ### [Midokura](http://www.midokura.com/) - We do virtualized networking for the cloud computing era. We use ZooKeeper for various aspects of our distributed control plane [1]. ### [Pinterest](https://www.pinterest.com/) - Pinterest uses the ZooKeeper for Service discovery and dynamic configuration.Like many large scale web sites, Pinterest’s infrastructure consists of servers that communicate with backend services composed of a number of individual servers for managing load and fault tolerance. Ideally, we’d like the configuration to reflect only the active hosts, so clients don’t need to deal with bad hosts as often. ZooKeeper provides a well known pattern to solve this problem [19]. ### [Rackspace](http://www.rackspace.com/email_hosting) - The Email & Apps team uses ZooKeeper to coordinate sharding and responsibility changes in a distributed e-mail client that pulls and indexes data for search. ZooKeeper also provides distributed locking for connections to prevent a cluster from overwhelming servers [1]. ### [Sematext](http://sematext.com/) - Uses ZooKeeper in SPM (which includes ZooKeeper monitoring component, too!), Search Analytics, and Logsene [1]. ### [Tubemogul](http://tubemogul.com/) - Uses ZooKeeper for leader election, configuration management, locking, group membership [1]. ### [Twitter](https://twitter.com/) - ZooKeeper is used at Twitter as the source of truth for storing critical metadata. It serves as a coordination kernel to provide distributed coordination services, such as leader election and distributed locking. Some concrete examples of ZooKeeper in action include [15,16]: - ZooKeeper is used to store service registry, which is used by Twitter’s naming service for service discovery. - Manhattan (Twitter’s in-house key-value database), Nighthawk (sharded Redis), and Blobstore (in-house photo and video storage), stores its cluster topology information in ZooKeeper. - EventBus, Twitter’s pub-sub messaging system, stores critical metadata in ZooKeeper and uses ZooKeeper for leader election. - Mesos, Twitter’s compute platform, uses ZooKeeper for leader election. ### [Vast.com](http://www.vast.com/) - Used internally as a part of sharding services, distributed synchronization of data/index updates, configuration management and failover support [1]. ### [Wealthfront](http://wealthfront.com/) - Wealthfront uses ZooKeeper for service discovery, leader election and distributed locking among its many backend services. ZK is an essential part of Wealthfront's continuous [deployment infrastructure](http://eng.wealthfront.com/2010/05/02/deployment-infrastructure-for-continuous-deployment/) [1]. ### [Yahoo!](http://www.yahoo.com/) - ZooKeeper is used for a myriad of services inside Yahoo! for doing leader election, configuration management, sharding, locking, group membership etc [1]. ### [Zynga](http://www.zynga.com/) - ZooKeeper at Zynga is used for a variety of services including configuration management, leader election, sharding and more [1]. #### References - [1] https://cwiki.apache.org/confluence/display/ZOOKEEPER/PoweredBy - [2] https://www.youtube.com/watch?v=Ew53T6h9oRw - [3] https://bookkeeper.apache.org/docs/4.7.3/getting-started/concepts/#ledgers - [4] http://cxf.apache.org/dosgi-discovery-demo-page.html - [5] https://flume.apache.org/FlumeUserGuide.html - [6] http://dubbo.apache.org/en-us/blog/dubbo-zk.html - [7] https://hadoop.apache.org/docs/r2.7.1/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html - [8] https://hbase.apache.org/book.html#zookeeper - [9] https://www.cloudkarafka.com/blog/2018-07-04-cloudkarafka_what_is_zookeeper.html - [10] http://mesos.apache.org/documentation/latest/high-availability/ - [11] http://incubator.apache.org/projects/s4.html - [12] https://lucene.apache.org/solr/guide/6_6/using-zookeeper-to-manage-configuration-files.html#UsingZooKeepertoManageConfigurationFiles-StartupBootstrap - [13] https://lucene.apache.org/solr/guide/6_6/setting-up-an-external-zookeeper-ensemble.html - [14] https://spark.apache.org/docs/latest/spark-standalone.html#standby-masters-with-zookeeper - [15] https://blog.twitter.com/engineering/en_us/topics/infrastructure/2018/zookeeper-at-twitter.html - [16] https://blog.twitter.com/engineering/en_us/topics/infrastructure/2018/dynamic-configuration-at-twitter.html - [17] TANG, C., KOOBURAT, T., VENKATACHALAM, P.,CHANDER, A., WEN, Z., NARAYANAN, A., DOWELL,P., AND KARL, R. Holistic Configuration Management at Facebook. In Proceedings of the 25th Symposium on Operating System Principles (SOSP’15) (Monterey, CA,USA, Oct. 2015). - [18] https://www.youtube.com/watch?v=SeZV373gUZc - [19] https://medium.com/@Pinterest_Engineering/zookeeper-resilience-at-pinterest-adfd8acf2a6b - [20] https://blog.cloudera.com/what-are-hbase-znodes/ - [21] https://helix.apache.org/Architecture.html - [22] http://storm.apache.org/releases/current/Setting-up-a-Storm-cluster.html - [23] https://ci.apache.org/projects/flink/flink-docs-release-1.9/ops/jobmanager_high_availability.html - [24] https://pulsar.apache.org/docs/en/concepts-architecture-overview/#metadata-store - [25] https://cwiki.apache.org/confluence/display/Hive/Locking - [26] *ZooKeeperHiveLockManager* implementation in the [hive](https://github.com/apache/hive/) code base - [27] https://druid.apache.org/docs/latest/dependencies/zookeeper.html - [28] https://mapr.com/blog/apache-drill-architecture-ultimate-guide/ - [29] https://oozie.apache.org/docs/4.1.0/AG_Install.html - [30] https://docs.spring.io/spring-xd/docs/current/reference/html/ - [31] https://cwiki.apache.org/confluence/display/CURATOR/Powered+By - [32] https://projects.spring.io/spring-statemachine/ - [33] https://www.tigeranalytics.com/blog/apache-kylin-architecture/ - [34] https://apacheignite.readme.io/docs/cluster-discovery - [35] http://atlas.apache.org/HighAvailability.html - [36] http://griffin.apache.org/docs/usecases.html - [37] https://fluo.apache.org/ - [38] https://spring.io/projects/spring-cloud-zookeeper apache-zookeeper-3.9.4/zookeeper-it/README.txt0100644 0000000 0000000 00000005226 15051152474 021434 0ustar00rootroot0000000 0000000 To run the system test we need to create processing containers that we can spawn tasks, called Instances, in. (how is that for a dangling preposition!?!) Start up InstanceContainers first. Then run the system test. The system test finds the InstanceContainers and directs them through ZooKeeper, so you are going to need an instance of ZooKeeper running that they can all access. The easiest way to do all of this is to use the zookeeper fat jar. Steps to run system test ------------------------ 1) build and transfer the fatjar from the `zookeeper-contrib/zookeeper-contrib-fatjar/target` directory to all systems participating in the test Command to build fatjar without executing the tests: `mvn clean install -P fatjar -DskipTests` 2) run a zookeeper standalone instance (cluster is ok too) e.g. java -jar zookeeper--fatjar.jar server Note: you must provide zoo.cfg, see sample in conf directory 3) on each host start the system test container e.g. java -jar zookeeper--fatjar.jar ic /sysTest name : name of the test container - must be unique typically it's a good idea to name after the host to aid debugging zkHostPort : the host:port of the server from step 2) 4) initiate the system test using the fatjar: java -jar build/contrib/fatjar/zookeeper--fatjar.jar systest org.apache.zookeeper.test.system.SimpleSysTest by default it will access the zk server started in 2) on localhost:2181 or you can specify a remote host:port using -DsysTest.zkHostPort=:,:,... java -DsysTest.zkHostPort=hostA:2181 -jar build/contrib/fatjar/zookeeper--fatjar.jar systest org.apache.zookeeper.test.system.SimpleSysTest where hostA is running the zk server started in step 2) above InstanceContainers can also be used to run a the saturation benchmark. The first two steps are the same as the system test. Step 3 is almost the same: 3) start the InstanceContainer on each host: e.g. java -jar zookeeper--fatjar.jar ic note prefix can be /sysTest or any other path. If you do use /sysTest, make sure the system test isn't running when you run the benchmark. 4) run GenerateLoad using the following java -jar build/contrib/fatjar/zookeeper--fatjar.jar generateLoad #servers #clients Once GenerateLoad is started, it will read commands from stdin. Usually the only command you need to know is "percentage" which sets the percentage of writes to use in the requests. Once a percentage is set, the benchmark will start. "percentage 0" will cause only reads to be issued and "percentage 100" will cause only writes to be issued. apache-zookeeper-3.9.4/zookeeper-it/pom.xml0100755 0000000 0000000 00000005747 15051152474 021266 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-it jar Apache ZooKeeper - Tests ZooKeeper system tests true true true org.apache.zookeeper zookeeper ${project.version} org.apache.zookeeper zookeeper tests test-jar ${project.version} org.junit.vintage junit-vintage-engine org.openjdk.jmh jmh-core 1.23 org.openjdk.jmh jmh-generator-annprocess 1.23 org.apache.maven.plugins maven-surefire-plugin ${project.basedir}/src/main/java/ ${project.build.directory}/classes/ apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/BenchMain.java0100644 0000000 0000000 00000002073 15051152474 031071 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.File; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; public class BenchMain { public static void main(String args[]) throws Exception { org.openjdk.jmh.Main.main(args); } } apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/server/watch/WatchBench.java0100644 0000000 0000000 00000022734 15051152474 033675 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.server.watch.IWatchManager; import org.apache.zookeeper.server.DumbWatcher; import org.openjdk.jmh.annotations.*; import java.util.concurrent.TimeUnit; @Fork(3) public class WatchBench { static final String pathPrefix = "/reasonably/long/path/"; static final EventType event = EventType.NodeDataChanged; static IWatchManager createWatchManager(String className) throws Exception { Class clazz = Class.forName( "org.apache.zookeeper.server.watch." + className); return (IWatchManager) clazz.getConstructor().newInstance(); } static void forceGC() { int gcTimes = 3; for (int i = 0; i < gcTimes; i++) { try { System.gc(); Thread.currentThread().sleep(1000); System.runFinalization(); Thread.currentThread().sleep(1000); } catch (InterruptedException ex) { /* ignore */ } } } static long getMemoryUse() { forceGC(); long totalMem = Runtime.getRuntime().totalMemory(); forceGC(); long freeMem = Runtime.getRuntime().freeMemory(); return totalMem - freeMem; } @State(Scope.Benchmark) public static class IterationState { @Param({"WatchManager", "WatchManagerOptimized"}) public String watchManagerClass; @Param({"10000"}) public int pathCount; String[] paths; long watchesAdded = 0; IWatchManager watchManager; long memWhenSetup = 0; @Setup(Level.Iteration) public void setup() throws Exception { paths = new String[pathCount]; for (int i = 0; i < paths.length; i++) { paths[i] = pathPrefix + i; } watchesAdded = 0; watchManager = createWatchManager(watchManagerClass); memWhenSetup = getMemoryUse(); } @TearDown(Level.Iteration) public void tearDown() { long memUsed = getMemoryUse() - memWhenSetup; System.out.println("Memory used: " + watchesAdded + " " + memUsed); double memPerMillionWatchesMB = memUsed * 1.0 / watchesAdded ; System.out.println( "Memory used per million watches " + String.format("%.2f", memPerMillionWatchesMB) + "MB"); } } /** * Test concenrate watch case where the watcher watches all paths. * * The output of this test will be the average time used to add the * watch to all paths. */ @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @Warmup(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS) public void testAddConcentrateWatch(IterationState state) throws Exception { Watcher watcher = new DumbWatcher(); // watch all paths for (String path : state.paths) { if (state.watchManager.addWatch(path, watcher)) { state.watchesAdded++; } } } @State(Scope.Benchmark) public static class InvocationState { @Param({"WatchManager", "WatchManagerOptimized"}) public String watchManagerClass; @Param({"1", "1000"}) public int pathCount; @Param({"1", "1000"}) public int watcherCount; String[] paths; Watcher[] watchers; IWatchManager watchManager; @Setup(Level.Invocation) public void setup() throws Exception { initialize(); prepare(); } void initialize() throws Exception { if (paths == null || paths.length != pathCount) { paths = new String[pathCount]; for (int i = 0; i < pathCount; i++) { paths[i] = pathPrefix + i; } } if (watchers == null || watchers.length != watcherCount) { watchers = new Watcher[watcherCount]; for (int i = 0; i < watcherCount; i++) { watchers[i] = new DumbWatcher(); } } if (watchManager == null || !watchManager.getClass().getSimpleName().contains( watchManagerClass)) { watchManager = createWatchManager(watchManagerClass); } } void prepare() { for (String path : paths) { for (Watcher watcher : watchers) { watchManager.addWatch(path, watcher); } } } } /** * Test trigger watches in concenrate case. * * The output of this test is the time used to trigger those watches on * all paths. */ @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @Warmup(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS) public void testTriggerConcentrateWatch(InvocationState state) throws Exception { for (String path : state.paths) { state.watchManager.triggerWatch(path, event, WatchedEvent.NO_ZXID, null); } } @State(Scope.Benchmark) public static class AddSparseWatchState extends InvocationState { @Param({"10000"}) public int pathCount; @Param({"10000"}) public int watcherCount; long watchesAdded = 0; long memWhenSetup = 0; @Override public void prepare() { watchesAdded = 0; memWhenSetup = getMemoryUse(); } @TearDown(Level.Invocation) public void tearDown() { long memUsed = getMemoryUse() - memWhenSetup; System.out.println("Memory used: " + watchesAdded + " " + memUsed); double memPerMillionWatchesMB = memUsed * 1.0 / watchesAdded ; System.out.println( "Memory used per million sparse watches " + String.format("%.2f", memPerMillionWatchesMB) + "MB"); // clear all the watches for (String path : paths) { watchManager.triggerWatch(path, event, WatchedEvent.NO_ZXID, null); } } } /** * Test sparse watch case where only one watcher watches all paths, and * only one path being watched by all watchers. * * The output of this test will be the average time used to add those * sparse watches. */ @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @Warmup(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS) public void testAddSparseWatch(AddSparseWatchState state) throws Exception { // All watchers are watching the 1st path for (Watcher watcher : state.watchers) { if (state.watchManager.addWatch(state.paths[0], watcher)) { state.watchesAdded++; } } // The 1st watcher is watching all paths for (String path : state.paths) { if (state.watchManager.addWatch(path, state.watchers[0])) { state.watchesAdded++; } } } @State(Scope.Benchmark) public static class TriggerSparseWatchState extends InvocationState { @Param({"10000"}) public int pathCount; @Param({"10000"}) public int watcherCount; @Override public void prepare() { // All watchers are watching the 1st path for (Watcher watcher : watchers) { watchManager.addWatch(paths[0], watcher); } // The 1st watcher is watching all paths for (String path : paths) { watchManager.addWatch(path, watchers[0]); } } } /** * Test trigger watches in sparse case. * * The output of this test is the time used to trigger those watches on * all paths. */ @Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @Warmup(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 3, time = 10, timeUnit = TimeUnit.SECONDS) public void testTriggerSparseWatch(TriggerSparseWatchState state) throws Exception { for (String path : state.paths) { state.watchManager.triggerWatch(path, event, WatchedEvent.NO_ZXID, null); } } } apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/BaseSysTest.java0100644 0000000 0000000 00000023427 15051152474 033767 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.runner.JUnitCore; @Ignore("No tests in this class.") public class BaseSysTest { private static final File testData = new File( System.getProperty("test.data.dir", "src/test/resources/data")); private static int fakeBasePort = 33222; private static String zkHostPort; protected String prefix = "/sysTest"; ZooKeeper zk; static { try { zkHostPort = System.getProperty("sysTest.zkHostPort", InetAddress.getLocalHost().getCanonicalHostName() + ":2181"); } catch (UnknownHostException e) { e.printStackTrace(); } } InstanceManager im; @Before public void setUp() throws Exception { if (!fakeMachines) { zk = new ZooKeeper(zkHostPort, 15000, new Watcher() {public void process(WatchedEvent e){}}); im = new InstanceManager(zk, prefix); } } @After public void tearDown() throws Exception { if (null != im) { im.close(); } } int serverCount = defaultServerCount; int clientCount = defaultClientCount; static int defaultServerCount = 5; static int defaultClientCount = 7; static { defaultServerCount = Integer.parseInt(System.getProperty("simpleSysTest.defaultServerCount", Integer.toString(defaultServerCount))); defaultClientCount = Integer.parseInt(System.getProperty("simpleSysTest.defaultClientCount", Integer.toString(defaultClientCount))); } String serverHostPort; String quorumHostPort; public String getHostPort() { return serverHostPort; } public int getServerCount() { return serverCount; } public int getClientCount() { return clientCount; } public void startServers() throws IOException { for(int i = 0; i < serverCount; i++) { startServer(i); } } public void stopServers() throws IOException { for(int i = 0; i < serverCount; i++) { stopServer(i); } } public void startClients() throws IOException { for(int i = 0; i < clientCount; i++) { startClient(i); } } public void stopClients() throws IOException { for(int i = 0; i < clientCount; i++) { stopClient(i); } } private static boolean fakeMachines = System.getProperty("baseSysTest.fakeMachines", "no").equals("yes"); public void configureServers(int count) throws Exception { serverCount = count; if (fakeMachines) { fakeConfigureServers(count); } else { distributedConfigureServers(count); } } private void distributedConfigureServers(int count) throws IOException { StringBuilder sbClient = new StringBuilder(); StringBuilder sbServer = new StringBuilder(); try { for(int i = 0; i < count; i++) { String r[] = QuorumPeerInstance.createServer(im, i); if (i > 0) { sbClient.append(','); sbServer.append(','); } sbClient.append(r[0]); // r[0] == "host:clientPort" sbServer.append(r[1]); // r[1] == "host:leaderPort:leaderElectionPort" sbServer.append(";"+(r[0].split(":"))[1]); // Appending ";clientPort" } serverHostPort = sbClient.toString(); quorumHostPort = sbServer.toString(); } catch(Exception e) { IOException ioe = new IOException(e.getMessage()); ioe.setStackTrace(e.getStackTrace()); throw ioe; } } private QuorumPeer qps[]; private File qpsDirs[]; Map peers; private void fakeConfigureServers(int count) throws IOException { peers = new HashMap(); qps = new QuorumPeer[count]; qpsDirs = new File[count]; for(int i = 1; i <= count; i++) { InetSocketAddress peerAddress = new InetSocketAddress("127.0.0.1", fakeBasePort + i); InetSocketAddress electionAddr = new InetSocketAddress("127.0.0.1", serverCount + fakeBasePort + i); peers.put(Long.valueOf(i), new QuorumServer(i, peerAddress, electionAddr)); } StringBuilder sb = new StringBuilder(); for(int i = 0; i < count; i++) { //make that testData exists otherwise it fails on windows testData.mkdirs(); qpsDirs[i] = File.createTempFile("sysTest", ".tmp", testData); qpsDirs[i].delete(); qpsDirs[i].mkdir(); int port = fakeBasePort+10+i; if (sb.length() > 0) { sb.append(','); } sb.append("localhost:"); sb.append(port); } serverHostPort = sb.toString(); } final static int tickTime = 2000; final static int initLimit = 3; final static int syncLimit = 3; final static int connectToLearnerMasterLimit = 3; public void startServer(int index) throws IOException { int port = fakeBasePort+10+index; if (fakeMachines) { qps[index] = new QuorumPeer(peers, qpsDirs[index], qpsDirs[index], port, 3, index+1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); qps[index].start(); } else { try { QuorumPeerInstance.startInstance(im, quorumHostPort, index); } catch(Exception e) { IOException ioe = new IOException(e.getClass().getName() + ": " + e.getMessage()); ioe.setStackTrace(e.getStackTrace()); throw ioe; } } } public void stopServer(int index) throws IOException { if (fakeMachines) { qps[index].shutdown(); } else { try { QuorumPeerInstance.stopInstance(im, index); } catch(Exception e) { IOException ioe = new IOException(e.getMessage()); ioe.setStackTrace(e.getStackTrace()); throw ioe; } } } public void configureClients(int count, Class clazz, String params) throws Exception { clientCount = count; if (fakeMachines) { fakeConfigureClients(count, clazz, params); } else { distributedConfigureClients(count, clazz, params); } } private Class clazz; String params; private void distributedConfigureClients(int count, Class clazz, String params) throws IOException { this.clazz = clazz; this.params = params; } private Instance fakeBaseClients[]; private void fakeConfigureClients(int count, Class clazz, String params) { fakeBaseClients = new Instance[count]; for(int i = 0; i < count; i++) { try { fakeBaseClients[i] = clazz.getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); return; } fakeBaseClients[i].configure(i + " " + params); } } public void startClient(int index) throws IOException { if (fakeMachines) { fakeStartClient(index); } else { distributedStartClient(index); } } private void distributedStartClient(int index) throws IOException { try { im.assignInstance("client" + index, clazz, index + " " + params, 1); } catch (Exception e) { throw new IOException(e.getMessage()); } } private void fakeStartClient(int index) { fakeBaseClients[index].start(); } public void stopClient(int index) throws IOException { if (fakeMachines) { fakeStopClient(index); } else { distributedStopClient(index); } } private void distributedStopClient(int index) throws IOException { try { im.removeInstance("client"+index); } catch (Exception e) { throw new IOException(e.getMessage()); } } private void fakeStopClient(int index) { fakeBaseClients[index].stop(); } static public void main(String args[]) { JUnitCore.main(args); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_D0100644 0000000 0000000 00000000170 15051152474 032636 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/DuplicateNameException.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/DuplicateNameExce0100644 0000000 0000000 00000002016 15051152474 034145 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; public class DuplicateNameException extends Exception { private static final long serialVersionUID = 1L; public DuplicateNameException(String mess) { super(mess); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_G0100644 0000000 0000000 00000000156 15051152474 032645 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/GenerateLoad.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/GenerateLoad.java0100644 0000000 0000000 00000062460 15051152474 034110 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.Set; import org.apache.zookeeper.server.ExitCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.common.Time; public class GenerateLoad { protected static final Logger LOG = LoggerFactory.getLogger(GenerateLoad.class); static ServerSocket ss; static Set slaves = Collections .synchronizedSet(new HashSet()); static Map totalByTime = new HashMap(); volatile static long currentInterval; static long lastChange; static PrintStream sf; static PrintStream tf; static { try { tf = new PrintStream(new FileOutputStream("trace")); } catch (FileNotFoundException e) { e.printStackTrace(); } } static final int INTERVAL = 6000; synchronized static void add(long time, int count, Socket s) { long interval = time / INTERVAL; if (currentInterval == 0 || currentInterval > interval) { LOG.info( "Dropping " + count + " for " + new Date(time) + " " + currentInterval + ">" + interval); return; } // We track totals by seconds Long total = totalByTime.get(interval); if (total == null) { totalByTime.put(interval, (long) count); } else { totalByTime.put(interval, total.longValue() + count); } tf.println(interval + " " + count + " " + s); } synchronized static long remove(long interval) { Long total = totalByTime.remove(interval); return total == null ? -1 : total; } static class SlaveThread extends Thread { Socket s; SlaveThread(Socket s) { setDaemon(true); this.s = s; start(); } public void run() { try { LOG.info("Connected to " + s); BufferedReader is = new BufferedReader(new InputStreamReader(s .getInputStream())); String result; while ((result = is.readLine()) != null) { String timePercentCount[] = result.split(" "); if (timePercentCount.length != 5) { LOG.error("Got " + result + " from " + s + " exitng."); throw new IOException(result); } long time = Long.parseLong(timePercentCount[0]); // int percent = Integer.parseInt(timePercentCount[1]); int count = Integer.parseInt(timePercentCount[2]); int errs = Integer.parseInt(timePercentCount[3]); if (errs > 0) { LOG.error(s + " Got an error! " + errs); } add(time, count, s); } } catch (Exception e) { e.printStackTrace(); } finally { close(); } } void send(int percentage) { try { s.getOutputStream().write((percentage + "\n").getBytes()); } catch (IOException e) { e.printStackTrace(); } } void close() { try { LOG.info("Closing " + s); slaves.remove(this); s.close(); } catch (IOException e) { e.printStackTrace(); } } } static class AcceptorThread extends Thread { AcceptorThread() { setDaemon(true); start(); } public void run() { try { while (true) { Socket s = ss.accept(); LOG.info("Accepted connection from " + s); slaves.add(new SlaveThread(s)); } } catch (IOException e) { e.printStackTrace(); } finally { for (Iterator it = slaves.iterator(); it.hasNext();) { SlaveThread st = it.next(); it.remove(); st.close(); } } } } static class ReporterThread extends Thread { static int percentage; ReporterThread() { setDaemon(true); start(); } public void run() { try { currentInterval = Time.currentElapsedTime() / INTERVAL; // Give things time to report; Thread.sleep(INTERVAL * 2); long min = 99999; long max = 0; long total = 0; int number = 0; while (true) { long now = Time.currentElapsedTime(); long lastInterval = currentInterval; currentInterval += 1; long count = remove(lastInterval); count = count * 1000 / INTERVAL; // Multiply by 1000 to get // reqs/sec if (lastChange != 0 && (lastChange + INTERVAL * 3) < now) { // We only want to print anything if things have had a // chance to change if (count < min) { min = count; } if (count > max) { max = count; } total += count; number++; Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(lastInterval * INTERVAL); String report = lastInterval + " " + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE) + ":" + calendar.get(Calendar.SECOND) + " " + percentage + "% " + count + " " + min + " " + ((double) total / (double) number) + " " + max; LOG.info(report); if (sf != null) { sf.println(report); } } else { max = total = 0; min = 999999999; number = 0; } Thread.sleep(INTERVAL); } } catch (Exception e) { e.printStackTrace(); } } } synchronized static void sendChange(int percentage) { long now = Time.currentElapsedTime(); long start = now; ReporterThread.percentage = percentage; for (SlaveThread st : slaves.toArray(new SlaveThread[0])) { st.send(percentage); } now = Time.currentElapsedTime(); long delay = now - start; if (delay > 1000) { LOG.info("Delay of " + delay + " to send new percentage"); } lastChange = now; } static public class GeneratorInstance implements Instance { byte bytes[]; int percentage = -1; int errors; final Object statSync = new Object(); int finished; int reads; int writes; int rlatency; int wlatency; int outstanding; volatile boolean alive; class ZooKeeperThread extends Thread implements Watcher, DataCallback, StatCallback { String host; ZooKeeperThread(String host) { setDaemon(true); alive = true; this.host = host; start(); } static final int outstandingLimit = 100; synchronized void incOutstanding() throws InterruptedException { outstanding++; while (outstanding > outstandingLimit) { wait(); } } synchronized void decOutstanding() { outstanding--; notifyAll(); } Random r = new Random(); String path; ZooKeeper zk; boolean connected; public void run() { try { zk = new ZooKeeper(host, 60000, this); synchronized (this) { if (!connected) { wait(20000); } } for (int i = 0; i < 300; i++) { try { Thread.sleep(100); path = zk.create("/client", new byte[16], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); break; } catch (KeeperException e) { LOG.error("keeper exception thrown", e); } } if (path == null) { LOG.error("Couldn't create a node in /!"); return; } while (alive) { if (r.nextInt(100) < percentage) { zk.setData(path, bytes, -1, this, System .currentTimeMillis()); } else { zk.getData(path, false, this, System .currentTimeMillis()); } incOutstanding(); } } catch (Exception e) { e.printStackTrace(); } finally { alive = false; try { zk.close(); } catch (InterruptedException e) { e.printStackTrace(); } } } public void process(WatchedEvent event) { LOG.info(event.toString()); synchronized (this) { if (event.getType() == EventType.None) { connected = (event.getState() == KeeperState.SyncConnected); notifyAll(); } } } public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { decOutstanding(); synchronized (statSync) { if (!alive) { return; } if (rc != 0) { LOG.info("Got rc = " + rc); errors++; } else { finished++; rlatency += Time.currentElapsedTime() - (Long) ctx; reads++; } } } public void processResult(int rc, String path, Object ctx, Stat stat) { decOutstanding(); synchronized (statSync) { if (rc != 0) { LOG.info("Got rc = " + rc); errors++; } else { finished++; wlatency += Time.currentElapsedTime() - (Long) ctx; writes++; } } } } class SenderThread extends Thread { Socket s; SenderThread(Socket s) { this.s = s; setDaemon(true); start(); } public void run() { try { OutputStream os = s.getOutputStream(); finished = 0; errors = 0; while (alive) { Thread.sleep(300); if (percentage == -1 || (finished == 0 && errors == 0)) { continue; } String report = Time.currentElapsedTime() + " " + percentage + " " + finished + " " + errors + " " + outstanding + "\n"; /* String subreport = reads + " " + (((double) rlatency) / reads) + " " + writes + " " + (((double) wlatency / writes)); */ synchronized (statSync) { finished = 0; errors = 0; reads = 0; writes = 0; rlatency = 0; wlatency = 0; } os.write(report.getBytes()); } } catch (Exception e) { e.printStackTrace(); } } } Socket s; ZooKeeperThread zkThread; SenderThread sendThread; Reporter r; public void configure(final String params) { LOG.info("Got " + params); new Thread() { public void run() { try { String parts[] = params.split(" "); String hostPort[] = parts[1].split(":"); int bytesSize = 1024; if (parts.length == 3) { try { bytesSize = Integer.parseInt(parts[2]); } catch (Exception e) { LOG.error("Not an integer: " + parts[2]); } } bytes = new byte[bytesSize]; s = new Socket(hostPort[0], Integer.parseInt(hostPort[1])); zkThread = new ZooKeeperThread(parts[0]); sendThread = new SenderThread(s); BufferedReader is = new BufferedReader(new InputStreamReader(s .getInputStream())); String line; while ((line = is.readLine()) != null) { percentage = Integer.parseInt(line); } } catch (Exception e) { e.printStackTrace(); } } }.start(); } public void setReporter(Reporter r) { this.r = r; } public void start() { try { r.report("started"); } catch (Exception e) { e.printStackTrace(); } } public void stop() { alive = false; zkThread.interrupt(); sendThread.interrupt(); try { zkThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } try { sendThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } try { r.report("stopped"); } catch (Exception e) { e.printStackTrace(); } try { s.close(); } catch (IOException e) { e.printStackTrace(); } } } private static class StatusWatcher implements Watcher { volatile boolean connected; public void process(WatchedEvent event) { if (event.getType() == Watcher.Event.EventType.None) { synchronized (this) { connected = event.getState() == Watcher.Event.KeeperState.SyncConnected; notifyAll(); } } } synchronized public boolean waitConnected(long timeout) throws InterruptedException { long endTime = Time.currentElapsedTime() + timeout; while (!connected && Time.currentElapsedTime() < endTime) { wait(endTime - Time.currentElapsedTime()); } return connected; } } private static boolean leaderOnly; private static boolean leaderServes; private static String []processOptions(String args[]) { ArrayList newArgs = new ArrayList(); for(String a: args) { if (a.equals("--leaderOnly")) { leaderOnly = true; leaderServes = true; } else if (a.equals("--leaderServes")) { leaderServes = true; } else { newArgs.add(a); } } return newArgs.toArray(new String[0]); } /** * @param args * @throws InterruptedException * @throws KeeperException * @throws DuplicateNameException * @throws NoAvailableContainers * @throws NoAssignmentException */ public static void main(String[] args) throws InterruptedException, KeeperException, NoAvailableContainers, DuplicateNameException, NoAssignmentException { args = processOptions(args); if (args.length == 5) { try { StatusWatcher statusWatcher = new StatusWatcher(); ZooKeeper zk = new ZooKeeper(args[0], 15000, statusWatcher); if (!statusWatcher.waitConnected(5000)) { LOG.error("Could not connect to " + args[0]); return; } InstanceManager im = new InstanceManager(zk, args[1]); ss = new ServerSocket(0); int port = ss.getLocalPort(); int serverCount = Integer.parseInt(args[2]); int clientCount = Integer.parseInt(args[3]); StringBuilder quorumHostPort = new StringBuilder(); StringBuilder zkHostPort = new StringBuilder(); for (int i = 0; i < serverCount; i++) { String r[] = QuorumPeerInstance.createServer(im, i, leaderServes); if (i > 0) { quorumHostPort.append(','); zkHostPort.append(','); } zkHostPort.append(r[0]); // r[0] == "host:clientPort" quorumHostPort.append(r[1]); // r[1] == "host:leaderPort:leaderElectionPort" quorumHostPort.append(";"+(r[0].split(":"))[1]); // Appending ";clientPort" } for (int i = 0; i < serverCount; i++) { QuorumPeerInstance.startInstance(im, quorumHostPort .toString(), i); } if (leaderOnly) { int tries = 0; outer: while(true) { Thread.sleep(1000); IOException lastException = null; String parts[] = zkHostPort.toString().split(","); for(int i = 0; i < parts.length; i++) { try { String mode = getMode(parts[i]); if (mode.equals("leader")) { zkHostPort = new StringBuilder(parts[i]); LOG.info("Connecting exclusively to " + zkHostPort.toString()); break outer; } } catch(IOException e) { lastException = e; } } if (tries++ > 3) { throw lastException; } } } for (int i = 0; i < clientCount; i++) { im.assignInstance("client" + i, GeneratorInstance.class, zkHostPort.toString() + ' ' + InetAddress.getLocalHost() .getCanonicalHostName() + ':' + port, 1); } new AcceptorThread(); new ReporterThread(); BufferedReader is = new BufferedReader(new InputStreamReader( System.in)); String line; while ((line = is.readLine()) != null) { try { String cmdNumber[] = line.split(" "); if (cmdNumber[0].equals("percentage") && cmdNumber.length > 1) { int number = Integer.parseInt(cmdNumber[1]); if (number < 0 || number > 100) { throw new NumberFormatException( "must be between 0 and 100"); } sendChange(number); } else if (cmdNumber[0].equals("sleep") && cmdNumber.length > 1) { int number = Integer.parseInt(cmdNumber[1]); Thread.sleep(number * 1000); } else if (cmdNumber[0].equals("save") && cmdNumber.length > 1) { sf = new PrintStream(cmdNumber[1]); } else { LOG.error("Commands must be:"); LOG.error("\tpercentage new_write_percentage"); LOG.error("\tsleep seconds_to_sleep"); LOG.error("\tsave file_to_save_output"); } } catch (NumberFormatException e) { LOG.error("Not a valid number: " + e.getMessage()); } } } catch (NumberFormatException e) { doUsage(); } catch (IOException e) { e.printStackTrace(); System.exit(ExitCode.INVALID_INVOCATION.getValue()); } } else { doUsage(); } } private static String getMode(String hostPort) throws NumberFormatException, UnknownHostException, IOException { String parts[] = hostPort.split(":"); Socket s = new Socket(parts[0], Integer.parseInt(parts[1])); s.getOutputStream().write("stat".getBytes()); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line; try { while((line = br.readLine()) != null) { if (line.startsWith("Mode: ")) { return line.substring(6); } } return "unknown"; } finally { s.close(); } } private static void doUsage() { System.err.println("USAGE: " + GenerateLoad.class.getName() + " [--leaderOnly] [--leaderServes] zookeeper_host:port containerPrefix #ofServers #ofClients requestSize"); System.exit(ExitCode.INVALID_INVOCATION.getValue()); } } apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/Instance.java0100644 0000000 0000000 00000003630 15051152474 033314 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import org.apache.zookeeper.KeeperException; /** * This interface is implemented by a class that can be run in an * instance container. * */ public interface Instance { /** * This object is used to report back changes in status. */ interface Reporter { void report(String report) throws KeeperException, InterruptedException; } /** * This will be the first method invoked by the InstanceContainer after * an instance of this interface has been constructed. It will only be * invoked once. * * @param r a handle to use to report on status changes. */ void setReporter(Reporter r); /** * This will be the second method invoked by the InstanceContainer. It * may be invoked again if the configuration changes. * * @param params parameters that were passed to the InstanceManager when * this instance was scheduled. */ void configure(String params); /** * Starts this instance. */ void start(); /** * Stops this instance. */ void stop(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_I0100644 0000000 0000000 00000000163 15051152474 032645 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/InstanceContainer.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/InstanceContainer0100644 0000000 0000000 00000027554 15051152474 034252 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.zookeeper.server.ExitCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.AsyncCallback; 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.ConnectionLossException; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.KeeperException.NodeExistsException; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.system.Instance.Reporter; /** * This class starts up, */ public class InstanceContainer implements Watcher, AsyncCallback.ChildrenCallback { private final class MyWatcher implements Watcher { String myNode; DataCallback dc; MyWatcher(String myNode, DataCallback dc) { this.myNode = myNode; this.dc = dc; } public void process(WatchedEvent event) { if (event.getPath() != null && event.getPath().equals(myNode)) { zk.getData(myNode, this, dc, this); } } } private final class MyDataCallback implements DataCallback { int lastVer; String myNode; Instance myInstance; MyDataCallback(String myNode, Instance myInstance, int ver) { this.myNode = myNode; this.myInstance = myInstance; lastVer = ver; } public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { if (rc == KeeperException.Code.NONODE.intValue()) { // we can just ignore because the child watcher takes care of this return; } if (rc != KeeperException.Code.OK.intValue()) { zk.getData(myNode, (Watcher)ctx, this, ctx); } int currVer = stat.getVersion(); if (currVer != lastVer) { String parts[] = new String(data).split(" ", 2); myInstance.configure(parts[1]); lastVer = currVer; } } } private final class MyReporter implements Reporter { String myReportNode; public MyReporter(String child) { myReportNode = reportsNode + '/' + child; } public void report(String report) throws KeeperException, InterruptedException { for(int j = 0; j < maxTries; j++) { try { try { zk.setData(myReportNode, report.getBytes(), -1); } catch(NoNodeException e) { zk.create(myReportNode, report.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } break; } catch(ConnectionLossException e) {} } } } private static final Logger LOG = LoggerFactory.getLogger(InstanceContainer.class); String name; String zkHostPort; // We only run if the readyNode exists String prefixNode; String statusNode = "available"; String reportsNode = "reports"; String assignmentsNode = "assignments"; ZooKeeper zk; static final int sessTimeout = 5000; static final int maxTries = 3; public InstanceContainer(String name, String zkHostPort, String prefix) throws UnknownHostException { if (name.length() == 0 || name.equals("hostname")) { name = InetAddress.getLocalHost().getCanonicalHostName(); } this.name = name; this.zkHostPort = zkHostPort; this.prefixNode = prefix; this.statusNode = prefix + '/' + this.statusNode + '/' + name; this.reportsNode = prefix + '/' + this.reportsNode; this.assignmentsNode = prefix + '/' + this.assignmentsNode + '/' + name; } private void rmnod(String path) throws InterruptedException, KeeperException { KeeperException lastException = null; for(int i = 0; i < maxTries; i++) { try { zk.delete(path, -1); lastException = null; break; } catch (KeeperException.NoNodeException e) { // cool this is what we want break; } catch (KeeperException e) { lastException = e; } } if (lastException != null) { throw lastException; } } private void mknod_inner(String path, CreateMode mode) throws KeeperException, InterruptedException { for(int i = 0; i < maxTries; i++) { try { zk.create(path, null, Ids.OPEN_ACL_UNSAFE, mode); break; } catch (NodeExistsException e) { if (mode != CreateMode.EPHEMERAL) { return; } Stat stat = zk.exists(path, false); if (stat == null) { continue; } if (stat.getEphemeralOwner() != zk.getSessionId()) { throw e; } break; } catch (ConnectionLossException e) { e.printStackTrace(); } } } private void mknod(String path, CreateMode mode) throws KeeperException, InterruptedException { String subpath[] = path.split("/"); StringBuilder sb = new StringBuilder(); // We start at 1 because / will create an empty part first for(int i = 1; i < subpath.length; i++) { sb.append("/"); sb.append(subpath[i]); CreateMode m = CreateMode.PERSISTENT; if (i == subpath.length-1) { m = mode; } mknod_inner(sb.toString(), m); } } public void run() throws IOException, InterruptedException, KeeperException { zk = new ZooKeeper(zkHostPort, sessTimeout, this); mknod(assignmentsNode, CreateMode.PERSISTENT); mknod(statusNode, CreateMode.EPHEMERAL); mknod(reportsNode, CreateMode.PERSISTENT); // Now we just start watching the assignments directory zk.getChildren(assignmentsNode, true, this, null); } /** * @param args the first parameter is the instance name, the second * is the ZooKeeper spec. if the instance name is the empty string * or "hostname", the hostname will be used. * @throws InterruptedException * @throws IOException * @throws UnknownHostException * @throws KeeperException */ public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException, KeeperException { if (args.length != 3) { System.err.println("USAGE: " + InstanceContainer.class.getName() + " name zkHostPort znodePrefix"); System.exit(ExitCode.INVALID_INVOCATION.getValue()); } new InstanceContainer(args[0], args[1], args[2]).run(); while(true) { Thread.sleep(1000); } } public void process(WatchedEvent event) { if (KeeperState.Expired == event.getState()) { // It's all over LOG.error("Lost session"); System.exit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue()); } if (event.getPath() != null && event.getPath().equals(assignmentsNode)) { // children have changed, so read in the new list zk.getChildren(assignmentsNode, true, this, null); } } Map instances = new HashMap(); @Override public void processResult(int rc, String path, Object ctx, List children) { if (rc != KeeperException.Code.OK.intValue()) { // try it again zk.getChildren(assignmentsNode, true, this, null); return; } Map newList = new HashMap(); // check for differences Stat stat = new Stat(); for(String child: children) { Instance i = instances.remove(child); if (i == null) { // Start up a new instance byte[] data = null; String myNode = assignmentsNode + '/' + child; while(true) { try { data = zk.getData(myNode, true, stat); break; } catch (NoNodeException e) { // The node doesn't exist anymore, so skip it break; } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { return; } } if (data != null) { String instanceSpec = new String(data); int spaceIndex = instanceSpec.indexOf(' '); String clazz; String conf; if (spaceIndex == -1) { clazz = instanceSpec; conf = null; } else { clazz = instanceSpec.substring(0, spaceIndex); conf = instanceSpec.substring(spaceIndex+1); } try { Class c = Class.forName(clazz); i = (Instance) c.getConstructor().newInstance(); Reporter reporter = new MyReporter(child); i.setReporter(reporter); i.configure(conf); i.start(); newList.put(child, i); int ver = stat.getVersion(); Instance myInstance = i; DataCallback dc = new MyDataCallback(myNode, myInstance, ver); Watcher watcher = new MyWatcher(myNode, dc); zk.getData(myNode, watcher, dc, watcher); } catch (Exception e) { LOG.warn("Skipping " + child, e); if (e.getCause() != null) { LOG.warn("Caused by", e.getCause()); } } } } else { // just move it to the new list newList.put(child, i); } } // kill anything that was removed for the children for(Map.Entry i: instances.entrySet()) { i.getValue().stop(); try { rmnod(reportsNode + '/' + i.getKey()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (KeeperException e) { e.printStackTrace(); } } instances = newList; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_I0100644 0000000 0000000 00000000161 15051152474 032643 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/InstanceManager.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/InstanceManager.j0100644 0000000 0000000 00000032642 15051152474 034124 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.AsyncCallback; 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.ConnectionLossException; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.KeeperException.NodeExistsException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.common.Time; /** * This class doles out assignments to InstanceContainers that are registered to * a ZooKeeper znode. The znode will have four child nodes: * * ready: this znode indicates that the InstanceManager is running * * available: the children of this znode are ephemeral nodes representing * running InstanceContainers * * assignments: there will be a child under this znode for each available * InstanceContainer. those znodes will have a child for each * assigned instance * * reports: there will be a child under this znode for each instance that is * running. it will have the report string from the instance. */ public class InstanceManager implements AsyncCallback.ChildrenCallback, Watcher { final private static Logger LOG = LoggerFactory.getLogger(InstanceManager.class); private ZooKeeper zk; private String prefixNode; private String reportsNode = "reports"; private String readyNode = "ready"; private String assignmentsNode = "assignments"; private String statusNode = "available"; private static final int maxTries = 3; private static final class Assigned { String container; int weight; Assigned(String container, int weight) { this.container = container; this.weight = weight; } } private static List preferredList = new ArrayList(); static { String list = System.getProperty("ic.preferredList"); if (list != null) { preferredList = Arrays.asList(list.split(",")); System.err.println("Preferred List: " + preferredList); } else { System.err.println("Preferred List is empty"); } } private Map> assignments = new HashMap>(); private Map instanceToAssignment = new HashMap(); public InstanceManager(ZooKeeper zk, String prefix) throws KeeperException, InterruptedException { this.zk = zk; this.prefixNode = prefix; this.readyNode = prefix + '/' + this.readyNode; this.assignmentsNode = prefix + '/' + this.assignmentsNode; this.reportsNode = prefix + '/' + this.reportsNode; this.statusNode = prefix + '/' + this.statusNode; for(int i = 0; i < maxTries; i++) { try { setupNodes(zk); break; } catch(ConnectionLossException e) {} } ConnectionLossException lastException = null; for(int i = 0; i < maxTries; i++) { try { List children = zk.getChildren(statusNode, this); processResult(0, statusNode, null, children); lastException = null; break; } catch(ConnectionLossException e) { lastException = e; } } if (lastException != null) { throw lastException; } } private void setupNodes(ZooKeeper zk) throws KeeperException, InterruptedException { try { zk.create(prefixNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch(NodeExistsException e) { /* this is ok */ } try { zk.create(assignmentsNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch(NodeExistsException e) { /* this is ok */ } try { zk.create(statusNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch(NodeExistsException e) { /* this is ok */ } try { zk.create(reportsNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch(NodeExistsException e) { /* this is ok */ } try { zk.create(readyNode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch(NodeExistsException e) { /* this is ok */ } } synchronized public void processResult(int rc, String path, Object ctx, List children) { if (rc != KeeperException.Code.OK.intValue()) { zk.getChildren(statusNode, this, this, null); return; } if (LOG.isDebugEnabled()) { LOG.debug("Got " + children + " children from " + path); } Map> newAssignments = new HashMap>(); for(String c: children) { HashSet a = assignments.remove(c); if (a != null) { newAssignments.put(c, a); } else { newAssignments.put(c, new HashSet()); } } // Clean up the dead machines for(String dead: assignments.keySet()) { try { removeInstance(dead); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } assignments = newAssignments; } public void process(WatchedEvent event) { if (event.getPath().equals(statusNode)) { zk.getChildren(statusNode, this, this, null); } } synchronized public String assignInstance(String name, Class clazz, String params, int weight) throws NoAvailableContainers, DuplicateNameException, InterruptedException, KeeperException { if (weight < 1) { // if the weights are not above zero, things will get messed up weight = 1; } String instanceSpec = clazz.getName() + ' ' + params; if (instanceToAssignment.get(name) != null) { throw new DuplicateNameException(name + " already exists"); } // find most idle node String mostIdle = null; int mostIdleWeight = Integer.MAX_VALUE; for(String preferred: preferredList) { HashSet assignmentList = assignments.get(preferred); int w = 0; if (assignmentList != null) { for(Assigned a: assignmentList) { w += a.weight; } if (w < mostIdleWeight) { mostIdleWeight = w; mostIdle = preferred; } } } for(Entry> e: assignments.entrySet()) { int w = 0; for(Assigned a: e.getValue()) { w += a.weight; } if (w < mostIdleWeight) { mostIdleWeight = w; mostIdle = e.getKey(); } } if (mostIdle == null) { throw new NoAvailableContainers("No available containers"); } Assigned a = new Assigned(mostIdle, weight); instanceToAssignment.put(name, a); HashSet as = assignments.get(mostIdle); if (as == null) { as = new HashSet(); assignments.put(mostIdle, as); } as.add(a); KeeperException lastException = null; for(int i = 0; i < maxTries; i++) { try { zk.create(assignmentsNode + '/' + mostIdle + '/' + name, instanceSpec.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); return mostIdle; } catch(NodeExistsException e) { return mostIdle; } catch (KeeperException e) { lastException = e; } } throw lastException; } public void reconfigureInstance(String name, String params) throws NoAssignmentException, InterruptedException, KeeperException { if (LOG.isDebugEnabled()) { LOG.debug("Reconfiguring " + name + " with " + params); } Assigned assigned = instanceToAssignment.get(name); if (assigned == null) { throw new NoAssignmentException(); } KeeperException lastException = null; for(int i = 0; i < maxTries; i++) { try { zk.setData(assignmentsNode + '/' + assigned.container + '/' + name, ("update " + params).getBytes(), -1); break; } catch (ConnectionLossException e) { lastException = e; } } if (lastException != null) { throw lastException; } } private void doDelete(String path) throws InterruptedException, KeeperException { KeeperException lastException = null; for(int i = 0; i < maxTries; i++) { try { zk.delete(path, -1); return; } catch(NoNodeException e) { return; } catch (KeeperException e) { lastException = e; } } throw lastException; } synchronized public void removeInstance(String name) throws InterruptedException, KeeperException { Assigned assigned = instanceToAssignment.remove(name); if (assigned == null) { return; } assignments.get(assigned.container).remove(name); doDelete(assignmentsNode + '/' + assigned.container + '/' + name); doDelete(reportsNode + '/' + name); } synchronized boolean isAlive(String name) { return instanceToAssignment.get(name) != null; } public void resetStatus(String name) throws InterruptedException, KeeperException { KeeperException lastException = null; for(int i = 0; i < maxTries; i++) { try { zk.delete(reportsNode + '/' + name, -1); lastException = null; break; } catch(ConnectionLossException e) { lastException = e; } catch(NoNodeException e) { // great this is what we want! } } if (lastException != null) { throw lastException; } } public String getStatus(String name, long timeout) throws KeeperException, InterruptedException { Stat stat = new Stat(); byte[] data = null; long endTime = Time.currentElapsedTime() + timeout; KeeperException lastException = null; for(int i = 0; i < maxTries && endTime > Time.currentElapsedTime(); i++) { try { data = zk.getData(reportsNode + '/' + name, false, stat); if (LOG.isDebugEnabled()) { LOG.debug("Got Data: " + ((data == null) ? "null" : new String(data))); } lastException = null; break; } catch(ConnectionLossException e) { lastException = e; } catch(NoNodeException e) { final Object eventObj = new Object(); synchronized(eventObj) { // wait for the node to appear Stat eStat = zk.exists(reportsNode + '/' + name, new Watcher() { public void process(WatchedEvent event) { synchronized(eventObj) { eventObj.notifyAll(); } }}); if (eStat == null) { eventObj.wait(endTime - Time.currentElapsedTime()); } } lastException = e; } } if (lastException != null) { throw lastException; } return new String(data); } synchronized public void close() throws InterruptedException { for(String name: instanceToAssignment.keySet().toArray(new String[0])) { try { removeInstance(name); } catch(KeeperException e) { e.printStackTrace(); } } try { doDelete(readyNode); } catch (KeeperException e) { e.printStackTrace(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_N0100644 0000000 0000000 00000000167 15051152474 032656 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/NoAssignmentException.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/NoAssignmentExcep0100644 0000000 0000000 00000001702 15051152474 034220 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; public class NoAssignmentException extends Exception { private static final long serialVersionUID = 1L; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_N0100644 0000000 0000000 00000000167 15051152474 032656 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/NoAvailableContainers.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/NoAvailableContai0100644 0000000 0000000 00000002022 15051152474 034135 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; public class NoAvailableContainers extends Exception { public NoAvailableContainers(String string) { super(string); } private static final long serialVersionUID = 1L; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_Q0100644 0000000 0000000 00000000164 15051152474 032656 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/QuorumPeerInstance.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/QuorumPeerInstanc0100644 0000000 0000000 00000026626 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.test.TestUtils; class QuorumPeerInstance implements Instance { final private static Logger LOG = LoggerFactory.getLogger(QuorumPeerInstance.class); private static final File testData = new File( System.getProperty("test.data.dir", "src/test/resources/data")); private static final int syncLimit = 3; private static final int initLimit = 3; private static final int connectToLearnerMasterLimit = 3; private static final int tickTime = 2000; String serverHostPort; int serverId; Reporter r; QuorumPeer peer; public void setReporter(Reporter r) { this.r = r; } InetSocketAddress clientAddr; InetSocketAddress quorumLeaderAddr; InetSocketAddress quorumLeaderElectionAddr; Map peers; File snapDir, logDir; public QuorumPeerInstance() { try { File tmpFile = File.createTempFile("test", ".dir", testData); File tmpDir = tmpFile.getParentFile(); tmpFile.delete(); File zkDirs = new File(tmpDir, "zktmp.cfg"); logDir = tmpDir; snapDir = tmpDir; Properties p; if (zkDirs.exists()) { p = new Properties(); FileInputStream input = new FileInputStream(zkDirs); try { p.load(input); } finally { input.close(); } } else { p = System.getProperties(); } logDir = new File(p.getProperty("logDir", tmpDir.getAbsolutePath())); snapDir = new File(p.getProperty("snapDir", tmpDir.getAbsolutePath())); logDir = File.createTempFile("zktst", ".dir", logDir); logDir.delete(); logDir.mkdirs(); snapDir = File.createTempFile("zktst", ".dir", snapDir); snapDir.delete(); snapDir.mkdirs(); } catch (IOException e) { e.printStackTrace(); } } public void configure(String params) { if (clientAddr == null) { String parts[] = params.split(" "); // The first time we are configured, it is just to tell // us which machine we are serverId = Integer.parseInt(parts[0]); if (LOG.isDebugEnabled()) { LOG.debug("Setting up server " + serverId); } if (parts.length > 1 && parts[1].equals("false")) { System.setProperty("zookeeper.leaderServes", "no"); } else { System.setProperty("zookeeper.leaderServes", "yes"); } // Let's grab two ports try { ServerSocket ss = new ServerSocket(0, 1, InetAddress.getLocalHost()); clientAddr = (InetSocketAddress) ss.getLocalSocketAddress(); ss.close(); } catch(IOException e) { e.printStackTrace(); } try { ServerSocket ss = new ServerSocket(0, 1, InetAddress.getLocalHost()); quorumLeaderAddr = (InetSocketAddress) ss.getLocalSocketAddress(); ss.close(); } catch(IOException e) { e.printStackTrace(); } try { ServerSocket ss = new ServerSocket(0, 1, InetAddress.getLocalHost()); quorumLeaderElectionAddr = (InetSocketAddress) ss.getLocalSocketAddress(); ss.close(); } catch(IOException e) { e.printStackTrace(); } String report = clientAddr.getHostString() + ':' + clientAddr.getPort() + ',' + quorumLeaderAddr.getHostString() + ':' + quorumLeaderAddr.getPort() + ':' + quorumLeaderElectionAddr.getPort(); try { if (LOG.isDebugEnabled()) { LOG.debug("Reporting " + report); } r.report(report); } catch (Exception e) { e.printStackTrace(); } return; } else { int spaceIndex = params.indexOf(' '); if (spaceIndex == -1) { LOG.warn("looking for host:port,... start|stop, but found " + params); return; } String quorumSpecs = params.substring(0, spaceIndex); String cmd = params.substring(spaceIndex+1); if (LOG.isDebugEnabled()) { LOG.debug("Running command: " + cmd); } if (!cmd.equals("start")) { if (peer != null) { peer.shutdown(); } peer = null; try { for(int i = 0; i < 5; i++) { Thread.sleep(500); try { // Wait until we can't connect new Socket("127.0.0.1", clientAddr.getPort()).close(); } catch(IOException e) { break; } } r.report("stopped"); } catch (Exception e) { LOG.error("Unhandled error", e); } return; } String parts[] = quorumSpecs.split(","); peers = new HashMap(); for(int i = 0; i < parts.length; i++) { // parts[i] == "host:leaderPort:leaderElectionPort;clientPort" String subparts[] = ((parts[i].split(";"))[0]).split(":"); String clientPort = (parts[i].split(";"))[1]; peers.put(Long.valueOf(i), new QuorumServer( i, new InetSocketAddress(subparts[0], Integer.parseInt(subparts[1])), new InetSocketAddress(subparts[0], Integer.parseInt(subparts[2])), new InetSocketAddress(subparts[0], Integer.parseInt(clientPort)))); } try { if (LOG.isDebugEnabled()) { LOG.debug("Starting quorumPeer " + serverId + " on port " + clientAddr.getPort()); } if (peer != null) { LOG.warn("Peer " + serverId + " already started"); return; } System.err.println("SnapDir = " + snapDir + " LogDir = " + logDir); peer = new QuorumPeer(peers, snapDir, logDir, clientAddr.getPort(), 3, serverId, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); peer.start(); for(int i = 0; i < 5; i++) { Thread.sleep(500); try { // Wait until we can connect new Socket("127.0.0.1", clientAddr.getPort()).close(); break; } catch(IOException e) {} } r.report("started"); } catch (Exception e) { LOG.error("Unhandled exception", e); } } } public void start() { } public void stop() { if (LOG.isDebugEnabled()) { LOG.debug("Stopping peer " + serverId); } if (peer != null) { peer.shutdown(); } if (logDir != null) { TestUtils.deleteFileRecursively(logDir); } if (snapDir != null) { TestUtils.deleteFileRecursively(snapDir); } } /** * This method is used to configure a QuorumPeerInstance * * @param im the InstanceManager that will be managing the new instance * @param i the server number to configure (should be zero based) * @throws NoAvailableContainers * @throws DuplicateNameException * @throws InterruptedException * @throws KeeperException */ public static String[] createServer(InstanceManager im, int i) throws NoAvailableContainers, DuplicateNameException, InterruptedException, KeeperException { return createServer(im, i, true); } /** * This method is used to configure a QuorumPeerInstance * * @param im the InstanceManager that will be managing the new instance * @param i the server number to configure (should be zero based) * @param leaderServes if false, the leader will not accept client connections * @throws NoAvailableContainers * @throws DuplicateNameException * @throws InterruptedException * @throws KeeperException */ public static String[] createServer(InstanceManager im, int i, boolean leaderServes) throws NoAvailableContainers, DuplicateNameException, InterruptedException, KeeperException { im.assignInstance("server"+i, QuorumPeerInstance.class, Integer.toString(i) + " " + leaderServes, 50); return im.getStatus("server"+i, 3000).split(","); } /** * Start an instance of the quorumPeer. * @param im the manager of the instance * @param quorumHostPort the comma-separated list of host:port pairs of quorum peers * @param index the zero based index of the server to start. * @throws InterruptedException * @throws KeeperException * @throws NoAssignmentException */ public static void startInstance(InstanceManager im, String quorumHostPort, int index) throws InterruptedException, KeeperException, NoAssignmentException { im.resetStatus("server" + index); im.reconfigureInstance("server"+index, quorumHostPort + " start"); im.getStatus("server" + index, 5000); } /** * Stop an instance of the quorumPeer * @param im the manager of the instance * @param index the zero based index of the server to stop * @throws InterruptedException * @throws KeeperException * @throws NoAssignmentException */ public static void stopInstance(InstanceManager im, int index) throws InterruptedException, KeeperException, NoAssignmentException { im.resetStatus("server" + index); im.reconfigureInstance("server"+index, Integer.toString(index) + " stop"); im.getStatus("server" + index, 3000); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_S0100644 0000000 0000000 00000000156 15051152474 032661 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/SimpleClient.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/SimpleClient.java0100644 0000000 0000000 00000007417 15051152474 034147 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; /** * The client that gets spawned for the SimpleSysTest * */ public class SimpleClient implements Instance, Watcher, AsyncCallback.DataCallback, StringCallback, StatCallback { private static final long serialVersionUID = 1L; String hostPort; ZooKeeper zk; transient int index; transient String myPath; byte[] data; boolean createdEphemeral; public void configure(String params) { String parts[] = params.split(" "); hostPort = parts[1]; this.index = Integer.parseInt(parts[0]); myPath = "/simpleCase/" + index; } public void start() { try { zk = new ZooKeeper(hostPort, 15000, this); zk.getData("/simpleCase", true, this, null); if (null != r) { r.report("Client " + index + " connecting to " + hostPort); } } catch (Exception e) { e.printStackTrace(); } } public void stop() { try { if (zk != null) { zk.close(); } } catch (InterruptedException e) { e.printStackTrace(); } } public void process(WatchedEvent event) { if (event.getPath() != null && event.getPath().equals("/simpleCase")) { zk.getData("/simpleCase", true, this, null); } } public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { if (rc != 0) { zk.getData("/simpleCase", true, this, null); } else { this.data = data; String content = new String(data); if (content.equals("die")) { this.stop(); return; } if (!createdEphemeral) { zk.create(myPath, data, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, this, null); createdEphemeral = true; } else { zk.setData(myPath, data, -1, this, null); } } } public void processResult(int rc, String path, Object ctx, String name) { if (rc != 0) { zk.create(myPath, data, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, this, null); } } public void processResult(int rc, String path, Object ctx, Stat stat) { if (rc != 0) { zk.setData(myPath, data, -1, this, null); } } @Override public String toString() { return SimpleClient.class.getName() + "[" + index + "] using " + hostPort; } Reporter r; public void setReporter(Reporter r) { this.r = r; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-it_src_main_java_org_apache_zookeeper_test_system_S0100644 0000000 0000000 00000000157 15051152474 032662 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/SimpleSysTest.java apache-zookeeper-3.9.4/zookeeper-it/src/main/java/org/apache/zookeeper/test/system/SimpleSysTest.jav0100644 0000000 0000000 00000015144 15051152474 034202 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test.system; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.KeeperException.ConnectionLossException; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.data.Stat; import org.junit.Assert; import org.junit.Test; import org.apache.zookeeper.common.Time; /** * This does a basic system test. It starts up an ensemble of servers and a set of clients. * It makes sure that all the clients come up. It kills off servers while making a change and * then ensures that all clients see the change. And then signals the clients to die and * watches them disappear. * */ public class SimpleSysTest extends BaseSysTest implements Watcher { int maxTries = 10; boolean connected; final private static Logger LOG = LoggerFactory.getLogger(SimpleSysTest.class); synchronized private boolean waitForConnect(ZooKeeper zk, long timeout) throws InterruptedException { connected = (zk.getState() == States.CONNECTED); long end = Time.currentElapsedTime() + timeout; while(!connected && end > Time.currentElapsedTime()) { wait(timeout); connected = (zk.getState() == States.CONNECTED); } return connected; } /** * This test checks the following: * 1) All clients connect successfully * 2) Half of the servers die (assuming odd number) and a write succeeds * 3) All servers are restarted and cluster stays alive * 4) Clients see a change by the server * 5) Clients' ephemeral nodes are cleaned up * * @throws Exception */ @Test public void testSimpleCase() throws Exception { configureServers(serverCount); configureClients(clientCount, SimpleClient.class, getHostPort()); Stat stat = new Stat(); startServers(); LOG.debug("Connecting to " + getHostPort()); ZooKeeper zk = new ZooKeeper(getHostPort(), 15000, this); waitForConnect(zk, 10000); zk.create("/simpleCase", "orig".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); startClients(); // Check that all clients connect properly for(int i = 0; i < getClientCount(); i++) { for(int j = 0; j < maxTries; j++) { try { byte b[] = zk.getData("/simpleCase/" + i, false, stat); Assert.assertEquals("orig", new String(b)); } catch(NoNodeException e) { if (j+1 == maxTries) { Assert.fail("Max tries exceeded on client " + i); } Thread.sleep(1000); } } } // Kill half the servers, make a change, restart the dead // servers, and then bounce the other servers one by one for(int i = 0; i < getServerCount(); i++) { stopServer(i); if (i+1 > getServerCount()/2) { startServer(i); } else if (i+1 == getServerCount()/2) { Assert.assertTrue("Connection didn't recover", waitForConnect(zk, 10000)); try { zk.setData("/simpleCase", "new".getBytes(), -1); } catch(ConnectionLossException e) { Assert.assertTrue("Connection didn't recover", waitForConnect(zk, 10000)); zk.setData("/simpleCase", "new".getBytes(), -1); } for(int j = 0; j < i; j++) { LOG.info("Starting server " + j); startServer(i); } } } Thread.sleep(100); // wait for things to stabilize Assert.assertTrue("Servers didn't bounce", waitForConnect(zk, 15000)); try { zk.getData("/simpleCase", false, stat); } catch(ConnectionLossException e) { Assert.assertTrue("Servers didn't bounce", waitForConnect(zk, 15000)); } // check that the change has propagated to everyone for(int i = 0; i < getClientCount(); i++) { for(int j = 0; j < maxTries; j++) { byte[] data = zk.getData("/simpleCase/" + i, false, stat); if (new String(data).equals("new")) { break; } if (j+1 == maxTries) { Assert.fail("max tries exceeded for " + i); } Thread.sleep(1000); } } // send out the kill signal zk.setData("/simpleCase", "die".getBytes(), -1); // watch for everyone to die for(int i = 0; i < getClientCount(); i++) { try { for(int j = 0; j < maxTries; j++) { zk.getData("/simpleCase/" + i, false, stat); if (j+1 == maxTries) { Assert.fail("max tries exceeded waiting for child " + i + " to die"); } Thread.sleep(200); } } catch(NoNodeException e) { // Great this is what we were hoping for! } } stopClients(); stopServers(); } public void process(WatchedEvent event) { if (event.getState() == KeeperState.SyncConnected) { synchronized(this) { connected = true; notifyAll(); } } else if (event.getState() == KeeperState.Disconnected) { synchronized(this) { connected = false; notifyAll(); } } } } apache-zookeeper-3.9.4/zookeeper-jute/pom.xml0100755 0000000 0000000 00000015062 15051152474 021610 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-jute jar Apache ZooKeeper - Jute ZooKeeper jute org.apache.yetus audience-annotations org.junit.jupiter junit-jupiter-engine test org.codehaus.mojo javacc-maven-plugin 2.6 generate-sources javacc javacc ${project.basedir}/src/main/java/org/apache/jute/compiler/generated/ rcc.jj 2 false ${project.build.directory}/generated-sources/java maven-compiler-plugin pre-compile-jute generate-sources compile org.codehaus.mojo exec-maven-plugin generate-Java-Jute generate-sources exec ${project.build.directory}/generated-sources/java java -classpath org.apache.jute.compiler.generated.Rcc -l java ${project.basedir}/src/main/resources/zookeeper.jute generate-C-Jute generate-sources exec ${project.basedir}/../zookeeper-client/zookeeper-client-c/generated/ java -classpath org.apache.jute.compiler.generated.Rcc -l c ${project.basedir}/src/main/resources/zookeeper.jute org.codehaus.mojo build-helper-maven-plugin jute-as-dependency generate-sources add-source ${basedir}/target/generated-sources/java com.github.spotbugs spotbugs-maven-plugin true org.apache.felix maven-bundle-plugin build bundle package bundle *;resolution:=optional org.apache.zookeeper.data, org.apache.zookeeper.proto, org.apache.zookeeper.txn, !org.apache.zookeeper*, org.apache.jute* ZooKeeper Jute Bundle https://zookeeper.apache.org/doc/current/ ${mvngit.commit.id} !Implementation-Build,* osgi apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/BinaryInputArchive.java0100644 0000000 0000000 00000011637 15051152474 032320 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; /** * */ public class BinaryInputArchive implements InputArchive { public static final String UNREASONBLE_LENGTH = "Unreasonable length = "; // CHECKSTYLE.OFF: ConstantName - for backward compatibility public static final int maxBuffer = Integer.getInteger("jute.maxbuffer", 0xfffff); // CHECKSTYLE.ON: private static final int extraMaxBuffer; static { final Integer configuredExtraMaxBuffer = Integer.getInteger("zookeeper.jute.maxbuffer.extrasize", maxBuffer); if (configuredExtraMaxBuffer < 1024) { // Earlier hard coded value was 1024, So the value should not be less than that value extraMaxBuffer = 1024; } else { extraMaxBuffer = configuredExtraMaxBuffer; } } private final DataInput in; private final int totalBufferSize; public static BinaryInputArchive getArchive(InputStream stream) { return new BinaryInputArchive(new DataInputStream(stream)); } private static class BinaryIndex implements Index { private int n; BinaryIndex(int nelems) { this.n = nelems; } public boolean done() { return (n <= 0); } public void incr() { n--; } } /** * Creates a new instance of BinaryInputArchive. */ public BinaryInputArchive(DataInput in) { this(in, maxBuffer, extraMaxBuffer); } public BinaryInputArchive(DataInput in, int maxBufferSize, int extraMaxBufferSize) { this.in = in; if ((long) maxBufferSize + extraMaxBufferSize > Integer.MAX_VALUE) { this.totalBufferSize = Integer.MAX_VALUE; } else { this.totalBufferSize = maxBufferSize + extraMaxBufferSize; } } public byte readByte(String tag) throws IOException { return in.readByte(); } public boolean readBool(String tag) throws IOException { return in.readBoolean(); } public int readInt(String tag) throws IOException { return in.readInt(); } public long readLong(String tag) throws IOException { return in.readLong(); } public float readFloat(String tag) throws IOException { return in.readFloat(); } public double readDouble(String tag) throws IOException { return in.readDouble(); } public String readString(String tag) throws IOException { int len = in.readInt(); if (len == -1) { return null; } checkLength(len); byte[] b = new byte[len]; in.readFully(b); return new String(b, StandardCharsets.UTF_8); } public byte[] readBuffer(String tag) throws IOException { int len = readInt(tag); if (len == -1) { return null; } checkLength(len); byte[] arr = new byte[len]; in.readFully(arr); return arr; } public void readRecord(Record r, String tag) throws IOException { r.deserialize(this, tag); } public void startRecord(String tag) throws IOException { } public void endRecord(String tag) throws IOException { } public Index startVector(String tag) throws IOException { int len = readInt(tag); if (len == -1) { return null; } return new BinaryIndex(len); } public void endVector(String tag) throws IOException { } public Index startMap(String tag) throws IOException { return new BinaryIndex(readInt(tag)); } public void endMap(String tag) throws IOException { } // Since this is a rough sanity check, add some padding to maxBuffer to // make up for extra fields, etc. (otherwise e.g. clients may be able to // write buffers larger than we can read from disk!) private void checkLength(int len) throws IOException { if (len < 0 || len > totalBufferSize) { throw new IOException(UNREASONBLE_LENGTH + len); } } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/BinaryOutputArchive.java0100644 0000000 0000000 00000011252 15051152474 032512 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.List; import java.util.TreeMap; /** * */ public class BinaryOutputArchive implements OutputArchive { private ByteBuffer bb = ByteBuffer.allocate(1024); private DataOutput out; private long dataSize; public static BinaryOutputArchive getArchive(OutputStream strm) { return new BinaryOutputArchive(new DataOutputStream(strm)); } /** * Creates a new instance of BinaryOutputArchive. */ public BinaryOutputArchive(DataOutput out) { this.out = out; } public void writeByte(byte b, String tag) throws IOException { out.writeByte(b); dataSize += 1; } public void writeBool(boolean b, String tag) throws IOException { out.writeBoolean(b); dataSize += 1; } public void writeInt(int i, String tag) throws IOException { out.writeInt(i); dataSize += 4; } public void writeLong(long l, String tag) throws IOException { out.writeLong(l); dataSize += 8; } public void writeFloat(float f, String tag) throws IOException { out.writeFloat(f); dataSize += 4; } public void writeDouble(double d, String tag) throws IOException { out.writeDouble(d); dataSize += 8; } /** * create our own char encoder to utf8. This is faster * then string.getbytes(UTF8). * * @param s the string to encode into utf8 * @return utf8 byte sequence. */ private ByteBuffer stringToByteBuffer(CharSequence s) { bb.clear(); final int len = s.length(); for (int i = 0; i < len; i++) { if (bb.remaining() < 3) { ByteBuffer n = ByteBuffer.allocate(bb.capacity() << 1); bb.flip(); n.put(bb); bb = n; } char c = s.charAt(i); if (c < 0x80) { bb.put((byte) c); } else if (c < 0x800) { bb.put((byte) (0xc0 | (c >> 6))); bb.put((byte) (0x80 | (c & 0x3f))); } else { bb.put((byte) (0xe0 | (c >> 12))); bb.put((byte) (0x80 | ((c >> 6) & 0x3f))); bb.put((byte) (0x80 | (c & 0x3f))); } } bb.flip(); return bb; } public void writeString(String s, String tag) throws IOException { if (s == null) { writeInt(-1, "len"); return; } ByteBuffer bb = stringToByteBuffer(s); int strLen = bb.remaining(); writeInt(strLen, "len"); out.write(bb.array(), bb.position(), bb.limit()); dataSize += strLen; } public void writeBuffer(byte[] barr, String tag) throws IOException { if (barr == null) { writeInt(-1, "len"); return; } int len = barr.length; writeInt(len, "len"); out.write(barr); dataSize += len; } public void writeRecord(Record r, String tag) throws IOException { r.serialize(this, tag); } public void startRecord(Record r, String tag) throws IOException { } public void endRecord(Record r, String tag) throws IOException { } public void startVector(List v, String tag) throws IOException { if (v == null) { writeInt(-1, tag); return; } writeInt(v.size(), tag); } public void endVector(List v, String tag) throws IOException { } public void startMap(TreeMap v, String tag) throws IOException { writeInt(v.size(), tag); } public void endMap(TreeMap v, String tag) throws IOException { } @Override public long getDataSize() { return dataSize; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/Index.java0100644 0000000 0000000 00000002325 15051152474 027613 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; /** * Interface that acts as an iterator for deserializing maps. * The deserializer returns an instance that the record uses to * read vectors and maps. An example of usage is as follows: * * * Index idx = startVector(...); * while (!idx.done()) { * .... // read element of a vector * idx.incr(); * } * * */ public interface Index { boolean done(); void incr(); } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/InputArchive.java0100644 0000000 0000000 00000003347 15051152474 031152 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; /** * Interface that all the Deserializers have to implement. * */ public interface InputArchive { byte readByte(String tag) throws IOException; boolean readBool(String tag) throws IOException; int readInt(String tag) throws IOException; long readLong(String tag) throws IOException; float readFloat(String tag) throws IOException; double readDouble(String tag) throws IOException; String readString(String tag) throws IOException; byte[] readBuffer(String tag) throws IOException; void readRecord(Record r, String tag) throws IOException; void startRecord(String tag) throws IOException; void endRecord(String tag) throws IOException; Index startVector(String tag) throws IOException; void endVector(String tag) throws IOException; Index startMap(String tag) throws IOException; void endMap(String tag) throws IOException; } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/OutputArchive.java0100644 0000000 0000000 00000003721 15051152474 031347 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; import java.util.List; import java.util.TreeMap; /** * Interface that all the serializers have to implement. * */ public interface OutputArchive { void writeByte(byte b, String tag) throws IOException; void writeBool(boolean b, String tag) throws IOException; void writeInt(int i, String tag) throws IOException; void writeLong(long l, String tag) throws IOException; void writeFloat(float f, String tag) throws IOException; void writeDouble(double d, String tag) throws IOException; void writeString(String s, String tag) throws IOException; void writeBuffer(byte[] buf, String tag) throws IOException; void writeRecord(Record r, String tag) throws IOException; void startRecord(Record r, String tag) throws IOException; void endRecord(Record r, String tag) throws IOException; void startVector(List v, String tag) throws IOException; void endVector(List v, String tag) throws IOException; void startMap(TreeMap v, String tag) throws IOException; void endMap(TreeMap v, String tag) throws IOException; long getDataSize(); } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/Record.java0100644 0000000 0000000 00000002234 15051152474 027761 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; import org.apache.yetus.audience.InterfaceAudience; /** * Interface that is implemented by generated classes. */ @InterfaceAudience.Public public interface Record { void serialize(OutputArchive archive, String tag) throws IOException; void deserialize(InputArchive archive, String tag) throws IOException; } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/RecordReader.java0100644 0000000 0000000 00000004771 15051152474 031114 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; /** * Front-end interface to deserializers. Also acts as a factory * for deserializers. */ public class RecordReader { private static HashMap archiveFactory; private InputArchive archive; static { archiveFactory = new HashMap<>(); try { archiveFactory.put( "binary", BinaryInputArchive.class.getDeclaredMethod("getArchive", InputStream.class)); } catch (SecurityException | NoSuchMethodException ex) { ex.printStackTrace(); } } private static InputArchive createArchive(InputStream in, String format) { Method factory = archiveFactory.get(format); if (factory != null) { Object[] params = {in}; try { return (InputArchive) factory.invoke(null, params); } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) { ex.printStackTrace(); } } return null; } /** * Creates a new instance of RecordReader. * * @param in Stream from which to deserialize a record * @param format Deserialization format ("binary", "xml", or "csv") */ public RecordReader(InputStream in, String format) { archive = createArchive(in, format); } /** * Deserialize a record. * * @param r Record to be deserialized */ public void read(Record r) throws IOException { r.deserialize(archive, ""); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/RecordWriter.java0100644 0000000 0000000 00000005130 15051152474 031154 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; /** * Front-end for serializers. Also serves as a factory for serializers. */ public class RecordWriter { private OutputArchive archive; static HashMap constructFactory() { HashMap factory = new HashMap<>(); try { factory.put( "binary", BinaryOutputArchive.class.getDeclaredMethod("getArchive", OutputStream.class)); } catch (SecurityException | NoSuchMethodException ex) { ex.printStackTrace(); } return factory; } private static HashMap archiveFactory = constructFactory(); private static OutputArchive createArchive(OutputStream out, String format) { Method factory = archiveFactory.get(format); if (factory != null) { Object[] params = {out}; try { return (OutputArchive) factory.invoke(null, params); } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException ex) { ex.printStackTrace(); } } return null; } /** * Creates a new instance of RecordWriter. * * @param out Output stream where the records will be serialized * @param format Serialization format ("binary", "xml", or "csv") */ public RecordWriter(OutputStream out, String format) { archive = createArchive(out, format); } /** * Serialize a record. * * @param r record to be serialized */ public void write(Record r) throws IOException { r.serialize(archive, ""); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/ToStringOutputArchive.java0100644 0000000 0000000 00000014130 15051152474 033035 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.TreeMap; /** * */ public class ToStringOutputArchive implements OutputArchive { private PrintStream stream; private boolean isFirst = true; private long dataSize; private void throwExceptionOnError(String tag) throws IOException { if (stream.checkError()) { throw new IOException("Error serializing " + tag); } } private void printCommaUnlessFirst() { if (!isFirst) { stream.print(","); dataSize += 1; } isFirst = false; } /** * Creates a new instance of ToStringOutputArchive. */ public ToStringOutputArchive(OutputStream out) throws UnsupportedEncodingException { stream = new PrintStream(out, true, "UTF-8"); } public void writeByte(byte b, String tag) throws IOException { writeLong((long) b, tag); } public void writeBool(boolean b, String tag) throws IOException { printCommaUnlessFirst(); String val = b ? "T" : "F"; stream.print(val); dataSize += 1; throwExceptionOnError(tag); } public void writeInt(int i, String tag) throws IOException { writeLong((long) i, tag); } public void writeLong(long l, String tag) throws IOException { printCommaUnlessFirst(); String strValue = String.valueOf(l); stream.print(strValue); dataSize += strValue.length(); throwExceptionOnError(tag); } public void writeFloat(float f, String tag) throws IOException { writeDouble((double) f, tag); } public void writeDouble(double d, String tag) throws IOException { printCommaUnlessFirst(); String strValue = String.valueOf(d); stream.print(strValue); dataSize += strValue.length(); throwExceptionOnError(tag); } public void writeString(String s, String tag) throws IOException { printCommaUnlessFirst(); String strValue = escapeString(s); stream.print(strValue); dataSize += strValue.length(); throwExceptionOnError(tag); } public void writeBuffer(byte[] buf, String tag) throws IOException { printCommaUnlessFirst(); String strValue = escapeBuffer(buf); stream.print(strValue); dataSize += strValue.length(); throwExceptionOnError(tag); } public void writeRecord(Record r, String tag) throws IOException { if (r == null) { return; } r.serialize(this, tag); } public void startRecord(Record r, String tag) throws IOException { if (tag != null && !"".equals(tag)) { printCommaUnlessFirst(); stream.print("s{"); dataSize += 2; isFirst = true; } } public void endRecord(Record r, String tag) throws IOException { if (tag == null || "".equals(tag)) { stream.print("\n"); dataSize += 1; isFirst = true; } else { stream.print("}"); dataSize += 1; isFirst = false; } } public void startVector(List v, String tag) throws IOException { printCommaUnlessFirst(); stream.print("v{"); dataSize += 2; isFirst = true; } public void endVector(List v, String tag) throws IOException { stream.print("}"); dataSize += 1; isFirst = false; } public void startMap(TreeMap v, String tag) throws IOException { printCommaUnlessFirst(); stream.print("m{"); dataSize += 2; isFirst = true; } public void endMap(TreeMap v, String tag) throws IOException { stream.print("}"); dataSize += 1; isFirst = false; } @Override public long getDataSize() { return dataSize; } private static String escapeString(String s) { if (s == null) { return ""; } StringBuilder sb = new StringBuilder(s.length() + 1); sb.append('\''); int len = s.length(); for (int i = 0; i < len; i++) { char c = s.charAt(i); switch (c) { case '\0': sb.append("%00"); break; case '\n': sb.append("%0A"); break; case '\r': sb.append("%0D"); break; case ',': sb.append("%2C"); break; case '}': sb.append("%7D"); break; case '%': sb.append("%25"); break; default: sb.append(c); } } return sb.toString(); } private static String escapeBuffer(byte[] barr) { if (barr == null || barr.length == 0) { return ""; } StringBuilder sb = new StringBuilder(barr.length + 1); sb.append('#'); for (byte b : barr) { sb.append(Integer.toHexString(b)); } return sb.toString(); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/Utils.java0100644 0000000 0000000 00000002630 15051152474 027643 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; /** * Various utility functions for Hadoop record I/O runtime. */ public class Utils { /** * Cannot create a new instance of Utils. */ private Utils() { super(); } public static int compareBytes(byte[] b1, int off1, int len1, byte[] b2, int off2, int len2) { int i; for (i = 0; i < len1 && i < len2; i++) { if (b1[off1 + i] != b2[off2 + i]) { return b1[off1 + i] < b2[off2 + i] ? -1 : 1; } } if (len1 != len2) { return len1 < len2 ? -1 : 1; } return 0; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/CGenerator.java0100644 0000000 0000000 00000013460 15051152474 032411 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Iterator; import java.util.List; /** * C++ Code generator front-end for Hadoop record I/O. */ class CGenerator { private String mName; private List mInclFiles; private List mRecList; private final File outputDirectory; /** * Creates a new instance of CppGenerator. * * @param name possibly full pathname to the file * @param ilist included files (as JFile) * @param rlist List of records defined within this file * @param outputDirectory */ CGenerator(String name, List ilist, List rlist, File outputDirectory) { this.outputDirectory = outputDirectory; mName = (new File(name)).getName(); mInclFiles = ilist; mRecList = rlist; } /** * Generate C++ code. This method only creates the requested file(s) * and spits-out file-level elements (such as include statements etc.) * record-level code is generated by JRecord. */ void genCode() throws IOException { if (!outputDirectory.exists()) { if (!outputDirectory.mkdirs()) { throw new IOException("unable to create output directory " + outputDirectory); } } try (FileWriter c = new FileWriter(new File(outputDirectory, mName + ".c")); FileWriter h = new FileWriter(new File(outputDirectory, mName + ".h")); ) { h.write("/**\n"); h.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); h.write("* or more contributor license agreements. See the NOTICE file\n"); h.write("* distributed with this work for additional information\n"); h.write("* regarding copyright ownership. The ASF licenses this file\n"); h.write("* to you under the Apache License, Version 2.0 (the\n"); h.write("* \"License\"); you may not use this file except in compliance\n"); h.write("* with the License. You may obtain a copy of the License at\n"); h.write("*\n"); h.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); h.write("*\n"); h.write("* Unless required by applicable law or agreed to in writing, software\n"); h.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); h.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); h.write("* See the License for the specific language governing permissions and\n"); h.write("* limitations under the License.\n"); h.write("*/\n"); h.write("\n"); c.write("/**\n"); c.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); c.write("* or more contributor license agreements. See the NOTICE file\n"); c.write("* distributed with this work for additional information\n"); c.write("* regarding copyright ownership. The ASF licenses this file\n"); c.write("* to you under the Apache License, Version 2.0 (the\n"); c.write("* \"License\"); you may not use this file except in compliance\n"); c.write("* with the License. You may obtain a copy of the License at\n"); c.write("*\n"); c.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); c.write("*\n"); c.write("* Unless required by applicable law or agreed to in writing, software\n"); c.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); c.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); c.write("* See the License for the specific language governing permissions and\n"); c.write("* limitations under the License.\n"); c.write("*/\n"); c.write("\n"); h.write("#ifndef __" + mName.toUpperCase().replace('.', '_') + "__\n"); h.write("#define __" + mName.toUpperCase().replace('.', '_') + "__\n"); h.write("#include \"recordio.h\"\n"); for (Iterator i = mInclFiles.iterator(); i.hasNext(); ) { JFile f = i.next(); h.write("#include \"" + f.getName() + ".h\"\n"); } // required for compilation from C++ h.write("\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"); c.write("#include \n"); // need it for calloc() & free() c.write("#include \"" + mName + ".h\"\n\n"); for (Iterator i = mRecList.iterator(); i.hasNext(); ) { JRecord jr = i.next(); jr.genCCode(h, c); } h.write("\n#ifdef __cplusplus\n}\n#endif\n\n"); h.write("#endif //" + mName.toUpperCase().replace('.', '_') + "__\n"); } } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/CSharpGenerator.java0100644 0000000 0000000 00000003535 15051152474 033411 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; import java.io.File; import java.io.IOException; import java.util.List; /** * */ public class CSharpGenerator { private List mRecList; private final File outputDirectory; /** * Creates a new instance of CSharpGenerator. * * @param name possibly full pathname to the file * @param ilist included files (as JFile) * @param rlist List of records defined within this file * @param outputDirectory */ CSharpGenerator(String name, List ilist, List rlist, File outputDirectory) { this.outputDirectory = outputDirectory; mRecList = rlist; } /** * Generate C# code. This method only creates the requested file(s) * and spits-out file-level elements (such as include statements etc.) * record-level code is generated by JRecord. */ void genCode() throws IOException { for (JRecord rec : mRecList) { rec.genCsharpCode(outputDirectory); } } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/CppGenerator.java0100644 0000000 0000000 00000013143 15051152474 032747 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Iterator; import java.util.List; /** * C++ Code generator front-end for Hadoop record I/O. */ class CppGenerator { private String mName; private List mInclFiles; private List mRecList; private final File outputDirectory; /** * Creates a new instance of CppGenerator. * * @param name possibly full pathname to the file * @param ilist included files (as JFile) * @param rlist List of records defined within this file * @param outputDirectory */ CppGenerator(String name, List ilist, List rlist, File outputDirectory) { this.outputDirectory = outputDirectory; mName = (new File(name)).getName(); mInclFiles = ilist; mRecList = rlist; } /** * Generate C++ code. This method only creates the requested file(s) * and spits-out file-level elements (such as include statements etc.) * record-level code is generated by JRecord. */ void genCode() throws IOException { if (!outputDirectory.exists()) { if (!outputDirectory.mkdirs()) { throw new IOException("unable to create output directory " + outputDirectory); } } try (FileWriter cc = new FileWriter(new File(outputDirectory, mName + ".cc")); FileWriter hh = new FileWriter(new File(outputDirectory, mName + ".hh")); ) { hh.write("/**\n"); hh.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); hh.write("* or more contributor license agreements. See the NOTICE file\n"); hh.write("* distributed with this work for additional information\n"); hh.write("* regarding copyright ownership. The ASF licenses this file\n"); hh.write("* to you under the Apache License, Version 2.0 (the\n"); hh.write("* \"License\"); you may not use this file except in compliance\n"); hh.write("* with the License. You may obtain a copy of the License at\n"); hh.write("*\n"); hh.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); hh.write("*\n"); hh.write("* Unless required by applicable law or agreed to in writing, software\n"); hh.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); hh.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); hh.write("* See the License for the specific language governing permissions and\n"); hh.write("* limitations under the License.\n"); hh.write("*/\n"); hh.write("\n"); cc.write("/**\n"); cc.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); cc.write("* or more contributor license agreements. See the NOTICE file\n"); cc.write("* distributed with this work for additional information\n"); cc.write("* regarding copyright ownership. The ASF licenses this file\n"); cc.write("* to you under the Apache License, Version 2.0 (the\n"); cc.write("* \"License\"); you may not use this file except in compliance\n"); cc.write("* with the License. You may obtain a copy of the License at\n"); cc.write("*\n"); cc.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); cc.write("*\n"); cc.write("* Unless required by applicable law or agreed to in writing, software\n"); cc.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); cc.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); cc.write("* See the License for the specific language governing permissions and\n"); cc.write("* limitations under the License.\n"); cc.write("*/\n"); cc.write("\n"); hh.write("#ifndef __" + mName.toUpperCase().replace('.', '_') + "__\n"); hh.write("#define __" + mName.toUpperCase().replace('.', '_') + "__\n"); hh.write("#include \"recordio.hh\"\n"); for (Iterator i = mInclFiles.iterator(); i.hasNext(); ) { JFile f = i.next(); hh.write("#include \"" + f.getName() + ".hh\"\n"); } cc.write("#include \"" + mName + ".hh\"\n"); for (Iterator i = mRecList.iterator(); i.hasNext(); ) { JRecord jr = i.next(); jr.genCppCode(hh, cc); } hh.write("#endif //" + mName.toUpperCase().replace('.', '_') + "__\n"); } } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JBoolean.java0100644 0000000 0000000 00000003266 15051152474 032054 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JBoolean extends JType { /** * Creates a new instance of JBoolean. */ public JBoolean() { super("int32_t", "bool", "bool", "boolean", "Bool", "Boolean", "bool", "toBoolean"); } public String getSignature() { return "z"; } public String genJavaCompareTo(String fname) { return " ret = (" + fname + " == peer." + fname + ")? 0 : (" + fname + "?1:-1);\n"; } public String genJavaHashCode(String fname) { return " ret = java.lang.Boolean.hashCode(" + fname + ");\n"; } String genCsharpHashCode(String fname) { return " ret = (" + capitalize(fname) + ")?0:1;\n"; } String genCsharpCompareTo(String name) { return " ret = (" + capitalize(name) + " == peer." + capitalize(name) + ")? 0 : (" + capitalize(name) + "?1:-1);\n"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JBuffer.java0100644 0000000 0000000 00000010247 15051152474 031703 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JBuffer extends JCompType { /** * Creates a new instance of JBuffer. */ public JBuffer() { super("struct buffer", " ::std::string", "byte[]", "byte[]", "Buffer", "byte[]", "byte[]"); } public String genCppGetSet(String fname, int fIdx) { String cgetFunc = " virtual const " + getCppType() + "& get" + fname + "() const {\n"; cgetFunc += " return m" + fname + ";\n"; cgetFunc += " }\n"; String getFunc = " virtual " + getCppType() + "& get" + fname + "() {\n"; getFunc += " bs_.set(" + fIdx + ");return m" + fname + ";\n"; getFunc += " }\n"; return cgetFunc + getFunc; } public String getSignature() { return "B"; } public String genJavaReadWrapper(String fname, String tag, boolean decl) { String ret = ""; if (decl) { ret = " byte[] " + fname + ";\n"; } return ret + " " + fname + "=a_.readBuffer(\"" + tag + "\");\n"; } public String genJavaWriteWrapper(String fname, String tag) { return " a_.writeBuffer(" + fname + ",\"" + tag + "\");\n"; } public String genJavaCompareTo(String fname, String other) { StringBuilder sb = new StringBuilder(); sb.append(" {\n"); sb.append(" byte[] my = " + fname + ";\n"); sb.append(" byte[] ur = " + other + ";\n"); sb.append(" ret = org.apache.jute.Utils.compareBytes(my,0,my.length,ur,0,ur.length);\n"); sb.append(" }\n"); return sb.toString(); } public String genJavaCompareTo(String fname) { return genJavaCompareTo(fname, "peer." + fname); } public String genJavaCompareToWrapper(String fname, String other) { return " " + genJavaCompareTo(fname, other); } public String genJavaEquals(String fname, String peer) { return " ret = java.util.Arrays.equals(" + fname + "," + peer + ");\n"; } public String genJavaHashCode(String fname) { return " ret = java.util.Arrays.hashCode(" + fname + ");\n"; } public String genJavaSlurpBytes(String b, String s, String l) { StringBuilder sb = new StringBuilder(); sb.append(" {\n"); sb.append(" int i = org.apache.jute.Utils.readVInt(" + b + ", " + s + ");\n"); sb.append(" int z = WritableUtils.getVIntSize(i);\n"); sb.append(" " + s + " += z+i; " + l + " -= (z+i);\n"); sb.append(" }\n"); return sb.toString(); } public String genJavaCompareBytes() { StringBuilder sb = new StringBuilder(); sb.append(" {\n"); sb.append(" int i1 = org.apache.jute.Utils.readVInt(b1, s1);\n"); sb.append(" int i2 = org.apache.jute.Utils.readVInt(b2, s2);\n"); sb.append(" int z1 = WritableUtils.getVIntSize(i1);\n"); sb.append(" int z2 = WritableUtils.getVIntSize(i2);\n"); sb.append(" s1+=z1; s2+=z2; l1-=z1; l2-=z2;\n"); sb.append(" int r1 = org.apache.jute.Utils.compareBytes(b1,s1,l1,b2,s2,l2);\n"); sb.append(" if (r1 != 0) { return (r1<0)?-1:0; }\n"); sb.append(" s1+=i1; s2+=i2; l1-=i1; l1-=i2;\n"); sb.append(" }\n"); return sb.toString(); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JByte.java0100644 0000000 0000000 00000002137 15051152474 031374 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JByte extends JType { /** * Creates a new instance of JByte. */ public JByte() { super("char", "int8_t", "byte", "byte", "Byte", "Byte", "byte", "toByte"); } public String getSignature() { return "b"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JCompType.java0100644 0000000 0000000 00000005205 15051152474 032230 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * Abstract base class for all the "compound" types such as ustring, * buffer, vector, map, and record. */ abstract class JCompType extends JType { /** * Creates a new instance of JCompType. */ JCompType( String cType, String cppType, String csharpType, String javaType, String suffix, String wrapper, String csharpWrapper ) { super(cType, cppType, csharpType, javaType, suffix, wrapper, csharpWrapper, null); } String genCppGetSet(String fname, int fIdx) { String cgetFunc = " virtual const " + getCppType() + "& get" + fname + "() const {\n"; cgetFunc += " return m" + fname + ";\n"; cgetFunc += " }\n"; String getFunc = " virtual " + getCppType() + "& get" + fname + "() {\n"; getFunc += " bs_.set(" + fIdx + ");return m" + fname + ";\n"; getFunc += " }\n"; return cgetFunc + getFunc; } String genJavaCompareTo(String fname) { return " ret = " + fname + ".compareTo(peer." + fname + ");\n"; } String genJavaEquals(String fname, String peer) { return " ret = " + fname + ".equals(" + peer + ");\n"; } String genJavaHashCode(String fname) { return " ret = " + fname + ".hashCode();\n"; } String genCsharpHashCode(String fname) { return " ret = " + capitalize(fname) + ".GetHashCode();\n"; } String genCsharpEquals(String name, String peer) { String[] peerSplit = peer.split("\\."); return " ret = " + capitalize(name) + ".Equals(" + peerSplit[0] + "." + capitalize(peerSplit[1]) + ");\n"; } String genCsharpCompareTo(String name) { return " ret = " + capitalize(name) + ".CompareTo(peer." + capitalize(name) + ");\n"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JDouble.java0100644 0000000 0000000 00000002365 15051152474 031706 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JDouble extends JType { /** * Creates a new instance of JDouble. */ public JDouble() { super("double", "double", "double", "double", "Double", "Double", "double", "toDouble"); } public String getSignature() { return "d"; } public String genJavaHashCode(String fname) { return " ret = java.lang.Double.hashCode(" + fname + ");\n"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JField.java0100644 0000000 0000000 00000007120 15051152474 031511 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JField { private JType mType; private String mName; /** * Creates a new instance of JField. */ public JField(JType type, String name) { mType = type; mName = name; } public String getSignature() { return mType.getSignature(); } public String genCppDecl() { return mType.genCppDecl(mName); } public String genCDecl() { return mType.genCDecl(mName); } public String genCsharpDecl() { return mType.genCsharpDecl(mName); } public String genCsharpConstructorParam(String fname) { return mType.genCsharpConstructorParam(fname); } public String genJavaDecl() { return mType.genJavaDecl(mName); } public String genJavaConstructorParam(String fname) { return mType.genJavaConstructorParam(fname); } public String getName() { return mName; } public String getCsharpName() { return "Id".equals(mName) ? "ZKId" : mName; } public String getTag() { return mName; } public JType getType() { return mType; } public String genCppGetSet(int fIdx) { return mType.genCppGetSet(mName, fIdx); } public String genCsharpConstructorSet(String fname) { return mType.genCsharpConstructorSet(mName, fname); } public String genCsharpGetSet(int fIdx) { return mType.genCsharpGetSet(getCsharpName(), fIdx); } public String genCsharpWriteMethodName() { return mType.genCsharpWriteMethod(getCsharpName(), getTag()); } public String genCsharpReadMethodName() { return mType.genCsharpReadMethod(getCsharpName(), getTag()); } public String genCsharpCompareTo() { return mType.genCsharpCompareTo(getCsharpName()); } public String genCsharpEquals() { return mType.genCsharpEquals(getCsharpName(), "peer." + getCsharpName()); } public String genCsharpHashCode() { return mType.genCsharpHashCode(getCsharpName()); } public String genJavaGetSet(int fIdx) { return mType.genJavaGetSet(mName, fIdx); } public String genJavaWriteMethodName() { return mType.genJavaWriteMethod(getName(), getTag()); } public String genJavaReadMethodName() { return mType.genJavaReadMethod(getName(), getTag()); } public String genJavaCompareTo() { return mType.genJavaCompareTo(getName()); } public String genJavaEquals() { return mType.genJavaEquals(getName(), "peer." + getName()); } public String genJavaHashCode() { return mType.genJavaHashCode(getName()); } public String genJavaConstructorSet(String fname) { return mType.genJavaConstructorSet(mName, fname); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JFile.java0100644 0000000 0000000 00000005622 15051152474 031352 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Container for the Hadoop Record DDL. * The main components of the file are filename, list of included files, * and records defined in that file. */ public class JFile { private String mName; private List mInclFiles; private List mRecords; /** * Creates a new instance of JFile. * * @param name possibly full pathname to the file * @param inclFiles included files (as JFile) * @param recList List of records defined within this file */ public JFile(String name, ArrayList inclFiles, ArrayList recList) { mName = name; mInclFiles = inclFiles; mRecords = recList; } /** * Strip the other pathname components and return the basename. */ String getName() { int idx = mName.lastIndexOf('/'); return (idx > 0) ? mName.substring(idx) : mName; } /** * Generate record code in given language. Language should be all * lowercase. * * @param outputDirectory */ public void genCode(String language, File outputDirectory) throws IOException { if ("c++".equals(language)) { CppGenerator gen = new CppGenerator(mName, mInclFiles, mRecords, outputDirectory); gen.genCode(); } else if ("java".equals(language)) { JavaGenerator gen = new JavaGenerator(mName, mInclFiles, mRecords, outputDirectory); gen.genCode(); } else if ("c".equals(language)) { CGenerator gen = new CGenerator(mName, mInclFiles, mRecords, outputDirectory); gen.genCode(); } else if ("csharp".equals(language)) { CSharpGenerator gen = new CSharpGenerator(mName, mInclFiles, mRecords, outputDirectory); gen.genCode(); } else { throw new IOException("Cannnot recognize language:" + language); } } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JFloat.java0100644 0000000 0000000 00000002351 15051152474 031534 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JFloat extends JType { /** * Creates a new instance of JFloat. */ public JFloat() { super("float", "float", "float", "float", "Float", "Float", "float", "toFloat"); } public String getSignature() { return "f"; } public String genJavaHashCode(String fname) { return " ret = java.lang.Float.hashCode(" + fname + ");\n"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JInt.java0100644 0000000 0000000 00000002136 15051152474 031222 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JInt extends JType { /** * Creates a new instance of JInt. */ public JInt() { super("int32_t", "int32_t", "int", "int", "Int", "Integer", "int", "toInt"); } public String getSignature() { return "i"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JLong.java0100644 0000000 0000000 00000002342 15051152474 031366 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JLong extends JType { /** * Creates a new instance of JLong. */ public JLong() { super("int64_t", "int64_t", "long", "long", "Long", "Long", "long", "toLong"); } public String getSignature() { return "l"; } public String genJavaHashCode(String fname) { return " ret = java.lang.Long.hashCode(" + fname + ");\n"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JMap.java0100644 0000000 0000000 00000015531 15051152474 031210 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JMap extends JCompType { private static int level = 0; private static String getLevel() { return Integer.toString(level); } private static void incrLevel() { level++; } private static void decrLevel() { level--; } private static String getId(String id) { return id + getLevel(); } private JType mKey; private JType mValue; /** * Creates a new instance of JMap. */ public JMap(JType t1, JType t2) { super("#error", " ::std::map<" + t1.getCppType() + "," + t2.getCppType() + ">", "System.Collections.Generic.SortedDictionary", "java.util.TreeMap", "Map", "System.Collections.Generic.SortedDictionary", "java.util.TreeMap"); mKey = t1; mValue = t2; } public String getSignature() { return "{" + mKey.getSignature() + mValue.getSignature() + "}"; } public String genJavaCompareTo(String fname) { return " throw new UnsupportedOperationException(\"comparing " + fname + " is unimplemented\");\n"; } public String genJavaReadWrapper(String fname, String tag, boolean decl) { StringBuilder ret = new StringBuilder(""); if (decl) { ret.append(" java.util.TreeMap " + fname + ";\n"); } ret.append(" {\n"); incrLevel(); ret.append(" org.apache.jute.Index " + getId("midx") + " = a_.startMap(\"" + tag + "\");\n"); ret.append(" " + fname + "=new java.util.TreeMap();\n"); ret.append(" for (; !" + getId("midx") + ".done(); " + getId("midx") + ".incr()) {\n"); ret.append(mKey.genJavaReadWrapper(getId("k"), getId("k"), true)); ret.append(mValue.genJavaReadWrapper(getId("v"), getId("v"), true)); ret.append(" " + fname + ".put(" + getId("k") + "," + getId("v") + ");\n"); ret.append(" }\n"); ret.append(" a_.endMap(\"" + tag + "\");\n"); decrLevel(); ret.append(" }\n"); return ret.toString(); } public String genJavaReadMethod(String fname, String tag) { return genJavaReadWrapper(fname, tag, false); } public String genJavaWriteWrapper(String fname, String tag) { StringBuilder ret = new StringBuilder(" {\n"); incrLevel(); ret.append(" a_.startMap(" + fname + ",\"" + tag + "\");\n"); ret.append(" java.util.Set " + getId("es") + " = " + fname + ".entrySet();\n"); ret.append(" for(java.util.Iterator " + getId("midx") + " = " + getId("es") + ".iterator(); " + getId("midx") + ".hasNext(); ) {\n"); ret.append(" java.util.Map.Entry " + getId("me") + " = (java.util.Map.Entry) " + getId("midx") + ".next();\n"); ret.append(" " + mKey.getJavaWrapperType() + " " + getId("k") + " = (" + mKey.getJavaWrapperType() + ") " + getId("me") + ".getKey();\n"); ret.append(" " + mValue.getJavaWrapperType() + " " + getId("v") + " = (" + mValue.getJavaWrapperType() + ") " + getId("me") + ".getValue();\n"); ret.append(mKey.genJavaWriteWrapper(getId("k"), getId("k"))); ret.append(mValue.genJavaWriteWrapper(getId("v"), getId("v"))); ret.append(" }\n"); ret.append(" a_.endMap(" + fname + ",\"" + tag + "\");\n"); ret.append(" }\n"); decrLevel(); return ret.toString(); } public String genJavaWriteMethod(String fname, String tag) { return genJavaWriteWrapper(fname, tag); } public String genCsharpWriteWrapper(String fname, int tag) { StringBuilder ret = new StringBuilder(" {\n"); incrLevel(); ret.append(" a_.StartMap(" + fname + ",\"" + tag + "\");\n"); ret.append(" java.util.Set " + getId("es") + " = " + fname + ".entrySet();\n"); ret.append(" for(java.util.Iterator " + getId("midx") + " = " + getId("es") + ".iterator(); " + getId("midx") + ".hasNext(); ) {\n"); ret.append(" java.util.Map.Entry " + getId("me") + " = (java.util.Map.Entry) " + getId("midx") + ".next();\n"); ret.append(" " + mKey.getCsharpWrapperType() + " " + getId("k") + " = (" + mKey.getCsharpWrapperType() + ") " + getId("me") + ".getKey();\n"); ret.append(" " + mValue.getCsharpWrapperType() + " " + getId("v") + " = (" + mValue.getCsharpWrapperType() + ") " + getId("me") + ".getValue();\n"); ret.append(mKey.genCsharpWriteWrapper(getId("k"), getId("k"))); ret.append(mValue.genCsharpWriteWrapper(getId("v"), getId("v"))); ret.append(" }\n"); ret.append(" a_.EndMap(" + fname + ",\"" + tag + "\");\n"); ret.append(" }\n"); decrLevel(); return ret.toString(); } String genCsharpWriteMethod(String fname, int tag) { return genCsharpWriteWrapper(fname, tag); } public String genCsharpReadWrapper(String fname, int tag, boolean decl) { StringBuilder ret = new StringBuilder(""); if (decl) { ret.append(" System.Collections.SortedDictionary " + capitalize(fname) + ";\n"); } ret.append(" {\n"); incrLevel(); ret.append(" Org.Apache.Jute.IIndex " + getId("midx") + " = a_.StartMap(\"" + tag + "\");\n"); ret.append(" " + fname + "= new System.Collections.SortedDictionary();\n"); ret.append(" for (; !" + getId("midx") + ".done(); " + getId("midx") + ".incr()) {\n"); ret.append(mKey.genCsharpReadWrapper(getId("k"), getId("k"), true)); ret.append(mValue.genCsharpReadWrapper(getId("v"), getId("v"), true)); ret.append(" " + fname + ".Add(" + getId("k") + "," + getId("v") + ");\n"); ret.append(" }\n"); ret.append(" a_.EndMap(\"" + tag + "\");\n"); decrLevel(); ret.append(" }\n"); return ret.toString(); } String genCsharpReadMethod(String fname, int tag) { return genCsharpReadWrapper(fname, tag, false); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JRecord.java0100644 0000000 0000000 00000105755 15051152474 031721 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * */ public class JRecord extends JCompType { private String mFQName; private String mName; private String mModule; private List mFields; /** * Creates a new instance of JRecord. */ public JRecord(String name, ArrayList flist) { super("struct " + name.substring(name.lastIndexOf('.') + 1), name.replaceAll("\\.", "::"), getCsharpFQName(name), name, "Record", name, getCsharpFQName("IRecord")); mFQName = name; int idx = name.lastIndexOf('.'); mName = name.substring(idx + 1); mModule = name.substring(0, idx); mFields = flist; } public String getName() { return mName; } public String getCsharpName() { return "Id".equals(mName) ? "ZKId" : mName; } public String getJavaFQName() { return mFQName; } public String getCppFQName() { return mFQName.replaceAll("\\.", "::"); } public String getJavaPackage() { return mModule; } public String getCppNameSpace() { return mModule.replaceAll("\\.", "::"); } public String getCsharpNameSpace() { String[] parts = mModule.split("\\."); StringBuffer namespace = new StringBuffer(); for (int i = 0; i < parts.length; i++) { String capitalized = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1).toLowerCase(); namespace.append(capitalized); if (i != parts.length - 1) { namespace.append("."); } } return namespace.toString(); } public List getFields() { return mFields; } public String getSignature() { StringBuilder sb = new StringBuilder(); sb.append("L").append(mName).append("("); for (Iterator i = mFields.iterator(); i.hasNext(); ) { String s = i.next().getSignature(); sb.append(s); } sb.append(")"); return sb.toString(); } public String genCppDecl(String fname) { return " " + getCppNameSpace() + "::" + mName + " m" + fname + ";\n"; } public String genJavaReadMethod(String fname, String tag) { return genJavaReadWrapper(fname, tag, false); } public String genJavaReadWrapper(String fname, String tag, boolean decl) { StringBuilder ret = new StringBuilder(""); if (decl) { ret.append(" " + getJavaFQName() + " " + fname + ";\n"); } ret.append(" " + fname + "= new " + getJavaFQName() + "();\n"); ret.append(" a_.readRecord(" + fname + ",\"" + tag + "\");\n"); return ret.toString(); } public String genJavaWriteWrapper(String fname, String tag) { return " a_.writeRecord(" + fname + ",\"" + tag + "\");\n"; } String genCsharpReadMethod(String fname, String tag) { //return " "+capitalize(fname)+"=a_.Read"+mMethodSuffix+"(" + capitalize(fname) + ",\""+tag+"\");\n"; return genCsharpReadWrapper(capitalize(fname), tag, false); } public String genCsharpReadWrapper(String fname, String tag, boolean decl) { StringBuilder ret = new StringBuilder(""); if (decl) { ret.append(" " + getCsharpFQName(mFQName) + " " + fname + ";\n"); } ret.append(" " + fname + "= new " + getCsharpFQName(mFQName) + "();\n"); ret.append(" a_.ReadRecord(" + fname + ",\"" + tag + "\");\n"); return ret.toString(); } public String genCsharpWriteWrapper(String fname, String tag) { return " a_.WriteRecord(" + fname + ",\"" + tag + "\");\n"; } static Map vectorStructs = new HashMap<>(); public void genCCode(FileWriter h, FileWriter c) throws IOException { for (JField f : mFields) { if (f.getType() instanceof JVector) { JVector jv = (JVector) f.getType(); JType jvType = jv.getElementType(); String structName = JVector.extractVectorName(jvType); if (vectorStructs.get(structName) == null) { vectorStructs.put(structName, structName); h.write("struct " + structName + " {\n int32_t count;\n" + jv.getElementType().genCDecl("*data") + "\n};\n"); h.write("int serialize_" + structName + "(struct oarchive *out, const char *tag, struct " + structName + " *v);\n"); h.write("int deserialize_" + structName + "(struct iarchive *in, const char *tag, struct " + structName + " *v);\n"); h.write("int allocate_" + structName + "(struct " + structName + " *v, int32_t len);\n"); h.write("int deallocate_" + structName + "(struct " + structName + " *v);\n"); c.write("int allocate_" + structName + "(struct " + structName + " *v, int32_t len) {\n"); c.write(" if (!len) {\n"); c.write(" v->count = 0;\n"); c.write(" v->data = 0;\n"); c.write(" } else {\n"); c.write(" v->count = len;\n"); c.write(" v->data = calloc(sizeof(*v->data), len);\n"); c.write(" }\n"); c.write(" return 0;\n"); c.write("}\n"); c.write("int deallocate_" + structName + "(struct " + structName + " *v) {\n"); c.write(" if (v->data) {\n"); c.write(" int32_t i;\n"); c.write(" for(i=0;icount; i++) {\n"); c.write(" deallocate_" + JRecord.extractMethodSuffix(jvType) + "(&v->data[i]);\n"); c.write(" }\n"); c.write(" free(v->data);\n"); c.write(" v->data = 0;\n"); c.write(" }\n"); c.write(" return 0;\n"); c.write("}\n"); c.write("int serialize_" + structName + "(struct oarchive *out, const char *tag, struct " + structName + " *v)\n"); c.write("{\n"); c.write(" int32_t count = v->count;\n"); c.write(" int rc = 0;\n"); c.write(" int32_t i;\n"); c.write(" rc = out->start_vector(out, tag, &count);\n"); c.write(" for(i=0;icount;i++) {\n"); genSerialize(c, jvType, "data", "data[i]"); c.write(" }\n"); c.write(" rc = rc ? rc : out->end_vector(out, tag);\n"); c.write(" return rc;\n"); c.write("}\n"); c.write("int deserialize_" + structName + "(struct iarchive *in, const char *tag, struct " + structName + " *v)\n"); c.write("{\n"); c.write(" int rc = 0;\n"); c.write(" int32_t i;\n"); c.write(" rc = in->start_vector(in, tag, &v->count);\n"); c.write(" v->data = calloc(v->count, sizeof(*v->data));\n"); c.write(" for(i=0;icount;i++) {\n"); genDeserialize(c, jvType, "value", "data[i]"); c.write(" }\n"); c.write(" rc = in->end_vector(in, tag);\n"); c.write(" return rc;\n"); c.write("}\n"); } } } String recName = getName(); h.write("struct " + recName + " {\n"); for (JField f : mFields) { h.write(f.genCDecl()); } h.write("};\n"); h.write("int serialize_" + recName + "(struct oarchive *out, const char *tag, struct " + recName + " *v);\n"); h.write("int deserialize_" + recName + "(struct iarchive *in, const char *tag, struct " + recName + "*v);\n"); h.write("void deallocate_" + recName + "(struct " + recName + "*);\n"); c.write("int serialize_" + recName + "(struct oarchive *out, const char *tag, struct " + recName + " *v)"); c.write("{\n"); c.write(" int rc;\n"); c.write(" rc = out->start_record(out, tag);\n"); for (JField f : mFields) { genSerialize(c, f.getType(), f.getTag(), f.getName()); } c.write(" rc = rc ? rc : out->end_record(out, tag);\n"); c.write(" return rc;\n"); c.write("}\n"); c.write("int deserialize_" + recName + "(struct iarchive *in, const char *tag, struct " + recName + "*v)"); c.write("{\n"); c.write(" int rc;\n"); c.write(" rc = in->start_record(in, tag);\n"); for (JField f : mFields) { genDeserialize(c, f.getType(), f.getTag(), f.getName()); } c.write(" rc = rc ? rc : in->end_record(in, tag);\n"); c.write(" return rc;\n"); c.write("}\n"); c.write("void deallocate_" + recName + "(struct " + recName + "*v)"); c.write("{\n"); for (JField f : mFields) { if (f.getType() instanceof JRecord) { c.write(" deallocate_" + extractStructName(f.getType()) + "(&v->" + f.getName() + ");\n"); } else if (f.getType() instanceof JVector) { JVector vt = (JVector) f.getType(); c.write(" deallocate_" + JVector.extractVectorName(vt.getElementType()) + "(&v->" + f.getName() + ");\n"); } else if (f.getType() instanceof JCompType) { c.write(" deallocate_" + extractMethodSuffix(f.getType()) + "(&v->" + f.getName() + ");\n"); } } c.write("}\n"); } private void genSerialize(FileWriter c, JType type, String tag, String name) throws IOException { if (type instanceof JRecord) { c.write(" rc = rc ? rc : serialize_" + extractStructName(type) + "(out, \"" + tag + "\", &v->" + name + ");\n"); } else if (type instanceof JVector) { c.write(" rc = rc ? rc : serialize_" + JVector.extractVectorName(((JVector) type).getElementType()) + "(out, \"" + tag + "\", &v->" + name + ");\n"); } else { c.write(" rc = rc ? rc : out->serialize_" + extractMethodSuffix(type) + "(out, \"" + tag + "\", &v->" + name + ");\n"); } } private void genDeserialize(FileWriter c, JType type, String tag, String name) throws IOException { if (type instanceof JRecord) { c.write(" rc = rc ? rc : deserialize_" + extractStructName(type) + "(in, \"" + tag + "\", &v->" + name + ");\n"); } else if (type instanceof JVector) { c.write(" rc = rc ? rc : deserialize_" + JVector.extractVectorName(((JVector) type).getElementType()) + "(in, \"" + tag + "\", &v->" + name + ");\n"); } else { c.write(" rc = rc ? rc : in->deserialize_" + extractMethodSuffix(type) + "(in, \"" + tag + "\", &v->" + name + ");\n"); } } static String extractMethodSuffix(JType t) { if (t instanceof JRecord) { return extractStructName(t); } return t.getMethodSuffix(); } private static String extractStructName(JType t) { String type = t.getCType(); if (!type.startsWith("struct ")) { return type; } return type.substring("struct ".length()); } public void genCppCode(FileWriter hh, FileWriter cc) throws IOException { String[] ns = getCppNameSpace().split("::"); for (int i = 0; i < ns.length; i++) { hh.write("namespace " + ns[i] + " {\n"); } hh.write("class " + getName() + " : public ::hadoop::Record {\n"); hh.write("private:\n"); for (Iterator i = mFields.iterator(); i.hasNext(); ) { JField jf = i.next(); hh.write(jf.genCppDecl()); } hh.write(" mutable std::bitset<" + mFields.size() + "> bs_;\n"); hh.write("public:\n"); hh.write(" virtual void serialize(::hadoop::OArchive& a_, const char* tag) const;\n"); hh.write(" virtual void deserialize(::hadoop::IArchive& a_, const char* tag);\n"); hh.write(" virtual const ::std::string& type() const;\n"); hh.write(" virtual const ::std::string& signature() const;\n"); hh.write(" virtual bool validate() const;\n"); hh.write(" virtual bool operator<(const " + getName() + "& peer_) const;\n"); hh.write(" virtual bool operator==(const " + getName() + "& peer_) const;\n"); hh.write(" virtual ~" + getName() + "() {};\n"); int fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); hh.write(jf.genCppGetSet(fIdx)); } hh.write("}; // end record " + getName() + "\n"); for (int i = ns.length - 1; i >= 0; i--) { hh.write("} // end namespace " + ns[i] + "\n"); } cc.write("void " + getCppFQName() + "::serialize(::hadoop::OArchive& a_, const char* tag) const {\n"); cc.write(" if (!validate()) throw new ::hadoop::IOException(\"All fields not set.\");\n"); cc.write(" a_.startRecord(*this,tag);\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); String name = jf.getName(); if (jf.getType() instanceof JBuffer) { cc.write(" a_.serialize(m" + name + ",m" + name + ".length(),\"" + jf.getTag() + "\");\n"); } else { cc.write(" a_.serialize(m" + name + ",\"" + jf.getTag() + "\");\n"); } cc.write(" bs_.reset(" + fIdx + ");\n"); } cc.write(" a_.endRecord(*this,tag);\n"); cc.write(" return;\n"); cc.write("}\n"); cc.write("void " + getCppFQName() + "::deserialize(::hadoop::IArchive& a_, const char* tag) {\n"); cc.write(" a_.startRecord(*this,tag);\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); String name = jf.getName(); if (jf.getType() instanceof JBuffer) { cc.write(" { size_t len=0; a_.deserialize(m" + name + ",len,\"" + jf.getTag() + "\");}\n"); } else { cc.write(" a_.deserialize(m" + name + ",\"" + jf.getTag() + "\");\n"); } cc.write(" bs_.set(" + fIdx + ");\n"); } cc.write(" a_.endRecord(*this,tag);\n"); cc.write(" return;\n"); cc.write("}\n"); cc.write("bool " + getCppFQName() + "::validate() const {\n"); cc.write(" if (bs_.size() != bs_.count()) return false;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = (JField) i.next(); JType type = jf.getType(); if (type instanceof JRecord) { cc.write(" if (!m" + jf.getName() + ".validate()) return false;\n"); } } cc.write(" return true;\n"); cc.write("}\n"); cc.write("bool " + getCppFQName() + "::operator< (const " + getCppFQName() + "& peer_) const {\n"); cc.write(" return (1\n"); for (Iterator i = mFields.iterator(); i.hasNext(); ) { JField jf = i.next(); String name = jf.getName(); cc.write(" && (m" + name + " < peer_.m" + name + ")\n"); } cc.write(" );\n"); cc.write("}\n"); cc.write("bool " + getCppFQName() + "::operator== (const " + getCppFQName() + "& peer_) const {\n"); cc.write(" return (1\n"); for (Iterator i = mFields.iterator(); i.hasNext(); ) { JField jf = i.next(); String name = jf.getName(); cc.write(" && (m" + name + " == peer_.m" + name + ")\n"); } cc.write(" );\n"); cc.write("}\n"); cc.write("const ::std::string&" + getCppFQName() + "::type() const {\n"); cc.write(" static const ::std::string type_(\"" + mName + "\");\n"); cc.write(" return type_;\n"); cc.write("}\n"); cc.write("const ::std::string&" + getCppFQName() + "::signature() const {\n"); cc.write(" static const ::std::string sig_(\"" + getSignature() + "\");\n"); cc.write(" return sig_;\n"); cc.write("}\n"); } public void genJavaCode(File outputDirectory) throws IOException { String pkg = getJavaPackage(); String pkgpath = pkg.replaceAll("\\.", "/"); File pkgdir = new File(outputDirectory, pkgpath); if (!pkgdir.exists()) { // create the pkg directory if (!pkgdir.mkdirs()) { throw new IOException("Cannnot create directory: " + pkgpath); } } else if (!pkgdir.isDirectory()) { throw new IOException(pkgpath + " is not a directory."); } try (FileWriter jj = new FileWriter(new File(pkgdir, getName() + ".java"))) { jj.write("// File generated by hadoop record compiler. Do not edit.\n"); jj.write("/**\n"); jj.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); jj.write("* or more contributor license agreements. See the NOTICE file\n"); jj.write("* distributed with this work for additional information\n"); jj.write("* regarding copyright ownership. The ASF licenses this file\n"); jj.write("* to you under the Apache License, Version 2.0 (the\n"); jj.write("* \"License\"); you may not use this file except in compliance\n"); jj.write("* with the License. You may obtain a copy of the License at\n"); jj.write("*\n"); jj.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); jj.write("*\n"); jj.write("* Unless required by applicable law or agreed to in writing, software\n"); jj.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); jj.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); jj.write("* See the License for the specific language governing permissions and\n"); jj.write("* limitations under the License.\n"); jj.write("*/\n"); jj.write("\n"); jj.write("package " + getJavaPackage() + ";\n\n"); jj.write("import org.apache.jute.*;\n"); jj.write("import org.apache.jute.Record; // JDK14 needs explicit import due to clash with java.lang.Record\n"); jj.write("import org.apache.yetus.audience.InterfaceAudience;\n"); jj.write("@InterfaceAudience.Public\n"); jj.write("public class " + getName() + " implements Record {\n"); for (Iterator i = mFields.iterator(); i.hasNext(); ) { JField jf = i.next(); jj.write(jf.genJavaDecl()); } jj.write(" public " + getName() + "() {\n"); jj.write(" }\n"); jj.write(" public " + getName() + "(\n"); int fIdx = 0; int fLen = mFields.size(); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaConstructorParam(jf.getName())); jj.write((fLen - 1 == fIdx) ? "" : ",\n"); } jj.write(") {\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaConstructorSet(jf.getName())); } jj.write(" }\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaGetSet(fIdx)); } jj.write(" public void serialize(OutputArchive a_, String tag) throws java.io.IOException {\n"); jj.write(" a_.startRecord(this,tag);\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaWriteMethodName()); } jj.write(" a_.endRecord(this,tag);\n"); jj.write(" }\n"); jj.write(" public void deserialize(InputArchive a_, String tag) throws java.io.IOException {\n"); jj.write(" a_.startRecord(tag);\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaReadMethodName()); } jj.write(" a_.endRecord(tag);\n"); jj.write("}\n"); jj.write(" public String toString() {\n"); jj.write(" try {\n"); jj.write(" java.io.ByteArrayOutputStream s =\n"); jj.write(" new java.io.ByteArrayOutputStream();\n"); jj.write(" ToStringOutputArchive a_ = \n"); jj.write(" new ToStringOutputArchive(s);\n"); jj.write(" a_.startRecord(this,\"\");\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaWriteMethodName()); } jj.write(" a_.endRecord(this,\"\");\n"); jj.write(" return new String(s.toByteArray(), java.nio.charset.StandardCharsets.UTF_8);\n"); jj.write(" } catch (Throwable ex) {\n"); jj.write(" ex.printStackTrace();\n"); jj.write(" }\n"); jj.write(" return \"ERROR\";\n"); jj.write(" }\n"); jj.write(" public void write(java.io.DataOutput out) throws java.io.IOException {\n"); jj.write(" BinaryOutputArchive archive = new BinaryOutputArchive(out);\n"); jj.write(" serialize(archive, \"\");\n"); jj.write(" }\n"); jj.write(" public void readFields(java.io.DataInput in) throws java.io.IOException {\n"); jj.write(" BinaryInputArchive archive = new BinaryInputArchive(in);\n"); jj.write(" deserialize(archive, \"\");\n"); jj.write(" }\n"); jj.write(" public int compareTo (Object peer_) throws ClassCastException {\n"); boolean unimplemented = false; for (JField f : mFields) { if ((f.getType() instanceof JMap) || (f.getType() instanceof JVector)) { unimplemented = true; } } if (unimplemented) { jj.write(" throw new UnsupportedOperationException(\"comparing " + getName() + " is unimplemented\");\n"); } else { jj.write(" if (!(peer_ instanceof " + getName() + ")) {\n"); jj.write(" throw new ClassCastException(\"Comparing different types of records.\");\n"); jj.write(" }\n"); jj.write(" " + getName() + " peer = (" + getName() + ") peer_;\n"); jj.write(" int ret = 0;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaCompareTo()); jj.write(" if (ret != 0) return ret;\n"); } jj.write(" return ret;\n"); } jj.write(" }\n"); jj.write(" public boolean equals(Object peer_) {\n"); jj.write(" if (!(peer_ instanceof " + getName() + ")) {\n"); jj.write(" return false;\n"); jj.write(" }\n"); jj.write(" if (peer_ == this) {\n"); jj.write(" return true;\n"); jj.write(" }\n"); jj.write(" " + getName() + " peer = (" + getName() + ") peer_;\n"); jj.write(" boolean ret = false;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaEquals()); jj.write(" if (!ret) return ret;\n"); } jj.write(" return ret;\n"); jj.write(" }\n"); jj.write(" public int hashCode() {\n"); jj.write(" int result = 17;\n"); jj.write(" int ret;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); jj.write(jf.genJavaHashCode()); jj.write(" result = 37*result + ret;\n"); } jj.write(" return result;\n"); jj.write(" }\n"); jj.write(" public static String signature() {\n"); jj.write(" return \"" + getSignature() + "\";\n"); jj.write(" }\n"); jj.write("}\n"); } } public void genCsharpCode(File outputDirectory) throws IOException { if (!outputDirectory.exists()) { // create the pkg directory if (!outputDirectory.mkdirs()) { throw new IOException("Cannnot create directory: " + outputDirectory); } } else if (!outputDirectory.isDirectory()) { throw new IOException(outputDirectory + " is not a directory."); } try (FileWriter cs = new FileWriter(new File(outputDirectory, getName() + ".cs"));) { cs.write("// File generated by hadoop record compiler. Do not edit.\n"); cs.write("/**\n"); cs.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); cs.write("* or more contributor license agreements. See the NOTICE file\n"); cs.write("* distributed with this work for additional information\n"); cs.write("* regarding copyright ownership. The ASF licenses this file\n"); cs.write("* to you under the Apache License, Version 2.0 (the\n"); cs.write("* \"License\"); you may not use this file except in compliance\n"); cs.write("* with the License. You may obtain a copy of the License at\n"); cs.write("*\n"); cs.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); cs.write("*\n"); cs.write("* Unless required by applicable law or agreed to in writing, software\n"); cs.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); cs.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); cs.write("* See the License for the specific language governing permissions and\n"); cs.write("* limitations under the License.\n"); cs.write("*/\n"); cs.write("\n"); cs.write("using System;\n"); cs.write("using Org.Apache.Jute;\n"); cs.write("\n"); cs.write("namespace " + getCsharpNameSpace() + "\n"); cs.write("{\n"); String className = getCsharpName(); cs.write("public class " + className + " : IRecord, IComparable \n"); cs.write("{\n"); cs.write(" public " + className + "() {\n"); cs.write(" }\n"); cs.write(" public " + className + "(\n"); int fIdx = 0; int fLen = mFields.size(); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpConstructorParam(jf.getCsharpName())); cs.write((fLen - 1 == fIdx) ? "" : ",\n"); } cs.write(") {\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpConstructorSet(jf.getCsharpName())); } cs.write(" }\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpGetSet(fIdx)); cs.write("\n"); } cs.write(" public void Serialize(IOutputArchive a_, String tag) {\n"); cs.write(" a_.StartRecord(this,tag);\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpWriteMethodName()); } cs.write(" a_.EndRecord(this,tag);\n"); cs.write(" }\n"); cs.write(" public void Deserialize(IInputArchive a_, String tag) {\n"); cs.write(" a_.StartRecord(tag);\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpReadMethodName()); } cs.write(" a_.EndRecord(tag);\n"); cs.write("}\n"); cs.write(" public override String ToString() {\n"); cs.write(" try {\n"); cs.write(" System.IO.MemoryStream ms = new System.IO.MemoryStream();\n"); cs.write(" MiscUtil.IO.EndianBinaryWriter writer =\n"); cs.write(" new MiscUtil.IO.EndianBinaryWriter(MiscUtil.Conversion.EndianBitConverter.Big, ms, System.Text.Encoding.UTF8);\n"); cs.write(" BinaryOutputArchive a_ = \n"); cs.write(" new BinaryOutputArchive(writer);\n"); cs.write(" a_.StartRecord(this,\"\");\n"); fIdx = 0; for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpWriteMethodName()); } cs.write(" a_.EndRecord(this,\"\");\n"); cs.write(" ms.Position = 0;\n"); cs.write(" return System.Text.Encoding.UTF8.GetString(ms.ToArray());\n"); cs.write(" } catch (Exception ex) {\n"); cs.write(" Console.WriteLine(ex.StackTrace);\n"); cs.write(" }\n"); cs.write(" return \"ERROR\";\n"); cs.write(" }\n"); cs.write(" public void Write(MiscUtil.IO.EndianBinaryWriter writer) {\n"); cs.write(" BinaryOutputArchive archive = new BinaryOutputArchive(writer);\n"); cs.write(" Serialize(archive, \"\");\n"); cs.write(" }\n"); cs.write(" public void ReadFields(MiscUtil.IO.EndianBinaryReader reader) {\n"); cs.write(" BinaryInputArchive archive = new BinaryInputArchive(reader);\n"); cs.write(" Deserialize(archive, \"\");\n"); cs.write(" }\n"); cs.write(" public int CompareTo (object peer_) {\n"); boolean unimplemented = false; for (JField f : mFields) { if ((f.getType() instanceof JMap) || (f.getType() instanceof JVector)) { unimplemented = true; } } if (unimplemented) { cs.write(" throw new InvalidOperationException(\"comparing " + getCsharpName() + " is unimplemented\");\n"); } else { cs.write(" if (!(peer_ is " + getCsharpName() + ")) {\n"); cs.write(" throw new InvalidOperationException(\"Comparing different types of records.\");\n"); cs.write(" }\n"); cs.write(" " + getCsharpName() + " peer = (" + getCsharpName() + ") peer_;\n"); cs.write(" int ret = 0;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpCompareTo()); cs.write(" if (ret != 0) return ret;\n"); } cs.write(" return ret;\n"); } cs.write(" }\n"); cs.write(" public override bool Equals(object peer_) {\n"); cs.write(" if (!(peer_ is " + getCsharpName() + ")) {\n"); cs.write(" return false;\n"); cs.write(" }\n"); cs.write(" if (peer_ == this) {\n"); cs.write(" return true;\n"); cs.write(" }\n"); cs.write(" bool ret = false;\n"); cs.write(" " + getCsharpName() + " peer = (" + getCsharpName() + ")peer_;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpEquals()); cs.write(" if (!ret) return ret;\n"); } cs.write(" return ret;\n"); cs.write(" }\n"); cs.write(" public override int GetHashCode() {\n"); cs.write(" int result = 17;\n"); cs.write(" int ret;\n"); for (Iterator i = mFields.iterator(); i.hasNext(); fIdx++) { JField jf = i.next(); cs.write(jf.genCsharpHashCode()); cs.write(" result = 37*result + ret;\n"); } cs.write(" return result;\n"); cs.write(" }\n"); cs.write(" public static string Signature() {\n"); cs.write(" return \"" + getSignature() + "\";\n"); cs.write(" }\n"); cs.write("}\n"); cs.write("}\n"); } } public static String getCsharpFQName(String name) { String[] packages = name.split("\\."); StringBuffer fQName = new StringBuffer(); for (int i = 0; i < packages.length; i++) { String pack = packages[i]; pack = capitalize(pack); pack = "Id".equals(pack) ? "ZKId" : pack; fQName.append(capitalize(pack)); if (i != packages.length - 1) { fQName.append("."); } } return fQName.toString(); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JString.java0100644 0000000 0000000 00000003025 15051152474 031734 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * */ public class JString extends JCompType { /** * Creates a new instance of JString. */ public JString() { super("char *", " ::std::string", "string", "String", "String", "String", "string"); } public String getSignature() { return "s"; } public String genJavaReadWrapper(String fname, String tag, boolean decl) { String ret = ""; if (decl) { ret = " String " + fname + ";\n"; } return ret + " " + fname + "=a_.readString(\"" + tag + "\");\n"; } public String genJavaWriteWrapper(String fname, String tag) { return " a_.writeString(" + fname + ",\"" + tag + "\");\n"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JType.java0100644 0000000 0000000 00000015363 15051152474 031417 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; /** * Abstract Base class for all types supported by Hadoop Record I/O. */ public abstract class JType { private String mCName; private String mCppName; private String mCsharpName; private String mJavaName; protected String mMethodSuffix; private String mWrapper; private String mSharpWrapper; private String mUnwrapMethod; /** * Creates a new instance of JType. */ JType(String cname, String cppname, String csharpName, String javaname, String suffix, String wrapper, String csharpWrapper, String unwrap) { mCName = cname; mCppName = cppname; mCsharpName = "Id".equals(csharpName) ? "ZKId" : csharpName; mJavaName = javaname; mMethodSuffix = suffix; mWrapper = wrapper; mSharpWrapper = csharpWrapper; mUnwrapMethod = unwrap; } abstract String getSignature(); String genCppDecl(String fname) { return " " + mCppName + " m" + fname + ";\n"; } String genCDecl(String name) { return " " + mCName + " " + name + ";\n"; } public String genCsharpDecl(String name) { return " private " + mCsharpName + " " + name + ";\n"; } String genJavaDecl(String fname) { return " private " + mJavaName + " " + fname + ";\n"; } String genJavaConstructorParam(String fname) { return " " + mJavaName + " " + fname; } String genCppGetSet(String fname, int fIdx) { String getFunc = " virtual " + mCppName + " get" + fname + "() const {\n"; getFunc += " return m" + fname + ";\n"; getFunc += " }\n"; String setFunc = " virtual void set" + fname + "(" + mCppName + " m_) {\n"; setFunc += " m" + fname + "=m_; bs_.set(" + fIdx + ");\n"; setFunc += " }\n"; return getFunc + setFunc; } String genCsharpGetSet(String fname, int fIdx) { String getFunc = " public " + getCsharpType() + " " + capitalize(fname) + " { get; set; } "; return getFunc; } static String capitalize(String s) { return s.substring(0, 1).toUpperCase() + s.substring(1); } String genJavaGetSet(String fname, int fIdx) { String getFunc = " public " + mJavaName + " get" + capitalize(fname) + "() {\n"; getFunc += " return " + fname + ";\n"; getFunc += " }\n"; String setFunc = " public void set" + capitalize(fname) + "(" + mJavaName + " m_) {\n"; setFunc += " " + fname + "=m_;\n"; setFunc += " }\n"; return getFunc + setFunc; } String getCType() { return mCName; } String getCppType() { return mCppName; } String getCsharpType() { return mCsharpName; } String getJavaType() { return mJavaName; } String getJavaWrapperType() { return mWrapper; } String getCsharpWrapperType() { return mSharpWrapper; } String getMethodSuffix() { return mMethodSuffix; } String genJavaWriteMethod(String fname, String tag) { return " a_.write" + mMethodSuffix + "(" + fname + ",\"" + tag + "\");\n"; } String genJavaReadMethod(String fname, String tag) { return " " + fname + "=a_.read" + mMethodSuffix + "(\"" + tag + "\");\n"; } String genJavaReadWrapper(String fname, String tag, boolean decl) { String ret = ""; if (decl) { ret = " " + mWrapper + " " + fname + ";\n"; } return ret + " " + fname + "=new " + mWrapper + "(a_.read" + mMethodSuffix + "(\"" + tag + "\"));\n"; } String genJavaWriteWrapper(String fname, String tag) { return " a_.write" + mMethodSuffix + "(" + fname + "." + mUnwrapMethod + "(),\"" + tag + "\");\n"; } String genJavaCompareTo(String fname) { return " ret = (" + fname + " == peer." + fname + ")? 0 :((" + fname + "", "System.Collections.Generic.List<" + t.getCsharpType() + ">", "java.util.List<" + t.getJavaType() + ">", "Vector", "System.Collections.Generic.List<" + t.getCsharpType() + ">", "java.util.ArrayList<" + t.getJavaType() + ">"); mElement = t; } public String getSignature() { return "[" + mElement.getSignature() + "]"; } public String genJavaCompareTo(String fname) { return " throw new UnsupportedOperationException(\"comparing " + fname + " is unimplemented\");\n"; } public String genJavaReadWrapper(String fname, String tag, boolean decl) { StringBuilder ret = new StringBuilder(""); if (decl) { ret.append(" java.util.List " + fname + ";\n"); } ret.append(" {\n"); incrLevel(); ret.append(" Index " + getId("vidx") + " = a_.startVector(\"" + tag + "\");\n"); ret.append(" if (" + getId("vidx") + "!= null) {"); ret.append(" " + fname + "=new java.util.ArrayList<" + mElement.getJavaType() + ">();\n"); ret.append(" for (; !" + getId("vidx") + ".done(); " + getId("vidx") + ".incr()) {\n"); ret.append(mElement.genJavaReadWrapper(getId("e"), getId("e"), true)); ret.append(" " + fname + ".add(" + getId("e") + ");\n"); ret.append(" }\n"); ret.append(" }\n"); ret.append(" a_.endVector(\"" + tag + "\");\n"); decrLevel(); ret.append(" }\n"); return ret.toString(); } public String genJavaReadMethod(String fname, String tag) { return genJavaReadWrapper(fname, tag, false); } public String genJavaWriteWrapper(String fname, String tag) { StringBuilder ret = new StringBuilder(" {\n"); incrLevel(); ret.append(" a_.startVector(" + fname + ",\"" + tag + "\");\n"); ret.append(" if (" + fname + "!= null) {"); ret.append(" int " + getId("len") + " = " + fname + ".size();\n"); ret.append(" for(int " + getId("vidx") + " = 0; " + getId("vidx") + "<" + getId("len") + "; " + getId("vidx") + "++) {\n"); ret.append(" " + mElement.getJavaWrapperType() + " " + getId("e") + " = (" + mElement.getJavaWrapperType() + ") " + fname + ".get(" + getId("vidx") + ");\n"); ret.append(mElement.genJavaWriteWrapper(getId("e"), getId("e"))); ret.append(" }\n"); ret.append(" }\n"); ret.append(" a_.endVector(" + fname + ",\"" + tag + "\");\n"); ret.append(" }\n"); decrLevel(); return ret.toString(); } public String genJavaWriteMethod(String fname, String tag) { return genJavaWriteWrapper(fname, tag); } public JType getElementType() { return mElement; } public String genCsharpWriteWrapper(String fname, String tag) { StringBuilder ret = new StringBuilder(" {\n"); incrLevel(); ret.append(" a_.StartVector(" + capitalize(fname) + ",\"" + tag + "\");\n"); ret.append(" if (" + capitalize(fname) + "!= null) {"); ret.append(" int " + getId("len") + " = " + capitalize(fname) + ".Count;\n"); ret.append(" for(int " + getId("vidx") + " = 0; " + getId("vidx") + "<" + getId("len") + "; " + getId("vidx") + "++) {\n"); ret.append(" " + mElement.getCsharpWrapperType() + " " + getId("e") + " = (" + mElement.getCsharpWrapperType() + ") " + capitalize(fname) + "[" + getId("vidx") + "];\n"); ret.append(mElement.genCsharpWriteWrapper(getId("e"), getId("e"))); ret.append(" }\n"); ret.append(" }\n"); ret.append(" a_.EndVector(" + capitalize(fname) + ",\"" + tag + "\");\n"); ret.append(" }\n"); decrLevel(); return ret.toString(); } String genCsharpWriteMethod(String fname, String tag) { return genCsharpWriteWrapper(fname, tag); } public String genCsharpReadWrapper(String fname, String tag, boolean decl) { StringBuilder ret = new StringBuilder(); if (decl) { ret.append(" System.Collections.Generic.List<" + mElement.getCsharpType() + "> " + capitalize(fname) + ";\n"); } ret.append(" {\n"); incrLevel(); ret.append(" IIndex " + getId("vidx") + " = a_.StartVector(\"" + tag + "\");\n"); ret.append(" if (" + getId("vidx") + "!= null) {"); ret.append(" " + capitalize(fname) + "=new System.Collections.Generic.List<" + mElement.getCsharpType() + ">();\n"); ret.append(" for (; !" + getId("vidx") + ".Done(); " + getId("vidx") + ".Incr()) {\n"); ret.append(mElement.genCsharpReadWrapper(getId("e"), getId("e"), true)); ret.append(" " + capitalize(fname) + ".Add(" + getId("e") + ");\n"); ret.append(" }\n"); ret.append(" }\n"); ret.append(" a_.EndVector(\"" + tag + "\");\n"); decrLevel(); ret.append(" }\n"); return ret.toString(); } String genCsharpReadMethod(String fname, String tag) { return genCsharpReadWrapper(fname, tag, false); } static String extractVectorName(JType jvType) { return JRecord.extractMethodSuffix(jvType) + "_vector"; } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/JavaGenerator.java0100644 0000000 0000000 00000003647 15051152474 033116 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler; import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.List; /** * Java Code generator front-end for Hadoop record I/O. */ class JavaGenerator { private List mRecList; private final File outputDirectory; /** * Creates a new instance of JavaGenerator. * * @param name possibly full pathname to the file * @param incl included files (as JFile) * @param records List of records defined within this file * @param outputDirectory */ JavaGenerator(String name, List incl, List records, File outputDirectory) { mRecList = records; this.outputDirectory = outputDirectory; } /** * Generate Java code for records. This method is only a front-end to * JRecord, since one file is generated for each record. */ void genCode() throws IOException { for (Iterator i = mRecList.iterator(); i.hasNext(); ) { JRecord rec = i.next(); rec.genJavaCode(outputDirectory); } } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/generated/package.html0100644 0000000 0000000 00000002167 15051152474 033736 0ustar00rootroot0000000 0000000 Hadoop Record Compiler: Parser This package contains code generated by JavaCC from the Hadoop record syntax file rcc.jj. For details about the record file syntax please @see org.apache.hadoop.record. apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/generated/rcc.jj0100644 0000000 0000000 00000020250 15051152474 032542 0ustar00rootroot0000000 0000000 options { STATIC=false; } PARSER_BEGIN(Rcc) /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute.compiler.generated; import org.apache.jute.compiler.*; import java.util.ArrayList; import java.util.Hashtable; import java.io.File; import java.io.FileReader; import java.io.FileNotFoundException; import java.io.IOException; @SuppressWarnings("unused") public class Rcc { private static Hashtable recTab = new Hashtable(); private static String curDir = System.getProperty("user.dir"); private static String curFileName; private static String curModuleName; public static void main(String args[]) { String language = "java"; ArrayList recFiles = new ArrayList(); JFile curFile=null; for (int i=0; i(); return parser.Input(); } finally { try { reader.close(); } catch (IOException e) { } } } } PARSER_END(Rcc) SKIP : { " " | "\t" | "\n" | "\r" } SPECIAL_TOKEN : { "//" : WithinOneLineComment } SPECIAL_TOKEN : { <("\n" | "\r" | "\r\n" )> : DEFAULT } MORE : { <~[]> } SPECIAL_TOKEN : { "/*" : WithinMultiLineComment } SPECIAL_TOKEN : { "*/" : DEFAULT } MORE : { <~[]> } TOKEN : { | | | | | | | | | | | | | | | | "> | | | | | } JFile Input() : { ArrayList ilist = new ArrayList(); ArrayList rlist = new ArrayList(); JFile i; ArrayList l; } { ( i = Include() { ilist.add(i); } | l = Module() { rlist.addAll(l); } )+ { return new JFile(curFileName, ilist, rlist); } } JFile Include() : { String fname; Token t; } { t = { JFile ret = null; fname = t.image.replaceAll("^\"", "").replaceAll("\"$",""); File file = new File(curDir, fname); String tmpDir = curDir; String tmpFile = curFileName; curDir = file.getParent(); curFileName = file.getName(); try { FileReader reader = new FileReader(file); Rcc parser = new Rcc(reader); try { ret = parser.Input(); System.out.println(fname + " Parsed Successfully"); } catch (ParseException e) { System.out.println(e.toString()); System.exit(1); } try { reader.close(); } catch (IOException e) { } } catch (FileNotFoundException e) { System.out.println("File " + fname + " Not found."); System.exit(1); } curDir = tmpDir; curFileName = tmpFile; return ret; } } ArrayList Module() : { String mName; ArrayList rlist; } { mName = ModuleName() { curModuleName = mName; } rlist = RecordList() { return rlist; } } String ModuleName() : { String name = ""; Token t; } { t = { name += t.image; } ( t = { name += "." + t.image; } )* { return name; } } ArrayList RecordList() : { ArrayList rlist = new ArrayList(); JRecord r; } { ( r = Record() { rlist.add(r); } )+ { return rlist; } } JRecord Record() : { String rname; ArrayList flist = new ArrayList(); Token t; JField f; } { t = { rname = t.image; } ( f = Field() { flist.add(f); } )+ { String fqn = curModuleName + "." + rname; JRecord r = new JRecord(fqn, flist); recTab.put(fqn, r); return r; } } JField Field() : { JType jt; Token t; } { jt = Type() t = { return new JField(jt, t.image); } } JType Type() : { JType jt; Token t; String rname; } { jt = Map() { return jt; } | jt = Vector() { return jt; } | { return new JByte(); } | { return new JBoolean(); } | { return new JInt(); } | { return new JLong(); } | { return new JFloat(); } | { return new JDouble(); } | { return new JString(); } | { return new JBuffer(); } | rname = ModuleName() { if (rname.indexOf('.', 0) < 0) { rname = curModuleName + "." + rname; } JRecord r = recTab.get(rname); if (r == null) { System.out.println("Type " + rname + " not known. Exiting."); System.exit(1); } return r; } } JMap Map() : { JType jt1; JType jt2; } { jt1 = Type() jt2 = Type() { return new JMap(jt1, jt2); } } JVector Vector() : { JType jt; } { jt = Type() { return new JVector(jt); } } apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/compiler/package.html0100644 0000000 0000000 00000002335 15051152474 031775 0ustar00rootroot0000000 0000000 Hadoop Record Compiler This package contains classes needed for code generation from the hadoop record compiler. CppGenerator and JavaGenerator are the main entry points from the parser. There are classes corrsponding to every primitive type and compound type included in Hadoop record I/O syntax. apache-zookeeper-3.9.4/zookeeper-jute/src/main/java/org/apache/jute/package.html0100644 0000000 0000000 00000054777 15051152474 030204 0ustar00rootroot0000000 0000000 Hadoop Record I/O Hadoop record I/O contains classes and a record description language translator for simplifying serialization and deserialization of records in a language-neutral manner.

Introduction

Software systems of any significant complexity require mechanisms for data interchange with the outside world. These interchanges typically involve the marshaling and unmarshaling of logical units of data to and from data streams (files, network connections, memory buffers etc.). Applications usually have some code for serializing and deserializing the data types that they manipulate embedded in them. The work of serialization has several features that make automatic code generation for it worthwhile. Given a particular output encoding (binary, XML, etc.), serialization of primitive types and simple compositions of primitives (structs, vectors etc.) is a very mechanical task. Manually written serialization code can be susceptible to bugs especially when records have a large number of fields or a record definition changes between software versions. Lastly, it can be very useful for applications written in different programming languages to be able to share and interchange data. This can be made a lot easier by describing the data records manipulated by these applications in a language agnostic manner and using the descriptions to derive implementations of serialization in multiple target languages. This document describes Hadoop Record I/O, a mechanism that is aimed at
  • enabling the specification of simple serializable data types (records)
  • enabling the generation of code in multiple target languages for marshaling and unmarshaling such types
  • providing target language specific support that will enable application programmers to incorporate generated code into their applications
The goals of Hadoop Record I/O are similar to those of mechanisms such as XDR, ASN.1, PADS and ICE. While these systems all include a DDL that enables the specification of most record types, they differ widely in what else they focus on. The focus in Hadoop Record I/O is on data marshaling and multi-lingual support. We take a translator-based approach to serialization. Hadoop users have to describe their data in a simple data description language. The Hadoop DDL translator rcc generates code that users can invoke in order to read/write their data from/to simple stream abstractions. Next we list explicitly some of the goals and non-goals of Hadoop Record I/O.

Goals

  • Support for commonly used primitive types. Hadoop should include as primitives commonly used builtin types from programming languages we intend to support.
  • Support for common data compositions (including recursive compositions). Hadoop should support widely used composite types such as structs and vectors.
  • Code generation in multiple target languages. Hadoop should be capable of generating serialization code in multiple target languages and should be easily extensible to new target languages. The initial target languages are C++ and Java.
  • Support for generated target languages. Hadooop should include support in the form of headers, libraries, packages for supported target languages that enable easy inclusion and use of generated code in applications.
  • Support for multiple output encodings. Candidates include packed binary, comma-separated text, XML etc.
  • Support for specifying record types in a backwards/forwards compatible manner. This will probably be in the form of support for optional fields in records. This version of the document does not include a description of the planned mechanism, we intend to include it in the next iteration.

Non-Goals

  • Serializing existing arbitrary C++ classes.
  • Serializing complex data structures such as trees, linked lists etc.
  • Built-in indexing schemes, compression, or check-sums.
  • Dynamic construction of objects from an XML schema.
The remainder of this document describes the features of Hadoop record I/O in more detail. Section 2 describes the data types supported by the system. Section 3 lays out the DDL syntax with some examples of simple records. Section 4 describes the process of code generation with rcc. Section 5 describes target language mappings and support for Hadoop types. We include a fairly complete description of C++ mappings with intent to include Java and others in upcoming iterations of this document. The last section talks about supported output encodings.

Data Types and Streams

This section describes the primitive and composite types supported by Hadoop. We aim to support a set of types that can be used to simply and efficiently express a wide range of record types in different programming languages.

Primitive Types

For the most part, the primitive types of Hadoop map directly to primitive types in high level programming languages. Special cases are the ustring (a Unicode string) and buffer types, which we believe find wide use and which are usually implemented in library code and not available as language built-ins. Hadoop also supplies these via library code when a target language built-in is not present and there is no widely adopted "standard" implementation. The complete list of primitive types is:
  • byte: An 8-bit unsigned integer.
  • boolean: A boolean value.
  • int: A 32-bit signed integer.
  • long: A 64-bit signed integer.
  • float: A single precision floating point number as described by IEEE-754.
  • double: A double precision floating point number as described by IEEE-754.
  • ustring: A string consisting of Unicode characters.
  • buffer: An arbitrary sequence of bytes.

Composite Types

Hadoop supports a small set of composite types that enable the description of simple aggregate types and containers. A composite type is serialized by sequentially serializing it constituent elements. The supported composite types are:
  • record: An aggregate type like a C-struct. This is a list of typed fields that are together considered a single unit of data. A record is serialized by sequentially serializing its constituent fields. In addition to serialization a record has comparison operations (equality and less-than) implemented for it, these are defined as memberwise comparisons.
  • vector: A sequence of entries of the same data type, primitive or composite.
  • map: An associative container mapping instances of a key type to instances of a value type. The key and value types may themselves be primitive or composite types.

Streams

Hadoop generates code for serializing and deserializing record types to abstract streams. For each target language Hadoop defines very simple input and output stream interfaces. Application writers can usually develop concrete implementations of these by putting a one method wrapper around an existing stream implementation.

DDL Syntax and Examples

We now describe the syntax of the Hadoop data description language. This is followed by a few examples of DDL usage.

Hadoop DDL Syntax


recfile = *include module *record
include = "include" path
path = (relative-path / absolute-path)
module = "module" module-name
module-name = name *("." name)
record := "class" name "{" 1*(field) "}"
field := type name ";"
name :=  ALPHA (ALPHA / DIGIT / "_" )*
type := (ptype / ctype)
ptype := ("byte" / "boolean" / "int" |
          "long" / "float" / "double"
          "ustring" / "buffer")
ctype := (("vector" "<" type ">") /
          ("map" "<" type "," type ">" ) ) / name)
A DDL file describes one or more record types. It begins with zero or more include declarations, a single mandatory module declaration followed by zero or more class declarations. The semantics of each of these declarations are described below:
  • include: An include declaration specifies a DDL file to be referenced when generating code for types in the current DDL file. Record types in the current compilation unit may refer to types in all included files. File inclusion is recursive. An include does not trigger code generation for the referenced file.
  • module: Every Hadoop DDL file must have a single module declaration that follows the list of includes and precedes all record declarations. A module declaration identifies a scope within which the names of all types in the current file are visible. Module names are mapped to C++ namespaces, Java packages etc. in generated code.
  • class: Records types are specified through class declarations. A class declaration is like a Java class declaration. It specifies a named record type and a list of fields that constitute records of the type. Usage is illustrated in the following examples.

Examples

  • A simple DDL file links.jr with just one record declaration.
    
    module links {
        class Link {
            ustring URL;
            boolean isRelative;
            ustring anchorText;
        };
    }
    
  • A DDL file outlinks.jr which includes another
    
    include "links.jr"
    
    module outlinks {
        class OutLinks {
            ustring baseURL;
            vector<links.Link> outLinks;
        };
    }
    

Code Generation

The Hadoop translator is written in Java. Invocation is done by executing a wrapper shell script named named rcc. It takes a list of record description files as a mandatory argument and an optional language argument (the default is Java) --language or -l. Thus a typical invocation would look like:

$ rcc -l C++ <filename> ...

Target Language Mappings and Support

For all target languages, the unit of code generation is a record type. For each record type, Hadoop generates code for serialization and deserialization, record comparison and access to record members.

C++

Support for including Hadoop generated C++ code in applications comes in the form of a header file recordio.hh which needs to be included in source that uses Hadoop types and a library librecordio.a which applications need to be linked with. The header declares the Hadoop C++ namespace which defines appropriate types for the various primitives, the basic interfaces for records and streams and enumerates the supported serialization encodings. Declarations of these interfaces and a description of their semantics follow:

namespace hadoop {

  enum RecFormat { kBinary };

  class InStream {
  public:
    virtual ssize_t read(void *buf, size_t n) = 0;
  };

  class OutStream {
  public:
    virtual ssize_t write(const void *buf, size_t n) = 0;
  };

  class IOError : public runtime_error {
  public:
    explicit IOError(const std::string& msg);
  };

  class IArchive;
  class OArchive;

  class RecordReader {
  public:
    RecordReader(InStream& in, RecFormat fmt);
    virtual ~RecordReader(void);

    virtual void read(Record& rec);
  };

  class RecordWriter {
  public:
    RecordWriter(OutStream& out, RecFormat fmt);
    virtual ~RecordWriter(void);

    virtual void write(Record& rec);
  };


  class Record {
  public:
    virtual std::string type(void) const = 0;
    virtual std::string signature(void) const = 0;
  protected:
    virtual bool validate(void) const = 0;

    virtual void
    serialize(OArchive& oa, const std::string& tag) const = 0;

    virtual void
    deserialize(IArchive& ia, const std::string& tag) = 0;
  };
}
  • RecFormat: An enumeration of the serialization encodings supported by this implementation of Hadoop.
  • InStream: A simple abstraction for an input stream. This has a single public read method that reads n bytes from the stream into the buffer buf. Has the same semantics as a blocking read system call. Returns the number of bytes read or -1 if an error occurs.
  • OutStream: A simple abstraction for an output stream. This has a single write method that writes n bytes to the stream from the buffer buf. Has the same semantics as a blocking write system call. Returns the number of bytes written or -1 if an error occurs.
  • RecordReader: A RecordReader reads records one at a time from an underlying stream in a specified record format. The reader is instantiated with a stream and a serialization format. It has a read method that takes an instance of a record and deserializes the record from the stream.
  • RecordWriter: A RecordWriter writes records one at a time to an underlying stream in a specified record format. The writer is instantiated with a stream and a serialization format. It has a write method that takes an instance of a record and serializes the record to the stream.
  • Record: The base class for all generated record types. This has two public methods type and signature that return the typename and the type signature of the record.
Two files are generated for each record file (note: not for each record). If a record file is named "name.jr", the generated files are "name.jr.cc" and "name.jr.hh" containing serialization implementations and record type declarations respectively. For each record in the DDL file, the generated header file will contain a class definition corresponding to the record type, method definitions for the generated type will be present in the '.cc' file. The generated class will inherit from the abstract class hadoop::Record. The DDL files module declaration determines the namespace the record belongs to. Each '.' delimited token in the module declaration results in the creation of a namespace. For instance, the declaration module docs.links results in the creation of a docs namespace and a nested docs::links namespace. In the preceding examples, the Link class is placed in the links namespace. The header file corresponding to the links.jr file will contain:

namespace links {
  class Link : public hadoop::Record {
    // ....
  };
};
Each field within the record will cause the generation of a private member declaration of the appropriate type in the class declaration, and one or more acccessor methods. The generated class will implement the serialize and deserialize methods defined in hadoop::Record+. It will also implement the inspection methods type and signature from hadoop::Record. A default constructor and virtual destructor will also be generated. Serialization code will read/write records into streams that implement the hadoop::InStream and the hadoop::OutStream interfaces. For each member of a record an accessor method is generated that returns either the member or a reference to the member. For members that are returned by value, a setter method is also generated. This is true for primitive data members of the types byte, int, long, boolean, float and double. For example, for a int field called MyField the following code is generated.

...
private:
  int32_t mMyField;
  ...
public:
  int32_t getMyField(void) const {
    return mMyField;
  };

  void setMyField(int32_t m) {
    mMyField = m;
  };
  ...
For a ustring or buffer or composite field. The generated code only contains accessors that return a reference to the field. A const and a non-const accessor are generated. For example:

...
private:
  std::string mMyBuf;
  ...
public:

  std::string& getMyBuf() {
    return mMyBuf;
  };

  const std::string& getMyBuf() const {
    return mMyBuf;
  };
  ...

Examples

Suppose the inclrec.jr file contains:

module inclrec {
    class RI {
        int      I32;
        double   D;
        ustring  S;
    };
}
and the testrec.jr file contains:

include "inclrec.jr"
module testrec {
    class R {
        vector<float> VF;
        RI            Rec;
        buffer        Buf;
    };
}
Then the invocation of rcc such as:

$ rcc -l c++ inclrec.jr testrec.jr
will result in generation of four files: inclrec.jr.{cc,hh} and testrec.jr.{cc,hh}. The inclrec.jr.hh will contain:

#ifndef _INCLREC_JR_HH_
#define _INCLREC_JR_HH_

#include "recordio.hh"

namespace inclrec {
  
  class RI : public hadoop::Record {

  private:

    int32_t      mI32;
    double       mD;
    std::string  mS;

  public:

    RI(void);
    virtual ~RI(void);

    virtual bool operator==(const RI& peer) const;
    virtual bool operator<(const RI& peer) const;

    virtual int32_t getI32(void) const { return mI32; }
    virtual void setI32(int32_t v) { mI32 = v; }

    virtual double getD(void) const { return mD; }
    virtual void setD(double v) { mD = v; }

    virtual std::string& getS(void) const { return mS; }
    virtual const std::string& getS(void) const { return mS; }

    virtual std::string type(void) const;
    virtual std::string signature(void) const;

  protected:

    virtual void serialize(hadoop::OArchive& a) const;
    virtual void deserialize(hadoop::IArchive& a);

    virtual bool validate(void);
  };
} // end namespace inclrec

#endif /* _INCLREC_JR_HH_ */

The testrec.jr.hh file will contain:


#ifndef _TESTREC_JR_HH_
#define _TESTREC_JR_HH_

#include "inclrec.jr.hh"

namespace testrec {
  class R : public hadoop::Record {

  private:

    std::vector<float> mVF;
    inclrec::RI        mRec;
    std::string        mBuf;

  public:

    R(void);
    virtual ~R(void);

    virtual bool operator==(const R& peer) const;
    virtual bool operator<(const R& peer) const;

    virtual std::vector<float>& getVF(void) const;
    virtual const std::vector<float>& getVF(void) const;

    virtual std::string& getBuf(void) const ;
    virtual const std::string& getBuf(void) const;

    virtual inclrec::RI& getRec(void) const;
    virtual const inclrec::RI& getRec(void) const;
    
    virtual bool serialize(hadoop::OutArchive& a) const;
    virtual bool deserialize(hadoop::InArchive& a);
    
    virtual std::string type(void) const;
    virtual std::string signature(void) const;
  };
}; // end namespace testrec
#endif /* _TESTREC_JR_HH_ */

Java

Code generation for Java is similar to that for C++. A Java class is generated for each record type with private members corresponding to the fields. Getters and setters for fields are also generated. Some differences arise in the way comparison is expressed and in the mapping of modules to packages and classes to files. For equality testing, an equals method is generated for each record type. As per Java requirements a hashCode method is also generated. For comparison a compareTo method is generated for each record type. This has the semantics as defined by the Java Comparable interface, that is, the method returns a negative integer, zero, or a positive integer as the invoked object is less than, equal to, or greater than the comparison parameter. A .java file is generated per record type as opposed to per DDL file as in C++. The module declaration translates to a Java package declaration. The module name maps to an identical Java package name. In addition to this mapping, the DDL compiler creates the appropriate directory hierarchy for the package and places the generated .java files in the correct directories.

Mapping Summary


DDL Type        C++ Type            Java Type 

boolean         bool                boolean
byte            int8_t              byte
int             int32_t             int
long            int64_t             long
float           float               float
double          double              double
ustring         std::string         Text
buffer          std::string         java.io.ByteArrayOutputStream
class type      class type          class type
vector<type>    std::vector<type>   java.util.ArrayList
map<type,type>  std::map<type,type> java.util.TreeMap

Data encodings

This section describes the format of the data encodings supported by Hadoop. Currently, one data encoding is supported, namely binary.

Binary Serialization Format

The binary data encoding format is fairly dense. Serialization of composite types is simply defined as a concatenation of serializations of the constituent elements (lengths are included in vectors and maps). Composite types are serialized as follows:
  • class: Sequence of serialized members.
  • vector: The number of elements serialized as an int. Followed by a sequence of serialized elements.
  • map: The number of key value pairs serialized as an int. Followed by a sequence of serialized (key,value) pairs.
Serialization of primitives is more interesting, with a zero compression optimization for integral types and normalization to UTF-8 for strings. Primitive types are serialized as follows:
  • byte: Represented by 1 byte, as is.
  • boolean: Represented by 1-byte (0 or 1)
  • int/long: Integers and longs are serialized zero compressed. Represented as 1-byte if -120 <= value < 128. Otherwise, serialized as a sequence of 2-5 bytes for ints, 2-9 bytes for longs. The first byte represents the number of trailing bytes, N, as the negative number (-120-N). For example, the number 1024 (0x400) is represented by the byte sequence 'x86 x04 x00'. This doesn't help much for 4-byte integers but does a reasonably good job with longs without bit twiddling.
  • float/double: Serialized in IEEE 754 single and double precision format in network byte order. This is the format used by Java.
  • ustring: Serialized as 4-byte zero compressed length followed by data encoded as UTF-8. Strings are normalized to UTF-8 regardless of native language representation.
  • buffer: Serialized as a 4-byte zero compressed length followed by the raw bytes in the buffer.
apache-zookeeper-3.9.4/zookeeper-jute/src/main/resources/zookeeper.jute0100644 0000000 0000000 00000021252 15051152474 026707 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ module org.apache.zookeeper.data { class Id { ustring scheme; ustring id; } class ACL { int perms; Id id; } // information shared with the client class Stat { long czxid; // created zxid long mzxid; // last modified zxid long ctime; // created long mtime; // last modified int version; // version int cversion; // child version int aversion; // acl version long ephemeralOwner; // owner id if ephemeral, 0 otw int dataLength; //length of the data in the node int numChildren; //number of children of this node long pzxid; // last modified children } // information explicitly stored by the server persistently class StatPersisted { long czxid; // created zxid long mzxid; // last modified zxid long ctime; // created long mtime; // last modified int version; // version int cversion; // child version int aversion; // acl version long ephemeralOwner; // owner id if ephemeral, 0 otw long pzxid; // last modified children } class ClientInfo { ustring authScheme; // Authentication scheme ustring user; // user name or any other id(for example ip) } } module org.apache.zookeeper.proto { class ConnectRequest { int protocolVersion; long lastZxidSeen; int timeOut; long sessionId; buffer passwd; boolean readOnly; } class ConnectResponse { int protocolVersion; int timeOut; long sessionId; buffer passwd; boolean readOnly; } class SetWatches { long relativeZxid; vectordataWatches; vectorexistWatches; vectorchildWatches; } class SetWatches2 { long relativeZxid; vectordataWatches; vectorexistWatches; vectorchildWatches; vectorpersistentWatches; vectorpersistentRecursiveWatches; } class RequestHeader { int xid; int type; } class MultiHeader { int type; boolean done; int err; } class AuthPacket { int type; ustring scheme; buffer auth; } class ReplyHeader { int xid; long zxid; int err; } class GetDataRequest { ustring path; boolean watch; } class SetDataRequest { ustring path; buffer data; int version; } class ReconfigRequest { ustring joiningServers; ustring leavingServers; ustring newMembers; long curConfigId; } class SetDataResponse { org.apache.zookeeper.data.Stat stat; } class GetSASLRequest { buffer token; } class SetSASLRequest { buffer token; } class SetSASLResponse { buffer token; } class CreateRequest { ustring path; buffer data; vector acl; int flags; } class CreateTTLRequest { ustring path; buffer data; vector acl; int flags; long ttl; } class DeleteRequest { ustring path; int version; } class GetChildrenRequest { ustring path; boolean watch; } class GetAllChildrenNumberRequest { ustring path; } class GetChildren2Request { ustring path; boolean watch; } class CheckVersionRequest { ustring path; int version; } class GetMaxChildrenRequest { ustring path; } class GetMaxChildrenResponse { int max; } class SetMaxChildrenRequest { ustring path; int max; } class SyncRequest { ustring path; } class SyncResponse { ustring path; } class GetACLRequest { ustring path; } class SetACLRequest { ustring path; vector acl; int version; } class SetACLResponse { org.apache.zookeeper.data.Stat stat; } class AddWatchRequest { ustring path; int mode; } class WatcherEvent { int type; // event type int state; // state of the Keeper client runtime ustring path; } class ErrorResponse { int err; } class CreateResponse { ustring path; } class Create2Response { ustring path; org.apache.zookeeper.data.Stat stat; } class ExistsRequest { ustring path; boolean watch; } class ExistsResponse { org.apache.zookeeper.data.Stat stat; } class GetDataResponse { buffer data; org.apache.zookeeper.data.Stat stat; } class GetChildrenResponse { vector children; } class GetAllChildrenNumberResponse { int totalNumber; } class GetChildren2Response { vector children; org.apache.zookeeper.data.Stat stat; } class GetACLResponse { vector acl; org.apache.zookeeper.data.Stat stat; } class CheckWatchesRequest { ustring path; int type; } class RemoveWatchesRequest { ustring path; int type; } class GetEphemeralsRequest { ustring prefixPath; } class GetEphemeralsResponse { vector ephemerals; } class WhoAmIResponse { vector clientInfo; } } module org.apache.zookeeper.server.quorum { class LearnerInfo { long serverid; int protocolVersion; long configVersion; } class QuorumPacket { int type; // Request, Ack, Commit, Ping long zxid; buffer data; // Only significant when type is request vector authinfo; } class QuorumAuthPacket { long magic; int status; buffer token; } } module org.apache.zookeeper.server.persistence { class FileHeader { int magic; int version; long dbid; } } module org.apache.zookeeper.txn { class TxnDigest { int version; long treeDigest; } class TxnHeader { long clientId; int cxid; long zxid; long time; int type; } class CreateTxnV0 { ustring path; buffer data; vector acl; boolean ephemeral; } class CreateTxn { ustring path; buffer data; vector acl; boolean ephemeral; int parentCVersion; } class CreateTTLTxn { ustring path; buffer data; vector acl; int parentCVersion; long ttl; } class CreateContainerTxn { ustring path; buffer data; vector acl; int parentCVersion; } class DeleteTxn { ustring path; } class SetDataTxn { ustring path; buffer data; int version; } class CheckVersionTxn { ustring path; int version; } class SetACLTxn { ustring path; vector acl; int version; } class SetMaxChildrenTxn { ustring path; int max; } class CreateSessionTxn { int timeOut; } class CloseSessionTxn { vector paths2Delete; } class ErrorTxn { int err; } class Txn { int type; buffer data; } class MultiTxn { vector txns; } } apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/BinaryInputArchiveTest.java0100644 0000000 0000000 00000015742 15051152474 033214 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import org.junit.jupiter.api.Test; // TODO: introduce JuteTestCase as in ZKTestCase /** * */ public class BinaryInputArchiveTest { @Test public void testReadStringCheckLength() { byte[] buf = new byte[]{ Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE}; ByteArrayInputStream is = new ByteArrayInputStream(buf); BinaryInputArchive ia = BinaryInputArchive.getArchive(is); try { ia.readString(""); fail("Should have thrown an IOException"); } catch (IOException e) { assertTrue(e.getMessage().startsWith(BinaryInputArchive.UNREASONBLE_LENGTH), () -> "Not 'Unreasonable length' exception: " + e); } } private void checkWriterAndReader(TestWriter writer, TestReader reader) { TestCheckWriterReader.checkWriterAndReader( BinaryOutputArchive::getArchive, BinaryInputArchive::getArchive, writer, reader ); } @Test public void testInt() { final int expected = 4; final String tag = "tag1"; checkWriterAndReader( (oa) -> oa.writeInt(expected, tag), (ia) -> { int actual = ia.readInt(tag); assertEquals(expected, actual); } ); } @Test public void testBool() { final boolean expected = false; final String tag = "tag1"; checkWriterAndReader( (oa) -> oa.writeBool(expected, tag), (ia) -> { boolean actual = ia.readBool(tag); assertEquals(expected, actual); } ); } @Test public void testString() { final String expected = "hello"; final String tag = "tag1"; checkWriterAndReader( (oa) -> oa.writeString(expected, tag), (ia) -> { String actual = ia.readString(tag); assertEquals(expected, actual); } ); } @Test public void testFloat() { final float expected = 3.14159f; final String tag = "tag1"; final float delta = 1e-10f; checkWriterAndReader( (oa) -> oa.writeFloat(expected, tag), (ia) -> { float actual = ia.readFloat(tag); assertEquals(expected, actual, delta); } ); } @Test public void testDouble() { final double expected = 3.14159f; final String tag = "tag1"; final float delta = 1e-20f; checkWriterAndReader( (oa) -> oa.writeDouble(expected, tag), (ia) -> { double actual = ia.readDouble(tag); assertEquals(expected, actual, delta); } ); } @Test public void testBuffer() { final byte[] expected = "hello-world".getBytes(StandardCharsets.UTF_8); final String tag = "tag1"; checkWriterAndReader( (oa) -> oa.writeBuffer(expected, tag), (ia) -> { byte[] actual = ia.readBuffer(tag); assertArrayEquals(expected, actual); } ); } /** * Record length is more than the maxbuffer + extrasize length. */ @Test public void testReadStringForRecordsHavingLengthMoreThanMaxAllowedSize() { int maxBufferSize = 2000; int extraMaxBufferSize = 1025; //this record size is more than the max allowed size int recordSize = maxBufferSize + extraMaxBufferSize + 100; BinaryInputArchive ia = getBinaryInputArchive(recordSize, maxBufferSize, extraMaxBufferSize); try { ia.readString(""); fail("Should have thrown an IOException"); } catch (IOException e) { assertTrue(e.getMessage().startsWith(BinaryInputArchive.UNREASONBLE_LENGTH), () -> "Not 'Unreasonable length' exception: " + e); } } /** * Record length is less than then maxbuffer + extrasize length. */ @Test public void testReadStringForRecordsHavingLengthLessThanMaxAllowedSize() throws IOException { int maxBufferSize = 2000; int extraMaxBufferSize = 1025; int recordSize = maxBufferSize + extraMaxBufferSize - 100; //Exception is not expected as record size is less than the allowed size BinaryInputArchive ia = getBinaryInputArchive(recordSize, maxBufferSize, extraMaxBufferSize); String s = ia.readString(""); assertNotNull(s); assertEquals(recordSize, s.getBytes().length); } private BinaryInputArchive getBinaryInputArchive(int recordSize, int maxBufferSize, int extraMaxBufferSize) { byte[] data = getData(recordSize); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data)); return new BinaryInputArchive(dis, maxBufferSize, extraMaxBufferSize); } private byte[] getData(int recordSize) { ByteBuffer buf = ByteBuffer.allocate(recordSize + 4); buf.putInt(recordSize); byte[] bytes = new byte[recordSize]; for (int i = 0; i < recordSize; i++) { bytes[i] = (byte) 'a'; } buf.put(bytes); return buf.array(); } @Test public void testTotalBufferSizeShouldNotBeMoreThanIntegerMaxValue() throws IOException { int maxBufferSize = 1 * 1024 * 1024 * 1024; // buffer size 1GB int extraMaxBufferSize = maxBufferSize; int recordSize = 1000; BinaryInputArchive ia = getBinaryInputArchive(recordSize, maxBufferSize, extraMaxBufferSize); String s = ia.readString(""); assertNotNull(s); assertEquals(recordSize, s.getBytes().length); } } apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/BinaryOutputArchiveTest.java0100644 0000000 0000000 00000007544 15051152474 033416 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import org.apache.zookeeper.data.ClientInfo; import org.apache.zookeeper.proto.WhoAmIResponse; import org.junit.jupiter.api.Test; public class BinaryOutputArchiveTest { @Test public void testDataSize() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(32); BinaryOutputArchive outputArchive = BinaryOutputArchive.getArchive(baos); int dataSize = 0; checkDataSize(dataSize, baos, outputArchive); int boolSize = 1; dataSize += boolSize; outputArchive.writeBool(true, "bool"); checkDataSize(dataSize, baos, outputArchive); int byteSize = 1; dataSize += byteSize; outputArchive.writeByte(Byte.MAX_VALUE, "byte"); checkDataSize(dataSize, baos, outputArchive); int intSize = 4; dataSize += intSize; outputArchive.writeInt(1, "int"); checkDataSize(dataSize, baos, outputArchive); int longSize = 8; dataSize += longSize; outputArchive.writeLong(8L, "long"); checkDataSize(dataSize, baos, outputArchive); int stringLengthSize = 4; String str = "ab"; dataSize += stringLengthSize + str.length(); outputArchive.writeString(str, "string"); checkDataSize(dataSize, baos, outputArchive); int floatSize = 4; dataSize += floatSize; outputArchive.writeFloat(12.0f, "float"); checkDataSize(dataSize, baos, outputArchive); int doubleSize = 8; dataSize += doubleSize; outputArchive.writeDouble(12.44d, "double"); checkDataSize(dataSize, baos, outputArchive); int bytesLengthSize = 4; byte[] bytes = new byte[4]; bytes[0] = 'a'; bytes[1] = 'b'; bytes[2] = 'c'; bytes[3] = 'd'; dataSize += bytesLengthSize + bytes.length; outputArchive.writeBuffer(bytes, "bytes"); checkDataSize(dataSize, baos, outputArchive); String schema = "custom"; String user1 = "horizon"; String user2 = "zhao"; WhoAmIResponse whoAmIResponse = new WhoAmIResponse(); whoAmIResponse.setClientInfo(Arrays.asList( new ClientInfo(schema, user1), new ClientInfo(schema, user2))); int listSizeLength = 4; int clientInfo1Length = stringLengthSize + schema.length() + stringLengthSize + user1.length(); int clientInfo2Length = stringLengthSize + schema.length() + stringLengthSize + user2.length(); dataSize += listSizeLength + clientInfo1Length + clientInfo2Length; outputArchive.writeRecord(whoAmIResponse, "record"); checkDataSize(dataSize, baos, outputArchive); } private void checkDataSize(int dataSize, ByteArrayOutputStream baos, OutputArchive outputArchive) { assertEquals(dataSize, outputArchive.getDataSize()); assertEquals(baos.size(), outputArchive.getDataSize()); } } apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/TestCheckWriterReader.java0100644 0000000 0000000 00000004024 15051152474 032772 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * TestOutputArchive creates an output archive from a given outputstream. */ interface TestOutputArchive { OutputArchive getArchive(OutputStream os) throws IOException; } interface TestInputArchive { InputArchive getArchive(InputStream is) throws IOException; } class TestCheckWriterReader { static void checkWriterAndReader( TestOutputArchive output, TestInputArchive input, TestWriter writer, TestReader reader) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { OutputArchive oa = output.getArchive(baos); writer.write(oa); } catch (IOException e) { fail("Should not throw IOException while writing"); } InputStream is = new ByteArrayInputStream(baos.toByteArray()); try { InputArchive ia = input.getArchive(is); reader.read(ia); } catch (IOException e) { fail("Should not throw IOException while reading back"); } } }apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/TestReader.java0100644 0000000 0000000 00000001674 15051152474 030647 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; /** * */ public interface TestReader { void read(InputArchive ia) throws IOException; } apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/TestWriter.java0100644 0000000 0000000 00000001770 15051152474 030716 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import java.io.IOException; /** * */ public interface TestWriter { /** * Write to the given output archive. */ void write(OutputArchive oa) throws IOException; }apache-zookeeper-3.9.4/zookeeper-jute/src/test/java/org/apache/jute/ToStringOutputArchiveTest.java0100644 0000000 0000000 00000010755 15051152474 033741 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jute; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import org.apache.zookeeper.data.ClientInfo; import org.apache.zookeeper.proto.WhoAmIResponse; import org.junit.jupiter.api.Test; public class ToStringOutputArchiveTest { @Test public void testDataSize() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(32); ToStringOutputArchive outputArchive = new ToStringOutputArchive(baos); int dataSize = 0; assertEquals(dataSize, outputArchive.getDataSize()); assertEquals(dataSize, baos.size()); int boolSize = 1; dataSize += boolSize; outputArchive.writeBool(true, "bool"); checkDataSize(dataSize, baos, outputArchive); String comma = ","; byte b = Byte.MAX_VALUE; int byteSize = String.valueOf(b).length(); dataSize += comma.length() + byteSize; outputArchive.writeByte(b, "byte"); checkDataSize(dataSize, baos, outputArchive); int i = 1; int intSize = String.valueOf(i).length(); dataSize += comma.length() + intSize; outputArchive.writeInt(i, "int"); checkDataSize(dataSize, baos, outputArchive); long l = 8L; int longSize = String.valueOf(l).length(); dataSize += comma.length() + longSize; outputArchive.writeLong(l, "long"); checkDataSize(dataSize, baos, outputArchive); String apostrophe = "'"; String str = "ab"; int strSize = str.length(); dataSize += comma.length() + apostrophe.length() + strSize; outputArchive.writeString(str, "string"); checkDataSize(dataSize, baos, outputArchive); float f = 12.0f; int floatSize = String.valueOf(f).length(); dataSize += comma.length() + floatSize; outputArchive.writeFloat(f, "float"); checkDataSize(dataSize, baos, outputArchive); double d = 12.44d; int doubleSize = String.valueOf(d).length(); dataSize += comma.length() + doubleSize; outputArchive.writeDouble(d, "double"); checkDataSize(dataSize, baos, outputArchive); byte[] bytes = new byte[4]; bytes[0] = 'a'; bytes[1] = 'b'; bytes[2] = 'c'; bytes[3] = 'd'; String poundSign = "#"; int bytesSize = Integer.toHexString(bytes[0]).length() + Integer.toHexString(bytes[1]).length() + Integer.toHexString(bytes[2]).length() + Integer.toHexString(bytes[3]).length(); dataSize += comma.length() + poundSign.length() + bytesSize; outputArchive.writeBuffer(bytes, "bytes"); checkDataSize(dataSize, baos, outputArchive); String schema = "custom"; String user1 = "horizon"; String user2 = "zhao"; WhoAmIResponse whoAmIResponse = new WhoAmIResponse(); whoAmIResponse.setClientInfo(Arrays.asList( new ClientInfo(schema, user1), new ClientInfo(schema, user2))); String whoAmIResponseStr = whoAmIResponse.toString().replace("\n", ""); String startRecordSign = "s{"; String endRecordSign = "}"; dataSize += comma.length() + startRecordSign.length() + whoAmIResponseStr.length() + endRecordSign.length(); outputArchive.writeRecord(whoAmIResponse, "record"); checkDataSize(dataSize, baos, outputArchive); } private void checkDataSize(int dataSize, ByteArrayOutputStream baos, OutputArchive outputArchive) { assertEquals(dataSize, outputArchive.getDataSize()); assertEquals(baos.size(), outputArchive.getDataSize()); } } apache-zookeeper-3.9.4/zookeeper-metrics-providers/pom.xml0100755 0000000 0000000 00000003012 15051152474 024312 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-metrics-providers pom Apache ZooKeeper - Metrics Providers ZooKeeper Metrics Providers zookeeper-prometheus-metrics apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/pom.xml0100755 0000000 0000000 00000007671 15051152474 032171 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-metrics-providers 3.9.4 zookeeper-prometheus-metrics jar Apache ZooKeeper - Prometheus.io Metrics Provider ZooKeeper Prometheus.io Metrics Provider implementation 0.9.0 org.apache.zookeeper zookeeper ${project.version} io.prometheus simpleclient ${prometheus.version} io.prometheus simpleclient_hotspot ${prometheus.version} io.prometheus simpleclient_servlet ${prometheus.version} org.eclipse.jetty jetty-server provided org.eclipse.jetty jetty-servlet provided org.mockito mockito-core test org.junit.jupiter junit-jupiter-engine test org.junit.platform junit-platform-runner test org.apache.maven.plugins maven-dependency-plugin copy-dependencies package copy-dependencies ${project.build.directory}/lib false true false ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000256 15051152474 032621 xustar000000000 0000000 174 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProvider.java apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/main/java/org/ap0100644 0000000 0000000 00000060375 15051152474 034417 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.prometheus; import io.prometheus.client.Collector; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.MetricsServlet; import io.prometheus.client.hotspot.DefaultExports; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Enumeration; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.metrics.Counter; import org.apache.zookeeper.metrics.CounterSet; import org.apache.zookeeper.metrics.Gauge; import org.apache.zookeeper.metrics.GaugeSet; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; import org.apache.zookeeper.server.RateLogger; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.security.Constraint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A Metrics Provider implementation based on https://prometheus.io. * * @since 3.6.0 */ public class PrometheusMetricsProvider implements MetricsProvider { private static final Logger LOG = LoggerFactory.getLogger(PrometheusMetricsProvider.class); private static final String LABEL = "key"; private static final String[] LABELS = {LABEL}; /** * Number of worker threads for reporting Prometheus summary metrics. * Default value is 1. * If the number is less than 1, the main thread will be used. */ static final String NUM_WORKER_THREADS = "numWorkerThreads"; /** * The max queue size for Prometheus summary metrics reporting task. * Default value is 1000000. */ static final String MAX_QUEUE_SIZE = "maxQueueSize"; /** * The timeout in ms for Prometheus worker threads shutdown. * Default value is 1000ms. */ static final String WORKER_SHUTDOWN_TIMEOUT_MS = "workerShutdownTimeoutMs"; /** * We are using the 'defaultRegistry'. *

* When you are running ZooKeeper (server or client) together with other * libraries every metrics will be expected as a single view. *

*/ private final CollectorRegistry collectorRegistry = CollectorRegistry.defaultRegistry; private final RateLogger rateLogger = new RateLogger(LOG, 60 * 1000); private String host = "0.0.0.0"; private int port = 7000; private boolean exportJvmInfo = true; private Server server; private final MetricsServletImpl servlet = new MetricsServletImpl(); private final Context rootContext = new Context(); private int numWorkerThreads = 1; private int maxQueueSize = 1000000; private long workerShutdownTimeoutMs = 1000; private Optional executorOptional = Optional.empty(); @Override public void configure(Properties configuration) throws MetricsProviderLifeCycleException { LOG.info("Initializing metrics, configuration: {}", configuration); this.host = configuration.getProperty("httpHost", "0.0.0.0"); this.port = Integer.parseInt(configuration.getProperty("httpPort", "7000")); this.exportJvmInfo = Boolean.parseBoolean(configuration.getProperty("exportJvmInfo", "true")); this.numWorkerThreads = Integer.parseInt( configuration.getProperty(NUM_WORKER_THREADS, "1")); this.maxQueueSize = Integer.parseInt( configuration.getProperty(MAX_QUEUE_SIZE, "1000000")); this.workerShutdownTimeoutMs = Long.parseLong( configuration.getProperty(WORKER_SHUTDOWN_TIMEOUT_MS, "1000")); } @Override public void start() throws MetricsProviderLifeCycleException { this.executorOptional = createExecutor(); try { LOG.info("Starting /metrics HTTP endpoint at host: {}, port: {}, exportJvmInfo: {}", host, port, exportJvmInfo); if (exportJvmInfo) { DefaultExports.initialize(); } server = new Server(new InetSocketAddress(host, port)); ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); constrainTraceMethod(context); server.setHandler(context); context.addServlet(new ServletHolder(servlet), "/metrics"); server.start(); } catch (Exception err) { LOG.error("Cannot start /metrics server", err); if (server != null) { try { server.stop(); } catch (Exception suppressed) { err.addSuppressed(suppressed); } finally { server = null; } } throw new MetricsProviderLifeCycleException(err); } } // for tests MetricsServletImpl getServlet() { return servlet; } @Override public MetricsContext getRootContext() { return rootContext; } @Override public void stop() { shutdownExecutor(); if (server != null) { try { server.stop(); } catch (Exception err) { LOG.error("Cannot safely stop Jetty server", err); } finally { server = null; } } } /** * Dump all values to the 4lw interface and to the Admin server. *

* This method is not expected to be used to serve metrics to Prometheus. We * are using the MetricsServlet provided by Prometheus for that, leaving the * real representation to the Prometheus Java client. *

* * @param sink the receiver of data (4lw interface, Admin server or tests) */ @Override public void dump(BiConsumer sink) { sampleGauges(); Enumeration samplesFamilies = collectorRegistry.metricFamilySamples(); while (samplesFamilies.hasMoreElements()) { Collector.MetricFamilySamples samples = samplesFamilies.nextElement(); samples.samples.forEach(sample -> { String key = buildKeyForDump(sample); sink.accept(key, sample.value); }); } } private static String buildKeyForDump(Collector.MetricFamilySamples.Sample sample) { StringBuilder keyBuilder = new StringBuilder(); keyBuilder.append(sample.name); if (sample.labelNames.size() > 0) { keyBuilder.append('{'); for (int i = 0; i < sample.labelNames.size(); ++i) { if (i > 0) { keyBuilder.append(','); } keyBuilder.append(sample.labelNames.get(i)); keyBuilder.append("=\""); keyBuilder.append(sample.labelValues.get(i)); keyBuilder.append('"'); } keyBuilder.append('}'); } return keyBuilder.toString(); } /** * Update Gauges. In ZooKeeper Metrics API Gauges are callbacks served by * internal components and the value is not held by Prometheus structures. */ private void sampleGauges() { rootContext.gauges.values() .forEach(PrometheusGaugeWrapper::sample); rootContext.gaugeSets.values() .forEach(PrometheusLabelledGaugeWrapper::sample); } @Override public void resetAllValues() { // not supported on Prometheus } /** * Add constraint to a given context to disallow TRACE method. * @param ctxHandler the context to modify */ private void constrainTraceMethod(ServletContextHandler ctxHandler) { Constraint c = new Constraint(); c.setAuthenticate(true); ConstraintMapping cmt = new ConstraintMapping(); cmt.setConstraint(c); cmt.setMethod("TRACE"); cmt.setPathSpec("/*"); ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); securityHandler.setConstraintMappings(new ConstraintMapping[] {cmt}); ctxHandler.setSecurityHandler(securityHandler); } private class Context implements MetricsContext { private final ConcurrentMap gauges = new ConcurrentHashMap<>(); private final ConcurrentMap gaugeSets = new ConcurrentHashMap<>(); private final ConcurrentMap counters = new ConcurrentHashMap<>(); private final ConcurrentMap counterSets = new ConcurrentHashMap<>(); private final ConcurrentMap basicSummaries = new ConcurrentHashMap<>(); private final ConcurrentMap summaries = new ConcurrentHashMap<>(); private final ConcurrentMap basicSummarySets = new ConcurrentHashMap<>(); private final ConcurrentMap summarySets = new ConcurrentHashMap<>(); @Override public MetricsContext getContext(String name) { // no hierarchy yet return this; } @Override public Counter getCounter(String name) { return counters.computeIfAbsent(name, PrometheusCounter::new); } @Override public CounterSet getCounterSet(final String name) { Objects.requireNonNull(name, "Cannot register a CounterSet with null name"); return counterSets.computeIfAbsent(name, PrometheusLabelledCounter::new); } /** * Gauges may go up and down, in ZooKeeper they are a way to export * internal values with a callback. * * @param name the name of the gauge * @param gauge the callback */ @Override public void registerGauge(String name, Gauge gauge) { Objects.requireNonNull(name); gauges.compute(name, (id, prev) -> new PrometheusGaugeWrapper(id, gauge, prev != null ? prev.inner : null)); } @Override public void unregisterGauge(String name) { PrometheusGaugeWrapper existing = gauges.remove(name); if (existing != null) { existing.unregister(); } } @Override public void registerGaugeSet(final String name, final GaugeSet gaugeSet) { Objects.requireNonNull(name, "Cannot register a GaugeSet with null name"); Objects.requireNonNull(gaugeSet, "Cannot register a null GaugeSet for " + name); gaugeSets.compute(name, (id, prev) -> new PrometheusLabelledGaugeWrapper(name, gaugeSet, prev != null ? prev.inner : null)); } @Override public void unregisterGaugeSet(final String name) { Objects.requireNonNull(name, "Cannot unregister GaugeSet with null name"); final PrometheusLabelledGaugeWrapper existing = gaugeSets.remove(name); if (existing != null) { existing.unregister(); } } @Override public Summary getSummary(String name, DetailLevel detailLevel) { if (detailLevel == DetailLevel.BASIC) { return basicSummaries.computeIfAbsent(name, (n) -> { if (summaries.containsKey(n)) { throw new IllegalArgumentException("Already registered a non basic summary as " + n); } return new PrometheusSummary(name, detailLevel); }); } else { return summaries.computeIfAbsent(name, (n) -> { if (basicSummaries.containsKey(n)) { throw new IllegalArgumentException("Already registered a basic summary as " + n); } return new PrometheusSummary(name, detailLevel); }); } } @Override public SummarySet getSummarySet(String name, DetailLevel detailLevel) { if (detailLevel == DetailLevel.BASIC) { return basicSummarySets.computeIfAbsent(name, (n) -> { if (summarySets.containsKey(n)) { throw new IllegalArgumentException("Already registered a non basic summary set as " + n); } return new PrometheusLabelledSummary(name, detailLevel); }); } else { return summarySets.computeIfAbsent(name, (n) -> { if (basicSummarySets.containsKey(n)) { throw new IllegalArgumentException("Already registered a basic summary set as " + n); } return new PrometheusLabelledSummary(name, detailLevel); }); } } } private class PrometheusCounter implements Counter { private final io.prometheus.client.Counter inner; private final String name; public PrometheusCounter(String name) { this.name = name; this.inner = io.prometheus.client.Counter .build(name, name) .register(collectorRegistry); } @Override public void add(long delta) { try { inner.inc(delta); } catch (IllegalArgumentException err) { LOG.error("invalid delta {} for metric {}", delta, name, err); } } @Override public long get() { // this method is used only for tests // Prometheus returns a "double" // it is safe to fine to a long // we are never setting non-integer values return (long) inner.get(); } } private class PrometheusLabelledCounter implements CounterSet { private final String name; private final io.prometheus.client.Counter inner; public PrometheusLabelledCounter(final String name) { this.name = name; this.inner = io.prometheus.client.Counter .build(name, name) .labelNames(LABELS) .register(collectorRegistry); } @Override public void add(final String key, final long delta) { try { inner.labels(key).inc(delta); } catch (final IllegalArgumentException e) { LOG.error("invalid delta {} for metric {} with key {}", delta, name, key, e); } } } private class PrometheusGaugeWrapper { private final io.prometheus.client.Gauge inner; private final Gauge gauge; private final String name; public PrometheusGaugeWrapper(String name, Gauge gauge, io.prometheus.client.Gauge prev) { this.name = name; this.gauge = gauge; this.inner = prev != null ? prev : io.prometheus.client.Gauge .build(name, name) .register(collectorRegistry); } /** * Call the callack and update Prometheus Gauge. This method is called * when the server is polling for a value. */ private void sample() { Number value = gauge.get(); this.inner.set(value != null ? value.doubleValue() : 0); } private void unregister() { collectorRegistry.unregister(inner); } } /** * Prometheus implementation of GaugeSet interface. It wraps the GaugeSet object and * uses the callback API to update the Prometheus Gauge. */ private class PrometheusLabelledGaugeWrapper { private final GaugeSet gaugeSet; private final io.prometheus.client.Gauge inner; private PrometheusLabelledGaugeWrapper(final String name, final GaugeSet gaugeSet, final io.prometheus.client.Gauge prev) { this.gaugeSet = gaugeSet; this.inner = prev != null ? prev : io.prometheus.client.Gauge .build(name, name) .labelNames(LABELS) .register(collectorRegistry); } /** * Call the callback provided by the GaugeSet and update Prometheus Gauge. * This method is called when the server is polling for a value. */ private void sample() { gaugeSet.values().forEach((key, value) -> this.inner.labels(key).set(value != null ? value.doubleValue() : 0)); } private void unregister() { collectorRegistry.unregister(inner); } } private class PrometheusSummary implements Summary { private final io.prometheus.client.Summary inner; private final String name; public PrometheusSummary(String name, MetricsContext.DetailLevel level) { this.name = name; if (level == MetricsContext.DetailLevel.ADVANCED) { this.inner = io.prometheus.client.Summary .build(name, name) .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error .register(collectorRegistry); } else { this.inner = io.prometheus.client.Summary .build(name, name) .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error .register(collectorRegistry); } } @Override public void add(long delta) { reportMetrics(() -> observe(delta)); } private void observe(final long delta) { try { inner.observe(delta); } catch (final IllegalArgumentException err) { LOG.error("invalid delta {} for metric {}", delta, name, err); } } } private class PrometheusLabelledSummary implements SummarySet { private final io.prometheus.client.Summary inner; private final String name; public PrometheusLabelledSummary(String name, MetricsContext.DetailLevel level) { this.name = name; if (level == MetricsContext.DetailLevel.ADVANCED) { this.inner = io.prometheus.client.Summary .build(name, name) .labelNames(LABELS) .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error .register(collectorRegistry); } else { this.inner = io.prometheus.client.Summary .build(name, name) .labelNames(LABELS) .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error .register(collectorRegistry); } } @Override public void add(String key, long value) { reportMetrics(() -> observe(key, value)); } private void observe(final String key, final long value) { try { inner.labels(key).observe(value); } catch (final IllegalArgumentException err) { LOG.error("invalid value {} for metric {} with key {}", value, name, key, err); } } } class MetricsServletImpl extends MetricsServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // little trick: update the Gauges before serving data // from Prometheus CollectorRegistry sampleGauges(); // serve data using Prometheus built in client. super.doGet(req, resp); } } private Optional createExecutor() { if (numWorkerThreads < 1) { LOG.info("Executor service was not created as numWorkerThreads {} is less than 1", numWorkerThreads); return Optional.empty(); } final BlockingQueue queue = new LinkedBlockingQueue<>(maxQueueSize); final ThreadPoolExecutor executor = new ThreadPoolExecutor(numWorkerThreads, numWorkerThreads, 0L, TimeUnit.MILLISECONDS, queue, new PrometheusWorkerThreadFactory()); LOG.info("Executor service was created with numWorkerThreads {} and maxQueueSize {}", numWorkerThreads, maxQueueSize); return Optional.of(executor); } private void shutdownExecutor() { if (executorOptional.isPresent()) { LOG.info("Shutdown executor service with timeout {}", workerShutdownTimeoutMs); final ExecutorService executor = executorOptional.get(); executor.shutdown(); try { if (!executor.awaitTermination(workerShutdownTimeoutMs, TimeUnit.MILLISECONDS)) { LOG.error("Not all the Prometheus worker threads terminated properly after {} timeout", workerShutdownTimeoutMs); executor.shutdownNow(); } } catch (final Exception e) { LOG.error("Error occurred while terminating Prometheus worker threads", e); executor.shutdownNow(); } } } private static class PrometheusWorkerThreadFactory implements ThreadFactory { private static final AtomicInteger workerCounter = new AtomicInteger(1); @Override public Thread newThread(final Runnable runnable) { final String threadName = "PrometheusMetricsProviderWorker-" + workerCounter.getAndIncrement(); final Thread thread = new Thread(runnable, threadName); thread.setDaemon(true); return thread; } } private void reportMetrics(final Runnable task) { if (executorOptional.isPresent()) { try { executorOptional.get().submit(task); } catch (final RejectedExecutionException e) { rateLogger.rateLimitLog("Prometheus metrics reporting task queue size exceeded the max", String.valueOf(maxQueueSize)); } } else { task.run(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000246 15051152474 032620 xustar000000000 0000000 166 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/ExportJvmInfoTest.java apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0100644 0000000 0000000 00000004126 15051152474 034442 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.prometheus; import static org.junit.jupiter.api.Assertions.assertEquals; import io.prometheus.client.CollectorRegistry; import java.util.Properties; import org.junit.jupiter.api.Test; /** * Tests about Prometheus Metrics Provider. Please note that we are not testing * Prometheus but our integration. */ public class ExportJvmInfoTest { @Test public void exportInfo() throws Exception { runTest(true); } @Test public void doNotExportInfo() throws Exception { runTest(false); } private void runTest(boolean exportJvmInfo) throws Exception { CollectorRegistry.defaultRegistry.clear(); PrometheusMetricsProvider provider = new PrometheusMetricsProvider(); try { Properties configuration = new Properties(); configuration.setProperty("httpPort", "0"); // ephemeral port configuration.setProperty("exportJvmInfo", exportJvmInfo + ""); provider.configure(configuration); provider.start(); boolean[] found = {false}; provider.dump((k, v) -> { found[0] = found[0] || k.contains("heap"); }); assertEquals(exportJvmInfo, found[0]); } finally { provider.stop(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000270 15051152474 032615 xustar000000000 0000000 184 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProviderConfigTest.java apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0100644 0000000 0000000 00000005017 15051152474 034442 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.prometheus; import io.prometheus.client.CollectorRegistry; import java.util.Properties; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.junit.Assert; import org.junit.Test; public class PrometheusMetricsProviderConfigTest { @Test public void testInvalidPort() { Assert.assertThrows(MetricsProviderLifeCycleException.class, () -> { CollectorRegistry.defaultRegistry.clear(); PrometheusMetricsProvider provider = new PrometheusMetricsProvider(); Properties configuration = new Properties(); configuration.setProperty("httpPort", "65536"); configuration.setProperty("exportJvmInfo", "false"); provider.configure(configuration); provider.start(); }); } @Test public void testInvalidAddr() { Assert.assertThrows(MetricsProviderLifeCycleException.class, () -> { CollectorRegistry.defaultRegistry.clear(); PrometheusMetricsProvider provider = new PrometheusMetricsProvider(); Properties configuration = new Properties(); configuration.setProperty("httpHost", "master"); provider.configure(configuration); provider.start(); }); } @Test public void testValidConfig() throws MetricsProviderLifeCycleException { CollectorRegistry.defaultRegistry.clear(); PrometheusMetricsProvider provider = new PrometheusMetricsProvider(); Properties configuration = new Properties(); configuration.setProperty("httpHost", "0.0.0.0"); configuration.setProperty("httpPort", "0"); provider.configure(configuration); provider.start(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000262 15051152474 032616 xustar000000000 0000000 178 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/apache/zookeeper/metrics/prometheus/PrometheusMetricsProviderTest.java apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/java/org/ap0100644 0000000 0000000 00000065630 15051152474 034451 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.prometheus; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import io.prometheus.client.CollectorRegistry; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.metrics.Counter; import org.apache.zookeeper.metrics.CounterSet; import org.apache.zookeeper.metrics.Gauge; import org.apache.zookeeper.metrics.GaugeSet; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; import org.apache.zookeeper.server.util.QuotaMetricsUtils; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests about Prometheus Metrics Provider. Please note that we are not testing * Prometheus but only our integration. */ public class PrometheusMetricsProviderTest { private static final String URL_FORMAT = "http://localhost:%d/metrics"; private PrometheusMetricsProvider provider; @BeforeEach public void setup() throws Exception { CollectorRegistry.defaultRegistry.clear(); provider = new PrometheusMetricsProvider(); Properties configuration = new Properties(); configuration.setProperty("numWorkerThreads", "0"); // sync behavior for test configuration.setProperty("httpHost", "127.0.0.1"); // local host for test configuration.setProperty("httpPort", "0"); // ephemeral port configuration.setProperty("exportJvmInfo", "false"); provider.configure(configuration); provider.start(); } @AfterEach public void tearDown() { if (provider != null) { provider.stop(); } CollectorRegistry.defaultRegistry.clear(); } @Test public void testCounters() throws Exception { Counter counter = provider.getRootContext().getCounter("cc"); counter.add(10); int[] count = {0}; provider.dump((k, v) -> { assertEquals("cc", k); assertEquals(10, ((Number) v).intValue()); count[0]++; } ); assertEquals(1, count[0]); count[0] = 0; // this is not allowed but it must not throw errors counter.add(-1); provider.dump((k, v) -> { assertEquals("cc", k); assertEquals(10, ((Number) v).intValue()); count[0]++; } ); assertEquals(1, count[0]); // we always must get the same object assertSame(counter, provider.getRootContext().getCounter("cc")); String res = callServlet(); assertThat(res, CoreMatchers.containsString("# TYPE cc counter")); assertThat(res, CoreMatchers.containsString("cc 10.0")); } @Test public void testCounterSet_single() throws Exception { // create and register a CounterSet final String name = QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; final CounterSet counterSet = provider.getRootContext().getCounterSet(name); final String[] keys = {"ns1", "ns2"}; final int count = 3; // update the CounterSet multiple times for (int i = 0; i < count; i++) { Arrays.asList(keys).forEach(key -> counterSet.inc(key)); Arrays.asList(keys).forEach(key -> counterSet.add(key, 2)); } // validate with dump call final Map expectedMetricsMap = new HashMap<>(); for (final String key : keys) { expectedMetricsMap.put(String.format("%s{key=\"%s\"}", name, key), count * 3.0); } validateWithDump(expectedMetricsMap); // validate with servlet call final List expectedNames = Collections.singletonList(String.format("# TYPE %s count", name)); final List expectedMetrics = new ArrayList<>(); for (final String key : keys) { expectedMetrics.add(String.format("%s{key=\"%s\",} %s", name, key, count * 3.0)); } validateWithServletCall(expectedNames, expectedMetrics); // validate registering with same name, no overwriting assertSame(counterSet, provider.getRootContext().getCounterSet(name)); } @Test public void testCounterSet_multiple() throws Exception { final String name = QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; final String[] names = new String[]{name + "_1", name + "_2"}; final String[] keys = new String[]{"ns21", "ns22"}; final int[] counts = new int[] {3, 5}; final int length = names.length; final CounterSet[] counterSets = new CounterSet[length]; // create and register the CounterSets for (int i = 0; i < length; i++) { counterSets[i] = provider.getRootContext().getCounterSet(names[i]); } // update each CounterSet multiple times for (int i = 0; i < length; i++) { for (int j = 0; j < counts[i]; j++) { counterSets[i].inc(keys[i]); } } // validate with dump call final Map expectedMetricsMap = new HashMap<>(); for (int i = 0; i < length; i++) { expectedMetricsMap.put(String.format("%s{key=\"%s\"}", names[i], keys[i]), counts[i] * 1.0); } validateWithDump(expectedMetricsMap); // validate with servlet call final List expectedNames = new ArrayList<>(); final List expectedMetrics = new ArrayList<>(); for (int i = 0; i < length; i++) { expectedNames.add(String.format("# TYPE %s count", names[i])); expectedMetrics.add(String.format("%s{key=\"%s\",} %s", names[i], keys[i], counts[i] * 1.0)); } validateWithServletCall(expectedNames, expectedMetrics); } @Test public void testCounterSet_registerWithNullName() { assertThrows(NullPointerException.class, () -> provider.getRootContext().getCounterSet(null)); } @Test public void testCounterSet_negativeValue() { // create and register a CounterSet final String name = QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; final CounterSet counterSet = provider.getRootContext().getCounterSet(name); // add negative value and make sure no exception is thrown counterSet.add("ns1", -1); } @Test public void testCounterSet_nullKey() { // create and register a CounterSet final String name = QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; final CounterSet counterSet = provider.getRootContext().getCounterSet(name); // increment the count with null key and make sure no exception is thrown counterSet.inc(null); counterSet.add(null, 2); } @Test public void testGauge() throws Exception { int[] values = {78, -89}; int[] callCounts = {0, 0}; Gauge gauge0 = () -> { callCounts[0]++; return values[0]; }; Gauge gauge1 = () -> { callCounts[1]++; return values[1]; }; provider.getRootContext().registerGauge("gg", gauge0); int[] count = {0}; provider.dump((k, v) -> { assertEquals("gg", k); assertEquals(values[0], ((Number) v).intValue()); count[0]++; } ); assertEquals(1, callCounts[0]); assertEquals(0, callCounts[1]); assertEquals(1, count[0]); count[0] = 0; String res2 = callServlet(); assertThat(res2, CoreMatchers.containsString("# TYPE gg gauge")); assertThat(res2, CoreMatchers.containsString("gg 78.0")); provider.getRootContext().unregisterGauge("gg"); provider.dump((k, v) -> { count[0]++; } ); assertEquals(2, callCounts[0]); assertEquals(0, callCounts[1]); assertEquals(0, count[0]); String res3 = callServlet(); assertTrue(res3.isEmpty()); provider.getRootContext().registerGauge("gg", gauge1); provider.dump((k, v) -> { assertEquals("gg", k); assertEquals(values[1], ((Number) v).intValue()); count[0]++; } ); assertEquals(2, callCounts[0]); assertEquals(1, callCounts[1]); assertEquals(1, count[0]); count[0] = 0; String res4 = callServlet(); assertThat(res4, CoreMatchers.containsString("# TYPE gg gauge")); assertThat(res4, CoreMatchers.containsString("gg -89.0")); assertEquals(2, callCounts[0]); // the servlet must sample the value again (from gauge1) assertEquals(2, callCounts[1]); // override gauge, without unregister provider.getRootContext().registerGauge("gg", gauge0); provider.dump((k, v) -> { count[0]++; } ); assertEquals(1, count[0]); assertEquals(3, callCounts[0]); assertEquals(2, callCounts[1]); } @Test public void testBasicSummary() throws Exception { Summary summary = provider.getRootContext() .getSummary("cc", MetricsContext.DetailLevel.BASIC); summary.add(10); summary.add(10); int[] count = {0}; provider.dump((k, v) -> { count[0]++; int value = ((Number) v).intValue(); switch (k) { case "cc{quantile=\"0.5\"}": assertEquals(10, value); break; case "cc_count": assertEquals(2, value); break; case "cc_sum": assertEquals(20, value); break; default: fail("unespected key " + k); break; } } ); assertEquals(3, count[0]); count[0] = 0; // we always must get the same object assertSame(summary, provider.getRootContext() .getSummary("cc", MetricsContext.DetailLevel.BASIC)); try { provider.getRootContext() .getSummary("cc", MetricsContext.DetailLevel.ADVANCED); fail("Can't get the same summary with a different DetailLevel"); } catch (IllegalArgumentException err) { assertThat(err.getMessage(), containsString("Already registered")); } String res = callServlet(); assertThat(res, containsString("# TYPE cc summary")); assertThat(res, CoreMatchers.containsString("cc_sum 20.0")); assertThat(res, CoreMatchers.containsString("cc_count 2.0")); assertThat(res, CoreMatchers.containsString("cc{quantile=\"0.5\",} 10.0")); } @Test public void testAdvancedSummary() throws Exception { Summary summary = provider.getRootContext() .getSummary("cc", MetricsContext.DetailLevel.ADVANCED); summary.add(10); summary.add(10); int[] count = {0}; provider.dump((k, v) -> { count[0]++; int value = ((Number) v).intValue(); switch (k) { case "cc{quantile=\"0.5\"}": assertEquals(10, value); break; case "cc{quantile=\"0.9\"}": assertEquals(10, value); break; case "cc{quantile=\"0.99\"}": assertEquals(10, value); break; case "cc_count": assertEquals(2, value); break; case "cc_sum": assertEquals(20, value); break; default: fail("unespected key " + k); break; } } ); assertEquals(5, count[0]); count[0] = 0; // we always must get the same object assertSame(summary, provider.getRootContext() .getSummary("cc", MetricsContext.DetailLevel.ADVANCED)); try { provider.getRootContext() .getSummary("cc", MetricsContext.DetailLevel.BASIC); fail("Can't get the same summary with a different DetailLevel"); } catch (IllegalArgumentException err) { assertThat(err.getMessage(), containsString("Already registered")); } String res = callServlet(); assertThat(res, containsString("# TYPE cc summary")); assertThat(res, CoreMatchers.containsString("cc_sum 20.0")); assertThat(res, CoreMatchers.containsString("cc_count 2.0")); assertThat(res, CoreMatchers.containsString("cc{quantile=\"0.5\",} 10.0")); assertThat(res, CoreMatchers.containsString("cc{quantile=\"0.9\",} 10.0")); assertThat(res, CoreMatchers.containsString("cc{quantile=\"0.99\",} 10.0")); } /** * Using TRACE method to visit metrics provider, the response should be 403 forbidden. */ @Test public void testTraceCall() throws IOException, IllegalAccessException, NoSuchFieldException { Field privateServerField = provider.getClass().getDeclaredField("server"); privateServerField.setAccessible(true); Server server = (Server) privateServerField.get(provider); int port = ((ServerConnector) server.getConnectors()[0]).getLocalPort(); String metricsUrl = String.format(URL_FORMAT, port); HttpURLConnection conn = (HttpURLConnection) new URL(metricsUrl).openConnection(); conn.setRequestMethod("TRACE"); conn.connect(); Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN, conn.getResponseCode()); } @Test public void testSummary_asyncAndExceedMaxQueueSize() throws Exception { final Properties config = new Properties(); config.setProperty("numWorkerThreads", "1"); config.setProperty("maxQueueSize", "1"); config.setProperty("httpPort", "0"); // ephemeral port config.setProperty("exportJvmInfo", "false"); PrometheusMetricsProvider metricsProvider = null; try { metricsProvider = new PrometheusMetricsProvider(); metricsProvider.configure(config); metricsProvider.start(); final Summary summary = metricsProvider.getRootContext().getSummary("cc", MetricsContext.DetailLevel.ADVANCED); // make sure no error is thrown for (int i = 0; i < 10; i++) { summary.add(10); } } finally { if (metricsProvider != null) { metricsProvider.stop(); } } } @Test public void testSummarySet() throws Exception { final String name = "ss"; final String[] keys = {"ns1", "ns2"}; final double count = 3.0; // create and register a SummarySet final SummarySet summarySet = provider.getRootContext() .getSummarySet(name, MetricsContext.DetailLevel.BASIC); // update the SummarySet multiple times for (int i = 0; i < count; i++) { Arrays.asList(keys).forEach(key -> summarySet.add(key, 1)); } // validate with dump call final Map expectedMetricsMap = new HashMap<>(); for (final String key : keys) { expectedMetricsMap.put(String.format("%s{key=\"%s\",quantile=\"0.5\"}", name, key), 1.0); expectedMetricsMap.put(String.format("%s_count{key=\"%s\"}", name, key), count); expectedMetricsMap.put(String.format("%s_sum{key=\"%s\"}", name, key), count); } validateWithDump(expectedMetricsMap); // validate with servlet call final List expectedNames = Collections.singletonList(String.format("# TYPE %s summary", name)); final List expectedMetrics = new ArrayList<>(); for (final String key : keys) { expectedMetrics.add(String.format("%s{key=\"%s\",quantile=\"0.5\",} %s", name, key, 1.0)); expectedMetrics.add(String.format("%s_count{key=\"%s\",} %s", name, key, count)); expectedMetrics.add(String.format("%s_sum{key=\"%s\",} %s", name, key, count)); } validateWithServletCall(expectedNames, expectedMetrics); // validate registering with same name, no overwriting assertSame(summarySet, provider.getRootContext() .getSummarySet(name, MetricsContext.DetailLevel.BASIC)); // validate registering with different DetailLevel, not allowed try { provider.getRootContext() .getSummarySet(name, MetricsContext.DetailLevel.ADVANCED); fail("Can't get the same summarySet with a different DetailLevel"); } catch (final IllegalArgumentException e) { assertThat(e.getMessage(), containsString("Already registered")); } } private String callServlet() throws ServletException, IOException { // we are not performing an HTTP request // but we are calling directly the servlet StringWriter writer = new StringWriter(); HttpServletResponse response = mock(HttpServletResponse.class); when(response.getWriter()).thenReturn(new PrintWriter(writer)); HttpServletRequest req = mock(HttpServletRequest.class); provider.getServlet().doGet(req, response); String res = writer.toString(); return res; } @Test public void testGaugeSet_singleGaugeSet() throws Exception { final String name = QuotaMetricsUtils.QUOTA_BYTES_LIMIT_PER_NAMESPACE; final Number[] values = {10.0, 100.0}; final String[] keys = {"ns11", "ns12"}; final Map metricsMap = new HashMap<>(); for (int i = 0; i < values.length; i++) { metricsMap.put(keys[i], values[i]); } final AtomicInteger callCount = new AtomicInteger(0); // create and register GaugeSet createAndRegisterGaugeSet(name, metricsMap, callCount); // validate with dump call final Map expectedMetricsMap = new HashMap<>(); for (int i = 0; i < values.length; i++) { expectedMetricsMap.put(String.format("%s{key=\"%s\"}", name, keys[i]), values[i]); } validateWithDump(expectedMetricsMap); assertEquals(1, callCount.get()); // validate with servlet call final List expectedNames = Collections.singletonList(String.format("# TYPE %s gauge", name)); final List expectedMetrics = new ArrayList<>(); for (int i = 0; i < values.length; i++) { expectedMetrics.add(String.format("%s{key=\"%s\",} %s", name, keys[i], values[i])); } validateWithServletCall(expectedNames, expectedMetrics); assertEquals(2, callCount.get()); // unregister the GaugeSet callCount.set(0); provider.getRootContext().unregisterGaugeSet(name); // validate with dump call validateWithDump(Collections.emptyMap()); assertEquals(0, callCount.get()); // validate with servlet call validateWithServletCall(new ArrayList<>(), new ArrayList<>()); assertEquals(0, callCount.get()); } @Test public void testGaugeSet_multipleGaugeSets() throws Exception { final String[] names = new String[] { QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE, QuotaMetricsUtils.QUOTA_COUNT_USAGE_PER_NAMESPACE }; final Number[] values = new Number[] {20.0, 200.0}; final String[] keys = new String[]{"ns21", "ns22"}; final int count = names.length; final AtomicInteger[] callCounts = new AtomicInteger[count]; // create and register the GaugeSets for (int i = 0; i < count; i++) { final Map metricsMap = new HashMap<>(); metricsMap.put(keys[i], values[i]); callCounts[i] = new AtomicInteger(0); createAndRegisterGaugeSet(names[i], metricsMap, callCounts[i]); } // validate with dump call final Map expectedMetricsMap = new HashMap<>(); for (int i = 0; i < count; i++) { expectedMetricsMap.put(String.format("%s{key=\"%s\"}", names[i], keys[i]), values[i]); } validateWithDump(expectedMetricsMap); for (int i = 0; i < count; i++) { assertEquals(1, callCounts[i].get()); } // validate with servlet call final List expectedNames = new ArrayList<>(); final List expectedMetrics = new ArrayList<>(); for (int i = 0; i < count; i++) { expectedNames.add(String.format("# TYPE %s gauge", names[i])); expectedMetrics.add(String.format("%s{key=\"%s\",} %s", names[i], keys[i], values[i])); } validateWithServletCall(expectedNames, expectedMetrics); for (int i = 0; i < count; i++) { assertEquals(2, callCounts[i].get()); } // unregister the GaugeSets for (int i = 0; i < count; i++) { callCounts[i].set(0); provider.getRootContext().unregisterGaugeSet(names[i]); } // validate with dump call validateWithDump(Collections.emptyMap()); for (int i = 0; i < count; i++) { assertEquals(0, callCounts[i].get()); } // validate with servlet call validateWithServletCall(new ArrayList<>(), new ArrayList<>()); for (int i = 0; i < count; i++) { assertEquals(0, callCounts[i].get()); } } @Test public void testGaugeSet_overwriteRegister() { final String[] names = new String[] { QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE, QuotaMetricsUtils.QUOTA_COUNT_USAGE_PER_NAMESPACE }; final int count = names.length; final Number[] values = new Number[]{30.0, 300.0}; final String[] keys = new String[] {"ns31", "ns32"}; final AtomicInteger[] callCounts = new AtomicInteger[count]; // create and register the GaugeSets for (int i = 0; i < count; i++) { final Map metricsMap = new HashMap<>(); metricsMap.put(keys[i], values[i]); callCounts[i] = new AtomicInteger(0); // use the same name so the first GaugeSet got overwrite createAndRegisterGaugeSet(names[0], metricsMap, callCounts[i]); } // validate with dump call to make sure the second GaugeSet overwrites the first final Map expectedMetricsMap = new HashMap<>(); expectedMetricsMap.put(String.format("%s{key=\"%s\"}", names[0], keys[1]), values[1]); validateWithDump(expectedMetricsMap); assertEquals(0, callCounts[0].get()); assertEquals(1, callCounts[1].get()); } @Test public void testGaugeSet_nullKey() { final String name = QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE; final Map metricsMap = new HashMap<>(); metricsMap.put(null, 10.0); final AtomicInteger callCount = new AtomicInteger(0); // create and register GaugeSet createAndRegisterGaugeSet(name, metricsMap, callCount); // validate with dump call assertThrows(IllegalArgumentException.class, () -> provider.dump(new HashMap<>()::put)); // validate with servlet call assertThrows(IllegalArgumentException.class, this::callServlet); } @Test public void testGaugeSet_registerWithNullGaugeSet() { assertThrows(NullPointerException.class, () -> provider.getRootContext().registerGaugeSet("name", null)); assertThrows(NullPointerException.class, () -> provider.getRootContext().registerGaugeSet(null, HashMap::new)); } @Test public void testGaugeSet_unregisterNull() { assertThrows(NullPointerException.class, () -> provider.getRootContext().unregisterGaugeSet(null)); } private void createAndRegisterGaugeSet(final String name, final Map metricsMap, final AtomicInteger callCount) { final GaugeSet gaugeSet = () -> { callCount.addAndGet(1); return metricsMap; }; provider.getRootContext().registerGaugeSet(name, gaugeSet); } private void validateWithDump(final Map expectedMetrics) { final Map returnedMetrics = new HashMap<>(); provider.dump(returnedMetrics::put); assertEquals(expectedMetrics.size(), returnedMetrics.size()); expectedMetrics.forEach((key, value) -> assertEquals(value, returnedMetrics.get(key))); } private void validateWithServletCall(final List expectedNames, final List expectedMetrics) throws Exception { final String response = callServlet(); if (expectedNames.isEmpty() && expectedMetrics.isEmpty()) { assertTrue(response.isEmpty()); } else { expectedNames.forEach(name -> assertThat(response, CoreMatchers.containsString(name))); expectedMetrics.forEach(metric -> assertThat(response, CoreMatchers.containsString(metric))); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-metrics-providers_zookeeper-prometheus-metrics_src_0100644 0000000 0000000 00000000170 15051152474 032614 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/resources/logback.xml apache-zookeeper-3.9.4/zookeeper-metrics-providers/zookeeper-prometheus-metrics/src/test/resources/l0100644 0000000 0000000 00000002430 15051152474 034573 0ustar00rootroot0000000 0000000 %d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n INFO apache-zookeeper-3.9.4/zookeeper-recipes/README.txt0100644 0000000 0000000 00000002366 15051152474 022454 0ustar00rootroot0000000 0000000 1) This source directory contains various Zookeeper recipe implementations. 2) The recipe directory name should specify the name of the recipe you are implementing - eg. zookeeper-recipes-lock/. 3) It would be great if you can provide both the java and c recipes for the zookeeper recipes. C recipes go in to zookeeper-recipes/zookeeper-recipes-[recipe-name]/src/c Java implementation goes into zookeeper-recipes/zookeeper-recipes-[recipe-name]/src/java. 4) The recipes hold high standards like our zookeeper c/java libraries, so make sure that you include some unit testing with both the c and java recipe code. 5) Also, please name your c client public methods as zkr_recipe-name_methodname (eg. zkr_lock_lock in zookeeper-recipes-lock/src/c) 6) The various recipes are in ../docs/recipes.html or ../../docs/reciped.pdf. Also, this is not an exhaustive list by any chance. Zookeeper is used (and can be used) for more than what we have listed in the docs. 7) To run the c tests in all the recipes, - make sure the main zookeeper c libraries in {top}/src/c/ are compiled. Run autoreconf -if;./configure; make. The libaries will be installed in {top}/src/c/.libs. - run autoreconf if;./configure;make run-check in zookeeper-recipes/$recipename/src/c apache-zookeeper-3.9.4/zookeeper-recipes/build-recipes.xml0100644 0000000 0000000 00000014265 15051152474 024230 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/build.xml0100644 0000000 0000000 00000004525 15051152474 022576 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-recipes/pom.xml0100755 0000000 0000000 00000007707 15051152474 022302 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper-recipes pom Apache ZooKeeper - Recipes 1) This module contains various Zookeeper recipe implementations. 2) The recipe directory name should specify the name of the recipe you are implementing - eg. zookeeper-recipes-lock. 3) It would be great if you can provide both the java and c recipes for the zookeeper recipes. C recipes go in to zookeeper-recipes/zookeeper-recipes-[recipe-name]/src/c Java implementation goes into zookeeper-recipes/zookeeper-recipes-[recipe-name]/src/java. 4) The recipes hold high standards like our zookeeper c/java libraries, so make sure that you include some unit testing with both the c and java recipe code. 5) Also, please name your c client public methods as zkr_recipe-name_methodname (eg. zkr_lock_lock in zookeeper-recipes-lock/src/c) 6) To run the c tests in all the recipes, - make sure the main zookeeper c libraries in zookeeper-client-c are compiled. Run autoreconf -if;./configure; make. The libraries will be installed in {top}/src/c/.libs. - run autoreconf if;./configure;make run-check in zookeeper-recipes/$recipename/src/c zookeeper-recipes-election zookeeper-recipes-lock zookeeper-recipes-queue org.apache.zookeeper zookeeper ${project.version} org.apache.zookeeper zookeeper ${project.version} test-jar test org.xerial.snappy snappy-java test org.junit.jupiter junit-jupiter-engine test io.dropwizard.metrics metrics-core test com.github.spotbugs spotbugs-annotations provided true maven-deploy-plugin true apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/README.txt0100644 0000000 0000000 00000002112 15051152474 027674 0ustar00rootroot0000000 0000000 1) This election interface recipe implements the leader election recipe mentioned in ../../docs/recipes.[html,pdf]. 2) To compile the leader election java recipe you can just run ant jar from this directory. Please report any bugs on the jira http://issues.apache.org/jira/browse/ZOOKEEPER apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/build.xml0100644 0000000 0000000 00000012510 15051152474 030022 0ustar00rootroot0000000 0000000 Tests failed! apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/pom.xml0100755 0000000 0000000 00000004173 15051152474 027527 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-recipes 3.9.4 zookeeper-recipes-election jar Apache ZooKeeper - Recipes - Election This election interface recipe implements the leader election recipe org.apache.maven.plugins maven-surefire-plugin ${surefire-forkcount} false -Xmx512m ${project.basedir} true ${project.build.directory}/surefire ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_main_java_or0100644 0000000 0000000 00000000230 15051152474 032523 xustar000000000 0000000 152 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeeper/recipes/leader/LeaderElectionAware.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000002605 15051152474 034250 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.leader; import org.apache.zookeeper.recipes.leader.LeaderElectionSupport.EventType; /** * An interface to be implemented by clients that want to receive election * events. */ public interface LeaderElectionAware { /** * Called during each state transition. Current, low level events are provided * at the beginning and end of each state. For instance, START may be followed * by OFFER_START, OFFER_COMPLETE, DETERMINE_START, DETERMINE_COMPLETE, and so * on. * * @param eventType */ void onElectionEvent(EventType eventType); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_main_java_or0100644 0000000 0000000 00000000232 15051152474 032525 xustar000000000 0000000 154 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeeper/recipes/leader/LeaderElectionSupport.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000041016 15051152474 034247 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.leader; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A leader election support library implementing the ZooKeeper election recipe. * *

This support library is meant to simplify the construction of an exclusive * leader system on top of Apache ZooKeeper. Any application that can become the * leader (usually a process that provides a service, exclusively) would * configure an instance of this class with their hostname, at least one * listener (an implementation of {@link LeaderElectionAware}), and either an * instance of {@link ZooKeeper} or the proper connection information. Once * configured, invoking {@link #start()} will cause the client to connect to * ZooKeeper and create a leader offer. The library then determines if it has * been elected the leader using the algorithm described below. The client * application can follow all state transitions via the listener callback. * *

Leader election algorithm * *

The library starts in a START state. Through each state transition, a state * start and a state complete event are sent to all listeners. When * {@link #start()} is called, a leader offer is created in ZooKeeper. A leader * offer is an ephemeral sequential node that indicates a process that can act * as a leader for this service. A read of all leader offers is then performed. * The offer with the lowest sequence number is said to be the leader. The * process elected leader will transition to the leader state. All other * processes will transition to a ready state. Internally, the library creates a * ZooKeeper watch on the leader offer with the sequence ID of N - 1 (where N is * the process's sequence ID). If that offer disappears due to a process * failure, the watching process will run through the election determination * process again to see if it should become the leader. Note that sequence ID * may not be contiguous due to failed processes. A process may revoke its offer * to be the leader at any time by calling {@link #stop()}. * *

Guarantees (not) Made and Caveats * *

    *
  • It is possible for a (poorly implemented) process to create a leader * offer, get the lowest sequence ID, but have something terrible occur where it * maintains its connection to ZK (and thus its ephemeral leader offer node) but * doesn't actually provide the service in question. It is up to the user to * ensure any failure to become the leader - and whatever that means in the * context of the user's application - results in a revocation of its leader * offer (i.e. that {@link #stop()} is called).
  • *
  • It is possible for ZK timeouts and retries to play a role in service * liveliness. In other words, if process A has the lowest sequence ID but * requires a few attempts to read the other leader offers' sequence IDs, * election can seem slow. Users should apply timeouts during the determination * process if they need to hit a specific SLA.
  • *
  • The library makes a "best effort" to detect catastrophic failures of the * process. It is possible that an unforeseen event results in (for instance) an * unchecked exception that propagates passed normal error handling code. This * normally doesn't matter as the same exception would almost certain destroy * the entire process and thus the connection to ZK and the leader offer * resulting in another round of leader determination.
  • *
*/ public class LeaderElectionSupport implements Watcher { private static final Logger LOG = LoggerFactory.getLogger(LeaderElectionSupport.class); private ZooKeeper zooKeeper; private State state; private Set listeners; private String rootNodeName; private LeaderOffer leaderOffer; private String hostName; public LeaderElectionSupport() { state = State.STOP; listeners = Collections.synchronizedSet(new HashSet<>()); } /** *

* Start the election process. This method will create a leader offer, * determine its status, and either become the leader or become ready. If an * instance of {@link ZooKeeper} has not yet been configured by the user, a * new instance is created using the connectString and sessionTime specified. *

*

* Any (anticipated) failures result in a failed event being sent to all * listeners. *

*/ public synchronized void start() { state = State.START; dispatchEvent(EventType.START); LOG.info("Starting leader election support"); if (zooKeeper == null) { throw new IllegalStateException( "No instance of zookeeper provided. Hint: use setZooKeeper()"); } if (hostName == null) { throw new IllegalStateException( "No hostname provided. Hint: use setHostName()"); } try { makeOffer(); determineElectionStatus(); } catch (KeeperException | InterruptedException e) { becomeFailed(e); } } /** * Stops all election services, revokes any outstanding leader offers, and * disconnects from ZooKeeper. */ public synchronized void stop() { state = State.STOP; dispatchEvent(EventType.STOP_START); LOG.info("Stopping leader election support"); if (leaderOffer != null) { try { zooKeeper.delete(leaderOffer.getNodePath(), -1); LOG.info("Removed leader offer {}", leaderOffer.getNodePath()); } catch (InterruptedException | KeeperException e) { becomeFailed(e); } } dispatchEvent(EventType.STOP_COMPLETE); } private void makeOffer() throws KeeperException, InterruptedException { state = State.OFFER; dispatchEvent(EventType.OFFER_START); LeaderOffer newLeaderOffer = new LeaderOffer(); byte[] hostnameBytes; synchronized (this) { newLeaderOffer.setHostName(hostName); hostnameBytes = hostName.getBytes(); newLeaderOffer.setNodePath(zooKeeper.create(rootNodeName + "/" + "n_", hostnameBytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL)); leaderOffer = newLeaderOffer; } LOG.debug("Created leader offer {}", leaderOffer); dispatchEvent(EventType.OFFER_COMPLETE); } private synchronized LeaderOffer getLeaderOffer() { return leaderOffer; } private void determineElectionStatus() throws KeeperException, InterruptedException { state = State.DETERMINE; dispatchEvent(EventType.DETERMINE_START); LeaderOffer currentLeaderOffer = getLeaderOffer(); String[] components = currentLeaderOffer.getNodePath().split("/"); currentLeaderOffer.setId(Integer.valueOf(components[components.length - 1].substring("n_".length()))); List leaderOffers = toLeaderOffers(zooKeeper.getChildren(rootNodeName, false)); /* * For each leader offer, find out where we fit in. If we're first, we * become the leader. If we're not elected the leader, attempt to stat the * offer just less than us. If they exist, watch for their failure, but if * they don't, become the leader. */ for (int i = 0; i < leaderOffers.size(); i++) { LeaderOffer leaderOffer = leaderOffers.get(i); if (leaderOffer.getId().equals(currentLeaderOffer.getId())) { LOG.debug("There are {} leader offers. I am {} in line.", leaderOffers.size(), i); dispatchEvent(EventType.DETERMINE_COMPLETE); if (i == 0) { becomeLeader(); } else { becomeReady(leaderOffers.get(i - 1)); } /* Once we've figured out where we are, we're done. */ break; } } } private void becomeReady(LeaderOffer neighborLeaderOffer) throws KeeperException, InterruptedException { LOG.info( "{} not elected leader. Watching node: {}", getLeaderOffer().getNodePath(), neighborLeaderOffer.getNodePath()); /* * Make sure to pass an explicit Watcher because we could be sharing this * zooKeeper instance with someone else. */ Stat stat = zooKeeper.exists(neighborLeaderOffer.getNodePath(), this); if (stat != null) { dispatchEvent(EventType.READY_START); LOG.debug( "We're behind {} in line and they're alive. Keeping an eye on them.", neighborLeaderOffer.getNodePath()); state = State.READY; dispatchEvent(EventType.READY_COMPLETE); } else { /* * If the stat fails, the node has gone missing between the call to * getChildren() and exists(). We need to try and become the leader. */ LOG.info( "We were behind {} but it looks like they died. Back to determination.", neighborLeaderOffer.getNodePath()); determineElectionStatus(); } } private void becomeLeader() { state = State.ELECTED; dispatchEvent(EventType.ELECTED_START); LOG.info("Becoming leader with node: {}", getLeaderOffer().getNodePath()); dispatchEvent(EventType.ELECTED_COMPLETE); } private void becomeFailed(Exception e) { LOG.error("Failed in state {}", state, e); state = State.FAILED; dispatchEvent(EventType.FAILED); } /** * Fetch the (user supplied) hostname of the current leader. Note that by the * time this method returns, state could have changed so do not depend on this * to be strongly consistent. This method has to read all leader offers from * ZooKeeper to deterime who the leader is (i.e. there is no caching) so * consider the performance implications of frequent invocation. If there are * no leader offers this method returns null. * * @return hostname of the current leader * @throws KeeperException * @throws InterruptedException */ public String getLeaderHostName() throws KeeperException, InterruptedException { List leaderOffers = toLeaderOffers(zooKeeper.getChildren(rootNodeName, false)); if (leaderOffers.size() > 0) { return leaderOffers.get(0).getHostName(); } return null; } private List toLeaderOffers(List strings) throws KeeperException, InterruptedException { List leaderOffers = new ArrayList<>(strings.size()); /* * Turn each child of rootNodeName into a leader offer. This is a tuple of * the sequence number and the node name. */ for (String offer : strings) { String hostName = new String(zooKeeper.getData(rootNodeName + "/" + offer, false, null)); leaderOffers.add(new LeaderOffer( Integer.valueOf(offer.substring("n_".length())), rootNodeName + "/" + offer, hostName)); } /* * We sort leader offers by sequence number (which may not be zero-based or * contiguous) and keep their paths handy for setting watches. */ Collections.sort(leaderOffers, new LeaderOffer.IdComparator()); return leaderOffers; } @Override public void process(WatchedEvent event) { if (event.getType().equals(Watcher.Event.EventType.NodeDeleted)) { if (!event.getPath().equals(getLeaderOffer().getNodePath()) && state != State.STOP) { LOG.debug( "Node {} deleted. Need to run through the election process.", event.getPath()); try { determineElectionStatus(); } catch (KeeperException | InterruptedException e) { becomeFailed(e); } } } } private void dispatchEvent(EventType eventType) { LOG.debug("Dispatching event: {}", eventType); synchronized (listeners) { if (listeners.size() > 0) { for (LeaderElectionAware observer : listeners) { observer.onElectionEvent(eventType); } } } } /** * Adds {@code listener} to the list of listeners who will receive events. * * @param listener */ public void addListener(LeaderElectionAware listener) { listeners.add(listener); } /** * Remove {@code listener} from the list of listeners who receive events. * * @param listener */ public void removeListener(LeaderElectionAware listener) { listeners.remove(listener); } @Override public String toString() { return "{" + " state:" + state + " leaderOffer:" + getLeaderOffer() + " zooKeeper:" + zooKeeper + " hostName:" + getHostName() + " listeners:" + listeners + " }"; } /** *

* Gets the ZooKeeper root node to use for this service. *

*

* For instance, a root node of {@code /mycompany/myservice} would be the * parent of all leader offers for this service. Obviously all processes that * wish to contend for leader status need to use the same root node. Note: We * assume this node already exists. *

* * @return a znode path */ public String getRootNodeName() { return rootNodeName; } /** *

* Sets the ZooKeeper root node to use for this service. *

*

* For instance, a root node of {@code /mycompany/myservice} would be the * parent of all leader offers for this service. Obviously all processes that * wish to contend for leader status need to use the same root node. Note: We * assume this node already exists. *

*/ public void setRootNodeName(String rootNodeName) { this.rootNodeName = rootNodeName; } /** * The {@link ZooKeeper} instance to use for all operations. Provided this * overrides any connectString or sessionTimeout set. */ public ZooKeeper getZooKeeper() { return zooKeeper; } public void setZooKeeper(ZooKeeper zooKeeper) { this.zooKeeper = zooKeeper; } /** * The hostname of this process. Mostly used as a convenience for logging and * to respond to {@link #getLeaderHostName()} requests. */ public synchronized String getHostName() { return hostName; } public synchronized void setHostName(String hostName) { this.hostName = hostName; } /** * The type of event. */ public enum EventType { START, OFFER_START, OFFER_COMPLETE, DETERMINE_START, DETERMINE_COMPLETE, ELECTED_START, ELECTED_COMPLETE, READY_START, READY_COMPLETE, FAILED, STOP_START, STOP_COMPLETE, } /** * The internal state of the election support service. */ public enum State { START, OFFER, DETERMINE, ELECTED, READY, FAILED, STOP } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_main_java_or0100644 0000000 0000000 00000000220 15051152474 032522 xustar000000000 0000000 144 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeeper/recipes/leader/LeaderOffer.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/main/java/org/apache/zookeep0100644 0000000 0000000 00000004535 15051152474 034254 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.leader; import java.io.Serializable; import java.util.Comparator; /** * A leader offer is a numeric id / path pair. The id is the sequential node id * assigned by ZooKeeper where as the path is the absolute path to the ZNode. */ public class LeaderOffer { private Integer id; private String nodePath; private String hostName; public LeaderOffer() { // Default constructor } public LeaderOffer(Integer id, String nodePath, String hostName) { this.id = id; this.nodePath = nodePath; this.hostName = hostName; } @Override public String toString() { return "{" + " id:" + id + " nodePath:" + nodePath + " hostName:" + hostName + " }"; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getNodePath() { return nodePath; } public void setNodePath(String nodePath) { this.nodePath = nodePath; } public String getHostName() { return hostName; } public void setHostName(String hostName) { this.hostName = hostName; } /** * Compare two instances of {@link LeaderOffer} using only the {code}id{code} * member. */ public static class IdComparator implements Comparator, Serializable { @Override public int compare(LeaderOffer o1, LeaderOffer o2) { return o1.getId().compareTo(o2.getId()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-election_src_test_java_or0100644 0000000 0000000 00000000236 15051152474 032564 xustar000000000 0000000 158 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeeper/recipes/leader/LeaderElectionSupportTest.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-election/src/test/java/org/apache/zookeep0100644 0000000 0000000 00000022512 15051152474 034302 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.leader; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.recipes.leader.LeaderElectionSupport.EventType; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test for {@link LeaderElectionSupport}. */ public class LeaderElectionSupportTest extends ClientBase { private static final Logger LOGGER = LoggerFactory.getLogger(LeaderElectionSupportTest.class); private static final String TEST_ROOT_NODE = "/" + System.currentTimeMillis() + "_"; private ZooKeeper zooKeeper; @BeforeEach public void setUp() throws Exception { super.setUp(); zooKeeper = createClient(); zooKeeper.create( TEST_ROOT_NODE + Thread.currentThread().getId(), new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } @AfterEach public void tearDown() throws Exception { if (zooKeeper != null) { zooKeeper.delete(TEST_ROOT_NODE + Thread.currentThread().getId(), -1); } super.tearDown(); } @Test public void testNode() throws Exception { LeaderElectionSupport electionSupport = createLeaderElectionSupport(); electionSupport.start(); Thread.sleep(3000); electionSupport.stop(); } @Test public void testNodes3() throws Exception { int testIterations = 3; final CountDownLatch latch = new CountDownLatch(testIterations); final AtomicInteger failureCounter = new AtomicInteger(); for (int i = 0; i < testIterations; i++) { runElectionSupportThread(latch, failureCounter); } assertEquals(0, failureCounter.get()); if (!latch.await(10, TimeUnit.SECONDS)) { LOGGER.info("Waited for all threads to start, but timed out. We had {} failures.", failureCounter); } } @Test public void testNodes9() throws Exception { int testIterations = 9; final CountDownLatch latch = new CountDownLatch(testIterations); final AtomicInteger failureCounter = new AtomicInteger(); for (int i = 0; i < testIterations; i++) { runElectionSupportThread(latch, failureCounter); } assertEquals(0, failureCounter.get()); if (!latch.await(10, TimeUnit.SECONDS)) { LOGGER.info("Waited for all threads to start, but timed out. We had {} failures.", failureCounter); } } @Test public void testNodes20() throws Exception { int testIterations = 20; final CountDownLatch latch = new CountDownLatch(testIterations); final AtomicInteger failureCounter = new AtomicInteger(); for (int i = 0; i < testIterations; i++) { runElectionSupportThread(latch, failureCounter); } assertEquals(0, failureCounter.get()); if (!latch.await(10, TimeUnit.SECONDS)) { LOGGER.info("Waited for all threads to start, but timed out. We had {} failures.", failureCounter); } } @Test public void testNodes100() throws Exception { int testIterations = 100; final CountDownLatch latch = new CountDownLatch(testIterations); final AtomicInteger failureCounter = new AtomicInteger(); for (int i = 0; i < testIterations; i++) { runElectionSupportThread(latch, failureCounter); } assertEquals(0, failureCounter.get()); if (!latch.await(20, TimeUnit.SECONDS)) { LOGGER.info("Waited for all threads to start, but timed out. We had {} failures.", failureCounter); } } @Test public void testOfferShuffle() throws InterruptedException { int testIterations = 10; final CountDownLatch latch = new CountDownLatch(testIterations); final AtomicInteger failureCounter = new AtomicInteger(); List threads = new ArrayList<>(testIterations); for (int i = 1; i <= testIterations; i++) { threads.add(runElectionSupportThread(latch, failureCounter, Math.min(i * 1200, 10000))); } if (!latch.await(60, TimeUnit.SECONDS)) { LOGGER.info("Waited for all threads to start, but timed out. We had {} failures.", failureCounter); } } @Test public void testGetLeaderHostName() throws Exception { LeaderElectionSupport electionSupport = createLeaderElectionSupport(); electionSupport.start(); // Sketchy: We assume there will be a leader (probably us) in 3 seconds. Thread.sleep(3000); String leaderHostName = electionSupport.getLeaderHostName(); assertNotNull(leaderHostName); assertEquals("foohost", leaderHostName); electionSupport.stop(); } @Test public void testReadyOffer() throws Exception { final ArrayList events = new ArrayList<>(); final CountDownLatch electedComplete = new CountDownLatch(1); final LeaderElectionSupport electionSupport1 = createLeaderElectionSupport(); electionSupport1.start(); LeaderElectionSupport electionSupport2 = createLeaderElectionSupport(); LeaderElectionAware listener = new LeaderElectionAware() { boolean stoppedElectedNode = false; @Override public void onElectionEvent(EventType eventType) { events.add(eventType); if (!stoppedElectedNode && eventType == EventType.DETERMINE_COMPLETE) { stoppedElectedNode = true; try { // stopping the ELECTED node, so re-election will happen. electionSupport1.stop(); } catch (Exception e) { LOGGER.error("Unexpected exception", e); } } if (eventType == EventType.ELECTED_COMPLETE) { electedComplete.countDown(); } } }; electionSupport2.addListener(listener); electionSupport2.start(); // waiting for re-election. electedComplete.await(CONNECTION_TIMEOUT / 3, TimeUnit.MILLISECONDS); final ArrayList expectedevents = new ArrayList<>(); expectedevents.add(EventType.START); expectedevents.add(EventType.OFFER_START); expectedevents.add(EventType.OFFER_COMPLETE); expectedevents.add(EventType.DETERMINE_START); expectedevents.add(EventType.DETERMINE_COMPLETE); expectedevents.add(EventType.DETERMINE_START); expectedevents.add(EventType.DETERMINE_COMPLETE); expectedevents.add(EventType.ELECTED_START); expectedevents.add(EventType.ELECTED_COMPLETE); assertEquals(expectedevents, events, "Events has failed to executed in the order"); electionSupport2.stop(); } private LeaderElectionSupport createLeaderElectionSupport() { LeaderElectionSupport electionSupport = new LeaderElectionSupport(); electionSupport.setZooKeeper(zooKeeper); electionSupport.setRootNodeName(TEST_ROOT_NODE + Thread.currentThread().getId()); electionSupport.setHostName("foohost"); return electionSupport; } private Thread runElectionSupportThread( final CountDownLatch latch, final AtomicInteger failureCounter) { return runElectionSupportThread(latch, failureCounter, 3000); } private Thread runElectionSupportThread( final CountDownLatch latch, final AtomicInteger failureCounter, final long sleepDuration) { final LeaderElectionSupport electionSupport = createLeaderElectionSupport(); Thread t = new Thread(() -> { try { electionSupport.start(); Thread.sleep(sleepDuration); electionSupport.stop(); latch.countDown(); } catch (Exception e) { LOGGER.warn("Failed to run leader election.", e); failureCounter.incrementAndGet(); } }); t.start(); return t; } } apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/README.txt0100644 0000000 0000000 00000002241 15051152474 027025 0ustar00rootroot0000000 0000000 1) This lock interface recipe implements the lock recipe mentioned in ../../docs/recipes.[html,pdf]. 2) To compile the lock java recipe you can just run ant jar from this directory. For compiling the c libarary go to zookeeper-client/zookeeper-client-c and read the INSTALLATION instructions. Please report any bugs on the jira http://issues.apache.org/jira/browse/ZOOKEEPER apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/build.xml0100644 0000000 0000000 00000013231 15051152474 027151 0ustar00rootroot0000000 0000000 Tests failed! apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/pom.xml0100755 0000000 0000000 00000004145 15051152474 026654 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-recipes 3.9.4 zookeeper-recipes-lock jar Apache ZooKeeper - Recipes - Lock This lock interface recipe implements the lock recipe org.apache.maven.plugins maven-surefire-plugin ${surefire-forkcount} false -Xmx512m ${project.basedir} true ${project.build.directory}/surefire apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/INSTALL0100644 0000000 0000000 00000022310 15051152474 030314 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/LICENSE0100644 0000000 0000000 00000026136 15051152474 030302 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/Makefile.am0100644 0000000 0000000 00000003257 15051152474 031330 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 $(top_srcdir)/aminclude.am AM_CFLAGS = -Wall -fPIC -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ -I$(top_srcdir)/include -I/usr/include AM_CPPFLAGS = -Wall -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated\ -I${top_srcdir}/include -I/usr/include EXTRA_DIST = LICENSE lib_LTLIBRARIES = libzoolock.la libzoolock_la_SOURCES = src/zoo_lock.c include/zoo_lock.h libzoolock_la_CPPFLAGS = -DDLOPEN_MODULE libzoolock_la_LDFLAGS = -version-info 0:1:0 #run the tests now TEST_SOURCES = tests/TestDriver.cc tests/TestClient.cc tests/Util.cc check_PROGRAMS = zklocktest nodist_zklocktest_SOURCES = ${TEST_SOURCES} zklocktest_LDADD = ${ZOOKEEPER_LD} libzoolock.la -lpthread ${CPPUNIT_LIBS} zklocktest_CXXFLAGS = -DUSE_STATIC_LIB ${CPPUNIT_CFLAGS} run-check: check ./zklocktest ${TEST_OPTIONS} clean-local: clean-check ${RM} ${DX_CLEANFILES} clean-check: ${RM} ${nodist_zklocktest_OBJECTS} apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/README.txt0100644 0000000 0000000 00000002437 15051152474 030771 0ustar00rootroot0000000 0000000 Zookeeper C lock client library 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) make sure that you compile the main zookeeper c client library. 2) change directory to zookeeper-recipes/zookeeper-recipes-lock/src/main/c and do a "autoreconf -if" to bootstrap autoconf, automake and libtool. Please make sure you have autoconf version 2.59 or greater installed. 3) do a "./configure [OPTIONS]" to generate the makefile. See INSTALL for general information about running configure. 4) 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. 5) 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" apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/acinclude.m40100644 0000000 0000000 00000026415 15051152474 031466 0ustar00rootroot0000000 0000000 # 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 of 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 ]) apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/aminclude.am0100644 0000000 0000000 00000011175 15051152474 031552 0ustar00rootroot0000000 0000000 # 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 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/c-doc.Doxyfile0100644 0000000 0000000 00000143151 15051152474 031764 0ustar00rootroot0000000 0000000 # 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/zoo_lock.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 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/configure.ac0100644 0000000 0000000 00000004332 15051152474 031555 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) AC_INIT([zoolock], [3.2.0]) AC_CONFIG_SRCDIR([include/zoo_lock.h]) PACKAGE=zoolock VERSION=1.0 AC_SUBST(PACKAGE) AC_SUBST(VERSION) BUILD_PATH="`pwd`" # Checks for programs. AC_LANG_CPLUSPLUS AM_INIT_AUTOMAKE([-Wall foreign]) # Checks for libraries. #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-locks],[c-doc.Doxyfile],[docs]) ZOOKEEPER_PATH=${BUILD_PATH}/../../../../../zookeeper-client/zookeeper-client-c ZOOKEEPER_LD=-L${BUILD_PATH}/../../../../../zookeeper-client/zookeeper-client-c\ -lzookeeper_mt 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_OFF_T AC_TYPE_SIZE_T AC_STRUCT_ST_BLOCKS AC_HEADER_TIME AC_C_VOLATILE AC_PROG_CC AC_PROG_LIBTOOL #check for cppunit AM_PATH_CPPUNIT(1.10.2) # 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_OUTPUT AC_C_VOLATILE apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/include/zoo_lock.h0100644 0000000 0000000 00000011660 15051152474 032704 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_LOCK_H_ #define ZOOKEEPER_LOCK_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** * \brief the call back function called on status change of lock * * the call back funtion is called with a rc of 0 if lock is acquired and * with an rc of 1 if the lock is released * \param rc the value to let us know if its locked or unlocked * \param cbdata the callback data that we passed when initializing * the zookeeper lock. */ typedef void (* zkr_lock_completion) (int rc, void* cbdata); /** * \file zoo_lock.h * \brief zookeeper recipe for locking and leader election. * this api implements a writelock on a given path in zookeeper. * this api can also be used for leader election. */ struct zkr_lock_mutex { zhandle_t *zh; char *path; struct ACL_vector *acl; char *id; void *cbdata; zkr_lock_completion completion; pthread_mutex_t pmutex; int isOwner; char* ownerid; }; typedef struct zkr_lock_mutex zkr_lock_mutex_t; /** * \brief initializing a zookeeper lock. * * this method instantiates the zookeeper mutex lock. * \param mutex the mutex to initialize * \param zh the zookeeper handle to use * \param path the path in zookeeper to use for locking * \param acl the acls to use in zookeeper. * \return return 0 if successful. */ ZOOAPI int zkr_lock_init(zkr_lock_mutex_t *mutex, zhandle_t* zh, char* path, struct ACL_vector *acl); /** * \brief initializing a zookeeper lock. * * * this method instantiates the zookeeper mutex lock with * a completion function. * * \param mutex the mutex to initialize * \param zh the zookeeper handle to use * \param path the path in zookeeper to use for locking * \param acl the acls to use in zookeeper. * \param completion the callback thats called when lock * is acquired and released. * \param cbdata the callback method is called with data * \return return 0 if successful. */ ZOOAPI int zkr_lock_init_cb(zkr_lock_mutex_t *mutex, zhandle_t* zh, char* path, struct ACL_vector *acl, zkr_lock_completion completion, void* cbdata); /** * \brief lock the zookeeper mutex * * this method tries locking the mutex * \param mutex the zookeeper mutex * \return return 0 if there is no error. check * with zkr_lock_isowner() if you have the lock */ ZOOAPI int zkr_lock_lock(zkr_lock_mutex_t *mutex); /** * \brief unlock the zookeeper mutex * * this method unlocks the zookeeper mutex * \param mutex the zookeeper mutex * \return return 0 if there is not error in executing unlock. * else returns non zero */ ZOOAPI int zkr_lock_unlock(zkr_lock_mutex_t *mutex); /** * \brief set the callback function for zookeeper mutex * * this method sets the callback for zookeeper mutex * \param mutex the zookeeper mutex * \param callback the call back completion function */ ZOOAPI void zkr_lock_setcallback(zkr_lock_mutex_t *mutex, zkr_lock_completion completion); /** * \brief get the callback function for zookeeper mutex * * this method gets the callback funtion for zookeeper mutex * \param mutex the zookeeper mutex * \return the lock completion function */ ZOOAPI zkr_lock_completion zkr_lock_getcallback(zkr_lock_mutex_t *mutex); /** * \brief destroy the mutex * this method free the mutex * \param mutex destroy the zookepeer lock. * \return return 0 if destroyed. */ ZOOAPI int zkr_lock_destroy(zkr_lock_mutex_t* mutex); /** * \brief return the parent path this mutex is using * this method returns the parent path * \param mutex the mutex * \return return the parent path */ ZOOAPI char* zkr_lock_getpath(zkr_lock_mutex_t *mutex); /** * \brief return if this mutex is owner of the lock * this method returns if its owner or not * \param mutex the mutex * \return return true if is owner and false if not */ ZOOAPI int zkr_lock_isowner(zkr_lock_mutex_t *mutex); /** * \brief return the id for this mutex * this mutex retunrns the id string * \param mutex the mutex * \return the id for this mutex */ ZOOAPI char* zkr_lock_getid(zkr_lock_mutex_t *mutex); #ifdef __cplusplus } #endif #endif //ZOOKEEPER_LOCK_H_ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/src/zoo_lock.c0100644 0000000 0000000 00000031176 15051152474 032047 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 DLL_EXPORT #define USE_STATIC_LIB #endif #if defined(__CYGWIN__) #define USE_IPV6 #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_GETPWUID_R #include #endif #define IF_DEBUG(x) if (logLevel==ZOO_LOG_LEVEL_DEBUG) {x;} ZOOAPI int zkr_lock_init(zkr_lock_mutex_t* mutex, zhandle_t* zh, char* path, struct ACL_vector *acl) { mutex->zh = zh; mutex->path = path; mutex->acl = acl; mutex->completion = NULL; mutex->cbdata = NULL; mutex->id = NULL; mutex->ownerid = NULL; mutex->isOwner = 0; pthread_mutex_init(&(mutex->pmutex), NULL); return 0; } ZOOAPI int zkr_lock_init_cb(zkr_lock_mutex_t *mutex, zhandle_t* zh, char *path, struct ACL_vector *acl, zkr_lock_completion completion, void* cbdata) { mutex->zh = zh; mutex->path = path; mutex->acl = acl; mutex->completion = completion; mutex->cbdata = cbdata; mutex->isOwner = 0; mutex->ownerid = NULL; mutex->id = NULL; pthread_mutex_init(&(mutex->pmutex), NULL); return 0; } static int _zkr_lock_unlock_nolock(zkr_lock_mutex_t *mutex) { zhandle_t *zh = mutex->zh; if (mutex->id != NULL) { int len = strlen(mutex->path) + strlen(mutex->id) + 2; char buf[len]; sprintf(buf, "%s/%s", mutex->path, mutex->id); int ret = 0; int count = 0; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = (.5)*1000000; ret = ZCONNECTIONLOSS; while (ret == ZCONNECTIONLOSS && (count < 3)) { ret = zoo_delete(zh, buf, -1); if (ret == ZCONNECTIONLOSS) { LOG_DEBUG(LOGCALLBACK(zh), ("connectionloss while deleting the node")); nanosleep(&ts, 0); count++; } } if (ret == ZOK || ret == ZNONODE) { zkr_lock_completion completion = mutex->completion; if (completion != NULL) { completion(1, mutex->cbdata); } free(mutex->id); mutex->id = NULL; return 0; } LOG_WARN(LOGCALLBACK(zh), ("not able to connect to server - giving up")); return ZCONNECTIONLOSS; } return ZSYSTEMERROR; } /** * unlock the mutex */ ZOOAPI int zkr_lock_unlock(zkr_lock_mutex_t *mutex) { int ret = 0; pthread_mutex_lock(&(mutex->pmutex)); ret = _zkr_lock_unlock_nolock(mutex); pthread_mutex_unlock(&(mutex->pmutex)); return ret; } static void free_String_vector(struct String_vector *v) { if (v->data) { int32_t i; for (i=0; icount; i++) { free(v->data[i]); } free(v->data); v->data = 0; } } static int strcmp_suffix(const char *str1, const char *str2) { return strcmp(strrchr(str1, '-')+1, strrchr(str2, '-')+1); } static int vstrcmp(const void* str1, const void* str2) { const char **a = (const char**)str1; const char **b = (const char**) str2; return strcmp_suffix(*a, *b); } static void sort_children(struct String_vector *vector) { qsort( vector->data, vector->count, sizeof(char*), &vstrcmp); } static char* child_floor(char **sorted_data, int len, char *element) { char* ret = NULL; int targetpos = -1, s = 0, e = len -1; while ( targetpos < 0 && s <= e ) { int const i = s + (e - s) / 2; int const cmp = strcmp_suffix(sorted_data[i], element); if (cmp < 0) { s = i + 1; } else if (cmp == 0) { targetpos = i; } else { e = i - 1; } } if (targetpos > 0) { ret = sorted_data[targetpos - 1]; } return ret; } static void lock_watcher_fn(zhandle_t* zh, int type, int state, const char* path, void *watcherCtx) { //callback that we registered //should be called zkr_lock_lock((zkr_lock_mutex_t*) watcherCtx); } /** * get the last name of the path */ static char* getName(char* str) { char* name = strrchr(str, '/'); if (name == NULL) return NULL; return strdup(name + 1); } /** * just a method to retry get children */ static int retry_getchildren(zhandle_t *zh, char* path, struct String_vector *vector, struct timespec *ts, int retry) { int ret = ZCONNECTIONLOSS; int count = 0; while (ret == ZCONNECTIONLOSS && count < retry) { ret = zoo_get_children(zh, path, 0, vector); if (ret == ZCONNECTIONLOSS) { LOG_DEBUG(LOGCALLBACK(zh), ("connection loss to the server")); nanosleep(ts, 0); count++; } } return ret; } /** see if our node already exists * if it does then we dup the name and * return it */ static char* lookupnode(struct String_vector *vector, char *prefix) { char *ret = NULL; if (vector->data) { int i = 0; for (i = 0; i < vector->count; i++) { char* child = vector->data[i]; if (strncmp(prefix, child, strlen(prefix)) == 0) { ret = strdup(child); break; } } } return ret; } /** retry zoo_wexists */ static int retry_zoowexists(zhandle_t *zh, char* path, watcher_fn watcher, void* ctx, struct Stat *stat, struct timespec *ts, int retry) { int ret = ZCONNECTIONLOSS; int count = 0; while (ret == ZCONNECTIONLOSS && count < retry) { ret = zoo_wexists(zh, path, watcher, ctx, stat); if (ret == ZCONNECTIONLOSS) { LOG_DEBUG(LOGCALLBACK(zh), ("connectionloss while setting watch on my predecessor")); nanosleep(ts, 0); count++; } } return ret; } /** * the main code that does the zookeeper leader * election. this code creates its own ephemeral * node on the given path and sees if its the first * one on the list and claims to be a leader if and only * if its the first one of children in the paretn path */ static int zkr_lock_operation(zkr_lock_mutex_t *mutex, struct timespec *ts) { zhandle_t *zh = mutex->zh; char *path = mutex->path; char *id = mutex->id; struct Stat stat; char* owner_id = NULL; int retry = 3; do { const clientid_t *cid = zoo_client_id(zh); // get the session id int64_t session = cid->client_id; char prefix[30]; int ret = 0; #if defined(__x86_64__) snprintf(prefix, 30, "x-%016lx-", session); #else snprintf(prefix, 30, "x-%016llx-", session); #endif struct String_vector vectorst; vectorst.data = NULL; vectorst.count = 0; ret = ZCONNECTIONLOSS; ret = retry_getchildren(zh, path, &vectorst, ts, retry); if (ret != ZOK) return ret; struct String_vector *vector = &vectorst; mutex->id = lookupnode(vector, prefix); free_String_vector(vector); if (mutex->id == NULL) { int len = strlen(path) + strlen(prefix) + 2; char buf[len]; char retbuf[len+20]; snprintf(buf, len, "%s/%s", path, prefix); ret = ZCONNECTIONLOSS; ret = zoo_create(zh, buf, NULL, 0, mutex->acl, ZOO_EPHEMERAL|ZOO_SEQUENCE, retbuf, (len+20)); // do not want to retry the create since // we would end up creating more than one child if (ret != ZOK) { LOG_WARN(LOGCALLBACK(zh), "could not create zoo node %s", buf); return ret; } mutex->id = getName(retbuf); } if (mutex->id != NULL) { ret = ZCONNECTIONLOSS; ret = retry_getchildren(zh, path, vector, ts, retry); if (ret != ZOK) { LOG_WARN(LOGCALLBACK(zh), ("could not connect to server")); return ret; } //sort this list sort_children(vector); owner_id = vector->data[0]; mutex->ownerid = strdup(owner_id); id = mutex->id; char* lessthanme = child_floor(vector->data, vector->count, id); if (lessthanme != NULL) { int flen = strlen(mutex->path) + strlen(lessthanme) + 2; char last_child[flen]; sprintf(last_child, "%s/%s",mutex->path, lessthanme); ret = ZCONNECTIONLOSS; ret = retry_zoowexists(zh, last_child, &lock_watcher_fn, mutex, &stat, ts, retry); // cannot watch my predecessor i am giving up // we need to be able to watch the predecessor // since if we do not become a leader the others // will keep waiting if (ret != ZOK) { free_String_vector(vector); LOG_WARN(LOGCALLBACK(zh), ("unable to watch my predecessor")); ret = _zkr_lock_unlock_nolock(mutex); while (ret == 0) { //we have to give up our leadership // since we cannot watch out predecessor ret = _zkr_lock_unlock_nolock(mutex); } return ret; } // we are not the owner of the lock mutex->isOwner = 0; } else { // this is the case when we are the owner // of the lock if (strcmp(mutex->id, owner_id) == 0) { LOG_DEBUG(LOGCALLBACK(zh), "got the zoo lock owner - %s", mutex->id); mutex->isOwner = 1; if (mutex->completion != NULL) { mutex->completion(0, mutex->cbdata); } return ZOK; } } free_String_vector(vector); return ZOK; } } while (mutex->id == NULL); return ZOK; } ZOOAPI int zkr_lock_lock(zkr_lock_mutex_t *mutex) { pthread_mutex_lock(&(mutex->pmutex)); zhandle_t *zh = mutex->zh; char *path = mutex->path; struct Stat stat; int exists = zoo_exists(zh, path, 0, &stat); int count = 0; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = (.5)*1000000; // retry to see if the path exists and // and create if the path does not exist while ((exists == ZCONNECTIONLOSS || exists == ZNONODE) && (count <4)) { count++; // retry the operation if (exists == ZCONNECTIONLOSS) exists = zoo_exists(zh, path, 0, &stat); else if (exists == ZNONODE) exists = zoo_create(zh, path, NULL, 0, mutex->acl, 0, NULL, 0); nanosleep(&ts, 0); } // need to check if we cannot still access the server int check_retry = ZCONNECTIONLOSS; count = 0; while (check_retry != ZOK && count <4) { check_retry = zkr_lock_operation(mutex, &ts); if (check_retry != ZOK) { nanosleep(&ts, 0); count++; } } pthread_mutex_unlock(&(mutex->pmutex)); return 0; } ZOOAPI char* zkr_lock_getpath(zkr_lock_mutex_t *mutex) { return mutex->path; } ZOOAPI int zkr_lock_isowner(zkr_lock_mutex_t *mutex) { return (mutex->id != NULL && mutex->ownerid != NULL && (strcmp(mutex->id, mutex->ownerid) == 0)); } ZOOAPI char* zkr_lock_getid(zkr_lock_mutex_t *mutex) { return mutex->ownerid; } ZOOAPI int zkr_lock_destroy(zkr_lock_mutex_t* mutex) { if (mutex->id) free(mutex->id); mutex->path = NULL; mutex->acl = NULL; mutex->completion = NULL; pthread_mutex_destroy(&(mutex->pmutex)); mutex->isOwner = 0; if (mutex->ownerid) free(mutex->ownerid); return 0; } apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/c/tests/TestClient.cc0100644 0000000 0000000 00000011632 15051152474 033017 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 using namespace std; #include #include #include #include #include static void yield(zhandle_t *zh, int i) { sleep(i); } typedef struct evt { string path; int type; } evt_t; typedef struct watchCtx { private: list events; public: bool connected; zhandle_t *zh; watchCtx() { connected = false; zh = 0; } ~watchCtx() { if (zh) { zookeeper_close(zh); zh = 0; } } evt_t getEvent() { evt_t evt; evt = events.front(); events.pop_front(); return evt; } int countEvents() { int count; count = events.size(); return count; } void putEvent(evt_t evt) { events.push_back(evt); } 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_locktest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_locktest); CPPUNIT_TEST(testlock); 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; } public: #define ZKSERVER_CMD "./tests/zkServer.sh" void setUp() { char cmd[1024]; sprintf(cmd, "%s startClean %s", ZKSERVER_CMD, getHostPorts()); CPPUNIT_ASSERT(system(cmd) == 0); } void startServer() { char cmd[1024]; sprintf(cmd, "%s start %s", ZKSERVER_CMD, getHostPorts()); CPPUNIT_ASSERT(system(cmd) == 0); } void stopServer() { tearDown(); } void tearDown() { char cmd[1024]; sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts()); CPPUNIT_ASSERT(system(cmd) == 0); } void testlock() { watchctx_t ctx; int rc; struct Stat stat; char buf[1024]; int blen; struct String_vector strings; const char *testName; zkr_lock_mutex_t mutexes[3]; int count = 3; int i = 0; char* path = "/test-lock"; for (i=0; i< 3; i++) { zhandle_t *zh = createClient(&ctx); zkr_lock_init(&mutexes[i], zh, path, &ZOO_OPEN_ACL_UNSAFE); zkr_lock_lock(&mutexes[i]); } sleep(30); zkr_lock_mutex leader = mutexes[0]; zkr_lock_mutex mutex; int ret = strcmp(leader.id, leader.ownerid); CPPUNIT_ASSERT(ret == 0); for(i=1; i < count; i++) { mutex = mutexes[i]; CPPUNIT_ASSERT(strcmp(mutex.id, mutex.ownerid) != 0); } zkr_lock_unlock(&leader); sleep(30); zkr_lock_mutex secondleader = mutexes[1]; CPPUNIT_ASSERT(strcmp(secondleader.id , secondleader.ownerid) == 0); for (i=2; i #include #include #include #include #include #include #include #include #include #include #include #include #include "Util.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); // 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 ); // Add a listener that print dots as tests run. // CPPUNIT_NS::TextTestProgressListener progress; CPPUNIT_NS::BriefTestProgressListener progress; controller.addListener( &progress ); CPPUNIT_NS::TestRunner runner; runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() ); try { cout << "Running " << globalTestConfig.getTestName(); runner.run( controller, globalTestConfig.getTestName()); cout< #include #include // 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(3,__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); // 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 /tmp/zk.log & echo $! > /tmp/zk.pid sleep 5 ;; stop) # Already killed above ;; *) echo "Unknown command " + $1 exit 2 esac ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000213 15051152474 032501 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/lock/LockListener.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0100644 0000000 0000000 00000002273 15051152474 034167 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; /** * This class has two methods which are call * back methods when a lock is acquired and * when the lock is released. * */ public interface LockListener { /** * call back called when the lock * is acquired. */ void lockAcquired(); /** * call back called when the lock is * released. */ void lockReleased(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000216 15051152474 032504 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/lock/ProtocolSupport.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0100644 0000000 0000000 00000013211 15051152474 034161 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A base class for protocol implementations which provides a number of higher * level helper methods for working with ZooKeeper along with retrying synchronous * operations if the connection to ZooKeeper closes such as * {@link #retryOperation(ZooKeeperOperation)}. */ class ProtocolSupport { private static final Logger LOG = LoggerFactory.getLogger(ProtocolSupport.class); private static final int RETRY_COUNT = 10; protected final ZooKeeper zookeeper; private AtomicBoolean closed = new AtomicBoolean(false); private long retryDelay = 500L; private List acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; public ProtocolSupport(ZooKeeper zookeeper) { this.zookeeper = zookeeper; } /** * Closes this strategy and releases any ZooKeeper resources; but keeps the * ZooKeeper instance open. */ public void close() { if (closed.compareAndSet(false, true)) { doClose(); } } /** * return zookeeper client instance. * * @return zookeeper client instance */ public ZooKeeper getZookeeper() { return zookeeper; } /** * return the acl its using. * * @return the acl. */ public List getAcl() { return acl; } /** * set the acl. * * @param acl the acl to set to */ public void setAcl(List acl) { this.acl = acl; } /** * get the retry delay in milliseconds. * * @return the retry delay */ public long getRetryDelay() { return retryDelay; } /** * Sets the time waited between retry delays. * * @param retryDelay the retry delay */ public void setRetryDelay(long retryDelay) { this.retryDelay = retryDelay; } /** * Allow derived classes to perform * some custom closing operations to release resources. */ protected void doClose() { } /** * Perform the given operation, retrying if the connection fails. * * @return object. it needs to be cast to the callee's expected * return type. */ protected Object retryOperation(ZooKeeperOperation operation) throws KeeperException, InterruptedException { KeeperException exception = null; for (int i = 0; i < RETRY_COUNT; i++) { try { return operation.execute(); } catch (KeeperException.SessionExpiredException e) { LOG.warn("Session expired {}. Reconnecting...", zookeeper, e); throw e; } catch (KeeperException.ConnectionLossException e) { if (exception == null) { exception = e; } LOG.debug("Attempt {} failed with connection loss. Reconnecting...", i); retryDelay(i); } } throw exception; } /** * Ensures that the given path exists with no data, the current * ACL and no flags. * * @param path */ protected void ensurePathExists(String path) { ensureExists(path, null, acl, CreateMode.PERSISTENT); } /** * Ensures that the given path exists with the given data, ACL and flags. * * @param path * @param acl * @param flags */ protected void ensureExists( final String path, final byte[] data, final List acl, final CreateMode flags) { try { retryOperation(() -> { Stat stat = zookeeper.exists(path, false); if (stat != null) { return true; } zookeeper.create(path, data, acl, flags); return true; }); } catch (KeeperException | InterruptedException e) { LOG.warn("Unexpected exception", e); } } /** * Returns true if this protocol has been closed. * * @return true if this protocol is closed */ protected boolean isClosed() { return closed.get(); } /** * Performs a retry delay if this is not the first attempt. * * @param attemptCount the number of the attempts performed so far */ protected void retryDelay(int attemptCount) { if (attemptCount > 0) { try { Thread.sleep(attemptCount * retryDelay); } catch (InterruptedException e) { LOG.warn("Failed to sleep.", e); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000210 15051152474 032476 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/lock/WriteLock.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0100644 0000000 0000000 00000024562 15051152474 034174 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; import static org.apache.zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A protocol to implement an exclusive * write lock or to elect a leader. * *

You invoke {@link #lock()} to start the process of grabbing the lock; * you may get the lock then or it may be some time later. * *

You can register a listener so that you are invoked when you get the lock; * otherwise you can ask if you have the lock by calling {@link #isOwner()}. * */ public class WriteLock extends ProtocolSupport { private static final Logger LOG = LoggerFactory.getLogger(WriteLock.class); private final String dir; private String id; private ZNodeName idName; private String ownerId; private String lastChildId; private byte[] data = {0x12, 0x34}; private LockListener callback; private LockZooKeeperOperation zop; /** * zookeeper contructor for writelock. * * @param zookeeper zookeeper client instance * @param dir the parent path you want to use for locking * @param acl the acls that you want to use for all the paths, if null world read/write is used. */ public WriteLock(ZooKeeper zookeeper, String dir, List acl) { super(zookeeper); this.dir = dir; if (acl != null) { setAcl(acl); } this.zop = new LockZooKeeperOperation(); } /** * zookeeper contructor for writelock with callback. * * @param zookeeper the zookeeper client instance * @param dir the parent path you want to use for locking * @param acl the acls that you want to use for all the paths * @param callback the call back instance */ public WriteLock( ZooKeeper zookeeper, String dir, List acl, LockListener callback) { this(zookeeper, dir, acl); this.callback = callback; } /** * return the current locklistener. * * @return the locklistener */ public synchronized LockListener getLockListener() { return this.callback; } /** * register a different call back listener. * * @param callback the call back instance */ public synchronized void setLockListener(LockListener callback) { this.callback = callback; } /** * Removes the lock or associated znode if * you no longer require the lock. this also * removes your request in the queue for locking * in case you do not already hold the lock. * * @throws RuntimeException throws a runtime exception * if it cannot connect to zookeeper. */ public synchronized void unlock() throws RuntimeException { if (!isClosed() && id != null) { // we don't need to retry this operation in the case of failure // as ZK will remove ephemeral files and we don't wanna hang // this process when closing if we cannot reconnect to ZK try { ZooKeeperOperation zopdel = () -> { zookeeper.delete(id, -1); return Boolean.TRUE; }; zopdel.execute(); } catch (InterruptedException e) { LOG.warn("Unexpected exception", e); // set that we have been interrupted. Thread.currentThread().interrupt(); } catch (KeeperException.NoNodeException e) { // do nothing } catch (KeeperException e) { LOG.warn("Unexpected exception", e); throw new RuntimeException(e.getMessage(), e); } finally { LockListener lockListener = getLockListener(); if (lockListener != null) { lockListener.lockReleased(); } id = null; } } } /** * the watcher called on * getting watch while watching * my predecessor. */ private class LockWatcher implements Watcher { public void process(WatchedEvent event) { // lets either become the leader or watch the new/updated node LOG.debug("Watcher fired: {}", event); try { lock(); } catch (Exception e) { LOG.warn("Failed to acquire lock", e); } } } /** * a zookeeper operation that is mainly responsible * for all the magic required for locking. */ private class LockZooKeeperOperation implements ZooKeeperOperation { /** * find if we have been created earler if not create our node. * * @param prefix the prefix node * @param zookeeper teh zookeeper client * @param dir the dir paretn * @throws KeeperException * @throws InterruptedException */ private void findPrefixInChildren(String prefix, ZooKeeper zookeeper, String dir) throws KeeperException, InterruptedException { List names = zookeeper.getChildren(dir, false); for (String name : names) { if (name.startsWith(prefix)) { id = name; LOG.debug("Found id created last time: {}", id); break; } } if (id == null) { id = zookeeper.create(dir + "/" + prefix, data, getAcl(), EPHEMERAL_SEQUENTIAL); LOG.debug("Created id: {}", id); } } /** * the command that is run and retried for actually * obtaining the lock. * * @return if the command was successful or not */ @SuppressFBWarnings( value = "NP_NULL_PARAM_DEREF_NONVIRTUAL", justification = "findPrefixInChildren will assign a value to this.id") public boolean execute() throws KeeperException, InterruptedException { do { if (id == null) { long sessionId = zookeeper.getSessionId(); String prefix = "x-" + sessionId + "-"; // lets try look up the current ID if we failed // in the middle of creating the znode findPrefixInChildren(prefix, zookeeper, dir); idName = new ZNodeName(id); } List names = zookeeper.getChildren(dir, false); if (names.isEmpty()) { LOG.warn("No children in: {} when we've just created one! Lets recreate it...", dir); // lets force the recreation of the id id = null; } else { // lets sort them explicitly (though they do seem to come back in order ususally :) SortedSet sortedNames = new TreeSet<>(); for (String name : names) { sortedNames.add(new ZNodeName(dir + "/" + name)); } ownerId = sortedNames.first().getName(); SortedSet lessThanMe = sortedNames.headSet(idName); if (!lessThanMe.isEmpty()) { ZNodeName lastChildName = lessThanMe.last(); lastChildId = lastChildName.getName(); LOG.debug("Watching less than me node: {}", lastChildId); Stat stat = zookeeper.exists(lastChildId, new LockWatcher()); if (stat != null) { return Boolean.FALSE; } else { LOG.warn("Could not find the stats for less than me: {}", lastChildName.getName()); } } else { if (isOwner()) { LockListener lockListener = getLockListener(); if (lockListener != null) { lockListener.lockAcquired(); } return Boolean.TRUE; } } } } while (id == null); return Boolean.FALSE; } } /** * Attempts to acquire the exclusive write lock returning whether or not it was * acquired. Note that the exclusive lock may be acquired some time later after * this method has been invoked due to the current lock owner going away. */ public synchronized boolean lock() throws KeeperException, InterruptedException { if (isClosed()) { return false; } ensurePathExists(dir); return (Boolean) retryOperation(zop); } /** * return the parent dir for lock. * * @return the parent dir used for locks. */ public String getDir() { return dir; } /** * Returns true if this node is the owner of the * lock (or the leader). */ public boolean isOwner() { return id != null && id.equals(ownerId); } /** * return the id for this lock. * * @return the id for this lock */ public String getId() { return this.id; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000210 15051152474 032476 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/lock/ZNodeName.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0100644 0000000 0000000 00000010232 15051152474 034161 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; import java.util.Objects; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents an immutable ephemeral znode name which has an ordered sequence * number and can be sorted in order. The expected name format of the znode is * as follows: * *

 * <name>-<sequence>
 *
 * For example: lock-00001
 * 
*/ class ZNodeName implements Comparable { private static final Logger LOG = LoggerFactory.getLogger(ZNodeName.class); private final String name; private final String prefix; private final Optional sequence; /** * Instantiate a ZNodeName with the provided znode name. * * @param name The name of the znode * @throws NullPointerException if {@code name} is {@code null} */ public ZNodeName(final String name) { this.name = Objects.requireNonNull(name, "ZNode name cannot be null"); int idx = name.lastIndexOf('-'); if (idx < 0) { this.prefix = name; this.sequence = Optional.empty(); } else { if (idx > 0 && name.charAt(idx - 1) == '-') { idx = idx - 1; } this.prefix = name.substring(0, idx); this.sequence = Optional.ofNullable(parseSequenceString(name.substring(idx + 1))); } } private Integer parseSequenceString(final String seq) { try { return Integer.parseInt(seq); } catch (Exception e) { LOG.warn("Number format exception for {}", seq, e); return null; } } @Override public String toString() { return "ZNodeName [name=" + name + ", prefix=" + prefix + ", sequence=" + sequence + "]"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ZNodeName other = (ZNodeName) o; return name.equals(other.name); } @Override public int hashCode() { return name.hashCode(); } /** * Compare znodes based on their sequence number. * * @param that other znode to compare to * @return the difference between their sequence numbers: a positive value if this * znode has a larger sequence number, 0 if they have the same sequence number * or a negative number if this znode has a lower sequence number */ public int compareTo(final ZNodeName that) { if (this.sequence.isPresent() && that.sequence.isPresent()) { int cseq = Integer.compare(this.sequence.get(), that.sequence.get()); return (cseq != 0) ? cseq : this.prefix.compareTo(that.prefix); } if (this.sequence.isPresent()) { return -1; } if (that.sequence.isPresent()) { return 1; } return this.prefix.compareTo(that.prefix); } /** * Returns the name of the znode. */ public String getName() { return name; } /** * Returns the optional sequence number. */ public Optional getSequence() { return sequence; } /** * Returns the text prefix before the sequence number. */ public String getPrefix() { return prefix; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_main_java_org_ap0100644 0000000 0000000 00000000221 15051152474 032500 xustar000000000 0000000 145 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/recipes/lock/ZooKeeperOperation.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/main/java/org/apache/zookeeper/r0100644 0000000 0000000 00000002644 15051152474 034171 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; import org.apache.zookeeper.KeeperException; /** * A callback object which can be used for implementing retry-able operations in the * {@link org.apache.zookeeper.recipes.lock.ProtocolSupport} class. * */ public interface ZooKeeperOperation { /** * Performs the operation - which may be involved multiple times if the connection * to ZooKeeper closes during this operation. * * @return the result of the operation or null * @throws KeeperException * @throws InterruptedException */ boolean execute() throws KeeperException, InterruptedException; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_test_java_org_ap0100644 0000000 0000000 00000000214 15051152474 032535 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/recipes/lock/WriteLockTest.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/r0100644 0000000 0000000 00000012574 15051152474 034227 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; /** * test for writelock. */ public class WriteLockTest extends ClientBase { protected int sessionTimeout = 10 * 1000; protected String dir = "/" + getClass().getName(); protected WriteLock[] nodes; protected CountDownLatch latch = new CountDownLatch(1); private boolean restartServer = true; private boolean workAroundClosingLastZNodeFails = true; private boolean killLeader = true; @Test public void testRun() throws Exception { runTest(3); } class LockCallback implements LockListener { public void lockAcquired() { latch.countDown(); } public void lockReleased() { } } protected void runTest(int count) throws Exception { nodes = new WriteLock[count]; for (int i = 0; i < count; i++) { ZooKeeper keeper = createClient(); WriteLock leader = new WriteLock(keeper, dir, null); leader.setLockListener(new LockCallback()); nodes[i] = leader; leader.lock(); } // lets wait for any previous leaders to die and one of our new // nodes to become the new leader latch.await(30, TimeUnit.SECONDS); WriteLock first = nodes[0]; dumpNodes(count); // lets assert that the first election is the leader assertTrue(first.isOwner(), "The first znode should be the leader " + first.getId()); for (int i = 1; i < count; i++) { WriteLock node = nodes[i]; assertFalse(node.isOwner(), "Node should not be the leader " + node.getId()); } if (count > 1) { if (killLeader) { System.out.println("Now killing the leader"); // now lets kill the leader latch = new CountDownLatch(1); first.unlock(); latch.await(30, TimeUnit.SECONDS); //Thread.sleep(10000); WriteLock second = nodes[1]; dumpNodes(count); // lets assert that the first election is the leader assertTrue(second.isOwner(), "The second znode should be the leader " + second.getId()); for (int i = 2; i < count; i++) { WriteLock node = nodes[i]; assertFalse(node.isOwner(), "Node should not be the leader " + node.getId()); } } if (restartServer) { // now lets stop the server System.out.println("Now stopping the server"); stopServer(); Thread.sleep(10000); // TODO lets assert that we are no longer the leader dumpNodes(count); System.out.println("Starting the server"); startServer(); Thread.sleep(10000); for (int i = 0; i < count - 1; i++) { System.out.println("Calling acquire for node: " + i); nodes[i].lock(); } dumpNodes(count); System.out.println("Now closing down..."); } } } protected void dumpNodes(int count) { for (int i = 0; i < count; i++) { WriteLock node = nodes[i]; System.out.println("node: " + i + " id: " + node.getId() + " is leader: " + node.isOwner()); } } @AfterEach public void tearDown() throws Exception { if (nodes != null) { for (int i = 0; i < nodes.length; i++) { WriteLock node = nodes[i]; if (node != null) { System.out.println("Closing node: " + i); node.close(); if (workAroundClosingLastZNodeFails && i == nodes.length - 1) { System.out.println("Not closing zookeeper: " + i + " due to bug!"); } else { System.out.println("Closing zookeeper: " + i); node.getZookeeper().close(); System.out.println("Closed zookeeper: " + i); } } } } System.out.println("Now lets stop the server"); super.tearDown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-lock_src_test_java_org_ap0100644 0000000 0000000 00000000214 15051152474 032535 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/recipes/lock/ZNodeNameTest.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-lock/src/test/java/org/apache/zookeeper/r0100644 0000000 0000000 00000016067 15051152474 034230 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.lock; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; /** * Test for znodenames. */ public class ZNodeNameTest { @Test public void testOrderWithSamePrefix() throws Exception { final String[] names = {"x-3", "x-5", "x-11", "x-1", "x--20"}; ZNodeName zname; final Collection nodeNames = Arrays.asList(names).stream() .map(name -> new ZNodeName(name)).sorted().collect(Collectors.toList()); final Iterator it = nodeNames.iterator(); zname = it.next(); assertEquals("x--20", zname.getName()); assertEquals("x", zname.getPrefix()); assertEquals(Integer.valueOf(-20), zname.getSequence().get()); zname = it.next(); assertEquals("x-1", zname.getName()); assertEquals("x", zname.getPrefix()); assertEquals(Integer.valueOf(1), zname.getSequence().get()); zname = it.next(); assertEquals("x-3", zname.getName()); assertEquals("x", zname.getPrefix()); assertEquals(Integer.valueOf(3), zname.getSequence().get()); zname = it.next(); assertEquals("x-5", zname.getName()); assertEquals("x", zname.getPrefix()); assertEquals(Integer.valueOf(5), zname.getSequence().get()); zname = it.next(); assertEquals("x-11", zname.getName()); assertEquals("x", zname.getPrefix()); assertEquals(Integer.valueOf(11), zname.getSequence().get()); } @Test public void testOrderWithDifferentPrefixes() throws Exception { final String[] names = {"r-3", "r-2", "r-1", "w-2", "w-1"}; ZNodeName zname; final Collection nodeNames = Arrays.asList(names).stream() .map(name -> new ZNodeName(name)).sorted().collect(Collectors.toList()); final Iterator it = nodeNames.iterator(); zname = it.next(); assertEquals("r-1", zname.getName()); assertEquals("r", zname.getPrefix()); assertEquals(Integer.valueOf(1), zname.getSequence().get()); zname = it.next(); assertEquals("w-1", zname.getName()); assertEquals("w", zname.getPrefix()); assertEquals(Integer.valueOf(1), zname.getSequence().get()); zname = it.next(); assertEquals("r-2", zname.getName()); assertEquals("r", zname.getPrefix()); assertEquals(Integer.valueOf(2), zname.getSequence().get()); zname = it.next(); assertEquals("w-2", zname.getName()); assertEquals("w", zname.getPrefix()); assertEquals(Integer.valueOf(2), zname.getSequence().get()); zname = it.next(); assertEquals("r-3", zname.getName()); assertEquals("r", zname.getPrefix()); assertEquals(Integer.valueOf(3), zname.getSequence().get()); } @Test public void testOrderWithDifferentPrefixIncludingSessionId() throws Exception { String[] names = { "x-242681582799028564-0000000002", "x-170623981976748329-0000000003", "x-98566387950223723-0000000001" }; ZNodeName zname; final Collection nodeNames = Arrays.asList(names).stream() .map(name -> new ZNodeName(name)).sorted().collect(Collectors.toList()); final Iterator it = nodeNames.iterator(); zname = it.next(); assertEquals("x-98566387950223723-0000000001", zname.getName()); assertEquals("x-98566387950223723", zname.getPrefix()); assertEquals(Integer.valueOf(1), zname.getSequence().get()); zname = it.next(); assertEquals("x-242681582799028564-0000000002", zname.getName()); assertEquals("x-242681582799028564", zname.getPrefix()); assertEquals(Integer.valueOf(2), zname.getSequence().get()); zname = it.next(); assertEquals("x-170623981976748329-0000000003", zname.getName()); assertEquals("x-170623981976748329", zname.getPrefix()); assertEquals(Integer.valueOf(3), zname.getSequence().get()); } @Test public void testOrderWithExtraPrefixes() throws Exception { String[] names = {"r-1-3-2", "r-2-2-1", "r-3-1-3"}; ZNodeName zname; final Collection nodeNames = Arrays.asList(names).stream() .map(name -> new ZNodeName(name)).sorted().collect(Collectors.toList()); final Iterator it = nodeNames.iterator(); zname = it.next(); assertEquals("r-2-2-1", zname.getName()); assertEquals("r-2-2", zname.getPrefix()); assertEquals(Integer.valueOf(1), zname.getSequence().get()); zname = it.next(); assertEquals("r-1-3-2", zname.getName()); assertEquals("r-1-3", zname.getPrefix()); assertEquals(Integer.valueOf(2), zname.getSequence().get()); zname = it.next(); assertEquals("r-3-1-3", zname.getName()); assertEquals("r-3-1", zname.getPrefix()); assertEquals(Integer.valueOf(3), zname.getSequence().get()); } @Test public void testMissingSequenceNumber() throws Exception { String[] names = {"c", "b-1", "a"}; ZNodeName zname; final Collection nodeNames = Arrays.asList(names).stream() .map(name -> new ZNodeName(name)).sorted().collect(Collectors.toList()); final Iterator it = nodeNames.iterator(); zname = it.next(); assertEquals("b-1", zname.getName()); assertEquals("b", zname.getPrefix()); assertEquals(Integer.valueOf(1), zname.getSequence().get()); zname = it.next(); assertEquals("a", zname.getName()); assertEquals("a", zname.getPrefix()); assertFalse(zname.getSequence().isPresent()); zname = it.next(); assertEquals("c", zname.getName()); assertEquals("c", zname.getPrefix()); assertFalse(zname.getSequence().isPresent()); } @Test public void testNullName() { assertThrows(NullPointerException.class, () -> { new ZNodeName(null); }); } } apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/README.txt0100644 0000000 0000000 00000002613 15051152474 027224 0ustar00rootroot0000000 0000000 1) This queue interface recipe implements the queue recipe mentioned in ../../../docs/recipes.[html,pdf]. A more detailed explanation is at http://www.cloudera.com/blog/2009/05/28/building-a-distributed-concurrent-queue-with-apache-zookeeper/ 2) This recipe does not handle KeeperException.ConnectionLossException or ZCONNECTIONLOSS. It will only work correctly once ZOOKEEPER-22 https://issues.apache.org/jira/browse/ZOOKEEPER-22 is resolved. 3) To compile the queue java recipe you can just run ant jar from this directory. Please report any bugs on the jira http://issues.apache.org/jira/browse/ZOOKEEPER apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/build.xml0100644 0000000 0000000 00000013232 15051152474 027346 0ustar00rootroot0000000 0000000 Tests failed! apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/pom.xml0100755 0000000 0000000 00000004711 15051152474 027047 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper zookeeper-recipes 3.9.4 zookeeper-recipes-queue jar Apache ZooKeeper - Recipes - Queue This queue interface recipe implements the queue recipe A more detailed explanation is at: http://www.cloudera.com/blog/2009/05/28/building-a-distributed-concurrent-queue-with-apache-zookeeper/ This recipe does not handle KeeperException.ConnectionLossException or ZCONNECTIONLOSS. It will only work correctly once ZOOKEEPER-22 https://issues.apache.org/jira/browse/ZOOKEEPER-22 is resolved. org.apache.maven.plugins maven-surefire-plugin ${surefire-forkcount} false -Xmx512m ${project.basedir} true ${project.build.directory}/surefire apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/INSTALL0100644 0000000 0000000 00000022310 15051152474 030510 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/LICENSE0100644 0000000 0000000 00000026136 15051152474 030476 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/Makefile.am0100644 0000000 0000000 00000003274 15051152474 031523 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 $(top_srcdir)/aminclude.am AM_CFLAGS = -Wall -fPIC -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated \ -I$(top_srcdir)/include -I/usr/include AM_CPPFLAGS = -Wall -I${ZOOKEEPER_PATH}/include -I${ZOOKEEPER_PATH}/generated\ -I${top_srcdir}/include -I/usr/include EXTRA_DIST = LICENSE lib_LTLIBRARIES = libzooqueue.la libzooqueue_la_SOURCES = src/zoo_queue.c include/zoo_queue.h libzooqueue_la_CPPFLAGS = -DDLOPEN_MODULE libzooqueue_la_LDFLAGS = -version-info 0:1:0 #run the tests now TEST_SOURCES = tests/TestDriver.cc tests/TestClient.cc tests/Util.cc check_PROGRAMS = zkqueuetest nodist_zkqueuetest_SOURCES = ${TEST_SOURCES} zkqueuetest_LDADD = ${ZOOKEEPER_LD} libzooqueue.la -lpthread ${CPPUNIT_LIBS} zkqueuetest_CXXFLAGS = -DUSE_STATIC_LIB ${CPPUNIT_CFLAGS} run-check: check ./zkqueuetest ${TEST_OPTIONS} clean-local: clean-check ${RM} ${DX_CLEANFILES} clean-check: ${RM} ${nodist_zkqueuetest_OBJECTS} apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/README.txt0100644 0000000 0000000 00000002675 15051152474 031171 0ustar00rootroot0000000 0000000 Zookeeper C queue client library 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. This recipe does not handle ZCONNECTIONLOSS. It will only work correctly once ZOOKEEPER-22 https://issues.apache.org/jira/browse/ZOOKEEPER-22 is resolved. 1) make sure that you compile the main zookeeper c client library. 2) change directory to zookeeper-recipes/zookeeper-recipes-queue/src/main/c and do a "autoreconf -if" to bootstrap autoconf, automake and libtool. Please make sure you have autoconf version 2.59 or greater installed. 3) do a "./configure [OPTIONS]" to generate the makefile. See INSTALL for general information about running configure. 4) 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. 5) 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" apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/acinclude.m40100644 0000000 0000000 00000026415 15051152474 031662 0ustar00rootroot0000000 0000000 # 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 of 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 ]) apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/aminclude.am0100644 0000000 0000000 00000011175 15051152474 031746 0ustar00rootroot0000000 0000000 # 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 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/c-doc.Doxyfile0100644 0000000 0000000 00000143152 15051152474 032161 0ustar00rootroot0000000 0000000 # 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/zoo_queue.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 apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/configure.ac0100644 0000000 0000000 00000004336 15051152474 031755 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) AC_INIT([zooqueue], [3.2.0]) AC_CONFIG_SRCDIR([include/zoo_queue.h]) PACKAGE=zooqueue VERSION=1.0 AC_SUBST(PACKAGE) AC_SUBST(VERSION) BUILD_PATH="`pwd`" # Checks for programs. AC_LANG_CPLUSPLUS AM_INIT_AUTOMAKE([-Wall foreign]) # Checks for libraries. #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-queues],[c-doc.Doxyfile],[docs]) ZOOKEEPER_PATH=${BUILD_PATH}/../../../../../zookeeper-client/zookeeper-client-c ZOOKEEPER_LD=-L${BUILD_PATH}/../../../../../zookeeper-client/zookeeper-client-c\ -lzookeeper_mt 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_OFF_T AC_TYPE_SIZE_T AC_STRUCT_ST_BLOCKS AC_HEADER_TIME AC_C_VOLATILE AC_PROG_CC AC_PROG_LIBTOOL #check for cppunit AM_PATH_CPPUNIT(1.10.2) # 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_OUTPUT AC_C_VOLATILE apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/include/zoo_queue.h0100644 0000000 0000000 00000010167 15051152474 033275 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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_QUEUE_H_ #define ZOOKEEPER_QUEUE_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** * \file zoo_queue.h * \brief zookeeper recipe for queues. */ struct zkr_queue { zhandle_t *zh; char *path; struct ACL_vector *acl; pthread_mutex_t pmutex; char *node_name; int node_name_length; char *cached_create_path; }; typedef struct zkr_queue zkr_queue_t; /** * \brief initializes a zookeeper queue * * this method instantiates a zookeeper queue * \param queue the zookeeper queue to initialize * \param zh the zookeeper handle to use * \param path the path in zookeeper to use for the queue * \param acl the acl to use in zookeeper. * \return return 0 if successful. */ ZOOAPI int zkr_queue_init(zkr_queue_t *queue, zhandle_t* zh, char* path, struct ACL_vector *acl); /** * \brief adds an element to a zookeeper queue * * this method adds an element to the back of a zookeeper queue. * \param queue the zookeeper queue to add the element to * \param data a pointer to a data buffer * \param buffer_len the length of the buffer * \return returns 0 (ZOK) if successful, otherwise returns a zookeeper error code. */ ZOOAPI int zkr_queue_offer(zkr_queue_t *queue, const char *data, int buffer_len); /** * \brief returns the head of a zookeeper queue * * this method returns the head of a zookeeper queue without removing it. * \param queue the zookeeper queue to add the element to * \param buffer a pointer to a data buffer * \param buffer_len a pointer to the length of the buffer * \return returns 0 (ZOK) and sets *buffer_len to the length of data written if successful (-1 if the queue is empty). Otherwise it will set *buffer_len to -1 and return a zookeeper error code. */ ZOOAPI int zkr_queue_element(zkr_queue_t *queue, char *buffer, int *buffer_len); /** * \brief returns the head of a zookeeper queue * * this method returns the head of a zookeeper queue without removing it. * \param queue the zookeeper queue to get the head of * \param buffer a pointer to a data buffer * \param buffer_len a pointer to the length of the buffer * \return returns 0 (ZOK) and sets *buffer_len to the length of data written if successful (-1 if the queue is empty). Otherwise it will set *buffer_len to -1 and return a zookeeper error code. */ ZOOAPI int zkr_queue_remove(zkr_queue_t *queue, char *buffer, int *buffer_len); /** * \brief removes and returns the head of a zookeeper queue, blocks if necessary * * this method returns the head of a zookeeper queue without removing it. * \param queue the zookeeper queue to remove and return the head of * \param buffer a pointer to a data buffer * \param buffer_len a pointer to the length of the buffer * \return returns 0 (ZOK) and sets *buffer_len to the length of data written if successful. Otherwise it will set *buffer_len to -1 and return a zookeeper error code. */ ZOOAPI int zkr_queue_take(zhandle_t *zh, zkr_queue_t *queue, char *buffer, int *buffer_len); /** * \brief destroys a zookeeper queue structure * * this destroys a zookeeper queue structure, this is only a local operation and will not affect * the state of the queue on the zookeeper server. * \param queue the zookeeper queue to destroy */ void zkr_queue_destroy(zkr_queue_t *queue); #ifdef __cplusplus } #endif #endif //ZOOKEEPER_QUEUE_H_ apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/src/zoo_queue.c0100644 0000000 0000000 00000033535 15051152474 032440 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 DLL_EXPORT #define USE_STATIC_LIB #endif #if defined(__CYGWIN__) #define USE_IPV6 #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_GETPWUID_R #include #endif #define IF_DEBUG(x) if (logLevel==ZOO_LOG_LEVEL_DEBUG) {x;} static void free_String_vector(struct String_vector *v) { if (v->data) { int32_t i; for (i=0; icount; i++) { free(v->data[i]); } free(v->data); v->data = 0; } } static int vstrcmp(const void* str1, const void* str2) { const char **a = (const char**)str1; const char **b = (const char**) str2; return strcmp(*a, *b); } static void sort_children(struct String_vector *vector) { qsort( vector->data, vector->count, sizeof(char*), &vstrcmp); } static void concat_path_nodename_n(char *buffer, int len, const char *path, const char *node_name){ snprintf(buffer, len, "%s/%s", path, node_name); } static char *concat_path_nodename(const char *path, const char *node_name){ int node_path_length = strlen(path) + 1+ strlen(node_name) +1; char *node_path = (char *) malloc(node_path_length * sizeof(char)); concat_path_nodename_n(node_path, node_path_length, path, node_name); return node_path; } static void zkr_queue_cache_create_path(zkr_queue_t *queue){ if(queue->cached_create_path != NULL){ free(queue->cached_create_path); } queue->cached_create_path = concat_path_nodename(queue->path, queue->node_name); } ZOOAPI int zkr_queue_init(zkr_queue_t *queue, zhandle_t* zh, char* path, struct ACL_vector *acl){ queue->zh = zh; queue->path = path; queue->node_name = "qn-"; queue->node_name_length = strlen(queue->node_name); queue->cached_create_path = NULL; queue->acl = acl; pthread_mutex_init(&(queue->pmutex), NULL); zkr_queue_cache_create_path(queue); return 0; } static ZOOAPI int create_queue_root(zkr_queue_t *queue){ return zoo_create(queue->zh, queue->path, NULL, 0, queue->acl, 0, NULL, 0 ); } static int valid_child_name(zkr_queue_t *queue, const char *child_name){ return strncmp(queue->node_name, child_name, queue->node_name_length); } ZOOAPI int zkr_queue_offer(zkr_queue_t *queue, const char *data, int buffer_len){ for(;;){ int rc = zoo_create(queue->zh, queue->cached_create_path, data, buffer_len, queue->acl, ZOO_SEQUENCE, NULL, 0 ); switch(rc){ int create_root_rc; case ZNONODE: create_root_rc = create_queue_root(queue); switch(create_root_rc){ case ZNODEEXISTS: case ZOK: break; default: return create_root_rc; } break; default: return rc; } } } ZOOAPI int zkr_queue_element(zkr_queue_t *queue, char *buffer, int *buffer_len){ int path_length = strlen(queue->path); for(;;){ struct String_vector stvector; struct String_vector *vector = &stvector; /*Get sorted children*/ int get_children_rc = zoo_get_children(queue->zh, queue->path, 0, vector); switch(get_children_rc){ case ZOK: break; case ZNONODE: *buffer_len = -1; return ZOK; default: return get_children_rc; } if(stvector.count == 0){ *buffer_len = -1; return ZOK; } sort_children(vector); /*try all*/ int i; for(i=0; i < stvector.count; i++){ char *child_name = stvector.data[i]; int child_path_length = path_length + 1 + strlen(child_name) +1; char child_path[child_path_length]; concat_path_nodename_n(child_path, child_path_length, queue->path, child_name); int get_rc = zoo_get(queue->zh, child_path, 0, buffer, buffer_len, NULL); switch(get_rc){ case ZOK: free_String_vector(vector); return ZOK; case ZNONODE: break; default: free_String_vector(vector); return get_rc; } } free_String_vector(vector); } } ZOOAPI int zkr_queue_remove(zkr_queue_t *queue, char *buffer, int *buffer_len){ int path_length = strlen(queue->path); for(;;){ struct String_vector stvector; struct String_vector *vector = &stvector; /*Get sorted children*/ int get_children_rc = zoo_get_children(queue->zh, queue->path, 0, &stvector); switch(get_children_rc){ case ZOK: break; case ZNONODE: *buffer_len = -1; return ZOK; default: *buffer_len = -1; return get_children_rc; } if(stvector.count == 0){ *buffer_len = -1; return ZOK; } sort_children(vector); /*try all*/ int i; for( i=0; i < stvector.count; i++){ char *child_name = stvector.data[i]; int child_path_length = path_length + 1 + strlen(child_name) +1; char child_path[child_path_length]; concat_path_nodename_n(child_path, child_path_length, queue->path, child_name); int get_rc = zoo_get(queue->zh, child_path, 0, buffer, buffer_len, NULL); switch(get_rc){ int delete_rc; case ZOK: delete_rc = zoo_delete(queue->zh, child_path, -1); switch(delete_rc){ case ZOK: free_String_vector(vector); return delete_rc; case ZNONODE: break; default: free_String_vector(vector); *buffer_len = -1; return delete_rc; } break; case ZNONODE: break; default: free_String_vector(vector); *buffer_len = -1; return get_rc; } } free_String_vector(vector); } } /** * The take_latch structure roughly emulates a Java CountdownLatch with 1 as the initial value. * It is meant to be used by a setter thread and a waiter thread. * * This latch is specialized to be used with the queue, all latches created for the same queue structure will use the same mutex. * * The setter thread at some point will call take_latch_setter_trigger_latch() on the thread. * * The waiter thread creates the latch and at some point either calls take_latch_waiter_await()s or take_latch_waiter_mark_unneeded()s it. * The await function will return after the setter thread has triggered the latch. * The mark unneeded function will return immediately and avoid some unneeded initialization. * * Whichever thread is last to call their required function disposes of the latch. * * The latch may disposed if no threads will call the waiting, marking, or triggering functions using take_latch_destroy_syncrhonized(). */ struct take_latch { enum take_state {take_init, take_waiting, take_triggered, take_not_needed} state; pthread_cond_t latch_condition; zkr_queue_t *queue; }; typedef struct take_latch take_latch_t; static void take_latch_init( take_latch_t *latch, zkr_queue_t *queue){ pthread_mutex_t *mutex = &(queue->pmutex); pthread_mutex_lock(mutex); latch->state = take_init; latch->queue = queue; pthread_mutex_unlock(mutex); } static take_latch_t *create_take_latch(zkr_queue_t *queue){ take_latch_t *new_take_latch = (take_latch_t *) malloc(sizeof(take_latch_t)); take_latch_init(new_take_latch, queue); return new_take_latch; } //Only call this when you own the mutex static void take_latch_destroy_unsafe(take_latch_t *latch){ if(latch->state == take_waiting){ pthread_cond_destroy(&(latch->latch_condition)); } free(latch); } static void take_latch_destroy_synchronized(take_latch_t *latch){ pthread_mutex_t *mutex = &(latch->queue->pmutex); pthread_mutex_lock(mutex); take_latch_destroy_unsafe(latch); pthread_mutex_unlock(mutex); } static void take_latch_setter_trigger_latch(zhandle_t *zh, take_latch_t *latch){ pthread_mutex_t *mutex = &(latch->queue->pmutex); pthread_mutex_lock(mutex); switch(latch->state){ case take_init: latch->state = take_triggered; break; case take_not_needed: take_latch_destroy_unsafe(latch); break; case take_triggered: LOG_DEBUG(LOGCALLBACK(zh), ("Error! Latch was triggered twice.")); break; case take_waiting: pthread_cond_signal(&(latch->latch_condition)); break; } pthread_mutex_unlock(mutex); } static void take_latch_waiter_await(zhandle_t *zh, take_latch_t *latch){ pthread_mutex_t *mutex = &(latch->queue->pmutex); pthread_mutex_lock(mutex); switch(latch->state){ case take_init: pthread_cond_init(&(latch->latch_condition),NULL); latch->state = take_waiting; pthread_cond_wait(&(latch->latch_condition),mutex); take_latch_destroy_unsafe(latch); break; case take_waiting: LOG_DEBUG(LOGCALLBACK(zh), ("Error! Called await twice.")); break; case take_not_needed: LOG_DEBUG(LOGCALLBACK(zh), ("Error! Waiting after marking not needed.")); break; case take_triggered: take_latch_destroy_unsafe(latch); break; } pthread_mutex_unlock(mutex); } static void take_latch_waiter_mark_unneeded(zhandle_t *zh, take_latch_t *latch){ pthread_mutex_t *mutex = &(latch->queue->pmutex); pthread_mutex_lock(mutex); switch(latch->state){ case take_init: latch->state = take_not_needed; break; case take_waiting: LOG_DEBUG(LOGCALLBACK(zh), ("Error! Can't mark unneeded after waiting.")); break; case take_not_needed: LOG_DEBUG(LOGCALLBACK(zh), ("Marked unneeded twice.")); break; case take_triggered: take_latch_destroy_unsafe(latch); break; } pthread_mutex_unlock(mutex); } static void take_watcher(zhandle_t *zh, int type, int state, const char *path, void *watcherCtx){ take_latch_t *latch = (take_latch_t *) watcherCtx; take_latch_setter_trigger_latch(zh, latch); } ZOOAPI int zkr_queue_take(zhandle_t *zh, zkr_queue_t *queue, char *buffer, int *buffer_len){ int path_length = strlen(queue->path); take_attempt: for(;;){ struct String_vector stvector; struct String_vector *vector = &stvector; /*Get sorted children*/ take_latch_t *take_latch = create_take_latch(queue); int get_children_rc = zoo_wget_children(queue->zh, queue->path, take_watcher, take_latch, &stvector); switch(get_children_rc){ case ZOK: break; int create_queue_rc; case ZNONODE: take_latch_destroy_synchronized(take_latch); create_queue_rc = create_queue_root(queue); switch(create_queue_rc){ case ZNODEEXISTS: case ZOK: goto take_attempt; default: *buffer_len = -1; return create_queue_rc; } default: take_latch_destroy_synchronized(take_latch); *buffer_len = -1; return get_children_rc; } if(stvector.count == 0){ take_latch_waiter_await(zh, take_latch); }else{ take_latch_waiter_mark_unneeded(zh, take_latch); } sort_children(vector); /*try all*/ int i; for( i=0; i < stvector.count; i++){ char *child_name = stvector.data[i]; int child_path_length = path_length + 1 + strlen(child_name) +1; char child_path[child_path_length]; concat_path_nodename_n(child_path, child_path_length, queue->path, child_name); int get_rc = zoo_get(queue->zh, child_path, 0, buffer, buffer_len, NULL); switch(get_rc){ int delete_rc; case ZOK: delete_rc = zoo_delete(queue->zh, child_path, -1); switch(delete_rc){ case ZOK: free_String_vector(vector); return delete_rc; case ZNONODE: break; default: free_String_vector(vector); *buffer_len = -1; return delete_rc; } break; case ZNONODE: break; default: free_String_vector(vector); *buffer_len = -1; return get_rc; } } free_String_vector(vector); } } ZOOAPI void zkr_queue_destroy(zkr_queue_t *queue){ pthread_mutex_destroy(&(queue->pmutex)); if(queue->cached_create_path != NULL){ free(queue->cached_create_path); } } apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/tests/TestClient.cc0100644 0000000 0000000 00000032657 15051152474 033225 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 using namespace std; #include #include #include #include static void yield(zhandle_t *zh, int i) { sleep(i); } typedef struct evt { string path; int type; } evt_t; typedef struct watchCtx { private: list events; public: bool connected; zhandle_t *zh; watchCtx() { connected = false; zh = 0; } ~watchCtx() { if (zh) { zookeeper_close(zh); zh = 0; } } evt_t getEvent() { evt_t evt; evt = events.front(); events.pop_front(); return evt; } int countEvents() { int count; count = events.size(); return count; } void putEvent(evt_t evt) { events.push_back(evt); } 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; extern "C" { const char *thread_test_string="Hello World!"; void *offer_thread_shared_queue(void *queue_handle){ zkr_queue_t *queue = (zkr_queue_t *) queue_handle; int test_string_buffer_length = strlen(thread_test_string) + 1; int offer_rc = zkr_queue_offer(queue, thread_test_string, test_string_buffer_length); pthread_exit(NULL); } void *take_thread_shared_queue(void *queue_handle){ zkr_queue_t *queue = (zkr_queue_t *) queue_handle; int test_string_buffer_length = strlen(thread_test_string) + 1; int receive_buffer_capacity = test_string_buffer_length; int receive_buffer_length = receive_buffer_capacity; char *receive_buffer = (char *) malloc(sizeof(char) * receive_buffer_capacity); int remove_rc = zkr_queue_take(queue, receive_buffer, &receive_buffer_length); switch(remove_rc){ case ZOK: pthread_exit(receive_buffer); default: free(receive_buffer); pthread_exit(NULL); } } int valid_test_string(void *result){ char *result_string = (char *) result; return !strncmp(result_string, thread_test_string, strlen(thread_test_string)); } } class Zookeeper_queuetest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(Zookeeper_queuetest); CPPUNIT_TEST(testInitDestroy); CPPUNIT_TEST(testOffer1); CPPUNIT_TEST(testOfferRemove1); CPPUNIT_TEST(testOfferRemove2); CPPUNIT_TEST(testOfferRemove3); CPPUNIT_TEST(testOfferRemove4); CPPUNIT_TEST(testOfferRemove5); CPPUNIT_TEST(testOfferRemove6); CPPUNIT_TEST(testOfferTake1); CPPUNIT_TEST(testOfferTake2); CPPUNIT_TEST(testOfferTake3); CPPUNIT_TEST(testOfferTake4); CPPUNIT_TEST(testOfferTake5); CPPUNIT_TEST(testOfferTake6); 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; } public: #define ZKSERVER_CMD "./tests/zkServer.sh" void setUp() { char cmd[1024]; sprintf(cmd, "%s startClean %s", ZKSERVER_CMD, getHostPorts()); CPPUNIT_ASSERT(system(cmd) == 0); } void startServer() { char cmd[1024]; sprintf(cmd, "%s start %s", ZKSERVER_CMD, getHostPorts()); CPPUNIT_ASSERT(system(cmd) == 0); } void stopServer() { tearDown(); } void tearDown() { char cmd[1024]; sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts()); CPPUNIT_ASSERT(system(cmd) == 0); } void initializeQueuesAndHandles(int num_clients, zhandle_t *zoohandles[], watchctx_t ctxs[], zkr_queue_t queues[], char *path){ int i; for(i=0; i< num_clients; i++){ zoohandles[i] = createClient(&ctxs[i]); zkr_queue_init(&queues[i], zoohandles[i], path, &ZOO_OPEN_ACL_UNSAFE); } } void cleanUpQueues(int num_clients, zkr_queue_t queues[]){ int i; for(i=0; i < num_clients; i++){ zkr_queue_destroy(&queues[i]); } } void testInitDestroy(){ int num_clients = 1; watchctx_t ctxs[num_clients]; zhandle_t *zoohandles[num_clients]; zkr_queue_t queues[num_clients]; char *path= (char *)"/testInitDestroy"; int i; for(i=0; i< num_clients; i++){ zoohandles[i] = createClient(&ctxs[i]); zkr_queue_init(&queues[i], zoohandles[i], path, &ZOO_OPEN_ACL_UNSAFE); } for(i=0; i< num_clients; i++){ zkr_queue_destroy(&queues[i]); } } void testOffer1(){ int num_clients = 1; watchctx_t ctxs[num_clients]; zhandle_t *zoohandles[num_clients]; zkr_queue_t queues[num_clients]; char *path= (char *)"/testOffer1"; initializeQueuesAndHandles(num_clients, zoohandles, ctxs, queues, path); const char *test_string="Hello World!"; int test_string_length = strlen(test_string); int test_string_buffer_length = test_string_length + 1; char buffer[test_string_buffer_length]; int offer_rc = zkr_queue_offer(&queues[0], test_string, test_string_buffer_length); CPPUNIT_ASSERT(offer_rc == ZOK); int removed_element_buffer_length = test_string_buffer_length; int remove_rc = zkr_queue_remove(&queues[0], buffer, &removed_element_buffer_length); CPPUNIT_ASSERT(remove_rc == ZOK); CPPUNIT_ASSERT(removed_element_buffer_length == test_string_buffer_length); CPPUNIT_ASSERT(strncmp(test_string,buffer,test_string_length)==0); cleanUpQueues(num_clients,queues); } void create_n_remove_m(char *path, int n, int m){ int num_clients = 2; watchctx_t ctxs[num_clients]; zhandle_t *zoohandles[num_clients]; zkr_queue_t queues[num_clients]; initializeQueuesAndHandles(num_clients, zoohandles, ctxs, queues, path); int i; int max_digits = sizeof(int)*3; const char *test_string = "Hello World!"; int buffer_length = strlen(test_string) + max_digits + 1; char correct_buffer[buffer_length]; char receive_buffer[buffer_length]; for(i = 0; i < n; i++){ snprintf(correct_buffer, buffer_length, "%s%d", test_string,i); int offer_rc = zkr_queue_offer(&queues[0], correct_buffer, buffer_length); CPPUNIT_ASSERT(offer_rc == ZOK); } printf("Offers\n"); for(i=0; i=n){ CPPUNIT_ASSERT(receive_buffer_length == -1); }else{ CPPUNIT_ASSERT(strncmp(correct_buffer,receive_buffer, buffer_length)==0); } } cleanUpQueues(num_clients,queues); } void testOfferRemove1(){ create_n_remove_m((char *)"/testOfferRemove1", 0,1); } void testOfferRemove2(){ create_n_remove_m((char *)"/testOfferRemove2", 1,1); } void testOfferRemove3(){ create_n_remove_m((char *)"/testOfferRemove3", 10,1); } void testOfferRemove4(){ create_n_remove_m((char *)"/testOfferRemove4", 10,10); } void testOfferRemove5(){ create_n_remove_m((char *)"/testOfferRemove5", 10,5); } void testOfferRemove6(){ create_n_remove_m((char *)"/testOfferRemove6", 10,11); } void create_n_take_m(char *path, int n, int m){ CPPUNIT_ASSERT(m<=n); int num_clients = 2; watchctx_t ctxs[num_clients]; zhandle_t *zoohandles[num_clients]; zkr_queue_t queues[num_clients]; initializeQueuesAndHandles(num_clients, zoohandles, ctxs, queues, path); int i; int max_digits = sizeof(int)*3; const char *test_string = "Hello World!"; int buffer_length = strlen(test_string) + max_digits + 1; char correct_buffer[buffer_length]; char receive_buffer[buffer_length]; for(i = 0; i < n; i++){ snprintf(correct_buffer, buffer_length, "%s%d", test_string,i); int offer_rc = zkr_queue_offer(&queues[0], correct_buffer, buffer_length); CPPUNIT_ASSERT(offer_rc == ZOK); } printf("Offers\n"); for(i=0; i=n){ CPPUNIT_ASSERT(receive_buffer_length == -1); }else{ CPPUNIT_ASSERT(strncmp(correct_buffer,receive_buffer, buffer_length)==0); } } cleanUpQueues(num_clients,queues); } void testOfferTake1(){ create_n_take_m((char *)"/testOfferTake1", 2,1); } void testOfferTake2(){ create_n_take_m((char *)"/testOfferTake2", 1,1); } void testOfferTake3(){ create_n_take_m((char *)"/testOfferTake3", 10,1); } void testOfferTake4(){ create_n_take_m((char *)"/testOfferTake4", 10,10); } void testOfferTake5(){ create_n_take_m((char *)"/testOfferTake5", 10,5); } void testOfferTake6(){ create_n_take_m((char *)"/testOfferTake6", 12,11); } void testTakeThreaded(){ int num_clients = 1; watchctx_t ctxs[num_clients]; zhandle_t *zoohandles[num_clients]; zkr_queue_t queues[num_clients]; char *path=(char *)"/testTakeThreaded"; initializeQueuesAndHandles(num_clients, zoohandles, ctxs, queues, path); pthread_t take_thread; pthread_create(&take_thread, NULL, take_thread_shared_queue, (void *) &queues[0]); usleep(1000); pthread_t offer_thread; pthread_create(&offer_thread, NULL, offer_thread_shared_queue, (void *) &queues[0]); pthread_join(offer_thread, NULL); void *take_thread_result; pthread_join(take_thread, &take_thread_result); CPPUNIT_ASSERT(take_thread_result != NULL); CPPUNIT_ASSERT(valid_test_string(take_thread_result)); cleanUpQueues(num_clients,queues); } void testTakeThreaded2(){ int num_clients = 1; watchctx_t ctxs[num_clients]; zhandle_t *zoohandles[num_clients]; zkr_queue_t queues[num_clients]; char *path=(char *)"/testTakeThreaded2"; initializeQueuesAndHandles(num_clients, zoohandles, ctxs, queues, path); int take_attempts; int num_take_attempts = 2; for(take_attempts=0; take_attempts < num_take_attempts; take_attempts++){ pthread_t take_thread; pthread_create(&take_thread, NULL, take_thread_shared_queue, (void *) &queues[0]); usleep(1000); pthread_t offer_thread; pthread_create(&offer_thread, NULL, offer_thread_shared_queue, (void *) &queues[0]); pthread_join(offer_thread, NULL); void *take_thread_result; pthread_join(take_thread, &take_thread_result); CPPUNIT_ASSERT(take_thread_result != NULL); CPPUNIT_ASSERT(valid_test_string(take_thread_result)); } cleanUpQueues(num_clients,queues); } }; const char Zookeeper_queuetest::hostPorts[] = "127.0.0.1:22181"; CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_queuetest); apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/c/tests/TestDriver.cc0100644 0000000 0000000 00000007021 15051152474 033225 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 "Util.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); // 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 ); // Add a listener that print dots as tests run. // CPPUNIT_NS::TextTestProgressListener progress; CPPUNIT_NS::BriefTestProgressListener progress; controller.addListener( &progress ); CPPUNIT_NS::TestRunner runner; runner.addTest( CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest() ); try { cout << "Running " << globalTestConfig.getTestName(); runner.run( controller, globalTestConfig.getTestName()); cout< #include #include // 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(3,__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); // 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 /tmp/zk.log & echo $! > /tmp/zk.pid sleep 5 ;; stop) # Already killed above ;; *) echo "Unknown command " + $1 exit 2 esac ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_main_java_org_a0100644 0000000 0000000 00000000221 15051152474 032514 xustar000000000 0000000 145 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/recipes/queue/DistributedQueue.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/main/java/org/apache/zookeeper/0100644 0000000 0000000 00000024260 15051152474 034201 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.queue; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.TreeMap; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A protocol to implement a distributed queue. */ public class DistributedQueue { private static final Logger LOG = LoggerFactory.getLogger(DistributedQueue.class); private final String dir; private ZooKeeper zookeeper; private List acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; private final String prefix = "qn-"; public DistributedQueue(ZooKeeper zookeeper, String dir, List acl) { this.dir = dir; if (acl != null) { this.acl = acl; } this.zookeeper = zookeeper; } /** * Returns a Map of the children, ordered by id. * @param watcher optional watcher on getChildren() operation. * @return map from id to child name for all children */ private Map orderedChildren(Watcher watcher) throws KeeperException, InterruptedException { Map orderedChildren = new TreeMap<>(); List childNames; childNames = zookeeper.getChildren(dir, watcher); for (String childName : childNames) { try { //Check format if (!childName.regionMatches(0, prefix, 0, prefix.length())) { LOG.warn("Found child node with improper name: {}", childName); continue; } String suffix = childName.substring(prefix.length()); Long childId = Long.parseLong(suffix); orderedChildren.put(childId, childName); } catch (NumberFormatException e) { LOG.warn("Found child node with improper format : {}", childName, e); } } return orderedChildren; } /** * Find the smallest child node. * @return The name of the smallest child node. */ private String smallestChildName() throws KeeperException, InterruptedException { long minId = Long.MAX_VALUE; String minName = ""; List childNames; try { childNames = zookeeper.getChildren(dir, false); } catch (KeeperException.NoNodeException e) { LOG.warn("Unexpected exception", e); return null; } for (String childName : childNames) { try { //Check format if (!childName.regionMatches(0, prefix, 0, prefix.length())) { LOG.warn("Found child node with improper name: {}", childName); continue; } String suffix = childName.substring(prefix.length()); long childId = Long.parseLong(suffix); if (childId < minId) { minId = childId; minName = childName; } } catch (NumberFormatException e) { LOG.warn("Found child node with improper format : {}", childName, e); } } if (minId < Long.MAX_VALUE) { return minName; } else { return null; } } /** * Return the head of the queue without modifying the queue. * @return the data at the head of the queue. * @throws NoSuchElementException * @throws KeeperException * @throws InterruptedException */ public byte[] element() throws NoSuchElementException, KeeperException, InterruptedException { Map orderedChildren; // element, take, and remove follow the same pattern. // We want to return the child node with the smallest sequence number. // Since other clients are remove()ing and take()ing nodes concurrently, // the child with the smallest sequence number in orderedChildren might be gone by the time we check. // We don't call getChildren again until we have tried the rest of the nodes in sequence order. while (true) { try { orderedChildren = orderedChildren(null); } catch (KeeperException.NoNodeException e) { throw new NoSuchElementException(); } if (orderedChildren.size() == 0) { throw new NoSuchElementException(); } for (String headNode : orderedChildren.values()) { if (headNode != null) { try { return zookeeper.getData(dir + "/" + headNode, false, null); } catch (KeeperException.NoNodeException e) { //Another client removed the node first, try next } } } } } /** * Attempts to remove the head of the queue and return it. * @return The former head of the queue * @throws NoSuchElementException * @throws KeeperException * @throws InterruptedException */ public byte[] remove() throws NoSuchElementException, KeeperException, InterruptedException { Map orderedChildren; // Same as for element. Should refactor this. while (true) { try { orderedChildren = orderedChildren(null); } catch (KeeperException.NoNodeException e) { throw new NoSuchElementException(); } if (orderedChildren.size() == 0) { throw new NoSuchElementException(); } for (String headNode : orderedChildren.values()) { String path = dir + "/" + headNode; try { byte[] data = zookeeper.getData(path, false, null); zookeeper.delete(path, -1); return data; } catch (KeeperException.NoNodeException e) { // Another client deleted the node first. } } } } private static class LatchChildWatcher implements Watcher { CountDownLatch latch; public LatchChildWatcher() { latch = new CountDownLatch(1); } public void process(WatchedEvent event) { LOG.debug("Watcher fired: {}", event); latch.countDown(); } public void await() throws InterruptedException { latch.await(); } } /** * Removes the head of the queue and returns it, blocks until it succeeds. * @return The former head of the queue * @throws NoSuchElementException * @throws KeeperException * @throws InterruptedException */ public byte[] take() throws KeeperException, InterruptedException { Map orderedChildren; // Same as for element. Should refactor this. while (true) { LatchChildWatcher childWatcher = new LatchChildWatcher(); try { orderedChildren = orderedChildren(childWatcher); } catch (KeeperException.NoNodeException e) { zookeeper.create(dir, new byte[0], acl, CreateMode.PERSISTENT); continue; } if (orderedChildren.size() == 0) { childWatcher.await(); continue; } for (String headNode : orderedChildren.values()) { String path = dir + "/" + headNode; try { byte[] data = zookeeper.getData(path, false, null); zookeeper.delete(path, -1); return data; } catch (KeeperException.NoNodeException e) { // Another client deleted the node first. } } } } /** * Inserts data into queue. * @param data * @return true if data was successfully added */ public boolean offer(byte[] data) throws KeeperException, InterruptedException { for (; ; ) { try { zookeeper.create(dir + "/" + prefix, data, acl, CreateMode.PERSISTENT_SEQUENTIAL); return true; } catch (KeeperException.NoNodeException e) { zookeeper.create(dir, new byte[0], acl, CreateMode.PERSISTENT); } } } /** * Returns the data at the first element of the queue, or null if the queue is empty. * @return data at the first element of the queue, or null. * @throws KeeperException * @throws InterruptedException */ public byte[] peek() throws KeeperException, InterruptedException { try { return element(); } catch (NoSuchElementException e) { return null; } } /** * Attempts to remove the head of the queue and return it. Returns null if the queue is empty. * @return Head of the queue or null. * @throws KeeperException * @throws InterruptedException */ public byte[] poll() throws KeeperException, InterruptedException { try { return remove(); } catch (NoSuchElementException e) { return null; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-recipes_zookeeper-recipes-queue_src_test_java_org_a0100644 0000000 0000000 00000000225 15051152474 032553 xustar000000000 0000000 149 path=apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/recipes/queue/DistributedQueueTest.java apache-zookeeper-3.9.4/zookeeper-recipes/zookeeper-recipes-queue/src/test/java/org/apache/zookeeper/0100644 0000000 0000000 00000022317 15051152474 034235 0ustar00rootroot0000000 0000000 /* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.recipes.queue; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import java.util.NoSuchElementException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; /** * Tests for {@link DistributedQueue}. */ public class DistributedQueueTest extends ClientBase { @AfterEach public void tearDown() throws Exception { super.tearDown(); } @Test public void testOffer1() throws Exception { String dir = "/testOffer1"; String testString = "Hello World"; final int numClients = 1; ZooKeeper[] clients = new ZooKeeper[numClients]; DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } queueHandles[0].offer(testString.getBytes(UTF_8)); byte[] dequeuedBytes = queueHandles[0].remove(); assertEquals(new String(dequeuedBytes, UTF_8), testString); } @Test public void testOffer2() throws Exception { String dir = "/testOffer2"; String testString = "Hello World"; final int numClients = 2; ZooKeeper[] clients = new ZooKeeper[numClients]; DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } queueHandles[0].offer(testString.getBytes(UTF_8)); byte[] dequeuedBytes = queueHandles[1].remove(); assertEquals(new String(dequeuedBytes, UTF_8), testString); } @Test public void testTake1() throws Exception { String dir = "/testTake1"; String testString = "Hello World"; final int numClients = 1; ZooKeeper[] clients = new ZooKeeper[numClients]; DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } queueHandles[0].offer(testString.getBytes(UTF_8)); byte[] dequeuedBytes = queueHandles[0].take(); assertEquals(new String(dequeuedBytes, UTF_8), testString); } @Test public void testRemove1() throws Exception { String dir = "/testRemove1"; final int numClients = 1; ZooKeeper[] clients = new ZooKeeper[numClients]; DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } try { queueHandles[0].remove(); } catch (NoSuchElementException e) { return; } fail(); } public void createNremoveMtest(String dir, int n, int m) throws Exception { String testString = "Hello World"; final int numClients = 2; ZooKeeper[] clients = new ZooKeeper[numClients]; DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } for (int i = 0; i < n; i++) { String offerString = testString + i; queueHandles[0].offer(offerString.getBytes(UTF_8)); } byte[] data = null; for (int i = 0; i < m; i++) { data = queueHandles[1].remove(); } assertNotNull(data); assertEquals(new String(data, UTF_8), testString + (m - 1)); } @Test public void testRemove2() throws Exception { createNremoveMtest("/testRemove2", 10, 2); } @Test public void testRemove3() throws Exception { createNremoveMtest("/testRemove3", 1000, 1000); } public void createNremoveMelementTest(String dir, int n, int m) throws Exception { String testString = "Hello World"; final int numClients = 2; ZooKeeper[] clients = new ZooKeeper[numClients]; DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } for (int i = 0; i < n; i++) { String offerString = testString + i; queueHandles[0].offer(offerString.getBytes(UTF_8)); } for (int i = 0; i < m; i++) { queueHandles[1].remove(); } assertEquals(new String(queueHandles[1].element(), UTF_8), testString + m); } @Test public void testElement1() throws Exception { createNremoveMelementTest("/testElement1", 1, 0); } @Test public void testElement2() throws Exception { createNremoveMelementTest("/testElement2", 10, 2); } @Test public void testElement3() throws Exception { createNremoveMelementTest("/testElement3", 1000, 500); } @Test public void testElement4() throws Exception { createNremoveMelementTest("/testElement4", 1000, 1000 - 1); } @Test public void testTakeWait1() throws Exception { String dir = "/testTakeWait1"; final String testString = "Hello World"; final int numClients = 1; final ZooKeeper[] clients = new ZooKeeper[numClients]; final DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } final byte[][] takeResult = new byte[1][]; Thread takeThread = new Thread(() -> { try { takeResult[0] = queueHandles[0].take(); } catch (KeeperException | InterruptedException ignore) { // no op } }); takeThread.start(); Thread.sleep(1000); Thread offerThread = new Thread(() -> { try { queueHandles[0].offer(testString.getBytes(UTF_8)); } catch (KeeperException | InterruptedException ignore) { // no op } }); offerThread.start(); offerThread.join(); takeThread.join(); assertNotNull(takeResult[0]); assertEquals(new String(takeResult[0], UTF_8), testString); } @Test public void testTakeWait2() throws Exception { String dir = "/testTakeWait2"; final String testString = "Hello World"; final int numClients = 1; final ZooKeeper[] clients = new ZooKeeper[numClients]; final DistributedQueue[] queueHandles = new DistributedQueue[numClients]; for (int i = 0; i < clients.length; i++) { clients[i] = createClient(); queueHandles[i] = new DistributedQueue(clients[i], dir, null); } int numAttempts = 2; for (int i = 0; i < numAttempts; i++) { final byte[][] takeResult = new byte[1][]; final String threadTestString = testString + i; Thread takeThread = new Thread(() -> { try { takeResult[0] = queueHandles[0].take(); } catch (KeeperException | InterruptedException ignore) { // no op } }); takeThread.start(); Thread.sleep(1000); Thread offerThread = new Thread(() -> { try { queueHandles[0].offer(threadTestString.getBytes(UTF_8)); } catch (KeeperException | InterruptedException ignore) { // no op } }); offerThread.start(); offerThread.join(); takeThread.join(); assertNotNull(takeResult[0]); assertEquals(new String(takeResult[0], UTF_8), threadTestString); } } } apache-zookeeper-3.9.4/zookeeper-server/pom.xml0100755 0000000 0000000 00000026670 15051152474 022156 0ustar00rootroot0000000 0000000 4.0.0 org.apache.zookeeper parent 3.9.4 zookeeper jar Apache ZooKeeper - Server ZooKeeper server com.github.spotbugs spotbugs-annotations provided true org.hamcrest hamcrest-library test org.apache.commons commons-collections4 test org.apache.zookeeper zookeeper-jute ${project.version} commons-cli commons-cli provided org.apache.yetus audience-annotations io.netty netty-handler io.netty netty-transport-native-epoll linux-x86_64 io.netty netty-tcnative-boringssl-static org.slf4j slf4j-api ch.qos.logback logback-classic runtime true org.eclipse.jetty jetty-server provided org.eclipse.jetty jetty-servlet provided org.eclipse.jetty jetty-client provided com.fasterxml.jackson.core jackson-databind provided org.bouncycastle bcprov-jdk18on test org.bouncycastle bcpkix-jdk18on test jline jline provided io.dropwizard.metrics metrics-core provided org.apache.kerby kerb-core test org.apache.kerby kerb-simplekdc test org.apache.kerby kerby-config test org.mockito mockito-core test org.jmockit jmockit test org.junit.jupiter junit-jupiter-api test org.junit.jupiter junit-jupiter-engine test org.junit.jupiter junit-jupiter-params test org.junit.platform junit-platform-runner test org.burningwave tools test org.xerial.snappy snappy-java provided commons-io commons-io compile org.codehaus.mojo build-helper-maven-plugin build-time timestamp-property build.time yyyy-MM-dd HH:mm zz en_US UTC parse-version parse-version generate-sources add-source ${project.build.directory}/generated-sources/java org.apache.maven.plugins maven-resources-plugin prepare-filtered-java-source copy-resources generate-sources ${project.build.directory}/generated-sources/java src/main/java-filtered true org.apache.maven.plugins maven-dependency-plugin copy-dependencies package copy-dependencies ${project.build.directory}/lib false true false org.apache.maven.plugins maven-surefire-plugin **/*Test.java ${surefire-forkcount} false -Xmx512m -Dtest.junit.threads=${surefire-forkcount} -Dzookeeper.junit.threadid=${surefire.forkNumber} -javaagent:${org.jmockit:jmockit:jar} ${project.basedir} ${project.build.directory}/surefire super:D/InIHSb7yEEbrWz8b9l71RjZJU= org.apache.felix maven-bundle-plugin build bundle package bundle io.netty.buffer;resolution:=optional, io.netty.channel;resolution:=optional, io.netty.channel.group;resolution:=optional, io.netty.channel.socket.nio;resolution:=optional, javax.management;resolution:=optional, javax.security.auth.callback, javax.security.auth.login, javax.security.sasl, org.ietf.jgss, org.osgi.framework;resolution:=optional, org.osgi.util.tracker;resolution:=optional, org.slf4j, *;resolution:=optional !org.apache.zookeeper.data, !org.apache.zookeeper.proto, !org.apache.zookeeper.txn, org.apache.zookeeper* ZooKeeper Bundle https://zookeeper.apache.org/doc/current/ ${mvngit.commit.id} !Implementation-Build,* osgi org.apache.maven.plugins maven-jar-plugin publish-test-jar test-jar apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/AddWatchMode.java0100644 0000000 0000000 00000004737 15051152474 032434 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * Modes available to {@link ZooKeeper#addWatch(String, Watcher, AddWatchMode)} */ public enum AddWatchMode { /** *

* Set a watcher on the given path that does not get removed when triggered (i.e. it stays active * until it is removed). This watcher * is triggered for both data and child events. To remove the watcher, use * removeWatches() with WatcherType.Any. The watcher behaves as if you placed an exists() watch and * a getData() watch on the ZNode at the given path. *

*/ PERSISTENT(ZooDefs.AddWatchModes.persistent), /** *

* Set a watcher on the given path that: a) does not get removed when triggered (i.e. it stays active * until it is removed); b) applies not only to the registered path but all child paths recursively. This watcher * is triggered for both data and child events. To remove the watcher, use * removeWatches() with WatcherType.Any *

* *

* The watcher behaves as if you placed an exists() watch and * a getData() watch on the ZNode at the given path and any ZNodes that are children * of the given path including children added later. *

* *

* NOTE: when there are active recursive watches there is a small performance decrease as all segments * of ZNode paths must be checked for watch triggering. *

*/ PERSISTENT_RECURSIVE(ZooDefs.AddWatchModes.persistentRecursive) ; public int getMode() { return mode; } private final int mode; AddWatchMode(int mode) { this.mode = mode; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/AsyncCallback.java0100644 0000000 0000000 00000035314 15051152474 032635 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.List; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; /** * Interface definitions of asynchronous callbacks. * *

ZooKeeper provides asynchronous version as equivalent to synchronous APIs. * *

An asynchronous callback is deferred to invoke after a function returns. * Asynchronous calls usually improve system efficiency on IO-related APIs. * *

It is highly recommended NOT to perform any blocking operation inside * the callbacks. If you block the thread the ZooKeeper client won't process * other events. */ @InterfaceAudience.Public public interface AsyncCallback { /** * This callback is used to retrieve the stat of the node. */ @InterfaceAudience.Public interface StatCallback extends AsyncCallback { /** * Process the result of the asynchronous call. * *

On success, rc is {@link KeeperException.Code#OK}. * *

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

    *
  • {@link KeeperException.Code#NONODE} * - The node on given path doesn't exist for some API calls.
  • *
  • {@link KeeperException.Code#BADVERSION} * - The given version doesn't match the node's version for some API calls.
  • *
* * @param rc The return code or the result of the call. * @param path The path that we passed to asynchronous calls. * @param ctx Whatever context object that we passed to asynchronous calls. * @param stat {@link Stat} object of the node on given path. * * @see ZooKeeper#exists(String, boolean, AsyncCallback.StatCallback, Object) * @see ZooKeeper#exists(String, Watcher, AsyncCallback.StatCallback, Object) * @see ZooKeeper#setData(String, byte[], int, AsyncCallback.StatCallback, Object) * @see ZooKeeper#setACL(String, List, int, AsyncCallback.StatCallback, Object) */ void processResult(int rc, String path, Object ctx, Stat stat); } /** * This callback is used to get all children node number of the node. * * @since 3.6.0 */ @InterfaceAudience.Public interface AllChildrenNumberCallback extends AsyncCallback { /** * @param rc The return code or the result of the call. * @param ctx Whatever context object that we passed to asynchronous calls. * @param number The number of children nodes under a specific path. * * @see ZooKeeper#getAllChildrenNumber(String, AsyncCallback.AllChildrenNumberCallback, Object) */ void processResult(int rc, String path, Object ctx, int number); } /** * This callback is used to retrieve the data and stat of the node. */ @InterfaceAudience.Public interface DataCallback extends AsyncCallback { /** * Process the result of asynchronous calls. * *

On success, rc is {@link KeeperException.Code#OK}. * *

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

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

On success, rc is {@link KeeperException.Code#OK}. * *

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

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

On success, rc is {@link KeeperException.Code#OK}. * *

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

    *
  • {@link KeeperException.Code#NONODE} * - The node on given path doesn't exist for some API calls.
  • *
* * @param rc The return code or the result of the call. * @param path The path that we passed to asynchronous calls. * @param ctx Whatever context object that we passed to asynchronous calls. * @param children An unordered array of children of the node on given path. * * @see ZooKeeper#getChildren(String, boolean, AsyncCallback.ChildrenCallback, Object) * @see ZooKeeper#getChildren(String, Watcher, AsyncCallback.ChildrenCallback, Object) */ void processResult(int rc, String path, Object ctx, List children); } /** * This callback is used to retrieve the children and stat of the node. */ @InterfaceAudience.Public interface Children2Callback extends AsyncCallback { /** * Process the result of the asynchronous call. * * @param rc The return code or the result of the call. * @param path The path that we passed to asynchronous calls. * @param ctx Whatever context object that we passed to asynchronous calls. * @param children An unordered array of children of the node on given path. * @param stat {@link Stat} object of the node on given path. * * @see ChildrenCallback * @see ZooKeeper#getChildren(String, boolean, AsyncCallback.Children2Callback, Object) * @see ZooKeeper#getChildren(String, Watcher, AsyncCallback.Children2Callback, Object) */ void processResult(int rc, String path, Object ctx, List children, Stat stat); } /** * This callback is used to retrieve the name and stat of the node. */ @InterfaceAudience.Public interface Create2Callback extends AsyncCallback { /** * Process the result of the asynchronous call. * * @param rc The return code or the result of the call. * @param path The path that we passed to asynchronous calls. * @param ctx Whatever context object that we passed to asynchronous calls. * @param name The name of the Znode that was created. On success, name * and path are usually equal, unless a sequential node has * been created. * @param stat {@link Stat} object of the node on given path. * * @see StringCallback * @see ZooKeeper#create(String, byte[], List, CreateMode, AsyncCallback.Create2Callback, Object) * @see ZooKeeper#create(String, byte[], List, CreateMode, AsyncCallback.Create2Callback, Object, long) */ void processResult(int rc, String path, Object ctx, String name, Stat stat); } /** * This callback is used to retrieve the name of the node. */ @InterfaceAudience.Public interface StringCallback extends AsyncCallback { /** * Process the result of the asynchronous call. * *

On success, rc is {@link KeeperException.Code#OK}. * *

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

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

On success, rc is {@link KeeperException.Code#OK}. * *

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

    *
  • {@link KeeperException.Code#NONODE} * - The node on given path doesn't exist for some API calls.
  • *
  • {@link KeeperException.Code#BADVERSION} * - The given version doesn't match the node's version for some API calls.
  • *
  • {@link KeeperException.Code#NOTEMPTY} * - the node has children and some API calls cannot succeed, e.g. * {@link ZooKeeper#delete(String, int, AsyncCallback.VoidCallback, Object)}.
  • *
* * @param rc The return code or the result of the call. * @param path The path that we passed to asynchronous calls. * @param ctx Whatever context object that we passed to asynchronous calls. * * @see ZooKeeper#delete(String, int, AsyncCallback.VoidCallback, Object) * @see ZooKeeper#removeAllWatches(String, Watcher.WatcherType, boolean, AsyncCallback.VoidCallback, Object) * @see ZooKeeper#removeWatches(String, Watcher, Watcher.WatcherType, boolean, AsyncCallback.VoidCallback, Object) * @see ZooKeeper#sync(String, AsyncCallback.VoidCallback, Object) * */ void processResult(int rc, String path, Object ctx); } /** * This callback is used to process the multiple results from a single multi call. */ @InterfaceAudience.Public interface MultiCallback extends AsyncCallback { /** * Process the result of the asynchronous call. * *

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

On failure, rc is a failure code in {@link KeeperException.Code}. Either * {@code opResults} is null, or all {@code opResults} are {@link OpResult.ErrorResult}. * All operations will be rolled back even if operations before the failing one were * successful. * * @param rc The return code or the result of the call. * @param path The path that we passed to asynchronous calls. * @param ctx Whatever context object that we passed to asynchronous calls. * @param opResults The list of results. One result for each operation, and the order * matches that of input. * * @see ZooKeeper#multi(Iterable, AsyncCallback.MultiCallback, Object) */ void processResult(int rc, String path, Object ctx, List opResults); } /** * This callback is used to process the getEphemerals results from a single getEphemerals call. * * @see ZooKeeper#getEphemerals(AsyncCallback.EphemeralsCallback, Object) * @see ZooKeeper#getEphemerals(String, AsyncCallback.EphemeralsCallback, Object) * * @since 3.6.0 */ interface EphemeralsCallback extends AsyncCallback { /** * @param rc The return code or the result of the call. * @param ctx Whatever context object that we passed to asynchronous calls. * @param paths The path that we passed to asynchronous calls. */ void processResult(int rc, Object ctx, List paths); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxn.java0100644 0000000 0000000 00000217366 15051152474 032221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; import javax.security.auth.login.LoginException; import javax.security.sasl.SaslException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.AsyncCallback.ACLCallback; import org.apache.zookeeper.AsyncCallback.AllChildrenNumberCallback; import org.apache.zookeeper.AsyncCallback.Children2Callback; import org.apache.zookeeper.AsyncCallback.ChildrenCallback; import org.apache.zookeeper.AsyncCallback.Create2Callback; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.EphemeralsCallback; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.Watcher.Event; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.ZooKeeper.WatchRegistration; import org.apache.zookeeper.client.FourLetterWordMain; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.client.ZooKeeperSaslClient; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.proto.AuthPacket; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.Create2Response; import org.apache.zookeeper.proto.CreateResponse; import org.apache.zookeeper.proto.ExistsResponse; import org.apache.zookeeper.proto.GetACLResponse; import org.apache.zookeeper.proto.GetAllChildrenNumberResponse; import org.apache.zookeeper.proto.GetChildren2Response; import org.apache.zookeeper.proto.GetChildrenResponse; import org.apache.zookeeper.proto.GetDataResponse; import org.apache.zookeeper.proto.GetEphemeralsResponse; import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetACLResponse; import org.apache.zookeeper.proto.SetDataResponse; import org.apache.zookeeper.proto.SetWatches; import org.apache.zookeeper.proto.SetWatches2; import org.apache.zookeeper.proto.WatcherEvent; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.ZooTrace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; /** * This class manages the socket i/o for the client. ClientCnxn maintains a list * of available servers to connect to and "transparently" switches servers it is * connected to as needed. * */ @SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}) public class ClientCnxn { private static final Logger LOG = LoggerFactory.getLogger(ClientCnxn.class); /* ZOOKEEPER-706: If a session has a large number of watches set then * attempting to re-establish those watches after a connection loss may * fail due to the SetWatches request exceeding the server's configured * jute.maxBuffer value. To avoid this we instead split the watch * re-establishement across multiple SetWatches calls. This constant * controls the size of each call. It is set to 128kB to be conservative * with respect to the server's 1MB default for jute.maxBuffer. */ private static final int SET_WATCHES_MAX_LENGTH = 128 * 1024; /* predefined xid's values recognized as special by the server */ // -1 means notification(WATCHER_EVENT) public static final int NOTIFICATION_XID = -1; // -2 is the xid for pings public static final int PING_XID = -2; // -4 is the xid for AuthPacket public static final int AUTHPACKET_XID = -4; // -8 is the xid for setWatch public static final int SET_WATCHES_XID = -8; static class AuthData { AuthData(String scheme, byte[] data) { this.scheme = scheme; this.data = data; } String scheme; byte[] data; } private final CopyOnWriteArraySet authInfo = new CopyOnWriteArraySet<>(); /** * These are the packets that have been sent and are waiting for a response. */ private final Queue pendingQueue = new ArrayDeque<>(); /** * These are the packets that need to be sent. */ private final LinkedBlockingDeque outgoingQueue = new LinkedBlockingDeque<>(); private int connectTimeout; /** * The timeout in ms the client negotiated with the server. This is the * "real" timeout, not the timeout request by the client (which may have * been increased/decreased by the server which applies bounds to this * value. */ private volatile int negotiatedSessionTimeout; private int readTimeout; private int expirationTimeout; private final int sessionTimeout; private final ZKWatchManager watchManager; private long sessionId; private byte[] sessionPasswd; /** * If true, the connection is allowed to go to r-o mode. This field's value * is sent, besides other data, during session creation handshake. If the * server on the other side of the wire is partitioned it'll accept * read-only clients only. */ private boolean readOnly; final String chrootPath; final SendThread sendThread; final EventThread eventThread; /** * Set to true when close is called. Latches the connection such that we * don't attempt to re-connect to the server if in the middle of closing the * connection (client sends session disconnect to server as part of close * operation) */ private volatile boolean closing = false; /** * A set of ZooKeeper hosts this client could connect to. */ private final HostProvider hostProvider; /** * Is set to true when a connection to a r/w server is established for the * first time; never changed afterwards. *

* Is used to handle situations when client without sessionId connects to a * read-only server. Such client receives "fake" sessionId from read-only * server, but this sessionId is invalid for other servers. So when such * client finds a r/w server, it sends 0 instead of fake sessionId during * connection handshake and establishes new, valid session. *

* If this field is false (which implies we haven't seen r/w server before) * then non-zero sessionId is fake, otherwise it is valid. */ volatile boolean seenRwServerBefore = false; private final ZKClientConfig clientConfig; /** * If any request's response in not received in configured requestTimeout * then it is assumed that the response packet is lost. */ private long requestTimeout; ZKWatchManager getWatcherManager() { return watchManager; } public long getSessionId() { return sessionId; } public byte[] getSessionPasswd() { return sessionPasswd; } public int getSessionTimeout() { return negotiatedSessionTimeout; } @Override public String toString() { StringBuilder sb = new StringBuilder(); SocketAddress local = sendThread.getClientCnxnSocket().getLocalSocketAddress(); SocketAddress remote = sendThread.getClientCnxnSocket().getRemoteSocketAddress(); sb.append("sessionid:0x").append(Long.toHexString(getSessionId())) .append(" local:").append(local) .append(" remoteserver:").append(remote) .append(" lastZxid:").append(lastZxid) .append(" xid:").append(xid) .append(" sent:").append(sendThread.getClientCnxnSocket().getSentCount()) .append(" recv:").append(sendThread.getClientCnxnSocket().getRecvCount()) .append(" queuedpkts:").append(outgoingQueue.size()) .append(" pendingresp:").append(pendingQueue.size()) .append(" queuedevents:").append(eventThread.waitingEvents.size()); return sb.toString(); } /** * This class allows us to pass the headers and the relevant records around. */ static class Packet { RequestHeader requestHeader; ReplyHeader replyHeader; Record request; Record response; ByteBuffer bb; /** Client's view of the path (may differ due to chroot) **/ String clientPath; /** Servers's view of the path (may differ due to chroot) **/ String serverPath; boolean finished; AsyncCallback cb; Object ctx; WatchRegistration watchRegistration; WatchDeregistration watchDeregistration; /** Convenience ctor */ Packet( RequestHeader requestHeader, ReplyHeader replyHeader, Record request, Record response, WatchRegistration watchRegistration ) { this.requestHeader = requestHeader; this.replyHeader = replyHeader; this.request = request; this.response = response; this.watchRegistration = watchRegistration; } public void createBB() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeInt(-1, "len"); // We'll fill this in later if (requestHeader != null) { requestHeader.serialize(boa, "header"); } if (request instanceof ConnectRequest) { request.serialize(boa, "connect"); } else if (request != null) { request.serialize(boa, "request"); } baos.close(); this.bb = ByteBuffer.wrap(baos.toByteArray()); this.bb.putInt(this.bb.capacity() - 4); this.bb.rewind(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("clientPath:" + clientPath); sb.append(" serverPath:" + serverPath); sb.append(" finished:" + finished); sb.append(" header:: " + requestHeader); sb.append(" replyHeader:: " + replyHeader); sb.append(" request:: " + request); sb.append(" response:: " + response); // jute toString is horrible, remove unnecessary newlines return sb.toString().replaceAll("\r*\n+", " "); } } /** * Creates a connection object. The actual network connect doesn't get * established until needed. The start() instance method must be called * subsequent to construction. * * @param chrootPath the chroot of this client. Should be removed from this Class in ZOOKEEPER-838 * @param hostProvider the list of ZooKeeper servers to connect to * @param sessionTimeout the timeout for connections. * @param clientConfig the client configuration. * @param defaultWatcher default watcher for this connection * @param clientCnxnSocket the socket implementation used (e.g. NIO/Netty) * @param canBeReadOnly whether the connection is allowed to go to read-only mode in case of partitioning */ public ClientCnxn( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig clientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly ) throws IOException { this( chrootPath, hostProvider, sessionTimeout, clientConfig, defaultWatcher, clientCnxnSocket, 0, new byte[16], canBeReadOnly); } /** * Creates a connection object. The actual network connect doesn't get * established until needed. The start() instance method must be called * subsequent to construction. * * @param chrootPath the chroot of this client. Should be removed from this Class in ZOOKEEPER-838 * @param hostProvider the list of ZooKeeper servers to connect to * @param sessionTimeout the timeout for connections. * @param clientConfig the client configuration. * @param defaultWatcher default watcher for this connection * @param clientCnxnSocket the socket implementation used (e.g. NIO/Netty) * @param sessionId session id if re-establishing session * @param sessionPasswd session passwd if re-establishing session * @param canBeReadOnly whether the connection is allowed to go to read-only mode in case of partitioning * @throws IOException in cases of broken network */ public ClientCnxn( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig clientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly ) throws IOException { this.chrootPath = chrootPath; this.hostProvider = hostProvider; this.sessionTimeout = sessionTimeout; this.clientConfig = clientConfig; this.sessionId = sessionId; this.sessionPasswd = sessionPasswd; this.readOnly = canBeReadOnly; this.watchManager = new ZKWatchManager( clientConfig.getBoolean(ZKClientConfig.DISABLE_AUTO_WATCH_RESET), defaultWatcher); this.connectTimeout = sessionTimeout / hostProvider.size(); this.readTimeout = sessionTimeout * 2 / 3; this.expirationTimeout = sessionTimeout * 4 / 3; this.sendThread = new SendThread(clientCnxnSocket); this.eventThread = new EventThread(); initRequestTimeout(); } public void start() { sendThread.start(); eventThread.start(); } private Object eventOfDeath = new Object(); private static class WatcherSetEventPair { private final Set watchers; private final WatchedEvent event; public WatcherSetEventPair(Set watchers, WatchedEvent event) { this.watchers = watchers; this.event = event; } } /** * Guard against creating "-EventThread-EventThread-EventThread-..." thread * names when ZooKeeper object is being created from within a watcher. * See ZOOKEEPER-795 for details. */ private static String makeThreadName(String suffix) { String name = Thread.currentThread().getName().replaceAll("-EventThread", ""); return name + suffix; } /** * Tests that current thread is the main event loop. * This method is useful only for tests inside ZooKeeper project * it is not a public API intended for use by external applications. * @return true if Thread.currentThread() is an EventThread. */ public static boolean isInEventThread() { return Thread.currentThread() instanceof EventThread; } class EventThread extends ZooKeeperThread { private final LinkedBlockingQueue waitingEvents = new LinkedBlockingQueue<>(); /** This is really the queued session state until the event * thread actually processes the event and hands it to the watcher. * But for all intents and purposes this is the state. */ private volatile KeeperState sessionState = KeeperState.Disconnected; private volatile boolean wasKilled = false; private volatile boolean isRunning = false; EventThread() { super(makeThreadName("-EventThread")); setDaemon(true); } public void queueEvent(WatchedEvent event) { queueEvent(event, null); } private void queueEvent(WatchedEvent event, Set materializedWatchers) { if (event.getType() == EventType.None && sessionState == event.getState()) { return; } sessionState = event.getState(); final Set watchers; if (materializedWatchers == null) { // materialize the watchers based on the event watchers = watchManager.materialize(event.getState(), event.getType(), event.getPath()); } else { watchers = new HashSet<>(materializedWatchers); } WatcherSetEventPair pair = new WatcherSetEventPair(watchers, event); // queue the pair (watch set & event) for later processing waitingEvents.add(pair); } public void queueCallback(AsyncCallback cb, int rc, String path, Object ctx) { waitingEvents.add(new LocalCallback(cb, rc, path, ctx)); } @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") public void queuePacket(Packet packet) { if (wasKilled) { synchronized (waitingEvents) { if (isRunning) { waitingEvents.add(packet); } else { processEvent(packet); } } } else { waitingEvents.add(packet); } } public void queueEventOfDeath() { waitingEvents.add(eventOfDeath); } @Override @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") public void run() { try { isRunning = true; while (true) { Object event = waitingEvents.take(); if (event == eventOfDeath) { wasKilled = true; } else { processEvent(event); } if (wasKilled) { synchronized (waitingEvents) { if (waitingEvents.isEmpty()) { isRunning = false; break; } } } } } catch (InterruptedException e) { LOG.error("Event thread exiting due to interruption", e); } LOG.info("EventThread shut down for session: 0x{}", Long.toHexString(getSessionId())); } private void processEvent(Object event) { try { if (event instanceof WatcherSetEventPair) { // each watcher will process the event WatcherSetEventPair pair = (WatcherSetEventPair) event; for (Watcher watcher : pair.watchers) { try { watcher.process(pair.event); } catch (Throwable t) { LOG.error("Error while calling watcher.", t); } } } else if (event instanceof LocalCallback) { LocalCallback lcb = (LocalCallback) event; if (lcb.cb instanceof StatCallback) { ((StatCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, null); } else if (lcb.cb instanceof DataCallback) { ((DataCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, null, null); } else if (lcb.cb instanceof ACLCallback) { ((ACLCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, null, null); } else if (lcb.cb instanceof ChildrenCallback) { ((ChildrenCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, null); } else if (lcb.cb instanceof Children2Callback) { ((Children2Callback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, null, null); } else if (lcb.cb instanceof StringCallback) { ((StringCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, null); } else if (lcb.cb instanceof AsyncCallback.EphemeralsCallback) { ((AsyncCallback.EphemeralsCallback) lcb.cb).processResult(lcb.rc, lcb.ctx, null); } else if (lcb.cb instanceof AsyncCallback.AllChildrenNumberCallback) { ((AsyncCallback.AllChildrenNumberCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, -1); } else if (lcb.cb instanceof AsyncCallback.MultiCallback) { ((AsyncCallback.MultiCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx, Collections.emptyList()); } else { ((VoidCallback) lcb.cb).processResult(lcb.rc, lcb.path, lcb.ctx); } } else { Packet p = (Packet) event; int rc = 0; String clientPath = p.clientPath; if (p.replyHeader.getErr() != 0) { rc = p.replyHeader.getErr(); } if (p.cb == null) { LOG.warn("Somehow a null cb got to EventThread!"); } else if (p.response instanceof ExistsResponse || p.response instanceof SetDataResponse || p.response instanceof SetACLResponse) { StatCallback cb = (StatCallback) p.cb; if (rc == Code.OK.intValue()) { if (p.response instanceof ExistsResponse) { cb.processResult(rc, clientPath, p.ctx, ((ExistsResponse) p.response).getStat()); } else if (p.response instanceof SetDataResponse) { cb.processResult(rc, clientPath, p.ctx, ((SetDataResponse) p.response).getStat()); } else if (p.response instanceof SetACLResponse) { cb.processResult(rc, clientPath, p.ctx, ((SetACLResponse) p.response).getStat()); } } else { cb.processResult(rc, clientPath, p.ctx, null); } } else if (p.response instanceof GetDataResponse) { DataCallback cb = (DataCallback) p.cb; GetDataResponse rsp = (GetDataResponse) p.response; if (rc == Code.OK.intValue()) { cb.processResult(rc, clientPath, p.ctx, rsp.getData(), rsp.getStat()); } else { cb.processResult(rc, clientPath, p.ctx, null, null); } } else if (p.response instanceof GetACLResponse) { ACLCallback cb = (ACLCallback) p.cb; GetACLResponse rsp = (GetACLResponse) p.response; if (rc == Code.OK.intValue()) { cb.processResult(rc, clientPath, p.ctx, rsp.getAcl(), rsp.getStat()); } else { cb.processResult(rc, clientPath, p.ctx, null, null); } } else if (p.response instanceof GetChildrenResponse) { ChildrenCallback cb = (ChildrenCallback) p.cb; GetChildrenResponse rsp = (GetChildrenResponse) p.response; if (rc == Code.OK.intValue()) { cb.processResult(rc, clientPath, p.ctx, rsp.getChildren()); } else { cb.processResult(rc, clientPath, p.ctx, null); } } else if (p.response instanceof GetAllChildrenNumberResponse) { AllChildrenNumberCallback cb = (AllChildrenNumberCallback) p.cb; GetAllChildrenNumberResponse rsp = (GetAllChildrenNumberResponse) p.response; if (rc == Code.OK.intValue()) { cb.processResult(rc, clientPath, p.ctx, rsp.getTotalNumber()); } else { cb.processResult(rc, clientPath, p.ctx, -1); } } else if (p.response instanceof GetChildren2Response) { Children2Callback cb = (Children2Callback) p.cb; GetChildren2Response rsp = (GetChildren2Response) p.response; if (rc == Code.OK.intValue()) { cb.processResult(rc, clientPath, p.ctx, rsp.getChildren(), rsp.getStat()); } else { cb.processResult(rc, clientPath, p.ctx, null, null); } } else if (p.response instanceof CreateResponse) { StringCallback cb = (StringCallback) p.cb; CreateResponse rsp = (CreateResponse) p.response; if (rc == Code.OK.intValue()) { cb.processResult( rc, clientPath, p.ctx, (chrootPath == null ? rsp.getPath() : rsp.getPath().substring(chrootPath.length()))); } else { cb.processResult(rc, clientPath, p.ctx, null); } } else if (p.response instanceof Create2Response) { Create2Callback cb = (Create2Callback) p.cb; Create2Response rsp = (Create2Response) p.response; if (rc == Code.OK.intValue()) { cb.processResult( rc, clientPath, p.ctx, (chrootPath == null ? rsp.getPath() : rsp.getPath().substring(chrootPath.length())), rsp.getStat()); } else { cb.processResult(rc, clientPath, p.ctx, null, null); } } else if (p.response instanceof MultiResponse) { MultiCallback cb = (MultiCallback) p.cb; MultiResponse rsp = (MultiResponse) p.response; if (rc == Code.OK.intValue()) { List results = rsp.getResultList(); int newRc = rc; for (OpResult result : results) { if (result instanceof ErrorResult && KeeperException.Code.OK.intValue() != (newRc = ((ErrorResult) result).getErr())) { break; } } cb.processResult(newRc, clientPath, p.ctx, results); } else { cb.processResult(rc, clientPath, p.ctx, null); } } else if (p.response instanceof GetEphemeralsResponse) { EphemeralsCallback cb = (EphemeralsCallback) p.cb; GetEphemeralsResponse rsp = (GetEphemeralsResponse) p.response; if (rc == Code.OK.intValue()) { cb.processResult(rc, p.ctx, rsp.getEphemerals()); } else { cb.processResult(rc, p.ctx, null); } } else if (p.cb instanceof VoidCallback) { VoidCallback cb = (VoidCallback) p.cb; cb.processResult(rc, clientPath, p.ctx); } } } catch (Throwable t) { LOG.error("Unexpected throwable", t); } } } // @VisibleForTesting protected void finishPacket(Packet p) { int err = p.replyHeader.getErr(); if (p.watchRegistration != null) { p.watchRegistration.register(err); } // Add all the removed watch events to the event queue, so that the // clients will be notified with 'Data/Child WatchRemoved' event type. if (p.watchDeregistration != null) { Map> materializedWatchers = null; try { materializedWatchers = p.watchDeregistration.unregister(err); for (Entry> entry : materializedWatchers.entrySet()) { Set watchers = entry.getValue(); if (watchers.size() > 0) { queueEvent(p.watchDeregistration.getClientPath(), err, watchers, entry.getKey()); // ignore connectionloss when removing from local // session p.replyHeader.setErr(Code.OK.intValue()); } } } catch (KeeperException.NoWatcherException nwe) { p.replyHeader.setErr(nwe.code().intValue()); } catch (KeeperException ke) { p.replyHeader.setErr(ke.code().intValue()); } } if (p.cb == null) { synchronized (p) { p.finished = true; p.notifyAll(); } } else { p.finished = true; eventThread.queuePacket(p); } } void queueEvent(String clientPath, int err, Set materializedWatchers, EventType eventType) { KeeperState sessionState = KeeperState.SyncConnected; if (KeeperException.Code.SESSIONEXPIRED.intValue() == err || KeeperException.Code.CONNECTIONLOSS.intValue() == err) { sessionState = Event.KeeperState.Disconnected; } WatchedEvent event = new WatchedEvent(eventType, sessionState, clientPath); eventThread.queueEvent(event, materializedWatchers); } void queueCallback(AsyncCallback cb, int rc, String path, Object ctx) { eventThread.queueCallback(cb, rc, path, ctx); } // for test only protected void onConnecting(InetSocketAddress addr) { } private void conLossPacket(Packet p) { if (p.replyHeader == null) { return; } switch (state) { case AUTH_FAILED: p.replyHeader.setErr(KeeperException.Code.AUTHFAILED.intValue()); break; case CLOSED: p.replyHeader.setErr(KeeperException.Code.SESSIONEXPIRED.intValue()); break; default: p.replyHeader.setErr(KeeperException.Code.CONNECTIONLOSS.intValue()); } finishPacket(p); } private volatile long lastZxid; public long getLastZxid() { return lastZxid; } static class EndOfStreamException extends IOException { private static final long serialVersionUID = -5438877188796231422L; public EndOfStreamException(String msg) { super(msg); } @Override public String toString() { return "EndOfStreamException: " + getMessage(); } } private static class ConnectionTimeoutException extends IOException { public ConnectionTimeoutException(String message) { super(message); } } private static class SessionTimeoutException extends IOException { private static final long serialVersionUID = 824482094072071178L; public SessionTimeoutException(String msg) { super(msg); } } private static class SessionExpiredException extends IOException { private static final long serialVersionUID = -1388816932076193249L; public SessionExpiredException(String msg) { super(msg); } } private static class RWServerFoundException extends IOException { private static final long serialVersionUID = 90431199887158758L; public RWServerFoundException(String msg) { super(msg); } } /** * This class services the outgoing request queue and generates the heart * beats. It also spawns the ReadThread. */ class SendThread extends ZooKeeperThread { private long lastPingSentNs; private final ClientCnxnSocket clientCnxnSocket; private boolean isFirstConnect = true; private volatile ZooKeeperSaslClient zooKeeperSaslClient; private final AtomicReference loginRef = new AtomicReference<>(); private String stripChroot(String serverPath) { if (serverPath.startsWith(chrootPath)) { if (serverPath.length() == chrootPath.length()) { return "/"; } return serverPath.substring(chrootPath.length()); } else if (serverPath.startsWith(ZooDefs.ZOOKEEPER_NODE_SUBTREE)) { return serverPath; } LOG.warn("Got server path {} which is not descendant of chroot path {}.", serverPath, chrootPath); return serverPath; } void readResponse(ByteBuffer incomingBuffer) throws IOException { ByteBufferInputStream bbis = new ByteBufferInputStream(incomingBuffer); BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis); ReplyHeader replyHdr = new ReplyHeader(); replyHdr.deserialize(bbia, "header"); switch (replyHdr.getXid()) { case PING_XID: LOG.debug("Got ping response for session id: 0x{} after {}ms.", Long.toHexString(sessionId), ((System.nanoTime() - lastPingSentNs) / 1000000)); return; case AUTHPACKET_XID: LOG.debug("Got auth session id: 0x{}", Long.toHexString(sessionId)); if (replyHdr.getErr() == KeeperException.Code.AUTHFAILED.intValue()) { changeZkState(States.AUTH_FAILED); eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.AuthFailed, null)); eventThread.queueEventOfDeath(); } return; case NOTIFICATION_XID: LOG.debug("Got notification session id: 0x{}", Long.toHexString(sessionId)); WatcherEvent event = new WatcherEvent(); event.deserialize(bbia, "response"); // convert from a server path to a client path if (chrootPath != null) { String serverPath = event.getPath(); String clientPath = stripChroot(serverPath); event.setPath(clientPath); } WatchedEvent we = new WatchedEvent(event, replyHdr.getZxid()); LOG.debug("Got {} for session id 0x{}", we, Long.toHexString(sessionId)); eventThread.queueEvent(we); return; default: break; } // If SASL authentication is currently in progress, construct and // send a response packet immediately, rather than queuing a // response as with other packets. if (tunnelAuthInProgress()) { GetSASLRequest request = new GetSASLRequest(); request.deserialize(bbia, "token"); zooKeeperSaslClient.respondToServer(request.getToken(), ClientCnxn.this); return; } Packet packet; synchronized (pendingQueue) { if (pendingQueue.size() == 0) { throw new IOException("Nothing in the queue, but got " + replyHdr.getXid()); } packet = pendingQueue.remove(); } /* * Since requests are processed in order, we better get a response * to the first request! */ try { if (packet.requestHeader.getXid() != replyHdr.getXid()) { packet.replyHeader.setErr(KeeperException.Code.CONNECTIONLOSS.intValue()); throw new IOException("Xid out of order. Got Xid " + replyHdr.getXid() + " with err " + replyHdr.getErr() + " expected Xid " + packet.requestHeader.getXid() + " for a packet with details: " + packet); } packet.replyHeader.setXid(replyHdr.getXid()); packet.replyHeader.setErr(replyHdr.getErr()); packet.replyHeader.setZxid(replyHdr.getZxid()); if (replyHdr.getZxid() > 0) { lastZxid = replyHdr.getZxid(); } if (packet.response != null && replyHdr.getErr() == 0) { packet.response.deserialize(bbia, "response"); } LOG.debug("Reading reply session id: 0x{}, packet:: {}", Long.toHexString(sessionId), packet); } finally { finishPacket(packet); } } SendThread(ClientCnxnSocket clientCnxnSocket) throws IOException { super(makeThreadName("-SendThread()")); changeZkState(States.CONNECTING); this.clientCnxnSocket = clientCnxnSocket; setDaemon(true); } // TODO: can not name this method getState since Thread.getState() // already exists // It would be cleaner to make class SendThread an implementation of // Runnable /** * Used by ClientCnxnSocket * * @return */ synchronized ZooKeeper.States getZkState() { return state; } synchronized void changeZkState(ZooKeeper.States newState) throws IOException { if (!state.isAlive() && newState == States.CONNECTING) { throw new IOException( "Connection has already been closed and reconnection is not allowed"); } // It's safer to place state modification at the end. state = newState; } ClientCnxnSocket getClientCnxnSocket() { return clientCnxnSocket; } /** * Setup session, previous watches, authentication. */ void primeConnection() throws IOException { LOG.info( "Socket connection established, initiating session, client: {}, server: {}", clientCnxnSocket.getLocalSocketAddress(), clientCnxnSocket.getRemoteSocketAddress()); isFirstConnect = false; long sessId = (seenRwServerBefore) ? sessionId : 0; ConnectRequest conReq = new ConnectRequest(0, lastZxid, sessionTimeout, sessId, sessionPasswd, readOnly); // We add backwards since we are pushing into the front // Only send if there's a pending watch if (!clientConfig.getBoolean(ZKClientConfig.DISABLE_AUTO_WATCH_RESET)) { List dataWatches = watchManager.getDataWatchList(); List existWatches = watchManager.getExistWatchList(); List childWatches = watchManager.getChildWatchList(); List persistentWatches = watchManager.getPersistentWatchList(); List persistentRecursiveWatches = watchManager.getPersistentRecursiveWatchList(); if (!dataWatches.isEmpty() || !existWatches.isEmpty() || !childWatches.isEmpty() || !persistentWatches.isEmpty() || !persistentRecursiveWatches.isEmpty()) { Iterator dataWatchesIter = prependChroot(dataWatches).iterator(); Iterator existWatchesIter = prependChroot(existWatches).iterator(); Iterator childWatchesIter = prependChroot(childWatches).iterator(); Iterator persistentWatchesIter = prependChroot(persistentWatches).iterator(); Iterator persistentRecursiveWatchesIter = prependChroot(persistentRecursiveWatches).iterator(); long setWatchesLastZxid = lastZxid; while (dataWatchesIter.hasNext() || existWatchesIter.hasNext() || childWatchesIter.hasNext() || persistentWatchesIter.hasNext() || persistentRecursiveWatchesIter.hasNext()) { List dataWatchesBatch = new ArrayList<>(); List existWatchesBatch = new ArrayList<>(); List childWatchesBatch = new ArrayList<>(); List persistentWatchesBatch = new ArrayList<>(); List persistentRecursiveWatchesBatch = new ArrayList<>(); int batchLength = 0; // Note, we may exceed our max length by a bit when we add the last // watch in the batch. This isn't ideal, but it makes the code simpler. while (batchLength < SET_WATCHES_MAX_LENGTH) { final String watch; if (dataWatchesIter.hasNext()) { watch = dataWatchesIter.next(); dataWatchesBatch.add(watch); } else if (existWatchesIter.hasNext()) { watch = existWatchesIter.next(); existWatchesBatch.add(watch); } else if (childWatchesIter.hasNext()) { watch = childWatchesIter.next(); childWatchesBatch.add(watch); } else if (persistentWatchesIter.hasNext()) { watch = persistentWatchesIter.next(); persistentWatchesBatch.add(watch); } else if (persistentRecursiveWatchesIter.hasNext()) { watch = persistentRecursiveWatchesIter.next(); persistentRecursiveWatchesBatch.add(watch); } else { break; } batchLength += watch.length(); } Record record; int opcode; if (persistentWatchesBatch.isEmpty() && persistentRecursiveWatchesBatch.isEmpty()) { // maintain compatibility with older servers - if no persistent/recursive watchers // are used, use the old version of SetWatches record = new SetWatches(setWatchesLastZxid, dataWatchesBatch, existWatchesBatch, childWatchesBatch); opcode = OpCode.setWatches; } else { record = new SetWatches2(setWatchesLastZxid, dataWatchesBatch, existWatchesBatch, childWatchesBatch, persistentWatchesBatch, persistentRecursiveWatchesBatch); opcode = OpCode.setWatches2; } RequestHeader header = new RequestHeader(ClientCnxn.SET_WATCHES_XID, opcode); Packet packet = new Packet(header, new ReplyHeader(), record, null, null); outgoingQueue.addFirst(packet); } } } for (AuthData id : authInfo) { outgoingQueue.addFirst( new Packet( new RequestHeader(ClientCnxn.AUTHPACKET_XID, OpCode.auth), null, new AuthPacket(0, id.scheme, id.data), null, null)); } outgoingQueue.addFirst(new Packet(null, null, conReq, null, null)); clientCnxnSocket.connectionPrimed(); LOG.debug("Session establishment request sent on {}", clientCnxnSocket.getRemoteSocketAddress()); } private List prependChroot(List paths) { if (chrootPath != null && !paths.isEmpty()) { for (int i = 0; i < paths.size(); ++i) { String clientPath = paths.get(i); String serverPath; // handle clientPath = "/" if (clientPath.length() == 1) { serverPath = chrootPath; } else { serverPath = chrootPath + clientPath; } paths.set(i, serverPath); } } return paths; } private void sendPing() { lastPingSentNs = System.nanoTime(); RequestHeader h = new RequestHeader(ClientCnxn.PING_XID, OpCode.ping); queuePacket(h, null, null, null, null, null, null, null, null); } private InetSocketAddress rwServerAddress = null; private static final int minPingRwTimeout = 100; private static final int maxPingRwTimeout = 60000; private int pingRwTimeout = minPingRwTimeout; // Set to true if and only if constructor of ZooKeeperSaslClient // throws a LoginException: see startConnect() below. private boolean saslLoginFailed = false; private void startConnect(InetSocketAddress addr) throws IOException { // initializing it for new connection saslLoginFailed = false; if (!isFirstConnect) { try { Thread.sleep(ThreadLocalRandom.current().nextLong(1000)); } catch (InterruptedException e) { LOG.warn("Unexpected exception", e); } } changeZkState(States.CONNECTING); String hostPort = addr.getHostString() + ":" + addr.getPort(); MDC.put("myid", hostPort); setName(getName().replaceAll("\\(.*\\)", "(" + hostPort + ")")); if (clientConfig.isSaslClientEnabled()) { try { zooKeeperSaslClient = new ZooKeeperSaslClient( SaslServerPrincipal.getServerPrincipal(addr, clientConfig), clientConfig, loginRef); } catch (LoginException e) { // An authentication error occurred when the SASL client tried to initialize: // for Kerberos this means that the client failed to authenticate with the KDC. // This is different from an authentication error that occurs during communication // with the Zookeeper server, which is handled below. LOG.warn( "SASL configuration failed. " + "Will continue connection to Zookeeper server without " + "SASL authentication, if Zookeeper server allows it.", e); eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.AuthFailed, null)); saslLoginFailed = true; } } logStartConnect(addr); clientCnxnSocket.connect(addr); } private void logStartConnect(InetSocketAddress addr) { LOG.info("Opening socket connection to server {}.", addr); if (zooKeeperSaslClient != null) { LOG.info("SASL config status: {}", zooKeeperSaslClient.getConfigStatus()); } } @Override @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") public void run() { clientCnxnSocket.introduce(this, sessionId, outgoingQueue); clientCnxnSocket.updateNow(); clientCnxnSocket.updateLastSendAndHeard(); int to; long lastPingRwServer = Time.currentElapsedTime(); final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds InetSocketAddress serverAddress = null; while (state.isAlive()) { try { if (!clientCnxnSocket.isConnected()) { // don't re-establish connection if we are closing if (closing) { break; } if (rwServerAddress != null) { serverAddress = rwServerAddress; rwServerAddress = null; } else { serverAddress = hostProvider.next(1000); } onConnecting(serverAddress); startConnect(serverAddress); // Update now to start the connection timer right after we make a connection attempt clientCnxnSocket.updateNow(); clientCnxnSocket.updateLastSend(); } if (state.isConnected()) { // determine whether we need to send an AuthFailed event. if (zooKeeperSaslClient != null) { boolean sendAuthEvent = false; if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) { try { zooKeeperSaslClient.initialize(ClientCnxn.this); } catch (SaslException e) { LOG.error("SASL authentication with Zookeeper Quorum member failed.", e); changeZkState(States.AUTH_FAILED); sendAuthEvent = true; } } KeeperState authState = zooKeeperSaslClient.getKeeperState(); if (authState != null) { if (authState == KeeperState.AuthFailed) { // An authentication error occurred during authentication with the Zookeeper Server. changeZkState(States.AUTH_FAILED); sendAuthEvent = true; } else { if (authState == KeeperState.SaslAuthenticated) { sendAuthEvent = true; } } } if (sendAuthEvent) { eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, authState, null)); if (state == States.AUTH_FAILED) { eventThread.queueEventOfDeath(); } } } to = readTimeout - clientCnxnSocket.getIdleRecv(); } else { to = connectTimeout - clientCnxnSocket.getIdleSend(); } int expiration = sessionId == 0 ? Integer.MAX_VALUE : expirationTimeout - clientCnxnSocket.getIdleRecv(); if (expiration <= 0) { String warnInfo = String.format( "Client session timed out, have not heard from server in %dms for session id 0x%s", clientCnxnSocket.getIdleRecv(), Long.toHexString(sessionId)); LOG.warn(warnInfo); changeZkState(States.CLOSED); throw new SessionTimeoutException(warnInfo); } else if (to <= 0) { String warnInfo = String.format( "Client connection timed out, have not heard from server in %dms for session id 0x%s", clientCnxnSocket.getIdleRecv(), Long.toHexString(sessionId)); throw new ConnectionTimeoutException(warnInfo); } if (state.isConnected()) { //1000(1 second) is to prevent race condition missing to send the second ping //also make sure not to send too many pings when readTimeout is small int timeToNextPing = readTimeout / 2 - clientCnxnSocket.getIdleSend() - ((clientCnxnSocket.getIdleSend() > 1000) ? 1000 : 0); //send a ping request either time is due or no packet sent out within MAX_SEND_PING_INTERVAL if (timeToNextPing <= 0 || clientCnxnSocket.getIdleSend() > MAX_SEND_PING_INTERVAL) { sendPing(); clientCnxnSocket.updateLastSend(); } else { if (timeToNextPing < to) { to = timeToNextPing; } } } // If we are in read-only mode, seek for read/write server if (state == States.CONNECTEDREADONLY) { long now = Time.currentElapsedTime(); int idlePingRwServer = (int) (now - lastPingRwServer); if (idlePingRwServer >= pingRwTimeout) { lastPingRwServer = now; idlePingRwServer = 0; pingRwTimeout = Math.min(2 * pingRwTimeout, maxPingRwTimeout); pingRwServer(); } to = Math.min(to, pingRwTimeout - idlePingRwServer); } clientCnxnSocket.doTransport(to, pendingQueue, ClientCnxn.this); } catch (Throwable e) { if (closing) { // closing so this is expected if (LOG.isDebugEnabled()) { LOG.debug( "An exception was thrown while closing send thread for session 0x{}.", Long.toHexString(getSessionId()), e); } break; } else { LOG.warn( "Session 0x{} for server {}, Closing socket connection. " + "Attempting reconnect except it is a SessionExpiredException or SessionTimeoutException.", Long.toHexString(getSessionId()), serverAddress, e); // At this point, there might still be new packets appended to outgoingQueue. // they will be handled in next connection or cleared up if closed. cleanAndNotifyState(); } } } synchronized (outgoingQueue) { // When it comes to this point, it guarantees that later queued // packet to outgoingQueue will be notified of death. cleanup(); } clientCnxnSocket.close(); if (state.isAlive()) { eventThread.queueEvent(new WatchedEvent(Event.EventType.None, Event.KeeperState.Disconnected, null)); } if (closing) { eventThread.queueEvent(new WatchedEvent(Event.EventType.None, KeeperState.Closed, null)); } else if (state == States.CLOSED) { eventThread.queueEvent(new WatchedEvent(Event.EventType.None, KeeperState.Expired, null)); } eventThread.queueEventOfDeath(); Login l = loginRef.getAndSet(null); if (l != null) { l.shutdown(); } ZooTrace.logTraceMessage( LOG, ZooTrace.getTextTraceLevel(), "SendThread exited loop for session: 0x" + Long.toHexString(getSessionId())); } private void cleanAndNotifyState() { cleanup(); if (state.isAlive()) { eventThread.queueEvent(new WatchedEvent(Event.EventType.None, Event.KeeperState.Disconnected, null)); } clientCnxnSocket.updateNow(); } private void pingRwServer() throws RWServerFoundException { String result = null; InetSocketAddress addr = hostProvider.next(0); LOG.info("Checking server {} for being r/w. Timeout {}", addr, pingRwTimeout); try { result = FourLetterWordMain.send4LetterWord(addr.getHostString(), addr.getPort(), "isro", clientConfig, 1000); } catch (ConnectException e) { // ignore, this just means server is not up } catch (IOException | X509Exception.SSLContextException e) { // some unexpected error, warn about it LOG.warn("Exception while seeking for r/w server.", e); } if ("rw\n".equals(result)) { pingRwTimeout = minPingRwTimeout; // save the found address so that it's used during the next // connection attempt rwServerAddress = addr; throw new RWServerFoundException("Majority server found at " + addr.getHostString() + ":" + addr.getPort()); } } private void cleanup() { clientCnxnSocket.cleanup(); synchronized (pendingQueue) { for (Packet p : pendingQueue) { conLossPacket(p); } pendingQueue.clear(); } // We can't call outgoingQueue.clear() here because // between iterating and clear up there might be new // packets added in queuePacket(). Iterator iter = outgoingQueue.iterator(); while (iter.hasNext()) { Packet p = iter.next(); conLossPacket(p); iter.remove(); } } /** * Callback invoked by the ClientCnxnSocket once a connection has been * established. */ void onConnected( int _negotiatedSessionTimeout, long _sessionId, byte[] _sessionPasswd, boolean isRO) throws IOException { negotiatedSessionTimeout = _negotiatedSessionTimeout; if (negotiatedSessionTimeout <= 0) { changeZkState(States.CLOSED); eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.Expired, null)); eventThread.queueEventOfDeath(); String warnInfo = String.format( "Unable to reconnect to ZooKeeper service, session 0x%s has expired", Long.toHexString(sessionId)); LOG.warn(warnInfo); throw new SessionExpiredException(warnInfo); } if (!readOnly && isRO) { LOG.error("Read/write client got connected to read-only server"); } readTimeout = negotiatedSessionTimeout * 2 / 3; expirationTimeout = negotiatedSessionTimeout * 4 / 3; connectTimeout = negotiatedSessionTimeout / hostProvider.size(); hostProvider.onConnected(); sessionId = _sessionId; sessionPasswd = _sessionPasswd; changeZkState((isRO) ? States.CONNECTEDREADONLY : States.CONNECTED); seenRwServerBefore |= !isRO; LOG.info( "Session establishment complete on server {}, session id = 0x{}, negotiated timeout = {}{}", clientCnxnSocket.getRemoteSocketAddress(), Long.toHexString(sessionId), negotiatedSessionTimeout, (isRO ? " (READ-ONLY mode)" : "")); KeeperState eventState = (isRO) ? KeeperState.ConnectedReadOnly : KeeperState.SyncConnected; eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, eventState, null)); } void close() { try { changeZkState(States.CLOSED); } catch (IOException e) { LOG.warn("Connection close fails when migrates state from {} to CLOSED", getZkState()); } clientCnxnSocket.onClosing(); } void testableCloseSocket() throws IOException { clientCnxnSocket.testableCloseSocket(); } public boolean tunnelAuthInProgress() { // 1. SASL client is disabled. if (!clientConfig.isSaslClientEnabled()) { return false; } // 2. SASL login failed. if (saslLoginFailed) { return false; } // 3. SendThread has not created the authenticating object yet, // therefore authentication is (at the earliest stage of being) in progress. if (zooKeeperSaslClient == null) { return true; } // 4. authenticating object exists, so ask it for its progress. return zooKeeperSaslClient.clientTunneledAuthenticationInProgress(); } public void sendPacket(Packet p) throws IOException { clientCnxnSocket.sendPacket(p); } public ZooKeeperSaslClient getZooKeeperSaslClient() { return zooKeeperSaslClient; } // VisibleForTesting Login getLogin() { return loginRef.get(); } } /** * Shutdown the send/event threads. This method should not be called * directly - rather it should be called as part of close operation. This * method is primarily here to allow the tests to verify disconnection * behavior. */ public void disconnect() { LOG.debug("Disconnecting client for session: 0x{}", Long.toHexString(getSessionId())); sendThread.close(); try { sendThread.join(); } catch (InterruptedException ex) { LOG.warn("Got interrupted while waiting for the sender thread to close", ex); } eventThread.queueEventOfDeath(); } /** * Close the connection, which includes; send session disconnect to the * server, shutdown the send/event threads. * * @throws IOException */ public void close() throws IOException { LOG.debug("Closing client for session: 0x{}", Long.toHexString(getSessionId())); try { RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.closeSession); submitRequest(h, null, null, null); } catch (InterruptedException e) { // ignore, close the send/event threads } finally { disconnect(); } } // @VisibleForTesting protected int xid = 1; // @VisibleForTesting volatile States state = States.NOT_CONNECTED; /* * getXid() is called externally by ClientCnxnNIO::doIO() when packets are sent from the outgoingQueue to * the server. Thus, getXid() must be public. */ public synchronized int getXid() { // Avoid negative cxid values. In particular, cxid values of -4, -2, and -1 are special and // must not be used for requests -- see SendThread.readResponse. // Skip from MAX to 1. if (xid == Integer.MAX_VALUE) { xid = 1; } return xid++; } public ReplyHeader submitRequest( RequestHeader h, Record request, Record response, WatchRegistration watchRegistration) throws InterruptedException { return submitRequest(h, request, response, watchRegistration, null); } public ReplyHeader submitRequest( RequestHeader h, Record request, Record response, WatchRegistration watchRegistration, WatchDeregistration watchDeregistration) throws InterruptedException { ReplyHeader r = new ReplyHeader(); Packet packet = queuePacket( h, r, request, response, null, null, null, null, watchRegistration, watchDeregistration); synchronized (packet) { if (requestTimeout > 0) { // Wait for request completion with timeout waitForPacketFinish(r, packet); } else { // Wait for request completion infinitely while (!packet.finished) { packet.wait(); } } } if (r.getErr() == Code.REQUESTTIMEOUT.intValue()) { sendThread.cleanAndNotifyState(); } return r; } /** * Wait for request completion with timeout. */ private void waitForPacketFinish(ReplyHeader r, Packet packet) throws InterruptedException { long remainingTime = requestTimeout; while (!packet.finished && remainingTime > 0) { long waitStartTime = Time.currentElapsedTime(); packet.wait(remainingTime); remainingTime -= (Time.currentElapsedTime() - waitStartTime); } if (!packet.finished) { LOG.error("Timeout error occurred for the packet '{}'.", packet); r.setErr(Code.REQUESTTIMEOUT.intValue()); } } public void saslCompleted() { sendThread.getClientCnxnSocket().saslCompleted(); } public void sendPacket(Record request, Record response, AsyncCallback cb, int opCode) throws IOException { // Generate Xid now because it will be sent immediately, // by call to sendThread.sendPacket() below. int xid = getXid(); RequestHeader h = new RequestHeader(); h.setXid(xid); h.setType(opCode); ReplyHeader r = new ReplyHeader(); r.setXid(xid); Packet p = new Packet(h, r, request, response, null); p.cb = cb; sendThread.sendPacket(p); } public Packet queuePacket( RequestHeader h, ReplyHeader r, Record request, Record response, AsyncCallback cb, String clientPath, String serverPath, Object ctx, WatchRegistration watchRegistration) { return queuePacket(h, r, request, response, cb, clientPath, serverPath, ctx, watchRegistration, null); } @SuppressFBWarnings("JLM_JSR166_UTILCONCURRENT_MONITORENTER") public Packet queuePacket( RequestHeader h, ReplyHeader r, Record request, Record response, AsyncCallback cb, String clientPath, String serverPath, Object ctx, WatchRegistration watchRegistration, WatchDeregistration watchDeregistration) { Packet packet = null; // Note that we do not generate the Xid for the packet yet. It is // generated later at send-time, by an implementation of ClientCnxnSocket::doIO(), // where the packet is actually sent. packet = new Packet(h, r, request, response, watchRegistration); packet.cb = cb; packet.ctx = ctx; packet.clientPath = clientPath; packet.serverPath = serverPath; packet.watchDeregistration = watchDeregistration; // The synchronized block here is for two purpose: // 1. synchronize with the final cleanup() in SendThread.run() to avoid race // 2. synchronized against each packet. So if a closeSession packet is added, // later packet will be notified. synchronized (outgoingQueue) { if (!state.isAlive() || closing) { conLossPacket(packet); } else { // If the client is asking to close the session then // mark as closing if (h.getType() == OpCode.closeSession) { closing = true; } outgoingQueue.add(packet); } } sendThread.getClientCnxnSocket().packetAdded(); return packet; } public void addAuthInfo(String scheme, byte[] auth) { if (!state.isAlive()) { return; } authInfo.add(new AuthData(scheme, auth)); queuePacket( new RequestHeader(ClientCnxn.AUTHPACKET_XID, OpCode.auth), null, new AuthPacket(0, scheme, auth), null, null, null, null, null, null); } States getState() { return state; } private static class LocalCallback { private final AsyncCallback cb; private final int rc; private final String path; private final Object ctx; public LocalCallback(AsyncCallback cb, int rc, String path, Object ctx) { this.cb = cb; this.rc = rc; this.path = path; this.ctx = ctx; } } private void initRequestTimeout() { try { requestTimeout = clientConfig.getLong( ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT, ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT_DEFAULT); LOG.info( "{} value is {}. feature enabled={}", ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT, requestTimeout, requestTimeout > 0); } catch (NumberFormatException e) { LOG.error( "Configured value {} for property {} can not be parsed to long.", clientConfig.getProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT), ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT); throw e; } } public ZooKeeperSaslClient getZooKeeperSaslClient() { return sendThread.getZooKeeperSaslClient(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocket.java0100644 0000000 0000000 00000017573 15051152474 033370 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.text.MessageFormat; import java.util.Queue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.BinaryInputArchive; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.compat.ProtocolManager; import org.apache.zookeeper.proto.ConnectResponse; import org.apache.zookeeper.server.ByteBufferInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A ClientCnxnSocket does the lower level communication with a socket * implementation. * * This code has been moved out of ClientCnxn so that a Netty implementation can * be provided as an alternative to the NIO socket code. * */ abstract class ClientCnxnSocket { private static final Logger LOG = LoggerFactory.getLogger(ClientCnxnSocket.class); private final ProtocolManager protocolManager = new ProtocolManager(); protected boolean initialized; /** * This buffer is only used to read the length of the incoming message. */ protected final ByteBuffer lenBuffer = ByteBuffer.allocateDirect(4); /** * After the length is read, a new incomingBuffer is allocated in * readLength() to receive the full message. */ protected ByteBuffer incomingBuffer = lenBuffer; protected final AtomicLong sentCount = new AtomicLong(0L); protected final AtomicLong recvCount = new AtomicLong(0L); // Used for reactive timeout detection, say connection read timeout and session expiration timeout. protected long lastHeard; // Used for proactive timeout detection, say ping timeout and connection establishment timeout. protected long lastSend; protected long now; protected ClientCnxn.SendThread sendThread; protected LinkedBlockingDeque outgoingQueue; protected ZKClientConfig clientConfig; private int packetLen = ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT; /** * The sessionId is only available here for Log and Exception messages. * Otherwise the socket doesn't need to know it. */ protected long sessionId; void introduce(ClientCnxn.SendThread sendThread, long sessionId, LinkedBlockingDeque outgoingQueue) { this.sendThread = sendThread; this.sessionId = sessionId; this.outgoingQueue = outgoingQueue; } void updateNow() { now = Time.currentElapsedTime(); } int getIdleRecv() { return (int) (now - lastHeard); } int getIdleSend() { return (int) (now - lastSend); } long getSentCount() { return sentCount.get(); } long getRecvCount() { return recvCount.get(); } void updateLastHeard() { this.lastHeard = now; } void updateLastSend() { this.lastSend = now; } void updateLastSendAndHeard() { this.lastSend = now; this.lastHeard = now; } void readLength() throws IOException { int len = incomingBuffer.getInt(); if (len < 0 || len > packetLen) { throw new IOException("Packet len " + len + " is out of range!"); } incomingBuffer = ByteBuffer.allocate(len); } void readConnectResult() throws IOException { if (LOG.isTraceEnabled()) { StringBuilder buf = new StringBuilder("0x["); for (byte b : incomingBuffer.array()) { buf.append(Integer.toHexString(b)).append(","); } buf.append("]"); if (LOG.isTraceEnabled()) { LOG.trace("readConnectResult {} {}", incomingBuffer.remaining(), buf); } } ByteBufferInputStream bbis = new ByteBufferInputStream(incomingBuffer); BinaryInputArchive bbia = BinaryInputArchive.getArchive(bbis); ConnectResponse conRsp = protocolManager.deserializeConnectResponse(bbia); if (!protocolManager.isReadonlyAvailable()) { LOG.warn("Connected to an old server; r-o mode will be unavailable"); } this.sessionId = conRsp.getSessionId(); sendThread.onConnected(conRsp.getTimeOut(), this.sessionId, conRsp.getPasswd(), conRsp.getReadOnly()); } abstract boolean isConnected(); abstract void connect(InetSocketAddress addr) throws IOException; /** * Returns the address to which the socket is connected. */ abstract SocketAddress getRemoteSocketAddress(); /** * Returns the address to which the socket is bound. */ abstract SocketAddress getLocalSocketAddress(); /** * Clean up resources for a fresh new socket. * It's called before reconnect or close. */ abstract void cleanup(); /** * new packets are added to outgoingQueue. */ abstract void packetAdded(); /** * connState is marked CLOSED and notify ClientCnxnSocket to react. */ abstract void onClosing(); /** * Sasl completes. Allows non-priming packgets to be sent. * Note that this method will only be called if Sasl starts and completes. */ abstract void saslCompleted(); /** * being called after ClientCnxn finish PrimeConnection */ abstract void connectionPrimed(); /** * Do transportation work: * - read packets into incomingBuffer. * - write outgoing queue packets. * - update relevant timestamp. * * @param waitTimeOut timeout in blocking wait. Unit in MilliSecond. * @param pendingQueue These are the packets that have been sent and * are waiting for a response. * @param cnxn * @throws IOException * @throws InterruptedException */ abstract void doTransport( int waitTimeOut, Queue pendingQueue, ClientCnxn cnxn) throws IOException, InterruptedException; /** * Close the socket. */ abstract void testableCloseSocket() throws IOException; /** * Close this client. */ abstract void close(); /** * Send Sasl packets directly. * The Sasl process will send the first (requestHeader == null) packet, * and then block the doTransport write, * finally unblock it when finished. */ abstract void sendPacket(Packet p) throws IOException; protected void initProperties() throws IOException { try { packetLen = clientConfig.getInt( ZKConfig.JUTE_MAXBUFFER, ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT); LOG.info("{} value is {} Bytes", ZKConfig.JUTE_MAXBUFFER, packetLen); } catch (NumberFormatException e) { String msg = MessageFormat.format( "Configured value {0} for property {1} can not be parsed to int", clientConfig.getProperty(ZKConfig.JUTE_MAXBUFFER), ZKConfig.JUTE_MAXBUFFER); LOG.error(msg); throw new IOException(msg); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNIO.java0100644 0000000 0000000 00000034733 15051152474 033733 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.channels.UnresolvedAddressException; import java.nio.channels.UnsupportedAddressTypeException; import java.util.Iterator; import java.util.Queue; import java.util.Set; import java.util.concurrent.LinkedBlockingDeque; import org.apache.zookeeper.ClientCnxn.EndOfStreamException; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.client.ZKClientConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientCnxnSocketNIO extends ClientCnxnSocket { private static final Logger LOG = LoggerFactory.getLogger(ClientCnxnSocketNIO.class); private final Selector selector = Selector.open(); private SelectionKey sockKey; private SocketAddress localSocketAddress; private SocketAddress remoteSocketAddress; ClientCnxnSocketNIO(ZKClientConfig clientConfig) throws IOException { this.clientConfig = clientConfig; initProperties(); } @Override boolean isConnected() { return sockKey != null; } /** * @throws InterruptedException * @throws IOException */ void doIO(Queue pendingQueue, ClientCnxn cnxn) throws InterruptedException, IOException { SocketChannel sock = (SocketChannel) sockKey.channel(); if (sock == null) { throw new IOException("Socket is null!"); } if (sockKey.isReadable()) { int rc = sock.read(incomingBuffer); if (rc < 0) { throw new EndOfStreamException("Unable to read additional data from server sessionid 0x" + Long.toHexString(sessionId) + ", likely server has closed socket"); } if (!incomingBuffer.hasRemaining()) { incomingBuffer.flip(); if (incomingBuffer == lenBuffer) { recvCount.getAndIncrement(); readLength(); } else if (!initialized) { readConnectResult(); enableRead(); if (findSendablePacket(outgoingQueue, sendThread.tunnelAuthInProgress()) != null) { // Since SASL authentication has completed (if client is configured to do so), // outgoing packets waiting in the outgoingQueue can now be sent. enableWrite(); } lenBuffer.clear(); incomingBuffer = lenBuffer; updateLastHeard(); initialized = true; } else { sendThread.readResponse(incomingBuffer); lenBuffer.clear(); incomingBuffer = lenBuffer; updateLastHeard(); } } } if (sockKey.isWritable()) { Packet p = findSendablePacket(outgoingQueue, sendThread.tunnelAuthInProgress()); if (p != null) { updateLastSend(); // If we already started writing p, p.bb will already exist if (p.bb == null) { if ((p.requestHeader != null) && (p.requestHeader.getType() != OpCode.ping) && (p.requestHeader.getType() != OpCode.auth)) { p.requestHeader.setXid(cnxn.getXid()); } p.createBB(); } sock.write(p.bb); if (!p.bb.hasRemaining()) { sentCount.getAndIncrement(); outgoingQueue.removeFirstOccurrence(p); if (p.requestHeader != null && p.requestHeader.getType() != OpCode.ping && p.requestHeader.getType() != OpCode.auth) { synchronized (pendingQueue) { pendingQueue.add(p); } } } } if (outgoingQueue.isEmpty()) { // No more packets to send: turn off write interest flag. // Will be turned on later by a later call to enableWrite(), // from within ZooKeeperSaslClient (if client is configured // to attempt SASL authentication), or in either doIO() or // in doTransport() if not. disableWrite(); } else if (!initialized && p != null && !p.bb.hasRemaining()) { // On initial connection, write the complete connect request // packet, but then disable further writes until after // receiving a successful connection response. If the // session is expired, then the server sends the expiration // response and immediately closes its end of the socket. If // the client is simultaneously writing on its end, then the // TCP stack may choose to abort with RST, in which case the // client would never receive the session expired event. See // http://docs.oracle.com/javase/6/docs/technotes/guides/net/articles/connection_release.html disableWrite(); } else { // Just in case enableWrite(); } } } private Packet findSendablePacket(LinkedBlockingDeque outgoingQueue, boolean tunneledAuthInProgres) { if (outgoingQueue.isEmpty()) { return null; } // If we've already starting sending the first packet, we better finish if (outgoingQueue.getFirst().bb != null || !tunneledAuthInProgres) { return outgoingQueue.getFirst(); } // Since client's authentication with server is in progress, // send only the null-header packet queued by primeConnection(). // This packet must be sent so that the SASL authentication process // can proceed, but all other packets should wait until // SASL authentication completes. Iterator iter = outgoingQueue.iterator(); while (iter.hasNext()) { Packet p = iter.next(); if (p.requestHeader == null) { // We've found the priming-packet. Move it to the beginning of the queue. iter.remove(); outgoingQueue.addFirst(p); return p; } else { // Non-priming packet: defer it until later, leaving it in the queue // until authentication completes. LOG.debug("Deferring non-priming packet {} until SASL authentication completes.", p); } } return null; } @Override void cleanup() { if (sockKey != null) { SocketChannel sock = (SocketChannel) sockKey.channel(); sockKey.cancel(); try { sock.socket().shutdownInput(); } catch (IOException e) { LOG.debug("Ignoring exception during shutdown input", e); } try { sock.socket().shutdownOutput(); } catch (IOException e) { LOG.debug("Ignoring exception during shutdown output", e); } try { sock.socket().close(); } catch (IOException e) { LOG.debug("Ignoring exception during socket close", e); } try { sock.close(); } catch (IOException e) { LOG.debug("Ignoring exception during channel close", e); } } try { Thread.sleep(100); } catch (InterruptedException e) { LOG.debug("SendThread interrupted during sleep, ignoring"); } sockKey = null; } @Override void close() { try { if (LOG.isTraceEnabled()) { LOG.trace("Doing client selector close"); } selector.close(); if (LOG.isTraceEnabled()) { LOG.trace("Closed client selector"); } } catch (IOException e) { LOG.warn("Ignoring exception during selector close", e); } } /** * create a socket channel. * @return the created socket channel * @throws IOException */ SocketChannel createSock() throws IOException { SocketChannel sock; sock = SocketChannel.open(); sock.configureBlocking(false); sock.socket().setSoLinger(false, -1); sock.socket().setTcpNoDelay(true); return sock; } /** * register with the selection and connect * @param sock the {@link SocketChannel} * @param addr the address of remote host * @throws IOException */ void registerAndConnect(SocketChannel sock, InetSocketAddress addr) throws IOException { sockKey = sock.register(selector, SelectionKey.OP_CONNECT); boolean immediateConnect = sock.connect(addr); if (immediateConnect) { sendThread.primeConnection(); } } @Override void connect(InetSocketAddress addr) throws IOException { SocketChannel sock = createSock(); try { registerAndConnect(sock, addr); } catch (UnresolvedAddressException | UnsupportedAddressTypeException | SecurityException | IOException e) { LOG.error("Unable to open socket to {}", addr); sock.close(); throw e; } initialized = false; /* * Reset incomingBuffer */ lenBuffer.clear(); incomingBuffer = lenBuffer; } /** * Returns the address to which the socket is connected. * * @return ip address of the remote side of the connection or null if not * connected */ @Override SocketAddress getRemoteSocketAddress() { return remoteSocketAddress; } /** * Returns the local address to which the socket is bound. * * @return ip address of the remote side of the connection or null if not * connected */ @Override SocketAddress getLocalSocketAddress() { return localSocketAddress; } private void updateSocketAddresses() { Socket socket = ((SocketChannel) sockKey.channel()).socket(); localSocketAddress = socket.getLocalSocketAddress(); remoteSocketAddress = socket.getRemoteSocketAddress(); } @Override void packetAdded() { wakeupCnxn(); } @Override void onClosing() { wakeupCnxn(); } private synchronized void wakeupCnxn() { selector.wakeup(); } @Override void doTransport( int waitTimeOut, Queue pendingQueue, ClientCnxn cnxn) throws IOException, InterruptedException { selector.select(waitTimeOut); Set selected; synchronized (this) { selected = selector.selectedKeys(); } // Everything below and until we get back to the select is // non-blocking, so time is effectively a constant. That is // Why we just have to do this once, here updateNow(); for (SelectionKey k : selected) { SocketChannel sc = ((SocketChannel) k.channel()); if (k.isConnectable()) { if (sc.finishConnect()) { updateLastSendAndHeard(); updateSocketAddresses(); sendThread.primeConnection(); } } else if (k.isReadable() || k.isWritable()) { doIO(pendingQueue, cnxn); } } if (sendThread.getZkState().isConnected()) { if (findSendablePacket(outgoingQueue, sendThread.tunnelAuthInProgress()) != null) { enableWrite(); } } selected.clear(); } //TODO should this be synchronized? @Override void testableCloseSocket() throws IOException { LOG.info("testableCloseSocket() called"); // sockKey may be concurrently accessed by multiple // threads. We use tmp here to avoid a race condition SelectionKey tmp = sockKey; if (tmp != null) { ((SocketChannel) tmp.channel()).socket().close(); } } @Override void saslCompleted() { enableWrite(); } synchronized void enableWrite() { int i = sockKey.interestOps(); if ((i & SelectionKey.OP_WRITE) == 0) { sockKey.interestOps(i | SelectionKey.OP_WRITE); } } private synchronized void disableWrite() { int i = sockKey.interestOps(); if ((i & SelectionKey.OP_WRITE) != 0) { sockKey.interestOps(i & (~SelectionKey.OP_WRITE)); } } private synchronized void enableRead() { int i = sockKey.interestOps(); if ((i & SelectionKey.OP_READ) == 0) { sockKey.interestOps(i | SelectionKey.OP_READ); } } @Override void connectionPrimed() { sockKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } Selector getSelector() { return selector; } @Override void sendPacket(Packet p) throws IOException { SocketChannel sock = (SocketChannel) sockKey.channel(); if (sock == null) { throw new IOException("Socket is null!"); } p.createBB(); ByteBuffer pbb = p.bb; sock.write(pbb); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_ClientCnx0100644 0000000 0000000 00000000157 15051152474 032576 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNetty.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ClientCnxnSocketNetty.jav0100755 0000000 0000000 00000046402 15051152474 034247 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.SocketChannel; import io.netty.handler.ssl.SslContext; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Iterator; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.net.ssl.SSLException; import org.apache.zookeeper.ClientCnxn.EndOfStreamException; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.NettyUtils; import org.apache.zookeeper.common.X509Exception; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ClientCnxnSocketNetty implements ClientCnxnSocket abstract methods. * It's responsible for connecting to server, reading/writing network traffic and * being a layer between network data and higher level packets. */ public class ClientCnxnSocketNetty extends ClientCnxnSocket { private static final Logger LOG = LoggerFactory.getLogger(ClientCnxnSocketNetty.class); private final EventLoopGroup eventLoopGroup; private Channel channel; private CountDownLatch firstConnect; private ChannelFuture connectFuture; private final Lock connectLock = new ReentrantLock(); private final AtomicBoolean disconnected = new AtomicBoolean(); private final AtomicBoolean needSasl = new AtomicBoolean(); private final Semaphore waitSasl = new Semaphore(0); private static final AtomicReference TEST_ALLOCATOR = new AtomicReference<>(null); ClientCnxnSocketNetty(ZKClientConfig clientConfig) throws IOException { this.clientConfig = clientConfig; // Client only has 1 outgoing socket, so the event loop group only needs // a single thread. eventLoopGroup = NettyUtils.newNioOrEpollEventLoopGroup(1 /* nThreads */); initProperties(); } /** * lifecycles diagram: *

* loop: * - try: * - - !isConnected() * - - - connect() * - - doTransport() * - catch: * - - cleanup() * close() *

* Other non-lifecycle methods are in jeopardy getting a null channel * when calling in concurrency. We must handle it. */ @Override boolean isConnected() { // Assuming that isConnected() is only used to initiate connection, // not used by some other connection status judgement. connectLock.lock(); try { return channel != null || connectFuture != null; } finally { connectLock.unlock(); } } private Bootstrap configureBootstrapAllocator(Bootstrap bootstrap) { ByteBufAllocator testAllocator = TEST_ALLOCATOR.get(); if (testAllocator != null) { return bootstrap.option(ChannelOption.ALLOCATOR, testAllocator); } else { return bootstrap; } } @Override void connect(InetSocketAddress addr) throws IOException { firstConnect = new CountDownLatch(1); Bootstrap bootstrap = new Bootstrap().group(eventLoopGroup) .channel(NettyUtils.nioOrEpollSocketChannel()) .option(ChannelOption.SO_LINGER, -1) .option(ChannelOption.TCP_NODELAY, true) .handler(new ZKClientPipelineFactory(addr.getHostString(), addr.getPort())); bootstrap = configureBootstrapAllocator(bootstrap); bootstrap.validate(); connectLock.lock(); try { connectFuture = bootstrap.connect(addr); connectFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { // this lock guarantees that channel won't be assigned after cleanup(). boolean connected = false; connectLock.lock(); try { if (!channelFuture.isSuccess()) { LOG.warn("future isn't success.", channelFuture.cause()); return; } else if (connectFuture == null) { LOG.info("connect attempt cancelled"); // If the connect attempt was cancelled but succeeded // anyway, make sure to close the channel, otherwise // we may leak a file descriptor. channelFuture.channel().close(); return; } // setup channel, variables, connection, etc. channel = channelFuture.channel(); disconnected.set(false); initialized = false; lenBuffer.clear(); incomingBuffer = lenBuffer; sendThread.primeConnection(); updateNow(); updateLastSendAndHeard(); if (sendThread.tunnelAuthInProgress()) { waitSasl.drainPermits(); needSasl.set(true); sendPrimePacket(); } else { needSasl.set(false); } connected = true; } finally { connectFuture = null; connectLock.unlock(); if (connected) { LOG.info("channel is connected: {}", channelFuture.channel()); } // need to wake on connect success or failure to avoid // timing out ClientCnxn.SendThread which may be // blocked waiting for first connect in doTransport(). wakeupCnxn(); firstConnect.countDown(); } } }); } finally { connectLock.unlock(); } } @Override void cleanup() { connectLock.lock(); try { if (connectFuture != null) { connectFuture.cancel(false); connectFuture = null; } if (channel != null) { channel.close(); channel = null; } } finally { connectLock.unlock(); } Iterator iter = outgoingQueue.iterator(); while (iter.hasNext()) { Packet p = iter.next(); if (p == WakeupPacket.getInstance()) { iter.remove(); } } } @Override void close() { eventLoopGroup.shutdownGracefully(); } @Override void saslCompleted() { needSasl.set(false); waitSasl.release(); } @Override void connectionPrimed() { } @Override void packetAdded() { // NO-OP. Adding a packet will already wake up a netty connection // so we don't need to add a dummy packet to the queue to trigger // a wake-up. } @Override void onClosing() { if (firstConnect != null) { firstConnect.countDown(); } wakeupCnxn(); LOG.info("channel is told closing"); } private void wakeupCnxn() { if (needSasl.get()) { waitSasl.release(); } if (outgoingQueue != null) { outgoingQueue.add(WakeupPacket.getInstance()); } } @Override void doTransport( int waitTimeOut, Queue pendingQueue, ClientCnxn cnxn) throws IOException, InterruptedException { try { if (!firstConnect.await(waitTimeOut, TimeUnit.MILLISECONDS)) { return; } Packet head = null; if (needSasl.get()) { if (!waitSasl.tryAcquire(waitTimeOut, TimeUnit.MILLISECONDS)) { return; } } else { head = outgoingQueue.poll(waitTimeOut, TimeUnit.MILLISECONDS); } // check if being waken up on closing. if (!sendThread.getZkState().isAlive()) { // adding back the packet to notify of failure in conLossPacket(). addBack(head); return; } // channel disconnection happened if (disconnected.get()) { addBack(head); throw new EndOfStreamException("channel for sessionid 0x" + Long.toHexString(sessionId) + " is lost"); } if (head != null) { doWrite(pendingQueue, head, cnxn); } } finally { updateNow(); } } private void addBack(Packet head) { if (head != null && head != WakeupPacket.getInstance()) { outgoingQueue.addFirst(head); } } /** * Sends a packet to the remote peer and flushes the channel. * @param p packet to send. * @return a ChannelFuture that will complete when the write operation * succeeds or fails. */ private ChannelFuture sendPktAndFlush(Packet p) throws IOException { return sendPkt(p, true); } /** * Sends a packet to the remote peer but does not flush() the channel. * @param p packet to send. * @return a ChannelFuture that will complete when the write operation * succeeds or fails. */ private ChannelFuture sendPktOnly(Packet p) throws IOException { return sendPkt(p, false); } // Use a single listener instance to reduce GC private final GenericFutureListener> onSendPktDoneListener = f -> { if (f.isSuccess()) { sentCount.getAndIncrement(); } }; private ChannelFuture sendPkt(Packet p, boolean doFlush) throws IOException { if (channel == null) { throw new IOException("channel has been closed"); } // Assuming the packet will be sent out successfully. Because if it fails, // the channel will close and clean up queues. p.createBB(); updateLastSend(); final ByteBuf writeBuffer = Unpooled.wrappedBuffer(p.bb); final ChannelFuture result = doFlush ? channel.writeAndFlush(writeBuffer) : channel.write(writeBuffer); result.addListener(onSendPktDoneListener); return result; } private void sendPrimePacket() throws IOException { // assuming the first packet is the priming packet. sendPktAndFlush(outgoingQueue.remove()); } /** * doWrite handles writing the packets from outgoingQueue via network to server. */ private void doWrite(Queue pendingQueue, Packet p, ClientCnxn cnxn) throws IOException { updateNow(); boolean anyPacketsSent = false; while (true) { if (p != WakeupPacket.getInstance()) { if ((p.requestHeader != null) && (p.requestHeader.getType() != ZooDefs.OpCode.ping) && (p.requestHeader.getType() != ZooDefs.OpCode.auth)) { p.requestHeader.setXid(cnxn.getXid()); synchronized (pendingQueue) { pendingQueue.add(p); } } sendPktOnly(p); anyPacketsSent = true; } if (outgoingQueue.isEmpty()) { break; } p = outgoingQueue.remove(); } // TODO: maybe we should flush in the loop above every N packets/bytes? // But, how do we determine the right value for N ... if (anyPacketsSent) { channel.flush(); } } @Override void sendPacket(ClientCnxn.Packet p) throws IOException { sendPktAndFlush(p); } @Override SocketAddress getRemoteSocketAddress() { Channel copiedChanRef = channel; return (copiedChanRef == null) ? null : copiedChanRef.remoteAddress(); } @Override SocketAddress getLocalSocketAddress() { Channel copiedChanRef = channel; return (copiedChanRef == null) ? null : copiedChanRef.localAddress(); } @Override void testableCloseSocket() throws IOException { Channel copiedChanRef = channel; if (copiedChanRef != null) { copiedChanRef.disconnect().awaitUninterruptibly(); } } // *************** CientCnxnSocketNetty ****************** private static class WakeupPacket { private static final Packet instance = new Packet(null, null, null, null, null); protected WakeupPacket() { // Exists only to defeat instantiation. } public static Packet getInstance() { return instance; } } /** * ZKClientPipelineFactory is the netty pipeline factory for this netty * connection implementation. */ private class ZKClientPipelineFactory extends ChannelInitializer { private SslContext sslContext = null; private final String host; private final int port; private ZKClientPipelineFactory(String host, int port) { this.host = host; this.port = port; } @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (clientConfig.getBoolean(ZKClientConfig.SECURE_CLIENT)) { initSSL(pipeline); } pipeline.addLast("handler", new ZKClientHandler()); } // The synchronized is to prevent the race on shared variable "sslContext". // Basically we only need to create it once. private synchronized void initSSL(ChannelPipeline pipeline) throws X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { if (sslContext == null) { try (ClientX509Util x509Util = new ClientX509Util()) { sslContext = x509Util.createNettySslContextForClient(clientConfig); } } pipeline.addLast("ssl", sslContext.newHandler(pipeline.channel().alloc(), host, port)); LOG.info("SSL handler added for channel: {}", pipeline.channel()); } } /** * ZKClientHandler is the netty handler that sits in netty upstream last * place. It mainly handles read traffic and helps synchronize connection state. */ private class ZKClientHandler extends SimpleChannelInboundHandler { AtomicBoolean channelClosed = new AtomicBoolean(false); @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { LOG.info("channel is disconnected: {}", ctx.channel()); cleanup(); } /** * netty handler has encountered problems. We are cleaning it up and tell outside to close * the channel/connection. */ private void cleanup() { if (!channelClosed.compareAndSet(false, true)) { return; } disconnected.set(true); onClosing(); } @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { updateNow(); while (buf.isReadable()) { if (incomingBuffer.remaining() > buf.readableBytes()) { int newLimit = incomingBuffer.position() + buf.readableBytes(); incomingBuffer.limit(newLimit); } buf.readBytes(incomingBuffer); incomingBuffer.limit(incomingBuffer.capacity()); if (!incomingBuffer.hasRemaining()) { incomingBuffer.flip(); if (incomingBuffer == lenBuffer) { recvCount.getAndIncrement(); readLength(); } else if (!initialized) { readConnectResult(); lenBuffer.clear(); incomingBuffer = lenBuffer; initialized = true; updateLastHeard(); } else { sendThread.readResponse(incomingBuffer); lenBuffer.clear(); incomingBuffer = lenBuffer; updateLastHeard(); } } } wakeupCnxn(); // Note: SimpleChannelInboundHandler releases the ByteBuf for us // so we don't need to do it. } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { LOG.error("Unexpected throwable", cause); cleanup(); } } /** * Sets the test ByteBufAllocator. This allocator will be used by all * future instances of this class. * It is not recommended to use this method outside of testing. * @param allocator the ByteBufAllocator to use for all netty buffer * allocations. */ static void setTestAllocator(ByteBufAllocator allocator) { TEST_ALLOCATOR.set(allocator); } /** * Clears the test ByteBufAllocator. The default allocator will be used * by all future instances of this class. * It is not recommended to use this method outside of testing. */ static void clearTestAllocator() { TEST_ALLOCATOR.set(null); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ClientWatchManager.java0100644 0000000 0000000 00000002771 15051152474 033644 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.Set; /** */ public interface ClientWatchManager { /** * Return a set of watchers that should be notified of the event. The * manager must not notify the watcher(s), however it will update it's * internal structure as if the watches had triggered. The intent being * that the callee is now responsible for notifying the watchers of the * event, possibly at some later time. * * @param state event state * @param type event type * @param path event path * @return may be empty set but must not be null */ Set materialize(Watcher.Event.KeeperState state, Watcher.Event.EventType type, String path); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/CreateMode.java0100644 0000000 0000000 00000012326 15051152474 032151 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /*** * CreateMode value determines how the znode is created on ZooKeeper. */ @InterfaceAudience.Public public enum CreateMode { /** * The znode will not be automatically deleted upon client's disconnect. */ PERSISTENT(0, false, false, false, false), /** * The znode will not be automatically deleted upon client's disconnect, * and its name will be appended with a monotonically increasing number. */ PERSISTENT_SEQUENTIAL(2, false, true, false, false), /** * The znode will be deleted upon the client's disconnect. */ EPHEMERAL(1, true, false, false, false), /** * The znode will be deleted upon the client's disconnect, and its name * will be appended with a monotonically increasing number. */ EPHEMERAL_SEQUENTIAL(3, true, true, false, false), /** * The znode will be a container node. Container * nodes are special purpose nodes useful for recipes such as leader, lock, * etc. When the last child of a container is deleted, the container becomes * a candidate to be deleted by the server at some point in the future. * Given this property, you should be prepared to get * {@link org.apache.zookeeper.KeeperException.NoNodeException} * when creating children inside of this container node. */ CONTAINER(4, false, false, true, false), /** * The znode will not be automatically deleted upon client's disconnect. * However if the znode has not been modified within the given TTL, it * will be deleted once it has no children. */ PERSISTENT_WITH_TTL(5, false, false, false, true), /** * The znode will not be automatically deleted upon client's disconnect, * and its name will be appended with a monotonically increasing number. * However if the znode has not been modified within the given TTL, it * will be deleted once it has no children. */ PERSISTENT_SEQUENTIAL_WITH_TTL(6, false, true, false, true); private static final Logger LOG = LoggerFactory.getLogger(CreateMode.class); private boolean ephemeral; private boolean sequential; private final boolean isContainer; private int flag; private boolean isTTL; CreateMode(int flag, boolean ephemeral, boolean sequential, boolean isContainer, boolean isTTL) { this.flag = flag; this.ephemeral = ephemeral; this.sequential = sequential; this.isContainer = isContainer; this.isTTL = isTTL; } public boolean isEphemeral() { return ephemeral; } public boolean isSequential() { return sequential; } public boolean isContainer() { return isContainer; } public boolean isTTL() { return isTTL; } public int toFlag() { return flag; } /** * Map an integer value to a CreateMode value */ public static CreateMode fromFlag(int flag) throws KeeperException { switch (flag) { case 0: return CreateMode.PERSISTENT; case 1: return CreateMode.EPHEMERAL; case 2: return CreateMode.PERSISTENT_SEQUENTIAL; case 3: return CreateMode.EPHEMERAL_SEQUENTIAL; case 4: return CreateMode.CONTAINER; case 5: return CreateMode.PERSISTENT_WITH_TTL; case 6: return CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL; default: String errMsg = "Received an invalid flag value: " + flag + " to convert to a CreateMode"; LOG.error(errMsg); throw new KeeperException.BadArgumentsException(errMsg); } } /** * Map an integer value to a CreateMode value */ public static CreateMode fromFlag(int flag, CreateMode defaultMode) { switch (flag) { case 0: return CreateMode.PERSISTENT; case 1: return CreateMode.EPHEMERAL; case 2: return CreateMode.PERSISTENT_SEQUENTIAL; case 3: return CreateMode.EPHEMERAL_SEQUENTIAL; case 4: return CreateMode.CONTAINER; case 5: return CreateMode.PERSISTENT_WITH_TTL; case 6: return CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL; default: return defaultMode; } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/CreateOptions.java0100644 0000000 0000000 00000005211 15051152474 032713 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.List; import java.util.Objects; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.server.EphemeralType; /** * Options for creating znode in ZooKeeper data tree. */ public class CreateOptions { private final CreateMode createMode; private final List acl; private final long ttl; public CreateMode getCreateMode() { return createMode; } public List getAcl() { return acl; } public long getTtl() { return ttl; } /** * Constructs a builder for {@link CreateOptions}. * * @param acl * the acl for the node * @param createMode * specifying whether the node to be created is ephemeral * and/or sequential */ public static Builder newBuilder(List acl, CreateMode createMode) { return new Builder(createMode, acl); } private CreateOptions(CreateMode createMode, List acl, long ttl) { this.createMode = createMode; this.acl = acl; this.ttl = ttl; EphemeralType.validateTTL(createMode, ttl); } /** * Builder for {@link CreateOptions}. */ public static class Builder { private final CreateMode createMode; private final List acl; private long ttl = -1; private Builder(CreateMode createMode, List acl) { this.createMode = Objects.requireNonNull(createMode, "create mode is mandatory for create options"); this.acl = Objects.requireNonNull(acl, "acl is mandatory for create options"); } public Builder withTtl(long ttl) { this.ttl = ttl; return this; } public CreateOptions build() { return new CreateOptions(createMode, acl, ttl); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_DeleteCon0100644 0000000 0000000 00000000160 15051152474 032543 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/DeleteContainerRequest.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/DeleteContainerRequest.ja0100644 0000000 0000000 00000003213 15051152474 034223 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; public class DeleteContainerRequest implements Record { private String path; public DeleteContainerRequest() { } public DeleteContainerRequest(String path) { this.path = path; } public String getPath() { return path; } @Override public void serialize(OutputArchive archive, String tag) throws IOException { archive.writeBuffer(path.getBytes(StandardCharsets.UTF_8), "path"); } @Override public void deserialize(InputArchive archive, String tag) throws IOException { byte[] bytes = archive.readBuffer("path"); path = new String(bytes, StandardCharsets.UTF_8); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/DigestWatcher.java0100644 0000000 0000000 00000002166 15051152474 032677 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * This interface is used to notify the digest mismatch event. */ public interface DigestWatcher { /** * Called when the digest mismatch is found on a given zxid. * * @param mismatchZxid the zxid when the digest mismatch happened. */ void process(long mismatchZxid); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Environment.java0100644 0000000 0000000 00000006671 15051152474 032453 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; /** * Provide insight into the runtime environment. * */ public class Environment { public static final String JAAS_CONF_KEY = "java.security.auth.login.config"; public static class Entry { private String k; private String v; public Entry(String k, String v) { this.k = k; this.v = v; } public String getKey() { return k; } public String getValue() { return v; } @Override public String toString() { return k + "=" + v; } } private static void put(List l, String k, String v) { l.add(new Entry(k, v)); } public static List list() { List l = new ArrayList<>(); put(l, "zookeeper.version", Version.getFullVersion()); try { put(l, "host.name", InetAddress.getLocalHost().getCanonicalHostName()); } catch (UnknownHostException e) { put(l, "host.name", ""); } put(l, "java.version", System.getProperty("java.version", "")); put(l, "java.vendor", System.getProperty("java.vendor", "")); put(l, "java.home", System.getProperty("java.home", "")); put(l, "java.class.path", System.getProperty("java.class.path", "")); put(l, "java.library.path", System.getProperty("java.library.path", "")); put(l, "java.io.tmpdir", System.getProperty("java.io.tmpdir", "")); put(l, "java.compiler", System.getProperty("java.compiler", "")); put(l, "os.name", System.getProperty("os.name", "")); put(l, "os.arch", System.getProperty("os.arch", "")); put(l, "os.version", System.getProperty("os.version", "")); put(l, "user.name", System.getProperty("user.name", "")); put(l, "user.home", System.getProperty("user.home", "")); put(l, "user.dir", System.getProperty("user.dir", "")); // Get memory information. Runtime runtime = Runtime.getRuntime(); int mb = 1024 * 1024; put(l, "os.memory.free", runtime.freeMemory() / mb + "MB"); put(l, "os.memory.max", runtime.maxMemory() / mb + "MB"); put(l, "os.memory.total", runtime.totalMemory() / mb + "MB"); return l; } public static void logEnv(String msg, Logger log) { List env = Environment.list(); for (Entry e : env) { log.info(msg + e.toString()); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/JLineZNodeCompleter.java0100644 0000000 0000000 00000005516 15051152474 033760 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.Collections; import java.util.List; import jline.console.completer.Completer; class JLineZNodeCompleter implements Completer { private ZooKeeper zk; public JLineZNodeCompleter(ZooKeeper zk) { this.zk = zk; } @SuppressWarnings({"unchecked", "rawtypes"}) public int complete(String buffer, int cursor, List candidates) { // Guarantee that the final token is the one we're expanding buffer = buffer.substring(0, cursor); String token = ""; if (!buffer.endsWith(" ")) { String[] tokens = buffer.split(" "); if (tokens.length != 0) { token = tokens[tokens.length - 1]; } } if (token.startsWith("/")) { return completeZNode(buffer, token, candidates); } return completeCommand(buffer, token, candidates); } private int completeCommand(String buffer, String token, List candidates) { for (String cmd : ZooKeeperMain.getCommands()) { if (cmd.startsWith(token)) { candidates.add(cmd); } } return buffer.lastIndexOf(" ") + 1; } private int completeZNode(String buffer, String token, List candidates) { String path = token; int idx = path.lastIndexOf("/") + 1; String prefix = path.substring(idx); try { // Only the root path can end in a /, so strip it off every other prefix String dir = idx == 1 ? "/" : path.substring(0, idx - 1); List children = zk.getChildren(dir, false); for (String child : children) { if (child.startsWith(prefix)) { candidates.add(child); } } } catch (InterruptedException e) { return 0; } catch (KeeperException e) { return 0; } Collections.sort(candidates); return candidates.size() == 0 ? buffer.length() : buffer.lastIndexOf("/") + 1; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/KeeperException.java0100644 0000000 0000000 00000070550 15051152474 033236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.yetus.audience.InterfaceAudience; @InterfaceAudience.Public public abstract class KeeperException extends Exception { /** * All multi-requests that result in an exception retain the results * here so that it is possible to examine the problems in the catch * scope. Non-multi requests will get a null if they try to access * these results. */ private List results; /** * All non-specific keeper exceptions should be constructed via * this factory method in order to guarantee consistency in error * codes and such. If you know the error code, then you should * construct the special purpose exception directly. That will * allow you to have the most specific possible declarations of * what exceptions might actually be thrown. * * @param code The error code. * @param path The ZooKeeper path being operated on. * @return The specialized exception, presumably to be thrown by * the caller. */ public static KeeperException create(Code code, String path) { KeeperException r = create(code); r.path = path; return r; } /** * @deprecated deprecated in 3.1.0, use {@link #create(Code, String)} * instead */ @Deprecated public static KeeperException create(int code, String path) { KeeperException r = create(Code.get(code)); r.path = path; return r; } /** * @deprecated deprecated in 3.1.0, use {@link #create(Code)} * instead */ @Deprecated public static KeeperException create(int code) { return create(Code.get(code)); } /** * All non-specific keeper exceptions should be constructed via * this factory method in order to guarantee consistency in error * codes and such. If you know the error code, then you should * construct the special purpose exception directly. That will * allow you to have the most specific possible declarations of * what exceptions might actually be thrown. * * @param code The error code of your new exception. This will * also determine the specific type of the exception that is * returned. * @return The specialized exception, presumably to be thrown by * the caller. */ public static KeeperException create(Code code) { switch (code) { case SYSTEMERROR: return new SystemErrorException(); case RUNTIMEINCONSISTENCY: return new RuntimeInconsistencyException(); case DATAINCONSISTENCY: return new DataInconsistencyException(); case CONNECTIONLOSS: return new ConnectionLossException(); case MARSHALLINGERROR: return new MarshallingErrorException(); case UNIMPLEMENTED: return new UnimplementedException(); case OPERATIONTIMEOUT: return new OperationTimeoutException(); case NEWCONFIGNOQUORUM: return new NewConfigNoQuorum(); case RECONFIGINPROGRESS: return new ReconfigInProgress(); case BADARGUMENTS: return new BadArgumentsException(); case APIERROR: return new APIErrorException(); case NONODE: return new NoNodeException(); case NOAUTH: return new NoAuthException(); case BADVERSION: return new BadVersionException(); case NOCHILDRENFOREPHEMERALS: return new NoChildrenForEphemeralsException(); case NODEEXISTS: return new NodeExistsException(); case INVALIDACL: return new InvalidACLException(); case AUTHFAILED: return new AuthFailedException(); case NOTEMPTY: return new NotEmptyException(); case SESSIONEXPIRED: return new SessionExpiredException(); case INVALIDCALLBACK: return new InvalidCallbackException(); case SESSIONMOVED: return new SessionMovedException(); case NOTREADONLY: return new NotReadOnlyException(); case EPHEMERALONLOCALSESSION: return new EphemeralOnLocalSessionException(); case NOWATCHER: return new NoWatcherException(); case RECONFIGDISABLED: return new ReconfigDisabledException(); case SESSIONCLOSEDREQUIRESASLAUTH: return new SessionClosedRequireAuthException(); case REQUESTTIMEOUT: return new RequestTimeoutException(); case QUOTAEXCEEDED: return new QuotaExceededException(); case THROTTLEDOP: return new ThrottledOpException(); case OK: default: throw new IllegalArgumentException("Invalid exception code:" + code.code); } } /** * Set the code for this exception * @param code error code * @deprecated deprecated in 3.1.0, exceptions should be immutable, this * method should not be used */ @Deprecated public void setCode(int code) { this.code = Code.get(code); } /** This interface contains the original static final int constants * which have now been replaced with an enumeration in Code. Do not * reference this class directly, if necessary (legacy code) continue * to access the constants through Code. * Note: an interface is used here due to the fact that enums cannot * reference constants defined within the same enum as said constants * are considered initialized _after_ the enum itself. By using an * interface as a super type this allows the deprecated constants to * be initialized first and referenced when constructing the enums. I * didn't want to have constants declared twice. This * interface should be private, but it's declared public to enable * javadoc to include in the user API spec. */ @SuppressWarnings("DeprecatedIsStillUsed") // still used in Code - kept until 4.0 @Deprecated @InterfaceAudience.Public public interface CodeDeprecated { /** * @deprecated deprecated in 3.1.0, use {@link Code#OK} instead */ @Deprecated int Ok = 0; /** * @deprecated deprecated in 3.1.0, use {@link Code#SYSTEMERROR} instead */ @Deprecated int SystemError = -1; /** * @deprecated deprecated in 3.1.0, use * {@link Code#RUNTIMEINCONSISTENCY} instead */ @Deprecated int RuntimeInconsistency = -2; /** * @deprecated deprecated in 3.1.0, use {@link Code#DATAINCONSISTENCY} * instead */ @Deprecated int DataInconsistency = -3; /** * @deprecated deprecated in 3.1.0, use {@link Code#CONNECTIONLOSS} * instead */ @Deprecated int ConnectionLoss = -4; /** * @deprecated deprecated in 3.1.0, use {@link Code#MARSHALLINGERROR} * instead */ @Deprecated int MarshallingError = -5; /** * @deprecated deprecated in 3.1.0, use {@link Code#UNIMPLEMENTED} * instead */ @Deprecated int Unimplemented = -6; /** * @deprecated deprecated in 3.1.0, use {@link Code#OPERATIONTIMEOUT} * instead */ @Deprecated int OperationTimeout = -7; /** * @deprecated deprecated in 3.1.0, use {@link Code#BADARGUMENTS} * instead */ @Deprecated int BadArguments = -8; @Deprecated int UnknownSession = -12; /** * @deprecated deprecated in 3.1.0, use {@link Code#NEWCONFIGNOQUORUM} * instead */ @Deprecated int NewConfigNoQuorum = -13; /** * @deprecated deprecated in 3.1.0, use {@link Code#RECONFIGINPROGRESS} * instead */ @Deprecated int ReconfigInProgress = -14; /** * @deprecated deprecated in 3.1.0, use {@link Code#APIERROR} instead */ @Deprecated int APIError = -100; /** * @deprecated deprecated in 3.1.0, use {@link Code#NONODE} instead */ @Deprecated int NoNode = -101; /** * @deprecated deprecated in 3.1.0, use {@link Code#NOAUTH} instead */ @Deprecated int NoAuth = -102; /** * @deprecated deprecated in 3.1.0, use {@link Code#BADVERSION} instead */ @Deprecated int BadVersion = -103; /** * @deprecated deprecated in 3.1.0, use * {@link Code#NOCHILDRENFOREPHEMERALS} * instead */ @Deprecated int NoChildrenForEphemerals = -108; /** * @deprecated deprecated in 3.1.0, use {@link Code#NODEEXISTS} instead */ @Deprecated int NodeExists = -110; /** * @deprecated deprecated in 3.1.0, use {@link Code#NOTEMPTY} instead */ @Deprecated int NotEmpty = -111; /** * @deprecated deprecated in 3.1.0, use {@link Code#SESSIONEXPIRED} instead */ @Deprecated int SessionExpired = -112; /** * @deprecated deprecated in 3.1.0, use {@link Code#INVALIDCALLBACK} * instead */ @Deprecated int InvalidCallback = -113; /** * @deprecated deprecated in 3.1.0, use {@link Code#INVALIDACL} instead */ @Deprecated int InvalidACL = -114; /** * @deprecated deprecated in 3.1.0, use {@link Code#AUTHFAILED} instead */ @Deprecated int AuthFailed = -115; // This value will be used directly in {@link CODE#SESSIONMOVED} // public static final int SessionMoved = -118; @Deprecated int EphemeralOnLocalSession = -120; } /** Codes which represent the various KeeperException * types. This enum replaces the deprecated earlier static final int * constants. The old, deprecated, values are in "camel case" while the new * enum values are in all CAPS. */ @InterfaceAudience.Public public enum Code implements CodeDeprecated { /** Everything is OK */ OK(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 #APIERROR}, are system errors. */ SYSTEMERROR(SystemError), /** A runtime inconsistency was found */ RUNTIMEINCONSISTENCY(RuntimeInconsistency), /** A data inconsistency was found */ DATAINCONSISTENCY(DataInconsistency), /** Connection to the server has been lost */ CONNECTIONLOSS(ConnectionLoss), /** Error while marshalling or unmarshalling data */ MARSHALLINGERROR(MarshallingError), /** Operation is unimplemented */ UNIMPLEMENTED(Unimplemented), /** Operation timeout */ OPERATIONTIMEOUT(OperationTimeout), /** Invalid arguments */ BADARGUMENTS(BadArguments), /** No quorum of new config is connected and up-to-date with the leader of last commmitted config - try * invoking reconfiguration after new servers are connected and synced */ NEWCONFIGNOQUORUM(NewConfigNoQuorum), /** Another reconfiguration is in progress -- concurrent reconfigs not supported (yet) */ RECONFIGINPROGRESS(ReconfigInProgress), /** Unknown session (internal server use only) */ UNKNOWNSESSION(UnknownSession), /** 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 #SYSTEMERROR}). */ APIERROR(APIError), /** Node does not exist */ NONODE(NoNode), /** Not authenticated */ NOAUTH(NoAuth), /** Version conflict In case of reconfiguration: reconfig requested from config version X but last seen config has a different version Y */ BADVERSION(BadVersion), /** Ephemeral nodes may not have children */ NOCHILDRENFOREPHEMERALS(NoChildrenForEphemerals), /** The node already exists */ NODEEXISTS(NodeExists), /** The node has children */ NOTEMPTY(NotEmpty), /** The session has been expired by the server */ SESSIONEXPIRED(SessionExpired), /** Invalid callback specified */ INVALIDCALLBACK(InvalidCallback), /** Invalid ACL specified */ INVALIDACL(InvalidACL), /** Client authentication failed */ AUTHFAILED(AuthFailed), /** Session moved to another server, so operation is ignored */ SESSIONMOVED(-118), /** State-changing request is passed to read-only server */ NOTREADONLY(-119), /** Attempt to create ephemeral node on a local session */ EPHEMERALONLOCALSESSION(EphemeralOnLocalSession), /** Attempts to remove a non-existing watcher */ NOWATCHER(-121), /** Request not completed within max allowed time.*/ REQUESTTIMEOUT(-122), /** Attempts to perform a reconfiguration operation when reconfiguration feature is disabled. */ RECONFIGDISABLED(-123), /** The session has been closed by server because server requires client to do authentication * with configured authentication scheme at the server, but client is not configured with * required authentication scheme or configured but authentication failed * (i.e. wrong credential used.). */ SESSIONCLOSEDREQUIRESASLAUTH(-124), /** Exceeded the quota that was set on the path.*/ QUOTAEXCEEDED(-125), /** Operation was throttled and not executed at all. This error code indicates that zookeeper server * is under heavy load and can't process incoming requests at full speed; please retry with back off. */ THROTTLEDOP (-127); private static final Map lookup = new HashMap<>(); static { for (Code c : EnumSet.allOf(Code.class)) { lookup.put(c.code, c); } } private final int code; Code(int code) { this.code = code; } /** * Get the int value for a particular Code. * @return error code as integer */ public int intValue() { return code; } /** * Get the Code value for a particular integer error code * @param code int error code * @return Code value corresponding to specified int code, if null throws IllegalArgumentException */ public static Code get(int code) { Code codeVal = lookup.get(code); if (codeVal == null) { throw new IllegalArgumentException("The current client version cannot lookup this code:" + code); } return codeVal; } } static String getCodeMessage(Code code) { switch (code) { case OK: return "ok"; case SYSTEMERROR: return "SystemError"; case RUNTIMEINCONSISTENCY: return "RuntimeInconsistency"; case DATAINCONSISTENCY: return "DataInconsistency"; case CONNECTIONLOSS: return "ConnectionLoss"; case MARSHALLINGERROR: return "MarshallingError"; case NEWCONFIGNOQUORUM: return "NewConfigNoQuorum"; case RECONFIGINPROGRESS: return "ReconfigInProgress"; case UNIMPLEMENTED: return "Unimplemented"; case OPERATIONTIMEOUT: return "OperationTimeout"; case BADARGUMENTS: return "BadArguments"; case APIERROR: return "APIError"; case NONODE: return "NoNode"; case NOAUTH: return "NoAuth"; case BADVERSION: return "BadVersion"; case NOCHILDRENFOREPHEMERALS: return "NoChildrenForEphemerals"; case NODEEXISTS: return "NodeExists"; case INVALIDACL: return "InvalidACL"; case AUTHFAILED: return "AuthFailed"; case NOTEMPTY: return "Directory not empty"; case SESSIONEXPIRED: return "Session expired"; case INVALIDCALLBACK: return "Invalid callback"; case SESSIONMOVED: return "Session moved"; case NOTREADONLY: return "Not a read-only call"; case EPHEMERALONLOCALSESSION: return "Ephemeral node on local session"; case NOWATCHER: return "No such watcher"; case RECONFIGDISABLED: return "Reconfig is disabled"; case SESSIONCLOSEDREQUIRESASLAUTH: return "Session closed because client failed to authenticate"; case QUOTAEXCEEDED: return "Quota has exceeded"; case THROTTLEDOP: return "Op throttled due to high load"; default: return "Unknown error " + code; } } private Code code; private String path; public KeeperException(Code code) { this.code = code; } KeeperException(Code code, String path) { this.code = code; this.path = path; } /** * Read the error code for this exception * @return the error code for this exception * @deprecated deprecated in 3.1.0, use {@link #code()} instead */ @Deprecated public int getCode() { return code.code; } /** * Read the error Code for this exception * @return the error Code for this exception */ public Code code() { return code; } /** * Read the path for this exception * @return the path associated with this error, null if none */ public String getPath() { return path; } @Override public String getMessage() { if (path == null || path.isEmpty()) { return "KeeperErrorCode = " + getCodeMessage(code); } return "KeeperErrorCode = " + getCodeMessage(code) + " for " + path; } void setMultiResults(List results) { this.results = results; } /** * If this exception was thrown by a multi-request then the (partial) results * and error codes can be retrieved using this getter. * @return A copy of the list of results from the operations in the multi-request. * * @since 3.4.0 * */ public List getResults() { return results != null ? new ArrayList(results) : null; } /** * @see Code#APIERROR */ @InterfaceAudience.Public public static class APIErrorException extends KeeperException { public APIErrorException() { super(Code.APIERROR); } } /** * @see Code#AUTHFAILED */ @InterfaceAudience.Public public static class AuthFailedException extends KeeperException { public AuthFailedException() { super(Code.AUTHFAILED); } } /** * @see Code#BADARGUMENTS */ @InterfaceAudience.Public public static class BadArgumentsException extends KeeperException { public BadArgumentsException() { super(Code.BADARGUMENTS); } public BadArgumentsException(String path) { super(Code.BADARGUMENTS, path); } } /** * @see Code#BADVERSION */ @InterfaceAudience.Public public static class BadVersionException extends KeeperException { public BadVersionException() { super(Code.BADVERSION); } public BadVersionException(String path) { super(Code.BADVERSION, path); } } /** * @see Code#CONNECTIONLOSS */ @InterfaceAudience.Public public static class ConnectionLossException extends KeeperException { public ConnectionLossException() { super(Code.CONNECTIONLOSS); } } /** * @see Code#DATAINCONSISTENCY */ @InterfaceAudience.Public public static class DataInconsistencyException extends KeeperException { public DataInconsistencyException() { super(Code.DATAINCONSISTENCY); } } /** * @see Code#INVALIDACL */ @InterfaceAudience.Public public static class InvalidACLException extends KeeperException { public InvalidACLException() { super(Code.INVALIDACL); } public InvalidACLException(String path) { super(Code.INVALIDACL, path); } } /** * @see Code#INVALIDCALLBACK */ @InterfaceAudience.Public public static class InvalidCallbackException extends KeeperException { public InvalidCallbackException() { super(Code.INVALIDCALLBACK); } } /** * @see Code#MARSHALLINGERROR */ @InterfaceAudience.Public public static class MarshallingErrorException extends KeeperException { public MarshallingErrorException() { super(Code.MARSHALLINGERROR); } } /** * @see Code#NOAUTH */ @InterfaceAudience.Public public static class NoAuthException extends KeeperException { public NoAuthException() { super(Code.NOAUTH); } } /** * @see Code#NEWCONFIGNOQUORUM */ @InterfaceAudience.Public public static class NewConfigNoQuorum extends KeeperException { public NewConfigNoQuorum() { super(Code.NEWCONFIGNOQUORUM); } } /** * @see Code#RECONFIGINPROGRESS */ @InterfaceAudience.Public public static class ReconfigInProgress extends KeeperException { public ReconfigInProgress() { super(Code.RECONFIGINPROGRESS); } } /** * @see Code#NOCHILDRENFOREPHEMERALS */ @InterfaceAudience.Public public static class NoChildrenForEphemeralsException extends KeeperException { public NoChildrenForEphemeralsException() { super(Code.NOCHILDRENFOREPHEMERALS); } public NoChildrenForEphemeralsException(String path) { super(Code.NOCHILDRENFOREPHEMERALS, path); } } /** * @see Code#NODEEXISTS */ @InterfaceAudience.Public public static class NodeExistsException extends KeeperException { public NodeExistsException() { super(Code.NODEEXISTS); } public NodeExistsException(String path) { super(Code.NODEEXISTS, path); } } /** * @see Code#NONODE */ @InterfaceAudience.Public public static class NoNodeException extends KeeperException { public NoNodeException() { super(Code.NONODE); } public NoNodeException(String path) { super(Code.NONODE, path); } } /** * @see Code#NOTEMPTY */ @InterfaceAudience.Public public static class NotEmptyException extends KeeperException { public NotEmptyException() { super(Code.NOTEMPTY); } public NotEmptyException(String path) { super(Code.NOTEMPTY, path); } } /** * @see Code#OPERATIONTIMEOUT */ @InterfaceAudience.Public public static class OperationTimeoutException extends KeeperException { public OperationTimeoutException() { super(Code.OPERATIONTIMEOUT); } } /** * @see Code#RUNTIMEINCONSISTENCY */ @InterfaceAudience.Public public static class RuntimeInconsistencyException extends KeeperException { public RuntimeInconsistencyException() { super(Code.RUNTIMEINCONSISTENCY); } } /** * @see Code#SESSIONEXPIRED */ @InterfaceAudience.Public public static class SessionExpiredException extends KeeperException { public SessionExpiredException() { super(Code.SESSIONEXPIRED); } } /** * @see Code#UNKNOWNSESSION */ @InterfaceAudience.Public public static class UnknownSessionException extends KeeperException { public UnknownSessionException() { super(Code.UNKNOWNSESSION); } } /** * @see Code#SESSIONMOVED */ @InterfaceAudience.Public public static class SessionMovedException extends KeeperException { public SessionMovedException() { super(Code.SESSIONMOVED); } } /** * @see Code#NOTREADONLY */ @InterfaceAudience.Public public static class NotReadOnlyException extends KeeperException { public NotReadOnlyException() { super(Code.NOTREADONLY); } } /** * @see Code#EPHEMERALONLOCALSESSION */ @InterfaceAudience.Public public static class EphemeralOnLocalSessionException extends KeeperException { public EphemeralOnLocalSessionException() { super(Code.EPHEMERALONLOCALSESSION); } } /** * @see Code#SYSTEMERROR */ @InterfaceAudience.Public public static class SystemErrorException extends KeeperException { public SystemErrorException() { super(Code.SYSTEMERROR); } } /** * @see Code#UNIMPLEMENTED */ @InterfaceAudience.Public public static class UnimplementedException extends KeeperException { public UnimplementedException() { super(Code.UNIMPLEMENTED); } } /** * @see Code#NOWATCHER */ @InterfaceAudience.Public public static class NoWatcherException extends KeeperException { public NoWatcherException() { super(Code.NOWATCHER); } public NoWatcherException(String path) { super(Code.NOWATCHER, path); } } /** * @see Code#RECONFIGDISABLED */ @InterfaceAudience.Public public static class ReconfigDisabledException extends KeeperException { public ReconfigDisabledException() { super(Code.RECONFIGDISABLED); } public ReconfigDisabledException(String path) { super(Code.RECONFIGDISABLED, path); } } /** * @see Code#SESSIONCLOSEDREQUIRESASLAUTH */ public static class SessionClosedRequireAuthException extends KeeperException { public SessionClosedRequireAuthException() { super(Code.SESSIONCLOSEDREQUIRESASLAUTH); } public SessionClosedRequireAuthException(String path) { super(Code.SESSIONCLOSEDREQUIRESASLAUTH, path); } } /** * @see Code#REQUESTTIMEOUT */ public static class RequestTimeoutException extends KeeperException { public RequestTimeoutException() { super(Code.REQUESTTIMEOUT); } } /** * @see Code#QUOTAEXCEEDED */ @InterfaceAudience.Public public static class QuotaExceededException extends KeeperException { public QuotaExceededException() { super(Code.QUOTAEXCEEDED); } public QuotaExceededException(String path) { super(Code.QUOTAEXCEEDED, path); } } /** * @see Code#THROTTLEDOP */ public static class ThrottledOpException extends KeeperException { public ThrottledOpException() { super(Code.THROTTLEDOP); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Login.java0100644 0000000 0000000 00000051646 15051152474 031221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * This class is responsible for refreshing Kerberos credentials for * logins for both Zookeeper client and server. * See ZooKeeperSaslServer for server-side usage. * See ZooKeeperSaslClient for client-side usage. */ import java.util.Date; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Supplier; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.ZooKeeperSaslServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Login { private static final String KINIT_COMMAND_DEFAULT = "/usr/bin/kinit"; private static final Logger LOG = LoggerFactory.getLogger(Login.class); public static final String SYSTEM_USER = System.getProperty("user.name", ""); private final Supplier callbackHandlerSupplier; // LoginThread will sleep until 80% of time from last refresh to // ticket's expiry has been reached, at which time it will wake // and try to renew the ticket. private static final float TICKET_RENEW_WINDOW = 0.80f; /** * Percentage of random jitter added to the renewal time */ private static final float TICKET_RENEW_JITTER = 0.05f; // Regardless of TICKET_RENEW_WINDOW setting above and the ticket expiry time, // thread will not sleep between refresh attempts any less than 1 minute (60*1000 milliseconds = 1 minute). // Change the '1' to e.g. 5, to change this to 5 minutes. private static final long DEFAULT_MIN_TIME_BEFORE_RELOGIN = 1 * 60 * 1000L; public static final String MIN_TIME_BEFORE_RELOGIN_CONFIG_KEY = "zookeeper.kerberos.minReLoginTimeMs"; private static final long MIN_TIME_BEFORE_RELOGIN = Long.getLong( MIN_TIME_BEFORE_RELOGIN_CONFIG_KEY, DEFAULT_MIN_TIME_BEFORE_RELOGIN); private Subject subject = null; private Thread t = null; private boolean isKrbTicket = false; private boolean isUsingTicketCache = false; private LoginContext login = null; private String loginContextName = null; private String principal = null; // Initialize 'lastLogin' to do a login at first time private long lastLogin = Time.currentElapsedTime() - MIN_TIME_BEFORE_RELOGIN; private final ZKConfig zkConfig; /** * LoginThread constructor. The constructor starts the thread used to * periodically re-login to the Kerberos Ticket Granting Server. * * @param loginContextName * name of section in JAAS file that will be use to login. Passed * as first param to javax.security.auth.login.LoginContext(). * * @param callbackHandlerSupplier * Per connection callbackhandler supplier. * * @param zkConfig * client or server configurations * @throws javax.security.auth.login.LoginException * Thrown if authentication fails. */ public Login(final String loginContextName, Supplier callbackHandlerSupplier, final ZKConfig zkConfig) throws LoginException { this.zkConfig = zkConfig; this.callbackHandlerSupplier = callbackHandlerSupplier; login = login(loginContextName); this.loginContextName = loginContextName; subject = login.getSubject(); isKrbTicket = !subject.getPrivateCredentials(KerberosTicket.class).isEmpty(); AppConfigurationEntry[] entries = Configuration.getConfiguration().getAppConfigurationEntry(loginContextName); for (AppConfigurationEntry entry : entries) { // there will only be a single entry, so this for() loop will only be iterated through once. if (entry.getOptions().get("useTicketCache") != null) { String val = (String) entry.getOptions().get("useTicketCache"); if (val.equals("true")) { isUsingTicketCache = true; } } if (entry.getOptions().get("principal") != null) { principal = (String) entry.getOptions().get("principal"); } break; } if (!isKrbTicket) { // if no TGT, do not bother with ticket management. return; } // Refresh the Ticket Granting Ticket (TGT) periodically. How often to refresh is determined by the // TGT's existing expiry date and the configured MIN_TIME_BEFORE_RELOGIN. For testing and development, // you can decrease the interval of expiration of tickets (for example, to 3 minutes) by running : // "modprinc -maxlife 3mins " in kadmin. t = new Thread(new Runnable() { public void run() { LOG.info("TGT refresh thread started."); while (true) { // renewal thread's main loop. if it exits from here, thread will exit. KerberosTicket tgt = getTGT(); long now = Time.currentWallTime(); long nextRefresh; Date nextRefreshDate; if (tgt == null) { nextRefresh = now + MIN_TIME_BEFORE_RELOGIN; nextRefreshDate = new Date(nextRefresh); LOG.warn("No TGT found: will try again at {}", nextRefreshDate); } else { nextRefresh = getRefreshTime(tgt); long expiry = tgt.getEndTime().getTime(); Date expiryDate = new Date(expiry); if ((isUsingTicketCache) && (tgt.getEndTime().equals(tgt.getRenewTill()))) { LOG.error( "The TGT cannot be renewed beyond the next expiry date: {}." + "This process will not be able to authenticate new SASL connections after that " + "time (for example, it will not be authenticate a new connection with a Zookeeper " + "Quorum member). Ask your system administrator to either increase the " + "'renew until' time by doing : 'modprinc -maxrenewlife {}' within " + "kadmin, or instead, to generate a keytab for {}. Because the TGT's " + "expiry cannot be further extended by refreshing, exiting refresh thread now.", expiryDate, principal, principal); return; } // determine how long to sleep from looking at ticket's expiry. // We should not allow the ticket to expire, but we should take into consideration // MIN_TIME_BEFORE_RELOGIN. Will not sleep less than MIN_TIME_BEFORE_RELOGIN, unless doing so // would cause ticket expiration. if ((nextRefresh > expiry) || ((now + MIN_TIME_BEFORE_RELOGIN) > expiry)) { // expiry is before next scheduled refresh). nextRefresh = now; } else { if (nextRefresh < (now + MIN_TIME_BEFORE_RELOGIN)) { // next scheduled refresh is sooner than (now + MIN_TIME_BEFORE_LOGIN). Date until = new Date(nextRefresh); Date newuntil = new Date(now + MIN_TIME_BEFORE_RELOGIN); LOG.warn( "TGT refresh thread time adjusted from : {} to : {} since " + "the former is sooner than the minimum refresh interval (" + "{} seconds) from now.", until, newuntil, (MIN_TIME_BEFORE_RELOGIN / 1000)); } nextRefresh = Math.max(nextRefresh, now + MIN_TIME_BEFORE_RELOGIN); } nextRefreshDate = new Date(nextRefresh); if (nextRefresh > expiry) { LOG.error( "next refresh: {} is later than expiry {}." + " This may indicate a clock skew problem." + " Check that this host and the KDC's " + "hosts' clocks are in sync. Exiting refresh thread.", nextRefreshDate, expiryDate); return; } } if (now == nextRefresh) { LOG.info("refreshing now because expiry is before next scheduled refresh time."); } else if (now < nextRefresh) { Date until = new Date(nextRefresh); LOG.info("TGT refresh sleeping until: {}", until.toString()); try { Thread.sleep(nextRefresh - now); } catch (InterruptedException ie) { LOG.warn("TGT renewal thread has been interrupted and will exit."); break; } } else { LOG.error( "nextRefresh:{} is in the past: exiting refresh thread. Check" + " clock sync between this host and KDC - (KDC's clock is likely ahead of this host)." + " Manual intervention will be required for this client to successfully authenticate." + " Exiting refresh thread.", nextRefreshDate); break; } if (isUsingTicketCache) { String cmd = zkConfig.getProperty(ZKConfig.KINIT_COMMAND, KINIT_COMMAND_DEFAULT); String kinitArgs = "-R"; int retry = 1; while (retry >= 0) { try { LOG.debug("running ticket cache refresh command: {} {}", cmd, kinitArgs); Shell.execCommand(cmd, kinitArgs); break; } catch (Exception e) { if (retry > 0) { --retry; // sleep for 10 seconds try { sleepBeforeRetryFailedRefresh(); } catch (InterruptedException ie) { LOG.error("Interrupted while renewing TGT, exiting Login thread"); return; } } else { LOG.warn( "Could not renew TGT due to problem running shell command: '{} {}'." + " Exiting refresh thread.", cmd, kinitArgs, e); return; } } } } try { int retry = 1; while (retry >= 0) { try { reLogin(); break; } catch (LoginException le) { if (retry > 0) { --retry; // sleep for 10 seconds. try { sleepBeforeRetryFailedRefresh(); } catch (InterruptedException e) { LOG.error("Interrupted during login retry after LoginException:", le); throw le; } } else { LOG.error("Could not refresh TGT for principal: {}.", principal, le); } } } } catch (LoginException le) { LOG.error("Failed to refresh TGT: refresh thread exiting now.", le); break; } } } }); t.setDaemon(true); } /** * Return a new CallbackHandler for connections * to avoid race conditions and state sharing in * connection login processing. * * @return connection dependent CallbackHandler */ public CallbackHandler newCallbackHandler() { return callbackHandlerSupplier.get(); } public void startThreadIfNeeded() { // thread object 't' will be null if a refresh thread is not needed. if (t != null) { t.start(); } } public void shutdown() { if ((t != null) && (t.isAlive())) { t.interrupt(); try { t.join(); } catch (InterruptedException e) { LOG.warn("error while waiting for Login thread to shutdown.", e); } } } public Subject getSubject() { return subject; } public String getUserName() { if (principal == null || principal.isEmpty()) { return SYSTEM_USER; } return principal; } public String getLoginContextName() { return loginContextName; } private synchronized LoginContext login(final String loginContextName) throws LoginException { if (loginContextName == null) { throw new LoginException("loginContext name (JAAS file section header) was null. " + "Please check your java.security.login.auth.config (=" + System.getProperty("java.security.login.auth.config") + ") and your " + getLoginContextMessage()); } LoginContext loginContext = new LoginContext(loginContextName, newCallbackHandler()); loginContext.login(); LOG.info("{} successfully logged in.", loginContextName); return loginContext; } private String getLoginContextMessage() { if (zkConfig instanceof ZKClientConfig) { return ZKClientConfig.LOGIN_CONTEXT_NAME_KEY + "(=" + zkConfig.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT) + ")"; } else { return ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY + "(=" + System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME) + ")"; } } // c.f. org.apache.hadoop.security.UserGroupInformation. private long getRefreshTime(KerberosTicket tgt) { long start = tgt.getStartTime().getTime(); long expires = tgt.getEndTime().getTime(); LOG.info("TGT valid starting at: {}", tgt.getStartTime().toString()); LOG.info("TGT expires: {}", tgt.getEndTime().toString()); long proposedRefresh = start + (long) ((expires - start) * (TICKET_RENEW_WINDOW + (TICKET_RENEW_JITTER * ThreadLocalRandom.current().nextDouble()))); if (proposedRefresh > expires) { // proposedRefresh is too far in the future: it's after ticket expires: simply return now. return Time.currentWallTime(); } else { return proposedRefresh; } } private synchronized KerberosTicket getTGT() { Set tickets = subject.getPrivateCredentials(KerberosTicket.class); for (KerberosTicket ticket : tickets) { KerberosPrincipal server = ticket.getServer(); if (server.getName().equals("krbtgt/" + server.getRealm() + "@" + server.getRealm())) { LOG.debug("Client principal is \"{}\".", ticket.getClient().getName()); LOG.debug("Server principal is \"{}\".", ticket.getServer().getName()); return ticket; } } return null; } private boolean hasSufficientTimeElapsed() { long now = Time.currentElapsedTime(); if (now - getLastLogin() < MIN_TIME_BEFORE_RELOGIN) { LOG.warn("Not attempting to re-login since the last re-login was " + "attempted less than {} seconds before.", (MIN_TIME_BEFORE_RELOGIN / 1000)); return false; } // register most recent relogin attempt setLastLogin(now); return true; } /** * Returns login object * @return login */ private LoginContext getLogin() { return login; } /** * Set the login object * @param login */ private void setLogin(LoginContext login) { this.login = login; } /** * Set the last login time. * @param time the number of milliseconds since the beginning of time */ private void setLastLogin(long time) { lastLogin = time; } /** * Get the time of the last login (ticket initialization or last ticket renewal). * @return the number of milliseconds since epoch. */ public long getLastLogin() { return lastLogin; } /** * Re-login a principal. This method assumes that {@link #login(String)} has happened already. * @throws javax.security.auth.login.LoginException on a failure */ // c.f. HADOOP-6559 private synchronized void reLogin() throws LoginException { if (!isKrbTicket) { return; } LoginContext login = getLogin(); if (login == null) { throw new LoginException("login must be done first"); } if (!hasSufficientTimeElapsed()) { return; } LOG.info("Initiating logout for {}", principal); synchronized (Login.class) { //clear up the kerberos state. But the tokens are not cleared! As per //the Java kerberos login module code, only the kerberos credentials //are cleared logout(); //login and also update the subject field of this instance to //have the new credentials (pass it to the LoginContext constructor) login = new LoginContext(loginContextName, getSubject()); LOG.info("Initiating re-login for {}", principal); login.login(); setLogin(login); } } // this method also visible for unit tests, to make sure kerberos state cleaned up protected synchronized void logout() throws LoginException { // We need to make sure not to call LoginContext.logout() when we // are not logged in. Since Java 9 this could result in an NPE. // See ZOOKEEPER-4477 for more details. if (subject != null && !subject.getPrincipals().isEmpty()) { login.logout(); } } // this method is overwritten in unit tests to test concurrency protected void sleepBeforeRetryFailedRefresh() throws InterruptedException { Thread.sleep(10 * 1000); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_MultiOper0100644 0000000 0000000 00000000156 15051152474 032626 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/MultiOperationRecord.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/MultiOperationRecord.java0100644 0000000 0000000 00000017415 15051152474 034257 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.proto.CheckVersionRequest; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.CreateTTLRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.GetChildrenRequest; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.proto.MultiHeader; import org.apache.zookeeper.proto.SetDataRequest; /** * Encodes a composite operation. In the wire format, each operation * consists of a single MultiHeader followed by the appropriate request. * Each of these MultiHeaders has a type which indicates * the type of the following operation or a negative number if no more operations * are included. * All of the operations must be from the same OpKind. */ public class MultiOperationRecord implements Record, Iterable { private List ops = new ArrayList<>(); private Op.OpKind opKind = null; public MultiOperationRecord() { } public MultiOperationRecord(Iterable ops) throws IllegalArgumentException { for (Op op : ops) { setOrCheckOpKind(op.getKind()); add(op); } } @Override public Iterator iterator() { return ops.iterator(); } public void add(Op op) throws IllegalArgumentException { setOrCheckOpKind(op.getKind()); ops.add(op); } public int size() { return ops.size(); } /** * Returns the kind of the operations contained by the record. * @return The OpKind value of all the elements in the record. */ public Op.OpKind getOpKind() { return opKind; } private void setOrCheckOpKind(Op.OpKind ok) throws IllegalArgumentException { if (opKind == null) { opKind = ok; } else if (ok != opKind) { throw new IllegalArgumentException("Mixing read and write operations (transactions)" + " is not allowed in a multi request."); } } @Override public void serialize(OutputArchive archive, String tag) throws IOException { archive.startRecord(this, tag); for (Op op : ops) { MultiHeader h = new MultiHeader(op.getType(), false, -1); h.serialize(archive, tag); switch (op.getType()) { case ZooDefs.OpCode.create: case ZooDefs.OpCode.create2: case ZooDefs.OpCode.createTTL: case ZooDefs.OpCode.createContainer: case ZooDefs.OpCode.delete: case ZooDefs.OpCode.setData: case ZooDefs.OpCode.check: case ZooDefs.OpCode.getChildren: case ZooDefs.OpCode.getData: op.toRequestRecord().serialize(archive, tag); break; default: throw new IOException("Invalid type of op"); } } new MultiHeader(-1, true, -1).serialize(archive, tag); archive.endRecord(this, tag); } @Override public void deserialize(InputArchive archive, String tag) throws IOException { archive.startRecord(tag); MultiHeader h = new MultiHeader(); h.deserialize(archive, tag); try { while (!h.getDone()) { switch (h.getType()) { case ZooDefs.OpCode.create: case ZooDefs.OpCode.create2: case ZooDefs.OpCode.createContainer: CreateRequest cr = new CreateRequest(); cr.deserialize(archive, tag); CreateMode createMode = CreateMode.fromFlag(cr.getFlags(), null); if (createMode == null) { throw new IOException("invalid flag " + cr.getFlags() + " for create mode"); } CreateOptions options = CreateOptions.newBuilder(cr.getAcl(), createMode).build(); add(Op.create(cr.getPath(), cr.getData(), options, h.getType())); break; case ZooDefs.OpCode.createTTL: CreateTTLRequest crTtl = new CreateTTLRequest(); crTtl.deserialize(archive, tag); add(Op.create(crTtl.getPath(), crTtl.getData(), crTtl.getAcl(), crTtl.getFlags(), crTtl.getTtl())); break; case ZooDefs.OpCode.delete: DeleteRequest dr = new DeleteRequest(); dr.deserialize(archive, tag); add(Op.delete(dr.getPath(), dr.getVersion())); break; case ZooDefs.OpCode.setData: SetDataRequest sdr = new SetDataRequest(); sdr.deserialize(archive, tag); add(Op.setData(sdr.getPath(), sdr.getData(), sdr.getVersion())); break; case ZooDefs.OpCode.check: CheckVersionRequest cvr = new CheckVersionRequest(); cvr.deserialize(archive, tag); add(Op.check(cvr.getPath(), cvr.getVersion())); break; case ZooDefs.OpCode.getChildren: GetChildrenRequest gcr = new GetChildrenRequest(); gcr.deserialize(archive, tag); add(Op.getChildren(gcr.getPath())); break; case ZooDefs.OpCode.getData: GetDataRequest gdr = new GetDataRequest(); gdr.deserialize(archive, tag); add(Op.getData(gdr.getPath())); break; default: throw new IOException("Invalid type of op"); } h.deserialize(archive, tag); } } catch (IllegalArgumentException e) { throw new IOException("Mixing different kind of ops"); } archive.endRecord(tag); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MultiOperationRecord)) { return false; } MultiOperationRecord that = (MultiOperationRecord) o; if (ops != null) { Iterator other = that.ops.iterator(); for (Op op : ops) { boolean hasMoreData = other.hasNext(); if (!hasMoreData) { return false; } Op otherOp = other.next(); if (!op.equals(otherOp)) { return false; } } return !other.hasNext(); } else { return that.ops == null; } } @Override public int hashCode() { int h = 1023; for (Op op : ops) { h = h * 25 + op.hashCode(); } return h; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/MultiResponse.java0100644 0000000 0000000 00000016504 15051152474 032754 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.proto.Create2Response; import org.apache.zookeeper.proto.CreateResponse; import org.apache.zookeeper.proto.ErrorResponse; import org.apache.zookeeper.proto.GetChildrenResponse; import org.apache.zookeeper.proto.GetDataResponse; import org.apache.zookeeper.proto.MultiHeader; import org.apache.zookeeper.proto.SetDataResponse; /** * Handles the response from a multi request. Such a response consists of * a sequence of responses each prefixed by a MultiResponse that indicates * the type of the response. The end of the list is indicated by a MultiHeader * with a negative type. Each individual response is in the same format as * with the corresponding operation in the original request list. */ public class MultiResponse implements Record, Iterable { private List results = new ArrayList<>(); public void add(OpResult x) { results.add(x); } @Override public Iterator iterator() { return results.iterator(); } public int size() { return results.size(); } @Override public void serialize(OutputArchive archive, String tag) throws IOException { archive.startRecord(this, tag); for (OpResult result : results) { int err = result.getType() == ZooDefs.OpCode.error ? ((OpResult.ErrorResult) result).getErr() : 0; new MultiHeader(result.getType(), false, err).serialize(archive, tag); switch (result.getType()) { case ZooDefs.OpCode.create: new CreateResponse(((OpResult.CreateResult) result).getPath()).serialize(archive, tag); break; case ZooDefs.OpCode.create2: OpResult.CreateResult createResult = (OpResult.CreateResult) result; new Create2Response(createResult.getPath(), createResult.getStat()).serialize(archive, tag); break; case ZooDefs.OpCode.delete: case ZooDefs.OpCode.check: break; case ZooDefs.OpCode.setData: new SetDataResponse(((OpResult.SetDataResult) result).getStat()).serialize(archive, tag); break; case ZooDefs.OpCode.getChildren: new GetChildrenResponse(((OpResult.GetChildrenResult) result).getChildren()).serialize(archive, tag); break; case ZooDefs.OpCode.getData: new GetDataResponse( ((OpResult.GetDataResult) result).getData(), ((OpResult.GetDataResult) result).getStat()) .serialize(archive, tag); break; case ZooDefs.OpCode.error: new ErrorResponse(((OpResult.ErrorResult) result).getErr()).serialize(archive, tag); break; default: throw new IOException("Invalid type " + result.getType() + " in MultiResponse"); } } new MultiHeader(-1, true, -1).serialize(archive, tag); archive.endRecord(this, tag); } @Override public void deserialize(InputArchive archive, String tag) throws IOException { results = new ArrayList<>(); archive.startRecord(tag); MultiHeader h = new MultiHeader(); h.deserialize(archive, tag); while (!h.getDone()) { switch (h.getType()) { case ZooDefs.OpCode.create: CreateResponse cr = new CreateResponse(); cr.deserialize(archive, tag); results.add(new OpResult.CreateResult(cr.getPath())); break; case ZooDefs.OpCode.create2: Create2Response cr2 = new Create2Response(); cr2.deserialize(archive, tag); results.add(new OpResult.CreateResult(cr2.getPath(), cr2.getStat())); break; case ZooDefs.OpCode.delete: results.add(new OpResult.DeleteResult()); break; case ZooDefs.OpCode.setData: SetDataResponse sdr = new SetDataResponse(); sdr.deserialize(archive, tag); results.add(new OpResult.SetDataResult(sdr.getStat())); break; case ZooDefs.OpCode.check: results.add(new OpResult.CheckResult()); break; case ZooDefs.OpCode.getChildren: GetChildrenResponse gcr = new GetChildrenResponse(); gcr.deserialize(archive, tag); results.add(new OpResult.GetChildrenResult(gcr.getChildren())); break; case ZooDefs.OpCode.getData: GetDataResponse gdr = new GetDataResponse(); gdr.deserialize(archive, tag); results.add(new OpResult.GetDataResult(gdr.getData(), gdr.getStat())); break; case ZooDefs.OpCode.error: // TODO: need way to more cleanly serialize/deserialize exceptions ErrorResponse er = new ErrorResponse(); er.deserialize(archive, tag); results.add(new OpResult.ErrorResult(er.getErr())); break; default: throw new IOException("Invalid type " + h.getType() + " in MultiResponse"); } h.deserialize(archive, tag); } archive.endRecord(tag); } public List getResultList() { return results; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof MultiResponse)) { return false; } MultiResponse other = (MultiResponse) o; if (results != null) { Iterator i = other.results.iterator(); for (OpResult result : results) { if (i.hasNext()) { if (!result.equals(i.next())) { return false; } } else { return false; } } return !i.hasNext(); } else { return other.results == null; } } @Override public int hashCode() { int hash = results.size(); for (OpResult result : results) { hash = (hash * 35) + result.hashCode(); } return hash; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Op.java0100644 0000000 0000000 00000046436 15051152474 030530 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.jute.Record; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.CheckVersionRequest; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.CreateTTLRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.GetChildrenRequest; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.server.EphemeralType; /** * Represents a single operation in a multi-operation transaction. Each operation can be a create, update, * delete, a version check or just read operations like getChildren or getData. * * Sub-classes of Op each represent each detailed type but should not normally be referenced except via * the provided factory methods. * * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode, org.apache.zookeeper.AsyncCallback.StringCallback, Object) * @see ZooKeeper#delete(String, int) * @see ZooKeeper#setData(String, byte[], int) * @see ZooKeeper#getData(String, boolean, Stat) * @see ZooKeeper#getChildren(String, boolean) */ public abstract class Op { public enum OpKind { TRANSACTION, READ } private int type; private String path; private OpKind opKind; // prevent untyped construction private Op(int type, String path, OpKind opKind) { this.type = type; this.path = path; this.opKind = opKind; } /** * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name. * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) * @see CreateMode#fromFlag(int) * * @param path * the path for the node * @param data * the initial data for the node * @param acl * the acl for the node * @param flags * specifying whether the node to be created is ephemeral * and/or sequential but using the integer encoding. */ public static Op create(String path, byte[] data, List acl, int flags) { return new Create(path, data, acl, flags); } /** * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name * but adding an optional ttl * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) * @see CreateMode#fromFlag(int) * * @param path * the path for the node * @param data * the initial data for the node * @param acl * the acl for the node * @param flags * specifying whether the node to be created is ephemeral * and/or sequential but using the integer encoding. * @param ttl * optional ttl or 0 (flags must imply a TTL creation mode) */ public static Op create(String path, byte[] data, List acl, int flags, long ttl) { CreateMode createMode = CreateMode.fromFlag(flags, CreateMode.PERSISTENT); if (createMode.isTTL()) { return new CreateTTL(path, data, acl, createMode, ttl); } return new Create(path, data, acl, flags); } /** * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name. * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) * * @param path * the path for the node * @param data * the initial data for the node * @param acl * the acl for the node * @param createMode * specifying whether the node to be created is ephemeral * and/or sequential */ public static Op create(String path, byte[] data, List acl, CreateMode createMode) { return new Create(path, data, acl, createMode); } /** * Constructs a create operation. Arguments are as for the ZooKeeper method of the same name * but adding an optional ttl * @see ZooKeeper#create(String, byte[], java.util.List, CreateMode) * * @param path * the path for the node * @param data * the initial data for the node * @param acl * the acl for the node * @param createMode * specifying whether the node to be created is ephemeral * and/or sequential * @param ttl * optional ttl or 0 (createMode must imply a TTL) */ public static Op create(String path, byte[] data, List acl, CreateMode createMode, long ttl) { if (createMode.isTTL()) { return new CreateTTL(path, data, acl, createMode, ttl); } return new Create(path, data, acl, createMode); } /** * Constructs a create operation which uses given op code if no one is inferred from create mode. * * @param path * the path for the node * @param data * the initial data for the node * @param options * options for creating znode * @param defaultOpCode * op code to be used if no one is inferred from create mode */ static Op create(String path, byte[] data, CreateOptions options, int defaultOpCode) { if (options.getCreateMode().isTTL()) { return new CreateTTL(path, data, options.getAcl(), options.getCreateMode(), options.getTtl()); } return new Create(path, data, options.getAcl(), options.getCreateMode(), defaultOpCode); } /** * Constructs a create operation which uses {@link ZooDefs.OpCode#create2} if no one is inferred from create mode. * *

The corresponding {@link OpResult.CreateResult#getStat()} could be null if connected to server without this * patch. * * @param path * the path for the node * @param data * the initial data for the node * @param options * options for creating znode */ public static Op create(String path, byte[] data, CreateOptions options) { return create(path, data, options, ZooDefs.OpCode.create2); } /** * Constructs a delete operation. Arguments are as for the ZooKeeper method of the same name. * @see ZooKeeper#delete(String, int) * * @param path * the path of the node to be deleted. * @param version * the expected node version. */ public static Op delete(String path, int version) { return new Delete(path, version); } /** * Constructs an update operation. Arguments are as for the ZooKeeper method of the same name. * @see ZooKeeper#setData(String, byte[], int) * * @param path * the path of the node * @param data * the data to set * @param version * the expected matching version */ public static Op setData(String path, byte[] data, int version) { return new SetData(path, data, version); } /** * Constructs an version check operation. Arguments are as for the ZooKeeper.setData method except that * no data is provided since no update is intended. The purpose for this is to allow read-modify-write * operations that apply to multiple znodes, but where some of the znodes are involved only in the read, * not the write. A similar effect could be achieved by writing the same data back, but that leads to * way more version updates than are necessary and more writing in general. * * @param path * the path of the node * @param version * the expected matching version */ public static Op check(String path, int version) { return new Check(path, version); } public static Op getChildren(String path) { return new GetChildren(path); } public static Op getData(String path) { return new GetData(path); } /** * Gets the integer type code for an Op. This code should be as from ZooDefs.OpCode * @see ZooDefs.OpCode * @return The type code. */ public int getType() { return type; } /** * Gets the path for an Op. * @return The path. */ public String getPath() { return path; } /** * Gets the kind of an Op. * @return The OpKind value. */ public OpKind getKind() { return opKind; } /** * Encodes an op for wire transmission. * @return An appropriate Record structure. */ public abstract Record toRequestRecord(); /** * Reconstructs the transaction with the chroot prefix. * @return transaction with chroot. */ abstract Op withChroot(String addRootPrefix); /** * Performs client path validations. * * @throws IllegalArgumentException * if an invalid path is specified * @throws KeeperException.BadArgumentsException * if an invalid create mode flag is specified */ void validate() throws KeeperException { PathUtils.validatePath(path); } ////////////////// // these internal classes are public, but should not generally be referenced. // public static class Create extends Op { protected byte[] data; protected List acl; protected int flags; private Create(String path, byte[] data, List acl, int flags) { this(path, data, acl, flags, ZooDefs.OpCode.create); } private Create(String path, byte[] data, List acl, int flags, int defaultOpCode) { super(getOpcode(CreateMode.fromFlag(flags, CreateMode.PERSISTENT), defaultOpCode), path, OpKind.TRANSACTION); this.data = data; this.acl = acl; this.flags = flags; } private static int getOpcode(CreateMode createMode, int defaultOpCode) { if (createMode.isTTL()) { return ZooDefs.OpCode.createTTL; } return createMode.isContainer() ? ZooDefs.OpCode.createContainer : defaultOpCode; } private Create(String path, byte[] data, List acl, CreateMode createMode) { this(path, data, acl, createMode, ZooDefs.OpCode.create); } private Create(String path, byte[] data, List acl, CreateMode createMode, int defaultOpCode) { super(getOpcode(createMode, defaultOpCode), path, OpKind.TRANSACTION); this.data = data; this.acl = acl; this.flags = createMode.toFlag(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Create)) { return false; } Create op = (Create) o; boolean aclEquals = true; Iterator i = op.acl.iterator(); for (ACL acl : op.acl) { boolean hasMoreData = i.hasNext(); if (!hasMoreData) { aclEquals = false; break; } ACL otherAcl = i.next(); if (!acl.equals(otherAcl)) { aclEquals = false; break; } } return !i.hasNext() && getType() == op.getType() && Arrays.equals(data, op.data) && flags == op.flags && aclEquals; } @Override public int hashCode() { return getType() + getPath().hashCode() + Arrays.hashCode(data); } @Override public Record toRequestRecord() { return new CreateRequest(getPath(), data, acl, flags); } @Override Op withChroot(String path) { return new Create(path, data, acl, flags); } @Override void validate() throws KeeperException { CreateMode createMode = CreateMode.fromFlag(flags); PathUtils.validatePath(getPath(), createMode.isSequential()); EphemeralType.validateTTL(createMode, -1); } } public static class CreateTTL extends Create { private final long ttl; private CreateTTL(String path, byte[] data, List acl, int flags, long ttl) { super(path, data, acl, flags); this.ttl = ttl; } private CreateTTL(String path, byte[] data, List acl, CreateMode createMode, long ttl) { super(path, data, acl, createMode); this.ttl = ttl; } @Override public boolean equals(Object o) { return super.equals(o) && (o instanceof CreateTTL) && (ttl == ((CreateTTL) o).ttl); } @Override public int hashCode() { return super.hashCode() + (int) (ttl ^ (ttl >>> 32)); } @Override public Record toRequestRecord() { return new CreateTTLRequest(getPath(), data, acl, flags, ttl); } @Override Op withChroot(String path) { return new CreateTTL(path, data, acl, flags, ttl); } @Override void validate() throws KeeperException { CreateMode createMode = CreateMode.fromFlag(flags); PathUtils.validatePath(getPath(), createMode.isSequential()); EphemeralType.validateTTL(createMode, ttl); } } public static class Delete extends Op { private int version; private Delete(String path, int version) { super(ZooDefs.OpCode.delete, path, OpKind.TRANSACTION); this.version = version; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Delete)) { return false; } Delete op = (Delete) o; return getType() == op.getType() && version == op.version && getPath().equals(op.getPath()); } @Override public int hashCode() { return getType() + getPath().hashCode() + version; } @Override public Record toRequestRecord() { return new DeleteRequest(getPath(), version); } @Override Op withChroot(String path) { return new Delete(path, version); } } public static class SetData extends Op { private byte[] data; private int version; private SetData(String path, byte[] data, int version) { super(ZooDefs.OpCode.setData, path, OpKind.TRANSACTION); this.data = data; this.version = version; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof SetData)) { return false; } SetData op = (SetData) o; return getType() == op.getType() && version == op.version && getPath().equals(op.getPath()) && Arrays.equals(data, op.data); } @Override public int hashCode() { return getType() + getPath().hashCode() + Arrays.hashCode(data) + version; } @Override public Record toRequestRecord() { return new SetDataRequest(getPath(), data, version); } @Override Op withChroot(String path) { return new SetData(path, data, version); } } public static class Check extends Op { private int version; private Check(String path, int version) { super(ZooDefs.OpCode.check, path, OpKind.TRANSACTION); this.version = version; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof Check)) { return false; } Check op = (Check) o; return getType() == op.getType() && getPath().equals(op.getPath()) && version == op.version; } @Override public int hashCode() { return getType() + getPath().hashCode() + version; } @Override public Record toRequestRecord() { return new CheckVersionRequest(getPath(), version); } @Override Op withChroot(String path) { return new Check(path, version); } } public static class GetChildren extends Op { GetChildren(String path) { super(ZooDefs.OpCode.getChildren, path, OpKind.READ); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GetChildren)) { return false; } GetChildren op = (GetChildren) o; return getType() == op.getType() && getPath().equals(op.getPath()); } @Override public int hashCode() { return getType() + getPath().hashCode(); } @Override public Record toRequestRecord() { return new GetChildrenRequest(getPath(), false); } @Override Op withChroot(String path) { return new GetChildren(path); } } public static class GetData extends Op { GetData(String path) { super(ZooDefs.OpCode.getData, path, OpKind.READ); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GetData)) { return false; } GetData op = (GetData) o; return getType() == op.getType() && getPath().equals(op.getPath()); } @Override public int hashCode() { return getType() + getPath().hashCode(); } @Override public Record toRequestRecord() { return new GetDataRequest(getPath(), false); } @Override Op withChroot(String path) { return new GetData(path); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/OpResult.java0100644 0000000 0000000 00000020547 15051152474 031722 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.Arrays; import java.util.List; import org.apache.zookeeper.data.Stat; /** * Encodes the result of a single part of a multiple operation commit. */ public abstract class OpResult { private int type; private OpResult(int type) { this.type = type; } /** * Encodes the return type as from ZooDefs.OpCode. Can be used * to dispatch to the correct cast needed for getting the desired * additional result data. * @see ZooDefs.OpCode * @return an integer identifying what kind of operation this result came from. */ public int getType() { return type; } /** * A result from a create operation. This kind of result allows the * path to be retrieved since the create might have been a sequential * create. */ public static class CreateResult extends OpResult { private String path; private Stat stat; public CreateResult(String path) { this(ZooDefs.OpCode.create, path, null); } public CreateResult(String path, Stat stat) { this(ZooDefs.OpCode.create2, path, stat); } private CreateResult(int opcode, String path, Stat stat) { super(opcode); this.path = path; this.stat = stat; } public String getPath() { return path; } public Stat getStat() { return stat; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof CreateResult)) { return false; } CreateResult other = (CreateResult) o; boolean statsAreEqual = stat == null && other.stat == null || (stat != null && other.stat != null && stat.getMzxid() == other.stat.getMzxid()); return getType() == other.getType() && path.equals(other.getPath()) && statsAreEqual; } @Override public int hashCode() { return (int) (getType() * 35 + path.hashCode() + (stat == null ? 0 : stat.getMzxid())); } } /** * A result from a delete operation. No special values are available. */ public static class DeleteResult extends OpResult { public DeleteResult() { super(ZooDefs.OpCode.delete); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof DeleteResult)) { return false; } DeleteResult opResult = (DeleteResult) o; return getType() == opResult.getType(); } @Override public int hashCode() { return getType(); } } /** * A result from a setData operation. This kind of result provides access * to the Stat structure from the update. */ public static class SetDataResult extends OpResult { private Stat stat; public SetDataResult(Stat stat) { super(ZooDefs.OpCode.setData); this.stat = stat; } public Stat getStat() { return stat; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof SetDataResult)) { return false; } SetDataResult other = (SetDataResult) o; return getType() == other.getType() && stat.getMzxid() == other.stat.getMzxid(); } @Override public int hashCode() { return (int) (getType() * 35 + stat.getMzxid()); } } /** * A result from a version check operation. No special values are available. */ public static class CheckResult extends OpResult { public CheckResult() { super(ZooDefs.OpCode.check); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof CheckResult)) { return false; } CheckResult other = (CheckResult) o; return getType() == other.getType(); } @Override public int hashCode() { return getType(); } } /** * A result from a getChildren operation. Provides a list which contains * the names of the children of a given node. */ public static class GetChildrenResult extends OpResult { private List children; public GetChildrenResult(List children) { super(ZooDefs.OpCode.getChildren); this.children = children; } public List getChildren() { return children; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GetChildrenResult)) { return false; } GetChildrenResult other = (GetChildrenResult) o; return getType() == other.getType() && children.equals(other.children); } @Override public int hashCode() { return getType() * 35 + children.hashCode(); } } /** * A result from a getData operation. The data is represented as a byte array. */ public static class GetDataResult extends OpResult { private byte[] data; private Stat stat; public GetDataResult(byte[] data, Stat stat) { super(ZooDefs.OpCode.getData); this.data = (data == null ? null : Arrays.copyOf(data, data.length)); this.stat = stat; } public byte[] getData() { return data == null ? null : Arrays.copyOf(data, data.length); } public Stat getStat() { return stat; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GetDataResult)) { return false; } GetDataResult other = (GetDataResult) o; return getType() == other.getType() && stat.equals(other.stat) && Arrays.equals(data, other.data); } @Override public int hashCode() { return (int) (getType() * 35 + stat.getMzxid() + Arrays.hashCode(data)); } } /** * An error result from any kind of operation. The point of error results * is that they contain an error code which helps understand what happened. * @see KeeperException.Code * */ public static class ErrorResult extends OpResult { private int err; public ErrorResult(int err) { super(ZooDefs.OpCode.error); this.err = err; } public int getErr() { return err; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ErrorResult)) { return false; } ErrorResult other = (ErrorResult) o; return getType() == other.getType() && err == other.getErr(); } @Override public int hashCode() { return getType() * 35 + err; } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Quotas.java0100644 0000000 0000000 00000005205 15051152474 031413 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * this class manages quotas * and has many other utils * for quota */ public class Quotas { /** the zookeeper nodes that acts as the management and status node **/ public static final String procZookeeper = "/zookeeper"; /** the zookeeper quota node that acts as the quota * management node for zookeeper */ public static final String quotaZookeeper = "/zookeeper/quota"; /** * the limit node that has the limit of * a subtree */ public static final String limitNode = "zookeeper_limits"; /** * the stat node that monitors the limit of * a subtree. */ public static final String statNode = "zookeeper_stats"; /** * return the quota path associated with this * prefix * @param path the actual path in zookeeper. * @return the quota path */ public static String quotaPath(String path) { return quotaZookeeper + path; } /** * return the limit quota path associated with this * prefix * @param path the actual path in zookeeper. * @return the limit quota path */ public static String limitPath(String path) { return quotaZookeeper + path + "/" + limitNode; } /** * return the stat quota path associated with this * prefix. * @param path the actual path in zookeeper * @return the stat quota path */ public static String statPath(String path) { return quotaZookeeper + path + "/" + statNode; } /** * return the real path associated with this * quotaPath. * @param quotaPath the quotaPath which's started with /zookeeper/quota * @return the real path associated with this quotaPath. */ public static String trimQuotaPath(String quotaPath) { return quotaPath.substring(quotaZookeeper.length()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_SaslClien0100644 0000000 0000000 00000000163 15051152474 032561 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/SaslClientCallbackHandler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/SaslClientCallbackHandler0100644 0000000 0000000 00000013112 15051152474 034167 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.RealmCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is used by the SASL mechanisms to get further information to complete * the authentication. For example, a SASL mechanism might use this callback * handler to do verification operation. The CallbackHandler interface here * refers to javax.security.auth.callback.CallbackHandler. It should not be * confused with ZooKeeper packet callbacks like * org.apache.zookeeper.server.auth.SaslServerCallbackHandler. */ public class SaslClientCallbackHandler implements CallbackHandler { private String password = null; private static final Logger LOG = LoggerFactory.getLogger(SaslClientCallbackHandler.class); private final String entity; public SaslClientCallbackHandler(String password, String client) { this.password = password; this.entity = client; } public void handle(Callback[] callbacks) throws UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { NameCallback nc = (NameCallback) callback; nc.setName(nc.getDefaultName()); } else { if (callback instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback) callback; if (password != null) { pc.setPassword(this.password.toCharArray()); } else { LOG.warn( "Could not login: the {} is being asked for a password, but the ZooKeeper {}" + " code does not currently support obtaining a password from the user." + " Make sure that the {} is configured to use a ticket cache (using" + " the JAAS configuration setting 'useTicketCache=true)' and restart the {}. If" + " you still get this message after that, the TGT in the ticket cache has expired and must" + " be manually refreshed. To do so, first determine if you are using a password or a" + " keytab. If the former, run kinit in a Unix shell in the environment of the user who" + " is running this Zookeeper {} using the command" + " 'kinit ' (where is the name of the {}'s Kerberos principal)." + " If the latter, do" + " 'kinit -k -t ' (where is the name of the Kerberos principal, and" + " is the location of the keytab file). After manually refreshing your cache," + " restart this {}. If you continue to see this message after manually refreshing" + " your cache, ensure that your KDC host's clock is in sync with this host's clock.", entity, entity, entity, entity, entity, entity, entity); } } else { if (callback instanceof RealmCallback) { RealmCallback rc = (RealmCallback) callback; rc.setText(rc.getDefaultText()); } else { if (callback instanceof AuthorizeCallback) { AuthorizeCallback ac = (AuthorizeCallback) callback; String authid = ac.getAuthenticationID(); String authzid = ac.getAuthorizationID(); if (authid.equals(authzid)) { ac.setAuthorized(true); } else { ac.setAuthorized(false); } if (ac.isAuthorized()) { ac.setAuthorizedID(authzid); } } else { throw new UnsupportedCallbackException( callback, "Unrecognized SASL " + entity + "Callback"); } } } } } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/SaslServerPrincipal.java0100644 0000000 0000000 00000011765 15051152474 034102 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.zookeeper.client.ZKClientConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Computes the Server Principal for a SASL client. */ public class SaslServerPrincipal { private static final Logger LOG = LoggerFactory.getLogger(SaslServerPrincipal.class); /** * Get the name of the server principal for a SASL client. * @param addr the address of the host. * @param clientConfig the configuration for the client. * @return the name of the principal. */ static String getServerPrincipal(InetSocketAddress addr, ZKClientConfig clientConfig) { return getServerPrincipal(new WrapperInetSocketAddress(addr), clientConfig); } /** * Get the name of the server principal for a SASL client. This is visible for testing purposes. * @param addr the address of the host. * @param clientConfig the configuration for the client. * @return the name of the principal. */ static String getServerPrincipal(WrapperInetSocketAddress addr, ZKClientConfig clientConfig) { String configuredServerPrincipal = clientConfig.getProperty(ZKClientConfig.ZOOKEEPER_SERVER_PRINCIPAL); if (configuredServerPrincipal != null) { // If server principal is already configured then return it return configuredServerPrincipal; } String principalUserName = clientConfig.getProperty( ZKClientConfig.ZK_SASL_CLIENT_USERNAME, ZKClientConfig.ZK_SASL_CLIENT_USERNAME_DEFAULT); String hostName = addr.getHostName(); boolean canonicalize = true; String canonicalizeText = clientConfig.getProperty( ZKClientConfig.ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME, ZKClientConfig.ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME_DEFAULT); try { canonicalize = Boolean.parseBoolean(canonicalizeText); } catch (IllegalArgumentException ea) { LOG.warn( "Could not parse config {} \"{}\" into a boolean using default {}", ZKClientConfig.ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME, canonicalizeText, canonicalize); } if (canonicalize) { WrapperInetAddress ia = addr.getAddress(); if (ia == null) { throw new IllegalArgumentException("Unable to canonicalize address " + addr + " because it's not resolvable"); } String canonicalHostName = ia.getCanonicalHostName(); //avoid using literal IP address when security check fails if (!canonicalHostName.equals(ia.getHostAddress())) { hostName = canonicalHostName; } LOG.debug("Canonicalized address to {}", hostName); } String serverPrincipal = principalUserName + "/" + hostName; return serverPrincipal; } /** * This is here to provide a way to unit test the core logic as the methods for * InetSocketAddress are marked as final. */ static class WrapperInetSocketAddress { private final InetSocketAddress addr; WrapperInetSocketAddress(InetSocketAddress addr) { this.addr = addr; } public String getHostName() { return addr.getHostName(); } public WrapperInetAddress getAddress() { InetAddress ia = addr.getAddress(); return ia == null ? null : new WrapperInetAddress(ia); } @Override public String toString() { return addr.toString(); } } /** * This is here to provide a way to unit test the core logic as the methods for * InetAddress are marked as final. */ static class WrapperInetAddress { private final InetAddress ia; WrapperInetAddress(InetAddress ia) { this.ia = ia; } public String getCanonicalHostName() { return ia.getCanonicalHostName(); } public String getHostAddress() { return ia.getHostAddress(); } @Override public String toString() { return ia.toString(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ServerAdminClient.java0100644 0000000 0000000 00000022223 15051152474 033514 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @InterfaceAudience.Public public class ServerAdminClient { private static final Logger LOG = LoggerFactory.getLogger(ServerAdminClient.class); public static void ruok(String host, int port) { Socket s = null; try { byte[] reqBytes = new byte[4]; ByteBuffer req = ByteBuffer.wrap(reqBytes); req.putInt(ByteBuffer.wrap("ruok".getBytes()).getInt()); s = new Socket(); s.setSoLinger(false, 10); s.setSoTimeout(20000); s.connect(new InetSocketAddress(host, port)); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write(reqBytes); byte[] resBytes = new byte[4]; int rc = is.read(resBytes); String retv = new String(resBytes, UTF_8); System.out.println("rc=" + rc + " retv=" + retv); } catch (IOException e) { LOG.warn("Unexpected exception", e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } } } public static void dump(String host, int port) { Socket s = null; try { byte[] reqBytes = new byte[4]; ByteBuffer req = ByteBuffer.wrap(reqBytes); req.putInt(ByteBuffer.wrap("dump".getBytes()).getInt()); s = new Socket(); s.setSoLinger(false, 10); s.setSoTimeout(20000); s.connect(new InetSocketAddress(host, port)); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write(reqBytes); byte[] resBytes = new byte[1024]; int rc = is.read(resBytes); String retv = new String(resBytes, UTF_8); System.out.println("rc=" + rc + " retv=" + retv); } catch (IOException e) { LOG.warn("Unexpected exception", e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } } } public static void stat(String host, int port) { Socket s = null; try { byte[] reqBytes = new byte[4]; ByteBuffer req = ByteBuffer.wrap(reqBytes); req.putInt(ByteBuffer.wrap("stat".getBytes()).getInt()); s = new Socket(); s.setSoLinger(false, 10); s.setSoTimeout(20000); s.connect(new InetSocketAddress(host, port)); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write(reqBytes); byte[] resBytes = new byte[1024]; int rc = is.read(resBytes); String retv = new String(resBytes, UTF_8); System.out.println("rc=" + rc + " retv=" + retv); } catch (IOException e) { LOG.warn("Unexpected exception", e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } } } public static void kill(String host, int port) { Socket s = null; try { byte[] reqBytes = new byte[4]; ByteBuffer req = ByteBuffer.wrap(reqBytes); req.putInt(ByteBuffer.wrap("kill".getBytes()).getInt()); s = new Socket(); s.setSoLinger(false, 10); s.setSoTimeout(20000); s.connect(new InetSocketAddress(host, port)); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write(reqBytes); byte[] resBytes = new byte[4]; int rc = is.read(resBytes); String retv = new String(resBytes, UTF_8); System.out.println("rc=" + rc + " retv=" + retv); } catch (IOException e) { LOG.warn("Unexpected exception", e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } } } public static void setTraceMask(String host, int port, String traceMaskStr) { Socket s = null; try { byte[] reqBytes = new byte[12]; ByteBuffer req = ByteBuffer.wrap(reqBytes); long traceMask = Long.parseLong(traceMaskStr, 8); req.putInt(ByteBuffer.wrap("stmk".getBytes()).getInt()); req.putLong(traceMask); s = new Socket(); s.setSoLinger(false, 10); s.setSoTimeout(20000); s.connect(new InetSocketAddress(host, port)); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write(reqBytes); byte[] resBytes = new byte[8]; int rc = is.read(resBytes); ByteBuffer res = ByteBuffer.wrap(resBytes); long retv = res.getLong(); System.out.println("rc=" + rc + " retv=0" + Long.toOctalString(retv) + " masks=0" + Long.toOctalString(traceMask)); assert (retv == traceMask); } catch (IOException e) { LOG.warn("Unexpected exception", e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } } } public static void getTraceMask(String host, int port) { Socket s = null; try { byte[] reqBytes = new byte[12]; ByteBuffer req = ByteBuffer.wrap(reqBytes); req.putInt(ByteBuffer.wrap("gtmk".getBytes()).getInt()); s = new Socket(); s.setSoLinger(false, 10); s.setSoTimeout(20000); s.connect(new InetSocketAddress(host, port)); InputStream is = s.getInputStream(); OutputStream os = s.getOutputStream(); os.write(reqBytes); byte[] resBytes = new byte[8]; int rc = is.read(resBytes); ByteBuffer res = ByteBuffer.wrap(resBytes); long retv = res.getLong(); System.out.println("rc=" + rc + " retv=0" + Long.toOctalString(retv)); } catch (IOException e) { LOG.warn("Unexpected exception", e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { LOG.warn("Unexpected exception", e); } } } } private static void usage() { System.out.println("usage: java [-cp CLASSPATH] org.apache.zookeeper.ServerAdminClient " + "host port op (ruok|stat|dump|kill|gettracemask|settracemask) [arguments]"); } public static void main(String[] args) { if (args.length < 3) { usage(); return; } String host = args[0]; int port = Integer.parseInt(args[1]); String op = args[2]; if (op.equalsIgnoreCase("gettracemask")) { getTraceMask(host, port); } else if (op.equalsIgnoreCase("settracemask")) { setTraceMask(host, port, args[3]); } else if (op.equalsIgnoreCase("ruok")) { ruok(host, port); } else if (op.equalsIgnoreCase("kill")) { kill(host, port); } else if (op.equalsIgnoreCase("stat")) { stat(host, port); } else if (op.equalsIgnoreCase("dump")) { dump(host, port); } else { System.out.println("Unrecognized op: " + op); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Shell.java0100644 0000000 0000000 00000040371 15051152474 031211 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 copied from Hadoop's security branch, * with the following changes: * 1. package changed from org.apache.hadoop.util to * org.apache.zookeeper. * 2. Usage of Hadoop's Configuration class removed since * it is not available in Zookeeper: instead, system properties * are used. * 3. The deprecated getUlimitMemoryCommand() method removed since * it is not needed. */ package org.apache.zookeeper; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.ExitCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A base class for running a Unix command. * * Shell can be used to run unix commands like du or * df. It also offers facilities to gate commands by * time-intervals. */ public abstract class Shell { private static final Logger LOG = LoggerFactory.getLogger(Shell.class); /** a Unix command to get the current user's name */ public static final String USER_NAME_COMMAND = "whoami"; /** a Unix command to get the current user's groups list */ public static String[] getGroupsCommand() { return new String[]{"bash", "-c", "groups"}; } /** a Unix command to get a given user's groups list */ public static String[] getGroupsForUserCommand(final String user) { //'groups username' command return is non-consistent across different unixes return new String[]{"bash", "-c", "id -Gn " + user}; } /** a Unix command to set permission */ public static final String SET_PERMISSION_COMMAND = "chmod"; /** a Unix command to set owner */ public static final String SET_OWNER_COMMAND = "chown"; public static final String SET_GROUP_COMMAND = "chgrp"; /** Return a Unix command to get permission information. */ public static String[] getGET_PERMISSION_COMMAND() { //force /bin/ls, except on windows. return new String[]{(WINDOWS ? "ls" : "/bin/ls"), "-ld"}; } /**Time after which the executing script would be timedout*/ protected long timeOutInterval = 0L; /** If or not script timed out*/ private AtomicBoolean timedOut; /** a Unix command to get ulimit of a process. */ public static final String ULIMIT_COMMAND = "ulimit"; /** * Get the Unix command for setting the maximum virtual memory available * to a given child process. This is only relevant when we are forking a * process from within the Mapper or the Reducer implementations. * Also see Hadoop Pipes and Hadoop Streaming. * * It also checks to ensure that we are running on a *nix platform else * (e.g. in Cygwin/Windows) it returns null. * @param memoryLimit virtual memory limit * @return a String[] with the ulimit command arguments or * null if we are running on a non *nix platform or * if the limit is unspecified. */ public static String[] getUlimitMemoryCommand(int memoryLimit) { // ulimit isn't supported on Windows if (WINDOWS) { return null; } return new String[]{ULIMIT_COMMAND, "-v", String.valueOf(memoryLimit)}; } /** Set to true on Windows platforms */ public static final boolean WINDOWS /* borrowed from Path.WINDOWS */ = System.getProperty("os.name").startsWith("Windows"); private long interval; // refresh interval in msec private long lastTime; // last time the command was performed private Map environment; // env for the command execution private File dir; private Process process; // sub process used to execute the command private int exitCode; /**If or not script finished executing*/ private volatile AtomicBoolean completed; public Shell() { this(0L); } /** * @param interval the minimum duration to wait before re-executing the * command. */ public Shell(long interval) { this.interval = interval; this.lastTime = (interval < 0) ? 0 : -interval; } /** set the environment for the command * @param env Mapping of environment variables */ protected void setEnvironment(Map env) { this.environment = env; } /** set the working directory * @param dir The directory where the command would be executed */ protected void setWorkingDirectory(File dir) { this.dir = dir; } /** check to see if a command needs to be executed and execute if needed */ protected void run() throws IOException { if (lastTime + interval > Time.currentElapsedTime()) { return; } exitCode = ExitCode.EXECUTION_FINISHED.getValue(); // reset for next run runCommand(); } /** Run a command */ private void runCommand() throws IOException { ProcessBuilder builder = new ProcessBuilder(getExecString()); Timer timeOutTimer = null; ShellTimeoutTimerTask timeoutTimerTask = null; timedOut = new AtomicBoolean(false); completed = new AtomicBoolean(false); if (environment != null) { builder.environment().putAll(this.environment); } if (dir != null) { builder.directory(this.dir); } process = builder.start(); if (timeOutInterval > 0) { timeOutTimer = new Timer(); timeoutTimerTask = new ShellTimeoutTimerTask(this); //One time scheduling. timeOutTimer.schedule(timeoutTimerTask, timeOutInterval); } final BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); BufferedReader inReader = new BufferedReader(new InputStreamReader(process.getInputStream())); final StringBuffer errMsg = new StringBuffer(); // read error and input streams as this would free up the buffers // free the error stream buffer Thread errThread = new Thread() { @Override public void run() { try { String line = errReader.readLine(); while ((line != null) && !isInterrupted()) { errMsg.append(line); errMsg.append(System.getProperty("line.separator")); line = errReader.readLine(); } } catch (IOException ioe) { LOG.warn("Error reading the error stream", ioe); } } }; try { errThread.start(); } catch (IllegalStateException ise) { } try { parseExecResult(inReader); // parse the output // clear the input stream buffer String line = inReader.readLine(); while (line != null) { line = inReader.readLine(); } // wait for the process to finish and check the exit code exitCode = process.waitFor(); try { // make sure that the error thread exits errThread.join(); } catch (InterruptedException ie) { LOG.warn("Interrupted while reading the error stream", ie); } completed.set(true); //the timeout thread handling //taken care in finally block if (exitCode != ExitCode.EXECUTION_FINISHED.getValue()) { throw new ExitCodeException(exitCode, errMsg.toString()); } } catch (InterruptedException ie) { throw new IOException(ie.toString()); } finally { if ((timeOutTimer != null) && !timedOut.get()) { timeOutTimer.cancel(); } // close the input stream try { inReader.close(); } catch (IOException ioe) { LOG.warn("Error while closing the input stream", ioe); } if (!completed.get()) { errThread.interrupt(); } try { errReader.close(); } catch (IOException ioe) { LOG.warn("Error while closing the error stream", ioe); } process.destroy(); lastTime = Time.currentElapsedTime(); } } /** return an array containing the command name and its parameters */ protected abstract String[] getExecString(); /** Parse the execution result */ protected abstract void parseExecResult(BufferedReader lines) throws IOException; /** get the current sub-process executing the given command * @return process executing the command */ public Process getProcess() { return process; } /** get the exit code * @return the exit code of the process */ public int getExitCode() { return exitCode; } /** * This is an IOException with exit code added. */ @SuppressWarnings("serial") public static class ExitCodeException extends IOException { int exitCode; public ExitCodeException(int exitCode, String message) { super(message); this.exitCode = exitCode; } public int getExitCode() { return exitCode; } } /** * A simple shell command executor. * * ShellCommandExecutorshould be used in cases where the output * of the command needs no explicit parsing and where the command, working * directory and the environment remains unchanged. The output of the command * is stored as-is and is expected to be small. */ public static class ShellCommandExecutor extends Shell { private String[] command; private StringBuffer output; public ShellCommandExecutor(String[] execString) { this(execString, null); } public ShellCommandExecutor(String[] execString, File dir) { this(execString, dir, null); } public ShellCommandExecutor(String[] execString, File dir, Map env) { this(execString, dir, env, 0L); } /** * Create a new instance of the ShellCommandExecutor to execute a command. * * @param execString The command to execute with arguments * @param dir If not-null, specifies the directory which should be set * as the current working directory for the command. * If null, the current working directory is not modified. * @param env If not-null, environment of the command will include the * key-value pairs specified in the map. If null, the current * environment is not modified. * @param timeout Specifies the time in milliseconds, after which the * command will be killed and the status marked as timedout. * If 0, the command will not be timed out. */ public ShellCommandExecutor(String[] execString, File dir, Map env, long timeout) { command = execString.clone(); if (dir != null) { setWorkingDirectory(dir); } if (env != null) { setEnvironment(env); } timeOutInterval = timeout; } /** Execute the shell command. */ public void execute() throws IOException { this.run(); } protected String[] getExecString() { return command; } protected void parseExecResult(BufferedReader lines) throws IOException { output = new StringBuffer(); char[] buf = new char[512]; int nRead; while ((nRead = lines.read(buf, 0, buf.length)) > 0) { output.append(buf, 0, nRead); } } /** Get the output of the shell command.*/ public String getOutput() { return (output == null) ? "" : output.toString(); } /** * Returns the commands of this instance. * Arguments with spaces in are presented with quotes round; other * arguments are presented raw * * @return a string representation of the object. */ public String toString() { StringBuilder builder = new StringBuilder(); String[] args = getExecString(); for (String s : args) { if (s.indexOf(' ') >= 0) { builder.append('"').append(s).append('"'); } else { builder.append(s); } builder.append(' '); } return builder.toString(); } } /** * To check if the passed script to shell command executor timed out or * not. * * @return if the script timed out. */ public boolean isTimedOut() { return timedOut.get(); } /** * Set if the command has timed out. * */ private void setTimedOut() { this.timedOut.set(true); } /** * Static method to execute a shell command. * Covers most of the simple cases without requiring the user to implement * the Shell interface. * @param cmd shell command to execute. * @return the output of the executed command. */ public static String execCommand(String... cmd) throws IOException { return execCommand(null, cmd, 0L); } /** * Static method to execute a shell command. * Covers most of the simple cases without requiring the user to implement * the Shell interface. * @param env the map of environment key=value * @param cmd shell command to execute. * @param timeout time in milliseconds after which script should be marked timeout * @return the output of the executed command.o */ public static String execCommand(Map env, String[] cmd, long timeout) throws IOException { ShellCommandExecutor exec = new ShellCommandExecutor(cmd, null, env, timeout); exec.execute(); return exec.getOutput(); } /** * Static method to execute a shell command. * Covers most of the simple cases without requiring the user to implement * the Shell interface. * @param env the map of environment key=value * @param cmd shell command to execute. * @return the output of the executed command. */ public static String execCommand(Map env, String... cmd) throws IOException { return execCommand(env, cmd, 0L); } /** * Timer which is used to timeout scripts spawned off by shell. */ private static class ShellTimeoutTimerTask extends TimerTask { private Shell shell; public ShellTimeoutTimerTask(Shell shell) { this.shell = shell; } @Override public void run() { Process p = shell.getProcess(); try { p.exitValue(); } catch (Exception e) { //Process has not terminated. //So check if it has completed //if not just destroy it. if (p != null && !shell.completed.get()) { shell.setTimedOut(); p.destroy(); } } } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/StatsTrack.java0100644 0000000 0000000 00000015103 15051152474 032220 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; import org.apache.zookeeper.common.StringUtils; /** * a class that represents the stats associated with quotas */ public class StatsTrack { private static final String countStr = "count"; private static final String countHardLimitStr = "countHardLimit"; private static final String byteStr = "bytes"; private static final String byteHardLimitStr = "byteHardLimit"; private final Map stats = new HashMap<>(); private static final Pattern PAIRS_SEPARATOR = Pattern.compile("[,;]+"); /** * a default constructor for * stats */ public StatsTrack() { this(""); } /** * * @param stat the byte[] stat to be initialized with */ public StatsTrack(byte[] stat) { this(new String(stat, StandardCharsets.UTF_8)); } /** * the stat string should be of the form key1str=long,key2str=long,.. * where either , or ; are valid separators * uninitialized values are returned as -1 * @param stat the stat string to be initialized with */ public StatsTrack(String stat) { this.stats.clear(); if (stat == null || stat.length() == 0) { return; } String[] keyValuePairs = PAIRS_SEPARATOR.split(stat); for (String keyValuePair : keyValuePairs) { String[] kv = keyValuePair.split("="); this.stats.put(kv[0], Long.parseLong(StringUtils.isEmpty(kv[1]) ? "-1" : kv[1])); } } /** * get the count of nodes allowed as part of quota * * @return the count as part of this string */ public long getCount() { return getValue(countStr); } /** * set the count for this stat tracker. * * @param count * the count to set with */ public void setCount(long count) { setValue(countStr, count); } /** * get the count of nodes allowed as part of quota (hard limit) * * @return the count as part of this string */ public long getCountHardLimit() { return getValue(countHardLimitStr); } /** * set the count hard limit * * @param count the count limit to set */ public void setCountHardLimit(long count) { setValue(countHardLimitStr, count); } /** * get the count of bytes allowed as part of quota * * @return the bytes as part of this string */ public long getBytes() { return getValue(byteStr); } /** * set the bytes for this stat tracker. * * @param bytes * the bytes to set with */ public void setBytes(long bytes) { setValue(byteStr, bytes); } /** * get the count of bytes allowed as part of quota (hard limit) * * @return the bytes as part of this string */ public long getByteHardLimit() { return getValue(byteHardLimitStr); } /** * set the byte hard limit * * @param bytes the byte limit to set */ public void setByteHardLimit(long bytes) { setValue(byteHardLimitStr, bytes); } /** * get helper to lookup a given key * * @param key the key to lookup * @return key's value or -1 if it doesn't exist */ private long getValue(String key) { Long val = this.stats.get(key); return val == null ? -1 : val.longValue(); } /** * set helper to set the value for the specified key * * @param key the key to set * @param value the value to set */ private void setValue(String key, long value) { this.stats.put(key, value); } /* * returns the string that maps to this stat tracking. * * Builds a string of the form * "count=4,bytes=5=;countHardLimit=10;byteHardLimit=10" * * This string is slightly hacky to preserve compatibility with 3.4.3 and * older parser. In particular, count must be first, bytes must be second, * all new fields must use a separator that is not a "," (so, ";"), and the * seemingly spurious "=" after the bytes field is essential to allowing * it to be parseable by the old parsing code. */ @Override public String toString() { StringBuilder buf = new StringBuilder(); ArrayList keys = new ArrayList<>(stats.keySet()); // Special handling for count=,byte= to enforce them coming first // for backwards compatibility keys.remove(countStr); keys.remove(byteStr); buf.append(countStr); buf.append("="); buf.append(getCount()); buf.append(","); buf.append(byteStr); buf.append("="); buf.append(getBytes()); if (!keys.isEmpty()) { // Add extra = to trick old parsing code so it will ignore new flags buf.append("="); Collections.sort(keys); for (String key : keys) { buf.append(";"); buf.append(key); buf.append("="); buf.append(stats.get(key)); } } return buf.toString(); } public byte[] getStatsBytes() { return toString().getBytes(StandardCharsets.UTF_8); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final StatsTrack that = (StatsTrack) o; return Objects.equals(stats, that.stats); } @Override public int hashCode() { return Objects.hash(stats); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Testable.java0100644 0000000 0000000 00000002556 15051152474 031710 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; /** * Abstraction that exposes various methods useful for testing ZooKeeper */ public interface Testable { /** * Cause the ZooKeeper instance to behave as if the session expired */ void injectSessionExpiration(); /** * Allow an event to be inserted into the event queue * * @param event event to insert */ void queueEvent(WatchedEvent event); /** * Close the ClientCnxn socket for testing purposes */ default void closeSocket() throws IOException { } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Transaction.java0100644 0000000 0000000 00000004256 15051152474 032431 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.ArrayList; import java.util.List; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.data.ACL; /** * Provides a builder style interface for doing multiple updates. This is * really just a thin layer on top of Zookeeper.multi(). * * @since 3.4.0 * */ @InterfaceAudience.Public public class Transaction { private ZooKeeper zk; private List ops = new ArrayList<>(); protected Transaction(ZooKeeper zk) { this.zk = zk; } public Transaction create(final String path, byte[] data, List acl, CreateMode createMode) { ops.add(Op.create(path, data, acl, createMode.toFlag())); return this; } public Transaction delete(final String path, int version) { ops.add(Op.delete(path, version)); return this; } public Transaction check(String path, int version) { ops.add(Op.check(path, version)); return this; } public Transaction setData(final String path, byte[] data, int version) { ops.add(Op.setData(path, data, version)); return this; } public List commit() throws InterruptedException, KeeperException { return zk.multi(ops); } public void commit(MultiCallback cb, Object ctx) { zk.multi(ops, cb, ctx); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Version.java0100644 0000000 0000000 00000007146 15051152474 031572 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.util.ServiceUtils; public class Version implements org.apache.zookeeper.version.Info { /* * Since the SVN to Git port this field doesn't return the revision anymore * In version 3.5.6, 3.5.7 and 3.6.0 this function is removed by accident. * From version 3.5.8+ and 3.6.1+ it is restored for backward compatibility, but will be removed later * @deprecated deprecated in 3.5.5, use @see {@link #getRevisionHash()} instead * @return the default value -1 */ @Deprecated public static int getRevision() { return REVISION; } public static String getRevisionHash() { return REVISION_HASH; } public static String getBuildDate() { return BUILD_DATE; } @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE", justification = "Missing QUALIFIER causes redundant null-check") public static String getVersion() { return MAJOR + "." + MINOR + "." + MICRO + (QUALIFIER == null ? "" : "-" + QUALIFIER); } public static String getVersionRevision() { return getVersion() + "-" + getRevisionHash(); } public static String getFullVersion() { return getVersionRevision() + ", built on " + getBuildDate(); } public static void printUsage() { System.out.print("Usage:\tjava -cp ... org.apache.zookeeper.Version " + "[--full | --short | --revision],\n\tPrints --full version " + "info if no arg specified."); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } /** * Prints the current version, revision and build date to the standard out. * * @param args *

    *
  • --short - prints a short version string "1.2.3" *
  • --revision - prints a short version string with the Git * repository revision "1.2.3-${revision_hash}" *
  • --full - prints the revision and the build date *
*/ public static void main(String[] args) { if (args.length > 1) { printUsage(); } if (args.length == 0 || (args.length == 1 && args[0].equals("--full"))) { System.out.println(getFullVersion()); ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue()); } if (args[0].equals("--short")) { System.out.println(getVersion()); } else if (args[0].equals("--revision")) { System.out.println(getVersionRevision()); } else { printUsage(); } ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue()); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/WatchDeregistration.java0100644 0000000 0000000 00000004312 15051152474 034107 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.Map; import java.util.Set; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.WatcherType; /** * Handles the special case of removing watches which has registered for a * client path */ public class WatchDeregistration { private final String clientPath; private final Watcher watcher; private final WatcherType watcherType; private final boolean local; private final ZKWatchManager zkManager; public WatchDeregistration( String clientPath, Watcher watcher, WatcherType watcherType, boolean local, ZKWatchManager zkManager) { this.clientPath = clientPath; this.watcher = watcher; this.watcherType = watcherType; this.local = local; this.zkManager = zkManager; } /** * Unregistering watcher that was added on path. * * @param rc * the result code of the operation that attempted to remove * watch on the path. */ public Map> unregister(int rc) throws KeeperException { return zkManager.removeWatcher(clientPath, watcher, watcherType, local, rc); } /** * Returns client path which has specified for unregistering its watcher * * @return client path */ public String getClientPath() { return clientPath; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/WatchedEvent.java0100644 0000000 0000000 00000007007 15051152474 032522 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.proto.WatcherEvent; /** * A WatchedEvent represents a change on the ZooKeeper that a Watcher * is able to respond to. The WatchedEvent includes exactly what happened, * the current state of the ZooKeeper, and the path of the znode that * was involved in the event. */ @InterfaceAudience.Public public class WatchedEvent { public static final long NO_ZXID = -1L; private final KeeperState keeperState; private final EventType eventType; private final String path; private final long zxid; /** * Create a WatchedEvent with specified type, state, path and zxid */ public WatchedEvent(EventType eventType, KeeperState keeperState, String path, long zxid) { this.keeperState = keeperState; this.eventType = eventType; this.path = path; this.zxid = zxid; } /** * Create a WatchedEvent with specified type, state and path */ public WatchedEvent(EventType eventType, KeeperState keeperState, String path) { this(eventType, keeperState, path, NO_ZXID); } /** * Convert a WatcherEvent sent over the wire into a full-fledged WatchedEvent */ public WatchedEvent(WatcherEvent eventMessage, long zxid) { keeperState = KeeperState.fromInt(eventMessage.getState()); eventType = EventType.fromInt(eventMessage.getType()); path = eventMessage.getPath(); this.zxid = zxid; } public KeeperState getState() { return keeperState; } public EventType getType() { return eventType; } public String getPath() { return path; } /** * Returns the zxid of the transaction that triggered this watch if it is * of one of the following types:
    *
  • {@link EventType#NodeCreated}
  • *
  • {@link EventType#NodeDeleted}
  • *
  • {@link EventType#NodeDataChanged}
  • *
  • {@link EventType#NodeChildrenChanged}
  • *
* Otherwise, returns {@value #NO_ZXID}. Note that {@value #NO_ZXID} is also * returned by old servers that do not support this feature. */ public long getZxid() { return zxid; } @Override public String toString() { return "WatchedEvent state:" + keeperState + " type:" + eventType + " path:" + path + " zxid: " + zxid; } /** * Convert WatchedEvent to type that can be sent over network */ public WatcherEvent getWrapper() { return new WatcherEvent(eventType.getIntValue(), keeperState.getIntValue(), path); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/Watcher.java0100644 0000000 0000000 00000017436 15051152474 031545 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import org.apache.yetus.audience.InterfaceAudience; /** * This interface specifies the public interface an event handler class must * implement. A ZooKeeper client will get various events from the ZooKeeper * server it connects to. An application using such a client handles these * events by registering a callback object with the client. The callback object * is expected to be an instance of a class that implements Watcher interface. * When {@link #process} is triggered by a watch firing, such as * {@link Event.EventType#NodeDataChanged}, {@link WatchedEvent#getZxid()} will * return the zxid of the transaction that caused said watch to fire. If * {@value WatchedEvent#NO_ZXID} is returned then the server must be updated to * support this feature. * */ @InterfaceAudience.Public public interface Watcher { /** * This interface defines the possible states an Event may represent */ @InterfaceAudience.Public interface Event { /** * Enumeration of states the ZooKeeper may be at the event */ @InterfaceAudience.Public enum KeeperState { /** Unused, this state is never generated by the server */ @Deprecated Unknown(-1), /** The client is in the disconnected state - it is not connected * to any server in the ensemble. */ Disconnected(0), /** Unused, this state is never generated by the server */ @Deprecated NoSyncConnected(1), /** The client is in the connected state - it is connected * to a server in the ensemble (one of the servers specified * in the host connection parameter during ZooKeeper client * creation). */ SyncConnected(3), /** * Auth failed state */ AuthFailed(4), /** * The client is connected to a read-only server, that is the * server which is not currently connected to the majority. * The only operations allowed after receiving this state is * read operations. * This state is generated for read-only clients only since * read/write clients aren't allowed to connect to r/o servers. */ ConnectedReadOnly(5), /** * SaslAuthenticated: used to notify clients that they are SASL-authenticated, * so that they can perform Zookeeper actions with their SASL-authorized permissions. */ SaslAuthenticated(6), /** The serving cluster has expired this session. The ZooKeeper * client connection (the session) is no longer valid. You must * create a new client connection (instantiate a new ZooKeeper * instance) if you with to access the ensemble. */ Expired(-112), /** * The client has been closed. This state is never generated by * the server, but is generated locally when a client calls * {@link ZooKeeper#close()} or {@link ZooKeeper#close(int)} */ Closed(7); private final int intValue; // Integer representation of value // for sending over wire KeeperState(int intValue) { this.intValue = intValue; } public int getIntValue() { return intValue; } public static KeeperState fromInt(int intValue) { switch (intValue) { case -1: return KeeperState.Unknown; case 0: return KeeperState.Disconnected; case 1: return KeeperState.NoSyncConnected; case 3: return KeeperState.SyncConnected; case 4: return KeeperState.AuthFailed; case 5: return KeeperState.ConnectedReadOnly; case 6: return KeeperState.SaslAuthenticated; case -112: return KeeperState.Expired; case 7: return KeeperState.Closed; default: throw new RuntimeException("Invalid integer value for conversion to KeeperState"); } } } /** * Enumeration of types of events that may occur on the ZooKeeper */ @InterfaceAudience.Public enum EventType { None(-1), NodeCreated(1), NodeDeleted(2), NodeDataChanged(3), NodeChildrenChanged(4), DataWatchRemoved(5), ChildWatchRemoved(6), PersistentWatchRemoved (7); private final int intValue; // Integer representation of value // for sending over wire EventType(int intValue) { this.intValue = intValue; } public int getIntValue() { return intValue; } public static EventType fromInt(int intValue) { switch (intValue) { case -1: return EventType.None; case 1: return EventType.NodeCreated; case 2: return EventType.NodeDeleted; case 3: return EventType.NodeDataChanged; case 4: return EventType.NodeChildrenChanged; case 5: return EventType.DataWatchRemoved; case 6: return EventType.ChildWatchRemoved; case 7: return EventType.PersistentWatchRemoved; default: throw new RuntimeException("Invalid integer value for conversion to EventType"); } } } } /** * Enumeration of types of watchers */ @InterfaceAudience.Public enum WatcherType { Children(1), Data(2), Persistent(4), PersistentRecursive(5), Any(3); // Integer representation of value private final int intValue; WatcherType(int intValue) { this.intValue = intValue; } public int getIntValue() { return intValue; } public static WatcherType fromInt(int intValue) { switch (intValue) { case 1: return WatcherType.Children; case 2: return WatcherType.Data; case 3: return WatcherType.Any; case 4: return Persistent; case 5: return PersistentRecursive; default: throw new RuntimeException("Invalid integer value for conversion to WatcherType"); } } } void process(WatchedEvent event); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZKUtil.java0100644 0000000 0000000 00000027302 15051152474 031323 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.File; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.ACL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZKUtil { private static final Logger LOG = LoggerFactory.getLogger(ZKUtil.class); private static final Map permCache = new ConcurrentHashMap<>(); /** * Recursively delete the node with the given path. *

* Important: All versions, of all nodes, under the given node are deleted. *

* If there is an error with deleting one of the sub-nodes in the tree, * this operation would abort and would be the responsibility of the app to handle the same. * * @param zk Zookeeper client * @param pathRoot path to be deleted * @param batchSize number of delete operations to be submitted in one call. * batchSize is also used to decide sync and async delete API invocation. * If batchSize>0 then async otherwise sync delete API is invoked. batchSize>0 * gives better performance. batchSize<=0 scenario is handled to preserve * backward compatibility. * @return true if given node and all its sub nodes are deleted successfully otherwise false * @throws IllegalArgumentException if an invalid path is specified */ public static boolean deleteRecursive( ZooKeeper zk, final String pathRoot, final int batchSize) throws InterruptedException, KeeperException { PathUtils.validatePath(pathRoot); List tree = listSubTreeBFS(zk, pathRoot); LOG.debug("Deleting tree: {}", tree); if (batchSize > 0) { return deleteInBatch(zk, tree, batchSize); } else { for (int i = tree.size() - 1; i >= 0; --i) { //Delete the leaves first and eventually get rid of the root zk.delete(tree.get(i), -1); //Delete all versions of the node with -1. } return true; } } /** * Same as {@link #deleteRecursive(org.apache.zookeeper.ZooKeeper, java.lang.String, int)} * kept here for compatibility with 3.5 clients. * * @since 3.6.1 */ public static void deleteRecursive( ZooKeeper zk, final String pathRoot) throws InterruptedException, KeeperException { // batchSize=0 is passed to preserve the backward compatibility with older clients. deleteRecursive(zk, pathRoot, 0); } private static class BatchedDeleteCbContext { public Semaphore sem; public AtomicBoolean success; public BatchedDeleteCbContext(int rateLimit) { sem = new Semaphore(rateLimit); success = new AtomicBoolean(true); } } private static boolean deleteInBatch(ZooKeeper zk, List tree, int batchSize) throws InterruptedException { int rateLimit = 10; List ops = new ArrayList<>(); BatchedDeleteCbContext context = new BatchedDeleteCbContext(rateLimit); MultiCallback cb = (rc, path, ctx, opResults) -> { if (rc != Code.OK.intValue()) { ((BatchedDeleteCbContext) ctx).success.set(false); } ((BatchedDeleteCbContext) ctx).sem.release(); }; // Delete the leaves first and eventually get rid of the root for (int i = tree.size() - 1; i >= 0; --i) { // Create Op to delete all versions of the node with -1. ops.add(Op.delete(tree.get(i), -1)); if (ops.size() == batchSize || i == 0) { if (!context.success.get()) { // fail fast break; } context.sem.acquire(); zk.multi(ops, cb, context); ops = new ArrayList<>(); } } // wait for all callbacks to finish context.sem.acquire(rateLimit); return context.success.get(); } /** * Recursively delete the node with the given path. (async version). * *

* Important: All versions, of all nodes, under the given node are deleted. *

* If there is an error with deleting one of the sub-nodes in the tree, * this operation would abort and would be the responsibility of the app to handle the same. *

* @param zk the zookeeper handle * @param pathRoot the path to be deleted * @param cb call back method * @param ctx the context the callback method is called with * @throws IllegalArgumentException if an invalid path is specified */ public static void deleteRecursive( ZooKeeper zk, final String pathRoot, VoidCallback cb, Object ctx) throws InterruptedException, KeeperException { PathUtils.validatePath(pathRoot); List tree = listSubTreeBFS(zk, pathRoot); LOG.debug("Deleting tree: {}", tree); for (int i = tree.size() - 1; i >= 0; --i) { //Delete the leaves first and eventually get rid of the root zk.delete(tree.get(i), -1, cb, ctx); //Delete all versions of the node with -1. } } /** * @param filePath the file path to be validated * @return Returns null if valid otherwise error message */ public static String validateFileInput(String filePath) { File file = new File(filePath); if (!file.exists()) { return "File '" + file.getAbsolutePath() + "' does not exist."; } if (!file.canRead()) { return "Read permission is denied on the file '" + file.getAbsolutePath() + "'"; } if (file.isDirectory()) { return "'" + file.getAbsolutePath() + "' is a directory. it must be a file."; } return null; } /** * BFS Traversal of the system under pathRoot, with the entries in the list, in the * same order as that of the traversal. *

* Important: This is not an atomic snapshot of the tree ever, but the * state as it exists across multiple RPCs from zkClient to the ensemble. * For practical purposes, it is suggested to bring the clients to the ensemble * down (i.e. prevent writes to pathRoot) to 'simulate' a snapshot behavior. * * @param zk the zookeeper handle * @param pathRoot The znode path, for which the entire subtree needs to be listed. * @throws InterruptedException * @throws KeeperException */ public static List listSubTreeBFS( ZooKeeper zk, final String pathRoot) throws KeeperException, InterruptedException { Queue queue = new ArrayDeque<>(); List tree = new ArrayList<>(); queue.add(pathRoot); tree.add(pathRoot); while (!queue.isEmpty()) { String node = queue.poll(); List children = zk.getChildren(node, false); for (final String child : children) { // Fix IllegalArgumentException when list "/". final String childPath = (node.equals("/") ? "" : node) + "/" + child; queue.add(childPath); tree.add(childPath); } } return tree; } /** * Visits the subtree with root as given path and calls the passed callback with each znode * found during the search. It performs a depth-first, pre-order traversal of the tree. *

* Important: This is not an atomic snapshot of the tree ever, but the * state as it exists across multiple RPCs from zkClient to the ensemble. * For practical purposes, it is suggested to bring the clients to the ensemble * down (i.e. prevent writes to pathRoot) to 'simulate' a snapshot behavior. */ public static void visitSubTreeDFS( ZooKeeper zk, final String path, boolean watch, StringCallback cb) throws KeeperException, InterruptedException { PathUtils.validatePath(path); zk.getData(path, watch, null); cb.processResult(Code.OK.intValue(), path, null, path); visitSubTreeDFSHelper(zk, path, watch, cb); } @SuppressWarnings("unchecked") private static void visitSubTreeDFSHelper( ZooKeeper zk, final String path, boolean watch, StringCallback cb) throws KeeperException, InterruptedException { // we've already validated, therefore if the path is of length 1 it's the root final boolean isRoot = path.length() == 1; try { List children = zk.getChildren(path, watch, null); Collections.sort(children); for (String child : children) { String childPath = (isRoot ? path : path + "/") + child; cb.processResult(Code.OK.intValue(), childPath, null, child); } for (String child : children) { String childPath = (isRoot ? path : path + "/") + child; visitSubTreeDFSHelper(zk, childPath, watch, cb); } } catch (KeeperException.NoNodeException e) { // Handle race condition where a node is listed // but gets deleted before it can be queried return; // ignore } } /** * @param perms * ACL permissions * @return string representation of permissions */ public static String getPermString(int perms) { return permCache.computeIfAbsent(perms, k -> constructPermString(k)); } private static String constructPermString(int perms) { StringBuilder p = new StringBuilder(); if ((perms & ZooDefs.Perms.CREATE) != 0) { p.append('c'); } if ((perms & ZooDefs.Perms.DELETE) != 0) { p.append('d'); } if ((perms & ZooDefs.Perms.READ) != 0) { p.append('r'); } if ((perms & ZooDefs.Perms.WRITE) != 0) { p.append('w'); } if ((perms & ZooDefs.Perms.ADMIN) != 0) { p.append('a'); } return p.toString(); } public static String aclToString(List acls) { StringBuilder sb = new StringBuilder(); for (ACL acl : acls) { sb.append(acl.getId().getScheme()); sb.append(":"); sb.append(acl.getId().getId()); sb.append(":"); sb.append(getPermString(acl.getPerms())); } return sb.toString(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZKWatchManager.java0100644 0000000 0000000 00000041117 15051152474 032747 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.zookeeper.server.watch.PathParentIterator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manage watchers and handle events generated by the {@link ClientCnxn} object. * * This class is intended to be packaged-private so that it doesn't serve * as part of ZooKeeper client API. */ class ZKWatchManager implements ClientWatchManager { private static final Logger LOG = LoggerFactory.getLogger(ZKWatchManager.class); private final Map> dataWatches = new HashMap<>(); private final Map> existWatches = new HashMap<>(); private final Map> childWatches = new HashMap<>(); private final Map> persistentWatches = new HashMap<>(); private final Map> persistentRecursiveWatches = new HashMap<>(); private final boolean disableAutoWatchReset; private volatile Watcher defaultWatcher; ZKWatchManager(boolean disableAutoWatchReset, Watcher defaultWatcher) { this.disableAutoWatchReset = disableAutoWatchReset; this.defaultWatcher = defaultWatcher; } void setDefaultWatcher(Watcher defaultWatcher) { this.defaultWatcher = defaultWatcher; } Watcher getDefaultWatcher() { return defaultWatcher; } List getDataWatchList() { synchronized (dataWatches) { return new ArrayList<>(dataWatches.keySet()); } } List getChildWatchList() { synchronized (childWatches) { return new ArrayList<>(childWatches.keySet()); } } List getExistWatchList() { synchronized (existWatches) { return new ArrayList<>(existWatches.keySet()); } } List getPersistentWatchList() { synchronized (persistentWatches) { return new ArrayList<>(persistentWatches.keySet()); } } List getPersistentRecursiveWatchList() { synchronized (persistentRecursiveWatches) { return new ArrayList<>(persistentRecursiveWatches.keySet()); } } Map> getDataWatches() { return dataWatches; } Map> getExistWatches() { return existWatches; } Map> getChildWatches() { return childWatches; } Map> getPersistentWatches() { return persistentWatches; } Map> getPersistentRecursiveWatches() { return persistentRecursiveWatches; } private void addTo(Set from, Set to) { if (from != null) { to.addAll(from); } } public Map> removeWatcher( String clientPath, Watcher watcher, Watcher.WatcherType watcherType, boolean local, int rc ) throws KeeperException { // Validate the provided znode path contains the given watcher of // watcherType containsWatcher(clientPath, watcher, watcherType); Map> removedWatchers = new HashMap<>(); HashSet childWatchersToRem = new HashSet<>(); removedWatchers.put(Watcher.Event.EventType.ChildWatchRemoved, childWatchersToRem); HashSet dataWatchersToRem = new HashSet<>(); removedWatchers.put(Watcher.Event.EventType.DataWatchRemoved, dataWatchersToRem); HashSet persistentWatchersToRem = new HashSet<>(); removedWatchers.put(Watcher.Event.EventType.PersistentWatchRemoved, persistentWatchersToRem); boolean removedWatcher = false; switch (watcherType) { case Children: { synchronized (childWatches) { removedWatcher = removeWatches(childWatches, watcher, clientPath, local, rc, childWatchersToRem); } break; } case Data: { synchronized (dataWatches) { removedWatcher = removeWatches(dataWatches, watcher, clientPath, local, rc, dataWatchersToRem); } synchronized (existWatches) { boolean removedDataWatcher = removeWatches(existWatches, watcher, clientPath, local, rc, dataWatchersToRem); removedWatcher |= removedDataWatcher; } break; } case Persistent: { synchronized (persistentWatches) { removedWatcher = removeWatches(persistentWatches, watcher, clientPath, local, rc, persistentWatchersToRem); } break; } case PersistentRecursive: { synchronized (persistentRecursiveWatches) { removedWatcher = removeWatches(persistentRecursiveWatches, watcher, clientPath, local, rc, persistentWatchersToRem); } break; } case Any: { synchronized (childWatches) { removedWatcher = removeWatches(childWatches, watcher, clientPath, local, rc, childWatchersToRem); } synchronized (dataWatches) { boolean removedDataWatcher = removeWatches(dataWatches, watcher, clientPath, local, rc, dataWatchersToRem); removedWatcher |= removedDataWatcher; } synchronized (existWatches) { boolean removedDataWatcher = removeWatches(existWatches, watcher, clientPath, local, rc, dataWatchersToRem); removedWatcher |= removedDataWatcher; } synchronized (persistentWatches) { boolean removedPersistentWatcher = removeWatches(persistentWatches, watcher, clientPath, local, rc, persistentWatchersToRem); removedWatcher |= removedPersistentWatcher; } synchronized (persistentRecursiveWatches) { boolean removedPersistentRecursiveWatcher = removeWatches(persistentRecursiveWatches, watcher, clientPath, local, rc, persistentWatchersToRem); removedWatcher |= removedPersistentRecursiveWatcher; } } } // Watcher function doesn't exists for the specified params if (!removedWatcher) { throw new KeeperException.NoWatcherException(clientPath); } return removedWatchers; } private boolean contains(String path, Watcher watcherObj, Map> pathVsWatchers) { boolean watcherExists = true; if (pathVsWatchers == null || pathVsWatchers.size() == 0) { watcherExists = false; } else { Set watchers = pathVsWatchers.get(path); if (watchers == null) { watcherExists = false; } else if (watcherObj == null) { watcherExists = watchers.size() > 0; } else { watcherExists = watchers.contains(watcherObj); } } return watcherExists; } /** * Validate the provided znode path contains the given watcher and * watcherType * * @param path * - client path * @param watcher * - watcher object reference * @param watcherType * - type of the watcher * @throws KeeperException.NoWatcherException */ void containsWatcher(String path, Watcher watcher, Watcher.WatcherType watcherType) throws KeeperException.NoWatcherException { boolean containsWatcher = false; switch (watcherType) { case Children: { synchronized (childWatches) { containsWatcher = contains(path, watcher, childWatches); } break; } case Data: { synchronized (dataWatches) { containsWatcher = contains(path, watcher, dataWatches); } synchronized (existWatches) { boolean contains_temp = contains(path, watcher, existWatches); containsWatcher |= contains_temp; } break; } case Persistent: { synchronized (persistentWatches) { containsWatcher |= contains(path, watcher, persistentWatches); } break; } case PersistentRecursive: { synchronized (persistentRecursiveWatches) { containsWatcher |= contains(path, watcher, persistentRecursiveWatches); } break; } case Any: { synchronized (childWatches) { containsWatcher = contains(path, watcher, childWatches); } synchronized (dataWatches) { boolean contains_temp = contains(path, watcher, dataWatches); containsWatcher |= contains_temp; } synchronized (existWatches) { boolean contains_temp = contains(path, watcher, existWatches); containsWatcher |= contains_temp; } synchronized (persistentWatches) { boolean contains_temp = contains(path, watcher, persistentWatches); containsWatcher |= contains_temp; } synchronized (persistentRecursiveWatches) { boolean contains_temp = contains(path, watcher, persistentRecursiveWatches); containsWatcher |= contains_temp; } } } // Watcher function doesn't exists for the specified params if (!containsWatcher) { throw new KeeperException.NoWatcherException(path); } } protected boolean removeWatches( Map> pathVsWatcher, Watcher watcher, String path, boolean local, int rc, Set removedWatchers) throws KeeperException { if (!local && rc != KeeperException.Code.OK.intValue()) { throw KeeperException.create(KeeperException.Code.get(rc), path); } boolean success = false; // When local flag is true, remove watchers for the given path // irrespective of rc. Otherwise shouldn't remove watchers locally // when sees failure from server. if (rc == KeeperException.Code.OK.intValue() || (local && rc != KeeperException.Code.OK.intValue())) { // Remove all the watchers for the given path if (watcher == null) { Set pathWatchers = pathVsWatcher.remove(path); if (pathWatchers != null) { // found path watchers removedWatchers.addAll(pathWatchers); success = true; } } else { Set watchers = pathVsWatcher.get(path); if (watchers != null) { if (watchers.remove(watcher)) { // found path watcher removedWatchers.add(watcher); // cleanup if (watchers.size() <= 0) { pathVsWatcher.remove(path); } success = true; } } } } return success; } /* (non-Javadoc) * @see org.apache.zookeeper.ClientWatchManager#materialize(Event.KeeperState, * Event.EventType, java.lang.String) */ @Override public Set materialize( Watcher.Event.KeeperState state, Watcher.Event.EventType type, String clientPath ) { final Set result = new HashSet<>(); switch (type) { case None: if (defaultWatcher != null) { result.add(defaultWatcher); } boolean clear = disableAutoWatchReset && state != Watcher.Event.KeeperState.SyncConnected; synchronized (dataWatches) { for (Set ws : dataWatches.values()) { result.addAll(ws); } if (clear) { dataWatches.clear(); } } synchronized (existWatches) { for (Set ws : existWatches.values()) { result.addAll(ws); } if (clear) { existWatches.clear(); } } synchronized (childWatches) { for (Set ws : childWatches.values()) { result.addAll(ws); } if (clear) { childWatches.clear(); } } synchronized (persistentWatches) { for (Set ws: persistentWatches.values()) { result.addAll(ws); } } synchronized (persistentRecursiveWatches) { for (Set ws: persistentRecursiveWatches.values()) { result.addAll(ws); } } return result; case NodeDataChanged: case NodeCreated: synchronized (dataWatches) { addTo(dataWatches.remove(clientPath), result); } synchronized (existWatches) { addTo(existWatches.remove(clientPath), result); } addPersistentWatches(clientPath, type, result); break; case NodeChildrenChanged: synchronized (childWatches) { addTo(childWatches.remove(clientPath), result); } addPersistentWatches(clientPath, type, result); break; case NodeDeleted: synchronized (dataWatches) { addTo(dataWatches.remove(clientPath), result); } // TODO This shouldn't be needed, but just in case synchronized (existWatches) { Set list = existWatches.remove(clientPath); if (list != null) { addTo(list, result); LOG.warn("We are triggering an exists watch for delete! Shouldn't happen!"); } } synchronized (childWatches) { addTo(childWatches.remove(clientPath), result); } addPersistentWatches(clientPath, type, result); break; default: String errorMsg = String.format( "Unhandled watch event type %s with state %s on path %s", type, state, clientPath); LOG.error(errorMsg); throw new RuntimeException(errorMsg); } return result; } private void addPersistentWatches(String clientPath, Watcher.Event.EventType type, Set result) { synchronized (persistentWatches) { addTo(persistentWatches.get(clientPath), result); } // The semantics of persistent recursive watch promise no child events on descendant nodes. When there // are standard child watches on descendants of node being watched in persistent recursive mode, server // will deliver child events to client inevitably. So we have to filter out child events for persistent // recursive watches on client side. if (type == Watcher.Event.EventType.NodeChildrenChanged) { return; } synchronized (persistentRecursiveWatches) { for (String path : PathParentIterator.forAll(clientPath).asIterable()) { addTo(persistentRecursiveWatches.get(path), result); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZooDefs.java0100644 0000000 0000000 00000007745 15051152474 031523 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.ArrayList; import java.util.Collections; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; @InterfaceAudience.Public public class ZooDefs { public static final String CONFIG_NODE = "/zookeeper/config"; public static final String ZOOKEEPER_NODE_SUBTREE = "/zookeeper/"; @InterfaceAudience.Public public interface OpCode { int notification = 0; int create = 1; int delete = 2; int exists = 3; int getData = 4; int setData = 5; int getACL = 6; int setACL = 7; int getChildren = 8; int sync = 9; int ping = 11; int getChildren2 = 12; int check = 13; int multi = 14; int create2 = 15; int reconfig = 16; int checkWatches = 17; int removeWatches = 18; int createContainer = 19; int deleteContainer = 20; int createTTL = 21; int multiRead = 22; int auth = 100; int setWatches = 101; int sasl = 102; int getEphemerals = 103; int getAllChildrenNumber = 104; int setWatches2 = 105; int addWatch = 106; int whoAmI = 107; int createSession = -10; int closeSession = -11; int error = -1; } @InterfaceAudience.Public public interface Perms { int READ = 1 << 0; int WRITE = 1 << 1; int CREATE = 1 << 2; int DELETE = 1 << 3; int ADMIN = 1 << 4; int ALL = READ | WRITE | CREATE | DELETE | ADMIN; } @InterfaceAudience.Public public interface Ids { /** * This Id represents anyone. */ Id ANYONE_ID_UNSAFE = new Id("world", "anyone"); /** * This Id is only usable to set ACLs. It will get substituted with the * Id's the client authenticated with. */ Id AUTH_IDS = new Id("auth", ""); /** * This is a completely open ACL . */ @SuppressFBWarnings(value = "MS_MUTABLE_COLLECTION", justification = "Cannot break API") ArrayList OPEN_ACL_UNSAFE = new ArrayList<>(Collections.singletonList(new ACL(Perms.ALL, ANYONE_ID_UNSAFE))); /** * This ACL gives the creators authentication id's all permissions. */ @SuppressFBWarnings(value = "MS_MUTABLE_COLLECTION", justification = "Cannot break API") ArrayList CREATOR_ALL_ACL = new ArrayList<>(Collections.singletonList(new ACL(Perms.ALL, AUTH_IDS))); /** * This ACL gives the world the ability to read. */ @SuppressFBWarnings(value = "MS_MUTABLE_COLLECTION", justification = "Cannot break API") ArrayList READ_ACL_UNSAFE = new ArrayList<>(Collections.singletonList(new ACL(Perms.READ, ANYONE_ID_UNSAFE))); } @InterfaceAudience.Public public interface AddWatchModes { int persistent = 0; // matches AddWatchMode.PERSISTENT int persistentRecursive = 1; // matches AddWatchMode.PERSISTENT_RECURSIVE } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeper.java0100644 0000000 0000000 00000401305 15051152474 032043 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.jute.Record; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.AsyncCallback.ACLCallback; import org.apache.zookeeper.AsyncCallback.Children2Callback; import org.apache.zookeeper.AsyncCallback.ChildrenCallback; import org.apache.zookeeper.AsyncCallback.Create2Callback; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.client.ConnectStringParser; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.StaticHostProvider; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.client.ZooKeeperSaslClient; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.ClientInfo; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.AddWatchRequest; import org.apache.zookeeper.proto.CheckWatchesRequest; import org.apache.zookeeper.proto.Create2Response; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.CreateResponse; import org.apache.zookeeper.proto.CreateTTLRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.ErrorResponse; import org.apache.zookeeper.proto.ExistsRequest; import org.apache.zookeeper.proto.GetACLRequest; import org.apache.zookeeper.proto.GetACLResponse; import org.apache.zookeeper.proto.GetAllChildrenNumberRequest; import org.apache.zookeeper.proto.GetAllChildrenNumberResponse; import org.apache.zookeeper.proto.GetChildren2Request; import org.apache.zookeeper.proto.GetChildren2Response; import org.apache.zookeeper.proto.GetChildrenRequest; import org.apache.zookeeper.proto.GetChildrenResponse; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.proto.GetDataResponse; import org.apache.zookeeper.proto.GetEphemeralsRequest; import org.apache.zookeeper.proto.GetEphemeralsResponse; import org.apache.zookeeper.proto.RemoveWatchesRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetACLRequest; import org.apache.zookeeper.proto.SetACLResponse; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.proto.SetDataResponse; import org.apache.zookeeper.proto.SyncRequest; import org.apache.zookeeper.proto.SyncResponse; import org.apache.zookeeper.proto.WhoAmIResponse; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.EphemeralType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the main class of ZooKeeper client library. To use a ZooKeeper * service, an application must first instantiate an object of ZooKeeper class. * All the iterations will be done by calling the methods of ZooKeeper class. * The methods of this class are thread-safe unless otherwise noted. *

* Once a connection to a server is established, a session ID is assigned to the * client. The client will send heart beats to the server periodically to keep * the session valid. *

* The application can call ZooKeeper APIs through a client as long as the * session ID of the client remains valid. *

* If for some reason, the client fails to send heart beats to the server for a * prolonged period of time (exceeding the sessionTimeout value, for instance), * the server will expire the session, and the session ID will become invalid. * The client object will no longer be usable. To make ZooKeeper API calls, the * application must create a new client object. *

* If the ZooKeeper server the client currently connects to fails or otherwise * does not respond, the client will automatically try to connect to another * server before its session ID expires. If successful, the application can * continue to use the client. *

* The ZooKeeper API methods are either synchronous or asynchronous. Synchronous * methods blocks until the server has responded. Asynchronous methods just queue * the request for sending and return immediately. They take a callback object that * will be executed either on successful execution of the request or on error with * an appropriate return code (rc) indicating the error. *

* Some successful ZooKeeper API calls can leave watches on the "data nodes" in * the ZooKeeper server. Other successful ZooKeeper API calls can trigger those * watches. Once a watch is triggered, an event will be delivered to the client * which left the watch at the first place. Each watch can be triggered only * once. Thus, up to one event will be delivered to a client for every watch it * leaves. *

* A client needs an object of a class implementing Watcher interface for * processing the events delivered to the client. * * When a client drops the current connection and re-connects to a server, all the * existing watches are considered as being triggered but the undelivered events * are lost. To emulate this, the client will generate a special event to tell * the event handler a connection has been dropped. This special event has * EventType None and KeeperState Disconnected. * */ /* * We suppress the "try" warning here because the close() method's signature * allows it to throw InterruptedException which is strongly advised against * by AutoCloseable (see: http://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html#close()). * close() will never throw an InterruptedException but the exception remains in the * signature for backwards compatibility purposes. */ @SuppressWarnings("try") @InterfaceAudience.Public public class ZooKeeper implements AutoCloseable { /** * @deprecated Use {@link ZKClientConfig#ZOOKEEPER_CLIENT_CNXN_SOCKET} * instead. */ @Deprecated public static final String ZOOKEEPER_CLIENT_CNXN_SOCKET = "zookeeper.clientCnxnSocket"; // Setting this to "true" will enable encrypted client-server communication. /** * @deprecated Use {@link ZKClientConfig#SECURE_CLIENT} * instead. */ @Deprecated public static final String SECURE_CLIENT = "zookeeper.client.secure"; protected final ClientCnxn cnxn; private static final Logger LOG; static { //Keep these two lines together to keep the initialization order explicit LOG = LoggerFactory.getLogger(ZooKeeper.class); Environment.logEnv("Client environment:", LOG); } protected final HostProvider hostProvider; /** * This function allows a client to update the connection string by providing * a new comma separated list of host:port pairs, each corresponding to a * ZooKeeper server. *

* The function invokes a * probabilistic load-balancing algorithm which may cause the client to disconnect from * its current host with the goal to achieve expected uniform number of connections per server * in the new list. In case the current host to which the client is connected is not in the new * list this call will always cause the connection to be dropped. Otherwise, the decision * is based on whether the number of servers has increased or decreased and by how much. * For example, if the previous connection string contained 3 hosts and now the list contains * these 3 hosts and 2 more hosts, 40% of clients connected to each of the 3 hosts will * move to one of the new hosts in order to balance the load. The algorithm will disconnect * from the current host with probability 0.4 and in this case cause the client to connect * to one of the 2 new hosts, chosen at random. *

* If the connection is dropped, the client moves to a special mode "reconfigMode" where he chooses * a new server to connect to using the probabilistic algorithm. After finding a server, * or exhausting all servers in the new list after trying all of them and failing to connect, * the client moves back to the normal mode of operation where it will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed (or the session is expired by the server). * @param connectString * 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" * If the optional chroot suffix is used the example would look * like: "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). * * @throws IOException in cases of network failure */ public void updateServerList(String connectString) throws IOException { ConnectStringParser connectStringParser = new ConnectStringParser(connectString); Collection serverAddresses = connectStringParser.getServerAddresses(); ClientCnxnSocket clientCnxnSocket = cnxn.sendThread.getClientCnxnSocket(); InetSocketAddress currentHost = (InetSocketAddress) clientCnxnSocket.getRemoteSocketAddress(); boolean reconfigMode = hostProvider.updateServerList(serverAddresses, currentHost); // cause disconnection - this will cause next to be called // which will in turn call nextReconfigMode if (reconfigMode) { clientCnxnSocket.testableCloseSocket(); } } public ZooKeeperSaslClient getSaslClient() { return cnxn.getZooKeeperSaslClient(); } private final ZKClientConfig clientConfig; public ZKClientConfig getClientConfig() { return clientConfig; } protected List getDataWatches() { return getWatchManager().getDataWatchList(); } protected List getExistWatches() { return getWatchManager().getExistWatchList(); } protected List getChildWatches() { return getWatchManager().getChildWatchList(); } protected List getPersistentWatches() { return getWatchManager().getPersistentWatchList(); } protected List getPersistentRecursiveWatches() { return getWatchManager().getPersistentRecursiveWatchList(); } ZKWatchManager getWatchManager() { return cnxn.getWatcherManager(); } /** * Register a watcher for a particular path. */ public abstract static class WatchRegistration { private Watcher watcher; private String clientPath; public WatchRegistration(Watcher watcher, String clientPath) { this.watcher = watcher; this.clientPath = clientPath; } protected abstract Map> getWatches(int rc); /** * Register the watcher with the set of watches on path. * @param rc the result code of the operation that attempted to * add the watch on the path. */ public void register(int rc) { if (shouldAddWatch(rc)) { Map> watches = getWatches(rc); synchronized (watches) { Set watchers = watches.get(clientPath); if (watchers == null) { watchers = new HashSet<>(); watches.put(clientPath, watchers); } watchers.add(watcher); } } } /** * Determine whether the watch should be added based on return code. * @param rc the result code of the operation that attempted to add the * watch on the node * @return true if the watch should be added, otw false */ protected boolean shouldAddWatch(int rc) { return rc == KeeperException.Code.OK.intValue(); } } /** Handle the special case of exists watches - they add a watcher * even in the case where NONODE result code is returned. */ class ExistsWatchRegistration extends WatchRegistration { public ExistsWatchRegistration(Watcher watcher, String clientPath) { super(watcher, clientPath); } @Override protected Map> getWatches(int rc) { return rc == KeeperException.Code.OK.intValue() ? getWatchManager().getDataWatches() : getWatchManager().getExistWatches(); } @Override protected boolean shouldAddWatch(int rc) { return rc == KeeperException.Code.OK.intValue() || rc == KeeperException.Code.NONODE.intValue(); } } class DataWatchRegistration extends WatchRegistration { public DataWatchRegistration(Watcher watcher, String clientPath) { super(watcher, clientPath); } @Override protected Map> getWatches(int rc) { return getWatchManager().getDataWatches(); } } class ChildWatchRegistration extends WatchRegistration { public ChildWatchRegistration(Watcher watcher, String clientPath) { super(watcher, clientPath); } @Override protected Map> getWatches(int rc) { return getWatchManager().getChildWatches(); } } class AddWatchRegistration extends WatchRegistration { private final AddWatchMode mode; public AddWatchRegistration(Watcher watcher, String clientPath, AddWatchMode mode) { super(watcher, clientPath); this.mode = mode; } @Override protected Map> getWatches(int rc) { switch (mode) { case PERSISTENT: return getWatchManager().getPersistentWatches(); case PERSISTENT_RECURSIVE: return getWatchManager().getPersistentRecursiveWatches(); } throw new IllegalArgumentException("Mode not supported: " + mode); } @Override protected boolean shouldAddWatch(int rc) { return rc == KeeperException.Code.OK.intValue() || rc == KeeperException.Code.NONODE.intValue(); } } @InterfaceAudience.Public public enum States { CONNECTING, ASSOCIATING, CONNECTED, CONNECTEDREADONLY, CLOSED, AUTH_FAILED, NOT_CONNECTED; public boolean isAlive() { return this != CLOSED && this != AUTH_FAILED; } /** * Returns whether we are connected to a server (which * could possibly be read-only, if this client is allowed * to go to read-only mode) * */ public boolean isConnected() { return this == CONNECTED || this == CONNECTEDREADONLY; } } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed. *

* 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). * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified */ public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) throws IOException { this(connectString, sessionTimeout, watcher, false); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed. *

* 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). * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param conf * (added in 3.5.2) passing this conf object gives each client the flexibility of * configuring properties differently compared to other instances * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, ZKClientConfig conf) throws IOException { this(connectString, sessionTimeout, watcher, false, conf); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed. *

* 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). *

* For backward compatibility, there is another version * {@link #ZooKeeper(String, int, Watcher, boolean)} which uses * default {@link StaticHostProvider} * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @param aHostProvider * use this as HostProvider to enable custom behaviour. * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, HostProvider aHostProvider) throws IOException { this(connectString, sessionTimeout, watcher, canBeReadOnly, aHostProvider, null); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed. *

* 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). *

* For backward compatibility, there is another version * {@link #ZooKeeper(String, int, Watcher, boolean)} which uses default * {@link StaticHostProvider} * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @param hostProvider * use this as HostProvider to enable custom behaviour. * @param clientConfig * (added in 3.5.2) passing this conf object gives each client the flexibility of * configuring properties differently compared to other instances * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, HostProvider hostProvider, ZKClientConfig clientConfig ) throws IOException { LOG.info( "Initiating client connection, connectString={} sessionTimeout={} watcher={}", connectString, sessionTimeout, watcher); this.clientConfig = clientConfig != null ? clientConfig : new ZKClientConfig(); this.hostProvider = hostProvider; ConnectStringParser connectStringParser = new ConnectStringParser(connectString); cnxn = createConnection( connectStringParser.getChrootPath(), hostProvider, sessionTimeout, this.clientConfig, watcher, getClientCnxnSocket(), canBeReadOnly); cnxn.start(); } ClientCnxn createConnection( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig clientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly ) throws IOException { return new ClientCnxn( chrootPath, hostProvider, sessionTimeout, clientConfig, defaultWatcher, clientCnxnSocket, canBeReadOnly); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed. *

* 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). *

* * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws IOException { this(connectString, sessionTimeout, watcher, canBeReadOnly, createDefaultHostProvider(connectString)); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed. *

* 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). *

* * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @param conf * (added in 3.5.2) passing this conf object gives each client the flexibility of * configuring properties differently compared to other instances * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, ZKClientConfig conf) throws IOException { this( connectString, sessionTimeout, watcher, canBeReadOnly, createDefaultHostProvider(connectString), conf); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed (or the session is expired by the server). *

* 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). *

* Use {@link #getSessionId} and {@link #getSessionPasswd} on an established * client connection, these values must be passed as sessionId and * sessionPasswd respectively if reconnecting. Otherwise, if not * reconnecting, use the other constructor which does not require these * parameters. * * @param connectString * 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" * If the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param sessionId * specific session id to use if reconnecting * @param sessionPasswd * password for this session * * @throws IOException in cases of network failure * @throws IllegalArgumentException if an invalid chroot path is specified * @throws IllegalArgumentException for an invalid list of ZooKeeper hosts */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd) throws IOException { this(connectString, sessionTimeout, watcher, sessionId, sessionPasswd, false); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed (or the session is expired by the server). *

* 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). *

* Use {@link #getSessionId} and {@link #getSessionPasswd} on an established * client connection, these values must be passed as sessionId and * sessionPasswd respectively if reconnecting. Otherwise, if not * reconnecting, use the other constructor which does not require these * parameters. *

* For backward compatibility, there is another version * {@link #ZooKeeper(String, int, Watcher, long, byte[], boolean)} which uses * default {@link StaticHostProvider} * * @param connectString * 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" * If the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param sessionId * specific session id to use if reconnecting * @param sessionPasswd * password for this session * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @param aHostProvider * use this as HostProvider to enable custom behaviour. * @throws IOException in cases of network failure * @throws IllegalArgumentException if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly, HostProvider aHostProvider) throws IOException { this( connectString, sessionTimeout, watcher, sessionId, sessionPasswd, canBeReadOnly, aHostProvider, null); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed (or the session is expired by the server). *

* 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). *

* Use {@link #getSessionId} and {@link #getSessionPasswd} on an established * client connection, these values must be passed as sessionId and * sessionPasswd respectively if reconnecting. Otherwise, if not * reconnecting, use the other constructor which does not require these * parameters. *

* For backward compatibility, there is another version * {@link #ZooKeeper(String, int, Watcher, long, byte[], boolean)} which uses * default {@link StaticHostProvider} * * @param connectString * 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" * If the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param sessionId * specific session id to use if reconnecting * @param sessionPasswd * password for this session * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @param hostProvider * use this as HostProvider to enable custom behaviour. * @param clientConfig * (added in 3.5.2) passing this conf object gives each client the flexibility of * configuring properties differently compared to other instances * @throws IOException in cases of network failure * @throws IllegalArgumentException if an invalid chroot path is specified * * @since 3.5.5 */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly, HostProvider hostProvider, ZKClientConfig clientConfig) throws IOException { LOG.info( "Initiating client connection, connectString={} " + "sessionTimeout={} watcher={} sessionId=0x{} sessionPasswd={}", connectString, sessionTimeout, watcher, Long.toHexString(sessionId), (sessionPasswd == null ? "" : "")); this.clientConfig = clientConfig != null ? clientConfig : new ZKClientConfig(); ConnectStringParser connectStringParser = new ConnectStringParser(connectString); this.hostProvider = hostProvider; cnxn = new ClientCnxn( connectStringParser.getChrootPath(), hostProvider, sessionTimeout, this.clientConfig, watcher, getClientCnxnSocket(), sessionId, sessionPasswd, canBeReadOnly); cnxn.seenRwServerBefore = true; // since user has provided sessionId cnxn.start(); } /** * To create a ZooKeeper client object, the application needs to pass a * connection string containing a comma separated list of host:port pairs, * each corresponding to a ZooKeeper server. *

* Session establishment is asynchronous. This constructor will initiate * connection to the server and return immediately - potentially (usually) * before the session is fully established. The watcher argument specifies * the watcher that will be notified of any changes in state. This * notification can come at any point before or after the constructor call * has returned. *

* The instantiated ZooKeeper client object will pick an arbitrary server * from the connectString and attempt to connect to it. If establishment of * the connection fails, another server in the connect string will be tried * (the order is non-deterministic, as we random shuffle the list), until a * connection is established. The client will continue attempts until the * session is explicitly closed (or the session is expired by the server). *

* 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). *

* Use {@link #getSessionId} and {@link #getSessionPasswd} on an established * client connection, these values must be passed as sessionId and * sessionPasswd respectively if reconnecting. Otherwise, if not * reconnecting, use the other constructor which does not require these * parameters. *

* This constructor uses a StaticHostProvider; there is another one * to enable custom behaviour. * * @param connectString * 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" * If the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param sessionId * specific session id to use if reconnecting * @param sessionPasswd * password for this session * @param canBeReadOnly * (added in 3.4) whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @throws IOException in cases of network failure * @throws IllegalArgumentException if an invalid chroot path is specified */ public ZooKeeper( String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly) throws IOException { this( connectString, sessionTimeout, watcher, sessionId, sessionPasswd, canBeReadOnly, createDefaultHostProvider(connectString)); } // default hostprovider private static HostProvider createDefaultHostProvider(String connectString) { return new StaticHostProvider(new ConnectStringParser(connectString).getServerAddresses()); } // VisibleForTesting public Testable getTestable() { return new ZooKeeperTestable(cnxn); } /** * The session id for this ZooKeeper client instance. The value returned is * not valid until the client connects to a server and may change after a * re-connect. * * This method is NOT thread safe * * @return current session id */ public long getSessionId() { return cnxn.getSessionId(); } /** * The session password for this ZooKeeper client instance. The value * returned is not valid until the client connects to a server and may * change after a re-connect. * * This method is NOT thread safe * * @return current session password */ public byte[] getSessionPasswd() { return cnxn.getSessionPasswd(); } /** * The negotiated session timeout for this ZooKeeper client instance. The * value returned is not valid until the client connects to a server and * may change after a re-connect. * * This method is NOT thread safe * * @return current session timeout */ public int getSessionTimeout() { return cnxn.getSessionTimeout(); } /** * Add the specified scheme:auth information to this connection. * * This method is NOT thread safe * * @param scheme * @param auth */ public void addAuthInfo(String scheme, byte[] auth) { cnxn.addAuthInfo(scheme, auth); } /** * Specify the default watcher for the connection (overrides the one * specified during construction). */ public synchronized void register(Watcher watcher) { getWatchManager().setDefaultWatcher(watcher); } /** * Close this client object. Once the client is closed, its session becomes * invalid. All the ephemeral nodes in the ZooKeeper server associated with * the session will be removed. The watches left on those nodes (and on * their parents) will be triggered. *

* Added in 3.5.3: try-with-resources * may be used instead of calling close directly. *

*

* This method does not wait for all internal threads to exit. * Use the {@link #close(int) } method to wait for all resources to be released *

* * @throws InterruptedException */ public synchronized void close() throws InterruptedException { if (!cnxn.getState().isAlive()) { LOG.debug("Close called on already closed client"); return; } LOG.debug("Closing session: 0x" + Long.toHexString(getSessionId())); try { cnxn.close(); } catch (IOException e) { LOG.debug("Ignoring unexpected exception during close", e); } LOG.info("Session: 0x{} closed", Long.toHexString(getSessionId())); } /** * Close this client object as the {@link #close() } method. * This method will wait for internal resources to be released. * * @param waitForShutdownTimeoutMs timeout (in milliseconds) to wait for resources to be released. * Use zero or a negative value to skip the wait * @throws InterruptedException * @return true if waitForShutdownTimeout is greater than zero and all of the resources have been released * * @since 3.5.4 */ public boolean close(int waitForShutdownTimeoutMs) throws InterruptedException { close(); return testableWaitForShutdown(waitForShutdownTimeoutMs); } /** * Prepend the chroot to the client path (if present). The expectation of * this function is that the client path has been validated before this * function is called * @param clientPath path to the node * @return server view of the path (chroot prepended to client path) */ private String prependChroot(String clientPath) { if (cnxn.chrootPath != null) { // handle clientPath = "/" if (clientPath.length() == 1) { return cnxn.chrootPath; } return cnxn.chrootPath + clientPath; } else { return clientPath; } } /** * Create a node with the given path. The node data will be the given data, * and node acl will be the given acl. *

* The flags argument specifies whether the created node will be ephemeral * or not. *

* An ephemeral node will be removed by the ZooKeeper automatically when the * session associated with the creation of the node expires. *

* The flags argument can also specify to create a sequential node. The * actual path name of a sequential node will be the given path plus a * suffix "i" where i is the current sequential number of the node. The sequence * number is always fixed length of 10 digits, 0 padded. Once * such a node is created, the sequential number will be incremented by one. *

* If a node with the same actual path already exists in the ZooKeeper, a * KeeperException with error code KeeperException.NodeExists will be * thrown. Note that since a different actual path is used for each * invocation of creating sequential node with the same path argument, the * call will never throw "file exists" KeeperException. *

* If the parent node does not exist in the ZooKeeper, a KeeperException * with error code KeeperException.NoNode will be thrown. *

* An ephemeral node cannot have children. If the parent node of the given * path is ephemeral, a KeeperException with error code * KeeperException.NoChildrenForEphemerals will be thrown. *

* This operation, if successful, will trigger all the watches left on the * node of the given path by exists and getData API calls, and the watches * left on the parent node by getChildren API calls. *

* If a node is created successfully, the ZooKeeper server will trigger the * watches on the path left by exists calls, and the watches on the parent * of the node by getChildren calls. *

* The maximum allowable size of the data array is 1 MB (1,048,576 bytes). * Arrays larger than this will cause a KeeperExecption to be thrown. * * @param path * the path for the node * @param data * the initial data for the node * @param acl * the acl for the node * @param createMode * specifying whether the node to be created is ephemeral * and/or sequential * @return the actual path of the created node * @throws KeeperException if the server returns a non-zero error code * @throws KeeperException.InvalidACLException if the ACL is invalid, null, or empty * @throws InterruptedException if the transaction is interrupted * @throws IllegalArgumentException if an invalid path is specified */ public String create( final String path, byte[] data, List acl, CreateMode createMode) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath, createMode.isSequential()); EphemeralType.validateTTL(createMode, -1); validateACL(acl); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create); CreateRequest request = new CreateRequest(); CreateResponse response = new CreateResponse(); request.setData(data); request.setFlags(createMode.toFlag()); request.setPath(serverPath); request.setAcl(acl); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } if (cnxn.chrootPath == null) { return response.getPath(); } else { return response.getPath().substring(cnxn.chrootPath.length()); } } /** * Create a node with the given path and returns the Stat of that node. The * node data will be the given data and node acl will be the given acl. *

* The flags argument specifies whether the created node will be ephemeral * or not. *

* An ephemeral node will be removed by the ZooKeeper automatically when the * session associated with the creation of the node expires. *

* The flags argument can also specify to create a sequential node. The * actual path name of a sequential node will be the given path plus a * suffix "i" where i is the current sequential number of the node. The sequence * number is always fixed length of 10 digits, 0 padded. Once * such a node is created, the sequential number will be incremented by one. *

* If a node with the same actual path already exists in the ZooKeeper, a * KeeperException with error code KeeperException.NodeExists will be * thrown. Note that since a different actual path is used for each * invocation of creating sequential node with the same path argument, the * call will never throw "file exists" KeeperException. *

* If the parent node does not exist in the ZooKeeper, a KeeperException * with error code KeeperException.NoNode will be thrown. *

* An ephemeral node cannot have children. If the parent node of the given * path is ephemeral, a KeeperException with error code * KeeperException.NoChildrenForEphemerals will be thrown. *

* This operation, if successful, will trigger all the watches left on the * node of the given path by exists and getData API calls, and the watches * left on the parent node by getChildren API calls. *

* If a node is created successfully, the ZooKeeper server will trigger the * watches on the path left by exists calls, and the watches on the parent * of the node by getChildren calls. *

* The maximum allowable size of the data array is 1 MB (1,048,576 bytes). * Arrays larger than this will cause a KeeperExecption to be thrown. * * @param path * the path for the node * @param data * the initial data for the node * @param acl * the acl for the node * @param createMode * specifying whether the node to be created is ephemeral * and/or sequential * @param stat * The output Stat object. * @return the actual path of the created node * @throws KeeperException if the server returns a non-zero error code * @throws KeeperException.InvalidACLException if the ACL is invalid, null, or empty * @throws InterruptedException if the transaction is interrupted * @throws IllegalArgumentException if an invalid path is specified */ public String create( final String path, byte[] data, List acl, CreateMode createMode, Stat stat) throws KeeperException, InterruptedException { return create(path, data, acl, createMode, stat, -1); } /** * same as {@link #create(String, byte[], List, CreateMode, Stat)} but * allows for specifying a TTL when mode is {@link CreateMode#PERSISTENT_WITH_TTL} * or {@link CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If the znode has not been modified * within the given TTL, it will be deleted once it has no children. The TTL unit is * milliseconds and must be greater than 0 and less than or equal to * {@link EphemeralType#maxValue()} for {@link EphemeralType#TTL}. */ public String create( final String path, byte[] data, List acl, CreateMode createMode, Stat stat, long ttl) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath, createMode.isSequential()); EphemeralType.validateTTL(createMode, ttl); validateACL(acl); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); setCreateHeader(createMode, h); Create2Response response = new Create2Response(); Record record = makeCreateRecord(createMode, serverPath, data, acl, ttl); ReplyHeader r = cnxn.submitRequest(h, record, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } if (stat != null) { DataTree.copyStat(response.getStat(), stat); } if (cnxn.chrootPath == null) { return response.getPath(); } else { return response.getPath().substring(cnxn.chrootPath.length()); } } private void setCreateHeader(CreateMode createMode, RequestHeader h) { if (createMode.isTTL()) { h.setType(ZooDefs.OpCode.createTTL); } else { h.setType(createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create2); } } private Record makeCreateRecord(CreateMode createMode, String serverPath, byte[] data, List acl, long ttl) { Record record; if (createMode.isTTL()) { CreateTTLRequest request = new CreateTTLRequest(); request.setData(data); request.setFlags(createMode.toFlag()); request.setPath(serverPath); request.setAcl(acl); request.setTtl(ttl); record = request; } else { CreateRequest request = new CreateRequest(); request.setData(data); request.setFlags(createMode.toFlag()); request.setPath(serverPath); request.setAcl(acl); record = request; } return record; } /** * The asynchronous version of create. * * @see #create(String, byte[], List, CreateMode) */ public void create( final String path, byte[] data, List acl, CreateMode createMode, StringCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath, createMode.isSequential()); EphemeralType.validateTTL(createMode, -1); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(createMode.isContainer() ? ZooDefs.OpCode.createContainer : ZooDefs.OpCode.create); CreateRequest request = new CreateRequest(); CreateResponse response = new CreateResponse(); ReplyHeader r = new ReplyHeader(); request.setData(data); request.setFlags(createMode.toFlag()); request.setPath(serverPath); request.setAcl(acl); cnxn.queuePacket(h, r, request, response, cb, clientPath, serverPath, ctx, null); } /** * The asynchronous version of create. * * @see #create(String, byte[], List, CreateMode, Stat) */ public void create( final String path, byte[] data, List acl, CreateMode createMode, Create2Callback cb, Object ctx) { create(path, data, acl, createMode, cb, ctx, -1); } /** * The asynchronous version of create with ttl. * * @see #create(String, byte[], List, CreateMode, Stat, long) */ public void create( final String path, byte[] data, List acl, CreateMode createMode, Create2Callback cb, Object ctx, long ttl) { final String clientPath = path; PathUtils.validatePath(clientPath, createMode.isSequential()); EphemeralType.validateTTL(createMode, ttl); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); setCreateHeader(createMode, h); ReplyHeader r = new ReplyHeader(); Create2Response response = new Create2Response(); Record record = makeCreateRecord(createMode, serverPath, data, acl, ttl); cnxn.queuePacket(h, r, record, response, cb, clientPath, serverPath, ctx, null); } /** * Delete the node with the given path. The call will succeed if such a node * exists, and the given version matches the node's version (if the given * version is -1, it matches any node's versions). *

* A KeeperException with error code KeeperException.NoNode will be thrown * if the nodes does not exist. *

* A KeeperException with error code KeeperException.BadVersion will be * thrown if the given version does not match the node's version. *

* A KeeperException with error code KeeperException.NotEmpty will be thrown * if the node has children. *

* This operation, if successful, will trigger all the watches on the node * of the given path left by exists API calls, and the watches on the parent * node left by getChildren API calls. * * @param path * the path of the node to be deleted. * @param version * the expected node version. * @throws InterruptedException IF the server transaction is interrupted * @throws KeeperException If the server signals an error with a non-zero * return code. * @throws IllegalArgumentException if an invalid path is specified */ public void delete(final String path, int version) throws InterruptedException, KeeperException { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath; // maintain semantics even in chroot case // specifically - root cannot be deleted // I think this makes sense even in chroot case. if (clientPath.equals("/")) { // a bit of a hack, but delete(/) will never succeed and ensures // that the same semantics are maintained serverPath = clientPath; } else { serverPath = prependChroot(clientPath); } RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.delete); DeleteRequest request = new DeleteRequest(); request.setPath(serverPath); request.setVersion(version); ReplyHeader r = cnxn.submitRequest(h, request, null, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } } /** * Executes multiple ZooKeeper operations. In case of transactions all of them or none of them will be executed. *

* On success, a list of results is returned. * On failure, an exception is raised which contains partial results and * error details, see {@link KeeperException#getResults} *

* Note: The maximum allowable size of all of the data arrays in all of * the setData operations in this single request is typically 1 MB * (1,048,576 bytes). This limit is specified on the server via * jute.maxbuffer. * Requests larger than this will cause a KeeperException to be * thrown. * * @param ops An iterable that contains the operations to be done. * These should be created using the factory methods on {@link Op} and must be the same kind of ops. * @return A list of results, one for each input Op, the order of * which exactly matches the order of the ops input * operations. * @throws InterruptedException If the operation was interrupted. * The operation may or may not have succeeded, but will not have * partially succeeded if this exception is thrown. * @throws KeeperException If the operation could not be completed * due to some error in doing one of the specified ops. * @throws IllegalArgumentException if an invalid path is specified or different kind of ops are mixed * * @since 3.4.0 */ public List multi(Iterable ops) throws InterruptedException, KeeperException { for (Op op : ops) { op.validate(); } return multiInternal(generateMultiTransaction(ops)); } /** * The asynchronous version of multi. * * @see #multi(Iterable) */ public void multi(Iterable ops, MultiCallback cb, Object ctx) { List results = validatePath(ops); if (results.size() > 0) { cb.processResult(KeeperException.Code.BADARGUMENTS.intValue(), null, ctx, results); return; } multiInternal(generateMultiTransaction(ops), cb, ctx); } private List validatePath(Iterable ops) { List results = new ArrayList<>(); boolean error = false; for (Op op : ops) { try { op.validate(); } catch (IllegalArgumentException iae) { LOG.error("Unexpected exception", iae); ErrorResult err = new ErrorResult(KeeperException.Code.BADARGUMENTS.intValue()); results.add(err); error = true; continue; } catch (KeeperException ke) { LOG.error("Unexpected exception", ke); ErrorResult err = new ErrorResult(ke.code().intValue()); results.add(err); error = true; continue; } ErrorResult err = new ErrorResult(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); results.add(err); } if (!error) { results.clear(); } return results; } private MultiOperationRecord generateMultiTransaction(Iterable ops) { // reconstructing transaction with the chroot prefix List transaction = new ArrayList<>(); for (Op op : ops) { transaction.add(withRootPrefix(op)); } return new MultiOperationRecord(transaction); } private Op withRootPrefix(Op op) { if (null != op.getPath()) { final String serverPath = prependChroot(op.getPath()); if (!op.getPath().equals(serverPath)) { return op.withChroot(serverPath); } } return op; } protected void multiInternal( MultiOperationRecord request, MultiCallback cb, Object ctx) throws IllegalArgumentException { if (request.size() == 0) { // nothing to do, early exit cnxn.queueCallback(cb, KeeperException.Code.OK.intValue(), null, ctx); return; } RequestHeader h = new RequestHeader(); switch (request.getOpKind()) { case TRANSACTION: h.setType(ZooDefs.OpCode.multi); break; case READ: h.setType(ZooDefs.OpCode.multiRead); break; default: throw new IllegalArgumentException("Unsupported OpKind: " + request.getOpKind()); } MultiResponse response = new MultiResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, null, null, ctx, null); } protected List multiInternal( MultiOperationRecord request) throws InterruptedException, KeeperException, IllegalArgumentException { RequestHeader h = new RequestHeader(); if (request.size() == 0) { // nothing to do, early exit return Collections.emptyList(); } switch (request.getOpKind()) { case TRANSACTION: h.setType(ZooDefs.OpCode.multi); break; case READ: h.setType(ZooDefs.OpCode.multiRead); break; default: throw new IllegalArgumentException("Unsupported OpKind: " + request.getOpKind()); } MultiResponse response = new MultiResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr())); } List results = response.getResultList(); // In case of only read operations there is no need to throw an exception // as the subResults are still possibly valid. if (request.getOpKind() == Op.OpKind.READ) { return results; } ErrorResult fatalError = null; for (OpResult result : results) { if (result instanceof ErrorResult && ((ErrorResult) result).getErr() != KeeperException.Code.OK.intValue()) { fatalError = (ErrorResult) result; break; } } if (fatalError != null) { KeeperException ex = KeeperException.create(KeeperException.Code.get(fatalError.getErr())); ex.setMultiResults(results); throw ex; } return results; } /** * A Transaction is a thin wrapper on the {@link #multi} method * which provides a builder object that can be used to construct * and commit an atomic set of operations. * * @since 3.4.0 * * @return a Transaction builder object */ public Transaction transaction() { return new Transaction(this); } /** * The asynchronous version of delete. * * @see #delete(String, int) */ public void delete(final String path, int version, VoidCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath; // maintain semantics even in chroot case // specifically - root cannot be deleted // I think this makes sense even in chroot case. if (clientPath.equals("/")) { // a bit of a hack, but delete(/) will never succeed and ensures // that the same semantics are maintained serverPath = clientPath; } else { serverPath = prependChroot(clientPath); } RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.delete); DeleteRequest request = new DeleteRequest(); request.setPath(serverPath); request.setVersion(version); cnxn.queuePacket(h, new ReplyHeader(), request, null, cb, clientPath, serverPath, ctx, null); } /** * Return the stat of the node of the given path. Return null if no such a * node exists. *

* If the watch is non-null and the call is successful (no exception is thrown), * a watch will be left on the node with the given path. The watch will be * triggered by a successful operation that creates/delete the node or sets * the data on the node. * * @param path the node path * @param watcher explicit watcher * @return the stat of the node of the given path; return null if no such a * node exists. * @throws KeeperException If the server signals an error * @throws InterruptedException If the server transaction is interrupted. * @throws IllegalArgumentException if an invalid path is specified */ public Stat exists(final String path, Watcher watcher) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new ExistsWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.exists); ExistsRequest request = new ExistsRequest(); request.setPath(serverPath); request.setWatch(watcher != null); SetDataResponse response = new SetDataResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, wcb); if (r.getErr() != 0) { if (r.getErr() == KeeperException.Code.NONODE.intValue()) { return null; } throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } return response.getStat().getCzxid() == -1 ? null : response.getStat(); } /** * Return the stat of the node of the given path. Return null if no such a * node exists. * *

If the watch is true and the call is successful (no exception is thrown), * a watch will be left on the node with the given path. The watch will be * triggered by a successful operation that creates/delete the node or sets * the data on the node. * * @param path the node path * @param watch whether need to watch this node * @return the stat of the node of the given path; return null if no such a * node exists. * @throws KeeperException If the server signals an error * @throws IllegalStateException if watch this node with a null default watcher * @throws InterruptedException If the server transaction is interrupted. */ public Stat exists(String path, boolean watch) throws KeeperException, InterruptedException { return exists(path, getDefaultWatcher(watch)); } /** * The asynchronous version of exists. * * @see #exists(String, Watcher) */ public void exists(final String path, Watcher watcher, StatCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new ExistsWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.exists); ExistsRequest request = new ExistsRequest(); request.setPath(serverPath); request.setWatch(watcher != null); SetDataResponse response = new SetDataResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, wcb); } /** * The asynchronous version of exists. * * @throws IllegalStateException if watch this node with a null default watcher * * @see #exists(String, boolean) */ public void exists(String path, boolean watch, StatCallback cb, Object ctx) { exists(path, getDefaultWatcher(watch), cb, ctx); } /** * Return the data and the stat of the node of the given path. *

* If the watch is non-null and the call is successful (no exception is * thrown), a watch will be left on the node with the given path. The watch * will be triggered by a successful operation that sets data on the node, or * deletes the node. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @param path the given path * @param watcher explicit watcher * @param stat the stat of the node * @return the data of the node * @throws KeeperException If the server signals an error with a non-zero error code * @throws InterruptedException If the server transaction is interrupted. * @throws IllegalArgumentException if an invalid path is specified */ public byte[] getData(final String path, Watcher watcher, Stat stat) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new DataWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getData); GetDataRequest request = new GetDataRequest(); request.setPath(serverPath); request.setWatch(watcher != null); GetDataResponse response = new GetDataResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, wcb); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } if (stat != null) { DataTree.copyStat(response.getStat(), stat); } return response.getData(); } /** * Return the data and the stat of the node of the given path. *

* If the watch is true and the call is successful (no exception is * thrown), a watch will be left on the node with the given path. The watch * will be triggered by a successful operation that sets data on the node, or * deletes the node. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @param path the given path * @param watch whether need to watch this node * @param stat the stat of the node * @return the data of the node * @throws KeeperException If the server signals an error with a non-zero error code * @throws IllegalStateException if watch this node with a null default watcher * @throws InterruptedException If the server transaction is interrupted. */ public byte[] getData(String path, boolean watch, Stat stat) throws KeeperException, InterruptedException { return getData(path, getDefaultWatcher(watch), stat); } /** * The asynchronous version of getData. * * @see #getData(String, Watcher, Stat) */ public void getData(final String path, Watcher watcher, DataCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new DataWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getData); GetDataRequest request = new GetDataRequest(); request.setPath(serverPath); request.setWatch(watcher != null); GetDataResponse response = new GetDataResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, wcb); } /** * The asynchronous version of getData. * * @throws IllegalStateException if watch this node with a null default watcher * * @see #getData(String, boolean, Stat) */ public void getData(String path, boolean watch, DataCallback cb, Object ctx) { getData(path, getDefaultWatcher(watch), cb, ctx); } /** * Return the last committed configuration (as known to the server to which the client is connected) * and the stat of the configuration. *

* If the watch is non-null and the call is successful (no exception is * thrown), a watch will be left on the configuration node (ZooDefs.CONFIG_NODE). The watch * will be triggered by a successful reconfig operation *

* A KeeperException with error code KeeperException.NoNode will be thrown * if the configuration node doesn't exists. * * @param watcher explicit watcher * @param stat the stat of the configuration node ZooDefs.CONFIG_NODE * @return configuration data stored in ZooDefs.CONFIG_NODE * @throws KeeperException If the server signals an error with a non-zero error code * @throws InterruptedException If the server transaction is interrupted. */ public byte[] getConfig(Watcher watcher, Stat stat) throws KeeperException, InterruptedException { final String configZnode = ZooDefs.CONFIG_NODE; // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new DataWatchRegistration(watcher, configZnode); } RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getData); GetDataRequest request = new GetDataRequest(); request.setPath(configZnode); request.setWatch(watcher != null); GetDataResponse response = new GetDataResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, wcb); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), configZnode); } if (stat != null) { DataTree.copyStat(response.getStat(), stat); } return response.getData(); } /** * The asynchronous version of getConfig. * * @see #getConfig(Watcher, Stat) */ public void getConfig(Watcher watcher, DataCallback cb, Object ctx) { final String configZnode = ZooDefs.CONFIG_NODE; // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new DataWatchRegistration(watcher, configZnode); } RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getData); GetDataRequest request = new GetDataRequest(); request.setPath(configZnode); request.setWatch(watcher != null); GetDataResponse response = new GetDataResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, configZnode, configZnode, ctx, wcb); } /** * Return the last committed configuration (as known to the server to which the client is connected) * and the stat of the configuration. *

* If the watch is true and the call is successful (no exception is * thrown), a watch will be left on the configuration node (ZooDefs.CONFIG_NODE). The watch * will be triggered by a successful reconfig operation *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @param watch whether need to watch this node * @param stat the stat of the configuration node ZooDefs.CONFIG_NODE * @return configuration data stored in ZooDefs.CONFIG_NODE * @throws KeeperException If the server signals an error with a non-zero error code * @throws IllegalStateException if watch this node with a null default watcher * @throws InterruptedException If the server transaction is interrupted. */ public byte[] getConfig(boolean watch, Stat stat) throws KeeperException, InterruptedException { return getConfig(getDefaultWatcher(watch), stat); } /** * The Asynchronous version of getConfig. * * @throws IllegalStateException if watch this node with a null default watcher * * @see #getData(String, boolean, Stat) */ public void getConfig(boolean watch, DataCallback cb, Object ctx) { getConfig(getDefaultWatcher(watch), cb, ctx); } /** * Set the data for the node of the given path if such a node exists and the * given version matches the version of the node (if the given version is * -1, it matches any node's versions). Return the stat of the node. *

* This operation, if successful, will trigger all the watches on the node * of the given path left by getData calls. *

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

* A KeeperException with error code KeeperException.BadVersion will be * thrown if the given version does not match the node's version. *

* The maximum allowable size of the data array is 1 MB (1,048,576 bytes). * Arrays larger than this will cause a KeeperException to be thrown. * * @param path * the path of the node * @param data * the data to set * @param version * the expected matching version * @return the state of the node * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. * @throws IllegalArgumentException if an invalid path is specified */ public Stat setData(final String path, byte[] data, int version) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.setData); SetDataRequest request = new SetDataRequest(); request.setPath(serverPath); request.setData(data); request.setVersion(version); SetDataResponse response = new SetDataResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } return response.getStat(); } /** * The asynchronous version of setData. * * @see #setData(String, byte[], int) */ public void setData(final String path, byte[] data, int version, StatCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.setData); SetDataRequest request = new SetDataRequest(); request.setPath(serverPath); request.setData(data); request.setVersion(version); SetDataResponse response = new SetDataResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, null); } /** * Return the ACL and stat of the node of the given path. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @param path * the given path for the node * @param stat * the stat of the node will be copied to this parameter if * not null. * @return the ACL array of the given node. * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. * @throws IllegalArgumentException if an invalid path is specified */ public List getACL(final String path, Stat stat) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getACL); GetACLRequest request = new GetACLRequest(); request.setPath(serverPath); GetACLResponse response = new GetACLResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } if (stat != null) { DataTree.copyStat(response.getStat(), stat); } return response.getAcl(); } /** * The asynchronous version of getACL. * * @see #getACL(String, Stat) */ public void getACL(final String path, Stat stat, ACLCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getACL); GetACLRequest request = new GetACLRequest(); request.setPath(serverPath); GetACLResponse response = new GetACLResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, null); } /** * Set the ACL for the node of the given path if such a node exists and the * given aclVersion matches the acl version of the node. Return the stat of the * node. *

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

* A KeeperException with error code KeeperException.BadVersion will be * thrown if the given aclVersion does not match the node's aclVersion. * * @param path the given path for the node * @param acl the given acl for the node * @param aclVersion the given acl version of the node * @return the stat of the node. * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. * @throws org.apache.zookeeper.KeeperException.InvalidACLException If the acl is invalide. * @throws IllegalArgumentException if an invalid path is specified */ public Stat setACL(final String path, List acl, int aclVersion) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); validateACL(acl); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.setACL); SetACLRequest request = new SetACLRequest(); request.setPath(serverPath); request.setAcl(acl); request.setVersion(aclVersion); SetACLResponse response = new SetACLResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } return response.getStat(); } /** * The asynchronous version of setACL. * * @see #setACL(String, List, int) */ public void setACL(final String path, List acl, int version, StatCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.setACL); SetACLRequest request = new SetACLRequest(); request.setPath(serverPath); request.setAcl(acl); request.setVersion(version); SetACLResponse response = new SetACLResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, null); } /** * Return the list of the children of the node of the given path. *

* If the watch is non-null and the call is successful (no exception is thrown), * a watch will be left on the node with the given path. The watch will be * triggered by a successful operation that deletes the node of the given * path or creates/delete a child under the node. *

* The list of children returned is not sorted and no guarantee is provided * as to its natural or lexical order. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @param path * @param watcher explicit watcher * @return an unordered array of children of the node with the given path * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. * @throws IllegalArgumentException if an invalid path is specified */ public List getChildren(final String path, Watcher watcher) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new ChildWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getChildren); GetChildrenRequest request = new GetChildrenRequest(); request.setPath(serverPath); request.setWatch(watcher != null); GetChildrenResponse response = new GetChildrenResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, wcb); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } return response.getChildren(); } /** * Return the list of the children of the node of the given path. *

* If the watch is true and the call is successful (no exception is thrown), * a watch will be left on the node with the given path. The watch will be * triggered by a successful operation that deletes the node of the given * path or creates/delete a child under the node. *

* The list of children returned is not sorted and no guarantee is provided * as to its natural or lexical order. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @param path the node path * @param watch whether need to watch this node * @return an unordered array of children of the node with the given path * @throws IllegalStateException if watch this node with a null default watcher * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. */ public List getChildren(String path, boolean watch) throws KeeperException, InterruptedException { return getChildren(path, getDefaultWatcher(watch)); } /** * The asynchronous version of getChildren. * * @see #getChildren(String, Watcher) */ public void getChildren(final String path, Watcher watcher, ChildrenCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new ChildWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getChildren); GetChildrenRequest request = new GetChildrenRequest(); request.setPath(serverPath); request.setWatch(watcher != null); GetChildrenResponse response = new GetChildrenResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, wcb); } /** * The asynchronous version of getChildren. * * @throws IllegalStateException if watch this node with a null default watcher * * @see #getChildren(String, boolean) */ public void getChildren(String path, boolean watch, ChildrenCallback cb, Object ctx) { getChildren(path, getDefaultWatcher(watch), cb, ctx); } /** * For the given znode path return the stat and children list. *

* If the watch is non-null and the call is successful (no exception is thrown), * a watch will be left on the node with the given path. The watch will be * triggered by a successful operation that deletes the node of the given * path or creates/delete a child under the node. *

* The list of children returned is not sorted and no guarantee is provided * as to its natural or lexical order. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @since 3.3.0 * * @param path * @param watcher explicit watcher * @param stat stat of the znode designated by path * @return an unordered array of children of the node with the given path * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. * @throws IllegalArgumentException if an invalid path is specified */ public List getChildren( final String path, Watcher watcher, Stat stat) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new ChildWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getChildren2); GetChildren2Request request = new GetChildren2Request(); request.setPath(serverPath); request.setWatch(watcher != null); GetChildren2Response response = new GetChildren2Response(); ReplyHeader r = cnxn.submitRequest(h, request, response, wcb); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } if (stat != null) { DataTree.copyStat(response.getStat(), stat); } return response.getChildren(); } /** * For the given znode path return the stat and children list. *

* If the watch is true and the call is successful (no exception is thrown), * a watch will be left on the node with the given path. The watch will be * triggered by a successful operation that deletes the node of the given * path or creates/delete a child under the node. *

* The list of children returned is not sorted and no guarantee is provided * as to its natural or lexical order. *

* A KeeperException with error code KeeperException.NoNode will be thrown * if no node with the given path exists. * * @since 3.3.0 * * @param path the node path * @param watch whether need to watch this node * @param stat stat of the znode designated by path * @return an unordered array of children of the node with the given path * @throws IllegalStateException if watch this node with a null default watcher * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero * error code. */ public List getChildren( String path, boolean watch, Stat stat) throws KeeperException, InterruptedException { return getChildren(path, getDefaultWatcher(watch), stat); } /** * The asynchronous version of getChildren. * * @since 3.3.0 * * @see #getChildren(String, Watcher, Stat) */ public void getChildren(final String path, Watcher watcher, Children2Callback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); // the watch contains the un-chroot path WatchRegistration wcb = null; if (watcher != null) { wcb = new ChildWatchRegistration(watcher, clientPath); } final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getChildren2); GetChildren2Request request = new GetChildren2Request(); request.setPath(serverPath); request.setWatch(watcher != null); GetChildren2Response response = new GetChildren2Response(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, wcb); } /** * The asynchronous version of getChildren. * * @since 3.3.0 * * @throws IllegalStateException if watch this node with a null default watcher * * @see #getChildren(String, boolean, Stat) */ public void getChildren(String path, boolean watch, Children2Callback cb, Object ctx) { getChildren(path, getDefaultWatcher(watch), cb, ctx); } /** * Synchronously gets all numbers of children nodes under a specific path * * @since 3.6.0 * @param path * @return Children nodes count under path * @throws KeeperException * @throws InterruptedException */ public int getAllChildrenNumber(final String path) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getAllChildrenNumber); GetAllChildrenNumberRequest request = new GetAllChildrenNumberRequest(serverPath); GetAllChildrenNumberResponse response = new GetAllChildrenNumberResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } return response.getTotalNumber(); } /** * Asynchronously gets all numbers of children nodes under a specific path * * @since 3.6.0 * @param path */ public void getAllChildrenNumber(final String path, AsyncCallback.AllChildrenNumberCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getAllChildrenNumber); GetAllChildrenNumberRequest request = new GetAllChildrenNumberRequest(serverPath); GetAllChildrenNumberResponse response = new GetAllChildrenNumberResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, null); } /** * Synchronously gets all the ephemeral nodes created by this session. * * @since 3.6.0 * */ public List getEphemerals() throws KeeperException, InterruptedException { return getEphemerals("/"); } /** * Synchronously gets all the ephemeral nodes matching prefixPath * created by this session. If prefixPath is "/" then it returns all * ephemerals * * @since 3.6.0 * */ public List getEphemerals(String prefixPath) throws KeeperException, InterruptedException { PathUtils.validatePath(prefixPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getEphemerals); GetEphemeralsRequest request = new GetEphemeralsRequest(prefixPath); GetEphemeralsResponse response = new GetEphemeralsResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr())); } return response.getEphemerals(); } /** * Asynchronously gets all the ephemeral nodes matching prefixPath * created by this session. If prefixPath is "/" then it returns all * ephemerals * * @since 3.6.0 * */ public void getEphemerals(String prefixPath, AsyncCallback.EphemeralsCallback cb, Object ctx) { PathUtils.validatePath(prefixPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.getEphemerals); GetEphemeralsRequest request = new GetEphemeralsRequest(prefixPath); GetEphemeralsResponse response = new GetEphemeralsResponse(); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, null, null, ctx, null); } /** * Asynchronously gets all the ephemeral nodes created by this session. * ephemerals * * @since 3.6.0 * */ public void getEphemerals(AsyncCallback.EphemeralsCallback cb, Object ctx) { getEphemerals("/", cb, ctx); } /** * Synchronous sync. Flushes channel between process and leader. * * @param path the given path * @throws KeeperException If the server signals an error with a non-zero error code * @throws InterruptedException If the server transaction is interrupted. * @throws IllegalArgumentException if an invalid path is specified */ public void sync(final String path) throws KeeperException, InterruptedException { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.sync); SyncRequest request = new SyncRequest(); SyncResponse response = new SyncResponse(); request.setPath(serverPath); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } } /** * Asynchronous sync. Flushes channel between process and leader. * @param path * @param cb a handler for the callback * @param ctx context to be provided to the callback * @throws IllegalArgumentException if an invalid path is specified */ public void sync(final String path, VoidCallback cb, Object ctx) { final String clientPath = path; PathUtils.validatePath(clientPath); final String serverPath = prependChroot(clientPath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.sync); SyncRequest request = new SyncRequest(); SyncResponse response = new SyncResponse(); request.setPath(serverPath); cnxn.queuePacket(h, new ReplyHeader(), request, response, cb, clientPath, serverPath, ctx, null); } /** * For the given znode path, removes the specified watcher of given * watcherType. * *

* Watcher shouldn't be null. A successful call guarantees that, the * removed watcher won't be triggered. *

* * @param path * - the path of the node * @param watcher * - a concrete watcher * @param watcherType * - the type of watcher to be removed * @param local * - whether the watcher can be removed locally when there is no * server connection * @throws InterruptedException * if the server transaction is interrupted. * @throws KeeperException.NoWatcherException * if no watcher exists that match the specified parameters * @throws KeeperException * if the server signals an error with a non-zero error code. * @throws IllegalArgumentException * if any of the following is true: *
    *
  • {@code path} is invalid *
  • {@code watcher} is null *
* * @since 3.5.0 */ public void removeWatches( String path, Watcher watcher, WatcherType watcherType, boolean local) throws InterruptedException, KeeperException { validateWatcher(watcher); removeWatches(ZooDefs.OpCode.checkWatches, path, watcher, watcherType, local); } /** * The asynchronous version of removeWatches. * * @see #removeWatches */ public void removeWatches( String path, Watcher watcher, WatcherType watcherType, boolean local, VoidCallback cb, Object ctx) { validateWatcher(watcher); removeWatches(ZooDefs.OpCode.checkWatches, path, watcher, watcherType, local, cb, ctx); } /** * For the given znode path, removes all the registered watchers of given * watcherType. * *

* A successful call guarantees that, the removed watchers won't be * triggered. *

* * @param path * - the path of the node * @param watcherType * - the type of watcher to be removed * @param local * - whether watches can be removed locally when there is no * server connection * @throws InterruptedException * if the server transaction is interrupted. * @throws KeeperException.NoWatcherException * if no watcher exists that match the specified parameters * @throws KeeperException * if the server signals an error with a non-zero error code. * @throws IllegalArgumentException * if an invalid {@code path} is specified * * @since 3.5.0 */ public void removeAllWatches( String path, WatcherType watcherType, boolean local) throws InterruptedException, KeeperException { removeWatches(ZooDefs.OpCode.removeWatches, path, null, watcherType, local); } /** * The asynchronous version of removeAllWatches. * * @see #removeAllWatches */ public void removeAllWatches(String path, WatcherType watcherType, boolean local, VoidCallback cb, Object ctx) { removeWatches(ZooDefs.OpCode.removeWatches, path, null, watcherType, local, cb, ctx); } /** * Add a watch to the given znode using the given mode. Note: not all * watch types can be set with this method. Only the modes available * in {@link AddWatchMode} can be set with this method. * * @param basePath the path that the watcher applies to * @param watcher the watcher * @param mode type of watcher to add * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero * error code. * @since 3.6.0 */ public void addWatch(String basePath, Watcher watcher, AddWatchMode mode) throws KeeperException, InterruptedException { PathUtils.validatePath(basePath); validateWatcher(watcher); String serverPath = prependChroot(basePath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.addWatch); AddWatchRequest request = new AddWatchRequest(serverPath, mode.getMode()); ReplyHeader r = cnxn.submitRequest(h, request, new ErrorResponse(), new AddWatchRegistration(watcher, basePath, mode)); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), basePath); } } /** * Add a watch to the given znode using the given mode. Note: not all * watch types can be set with this method. Only the modes available * in {@link AddWatchMode} can be set with this method. In this version of the method, * the default watcher is used * * @param basePath the path that the watcher applies to * @param mode type of watcher to add * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero * error code. * @since 3.6.0 */ public void addWatch( String basePath, AddWatchMode mode ) throws KeeperException, InterruptedException { addWatch(basePath, getWatchManager().getDefaultWatcher(), mode); } /** * Async version of {@link #addWatch(String, Watcher, AddWatchMode)} (see it for details) * * @param basePath the path that the watcher applies to * @param watcher the watcher * @param mode type of watcher to add * @param cb a handler for the callback * @param ctx context to be provided to the callback * @throws IllegalArgumentException if an invalid path is specified * @since 3.6.0 */ public void addWatch( String basePath, Watcher watcher, AddWatchMode mode, VoidCallback cb, Object ctx ) { PathUtils.validatePath(basePath); validateWatcher(watcher); String serverPath = prependChroot(basePath); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.addWatch); AddWatchRequest request = new AddWatchRequest(serverPath, mode.getMode()); cnxn.queuePacket(h, new ReplyHeader(), request, new ErrorResponse(), cb, basePath, serverPath, ctx, new AddWatchRegistration(watcher, basePath, mode)); } /** * Async version of {@link #addWatch(String, AddWatchMode)} (see it for details) * * @param basePath the path that the watcher applies to * @param mode type of watcher to add * @param cb a handler for the callback * @param ctx context to be provided to the callback * @throws IllegalArgumentException if an invalid path is specified * @since 3.6.0 */ public void addWatch(String basePath, AddWatchMode mode, VoidCallback cb, Object ctx) { addWatch(basePath, getWatchManager().getDefaultWatcher(), mode, cb, ctx); } private void validateWatcher(Watcher watcher) { if (watcher == null) { throw new IllegalArgumentException("Invalid Watcher, shouldn't be null!"); } } private void removeWatches( int opCode, String path, Watcher watcher, WatcherType watcherType, boolean local) throws InterruptedException, KeeperException { PathUtils.validatePath(path); final String clientPath = path; final String serverPath = prependChroot(clientPath); WatchDeregistration wcb = new WatchDeregistration(clientPath, watcher, watcherType, local, getWatchManager()); RequestHeader h = new RequestHeader(); h.setType(opCode); Record request = getRemoveWatchesRequest(opCode, watcherType, serverPath); ReplyHeader r = cnxn.submitRequest(h, request, null, null, wcb); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), clientPath); } } private void removeWatches( int opCode, String path, Watcher watcher, WatcherType watcherType, boolean local, VoidCallback cb, Object ctx) { PathUtils.validatePath(path); final String clientPath = path; final String serverPath = prependChroot(clientPath); WatchDeregistration wcb = new WatchDeregistration(clientPath, watcher, watcherType, local, getWatchManager()); RequestHeader h = new RequestHeader(); h.setType(opCode); Record request = getRemoveWatchesRequest(opCode, watcherType, serverPath); cnxn.queuePacket(h, new ReplyHeader(), request, null, cb, clientPath, serverPath, ctx, null, wcb); } private Record getRemoveWatchesRequest(int opCode, WatcherType watcherType, final String serverPath) { Record request = null; switch (opCode) { case ZooDefs.OpCode.checkWatches: CheckWatchesRequest chkReq = new CheckWatchesRequest(); chkReq.setPath(serverPath); chkReq.setType(watcherType.getIntValue()); request = chkReq; break; case ZooDefs.OpCode.removeWatches: RemoveWatchesRequest rmReq = new RemoveWatchesRequest(); rmReq.setPath(serverPath); rmReq.setType(watcherType.getIntValue()); request = rmReq; break; default: LOG.warn("unknown type " + opCode); break; } return request; } public States getState() { return cnxn.getState(); } /** * String representation of this ZooKeeper client. Suitable for things * like logging. * * Do NOT count on the format of this string, it may change without * warning. * * @since 3.3.0 */ @Override public String toString() { States state = getState(); return ("State:" + state.toString() + (state.isConnected() ? " Timeout:" + getSessionTimeout() + " " : " ") + cnxn); } /* * Methods to aid in testing follow. * * THESE METHODS ARE EXPECTED TO BE USED FOR TESTING ONLY!!! */ /** * Wait up to wait milliseconds for the underlying threads to shutdown. * THIS METHOD IS EXPECTED TO BE USED FOR TESTING ONLY!!! * * @since 3.3.0 * * @param wait max wait in milliseconds * @return true iff all threads are shutdown, otw false */ protected boolean testableWaitForShutdown(int wait) throws InterruptedException { cnxn.sendThread.join(wait); if (cnxn.sendThread.isAlive()) { return false; } cnxn.eventThread.join(wait); return !cnxn.eventThread.isAlive(); } /** * Returns the address to which the socket is connected. Useful for testing * against an ensemble - test client may need to know which server * to shutdown if interested in verifying that the code handles * disconnection/reconnection correctly. * THIS METHOD IS EXPECTED TO BE USED FOR TESTING ONLY!!! * * @since 3.3.0 * * @return ip address of the remote side of the connection or null if * not connected */ protected SocketAddress testableRemoteSocketAddress() { return cnxn.sendThread.getClientCnxnSocket().getRemoteSocketAddress(); } /** * Returns the local address to which the socket is bound. * THIS METHOD IS EXPECTED TO BE USED FOR TESTING ONLY!!! * * @since 3.3.0 * * @return ip address of the remote side of the connection or null if * not connected */ protected SocketAddress testableLocalSocketAddress() { return cnxn.sendThread.getClientCnxnSocket().getLocalSocketAddress(); } private ClientCnxnSocket getClientCnxnSocket() throws IOException { String clientCnxnSocketName = getClientConfig().getProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); if (clientCnxnSocketName == null || clientCnxnSocketName.equals(ClientCnxnSocketNIO.class.getSimpleName())) { clientCnxnSocketName = ClientCnxnSocketNIO.class.getName(); } else if (clientCnxnSocketName.equals(ClientCnxnSocketNetty.class.getSimpleName())) { clientCnxnSocketName = ClientCnxnSocketNetty.class.getName(); } try { Constructor clientCxnConstructor = Class.forName(clientCnxnSocketName) .getDeclaredConstructor(ZKClientConfig.class); ClientCnxnSocket clientCxnSocket = (ClientCnxnSocket) clientCxnConstructor.newInstance(getClientConfig()); return clientCxnSocket; } catch (Exception e) { throw new IOException("Couldn't instantiate " + clientCnxnSocketName, e); } } /** * Return the default watcher of this instance if required. * * @param required if the default watcher required * @return the default watcher if required, otherwise {@code null}. * @throws IllegalStateException if a null default watcher is required */ private Watcher getDefaultWatcher(boolean required) { if (required) { final Watcher defaultWatcher = getWatchManager().getDefaultWatcher(); if (defaultWatcher != null) { return defaultWatcher; } else { throw new IllegalStateException("Default watcher is required, but it is null."); } } return null; } /** * Validates the provided ACL list for null, empty or null value in it. * * @param acl * ACL list * @throws KeeperException.InvalidACLException * if ACL list is not valid */ private void validateACL(List acl) throws KeeperException.InvalidACLException { if (acl == null || acl.isEmpty() || acl.contains(null)) { throw new KeeperException.InvalidACLException(); } } /** * Gives all authentication information added into the current session. * * @return list of authentication info * @throws InterruptedException when interrupted */ public synchronized List whoAmI() throws InterruptedException { RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.whoAmI); WhoAmIResponse response = new WhoAmIResponse(); cnxn.submitRequest(h, null, response, null); return response.getClientInfo(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperMain.java0100644 0000000 0000000 00000036633 15051152474 032660 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.cli.CliCommand; import org.apache.zookeeper.cli.CliException; import org.apache.zookeeper.cli.CommandFactory; import org.apache.zookeeper.cli.CommandNotFoundException; import org.apache.zookeeper.cli.MalformedCommandException; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The command line client to ZooKeeper. * */ @InterfaceAudience.Public public class ZooKeeperMain { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperMain.class); static final Map commandMap = new HashMap<>(); static final Map commandMapCli = new HashMap<>(); protected MyCommandOptions cl = new MyCommandOptions(); protected HashMap history = new HashMap<>(); protected int commandCount = 0; protected boolean printWatches = true; protected int exitCode = ExitCode.EXECUTION_FINISHED.getValue(); protected ZooKeeper zk; protected String host = ""; private CountDownLatch connectLatch = null; public boolean getPrintWatches() { return printWatches; } static { commandMap.put("connect", "host:port"); commandMap.put("history", ""); commandMap.put("redo", "cmdno"); commandMap.put("printwatches", "on|off"); commandMap.put("quit", ""); Stream.of(CommandFactory.Command.values()) .map(command -> CommandFactory.getInstance(command)) // add all commands to commandMapCli and commandMap .forEach(cliCommand ->{ cliCommand.addToMap(commandMapCli); commandMap.put( cliCommand.getCmdStr(), cliCommand.getOptionStr()); }); } static void usage() { System.err.println("ZooKeeper -server host:port -client-configuration properties-file cmd args"); List cmdList = new ArrayList<>(commandMap.keySet()); Collections.sort(cmdList); for (String cmd : cmdList) { System.err.println("\t" + cmd + " " + commandMap.get(cmd)); } } private class MyWatcher implements Watcher { public void process(WatchedEvent event) { if (getPrintWatches()) { ZooKeeperMain.printMessage("WATCHER::"); ZooKeeperMain.printMessage(event.toString()); } if (connectLatch != null) { // connection success if (event.getType() == Event.EventType.None && event.getState() == Event.KeeperState.SyncConnected) { connectLatch.countDown(); } } } } /** * A storage class for both command line options and shell commands. * */ static class MyCommandOptions { private Map options = new HashMap<>(); private List cmdArgs = null; private String command = null; public static final Pattern ARGS_PATTERN = Pattern.compile("\\s*([^\"\']\\S*|\"[^\"]*\"|'[^']*')\\s*"); public static final Pattern QUOTED_PATTERN = Pattern.compile("^([\'\"])(.*)(\\1)$"); public MyCommandOptions() { options.put("server", "localhost:2181"); options.put("timeout", "30000"); } public String getOption(String opt) { return options.get(opt); } public String getCommand() { return command; } public String getCmdArgument(int index) { return cmdArgs.get(index); } public int getNumArguments() { return cmdArgs.size(); } public String[] getArgArray() { return cmdArgs.toArray(new String[0]); } /** * Parses a command line that may contain one or more flags * before an optional command string * @param args command line arguments * @return true if parsing succeeded, false otherwise. */ public boolean parseOptions(String[] args) { List argList = Arrays.asList(args); Iterator it = argList.iterator(); while (it.hasNext()) { String opt = it.next(); try { if (opt.equals("-server")) { options.put("server", it.next()); } else if (opt.equals("-timeout")) { options.put("timeout", it.next()); } else if (opt.equals("-r")) { options.put("readonly", "true"); } else if (opt.equals("-client-configuration")) { options.put("client-configuration", it.next()); } else if (opt.equals("-waitforconnection")) { options.put("waitforconnection", "true"); } } catch (NoSuchElementException e) { System.err.println("Error: no argument found for option " + opt); return false; } if (!opt.startsWith("-")) { command = opt; cmdArgs = new ArrayList<>(); cmdArgs.add(command); while (it.hasNext()) { cmdArgs.add(it.next()); } return true; } } return true; } /** * Breaks a string into command + arguments. * @param cmdstring string of form "cmd arg1 arg2..etc" * @return true if parsing succeeded. */ public boolean parseCommand(String cmdstring) { Matcher matcher = ARGS_PATTERN.matcher(cmdstring); List args = new LinkedList<>(); while (matcher.find()) { String value = matcher.group(1); if (QUOTED_PATTERN.matcher(value).matches()) { // Strip off the surrounding quotes value = value.substring(1, value.length() - 1); } args.add(value); } if (args.isEmpty()) { return false; } command = args.get(0); cmdArgs = args; return true; } } /** * Makes a list of possible completions, either for commands * or for zk nodes if the token to complete begins with / * */ protected void addToHistory(int i, String cmd) { history.put(i, cmd); } public static List getCommands() { List cmdList = new ArrayList<>(commandMap.keySet()); Collections.sort(cmdList); return cmdList; } protected String getPrompt() { return "[zk: " + host + "(" + zk.getState() + ")" + " " + commandCount + "] "; } public static void printMessage(String msg) { System.out.println("\n" + msg); } protected void connectToZK(String newHost) throws InterruptedException, IOException { if (zk != null && zk.getState().isAlive()) { zk.close(); } host = newHost; boolean readOnly = cl.getOption("readonly") != null; if (cl.getOption("secure") != null) { System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); System.out.println("Secure connection is enabled"); } ZKClientConfig clientConfig = null; if (cl.getOption("client-configuration") != null) { try { clientConfig = new ZKClientConfig(cl.getOption("client-configuration")); } catch (QuorumPeerConfig.ConfigException e) { e.printStackTrace(); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } } if (cl.getOption("waitforconnection") != null) { connectLatch = new CountDownLatch(1); } int timeout = Integer.parseInt(cl.getOption("timeout")); zk = new ZooKeeperAdmin(host, timeout, new MyWatcher(), readOnly, clientConfig); if (connectLatch != null) { if (!connectLatch.await(timeout, TimeUnit.MILLISECONDS)) { zk.close(); throw new IOException(KeeperException.create(KeeperException.Code.CONNECTIONLOSS)); } } } public static void main(String[] args) throws IOException, InterruptedException { ZooKeeperMain main = new ZooKeeperMain(args); main.run(); } public ZooKeeperMain(String[] args) throws IOException, InterruptedException { cl.parseOptions(args); System.out.println("Connecting to " + cl.getOption("server")); connectToZK(cl.getOption("server")); } public ZooKeeperMain(ZooKeeper zk) { this.zk = zk; } void run() throws IOException, InterruptedException { if (cl.getCommand() == null) { System.out.println("Welcome to ZooKeeper!"); boolean jlinemissing = false; // only use jline if it's in the classpath try { Class consoleC = Class.forName("jline.console.ConsoleReader"); Class completorC = Class.forName("org.apache.zookeeper.JLineZNodeCompleter"); System.out.println("JLine support is enabled"); Object console = consoleC.getConstructor().newInstance(); Object completor = completorC.getConstructor(ZooKeeper.class).newInstance(zk); Method addCompletor = consoleC.getMethod("addCompleter", Class.forName("jline.console.completer.Completer")); addCompletor.invoke(console, completor); String line; Method readLine = consoleC.getMethod("readLine", String.class); while ((line = (String) readLine.invoke(console, getPrompt())) != null) { executeLine(line); } } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e ) { LOG.debug("Unable to start jline", e); jlinemissing = true; } if (jlinemissing) { System.out.println("JLine support is disabled"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line; while ((line = br.readLine()) != null) { executeLine(line); } } } else { // Command line args non-null. Run what was passed. processCmd(cl); } ServiceUtils.requestSystemExit(exitCode); } public void executeLine(String line) throws InterruptedException, IOException { if (!line.equals("")) { cl.parseCommand(line); addToHistory(commandCount, line); processCmd(cl); commandCount++; } } protected boolean processCmd(MyCommandOptions co) throws IOException, InterruptedException { boolean watch = false; try { watch = processZKCmd(co); exitCode = ExitCode.EXECUTION_FINISHED.getValue(); } catch (CliException ex) { exitCode = ex.getExitCode(); System.err.println(ex.getMessage()); } return watch; } protected boolean processZKCmd(MyCommandOptions co) throws CliException, IOException, InterruptedException { String[] args = co.getArgArray(); String cmd = co.getCommand(); if (args.length < 1) { usage(); throw new MalformedCommandException("No command entered"); } if (!commandMap.containsKey(cmd)) { usage(); throw new CommandNotFoundException("Command not found " + cmd); } boolean watch = false; LOG.debug("Processing {}", cmd); if (cmd.equals("quit")) { zk.close(); ServiceUtils.requestSystemExit(exitCode); } else if (cmd.equals("redo") && args.length >= 2) { Integer i = Integer.decode(args[1]); if (commandCount <= i || i < 0) { // don't allow redoing this redo throw new MalformedCommandException("Command index out of range"); } cl.parseCommand(history.get(i)); if (cl.getCommand().equals("redo")) { throw new MalformedCommandException("No redoing redos"); } history.put(commandCount, history.get(i)); processCmd(cl); } else if (cmd.equals("history")) { for (int i = commandCount - 10; i <= commandCount; ++i) { if (i < 0) { continue; } System.out.println(i + " - " + history.get(i)); } } else if (cmd.equals("printwatches")) { if (args.length == 1) { System.out.println("printwatches is " + (printWatches ? "on" : "off")); } else { printWatches = args[1].equals("on"); } } else if (cmd.equals("connect")) { if (args.length >= 2) { connectToZK(args[1]); } else { connectToZK(host); } } // Below commands all need a live connection if (zk == null || !zk.getState().isAlive()) { System.out.println("Not connected"); return false; } // execute from commandMap CliCommand cliCmd = commandMapCli.get(cmd); if (cliCmd != null) { cliCmd.setZk(zk); watch = cliCmd.parse(args).exec(); } else if (!commandMap.containsKey(cmd)) { usage(); } return watch; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZooKeeperTestable.java0100644 0000000 0000000 00000003644 15051152474 033533 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class ZooKeeperTestable implements Testable { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperTestable.class); private final ClientCnxn clientCnxn; ZooKeeperTestable(ClientCnxn clientCnxn) { this.clientCnxn = clientCnxn; } @Override public void injectSessionExpiration() { LOG.info("injectSessionExpiration() called"); clientCnxn.eventThread.queueEvent(new WatchedEvent(Watcher.Event.EventType.None, Watcher.Event.KeeperState.Expired, null)); clientCnxn.eventThread.queueEventOfDeath(); clientCnxn.state = ZooKeeper.States.CLOSED; clientCnxn.sendThread.getClientCnxnSocket().onClosing(); } @Override public void queueEvent(WatchedEvent event) { LOG.info("queueEvent() called: {}", event); clientCnxn.eventThread.queueEvent(event); } @Override public void closeSocket() throws IOException { LOG.info("closeSocket() called"); clientCnxn.sendThread.testableCloseSocket(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/ZookeeperBanner.java0100644 0000000 0000000 00000003460 15051152474 033231 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import org.slf4j.Logger; /** * ZookeeperBanner which writes the 'Zookeeper' banner at the start of zk server. * */ public class ZookeeperBanner { private static final String[] BANNER = { "", " ______ _ ", " |___ / | | ", " / / ___ ___ | | __ ___ ___ _ __ ___ _ __ ", " / / / _ \\ / _ \\ | |/ / / _ \\ / _ \\ | '_ \\ / _ \\ | '__|", " / /__ | (_) | | (_) | | < | __/ | __/ | |_) | | __/ | | ", " /_____| \\___/ \\___/ |_|\\_\\ \\___| \\___| | .__/ \\___| |_|", " | | ", " |_| ", ""}; public static void printBanner(Logger log) { for (String line : BANNER) { log.info(line); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_admin_Zoo0100644 0000000 0000000 00000000156 15051152474 032625 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/admin/ZooKeeperAdmin.java0100644 0000000 0000000 00000032675 15051152474 034116 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.IOException; import java.util.List; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetDataResponse; import org.apache.zookeeper.proto.ReconfigRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.server.DataTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the main class for ZooKeeperAdmin client library. * This library is used to perform cluster administration tasks, * such as reconfigure cluster membership. The ZooKeeperAdmin class * inherits ZooKeeper and has similar usage pattern as ZooKeeper class. * Please check {@link ZooKeeper} class document for more details. * * @since 3.5.3 */ // See ZooKeeper.java for an explanation of why we need @SuppressWarnings("try") @SuppressWarnings("try") @InterfaceAudience.Public public class ZooKeeperAdmin extends ZooKeeper { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperAdmin.class); /** * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration * operations. * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified * * @see ZooKeeper#ZooKeeper(String, int, Watcher) * */ public ZooKeeperAdmin( String connectString, int sessionTimeout, Watcher watcher) throws IOException { super(connectString, sessionTimeout, watcher); } /** * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration * operations. * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param conf * passing this conf object gives each client the flexibility of * configuring properties differently compared to other instances * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified * * @see ZooKeeper#ZooKeeper(String, int, Watcher, ZKClientConfig) */ public ZooKeeperAdmin( String connectString, int sessionTimeout, Watcher watcher, ZKClientConfig conf) throws IOException { super(connectString, sessionTimeout, watcher, conf); } /** * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration * operations. * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param canBeReadOnly * whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * @param conf * passing this conf object gives each client the flexibility of * configuring properties differently compared to other instances * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified * * @see ZooKeeper#ZooKeeper(String, int, Watcher, boolean, ZKClientConfig) * * @since 3.6.1 */ public ZooKeeperAdmin( String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly, ZKClientConfig conf) throws IOException { super(connectString, sessionTimeout, watcher, canBeReadOnly, conf); } /** * Create a ZooKeeperAdmin object which is used to perform dynamic reconfiguration * operations. * * @param connectString * 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" If * the optional chroot suffix is used the example would look * like: "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). * @param sessionTimeout * session timeout in milliseconds * @param watcher * a watcher object which will be notified of state changes, may * also be notified for node events * @param canBeReadOnly * whether the created client is allowed to go to * read-only mode in case of partitioning. Read-only mode * basically means that if the client can't find any majority * servers but there's partitioned server it could reach, it * connects to one in read-only mode, i.e. read requests are * allowed while write requests are not. It continues seeking for * majority in the background. * * @throws IOException * in cases of network failure * @throws IllegalArgumentException * if an invalid chroot path is specified * * @see ZooKeeper#ZooKeeper(String, int, Watcher, boolean) */ public ZooKeeperAdmin( String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly) throws IOException { super(connectString, sessionTimeout, watcher, canBeReadOnly); } /** * Reconfigure - add/remove servers. Return the new configuration. * @param joiningServers * a comma separated list of servers being added (incremental reconfiguration) * @param leavingServers * a comma separated list of servers being removed (incremental reconfiguration) * @param newMembers * a comma separated list of new membership (non-incremental reconfiguration) * @param fromConfig * version of the current configuration * (optional - causes reconfiguration to throw an exception if configuration is no longer current) * @param stat the stat of /zookeeper/config znode will be copied to this * parameter if not null. * @return new configuration * @throws InterruptedException If the server transaction is interrupted. * @throws KeeperException If the server signals an error with a non-zero error code. */ public byte[] reconfigure( String joiningServers, String leavingServers, String newMembers, long fromConfig, Stat stat) throws KeeperException, InterruptedException { RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.reconfig); ReconfigRequest request = new ReconfigRequest(joiningServers, leavingServers, newMembers, fromConfig); GetDataResponse response = new GetDataResponse(); ReplyHeader r = cnxn.submitRequest(h, request, response, null); if (r.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(r.getErr()), ""); } if (stat != null) { DataTree.copyStat(response.getStat(), stat); } return response.getData(); } /** * Convenience wrapper around reconfig that takes Lists of strings instead of comma-separated servers. * * @see #reconfigure * */ public byte[] reconfigure( List joiningServers, List leavingServers, List newMembers, long fromConfig, Stat stat) throws KeeperException, InterruptedException { return reconfigure( StringUtils.joinStrings(joiningServers, ","), StringUtils.joinStrings(leavingServers, ","), StringUtils.joinStrings(newMembers, ","), fromConfig, stat); } /** * The Asynchronous version of reconfig. * * @see #reconfigure * **/ public void reconfigure( String joiningServers, String leavingServers, String newMembers, long fromConfig, DataCallback cb, Object ctx) { RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.reconfig); ReconfigRequest request = new ReconfigRequest(joiningServers, leavingServers, newMembers, fromConfig); GetDataResponse response = new GetDataResponse(); cnxn.queuePacket( h, new ReplyHeader(), request, response, cb, ZooDefs.CONFIG_NODE, ZooDefs.CONFIG_NODE, ctx, null); } /** * Convenience wrapper around asynchronous reconfig that takes Lists of strings instead of comma-separated servers. * * @see #reconfigure * */ public void reconfigure( List joiningServers, List leavingServers, List newMembers, long fromConfig, DataCallback cb, Object ctx) { reconfigure( StringUtils.joinStrings(joiningServers, ","), StringUtils.joinStrings(leavingServers, ","), StringUtils.joinStrings(newMembers, ","), fromConfig, cb, ctx); } /** * String representation of this ZooKeeperAdmin client. Suitable for things * like logging. * * Do NOT count on the format of this string, it may change without * warning. * * @since 3.5.3 */ @Override public String toString() { return super.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_audit_Aud0100644 0000000 0000000 00000000156 15051152474 032605 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/AuditConstants.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/AuditConstants.java0100644 0000000 0000000 00000002750 15051152474 034212 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; public final class AuditConstants { private AuditConstants() { //Utility classes should not have public constructors } static final String OP_START = "serverStart"; static final String OP_STOP = "serverStop"; public static final String OP_CREATE = "create"; public static final String OP_DELETE = "delete"; public static final String OP_SETDATA = "setData"; public static final String OP_SETACL = "setAcl"; public static final String OP_MULTI_OP = "multiOperation"; public static final String OP_RECONFIG = "reconfig"; public static final String OP_DEL_EZNODE_EXP = "ephemeralZNodeDeletionOnSessionCloseOrExpire"; } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/AuditEvent.java0100644 0000000 0000000 00000005723 15051152474 033322 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public final class AuditEvent { private static final char PAIR_SEPARATOR = '\t'; private static final String KEY_VAL_SEPARATOR = "="; // Holds the entries which to be logged. private Map logEntries = new LinkedHashMap<>(); private Result result; AuditEvent(Result result) { this.result = result; } /** * Gives all entries to be logged. * * @return log entries */ public Set> getLogEntries() { return logEntries.entrySet(); } void addEntry(FieldName fieldName, String value) { if (value != null) { logEntries.put(fieldName.name().toLowerCase(), value); } } public String getValue(FieldName fieldName) { return logEntries.get(fieldName.name().toLowerCase()); } public Result getResult() { return result; } /** * Gives the string to be logged, ignores fields with null values * * @return String */ @Override public String toString() { StringBuilder buffer = new StringBuilder(); boolean first = true; for (Map.Entry entry : logEntries.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (null != value) { // if first field then no need to add the tabs if (first) { first = false; } else { buffer.append(PAIR_SEPARATOR); } buffer.append(key).append(KEY_VAL_SEPARATOR) .append(value); } } //add result field if (buffer.length() > 0) { buffer.append(PAIR_SEPARATOR); } buffer.append("result").append(KEY_VAL_SEPARATOR) .append(result.name().toLowerCase()); return buffer.toString(); } public enum FieldName { USER, OPERATION, IP, ACL, ZNODE, SESSION, ZNODE_TYPE } public enum Result { SUCCESS, FAILURE, INVOKED } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/AuditHelper.java0100644 0000000 0000000 00000021352 15051152474 033454 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.MultiOperationRecord; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.audit.AuditEvent.Result; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.CreateTTLRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.SetACLRequest; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.Request; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Helper class to decouple audit log code. */ public final class AuditHelper { private static final Logger LOG = LoggerFactory.getLogger(AuditHelper.class); public static void addAuditLog(Request request, ProcessTxnResult rc) { addAuditLog(request, rc, false); } /** * Add audit log if audit log is enabled and operation is of type which to be audit logged. * * @param request user request * @param txnResult ProcessTxnResult * @param failedTxn whether audit is being done failed transaction for normal transaction */ public static void addAuditLog(Request request, ProcessTxnResult txnResult, boolean failedTxn) { if (!ZKAuditProvider.isAuditEnabled()) { return; } String op; //For failed transaction rc.path is null String path = txnResult.path; String acls = null; String createMode = null; try { switch (request.type) { case ZooDefs.OpCode.create: case ZooDefs.OpCode.create2: case ZooDefs.OpCode.createContainer: op = AuditConstants.OP_CREATE; CreateRequest createRequest = request.readRequestRecord(CreateRequest::new); createMode = getCreateMode(createRequest.getFlags()); if (failedTxn) { path = createRequest.getPath(); } break; case ZooDefs.OpCode.createTTL: op = AuditConstants.OP_CREATE; CreateTTLRequest createTtlRequest = request.readRequestRecord(CreateTTLRequest::new); createMode = getCreateMode(createTtlRequest.getFlags()); if (failedTxn) { path = createTtlRequest.getPath(); } break; case ZooDefs.OpCode.delete: case ZooDefs.OpCode.deleteContainer: op = AuditConstants.OP_DELETE; if (failedTxn) { DeleteRequest deleteRequest = request.readRequestRecord(DeleteRequest::new); path = deleteRequest.getPath(); } break; case ZooDefs.OpCode.setData: op = AuditConstants.OP_SETDATA; if (failedTxn) { SetDataRequest setDataRequest = request.readRequestRecord(SetDataRequest::new); path = setDataRequest.getPath(); } break; case ZooDefs.OpCode.setACL: op = AuditConstants.OP_SETACL; SetACLRequest setACLRequest = request.readRequestRecord(SetACLRequest::new); acls = ZKUtil.aclToString(setACLRequest.getAcl()); if (failedTxn) { path = setACLRequest.getPath(); } break; case ZooDefs.OpCode.multi: if (failedTxn) { op = AuditConstants.OP_MULTI_OP; } else { logMultiOperation(request, txnResult); //operation si already logged return; } break; case ZooDefs.OpCode.reconfig: op = AuditConstants.OP_RECONFIG; break; default: //Not an audit log operation return; } Result result = getResult(txnResult, failedTxn); log(request, path, op, acls, createMode, result); } catch (Throwable e) { LOG.error("Failed to audit log request {}", request.type, e); } } private static Result getResult(ProcessTxnResult rc, boolean failedTxn) { if (failedTxn) { return Result.FAILURE; } else { return rc.err == KeeperException.Code.OK.intValue() ? Result.SUCCESS : Result.FAILURE; } } private static void logMultiOperation(Request request, ProcessTxnResult rc) throws IOException, KeeperException { Map createModes = AuditHelper.getCreateModes(request); boolean multiFailed = false; for (ProcessTxnResult subTxnResult : rc.multiResult) { switch (subTxnResult.type) { case ZooDefs.OpCode.create: case ZooDefs.OpCode.create2: case ZooDefs.OpCode.createTTL: case ZooDefs.OpCode.createContainer: log(request, subTxnResult.path, AuditConstants.OP_CREATE, null, createModes.get(subTxnResult.path), Result.SUCCESS); break; case ZooDefs.OpCode.delete: case ZooDefs.OpCode.deleteContainer: log(request, subTxnResult.path, AuditConstants.OP_DELETE, null, null, Result.SUCCESS); break; case ZooDefs.OpCode.setData: log(request, subTxnResult.path, AuditConstants.OP_SETDATA, null, null, Result.SUCCESS); break; case ZooDefs.OpCode.error: multiFailed = true; break; default: // Do nothing, it ok, we do not log all multi operations } } if (multiFailed) { log(request, rc.path, AuditConstants.OP_MULTI_OP, null, null, Result.FAILURE); } } private static void log(Request request, String path, String op, String acls, String createMode, Result result) { log(request.getUsersForAudit(), op, path, acls, createMode, request.cnxn.getSessionIdHex(), request.cnxn.getHostAddress(), result); } private static void log(String user, String operation, String znode, String acl, String createMode, String session, String ip, Result result) { ZKAuditProvider.log(user, operation, znode, acl, createMode, session, ip, result); } private static String getCreateMode(int createFlags) throws KeeperException { return CreateMode.fromFlag(createFlags).toString().toLowerCase(); } private static Map getCreateModes(Request request) throws IOException, KeeperException { Map createModes = new HashMap<>(); if (!ZKAuditProvider.isAuditEnabled()) { return createModes; } MultiOperationRecord multiRequest = request.readRequestRecord(MultiOperationRecord::new); for (Op op : multiRequest) { if (op.getType() == ZooDefs.OpCode.create || op.getType() == ZooDefs.OpCode.create2 || op.getType() == ZooDefs.OpCode.createContainer) { CreateRequest requestRecord = (CreateRequest) op.toRequestRecord(); createModes.put(requestRecord.getPath(), getCreateMode(requestRecord.getFlags())); } } return createModes; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/AuditLogger.java0100644 0000000 0000000 00000002177 15051152474 033460 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; public interface AuditLogger { /** * Called during initialization of the logger. */ default void initialize() { } /** * Called to log an audit event. * * @param auditEvent contains all the fields to be logged */ void logAuditEvent(AuditEvent auditEvent); }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_audit_Slf0100644 0000000 0000000 00000000160 15051152474 032613 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/Slf4jAuditLogger.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/Slf4jAuditLogger.ja0100644 0000000 0000000 00000002550 15051152474 034027 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import org.apache.zookeeper.audit.AuditEvent.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Slf4j based audit logger */ public class Slf4jAuditLogger implements AuditLogger { private static final Logger LOG = LoggerFactory.getLogger(Slf4jAuditLogger.class); @Override public void logAuditEvent(AuditEvent auditEvent) { if (auditEvent.getResult() == Result.FAILURE) { LOG.error(auditEvent.toString()); } else { LOG.info(auditEvent.toString()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_audit_ZKA0100644 0000000 0000000 00000000157 15051152474 032522 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/ZKAuditProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/audit/ZKAuditProvider.jav0100644 0000000 0000000 00000011664 15051152474 034140 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import static org.apache.zookeeper.audit.AuditEvent.FieldName; import java.lang.reflect.Constructor; import org.apache.zookeeper.audit.AuditEvent.Result; import org.apache.zookeeper.server.ServerCnxnFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZKAuditProvider { static final String AUDIT_ENABLE = "zookeeper.audit.enable"; static final String AUDIT_IMPL_CLASS = "zookeeper.audit.impl.class"; private static final Logger LOG = LoggerFactory.getLogger(ZKAuditProvider.class); // By default audit logging is disabled private static boolean auditEnabled; private static AuditLogger auditLogger; static { auditEnabled = Boolean.getBoolean(AUDIT_ENABLE); if (auditEnabled) { //initialise only when audit logging is enabled auditLogger = getAuditLogger(); LOG.info("ZooKeeper audit is enabled."); } else { LOG.info("ZooKeeper audit is disabled."); } } private static AuditLogger getAuditLogger() { String auditLoggerClass = System.getProperty(AUDIT_IMPL_CLASS); if (auditLoggerClass == null) { auditLoggerClass = Slf4jAuditLogger.class.getName(); } try { Constructor clientCxnConstructor = Class.forName(auditLoggerClass) .getDeclaredConstructor(); AuditLogger auditLogger = (AuditLogger) clientCxnConstructor.newInstance(); auditLogger.initialize(); return auditLogger; } catch (Exception e) { throw new RuntimeException("Couldn't instantiate " + auditLoggerClass, e); } } /** * @return true if audit log is enabled */ public static boolean isAuditEnabled() { return auditEnabled; } public static void log(String user, String operation, String znode, String acl, String createMode, String session, String ip, Result result) { auditLogger.logAuditEvent(createLogEvent(user, operation, znode, acl, createMode, session, ip, result)); } /** * A helper api for creating an AuditEvent object. */ static AuditEvent createLogEvent(String user, String operation, Result result) { AuditEvent event = new AuditEvent(result); event.addEntry(FieldName.USER, user); event.addEntry(FieldName.OPERATION, operation); return event; } /** * A helper api for creating an AuditEvent object. */ static AuditEvent createLogEvent(String user, String operation, String znode, String acl, String createMode, String session, String ip, Result result) { AuditEvent event = new AuditEvent(result); event.addEntry(FieldName.SESSION, session); event.addEntry(FieldName.USER, user); event.addEntry(FieldName.IP, ip); event.addEntry(FieldName.OPERATION, operation); event.addEntry(FieldName.ZNODE, znode); event.addEntry(FieldName.ZNODE_TYPE, createMode); event.addEntry(FieldName.ACL, acl); return event; } /** * Add audit log for server start and register server stop log. */ public static void addZKStartStopAuditLog() { if (isAuditEnabled()) { log(getZKUser(), AuditConstants.OP_START, Result.SUCCESS); Runtime.getRuntime().addShutdownHook(new Thread(() -> { log(getZKUser(), AuditConstants.OP_STOP, Result.INVOKED); })); } } /** * Add audit log for server start fail. */ public static void addServerStartFailureAuditLog() { if (isAuditEnabled()) { log(ZKAuditProvider.getZKUser(), AuditConstants.OP_START, Result.FAILURE); } } private static void log(String user, String operation, Result result) { auditLogger.logAuditEvent(createLogEvent(user, operation, result)); } /** * User who has started the ZooKeeper server user, it will be the logged-in * user. If no user logged-in then system user. */ public static String getZKUser() { return ServerCnxnFactory.getUserName(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/AclParser.java0100644 0000000 0000000 00000005251 15051152474 032563 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; /** * a parser for ACL strings */ public class AclParser { /** * parse string into list of ACL * @param aclString * @return list of ACL */ public static List parse(String aclString) { List acl; String[] acls = aclString.split(","); acl = new ArrayList<>(); for (String a : acls) { int firstColon = a.indexOf(':'); int lastColon = a.lastIndexOf(':'); if (firstColon == -1 || lastColon == -1 || firstColon == lastColon) { System.err.println(a + " does not have the form scheme:id:perm"); continue; } ACL newAcl = new ACL(); newAcl.setId(new Id(a.substring(0, firstColon), a.substring(firstColon + 1, lastColon))); newAcl.setPerms(getPermFromString(a.substring(lastColon + 1))); acl.add(newAcl); } return acl; } private static int getPermFromString(String permString) { int perm = 0; for (int i = 0; i < permString.length(); i++) { switch (permString.charAt(i)) { case 'r': perm |= ZooDefs.Perms.READ; break; case 'w': perm |= ZooDefs.Perms.WRITE; break; case 'c': perm |= ZooDefs.Perms.CREATE; break; case 'd': perm |= ZooDefs.Perms.DELETE; break; case 'a': perm |= ZooDefs.Perms.ADMIN; break; default: System.err.println("Unknown perm type: " + permString.charAt(i)); } } return perm; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/AddAuthCommand.java0100644 0000000 0000000 00000003754 15051152474 033526 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static java.nio.charset.StandardCharsets.UTF_8; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; /** * addAuth command for cli */ public class AddAuthCommand extends CliCommand { private static Options options = new Options(); private String[] args; public AddAuthCommand() { super("addauth", "scheme auth"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); CommandLine cl; try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { byte[] b = null; if (args.length >= 3) { b = args[2].getBytes(UTF_8); } zk.addAuthInfo(args[1], b); return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/AddWatchCommand.java0100644 0000000 0000000 00000005275 15051152474 033673 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.Arrays; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.KeeperException; /** * addWatch command for cli. * Matches the ZooKeeper API addWatch() */ public class AddWatchCommand extends CliCommand { private static final Options options = new Options(); private static final AddWatchMode defaultMode = AddWatchMode.PERSISTENT_RECURSIVE; private CommandLine cl; private AddWatchMode mode = defaultMode; static { options.addOption("m", true, ""); } public AddWatchCommand() { super("addWatch", "[-m mode] path # optional mode is one of " + Arrays.toString(AddWatchMode.values()) + " - default is " + defaultMode.name()); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } if (cl.getArgs().length != 2) { throw new CliParseException(getUsageStr()); } if (cl.hasOption("m")) { try { mode = AddWatchMode.valueOf(cl.getOptionValue("m").toUpperCase()); } catch (IllegalArgumentException e) { throw new CliParseException(getUsageStr()); } } return this; } @Override public boolean exec() throws CliException { String path = cl.getArgs()[1]; try { zk.addWatch(path, mode); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Base60100644 0000000 0000000 00000000163 15051152474 032473 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/Base64OutputFormatter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/Base64OutputFormatter0100644 0000000 0000000 00000002163 15051152474 034117 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.Base64; public class Base64OutputFormatter implements OutputFormatter { public static final Base64OutputFormatter INSTANCE = new Base64OutputFormatter(); @Override public String format(byte[] data) { return Base64.getEncoder().encodeToString(data); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CliCommand.java0100644 0000000 0000000 00000007076 15051152474 032724 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.io.PrintStream; import java.util.Map; import javax.annotation.Nullable; import org.apache.commons.cli.Options; import org.apache.zookeeper.ZooKeeper; /** * base class for all CLI commands */ public abstract class CliCommand { protected ZooKeeper zk; protected PrintStream out; protected PrintStream err; private String cmdStr; private String optionStr; @Nullable private Options options; /** * a CLI command with command string and options. * Using System.out and System.err for printing * @param cmdStr the string used to call this command * @param optionStr the string used to call this command */ public CliCommand(String cmdStr, String optionStr) { this(cmdStr, optionStr, null); } /** * a CLI command with command string and options. * Using System.out and System.err for printing * @param cmdStr the string used to call this command * @param optionStr the string used to call this command * @param options the command options */ public CliCommand(String cmdStr, String optionStr, Options options) { this.out = System.out; this.err = System.err; this.cmdStr = cmdStr; this.optionStr = optionStr; this.options = options; } /** * Set out printStream (usable for testing) * @param out */ public void setOut(PrintStream out) { this.out = out; } /** * Set err printStream (usable for testing) * @param err */ public void setErr(PrintStream err) { this.err = err; } /** * set the zookeeper instance * @param zk the ZooKeeper instance. */ public void setZk(ZooKeeper zk) { this.zk = zk; } /** * get the string used to call this command */ public String getCmdStr() { return cmdStr; } /** * get the option string */ public String getOptionStr() { return optionStr; } /** * get a usage string, contains the command and the options */ public String getUsageStr() { return CommandUsageHelper.getUsage(cmdStr + " " + optionStr, options); } /** * add this command to a map. Use the command string as key. * @param cmdMap */ public void addToMap(Map cmdMap) { cmdMap.put(cmdStr, this); } /** * parse the command arguments * @param cmdArgs * @return this CliCommand * @throws CliParseException */ public abstract CliCommand parse(String[] cmdArgs) throws CliParseException; /** * * @return true if command has watch option, false otherwise * @throws CliException */ public abstract boolean exec() throws CliException; } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CliException.java0100644 0000000 0000000 00000003374 15051152474 033301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; @SuppressWarnings("serial") public class CliException extends Exception { protected int exitCode; protected static final int DEFAULT_EXCEPTION_EXIT_CODE = 1; public CliException(String message) { this(message, DEFAULT_EXCEPTION_EXIT_CODE); } public CliException(String message, int exitCode) { super(message); this.exitCode = exitCode; } public CliException(Throwable cause) { this(cause, DEFAULT_EXCEPTION_EXIT_CODE); } public CliException(Throwable cause, int exitCode) { super(cause); this.exitCode = exitCode; } public CliException(String message, Throwable cause) { this(message, cause, DEFAULT_EXCEPTION_EXIT_CODE); } public CliException(String message, Throwable cause, int exitCode) { super(message, cause); this.exitCode = exitCode; } public int getExitCode() { return exitCode; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_CliPa0100644 0000000 0000000 00000000157 15051152474 032526 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CliParseException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CliParseException.jav0100644 0000000 0000000 00000002200 15051152474 034116 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.ParseException; @SuppressWarnings("serial") public class CliParseException extends CliException { public CliParseException(ParseException parseException) { super(parseException); } public CliParseException(String message) { super(message); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_CliWr0100644 0000000 0000000 00000000161 15051152474 032551 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CliWrapperException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CliWrapperException.j0100644 0000000 0000000 00000006706 15051152474 034154 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.zookeeper.KeeperException; @SuppressWarnings("serial") public class CliWrapperException extends CliException { public CliWrapperException(Throwable cause) { super(getMessage(cause), cause); } private static String getMessage(Throwable cause) { if (cause instanceof KeeperException) { KeeperException keeperException = (KeeperException) cause; if (keeperException instanceof KeeperException.NoNodeException) { return "Node does not exist: " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.NoChildrenForEphemeralsException) { return "Ephemerals cannot have children: " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.NodeExistsException) { return "Node already exists: " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.NotEmptyException) { return "Node not empty: " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.NotReadOnlyException) { return "Not a read-only call: " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.InvalidACLException) { return "Acl is not valid : " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.NoAuthException) { return "Insufficient permission : " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.BadArgumentsException) { return "Arguments are not valid : " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.BadVersionException) { return "version No is not valid : " + keeperException.getPath(); } else if (keeperException instanceof KeeperException.ReconfigInProgress) { return "Another reconfiguration is in progress -- concurrent " + "reconfigs not supported (yet)"; } else if (keeperException instanceof KeeperException.NewConfigNoQuorum) { return "No quorum of new config is connected and " + "up-to-date with the leader of last commmitted config - try invoking reconfiguration after " + "new servers are connected and synced"; } else if (keeperException instanceof KeeperException.QuotaExceededException) { return "Quota has exceeded : " + keeperException.getPath(); } } return cause.getMessage(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CloseCommand.java0100644 0000000 0000000 00000002453 15051152474 033254 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; /** * close command for cli */ public class CloseCommand extends CliCommand { public CloseCommand() { super("close", ""); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { return this; } @Override public boolean exec() throws CliException { try { zk.close(); } catch (Exception ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CommandFactory.java0100644 0000000 0000000 00000004744 15051152474 033623 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.function.Supplier; /** * Factory class for creating instances of {@link CliCommand}. */ public class CommandFactory { /** * All Cli Commands. */ public enum Command { CLOSE(CloseCommand::new), CREATE(CreateCommand::new), DELETE(DeleteCommand::new), DELETE_ALL(DeleteAllCommand::new), SET(SetCommand::new), GET(GetCommand::new), LS(LsCommand::new), GET_ACL(GetAclCommand::new), SET_ACL(SetAclCommand::new), STAT(StatCommand::new), SYNC(SyncCommand::new), SET_QUOTA(SetQuotaCommand::new), LIST_QUOTA(ListQuotaCommand::new), DEL_QUOTA(DelQuotaCommand::new), ADD_AUTH(AddAuthCommand::new), RECONFIG(ReconfigCommand::new), GET_CONFIG(GetConfigCommand::new), REMOVE_WATCHES(RemoveWatchesCommand::new), GET_EPHEMERALS(GetEphemeralsCommand::new), GET_ALL_CHILDREN_NUMBER(GetAllChildrenNumberCommand::new), VERSION(VersionCommand::new), ADD_WATCH(AddWatchCommand::new), WHO_AM_I(WhoAmICommand::new); private Supplier instantiator; private CliCommand getInstance() { return instantiator.get(); } Command(Supplier instantiator) { this.instantiator = instantiator; } } /** * Creates a new {@link CliCommand} instance. * @param command the {@link Command} to create a new instance of * @return the new {@code CliCommand} instance */ public static CliCommand getInstance (Command command) { return command.getInstance(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Comma0100644 0000000 0000000 00000000166 15051152474 032572 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CommandNotFoundException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CommandNotFoundExcept0100644 0000000 0000000 00000002030 15051152474 034163 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; @SuppressWarnings("serial") public class CommandNotFoundException extends CliException { public CommandNotFoundException(String command) { super("Command not found: " + command, 127); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Comma0100644 0000000 0000000 00000000160 15051152474 032564 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CommandUsageHelper.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CommandUsageHelper.ja0100644 0000000 0000000 00000003250 15051152474 034060 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.io.PrintWriter; import java.io.StringWriter; import javax.annotation.Nullable; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; public class CommandUsageHelper { public static String getUsage(String commandSyntax, @Nullable Options options) { StringBuilder buffer = new StringBuilder(); buffer.append(commandSyntax); if (options != null && !options.getOptions().isEmpty()) { buffer.append(System.lineSeparator()); StringWriter out = new StringWriter(); HelpFormatter formatter = new HelpFormatter(); formatter.printOptions(new PrintWriter(out), formatter.getWidth(), options, formatter.getLeftPadding(), formatter.getDescPadding()); buffer.append(out); } return buffer.toString(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/CreateCommand.java0100644 0000000 0000000 00000012032 15051152474 033404 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.EphemeralType; /** * create command for cli */ public class CreateCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption(new Option("e", false, "ephemeral")); options.addOption(new Option("s", false, "sequential")); options.addOption(new Option("c", false, "container")); options.addOption(new Option("t", true, "ttl")); } public CreateCommand() { super("create", "[-s] [-e] [-c] [-t ttl] path [data] [acl]"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { boolean hasE = cl.hasOption("e"); boolean hasS = cl.hasOption("s"); boolean hasC = cl.hasOption("c"); boolean hasT = cl.hasOption("t"); if (hasC && (hasE || hasS)) { throw new MalformedCommandException("-c cannot be combined with -s or -e. Containers cannot be ephemeral or sequential."); } long ttl; try { ttl = hasT ? Long.parseLong(cl.getOptionValue("t")) : 0; } catch (NumberFormatException e) { throw new MalformedCommandException("-t argument must be a long value"); } if (hasT && hasE) { throw new MalformedCommandException("TTLs cannot be used with Ephemeral znodes"); } if (hasT && hasC) { throw new MalformedCommandException("TTLs cannot be used with Container znodes"); } CreateMode flags; if (hasE && hasS) { flags = CreateMode.EPHEMERAL_SEQUENTIAL; } else if (hasE) { flags = CreateMode.EPHEMERAL; } else if (hasS) { flags = hasT ? CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL : CreateMode.PERSISTENT_SEQUENTIAL; } else if (hasC) { flags = CreateMode.CONTAINER; } else { flags = hasT ? CreateMode.PERSISTENT_WITH_TTL : CreateMode.PERSISTENT; } if (hasT) { try { EphemeralType.TTL.toEphemeralOwner(ttl); } catch (IllegalArgumentException e) { throw new MalformedCommandException(e.getMessage()); } } String path = args[1]; byte[] data = null; if (args.length > 2) { data = args[2].getBytes(UTF_8); } List acl = ZooDefs.Ids.OPEN_ACL_UNSAFE; if (args.length > 3) { acl = AclParser.parse(args[3]); } try { String newPath = hasT ? zk.create(path, data, acl, flags, new Stat(), ttl) : zk.create(path, data, acl, flags); err.println("Created " + newPath); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException.EphemeralOnLocalSessionException e) { err.println("Unable to create ephemeral node on a local session"); throw new CliWrapperException(e); } catch (KeeperException.InvalidACLException ex) { err.println(ex.getMessage()); throw new CliWrapperException(ex); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return true; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/DelQuotaCommand.java0100644 0000000 0000000 00000014714 15051152474 033730 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; /** * delQuota command for cli */ public class DelQuotaCommand extends CliCommand { private Options options = new Options(); private String[] args; private CommandLine cl; public DelQuotaCommand() { super("delquota", "[-n|-b|-N|-B] path"); OptionGroup og1 = new OptionGroup(); og1.addOption(new Option("n", false, "num soft quota")); og1.addOption(new Option("b", false, "bytes soft quota")); og1.addOption(new Option("N", false, "num hard quota")); og1.addOption(new Option("B", false, "bytes hard quota")); options.addOptionGroup(og1); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { String path = args[1]; // Use a StatsTrack object to pass in to delQuota which quotas // to delete by setting them to 1 as a flag. StatsTrack quota = new StatsTrack(); if (cl.hasOption("n")) { quota.setCount(1); } if (cl.hasOption("b")) { quota.setBytes(1); } if (cl.hasOption("N")) { quota.setCountHardLimit(1); } if (cl.hasOption("B")) { quota.setByteHardLimit(1); } boolean flagSet = (cl.hasOption("n") || cl.hasOption("N") || cl.hasOption("b") || cl.hasOption("B")); try { delQuota(zk, path, flagSet ? quota : null); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException.NoNodeException ne) { err.println("quota for " + path + " does not exist."); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } /** * this method deletes quota for a node. * * @param zk the zookeeper client * @param path the path to delete quota for * @param quota the quotas to delete (set to 1), null to delete all * @return true if quota deletion is successful * @throws KeeperException * @throws MalformedPathException * @throws InterruptedException */ public static boolean delQuota(ZooKeeper zk, String path, StatsTrack quota) throws KeeperException, InterruptedException, MalformedPathException { String parentPath = Quotas.quotaPath(path); String quotaPath = Quotas.limitPath(path); if (zk.exists(quotaPath, false) == null) { System.out.println("Quota does not exist for " + path); return true; } byte[] data = null; try { data = zk.getData(quotaPath, false, new Stat()); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException.NoNodeException ne) { throw new KeeperException.NoNodeException(ne.getMessage()); } StatsTrack strack = new StatsTrack(data); if (quota == null) { // delete till you can find a node with more than // one child List children = zk.getChildren(parentPath, false); /// delete the direct children first for (String child : children) { zk.delete(parentPath + "/" + child, -1); } // cut the tree till their is more than one child trimProcQuotas(zk, parentPath); } else { if (quota.getCount() > 0) { strack.setCount(-1); } if (quota.getBytes() > 0) { strack.setBytes(-1L); } if (quota.getCountHardLimit() > 0) { strack.setCountHardLimit(-1); } if (quota.getByteHardLimit() > 0) { strack.setByteHardLimit(-1L); } zk.setData(quotaPath, strack.getStatsBytes(), -1); } return true; } /** * trim the quota tree to recover unwanted tree elements in the quota's tree * * @param zk the zookeeper client * @param path the path to start from and go up and see if their is any * unwanted parent in the path. * @return true if successful * @throws KeeperException * @throws InterruptedException */ private static boolean trimProcQuotas(ZooKeeper zk, String path) throws KeeperException, InterruptedException { if (Quotas.quotaZookeeper.equals(path)) { return true; } List children = zk.getChildren(path, false); if (children.size() == 0) { zk.delete(path, -1); String parent = path.substring(0, path.lastIndexOf('/')); return trimProcQuotas(zk, parent); } else { return true; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Delet0100644 0000000 0000000 00000000156 15051152474 032572 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/DeleteAllCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/DeleteAllCommand.java0100644 0000000 0000000 00000005460 15051152474 034043 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKUtil; /** * deleteAll command for cli */ public class DeleteAllCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption(new Option("b", true, "batch size")); } public DeleteAllCommand() { this("deleteall"); } public DeleteAllCommand(String cmdStr) { super(cmdStr, "path [-b batch size]"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { int batchSize; try { batchSize = cl.hasOption("b") ? Integer.parseInt(cl.getOptionValue("b")) : 1000; } catch (NumberFormatException e) { throw new MalformedCommandException("-b argument must be an int value"); } String path = args[1]; try { boolean success = ZKUtil.deleteRecursive(zk, path, batchSize); if (!success) { err.println("Failed to delete some node(s) in the subtree!"); } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/DeleteCommand.java0100644 0000000 0000000 00000005706 15051152474 033415 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; /** * delete command for cli */ public class DeleteCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("v", true, "version"); } public DeleteCommand() { super("delete", "[-v version] path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } retainCompatibility(cmdArgs); return this; } private void retainCompatibility(String[] cmdArgs) throws CliParseException { if (args.length > 2) { err.println("'delete path [version]' has been deprecated. " + "Please use 'delete [-v version] path' instead."); DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); } } @Override public boolean exec() throws CliException { String path = args[1]; int version; if (cl.hasOption("v")) { version = Integer.parseInt(cl.getOptionValue("v")); } else { version = -1; } try { zk.delete(path, version); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetAclCommand.java0100644 0000000 0000000 00000005122 15051152474 033342 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS 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.cli; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; /** * getAcl command for cli */ public class GetAclCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("s", false, "stats"); } public GetAclCommand() { super("getAcl", "[-s] path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { String path = args[1]; Stat stat = new Stat(); List acl; try { acl = zk.getACL(path, stat); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } for (ACL a : acl) { out.println(a.getId() + ": " + ZKUtil.getPermString(a.getPerms())); } if (cl.hasOption("s")) { new StatPrinter(out).print(stat); } return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_GetAl0100644 0000000 0000000 00000000171 15051152474 032526 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetAllChildrenNumberCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetAllChildrenNumberC0100644 0000000 0000000 00000004403 15051152474 034062 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; /** * getAllChildrenNumber command for CLI */ public class GetAllChildrenNumberCommand extends CliCommand { private static Options options = new Options(); private String[] args; public GetAllChildrenNumberCommand() { super("getAllChildrenNumber", "path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); CommandLine cl; try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); return this; } @Override public boolean exec() throws CliException { if (args.length < 2) { throw new MalformedCommandException(getUsageStr()); } try { String path = args[1]; int allChildrenNumber = zk.getAllChildrenNumber(path); out.println(allChildrenNumber); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetCommand.java0100644 0000000 0000000 00000007245 15051152474 032732 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; /** * get command for cli */ public class GetCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("s", false, "Print znode stats additionally"); options.addOption("w", false, "Watch for changes on the znode"); options.addOption("b", false, "Output data in base64 format"); options.addOption("x", false, "Output data in hexdump format"); } public GetCommand() { super("get", "[-s] [-w] [-b] [-x] path", options); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } retainCompatibility(cmdArgs); return this; } private void retainCompatibility(String[] cmdArgs) throws CliParseException { // get path [watch] if (args.length > 2) { // rewrite to option cmdArgs[2] = "-w"; err.println("'get path [watch]' has been deprecated. " + "Please use 'get [-s] [-w] path' instead."); DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); } } @Override public boolean exec() throws CliException { boolean watch = cl.hasOption("w"); String path = args[1]; Stat stat = new Stat(); byte[] data; try { data = zk.getData(path, watch, stat); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } OutputFormatter formatter = PlainOutputFormatter.INSTANCE; if (cl.hasOption("b")) { formatter = Base64OutputFormatter.INSTANCE; } if (cl.hasOption("x")) { formatter = HexDumpOutputFormatter.INSTANCE; } data = (data == null) ? "null".getBytes() : data; out.println(formatter.format(data)); if (cl.hasOption("s")) { new StatPrinter(out).print(stat); } return watch; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_GetCo0100644 0000000 0000000 00000000156 15051152474 032536 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetConfigCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetConfigCommand.java0100644 0000000 0000000 00000005415 15051152474 034055 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static java.nio.charset.StandardCharsets.UTF_8; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.util.ConfigUtils; /** * get command for cli */ public class GetConfigCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("s", false, "stats"); options.addOption("w", false, "watch"); options.addOption("c", false, "client connection string"); } public GetConfigCommand() { super("config", "[-c] [-w] [-s]"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 1) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { boolean watch = cl.hasOption("w"); Stat stat = new Stat(); byte[] data; try { data = zk.getConfig(watch, stat); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } data = (data == null) ? "null".getBytes(UTF_8) : data; if (cl.hasOption("c")) { out.println(ConfigUtils.getClientConfigStr(new String(data, UTF_8))); } else { out.println(new String(data, UTF_8)); } if (cl.hasOption("s")) { new StatPrinter(out).print(stat); } return watch; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_GetEp0100644 0000000 0000000 00000000162 15051152474 032536 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetEphemeralsCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/GetEphemeralsCommand.0100644 0000000 0000000 00000004543 15051152474 034074 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS 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.cli; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; /** * getEphemerals command for CLI */ public class GetEphemeralsCommand extends CliCommand { private static Options options = new Options(); private String[] args; public GetEphemeralsCommand() { super("getEphemerals", "path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); CommandLine cl; try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); return this; } @Override public boolean exec() throws CliException { String path; List ephemerals; try { if (args.length < 2) { // gets all the ephemeral nodes for the session ephemerals = zk.getEphemerals(); } else { path = args[1]; ephemerals = zk.getEphemerals(path); } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } out.println(ephemerals); return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_HexDu0100644 0000000 0000000 00000000164 15051152474 032551 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/HexDumpOutputFormatter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/HexDumpOutputFormatte0100644 0000000 0000000 00000002354 15051152474 034265 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; public class HexDumpOutputFormatter implements OutputFormatter { public static final HexDumpOutputFormatter INSTANCE = new HexDumpOutputFormatter(); @Override public String format(byte[] data) { ByteBuf buf = Unpooled.wrappedBuffer(data); return ByteBufUtil.prettyHexDump(buf); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_ListQ0100644 0000000 0000000 00000000156 15051152474 032571 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/ListQuotaCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/ListQuotaCommand.java0100644 0000000 0000000 00000006752 15051152474 034142 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.ArrayList; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; /** * listQuota command for cli */ public class ListQuotaCommand extends CliCommand { private static Options options = new Options(); private String[] args; public ListQuotaCommand() { super("listquota", "path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); CommandLine cl; try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { String path = args[1]; String absolutePath = Quotas.limitPath(path); try { err.println("absolute path is " + absolutePath); List statsTracks = listQuota(zk, path); for (int i = 0; i < statsTracks.size(); i++) { StatsTrack st = statsTracks.get(i); if (i == 0) { out.println("Output quota for " + path + " " + st.toString()); } else { out.println("Output stat for " + path + " " + st.toString()); } } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException.NoNodeException ne) { err.println("quota for " + path + " does not exist."); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } // @VisibleForTesting public static List listQuota(ZooKeeper zk, String path) throws KeeperException, InterruptedException { List statsTracks = new ArrayList<>(); Stat stat = new Stat(); byte[] data = zk.getData(Quotas.limitPath(path), false, stat); StatsTrack st = new StatsTrack(data); statsTracks.add(st); data = zk.getData(Quotas.statPath(path), false, stat); st = new StatsTrack(data); statsTracks.add(st); return statsTracks; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/LsCommand.java0100644 0000000 0000000 00000010366 15051152474 032567 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS 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.cli; import java.util.Collections; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.data.Stat; /** * ls command for cli */ public class LsCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("?", false, "help"); options.addOption("s", false, "stat"); options.addOption("w", false, "watch"); options.addOption("R", false, "recurse"); } public LsCommand() { super("ls", "[-s] [-w] [-R] path"); } private void printHelp() { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("ls [options] path", options); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (cl.hasOption("?")) { printHelp(); } retainCompatibility(cmdArgs); return this; } private void retainCompatibility(String[] cmdArgs) throws CliParseException { // get path [watch] if (args.length > 2) { // rewrite to option cmdArgs[2] = "-w"; err.println("'ls path [watch]' has been deprecated. " + "Please use 'ls [-w] path' instead."); DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); } } @Override public boolean exec() throws CliException { if (args.length < 2) { throw new MalformedCommandException(getUsageStr()); } String path = args[1]; boolean watch = cl.hasOption("w"); boolean withStat = cl.hasOption("s"); boolean recursive = cl.hasOption("R"); try { if (recursive) { ZKUtil.visitSubTreeDFS(zk, path, watch, (rc, path1, ctx, name) -> out.println(path1)); } else { Stat stat = withStat ? new Stat() : null; List children = zk.getChildren(path, watch, stat); printChildren(children, stat); } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return watch; } private void printChildren(List children, Stat stat) { Collections.sort(children); out.append("["); boolean first = true; for (String child : children) { if (!first) { out.append(", "); } else { first = false; } out.append(child); } out.append("]\n"); if (stat != null) { new StatPrinter(out).print(stat); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Malfo0100644 0000000 0000000 00000000167 15051152474 032575 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/MalformedCommandException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/MalformedCommandExcep0100644 0000000 0000000 00000001775 15051152474 034170 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; @SuppressWarnings("serial") public class MalformedCommandException extends CliException { public MalformedCommandException(String message) { super(message); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Malfo0100644 0000000 0000000 00000000164 15051152474 032572 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/MalformedPathException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/MalformedPathExceptio0100644 0000000 0000000 00000001767 15051152474 034223 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; @SuppressWarnings("serial") public class MalformedPathException extends CliException { public MalformedPathException(String message) { super(message); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/OutputFormatter.java0100644 0000000 0000000 00000001620 15051152474 034067 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; public interface OutputFormatter { String format(byte[] data); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Plain0100644 0000000 0000000 00000000162 15051152474 032575 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/PlainOutputFormatter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/PlainOutputFormatter.0100644 0000000 0000000 00000002175 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static java.nio.charset.StandardCharsets.UTF_8; public class PlainOutputFormatter implements OutputFormatter { public static final PlainOutputFormatter INSTANCE = new PlainOutputFormatter(); @Override public String format(byte[] data) { return new String(data, UTF_8); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/ReconfigCommand.java0100644 0000000 0000000 00000016040 15051152474 033740 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.FileInputStream; import java.util.Properties; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; /** * reconfig command for cli */ public class ReconfigCommand extends CliCommand { private static Options options = new Options(); /* joining - comma separated list of server config strings for servers to be added to the ensemble. * Each entry is identical in syntax as it would appear in a configuration file. Only used for * incremental reconfigurations. */ private String joining; /* leaving - comma separated list of server IDs to be removed from the ensemble. Only used for * incremental reconfigurations. */ private String leaving; /* members - comma separated list of new membership information (e.g., contents of a membership * configuration file) - for use only with a non-incremental reconfiguration. This may be specified * manually via the -members flag or it will automatically be filled in by reading the contents * of an actual configuration file using the -file flag. */ private String members; /* version - version of config from which we want to reconfigure - if current config is different * reconfiguration will fail. Should be committed from the CLI to disable this option. */ long version = -1; private CommandLine cl; static { options.addOption("s", false, "stats"); options.addOption("v", true, "required current config version"); options.addOption("file", true, "path of config file to parse for membership"); options.addOption("members", true, "comma-separated list of config strings for " + "non-incremental reconfig"); options.addOption("add", true, "comma-separated list of config strings for " + "new servers"); options.addOption("remove", true, "comma-separated list of server IDs to remove"); } public ReconfigCommand() { super("reconfig", "[-s] " + "[-v version] " + "[[-file path] | " + "[-members serverID=host:port1:port2;port3[,...]*]] | " + "[-add serverId=host:port1:port2;port3[,...]]* " + "[-remove serverId[,...]*]"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { joining = null; leaving = null; members = null; DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } if (!(cl.hasOption("file") || cl.hasOption("members")) && !cl.hasOption("add") && !cl.hasOption("remove")) { throw new CliParseException(getUsageStr()); } if (cl.hasOption("v")) { try { version = Long.parseLong(cl.getOptionValue("v"), 16); } catch (NumberFormatException e) { throw new CliParseException("-v must be followed by a long (configuration version)"); } } else { version = -1; } // Simple error checking for conflicting modes if ((cl.hasOption("file") || cl.hasOption("members")) && (cl.hasOption("add") || cl.hasOption("remove"))) { throw new CliParseException("Can't use -file or -members together with -add or -remove (mixing incremental" + " and non-incremental modes is not allowed)"); } if (cl.hasOption("file") && cl.hasOption("members")) { throw new CliParseException("Can't use -file and -members together (conflicting non-incremental modes)"); } // Set the joining/leaving/members values based on the mode we're in if (cl.hasOption("add")) { joining = cl.getOptionValue("add").toLowerCase(); } if (cl.hasOption("remove")) { leaving = cl.getOptionValue("remove").toLowerCase(); } if (cl.hasOption("members")) { members = cl.getOptionValue("members").toLowerCase(); } if (cl.hasOption("file")) { try { Properties dynamicCfg = new Properties(); try (FileInputStream inConfig = new FileInputStream(cl.getOptionValue("file"))) { dynamicCfg.load(inConfig); } //check that membership makes sense; leader will make these checks again //don't check for leader election ports since //client doesn't know what leader election alg is used members = QuorumPeerConfig.parseDynamicConfig(dynamicCfg, 0, true, false, null).toString(); } catch (Exception e) { throw new CliParseException("Error processing " + cl.getOptionValue("file") + e.getMessage()); } } return this; } @Override public boolean exec() throws CliException { try { Stat stat = new Stat(); if (!(zk instanceof ZooKeeperAdmin)) { // This should never happen when executing reconfig command line, // because it is guaranteed that we have a ZooKeeperAdmin instance ready // to use in CliCommand stack. // The only exception would be in test code where clients can directly set // ZooKeeper object to ZooKeeperMain. return false; } byte[] curConfig = ((ZooKeeperAdmin) zk).reconfigure(joining, leaving, members, version, stat); out.println("Committed new configuration:\n" + new String(curConfig, UTF_8)); if (cl.hasOption("s")) { new StatPrinter(out).print(stat); } } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_cli_Remov0100644 0000000 0000000 00000000162 15051152474 032622 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/RemoveWatchesCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/RemoveWatchesCommand.0100644 0000000 0000000 00000006565 15051152474 034131 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher.WatcherType; /** * Remove watches command for cli */ public class RemoveWatchesCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("c", false, "child watcher type"); options.addOption("d", false, "data watcher type"); options.addOption("p", false, "persistent watcher type"); options.addOption("r", false, "persistent recursive watcher type"); options.addOption("a", false, "any watcher type"); options.addOption("l", false, "remove locally when there is no server connection"); } public RemoveWatchesCommand() { super("removewatches", "path [-c|-d|-a] [-l]"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliWrapperException, MalformedPathException { String path = args[1]; WatcherType wtype = WatcherType.Any; // if no matching option -c or -d or -a is specified, we remove // the watches of the given node by choosing WatcherType.Any if (cl.hasOption("c")) { wtype = WatcherType.Children; } else if (cl.hasOption("d")) { wtype = WatcherType.Data; } else if (cl.hasOption("p")) { wtype = WatcherType.Persistent; } else if (cl.hasOption("r")) { wtype = WatcherType.PersistentRecursive; } else if (cl.hasOption("a")) { wtype = WatcherType.Any; } // whether to remove the watches locally boolean local = cl.hasOption("l"); try { zk.removeAllWatches(path, wtype, local); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return true; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/SetAclCommand.java0100644 0000000 0000000 00000006700 15051152474 033361 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; /** * setAcl command for cli. * Available options are s for printing znode's stats, v for set version of znode(s), R for * recursive setting. User can combine v and R options together, but not s and R considering the * number of znodes could be large. */ public class SetAclCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("s", false, "stats"); options.addOption("v", true, "version"); options.addOption("R", false, "recursive"); } public SetAclCommand() { super("setAcl", "[-s] [-v version] [-R] path acl"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 3) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { String path = args[1]; String aclStr = args[2]; List acl = AclParser.parse(aclStr); int version; if (cl.hasOption("v")) { version = Integer.parseInt(cl.getOptionValue("v")); } else { version = -1; } try { if (cl.hasOption("R")) { ZKUtil.visitSubTreeDFS(zk, path, false, (rc, p, ctx, name) -> { try { zk.setACL(p, acl, version); } catch (KeeperException | InterruptedException e) { out.print(e.getMessage()); } }); } else { Stat stat = zk.setACL(path, acl, version); if (cl.hasOption("s")) { new StatPrinter(out).print(stat); } } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/SetCommand.java0100644 0000000 0000000 00000005724 15051152474 032746 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.Base64; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; /** * set command for cli */ public class SetCommand extends CliCommand { private static Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("s", false, "Print znode stats additionally"); options.addOption("v", true, "Set with an expected version"); options.addOption("b", false, "Supply data in base64 format"); } public SetCommand() { super("set", "path data [-s] [-v version] [-b]", options); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 3) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { String path = args[1]; byte[] data; if (cl.hasOption("b")) { data = Base64.getDecoder().decode(args[2]); } else { data = args[2].getBytes(UTF_8); } int version; if (cl.hasOption("v")) { version = Integer.parseInt(cl.getOptionValue("v")); } else { version = -1; } try { Stat stat = zk.setData(path, data, version); if (cl.hasOption("s")) { new StatPrinter(out).print(stat); } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/SetQuotaCommand.java0100644 0000000 0000000 00000031723 15051152474 033756 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.ArrayList; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * setQuota command for cli */ public class SetQuotaCommand extends CliCommand { private static final Logger LOG = LoggerFactory.getLogger(SetQuotaCommand.class); private Options options = new Options(); private String[] args; private CommandLine cl; public SetQuotaCommand() { super("setquota", "-n|-b|-N|-B val path"); OptionGroup og1 = new OptionGroup(); og1.addOption(new Option("n", true, "num soft quota")); og1.addOption(new Option("b", true, "bytes soft quota")); og1.addOption(new Option("N", true, "num hard quota")); og1.addOption(new Option("B", true, "bytes hard quota")); og1.setRequired(true); options.addOptionGroup(og1); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { // get the args String path = args[1]; if (path.startsWith(Quotas.quotaZookeeper)) { err.println("cannot set a quota under the path: " + Quotas.quotaZookeeper); return false; } StatsTrack quota = new StatsTrack(); quota.setCount(-1); quota.setBytes(-1L); quota.setCountHardLimit(-1); quota.setByteHardLimit(-1L); if (!checkOptionValue(quota)) { return false; } boolean flagSet = (cl.hasOption("n") || cl.hasOption("N") || cl.hasOption("b") || cl.hasOption("B")); if (flagSet) { try { createQuota(zk, path, quota); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } } else { err.println(getUsageStr()); } return false; } private boolean checkOptionValue(StatsTrack quota) { try { if (cl.hasOption("n")) { // we are setting the num quota int count = Integer.parseInt(cl.getOptionValue("n")); if (count > 0) { quota.setCount(count); } else { err.println("the num quota must be greater than zero"); return false; } } if (cl.hasOption("b")) { // we are setting the bytes quota long bytes = Long.parseLong(cl.getOptionValue("b")); if (bytes >= 0) { quota.setBytes(bytes); } else { err.println("the bytes quota must be greater than or equal to zero"); return false; } } if (cl.hasOption("N")) { // we are setting the num hard quota int count = Integer.parseInt(cl.getOptionValue("N")); if (count > 0) { quota.setCountHardLimit(count); } else { err.println("the num quota must be greater than zero"); return false; } } if (cl.hasOption("B")) { // we are setting the byte hard quota long bytes = Long.parseLong(cl.getOptionValue("B")); if (bytes >= 0) { quota.setByteHardLimit(bytes); } else { err.println("the bytes quota must be greater than or equal to zero"); return false; } } } catch (NumberFormatException e) { err.println("NumberFormatException happens when parsing the option value"); return false; } return true; } /** * this method creates a quota node for the path * @param zk the ZooKeeper client * @param path the path for which quota needs to be created * @param quota the quotas * @return true if its successful and false if not. */ public static boolean createQuota(ZooKeeper zk, String path, StatsTrack quota) throws KeeperException, InterruptedException, MalformedPathException { // check if the path exists. We cannot create // quota for a path that doesn't exist in zookeeper // for now. Stat initStat; try { initStat = zk.exists(path, false); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } if (initStat == null) { throw new IllegalArgumentException(path + " does not exist."); } // now check if their is already existing // parent or child that has quota String quotaPath = Quotas.quotaZookeeper; // check for more than 2 children -- // if zookeeper_stats and zookeeper_quotas // are not the children then this path // is an ancestor of some path that // already has quota //check if the child node has a quota. checkIfChildQuota(zk, path); //check for any parent that has been quota checkIfParentQuota(zk, path); // this is valid node for quota // start creating all the parents if (zk.exists(quotaPath, false) == null) { try { zk.create(Quotas.procZookeeper, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(Quotas.quotaZookeeper, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException ne) { // do nothing } } // now create the direct children // and the stat and quota nodes String[] splits = path.split("/"); StringBuilder sb = new StringBuilder(); sb.append(quotaPath); for (int i = 1; i < splits.length; i++) { sb.append("/").append(splits[i]); quotaPath = sb.toString(); if (zk.exists(quotaPath, false) == null) { try { zk.create(quotaPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException ne) { //do nothing } } } String statPath = quotaPath + "/" + Quotas.statNode; quotaPath = quotaPath + "/" + Quotas.limitNode; byte[] data; if (zk.exists(quotaPath, false) == null) { zk.create(quotaPath, quota.getStatsBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack stats = new StatsTrack(); stats.setCount(0); stats.setBytes(0L); zk.create(statPath, stats.getStatsBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); data = zk.getData(quotaPath, false, new Stat()); StatsTrack quotaStrack = new StatsTrack(data); data = zk.getData(statPath, false, new Stat()); StatsTrack statStrack = new StatsTrack(data); checkQuota(quotaStrack, statStrack); } else { data = zk.getData(quotaPath, false, new Stat()); StatsTrack quotaStrack = new StatsTrack(data); if (quota.getCount() > -1) { quotaStrack.setCount(quota.getCount()); } if (quota.getBytes() > -1L) { quotaStrack.setBytes(quota.getBytes()); } if (quota.getCountHardLimit() > -1) { quotaStrack.setCountHardLimit(quota.getCountHardLimit()); } if (quota.getByteHardLimit() > -1L) { quotaStrack.setByteHardLimit(quota.getByteHardLimit()); } data = zk.getData(statPath, false, new Stat()); StatsTrack statStrack = new StatsTrack(data); checkQuota(quotaStrack, statStrack); zk.setData(quotaPath, quotaStrack.getStatsBytes(), -1); } return true; } private static void checkQuota(StatsTrack quotaStrack, StatsTrack statStrack) { if ((quotaStrack.getCount() > -1 && quotaStrack.getCount() < statStrack.getCount()) || (quotaStrack.getCountHardLimit() > -1 && quotaStrack.getCountHardLimit() < statStrack.getCount())) { System.out.println("[Warning]: the count quota you create is less than the existing count:" + statStrack.getCount()); } if ((quotaStrack.getBytes() > -1 && quotaStrack.getBytes() < statStrack.getBytes()) || (quotaStrack.getByteHardLimit() > -1 && quotaStrack.getByteHardLimit() < statStrack.getBytes())) { System.out.println("[Warning]: the bytes quota you create is less than the existing bytes:" + statStrack.getBytes()); } } private static void checkIfChildQuota(ZooKeeper zk, String path) throws KeeperException, InterruptedException { String realPath = Quotas.quotaPath(path); try { ZKUtil.visitSubTreeDFS(zk, realPath, false, (rc, quotaPath, ctx, name) -> { List children = new ArrayList<>(); try { children = zk.getChildren(quotaPath, false); } catch (KeeperException.NoNodeException ne) { LOG.debug("child removed during quota check", ne); return; } catch (InterruptedException | KeeperException e) { e.printStackTrace(); } if (children.size() == 0) { return; } for (String child : children) { if (!quotaPath.equals(Quotas.quotaZookeeper + path) && Quotas.limitNode.equals(child)) { throw new IllegalArgumentException(path + " has a child " + Quotas.trimQuotaPath(quotaPath) + " which has a quota"); } } }); } catch (KeeperException.NoNodeException ne) { // this is fine } } private static void checkIfParentQuota(ZooKeeper zk, String path) throws InterruptedException, KeeperException { final String[] splits = path.split("/"); String quotaPath = Quotas.quotaZookeeper; StringBuilder sb = new StringBuilder(); sb.append(quotaPath); for (int i = 1; i < splits.length - 1; i++) { sb.append("/"); sb.append(splits[i]); quotaPath = sb.toString(); List children = null; try { children = zk.getChildren(quotaPath, false); } catch (KeeperException.NoNodeException ne) { LOG.debug("child removed during quota check", ne); return; } if (children.size() == 0) { return; } for (String child : children) { if (!quotaPath.equals(Quotas.quotaPath(path)) && Quotas.limitNode.equals(child)) { throw new IllegalArgumentException(path + " has a parent " + Quotas.trimQuotaPath(quotaPath) + " which has a quota"); } } } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/StatCommand.java0100644 0000000 0000000 00000006126 15051152474 033123 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; /** * stat command for cli */ public class StatCommand extends CliCommand { private static final Options options = new Options(); private String[] args; private CommandLine cl; static { options.addOption("w", false, "watch"); } public StatCommand() { super("stat", "[-w] path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } retainCompatibility(cmdArgs); return this; } private void retainCompatibility(String[] cmdArgs) throws CliParseException { // stat path [watch] if (args.length > 2) { // rewrite to option cmdArgs[2] = "-w"; err.println("'stat path [watch]' has been deprecated. " + "Please use 'stat [-w] path' instead."); DefaultParser parser = new DefaultParser(); try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); } } @Override public boolean exec() throws CliException { String path = args[1]; boolean watch = cl.hasOption("w"); Stat stat; try { stat = zk.exists(path, watch); } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (KeeperException | InterruptedException ex) { throw new CliWrapperException(ex); } if (stat == null) { throw new CliWrapperException(new KeeperException.NoNodeException(path)); } new StatPrinter(out).print(stat); return watch; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/StatPrinter.java0100644 0000000 0000000 00000003555 15051152474 033173 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.io.PrintStream; import java.util.Date; import org.apache.zookeeper.data.Stat; /** * utility for printing stat values s */ public class StatPrinter { protected PrintStream out; public StatPrinter(PrintStream out) { this.out = out; } public void print(Stat stat) { out.println("cZxid = 0x" + Long.toHexString(stat.getCzxid())); out.println("ctime = " + new Date(stat.getCtime()).toString()); out.println("mZxid = 0x" + Long.toHexString(stat.getMzxid())); out.println("mtime = " + new Date(stat.getMtime()).toString()); out.println("pZxid = 0x" + Long.toHexString(stat.getPzxid())); out.println("cversion = " + stat.getCversion()); out.println("dataVersion = " + stat.getVersion()); out.println("aclVersion = " + stat.getAversion()); out.println("ephemeralOwner = 0x" + Long.toHexString(stat.getEphemeralOwner())); out.println("dataLength = " + stat.getDataLength()); out.println("numChildren = " + stat.getNumChildren()); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/SyncCommand.java0100644 0000000 0000000 00000005452 15051152474 033125 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS 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.cli; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; /** * sync command for cli */ public class SyncCommand extends CliCommand { private static Options options = new Options(); private String[] args; public static final long SYNC_TIMEOUT = TimeUnit.SECONDS.toMillis(30L); public SyncCommand() { super("sync", "path"); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); CommandLine cl; try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length < 2) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { String path = args[1]; CompletableFuture cf = new CompletableFuture<>(); try { zk.sync(path, (rc, path1, ctx) -> cf.complete(rc), null); int resultCode = cf.get(SYNC_TIMEOUT, TimeUnit.MILLISECONDS); if (resultCode == 0) { out.println("Sync is OK"); } else { out.println("Sync has failed. rc=" + resultCode); } } catch (IllegalArgumentException ex) { throw new MalformedPathException(ex.getMessage()); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); throw new CliWrapperException(ie); } catch (TimeoutException | ExecutionException ex) { throw new CliWrapperException(ex); } return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/VersionCommand.java0100644 0000000 0000000 00000003576 15051152474 033643 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS 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.cli; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.zookeeper.Version; /** * version command for cli */ public class VersionCommand extends CliCommand { private static Options options = new Options(); private String[] args; public VersionCommand() { super("version", ""); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { DefaultParser parser = new DefaultParser(); CommandLine cl; try { cl = parser.parse(options, cmdArgs); } catch (ParseException ex) { throw new CliParseException(ex); } args = cl.getArgs(); if (args.length > 1) { throw new CliParseException(getUsageStr()); } return this; } @Override public boolean exec() throws CliException { out.println("ZooKeeper CLI version: " + Version.getFullVersion()); return false; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/cli/WhoAmICommand.java0100644 0000000 0000000 00000003363 15051152474 033334 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import java.util.List; import org.apache.zookeeper.data.ClientInfo; /** * WhoAmI command for cli */ public class WhoAmICommand extends CliCommand { public WhoAmICommand() { super("whoami", ""); } @Override public CliCommand parse(String[] cmdArgs) throws CliParseException { return this; } @Override public boolean exec() throws CliException { try { List clientInfos = zk.whoAmI(); out.println("Auth scheme: User"); if (clientInfos != null) { // clientInfos will never be null, added null check to pass static checks clientInfos.forEach(clientInfo -> { out.println(clientInfo.getAuthScheme() + ": " + clientInfo.getUser()); }); } } catch (Exception ex) { throw new CliWrapperException(ex); } return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_client_Co0100644 0000000 0000000 00000000164 15051152474 032604 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/ConnectStringParser.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/ConnectStringParse0100644 0000000 0000000 00000006632 15051152474 034255 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import static org.apache.zookeeper.common.StringUtils.split; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.common.NetUtils; import org.apache.zookeeper.common.PathUtils; /** * A parser for ZooKeeper Client connect strings. * * This class is not meant to be seen or used outside of ZooKeeper itself. * * The chrootPath member should be replaced by a Path object in issue * ZOOKEEPER-849. * * @see org.apache.zookeeper.ZooKeeper */ public final class ConnectStringParser { private static final int DEFAULT_PORT = 2181; private final String chrootPath; private final ArrayList serverAddresses = new ArrayList<>(); /** * Parse host and port by spliting client connectString * with support for IPv6 literals * @throws IllegalArgumentException * for an invalid chroot path. */ public ConnectStringParser(String connectString) { // parse out chroot, if any int off = connectString.indexOf('/'); if (off >= 0) { String chrootPath = connectString.substring(off); // ignore "/" chroot spec, same as null if (chrootPath.length() == 1) { this.chrootPath = null; } else { PathUtils.validatePath(chrootPath); this.chrootPath = chrootPath; } connectString = connectString.substring(0, off); } else { this.chrootPath = null; } List hostsList = split(connectString, ","); for (String host : hostsList) { int port = DEFAULT_PORT; String[] hostAndPort = NetUtils.getIPV6HostAndPort(host); if (hostAndPort.length != 0) { host = hostAndPort[0]; if (hostAndPort.length == 2) { port = Integer.parseInt(hostAndPort[1]); } } else { int pidx = host.lastIndexOf(':'); if (pidx >= 0) { // otherwise : is at the end of the string, ignore if (pidx < host.length() - 1) { port = Integer.parseInt(host.substring(pidx + 1)); } host = host.substring(0, pidx); } } serverAddresses.add(InetSocketAddress.createUnresolved(host, port)); } } public String getChrootPath() { return chrootPath; } public ArrayList getServerAddresses() { return serverAddresses; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_client_Fo0100644 0000000 0000000 00000000163 15051152474 032606 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/FourLetterWordMain.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/FourLetterWordMain0100644 0000000 0000000 00000017226 15051152474 034237 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.common.X509Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @InterfaceAudience.Public public class FourLetterWordMain { //in milliseconds, socket should connect/read within this period otherwise SocketTimeoutException private static final int DEFAULT_SOCKET_TIMEOUT = 5000; protected static final Logger LOG = LoggerFactory.getLogger(FourLetterWordMain.class); /** * Send the 4letterword * @param host the destination host * @param port the destination port * @param cmd the 4letterword * @return server response * @throws java.io.IOException * @throws SSLContextException */ public static String send4LetterWord(String host, int port, String cmd) throws IOException, SSLContextException { return send4LetterWord(host, port, cmd, false, DEFAULT_SOCKET_TIMEOUT); } /** * Send the 4letterword * @param host the destination host * @param port the destination port * @param cmd the 4letterword * @param secure whether to use SSL * @return server response * @throws java.io.IOException * @throws SSLContextException */ public static String send4LetterWord( String host, int port, String cmd, boolean secure) throws IOException, SSLContextException { return send4LetterWord(host, port, cmd, secure, DEFAULT_SOCKET_TIMEOUT); } /** * Send the 4letterword * @param host the destination host * @param port the destination port * @param cmd the 4letterword * @param secure whether to use SSL * @param timeout in milliseconds, maximum time to wait while connecting/reading data * @return server response * @throws java.io.IOException * @throws SSLContextException */ public static String send4LetterWord( String host, int port, String cmd, boolean secure, int timeout) throws IOException, SSLContextException { return send4LetterWord(host, port, cmd, secure, timeout, null); } /** * Send the 4letterword * @param host the destination host * @param port the destination port * @param cmd the 4letterword * @param clientConfig client config * @param timeout in milliseconds, maximum time to wait while connecting/reading data * @return server response * @throws SSLContextException * @throws IOException */ public static String send4LetterWord( String host, int port, String cmd, ZKClientConfig clientConfig, int timeout) throws SSLContextException, IOException { boolean useSecure = clientConfig.getBoolean(ZKClientConfig.SECURE_CLIENT); SSLContext sslContext = null; if (useSecure) { try (X509Util x509Util = new ClientX509Util()) { sslContext = x509Util.createSSLContext(clientConfig); } } return send4LetterWord(host, port, cmd, useSecure, timeout, sslContext); } /** * Send the 4letterword * @param host the destination host * @param port the destination port * @param cmd the 4letterword * @param secure whether to use SSL * @param timeout in milliseconds, maximum time to wait while connecting/reading data * @param sslContext SSL context * @return server response * @throws java.io.IOException * @throws SSLContextException */ public static String send4LetterWord( String host, int port, String cmd, boolean secure, int timeout, SSLContext sslContext) throws IOException, SSLContextException { LOG.info("connecting to {}:{} (secure={})", host, port, secure); Socket sock = null; BufferedReader reader = null; try { InetSocketAddress hostaddress = host != null ? new InetSocketAddress(host, port) : new InetSocketAddress(InetAddress.getByName(null), port); if (secure) { LOG.info("using secure socket"); if (sslContext == null) { try (X509Util x509Util = new ClientX509Util()) { sslContext = x509Util.getDefaultSSLContext(); } } SSLSocketFactory socketFactory = sslContext.getSocketFactory(); SSLSocket sslSock = (SSLSocket) socketFactory.createSocket(); sslSock.connect(hostaddress, timeout); sslSock.startHandshake(); sock = sslSock; } else { sock = new Socket(); sock.connect(hostaddress, timeout); } sock.setSoLinger(false, -1); sock.setSoTimeout(timeout); sock.setTcpNoDelay(true); OutputStream outstream = sock.getOutputStream(); outstream.write(cmd.getBytes(UTF_8)); outstream.flush(); // this replicates NC - close the output stream before reading if (!secure) { // SSL prohibits unilateral half-close sock.shutdownOutput(); } reader = new BufferedReader(new InputStreamReader(sock.getInputStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } return sb.toString(); } catch (SocketTimeoutException e) { throw new IOException("Exception while executing four letter word: " + cmd, e); } finally { if (sock != null) { sock.close(); } if (reader != null) { reader.close(); } } } public static void main(String[] args) throws IOException, SSLContextException { if (args.length == 3) { System.out.println(send4LetterWord(args[0], Integer.parseInt(args[1]), args[2])); } else if (args.length == 4) { System.out.println(send4LetterWord(args[0], Integer.parseInt(args[1]), args[2], Boolean.parseBoolean(args[3]))); } else { System.out.println("Usage: FourLetterWordMain "); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/HostProvider.java0100644 0000000 0000000 00000005277 15051152474 034056 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import java.net.InetSocketAddress; import java.util.Collection; import org.apache.yetus.audience.InterfaceAudience; /** * A set of hosts a ZooKeeper client should connect to. * * Classes implementing this interface must guarantee the following: * * * Every call to next() returns an InetSocketAddress. So the iterator never * ends. * * * The size() of a HostProvider may never be zero. * * A HostProvider must return resolved InetSocketAddress instances on next() if the next address is resolvable. * In that case, it's up to the HostProvider, whether it returns the next resolvable address in the list or return * the next one as UnResolved. * * Different HostProvider could be imagined: * * * A HostProvider that loads the list of Hosts from an URL or from DNS * * A HostProvider that re-resolves the InetSocketAddress after a timeout. * * A HostProvider that prefers nearby hosts. */ @InterfaceAudience.Public public interface HostProvider { int size(); /** * The next host to try to connect to. * * For a spinDelay of 0 there should be no wait. * * @param spinDelay * Milliseconds to wait if all hosts have been tried once. */ InetSocketAddress next(long spinDelay); /** * Notify the HostProvider of a successful connection. * * The HostProvider may use this notification to reset it's inner state. */ void onConnected(); /** * Update the list of servers. This returns true if changing connections is necessary for load-balancing, false otherwise. * @param serverAddresses new host list * @param currentHost the host to which this client is currently connected * @return true if changing connections is necessary for load-balancing, false otherwise */ boolean updateServerList(Collection serverAddresses, InetSocketAddress currentHost); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_client_St0100644 0000000 0000000 00000000163 15051152474 032630 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/StaticHostProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/StaticHostProvider0100644 0000000 0000000 00000035435 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Random; import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Most simple HostProvider, resolves on every next() call. * * Please be aware that although this class doesn't do any DNS caching, there're multiple levels of caching already * present across the stack like in JVM, OS level, hardware, etc. The best we could do here is to get the most recent * address from the underlying system which is considered up-to-date. * */ @InterfaceAudience.Public public final class StaticHostProvider implements HostProvider { public interface Resolver { InetAddress[] getAllByName(String name) throws UnknownHostException; } private static final Logger LOG = LoggerFactory.getLogger(StaticHostProvider.class); private List serverAddresses = new ArrayList<>(5); private Random sourceOfRandomness; private int lastIndex = -1; private int currentIndex = -1; /** * The following fields are used to migrate clients during reconfiguration */ private boolean reconfigMode = false; private final List oldServers = new ArrayList<>(5); private final List newServers = new ArrayList<>(5); private int currentIndexOld = -1; private int currentIndexNew = -1; private float pOld, pNew; private Resolver resolver; /** * Constructs a SimpleHostSet. * * @param serverAddresses * possibly unresolved ZooKeeper server addresses * @throws IllegalArgumentException * if serverAddresses is empty or resolves to an empty list */ public StaticHostProvider(Collection serverAddresses) { init(serverAddresses, System.currentTimeMillis() ^ this.hashCode(), new Resolver() { @Override public InetAddress[] getAllByName(String name) throws UnknownHostException { return InetAddress.getAllByName(name); } }); } /** * Constructs a SimpleHostSet. * * Introduced for testing purposes. getAllByName() is a static method of InetAddress, therefore cannot be easily mocked. * By abstraction of Resolver interface we can easily inject a mocked implementation in tests. * * @param serverAddresses * possibly unresolved ZooKeeper server addresses * @param resolver * custom resolver implementation */ public StaticHostProvider(Collection serverAddresses, Resolver resolver) { init(serverAddresses, System.currentTimeMillis() ^ this.hashCode(), resolver); } /** * Constructs a SimpleHostSet. This constructor is used from StaticHostProviderTest to produce deterministic test results * by initializing sourceOfRandomness with the same seed * * @param serverAddresses * possibly unresolved ZooKeeper server addresses * @param randomnessSeed a seed used to initialize sourceOfRandomnes * @throws IllegalArgumentException * if serverAddresses is empty or resolves to an empty list */ public StaticHostProvider(Collection serverAddresses, long randomnessSeed) { init(serverAddresses, randomnessSeed, new Resolver() { @Override public InetAddress[] getAllByName(String name) throws UnknownHostException { return InetAddress.getAllByName(name); } }); } private void init(Collection serverAddresses, long randomnessSeed, Resolver resolver) { this.sourceOfRandomness = new Random(randomnessSeed); this.resolver = resolver; if (serverAddresses.isEmpty()) { throw new IllegalArgumentException("A HostProvider may not be empty!"); } this.serverAddresses = shuffle(serverAddresses); currentIndex = -1; lastIndex = -1; } private InetSocketAddress resolve(InetSocketAddress address) { try { String curHostString = address.getHostString(); List resolvedAddresses = new ArrayList<>(Arrays.asList(this.resolver.getAllByName(curHostString))); if (resolvedAddresses.isEmpty()) { return address; } Collections.shuffle(resolvedAddresses); return new InetSocketAddress(resolvedAddresses.get(0), address.getPort()); } catch (UnknownHostException e) { LOG.error("Unable to resolve address: {}", address.toString(), e); return address; } } private List shuffle(Collection serverAddresses) { List tmpList = new ArrayList<>(serverAddresses.size()); tmpList.addAll(serverAddresses); Collections.shuffle(tmpList, sourceOfRandomness); return tmpList; } /** * Update the list of servers. This returns true if changing connections is necessary for load-balancing, false * otherwise. Changing connections is necessary if one of the following holds: * a) the host to which this client is currently connected is not in serverAddresses. * Otherwise (if currentHost is in the new list serverAddresses): * b) the number of servers in the cluster is increasing - in this case the load on currentHost should decrease, * which means that SOME of the clients connected to it will migrate to the new servers. The decision whether * this client migrates or not (i.e., whether true or false is returned) is probabilistic so that the expected * number of clients connected to each server is the same. * * If true is returned, the function sets pOld and pNew that correspond to the probability to migrate to ones of the * new servers in serverAddresses or one of the old servers (migrating to one of the old servers is done only * if our client's currentHost is not in serverAddresses). See nextHostInReconfigMode for the selection logic. * * See ZOOKEEPER-1355 * for the protocol and its evaluation, and StaticHostProviderTest for the tests that illustrate how load balancing * works with this policy. * * @param serverAddresses new host list * @param currentHost the host to which this client is currently connected * @return true if changing connections is necessary for load-balancing, false otherwise */ @Override public synchronized boolean updateServerList( Collection serverAddresses, InetSocketAddress currentHost) { List shuffledList = shuffle(serverAddresses); if (shuffledList.isEmpty()) { throw new IllegalArgumentException("A HostProvider may not be empty!"); } // Check if client's current server is in the new list of servers boolean myServerInNewConfig = false; InetSocketAddress myServer = currentHost; // choose "current" server according to the client rebalancing algorithm if (reconfigMode) { myServer = next(0); } // if the client is not currently connected to any server if (myServer == null) { // reconfigMode = false (next shouldn't return null). if (lastIndex >= 0) { // take the last server to which we were connected myServer = this.serverAddresses.get(lastIndex); } else { // take the first server on the list myServer = this.serverAddresses.get(0); } } for (InetSocketAddress addr : shuffledList) { if (addr.getPort() == myServer.getPort() && ((addr.getAddress() != null && myServer.getAddress() != null && addr.getAddress().equals(myServer.getAddress())) || addr.getHostString().equals(myServer.getHostString()))) { myServerInNewConfig = true; break; } } reconfigMode = true; newServers.clear(); oldServers.clear(); // Divide the new servers into oldServers that were in the previous list // and newServers that were not in the previous list for (InetSocketAddress address : shuffledList) { if (this.serverAddresses.contains(address)) { oldServers.add(address); } else { newServers.add(address); } } int numOld = oldServers.size(); int numNew = newServers.size(); // number of servers increased if (numOld + numNew > this.serverAddresses.size()) { if (myServerInNewConfig) { // my server is in new config, but load should be decreased. // Need to decide if this client // is moving to one of the new servers if (sourceOfRandomness.nextFloat() <= (1 - ((float) this.serverAddresses.size()) / (numOld + numNew))) { pNew = 1; pOld = 0; } else { // do nothing special - stay with the current server reconfigMode = false; } } else { // my server is not in new config, and load on old servers must // be decreased, so connect to // one of the new servers pNew = 1; pOld = 0; } } else { // number of servers stayed the same or decreased if (myServerInNewConfig) { // my server is in new config, and load should be increased, so // stay with this server and do nothing special reconfigMode = false; } else { pOld = ((float) (numOld * (this.serverAddresses.size() - (numOld + numNew)))) / ((numOld + numNew) * (this.serverAddresses.size() - numOld)); pNew = 1 - pOld; } } if (!reconfigMode) { currentIndex = shuffledList.indexOf(getServerAtCurrentIndex()); } else { currentIndex = -1; } this.serverAddresses = shuffledList; currentIndexOld = -1; currentIndexNew = -1; lastIndex = currentIndex; return reconfigMode; } public synchronized InetSocketAddress getServerAtIndex(int i) { if (i < 0 || i >= serverAddresses.size()) { return null; } return serverAddresses.get(i); } public synchronized InetSocketAddress getServerAtCurrentIndex() { return getServerAtIndex(currentIndex); } public synchronized int size() { return serverAddresses.size(); } /** * Get the next server to connect to, when in "reconfigMode", which means that * you've just updated the server list, and now trying to find some server to connect to. * Once onConnected() is called, reconfigMode is set to false. Similarly, if we tried to connect * to all servers in new config and failed, reconfigMode is set to false. * * While in reconfigMode, we should connect to a server in newServers with probability pNew and to servers in * oldServers with probability pOld (which is just 1-pNew). If we tried out all servers in either oldServers * or newServers we continue to try servers from the other set, regardless of pNew or pOld. If we tried all servers * we give up and go back to the normal round robin mode * * When called, this should be protected by synchronized(this) */ private InetSocketAddress nextHostInReconfigMode() { boolean takeNew = (sourceOfRandomness.nextFloat() <= pNew); // take one of the new servers if it is possible (there are still such // servers we didn't try), // and either the probability tells us to connect to one of the new // servers or if we already // tried all the old servers if (((currentIndexNew + 1) < newServers.size()) && (takeNew || (currentIndexOld + 1) >= oldServers.size())) { ++currentIndexNew; return newServers.get(currentIndexNew); } // start taking old servers if ((currentIndexOld + 1) < oldServers.size()) { ++currentIndexOld; return oldServers.get(currentIndexOld); } return null; } public InetSocketAddress next(long spinDelay) { boolean needToSleep = false; InetSocketAddress addr; synchronized (this) { if (reconfigMode) { addr = nextHostInReconfigMode(); if (addr != null) { currentIndex = serverAddresses.indexOf(addr); return resolve(addr); } //tried all servers and couldn't connect reconfigMode = false; needToSleep = (spinDelay > 0); } ++currentIndex; if (currentIndex == serverAddresses.size()) { currentIndex = 0; } addr = serverAddresses.get(currentIndex); needToSleep = needToSleep || (currentIndex == lastIndex && spinDelay > 0); if (lastIndex == -1) { // We don't want to sleep on the first ever connect attempt. lastIndex = 0; } } if (needToSleep) { try { Thread.sleep(spinDelay); } catch (InterruptedException e) { LOG.warn("Unexpected exception", e); } } return resolve(addr); } public synchronized void onConnected() { lastIndex = currentIndex; reconfigMode = false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_client_ZK0100644 0000000 0000000 00000000157 15051152474 032571 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZKClientConfig.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZKClientConfig.jav0100644 0000000 0000000 00000014104 15051152474 034063 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import java.io.File; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; /** * Handles client specific properties * @since 3.5.2 */ @InterfaceAudience.Public public class ZKClientConfig extends ZKConfig { public static final String ZK_SASL_CLIENT_USERNAME = "zookeeper.sasl.client.username"; public static final String ZK_SASL_CLIENT_USERNAME_DEFAULT = "zookeeper"; public static final String ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME = "zookeeper.sasl.client.canonicalize.hostname"; public static final String ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME_DEFAULT = "true"; @SuppressWarnings("deprecation") public static final String LOGIN_CONTEXT_NAME_KEY = ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY; public static final String LOGIN_CONTEXT_NAME_KEY_DEFAULT = "Client"; @SuppressWarnings("deprecation") public static final String ENABLE_CLIENT_SASL_KEY = ZooKeeperSaslClient.ENABLE_CLIENT_SASL_KEY; @SuppressWarnings("deprecation") public static final String ENABLE_CLIENT_SASL_DEFAULT = ZooKeeperSaslClient.ENABLE_CLIENT_SASL_DEFAULT; public static final String ZOOKEEPER_SERVER_REALM = "zookeeper.server.realm"; /** * This controls whether automatic watch resetting is enabled. Clients * automatically reset watches during session reconnect, this option allows * the client to turn off this behavior by setting the property * "zookeeper.disableAutoWatchReset" to "true" */ public static final String DISABLE_AUTO_WATCH_RESET = "zookeeper.disableAutoWatchReset"; @SuppressWarnings("deprecation") public static final String ZOOKEEPER_CLIENT_CNXN_SOCKET = ZooKeeper.ZOOKEEPER_CLIENT_CNXN_SOCKET; /** * Setting this to "true" will enable encrypted client-server communication. */ @SuppressWarnings("deprecation") public static final String SECURE_CLIENT = ZooKeeper.SECURE_CLIENT; public static final int CLIENT_MAX_PACKET_LENGTH_DEFAULT = 0xfffff; /* 1 MB */ public static final String ZOOKEEPER_REQUEST_TIMEOUT = "zookeeper.request.timeout"; public static final String ZOOKEEPER_SERVER_PRINCIPAL = "zookeeper.server.principal"; /** * Feature is disabled by default. */ public static final long ZOOKEEPER_REQUEST_TIMEOUT_DEFAULT = 0; public ZKClientConfig() { super(); initFromJavaSystemProperties(); } public ZKClientConfig(File configFile) throws ConfigException { super(configFile); } public ZKClientConfig(String configPath) throws ConfigException { super(configPath); } /** * Initialize all the ZooKeeper client properties which are configurable as * java system property */ private void initFromJavaSystemProperties() { setProperty(ZOOKEEPER_REQUEST_TIMEOUT, System.getProperty(ZOOKEEPER_REQUEST_TIMEOUT)); setProperty(ZOOKEEPER_SERVER_PRINCIPAL, System.getProperty(ZOOKEEPER_SERVER_PRINCIPAL)); } @Override protected void handleBackwardCompatibility() { /** * backward compatibility for properties which are common to both client * and server */ super.handleBackwardCompatibility(); /** * backward compatibility for client specific properties */ setProperty(ZK_SASL_CLIENT_USERNAME, System.getProperty(ZK_SASL_CLIENT_USERNAME)); setProperty(ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME, System.getProperty(ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME)); setProperty(LOGIN_CONTEXT_NAME_KEY, System.getProperty(LOGIN_CONTEXT_NAME_KEY)); setProperty(ENABLE_CLIENT_SASL_KEY, System.getProperty(ENABLE_CLIENT_SASL_KEY)); setProperty(ZOOKEEPER_SERVER_REALM, System.getProperty(ZOOKEEPER_SERVER_REALM)); setProperty(DISABLE_AUTO_WATCH_RESET, System.getProperty(DISABLE_AUTO_WATCH_RESET)); setProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET, System.getProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET)); setProperty(SECURE_CLIENT, System.getProperty(SECURE_CLIENT)); } /** * Returns true if the SASL client is enabled. By default, the client is * enabled but can be disabled by setting the system property * zookeeper.sasl.client to false. See * ZOOKEEPER-1657 for more information. * * @return true if the SASL client is enabled. */ public boolean isSaslClientEnabled() { return Boolean.valueOf(getProperty(ENABLE_CLIENT_SASL_KEY, ENABLE_CLIENT_SASL_DEFAULT)); } /** * Get the value of the key property as an long. * If property is not set, the provided defaultValue is * returned * * @param key * property key. * @param defaultValue * default value. * @throws NumberFormatException * when the value is invalid * @return return property value as an long, or * defaultValue */ public long getLong(String key, long defaultValue) { String value = getProperty(key); if (value != null) { return Long.parseLong(value.trim()); } return defaultValue; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_client_Zo0100644 0000000 0000000 00000000164 15051152474 032633 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperSaslClient.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/client/ZooKeeperSaslClien0100644 0000000 0000000 00000050725 15051152474 034205 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import java.io.IOException; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.Login; import org.apache.zookeeper.SaslClientCallbackHandler; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.SetSASLResponse; import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class manages SASL authentication for the client. It * allows ClientCnxn to authenticate using SASL with a ZooKeeper server. */ public class ZooKeeperSaslClient { /** * @deprecated Use {@link ZKClientConfig#LOGIN_CONTEXT_NAME_KEY} * instead. */ @Deprecated public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.clientconfig"; /** * @deprecated Use {@link ZKClientConfig#ENABLE_CLIENT_SASL_KEY} * instead. */ @Deprecated public static final String ENABLE_CLIENT_SASL_KEY = "zookeeper.sasl.client"; /** * @deprecated Use {@link ZKClientConfig#ENABLE_CLIENT_SASL_DEFAULT} * instead. */ @Deprecated public static final String ENABLE_CLIENT_SASL_DEFAULT = "true"; /** * Returns true if the SASL client is enabled. By default, the client * is enabled but can be disabled by setting the system property * zookeeper.sasl.client to false. See * ZOOKEEPER-1657 for more information. * * @return true if the SASL client is enabled. * @deprecated Use {@link ZKClientConfig#isSaslClientEnabled} instead */ @Deprecated public static boolean isEnabled() { return Boolean.parseBoolean(System.getProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, ZKClientConfig.ENABLE_CLIENT_SASL_DEFAULT)); } private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslClient.class); private Login login = null; private SaslClient saslClient; private boolean isSASLConfigured = true; private final ZKClientConfig clientConfig; private byte[] saslToken = new byte[0]; public enum SaslState { INITIAL, INTERMEDIATE, COMPLETE, FAILED } private SaslState saslState = SaslState.INITIAL; private boolean gotLastPacket = false; /** informational message indicating the current configuration status */ private final String configStatus; public SaslState getSaslState() { return saslState; } public String getLoginContext() { if (login != null) { return login.getLoginContextName(); } return null; } public ZooKeeperSaslClient(final String serverPrincipal, ZKClientConfig clientConfig, AtomicReference loginRef) throws LoginException { /** * ZOOKEEPER-1373: allow system property to specify the JAAS * configuration section that the zookeeper client should use. * Default to "Client". */ String clientSection = clientConfig.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT); this.clientConfig = clientConfig; // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. AppConfigurationEntry[] entries = null; RuntimeException runtimeException = null; try { entries = Configuration.getConfiguration().getAppConfigurationEntry(clientSection); } catch (SecurityException e) { // handle below: might be harmless if the user doesn't intend to use JAAS authentication. runtimeException = e; } catch (IllegalArgumentException e) { // third party customized getAppConfigurationEntry could throw IllegalArgumentException when JAAS // configuration isn't set. We can reevaluate whether to catch RuntimeException instead when more // different types of RuntimeException found runtimeException = e; } if (entries != null) { this.configStatus = "Will attempt to SASL-authenticate using Login Context section '" + clientSection + "'"; this.saslClient = createSaslClient(serverPrincipal, clientSection, loginRef); this.login = loginRef.get(); } else { // Handle situation of clientSection's being null: it might simply because the client does not intend to // use SASL, so not necessarily an error. saslState = SaslState.FAILED; String explicitClientSection = clientConfig.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY); if (explicitClientSection != null) { // If the user explicitly overrides the default Login Context, they probably expected SASL to // succeed. But if we got here, SASL failed. if (runtimeException != null) { throw new LoginException("Zookeeper client cannot authenticate using the " + explicitClientSection + " section of the supplied JAAS configuration: '" + clientConfig.getJaasConfKey() + "' because of a " + "RuntimeException: " + runtimeException); } else { throw new LoginException("Client cannot SASL-authenticate because the specified JAAS configuration " + "section '" + explicitClientSection + "' could not be found."); } } else { // The user did not override the default context. It might be that they just don't intend to use SASL, // so log at INFO, not WARN, since they don't expect any SASL-related information. String msg = "Will not attempt to authenticate using SASL "; if (runtimeException != null) { msg += "(" + runtimeException + ")"; } else { msg += "(unknown error)"; } this.configStatus = msg; this.isSASLConfigured = false; } if (clientConfig.getJaasConfKey() != null) { // Again, the user explicitly set something SASL-related, so // they probably expected SASL to succeed. if (runtimeException != null) { throw new LoginException("Zookeeper client cannot authenticate using the '" + clientConfig.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT) + "' section of the supplied JAAS configuration: '" + clientConfig.getJaasConfKey() + "' because of a " + "RuntimeException: " + runtimeException); } else { throw new LoginException("No JAAS configuration section named '" + clientConfig.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT) + "' was found in specified JAAS configuration file: '" + clientConfig.getJaasConfKey() + "'."); } } } } /** * @return informational message indicating the current configuration status. */ public String getConfigStatus() { return configStatus; } public boolean isComplete() { return (saslState == SaslState.COMPLETE); } public boolean isFailed() { return (saslState == SaslState.FAILED); } public static class ServerSaslResponseCallback implements AsyncCallback.DataCallback { public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { // processResult() is used by ClientCnxn's sendThread to respond to // data[] contains the Zookeeper Server's SASL token. // ctx is the ZooKeeperSaslClient object. We use this object's respondToServer() method // to reply to the Zookeeper Server's SASL token ZooKeeperSaslClient client = ((ClientCnxn) ctx).getZooKeeperSaslClient(); if (client == null) { LOG.warn("sasl client was unexpectedly null: cannot respond to Zookeeper server."); return; } byte[] usedata = data; if (data != null) { LOG.debug("ServerSaslResponseCallback(): saslToken server response: (length={})", usedata.length); } else { usedata = new byte[0]; LOG.debug("ServerSaslResponseCallback(): using empty data[] as server response (length={})", usedata.length); } client.respondToServer(usedata, (ClientCnxn) ctx); } } private SaslClient createSaslClient( final String servicePrincipal, final String loginContext, final AtomicReference loginRef) throws LoginException { try { if (loginRef.get() == null) { LOG.debug("JAAS loginContext is: {}", loginContext); Supplier callbackHandlerSupplier = () -> { return new SaslClientCallbackHandler(null, "Client"); }; Login l = new Login(loginContext, callbackHandlerSupplier, clientConfig); if (loginRef.compareAndSet(null, l)) { l.startThreadIfNeeded(); } } return SecurityUtils.createSaslClient(clientConfig, loginRef.get().getSubject(), servicePrincipal, "zookeeper", "zk-sasl-md5", LOG, "Client"); } catch (LoginException e) { // We throw LoginExceptions... throw e; } catch (Exception e) { // ...but consume (with a log message) all other types of exceptions. LOG.error("Exception while trying to create SASL client.", e); return null; } } public void respondToServer(byte[] serverToken, ClientCnxn cnxn) { if (saslClient == null) { LOG.error("saslClient is unexpectedly null. Cannot respond to server's SASL message; ignoring."); return; } if (!(saslClient.isComplete())) { try { saslToken = createSaslToken(serverToken); if (saslToken != null) { sendSaslPacket(saslToken, cnxn); } } catch (SaslException e) { LOG.error( "SASL authentication failed using login context '{}'.", this.getLoginContext(), e); saslState = SaslState.FAILED; gotLastPacket = true; } } if (saslClient.isComplete()) { // GSSAPI: server sends a final packet after authentication succeeds // or fails. if ((serverToken == null) && (saslClient.getMechanismName().equals("GSSAPI"))) { gotLastPacket = true; } // non-GSSAPI: no final packet from server. if (!saslClient.getMechanismName().equals("GSSAPI")) { gotLastPacket = true; } // SASL authentication is completed, successfully or not: // enable the socket's writable flag so that any packets waiting for authentication to complete in // the outgoing queue will be sent to the Zookeeper server. cnxn.saslCompleted(); } } private byte[] createSaslToken() throws SaslException { saslState = SaslState.INTERMEDIATE; return createSaslToken(saslToken); } private byte[] createSaslToken(final byte[] saslToken) throws SaslException { if (saslToken == null) { // TODO: introspect about runtime environment (such as jaas.conf) saslState = SaslState.FAILED; throw new SaslException("Error in authenticating with a Zookeeper Quorum member: the quorum member's saslToken is null."); } Subject subject = login.getSubject(); if (subject != null) { synchronized (login) { try { final byte[] retval = Subject.doAs(subject, new PrivilegedExceptionAction() { public byte[] run() throws SaslException { LOG.debug("saslClient.evaluateChallenge(len={})", saslToken.length); return saslClient.evaluateChallenge(saslToken); } }); return retval; } catch (PrivilegedActionException e) { String error = "An error: (" + e + ") occurred when evaluating Zookeeper Quorum Member's " + " received SASL token."; // Try to provide hints to use about what went wrong so they can fix their configuration. // TODO: introspect about e: look for GSS information. final String UNKNOWN_SERVER_ERROR_TEXT = "(Mechanism level: Server not found in Kerberos database (7) - UNKNOWN_SERVER)"; if (e.toString().contains(UNKNOWN_SERVER_ERROR_TEXT)) { error += " This may be caused by Java's being unable to resolve the Zookeeper Quorum Member's" + " hostname correctly. You may want to try to adding" + " '-Dsun.net.spi.nameservice.provider.1=dns,sun' to your client's JVMFLAGS environment."; } error += " Zookeeper Client will go to AUTH_FAILED state."; LOG.error(error); saslState = SaslState.FAILED; throw new SaslException(error, e); } } } else { throw new SaslException("Cannot make SASL token without subject defined. " + "For diagnosis, please look for WARNs and ERRORs in your log related to the Login class."); } } private void sendSaslPacket(byte[] saslToken, ClientCnxn cnxn) throws SaslException { LOG.debug("ClientCnxn:sendSaslPacket:length={}", saslToken.length); GetSASLRequest request = new GetSASLRequest(); request.setToken(saslToken); SetSASLResponse response = new SetSASLResponse(); ServerSaslResponseCallback cb = new ServerSaslResponseCallback(); try { cnxn.sendPacket(request, response, cb, ZooDefs.OpCode.sasl); } catch (IOException e) { throw new SaslException("Failed to send SASL packet to server.", e); } } private void sendSaslPacket(ClientCnxn cnxn) throws SaslException { LOG.debug("ClientCnxn:sendSaslPacket:length={}", saslToken.length); GetSASLRequest request = new GetSASLRequest(); request.setToken(createSaslToken()); SetSASLResponse response = new SetSASLResponse(); ServerSaslResponseCallback cb = new ServerSaslResponseCallback(); try { cnxn.sendPacket(request, response, cb, ZooDefs.OpCode.sasl); } catch (IOException e) { throw new SaslException("Failed to send SASL packet to server due " + "to IOException:", e); } } // used by ClientCnxn to know whether to emit a SASL-related event: either AuthFailed or SaslAuthenticated, // or none, if not ready yet. Sets saslState to COMPLETE as a side-effect. public KeeperState getKeeperState() { if (saslClient != null) { if (saslState == SaslState.FAILED) { return KeeperState.AuthFailed; } if (saslClient.isComplete()) { if (saslState == SaslState.INTERMEDIATE) { saslState = SaslState.COMPLETE; return KeeperState.SaslAuthenticated; } } } // No event ready to emit yet. return null; } // Initialize the client's communications with the Zookeeper server by sending the server the first // authentication packet. public void initialize(ClientCnxn cnxn) throws SaslException { if (saslClient == null) { saslState = SaslState.FAILED; throw new SaslException("saslClient failed to initialize properly: it's null."); } if (saslState == SaslState.INITIAL) { if (saslClient.hasInitialResponse()) { sendSaslPacket(cnxn); } else { byte[] emptyToken = new byte[0]; sendSaslPacket(emptyToken, cnxn); } saslState = SaslState.INTERMEDIATE; } } public boolean clientTunneledAuthenticationInProgress() { if (!isSASLConfigured) { return false; } // TODO: Rather than checking a disjunction here, should be a single member // variable or method in this class to determine whether the client is // configured to use SASL. (see also ZOOKEEPER-1455). try { if ((clientConfig.getJaasConfKey() != null) || ((Configuration.getConfiguration() != null) && (Configuration.getConfiguration().getAppConfigurationEntry( clientConfig.getProperty( ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT)) != null))) { // Client is configured to use a valid login Configuration, so // authentication is either in progress, successful, or failed. // 1. Authentication hasn't finished yet: we must wait for it to do so. if (!isComplete() && !isFailed()) { return true; } // 2. SASL authentication has succeeded or failed.. //noinspection RedundantIfStatement if (!gotLastPacket) { // ..but still in progress, because there is a final SASL // message from server which must be received. return true; } } // Either client is not configured to use a tunnelled authentication // scheme, or tunnelled authentication has completed (successfully or // not), and all server SASL messages have been received. return false; } catch (SecurityException e) { // Thrown if the caller does not have permission to retrieve the Configuration. // In this case, simply returning false is correct. LOG.debug("Could not retrieve login configuration", e); return false; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_At0100644 0000000 0000000 00000000167 15051152474 032624 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileOutputStream.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileOutputSt0100644 0000000 0000000 00000010603 15051152474 034251 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * This code is originally from HDFS, see the similarly named files there * in case of bug fixing, history, etc... */ /** * A FileOutputStream that has the property that it will only show up at its * destination once it has been entirely written and flushed to disk. While * being written, it will use a .tmp suffix. * * When the output stream is closed, it is flushed, fsynced, and will be moved * into place, overwriting any file that already exists at that location. * * NOTE: on Windows platforms, it will not atomically replace the target * file - instead the target file is deleted before this one is moved into * place. */ public class AtomicFileOutputStream extends FilterOutputStream { public static final String TMP_EXTENSION = ".tmp"; private static final Logger LOG = LoggerFactory.getLogger(AtomicFileOutputStream.class); private final File origFile; private final File tmpFile; public AtomicFileOutputStream(File f) throws FileNotFoundException { // Code unfortunately must be duplicated below since we can't assign // anything // before calling super super(new FileOutputStream(new File(f.getParentFile(), f.getName() + TMP_EXTENSION))); origFile = f.getAbsoluteFile(); tmpFile = new File(f.getParentFile(), f.getName() + TMP_EXTENSION).getAbsoluteFile(); } /** * The default write method in FilterOutputStream does not call the write * method of its underlying input stream with the same arguments. Instead * it writes the data byte by byte, override it here to make it more * efficient. */ @Override public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); } @Override public void close() throws IOException { boolean triedToClose = false, success = false; try { flush(); ((FileOutputStream) out).getFD().sync(); triedToClose = true; super.close(); success = true; } finally { if (success) { boolean renamed = tmpFile.renameTo(origFile); if (!renamed) { // On windows, renameTo does not replace. if (!origFile.delete() || !tmpFile.renameTo(origFile)) { throw new IOException("Could not rename temporary file " + tmpFile + " to " + origFile); } } } else { if (!triedToClose) { // If we failed when flushing, try to close it to not leak // an FD IOUtils.closeStream(out); } // close wasn't successful, try to delete the tmp file if (!tmpFile.delete()) { LOG.warn("Unable to delete tmp file {}", tmpFile); } } } } /** * Close the atomic file, but do not "commit" the temporary file on top of * the destination. This should be used if there is a failure in writing. */ public void abort() { try { super.close(); } catch (IOException ioe) { LOG.warn("Unable to abort file {}", tmpFile, ioe); } if (!tmpFile.delete()) { LOG.warn("Unable to delete tmp file during abort {}", tmpFile); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_At0100644 0000000 0000000 00000000167 15051152474 032624 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileWritingIdiom.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/AtomicFileWritingI0100644 0000000 0000000 00000006100 15051152474 034173 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; /* * Used to perform an atomic write into a file. * If there is a failure in the middle of the writing operation, * the original file (if it exists) is left intact. * Based on the org.apache.zookeeper.server.quorum.QuorumPeer.writeLongToFile(...) idiom * using the HDFS AtomicFileOutputStream class. */ public class AtomicFileWritingIdiom { public interface OutputStreamStatement { void write(OutputStream os) throws IOException; } public interface WriterStatement { void write(Writer os) throws IOException; } public AtomicFileWritingIdiom(File targetFile, OutputStreamStatement osStmt) throws IOException { this(targetFile, osStmt, null); } public AtomicFileWritingIdiom(File targetFile, WriterStatement wStmt) throws IOException { this(targetFile, null, wStmt); } private AtomicFileWritingIdiom( File targetFile, OutputStreamStatement osStmt, WriterStatement wStmt) throws IOException { AtomicFileOutputStream out = null; boolean triedToClose = false; try { out = new AtomicFileOutputStream(targetFile); if (wStmt == null) { // execute output stream operation osStmt.write(out); } else { BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out)); // execute writer operation and flush wStmt.write(bw); bw.flush(); } triedToClose = true; // close() will do the best to clean up file/resources in case of errors // worst case the tmp file may still exist out.close(); // everything went ok } finally { // nothing interesting to do if out == null if (out != null) { if (!triedToClose) { // worst case here the tmp file/resources(fd) are not cleaned up // and the caller will be notified (IOException) out.abort(); } } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_BC0100644 0000000 0000000 00000000160 15051152474 032535 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/BCFKSFileLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/BCFKSFileLoader.ja0100644 0000000 0000000 00000003062 15051152474 033656 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * Implementation of {@link FileKeyStoreLoader} that loads from BCKFS files. */ class BCFKSFileLoader extends StandardTypeFileKeyStoreLoader { private BCFKSFileLoader(String keyStorePath, String trustStorePath, String keyStorePassword, String trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.BCFKS); } static class Builder extends FileKeyStoreLoader.Builder { @Override BCFKSFileLoader build() { return new BCFKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Cl0100644 0000000 0000000 00000000157 15051152474 032615 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ClientX509Util.jav0100644 0000000 0000000 00000022113 15051152474 033725 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import io.netty.handler.ssl.DelegatingSslContext; import io.netty.handler.ssl.OpenSsl; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslProvider; import java.util.Arrays; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLParameters; import javax.net.ssl.TrustManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * X509 utilities specific for client-server communication framework. */ public class ClientX509Util extends X509Util { private static final Logger LOG = LoggerFactory.getLogger(ClientX509Util.class); private final String sslAuthProviderProperty = getConfigPrefix() + "authProvider"; private final String sslProviderProperty = getConfigPrefix() + "sslProvider"; @Override protected String getConfigPrefix() { return "zookeeper.ssl."; } @Override protected boolean shouldVerifyClientHostname() { return false; } public String getSslAuthProviderProperty() { return sslAuthProviderProperty; } public String getSslProviderProperty() { return sslProviderProperty; } public SslContext createNettySslContextForClient(ZKConfig config) throws X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), getSslKeystorePasswdPathProperty()); String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); SslContextBuilder sslContextBuilder = SslContextBuilder.forClient(); if (keyStoreLocation.isEmpty()) { LOG.warn("{} not specified", getSslKeystoreLocationProperty()); } else { sslContextBuilder.keyManager(createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType)); } TrustManager tm = getTrustManager(config); if (tm != null) { sslContextBuilder.trustManager(tm); } handleTcnativeOcspStapling(sslContextBuilder, config); sslContextBuilder.protocols(getEnabledProtocols(config)); Iterable enabledCiphers = getCipherSuites(config); if (enabledCiphers != null) { sslContextBuilder.ciphers(enabledCiphers); } sslContextBuilder.sslProvider(getSslProvider(config)); SslContext sslContext1 = sslContextBuilder.build(); if ((getFipsMode(config) || tm == null) && isServerHostnameVerificationEnabled(config)) { return addHostnameVerification(sslContext1, "Server"); } else { return sslContext1; } } public SslContext createNettySslContextForServer(ZKConfig config) throws X509Exception.SSLContextException, X509Exception.KeyManagerException, X509Exception.TrustManagerException, SSLException { String keyStoreLocation = config.getProperty(getSslKeystoreLocationProperty(), ""); String keyStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslKeystorePasswdProperty(), getSslKeystorePasswdPathProperty()); String keyStoreType = config.getProperty(getSslKeystoreTypeProperty()); if (keyStoreLocation.isEmpty()) { throw new X509Exception.SSLContextException( "Keystore is required for SSL server: " + getSslKeystoreLocationProperty()); } KeyManager km = createKeyManager(keyStoreLocation, keyStorePassword, keyStoreType); return createNettySslContextForServer(config, km, getTrustManager(config)); } public SslContext createNettySslContextForServer(ZKConfig config, KeyManager keyManager, TrustManager trustManager) throws SSLException { SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(keyManager); if (trustManager != null) { sslContextBuilder.trustManager(trustManager); } handleTcnativeOcspStapling(sslContextBuilder, config); sslContextBuilder.protocols(getEnabledProtocols(config)); sslContextBuilder.clientAuth(getClientAuth(config).toNettyClientAuth()); Iterable enabledCiphers = getCipherSuites(config); if (enabledCiphers != null) { sslContextBuilder.ciphers(enabledCiphers); } sslContextBuilder.sslProvider(getSslProvider(config)); SslContext sslContext1 = sslContextBuilder.build(); if ((getFipsMode(config) || trustManager == null) && isClientHostnameVerificationEnabled(config)) { return addHostnameVerification(sslContext1, "Client"); } else { return sslContext1; } } private SslContextBuilder handleTcnativeOcspStapling(SslContextBuilder builder, ZKConfig config) { SslProvider sslProvider = getSslProvider(config); boolean tcnative = sslProvider == SslProvider.OPENSSL || sslProvider == SslProvider.OPENSSL_REFCNT; boolean ocspEnabled = config.getBoolean(getSslOcspEnabledProperty()); if (tcnative && ocspEnabled && OpenSsl.isOcspSupported()) { builder.enableOcsp(ocspEnabled); } return builder; } private SslContext addHostnameVerification(SslContext sslContext, String clientOrServer) { return new DelegatingSslContext(sslContext) { @Override protected void initEngine(SSLEngine sslEngine) { SSLParameters sslParameters = sslEngine.getSSLParameters(); sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); sslEngine.setSSLParameters(sslParameters); if (LOG.isDebugEnabled()) { LOG.debug("{} hostname verification: enabled HTTPS style endpoint identification algorithm", clientOrServer); } } }; } private String[] getEnabledProtocols(final ZKConfig config) { String enabledProtocolsInput = config.getProperty(getSslEnabledProtocolsProperty()); if (enabledProtocolsInput == null) { return new String[]{ config.getProperty(getSslProtocolProperty(), DEFAULT_PROTOCOL) }; } return enabledProtocolsInput.split(","); } private X509Util.ClientAuth getClientAuth(final ZKConfig config) { return X509Util.ClientAuth.fromPropertyValue(config.getProperty(getSslClientAuthProperty())); } private Iterable getCipherSuites(final ZKConfig config) { String cipherSuitesInput = config.getProperty(getSslCipherSuitesProperty()); if (cipherSuitesInput == null) { if (getSslProvider(config) != SslProvider.JDK) { return null; } return Arrays.asList(X509Util.getDefaultCipherSuites()); } else { return Arrays.asList(cipherSuitesInput.split(",")); } } public SslProvider getSslProvider(ZKConfig config) { return SslProvider.valueOf(config.getProperty(getSslProviderProperty(), "JDK")); } private TrustManager getTrustManager(ZKConfig config) throws X509Exception.TrustManagerException { String trustStoreLocation = config.getProperty(getSslTruststoreLocationProperty(), ""); String trustStorePassword = getPasswordFromConfigPropertyOrFile(config, getSslTruststorePasswdProperty(), getSslTruststorePasswdPathProperty()); String trustStoreType = config.getProperty(getSslTruststoreTypeProperty()); boolean sslCrlEnabled = config.getBoolean(getSslCrlEnabledProperty()); boolean sslOcspEnabled = config.getBoolean(getSslOcspEnabledProperty()); boolean sslServerHostnameVerificationEnabled = isServerHostnameVerificationEnabled(config); boolean sslClientHostnameVerificationEnabled = isClientHostnameVerificationEnabled(config); if (trustStoreLocation.isEmpty()) { LOG.warn("{} not specified", getSslTruststoreLocationProperty()); return null; } else { return createTrustManager(trustStoreLocation, trustStorePassword, trustStoreType, sslCrlEnabled, sslOcspEnabled, sslServerHostnameVerificationEnabled, sslClientHostnameVerificationEnabled, getFipsMode(config)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Fi0100644 0000000 0000000 00000000162 15051152474 032611 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileChangeWatcher.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileChangeWatcher.0100644 0000000 0000000 00000022447 15051152474 034077 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.IOException; import java.nio.file.ClosedWatchServiceException; import java.nio.file.FileSystem; import java.nio.file.Path; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.function.Consumer; import org.apache.zookeeper.server.ZooKeeperThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Instances of this class can be used to watch a directory for file changes. When a file is added to, deleted from, * or is modified in the given directory, the callback provided by the user will be called from a background thread. * Some things to keep in mind: *
    *
  • The callback should be thread-safe.
  • *
  • Changes that happen around the time the thread is started may be missed.
  • *
  • There is a delay between a file changing and the callback firing.
  • *
  • The watch is not recursive - changes to subdirectories will not trigger a callback.
  • *
*/ public final class FileChangeWatcher { private static final Logger LOG = LoggerFactory.getLogger(FileChangeWatcher.class); public enum State { NEW, // object created but start() not called yet STARTING, // start() called but background thread has not entered main loop RUNNING, // background thread is running STOPPING, // stop() called but background thread has not exited main loop STOPPED // stop() called and background thread has exited, or background thread crashed } private final WatcherThread watcherThread; private State state; // protected by synchronized(this) /** * Creates a watcher that watches dirPath and invokes callback on changes. * * @param dirPath the directory to watch. * @param callback the callback to invoke with events. event.kind() will return the type of event, * and event.context() will return the filename relative to dirPath. * @throws IOException if there is an error creating the WatchService. */ public FileChangeWatcher(Path dirPath, Consumer> callback) throws IOException { FileSystem fs = dirPath.getFileSystem(); WatchService watchService = fs.newWatchService(); LOG.debug("Registering with watch service: {}", dirPath); dirPath.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.OVERFLOW}); state = State.NEW; this.watcherThread = new WatcherThread(watchService, callback); this.watcherThread.setDaemon(true); } /** * Returns the current {@link FileChangeWatcher.State}. * @return the current state. */ public synchronized State getState() { return state; } /** * Blocks until the current state becomes desiredState. * Currently only used by tests, thus package-private. * @param desiredState the desired state. * @throws InterruptedException if the current thread gets interrupted. */ synchronized void waitForState(State desiredState) throws InterruptedException { while (this.state != desiredState) { this.wait(); } } /** * Sets the state to newState. * @param newState the new state. */ private synchronized void setState(State newState) { state = newState; this.notifyAll(); } /** * Atomically sets the state to update if and only if the * state is currently expected. * @param expected the expected state. * @param update the new state. * @return true if the update succeeds, or false if the current state * does not equal expected. */ private synchronized boolean compareAndSetState(State expected, State update) { if (state == expected) { setState(update); return true; } else { return false; } } /** * Atomically sets the state to update if and only if the * state is currently one of expectedStates. * @param expectedStates the expected states. * @param update the new state. * @return true if the update succeeds, or false if the current state * does not equal any of the expectedStates. */ private synchronized boolean compareAndSetState(State[] expectedStates, State update) { for (State expected : expectedStates) { if (state == expected) { setState(update); return true; } } return false; } /** * Tells the background thread to start. Does not wait for it to be running. * Calling this method more than once has no effect. */ public void start() { if (!compareAndSetState(State.NEW, State.STARTING)) { // If previous state was not NEW, start() has already been called. return; } this.watcherThread.start(); } /** * Tells the background thread to stop. Does not wait for it to exit. */ public void stop() { if (compareAndSetState(new State[]{State.RUNNING, State.STARTING}, State.STOPPING)) { watcherThread.interrupt(); } } /** * Inner class that implements the watcher thread logic. */ private class WatcherThread extends ZooKeeperThread { private static final String THREAD_NAME = "FileChangeWatcher"; final WatchService watchService; final Consumer> callback; WatcherThread(WatchService watchService, Consumer> callback) { super(THREAD_NAME); this.watchService = watchService; this.callback = callback; } @Override public void run() { try { LOG.info("{} thread started", getName()); if (!compareAndSetState(FileChangeWatcher.State.STARTING, FileChangeWatcher.State.RUNNING)) { // stop() called shortly after start(), before // this thread started running. FileChangeWatcher.State state = FileChangeWatcher.this.getState(); if (state != FileChangeWatcher.State.STOPPING) { throw new IllegalStateException("Unexpected state: " + state); } return; } runLoop(); } catch (Exception e) { LOG.warn("Error in runLoop()", e); throw e; } finally { try { watchService.close(); } catch (IOException e) { LOG.warn("Error closing watch service", e); } LOG.info("{} thread finished", getName()); FileChangeWatcher.this.setState(FileChangeWatcher.State.STOPPED); } } private void runLoop() { while (FileChangeWatcher.this.getState() == FileChangeWatcher.State.RUNNING) { WatchKey key; try { key = watchService.take(); } catch (InterruptedException | ClosedWatchServiceException e) { LOG.debug("{} was interrupted and is shutting down...", getName()); break; } for (WatchEvent event : key.pollEvents()) { LOG.debug("Got file changed event: {} with context: {}", event.kind(), event.context()); try { callback.accept(event); } catch (Throwable e) { LOG.error("Error from callback", e); } } boolean isKeyValid = key.reset(); if (!isKeyValid) { // This is likely a problem, it means that file reloading is broken, probably because the // directory we are watching was deleted or otherwise became inaccessible (unmounted, permissions // changed, ???). // For now, we log an error and exit the watcher thread. LOG.error("Watch key no longer valid, maybe the directory is inaccessible?"); break; } } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Fi0100644 0000000 0000000 00000000163 15051152474 032612 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoader0100644 0000000 0000000 00000005114 15051152474 034202 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.util.Objects; /** * Base class for instances of {@link KeyStoreLoader} which load the key/trust * stores from files on a filesystem. */ abstract class FileKeyStoreLoader implements KeyStoreLoader { final String keyStorePath; final String trustStorePath; final String keyStorePassword; final String trustStorePassword; FileKeyStoreLoader(String keyStorePath, String trustStorePath, String keyStorePassword, String trustStorePassword) { this.keyStorePath = keyStorePath; this.trustStorePath = trustStorePath; this.keyStorePassword = keyStorePassword; this.trustStorePassword = trustStorePassword; } /** * Base class for builder pattern used by subclasses. * @param the subtype of FileKeyStoreLoader created by the Builder. */ abstract static class Builder { String keyStorePath; String trustStorePath; String keyStorePassword; String trustStorePassword; Builder() { } Builder setKeyStorePath(String keyStorePath) { this.keyStorePath = Objects.requireNonNull(keyStorePath); return this; } Builder setTrustStorePath(String trustStorePath) { this.trustStorePath = Objects.requireNonNull(trustStorePath); return this; } Builder setKeyStorePassword(String keyStorePassword) { this.keyStorePassword = Objects.requireNonNull(keyStorePassword); return this; } Builder setTrustStorePassword(String trustStorePassword) { this.trustStorePassword = Objects.requireNonNull(trustStorePassword); return this; } abstract T build(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Fi0100644 0000000 0000000 00000000202 15051152474 032604 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/FileKeyStoreLoader0100644 0000000 0000000 00000003343 15051152474 034204 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.util.Objects; public class FileKeyStoreLoaderBuilderProvider { /** * Returns a {@link FileKeyStoreLoader.Builder} that can build a loader * which loads keys and certs from files of the given * {@link KeyStoreFileType}. * * @param type the file type to load keys/certs from. * @return a new Builder. */ static FileKeyStoreLoader.Builder getBuilderForKeyStoreFileType(KeyStoreFileType type) { switch (Objects.requireNonNull(type)) { case JKS: return new JKSFileLoader.Builder(); case PEM: return new PEMFileLoader.Builder(); case PKCS12: return new PKCS12FileLoader.Builder(); case BCFKS: return new BCFKSFileLoader.Builder(); default: throw new AssertionError("Unexpected StoreFileType: " + type.name()); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/IOUtils.java0100644 0000000 0000000 00000007436 15051152474 032767 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import org.slf4j.Logger; /* * This code is originally from HDFS, see the similarly named files there * in case of bug fixing, history, etc... */ public class IOUtils { /** * Closes the stream ignoring {@link IOException}. Must only be called in * cleaning up from exception handlers. * * @param stream * the Stream to close */ public static void closeStream(Closeable stream) { cleanup(null, stream); } /** * Close the Closeable objects and ignore any {@link IOException} or * null pointers. Must only be used for cleanup in exception handlers. * * @param log * the log to record problems to at debug level. Can be null. * @param closeables * the objects to close */ public static void cleanup(Logger log, Closeable... closeables) { for (Closeable c : closeables) { if (c != null) { try { c.close(); } catch (IOException e) { if (log != null) { log.warn("Exception in closing " + c, e); } } } } } /** * Copies from one stream to another. * * @param in * InputStrem to read from * @param out * OutputStream to write to * @param buffSize * the size of the buffer * @param close * whether or not close the InputStream and OutputStream at the * end. The streams are closed in the finally clause. */ public static void copyBytes(InputStream in, OutputStream out, int buffSize, boolean close) throws IOException { try { copyBytes(in, out, buffSize); if (close) { out.close(); out = null; in.close(); in = null; } } finally { if (close) { closeStream(out); closeStream(in); } } } /** * Copies from one stream to another. * * @param in * InputStrem to read from * @param out * OutputStream to write to * @param buffSize * the size of the buffer */ public static void copyBytes(InputStream in, OutputStream out, int buffSize) throws IOException { PrintStream ps = out instanceof PrintStream ? (PrintStream) out : null; byte[] buf = new byte[buffSize]; int bytesRead = in.read(buf); while (bytesRead >= 0) { out.write(buf, 0, bytesRead); if ((ps != null) && ps.checkError()) { throw new IOException("Unable to write to output stream."); } bytesRead = in.read(buf); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_JK0100644 0000000 0000000 00000000156 15051152474 032562 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/JKSFileLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/JKSFileLoader.java0100644 0000000 0000000 00000002764 15051152474 034014 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * Implementation of {@link FileKeyStoreLoader} that loads from JKS files. */ class JKSFileLoader extends StandardTypeFileKeyStoreLoader { private JKSFileLoader( String keyStorePath, String trustStorePath, String keyStorePassword, String trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.JKS); } static class Builder extends FileKeyStoreLoader.Builder { @Override JKSFileLoader build() { return new JKSFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Ke0100644 0000000 0000000 00000000161 15051152474 032611 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreFileType.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreFileType.j0100644 0000000 0000000 00000011602 15051152474 034144 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * This enum represents the file type of a KeyStore or TrustStore. * Currently, JKS (Java keystore), PEM, PKCS12, and BCFKS types are supported. */ public enum KeyStoreFileType { JKS(".jks"), PEM(".pem"), PKCS12(".p12"), BCFKS(".bcfks"); private final String defaultFileExtension; KeyStoreFileType(String defaultFileExtension) { this.defaultFileExtension = defaultFileExtension; } /** * The property string that specifies that a key store or trust store * should use this store file type. */ public String getPropertyValue() { return this.name(); } /** * The file extension that is associated with this file type. */ public String getDefaultFileExtension() { return defaultFileExtension; } /** * Converts a property value to a StoreFileType enum. If the property value * is null or an empty string, returns null. * @param propertyValue the property value. * @return the KeyStoreFileType, or null if * propertyValue is null or empty. * @throws IllegalArgumentException if propertyValue is not * one of "JKS", "PEM", "BCFKS", "PKCS12", or empty/null. */ public static KeyStoreFileType fromPropertyValue(String propertyValue) { if (propertyValue == null || propertyValue.length() == 0) { return null; } return KeyStoreFileType.valueOf(propertyValue.toUpperCase()); } /** * Detects the type of KeyStore / TrustStore file from the file extension. * If the file name ends with ".jks", returns StoreFileType.JKS. * If the file name ends with ".pem", returns StoreFileType.PEM. * If the file name ends with ".p12", returns StoreFileType.PKCS12. * If the file name ends with ".bckfs", returns StoreFileType.BCKFS. * Otherwise, throws an IllegalArgumentException. * @param filename the filename of the key store or trust store file. * @return a KeyStoreFileType. * @throws IllegalArgumentException if the filename does not end with * ".jks", ".pem", "p12" or "bcfks". */ public static KeyStoreFileType fromFilename(String filename) { int i = filename.lastIndexOf('.'); if (i >= 0) { String extension = filename.substring(i); for (KeyStoreFileType storeFileType : KeyStoreFileType.values()) { if (storeFileType.getDefaultFileExtension().equals(extension)) { return storeFileType; } } } throw new IllegalArgumentException("Unable to auto-detect store file type from file name: " + filename); } /** * If propertyValue is not null or empty, returns the result * of KeyStoreFileType.fromPropertyValue(propertyValue). Else, * returns the result of KeyStoreFileType.fromFileName(filename). * @param propertyValue property value describing the KeyStoreFileType, or * null/empty to auto-detect the type from the file * name. * @param filename file name of the key store file. The file extension is * used to auto-detect the KeyStoreFileType when * propertyValue is null or empty. * @return a KeyStoreFileType. * @throws IllegalArgumentException if propertyValue is not * one of "JKS", "PEM", "PKCS12", "BCFKS", or empty/null. * @throws IllegalArgumentException if propertyValueis empty * or null and the type could not be determined from the file name. */ public static KeyStoreFileType fromPropertyValueOrFileName(String propertyValue, String filename) { KeyStoreFileType result = KeyStoreFileType.fromPropertyValue(propertyValue); if (result == null) { result = KeyStoreFileType.fromFilename(filename); } return result; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Ke0100644 0000000 0000000 00000000157 15051152474 032616 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreLoader.jav0100644 0000000 0000000 00000004050 15051152474 034157 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; /** * An interface for an object that can load key stores or trust stores. */ interface KeyStoreLoader { /** * Loads a KeyStore which contains at least one private key and the * associated X509 cert chain. * * @return a new KeyStore * @throws IOException if loading the key store fails due to an IO error, * such as "file not found". * @throws GeneralSecurityException if loading the key store fails due to * a security error, such as "unsupported crypto algorithm". */ KeyStore loadKeyStore() throws IOException, GeneralSecurityException; /** * Loads a KeyStore which contains at least one X509 cert chain for a * trusted Certificate Authority (CA). * * @return a new KeyStore * @throws IOException if loading the trust store fails due to an IO error, * such as "file not found". * @throws GeneralSecurityException if loading the trust store fails due to * a security error, such as "unsupported crypto algorithm". */ KeyStore loadTrustStore() throws IOException, GeneralSecurityException; } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/NetUtils.java0100644 0000000 0000000 00000006712 15051152474 033202 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; /** * This class contains common utilities for netstuff. Like printing IPv6 literals correctly */ public class NetUtils { /** * Prefer using the hostname for formatting, but without requesting reverse DNS lookup. * Fall back to IP address if hostname is unavailable and use [] brackets for IPv6 literal. */ public static String formatInetAddr(InetSocketAddress addr) { String hostString = addr.getHostString(); InetAddress ia = addr.getAddress(); if (ia instanceof Inet6Address && hostString.contains(":")) { return String.format("[%s]:%s", hostString, addr.getPort()); } else { return String.format("%s:%s", hostString, addr.getPort()); } } /** * Separates host and port from given host port string if host port string is enclosed * within square bracket. * * @param hostPort host port string * @return String[]{host, port} if host port string is host:port * or String[] {host, port:port} if host port string is host:port:port * or String[] {host} if host port string is host * or String[]{} if not a ipv6 host port string. */ public static String[] getIPV6HostAndPort(String hostPort) { if (hostPort.startsWith("[")) { int i = hostPort.lastIndexOf(']'); if (i < 0) { throw new IllegalArgumentException( hostPort + " starts with '[' but has no matching ']'"); } String host = hostPort.substring(1, i); if (host.isEmpty()) { throw new IllegalArgumentException(host + " is empty."); } if (hostPort.length() > i + 1) { return getHostPort(hostPort, i, host); } return new String[] { host }; } else { //Not an IPV6 host port string return new String[] {}; } } private static String[] getHostPort(String hostPort, int indexOfClosingBracket, String host) { // [127::1]:2181 , check separator : exits if (hostPort.charAt(indexOfClosingBracket + 1) != ':') { throw new IllegalArgumentException(hostPort + " does not have : after ]"); } // [127::1]: scenario if (indexOfClosingBracket + 2 == hostPort.length()) { throw new IllegalArgumentException(hostPort + " doesn't have a port after colon."); } //do not include String port = hostPort.substring(indexOfClosingBracket + 2); return new String[] { host, port }; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/NettyUtils.java0100644 0000000 0000000 00000016444 15051152474 033562 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import io.netty.channel.EventLoopGroup; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.epoll.EpollSocketChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Helper methods for netty code. */ public class NettyUtils { public static final String THREAD_POOL_NAME_PREFIX = "zkNetty-"; private static final Logger LOG = LoggerFactory.getLogger(NettyUtils.class); private static final int DEFAULT_INET_ADDRESS_COUNT = 1; /** * Returns a ThreadFactory which generates daemon threads, and uses * the passed class's name to generate the thread names. * * @param clazz Class name to use for generating thread names * @return Netty DefaultThreadFactory configured to create daemon threads */ private static ThreadFactory createThreadFactory(String clazz) { final String poolName = THREAD_POOL_NAME_PREFIX + clazz; return new DefaultThreadFactory(poolName, true); } /** * If {@link Epoll#isAvailable()} == true, returns a new * {@link EpollEventLoopGroup}, otherwise returns a new * {@link NioEventLoopGroup}. Creates the event loop group using the * default number of threads. * @return a new {@link EventLoopGroup}. */ public static EventLoopGroup newNioOrEpollEventLoopGroup() { return newNioOrEpollEventLoopGroup(0); } /** * If {@link Epoll#isAvailable()} == true, returns a new * {@link EpollEventLoopGroup}, otherwise returns a new * {@link NioEventLoopGroup}. Creates the event loop group using the * specified number of threads instead of the default. * @param nThreads see {@link NioEventLoopGroup#NioEventLoopGroup(int)}. * @return a new {@link EventLoopGroup}. */ public static EventLoopGroup newNioOrEpollEventLoopGroup(int nThreads) { if (Epoll.isAvailable()) { final String clazz = EpollEventLoopGroup.class.getSimpleName(); final ThreadFactory factory = createThreadFactory(clazz); return new EpollEventLoopGroup(nThreads, factory); } else { final String clazz = NioEventLoopGroup.class.getSimpleName(); final ThreadFactory factory = createThreadFactory(clazz); return new NioEventLoopGroup(nThreads, factory); } } /** * If {@link Epoll#isAvailable()} == true, returns * {@link EpollSocketChannel}, otherwise returns {@link NioSocketChannel}. * @return a socket channel class. */ public static Class nioOrEpollSocketChannel() { if (Epoll.isAvailable()) { return EpollSocketChannel.class; } else { return NioSocketChannel.class; } } /** * If {@link Epoll#isAvailable()} == true, returns * {@link EpollServerSocketChannel}, otherwise returns * {@link NioServerSocketChannel}. * @return a server socket channel class. */ public static Class nioOrEpollServerSocketChannel() { if (Epoll.isAvailable()) { return EpollServerSocketChannel.class; } else { return NioServerSocketChannel.class; } } /** * Attempts to detect and return the number of local network addresses that could be * used by a client to reach this server. This means we exclude the following address types: *
    *
  • Multicast addresses. Zookeeper server sockets use TCP, thus cannot bind to a multicast address.
  • *
  • Link-local addresses. Routers don't forward traffic sent to a link-local address, so * any realistic server deployment would not have clients using these.
  • *
  • Loopback addresses. These are typically only used for testing.
  • *
* Any remaining addresses are counted, and the total count is returned. This number is * used to configure the number of threads for the "boss" event loop group, to make sure we have * enough threads for each address in case the server is configured to listen on * all available addresses. * If listing the network interfaces fails, this method will return 1. * * @return the number of client-reachable local network addresses found, or * 1 if listing the network interfaces fails. */ public static int getClientReachableLocalInetAddressCount() { try { Set validInetAddresses = new HashSet<>(); Enumeration allNetworkInterfaces = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface networkInterface : Collections.list(allNetworkInterfaces)) { for (InetAddress inetAddress : Collections.list(networkInterface.getInetAddresses())) { if (inetAddress.isLinkLocalAddress()) { LOG.debug("Ignoring link-local InetAddress {}", inetAddress); continue; } if (inetAddress.isMulticastAddress()) { LOG.debug("Ignoring multicast InetAddress {}", inetAddress); continue; } if (inetAddress.isLoopbackAddress()) { LOG.debug("Ignoring loopback InetAddress {}", inetAddress); continue; } validInetAddresses.add(inetAddress); } } LOG.debug("Detected {} local network addresses: {}", validInetAddresses.size(), validInetAddresses); return !validInetAddresses.isEmpty() ? validInetAddresses.size() : DEFAULT_INET_ADDRESS_COUNT; } catch (SocketException ex) { LOG.warn("Failed to list all network interfaces, assuming 1", ex); return DEFAULT_INET_ADDRESS_COUNT; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_PE0100644 0000000 0000000 00000000156 15051152474 032562 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/PEMFileLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/PEMFileLoader.java0100644 0000000 0000000 00000004411 15051152474 033775 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Optional; import org.apache.zookeeper.util.PemReader; /** * Implementation of {@link FileKeyStoreLoader} that loads from PEM files. */ class PEMFileLoader extends FileKeyStoreLoader { private PEMFileLoader( String keyStorePath, String trustStorePath, String keyStorePassword, String trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } @Override public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { Optional passwordOption; if (keyStorePassword == null || keyStorePassword.length() == 0) { passwordOption = Optional.empty(); } else { passwordOption = Optional.of(keyStorePassword); } File file = new File(keyStorePath); return PemReader.loadKeyStore(file, file, passwordOption); } @Override public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { return PemReader.loadTrustStore(new File(trustStorePath)); } static class Builder extends FileKeyStoreLoader.Builder { @Override PEMFileLoader build() { return new PEMFileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_PK0100644 0000000 0000000 00000000161 15051152474 032564 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/PKCS12FileLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/PKCS12FileLoader.j0100644 0000000 0000000 00000003011 15051152474 033562 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * Implementation of {@link FileKeyStoreLoader} that loads from PKCS12 files. */ class PKCS12FileLoader extends StandardTypeFileKeyStoreLoader { private PKCS12FileLoader( String keyStorePath, String trustStorePath, String keyStorePassword, String trustStorePassword) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword, SupportedStandardKeyFormat.PKCS12); } static class Builder extends FileKeyStoreLoader.Builder { @Override PKCS12FileLoader build() { return new PKCS12FileLoader(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathTrie.java0100644 0000000 0000000 00000024466 15051152474 033161 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * a class that implements prefix matching for * components of a filesystem path. the trie * looks like a tree with edges mapping to * the component of a path. * example /ab/bc/cf would map to a trie * / * ab/ * (ab) * bc/ * / * (bc) * cf/ * (cf) */ public class PathTrie { /** Logger for this class */ private static final Logger LOG = LoggerFactory.getLogger(PathTrie.class); /** Root node of PathTrie */ private final TrieNode rootNode; private final ReadWriteLock lock = new ReentrantReadWriteLock(true); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); static class TrieNode { final String value; final Map children; boolean property; TrieNode parent; /** * Create a trie node with parent as parameter. * * @param parent the parent of this node * @param value the value stored in this node */ private TrieNode(TrieNode parent, String value) { this.value = value; this.parent = parent; this.property = false; this.children = new HashMap<>(4); } /** * Get the parent of this node. * * @return the parent node */ TrieNode getParent() { return this.parent; } /** * set the parent of this node. * * @param parent the parent to set to */ void setParent(TrieNode parent) { this.parent = parent; } /** * A property that is set for a node - making it special. */ void setProperty(boolean prop) { this.property = prop; } /** * The property of this node. * * @return the property for this node */ boolean hasProperty() { return this.property; } /** * The value stored in this node. * * @return the value stored in this node */ public String getValue() { return this.value; } /** * Add a child to the existing node. * * @param childName the string name of the child * @param node the node that is the child */ void addChild(String childName, TrieNode node) { this.children.putIfAbsent(childName, node); } /** * Delete child from this node. * * @param childName the name of the child to be deleted */ void deleteChild(String childName) { this.children.computeIfPresent(childName, (key, childNode) -> { // Node no longer has an external property associated childNode.setProperty(false); // Delete it if it has no children (is a leaf node) if (childNode.isLeafNode()) { childNode.setParent(null); return null; } return childNode; }); } /** * Return the child of a node mapping to the input child name. * * @param childName the name of the child * @return the child of a node */ TrieNode getChild(String childName) { return this.children.get(childName); } /** * Get the list of children of this trienode. * * @return A collection containing the node's children */ Collection getChildren() { return children.keySet(); } /** * Determine if this node is a leaf (has no children). * * @return true if this node is a lead node; otherwise false */ boolean isLeafNode() { return children.isEmpty(); } @Override public String toString() { return "TrieNode [name=" + value + ", property=" + property + ", children=" + children.keySet() + "]"; } } /** * Construct a new PathTrie with a root node. */ public PathTrie() { this.rootNode = new TrieNode(null, "/"); } /** * Add a path to the path trie. All paths are relative to the root node. * * @param path the path to add to the trie */ public void addPath(final String path) { Objects.requireNonNull(path, "Path cannot be null"); if (path.length() == 0) { throw new IllegalArgumentException("Invalid path: " + path); } final String[] pathComponents = split(path); writeLock.lock(); try { TrieNode parent = rootNode; for (final String part : pathComponents) { TrieNode child = parent.getChild(part); if (child == null) { child = new TrieNode(parent, part); parent.addChild(part, child); } parent = child; } parent.setProperty(true); } finally { writeLock.unlock(); } } /** * Delete a path from the trie. All paths are relative to the root node. * * @param path the path to be deleted */ public void deletePath(final String path) { Objects.requireNonNull(path, "Path cannot be null"); if (path.length() == 0) { throw new IllegalArgumentException("Invalid path: " + path); } final String[] pathComponents = split(path); writeLock.lock(); try { TrieNode parent = rootNode; for (final String part : pathComponents) { if (parent.getChild(part) == null) { // the path does not exist return; } parent = parent.getChild(part); LOG.debug("{}", parent); } final TrieNode realParent = parent.getParent(); realParent.deleteChild(parent.getValue()); } finally { writeLock.unlock(); } } /** * Return true if the given path exists in the trie, otherwise return false; * All paths are relative to the root node. * * @param path the input path * @return the largest prefix for the */ public boolean existsNode(final String path) { Objects.requireNonNull(path, "Path cannot be null"); if (path.length() == 0) { throw new IllegalArgumentException("Invalid path: " + path); } final String[] pathComponents = split(path); readLock.lock(); try { TrieNode parent = rootNode; for (final String part : pathComponents) { if (parent.getChild(part) == null) { // the path does not exist return false; } parent = parent.getChild(part); LOG.debug("{}", parent); } } finally { readLock.unlock(); } return true; } /** * Return the largest prefix for the input path. All paths are relative to the * root node. * * @param path the input path * @return the largest prefix for the input path */ public String findMaxPrefix(final String path) { Objects.requireNonNull(path, "Path cannot be null"); final String[] pathComponents = split(path); readLock.lock(); try { TrieNode parent = rootNode; TrieNode deepestPropertyNode = null; for (final String element : pathComponents) { parent = parent.getChild(element); if (parent == null) { LOG.debug("{}", element); break; } if (parent.hasProperty()) { deepestPropertyNode = parent; } } if (deepestPropertyNode == null) { return "/"; } final Deque treePath = new ArrayDeque<>(); TrieNode node = deepestPropertyNode; while (node != this.rootNode) { treePath.offerFirst(node.getValue()); node = node.parent; } return "/" + String.join("/", treePath); } finally { readLock.unlock(); } } /** * Clear all nodes in the trie. */ public void clear() { writeLock.lock(); try { rootNode.getChildren().clear(); } finally { writeLock.unlock(); } } private static String[] split(final String path){ return Stream.of(path.split("/")) .filter(t -> !t.trim().isEmpty()) .toArray(String[]::new); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathUtils.java0100644 0000000 0000000 00000010750 15051152474 033345 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * Path related utilities */ public class PathUtils { /** validate the provided znode path string * @param path znode path string * @param isSequential if the path is being created * with a sequential flag * @throws IllegalArgumentException if the path is invalid */ public static void validatePath(String path, boolean isSequential) throws IllegalArgumentException { validatePath(isSequential ? path + "1" : path); } /** * Validate the provided znode path string * @param path znode path string * @throws IllegalArgumentException if the path is invalid */ public static void validatePath(String path) throws IllegalArgumentException { if (path == null) { throw new IllegalArgumentException("Path cannot be null"); } if (path.length() == 0) { throw new IllegalArgumentException("Path length must be > 0"); } if (path.charAt(0) != '/') { throw new IllegalArgumentException("Path must start with / character"); } if (path.length() == 1) { // done checking - it's the root return; } if (path.charAt(path.length() - 1) == '/') { throw new IllegalArgumentException("Path must not end with / character"); } String reason = null; char lastc = '/'; char[] chars = path.toCharArray(); char c; for (int i = 1; i < chars.length; lastc = chars[i], i++) { c = chars[i]; if (c == 0) { reason = "null character not allowed @" + i; break; } else if (c == '/' && lastc == '/') { reason = "empty node name specified @" + i; break; } else if (c == '.' && lastc == '.') { if (chars[i - 2] == '/' && ((i + 1 == chars.length) || chars[i + 1] == '/')) { reason = "relative paths not allowed @" + i; break; } } else if (c == '.') { if (chars[i - 1] == '/' && ((i + 1 == chars.length) || chars[i + 1] == '/')) { reason = "relative paths not allowed @" + i; break; } } else if (c > '\u0000' && c <= '\u001f' || c >= '\u007f' && c <= '\u009F' || c >= '\ud800' && c <= '\uf8ff' || c >= '\ufff0' && c <= '\uffff') { reason = "invalid character @" + i; break; } } if (reason != null) { throw new IllegalArgumentException("Invalid path string \"" + path + "\" caused by " + reason); } } /** * Convert Windows path to Unix * * @param path * file path * @return converted file path */ public static String normalizeFileSystemPath(String path) { if (path != null) { String osname = java.lang.System.getProperty("os.name"); if (osname.toLowerCase().contains("windows")) { return path.replace('\\', '/'); } } return path; } /** * return the top namespace of a znode path * * @param path znode path string * * @return the top namespace. If not exist, return null */ public static String getTopNamespace(final String path) { if (path == null) { return null; } final String[] parts = path.split("/"); return parts.length > 1 ? parts[1] : null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_Qu0100644 0000000 0000000 00000000157 15051152474 032644 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/QuorumX509Util.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/QuorumX509Util.jav0100644 0000000 0000000 00000002233 15051152474 034000 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * X509 utilities specific for server-server (quorum) communication framework. */ public class QuorumX509Util extends X509Util { @Override protected String getConfigPrefix() { return "zookeeper.ssl.quorum."; } @Override protected boolean shouldVerifyClientHostname() { return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_SS0100644 0000000 0000000 00000000165 15051152474 032603 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/SSLContextAndOptions.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/SSLContextAndOptio0100644 0000000 0000000 00000020036 15051152474 034152 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static java.util.Objects.requireNonNull; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.Socket; import java.util.Arrays; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Wrapper class for an SSLContext + some config options that can't be set on the context when it is created but * must be set on a secure socket created by the context after the socket creation. By wrapping the options in this * class we avoid reading from global system properties during socket configuration. This makes testing easier * since we can create different X509Util instances with different configurations in a single test process, and * unit test interactions between them. */ public class SSLContextAndOptions { private static final Logger LOG = LoggerFactory.getLogger(SSLContextAndOptions.class); private final X509Util x509Util; private final String[] enabledProtocols; private final String[] cipherSuites; private final X509Util.ClientAuth clientAuth; private final SSLContext sslContext; private final int handshakeDetectionTimeoutMillis; /** * Note: constructor is intentionally package-private, only the X509Util class should be creating instances of this * class. * @param x509Util the X509Util that created this object. * @param config a ZKConfig that holds config properties. * @param sslContext the SSLContext. */ SSLContextAndOptions(final X509Util x509Util, final ZKConfig config, final SSLContext sslContext) { this.x509Util = requireNonNull(x509Util); this.sslContext = requireNonNull(sslContext); this.enabledProtocols = getEnabledProtocols(requireNonNull(config), sslContext); this.cipherSuites = getCipherSuites(config); this.clientAuth = getClientAuth(config); this.handshakeDetectionTimeoutMillis = getHandshakeDetectionTimeoutMillis(config); } public SSLContext getSSLContext() { return sslContext; } public SSLSocket createSSLSocket() throws IOException { return configureSSLSocket((SSLSocket) sslContext.getSocketFactory().createSocket(), true); } public SSLSocket createSSLSocket(Socket socket, byte[] pushbackBytes) throws IOException { SSLSocket sslSocket; if (pushbackBytes != null && pushbackBytes.length > 0) { sslSocket = (SSLSocket) sslContext.getSocketFactory() .createSocket(socket, new ByteArrayInputStream(pushbackBytes), true); } else { sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(socket, null, socket.getPort(), true); } return configureSSLSocket(sslSocket, false); } public SSLServerSocket createSSLServerSocket() throws IOException { SSLServerSocket sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(); return configureSSLServerSocket(sslServerSocket); } public SSLServerSocket createSSLServerSocket(int port) throws IOException { SSLServerSocket sslServerSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port); return configureSSLServerSocket(sslServerSocket); } public int getHandshakeDetectionTimeoutMillis() { return handshakeDetectionTimeoutMillis; } private SSLSocket configureSSLSocket(SSLSocket socket, boolean isClientSocket) { SSLParameters sslParameters = socket.getSSLParameters(); configureSslParameters(sslParameters, isClientSocket); socket.setSSLParameters(sslParameters); socket.setUseClientMode(isClientSocket); return socket; } private SSLServerSocket configureSSLServerSocket(SSLServerSocket socket) { SSLParameters sslParameters = socket.getSSLParameters(); configureSslParameters(sslParameters, false); socket.setSSLParameters(sslParameters); socket.setUseClientMode(false); return socket; } private void configureSslParameters(SSLParameters sslParameters, boolean isClientSocket) { if (cipherSuites != null) { LOG.debug( "Setup cipher suites for {} socket: {}", isClientSocket ? "client" : "server", Arrays.toString(cipherSuites)); sslParameters.setCipherSuites(cipherSuites); } if (enabledProtocols != null) { LOG.debug( "Setup enabled protocols for {} socket: {}", isClientSocket ? "client" : "server", Arrays.toString(enabledProtocols)); sslParameters.setProtocols(enabledProtocols); } if (!isClientSocket) { switch (clientAuth) { case NEED: sslParameters.setNeedClientAuth(true); break; case WANT: sslParameters.setWantClientAuth(true); break; default: sslParameters.setNeedClientAuth(false); // also clears the wantClientAuth flag according to docs break; } } } private String[] getEnabledProtocols(final ZKConfig config, final SSLContext sslContext) { String enabledProtocolsInput = config.getProperty(x509Util.getSslEnabledProtocolsProperty()); if (enabledProtocolsInput == null) { // Use JDK defaults for enabled protocols: // Protocol TLSv1.3 -> enabled protocols TLSv1.3 and TLSv1.2 // Protocol TLSv1.2 -> enabled protocols TLSv1.2 return sslContext.getDefaultSSLParameters().getProtocols(); } return enabledProtocolsInput.split(","); } private String[] getCipherSuites(final ZKConfig config) { String cipherSuitesInput = config.getProperty(x509Util.getSslCipherSuitesProperty()); if (cipherSuitesInput == null) { return X509Util.getDefaultCipherSuites(); } else { return cipherSuitesInput.split(","); } } private X509Util.ClientAuth getClientAuth(final ZKConfig config) { return X509Util.ClientAuth.fromPropertyValue(config.getProperty(x509Util.getSslClientAuthProperty())); } private int getHandshakeDetectionTimeoutMillis(final ZKConfig config) { String propertyString = config.getProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty()); int result; if (propertyString == null) { result = X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS; } else { result = Integer.parseInt(propertyString); if (result < 1) { // Timeout of 0 is not allowed, since an infinite timeout can permanently lock up an // accept() thread. LOG.warn( "Invalid value for {}: {}, using the default value of {}", x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), result, X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS); result = X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS; } } return result; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/SecretUtils.java0100644 0000000 0000000 00000003733 15051152474 033701 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class for handling secret such as key/trust store password */ public final class SecretUtils { private static final Logger LOG = LoggerFactory.getLogger(SecretUtils.class); private SecretUtils() { } public static char[] readSecret(final String pathToFile) { LOG.info("Reading secret from {}", pathToFile); try { final String secretValue = new String( Files.readAllBytes(Paths.get(pathToFile)), StandardCharsets.UTF_8); if (secretValue.endsWith(System.lineSeparator())) { return secretValue.substring(0, secretValue.length() - System.lineSeparator().length()).toCharArray(); } return secretValue.toCharArray(); } catch (final Throwable e) { LOG.error("Exception occurred when reading secret from file {}", pathToFile, e); throw new IllegalStateException("Exception occurred when reading secret from file " + pathToFile, e); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_St0100644 0000000 0000000 00000000177 15051152474 032647 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/StandardTypeFileKeyStoreLoader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/StandardTypeFileKe0100644 0000000 0000000 00000005401 15051152474 034167 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; /** * Base class for instances of {@link KeyStoreLoader} which load the key/trust * stores from files on a filesystem using standard {@link KeyStore} types like * JKS or PKCS12. */ abstract class StandardTypeFileKeyStoreLoader extends FileKeyStoreLoader { private static final char[] EMPTY_CHAR_ARRAY = new char[0]; protected final SupportedStandardKeyFormat format; protected enum SupportedStandardKeyFormat { JKS, PKCS12, BCFKS } StandardTypeFileKeyStoreLoader(String keyStorePath, String trustStorePath, String keyStorePassword, String trustStorePassword, SupportedStandardKeyFormat format) { super(keyStorePath, trustStorePath, keyStorePassword, trustStorePassword); this.format = format; } @Override public KeyStore loadKeyStore() throws IOException, GeneralSecurityException { try (InputStream inputStream = new FileInputStream(new File(keyStorePath))) { KeyStore ks = keyStoreInstance(); ks.load(inputStream, passwordStringToCharArray(keyStorePassword)); return ks; } } @Override public KeyStore loadTrustStore() throws IOException, GeneralSecurityException { try (InputStream inputStream = new FileInputStream(new File(trustStorePath))) { KeyStore ts = keyStoreInstance(); ts.load(inputStream, passwordStringToCharArray(trustStorePassword)); return ts; } } private KeyStore keyStoreInstance() throws KeyStoreException { return KeyStore.getInstance(format.name()); } private static char[] passwordStringToCharArray(String password) { return password == null ? EMPTY_CHAR_ARRAY : password.toCharArray(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/StringUtils.java0100644 0000000 0000000 00000006766 15051152474 033733 0ustar00rootroot0000000 0000000 /* Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; public class StringUtils { private StringUtils() {/** non instantiable and non inheritable **/} /** * This method returns an immutable List<String>, but different from String's split() * it trims the results in the input String, and removes any empty string from * the resulting List. * */ public static List split(String value, String separator) { String[] splits = value.split(separator); List results = new ArrayList<>(); for (int i = 0; i < splits.length; i++) { splits[i] = splits[i].trim(); if (splits[i].length() > 0) { results.add(splits[i]); } } return Collections.unmodifiableList(results); } /** * This method takes a List<String> and a delimiter and joins the * strings into a single string, where the original strings are separated * using the given delimiter. This method is a null-safe version of * {@link String#join(CharSequence, Iterable)} * *

* Note that if an individual element is null, then "null" is added. *

* @param list a {@code List} that will have its elements joined together * @param delim a sequence of characters that is used to separate each of the * elements in the resulting String * @return a new String that is composed from the elements argument or * {@code null} if list is {@code null} * @throws NullPointerException if delim is {@code null} */ public static String joinStrings(List list, String delim) { Objects.requireNonNull(delim); return list == null ? null : String.join(delim, list); } /** * Returns true if the string is null or it does not contain any non space characters. * @param s the string * @return true if the string is null or it does not contain any non space characters. */ public static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); } /** *

Checks if a String is empty ("") or null.

* *
     * StringUtils.isEmpty(null)      = true
     * StringUtils.isEmpty("")        = true
     * StringUtils.isEmpty(" ")       = false
     * StringUtils.isEmpty("bob")     = false
     * StringUtils.isEmpty("  bob  ") = false
     * 
* * @param str the String to check, may be null * @return true if the String is empty or null */ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/Time.java0100644 0000000 0000000 00000003657 15051152474 032336 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.util.Date; public class Time { /** * Returns time in milliseconds as does System.currentTimeMillis(), * but uses elapsed time from an arbitrary epoch more like System.nanoTime(). * The difference is that if somebody changes the system clock, * Time.currentElapsedTime will change but nanoTime won't. On the other hand, * all of ZK assumes that time is measured in milliseconds. * @return The time in milliseconds from some arbitrary point in time. */ public static long currentElapsedTime() { return System.nanoTime() / 1000000; } /** * Explicitly returns system dependent current wall time. * @return Current time in msec. */ public static long currentWallTime() { return System.currentTimeMillis(); } /** * This is to convert the elapsedTime to a Date. * @return A date object indicated by the elapsedTime. */ public static Date elapsedTimeToDate(long elapsedTime) { long wallTime = currentWallTime() + elapsedTime - currentElapsedTime(); return new Date(wallTime); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_X50100644 0000000 0000000 00000000156 15051152474 032552 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Exception.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Exception.java0100644 0000000 0000000 00000004023 15051152474 033750 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; @SuppressWarnings("serial") public class X509Exception extends Exception { public X509Exception(String message) { super(message); } public X509Exception(Throwable cause) { super(cause); } public X509Exception(String message, Throwable cause) { super(message, cause); } public static class KeyManagerException extends X509Exception { public KeyManagerException(String message) { super(message); } public KeyManagerException(Throwable cause) { super(cause); } } public static class TrustManagerException extends X509Exception { public TrustManagerException(String message) { super(message); } public TrustManagerException(Throwable cause) { super(cause); } } public static class SSLContextException extends X509Exception { public SSLContextException(String message) { super(message); } public SSLContextException(Throwable cause) { super(cause); } public SSLContextException(String message, Throwable cause) { super(message, cause); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Util.java0100644 0000000 0000000 00000104046 15051152474 032735 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.Socket; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.security.GeneralSecurityException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.security.cert.PKIXBuilderParameters; import java.security.cert.X509CertSelector; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.SSLSocket; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import org.apache.zookeeper.common.X509Exception.KeyManagerException; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.common.X509Exception.TrustManagerException; import org.apache.zookeeper.server.NettyServerCnxnFactory; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility code for X509 handling * * Default cipher suites: * * Performance testing done by Facebook engineers shows that on Intel x86_64 machines, Java9 performs better with * GCM and Java8 performs better with CBC, so these seem like reasonable defaults. */ public abstract class X509Util implements Closeable, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(X509Util.class); private static final String REJECT_CLIENT_RENEGOTIATION_PROPERTY = "jdk.tls.rejectClientInitiatedRenegotiation"; public static final String FIPS_MODE_PROPERTY = "zookeeper.fips-mode"; private static final boolean FIPS_MODE_DEFAULT = true; public static final String TLS_1_1 = "TLSv1.1"; public static final String TLS_1_2 = "TLSv1.2"; public static final String TLS_1_3 = "TLSv1.3"; static { // Client-initiated renegotiation in TLS is unsafe and // allows MITM attacks, so we should disable it unless // it was explicitly enabled by the user. // A brief summary of the issue can be found at // https://www.ietf.org/proceedings/76/slides/tls-7.pdf if (System.getProperty(REJECT_CLIENT_RENEGOTIATION_PROPERTY) == null) { LOG.info("Setting -D {}=true to disable client-initiated TLS renegotiation", REJECT_CLIENT_RENEGOTIATION_PROPERTY); System.setProperty(REJECT_CLIENT_RENEGOTIATION_PROPERTY, Boolean.TRUE.toString()); } } public static final String DEFAULT_PROTOCOL = defaultTlsProtocol(); /** * Return TLSv1.3 or TLSv1.2 depending on Java runtime version being used. * TLSv1.3 was first introduced in JDK11 and back-ported to OpenJDK 8u272. */ private static String defaultTlsProtocol() { String defaultProtocol = TLS_1_2; List supported = new ArrayList<>(); try { supported = Arrays.asList(SSLContext.getDefault().getSupportedSSLParameters().getProtocols()); if (supported.contains(TLS_1_3)) { defaultProtocol = TLS_1_3; } } catch (NoSuchAlgorithmException e) { // Ignore. } LOG.info("Default TLS protocol is {}, supported TLS protocols are {}", defaultProtocol, supported); return defaultProtocol; } // ChaCha20 was introduced in OpenJDK 11.0.15 and it is not supported by JDK8. private static String[] getTLSv13Ciphers() { return new String[]{"TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256", "TLS_CHACHA20_POLY1305_SHA256"}; } private static String[] getGCMCiphers() { return new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"}; } private static String[] getCBCCiphers() { return new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"}; } /** * Returns a filtered set of ciphers, where ciphers not supported by the JDK are removed. */ private static String[] getSupportedCiphers(String[]... cipherLists) { List supported = Arrays.asList( ((SSLServerSocketFactory) SSLServerSocketFactory.getDefault()).getSupportedCipherSuites()); return Arrays.stream(cipherLists).flatMap(Arrays::stream).filter(supported::contains).collect(Collectors.toList()).toArray(new String[0]); } // On Java 8, prefer CBC ciphers since AES-NI support is lacking and GCM is slower than CBC. private static final String[] DEFAULT_CIPHERS_JAVA8 = getSupportedCiphers(getCBCCiphers(), getGCMCiphers(), getTLSv13Ciphers()); // On Java 9 and later, prefer GCM ciphers due to improved AES-NI support. // Note that this performance assumption might not hold true for architectures other than x86_64. // TLSv1.3 ciphers can be added at the end of the list without impacting the priority of TLSv1.3 vs TLSv1.2. private static final String[] DEFAULT_CIPHERS_JAVA9 = getSupportedCiphers(getGCMCiphers(), getCBCCiphers(), getTLSv13Ciphers()); public static final int DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS = 5000; /** * Enum specifying the client auth requirement of server-side TLS sockets created by this X509Util. *
    *
  • NONE - do not request a client certificate.
  • *
  • WANT - request a client certificate, but allow anonymous clients to connect.
  • *
  • NEED - require a client certificate, disconnect anonymous clients.
  • *
* * If the config property is not set, the default value is NEED. */ public enum ClientAuth { NONE(io.netty.handler.ssl.ClientAuth.NONE), WANT(io.netty.handler.ssl.ClientAuth.OPTIONAL), NEED(io.netty.handler.ssl.ClientAuth.REQUIRE); private final io.netty.handler.ssl.ClientAuth nettyAuth; ClientAuth(io.netty.handler.ssl.ClientAuth nettyAuth) { this.nettyAuth = nettyAuth; } /** * Converts a property value to a ClientAuth enum. If the input string is empty or null, returns * ClientAuth.NEED. * @param prop the property string. * @return the ClientAuth. * @throws IllegalArgumentException if the property value is not "NONE", "WANT", "NEED", or empty/null. */ public static ClientAuth fromPropertyValue(String prop) { if (prop == null || prop.length() == 0) { return NEED; } return ClientAuth.valueOf(prop.toUpperCase()); } public io.netty.handler.ssl.ClientAuth toNettyClientAuth() { return nettyAuth; } } private final String sslProtocolProperty = getConfigPrefix() + "protocol"; private final String sslEnabledProtocolsProperty = getConfigPrefix() + "enabledProtocols"; private final String cipherSuitesProperty = getConfigPrefix() + "ciphersuites"; private final String sslKeystoreLocationProperty = getConfigPrefix() + "keyStore.location"; private final String sslKeystorePasswdProperty = getConfigPrefix() + "keyStore.password"; private final String sslKeystorePasswdPathProperty = getConfigPrefix() + "keyStore.passwordPath"; private final String sslKeystoreTypeProperty = getConfigPrefix() + "keyStore.type"; private final String sslTruststoreLocationProperty = getConfigPrefix() + "trustStore.location"; private final String sslTruststorePasswdProperty = getConfigPrefix() + "trustStore.password"; private final String sslTruststorePasswdPathProperty = getConfigPrefix() + "trustStore.passwordPath"; private final String sslTruststoreTypeProperty = getConfigPrefix() + "trustStore.type"; private final String sslContextSupplierClassProperty = getConfigPrefix() + "context.supplier.class"; private final String sslHostnameVerificationEnabledProperty = getConfigPrefix() + "hostnameVerification"; private final String sslClientHostnameVerificationEnabledProperty = getConfigPrefix() + "clientHostnameVerification"; private final String sslCrlEnabledProperty = getConfigPrefix() + "crl"; private final String sslOcspEnabledProperty = getConfigPrefix() + "ocsp"; private final String sslClientAuthProperty = getConfigPrefix() + "clientAuth"; private final String sslHandshakeDetectionTimeoutMillisProperty = getConfigPrefix() + "handshakeDetectionTimeoutMillis"; private final AtomicReference defaultSSLContextAndOptions = new AtomicReference<>(null); private FileChangeWatcher keyStoreFileWatcher; private FileChangeWatcher trustStoreFileWatcher; public X509Util() { keyStoreFileWatcher = trustStoreFileWatcher = null; } protected abstract String getConfigPrefix(); protected abstract boolean shouldVerifyClientHostname(); public String getSslProtocolProperty() { return sslProtocolProperty; } public String getSslEnabledProtocolsProperty() { return sslEnabledProtocolsProperty; } public String getCipherSuitesProperty() { return cipherSuitesProperty; } public String getSslKeystoreLocationProperty() { return sslKeystoreLocationProperty; } public String getSslCipherSuitesProperty() { return cipherSuitesProperty; } public String getSslKeystorePasswdProperty() { return sslKeystorePasswdProperty; } public String getSslKeystorePasswdPathProperty() { return sslKeystorePasswdPathProperty; } public String getSslKeystoreTypeProperty() { return sslKeystoreTypeProperty; } public String getSslTruststoreLocationProperty() { return sslTruststoreLocationProperty; } public String getSslTruststorePasswdProperty() { return sslTruststorePasswdProperty; } public String getSslTruststorePasswdPathProperty() { return sslTruststorePasswdPathProperty; } public String getSslTruststoreTypeProperty() { return sslTruststoreTypeProperty; } public String getSslContextSupplierClassProperty() { return sslContextSupplierClassProperty; } public String getSslHostnameVerificationEnabledProperty() { return sslHostnameVerificationEnabledProperty; } public String getSslClientHostnameVerificationEnabledProperty() { return sslClientHostnameVerificationEnabledProperty; } public String getSslCrlEnabledProperty() { return sslCrlEnabledProperty; } public String getSslOcspEnabledProperty() { return sslOcspEnabledProperty; } public String getSslClientAuthProperty() { return sslClientAuthProperty; } /** * Returns the config property key that controls the amount of time, in milliseconds, that the first * UnifiedServerSocket read operation will block for when trying to detect the client mode (TLS or PLAINTEXT). * * @return the config property key. */ public String getSslHandshakeDetectionTimeoutMillisProperty() { return sslHandshakeDetectionTimeoutMillisProperty; } public String getFipsModeProperty() { return FIPS_MODE_PROPERTY; } public static boolean getFipsMode(ZKConfig config) { return config.getBoolean(FIPS_MODE_PROPERTY, FIPS_MODE_DEFAULT); } public boolean isServerHostnameVerificationEnabled(ZKConfig config) { return config.getBoolean(this.getSslHostnameVerificationEnabledProperty(), true); } public boolean isClientHostnameVerificationEnabled(ZKConfig config) { return isServerHostnameVerificationEnabled(config) && config.getBoolean(this.getSslClientHostnameVerificationEnabledProperty(), shouldVerifyClientHostname()); } public SSLContext getDefaultSSLContext() throws X509Exception.SSLContextException { return getDefaultSSLContextAndOptions().getSSLContext(); } public SSLContext createSSLContext(ZKConfig config) throws SSLContextException { return createSSLContextAndOptions(config).getSSLContext(); } public SSLContextAndOptions getDefaultSSLContextAndOptions() throws X509Exception.SSLContextException { SSLContextAndOptions result = defaultSSLContextAndOptions.get(); if (result == null) { result = createSSLContextAndOptions(); if (!defaultSSLContextAndOptions.compareAndSet(null, result)) { // lost the race, another thread already set the value result = defaultSSLContextAndOptions.get(); } } return result; } private void resetDefaultSSLContextAndOptions() throws X509Exception.SSLContextException { SSLContextAndOptions newContext = createSSLContextAndOptions(); defaultSSLContextAndOptions.set(newContext); if (Boolean.getBoolean(NettyServerCnxnFactory.CLIENT_CERT_RELOAD_KEY)) { ProviderRegistry.addOrUpdateProvider(ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX + "x509"); } } private SSLContextAndOptions createSSLContextAndOptions() throws SSLContextException { /* * Since Configuration initializes the key store and trust store related * configuration from system property. Reading property from * configuration will be same reading from system property */ return createSSLContextAndOptions(new ZKConfig()); } /** * Returns the max amount of time, in milliseconds, that the first UnifiedServerSocket read() operation should * block for when trying to detect the client mode (TLS or PLAINTEXT). * Defaults to {@link X509Util#DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS}. * * @return the handshake detection timeout, in milliseconds. */ public int getSslHandshakeTimeoutMillis() { try { SSLContextAndOptions ctx = getDefaultSSLContextAndOptions(); return ctx.getHandshakeDetectionTimeoutMillis(); } catch (SSLContextException e) { LOG.error("Error creating SSL context and options", e); return DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS; } catch (Exception e) { LOG.error("Error parsing config property {}", getSslHandshakeDetectionTimeoutMillisProperty(), e); return DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS; } } @SuppressWarnings("unchecked") public SSLContextAndOptions createSSLContextAndOptions(ZKConfig config) throws SSLContextException { final String supplierContextClassName = config.getProperty(sslContextSupplierClassProperty); if (supplierContextClassName != null) { LOG.debug("Loading SSLContext supplier from property '{}'", sslContextSupplierClassProperty); try { Class sslContextClass = Class.forName(supplierContextClassName); Supplier sslContextSupplier = (Supplier) sslContextClass.getConstructor().newInstance(); return new SSLContextAndOptions(this, config, sslContextSupplier.get()); } catch (ClassNotFoundException | ClassCastException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { throw new SSLContextException("Could not retrieve the SSLContext from supplier source '" + supplierContextClassName + "' provided in the property '" + sslContextSupplierClassProperty + "'", e); } } else { return createSSLContextAndOptionsFromConfig(config); } } public SSLContextAndOptions createSSLContextAndOptionsFromConfig(ZKConfig config) throws SSLContextException { KeyManager[] keyManagers = null; TrustManager[] trustManagers = null; String keyStoreLocationProp = config.getProperty(sslKeystoreLocationProperty, ""); String keyStorePasswordProp = getPasswordFromConfigPropertyOrFile(config, sslKeystorePasswdProperty, sslKeystorePasswdPathProperty); String keyStoreTypeProp = config.getProperty(sslKeystoreTypeProperty); // There are legal states in some use cases for null KeyManager or TrustManager. // But if a user wanna specify one, location is required. Password defaults to empty string if it is not // specified by the user. if (keyStoreLocationProp.isEmpty()) { LOG.warn("{} not specified", getSslKeystoreLocationProperty()); } else { try { keyManagers = new KeyManager[]{createKeyManager(keyStoreLocationProp, keyStorePasswordProp, keyStoreTypeProp)}; } catch (KeyManagerException keyManagerException) { throw new SSLContextException("Failed to create KeyManager", keyManagerException); } catch (IllegalArgumentException e) { throw new SSLContextException("Bad value for " + sslKeystoreTypeProperty + ": " + keyStoreTypeProp, e); } } String trustStoreLocationProp = config.getProperty(sslTruststoreLocationProperty, ""); String trustStorePasswordProp = getPasswordFromConfigPropertyOrFile(config, sslTruststorePasswdProperty, sslTruststorePasswdPathProperty); String trustStoreTypeProp = config.getProperty(sslTruststoreTypeProperty); boolean sslCrlEnabled = config.getBoolean(this.sslCrlEnabledProperty); boolean sslOcspEnabled = config.getBoolean(this.sslOcspEnabledProperty); boolean sslServerHostnameVerificationEnabled = isServerHostnameVerificationEnabled(config); boolean sslClientHostnameVerificationEnabled = isClientHostnameVerificationEnabled(config); boolean fipsMode = getFipsMode(config); if (trustStoreLocationProp.isEmpty()) { LOG.warn("{} not specified", getSslTruststoreLocationProperty()); } else { try { trustManagers = new TrustManager[]{ createTrustManager(trustStoreLocationProp, trustStorePasswordProp, trustStoreTypeProp, sslCrlEnabled, sslOcspEnabled, sslServerHostnameVerificationEnabled, sslClientHostnameVerificationEnabled, fipsMode)}; } catch (TrustManagerException trustManagerException) { throw new SSLContextException("Failed to create TrustManager", trustManagerException); } catch (IllegalArgumentException e) { throw new SSLContextException("Bad value for " + sslTruststoreTypeProperty + ": " + trustStoreTypeProp, e); } } String protocol = config.getProperty(sslProtocolProperty, DEFAULT_PROTOCOL); try { SSLContext sslContext = SSLContext.getInstance(protocol); sslContext.init(keyManagers, trustManagers, null); return new SSLContextAndOptions(this, config, sslContext); } catch (NoSuchAlgorithmException | KeyManagementException sslContextInitException) { throw new SSLContextException(sslContextInitException); } } public static KeyStore loadKeyStore( String keyStoreLocation, String keyStorePassword, String keyStoreTypeProp) throws IOException, GeneralSecurityException { KeyStoreFileType storeFileType = KeyStoreFileType.fromPropertyValueOrFileName(keyStoreTypeProp, keyStoreLocation); return FileKeyStoreLoaderBuilderProvider .getBuilderForKeyStoreFileType(storeFileType) .setKeyStorePath(keyStoreLocation) .setKeyStorePassword(keyStorePassword) .build() .loadKeyStore(); } public static KeyStore loadTrustStore( String trustStoreLocation, String trustStorePassword, String trustStoreTypeProp) throws IOException, GeneralSecurityException { KeyStoreFileType storeFileType = KeyStoreFileType.fromPropertyValueOrFileName(trustStoreTypeProp, trustStoreLocation); return FileKeyStoreLoaderBuilderProvider .getBuilderForKeyStoreFileType(storeFileType) .setTrustStorePath(trustStoreLocation) .setTrustStorePassword(trustStorePassword) .build() .loadTrustStore(); } /** * Returns the password specified by the given property or from the file specified by the given path property. * If both are specified, the value stored in the file will be returned. * * @param config Zookeeper configuration * @param propertyName property name * @param pathPropertyName path property name * @return the password value */ public String getPasswordFromConfigPropertyOrFile(final ZKConfig config, final String propertyName, final String pathPropertyName) { String value = config.getProperty(propertyName, ""); final String pathProperty = config.getProperty(pathPropertyName, ""); if (!pathProperty.isEmpty()) { value = String.valueOf(SecretUtils.readSecret(pathProperty)); } return value; } /** * Creates a key manager by loading the key store from the given file of * the given type, optionally decrypting it using the given password. * @param keyStoreLocation the location of the key store file. * @param keyStorePassword optional password to decrypt the key store. If * empty, assumes the key store is not encrypted. * @param keyStoreTypeProp must be JKS, PEM, PKCS12, BCFKS or null. If null, * attempts to autodetect the key store type from * the file extension (e.g. .jks / .pem). * @return the key manager. * @throws KeyManagerException if something goes wrong. */ public static X509KeyManager createKeyManager( String keyStoreLocation, String keyStorePassword, String keyStoreTypeProp) throws KeyManagerException { if (keyStorePassword == null) { keyStorePassword = ""; } try { KeyStore ks = loadKeyStore(keyStoreLocation, keyStorePassword, keyStoreTypeProp); KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX"); kmf.init(ks, keyStorePassword.toCharArray()); for (KeyManager km : kmf.getKeyManagers()) { if (km instanceof X509KeyManager) { return (X509KeyManager) km; } } throw new KeyManagerException("Couldn't find X509KeyManager"); } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { throw new KeyManagerException(e); } } /** * Creates a trust manager by loading the trust store from the given file * of the given type, optionally decrypting it using the given password. * * @param trustStoreLocation the location of the trust store file. * @param trustStorePassword optional password to decrypt the trust store * (only applies to JKS trust stores). If empty, * assumes the trust store is not encrypted. * @param trustStoreTypeProp must be JKS, PEM, PKCS12, BCFKS or null. If * null, attempts to autodetect the trust store * type from the file extension (e.g. .jks / .pem). * @param crlEnabled enable CRL (certificate revocation list) checks. * @param ocspEnabled enable OCSP (online certificate status protocol) * checks. * @param serverHostnameVerificationEnabled if true, verify hostnames of * remote servers that client * sockets created by this * X509Util connect to. * @param clientHostnameVerificationEnabled if true, verify hostnames of * remote clients that server * sockets created by this * X509Util accept connections * from. * @return the trust manager. * @throws TrustManagerException if something goes wrong. */ public static X509TrustManager createTrustManager( String trustStoreLocation, String trustStorePassword, String trustStoreTypeProp, boolean crlEnabled, boolean ocspEnabled, final boolean serverHostnameVerificationEnabled, final boolean clientHostnameVerificationEnabled, final boolean fipsMode) throws TrustManagerException { if (trustStorePassword == null) { trustStorePassword = ""; } try { KeyStore ts = loadTrustStore(trustStoreLocation, trustStorePassword, trustStoreTypeProp); PKIXBuilderParameters pbParams = new PKIXBuilderParameters(ts, new X509CertSelector()); if (crlEnabled || ocspEnabled) { pbParams.setRevocationEnabled(true); System.setProperty("com.sun.net.ssl.checkRevocation", "true"); System.setProperty("com.sun.security.enableCRLDP", "true"); if (ocspEnabled) { Security.setProperty("ocsp.enable", "true"); } } else { pbParams.setRevocationEnabled(false); } // Revocation checking is only supported with the PKIX algorithm TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); tmf.init(new CertPathTrustManagerParameters(pbParams)); for (final TrustManager tm : tmf.getTrustManagers()) { if (tm instanceof X509ExtendedTrustManager) { if (fipsMode) { if (LOG.isDebugEnabled()) { LOG.debug("FIPS mode is ON: selecting standard x509 trust manager {}", tm); } return (X509TrustManager) tm; } if (LOG.isDebugEnabled()) { LOG.debug("FIPS mode is OFF: creating ZKTrustManager"); } return new ZKTrustManager((X509ExtendedTrustManager) tm, serverHostnameVerificationEnabled, clientHostnameVerificationEnabled); } } throw new TrustManagerException("Couldn't find X509TrustManager"); } catch (IOException | GeneralSecurityException | IllegalArgumentException e) { throw new TrustManagerException(e); } } public SSLSocket createSSLSocket() throws X509Exception, IOException { return getDefaultSSLContextAndOptions().createSSLSocket(); } public SSLSocket createSSLSocket(Socket socket, byte[] pushbackBytes) throws X509Exception, IOException { return getDefaultSSLContextAndOptions().createSSLSocket(socket, pushbackBytes); } public SSLServerSocket createSSLServerSocket() throws X509Exception, IOException { return getDefaultSSLContextAndOptions().createSSLServerSocket(); } public SSLServerSocket createSSLServerSocket(int port) throws X509Exception, IOException { return getDefaultSSLContextAndOptions().createSSLServerSocket(port); } static String[] getDefaultCipherSuites() { return getDefaultCipherSuitesForJavaVersion(System.getProperty("java.specification.version")); } static String[] getDefaultCipherSuitesForJavaVersion(String javaVersion) { Objects.requireNonNull(javaVersion); if (javaVersion.matches("\\d+")) { // Must be Java 9 or later LOG.debug("Using Java9+ optimized cipher suites for Java version {}", javaVersion); return DEFAULT_CIPHERS_JAVA9; } else if (javaVersion.startsWith("1.")) { // Must be Java 1.8 or earlier LOG.debug("Using Java8 optimized cipher suites for Java version {}", javaVersion); return DEFAULT_CIPHERS_JAVA8; } else { LOG.debug("Could not parse java version {}, using Java8 optimized cipher suites", javaVersion); return DEFAULT_CIPHERS_JAVA8; } } private FileChangeWatcher newFileChangeWatcher(String fileLocation) throws IOException { if (fileLocation == null || fileLocation.isEmpty()) { return null; } final Path filePath = Paths.get(fileLocation).toAbsolutePath(); Path parentPath = filePath.getParent(); if (parentPath == null) { throw new IOException("Key/trust store path does not have a parent: " + filePath); } return new FileChangeWatcher(parentPath, watchEvent -> { handleWatchEvent(filePath, watchEvent); }); } /** * Enables automatic reloading of the trust store and key store files when they change on disk. * * @throws IOException if creating the FileChangeWatcher objects fails. */ public void enableCertFileReloading() throws IOException { LOG.info("enabling cert file reloading"); ZKConfig config = new ZKConfig(); FileChangeWatcher newKeyStoreFileWatcher = newFileChangeWatcher(config.getProperty(sslKeystoreLocationProperty)); if (newKeyStoreFileWatcher != null) { // stop old watcher if there is one if (keyStoreFileWatcher != null) { keyStoreFileWatcher.stop(); } keyStoreFileWatcher = newKeyStoreFileWatcher; keyStoreFileWatcher.start(); } FileChangeWatcher newTrustStoreFileWatcher = newFileChangeWatcher(config.getProperty(sslTruststoreLocationProperty)); if (newTrustStoreFileWatcher != null) { // stop old watcher if there is one if (trustStoreFileWatcher != null) { trustStoreFileWatcher.stop(); } trustStoreFileWatcher = newTrustStoreFileWatcher; trustStoreFileWatcher.start(); } } /** * Disables automatic reloading of the trust store and key store files when they change on disk. * Stops background threads and closes WatchService instances. */ @Override public void close() { defaultSSLContextAndOptions.set(null); if (keyStoreFileWatcher != null) { keyStoreFileWatcher.stop(); keyStoreFileWatcher = null; } if (trustStoreFileWatcher != null) { trustStoreFileWatcher.stop(); trustStoreFileWatcher = null; } } /** * Handler for watch events that let us know a file we may care about has changed on disk. * * @param filePath the path to the file we are watching for changes. * @param event the WatchEvent. */ private void handleWatchEvent(Path filePath, WatchEvent event) { boolean shouldResetContext = false; Path dirPath = filePath.getParent(); if (event.kind().equals(StandardWatchEventKinds.OVERFLOW)) { // If we get notified about possibly missed events, reload the key store / trust store just to be sure. shouldResetContext = true; } else if (event.kind().equals(StandardWatchEventKinds.ENTRY_MODIFY) || event.kind().equals(StandardWatchEventKinds.ENTRY_CREATE)) { Path eventFilePath = dirPath.resolve((Path) event.context()); if (filePath.equals(eventFilePath)) { shouldResetContext = true; } } // Note: we don't care about delete events if (shouldResetContext) { LOG.debug( "Attempting to reset default SSL context after receiving watch event: {} with context: {}", event.kind(), event.context()); try { this.resetDefaultSSLContextAndOptions(); } catch (SSLContextException e) { throw new RuntimeException(e); } } else { LOG.debug( "Ignoring watch event and keeping previous default SSL context. Event kind: {} with context: {}", event.kind(), event.context()); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKConfig.java0100644 0000000 0000000 00000027022 15051152474 033102 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import org.apache.zookeeper.Environment; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.util.VerifyingFileFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is a base class for the configurations of both client and server. * It supports reading client configuration from both system properties and * configuration file. A user can override any system property by calling * {@link #setProperty(String, String)}. * @since 3.5.2 */ public class ZKConfig { private static final Logger LOG = LoggerFactory.getLogger(ZKConfig.class); public static final String JUTE_MAXBUFFER = "jute.maxbuffer"; /** * Path to a kinit binary: {@value}. Defaults to * "/usr/bin/kinit" */ public static final String KINIT_COMMAND = "zookeeper.kinit"; public static final String JGSS_NATIVE = "sun.security.jgss.native"; private final Map properties = new HashMap<>(); /** * properties, which are common to both client and server, are initialized * from system properties */ public ZKConfig() { init(); } /** * @param configPath * Configuration file path * @throws ConfigException * if failed to load configuration properties */ public ZKConfig(String configPath) throws ConfigException { this(new File(configPath)); } /** * * @param configFile * Configuration file * @throws ConfigException * if failed to load configuration properties */ public ZKConfig(File configFile) throws ConfigException { this(); addConfiguration(configFile); LOG.info("ZK Config {}", this.properties); } private void init() { /** * backward compatibility for all currently available client properties */ handleBackwardCompatibility(); } /** * Now onwards client code will use properties from this class but older * clients still be setting properties through system properties. So to make * this change backward compatible we should set old system properties in * this configuration. */ protected void handleBackwardCompatibility() { properties.put(JUTE_MAXBUFFER, System.getProperty(JUTE_MAXBUFFER)); properties.put(KINIT_COMMAND, System.getProperty(KINIT_COMMAND)); properties.put(JGSS_NATIVE, System.getProperty(JGSS_NATIVE)); try (ClientX509Util clientX509Util = new ClientX509Util()) { putSSLProperties(clientX509Util); properties.put(clientX509Util.getSslAuthProviderProperty(), System.getProperty(clientX509Util.getSslAuthProviderProperty())); properties.put(clientX509Util.getSslProviderProperty(), System.getProperty(clientX509Util.getSslProviderProperty())); } try (X509Util x509Util = new QuorumX509Util()) { putSSLProperties(x509Util); } } private void putSSLProperties(X509Util x509Util) { properties.put(x509Util.getSslProtocolProperty(), System.getProperty(x509Util.getSslProtocolProperty())); properties.put(x509Util.getSslEnabledProtocolsProperty(), System.getProperty(x509Util.getSslEnabledProtocolsProperty())); properties.put(x509Util.getSslCipherSuitesProperty(), System.getProperty(x509Util.getSslCipherSuitesProperty())); properties.put(x509Util.getSslKeystoreLocationProperty(), System.getProperty(x509Util.getSslKeystoreLocationProperty())); properties.put(x509Util.getSslKeystorePasswdProperty(), System.getProperty(x509Util.getSslKeystorePasswdProperty())); properties.put(x509Util.getSslKeystorePasswdPathProperty(), System.getProperty(x509Util.getSslKeystorePasswdPathProperty())); properties.put(x509Util.getSslKeystoreTypeProperty(), System.getProperty(x509Util.getSslKeystoreTypeProperty())); properties.put(x509Util.getSslTruststoreLocationProperty(), System.getProperty(x509Util.getSslTruststoreLocationProperty())); properties.put(x509Util.getSslTruststorePasswdProperty(), System.getProperty(x509Util.getSslTruststorePasswdProperty())); properties.put(x509Util.getSslTruststorePasswdPathProperty(), System.getProperty(x509Util.getSslTruststorePasswdPathProperty())); properties.put(x509Util.getSslTruststoreTypeProperty(), System.getProperty(x509Util.getSslTruststoreTypeProperty())); properties.put(x509Util.getSslContextSupplierClassProperty(), System.getProperty(x509Util.getSslContextSupplierClassProperty())); properties.put(x509Util.getSslClientHostnameVerificationEnabledProperty(), System.getProperty(x509Util.getSslClientHostnameVerificationEnabledProperty())); properties.put(x509Util.getSslHostnameVerificationEnabledProperty(), System.getProperty(x509Util.getSslHostnameVerificationEnabledProperty())); properties.put(x509Util.getSslCrlEnabledProperty(), System.getProperty(x509Util.getSslCrlEnabledProperty())); properties.put(x509Util.getSslOcspEnabledProperty(), System.getProperty(x509Util.getSslOcspEnabledProperty())); properties.put(x509Util.getSslClientAuthProperty(), System.getProperty(x509Util.getSslClientAuthProperty())); properties.put(x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), System.getProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty())); properties.put(x509Util.getFipsModeProperty(), System.getProperty(x509Util.getFipsModeProperty())); } /** * Get the property value * * @param key * @return property value */ public String getProperty(String key) { return properties.get(key); } /** * Get the property value, if it is null return default value * * @param key * property key * @param defaultValue * @return property value or default value */ public String getProperty(String key, String defaultValue) { String value = properties.get(key); return (value == null) ? defaultValue : value; } /** * Return the value of "java.security.auth.login.config" system property * * @return value */ public String getJaasConfKey() { return System.getProperty(Environment.JAAS_CONF_KEY); } /** * Maps the specified key to the specified value. * key can not be null. If key is already mapped then the old * value of the key is replaced by the specified * value. * * @param key * @param value */ public void setProperty(String key, String value) { if (null == key) { throw new IllegalArgumentException("property key is null."); } String oldValue = properties.put(key, value); if (null != oldValue && !oldValue.equals(value)) { LOG.debug("key {}'s value {} is replaced with new value {}", key, oldValue, value); } } /** * Add a configuration resource. The properties form this configuration will * overwrite corresponding already loaded property and system property * * @param configFile * Configuration file. */ public void addConfiguration(File configFile) throws ConfigException { LOG.info("Reading configuration from: {}", configFile.getAbsolutePath()); try { configFile = (new VerifyingFileFactory.Builder(LOG).warnForRelativePath() .failForNonExistingPath() .build()).validate(configFile); Properties cfg = new Properties(); FileInputStream in = new FileInputStream(configFile); try { cfg.load(in); } finally { in.close(); } parseProperties(cfg); } catch (IOException | IllegalArgumentException e) { LOG.error("Error while configuration from: {}", configFile.getAbsolutePath(), e); throw new ConfigException("Error while processing " + configFile.getAbsolutePath(), e); } } /** * Add a configuration resource. The properties form this configuration will * overwrite corresponding already loaded property and system property * * @param configPath * Configuration file path. */ public void addConfiguration(String configPath) throws ConfigException { addConfiguration(new File(configPath)); } private void parseProperties(Properties cfg) { for (Entry entry : cfg.entrySet()) { String key = entry.getKey().toString().trim(); String value = entry.getValue().toString().trim(); setProperty(key, value); } } /** * Returns {@code true} if and only if the property named by the argument * exists and is equal to the string {@code "true"}. */ public boolean getBoolean(String key) { return getBoolean(key, false); } /** * Get the value of the key property as a boolean. Returns * {@code true} if and only if the property named by the argument exists and is equal * to the string {@code "true"}. If the property is not set, the provided * defaultValue is returned. * * @param key * property key. * @param defaultValue * default value. * @return return property value as an boolean, or * defaultValue */ public boolean getBoolean(String key, boolean defaultValue) { String propertyValue = getProperty(key); if (propertyValue == null) { return defaultValue; } else { return Boolean.parseBoolean(propertyValue.trim()); } } /** * Get the value of the key property as an int. If * property is not set, the provided defaultValue is returned * * @param key * property key. * @param defaultValue * default value. * @throws NumberFormatException * when the value is invalid * @return return property value as an int, or * defaultValue */ public int getInt(String key, int defaultValue) { String value = getProperty(key); if (value != null) { return Integer.decode(value.trim()); } return defaultValue; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_ZK0100644 0000000 0000000 00000000163 15051152474 032600 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKHostnameVerifier.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKHostnameVerifier0100644 0000000 0000000 00000033417 15051152474 034234 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.cert.Certificate; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.NoSuchElementException; import java.util.Objects; import java.util.regex.Pattern; import javax.naming.InvalidNameException; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Note: copied from Apache httpclient with some modifications. We want host verification, but depending * on the httpclient jar caused unexplained performance regressions (even when the code was not used). */ class ZKHostnameVerifier implements HostnameVerifier { /** * Note: copied from Apache httpclient with some minor modifications. We want host verification, but depending * on the httpclient jar caused unexplained performance regressions (even when the code was not used). */ private static final class SubjectName { static final int DNS = 2; static final int IP = 7; private final String value; private final int type; static SubjectName IP(final String value) { return new SubjectName(value, IP); } static SubjectName DNS(final String value) { return new SubjectName(value, DNS); } SubjectName(final String value, final int type) { if (type != DNS && type != IP) { throw new IllegalArgumentException("Invalid type: " + type); } this.value = Objects.requireNonNull(value); this.type = type; } public int getType() { return type; } public String getValue() { return value; } @Override public String toString() { return value; } } /** * Note: copied from Apache httpclient. We want host verification, but depending on the * httpclient jar caused unexplained performance regressions (even when the code was not used). */ private static class InetAddressUtils { private InetAddressUtils() { } private static final Pattern IPV4_PATTERN = Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"); private static final Pattern IPV6_STD_PATTERN = Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$"); private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = Pattern.compile("^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$"); static boolean isIPv4Address(final String input) { return IPV4_PATTERN.matcher(input).matches(); } static boolean isIPv6StdAddress(final String input) { return IPV6_STD_PATTERN.matcher(input).matches(); } static boolean isIPv6HexCompressedAddress(final String input) { return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); } static boolean isIPv6Address(final String input) { return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input); } } enum HostNameType { IPv4(7), IPv6(7), DNS(2); final int subjectType; HostNameType(final int subjectType) { this.subjectType = subjectType; } } private final Logger log = LoggerFactory.getLogger(ZKHostnameVerifier.class); @Override public boolean verify(final String host, final SSLSession session) { try { final Certificate[] certs = session.getPeerCertificates(); final X509Certificate x509 = (X509Certificate) certs[0]; verify(host, x509); return true; } catch (final SSLException ex) { log.debug("Unexpected exception", ex); return false; } } void verify(final String host, final X509Certificate cert) throws SSLException { final HostNameType hostType = determineHostFormat(host); final List subjectAlts = getSubjectAltNames(cert); if (subjectAlts != null && !subjectAlts.isEmpty()) { switch (hostType) { case IPv4: matchIPAddress(host, subjectAlts); break; case IPv6: matchIPv6Address(host, subjectAlts); break; default: matchDNSName(host, subjectAlts); } } else { // CN matching has been deprecated by rfc2818 and can be used // as fallback only when no subjectAlts are available final X500Principal subjectPrincipal = cert.getSubjectX500Principal(); final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253)); if (cn == null) { throw new SSLException("Certificate subject for <" + host + "> doesn't contain " + "a common name and does not have alternative names"); } matchCN(host, cn); } } private static void matchIPAddress(final String host, final List subjectAlts) throws SSLException { for (int i = 0; i < subjectAlts.size(); i++) { final SubjectName subjectAlt = subjectAlts.get(i); if (subjectAlt.getType() == SubjectName.IP) { if (host.equals(subjectAlt.getValue())) { return; } } } throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } private static void matchIPv6Address(final String host, final List subjectAlts) throws SSLException { final String normalisedHost = normaliseAddress(host); for (int i = 0; i < subjectAlts.size(); i++) { final SubjectName subjectAlt = subjectAlts.get(i); if (subjectAlt.getType() == SubjectName.IP) { final String normalizedSubjectAlt = normaliseAddress(subjectAlt.getValue()); if (normalisedHost.equals(normalizedSubjectAlt)) { return; } } } throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } private static void matchDNSName(final String host, final List subjectAlts) throws SSLException { final String normalizedHost = host.toLowerCase(Locale.ROOT); for (int i = 0; i < subjectAlts.size(); i++) { final SubjectName subjectAlt = subjectAlts.get(i); if (subjectAlt.getType() == SubjectName.DNS) { final String normalizedSubjectAlt = subjectAlt.getValue().toLowerCase(Locale.ROOT); if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt)) { return; } } } throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " + "of the subject alternative names: " + subjectAlts); } private static void matchCN(final String host, final String cn) throws SSLException { final String normalizedHost = host.toLowerCase(Locale.ROOT); final String normalizedCn = cn.toLowerCase(Locale.ROOT); if (!matchIdentityStrict(normalizedHost, normalizedCn)) { throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match " + "common name of the certificate subject: " + cn); } } private static boolean matchIdentity(final String host, final String identity, final boolean strict) { // RFC 2818, 3.1. Server Identity // "...Names may contain the wildcard // character * which is considered to match any single domain name // component or component fragment..." // Based on this statement presuming only singular wildcard is legal final int asteriskIdx = identity.indexOf('*'); if (asteriskIdx != -1) { final String prefix = identity.substring(0, asteriskIdx); final String suffix = identity.substring(asteriskIdx + 1); if (!prefix.isEmpty() && !host.startsWith(prefix)) { return false; } if (!suffix.isEmpty() && !host.endsWith(suffix)) { return false; } // Additional sanity checks on content selected by wildcard can be done here if (strict) { final String remainder = host.substring(prefix.length(), host.length() - suffix.length()); return !remainder.contains("."); } return true; } return host.equalsIgnoreCase(identity); } private static boolean matchIdentityStrict(final String host, final String identity) { return matchIdentity(host, identity, true); } private static String extractCN(final String subjectPrincipal) throws SSLException { if (subjectPrincipal == null) { return null; } try { final LdapName subjectDN = new LdapName(subjectPrincipal); final List rdns = subjectDN.getRdns(); for (int i = rdns.size() - 1; i >= 0; i--) { final Rdn rds = rdns.get(i); final Attributes attributes = rds.toAttributes(); final Attribute cn = attributes.get("cn"); if (cn != null) { try { final Object value = cn.get(); if (value != null) { return value.toString(); } } catch (final NoSuchElementException ignore) { // ignore exception } catch (final NamingException ignore) { // ignore exception } } } return null; } catch (final InvalidNameException e) { throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name"); } } private static HostNameType determineHostFormat(final String host) { if (InetAddressUtils.isIPv4Address(host)) { return HostNameType.IPv4; } String s = host; if (s.startsWith("[") && s.endsWith("]")) { s = host.substring(1, host.length() - 1); } if (InetAddressUtils.isIPv6Address(s)) { return HostNameType.IPv6; } return HostNameType.DNS; } private static List getSubjectAltNames(final X509Certificate cert) { try { final Collection> entries = cert.getSubjectAlternativeNames(); if (entries == null) { return Collections.emptyList(); } final List result = new ArrayList<>(); for (List entry : entries) { final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null; if (type != null) { if (type == SubjectName.DNS || type == SubjectName.IP) { final Object o = entry.get(1); if (o instanceof String) { result.add(new SubjectName((String) o, type)); } else if (o instanceof byte[]) { // TODO ASN.1 DER encoded form } } } } return result; } catch (final CertificateParsingException ignore) { return Collections.emptyList(); } } /* * Normalize IPv6 or DNS name. */ private static String normaliseAddress(final String hostname) { if (hostname == null) { return hostname; } try { final InetAddress inetAddress = InetAddress.getByName(hostname); return inetAddress.getHostAddress(); } catch (final UnknownHostException unexpected) { // Should not happen, because we check for IPv6 address above return hostname; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_common_ZK0100644 0000000 0000000 00000000157 15051152474 032603 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/common/ZKTrustManager.jav0100644 0000000 0000000 00000020276 15051152474 034154 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.X509ExtendedTrustManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A custom TrustManager that supports hostname verification via org.apache.http.conn.ssl.DefaultHostnameVerifier. * * We attempt to perform verification using just the IP address first and if that fails will attempt to perform a * reverse DNS lookup and verify using the hostname. */ public class ZKTrustManager extends X509ExtendedTrustManager { private static final Logger LOG = LoggerFactory.getLogger(ZKTrustManager.class); private final X509ExtendedTrustManager x509ExtendedTrustManager; private final boolean serverHostnameVerificationEnabled; private final boolean clientHostnameVerificationEnabled; private final ZKHostnameVerifier hostnameVerifier; /** * Instantiate a new ZKTrustManager. * * @param x509ExtendedTrustManager The trustmanager to use for checkClientTrusted/checkServerTrusted logic * @param serverHostnameVerificationEnabled If true, this TrustManager should verify hostnames of servers that this * instance connects to. * @param clientHostnameVerificationEnabled If true, the hostname of a client connecting to this machine will be * verified. */ ZKTrustManager( X509ExtendedTrustManager x509ExtendedTrustManager, boolean serverHostnameVerificationEnabled, boolean clientHostnameVerificationEnabled) { this(x509ExtendedTrustManager, serverHostnameVerificationEnabled, clientHostnameVerificationEnabled, new ZKHostnameVerifier()); } ZKTrustManager( X509ExtendedTrustManager x509ExtendedTrustManager, boolean serverHostnameVerificationEnabled, boolean clientHostnameVerificationEnabled, ZKHostnameVerifier hostnameVerifier) { this.x509ExtendedTrustManager = x509ExtendedTrustManager; this.serverHostnameVerificationEnabled = serverHostnameVerificationEnabled; this.clientHostnameVerificationEnabled = clientHostnameVerificationEnabled; this.hostnameVerifier = hostnameVerifier; } @Override public X509Certificate[] getAcceptedIssuers() { return x509ExtendedTrustManager.getAcceptedIssuers(); } @Override public void checkClientTrusted( X509Certificate[] chain, String authType, Socket socket) throws CertificateException { x509ExtendedTrustManager.checkClientTrusted(chain, authType, socket); if (clientHostnameVerificationEnabled) { if (LOG.isDebugEnabled()) { LOG.debug("Check client trusted socket.getInetAddress(): {}, {}", socket.getInetAddress(), socket); } performHostVerification(socket.getInetAddress(), chain[0]); } } @Override public void checkServerTrusted( X509Certificate[] chain, String authType, Socket socket) throws CertificateException { x509ExtendedTrustManager.checkServerTrusted(chain, authType, socket); if (serverHostnameVerificationEnabled) { if (LOG.isDebugEnabled()) { LOG.debug("Check server trusted socket.getInetAddress(): {}, {}", socket.getInetAddress(), socket); } performHostVerification(socket.getInetAddress(), chain[0]); } } @Override public void checkClientTrusted( X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { x509ExtendedTrustManager.checkClientTrusted(chain, authType, engine); if (clientHostnameVerificationEnabled) { try { if (LOG.isDebugEnabled()) { LOG.debug("Check client trusted engine.getPeerHost(): {}, {}", engine.getPeerHost(), engine); } performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); } catch (UnknownHostException e) { throw new CertificateException("Failed to verify host", e); } } } @Override public void checkServerTrusted( X509Certificate[] chain, String authType, SSLEngine engine ) throws CertificateException { x509ExtendedTrustManager.checkServerTrusted(chain, authType, engine); if (serverHostnameVerificationEnabled) { try { if (LOG.isDebugEnabled()) { LOG.debug("Check server trusted engine.getPeerHost(): {}, {}", engine.getPeerHost(), engine); } performHostVerification(InetAddress.getByName(engine.getPeerHost()), chain[0]); } catch (UnknownHostException e) { throw new CertificateException("Failed to verify host", e); } } } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { x509ExtendedTrustManager.checkClientTrusted(chain, authType); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { x509ExtendedTrustManager.checkServerTrusted(chain, authType); } /** * Compares peer's hostname with the one stored in the provided certificate. Performs verification * with the help of provided HostnameVerifier. * * Attempts to verify the IP address first, if it fails, check the hostname. Performs reverse DNS lookup * if hostname is not available. (Mostly the case in client verifications.) * * @param inetAddress Peer's inet address. * @param certificate Peer's certificate * @throws CertificateException Thrown if the provided certificate doesn't match the peer hostname. */ private void performHostVerification( InetAddress inetAddress, X509Certificate certificate ) throws CertificateException { String hostAddress = ""; String hostName = ""; try { hostAddress = inetAddress.getHostAddress(); if (LOG.isDebugEnabled()) { LOG.debug("Trying to verify host address first: {}", hostAddress); } hostnameVerifier.verify(hostAddress, certificate); } catch (SSLException addressVerificationException) { try { hostName = inetAddress.getHostName(); if (LOG.isDebugEnabled()) { LOG.debug( "Failed to verify host address: {}, trying to verify host name: {}", hostAddress, hostName); } hostnameVerifier.verify(hostName, certificate); } catch (SSLException hostnameVerificationException) { LOG.error("Failed to verify host address: {}", hostAddress, addressVerificationException); LOG.error("Failed to verify hostname: {}", hostName, hostnameVerificationException); throw new CertificateException("Failed to verify both host address and host name", hostnameVerificationException); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_compat_Pr0100644 0000000 0000000 00000000160 15051152474 032625 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/compat/ProtocolManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/compat/ProtocolManager.ja0100644 0000000 0000000 00000015464 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.compat; import java.io.IOException; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ConnectResponse; /** * A manager for switching behaviours between difference wire protocol. *

* Basically, wire protocol should be backward and forward compatible between minor versions. * However, there are several cases that it's different due to Jute's limitations. */ public final class ProtocolManager { private volatile Boolean isReadonlyAvailable = null; public boolean isReadonlyAvailable() { return isReadonlyAvailable != null && isReadonlyAvailable; } /** * Deserializing {@link ConnectRequest} should be specially handled for request from client * version before and including ZooKeeper 3.3 which doesn't understand readOnly field. */ public ConnectRequest deserializeConnectRequest(InputArchive inputArchive) throws IOException { if (isReadonlyAvailable != null) { if (isReadonlyAvailable) { return deserializeConnectRequestWithReadonly(inputArchive); } else { return deserializeConnectRequestWithoutReadonly(inputArchive); } } final ConnectRequest request = deserializeConnectRequestWithoutReadonly(inputArchive); try { request.setReadOnly(inputArchive.readBool("readOnly")); this.isReadonlyAvailable = true; } catch (Exception e) { request.setReadOnly(false); // old version doesn't have readonly concept this.isReadonlyAvailable = false; } return request; } private ConnectRequest deserializeConnectRequestWithReadonly(InputArchive inputArchive) throws IOException { final ConnectRequest request = new ConnectRequest(); request.deserialize(inputArchive, "connect"); return request; } private ConnectRequest deserializeConnectRequestWithoutReadonly(InputArchive inputArchive) throws IOException { final ConnectRequest request = new ConnectRequest(); inputArchive.startRecord("connect"); request.setProtocolVersion(inputArchive.readInt("protocolVersion")); request.setLastZxidSeen(inputArchive.readLong("lastZxidSeen")); request.setTimeOut(inputArchive.readInt("timeOut")); request.setSessionId(inputArchive.readLong("sessionId")); request.setPasswd(inputArchive.readBuffer("passwd")); inputArchive.endRecord("connect"); return request; } /** * Deserializing {@link ConnectResponse} should be specially handled for response from server * version before and including ZooKeeper 3.3 which doesn't understand readOnly field. */ public ConnectResponse deserializeConnectResponse(InputArchive inputArchive) throws IOException { if (isReadonlyAvailable != null) { if (isReadonlyAvailable) { return deserializeConnectResponseWithReadonly(inputArchive); } else { return deserializeConnectResponseWithoutReadonly(inputArchive); } } final ConnectResponse response = deserializeConnectResponseWithoutReadonly(inputArchive); try { response.setReadOnly(inputArchive.readBool("readOnly")); this.isReadonlyAvailable = true; } catch (Exception e) { response.setReadOnly(false); // old version doesn't have readonly concept this.isReadonlyAvailable = false; } return response; } private ConnectResponse deserializeConnectResponseWithReadonly(InputArchive inputArchive) throws IOException { final ConnectResponse response = new ConnectResponse(); response.deserialize(inputArchive, "connect"); return response; } private ConnectResponse deserializeConnectResponseWithoutReadonly(InputArchive inputArchive) throws IOException { final ConnectResponse response = new ConnectResponse(); inputArchive.startRecord("connect"); response.setProtocolVersion(inputArchive.readInt("protocolVersion")); response.setTimeOut(inputArchive.readInt("timeOut")); response.setSessionId(inputArchive.readLong("sessionId")); response.setPasswd(inputArchive.readBuffer("passwd")); inputArchive.endRecord("connect"); return response; } /** * The serialization of {@link ConnectResponse} has to be handled * specially as clients earlier than 3.5 might not expect the * {@code readOnly} flag. * * @param response the response to serialize * @param outputArchive the serialization destination * @see #deserializeConnectRequest(InputArchive) */ public void serializeConnectResponse(final ConnectResponse response, OutputArchive outputArchive) throws IOException { serializeConnectResponse(response, outputArchive, isReadonlyAvailable()); } private static void serializeConnectResponse(final ConnectResponse response, OutputArchive outputArchive, boolean withReadonly) throws IOException { if (withReadonly) { serializeConnectResponseWithReadonly(response, outputArchive); } else { serializeConnectResponseWithoutReadonly(response, outputArchive); } } private static void serializeConnectResponseWithReadonly(ConnectResponse response, OutputArchive outputArchive) throws IOException { response.serialize(outputArchive, "connect"); } private static void serializeConnectResponseWithoutReadonly(ConnectResponse response, OutputArchive outputArchive) throws IOException { outputArchive.startRecord(response, "connect"); outputArchive.writeInt(response.getProtocolVersion(), "protocolVersion"); outputArchive.writeInt(response.getTimeOut(), "timeOut"); outputArchive.writeLong(response.getSessionId(), "sessionId"); outputArchive.writeBuffer(response.getPasswd(), "passwd"); outputArchive.endRecord(response, "connect"); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/jmx/MBeanRegistry.java0100644 0000000 0000000 00000016435 15051152474 033457 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.jmx; import java.lang.management.ManagementFactory; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides a unified interface for registering/unregistering of * zookeeper MBeans with the platform MBean server. It builds a hierarchy of MBeans * where each MBean represented by a filesystem-like path. Eventually, this hierarchy * will be stored in the zookeeper data tree instance as a virtual data tree. */ public class MBeanRegistry { public static final String DOMAIN = "org.apache.ZooKeeperService"; private static final Logger LOG = LoggerFactory.getLogger(MBeanRegistry.class); private static volatile MBeanRegistry instance = new MBeanRegistry(); private final Object LOCK = new Object(); private Map mapBean2Path = new ConcurrentHashMap<>(); private MBeanServer mBeanServer; /** * Useful for unit tests. Change the MBeanRegistry instance * * @param instance new instance */ public static void setInstance(MBeanRegistry instance) { MBeanRegistry.instance = instance; } public static MBeanRegistry getInstance() { return instance; } public MBeanRegistry() { try { mBeanServer = ManagementFactory.getPlatformMBeanServer(); } catch (Error e) { // Account for running within IKVM and create a new MBeanServer // if the PlatformMBeanServer does not exist. mBeanServer = MBeanServerFactory.createMBeanServer(); } } /** * Return the underlying MBeanServer that is being * used to register MBean's. The returned MBeanServer * may be a new empty MBeanServer if running through IKVM. */ public MBeanServer getPlatformMBeanServer() { return mBeanServer; } /** * Registers a new MBean with the platform MBean server. * @param bean the bean being registered * @param parent if not null, the new bean will be registered as a child * node of this parent. */ public void register(ZKMBeanInfo bean, ZKMBeanInfo parent) throws JMException { assert bean != null; String path = null; if (parent != null) { path = mapBean2Path.get(parent); assert path != null; } path = makeFullPath(path, parent); if (bean.isHidden()) { return; } ObjectName oname = makeObjectName(path, bean); try { synchronized (LOCK) { mBeanServer.registerMBean(bean, oname); mapBean2Path.put(bean, path); } } catch (JMException e) { LOG.warn("Failed to register MBean {}", bean.getName()); throw e; } } /** * Unregister the MBean identified by the path. * @param path * @param bean */ private void unregister(String path, ZKMBeanInfo bean) throws JMException { if (path == null) { return; } if (!bean.isHidden()) { final ObjectName objName = makeObjectName(path, bean); LOG.debug("Unregister MBean [{}]", objName); synchronized (LOCK) { mBeanServer.unregisterMBean(objName); } } } /** * @return a {@link Collection} with the {@link ZKMBeanInfo} instances not * unregistered. Mainly for testing purposes. */ public Set getRegisteredBeans() { return new HashSet<>(mapBean2Path.keySet()); } /** * Unregister MBean. * @param bean */ public void unregister(ZKMBeanInfo bean) { if (bean == null) { return; } String path = mapBean2Path.remove(bean); try { unregister(path, bean); } catch (JMException e) { LOG.warn("Error during unregister of [{}]", bean.getName(), e); } catch (Throwable t) { LOG.error("Unexpected exception during unregister of [{}]. It should be reviewed and fixed.", bean.getName(), t); } } /** * Generate a filesystem-like path. * @param prefix path prefix * @param name path elements * @return absolute path */ public String makeFullPath(String prefix, String... name) { StringBuilder sb = new StringBuilder(prefix == null ? "/" : (prefix.equals("/") ? prefix : prefix + "/")); boolean first = true; for (String s : name) { if (s == null) { continue; } if (!first) { sb.append("/"); } else { first = false; } sb.append(s); } return sb.toString(); } protected String makeFullPath(String prefix, ZKMBeanInfo bean) { return makeFullPath(prefix, bean == null ? null : bean.getName()); } /** * This takes a path, such as /a/b/c, and converts it to * name0=a,name1=b,name2=c */ private int tokenize(StringBuilder sb, String path, int index) { String[] tokens = path.split("/"); for (String s : tokens) { if (s.length() == 0) { continue; } sb.append("name").append(index++).append("=").append(s).append(","); } return index; } /** * Builds an MBean path and creates an ObjectName instance using the path. * @param path MBean path * @param bean the MBean instance * @return ObjectName to be registered with the platform MBean server */ protected ObjectName makeObjectName(String path, ZKMBeanInfo bean) throws MalformedObjectNameException { if (path == null) { return null; } StringBuilder beanName = new StringBuilder(DOMAIN + ":"); int counter = 0; counter = tokenize(beanName, path, counter); tokenize(beanName, bean.getName(), counter); beanName.deleteCharAt(beanName.length() - 1); try { return new ObjectName(beanName.toString()); } catch (MalformedObjectNameException e) { LOG.warn("Invalid name \"{}\" for class {}", beanName, bean.getClass()); throw e; } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/jmx/ManagedUtil.java0100644 0000000 0000000 00000012217 15051152474 033130 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.jmx; import java.util.Enumeration; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Shared utilities */ public class ManagedUtil { private static final Logger LOG = LoggerFactory.getLogger(ManagedUtil.class); private static boolean isLog4jJmxEnabled() { boolean enabled = false; if (Boolean.getBoolean("zookeeper.jmx.log4j.disable")) { LOG.info("Log4j 1.2 jmx support is disabled by property."); } else { try { Class.forName("org.apache.log4j.jmx.HierarchyDynamicMBean"); enabled = true; LOG.info("Log4j 1.2 jmx support found and enabled."); } catch (ClassNotFoundException e) { LOG.info("Log4j 1.2 jmx support not found; jmx disabled."); } } return enabled; } /** * Register the log4j JMX mbeans. Set system property * "zookeeper.jmx.log4j.disable" to true to disable registration. * @see Log4J 1.2 API docs * @throws JMException if registration fails */ @SuppressWarnings("rawtypes") public static void registerLog4jMBeans() throws JMException { if (isLog4jJmxEnabled()) { LOG.debug("registerLog4jMBeans()"); MBeanServer mbs = MBeanRegistry.getInstance().getPlatformMBeanServer(); try { // Create and Register the top level Log4J MBean // org.apache.log4j.jmx.HierarchyDynamicMBean hdm = new org.apache.log4j.jmx.HierarchyDynamicMBean(); Object hdm = Class.forName("org.apache.log4j.jmx.HierarchyDynamicMBean").getConstructor().newInstance(); String mbean = System.getProperty("zookeeper.jmx.log4j.mbean", "log4j:hierarchy=default"); ObjectName mbo = new ObjectName(mbean); mbs.registerMBean(hdm, mbo); // Add the root logger to the Hierarchy MBean // org.apache.log4j.Logger rootLogger = // org.apache.log4j.Logger.getRootLogger(); Object rootLogger = Class.forName("org.apache.log4j.Logger") .getMethod("getRootLogger", (Class[]) null) .invoke(null, (Object[]) null); // hdm.addLoggerMBean(rootLogger.getName()); Object rootLoggerName = rootLogger.getClass() .getMethod("getName", (Class[]) null) .invoke(rootLogger, (Object[]) null); hdm.getClass().getMethod("addLoggerMBean", String.class).invoke(hdm, rootLoggerName); // Get each logger from the Log4J Repository and add it to the // Hierarchy MBean created above. // org.apache.log4j.spi.LoggerRepository r = // org.apache.log4j.LogManager.getLoggerRepository(); Object r = Class.forName("org.apache.log4j.LogManager") .getMethod("getLoggerRepository", (Class[]) null) .invoke(null, (Object[]) null); // Enumeration enumer = r.getCurrentLoggers(); Enumeration enumer = (Enumeration) r.getClass() .getMethod("getCurrentLoggers", (Class[]) null) .invoke(r, (Object[]) null); while (enumer.hasMoreElements()) { Object logger = enumer.nextElement(); // hdm.addLoggerMBean(logger.getName()); Object loggerName = logger.getClass() .getMethod("getName", (Class[]) null) .invoke(logger, (Object[]) null); hdm.getClass().getMethod("addLoggerMBean", String.class).invoke(hdm, loggerName); } } catch (Exception e) { LOG.error("Problems while registering log4j 1.2 jmx beans!", e); throw new JMException(e.toString()); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/jmx/ZKMBeanInfo.java0100644 0000000 0000000 00000002446 15051152474 033004 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.jmx; /** * Zookeeper MBean info interface. MBeanRegistry uses the interface to generate * JMX object name. */ public interface ZKMBeanInfo { /** * @return a string identifying the MBean */ String getName(); /** * If isHidden returns true, the MBean won't be registered with MBean server, * and thus won't be available for management tools. Used for grouping MBeans. * @return true if the MBean is hidden. */ boolean isHidden(); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/Counter.java0100644 0000000 0000000 00000003217 15051152474 033225 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * A counter refers to a value which can only increase. * Usually the value is reset when the process starts. */ public interface Counter { /** * Increment the value by one. *

This method is thread safe, The MetricsProvider will take care of synchronization.

*/ default void inc() { add(1); } /** * Increment the value by a given amount. *

This method is thread safe, The MetricsProvider will take care of synchronization.

* * @param delta amount to increment, this cannot be a negative number. */ void add(long delta); /** * Get the current value held by the counter. *

This method is thread safe, The MetricsProvider will take care of synchronization.

* * @return the current value */ long get(); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/CounterSet.java0100644 0000000 0000000 00000003257 15051152474 033705 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * A counter refers to a value which can only increase. * Usually the value is reset when the process starts. * * A CounterSet is a set of {@link Counter} grouped by keys. */ public interface CounterSet { /** * Increment the value by one for the given key *

This method is thread safe, The MetricsProvider will take care of synchronization.

* * @param key the key to increment the count */ default void inc(String key) { add(key, 1L); } /** * Increment the value by a given amount for the given key *

This method is thread safe, The MetricsProvider will take care of synchronization.

* * @param key the key to increment the count for the given key * @param delta amount to increment, this cannot be a negative number. */ void add(String key, long delta); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/Gauge.java0100644 0000000 0000000 00000002447 15051152474 032642 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * A Gauge is an application provided object which will be called by the framework in order to sample the value * of an integer value. */ public interface Gauge { /** * Returns the current value associated with this gauge. * The MetricsProvider will call this callback without taking care of synchronization, it is up to the application * to handle thread safety. * * @return the current value for the gauge */ Number get(); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/GaugeSet.java0100644 0000000 0000000 00000002567 15051152474 033321 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; import java.util.Map; /** * A Gauge is an application provided object which will be called by the metrics framework to sample a numeric value. * * A GaugeSet is a set of {@link Gauge} grouped by keys. */ public interface GaugeSet { /** * Returns all values and the associated keys of the GaugeSet. * The MetricsProvider will call this callback without taking care of synchronization, it is up to the application * to handle thread safety. * * @return all the values and keys */ Map values(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_metrics_M0100644 0000000 0000000 00000000160 15051152474 032623 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/MetricsContext.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/MetricsContext.ja0100644 0000000 0000000 00000010022 15051152474 034222 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * A MetricsContext is like a namespace for metrics. Each component/submodule * will have its own MetricsContext. *

* In some cases it is possible to have a separate MetricsContext for each * instance of a component, for instance on the server side a possible usecase * it to gather metrics for every other peer. *

*

* Contexts are organized in a hierarchy. *

* */ public interface MetricsContext { /** * Returns a sub context. * * @param name the name of the subcontext * * @return a new metrics context. */ MetricsContext getContext(String name); /** * Returns a counter. * * @param name * @return the counter identified by name in this context. */ Counter getCounter(String name); /** * Returns the CounterSet identified by the given name * Null name is not allowed * * @param name * @return CounterSet identified by the name in this context. */ CounterSet getCounterSet(String name); /** * Registers an user provided {@link Gauge} which will be called by the * MetricsProvider in order to sample an integer value. * If another Gauge was already registered the new one will * take its place. * Registering a null callback is not allowed. * * @param name unique name of the Gauge in this context * @param gauge the implementation of the Gauge * */ void registerGauge(String name, Gauge gauge); /** * Unregisters the user provided {@link Gauge} bound to the given name. * * @param name unique name of the Gauge in this context * */ void unregisterGauge(String name); /** * Registers a user provided {@link GaugeSet} which will be called by the * MetricsProvider in order to sample number values. * If another GaugeSet was already registered, the new one will take its place. * Registering with a null name or null callback is not allowed. * * @param name unique name of the GaugeSet in this context * @param gaugeSet the implementation of the GaugeSet * */ void registerGaugeSet(String name, GaugeSet gaugeSet); /** * Unregisters the user provided {@link GaugeSet} bound to the given name. * * Unregistering with a null name is not allowed. * @param name unique name of the GaugeSet in this context * */ void unregisterGaugeSet(String name); enum DetailLevel { /** * The returned Summary is expected to track only simple aggregated * values, like min/max/avg */ BASIC, /** * It is expected that the returned Summary performs expensive * aggregations, like percentiles. */ ADVANCED } /** * Returns a summary. * * @param name * @param detailLevel * @return the summary identified by name in this context. */ Summary getSummary(String name, DetailLevel detailLevel); /** * Returns a set of summaries. * * @param name * @param detailLevel * @return the summary identified by name in this context. */ SummarySet getSummarySet(String name, DetailLevel detailLevel); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_metrics_M0100644 0000000 0000000 00000000161 15051152474 032624 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/MetricsProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/MetricsProvider.j0100644 0000000 0000000 00000005261 15051152474 034240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; import java.util.Properties; import java.util.function.BiConsumer; /** * A MetricsProvider is a system which collects Metrics and publishes current values to external facilities. * * The system will create an instance of the configured class using the default constructor, which must be public.
* After the instantiation of the provider, the system will call {@link #configure(java.util.Properties) } in order to provide configuration, * and then when the system is ready to work it will call {@link #start() }. *
* Providers can be used both on ZooKeeper servers and on ZooKeeper clients. */ public interface MetricsProvider { /** * Configure the provider. * * @param configuration the configuration. * * @throws MetricsProviderLifeCycleException in case of invalid configuration. */ void configure(Properties configuration) throws MetricsProviderLifeCycleException; /** * Start the provider. * For instance such method will start a network endpoint. * * @throws MetricsProviderLifeCycleException in case of failure */ void start() throws MetricsProviderLifeCycleException; /** * Provides access to the root context. * * @return the root context */ MetricsContext getRootContext(); /** * Releases resources held by the provider.
* This method must not throw exceptions.
* This method can be called more than once. */ void stop(); /** * Dumps all metrics as a key-value pair. * This method will be used in legacy monitor command. * @param sink the receiver of all of the current values. */ void dump(BiConsumer sink); /** * Reset all values. * This method is optional and can be noop, depending * on the underlying implementation. */ void resetAllValues(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_metrics_M0100644 0000000 0000000 00000000203 15051152474 032621 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/MetricsProviderLifeCycleException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/MetricsProviderLi0100644 0000000 0000000 00000003010 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * A generic exception thrown during the licecycle of a MetricsProvider. *

These exception will prevent the system from booting.

*

Normally these exception will be ignored during shutdown.

*/ public class MetricsProviderLifeCycleException extends Exception { private static final long serialVersionUID = 1L; public MetricsProviderLifeCycleException() { } public MetricsProviderLifeCycleException(String message) { super(message); } public MetricsProviderLifeCycleException(String message, Throwable cause) { super(message, cause); } public MetricsProviderLifeCycleException(Throwable cause) { super(cause); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/Summary.java0100644 0000000 0000000 00000002350 15051152474 033240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * Summaries track the size and number of events. * They are able to publish minumum, maximum, average values, depending on the capabilities of the MetricsProvider. */ public interface Summary { /** * Register a value. *

This method is thread safe, The MetricsProvider will take care of synchronization.

* * @param value current value */ void add(long value); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/SummarySet.java0100644 0000000 0000000 00000002546 15051152474 033723 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; /** * Summaries track the size and number of events. * They are able to publish minumum, maximum, average values, depending on the capabilities of the MetricsProvider. * A SummarySet is a set of {@link Summary}. */ public interface SummarySet { /** * Register a value. *

This method is thread safe, The MetricsProvider will take care of synchronization.

* * @param key the key to access the Summary for the given key * @param value current value */ void add(String key, long value); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_metrics_i0100644 0000000 0000000 00000000175 15051152474 032665 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/DefaultMetricsProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/DefaultMetri0100644 0000000 0000000 00000021537 15051152474 034221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.impl; import java.util.Objects; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.BiConsumer; import org.apache.zookeeper.metrics.Counter; import org.apache.zookeeper.metrics.CounterSet; import org.apache.zookeeper.metrics.Gauge; import org.apache.zookeeper.metrics.GaugeSet; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; import org.apache.zookeeper.server.metric.AvgMinMaxCounter; import org.apache.zookeeper.server.metric.AvgMinMaxCounterSet; import org.apache.zookeeper.server.metric.AvgMinMaxPercentileCounter; import org.apache.zookeeper.server.metric.AvgMinMaxPercentileCounterSet; import org.apache.zookeeper.server.metric.SimpleCounter; import org.apache.zookeeper.server.metric.SimpleCounterSet; /** * Default implementation of {@link MetricsProvider}.
* It does not implement a real hierarchy of contexts, but metrics are flattened * in a single namespace.
* It is mostly useful to make the legacy 4 letter words interface work as * expected. */ public class DefaultMetricsProvider implements MetricsProvider { private final DefaultMetricsContext rootMetricsContext = new DefaultMetricsContext(); @Override public void configure(Properties configuration) throws MetricsProviderLifeCycleException { } @Override public void start() throws MetricsProviderLifeCycleException { } @Override public MetricsContext getRootContext() { return rootMetricsContext; } @Override public void stop() { // release all references to external objects rootMetricsContext.gauges.clear(); rootMetricsContext.gaugeSets.clear(); } @Override public void dump(BiConsumer sink) { rootMetricsContext.dump(sink); } @Override public void resetAllValues() { rootMetricsContext.reset(); } private static final class DefaultMetricsContext implements MetricsContext { private final ConcurrentMap gauges = new ConcurrentHashMap<>(); private final ConcurrentMap gaugeSets = new ConcurrentHashMap<>(); private final ConcurrentMap counters = new ConcurrentHashMap<>(); private final ConcurrentMap counterSets = new ConcurrentHashMap<>(); private final ConcurrentMap basicSummaries = new ConcurrentHashMap<>(); private final ConcurrentMap summaries = new ConcurrentHashMap<>(); private final ConcurrentMap basicSummarySets = new ConcurrentHashMap<>(); private final ConcurrentMap summarySets = new ConcurrentHashMap<>(); @Override public MetricsContext getContext(String name) { // no hierarchy yet return this; } @Override public Counter getCounter(String name) { return counters.computeIfAbsent(name, (n) -> { return new SimpleCounter(n); }); } @Override public CounterSet getCounterSet(final String name) { Objects.requireNonNull(name, "Cannot register a CounterSet with null name"); return counterSets.computeIfAbsent(name, SimpleCounterSet::new); } @Override public void registerGauge(String name, Gauge gauge) { Objects.requireNonNull(gauge, "Cannot register a null Gauge for " + name); gauges.put(name, gauge); } @Override public void unregisterGauge(String name) { gauges.remove(name); } @Override public void registerGaugeSet(final String name, final GaugeSet gaugeSet) { Objects.requireNonNull(name, "Cannot register a GaugeSet with null name"); Objects.requireNonNull(gaugeSet, "Cannot register a null GaugeSet for " + name); gaugeSets.put(name, gaugeSet); } @Override public void unregisterGaugeSet(final String name) { Objects.requireNonNull(name, "Cannot unregister GaugeSet with null name"); gaugeSets.remove(name); } @Override public Summary getSummary(String name, DetailLevel detailLevel) { if (detailLevel == DetailLevel.BASIC) { return basicSummaries.computeIfAbsent(name, (n) -> { if (summaries.containsKey(n)) { throw new IllegalArgumentException("Already registered a non basic summary as " + n); } return new AvgMinMaxCounter(name); }); } else { return summaries.computeIfAbsent(name, (n) -> { if (basicSummaries.containsKey(n)) { throw new IllegalArgumentException("Already registered a basic summary as " + n); } return new AvgMinMaxPercentileCounter(name); }); } } @Override public SummarySet getSummarySet(String name, DetailLevel detailLevel) { if (detailLevel == DetailLevel.BASIC) { return basicSummarySets.computeIfAbsent(name, (n) -> { if (summarySets.containsKey(n)) { throw new IllegalArgumentException("Already registered a non basic summary set as " + n); } return new AvgMinMaxCounterSet(name); }); } else { return summarySets.computeIfAbsent(name, (n) -> { if (basicSummarySets.containsKey(n)) { throw new IllegalArgumentException("Already registered a basic summary set as " + n); } return new AvgMinMaxPercentileCounterSet(name); }); } } void dump(BiConsumer sink) { gauges.forEach((name, metric) -> { Number value = metric.get(); if (value != null) { sink.accept(name, value); } }); gaugeSets.forEach((name, gaugeSet) -> gaugeSet.values().forEach((key, value) -> { if (key != null) { sink.accept(key + "_" + name, value != null ? value : 0); } }) ); counters.values().forEach(metric -> { metric.values().forEach(sink); }); counterSets.values().forEach(metric -> { metric.values().forEach(sink); }); basicSummaries.values().forEach(metric -> { metric.values().forEach(sink); }); summaries.values().forEach(metric -> { metric.values().forEach(sink); }); basicSummarySets.values().forEach(metric -> { metric.values().forEach(sink); }); summarySets.values().forEach(metric -> { metric.values().forEach(sink); }); } void reset() { counters.values().forEach(metric -> { metric.reset(); }); counterSets.values().forEach(metric -> { metric.reset(); }); basicSummaries.values().forEach(metric -> { metric.reset(); }); summaries.values().forEach(metric -> { metric.reset(); }); basicSummarySets.values().forEach(metric -> { metric.reset(); }); summarySets.values().forEach(metric -> { metric.reset(); }); // no need to reset gauges } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_metrics_i0100644 0000000 0000000 00000000177 15051152474 032667 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/MetricsProviderBootstrap.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/MetricsProvi0100644 0000000 0000000 00000004725 15051152474 034262 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.impl; import java.lang.reflect.InvocationTargetException; import java.util.Properties; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility for bootstrap process of MetricsProviders */ public abstract class MetricsProviderBootstrap { private static final Logger LOG = LoggerFactory.getLogger(MetricsProviderBootstrap.class); public static MetricsProvider startMetricsProvider( String metricsProviderClassName, Properties configuration) throws MetricsProviderLifeCycleException { try { Class clazz = Class.forName( metricsProviderClassName, true, Thread.currentThread().getContextClassLoader()); MetricsProvider metricsProvider = (MetricsProvider) clazz.getConstructor().newInstance(); metricsProvider.configure(configuration); metricsProvider.start(); return metricsProvider; } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException error) { LOG.error("Cannot boot MetricsProvider {}", metricsProviderClassName, error); throw new MetricsProviderLifeCycleException("Cannot boot MetricsProvider " + metricsProviderClassName, error); } catch (MetricsProviderLifeCycleException error) { LOG.error("Cannot boot MetricsProvider {}", metricsProviderClassName, error); throw error; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_metrics_i0100644 0000000 0000000 00000000172 15051152474 032662 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/NullMetricsProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/metrics/impl/NullMetricsP0100644 0000000 0000000 00000010353 15051152474 034207 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics.impl; import java.util.Properties; import java.util.function.BiConsumer; import org.apache.zookeeper.metrics.Counter; import org.apache.zookeeper.metrics.CounterSet; import org.apache.zookeeper.metrics.Gauge; import org.apache.zookeeper.metrics.GaugeSet; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; /** * This is a dummy MetricsProvider which does nothing. */ public class NullMetricsProvider implements MetricsProvider { /** * Instance of NullMetricsProvider useful for tests. */ public static final MetricsProvider INSTANCE = new NullMetricsProvider(); @Override public void configure(Properties configuration) throws MetricsProviderLifeCycleException { } @Override public void start() throws MetricsProviderLifeCycleException { } @Override public MetricsContext getRootContext() { return NullMetricsContext.INSTANCE; } @Override public void dump(BiConsumer sink) { } @Override public void resetAllValues() { } @Override public void stop() { } public static final class NullMetricsContext implements MetricsContext { public static final NullMetricsContext INSTANCE = new NullMetricsContext(); @Override public MetricsContext getContext(String name) { return INSTANCE; } @Override public Counter getCounter(String name) { return NullCounter.INSTANCE; } @Override public CounterSet getCounterSet(final String name) { return NullCounterSet.INSTANCE; } @Override public void registerGauge(String name, Gauge gauge) { } @Override public void unregisterGauge(String name) { } @Override public void registerGaugeSet(final String name, final GaugeSet gaugeSet) { } @Override public void unregisterGaugeSet(final String name) { } @Override public Summary getSummary(String name, DetailLevel detailLevel) { return NullSummary.INSTANCE; } @Override public SummarySet getSummarySet(String name, DetailLevel detailLevel) { return NullSummarySet.INSTANCE; } } private static final class NullCounter implements Counter { private static final NullCounter INSTANCE = new NullCounter(); @Override public void add(long delta) { } @Override public long get() { return 0; } } private static final class NullCounterSet implements CounterSet { private static final NullCounterSet INSTANCE = new NullCounterSet(); @Override public void add(final String key, final long delta) { } } private static final class NullSummary implements Summary { private static final NullSummary INSTANCE = new NullSummary(); @Override public void add(long value) { } } private static final class NullSummarySet implements SummarySet { private static final NullSummarySet INSTANCE = new NullSummarySet(); @Override public void add(String key, long value) { } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Au0100644 0000000 0000000 00000000165 15051152474 032641 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/AuthenticationHelper.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/AuthenticationHelp0100644 0000000 0000000 00000012744 15051152474 034323 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Contains helper methods to enforce authentication */ public class AuthenticationHelper { private static final Logger LOG = LoggerFactory.getLogger(AuthenticationHelper.class); public static final String ENFORCE_AUTH_ENABLED = "zookeeper.enforce.auth.enabled"; public static final String ENFORCE_AUTH_SCHEMES = "zookeeper.enforce.auth.schemes"; public static final String SESSION_REQUIRE_CLIENT_SASL_AUTH = "zookeeper.sessionRequireClientSASLAuth"; public static final String SASL_AUTH_SCHEME = "sasl"; private boolean enforceAuthEnabled; private List enforceAuthSchemes = new ArrayList<>(); private boolean saslAuthRequired; public AuthenticationHelper() { initConfigurations(); } private void initConfigurations() { if (Boolean.parseBoolean(System.getProperty(SESSION_REQUIRE_CLIENT_SASL_AUTH, "false"))) { enforceAuthEnabled = true; enforceAuthSchemes.add(SASL_AUTH_SCHEME); } else { enforceAuthEnabled = Boolean.parseBoolean(System.getProperty(ENFORCE_AUTH_ENABLED, "false")); String enforceAuthSchemesProp = System.getProperty(ENFORCE_AUTH_SCHEMES); if (enforceAuthSchemesProp != null) { Arrays.stream(enforceAuthSchemesProp.split(",")).forEach(s -> { enforceAuthSchemes.add(s.trim()); }); } } LOG.info("{} = {}", ENFORCE_AUTH_ENABLED, enforceAuthEnabled); LOG.info("{} = {}", ENFORCE_AUTH_SCHEMES, enforceAuthSchemes); validateConfiguredProperties(); saslAuthRequired = enforceAuthEnabled && enforceAuthSchemes.contains(SASL_AUTH_SCHEME); } private void validateConfiguredProperties() { if (enforceAuthEnabled) { if (enforceAuthSchemes.isEmpty()) { String msg = ENFORCE_AUTH_ENABLED + " is true " + ENFORCE_AUTH_SCHEMES + " must be " + "configured."; LOG.error(msg); throw new IllegalArgumentException(msg); } enforceAuthSchemes.forEach(scheme -> { if (ProviderRegistry.getProvider(scheme) == null) { String msg = "Authentication scheme " + scheme + " is not available."; LOG.error(msg); throw new IllegalArgumentException(msg); } }); } } /** * Checks if connection is authenticated or not. * * @param cnxn server connection * @return boolean */ private boolean isCnxnAuthenticated(ServerCnxn cnxn) { for (Id id : cnxn.getAuthInfo()) { if (enforceAuthSchemes.contains(id.getScheme())) { return true; } } return false; } public boolean isEnforceAuthEnabled() { return enforceAuthEnabled; } /** * Returns true when authentication enforcement was success otherwise returns false * also closes the connection * * @param connection server connection * @param xid current operation xid * @return true when authentication enforcement is success otherwise false */ public boolean enforceAuthentication(ServerCnxn connection, int xid) throws IOException { if (isEnforceAuthEnabled() && !isCnxnAuthenticated(connection)) { //Un authenticated connection, lets inform user with response and then close the session LOG.error("Client authentication scheme(s) {} does not match with any of the expected " + "authentication scheme {}, closing session.", getAuthSchemes(connection), enforceAuthSchemes); ReplyHeader replyHeader = new ReplyHeader(xid, 0, KeeperException.Code.SESSIONCLOSEDREQUIRESASLAUTH.intValue()); connection.sendResponse(replyHeader, null, "response"); connection.sendCloseSession(); connection.disableRecv(); return false; } return true; } private List getAuthSchemes(ServerCnxn connection) { return connection.getAuthInfo().stream().map(Id::getScheme).collect(Collectors.toList()); } public boolean isSaslAuthRequired() { return saslAuthRequired; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/BlueThrottle.java0100644 0000000 0000000 00000034441 15051152474 034066 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Random; import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implements a token-bucket based rate limiting mechanism with optional * probabilistic dropping inspired by the BLUE queue management algorithm [1]. * * The throttle provides the {@link #checkLimit(int)} method which provides * a binary yes/no decision. * * The core token bucket algorithm starts with an initial set of tokens based * on the maxTokens setting. Tokens are dispensed each * {@link #checkLimit(int)} call, which fails if there are not enough tokens to * satisfy a given request. * * The token bucket refills over time, providing fillCount tokens * every fillTime milliseconds, capping at maxTokens. * * This design allows the throttle to allow short bursts to pass, while still * capping the total number of requests per time interval. * * One issue with a pure token bucket approach for something like request or * connection throttling is that the wall clock arrival time of requests affects * the probability of a request being allowed to pass or not. Under constant * load this can lead to request starvation for requests that constantly arrive * later than the majority. * * In an attempt to combat this, this throttle can also provide probabilistic * dropping. This is enabled anytime freezeTime is set to a value * other than -1. * * The probabilistic algorithm starts with an initial drop probability of 0, and * adjusts this probability roughly every freezeTime milliseconds. * The first request after freezeTime, the algorithm checks the * token bucket. If the token bucket is empty, the drop probability is increased * by dropIncrease up to a maximum of 1. Otherwise, if * the bucket has a token deficit less than decreasePoint * maxTokens, * the probability is decreased by dropDecrease. * * Given a call to {@link #checkLimit(int)}, requests are first dropped randomly * based on the current drop probability, and only surviving requests are then * checked against the token bucket. * * When under constant load, the probabilistic algorithm will adapt to a drop * frequency that should keep requests within the token limit. When load drops, * the drop probability will decrease, eventually returning to zero if possible. * * [1] "BLUE: A New Class of Active Queue Management Algorithms" **/ public class BlueThrottle { private static final Logger LOG = LoggerFactory.getLogger(BlueThrottle.class); private int maxTokens; private int fillTime; private int fillCount; private int tokens; private long lastTime; private int freezeTime; private long lastFreeze; private double dropIncrease; private double dropDecrease; private double decreasePoint; private double drop; Random rng; public static final String CONNECTION_THROTTLE_TOKENS = "zookeeper.connection_throttle_tokens"; private static final int DEFAULT_CONNECTION_THROTTLE_TOKENS; public static final String CONNECTION_THROTTLE_FILL_TIME = "zookeeper.connection_throttle_fill_time"; private static final int DEFAULT_CONNECTION_THROTTLE_FILL_TIME; public static final String CONNECTION_THROTTLE_FILL_COUNT = "zookeeper.connection_throttle_fill_count"; private static final int DEFAULT_CONNECTION_THROTTLE_FILL_COUNT; public static final String CONNECTION_THROTTLE_FREEZE_TIME = "zookeeper.connection_throttle_freeze_time"; private static final int DEFAULT_CONNECTION_THROTTLE_FREEZE_TIME; public static final String CONNECTION_THROTTLE_DROP_INCREASE = "zookeeper.connection_throttle_drop_increase"; private static final double DEFAULT_CONNECTION_THROTTLE_DROP_INCREASE; public static final String CONNECTION_THROTTLE_DROP_DECREASE = "zookeeper.connection_throttle_drop_decrease"; private static final double DEFAULT_CONNECTION_THROTTLE_DROP_DECREASE; public static final String CONNECTION_THROTTLE_DECREASE_RATIO = "zookeeper.connection_throttle_decrease_ratio"; private static final double DEFAULT_CONNECTION_THROTTLE_DECREASE_RATIO; public static final String WEIGHED_CONNECTION_THROTTLE = "zookeeper.connection_throttle_weight_enabled"; private static boolean connectionWeightEnabled; public static final String GLOBAL_SESSION_WEIGHT = "zookeeper.connection_throttle_global_session_weight"; private static final int DEFAULT_GLOBAL_SESSION_WEIGHT; public static final String LOCAL_SESSION_WEIGHT = "zookeeper.connection_throttle_local_session_weight"; private static final int DEFAULT_LOCAL_SESSION_WEIGHT; public static final String RENEW_SESSION_WEIGHT = "zookeeper.connection_throttle_renew_session_weight"; private static final int DEFAULT_RENEW_SESSION_WEIGHT; // for unit tests only protected static void setConnectionWeightEnabled(boolean enabled) { connectionWeightEnabled = enabled; logWeighedThrottlingSetting(); } private static void logWeighedThrottlingSetting() { if (connectionWeightEnabled) { LOG.info("Weighed connection throttling is enabled. " + "But it will only be effective if connection throttling is enabled"); LOG.info( "The weights for different session types are: global {} renew {} local {}", DEFAULT_GLOBAL_SESSION_WEIGHT, DEFAULT_RENEW_SESSION_WEIGHT, DEFAULT_LOCAL_SESSION_WEIGHT ); } else { LOG.info("Weighed connection throttling is disabled"); } } static { int tokens = Integer.getInteger(CONNECTION_THROTTLE_TOKENS, 0); int fillCount = Integer.getInteger(CONNECTION_THROTTLE_FILL_COUNT, 1); connectionWeightEnabled = Boolean.getBoolean(WEIGHED_CONNECTION_THROTTLE); // if not specified, the weights for a global session, a local session, and a renew session // are 3, 1, 2 respectively. The weight for a global session is 3 because in our connection benchmarking, // the throughput of global sessions is about one third of that of local sessions. Renewing a session // requires is more expensive than establishing a local session and cheaper than creating a global session so // its default weight is set to 2. int globalWeight = Integer.getInteger(GLOBAL_SESSION_WEIGHT, 3); int localWeight = Integer.getInteger(LOCAL_SESSION_WEIGHT, 1); int renewWeight = Integer.getInteger(RENEW_SESSION_WEIGHT, 2); if (globalWeight <= 0) { LOG.warn("Invalid global session weight {}. It should be larger than 0", globalWeight); DEFAULT_GLOBAL_SESSION_WEIGHT = 3; } else if (globalWeight < localWeight) { LOG.warn( "The global session weight {} is less than the local session weight {}. Use the local session weight.", globalWeight, localWeight); DEFAULT_GLOBAL_SESSION_WEIGHT = localWeight; } else { DEFAULT_GLOBAL_SESSION_WEIGHT = globalWeight; } if (localWeight <= 0) { LOG.warn("Invalid local session weight {}. It should be larger than 0", localWeight); DEFAULT_LOCAL_SESSION_WEIGHT = 1; } else { DEFAULT_LOCAL_SESSION_WEIGHT = localWeight; } if (renewWeight <= 0) { LOG.warn("Invalid renew session weight {}. It should be larger than 0", renewWeight); DEFAULT_RENEW_SESSION_WEIGHT = 2; } else if (renewWeight < localWeight) { LOG.warn( "The renew session weight {} is less than the local session weight {}. Use the local session weight.", renewWeight, localWeight); DEFAULT_RENEW_SESSION_WEIGHT = localWeight; } else { DEFAULT_RENEW_SESSION_WEIGHT = renewWeight; } // This is based on the assumption that tokens set in config are for global sessions DEFAULT_CONNECTION_THROTTLE_TOKENS = connectionWeightEnabled ? DEFAULT_GLOBAL_SESSION_WEIGHT * tokens : tokens; DEFAULT_CONNECTION_THROTTLE_FILL_TIME = Integer.getInteger(CONNECTION_THROTTLE_FILL_TIME, 1); DEFAULT_CONNECTION_THROTTLE_FILL_COUNT = connectionWeightEnabled ? DEFAULT_GLOBAL_SESSION_WEIGHT * fillCount : fillCount; DEFAULT_CONNECTION_THROTTLE_FREEZE_TIME = Integer.getInteger(CONNECTION_THROTTLE_FREEZE_TIME, -1); DEFAULT_CONNECTION_THROTTLE_DROP_INCREASE = getDoubleProp(CONNECTION_THROTTLE_DROP_INCREASE, 0.02); DEFAULT_CONNECTION_THROTTLE_DROP_DECREASE = getDoubleProp(CONNECTION_THROTTLE_DROP_DECREASE, 0.002); DEFAULT_CONNECTION_THROTTLE_DECREASE_RATIO = getDoubleProp(CONNECTION_THROTTLE_DECREASE_RATIO, 0); logWeighedThrottlingSetting(); } /* Varation of Integer.getInteger for real number properties */ private static double getDoubleProp(String name, double def) { String val = System.getProperty(name); if (val != null) { return Double.parseDouble(val); } else { return def; } } public BlueThrottle() { // Disable throttling by default (maxTokens = 0) this.maxTokens = DEFAULT_CONNECTION_THROTTLE_TOKENS; this.fillTime = DEFAULT_CONNECTION_THROTTLE_FILL_TIME; this.fillCount = DEFAULT_CONNECTION_THROTTLE_FILL_COUNT; this.tokens = maxTokens; this.lastTime = Time.currentElapsedTime(); // Disable BLUE throttling by default (freezeTime = -1) this.freezeTime = DEFAULT_CONNECTION_THROTTLE_FREEZE_TIME; this.lastFreeze = Time.currentElapsedTime(); this.dropIncrease = DEFAULT_CONNECTION_THROTTLE_DROP_INCREASE; this.dropDecrease = DEFAULT_CONNECTION_THROTTLE_DROP_DECREASE; this.decreasePoint = DEFAULT_CONNECTION_THROTTLE_DECREASE_RATIO; this.drop = 0; this.rng = new Random(); } public synchronized void setMaxTokens(int max) { int deficit = maxTokens - tokens; maxTokens = max; tokens = max - deficit; } public synchronized void setFillTime(int time) { fillTime = time; } public synchronized void setFillCount(int count) { fillCount = count; } public synchronized void setFreezeTime(int time) { freezeTime = time; } public synchronized void setDropIncrease(double increase) { dropIncrease = increase; } public synchronized void setDropDecrease(double decrease) { dropDecrease = decrease; } public synchronized void setDecreasePoint(double ratio) { decreasePoint = ratio; } public synchronized int getMaxTokens() { return maxTokens; } public synchronized int getFillTime() { return fillTime; } public synchronized int getFillCount() { return fillCount; } public synchronized int getFreezeTime() { return freezeTime; } public synchronized double getDropIncrease() { return dropIncrease; } public synchronized double getDropDecrease() { return dropDecrease; } public synchronized double getDecreasePoint() { return decreasePoint; } public synchronized double getDropChance() { return drop; } public synchronized int getDeficit() { return maxTokens - tokens; } public int getRequiredTokensForGlobal() { return BlueThrottle.DEFAULT_GLOBAL_SESSION_WEIGHT; } public int getRequiredTokensForLocal() { return BlueThrottle.DEFAULT_LOCAL_SESSION_WEIGHT; } public int getRequiredTokensForRenew() { return BlueThrottle.DEFAULT_RENEW_SESSION_WEIGHT; } public boolean isConnectionWeightEnabled() { return BlueThrottle.connectionWeightEnabled; } public synchronized boolean checkLimit(int need) { // A maxTokens setting of zero disables throttling if (maxTokens == 0) { return true; } long now = Time.currentElapsedTime(); long diff = now - lastTime; if (diff > fillTime) { long refill = diff * fillCount / fillTime; tokens = (int) Math.min(tokens + refill, maxTokens); if (tokens < 0) { tokens = maxTokens; LOG.error("Throttle config values {}({}) and {}({}) are insane and cause long integer overflow after {}ms", CONNECTION_THROTTLE_FILL_TIME, fillTime, CONNECTION_THROTTLE_FILL_COUNT, fillCount, diff); } lastTime = now; } // A freeze time of -1 disables BLUE randomized throttling if (freezeTime != -1) { if (!checkBlue(now)) { return false; } } if (tokens < need) { return false; } tokens -= need; return true; } public synchronized boolean checkBlue(long now) { int length = maxTokens - tokens; int limit = maxTokens; long diff = now - lastFreeze; long threshold = Math.round(maxTokens * decreasePoint); if (diff > freezeTime) { if ((length == limit) && (drop < 1)) { drop = Math.min(drop + dropIncrease, 1); } else if ((length <= threshold) && (drop > 0)) { drop = Math.max(drop - dropDecrease, 0); } lastFreeze = now; } return !(rng.nextDouble() < drop); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_By0100644 0000000 0000000 00000000166 15051152474 032647 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ByteBufferInputStream.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ByteBufferInputStr0100644 0000000 0000000 00000004476 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import javax.annotation.Nonnull; import org.apache.jute.BinaryInputArchive; import org.apache.jute.Record; public class ByteBufferInputStream extends InputStream { private final ByteBuffer bb; public ByteBufferInputStream(ByteBuffer bb) { this.bb = bb; } @Override public int read() throws IOException { if (bb.remaining() == 0) { return -1; } return bb.get() & 0xff; } @Override public int available() throws IOException { return bb.remaining(); } @Override public int read(@Nonnull byte[] b, int off, int len) throws IOException { if (bb.remaining() == 0) { return -1; } if (len > bb.remaining()) { len = bb.remaining(); } bb.get(b, off, len); return len; } @Override public int read(@Nonnull byte[] b) throws IOException { return read(b, 0, b.length); } @Override public long skip(long n) throws IOException { if (n < 0L) { return 0; } n = Math.min(n, bb.remaining()); bb.position(bb.position() + (int) n); return n; } public static void byteBuffer2Record(ByteBuffer bb, Record record) throws IOException { BinaryInputArchive ia; ia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb)); record.deserialize(ia, "request"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_By0100644 0000000 0000000 00000000167 15051152474 032650 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ByteBufferOutputStream.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ByteBufferOutputSt0100644 0000000 0000000 00000003401 15051152474 034306 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import javax.annotation.Nonnull; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; public class ByteBufferOutputStream extends OutputStream { private final ByteBuffer bb; public ByteBufferOutputStream(ByteBuffer bb) { this.bb = bb; } @Override public void write(int b) throws IOException { bb.put((byte) b); } @Override public void write(@Nonnull byte[] b) throws IOException { bb.put(b); } @Override public void write(@Nonnull byte[] b, int off, int len) throws IOException { bb.put(b, off, len); } public static void record2ByteBuffer(Record record, ByteBuffer bb) throws IOException { BinaryOutputArchive oa; oa = BinaryOutputArchive.getArchive(new ByteBufferOutputStream(bb)); record.serialize(oa, "request"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_By0100644 0000000 0000000 00000000170 15051152474 032642 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ByteBufferRequestRecord.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ByteBufferRequestR0100644 0000000 0000000 00000003557 15051152474 034265 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.nio.ByteBuffer; import java.util.function.Supplier; import org.apache.jute.Record; public class ByteBufferRequestRecord implements RequestRecord { private final ByteBuffer request; private volatile Record record; public ByteBufferRequestRecord(ByteBuffer request) { this.request = request; } @SuppressWarnings("unchecked") @Override public T readRecord(Supplier constructor) throws IOException { if (record != null) { return (T) record; } record = constructor.get(); request.rewind(); ByteBufferInputStream.byteBuffer2Record(request, record); request.rewind(); return (T) record; } @Override public byte[] readBytes() { request.rewind(); int len = request.remaining(); byte[] b = new byte[len]; request.get(b); request.rewind(); return b; } @Override public int limit() { return request.limit(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Cl0100644 0000000 0000000 00000000171 15051152474 032627 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ClientCnxnLimitException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ClientCnxnLimitExc0100644 0000000 0000000 00000002220 15051152474 034223 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * Indicates that the number of client connections has exceeded some limit. */ public class ClientCnxnLimitException extends Exception { private static final long serialVersionUID = -8655587505476768446L; public ClientCnxnLimitException() { super("Connection throttle rejected connection"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Co0100644 0000000 0000000 00000000157 15051152474 032636 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ConnectionBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ConnectionBean.jav0100644 0000000 0000000 00000011274 15051152474 034174 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Arrays; import javax.management.ObjectName; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of connection MBean interface. */ public class ConnectionBean implements ConnectionMXBean, ZKMBeanInfo { private static final Logger LOG = LoggerFactory.getLogger(ConnectionBean.class); private final ServerCnxn connection; private final Stats stats; private final ZooKeeperServer zk; private final String remoteIP; private final long sessionId; public ConnectionBean(ServerCnxn connection, ZooKeeperServer zk) { this.connection = connection; this.stats = connection; this.zk = zk; InetSocketAddress sockAddr = connection.getRemoteSocketAddress(); if (sockAddr == null) { remoteIP = "Unknown"; } else { InetAddress addr = sockAddr.getAddress(); if (addr instanceof Inet6Address) { remoteIP = ObjectName.quote(addr.getHostAddress()); } else { remoteIP = addr.getHostAddress(); } } sessionId = connection.getSessionId(); } public String getSessionId() { return "0x" + Long.toHexString(sessionId); } public String getSourceIP() { InetSocketAddress sockAddr = connection.getRemoteSocketAddress(); if (sockAddr == null) { return null; } return sockAddr.getAddress().getHostAddress() + ":" + sockAddr.getPort(); } public String getName() { return MBeanRegistry.getInstance().makeFullPath("Connections", remoteIP, getSessionId()); } public boolean isHidden() { return false; } public String[] getEphemeralNodes() { if (zk.getZKDatabase() != null) { String[] res = zk.getZKDatabase().getEphemerals(sessionId).toArray(new String[0]); Arrays.sort(res); return res; } return null; } public String getStartedTime() { return stats.getEstablished().toString(); } public void terminateSession() { try { zk.closeSession(sessionId); } catch (Exception e) { LOG.warn("Unable to closeSession() for session: 0x{}", getSessionId(), e); } } public void terminateConnection() { connection.sendCloseSession(); } public void resetCounters() { stats.resetStats(); } @Override public String toString() { return "ConnectionBean{ClientIP=" + ObjectName.quote(getSourceIP()) + ",SessionId=0x" + getSessionId() + "}"; } public long getOutstandingRequests() { return stats.getOutstandingRequests(); } public long getPacketsReceived() { return stats.getPacketsReceived(); } public long getPacketsSent() { return stats.getPacketsSent(); } public int getSessionTimeout() { return connection.getSessionTimeout(); } public long getMinLatency() { return stats.getMinLatency(); } public long getAvgLatency() { return stats.getAvgLatency(); } public long getMaxLatency() { return stats.getMaxLatency(); } public String getLastOperation() { return stats.getLastOperation(); } public String getLastCxid() { return "0x" + Long.toHexString(stats.getLastCxid()); } public String getLastZxid() { return "0x" + Long.toHexString(stats.getLastZxid()); } public String getLastResponseTime() { return Time.elapsedTimeToDate(stats.getLastResponseTime()).toString(); } public long getLastLatency() { return stats.getLastLatency(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Co0100644 0000000 0000000 00000000161 15051152474 032631 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ConnectionMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ConnectionMXBean.j0100644 0000000 0000000 00000005412 15051152474 034107 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * This MBean represents a client connection. */ public interface ConnectionMXBean { /** * @return source (client) IP address */ String getSourceIP(); /** * @return client's session id */ String getSessionId(); /** * @return time the connection was started */ String getStartedTime(); /** * @return number of ephemeral nodes owned by this connection */ String[] getEphemeralNodes(); /** * @return packets received from this client */ long getPacketsReceived(); /** * @return number of packets sent to this client */ long getPacketsSent(); /** * @return number of requets being processed */ long getOutstandingRequests(); /** * @return session timeout in ms */ int getSessionTimeout(); /** * Terminate this client session. The client will reconnect with a different * session id. */ void terminateSession(); /** * Terminate thei client connection. The client will immediately attempt to * reconnect with the same session id. */ void terminateConnection(); /** Min latency in ms * @since 3.3.0 */ long getMinLatency(); /** Average latency in ms * @since 3.3.0 */ long getAvgLatency(); /** Max latency in ms * @since 3.3.0 */ long getMaxLatency(); /** Last operation performed by this connection * @since 3.3.0 */ String getLastOperation(); /** Last cxid of this connection * @since 3.3.0 */ String getLastCxid(); /** Last zxid of this connection * @since 3.3.0 */ String getLastZxid(); /** Last time server sent a response to client on this connection * @since 3.3.0 */ String getLastResponseTime(); /** Latency of last response to client on this connection in ms * @since 3.3.0 */ long getLastLatency(); /** Reset counters * @since 3.3.0 */ void resetCounters(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Co0100644 0000000 0000000 00000000161 15051152474 032631 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ContainerManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ContainerManager.j0100644 0000000 0000000 00000020535 15051152474 034175 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.apache.zookeeper.DeleteContainerRequest; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manages cleanup of container ZNodes. This class is meant to only * be run from the leader. There's no harm in running from followers/observers * but that will be extra work that's not needed. Once started, it periodically * checks container nodes that have a cversion > 0 and have no children. A * delete is attempted on the node. The result of the delete is unimportant. * If the proposal fails or the container node is not empty there's no harm. */ public class ContainerManager { private static final Logger LOG = LoggerFactory.getLogger(ContainerManager.class); private final ZKDatabase zkDb; private final RequestProcessor requestProcessor; private final int checkIntervalMs; private final int maxPerMinute; private final long maxNeverUsedIntervalMs; private final Timer timer; private final AtomicReference task = new AtomicReference<>(null); /** * @param zkDb the ZK database * @param requestProcessor request processer - used to inject delete * container requests * @param checkIntervalMs how often to check containers in milliseconds * @param maxPerMinute the max containers to delete per second - avoids * herding of container deletions */ public ContainerManager(ZKDatabase zkDb, RequestProcessor requestProcessor, int checkIntervalMs, int maxPerMinute) { this(zkDb, requestProcessor, checkIntervalMs, maxPerMinute, 0); } /** * @param zkDb the ZK database * @param requestProcessor request processer - used to inject delete * container requests * @param checkIntervalMs how often to check containers in milliseconds * @param maxPerMinute the max containers to delete per second - avoids * herding of container deletions * @param maxNeverUsedIntervalMs the max time in milliseconds that a container that has never had * any children is retained */ public ContainerManager(ZKDatabase zkDb, RequestProcessor requestProcessor, int checkIntervalMs, int maxPerMinute, long maxNeverUsedIntervalMs) { this.zkDb = zkDb; this.requestProcessor = requestProcessor; this.checkIntervalMs = checkIntervalMs; this.maxPerMinute = maxPerMinute; this.maxNeverUsedIntervalMs = maxNeverUsedIntervalMs; timer = new Timer("ContainerManagerTask", true); LOG.info("Using checkIntervalMs={} maxPerMinute={} maxNeverUsedIntervalMs={}", checkIntervalMs, maxPerMinute, maxNeverUsedIntervalMs); } /** * start/restart the timer the runs the check. Can safely be called * multiple times. */ public void start() { if (task.get() == null) { TimerTask timerTask = new TimerTask() { @Override public void run() { try { checkContainers(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); LOG.info("interrupted"); cancel(); } catch (Throwable e) { LOG.error("Error checking containers", e); } } }; if (task.compareAndSet(null, timerTask)) { timer.scheduleAtFixedRate(timerTask, checkIntervalMs, checkIntervalMs); } } } /** * stop the timer if necessary. Can safely be called multiple times. */ public void stop() { TimerTask timerTask = task.getAndSet(null); if (timerTask != null) { timerTask.cancel(); } timer.cancel(); } /** * Manually check the containers. Not normally used directly */ public void checkContainers() throws InterruptedException { long minIntervalMs = getMinIntervalMs(); for (String containerPath : getCandidates()) { long startMs = Time.currentElapsedTime(); DeleteContainerRequest record = new DeleteContainerRequest(containerPath); Request request = new Request(null, 0, 0, ZooDefs.OpCode.deleteContainer, RequestRecord.fromRecord(record), null); try { LOG.info("Attempting to delete candidate container: {}", containerPath); postDeleteRequest(request); } catch (Exception e) { LOG.error("Could not delete container: {}", containerPath, e); } long elapsedMs = Time.currentElapsedTime() - startMs; long waitMs = minIntervalMs - elapsedMs; if (waitMs > 0) { Thread.sleep(waitMs); } } } // VisibleForTesting protected void postDeleteRequest(Request request) throws RequestProcessor.RequestProcessorException { requestProcessor.processRequest(request); } // VisibleForTesting protected long getMinIntervalMs() { return TimeUnit.MINUTES.toMillis(1) / maxPerMinute; } // VisibleForTesting protected Collection getCandidates() { Set candidates = new HashSet<>(); for (String containerPath : zkDb.getDataTree().getContainers()) { DataNode node = zkDb.getDataTree().getNode(containerPath); if ((node != null) && node.getChildren().isEmpty()) { /* cversion > 0: keep newly created containers from being deleted before any children have been added. If you were to create the container just before a container cleaning period the container would be immediately be deleted. */ if (node.stat.getCversion() > 0) { candidates.add(containerPath); } else { /* Users may not want unused containers to live indefinitely. Allow a system property to be set that sets the max time for a cversion-0 container to stay before being deleted */ if ((maxNeverUsedIntervalMs != 0) && (getElapsed(node) > maxNeverUsedIntervalMs)) { candidates.add(containerPath); } } } } for (String ttlPath : zkDb.getDataTree().getTtls()) { DataNode node = zkDb.getDataTree().getNode(ttlPath); if (node != null) { Set children = node.getChildren(); if (children.isEmpty()) { if (EphemeralType.get(node.stat.getEphemeralOwner()) == EphemeralType.TTL) { long ttl = EphemeralType.TTL.getValue(node.stat.getEphemeralOwner()); if ((ttl != 0) && (getElapsed(node) > ttl)) { candidates.add(ttlPath); } } } } } return candidates; } // VisibleForTesting protected long getElapsed(DataNode node) { return Time.currentWallTime() - node.stat.getMtime(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataNode.java0100644 0000000 0000000 00000014647 15051152474 033136 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.data.StatPersisted; /** * This class contains the data for a node in the data tree. *

* A data node contains a reference to its parent, a byte array as its data, an * array of ACLs, a stat object, and a set of its children's paths. * */ @SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}) public class DataNode implements Record { // the digest value of this node, calculated from path, data and stat private volatile long digest; // indicate if the digest of this node is up to date or not, used to // optimize the performance. volatile boolean digestCached; /** the data for this datanode */ byte[] data; /** * the acl map long for this datanode. the datatree has the map */ Long acl; /** * the stat for this node that is persisted to disk. */ public StatPersisted stat; /** * the list of children for this node. note that the list of children string * does not contain the parent path -- just the last part of the path. This * should be synchronized on except deserializing (for speed up issues). */ private Set children = null; private static final Set EMPTY_SET = Collections.emptySet(); /** * default constructor for the datanode */ DataNode() { // default constructor } /** * create a DataNode with parent, data, acls and stat * * @param data * the data to be set * @param acl * the acls for this node * @param stat * the stat for this node. */ public DataNode(byte[] data, Long acl, StatPersisted stat) { this.data = data; this.acl = acl; this.stat = stat; } /** * Method that inserts a child into the children set * * @param child * to be inserted * @return true if this set did not already contain the specified element */ public synchronized boolean addChild(String child) { if (children == null) { // let's be conservative on the typical number of children children = new HashSet<>(8); } return children.add(child); } /** * Method that removes a child from the children set * * @param child * @return true if this set contained the specified element */ public synchronized boolean removeChild(String child) { if (children == null) { return false; } return children.remove(child); } /** * convenience method for setting the children for this datanode * * @param children */ public synchronized void setChildren(HashSet children) { this.children = children; } /** * convenience methods to get the children * * @return the children of this datanode. If the datanode has no children, empty * set is returned */ public synchronized Set getChildren() { if (children == null) { return EMPTY_SET; } return Collections.unmodifiableSet(children); } public synchronized void copyStat(Stat to) { to.setAversion(stat.getAversion()); to.setCtime(stat.getCtime()); to.setCzxid(stat.getCzxid()); to.setMtime(stat.getMtime()); to.setMzxid(stat.getMzxid()); to.setPzxid(stat.getPzxid()); to.setVersion(stat.getVersion()); to.setEphemeralOwner(getClientEphemeralOwner(stat)); to.setDataLength(data == null ? 0 : data.length); int numChildren = 0; if (this.children != null) { numChildren = children.size(); } // when we do the Cversion we need to translate from the count of the creates // to the count of the changes (v3 semantics) // for every create there is a delete except for the children still present to.setCversion(stat.getCversion() * 2 - numChildren); to.setNumChildren(numChildren); } private static long getClientEphemeralOwner(StatPersisted stat) { EphemeralType ephemeralType = EphemeralType.get(stat.getEphemeralOwner()); if (ephemeralType != EphemeralType.NORMAL) { return 0; } return stat.getEphemeralOwner(); } public synchronized void deserialize(InputArchive archive, String tag) throws IOException { archive.startRecord("node"); data = archive.readBuffer("data"); acl = archive.readLong("acl"); stat = new StatPersisted(); stat.deserialize(archive, "statpersisted"); archive.endRecord("node"); } public synchronized void serialize(OutputArchive archive, String tag) throws IOException { archive.startRecord(this, "node"); archive.writeBuffer(data, "data"); archive.writeLong(acl, "acl"); stat.serialize(archive, "statpersisted"); archive.endRecord(this, "node"); } public boolean isDigestCached() { return digestCached; } public void setDigestCached(boolean digestCached) { this.digestCached = digestCached; } public long getDigest() { return digest; } public void setDigest(long digest) { this.digest = digest; } public synchronized byte[] getData() { return data; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTree.java0100644 0000000 0000000 00000217454 15051152474 033151 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.EOFException; import java.io.IOException; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.DigestWatcher; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.KeeperException.NodeExistsException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.audit.AuditConstants; import org.apache.zookeeper.audit.AuditEvent.Result; import org.apache.zookeeper.audit.ZKAuditProvider; import org.apache.zookeeper.common.PathTrie; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.server.watch.IWatchManager; import org.apache.zookeeper.server.watch.WatchManagerFactory; import org.apache.zookeeper.server.watch.WatcherMode; import org.apache.zookeeper.server.watch.WatcherOrBitSet; import org.apache.zookeeper.server.watch.WatchesPathReport; import org.apache.zookeeper.server.watch.WatchesReport; import org.apache.zookeeper.server.watch.WatchesSummary; import org.apache.zookeeper.txn.CheckVersionTxn; import org.apache.zookeeper.txn.CloseSessionTxn; import org.apache.zookeeper.txn.CreateContainerTxn; import org.apache.zookeeper.txn.CreateTTLTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.ErrorTxn; import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.SetACLTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.Txn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class maintains the tree data structure. It doesn't have any networking * or client connection code in it so that it can be tested in a standalone way. * *

The tree maintains two parallel data structures: a hashtable that maps from * full paths to DataNodes and a tree of DataNodes. All accesses to a path is * through the hashtable. The tree is traversed only when serializing to disk. */ public class DataTree { private static final Logger LOG = LoggerFactory.getLogger(DataTree.class); private final RateLogger RATE_LOGGER = new RateLogger(LOG, 15 * 60 * 1000); /** * This map provides a fast lookup to the data nodes. The tree is the * source of truth and is where all the locking occurs */ private final NodeHashMap nodes; private IWatchManager dataWatches; private IWatchManager childWatches; /** cached total size of paths and data for all DataNodes */ private final AtomicLong nodeDataSize = new AtomicLong(0); /** the root of zookeeper tree */ private static final String rootZookeeper = "/"; /** the zookeeper nodes that acts as the management and status node **/ private static final String procZookeeper = Quotas.procZookeeper; /** this will be the string that's stored as a child of root */ private static final String procChildZookeeper = procZookeeper.substring(1); /** * the zookeeper quota node that acts as the quota management node for * zookeeper */ private static final String quotaZookeeper = Quotas.quotaZookeeper; /** this will be the string that's stored as a child of /zookeeper */ private static final String quotaChildZookeeper = quotaZookeeper.substring(procZookeeper.length() + 1); /** * the zookeeper config node that acts as the config management node for * zookeeper */ private static final String configZookeeper = ZooDefs.CONFIG_NODE; /** this will be the string that's stored as a child of /zookeeper */ private static final String configChildZookeeper = configZookeeper.substring(procZookeeper.length() + 1); /** * the path trie that keeps track of the quota nodes in this datatree */ private final PathTrie pTrie = new PathTrie(); /** * over-the-wire size of znode stat. Counting the fields of Stat class */ public static final int STAT_OVERHEAD_BYTES = (6 * 8) + (5 * 4); /** * This hashtable lists the paths of the ephemeral nodes of a session. */ private final Map> ephemerals = new ConcurrentHashMap<>(); /** * This set contains the paths of all container nodes */ private final Set containers = Collections.newSetFromMap(new ConcurrentHashMap<>()); /** * This set contains the paths of all ttl nodes */ private final Set ttls = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final ReferenceCountedACLCache aclCache = new ReferenceCountedACLCache(); // The maximum number of tree digests that we will keep in our history public static final int DIGEST_LOG_LIMIT = 1024; // Dump digest every 128 txns, in hex it's 80, which will make it easier // to align and compare between servers. public static final int DIGEST_LOG_INTERVAL = 128; // If this is not null, we are actively looking for a target zxid that we // want to validate the digest for private ZxidDigest digestFromLoadedSnapshot; // The digest associated with the highest zxid in the data tree. private volatile ZxidDigest lastProcessedZxidDigest; private boolean firstMismatchTxn = true; // Will be notified when digest mismatch event triggered. private final List digestWatchers = new ArrayList<>(); // The historical digests list. private final LinkedList digestLog = new LinkedList<>(); private final DigestCalculator digestCalculator; @SuppressWarnings("unchecked") public Set getEphemerals(long sessionId) { HashSet ret = ephemerals.get(sessionId); if (ret == null) { return new HashSet<>(); } synchronized (ret) { return (HashSet) ret.clone(); } } public Set getContainers() { return new HashSet<>(containers); } public Set getTtls() { return new HashSet<>(ttls); } public Collection getSessions() { return ephemerals.keySet(); } public DataNode getNode(String path) { return nodes.get(path); } public int getNodeCount() { return nodes.size(); } public int getWatchCount() { return dataWatches.size() + childWatches.size(); } public int getEphemeralsCount() { int result = 0; for (HashSet set : ephemerals.values()) { result += set.size(); } return result; } /** * Get the size of the nodes based on path and data length. * * @return size of the data */ public long approximateDataSize() { long result = 0; for (Map.Entry entry : nodes.entrySet()) { DataNode value = entry.getValue(); synchronized (value) { result += getNodeSize(entry.getKey(), value.data); } } return result; } /** * Get the size of the node based on path and data length. */ private static long getNodeSize(String path, byte[] data) { return (path == null ? 0 : path.length()) + (data == null ? 0 : data.length); } public long cachedApproximateDataSize() { return nodeDataSize.get(); } /** * This is a pointer to the root of the DataTree. It is the source of truth, * but we usually use the nodes hashmap to find nodes in the tree. */ private DataNode root = new DataNode(new byte[0], -1L, new StatPersisted()); /** * create a /zookeeper filesystem that is the proc filesystem of zookeeper */ private final DataNode procDataNode = new DataNode(new byte[0], -1L, new StatPersisted()); /** * create a /zookeeper/quota node for maintaining quota properties for * zookeeper */ private final DataNode quotaDataNode = new DataNode(new byte[0], -1L, new StatPersisted()); public DataTree() { this(new DigestCalculator()); } DataTree(DigestCalculator digestCalculator) { this.digestCalculator = digestCalculator; nodes = new NodeHashMapImpl(digestCalculator); // rather than fight it, let root have an alias nodes.put("", root); nodes.putWithoutDigest(rootZookeeper, root); // add the proc node and quota node root.addChild(procChildZookeeper); nodes.put(procZookeeper, procDataNode); procDataNode.addChild(quotaChildZookeeper); nodes.put(quotaZookeeper, quotaDataNode); addConfigNode(); nodeDataSize.set(approximateDataSize()); try { dataWatches = WatchManagerFactory.createWatchManager(); childWatches = WatchManagerFactory.createWatchManager(); } catch (Exception e) { LOG.error("Unexpected exception when creating WatchManager, exiting abnormally", e); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } } /** * create a /zookeeper/config node for maintaining the configuration (membership and quorum system) info for * zookeeper */ public void addConfigNode() { DataNode zookeeperZnode = nodes.get(procZookeeper); if (zookeeperZnode != null) { // should always be the case zookeeperZnode.addChild(configChildZookeeper); } else { assert false : "There's no /zookeeper znode - this should never happen."; } nodes.put(configZookeeper, new DataNode(new byte[0], -1L, new StatPersisted())); try { // Reconfig node is access controlled by default (ZOOKEEPER-2014). setACL(configZookeeper, ZooDefs.Ids.READ_ACL_UNSAFE, -1); } catch (NoNodeException e) { assert false : "There's no " + configZookeeper + " znode - this should never happen."; } } /** * is the path one of the special paths owned by zookeeper. * * @param path * the path to be checked * @return true if a special path. false if not. */ boolean isSpecialPath(String path) { return rootZookeeper.equals(path) || procZookeeper.equals(path) || quotaZookeeper.equals(path) || configZookeeper.equals(path); } public static void copyStatPersisted(StatPersisted from, StatPersisted to) { to.setAversion(from.getAversion()); to.setCtime(from.getCtime()); to.setCversion(from.getCversion()); to.setCzxid(from.getCzxid()); to.setMtime(from.getMtime()); to.setMzxid(from.getMzxid()); to.setPzxid(from.getPzxid()); to.setVersion(from.getVersion()); to.setEphemeralOwner(from.getEphemeralOwner()); } public static void copyStat(Stat from, Stat to) { to.setAversion(from.getAversion()); to.setCtime(from.getCtime()); to.setCversion(from.getCversion()); to.setCzxid(from.getCzxid()); to.setMtime(from.getMtime()); to.setMzxid(from.getMzxid()); to.setPzxid(from.getPzxid()); to.setVersion(from.getVersion()); to.setEphemeralOwner(from.getEphemeralOwner()); to.setDataLength(from.getDataLength()); to.setNumChildren(from.getNumChildren()); } /** * update the count/bytes of this stat data node * * @param lastPrefix * the path of the node that has a quota. * @param bytesDiff * the diff to be added to number of bytes * @param countDiff * the diff to be added to the count */ public void updateQuotaStat(String lastPrefix, long bytesDiff, int countDiff) { String statNodePath = Quotas.statPath(lastPrefix); DataNode statNode = nodes.get(statNodePath); if (statNode == null) { // should not happen LOG.error("Missing node for stat {}", statNodePath); return; } synchronized (statNode) { StatsTrack updatedStat = new StatsTrack(statNode.data); updatedStat.setCount(updatedStat.getCount() + countDiff); updatedStat.setBytes(updatedStat.getBytes() + bytesDiff); statNode.data = updatedStat.getStatsBytes(); } } /** * Add a new node to the DataTree. * @param path * Path for the new node. * @param data * Data to store in the node. * @param acl * Node acls * @param ephemeralOwner * the session id that owns this node. -1 indicates this is not * an ephemeral node. * @param zxid * Transaction ID * @param time * @throws NodeExistsException * @throws NoNodeException */ public void createNode(final String path, byte[] data, List acl, long ephemeralOwner, int parentCVersion, long zxid, long time) throws NoNodeException, NodeExistsException { createNode(path, data, acl, ephemeralOwner, parentCVersion, zxid, time, null); } /** * Add a new node to the DataTree. * @param path * Path for the new node. * @param data * Data to store in the node. * @param acl * Node acls * @param ephemeralOwner * the session id that owns this node. -1 indicates this is not * an ephemeral node. * @param zxid * Transaction ID * @param time * @param outputStat * A Stat object to store Stat output results into. * @throws NodeExistsException * @throws NoNodeException */ public void createNode(final String path, byte[] data, List acl, long ephemeralOwner, int parentCVersion, long zxid, long time, Stat outputStat) throws NoNodeException, NodeExistsException { int lastSlash = path.lastIndexOf('/'); String parentName = path.substring(0, lastSlash); String childName = path.substring(lastSlash + 1); StatPersisted stat = createStat(zxid, time, ephemeralOwner); DataNode parent = nodes.get(parentName); if (parent == null) { throw new NoNodeException(); } List parentAcl; synchronized (parent) { parentAcl = getACL(parent); // Add the ACL to ACL cache first, to avoid the ACL not being // created race condition during fuzzy snapshot sync. // // This is the simplest fix, which may add ACL reference count // again if it's already counted in the ACL map of fuzzy // snapshot, which might also happen for deleteNode txn, but // at least it won't cause the ACL not exist issue. // // Later we can audit and delete all non-referenced ACLs from // ACL map when loading the snapshot/txns from disk, like what // we did for the global sessions. Long acls = aclCache.convertAcls(acl); DataNode existingChild = nodes.get(path); if (existingChild != null) { existingChild.acl = acls; throw new NodeExistsException(); } nodes.preChange(parentName, parent); if (parentCVersion == -1) { parentCVersion = parent.stat.getCversion(); parentCVersion++; } // There is possibility that we'll replay txns for a node which // was created and then deleted in the fuzzy range, and it's not // exist in the snapshot, so replay the creation might revert the // cversion and pzxid, need to check and only update when it's // larger. if (parentCVersion > parent.stat.getCversion()) { parent.stat.setCversion(parentCVersion); parent.stat.setPzxid(zxid); } DataNode child = new DataNode(data, acls, stat); parent.addChild(childName); nodes.postChange(parentName, parent); nodeDataSize.addAndGet(getNodeSize(path, child.data)); nodes.put(path, child); EphemeralType ephemeralType = EphemeralType.get(ephemeralOwner); if (ephemeralType == EphemeralType.CONTAINER) { containers.add(path); } else if (ephemeralType == EphemeralType.TTL) { ttls.add(path); } else if (ephemeralOwner != 0) { HashSet list = ephemerals.computeIfAbsent(ephemeralOwner, k -> new HashSet<>()); synchronized (list) { list.add(path); } } if (outputStat != null) { child.copyStat(outputStat); } } // now check if its one of the zookeeper node child if (parentName.startsWith(quotaZookeeper)) { // now check if it's the limit node if (Quotas.limitNode.equals(childName)) { // this is the limit node // get the parent and add it to the trie pTrie.addPath(Quotas.trimQuotaPath(parentName)); } if (Quotas.statNode.equals(childName)) { updateQuotaForPath(Quotas.trimQuotaPath(parentName)); } } String lastPrefix = getMaxPrefixWithQuota(path); long bytes = data == null ? 0 : data.length; // also check to update the quotas for this node if (lastPrefix != null) { // ok we have some match and need to update updateQuotaStat(lastPrefix, bytes, 1); } updateWriteStat(path, bytes); dataWatches.triggerWatch(path, Event.EventType.NodeCreated, zxid, acl); childWatches.triggerWatch(parentName.equals("") ? "/" : parentName, Event.EventType.NodeChildrenChanged, zxid, parentAcl); } /** * remove the path from the datatree * * @param path * the path to of the node to be deleted * @param zxid * the current zxid * @throws NoNodeException */ public void deleteNode(String path, long zxid) throws NoNodeException { int lastSlash = path.lastIndexOf('/'); String parentName = path.substring(0, lastSlash); String childName = path.substring(lastSlash + 1); // The child might already be deleted during taking fuzzy snapshot, // but we still need to update the pzxid here before throw exception // for no such child DataNode parent = nodes.get(parentName); if (parent == null) { throw new NoNodeException(); } synchronized (parent) { nodes.preChange(parentName, parent); parent.removeChild(childName); // Only update pzxid when the zxid is larger than the current pzxid, // otherwise we might override some higher pzxid set by a CreateTxn, // which could cause the cversion and pzxid inconsistent if (zxid > parent.stat.getPzxid()) { parent.stat.setPzxid(zxid); } nodes.postChange(parentName, parent); } DataNode node = nodes.get(path); if (node == null) { throw new NoNodeException(); } List acl; nodes.remove(path); synchronized (node) { acl = getACL(node); aclCache.removeUsage(node.acl); nodeDataSize.addAndGet(-getNodeSize(path, node.data)); } // Synchronized to sync the containers and ttls change, probably // only need to sync on containers and ttls, will update it in a // separate patch. List parentAcl; synchronized (parent) { parentAcl = getACL(parent); long owner = node.stat.getEphemeralOwner(); EphemeralType ephemeralType = EphemeralType.get(owner); if (ephemeralType == EphemeralType.CONTAINER) { containers.remove(path); } else if (ephemeralType == EphemeralType.TTL) { ttls.remove(path); } else if (owner != 0) { Set nodes = ephemerals.get(owner); if (nodes != null) { synchronized (nodes) { nodes.remove(path); } } } } if (parentName.startsWith(procZookeeper) && Quotas.limitNode.equals(childName)) { // delete the node in the trie. // we need to update the trie as well pTrie.deletePath(Quotas.trimQuotaPath(parentName)); } // also check to update the quotas for this node String lastPrefix = getMaxPrefixWithQuota(path); if (lastPrefix != null) { // ok we have some match and need to update long bytes; synchronized (node) { bytes = (node.data == null ? 0 : -(node.data.length)); } updateQuotaStat(lastPrefix, bytes, -1); } updateWriteStat(path, 0L); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "dataWatches.triggerWatch " + path); ZooTrace.logTraceMessage( LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "childWatches.triggerWatch " + parentName); } WatcherOrBitSet processed = dataWatches.triggerWatch(path, EventType.NodeDeleted, zxid, acl); childWatches.triggerWatch(path, EventType.NodeDeleted, zxid, acl, processed); childWatches.triggerWatch("".equals(parentName) ? "/" : parentName, EventType.NodeChildrenChanged, zxid, parentAcl); } public Stat setData(String path, byte[] data, int version, long zxid, long time) throws NoNodeException { Stat s = new Stat(); DataNode n = nodes.get(path); if (n == null) { throw new NoNodeException(); } List acl; byte[] lastData; synchronized (n) { acl = getACL(n); lastData = n.data; nodes.preChange(path, n); n.data = data; n.stat.setMtime(time); n.stat.setMzxid(zxid); n.stat.setVersion(version); n.copyStat(s); nodes.postChange(path, n); } // first do a quota check if the path is in a quota subtree. String lastPrefix = getMaxPrefixWithQuota(path); long bytesDiff = (data == null ? 0 : data.length) - (lastData == null ? 0 : lastData.length); // now update if the path is in a quota subtree. long dataBytes = data == null ? 0 : data.length; if (lastPrefix != null) { updateQuotaStat(lastPrefix, bytesDiff, 0); } nodeDataSize.addAndGet(getNodeSize(path, data) - getNodeSize(path, lastData)); updateWriteStat(path, dataBytes); dataWatches.triggerWatch(path, EventType.NodeDataChanged, zxid, acl); return s; } /** * If there is a quota set, return the appropriate prefix for that quota * Else return null * @param path The ZK path to check for quota * @return Max quota prefix, or null if none */ public String getMaxPrefixWithQuota(String path) { // do nothing for the root. // we are not keeping a quota on the zookeeper // root node for now. String lastPrefix = pTrie.findMaxPrefix(path); if (rootZookeeper.equals(lastPrefix) || lastPrefix.isEmpty()) { return null; } else { return lastPrefix; } } public void addWatch(String basePath, Watcher watcher, int mode) { WatcherMode watcherMode = WatcherMode.fromZooDef(mode); dataWatches.addWatch(basePath, watcher, watcherMode); if (watcherMode != WatcherMode.PERSISTENT_RECURSIVE) { childWatches.addWatch(basePath, watcher, watcherMode); } } public byte[] getData(String path, Stat stat, Watcher watcher) throws NoNodeException { DataNode n = nodes.get(path); if (n == null) { throw new NoNodeException(); } byte[] data; synchronized (n) { n.copyStat(stat); if (watcher != null) { dataWatches.addWatch(path, watcher); } data = n.data; } updateReadStat(path, data == null ? 0 : data.length); return data; } public Stat statNode(String path, Watcher watcher) throws NoNodeException { if (watcher != null) { dataWatches.addWatch(path, watcher); } DataNode n = nodes.get(path); if (n == null) { throw new NoNodeException(); } Stat stat = new Stat(); synchronized (n) { n.copyStat(stat); } updateReadStat(path, 0L); return stat; } public List getChildren(String path, Stat stat, Watcher watcher) throws NoNodeException { DataNode n = nodes.get(path); if (n == null) { throw new NoNodeException(); } List children; synchronized (n) { if (stat != null) { n.copyStat(stat); } children = new ArrayList<>(n.getChildren()); if (watcher != null) { childWatches.addWatch(path, watcher); } } int bytes = 0; for (String child : children) { bytes += child.length(); } updateReadStat(path, bytes); return children; } public int getAllChildrenNumber(String path) { // cull out these two keys:"", "/" if ("/".equals(path)) { return nodes.size() - 2; } return (int) nodes.entrySet().parallelStream().filter(entry -> entry.getKey().startsWith(path + "/")).count(); } public Stat setACL(String path, List acl, int version) throws NoNodeException { DataNode n = nodes.get(path); if (n == null) { throw new NoNodeException(); } synchronized (n) { Stat stat = new Stat(); aclCache.removeUsage(n.acl); nodes.preChange(path, n); n.stat.setAversion(version); n.acl = aclCache.convertAcls(acl); n.copyStat(stat); nodes.postChange(path, n); return stat; } } public List getACL(String path, Stat stat) throws NoNodeException { DataNode n = nodes.get(path); if (n == null) { throw new NoNodeException(); } synchronized (n) { if (stat != null) { n.copyStat(stat); } return new ArrayList<>(aclCache.convertLong(n.acl)); } } public List getACL(DataNode node) { synchronized (node) { return aclCache.convertLong(node.acl); } } public int aclCacheSize() { return aclCache.size(); } public static class ProcessTxnResult { public long clientId; public int cxid; public long zxid; public int err; public int type; public String path; public Stat stat; public List multiResult; /** * Equality is defined as the clientId and the cxid being the same. This * allows us to use hash tables to track completion of transactions. * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object o) { if (o instanceof ProcessTxnResult) { ProcessTxnResult other = (ProcessTxnResult) o; return other.clientId == clientId && other.cxid == cxid; } return false; } /** * See equals() to find the rationale for how this hashcode is generated. * * @see ProcessTxnResult#equals(Object) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return (int) ((clientId ^ cxid) % Integer.MAX_VALUE); } } public volatile long lastProcessedZxid = 0; public ProcessTxnResult processTxn(TxnHeader header, Record txn, TxnDigest digest) { ProcessTxnResult result = processTxn(header, txn); compareDigest(header, txn, digest); return result; } public ProcessTxnResult processTxn(TxnHeader header, Record txn) { return this.processTxn(header, txn, false); } public ProcessTxnResult processTxn(TxnHeader header, Record txn, boolean isSubTxn) { ProcessTxnResult rc = new ProcessTxnResult(); try { rc.clientId = header.getClientId(); rc.cxid = header.getCxid(); rc.zxid = header.getZxid(); rc.type = header.getType(); rc.err = 0; rc.multiResult = null; switch (header.getType()) { case OpCode.create: CreateTxn createTxn = (CreateTxn) txn; rc.path = createTxn.getPath(); createNode( createTxn.getPath(), createTxn.getData(), createTxn.getAcl(), createTxn.getEphemeral() ? header.getClientId() : 0, createTxn.getParentCVersion(), header.getZxid(), header.getTime(), null); break; case OpCode.create2: CreateTxn create2Txn = (CreateTxn) txn; rc.path = create2Txn.getPath(); Stat stat = new Stat(); createNode( create2Txn.getPath(), create2Txn.getData(), create2Txn.getAcl(), create2Txn.getEphemeral() ? header.getClientId() : 0, create2Txn.getParentCVersion(), header.getZxid(), header.getTime(), stat); rc.stat = stat; break; case OpCode.createTTL: CreateTTLTxn createTtlTxn = (CreateTTLTxn) txn; rc.path = createTtlTxn.getPath(); stat = new Stat(); createNode( createTtlTxn.getPath(), createTtlTxn.getData(), createTtlTxn.getAcl(), EphemeralType.TTL.toEphemeralOwner(createTtlTxn.getTtl()), createTtlTxn.getParentCVersion(), header.getZxid(), header.getTime(), stat); rc.stat = stat; break; case OpCode.createContainer: CreateContainerTxn createContainerTxn = (CreateContainerTxn) txn; rc.path = createContainerTxn.getPath(); stat = new Stat(); createNode( createContainerTxn.getPath(), createContainerTxn.getData(), createContainerTxn.getAcl(), EphemeralType.CONTAINER_EPHEMERAL_OWNER, createContainerTxn.getParentCVersion(), header.getZxid(), header.getTime(), stat); rc.stat = stat; break; case OpCode.delete: case OpCode.deleteContainer: DeleteTxn deleteTxn = (DeleteTxn) txn; rc.path = deleteTxn.getPath(); deleteNode(deleteTxn.getPath(), header.getZxid()); break; case OpCode.reconfig: case OpCode.setData: SetDataTxn setDataTxn = (SetDataTxn) txn; rc.path = setDataTxn.getPath(); rc.stat = setData( setDataTxn.getPath(), setDataTxn.getData(), setDataTxn.getVersion(), header.getZxid(), header.getTime()); break; case OpCode.setACL: SetACLTxn setACLTxn = (SetACLTxn) txn; rc.path = setACLTxn.getPath(); rc.stat = setACL(setACLTxn.getPath(), setACLTxn.getAcl(), setACLTxn.getVersion()); break; case OpCode.closeSession: long sessionId = header.getClientId(); if (txn != null) { killSession(sessionId, header.getZxid(), ephemerals.remove(sessionId), ((CloseSessionTxn) txn).getPaths2Delete()); } else { killSession(sessionId, header.getZxid()); } break; case OpCode.error: ErrorTxn errTxn = (ErrorTxn) txn; rc.err = errTxn.getErr(); break; case OpCode.check: CheckVersionTxn checkTxn = (CheckVersionTxn) txn; rc.path = checkTxn.getPath(); break; case OpCode.multi: MultiTxn multiTxn = (MultiTxn) txn; List txns = multiTxn.getTxns(); rc.multiResult = new ArrayList<>(); boolean failed = false; for (Txn subtxn : txns) { if (subtxn.getType() == OpCode.error) { failed = true; break; } } boolean post_failed = false; for (Txn subtxn : txns) { ByteBuffer bb = ByteBuffer.wrap(subtxn.getData()); Record record; switch (subtxn.getType()) { case OpCode.create: case OpCode.create2: record = new CreateTxn(); break; case OpCode.createTTL: record = new CreateTTLTxn(); break; case OpCode.createContainer: record = new CreateContainerTxn(); break; case OpCode.delete: case OpCode.deleteContainer: record = new DeleteTxn(); break; case OpCode.setData: record = new SetDataTxn(); break; case OpCode.error: record = new ErrorTxn(); post_failed = true; break; case OpCode.check: record = new CheckVersionTxn(); break; default: throw new IOException("Invalid type of op: " + subtxn.getType()); } assert record != null; ByteBufferInputStream.byteBuffer2Record(bb, record); if (failed && subtxn.getType() != OpCode.error) { int ec = post_failed ? Code.RUNTIMEINCONSISTENCY.intValue() : Code.OK.intValue(); subtxn.setType(OpCode.error); record = new ErrorTxn(ec); } assert !failed || (subtxn.getType() == OpCode.error); TxnHeader subHdr = new TxnHeader( header.getClientId(), header.getCxid(), header.getZxid(), header.getTime(), subtxn.getType()); ProcessTxnResult subRc = processTxn(subHdr, record, true); rc.multiResult.add(subRc); if (subRc.err != 0 && rc.err == 0) { rc.err = subRc.err; } } break; } } catch (KeeperException e) { LOG.debug("Failed: {}:{}", header, txn, e); rc.err = e.code().intValue(); } catch (IOException e) { LOG.debug("Failed: {}:{}", header, txn, e); } /* * Snapshots are taken lazily. When serializing a node, it's data * and children copied in a synchronization block on that node, * which means newly created node won't be in the snapshot, so * we won't have mismatched cversion and pzxid when replaying the * createNode txn. * * But there is a tricky scenario that if the child is deleted due * to session close and re-created in a different global session * after that the parent is serialized, then when replay the txn * because the node belongs to a different session, replay the * closeSession txn won't delete it anymore, and we'll get NODEEXISTS * error when replay the createNode txn. In this case, we need to * update the cversion and pzxid to the new value. * * Note, such failures on DT should be seen only during * restore. */ if (header.getType() == OpCode.create && rc.err == Code.NODEEXISTS.intValue()) { LOG.debug("Adjusting parent cversion for Txn: {} path: {} err: {}", header.getType(), rc.path, rc.err); int lastSlash = rc.path.lastIndexOf('/'); String parentName = rc.path.substring(0, lastSlash); CreateTxn cTxn = (CreateTxn) txn; try { setCversionPzxid(parentName, cTxn.getParentCVersion(), header.getZxid()); } catch (NoNodeException e) { LOG.error("Failed to set parent cversion for: {}", parentName, e); rc.err = e.code().intValue(); } } else if (rc.err != Code.OK.intValue()) { LOG.debug("Ignoring processTxn failure hdr: {} : error: {}", header.getType(), rc.err); } /* * Things we can only update after the whole txn is applied to data * tree. * * If we update the lastProcessedZxid with the first sub txn in multi * and there is a snapshot in progress, it's possible that the zxid * associated with the snapshot only include partial of the multi op. * * When loading snapshot, it will only load the txns after the zxid * associated with snapshot file, which could cause data inconsistency * due to missing sub txns. * * To avoid this, we only update the lastProcessedZxid when the whole * multi-op txn is applied to DataTree. */ if (!isSubTxn) { /* * A snapshot might be in progress while we are modifying the data * tree. If we set lastProcessedZxid prior to making corresponding * change to the tree, then the zxid associated with the snapshot * file will be ahead of its contents. Thus, while restoring from * the snapshot, the restore method will not apply the transaction * for zxid associated with the snapshot file, since the restore * method assumes that transaction to be present in the snapshot. * * To avoid this, we first apply the transaction and then modify * lastProcessedZxid. During restore, we correctly handle the * case where the snapshot contains data ahead of the zxid associated * with the file. */ if (rc.zxid > lastProcessedZxid) { lastProcessedZxid = rc.zxid; } if (digestFromLoadedSnapshot != null) { compareSnapshotDigests(rc.zxid); } else { // only start recording digest when we're not in fuzzy state logZxidDigest(rc.zxid, getTreeDigest()); } } return rc; } void killSession(long session, long zxid) { // The list is already removed from the ephemerals, // so we do not have to worry about synchronizing on // the list. This is only called from FinalRequestProcessor // so there is no need for synchronization. The list is not // changed here. Only create and delete change the list which // are again called from FinalRequestProcessor in sequence. killSession(session, zxid, ephemerals.remove(session), null); } void killSession(long session, long zxid, Set paths2DeleteLocal, List paths2DeleteInTxn) { if (paths2DeleteInTxn != null) { deleteNodes(session, zxid, paths2DeleteInTxn); } if (paths2DeleteLocal == null) { return; } if (paths2DeleteInTxn != null) { // explicitly check and remove to avoid potential performance // issue when using removeAll for (String path: paths2DeleteInTxn) { paths2DeleteLocal.remove(path); } if (!paths2DeleteLocal.isEmpty()) { LOG.warn( "Unexpected extra paths under session {} which are not in txn 0x{}", paths2DeleteLocal, Long.toHexString(zxid)); } } deleteNodes(session, zxid, paths2DeleteLocal); } void deleteNodes(long session, long zxid, Iterable paths2Delete) { for (String path : paths2Delete) { boolean deleted = false; String sessionHex = "0x" + Long.toHexString(session); try { deleteNode(path, zxid); deleted = true; LOG.debug("Deleting ephemeral node {} for session {}", path, sessionHex); } catch (NoNodeException e) { LOG.warn( "Ignoring NoNodeException for path {} while removing ephemeral for dead session {}", path, sessionHex); } if (ZKAuditProvider.isAuditEnabled()) { if (deleted) { ZKAuditProvider.log(ZKAuditProvider.getZKUser(), AuditConstants.OP_DEL_EZNODE_EXP, path, null, null, sessionHex, null, Result.SUCCESS); } else { ZKAuditProvider.log(ZKAuditProvider.getZKUser(), AuditConstants.OP_DEL_EZNODE_EXP, path, null, null, sessionHex, null, Result.FAILURE); } } } } /** * An encapsulation class for return value */ private static class Counts { long bytes; int count; } /** * this method gets the count of nodes and the bytes under a subtree * * @param path the path to be used * @param counts the int count */ private void getCounts(String path, Counts counts) { DataNode node = getNode(path); if (node == null) { return; } String[] children; int len; synchronized (node) { children = node.getChildren().toArray(new String[0]); len = (node.data == null ? 0 : node.data.length); } // add itself counts.count += 1; counts.bytes += len; for (String child : children) { getCounts(path + "/" + child, counts); } } /** * update the quota for the given path * * @param path the path to be used */ private void updateQuotaForPath(String path) { Counts c = new Counts(); getCounts(path, c); StatsTrack statsTrack = new StatsTrack(); statsTrack.setBytes(c.bytes); statsTrack.setCount(c.count); String statPath = Quotas.statPath(path); DataNode node = getNode(statPath); // it should exist if (node == null) { LOG.warn("Missing quota stat node {}", statPath); return; } synchronized (node) { nodes.preChange(statPath, node); node.data = statsTrack.getStatsBytes(); nodes.postChange(statPath, node); } } /** * this method traverses the quota path and update the path trie and sets * * @param path the path to be used */ private void traverseNode(String path) { DataNode node = getNode(path); String[] children; synchronized (node) { children = node.getChildren().toArray(new String[0]); } if (children.length == 0) { // this node does not have a child // is the leaf node // check if it's the leaf node String endString = "/" + Quotas.limitNode; if (path.endsWith(endString)) { // ok this is the limit node // get the real node and update // the count and the bytes String realPath = path.substring(Quotas.quotaZookeeper.length(), path.indexOf(endString)); updateQuotaForPath(realPath); this.pTrie.addPath(realPath); } return; } for (String child : children) { traverseNode(path + "/" + child); } } /** * this method sets up the path trie and sets up stats for quota nodes */ private void setupQuota() { String quotaPath = Quotas.quotaZookeeper; DataNode node = getNode(quotaPath); if (node == null) { return; } traverseNode(quotaPath); } /** * this method uses a stringbuilder to create a new path for children. This * is faster than string appends ( str1 + str2). * * @param oa OutputArchive to write to. * @param path a string builder. * @throws IOException */ void serializeNode(OutputArchive oa, StringBuilder path) throws IOException { String pathString = path.toString(); DataNode node = getNode(pathString); if (node == null) { return; } String[] children; DataNode nodeCopy; synchronized (node) { StatPersisted statCopy = new StatPersisted(); copyStatPersisted(node.stat, statCopy); //we do not need to make a copy of node.data because the contents //are never changed nodeCopy = new DataNode(node.data, node.acl, statCopy); children = node.getChildren().toArray(new String[0]); } serializeNodeData(oa, pathString, nodeCopy); path.append('/'); int off = path.length(); for (String child : children) { // Since this is single buffer being reused, we need to truncate the previous bytes of string. path.delete(off, Integer.MAX_VALUE); path.append(child); serializeNode(oa, path); } } // visible for test public void serializeNodeData(OutputArchive oa, String path, DataNode node) throws IOException { oa.writeString(path, "path"); oa.writeRecord(node, "node"); } public void serializeAcls(OutputArchive oa) throws IOException { aclCache.serialize(oa); } public void serializeNodes(OutputArchive oa) throws IOException { serializeNode(oa, new StringBuilder()); // / marks end of stream // we need to check if clear had been called in between the snapshot. if (root != null) { oa.writeString("/", "path"); } } public void serialize(OutputArchive oa, String tag) throws IOException { serializeAcls(oa); serializeNodes(oa); } public void deserialize(InputArchive ia, String tag) throws IOException { aclCache.deserialize(ia); nodes.clear(); pTrie.clear(); nodeDataSize.set(0); String path = ia.readString("path"); while (!"/".equals(path)) { DataNode node = new DataNode(); ia.readRecord(node, "node"); nodes.put(path, node); synchronized (node) { aclCache.addUsage(node.acl); } int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1) { root = node; } else { String parentPath = path.substring(0, lastSlash); DataNode parent = nodes.get(parentPath); if (parent == null) { throw new IOException( "Invalid Datatree, unable to find parent " + parentPath + " of path " + path); } parent.addChild(path.substring(lastSlash + 1)); long owner = node.stat.getEphemeralOwner(); EphemeralType ephemeralType = EphemeralType.get(owner); if (ephemeralType == EphemeralType.CONTAINER) { containers.add(path); } else if (ephemeralType == EphemeralType.TTL) { ttls.add(path); } else if (owner != 0) { HashSet list = ephemerals.computeIfAbsent(owner, k -> new HashSet<>()); list.add(path); } } path = ia.readString("path"); } // have counted digest for root node with "", ignore here to avoid // counting twice for root node nodes.putWithoutDigest("/", root); nodeDataSize.set(approximateDataSize()); // we are done with deserializing the datatree // update the quotas - create path trie // and also update the stat nodes setupQuota(); aclCache.purgeUnused(); } /** * Summary of the watches on the datatree. * @param writer the output to write to */ public synchronized void dumpWatchesSummary(PrintWriter writer) { writer.print(dataWatches.toString()); } /** * Write a text dump of all the watches on the datatree. * Warning, this is expensive, use sparingly! * @param writer the output to write to */ public synchronized void dumpWatches(PrintWriter writer, boolean byPath) { dataWatches.dumpWatches(writer, byPath); } /** * Returns a watch report. * * @return watch report * @see WatchesReport */ public synchronized WatchesReport getWatches() { return dataWatches.getWatches(); } /** * Returns a watch report by path. * * @return watch report * @see WatchesPathReport */ public synchronized WatchesPathReport getWatchesByPath() { return dataWatches.getWatchesByPath(); } /** * Returns a watch summary. * * @return watch summary * @see WatchesSummary */ public synchronized WatchesSummary getWatchesSummary() { return dataWatches.getWatchesSummary(); } /** * Write a text dump of all the ephemerals in the datatree. * @param writer the output to write to */ public void dumpEphemerals(PrintWriter writer) { writer.println("Sessions with Ephemerals (" + ephemerals.keySet().size() + "):"); for (Entry> entry : ephemerals.entrySet()) { writer.print("0x" + Long.toHexString(entry.getKey())); writer.println(":"); Set tmp = entry.getValue(); if (tmp != null) { synchronized (tmp) { for (String path : tmp) { writer.println("\t" + path); } } } } } public void shutdownWatcher() { dataWatches.shutdown(); childWatches.shutdown(); } /** * Returns a mapping of session ID to ephemeral znodes. * * @return map of session ID to sets of ephemeral znodes */ public Map> getEphemerals() { Map> ephemeralsCopy = new HashMap<>(); for (Entry> e : ephemerals.entrySet()) { synchronized (e.getValue()) { ephemeralsCopy.put(e.getKey(), new HashSet<>(e.getValue())); } } return ephemeralsCopy; } public void removeCnxn(Watcher watcher) { dataWatches.removeWatcher(watcher); childWatches.removeWatcher(watcher); } public void setWatches(long relativeZxid, List dataWatches, List existWatches, List childWatches, List persistentWatches, List persistentRecursiveWatches, Watcher watcher) { for (String path : dataWatches) { DataNode node = getNode(path); if (node == null) { watcher.process(new WatchedEvent(EventType.NodeDeleted, KeeperState.SyncConnected, path)); } else if (node.stat.getMzxid() > relativeZxid) { watcher.process(new WatchedEvent(EventType.NodeDataChanged, KeeperState.SyncConnected, path)); } else { this.dataWatches.addWatch(path, watcher); } } for (String path : existWatches) { DataNode node = getNode(path); if (node != null) { watcher.process(new WatchedEvent(EventType.NodeCreated, KeeperState.SyncConnected, path)); } else { this.dataWatches.addWatch(path, watcher); } } for (String path : childWatches) { DataNode node = getNode(path); if (node == null) { watcher.process(new WatchedEvent(EventType.NodeDeleted, KeeperState.SyncConnected, path)); } else if (node.stat.getPzxid() > relativeZxid) { watcher.process(new WatchedEvent(EventType.NodeChildrenChanged, KeeperState.SyncConnected, path)); } else { this.childWatches.addWatch(path, watcher); } } for (String path : persistentWatches) { this.childWatches.addWatch(path, watcher, WatcherMode.PERSISTENT); this.dataWatches.addWatch(path, watcher, WatcherMode.PERSISTENT); } for (String path : persistentRecursiveWatches) { this.dataWatches.addWatch(path, watcher, WatcherMode.PERSISTENT_RECURSIVE); } } /** * This method sets the Cversion and Pzxid for the specified node to the * values passed as arguments. The values are modified only if newCversion * is greater than the current Cversion. A NoNodeException is thrown if * a znode for the specified path is not found. * * @param path * Full path to the znode whose Cversion needs to be modified. * A "/" at the end of the path is ignored. * @param newCversion * Value to be assigned to Cversion * @param zxid * Value to be assigned to Pzxid * @throws NoNodeException * If znode not found. **/ public void setCversionPzxid(String path, int newCversion, long zxid) throws NoNodeException { if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } DataNode node = nodes.get(path); if (node == null) { throw new NoNodeException(path); } synchronized (node) { if (newCversion == -1) { newCversion = node.stat.getCversion() + 1; } if (newCversion > node.stat.getCversion()) { nodes.preChange(path, node); node.stat.setCversion(newCversion); node.stat.setPzxid(zxid); nodes.postChange(path, node); } } } public boolean containsWatcher(String path, WatcherType type, Watcher watcher) { boolean containsWatcher = false; switch (type) { case Children: containsWatcher = this.childWatches.containsWatcher(path, watcher, WatcherMode.STANDARD); break; case Data: containsWatcher = this.dataWatches.containsWatcher(path, watcher, WatcherMode.STANDARD); break; case Persistent: containsWatcher = this.dataWatches.containsWatcher(path, watcher, WatcherMode.PERSISTENT); break; case PersistentRecursive: containsWatcher = this.dataWatches.containsWatcher(path, watcher, WatcherMode.PERSISTENT_RECURSIVE); break; case Any: if (this.childWatches.containsWatcher(path, watcher, null)) { containsWatcher = true; } else if (this.dataWatches.containsWatcher(path, watcher, null)) { containsWatcher = true; } break; } return containsWatcher; } public boolean removeWatch(String path, WatcherType type, Watcher watcher) { boolean removed = false; switch (type) { case Children: removed = this.childWatches.removeWatcher(path, watcher, WatcherMode.STANDARD); break; case Data: removed = this.dataWatches.removeWatcher(path, watcher, WatcherMode.STANDARD); break; case Persistent: if (this.childWatches.removeWatcher(path, watcher, WatcherMode.PERSISTENT)) { removed = true; } if (this.dataWatches.removeWatcher(path, watcher, WatcherMode.PERSISTENT)) { removed = true; } break; case PersistentRecursive: removed = this.dataWatches.removeWatcher(path, watcher, WatcherMode.PERSISTENT_RECURSIVE); break; case Any: if (this.childWatches.removeWatcher(path, watcher, null)) { removed = true; } if (this.dataWatches.removeWatcher(path, watcher, null)) { removed = true; } break; } return removed; } // visible for testing public ReferenceCountedACLCache getReferenceCountedAclCache() { return aclCache; } private void updateReadStat(String path, long bytes) { final String namespace = PathUtils.getTopNamespace(path); if (namespace == null) { return; } long totalBytes = path.length() + bytes + STAT_OVERHEAD_BYTES; ServerMetrics.getMetrics().READ_PER_NAMESPACE.add(namespace, totalBytes); } private void updateWriteStat(String path, long bytes) { final String namespace = PathUtils.getTopNamespace(path); if (namespace == null) { return; } ServerMetrics.getMetrics().WRITE_PER_NAMESPACE.add(namespace, path.length() + bytes); } /** * Add the digest to the historical list, and update the latest zxid digest. */ private void logZxidDigest(long zxid, long digest) { ZxidDigest zxidDigest = new ZxidDigest(zxid, digestCalculator.getDigestVersion(), digest); lastProcessedZxidDigest = zxidDigest; if (zxidDigest.zxid % DIGEST_LOG_INTERVAL == 0) { synchronized (digestLog) { digestLog.add(zxidDigest); if (digestLog.size() > DIGEST_LOG_LIMIT) { digestLog.poll(); } } } } /** * Serializing the digest to snapshot, this is done after the data tree * is being serialized, so when we replay the txns, and it hits this zxid * we know we should be in a non-fuzzy state, and have the same digest. * * @param oa the output stream to write to * @return true if the digest is serialized successfully */ public boolean serializeZxidDigest(OutputArchive oa) throws IOException { if (!ZooKeeperServer.isDigestEnabled()) { return false; } ZxidDigest zxidDigest = lastProcessedZxidDigest; if (zxidDigest == null) { // write an empty digest zxidDigest = new ZxidDigest(); } zxidDigest.serialize(oa); return true; } /** * Deserializing the zxid digest from the input stream and update the * digestFromLoadedSnapshot. * * @param ia the input stream to read from * @param startZxidOfSnapshot the zxid of snapshot file * @return the true if it deserialized successfully */ public boolean deserializeZxidDigest(InputArchive ia, long startZxidOfSnapshot) throws IOException { if (!ZooKeeperServer.isDigestEnabled()) { return false; } try { ZxidDigest zxidDigest = new ZxidDigest(); zxidDigest.deserialize(ia); if (zxidDigest.zxid > 0) { digestFromLoadedSnapshot = zxidDigest; LOG.info("The digest in the snapshot has digest version of {}, " + "with zxid as 0x{}, and digest value as {}", digestFromLoadedSnapshot.digestVersion, Long.toHexString(digestFromLoadedSnapshot.zxid), digestFromLoadedSnapshot.digest); } else { digestFromLoadedSnapshot = null; LOG.info("The digest value is empty in snapshot"); } // There is possibility that the start zxid of a snapshot might // be larger than the digest zxid in snapshot. // // Known cases: // // The new leader set the last processed zxid to be the new // epoch + 0, which is not mapping to any txn, and it uses // this to take snapshot, which is possible if we don't // clean database before switching to LOOKING. In this case // the currentZxidDigest will be the zxid of last epoch, and // it's smaller than the zxid of the snapshot file. // // It's safe to reset the targetZxidDigest to null and start // to compare digest when replaying the first txn, since it's // a non-fuzzy snapshot. if (digestFromLoadedSnapshot != null && digestFromLoadedSnapshot.zxid < startZxidOfSnapshot) { LOG.info("The zxid of snapshot digest 0x{} is smaller " + "than the known snapshot highest zxid, the snapshot " + "started with zxid 0x{}. It will be invalid to use " + "this snapshot digest associated with this zxid, will " + "ignore comparing it.", Long.toHexString(digestFromLoadedSnapshot.zxid), Long.toHexString(startZxidOfSnapshot)); digestFromLoadedSnapshot = null; } return true; } catch (EOFException e) { LOG.warn("Got EOF exception while reading the digest, likely due to the reading an older snapshot."); return false; } } /** * Serializes the lastProcessedZxid so we can get it from snapshot instead the snapshot file name. * This is needed for performing snapshot and restore via admin server commands. * * @param oa the output stream to write to * @return true if the lastProcessedZxid is serialized successfully, otherwise false * @throws IOException if there is an I/O error */ public boolean serializeLastProcessedZxid(final OutputArchive oa) throws IOException { if (!ZooKeeperServer.isSerializeLastProcessedZxidEnabled()) { return false; } oa.writeLong(lastProcessedZxid, "lastZxid"); return true; } /** * Deserializes the lastProcessedZxid from the input stream and updates the lastProcessedZxid field. * * @param ia the input stream to read from * @return true if lastProcessedZxid is deserialized successfully, otherwise false * @throws IOException if there is an I/O error */ public boolean deserializeLastProcessedZxid(final InputArchive ia) throws IOException { if (!ZooKeeperServer.isSerializeLastProcessedZxidEnabled()) { return false; } try { lastProcessedZxid = ia.readLong("lastZxid"); } catch (final EOFException e) { LOG.warn("Got EOFException while reading the last processed zxid, likely due to reading an older snapshot."); return false; } return true; } /** * Compares the actual tree's digest with that in the snapshot. * Resets digestFromLoadedSnapshot after comparison. * * @param zxid zxid */ public void compareSnapshotDigests(long zxid) { if (zxid == digestFromLoadedSnapshot.zxid) { if (digestCalculator.getDigestVersion() != digestFromLoadedSnapshot.digestVersion) { LOG.info( "Digest version changed, local: {}, new: {}, skip comparing digest now.", digestFromLoadedSnapshot.digestVersion, digestCalculator.getDigestVersion()); digestFromLoadedSnapshot = null; return; } if (getTreeDigest() != digestFromLoadedSnapshot.getDigest()) { reportDigestMismatch(zxid); } digestFromLoadedSnapshot = null; } else if (digestFromLoadedSnapshot.zxid != 0 && zxid > digestFromLoadedSnapshot.zxid) { RATE_LOGGER.rateLimitLog( "The txn 0x{} of snapshot digest does not exist.", Long.toHexString(digestFromLoadedSnapshot.zxid)); } } /** * Compares the digest of the tree with the digest present in transaction digest. * If there is any error, logs and alerts the watchers. * * @param header transaction header being applied * @param txn transaction * @param digest transaction digest * * @return false if digest in the txn doesn't match what we have now in the data tree */ public boolean compareDigest(TxnHeader header, Record txn, TxnDigest digest) { long zxid = header.getZxid(); if (!ZooKeeperServer.isDigestEnabled() || digest == null) { return true; } // do not compare digest if we're still in fuzzy state if (digestFromLoadedSnapshot != null) { return true; } // do not compare digest if there is digest version change if (digestCalculator.getDigestVersion() != digest.getVersion()) { RATE_LOGGER.rateLimitLog("Digest version not the same on zxid.", String.valueOf(zxid)); return true; } long logDigest = digest.getTreeDigest(); long actualDigest = getTreeDigest(); if (logDigest != actualDigest) { reportDigestMismatch(zxid); LOG.debug("Digest in log: {}, actual tree: {}", logDigest, actualDigest); if (firstMismatchTxn) { LOG.error( "First digest mismatch on txn: {}, {}, expected digest is {}, actual digest is {}, ", header, txn, digest, actualDigest); firstMismatchTxn = false; } return false; } else { RATE_LOGGER.flush(); LOG.debug( "Digests are matching for Zxid: {}, Digest in log and actual tree: {}", Long.toHexString(zxid), logDigest); return true; } } /** * Reports any mismatch in the transaction digest. * @param zxid zxid for which the error is being reported. */ public void reportDigestMismatch(long zxid) { ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT.add(1); RATE_LOGGER.rateLimitLog("Digests are not matching. Value is Zxid.", String.valueOf(zxid)); for (DigestWatcher watcher : digestWatchers) { watcher.process(zxid); } } public long getTreeDigest() { return nodes.getDigest(); } public ZxidDigest getLastProcessedZxidDigest() { return lastProcessedZxidDigest; } public ZxidDigest getDigestFromLoadedSnapshot() { return digestFromLoadedSnapshot; } /** * Add digest mismatch event handler. * * @param digestWatcher the handler to add */ public void addDigestWatcher(DigestWatcher digestWatcher) { digestWatchers.add(digestWatcher); } /** * Return all the digests in the historical digest list. */ public List getDigestLog() { synchronized (digestLog) { // Return a copy of current digest log return new LinkedList<>(digestLog); } } /** * A helper class to maintain the digest meta associated with specific zxid. */ public class ZxidDigest { long zxid; // the digest value associated with this zxid long digest; // the version when the digest was calculated int digestVersion; ZxidDigest() { this(0, digestCalculator.getDigestVersion(), 0); } ZxidDigest(long zxid, int digestVersion, long digest) { this.zxid = zxid; this.digestVersion = digestVersion; this.digest = digest; } public void serialize(OutputArchive oa) throws IOException { oa.writeLong(zxid, "zxid"); oa.writeInt(digestVersion, "digestVersion"); oa.writeLong(digest, "digest"); } public void deserialize(InputArchive ia) throws IOException { zxid = ia.readLong("zxid"); digestVersion = ia.readInt("digestVersion"); // the old version is using hex string as the digest if (digestVersion < 2) { String d = ia.readString("digest"); if (d != null) { digest = Long.parseLong(d, 16); } } else { digest = ia.readLong("digest"); } } public long getZxid() { return zxid; } public int getDigestVersion() { return digestVersion; } public long getDigest() { return digest; } } /** * Create a node stat from the given params. * * @param zxid the zxid associated with the txn * @param time the time when the txn is created * @param ephemeralOwner the owner if the node is an ephemeral * @return the stat */ public static StatPersisted createStat(long zxid, long time, long ephemeralOwner) { StatPersisted stat = new StatPersisted(); stat.setCtime(time); stat.setMtime(time); stat.setCzxid(zxid); stat.setMzxid(zxid); stat.setPzxid(zxid); stat.setVersion(0); stat.setAversion(0); stat.setEphemeralOwner(ephemeralOwner); return stat; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTreeBean.java0100644 0000000 0000000 00000003317 15051152474 033726 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.apache.zookeeper.jmx.ZKMBeanInfo; /** * This class implements the data tree MBean. */ public class DataTreeBean implements DataTreeMXBean, ZKMBeanInfo { DataTree dataTree; public DataTreeBean(org.apache.zookeeper.server.DataTree dataTree) { this.dataTree = dataTree; } public int getNodeCount() { return dataTree.getNodeCount(); } public long approximateDataSize() { return dataTree.cachedApproximateDataSize(); } public int countEphemerals() { return dataTree.getEphemeralsCount(); } public int getWatchCount() { return dataTree.getWatchCount(); } public String getName() { return "InMemoryDataTree"; } public boolean isHidden() { return false; } public String getLastZxid() { return "0x" + Long.toHexString(dataTree.lastProcessedZxid); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Da0100644 0000000 0000000 00000000157 15051152474 032621 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTreeMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DataTreeMXBean.jav0100644 0000000 0000000 00000002641 15051152474 034031 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * Zookeeper data tree MBean. */ public interface DataTreeMXBean { /** * @return number of znodes in the data tree. */ int getNodeCount(); /** * @return the most recent zxid processed by the data tree. */ String getLastZxid(); /** * @return number of watches set. */ int getWatchCount(); /** * @return data tree size in bytes. The size includes the znode path and * its value. */ long approximateDataSize(); /** * @return number of ephemeral nodes in the data tree */ int countEphemerals(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Da0100644 0000000 0000000 00000000166 15051152474 032621 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DatadirCleanupManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DatadirCleanupMana0100644 0000000 0000000 00000013237 15051152474 034206 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.File; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class manages the cleanup of snapshots and corresponding transaction * logs by scheduling the auto purge task with the specified * 'autopurge.purgeInterval'. It keeps the most recent * 'autopurge.snapRetainCount' number of snapshots and corresponding transaction * logs. */ public class DatadirCleanupManager { private static final Logger LOG = LoggerFactory.getLogger(DatadirCleanupManager.class); /** * Status of the dataDir purge task */ public enum PurgeTaskStatus { NOT_STARTED, STARTED, COMPLETED } private PurgeTaskStatus purgeTaskStatus = PurgeTaskStatus.NOT_STARTED; private final File snapDir; private final File dataLogDir; private final int snapRetainCount; private final int purgeInterval; private Timer timer; /** * Constructor of DatadirCleanupManager. It takes the parameters to schedule * the purge task. * * @param snapDir * snapshot directory * @param dataLogDir * transaction log directory * @param snapRetainCount * number of snapshots to be retained after purge * @param purgeInterval * purge interval in hours */ public DatadirCleanupManager(File snapDir, File dataLogDir, int snapRetainCount, int purgeInterval) { this.snapDir = snapDir; this.dataLogDir = dataLogDir; this.snapRetainCount = snapRetainCount; this.purgeInterval = purgeInterval; LOG.info("autopurge.snapRetainCount set to {}", snapRetainCount); LOG.info("autopurge.purgeInterval set to {}", purgeInterval); } /** * Validates the purge configuration and schedules the purge task. Purge * task keeps the most recent snapRetainCount number of * snapshots and deletes the remaining for every purgeInterval * hour(s). *

* purgeInterval of 0 or * negative integer will not schedule the purge task. *

* * @see PurgeTxnLog#purge(File, File, int) */ public void start() { if (PurgeTaskStatus.STARTED == purgeTaskStatus) { LOG.warn("Purge task is already running."); return; } // Don't schedule the purge task with zero or negative purge interval. if (purgeInterval <= 0) { LOG.info("Purge task is not scheduled."); return; } timer = new Timer("PurgeTask", true); TimerTask task = new PurgeTask(dataLogDir, snapDir, snapRetainCount); timer.scheduleAtFixedRate(task, 0, TimeUnit.HOURS.toMillis(purgeInterval)); purgeTaskStatus = PurgeTaskStatus.STARTED; } /** * Shutdown the purge task. */ public void shutdown() { if (PurgeTaskStatus.STARTED == purgeTaskStatus) { LOG.info("Shutting down purge task."); timer.cancel(); purgeTaskStatus = PurgeTaskStatus.COMPLETED; } else { LOG.warn("Purge task not started. Ignoring shutdown!"); } } static class PurgeTask extends TimerTask { private File logsDir; private File snapsDir; private int snapRetainCount; public PurgeTask(File dataDir, File snapDir, int count) { logsDir = dataDir; snapsDir = snapDir; snapRetainCount = count; } @Override public void run() { LOG.info("Purge task started."); try { PurgeTxnLog.purge(logsDir, snapsDir, snapRetainCount); } catch (Exception e) { LOG.error("Error occurred while purging.", e); } LOG.info("Purge task completed."); } } /** * Returns the status of the purge task. * * @return the status of the purge task */ public PurgeTaskStatus getPurgeTaskStatus() { return purgeTaskStatus; } /** * Returns the snapshot directory. * * @return the snapshot directory. */ public File getSnapDir() { return snapDir; } /** * Returns transaction log directory. * * @return the transaction log directory. */ public File getDataLogDir() { return dataLogDir; } /** * Returns purge interval in hours. * * @return the purge interval in hours. */ public int getPurgeInterval() { return purgeInterval; } /** * Returns the number of snapshots to be retained after purge. * * @return the number of snapshots to be retained after purge. */ public int getSnapRetainCount() { return snapRetainCount; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Di0100644 0000000 0000000 00000000161 15051152474 032624 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DigestCalculator.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DigestCalculator.j0100644 0000000 0000000 00000010102 15051152474 034176 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.nio.ByteBuffer; import java.util.zip.CRC32; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.StatPersisted; /** * Defines how to calculate the digest for a given node. */ public class DigestCalculator { // The hardcoded digest version, should bump up this version whenever // we changed the digest method or fields. private static final int DIGEST_VERSION = 2; /** * Calculate the digest based on the given params. * * Besides the path and data, the following stat fields are included in * the digest calculation: * * - long czxid 8 bytes * - long mzxid 8 bytes * - long pzxid 8 bytes * - long ctime 8 bytes * - long mtime 8 bytes * - int version 4 bytes * - int cversion 4 bytes * - int aversion 4 bytes * - long ephemeralOwner 8 bytes * * @param path the path of the node * @param data the data of the node * @param stat the stat associated with the node * @return the digest calculated from the given params */ long calculateDigest(String path, byte[] data, StatPersisted stat) { if (!ZooKeeperServer.isDigestEnabled()) { return 0; } // Quota nodes are updated locally, there is inconsistent issue // when we tried to release digest feature at the beginning. // // Instead of taking time to fix that, we decided to disable digest // check for all the nodes under /zookeeper/ first. // // We can enable this after fixing that inconsistent problem. The // digest version in the protocol enables us to change the digest // calculation without disrupting the system. if (path.startsWith(ZooDefs.ZOOKEEPER_NODE_SUBTREE)) { return 0; } // "" and "/" are aliases to each other, in DataTree when adding child // under "/", it will use "" as the path, but when set data or change // ACL on "/", it will use "/" as the path. Always mapping "/" to "" // to avoid mismatch. if (path.equals("/")) { path = ""; } // total = 8 * 6 + 4 * 3 = 60 bytes byte[] b = new byte[60]; ByteBuffer bb = ByteBuffer.wrap(b); bb.putLong(stat.getCzxid()); bb.putLong(stat.getMzxid()); bb.putLong(stat.getPzxid()); bb.putLong(stat.getCtime()); bb.putLong(stat.getMtime()); bb.putInt(stat.getVersion()); bb.putInt(stat.getCversion()); bb.putInt(stat.getAversion()); bb.putLong(stat.getEphemeralOwner()); CRC32 crc = new CRC32(); crc.update(path.getBytes()); if (data != null) { crc.update(data); } crc.update(b); return crc.getValue(); } /** * Calculate the digest based on the given path and data node. */ long calculateDigest(String path, DataNode node) { if (!node.isDigestCached()) { node.setDigest(calculateDigest(path, node.getData(), node.stat)); node.setDigestCached(true); } return node.getDigest(); } /** * Returns with the current digest version. */ int getDigestVersion() { return DIGEST_VERSION; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/DumbWatcher.java0100644 0000000 0000000 00000006642 15051152474 033660 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.util.List; import org.apache.jute.Record; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.ReplyHeader; /** * A empty watcher implementation used in bench and unit test. */ public class DumbWatcher extends ServerCnxn { private long sessionId; private String mostRecentPath; private Event.EventType mostRecentEventType; private long mostRecentZxid = WatchedEvent.NO_ZXID; public DumbWatcher() { this(0); } public DumbWatcher(long sessionId) { super(null); this.sessionId = sessionId; } @Override void setSessionTimeout(int sessionTimeout) { } @Override public void process(WatchedEvent event, List znodeAcl) { mostRecentEventType = event.getType(); mostRecentZxid = event.getZxid(); mostRecentPath = event.getPath(); } public String getMostRecentPath() { return mostRecentPath; } public Event.EventType getMostRecentEventType() { return mostRecentEventType; } public long getMostRecentZxid() { return mostRecentZxid; } @Override int getSessionTimeout() { return 0; } @Override public void close(DisconnectReason reason) { } @Override public int sendResponse(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) throws IOException { return 0; } @Override public void sendCloseSession() { } @Override public long getSessionId() { return sessionId; } @Override void setSessionId(long sessionId) { } @Override void sendBuffer(ByteBuffer... closeConn) { } @Override void enableRecv() { } @Override void disableRecv(boolean waitDisableRecv) { } @Override protected ServerStats serverStats() { return null; } @Override public long getOutstandingRequests() { return 0; } @Override public InetSocketAddress getRemoteSocketAddress() { return null; } @Override public int getInterestOps() { return 0; } @Override public boolean isSecure() { return false; } @Override public Certificate[] getClientCertificateChain() { return null; } @Override public void setClientCertificateChain(Certificate[] chain) { } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Ep0100644 0000000 0000000 00000000156 15051152474 032640 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/EphemeralType.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/EphemeralType.java0100644 0000000 0000000 00000021146 15051152474 034213 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.CreateMode; /** *

* Abstraction that interprets the ephemeralOwner field of a ZNode. Originally, * the ephemeralOwner noted that a ZNode is ephemeral and which session created the node. * Through an optional system property (zookeeper.extendedTypesEnabled) "extended" * features such as TTL Nodes can be enabled. Special bits of the ephemeralOwner are used to * denote which feature is enabled and the remaining bits of the ephemeralOwner are feature * specific. *

*

*

* When the system property zookeeper.extendedTypesEnabled is true, extended types * are enabled. An extended ephemeralOwner is defined as an ephemeralOwner whose high 8 bits are * set (0xff00000000000000L). The two bytes that follow the high 8 bits are * used to denote which extended feature the ephemeralOwner represents. The remaining 5 bytes are * used by the feature for whatever purpose is needed *

*

*

* Currently, the only extended feature is TTL Nodes. It is denoted by the extended feature value of 0. * i.e. for TTL Nodes, the ephemeralOwner has the high byte set to 0xff and the next 2 bytes are 0 followed * by 5 bytes that represent the TTL value in milliseconds. So, an ephemeralOwner with a TTL value of 1 * millisecond is: 0xff00000000000001. *

*

*

* To add new extended features: a) Add a new name to the enum, b) define a constant EXTENDED_BIT_xxxx that's next * in line (after TTLs, that would be 0x0001), c) add a mapping to the extendedFeatureMap via the static * initializer *

*

*

* NOTE: "Container" nodes technically are extended types but as it was implemented before this feature they are * denoted specially. An ephemeral owner with only the high bit set (0x8000000000000000L) is by definition * a container node (irrespective of whether or not extended types are enabled). *

*/ public enum EphemeralType { /** * Not ephemeral */ VOID, /** * Standard, pre-3.5.x EPHEMERAL */ NORMAL, /** * Container node */ CONTAINER, /** * TTL node */ TTL() { @Override public long maxValue() { return EXTENDED_FEATURE_VALUE_MASK; // 12725 days, about 34 years } @Override public long toEphemeralOwner(long ttl) { if ((ttl > TTL.maxValue()) || (ttl <= 0)) { throw new IllegalArgumentException("ttl must be positive and cannot be larger than: " + TTL.maxValue()); } //noinspection PointlessBitwiseExpression return EXTENDED_MASK | EXTENDED_BIT_TTL | ttl; // TTL_RESERVED_BIT is actually zero - but it serves to document that the proper extended bit needs to be set } @Override public long getValue(long ephemeralOwner) { return getExtendedFeatureValue(ephemeralOwner); } }; /** * For types that support it, the maximum extended value * * @return 0 or max */ public long maxValue() { return 0; } /** * For types that support it, convert a value to an extended ephemeral owner * * @return 0 or extended ephemeral owner */ public long toEphemeralOwner(long value) { return 0; } /** * For types that support it, return the extended value from an extended ephemeral owner * * @return 0 or extended value */ public long getValue(long ephemeralOwner) { return 0; } public static final long CONTAINER_EPHEMERAL_OWNER = Long.MIN_VALUE; public static final long MAX_EXTENDED_SERVER_ID = 0xfe; // 254 private static final long EXTENDED_MASK = 0xff00000000000000L; private static final long EXTENDED_BIT_TTL = 0x0000; private static final long RESERVED_BITS_MASK = 0x00ffff0000000000L; private static final long RESERVED_BITS_SHIFT = 40; private static final Map extendedFeatureMap; static { Map map = new HashMap<>(); map.put(EXTENDED_BIT_TTL, TTL); extendedFeatureMap = Collections.unmodifiableMap(map); } private static final long EXTENDED_FEATURE_VALUE_MASK = ~(EXTENDED_MASK | RESERVED_BITS_MASK); // Visible for testing static final String EXTENDED_TYPES_ENABLED_PROPERTY = "zookeeper.extendedTypesEnabled"; static final String TTL_3_5_3_EMULATION_PROPERTY = "zookeeper.emulate353TTLNodes"; /** * Return true if extended ephemeral types are enabled * * @return true/false */ public static boolean extendedEphemeralTypesEnabled() { return Boolean.getBoolean(EXTENDED_TYPES_ENABLED_PROPERTY); } /** * Convert a ZNode ephemeral owner to an ephemeral type. If extended types are not * enabled, VOID or NORMAL is always returned * * @param ephemeralOwner the ZNode's ephemeral owner * @return type */ public static EphemeralType get(long ephemeralOwner) { if (extendedEphemeralTypesEnabled()) { if (Boolean.getBoolean(TTL_3_5_3_EMULATION_PROPERTY)) { if (EphemeralTypeEmulate353.get(ephemeralOwner) == EphemeralTypeEmulate353.TTL) { return TTL; } } if ((ephemeralOwner & EXTENDED_MASK) == EXTENDED_MASK) { long extendedFeatureBit = getExtendedFeatureBit(ephemeralOwner); EphemeralType ephemeralType = extendedFeatureMap.get(extendedFeatureBit); if (ephemeralType == null) { throw new IllegalArgumentException(String.format("Invalid ephemeralOwner. [%s]", Long.toHexString(ephemeralOwner))); } return ephemeralType; } } if (ephemeralOwner == CONTAINER_EPHEMERAL_OWNER) { return CONTAINER; } return (ephemeralOwner == 0) ? VOID : NORMAL; } /** * Make sure the given server ID is compatible with the current extended ephemeral setting * * @param serverId Server ID * @throws RuntimeException extendedTypesEnabled is true but Server ID is too large */ public static void validateServerId(long serverId) { // TODO: in the future, serverId should be validated for all cases, not just the extendedEphemeralTypesEnabled case // TODO: however, for now, it would be too disruptive if (extendedEphemeralTypesEnabled()) { if (serverId > EphemeralType.MAX_EXTENDED_SERVER_ID) { throw new RuntimeException( "extendedTypesEnabled is true but Server ID is too large. Cannot be larger than " + EphemeralType.MAX_EXTENDED_SERVER_ID); } } } /** * Utility to validate a create mode and a ttl * * @param mode create mode * @param ttl ttl * @throws IllegalArgumentException if the ttl is not valid for the mode */ @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT", justification = "toEphemeralOwner may throw IllegalArgumentException") public static void validateTTL(CreateMode mode, long ttl) { if (mode.isTTL()) { TTL.toEphemeralOwner(ttl); } else if (ttl >= 0) { throw new IllegalArgumentException("ttl not valid for mode: " + mode); } } private static long getExtendedFeatureBit(long ephemeralOwner) { return (ephemeralOwner & RESERVED_BITS_MASK) >> RESERVED_BITS_SHIFT; } private static long getExtendedFeatureValue(long ephemeralOwner) { return ephemeralOwner & EXTENDED_FEATURE_VALUE_MASK; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Ep0100644 0000000 0000000 00000000170 15051152474 032634 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/EphemeralTypeEmulate353.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/EphemeralTypeEmula0100644 0000000 0000000 00000004367 15051152474 034265 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * See https://issues.apache.org/jira/browse/ZOOKEEPER-2901 * * version 3.5.3 introduced bugs associated with how TTL nodes were implemented. version 3.5.4 * fixes the problems but makes TTL nodes created in 3.5.3 invalid. EphemeralTypeEmulate353 is a copy * of the old - bad - implementation that is provided as a workaround. {@link EphemeralType#TTL_3_5_3_EMULATION_PROPERTY} * can be used to emulate support of the badly specified TTL nodes. */ public enum EphemeralTypeEmulate353 { /** * Not ephemeral */ VOID, /** * Standard, pre-3.5.x EPHEMERAL */ NORMAL, /** * Container node */ CONTAINER, /** * TTL node */ TTL; public static final long CONTAINER_EPHEMERAL_OWNER = Long.MIN_VALUE; public static final long MAX_TTL = 0x0fffffffffffffffL; public static final long TTL_MASK = 0x8000000000000000L; public static EphemeralTypeEmulate353 get(long ephemeralOwner) { if (ephemeralOwner == CONTAINER_EPHEMERAL_OWNER) { return CONTAINER; } if (ephemeralOwner < 0) { return TTL; } return (ephemeralOwner == 0) ? VOID : NORMAL; } public static long ttlToEphemeralOwner(long ttl) { if ((ttl > MAX_TTL) || (ttl <= 0)) { throw new IllegalArgumentException("ttl must be positive and cannot be larger than: " + MAX_TTL); } return TTL_MASK | ttl; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ExitCode.java0100644 0000000 0000000 00000003550 15051152474 033152 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; /** * Exit code used to exit server */ public enum ExitCode { /** Execution finished normally */ EXECUTION_FINISHED(0), /** Unexpected errors like IO Exceptions */ UNEXPECTED_ERROR(1), /** Invalid arguments during invocations */ INVALID_INVOCATION(2), /** Cannot access datadir when trying to replicate server */ UNABLE_TO_ACCESS_DATADIR(3), /** Unable to start admin server at ZooKeeper startup */ ERROR_STARTING_ADMIN_SERVER(4), /** Severe error during snapshot IO */ TXNLOG_ERROR_TAKING_SNAPSHOT(10), /** zxid from COMMIT does not match the one from pendingTxns queue */ UNMATCHED_TXN_COMMIT(12), /** Unexpected packet from leader, or unable to truncate log on Leader.TRUNC */ QUORUM_PACKET_ERROR(13), /** Unable to bind to the quorum (election) port after multiple retry */ UNABLE_TO_BIND_QUORUM_PORT(14); private final int value; ExitCode(final int newValue) { value = newValue; } public int getValue() { return value; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ExpiryQueue.java0100644 0000000 0000000 00000015203 15051152474 033731 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.common.Time; /** * ExpiryQueue tracks elements in time sorted fixed duration buckets. * It's used by SessionTrackerImpl to expire sessions and NIOServerCnxnFactory * to expire connections. */ public class ExpiryQueue { private final ConcurrentHashMap elemMap = new ConcurrentHashMap<>(); /** * The maximum number of buckets is equal to max timeout/expirationInterval, * so the expirationInterval should not be too small compared to the * max timeout that this expiry queue needs to maintain. */ private final ConcurrentHashMap> expiryMap = new ConcurrentHashMap<>(); private final AtomicLong nextExpirationTime = new AtomicLong(); private final int expirationInterval; public ExpiryQueue(int expirationInterval) { this.expirationInterval = expirationInterval; nextExpirationTime.set(roundToNextInterval(Time.currentElapsedTime())); } private long roundToNextInterval(long time) { return (time / expirationInterval + 1) * expirationInterval; } /** * Removes element from the queue. * @param elem element to remove * @return time at which the element was set to expire, or null if * it wasn't present */ public Long remove(E elem) { Long expiryTime = elemMap.remove(elem); if (expiryTime != null) { Set set = expiryMap.get(expiryTime); if (set != null) { set.remove(elem); // We don't need to worry about removing empty sets, // they'll eventually be removed when they expire. } } return expiryTime; } /** * Adds or updates expiration time for element in queue, rounding the * timeout to the expiry interval bucketed used by this queue. * @param elem element to add/update * @param timeout timout in milliseconds * @return time at which the element is now set to expire if * changed, or null if unchanged */ public Long update(E elem, int timeout) { Long prevExpiryTime = elemMap.get(elem); long now = Time.currentElapsedTime(); Long newExpiryTime = roundToNextInterval(now + timeout); if (newExpiryTime.equals(prevExpiryTime)) { // No change, so nothing to update return null; } // First add the elem to the new expiry time bucket in expiryMap. Set set = expiryMap.get(newExpiryTime); if (set == null) { // Construct a ConcurrentHashSet using a ConcurrentHashMap set = Collections.newSetFromMap(new ConcurrentHashMap<>()); // Put the new set in the map, but only if another thread // hasn't beaten us to it Set existingSet = expiryMap.putIfAbsent(newExpiryTime, set); if (existingSet != null) { set = existingSet; } } set.add(elem); // Map the elem to the new expiry time. If a different previous // mapping was present, clean up the previous expiry bucket. prevExpiryTime = elemMap.put(elem, newExpiryTime); if (prevExpiryTime != null && !newExpiryTime.equals(prevExpiryTime)) { Set prevSet = expiryMap.get(prevExpiryTime); if (prevSet != null) { prevSet.remove(elem); } } return newExpiryTime; } /** * @return milliseconds until next expiration time, or 0 if has already past */ public long getWaitTime() { long now = Time.currentElapsedTime(); long expirationTime = nextExpirationTime.get(); return now < expirationTime ? (expirationTime - now) : 0L; } /** * Remove the next expired set of elements from expireMap. This method needs * to be called frequently enough by checking getWaitTime(), otherwise there * will be a backlog of empty sets queued up in expiryMap. * * @return next set of expired elements, or an empty set if none are * ready */ public Set poll() { long now = Time.currentElapsedTime(); long expirationTime = nextExpirationTime.get(); if (now < expirationTime) { return Collections.emptySet(); } Set set = null; long newExpirationTime = expirationTime + expirationInterval; if (nextExpirationTime.compareAndSet(expirationTime, newExpirationTime)) { set = expiryMap.remove(expirationTime); } if (set == null) { return Collections.emptySet(); } return set; } public void dump(PrintWriter pwriter) { pwriter.print("Sets ("); pwriter.print(expiryMap.size()); pwriter.print(")/("); pwriter.print(elemMap.size()); pwriter.println("):"); ArrayList keys = new ArrayList<>(expiryMap.keySet()); Collections.sort(keys); for (long time : keys) { Set set = expiryMap.get(time); if (set != null) { pwriter.print(set.size()); pwriter.print(" expire at "); pwriter.print(Time.elapsedTimeToDate(time)); pwriter.println(":"); for (E elem : set) { pwriter.print("\t"); pwriter.println(elem.toString()); } } } } /** * Returns an unmodifiable view of the expiration time -> elements mapping. */ public Map> getExpiryMap() { return Collections.unmodifiableMap(expiryMap); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Fi0100644 0000000 0000000 00000000166 15051152474 032633 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/FinalRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/FinalRequestProces0100644 0000000 0000000 00000073254 15051152474 034314 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Set; import org.apache.jute.Record; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.KeeperException.SessionMovedException; import org.apache.zookeeper.MultiOperationRecord; import org.apache.zookeeper.MultiResponse; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.OpResult.CheckResult; import org.apache.zookeeper.OpResult.CreateResult; import org.apache.zookeeper.OpResult.DeleteResult; import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.OpResult.GetChildrenResult; import org.apache.zookeeper.OpResult.GetDataResult; import org.apache.zookeeper.OpResult.SetDataResult; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.audit.AuditHelper; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.AddWatchRequest; import org.apache.zookeeper.proto.CheckWatchesRequest; import org.apache.zookeeper.proto.Create2Response; import org.apache.zookeeper.proto.CreateResponse; import org.apache.zookeeper.proto.ErrorResponse; import org.apache.zookeeper.proto.ExistsRequest; import org.apache.zookeeper.proto.ExistsResponse; import org.apache.zookeeper.proto.GetACLRequest; import org.apache.zookeeper.proto.GetACLResponse; import org.apache.zookeeper.proto.GetAllChildrenNumberRequest; import org.apache.zookeeper.proto.GetAllChildrenNumberResponse; import org.apache.zookeeper.proto.GetChildren2Request; import org.apache.zookeeper.proto.GetChildren2Response; import org.apache.zookeeper.proto.GetChildrenRequest; import org.apache.zookeeper.proto.GetChildrenResponse; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.proto.GetDataResponse; import org.apache.zookeeper.proto.GetEphemeralsRequest; import org.apache.zookeeper.proto.GetEphemeralsResponse; import org.apache.zookeeper.proto.RemoveWatchesRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.SetACLResponse; import org.apache.zookeeper.proto.SetDataResponse; import org.apache.zookeeper.proto.SetWatches; import org.apache.zookeeper.proto.SetWatches2; import org.apache.zookeeper.proto.SyncRequest; import org.apache.zookeeper.proto.SyncResponse; import org.apache.zookeeper.proto.WhoAmIResponse; import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.quorum.QuorumZooKeeperServer; import org.apache.zookeeper.server.util.AuthUtil; import org.apache.zookeeper.server.util.RequestPathMetricsCollector; import org.apache.zookeeper.txn.ErrorTxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This Request processor actually applies any transaction associated with a * request and services any queries. It is always at the end of a * RequestProcessor chain (hence the name), so it does not have a nextProcessor * member. * * This RequestProcessor counts on ZooKeeperServer to populate the * outstandingRequests member of ZooKeeperServer. */ public class FinalRequestProcessor implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(FinalRequestProcessor.class); private final RequestPathMetricsCollector requestPathMetricsCollector; ZooKeeperServer zks; public FinalRequestProcessor(ZooKeeperServer zks) { this.zks = zks; this.requestPathMetricsCollector = zks.getRequestPathMetricsCollector(); } private ProcessTxnResult applyRequest(Request request) { ProcessTxnResult rc = zks.processTxn(request); // ZOOKEEPER-558: // In some cases the server does not close the connection (e.g., closeconn buffer // was not being queued — ZOOKEEPER-558) properly. This happens, for example, // when the client closes the connection. The server should still close the session, though. // Calling closeSession() after losing the cnxn, results in the client close session response being dropped. if (request.type == OpCode.closeSession && connClosedByClient(request)) { // We need to check if we can close the session id. // Sometimes the corresponding ServerCnxnFactory could be null because // we are just playing diffs from the leader. if (closeSession(zks.serverCnxnFactory, request.sessionId) || closeSession(zks.secureServerCnxnFactory, request.sessionId)) { return rc; } } if (request.getHdr() != null) { /* * Request header is created only by the leader, so this must be * a quorum request. Since we're comparing timestamps across hosts, * this metric may be incorrect. However, it's still a very useful * metric to track in the happy case. If there is clock drift, * the latency can go negative. Note: headers use wall time, not * CLOCK_MONOTONIC. */ long propagationLatency = Time.currentWallTime() - request.getHdr().getTime(); if (propagationLatency >= 0) { ServerMetrics.getMetrics().PROPAGATION_LATENCY.add(propagationLatency); } } return rc; } public void processRequest(Request request) { LOG.debug("Processing request:: {}", request); if (LOG.isTraceEnabled()) { long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK; if (request.type == OpCode.ping) { traceMask = ZooTrace.SERVER_PING_TRACE_MASK; } ZooTrace.logRequest(LOG, traceMask, 'E', request, ""); } ProcessTxnResult rc = null; if (!request.isThrottled()) { rc = applyRequest(request); } if (request.cnxn == null) { return; } ServerCnxn cnxn = request.cnxn; long lastZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid(); String lastOp = "NA"; // Notify ZooKeeperServer that the request has finished so that it can // update any request accounting/throttling limits zks.decInProcess(); zks.requestFinished(request); Code err = Code.OK; Record rsp = null; String path = null; int responseSize = 0; try { if (request.getHdr() != null && request.getHdr().getType() == OpCode.error) { AuditHelper.addAuditLog(request, rc, true); /* * When local session upgrading is disabled, leader will * reject the ephemeral node creation due to session expire. * However, if this is the follower that issue the request, * it will have the correct error code, so we should use that * and report to user */ if (request.getException() != null) { throw request.getException(); } else { throw KeeperException.create(KeeperException.Code.get(((ErrorTxn) request.getTxn()).getErr())); } } KeeperException ke = request.getException(); if (ke instanceof SessionMovedException) { throw ke; } if (ke != null && request.type != OpCode.multi) { throw ke; } LOG.debug("{}", request); if (request.isStale()) { ServerMetrics.getMetrics().STALE_REPLIES.add(1); } if (request.isThrottled()) { throw KeeperException.create(Code.THROTTLEDOP); } AuditHelper.addAuditLog(request, rc); switch (request.type) { case OpCode.ping: { lastOp = "PING"; updateStats(request, lastOp, lastZxid); responseSize = cnxn.sendResponse(new ReplyHeader(ClientCnxn.PING_XID, lastZxid, 0), null, "response"); return; } case OpCode.createSession: { lastOp = "SESS"; updateStats(request, lastOp, lastZxid); zks.finishSessionInit(request.cnxn, true); return; } case OpCode.multi: { lastOp = "MULT"; rsp = new MultiResponse(); for (ProcessTxnResult subTxnResult : rc.multiResult) { OpResult subResult; switch (subTxnResult.type) { case OpCode.check: subResult = new CheckResult(); break; case OpCode.create: subResult = new CreateResult(subTxnResult.path); break; case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: subResult = new CreateResult(subTxnResult.path, subTxnResult.stat); break; case OpCode.delete: case OpCode.deleteContainer: subResult = new DeleteResult(); break; case OpCode.setData: subResult = new SetDataResult(subTxnResult.stat); break; case OpCode.error: subResult = new ErrorResult(subTxnResult.err); if (subTxnResult.err == Code.SESSIONMOVED.intValue()) { throw new SessionMovedException(); } break; default: throw new IOException("Invalid type of op"); } ((MultiResponse) rsp).add(subResult); } break; } case OpCode.multiRead: { lastOp = "MLTR"; MultiOperationRecord multiReadRecord = request.readRequestRecord(MultiOperationRecord::new); rsp = new MultiResponse(); OpResult subResult; for (Op readOp : multiReadRecord) { try { Record rec; switch (readOp.getType()) { case OpCode.getChildren: rec = handleGetChildrenRequest(readOp.toRequestRecord(), cnxn, request.authInfo); subResult = new GetChildrenResult(((GetChildrenResponse) rec).getChildren()); break; case OpCode.getData: rec = handleGetDataRequest(readOp.toRequestRecord(), cnxn, request.authInfo); GetDataResponse gdr = (GetDataResponse) rec; subResult = new GetDataResult(gdr.getData(), gdr.getStat()); break; default: throw new IOException("Invalid type of readOp"); } } catch (KeeperException e) { subResult = new ErrorResult(e.code().intValue()); } ((MultiResponse) rsp).add(subResult); } break; } case OpCode.create: { lastOp = "CREA"; rsp = new CreateResponse(rc.path); err = Code.get(rc.err); requestPathMetricsCollector.registerRequest(request.type, rc.path); break; } case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: { lastOp = "CREA"; rsp = new Create2Response(rc.path, rc.stat); err = Code.get(rc.err); requestPathMetricsCollector.registerRequest(request.type, rc.path); break; } case OpCode.delete: case OpCode.deleteContainer: { lastOp = "DELE"; err = Code.get(rc.err); requestPathMetricsCollector.registerRequest(request.type, rc.path); break; } case OpCode.setData: { lastOp = "SETD"; rsp = new SetDataResponse(rc.stat); err = Code.get(rc.err); requestPathMetricsCollector.registerRequest(request.type, rc.path); break; } case OpCode.reconfig: { lastOp = "RECO"; rsp = new GetDataResponse( ((QuorumZooKeeperServer) zks).self.getQuorumVerifier().toString().getBytes(UTF_8), rc.stat); err = Code.get(rc.err); break; } case OpCode.setACL: { lastOp = "SETA"; rsp = new SetACLResponse(rc.stat); err = Code.get(rc.err); requestPathMetricsCollector.registerRequest(request.type, rc.path); break; } case OpCode.closeSession: { lastOp = "CLOS"; err = Code.get(rc.err); break; } case OpCode.sync: { lastOp = "SYNC"; SyncRequest syncRequest = request.readRequestRecord(SyncRequest::new); rsp = new SyncResponse(syncRequest.getPath()); requestPathMetricsCollector.registerRequest(request.type, syncRequest.getPath()); break; } case OpCode.check: { lastOp = "CHEC"; rsp = new SetDataResponse(rc.stat); err = Code.get(rc.err); break; } case OpCode.exists: { lastOp = "EXIS"; ExistsRequest existsRequest = request.readRequestRecord(ExistsRequest::new); path = existsRequest.getPath(); if (path.indexOf('\0') != -1) { throw new KeeperException.BadArgumentsException(); } DataNode n = zks.getZKDatabase().getNode(path); if (n != null) { zks.checkACL( request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, request.authInfo, path, null); } Stat stat = zks.getZKDatabase().statNode(path, existsRequest.getWatch() ? cnxn : null); rsp = new ExistsResponse(stat); requestPathMetricsCollector.registerRequest(request.type, path); break; } case OpCode.getData: { lastOp = "GETD"; GetDataRequest getDataRequest = request.readRequestRecord(GetDataRequest::new); path = getDataRequest.getPath(); rsp = handleGetDataRequest(getDataRequest, cnxn, request.authInfo); requestPathMetricsCollector.registerRequest(request.type, path); break; } case OpCode.setWatches: { lastOp = "SETW"; SetWatches setWatches = request.readRequestRecord(SetWatches::new); long relativeZxid = setWatches.getRelativeZxid(); zks.getZKDatabase() .setWatches( relativeZxid, setWatches.getDataWatches(), setWatches.getExistWatches(), setWatches.getChildWatches(), Collections.emptyList(), Collections.emptyList(), cnxn); break; } case OpCode.setWatches2: { lastOp = "STW2"; SetWatches2 setWatches = request.readRequestRecord(SetWatches2::new); long relativeZxid = setWatches.getRelativeZxid(); zks.getZKDatabase().setWatches(relativeZxid, setWatches.getDataWatches(), setWatches.getExistWatches(), setWatches.getChildWatches(), setWatches.getPersistentWatches(), setWatches.getPersistentRecursiveWatches(), cnxn); break; } case OpCode.addWatch: { lastOp = "ADDW"; AddWatchRequest addWatcherRequest = request.readRequestRecord(AddWatchRequest::new); zks.getZKDatabase().addWatch(addWatcherRequest.getPath(), cnxn, addWatcherRequest.getMode()); rsp = new ErrorResponse(0); break; } case OpCode.getACL: { lastOp = "GETA"; GetACLRequest getACLRequest = request.readRequestRecord(GetACLRequest::new); path = getACLRequest.getPath(); DataNode n = zks.getZKDatabase().getNode(path); if (n == null) { throw new KeeperException.NoNodeException(); } zks.checkACL( request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ | ZooDefs.Perms.ADMIN, request.authInfo, path, null); Stat stat = new Stat(); List acl = zks.getZKDatabase().getACL(path, stat); requestPathMetricsCollector.registerRequest(request.type, getACLRequest.getPath()); try { zks.checkACL( request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.ADMIN, request.authInfo, path, null); rsp = new GetACLResponse(acl, stat); } catch (KeeperException.NoAuthException e) { List acl1 = new ArrayList<>(acl.size()); for (ACL a : acl) { if ("digest".equals(a.getId().getScheme())) { Id id = a.getId(); Id id1 = new Id(id.getScheme(), id.getId().replaceAll(":.*", ":x")); acl1.add(new ACL(a.getPerms(), id1)); } else { acl1.add(a); } } rsp = new GetACLResponse(acl1, stat); } break; } case OpCode.getChildren: { lastOp = "GETC"; GetChildrenRequest getChildrenRequest = request.readRequestRecord(GetChildrenRequest::new); path = getChildrenRequest.getPath(); rsp = handleGetChildrenRequest(getChildrenRequest, cnxn, request.authInfo); requestPathMetricsCollector.registerRequest(request.type, path); break; } case OpCode.getAllChildrenNumber: { lastOp = "GETACN"; GetAllChildrenNumberRequest getAllChildrenNumberRequest = request.readRequestRecord(GetAllChildrenNumberRequest::new); path = getAllChildrenNumberRequest.getPath(); DataNode n = zks.getZKDatabase().getNode(path); if (n == null) { throw new KeeperException.NoNodeException(); } zks.checkACL( request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, request.authInfo, path, null); int number = zks.getZKDatabase().getAllChildrenNumber(path); rsp = new GetAllChildrenNumberResponse(number); break; } case OpCode.getChildren2: { lastOp = "GETC"; GetChildren2Request getChildren2Request = request.readRequestRecord(GetChildren2Request::new); Stat stat = new Stat(); path = getChildren2Request.getPath(); DataNode n = zks.getZKDatabase().getNode(path); if (n == null) { throw new KeeperException.NoNodeException(); } zks.checkACL( request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, request.authInfo, path, null); List children = zks.getZKDatabase() .getChildren(path, stat, getChildren2Request.getWatch() ? cnxn : null); rsp = new GetChildren2Response(children, stat); requestPathMetricsCollector.registerRequest(request.type, path); break; } case OpCode.checkWatches: { lastOp = "CHKW"; CheckWatchesRequest checkWatches = request.readRequestRecord(CheckWatchesRequest::new); WatcherType type = WatcherType.fromInt(checkWatches.getType()); path = checkWatches.getPath(); boolean containsWatcher = zks.getZKDatabase().containsWatcher(path, type, cnxn); if (!containsWatcher) { String msg = String.format(Locale.ENGLISH, "%s (type: %s)", path, type); throw new KeeperException.NoWatcherException(msg); } requestPathMetricsCollector.registerRequest(request.type, checkWatches.getPath()); break; } case OpCode.removeWatches: { lastOp = "REMW"; RemoveWatchesRequest removeWatches = request.readRequestRecord(RemoveWatchesRequest::new); WatcherType type = WatcherType.fromInt(removeWatches.getType()); path = removeWatches.getPath(); boolean removed = zks.getZKDatabase().removeWatch(path, type, cnxn); if (!removed) { String msg = String.format(Locale.ENGLISH, "%s (type: %s)", path, type); throw new KeeperException.NoWatcherException(msg); } requestPathMetricsCollector.registerRequest(request.type, removeWatches.getPath()); break; } case OpCode.whoAmI: { lastOp = "HOMI"; rsp = new WhoAmIResponse(AuthUtil.getClientInfos(request.authInfo)); break; } case OpCode.getEphemerals: { lastOp = "GETE"; GetEphemeralsRequest getEphemerals = request.readRequestRecord(GetEphemeralsRequest::new); String prefixPath = getEphemerals.getPrefixPath(); Set allEphems = zks.getZKDatabase().getDataTree().getEphemerals(request.sessionId); List ephemerals = new ArrayList<>(); if (prefixPath == null || prefixPath.trim().isEmpty() || "/".equals(prefixPath.trim())) { ephemerals.addAll(allEphems); } else { for (String p : allEphems) { if (p.startsWith(prefixPath)) { ephemerals.add(p); } } } rsp = new GetEphemeralsResponse(ephemerals); break; } } } catch (SessionMovedException e) { // session moved is a connection level error, we need to tear // down the connection otw ZOOKEEPER-710 might happen // ie client on slow follower starts to renew session, fails // before this completes, then tries the fast follower (leader) // and is successful, however the initial renew is then // successfully fwd/processed by the leader and as a result // the client and leader disagree on where the client is most // recently attached (and therefore invalid SESSION MOVED generated) cnxn.sendCloseSession(); return; } catch (KeeperException e) { err = e.code(); } catch (Exception e) { // log at error level as we are returning a marshalling // error to the user LOG.error("Failed to process {}", request, e); String digest = request.requestDigest(); LOG.error("Dumping request buffer for request type {}: 0x{}", Request.op2String(request.type), digest); err = Code.MARSHALLINGERROR; } ReplyHeader hdr = new ReplyHeader(request.cxid, lastZxid, err.intValue()); updateStats(request, lastOp, lastZxid); try { if (path == null || rsp == null) { responseSize = cnxn.sendResponse(hdr, rsp, "response"); } else { int opCode = request.type; Stat stat = null; // Serialized read and get children responses could be cached by the connection // object. Cache entries are identified by their path and last modified zxid, // so these values are passed along with the response. switch (opCode) { case OpCode.getData : { GetDataResponse getDataResponse = (GetDataResponse) rsp; stat = getDataResponse.getStat(); responseSize = cnxn.sendResponse(hdr, rsp, "response", path, stat, opCode); break; } case OpCode.getChildren2 : { GetChildren2Response getChildren2Response = (GetChildren2Response) rsp; stat = getChildren2Response.getStat(); responseSize = cnxn.sendResponse(hdr, rsp, "response", path, stat, opCode); break; } default: responseSize = cnxn.sendResponse(hdr, rsp, "response"); } } if (request.type == OpCode.closeSession) { cnxn.sendCloseSession(); } } catch (IOException e) { LOG.error("FIXMSG", e); } finally { ServerMetrics.getMetrics().RESPONSE_BYTES.add(responseSize); } } private Record handleGetChildrenRequest(Record request, ServerCnxn cnxn, List authInfo) throws KeeperException, IOException { GetChildrenRequest getChildrenRequest = (GetChildrenRequest) request; String path = getChildrenRequest.getPath(); DataNode n = zks.getZKDatabase().getNode(path); if (n == null) { throw new KeeperException.NoNodeException(); } zks.checkACL(cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, authInfo, path, null); List children = zks.getZKDatabase() .getChildren(path, null, getChildrenRequest.getWatch() ? cnxn : null); return new GetChildrenResponse(children); } private Record handleGetDataRequest(Record request, ServerCnxn cnxn, List authInfo) throws KeeperException, IOException { GetDataRequest getDataRequest = (GetDataRequest) request; String path = getDataRequest.getPath(); DataNode n = zks.getZKDatabase().getNode(path); if (n == null) { throw new KeeperException.NoNodeException(); } zks.checkACL(cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, authInfo, path, null); Stat stat = new Stat(); byte[] b = zks.getZKDatabase().getData(path, stat, getDataRequest.getWatch() ? cnxn : null); return new GetDataResponse(b, stat); } private boolean closeSession(ServerCnxnFactory serverCnxnFactory, long sessionId) { if (serverCnxnFactory == null) { return false; } return serverCnxnFactory.closeSession(sessionId, ServerCnxn.DisconnectReason.CLIENT_CLOSED_SESSION); } private boolean connClosedByClient(Request request) { return request.cnxn == null; } public void shutdown() { // we are the final link in the chain LOG.info("shutdown of request processor complete"); } private void updateStats(Request request, String lastOp, long lastZxid) { if (request.cnxn == null) { return; } long currentTime = Time.currentElapsedTime(); zks.serverStats().updateLatency(request, currentTime); request.cnxn.updateStatsForResponse(request.cxid, lastZxid, lastOp, request.createTime, currentTime); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_NI0100644 0000000 0000000 00000000156 15051152474 032602 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NIOServerCnxn.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NIOServerCnxn.java0100644 0000000 0000000 00000071371 15051152474 034117 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.CancelledKeyException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.security.cert.Certificate; import java.util.List; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.jute.BinaryInputArchive; import org.apache.jute.Record; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.WatcherEvent; import org.apache.zookeeper.server.NIOServerCnxnFactory.SelectorThread; import org.apache.zookeeper.server.command.CommandExecutor; import org.apache.zookeeper.server.command.FourLetterCommands; import org.apache.zookeeper.server.command.NopCommand; import org.apache.zookeeper.server.command.SetTraceMaskCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class handles communication with clients using NIO. There is one per * client, but only one thread doing the communication. */ public class NIOServerCnxn extends ServerCnxn { private static final Logger LOG = LoggerFactory.getLogger(NIOServerCnxn.class); private final NIOServerCnxnFactory factory; private final SocketChannel sock; private final SelectorThread selectorThread; private final SelectionKey sk; private boolean initialized; private final ByteBuffer lenBuffer = ByteBuffer.allocate(4); protected ByteBuffer incomingBuffer = lenBuffer; private final Queue outgoingBuffers = new LinkedBlockingQueue<>(); private int sessionTimeout; /** * This is the id that uniquely identifies the session of a client. Once * this session is no longer active, the ephemeral nodes will go away. */ private long sessionId; /** * Client socket option for TCP keepalive */ private final boolean clientTcpKeepAlive = Boolean.getBoolean("zookeeper.clientTcpKeepAlive"); public NIOServerCnxn(ZooKeeperServer zk, SocketChannel sock, SelectionKey sk, NIOServerCnxnFactory factory, SelectorThread selectorThread) throws IOException { super(zk); this.sock = sock; this.sk = sk; this.factory = factory; this.selectorThread = selectorThread; if (this.factory.login != null) { this.zooKeeperSaslServer = new ZooKeeperSaslServer(factory.login); } sock.socket().setTcpNoDelay(true); /* set socket linger to false, so that socket close does not block */ sock.socket().setSoLinger(false, -1); sock.socket().setKeepAlive(clientTcpKeepAlive); InetAddress addr = ((InetSocketAddress) sock.socket().getRemoteSocketAddress()).getAddress(); addAuthInfo(new Id("ip", addr.getHostAddress())); this.sessionTimeout = factory.sessionlessCnxnTimeout; } /* Send close connection packet to the client, doIO will eventually * close the underlying machinery (like socket, selectorkey, etc...) */ public void sendCloseSession() { sendBuffer(ServerCnxnFactory.closeConn); } /** * send buffer without using the asynchronous * calls to selector and then close the socket * @param bb */ void sendBufferSync(ByteBuffer bb) { try { /* configure socket to be blocking * so that we dont have to do write in * a tight while loop */ if (bb != ServerCnxnFactory.closeConn) { if (sock.isOpen()) { sock.configureBlocking(true); sock.write(bb); } packetSent(); } } catch (IOException ie) { LOG.error("Error sending data synchronously ", ie); } } /** * sendBuffer pushes a byte buffer onto the outgoing buffer queue for * asynchronous writes. */ public void sendBuffer(ByteBuffer... buffers) { if (LOG.isTraceEnabled()) { LOG.trace("Add a buffer to outgoingBuffers, sk {} is valid: {}", sk, sk.isValid()); } synchronized (outgoingBuffers) { for (ByteBuffer buffer : buffers) { outgoingBuffers.add(buffer); } outgoingBuffers.add(packetSentinel); } requestInterestOpsUpdate(); } /** * When read on socket failed, this is typically because client closed the * connection. In most cases, the client does this when the server doesn't * respond within 2/3 of session timeout. This possibly indicates server * health/performance issue, so we need to log and keep track of stat * * @throws EndOfStreamException */ private void handleFailedRead() throws EndOfStreamException { setStale(); ServerMetrics.getMetrics().CONNECTION_DROP_COUNT.add(1); throw new EndOfStreamException("Unable to read additional data from client," + " it probably closed the socket:" + " address = " + sock.socket().getRemoteSocketAddress() + "," + " session = 0x" + Long.toHexString(sessionId), DisconnectReason.UNABLE_TO_READ_FROM_CLIENT); } /** Read the request payload (everything following the length prefix) */ private void readPayload() throws IOException, InterruptedException, ClientCnxnLimitException { if (incomingBuffer.remaining() != 0) { // have we read length bytes? int rc = sock.read(incomingBuffer); // sock is non-blocking, so ok if (rc < 0) { handleFailedRead(); } } if (incomingBuffer.remaining() == 0) { // have we read length bytes? incomingBuffer.flip(); packetReceived(4 + incomingBuffer.remaining()); if (!initialized) { readConnectRequest(); } else { readRequest(); } lenBuffer.clear(); incomingBuffer = lenBuffer; } } /** * This boolean tracks whether the connection is ready for selection or * not. A connection is marked as not ready for selection while it is * processing an IO request. The flag is used to gatekeep pushing interest * op updates onto the selector. */ private final AtomicBoolean selectable = new AtomicBoolean(true); public boolean isSelectable() { return sk.isValid() && selectable.get(); } public void disableSelectable() { selectable.set(false); } public void enableSelectable() { selectable.set(true); } private void requestInterestOpsUpdate() { if (isSelectable()) { selectorThread.addInterestOpsUpdateRequest(sk); } } void handleWrite(SelectionKey k) throws IOException { if (outgoingBuffers.isEmpty()) { return; } /* * 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. */ ByteBuffer directBuffer = NIOServerCnxnFactory.getDirectBuffer(); if (directBuffer == null) { ByteBuffer[] bufferList = new ByteBuffer[outgoingBuffers.size()]; // Use gathered write call. This updates the positions of the // byte buffers to reflect the bytes that were written out. sock.write(outgoingBuffers.toArray(bufferList)); // Remove the buffers that we have sent ByteBuffer bb; while ((bb = outgoingBuffers.peek()) != null) { if (bb == ServerCnxnFactory.closeConn) { throw new CloseRequestException("close requested", DisconnectReason.CLIENT_CLOSED_CONNECTION); } if (bb == packetSentinel) { packetSent(); } if (bb.remaining() > 0) { break; } outgoingBuffers.remove(); } } else { 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 ((bb = outgoingBuffers.peek()) != null) { if (bb == ServerCnxnFactory.closeConn) { throw new CloseRequestException("close requested", DisconnectReason.CLIENT_CLOSED_CONNECTION); } if (bb == packetSentinel) { packetSent(); } if (sent < bb.remaining()) { /* * We only partially sent this buffer, so we update * the position and exit the loop. */ bb.position(bb.position() + sent); break; } /* We've sent the whole buffer, so drop the buffer */ sent -= bb.remaining(); outgoingBuffers.remove(); } } } /** * Only used in order to allow testing */ protected boolean isSocketOpen() { return sock.isOpen(); } /** * Handles read/write IO on connection. */ void doIO(SelectionKey k) throws InterruptedException { try { if (!isSocketOpen()) { LOG.warn("trying to do i/o on a null socket for session: 0x{}", Long.toHexString(sessionId)); return; } if (k.isReadable()) { int rc = sock.read(incomingBuffer); if (rc < 0) { try { handleFailedRead(); } catch (EndOfStreamException e) { // no stacktrace. this case is very common, and it is usually not a problem. LOG.info("{}", e.getMessage()); // expecting close to log session closure close(e.getReason()); return; } } if (incomingBuffer.remaining() == 0) { boolean isPayload; if (incomingBuffer == lenBuffer) { // start of next request incomingBuffer.flip(); isPayload = readLength(k); incomingBuffer.clear(); } else { // continuation isPayload = true; } if (isPayload) { // not the case for 4letterword readPayload(); } else { // four letter words take care // need not do anything else return; } } } if (k.isWritable()) { handleWrite(k); if (!initialized && !getReadInterest() && !getWriteInterest()) { throw new CloseRequestException("responded to info probe", DisconnectReason.INFO_PROBE); } } } catch (CancelledKeyException e) { LOG.warn("CancelledKeyException causing close of session: 0x{}", Long.toHexString(sessionId)); LOG.debug("CancelledKeyException stack trace", e); close(DisconnectReason.CANCELLED_KEY_EXCEPTION); } catch (CloseRequestException e) { // expecting close to log session closure close(); } catch (EndOfStreamException e) { LOG.warn("Unexpected exception", e); // expecting close to log session closure close(e.getReason()); } catch (ClientCnxnLimitException e) { // Common case exception, print at debug level ServerMetrics.getMetrics().CONNECTION_REJECTED.add(1); LOG.warn("Closing session 0x{}", Long.toHexString(sessionId), e); close(DisconnectReason.CLIENT_CNX_LIMIT); } catch (IOException e) { LOG.warn("Close of session 0x{}", Long.toHexString(sessionId), e); close(DisconnectReason.IO_EXCEPTION); } } protected void readRequest() throws IOException { RequestHeader h = new RequestHeader(); ByteBufferInputStream.byteBuffer2Record(incomingBuffer, h); RequestRecord request = RequestRecord.fromBytes(incomingBuffer.slice()); zkServer.processPacket(this, h, request); } // returns whether we are interested in writing, which is determined // by whether we have any pending buffers on the output queue or not private boolean getWriteInterest() { return !outgoingBuffers.isEmpty(); } // returns whether we are interested in taking new requests, which is // determined by whether we are currently throttled or not private boolean getReadInterest() { return !throttled.get(); } private final AtomicBoolean throttled = new AtomicBoolean(false); // Throttle acceptance of new requests. If this entailed a state change, // register an interest op update request with the selector. // // Don't support wait disable receive in NIO, ignore the parameter public void disableRecv(boolean waitDisableRecv) { if (throttled.compareAndSet(false, true)) { requestInterestOpsUpdate(); } } // Disable throttling and resume acceptance of new requests. If this // entailed a state change, register an interest op update request with // the selector. public void enableRecv() { if (throttled.compareAndSet(true, false)) { requestInterestOpsUpdate(); } } private void readConnectRequest() throws IOException, ClientCnxnLimitException { if (!isZKServerRunning()) { throw new IOException("ZooKeeperServer not running"); } BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(incomingBuffer)); ConnectRequest request = protocolManager.deserializeConnectRequest(bia); zkServer.processConnectRequest(this, request); initialized = true; } /** * This class wraps the sendBuffer method of NIOServerCnxn. It is * responsible for chunking up the response to a client. Rather * than cons'ing up a response fully in memory, which may be large * for some commands, this class chunks up the result. */ private class SendBufferWriter extends Writer { private StringBuffer sb = new StringBuffer(); /** * Check if we are ready to send another chunk. * @param force force sending, even if not a full chunk */ private void checkFlush(boolean force) { if ((force && sb.length() > 0) || sb.length() > 2048) { sendBufferSync(ByteBuffer.wrap(sb.toString().getBytes(UTF_8))); // clear our internal buffer sb.setLength(0); } } @Override public void close() throws IOException { if (sb == null) { return; } checkFlush(true); sb = null; // clear out the ref to ensure no reuse } @Override public void flush() throws IOException { checkFlush(true); } @Override public void write(char[] cbuf, int off, int len) throws IOException { sb.append(cbuf, off, len); checkFlush(false); } } /** Return if four letter word found and responded to, otw false **/ private boolean checkFourLetterWord(final SelectionKey k, final int len) throws IOException { // We take advantage of the limited size of the length to look // for cmds. They are all 4-bytes which fits inside of an int if (!FourLetterCommands.isKnown(len)) { return false; } String cmd = FourLetterCommands.getCommandString(len); packetReceived(4); /** cancel the selection key to remove the socket handling * from selector. This is to prevent netcat problem wherein * netcat immediately closes the sending side after sending the * commands and still keeps the receiving channel open. * The idea is to remove the selectionkey from the selector * so that the selector does not notice the closed read on the * socket channel and keep the socket alive to write the data to * and makes sure to close the socket after its done writing the data */ if (k != null) { try { k.cancel(); } catch (Exception e) { LOG.error("Error cancelling command selection key", e); } } final PrintWriter pwriter = new PrintWriter(new BufferedWriter(new SendBufferWriter())); // ZOOKEEPER-2693: don't execute 4lw if it's not enabled. if (!FourLetterCommands.isEnabled(cmd)) { LOG.debug("Command {} is not executed because it is not in the whitelist.", cmd); NopCommand nopCmd = new NopCommand( pwriter, this, cmd + " is not executed because it is not in the whitelist."); nopCmd.start(); return true; } LOG.info("Processing {} command from {}", cmd, sock.socket().getRemoteSocketAddress()); if (len == FourLetterCommands.setTraceMaskCmd) { incomingBuffer = ByteBuffer.allocate(8); int rc = sock.read(incomingBuffer); if (rc < 0) { throw new IOException("Read error"); } incomingBuffer.flip(); long traceMask = incomingBuffer.getLong(); ZooTrace.setTextTraceLevel(traceMask); SetTraceMaskCommand setMask = new SetTraceMaskCommand(pwriter, this, traceMask); setMask.start(); return true; } else { CommandExecutor commandExecutor = new CommandExecutor(); return commandExecutor.execute(this, pwriter, len, zkServer, factory); } } /** Reads the first 4 bytes of lenBuffer, which could be true length or * four letter word. * * @param k selection key * @return true if length read, otw false (wasn't really the length) * @throws IOException if buffer size exceeds maxBuffer size */ private boolean readLength(SelectionKey k) throws IOException { // Read the length, now get the buffer int len = lenBuffer.getInt(); if (!initialized && checkFourLetterWord(sk, len)) { return false; } if (len < 0 || len > BinaryInputArchive.maxBuffer) { throw new IOException("Len error. " + "A message from " + this.getRemoteSocketAddress() + " with advertised length of " + len + " is either a malformed message or too large to process" + " (length is greater than jute.maxbuffer=" + BinaryInputArchive.maxBuffer + ")"); } if (!isZKServerRunning()) { throw new IOException("ZooKeeperServer not running"); } // checkRequestSize will throw IOException if request is rejected zkServer.checkRequestSizeWhenReceivingMessage(len); incomingBuffer = ByteBuffer.allocate(len); return true; } /* * (non-Javadoc) * * @see org.apache.zookeeper.server.ServerCnxnIface#getSessionTimeout() */ public int getSessionTimeout() { return sessionTimeout; } /** * Used by "dump" 4-letter command to list all connection in * cnxnExpiryMap */ @Override public String toString() { return "ip: " + sock.socket().getRemoteSocketAddress() + " sessionId: 0x" + Long.toHexString(sessionId); } /** * Close the cnxn and remove it from the factory cnxns list. */ @Override public void close(DisconnectReason reason) { disconnectReason = reason; close(); } private void close() { setStale(); if (!factory.removeCnxn(this)) { return; } if (zkServer != null) { zkServer.removeCnxn(this); } if (sk != null) { try { // need to cancel this selection key from the selector sk.cancel(); } catch (Exception e) { LOG.debug("ignoring exception during selectionkey cancel", e); } } closeSock(); } /** * Close resources associated with the sock of this cnxn. */ private void closeSock() { if (!sock.isOpen()) { return; } String logMsg = String.format( "Closed socket connection for client %s %s", sock.socket().getRemoteSocketAddress(), sessionId != 0 ? "which had sessionid 0x" + Long.toHexString(sessionId) : "(no session established for client)" ); LOG.debug(logMsg); closeSock(sock); } /** * Close resources associated with a sock. */ public static void closeSock(SocketChannel sock) { if (!sock.isOpen()) { return; } 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 LOG.debug("ignoring exception during output shutdown", e); } try { sock.socket().shutdownInput(); } catch (IOException e) { // This is a relatively common exception that we can't avoid LOG.debug("ignoring exception during input shutdown", e); } try { sock.socket().close(); } catch (IOException e) { LOG.debug("ignoring exception during socket close", e); } try { sock.close(); } catch (IOException e) { LOG.debug("ignoring exception during socketchannel close", e); } } private static final ByteBuffer packetSentinel = ByteBuffer.allocate(0); @Override public int sendResponse(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) { int responseSize = 0; try { ByteBuffer[] bb = serialize(h, r, tag, cacheKey, stat, opCode); responseSize = bb[0].getInt(); bb[0].rewind(); sendBuffer(bb); decrOutstandingAndCheckThrottle(h); } catch (Exception e) { LOG.warn("Unexpected exception. Destruction averted.", e); } return responseSize; } /* * (non-Javadoc) * * @see org.apache.zookeeper.server.ServerCnxnIface#process(org.apache.zookeeper.proto.WatcherEvent) */ @Override public void process(WatchedEvent event, List znodeAcl) { try { zkServer.checkACL(this, znodeAcl, ZooDefs.Perms.READ, getAuthInfo(), event.getPath(), null); } catch (KeeperException.NoAuthException e) { if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "Not delivering event " + event + " to 0x" + Long.toHexString(this.sessionId) + " (filtered by ACL)"); } return; } ReplyHeader h = new ReplyHeader(ClientCnxn.NOTIFICATION_XID, event.getZxid(), 0); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "Deliver event " + event + " to 0x" + Long.toHexString(this.sessionId) + " through " + this); } // Convert WatchedEvent to a type that can be sent over the wire WatcherEvent e = event.getWrapper(); // The last parameter OpCode here is used to select the response cache. // Passing OpCode.error (with a value of -1) means we don't care, as we don't need // response cache on delivering watcher events. int responseSize = sendResponse(h, e, "notification", null, null, ZooDefs.OpCode.error); ServerMetrics.getMetrics().WATCH_BYTES.add(responseSize); } /* * (non-Javadoc) * * @see org.apache.zookeeper.server.ServerCnxnIface#getSessionId() */ @Override public long getSessionId() { return sessionId; } @Override public void setSessionId(long sessionId) { this.sessionId = sessionId; factory.addSession(sessionId, this); } @Override public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; factory.touchCnxn(this); } @Override public int getInterestOps() { if (!isSelectable()) { return 0; } int interestOps = 0; if (getReadInterest()) { interestOps |= SelectionKey.OP_READ; } if (getWriteInterest()) { interestOps |= SelectionKey.OP_WRITE; } return interestOps; } @Override public InetSocketAddress getRemoteSocketAddress() { if (!sock.isOpen()) { return null; } return (InetSocketAddress) sock.socket().getRemoteSocketAddress(); } public InetAddress getSocketAddress() { if (!sock.isOpen()) { return null; } return sock.socket().getInetAddress(); } @Override protected ServerStats serverStats() { if (zkServer == null) { return null; } return zkServer.serverStats(); } @Override public boolean isSecure() { return false; } @Override public Certificate[] getClientCertificateChain() { throw new UnsupportedOperationException("SSL is unsupported in NIOServerCnxn"); } @Override public void setClientCertificateChain(Certificate[] chain) { throw new UnsupportedOperationException("SSL is unsupported in NIOServerCnxn"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_NI0100644 0000000 0000000 00000000165 15051152474 032602 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NIOServerCnxnFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NIOServerCnxnFacto0100644 0000000 0000000 00000105553 15051152474 034154 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; 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.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * NIOServerCnxnFactory implements a multi-threaded ServerCnxnFactory using * NIO non-blocking socket calls. Communication between threads is handled via * queues. * * - 1 accept thread, which accepts new connections and assigns to a * selector thread * - 1-N selector threads, each of which selects on 1/N of the connections. * The reason the factory supports more than one selector thread is that * with large numbers of connections, select() itself can become a * performance bottleneck. * - 0-M socket I/O worker threads, which perform basic socket reads and * writes. If configured with 0 worker threads, the selector threads * do the socket I/O directly. * - 1 connection expiration thread, which closes idle connections; this is * necessary to expire connections on which no session is established. * * Typical (default) thread counts are: on a 32 core machine, 1 accept thread, * 1 connection expiration thread, 4 selector threads, and 64 worker threads. */ public class NIOServerCnxnFactory extends ServerCnxnFactory { private static final Logger LOG = LoggerFactory.getLogger(NIOServerCnxnFactory.class); /** Default sessionless connection timeout in ms: 10000 (10s) */ public static final String ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT = "zookeeper.nio.sessionlessCnxnTimeout"; /** * With 500 connections to an observer with watchers firing on each, is * unable to exceed 1GigE rates with only 1 selector. * Defaults to using 2 selector threads with 8 cores and 4 with 32 cores. * Expressed as sqrt(numCores/2). Must have at least 1 selector thread. */ public static final String ZOOKEEPER_NIO_NUM_SELECTOR_THREADS = "zookeeper.nio.numSelectorThreads"; /** Default: 2 * numCores */ public static final String ZOOKEEPER_NIO_NUM_WORKER_THREADS = "zookeeper.nio.numWorkerThreads"; /** Default: 64kB */ public static final String ZOOKEEPER_NIO_DIRECT_BUFFER_BYTES = "zookeeper.nio.directBufferBytes"; /** Default worker pool shutdown timeout in ms: 5000 (5s) */ public static final String ZOOKEEPER_NIO_SHUTDOWN_TIMEOUT = "zookeeper.nio.shutdownTimeout"; static { Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOG.error("Thread {} died", t, e)); /** * Value of 0 disables use of direct buffers and instead uses * gathered write call. * * Default to using 64k direct buffers. */ directBufferBytes = Integer.getInteger(ZOOKEEPER_NIO_DIRECT_BUFFER_BYTES, 64 * 1024); } /** * AbstractSelectThread is an abstract base class containing a few bits * of code shared by the AcceptThread (which selects on the listen socket) * and SelectorThread (which selects on client connections) classes. */ private abstract class AbstractSelectThread extends ZooKeeperThread { protected final Selector selector; public AbstractSelectThread(String name) throws IOException { super(name); // Allows the JVM to shutdown even if this thread is still running. setDaemon(true); this.selector = Selector.open(); } public void wakeupSelector() { selector.wakeup(); } /** * Close the selector. This should be called when the thread is about to * exit and no operation is going to be performed on the Selector or * SelectionKey */ protected void closeSelector() { try { selector.close(); } catch (IOException e) { LOG.warn("ignored exception during selector close.", e); } } protected void cleanupSelectionKey(SelectionKey key) { if (key != null) { try { key.cancel(); } catch (Exception ex) { LOG.debug("ignoring exception during selectionkey cancel", ex); } } } protected void fastCloseSock(SocketChannel sc) { if (sc != null) { try { // Hard close immediately, discarding buffers sc.socket().setSoLinger(true, 0); } catch (SocketException e) { LOG.warn("Unable to set socket linger to 0, socket close may stall in CLOSE_WAIT", e); } NIOServerCnxn.closeSock(sc); } } } /** * There is a single AcceptThread which accepts new connections and assigns * them to a SelectorThread using a simple round-robin scheme to spread * them across the SelectorThreads. It enforces maximum number of * connections per IP and attempts to cope with running out of file * descriptors by briefly sleeping before retrying. */ private class AcceptThread extends AbstractSelectThread { private final ServerSocketChannel acceptSocket; private final SelectionKey acceptKey; private final RateLogger acceptErrorLogger = new RateLogger(LOG); private final Collection selectorThreads; private Iterator selectorIterator; private volatile boolean reconfiguring = false; public AcceptThread(ServerSocketChannel ss, InetSocketAddress addr, Set selectorThreads) throws IOException { super("NIOServerCxnFactory.AcceptThread:" + addr); this.acceptSocket = ss; this.acceptKey = acceptSocket.register(selector, SelectionKey.OP_ACCEPT); this.selectorThreads = Collections.unmodifiableList(new ArrayList(selectorThreads)); selectorIterator = this.selectorThreads.iterator(); } public void run() { try { while (!stopped && !acceptSocket.socket().isClosed()) { try { select(); } catch (RuntimeException e) { LOG.warn("Ignoring unexpected runtime exception", e); } catch (Exception e) { LOG.warn("Ignoring unexpected exception", e); } } } finally { closeSelector(); // This will wake up the selector threads, and tell the // worker thread pool to begin shutdown. if (!reconfiguring) { NIOServerCnxnFactory.this.stop(); } LOG.info("accept thread exitted run method"); } } public void setReconfiguring() { reconfiguring = true; } private void select() { try { selector.select(); Iterator selectedKeys = selector.selectedKeys().iterator(); while (!stopped && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selectedKeys.remove(); if (!key.isValid()) { continue; } if (key.isAcceptable()) { if (!doAccept()) { // If unable to pull a new connection off the accept // queue, pause accepting to give us time to free // up file descriptors and so the accept thread // doesn't spin in a tight loop. pauseAccept(10); } } else { LOG.warn("Unexpected ops in accept select {}", key.readyOps()); } } } catch (IOException e) { LOG.warn("Ignoring IOException while selecting", e); } } /** * Mask off the listen socket interest ops and use select() to sleep * so that other threads can wake us up by calling wakeup() on the * selector. */ private void pauseAccept(long millisecs) { acceptKey.interestOps(0); try { selector.select(millisecs); } catch (IOException e) { // ignore } finally { acceptKey.interestOps(SelectionKey.OP_ACCEPT); } } /** * Accept new socket connections. Enforces maximum number of connections * per client IP address. Round-robin assigns to selector thread for * handling. Returns whether pulled a connection off the accept queue * or not. If encounters an error attempts to fast close the socket. * * @return whether was able to accept a connection or not */ private boolean doAccept() { boolean accepted = false; SocketChannel sc = null; try { sc = acceptSocket.accept(); accepted = true; if (limitTotalNumberOfCnxns()) { throw new IOException("Too many connections max allowed is " + maxCnxns); } InetAddress ia = sc.socket().getInetAddress(); int cnxncount = getClientCnxnCount(ia); if (maxClientCnxns > 0 && cnxncount >= maxClientCnxns) { throw new IOException("Too many connections from " + ia + " - max is " + maxClientCnxns); } LOG.debug("Accepted socket connection from {}", sc.socket().getRemoteSocketAddress()); sc.configureBlocking(false); // Round-robin assign this connection to a selector thread if (!selectorIterator.hasNext()) { selectorIterator = selectorThreads.iterator(); } SelectorThread selectorThread = selectorIterator.next(); if (!selectorThread.addAcceptedConnection(sc)) { throw new IOException("Unable to add connection to selector queue" + (stopped ? " (shutdown in progress)" : "")); } acceptErrorLogger.flush(); } catch (IOException e) { // accept, maxClientCnxns, configureBlocking ServerMetrics.getMetrics().CONNECTION_REJECTED.add(1); acceptErrorLogger.rateLimitLog("Error accepting new connection: " + e.getMessage()); fastCloseSock(sc); } return accepted; } } /** * The SelectorThread receives newly accepted connections from the * AcceptThread and is responsible for selecting for I/O readiness * across the connections. This thread is the only thread that performs * any non-threadsafe or potentially blocking calls on the selector * (registering new connections and reading/writing interest ops). * * Assignment of a connection to a SelectorThread is permanent and only * one SelectorThread will ever interact with the connection. There are * 1-N SelectorThreads, with connections evenly apportioned between the * SelectorThreads. * * If there is a worker thread pool, when a connection has I/O to perform * the SelectorThread removes it from selection by clearing its interest * ops and schedules the I/O for processing by a worker thread. When the * work is complete, the connection is placed on the ready queue to have * its interest ops restored and resume selection. * * If there is no worker thread pool, the SelectorThread performs the I/O * directly. */ public class SelectorThread extends AbstractSelectThread { private final int id; private final Queue acceptedQueue; private final Queue updateQueue; public SelectorThread(int id) throws IOException { super("NIOServerCxnFactory.SelectorThread-" + id); this.id = id; acceptedQueue = new LinkedBlockingQueue<>(); updateQueue = new LinkedBlockingQueue<>(); } /** * Place new accepted connection onto a queue for adding. Do this * so only the selector thread modifies what keys are registered * with the selector. */ public boolean addAcceptedConnection(SocketChannel accepted) { if (stopped || !acceptedQueue.offer(accepted)) { return false; } wakeupSelector(); return true; } /** * Place interest op update requests onto a queue so that only the * selector thread modifies interest ops, because interest ops * reads/sets are potentially blocking operations if other select * operations are happening. */ public boolean addInterestOpsUpdateRequest(SelectionKey sk) { if (stopped || !updateQueue.offer(sk)) { return false; } wakeupSelector(); return true; } /** * The main loop for the thread selects() on the connections and * dispatches ready I/O work requests, then registers all pending * newly accepted connections and updates any interest ops on the * queue. */ public void run() { try { while (!stopped) { try { select(); processAcceptedConnections(); processInterestOpsUpdateRequests(); } catch (RuntimeException e) { LOG.warn("Ignoring unexpected runtime exception", e); } catch (Exception e) { LOG.warn("Ignoring unexpected exception", e); } } // Close connections still pending on the selector. Any others // with in-flight work, let drain out of the work queue. for (SelectionKey key : selector.keys()) { NIOServerCnxn cnxn = (NIOServerCnxn) key.attachment(); if (cnxn.isSelectable()) { cnxn.close(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN); } cleanupSelectionKey(key); } SocketChannel accepted; while ((accepted = acceptedQueue.poll()) != null) { fastCloseSock(accepted); } updateQueue.clear(); } finally { closeSelector(); // This will wake up the accept thread and the other selector // threads, and tell the worker thread pool to begin shutdown. NIOServerCnxnFactory.this.stop(); LOG.info("selector thread exitted run method"); } } private void select() { try { selector.select(); Set selected = selector.selectedKeys(); ArrayList selectedList = new ArrayList<>(selected); Collections.shuffle(selectedList); Iterator selectedKeys = selectedList.iterator(); while (!stopped && selectedKeys.hasNext()) { SelectionKey key = selectedKeys.next(); selected.remove(key); if (!key.isValid()) { cleanupSelectionKey(key); continue; } if (key.isReadable() || key.isWritable()) { handleIO(key); } else { LOG.warn("Unexpected ops in select {}", key.readyOps()); } } } catch (IOException e) { LOG.warn("Ignoring IOException while selecting", e); } } /** * Schedule I/O for processing on the connection associated with * the given SelectionKey. If a worker thread pool is not being used, * I/O is run directly by this thread. */ private void handleIO(SelectionKey key) { IOWorkRequest workRequest = new IOWorkRequest(this, key); NIOServerCnxn cnxn = (NIOServerCnxn) key.attachment(); // Stop selecting this key while processing on its // connection cnxn.disableSelectable(); key.interestOps(0); touchCnxn(cnxn); workerPool.schedule(workRequest); } /** * Iterate over the queue of accepted connections that have been * assigned to this thread but not yet placed on the selector. */ private void processAcceptedConnections() { SocketChannel accepted; while (!stopped && (accepted = acceptedQueue.poll()) != null) { SelectionKey key = null; try { key = accepted.register(selector, SelectionKey.OP_READ); NIOServerCnxn cnxn = createConnection(accepted, key, this); key.attach(cnxn); addCnxn(cnxn); } catch (IOException e) { // register, createConnection cleanupSelectionKey(key); fastCloseSock(accepted); } } } /** * Iterate over the queue of connections ready to resume selection, * and restore their interest ops selection mask. */ private void processInterestOpsUpdateRequests() { SelectionKey key; while (!stopped && (key = updateQueue.poll()) != null) { if (!key.isValid()) { cleanupSelectionKey(key); } NIOServerCnxn cnxn = (NIOServerCnxn) key.attachment(); if (cnxn.isSelectable()) { key.interestOps(cnxn.getInterestOps()); } } } } /** * IOWorkRequest is a small wrapper class to allow doIO() calls to be * run on a connection using a WorkerService. */ private class IOWorkRequest extends WorkerService.WorkRequest { private final SelectorThread selectorThread; private final SelectionKey key; private final NIOServerCnxn cnxn; IOWorkRequest(SelectorThread selectorThread, SelectionKey key) { this.selectorThread = selectorThread; this.key = key; this.cnxn = (NIOServerCnxn) key.attachment(); } public void doWork() throws InterruptedException { if (!key.isValid()) { selectorThread.cleanupSelectionKey(key); return; } if (key.isReadable() || key.isWritable()) { cnxn.doIO(key); // Check if we shutdown or doIO() closed this connection if (stopped) { cnxn.close(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN); return; } if (!key.isValid()) { selectorThread.cleanupSelectionKey(key); return; } touchCnxn(cnxn); } // Mark this connection as once again ready for selection cnxn.enableSelectable(); // Push an update request on the queue to resume selecting // on the current set of interest ops, which may have changed // as a result of the I/O operations we just performed. if (!selectorThread.addInterestOpsUpdateRequest(key)) { cnxn.close(ServerCnxn.DisconnectReason.CONNECTION_MODE_CHANGED); } } @Override public void cleanup() { cnxn.close(ServerCnxn.DisconnectReason.CLEAN_UP); } } /** * This thread is responsible for closing stale connections so that * connections on which no session is established are properly expired. */ private class ConnectionExpirerThread extends ZooKeeperThread { ConnectionExpirerThread() { super("ConnnectionExpirer"); } public void run() { try { while (!stopped) { long waitTime = cnxnExpiryQueue.getWaitTime(); if (waitTime > 0) { Thread.sleep(waitTime); continue; } for (NIOServerCnxn conn : cnxnExpiryQueue.poll()) { ServerMetrics.getMetrics().SESSIONLESS_CONNECTIONS_EXPIRED.add(1); conn.close(ServerCnxn.DisconnectReason.CONNECTION_EXPIRED); } } } catch (InterruptedException e) { LOG.info("ConnnectionExpirerThread interrupted"); } } } ServerSocketChannel ss; /** * We use this buffer to do efficient socket I/O. Because I/O is handled * by the worker threads (or the selector threads directly, if no worker * thread pool is created), we can create a fixed set of these to be * shared by connections. */ private static final ThreadLocal directBuffer = new ThreadLocal() { @Override protected ByteBuffer initialValue() { return ByteBuffer.allocateDirect(directBufferBytes); } }; public static ByteBuffer getDirectBuffer() { return directBufferBytes > 0 ? directBuffer.get() : null; } // ipMap is used to limit connections per IP private final ConcurrentHashMap> ipMap = new ConcurrentHashMap<>(); protected int maxClientCnxns = 60; int listenBacklog = -1; int sessionlessCnxnTimeout; private ExpiryQueue cnxnExpiryQueue; protected WorkerService workerPool; private static int directBufferBytes; private int numSelectorThreads; private int numWorkerThreads; private long workerShutdownTimeoutMS; /** * Construct a new server connection factory which will accept an unlimited number * of concurrent connections from each client (up to the file descriptor * limits of the operating system). startup(zks) must be called subsequently. */ public NIOServerCnxnFactory() { } private volatile boolean stopped = true; private ConnectionExpirerThread expirerThread; private AcceptThread acceptThread; private final Set selectorThreads = new HashSet<>(); @Override public void configure(InetSocketAddress addr, int maxcc, int backlog, boolean secure) throws IOException { if (secure) { throw new UnsupportedOperationException("SSL isn't supported in NIOServerCnxn"); } configureSaslLogin(); maxClientCnxns = maxcc; initMaxCnxns(); sessionlessCnxnTimeout = Integer.getInteger(ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT, 10000); // We also use the sessionlessCnxnTimeout as expiring interval for // cnxnExpiryQueue. These don't need to be the same, but the expiring // interval passed into the ExpiryQueue() constructor below should be // less than or equal to the timeout. cnxnExpiryQueue = new ExpiryQueue<>(sessionlessCnxnTimeout); expirerThread = new ConnectionExpirerThread(); int numCores = Runtime.getRuntime().availableProcessors(); // 32 cores sweet spot seems to be 4 selector threads numSelectorThreads = Integer.getInteger( ZOOKEEPER_NIO_NUM_SELECTOR_THREADS, Math.max((int) Math.sqrt((float) numCores / 2), 1)); if (numSelectorThreads < 1) { throw new IOException("numSelectorThreads must be at least 1"); } numWorkerThreads = Integer.getInteger(ZOOKEEPER_NIO_NUM_WORKER_THREADS, 2 * numCores); workerShutdownTimeoutMS = Long.getLong(ZOOKEEPER_NIO_SHUTDOWN_TIMEOUT, 5000); String logMsg = "Configuring NIO connection handler with " + (sessionlessCnxnTimeout / 1000) + "s sessionless connection timeout, " + numSelectorThreads + " selector thread(s), " + (numWorkerThreads > 0 ? numWorkerThreads : "no") + " worker threads, and " + (directBufferBytes == 0 ? "gathered writes." : ("" + (directBufferBytes / 1024) + " kB direct buffers.")); LOG.info(logMsg); for (int i = 0; i < numSelectorThreads; ++i) { selectorThreads.add(new SelectorThread(i)); } listenBacklog = backlog; this.ss = ServerSocketChannel.open(); ss.socket().setReuseAddress(true); LOG.info("binding to port {}", addr); if (listenBacklog == -1) { ss.socket().bind(addr); } else { ss.socket().bind(addr, listenBacklog); } if (addr.getPort() == 0) { // We're likely bound to a different port than was requested, so log that too LOG.info("bound to port {}", ss.getLocalAddress()); } ss.configureBlocking(false); acceptThread = new AcceptThread(ss, addr, selectorThreads); } private void tryClose(ServerSocketChannel s) { try { s.close(); } catch (IOException sse) { LOG.error("Error while closing server socket.", sse); } } @Override public void reconfigure(InetSocketAddress addr) { ServerSocketChannel oldSS = ss; try { acceptThread.setReconfiguring(); tryClose(oldSS); acceptThread.wakeupSelector(); try { acceptThread.join(); } catch (InterruptedException e) { LOG.error("Error joining old acceptThread when reconfiguring client port.", e); Thread.currentThread().interrupt(); } this.ss = ServerSocketChannel.open(); ss.socket().setReuseAddress(true); LOG.info("binding to port {}", addr); ss.socket().bind(addr); ss.configureBlocking(false); acceptThread = new AcceptThread(ss, addr, selectorThreads); acceptThread.start(); } catch (IOException e) { LOG.error("Error reconfiguring client port to {}", addr, e); tryClose(oldSS); } } /** {@inheritDoc} */ public int getMaxClientCnxnsPerHost() { return maxClientCnxns; } /** {@inheritDoc} */ public void setMaxClientCnxnsPerHost(int max) { maxClientCnxns = max; } /** {@inheritDoc} */ public int getSocketListenBacklog() { return listenBacklog; } @Override public void start() { stopped = false; if (workerPool == null) { workerPool = new WorkerService("NIOWorker", numWorkerThreads, false); } for (SelectorThread thread : selectorThreads) { if (thread.getState() == Thread.State.NEW) { thread.start(); } } // ensure thread is started once and only once if (acceptThread.getState() == Thread.State.NEW) { acceptThread.start(); } if (expirerThread.getState() == Thread.State.NEW) { expirerThread.start(); } } @Override public void startup(ZooKeeperServer zks, boolean startServer) throws IOException, InterruptedException { start(); setZooKeeperServer(zks); if (startServer) { zks.startdata(); zks.startup(); } } @Override public InetSocketAddress getLocalAddress() { return (InetSocketAddress) ss.socket().getLocalSocketAddress(); } @Override public int getLocalPort() { return ss.socket().getLocalPort(); } /** * De-registers the connection from the various mappings maintained * by the factory. */ public boolean removeCnxn(NIOServerCnxn cnxn) { // If the connection is not in the master list it's already been closed if (!cnxns.remove(cnxn)) { return false; } cnxnExpiryQueue.remove(cnxn); removeCnxnFromSessionMap(cnxn); InetAddress addr = cnxn.getSocketAddress(); if (addr != null) { Set set = ipMap.get(addr); if (set != null) { set.remove(cnxn); // Note that we make no effort here to remove empty mappings // from ipMap. } } // unregister from JMX unregisterConnection(cnxn); return true; } /** * Add or update cnxn in our cnxnExpiryQueue * @param cnxn */ public void touchCnxn(NIOServerCnxn cnxn) { cnxnExpiryQueue.update(cnxn, cnxn.getSessionTimeout()); } private void addCnxn(NIOServerCnxn cnxn) throws IOException { InetAddress addr = cnxn.getSocketAddress(); if (addr == null) { throw new IOException("Socket of " + cnxn + " has been closed"); } Set set = ipMap.get(addr); if (set == null) { // in general we will see 1 connection from each // host, setting the initial cap to 2 allows us // to minimize mem usage in the common case // of 1 entry -- we need to set the initial cap // to 2 to avoid rehash when the first entry is added // Construct a ConcurrentHashSet using a ConcurrentHashMap set = Collections.newSetFromMap(new ConcurrentHashMap<>(2)); // Put the new set in the map, but only if another thread // hasn't beaten us to it Set existingSet = ipMap.putIfAbsent(addr, set); if (existingSet != null) { set = existingSet; } } set.add(cnxn); cnxns.add(cnxn); touchCnxn(cnxn); } protected NIOServerCnxn createConnection(SocketChannel sock, SelectionKey sk, SelectorThread selectorThread) throws IOException { return new NIOServerCnxn(zkServer, sock, sk, this, selectorThread); } private int getClientCnxnCount(InetAddress cl) { Set s = ipMap.get(cl); if (s == null) { return 0; } return s.size(); } /** * clear all the connections in the selector * */ @Override @SuppressWarnings("unchecked") public void closeAll(ServerCnxn.DisconnectReason reason) { // clear all the connections on which we are selecting for (ServerCnxn cnxn : cnxns) { try { // This will remove the cnxn from cnxns cnxn.close(reason); } catch (Exception e) { LOG.warn( "Ignoring exception closing cnxn session id 0x{}", Long.toHexString(cnxn.getSessionId()), e); } } } public void stop() { stopped = true; // Stop queuing connection attempts try { ss.close(); } catch (IOException e) { LOG.warn("Error closing listen socket", e); } if (acceptThread != null) { if (acceptThread.isAlive()) { acceptThread.wakeupSelector(); } else { acceptThread.closeSelector(); } } if (expirerThread != null) { expirerThread.interrupt(); } for (SelectorThread thread : selectorThreads) { if (thread.isAlive()) { thread.wakeupSelector(); } else { thread.closeSelector(); } } if (workerPool != null) { workerPool.stop(); } } public void shutdown() { try { // close listen socket and signal selector threads to stop stop(); // wait for selector and worker threads to shutdown join(); // close all open connections closeAll(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN); if (login != null) { login.shutdown(); } } catch (InterruptedException e) { LOG.warn("Ignoring interrupted exception during shutdown", e); } catch (Exception e) { LOG.warn("Ignoring unexpected exception during shutdown", e); } if (zkServer != null) { zkServer.shutdown(); } } @Override public void join() throws InterruptedException { if (acceptThread != null) { acceptThread.join(); } for (SelectorThread thread : selectorThreads) { thread.join(); } if (workerPool != null) { workerPool.join(workerShutdownTimeoutMS); } } @Override public Iterable getConnections() { return cnxns; } public void dumpConnections(PrintWriter pwriter) { pwriter.print("Connections "); cnxnExpiryQueue.dump(pwriter); } @Override public void resetAllConnectionStats() { // No need to synchronize since cnxns is backed by a ConcurrentHashMap for (ServerCnxn c : cnxns) { c.resetStats(); } } @Override public Iterable> getAllConnectionInfo(boolean brief) { HashSet> info = new HashSet<>(); // No need to synchronize since cnxns is backed by a ConcurrentHashMap for (ServerCnxn c : cnxns) { info.add(c.getConnectionInfo(brief)); } return info; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Ne0100644 0000000 0000000 00000000160 15051152474 032631 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxn.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxn.ja0100644 0000000 0000000 00000064052 15051152474 034244 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static java.nio.charset.StandardCharsets.UTF_8; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.io.BufferedWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.security.cert.Certificate; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.jute.BinaryInputArchive; import org.apache.jute.Record; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.WatcherEvent; import org.apache.zookeeper.server.command.CommandExecutor; import org.apache.zookeeper.server.command.FourLetterCommands; import org.apache.zookeeper.server.command.NopCommand; import org.apache.zookeeper.server.command.SetTraceMaskCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NettyServerCnxn extends ServerCnxn { private static final Logger LOG = LoggerFactory.getLogger(NettyServerCnxn.class); private final Channel channel; private CompositeByteBuf queuedBuffer; private final AtomicBoolean throttled = new AtomicBoolean(false); private ByteBuffer bb; private final ByteBuffer bbLen = ByteBuffer.allocate(4); private long sessionId; private int sessionTimeout; private Certificate[] clientChain; private volatile boolean closingChannel; private final NettyServerCnxnFactory factory; private boolean initialized; public int readIssuedAfterReadComplete; private volatile HandshakeState handshakeState = HandshakeState.NONE; public enum HandshakeState { NONE, STARTED, FINISHED } NettyServerCnxn(Channel channel, ZooKeeperServer zks, NettyServerCnxnFactory factory) { super(zks); this.channel = channel; this.closingChannel = false; this.factory = factory; if (this.factory.login != null) { this.zooKeeperSaslServer = new ZooKeeperSaslServer(factory.login); } InetAddress addr = ((InetSocketAddress) channel.remoteAddress()).getAddress(); addAuthInfo(new Id("ip", addr.getHostAddress())); } /** * Close the cnxn and remove it from the factory cnxns list. */ @Override public void close(DisconnectReason reason) { disconnectReason = reason; close(); } public void close() { closingChannel = true; LOG.debug("close called for session id: 0x{}", Long.toHexString(sessionId)); setStale(); // ZOOKEEPER-2743: // Always unregister connection upon close to prevent // connection bean leak under certain race conditions. factory.unregisterConnection(this); // if this is not in cnxns then it's already closed if (!factory.cnxns.remove(this)) { LOG.debug("cnxns size:{}", factory.cnxns.size()); if (channel.isOpen()) { channel.close(); } return; } LOG.debug("close in progress for session id: 0x{}", Long.toHexString(sessionId)); factory.removeCnxnFromSessionMap(this); factory.removeCnxnFromIpMap(this, ((InetSocketAddress) channel.remoteAddress()).getAddress()); if (zkServer != null) { zkServer.removeCnxn(this); } if (channel.isOpen()) { // Since we don't check on the futures created by write calls to the channel complete we need to make sure // that all writes have been completed before closing the channel or we risk data loss // See: http://lists.jboss.org/pipermail/netty-users/2009-August/001122.html channel.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { future.channel().close().addListener(f -> releaseQueuedBuffer()); } }); } else { ServerMetrics.getMetrics().CONNECTION_DROP_COUNT.add(1); channel.eventLoop().execute(this::releaseQueuedBuffer); } } @Override public long getSessionId() { return sessionId; } @Override public int getSessionTimeout() { return sessionTimeout; } @Override public void process(WatchedEvent event, List znodeAcl) { try { zkServer.checkACL(this, znodeAcl, ZooDefs.Perms.READ, getAuthInfo(), event.getPath(), null); } catch (KeeperException.NoAuthException e) { if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "Not delivering event " + event + " to 0x" + Long.toHexString(this.sessionId) + " (filtered by ACL)"); } return; } ReplyHeader h = new ReplyHeader(ClientCnxn.NOTIFICATION_XID, event.getZxid(), 0); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "Deliver event " + event + " to 0x" + Long.toHexString(this.sessionId) + " through " + this); } // Convert WatchedEvent to a type that can be sent over the wire WatcherEvent e = event.getWrapper(); try { int responseSize = sendResponse(h, e, "notification"); ServerMetrics.getMetrics().WATCH_BYTES.add(responseSize); } catch (IOException e1) { LOG.debug("Problem sending to {}", getRemoteSocketAddress(), e1); close(); } } @Override public int sendResponse(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) throws IOException { // cacheKey and stat are used in caching, which is not // implemented here. Implementation example can be found in NIOServerCnxn. if (closingChannel || !channel.isOpen()) { return 0; } ByteBuffer[] bb = serialize(h, r, tag, cacheKey, stat, opCode); int responseSize = bb[0].getInt(); bb[0].rewind(); sendBuffer(bb); decrOutstandingAndCheckThrottle(h); return responseSize; } @Override public void setSessionId(long sessionId) { this.sessionId = sessionId; factory.addSession(sessionId, this); } // Use a single listener instance to reduce GC private final GenericFutureListener> onSendBufferDoneListener = f -> { if (f.isSuccess()) { packetSent(); } }; @Override public void sendBuffer(ByteBuffer... buffers) { if (buffers.length == 1 && buffers[0] == ServerCnxnFactory.closeConn) { close(DisconnectReason.CLIENT_CLOSED_CONNECTION); return; } channel.writeAndFlush(Unpooled.wrappedBuffer(buffers)).addListener(onSendBufferDoneListener); } /** * This class wraps the sendBuffer method of NIOServerCnxn. It is * responsible for chunking up the response to a client. Rather * than cons'ing up a response fully in memory, which may be large * for some commands, this class chunks up the result. */ private class SendBufferWriter extends Writer { private StringBuffer sb = new StringBuffer(); /** * Check if we are ready to send another chunk. * @param force force sending, even if not a full chunk */ private void checkFlush(boolean force) { if ((force && sb.length() > 0) || sb.length() > 2048) { sendBuffer(ByteBuffer.wrap(sb.toString().getBytes(UTF_8))); // clear our internal buffer sb.setLength(0); } } @Override public void close() throws IOException { if (sb == null) { return; } checkFlush(true); sb = null; // clear out the ref to ensure no reuse } @Override public void flush() throws IOException { checkFlush(true); } @Override public void write(char[] cbuf, int off, int len) throws IOException { sb.append(cbuf, off, len); checkFlush(false); } } /** Return if four letter word found and responded to, otw false **/ private boolean checkFourLetterWord(final Channel channel, ByteBuf message, final int len) { // We take advantage of the limited size of the length to look // for cmds. They are all 4-bytes which fits inside of an int if (!FourLetterCommands.isKnown(len)) { return false; } String cmd = FourLetterCommands.getCommandString(len); // Stops automatic reads of incoming data on this channel. We don't // expect any more traffic from the client when processing a 4LW // so this shouldn't break anything. channel.config().setAutoRead(false); packetReceived(4); final PrintWriter pwriter = new PrintWriter(new BufferedWriter(new SendBufferWriter())); // ZOOKEEPER-2693: don't execute 4lw if it's not enabled. if (!FourLetterCommands.isEnabled(cmd)) { LOG.debug("Command {} is not executed because it is not in the whitelist.", cmd); NopCommand nopCmd = new NopCommand( pwriter, this, cmd + " is not executed because it is not in the whitelist."); nopCmd.start(); return true; } LOG.info("Processing {} command from {}", cmd, channel.remoteAddress()); if (len == FourLetterCommands.setTraceMaskCmd) { ByteBuffer mask = ByteBuffer.allocate(8); message.readBytes(mask); mask.flip(); long traceMask = mask.getLong(); ZooTrace.setTextTraceLevel(traceMask); SetTraceMaskCommand setMask = new SetTraceMaskCommand(pwriter, this, traceMask); setMask.start(); return true; } else { CommandExecutor commandExecutor = new CommandExecutor(); return commandExecutor.execute(this, pwriter, len, zkServer, factory); } } /** * Helper that throws an IllegalStateException if the current thread is not * executing in the channel's event loop thread. * @param callerMethodName the name of the calling method to add to the exception message. */ private void checkIsInEventLoop(String callerMethodName) { if (!channel.eventLoop().inEventLoop()) { throw new IllegalStateException(callerMethodName + "() called from non-EventLoop thread"); } } /** * Appends buf to queuedBuffer. Does not duplicate buf * or call any flavor of {@link ByteBuf#retain()}. Caller must ensure that buf * is not owned by anyone else, as this call transfers ownership of buf to the * queuedBuffer. * * This method should only be called from the event loop thread. * @param buf the buffer to append to the queue. */ private void appendToQueuedBuffer(ByteBuf buf) { checkIsInEventLoop("appendToQueuedBuffer"); if (queuedBuffer.numComponents() == queuedBuffer.maxNumComponents()) { // queuedBuffer has reached its component limit, so combine the existing components. queuedBuffer.consolidate(); } queuedBuffer.addComponent(true, buf); ServerMetrics.getMetrics().NETTY_QUEUED_BUFFER.add(queuedBuffer.capacity()); } /** * Process incoming message. This should only be called from the event * loop thread. * Note that this method does not call buf.release(). The caller * is responsible for making sure the buf is released after this method * returns. * @param buf the message bytes to process. */ void processMessage(ByteBuf buf) { checkIsInEventLoop("processMessage"); LOG.debug("0x{} queuedBuffer: {}", Long.toHexString(sessionId), queuedBuffer); if (closingChannel) { LOG.debug("Drop incoming message during connection closing for session 0x{}", Long.toHexString(sessionId)); return; } if (LOG.isTraceEnabled()) { LOG.trace("0x{} buf {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(buf)); } if (throttled.get()) { LOG.debug("Received message while throttled"); // we are throttled, so we need to queue if (queuedBuffer == null) { LOG.debug("allocating queue"); queuedBuffer = channel.alloc().compositeBuffer(); } appendToQueuedBuffer(buf.retainedDuplicate()); if (LOG.isTraceEnabled()) { LOG.trace("0x{} queuedBuffer {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(queuedBuffer)); } } else { LOG.debug("not throttled"); if (queuedBuffer != null) { appendToQueuedBuffer(buf.retainedDuplicate()); processQueuedBuffer(); } else { receiveMessage(buf); // Have to check !closingChannel, because an error in // receiveMessage() could have led to close() being called. if (!closingChannel && buf.isReadable()) { if (LOG.isTraceEnabled()) { LOG.trace("Before copy {}", buf); } if (queuedBuffer == null) { queuedBuffer = channel.alloc().compositeBuffer(); } appendToQueuedBuffer(buf.retainedSlice(buf.readerIndex(), buf.readableBytes())); if (LOG.isTraceEnabled()) { LOG.trace("Copy is {}", queuedBuffer); LOG.trace("0x{} queuedBuffer {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(queuedBuffer)); } } } } } /** * Try to process previously queued message. This should only be called * from the event loop thread. */ void processQueuedBuffer() { checkIsInEventLoop("processQueuedBuffer"); if (queuedBuffer != null) { if (LOG.isTraceEnabled()) { LOG.trace("processing queue 0x{} queuedBuffer {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(queuedBuffer)); } receiveMessage(queuedBuffer); if (closingChannel) { // close() could have been called if receiveMessage() failed LOG.debug("Processed queue - channel closed, dropping remaining bytes"); } else if (!queuedBuffer.isReadable()) { LOG.debug("Processed queue - no bytes remaining"); releaseQueuedBuffer(); } else { LOG.debug("Processed queue - bytes remaining"); // Try to reduce memory consumption by freeing up buffer space // which is no longer needed. queuedBuffer.discardReadComponents(); } } else { LOG.debug("queue empty"); } } /** * Clean up queued buffer once it's no longer needed. This should only be * called from the event loop thread. */ private void releaseQueuedBuffer() { checkIsInEventLoop("releaseQueuedBuffer"); if (queuedBuffer != null) { queuedBuffer.release(); queuedBuffer = null; } } /** * Receive a message, which can come from the queued buffer or from a new * buffer coming in over the channel. This should only be called from the * event loop thread. * Note that this method does not call message.release(). The * caller is responsible for making sure the message is released after this * method returns. * @param message the message bytes to process. */ private void receiveMessage(ByteBuf message) { checkIsInEventLoop("receiveMessage"); try { while (message.isReadable() && !throttled.get()) { if (bb != null) { if (LOG.isTraceEnabled()) { LOG.trace("message readable {} bb len {} {}", message.readableBytes(), bb.remaining(), bb); ByteBuffer dat = bb.duplicate(); dat.flip(); LOG.trace("0x{} bb {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(Unpooled.wrappedBuffer(dat))); } if (bb.remaining() > message.readableBytes()) { int newLimit = bb.position() + message.readableBytes(); bb.limit(newLimit); } message.readBytes(bb); bb.limit(bb.capacity()); if (LOG.isTraceEnabled()) { LOG.trace("after readBytes message readable {} bb len {} {}", message.readableBytes(), bb.remaining(), bb); ByteBuffer dat = bb.duplicate(); dat.flip(); LOG.trace("after readbytes 0x{} bb {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(Unpooled.wrappedBuffer(dat))); } if (bb.remaining() == 0) { bb.flip(); packetReceived(4 + bb.remaining()); ZooKeeperServer zks = this.zkServer; if (zks == null || !zks.isRunning()) { throw new IOException("ZK down"); } if (initialized) { RequestHeader h = new RequestHeader(); ByteBufferInputStream.byteBuffer2Record(bb, h); RequestRecord request = RequestRecord.fromBytes(bb.slice()); zks.processPacket(this, h, request); } else { LOG.debug("got conn req request from {}", getRemoteSocketAddress()); BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb)); ConnectRequest request = protocolManager.deserializeConnectRequest(bia); zks.processConnectRequest(this, request); initialized = true; } bb = null; } } else { if (LOG.isTraceEnabled()) { LOG.trace("message readable {} bblenrem {}", message.readableBytes(), bbLen.remaining()); ByteBuffer dat = bbLen.duplicate(); dat.flip(); LOG.trace("0x{} bbLen {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(Unpooled.wrappedBuffer(dat))); } if (message.readableBytes() < bbLen.remaining()) { bbLen.limit(bbLen.position() + message.readableBytes()); } message.readBytes(bbLen); bbLen.limit(bbLen.capacity()); if (bbLen.remaining() == 0) { bbLen.flip(); if (LOG.isTraceEnabled()) { LOG.trace("0x{} bbLen {}", Long.toHexString(sessionId), ByteBufUtil.hexDump(Unpooled.wrappedBuffer(bbLen))); } int len = bbLen.getInt(); if (LOG.isTraceEnabled()) { LOG.trace("0x{} bbLen len is {}", Long.toHexString(sessionId), len); } bbLen.clear(); if (!initialized) { if (checkFourLetterWord(channel, message, len)) { return; } } if (len < 0 || len > BinaryInputArchive.maxBuffer) { throw new IOException("Len error " + len); } ZooKeeperServer zks = this.zkServer; if (zks == null || !zks.isRunning()) { LOG.info("Closing connection to {} because the server is not ready", getRemoteSocketAddress()); close(DisconnectReason.IO_EXCEPTION); return; } // checkRequestSize will throw IOException if request is rejected zks.checkRequestSizeWhenReceivingMessage(len); bb = ByteBuffer.allocate(len); } } } } catch (IOException e) { LOG.warn("Closing connection to {}", getRemoteSocketAddress(), e); close(DisconnectReason.IO_EXCEPTION); } catch (ClientCnxnLimitException e) { // Common case exception, print at debug level ServerMetrics.getMetrics().CONNECTION_REJECTED.add(1); LOG.debug("Closing connection to {}", getRemoteSocketAddress(), e); close(DisconnectReason.CLIENT_RATE_LIMIT); } } /** * An event that triggers a change in the channel's read setting. * Used for throttling. By using an enum we can treat the two values as * singletons and compare with ==. */ enum ReadEvent { DISABLE, ENABLE } /** * Note that the netty implementation ignores the waitDisableRecv * parameter and is always asynchronous. * @param waitDisableRecv ignored by this implementation. */ @Override public void disableRecv(boolean waitDisableRecv) { if (throttled.compareAndSet(false, true)) { LOG.debug("Throttling - disabling recv {}", this); channel.pipeline().fireUserEventTriggered(ReadEvent.DISABLE); } } @Override public void enableRecv() { if (throttled.compareAndSet(true, false)) { LOG.debug("Sending unthrottle event {}", this); channel.pipeline().fireUserEventTriggered(ReadEvent.ENABLE); } } @Override public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; } @Override public int getInterestOps() { // This might not be 100% right, but it's only used for printing // connection info in the netty implementation so it's probably ok. if (channel == null || !channel.isOpen()) { return 0; } int interestOps = 0; if (!throttled.get()) { interestOps |= SelectionKey.OP_READ; } if (!channel.isWritable()) { // OP_READ means "can read", but OP_WRITE means "cannot write", // it's weird. interestOps |= SelectionKey.OP_WRITE; } return interestOps; } @Override public InetSocketAddress getRemoteSocketAddress() { return (InetSocketAddress) channel.remoteAddress(); } /** Send close connection packet to the client. */ @Override public void sendCloseSession() { sendBuffer(ServerCnxnFactory.closeConn); } @Override protected ServerStats serverStats() { if (zkServer == null) { return null; } return zkServer.serverStats(); } @Override public boolean isSecure() { return factory.secure; } @Override public Certificate[] getClientCertificateChain() { if (clientChain == null) { return null; } return Arrays.copyOf(clientChain, clientChain.length); } @Override public void setClientCertificateChain(Certificate[] chain) { if (chain == null) { clientChain = null; } else { clientChain = Arrays.copyOf(chain, chain.length); } } // For tests and NettyServerCnxnFactory only, thus package-private. Channel getChannel() { return channel; } public int getQueuedReadableBytes() { checkIsInEventLoop("getQueuedReadableBytes"); if (queuedBuffer != null) { return queuedBuffer.readableBytes(); } return 0; } public void setHandshakeState(HandshakeState state) { this.handshakeState = state; } public HandshakeState getHandshakeState() { return this.handshakeState; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Ne0100644 0000000 0000000 00000000167 15051152474 032640 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NettyServerCnxnFac0100644 0000000 0000000 00000107073 15051152474 034266 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoopGroup; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.ChannelGroupFuture; import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.socket.SocketChannel; import io.netty.handler.ssl.OptionalSslHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; import io.netty.util.AttributeKey; import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.DefaultEventExecutor; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.NettyUtils; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.NettyServerCnxn.HandshakeState; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.auth.X509AuthenticationProvider; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NettyServerCnxnFactory extends ServerCnxnFactory { private static final Logger LOG = LoggerFactory.getLogger(NettyServerCnxnFactory.class); /** * Allow client-server sockets to accept both SSL and plaintext connections */ public static final String PORT_UNIFICATION_KEY = "zookeeper.client.portUnification"; public static final String EARLY_DROP_SECURE_CONNECTION_HANDSHAKES = "zookeeper.netty.server.earlyDropSecureConnectionHandshakes"; private final boolean shouldUsePortUnification; /** * The first byte in TLS protocol is the content type of the subsequent record. * Handshakes use value 22 (0x16) so the first byte offered on any TCP connection * attempting to establish a TLS connection will be this value. * https://tools.ietf.org/html/rfc8446#page-79 */ private static final byte TLS_HANDSHAKE_RECORD_TYPE = 0x16; private final AtomicInteger outstandingHandshake = new AtomicInteger(); public static final String OUTSTANDING_HANDSHAKE_LIMIT = "zookeeper.netty.server.outstandingHandshake.limit"; private int outstandingHandshakeLimit; private boolean handshakeThrottlingEnabled; public void setOutstandingHandshakeLimit(int limit) { outstandingHandshakeLimit = limit; handshakeThrottlingEnabled = (secure || shouldUsePortUnification) && outstandingHandshakeLimit > 0; LOG.info("handshakeThrottlingEnabled = {}, {} = {}", handshakeThrottlingEnabled, OUTSTANDING_HANDSHAKE_LIMIT, outstandingHandshakeLimit); } private final ServerBootstrap bootstrap; private Channel parentChannel; private final ChannelGroup allChannels = new DefaultChannelGroup("zkServerCnxns", new DefaultEventExecutor()); private final Map ipMap = new ConcurrentHashMap<>(); private InetSocketAddress localAddress; private int maxClientCnxns = 60; int listenBacklog = -1; private final ClientX509Util x509Util; public static final String NETTY_ADVANCED_FLOW_CONTROL = "zookeeper.netty.advancedFlowControl.enabled"; private boolean advancedFlowControlEnabled = false; private static final AttributeKey CONNECTION_ATTRIBUTE = AttributeKey.valueOf("NettyServerCnxn"); private static final AtomicReference TEST_ALLOCATOR = new AtomicReference<>(null); public static final String CLIENT_CERT_RELOAD_KEY = "zookeeper.client.certReload"; /** * A handler that detects whether the client would like to use * TLS or not and responds in kind. The first bytes are examined * for the static TLS headers to make the determination and * placed back in the stream with the correct ChannelHandler * instantiated. */ class DualModeSslHandler extends OptionalSslHandler { DualModeSslHandler(SslContext sslContext) { super(sslContext); } @Override protected void decode(ChannelHandlerContext context, ByteBuf in, List out) throws Exception { if (in.readableBytes() >= 5) { super.decode(context, in, out); } else if (in.readableBytes() > 0) { // It requires 5 bytes to detect a proper ssl connection. In the // case that the server receives fewer, check if we can fail to plaintext. // This will occur when for any four letter work commands. if (TLS_HANDSHAKE_RECORD_TYPE != in.getByte(0)) { LOG.debug("first byte {} does not match TLS handshake, failing to plaintext", in.getByte(0)); handleNonSsl(context); } } } /** * pulled directly from OptionalSslHandler to allow for access * @param context */ private void handleNonSsl(ChannelHandlerContext context) { ChannelHandler handler = this.newNonSslHandler(context); if (handler != null) { context.pipeline().replace(this, this.newNonSslHandlerName(), handler); } else { context.pipeline().remove(this); } } @Override protected SslHandler newSslHandler(ChannelHandlerContext context, SslContext sslContext) { NettyServerCnxn cnxn = Objects.requireNonNull(context.channel().attr(CONNECTION_ATTRIBUTE).get()); LOG.debug("creating ssl handler for session {}", cnxn.getSessionId()); SslHandler handler = super.newSslHandler(context, sslContext); Future handshakeFuture = handler.handshakeFuture(); handshakeFuture.addListener(new CertificateVerifier(handler, cnxn)); return handler; } @Override protected ChannelHandler newNonSslHandler(ChannelHandlerContext context) { NettyServerCnxn cnxn = Objects.requireNonNull(context.channel().attr(CONNECTION_ATTRIBUTE).get()); LOG.debug("creating plaintext handler for session {}", cnxn.getSessionId()); // Mark handshake finished if it's a insecure cnxn updateHandshakeCountIfStarted(cnxn); allChannels.add(context.channel()); addCnxn(cnxn); return super.newNonSslHandler(context); } } private void updateHandshakeCountIfStarted(NettyServerCnxn cnxn) { if (cnxn != null && cnxn.getHandshakeState() == HandshakeState.STARTED) { cnxn.setHandshakeState(HandshakeState.FINISHED); outstandingHandshake.addAndGet(-1); } } /** * This is an inner class since we need to extend ChannelDuplexHandler, but * NettyServerCnxnFactory already extends ServerCnxnFactory. By making it inner * this class gets access to the member variables and methods. */ @Sharable class CnxnChannelHandler extends ChannelDuplexHandler { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Channel active {}", ctx.channel()); } final Channel channel = ctx.channel(); if (limitTotalNumberOfCnxns()) { ServerMetrics.getMetrics().CONNECTION_REJECTED.add(1); channel.close(); return; } InetAddress addr = ((InetSocketAddress) channel.remoteAddress()).getAddress(); if (maxClientCnxns > 0 && getClientCnxnCount(addr) >= maxClientCnxns) { ServerMetrics.getMetrics().CONNECTION_REJECTED.add(1); LOG.warn("Too many connections from {} - max is {}", addr, maxClientCnxns); channel.close(); return; } NettyServerCnxn cnxn = new NettyServerCnxn(channel, zkServer, NettyServerCnxnFactory.this); ctx.channel().attr(CONNECTION_ATTRIBUTE).set(cnxn); // Check the zkServer assigned to the cnxn is still running, // close it before starting the heavy TLS handshake if (secure && !cnxn.isZKServerRunning()) { boolean earlyDropSecureConnectionHandshakes = Boolean.getBoolean(EARLY_DROP_SECURE_CONNECTION_HANDSHAKES); if (earlyDropSecureConnectionHandshakes) { LOG.info("Zookeeper server is not running, close the connection to {} before starting the TLS handshake", cnxn.getChannel().remoteAddress()); ServerMetrics.getMetrics().CNXN_CLOSED_WITHOUT_ZK_SERVER_RUNNING.add(1); channel.close(); return; } } if (handshakeThrottlingEnabled) { // Favor to check and throttling even in dual mode which // accepts both secure and insecure connections, since // it's more efficient than throttling when we know it's // a secure connection in DualModeSslHandler. // // From benchmark, this reduced around 15% reconnect time. int outstandingHandshakesNum = outstandingHandshake.addAndGet(1); if (outstandingHandshakesNum > outstandingHandshakeLimit) { outstandingHandshake.addAndGet(-1); channel.close(); ServerMetrics.getMetrics().TLS_HANDSHAKE_EXCEEDED.add(1); } else { cnxn.setHandshakeState(HandshakeState.STARTED); } } if (secure) { SslHandler sslHandler = ctx.pipeline().get(SslHandler.class); Future handshakeFuture = sslHandler.handshakeFuture(); handshakeFuture.addListener(new CertificateVerifier(sslHandler, cnxn)); } else if (!shouldUsePortUnification) { allChannels.add(ctx.channel()); addCnxn(cnxn); } if (ctx.channel().pipeline().get(SslHandler.class) == null) { if (zkServer != null) { SocketAddress remoteAddress = cnxn.getChannel().remoteAddress(); if (remoteAddress != null && !((InetSocketAddress) remoteAddress).getAddress().isLoopbackAddress()) { LOG.trace("NettyChannelHandler channelActive: remote={} local={}", remoteAddress, cnxn.getChannel().localAddress()); zkServer.serverStats().incrementNonMTLSRemoteConnCount(); } else { zkServer.serverStats().incrementNonMTLSLocalConnCount(); } } else { LOG.trace("Opened non-TLS connection from {} but zkServer is not running", cnxn.getChannel().remoteAddress()); } } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Channel inactive {}", ctx.channel()); } allChannels.remove(ctx.channel()); NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).getAndSet(null); if (cnxn != null) { if (LOG.isTraceEnabled()) { LOG.trace("Channel inactive caused close {}", cnxn); } updateHandshakeCountIfStarted(cnxn); cnxn.close(ServerCnxn.DisconnectReason.CHANNEL_DISCONNECTED); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { LOG.warn("Exception caught", cause); NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).getAndSet(null); if (cnxn != null) { LOG.debug("Closing {}", cnxn); updateHandshakeCountIfStarted(cnxn); cnxn.close(ServerCnxn.DisconnectReason.CHANNEL_CLOSED_EXCEPTION); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { try { if (evt == NettyServerCnxn.ReadEvent.ENABLE) { LOG.debug("Received ReadEvent.ENABLE"); NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).get(); // TODO: Not sure if cnxn can be null here. It becomes null if channelInactive() // or exceptionCaught() trigger, but it's unclear to me if userEventTriggered() can run // after either of those. Check for null just to be safe ... if (cnxn != null) { if (cnxn.getQueuedReadableBytes() > 0) { cnxn.processQueuedBuffer(); if (advancedFlowControlEnabled && cnxn.getQueuedReadableBytes() == 0) { // trigger a read if we have consumed all // backlog ctx.read(); LOG.debug("Issued a read after queuedBuffer drained"); } } } if (!advancedFlowControlEnabled) { ctx.channel().config().setAutoRead(true); } } else if (evt == NettyServerCnxn.ReadEvent.DISABLE) { LOG.debug("Received ReadEvent.DISABLE"); ctx.channel().config().setAutoRead(false); } } finally { ReferenceCountUtil.release(evt); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { if (LOG.isTraceEnabled()) { LOG.trace("message received called {}", msg); } try { LOG.debug("New message {} from {}", msg, ctx.channel()); NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).get(); if (cnxn == null) { LOG.error("channelRead() on a closed or closing NettyServerCnxn"); } else { cnxn.processMessage((ByteBuf) msg); } } catch (Exception ex) { LOG.error("Unexpected exception in receive", ex); throw ex; } } finally { ReferenceCountUtil.release(msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { if (advancedFlowControlEnabled) { NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).get(); if (cnxn != null && cnxn.getQueuedReadableBytes() == 0 && cnxn.readIssuedAfterReadComplete == 0) { ctx.read(); LOG.debug("Issued a read since we do not have anything to consume after channelReadComplete"); } } ctx.fireChannelReadComplete(); } // Use a single listener instance to reduce GC // Note: this listener is only added when LOG.isTraceEnabled() is true, // so it should not do any work other than trace logging. private final GenericFutureListener> onWriteCompletedTracer = (f) -> { if (LOG.isTraceEnabled()) { LOG.trace("write success: {}", f.isSuccess()); } }; @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (LOG.isTraceEnabled()) { promise.addListener(onWriteCompletedTracer); } super.write(ctx, msg, promise); } } final class CertificateVerifier implements GenericFutureListener> { private final SslHandler sslHandler; private final NettyServerCnxn cnxn; CertificateVerifier(SslHandler sslHandler, NettyServerCnxn cnxn) { this.sslHandler = sslHandler; this.cnxn = cnxn; } /** * Only allow the connection to stay open if certificate passes auth */ public void operationComplete(Future future) { updateHandshakeCountIfStarted(cnxn); if (future.isSuccess()) { LOG.debug("Successful handshake with session 0x{}", Long.toHexString(cnxn.getSessionId())); SSLEngine eng = sslHandler.engine(); // Don't try to verify certificate if we didn't ask client to present one if (eng.getNeedClientAuth() || eng.getWantClientAuth()) { SSLSession session = eng.getSession(); try { cnxn.setClientCertificateChain(session.getPeerCertificates()); } catch (SSLPeerUnverifiedException e) { if (eng.getNeedClientAuth()) { // Certificate was requested but not present LOG.error("Error getting peer certificates", e); cnxn.close(); return; } else { // Certificate was requested but was optional // TODO: what auth info should we set on the connection? final Channel futureChannel = future.getNow(); allChannels.add(Objects.requireNonNull(futureChannel)); addCnxn(cnxn); return; } } catch (Exception e) { LOG.error("Error getting peer certificates", e); cnxn.close(); return; } String authProviderProp = System.getProperty(x509Util.getSslAuthProviderProperty(), "x509"); X509AuthenticationProvider authProvider = (X509AuthenticationProvider) ProviderRegistry.getProvider(authProviderProp); if (authProvider == null) { LOG.error("X509 Auth provider not found: {}", authProviderProp); cnxn.close(ServerCnxn.DisconnectReason.AUTH_PROVIDER_NOT_FOUND); return; } KeeperException.Code code = authProvider.handleAuthentication(cnxn, null); if (KeeperException.Code.OK != code) { zkServer.serverStats().incrementAuthFailedCount(); LOG.error("Authentication failed for session 0x{}", Long.toHexString(cnxn.getSessionId())); cnxn.close(ServerCnxn.DisconnectReason.SASL_AUTH_FAILURE); return; } } final Channel futureChannel = future.getNow(); allChannels.add(Objects.requireNonNull(futureChannel)); addCnxn(cnxn); } else { zkServer.serverStats().incrementAuthFailedCount(); LOG.error("Unsuccessful handshake with session 0x{}", Long.toHexString(cnxn.getSessionId())); ServerMetrics.getMetrics().UNSUCCESSFUL_HANDSHAKE.add(1); cnxn.close(ServerCnxn.DisconnectReason.FAILED_HANDSHAKE); } } } @Sharable static class ReadIssuedTrackingHandler extends ChannelDuplexHandler { @Override public void read(ChannelHandlerContext ctx) throws Exception { NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).get(); if (cnxn != null) { cnxn.readIssuedAfterReadComplete++; } ctx.read(); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { NettyServerCnxn cnxn = ctx.channel().attr(CONNECTION_ATTRIBUTE).get(); if (cnxn != null) { cnxn.readIssuedAfterReadComplete = 0; } ctx.fireChannelReadComplete(); } } CnxnChannelHandler channelHandler = new CnxnChannelHandler(); ReadIssuedTrackingHandler readIssuedTrackingHandler = new ReadIssuedTrackingHandler(); private ServerBootstrap configureBootstrapAllocator(ServerBootstrap bootstrap) { ByteBufAllocator testAllocator = TEST_ALLOCATOR.get(); if (testAllocator != null) { return bootstrap.option(ChannelOption.ALLOCATOR, testAllocator) .childOption(ChannelOption.ALLOCATOR, testAllocator); } else { return bootstrap; } } NettyServerCnxnFactory() { x509Util = new ClientX509Util(); boolean useClientReload = Boolean.getBoolean(CLIENT_CERT_RELOAD_KEY); LOG.info("{}={}", CLIENT_CERT_RELOAD_KEY, useClientReload); if (useClientReload) { try { x509Util.enableCertFileReloading(); } catch (IOException e) { LOG.error("unable to set up client certificate reload filewatcher", e); useClientReload = false; } } boolean usePortUnification = Boolean.getBoolean(PORT_UNIFICATION_KEY); LOG.info("{}={}", PORT_UNIFICATION_KEY, usePortUnification); if (usePortUnification) { try { QuorumPeerConfig.configureSSLAuth(); } catch (QuorumPeerConfig.ConfigException e) { LOG.error("unable to set up SslAuthProvider, turning off client port unification", e); usePortUnification = false; } } this.shouldUsePortUnification = usePortUnification; this.advancedFlowControlEnabled = Boolean.getBoolean(NETTY_ADVANCED_FLOW_CONTROL); LOG.info("{} = {}", NETTY_ADVANCED_FLOW_CONTROL, this.advancedFlowControlEnabled); setOutstandingHandshakeLimit(Integer.getInteger(OUTSTANDING_HANDSHAKE_LIMIT, -1)); EventLoopGroup bossGroup = NettyUtils.newNioOrEpollEventLoopGroup(NettyUtils.getClientReachableLocalInetAddressCount()); EventLoopGroup workerGroup = NettyUtils.newNioOrEpollEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup, workerGroup) .channel(NettyUtils.nioOrEpollServerSocketChannel()) // parent channel options .option(ChannelOption.SO_REUSEADDR, true) // child channels options .childOption(ChannelOption.TCP_NODELAY, true) .childOption(ChannelOption.SO_LINGER, -1) .childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (advancedFlowControlEnabled) { pipeline.addLast(readIssuedTrackingHandler); } if (secure) { initSSL(pipeline, false); } else if (shouldUsePortUnification) { initSSL(pipeline, true); } pipeline.addLast("servercnxnfactory", channelHandler); } }); this.bootstrap = configureBootstrapAllocator(bootstrap); this.bootstrap.validate(); } private synchronized void initSSL(ChannelPipeline p, boolean supportPlaintext) throws X509Exception, SSLException { String authProviderProp = System.getProperty(x509Util.getSslAuthProviderProperty()); SslContext nettySslContext; if (authProviderProp == null) { nettySslContext = x509Util.createNettySslContextForServer(new ZKConfig()); } else { X509AuthenticationProvider authProvider = (X509AuthenticationProvider) ProviderRegistry.getProvider( System.getProperty(x509Util.getSslAuthProviderProperty(), "x509")); if (authProvider == null) { LOG.error("Auth provider not found: {}", authProviderProp); throw new SSLContextException("Could not create SSLContext with specified auth provider: " + authProviderProp); } nettySslContext = x509Util.createNettySslContextForServer( new ZKConfig(), authProvider.getKeyManager(), authProvider.getTrustManager()); } if (supportPlaintext) { p.addLast("ssl", new DualModeSslHandler(nettySslContext)); LOG.debug("dual mode SSL handler added for channel: {}", p.channel()); } else { p.addLast("ssl", nettySslContext.newHandler(p.channel().alloc())); LOG.debug("SSL handler added for channel: {}", p.channel()); } } @Override public void closeAll(ServerCnxn.DisconnectReason reason) { LOG.debug("closeAll()"); // clear all the connections on which we are selecting int length = cnxns.size(); for (ServerCnxn cnxn : cnxns) { try { // This will remove the cnxn from cnxns cnxn.close(reason); } catch (Exception e) { LOG.warn("Ignoring exception closing cnxn sessionid 0x{}", Long.toHexString(cnxn.getSessionId()), e); } } LOG.debug("allChannels size: {} cnxns size: {}", allChannels.size(), length); } @Override public void configure(InetSocketAddress addr, int maxClientCnxns, int backlog, boolean secure) throws IOException { configureSaslLogin(); initMaxCnxns(); localAddress = addr; this.maxClientCnxns = maxClientCnxns; this.secure = secure; this.listenBacklog = backlog; LOG.info("configure {} secure: {} on addr {}", this, secure, addr); } /** {@inheritDoc} */ public int getMaxClientCnxnsPerHost() { return maxClientCnxns; } /** {@inheritDoc} */ public void setMaxClientCnxnsPerHost(int max) { maxClientCnxns = max; } /** {@inheritDoc} */ public int getSocketListenBacklog() { return listenBacklog; } @Override public int getLocalPort() { return localAddress.getPort(); } private boolean killed; // use synchronized(this) to access @Override public void join() throws InterruptedException { synchronized (this) { while (!killed) { wait(); } } } @Override public void shutdown() { synchronized (this) { if (killed) { LOG.info("already shutdown {}", localAddress); return; } } LOG.info("shutdown called {}", localAddress); x509Util.close(); if (login != null) { login.shutdown(); } final EventLoopGroup bossGroup = bootstrap.config().group(); final EventLoopGroup workerGroup = bootstrap.config().childGroup(); // null if factory never started if (parentChannel != null) { ChannelFuture parentCloseFuture = parentChannel.close(); if (bossGroup != null) { parentCloseFuture.addListener(future -> { bossGroup.shutdownGracefully(); }); } closeAll(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN); ChannelGroupFuture allChannelsCloseFuture = allChannels.close(); if (workerGroup != null) { allChannelsCloseFuture.addListener(future -> { workerGroup.shutdownGracefully(); }); } } else { if (bossGroup != null) { bossGroup.shutdownGracefully(); } if (workerGroup != null) { workerGroup.shutdownGracefully(); } } if (zkServer != null) { zkServer.shutdown(); } synchronized (this) { killed = true; notifyAll(); } } @Override public void start() { if (listenBacklog != -1) { bootstrap.option(ChannelOption.SO_BACKLOG, listenBacklog); } LOG.info("binding to port {}", localAddress); parentChannel = bootstrap.bind(localAddress).syncUninterruptibly().channel(); // Port changes after bind() if the original port was 0, update // localAddress to get the real port. localAddress = (InetSocketAddress) parentChannel.localAddress(); LOG.info("bound to port {}", getLocalPort()); } public void reconfigure(InetSocketAddress addr) { LOG.info("binding to port {}, {}", addr, localAddress); if (addr != null && localAddress != null) { if (addr.equals(localAddress) || (addr.getAddress().isAnyLocalAddress() && localAddress.getAddress().isAnyLocalAddress() && addr.getPort() == localAddress.getPort())) { LOG.info("address is the same, skip rebinding"); return; } } Channel oldChannel = parentChannel; try { parentChannel = bootstrap.bind(addr).syncUninterruptibly().channel(); // Port changes after bind() if the original port was 0, update // localAddress to get the real port. localAddress = (InetSocketAddress) parentChannel.localAddress(); LOG.info("bound to port {}", getLocalPort()); } catch (Exception e) { LOG.error("Error while reconfiguring", e); } finally { oldChannel.close(); } } @Override public void startup(ZooKeeperServer zks, boolean startServer) throws IOException, InterruptedException { start(); setZooKeeperServer(zks); if (startServer) { zks.startdata(); zks.startup(); } } @Override public Iterable getConnections() { return cnxns; } @Override public InetSocketAddress getLocalAddress() { return localAddress; } private void addCnxn(final NettyServerCnxn cnxn) { cnxns.add(cnxn); InetAddress addr = ((InetSocketAddress) cnxn.getChannel().remoteAddress()).getAddress(); ipMap.compute(addr, (a, cnxnCount) -> { if (cnxnCount == null) { cnxnCount = new AtomicInteger(); } cnxnCount.incrementAndGet(); return cnxnCount; }); } void removeCnxnFromIpMap(NettyServerCnxn cnxn, InetAddress remoteAddress) { ipMap.compute(remoteAddress, (addr, cnxnCount) -> { if (cnxnCount == null) { LOG.error("Unexpected remote address {} when removing cnxn {}", remoteAddress, cnxn); return null; } final int newValue = cnxnCount.decrementAndGet(); return newValue == 0 ? null : cnxnCount; }); } private int getClientCnxnCount(final InetAddress addr) { final AtomicInteger count = ipMap.get(addr); return count == null ? 0 : count.get(); } @Override public void resetAllConnectionStats() { // No need to synchronize since cnxns is backed by a ConcurrentHashMap for (ServerCnxn c : cnxns) { c.resetStats(); } } @Override public Iterable> getAllConnectionInfo(boolean brief) { Set> info = new HashSet<>(); // No need to synchronize since cnxns is backed by a ConcurrentHashMap for (ServerCnxn c : cnxns) { info.add(c.getConnectionInfo(brief)); } return info; } /** * Sets the test ByteBufAllocator. This allocator will be used by all * future instances of this class. * It is not recommended to use this method outside of testing. * @param allocator the ByteBufAllocator to use for all netty buffer * allocations. */ static void setTestAllocator(ByteBufAllocator allocator) { TEST_ALLOCATOR.set(allocator); } /** * Clears the test ByteBufAllocator. The default allocator will be used * by all future instances of this class. * It is not recommended to use this method outside of testing. */ static void clearTestAllocator() { TEST_ALLOCATOR.set(null); } // VisibleForTest public void setAdvancedFlowControlEnabled(boolean advancedFlowControlEnabled) { this.advancedFlowControlEnabled = advancedFlowControlEnabled; } // VisibleForTest public void setSecure(boolean secure) { this.secure = secure; } // VisibleForTest public Channel getParentChannel() { return parentChannel; } public int getOutstandingHandshakeNum() { return outstandingHandshake.get(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NodeHashMap.java0100644 0000000 0000000 00000005256 15051152474 033602 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Map; import java.util.Set; /** * The interface defined to manage the hash based on the entries in the * nodes map. */ public interface NodeHashMap { /** * Add the node into the map and update the digest with the new node. * * @param path the path of the node * @param node the actual node associated with this path */ DataNode put(String path, DataNode node); /** * Add the node into the map without update the digest. * * @param path the path of the node * @param node the actual node associated with this path */ DataNode putWithoutDigest(String path, DataNode node); /** * Return the data node associated with the path. * * @param path the path to read from */ DataNode get(String path); /** * Remove the path from the internal nodes map. * * @param path the path to remove * @return the node being removed */ DataNode remove(String path); /** * Return all the entries inside this map. */ Set> entrySet(); /** * Clear all the items stored inside this map. */ void clear(); /** * Return the size of the nodes stored in this map. */ int size(); /** * Called before we made the change on the node, which will clear * the digest associated with it. * * @param path the path being changed * @param node the node associated with the path */ void preChange(String path, DataNode node); /** * Called after making the changes on the node, which will update * the digest. * * @param path the path being changed * @param node the node associated with the path */ void postChange(String path, DataNode node); /** * Return the digest value. */ long getDigest(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_No0100644 0000000 0000000 00000000160 15051152474 032643 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NodeHashMapImpl.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/NodeHashMapImpl.ja0100644 0000000 0000000 00000007177 15051152474 034101 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.util.AdHash; /** * a simple wrapper to ConcurrentHashMap that recalculates a digest after * each mutation. */ public class NodeHashMapImpl implements NodeHashMap { private final ConcurrentHashMap nodes; private final boolean digestEnabled; private final DigestCalculator digestCalculator; private final AdHash hash; public NodeHashMapImpl(DigestCalculator digestCalculator) { this.digestCalculator = digestCalculator; nodes = new ConcurrentHashMap<>(); hash = new AdHash(); digestEnabled = ZooKeeperServer.isDigestEnabled(); } @Override public DataNode put(String path, DataNode node) { DataNode oldNode = nodes.put(path, node); addDigest(path, node); if (oldNode != null) { removeDigest(path, oldNode); } return oldNode; } @Override public DataNode putWithoutDigest(String path, DataNode node) { return nodes.put(path, node); } @Override public DataNode get(String path) { return nodes.get(path); } @Override public DataNode remove(String path) { DataNode oldNode = nodes.remove(path); if (oldNode != null) { removeDigest(path, oldNode); } return oldNode; } @Override public Set> entrySet() { return nodes.entrySet(); } @Override public void clear() { nodes.clear(); hash.clear(); } @Override public int size() { return nodes.size(); } @Override public void preChange(String path, DataNode node) { removeDigest(path, node); } @Override public void postChange(String path, DataNode node) { // we just made a change, so make sure the digest is // invalidated node.digestCached = false; addDigest(path, node); } private void addDigest(String path, DataNode node) { // Excluding everything under '/zookeeper/' for digest calculation. if (path.startsWith(ZooDefs.ZOOKEEPER_NODE_SUBTREE)) { return; } if (digestEnabled) { hash.addDigest(digestCalculator.calculateDigest(path, node)); } } private void removeDigest(String path, DataNode node) { // Excluding everything under '/zookeeper/' for digest calculation. if (path.startsWith(ZooDefs.ZOOKEEPER_NODE_SUBTREE)) { return; } if (digestEnabled) { hash.removeDigest(digestCalculator.calculateDigest(path, node)); } } @Override public long getDigest() { return hash.getHash(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ObserverBean.java0100644 0000000 0000000 00000004221 15051152474 034017 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.net.InetSocketAddress; import org.apache.zookeeper.server.quorum.Observer; import org.apache.zookeeper.server.quorum.ObserverMXBean; import org.apache.zookeeper.server.quorum.QuorumPeer; /** * ObserverBean * */ public class ObserverBean extends ZooKeeperServerBean implements ObserverMXBean { private Observer observer; public ObserverBean(Observer observer, ZooKeeperServer zks) { super(zks); this.observer = observer; } public String getName() { return "Observer"; } public int getPendingRevalidationCount() { return this.observer.getPendingRevalidationsCount(); } public String getQuorumAddress() { return observer.getSocket().toString(); } public String getLearnerMaster() { QuorumPeer.QuorumServer learnerMaster = observer.getCurrentLearnerMaster(); if (learnerMaster == null || learnerMaster.addr.isEmpty()) { return "Unknown"; } InetSocketAddress address = learnerMaster.addr.getReachableOrOne(); return address.getAddress().getHostAddress() + ":" + address.getPort(); } public void setLearnerMaster(String learnerMaster) { if (!observer.setLearnerMaster(learnerMaster)) { throw new IllegalArgumentException("Not a valid learner master"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Pr0100644 0000000 0000000 00000000165 15051152474 032655 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/PrepRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/PrepRequestProcess0100644 0000000 0000000 00000145371 15051152474 034354 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DeleteContainerRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.BadArgumentsException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.MultiOperationRecord; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.proto.CheckVersionRequest; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.CreateTTLRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.ReconfigRequest; import org.apache.zookeeper.proto.SetACLRequest; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.server.ZooKeeperServer.ChangeRecord; import org.apache.zookeeper.server.ZooKeeperServer.PrecalculatedDigest; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.auth.ServerAuthenticationProvider; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumOracleMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.txn.CheckVersionTxn; import org.apache.zookeeper.txn.CloseSessionTxn; import org.apache.zookeeper.txn.CreateContainerTxn; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.CreateTTLTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.ErrorTxn; import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.SetACLTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.Txn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This request processor is generally at the start of a RequestProcessor * change. It sets up any transactions associated with requests that change the * state of the system. It counts on ZooKeeperServer to update * outstandingRequests, so that it can take into account transactions that are * in the queue to be applied when generating a transaction. */ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(PrepRequestProcessor.class); /** * this is only for testing purposes. * should never be used otherwise */ private static boolean failCreate = false; LinkedBlockingQueue submittedRequests = new LinkedBlockingQueue<>(); private final RequestProcessor nextProcessor; private final boolean digestEnabled; private DigestCalculator digestCalculator; ZooKeeperServer zks; public enum DigestOpCode { NOOP, ADD, REMOVE, UPDATE; } public PrepRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { super( "ProcessThread(sid:" + zks.getServerId() + " cport:" + zks.getClientPort() + "):", zks.getZooKeeperServerListener()); this.nextProcessor = nextProcessor; this.zks = zks; this.digestEnabled = ZooKeeperServer.isDigestEnabled(); if (this.digestEnabled) { this.digestCalculator = new DigestCalculator(); } } /** * method for tests to set failCreate * @param b */ public static void setFailCreate(boolean b) { failCreate = b; } @Override public void run() { LOG.info(String.format("PrepRequestProcessor (sid:%d) started, reconfigEnabled=%s", zks.getServerId(), zks.reconfigEnabled)); try { while (true) { ServerMetrics.getMetrics().PREP_PROCESSOR_QUEUE_SIZE.add(submittedRequests.size()); Request request = submittedRequests.take(); ServerMetrics.getMetrics().PREP_PROCESSOR_QUEUE_TIME .add(Time.currentElapsedTime() - request.prepQueueStartTime); if (LOG.isTraceEnabled()) { long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK; if (request.type == OpCode.ping) { traceMask = ZooTrace.CLIENT_PING_TRACE_MASK; } ZooTrace.logRequest(LOG, traceMask, 'P', request, ""); } if (Request.requestOfDeath == request) { break; } request.prepStartTime = Time.currentElapsedTime(); pRequest(request); } } catch (Exception e) { handleException(this.getName(), e); } LOG.info("PrepRequestProcessor exited loop!"); } private ChangeRecord getRecordForPath(String path) throws KeeperException.NoNodeException { ChangeRecord lastChange = null; synchronized (zks.outstandingChanges) { lastChange = zks.outstandingChangesForPath.get(path); if (lastChange == null) { DataNode n = zks.getZKDatabase().getNode(path); if (n != null) { Set children; synchronized (n) { children = n.getChildren(); } lastChange = new ChangeRecord(-1, path, n.stat, children.size(), zks.getZKDatabase().aclForNode(n)); if (digestEnabled) { lastChange.precalculatedDigest = new PrecalculatedDigest( digestCalculator.calculateDigest(path, n), 0); } lastChange.data = n.getData(); } } } if (lastChange == null || lastChange.stat == null) { throw new KeeperException.NoNodeException(path); } return lastChange; } private ChangeRecord getOutstandingChange(String path) { synchronized (zks.outstandingChanges) { return zks.outstandingChangesForPath.get(path); } } protected void addChangeRecord(ChangeRecord c) { synchronized (zks.outstandingChanges) { zks.outstandingChanges.add(c); zks.outstandingChangesForPath.put(c.path, c); ServerMetrics.getMetrics().OUTSTANDING_CHANGES_QUEUED.add(1); } } /** * Grab current pending change records for each op in a multi-op. * * This is used inside MultiOp error code path to rollback in the event * of a failed multi-op. * * @param multiRequest * @return a map that contains previously existed records that probably need to be * rolled back in any failure. */ private Map getPendingChanges(MultiOperationRecord multiRequest) { Map pendingChangeRecords = new HashMap<>(); for (Op op : multiRequest) { String path = op.getPath(); ChangeRecord cr = getOutstandingChange(path); // only previously existing records need to be rolled back. if (cr != null) { pendingChangeRecords.put(path, cr); } /* * ZOOKEEPER-1624 - We need to store for parent's ChangeRecord * of the parent node of a request. So that if this is a * sequential node creation request, rollbackPendingChanges() * can restore previous parent's ChangeRecord correctly. * * Otherwise, sequential node name generation will be incorrect * for a subsequent request. */ int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1) { continue; } String parentPath = path.substring(0, lastSlash); ChangeRecord parentCr = getOutstandingChange(parentPath); if (parentCr != null) { pendingChangeRecords.put(parentPath, parentCr); } } return pendingChangeRecords; } /** * Rollback pending changes records from a failed multi-op. * * If a multi-op fails, we can't leave any invalid change records we created * around. We also need to restore their prior value (if any) if their prior * value is still valid. * * @param zxid * @param pendingChangeRecords */ void rollbackPendingChanges(long zxid, Map pendingChangeRecords) { synchronized (zks.outstandingChanges) { // Grab a list iterator starting at the END of the list so we can iterate in reverse Iterator iter = zks.outstandingChanges.descendingIterator(); while (iter.hasNext()) { ChangeRecord c = iter.next(); if (c.zxid == zxid) { iter.remove(); // Remove all outstanding changes for paths of this multi. // Previous records will be added back later. zks.outstandingChangesForPath.remove(c.path); } else { break; } } // we don't need to roll back any records because there is nothing left. if (zks.outstandingChanges.isEmpty()) { return; } long firstZxid = zks.outstandingChanges.peek().zxid; for (ChangeRecord c : pendingChangeRecords.values()) { // Don't apply any prior change records less than firstZxid. // Note that previous outstanding requests might have been removed // once they are completed. if (c.zxid < firstZxid) { continue; } // add previously existing records back. zks.outstandingChangesForPath.put(c.path, c); } } } /** * Performs basic validation of a path for a create request. * Throws if the path is not valid and returns the parent path. * @throws BadArgumentsException */ private String validatePathForCreate(String path, long sessionId) throws BadArgumentsException { int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 || failCreate) { LOG.info("Invalid path {} with session 0x{}", path, Long.toHexString(sessionId)); throw new KeeperException.BadArgumentsException(path); } return path.substring(0, lastSlash); } /** * This method will be called inside the ProcessRequestThread, which is a * singleton, so there will be a single thread calling this code. */ protected void pRequest2Txn(int type, long zxid, Request request, Record record) throws KeeperException, IOException, RequestProcessorException { if (request.getHdr() == null) { request.setHdr(new TxnHeader(request.sessionId, request.cxid, zxid, Time.currentWallTime(), type)); } switch (type) { case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: { pRequest2TxnCreate(type, request, record); break; } case OpCode.deleteContainer: { DeleteContainerRequest txn = (DeleteContainerRequest) record; String path = txn.getPath(); String parentPath = getParentPathAndValidate(path); ChangeRecord nodeRecord = getRecordForPath(path); if (nodeRecord.childCount > 0) { throw new KeeperException.NotEmptyException(path); } if (EphemeralType.get(nodeRecord.stat.getEphemeralOwner()) == EphemeralType.NORMAL) { throw new KeeperException.BadVersionException(path); } ChangeRecord parentRecord = getRecordForPath(parentPath); request.setTxn(new DeleteTxn(path)); parentRecord = parentRecord.duplicate(request.getHdr().getZxid()); parentRecord.childCount--; parentRecord.stat.setPzxid(request.getHdr().getZxid()); parentRecord.precalculatedDigest = precalculateDigest( DigestOpCode.UPDATE, parentPath, parentRecord.data, parentRecord.stat); addChangeRecord(parentRecord); nodeRecord = new ChangeRecord(request.getHdr().getZxid(), path, null, -1, null); nodeRecord.precalculatedDigest = precalculateDigest(DigestOpCode.REMOVE, path); setTxnDigest(request, nodeRecord.precalculatedDigest); addChangeRecord(nodeRecord); break; } case OpCode.delete: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); DeleteRequest deleteRequest = (DeleteRequest) record; String path = deleteRequest.getPath(); String parentPath = getParentPathAndValidate(path); ChangeRecord parentRecord = getRecordForPath(parentPath); zks.checkACL(request.cnxn, parentRecord.acl, ZooDefs.Perms.DELETE, request.authInfo, path, null); ChangeRecord nodeRecord = getRecordForPath(path); checkAndIncVersion(nodeRecord.stat.getVersion(), deleteRequest.getVersion(), path); if (nodeRecord.childCount > 0) { throw new KeeperException.NotEmptyException(path); } request.setTxn(new DeleteTxn(path)); parentRecord = parentRecord.duplicate(request.getHdr().getZxid()); parentRecord.childCount--; parentRecord.stat.setPzxid(request.getHdr().getZxid()); parentRecord.precalculatedDigest = precalculateDigest( DigestOpCode.UPDATE, parentPath, parentRecord.data, parentRecord.stat); addChangeRecord(parentRecord); nodeRecord = new ChangeRecord(request.getHdr().getZxid(), path, null, -1, null); nodeRecord.precalculatedDigest = precalculateDigest(DigestOpCode.REMOVE, path); setTxnDigest(request, nodeRecord.precalculatedDigest); addChangeRecord(nodeRecord); break; case OpCode.setData: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); SetDataRequest setDataRequest = (SetDataRequest) record; path = setDataRequest.getPath(); validatePath(path, request.sessionId); nodeRecord = getRecordForPath(path); zks.checkACL(request.cnxn, nodeRecord.acl, ZooDefs.Perms.WRITE, request.authInfo, path, null); zks.checkQuota(path, nodeRecord.data, setDataRequest.getData(), OpCode.setData); int newVersion = checkAndIncVersion(nodeRecord.stat.getVersion(), setDataRequest.getVersion(), path); request.setTxn(new SetDataTxn(path, setDataRequest.getData(), newVersion)); nodeRecord = nodeRecord.duplicate(request.getHdr().getZxid()); nodeRecord.stat.setVersion(newVersion); nodeRecord.stat.setMtime(request.getHdr().getTime()); nodeRecord.stat.setMzxid(zxid); nodeRecord.data = setDataRequest.getData(); nodeRecord.precalculatedDigest = precalculateDigest( DigestOpCode.UPDATE, path, nodeRecord.data, nodeRecord.stat); setTxnDigest(request, nodeRecord.precalculatedDigest); addChangeRecord(nodeRecord); break; case OpCode.reconfig: if (!zks.isReconfigEnabled()) { LOG.error("Reconfig operation requested but reconfig feature is disabled."); throw new KeeperException.ReconfigDisabledException(); } if (ZooKeeperServer.skipACL) { LOG.warn("skipACL is set, reconfig operation will skip ACL checks!"); } zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); LeaderZooKeeperServer lzks; try { lzks = (LeaderZooKeeperServer) zks; } catch (ClassCastException e) { // standalone mode - reconfiguration currently not supported throw new KeeperException.UnimplementedException(); } QuorumVerifier lastSeenQV = lzks.self.getLastSeenQuorumVerifier(); // check that there's no reconfig in progress if (lastSeenQV.getVersion() != lzks.self.getQuorumVerifier().getVersion()) { throw new KeeperException.ReconfigInProgress(); } ReconfigRequest reconfigRequest = (ReconfigRequest) record; long configId = reconfigRequest.getCurConfigId(); if (configId != -1 && configId != lzks.self.getLastSeenQuorumVerifier().getVersion()) { String msg = "Reconfiguration from version " + configId + " failed -- last seen version is " + lzks.self.getLastSeenQuorumVerifier().getVersion(); throw new KeeperException.BadVersionException(msg); } String newMembers = reconfigRequest.getNewMembers(); if (newMembers != null) { //non-incremental membership change LOG.info("Non-incremental reconfig"); // Input may be delimited by either commas or newlines so convert to common newline separated format newMembers = newMembers.replaceAll(",", "\n"); try { Properties props = new Properties(); props.load(new StringReader(newMembers)); request.qv = QuorumPeerConfig.parseDynamicConfig(props, lzks.self.getElectionType(), true, false, lastSeenQV.getOraclePath()); request.qv.setVersion(request.getHdr().getZxid()); } catch (IOException | ConfigException e) { throw new KeeperException.BadArgumentsException(e.getMessage()); } } else { //incremental change - must be a majority quorum system LOG.info("Incremental reconfig"); List joiningServers = null; String joiningServersString = reconfigRequest.getJoiningServers(); if (joiningServersString != null) { joiningServers = StringUtils.split(joiningServersString, ","); } List leavingServers = null; String leavingServersString = reconfigRequest.getLeavingServers(); if (leavingServersString != null) { leavingServers = StringUtils.split(leavingServersString, ","); } if (!(lastSeenQV instanceof QuorumMaj) && !(lastSeenQV instanceof QuorumOracleMaj)) { String msg = "Incremental reconfiguration requested but last configuration seen has a non-majority quorum system"; LOG.warn(msg); throw new KeeperException.BadArgumentsException(msg); } Map nextServers = new HashMap<>(lastSeenQV.getAllMembers()); try { if (leavingServers != null) { for (String leaving : leavingServers) { long sid = Long.parseLong(leaving); nextServers.remove(sid); } } if (joiningServers != null) { for (String joiner : joiningServers) { // joiner should have the following format: server.x = server_spec;client_spec String[] parts = StringUtils.split(joiner, "=").toArray(new String[0]); if (parts.length != 2) { throw new KeeperException.BadArgumentsException("Wrong format of server string"); } // extract server id x from first part of joiner: server.x Long sid = Long.parseLong(parts[0].substring(parts[0].lastIndexOf('.') + 1)); QuorumServer qs = new QuorumServer(sid, parts[1]); if (qs.clientAddr == null || qs.electionAddr == null || qs.addr == null) { throw new KeeperException.BadArgumentsException("Wrong format of server string - each server should have 3 ports specified"); } // check duplication of addresses and ports for (QuorumServer nqs : nextServers.values()) { if (qs.id == nqs.id) { continue; } qs.checkAddressDuplicate(nqs); } nextServers.remove(qs.id); nextServers.put(qs.id, qs); } } } catch (ConfigException e) { throw new KeeperException.BadArgumentsException("Reconfiguration failed"); } if (lastSeenQV instanceof QuorumMaj) { request.qv = new QuorumMaj(nextServers); } else { request.qv = new QuorumOracleMaj(nextServers, lastSeenQV.getOraclePath()); } request.qv.setVersion(request.getHdr().getZxid()); } if (QuorumPeerConfig.isStandaloneEnabled() && request.qv.getVotingMembers().size() < 2) { String msg = "Reconfig failed - new configuration must include at least 2 followers"; LOG.warn(msg); throw new KeeperException.BadArgumentsException(msg); } else if (request.qv.getVotingMembers().size() < 1) { String msg = "Reconfig failed - new configuration must include at least 1 follower"; LOG.warn(msg); throw new KeeperException.BadArgumentsException(msg); } if (!lzks.getLeader().isQuorumSynced(request.qv)) { String msg2 = "Reconfig failed - there must be a connected and synced quorum in new configuration"; LOG.warn(msg2); throw new KeeperException.NewConfigNoQuorum(); } nodeRecord = getRecordForPath(ZooDefs.CONFIG_NODE); zks.checkACL(request.cnxn, nodeRecord.acl, ZooDefs.Perms.WRITE, request.authInfo, null, null); SetDataTxn setDataTxn = new SetDataTxn(ZooDefs.CONFIG_NODE, request.qv.toString().getBytes(), -1); request.setTxn(setDataTxn); nodeRecord = nodeRecord.duplicate(request.getHdr().getZxid()); nodeRecord.stat.setVersion(-1); nodeRecord.stat.setMtime(request.getHdr().getTime()); nodeRecord.stat.setMzxid(zxid); nodeRecord.data = setDataTxn.getData(); // Reconfig is currently a noop from digest computation // perspective since config node is not covered by the digests. nodeRecord.precalculatedDigest = precalculateDigest( DigestOpCode.NOOP, ZooDefs.CONFIG_NODE, nodeRecord.data, nodeRecord.stat); setTxnDigest(request, nodeRecord.precalculatedDigest); addChangeRecord(nodeRecord); break; case OpCode.setACL: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); SetACLRequest setAclRequest = (SetACLRequest) record; path = setAclRequest.getPath(); validatePath(path, request.sessionId); List listACL = fixupACL(path, request.authInfo, setAclRequest.getAcl()); nodeRecord = getRecordForPath(path); zks.checkACL(request.cnxn, nodeRecord.acl, ZooDefs.Perms.ADMIN, request.authInfo, path, listACL); newVersion = checkAndIncVersion(nodeRecord.stat.getAversion(), setAclRequest.getVersion(), path); request.setTxn(new SetACLTxn(path, listACL, newVersion)); nodeRecord = nodeRecord.duplicate(request.getHdr().getZxid()); nodeRecord.stat.setAversion(newVersion); nodeRecord.precalculatedDigest = precalculateDigest( DigestOpCode.UPDATE, path, nodeRecord.data, nodeRecord.stat); setTxnDigest(request, nodeRecord.precalculatedDigest); addChangeRecord(nodeRecord); break; case OpCode.createSession: CreateSessionTxn createSessionTxn = request.readRequestRecord(CreateSessionTxn::new); request.setTxn(createSessionTxn); // only add the global session tracker but not to ZKDb zks.sessionTracker.trackSession(request.sessionId, createSessionTxn.getTimeOut()); zks.setOwner(request.sessionId, request.getOwner()); break; case OpCode.closeSession: // We don't want to do this check since the session expiration thread // queues up this operation without being the session owner. // this request is the last of the session so it should be ok //zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); long startTime = Time.currentElapsedTime(); synchronized (zks.outstandingChanges) { // need to move getEphemerals into zks.outstandingChanges // synchronized block, otherwise there will be a race // condition with the on flying deleteNode txn, and we'll // delete the node again here, which is not correct Set es = zks.getZKDatabase().getEphemerals(request.sessionId); for (ChangeRecord c : zks.outstandingChanges) { if (c.stat == null) { // Doing a delete es.remove(c.path); } else if (c.stat.getEphemeralOwner() == request.sessionId) { es.add(c.path); } } for (String path2Delete : es) { if (digestEnabled) { parentPath = getParentPathAndValidate(path2Delete); parentRecord = getRecordForPath(parentPath); parentRecord = parentRecord.duplicate(request.getHdr().getZxid()); parentRecord.stat.setPzxid(request.getHdr().getZxid()); parentRecord.precalculatedDigest = precalculateDigest( DigestOpCode.UPDATE, parentPath, parentRecord.data, parentRecord.stat); addChangeRecord(parentRecord); } nodeRecord = new ChangeRecord( request.getHdr().getZxid(), path2Delete, null, 0, null); nodeRecord.precalculatedDigest = precalculateDigest( DigestOpCode.REMOVE, path2Delete); addChangeRecord(nodeRecord); } if (ZooKeeperServer.isCloseSessionTxnEnabled()) { request.setTxn(new CloseSessionTxn(new ArrayList(es))); } zks.sessionTracker.setSessionClosing(request.sessionId); } ServerMetrics.getMetrics().CLOSE_SESSION_PREP_TIME.add(Time.currentElapsedTime() - startTime); break; case OpCode.check: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); CheckVersionRequest checkVersionRequest = (CheckVersionRequest) record; path = checkVersionRequest.getPath(); validatePath(path, request.sessionId); nodeRecord = getRecordForPath(path); zks.checkACL(request.cnxn, nodeRecord.acl, ZooDefs.Perms.READ, request.authInfo, path, null); request.setTxn(new CheckVersionTxn( path, checkAndIncVersion(nodeRecord.stat.getVersion(), checkVersionRequest.getVersion(), path))); break; default: LOG.warn("unknown type {}", type); break; } // If the txn is not going to mutate anything, like createSession, // we just set the current tree digest in it if (request.getTxnDigest() == null && digestEnabled) { setTxnDigest(request); } } private void pRequest2TxnCreate(int type, Request request, Record record) throws IOException, KeeperException { int flags; String path; List acl; byte[] data; long ttl; if (type == OpCode.createTTL) { CreateTTLRequest createTtlRequest = (CreateTTLRequest) record; flags = createTtlRequest.getFlags(); path = createTtlRequest.getPath(); acl = createTtlRequest.getAcl(); data = createTtlRequest.getData(); ttl = createTtlRequest.getTtl(); } else { CreateRequest createRequest = (CreateRequest) record; flags = createRequest.getFlags(); path = createRequest.getPath(); acl = createRequest.getAcl(); data = createRequest.getData(); ttl = -1; } CreateMode createMode = CreateMode.fromFlag(flags); validateCreateRequest(path, createMode, request, ttl); String parentPath = validatePathForCreate(path, request.sessionId); List listACL = fixupACL(path, request.authInfo, acl); ChangeRecord parentRecord = getRecordForPath(parentPath); zks.checkACL(request.cnxn, parentRecord.acl, ZooDefs.Perms.CREATE, request.authInfo, path, listACL); int parentCVersion = parentRecord.stat.getCversion(); if (createMode.isSequential()) { path = path + String.format(Locale.ENGLISH, "%010d", parentCVersion); } validatePath(path, request.sessionId); try { if (getRecordForPath(path) != null) { throw new KeeperException.NodeExistsException(path); } } catch (KeeperException.NoNodeException e) { // ignore this one } boolean ephemeralParent = EphemeralType.get(parentRecord.stat.getEphemeralOwner()) == EphemeralType.NORMAL; if (ephemeralParent) { throw new KeeperException.NoChildrenForEphemeralsException(path); } int newCversion = parentRecord.stat.getCversion() + 1; zks.checkQuota(path, null, data, OpCode.create); if (type == OpCode.createContainer) { request.setTxn(new CreateContainerTxn(path, data, listACL, newCversion)); } else if (type == OpCode.createTTL) { request.setTxn(new CreateTTLTxn(path, data, listACL, newCversion, ttl)); } else { request.setTxn(new CreateTxn(path, data, listACL, createMode.isEphemeral(), newCversion)); } TxnHeader hdr = request.getHdr(); long ephemeralOwner = 0; if (createMode.isContainer()) { ephemeralOwner = EphemeralType.CONTAINER_EPHEMERAL_OWNER; } else if (createMode.isTTL()) { ephemeralOwner = EphemeralType.TTL.toEphemeralOwner(ttl); } else if (createMode.isEphemeral()) { ephemeralOwner = request.sessionId; } StatPersisted s = DataTree.createStat(hdr.getZxid(), hdr.getTime(), ephemeralOwner); parentRecord = parentRecord.duplicate(request.getHdr().getZxid()); parentRecord.childCount++; parentRecord.stat.setCversion(newCversion); parentRecord.stat.setPzxid(request.getHdr().getZxid()); parentRecord.precalculatedDigest = precalculateDigest( DigestOpCode.UPDATE, parentPath, parentRecord.data, parentRecord.stat); addChangeRecord(parentRecord); ChangeRecord nodeRecord = new ChangeRecord( request.getHdr().getZxid(), path, s, 0, listACL); nodeRecord.data = data; nodeRecord.precalculatedDigest = precalculateDigest( DigestOpCode.ADD, path, nodeRecord.data, s); setTxnDigest(request, nodeRecord.precalculatedDigest); addChangeRecord(nodeRecord); } private void validatePath(String path, long sessionId) throws BadArgumentsException { try { PathUtils.validatePath(path); } catch (IllegalArgumentException ie) { LOG.info("Invalid path {} with session 0x{}, reason: {}", path, Long.toHexString(sessionId), ie.getMessage()); throw new BadArgumentsException(path); } } private String getParentPathAndValidate(String path) throws BadArgumentsException { int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 || zks.getZKDatabase().isSpecialPath(path)) { throw new BadArgumentsException(path); } return path.substring(0, lastSlash); } private static int checkAndIncVersion(int currentVersion, int expectedVersion, String path) throws KeeperException.BadVersionException { if (expectedVersion != -1 && expectedVersion != currentVersion) { throw new KeeperException.BadVersionException(path); } return currentVersion + 1; } /** * This method will be called inside the ProcessRequestThread, which is a * singleton, so there will be a single thread calling this code. * * @param request */ protected void pRequest(Request request) throws RequestProcessorException { request.setHdr(null); request.setTxn(null); if (!request.isThrottled()) { pRequestHelper(request); } request.zxid = zks.getZxid(); long timeFinishedPrepare = Time.currentElapsedTime(); ServerMetrics.getMetrics().PREP_PROCESS_TIME.add(timeFinishedPrepare - request.prepStartTime); nextProcessor.processRequest(request); ServerMetrics.getMetrics().PROPOSAL_PROCESS_TIME.add(Time.currentElapsedTime() - timeFinishedPrepare); } /** * This method is a helper to pRequest method */ private void pRequestHelper(Request request) { try { switch (request.type) { case OpCode.createContainer: case OpCode.create: case OpCode.create2: CreateRequest create2Request = request.readRequestRecord(CreateRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, create2Request); break; case OpCode.createTTL: CreateTTLRequest createTtlRequest = request.readRequestRecord(CreateTTLRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, createTtlRequest); break; case OpCode.deleteContainer: DeleteContainerRequest deleteContainerRequest = request.readRequestRecord(DeleteContainerRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, deleteContainerRequest); break; case OpCode.delete: DeleteRequest deleteRequest = request.readRequestRecord(DeleteRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, deleteRequest); break; case OpCode.setData: SetDataRequest setDataRequest = request.readRequestRecord(SetDataRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest); break; case OpCode.reconfig: ReconfigRequest reconfigRequest = request.readRequestRecord(ReconfigRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, reconfigRequest); break; case OpCode.setACL: SetACLRequest setAclRequest = request.readRequestRecord(SetACLRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest); break; case OpCode.check: CheckVersionRequest checkRequest = request.readRequestRecord(CheckVersionRequest::new); pRequest2Txn(request.type, zks.getNextZxid(), request, checkRequest); break; case OpCode.multi: MultiOperationRecord multiRequest; try { multiRequest = request.readRequestRecord(MultiOperationRecord::new); } catch (IOException e) { request.setHdr(new TxnHeader(request.sessionId, request.cxid, zks.getNextZxid(), Time.currentWallTime(), OpCode.multi)); throw e; } List txns = new ArrayList<>(); //Each op in a multi-op must have the same zxid! long zxid = zks.getNextZxid(); KeeperException ke = null; //Store off current pending change records in case we need to rollback Map pendingChanges = getPendingChanges(multiRequest); request.setHdr(new TxnHeader(request.sessionId, request.cxid, zxid, Time.currentWallTime(), request.type)); for (Op op : multiRequest) { Record subrequest = op.toRequestRecord(); int type; Record txn; /* If we've already failed one of the ops, don't bother * trying the rest as we know it's going to fail and it * would be confusing in the logfiles. */ if (ke != null) { type = OpCode.error; txn = new ErrorTxn(Code.RUNTIMEINCONSISTENCY.intValue()); } else { /* Prep the request and convert to a Txn */ try { pRequest2Txn(op.getType(), zxid, request, subrequest); type = op.getType(); txn = request.getTxn(); } catch (KeeperException e) { ke = e; type = OpCode.error; txn = new ErrorTxn(e.code().intValue()); if (e.code().intValue() > Code.APIERROR.intValue()) { LOG.info("Got user-level KeeperException when processing {} aborting" + " remaining multi ops. Error Path:{} Error:{}", request.toString(), e.getPath(), e.getMessage()); } request.setException(e); /* Rollback change records from failed multi-op */ rollbackPendingChanges(zxid, pendingChanges); } } // TODO: I don't want to have to serialize it here and then // immediately deserialize in next processor. But I'm // not sure how else to get the txn stored into our list. try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); txn.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); txns.add(new Txn(type, bb.array())); } } request.setTxn(new MultiTxn(txns)); if (digestEnabled) { setTxnDigest(request); } break; //create/close session don't require request record case OpCode.createSession: case OpCode.closeSession: if (!request.isLocalSession()) { pRequest2Txn(request.type, zks.getNextZxid(), request, null); } break; //All the rest don't need to create a Txn - just verify session case OpCode.sync: case OpCode.exists: case OpCode.getData: case OpCode.getACL: case OpCode.getChildren: case OpCode.getAllChildrenNumber: case OpCode.getChildren2: case OpCode.ping: case OpCode.setWatches: case OpCode.setWatches2: case OpCode.checkWatches: case OpCode.removeWatches: case OpCode.getEphemerals: case OpCode.multiRead: case OpCode.addWatch: case OpCode.whoAmI: zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); break; default: LOG.warn("unknown type {}", request.type); break; } } catch (KeeperException e) { if (request.getHdr() != null) { request.getHdr().setType(OpCode.error); request.setTxn(new ErrorTxn(e.code().intValue())); } if (e.code().intValue() > Code.APIERROR.intValue()) { LOG.info( "Got user-level KeeperException when processing {} Error Path:{} Error:{}", request.toString(), e.getPath(), e.getMessage()); } request.setException(e); } catch (Exception e) { // log at error level as we are returning a marshalling // error to the user LOG.error("Failed to process {}", request, e); String digest = request.requestDigest(); LOG.error("Dumping request buffer for request type {}: 0x{}", Request.op2String(request.type), digest); if (request.getHdr() == null) { request.setHdr(new TxnHeader(request.sessionId, request.cxid, zks.getZxid(), Time.currentWallTime(), request.type)); } request.getHdr().setType(OpCode.error); request.setTxn(new ErrorTxn(Code.MARSHALLINGERROR.intValue())); } } private static List removeDuplicates(final List acls) { if (acls == null || acls.isEmpty()) { return Collections.emptyList(); } // This would be done better with a Set but ACL hashcode/equals do not // allow for null values final ArrayList retval = new ArrayList<>(acls.size()); for (final ACL acl : acls) { if (!retval.contains(acl)) { retval.add(acl); } } return retval; } private void validateCreateRequest(String path, CreateMode createMode, Request request, long ttl) throws KeeperException { if (createMode.isTTL() && !EphemeralType.extendedEphemeralTypesEnabled()) { throw new KeeperException.UnimplementedException(); } try { EphemeralType.validateTTL(createMode, ttl); } catch (IllegalArgumentException e) { throw new BadArgumentsException(path); } if (createMode.isEphemeral()) { // Exception is set when local session failed to upgrade // so we just need to report the error if (request.getException() != null) { throw request.getException(); } zks.sessionTracker.checkGlobalSession(request.sessionId, request.getOwner()); } else { zks.sessionTracker.checkSession(request.sessionId, request.getOwner()); } } /** * This method checks out the acl making sure it isn't null or empty, * it has valid schemes and ids, and expanding any relative ids that * depend on the requestor's authentication information. * * @param authInfo list of ACL IDs associated with the client connection * @param acls list of ACLs being assigned to the node (create or setACL operation) * @return verified and expanded ACLs * @throws KeeperException.InvalidACLException */ public static List fixupACL(String path, List authInfo, List acls) throws KeeperException.InvalidACLException { // check for well formed ACLs // This resolves https://issues.apache.org/jira/browse/ZOOKEEPER-1877 List uniqacls = removeDuplicates(acls); if (uniqacls == null || uniqacls.size() == 0) { throw new KeeperException.InvalidACLException(path); } List rv = new ArrayList<>(); for (ACL a : uniqacls) { LOG.debug("Processing ACL: {}", a); if (a == null) { throw new KeeperException.InvalidACLException(path); } Id id = a.getId(); if (id == null || id.getScheme() == null) { throw new KeeperException.InvalidACLException(path); } if (id.getScheme().equals("world") && id.getId().equals("anyone")) { rv.add(a); } else if (id.getScheme().equals("auth")) { // This is the "auth" id, so we have to expand it to the // authenticated ids of the requestor boolean authIdValid = false; for (Id cid : authInfo) { ServerAuthenticationProvider ap = ProviderRegistry.getServerProvider(cid.getScheme()); if (ap == null) { LOG.error("Missing AuthenticationProvider for {}", cid.getScheme()); } else if (ap.isAuthenticated()) { authIdValid = true; rv.add(new ACL(a.getPerms(), cid)); } } if (!authIdValid) { throw new KeeperException.InvalidACLException(path); } } else { ServerAuthenticationProvider ap = ProviderRegistry.getServerProvider(id.getScheme()); if (ap == null || !ap.isValid(id.getId())) { throw new KeeperException.InvalidACLException(path); } rv.add(a); } } return rv; } public void processRequest(Request request) { request.prepQueueStartTime = Time.currentElapsedTime(); submittedRequests.add(request); ServerMetrics.getMetrics().PREP_PROCESSOR_QUEUED.add(1); } public void shutdown() { LOG.info("Shutting down"); submittedRequests.clear(); submittedRequests.add(Request.requestOfDeath); nextProcessor.shutdown(); } /** * Calculate the node digest and tree digest after the change. * * @param type the type of operations about the digest change * @param path the path of the node * @param data the data of the node * @param s the stat of the node * * @return PrecalculatedDigest the pair of node and tree digest */ private PrecalculatedDigest precalculateDigest(DigestOpCode type, String path, byte[] data, StatPersisted s) throws KeeperException.NoNodeException { if (!digestEnabled) { return null; } long prevNodeDigest; long newNodeDigest; switch (type) { case ADD: prevNodeDigest = 0; newNodeDigest = digestCalculator.calculateDigest(path, data, s); break; case REMOVE: prevNodeDigest = getRecordForPath(path).precalculatedDigest.nodeDigest; newNodeDigest = 0; break; case UPDATE: prevNodeDigest = getRecordForPath(path).precalculatedDigest.nodeDigest; newNodeDigest = digestCalculator.calculateDigest(path, data, s); break; case NOOP: newNodeDigest = prevNodeDigest = 0; break; default: return null; } long treeDigest = getCurrentTreeDigest() - prevNodeDigest + newNodeDigest; return new PrecalculatedDigest(newNodeDigest, treeDigest); } private PrecalculatedDigest precalculateDigest( DigestOpCode type, String path) throws KeeperException.NoNodeException { return precalculateDigest(type, path, null, null); } /** * Query the current tree digest from DataTree or outstandingChanges list. * * @return current tree digest */ private long getCurrentTreeDigest() { long digest; synchronized (zks.outstandingChanges) { if (zks.outstandingChanges.isEmpty()) { digest = zks.getZKDatabase().getDataTree().getTreeDigest(); LOG.debug("Digest got from data tree is: {}", digest); } else { digest = zks.outstandingChanges.peekLast().precalculatedDigest.treeDigest; LOG.debug("Digest got from outstandingChanges is: {}", digest); } } return digest; } private void setTxnDigest(Request request) { request.setTxnDigest(new TxnDigest(digestCalculator.getDigestVersion(), getCurrentTreeDigest())); } private void setTxnDigest(Request request, PrecalculatedDigest preCalculatedDigest) { if (preCalculatedDigest == null) { return; } request.setTxnDigest(new TxnDigest(digestCalculator.getDigestVersion(), preCalculatedDigest.treeDigest)); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/PurgeTxnLog.java0100644 0000000 0000000 00000021756 15051152474 033674 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * this class is used to clean up the * snapshot and data log dir's. This is usually * run as a cronjob on the zookeeper server machine. * Invocation of this class will clean up the datalogdir * files and snapdir files keeping the last "-n" snapshot files * and the corresponding logs. */ @InterfaceAudience.Public public class PurgeTxnLog { private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnLog.class); private static final String COUNT_ERR_MSG = "count should be greater than or equal to 3"; static void printUsage() { System.out.println("Usage:"); System.out.println("PurgeTxnLog dataLogDir [snapDir] -n count"); System.out.println("\tdataLogDir -- path to the txn log directory"); System.out.println("\tsnapDir -- path to the snapshot directory"); System.out.println("\tcount -- the number of old snaps/logs you want " + "to keep, value should be greater than or equal to 3"); } private static final String PREFIX_SNAPSHOT = "snapshot"; private static final String PREFIX_LOG = "log"; /** * Purges the snapshot and logs keeping the last num snapshots and the * corresponding logs. If logs are rolling or a new snapshot is created * during this process, these newest N snapshots or any data logs will be * excluded from current purging cycle. * * @param dataDir the dir that has the logs * @param snapDir the dir that has the snapshots * @param num the number of snapshots to keep * @throws IOException */ public static void purge(File dataDir, File snapDir, int num) throws IOException { if (num < 3) { throw new IllegalArgumentException(COUNT_ERR_MSG); } FileTxnSnapLog txnLog = new FileTxnSnapLog(dataDir, snapDir); List snaps = txnLog.findNValidSnapshots(num); int numSnaps = snaps.size(); if (numSnaps > 0) { purgeOlderSnapshots(txnLog, snaps.get(numSnaps - 1)); } } // VisibleForTesting static void purgeOlderSnapshots(FileTxnSnapLog txnLog, File snapShot) { final long leastZxidToBeRetain = Util.getZxidFromName(snapShot.getName(), PREFIX_SNAPSHOT); /** * We delete all files with a zxid in their name that is less than leastZxidToBeRetain. * This rule applies to both snapshot files as well as log files, with the following * exception for log files. * * A log file with zxid less than X may contain transactions with zxid larger than X. More * precisely, a log file named log.(X-a) may contain transactions newer than snapshot.X if * there are no other log files with starting zxid in the interval (X-a, X]. Assuming the * latter condition is true, log.(X-a) must be retained to ensure that snapshot.X is * recoverable. In fact, this log file may very well extend beyond snapshot.X to newer * snapshot files if these newer snapshots were not accompanied by log rollover (possible in * the learner state machine at the time of this writing). We can make more precise * determination of whether log.(leastZxidToBeRetain-a) for the smallest 'a' is actually * needed or not (e.g. not needed if there's a log file named log.(leastZxidToBeRetain+1)), * but the complexity quickly adds up with gains only in uncommon scenarios. It's safe and * simple to just preserve log.(leastZxidToBeRetain-a) for the smallest 'a' to ensure * recoverability of all snapshots being retained. We determine that log file here by * calling txnLog.getSnapshotLogs(). */ final Set retainedTxnLogs = new HashSet<>(); retainedTxnLogs.addAll(Arrays.asList(txnLog.getSnapshotLogs(leastZxidToBeRetain))); /** * Finds all candidates for deletion, which are files with a zxid in their name that is less * than leastZxidToBeRetain. There's an exception to this rule, as noted above. */ class MyFileFilter implements FileFilter { private final String prefix; MyFileFilter(String prefix) { this.prefix = prefix; } public boolean accept(File f) { if (!f.getName().startsWith(prefix + ".")) { return false; } if (retainedTxnLogs.contains(f)) { return false; } long fZxid = Util.getZxidFromName(f.getName(), prefix); return fZxid < leastZxidToBeRetain; } } // add all non-excluded log files File[] logs = txnLog.getDataLogDir().listFiles(new MyFileFilter(PREFIX_LOG)); List files = new ArrayList<>(); if (logs != null) { files.addAll(Arrays.asList(logs)); } // add all non-excluded snapshot files to the deletion list File[] snapshots = txnLog.getSnapDir().listFiles(new MyFileFilter(PREFIX_SNAPSHOT)); if (snapshots != null) { files.addAll(Arrays.asList(snapshots)); } // remove the old files for (File f : files) { final String msg = String.format( "Removing file: %s\t%s", DateFormat.getDateTimeInstance().format(f.lastModified()), f.getPath()); LOG.info(msg); System.out.println(msg); if (!f.delete()) { System.err.println("Failed to remove " + f.getPath()); } } } /** * @param args dataLogDir [snapDir] -n count * dataLogDir -- path to the txn log directory * snapDir -- path to the snapshot directory * count -- the number of old snaps/logs you want to keep, value should be greater than or equal to 3
*/ public static void main(String[] args) throws IOException { if (args.length < 3 || args.length > 4) { printUsageThenExit(); } File dataDir = validateAndGetFile(args[0]); File snapDir = dataDir; int num = -1; String countOption = ""; if (args.length == 3) { countOption = args[1]; num = validateAndGetCount(args[2]); } else { snapDir = validateAndGetFile(args[1]); countOption = args[2]; num = validateAndGetCount(args[3]); } if (!"-n".equals(countOption)) { printUsageThenExit(); } purge(dataDir, snapDir, num); } /** * validates file existence and returns the file * * @param path * @return File */ private static File validateAndGetFile(String path) { File file = new File(path); if (!file.exists()) { System.err.println("Path '" + file.getAbsolutePath() + "' does not exist. "); printUsageThenExit(); } return file; } /** * Returns integer if parsed successfully and it is valid otherwise prints * error and usage and then exits * * @param number * @return count */ private static int validateAndGetCount(String number) { int result = 0; try { result = Integer.parseInt(number); if (result < 3) { System.err.println(COUNT_ERR_MSG); printUsageThenExit(); } } catch (NumberFormatException e) { System.err.println("'" + number + "' can not be parsed to integer."); printUsageThenExit(); } return result; } private static void printUsageThenExit() { printUsage(); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RateLogger.java0100644 0000000 0000000 00000005062 15051152474 033501 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Objects; import org.apache.zookeeper.common.Time; import org.slf4j.Logger; /** * This logs the message once in the beginning and once every LOG_INTERVAL. */ public class RateLogger { private final long LOG_INTERVAL; // Duration is in ms public RateLogger(Logger log) { this(log, 100); } public RateLogger(Logger log, long interval) { LOG = log; LOG_INTERVAL = interval; } private final Logger LOG; private String msg = null; private long timestamp; private int count = 0; private String value = null; public void flush() { if (msg != null && count > 0) { String log = ""; if (count > 1) { log = "[" + count + " times] "; } log += "Message: " + msg; if (value != null) { log += " Last value:" + value; } LOG.warn(log); } msg = null; value = null; count = 0; } public void rateLimitLog(String newMsg) { rateLimitLog(newMsg, null); } /** * In addition to the message, it also takes a value. */ public void rateLimitLog(String newMsg, String newValue) { long now = Time.currentElapsedTime(); if (Objects.equals(newMsg, msg)) { ++count; value = newValue; if (now - timestamp >= LOG_INTERVAL) { flush(); msg = newMsg; timestamp = now; value = newValue; } } else { flush(); msg = newMsg; value = newValue; timestamp = now; LOG.warn("Message:{} Value:{}", msg, value); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000171 15051152474 032637 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ReferenceCountedACLCache.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ReferenceCountedAC0100644 0000000 0000000 00000016656 15051152474 034165 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.Index; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReferenceCountedACLCache { private static final Logger LOG = LoggerFactory.getLogger(ReferenceCountedACLCache.class); final Map> longKeyMap = new HashMap<>(); final Map, Long> aclKeyMap = new HashMap<>(); final Map referenceCounter = new HashMap<>(); private static final long OPEN_UNSAFE_ACL_ID = -1L; /** * these are the number of acls that we have in the datatree */ long aclIndex = 0; /** * converts the list of acls to a long. * Increments the reference counter for this ACL. * @param acls * @return a long that map to the acls */ public synchronized Long convertAcls(List acls) { if (acls == null) { return OPEN_UNSAFE_ACL_ID; } // get the value from the map Long ret = aclKeyMap.get(acls); if (ret == null) { ret = incrementIndex(); longKeyMap.put(ret, acls); aclKeyMap.put(acls, ret); } addUsage(ret); return ret; } /** * converts a long to a list of acls. * * @param longVal * @return a list of ACLs that map to the long */ public synchronized List convertLong(Long longVal) { if (longVal == null) { return null; } if (longVal == OPEN_UNSAFE_ACL_ID) { return ZooDefs.Ids.OPEN_ACL_UNSAFE; } List acls = longKeyMap.get(longVal); if (acls == null) { LOG.error("ERROR: ACL not available for long {}", longVal); throw new RuntimeException("Failed to fetch acls for " + longVal); } return acls; } private long incrementIndex() { return ++aclIndex; } public void deserialize(InputArchive ia) throws IOException { clear(); int i = ia.readInt("map"); LinkedHashMap> deserializedMap = new LinkedHashMap<>(); // keep read operations out of synchronization block while (i > 0) { Long val = ia.readLong("long"); List aclList = new ArrayList<>(); Index j = ia.startVector("acls"); if (j == null) { throw new RuntimeException("Incorrent format of InputArchive when deserialize DataTree - missing acls"); } while (!j.done()) { ACL acl = new ACL(); acl.deserialize(ia, "acl"); aclList.add(acl); j.incr(); } deserializedMap.put(val, aclList); i--; } synchronized (this) { for (Map.Entry> entry : deserializedMap.entrySet()) { Long val = entry.getKey(); List aclList = entry.getValue(); if (aclIndex < val) { aclIndex = val; } longKeyMap.put(val, aclList); aclKeyMap.put(aclList, val); referenceCounter.put(val, new AtomicLongWithEquals(0)); } } } public void serialize(OutputArchive oa) throws IOException { Map> clonedLongKeyMap; synchronized (this) { clonedLongKeyMap = new HashMap<>(longKeyMap); } oa.writeInt(clonedLongKeyMap.size(), "map"); for (Map.Entry> val : clonedLongKeyMap.entrySet()) { oa.writeLong(val.getKey(), "long"); List aclList = val.getValue(); oa.startVector(aclList, "acls"); for (ACL acl : aclList) { acl.serialize(oa, "acl"); } oa.endVector(aclList, "acls"); } } public int size() { return aclKeyMap.size(); } private void clear() { aclKeyMap.clear(); longKeyMap.clear(); referenceCounter.clear(); } public synchronized void addUsage(Long acl) { if (acl == OPEN_UNSAFE_ACL_ID) { return; } if (!longKeyMap.containsKey(acl)) { LOG.info("Ignoring acl {} as it does not exist in the cache", acl); return; } AtomicLong count = referenceCounter.get(acl); if (count == null) { referenceCounter.put(acl, new AtomicLongWithEquals(1)); } else { count.incrementAndGet(); } } public synchronized void removeUsage(Long acl) { if (acl == OPEN_UNSAFE_ACL_ID) { return; } if (!longKeyMap.containsKey(acl)) { LOG.info("Ignoring acl {} as it does not exist in the cache", acl); return; } long newCount = referenceCounter.get(acl).decrementAndGet(); if (newCount <= 0) { referenceCounter.remove(acl); aclKeyMap.remove(longKeyMap.get(acl)); longKeyMap.remove(acl); } } public synchronized void purgeUnused() { Iterator> refCountIter = referenceCounter.entrySet().iterator(); while (refCountIter.hasNext()) { Map.Entry entry = refCountIter.next(); if (entry.getValue().get() <= 0) { Long acl = entry.getKey(); aclKeyMap.remove(longKeyMap.get(acl)); longKeyMap.remove(acl); refCountIter.remove(); } } } private static class AtomicLongWithEquals extends AtomicLong { private static final long serialVersionUID = 3355155896813725462L; public AtomicLongWithEquals(long i) { super(i); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } return equals((AtomicLongWithEquals) o); } public boolean equals(AtomicLongWithEquals that) { return get() == that.get(); } @Override public int hashCode() { return 31 * Long.valueOf(get()).hashCode(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/Request.java0100644 0000000 0000000 00000041324 15051152474 033077 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.function.Supplier; import org.apache.jute.Record; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.AuthUtil; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the structure that represents a request moving through a chain of * RequestProcessors. There are various pieces of information that is tacked * onto the request as it is processed. */ public class Request { private static final Logger LOG = LoggerFactory.getLogger(Request.class); public static final Request requestOfDeath = new Request(null, 0, 0, 0, null, null); // Considers a request stale if the request's connection has closed. Enabled // by default. private static volatile boolean staleConnectionCheck = Boolean.parseBoolean(System.getProperty("zookeeper.request_stale_connection_check", "true")); // Considers a request stale if the request latency is higher than its // associated session timeout. Disabled by default. private static volatile boolean staleLatencyCheck = Boolean.parseBoolean(System.getProperty("zookeeper.request_stale_latency_check", "false")); public Request(ServerCnxn cnxn, long sessionId, int xid, int type, RequestRecord request, List authInfo) { this.cnxn = cnxn; this.sessionId = sessionId; this.cxid = xid; this.type = type; this.request = request; this.authInfo = authInfo; } public Request(long sessionId, int xid, int type, TxnHeader hdr, Record txn, long zxid) { this.sessionId = sessionId; this.cxid = xid; this.type = type; this.hdr = hdr; this.txn = txn; this.zxid = zxid; this.request = null; this.cnxn = null; this.authInfo = null; } public Request(TxnHeader hdr, Record txn, TxnDigest digest) { this.sessionId = hdr.getClientId(); this.cxid = hdr.getCxid(); this.type = hdr.getType(); this.hdr = hdr; this.txn = txn; this.zxid = hdr.getZxid(); this.request = null; this.cnxn = null; this.authInfo = null; this.txnDigest = digest; } public final long sessionId; public final int cxid; public final int type; private final RequestRecord request; public T readRequestRecord(Supplier constructor) throws IOException { if (request != null) { return request.readRecord(constructor); } throw new IOException(new NullPointerException("request")); } public T readRequestRecordNoException(Supplier constructor) { try { return readRequestRecord(constructor); } catch (IOException e) { return null; } } public byte[] readRequestBytes() { if (request != null) { return request.readBytes(); } return null; } public String requestDigest() { if (request != null) { final StringBuilder sb = new StringBuilder(); final byte[] payload = request.readBytes(); for (byte b : payload) { sb.append(String.format("%02x", (0xff & b))); } return sb.toString(); } return "request buffer is null"; } public final ServerCnxn cnxn; private TxnHeader hdr; private Record txn; public long zxid = -1; public final List authInfo; public final long createTime = Time.currentElapsedTime(); public long prepQueueStartTime = -1; public long prepStartTime = -1; public long commitProcQueueStartTime = -1; public long commitRecvTime = -1; public long syncQueueStartTime; public long requestThrottleQueueTime; private Object owner; private KeeperException e; public QuorumVerifier qv = null; private TxnDigest txnDigest; private boolean isThrottledFlag = false; public boolean isThrottled() { return isThrottledFlag; } public void setIsThrottled(boolean val) { isThrottledFlag = val; } public boolean isThrottlable() { return this.type != OpCode.ping && this.type != OpCode.closeSession && this.type != OpCode.createSession; } public byte[] getSerializeData() { if (this.hdr == null) { return null; } try { return Util.marshallTxnEntry(this.hdr, this.txn, this.txnDigest); } catch (IOException e) { LOG.error("This really should be impossible.", e); return new byte[32]; } } /** * If this is a create or close request for a local-only session. */ private boolean isLocalSession = false; private int largeRequestSize = -1; public boolean isLocalSession() { return isLocalSession; } public void setLocalSession(boolean isLocalSession) { this.isLocalSession = isLocalSession; } public void setLargeRequestSize(int size) { largeRequestSize = size; } public int getLargeRequestSize() { return largeRequestSize; } public Object getOwner() { return owner; } public void setOwner(Object owner) { this.owner = owner; } public TxnHeader getHdr() { return hdr; } public void setHdr(TxnHeader hdr) { this.hdr = hdr; } public Record getTxn() { return txn; } public void setTxn(Record txn) { this.txn = txn; } public ServerCnxn getConnection() { return cnxn; } public static boolean getStaleLatencyCheck() { return staleLatencyCheck; } public static void setStaleLatencyCheck(boolean check) { staleLatencyCheck = check; } public static boolean getStaleConnectionCheck() { return staleConnectionCheck; } public static void setStaleConnectionCheck(boolean check) { staleConnectionCheck = check; } public boolean isStale() { if (cnxn == null) { return false; } // closeSession requests should be able to outlive the session in order // to clean-up state. if (type == OpCode.closeSession) { return false; } if (staleConnectionCheck) { // If the connection is closed, consider the request stale. if (cnxn.isStale() || cnxn.isInvalid()) { return true; } } if (staleLatencyCheck) { // If the request latency is higher than session timeout, consider // the request stale. long currentTime = Time.currentElapsedTime(); return (currentTime - createTime) > cnxn.getSessionTimeout(); } return false; } /** * A prior request was dropped on this request's connection and * therefore this request must also be dropped to ensure correct * ordering semantics. */ public boolean mustDrop() { return ((cnxn != null) && cnxn.isInvalid()); } /** * is the packet type a valid packet in zookeeper * * @param type * the type of the packet * @return true if a valid packet, false if not */ static boolean isValid(int type) { // make sure this is always synchronized with Zoodefs!! switch (type) { case OpCode.notification: case OpCode.check: return false; case OpCode.closeSession: case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: case OpCode.createSession: case OpCode.delete: case OpCode.deleteContainer: case OpCode.exists: case OpCode.getACL: case OpCode.getChildren: case OpCode.getAllChildrenNumber: case OpCode.getChildren2: case OpCode.getData: case OpCode.getEphemerals: case OpCode.multi: case OpCode.multiRead: case OpCode.ping: case OpCode.reconfig: case OpCode.setACL: case OpCode.setData: case OpCode.setWatches: case OpCode.setWatches2: case OpCode.sync: case OpCode.checkWatches: case OpCode.removeWatches: case OpCode.addWatch: case OpCode.whoAmI: return true; default: return false; } } public boolean isQuorum() { switch (this.type) { case OpCode.exists: case OpCode.getACL: case OpCode.getChildren: case OpCode.getAllChildrenNumber: case OpCode.getChildren2: case OpCode.getData: case OpCode.getEphemerals: case OpCode.multiRead: case OpCode.whoAmI: return false; case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: case OpCode.error: case OpCode.delete: case OpCode.deleteContainer: case OpCode.setACL: case OpCode.setData: case OpCode.check: case OpCode.multi: case OpCode.reconfig: return true; case OpCode.closeSession: case OpCode.createSession: return !this.isLocalSession; default: return false; } } public static String op2String(int op) { switch (op) { case OpCode.notification: return "notification"; case OpCode.create: return "create"; case OpCode.delete: return "delete"; case OpCode.exists: return "exists"; case OpCode.getData: return "getData"; case OpCode.setData: return "setData"; case OpCode.getACL: return "getACL"; case OpCode.setACL: return "setACL"; case OpCode.getChildren: return "getChildren"; case OpCode.sync: return "sync"; case OpCode.ping: return "ping"; case OpCode.getChildren2: return "getChildren2"; case OpCode.check: return "check"; case OpCode.multi: return "multi"; case OpCode.create2: return "create2"; case OpCode.reconfig: return "reconfig"; case OpCode.checkWatches: return "checkWatches"; case OpCode.removeWatches: return "removeWatches"; case OpCode.createContainer: return "createContainer"; case OpCode.deleteContainer: return "deleteContainer"; case OpCode.createTTL: return "createTTL"; case OpCode.multiRead: return "multiRead"; case OpCode.auth: return "auth"; case OpCode.setWatches: return "setWatches"; case OpCode.setWatches2: return "setWatches2"; case OpCode.addWatch: return "addWatch"; case OpCode.sasl: return "sasl"; case OpCode.getEphemerals: return "getEphemerals"; case OpCode.getAllChildrenNumber: return "getAllChildrenNumber"; case OpCode.createSession: return "createSession"; case OpCode.closeSession: return "closeSession"; case OpCode.error: return "error"; case OpCode.whoAmI: return "whoAmI"; default: return "unknown " + op; } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("sessionid:0x").append(Long.toHexString(sessionId)) .append(" type:").append(op2String(type)) .append(" cxid:0x").append(Long.toHexString(cxid)) .append(" zxid:0x").append(Long.toHexString(hdr == null ? -2 : hdr.getZxid())) .append(" txntype:").append(hdr == null ? "unknown" : "" + hdr.getType()); // best effort to print the path assoc with this request String path = "n/a"; if (type != OpCode.createSession && type != OpCode.setWatches && type != OpCode.setWatches2 && type != OpCode.closeSession && request != null) { try { // make sure we don't mess with request itself byte[] bytes = request.readBytes(); if (bytes != null && bytes.length >= 4) { ByteBuffer buf = ByteBuffer.wrap(bytes); int pathLen = buf.getInt(); // sanity check if (pathLen >= 0 && pathLen < 4096 && buf.remaining() >= pathLen) { byte[] b = new byte[pathLen]; buf.get(b); path = new String(b, UTF_8); } } } catch (Exception e) { // ignore - can't find the path, will output "n/a" instead } } sb.append(" reqpath:").append(path); return sb.toString(); } public void setException(KeeperException e) { this.e = e; } public KeeperException getException() { return e; } public void logLatency(Summary metric) { logLatency(metric, Time.currentWallTime()); } public void logLatency(Summary metric, long currentTime) { if (hdr != null) { /* Request header is created by leader. If there is clock drift * latency might be negative. Headers use wall time, not * CLOCK_MONOTONIC. */ long latency = currentTime - hdr.getTime(); if (latency >= 0) { metric.add(latency); } } } public void logLatency(SummarySet metric, String key, long currentTime) { if (hdr != null) { /* Request header is created by leader. If there is clock drift * latency might be negative. Headers use wall time, not * CLOCK_MONOTONIC. */ long latency = currentTime - hdr.getTime(); if (latency >= 0) { metric.add(key, latency); } } } public void logLatency(SummarySet metric, String key) { logLatency(metric, key, Time.currentWallTime()); } /** * Returns a formatted, comma-separated list of the user IDs * associated with this {@code Request}, or {@code null} if no * user IDs were found. * * The return value is used for audit logging. While it may be * easy on the eyes, it is underspecified: it does not mention the * corresponding {@code scheme}, nor are its components escaped. * This is not a security feature. * * @return a comma-separated list of user IDs, or {@code null} if * no user IDs were found. */ public String getUsersForAudit() { return AuthUtil.getUsers(authInfo); } public TxnDigest getTxnDigest() { return txnDigest; } public void setTxnDigest(TxnDigest txnDigest) { this.txnDigest = txnDigest; } public boolean isFromLearner() { return owner instanceof LearnerHandler; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000161 15051152474 032636 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestProcessor.j0100644 0000000 0000000 00000003331 15051152474 034303 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * RequestProcessors are chained together to process transactions. Requests are * always processed in order. The standalone server, follower, and leader all * have slightly different RequestProcessors chained together. * * Requests always move forward through the chain of RequestProcessors. Requests * are passed to a RequestProcessor through processRequest(). Generally method * will always be invoked by a single thread. * * When shutdown is called, the request RequestProcessor should also shutdown * any RequestProcessors that it is connected to. */ public interface RequestProcessor { @SuppressWarnings("serial") class RequestProcessorException extends Exception { public RequestProcessorException(String msg, Throwable t) { super(msg, t); } } void processRequest(Request request) throws RequestProcessorException; void shutdown(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000156 15051152474 032642 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestRecord.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestRecord.java0100644 0000000 0000000 00000002657 15051152474 034244 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.nio.ByteBuffer; import java.util.function.Supplier; import org.apache.jute.Record; public interface RequestRecord { static RequestRecord fromBytes(ByteBuffer buffer) { return new ByteBufferRequestRecord(buffer); } static RequestRecord fromBytes(byte[] bytes) { return fromBytes(ByteBuffer.wrap(bytes)); } static RequestRecord fromRecord(Record record) { return new SimpleRequestRecord(record); } T readRecord(Supplier clazz) throws IOException; byte[] readBytes(); int limit(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000161 15051152474 032636 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestThrottler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/RequestThrottler.j0100644 0000000 0000000 00000025072 15051152474 034321 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.concurrent.LinkedBlockingQueue; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * When enabled, the RequestThrottler limits the number of outstanding requests * currently submitted to the request processor pipeline. The throttler augments * the limit imposed by the globalOutstandingLimit that is enforced * by the connection layer ({@link NIOServerCnxn}, {@link NettyServerCnxn}). * * The connection layer limit applies backpressure against the TCP connection by * disabling selection on connections once the request limit is reached. However, * the connection layer always allows a connection to send at least one request * before disabling selection on that connection. Thus, in a scenario with 40000 * client connections, the total number of requests inflight may be as high as * 40000 even if the globalOustandingLimit was set lower. * * The RequestThrottler addresses this issue by adding additional queueing. When * enabled, client connections no longer submit requests directly to the request * processor pipeline but instead to the RequestThrottler. The RequestThrottler * is then responsible for issuing requests to the request processors, and * enforces a separate maxRequests limit. If the total number of * outstanding requests is higher than maxRequests, the throttler * will continually stall for stallTime milliseconds until * underlimit. * * The RequestThrottler can also optionally drop stale requests rather than * submit them to the processor pipeline. A stale request is a request sent * by a connection that is already closed, and/or a request whose latency * will end up being higher than its associated session timeout. The notion * of staleness is configurable, @see Request for more details. * * To ensure ordering guarantees, if a request is ever dropped from a connection * that connection is closed and flagged as invalid. All subsequent requests * inflight from that connection are then dropped as well. */ public class RequestThrottler extends ZooKeeperCriticalThread { private static final Logger LOG = LoggerFactory.getLogger(RequestThrottler.class); private final LinkedBlockingQueue submittedRequests = new LinkedBlockingQueue<>(); private final ZooKeeperServer zks; private volatile boolean stopping; private volatile boolean killed; private static final String SHUTDOWN_TIMEOUT = "zookeeper.request_throttler.shutdownTimeout"; private static int shutdownTimeout; static { shutdownTimeout = Integer.getInteger(SHUTDOWN_TIMEOUT, 10000); LOG.info("{} = {} ms", SHUTDOWN_TIMEOUT, shutdownTimeout); } /** * The total number of outstanding requests allowed before the throttler * starts stalling. * * When maxRequests = 0, throttling is disabled. */ private static volatile int maxRequests = Integer.getInteger("zookeeper.request_throttle_max_requests", 0); /** * The time (in milliseconds) this is the maximum time for which throttler * thread may wait to be notified that it may proceed processing a request. */ private static volatile int stallTime = Integer.getInteger("zookeeper.request_throttle_stall_time", 100); /** * When true, the throttler will drop stale requests rather than issue * them to the request pipeline. A stale request is a request sent by * a connection that is now closed, and/or a request that will have a * request latency higher than the sessionTimeout. The staleness of * a request is tunable property, @see Request for details. */ private static volatile boolean dropStaleRequests = Boolean.parseBoolean(System.getProperty("zookeeper.request_throttle_drop_stale", "true")); protected boolean shouldThrottleOp(Request request, long elapsedTime) { return request.isThrottlable() && ZooKeeperServer.getThrottledOpWaitTime() > 0 && elapsedTime > ZooKeeperServer.getThrottledOpWaitTime(); } public RequestThrottler(ZooKeeperServer zks) { super("RequestThrottler", zks.getZooKeeperServerListener()); this.zks = zks; this.stopping = false; this.killed = false; } public static int getMaxRequests() { return maxRequests; } public static void setMaxRequests(int requests) { maxRequests = requests; } public static int getStallTime() { return stallTime; } public static void setStallTime(int time) { stallTime = time; } public static boolean getDropStaleRequests() { return dropStaleRequests; } public static void setDropStaleRequests(boolean drop) { dropStaleRequests = drop; } @Override public void run() { try { while (true) { if (killed) { break; } Request request = submittedRequests.take(); if (Request.requestOfDeath == request) { break; } if (request.mustDrop()) { continue; } // Throttling is disabled when maxRequests = 0 if (maxRequests > 0) { while (!killed) { if (dropStaleRequests && request.isStale()) { // Note: this will close the connection dropRequest(request); ServerMetrics.getMetrics().STALE_REQUESTS_DROPPED.add(1); request = null; break; } if (zks.getInProcess() < maxRequests) { break; } throttleSleep(stallTime); } } if (killed) { break; } // A dropped stale request will be null if (request != null) { if (request.isStale()) { ServerMetrics.getMetrics().STALE_REQUESTS.add(1); } final long elapsedTime = Time.currentElapsedTime() - request.requestThrottleQueueTime; ServerMetrics.getMetrics().REQUEST_THROTTLE_QUEUE_TIME.add(elapsedTime); if (shouldThrottleOp(request, elapsedTime)) { request.setIsThrottled(true); ServerMetrics.getMetrics().THROTTLED_OPS.add(1); } zks.submitRequestNow(request); } } } catch (InterruptedException e) { LOG.error("Unexpected interruption", e); } int dropped = drainQueue(); LOG.info("RequestThrottler shutdown. Dropped {} requests", dropped); } // @VisibleForTesting synchronized void throttleSleep(int stallTime) throws InterruptedException { ServerMetrics.getMetrics().REQUEST_THROTTLE_WAIT_COUNT.add(1); this.wait(stallTime); } @SuppressFBWarnings(value = "NN_NAKED_NOTIFY", justification = "state change is in ZooKeeperServer.decInProgress() ") public synchronized void throttleWake() { this.notify(); } private int drainQueue() { // If the throttler shutdown gracefully, the queue will be empty. // However, if the shutdown time limit was reached and the throttler // was killed, we have no other option than to drop all remaining // requests on the floor. int dropped = 0; Request request; LOG.info("Draining request throttler queue"); while ((request = submittedRequests.poll()) != null) { dropped += 1; dropRequest(request); } return dropped; } private void dropRequest(Request request) { // Since we're dropping a request on the floor, we must mark the // connection as invalid to ensure any future requests from this // connection are also dropped in order to ensure ordering // semantics. ServerCnxn conn = request.getConnection(); if (conn != null) { // Note: this will close the connection conn.setInvalid(); } // Notify ZooKeeperServer that the request has finished so that it can // update any request accounting/throttling limits. zks.requestFinished(request); } public void submitRequest(Request request) { if (stopping) { LOG.debug("Shutdown in progress. Request cannot be processed"); dropRequest(request); } else { request.requestThrottleQueueTime = Time.currentElapsedTime(); submittedRequests.add(request); } } public int getInflight() { return submittedRequests.size(); } public void shutdown() { // Try to shutdown gracefully LOG.info("Shutting down"); stopping = true; submittedRequests.add(Request.requestOfDeath); try { this.join(shutdownTimeout); } catch (InterruptedException e) { LOG.warn("Interrupted while waiting for {} to finish", this); } // Forcibly shutdown if necessary in order to ensure request // queue is drained. killed = true; try { this.join(); } catch (InterruptedException e) { LOG.warn("Interrupted while waiting for {} to finish", this); //TODO apply ZOOKEEPER-575 and remove this line. ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000156 15051152474 032642 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ResponseCache.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ResponseCache.java0100644 0000000 0000000 00000005446 15051152474 034176 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings("serial") public class ResponseCache { private static final Logger LOG = LoggerFactory.getLogger(ResponseCache.class); // Magic number chosen to be "big enough but not too big" public static final int DEFAULT_RESPONSE_CACHE_SIZE = 400; private final int cacheSize; private static class Entry { public final Stat stat; public final byte[] data; Entry(Stat stat, byte[] data) { this.stat = stat; this.data = data; } } private final Map cache; public ResponseCache(int cacheSize, String requestType) { this.cacheSize = cacheSize; cache = Collections.synchronizedMap(new LRUCache<>(cacheSize)); LOG.info("{} response cache size is initialized with value {}.", requestType, cacheSize); } public int getCacheSize() { return cacheSize; } public void put(String path, byte[] data, Stat stat) { cache.put(path, new Entry(stat, data)); } public byte[] get(String key, Stat stat) { Entry entry = cache.get(key); if (entry == null) { return null; } if (!stat.equals(entry.stat)) { // The node has been modified, invalidate cache. cache.remove(key); return null; } else { return entry.data; } } public boolean isEnabled() { return cacheSize > 0; } private static class LRUCache extends LinkedHashMap { private final int cacheSize; LRUCache(int cacheSize) { super(cacheSize / 4, 0.75f, true); this.cacheSize = cacheSize; } protected boolean removeEldestEntry(Map.Entry eldest) { return size() >= cacheSize; } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxn.java0100644 0000000 0000000 00000052350 15051152474 033545 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.compat.ProtocolManager; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.metrics.Counter; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Interface to a Server connection - represents a connection from a client * to the server. */ public abstract class ServerCnxn implements Stats, ServerWatcher { // This is just an arbitrary object to represent requests issued by // (aka owned by) this class public static final Object me = new Object(); private static final Logger LOG = LoggerFactory.getLogger(ServerCnxn.class); public final ProtocolManager protocolManager = new ProtocolManager(); private final Set authInfo = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final AtomicLong outstandingCount = new AtomicLong(); /** The ZooKeeperServer for this connection. May be null if the server * is not currently serving requests (for example if the server is not * an active quorum participant. */ final ZooKeeperServer zkServer; public enum DisconnectReason { UNKNOWN("unknown"), SERVER_SHUTDOWN("server_shutdown"), CLOSE_ALL_CONNECTIONS_FORCED("close_all_connections_forced"), CONNECTION_CLOSE_FORCED("connection_close_forced"), CONNECTION_EXPIRED("connection_expired"), CLIENT_CLOSED_CONNECTION("client_closed_connection"), CLIENT_CLOSED_SESSION("client_closed_session"), UNABLE_TO_READ_FROM_CLIENT("unable_to_read_from_client"), NOT_READ_ONLY_CLIENT("not_read_only_client"), CLIENT_ZXID_AHEAD("client_zxid_ahead"), INFO_PROBE("info_probe"), CLIENT_RECONNECT("client_reconnect"), CANCELLED_KEY_EXCEPTION("cancelled_key_exception"), IO_EXCEPTION("io_exception"), IO_EXCEPTION_IN_SESSION_INIT("io_exception_in_session_init"), BUFFER_UNDERFLOW_EXCEPTION("buffer_underflow_exception"), SASL_AUTH_FAILURE("sasl_auth_failure"), RESET_COMMAND("reset_command"), CLOSE_CONNECTION_COMMAND("close_connection_command"), CLEAN_UP("clean_up"), CONNECTION_MODE_CHANGED("connection_mode_changed"), RENEW_GLOBAL_SESSION_IN_RO_MODE("renew a global session in readonly mode"), // Below reasons are NettyServerCnxnFactory only CHANNEL_DISCONNECTED("channel disconnected"), CHANNEL_CLOSED_EXCEPTION("channel_closed_exception"), AUTH_PROVIDER_NOT_FOUND("auth provider not found"), FAILED_HANDSHAKE("Unsuccessful handshake"), CLIENT_RATE_LIMIT("Client hits rate limiting threshold"), CLIENT_CNX_LIMIT("Client hits connection limiting threshold"); String disconnectReason; DisconnectReason(String reason) { this.disconnectReason = reason; } public String toDisconnectReasonString() { return disconnectReason; } } public ServerCnxn(final ZooKeeperServer zkServer) { this.zkServer = zkServer; } /** * Flag that indicates that this connection is known to be closed/closing * and from which we can optionally ignore outstanding requests as part * of request throttling. This flag may be false when a connection is * actually closed (false negative), but should never be true with * a connection is still alive (false positive). */ private volatile boolean stale = false; /** * Flag that indicates that a request for this connection was previously * dropped as part of request throttling and therefore all future requests * must also be dropped to ensure ordering guarantees. */ private volatile boolean invalid = false; abstract int getSessionTimeout(); public void incrOutstandingAndCheckThrottle(RequestHeader h) { if (h.getXid() <= 0) { return; } if (zkServer.shouldThrottle(outstandingCount.incrementAndGet())) { disableRecv(false); } } // will be called from zkServer.processPacket public void decrOutstandingAndCheckThrottle(ReplyHeader h) { if (h.getXid() <= 0) { return; } if (!zkServer.shouldThrottle(outstandingCount.decrementAndGet())) { enableRecv(); } } public abstract void close(DisconnectReason reason); /** * Serializes a ZooKeeper response and enqueues it for sending. * * Serializes client response parts and enqueues them into outgoing queue. * * If both cache key and last modified zxid are provided, the serialized * response is caÑhed under the provided key, the last modified zxid is * stored along with the value. A cache entry is invalidated if the * provided last modified zxid is more recent than the stored one. * * Attention: this function is not thread safe, due to caching not being * thread safe. * * @param h reply header * @param r reply payload, can be null * @param tag Jute serialization tag, can be null * @param cacheKey Key for caching the serialized payload. A null value prevents caching. * @param stat Stat information for the the reply payload, used for cache invalidation. * A value of 0 prevents caching. * @param opCode The op code appertains to the corresponding request of the response, * used to decide which cache (e.g. read response cache, * list of children response cache, ...) object to look up to when applicable. */ public abstract int sendResponse(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) throws IOException; public int sendResponse(ReplyHeader h, Record r, String tag) throws IOException { return sendResponse(h, r, tag, null, null, -1); } protected byte[] serializeRecord(Record record) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(ZooKeeperServer.intBufferStartingSizeBytes); BinaryOutputArchive bos = BinaryOutputArchive.getArchive(baos); bos.writeRecord(record, null); return baos.toByteArray(); } protected ByteBuffer[] serialize(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) throws IOException { byte[] header = serializeRecord(h); byte[] data = null; if (r != null) { ResponseCache cache = null; Counter cacheHit = null, cacheMiss = null; switch (opCode) { case OpCode.getData : { cache = zkServer.getReadResponseCache(); cacheHit = ServerMetrics.getMetrics().RESPONSE_PACKET_CACHE_HITS; cacheMiss = ServerMetrics.getMetrics().RESPONSE_PACKET_CACHE_MISSING; break; } case OpCode.getChildren2 : { cache = zkServer.getGetChildrenResponseCache(); cacheHit = ServerMetrics.getMetrics().RESPONSE_PACKET_GET_CHILDREN_CACHE_HITS; cacheMiss = ServerMetrics.getMetrics().RESPONSE_PACKET_GET_CHILDREN_CACHE_MISSING; break; } default: // op codes where response cache is not supported. } if (cache != null && stat != null && cacheKey != null && !cacheKey.endsWith(Quotas.statNode)) { // Use cache to get serialized data. // // NB: Tag is ignored both during cache lookup and serialization, // since is is not used in read responses, which are being cached. data = cache.get(cacheKey, stat); if (data == null) { // Cache miss, serialize the response and put it in cache. data = serializeRecord(r); cache.put(cacheKey, data, stat); cacheMiss.add(1); } else { cacheHit.add(1); } } else { data = serializeRecord(r); } } int dataLength = data == null ? 0 : data.length; int packetLength = header.length + dataLength; ServerStats serverStats = serverStats(); if (serverStats != null) { serverStats.updateClientResponseSize(packetLength); } ByteBuffer lengthBuffer = ByteBuffer.allocate(4).putInt(packetLength); lengthBuffer.rewind(); int bufferLen = data != null ? 3 : 2; ByteBuffer[] buffers = new ByteBuffer[bufferLen]; buffers[0] = lengthBuffer; buffers[1] = ByteBuffer.wrap(header); if (data != null) { buffers[2] = ByteBuffer.wrap(data); } return buffers; } /* notify the client the session is closing and close/cleanup socket */ public abstract void sendCloseSession(); public void process(WatchedEvent event) { process(event, null); } public abstract void process(WatchedEvent event, List znodeAcl); public abstract long getSessionId(); abstract void setSessionId(long sessionId); /** auth info for the cnxn, returns an unmodifyable list */ public List getAuthInfo() { return Collections.unmodifiableList(new ArrayList<>(authInfo)); } public void addAuthInfo(Id id) { authInfo.add(id); } public boolean removeAuthInfo(Id id) { return authInfo.remove(id); } abstract void sendBuffer(ByteBuffer... buffers); abstract void enableRecv(); void disableRecv() { disableRecv(true); } abstract void disableRecv(boolean waitDisableRecv); abstract void setSessionTimeout(int sessionTimeout); protected ZooKeeperSaslServer zooKeeperSaslServer = null; public static class CloseRequestException extends IOException { private static final long serialVersionUID = -7854505709816442681L; private DisconnectReason reason; public CloseRequestException(String msg, DisconnectReason reason) { super(msg); this.reason = reason; } public DisconnectReason getReason() { return reason; } } protected static class EndOfStreamException extends IOException { private static final long serialVersionUID = -8255690282104294178L; private DisconnectReason reason; public EndOfStreamException(String msg, DisconnectReason reason) { super(msg); this.reason = reason; } public String toString() { return "EndOfStreamException: " + getMessage(); } public DisconnectReason getReason() { return reason; } } public boolean isStale() { return stale; } public void setStale() { stale = true; } public boolean isInvalid() { return invalid; } public void setInvalid() { if (!invalid) { if (!stale) { sendCloseSession(); } invalid = true; } } protected void packetReceived(long bytes) { incrPacketsReceived(); ServerStats serverStats = serverStats(); if (serverStats != null) { serverStats().incrementPacketsReceived(); } ServerMetrics.getMetrics().BYTES_RECEIVED_COUNT.add(bytes); } protected void packetSent() { incrPacketsSent(); ServerStats serverStats = serverStats(); if (serverStats != null) { serverStats.incrementPacketsSent(); } } protected abstract ServerStats serverStats(); protected final Date established = new Date(); protected final AtomicLong packetsReceived = new AtomicLong(); protected final AtomicLong packetsSent = new AtomicLong(); protected long minLatency; protected long maxLatency; protected String lastOp; protected long lastCxid; protected long lastZxid; protected long lastResponseTime; protected long lastLatency; protected long count; protected long totalLatency; protected DisconnectReason disconnectReason = DisconnectReason.UNKNOWN; public synchronized void resetStats() { disconnectReason = DisconnectReason.RESET_COMMAND; packetsReceived.set(0); packetsSent.set(0); minLatency = Long.MAX_VALUE; maxLatency = 0; lastOp = "NA"; lastCxid = -1; lastZxid = -1; lastResponseTime = 0; lastLatency = 0; count = 0; totalLatency = 0; } protected long incrPacketsReceived() { return packetsReceived.incrementAndGet(); } protected long incrPacketsSent() { return packetsSent.incrementAndGet(); } protected synchronized void updateStatsForResponse(long cxid, long zxid, String op, long start, long end) { // don't overwrite with "special" xids - we're interested // in the clients last real operation if (cxid >= 0) { lastCxid = cxid; } lastZxid = zxid; lastOp = op; lastResponseTime = end; long elapsed = end - start; lastLatency = elapsed; if (elapsed < minLatency) { minLatency = elapsed; } if (elapsed > maxLatency) { maxLatency = elapsed; } count++; totalLatency += elapsed; } public Date getEstablished() { return (Date) established.clone(); } public long getOutstandingRequests() { return outstandingCount.longValue(); } public long getPacketsReceived() { return packetsReceived.longValue(); } public long getPacketsSent() { return packetsSent.longValue(); } public synchronized long getMinLatency() { return minLatency == Long.MAX_VALUE ? 0 : minLatency; } public synchronized long getAvgLatency() { return count == 0 ? 0 : totalLatency / count; } public synchronized long getMaxLatency() { return maxLatency; } public synchronized String getLastOperation() { return lastOp; } public synchronized long getLastCxid() { return lastCxid; } public synchronized long getLastZxid() { return lastZxid; } public synchronized long getLastResponseTime() { return lastResponseTime; } public synchronized long getLastLatency() { return lastLatency; } /** * Prints detailed stats information for the connection. * * @see #dumpConnectionInfo(PrintWriter, boolean) for brief stats */ @Override public String toString() { StringWriter sw = new StringWriter(); PrintWriter pwriter = new PrintWriter(sw); dumpConnectionInfo(pwriter, false); pwriter.flush(); pwriter.close(); return sw.toString(); } public abstract InetSocketAddress getRemoteSocketAddress(); public abstract int getInterestOps(); public abstract boolean isSecure(); public abstract Certificate[] getClientCertificateChain(); public abstract void setClientCertificateChain(Certificate[] chain); /** * Print information about the connection. * @param brief iff true prints brief details, otw full detail */ public synchronized void dumpConnectionInfo(PrintWriter pwriter, boolean brief) { pwriter.print(" "); pwriter.print(getRemoteSocketAddress()); pwriter.print("["); int interestOps = getInterestOps(); pwriter.print(interestOps == 0 ? "0" : Integer.toHexString(interestOps)); pwriter.print("](queued="); pwriter.print(getOutstandingRequests()); pwriter.print(",recved="); pwriter.print(getPacketsReceived()); pwriter.print(",sent="); pwriter.print(getPacketsSent()); if (!brief) { long sessionId = getSessionId(); if (sessionId != 0) { pwriter.print(",sid=0x"); pwriter.print(Long.toHexString(sessionId)); pwriter.print(",lop="); pwriter.print(getLastOperation()); pwriter.print(",est="); pwriter.print(getEstablished().getTime()); pwriter.print(",to="); pwriter.print(getSessionTimeout()); long lastCxid = getLastCxid(); if (lastCxid >= 0) { pwriter.print(",lcxid=0x"); pwriter.print(Long.toHexString(lastCxid)); } pwriter.print(",lzxid=0x"); pwriter.print(Long.toHexString(getLastZxid())); pwriter.print(",lresp="); pwriter.print(getLastResponseTime()); pwriter.print(",llat="); pwriter.print(getLastLatency()); pwriter.print(",minlat="); pwriter.print(getMinLatency()); pwriter.print(",avglat="); pwriter.print(getAvgLatency()); pwriter.print(",maxlat="); pwriter.print(getMaxLatency()); } } pwriter.print(")"); } public synchronized Map getConnectionInfo(boolean brief) { Map info = new LinkedHashMap<>(); info.put("remote_socket_address", getRemoteSocketAddress()); info.put("interest_ops", getInterestOps()); info.put("outstanding_requests", getOutstandingRequests()); info.put("packets_received", getPacketsReceived()); info.put("packets_sent", getPacketsSent()); if (!brief) { info.put("session_id", getSessionId()); info.put("last_operation", getLastOperation()); info.put("established", getEstablished()); info.put("session_timeout", getSessionTimeout()); info.put("last_cxid", getLastCxid()); info.put("last_zxid", getLastZxid()); info.put("last_response_time", getLastResponseTime()); info.put("last_latency", getLastLatency()); info.put("min_latency", getMinLatency()); info.put("avg_latency", getAvgLatency()); info.put("max_latency", getMaxLatency()); } return info; } /** * clean up the socket related to a command and also make sure we flush the * data before we do that * * @param pwriter * the pwriter for a command socket */ public void cleanupWriterSocket(PrintWriter pwriter) { try { if (pwriter != null) { pwriter.flush(); pwriter.close(); } } catch (Exception e) { LOG.info("Error closing PrintWriter ", e); } finally { try { close(DisconnectReason.CLOSE_CONNECTION_COMMAND); } catch (Exception e) { LOG.error("Error closing a command socket ", e); } } } /** * @return true if the server is running, false otherwise. */ public boolean isZKServerRunning() { return zkServer != null && zkServer.isRunning(); } /** * Returns the IP address or empty string. */ public String getHostAddress() { InetSocketAddress remoteSocketAddress = getRemoteSocketAddress(); if (remoteSocketAddress == null) { return ""; } InetAddress address = remoteSocketAddress.getAddress(); if (address == null) { return ""; } return address.getHostAddress(); } /** * Get session id in hexadecimal notation. */ public String getSessionIdHex() { return "0x" + Long.toHexString(getSessionId()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000162 15051152474 032640 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnFactory.0100644 0000000 0000000 00000034314 15051152474 034233 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import javax.management.JMException; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; import org.apache.zookeeper.Environment; import org.apache.zookeeper.Login; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.auth.SaslServerCallbackHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class ServerCnxnFactory { public static final String ZOOKEEPER_SERVER_CNXN_FACTORY = "zookeeper.serverCnxnFactory"; private static final String ZOOKEEPER_MAX_CONNECTION = "zookeeper.maxCnxns"; private static final String DIGEST_MD5_USER_PREFIX = "user_"; public static final int ZOOKEEPER_MAX_CONNECTION_DEFAULT = 0; private static final Logger LOG = LoggerFactory.getLogger(ServerCnxnFactory.class); // Tells whether SSL is enabled on this ServerCnxnFactory protected boolean secure; /** * The buffer will cause the connection to be close when we do a send. */ static final ByteBuffer closeConn = ByteBuffer.allocate(0); // total number of connections accepted by the ZooKeeper server protected int maxCnxns; // sessionMap is used by closeSession() final ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); private static String loginUser = Login.SYSTEM_USER; public void addSession(long sessionId, ServerCnxn cnxn) { sessionMap.put(sessionId, cnxn); } public void removeCnxnFromSessionMap(ServerCnxn cnxn) { long sessionId = cnxn.getSessionId(); if (sessionId != 0) { sessionMap.remove(sessionId); } } /** * @return true if the cnxn that contains the sessionId exists in this ServerCnxnFactory * and it's closed. Otherwise false. */ public boolean closeSession(long sessionId, ServerCnxn.DisconnectReason reason) { ServerCnxn cnxn = sessionMap.remove(sessionId); if (cnxn != null) { try { cnxn.close(reason); } catch (Exception e) { LOG.warn("exception during session close", e); } return true; } return false; } public abstract int getLocalPort(); public abstract Iterable getConnections(); public int getNumAliveConnections() { return cnxns.size(); } public final ZooKeeperServer getZooKeeperServer() { return zkServer; } public void configure(InetSocketAddress addr, int maxcc) throws IOException { configure(addr, maxcc, -1); } public void configure(InetSocketAddress addr, int maxcc, int backlog) throws IOException { configure(addr, maxcc, backlog, false); } public abstract void configure(InetSocketAddress addr, int maxcc, int backlog, boolean secure) throws IOException; public abstract void reconfigure(InetSocketAddress addr); public Login login; /** Maximum number of connections allowed from particular host (ip) */ public abstract int getMaxClientCnxnsPerHost(); /** Maximum number of connections allowed from particular host (ip) */ public abstract void setMaxClientCnxnsPerHost(int max); public boolean isSecure() { return secure; } public void startup(ZooKeeperServer zkServer) throws IOException, InterruptedException { startup(zkServer, true); } // This method is to maintain compatiblity of startup(zks) and enable sharing of zks // when we add secureCnxnFactory. public abstract void startup(ZooKeeperServer zkServer, boolean startServer) throws IOException, InterruptedException; /** The maximum queue length of the ZooKeeper server's socket */ public abstract int getSocketListenBacklog(); public abstract void join() throws InterruptedException; public abstract void shutdown(); public abstract void start(); protected ZooKeeperServer zkServer; public final void setZooKeeperServer(ZooKeeperServer zks) { this.zkServer = zks; if (zks != null) { if (secure) { zks.setSecureServerCnxnFactory(this); } else { zks.setServerCnxnFactory(this); } } } public abstract void closeAll(ServerCnxn.DisconnectReason reason); public static ServerCnxnFactory createFactory() throws IOException { String serverCnxnFactoryName = System.getProperty(ZOOKEEPER_SERVER_CNXN_FACTORY); if (serverCnxnFactoryName == null) { serverCnxnFactoryName = NIOServerCnxnFactory.class.getName(); } try { ServerCnxnFactory serverCnxnFactory = (ServerCnxnFactory) Class.forName(serverCnxnFactoryName) .getDeclaredConstructor() .newInstance(); LOG.info("Using {} as server connection factory", serverCnxnFactoryName); return serverCnxnFactory; } catch (Exception e) { IOException ioe = new IOException("Couldn't instantiate " + serverCnxnFactoryName, e); throw ioe; } } public static ServerCnxnFactory createFactory(int clientPort, int maxClientCnxns) throws IOException { return createFactory(new InetSocketAddress(clientPort), maxClientCnxns, -1); } public static ServerCnxnFactory createFactory(int clientPort, int maxClientCnxns, int backlog) throws IOException { return createFactory(new InetSocketAddress(clientPort), maxClientCnxns, backlog); } public static ServerCnxnFactory createFactory(InetSocketAddress addr, int maxClientCnxns) throws IOException { return createFactory(addr, maxClientCnxns, -1); } public static ServerCnxnFactory createFactory(InetSocketAddress addr, int maxClientCnxns, int backlog) throws IOException { ServerCnxnFactory factory = createFactory(); factory.configure(addr, maxClientCnxns, backlog); return factory; } public abstract InetSocketAddress getLocalAddress(); public abstract void resetAllConnectionStats(); public abstract Iterable> getAllConnectionInfo(boolean brief); private final ConcurrentHashMap connectionBeans = new ConcurrentHashMap<>(); // Connection set is relied on heavily by four letter commands // Construct a ConcurrentHashSet using a ConcurrentHashMap protected final Set cnxns = Collections.newSetFromMap(new ConcurrentHashMap<>()); public void unregisterConnection(ServerCnxn serverCnxn) { ConnectionBean jmxConnectionBean = connectionBeans.remove(serverCnxn); if (jmxConnectionBean != null) { MBeanRegistry.getInstance().unregister(jmxConnectionBean); } } public void registerConnection(ServerCnxn serverCnxn) { if (zkServer != null) { ConnectionBean jmxConnectionBean = new ConnectionBean(serverCnxn, zkServer); try { MBeanRegistry.getInstance().register(jmxConnectionBean, zkServer.jmxServerBean); connectionBeans.put(serverCnxn, jmxConnectionBean); } catch (JMException e) { LOG.warn("Could not register connection", e); } } } /** * Initialize the server SASL if specified. * * If the user has specified a "ZooKeeperServer.LOGIN_CONTEXT_NAME_KEY" * or a jaas.conf using "java.security.auth.login.config" * the authentication is required and an exception is raised. * Otherwise no authentication is configured and no exception is raised. * * @throws IOException if jaas.conf is missing or there's an error in it. */ protected void configureSaslLogin() throws IOException { String serverSection = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, ZooKeeperSaslServer.DEFAULT_LOGIN_CONTEXT_NAME); // Note that 'Configuration' here refers to javax.security.auth.login.Configuration. AppConfigurationEntry[] entries = null; SecurityException securityException = null; try { entries = Configuration.getConfiguration().getAppConfigurationEntry(serverSection); } catch (SecurityException e) { // handle below: might be harmless if the user doesn't intend to use JAAS authentication. securityException = e; } // No entries in jaas.conf // If there's a configuration exception fetching the jaas section and // the user has required sasl by specifying a LOGIN_CONTEXT_NAME_KEY or a jaas file // we throw an exception otherwise we continue without authentication. if (entries == null) { String jaasFile = System.getProperty(Environment.JAAS_CONF_KEY); String loginContextName = System.getProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY); if (securityException != null && (loginContextName != null || jaasFile != null)) { String errorMessage = "No JAAS configuration section named '" + serverSection + "' was found"; if (jaasFile != null) { errorMessage += " in '" + jaasFile + "'."; } if (loginContextName != null) { errorMessage += " But " + ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY + " was set."; } LOG.error(errorMessage, securityException); throw new IOException(errorMessage); } return; } // jaas.conf entry available try { Map credentials = getDigestMd5Credentials(entries); Supplier callbackHandlerSupplier = () -> { return new SaslServerCallbackHandler(credentials); }; login = new Login(serverSection, callbackHandlerSupplier, new ZKConfig()); setLoginUser(login.getUserName()); login.startThreadIfNeeded(); } catch (LoginException e) { throw new IOException("Could not configure server because SASL configuration did not allow the " + " ZooKeeper server to authenticate itself properly: " + e); } } /** * make server credentials map from configuration's server section. * @param appConfigurationEntries AppConfigurationEntry List * @return Server credentials map */ private static Map getDigestMd5Credentials(final AppConfigurationEntry[] appConfigurationEntries) { Map credentials = new HashMap<>(); for (AppConfigurationEntry entry : appConfigurationEntries) { Map options = entry.getOptions(); // Populate DIGEST-MD5 user -> password map with JAAS configuration entries from the "Server" section. // Usernames are distinguished from other options by prefixing the username with a "user_" prefix. for (Map.Entry pair : options.entrySet()) { String key = pair.getKey(); if (key.startsWith(DIGEST_MD5_USER_PREFIX)) { String userName = key.substring(DIGEST_MD5_USER_PREFIX.length()); credentials.put(userName, (String) pair.getValue()); } } } return credentials; } private static void setLoginUser(String name) { //Created this method to avoid ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD find bug issue loginUser = name; } /** * User who has started the ZooKeeper server user, it will be the logged-in * user. If no user logged-in then system user */ public static String getUserName() { return loginUser; } /** * Maximum number of connections allowed in the ZooKeeper system */ public int getMaxCnxns() { return maxCnxns; } protected void initMaxCnxns() { maxCnxns = Integer.getInteger(ZOOKEEPER_MAX_CONNECTION, ZOOKEEPER_MAX_CONNECTION_DEFAULT); if (maxCnxns < 0) { maxCnxns = ZOOKEEPER_MAX_CONNECTION_DEFAULT; LOG.warn("maxCnxns should be greater than or equal to 0, using default value {}.", ZOOKEEPER_MAX_CONNECTION_DEFAULT); } else if (maxCnxns == ZOOKEEPER_MAX_CONNECTION_DEFAULT) { LOG.warn("maxCnxns is not configured, using default value {}.", ZOOKEEPER_MAX_CONNECTION_DEFAULT); } else { LOG.info("maxCnxns configured value is {}.", maxCnxns); } } /** * Ensure total number of connections are less than the maxCnxns */ protected boolean limitTotalNumberOfCnxns() { if (maxCnxns <= 0) { // maxCnxns limit is disabled return false; } int cnxns = getNumAliveConnections(); if (cnxns >= maxCnxns) { LOG.error("Too many connections " + cnxns + " - max is " + maxCnxns); return true; } return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000161 15051152474 032637 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnHelper.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerCnxnHelper.j0100644 0000000 0000000 00000002526 15051152474 034215 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; public class ServerCnxnHelper { /** gets maximum number of connections in ZooKeeper */ public static int getMaxCnxns(ServerCnxnFactory secureServerCnxnFactory, ServerCnxnFactory serverCnxnFactory) { if (serverCnxnFactory != null) { return serverCnxnFactory.getMaxCnxns(); } if (secureServerCnxnFactory != null) { return secureServerCnxnFactory.getMaxCnxns(); } // default return ServerCnxnFactory.ZOOKEEPER_MAX_CONNECTION_DEFAULT; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerConfig.java0100644 0000000 0000000 00000014341 15051152474 034042 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.File; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Properties; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.metrics.impl.DefaultMetricsProvider; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; /** * Server configuration storage. * * We use this instead of Properties as it's typed. * */ @InterfaceAudience.Public public class ServerConfig { //// //// If you update the configuration parameters be sure //// to update the "conf" 4letter word //// protected InetSocketAddress clientPortAddress; protected InetSocketAddress secureClientPortAddress; protected File dataDir; protected File dataLogDir; protected int tickTime = ZooKeeperServer.DEFAULT_TICK_TIME; protected int maxClientCnxns; /** defaults to -1 if not set explicitly */ protected int minSessionTimeout = -1; /** defaults to -1 if not set explicitly */ protected int maxSessionTimeout = -1; protected String metricsProviderClassName = DefaultMetricsProvider.class.getName(); protected Properties metricsProviderConfiguration = new Properties(); /** defaults to -1 if not set explicitly */ protected int listenBacklog = -1; protected String initialConfig; /** JVM Pause Monitor feature switch */ protected boolean jvmPauseMonitorToRun = false; /** JVM Pause Monitor warn threshold in ms */ protected long jvmPauseWarnThresholdMs; /** JVM Pause Monitor info threshold in ms */ protected long jvmPauseInfoThresholdMs; /** JVM Pause Monitor sleep time in ms */ protected long jvmPauseSleepTimeMs; /** * Parse arguments for server configuration * @param args clientPort dataDir and optional tickTime and maxClientCnxns * @throws IllegalArgumentException on invalid usage */ public void parse(String[] args) { if (args.length < 2 || args.length > 4) { throw new IllegalArgumentException("Invalid number of arguments:" + Arrays.toString(args)); } clientPortAddress = new InetSocketAddress(Integer.parseInt(args[0])); dataDir = new File(args[1]); dataLogDir = dataDir; if (args.length >= 3) { tickTime = Integer.parseInt(args[2]); } if (args.length == 4) { maxClientCnxns = Integer.parseInt(args[3]); } } /** * Parse a ZooKeeper configuration file * @param path the patch of the configuration file * @throws ConfigException error processing configuration */ public void parse(String path) throws ConfigException { QuorumPeerConfig config = new QuorumPeerConfig(); config.parse(path); // let qpconfig parse the file and then pull the stuff we are // interested in readFrom(config); } /** * Read attributes from a QuorumPeerConfig. * @param config */ public void readFrom(QuorumPeerConfig config) { clientPortAddress = config.getClientPortAddress(); secureClientPortAddress = config.getSecureClientPortAddress(); dataDir = config.getDataDir(); dataLogDir = config.getDataLogDir(); tickTime = config.getTickTime(); maxClientCnxns = config.getMaxClientCnxns(); minSessionTimeout = config.getMinSessionTimeout(); maxSessionTimeout = config.getMaxSessionTimeout(); jvmPauseMonitorToRun = config.isJvmPauseMonitorToRun(); jvmPauseInfoThresholdMs = config.getJvmPauseInfoThresholdMs(); jvmPauseWarnThresholdMs = config.getJvmPauseWarnThresholdMs(); jvmPauseSleepTimeMs = config.getJvmPauseSleepTimeMs(); metricsProviderClassName = config.getMetricsProviderClassName(); metricsProviderConfiguration = config.getMetricsProviderConfiguration(); listenBacklog = config.getClientPortListenBacklog(); initialConfig = config.getInitialConfig(); } public InetSocketAddress getClientPortAddress() { return clientPortAddress; } public InetSocketAddress getSecureClientPortAddress() { return secureClientPortAddress; } public File getDataDir() { return dataDir; } public File getDataLogDir() { return dataLogDir; } public int getTickTime() { return tickTime; } public int getMaxClientCnxns() { return maxClientCnxns; } /** minimum session timeout in milliseconds, -1 if unset */ public int getMinSessionTimeout() { return minSessionTimeout; } /** maximum session timeout in milliseconds, -1 if unset */ public int getMaxSessionTimeout() { return maxSessionTimeout; } public long getJvmPauseInfoThresholdMs() { return jvmPauseInfoThresholdMs; } public long getJvmPauseWarnThresholdMs() { return jvmPauseWarnThresholdMs; } public long getJvmPauseSleepTimeMs() { return jvmPauseSleepTimeMs; } public boolean isJvmPauseMonitorToRun() { return jvmPauseMonitorToRun; } public String getMetricsProviderClassName() { return metricsProviderClassName; } public Properties getMetricsProviderConfiguration() { return metricsProviderConfiguration; } /** Maximum number of pending socket connections to read, -1 if unset */ public int getClientPortListenBacklog() { return listenBacklog; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000156 15051152474 032643 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerMetrics.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerMetrics.java0100644 0000000 0000000 00000060570 15051152474 034250 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.apache.zookeeper.metrics.Counter; import org.apache.zookeeper.metrics.CounterSet; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.metrics.MetricsContext.DetailLevel; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.Summary; import org.apache.zookeeper.metrics.SummarySet; import org.apache.zookeeper.metrics.impl.DefaultMetricsProvider; import org.apache.zookeeper.metrics.impl.NullMetricsProvider; import org.apache.zookeeper.server.util.QuotaMetricsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class ServerMetrics { private static final Logger LOG = LoggerFactory.getLogger(ServerMetrics.class); /** * Dummy instance useful for tests. */ public static final ServerMetrics NULL_METRICS = new ServerMetrics(NullMetricsProvider.INSTANCE); /** * Dummy instance useful for tests. */ public static final ServerMetrics DEFAULT_METRICS_FOR_TESTS = new ServerMetrics(new DefaultMetricsProvider()); /** * Real instance used for tracking server side metrics. The final value is * assigned after the {@link MetricsProvider} bootstrap. */ private static volatile ServerMetrics CURRENT = DEFAULT_METRICS_FOR_TESTS; /** * Access current ServerMetrics. * * @return a reference to the current Metrics */ public static ServerMetrics getMetrics() { return CURRENT; } public static void metricsProviderInitialized(MetricsProvider metricsProvider) { LOG.info("ServerMetrics initialized with provider {}", metricsProvider); CURRENT = new ServerMetrics(metricsProvider); } private ServerMetrics(MetricsProvider metricsProvider) { this.metricsProvider = metricsProvider; MetricsContext metricsContext = this.metricsProvider.getRootContext(); FSYNC_TIME = metricsContext.getSummary("fsynctime", DetailLevel.BASIC); SNAPSHOT_TIME = metricsContext.getSummary("snapshottime", DetailLevel.BASIC); SNAPSHOT_ERROR_COUNT = metricsContext.getCounter("snapshot_error_count"); SNAPSHOT_RATE_LIMITED_COUNT = metricsContext.getCounter("snapshot_rate_limited_count"); RESTORE_TIME = metricsContext.getSummary("restore_time", DetailLevel.BASIC); RESTORE_ERROR_COUNT = metricsContext.getCounter("restore_error_count"); RESTORE_RATE_LIMITED_COUNT = metricsContext.getCounter("restore_rate_limited_count"); DB_INIT_TIME = metricsContext.getSummary("dbinittime", DetailLevel.BASIC); READ_LATENCY = metricsContext.getSummary("readlatency", DetailLevel.ADVANCED); UPDATE_LATENCY = metricsContext.getSummary("updatelatency", DetailLevel.ADVANCED); PROPAGATION_LATENCY = metricsContext.getSummary("propagation_latency", DetailLevel.ADVANCED); FOLLOWER_SYNC_TIME = metricsContext.getSummary("follower_sync_time", DetailLevel.BASIC); OBSERVER_SYNC_TIME = metricsContext.getSummary("observer_sync_time", DetailLevel.BASIC); ELECTION_TIME = metricsContext.getSummary("election_time", DetailLevel.BASIC); LOOKING_COUNT = metricsContext.getCounter("looking_count"); DIFF_COUNT = metricsContext.getCounter("diff_count"); SNAP_COUNT = metricsContext.getCounter("snap_count"); COMMIT_COUNT = metricsContext.getCounter("commit_count"); CONNECTION_REQUEST_COUNT = metricsContext.getCounter("connection_request_count"); CONNECTION_TOKEN_DEFICIT = metricsContext.getSummary("connection_token_deficit", DetailLevel.BASIC); CONNECTION_REJECTED = metricsContext.getCounter("connection_rejected"); INFLIGHT_SNAP_COUNT = metricsContext.getSummary("inflight_snap_count", DetailLevel.BASIC); INFLIGHT_DIFF_COUNT = metricsContext.getSummary("inflight_diff_count", DetailLevel.BASIC); WRITE_PER_NAMESPACE = metricsContext.getSummarySet("write_per_namespace", DetailLevel.BASIC); READ_PER_NAMESPACE = metricsContext.getSummarySet("read_per_namespace", DetailLevel.BASIC); BYTES_RECEIVED_COUNT = metricsContext.getCounter("bytes_received_count"); UNRECOVERABLE_ERROR_COUNT = metricsContext.getCounter("unrecoverable_error_count"); NODE_CREATED_WATCHER = metricsContext.getSummary("node_created_watch_count", DetailLevel.BASIC); NODE_DELETED_WATCHER = metricsContext.getSummary("node_deleted_watch_count", DetailLevel.BASIC); NODE_CHANGED_WATCHER = metricsContext.getSummary("node_changed_watch_count", DetailLevel.BASIC); NODE_CHILDREN_WATCHER = metricsContext.getSummary("node_children_watch_count", DetailLevel.BASIC); /* * Number of dead watchers in DeadWatcherListener */ ADD_DEAD_WATCHER_STALL_TIME = metricsContext.getCounter("add_dead_watcher_stall_time"); DEAD_WATCHERS_QUEUED = metricsContext.getCounter("dead_watchers_queued"); DEAD_WATCHERS_CLEARED = metricsContext.getCounter("dead_watchers_cleared"); DEAD_WATCHERS_CLEANER_LATENCY = metricsContext.getSummary("dead_watchers_cleaner_latency", DetailLevel.ADVANCED); RESPONSE_PACKET_CACHE_HITS = metricsContext.getCounter("response_packet_cache_hits"); RESPONSE_PACKET_CACHE_MISSING = metricsContext.getCounter("response_packet_cache_misses"); RESPONSE_PACKET_GET_CHILDREN_CACHE_HITS = metricsContext.getCounter("response_packet_get_children_cache_hits"); RESPONSE_PACKET_GET_CHILDREN_CACHE_MISSING = metricsContext.getCounter("response_packet_get_children_cache_misses"); ENSEMBLE_AUTH_SUCCESS = metricsContext.getCounter("ensemble_auth_success"); ENSEMBLE_AUTH_FAIL = metricsContext.getCounter("ensemble_auth_fail"); ENSEMBLE_AUTH_SKIP = metricsContext.getCounter("ensemble_auth_skip"); PREP_PROCESSOR_QUEUE_TIME = metricsContext.getSummary("prep_processor_queue_time_ms", DetailLevel.ADVANCED); PREP_PROCESSOR_QUEUE_SIZE = metricsContext.getSummary("prep_processor_queue_size", DetailLevel.BASIC); PREP_PROCESSOR_QUEUED = metricsContext.getCounter("prep_processor_request_queued"); OUTSTANDING_CHANGES_QUEUED = metricsContext.getCounter("outstanding_changes_queued"); OUTSTANDING_CHANGES_REMOVED = metricsContext.getCounter("outstanding_changes_removed"); PREP_PROCESS_TIME = metricsContext.getSummary("prep_process_time", DetailLevel.BASIC); PROPOSAL_PROCESS_TIME = metricsContext.getSummary("proposal_process_time", DetailLevel.BASIC); CLOSE_SESSION_PREP_TIME = metricsContext.getSummary("close_session_prep_time", DetailLevel.ADVANCED); REVALIDATE_COUNT = metricsContext.getCounter("revalidate_count"); CONNECTION_DROP_COUNT = metricsContext.getCounter("connection_drop_count"); CONNECTION_REVALIDATE_COUNT = metricsContext.getCounter("connection_revalidate_count"); // Expiry queue stats SESSIONLESS_CONNECTIONS_EXPIRED = metricsContext.getCounter("sessionless_connections_expired"); STALE_SESSIONS_EXPIRED = metricsContext.getCounter("stale_sessions_expired"); UNAVAILABLE_TIME = metricsContext.getSummary("unavailable_time", DetailLevel.BASIC); LEADER_UNAVAILABLE_TIME = metricsContext.getSummary("leader_unavailable_time", DetailLevel.BASIC); /* * Number of requests that are in the session queue. */ REQUESTS_IN_SESSION_QUEUE = metricsContext.getSummary("requests_in_session_queue", DetailLevel.BASIC); PENDING_SESSION_QUEUE_SIZE = metricsContext.getSummary("pending_session_queue_size", DetailLevel.BASIC); /* * Consecutive number of read requests that are in the session queue right after a commit request. */ READS_AFTER_WRITE_IN_SESSION_QUEUE = metricsContext.getSummary("reads_after_write_in_session_queue", DetailLevel.BASIC); READ_ISSUED_FROM_SESSION_QUEUE = metricsContext.getSummary("reads_issued_from_session_queue", DetailLevel.BASIC); SESSION_QUEUES_DRAINED = metricsContext.getSummary("session_queues_drained", DetailLevel.BASIC); TIME_WAITING_EMPTY_POOL_IN_COMMIT_PROCESSOR_READ = metricsContext.getSummary("time_waiting_empty_pool_in_commit_processor_read_ms", DetailLevel.BASIC); WRITE_BATCH_TIME_IN_COMMIT_PROCESSOR = metricsContext.getSummary("write_batch_time_in_commit_processor", DetailLevel.BASIC); CONCURRENT_REQUEST_PROCESSING_IN_COMMIT_PROCESSOR = metricsContext.getSummary("concurrent_request_processing_in_commit_processor", DetailLevel.BASIC); READS_QUEUED_IN_COMMIT_PROCESSOR = metricsContext.getSummary("read_commit_proc_req_queued", DetailLevel.BASIC); WRITES_QUEUED_IN_COMMIT_PROCESSOR = metricsContext.getSummary("write_commit_proc_req_queued", DetailLevel.BASIC); COMMITS_QUEUED_IN_COMMIT_PROCESSOR = metricsContext.getSummary("commit_commit_proc_req_queued", DetailLevel.BASIC); COMMITS_QUEUED = metricsContext.getCounter("request_commit_queued"); READS_ISSUED_IN_COMMIT_PROC = metricsContext.getSummary("read_commit_proc_issued", DetailLevel.BASIC); WRITES_ISSUED_IN_COMMIT_PROC = metricsContext.getSummary("write_commit_proc_issued", DetailLevel.BASIC); THROTTLED_OPS = metricsContext.getCounter("throttled_ops"); /** * Time spent by a read request in the commit processor. */ READ_COMMITPROC_TIME = metricsContext.getSummary("read_commitproc_time_ms", DetailLevel.ADVANCED); /** * Time spent by a write request in the commit processor. */ WRITE_COMMITPROC_TIME = metricsContext.getSummary("write_commitproc_time_ms", DetailLevel.ADVANCED); /** * Time spent by a committed request, for a locally issued write, in the * commit processor. */ LOCAL_WRITE_COMMITTED_TIME = metricsContext.getSummary("local_write_committed_time_ms", DetailLevel.ADVANCED); /** * Time spent by a committed request for a write, issued by other server, in the * commit processor. */ SERVER_WRITE_COMMITTED_TIME = metricsContext.getSummary("server_write_committed_time_ms", DetailLevel.ADVANCED); COMMIT_PROCESS_TIME = metricsContext.getSummary("commit_process_time", DetailLevel.BASIC); /** * Observer Master processing metrics. */ OM_PROPOSAL_PROCESS_TIME = metricsContext.getSummary("om_proposal_process_time_ms", DetailLevel.ADVANCED); OM_COMMIT_PROCESS_TIME = metricsContext.getSummary("om_commit_process_time_ms", DetailLevel.ADVANCED); /** * Time spent by the final processor. This is tracked in the commit processor. */ READ_FINAL_PROC_TIME = metricsContext.getSummary("read_final_proc_time_ms", DetailLevel.ADVANCED); WRITE_FINAL_PROC_TIME = metricsContext.getSummary("write_final_proc_time_ms", DetailLevel.ADVANCED); PROPOSAL_LATENCY = metricsContext.getSummary("proposal_latency", DetailLevel.ADVANCED); PROPOSAL_ACK_CREATION_LATENCY = metricsContext.getSummary("proposal_ack_creation_latency", DetailLevel.ADVANCED); COMMIT_PROPAGATION_LATENCY = metricsContext.getSummary("commit_propagation_latency", DetailLevel.ADVANCED); LEARNER_PROPOSAL_RECEIVED_COUNT = metricsContext.getCounter("learner_proposal_received_count"); LEARNER_COMMIT_RECEIVED_COUNT = metricsContext.getCounter("learner_commit_received_count"); /** * Learner handler quorum packet metrics. */ LEARNER_HANDLER_QP_SIZE = metricsContext.getSummarySet("learner_handler_qp_size", DetailLevel.BASIC); LEARNER_HANDLER_QP_TIME = metricsContext.getSummarySet("learner_handler_qp_time_ms", DetailLevel.ADVANCED); STARTUP_TXNS_LOADED = metricsContext.getSummary("startup_txns_loaded", DetailLevel.BASIC); STARTUP_TXNS_LOAD_TIME = metricsContext.getSummary("startup_txns_load_time", DetailLevel.BASIC); STARTUP_SNAP_LOAD_TIME = metricsContext.getSummary("startup_snap_load_time", DetailLevel.BASIC); SYNC_PROCESSOR_QUEUE_AND_FLUSH_TIME = metricsContext.getSummary("sync_processor_queue_and_flush_time_ms", DetailLevel.ADVANCED); SYNC_PROCESSOR_QUEUE_SIZE = metricsContext.getSummary("sync_processor_queue_size", DetailLevel.BASIC); SYNC_PROCESSOR_QUEUED = metricsContext.getCounter("sync_processor_request_queued"); SYNC_PROCESSOR_QUEUE_TIME = metricsContext.getSummary("sync_processor_queue_time_ms", DetailLevel.ADVANCED); SYNC_PROCESSOR_FLUSH_TIME = metricsContext.getSummary("sync_processor_queue_flush_time_ms", DetailLevel.ADVANCED); SYNC_PROCESS_TIME = metricsContext.getSummary("sync_process_time", DetailLevel.BASIC); BATCH_SIZE = metricsContext.getSummary("sync_processor_batch_size", DetailLevel.BASIC); QUORUM_ACK_LATENCY = metricsContext.getSummary("quorum_ack_latency", DetailLevel.ADVANCED); ACK_LATENCY = metricsContext.getSummarySet("ack_latency", DetailLevel.ADVANCED); PROPOSAL_COUNT = metricsContext.getCounter("proposal_count"); QUIT_LEADING_DUE_TO_DISLOYAL_VOTER = metricsContext.getCounter("quit_leading_due_to_disloyal_voter"); STALE_REQUESTS = metricsContext.getCounter("stale_requests"); STALE_REQUESTS_DROPPED = metricsContext.getCounter("stale_requests_dropped"); STALE_REPLIES = metricsContext.getCounter("stale_replies"); REQUEST_THROTTLE_QUEUE_TIME = metricsContext.getSummary("request_throttle_queue_time_ms", DetailLevel.ADVANCED); REQUEST_THROTTLE_WAIT_COUNT = metricsContext.getCounter("request_throttle_wait_count"); LARGE_REQUESTS_REJECTED = metricsContext.getCounter("large_requests_rejected"); NETTY_QUEUED_BUFFER = metricsContext.getSummary("netty_queued_buffer_capacity", DetailLevel.BASIC); DIGEST_MISMATCHES_COUNT = metricsContext.getCounter("digest_mismatches_count"); LEARNER_REQUEST_PROCESSOR_QUEUE_SIZE = metricsContext.getSummary("learner_request_processor_queue_size", DetailLevel.BASIC); UNSUCCESSFUL_HANDSHAKE = metricsContext.getCounter("unsuccessful_handshake"); INSECURE_ADMIN = metricsContext.getCounter("insecure_admin_count"); TLS_HANDSHAKE_EXCEEDED = metricsContext.getCounter("tls_handshake_exceeded"); CNXN_CLOSED_WITHOUT_ZK_SERVER_RUNNING = metricsContext.getCounter("cnxn_closed_without_zk_server_running"); SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR_COUNT = metricsContext.getCounter("skip_learner_request_to_next_processor_count"); SOCKET_CLOSING_TIME = metricsContext.getSummary("socket_closing_time", DetailLevel.BASIC); REQUESTS_NOT_FORWARDED_TO_COMMIT_PROCESSOR = metricsContext.getCounter( "requests_not_forwarded_to_commit_processor"); RESPONSE_BYTES = metricsContext.getCounter("response_bytes"); WATCH_BYTES = metricsContext.getCounter("watch_bytes"); JVM_PAUSE_TIME = metricsContext.getSummary("jvm_pause_time_ms", DetailLevel.ADVANCED); QUOTA_EXCEEDED_ERROR_PER_NAMESPACE = metricsContext.getCounterSet(QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE); } /** * Txnlog fsync time */ public final Summary FSYNC_TIME; /** * Snapshot writing time */ public final Summary SNAPSHOT_TIME; /** * Snapshot error count */ public final Counter SNAPSHOT_ERROR_COUNT; /** * Snapshot rate limited count */ public final Counter SNAPSHOT_RATE_LIMITED_COUNT; /** * Restore time */ public final Summary RESTORE_TIME; /** * Restore error count */ public final Counter RESTORE_ERROR_COUNT; /** * Restore rate limited count */ public final Counter RESTORE_RATE_LIMITED_COUNT; /** * Db init time (snapshot loading + txnlog replay) */ public final Summary DB_INIT_TIME; /** * Stats for read request. The timing start from when the server see the * request until it leave final request processor. */ public final Summary READ_LATENCY; /** * Stats for request that need quorum voting. Timing is the same as read * request. We only keep track of stats for request that originated from * this machine only. */ public final Summary UPDATE_LATENCY; /** * Stats for all quorum request. The timing start from when the leader see * the request until it reach the learner. */ public final Summary PROPAGATION_LATENCY; public final Summary FOLLOWER_SYNC_TIME; public final Summary OBSERVER_SYNC_TIME; public final Summary ELECTION_TIME; public final Counter LOOKING_COUNT; public final Counter DIFF_COUNT; public final Counter SNAP_COUNT; public final Counter COMMIT_COUNT; public final Counter CONNECTION_REQUEST_COUNT; public final Counter REVALIDATE_COUNT; public final Counter CONNECTION_DROP_COUNT; public final Counter CONNECTION_REVALIDATE_COUNT; // Expiry queue stats public final Counter SESSIONLESS_CONNECTIONS_EXPIRED; public final Counter STALE_SESSIONS_EXPIRED; public final Summary UNAVAILABLE_TIME; public final Summary LEADER_UNAVAILABLE_TIME; // Connection throttling related public final Summary CONNECTION_TOKEN_DEFICIT; public final Counter CONNECTION_REJECTED; public final Summary INFLIGHT_SNAP_COUNT; public final Summary INFLIGHT_DIFF_COUNT; public final Counter UNRECOVERABLE_ERROR_COUNT; public final SummarySet WRITE_PER_NAMESPACE; public final SummarySet READ_PER_NAMESPACE; public final Counter BYTES_RECEIVED_COUNT; public final Summary PREP_PROCESSOR_QUEUE_TIME; public final Summary PREP_PROCESSOR_QUEUE_SIZE; public final Counter PREP_PROCESSOR_QUEUED; public final Counter OUTSTANDING_CHANGES_QUEUED; public final Counter OUTSTANDING_CHANGES_REMOVED; public final Summary PREP_PROCESS_TIME; public final Summary PROPOSAL_PROCESS_TIME; public final Summary CLOSE_SESSION_PREP_TIME; public final Summary PROPOSAL_LATENCY; public final Summary PROPOSAL_ACK_CREATION_LATENCY; public final Summary COMMIT_PROPAGATION_LATENCY; public final Counter LEARNER_PROPOSAL_RECEIVED_COUNT; public final Counter LEARNER_COMMIT_RECEIVED_COUNT; public final Summary STARTUP_TXNS_LOADED; public final Summary STARTUP_TXNS_LOAD_TIME; public final Summary STARTUP_SNAP_LOAD_TIME; public final Summary SYNC_PROCESSOR_QUEUE_AND_FLUSH_TIME; public final Summary SYNC_PROCESSOR_QUEUE_SIZE; public final Counter SYNC_PROCESSOR_QUEUED; public final Summary SYNC_PROCESSOR_QUEUE_TIME; public final Summary SYNC_PROCESSOR_FLUSH_TIME; public final Summary SYNC_PROCESS_TIME; public final Summary BATCH_SIZE; public final Summary QUORUM_ACK_LATENCY; public final SummarySet ACK_LATENCY; public final Counter PROPOSAL_COUNT; public final Counter QUIT_LEADING_DUE_TO_DISLOYAL_VOTER; /** * Fired watcher stats. */ public final Summary NODE_CREATED_WATCHER; public final Summary NODE_DELETED_WATCHER; public final Summary NODE_CHANGED_WATCHER; public final Summary NODE_CHILDREN_WATCHER; /* * Number of dead watchers in DeadWatcherListener */ public final Counter ADD_DEAD_WATCHER_STALL_TIME; public final Counter DEAD_WATCHERS_QUEUED; public final Counter DEAD_WATCHERS_CLEARED; public final Summary DEAD_WATCHERS_CLEANER_LATENCY; /* * Response cache hit and miss metrics. */ public final Counter RESPONSE_PACKET_CACHE_HITS; public final Counter RESPONSE_PACKET_CACHE_MISSING; public final Counter RESPONSE_PACKET_GET_CHILDREN_CACHE_HITS; public final Counter RESPONSE_PACKET_GET_CHILDREN_CACHE_MISSING; /** * Learner handler quorum packet metrics. */ public final SummarySet LEARNER_HANDLER_QP_SIZE; public final SummarySet LEARNER_HANDLER_QP_TIME; /* * Number of requests that are in the session queue. */ public final Summary REQUESTS_IN_SESSION_QUEUE; public final Summary PENDING_SESSION_QUEUE_SIZE; /* * Consecutive number of read requests that are in the session queue right after a commit request. */ public final Summary READS_AFTER_WRITE_IN_SESSION_QUEUE; public final Summary READ_ISSUED_FROM_SESSION_QUEUE; public final Summary SESSION_QUEUES_DRAINED; public final Summary TIME_WAITING_EMPTY_POOL_IN_COMMIT_PROCESSOR_READ; public final Summary WRITE_BATCH_TIME_IN_COMMIT_PROCESSOR; public final Summary CONCURRENT_REQUEST_PROCESSING_IN_COMMIT_PROCESSOR; public final Summary READS_QUEUED_IN_COMMIT_PROCESSOR; public final Summary WRITES_QUEUED_IN_COMMIT_PROCESSOR; public final Summary COMMITS_QUEUED_IN_COMMIT_PROCESSOR; public final Counter COMMITS_QUEUED; public final Summary READS_ISSUED_IN_COMMIT_PROC; public final Summary WRITES_ISSUED_IN_COMMIT_PROC; // Request op throttling related public final Counter THROTTLED_OPS; /** * Time spent by a read request in the commit processor. */ public final Summary READ_COMMITPROC_TIME; /** * Time spent by a write request in the commit processor. */ public final Summary WRITE_COMMITPROC_TIME; /** * Time spent by a committed request, for a locally issued write, in the * commit processor. */ public final Summary LOCAL_WRITE_COMMITTED_TIME; /** * Time spent by a committed request for a write, issued by other server, in the * commit processor. */ public final Summary SERVER_WRITE_COMMITTED_TIME; public final Summary COMMIT_PROCESS_TIME; /** * Observer Master processing metrics. */ public final Summary OM_PROPOSAL_PROCESS_TIME; public final Summary OM_COMMIT_PROCESS_TIME; /** * Time spent by the final processor. This is tracked in the commit processor. */ public final Summary READ_FINAL_PROC_TIME; public final Summary WRITE_FINAL_PROC_TIME; /* * Number of successful matches of expected ensemble name in EnsembleAuthenticationProvider. */ public final Counter ENSEMBLE_AUTH_SUCCESS; /* * Number of unsuccessful matches of expected ensemble name in EnsembleAuthenticationProvider. */ public final Counter ENSEMBLE_AUTH_FAIL; /* * Number of client auth requests with no ensemble set in EnsembleAuthenticationProvider. */ public final Counter ENSEMBLE_AUTH_SKIP; public final Counter STALE_REQUESTS; public final Counter STALE_REQUESTS_DROPPED; public final Counter STALE_REPLIES; public final Summary REQUEST_THROTTLE_QUEUE_TIME; public final Counter REQUEST_THROTTLE_WAIT_COUNT; public final Counter LARGE_REQUESTS_REJECTED; public final Summary NETTY_QUEUED_BUFFER; // Total number of digest mismatches that are observed when applying // txns to data tree. public final Counter DIGEST_MISMATCHES_COUNT; public final Summary LEARNER_REQUEST_PROCESSOR_QUEUE_SIZE; public final Counter UNSUCCESSFUL_HANDSHAKE; /* * Number of insecure connections to admin port */ public final Counter INSECURE_ADMIN; public final Counter TLS_HANDSHAKE_EXCEEDED; public final Counter CNXN_CLOSED_WITHOUT_ZK_SERVER_RUNNING; public final Counter SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR_COUNT; public final Summary SOCKET_CLOSING_TIME; public final Counter REQUESTS_NOT_FORWARDED_TO_COMMIT_PROCESSOR; /** * Number of response/watch bytes written to clients. */ public final Counter RESPONSE_BYTES; public final Counter WATCH_BYTES; public final Summary JVM_PAUSE_TIME; public final CounterSet QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; private final MetricsProvider metricsProvider; public void resetAll() { metricsProvider.resetAllValues(); } public MetricsProvider getMetricsProvider() { return metricsProvider; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerStats.java0100644 0000000 0000000 00000015217 15051152474 033736 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.metric.AvgMinMaxCounter; import org.apache.zookeeper.server.quorum.BufferStats; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Basic Server Statistics */ public class ServerStats { private static final Logger LOG = LoggerFactory.getLogger(ServerStats.class); private final AtomicLong packetsSent = new AtomicLong(); private final AtomicLong packetsReceived = new AtomicLong(); private final AvgMinMaxCounter requestLatency = new AvgMinMaxCounter("request_latency"); private final AtomicLong fsyncThresholdExceedCount = new AtomicLong(0); private final BufferStats clientResponseStats = new BufferStats(); private AtomicLong nonMTLSRemoteConnCntr = new AtomicLong(0); private AtomicLong nonMTLSLocalConnCntr = new AtomicLong(0); private AtomicLong authFailedCntr = new AtomicLong(0); private final Provider provider; private final long startTime = Time.currentElapsedTime(); public interface Provider { long getOutstandingRequests(); long getLastProcessedZxid(); String getState(); int getNumAliveConnections(); long getDataDirSize(); long getLogDirSize(); } public ServerStats(Provider provider) { this.provider = provider; } // getters public long getMinLatency() { return requestLatency.getMin(); } public double getAvgLatency() { return requestLatency.getAvg(); } public long getMaxLatency() { return requestLatency.getMax(); } public long getOutstandingRequests() { return provider.getOutstandingRequests(); } public long getLastProcessedZxid() { return provider.getLastProcessedZxid(); } public long getDataDirSize() { return provider.getDataDirSize(); } public long getLogDirSize() { return provider.getLogDirSize(); } public long getPacketsReceived() { return packetsReceived.get(); } public long getPacketsSent() { return packetsSent.get(); } public String getServerState() { return provider.getState(); } /** The number of client connections alive to this server */ public int getNumAliveClientConnections() { return provider.getNumAliveConnections(); } public long getUptime() { return Time.currentElapsedTime() - startTime; } public boolean isProviderNull() { return provider == null; } @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"); sb.append("Connections: " + getNumAliveClientConnections() + "\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(); } /** * Update request statistic. This should only be called from a request * that originated from that machine. */ public void updateLatency(Request request, long currentTime) { long latency = currentTime - request.createTime; if (latency < 0) { return; } requestLatency.addDataPoint(latency); if (request.getHdr() != null) { // Only quorum request should have header ServerMetrics.getMetrics().UPDATE_LATENCY.add(latency); } else { // All read request should goes here ServerMetrics.getMetrics().READ_LATENCY.add(latency); } } public void resetLatency() { requestLatency.reset(); } public void resetMaxLatency() { requestLatency.resetMax(); } public void incrementPacketsReceived() { packetsReceived.incrementAndGet(); } public void incrementPacketsSent() { packetsSent.incrementAndGet(); } public void resetRequestCounters() { packetsReceived.set(0); packetsSent.set(0); } public long getFsyncThresholdExceedCount() { return fsyncThresholdExceedCount.get(); } public void incrementFsyncThresholdExceedCount() { fsyncThresholdExceedCount.incrementAndGet(); } public void resetFsyncThresholdExceedCount() { fsyncThresholdExceedCount.set(0); } public long getNonMTLSLocalConnCount() { return nonMTLSLocalConnCntr.get(); } public void incrementNonMTLSLocalConnCount() { nonMTLSLocalConnCntr.incrementAndGet(); } public void resetNonMTLSLocalConnCount() { nonMTLSLocalConnCntr.set(0); } public long getNonMTLSRemoteConnCount() { return nonMTLSRemoteConnCntr.get(); } public void incrementNonMTLSRemoteConnCount() { nonMTLSRemoteConnCntr.incrementAndGet(); } public void resetNonMTLSRemoteConnCount() { nonMTLSRemoteConnCntr.set(0); } public long getAuthFailedCount() { return authFailedCntr.get(); } public void incrementAuthFailedCount() { authFailedCntr.incrementAndGet(); } public void resetAuthFailedCount() { authFailedCntr.set(0); } public void reset() { resetLatency(); resetRequestCounters(); clientResponseStats.reset(); ServerMetrics.getMetrics().resetAll(); } public void updateClientResponseSize(int size) { clientResponseStats.setLastBufferSize(size); } public BufferStats getClientResponseStats() { return clientResponseStats; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000156 15051152474 032643 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerWatcher.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ServerWatcher.java0100644 0000000 0000000 00000002106 15051152474 034226 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.List; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.data.ACL; public interface ServerWatcher extends Watcher { void process(WatchedEvent event, List znodeAcl); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000157 15051152474 032644 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SessionTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SessionTracker.jav0100644 0000000 0000000 00000010657 15051152474 034252 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.PrintWriter; import java.util.Map; import java.util.Set; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.SessionExpiredException; /** * This is the basic interface that ZooKeeperServer uses to track sessions. The * standalone and leader ZooKeeperServer use the same SessionTracker. The * FollowerZooKeeperServer uses a SessionTracker which is basically a simple * shell to track information to be forwarded to the leader. */ public interface SessionTracker { interface Session { long getSessionId(); int getTimeout(); boolean isClosing(); } interface SessionExpirer { void expire(Session session); long getServerId(); } long createSession(int sessionTimeout); /** * Track the session expire, not add to ZkDb. * @param id sessionId * @param to sessionTimeout * @return whether the session was newly tracked (if false, already tracked) */ boolean trackSession(long id, int to); /** * Add the session to the local session map or global one in zkDB. * @param id sessionId * @param to sessionTimeout * @return whether the session was newly added (if false, already existed) */ boolean commitSession(long id, int to); /** * @param sessionId * @param sessionTimeout * @return false if session is no longer active */ boolean touchSession(long sessionId, int sessionTimeout); /** * Mark that the session is in the process of closing. * @param sessionId */ void setSessionClosing(long sessionId); /** * */ void shutdown(); /** * @param sessionId */ void removeSession(long sessionId); /** * @param sessionId * @return whether or not the SessionTracker is aware of this session */ boolean isTrackingSession(long sessionId); /** * Checks whether the SessionTracker is aware of this session, the session * is still active, and the owner matches. If the owner wasn't previously * set, this sets the owner of the session. * * UnknownSessionException should never been thrown to the client. It is * only used internally to deal with possible local session from other * machine * * @param sessionId * @param owner */ void checkSession(long sessionId, Object owner) throws KeeperException.SessionExpiredException, KeeperException.SessionMovedException, KeeperException.UnknownSessionException; /** * Strictly check that a given session is a global session or not * @param sessionId * @param owner * @throws KeeperException.SessionExpiredException * @throws KeeperException.SessionMovedException */ void checkGlobalSession(long sessionId, Object owner) throws KeeperException.SessionExpiredException, KeeperException.SessionMovedException; void setOwner(long id, Object owner) throws SessionExpiredException; /** * Text dump of session information, suitable for debugging. * @param pwriter the output writer */ void dumpSessions(PrintWriter pwriter); /** * Returns a mapping of time to session IDs that expire at that time. */ Map> getSessionExpiryMap(); /** * If this session tracker supports local sessions, return how many. * otherwise returns 0; */ long getLocalSessionCount(); boolean isLocalSessionsEnabled(); /** * Get a set of global session IDs */ Set globalSessions(); /** * Get a set of local session IDs */ Set localSessions(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000163 15051152474 032641 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SessionTrackerImpl.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SessionTrackerImpl0100644 0000000 0000000 00000027542 15051152474 034316 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.PrintWriter; import java.io.StringWriter; import java.text.MessageFormat; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a full featured SessionTracker. It tracks session in grouped by tick * interval. It always rounds up the tick interval to provide a sort of grace * period. Sessions are thus expired in batches made up of sessions that expire * in a given interval. */ public class SessionTrackerImpl extends ZooKeeperCriticalThread implements SessionTracker { private static final Logger LOG = LoggerFactory.getLogger(SessionTrackerImpl.class); protected final ConcurrentHashMap sessionsById = new ConcurrentHashMap<>(); private final ExpiryQueue sessionExpiryQueue; protected final ConcurrentMap sessionsWithTimeout; private final AtomicLong nextSessionId = new AtomicLong(); public static class SessionImpl implements Session { SessionImpl(long sessionId, int timeout) { this.sessionId = sessionId; this.timeout = timeout; isClosing = false; } final long sessionId; final int timeout; boolean isClosing; Object owner; public long getSessionId() { return sessionId; } public int getTimeout() { return timeout; } public boolean isClosing() { return isClosing; } public String toString() { return "0x" + Long.toHexString(sessionId); } } /** * Generates an initial sessionId. * *

High order 1 byte is serverId, next 5 bytes are from timestamp, and low order 2 bytes are 0s. * Use {@literal ">>> 8"}, not {@literal ">> 8"} to make sure that the high order 1 byte is entirely * up to the server Id. * *

See also http://jira.apache.org/jira/browse/ZOOKEEPER-1622 * * @param id server Id * @return the session Id */ public static long initializeNextSessionId(long id) { long nextSid; nextSid = (Time.currentElapsedTime() << 24) >>> 8; nextSid = nextSid | (id << 56); if (nextSid == EphemeralType.CONTAINER_EPHEMERAL_OWNER) { ++nextSid; // this is an unlikely edge case, but check it just in case } return nextSid; } private final SessionExpirer expirer; public SessionTrackerImpl(SessionExpirer expirer, ConcurrentMap sessionsWithTimeout, int tickTime, long serverId, ZooKeeperServerListener listener) { super("SessionTracker", listener); this.expirer = expirer; this.sessionExpiryQueue = new ExpiryQueue<>(tickTime); this.sessionsWithTimeout = sessionsWithTimeout; this.nextSessionId.set(initializeNextSessionId(serverId)); for (Entry e : sessionsWithTimeout.entrySet()) { trackSession(e.getKey(), e.getValue()); } EphemeralType.validateServerId(serverId); } volatile boolean running = true; public void dumpSessions(PrintWriter pwriter) { pwriter.print("Session "); sessionExpiryQueue.dump(pwriter); } /** * Returns a mapping from time to session IDs of sessions expiring at that time. */ public synchronized Map> getSessionExpiryMap() { // Convert time -> sessions map to time -> session IDs map Map> expiryMap = sessionExpiryQueue.getExpiryMap(); Map> sessionExpiryMap = new TreeMap<>(); for (Entry> e : expiryMap.entrySet()) { Set ids = new HashSet<>(); sessionExpiryMap.put(e.getKey(), ids); for (SessionImpl s : e.getValue()) { ids.add(s.sessionId); } } return sessionExpiryMap; } @Override public String toString() { StringWriter sw = new StringWriter(); PrintWriter pwriter = new PrintWriter(sw); dumpSessions(pwriter); pwriter.flush(); pwriter.close(); return sw.toString(); } @Override public void run() { try { while (running) { long waitTime = sessionExpiryQueue.getWaitTime(); if (waitTime > 0) { Thread.sleep(waitTime); continue; } for (SessionImpl s : sessionExpiryQueue.poll()) { ServerMetrics.getMetrics().STALE_SESSIONS_EXPIRED.add(1); setSessionClosing(s.sessionId); expirer.expire(s); } } } catch (InterruptedException e) { handleException(this.getName(), e); } LOG.info("SessionTrackerImpl exited loop!"); } public synchronized boolean touchSession(long sessionId, int timeout) { SessionImpl s = sessionsById.get(sessionId); if (s == null) { logTraceTouchInvalidSession(sessionId, timeout); return false; } if (s.isClosing()) { logTraceTouchClosingSession(sessionId, timeout); return false; } updateSessionExpiry(s, timeout); return true; } private void updateSessionExpiry(SessionImpl s, int timeout) { logTraceTouchSession(s.sessionId, timeout, ""); sessionExpiryQueue.update(s, timeout); } private void logTraceTouchSession(long sessionId, int timeout, String sessionStatus) { if (LOG.isTraceEnabled()) { String msg = MessageFormat.format( "SessionTrackerImpl --- Touch {0}session: 0x{1} with timeout {2}", sessionStatus, Long.toHexString(sessionId), Integer.toString(timeout)); ZooTrace.logTraceMessage(LOG, ZooTrace.CLIENT_PING_TRACE_MASK, msg); } } private void logTraceTouchInvalidSession(long sessionId, int timeout) { logTraceTouchSession(sessionId, timeout, "invalid "); } private void logTraceTouchClosingSession(long sessionId, int timeout) { logTraceTouchSession(sessionId, timeout, "closing "); } public int getSessionTimeout(long sessionId) { return sessionsWithTimeout.get(sessionId); } public synchronized void setSessionClosing(long sessionId) { if (LOG.isTraceEnabled()) { LOG.trace("Session closing: 0x{}", Long.toHexString(sessionId)); } SessionImpl s = sessionsById.get(sessionId); if (s == null) { return; } s.isClosing = true; } public synchronized void removeSession(long sessionId) { LOG.debug("Removing session 0x{}", Long.toHexString(sessionId)); SessionImpl s = sessionsById.remove(sessionId); sessionsWithTimeout.remove(sessionId); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "SessionTrackerImpl --- Removing session 0x" + Long.toHexString(sessionId)); } if (s != null) { sessionExpiryQueue.remove(s); } } public void shutdown() { LOG.info("Shutting down"); running = false; if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(), "Shutdown SessionTrackerImpl!"); } } public long createSession(int sessionTimeout) { long sessionId = nextSessionId.getAndIncrement(); trackSession(sessionId, sessionTimeout); return sessionId; } @Override public synchronized boolean trackSession(long id, int sessionTimeout) { boolean added = false; SessionImpl session = sessionsById.get(id); if (session == null) { session = new SessionImpl(id, sessionTimeout); } // findbugs2.0.3 complains about get after put. // long term strategy would be use computeIfAbsent after JDK 1.8 SessionImpl existedSession = sessionsById.putIfAbsent(id, session); if (existedSession != null) { session = existedSession; } else { added = true; LOG.debug("Adding session 0x{}", Long.toHexString(id)); } if (LOG.isTraceEnabled()) { String actionStr = added ? "Adding" : "Existing"; ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "SessionTrackerImpl --- " + actionStr + " session 0x" + Long.toHexString(id) + " " + sessionTimeout); } updateSessionExpiry(session, sessionTimeout); return added; } public synchronized boolean commitSession(long id, int sessionTimeout) { return sessionsWithTimeout.put(id, sessionTimeout) == null; } public boolean isTrackingSession(long sessionId) { return sessionsById.containsKey(sessionId); } public synchronized void checkSession(long sessionId, Object owner) throws KeeperException.SessionExpiredException, KeeperException.SessionMovedException, KeeperException.UnknownSessionException { LOG.debug("Checking session 0x{}", Long.toHexString(sessionId)); SessionImpl session = sessionsById.get(sessionId); if (session == null) { throw new KeeperException.UnknownSessionException(); } if (session.isClosing()) { throw new KeeperException.SessionExpiredException(); } if (session.owner == null) { session.owner = owner; } else if (session.owner != owner) { throw new KeeperException.SessionMovedException(); } } public synchronized void setOwner(long id, Object owner) throws SessionExpiredException { SessionImpl session = sessionsById.get(id); if (session == null || session.isClosing()) { throw new KeeperException.SessionExpiredException(); } session.owner = owner; } public void checkGlobalSession(long sessionId, Object owner) throws KeeperException.SessionExpiredException, KeeperException.SessionMovedException { try { checkSession(sessionId, owner); } catch (KeeperException.UnknownSessionException e) { throw new KeeperException.SessionExpiredException(); } } public long getLocalSessionCount() { return 0; } @Override public boolean isLocalSessionsEnabled() { return false; } public Set globalSessions() { return sessionsById.keySet(); } public Set localSessions() { return Collections.emptySet(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Si0100644 0000000 0000000 00000000164 15051152474 032646 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SimpleRequestRecord.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SimpleRequestRecor0100644 0000000 0000000 00000004156 15051152474 034326 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.util.function.Supplier; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; public class SimpleRequestRecord implements RequestRecord { private final Record record; private volatile byte[] bytes; public SimpleRequestRecord(Record record) { this.record = record; } @SuppressWarnings("unchecked") @Override public T readRecord(Supplier constructor) { return (T) record; } @SuppressFBWarnings("EI_EXPOSE_REP") @Override public byte[] readBytes() { if (bytes != null) { return bytes; } try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); record.serialize(boa, "request"); bytes = baos.toByteArray(); return bytes; } catch (IOException e) { throw new UncheckedIOException(e); } } @Override public int limit() { byte[] bytes = readBytes(); return ByteBuffer.wrap(bytes).limit(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Sn0100644 0000000 0000000 00000000161 15051152474 032650 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotComparer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotComparer.j0100644 0000000 0000000 00000045346 15051152474 034257 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.zip.CheckedInputStream; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; import org.apache.zookeeper.server.persistence.FileSnap; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.util.ServiceUtils; /** * SnapshotComparer is a tool that loads and compares two snapshots with configurable threshold and various filters, and outputs information about the delta. * The delta includes specific znode paths added, updated, deleted comparing one snapshot to another. * It's useful in use cases that involve snapshot analysis, such as offline data consistency checking, and data trending analysis (e.g. what's growing under which zNode path during when). * Only outputs information about permanent nodes, ignoring both sessions and ephemeral nodes. */ public class SnapshotComparer { private final Options options; private static final String leftOption = "left"; private static final String rightOption = "right"; private static final String byteThresholdOption = "bytes"; private static final String nodeThresholdOption = "nodes"; private static final String debugOption = "debug"; private static final String interactiveOption = "interactive"; @SuppressWarnings("static") private SnapshotComparer() { options = new Options(); options.addOption( Option.builder("l") .hasArg() .required(true) .longOpt(leftOption) .desc("(Required) The left snapshot file.") .argName("LEFT") .type(File.class) .build()); options.addOption( Option.builder("r") .hasArg() .required(true) .longOpt(rightOption) .desc("(Required) The right snapshot file.") .argName("RIGHT") .type(File.class) .build()); options.addOption( Option.builder("b") .hasArg() .required(true) .longOpt(byteThresholdOption) .desc("(Required) The node data delta size threshold, in bytes, for printing the node.") .argName("BYTETHRESHOLD") .type(String.class) .build()); options.addOption( Option.builder("n") .hasArg() .required(true) .longOpt(nodeThresholdOption) .desc("(Required) The descendant node delta size threshold, in nodes, for printing the node.") .argName("NODETHRESHOLD") .type(String.class) .build()); options.addOption("d", debugOption, false, "Use debug output."); options.addOption("i", interactiveOption, false, "Enter interactive mode."); } private void usage() { HelpFormatter help = new HelpFormatter(); help.printHelp( 120, "java -cp " + SnapshotComparer.class.getName(), "", options, ""); } public static void main(String[] args) throws Exception { SnapshotComparer app = new SnapshotComparer(); app.compareSnapshots(args); } private void compareSnapshots(String[] args) throws Exception { CommandLine parsedOptions; try { parsedOptions = new DefaultParser().parse(options, args); } catch (ParseException e) { System.err.println(e.getMessage()); usage(); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); return; } File left = (File) parsedOptions.getParsedOptionValue(leftOption); File right = (File) parsedOptions.getParsedOptionValue(rightOption); int byteThreshold = Integer.parseInt((String) parsedOptions.getParsedOptionValue(byteThresholdOption)); int nodeThreshold = Integer.parseInt((String) parsedOptions.getParsedOptionValue(nodeThresholdOption)); boolean debug = parsedOptions.hasOption(debugOption); boolean interactive = parsedOptions.hasOption(interactiveOption); System.out.println("Successfully parsed options!"); TreeInfo leftTree = new TreeInfo(left); TreeInfo rightTree = new TreeInfo(right); System.out.println(leftTree.toString()); System.out.println(rightTree.toString()); compareTrees(leftTree, rightTree, byteThreshold, nodeThreshold, debug, interactive); } private static class TreeInfo { public static class TreeNode { final String label; final long size; final List children; long descendantSize; long descendantCount; public static class AlphabeticComparator implements Comparator, Serializable { private static final long serialVersionUID = 2601197766392565593L; public int compare(TreeNode left, TreeNode right) { if (left == right) { return 0; } if (left == null) { return -1; } if (right == null) { return 1; } return left.label.compareTo(right.label); } } public TreeNode(String label, long size) { this.label = label; this.size = size; this.children = new ArrayList<>(); } void populateChildren(String path, DataTree dataTree, TreeInfo treeInfo) throws Exception { populateChildren(path, dataTree, treeInfo, 1); } void populateChildren(String path, DataTree dataTree, TreeInfo treeInfo, int currentDepth) throws Exception { List childLabels = null; childLabels = dataTree.getChildren(path, null, null); if (childLabels != null && !childLabels.isEmpty()) { for (String childName : childLabels){ String childPath = path + "/" + childName; DataNode childNode = dataTree.getNode(childPath); long size; synchronized (childNode) { size = childNode.data == null ? 0 : childNode.data.length; } TreeNode childTreeNode = new TreeNode(childPath, size); childTreeNode.populateChildren(childPath, dataTree, treeInfo, currentDepth + 1); children.add(childTreeNode); } } descendantSize = 0; descendantCount = 0; for (TreeNode child : children) { descendantSize += child.descendantSize; descendantCount += child.descendantCount; } descendantSize += this.size; descendantCount += this.children.size(); treeInfo.registerNode(this, currentDepth); } } final TreeNode root; long count; List> nodesAtDepths = new ArrayList<>(); Map nodesByName = new HashMap<>(); TreeInfo(File snapshot) throws Exception { DataTree dataTree = getSnapshot(snapshot); count = 0; long beginning = System.nanoTime(); DataNode root = dataTree.getNode(""); long size = root.data == null ? 0 : root.data.length; this.root = new TreeNode("", size); // Construct TreeInfo tree from DataTree this.root.populateChildren("", dataTree, this); long end = System.nanoTime(); System.out.println(String.format("Processed data tree in %f seconds", ((((double) end - beginning) / 1000000)) / 1000)); } void registerNode(TreeNode node, int depth) { while (depth > nodesAtDepths.size()) { nodesAtDepths.add(new ArrayList<>()); } nodesAtDepths.get(depth - 1).add(node); nodesByName.put(node.label, node); this.count++; } public String toString() { StringBuilder builder = new StringBuilder(); builder.append(String.format("Node count: %d%n", count)); builder.append(String.format("Total size: %d%n", root.descendantSize)); builder.append(String.format("Max depth: %d%n", nodesAtDepths.size())); for (int i = 0; i < nodesAtDepths.size(); i++) { builder.append(String.format("Count of nodes at depth %d: %d%n", i, nodesAtDepths.get(i).size())); } return builder.toString(); } public static Comparator MakeAlphabeticComparator() { return new TreeNode.AlphabeticComparator(); } } /** * Parse a Zookeeper snapshot file to DataTree * @param file the snapshot file * @throws Exception */ private static DataTree getSnapshot(File file) throws Exception { DataTree dataTree = new DataTree(); Map sessions = new HashMap<>(); CheckedInputStream snapIS = SnapStream.getInputStream(file); long beginning = System.nanoTime(); InputArchive ia = BinaryInputArchive.getArchive(snapIS); FileSnap.deserialize(dataTree, sessions, ia); long end = System.nanoTime(); System.out.println(String.format("Deserialized snapshot in %s in %f seconds", file.getName(), (((double) (end - beginning) / 1000000)) / 1000)); return dataTree; } private static void printThresholdInfo(int byteThreshold, int nodeThreshold) { System.out.println(String.format("Printing analysis for nodes difference larger than %d bytes or node count difference larger than %d.", byteThreshold, nodeThreshold)); } private static void compareTrees(TreeInfo left, TreeInfo right, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { int maxDepth = Math.max(left.nodesAtDepths.size(), right.nodesAtDepths.size()); if (!interactive) { printThresholdInfo(byteThreshold, nodeThreshold); for (int i = 0; i < maxDepth; i++) { System.out.println(String.format("Analysis for depth %d", i)); compareLine(left, right, i, byteThreshold, nodeThreshold, debug, interactive); } } else { // interactive mode Scanner scanner = new Scanner(System.in); int currentDepth = 0; while (currentDepth < maxDepth) { System.out.println(String.format("Current depth is %d", currentDepth)); System.out.println("- Press enter to move to print current depth layer;\n- Type a number to jump to and print all nodes at a given depth;\n- Enter an ABSOLUTE path to print the immediate subtree of a node. Path must start with '/'."); String input = scanner.nextLine(); printThresholdInfo(byteThreshold, nodeThreshold); if (input.isEmpty()) { // input is Enter System.out.println(String.format("Analysis for depth %d", currentDepth)); compareLine(left, right, currentDepth, byteThreshold, nodeThreshold, debug, interactive); currentDepth++; } else { // input is a path if (input.startsWith("/")){ System.out.println(String.format("Analysis for node %s", input)); compareSubtree(left, right, input, byteThreshold, nodeThreshold, debug, interactive); } else { // input is a number try { int depth = Integer.parseInt(input); if (depth < 0 || depth >= maxDepth) { System.out.println(String.format("Depth must be in range [%d, %d]", 0, maxDepth - 1)); continue; } currentDepth = depth; System.out.println(String.format("Analysis for depth %d", currentDepth)); compareLine(left, right, currentDepth, byteThreshold, nodeThreshold, debug, interactive); } catch (NumberFormatException ex) { // input is invalid System.out.println(String.format("Input %s is not valid. Depth must be in range [%d, %d]. Path must be an absolute path which starts with '/'.", input, 0, maxDepth - 1)); } } } System.out.println(""); } } System.out.println("All layers compared."); } private static void compareSubtree(TreeInfo left, TreeInfo right, String path, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { TreeInfo.TreeNode leftRoot = left.nodesByName.get(path); TreeInfo.TreeNode rightRoot = right.nodesByName.get(path); List leftList = leftRoot == null ? new ArrayList() : leftRoot.children; List rightList = rightRoot == null ? new ArrayList() : rightRoot.children; if (leftRoot == null && rightRoot == null) { System.out.println(String.format("Path %s is neither found in left tree nor right tree.", path)); } else { compareNodes(leftList, rightList, byteThreshold, nodeThreshold, debug, interactive); } } /** * Compare left tree and right tree at the same depth. * @param left the left data tree * @param right the right data tree * @param depth the depth of the data tree to be compared at * @param byteThreshold the node data delta size threshold, in bytes, for printing the node * @param nodeThreshold the descendant node delta size threshold, in nodes, for printing the node * @param debug If true, print more detailed debug information * @param interactive If true, enter interactive mode */ private static void compareLine(TreeInfo left, TreeInfo right, int depth, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { List leftList = depth >= left.nodesAtDepths.size() ? new ArrayList<>() : left.nodesAtDepths.get(depth); List rightList = depth >= right.nodesAtDepths.size() ? new ArrayList<>() : right.nodesAtDepths.get(depth); compareNodes(leftList, rightList, byteThreshold, nodeThreshold, debug, interactive); } private static void compareNodes(List leftList, List rightList, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { Comparator alphabeticComparator = TreeInfo.MakeAlphabeticComparator(); Collections.sort(leftList, alphabeticComparator); Collections.sort(rightList, alphabeticComparator); int leftIndex = 0; int rightIndex = 0; boolean leftRemaining = leftList.size() > leftIndex; boolean rightRemaining = rightList.size() > rightIndex; while (leftRemaining || rightRemaining) { TreeInfo.TreeNode leftNode = null; if (leftRemaining) { leftNode = leftList.get(leftIndex); } TreeInfo.TreeNode rightNode = null; if (rightRemaining) { rightNode = rightList.get(rightIndex); } if (leftNode != null && rightNode != null) { if (debug) { System.out.println(String.format("Comparing %s to %s", leftNode.label, rightNode.label)); } int result = leftNode.label.compareTo(rightNode.label); if (result < 0) { if (debug) { System.out.println("left is less"); } printLeftOnly(leftNode, byteThreshold, nodeThreshold, debug, interactive); leftIndex++; } else if (result > 0) { if (debug) { System.out.println("right is less"); } printRightOnly(rightNode, byteThreshold, nodeThreshold, debug, interactive); rightIndex++; } else { if (debug) { System.out.println("same"); } printBoth(leftNode, rightNode, byteThreshold, nodeThreshold, debug, interactive); leftIndex++; rightIndex++; } } else if (leftNode != null) { printLeftOnly(leftNode, byteThreshold, nodeThreshold, debug, interactive); leftIndex++; } else { printRightOnly(rightNode, byteThreshold, nodeThreshold, debug, interactive); rightIndex++; } leftRemaining = leftList.size() > leftIndex; rightRemaining = rightList.size() > rightIndex; } } static void printLeftOnly(TreeInfo.TreeNode node, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { if (node.descendantSize > byteThreshold || node.descendantCount > nodeThreshold) { StringBuilder builder = new StringBuilder(); builder.append(String.format("Node %s found only in left tree. ", node.label)); printNode(node, builder); System.out.println(builder.toString()); } else if (debug || interactive) { System.out.println(String.format("Filtered left node %s of size %d", node.label, node.descendantSize)); } } static void printRightOnly(TreeInfo.TreeNode node, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { if (node.descendantSize > byteThreshold || node.descendantCount > nodeThreshold) { StringBuilder builder = new StringBuilder(); builder.append(String.format("Node %s found only in right tree. ", node.label)); printNode(node, builder); System.out.println(builder.toString()); } else if (debug || interactive) { System.out.println(String.format("Filtered right node %s of size %d", node.label, node.descendantSize)); } } static void printBoth(TreeInfo.TreeNode leftNode, TreeInfo.TreeNode rightNode, int byteThreshold, int nodeThreshold, boolean debug, boolean interactive) { if (Math.abs(rightNode.descendantSize - leftNode.descendantSize) > byteThreshold || Math.abs(rightNode.descendantCount - leftNode.descendantCount) > nodeThreshold) { System.out.println(String.format( "Node %s found in both trees. Delta: %d bytes, %d descendants", leftNode.label, rightNode.descendantSize - leftNode.descendantSize, rightNode.descendantCount - leftNode.descendantCount)); } else if (debug || interactive) { System.out.println(String.format("Filtered node %s of left size %d, right size %d", leftNode.label, leftNode.descendantSize, rightNode.descendantSize)); } } static void printNode(TreeInfo.TreeNode node, StringBuilder builder) { builder.append(String.format("Descendant size: %d. Descendant count: %d", node.descendantSize, node.descendantCount)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Sn0100644 0000000 0000000 00000000162 15051152474 032651 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotFormatter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotFormatter.0100644 0000000 0000000 00000022114 15051152474 034264 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.server.persistence.FileSnap.SNAPSHOT_FILE_PREFIX; import com.fasterxml.jackson.core.io.JsonStringEncoder; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.server.persistence.FileSnap; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.util.ServiceUtils; /** * Dump a snapshot file to stdout. * * For JSON format, followed https://dev.yorhel.nl/ncdu/jsonfmt */ @InterfaceAudience.Public public class SnapshotFormatter { // per-znode counter so ncdu treats each as a unique object private static Integer INODE_IDX = 1000; /** * USAGE: SnapshotFormatter snapshot_file or the ready-made script: zkSnapShotToolkit.sh */ public static void main(String[] args) throws Exception { String snapshotFile = null; boolean dumpData = false; boolean dumpJson = false; int i; for (i = 0; i < args.length; i++) { if (args[i].equals("-d")) { dumpData = true; } else if (args[i].equals("-json")) { dumpJson = true; } else { snapshotFile = args[i]; i++; break; } } if (args.length != i || snapshotFile == null) { System.err.println("USAGE: SnapshotFormatter [-d|-json] snapshot_file"); System.err.println(" -d dump the data for each znode"); System.err.println(" -json dump znode info in json format"); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); return; } String error = ZKUtil.validateFileInput(snapshotFile); if (null != error) { System.err.println(error); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } if (dumpData && dumpJson) { System.err.println("Cannot specify both data dump (-d) and json mode (-json) in same call"); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } new SnapshotFormatter().run(snapshotFile, dumpData, dumpJson); } public void run(String snapshotFileName, boolean dumpData, boolean dumpJson) throws IOException { File snapshotFile = new File(snapshotFileName); try (InputStream is = SnapStream.getInputStream(snapshotFile)) { InputArchive ia = BinaryInputArchive.getArchive(is); DataTree dataTree = new DataTree(); Map sessions = new HashMap<>(); FileSnap.deserialize(dataTree, sessions, ia); long fileNameZxid = Util.getZxidFromName(snapshotFile.getName(), SNAPSHOT_FILE_PREFIX); if (dumpJson) { printSnapshotJson(dataTree); } else { printDetails(dataTree, sessions, dumpData, fileNameZxid); } } } private void printDetails(DataTree dataTree, Map sessions, boolean dumpData, long fileNameZxid) { long dtZxid = printZnodeDetails(dataTree, dumpData); printSessionDetails(dataTree, sessions); DataTree.ZxidDigest targetZxidDigest = dataTree.getDigestFromLoadedSnapshot(); if (targetZxidDigest != null) { System.out.println(String.format("Target zxid digest is: %s, %s", Long.toHexString(targetZxidDigest.zxid), targetZxidDigest.digest)); } System.out.println(String.format("----%nLast zxid: 0x%s", Long.toHexString(Math.max(fileNameZxid, dtZxid)))); } private long printZnodeDetails(DataTree dataTree, boolean dumpData) { System.out.println(String.format("ZNode Details (count=%d):", dataTree.getNodeCount())); final long zxid = printZnode(dataTree, "/", dumpData); System.out.println("----"); return zxid; } private long printZnode(DataTree dataTree, String name, boolean dumpData) { System.out.println("----"); DataNode n = dataTree.getNode(name); Set children; long zxid; synchronized (n) { // keep findbugs happy System.out.println(name); printStat(n.stat); zxid = Math.max(n.stat.getMzxid(), n.stat.getPzxid()); if (dumpData) { System.out.println(" data = " + (n.data == null ? "" : Base64.getEncoder().encodeToString(n.data))); } else { System.out.println(" dataLength = " + (n.data == null ? 0 : n.data.length)); } children = n.getChildren(); } if (children != null) { for (String child : children) { long cxid = printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child, dumpData); zxid = Math.max(zxid, cxid); } } return zxid; } private void printSessionDetails(DataTree dataTree, Map sessions) { System.out.println("Session Details (sid, timeout, ephemeralCount):"); for (Map.Entry e : sessions.entrySet()) { long sid = e.getKey(); System.out.println(String.format("%#016x, %d, %d", sid, e.getValue(), dataTree.getEphemerals(sid).size())); } } private void printStat(StatPersisted stat) { printHex("cZxid", stat.getCzxid()); System.out.println(" ctime = " + new Date(stat.getCtime()).toString()); printHex("mZxid", stat.getMzxid()); System.out.println(" mtime = " + new Date(stat.getMtime()).toString()); printHex("pZxid", stat.getPzxid()); System.out.println(" cversion = " + stat.getCversion()); System.out.println(" dataVersion = " + stat.getVersion()); System.out.println(" aclVersion = " + stat.getAversion()); printHex("ephemeralOwner", stat.getEphemeralOwner()); } private void printHex(String prefix, long value) { System.out.println(String.format(" %s = %#016x", prefix, value)); } private void printSnapshotJson(final DataTree dataTree) { JsonStringEncoder encoder = JsonStringEncoder.getInstance(); System.out.printf( "[1,0,{\"progname\":\"SnapshotFormatter.java\",\"progver\":\"0.01\",\"timestamp\":%d}", System.currentTimeMillis()); printZnodeJson(dataTree, "/", encoder); System.out.print("]"); } private void printZnodeJson(final DataTree dataTree, final String fullPath, JsonStringEncoder encoder) { final DataNode n = dataTree.getNode(fullPath); if (null == n) { System.err.println("DataTree Node for " + fullPath + " doesn't exist"); return; } final String name = fullPath.equals("/") ? fullPath : fullPath.substring(fullPath.lastIndexOf("/") + 1); System.out.print(","); int dataLen; synchronized (n) { // keep findbugs happy dataLen = (n.data == null) ? 0 : n.data.length; } StringBuilder nodeSB = new StringBuilder(); nodeSB.append("{"); nodeSB.append("\"name\":\"").append(encoder.quoteAsString(name)).append("\"").append(","); nodeSB.append("\"asize\":").append(dataLen).append(","); nodeSB.append("\"dsize\":").append(dataLen).append(","); nodeSB.append("\"dev\":").append(0).append(","); nodeSB.append("\"ino\":").append(++INODE_IDX); nodeSB.append("}"); Set children; synchronized (n) { // keep findbugs happy children = n.getChildren(); } if (children != null && children.size() > 0) { System.out.print("[" + nodeSB); for (String child : children) { printZnodeJson(dataTree, fullPath + (fullPath.equals("/") ? "" : "/") + child, encoder); } System.out.print("]"); } else { System.out.print(nodeSB); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Sn0100644 0000000 0000000 00000000171 15051152474 032651 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotRecursiveSummary.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SnapshotRecursiveS0100644 0000000 0000000 00000011765 15051152474 034347 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.server.persistence.FileSnap; import org.apache.zookeeper.server.persistence.SnapStream; /** * Recursively processes a snapshot file collecting child node count and summarizes the data size * below each node. * "starting_node" defines the node where the recursion starts * "max_depth" defines the depth where the tool still writes to the output. * 0 means there is no depth limit, every non-leaf node's stats will be displayed, 1 means it will * only contain the starting node's and it's children's stats, 2 ads another level and so on. * This ONLY affects the level of details displayed, NOT the calculation. */ @InterfaceAudience.Public public class SnapshotRecursiveSummary { /** * USAGE: SnapsotRecursiveSummary snapshot_file starting_node max_depth * */ public static void main(String[] args) throws Exception { if (args.length != 3) { System.err.println(getUsage()); System.exit(2); } int maxDepth = 0; try { maxDepth = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.err.println(getUsage()); System.exit(2); } new SnapshotRecursiveSummary().run(args[0], args[1], maxDepth); } public void run(String snapshotFileName, String startingNode, int maxDepth) throws IOException { File snapshotFile = new File(snapshotFileName); try (InputStream is = SnapStream.getInputStream(snapshotFile)) { InputArchive ia = BinaryInputArchive.getArchive(is); DataTree dataTree = new DataTree(); Map sessions = new HashMap<>(); FileSnap.deserialize(dataTree, sessions, ia); printZnodeDetails(dataTree, startingNode, maxDepth); } } private void printZnodeDetails(DataTree dataTree, String startingNode, int maxDepth) { StringBuilder builder = new StringBuilder(); printZnode(dataTree, startingNode, builder, 0, maxDepth); System.out.println(builder); } private long[] printZnode(DataTree dataTree, String name, StringBuilder builder, int level, int maxDepth) { DataNode n = dataTree.getNode(name); Set children; long dataSum = 0L; synchronized (n) { // keep findbugs happy if (n.data != null) { dataSum += n.data.length; } children = n.getChildren(); } long[] result = {1L, dataSum}; if (children.size() == 0) { return result; } StringBuilder childBuilder = new StringBuilder(); for (String child : children) { long[] childResult = printZnode(dataTree, name + (name.equals("/") ? "" : "/") + child, childBuilder, level + 1, maxDepth); result[0] = result[0] + childResult[0]; result[1] = result[1] + childResult[1]; } if (maxDepth == 0 || level <= maxDepth) { String tab = String.join("", Collections.nCopies(level, "--")); builder.append(tab + " " + name + "\n"); builder.append(tab + " children: " + (result[0] - 1) + "\n"); builder.append(tab + " data: " + result[1] + "\n"); builder.append(childBuilder); } return result; } public static String getUsage() { String newLine = System.getProperty("line.separator"); return String.join(newLine, "USAGE:", newLine, "SnapshotRecursiveSummary ", newLine, "snapshot_file: path to the zookeeper snapshot", "starting_node: the path in the zookeeper tree where the traversal should begin", "max_depth: defines the depth where the tool still writes to the output. " + "0 means there is no depth limit, every non-leaf node's stats will be displayed, " + "1 means it will only contain the starting node's and it's children's stats, " + "2 ads another level and so on. This ONLY affects the level of details displayed, " + "NOT the calculation."); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/Stats.java0100644 0000000 0000000 00000004131 15051152474 032540 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Date; /** * Statistics on the ServerCnxn */ interface Stats { /** Date/time the connection was established * @since 3.3.0 */ Date getEstablished(); /** * The number of requests that have been submitted but not yet * responded to. */ long getOutstandingRequests(); /** Number of packets received */ long getPacketsReceived(); /** Number of packets sent (incl notifications) */ long getPacketsSent(); /** Min latency in ms * @since 3.3.0 */ long getMinLatency(); /** Average latency in ms * @since 3.3.0 */ long getAvgLatency(); /** Max latency in ms * @since 3.3.0 */ long getMaxLatency(); /** Last operation performed by this connection * @since 3.3.0 */ String getLastOperation(); /** Last cxid of this connection * @since 3.3.0 */ long getLastCxid(); /** Last zxid of this connection * @since 3.3.0 */ long getLastZxid(); /** Last time server sent a response to client on this connection * @since 3.3.0 */ long getLastResponseTime(); /** Latency of last response to client on this connection in ms * @since 3.3.0 */ long getLastLatency(); /** Reset counters * @since 3.3.0 */ void resetStats(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Sy0100644 0000000 0000000 00000000165 15051152474 032667 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SyncRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/SyncRequestProcess0100644 0000000 0000000 00000025220 15051152474 034350 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.Flushable; import java.io.IOException; import java.util.ArrayDeque; import java.util.Objects; import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This RequestProcessor logs requests to disk. It batches the requests to do * the io efficiently. The request is not passed to the next RequestProcessor * until its log has been synced to disk. * * SyncRequestProcessor is used in 3 different cases * 1. Leader - Sync request to disk and forward it to AckRequestProcessor which * send ack back to itself. * 2. Follower - Sync request to disk and forward request to * SendAckRequestProcessor which send the packets to leader. * SendAckRequestProcessor is flushable which allow us to force * push packets to leader. * 3. Observer - Sync committed request to disk (received as INFORM packet). * It never send ack back to the leader, so the nextProcessor will * be null. This change the semantic of txnlog on the observer * since it only contains committed txns. */ public class SyncRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(SyncRequestProcessor.class); private static final Request REQUEST_OF_DEATH = Request.requestOfDeath; /** The number of log entries to log before starting a snapshot */ private static int snapCount = ZooKeeperServer.getSnapCount(); /** * The total size of log entries before starting a snapshot */ private static long snapSizeInBytes = ZooKeeperServer.getSnapSizeInBytes(); /** * Random numbers used to vary snapshot timing */ private int randRoll; private long randSize; private final BlockingQueue queuedRequests = new LinkedBlockingQueue<>(); private final Semaphore snapThreadMutex = new Semaphore(1); private final ZooKeeperServer zks; private final RequestProcessor nextProcessor; /** * Transactions that have been written and are waiting to be flushed to * disk. Basically this is the list of SyncItems whose callbacks will be * invoked after flush returns successfully. */ private final Queue toFlush; private long lastFlushTime; public SyncRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { super("SyncThread:" + zks.getServerId(), zks.getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; this.toFlush = new ArrayDeque<>(zks.getMaxBatchSize()); } /** * used by tests to check for changing * snapcounts * @param count */ public static void setSnapCount(int count) { snapCount = count; } /** * used by tests to get the snapcount * @return the snapcount */ public static int getSnapCount() { return snapCount; } private long getRemainingDelay() { long flushDelay = zks.getFlushDelay(); long duration = Time.currentElapsedTime() - lastFlushTime; if (duration < flushDelay) { return flushDelay - duration; } return 0; } /** If both flushDelay and maxMaxBatchSize are set (bigger than 0), flush * whenever either condition is hit. If only one or the other is * set, flush only when the relevant condition is hit. */ private boolean shouldFlush() { long flushDelay = zks.getFlushDelay(); long maxBatchSize = zks.getMaxBatchSize(); if ((flushDelay > 0) && (getRemainingDelay() == 0)) { return true; } return (maxBatchSize > 0) && (toFlush.size() >= maxBatchSize); } /** * used by tests to check for changing * snapcounts * @param size */ public static void setSnapSizeInBytes(long size) { snapSizeInBytes = size; } private boolean shouldSnapshot() { int logCount = zks.getZKDatabase().getTxnCount(); long logSize = zks.getZKDatabase().getTxnSize(); return (logCount > (snapCount / 2 + randRoll)) || (snapSizeInBytes > 0 && logSize > (snapSizeInBytes / 2 + randSize)); } private void resetSnapshotStats() { randRoll = ThreadLocalRandom.current().nextInt(snapCount / 2); randSize = Math.abs(ThreadLocalRandom.current().nextLong() % (snapSizeInBytes / 2)); } @Override public void run() { try { // we do this in an attempt to ensure that not all of the servers // in the ensemble take a snapshot at the same time resetSnapshotStats(); lastFlushTime = Time.currentElapsedTime(); while (true) { ServerMetrics.getMetrics().SYNC_PROCESSOR_QUEUE_SIZE.add(queuedRequests.size()); long pollTime = Math.min(zks.getMaxWriteQueuePollTime(), getRemainingDelay()); Request si = queuedRequests.poll(pollTime, TimeUnit.MILLISECONDS); if (si == null) { /* We timed out looking for more writes to batch, go ahead and flush immediately */ flush(); si = queuedRequests.take(); } if (si == REQUEST_OF_DEATH) { break; } long startProcessTime = Time.currentElapsedTime(); ServerMetrics.getMetrics().SYNC_PROCESSOR_QUEUE_TIME.add(startProcessTime - si.syncQueueStartTime); // track the number of records written to the log if (!si.isThrottled() && zks.getZKDatabase().append(si)) { if (shouldSnapshot()) { resetSnapshotStats(); // roll the log zks.getZKDatabase().rollLog(); // take a snapshot if (!snapThreadMutex.tryAcquire()) { LOG.warn("Too busy to snap, skipping"); } else { new ZooKeeperThread("Snapshot Thread") { public void run() { try { zks.takeSnapshot(); } catch (Exception e) { LOG.warn("Unexpected exception", e); } finally { snapThreadMutex.release(); } } }.start(); } } } else if (toFlush.isEmpty()) { // optimization for read heavy workloads // iff this is a read or a throttled request(which doesn't need to be written to the disk), // and there are no pending flushes (writes), then just pass this to the next processor if (nextProcessor != null) { nextProcessor.processRequest(si); if (nextProcessor instanceof Flushable) { ((Flushable) nextProcessor).flush(); } } continue; } toFlush.add(si); if (shouldFlush()) { flush(); } ServerMetrics.getMetrics().SYNC_PROCESS_TIME.add(Time.currentElapsedTime() - startProcessTime); } } catch (Throwable t) { handleException(this.getName(), t); } LOG.info("SyncRequestProcessor exited!"); } private void flush() throws IOException, RequestProcessorException { if (this.toFlush.isEmpty()) { return; } ServerMetrics.getMetrics().BATCH_SIZE.add(toFlush.size()); long flushStartTime = Time.currentElapsedTime(); zks.getZKDatabase().commit(); ServerMetrics.getMetrics().SYNC_PROCESSOR_FLUSH_TIME.add(Time.currentElapsedTime() - flushStartTime); if (this.nextProcessor == null) { this.toFlush.clear(); } else { while (!this.toFlush.isEmpty()) { final Request i = this.toFlush.remove(); long latency = Time.currentElapsedTime() - i.syncQueueStartTime; ServerMetrics.getMetrics().SYNC_PROCESSOR_QUEUE_AND_FLUSH_TIME.add(latency); this.nextProcessor.processRequest(i); } if (this.nextProcessor instanceof Flushable) { ((Flushable) this.nextProcessor).flush(); } } lastFlushTime = Time.currentElapsedTime(); } public void shutdown() { LOG.info("Shutting down"); queuedRequests.add(REQUEST_OF_DEATH); try { this.join(); this.flush(); } catch (InterruptedException e) { LOG.warn("Interrupted while wating for {} to finish", this); Thread.currentThread().interrupt(); } catch (IOException e) { LOG.warn("Got IO exception during shutdown"); } catch (RequestProcessorException e) { LOG.warn("Got request processor exception during shutdown"); } if (nextProcessor != null) { nextProcessor.shutdown(); } } public void processRequest(final Request request) { Objects.requireNonNull(request, "Request cannot be null"); request.syncQueueStartTime = Time.currentElapsedTime(); queuedRequests.add(request); ServerMetrics.getMetrics().SYNC_PROCESSOR_QUEUED.add(1); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Tr0100644 0000000 0000000 00000000157 15051152474 032662 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/TraceFormatter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/TraceFormatter.jav0100644 0000000 0000000 00000005702 15051152474 034230 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.text.DateFormat; import java.util.Date; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.util.ServiceUtils; public class TraceFormatter { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("USAGE: TraceFormatter trace_file"); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } FileChannel fc = new FileInputStream(args[0]).getChannel(); while (true) { ByteBuffer bb = ByteBuffer.allocate(41); fc.read(bb); bb.flip(); byte app = bb.get(); long time = bb.getLong(); long id = bb.getLong(); int cxid = bb.getInt(); long zxid = bb.getLong(); int txnType = bb.getInt(); int type = bb.getInt(); int len = bb.getInt(); bb = ByteBuffer.allocate(len); fc.read(bb); bb.flip(); String path = "n/a"; if (bb.remaining() > 0) { if (type != OpCode.createSession) { int pathLen = bb.getInt(); byte[] b = new byte[pathLen]; bb.get(b); path = new String(b); } } System.out.println(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG).format(new Date(time)) + ": " + (char) app + " id=0x" + Long.toHexString(id) + " cxid=" + cxid + " op=" + Request.op2String(type) + " zxid=0x" + Long.toHexString(zxid) + " txnType=" + txnType + " len=" + len + " path=" + path); } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/TxnLogEntry.java0100644 0000000 0000000 00000003072 15051152474 033702 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.apache.jute.Record; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; /** * A helper class to represent the txn entry. */ public final class TxnLogEntry { private final Record txn; private final TxnHeader header; private final TxnDigest digest; public TxnLogEntry(Record txn, TxnHeader header, TxnDigest digest) { this.txn = txn; this.header = header; this.digest = digest; } public Record getTxn() { return txn; } public TxnHeader getHeader() { return header; } public TxnDigest getDigest() { return digest; } public Request toRequest() { return new Request(header, txn, digest); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Tx0100644 0000000 0000000 00000000167 15051152474 032671 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/TxnLogProposalIterator.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/TxnLogProposalIter0100644 0000000 0000000 00000006521 15051152474 034306 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.util.Iterator; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.QuorumPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides an iterator interface to access Proposal deserialized * from on-disk txnlog. The iterator deserializes one proposal at a time * to reduce memory footprint. Note that the request part of the proposal * is not initialized and set to null since we don't need it during * follower sync-up. * */ public class TxnLogProposalIterator implements Iterator { private static final Logger LOG = LoggerFactory.getLogger(TxnLogProposalIterator.class); public static final TxnLogProposalIterator EMPTY_ITERATOR = new TxnLogProposalIterator(); private boolean hasNext = false; private TxnIterator itr; @Override public boolean hasNext() { return hasNext; } /** * Proposal returned by this iterator has request part set to null, since * it is not used for follower sync-up. */ @Override public Proposal next() { Proposal p; try { byte[] serializedData = Util.marshallTxnEntry(itr.getHeader(), itr.getTxn(), itr.getDigest()); QuorumPacket pp = new QuorumPacket(Leader.PROPOSAL, itr.getHeader().getZxid(), serializedData, null); p = new Proposal(pp); // This is the only place that can throw IO exception hasNext = itr.next(); } catch (IOException e) { LOG.error("Unable to read txnlog from disk", e); hasNext = false; p = new Proposal(); } return p; } @Override public void remove() { throw new UnsupportedOperationException(); } /** * Close the files and release the resources which are used for iterating * transaction records */ public void close() { if (itr != null) { try { itr.close(); } catch (IOException ioe) { LOG.warn("Error closing file iterator", ioe); } } } private TxnLogProposalIterator() { } public TxnLogProposalIterator(TxnIterator itr) { if (itr != null) { this.itr = itr; hasNext = (itr.getHeader() != null); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Un0100644 0000000 0000000 00000000176 15051152474 032660 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/UnimplementedRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/UnimplementedReque0100644 0000000 0000000 00000003401 15051152474 034331 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.proto.ReplyHeader; /** * Manages the unknown requests (i.e. unknown OpCode), by: * - sending back the KeeperException.UnimplementedException() error code to the client * - closing the connection. */ public class UnimplementedRequestProcessor implements RequestProcessor { public void processRequest(Request request) throws RequestProcessorException { KeeperException ke = new KeeperException.UnimplementedException(); request.setException(ke); ReplyHeader rh = new ReplyHeader(request.cxid, request.zxid, ke.code().intValue()); try { request.cnxn.sendResponse(rh, null, "response"); } catch (IOException e) { throw new RequestProcessorException("Can't send the response", e); } request.cnxn.sendCloseSession(); } public void shutdown() { } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Wo0100644 0000000 0000000 00000000156 15051152474 032661 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/WorkerService.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/WorkerService.java0100644 0000000 0000000 00000021036 15051152474 034237 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.common.Time; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * WorkerService is a worker thread pool for running tasks and is implemented * using one or more ExecutorServices. A WorkerService can support assignable * threads, which it does by creating N separate single thread ExecutorServices, * or non-assignable threads, which it does by creating a single N-thread * ExecutorService. * - NIOServerCnxnFactory uses a non-assignable WorkerService because the * socket IO requests are order independent and allowing the * ExecutorService to handle thread assignment gives optimal performance. * - CommitProcessor uses an assignable WorkerService because requests for * a given session must be processed in order. * ExecutorService provides queue management and thread restarting, so it's * useful even with a single thread. */ public class WorkerService { private static final Logger LOG = LoggerFactory.getLogger(WorkerService.class); private final ArrayList workers = new ArrayList<>(); private final String threadNamePrefix; private int numWorkerThreads; private boolean threadsAreAssignable; private volatile boolean stopped = true; /** * @param name worker threads are named <name>Thread-## * @param numThreads number of worker threads (0 - N) * If 0, scheduled work is run immediately by * the calling thread. * @param useAssignableThreads whether the worker threads should be * individually assignable or not */ public WorkerService(String name, int numThreads, boolean useAssignableThreads) { this.threadNamePrefix = (name == null ? "" : name) + "Thread"; this.numWorkerThreads = numThreads; this.threadsAreAssignable = useAssignableThreads; start(); } /** * Callers should implement a class extending WorkRequest in order to * schedule work with the service. */ public abstract static class WorkRequest { /** * Must be implemented. Is called when the work request is run. */ public abstract void doWork() throws Exception; /** * (Optional) If implemented, is called if the service is stopped * or unable to schedule the request. */ public void cleanup() { } } /** * Schedule work to be done. If a worker thread pool is not being * used, work is done directly by this thread. This schedule API is * for use with non-assignable WorkerServices. For assignable * WorkerServices, will always run on the first thread. */ public void schedule(WorkRequest workRequest) { schedule(workRequest, 0); } /** * Schedule work to be done by the thread assigned to this id. Thread * assignment is a single mod operation on the number of threads. If a * worker thread pool is not being used, work is done directly by * this thread. */ public void schedule(WorkRequest workRequest, long id) { if (stopped) { workRequest.cleanup(); return; } ScheduledWorkRequest scheduledWorkRequest = new ScheduledWorkRequest(workRequest); // If we have a worker thread pool, use that; otherwise, do the work // directly. int size = workers.size(); if (size > 0) { try { // make sure to map negative ids as well to [0, size-1] int workerNum = ((int) (id % size) + size) % size; ExecutorService worker = workers.get(workerNum); worker.execute(scheduledWorkRequest); } catch (RejectedExecutionException e) { LOG.warn("ExecutorService rejected execution", e); workRequest.cleanup(); } } else { // When there is no worker thread pool, do the work directly // and wait for its completion scheduledWorkRequest.run(); } } private class ScheduledWorkRequest implements Runnable { private final WorkRequest workRequest; ScheduledWorkRequest(WorkRequest workRequest) { this.workRequest = workRequest; } @Override public void run() { try { // Check if stopped while request was on queue if (stopped) { workRequest.cleanup(); return; } workRequest.doWork(); } catch (Exception e) { LOG.warn("Unexpected exception", e); workRequest.cleanup(); } } } /** * ThreadFactory for the worker thread pool. We don't use the default * thread factory because (1) we want to give the worker threads easier * to identify names; and (2) we want to make the worker threads daemon * threads so they don't block the server from shutting down. */ private static class DaemonThreadFactory implements ThreadFactory { final ThreadGroup group; final AtomicInteger threadNumber = new AtomicInteger(1); final String namePrefix; DaemonThreadFactory(String name) { this(name, 1); } DaemonThreadFactory(String name, int firstThreadNum) { threadNumber.set(firstThreadNum); SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = name + "-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (!t.isDaemon()) { t.setDaemon(true); } if (t.getPriority() != Thread.NORM_PRIORITY) { t.setPriority(Thread.NORM_PRIORITY); } return t; } } public void start() { if (numWorkerThreads > 0) { if (threadsAreAssignable) { for (int i = 1; i <= numWorkerThreads; ++i) { workers.add(Executors.newFixedThreadPool(1, new DaemonThreadFactory(threadNamePrefix, i))); } } else { workers.add(Executors.newFixedThreadPool(numWorkerThreads, new DaemonThreadFactory(threadNamePrefix))); } } stopped = false; } public void stop() { stopped = true; // Signal for graceful shutdown for (ExecutorService worker : workers) { worker.shutdown(); } } public void join(long shutdownTimeoutMS) { // Give the worker threads time to finish executing long now = Time.currentElapsedTime(); long endTime = now + shutdownTimeoutMS; for (ExecutorService worker : workers) { boolean terminated = false; while ((now = Time.currentElapsedTime()) <= endTime) { try { terminated = worker.awaitTermination(endTime - now, TimeUnit.MILLISECONDS); break; } catch (InterruptedException e) { // ignore } } if (!terminated) { // If we've timed out, do a hard shutdown worker.shutdownNow(); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZKDatabase.java0100644 0000000 0000000 00000065735 15051152474 033434 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.zip.CheckedInputStream; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.persistence.FileSnap; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.FileTxnSnapLog.PlayBackListener; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.Leader.PureRequestProposal; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class maintains the in memory database of zookeeper * server states that includes the sessions, datatree and the * committed logs. It is booted up after reading the logs * and snapshots from the disk. */ public class ZKDatabase { private static final Logger LOG = LoggerFactory.getLogger(ZKDatabase.class); /** * make sure on a clear you take care of * all these members. */ protected DataTree dataTree; protected ConcurrentHashMap sessionsWithTimeouts; protected FileTxnSnapLog snapLog; protected long minCommittedLog, maxCommittedLog; private final boolean allowDiscontinuousProposals = Boolean.getBoolean("zookeeper.test.allowDiscontinuousProposals"); /** * Default value is to use snapshot if txnlog size exceeds 1/3 the size of snapshot */ public static final String SNAPSHOT_SIZE_FACTOR = "zookeeper.snapshotSizeFactor"; public static final double DEFAULT_SNAPSHOT_SIZE_FACTOR = 0.33; private double snapshotSizeFactor; public static final String COMMIT_LOG_COUNT = "zookeeper.commitLogCount"; public static final int DEFAULT_COMMIT_LOG_COUNT = 500; public int commitLogCount; protected Queue committedLog = new ArrayDeque<>(); protected ReentrantReadWriteLock logLock = new ReentrantReadWriteLock(); private volatile boolean initialized = false; /** * Number of txn since last snapshot; */ private AtomicInteger txnCount = new AtomicInteger(0); /** * the filetxnsnaplog that this zk database * maps to. There is a one to one relationship * between a filetxnsnaplog and zkdatabase. * @param snapLog the FileTxnSnapLog mapping this zkdatabase */ public ZKDatabase(FileTxnSnapLog snapLog) { dataTree = createDataTree(); sessionsWithTimeouts = new ConcurrentHashMap<>(); this.snapLog = snapLog; try { snapshotSizeFactor = Double.parseDouble( System.getProperty(SNAPSHOT_SIZE_FACTOR, Double.toString(DEFAULT_SNAPSHOT_SIZE_FACTOR))); if (snapshotSizeFactor > 1) { snapshotSizeFactor = DEFAULT_SNAPSHOT_SIZE_FACTOR; LOG.warn( "The configured {} is invalid, going to use the default {}", SNAPSHOT_SIZE_FACTOR, DEFAULT_SNAPSHOT_SIZE_FACTOR); } } catch (NumberFormatException e) { LOG.error( "Error parsing {}, using default value {}", SNAPSHOT_SIZE_FACTOR, DEFAULT_SNAPSHOT_SIZE_FACTOR); snapshotSizeFactor = DEFAULT_SNAPSHOT_SIZE_FACTOR; } LOG.info("{} = {}", SNAPSHOT_SIZE_FACTOR, snapshotSizeFactor); try { commitLogCount = Integer.parseInt( System.getProperty(COMMIT_LOG_COUNT, Integer.toString(DEFAULT_COMMIT_LOG_COUNT))); if (commitLogCount < DEFAULT_COMMIT_LOG_COUNT) { commitLogCount = DEFAULT_COMMIT_LOG_COUNT; LOG.warn( "The configured commitLogCount {} is less than the recommended {}, going to use the recommended one", COMMIT_LOG_COUNT, DEFAULT_COMMIT_LOG_COUNT); } } catch (NumberFormatException e) { LOG.error( "Error parsing {} - use default value {}", COMMIT_LOG_COUNT, DEFAULT_COMMIT_LOG_COUNT); commitLogCount = DEFAULT_COMMIT_LOG_COUNT; } LOG.info("{}={}", COMMIT_LOG_COUNT, commitLogCount); } /** * checks to see if the zk database has been * initialized or not. * @return true if zk database is initialized and false if not */ public boolean isInitialized() { return initialized; } /** * clear the zkdatabase. * Note to developers - be careful to see that * the clear method does clear out all the * data structures in zkdatabase. */ public void clear() { /* to be safe we just create a new * datatree. */ dataTree.shutdownWatcher(); dataTree = createDataTree(); sessionsWithTimeouts.clear(); WriteLock lock = logLock.writeLock(); try { lock.lock(); committedLog.clear(); minCommittedLog = 0; maxCommittedLog = 0; } finally { lock.unlock(); } initialized = false; } /** * the datatree for this zkdatabase * @return the datatree for this zkdatabase */ public DataTree getDataTree() { return this.dataTree; } /** * the committed log for this zk database * @return the committed log for this zkdatabase */ public long getmaxCommittedLog() { return maxCommittedLog; } /** * the minimum committed transaction log * available in memory * @return the minimum committed transaction * log available in memory */ public long getminCommittedLog() { return minCommittedLog; } /** * Get the lock that controls the committedLog. If you want to get the pointer to the committedLog, you need * to use this lock to acquire a read lock before calling getCommittedLog() * @return the lock that controls the committed log */ public ReentrantReadWriteLock getLogLock() { return logLock; } public synchronized Collection getCommittedLog() { final Collection result; ReadLock rl = logLock.readLock(); // make a copy if this thread is not already holding a lock if (logLock.getReadHoldCount() > 0) { result = this.committedLog; } else { rl.lock(); try { result = new ArrayList<>(this.committedLog); } finally { rl.unlock(); } } return Collections.unmodifiableCollection(result); } /** * get the last processed zxid from a datatree * @return the last processed zxid of a datatree */ public long getDataTreeLastProcessedZxid() { return dataTree.lastProcessedZxid; } /** * return the sessions in the datatree * @return the data tree sessions */ public Collection getSessions() { return dataTree.getSessions(); } /** * @return number of (global) sessions */ public long getSessionCount() { return sessionsWithTimeouts.size(); } /** * get sessions with timeouts * @return the hashmap of sessions with timeouts */ public ConcurrentHashMap getSessionWithTimeOuts() { return sessionsWithTimeouts; } private final PlayBackListener commitProposalPlaybackListener = new PlayBackListener() { public void onTxnLoaded(TxnHeader hdr, Record txn, TxnDigest digest) { addCommittedProposal(hdr, txn, digest); } }; /** * load the database from the disk onto memory and also add * the transactions to the committedlog in memory. * @return the last valid zxid on disk * @throws IOException */ public long loadDataBase() throws IOException { long startTime = Time.currentElapsedTime(); long zxid = snapLog.restore(dataTree, sessionsWithTimeouts, commitProposalPlaybackListener); initialized = true; long loadTime = Time.currentElapsedTime() - startTime; ServerMetrics.getMetrics().DB_INIT_TIME.add(loadTime); LOG.info("Snapshot loaded in {} ms, highest zxid is 0x{}, digest is {}", loadTime, Long.toHexString(zxid), dataTree.getTreeDigest()); return zxid; } /** * Fast-forward the database adding transactions from the committed log into memory. * @return the last valid zxid. * @throws IOException */ public long fastForwardDataBase() throws IOException { long zxid = snapLog.fastForwardFromEdits(dataTree, sessionsWithTimeouts, commitProposalPlaybackListener); initialized = true; return zxid; } private void addCommittedProposal(TxnHeader hdr, Record txn, TxnDigest digest) { Request r = new Request(0, hdr.getCxid(), hdr.getType(), hdr, txn, hdr.getZxid()); r.setTxnDigest(digest); addCommittedProposal(r); } /** * maintains a list of last committedLog * or so committed requests. This is used for * fast follower synchronization. * @param request committed request */ public void addCommittedProposal(Request request) { WriteLock wl = logLock.writeLock(); try { wl.lock(); if (committedLog.isEmpty()) { minCommittedLog = request.zxid; maxCommittedLog = request.zxid; } else if (request.zxid <= maxCommittedLog) { // This could happen if lastProcessedZxid is rewinded and database is re-synced. // Currently, it only happens in test codes, but it should also be safe for production path. return; } else if (!allowDiscontinuousProposals && request.zxid != maxCommittedLog + 1 && ZxidUtils.getEpochFromZxid(request.zxid) <= ZxidUtils.getEpochFromZxid(maxCommittedLog)) { String msg = String.format( "Committed proposal cached out of order: 0x%s is not the next proposal of 0x%s", ZxidUtils.zxidToString(request.zxid), ZxidUtils.zxidToString(maxCommittedLog)); LOG.error(msg); throw new IllegalStateException(msg); } PureRequestProposal p = new PureRequestProposal(request); committedLog.add(p); maxCommittedLog = p.getZxid(); if (committedLog.size() > commitLogCount) { committedLog.remove(); minCommittedLog = committedLog.peek().getZxid(); } } finally { wl.unlock(); } } public boolean isTxnLogSyncEnabled() { boolean enabled = snapshotSizeFactor >= 0; if (enabled) { LOG.info("On disk txn sync enabled with snapshotSizeFactor {}", snapshotSizeFactor); } else { LOG.info("On disk txn sync disabled"); } return enabled; } public long calculateTxnLogSizeLimit() { long snapSize = 0; try { File snapFile = snapLog.findMostRecentSnapshot(); if (snapFile != null) { snapSize = snapFile.length(); } } catch (IOException e) { LOG.error("Unable to get size of most recent snapshot"); } return (long) (snapSize * snapshotSizeFactor); } /** * Get proposals from txnlog. Only packet part of proposal is populated. * * @param startZxid the starting zxid of the proposal * @param sizeLimit maximum on-disk size of txnlog to fetch * 0 is unlimited, negative value means disable. * @return list of proposal (request part of each proposal is null) */ public Iterator getProposalsFromTxnLog(long startZxid, long sizeLimit) { if (sizeLimit < 0) { LOG.debug("Negative size limit - retrieving proposal via txnlog is disabled"); return TxnLogProposalIterator.EMPTY_ITERATOR; } TxnIterator itr = null; try { itr = snapLog.readTxnLog(startZxid, false); // If we cannot guarantee that this is strictly the starting txn // after a given zxid, we should fail. if ((itr.getHeader() != null) && (itr.getHeader().getZxid() > startZxid)) { LOG.warn( "Unable to find proposals from txnlog for zxid: 0x{}", Long.toHexString(startZxid)); itr.close(); return TxnLogProposalIterator.EMPTY_ITERATOR; } if (sizeLimit > 0) { long txnSize = itr.getStorageSize(); if (txnSize > sizeLimit) { LOG.info("Txnlog size: {} exceeds sizeLimit: {}", txnSize, sizeLimit); itr.close(); return TxnLogProposalIterator.EMPTY_ITERATOR; } } } catch (IOException e) { LOG.error("Unable to read txnlog from disk", e); try { if (itr != null) { itr.close(); } } catch (IOException ioe) { LOG.warn("Error closing file iterator", ioe); } return TxnLogProposalIterator.EMPTY_ITERATOR; } return new TxnLogProposalIterator(itr); } public List aclForNode(DataNode n) { return dataTree.getACL(n); } /** * remove a cnxn from the datatree * @param cnxn the cnxn to remove from the datatree */ public void removeCnxn(ServerCnxn cnxn) { dataTree.removeCnxn(cnxn); } /** * kill a given session in the datatree * @param sessionId the session id to be killed * @param zxid the zxid of kill session transaction */ public void killSession(long sessionId, long zxid) { dataTree.killSession(sessionId, zxid); } /** * write a text dump of all the ephemerals in the datatree * @param pwriter the output to write to */ public void dumpEphemerals(PrintWriter pwriter) { dataTree.dumpEphemerals(pwriter); } public Map> getEphemerals() { return dataTree.getEphemerals(); } /** * the node count of the datatree * @return the node count of datatree */ public int getNodeCount() { return dataTree.getNodeCount(); } /** * the paths for ephemeral session id * @param sessionId the session id for which paths match to * @return the paths for a session id */ public Set getEphemerals(long sessionId) { return dataTree.getEphemerals(sessionId); } /** * the last processed zxid in the datatree * @param zxid the last processed zxid in the datatree */ public void setlastProcessedZxid(long zxid) { dataTree.lastProcessedZxid = zxid; } /** * the process txn on the data and perform digest comparision. * @param hdr the txnheader for the txn * @param txn the transaction that needs to be processed * @param digest the expected digest. A null value would skip the check * @return the result of processing the transaction on this * datatree/zkdatabase */ public ProcessTxnResult processTxn(TxnHeader hdr, Record txn, TxnDigest digest) { return dataTree.processTxn(hdr, txn, digest); } /** * stat the path * @param path the path for which stat is to be done * @param serverCnxn the servercnxn attached to this request * @return the stat of this node * @throws KeeperException.NoNodeException */ public Stat statNode(String path, ServerCnxn serverCnxn) throws KeeperException.NoNodeException { return dataTree.statNode(path, serverCnxn); } /** * get the datanode for this path * @param path the path to lookup * @return the datanode for getting the path */ public DataNode getNode(String path) { return dataTree.getNode(path); } /** * get data and stat for a path * @param path the path being queried * @param stat the stat for this path * @param watcher the watcher function * @throws KeeperException.NoNodeException */ public byte[] getData(String path, Stat stat, Watcher watcher) throws KeeperException.NoNodeException { return dataTree.getData(path, stat, watcher); } /** * set watches on the datatree * @param relativeZxid the relative zxid that client has seen * @param dataWatches the data watches the client wants to reset * @param existWatches the exists watches the client wants to reset * @param childWatches the child watches the client wants to reset * @param persistentWatches the persistent watches the client wants to reset * @param persistentRecursiveWatches the persistent recursive watches the client wants to reset * @param watcher the watcher function */ public void setWatches(long relativeZxid, List dataWatches, List existWatches, List childWatches, List persistentWatches, List persistentRecursiveWatches, Watcher watcher) { dataTree.setWatches(relativeZxid, dataWatches, existWatches, childWatches, persistentWatches, persistentRecursiveWatches, watcher); } /** * Add a watch * * @param basePath * watch base * @param watcher * the watcher * @param mode * a mode from ZooDefs.AddWatchModes */ public void addWatch(String basePath, Watcher watcher, int mode) { dataTree.addWatch(basePath, watcher, mode); } /** * get acl for a path * @param path the path to query for acl * @param stat the stat for the node * @return the acl list for this path * @throws NoNodeException */ public List getACL(String path, Stat stat) throws NoNodeException { return dataTree.getACL(path, stat); } /** * get children list for this path * @param path the path of the node * @param stat the stat of the node * @param watcher the watcher function for this path * @return the list of children for this path * @throws KeeperException.NoNodeException */ public List getChildren(String path, Stat stat, Watcher watcher) throws KeeperException.NoNodeException { return dataTree.getChildren(path, stat, watcher); } /* * get all sub-children number of this node * */ public int getAllChildrenNumber(String path) throws KeeperException.NoNodeException { return dataTree.getAllChildrenNumber(path); } /** * check if the path is special or not * @param path the input path * @return true if path is special and false if not */ public boolean isSpecialPath(String path) { return dataTree.isSpecialPath(path); } /** * get the acl size of the datatree * @return the acl size of the datatree */ public int getAclSize() { return dataTree.aclCacheSize(); } /** * Truncate the ZKDatabase to the specified zxid * @param zxid the zxid to truncate zk database to * @return true if the truncate is successful and false if not * @throws IOException */ public boolean truncateLog(long zxid) throws IOException { clear(); // truncate the log boolean truncated = snapLog.truncateLog(zxid); if (!truncated) { return false; } loadDataBase(); return true; } /** * deserialize a snapshot from an input archive * @param ia the input archive you want to deserialize from * @throws IOException */ public void deserializeSnapshot(InputArchive ia) throws IOException { clear(); SerializeUtils.deserializeSnapshot(getDataTree(), ia, getSessionWithTimeOuts()); initialized = true; } /** * Deserialize a snapshot that contains FileHeader from an input archive. It is used by * the admin restore command. * * @param ia the input archive to deserialize from * @param is the CheckInputStream to check integrity * * @throws IOException */ public void deserializeSnapshot(final InputArchive ia, final CheckedInputStream is) throws IOException { clear(); // deserialize data tree final DataTree dataTree = getDataTree(); FileSnap.deserialize(dataTree, getSessionWithTimeOuts(), ia); SnapStream.checkSealIntegrity(is, ia); // deserialize digest and check integrity if (dataTree.deserializeZxidDigest(ia, 0)) { SnapStream.checkSealIntegrity(is, ia); } // deserialize lastProcessedZxid and check integrity if (dataTree.deserializeLastProcessedZxid(ia)) { SnapStream.checkSealIntegrity(is, ia); } // compare the digest to find inconsistency if (dataTree.getDigestFromLoadedSnapshot() != null) { dataTree.compareSnapshotDigests(dataTree.lastProcessedZxid); } initialized = true; } /** * serialize the snapshot * @param oa the output archive to which the snapshot needs to be serialized * @throws IOException * @throws InterruptedException */ public void serializeSnapshot(OutputArchive oa) throws IOException, InterruptedException { SerializeUtils.serializeSnapshot(getDataTree(), oa, getSessionWithTimeOuts()); } /** * append to the underlying transaction log * @param si the request to append * @return true if the append was succesfull and false if not */ public boolean append(Request si) throws IOException { if (this.snapLog.append(si)) { txnCount.incrementAndGet(); return true; } return false; } /** * roll the underlying log */ public void rollLog() throws IOException { this.snapLog.rollLog(); resetTxnCount(); } /** * commit to the underlying transaction log * @throws IOException */ public void commit() throws IOException { this.snapLog.commit(); } /** * close this database. free the resources * @throws IOException */ public void close() throws IOException { this.snapLog.close(); } public synchronized void initConfigInZKDatabase(QuorumVerifier qv) { if (qv == null) { return; // only happens during tests } try { if (this.dataTree.getNode(ZooDefs.CONFIG_NODE) == null) { // should only happen during upgrade LOG.warn("configuration znode missing (should only happen during upgrade), creating the node"); this.dataTree.addConfigNode(); } this.dataTree.setData( ZooDefs.CONFIG_NODE, qv.toString().getBytes(UTF_8), -1, qv.getVersion(), Time.currentWallTime()); } catch (NoNodeException e) { System.out.println("configuration node missing - should not happen"); } } /** * Use for unit testing, so we can turn this feature on/off * @param snapshotSizeFactor Set to minus value to turn this off. */ public void setSnapshotSizeFactor(double snapshotSizeFactor) { this.snapshotSizeFactor = snapshotSizeFactor; } /** * Check whether the given watcher exists in datatree * * @param path * node to check watcher existence * @param type * type of watcher * @param watcher * watcher function */ public boolean containsWatcher(String path, WatcherType type, Watcher watcher) { return dataTree.containsWatcher(path, type, watcher); } /** * Remove watch from the datatree * * @param path * node to remove watches from * @param type * type of watcher to remove * @param watcher * watcher function to remove */ public boolean removeWatch(String path, WatcherType type, Watcher watcher) { return dataTree.removeWatch(path, type, watcher); } // visible for testing public DataTree createDataTree() { return new DataTree(); } /** * Reset the number of txn since last rollLog */ public void resetTxnCount() { txnCount.set(0); snapLog.setTotalLogSize(0); } /** * Get the number of txn since last snapshot */ public int getTxnCount() { return txnCount.get(); } /** * Get the size of txn since last snapshot */ public long getTxnSize() { return snapLog.getTotalLogSize(); } public boolean compareDigest(TxnHeader header, Record txn, TxnDigest digest) { return dataTree.compareDigest(header, txn, digest); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000170 15051152474 032660 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperCriticalThread.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperCriticalT0100644 0000000 0000000 00000003635 15051152474 034234 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Represents critical thread. When there is an uncaught exception thrown by the * thread this will exit the system. */ public class ZooKeeperCriticalThread extends ZooKeeperThread { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperCriticalThread.class); private final ZooKeeperServerListener listener; public ZooKeeperCriticalThread(String threadName, ZooKeeperServerListener listener) { super(threadName); this.listener = listener; } /** * This will be used by the uncaught exception handler and make the system * exit. * * @param threadName * - thread name * @param e * - exception object */ @Override protected void handleException(String threadName, Throwable e) { LOG.error("Severe unrecoverable error, from thread : {}", threadName, e); listener.notifyStopping(threadName, ExitCode.UNEXPECTED_ERROR.getValue()); ServerMetrics.getMetrics().UNRECOVERABLE_ERROR_COUNT.add(1); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000164 15051152474 032663 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperSaslServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperSaslServe0100644 0000000 0000000 00000004110 15051152474 034252 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import javax.security.auth.Subject; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.zookeeper.Login; import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZooKeeperSaslServer { public static final String LOGIN_CONTEXT_NAME_KEY = "zookeeper.sasl.serverconfig"; public static final String DEFAULT_LOGIN_CONTEXT_NAME = "Server"; private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslServer.class); private SaslServer saslServer; ZooKeeperSaslServer(final Login login) { saslServer = createSaslServer(login); } private SaslServer createSaslServer(final Login login) { synchronized (login) { Subject subject = login.getSubject(); return SecurityUtils.createSaslServer(subject, "zookeeper", "zk-sasl-md5", login.newCallbackHandler(), LOG); } } public byte[] evaluateResponse(byte[] response) throws SaslException { return saslServer.evaluateResponse(response); } public boolean isComplete() { return saslServer.isComplete(); } public String getAuthorizationID() { return saslServer.getAuthorizationID(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000160 15051152474 032657 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServer.ja0100644 0000000 0000000 00000264645 15051152474 034227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; import javax.security.sasl.SaslException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.Record; import org.apache.zookeeper.Environment; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.Version; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZookeeperBanner; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.proto.AuthPacket; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ConnectResponse; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.GetSASLRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetACLRequest; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.proto.SetSASLResponse; import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.RequestProcessor.RequestProcessorException; import org.apache.zookeeper.server.ServerCnxn.CloseRequestException; import org.apache.zookeeper.server.SessionTracker.Session; import org.apache.zookeeper.server.SessionTracker.SessionExpirer; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.auth.ServerAuthenticationProvider; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; import org.apache.zookeeper.server.util.JvmPauseMonitor; import org.apache.zookeeper.server.util.OSMXBean; import org.apache.zookeeper.server.util.QuotaMetricsUtils; import org.apache.zookeeper.server.util.RequestPathMetricsCollector; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class implements a simple standalone ZooKeeperServer. It sets up the * following chain of RequestProcessors to process requests: * PrepRequestProcessor -> SyncRequestProcessor -> FinalRequestProcessor */ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider { protected static final Logger LOG; private static final RateLogger RATE_LOGGER; public static final String GLOBAL_OUTSTANDING_LIMIT = "zookeeper.globalOutstandingLimit"; public static final String ENABLE_EAGER_ACL_CHECK = "zookeeper.enableEagerACLCheck"; public static final String SKIP_ACL = "zookeeper.skipACL"; public static final String ENFORCE_QUOTA = "zookeeper.enforceQuota"; // When enabled, will check ACL constraints appertained to the requests first, // before sending the requests to the quorum. static boolean enableEagerACLCheck; static final boolean skipACL; public static final boolean enforceQuota; public static final String SASL_SUPER_USER = "zookeeper.superUser"; public static final String ALLOW_SASL_FAILED_CLIENTS = "zookeeper.allowSaslFailedClients"; public static final String ZOOKEEPER_DIGEST_ENABLED = "zookeeper.digest.enabled"; private static boolean digestEnabled; public static final String ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED = "zookeeper.serializeLastProcessedZxid.enabled"; private static boolean serializeLastProcessedZxidEnabled; // Add a enable/disable option for now, we should remove this one when // this feature is confirmed to be stable public static final String CLOSE_SESSION_TXN_ENABLED = "zookeeper.closeSessionTxn.enabled"; private static boolean closeSessionTxnEnabled = true; private volatile CountDownLatch restoreLatch; // exclusive lock for taking snapshot and restore private final Object snapshotAndRestoreLock = new Object(); static { LOG = LoggerFactory.getLogger(ZooKeeperServer.class); RATE_LOGGER = new RateLogger(LOG); ZookeeperBanner.printBanner(LOG); Environment.logEnv("Server environment:", LOG); enableEagerACLCheck = Boolean.getBoolean(ENABLE_EAGER_ACL_CHECK); LOG.info("{} = {}", ENABLE_EAGER_ACL_CHECK, enableEagerACLCheck); skipACL = System.getProperty(SKIP_ACL, "no").equals("yes"); if (skipACL) { LOG.info("{}==\"yes\", ACL checks will be skipped", SKIP_ACL); } enforceQuota = Boolean.parseBoolean(System.getProperty(ENFORCE_QUOTA, "false")); if (enforceQuota) { LOG.info("{} = {}, Quota Enforce enables", ENFORCE_QUOTA, enforceQuota); } digestEnabled = Boolean.parseBoolean(System.getProperty(ZOOKEEPER_DIGEST_ENABLED, "true")); LOG.info("{} = {}", ZOOKEEPER_DIGEST_ENABLED, digestEnabled); closeSessionTxnEnabled = Boolean.parseBoolean( System.getProperty(CLOSE_SESSION_TXN_ENABLED, "true")); LOG.info("{} = {}", CLOSE_SESSION_TXN_ENABLED, closeSessionTxnEnabled); setSerializeLastProcessedZxidEnabled(Boolean.parseBoolean( System.getProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED, "true"))); } // @VisibleForTesting public static boolean isEnableEagerACLCheck() { return enableEagerACLCheck; } // @VisibleForTesting public static void setEnableEagerACLCheck(boolean enabled) { ZooKeeperServer.enableEagerACLCheck = enabled; LOG.info("Update {} to {}", ENABLE_EAGER_ACL_CHECK, enabled); } public static boolean isCloseSessionTxnEnabled() { return closeSessionTxnEnabled; } public static void setCloseSessionTxnEnabled(boolean enabled) { ZooKeeperServer.closeSessionTxnEnabled = enabled; LOG.info("Update {} to {}", CLOSE_SESSION_TXN_ENABLED, ZooKeeperServer.closeSessionTxnEnabled); } protected ZooKeeperServerBean jmxServerBean; protected DataTreeBean jmxDataTreeBean; public static final int DEFAULT_TICK_TIME = 3000; protected int tickTime = DEFAULT_TICK_TIME; public static final int DEFAULT_THROTTLED_OP_WAIT_TIME = 0; // disabled protected static volatile int throttledOpWaitTime = Integer.getInteger("zookeeper.throttled_op_wait_time", DEFAULT_THROTTLED_OP_WAIT_TIME); /** value of -1 indicates unset, use default */ protected int minSessionTimeout = -1; /** value of -1 indicates unset, use default */ protected int maxSessionTimeout = -1; /** Socket listen backlog. Value of -1 indicates unset */ protected int listenBacklog = -1; protected SessionTracker sessionTracker; private FileTxnSnapLog txnLogFactory = null; private ZKDatabase zkDb; private ResponseCache readResponseCache; private ResponseCache getChildrenResponseCache; private final AtomicLong hzxid = new AtomicLong(0); public static final Exception ok = new Exception("No prob"); protected RequestProcessor firstProcessor; protected JvmPauseMonitor jvmPauseMonitor; protected volatile State state = State.INITIAL; private boolean isResponseCachingEnabled = true; /* contains the configuration file content read at startup */ protected String initialConfig; protected boolean reconfigEnabled; private final RequestPathMetricsCollector requestPathMetricsCollector; private static final int DEFAULT_SNAP_COUNT = 100000; private static final int DEFAULT_GLOBAL_OUTSTANDING_LIMIT = 1000; private boolean localSessionEnabled = false; protected enum State { INITIAL, RUNNING, SHUTDOWN, ERROR } /** * This is the secret that we use to generate passwords. For the moment, * it's more of a checksum that's used in reconnection, which carries no * security weight, and is treated internally as if it carries no * security weight. */ private static final long superSecret = 0XB3415C00L; private final AtomicInteger requestsInProcess = new AtomicInteger(0); final Deque outstandingChanges = new ArrayDeque<>(); // this data structure must be accessed under the outstandingChanges lock final Map outstandingChangesForPath = new HashMap<>(); protected ServerCnxnFactory serverCnxnFactory; protected ServerCnxnFactory secureServerCnxnFactory; private final ServerStats serverStats; private final ZooKeeperServerListener listener; private ZooKeeperServerShutdownHandler zkShutdownHandler; private volatile int createSessionTrackerServerId = 1; private static final String FLUSH_DELAY = "zookeeper.flushDelay"; private static volatile long flushDelay; private static final String MAX_WRITE_QUEUE_POLL_SIZE = "zookeeper.maxWriteQueuePollTime"; private static volatile long maxWriteQueuePollTime; private static final String MAX_BATCH_SIZE = "zookeeper.maxBatchSize"; private static volatile int maxBatchSize; /** * Starting size of read and write ByteArrayOutputBuffers. Default is 32 bytes. * Flag not used for small transfers like connectResponses. */ public static final String INT_BUFFER_STARTING_SIZE_BYTES = "zookeeper.intBufferStartingSizeBytes"; public static final int DEFAULT_STARTING_BUFFER_SIZE = 1024; public static final int intBufferStartingSizeBytes; public static final String GET_DATA_RESPONSE_CACHE_SIZE = "zookeeper.maxResponseCacheSize"; public static final String GET_CHILDREN_RESPONSE_CACHE_SIZE = "zookeeper.maxGetChildrenResponseCacheSize"; static { long configuredFlushDelay = Long.getLong(FLUSH_DELAY, 0); setFlushDelay(configuredFlushDelay); setMaxWriteQueuePollTime(Long.getLong(MAX_WRITE_QUEUE_POLL_SIZE, configuredFlushDelay / 3)); setMaxBatchSize(Integer.getInteger(MAX_BATCH_SIZE, 1000)); intBufferStartingSizeBytes = Integer.getInteger(INT_BUFFER_STARTING_SIZE_BYTES, DEFAULT_STARTING_BUFFER_SIZE); if (intBufferStartingSizeBytes < 32) { String msg = "Buffer starting size (" + intBufferStartingSizeBytes + ") must be greater than or equal to 32. " + "Configure with \"-Dzookeeper.intBufferStartingSizeBytes=\" "; LOG.error(msg); throw new IllegalArgumentException(msg); } LOG.info("{} = {}", INT_BUFFER_STARTING_SIZE_BYTES, intBufferStartingSizeBytes); } // Connection throttling private final BlueThrottle connThrottle = new BlueThrottle(); @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "Internally the throttler has a BlockingQueue so " + "once the throttler is created and started, it is thread-safe") private RequestThrottler requestThrottler; public static final String SNAP_COUNT = "zookeeper.snapCount"; /** * This setting sets a limit on the total number of large requests that * can be inflight and is designed to prevent ZooKeeper from accepting * too many large requests such that the JVM runs out of usable heap and * ultimately crashes. * * The limit is enforced by the {@link #checkRequestSizeWhenReceivingMessage(int)} * method which is called by the connection layer ({@link NIOServerCnxn}, * {@link NettyServerCnxn}) before allocating a byte buffer and pulling * data off the TCP socket. The limit is then checked again by the * ZooKeeper server in {@link #processPacket(ServerCnxn, RequestHeader, RequestRecord)} which * also atomically updates {@link #currentLargeRequestBytes}. The request is * then marked as a large request, with the request size stored in the Request * object so that it can later be decremented from {@link #currentLargeRequestBytes}. * * When a request is completed or dropped, the relevant code path calls the * {@link #requestFinished(Request)} method which performs the decrement if * needed. */ private volatile int largeRequestMaxBytes = 100 * 1024 * 1024; /** * The size threshold after which a request is considered a large request * and is checked against the large request byte limit. */ private volatile int largeRequestThreshold = -1; private final AtomicInteger currentLargeRequestBytes = new AtomicInteger(0); private final AuthenticationHelper authHelper = new AuthenticationHelper(); void removeCnxn(ServerCnxn cnxn) { zkDb.removeCnxn(cnxn); } /** * Creates a ZooKeeperServer instance. Nothing is setup, use the setX * methods to prepare the instance (eg datadir, datalogdir, ticktime, * builder, etc...) * */ public ZooKeeperServer() { listener = new ZooKeeperServerListenerImpl(this); serverStats = new ServerStats(this); this.requestPathMetricsCollector = new RequestPathMetricsCollector(); } /** * Keeping this constructor for backward compatibility */ public ZooKeeperServer(FileTxnSnapLog txnLogFactory, int tickTime, int minSessionTimeout, int maxSessionTimeout, int clientPortListenBacklog, ZKDatabase zkDb, String initialConfig) { this(txnLogFactory, tickTime, minSessionTimeout, maxSessionTimeout, clientPortListenBacklog, zkDb, initialConfig, QuorumPeerConfig.isReconfigEnabled()); } /** * * Creates a ZooKeeperServer instance. It sets everything up, but doesn't * actually start listening for clients until run() is invoked. * */ public ZooKeeperServer(FileTxnSnapLog txnLogFactory, int tickTime, int minSessionTimeout, int maxSessionTimeout, int clientPortListenBacklog, ZKDatabase zkDb, String initialConfig, boolean reconfigEnabled) { serverStats = new ServerStats(this); this.txnLogFactory = txnLogFactory; this.txnLogFactory.setServerStats(this.serverStats); this.zkDb = zkDb; this.tickTime = tickTime; setMinSessionTimeout(minSessionTimeout); setMaxSessionTimeout(maxSessionTimeout); this.listenBacklog = clientPortListenBacklog; this.reconfigEnabled = reconfigEnabled; listener = new ZooKeeperServerListenerImpl(this); readResponseCache = new ResponseCache(Integer.getInteger( GET_DATA_RESPONSE_CACHE_SIZE, ResponseCache.DEFAULT_RESPONSE_CACHE_SIZE), "getData"); getChildrenResponseCache = new ResponseCache(Integer.getInteger( GET_CHILDREN_RESPONSE_CACHE_SIZE, ResponseCache.DEFAULT_RESPONSE_CACHE_SIZE), "getChildren"); this.initialConfig = initialConfig; this.requestPathMetricsCollector = new RequestPathMetricsCollector(); this.initLargeRequestThrottlingSettings(); LOG.info( "Created server with" + " tickTime {} ms" + " minSessionTimeout {} ms" + " maxSessionTimeout {} ms" + " clientPortListenBacklog {}" + " dataLogdir {}" + " snapdir {}", tickTime, getMinSessionTimeout(), getMaxSessionTimeout(), getClientPortListenBacklog(), txnLogFactory.getDataLogDir(), txnLogFactory.getSnapDir()); } public String getInitialConfig() { return initialConfig; } /** * Adds JvmPauseMonitor and calls * {@link #ZooKeeperServer(FileTxnSnapLog, int, int, int, int, ZKDatabase, String)} * */ public ZooKeeperServer(JvmPauseMonitor jvmPauseMonitor, FileTxnSnapLog txnLogFactory, int tickTime, int minSessionTimeout, int maxSessionTimeout, int clientPortListenBacklog, ZKDatabase zkDb, String initialConfig) { this(txnLogFactory, tickTime, minSessionTimeout, maxSessionTimeout, clientPortListenBacklog, zkDb, initialConfig, QuorumPeerConfig.isReconfigEnabled()); this.jvmPauseMonitor = jvmPauseMonitor; if (jvmPauseMonitor != null) { LOG.info("Added JvmPauseMonitor to server"); } } /** * creates a zookeeperserver instance. * @param txnLogFactory the file transaction snapshot logging class * @param tickTime the ticktime for the server * @throws IOException */ public ZooKeeperServer(FileTxnSnapLog txnLogFactory, int tickTime, String initialConfig) { this(txnLogFactory, tickTime, -1, -1, -1, new ZKDatabase(txnLogFactory), initialConfig, QuorumPeerConfig.isReconfigEnabled()); } public ServerStats serverStats() { return serverStats; } public RequestPathMetricsCollector getRequestPathMetricsCollector() { return requestPathMetricsCollector; } public BlueThrottle connThrottle() { return connThrottle; } public void dumpConf(PrintWriter pwriter) { pwriter.print("clientPort="); pwriter.println(getClientPort()); pwriter.print("secureClientPort="); pwriter.println(getSecureClientPort()); pwriter.print("dataDir="); pwriter.println(zkDb.snapLog.getSnapDir().getAbsolutePath()); pwriter.print("dataDirSize="); pwriter.println(getDataDirSize()); pwriter.print("dataLogDir="); pwriter.println(zkDb.snapLog.getDataLogDir().getAbsolutePath()); pwriter.print("dataLogSize="); pwriter.println(getLogDirSize()); pwriter.print("tickTime="); pwriter.println(getTickTime()); pwriter.print("maxClientCnxns="); pwriter.println(getMaxClientCnxnsPerHost()); pwriter.print("minSessionTimeout="); pwriter.println(getMinSessionTimeout()); pwriter.print("maxSessionTimeout="); pwriter.println(getMaxSessionTimeout()); pwriter.print("clientPortListenBacklog="); pwriter.println(getClientPortListenBacklog()); pwriter.print("serverId="); pwriter.println(getServerId()); } public ZooKeeperServerConf getConf() { return new ZooKeeperServerConf( getClientPort(), zkDb.snapLog.getSnapDir().getAbsolutePath(), zkDb.snapLog.getDataLogDir().getAbsolutePath(), getTickTime(), getMaxClientCnxnsPerHost(), getMinSessionTimeout(), getMaxSessionTimeout(), getServerId(), getClientPortListenBacklog()); } /** * This constructor is for backward compatibility with the existing unit * test code. * It defaults to FileLogProvider persistence provider. */ public ZooKeeperServer(File snapDir, File logDir, int tickTime) throws IOException { this(new FileTxnSnapLog(snapDir, logDir), tickTime, ""); } /** * Default constructor, relies on the config for its argument values * * @throws IOException */ public ZooKeeperServer(FileTxnSnapLog txnLogFactory) throws IOException { this(txnLogFactory, DEFAULT_TICK_TIME, -1, -1, -1, new ZKDatabase(txnLogFactory), "", QuorumPeerConfig.isReconfigEnabled()); } /** * get the zookeeper database for this server * @return the zookeeper database for this server */ public ZKDatabase getZKDatabase() { return this.zkDb; } /** * set the zkdatabase for this zookeeper server * @param zkDb */ public void setZKDatabase(ZKDatabase zkDb) { this.zkDb = zkDb; } /** * Restore sessions and data */ public void loadData() throws IOException, InterruptedException { /* * When a new leader starts executing Leader#lead, it * invokes this method. The database, however, has been * initialized before running leader election so that * the server could pick its zxid for its initial vote. * It does it by invoking QuorumPeer#getLastLoggedZxid. * Consequently, we don't need to initialize it once more * and avoid the penalty of loading it a second time. Not * reloading it is particularly important for applications * that host a large database. * * The following if block checks whether the database has * been initialized or not. Note that this method is * invoked by at least one other method: * ZooKeeperServer#startdata. * * See ZOOKEEPER-1642 for more detail. */ if (zkDb.isInitialized()) { setZxid(zkDb.getDataTreeLastProcessedZxid()); } else { setZxid(zkDb.loadDataBase()); } // Clean up dead sessions zkDb.getSessions().stream() .filter(session -> zkDb.getSessionWithTimeOuts().get(session) == null) .forEach(session -> killSession(session, zkDb.getDataTreeLastProcessedZxid())); // Make a clean snapshot takeSnapshot(); } public File takeSnapshot() throws IOException { return takeSnapshot(false); } public File takeSnapshot(boolean syncSnap) throws IOException { return takeSnapshot(syncSnap, true); } /** * Takes a snapshot on the server. * * @param syncSnap syncSnap sync the snapshot immediately after write * @param isSevere if true system exist, otherwise throw IOException * @return file snapshot file object * @throws IOException */ public File takeSnapshot(boolean syncSnap, boolean isSevere) throws IOException { long start = Time.currentElapsedTime(); File snapFile = null; try { synchronized (snapshotAndRestoreLock) { snapFile = txnLogFactory.save(zkDb.getDataTree(), zkDb.getSessionWithTimeOuts(), syncSnap); } } catch (IOException e) { if (isSevere) { LOG.error("Severe unrecoverable error, exiting", e); // This is a severe error that we cannot recover from, // so we need to exit ServiceUtils.requestSystemExit(ExitCode.TXNLOG_ERROR_TAKING_SNAPSHOT.getValue()); } else { throw e; } } long elapsed = Time.currentElapsedTime() - start; LOG.info("Snapshot taken in {} ms", elapsed); ServerMetrics.getMetrics().SNAPSHOT_TIME.add(elapsed); return snapFile; } /** * Restores database from a snapshot. It is used by the restore admin server command. * * @param inputStream input stream of snapshot * @Return last processed zxid * @throws IOException */ public long restoreFromSnapshot(final InputStream inputStream) throws IOException { if (inputStream == null) { throw new IllegalArgumentException("InputStream can not be null when restoring from snapshot"); } long start = Time.currentElapsedTime(); LOG.info("Before restore database. lastProcessedZxid={}, nodeCount={},sessionCount={}", getZKDatabase().getDataTreeLastProcessedZxid(), getZKDatabase().dataTree.getNodeCount(), getZKDatabase().getSessionCount()); // restore to a new zkDatabase final ZKDatabase newZKDatabase = new ZKDatabase(this.txnLogFactory); final CheckedInputStream cis = new CheckedInputStream(new BufferedInputStream(inputStream), new Adler32()); final InputArchive ia = BinaryInputArchive.getArchive(cis); newZKDatabase.deserializeSnapshot(ia, cis); LOG.info("Restored to a new database. lastProcessedZxid={}, nodeCount={}, sessionCount={}", newZKDatabase.getDataTreeLastProcessedZxid(), newZKDatabase.dataTree.getNodeCount(), newZKDatabase.getSessionCount()); // create a CountDownLatch restoreLatch = new CountDownLatch(1); try { synchronized (snapshotAndRestoreLock) { // set to the new zkDatabase setZKDatabase(newZKDatabase); } // re-create SessionTrack createSessionTracker(); } finally { // unblock request submission restoreLatch.countDown(); restoreLatch = null; } LOG.info("After restore database. lastProcessedZxid={}, nodeCount={}, sessionCount={}", getZKDatabase().getDataTreeLastProcessedZxid(), getZKDatabase().dataTree.getNodeCount(), getZKDatabase().getSessionCount()); long elapsed = Time.currentElapsedTime() - start; LOG.info("Restore taken in {} ms", elapsed); ServerMetrics.getMetrics().RESTORE_TIME.add(elapsed); return getLastProcessedZxid(); } public boolean shouldForceWriteInitialSnapshotAfterLeaderElection() { return txnLogFactory.shouldForceWriteInitialSnapshotAfterLeaderElection(); } @Override public long getDataDirSize() { if (zkDb == null) { return 0L; } File path = zkDb.snapLog.getSnapDir(); return getDirSize(path); } @Override public long getLogDirSize() { if (zkDb == null) { return 0L; } File path = zkDb.snapLog.getDataLogDir(); return getDirSize(path); } private long getDirSize(File file) { long size = 0L; if (file.isDirectory()) { File[] files = file.listFiles(); if (files != null) { for (File f : files) { size += getDirSize(f); } } } else { size = file.length(); } return size; } public long getZxid() { return hzxid.get(); } public SessionTracker getSessionTracker() { return sessionTracker; } long getNextZxid() { return hzxid.incrementAndGet(); } public void setZxid(long zxid) { hzxid.set(zxid); } private void close(long sessionId) { Request si = new Request(null, sessionId, 0, OpCode.closeSession, null, null); submitRequest(si); } public void closeSession(long sessionId) { LOG.info("Closing session 0x{}", Long.toHexString(sessionId)); // we do not want to wait for a session close. send it as soon as we // detect it! close(sessionId); } protected void killSession(long sessionId, long zxid) { zkDb.killSession(sessionId, zxid); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "ZooKeeperServer --- killSession: 0x" + Long.toHexString(sessionId)); } if (sessionTracker != null) { sessionTracker.removeSession(sessionId); } } public void expire(Session session) { long sessionId = session.getSessionId(); LOG.info( "Expiring session 0x{}, timeout of {}ms exceeded", Long.toHexString(sessionId), session.getTimeout()); close(sessionId); } public void expire(long sessionId) { LOG.info("forcibly expiring session 0x{}", Long.toHexString(sessionId)); close(sessionId); } public static class MissingSessionException extends IOException { private static final long serialVersionUID = 7467414635467261007L; public MissingSessionException(String msg) { super(msg); } } void touch(ServerCnxn cnxn) throws MissingSessionException { if (cnxn == null) { return; } long id = cnxn.getSessionId(); int to = cnxn.getSessionTimeout(); if (!sessionTracker.touchSession(id, to)) { throw new MissingSessionException("No session with sessionid 0x" + Long.toHexString(id) + " exists, probably expired and removed"); } } protected void registerJMX() { // register with JMX try { jmxServerBean = new ZooKeeperServerBean(this); MBeanRegistry.getInstance().register(jmxServerBean, null); try { jmxDataTreeBean = new DataTreeBean(zkDb.getDataTree()); MBeanRegistry.getInstance().register(jmxDataTreeBean, jmxServerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxDataTreeBean = null; } } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxServerBean = null; } } public void startdata() throws IOException, InterruptedException { //check to see if zkDb is not null if (zkDb == null) { zkDb = new ZKDatabase(this.txnLogFactory); } if (!zkDb.isInitialized()) { loadData(); } } public synchronized void startup() { if (sessionTracker == null) { createSessionTracker(); } startSessionTracker(); setupRequestProcessors(); startRequestThrottler(); registerJMX(); startJvmPauseMonitor(); registerMetrics(); setState(State.RUNNING); requestPathMetricsCollector.start(); localSessionEnabled = sessionTracker.isLocalSessionsEnabled(); notifyAll(); } protected void startJvmPauseMonitor() { if (this.jvmPauseMonitor != null) { this.jvmPauseMonitor.serviceStart(); } } protected void startRequestThrottler() { requestThrottler = createRequestThrottler(); requestThrottler.start(); } protected RequestThrottler createRequestThrottler() { return new RequestThrottler(this); } protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); RequestProcessor syncProcessor = new SyncRequestProcessor(this, finalProcessor); ((SyncRequestProcessor) syncProcessor).start(); firstProcessor = new PrepRequestProcessor(this, syncProcessor); ((PrepRequestProcessor) firstProcessor).start(); } public ZooKeeperServerListener getZooKeeperServerListener() { return listener; } /** * Change the server ID used by {@link #createSessionTracker()}. Must be called prior to * {@link #startup()} being called * * @param newId ID to use */ public void setCreateSessionTrackerServerId(int newId) { createSessionTrackerServerId = newId; } protected void createSessionTracker() { sessionTracker = new SessionTrackerImpl(this, zkDb.getSessionWithTimeOuts(), tickTime, createSessionTrackerServerId, getZooKeeperServerListener()); } protected void startSessionTracker() { ((SessionTrackerImpl) sessionTracker).start(); } /** * Sets the state of ZooKeeper server. After changing the state, it notifies * the server state change to a registered shutdown handler, if any. *

* The following are the server state transitions: *

  • During startup the server will be in the INITIAL state.
  • *
  • After successfully starting, the server sets the state to RUNNING. *
  • *
  • The server transitions to the ERROR state if it hits an internal * error. {@link ZooKeeperServerListenerImpl} notifies any critical resource * error events, e.g., SyncRequestProcessor not being able to write a txn to * disk.
  • *
  • During shutdown the server sets the state to SHUTDOWN, which * corresponds to the server not running.
* *
  • During maintenance (e.g. restore) the server sets the state to MAINTENANCE *
  • * * @param state new server state. */ protected void setState(State state) { this.state = state; // Notify server state changes to the registered shutdown handler, if any. if (zkShutdownHandler != null) { zkShutdownHandler.handle(state); } else { LOG.debug( "ZKShutdownHandler is not registered, so ZooKeeper server" + " won't take any action on ERROR or SHUTDOWN server state changes"); } } /** * This can be used while shutting down the server to see whether the server * is already shutdown or not. * * @return true if the server is running or server hits an error, false * otherwise. */ private boolean canShutdown() { return state == State.RUNNING || state == State.ERROR; } /** * @return true if the server is running, false otherwise. */ public boolean isRunning() { return state == State.RUNNING; } public final void shutdown() { shutdown(false); } /** * Shut down the server instance * @param fullyShutDown true when no other server will use the same database to replace this one */ public final synchronized void shutdown(boolean fullyShutDown) { if (canShutdown()) { LOG.info("Shutting down"); shutdownComponents(); if (zkDb != null && !fullyShutDown) { // There is no need to clear the database if we are going to reuse it: // * When a new quorum is established we can still apply the diff // on top of the same zkDb data // * If we fetch a new snapshot from leader, the zkDb will be // cleared anyway before loading the snapshot try { // This will fast-forward the database to the last recorded transaction zkDb.fastForwardDataBase(); } catch (IOException e) { LOG.error("Error updating DB", e); fullyShutDown = true; } } setState(State.SHUTDOWN); } else { LOG.debug("ZooKeeper server is not running, so not proceeding to shutdown!"); } if (zkDb != null && fullyShutDown) { zkDb.clear(); } } /** * @implNote * Shuts down components owned by this class; * remember to call super.shutdownComponents() when overriding! */ protected void shutdownComponents() { // unregister all metrics that are keeping a strong reference to this object // subclasses will do their specific clean up unregisterMetrics(); if (requestThrottler != null) { requestThrottler.shutdown(); } // Since sessionTracker and syncThreads poll we just have to set running to false, // and they will detect it during the poll interval. if (sessionTracker != null) { sessionTracker.shutdown(); } if (firstProcessor != null) { firstProcessor.shutdown(); } if (jvmPauseMonitor != null) { jvmPauseMonitor.serviceStop(); } requestPathMetricsCollector.shutdown(); unregisterJMX(); } protected void unregisterJMX() { // unregister from JMX try { if (jmxDataTreeBean != null) { MBeanRegistry.getInstance().unregister(jmxDataTreeBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } try { if (jmxServerBean != null) { MBeanRegistry.getInstance().unregister(jmxServerBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxServerBean = null; jmxDataTreeBean = null; } public void incInProcess() { requestsInProcess.incrementAndGet(); } public void decInProcess() { requestsInProcess.decrementAndGet(); if (requestThrottler != null) { requestThrottler.throttleWake(); } } public int getInProcess() { return requestsInProcess.get(); } public int getInflight() { return requestThrottleInflight(); } private int requestThrottleInflight() { if (requestThrottler != null) { return requestThrottler.getInflight(); } return 0; } static class PrecalculatedDigest { final long nodeDigest; final long treeDigest; PrecalculatedDigest(long nodeDigest, long treeDigest) { this.nodeDigest = nodeDigest; this.treeDigest = treeDigest; } } /** * This structure is used to facilitate information sharing between PrepRP * and FinalRP. */ static class ChangeRecord { PrecalculatedDigest precalculatedDigest; byte[] data; ChangeRecord(long zxid, String path, StatPersisted stat, int childCount, List acl) { this.zxid = zxid; this.path = path; this.stat = stat; this.childCount = childCount; this.acl = acl; } long zxid; String path; StatPersisted stat; /* Make sure to create a new object when changing */ int childCount; List acl; /* Make sure to create a new object when changing */ ChangeRecord duplicate(long zxid) { StatPersisted stat = new StatPersisted(); if (this.stat != null) { DataTree.copyStatPersisted(this.stat, stat); } ChangeRecord changeRecord = new ChangeRecord(zxid, path, stat, childCount, acl == null ? new ArrayList<>() : new ArrayList<>(acl)); changeRecord.precalculatedDigest = precalculatedDigest; changeRecord.data = data; return changeRecord; } } byte[] generatePasswd(long id) { Random r = new Random(id ^ superSecret); byte[] p = new byte[16]; r.nextBytes(p); return p; } protected boolean checkPasswd(long sessionId, byte[] passwd) { return sessionId != 0 && Arrays.equals(passwd, generatePasswd(sessionId)); } long createSession(ServerCnxn cnxn, byte[] passwd, int timeout) { if (passwd == null) { // Possible since it's just deserialized from a packet on the wire. passwd = new byte[0]; } long sessionId = sessionTracker.createSession(timeout); Random r = new Random(sessionId ^ superSecret); r.nextBytes(passwd); CreateSessionTxn txn = new CreateSessionTxn(timeout); cnxn.setSessionId(sessionId); Request si = new Request(cnxn, sessionId, 0, OpCode.createSession, RequestRecord.fromRecord(txn), null); submitRequest(si); return sessionId; } /** * set the owner of this session as owner * @param id the session id * @param owner the owner of the session * @throws SessionExpiredException */ public void setOwner(long id, Object owner) throws SessionExpiredException { sessionTracker.setOwner(id, owner); } protected void revalidateSession(ServerCnxn cnxn, long sessionId, int sessionTimeout) throws IOException { boolean rc = sessionTracker.touchSession(sessionId, sessionTimeout); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "Session 0x" + Long.toHexString(sessionId) + " is valid: " + rc); } finishSessionInit(cnxn, rc); } public void reopenSession(ServerCnxn cnxn, long sessionId, byte[] passwd, int sessionTimeout) throws IOException { if (checkPasswd(sessionId, passwd)) { revalidateSession(cnxn, sessionId, sessionTimeout); } else { LOG.warn( "Incorrect password from {} for session 0x{}", cnxn.getRemoteSocketAddress(), Long.toHexString(sessionId)); finishSessionInit(cnxn, false); } } public void finishSessionInit(ServerCnxn cnxn, boolean valid) { // register with JMX try { if (valid) { if (serverCnxnFactory != null && serverCnxnFactory.cnxns.contains(cnxn)) { serverCnxnFactory.registerConnection(cnxn); } else if (secureServerCnxnFactory != null && secureServerCnxnFactory.cnxns.contains(cnxn)) { secureServerCnxnFactory.registerConnection(cnxn); } } } catch (Exception e) { LOG.warn("Failed to register with JMX", e); } try { ConnectResponse rsp = new ConnectResponse( 0, valid ? cnxn.getSessionTimeout() : 0, valid ? cnxn.getSessionId() : 0, // send 0 if session is no // longer valid valid ? generatePasswd(cnxn.getSessionId()) : new byte[16], this instanceof ReadOnlyZooKeeperServer); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive bos = BinaryOutputArchive.getArchive(baos); bos.writeInt(-1, "len"); cnxn.protocolManager.serializeConnectResponse(rsp, bos); baos.close(); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); bb.putInt(bb.remaining() - 4).rewind(); cnxn.sendBuffer(bb); if (valid) { LOG.debug( "Established session 0x{} with negotiated timeout {} for client {}", Long.toHexString(cnxn.getSessionId()), cnxn.getSessionTimeout(), cnxn.getRemoteSocketAddress()); cnxn.enableRecv(); } else { LOG.info( "Invalid session 0x{} for client {}, probably expired", Long.toHexString(cnxn.getSessionId()), cnxn.getRemoteSocketAddress()); cnxn.sendBuffer(ServerCnxnFactory.closeConn); } } catch (Exception e) { LOG.warn("Exception while establishing session, closing", e); cnxn.close(ServerCnxn.DisconnectReason.IO_EXCEPTION_IN_SESSION_INIT); } } public void closeSession(ServerCnxn cnxn, RequestHeader requestHeader) { closeSession(cnxn.getSessionId()); } public long getServerId() { return 0; } /** * If the underlying Zookeeper server support local session, this method * will set a isLocalSession to true if a request is associated with * a local session. * * @param si */ protected void setLocalSessionFlag(Request si) { } public void submitRequest(Request si) { if (restoreLatch != null) { try { LOG.info("Blocking request submission while restore is in progress"); restoreLatch.await(); } catch (final InterruptedException e) { LOG.warn("Unexpected interruption", e); } } enqueueRequest(si); } public void enqueueRequest(Request si) { if (requestThrottler == null) { synchronized (this) { try { // Since all requests are passed to the request // processor it should wait for setting up the request // processor chain. The state will be updated to RUNNING // after the setup. while (state == State.INITIAL) { wait(1000); } } catch (InterruptedException e) { LOG.warn("Unexpected interruption", e); } if (requestThrottler == null) { throw new RuntimeException("Not started"); } } } requestThrottler.submitRequest(si); } public void submitRequestNow(Request si) { if (firstProcessor == null) { synchronized (this) { try { // Since all requests are passed to the request // processor it should wait for setting up the request // processor chain. The state will be updated to RUNNING // after the setup. while (state == State.INITIAL) { wait(1000); } } catch (InterruptedException e) { LOG.warn("Unexpected interruption", e); } if (firstProcessor == null || state != State.RUNNING) { throw new RuntimeException("Not started"); } } } try { touch(si.cnxn); boolean validpacket = Request.isValid(si.type); if (validpacket) { setLocalSessionFlag(si); firstProcessor.processRequest(si); if (si.cnxn != null) { incInProcess(); } } else { LOG.warn("Received packet at server of unknown type {}", si.type); // Update request accounting/throttling limits requestFinished(si); new UnimplementedRequestProcessor().processRequest(si); } } catch (MissingSessionException e) { LOG.debug("Dropping request.", e); // Update request accounting/throttling limits requestFinished(si); } catch (RequestProcessorException e) { LOG.error("Unable to process request", e); // Update request accounting/throttling limits requestFinished(si); } } public static int getSnapCount() { int snapCount = Integer.getInteger(SNAP_COUNT, DEFAULT_SNAP_COUNT); // snapCount must be 2 or more. See org.apache.zookeeper.server.SyncRequestProcessor if (snapCount < 2) { LOG.warn("SnapCount should be 2 or more. Now, snapCount is reset to 2"); snapCount = 2; } return snapCount; } public int getGlobalOutstandingLimit() { return Integer.getInteger(GLOBAL_OUTSTANDING_LIMIT, DEFAULT_GLOBAL_OUTSTANDING_LIMIT); } public static long getSnapSizeInBytes() { long size = Long.getLong("zookeeper.snapSizeLimitInKb", 4194304L); // 4GB by default if (size <= 0) { LOG.info("zookeeper.snapSizeLimitInKb set to a non-positive value {}; disabling feature", size); } return size * 1024; // Convert to bytes } public void setServerCnxnFactory(ServerCnxnFactory factory) { serverCnxnFactory = factory; } public ServerCnxnFactory getServerCnxnFactory() { return serverCnxnFactory; } public ServerCnxnFactory getSecureServerCnxnFactory() { return secureServerCnxnFactory; } public void setSecureServerCnxnFactory(ServerCnxnFactory factory) { secureServerCnxnFactory = factory; } /** * return the last processed id from the * datatree */ public long getLastProcessedZxid() { return zkDb.getDataTreeLastProcessedZxid(); } /** * return the outstanding requests * in the queue, which haven't been * processed yet */ public long getOutstandingRequests() { return getInProcess(); } /** * return the total number of client connections that are alive * to this server */ public int getNumAliveConnections() { int numAliveConnections = 0; if (serverCnxnFactory != null) { numAliveConnections += serverCnxnFactory.getNumAliveConnections(); } if (secureServerCnxnFactory != null) { numAliveConnections += secureServerCnxnFactory.getNumAliveConnections(); } return numAliveConnections; } /** * truncate the log to get in sync with others * if in a quorum * @param zxid the zxid that it needs to get in sync * with others * @throws IOException */ public void truncateLog(long zxid) throws IOException { this.zkDb.truncateLog(zxid); } public int getTickTime() { return tickTime; } public void setTickTime(int tickTime) { LOG.info("tickTime set to {} ms", tickTime); this.tickTime = tickTime; } public static int getThrottledOpWaitTime() { return throttledOpWaitTime; } public static void setThrottledOpWaitTime(int time) { LOG.info("throttledOpWaitTime set to {} ms", time); throttledOpWaitTime = time; } public int getMinSessionTimeout() { return minSessionTimeout; } public void setMinSessionTimeout(int min) { this.minSessionTimeout = min == -1 ? tickTime * 2 : min; LOG.info("minSessionTimeout set to {} ms", this.minSessionTimeout); } public int getMaxSessionTimeout() { return maxSessionTimeout; } public void setMaxSessionTimeout(int max) { this.maxSessionTimeout = max == -1 ? tickTime * 20 : max; LOG.info("maxSessionTimeout set to {} ms", this.maxSessionTimeout); } public int getClientPortListenBacklog() { return listenBacklog; } public void setClientPortListenBacklog(int backlog) { this.listenBacklog = backlog; LOG.info("clientPortListenBacklog set to {}", backlog); } public int getClientPort() { return serverCnxnFactory != null ? serverCnxnFactory.getLocalPort() : -1; } public int getSecureClientPort() { return secureServerCnxnFactory != null ? secureServerCnxnFactory.getLocalPort() : -1; } /** Maximum number of connections allowed from particular host (ip) */ public int getMaxClientCnxnsPerHost() { if (serverCnxnFactory != null) { return serverCnxnFactory.getMaxClientCnxnsPerHost(); } if (secureServerCnxnFactory != null) { return secureServerCnxnFactory.getMaxClientCnxnsPerHost(); } return -1; } public void setTxnLogFactory(FileTxnSnapLog txnLog) { this.txnLogFactory = txnLog; } public FileTxnSnapLog getTxnLogFactory() { return this.txnLogFactory; } /** * Returns the elapsed sync of time of transaction log in milliseconds. */ public long getTxnLogElapsedSyncTime() { return txnLogFactory.getTxnLogElapsedSyncTime(); } public String getState() { return "standalone"; } public void dumpEphemerals(PrintWriter pwriter) { zkDb.dumpEphemerals(pwriter); } public Map> getEphemerals() { return zkDb.getEphemerals(); } public double getConnectionDropChance() { return connThrottle.getDropChance(); } @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "the value won't change after startup") public void processConnectRequest(ServerCnxn cnxn, ConnectRequest request) throws IOException, ClientCnxnLimitException { LOG.debug( "Session establishment request from client {} client's lastZxid is 0x{}", cnxn.getRemoteSocketAddress(), Long.toHexString(request.getLastZxidSeen())); long sessionId = request.getSessionId(); int tokensNeeded = 1; if (connThrottle.isConnectionWeightEnabled()) { if (sessionId == 0) { if (localSessionEnabled) { tokensNeeded = connThrottle.getRequiredTokensForLocal(); } else { tokensNeeded = connThrottle.getRequiredTokensForGlobal(); } } else { tokensNeeded = connThrottle.getRequiredTokensForRenew(); } } if (!connThrottle.checkLimit(tokensNeeded)) { throw new ClientCnxnLimitException(); } ServerMetrics.getMetrics().CONNECTION_TOKEN_DEFICIT.add(connThrottle.getDeficit()); ServerMetrics.getMetrics().CONNECTION_REQUEST_COUNT.add(1); if (!cnxn.protocolManager.isReadonlyAvailable()) { LOG.warn( "Connection request from old client {}; will be dropped if server is in r-o mode", cnxn.getRemoteSocketAddress()); } if (!request.getReadOnly() && this instanceof ReadOnlyZooKeeperServer) { String msg = "Refusing session request for not-read-only client " + cnxn.getRemoteSocketAddress(); LOG.info(msg); throw new CloseRequestException(msg, ServerCnxn.DisconnectReason.NOT_READ_ONLY_CLIENT); } if (request.getLastZxidSeen() > zkDb.dataTree.lastProcessedZxid) { String msg = "Refusing session(0x" + Long.toHexString(sessionId) + ") request for client " + cnxn.getRemoteSocketAddress() + " as it has seen zxid 0x" + Long.toHexString(request.getLastZxidSeen()) + " our last zxid is 0x" + Long.toHexString(getZKDatabase().getDataTreeLastProcessedZxid()) + " client must try another server"; LOG.info(msg); throw new CloseRequestException(msg, ServerCnxn.DisconnectReason.CLIENT_ZXID_AHEAD); } int sessionTimeout = request.getTimeOut(); byte[] passwd = request.getPasswd(); int minSessionTimeout = getMinSessionTimeout(); if (sessionTimeout < minSessionTimeout) { sessionTimeout = minSessionTimeout; } int maxSessionTimeout = getMaxSessionTimeout(); if (sessionTimeout > maxSessionTimeout) { sessionTimeout = maxSessionTimeout; } cnxn.setSessionTimeout(sessionTimeout); // We don't want to receive any packets until we are sure that the // session is setup cnxn.disableRecv(); if (sessionId == 0) { long id = createSession(cnxn, passwd, sessionTimeout); LOG.debug( "Client attempting to establish new session: session = 0x{}, zxid = 0x{}, timeout = {}, address = {}", Long.toHexString(id), Long.toHexString(request.getLastZxidSeen()), request.getTimeOut(), cnxn.getRemoteSocketAddress()); } else { validateSession(cnxn, sessionId); LOG.debug( "Client attempting to renew session: session = 0x{}, zxid = 0x{}, timeout = {}, address = {}", Long.toHexString(sessionId), Long.toHexString(request.getLastZxidSeen()), request.getTimeOut(), cnxn.getRemoteSocketAddress()); if (serverCnxnFactory != null) { serverCnxnFactory.closeSession(sessionId, ServerCnxn.DisconnectReason.CLIENT_RECONNECT); } if (secureServerCnxnFactory != null) { secureServerCnxnFactory.closeSession(sessionId, ServerCnxn.DisconnectReason.CLIENT_RECONNECT); } cnxn.setSessionId(sessionId); reopenSession(cnxn, sessionId, passwd, sessionTimeout); ServerMetrics.getMetrics().CONNECTION_REVALIDATE_COUNT.add(1); } } /** * Validate if a particular session can be reestablished. * * @param cnxn * @param sessionId */ protected void validateSession(ServerCnxn cnxn, long sessionId) throws IOException { // do nothing } public boolean shouldThrottle(long outStandingCount) { int globalOutstandingLimit = getGlobalOutstandingLimit(); if (globalOutstandingLimit < getInflight() || globalOutstandingLimit < getInProcess()) { return outStandingCount > 0; } return false; } long getFlushDelay() { return flushDelay; } static void setFlushDelay(long delay) { LOG.info("{} = {} ms", FLUSH_DELAY, delay); flushDelay = delay; } long getMaxWriteQueuePollTime() { return maxWriteQueuePollTime; } static void setMaxWriteQueuePollTime(long maxTime) { LOG.info("{} = {} ms", MAX_WRITE_QUEUE_POLL_SIZE, maxTime); maxWriteQueuePollTime = maxTime; } int getMaxBatchSize() { return maxBatchSize; } static void setMaxBatchSize(int size) { LOG.info("{}={}", MAX_BATCH_SIZE, size); maxBatchSize = size; } private void initLargeRequestThrottlingSettings() { setLargeRequestMaxBytes(Integer.getInteger("zookeeper.largeRequestMaxBytes", largeRequestMaxBytes)); setLargeRequestThreshold(Integer.getInteger("zookeeper.largeRequestThreshold", -1)); } public int getLargeRequestMaxBytes() { return largeRequestMaxBytes; } public void setLargeRequestMaxBytes(int bytes) { if (bytes <= 0) { LOG.warn("Invalid max bytes for all large requests {}. It should be a positive number.", bytes); LOG.warn("Will not change the setting. The max bytes stay at {}", largeRequestMaxBytes); } else { largeRequestMaxBytes = bytes; LOG.info("The max bytes for all large requests are set to {}", largeRequestMaxBytes); } } public int getLargeRequestThreshold() { return largeRequestThreshold; } public void setLargeRequestThreshold(int threshold) { if (threshold == 0 || threshold < -1) { LOG.warn("Invalid large request threshold {}. It should be -1 or positive. Setting to -1 ", threshold); largeRequestThreshold = -1; } else { largeRequestThreshold = threshold; LOG.info("The large request threshold is set to {}", largeRequestThreshold); } } public int getLargeRequestBytes() { return currentLargeRequestBytes.get(); } private boolean isLargeRequest(int length) { // The large request limit is disabled when threshold is -1 if (largeRequestThreshold == -1) { return false; } return length > largeRequestThreshold; } public boolean checkRequestSizeWhenReceivingMessage(int length) throws IOException { if (!isLargeRequest(length)) { return true; } if (currentLargeRequestBytes.get() + length <= largeRequestMaxBytes) { return true; } else { ServerMetrics.getMetrics().LARGE_REQUESTS_REJECTED.add(1); throw new IOException("Rejecting large request"); } } private boolean checkRequestSizeWhenMessageReceived(int length) throws IOException { if (!isLargeRequest(length)) { return true; } int bytes = currentLargeRequestBytes.addAndGet(length); if (bytes > largeRequestMaxBytes) { currentLargeRequestBytes.addAndGet(-length); ServerMetrics.getMetrics().LARGE_REQUESTS_REJECTED.add(1); throw new IOException("Rejecting large request"); } return true; } public void requestFinished(Request request) { int largeRequestLength = request.getLargeRequestSize(); if (largeRequestLength != -1) { currentLargeRequestBytes.addAndGet(-largeRequestLength); } } public void processPacket(ServerCnxn cnxn, RequestHeader h, RequestRecord request) throws IOException { // Need to increase the outstanding request count first, otherwise // there might be a race condition that it enabled recv after // processing request and then disabled when check throttling. // // Be aware that we're actually checking the global outstanding // request before this request. // // It's fine if the IOException thrown before we decrease the count // in cnxn, since it will close the cnxn anyway. cnxn.incrOutstandingAndCheckThrottle(h); if (h.getType() == OpCode.auth) { LOG.info("got auth packet {}", cnxn.getRemoteSocketAddress()); AuthPacket authPacket = request.readRecord(AuthPacket::new); String scheme = authPacket.getScheme(); ServerAuthenticationProvider ap = ProviderRegistry.getServerProvider(scheme); Code authReturn = KeeperException.Code.AUTHFAILED; if (ap != null) { try { // handleAuthentication may close the connection, to allow the client to choose // a different server to connect to. authReturn = ap.handleAuthentication( new ServerAuthenticationProvider.ServerObjs(this, cnxn), authPacket.getAuth()); } catch (RuntimeException e) { LOG.warn("Caught runtime exception from AuthenticationProvider: {}", scheme, e); authReturn = KeeperException.Code.AUTHFAILED; } } if (authReturn == KeeperException.Code.OK) { LOG.info("Session 0x{}: auth success for scheme {} and address {}", Long.toHexString(cnxn.getSessionId()), scheme, cnxn.getRemoteSocketAddress()); ReplyHeader rh = new ReplyHeader(h.getXid(), 0, KeeperException.Code.OK.intValue()); cnxn.sendResponse(rh, null, null); } else { if (ap == null) { LOG.warn( "No authentication provider for scheme: {} has {}", scheme, ProviderRegistry.listProviders()); } else { LOG.warn("Authentication failed for scheme: {}", scheme); } // send a response... ReplyHeader rh = new ReplyHeader(h.getXid(), 0, KeeperException.Code.AUTHFAILED.intValue()); cnxn.sendResponse(rh, null, null); // ... and close connection cnxn.sendBuffer(ServerCnxnFactory.closeConn); cnxn.disableRecv(); } return; } else if (h.getType() == OpCode.sasl) { processSasl(request, cnxn, h); } else { if (!authHelper.enforceAuthentication(cnxn, h.getXid())) { // Authentication enforcement is failed // Already sent response to user about failure and closed the session, lets return return; } else { Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(), h.getType(), request, cnxn.getAuthInfo()); int length = request.limit(); if (isLargeRequest(length)) { // checkRequestSize will throw IOException if request is rejected checkRequestSizeWhenMessageReceived(length); si.setLargeRequestSize(length); } si.setOwner(ServerCnxn.me); submitRequest(si); } } } private static boolean isSaslSuperUser(String id) { if (id == null || id.isEmpty()) { return false; } Properties properties = System.getProperties(); int prefixLen = SASL_SUPER_USER.length(); for (String k : properties.stringPropertyNames()) { if (k.startsWith(SASL_SUPER_USER) && (k.length() == prefixLen || k.charAt(prefixLen) == '.')) { String value = properties.getProperty(k); if (value != null && value.equals(id)) { return true; } } } return false; } private static boolean shouldAllowSaslFailedClientsConnect() { return Boolean.getBoolean(ALLOW_SASL_FAILED_CLIENTS); } private void processSasl(RequestRecord request, ServerCnxn cnxn, RequestHeader requestHeader) throws IOException { LOG.debug("Responding to client SASL token."); GetSASLRequest clientTokenRecord = request.readRecord(GetSASLRequest::new); byte[] clientToken = clientTokenRecord.getToken(); LOG.debug("Size of client SASL token: {}", clientToken.length); byte[] responseToken = null; try { ZooKeeperSaslServer saslServer = cnxn.zooKeeperSaslServer; try { // note that clientToken might be empty (clientToken.length == 0): // if using the DIGEST-MD5 mechanism, clientToken will be empty at the beginning of the // SASL negotiation process. responseToken = saslServer.evaluateResponse(clientToken); if (saslServer.isComplete()) { String authorizationID = saslServer.getAuthorizationID(); LOG.info("Session 0x{}: adding SASL authorization for authorizationID: {}", Long.toHexString(cnxn.getSessionId()), authorizationID); cnxn.addAuthInfo(new Id("sasl", authorizationID)); if (isSaslSuperUser(authorizationID)) { cnxn.addAuthInfo(new Id("super", "")); LOG.info( "Session 0x{}: Authenticated Id '{}' as super user", Long.toHexString(cnxn.getSessionId()), authorizationID); } } } catch (SaslException e) { LOG.warn("Client {} failed to SASL authenticate: {}", cnxn.getRemoteSocketAddress(), e); if (shouldAllowSaslFailedClientsConnect() && !authHelper.isSaslAuthRequired()) { LOG.warn("Maintaining client connection despite SASL authentication failure."); } else { int error; if (authHelper.isSaslAuthRequired()) { LOG.warn( "Closing client connection due to server requires client SASL authentication," + "but client SASL authentication has failed, or client is not configured with SASL " + "authentication."); error = Code.SESSIONCLOSEDREQUIRESASLAUTH.intValue(); } else { LOG.warn("Closing client connection due to SASL authentication failure."); error = Code.AUTHFAILED.intValue(); } ReplyHeader replyHeader = new ReplyHeader(requestHeader.getXid(), 0, error); cnxn.sendResponse(replyHeader, new SetSASLResponse(null), "response"); cnxn.sendCloseSession(); cnxn.disableRecv(); return; } } } catch (NullPointerException e) { LOG.error("cnxn.saslServer is null: cnxn object did not initialize its saslServer properly."); } if (responseToken != null) { LOG.debug("Size of server SASL response: {}", responseToken.length); } ReplyHeader replyHeader = new ReplyHeader(requestHeader.getXid(), 0, Code.OK.intValue()); Record record = new SetSASLResponse(responseToken); cnxn.sendResponse(replyHeader, record, "response"); } public ProcessTxnResult processTxn(Request request) { TxnHeader hdr = request.getHdr(); processTxnForSessionEvents(request, hdr, request.getTxn()); final boolean writeRequest = (hdr != null); final boolean quorumRequest = request.isQuorum(); // return fast w/o synchronization when we get a read if (!writeRequest && !quorumRequest) { return new ProcessTxnResult(); } ProcessTxnResult rc; synchronized (outstandingChanges) { rc = processTxnInDB(hdr, request.getTxn(), request.getTxnDigest()); // request.hdr is set for write requests, which are the only ones // that add to outstandingChanges. if (writeRequest) { long zxid = hdr.getZxid(); while (!outstandingChanges.isEmpty() && outstandingChanges.peek().zxid <= zxid) { ChangeRecord cr = outstandingChanges.remove(); ServerMetrics.getMetrics().OUTSTANDING_CHANGES_REMOVED.add(1); if (cr.zxid < zxid) { LOG.warn( "Zxid outstanding 0x{} is less than current 0x{}", Long.toHexString(cr.zxid), Long.toHexString(zxid)); } if (outstandingChangesForPath.get(cr.path) == cr) { outstandingChangesForPath.remove(cr.path); } } } } // do not add non quorum packets to the queue. if (quorumRequest) { getZKDatabase().addCommittedProposal(request); } return rc; } private void processTxnForSessionEvents(Request request, TxnHeader hdr, Record txn) { int opCode = (request == null) ? hdr.getType() : request.type; long sessionId = (request == null) ? hdr.getClientId() : request.sessionId; if (opCode == OpCode.createSession) { if (hdr != null && txn instanceof CreateSessionTxn) { CreateSessionTxn cst = (CreateSessionTxn) txn; sessionTracker.commitSession(sessionId, cst.getTimeOut()); } else if (request == null || !request.isLocalSession()) { LOG.warn("*****>>>>> Got {} {}", txn.getClass(), txn.toString()); } } else if (opCode == OpCode.closeSession) { sessionTracker.removeSession(sessionId); } } private ProcessTxnResult processTxnInDB(TxnHeader hdr, Record txn, TxnDigest digest) { if (hdr == null) { return new ProcessTxnResult(); } else { return getZKDatabase().processTxn(hdr, txn, digest); } } public Map> getSessionExpiryMap() { return sessionTracker.getSessionExpiryMap(); } /** * This method is used to register the ZooKeeperServerShutdownHandler to get * server's error or shutdown state change notifications. * {@link ZooKeeperServerShutdownHandler#handle(State)} will be called for * every server state changes {@link #setState(State)}. * * @param zkShutdownHandler shutdown handler */ void registerServerShutdownHandler(ZooKeeperServerShutdownHandler zkShutdownHandler) { this.zkShutdownHandler = zkShutdownHandler; } public boolean isResponseCachingEnabled() { return isResponseCachingEnabled; } public void setResponseCachingEnabled(boolean isEnabled) { isResponseCachingEnabled = isEnabled; } public ResponseCache getReadResponseCache() { return isResponseCachingEnabled ? readResponseCache : null; } public ResponseCache getGetChildrenResponseCache() { return isResponseCachingEnabled ? getChildrenResponseCache : null; } protected void registerMetrics() { MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); final ZKDatabase zkdb = this.getZKDatabase(); final ServerStats stats = this.serverStats(); rootContext.registerGauge("avg_latency", stats::getAvgLatency); rootContext.registerGauge("max_latency", stats::getMaxLatency); rootContext.registerGauge("min_latency", stats::getMinLatency); rootContext.registerGauge("packets_received", stats::getPacketsReceived); rootContext.registerGauge("packets_sent", stats::getPacketsSent); rootContext.registerGauge("num_alive_connections", stats::getNumAliveClientConnections); rootContext.registerGauge("outstanding_requests", stats::getOutstandingRequests); rootContext.registerGauge("uptime", stats::getUptime); rootContext.registerGauge("znode_count", zkdb::getNodeCount); rootContext.registerGauge("watch_count", zkdb.getDataTree()::getWatchCount); rootContext.registerGauge("ephemerals_count", zkdb.getDataTree()::getEphemeralsCount); rootContext.registerGauge("approximate_data_size", zkdb.getDataTree()::cachedApproximateDataSize); rootContext.registerGauge("global_sessions", zkdb::getSessionCount); rootContext.registerGauge("local_sessions", this.getSessionTracker()::getLocalSessionCount); OSMXBean osMbean = new OSMXBean(); rootContext.registerGauge("open_file_descriptor_count", osMbean::getOpenFileDescriptorCount); rootContext.registerGauge("max_file_descriptor_count", osMbean::getMaxFileDescriptorCount); rootContext.registerGauge("connection_drop_probability", this::getConnectionDropChance); rootContext.registerGauge("last_client_response_size", stats.getClientResponseStats()::getLastBufferSize); rootContext.registerGauge("max_client_response_size", stats.getClientResponseStats()::getMaxBufferSize); rootContext.registerGauge("min_client_response_size", stats.getClientResponseStats()::getMinBufferSize); rootContext.registerGauge("outstanding_tls_handshake", this::getOutstandingHandshakeNum); rootContext.registerGauge("auth_failed_count", stats::getAuthFailedCount); rootContext.registerGauge("non_mtls_remote_conn_count", stats::getNonMTLSRemoteConnCount); rootContext.registerGauge("non_mtls_local_conn_count", stats::getNonMTLSLocalConnCount); rootContext.registerGaugeSet(QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE, () -> QuotaMetricsUtils.getQuotaCountLimit(zkDb.getDataTree())); rootContext.registerGaugeSet(QuotaMetricsUtils.QUOTA_BYTES_LIMIT_PER_NAMESPACE, () -> QuotaMetricsUtils.getQuotaBytesLimit(zkDb.getDataTree())); rootContext.registerGaugeSet(QuotaMetricsUtils.QUOTA_COUNT_USAGE_PER_NAMESPACE, () -> QuotaMetricsUtils.getQuotaCountUsage(zkDb.getDataTree())); rootContext.registerGaugeSet(QuotaMetricsUtils.QUOTA_BYTES_USAGE_PER_NAMESPACE, () -> QuotaMetricsUtils.getQuotaBytesUsage(zkDb.getDataTree())); } protected void unregisterMetrics() { MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.unregisterGauge("avg_latency"); rootContext.unregisterGauge("max_latency"); rootContext.unregisterGauge("min_latency"); rootContext.unregisterGauge("packets_received"); rootContext.unregisterGauge("packets_sent"); rootContext.unregisterGauge("num_alive_connections"); rootContext.unregisterGauge("outstanding_requests"); rootContext.unregisterGauge("uptime"); rootContext.unregisterGauge("znode_count"); rootContext.unregisterGauge("watch_count"); rootContext.unregisterGauge("ephemerals_count"); rootContext.unregisterGauge("approximate_data_size"); rootContext.unregisterGauge("global_sessions"); rootContext.unregisterGauge("local_sessions"); rootContext.unregisterGauge("open_file_descriptor_count"); rootContext.unregisterGauge("max_file_descriptor_count"); rootContext.unregisterGauge("connection_drop_probability"); rootContext.unregisterGauge("last_client_response_size"); rootContext.unregisterGauge("max_client_response_size"); rootContext.unregisterGauge("min_client_response_size"); rootContext.unregisterGauge("auth_failed_count"); rootContext.unregisterGauge("non_mtls_remote_conn_count"); rootContext.unregisterGauge("non_mtls_local_conn_count"); rootContext.unregisterGaugeSet(QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE); rootContext.unregisterGaugeSet(QuotaMetricsUtils.QUOTA_BYTES_LIMIT_PER_NAMESPACE); rootContext.unregisterGaugeSet(QuotaMetricsUtils.QUOTA_COUNT_USAGE_PER_NAMESPACE); rootContext.unregisterGaugeSet(QuotaMetricsUtils.QUOTA_BYTES_USAGE_PER_NAMESPACE); } /** * Hook into admin server, useful to expose additional data * that do not represent metrics. * * @param response a sink which collects the data. */ public void dumpMonitorValues(BiConsumer response) { ServerStats stats = serverStats(); response.accept("version", Version.getFullVersion()); response.accept("server_state", stats.getServerState()); } /** * Grant or deny authorization to an operation on a node as a function of: * @param cnxn : the server connection or null for admin server commands * @param acl : set of ACLs for the node * @param perm : the permission that the client is requesting * @param ids : the credentials supplied by the client * @param path : the ZNode path * @param setAcls : for set ACL operations, the list of ACLs being set. Otherwise null. */ public void checkACL(ServerCnxn cnxn, List acl, int perm, List ids, String path, List setAcls) throws KeeperException.NoAuthException { if (skipACL) { return; } LOG.debug("Permission requested: {} ", perm); LOG.debug("ACLs for node: {}", acl); LOG.debug("Client credentials: {}", ids); if (acl == null || acl.size() == 0) { return; } for (Id authId : ids) { if (authId.getScheme().equals("super")) { return; } } for (ACL a : acl) { Id id = a.getId(); if ((a.getPerms() & perm) != 0) { if (id.getScheme().equals("world") && id.getId().equals("anyone")) { return; } ServerAuthenticationProvider ap = ProviderRegistry.getServerProvider(id.getScheme()); if (ap != null) { for (Id authId : ids) { if (authId.getScheme().equals(id.getScheme()) && ap.matches( new ServerAuthenticationProvider.ServerObjs(this, cnxn), new ServerAuthenticationProvider.MatchValues(path, authId.getId(), id.getId(), perm, setAcls))) { return; } } } } } throw new KeeperException.NoAuthException(); } /** * check a path whether exceeded the quota. * * @param path * the path of the node, used for the quota prefix check * @param lastData * the current node data, {@code null} for none * @param data * the data to be set, or {@code null} for none * @param type * currently, create and setData need to check quota */ public void checkQuota(String path, byte[] lastData, byte[] data, int type) throws KeeperException.QuotaExceededException { if (!enforceQuota) { return; } long dataBytes = (data == null) ? 0 : data.length; ZKDatabase zkDatabase = getZKDatabase(); String lastPrefix = zkDatabase.getDataTree().getMaxPrefixWithQuota(path); if (StringUtils.isEmpty(lastPrefix)) { return; } final String namespace = PathUtils.getTopNamespace(path); switch (type) { case OpCode.create: checkQuota(lastPrefix, dataBytes, 1, namespace); break; case OpCode.setData: checkQuota(lastPrefix, dataBytes - (lastData == null ? 0 : lastData.length), 0, namespace); break; default: throw new IllegalArgumentException("Unsupported OpCode for checkQuota: " + type); } } /** * check a path whether exceeded the quota. * * @param lastPrefix the path of the node which has a quota. * @param bytesDiff * the diff to be added to number of bytes * @param countDiff * the diff to be added to the count * @param namespace * the namespace for collecting quota exceeded errors */ private void checkQuota(String lastPrefix, long bytesDiff, long countDiff, String namespace) throws KeeperException.QuotaExceededException { LOG.debug("checkQuota: lastPrefix={}, bytesDiff={}, countDiff={}", lastPrefix, bytesDiff, countDiff); // now check the quota we set String limitNode = Quotas.limitPath(lastPrefix); DataNode node = getZKDatabase().getNode(limitNode); StatsTrack limitStats; if (node == null) { // should not happen LOG.error("Missing limit node for quota {}", limitNode); return; } synchronized (node) { limitStats = new StatsTrack(node.data); } //check the quota boolean checkCountQuota = countDiff != 0 && (limitStats.getCount() > -1 || limitStats.getCountHardLimit() > -1); boolean checkByteQuota = bytesDiff != 0 && (limitStats.getBytes() > -1 || limitStats.getByteHardLimit() > -1); if (!checkCountQuota && !checkByteQuota) { return; } //check the statPath quota String statNode = Quotas.statPath(lastPrefix); node = getZKDatabase().getNode(statNode); StatsTrack currentStats; if (node == null) { // should not happen LOG.error("Missing node for stat {}", statNode); return; } synchronized (node) { currentStats = new StatsTrack(node.data); } //check the Count Quota if (checkCountQuota) { long newCount = currentStats.getCount() + countDiff; boolean isCountHardLimit = limitStats.getCountHardLimit() > -1; long countLimit = isCountHardLimit ? limitStats.getCountHardLimit() : limitStats.getCount(); if (newCount > countLimit) { String msg = "Quota exceeded: " + lastPrefix + " [current count=" + newCount + ", " + (isCountHardLimit ? "hard" : "soft") + "CountLimit=" + countLimit + "]"; RATE_LOGGER.rateLimitLog(msg); if (isCountHardLimit) { updateQuotaExceededMetrics(namespace); throw new KeeperException.QuotaExceededException(lastPrefix); } } } //check the Byte Quota if (checkByteQuota) { long newBytes = currentStats.getBytes() + bytesDiff; boolean isByteHardLimit = limitStats.getByteHardLimit() > -1; long byteLimit = isByteHardLimit ? limitStats.getByteHardLimit() : limitStats.getBytes(); if (newBytes > byteLimit) { String msg = "Quota exceeded: " + lastPrefix + " [current bytes=" + newBytes + ", " + (isByteHardLimit ? "hard" : "soft") + "ByteLimit=" + byteLimit + "]"; RATE_LOGGER.rateLimitLog(msg); if (isByteHardLimit) { updateQuotaExceededMetrics(namespace); throw new KeeperException.QuotaExceededException(lastPrefix); } } } } public static boolean isDigestEnabled() { return digestEnabled; } public static void setDigestEnabled(boolean digestEnabled) { LOG.info("{} = {}", ZOOKEEPER_DIGEST_ENABLED, digestEnabled); ZooKeeperServer.digestEnabled = digestEnabled; } public static boolean isSerializeLastProcessedZxidEnabled() { return serializeLastProcessedZxidEnabled; } public static void setSerializeLastProcessedZxidEnabled(boolean serializeLastZxidEnabled) { serializeLastProcessedZxidEnabled = serializeLastZxidEnabled; LOG.info("{} = {}", ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED, serializeLastZxidEnabled); } /** * Trim a path to get the immediate predecessor. * * @param path * @return * @throws KeeperException.BadArgumentsException */ private String parentPath(String path) throws KeeperException.BadArgumentsException { int lastSlash = path.lastIndexOf('/'); if (lastSlash == -1 || path.indexOf('\0') != -1 || getZKDatabase().isSpecialPath(path)) { throw new KeeperException.BadArgumentsException(path); } return lastSlash == 0 ? "/" : path.substring(0, lastSlash); } private String effectiveACLPath(Request request) throws KeeperException.BadArgumentsException, KeeperException.InvalidACLException { boolean mustCheckACL = false; String path = null; List acl = null; switch (request.type) { case OpCode.create: case OpCode.create2: { CreateRequest req = request.readRequestRecordNoException(CreateRequest::new); if (req != null) { mustCheckACL = true; acl = req.getAcl(); path = parentPath(req.getPath()); } break; } case OpCode.delete: { DeleteRequest req = request.readRequestRecordNoException(DeleteRequest::new); if (req != null) { path = parentPath(req.getPath()); } break; } case OpCode.setData: { SetDataRequest req = request.readRequestRecordNoException(SetDataRequest::new); if (req != null) { path = req.getPath(); } break; } case OpCode.setACL: { SetACLRequest req = request.readRequestRecordNoException(SetACLRequest::new); if (req != null) { mustCheckACL = true; acl = req.getAcl(); path = req.getPath(); } break; } } if (mustCheckACL) { /* we ignore the extrapolated ACL returned by fixupACL because * we only care about it being well-formed (and if it isn't, an * exception will be raised). */ PrepRequestProcessor.fixupACL(path, request.authInfo, acl); } return path; } private int effectiveACLPerms(Request request) { switch (request.type) { case OpCode.create: case OpCode.create2: return ZooDefs.Perms.CREATE; case OpCode.delete: return ZooDefs.Perms.DELETE; case OpCode.setData: return ZooDefs.Perms.WRITE; case OpCode.setACL: return ZooDefs.Perms.ADMIN; default: return ZooDefs.Perms.ALL; } } /** * Check Write Requests for Potential Access Restrictions *

    * Before a request is being proposed to the quorum, lets check it * against local ACLs. Non-write requests (read, session, etc.) * are passed along. Invalid requests are sent a response. *

    * While we are at it, if the request will set an ACL: make sure it's * a valid one. * * @param request * @return true if request is permitted, false if not. * @throws java.io.IOException */ public boolean authWriteRequest(Request request) { int err; String pathToCheck; if (!enableEagerACLCheck) { return true; } err = KeeperException.Code.OK.intValue(); try { pathToCheck = effectiveACLPath(request); if (pathToCheck != null) { checkACL(request.cnxn, zkDb.getACL(pathToCheck, null), effectiveACLPerms(request), request.authInfo, pathToCheck, null); } } catch (KeeperException.NoAuthException e) { LOG.debug("Request failed ACL check", e); err = e.code().intValue(); } catch (KeeperException.InvalidACLException e) { LOG.debug("Request has an invalid ACL check", e); err = e.code().intValue(); } catch (KeeperException.NoNodeException e) { LOG.debug("ACL check against non-existent node: {}", e.getMessage()); } catch (KeeperException.BadArgumentsException e) { LOG.debug("ACL check against illegal node path: {}", e.getMessage()); } catch (Throwable t) { LOG.error("Uncaught exception in authWriteRequest with: ", t); throw t; } finally { if (err != KeeperException.Code.OK.intValue()) { /* This request has a bad ACL, so we are dismissing it early. */ decInProcess(); ReplyHeader rh = new ReplyHeader(request.cxid, 0, err); try { request.cnxn.sendResponse(rh, null, null); } catch (IOException e) { LOG.error("IOException : {}", e); } } } return err == KeeperException.Code.OK.intValue(); } public int getOutstandingHandshakeNum() { if (serverCnxnFactory instanceof NettyServerCnxnFactory) { return ((NettyServerCnxnFactory) serverCnxnFactory).getOutstandingHandshakeNum(); } else { return 0; } } public boolean isReconfigEnabled() { return this.reconfigEnabled; } public ZooKeeperServerShutdownHandler getZkShutdownHandler() { return zkShutdownHandler; } static void updateQuotaExceededMetrics(final String namespace) { if (namespace == null) { return; } ServerMetrics.getMetrics().QUOTA_EXCEEDED_ERROR_PER_NAMESPACE.add(namespace, 1); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000164 15051152474 032663 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerBea0100644 0000000 0000000 00000030352 15051152474 034230 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.Date; import org.apache.jute.BinaryInputArchive; import org.apache.zookeeper.Version; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.server.quorum.CommitProcessor; /** * This class implements the ZooKeeper server MBean interface. */ public class ZooKeeperServerBean implements ZooKeeperServerMXBean, ZKMBeanInfo { private final Date startTime; private final String name; protected final ZooKeeperServer zks; public ZooKeeperServerBean(ZooKeeperServer zks) { startTime = new Date(); this.zks = zks; name = "StandaloneServer_port" + zks.getClientPort(); } public String getClientPort() { return Integer.toString(zks.getClientPort()); } public String getName() { return name; } public boolean isHidden() { return false; } public String getStartTime() { return startTime.toString(); } public String getVersion() { return Version.getFullVersion(); } public double getAvgRequestLatency() { return zks.serverStats().getAvgLatency(); } public long getMaxRequestLatency() { return zks.serverStats().getMaxLatency(); } public long getMinRequestLatency() { return zks.serverStats().getMinLatency(); } public long getOutstandingRequests() { return zks.serverStats().getOutstandingRequests(); } public int getTickTime() { return zks.getTickTime(); } public void setTickTime(int tickTime) { zks.setTickTime(tickTime); } public int getMaxClientCnxnsPerHost() { return zks.getMaxClientCnxnsPerHost(); } public void setMaxClientCnxnsPerHost(int max) { if (zks.serverCnxnFactory != null) { zks.serverCnxnFactory.setMaxClientCnxnsPerHost(max); } if (zks.secureServerCnxnFactory != null) { zks.secureServerCnxnFactory.setMaxClientCnxnsPerHost(max); } } public int getMinSessionTimeout() { return zks.getMinSessionTimeout(); } public void setMinSessionTimeout(int min) { zks.setMinSessionTimeout(min); } public int getMaxSessionTimeout() { return zks.getMaxSessionTimeout(); } public void setMaxSessionTimeout(int max) { zks.setMaxSessionTimeout(max); } public long getDataDirSize() { return zks.getDataDirSize(); } public long getLogDirSize() { return zks.getLogDirSize(); } public long getPacketsReceived() { return zks.serverStats().getPacketsReceived(); } public long getPacketsSent() { return zks.serverStats().getPacketsSent(); } public long getFsyncThresholdExceedCount() { return zks.serverStats().getFsyncThresholdExceedCount(); } public long getAuthFailedCount() { return zks.serverStats().getAuthFailedCount(); } public long getNonMTLSRemoteConnCount() { return zks.serverStats().getNonMTLSRemoteConnCount(); } public long getNonMTLSLocalConnCount() { return zks.serverStats().getNonMTLSLocalConnCount(); } public void resetLatency() { zks.serverStats().resetLatency(); } public void resetMaxLatency() { zks.serverStats().resetMaxLatency(); } public void resetFsyncThresholdExceedCount() { zks.serverStats().resetFsyncThresholdExceedCount(); } public void resetAuthFailedCount() { zks.serverStats().resetAuthFailedCount(); } public void resetNonMTLSConnCount() { zks.serverStats().resetNonMTLSRemoteConnCount(); zks.serverStats().resetNonMTLSLocalConnCount(); } public void resetStatistics() { ServerStats serverStats = zks.serverStats(); serverStats.resetRequestCounters(); serverStats.resetLatency(); serverStats.resetFsyncThresholdExceedCount(); serverStats.resetAuthFailedCount(); serverStats.resetNonMTLSRemoteConnCount(); serverStats.resetNonMTLSLocalConnCount(); } public long getNumAliveConnections() { return zks.getNumAliveConnections(); } @Override public String getSecureClientPort() { if (zks.secureServerCnxnFactory != null) { return Integer.toString(zks.secureServerCnxnFactory.getLocalPort()); } return ""; } @Override public String getSecureClientAddress() { if (zks.secureServerCnxnFactory != null) { return String.format("%s:%d", zks.secureServerCnxnFactory.getLocalAddress().getHostString(), zks.secureServerCnxnFactory.getLocalPort()); } return ""; } @Override public long getTxnLogElapsedSyncTime() { return zks.getTxnLogElapsedSyncTime(); } @Override public int getJuteMaxBufferSize() { return BinaryInputArchive.maxBuffer; } @Override public int getLastClientResponseSize() { return zks.serverStats().getClientResponseStats().getLastBufferSize(); } @Override public int getMinClientResponseSize() { return zks.serverStats().getClientResponseStats().getMinBufferSize(); } @Override public int getMaxClientResponseSize() { return zks.serverStats().getClientResponseStats().getMaxBufferSize(); } @Override public boolean getResponseCachingEnabled() { return zks.isResponseCachingEnabled(); } @Override public void setResponseCachingEnabled(boolean isEnabled) { zks.setResponseCachingEnabled(isEnabled); } // Connection throttling settings /////////////////////////////////////////////////////////////////////////// public int getConnectionMaxTokens() { return zks.connThrottle().getMaxTokens(); } public void setConnectionMaxTokens(int val) { zks.connThrottle().setMaxTokens(val); } /////////////////////////////////////////////////////////////////////////// public int getConnectionTokenFillTime() { return zks.connThrottle().getFillTime(); } public void setConnectionTokenFillTime(int val) { zks.connThrottle().setFillTime(val); } /////////////////////////////////////////////////////////////////////////// public int getConnectionTokenFillCount() { return zks.connThrottle().getFillCount(); } public void setConnectionTokenFillCount(int val) { zks.connThrottle().setFillCount(val); } /////////////////////////////////////////////////////////////////////////// public int getConnectionFreezeTime() { return zks.connThrottle().getFreezeTime(); } public void setConnectionFreezeTime(int val) { zks.connThrottle().setFreezeTime(val); } /////////////////////////////////////////////////////////////////////////// public double getConnectionDropIncrease() { return zks.connThrottle().getDropIncrease(); } public void setConnectionDropIncrease(double val) { zks.connThrottle().setDropIncrease(val); } /////////////////////////////////////////////////////////////////////////// public double getConnectionDropDecrease() { return zks.connThrottle().getDropDecrease(); } public void setConnectionDropDecrease(double val) { zks.connThrottle().setDropDecrease(val); } /////////////////////////////////////////////////////////////////////////// public double getConnectionDecreaseRatio() { return zks.connThrottle().getDecreasePoint(); } public void setConnectionDecreaseRatio(double val) { zks.connThrottle().setDecreasePoint(val); } /////////////////////////////////////////////////////////////////////////// public int getCommitProcMaxReadBatchSize() { return CommitProcessor.getMaxReadBatchSize(); } public void setCommitProcMaxReadBatchSize(int size) { CommitProcessor.setMaxReadBatchSize(size); } /////////////////////////////////////////////////////////////////////////// public int getCommitProcMaxCommitBatchSize() { return CommitProcessor.getMaxCommitBatchSize(); } public void setCommitProcMaxCommitBatchSize(int size) { CommitProcessor.setMaxCommitBatchSize(size); } /////////////////////////////////////////////////////////////////////////// @Override public long getFlushDelay() { return zks.getFlushDelay(); } @Override public void setFlushDelay(long delay) { ZooKeeperServer.setFlushDelay(delay); } // Request throttling settings /////////////////////////////////////////////////////////////////////////// public int getThrottledOpWaitTime() { return ZooKeeperServer.getThrottledOpWaitTime(); } public void setThrottledOpWaitTime(int val) { ZooKeeperServer.setThrottledOpWaitTime(val); } /////////////////////////////////////////////////////////////////////////// public int getRequestThrottleLimit() { return RequestThrottler.getMaxRequests(); } public void setRequestThrottleLimit(int requests) { RequestThrottler.setMaxRequests(requests); } /////////////////////////////////////////////////////////////////////////// public int getRequestThrottleStallTime() { return RequestThrottler.getStallTime(); } public void setRequestThrottleStallTime(int time) { RequestThrottler.setStallTime(time); } /////////////////////////////////////////////////////////////////////////// public boolean getRequestThrottleDropStale() { return RequestThrottler.getDropStaleRequests(); } public void setRequestThrottleDropStale(boolean drop) { RequestThrottler.setDropStaleRequests(drop); } /////////////////////////////////////////////////////////////////////////// @Override public long getMaxWriteQueuePollTime() { return zks.getMaxWriteQueuePollTime(); } @Override public void setMaxWriteQueuePollTime(long delay) { ZooKeeperServer.setMaxWriteQueuePollTime(delay); } public boolean getRequestStaleLatencyCheck() { return Request.getStaleLatencyCheck(); } public void setRequestStaleLatencyCheck(boolean check) { Request.setStaleLatencyCheck(check); } /////////////////////////////////////////////////////////////////////////// @Override public int getMaxBatchSize() { return zks.getMaxBatchSize(); } @Override public void setMaxBatchSize(int size) { ZooKeeperServer.setMaxBatchSize(size); } public boolean getRequestStaleConnectionCheck() { return Request.getStaleConnectionCheck(); } public void setRequestStaleConnectionCheck(boolean check) { Request.setStaleConnectionCheck(check); } /////////////////////////////////////////////////////////////////////////// public int getLargeRequestMaxBytes() { return zks.getLargeRequestMaxBytes(); } public void setLargeRequestMaxBytes(int bytes) { zks.setLargeRequestMaxBytes(bytes); } /////////////////////////////////////////////////////////////////////////// public int getLargeRequestThreshold() { return zks.getLargeRequestThreshold(); } public void setLargeRequestThreshold(int threshold) { zks.setLargeRequestThreshold(threshold); } public int getMaxCnxns() { return ServerCnxnHelper.getMaxCnxns(zks.secureServerCnxnFactory, zks.serverCnxnFactory); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000164 15051152474 032663 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerConf.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerCon0100644 0000000 0000000 00000014272 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.LinkedHashMap; import java.util.Map; /** * Configuration data for a {@link ZooKeeperServer}. This class is immutable. */ public class ZooKeeperServerConf { /** * The key in the map returned by {@link #toMap()} for the client port. */ public static final String KEY_CLIENT_PORT = "client_port"; /** * The key in the map returned by {@link #toMap()} for the data directory. */ public static final String KEY_DATA_DIR = "data_dir"; /** * The key in the map returned by {@link #toMap()} for the data log * directory. */ public static final String KEY_DATA_LOG_DIR = "data_log_dir"; /** * The key in the map returned by {@link #toMap()} for the tick time. */ public static final String KEY_TICK_TIME = "tick_time"; /** * The key in the map returned by {@link #toMap()} for the maximum * client connections per host. */ public static final String KEY_MAX_CLIENT_CNXNS = "max_client_cnxns"; /** * The key in the map returned by {@link #toMap()} for the minimum session * timeout. */ public static final String KEY_MIN_SESSION_TIMEOUT = "min_session_timeout"; /** * The key in the map returned by {@link #toMap()} for the maximum session * timeout. */ public static final String KEY_MAX_SESSION_TIMEOUT = "max_session_timeout"; /** * The key in the map returned by {@link #toMap()} for the server ID. */ public static final String KEY_SERVER_ID = "server_id"; /** * The key in the map returned by {@link #toMap()} for the server socket * listen backlog. */ public static final String KEY_CLIENT_PORT_LISTEN_BACKLOG = "client_port_listen_backlog"; private final int clientPort; private final String dataDir; private final String dataLogDir; private final int tickTime; private final int maxClientCnxnsPerHost; private final int minSessionTimeout; private final int maxSessionTimeout; private final long serverId; private final int clientPortListenBacklog; /** * Creates a new configuration. * * @param clientPort client port * @param dataDir absolute path to data directory * @param dataLogDir absolute path to data log directory * @param tickTime tick time * @param maxClientCnxnsPerHost maximum number of client connections * @param minSessionTimeout minimum session timeout * @param maxSessionTimeout maximum session timeout * @param serverId server ID */ ZooKeeperServerConf(int clientPort, String dataDir, String dataLogDir, int tickTime, int maxClientCnxnsPerHost, int minSessionTimeout, int maxSessionTimeout, long serverId, int clientPortListenBacklog) { this.clientPort = clientPort; this.dataDir = dataDir; this.dataLogDir = dataLogDir; this.tickTime = tickTime; this.maxClientCnxnsPerHost = maxClientCnxnsPerHost; this.minSessionTimeout = minSessionTimeout; this.maxSessionTimeout = maxSessionTimeout; this.serverId = serverId; this.clientPortListenBacklog = clientPortListenBacklog; } /** * Gets the client port. * * @return client port */ public int getClientPort() { return clientPort; } /** * Gets the data directory. * * @return data directory */ public String getDataDir() { return dataDir; } /** * Gets the data log directory. * * @return data log directory */ public String getDataLogDir() { return dataLogDir; } /** * Gets the tick time. * * @return tick time */ public int getTickTime() { return tickTime; } /** * Gets the maximum client connections per host. * * @return maximum client connections per host */ public int getMaxClientCnxnsPerHost() { return maxClientCnxnsPerHost; } /** * Gets the minimum session timeout. * * @return minimum session timeout */ public int getMinSessionTimeout() { return minSessionTimeout; } /** * Gets the maximum session timeout. * * @return maximum session timeout */ public int getMaxSessionTimeout() { return maxSessionTimeout; } /** * Gets the server ID. * * @return server ID */ public long getServerId() { return serverId; } /** * Returns the server socket listen backlog length. */ public int getClientPortListenBacklog() { return clientPortListenBacklog; } /** * Converts this configuration to a map. The returned map is mutable, and * changes to it do not reflect back into this configuration. * * @return map representation of configuration */ public Map toMap() { Map conf = new LinkedHashMap<>(); conf.put(KEY_CLIENT_PORT, clientPort); conf.put(KEY_DATA_DIR, dataDir); conf.put(KEY_DATA_LOG_DIR, dataLogDir); conf.put(KEY_TICK_TIME, tickTime); conf.put(KEY_MAX_CLIENT_CNXNS, maxClientCnxnsPerHost); conf.put(KEY_MIN_SESSION_TIMEOUT, minSessionTimeout); conf.put(KEY_MAX_SESSION_TIMEOUT, maxSessionTimeout); conf.put(KEY_SERVER_ID, serverId); conf.put(KEY_CLIENT_PORT_LISTEN_BACKLOG, clientPortListenBacklog); return conf; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000170 15051152474 032660 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerListener.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerLis0100644 0000000 0000000 00000002376 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; /** * Listener for the critical resource events. */ public interface ZooKeeperServerListener { /** * This will notify the server that some critical thread has stopped. * It usually takes place when fatal error occurred. * * @param threadName * - name of the thread * @param errorCode * - error code */ void notifyStopping(String threadName, int errorCode); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000174 15051152474 032664 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerListenerImpl.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerLis0100644 0000000 0000000 00000003346 15051152474 034273 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.apache.zookeeper.server.ZooKeeperServer.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default listener implementation, which will be used to notify internal * errors. For example, if some critical thread has stopped due to fatal errors, * then it will get notifications and will change the state of ZooKeeper server * to ERROR representing an error status. */ class ZooKeeperServerListenerImpl implements ZooKeeperServerListener { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerListenerImpl.class); private final ZooKeeperServer zkServer; ZooKeeperServerListenerImpl(ZooKeeperServer zkServer) { this.zkServer = zkServer; } @Override public void notifyStopping(String threadName, int exitCode) { LOG.info("Thread {} exits, error code {}", threadName, exitCode); zkServer.setState(State.ERROR); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000166 15051152474 032665 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMXB0100644 0000000 0000000 00000014725 15051152474 034175 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * ZooKeeper server MBean. */ public interface ZooKeeperServerMXBean { /** * @return the server socket port number */ String getClientPort(); /** * @return the zookeeper server version */ String getVersion(); /** * @return time the server was started */ String getStartTime(); /** * @return min request latency in ms */ long getMinRequestLatency(); /** * @return average request latency in ms */ double getAvgRequestLatency(); /** * @return max request latency in ms */ long getMaxRequestLatency(); /** * @return number of packets received so far */ long getPacketsReceived(); /** * @return number of packets sent so far */ long getPacketsSent(); /** * @return number of fsync threshold exceeds so far */ long getFsyncThresholdExceedCount(); /** * @return number of AuthFailedCount so far */ long getAuthFailedCount(); /** * @return number of NonMTLSLocalConnCount so far */ long getNonMTLSLocalConnCount(); /** * @return number of NonMTLSRemoteConnCount so far */ long getNonMTLSRemoteConnCount(); /** * @return number of outstanding requests. */ long getOutstandingRequests(); /** * Current TickTime of server in milliseconds */ int getTickTime(); /** * Set TickTime of server in milliseconds */ void setTickTime(int tickTime); /** Current maxClientCnxns allowed from a particular host */ int getMaxClientCnxnsPerHost(); /** Set maxClientCnxns allowed from a particular host */ void setMaxClientCnxnsPerHost(int max); /** * Current minSessionTimeout of the server in milliseconds */ int getMinSessionTimeout(); /** * Set minSessionTimeout of server in milliseconds */ void setMinSessionTimeout(int min); /** * Current maxSessionTimeout of the server in milliseconds */ int getMaxSessionTimeout(); /** * Set maxSessionTimeout of server in milliseconds */ void setMaxSessionTimeout(int max); boolean getResponseCachingEnabled(); void setResponseCachingEnabled(boolean isEnabled); /* Connection throttling settings */ int getConnectionMaxTokens(); void setConnectionMaxTokens(int val); int getConnectionTokenFillTime(); void setConnectionTokenFillTime(int val); int getConnectionTokenFillCount(); void setConnectionTokenFillCount(int val); int getConnectionFreezeTime(); void setConnectionFreezeTime(int val); double getConnectionDropIncrease(); void setConnectionDropIncrease(double val); double getConnectionDropDecrease(); void setConnectionDropDecrease(double val); double getConnectionDecreaseRatio(); void setConnectionDecreaseRatio(double val); int getCommitProcMaxReadBatchSize(); void setCommitProcMaxReadBatchSize(int size); int getCommitProcMaxCommitBatchSize(); void setCommitProcMaxCommitBatchSize(int size); int getRequestThrottleLimit(); void setRequestThrottleLimit(int requests); int getRequestThrottleStallTime(); void setRequestThrottleStallTime(int time); boolean getRequestThrottleDropStale(); void setRequestThrottleDropStale(boolean drop); int getThrottledOpWaitTime(); void setThrottledOpWaitTime(int val); boolean getRequestStaleLatencyCheck(); void setRequestStaleLatencyCheck(boolean check); boolean getRequestStaleConnectionCheck(); void setRequestStaleConnectionCheck(boolean check); int getLargeRequestMaxBytes(); void setLargeRequestMaxBytes(int bytes); int getLargeRequestThreshold(); void setLargeRequestThreshold(int threshold); /** * Reset packet and latency statistics */ void resetStatistics(); /** * Reset min/avg/max latency statistics */ void resetLatency(); /** * Reset max latency statistics only. */ void resetMaxLatency(); /** * Reset Fsync Threshold Exceed Count statistics only. */ void resetFsyncThresholdExceedCount(); /** * Reset NonMTLS(Local+Remote)ConnCount statistics only. */ void resetNonMTLSConnCount(); /** * Reset AuthFailedCount statistics only. */ void resetAuthFailedCount(); /** * @return number of alive client connections */ long getNumAliveConnections(); /** * @return estimated size of data directory in bytes */ long getDataDirSize(); /** * @return estimated size of log directory in bytes */ long getLogDirSize(); /** * @return secure client port */ String getSecureClientPort(); /** * @return secure client address */ String getSecureClientAddress(); /** * Returns the elapsed sync of time of transaction log in milliseconds. */ long getTxnLogElapsedSyncTime(); /** * @return Returns the value of the following config setting: jute.maxbuffer */ int getJuteMaxBufferSize(); /** * @return size of latest generated client response */ int getLastClientResponseSize(); /** * @return size of smallest generated client response */ int getMinClientResponseSize(); /** * @return size of largest generated client response */ int getMaxClientResponseSize(); long getFlushDelay(); void setFlushDelay(long delay); long getMaxWriteQueuePollTime(); void setMaxWriteQueuePollTime(long delay); int getMaxBatchSize(); void setMaxBatchSize(int size); /** * @return Current maxCnxns allowed to a single ZooKeeper server */ int getMaxCnxns(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000164 15051152474 032663 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMain.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerMai0100644 0000000 0000000 00000027662 15051152474 034261 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.management.JMException; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.audit.ZKAuditProvider; import org.apache.zookeeper.jmx.ManagedUtil; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.apache.zookeeper.metrics.impl.MetricsProviderBootstrap; import org.apache.zookeeper.server.admin.AdminServer; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.admin.AdminServerFactory; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.FileTxnSnapLog.DatadirException; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.util.JvmPauseMonitor; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class starts and runs a standalone ZooKeeperServer. */ @InterfaceAudience.Public public class ZooKeeperServerMain { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerMain.class); private static final String USAGE = "Usage: ZooKeeperServerMain configfile | port datadir [ticktime] [maxcnxns]"; // ZooKeeper server supports two kinds of connection: unencrypted and encrypted. private ServerCnxnFactory cnxnFactory; private ServerCnxnFactory secureCnxnFactory; private ContainerManager containerManager; private MetricsProvider metricsProvider; private AdminServer adminServer; /* * Start up the ZooKeeper server. * * @param args the configfile or the port datadir [ticktime] */ public static void main(String[] args) { ZooKeeperServerMain main = new ZooKeeperServerMain(); try { main.initializeAndRun(args); } catch (IllegalArgumentException e) { LOG.error("Invalid arguments, exiting abnormally", e); LOG.info(USAGE); System.err.println(USAGE); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } catch (ConfigException e) { LOG.error("Invalid config, exiting abnormally", e); System.err.println("Invalid config, exiting abnormally"); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } catch (DatadirException e) { LOG.error("Unable to access datadir, exiting abnormally", e); System.err.println("Unable to access datadir, exiting abnormally"); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue()); } catch (AdminServerException e) { LOG.error("Unable to start AdminServer, exiting abnormally", e); System.err.println("Unable to start AdminServer, exiting abnormally"); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue()); } catch (Exception e) { LOG.error("Unexpected exception, exiting abnormally", e); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } LOG.info("Exiting normally"); ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue()); } protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException { try { ManagedUtil.registerLog4jMBeans(); } catch (JMException e) { LOG.warn("Unable to register log4j JMX control", e); } ServerConfig config = new ServerConfig(); if (args.length == 1) { config.parse(args[0]); } else { config.parse(args); } runFromConfig(config); } /** * Run from a ServerConfig. * @param config ServerConfig to use. * @throws IOException * @throws AdminServerException */ public void runFromConfig(ServerConfig config) throws IOException, AdminServerException { LOG.info("Starting server"); FileTxnSnapLog txnLog = null; try { try { metricsProvider = MetricsProviderBootstrap.startMetricsProvider( config.getMetricsProviderClassName(), config.getMetricsProviderConfiguration()); } catch (MetricsProviderLifeCycleException error) { throw new IOException("Cannot boot MetricsProvider " + config.getMetricsProviderClassName(), error); } ServerMetrics.metricsProviderInitialized(metricsProvider); ProviderRegistry.initialize(); // Note that this thread isn't going to be doing anything else, // so rather than spawning another thread, we will just call // run() in this thread. // create a file logger url from the command line args txnLog = new FileTxnSnapLog(config.dataLogDir, config.dataDir); JvmPauseMonitor jvmPauseMonitor = null; if (config.jvmPauseMonitorToRun) { jvmPauseMonitor = new JvmPauseMonitor(config); } final ZooKeeperServer zkServer = new ZooKeeperServer(jvmPauseMonitor, txnLog, config.tickTime, config.minSessionTimeout, config.maxSessionTimeout, config.listenBacklog, null, config.initialConfig); txnLog.setServerStats(zkServer.serverStats()); // Registers shutdown handler which will be used to know the // server error or shutdown state changes. final CountDownLatch shutdownLatch = new CountDownLatch(1); zkServer.registerServerShutdownHandler(new ZooKeeperServerShutdownHandler(shutdownLatch)); // Start Admin server adminServer = AdminServerFactory.createAdminServer(); adminServer.setZooKeeperServer(zkServer); adminServer.start(); boolean needStartZKServer = true; if (config.getClientPortAddress() != null) { cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), false); cnxnFactory.startup(zkServer); // zkServer has been started. So we don't need to start it again in secureCnxnFactory. needStartZKServer = false; } if (config.getSecureClientPortAddress() != null) { secureCnxnFactory = ServerCnxnFactory.createFactory(); secureCnxnFactory.configure(config.getSecureClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), true); secureCnxnFactory.startup(zkServer, needStartZKServer); } containerManager = new ContainerManager( zkServer.getZKDatabase(), zkServer.firstProcessor, Integer.getInteger("znode.container.checkIntervalMs", (int) TimeUnit.MINUTES.toMillis(1)), Integer.getInteger("znode.container.maxPerMinute", 10000), Long.getLong("znode.container.maxNeverUsedIntervalMs", 0) ); containerManager.start(); ZKAuditProvider.addZKStartStopAuditLog(); serverStarted(); // Watch status of ZooKeeper server. It will do a graceful shutdown // if the server is not running or hits an internal error. shutdownLatch.await(); shutdown(); if (cnxnFactory != null) { cnxnFactory.join(); } if (secureCnxnFactory != null) { secureCnxnFactory.join(); } zkServer.shutdown(true); } catch (InterruptedException e) { // warn, but generally this is ok LOG.warn("Server interrupted", e); } finally { if (txnLog != null) { txnLog.close(); } if (metricsProvider != null) { try { metricsProvider.stop(); } catch (Throwable error) { LOG.warn("Error while stopping metrics", error); } } } } /** * Shutdown the serving instance */ protected void shutdown() { if (containerManager != null) { containerManager.stop(); } if (cnxnFactory != null) { cnxnFactory.shutdown(); } if (secureCnxnFactory != null) { secureCnxnFactory.shutdown(); } try { if (adminServer != null) { adminServer.shutdown(); } } catch (AdminServerException e) { LOG.warn("Problem stopping AdminServer", e); } } // VisibleForTesting ServerCnxnFactory getCnxnFactory() { return cnxnFactory; } // VisibleForTesting ServerCnxnFactory getSecureCnxnFactory() { return secureCnxnFactory; } // VisibleForTesting public int getClientPort() { if (cnxnFactory != null) { return cnxnFactory.getLocalPort(); } return 0; } // VisibleForTesting public int getSecureClientPort() { if (secureCnxnFactory != null) { return secureCnxnFactory.getLocalPort(); } return 0; } /** * Shutdowns properly the service, this method is not a public API. */ public void close() { ServerCnxnFactory primaryCnxnFactory = this.cnxnFactory; ServerCnxnFactory secondaryCnxnFactory = this.secureCnxnFactory; try { if (primaryCnxnFactory == null) { // in case of pure TLS we can hook into secureCnxnFactory primaryCnxnFactory = secondaryCnxnFactory; } if (primaryCnxnFactory == null || primaryCnxnFactory.getZooKeeperServer() == null) { LOG.info("Connection factory did not start"); return; } ZooKeeperServerShutdownHandler zkShutdownHandler = primaryCnxnFactory.getZooKeeperServer().getZkShutdownHandler(); zkShutdownHandler.handle(ZooKeeperServer.State.SHUTDOWN); try { // ServerCnxnFactory will call the shutdown primaryCnxnFactory.join(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } finally { // ensure that we are closing the sockets if (primaryCnxnFactory != null) { primaryCnxnFactory.shutdown(); } if (secondaryCnxnFactory != null) { secondaryCnxnFactory.shutdown(); } } } protected void serverStarted() { } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000177 15051152474 032667 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerShutdownHandler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperServerShu0100644 0000000 0000000 00000003157 15051152474 034303 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.server.ZooKeeperServer.State; /** * ZooKeeper server shutdown handler which will be used to handle ERROR or * SHUTDOWN server state transitions, which in turn releases the associated * shutdown latch. */ public final class ZooKeeperServerShutdownHandler { private final CountDownLatch shutdownLatch; ZooKeeperServerShutdownHandler(CountDownLatch shutdownLatch) { this.shutdownLatch = shutdownLatch; } /** * This will be invoked when the server transition to a new server state. * * @param state new server state */ public void handle(State state) { if (state == State.ERROR || state == State.SHUTDOWN) { shutdownLatch.countDown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000160 15051152474 032657 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperThread.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperThread.ja0100644 0000000 0000000 00000003355 15051152474 034155 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is the main class for catching all the uncaught exceptions thrown by the * threads. */ public class ZooKeeperThread extends Thread { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperThread.class); private UncaughtExceptionHandler uncaughtExceptionalHandler = (t, e) -> handleException(t.getName(), e); public ZooKeeperThread(String threadName) { super(threadName); setUncaughtExceptionHandler(uncaughtExceptionalHandler); } /** * This will be used by the uncaught exception handler and just log a * warning message and return. * * @param thName * - thread name * @param e * - exception object */ protected void handleException(String thName, Throwable e) { LOG.warn("Exception occurred from thread {}", thName, e); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooTrace.java0100644 0000000 0000000 00000006171 15051152474 033176 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.server.quorum.QuorumPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class encapsulates and centralizes tracing for the ZooKeeper server. * Trace messages go to the log with TRACE level. *

    * Logback must be correctly configured to capture the TRACE messages. */ public class ZooTrace { public static final long CLIENT_REQUEST_TRACE_MASK = 1 << 1; /** * this field is obsolete */ @Deprecated public static final long CLIENT_DATA_PACKET_TRACE_MASK = 1 << 2; public static final long CLIENT_PING_TRACE_MASK = 1 << 3; public static final long SERVER_PACKET_TRACE_MASK = 1 << 4; public static final long SESSION_TRACE_MASK = 1 << 5; public static final long EVENT_DELIVERY_TRACE_MASK = 1 << 6; public static final long SERVER_PING_TRACE_MASK = 1 << 7; public static final long WARNING_TRACE_MASK = 1 << 8; /** * this field is obsolete */ @Deprecated public static final long JMX_TRACE_MASK = 1 << 9; private static long traceMask = CLIENT_REQUEST_TRACE_MASK | SERVER_PACKET_TRACE_MASK | SESSION_TRACE_MASK | WARNING_TRACE_MASK; public static synchronized long getTextTraceLevel() { return traceMask; } public static synchronized void setTextTraceLevel(long mask) { traceMask = mask; final Logger LOG = LoggerFactory.getLogger(ZooTrace.class); LOG.info("Set text trace mask to 0x{}", Long.toHexString(mask)); } public static synchronized boolean isTraceEnabled(Logger log, long mask) { return log.isTraceEnabled() && (mask & traceMask) != 0; } public static void logTraceMessage(Logger log, long mask, String msg) { if (isTraceEnabled(log, mask)) { log.trace(msg); } } public static void logQuorumPacket(Logger log, long mask, char direction, QuorumPacket qp) { if (isTraceEnabled(log, mask)) { logTraceMessage(log, mask, direction + " " + LearnerHandler.packetToString(qp)); } } public static void logRequest(Logger log, long mask, char rp, Request request, String header) { if (isTraceEnabled(log, mask)) { log.trace(header + ":" + rp + request.toString()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000162 15051152474 032655 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/AdminServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/AdminServer.0100644 0000000 0000000 00000003317 15051152474 034114 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.server.ZooKeeperServer; /** * Interface for an embedded admin server that runs Commands. There is only one * functional implementation, JettyAdminServer. DummyAdminServer, which does * nothing, is used when we do not wish to run a server. */ @InterfaceAudience.Public public interface AdminServer { void start() throws AdminServerException; void shutdown() throws AdminServerException; void setZooKeeperServer(ZooKeeperServer zkServer); @InterfaceAudience.Public class AdminServerException extends Exception { private static final long serialVersionUID = 1L; public AdminServerException(String message, Throwable cause) { super(message, cause); } public AdminServerException(Throwable cause) { super(cause); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000171 15051152474 032655 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/AdminServerFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/AdminServerF0100644 0000000 0000000 00000005316 15051152474 034145 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.lang.reflect.InvocationTargetException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Factory class for creating an AdminServer. */ public class AdminServerFactory { private static final Logger LOG = LoggerFactory.getLogger(AdminServerFactory.class); /** * This method encapsulates the logic for whether we should use a * JettyAdminServer (i.e., the AdminServer is enabled) or a DummyAdminServer * (i.e., the AdminServer is disabled). It uses reflection when attempting * to create a JettyAdminServer, rather than referencing the class directly, * so that it's ok to omit Jetty from the classpath if a user doesn't wish * to pull in Jetty with ZooKeeper. */ public static AdminServer createAdminServer() { if (!"false".equals(System.getProperty("zookeeper.admin.enableServer"))) { try { Class jettyAdminServerC = Class.forName("org.apache.zookeeper.server.admin.JettyAdminServer"); Object adminServer = jettyAdminServerC.getConstructor().newInstance(); return (AdminServer) adminServer; } catch (ClassNotFoundException e) { LOG.warn("Unable to start JettyAdminServer", e); } catch (InstantiationException e) { LOG.warn("Unable to start JettyAdminServer", e); } catch (IllegalAccessException e) { LOG.warn("Unable to start JettyAdminServer", e); } catch (InvocationTargetException e) { LOG.warn("Unable to start JettyAdminServer", e); } catch (NoSuchMethodException e) { LOG.warn("Unable to start JettyAdminServer", e); } catch (NoClassDefFoundError e) { LOG.warn("Unable to load jetty, not starting JettyAdminServer", e); } } return new DummyAdminServer(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000162 15051152474 032655 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/AuthRequest.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/AuthRequest.0100644 0000000 0000000 00000003527 15051152474 034152 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import org.apache.zookeeper.ZooDefs; /** * Class representing auth data for performing ACL check on admin server commands. * * For example, SnapshotCommand requires {@link ZooDefs.Perms.ALL} permission on * the root node. * */ public class AuthRequest { private final int permission; private final String path; /** * @param permission * the required permission for auth check * @param path * the ZNode path for auth check */ public AuthRequest(final int permission, final String path) { this.permission = permission; this.path = path; } /** * @return permission */ public int getPermission() { return permission; } /** * @return ZNode path */ public String getPath() { return path; } @Override public String toString() { return "AuthRequest{" + "permission=" + permission + ", path='" + path + '\'' + '}'; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000156 15051152474 032660 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Command.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Command.java0100644 0000000 0000000 00000007127 15051152474 034120 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.InputStream; import java.util.Map; import java.util.Set; import org.apache.zookeeper.server.ZooKeeperServer; /** * Interface implemented by all commands runnable by JettyAdminServer. * * @see CommandBase * @see Commands * @see JettyAdminServer */ public interface Command { /** * The set of all names that can be used to refer to this command (e.g., * "configuration", "config", and "conf"). */ Set getNames(); /** * The name that is returned with the command response and that appears in * the list of all commands. This should be a member of the set returned by * getNames(). */ String getPrimaryName(); /** * @return true if the command requires an active ZooKeeperServer or a * synced peer in order to resolve */ boolean isServerRequired(); /** * @return AuthRequest associated to the command. Null means auth check is not required. */ AuthRequest getAuthRequest(); /** * Run this command for HTTP GET request. Commands take a ZooKeeperServer, String-valued keyword * arguments and return a CommandResponse object containing any information * constituting the response to the command. Commands are responsible for * parsing keyword arguments and performing any error handling if necessary. * Errors should be reported by setting the "error" entry of the returned * map with an appropriate message rather than throwing an exception. * * @param zkServer ZooKeeper server * @param kwargs keyword -> argument value mapping * @return CommandResponse representing response to command containing at minimum: * - "command" key containing the command's primary name * - "error" key containing a String error message or null if no error */ CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs); /** * Run this command for HTTP POST. Commands take a ZooKeeperServer and InputStream and * return a CommandResponse object containing any information * constituting the response to the command. Commands are responsible for * parsing keyword arguments and performing any error handling if necessary. * Errors should be reported by setting the "error" entry of the returned * map with an appropriate message rather than throwing an exception. * * @param zkServer ZooKeeper server * @param inputStream InputStream from request * @return CommandResponse representing response to command containing at minimum: * - "command" key containing the command's primary name * - "error" key containing a String error message or null if no error */ CommandResponse runPost(ZooKeeperServer zkServer, InputStream inputStream); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000162 15051152474 032655 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/CommandBase.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/CommandBase.0100644 0000000 0000000 00000004774 15051152474 034056 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.util.HashSet; import java.util.List; import java.util.Set; public abstract class CommandBase implements Command { private final String primaryName; private final Set names; private final boolean serverRequired; private final AuthRequest authRequest; /** * @param names The possible names of this command, with the primary name first. */ protected CommandBase(List names) { this(names, true); } protected CommandBase(List names, boolean serverRequired) { this(names, serverRequired, null); } protected CommandBase(List names, boolean serverRequired, AuthRequest authRequest) { if (authRequest != null && !serverRequired) { throw new IllegalArgumentException("An active server is required for auth check"); } this.primaryName = names.get(0); this.names = new HashSet<>(names); this.serverRequired = serverRequired; this.authRequest = authRequest; } @Override public String getPrimaryName() { return primaryName; } @Override public Set getNames() { return names; } @Override public boolean isServerRequired() { return serverRequired; } @Override public AuthRequest getAuthRequest() { return authRequest; } /** * @return A response with the command set to the primary name and the * error set to null (these are the two entries that all command * responses are required to include). */ protected CommandResponse initializeResponse() { return new CommandResponse(primaryName); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000167 15051152474 032662 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/CommandOutputter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/CommandOutpu0100644 0000000 0000000 00000002557 15051152474 034237 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.OutputStream; import java.io.PrintWriter; /** * CommandOutputters are used to format the responses from Commands. * * @see Command * @see JettyAdminServer */ public interface CommandOutputter { /** The MIME type of this output (e.g., "application/json") */ String getContentType(); /** Print out data as output */ default void output(CommandResponse response, PrintWriter pw) {} /** Stream out data as output */ default void output(final CommandResponse response, final OutputStream os) {} } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000166 15051152474 032661 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/CommandResponse.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/CommandRespo0100644 0000000 0000000 00000011551 15051152474 034205 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.InputStream; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; /** * A response from running a {@link Command}. */ public class CommandResponse { /** * The key in the map returned by {@link #toMap()} for the command name. */ public static final String KEY_COMMAND = "command"; /** * The key in the map returned by {@link #toMap()} for the error string. */ public static final String KEY_ERROR = "error"; private final String command; private final String error; private final Map data; private final Map headers; private int statusCode; private InputStream inputStream; /** * Creates a new response with no error string. * * @param command command name */ public CommandResponse(String command) { this(command, null, HttpServletResponse.SC_OK); } /** * Creates a new response. * * @param command command name * @param error error string (may be null) * @param statusCode http status code */ public CommandResponse(String command, String error, int statusCode) { this(command, error, statusCode, null); } /** * Creates a new response. * * @param command command name * @param error error string (may be null) * @param statusCode http status code * @param inputStream inputStream to send out data (may be null) */ public CommandResponse(final String command, final String error, final int statusCode, final InputStream inputStream) { this.command = command; this.error = error; data = new LinkedHashMap<>(); headers = new HashMap<>(); this.statusCode = statusCode; this.inputStream = inputStream; } /** * Gets the command name. * * @return command name */ public String getCommand() { return command; } /** * Gets the error string (may be null). * * @return error string */ public String getError() { return error; } /** * Gets the http status code * * @return http status code */ public int getStatusCode() { return statusCode; } /** * Sets the http status code */ public void setStatusCode(int statusCode) { this.statusCode = statusCode; } /** * Gets the InputStream (may be null). * * @return InputStream */ public InputStream getInputStream() { return inputStream; } /** * Sets the InputStream */ public void setInputStream(final InputStream inputStream) { this.inputStream = inputStream; } /** * Adds a key/value pair to this response. * * @param key key * @param value value * @return prior value for key, or null if none */ public Object put(String key, Object value) { return data.put(key, value); } /** * Adds all key/value pairs in the given map to this response. * * @param m map of key/value pairs */ public void putAll(Map m) { data.putAll(m); } /** * Adds a header to this response. * * @param name name of the header * @param value value of the header */ public void addHeader(final String name, final String value) { headers.put(name, value); } /** * Returns all headers * * @return map representation of all headers */ public Map getHeaders() { return headers; } /** * Converts this response to a map. The returned map is mutable, and * changes to it do not reflect back into this response. * * @return map representation of response */ public Map toMap() { Map m = new LinkedHashMap<>(data); m.put(KEY_COMMAND, command); m.put(KEY_ERROR, error); m.putAll(data); return m; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000157 15051152474 032661 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.jav0100644 0000000 0000000 00000132201 15051152474 034132 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.apache.zookeeper.server.persistence.FileSnap.SNAPSHOT_FILE_PREFIX; import com.fasterxml.jackson.annotation.JsonProperty; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.Environment; import org.apache.zookeeper.Environment.Entry; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Version; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.auth.ServerAuthenticationProvider; import org.apache.zookeeper.server.persistence.SnapshotInfo; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.server.quorum.Follower; import org.apache.zookeeper.server.quorum.FollowerZooKeeperServer; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.server.quorum.MultipleAddresses; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumZooKeeperServer; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.RateLimiter; import org.apache.zookeeper.server.util.ZxidUtils; import org.eclipse.jetty.http.HttpStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class containing static methods for registering and running Commands, as well * as default Command definitions. * * @see Command * @see JettyAdminServer */ public class Commands { static final Logger LOG = LoggerFactory.getLogger(Commands.class); // VisibleForTesting static final String ADMIN_RATE_LIMITER_INTERVAL = "zookeeper.admin.rateLimiterIntervalInMS"; private static final long rateLimiterInterval = Integer.parseInt(System.getProperty(ADMIN_RATE_LIMITER_INTERVAL, "300000")); // VisibleForTesting static final String AUTH_INFO_SEPARATOR = " "; // VisibleForTesting static final String ROOT_PATH = "/"; /** Maps command names to Command instances */ private static Map commands = new HashMap<>(); private static Set primaryNames = new HashSet<>(); /** * Registers the given command. Registered commands can be run by passing * any of their names to runCommand. */ public static void registerCommand(Command command) { for (String name : command.getNames()) { Command prev = commands.put(name, command); if (prev != null) { LOG.warn("Re-registering command {} (primary name = {})", name, command.getPrimaryName()); } } primaryNames.add(command.getPrimaryName()); } /** * Run the registered command with name cmdName. Commands should not produce * any exceptions; any (anticipated) errors should be reported in the * "error" entry of the returned map. Likewise, if no command with the given * name is registered, this will be noted in the "error" entry. * * @param cmdName * @param zkServer * @param kwargs String-valued keyword arguments to the command from HTTP GET request * (may be null if command requires no additional arguments) * @param authInfo auth info for auth check * (null if command requires no auth check) * @param request HTTP request * @return Map representing response to command containing at minimum: * - "command" key containing the command's primary name * - "error" key containing a String error message or null if no error */ public static CommandResponse runGetCommand( String cmdName, ZooKeeperServer zkServer, Map kwargs, String authInfo, HttpServletRequest request) { return runCommand(cmdName, zkServer, kwargs, null, authInfo, request, true); } /** * Run the registered command with name cmdName. Commands should not produce * any exceptions; any (anticipated) errors should be reported in the * "error" entry of the returned map. Likewise, if no command with the given * name is registered, this will be noted in the "error" entry. * * @param cmdName * @param zkServer * @param inputStream InputStream from HTTP POST request * @return Map representing response to command containing at minimum: * - "command" key containing the command's primary name * - "error" key containing a String error message or null if no error */ public static CommandResponse runPostCommand( String cmdName, ZooKeeperServer zkServer, InputStream inputStream, String authInfo, HttpServletRequest request) { return runCommand(cmdName, zkServer, null, inputStream, authInfo, request, false); } private static CommandResponse runCommand( String cmdName, ZooKeeperServer zkServer, Map kwargs, InputStream inputStream, String authInfo, HttpServletRequest request, boolean isGet) { Command command = getCommand(cmdName); if (command == null) { // set the status code to 200 to keep the current behavior of existing commands LOG.warn("Unknown command"); return new CommandResponse(cmdName, "Unknown command: " + cmdName, HttpServletResponse.SC_OK); } if (command.isServerRequired() && (zkServer == null || !zkServer.isRunning())) { // set the status code to 200 to keep the current behavior of existing commands LOG.warn("This ZooKeeper instance is not currently serving requests for command"); return new CommandResponse(cmdName, "This ZooKeeper instance is not currently serving requests", HttpServletResponse.SC_OK); } final AuthRequest authRequest = command.getAuthRequest(); if (authRequest != null) { if (authInfo == null) { LOG.warn("Auth info is missing for command"); return new CommandResponse(cmdName, "Auth info is missing for the command", HttpServletResponse.SC_UNAUTHORIZED); } try { final List ids = handleAuthentication(request, authInfo); handleAuthorization(zkServer, ids, authRequest.getPermission(), authRequest.getPath()); } catch (final KeeperException.AuthFailedException e) { return new CommandResponse(cmdName, "Not authenticated", HttpServletResponse.SC_UNAUTHORIZED); } catch (final KeeperException.NoAuthException e) { return new CommandResponse(cmdName, "Not authorized", HttpServletResponse.SC_FORBIDDEN); } catch (final Exception e) { LOG.warn("Error occurred during auth for command", e); return new CommandResponse(cmdName, "Error occurred during auth", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } return isGet ? command.runGet(zkServer, kwargs) : command.runPost(zkServer, inputStream); } private static List handleAuthentication(final HttpServletRequest request, final String authInfo) throws KeeperException.AuthFailedException { final String[] authData = authInfo.split(AUTH_INFO_SEPARATOR); // for IP and x509, auth info only contains the schema and Auth Id will be extracted from HTTP request if (authData.length != 1 && authData.length != 2) { LOG.warn("Invalid auth info length"); throw new KeeperException.AuthFailedException(); } final String schema = authData[0]; final ServerAuthenticationProvider authProvider = ProviderRegistry.getServerProvider(schema); if (authProvider != null) { try { final byte[] auth = authData.length == 2 ? authData[1].getBytes(StandardCharsets.UTF_8) : null; final List ids = authProvider.handleAuthentication(request, auth); if (ids.isEmpty()) { LOG.warn("Auth Id list is empty"); throw new KeeperException.AuthFailedException(); } return ids; } catch (final RuntimeException e) { LOG.warn("Caught runtime exception from AuthenticationProvider", e); throw new KeeperException.AuthFailedException(); } } else { LOG.warn("Auth provider not found for schema"); throw new KeeperException.AuthFailedException(); } } /** * Grant or deny authorization for a command by matching * request-provided credentials with the ACLs present on a node. * * @param zkServer the ZooKeeper server object. * @param ids the credentials extracted from the Authorization header. * @param perm the set of permission bits required by the command. * @param path the ZooKeeper node path whose ACLs should be used * to satisfy the perm bits. * @throws KeeperException.NoAuthException if one or more perm * bits could not be satisfied. */ private static void handleAuthorization(final ZooKeeperServer zkServer, final List ids, final int perm, final String path) throws KeeperException.NoNodeException, KeeperException.NoAuthException { final DataNode dataNode = zkServer.getZKDatabase().getNode(path); if (dataNode == null) { throw new KeeperException.NoNodeException(path); } final List acls = zkServer.getZKDatabase().aclForNode(dataNode); // Check the individual bits of perm. final int bitWidth = Integer.SIZE - Integer.numberOfLeadingZeros(perm); for (int b = 0; b < bitWidth; b++) { final int permBit = 1 << b; if ((perm & permBit) != 0) { zkServer.checkACL(null, acls, permBit, ids, path, null); } } } /** * Returns the primary names of all registered commands. */ public static Set getPrimaryNames() { return primaryNames; } /** * Returns the commands registered under cmdName with registerCommand, or * null if no command is registered with that name. */ public static Command getCommand(String cmdName) { return commands.get(cmdName); } static { registerCommand(new CnxnStatResetCommand()); registerCommand(new ConfCommand()); registerCommand(new ConsCommand()); registerCommand(new DigestCommand()); registerCommand(new DirsCommand()); registerCommand(new DumpCommand()); registerCommand(new EnvCommand()); registerCommand(new GetTraceMaskCommand()); registerCommand(new InitialConfigurationCommand()); registerCommand(new IsroCommand()); registerCommand(new LastSnapshotCommand()); registerCommand(new LeaderCommand()); registerCommand(new MonitorCommand()); registerCommand(new ObserverCnxnStatResetCommand()); registerCommand(new RestoreCommand()); registerCommand(new RuokCommand()); registerCommand(new SetTraceMaskCommand()); registerCommand(new SnapshotCommand()); registerCommand(new SrvrCommand()); registerCommand(new StatCommand()); registerCommand(new StatResetCommand()); registerCommand(new SyncedObserverConsCommand()); registerCommand(new SystemPropertiesCommand()); registerCommand(new VotingViewCommand()); registerCommand(new WatchCommand()); registerCommand(new WatchesByPathCommand()); registerCommand(new WatchSummaryCommand()); registerCommand(new ZabStateCommand()); } /** * Reset all connection statistics. */ public static class CnxnStatResetCommand extends GetCommand { public CnxnStatResetCommand() { super(Arrays.asList("connection_stat_reset", "crst")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); zkServer.getServerCnxnFactory().resetAllConnectionStats(); return response; } } /** * Server configuration parameters. * @see ZooKeeperServer#getConf() */ public static class ConfCommand extends GetCommand { public ConfCommand() { super(Arrays.asList("configuration", "conf", "config")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.putAll(zkServer.getConf().toMap()); return response; } } /** * Information on client connections to server. Returned Map contains: * - "connections": list of connection info objects * @see org.apache.zookeeper.server.ServerCnxn#getConnectionInfo(boolean) */ public static class ConsCommand extends GetCommand { public ConsCommand() { super(Arrays.asList("connections", "cons")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); ServerCnxnFactory serverCnxnFactory = zkServer.getServerCnxnFactory(); if (serverCnxnFactory != null) { response.put("connections", serverCnxnFactory.getAllConnectionInfo(false)); } else { response.put("connections", Collections.emptyList()); } ServerCnxnFactory secureServerCnxnFactory = zkServer.getSecureServerCnxnFactory(); if (secureServerCnxnFactory != null) { response.put("secure_connections", secureServerCnxnFactory.getAllConnectionInfo(false)); } else { response.put("secure_connections", Collections.emptyList()); } return response; } } /** * Information on ZK datadir and snapdir size in bytes */ public static class DirsCommand extends GetCommand { public DirsCommand() { super(Arrays.asList("dirs")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.put("datadir_size", zkServer.getDataDirSize()); response.put("logdir_size", zkServer.getLogDirSize()); return response; } } /** * Information on session expirations and ephemerals. Returned map contains: * - "expiry_time_to_session_ids": Map<Long, Set<Long>> * time -> sessions IDs of sessions that expire at time * - "session_id_to_ephemeral_paths": Map<Long, Set<String>> * session ID -> ephemeral paths created by that session * @see ZooKeeperServer#getSessionExpiryMap() * @see ZooKeeperServer#getEphemerals() */ public static class DumpCommand extends GetCommand { public DumpCommand() { super(Arrays.asList("dump")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.put("expiry_time_to_session_ids", zkServer.getSessionExpiryMap()); response.put("session_id_to_ephemeral_paths", zkServer.getEphemerals()); return response; } } /** * All defined environment variables. */ public static class EnvCommand extends GetCommand { public EnvCommand() { super(Arrays.asList("environment", "env", "envi"), false); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); for (Entry e : Environment.list()) { response.put(e.getKey(), e.getValue()); } return response; } } /** * Digest histories for every specific number of txns. */ public static class DigestCommand extends GetCommand { public DigestCommand() { super(Arrays.asList("hash")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.put("digests", zkServer.getZKDatabase().getDataTree().getDigestLog()); return response; } } /** * The current trace mask. Returned map contains: * - "tracemask": Long */ public static class GetTraceMaskCommand extends GetCommand { public GetTraceMaskCommand() { super(Arrays.asList("get_trace_mask", "gtmk"), false); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.put("tracemask", ZooTrace.getTextTraceLevel()); return response; } } public static class InitialConfigurationCommand extends GetCommand { public InitialConfigurationCommand() { super(Arrays.asList("initial_configuration", "icfg")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.put("initial_configuration", zkServer.getInitialConfig()); return response; } } /** * Is this server in read-only mode. Returned map contains: * - "is_read_only": Boolean */ public static class IsroCommand extends GetCommand { public IsroCommand() { super(Arrays.asList("is_read_only", "isro")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); response.put("read_only", zkServer instanceof ReadOnlyZooKeeperServer); return response; } } /** * Command returns information of the last snapshot that zookeeper server * has finished saving to disk. During the time between the server starts up * and it finishes saving its first snapshot, the command returns the zxid * and last modified time of the snapshot file used for restoration at * server startup. Returned map contains: * - "zxid": String * - "timestamp": Long */ public static class LastSnapshotCommand extends GetCommand { public LastSnapshotCommand() { super(Arrays.asList("last_snapshot", "lsnp")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); SnapshotInfo info = zkServer.getTxnLogFactory().getLastSnapshotInfo(); response.put("zxid", Long.toHexString(info == null ? -1L : info.zxid)); response.put("timestamp", info == null ? -1L : info.timestamp); return response; } } /** * Returns the leader status of this instance and the leader host string. */ public static class LeaderCommand extends GetCommand { public LeaderCommand() { super(Arrays.asList("leader", "lead")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); if (zkServer instanceof QuorumZooKeeperServer) { response.put("is_leader", zkServer instanceof LeaderZooKeeperServer); QuorumPeer peer = ((QuorumZooKeeperServer) zkServer).self; response.put("leader_id", peer.getLeaderId()); String leaderAddress = peer.getLeaderAddress(); response.put("leader_ip", leaderAddress != null ? leaderAddress : ""); } else { response.put("error", "server is not initialized"); } return response; } } /** * Some useful info for monitoring. Returned map contains: * - "version": String * server version * - "avg_latency": Long * - "max_latency": Long * - "min_latency": Long * - "packets_received": Long * - "packets_sents": Long * - "num_alive_connections": Integer * - "outstanding_requests": Long * number of unprocessed requests * - "server_state": "leader", "follower", or "standalone" * - "znode_count": Integer * - "watch_count": Integer * - "ephemerals_count": Integer * - "approximate_data_size": Long * - "open_file_descriptor_count": Long (unix only) * - "max_file_descriptor_count": Long (unix only) * - "fsync_threshold_exceed_count": Long * - "non_mtls_conn_count": Long * - "non_mtls_remote_conn_count": Long * - "non_mtls_local_conn_count": Long * - "followers": Integer (leader only) * - "synced_followers": Integer (leader only) * - "pending_syncs": Integer (leader only) */ public static class MonitorCommand extends GetCommand { public MonitorCommand() { super(Arrays.asList("monitor", "mntr"), false); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); zkServer.dumpMonitorValues(response::put); ServerMetrics.getMetrics().getMetricsProvider().dump(response::put); return response; } } /** * Reset all observer connection statistics. */ public static class ObserverCnxnStatResetCommand extends GetCommand { public ObserverCnxnStatResetCommand() { super(Arrays.asList("observer_connection_stat_reset", "orst")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); if (zkServer instanceof LeaderZooKeeperServer) { Leader leader = ((LeaderZooKeeperServer) zkServer).getLeader(); leader.resetObserverConnectionStats(); } else if (zkServer instanceof FollowerZooKeeperServer) { Follower follower = ((FollowerZooKeeperServer) zkServer).getFollower(); follower.resetObserverConnectionStats(); } return response; } } /** * Restore from snapshot on the current server. * * Returned map contains: * - "last_zxid": String */ public static class RestoreCommand extends PostCommand { static final String RESPONSE_DATA_LAST_ZXID = "last_zxid"; static final String ADMIN_RESTORE_ENABLED = "zookeeper.admin.restore.enabled"; private RateLimiter rateLimiter; public RestoreCommand() { super(Arrays.asList("restore", "rest"), true, new AuthRequest(ZooDefs.Perms.ALL, ROOT_PATH)); rateLimiter = new RateLimiter(1, rateLimiterInterval, TimeUnit.MILLISECONDS); } @Override public CommandResponse runPost(final ZooKeeperServer zkServer, final InputStream inputStream) { final CommandResponse response = initializeResponse(); // check feature flag final boolean restoreEnabled = Boolean.parseBoolean(System.getProperty(ADMIN_RESTORE_ENABLED, "true")); if (!restoreEnabled) { response.setStatusCode(HttpServletResponse.SC_SERVICE_UNAVAILABLE); LOG.warn("Restore command is disabled"); return response; } if (!zkServer.isSerializeLastProcessedZxidEnabled()) { response.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); LOG.warn("Restore command requires serializeLastProcessedZxidEnable flag is set to true"); return response; } if (inputStream == null){ response.setStatusCode(HttpServletResponse.SC_BAD_REQUEST); LOG.warn("InputStream from restore request is null"); return response; } // check rate limiting if (!rateLimiter.allow()) { response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS_429); ServerMetrics.getMetrics().RESTORE_RATE_LIMITED_COUNT.add(1); LOG.warn("Restore request was rate limited"); return response; } // restore from snapshot InputStream try { final long lastZxid = zkServer.restoreFromSnapshot(inputStream); response.put(RESPONSE_DATA_LAST_ZXID, lastZxid); } catch (final Exception e) { response.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); ServerMetrics.getMetrics().RESTORE_ERROR_COUNT.add(1); LOG.warn("Exception occurred when restore snapshot via the restore command", e); } return response; } } /** * No-op command, check if the server is running */ public static class RuokCommand extends GetCommand { public RuokCommand() { super(Arrays.asList("ruok")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { return initializeResponse(); } } /** * Sets the trace mask. Required arguments: * - "traceMask": Long * Returned Map contains: * - "tracemask": Long */ public static class SetTraceMaskCommand extends GetCommand { public SetTraceMaskCommand() { super(Arrays.asList("set_trace_mask", "stmk"), false); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); long traceMask; if (!kwargs.containsKey("traceMask")) { response.put("error", "setTraceMask requires long traceMask argument"); return response; } try { traceMask = Long.parseLong(kwargs.get("traceMask")); } catch (NumberFormatException e) { response.put("error", "setTraceMask requires long traceMask argument, got " + kwargs.get("traceMask")); return response; } ZooTrace.setTextTraceLevel(traceMask); response.put("tracemask", traceMask); return response; } } /** * Take a snapshot of current server and stream out the data. * * Argument: * - "streaming": optional String to indicate whether streaming out data * * Returned snapshot as stream if streaming is true and metadata of the snapshot * - "last_zxid": String * - "snapshot_size": String */ public static class SnapshotCommand extends GetCommand { static final String REQUEST_QUERY_PARAM_STREAMING = "streaming"; static final String RESPONSE_HEADER_LAST_ZXID = "last_zxid"; static final String RESPONSE_HEADER_SNAPSHOT_SIZE = "snapshot_size"; static final String ADMIN_SNAPSHOT_ENABLED = "zookeeper.admin.snapshot.enabled"; private final RateLimiter rateLimiter; public SnapshotCommand() { super(Arrays.asList("snapshot", "snap"), true, new AuthRequest(ZooDefs.Perms.ALL, ROOT_PATH)); rateLimiter = new RateLimiter(1, rateLimiterInterval, TimeUnit.MICROSECONDS); } @SuppressFBWarnings(value = "OBL_UNSATISFIED_OBLIGATION", justification = "FileInputStream is passed to CommandResponse and closed in StreamOutputter") @Override public CommandResponse runGet(final ZooKeeperServer zkServer, final Map kwargs) { final CommandResponse response = initializeResponse(); // check feature flag final boolean snapshotEnabled = Boolean.parseBoolean(System.getProperty(ADMIN_SNAPSHOT_ENABLED, "true")); if (!snapshotEnabled) { response.setStatusCode(HttpServletResponse.SC_SERVICE_UNAVAILABLE); LOG.warn("Snapshot command is disabled"); return response; } if (!zkServer.isSerializeLastProcessedZxidEnabled()) { response.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); LOG.warn("Snapshot command requires serializeLastProcessedZxidEnable flag is set to true"); return response; } // check rate limiting if (!rateLimiter.allow()) { response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS_429); ServerMetrics.getMetrics().SNAPSHOT_RATE_LIMITED_COUNT.add(1); LOG.warn("Snapshot request was rate limited"); return response; } // check the streaming query param boolean streaming = true; if (kwargs.containsKey(REQUEST_QUERY_PARAM_STREAMING)) { streaming = Boolean.parseBoolean(kwargs.get(REQUEST_QUERY_PARAM_STREAMING)); } // take snapshot and stream out data if needed try { final File snapshotFile = zkServer.takeSnapshot(false, false); final long lastZxid = Util.getZxidFromName(snapshotFile.getName(), SNAPSHOT_FILE_PREFIX); response.addHeader(RESPONSE_HEADER_LAST_ZXID, "0x" + ZxidUtils.zxidToString(lastZxid)); final long size = snapshotFile.length(); response.addHeader(RESPONSE_HEADER_SNAPSHOT_SIZE, String.valueOf(size)); if (size == 0) { response.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); ServerMetrics.getMetrics().SNAPSHOT_ERROR_COUNT.add(1); LOG.warn("Snapshot file {} is empty", snapshotFile); } else if (streaming) { response.setInputStream(new FileInputStream(snapshotFile)); } } catch (final Exception e) { response.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); ServerMetrics.getMetrics().SNAPSHOT_ERROR_COUNT.add(1); LOG.warn("Exception occurred when taking the snapshot via the snapshot admin command", e); } return response; } } /** * Server information. Returned map contains: * - "version": String * version of server * - "read_only": Boolean * is server in read-only mode * - "server_stats": ServerStats object * - "node_count": Integer */ public static class SrvrCommand extends GetCommand { public SrvrCommand() { super(Arrays.asList("server_stats", "srvr")); } // Allow subclasses (e.g. StatCommand) to specify their own names protected SrvrCommand(List names) { super(names); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); LOG.info("running stat"); response.put("version", Version.getFullVersion()); response.put("read_only", zkServer instanceof ReadOnlyZooKeeperServer); response.put("server_stats", zkServer.serverStats()); response.put("client_response", zkServer.serverStats().getClientResponseStats()); if (zkServer instanceof LeaderZooKeeperServer) { Leader leader = ((LeaderZooKeeperServer) zkServer).getLeader(); response.put("proposal_stats", leader.getProposalStats()); } response.put("node_count", zkServer.getZKDatabase().getNodeCount()); return response; } } /** * Same as SrvrCommand but has extra "connections" entry. */ public static class StatCommand extends SrvrCommand { public StatCommand() { super(Arrays.asList("stats", "stat")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = super.runGet(zkServer, kwargs); final Iterable> connections; if (zkServer.getServerCnxnFactory() != null) { connections = zkServer.getServerCnxnFactory().getAllConnectionInfo(true); } else { connections = Collections.emptyList(); } response.put("connections", connections); final Iterable> secureConnections; if (zkServer.getSecureServerCnxnFactory() != null) { secureConnections = zkServer.getSecureServerCnxnFactory().getAllConnectionInfo(true); } else { secureConnections = Collections.emptyList(); } response.put("secure_connections", secureConnections); return response; } } /** * Resets server statistics. */ public static class StatResetCommand extends GetCommand { public StatResetCommand() { super(Arrays.asList("stat_reset", "srst")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); zkServer.serverStats().reset(); return response; } } /** * Information on observer connections to server. Returned Map contains: * - "synced_observers": Integer (leader/follower only) * - "observers": list of observer learner handler info objects (leader/follower only) * @see org.apache.zookeeper.server.quorum.LearnerHandler#getLearnerHandlerInfo() */ public static class SyncedObserverConsCommand extends GetCommand { public SyncedObserverConsCommand() { super(Arrays.asList("observers", "obsr")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); if (zkServer instanceof LeaderZooKeeperServer) { Leader leader = ((LeaderZooKeeperServer) zkServer).getLeader(); response.put("synced_observers", leader.getObservingLearners().size()); response.put("observers", leader.getObservingLearnersInfo()); return response; } else if (zkServer instanceof FollowerZooKeeperServer) { Follower follower = ((FollowerZooKeeperServer) zkServer).getFollower(); Integer syncedObservers = follower.getSyncedObserverSize(); if (syncedObservers != null) { response.put("synced_observers", syncedObservers); response.put("observers", follower.getSyncedObserversInfo()); return response; } } response.put("synced_observers", 0); response.put("observers", Collections.emptySet()); return response; } } /** * All defined system properties. */ public static class SystemPropertiesCommand extends GetCommand { public SystemPropertiesCommand() { super(Arrays.asList("system_properties", "sysp"), false); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); Properties systemProperties = System.getProperties(); SortedMap sortedSystemProperties = new TreeMap<>(); systemProperties.forEach((k, v) -> sortedSystemProperties.put(k.toString(), v.toString())); response.putAll(sortedSystemProperties); return response; } } /** * Returns the current ensemble configuration information. * It provides list of current voting members in the ensemble. */ public static class VotingViewCommand extends GetCommand { public VotingViewCommand() { super(Arrays.asList("voting_view")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); if (zkServer instanceof QuorumZooKeeperServer) { QuorumPeer peer = ((QuorumZooKeeperServer) zkServer).self; Map votingView = peer.getVotingView().entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> new QuorumServerView(e.getValue()))); response.put("current_config", votingView); } else { response.put("current_config", Collections.emptyMap()); } return response; } @SuppressFBWarnings(value = "URF_UNREAD_FIELD", justification = "class is used only for JSON serialization") private static class QuorumServerView { @JsonProperty private List serverAddresses; @JsonProperty private List electionAddresses; @JsonProperty private String clientAddress; @JsonProperty private String learnerType; public QuorumServerView(QuorumPeer.QuorumServer quorumServer) { this.serverAddresses = getMultiAddressString(quorumServer.addr); this.electionAddresses = getMultiAddressString(quorumServer.electionAddr); this.learnerType = quorumServer.type.equals(LearnerType.PARTICIPANT) ? "participant" : "observer"; this.clientAddress = getAddressString(quorumServer.clientAddr); } private static List getMultiAddressString(MultipleAddresses multipleAddresses) { if (multipleAddresses == null) { return Collections.emptyList(); } return multipleAddresses.getAllAddresses().stream() .map(QuorumServerView::getAddressString) .collect(Collectors.toList()); } private static String getAddressString(InetSocketAddress address) { if (address == null) { return ""; } return String.format("%s:%d", QuorumPeer.QuorumServer.delimitedHostString(address), address.getPort()); } } } /** * Watch information aggregated by session. Returned Map contains: * - "session_id_to_watched_paths": Map<Long, Set<String>> session ID -> watched paths * @see DataTree#getWatches() * @see DataTree#getWatches() */ public static class WatchCommand extends GetCommand { public WatchCommand() { super(Arrays.asList("watches", "wchc")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { DataTree dt = zkServer.getZKDatabase().getDataTree(); CommandResponse response = initializeResponse(); response.put("session_id_to_watched_paths", dt.getWatches().toMap()); return response; } } /** * Watch information aggregated by path. Returned Map contains: * - "path_to_session_ids": Map<String, Set<Long>> path -> session IDs of sessions watching path * @see DataTree#getWatchesByPath() */ public static class WatchesByPathCommand extends GetCommand { public WatchesByPathCommand() { super(Arrays.asList("watches_by_path", "wchp")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { DataTree dt = zkServer.getZKDatabase().getDataTree(); CommandResponse response = initializeResponse(); response.put("path_to_session_ids", dt.getWatchesByPath().toMap()); return response; } } /** * Summarized watch information. * @see DataTree#getWatchesSummary() */ public static class WatchSummaryCommand extends GetCommand { public WatchSummaryCommand() { super(Arrays.asList("watch_summary", "wchs")); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { DataTree dt = zkServer.getZKDatabase().getDataTree(); CommandResponse response = initializeResponse(); response.putAll(dt.getWatchesSummary().toMap()); return response; } } /** * Returns the current phase of Zab protocol that peer is running. * It can be in one of these phases: ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST */ public static class ZabStateCommand extends GetCommand { public ZabStateCommand() { super(Arrays.asList("zabstate"), false); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { CommandResponse response = initializeResponse(); if (zkServer instanceof QuorumZooKeeperServer) { QuorumPeer peer = ((QuorumZooKeeperServer) zkServer).self; QuorumPeer.ZabState zabState = peer.getZabState(); QuorumVerifier qv = peer.getQuorumVerifier(); QuorumPeer.QuorumServer voter = qv.getVotingMembers().get(peer.getMyId()); boolean voting = ( voter != null && voter.addr.equals(peer.getQuorumAddress()) && voter.electionAddr.equals(peer.getElectionAddress()) ); response.put("myid", zkServer.getConf().getServerId()); response.put("is_leader", zkServer instanceof LeaderZooKeeperServer); response.put("quorum_address", peer.getQuorumAddress()); response.put("election_address", peer.getElectionAddress()); response.put("client_address", peer.getClientAddress()); response.put("voting", voting); long lastProcessedZxid = zkServer.getZKDatabase().getDataTreeLastProcessedZxid(); response.put("last_zxid", "0x" + ZxidUtils.zxidToString(lastProcessedZxid)); response.put("zab_epoch", ZxidUtils.getEpochFromZxid(lastProcessedZxid)); response.put("zab_counter", ZxidUtils.getCounterFromZxid(lastProcessedZxid)); response.put("zabstate", zabState.name().toLowerCase()); } else { response.put("voting", false); response.put("zabstate", ""); } return response; } } private Commands() { } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000167 15051152474 032662 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/DummyAdminServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/DummyAdminSe0100644 0000000 0000000 00000002655 15051152474 034157 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import org.apache.zookeeper.server.ZooKeeperServer; /** * An AdminServer that does nothing. * * We use this class when we wish to disable the AdminServer. (This way we only * have to consider whether the server is enabled when we create the * AdminServer, which is handled by AdminServerFactory.) */ public class DummyAdminServer implements AdminServer { @Override public void start() throws AdminServerException { } @Override public void shutdown() throws AdminServerException { } @Override public void setZooKeeperServer(ZooKeeperServer zkServer) { } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000161 15051152474 032654 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/GetCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/GetCommand.j0100644 0000000 0000000 00000003005 15051152474 034057 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.InputStream; import java.util.List; import org.apache.zookeeper.server.ZooKeeperServer; /** * Command that represents HTTP GET request */ public abstract class GetCommand extends CommandBase { protected GetCommand(List names) { super(names); } protected GetCommand(List names, boolean serverRequired) { super(names, serverRequired); } protected GetCommand(List names, boolean serverRequired, AuthRequest authRequest) { super(names, serverRequired, authRequest); } @Override public CommandResponse runPost(ZooKeeperServer zkServer, InputStream inputStream) { return null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000167 15051152474 032662 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JettyAdminServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JettyAdminSe0100644 0000000 0000000 00000041316 15051152474 034160 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.common.QuorumX509Util; import org.apache.zookeeper.common.SecretUtils; import org.apache.zookeeper.common.X509Util; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.auth.IPAuthenticationProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class encapsulates a Jetty server for running Commands. * * Given the default settings, start a ZooKeeper server and visit * http://hostname:8080/commands for links to all registered commands. Visiting * http://hostname:8080/commands/commandname will execute the associated * Command and return the result in the body of the response. Any keyword * arguments to the command are specified with URL parameters (e.g., * http://localhost:8080/commands/set_trace_mask?traceMask=306). * * @see Commands * @see CommandOutputter */ public class JettyAdminServer implements AdminServer { static final Logger LOG = LoggerFactory.getLogger(JettyAdminServer.class); public static final int DEFAULT_PORT = 8080; public static final int DEFAULT_IDLE_TIMEOUT = 30000; public static final String DEFAULT_COMMAND_URL = "/commands"; private static final String DEFAULT_ADDRESS = "0.0.0.0"; public static final int DEFAULT_STS_MAX_AGE = 1 * 24 * 60 * 60; // seconds in a day public static final int DEFAULT_HTTP_VERSION = 11; // based on HttpVersion.java in jetty private final Server server; private final String address; private final int port; private final int idleTimeout; private final String commandUrl; private ZooKeeperServer zkServer; public JettyAdminServer() throws AdminServerException, IOException, GeneralSecurityException { this( System.getProperty("zookeeper.admin.serverAddress", DEFAULT_ADDRESS), Integer.getInteger("zookeeper.admin.serverPort", DEFAULT_PORT), Integer.getInteger("zookeeper.admin.idleTimeout", DEFAULT_IDLE_TIMEOUT), System.getProperty("zookeeper.admin.commandURL", DEFAULT_COMMAND_URL), Integer.getInteger("zookeeper.admin.httpVersion", DEFAULT_HTTP_VERSION), Boolean.getBoolean("zookeeper.admin.portUnification"), Boolean.getBoolean("zookeeper.admin.forceHttps"), Boolean.getBoolean("zookeeper.admin.needClientAuth")); } public JettyAdminServer( String address, int port, int timeout, String commandUrl, int httpVersion, boolean portUnification, boolean forceHttps, boolean needClientAuth) throws IOException, GeneralSecurityException { this.port = port; this.idleTimeout = timeout; this.commandUrl = commandUrl; this.address = address; server = new Server(); ServerConnector connector = null; if (!portUnification && !forceHttps) { connector = new ServerConnector(server); } else { SecureRequestCustomizer customizer = new SecureRequestCustomizer(); customizer.setStsMaxAge(DEFAULT_STS_MAX_AGE); customizer.setStsIncludeSubDomains(true); HttpConfiguration config = new HttpConfiguration(); config.setSecureScheme("https"); config.addCustomizer(customizer); try (QuorumX509Util x509Util = new QuorumX509Util()) { String privateKeyType = System.getProperty(x509Util.getSslKeystoreTypeProperty(), ""); String privateKeyPath = System.getProperty(x509Util.getSslKeystoreLocationProperty(), ""); String privateKeyPassword = getPasswordFromSystemPropertyOrFile( x509Util.getSslKeystorePasswdProperty(), x509Util.getSslKeystorePasswdPathProperty()); String certAuthType = System.getProperty(x509Util.getSslTruststoreTypeProperty(), ""); String certAuthPath = System.getProperty(x509Util.getSslTruststoreLocationProperty(), ""); String certAuthPassword = getPasswordFromSystemPropertyOrFile( x509Util.getSslTruststorePasswdProperty(), x509Util.getSslTruststorePasswdPathProperty()); KeyStore keyStore = null, trustStore = null; try { keyStore = X509Util.loadKeyStore(privateKeyPath, privateKeyPassword, privateKeyType); trustStore = X509Util.loadTrustStore(certAuthPath, certAuthPassword, certAuthType); LOG.info("Successfully loaded private key from {}", privateKeyPath); LOG.info("Successfully loaded certificate authority from {}", certAuthPath); } catch (Exception e) { LOG.error("Failed to load authentication certificates for admin server.", e); throw e; } SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStore(keyStore); sslContextFactory.setKeyStorePassword(privateKeyPassword); sslContextFactory.setTrustStore(trustStore); sslContextFactory.setTrustStorePassword(certAuthPassword); sslContextFactory.setNeedClientAuth(needClientAuth); if (forceHttps) { connector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.fromVersion(httpVersion).asString()), new HttpConnectionFactory(config)); } else { connector = new ServerConnector( server, new UnifiedConnectionFactory(sslContextFactory, HttpVersion.fromVersion(httpVersion).asString()), new HttpConnectionFactory(config)); } } } connector.setHost(address); connector.setPort(port); connector.setIdleTimeout(idleTimeout); server.addConnector(connector); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/*"); constrainTraceMethod(context); server.setHandler(context); context.addServlet(new ServletHolder(new CommandServlet()), commandUrl + "/*"); } /** * Start the embedded Jetty server. */ @Override public void start() throws AdminServerException { try { server.start(); } catch (Exception e) { // Server.start() only throws Exception, so let's at least wrap it // in an identifiable subclass String message = String.format( "Problem starting AdminServer on address %s, port %d and command URL %s", address, port, commandUrl); throw new AdminServerException(message, e); } LOG.info("Started AdminServer on address {}, port {} and command URL {}", address, port, commandUrl); } /** * Stop the embedded Jetty server. * * This is not very important except for tests where multiple * JettyAdminServers are started and may try to bind to the same ports if * previous servers aren't shut down. */ @Override public void shutdown() throws AdminServerException { try { server.stop(); } catch (Exception e) { String message = String.format( "Problem stopping AdminServer on address %s, port %d and command URL %s", address, port, commandUrl); throw new AdminServerException(message, e); } } /** * Set the ZooKeeperServer that will be used to run Commands. * * It is not necessary to set the ZK server before calling * AdminServer.start(), and the ZK server can be set to null when, e.g., * that server is being shut down. If the ZK server is not set or set to * null, the AdminServer will still be able to issue Commands, but they will * return an error until a ZK server is set. */ @Override public void setZooKeeperServer(ZooKeeperServer zkServer) { this.zkServer = zkServer; } private class CommandServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override protected void doGet( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Capture the command name from the URL String cmd = request.getPathInfo(); if (cmd == null || cmd.equals("/")) { // No command specified, print links to all commands instead for (String link : commandLinks()) { response.getWriter().println(link); response.getWriter().println("
    "); } return; } // Strip leading "/" cmd = cmd.substring(1); // Extract keyword arguments to command from request parameters @SuppressWarnings("unchecked") Map parameterMap = request.getParameterMap(); Map kwargs = new HashMap<>(); for (Map.Entry entry : parameterMap.entrySet()) { kwargs.put(entry.getKey(), entry.getValue()[0]); } final String authInfo = request.getHeader(HttpHeader.AUTHORIZATION.asString()); // Run the command final CommandResponse cmdResponse = Commands.runGetCommand(cmd, zkServer, kwargs, authInfo, request); response.setStatus(cmdResponse.getStatusCode()); final Map headers = cmdResponse.getHeaders(); for (final Map.Entry header : headers.entrySet()) { response.addHeader(header.getKey(), header.getValue()); } final String clientIP = IPAuthenticationProvider.getClientIPAddress(request); if (cmdResponse.getInputStream() == null) { // Format and print the output of the command CommandOutputter outputter = new JsonOutputter(clientIP); response.setContentType(outputter.getContentType()); outputter.output(cmdResponse, response.getWriter()); } else { // Stream out the output of the command CommandOutputter outputter = new StreamOutputter(clientIP); response.setContentType(outputter.getContentType()); outputter.output(cmdResponse, response.getOutputStream()); } } /** * Serves HTTP POST requests. It reads request payload as raw data. * It's up to each command to process the payload accordingly. * For example, RestoreCommand uses the payload InputStream directly * to read snapshot data. */ @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String cmdName = extractCommandNameFromURL(request, response); if (cmdName != null) { final String authInfo = request.getHeader(HttpHeader.AUTHORIZATION.asString()); final CommandResponse cmdResponse = Commands.runPostCommand(cmdName, zkServer, request.getInputStream(), authInfo, request); final String clientIP = IPAuthenticationProvider.getClientIPAddress(request); sendJSONResponse(response, cmdResponse, clientIP); } } /** * Extracts the command name from URL if it exists otherwise null */ private String extractCommandNameFromURL(final HttpServletRequest request, final HttpServletResponse response) throws IOException { String cmd = request.getPathInfo(); if (cmd == null || cmd.equals("/")) { printCommandLinks(response); return null; } // Strip leading "/" return cmd.substring(1); } /** * Prints the list of URLs to each registered command as response. */ private void printCommandLinks(final HttpServletResponse response) throws IOException { for (final String link : commandLinks()) { response.getWriter().println(link); response.getWriter().println("
    "); } } /** * Send JSON string as the response. */ private void sendJSONResponse(final HttpServletResponse response, final CommandResponse cmdResponse, final String clientIP) throws IOException { final CommandOutputter outputter = new JsonOutputter(clientIP); response.setStatus(cmdResponse.getStatusCode()); response.setContentType(outputter.getContentType()); outputter.output(cmdResponse, response.getWriter()); } } /** * Returns a list of URLs to each registered Command. */ private List commandLinks() { return Commands.getPrimaryNames().stream().sorted().map(command -> String.format("%s", commandUrl + "/" + command , command)).collect(Collectors.toList()); } /** * Add constraint to a given context to disallow TRACE method * @param ctxHandler the context to modify */ private void constrainTraceMethod(ServletContextHandler ctxHandler) { Constraint c = new Constraint(); c.setAuthenticate(true); ConstraintMapping cmt = new ConstraintMapping(); cmt.setConstraint(c); cmt.setMethod("TRACE"); cmt.setPathSpec("/*"); ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); securityHandler.setConstraintMappings(new ConstraintMapping[] {cmt}); ctxHandler.setSecurityHandler(securityHandler); } /** * Returns the password specified by the given property or stored in the file specified by the * given path property. If both are specified, the password stored in the file will be returned. * @param propertyName the name of the property * @param pathPropertyName the name of the path property * @return password value */ private String getPasswordFromSystemPropertyOrFile(final String propertyName, final String pathPropertyName) { String value = System.getProperty(propertyName, ""); final String pathValue = System.getProperty(pathPropertyName, ""); if (!pathValue.isEmpty()) { value = String.valueOf(SecretUtils.readSecret(pathValue)); } return value; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000164 15051152474 032657 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JsonOutputter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/JsonOutputte0100644 0000000 0000000 00000005145 15051152474 034303 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.SerializationFeature; import java.io.IOException; import java.io.PrintWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JsonOutputter implements CommandOutputter { static final Logger LOG = LoggerFactory.getLogger(JsonOutputter.class); public static final String ERROR_RESPONSE = "{\"error\": \"Exception writing command response to JSON\"}"; private ObjectMapper mapper; private final String clientIP; public JsonOutputter(final String clientIP) { mapper = new ObjectMapper(); mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); this.clientIP = clientIP; } @Override public String getContentType() { return "application/json"; } @Override public void output(CommandResponse response, PrintWriter pw) { try { mapper.writeValue(pw, response.toMap()); } catch (JsonGenerationException e) { LOG.warn("Exception writing command response to JSON:", e); pw.write(ERROR_RESPONSE); } catch (JsonMappingException e) { LOG.warn("Exception writing command response to JSON:", e); pw.write(ERROR_RESPONSE); } catch (IOException e) { LOG.warn("Exception writing command response as JSON to {}", clientIP, e); pw.write(ERROR_RESPONSE); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000162 15051152474 032655 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/PostCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/PostCommand.0100644 0000000 0000000 00000002502 15051152474 034114 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Command that represents HTTP POST request */ package org.apache.zookeeper.server.admin; import java.util.List; import java.util.Map; import org.apache.zookeeper.server.ZooKeeperServer; public abstract class PostCommand extends CommandBase { protected PostCommand(List names, boolean serverRequired, AuthRequest authRequest) { super(names, serverRequired, authRequest); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { return null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000170 15051152474 032654 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/ReadAheadEndpoint.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/ReadAheadEnd0100644 0000000 0000000 00000013646 15051152474 034052 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 code was found and refactored from here: // https://stackoverflow.com/questions/11182192/how-do-i-serve-https-and-http-for-jetty-from-one-port/40076056#40076056 package org.apache.zookeeper.server.admin; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ReadPendingException; import java.nio.channels.WritePendingException; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.Callback; public class ReadAheadEndpoint implements EndPoint { private final EndPoint endPoint; private final ByteBuffer start; private final byte[] bytes; private int leftToRead; private IOException pendingException = null; @Override public InetSocketAddress getLocalAddress() { return endPoint.getLocalAddress(); } @Override public InetSocketAddress getRemoteAddress() { return endPoint.getRemoteAddress(); } @Override public boolean isOpen() { return endPoint.isOpen(); } @Override public long getCreatedTimeStamp() { return endPoint.getCreatedTimeStamp(); } @Override public boolean isOutputShutdown() { return endPoint.isOutputShutdown(); } @Override public boolean isInputShutdown() { return endPoint.isInputShutdown(); } @Override public void shutdownOutput() { endPoint.shutdownOutput(); } @Override public void close() { endPoint.close(); } @Override public Object getTransport() { return endPoint.getTransport(); } @Override public long getIdleTimeout() { return endPoint.getIdleTimeout(); } @Override public Connection getConnection() { return endPoint.getConnection(); } @Override public void onOpen() { endPoint.onOpen(); } @Override public void onClose() { endPoint.onClose(); } @Override public boolean isOptimizedForDirectBuffers() { return endPoint.isOptimizedForDirectBuffers(); } @Override public boolean isFillInterested() { return endPoint.isFillInterested(); } @Override public boolean tryFillInterested(Callback v) { return endPoint.tryFillInterested(v); } @Override public boolean flush(ByteBuffer... v) throws IOException { return endPoint.flush(v); } @Override public void setIdleTimeout(long v) { endPoint.setIdleTimeout(v); } @Override public void write(Callback v, ByteBuffer... b) throws WritePendingException { endPoint.write(v, b); } @Override public void setConnection(Connection v) { endPoint.setConnection(v); } @Override public void upgrade(Connection v) { endPoint.upgrade(v); } @Override public void fillInterested(Callback v) throws ReadPendingException { endPoint.fillInterested(v); } public ReadAheadEndpoint(final EndPoint channel, final int readAheadLength) { if (channel == null) { throw new IllegalArgumentException("channel cannot be null"); } this.endPoint = channel; start = ByteBuffer.wrap(bytes = new byte[readAheadLength]); start.flip(); leftToRead = readAheadLength; } private synchronized void readAhead() throws IOException { if (leftToRead > 0) { int n = 0; do { n = endPoint.fill(start); } while (n == 0 && endPoint.isOpen() && !endPoint.isInputShutdown()); if (n == -1) { leftToRead = -1; } else { leftToRead -= n; } if (leftToRead <= 0) { start.rewind(); } } } private int readFromStart(final ByteBuffer dst) throws IOException { final int n = Math.min(dst.remaining(), start.remaining()); if (n > 0) { dst.put(bytes, start.position(), n); start.position(start.position() + n); dst.flip(); } return n; } @Override public synchronized int fill(final ByteBuffer dst) throws IOException { throwPendingException(); if (leftToRead > 0) { readAhead(); } if (leftToRead > 0) { return 0; } final int sr = start.remaining(); if (sr > 0) { dst.compact(); final int n = readFromStart(dst); if (n < sr) { return n; } } return sr + endPoint.fill(dst); } public byte[] getBytes() { if (pendingException == null) { try { readAhead(); } catch (IOException e) { pendingException = e; } } byte[] ret = new byte[bytes.length]; System.arraycopy(bytes, 0, ret, 0, ret.length); return ret; } private void throwPendingException() throws IOException { if (pendingException != null) { IOException e = pendingException; pendingException = null; throw e; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000166 15051152474 032661 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/StreamOutputter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/StreamOutput0100644 0000000 0000000 00000003407 15051152474 034273 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.zookeeper.common.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A class for streaming data out. */ public class StreamOutputter implements CommandOutputter{ private static final Logger LOG = LoggerFactory.getLogger(StreamOutputter.class); private final String clientIP; public StreamOutputter(final String clientIP) { this.clientIP = clientIP; } @Override public String getContentType() { return "application/octet-stream"; } @Override public void output(final CommandResponse response, final OutputStream os) { try (final InputStream is = response.getInputStream()){ IOUtils.copyBytes(is, os, 1024, true); } catch (final IOException e) { LOG.warn("Exception streaming out data to {}", clientIP, e); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000177 15051152474 032663 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/UnifiedConnectionFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/UnifiedConne0100644 0000000 0000000 00000011424 15051152474 034163 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSession; import org.apache.zookeeper.server.ServerMetrics; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The purpose of this class is to dynamically determine whether to create * a plaintext or SSL connection whenever newConnection() is called. It works * in conjunction with ReadAheadEndpoint to inspect bytes on the incoming * connection. */ public class UnifiedConnectionFactory extends AbstractConnectionFactory { private static final Logger LOG = LoggerFactory.getLogger(UnifiedConnectionFactory.class); private final SslContextFactory sslContextFactory; private final String nextProtocol; public UnifiedConnectionFactory(String nextProtocol) { this(null, nextProtocol); } public UnifiedConnectionFactory(SslContextFactory factory, String nextProtocol) { super("SSL"); this.sslContextFactory = (factory == null) ? new SslContextFactory.Server() : factory; this.nextProtocol = nextProtocol; this.addBean(this.sslContextFactory); } @Override protected void doStart() throws Exception { super.doStart(); SSLEngine engine = this.sslContextFactory.newSSLEngine(); SSLSession session = engine.getSession(); engine.setUseClientMode(false); if (session.getPacketBufferSize() > this.getInputBufferSize()) { this.setInputBufferSize(session.getPacketBufferSize()); } } @Override public Connection newConnection(Connector connector, EndPoint realEndPoint) { ReadAheadEndpoint aheadEndpoint = new ReadAheadEndpoint(realEndPoint, 1); byte[] bytes = aheadEndpoint.getBytes(); boolean isSSL; if (bytes == null || bytes.length == 0) { isSSL = false; LOG.warn("Incoming connection has no data"); } else { byte b = bytes[0]; // TLS first byte is 0x16, let's not support SSLv3 and below isSSL = b == 0x16; // matches SSL detection in NettyServerCnxnFactory.java } LOG.debug(String.format("UnifiedConnectionFactory: newConnection() with SSL = %b", isSSL)); EndPoint plainEndpoint; SslConnection sslConnection; if (isSSL) { SSLEngine engine = this.sslContextFactory.newSSLEngine(aheadEndpoint.getRemoteAddress()); engine.setUseClientMode(false); sslConnection = this.newSslConnection(connector, aheadEndpoint, engine); sslConnection.setRenegotiationAllowed(this.sslContextFactory.isRenegotiationAllowed()); this.configure(sslConnection, connector, aheadEndpoint); plainEndpoint = sslConnection.getDecryptedEndPoint(); } else { sslConnection = null; plainEndpoint = aheadEndpoint; ServerMetrics.getMetrics().INSECURE_ADMIN.add(1); } ConnectionFactory next = connector.getConnectionFactory(nextProtocol); Connection connection = next.newConnection(connector, plainEndpoint); plainEndpoint.setConnection(connection); return (sslConnection == null) ? connection : sslConnection; } protected SslConnection newSslConnection( final Connector connector, final EndPoint endPoint, final SSLEngine engine) { return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine); } @Override public String toString() { return String.format( "%s@%x{%s->%s}", this.getClass().getSimpleName(), this.hashCode(), this.getProtocol(), this.nextProtocol); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000174 15051152474 032701 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/AuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/Authenticatio0100644 0000000 0000000 00000007757 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; /** * This interface is implemented by authentication providers to add new kinds of * authentication schemes to ZooKeeper. */ public interface AuthenticationProvider { /** * The String used to represent this provider. This will correspond to the * scheme field of an Id. * * @return the scheme of this provider. */ String getScheme(); /** * This method is called when a client passes authentication data for this * scheme. The authData is directly from the authentication packet. The * implementor may attach new ids to the authInfo field of cnxn or may use * cnxn to send packets back to the client. * * @param cnxn * the cnxn that received the authentication information. * @param authData * the authentication data received. * @return TODO */ KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData); /** * This method is called when admin server command passes authentication data for this * scheme. * * @param request * the request that contains the authentication information. * @param authData * the authentication data received. * @return Ids * the list of Id. Empty list means not authenticated */ default List handleAuthentication(HttpServletRequest request, byte[] authData) { return new ArrayList<>(); } /** * This method is called to see if the given id matches the given id * expression in the ACL. This allows schemes to use application specific * wild cards. * * @param id * the id to check. * @param aclExpr * the expression to match ids against. * @return true if the id can be matched by the expression. */ boolean matches(String id, String aclExpr); /** * This method is used to check if the authentication done by this provider * should be used to identify the creator of a node. Some ids such as hosts * and ip addresses are rather transient and in general don't really * identify a client even though sometimes they do. * * @return true if this provider identifies creators. */ boolean isAuthenticated(); /** * Validates the syntax of an id. * * @param id * the id to validate. * @return true if id is well formed. */ boolean isValid(String id); /** * id represents the authentication info which is set in server connection. * id may contain both user name as well as password. * This method should be implemented to extract the user name. * * @param id authentication info set by client. * @return String user name */ default String getUserName(String id) { // Most of the authentication providers id contains only user name. return id; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000202 15051152474 032671 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/DigestAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/DigestAuthent0100644 0000000 0000000 00000015066 15051152474 034244 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import static java.nio.charset.StandardCharsets.UTF_8; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DigestAuthenticationProvider implements AuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(DigestAuthenticationProvider.class); private static final String DEFAULT_DIGEST_ALGORITHM = "SHA1"; public static final String DIGEST_ALGORITHM_KEY = "zookeeper.DigestAuthenticationProvider.digestAlg"; private static final String DIGEST_ALGORITHM = System.getProperty(DIGEST_ALGORITHM_KEY, DEFAULT_DIGEST_ALGORITHM); static { try { //sanity check, pre-check the availability of the algorithm to avoid some unexpected exceptions in the runtime generateDigest(DIGEST_ALGORITHM); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("don't support this ACL digest algorithm: " + DIGEST_ALGORITHM + " in the current environment"); } LOG.info("ACL digest algorithm is: {}", DIGEST_ALGORITHM); } private static final String DIGEST_AUTH_ENABLED = "zookeeper.DigestAuthenticationProvider.enabled"; /** specify a command line property with key of * "zookeeper.DigestAuthenticationProvider.superDigest" * and value of "super:<base64encoded(SHA1(password))>" to enable * super user access (i.e. acls disabled) */ private static final String superDigest = System.getProperty("zookeeper.DigestAuthenticationProvider.superDigest"); public static boolean isEnabled() { boolean enabled = Boolean.parseBoolean(System.getProperty(DIGEST_AUTH_ENABLED, "true")); LOG.info("{} = {}", DIGEST_AUTH_ENABLED, enabled); return enabled; } public String getScheme() { return "digest"; } private static String base64Encode(byte[] b) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < b.length; ) { int pad = 0; int v = (b[i++] & 0xff) << 16; if (i < b.length) { v |= (b[i++] & 0xff) << 8; } else { pad++; } if (i < b.length) { v |= (b[i++] & 0xff); } else { pad++; } sb.append(encode(v >> 18)); sb.append(encode(v >> 12)); if (pad < 2) { sb.append(encode(v >> 6)); } else { sb.append('='); } if (pad < 1) { sb.append(encode(v)); } else { sb.append('='); } } return sb.toString(); } private static char encode(int i) { i &= 0x3f; if (i < 26) { return (char) ('A' + i); } if (i < 52) { return (char) ('a' + i - 26); } if (i < 62) { return (char) ('0' + i - 52); } return i == 62 ? '+' : '/'; } public static String generateDigest(String idPassword) throws NoSuchAlgorithmException { String[] parts = idPassword.split(":", 2); byte[] digest = digest(idPassword); return parts[0] + ":" + base64Encode(digest); } // @VisibleForTesting public static byte[] digest(String idPassword) throws NoSuchAlgorithmException { return MessageDigest.getInstance(DIGEST_ALGORITHM).digest(idPassword.getBytes(UTF_8)); } public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { final List ids = handleAuthentication(authData); if (ids.isEmpty()) { return KeeperException.Code.AUTHFAILED; } for (Id id : ids) { cnxn.addAuthInfo(id); } return KeeperException.Code.OK; } @Override public List handleAuthentication(HttpServletRequest request, byte[] authData) { return handleAuthentication(authData); } public boolean isAuthenticated() { return true; } public boolean isValid(String id) { String[] parts = id.split(":"); return parts.length == 2; } public boolean matches(String id, String aclExpr) { return id.equals(aclExpr); } @Override public String getUserName(String id) { /** * format is already enforced in server code. so no need to check it * again, just assume it is in correct format */ return id.split(":")[0]; } /** Call with a single argument of user:pass to generate authdata. * Authdata output can be used when setting superDigest for example. * @param args single argument of user:pass * @throws NoSuchAlgorithmException */ public static void main(String[] args) throws NoSuchAlgorithmException { for (int i = 0; i < args.length; i++) { System.out.println(args[i] + "->" + generateDigest(args[i])); } } private List handleAuthentication(final byte[] authData) { final List ids = new ArrayList<>(); final String id = new String(authData); try { final String digest = generateDigest(id); if (digest.equals(superDigest)) { ids.add(new Id("super", "")); } ids.add(new Id(getScheme(), digest)); } catch (final NoSuchAlgorithmException e) { LOG.error("Missing algorithm", e); } return Collections.unmodifiableList(ids); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000167 15051152474 032703 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/DigestLoginModule.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/DigestLoginMo0100644 0000000 0000000 00000004150 15051152474 034170 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.util.Map; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.spi.LoginModule; public class DigestLoginModule implements LoginModule { private Subject subject; public boolean abort() { return false; } public boolean commit() { return true; } public void initialize( Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { if (options.containsKey("username")) { // Zookeeper client: get username and password from JAAS conf (only used if using DIGEST-MD5). this.subject = subject; String username = (String) options.get("username"); this.subject.getPublicCredentials().add(username); String password = (String) options.get("password"); this.subject.getPrivateCredentials().add(password); } return; } public boolean logout() { return true; } public boolean login() { // Unlike with Krb5LoginModule, we don't do any actual login or credential passing here: authentication to Zookeeper // is done later, through the SASLClient object. return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000204 15051152474 032673 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/EnsembleAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/EnsembleAuthe0100644 0000000 0000000 00000010720 15051152474 034205 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is not a true AuthenticationProvider in the strict sense. it does * handle add auth requests, but rather than authenticate the client, it checks * to make sure that the ensemble name the client intends to connect to * matches the name that the server thinks it belongs to. if the name does not match, * this provider will close the connection. */ public class EnsembleAuthenticationProvider implements AuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(EnsembleAuthenticationProvider.class); public static final String ENSEMBLE_PROPERTY = "zookeeper.ensembleAuthName"; private static final int MIN_LOGGING_INTERVAL_MS = 1000; private Set ensembleNames; public EnsembleAuthenticationProvider() { String namesCSV = System.getProperty(ENSEMBLE_PROPERTY); if (namesCSV != null) { LOG.info("Set expected ensemble names to {}", namesCSV); setEnsembleNames(namesCSV); } } public void setEnsembleNames(String namesCSV) { ensembleNames = new HashSet<>(); for (String name : namesCSV.split(",")) { ensembleNames.add(name.trim()); } } /* provider methods */ @Override public String getScheme() { return "ensemble"; } /** * if things go bad, we don't want to freak out with the logging, so track * the last time we logged something here. */ private long lastFailureLogged; @Override public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { if (authData == null || authData.length == 0) { ServerMetrics.getMetrics().ENSEMBLE_AUTH_SKIP.add(1); return KeeperException.Code.OK; } String receivedEnsembleName = new String(authData, StandardCharsets.UTF_8); if (ensembleNames == null) { ServerMetrics.getMetrics().ENSEMBLE_AUTH_SKIP.add(1); return KeeperException.Code.OK; } if (ensembleNames.contains(receivedEnsembleName)) { ServerMetrics.getMetrics().ENSEMBLE_AUTH_SUCCESS.add(1); return KeeperException.Code.OK; } long currentTime = System.currentTimeMillis(); if (lastFailureLogged + MIN_LOGGING_INTERVAL_MS < currentTime) { String id = cnxn.getRemoteSocketAddress().getAddress().getHostAddress(); LOG.warn("Unexpected ensemble name: ensemble name: {} client ip: {}", receivedEnsembleName, id); lastFailureLogged = currentTime; } /* * we are doing a close here rather than returning some other error * since we want the client to choose another server to connect to. if * we return an error, the client will get a fatal auth error and * shutdown. */ ServerMetrics.getMetrics().ENSEMBLE_AUTH_FAIL.add(1); cnxn.close(ServerCnxn.DisconnectReason.FAILED_HANDSHAKE); return KeeperException.Code.BADARGUMENTS; } /* * since we aren't a true provider we return false for everything so that * it isn't used in ACLs. */ @Override public boolean matches(String id, String aclExpr) { return false; } @Override public boolean isAuthenticated() { return false; } @Override public boolean isValid(String id) { return false; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000176 15051152474 032703 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/IPAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/IPAuthenticat0100644 0000000 0000000 00000023042 15051152474 034167 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class IPAuthenticationProvider implements AuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(IPAuthenticationProvider.class); public static final String X_FORWARDED_FOR_HEADER_NAME = "X-Forwarded-For"; public static final String USE_X_FORWARDED_FOR_KEY = "zookeeper.IPAuthenticationProvider.usexforwardedfor"; private static final int IPV6_BYTE_LENGTH = 16; // IPv6 address is 128 bits = 16 bytes private static final int IPV6_SEGMENT_COUNT = 8; // IPv6 address has 8 segments private static final int IPV6_SEGMENT_BYTE_LENGTH = 2; // Each segment has up to two bytes private static final int IPV6_SEGMENT_HEX_LENGTH = 4; // Each segment has up to 4 hex digits private static final Pattern IPV6_PATTERN = Pattern.compile(":"); private static final Pattern IPV4_PATTERN = Pattern.compile("\\."); public String getScheme() { return "ip"; } public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { String id = cnxn.getRemoteSocketAddress().getAddress().getHostAddress(); cnxn.addAuthInfo(new Id(getScheme(), id)); return KeeperException.Code.OK; } @Override public List handleAuthentication(HttpServletRequest request, byte[] authData) { final List ids = new ArrayList<>(); final String ip = getClientIPAddress(request); ids.add(new Id(getScheme(), ip)); return Collections.unmodifiableList(ids); } // This is a bit weird but we need to return the address and the number of // bytes (to distinguish between IPv4 and IPv6 private byte[] addr2Bytes(String addr) { if (IPV6_PATTERN.matcher(addr).find()) { return v6addr2Bytes(addr); } else if (IPV4_PATTERN.matcher(addr).find()) { return v4addr2Bytes(addr); } else { LOG.warn("Input string does not resemble an IPv4 or IPv6 address: {}", addr); return null; } } private byte[] v4addr2Bytes(String addr) { String[] parts = addr.split("\\.", -1); if (parts.length != 4) { return null; } byte[] b = new byte[4]; for (int i = 0; i < 4; i++) { try { int v = Integer.parseInt(parts[i]); if (v >= 0 && v <= 255) { b[i] = (byte) v; } else { return null; } } catch (NumberFormatException e) { return null; } } return b; } /** * Validates an IPv6 address string and converts it into a byte array. * * @param ipv6Addr The IPv6 address string to validate. * @return A byte array representing the IPv6 address if valid, or null if the address * is invalid or cannot be parsed. */ static byte[] v6addr2Bytes(String ipv6Addr) { try { return parseV6addr(ipv6Addr); } catch (IllegalArgumentException e) { LOG.warn("Fail to parse {} as IPv6 address: {}", ipv6Addr, e.getMessage()); return null; } } static byte[] parseV6addr(String ipv6Addr) { // Split the address by "::" to handle zero compression, -1 to keep trailing empty strings String[] parts = ipv6Addr.split("::", -1); String[] segments1 = new String[0]; String[] segments2 = new String[0]; // Case 1: No "::" (full address) if (parts.length == 1) { segments1 = parts[0].split(":", -1); if (segments1.length != IPV6_SEGMENT_COUNT) { String reason = "wrong number of segments"; throw new IllegalArgumentException(reason); } } else if (parts.length == 2) { // Case 2: "::" is present // Handle cases like "::1" or "1::" if (!parts[0].isEmpty()) { segments1 = parts[0].split(":", -1); } if (!parts[1].isEmpty()) { segments2 = parts[1].split(":", -1); } // Check if the total number of explicit segments exceeds 8 if (segments1.length + segments2.length >= IPV6_SEGMENT_COUNT) { String reason = "too many segments"; throw new IllegalArgumentException(reason); } } else { // Case 3: Invalid number of parts after splitting by "::" (should be 1 or 2) String reason = "too many '::'"; throw new IllegalArgumentException(reason); } byte[] result = new byte[IPV6_BYTE_LENGTH]; // Process segments before "::" parseV6Segment(result, 0, segments1); // Process segments after "::" parseV6Segment(result, IPV6_BYTE_LENGTH - segments2.length * IPV6_SEGMENT_BYTE_LENGTH, segments2); return result; } private static void parseV6Segment(byte[] addr, int i, String[] segments) { for (String segment : segments) { if (segment.isEmpty()) { throw new IllegalArgumentException("empty segment"); } else if (segment.length() > IPV6_SEGMENT_HEX_LENGTH) { throw new IllegalArgumentException("segment too long"); } try { int value = Integer.parseInt(segment, 16); addr[i++] = (byte) ((value >> 8) & 0xFF); addr[i++] = (byte) (value & 0xFF); } catch (NumberFormatException e) { throw new IllegalArgumentException("invalid hexadecimal characters in segment: " + segment); } } } private void mask(byte[] b, int bits) { int start = bits / 8; int startMask = (1 << (8 - (bits % 8))) - 1; startMask = ~startMask; while (start < b.length) { b[start] &= startMask; startMask = 0; start++; } } public boolean matches(String id, String aclExpr) { LOG.trace("id: '{}' aclExpr: {}", id, aclExpr); String[] parts = aclExpr.split("/", 2); byte[] aclAddr = addr2Bytes(parts[0]); if (aclAddr == null) { return false; } int bits = aclAddr.length * 8; if (parts.length == 2) { try { bits = Integer.parseInt(parts[1]); if (bits < 0 || bits > aclAddr.length * 8) { return false; } } catch (NumberFormatException e) { return false; } } mask(aclAddr, bits); byte[] remoteAddr = addr2Bytes(id); if (remoteAddr == null) { return false; } mask(remoteAddr, bits); // Check if id and acl expression are of different formats (ipv6 or iv4) return false if (remoteAddr.length != aclAddr.length) { return false; } for (int i = 0; i < remoteAddr.length; i++) { if (remoteAddr[i] != aclAddr[i]) { return false; } } return true; } public boolean isAuthenticated() { return false; } public boolean isValid(String id) { String[] parts = id.split("/", 2); byte[] aclAddr = addr2Bytes(parts[0]); if (aclAddr == null) { return false; } if (parts.length == 2) { try { int bits = Integer.parseInt(parts[1]); if (bits < 0 || bits > aclAddr.length * 8) { return false; } } catch (NumberFormatException e) { return false; } } return true; } /** * Returns the HTTP(s) client IP address * @param request HttpServletRequest * @return IP address */ public static String getClientIPAddress(final HttpServletRequest request) { if (!Boolean.getBoolean(USE_X_FORWARDED_FOR_KEY)) { return request.getRemoteAddr(); } // to handle the case that a HTTP(s) client connects via a proxy or load balancer final String xForwardedForHeader = request.getHeader(X_FORWARDED_FOR_HEADER_NAME); if (xForwardedForHeader == null) { return request.getRemoteAddr(); } // the format of the field is: X-Forwarded-For: client, proxy1, proxy2 ... return new StringTokenizer(xForwardedForHeader, ",").nextToken().trim(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000162 15051152474 032676 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/KerberosName.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/KerberosName.0100644 0000000 0000000 00000033764 15051152474 034134 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 copied from Hadoop's security branch, * with the following changes: * 1. package changed from org.apache.hadoop.security to * org.apache.zookeeper.server.auth. * 2. Usage of Hadoop's Configuration class removed since * it is not available in Zookeeper: instead, system property * "zookeeper.security.auth_to_local" is used. */ package org.apache.zookeeper.server.auth; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.zookeeper.server.util.KerberosUtil; /** * This class implements parsing and handling of Kerberos principal names. In * particular, it splits them apart and translates them down into local * operating system names. */ public class KerberosName { /** The first component of the name */ private final String serviceName; /** The second component of the name. It may be null. */ private final String hostName; /** The realm of the name. */ private final String realm; /** * A pattern that matches a Kerberos name with at most 2 components. */ private static final Pattern nameParser = Pattern.compile("([^/@]*)(/([^/@]*))?@([^/@]*)"); /** * A pattern that matches a string with out '$' and then a single * parameter with $n. */ private static Pattern parameterPattern = Pattern.compile("([^$]*)(\\$(\\d*))?"); /** * A pattern for parsing a auth_to_local rule. */ private static final Pattern ruleParser = Pattern.compile( "\\s*((DEFAULT)|(RULE:\\[(\\d*):([^\\]]*)](\\(([^)]*)\\))?" + "(s/([^/]*)/([^/]*)/(g)?)?))"); /** * A pattern that recognizes simple/non-simple names. */ private static final Pattern nonSimplePattern = Pattern.compile("[/@]"); /** * The list of translation rules. */ private static List rules; private static String defaultRealm; static { try { defaultRealm = KerberosUtil.getDefaultRealm(); } catch (Exception ke) { if ((System.getProperty("zookeeper.requireKerberosConfig") != null) && (System.getProperty("zookeeper.requireKerberosConfig").equals("true"))) { throw new IllegalArgumentException("Can't get Kerberos configuration", ke); } else { defaultRealm = ""; } } try { // setConfiguration() will work even if the above try() fails due // to a missing Kerberos configuration (unless zookeeper.requireKerberosConfig // is set to true, which would not allow execution to reach here due to the // throwing of an IllegalArgumentException above). setConfiguration(); } catch (IOException e) { throw new IllegalArgumentException("Could not configure Kerberos principal name mapping."); } } /** * Create a name from the full Kerberos principal name. * @param name */ public KerberosName(String name) { Matcher match = nameParser.matcher(name); if (!match.matches()) { if (name.contains("@")) { throw new IllegalArgumentException("Malformed Kerberos name: " + name); } else { serviceName = name; hostName = null; realm = null; } } else { serviceName = match.group(1); hostName = match.group(3); realm = match.group(4); } } /** * Get the configured default realm. * @return the default realm from the krb5.conf */ public String getDefaultRealm() { return defaultRealm; } /** * Put the name back together from the parts. */ @Override public String toString() { StringBuilder result = new StringBuilder(); result.append(serviceName); if (hostName != null) { result.append('/'); result.append(hostName); } if (realm != null) { result.append('@'); result.append(realm); } return result.toString(); } /** * Get the first component of the name. * @return the first section of the Kerberos principal name */ public String getServiceName() { return serviceName; } /** * Get the second component of the name. * @return the second section of the Kerberos principal name, and may be null */ public String getHostName() { return hostName; } /** * Get the realm of the name. * @return the realm of the name, may be null */ public String getRealm() { return realm; } /** * An encoding of a rule for translating kerberos names. */ private static class Rule { private final boolean isDefault; private final int numOfComponents; private final String format; private final Pattern match; private final Pattern fromPattern; private final String toPattern; private final boolean repeat; Rule() { isDefault = true; numOfComponents = 0; format = null; match = null; fromPattern = null; toPattern = null; repeat = false; } Rule(int numOfComponents, String format, String match, String fromPattern, String toPattern, boolean repeat) { isDefault = false; this.numOfComponents = numOfComponents; this.format = format; this.match = match == null ? null : Pattern.compile(match); this.fromPattern = fromPattern == null ? null : Pattern.compile(fromPattern); this.toPattern = toPattern; this.repeat = repeat; } @Override public String toString() { StringBuilder buf = new StringBuilder(); if (isDefault) { buf.append("DEFAULT"); } else { buf.append("RULE:["); buf.append(numOfComponents); buf.append(':'); buf.append(format); buf.append(']'); if (match != null) { buf.append('('); buf.append(match); buf.append(')'); } if (fromPattern != null) { buf.append("s/"); buf.append(fromPattern); buf.append('/'); buf.append(toPattern); buf.append('/'); if (repeat) { buf.append('g'); } } } return buf.toString(); } /** * Replace the numbered parameters of the form $n where n is from 1 to * the length of params. Normal text is copied directly and $n is replaced * by the corresponding parameter. * @param format the string to replace parameters again * @param params the list of parameters * @return the generated string with the parameter references replaced. * @throws BadFormatString */ static String replaceParameters(String format, String[] params) throws BadFormatString { Matcher match = parameterPattern.matcher(format); int start = 0; StringBuilder result = new StringBuilder(); while (start < format.length() && match.find(start)) { result.append(match.group(1)); String paramNum = match.group(3); if (paramNum != null) { try { int num = Integer.parseInt(paramNum); if (num < 0 || num > params.length) { throw new BadFormatString(String.format( "index %d from %s is outside of the valid range 0 to %d", num, format, (params.length - 1))); } result.append(params[num]); } catch (NumberFormatException nfe) { throw new BadFormatString("bad format in username mapping in " + paramNum, nfe); } } start = match.end(); } return result.toString(); } /** * Replace the matches of the from pattern in the base string with the value * of the to string. * @param base the string to transform * @param from the pattern to look for in the base string * @param to the string to replace matches of the pattern with * @param repeat whether the substitution should be repeated * @return */ static String replaceSubstitution(String base, Pattern from, String to, boolean repeat) { Matcher match = from.matcher(base); if (repeat) { return match.replaceAll(to); } else { return match.replaceFirst(to); } } /** * Try to apply this rule to the given name represented as a parameter * array. * @param params first element is the realm, second and later elements are * are the components of the name "a/b@FOO" -> {"FOO", "a", "b"} * @return the short name if this rule applies or null * @throws IOException throws if something is wrong with the rules */ String apply(String[] params) throws IOException { String result = null; if (isDefault) { if (defaultRealm.equals(params[0])) { result = params[1]; } } else if (params.length - 1 == numOfComponents) { String base = replaceParameters(format, params); if (match == null || match.matcher(base).matches()) { if (fromPattern == null) { result = base; } else { result = replaceSubstitution(base, fromPattern, toPattern, repeat); } } } if (result != null && nonSimplePattern.matcher(result).find()) { throw new NoMatchingRule("Non-simple name " + result + " after auth_to_local rule " + this); } return result; } } static List parseRules(String rules) { List result = new ArrayList<>(); String remaining = rules.trim(); while (remaining.length() > 0) { Matcher matcher = ruleParser.matcher(remaining); if (!matcher.lookingAt()) { throw new IllegalArgumentException("Invalid rule: " + remaining); } if (matcher.group(2) != null) { result.add(new Rule()); } else { result.add(new Rule( Integer.parseInt(matcher.group(4)), matcher.group(5), matcher.group(7), matcher.group(9), matcher.group(10), "g".equals(matcher.group(11)))); } remaining = remaining.substring(matcher.end()); } return result; } /** * Set the static configuration to get the rules. * @throws IOException */ public static void setConfiguration() throws IOException { String ruleString = System.getProperty("zookeeper.security.auth_to_local", "DEFAULT"); rules = parseRules(ruleString); } @SuppressWarnings("serial") public static class BadFormatString extends IOException { BadFormatString(String msg) { super(msg); } BadFormatString(String msg, Throwable err) { super(msg, err); } } @SuppressWarnings("serial") public static class NoMatchingRule extends IOException { NoMatchingRule(String msg) { super(msg); } } /** * Get the translation of the principal name into an operating system * user name. * @return the short name * @throws IOException */ public String getShortName() throws IOException { String[] params; if (hostName == null) { // if it is already simple, just return it if (realm == null) { return serviceName; } params = new String[]{realm, serviceName}; } else { params = new String[]{realm, serviceName, hostName}; } for (Rule r : rules) { String result = r.apply(params); if (result != null) { return result; } } throw new NoMatchingRule("No rules applied to " + toString()); } static void printRules() throws IOException { int i = 0; for (Rule r : rules) { System.out.println(++i + " " + r); } } public static void main(String[] args) throws Exception { for (String arg : args) { KerberosName name = new KerberosName(arg); System.out.println("Name: " + name + " to " + name.getShortName()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000177 15051152474 032704 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/KeyAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/KeyAuthentica0100644 0000000 0000000 00000011636 15051152474 034231 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import static java.nio.charset.StandardCharsets.UTF_8; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * This class is a sample implementation of being passed the ZooKeeperServer * handle in the constructor, and reading data from zknodes to authenticate. * At a minimum, a real Auth provider would need to override validate() to * e.g. perform certificate validation of auth based a public key. * * See the "Pluggable ZooKeeper authentication" section of the * "Zookeeper Programmer's Guide" for general details of implementing an * authentication plugin. e.g. * http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#sc_ZooKeeperPluggableAuthentication * * This class looks for a numeric "key" under the /key node. * Authorization is granted if the user passes in as authorization a number * which is a multiple of the key value, i.e. * (auth % key) == 0 * In a real implementation, you might do something like storing a public * key in /key, and using it to verify that auth tokens passed in were signed * by the corresponding private key. * * When the node /key does not exist, any auth token is accepted, so that * bootstrapping may occur. * */ public class KeyAuthenticationProvider extends ServerAuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(KeyAuthenticationProvider.class); public String getScheme() { return "key"; } private byte[] getKey(ZooKeeperServer zks) { ZKDatabase db = zks.getZKDatabase(); if (db != null) { try { Stat stat = new Stat(); return db.getData("/key", stat, null); } catch (NoNodeException e) { LOG.error("getData failed", e); } } return null; } private boolean validate(byte[] key, byte[] auth) { // perform arbitrary function (auth is a multiple of key) try { String keyStr = new String(key, UTF_8); String authStr = new String(auth, UTF_8); int keyVal = Integer.parseInt(keyStr); int authVal = Integer.parseInt(authStr); if (keyVal != 0 && ((authVal % keyVal) != 0)) { return false; } } catch (NumberFormatException nfe) { LOG.error("bad formatting", nfe); return false; } return true; } @Override public KeeperException.Code handleAuthentication(ServerObjs serverObjs, byte[] authData) { byte[] key = getKey(serverObjs.getZks()); String authStr = new String(authData, UTF_8); String keyStr = ""; if (key != null) { if (!validate(key, authData)) { keyStr = new String(key, UTF_8); LOG.debug("KeyAuthenticationProvider handleAuthentication ({}, {}) -> FAIL.\n", keyStr, authStr); return KeeperException.Code.AUTHFAILED; } } // default to allow, so the key can be initially written LOG.debug("KeyAuthenticationProvider handleAuthentication -> OK.\n"); // NOTE: keyStr in addAuthInfo() sticks with the created node ACLs. // For transient keys or certificates, this presents a problem. // In that case, replace it with something non-ephemeral (or punt with null). // // BOTH addAuthInfo and an OK return-code are needed for authentication. serverObjs.getCnxn().addAuthInfo(new Id(getScheme(), keyStr)); return KeeperException.Code.OK; } @Override public boolean matches(ServerObjs serverObjs, MatchValues matchValues) { return matchValues.getId().equals(matchValues.getAclExpr()); } @Override public boolean isAuthenticated() { return true; } @Override public boolean isValid(String id) { return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000166 15051152474 032702 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/ProviderRegistry.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/ProviderRegis0100644 0000000 0000000 00000007216 15051152474 034256 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.server.ZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ProviderRegistry { private static final Logger LOG = LoggerFactory.getLogger(ProviderRegistry.class); public static final String AUTHPROVIDER_PROPERTY_PREFIX = "zookeeper.authProvider."; private static boolean initialized = false; private static final Map authenticationProviders = new HashMap<>(); //VisibleForTesting public static void reset() { synchronized (ProviderRegistry.class) { initialized = false; authenticationProviders.clear(); } } public static void initialize() { synchronized (ProviderRegistry.class) { IPAuthenticationProvider ipp = new IPAuthenticationProvider(); authenticationProviders.put(ipp.getScheme(), ipp); if (DigestAuthenticationProvider.isEnabled()) { DigestAuthenticationProvider digp = new DigestAuthenticationProvider(); authenticationProviders.put(digp.getScheme(), digp); } Enumeration en = System.getProperties().keys(); while (en.hasMoreElements()) { String k = (String) en.nextElement(); addOrUpdateProvider(k); } initialized = true; } } public static void addOrUpdateProvider(String authKey) { synchronized (ProviderRegistry.class) { if (authKey.startsWith(AUTHPROVIDER_PROPERTY_PREFIX)) { String className = System.getProperty(authKey); try { Class c = ZooKeeperServer.class.getClassLoader().loadClass(className); AuthenticationProvider ap = (AuthenticationProvider) c.getDeclaredConstructor().newInstance(); authenticationProviders.put(ap.getScheme(), ap); } catch (Exception e) { LOG.warn("Problems loading {}", className, e); } } } } public static ServerAuthenticationProvider getServerProvider(String scheme) { return WrappedAuthenticationProvider.wrap(getProvider(scheme)); } public static AuthenticationProvider getProvider(String scheme) { if (!initialized) { initialize(); } return authenticationProviders.get(scheme); } public static void removeProvider(String scheme) { authenticationProviders.remove(scheme); } public static String listProviders() { StringBuilder sb = new StringBuilder(); for (String s : authenticationProviders.keySet()) { sb.append(s).append(" "); } return sb.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000200 15051152474 032667 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/SASLAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/SASLAuthentic0100644 0000000 0000000 00000004736 15051152474 034105 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.server.ServerCnxn; public class SASLAuthenticationProvider implements AuthenticationProvider { public String getScheme() { return "sasl"; } public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { // Should never call this: SASL authentication is negotiated at session initiation. // TODO: consider substituting current implementation of direct ClientCnxn manipulation with // a call to this method (SASLAuthenticationProvider:handleAuthentication()) at session initiation. return KeeperException.Code.AUTHFAILED; } public boolean matches(String id, String aclExpr) { if ((id.equals("super") || id.equals(aclExpr))) { return true; } String readAccessUser = System.getProperty("zookeeper.letAnySaslUserDoX"); return readAccessUser != null && aclExpr.equals(readAccessUser); } public boolean isAuthenticated() { return true; } public boolean isValid(String id) { // Since the SASL authenticator will usually be used with Kerberos authentication, // it should enforce that these names are valid according to Kerberos's // syntax for principals. // // Use the KerberosName(id) constructor to define validity: // if KerberosName(id) throws IllegalArgumentException, then id is invalid. // otherwise, it is valid. // try { new KerberosName(id); return true; } catch (IllegalArgumentException e) { return false; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000177 15051152474 032704 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/SaslServerCallbackHandler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/SaslServerCal0100644 0000000 0000000 00000012720 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.io.IOException; import java.util.Map; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.RealmCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SaslServerCallbackHandler implements CallbackHandler { private static final Logger LOG = LoggerFactory.getLogger(SaslServerCallbackHandler.class); private static final String SYSPROP_SUPER_PASSWORD = "zookeeper.SASLAuthenticationProvider.superPassword"; private static final String SYSPROP_REMOVE_HOST = "zookeeper.kerberos.removeHostFromPrincipal"; private static final String SYSPROP_REMOVE_REALM = "zookeeper.kerberos.removeRealmFromPrincipal"; private String userName; private final Map credentials; public SaslServerCallbackHandler(Map credentials) { this.credentials = credentials; } public void handle(Callback[] callbacks) throws UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { handleNameCallback((NameCallback) callback); } else if (callback instanceof PasswordCallback) { handlePasswordCallback((PasswordCallback) callback); } else if (callback instanceof RealmCallback) { handleRealmCallback((RealmCallback) callback); } else if (callback instanceof AuthorizeCallback) { handleAuthorizeCallback((AuthorizeCallback) callback); } } } private void handleNameCallback(NameCallback nc) { // check to see if this user is in the user password database. if (credentials.get(nc.getDefaultName()) == null) { LOG.warn("User '{}' not found in list of DIGEST-MD5 authenticateable users.", nc.getDefaultName()); return; } nc.setName(nc.getDefaultName()); userName = nc.getDefaultName(); } private void handlePasswordCallback(PasswordCallback pc) { if ("super".equals(this.userName) && System.getProperty(SYSPROP_SUPER_PASSWORD) != null) { // superuser: use Java system property for password, if available. pc.setPassword(System.getProperty(SYSPROP_SUPER_PASSWORD).toCharArray()); } else if (credentials.containsKey(userName)) { pc.setPassword(credentials.get(userName).toCharArray()); } else { LOG.warn("No password found for user: {}", userName); } } private void handleRealmCallback(RealmCallback rc) { LOG.debug("client supplied realm: {}", rc.getDefaultText()); rc.setText(rc.getDefaultText()); } private void handleAuthorizeCallback(AuthorizeCallback ac) { String authenticationID = ac.getAuthenticationID(); String authorizationID = ac.getAuthorizationID(); LOG.info("Successfully authenticated client: authenticationID={}; authorizationID={}.", authenticationID, authorizationID); ac.setAuthorized(true); // canonicalize authorization id according to system properties: // zookeeper.kerberos.removeRealmFromPrincipal(={true,false}) // zookeeper.kerberos.removeHostFromPrincipal(={true,false}) KerberosName kerberosName = new KerberosName(authenticationID); try { StringBuilder userNameBuilder = new StringBuilder(kerberosName.getShortName()); if (shouldAppendHost(kerberosName)) { userNameBuilder.append("/").append(kerberosName.getHostName()); } if (shouldAppendRealm(kerberosName)) { userNameBuilder.append("@").append(kerberosName.getRealm()); } LOG.info("Setting authorizedID: {}", userNameBuilder); ac.setAuthorizedID(userNameBuilder.toString()); } catch (IOException e) { LOG.error("Failed to set name based on Kerberos authentication rules.", e); } } private boolean shouldAppendRealm(KerberosName kerberosName) { return !isSystemPropertyTrue(SYSPROP_REMOVE_REALM) && kerberosName.getRealm() != null; } private boolean shouldAppendHost(KerberosName kerberosName) { return !isSystemPropertyTrue(SYSPROP_REMOVE_HOST) && kerberosName.getHostName() != null; } private boolean isSystemPropertyTrue(String propertyName) { return "true".equals(System.getProperty(propertyName)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000202 15051152474 032671 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/ServerAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/ServerAuthent0100644 0000000 0000000 00000011027 15051152474 034264 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.util.List; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ZooKeeperServer; /** * A variation on {@link AuthenticationProvider} that provides additional * parameters for more detailed authentication */ public abstract class ServerAuthenticationProvider implements AuthenticationProvider { public static class ServerObjs { private final ZooKeeperServer zks; private final ServerCnxn cnxn; /** * @param zks * the ZooKeeper server instance * @param cnxn * the cnxn that received the authentication information. */ public ServerObjs(ZooKeeperServer zks, ServerCnxn cnxn) { this.zks = zks; this.cnxn = cnxn; } public ZooKeeperServer getZks() { return zks; } public ServerCnxn getCnxn() { return cnxn; } } public static class MatchValues { private final String path; private final String id; private final String aclExpr; private final int perm; private final List setAcls; /** * @param path * the path of the operation being authenticated * @param id * the id to check. * @param aclExpr * the expression to match ids against. * @param perm * the permission value being authenticated * @param setAcls * for set ACL operations, the list of ACLs being set. Otherwise null. */ public MatchValues(String path, String id, String aclExpr, int perm, List setAcls) { this.path = path; this.id = id; this.aclExpr = aclExpr; this.perm = perm; this.setAcls = setAcls; } public String getPath() { return path; } public String getId() { return id; } public String getAclExpr() { return aclExpr; } public int getPerm() { return perm; } public List getSetAcls() { return setAcls; } } /** * This method is called when a client passes authentication data for this * scheme. The authData is directly from the authentication packet. The * implementor may attach new ids to the authInfo field of cnxn or may use * cnxn to send packets back to the client. * * @param serverObjs * cnxn/server/etc that received the authentication information. * @param authData * the authentication data received. * @return indication of success or failure */ public abstract KeeperException.Code handleAuthentication(ServerObjs serverObjs, byte[] authData); /** * This method is called to see if the given id matches the given id * expression in the ACL. This allows schemes to use application specific * wild cards. * * @param serverObjs * cnxn/server/etc that received the authentication information. * @param matchValues * values to be matched */ public abstract boolean matches(ServerObjs serverObjs, MatchValues matchValues); @Override public final KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { throw new UnsupportedOperationException(); } @Override public final boolean matches(String id, String aclExpr) { throw new UnsupportedOperationException(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000203 15051152474 032672 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/WrappedAuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/WrappedAuthen0100644 0000000 0000000 00000006007 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; /** * Provides backwards compatibility between older {@link AuthenticationProvider} * implementations and the new {@link ServerAuthenticationProvider} interface. */ class WrappedAuthenticationProvider extends ServerAuthenticationProvider { private final AuthenticationProvider implementation; static ServerAuthenticationProvider wrap(AuthenticationProvider provider) { if (provider == null) { return null; } return (provider instanceof ServerAuthenticationProvider) ? (ServerAuthenticationProvider) provider : new WrappedAuthenticationProvider(provider); } private WrappedAuthenticationProvider(AuthenticationProvider implementation) { this.implementation = implementation; } /** * {@inheritDoc} * * forwards to older method {@link #handleAuthentication(ServerCnxn, byte[])} */ @Override public KeeperException.Code handleAuthentication(ServerObjs serverObjs, byte[] authData) { return implementation.handleAuthentication(serverObjs.getCnxn(), authData); } /** * {@inheritDoc} * * forwards to older method {@link #handleAuthentication(HttpServletRequest, byte[])} * @return */ @Override public List handleAuthentication(HttpServletRequest request, byte[] authData) { return implementation.handleAuthentication(request, authData); } /** * {@inheritDoc} * * forwards to older method {@link #matches(String, String)} */ @Override public boolean matches(ServerObjs serverObjs, MatchValues matchValues) { return implementation.matches(matchValues.getId(), matchValues.getAclExpr()); } @Override public String getScheme() { return implementation.getScheme(); } @Override public boolean isAuthenticated() { return implementation.isAuthenticated(); } @Override public boolean isValid(String id) { return implementation.isValid(id); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000200 15051152474 032667 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/X509AuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/X509Authentic0100644 0000000 0000000 00000026306 15051152474 034005 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.common.X509Exception.KeyManagerException; import org.apache.zookeeper.common.X509Exception.TrustManagerException; import org.apache.zookeeper.common.X509Util; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An AuthenticationProvider backed by an X509TrustManager and an X509KeyManager * to perform remote host certificate authentication. The default algorithm is * SunX509 and a JKS KeyStore. To specify the locations of the key store and * trust store, set the following system properties: *
    zookeeper.ssl.keyStore.location *
    zookeeper.ssl.trustStore.location *
    To specify store passwords, set the following system properties: *
    zookeeper.ssl.keyStore.password *
    zookeeper.ssl.trustStore.password *
    Alternatively, the passwords can be specified by the following password file path properties: *
    zookeeper.ssl.keyStore.passwordPath *
    zookeeper.ssl.trustStore.passwordPath *
    Alternatively, this can be plugged with any X509TrustManager and * X509KeyManager implementation. */ public class X509AuthenticationProvider implements AuthenticationProvider { public static final String X509_CERTIFICATE_ATTRIBUTE_NAME = "javax.servlet.request.X509Certificate"; static final String ZOOKEEPER_X509AUTHENTICATIONPROVIDER_SUPERUSER = "zookeeper.X509AuthenticationProvider.superUser"; private static final Logger LOG = LoggerFactory.getLogger(X509AuthenticationProvider.class); private final X509TrustManager trustManager; private final X509KeyManager keyManager; /** * Initialize the X509AuthenticationProvider with a JKS KeyStore and JKS * TrustStore according to the following system properties: *
    zookeeper.ssl.keyStore.location *
    zookeeper.ssl.trustStore.location *
    zookeeper.ssl.keyStore.password *
    zookeeper.ssl.keyStore.passwordPath *
    zookeeper.ssl.trustStore.password *
    zookeeper.ssl.trustStore.passwordPath */ public X509AuthenticationProvider() throws X509Exception { ZKConfig config = new ZKConfig(); try (X509Util x509Util = new ClientX509Util()) { String keyStoreLocation = config.getProperty(x509Util.getSslKeystoreLocationProperty(), ""); String keyStorePassword = x509Util.getPasswordFromConfigPropertyOrFile(config, x509Util.getSslKeystorePasswdProperty(), x509Util.getSslKeystorePasswdPathProperty()); String keyStoreTypeProp = config.getProperty(x509Util.getSslKeystoreTypeProperty()); boolean crlEnabled = Boolean.parseBoolean(config.getProperty(x509Util.getSslCrlEnabledProperty())); boolean ocspEnabled = Boolean.parseBoolean(config.getProperty(x509Util.getSslOcspEnabledProperty())); boolean hostnameVerificationEnabled = Boolean.parseBoolean(config.getProperty(x509Util.getSslHostnameVerificationEnabledProperty())); X509KeyManager km = null; X509TrustManager tm = null; if (keyStoreLocation.isEmpty()) { LOG.warn("keystore not specified for client connection"); } else { try { km = X509Util.createKeyManager(keyStoreLocation, keyStorePassword, keyStoreTypeProp); } catch (KeyManagerException e) { LOG.error("Failed to create key manager", e); } } String trustStoreLocation = config.getProperty(x509Util.getSslTruststoreLocationProperty(), ""); String trustStorePassword = x509Util.getPasswordFromConfigPropertyOrFile(config, x509Util.getSslTruststorePasswdProperty(), x509Util.getSslTruststorePasswdPathProperty()); String trustStoreTypeProp = config.getProperty(x509Util.getSslTruststoreTypeProperty()); boolean fipsMode = X509Util.getFipsMode(config); if (trustStoreLocation.isEmpty()) { LOG.warn("Truststore not specified for client connection"); } else { try { tm = X509Util.createTrustManager( trustStoreLocation, trustStorePassword, trustStoreTypeProp, crlEnabled, ocspEnabled, hostnameVerificationEnabled, false, fipsMode); } catch (TrustManagerException e) { LOG.error("Failed to create trust manager", e); } } this.keyManager = km; this.trustManager = tm; } } /** * Initialize the X509AuthenticationProvider with the provided * X509TrustManager and X509KeyManager. * * @param trustManager X509TrustManager implementation to use for remote * host authentication. * @param keyManager X509KeyManager implementation to use for certificate * management. */ public X509AuthenticationProvider(X509TrustManager trustManager, X509KeyManager keyManager) { this.trustManager = trustManager; this.keyManager = keyManager; } @Override public String getScheme() { return "x509"; } @Override public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { List certs = Arrays.asList(cnxn.getClientCertificateChain()); X509Certificate[] certChain = certs.toArray(new X509Certificate[certs.size()]); final Collection ids = handleAuthentication(certChain); if (ids.isEmpty()) { LOG.error("Failed to authenticate session 0x{}", Long.toHexString(cnxn.getSessionId())); return KeeperException.Code.AUTHFAILED; } for (Id id : ids) { cnxn.addAuthInfo(id); } return KeeperException.Code.OK; } @Override public List handleAuthentication(HttpServletRequest request, byte[] authData) { final X509Certificate[] certChain = (X509Certificate[]) request.getAttribute(X509_CERTIFICATE_ATTRIBUTE_NAME); return handleAuthentication(certChain); } /** * Determine the string to be used as the remote host session Id for * authorization purposes. Associate this client identifier with a * ServerCnxn that has been authenticated over SSL, and any ACLs that refer * to the authenticated client. * * @param clientCert Authenticated X509Certificate associated with the * remote host. * @return Identifier string to be associated with the client. */ protected String getClientId(X509Certificate clientCert) { return clientCert.getSubjectX500Principal().getName(); } @Override public boolean matches(String id, String aclExpr) { if (System.getProperty(ZOOKEEPER_X509AUTHENTICATIONPROVIDER_SUPERUSER) != null) { return id.equals(System.getProperty(ZOOKEEPER_X509AUTHENTICATIONPROVIDER_SUPERUSER)) || id.equals(aclExpr); } return id.equals(aclExpr); } @Override public boolean isAuthenticated() { return true; } @Override public boolean isValid(String id) { try { new X500Principal(id); return true; } catch (IllegalArgumentException e) { return false; } } /** * Get the X509TrustManager implementation used for remote host * authentication. * * @return The X509TrustManager. * @throws TrustManagerException When there is no trust manager available. */ public X509TrustManager getTrustManager() throws TrustManagerException { if (trustManager == null) { throw new TrustManagerException("No trust manager available"); } return trustManager; } /** * Get the X509KeyManager implementation used for certificate management. * * @return The X509KeyManager. * @throws KeyManagerException When there is no key manager available. */ public X509KeyManager getKeyManager() throws KeyManagerException { if (keyManager == null) { throw new KeyManagerException("No key manager available"); } return keyManager; } private List handleAuthentication(final X509Certificate[] certChain) { final List ids = new ArrayList<>(); if (certChain == null || certChain.length == 0) { LOG.warn("No certificate chain available to authenticate"); return ids; } if (trustManager == null) { LOG.error("No trust manager available to authenticate"); return ids; } final X509Certificate clientCert = certChain[0]; try { // Authenticate client certificate trustManager.checkClientTrusted(certChain, clientCert.getPublicKey().getAlgorithm()); } catch (CertificateException ce) { LOG.error("Failed to trust certificate", ce); return ids; } final String clientId = getClientId(clientCert); if (clientId.equals(System.getProperty(ZOOKEEPER_X509AUTHENTICATIONPROVIDER_SUPERUSER))) { ids.add(new Id("super", clientId)); LOG.info("Authenticated Id '{}' as super user", clientId); } final Id id = new Id(getScheme(), clientId); ids.add(id); LOG.info("Authenticated Id '{}' for scheme '{}'", id.getId(), id.getScheme()); return Collections.unmodifiableList(ids); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000202 15051152474 032665 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/AbstractFourLetterCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/AbstractFo0100644 0000000 0000000 00000005065 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.IOException; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Set of threads for command ports. All the 4 letter commands are run via a * thread. Each class maps to a corresponding 4 letter command. CommandThread is * the abstract class from which all the others inherit. */ public abstract class AbstractFourLetterCommand { private static final Logger LOG = LoggerFactory.getLogger(AbstractFourLetterCommand.class); public static final String ZK_NOT_SERVING = "This ZooKeeper instance is not currently serving requests"; protected PrintWriter pw; protected ServerCnxn serverCnxn; protected ZooKeeperServer zkServer; protected ServerCnxnFactory factory; public AbstractFourLetterCommand(PrintWriter pw, ServerCnxn serverCnxn) { this.pw = pw; this.serverCnxn = serverCnxn; } public void start() { run(); } public void run() { try { commandRun(); } catch (IOException ie) { LOG.error("Error in running command ", ie); } finally { serverCnxn.cleanupWriterSocket(pw); } } public void setZkServer(ZooKeeperServer zkServer) { this.zkServer = zkServer; } /** * @return true if the server is running, false otherwise. */ boolean isZKServerRunning() { return zkServer != null && zkServer.isRunning(); } public void setFactory(ServerCnxnFactory factory) { this.factory = factory; } public abstract void commandRun() throws IOException; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000175 15051152474 032676 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/CnxnStatResetCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/CnxnStatRe0100644 0000000 0000000 00000002526 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; public class CnxnStatResetCommand extends AbstractFourLetterCommand { public CnxnStatResetCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { factory.resetAllConnectionStats(); pw.println("Connection stats reset."); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000170 15051152474 032671 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/CommandExecutor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/CommandExe0100644 0000000 0000000 00000007367 15051152474 034176 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; public class CommandExecutor { /** * This class decides which command to be executed and then executes */ public boolean execute( ServerCnxn serverCnxn, PrintWriter pwriter, final int commandCode, ZooKeeperServer zkServer, ServerCnxnFactory factory) { AbstractFourLetterCommand command = getCommand(serverCnxn, pwriter, commandCode); if (command == null) { return false; } command.setZkServer(zkServer); command.setFactory(factory); command.start(); return true; } private AbstractFourLetterCommand getCommand( ServerCnxn serverCnxn, PrintWriter pwriter, final int commandCode) { AbstractFourLetterCommand command = null; if (commandCode == FourLetterCommands.ruokCmd) { command = new RuokCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.getTraceMaskCmd) { command = new TraceMaskCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.enviCmd) { command = new EnvCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.confCmd) { command = new ConfCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.srstCmd) { command = new StatResetCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.crstCmd) { command = new CnxnStatResetCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.dirsCmd) { command = new DirsCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.dumpCmd) { command = new DumpCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.statCmd || commandCode == FourLetterCommands.srvrCmd) { command = new StatCommand(pwriter, serverCnxn, commandCode); } else if (commandCode == FourLetterCommands.consCmd) { command = new ConsCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.wchpCmd || commandCode == FourLetterCommands.wchcCmd || commandCode == FourLetterCommands.wchsCmd) { command = new WatchCommand(pwriter, serverCnxn, commandCode); } else if (commandCode == FourLetterCommands.mntrCmd) { command = new MonitorCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.isroCmd) { command = new IsroCommand(pwriter, serverCnxn); } else if (commandCode == FourLetterCommands.hashCmd) { command = new DigestCommand(pwriter, serverCnxn); } return command; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/ConfCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/ConfComman0100644 0000000 0000000 00000002376 15051152474 034171 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; public class ConfCommand extends AbstractFourLetterCommand { ConfCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { zkServer.dumpConf(pw); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/ConsCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/ConsComman0100644 0000000 0000000 00000002625 15051152474 034203 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; public class ConsCommand extends AbstractFourLetterCommand { public ConsCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { for (ServerCnxn c : factory.getConnections()) { c.dumpConnectionInfo(pw, false); pw.println(); } pw.println(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000166 15051152474 032676 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/DigestCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/DigestComm0100644 0000000 0000000 00000003140 15051152474 034172 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import java.util.List; import org.apache.zookeeper.server.DataTree.ZxidDigest; import org.apache.zookeeper.server.ServerCnxn; /** * Command used to dump the latest digest histories. */ public class DigestCommand extends AbstractFourLetterCommand { public DigestCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.print(ZK_NOT_SERVING); } else { List digestLog = zkServer.getZKDatabase().getDataTree().getDigestLog(); for (ZxidDigest zd : digestLog) { pw.println(Long.toHexString(zd.getZxid()) + ": " + zd.getDigest()); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/DirsCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/DirsComman0100644 0000000 0000000 00000002626 15051152474 034203 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.IOException; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; public class DirsCommand extends AbstractFourLetterCommand { public DirsCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() throws IOException { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); return; } pw.println("datadir_size: " + zkServer.getDataDirSize()); pw.println("logdir_size: " + zkServer.getLogDirSize()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/DumpCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/DumpComman0100644 0000000 0000000 00000003361 15051152474 034204 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxn; public class DumpCommand extends AbstractFourLetterCommand { public DumpCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { pw.println("SessionTracker dump:"); zkServer.getSessionTracker().dumpSessions(pw); pw.println("ephemeral nodes dump:"); zkServer.dumpEphemerals(pw); pw.println("Connections dump:"); //dumpConnections connection is implemented only in NIOServerCnxnFactory if (factory instanceof NIOServerCnxnFactory) { ((NIOServerCnxnFactory) factory).dumpConnections(pw); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000163 15051152474 032673 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/EnvCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/EnvCommand0100644 0000000 0000000 00000002650 15051152474 034173 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import java.util.List; import org.apache.zookeeper.Environment; import org.apache.zookeeper.server.ServerCnxn; public class EnvCommand extends AbstractFourLetterCommand { EnvCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { List env = Environment.list(); pw.println("Environment:"); for (Environment.Entry e : env) { pw.print(e.getKey()); pw.print("="); pw.println(e.getValue()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000173 15051152474 032674 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/FourLetterCommands.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/FourLetter0100644 0000000 0000000 00000022510 15051152474 034234 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class contains constants for all the four letter commands */ public class FourLetterCommands { /* * See * Zk Admin. this link is for all the commands. */ public static final int confCmd = ByteBuffer.wrap("conf".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int consCmd = ByteBuffer.wrap("cons".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int crstCmd = ByteBuffer.wrap("crst".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int dirsCmd = ByteBuffer.wrap("dirs".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int dumpCmd = ByteBuffer.wrap("dump".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int enviCmd = ByteBuffer.wrap("envi".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int getTraceMaskCmd = ByteBuffer.wrap("gtmk".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int ruokCmd = ByteBuffer.wrap("ruok".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int setTraceMaskCmd = ByteBuffer.wrap("stmk".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int srvrCmd = ByteBuffer.wrap("srvr".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int srstCmd = ByteBuffer.wrap("srst".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int statCmd = ByteBuffer.wrap("stat".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int wchcCmd = ByteBuffer.wrap("wchc".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int wchpCmd = ByteBuffer.wrap("wchp".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int wchsCmd = ByteBuffer.wrap("wchs".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int mntrCmd = ByteBuffer.wrap("mntr".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ public static final int isroCmd = ByteBuffer.wrap("isro".getBytes()).getInt(); /* * See * Zk Admin. this link is for all the commands. */ protected static final int hashCmd = ByteBuffer.wrap("hash".getBytes()).getInt(); /* * The control sequence sent by the telnet program when it closes a * connection. Include simply to keep the logs cleaner (the server would * close the connection anyway because it would parse this as a negative * length). */ public static final int telnetCloseCmd = 0xfff4fffd; private static final String ZOOKEEPER_4LW_COMMANDS_WHITELIST = "zookeeper.4lw.commands.whitelist"; private static final Logger LOG = LoggerFactory.getLogger(FourLetterCommands.class); private static final Map cmd2String = new HashMap<>(); private static final Set whiteListedCommands = new HashSet<>(); private static boolean whiteListInitialized = false; // @VisibleForTesting public static synchronized void resetWhiteList() { whiteListInitialized = false; whiteListedCommands.clear(); } /** * Return the string representation of the specified command code. */ public static String getCommandString(int command) { return cmd2String.get(command); } /** * Check if the specified command code is from a known command. * * @param command The integer code of command. * @return true if the specified command is known, false otherwise. */ public static boolean isKnown(int command) { return cmd2String.containsKey(command); } /** * Check if the specified command is enabled. * * In ZOOKEEPER-2693 we introduce a configuration option to only * allow a specific set of white listed commands to execute. * A command will only be executed if it is also configured * in the white list. * * @param command The command string. * @return true if the specified command is enabled */ public static synchronized boolean isEnabled(String command) { if (whiteListInitialized) { return whiteListedCommands.contains(command); } String commands = System.getProperty(ZOOKEEPER_4LW_COMMANDS_WHITELIST); if (commands != null) { String[] list = commands.split(","); for (String cmd : list) { if (cmd.trim().equals("*")) { for (Map.Entry entry : cmd2String.entrySet()) { whiteListedCommands.add(entry.getValue()); } break; } if (!cmd.trim().isEmpty()) { whiteListedCommands.add(cmd.trim()); } } } // It is sad that isro and srvr are used by ZooKeeper itself. Need fix this // before deprecating 4lw. if (System.getProperty("readonlymode.enabled", "false").equals("true")) { whiteListedCommands.add("isro"); } // zkServer.sh depends on "srvr". whiteListedCommands.add("srvr"); whiteListInitialized = true; LOG.info("The list of known four letter word commands is : {}", Arrays.asList(cmd2String)); LOG.info("The list of enabled four letter word commands is : {}", Arrays.asList(whiteListedCommands)); return whiteListedCommands.contains(command); } // specify all of the commands that are available static { cmd2String.put(confCmd, "conf"); cmd2String.put(consCmd, "cons"); cmd2String.put(crstCmd, "crst"); cmd2String.put(dirsCmd, "dirs"); cmd2String.put(dumpCmd, "dump"); cmd2String.put(enviCmd, "envi"); cmd2String.put(getTraceMaskCmd, "gtmk"); cmd2String.put(ruokCmd, "ruok"); cmd2String.put(setTraceMaskCmd, "stmk"); cmd2String.put(srstCmd, "srst"); cmd2String.put(srvrCmd, "srvr"); cmd2String.put(statCmd, "stat"); cmd2String.put(wchcCmd, "wchc"); cmd2String.put(wchpCmd, "wchp"); cmd2String.put(wchsCmd, "wchs"); cmd2String.put(mntrCmd, "mntr"); cmd2String.put(isroCmd, "isro"); cmd2String.put(telnetCloseCmd, "telnet close"); cmd2String.put(hashCmd, "hash"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/IsroCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/IsroComman0100644 0000000 0000000 00000002625 15051152474 034215 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; public class IsroCommand extends AbstractFourLetterCommand { public IsroCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.print("null"); } else if (zkServer instanceof ReadOnlyZooKeeperServer) { pw.print("ro"); } else { pw.print("rw"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000167 15051152474 032677 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/MonitorCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/MonitorCom0100644 0000000 0000000 00000004050 15051152474 034226 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; public class MonitorCommand extends AbstractFourLetterCommand { MonitorCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); return; } // non metrics zkServer.dumpMonitorValues(this::print); ServerMetrics.getMetrics().getMetricsProvider().dump(this::print); } private void print(String key, Object value) { if (value == null) { output(key, null); } else if (value instanceof Long || value instanceof Integer) { // format as integers output(key, value + ""); } else if (value instanceof Number) { // format as floating point output(key, ((Number) value).doubleValue() + ""); } else { output(key, value.toString()); } } private void output(String key, String value) { pw.print("zk_"); pw.print(key); pw.print("\t"); pw.println(value); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000163 15051152474 032673 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/NopCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/NopCommand0100644 0000000 0000000 00000002607 15051152474 034201 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; /** * A command that does not do anything except reply to client with predefined message. * It is used to inform clients who execute none white listed four letter word commands. */ public class NopCommand extends AbstractFourLetterCommand { private String msg; public NopCommand(PrintWriter pw, ServerCnxn serverCnxn, String msg) { super(pw, serverCnxn); this.msg = msg; } @Override public void commandRun() { pw.println(msg); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/RuokCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/RuokComman0100644 0000000 0000000 00000002225 15051152474 034215 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; public class RuokCommand extends AbstractFourLetterCommand { public RuokCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { pw.print("imok"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000174 15051152474 032675 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/SetTraceMaskCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/SetTraceMa0100644 0000000 0000000 00000002340 15051152474 034130 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; public class SetTraceMaskCommand extends AbstractFourLetterCommand { long trace = 0; public SetTraceMaskCommand(PrintWriter pw, ServerCnxn serverCnxn, long trace) { super(pw, serverCnxn); this.trace = trace; } @Override public void commandRun() { pw.print(trace); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/StatCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/StatComman0100644 0000000 0000000 00000005542 15051152474 034215 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.Version; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.quorum.BufferStats; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.server.quorum.ReadOnlyZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class StatCommand extends AbstractFourLetterCommand { private static final Logger LOG = LoggerFactory.getLogger(AbstractFourLetterCommand.class); private int len; public StatCommand(PrintWriter pw, ServerCnxn serverCnxn, int len) { super(pw, serverCnxn); this.len = len; } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { pw.print("Zookeeper version: "); pw.println(Version.getFullVersion()); if (zkServer instanceof ReadOnlyZooKeeperServer) { pw.println("READ-ONLY mode; serving only read-only clients"); } if (len == FourLetterCommands.statCmd) { LOG.info("Stat command output"); pw.println("Clients:"); for (ServerCnxn c : factory.getConnections()) { c.dumpConnectionInfo(pw, true); pw.println(); } pw.println(); } ServerStats serverStats = zkServer.serverStats(); pw.print(serverStats.toString()); pw.print("Node count: "); pw.println(zkServer.getZKDatabase().getNodeCount()); if (serverStats.getServerState().equals("leader")) { Leader leader = ((LeaderZooKeeperServer) zkServer).getLeader(); BufferStats proposalStats = leader.getProposalStats(); pw.printf("Proposal sizes last/min/max: %s%n", proposalStats.toString()); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000171 15051152474 032672 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/StatResetCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/StatResetC0100644 0000000 0000000 00000003225 15051152474 034164 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; public class StatResetCommand extends AbstractFourLetterCommand { public StatResetCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { ServerStats serverStats = zkServer.serverStats(); serverStats.reset(); if (serverStats.getServerState().equals("leader")) { ((LeaderZooKeeperServer) zkServer).getLeader().getProposalStats().reset(); } pw.println("Server stats reset."); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000171 15051152474 032672 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/TraceMaskCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/TraceMaskC0100644 0000000 0000000 00000002377 15051152474 034127 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ZooTrace; public class TraceMaskCommand extends AbstractFourLetterCommand { TraceMaskCommand(PrintWriter pw, ServerCnxn serverCnxn) { super(pw, serverCnxn); } @Override public void commandRun() { long traceMask = ZooTrace.getTextTraceLevel(); pw.print(traceMask); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000165 15051152474 032675 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/WatchCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/command/WatchComma0100644 0000000 0000000 00000003263 15051152474 034170 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.command; import java.io.PrintWriter; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerCnxn; public class WatchCommand extends AbstractFourLetterCommand { int len = 0; public WatchCommand(PrintWriter pw, ServerCnxn serverCnxn, int len) { super(pw, serverCnxn); this.len = len; } @Override public void commandRun() { if (!isZKServerRunning()) { pw.println(ZK_NOT_SERVING); } else { DataTree dt = zkServer.getZKDatabase().getDataTree(); if (len == FourLetterCommands.wchsCmd) { dt.dumpWatchesSummary(pw); } else if (len == FourLetterCommands.wchpCmd) { dt.dumpWatches(pw, true); } else { dt.dumpWatches(pw, false); } pw.println(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000171 15051152474 032672 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/CommandClient.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Command0100644 0000000 0000000 00000012234 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A convenient helper to send controller command to ControllerService. */ public class CommandClient { private final int requestTimeoutInMs; private static final int DEFAULT_TIMEOUT = 10000; private static final Logger LOG = LoggerFactory.getLogger(CommandClient.class); private final int hostPort; private final String hostName; private HttpClient client; private boolean started = false; /** * Instantiate a client configured to send requests to localhost. * @param localHostPort Port that the localhost CommandListener is listening on. * @param requestTimeoutInMs Timeout in ms for synchronous requests to timeout. */ public CommandClient(int localHostPort, int requestTimeoutInMs) { this.client = new HttpClient(); this.requestTimeoutInMs = requestTimeoutInMs; this.hostName = "localhost"; this.hostPort = localHostPort; } /** * Instantiate a client configured to send requests to the specified host address. * @param hostAddress The host address of the listening server. * @param requestTimeoutInMs Timeout in ms for synchronous requests to timeout. */ public CommandClient(InetSocketAddress hostAddress, int requestTimeoutInMs) { this.client = new HttpClient(); this.requestTimeoutInMs = requestTimeoutInMs; this.hostName = hostAddress.getHostName(); this.hostPort = hostAddress.getPort(); } public CommandClient(int localhostPort) { this(localhostPort, DEFAULT_TIMEOUT); } public synchronized void close() { try { if (client != null) { client.stop(); client = null; } } catch (Exception ex) { LOG.warn("Exception during shutdown", ex); } } /** * Send a command with no parameters to the server and wait for a response. * Returns true if we received a good (200) response and false otherwise. */ public boolean trySendCommand(ControlCommand.Action action) { return trySendCommand(action, null); } /** * Send a command with an optional command parameter to the server and wait for a response. * @param action The command Action to send. * @param commandParameter The command parameter, in the form of command/action/parameter. * @return true if we received a good (200) response and false otherwise. */ public boolean trySendCommand(ControlCommand.Action action, String commandParameter) { try { if (!started) { client.start(); started = true; } ContentResponse response = sendCommand(action, commandParameter); LOG.info("Received {} response from the server", response); return (response.getStatus() == 200); } catch (InterruptedException | IOException ex) { LOG.warn("Failed to get response from server", ex); } catch (Exception ex) { LOG.error("Unknown exception when sending command", ex); } return false; } /** * Send a command and optional command parameter to the server and block until receiving * a response. * * @param action The command Action to send. * @param commandParameter The command parameter, in the form of command/action/parameter. * @return The full response body from the CommandListener server. */ public ContentResponse sendCommand(ControlCommand.Action action, String commandParameter) throws Exception { String command = String.format("%s%s:%s/%s", "http://", this.hostName, this.hostPort, ControlCommand.createCommandUri(action, commandParameter)); ContentResponse response = this.client.newRequest(command).timeout(this.requestTimeoutInMs, TimeUnit.MILLISECONDS).send(); LOG.info("Sent command {}", command); LOG.info("Response body {}", new String(response.getContent(), StandardCharsets.UTF_8)); return response; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000173 15051152474 032674 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/CommandListener.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Command0100644 0000000 0000000 00000007522 15051152474 034272 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.util.ServiceUtils; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * An HTTP server listening to incoming controller commands sent from CommandClient (or any of your favorite REST client * ) and dispatching the command to the ZooKeeperServerController for execution. */ public class CommandListener { private static final Logger LOG = LoggerFactory.getLogger(CommandListener.class); private ZooKeeperServerController controller; private Server server; public CommandListener(ZooKeeperServerController controller, ControllerServerConfig config) { try { this.controller = controller; String host = config.getControllerAddress().getHostName(); int port = config.getControllerAddress().getPort(); server = new Server(port); LOG.info("CommandListener server host: {} with port: {}", host, port); server.setHandler(new CommandHandler()); server.start(); } catch (Exception ex) { LOG.error("Failed to instantiate CommandListener.", ex); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } } public void close() { try { if (server != null) { server.stop(); server = null; } } catch (Exception ex) { LOG.warn("Exception during shutdown CommandListener server", ex); } } private class CommandHandler extends AbstractHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { // Extract command string from request path. Remove leading '/'. String commandStr = request.getPathInfo().substring(1); int responseCode; response.setContentType("text/html;charset=utf-8"); try { ControlCommand command = ControlCommand.parseUri(commandStr); controller.processCommand(command); baseRequest.setHandled(true); responseCode = HttpServletResponse.SC_OK; } catch (IllegalArgumentException ex) { LOG.error("Bad argument or command", ex); responseCode = HttpServletResponse.SC_BAD_REQUEST; } catch (Exception ex) { LOG.error("Failed processing the request", ex); throw ex; } response.setStatus(responseCode); response.getWriter().println(commandStr); LOG.info("CommandListener processed command {} with response code {}", commandStr, responseCode); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000172 15051152474 032673 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ControlCommand.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000010673 15051152474 034335 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; /** * Set of commands that this controller can execute. Commands are comprised * of an action and an optional parameter specific to that action. */ public class ControlCommand { /** * Actions available to the controller */ public enum Action { // Simple "are you there" ping to confirm the controller is up and running. PING, // Shutdown everything, including CommandListener, ControllerService, Controller and the ZooKeeperServer. SHUTDOWN, // Close a connection triggering a client disconnect (and then reconnect attempt). // No parameter indicates close all connections. Optional parameter indicates a specific session id (as long). CLOSECONNECTION, // More actions go here in the future (force drop sessions, etc). EXPIRESESSION, // Reject all future connections. No parameter required. REJECTCONNECTIONS, // Add latency to server replies. // Optional parameter indicates time in milliseconds to delay // (default = 1 second). ADDDELAY, // Fail requests. // Optional parameter indicates how many requests to fail. // (default = all requests until RESET). FAILREQUESTS, // Process requests but do not send a response. // Optional parameter indicates how many requests to fail. // (default = all requests until RESET). NORESPONSE, // No parameter indicates fail all requests. // Optional parameter indicates undo all the chaotic action commands // (reject connections, add delay, fail requests, eat requests and so on...). RESET, // Force the quorum to elect a new leader. ELECTNEWLEADER, // More actions go here in the future... } public static final String ENDPOINT = "command"; public static final String ENDPOINT_PREFIX = ENDPOINT + "/"; private Action action; public Action getAction() { return action; } private String parameter; protected String getParameter() { return parameter; } public ControlCommand(Action action) { this(action, null); } public ControlCommand(Action action, String param) { this.action = action; this.parameter = param; } /** * Create a REST command uri. * @param action The 'verb' of the command. * @param parameter The optional parameter. * @return A string to send to the server as the end of the Uri. */ public static String createCommandUri(Action action, String parameter) { return ENDPOINT_PREFIX + action.toString() + (parameter != null && !parameter.isEmpty() ? "/" + parameter : ""); } /** * Parse a Uri into the required Command action and parameter. * @param commandUri the properly formatted Uri. */ public static ControlCommand parseUri(String commandUri) { if (commandUri == null) { throw new IllegalArgumentException("commandUri can't be null."); } if (!commandUri.startsWith(ENDPOINT_PREFIX)) { throw new IllegalArgumentException("Missing required prefix: " + ENDPOINT_PREFIX); } String uri = commandUri.substring(ENDPOINT_PREFIX.length()); String name; String param; int separatorIndex = uri.indexOf('/'); if (separatorIndex < 0) { name = uri; param = null; } else { name = uri.substring(0, separatorIndex); param = uri.substring(separatorIndex + 1); } return new ControlCommand(Action.valueOf(name.toUpperCase()), param); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000202 15051152474 032665 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ControllableConnection.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000006630 15051152474 034333 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import org.apache.jute.BinaryInputArchive; import org.apache.jute.Record; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.NIOServerCnxn; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Extension of NIOServerCnxn which can inject changes per controller commands. * Similar extensions can implement on top of NettyServerCnxn as well. */ @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "factory is ControllableConnectionFactory type.") public class ControllableConnection extends NIOServerCnxn { private static final Logger LOG = LoggerFactory.getLogger(ControllableConnection.class); private final ControllableConnectionFactory controller; public ControllableConnection(ZooKeeperServer zk, SocketChannel sock, SelectionKey sk, NIOServerCnxnFactory factory, NIOServerCnxnFactory.SelectorThread selectorThread) throws IOException { super(zk, sock, sk, factory, selectorThread); controller = (ControllableConnectionFactory) factory; } @Override public int sendResponse(ReplyHeader header, Record record, String tag) { if (controller.shouldSendResponse()) { try { return super.sendResponse(header, record, tag); } catch (IOException ex) { LOG.warn("IO Exception occurred", ex); } } else { LOG.warn("Controller is configured to NOT sending response back to client."); } return -1; } @Override protected void readRequest() throws IOException { if (controller.shouldFailNextRequest()) { ByteBuffer buffer = incomingBuffer.slice(); BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(buffer)); RequestHeader h = new RequestHeader(); h.deserialize(bia, "header"); super.sendResponse(new ReplyHeader(h.getXid(), 0, KeeperException.Code.APIERROR.intValue()), null, null); } else { controller.delayRequestIfNeeded(); super.readRequest(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000211 15051152474 032665 xustar000000000 0000000 137 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ControllableConnectionFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000010276 15051152474 034334 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import org.apache.zookeeper.server.NIOServerCnxn; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Extension of NIOServerCnxnFactory which can inject changes per controller commands. * Similar extensions can implement on top of NettyServerCnxnFactory as well. */ @SuppressFBWarnings(value = "SWL_SLEEP_WITH_LOCK_HELD", justification = "no dead lock") public class ControllableConnectionFactory extends NIOServerCnxnFactory { private static final Logger LOG = LoggerFactory.getLogger(ControllableConnectionFactory.class); private long responseDelayInMs = 0; private long remainingRequestsToFail = 0; private long remainingResponsesToHold = 0; public ControllableConnectionFactory() { } @Override protected NIOServerCnxn createConnection(SocketChannel sock, SelectionKey sk, SelectorThread selectorThread) throws IOException { return new ControllableConnection(zkServer, sock, sk, this, selectorThread); } /** * Called by the connection to delay processing requests from the client. */ public synchronized void delayRequestIfNeeded() { try { if (responseDelayInMs > 0) { Thread.sleep(responseDelayInMs); } } catch (InterruptedException ex) { LOG.warn("Interrupted while delaying requests", ex); } } /** * Check if we should fail the next incoming request. * If so, decrement the remaining requests to fail. */ public synchronized boolean shouldFailNextRequest() { if (remainingRequestsToFail == 0) { return false; } // Value < 0 indicates fail all requests. if (remainingRequestsToFail > 0) { remainingRequestsToFail--; } return true; } /** * Check if we should send a response to the latest processed request (true), * or eat the response to mess with the client (false). * If so, decrement the remaining requests to eat. */ public synchronized boolean shouldSendResponse() { if (remainingResponsesToHold == 0) { return true; } // Value < 0 indicates hold all the responses. if (remainingResponsesToHold > 0) { remainingResponsesToHold--; } return false; } public synchronized void delayResponses(long delayInMs) { if (delayInMs < 0) { throw new IllegalArgumentException("delay must be non-negative"); } responseDelayInMs = delayInMs; } public synchronized void resetBadBehavior() { responseDelayInMs = 0; remainingRequestsToFail = 0; remainingResponsesToHold = 0; } public synchronized void failAllFutureRequests() { this.remainingRequestsToFail = -1; } public synchronized void failFutureRequests(long requestsToFail) { this.remainingRequestsToFail = requestsToFail; } public synchronized void holdAllFutureResponses() { this.remainingResponsesToHold = -1; } public synchronized void holdFutureResponses(long requestsToHold) { this.remainingResponsesToHold = requestsToHold; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000202 15051152474 032665 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ControllerServerConfig.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000014271 15051152474 034333 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; /** * Config for the ControllerService. Responsible for providing the minimum set of configurations * that's required to spin up a single member ensemble. */ public class ControllerServerConfig extends QuorumPeerConfig { public static final String CONTROLLER_PORT_KEY = "zookeeper.controllerPort"; public static final String CLIENT_PORT_KEY = "zookeeper.clientPortAddress"; private InetSocketAddress controllerAddress; public InetSocketAddress getControllerAddress() { return controllerAddress; } /** * Instantiate a new config via a zk config file. * @param configFile path to the configuration file * @throws ConfigException */ public ControllerServerConfig(String configFile) throws ConfigException { parse(configFile); } /** * Instantiate a config object with required parameters. * @param hostAddress The address to bind to (likely loopback or localhost) * @param controllerPort Port the controller will listen for incoming control command sent from CommandClient. * @param zkServerPort Port the ZooKeeper server will listen on. * @param dataDirPath Path to the data directory that ZooKeeperServer uses. */ public ControllerServerConfig(InetAddress hostAddress, int controllerPort, int zkServerPort, String dataDirPath) { controllerAddress = new InetSocketAddress(hostAddress, controllerPort); clientPortAddress = new InetSocketAddress(hostAddress, zkServerPort); dataDir = new File(dataDirPath); dataLogDir = dataDir; serverId = 0; } /** * Instantiate a config object with required parameters. * @param controllerPort Port the controller will listen for incoming control command sent from CommandClient. * @param zkServerPort Port the ZooKeeper server will listen on. * @param dataDirPath Path to the data directory that ZooKeeperServer uses. */ public ControllerServerConfig(int controllerPort, int zkServerPort, String dataDirPath) { this(InetAddress.getLoopbackAddress(), controllerPort, zkServerPort, dataDirPath); } public ServerConfig getZooKeeperServerConfig() { ServerConfig serverConfig = new ServerConfig(); serverConfig.readFrom(this); return serverConfig; } @Override public void parse(String configFile) throws ConfigException { super.parse(configFile); for (String key : System.getProperties().stringPropertyNames()) { if (CONTROLLER_PORT_KEY.equalsIgnoreCase(key)) { setControllerAddress(System.getProperty(key)); } if (CLIENT_PORT_KEY.equals(key)) { setClientAddress(System.getProperty(key)); } } if (controllerAddress == null) { throw new ConfigException("Missing required parameter " + CONTROLLER_PORT_KEY); } if (clientPortAddress == null) { throw new ConfigException("Missing required parameter " + CLIENT_PORT_KEY); } } private void setControllerAddress(String port) { try { controllerAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), Integer.parseInt(port)); } catch (NumberFormatException ex) { throw new IllegalArgumentException("Invalid port", ex); } } private void setClientAddress(String port) { try { clientPortAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), Integer.parseInt(port)); } catch (NumberFormatException ex) { throw new IllegalArgumentException("Invalid port", ex); } } /** * Ensure config is acceptable by filling in default values for any missing quorum configuration * (specifically in the case of a single machine ensemble) * * @throws IOException */ public void ensureComplete() throws IOException { if (this.quorumVerifier != null && this.quorumVerifier.getAllMembers().size() > 0) { return; } // QuorumPeer requires a QuorumVerifier. // We will use majority strategy with only this host in the quorum. // We need to provide 2 more ports: one for elections and one for quorum communications. // We will also mark this host as the leader. ServerSocket randomSocket1 = new ServerSocket(0); int quorumPort = randomSocket1.getLocalPort(); ServerSocket randomSocket2 = new ServerSocket(0); int electionPort = randomSocket2.getLocalPort(); randomSocket1.close(); randomSocket2.close(); QuorumPeer.QuorumServer selfAsPeer = new QuorumPeer.QuorumServer( 0, new InetSocketAddress(quorumPort), new InetSocketAddress(electionPort), this.clientPortAddress ); Map peers = new HashMap<>(); peers.put(selfAsPeer.id, selfAsPeer); this.quorumVerifier = new QuorumMaj(peers); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000175 15051152474 032676 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ControllerService.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000012730 15051152474 034331 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import java.io.IOException; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Main class which starts a ZooKeeperServer, a ZooKeeperServerController and the ControllerService. * Tests should either invoke this class as the main target of a new JVM process OR explicitly * start and stop a singleton of this class in their test process. */ public class ControllerService { private static final Logger LOG = LoggerFactory.getLogger(ControllerService.class); private ZooKeeperServerController controller; private CommandListener listener; protected QuorumPeerConfig config; private ServerCnxnFactory serverCnxnFactory = null; protected QuorumPeer quorumPeer = null; /** * Starts the ControllerService as a stand alone app. Useful for out of process testing * - such as during integration testing. */ public static void main(String[] args) { ControllerServerConfig config; try { if (args.length != 1) { throw new IllegalArgumentException("Require config file as cmd line argument"); } else { config = new ControllerServerConfig(args[0]); } new ControllerService().start(config); } catch (Exception ex) { System.err.println(ex.getMessage()); System.err.println("Usage: TestControllerMain controller-port configfile"); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } } /** * Starts a new thread to run the controller (useful when this service is hosted in process * - such as during unit testing). */ public Thread start(ControllerServerConfig controllerConfig) { this.config = controllerConfig; final ControllerService svc = this; Thread runner = new Thread(() -> { try { svc.run(); } catch (Exception e) { } }); runner.setDaemon(true); runner.start(); return runner; } public synchronized void shutdown() { if (listener != null) { listener.close(); listener = null; } if (controller != null) { controller.shutdown(); controller = null; } } /** * Initializes an instance of the ZooKeeperServer, the ZooKeeperServerController, and a new * Http listener (CommandListener) for the controller. */ protected void initService() throws IOException { ControllerServerConfig controllerConfig = (ControllerServerConfig) config; controllerConfig.ensureComplete(); this.controller = new ZooKeeperServerController(controllerConfig); this.listener = new CommandListener(controller, controllerConfig); this.serverCnxnFactory = controller.getCnxnFactory(); } protected void runServices() { this.controller.run(); } protected void cleanup() { if (listener != null) { listener.close(); listener = null; } } /** * Runs the main loop for this application but does not exit the process. */ public void initializeAndRun(String[] args) throws QuorumPeerConfig.ConfigException { initConfig(args); run(); } /** * Derived classes may override to do custom initialization of command line args. */ protected void initConfig(String[] args) throws QuorumPeerConfig.ConfigException { if (args.length == 1) { config.parse(args[0]); } } /** * Run the app given a QuorumPeerConfig. * * @param config The quorum peer config. */ public void runFromConfig(QuorumPeerConfig config) { LOG.info("Starting quorum peer from peer config"); this.config = config; run(); } protected void run() { try { initService(); } catch (Exception ex) { LOG.error("Failed to start ControllerService.", ex); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } runServices(); cleanup(); } /** * Is the service up with all necessary initialization and port opening complete? * * @return true if the controller service is ready to use; false otherwise. */ public boolean isReady() { return controller != null && controller.isReady(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000205 15051152474 032670 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ZooKeeperServerController.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/controller/ZooKeep0100644 0000000 0000000 00000016053 15051152474 034267 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class which accepts commands to modify ZooKeeperServer state or Connection state at runtime for the purpose of * single machine integration testing. Not meant to be used in production. It is recommended to use this in conjunction * with the CommandListener HttpServer and CommandClient. * */ @SuppressFBWarnings(value = "IS2_INCONSISTENT_SYNC", justification = "quorum peer is internally synchronized.") public class ZooKeeperServerController { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerController.class); private static final long DEFAULT_DELAY_MS = 1000; private QuorumPeer quorumPeer; private ControllableConnectionFactory cnxnFactory; public ZooKeeperServerController(QuorumPeerConfig config) throws IOException { if (config == null) { throw new IllegalArgumentException("ZooKeeperServerController requires a valid config!"); } cnxnFactory = new ControllableConnectionFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog()); quorumPeer = QuorumPeer.createFromConfig(config); quorumPeer.setCnxnFactory(cnxnFactory); } public void run() { try { quorumPeer.start(); quorumPeer.join(); } catch (Exception ex) { LOG.error("Fatal error starting quorum peer", ex); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } } protected ServerCnxnFactory getCnxnFactory() { return cnxnFactory; } public synchronized void shutdown() { if (this.cnxnFactory != null) { this.cnxnFactory.shutdown(); this.cnxnFactory = null; } if (this.quorumPeer != null && this.quorumPeer.isRunning()) { this.quorumPeer.shutdown(); this.quorumPeer = null; } } public synchronized boolean isReady() { return this.cnxnFactory != null && this.quorumPeer != null && this.quorumPeer.isRunning() && this.quorumPeer.getActiveServer() != null; } /** * Process the command. An exception indicates errors. No exception indicates success. */ public void processCommand(ControlCommand command) { if (command == null) { throw new IllegalArgumentException("Invalid command parameter!"); } LOG.info("processing command {}{}", command.getAction(), command.getParameter() == null ? "" : "[" + command.getParameter() + "]"); // Don't process command if we are shutting down or still initializing. if (!isReady()) { throw new IllegalStateException("Service is not ready. It has already been shutdown or is still initializing."); } switch (command.getAction()) { case PING: // NO-OP break; case SHUTDOWN: shutdown(); break; case CLOSECONNECTION: if (command.getParameter() == null) { cnxnFactory.closeAll(ServerCnxn.DisconnectReason.CLOSE_ALL_CONNECTIONS_FORCED); } else { // A single parameter should be a session id as long. // Parse failure exceptions will be sent to the caller. cnxnFactory.closeSession(Long.decode(command.getParameter()), ServerCnxn.DisconnectReason.CONNECTION_CLOSE_FORCED); } break; case EXPIRESESSION: if (command.getParameter() == null) { expireAllSessions(); } else { // A single parameter should be a session id as long. // Parse failure exceptions will be sent to the caller expireSession(Long.decode(command.getParameter())); } break; case REJECTCONNECTIONS: // TODO: (hanm) implement once dependent feature is ready. //cnxnFactory.rejectNewConnections(); break; case ADDDELAY: cnxnFactory.delayResponses(command.getParameter() == null ? DEFAULT_DELAY_MS : Long.decode(command.getParameter())); break; case NORESPONSE: if (command.getParameter() == null) { cnxnFactory.holdAllFutureResponses(); } else { cnxnFactory.holdFutureResponses(Long.decode(command.getParameter())); } break; case FAILREQUESTS: if (command.getParameter() == null) { cnxnFactory.failAllFutureRequests(); } else { cnxnFactory.failFutureRequests(Long.decode(command.getParameter())); } break; case RESET: cnxnFactory.resetBadBehavior(); break; case ELECTNEWLEADER: quorumPeer.startLeaderElection(); break; default: throw new IllegalArgumentException("Unknown command: " + command); } } private ZooKeeperServer getServer() { return quorumPeer.getActiveServer(); } private void expireSession(long sessionId) { getServer().expire(sessionId); } private void expireAllSessions() { for (Long sessionId : getServer().getSessionTracker().localSessions()) { expireSession(sessionId); } for (Long sessionId : getServer().getSessionTracker().globalSessions()) { expireSession(sessionId); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000165 15051152474 032675 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/ExitHandler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/ExitHandl0100644 0000000 0000000 00000002333 15051152474 034135 0ustar00rootroot0000000 0000000 package org.apache.zookeeper.server.embedded; /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Behaviour of the server in case of internal error. * When you are running tests you will use {@link #LOG_ONLY}, * but please take care of using {@link #EXIT} when runnning in production. */ public enum ExitHandler { /** * Exit the Java process. */ EXIT, /** * Only log the error. This option is meant to be used only in tests. */ LOG_ONLY; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000201 15051152474 032664 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/ZooKeeperServerEmbedded.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/ZooKeeper0100644 0000000 0000000 00000011156 15051152474 034163 0ustar00rootroot0000000 0000000 package org.apache.zookeeper.server.embedded; /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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.file.Path; import java.util.Objects; import java.util.Properties; import org.apache.yetus.audience.InterfaceAudience; import org.apache.yetus.audience.InterfaceStability; /** * This API allows you to start a ZooKeeper server node from Java code

    * The server will run inside the same process.

    * Typical usecases are: *

      *
    • Running automated tests
    • *
    • Launch ZooKeeper server with a Java based service management system
    • *
    *

    * Please take into consideration that in production usually it is better to not run the client * together with the server in order to avoid race conditions, especially around how ephemeral nodes work. */ @InterfaceAudience.Public @InterfaceStability.Evolving public interface ZooKeeperServerEmbedded extends AutoCloseable { /** * Builder for ZooKeeperServerEmbedded. */ class ZookKeeperServerEmbeddedBuilder { private Path baseDir; private Properties configuration; private ExitHandler exitHandler = ExitHandler.EXIT; /** * Base directory of the server. * The system will create a temporary configuration file inside this directory. * Please remember that dynamic configuration files wil be saved into this directory by default. *

    * If you do not set a 'dataDir' configuration entry the system will use a subdirectory of baseDir. * @param baseDir * @return the builder */ public ZookKeeperServerEmbeddedBuilder baseDir(Path baseDir) { this.baseDir = Objects.requireNonNull(baseDir); return this; } /** * Set the contents of the main configuration as it would be in zk_server.conf file. * @param configuration the configuration * @return the builder */ public ZookKeeperServerEmbeddedBuilder configuration(Properties configuration) { this.configuration = Objects.requireNonNull(configuration); return this; } /** * Set the behaviour in case of hard system errors, see {@link ExitHandler}. * @param exitHandler the handler * @return the builder */ public ZookKeeperServerEmbeddedBuilder exitHandler(ExitHandler exitHandler) { this.exitHandler = Objects.requireNonNull(exitHandler); return this; } /** * Validate the configuration and create the server, without starting it. * @return the new server * @throws Exception * @see #start() */ public ZooKeeperServerEmbedded build() throws Exception { if (baseDir == null) { throw new IllegalStateException("baseDir is null"); } if (configuration == null) { throw new IllegalStateException("configuration is null"); } return new ZooKeeperServerEmbeddedImpl(configuration, baseDir, exitHandler); } } static ZookKeeperServerEmbeddedBuilder builder() { return new ZookKeeperServerEmbeddedBuilder(); } /** * Start the server. * @throws Exception */ void start() throws Exception; /** * Start the server * @param startupTimeout time to wait in millis for the server to start * @throws Exception */ void start(long startupTimeout) throws Exception; /** * Get a connection string useful for the client. * @return the connection string * @throws Exception in case the connection string is not available */ String getConnectionString() throws Exception; String getSecureConnectionString() throws Exception; /** * Shutdown gracefully the server and wait for resources to be released. */ @Override void close(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000205 15051152474 032670 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/ZooKeeperServerEmbeddedImpl.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/embedded/ZooKeeper0100644 0000000 0000000 00000022262 15051152474 034163 0ustar00rootroot0000000 0000000 package org.apache.zookeeper.server.embedded; import java.io.OutputStream; import java.net.InetSocketAddress; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.security.sasl.SaslException; import org.apache.zookeeper.server.DatadirCleanupManager; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerMain; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the * License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing permissions and limitations under the License. */ /** * Implementation of ZooKeeperServerEmbedded. */ class ZooKeeperServerEmbeddedImpl implements ZooKeeperServerEmbedded { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerEmbeddedImpl.class); private final QuorumPeerConfig config; private QuorumPeerMain maincluster; private ZooKeeperServerMain mainsingle; private Thread thread; private DatadirCleanupManager purgeMgr; private final ExitHandler exitHandler; private volatile boolean stopping; private int boundClientPort; private int boundSecureClientPort; ZooKeeperServerEmbeddedImpl(Properties p, Path baseDir, ExitHandler exitHandler) throws Exception { if (!p.containsKey("dataDir")) { p.put("dataDir", baseDir.resolve("data").toAbsolutePath().toString()); } Path configFile = Files.createTempFile(baseDir, "zookeeper.configuration", ".properties"); try (OutputStream oo = Files.newOutputStream(configFile)) { p.store(oo, "Automatically generated at every-boot"); } this.exitHandler = exitHandler; LOG.info("Current configuration is at {}", configFile.toAbsolutePath()); config = new QuorumPeerConfig(); config.parse(configFile.toAbsolutePath().toString()); LOG.info("ServerID:" + config.getServerId()); LOG.info("DataDir:" + config.getDataDir()); LOG.info("Servers:" + config.getServers()); LOG.info("ElectionPort:" + config.getElectionPort()); LOG.info("SyncLimit:" + config.getSyncLimit()); LOG.info("PeerType:" + config.getPeerType()); LOG.info("Distributed:" + config.isDistributed()); LOG.info("SyncEnabled:" + config.getSyncEnabled()); LOG.info("MetricsProviderClassName:" + config.getMetricsProviderClassName()); for (Map.Entry server : config.getServers().entrySet()) { LOG.info("Server: " + server.getKey() + " -> addr " + server.getValue().addr + " elect " + server.getValue().electionAddr + " id=" + server.getValue().id + " type " + server.getValue().type); } } @Override public void start() throws Exception { start(Integer.MAX_VALUE); } @Override public void start(long startupTimeout) throws Exception { switch (exitHandler) { case EXIT: ServiceUtils.setSystemExitProcedure(ServiceUtils.SYSTEM_EXIT); break; case LOG_ONLY: ServiceUtils.setSystemExitProcedure(ServiceUtils.LOG_ONLY); break; default: ServiceUtils.setSystemExitProcedure(ServiceUtils.SYSTEM_EXIT); break; } final CompletableFuture started = new CompletableFuture<>(); if (config.getServers().size() > 1 || config.isDistributed()) { LOG.info("Running ZK Server in single Quorum MODE"); maincluster = new QuorumPeerMain() { protected QuorumPeer getQuorumPeer() throws SaslException { return new QuorumPeer() { @Override public void start() { super.start(); boundClientPort = getClientPort(); boundSecureClientPort = getSecureClientPort(); LOG.info("ZK Server {} started", this); started.complete(null); } }; } }; // Start and schedule the the purge task purgeMgr = new DatadirCleanupManager(config .getDataDir(), config.getDataLogDir(), config .getSnapRetainCount(), config.getPurgeInterval()); purgeMgr.start(); thread = new Thread("zkservermainrunner") { @Override public void run() { try { maincluster.runFromConfig(config); maincluster.close(); LOG.info("ZK server died. Requsting stop on JVM"); if (!stopping) { ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue()); } } catch (Throwable t) { LOG.error("error during server lifecycle", t); maincluster.close(); if (!stopping) { ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } } } }; thread.start(); } else { LOG.info("Running ZK Server in single STANDALONE MODE"); mainsingle = new ZooKeeperServerMain() { @Override public void serverStarted() { LOG.info("ZK Server started"); boundClientPort = getClientPort(); boundSecureClientPort = getSecureClientPort(); started.complete(null); } }; purgeMgr = new DatadirCleanupManager(config .getDataDir(), config.getDataLogDir(), config .getSnapRetainCount(), config.getPurgeInterval()); purgeMgr.start(); thread = new Thread("zkservermainrunner") { @Override public void run() { try { ServerConfig cc = new ServerConfig(); cc.readFrom(config); LOG.info("ZK server starting"); mainsingle.runFromConfig(cc); LOG.info("ZK server died. Requesting stop on JVM"); if (!stopping) { ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue()); } } catch (Throwable t) { LOG.error("error during server lifecycle", t); mainsingle.close(); if (!stopping) { ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } } } }; thread.start(); } try { started.get(startupTimeout, TimeUnit.MILLISECONDS); } catch (TimeoutException err) { LOG.info("Startup timed out, trying to close"); close(); throw err; } } @Override public String getConnectionString() { return prettifyConnectionString(config.getClientPortAddress(), boundClientPort); } @Override public String getSecureConnectionString() { return prettifyConnectionString(config.getSecureClientPortAddress(), boundSecureClientPort); } private String prettifyConnectionString(InetSocketAddress confAddress, int boundPort) { if (confAddress != null) { return confAddress.getHostString() .replace("0.0.0.0", "localhost") .replace("0:0:0:0:0:0:0:0", "localhost") + ":" + boundPort; } throw new IllegalStateException("No client address is configured"); } @Override public void close() { LOG.info("Stopping ZK Server"); stopping = true; if (mainsingle != null) { mainsingle.close(); } if (maincluster != null) { maincluster.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000170 15051152474 032671 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxCounter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxCo0100644 0000000 0000000 00000007012 15051152474 034117 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.metrics.Summary; /** * Generic long counter that keep track of min/max/avg. The counter is * thread-safe */ public class AvgMinMaxCounter extends Metric implements Summary { private final String name; private final AtomicLong total = new AtomicLong(); private final AtomicLong min = new AtomicLong(Long.MAX_VALUE); private final AtomicLong max = new AtomicLong(Long.MIN_VALUE); private final AtomicLong count = new AtomicLong(); public AvgMinMaxCounter(String name) { this.name = name; } public void addDataPoint(long value) { total.addAndGet(value); count.incrementAndGet(); setMin(value); setMax(value); } private void setMax(long value) { long current; while (value > (current = max.get()) && !max.compareAndSet(current, value)) { // no op } } private void setMin(long value) { long current; while (value < (current = min.get()) && !min.compareAndSet(current, value)) { // no op } } public double getAvg() { // There is possible race-condition but we don't need the stats to be // extremely accurate. long currentCount = count.get(); long currentTotal = total.get(); if (currentCount > 0) { double avgLatency = currentTotal / (double) currentCount; BigDecimal bg = new BigDecimal(avgLatency); return bg.setScale(4, RoundingMode.HALF_UP).doubleValue(); } return 0; } public long getCount() { return count.get(); } public long getMax() { long current = max.get(); return (current == Long.MIN_VALUE) ? 0 : current; } public long getMin() { long current = min.get(); return (current == Long.MAX_VALUE) ? 0 : current; } public long getTotal() { return total.get(); } public void resetMax() { max.set(getMin()); } public void reset() { count.set(0); total.set(0); min.set(Long.MAX_VALUE); max.set(Long.MIN_VALUE); } public void add(long value) { addDataPoint(value); } public Map values() { Map m = new LinkedHashMap<>(); m.put("avg_" + name, this.getAvg()); m.put("min_" + name, this.getMin()); m.put("max_" + name, this.getMax()); m.put("cnt_" + name, this.getCount()); m.put("sum_" + name, this.getTotal()); return m; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000173 15051152474 032674 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxCounterSet.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxCo0100644 0000000 0000000 00000004513 15051152474 034122 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.metrics.SummarySet; /** * Generic set of long counters that keep track of min/max/avg * for different keys. * The counter is thread-safe */ public class AvgMinMaxCounterSet extends Metric implements SummarySet { private final String name; private ConcurrentHashMap counters = new ConcurrentHashMap<>(); public AvgMinMaxCounterSet(String name) { this.name = name; } private AvgMinMaxCounter getCounterForKey(String key) { return counters.computeIfAbsent(key, k-> new AvgMinMaxCounter(k + "_" + name)); } public void addDataPoint(String key, long value) { getCounterForKey(key).addDataPoint(value); } public void resetMax() { for (Map.Entry entry : counters.entrySet()) { entry.getValue().resetMax(); } } public void reset() { for (Map.Entry entry : counters.entrySet()) { entry.getValue().reset(); } } @Override public void add(String key, long value) { addDataPoint(key, value); } @Override public Map values() { Map m = new LinkedHashMap<>(); for (Map.Entry entry : counters.entrySet()) { m.putAll(entry.getValue().values()); } return m; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000202 15051152474 032665 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxPercentileCounter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxPe0100644 0000000 0000000 00000010574 15051152474 034131 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import com.codahale.metrics.Histogram; import com.codahale.metrics.Reservoir; import com.codahale.metrics.Snapshot; import com.codahale.metrics.UniformSnapshot; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; import org.apache.zookeeper.metrics.Summary; /** * Generic long counter that keep track of min/max/avg/percentiles. * The counter is thread-safe */ public class AvgMinMaxPercentileCounter extends Metric implements Summary { private final String name; private final AvgMinMaxCounter counter; private final ResettableUniformReservoir reservoir; private final Histogram histogram; static class ResettableUniformReservoir implements Reservoir { private static final int DEFAULT_SIZE = 4096; private static final int BITS_PER_LONG = 63; private final AtomicLong count = new AtomicLong(); private volatile AtomicLongArray values = new AtomicLongArray(DEFAULT_SIZE); @Override public int size() { final long c = count.get(); if (c > values.length()) { return values.length(); } return (int) c; } @Override public void update(long value) { final long c = count.incrementAndGet(); if (c <= values.length()) { values.set((int) c - 1, value); } else { final long r = nextLong(c); if (r < values.length()) { values.set((int) r, value); } } } private static long nextLong(long n) { long bits, val; do { bits = ThreadLocalRandom.current().nextLong() & (~(1L << BITS_PER_LONG)); val = bits % n; } while (bits - val + (n - 1) < 0L); return val; } @Override public Snapshot getSnapshot() { final int s = size(); final List copy = new ArrayList<>(s); for (int i = 0; i < s; i++) { copy.add(values.get(i)); } return new UniformSnapshot(copy); } public void reset() { count.set(0); values = new AtomicLongArray(DEFAULT_SIZE); } } public AvgMinMaxPercentileCounter(String name) { this.name = name; this.counter = new AvgMinMaxCounter(this.name); reservoir = new ResettableUniformReservoir(); histogram = new Histogram(reservoir); } public void addDataPoint(long value) { counter.add(value); histogram.update(value); } public void resetMax() { // To match existing behavior in upstream counter.resetMax(); } public void reset() { counter.reset(); reservoir.reset(); } public void add(long value) { addDataPoint(value); } public Map values() { Map m = new LinkedHashMap<>(); m.putAll(counter.values()); m.put("p50_" + name, Math.round(this.histogram.getSnapshot().getMedian())); m.put("p95_" + name, Math.round(this.histogram.getSnapshot().get95thPercentile())); m.put("p99_" + name, Math.round(this.histogram.getSnapshot().get99thPercentile())); m.put("p999_" + name, Math.round(this.histogram.getSnapshot().get999thPercentile())); return m; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000205 15051152474 032670 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxPercentileCounterSet.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/AvgMinMaxPe0100644 0000000 0000000 00000005075 15051152474 034131 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.metrics.SummarySet; /** * Generic set of long counters that keep track of min/max/avg * for different keys. * The counter is thread-safe */ public class AvgMinMaxPercentileCounterSet extends Metric implements SummarySet { private final String name; private ConcurrentHashMap counters = new ConcurrentHashMap<>(); public AvgMinMaxPercentileCounterSet(String name) { this.name = name; } private AvgMinMaxPercentileCounter getCounterForKey(String key) { AvgMinMaxPercentileCounter counter = counters.get(key); if (counter == null) { counters.putIfAbsent(key, new AvgMinMaxPercentileCounter(key + "_" + name)); counter = counters.get(key); } return counter; } public void addDataPoint(String key, long value) { getCounterForKey(key).addDataPoint(value); } public void resetMax() { for (Map.Entry entry : counters.entrySet()) { entry.getValue().resetMax(); } } public void reset() { for (Map.Entry entry : counters.entrySet()) { entry.getValue().reset(); } } @Override public void add(String key, long value) { addDataPoint(key, value); } @Override public Map values() { Map m = new LinkedHashMap<>(); for (Map.Entry entry : counters.entrySet()) { m.putAll(entry.getValue().values()); } return m; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000156 15051152474 032675 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/Metric.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/Metric.java0100644 0000000 0000000 00000002155 15051152474 034154 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import java.util.Map; public abstract class Metric { public void add(long value) { } public void add(int key, long value) { } public void add(String key, long value) { } public void reset() { } public abstract Map values(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000165 15051152474 032675 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/SimpleCounter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/SimpleCount0100644 0000000 0000000 00000003121 15051152474 034245 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.metrics.Counter; public class SimpleCounter extends Metric implements Counter { private final String name; private final AtomicLong counter = new AtomicLong(); public SimpleCounter(String name) { this.name = name; } @Override public void add(long value) { counter.addAndGet(value); } @Override public void reset() { counter.set(0); } public long get() { return counter.get(); } @Override public Map values() { Map m = new LinkedHashMap<>(); m.put(name, this.get()); return m; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000170 15051152474 032671 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/SimpleCounterSet.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/metric/SimpleCount0100644 0000000 0000000 00000003600 15051152474 034247 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.metrics.CounterSet; /** * Represent a set of counters identified by different keys. * The counter is thread-safe */ public class SimpleCounterSet extends Metric implements CounterSet { private final String name; private final ConcurrentHashMap counters = new ConcurrentHashMap<>(); public SimpleCounterSet(final String name) { this.name = name; } @Override public void add(final String key, final long delta) { final SimpleCounter counter = counters.computeIfAbsent(key, (k) -> new SimpleCounter(k + "_" + name)); counter.add(delta); } @Override public void reset() { counters.values().forEach(SimpleCounter::reset); } @Override public Map values() { final Map m = new LinkedHashMap<>(); counters.values().forEach(counter -> m.putAll(counter.values())); return m; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/package.html0100644 0000000 0000000 00000011562 15051152474 033066 0ustar00rootroot0000000 0000000

    ZooKeeper server theory of operation

    ZooKeeperServer is designed to work in standalone mode and also be extensible so that it can be used to implement the quorum based version of ZooKeeper.

    ZooKeeper maintains a order when processing requests:

    • All requests will be processed in order.
    • All responses will return in order.
    • All watches will be sent in the order that the update takes place.

    We will explain the three aspects of ZooKeeperServer: request processing, data structure maintenance, and session tracking.

    Request processing

    Requests are received by the ServerCnxn. Demarshalling of a request is done by ClientRequestHandler. After a request has been demarshalled, ClientRequestHandler invokes the relevant method in ZooKeeper and marshals the result.

    If the request is just a query, it will be processed by ZooKeeper and returned. Otherwise, the request will be validated and a transaction will be generated and logged. This the request will then wait until the request has been logged before continuing processing.

    Requests are logged as a group. Transactions are queued up and the SyncThread will process them at predefined intervals. (Currently 20ms) The SyncThread interacts with ZooKeeperServer the txnQueue. Transactions are added to the txnQueue of SyncThread via queueItem. When the transaction has been synced to disk, its callback will be invoked which will cause the request processing to be completed.

    Data structure maintenance

    ZooKeeper data is stored in-memory. Each znode is stored in a DataNode object. This object is accessed through a hash table that maps paths to DataNodes. DataNodes also organize themselves into a tree. This tree is only used for serializing nodes.

    We guarantee that changes to nodes are stored to non-volatile media before responding to a client. We do this quickly by writing changes as a sequence of transactions in a log file. Even though we flush transactions as a group, we need to avoid seeks as much as possible. Also, since the server can fail at any point, we need to be careful of partial records.

    We address the above problems by

    • Pre-allocating 1M chunks of file space. This allows us to append to the file without causing seeks to update file size. It also means that we need to check for the end of the log by looking for a zero length transaction rather than simply end of file.
    • Writing a signature at the end of each transaction. When processing transactions, we only use transactions that have a valid signature at the end.

    As the server runs, the log file will grow quite large. To avoid long startup times we periodically take a snapshot of the tree of DataNodes. We cannot take the snapshot synchronously as the data takes a while to write out, so instead we asynchronously write out the tree. This means that we end up with a "corrupt" snapshot of the data tree. More formally if we define T to be the real snapshot of the tree at the time we begin taking the snapshot and l as the sequence of transactions that are applied to the tree between the time the snapshot begins and the time the snapshot completes, we write to disk T+l' where l' is a subset of the transactions in l. While we do not have a way of figuring out which transactions make up l', it doesn't really matter. T+l'+l = T+l since the transactions we log are idempotent (applying the transaction multiple times has the same result as applying the transaction once). So when we restore the snapshot we also play all transactions in the log that occur after the snapshot was begun. We can easily figure out where to start the replay because we start a new logfile when we start a snapshot. Both the snapshot file and log file have a numeric suffix that represent the transaction id that created the respective files.

    Session tracking

    Rather than tracking sessions exactly, we track them in batches. That are processed at fixed intervals. This is easier to implement than exact session tracking and it is more efficient in terms of performance. It also provides a small grace period for session renewal. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000170 15051152474 032674 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FilePadding.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FilePa0100644 0000000 0000000 00000010063 15051152474 034207 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FilePadding { private static final Logger LOG; private static long preAllocSize = 65536 * 1024; private static final ByteBuffer fill = ByteBuffer.allocateDirect(1); static { LOG = LoggerFactory.getLogger(FileTxnLog.class); String size = System.getProperty("zookeeper.preAllocSize"); if (size != null) { try { preAllocSize = Long.parseLong(size) * 1024; } catch (NumberFormatException e) { LOG.warn("{} is not a valid value for preAllocSize", size); } } } private long currentSize; /** * Getter of preAllocSize has been added for testing */ public static long getPreAllocSize() { return preAllocSize; } /** * method to allow setting preallocate size * of log file to pad the file. * * @param size the size to set to in bytes */ public static void setPreallocSize(long size) { preAllocSize = size; } public void setCurrentSize(long currentSize) { this.currentSize = currentSize; } /** * pad the current file to increase its size to the next multiple of preAllocSize greater than the current size and position * * @param fileChannel the fileChannel of the file to be padded * @throws IOException */ long padFile(FileChannel fileChannel) throws IOException { return this.padFile(fileChannel, fileChannel.position()); } long padFile(FileChannel fileChannel, long position) throws IOException { long newFileSize = calculateFileSizeWithPadding(position, currentSize, preAllocSize); if (currentSize != newFileSize) { fileChannel.write((ByteBuffer) fill.position(0), newFileSize - fill.remaining()); currentSize = newFileSize; } return currentSize; } /** * Calculates a new file size with padding. We only return a new size if * the current file position is sufficiently close (less than 4K) to end of * file and preAllocSize is > 0. * * @param position the point in the file we have written to * @param fileSize application keeps track of the current file size * @param preAllocSize how many bytes to pad * @return the new file size. It can be the same as fileSize if no * padding was done. */ // VisibleForTesting public static long calculateFileSizeWithPadding(long position, long fileSize, long preAllocSize) { // If preAllocSize is positive and we are within 4KB of the known end of the file calculate a new file size if (preAllocSize > 0 && position + 4096 >= fileSize) { // If we have written more than we have previously preallocated we need to make sure the new // file size is larger than what we already have if (position > fileSize) { fileSize = position + preAllocSize; fileSize -= fileSize % preAllocSize; } else { fileSize += preAllocSize; } } return fileSize; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000165 15051152474 032700 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FileSnap.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FileSn0100644 0000000 0000000 00000025150 15051152474 034232 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.zip.CheckedInputStream; import java.util.zip.CheckedOutputStream; import javax.annotation.Nonnull; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.util.SerializeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class implements the snapshot interface. * it is responsible for storing, serializing * and deserializing the right snapshot. * and provides access to the snapshots. */ public class FileSnap implements SnapShot { File snapDir; SnapshotInfo lastSnapshotInfo = null; private volatile boolean close = false; private static final int VERSION = 2; private static final long dbId = -1; private static final Logger LOG = LoggerFactory.getLogger(FileSnap.class); public static final int SNAP_MAGIC = ByteBuffer.wrap("ZKSN".getBytes()).getInt(); public static final String SNAPSHOT_FILE_PREFIX = "snapshot"; public FileSnap(@Nonnull File snapDir) { this.snapDir = snapDir; } /** * get information of the last saved/restored snapshot * @return info of last snapshot */ public SnapshotInfo getLastSnapshotInfo() { return this.lastSnapshotInfo; } /** * deserialize a data tree from the most recent snapshot * @return the zxid of the snapshot */ public long deserialize(DataTree dt, Map sessions) throws IOException { // we run through 100 snapshots (not all of them) // if we cannot get it running within 100 snapshots // we should give up List snapList = findNValidSnapshots(100); if (snapList.size() == 0) { return -1L; } File snap = null; long snapZxid = -1; boolean foundValid = false; for (int i = 0, snapListSize = snapList.size(); i < snapListSize; i++) { snap = snapList.get(i); LOG.info("Reading snapshot {}", snap); snapZxid = Util.getZxidFromName(snap.getName(), SNAPSHOT_FILE_PREFIX); try (CheckedInputStream snapIS = SnapStream.getInputStream(snap)) { InputArchive ia = BinaryInputArchive.getArchive(snapIS); deserialize(dt, sessions, ia); SnapStream.checkSealIntegrity(snapIS, ia); // Digest feature was added after the CRC to make it backward // compatible, the older code can still read snapshots which // includes digest. // // To check the intact, after adding digest we added another // CRC check. if (dt.deserializeZxidDigest(ia, snapZxid)) { SnapStream.checkSealIntegrity(snapIS, ia); } // deserialize lastProcessedZxid and check inconsistency if (dt.deserializeLastProcessedZxid(ia)) { SnapStream.checkSealIntegrity(snapIS, ia); } foundValid = true; break; } catch (IOException e) { LOG.warn("problem reading snap file {}", snap, e); } } if (!foundValid) { throw new IOException("Not able to find valid snapshots in " + snapDir); } dt.lastProcessedZxid = snapZxid; lastSnapshotInfo = new SnapshotInfo(dt.lastProcessedZxid, snap.lastModified() / 1000); // compare the digest if this is not a fuzzy snapshot, we want to compare // and find inconsistent asap. if (dt.getDigestFromLoadedSnapshot() != null) { dt.compareSnapshotDigests(dt.lastProcessedZxid); } return dt.lastProcessedZxid; } /** * deserialize the datatree from an inputarchive * @param dt the datatree to be serialized into * @param sessions the sessions to be filled up * @param ia the input archive to restore from * @throws IOException */ public static void deserialize(DataTree dt, Map sessions, InputArchive ia) throws IOException { FileHeader header = new FileHeader(); header.deserialize(ia, "fileheader"); if (header.getMagic() != SNAP_MAGIC) { throw new IOException("mismatching magic headers " + header.getMagic() + " != " + FileSnap.SNAP_MAGIC); } SerializeUtils.deserializeSnapshot(dt, ia, sessions); } /** * find the most recent snapshot in the database. * @return the file containing the most recent snapshot */ public File findMostRecentSnapshot() { List files = findNValidSnapshots(1); if (files.size() == 0) { return null; } return files.get(0); } /** * find the last (maybe) valid n snapshots. this does some * minor checks on the validity of the snapshots. It just * checks for / at the end of the snapshot. This does * not mean that the snapshot is truly valid but is * valid with a high probability. also, the most recent * will be first on the list. * @param n the number of most recent snapshots * @return the last n snapshots (the number might be * less than n in case enough snapshots are not available). */ protected List findNValidSnapshots(int n) { List files = Util.sortDataDir(snapDir.listFiles(), SNAPSHOT_FILE_PREFIX, false); int count = 0; List list = new ArrayList<>(); for (File f : files) { // we should catch the exceptions // from the valid snapshot and continue // until we find a valid one try { if (SnapStream.isValidSnapshot(f)) { list.add(f); count++; if (count == n) { break; } } } catch (IOException e) { LOG.warn("invalid snapshot {}", f, e); } } return list; } /** * find the last n snapshots. this does not have * any checks if the snapshot might be valid or not * @param n the number of most recent snapshots * @return the last n snapshots * @throws IOException */ public List findNRecentSnapshots(int n) throws IOException { List files = Util.sortDataDir(snapDir.listFiles(), SNAPSHOT_FILE_PREFIX, false); int count = 0; List list = new ArrayList<>(); for (File f : files) { if (count == n) { break; } if (Util.getZxidFromName(f.getName(), SNAPSHOT_FILE_PREFIX) != -1) { count++; list.add(f); } } return list; } /** * serialize the datatree and sessions * @param dt the datatree to be serialized * @param sessions the sessions to be serialized * @param oa the output archive to serialize into * @param header the header of this snapshot * @throws IOException */ protected void serialize( DataTree dt, Map sessions, OutputArchive oa, FileHeader header) throws IOException { // this is really a programmatic error and not something that can // happen at runtime if (header == null) { throw new IllegalStateException("Snapshot's not open for writing: uninitialized header"); } header.serialize(oa, "fileheader"); SerializeUtils.serializeSnapshot(dt, oa, sessions); } /** * serialize the datatree and session into the file snapshot * @param dt the datatree to be serialized * @param sessions the sessions to be serialized * @param snapShot the file to store snapshot into * @param fsync sync the file immediately after write */ public synchronized void serialize( DataTree dt, Map sessions, File snapShot, boolean fsync) throws IOException { if (!close) { try (CheckedOutputStream snapOS = SnapStream.getOutputStream(snapShot, fsync)) { OutputArchive oa = BinaryOutputArchive.getArchive(snapOS); FileHeader header = new FileHeader(SNAP_MAGIC, VERSION, dbId); serialize(dt, sessions, oa, header); SnapStream.sealStream(snapOS, oa); // Digest feature was added after the CRC to make it backward // compatible, the older code cal still read snapshots which // includes digest. // // To check the intact, after adding digest we added another // CRC check. if (dt.serializeZxidDigest(oa)) { SnapStream.sealStream(snapOS, oa); } // serialize the last processed zxid and add another CRC check if (dt.serializeLastProcessedZxid(oa)) { SnapStream.sealStream(snapOS, oa); } lastSnapshotInfo = new SnapshotInfo( Util.getZxidFromName(snapShot.getName(), SNAPSHOT_FILE_PREFIX), snapShot.lastModified() / 1000); } } else { throw new IOException("FileSnap has already been closed"); } } /** * synchronized close just so that if serialize is in place * the close operation will block and will wait till serialize * is done and will set the close flag */ @Override public synchronized void close() throws IOException { close = true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000167 15051152474 032702 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FileTxnLog.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FileTx0100644 0000000 0000000 00000070741 15051152474 034253 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.TimeUnit; import java.util.zip.Adler32; import java.util.zip.Checksum; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class implements the TxnLog interface. It provides api's * to access the txnlogs and add entries to it. *

    * The format of a Transactional log is as follows: *

     * LogFile:
     *     FileHeader TxnList ZeroPad
     *
     * FileHeader: {
     *     magic 4bytes (ZKLG)
     *     version 4bytes
     *     dbid 8bytes
     *   }
     *
     * TxnList:
     *     Txn || Txn TxnList
     *
     * Txn:
     *     checksum Txnlen TxnHeader Record 0x42
     *
     * checksum: 8bytes Adler32 is currently used
     *   calculated across payload -- Txnlen, TxnHeader, Record and 0x42
     *
     * Txnlen:
     *     len 4bytes
     *
     * TxnHeader: {
     *     sessionid 8bytes
     *     cxid 4bytes
     *     zxid 8bytes
     *     time 8bytes
     *     type 4bytes
     *   }
     *
     * Record:
     *     See Jute definition file for details on the various record types
     *
     * ZeroPad:
     *     0 padded to EOF (filled during preallocation stage)
     * 
    */ public class FileTxnLog implements TxnLog, Closeable { private static final Logger LOG; public static final int TXNLOG_MAGIC = ByteBuffer.wrap("ZKLG".getBytes()).getInt(); public static final int VERSION = 2; public static final String LOG_FILE_PREFIX = "log"; static final String FSYNC_WARNING_THRESHOLD_MS_PROPERTY = "fsync.warningthresholdms"; static final String ZOOKEEPER_FSYNC_WARNING_THRESHOLD_MS_PROPERTY = "zookeeper." + FSYNC_WARNING_THRESHOLD_MS_PROPERTY; /** Maximum time we allow for elapsed fsync before WARNing */ private static final long fsyncWarningThresholdMS; /** * This parameter limit the size of each txnlog to a given limit (KB). * It does not affect how often the system will take a snapshot [zookeeper.snapCount] * We roll the txnlog when either of the two limits are reached. * Also since we only roll the logs at transaction boundaries, actual file size can exceed * this limit by the maximum size of a serialized transaction. * The feature is disabled by default (-1) */ private static final String txnLogSizeLimitSetting = "zookeeper.txnLogSizeLimitInKb"; /** * The actual txnlog size limit in bytes. */ private static long txnLogSizeLimit = -1; static { LOG = LoggerFactory.getLogger(FileTxnLog.class); /** Local variable to read fsync.warningthresholdms into */ Long fsyncWarningThreshold; if ((fsyncWarningThreshold = Long.getLong(ZOOKEEPER_FSYNC_WARNING_THRESHOLD_MS_PROPERTY)) == null) { fsyncWarningThreshold = Long.getLong(FSYNC_WARNING_THRESHOLD_MS_PROPERTY, 1000); } fsyncWarningThresholdMS = fsyncWarningThreshold; Long logSize = Long.getLong(txnLogSizeLimitSetting, -1); if (logSize > 0) { LOG.info("{} = {}", txnLogSizeLimitSetting, logSize); // Convert to bytes logSize = logSize * 1024; txnLogSizeLimit = logSize; } } long lastZxidSeen; volatile BufferedOutputStream logStream = null; volatile OutputArchive oa; volatile FileOutputStream fos = null; File logDir; private final boolean forceSync = !System.getProperty("zookeeper.forceSync", "yes").equals("no"); long dbId; private final Queue streamsToFlush = new ArrayDeque<>(); File logFileWrite = null; private FilePadding filePadding = new FilePadding(); private ServerStats serverStats; private volatile long syncElapsedMS = -1L; /** * A running total of all complete log files * This does not include the current file being written to */ private long prevLogsRunningTotal; long filePosition = 0; private long unFlushedSize = 0; private long fileSize = 0; /** * constructor for FileTxnLog. Take the directory * where the txnlogs are stored * @param logDir the directory where the txnlogs are stored */ public FileTxnLog(File logDir) { this.logDir = logDir; } /** * method to allow setting preallocate size * of log file to pad the file. * @param size the size to set to in bytes */ public static void setPreallocSize(long size) { FilePadding.setPreallocSize(size); } /** * Setter for ServerStats to monitor fsync threshold exceed * @param serverStats used to update fsyncThresholdExceedCount */ @Override public synchronized void setServerStats(ServerStats serverStats) { this.serverStats = serverStats; } /** * Set log size limit */ public static void setTxnLogSizeLimit(long size) { txnLogSizeLimit = size; } /** * Return the current on-disk size of log size. This will be accurate only * after commit() is called. Otherwise, unflushed txns may not be included. */ public synchronized long getCurrentLogSize() { if (logFileWrite != null) { return fileSize; } return 0; } public synchronized void setTotalLogSize(long size) { prevLogsRunningTotal = size; } public synchronized long getTotalLogSize() { return prevLogsRunningTotal + getCurrentLogSize(); } /** * Get log size limit */ public static long getTxnLogSizeLimit() { return txnLogSizeLimit; } /** * creates a checksum algorithm to be used * @return the checksum used for this txnlog */ protected Checksum makeChecksumAlgorithm() { return new Adler32(); } /** * rollover the current log file to a new one. * @throws IOException */ public synchronized void rollLog() throws IOException { if (logStream != null) { this.logStream.flush(); prevLogsRunningTotal += getCurrentLogSize(); this.logStream = null; oa = null; fileSize = 0; filePosition = 0; unFlushedSize = 0; // Roll over the current log file into the running total } } /** * close all the open file handles * @throws IOException */ public synchronized void close() throws IOException { if (logStream != null) { logStream.close(); } for (FileOutputStream log : streamsToFlush) { log.close(); } } @Override public synchronized boolean append(Request request) throws IOException { TxnHeader hdr = request.getHdr(); if (hdr == null) { return false; } if (hdr.getZxid() <= lastZxidSeen) { LOG.warn( "Current zxid {} is <= {} for {}", hdr.getZxid(), lastZxidSeen, Request.op2String(hdr.getType())); } else { lastZxidSeen = hdr.getZxid(); } if (logStream == null) { LOG.info("Creating new log file: {}", Util.makeLogName(hdr.getZxid())); logFileWrite = new File(logDir, Util.makeLogName(hdr.getZxid())); fos = new FileOutputStream(logFileWrite); logStream = new BufferedOutputStream(fos); oa = BinaryOutputArchive.getArchive(logStream); FileHeader fhdr = new FileHeader(TXNLOG_MAGIC, VERSION, dbId); long dataSize = oa.getDataSize(); fhdr.serialize(oa, "fileheader"); // Make sure that the magic number is written before padding. logStream.flush(); // Before writing data, first obtain the size of the OutputArchive. // After writing the data, obtain the size of the OutputArchive again, // so we can obtain the size of the data written this time. // In this case, the data already flush into the channel, so add the size to filePosition. filePosition += oa.getDataSize() - dataSize; filePadding.setCurrentSize(filePosition); streamsToFlush.add(fos); } fileSize = filePadding.padFile(fos.getChannel(), filePosition); byte[] buf = request.getSerializeData(); if (buf == null || buf.length == 0) { throw new IOException("Faulty serialization for header " + "and txn"); } long dataSize = oa.getDataSize(); Checksum crc = makeChecksumAlgorithm(); crc.update(buf, 0, buf.length); oa.writeLong(crc.getValue(), "txnEntryCRC"); Util.writeTxnBytes(oa, buf); // Before writing data, first obtain the size of the OutputArchive. // After writing the data, obtain the size of the OutputArchive again, // so we can obtain the size of the data written this time. // In this case, the data just write to the cache, not flushed, so add the size to unFlushedSize. // After flushed, the unFlushedSize will add to the filePosition. unFlushedSize += oa.getDataSize() - dataSize; return true; } /** * Find the log file that starts at, or just before, the snapshot. Return * this and all subsequent logs. Results are ordered by zxid of file, * ascending order. * @param logDirList array of files * @param snapshotZxid return files at, or before this zxid * @return log files that starts at, or just before, the snapshot and subsequent ones */ public static File[] getLogFiles(File[] logDirList, long snapshotZxid) { List files = Util.sortDataDir(logDirList, LOG_FILE_PREFIX, true); long logZxid = 0; // Find the log file that starts before or at the same time as the // zxid of the snapshot for (File f : files) { long fzxid = Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX); if (fzxid > snapshotZxid) { break; } // the files // are sorted with zxid's if (fzxid > logZxid) { logZxid = fzxid; } } List v = new ArrayList<>(5); for (File f : files) { long fzxid = Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX); if (fzxid < logZxid) { continue; } v.add(f); } return v.toArray(new File[0]); } /** * get the last zxid that was logged in the transaction logs * @return the last zxid logged in the transaction logs */ public long getLastLoggedZxid() { File[] files = getLogFiles(logDir.listFiles(), 0); long maxLog = files.length > 0 ? Util.getZxidFromName(files[files.length - 1].getName(), LOG_FILE_PREFIX) : -1; // if a log file is more recent we must scan it to find // the highest zxid long zxid = maxLog; try (FileTxnLog txn = new FileTxnLog(logDir); TxnIterator itr = txn.read(maxLog)) { while (true) { if (!itr.next()) { break; } TxnHeader hdr = itr.getHeader(); zxid = hdr.getZxid(); } } catch (IOException e) { LOG.warn("Unexpected exception", e); } return zxid; } /** * commit the logs. make sure that everything hits the * disk */ public synchronized void commit() throws IOException { if (logStream != null) { logStream.flush(); filePosition += unFlushedSize; // If we have written more than we have previously preallocated, // we should override the fileSize by filePosition. if (filePosition > fileSize) { fileSize = filePosition; } unFlushedSize = 0; } for (FileOutputStream log : streamsToFlush) { log.flush(); if (forceSync) { long startSyncNS = System.nanoTime(); FileChannel channel = log.getChannel(); channel.force(false); syncElapsedMS = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startSyncNS); if (syncElapsedMS > fsyncWarningThresholdMS) { if (serverStats != null) { serverStats.incrementFsyncThresholdExceedCount(); } LOG.warn( "fsync-ing the write ahead log in {} took {}ms which will adversely effect operation latency." + "File size is {} bytes. See the ZooKeeper troubleshooting guide", Thread.currentThread().getName(), syncElapsedMS, channel.size()); } ServerMetrics.getMetrics().FSYNC_TIME.add(syncElapsedMS); } } while (streamsToFlush.size() > 1) { streamsToFlush.poll().close(); } // Roll the log file if we exceed the size limit if (txnLogSizeLimit > 0) { long logSize = getCurrentLogSize(); if (logSize > txnLogSizeLimit) { LOG.debug("Log size limit reached: {}", logSize); rollLog(); } } } /** * * @return elapsed sync time of transaction log in milliseconds */ public long getTxnLogSyncElapsedTime() { return syncElapsedMS; } /** * start reading all the transactions from the given zxid * @param zxid the zxid to start reading transactions from * @return returns an iterator to iterate through the transaction * logs */ public TxnIterator read(long zxid) throws IOException { return read(zxid, true); } /** * start reading all the transactions from the given zxid. * * @param zxid the zxid to start reading transactions from * @param fastForward true if the iterator should be fast forwarded to point * to the txn of a given zxid, else the iterator will point to the * starting txn of a txnlog that may contain txn of a given zxid * @return returns an iterator to iterate through the transaction logs */ public TxnIterator read(long zxid, boolean fastForward) throws IOException { return new FileTxnIterator(logDir, zxid, fastForward); } /** * truncate the current transaction logs * @param zxid the zxid to truncate the logs to * @return true if successful false if not */ public boolean truncate(long zxid) throws IOException { try (FileTxnIterator itr = new FileTxnIterator(this.logDir, zxid)) { PositionInputStream input = itr.inputStream; if (input == null) { throw new IOException("No log files found to truncate! This could " + "happen if you still have snapshots from an old setup or " + "log files were deleted accidentally or dataLogDir was changed in zoo.cfg."); } long pos = input.getPosition(); // now, truncate at the current position RandomAccessFile raf = new RandomAccessFile(itr.logFile, "rw"); raf.setLength(pos); raf.close(); while (itr.goToNextLog()) { if (!itr.logFile.delete()) { LOG.warn("Unable to truncate {}", itr.logFile); } } } return true; } /** * read the header of the transaction file * @param file the transaction file to read * @return header that was read from the file * @throws IOException */ private static FileHeader readHeader(File file) throws IOException { InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(file)); InputArchive ia = BinaryInputArchive.getArchive(is); FileHeader hdr = new FileHeader(); hdr.deserialize(ia, "fileheader"); return hdr; } finally { try { if (is != null) { is.close(); } } catch (IOException e) { LOG.warn("Ignoring exception during close", e); } } } /** * the dbid of this transaction database * @return the dbid of this database */ public long getDbId() throws IOException { FileTxnIterator itr = new FileTxnIterator(logDir, 0); FileHeader fh = readHeader(itr.logFile); itr.close(); if (fh == null) { throw new IOException("Unsupported Format."); } return fh.getDbid(); } /** * the forceSync value. true if forceSync is enabled, false otherwise. * @return the forceSync value */ public boolean isForceSync() { return forceSync; } /** * a class that keeps track of the position * in the input stream. The position points to offset * that has been consumed by the applications. It can * wrap buffered input streams to provide the right offset * for the application. */ static class PositionInputStream extends FilterInputStream { long position; protected PositionInputStream(InputStream in) { super(in); position = 0; } @Override public int read() throws IOException { int rc = super.read(); if (rc > -1) { position++; } return rc; } public int read(byte[] b) throws IOException { int rc = super.read(b); if (rc > 0) { position += rc; } return rc; } @Override public int read(byte[] b, int off, int len) throws IOException { int rc = super.read(b, off, len); if (rc > 0) { position += rc; } return rc; } @Override public long skip(long n) throws IOException { long rc = super.skip(n); if (rc > 0) { position += rc; } return rc; } public long getPosition() { return position; } @Override public boolean markSupported() { return false; } @Override public void mark(int readLimit) { throw new UnsupportedOperationException("mark"); } @Override public void reset() { throw new UnsupportedOperationException("reset"); } } /** * this class implements the txnlog iterator interface * which is used for reading the transaction logs */ public static class FileTxnIterator implements TxnLog.TxnIterator { File logDir; long zxid; TxnHeader hdr; Record record; TxnDigest digest; File logFile; InputArchive ia; static final String CRC_ERROR = "CRC check failed"; PositionInputStream inputStream = null; //stored files is the list of files greater than //the zxid we are looking for. private ArrayList storedFiles; /** * create an iterator over a transaction database directory * @param logDir the transaction database directory * @param zxid the zxid to start reading from * @param fastForward true if the iterator should be fast forwarded to * point to the txn of a given zxid, else the iterator will * point to the starting txn of a txnlog that may contain txn of * a given zxid * @throws IOException */ public FileTxnIterator(File logDir, long zxid, boolean fastForward) throws IOException { this.logDir = logDir; this.zxid = zxid; init(); if (fastForward && hdr != null) { while (hdr.getZxid() < zxid) { if (!next()) { break; } } } } /** * create an iterator over a transaction database directory * @param logDir the transaction database directory * @param zxid the zxid to start reading from * @throws IOException */ public FileTxnIterator(File logDir, long zxid) throws IOException { this(logDir, zxid, true); } /** * initialize to the zxid specified * this is inclusive of the zxid * @throws IOException */ void init() throws IOException { storedFiles = new ArrayList<>(); List files = Util.sortDataDir( FileTxnLog.getLogFiles(logDir.listFiles(), 0), LOG_FILE_PREFIX, false); for (File f : files) { if (Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX) >= zxid) { storedFiles.add(f); } else if (Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX) < zxid) { // add the last logfile that is less than the zxid storedFiles.add(f); break; } } goToNextLog(); next(); } /** * Return total storage size of txnlog that will return by this iterator. */ public long getStorageSize() { long sum = 0; for (File f : storedFiles) { sum += f.length(); } return sum; } /** * go to the next logfile * * @return true if there is one and false if there is no * new file to be read */ private boolean goToNextLog() throws IOException { if (!storedFiles.isEmpty()) { this.logFile = storedFiles.remove(storedFiles.size() - 1); try { ia = createInputArchive(this.logFile); } catch (EOFException ex) { // If this file is the last log file in the database and is empty, // it means that the last time the file was created // before the header was written. if (storedFiles.isEmpty() && this.logFile.length() == 0) { boolean deleted = this.logFile.delete(); if (!deleted) { throw new IOException("Failed to delete empty tail log file: " + this.logFile.getName()); } LOG.warn("Delete empty tail log file to recover from corruption file: {}", this.logFile.getName()); return false; } throw ex; } return true; } return false; } /** * read the header from the inputarchive * @param ia the inputarchive to be read from * @param is the inputstream * @throws IOException */ protected void inStreamCreated(InputArchive ia, InputStream is) throws IOException { FileHeader header = new FileHeader(); header.deserialize(ia, "fileheader"); if (header.getMagic() != FileTxnLog.TXNLOG_MAGIC) { throw new IOException("Transaction log: " + this.logFile + " has invalid magic number " + header.getMagic() + " != " + FileTxnLog.TXNLOG_MAGIC); } } /** * Invoked to indicate that the input stream has been created. * @param logFile the file to read. * @throws IOException **/ protected InputArchive createInputArchive(File logFile) throws IOException { if (inputStream == null) { inputStream = new PositionInputStream(new BufferedInputStream(new FileInputStream(logFile))); LOG.debug("Created new input stream: {}", logFile); ia = BinaryInputArchive.getArchive(inputStream); inStreamCreated(ia, inputStream); LOG.debug("Created new input archive: {}", logFile); } return ia; } /** * create a checksum algorithm * @return the checksum algorithm */ protected Checksum makeChecksumAlgorithm() { return new Adler32(); } /** * the iterator that moves to the next transaction * @return true if there is more transactions to be read * false if not. */ public boolean next() throws IOException { if (ia == null) { return false; } try { long crcValue = ia.readLong("crcvalue"); byte[] bytes = Util.readTxnBytes(ia); // Since we preallocate, we define EOF to be an if (bytes == null || bytes.length == 0) { throw new EOFException("Failed to read " + logFile); } // EOF or corrupted record // validate CRC Checksum crc = makeChecksumAlgorithm(); crc.update(bytes, 0, bytes.length); if (crcValue != crc.getValue()) { throw new IOException(CRC_ERROR); } TxnLogEntry logEntry = SerializeUtils.deserializeTxn(bytes); hdr = logEntry.getHeader(); record = logEntry.getTxn(); digest = logEntry.getDigest(); } catch (EOFException e) { LOG.debug("EOF exception", e); inputStream.close(); inputStream = null; ia = null; hdr = null; // this means that the file has ended // we should go to the next file if (!goToNextLog()) { return false; } // if we went to the next log file, we should call next() again return next(); } catch (IOException e) { inputStream.close(); throw e; } return true; } /** * return the current header * @return the current header that * is read */ public TxnHeader getHeader() { return hdr; } /** * return the current transaction * @return the current transaction * that is read */ public Record getTxn() { return record; } public TxnDigest getDigest() { return digest; } /** * close the iterator * and release the resources. */ public void close() throws IOException { if (inputStream != null) { inputStream.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000173 15051152474 032677 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FileTxnSnapLog.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/FileTx0100644 0000000 0000000 00000060510 15051152474 034244 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.nio.file.Files; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.jute.Record; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a helper class * above the implementations * of txnlog and snapshot * classes */ public class FileTxnSnapLog { //the directory containing the //the transaction logs final File dataDir; //the directory containing the //the snapshot directory final File snapDir; TxnLog txnLog; SnapShot snapLog; private final boolean autoCreateDB; private final boolean trustEmptySnapshot; public static final int VERSION = 2; public static final String version = "version-"; private static final Logger LOG = LoggerFactory.getLogger(FileTxnSnapLog.class); public static final String ZOOKEEPER_DATADIR_AUTOCREATE = "zookeeper.datadir.autocreate"; public static final String ZOOKEEPER_DATADIR_AUTOCREATE_DEFAULT = "true"; static final String ZOOKEEPER_DB_AUTOCREATE = "zookeeper.db.autocreate"; private static final String ZOOKEEPER_DB_AUTOCREATE_DEFAULT = "true"; public static final String ZOOKEEPER_SNAPSHOT_TRUST_EMPTY = "zookeeper.snapshot.trust.empty"; private static final String EMPTY_SNAPSHOT_WARNING = "No snapshot found, but there are log entries. "; /** * This listener helps * the external apis calling * restore to gather information * while the data is being * restored. */ public interface PlayBackListener { void onTxnLoaded(TxnHeader hdr, Record rec, TxnDigest digest); } /** * Finalizing restore of data tree through * a set of operations (replaying transaction logs, * calculating data tree digests, and so on.). */ private interface RestoreFinalizer { /** * @return the highest zxid of restored data tree. */ long run() throws IOException; } /** * the constructor which takes the datadir and * snapdir. * @param dataDir the transaction directory * @param snapDir the snapshot directory */ public FileTxnSnapLog(File dataDir, File snapDir) throws IOException { LOG.debug("Opening datadir:{} snapDir:{}", dataDir, snapDir); this.dataDir = new File(dataDir, version + VERSION); this.snapDir = new File(snapDir, version + VERSION); // by default create snap/log dirs, but otherwise complain instead // See ZOOKEEPER-1161 for more details boolean enableAutocreate = Boolean.parseBoolean( System.getProperty(ZOOKEEPER_DATADIR_AUTOCREATE, ZOOKEEPER_DATADIR_AUTOCREATE_DEFAULT)); trustEmptySnapshot = Boolean.getBoolean(ZOOKEEPER_SNAPSHOT_TRUST_EMPTY); LOG.info("{} : {}", ZOOKEEPER_SNAPSHOT_TRUST_EMPTY, trustEmptySnapshot); if (!this.dataDir.exists()) { if (!enableAutocreate) { throw new DatadirException(String.format( "Missing data directory %s, automatic data directory creation is disabled (%s is false)." + " Please create this directory manually.", this.dataDir, ZOOKEEPER_DATADIR_AUTOCREATE)); } if (!this.dataDir.mkdirs() && !this.dataDir.exists()) { throw new DatadirException("Unable to create data directory " + this.dataDir); } } if (!this.dataDir.canWrite()) { throw new DatadirException("Cannot write to data directory " + this.dataDir); } if (!this.snapDir.exists()) { // by default create this directory, but otherwise complain instead // See ZOOKEEPER-1161 for more details if (!enableAutocreate) { throw new DatadirException(String.format( "Missing snap directory %s, automatic data directory creation is disabled (%s is false)." + "Please create this directory manually.", this.snapDir, ZOOKEEPER_DATADIR_AUTOCREATE)); } if (!this.snapDir.mkdirs() && !this.snapDir.exists()) { throw new DatadirException("Unable to create snap directory " + this.snapDir); } } if (!this.snapDir.canWrite()) { throw new DatadirException("Cannot write to snap directory " + this.snapDir); } // check content of transaction log and snapshot dirs if they are two different directories // See ZOOKEEPER-2967 for more details if (!this.dataDir.getPath().equals(this.snapDir.getPath())) { checkLogDir(); checkSnapDir(); } txnLog = new FileTxnLog(this.dataDir); snapLog = new FileSnap(this.snapDir); autoCreateDB = Boolean.parseBoolean( System.getProperty(ZOOKEEPER_DB_AUTOCREATE, ZOOKEEPER_DB_AUTOCREATE_DEFAULT)); } public void setServerStats(ServerStats serverStats) { txnLog.setServerStats(serverStats); } private void checkLogDir() throws LogDirContentCheckException { File[] files = this.dataDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return Util.isSnapshotFileName(name); } }); if (files != null && files.length > 0) { throw new LogDirContentCheckException( "Log directory has snapshot files. Check if dataLogDir and dataDir configuration is correct."); } } private void checkSnapDir() throws SnapDirContentCheckException { File[] files = this.snapDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return Util.isLogFileName(name); } }); if (files != null && files.length > 0) { throw new SnapDirContentCheckException( "Snapshot directory has log files. Check if dataLogDir and dataDir configuration is correct."); } } /** * get the data log dir used by this filetxn * snap log * @return the data log dir */ public File getDataLogDir() { return this.dataDir; } /** * get the snap dir used by this * filetxn snap log * @return the snap dir */ public File getSnapDir() { return this.snapDir; } /** * get information of the last saved/restored snapshot * @return info of last snapshot */ public SnapshotInfo getLastSnapshotInfo() { return this.snapLog.getLastSnapshotInfo(); } /** * whether to force the write of an initial snapshot after a leader election, * to address ZOOKEEPER-3781 after upgrading from Zookeeper 3.4.x. * @return true if an initial snapshot should be written even if not otherwise required, false otherwise. */ public boolean shouldForceWriteInitialSnapshotAfterLeaderElection() { return trustEmptySnapshot && getLastSnapshotInfo() == null; } /** * this function restores the server * database after reading from the * snapshots and transaction logs * @param dt the datatree to be restored * @param sessions the sessions to be restored * @param listener the playback listener to run on the * database restoration * @return the highest zxid restored * @throws IOException */ public long restore(DataTree dt, Map sessions, PlayBackListener listener) throws IOException { long snapLoadingStartTime = Time.currentElapsedTime(); long deserializeResult = snapLog.deserialize(dt, sessions); ServerMetrics.getMetrics().STARTUP_SNAP_LOAD_TIME.add(Time.currentElapsedTime() - snapLoadingStartTime); FileTxnLog txnLog = new FileTxnLog(dataDir); boolean trustEmptyDB; File initFile = new File(dataDir.getParent(), "initialize"); if (Files.deleteIfExists(initFile.toPath())) { LOG.info("Initialize file found, an empty database will not block voting participation"); trustEmptyDB = true; } else { trustEmptyDB = autoCreateDB; } RestoreFinalizer finalizer = () -> { long highestZxid = fastForwardFromEdits(dt, sessions, listener); // The snapshotZxidDigest will reset after replaying the txn of the // zxid in the snapshotZxidDigest, if it's not reset to null after // restoring, it means either there are not enough txns to cover that // zxid or that txn is missing DataTree.ZxidDigest snapshotZxidDigest = dt.getDigestFromLoadedSnapshot(); if (snapshotZxidDigest != null) { LOG.warn( "Highest txn zxid 0x{} is not covering the snapshot digest zxid 0x{}, " + "which might lead to inconsistent state", Long.toHexString(highestZxid), Long.toHexString(snapshotZxidDigest.getZxid())); } return highestZxid; }; if (-1L == deserializeResult) { /* this means that we couldn't find any snapshot, so we need to * initialize an empty database (reported in ZOOKEEPER-2325) */ if (txnLog.getLastLoggedZxid() != -1) { // ZOOKEEPER-3056: provides an escape hatch for users upgrading // from old versions of zookeeper (3.4.x, pre 3.5.3). if (!trustEmptySnapshot) { throw new IOException(EMPTY_SNAPSHOT_WARNING + "Something is broken!"); } else { LOG.warn("{}This should only be allowed during upgrading.", EMPTY_SNAPSHOT_WARNING); return finalizer.run(); } } if (trustEmptyDB) { /* TODO: (br33d) we should either put a ConcurrentHashMap on restore() * or use Map on save() */ save(dt, (ConcurrentHashMap) sessions, false); /* return a zxid of 0, since we know the database is empty */ return 0L; } else { /* return a zxid of -1, since we are possibly missing data */ LOG.warn("Unexpected empty data tree, setting zxid to -1"); dt.lastProcessedZxid = -1L; return -1L; } } return finalizer.run(); } /** * This function will fast-forward the server database to have the latest * transactions in it. This is the same as restore, but only reads from * the transaction logs and not restores from a snapshot. * @param dt the datatree to write transactions to. * @param sessions the sessions to be restored. * @param listener the playback listener to run on the * database transactions. * @return the highest zxid restored. * @throws IOException */ public long fastForwardFromEdits( DataTree dt, Map sessions, PlayBackListener listener) throws IOException { TxnIterator itr = txnLog.read(dt.lastProcessedZxid + 1); long highestZxid = dt.lastProcessedZxid; TxnHeader hdr; int txnLoaded = 0; long startTime = Time.currentElapsedTime(); try { while (true) { // iterator points to // the first valid txn when initialized hdr = itr.getHeader(); if (hdr == null) { //empty logs return dt.lastProcessedZxid; } if (hdr.getZxid() < highestZxid && highestZxid != 0) { LOG.error("{}(highestZxid) > {}(next log) for type {}", highestZxid, hdr.getZxid(), hdr.getType()); } else { highestZxid = hdr.getZxid(); } try { processTransaction(hdr, dt, sessions, itr.getTxn()); dt.compareDigest(hdr, itr.getTxn(), itr.getDigest()); txnLoaded++; } catch (KeeperException.NoNodeException e) { throw new IOException("Failed to process transaction type: " + hdr.getType() + " error: " + e.getMessage(), e); } listener.onTxnLoaded(hdr, itr.getTxn(), itr.getDigest()); if (!itr.next()) { break; } } } finally { if (itr != null) { itr.close(); } } long loadTime = Time.currentElapsedTime() - startTime; LOG.info("{} txns loaded in {} ms", txnLoaded, loadTime); ServerMetrics.getMetrics().STARTUP_TXNS_LOADED.add(txnLoaded); ServerMetrics.getMetrics().STARTUP_TXNS_LOAD_TIME.add(loadTime); return highestZxid; } /** * Get TxnIterator for iterating through txnlog starting at a given zxid * * @param zxid starting zxid * @return TxnIterator * @throws IOException */ public TxnIterator readTxnLog(long zxid) throws IOException { return readTxnLog(zxid, true); } /** * Get TxnIterator for iterating through txnlog starting at a given zxid * * @param zxid starting zxid * @param fastForward true if the iterator should be fast forwarded to point * to the txn of a given zxid, else the iterator will point to the * starting txn of a txnlog that may contain txn of a given zxid * @return TxnIterator * @throws IOException */ public TxnIterator readTxnLog(long zxid, boolean fastForward) throws IOException { FileTxnLog txnLog = new FileTxnLog(dataDir); return txnLog.read(zxid, fastForward); } /** * process the transaction on the datatree * @param hdr the hdr of the transaction * @param dt the datatree to apply transaction to * @param sessions the sessions to be restored * @param txn the transaction to be applied */ public void processTransaction( TxnHeader hdr, DataTree dt, Map sessions, Record txn) throws KeeperException.NoNodeException { ProcessTxnResult rc; switch (hdr.getType()) { case OpCode.createSession: sessions.put(hdr.getClientId(), ((CreateSessionTxn) txn).getTimeOut()); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "playLog --- create session in log: 0x" + Long.toHexString(hdr.getClientId()) + " with timeout: " + ((CreateSessionTxn) txn).getTimeOut()); } // give dataTree a chance to sync its lastProcessedZxid rc = dt.processTxn(hdr, txn); break; case OpCode.closeSession: sessions.remove(hdr.getClientId()); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "playLog --- close session in log: 0x" + Long.toHexString(hdr.getClientId())); } rc = dt.processTxn(hdr, txn); break; default: rc = dt.processTxn(hdr, txn); } /* * Snapshots are lazily created. So when a snapshot is in progress, * there is a chance for later transactions to make into the * snapshot. Then when the snapshot is restored, NONODE/NODEEXISTS * errors could occur. It should be safe to ignore these. */ if (rc.err != Code.OK.intValue()) { LOG.debug("Ignoring processTxn failure hdr: {}, error: {}, path: {}", hdr.getType(), rc.err, rc.path); } } /** * the last logged zxid on the transaction logs * @return the last logged zxid */ public long getLastLoggedZxid() { FileTxnLog txnLog = new FileTxnLog(dataDir); return txnLog.getLastLoggedZxid(); } /** * save the datatree and the sessions into a snapshot * @param dataTree the datatree to be serialized onto disk * @param sessionsWithTimeouts the session timeouts to be * serialized onto disk * @param syncSnap sync the snapshot immediately after write * @return the snapshot file * @throws IOException */ public File save( DataTree dataTree, ConcurrentHashMap sessionsWithTimeouts, boolean syncSnap) throws IOException { long lastZxid = dataTree.lastProcessedZxid; File snapshotFile = new File(snapDir, Util.makeSnapshotName(lastZxid)); LOG.info("Snapshotting: 0x{} to {}", Long.toHexString(lastZxid), snapshotFile); try { snapLog.serialize(dataTree, sessionsWithTimeouts, snapshotFile, syncSnap); return snapshotFile; } catch (IOException e) { if (snapshotFile.length() == 0) { /* This may be caused by a full disk. In such a case, the server * will get stuck in a loop where it tries to write a snapshot * out to disk, and ends up creating an empty file instead. * Doing so will eventually result in valid snapshots being * removed during cleanup. */ if (snapshotFile.delete()) { LOG.info("Deleted empty snapshot file: {}", snapshotFile.getAbsolutePath()); } else { LOG.warn("Could not delete empty snapshot file: {}", snapshotFile.getAbsolutePath()); } } else { /* Something else went wrong when writing the snapshot out to * disk. If this snapshot file is invalid, when restarting, * ZooKeeper will skip it, and find the last known good snapshot * instead. */ } throw e; } } /** * truncate the transaction logs the zxid * specified * @param zxid the zxid to truncate the logs to * @return true if able to truncate the log, false if not * @throws IOException */ public boolean truncateLog(long zxid) { try { // close the existing txnLog and snapLog close(); // truncate it try (FileTxnLog truncLog = new FileTxnLog(dataDir)) { boolean truncated = truncLog.truncate(zxid); // re-open the txnLog and snapLog // I'd rather just close/reopen this object itself, however that // would have a big impact outside ZKDatabase as there are other // objects holding a reference to this object. txnLog = new FileTxnLog(dataDir); snapLog = new FileSnap(snapDir); return truncated; } } catch (IOException e) { LOG.error("Unable to truncate Txn log", e); return false; } } /** * the most recent snapshot in the snapshot * directory * @return the file that contains the most * recent snapshot * @throws IOException */ public File findMostRecentSnapshot() throws IOException { FileSnap snaplog = new FileSnap(snapDir); return snaplog.findMostRecentSnapshot(); } /** * the n most recent snapshots * @param n the number of recent snapshots * @return the list of n most recent snapshots, with * the most recent in front * @throws IOException */ public List findNRecentSnapshots(int n) throws IOException { FileSnap snaplog = new FileSnap(snapDir); return snaplog.findNRecentSnapshots(n); } /** * the n recent valid snapshots * @param n the number of recent valid snapshots * @return the list of n recent valid snapshots, with * the most recent in front */ public List findNValidSnapshots(int n) { FileSnap snaplog = new FileSnap(snapDir); return snaplog.findNValidSnapshots(n); } /** * get the snapshot logs which may contain transactions newer than the given zxid. * This includes logs with starting zxid greater than given zxid, as well as the * newest transaction log with starting zxid less than given zxid. The latter log * file may contain transactions beyond given zxid. * @param zxid the zxid that contains logs greater than * zxid * @return the snapshot logs which may contain transactions newer than the given zxid */ public File[] getSnapshotLogs(long zxid) { return FileTxnLog.getLogFiles(dataDir.listFiles(), zxid); } /** * append the request to the transaction logs * @param si the request to be appended * @return true iff something appended, otw false * @throws IOException */ public boolean append(Request si) throws IOException { return txnLog.append(si); } /** * commit the transaction of logs * @throws IOException */ public void commit() throws IOException { txnLog.commit(); } /** * * @return elapsed sync time of transaction log commit in milliseconds */ public long getTxnLogElapsedSyncTime() { return txnLog.getTxnLogSyncElapsedTime(); } /** * roll the transaction logs * @throws IOException */ public void rollLog() throws IOException { txnLog.rollLog(); } /** * close the transaction log files * @throws IOException */ public void close() throws IOException { TxnLog txnLogToClose = txnLog; if (txnLogToClose != null) { txnLogToClose.close(); } txnLog = null; SnapShot snapSlogToClose = snapLog; if (snapSlogToClose != null) { snapSlogToClose.close(); } snapLog = null; } @SuppressWarnings("serial") public static class DatadirException extends IOException { public DatadirException(String msg) { super(msg); } public DatadirException(String msg, Exception e) { super(msg, e); } } @SuppressWarnings("serial") public static class LogDirContentCheckException extends DatadirException { public LogDirContentCheckException(String msg) { super(msg); } } @SuppressWarnings("serial") public static class SnapDirContentCheckException extends DatadirException { public SnapDirContentCheckException(String msg) { super(msg); } } public void setTotalLogSize(long size) { txnLog.setTotalLogSize(size); } public long getTotalLogSize() { return txnLog.getTotalLogSize(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000165 15051152474 032700 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/SnapShot.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/SnapSh0100644 0000000 0000000 00000004644 15051152474 034253 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.File; import java.io.IOException; import java.util.Map; import org.apache.zookeeper.server.DataTree; /** * snapshot interface for the persistence layer. * implement this interface for implementing * snapshots. */ public interface SnapShot { /** * deserialize a data tree from the last valid snapshot and * return the last zxid that was deserialized * @param dt the datatree to be deserialized into * @param sessions the sessions to be deserialized into * @return the last zxid that was deserialized from the snapshot * @throws IOException */ long deserialize(DataTree dt, Map sessions) throws IOException; /** * persist the datatree and the sessions into a persistence storage * @param dt the datatree to be serialized * @param sessions the session timeouts to be serialized * @param name the object name to store snapshot into * @param fsync sync the snapshot immediately after write * @throws IOException */ void serialize(DataTree dt, Map sessions, File name, boolean fsync) throws IOException; /** * find the most recent snapshot file * @return the most recent snapshot file * @throws IOException */ File findMostRecentSnapshot() throws IOException; /** * get information of the last saved/restored snapshot * @return info of last snapshot */ SnapshotInfo getLastSnapshotInfo(); /** * free resources from this snapshot immediately * @throws IOException */ void close() throws IOException; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000167 15051152474 032702 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/SnapStream.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/SnapSt0100644 0000000 0000000 00000025716 15051152474 034272 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; import java.util.zip.CheckedOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xerial.snappy.SnappyCodec; import org.xerial.snappy.SnappyInputStream; import org.xerial.snappy.SnappyOutputStream; /** * Represent the Stream used in serialize and deserialize the Snapshot. */ public class SnapStream { private static final Logger LOG = LoggerFactory.getLogger(SnapStream.class); public static final String ZOOKEEPER_SHAPSHOT_STREAM_MODE = "zookeeper.snapshot.compression.method"; private static StreamMode streamMode = StreamMode.fromString( System.getProperty(ZOOKEEPER_SHAPSHOT_STREAM_MODE, StreamMode.DEFAULT_MODE.getName())); static { LOG.info("{} = {}", ZOOKEEPER_SHAPSHOT_STREAM_MODE, streamMode); } public enum StreamMode { GZIP("gz"), SNAPPY("snappy"), CHECKED(""); public static final StreamMode DEFAULT_MODE = CHECKED; private String name; StreamMode(String name) { this.name = name; } public String getName() { return name; } public String getFileExtension() { return name.isEmpty() ? "" : "." + name; } public static StreamMode fromString(String name) { for (StreamMode c : values()) { if (c.getName().compareToIgnoreCase(name) == 0) { return c; } } return DEFAULT_MODE; } } /** * Return the CheckedInputStream based on the extension of the fileName. * * @param file the file the InputStream read from * @return the specific InputStream * @throws IOException */ public static CheckedInputStream getInputStream(File file) throws IOException { FileInputStream fis = new FileInputStream(file); InputStream is; try { switch (getStreamMode(file.getName())) { case GZIP: is = new GZIPInputStream(fis); break; case SNAPPY: is = new SnappyInputStream(fis); break; case CHECKED: default: is = new BufferedInputStream(fis); } return new CheckedInputStream(is, new Adler32()); } catch (IOException e) { fis.close(); throw e; } } /** * Return the OutputStream based on predefined stream mode. * * @param file the file the OutputStream writes to * @param fsync sync the file immediately after write * @return the specific OutputStream * @throws IOException */ public static CheckedOutputStream getOutputStream(File file, boolean fsync) throws IOException { OutputStream fos = fsync ? new AtomicFileOutputStream(file) : new FileOutputStream(file); OutputStream os; switch (streamMode) { case GZIP: try { os = new GZIPOutputStream(fos); } catch (IOException e) { fos.close(); throw e; } break; case SNAPPY: // Unlike SnappyInputStream, the SnappyOutputStream // constructor cannot throw an IOException. os = new SnappyOutputStream(fos); break; case CHECKED: default: os = new BufferedOutputStream(fos); } return new CheckedOutputStream(os, new Adler32()); } /** * Write specific seal to the OutputArchive and close the OutputStream. * Currently, only CheckedOutputStream will write it's checkSum to the * end of the stream. * */ public static void sealStream(CheckedOutputStream os, OutputArchive oa) throws IOException { long val = os.getChecksum().getValue(); oa.writeLong(val, "val"); oa.writeString("/", "path"); } /** * Verify the integrity of the seal, only CheckedInputStream will verify * the checkSum of the content. * */ public static void checkSealIntegrity(CheckedInputStream is, InputArchive ia) throws IOException { long checkSum = is.getChecksum().getValue(); long val = ia.readLong("val"); ia.readString("path"); // Read and ignore "/" written by SealStream. if (val != checkSum) { throw new IOException("CRC corruption"); } } /** * Verifies that the file is a valid snapshot. Snapshot may be invalid if * it's incomplete as in a situation when the server dies while in the * process of storing a snapshot. Any files that are improperly formated * or corrupted are invalid. Any file that is not a snapshot is also an * invalid snapshot. * * @param file file to verify * @return true if the snapshot is valid * @throws IOException */ public static boolean isValidSnapshot(File file) throws IOException { if (file == null || Util.getZxidFromName(file.getName(), FileSnap.SNAPSHOT_FILE_PREFIX) == -1) { return false; } boolean isValid = false; switch (getStreamMode(file.getName())) { case GZIP: isValid = isValidGZipStream(file); break; case SNAPPY: isValid = isValidSnappyStream(file); break; case CHECKED: default: isValid = isValidCheckedStream(file); } return isValid; } public static void setStreamMode(StreamMode mode) { streamMode = mode; } public static StreamMode getStreamMode() { return streamMode; } /** * Detect the stream mode from file name extension * * @param fileName * @return the stream mode detected */ public static StreamMode getStreamMode(String fileName) { String[] splitSnapName = fileName.split("\\."); // Use file extension to detect format if (splitSnapName.length > 1) { String mode = splitSnapName[splitSnapName.length - 1]; return StreamMode.fromString(mode); } return StreamMode.CHECKED; } /** * Certify the GZip stream integrity by checking the header * for the GZip magic string * * @param f file to verify * @return true if it has the correct GZip magic string * @throws IOException */ private static boolean isValidGZipStream(File f) throws IOException { byte[] byteArray = new byte[2]; try (FileInputStream fis = new FileInputStream(f)) { if (2 != fis.read(byteArray, 0, 2)) { LOG.error("Read incorrect number of bytes from {}", f.getName()); return false; } ByteBuffer bb = ByteBuffer.wrap(byteArray); byte[] magicHeader = new byte[2]; bb.get(magicHeader, 0, 2); int magic = magicHeader[0] & 0xff | ((magicHeader[1] << 8) & 0xff00); return magic == GZIPInputStream.GZIP_MAGIC; } catch (FileNotFoundException e) { LOG.error("Unable to open file {}", f.getName(), e); return false; } } /** * Certify the Snappy stream integrity by checking the header * for the Snappy magic string * * @param f file to verify * @return true if it has the correct Snappy magic string * @throws IOException */ private static boolean isValidSnappyStream(File f) throws IOException { byte[] byteArray = new byte[SnappyCodec.MAGIC_LEN]; try (FileInputStream fis = new FileInputStream(f)) { if (SnappyCodec.MAGIC_LEN != fis.read(byteArray, 0, SnappyCodec.MAGIC_LEN)) { LOG.error("Read incorrect number of bytes from {}", f.getName()); return false; } ByteBuffer bb = ByteBuffer.wrap(byteArray); byte[] magicHeader = new byte[SnappyCodec.MAGIC_LEN]; bb.get(magicHeader, 0, SnappyCodec.MAGIC_LEN); return Arrays.equals(magicHeader, SnappyCodec.getMagicHeader()); } catch (FileNotFoundException e) { LOG.error("Unable to open file {}", f.getName(), e); return false; } } /** * Certify the Checked stream integrity by checking the header * length and format * * @param f file to verify * @return true if it has the correct header * @throws IOException */ private static boolean isValidCheckedStream(File f) throws IOException { try (RandomAccessFile raf = new RandomAccessFile(f, "r")) { // including the header and the last / bytes // the snapshot should be at least 10 bytes if (raf.length() < 10) { return false; } raf.seek(raf.length() - 5); byte[] bytes = new byte[5]; int readlen = 0; int l; while (readlen < 5 && (l = raf.read(bytes, readlen, bytes.length - readlen)) >= 0) { readlen += l; } if (readlen != bytes.length) { LOG.info("Invalid snapshot {}. too short, len = {} bytes", f.getName(), readlen); return false; } ByteBuffer bb = ByteBuffer.wrap(bytes); int len = bb.getInt(); byte b = bb.get(); if (len != 1 || b != '/') { LOG.info("Invalid snapshot {}. len = {}, byte = {}", f.getName(), len, (b & 0xff)); return false; } } return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000171 15051152474 032675 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/SnapshotInfo.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/Snapsh0100644 0000000 0000000 00000002204 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; /** * stores the zxid (as in its file name) and the last modified timestamp * of a snapshot file */ public class SnapshotInfo { public long zxid; public long timestamp; SnapshotInfo(long zxid, long timestamp) { this.zxid = zxid; this.timestamp = timestamp; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000163 15051152474 032676 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLog.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLog0100644 0000000 0000000 00000010307 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.Closeable; import java.io.IOException; import org.apache.jute.Record; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; /** * Interface for reading transaction logs. * */ public interface TxnLog extends Closeable { /** * Setter for ServerStats to monitor fsync threshold exceed * @param serverStats used to update fsyncThresholdExceedCount */ void setServerStats(ServerStats serverStats); /** * roll the current * log being appended to * @throws IOException */ void rollLog() throws IOException; /** * Append a request to the transaction log with a digset * @param request the request to be appended * returns true iff something appended, otw false * @throws IOException */ boolean append(Request request) throws IOException; /** * Start reading the transaction logs * from a given zxid * @param zxid * @return returns an iterator to read the * next transaction in the logs. * @throws IOException */ TxnIterator read(long zxid) throws IOException; /** * the last zxid of the logged transactions. * @return the last zxid of the logged transactions. * @throws IOException */ long getLastLoggedZxid() throws IOException; /** * truncate the log to get in sync with the * leader. * @param zxid the zxid to truncate at. * @throws IOException */ boolean truncate(long zxid) throws IOException; /** * the dbid for this transaction log. * @return the dbid for this transaction log. * @throws IOException */ long getDbId() throws IOException; /** * commit the transaction and make sure * they are persisted * @throws IOException */ void commit() throws IOException; /** * * @return transaction log's elapsed sync time in milliseconds */ long getTxnLogSyncElapsedTime(); /** * close the transactions logs */ void close() throws IOException; /** * Sets the total size of all log files */ void setTotalLogSize(long size); /** * Gets the total size of all log files */ long getTotalLogSize(); /** * an iterating interface for reading * transaction logs. */ interface TxnIterator extends Closeable { /** * return the transaction header. * @return return the transaction header. */ TxnHeader getHeader(); /** * return the transaction record. * @return return the transaction record. */ Record getTxn(); /** * @return the digest associated with the transaction. */ TxnDigest getDigest(); /** * go to the next transaction record. * @throws IOException */ boolean next() throws IOException; /** * close files and release the * resources * @throws IOException */ void close() throws IOException; /** * Get an estimated storage space used to store transaction records * that will return by this iterator * @throws IOException */ long getStorageSize() throws IOException; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000172 15051152474 032676 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLogToolkit.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/TxnLog0100644 0000000 0000000 00000041244 15051152474 034267 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.apache.zookeeper.server.persistence.FileTxnLog.TXNLOG_MAGIC; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.util.Date; import java.util.List; import java.util.Scanner; import java.util.zip.Adler32; import java.util.zip.Checksum; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.util.LogChopper; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.CreateContainerTxn; import org.apache.zookeeper.txn.CreateTTLTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.Txn; import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.util.ServiceUtils; public class TxnLogToolkit implements Closeable { static class TxnLogToolkitException extends Exception { private static final long serialVersionUID = 1L; private int exitCode; TxnLogToolkitException(int exitCode, String message, Object... params) { super(String.format(message, params)); this.exitCode = exitCode; } int getExitCode() { return exitCode; } } static class TxnLogToolkitParseException extends TxnLogToolkitException { private static final long serialVersionUID = 1L; private Options options; TxnLogToolkitParseException(Options options, int exitCode, String message, Object... params) { super(exitCode, message, params); this.options = options; } Options getOptions() { return options; } } private File txnLogFile; private boolean recoveryMode = false; private boolean verbose = false; private FileInputStream txnFis; private BinaryInputArchive logStream; // Recovery mode private int crcFixed = 0; private FileOutputStream recoveryFos; private BinaryOutputArchive recoveryOa; private File recoveryLogFile; private FilePadding filePadding = new FilePadding(); private boolean force = false; // chop mode private long zxid = -1L; /** * @param args Command line arguments */ public static void main(String[] args) throws Exception { try (final TxnLogToolkit lt = parseCommandLine(args)) { if (lt.isDumpMode()) { lt.dump(new Scanner(System.in)); lt.printStat(); } else { lt.chop(); } } catch (TxnLogToolkitParseException e) { System.err.println(e.getMessage() + "\n"); printHelpAndExit(e.getExitCode(), e.getOptions()); } catch (TxnLogToolkitException e) { System.err.println(e.getMessage()); ServiceUtils.requestSystemExit(e.getExitCode()); } } public TxnLogToolkit( boolean recoveryMode, boolean verbose, String txnLogFileName, boolean force) throws FileNotFoundException, TxnLogToolkitException { this.recoveryMode = recoveryMode; this.verbose = verbose; this.force = force; txnLogFile = loadTxnFile(txnLogFileName); if (recoveryMode) { recoveryLogFile = new File(txnLogFile.toString() + ".fixed"); if (recoveryLogFile.exists()) { throw new TxnLogToolkitException( ExitCode.UNEXPECTED_ERROR.getValue(), "Recovery file %s already exists or not writable", recoveryLogFile); } } openTxnLogFile(); if (recoveryMode) { openRecoveryFile(); } } public TxnLogToolkit(String txnLogFileName, String zxidName) throws TxnLogToolkitException { txnLogFile = loadTxnFile(txnLogFileName); zxid = Long.decode(zxidName); } private File loadTxnFile(String txnLogFileName) throws TxnLogToolkitException { File logFile = new File(txnLogFileName); if (!logFile.exists() || !logFile.canRead()) { throw new TxnLogToolkitException( ExitCode.UNEXPECTED_ERROR.getValue(), "File doesn't exist or not readable: %s", logFile); } return logFile; } public void dump(Scanner scanner) throws Exception { crcFixed = 0; FileHeader fhdr = new FileHeader(); fhdr.deserialize(logStream, "fileheader"); if (fhdr.getMagic() != TXNLOG_MAGIC) { throw new TxnLogToolkitException( ExitCode.INVALID_INVOCATION.getValue(), "Invalid magic number for %s", txnLogFile.getName()); } System.out.println("ZooKeeper Transactional Log File with dbid " + fhdr.getDbid() + " txnlog format version " + fhdr.getVersion()); if (recoveryMode) { fhdr.serialize(recoveryOa, "fileheader"); recoveryFos.flush(); filePadding.setCurrentSize(recoveryFos.getChannel().position()); } int count = 0; while (true) { long crcValue; byte[] bytes; try { crcValue = logStream.readLong("crcvalue"); bytes = logStream.readBuffer("txnEntry"); } catch (EOFException e) { System.out.println("EOF reached after " + count + " txns."); return; } if (bytes.length == 0) { // Since we preallocate, we define EOF to be an // empty transaction System.out.println("EOF reached after " + count + " txns."); return; } Checksum crc = new Adler32(); crc.update(bytes, 0, bytes.length); if (crcValue != crc.getValue()) { if (recoveryMode) { if (!force) { printTxn(bytes, "CRC ERROR"); if (askForFix(scanner)) { crcValue = crc.getValue(); ++crcFixed; } } else { crcValue = crc.getValue(); printTxn(bytes, "CRC FIXED"); ++crcFixed; } } else { printTxn(bytes, "CRC ERROR"); } } if (!recoveryMode || verbose) { printTxn(bytes); } if (logStream.readByte("EOR") != 'B') { throw new TxnLogToolkitException(ExitCode.UNEXPECTED_ERROR.getValue(), "Last transaction was partial."); } if (recoveryMode) { filePadding.padFile(recoveryFos.getChannel()); recoveryOa.writeLong(crcValue, "crcvalue"); recoveryOa.writeBuffer(bytes, "txnEntry"); recoveryOa.writeByte((byte) 'B', "EOR"); } count++; } } public void chop() { File targetFile = new File(txnLogFile.getParentFile(), txnLogFile.getName() + ".chopped" + zxid); try (InputStream is = new BufferedInputStream(new FileInputStream(txnLogFile)); OutputStream os = new BufferedOutputStream(new FileOutputStream(targetFile))) { if (!LogChopper.chop(is, os, zxid)) { throw new TxnLogToolkitException( ExitCode.INVALID_INVOCATION.getValue(), "Failed to chop %s", txnLogFile.getName()); } } catch (Exception e) { System.out.println("Got exception: " + e.getMessage()); } } public boolean isDumpMode() { return zxid < 0; } private boolean askForFix(Scanner scanner) throws TxnLogToolkitException { while (true) { System.out.print("Would you like to fix it (Yes/No/Abort) ? "); char answer = Character.toUpperCase(scanner.next().charAt(0)); switch (answer) { case 'Y': return true; case 'N': return false; case 'A': throw new TxnLogToolkitException(ExitCode.EXECUTION_FINISHED.getValue(), "Recovery aborted."); } } } private void printTxn(byte[] bytes) throws IOException { printTxn(bytes, ""); } private void printTxn(byte[] bytes, String prefix) throws IOException { TxnLogEntry logEntry = SerializeUtils.deserializeTxn(bytes); TxnHeader hdr = logEntry.getHeader(); Record txn = logEntry.getTxn(); String txnStr = getFormattedTxnStr(txn); String txns = String.format( "%s session 0x%s cxid 0x%s zxid 0x%s %s %s", DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG).format(new Date(hdr.getTime())), Long.toHexString(hdr.getClientId()), Long.toHexString(hdr.getCxid()), Long.toHexString(hdr.getZxid()), Request.op2String(hdr.getType()), txnStr); if (prefix != null && !"".equals(prefix.trim())) { System.out.print(prefix + " - "); } if (txns.endsWith("\n")) { System.out.print(txns); } else { System.out.println(txns); } } /** * get the formatted string from the txn. * @param txn transaction log data * @return the formatted string */ private static String getFormattedTxnStr(Record txn) throws IOException { StringBuilder txnData = new StringBuilder(); if (txn == null) { return txnData.toString(); } if (txn instanceof CreateTxn) { CreateTxn createTxn = ((CreateTxn) txn); txnData.append(createTxn.getPath() + "," + checkNullToEmpty(createTxn.getData())) .append("," + createTxn.getAcl() + "," + createTxn.getEphemeral()) .append("," + createTxn.getParentCVersion()); } else if (txn instanceof SetDataTxn) { SetDataTxn setDataTxn = ((SetDataTxn) txn); txnData.append(setDataTxn.getPath() + "," + checkNullToEmpty(setDataTxn.getData())) .append("," + setDataTxn.getVersion()); } else if (txn instanceof CreateContainerTxn) { CreateContainerTxn createContainerTxn = ((CreateContainerTxn) txn); txnData.append(createContainerTxn.getPath() + "," + checkNullToEmpty(createContainerTxn.getData())) .append("," + createContainerTxn.getAcl() + "," + createContainerTxn.getParentCVersion()); } else if (txn instanceof CreateTTLTxn) { CreateTTLTxn createTTLTxn = ((CreateTTLTxn) txn); txnData.append(createTTLTxn.getPath() + "," + checkNullToEmpty(createTTLTxn.getData())) .append("," + createTTLTxn.getAcl() + "," + createTTLTxn.getParentCVersion()) .append("," + createTTLTxn.getTtl()); } else if (txn instanceof MultiTxn) { MultiTxn multiTxn = ((MultiTxn) txn); List txnList = multiTxn.getTxns(); for (int i = 0; i < txnList.size(); i++) { Txn t = txnList.get(i); if (i == 0) { txnData.append(Request.op2String(t.getType()) + ":" + checkNullToEmpty(t.getData())); } else { txnData.append(";" + Request.op2String(t.getType()) + ":" + checkNullToEmpty(t.getData())); } } } else { txnData.append(txn.toString()); } return txnData.toString(); } private static String checkNullToEmpty(byte[] data) throws IOException { if (data == null || data.length == 0) { return ""; } return new String(data, StandardCharsets.UTF_8); } private void openTxnLogFile() throws FileNotFoundException { txnFis = new FileInputStream(txnLogFile); logStream = BinaryInputArchive.getArchive(txnFis); } private void closeTxnLogFile() throws IOException { if (txnFis != null) { txnFis.close(); } } private void openRecoveryFile() throws FileNotFoundException { recoveryFos = new FileOutputStream(recoveryLogFile); recoveryOa = BinaryOutputArchive.getArchive(recoveryFos); } private void closeRecoveryFile() throws IOException { if (recoveryFos != null) { recoveryFos.close(); } } private static TxnLogToolkit parseCommandLine(String[] args) throws TxnLogToolkitException, FileNotFoundException { CommandLineParser parser = new DefaultParser(); Options options = new Options(); Option helpOpt = new Option("h", "help", false, "Print help message"); options.addOption(helpOpt); Option recoverOpt = new Option("r", "recover", false, "Recovery mode. Re-calculate CRC for broken entries."); options.addOption(recoverOpt); Option quietOpt = new Option("v", "verbose", false, "Be verbose in recovery mode: print all entries, not just fixed ones."); options.addOption(quietOpt); Option dumpOpt = new Option("d", "dump", false, "Dump mode. Dump all entries of the log file with printing the content of a nodepath (default)"); options.addOption(dumpOpt); Option forceOpt = new Option("y", "yes", false, "Non-interactive mode: repair all CRC errors without asking"); options.addOption(forceOpt); // Chop mode options Option chopOpt = new Option("c", "chop", false, "Chop mode. Chop txn file to a zxid."); Option zxidOpt = new Option("z", "zxid", true, "Used with chop. Zxid to which to chop."); options.addOption(chopOpt); options.addOption(zxidOpt); try { CommandLine cli = parser.parse(options, args); if (cli.hasOption("help")) { printHelpAndExit(0, options); } if (cli.getArgs().length < 1) { printHelpAndExit(1, options); } if (cli.hasOption("chop") && cli.hasOption("zxid")) { return new TxnLogToolkit(cli.getArgs()[0], cli.getOptionValue("zxid")); } return new TxnLogToolkit(cli.hasOption("recover"), cli.hasOption("verbose"), cli.getArgs()[0], cli.hasOption("yes")); } catch (ParseException e) { throw new TxnLogToolkitParseException(options, ExitCode.UNEXPECTED_ERROR.getValue(), e.getMessage()); } } private static void printHelpAndExit(int exitCode, Options options) { HelpFormatter help = new HelpFormatter(); help.printHelp(120, "TxnLogToolkit [-dhrvc] (-z )", "", options, ""); ServiceUtils.requestSystemExit(exitCode); } private void printStat() { if (recoveryMode) { System.out.printf("Recovery file %s has been written with %d fixed CRC error(s)%n", recoveryLogFile, crcFixed); } } @Override public void close() throws IOException { if (recoveryMode) { closeRecoveryFile(); } closeTxnLogFile(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000161 15051152474 032674 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/Util.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/persistence/Util.j0100644 0000000 0000000 00000022150 15051152474 034214 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Properties; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A collection of utility methods for dealing with file name parsing, * low level I/O file operations and marshalling/unmarshalling. */ public class Util { private static final Logger LOG = LoggerFactory.getLogger(Util.class); private static final String SNAP_DIR = "snapDir"; private static final String LOG_DIR = "logDir"; private static final String DB_FORMAT_CONV = "dbFormatConversion"; public static String makeURIString(String dataDir, String dataLogDir, String convPolicy) { String uri = "file:" + SNAP_DIR + "=" + dataDir + ";" + LOG_DIR + "=" + dataLogDir; if (convPolicy != null) { uri += ";" + DB_FORMAT_CONV + "=" + convPolicy; } return uri.replace('\\', '/'); } /** * Given two directory files the method returns a well-formed * logfile provider URI. This method is for backward compatibility with the * existing code that only supports logfile persistence and expects these two * parameters passed either on the command-line or in the configuration file. * * @param dataDir snapshot directory * @param dataLogDir transaction log directory * @return logfile provider URI */ public static URI makeFileLoggerURL(File dataDir, File dataLogDir) { return URI.create(makeURIString(dataDir.getPath(), dataLogDir.getPath(), null)); } public static URI makeFileLoggerURL(File dataDir, File dataLogDir, String convPolicy) { return URI.create(makeURIString(dataDir.getPath(), dataLogDir.getPath(), convPolicy)); } /** * Creates a valid transaction log file name. * * @param zxid used as a file name suffix (extension) * @return file name */ public static String makeLogName(long zxid) { return FileTxnLog.LOG_FILE_PREFIX + "." + Long.toHexString(zxid); } /** * Creates a snapshot file name. * * @param zxid used as a suffix * @return file name */ public static String makeSnapshotName(long zxid) { return FileSnap.SNAPSHOT_FILE_PREFIX + "." + Long.toHexString(zxid) + SnapStream.getStreamMode().getFileExtension(); } /** * Extracts snapshot directory property value from the container. * * @param props properties container * @return file representing the snapshot directory */ public static File getSnapDir(Properties props) { return new File(props.getProperty(SNAP_DIR)); } /** * Extracts transaction log directory property value from the container. * * @param props properties container * @return file representing the txn log directory */ public static File getLogDir(Properties props) { return new File(props.getProperty(LOG_DIR)); } /** * Extracts the value of the dbFormatConversion attribute. * * @param props properties container * @return value of the dbFormatConversion attribute */ public static String getFormatConversionPolicy(Properties props) { return props.getProperty(DB_FORMAT_CONV); } /** * Extracts zxid from the file name. The file name should have been created * using one of the {@link #makeLogName(long)} or {@link #makeSnapshotName(long)}. * * @param name the file name to parse * @param prefix the file name prefix (snapshot or log) * @return zxid */ public static long getZxidFromName(String name, String prefix) { long zxid = -1; String[] nameParts = name.split("\\."); if (nameParts.length >= 2 && nameParts[0].equals(prefix)) { try { zxid = Long.parseLong(nameParts[1], 16); } catch (NumberFormatException e) { } } return zxid; } /** * Reads a transaction entry from the input archive. * @param ia archive to read from * @return null if the entry is corrupted or EOF has been reached; a buffer * (possible empty) containing serialized transaction record. * @throws IOException */ public static byte[] readTxnBytes(InputArchive ia) throws IOException { try { byte[] bytes = ia.readBuffer("txtEntry"); // Since we preallocate, we define EOF to be an // empty transaction if (bytes.length == 0) { return bytes; } if (ia.readByte("EOF") != 'B') { LOG.error("Last transaction was partial."); return null; } return bytes; } catch (EOFException e) { } return null; } /** * Serializes transaction header and transaction data into a byte buffer. * * @param hdr transaction header * @param txn transaction data * @param digest transaction digest * * @return serialized transaction record */ public static byte[] marshallTxnEntry(TxnHeader hdr, Record txn, TxnDigest digest) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive boa = BinaryOutputArchive.getArchive(baos); hdr.serialize(boa, "hdr"); if (txn != null) { txn.serialize(boa, "txn"); } if (digest != null) { digest.serialize(boa, "digest"); } return baos.toByteArray(); } /** * Write the serialized transaction record to the output archive. * * @param oa output archive * @param bytes serialized transaction record * @throws IOException */ public static void writeTxnBytes(OutputArchive oa, byte[] bytes) throws IOException { oa.writeBuffer(bytes, "txnEntry"); oa.writeByte((byte) 0x42, "EOR"); // 'B' } /** * Compare file file names of form "prefix.version". Sort order result * returned in order of version. */ private static class DataDirFileComparator implements Comparator, Serializable { private static final long serialVersionUID = -2648639884525140318L; private String prefix; private boolean ascending; public DataDirFileComparator(String prefix, boolean ascending) { this.prefix = prefix; this.ascending = ascending; } public int compare(File o1, File o2) { long z1 = Util.getZxidFromName(o1.getName(), prefix); long z2 = Util.getZxidFromName(o2.getName(), prefix); int result = z1 < z2 ? -1 : (z1 > z2 ? 1 : 0); return ascending ? result : -result; } } /** * Sort the list of files. Recency as determined by the version component * of the file name. * * @param files array of files * @param prefix files not matching this prefix are assumed to have a * version = -1) * @param ascending true sorted in ascending order, false results in * descending order * @return sorted input files */ public static List sortDataDir(File[] files, String prefix, boolean ascending) { if (files == null) { return new ArrayList<>(0); } List filelist = Arrays.asList(files); Collections.sort(filelist, new DataDirFileComparator(prefix, ascending)); return filelist; } /** * Returns true if fileName is a log file name. * * @param fileName */ public static boolean isLogFileName(String fileName) { return fileName.startsWith(FileTxnLog.LOG_FILE_PREFIX + "."); } /** * Returns true if fileName is a snapshot file name. * * @param fileName */ public static boolean isSnapshotFileName(String fileName) { return fileName.startsWith(FileSnap.SNAPSHOT_FILE_PREFIX + "."); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000173 15051152474 032720 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/AckRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/AckRequestP0100644 0000000 0000000 00000003607 15051152474 034250 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is a very simple RequestProcessor that simply forwards a request from a * previous stage to the leader as an ACK. */ class AckRequestProcessor implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(AckRequestProcessor.class); Leader leader; AckRequestProcessor(Leader leader) { this.leader = leader; } /** * Forward the request as an ACK to the leader */ public void processRequest(Request request) { QuorumPeer self = leader.self; if (self != null) { request.logLatency(ServerMetrics.getMetrics().PROPOSAL_ACK_CREATION_LATENCY); leader.processAck(self.getMyId(), request.zxid, null); } else { LOG.error("Null QuorumPeer"); } } public void shutdown() { // TODO No need to do anything } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000163 15051152474 032717 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/BufferStats.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/BufferStats0100644 0000000 0000000 00000004673 15051152474 034315 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * Provides live statistics about Jute buffer usage in term of proposal and client request size. */ public class BufferStats { public static final int INIT_VALUE = -1; /** * Size of the last buffer usage. */ private int lastBufferSize = INIT_VALUE; /** * Size of the smallest buffer usage. */ private int minBufferSize = INIT_VALUE; /** * Size of the largest buffer usage. */ private int maxBufferSize = INIT_VALUE; /** * Size of the last buffer usage. */ public synchronized int getLastBufferSize() { return lastBufferSize; } /** * Updates statistics by setting the last buffer usage size. */ public synchronized void setLastBufferSize(int value) { lastBufferSize = value; if (minBufferSize == INIT_VALUE || value < minBufferSize) { minBufferSize = value; } if (value > maxBufferSize) { maxBufferSize = value; } } /** * Size of the smallest buffer usage. */ public synchronized int getMinBufferSize() { return minBufferSize; } /** * Size of the largest buffer usage. */ public synchronized int getMaxBufferSize() { return maxBufferSize; } /** * Reset statistics. */ public synchronized void reset() { lastBufferSize = INIT_VALUE; minBufferSize = INIT_VALUE; maxBufferSize = INIT_VALUE; } @Override public synchronized String toString() { return String.format("%d/%d/%d", lastBufferSize, minBufferSize, maxBufferSize); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000167 15051152474 032723 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/CommitProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/CommitProce0100644 0000000 0000000 00000067226 15051152474 034311 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.WorkerService; import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This RequestProcessor matches the incoming committed requests with the * locally submitted requests. The trick is that locally submitted requests that * change the state of the system will come back as incoming committed requests, * so we need to match them up. Instead of just waiting for the committed requests, * we process the uncommitted requests that belong to other sessions. * * The CommitProcessor is multi-threaded. Communication between threads is * handled via queues, atomics, and wait/notifyAll synchronized on the * processor. The CommitProcessor acts as a gateway for allowing requests to * continue with the remainder of the processing pipeline. It will allow many * read requests but only a single write request to be in flight simultaneously, * thus ensuring that write requests are processed in transaction id order. * * - 1 commit processor main thread, which watches the request queues and * assigns requests to worker threads based on their sessionId so that * read and write requests for a particular session are always assigned * to the same thread (and hence are guaranteed to run in order). * - 0-N worker threads, which run the rest of the request processor pipeline * on the requests. If configured with 0 worker threads, the primary * commit processor thread runs the pipeline directly. * * Typical (default) thread counts are: on a 32 core machine, 1 commit * processor thread and 32 worker threads. * * Multi-threading constraints: * - Each session's requests must be processed in order. * - Write requests must be processed in zxid order * - Must ensure no race condition between writes in one session that would * trigger a watch being set by a read request in another session * * The current implementation solves the third constraint by simply allowing no * read requests to be processed in parallel with write requests. */ public class CommitProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(CommitProcessor.class); /** Default: numCores */ public static final String ZOOKEEPER_COMMIT_PROC_NUM_WORKER_THREADS = "zookeeper.commitProcessor.numWorkerThreads"; /** Default worker pool shutdown timeout in ms: 5000 (5s) */ public static final String ZOOKEEPER_COMMIT_PROC_SHUTDOWN_TIMEOUT = "zookeeper.commitProcessor.shutdownTimeout"; /** Default max read batch size: -1 to disable the feature */ public static final String ZOOKEEPER_COMMIT_PROC_MAX_READ_BATCH_SIZE = "zookeeper.commitProcessor.maxReadBatchSize"; /** Default max commit batch size: 1 */ public static final String ZOOKEEPER_COMMIT_PROC_MAX_COMMIT_BATCH_SIZE = "zookeeper.commitProcessor.maxCommitBatchSize"; /** * Incoming requests. */ protected LinkedBlockingQueue queuedRequests = new LinkedBlockingQueue<>(); /** * Incoming requests that are waiting on a commit, * contained in order of arrival */ protected final LinkedBlockingQueue queuedWriteRequests = new LinkedBlockingQueue<>(); /** * The number of read requests currently held in all session queues */ private AtomicInteger numReadQueuedRequests = new AtomicInteger(0); /** * The number of quorum requests currently held in all session queued */ private AtomicInteger numWriteQueuedRequests = new AtomicInteger(0); /** * Requests that have been committed. */ protected final LinkedBlockingQueue committedRequests = new LinkedBlockingQueue<>(); /** * Requests that we are holding until commit comes in. Keys represent * session ids, each value is a linked list of the session's requests. */ protected final Map> pendingRequests = new HashMap<>(10000); /** The number of requests currently being processed */ protected final AtomicInteger numRequestsProcessing = new AtomicInteger(0); RequestProcessor nextProcessor; /** For testing purposes, we use a separated stopping condition for the * outer loop.*/ protected volatile boolean stoppedMainLoop = true; protected volatile boolean stopped = true; private long workerShutdownTimeoutMS; protected WorkerService workerPool; private Object emptyPoolSync = new Object(); /** * Max number of reads to process from queuedRequests before switching to * processing commits. If the value is negative, we switch whenever we have * a local write, and pending commits. * A high read batch size will delay commit processing causing us to * serve stale data. */ private static volatile int maxReadBatchSize; /** * Max number of commits to process before processing reads. We will try to * process as many remote/local commits as we can till we reach this * count. * A high commit batch size will delay reads while processing more commits. * A low commit batch size will favor reads. */ private static volatile int maxCommitBatchSize; /** * This flag indicates whether we need to wait for a response to come back from the * leader or we just let the sync operation flow through like a read. The flag will * be false if the CommitProcessor is in a Leader pipeline. */ boolean matchSyncs; public CommitProcessor(RequestProcessor nextProcessor, String id, boolean matchSyncs, ZooKeeperServerListener listener) { super("CommitProcessor:" + id, listener); this.nextProcessor = nextProcessor; this.matchSyncs = matchSyncs; } private boolean isProcessingRequest() { return numRequestsProcessing.get() != 0; } protected boolean needCommit(Request request) { if (request.isThrottled()) { return false; } switch (request.type) { case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: case OpCode.delete: case OpCode.deleteContainer: case OpCode.setData: case OpCode.reconfig: case OpCode.multi: case OpCode.setACL: case OpCode.check: return true; case OpCode.sync: return matchSyncs; case OpCode.createSession: case OpCode.closeSession: return !request.isLocalSession(); default: return false; } } @Override public void run() { try { /* * In each iteration of the following loop we process at most * requestsToProcess requests of queuedRequests. We have to limit * the number of request we poll from queuedRequests, since it is * possible to endlessly poll read requests from queuedRequests, and * that will lead to a starvation of non-local committed requests. */ int requestsToProcess = 0; boolean commitIsWaiting = false; do { /* * Since requests are placed in the queue before being sent to * the leader, if commitIsWaiting = true, the commit belongs to * the first update operation in the queuedRequests or to a * request from a client on another server (i.e., the order of * the following two lines is important!). */ synchronized (this) { commitIsWaiting = !committedRequests.isEmpty(); requestsToProcess = queuedRequests.size(); if (requestsToProcess == 0 && !commitIsWaiting) { // Waiting for requests to process while (!stopped && requestsToProcess == 0 && !commitIsWaiting) { wait(); commitIsWaiting = !committedRequests.isEmpty(); requestsToProcess = queuedRequests.size(); } } } ServerMetrics.getMetrics().READS_QUEUED_IN_COMMIT_PROCESSOR.add(numReadQueuedRequests.get()); ServerMetrics.getMetrics().WRITES_QUEUED_IN_COMMIT_PROCESSOR.add(numWriteQueuedRequests.get()); ServerMetrics.getMetrics().COMMITS_QUEUED_IN_COMMIT_PROCESSOR.add(committedRequests.size()); long time = Time.currentElapsedTime(); /* * Processing up to requestsToProcess requests from the incoming * queue (queuedRequests). If maxReadBatchSize is set then no * commits will be processed until maxReadBatchSize number of * reads are processed (or no more reads remain in the queue). * After the loop a single committed request is processed if * one is waiting (or a batch of commits if maxCommitBatchSize * is set). */ Request request; int readsProcessed = 0; while (!stopped && requestsToProcess > 0 && (maxReadBatchSize < 0 || readsProcessed <= maxReadBatchSize) && (request = queuedRequests.poll()) != null) { requestsToProcess--; if (needCommit(request) || pendingRequests.containsKey(request.sessionId)) { // Add request to pending Deque requests = pendingRequests.computeIfAbsent(request.sessionId, sid -> new ArrayDeque<>()); requests.addLast(request); ServerMetrics.getMetrics().REQUESTS_IN_SESSION_QUEUE.add(requests.size()); } else { readsProcessed++; numReadQueuedRequests.decrementAndGet(); sendToNextProcessor(request); } /* * Stop feeding the pool if there is a local pending update * and a committed request that is ready. Once we have a * pending request with a waiting committed request, we know * we can process the committed one. This is because commits * for local requests arrive in the order they appeared in * the queue, so if we have a pending request and a * committed request, the committed request must be for that * pending write or for a write originating at a different * server. We skip this if maxReadBatchSize is set. */ if (maxReadBatchSize < 0 && !pendingRequests.isEmpty() && !committedRequests.isEmpty()) { /* * We set commitIsWaiting so that we won't check * committedRequests again. */ commitIsWaiting = true; break; } } ServerMetrics.getMetrics().READS_ISSUED_IN_COMMIT_PROC.add(readsProcessed); if (!commitIsWaiting) { commitIsWaiting = !committedRequests.isEmpty(); } /* * Handle commits, if any. */ if (commitIsWaiting && !stopped) { /* * Drain outstanding reads */ waitForEmptyPool(); if (stopped) { return; } int commitsToProcess = maxCommitBatchSize; /* * Loop through all the commits, and try to drain them. */ Set queuesToDrain = new HashSet<>(); long startWriteTime = Time.currentElapsedTime(); int commitsProcessed = 0; while (commitIsWaiting && !stopped && commitsToProcess > 0) { // Process committed head request = committedRequests.peek(); if (request.isThrottled()) { LOG.error("Throttled request in committed pool: {}. Exiting.", request); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } /* * Check if this is a local write request is pending, * if so, update it with the committed info. If the commit matches * the first write queued in the blockedRequestQueue, we know this is * a commit for a local write, as commits are received in order. Else * it must be a commit for a remote write. */ if (!queuedWriteRequests.isEmpty() && queuedWriteRequests.peek().sessionId == request.sessionId && queuedWriteRequests.peek().cxid == request.cxid) { /* * Commit matches the earliest write in our write queue. */ Deque sessionQueue = pendingRequests.get(request.sessionId); ServerMetrics.getMetrics().PENDING_SESSION_QUEUE_SIZE.add(pendingRequests.size()); if (sessionQueue == null || sessionQueue.isEmpty() || !needCommit(sessionQueue.peek())) { /* * Can't process this write yet. * Either there are reads pending in this session, or we * haven't gotten to this write yet. */ break; } else { ServerMetrics.getMetrics().REQUESTS_IN_SESSION_QUEUE.add(sessionQueue.size()); // If session queue != null, then it is also not empty. Request topPending = sessionQueue.poll(); /* * Generally, we want to send to the next processor our version of the request, * since it contains the session information that is needed for post update processing. * In more details, when a request is in the local queue, there is (or could be) a client * attached to this server waiting for a response, and there is other bookkeeping of * requests that are outstanding and have originated from this server * (e.g., for setting the max outstanding requests) - we need to update this info when an * outstanding request completes. Note that in the other case, the operation * originated from a different server and there is no local bookkeeping or a local client * session that needs to be notified. */ topPending.setHdr(request.getHdr()); topPending.setTxn(request.getTxn()); topPending.setTxnDigest(request.getTxnDigest()); topPending.zxid = request.zxid; topPending.commitRecvTime = request.commitRecvTime; request = topPending; if (request.isThrottled()) { LOG.error("Throttled request in committed & pending pool: {}. Exiting.", request); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } // Only decrement if we take a request off the queue. numWriteQueuedRequests.decrementAndGet(); queuedWriteRequests.poll(); queuesToDrain.add(request.sessionId); } } /* * Pull the request off the commit queue, now that we are going * to process it. */ committedRequests.remove(); commitsToProcess--; commitsProcessed++; // Process the write inline. processWrite(request); commitIsWaiting = !committedRequests.isEmpty(); } ServerMetrics.getMetrics().WRITE_BATCH_TIME_IN_COMMIT_PROCESSOR .add(Time.currentElapsedTime() - startWriteTime); ServerMetrics.getMetrics().WRITES_ISSUED_IN_COMMIT_PROC.add(commitsProcessed); /* * Process following reads if any, remove session queue(s) if * empty. */ readsProcessed = 0; for (Long sessionId : queuesToDrain) { Deque sessionQueue = pendingRequests.get(sessionId); int readsAfterWrite = 0; while (!stopped && !sessionQueue.isEmpty() && !needCommit(sessionQueue.peek())) { numReadQueuedRequests.decrementAndGet(); sendToNextProcessor(sessionQueue.poll()); readsAfterWrite++; } ServerMetrics.getMetrics().READS_AFTER_WRITE_IN_SESSION_QUEUE.add(readsAfterWrite); readsProcessed += readsAfterWrite; // Remove empty queues if (sessionQueue.isEmpty()) { pendingRequests.remove(sessionId); } } ServerMetrics.getMetrics().SESSION_QUEUES_DRAINED.add(queuesToDrain.size()); ServerMetrics.getMetrics().READ_ISSUED_FROM_SESSION_QUEUE.add(readsProcessed); } ServerMetrics.getMetrics().COMMIT_PROCESS_TIME.add(Time.currentElapsedTime() - time); endOfIteration(); } while (!stoppedMainLoop); } catch (Throwable e) { handleException(this.getName(), e); } LOG.info("CommitProcessor exited loop!"); } //for test only protected void endOfIteration() { } protected void waitForEmptyPool() throws InterruptedException { int numRequestsInProcess = numRequestsProcessing.get(); if (numRequestsInProcess != 0) { ServerMetrics.getMetrics().CONCURRENT_REQUEST_PROCESSING_IN_COMMIT_PROCESSOR.add(numRequestsInProcess); } long startWaitTime = Time.currentElapsedTime(); synchronized (emptyPoolSync) { while ((!stopped) && isProcessingRequest()) { emptyPoolSync.wait(); } } ServerMetrics.getMetrics().TIME_WAITING_EMPTY_POOL_IN_COMMIT_PROCESSOR_READ .add(Time.currentElapsedTime() - startWaitTime); } @Override public void start() { int numCores = Runtime.getRuntime().availableProcessors(); int numWorkerThreads = Integer.getInteger(ZOOKEEPER_COMMIT_PROC_NUM_WORKER_THREADS, numCores); workerShutdownTimeoutMS = Long.getLong(ZOOKEEPER_COMMIT_PROC_SHUTDOWN_TIMEOUT, 5000); initBatchSizes(); LOG.info( "Configuring CommitProcessor with {} worker threads.", numWorkerThreads > 0 ? numWorkerThreads : "no"); if (workerPool == null) { workerPool = new WorkerService("CommitProcWork", numWorkerThreads, true); } stopped = false; stoppedMainLoop = false; super.start(); } /** * Schedule final request processing; if a worker thread pool is not being * used, processing is done directly by this thread. */ private void sendToNextProcessor(Request request) { numRequestsProcessing.incrementAndGet(); CommitWorkRequest workRequest = new CommitWorkRequest(request); workerPool.schedule(workRequest, request.sessionId); } private void processWrite(Request request) throws RequestProcessorException { processCommitMetrics(request, true); long timeBeforeFinalProc = Time.currentElapsedTime(); nextProcessor.processRequest(request); ServerMetrics.getMetrics().WRITE_FINAL_PROC_TIME.add(Time.currentElapsedTime() - timeBeforeFinalProc); } private static void initBatchSizes() { maxReadBatchSize = Integer.getInteger(ZOOKEEPER_COMMIT_PROC_MAX_READ_BATCH_SIZE, -1); maxCommitBatchSize = Integer.getInteger(ZOOKEEPER_COMMIT_PROC_MAX_COMMIT_BATCH_SIZE, 1); if (maxCommitBatchSize <= 0) { String errorMsg = "maxCommitBatchSize must be positive, was " + maxCommitBatchSize; throw new IllegalArgumentException(errorMsg); } LOG.info ("Configuring CommitProcessor with readBatchSize {} commitBatchSize {}", maxReadBatchSize, maxCommitBatchSize); } private static void processCommitMetrics(Request request, boolean isWrite) { if (isWrite) { if (request.commitProcQueueStartTime != -1 && request.commitRecvTime != -1) { // Locally issued writes. long currentTime = Time.currentElapsedTime(); ServerMetrics.getMetrics().WRITE_COMMITPROC_TIME.add(currentTime - request.commitProcQueueStartTime); ServerMetrics.getMetrics().LOCAL_WRITE_COMMITTED_TIME.add(currentTime - request.commitRecvTime); } else if (request.commitRecvTime != -1) { // Writes issued by other servers. ServerMetrics.getMetrics().SERVER_WRITE_COMMITTED_TIME .add(Time.currentElapsedTime() - request.commitRecvTime); } } else { if (request.commitProcQueueStartTime != -1) { ServerMetrics.getMetrics().READ_COMMITPROC_TIME .add(Time.currentElapsedTime() - request.commitProcQueueStartTime); } } } public static int getMaxReadBatchSize() { return maxReadBatchSize; } public static int getMaxCommitBatchSize() { return maxCommitBatchSize; } public static void setMaxReadBatchSize(int size) { maxReadBatchSize = size; LOG.info("Configuring CommitProcessor with readBatchSize {}", maxReadBatchSize); } public static void setMaxCommitBatchSize(int size) { if (size > 0) { maxCommitBatchSize = size; LOG.info("Configuring CommitProcessor with commitBatchSize {}", maxCommitBatchSize); } } /** * CommitWorkRequest is a small wrapper class to allow * downstream processing to be run using the WorkerService */ private class CommitWorkRequest extends WorkerService.WorkRequest { private final Request request; CommitWorkRequest(Request request) { this.request = request; } @Override public void cleanup() { if (!stopped) { LOG.error("Exception thrown by downstream processor, unable to continue."); CommitProcessor.this.halt(); } } public void doWork() throws RequestProcessorException { try { processCommitMetrics(request, needCommit(request)); long timeBeforeFinalProc = Time.currentElapsedTime(); nextProcessor.processRequest(request); if (needCommit(request)) { ServerMetrics.getMetrics().WRITE_FINAL_PROC_TIME .add(Time.currentElapsedTime() - timeBeforeFinalProc); } else { ServerMetrics.getMetrics().READ_FINAL_PROC_TIME .add(Time.currentElapsedTime() - timeBeforeFinalProc); } } finally { if (numRequestsProcessing.decrementAndGet() == 0) { wakeupOnEmpty(); } } } } @SuppressFBWarnings("NN_NAKED_NOTIFY") private synchronized void wakeup() { notifyAll(); } private void wakeupOnEmpty() { synchronized (emptyPoolSync) { emptyPoolSync.notifyAll(); } } public void commit(Request request) { if (stopped || request == null) { return; } LOG.debug("Committing request:: {}", request); request.commitRecvTime = Time.currentElapsedTime(); ServerMetrics.getMetrics().COMMITS_QUEUED.add(1); committedRequests.add(request); wakeup(); } @Override public void processRequest(Request request) { if (stopped) { return; } LOG.debug("Processing request:: {}", request); request.commitProcQueueStartTime = Time.currentElapsedTime(); queuedRequests.add(request); // If the request will block, add it to the queue of blocking requests if (needCommit(request)) { queuedWriteRequests.add(request); numWriteQueuedRequests.incrementAndGet(); } else { numReadQueuedRequests.incrementAndGet(); } wakeup(); } private void halt() { stoppedMainLoop = true; stopped = true; wakeupOnEmpty(); wakeup(); queuedRequests.clear(); if (workerPool != null) { workerPool.stop(); } } public void shutdown() { LOG.info("Shutting down"); halt(); if (workerPool != null) { workerPool.join(workerShutdownTimeoutMS); } if (nextProcessor != null) { nextProcessor.shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000160 15051152474 032714 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Election.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Election.ja0100644 0000000 0000000 00000001677 15051152474 034221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; public interface Election { Vote lookForLeader() throws InterruptedException; void shutdown(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032717 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FastLeaderElection.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FastLeaderE0100644 0000000 0000000 00000144233 15051152474 034201 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.quorum.QuorumCnxManager.Message; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.quorum.flexible.QuorumOracleMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ZxidUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of leader election using TCP. It uses an object of the class * QuorumCnxManager to manage connections. Otherwise, the algorithm is push-based * as with the other UDP implementations. * * There are a few parameters that can be tuned to change its behavior. First, * finalizeWait determines the amount of time to wait until deciding upon a leader. * This is part of the leader election algorithm. */ public class FastLeaderElection implements Election { private static final Logger LOG = LoggerFactory.getLogger(FastLeaderElection.class); /** * Determine how much time a process has to wait * once it believes that it has reached the end of * leader election. */ static final int finalizeWait = 200; /** * Upper bound on the amount of time between two consecutive * notification checks. This impacts the amount of time to get * the system up again after long partitions. Currently 60 seconds. */ private static int maxNotificationInterval = 60000; /** * Lower bound for notification check. The observer don't need to use * the same lower bound as participant members */ private static int minNotificationInterval = finalizeWait; /** * Minimum notification interval, default is equal to finalizeWait */ public static final String MIN_NOTIFICATION_INTERVAL = "zookeeper.fastleader.minNotificationInterval"; /** * Maximum notification interval, default is 60s */ public static final String MAX_NOTIFICATION_INTERVAL = "zookeeper.fastleader.maxNotificationInterval"; static { minNotificationInterval = Integer.getInteger(MIN_NOTIFICATION_INTERVAL, minNotificationInterval); LOG.info("{} = {} ms", MIN_NOTIFICATION_INTERVAL, minNotificationInterval); maxNotificationInterval = Integer.getInteger(MAX_NOTIFICATION_INTERVAL, maxNotificationInterval); LOG.info("{} = {} ms", MAX_NOTIFICATION_INTERVAL, maxNotificationInterval); } /** * Connection manager. Fast leader election uses TCP for * communication between peers, and QuorumCnxManager manages * such connections. */ QuorumCnxManager manager; private SyncedLearnerTracker leadingVoteSet; /** * Notifications are messages that let other peers know that * a given peer has changed its vote, either because it has * joined leader election or because it learned of another * peer with higher zxid or same zxid and higher server id */ public static class Notification { /* * Format version, introduced in 3.4.6 */ public static final int CURRENTVERSION = 0x2; int version; /* * Proposed leader */ long leader; /* * zxid of the proposed leader */ long zxid; /* * Epoch */ long electionEpoch; /* * current state of sender */ QuorumPeer.ServerState state; /* * Address of sender */ long sid; QuorumVerifier qv; /* * epoch of the proposed leader */ long peerEpoch; } static byte[] dummyData = new byte[0]; /** * Messages that a peer wants to send to other peers. * These messages can be both Notifications and Acks * of reception of notification. */ public static class ToSend { enum mType { crequest, challenge, notification, ack } ToSend(mType type, long leader, long zxid, long electionEpoch, ServerState state, long sid, long peerEpoch, byte[] configData) { this.leader = leader; this.zxid = zxid; this.electionEpoch = electionEpoch; this.state = state; this.sid = sid; this.peerEpoch = peerEpoch; this.configData = configData; } /* * Proposed leader in the case of notification */ long leader; /* * id contains the tag for acks, and zxid for notifications */ long zxid; /* * Epoch */ long electionEpoch; /* * Current state; */ QuorumPeer.ServerState state; /* * Address of recipient */ long sid; /* * Used to send a QuorumVerifier (configuration info) */ byte[] configData = dummyData; /* * Leader epoch */ long peerEpoch; } LinkedBlockingQueue sendqueue; LinkedBlockingQueue recvqueue; /** * Multi-threaded implementation of message handler. Messenger * implements two sub-classes: WorkReceiver and WorkSender. The * functionality of each is obvious from the name. Each of these * spawns a new thread. */ protected class Messenger { /** * Receives messages from instance of QuorumCnxManager on * method run(), and processes such messages. */ class WorkerReceiver extends ZooKeeperThread { volatile boolean stop; QuorumCnxManager manager; WorkerReceiver(QuorumCnxManager manager) { super("WorkerReceiver"); this.stop = false; this.manager = manager; } public void run() { Message response; while (!stop) { // Sleeps on receive try { response = manager.pollRecvQueue(3000, TimeUnit.MILLISECONDS); if (response == null) { continue; } final int capacity = response.buffer.capacity(); // The current protocol and two previous generations all send at least 28 bytes if (capacity < 28) { LOG.error("Got a short response from server {}: {}", response.sid, capacity); continue; } // this is the backwardCompatibility mode in place before ZK-107 // It is for a version of the protocol in which we didn't send peer epoch // With peer epoch and version the message became 40 bytes boolean backCompatibility28 = (capacity == 28); // this is the backwardCompatibility mode for no version information boolean backCompatibility40 = (capacity == 40); response.buffer.clear(); // Instantiate Notification and set its attributes Notification n = new Notification(); int rstate = response.buffer.getInt(); long rleader = response.buffer.getLong(); long rzxid = response.buffer.getLong(); long relectionEpoch = response.buffer.getLong(); long rpeerepoch; int version = 0x0; QuorumVerifier rqv = null; try { if (!backCompatibility28) { rpeerepoch = response.buffer.getLong(); if (!backCompatibility40) { /* * Version added in 3.4.6 */ version = response.buffer.getInt(); } else { LOG.info("Backward compatibility mode (36 bits), server id: {}", response.sid); } } else { LOG.info("Backward compatibility mode (28 bits), server id: {}", response.sid); rpeerepoch = ZxidUtils.getEpochFromZxid(rzxid); } // check if we have a version that includes config. If so extract config info from message. if (version > 0x1) { int configLength = response.buffer.getInt(); // we want to avoid errors caused by the allocation of a byte array with negative length // (causing NegativeArraySizeException) or huge length (causing e.g. OutOfMemoryError) if (configLength < 0 || configLength > capacity) { throw new IOException(String.format("Invalid configLength in notification message! sid=%d, capacity=%d, version=%d, configLength=%d", response.sid, capacity, version, configLength)); } byte[] b = new byte[configLength]; response.buffer.get(b); synchronized (self) { try { rqv = self.configFromString(new String(b, UTF_8)); QuorumVerifier curQV = self.getQuorumVerifier(); if (rqv.getVersion() > curQV.getVersion()) { LOG.info("{} Received version: {} my version: {}", self.getMyId(), Long.toHexString(rqv.getVersion()), Long.toHexString(self.getQuorumVerifier().getVersion())); if (self.getPeerState() == ServerState.LOOKING) { LOG.debug("Invoking processReconfig(), state: {}", self.getServerState()); self.processReconfig(rqv, null, null, false); if (!rqv.equals(curQV)) { LOG.info("restarting leader election"); self.shuttingDownLE = true; self.getElectionAlg().shutdown(); break; } } else { LOG.debug("Skip processReconfig(), state: {}", self.getServerState()); } } } catch (IOException | ConfigException e) { LOG.error("Something went wrong while processing config received from {}", response.sid); } } } else { LOG.info("Backward compatibility mode (before reconfig), server id: {}", response.sid); } } catch (BufferUnderflowException | IOException e) { LOG.warn("Skipping the processing of a partial / malformed response message sent by sid={} (message length: {})", response.sid, capacity, e); continue; } /* * If it is from a non-voting server (such as an observer or * a non-voting follower), respond right away. */ if (!validVoter(response.sid)) { Vote current = self.getCurrentVote(); QuorumVerifier qv = self.getQuorumVerifier(); ToSend notmsg = new ToSend( ToSend.mType.notification, current.getId(), current.getZxid(), logicalclock.get(), self.getPeerState(), response.sid, current.getPeerEpoch(), qv.toString().getBytes(UTF_8)); sendqueue.offer(notmsg); } else { // Receive new message LOG.debug("Receive new notification message. My id = {}", self.getMyId()); // State of peer that sent this message QuorumPeer.ServerState ackstate = QuorumPeer.ServerState.LOOKING; switch (rstate) { case 0: ackstate = QuorumPeer.ServerState.LOOKING; break; case 1: ackstate = QuorumPeer.ServerState.FOLLOWING; break; case 2: ackstate = QuorumPeer.ServerState.LEADING; break; case 3: ackstate = QuorumPeer.ServerState.OBSERVING; break; default: continue; } n.leader = rleader; n.zxid = rzxid; n.electionEpoch = relectionEpoch; n.state = ackstate; n.sid = response.sid; n.peerEpoch = rpeerepoch; n.version = version; n.qv = rqv; /* * Print notification info */ LOG.info( "Notification: my state:{}; n.sid:{}, n.state:{}, n.leader:{}, n.round:0x{}, " + "n.peerEpoch:0x{}, n.zxid:0x{}, message format version:0x{}, n.config version:0x{}", self.getPeerState(), n.sid, n.state, n.leader, Long.toHexString(n.electionEpoch), Long.toHexString(n.peerEpoch), Long.toHexString(n.zxid), Long.toHexString(n.version), (n.qv != null ? (Long.toHexString(n.qv.getVersion())) : "0")); /* * If this server is looking, then send proposed leader */ if (self.getPeerState() == QuorumPeer.ServerState.LOOKING) { recvqueue.offer(n); /* * Send a notification back if the peer that sent this * message is also looking and its logical clock is * lagging behind. */ if ((ackstate == QuorumPeer.ServerState.LOOKING) && (n.electionEpoch < logicalclock.get())) { Vote v = getVote(); QuorumVerifier qv = self.getQuorumVerifier(); ToSend notmsg = new ToSend( ToSend.mType.notification, v.getId(), v.getZxid(), logicalclock.get(), self.getPeerState(), response.sid, v.getPeerEpoch(), qv.toString().getBytes(UTF_8)); sendqueue.offer(notmsg); } } else { /* * If this server is not looking, but the one that sent the ack * is looking, then send back what it believes to be the leader. */ Vote current = self.getCurrentVote(); if (ackstate == QuorumPeer.ServerState.LOOKING) { if (self.leader != null) { if (leadingVoteSet != null) { self.leader.setLeadingVoteSet(leadingVoteSet); leadingVoteSet = null; } self.leader.reportLookingSid(response.sid); } LOG.debug( "Sending new notification. My id ={} recipient={} zxid=0x{} leader={} config version = {}", self.getMyId(), response.sid, Long.toHexString(current.getZxid()), current.getId(), Long.toHexString(self.getQuorumVerifier().getVersion())); QuorumVerifier qv = self.getQuorumVerifier(); ToSend notmsg = new ToSend( ToSend.mType.notification, current.getId(), current.getZxid(), current.getElectionEpoch(), self.getPeerState(), response.sid, current.getPeerEpoch(), qv.toString().getBytes(UTF_8)); sendqueue.offer(notmsg); } } } } catch (InterruptedException e) { LOG.warn("Interrupted Exception while waiting for new message", e); } } LOG.info("WorkerReceiver is down"); } } /** * This worker simply dequeues a message to send and * and queues it on the manager's queue. */ class WorkerSender extends ZooKeeperThread { volatile boolean stop; QuorumCnxManager manager; WorkerSender(QuorumCnxManager manager) { super("WorkerSender"); this.stop = false; this.manager = manager; } public void run() { while (!stop) { try { ToSend m = sendqueue.poll(3000, TimeUnit.MILLISECONDS); if (m == null) { continue; } process(m); } catch (InterruptedException e) { break; } } LOG.info("WorkerSender is down"); } /** * Called by run() once there is a new message to send. * * @param m message to send */ void process(ToSend m) { ByteBuffer requestBuffer = buildMsg(m.state.ordinal(), m.leader, m.zxid, m.electionEpoch, m.peerEpoch, m.configData); manager.toSend(m.sid, requestBuffer); } } WorkerSender ws; WorkerReceiver wr; Thread wsThread = null; Thread wrThread = null; /** * Constructor of class Messenger. * * @param manager Connection manager */ Messenger(QuorumCnxManager manager) { this.ws = new WorkerSender(manager); this.wsThread = new Thread(this.ws, "WorkerSender[myid=" + self.getMyId() + "]"); this.wsThread.setDaemon(true); this.wr = new WorkerReceiver(manager); this.wrThread = new Thread(this.wr, "WorkerReceiver[myid=" + self.getMyId() + "]"); this.wrThread.setDaemon(true); } /** * Starts instances of WorkerSender and WorkerReceiver */ void start() { this.wsThread.start(); this.wrThread.start(); } /** * Stops instances of WorkerSender and WorkerReceiver */ void halt() { this.ws.stop = true; this.wr.stop = true; } } QuorumPeer self; Messenger messenger; AtomicLong logicalclock = new AtomicLong(); /* Election instance */ long proposedLeader; long proposedZxid; long proposedEpoch; /** * Returns the current value of the logical clock counter */ public long getLogicalClock() { return logicalclock.get(); } static ByteBuffer buildMsg(int state, long leader, long zxid, long electionEpoch, long epoch) { byte[] requestBytes = new byte[40]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); /* * Building notification packet to send, this is called directly only in tests */ requestBuffer.clear(); requestBuffer.putInt(state); requestBuffer.putLong(leader); requestBuffer.putLong(zxid); requestBuffer.putLong(electionEpoch); requestBuffer.putLong(epoch); requestBuffer.putInt(0x1); return requestBuffer; } static ByteBuffer buildMsg(int state, long leader, long zxid, long electionEpoch, long epoch, byte[] configData) { byte[] requestBytes = new byte[44 + configData.length]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); /* * Building notification packet to send */ requestBuffer.clear(); requestBuffer.putInt(state); requestBuffer.putLong(leader); requestBuffer.putLong(zxid); requestBuffer.putLong(electionEpoch); requestBuffer.putLong(epoch); requestBuffer.putInt(Notification.CURRENTVERSION); requestBuffer.putInt(configData.length); requestBuffer.put(configData); return requestBuffer; } /** * Constructor of FastLeaderElection. It takes two parameters, one * is the QuorumPeer object that instantiated this object, and the other * is the connection manager. Such an object should be created only once * by each peer during an instance of the ZooKeeper service. * * @param self QuorumPeer that created this object * @param manager Connection manager */ public FastLeaderElection(QuorumPeer self, QuorumCnxManager manager) { this.stop = false; this.manager = manager; starter(self, manager); } /** * This method is invoked by the constructor. Because it is a * part of the starting procedure of the object that must be on * any constructor of this class, it is probably best to keep as * a separate method. As we have a single constructor currently, * it is not strictly necessary to have it separate. * * @param self QuorumPeer that created this object * @param manager Connection manager */ private void starter(QuorumPeer self, QuorumCnxManager manager) { this.self = self; proposedLeader = -1; proposedZxid = -1; sendqueue = new LinkedBlockingQueue<>(); recvqueue = new LinkedBlockingQueue<>(); this.messenger = new Messenger(manager); } /** * This method starts the sender and receiver threads. */ public void start() { this.messenger.start(); } private void leaveInstance(Vote v) { LOG.debug( "About to leave FLE instance: leader={}, zxid=0x{}, my id={}, my state={}", v.getId(), Long.toHexString(v.getZxid()), self.getMyId(), self.getPeerState()); recvqueue.clear(); } public QuorumCnxManager getCnxManager() { return manager; } volatile boolean stop; public void shutdown() { stop = true; proposedLeader = -1; proposedZxid = -1; leadingVoteSet = null; LOG.debug("Shutting down connection manager"); manager.halt(); LOG.debug("Shutting down messenger"); messenger.halt(); LOG.debug("FLE is down"); } /** * Send notifications to all peers upon a change in our vote */ private void sendNotifications() { for (long sid : self.getCurrentAndNextConfigVoters()) { QuorumVerifier qv = self.getQuorumVerifier(); ToSend notmsg = new ToSend( ToSend.mType.notification, proposedLeader, proposedZxid, logicalclock.get(), QuorumPeer.ServerState.LOOKING, sid, proposedEpoch, qv.toString().getBytes(UTF_8)); LOG.debug( "Sending Notification: {} (n.leader), 0x{} (n.zxid), 0x{} (n.peerEpoch), 0x{} (n.round), {} (recipient)," + " {} (myid) ", proposedLeader, Long.toHexString(proposedZxid), Long.toHexString(proposedEpoch), Long.toHexString(logicalclock.get()), sid, self.getMyId()); sendqueue.offer(notmsg); } } /** * Check if a pair (server id, zxid) succeeds our * current vote. * */ protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) { LOG.debug( "id: {}, proposed id: {}, zxid: 0x{}, proposed zxid: 0x{}, epoch: 0x{}, proposed epoch: 0x{}", newId, curId, Long.toHexString(newZxid), Long.toHexString(curZxid), Long.toHexString(newEpoch), Long.toHexString(curEpoch)); if (self.getQuorumVerifier().getWeight(newId) == 0) { return false; } /* * We return true if one of the following three cases hold: * 1- New epoch is higher * 2- New epoch is the same as current epoch, but new zxid is higher * 3- New epoch is the same as current epoch, new zxid is the same * as current zxid, but server id is higher. */ return ((newEpoch > curEpoch) || ((newEpoch == curEpoch) && ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId))))); } /** * Given a set of votes, return the SyncedLearnerTracker which is used to * determines if have sufficient to declare the end of the election round. * * @param votes * Set of votes * @param vote * Identifier of the vote received last * @return the SyncedLearnerTracker with vote details */ protected SyncedLearnerTracker getVoteTracker(Map votes, Vote vote) { SyncedLearnerTracker voteSet = new SyncedLearnerTracker(); voteSet.addQuorumVerifier(self.getQuorumVerifier()); if (self.getLastSeenQuorumVerifier() != null && self.getLastSeenQuorumVerifier().getVersion() > self.getQuorumVerifier().getVersion()) { voteSet.addQuorumVerifier(self.getLastSeenQuorumVerifier()); } /* * First make the views consistent. Sometimes peers will have different * zxids for a server depending on timing. */ for (Map.Entry entry : votes.entrySet()) { if (vote.equals(entry.getValue())) { voteSet.addAck(entry.getKey()); } } return voteSet; } /** * In the case there is a leader elected, and a quorum supporting * this leader, we have to check if the leader has voted and acked * that it is leading. We need this check to avoid that peers keep * electing over and over a peer that has crashed and it is no * longer leading. * * @param votes set of votes * @param leader leader id * @param electionEpoch epoch id */ protected boolean checkLeader(Map votes, long leader, long electionEpoch) { boolean predicate = true; /* * If everyone else thinks I'm the leader, I must be the leader. * The other two checks are just for the case in which I'm not the * leader. If I'm not the leader and I haven't received a message * from leader stating that it is leading, then predicate is false. */ if (leader != self.getMyId()) { if (votes.get(leader) == null) { predicate = false; } else if (votes.get(leader).getState() != ServerState.LEADING) { predicate = false; } } else if (logicalclock.get() != electionEpoch) { predicate = false; } return predicate; } synchronized void updateProposal(long leader, long zxid, long epoch) { LOG.debug( "Updating proposal: {} (newleader), 0x{} (newzxid), {} (oldleader), 0x{} (oldzxid)", leader, Long.toHexString(zxid), proposedLeader, Long.toHexString(proposedZxid)); proposedLeader = leader; proposedZxid = zxid; proposedEpoch = epoch; } public synchronized Vote getVote() { return new Vote(proposedLeader, proposedZxid, proposedEpoch); } /** * A learning state can be either FOLLOWING or OBSERVING. * This method simply decides which one depending on the * role of the server. * * @return ServerState */ private ServerState learningState() { if (self.getLearnerType() == LearnerType.PARTICIPANT) { LOG.debug("I am a participant: {}", self.getMyId()); return ServerState.FOLLOWING; } else { LOG.debug("I am an observer: {}", self.getMyId()); return ServerState.OBSERVING; } } /** * Returns the initial vote value of server identifier. * * @return long */ private long getInitId() { if (self.getQuorumVerifier().getVotingMembers().containsKey(self.getMyId())) { return self.getMyId(); } else { return Long.MIN_VALUE; } } /** * Returns initial last logged zxid. * * @return long */ private long getInitLastLoggedZxid() { if (self.getLearnerType() == LearnerType.PARTICIPANT) { return self.getLastLoggedZxid(); } else { return Long.MIN_VALUE; } } /** * Returns the initial vote value of the peer epoch. * * @return long */ private long getPeerEpoch() { if (self.getLearnerType() == LearnerType.PARTICIPANT) { try { return self.getCurrentEpoch(); } catch (IOException e) { RuntimeException re = new RuntimeException(e.getMessage()); re.setStackTrace(e.getStackTrace()); throw re; } } else { return Long.MIN_VALUE; } } /** * Update the peer state based on the given proposedLeader. Also update * the leadingVoteSet if it becomes the leader. */ private void setPeerState(long proposedLeader, SyncedLearnerTracker voteSet) { ServerState ss = (proposedLeader == self.getMyId()) ? ServerState.LEADING : learningState(); self.setPeerState(ss); if (ss == ServerState.LEADING) { leadingVoteSet = voteSet; } } /** * Starts a new round of leader election. Whenever our QuorumPeer * changes its state to LOOKING, this method is invoked, and it * sends notifications to all other peers. */ public Vote lookForLeader() throws InterruptedException { try { self.jmxLeaderElectionBean = new LeaderElectionBean(); MBeanRegistry.getInstance().register(self.jmxLeaderElectionBean, self.jmxLocalPeerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); self.jmxLeaderElectionBean = null; } self.start_fle = Time.currentElapsedTime(); try { /* * The votes from the current leader election are stored in recvset. In other words, a vote v is in recvset * if v.electionEpoch == logicalclock. The current participant uses recvset to deduce on whether a majority * of participants has voted for it. */ Map recvset = new HashMap<>(); /* * The votes from previous leader elections, as well as the votes from the current leader election are * stored in outofelection. Note that notifications in a LOOKING state are not stored in outofelection. * Only FOLLOWING or LEADING notifications are stored in outofelection. The current participant could use * outofelection to learn which participant is the leader if it arrives late (i.e., higher logicalclock than * the electionEpoch of the received notifications) in a leader election. */ Map outofelection = new HashMap<>(); int notTimeout = minNotificationInterval; synchronized (this) { logicalclock.incrementAndGet(); updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch()); } LOG.info( "New election. My id = {}, proposed zxid=0x{}", self.getMyId(), Long.toHexString(proposedZxid)); sendNotifications(); SyncedLearnerTracker voteSet = null; /* * Loop in which we exchange notifications until we find a leader */ while ((self.getPeerState() == ServerState.LOOKING) && (!stop)) { /* * Remove next notification from queue, times out after 2 times * the termination time */ Notification n = recvqueue.poll(notTimeout, TimeUnit.MILLISECONDS); /* * Sends more notifications if haven't received enough. * Otherwise processes new notification. */ if (n == null) { if (manager.haveDelivered()) { sendNotifications(); } else { manager.connectAll(); } /* * Exponential backoff */ notTimeout = Math.min(notTimeout << 1, maxNotificationInterval); /* * When a leader failure happens on a master, the backup will be supposed to receive the honour from * Oracle and become a leader, but the honour is likely to be delay. We do a re-check once timeout happens * * The leader election algorithm does not provide the ability of electing a leader from a single instance * which is in a configuration of 2 instances. * */ if (self.getQuorumVerifier() instanceof QuorumOracleMaj && self.getQuorumVerifier().revalidateVoteset(voteSet, notTimeout != minNotificationInterval)) { setPeerState(proposedLeader, voteSet); Vote endVote = new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch); leaveInstance(endVote); return endVote; } LOG.info("Notification time out: {} ms", notTimeout); } else if (validVoter(n.sid) && validVoter(n.leader)) { /* * Only proceed if the vote comes from a replica in the current or next * voting view for a replica in the current or next voting view. */ switch (n.state) { case LOOKING: if (getInitLastLoggedZxid() == -1) { LOG.debug("Ignoring notification as our zxid is -1"); break; } if (n.zxid == -1) { LOG.debug("Ignoring notification from member with -1 zxid {}", n.sid); break; } // If notification > current, replace and send messages out if (n.electionEpoch > logicalclock.get()) { logicalclock.set(n.electionEpoch); recvset.clear(); if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) { updateProposal(n.leader, n.zxid, n.peerEpoch); } else { updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch()); } sendNotifications(); } else if (n.electionEpoch < logicalclock.get()) { LOG.debug( "Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x{}, logicalclock=0x{}", Long.toHexString(n.electionEpoch), Long.toHexString(logicalclock.get())); break; } else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, proposedLeader, proposedZxid, proposedEpoch)) { updateProposal(n.leader, n.zxid, n.peerEpoch); sendNotifications(); } LOG.debug( "Adding vote: from={}, proposed leader={}, proposed zxid=0x{}, proposed election epoch=0x{}", n.sid, n.leader, Long.toHexString(n.zxid), Long.toHexString(n.electionEpoch)); // don't care about the version if it's in LOOKING state recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch)); voteSet = getVoteTracker(recvset, new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch)); if (voteSet.hasAllQuorums()) { // Verify if there is any change in the proposed leader while ((n = recvqueue.poll(finalizeWait, TimeUnit.MILLISECONDS)) != null) { if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch, proposedLeader, proposedZxid, proposedEpoch)) { recvqueue.put(n); break; } } /* * This predicate is true once we don't read any new * relevant message from the reception queue */ if (n == null) { setPeerState(proposedLeader, voteSet); Vote endVote = new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch); leaveInstance(endVote); return endVote; } } break; case OBSERVING: LOG.debug("Notification from observer: {}", n.sid); break; /* * In ZOOKEEPER-3922, we separate the behaviors of FOLLOWING and LEADING. * To avoid the duplication of codes, we create a method called followingBehavior which was used to * shared by FOLLOWING and LEADING. This method returns a Vote. When the returned Vote is null, it follows * the original idea to break swtich statement; otherwise, a valid returned Vote indicates, a leader * is generated. * * The reason why we need to separate these behaviors is to make the algorithm runnable for 2-node * setting. An extra condition for generating leader is needed. Due to the majority rule, only when * there is a majority in the voteset, a leader will be generated. However, in a configuration of 2 nodes, * the number to achieve the majority remains 2, which means a recovered node cannot generate a leader which is * the existed leader. Therefore, we need the Oracle to kick in this situation. In a two-node configuration, the Oracle * only grants the permission to maintain the progress to one node. The oracle either grants the permission to the * remained node and makes it a new leader when there is a faulty machine, which is the case to maintain the progress. * Otherwise, the oracle does not grant the permission to the remained node, which further causes a service down. * * In the former case, when a failed server recovers and participate in the leader election, it would not locate a * new leader because there does not exist a majority in the voteset. It fails on the containAllQuorum() infinitely due to * two facts. First one is the fact that it does do not have a majority in the voteset. The other fact is the fact that * the oracle would not give the permission since the oracle already gave the permission to the existed leader, the healthy machine. * Logically, when the oracle replies with negative, it implies the existed leader which is LEADING notification comes from is a valid leader. * To threat this negative replies as a permission to generate the leader is the purpose to separate these two behaviors. * * * */ case FOLLOWING: /* * To avoid duplicate codes * */ Vote resultFN = receivedFollowingNotification(recvset, outofelection, voteSet, n); if (resultFN == null) { break; } else { return resultFN; } case LEADING: /* * In leadingBehavior(), it performs followingBehvior() first. When followingBehavior() returns * a null pointer, ask Oracle whether to follow this leader. * */ Vote resultLN = receivedLeadingNotification(recvset, outofelection, voteSet, n); if (resultLN == null) { break; } else { return resultLN; } default: LOG.warn("Notification state unrecognized: {} (n.state), {}(n.sid)", n.state, n.sid); break; } } else { if (!validVoter(n.leader)) { LOG.warn("Ignoring notification for non-cluster member sid {} from sid {}", n.leader, n.sid); } if (!validVoter(n.sid)) { LOG.warn("Ignoring notification for sid {} from non-quorum member sid {}", n.leader, n.sid); } } } return null; } finally { try { if (self.jmxLeaderElectionBean != null) { MBeanRegistry.getInstance().unregister(self.jmxLeaderElectionBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } self.jmxLeaderElectionBean = null; LOG.debug("Number of connection processing threads: {}", manager.getConnectionThreadCount()); } } private Vote receivedFollowingNotification(Map recvset, Map outofelection, SyncedLearnerTracker voteSet, Notification n) { /* * Consider all notifications from the same epoch * together. */ if (n.electionEpoch == logicalclock.get()) { recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch, n.state)); voteSet = getVoteTracker(recvset, new Vote(n.version, n.leader, n.zxid, n.electionEpoch, n.peerEpoch, n.state)); if (voteSet.hasAllQuorums() && checkLeader(recvset, n.leader, n.electionEpoch)) { setPeerState(n.leader, voteSet); Vote endVote = new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch); leaveInstance(endVote); return endVote; } } /* * Before joining an established ensemble, verify that * a majority are following the same leader. * * Note that the outofelection map also stores votes from the current leader election. * See ZOOKEEPER-1732 for more information. */ outofelection.put(n.sid, new Vote(n.version, n.leader, n.zxid, n.electionEpoch, n.peerEpoch, n.state)); voteSet = getVoteTracker(outofelection, new Vote(n.version, n.leader, n.zxid, n.electionEpoch, n.peerEpoch, n.state)); if (voteSet.hasAllQuorums() && checkLeader(outofelection, n.leader, n.electionEpoch)) { synchronized (this) { logicalclock.set(n.electionEpoch); setPeerState(n.leader, voteSet); } Vote endVote = new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch); leaveInstance(endVote); return endVote; } return null; } private Vote receivedLeadingNotification(Map recvset, Map outofelection, SyncedLearnerTracker voteSet, Notification n) { /* * * In a two-node configuration, a recovery nodes cannot locate a leader because of the lack of the majority in the voteset. * Therefore, it is the time for Oracle to take place as a tight breaker. * * */ Vote result = receivedFollowingNotification(recvset, outofelection, voteSet, n); if (result == null) { /* * Ask Oracle to see if it is okay to follow this leader. * * We don't need the CheckLeader() because itself cannot be a leader candidate * */ if (self.getQuorumVerifier().getNeedOracle() && !self.getQuorumVerifier().askOracle()) { LOG.info("Oracle indicates to follow"); setPeerState(n.leader, voteSet); Vote endVote = new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch); leaveInstance(endVote); return endVote; } else { LOG.info("Oracle indicates not to follow"); return null; } } else { return result; } } /** * Check if a given sid is represented in either the current or * the next voting view * * @param sid Server identifier * @return boolean */ private boolean validVoter(long sid) { return self.getCurrentAndNextConfigVoters().contains(sid); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000160 15051152474 032714 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Follower.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Follower.ja0100644 0000000 0000000 00000025606 15051152474 034246 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.Map; import java.util.Objects; import org.apache.jute.Record; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnHeader; /** * This class has the control logic for the Follower. */ public class Follower extends Learner { private long lastQueued; // This is the same object as this.zk, but we cache the downcast op final FollowerZooKeeperServer fzk; ObserverMaster om; Follower(final QuorumPeer self, final FollowerZooKeeperServer zk) { this.self = Objects.requireNonNull(self); this.fzk = Objects.requireNonNull(zk); this.zk = zk; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Follower ").append(sock); sb.append(" lastQueuedZxid:").append(lastQueued); sb.append(" pendingRevalidationCount:").append(pendingRevalidations.size()); return sb.toString(); } /** * the main method called by the follower to follow the leader * * @throws InterruptedException */ void followLeader() throws InterruptedException { self.end_fle = Time.currentElapsedTime(); long electionTimeTaken = self.end_fle - self.start_fle; self.setElectionTimeTaken(electionTimeTaken); ServerMetrics.getMetrics().ELECTION_TIME.add(electionTimeTaken); LOG.info("FOLLOWING - LEADER ELECTION TOOK - {} {}", electionTimeTaken, QuorumPeer.FLE_TIME_UNIT); self.start_fle = 0; self.end_fle = 0; fzk.registerJMX(new FollowerBean(this, zk), self.jmxLocalPeerBean); long connectionTime = 0; boolean completedSync = false; try { self.setZabState(QuorumPeer.ZabState.DISCOVERY); QuorumServer leaderServer = findLeader(); try { connectToLeader(leaderServer.addr, leaderServer.hostname); connectionTime = System.currentTimeMillis(); long newEpochZxid = registerWithLeader(Leader.FOLLOWERINFO); if (self.isReconfigStateChange()) { throw new Exception("learned about role change"); } //check to see if the leader zxid is lower than ours //this should never happen but is just a safety check long newEpoch = ZxidUtils.getEpochFromZxid(newEpochZxid); if (newEpoch < self.getAcceptedEpoch()) { LOG.error("Proposed leader epoch " + ZxidUtils.zxidToString(newEpochZxid) + " is less than our accepted epoch " + ZxidUtils.zxidToString(self.getAcceptedEpoch())); throw new IOException("Error: Epoch of leader is lower"); } long startTime = Time.currentElapsedTime(); self.setLeaderAddressAndId(leaderServer.addr, leaderServer.getId()); self.setZabState(QuorumPeer.ZabState.SYNCHRONIZATION); syncWithLeader(newEpochZxid); self.setZabState(QuorumPeer.ZabState.BROADCAST); completedSync = true; long syncTime = Time.currentElapsedTime() - startTime; ServerMetrics.getMetrics().FOLLOWER_SYNC_TIME.add(syncTime); if (self.getObserverMasterPort() > 0) { LOG.info("Starting ObserverMaster"); om = new ObserverMaster(self, fzk, self.getObserverMasterPort()); om.start(); } else { om = null; } // create a reusable packet to reduce gc impact QuorumPacket qp = new QuorumPacket(); while (this.isRunning()) { readPacket(qp); processPacket(qp); } } catch (Exception e) { LOG.warn("Exception when following the leader", e); closeSocket(); // clear pending revalidations pendingRevalidations.clear(); } } finally { if (om != null) { om.stop(); } zk.unregisterJMX(this); if (connectionTime != 0) { long connectionDuration = System.currentTimeMillis() - connectionTime; LOG.info( "Disconnected from leader (with address: {}). Was connected for {}ms. Sync state: {}", leaderAddr, connectionDuration, completedSync); messageTracker.dumpToLog(leaderAddr.toString()); } } } /** * Examine the packet received in qp and dispatch based on its contents. * @param qp * @throws IOException */ protected void processPacket(QuorumPacket qp) throws Exception { switch (qp.getType()) { case Leader.PING: ping(qp); break; case Leader.PROPOSAL: ServerMetrics.getMetrics().LEARNER_PROPOSAL_RECEIVED_COUNT.add(1); TxnLogEntry logEntry = SerializeUtils.deserializeTxn(qp.getData()); TxnHeader hdr = logEntry.getHeader(); Record txn = logEntry.getTxn(); if (hdr.getZxid() != lastQueued + 1) { LOG.warn( "Got zxid 0x{} expected 0x{}", Long.toHexString(hdr.getZxid()), Long.toHexString(lastQueued + 1)); } lastQueued = hdr.getZxid(); if (hdr.getType() == OpCode.reconfig) { SetDataTxn setDataTxn = (SetDataTxn) txn; QuorumVerifier qv = self.configFromString(new String(setDataTxn.getData(), UTF_8)); self.setLastSeenQuorumVerifier(qv, true); } fzk.logRequest(logEntry.toRequest()); if (hdr != null) { /* * Request header is created only by the leader, so this is only set * for quorum packets. If there is a clock drift, the latency may be * negative. Headers use wall time, not CLOCK_MONOTONIC. */ long now = Time.currentWallTime(); long latency = now - hdr.getTime(); if (latency >= 0) { ServerMetrics.getMetrics().PROPOSAL_LATENCY.add(latency); } } if (om != null) { final long startTime = Time.currentElapsedTime(); om.proposalReceived(qp); ServerMetrics.getMetrics().OM_PROPOSAL_PROCESS_TIME.add(Time.currentElapsedTime() - startTime); } break; case Leader.COMMIT: ServerMetrics.getMetrics().LEARNER_COMMIT_RECEIVED_COUNT.add(1); fzk.commit(qp.getZxid()); if (om != null) { final long startTime = Time.currentElapsedTime(); om.proposalCommitted(qp.getZxid()); ServerMetrics.getMetrics().OM_COMMIT_PROCESS_TIME.add(Time.currentElapsedTime() - startTime); } break; case Leader.COMMITANDACTIVATE: // get the new configuration from the request Request request = fzk.pendingTxns.element(); SetDataTxn setDataTxn = (SetDataTxn) request.getTxn(); QuorumVerifier qv = self.configFromString(new String(setDataTxn.getData(), UTF_8)); // get new designated leader from (current) leader's message ByteBuffer buffer = ByteBuffer.wrap(qp.getData()); long suggestedLeaderId = buffer.getLong(); final long zxid = qp.getZxid(); boolean majorChange = self.processReconfig(qv, suggestedLeaderId, zxid, true); // commit (writes the new config to ZK tree (/zookeeper/config) fzk.commit(zxid); if (om != null) { om.informAndActivate(zxid, suggestedLeaderId); } if (majorChange) { throw new Exception("changes proposed in reconfig"); } break; case Leader.UPTODATE: LOG.error("Received an UPTODATE message after Follower started"); break; case Leader.REVALIDATE: if (om == null || !om.revalidateLearnerSession(qp)) { revalidate(qp); } break; case Leader.SYNC: fzk.sync(); break; default: LOG.warn("Unknown packet type: {}", LearnerHandler.packetToString(qp)); break; } } /** * The zxid of the last operation seen * @return zxid */ public long getZxid() { synchronized (fzk) { return fzk.getZxid(); } } /** * The zxid of the last operation queued * @return zxid */ protected long getLastQueued() { return lastQueued; } public Integer getSyncedObserverSize() { return om == null ? null : om.getNumActiveObservers(); } public Iterable> getSyncedObserversInfo() { if (om != null && om.getNumActiveObservers() > 0) { return om.getActiveObservers(); } return Collections.emptySet(); } public void resetObserverConnectionStats() { if (om != null && om.getNumActiveObservers() > 0) { om.resetObserverConnectionStats(); } } @Override public void shutdown() { LOG.info("shutdown Follower"); super.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032720 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerBea0100644 0000000 0000000 00000005475 15051152474 034267 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServerBean; /** * Follower MBean interface implementation */ public class FollowerBean extends ZooKeeperServerBean implements FollowerMXBean { private final Follower follower; public FollowerBean(Follower follower, ZooKeeperServer zks) { super(zks); this.follower = follower; } public String getName() { return "Follower"; } public String getQuorumAddress() { return follower.sock.toString(); } public String getLastQueuedZxid() { return "0x" + Long.toHexString(follower.getLastQueued()); } public int getPendingRevalidationCount() { return follower.getPendingRevalidationsCount(); } @Override public long getElectionTimeTaken() { return follower.self.getElectionTimeTaken(); } @Override public int getObserverMasterPacketSizeLimit() { return follower.om == null ? -1 : follower.om.getPktsSizeLimit(); } @Override public void setObserverMasterPacketSizeLimit(int sizeLimit) { ObserverMaster.setPktsSizeLimit(sizeLimit); } @Override public int getMaxConcurrentSnapSyncs() { final ObserverMaster om = follower.om; return om == null ? -1 : om.getMaxConcurrentSnapSyncs(); } @Override public void setMaxConcurrentSnapSyncs(int maxConcurrentSnapshots) { final ObserverMaster om = follower.om; if (om != null) { om.setMaxConcurrentSnapSyncs(maxConcurrentSnapshots); } } @Override public int getMaxConcurrentDiffSyncs() { final ObserverMaster om = follower.om; return om == null ? -1 : om.getMaxConcurrentDiffSyncs(); } @Override public void setMaxConcurrentDiffSyncs(int maxConcurrentDiffSyncs) { final ObserverMaster om = follower.om; if (om != null) { om.setMaxConcurrentDiffSyncs(maxConcurrentDiffSyncs); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032722 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerMXB0100644 0000000 0000000 00000004374 15051152474 034223 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.ZooKeeperServerMXBean; /** * Follower MBean */ public interface FollowerMXBean extends ZooKeeperServerMXBean { /** * @return socket address */ String getQuorumAddress(); /** * @return last queued zxid */ String getLastQueuedZxid(); /** * @return count of pending revalidations */ int getPendingRevalidationCount(); /** * @return time taken for leader election in milliseconds. */ long getElectionTimeTaken(); /** * @return the size limit in bytes for the observer master commit packet queue */ int getObserverMasterPacketSizeLimit(); /** * set the size limit in bytes for the observer master commit packet queue */ void setObserverMasterPacketSizeLimit(int sizeLimit); /** * @return Number of concurrent snapshots permitted to send to observers */ int getMaxConcurrentSnapSyncs(); /** * @param maxConcurrentSnapSyncs Number of concurrent snapshots permitted to send to observers */ void setMaxConcurrentSnapSyncs(int maxConcurrentSnapSyncs); /** * @return Number of concurrent diff syncs permitted to send to observers */ int getMaxConcurrentDiffSyncs(); /** * @param maxConcurrentDiffSyncs Number of concurrent diff syncs permitted to send to observers */ void setMaxConcurrentDiffSyncs(int maxConcurrentDiffSyncs); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032707 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerReq0100644 0000000 0000000 00000015722 15051152474 034323 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.txn.ErrorTxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This RequestProcessor forwards any requests that modify the state of the * system to the Leader. */ public class FollowerRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(FollowerRequestProcessor.class); public static final String SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR = "zookeeper.follower.skipLearnerRequestToNextProcessor"; private final boolean skipLearnerRequestToNextProcessor; FollowerZooKeeperServer zks; RequestProcessor nextProcessor; LinkedBlockingQueue queuedRequests = new LinkedBlockingQueue<>(); boolean finished = false; public FollowerRequestProcessor(FollowerZooKeeperServer zks, RequestProcessor nextProcessor) { super("FollowerRequestProcessor:" + zks.getServerId(), zks.getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; this.skipLearnerRequestToNextProcessor = Boolean.getBoolean(SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR); LOG.info("Initialized FollowerRequestProcessor with {} as {}", SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR, skipLearnerRequestToNextProcessor); } @Override public void run() { try { while (!finished) { ServerMetrics.getMetrics().LEARNER_REQUEST_PROCESSOR_QUEUE_SIZE.add(queuedRequests.size()); Request request = queuedRequests.take(); if (LOG.isTraceEnabled()) { ZooTrace.logRequest(LOG, ZooTrace.CLIENT_REQUEST_TRACE_MASK, 'F', request, ""); } if (request == Request.requestOfDeath) { break; } // Screen quorum requests against ACLs first if (!zks.authWriteRequest(request)) { continue; } // We want to queue the request to be processed before we submit // the request to the leader so that we are ready to receive // the response maybeSendRequestToNextProcessor(request); if (request.isThrottled()) { continue; } // We now ship the request to the leader. As with all // other quorum operations, sync also follows this code // path, but different from others, we need to keep track // of the sync operations this follower has pending, so we // add it to pendingSyncs. switch (request.type) { case OpCode.sync: zks.pendingSyncs.add(request); zks.getFollower().request(request); break; case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: case OpCode.delete: case OpCode.deleteContainer: case OpCode.setData: case OpCode.reconfig: case OpCode.setACL: case OpCode.multi: case OpCode.check: zks.getFollower().request(request); break; case OpCode.createSession: case OpCode.closeSession: // Don't forward local sessions to the leader. if (!request.isLocalSession()) { zks.getFollower().request(request); } break; } } } catch (RuntimeException e) { // spotbugs require explicit catch of RuntimeException handleException(this.getName(), e); } catch (Exception e) { handleException(this.getName(), e); } LOG.info("FollowerRequestProcessor exited loop!"); } private void maybeSendRequestToNextProcessor(Request request) throws RequestProcessorException { if (skipLearnerRequestToNextProcessor && request.isFromLearner()) { ServerMetrics.getMetrics().SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR_COUNT.add(1); } else { nextProcessor.processRequest(request); } } public void processRequest(Request request) { processRequest(request, true); } void processRequest(Request request, boolean checkForUpgrade) { if (!finished) { if (checkForUpgrade) { // Before sending the request, check if the request requires a // global session and what we have is a local session. If so do // an upgrade. Request upgradeRequest = null; try { upgradeRequest = zks.checkUpgradeSession(request); } catch (KeeperException ke) { if (request.getHdr() != null) { request.getHdr().setType(OpCode.error); request.setTxn(new ErrorTxn(ke.code().intValue())); } request.setException(ke); LOG.warn("Error creating upgrade request", ke); } catch (IOException ie) { LOG.error("Unexpected error in upgrade", ie); } if (upgradeRequest != null) { queuedRequests.add(upgradeRequest); } } queuedRequests.add(request); } } public void shutdown() { LOG.info("Shutting down"); finished = true; queuedRequests.clear(); queuedRequests.add(Request.requestOfDeath); nextProcessor.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032724 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/FollowerZoo0100644 0000000 0000000 00000015326 15051152474 034343 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import javax.management.JMException; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Just like the standard ZooKeeperServer. We just replace the request * processors: FollowerRequestProcessor -> CommitProcessor -> * FinalRequestProcessor * * A SyncRequestProcessor is also spawned off to log proposals from the leader. */ public class FollowerZooKeeperServer extends LearnerZooKeeperServer { private static final Logger LOG = LoggerFactory.getLogger(FollowerZooKeeperServer.class); /* * Pending sync requests */ ConcurrentLinkedQueue pendingSyncs; /** * @throws IOException */ FollowerZooKeeperServer(FileTxnSnapLog logFactory, QuorumPeer self, ZKDatabase zkDb) throws IOException { super(logFactory, self.tickTime, self.minSessionTimeout, self.maxSessionTimeout, self.clientPortListenBacklog, zkDb, self); this.pendingSyncs = new ConcurrentLinkedQueue<>(); } public Follower getFollower() { return self.follower; } @Override protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); commitProcessor = new CommitProcessor(finalProcessor, Long.toString(getServerId()), true, getZooKeeperServerListener()); commitProcessor.start(); firstProcessor = new FollowerRequestProcessor(this, commitProcessor); ((FollowerRequestProcessor) firstProcessor).start(); syncProcessor = new SyncRequestProcessor(this, new SendAckRequestProcessor(getFollower())); syncProcessor.start(); } LinkedBlockingQueue pendingTxns = new LinkedBlockingQueue<>(); public void logRequest(Request request) { if ((request.zxid & 0xffffffffL) != 0) { pendingTxns.add(request); } syncProcessor.processRequest(request); } /** * Append txn request to the transaction log directly without go through request processors. */ public void appendRequest(Request request) throws IOException { getZKDatabase().append(request); } /** * When a COMMIT message is received, eventually this method is called, * which matches up the zxid from the COMMIT with (hopefully) the head of * the pendingTxns queue and hands it to the commitProcessor to commit. * @param zxid - must correspond to the head of pendingTxns if it exists */ public void commit(long zxid) { if (pendingTxns.size() == 0) { LOG.warn("Committing " + Long.toHexString(zxid) + " without seeing txn"); return; } long firstElementZxid = pendingTxns.element().zxid; if (firstElementZxid != zxid) { LOG.error("Committing zxid 0x" + Long.toHexString(zxid) + " but next pending txn 0x" + Long.toHexString(firstElementZxid)); ServiceUtils.requestSystemExit(ExitCode.UNMATCHED_TXN_COMMIT.getValue()); } Request request = pendingTxns.remove(); request.logLatency(ServerMetrics.getMetrics().COMMIT_PROPAGATION_LATENCY); commitProcessor.commit(request); } public synchronized void sync() { if (pendingSyncs.size() == 0) { LOG.warn("Not expecting a sync."); return; } Request r = pendingSyncs.remove(); if (r instanceof LearnerSyncRequest) { LearnerSyncRequest lsr = (LearnerSyncRequest) r; lsr.fh.queuePacket(new QuorumPacket(Leader.SYNC, 0, null, null)); } commitProcessor.commit(r); } @Override public int getGlobalOutstandingLimit() { int divisor = self.getQuorumSize() > 2 ? self.getQuorumSize() - 1 : 1; int globalOutstandingLimit = super.getGlobalOutstandingLimit() / divisor; return globalOutstandingLimit; } @Override public String getState() { return "follower"; } @Override public Learner getLearner() { return getFollower(); } /** * Process a request received from external Learner through the LearnerMaster * These requests have already passed through validation and checks for * session upgrade and can be injected into the middle of the pipeline. * * @param request received from external Learner */ void processObserverRequest(Request request) { ((FollowerRequestProcessor) firstProcessor).processRequest(request, false); } boolean registerJMX(LearnerHandlerBean handlerBean) { try { MBeanRegistry.getInstance().register(handlerBean, jmxServerBean); return true; } catch (JMException e) { LOG.warn("Could not register connection", e); } return false; } @Override protected void registerMetrics() { super.registerMetrics(); MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.registerGauge("synced_observers", self::getSynced_observers_metric); } @Override protected void unregisterMetrics() { super.unregisterMetrics(); MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.unregisterGauge("synced_observers"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000156 15051152474 032721 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Leader.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Leader.java0100644 0000000 0000000 00000203364 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import javax.security.sasl.SaslException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class has the control logic for the Leader. */ public class Leader extends LearnerMaster { private static final Logger LOG = LoggerFactory.getLogger(Leader.class); private static final boolean nodelay = System.getProperty("leader.nodelay", "true").equals("true"); static { LOG.info("TCP NoDelay set to: {}", nodelay); } public static class Proposal extends SyncedLearnerTracker { private QuorumPacket packet; protected Request request; public Proposal() { } public Proposal(QuorumPacket packet) { this.packet = packet; } public Proposal(Request request, QuorumPacket packet) { this.request = request; this.packet = packet; } public QuorumPacket getQuorumPacket() { return packet; } public Request getRequest() { return request; } public long getZxid() { return packet.getZxid(); } @Override public String toString() { return packet.getType() + ", " + packet.getZxid() + ", " + request; } } public static class PureRequestProposal extends Proposal { public PureRequestProposal(Request request) { this.request = request; } @Override public QuorumPacket getQuorumPacket() { byte[] data = request.getSerializeData(); return new QuorumPacket(Leader.PROPOSAL, request.zxid, data, null); } @Override public long getZxid() { return request.zxid; } @Override public String toString() { return request.toString(); } } // log ack latency if zxid is a multiple of ackLoggingFrequency. If <=0, disable logging. private static final String ACK_LOGGING_FREQUENCY = "zookeeper.leader.ackLoggingFrequency"; private static int ackLoggingFrequency; static { ackLoggingFrequency = Integer.getInteger(ACK_LOGGING_FREQUENCY, 1000); LOG.info("{} = {}", ACK_LOGGING_FREQUENCY, ackLoggingFrequency); } public static void setAckLoggingFrequency(int frequency) { ackLoggingFrequency = frequency; } public static int getAckLoggingFrequency() { return ackLoggingFrequency; } final LeaderZooKeeperServer zk; final QuorumPeer self; // VisibleForTesting protected boolean quorumFormed = false; // the follower acceptor thread volatile LearnerCnxAcceptor cnxAcceptor = null; // list of all the learners, including followers and observers private final HashSet learners = new HashSet<>(); private final BufferStats proposalStats; public BufferStats getProposalStats() { return proposalStats; } // beans for all learners private final ConcurrentHashMap connectionBeans = new ConcurrentHashMap<>(); /** * Returns a copy of the current learner snapshot */ public List getLearners() { synchronized (learners) { return new ArrayList<>(learners); } } // list of followers that are ready to follow (i.e synced with the leader) private final HashSet forwardingFollowers = new HashSet<>(); /** * Returns a copy of the current forwarding follower snapshot */ public List getForwardingFollowers() { synchronized (forwardingFollowers) { return new ArrayList<>(forwardingFollowers); } } public List getNonVotingFollowers() { List nonVotingFollowers = new ArrayList<>(); synchronized (forwardingFollowers) { for (LearnerHandler lh : forwardingFollowers) { if (!isParticipant(lh.getSid())) { nonVotingFollowers.add(lh); } } } return nonVotingFollowers; } void addForwardingFollower(LearnerHandler lh) { synchronized (forwardingFollowers) { forwardingFollowers.add(lh); /* * Any changes on forwardiongFollowers could possible affect the need of Oracle. * */ self.getQuorumVerifier().updateNeedOracle(new ArrayList<>(forwardingFollowers)); } } private final HashSet observingLearners = new HashSet<>(); /** * Returns a copy of the current observer snapshot */ public List getObservingLearners() { synchronized (observingLearners) { return new ArrayList<>(observingLearners); } } private void addObserverLearnerHandler(LearnerHandler lh) { synchronized (observingLearners) { observingLearners.add(lh); } } public Iterable> getObservingLearnersInfo() { Set> info = new HashSet<>(); synchronized (observingLearners) { for (LearnerHandler lh : observingLearners) { info.add(lh.getLearnerHandlerInfo()); } } return info; } public void resetObserverConnectionStats() { synchronized (observingLearners) { for (LearnerHandler lh : observingLearners) { lh.resetObserverConnectionStats(); } } } // Pending sync requests. Must access under 'this' lock. private final Map> pendingSyncs = new HashMap<>(); public synchronized int getNumPendingSyncs() { return pendingSyncs.size(); } //Follower counter final AtomicLong followerCounter = new AtomicLong(-1); /** * Adds peer to the leader. * * @param learner * instance of learner handle */ @Override public void addLearnerHandler(LearnerHandler learner) { synchronized (learners) { learners.add(learner); } } /** * Remove the learner from the learner list * * @param peer */ @Override public void removeLearnerHandler(LearnerHandler peer) { synchronized (forwardingFollowers) { forwardingFollowers.remove(peer); } synchronized (learners) { learners.remove(peer); } synchronized (observingLearners) { observingLearners.remove(peer); } } boolean isLearnerSynced(LearnerHandler peer) { synchronized (forwardingFollowers) { return forwardingFollowers.contains(peer); } } /** * Returns true if a quorum in qv is connected and synced with the leader * and false otherwise * * @param qv is a QuorumVerifier */ public boolean isQuorumSynced(QuorumVerifier qv) { HashSet ids = new HashSet<>(); if (qv.getVotingMembers().containsKey(self.getMyId())) { ids.add(self.getMyId()); } synchronized (forwardingFollowers) { for (LearnerHandler learnerHandler : forwardingFollowers) { if (learnerHandler.synced() && qv.getVotingMembers().containsKey(learnerHandler.getSid())) { ids.add(learnerHandler.getSid()); } } } return qv.containsQuorum(ids); } private final List serverSockets = new LinkedList<>(); public Leader(QuorumPeer self, LeaderZooKeeperServer zk) throws IOException { this.self = self; this.proposalStats = new BufferStats(); Set addresses; if (self.getQuorumListenOnAllIPs()) { addresses = self.getQuorumAddress().getWildcardAddresses(); } else { addresses = self.getQuorumAddress().getAllAddresses(); } addresses.stream() .map(address -> createServerSocket(address, self.shouldUsePortUnification(), self.isSslQuorum())) .filter(Optional::isPresent) .map(Optional::get) .forEach(serverSockets::add); if (serverSockets.isEmpty()) { throw new IOException("Leader failed to initialize any of the following sockets: " + addresses); } this.zk = zk; } InetSocketAddress recreateInetSocketAddr(String hostString, int port) { return new InetSocketAddress(hostString, port); } Optional createServerSocket(InetSocketAddress address, boolean portUnification, boolean sslQuorum) { ServerSocket serverSocket; try { if (portUnification || sslQuorum) { serverSocket = new UnifiedServerSocket(self.getX509Util(), portUnification); } else { serverSocket = new ServerSocket(); } serverSocket.setReuseAddress(true); serverSocket.bind(recreateInetSocketAddr(address.getHostString(), address.getPort())); return Optional.of(serverSocket); } catch (IOException e) { LOG.error("Couldn't bind to {}", address.toString(), e); } return Optional.empty(); } /** * This message is for follower to expect diff */ static final int DIFF = 13; /** * This is for follower to truncate its logs */ static final int TRUNC = 14; /** * This is for follower to download the snapshots */ static final int SNAP = 15; /** * This tells the leader that the connecting peer is actually an observer */ static final int OBSERVERINFO = 16; /** * This message type is sent by the leader to indicate it's zxid and if * needed, its database. */ static final int NEWLEADER = 10; /** * This message type is sent by a follower to pass the last zxid. This is here * for backward compatibility purposes. */ static final int FOLLOWERINFO = 11; /** * This message type is sent by the leader to indicate that the follower is * now uptodate andt can start responding to clients. */ static final int UPTODATE = 12; /** * This message is the first that a follower receives from the leader. * It has the protocol version and the epoch of the leader. */ public static final int LEADERINFO = 17; /** * This message is used by the follow to ack a proposed epoch. */ public static final int ACKEPOCH = 18; /** * This message type is sent to a leader to request and mutation operation. * The payload will consist of a request header followed by a request. */ static final int REQUEST = 1; /** * This message type is sent by a leader to propose a mutation. */ public static final int PROPOSAL = 2; /** * This message type is sent by a follower after it has synced a proposal. */ static final int ACK = 3; /** * This message type is sent by a leader to commit a proposal and cause * followers to start serving the corresponding data. */ static final int COMMIT = 4; /** * This message type is enchanged between follower and leader (initiated by * follower) to determine liveliness. */ static final int PING = 5; /** * This message type is to validate a session that should be active. */ static final int REVALIDATE = 6; /** * This message is a reply to a synchronize command flushing the pipe * between the leader and the follower. */ static final int SYNC = 7; /** * This message type informs observers of a committed proposal. */ static final int INFORM = 8; /** * Similar to COMMIT, only for a reconfig operation. */ static final int COMMITANDACTIVATE = 9; /** * Similar to INFORM, only for a reconfig operation. */ static final int INFORMANDACTIVATE = 19; final ConcurrentMap outstandingProposals = new ConcurrentHashMap<>(); private final ConcurrentLinkedQueue toBeApplied = new ConcurrentLinkedQueue<>(); // VisibleForTesting protected final Proposal newLeaderProposal = new Proposal(); class LearnerCnxAcceptor extends ZooKeeperCriticalThread { private final AtomicBoolean stop = new AtomicBoolean(false); private final AtomicBoolean fail = new AtomicBoolean(false); LearnerCnxAcceptor() { super("LearnerCnxAcceptor-" + serverSockets.stream() .map(ServerSocket::getLocalSocketAddress) .map(Objects::toString) .collect(Collectors.joining("|")), zk.getZooKeeperServerListener()); } @Override public void run() { if (!stop.get() && !serverSockets.isEmpty()) { ExecutorService executor = Executors.newFixedThreadPool(serverSockets.size()); CountDownLatch latch = new CountDownLatch(serverSockets.size()); serverSockets.forEach(serverSocket -> executor.submit(new LearnerCnxAcceptorHandler(serverSocket, latch))); try { latch.await(); } catch (InterruptedException ie) { LOG.error("Interrupted while sleeping in LearnerCnxAcceptor.", ie); } finally { closeSockets(); executor.shutdown(); try { if (!executor.awaitTermination(1, TimeUnit.SECONDS)) { LOG.error("not all the LearnerCnxAcceptorHandler terminated properly"); } } catch (InterruptedException ie) { LOG.error("Interrupted while terminating LearnerCnxAcceptor.", ie); } } } } public void halt() { stop.set(true); closeSockets(); } class LearnerCnxAcceptorHandler implements Runnable { private ServerSocket serverSocket; private CountDownLatch latch; LearnerCnxAcceptorHandler(ServerSocket serverSocket, CountDownLatch latch) { this.serverSocket = serverSocket; this.latch = latch; } @Override public void run() { try { Thread.currentThread().setName("LearnerCnxAcceptorHandler-" + serverSocket.getLocalSocketAddress()); while (!stop.get()) { acceptConnections(); } } catch (Exception e) { LOG.warn("Exception while accepting follower", e); if (fail.compareAndSet(false, true)) { handleException(getName(), e); halt(); } } finally { latch.countDown(); } } private void acceptConnections() throws IOException { Socket socket = null; boolean error = false; try { socket = serverSocket.accept(); // start with the initLimit, once the ack is processed // in LearnerHandler switch to the syncLimit socket.setSoTimeout(self.tickTime * self.initLimit); socket.setTcpNoDelay(nodelay); BufferedInputStream is = new BufferedInputStream(socket.getInputStream()); LearnerHandler fh = new LearnerHandler(socket, is, Leader.this); fh.start(); } catch (SocketException e) { error = true; if (stop.get()) { LOG.warn("Exception while shutting down acceptor.", e); } else { throw e; } } catch (SaslException e) { LOG.error("Exception while connecting to quorum learner", e); error = true; } catch (Exception e) { error = true; throw e; } finally { // Don't leak sockets on errors if (error && socket != null && !socket.isClosed()) { try { socket.close(); } catch (IOException e) { LOG.warn("Error closing socket: " + socket, e); } } } } } } StateSummary leaderStateSummary; long epoch = -1; boolean waitingForNewEpoch = true; // when a reconfig occurs where the leader is removed or becomes an observer, // it does not commit ops after committing the reconfig boolean allowedToCommit = true; /** * Timestamp when this leader started serving request (Quorum is running) */ private long leaderStartTime; public long getUptime() { if (leaderStartTime > 0) { return Time.currentElapsedTime() - leaderStartTime; } return 0; } /** * This method is main function that is called to lead * * @throws IOException * @throws InterruptedException */ void lead() throws IOException, InterruptedException { self.end_fle = Time.currentElapsedTime(); long electionTimeTaken = self.end_fle - self.start_fle; self.setElectionTimeTaken(electionTimeTaken); ServerMetrics.getMetrics().ELECTION_TIME.add(electionTimeTaken); LOG.info("LEADING - LEADER ELECTION TOOK - {} {}", electionTimeTaken, QuorumPeer.FLE_TIME_UNIT); self.start_fle = 0; self.end_fle = 0; zk.registerJMX(new LeaderBean(this, zk), self.jmxLocalPeerBean); try { self.setZabState(QuorumPeer.ZabState.DISCOVERY); self.tick.set(0); zk.loadData(); leaderStateSummary = new StateSummary(self.getCurrentEpoch(), zk.getLastProcessedZxid()); // Start thread that waits for connection requests from // new followers. cnxAcceptor = new LearnerCnxAcceptor(); cnxAcceptor.start(); long epoch = getEpochToPropose(self.getMyId(), self.getAcceptedEpoch()); zk.setZxid(ZxidUtils.makeZxid(epoch, 0)); synchronized (this) { lastProposed = zk.getZxid(); } newLeaderProposal.packet = new QuorumPacket(NEWLEADER, zk.getZxid(), null, null); if ((newLeaderProposal.packet.getZxid() & 0xffffffffL) != 0) { LOG.info("NEWLEADER proposal has Zxid of {}", Long.toHexString(newLeaderProposal.packet.getZxid())); } QuorumVerifier lastSeenQV = self.getLastSeenQuorumVerifier(); QuorumVerifier curQV = self.getQuorumVerifier(); if (curQV.getVersion() == 0 && curQV.getVersion() == lastSeenQV.getVersion()) { // This was added in ZOOKEEPER-1783. The initial config has version 0 (not explicitly // specified by the user; the lack of version in a config file is interpreted as version=0). // As soon as a config is established we would like to increase its version so that it // takes presedence over other initial configs that were not established (such as a config // of a server trying to join the ensemble, which may be a partial view of the system, not the full config). // We chose to set the new version to the one of the NEWLEADER message. However, before we can do that // there must be agreement on the new version, so we can only change the version when sending/receiving UPTODATE, // not when sending/receiving NEWLEADER. In other words, we can't change curQV here since its the committed quorum verifier, // and there's still no agreement on the new version that we'd like to use. Instead, we use // lastSeenQuorumVerifier which is being sent with NEWLEADER message // so its a good way to let followers know about the new version. (The original reason for sending // lastSeenQuorumVerifier with NEWLEADER is so that the leader completes any potentially uncommitted reconfigs // that it finds before starting to propose operations. Here we're reusing the same code path for // reaching consensus on the new version number.) // It is important that this is done before the leader executes waitForEpochAck, // so before LearnerHandlers return from their waitForEpochAck // hence before they construct the NEWLEADER message containing // the last-seen-quorumverifier of the leader, which we change below try { LOG.debug(String.format("set lastSeenQuorumVerifier to currentQuorumVerifier (%s)", curQV.toString())); QuorumVerifier newQV = self.configFromString(curQV.toString()); newQV.setVersion(zk.getZxid()); self.setLastSeenQuorumVerifier(newQV, true); } catch (Exception e) { throw new IOException(e); } } newLeaderProposal.addQuorumVerifier(self.getQuorumVerifier()); if (self.getLastSeenQuorumVerifier().getVersion() > self.getQuorumVerifier().getVersion()) { newLeaderProposal.addQuorumVerifier(self.getLastSeenQuorumVerifier()); } // We have to get at least a majority of servers in sync with // us. We do this by waiting for the NEWLEADER packet to get // acknowledged waitForEpochAck(self.getMyId(), leaderStateSummary); self.setCurrentEpoch(epoch); self.setLeaderAddressAndId(self.getQuorumAddress(), self.getMyId()); self.setZabState(QuorumPeer.ZabState.SYNCHRONIZATION); try { waitForNewLeaderAck(self.getMyId(), zk.getZxid()); } catch (InterruptedException e) { shutdown("Waiting for a quorum of followers, only synced with sids: [ " + newLeaderProposal.ackSetsToString() + " ]"); HashSet followerSet = new HashSet<>(); for (LearnerHandler f : getLearners()) { if (self.getQuorumVerifier().getVotingMembers().containsKey(f.getSid())) { followerSet.add(f.getSid()); } } boolean initTicksShouldBeIncreased = true; for (Proposal.QuorumVerifierAcksetPair qvAckset : newLeaderProposal.qvAcksetPairs) { if (!qvAckset.getQuorumVerifier().containsQuorum(followerSet)) { initTicksShouldBeIncreased = false; break; } } if (initTicksShouldBeIncreased) { LOG.warn("Enough followers present. Perhaps the initTicks need to be increased."); } return; } startZkServer(); /** * WARNING: do not use this for anything other than QA testing * on a real cluster. Specifically to enable verification that quorum * can handle the lower 32bit roll-over issue identified in * ZOOKEEPER-1277. Without this option it would take a very long * time (on order of a month say) to see the 4 billion writes * necessary to cause the roll-over to occur. * * This field allows you to override the zxid of the server. Typically * you'll want to set it to something like 0xfffffff0 and then * start the quorum, run some operations and see the re-election. */ String initialZxid = System.getProperty("zookeeper.testingonly.initialZxid"); if (initialZxid != null) { long zxid = Long.parseLong(initialZxid); zk.setZxid((zk.getZxid() & 0xffffffff00000000L) | zxid); } if (!System.getProperty("zookeeper.leaderServes", "yes").equals("no")) { self.setZooKeeperServer(zk); } self.setZabState(QuorumPeer.ZabState.BROADCAST); self.adminServer.setZooKeeperServer(zk); // We ping twice a tick, so we only update the tick every other // iteration boolean tickSkip = true; // If not null then shutdown this leader String shutdownMessage = null; while (true) { synchronized (this) { long start = Time.currentElapsedTime(); long cur = start; long end = start + self.tickTime / 2; while (cur < end) { wait(end - cur); cur = Time.currentElapsedTime(); } if (!tickSkip) { self.tick.incrementAndGet(); } // We use an instance of SyncedLearnerTracker to // track synced learners to make sure we still have a // quorum of current (and potentially next pending) view. SyncedLearnerTracker syncedAckSet = new SyncedLearnerTracker(); syncedAckSet.addQuorumVerifier(self.getQuorumVerifier()); if (self.getLastSeenQuorumVerifier() != null && self.getLastSeenQuorumVerifier().getVersion() > self.getQuorumVerifier().getVersion()) { syncedAckSet.addQuorumVerifier(self.getLastSeenQuorumVerifier()); } syncedAckSet.addAck(self.getMyId()); for (LearnerHandler f : getLearners()) { if (f.synced()) { syncedAckSet.addAck(f.getSid()); } } // check leader running status if (!this.isRunning()) { // set shutdown flag shutdownMessage = "Unexpected internal error"; break; } /* * * We will need to re-validate the outstandingProposal to maintain the progress of ZooKeeper. * It is likely a proposal is waiting for enough ACKs to be committed. The proposals are sent out, but the * only follower goes away which makes the proposals will not be committed until the follower recovers back. * An earlier proposal which is not committed will block any further proposals. So, We need to re-validate those * outstanding proposal with the help from Oracle. A key point in the process of re-validation is that the proposals * need to be processed in order. * * We make the whole method blocking to avoid any possible race condition on outstandingProposal and lastCommitted * as well as to avoid nested synchronization. * * As a more generic approach, we pass the object of forwardingFollowers to QuorumOracleMaj to determine if we need * the help from Oracle. * * * the size of outstandingProposals can be 1. The only one outstanding proposal is the one waiting for the ACK from * the leader itself. * */ if (!tickSkip && !syncedAckSet.hasAllQuorums() && !(self.getQuorumVerifier().overrideQuorumDecision(getForwardingFollowers()) && self.getQuorumVerifier().revalidateOutstandingProp(this, new ArrayList<>(outstandingProposals.values()), lastCommitted))) { // Lost quorum of last committed and/or last proposed // config, set shutdown flag shutdownMessage = "Not sufficient followers synced, only synced with sids: [ " + syncedAckSet.ackSetsToString() + " ]"; break; } tickSkip = !tickSkip; } for (LearnerHandler f : getLearners()) { f.ping(); } } if (shutdownMessage != null) { shutdown(shutdownMessage); // leader goes in looking state } } finally { zk.unregisterJMX(this); } } boolean isShutdown; /** * Close down all the LearnerHandlers */ void shutdown(String reason) { LOG.info("Shutting down"); if (isShutdown) { return; } LOG.info("Shutdown called. For the reason {}", reason); if (cnxAcceptor != null) { cnxAcceptor.halt(); } else { closeSockets(); } // NIO should not accept conenctions self.setZooKeeperServer(null); self.adminServer.setZooKeeperServer(null); self.closeAllConnections(); // shutdown the previous zk if (zk != null) { zk.shutdown(); } synchronized (learners) { for (Iterator it = learners.iterator(); it.hasNext(); ) { LearnerHandler f = it.next(); it.remove(); f.shutdown(); } } isShutdown = true; } synchronized void closeSockets() { for (ServerSocket serverSocket : serverSockets) { if (!serverSocket.isClosed()) { try { serverSocket.close(); } catch (IOException e) { LOG.warn("Ignoring unexpected exception during close {}", serverSocket, e); } } } } /** In a reconfig operation, this method attempts to find the best leader for next configuration. * If the current leader is a voter in the next configuartion, then it remains the leader. * Otherwise, choose one of the new voters that acked the reconfiguartion, such that it is as * up-to-date as possible, i.e., acked as many outstanding proposals as possible. * * @param reconfigProposal * @param zxid of the reconfigProposal * @return server if of the designated leader */ private long getDesignatedLeader(Proposal reconfigProposal, long zxid) { //new configuration Proposal.QuorumVerifierAcksetPair newQVAcksetPair = reconfigProposal.qvAcksetPairs.get(reconfigProposal.qvAcksetPairs.size() - 1); //check if I'm in the new configuration with the same quorum address - // if so, I'll remain the leader if (newQVAcksetPair.getQuorumVerifier().getVotingMembers().containsKey(self.getMyId()) && newQVAcksetPair.getQuorumVerifier().getVotingMembers().get(self.getMyId()).addr.equals(self.getQuorumAddress())) { return self.getMyId(); } // start with an initial set of candidates that are voters from new config that // acknowledged the reconfig op (there must be a quorum). Choose one of them as // current leader candidate HashSet candidates = new HashSet<>(newQVAcksetPair.getAckset()); candidates.remove(self.getMyId()); // if we're here, I shouldn't be the leader long curCandidate = candidates.iterator().next(); //go over outstanding ops in order, and try to find a candidate that acked the most ops. //this way it will be the most up-to-date and we'll minimize the number of ops that get dropped long curZxid = zxid + 1; Proposal p = outstandingProposals.get(curZxid); while (p != null && !candidates.isEmpty()) { for (Proposal.QuorumVerifierAcksetPair qvAckset : p.qvAcksetPairs) { //reduce the set of candidates to those that acknowledged p candidates.retainAll(qvAckset.getAckset()); //no candidate acked p, return the best candidate found so far if (candidates.isEmpty()) { return curCandidate; } //update the current candidate, and if it is the only one remaining, return it curCandidate = candidates.iterator().next(); if (candidates.size() == 1) { return curCandidate; } } curZxid++; p = outstandingProposals.get(curZxid); } return curCandidate; } /** * @return True if committed, otherwise false. **/ public synchronized boolean tryToCommit(Proposal p, long zxid, SocketAddress followerAddr) { // make sure that ops are committed in order. With reconfigurations it is now possible // that different operations wait for different sets of acks, and we still want to enforce // that they are committed in order. Currently we only permit one outstanding reconfiguration // such that the reconfiguration and subsequent outstanding ops proposed while the reconfig is // pending all wait for a quorum of old and new config, so it's not possible to get enough acks // for an operation without getting enough acks for preceding ops. But in the future if multiple // concurrent reconfigs are allowed, this can happen. if (outstandingProposals.containsKey(zxid - 1)) { return false; } // in order to be committed, a proposal must be accepted by a quorum. // // getting a quorum from all necessary configurations. if (!p.hasAllQuorums()) { return false; } // commit proposals in order if (zxid != lastCommitted + 1) { LOG.warn( "Commiting zxid 0x{} from {} not first!", Long.toHexString(zxid), followerAddr); LOG.warn("First is 0x{}", Long.toHexString(lastCommitted + 1)); } outstandingProposals.remove(zxid); if (p.request != null) { toBeApplied.add(p); } if (p.request == null) { LOG.warn("Going to commit null: {}", p); } else if (p.request.getHdr().getType() == OpCode.reconfig) { LOG.debug("Committing a reconfiguration! {}", outstandingProposals.size()); //if this server is voter in new config with the same quorum address, //then it will remain the leader //otherwise an up-to-date follower will be designated as leader. This saves //leader election time, unless the designated leader fails Long designatedLeader = getDesignatedLeader(p, zxid); QuorumVerifier newQV = p.qvAcksetPairs.get(p.qvAcksetPairs.size() - 1).getQuorumVerifier(); self.processReconfig(newQV, designatedLeader, zk.getZxid(), true); if (designatedLeader != self.getMyId()) { LOG.info(String.format("Committing a reconfiguration (reconfigEnabled=%s); this leader is not the designated " + "leader anymore, setting allowedToCommit=false", self.isReconfigEnabled())); allowedToCommit = false; } // we're sending the designated leader, and if the leader is changing the followers are // responsible for closing the connection - this way we are sure that at least a majority of them // receive the commit message. commitAndActivate(zxid, designatedLeader); informAndActivate(p, designatedLeader); } else { p.request.logLatency(ServerMetrics.getMetrics().QUORUM_ACK_LATENCY); commit(zxid); inform(p); } zk.commitProcessor.commit(p.request); if (pendingSyncs.containsKey(zxid)) { for (LearnerSyncRequest r : pendingSyncs.remove(zxid)) { sendSync(r); } } return true; } /** * Keep a count of acks that are received by the leader for a particular * proposal * * @param sid is the id of the server that sent the ack * @param zxid is the zxid of the proposal sent out * @param followerAddr */ @Override public synchronized void processAck(long sid, long zxid, SocketAddress followerAddr) { if (!allowedToCommit) { return; // last op committed was a leader change - from now on } // the new leader should commit if (LOG.isTraceEnabled()) { LOG.trace("Ack zxid: 0x{}", Long.toHexString(zxid)); for (Proposal p : outstandingProposals.values()) { long packetZxid = p.packet.getZxid(); LOG.trace("outstanding proposal: 0x{}", Long.toHexString(packetZxid)); } LOG.trace("outstanding proposals all"); } if ((zxid & 0xffffffffL) == 0) { /* * We no longer process NEWLEADER ack with this method. However, * the learner sends an ack back to the leader after it gets * UPTODATE, so we just ignore the message. */ return; } if (outstandingProposals.size() == 0) { LOG.debug("outstanding is 0"); return; } if (lastCommitted >= zxid) { LOG.debug( "proposal has already been committed, pzxid: 0x{} zxid: 0x{}", Long.toHexString(lastCommitted), Long.toHexString(zxid)); // The proposal has already been committed return; } Proposal p = outstandingProposals.get(zxid); if (p == null) { LOG.warn("Trying to commit future proposal: zxid 0x{} from {}", Long.toHexString(zxid), followerAddr); return; } if (ackLoggingFrequency > 0 && (zxid % ackLoggingFrequency == 0)) { p.request.logLatency(ServerMetrics.getMetrics().ACK_LATENCY, Long.toString(sid)); } p.addAck(sid); boolean hasCommitted = tryToCommit(p, zxid, followerAddr); // If p is a reconfiguration, multiple other operations may be ready to be committed, // since operations wait for different sets of acks. // Currently we only permit one outstanding reconfiguration at a time // such that the reconfiguration and subsequent outstanding ops proposed while the reconfig is // pending all wait for a quorum of old and new config, so its not possible to get enough acks // for an operation without getting enough acks for preceding ops. But in the future if multiple // concurrent reconfigs are allowed, this can happen and then we need to check whether some pending // ops may already have enough acks and can be committed, which is what this code does. if (hasCommitted && p.request != null && p.request.getHdr().getType() == OpCode.reconfig) { long curZxid = zxid; while (allowedToCommit && hasCommitted && p != null) { curZxid++; p = outstandingProposals.get(curZxid); if (p != null) { hasCommitted = tryToCommit(p, curZxid, null); } } } } static class ToBeAppliedRequestProcessor implements RequestProcessor { private final RequestProcessor next; private final Leader leader; /** * This request processor simply maintains the toBeApplied list. For * this to work next must be a FinalRequestProcessor and * FinalRequestProcessor.processRequest MUST process the request * synchronously! * * @param next * a reference to the FinalRequestProcessor */ ToBeAppliedRequestProcessor(RequestProcessor next, Leader leader) { if (!(next instanceof FinalRequestProcessor)) { throw new RuntimeException(ToBeAppliedRequestProcessor.class.getName() + " must be connected to " + FinalRequestProcessor.class.getName() + " not " + next.getClass().getName()); } this.leader = leader; this.next = next; } /* * (non-Javadoc) * * @see org.apache.zookeeper.server.RequestProcessor#processRequest(org.apache.zookeeper.server.Request) */ public void processRequest(Request request) throws RequestProcessorException { next.processRequest(request); // The only requests that should be on toBeApplied are write // requests, for which we will have a hdr. We can't simply use // request.zxid here because that is set on read requests to equal // the zxid of the last write op. if (request.getHdr() != null) { long zxid = request.getHdr().getZxid(); Iterator iter = leader.toBeApplied.iterator(); if (iter.hasNext()) { Proposal p = iter.next(); if (p.request != null && p.request.zxid == zxid) { iter.remove(); return; } } LOG.error("Committed request not found on toBeApplied: {}", request); } } /* * (non-Javadoc) * * @see org.apache.zookeeper.server.RequestProcessor#shutdown() */ public void shutdown() { LOG.info("Shutting down"); next.shutdown(); } } /** * send a packet to all the followers ready to follow * * @param qp * the packet to be sent */ void sendPacket(QuorumPacket qp) { synchronized (forwardingFollowers) { for (LearnerHandler f : forwardingFollowers) { f.queuePacket(qp); } } } /** * send a packet to all observers */ void sendObserverPacket(QuorumPacket qp) { for (LearnerHandler f : getObservingLearners()) { f.queuePacket(qp); } } long lastCommitted = -1; /** * Create a commit packet and send it to all the members of the quorum * * @param zxid */ public void commit(long zxid) { synchronized (this) { lastCommitted = zxid; } QuorumPacket qp = new QuorumPacket(Leader.COMMIT, zxid, null, null); sendPacket(qp); ServerMetrics.getMetrics().COMMIT_COUNT.add(1); } //commit and send some info public void commitAndActivate(long zxid, long designatedLeader) { synchronized (this) { lastCommitted = zxid; } byte[] data = new byte[8]; ByteBuffer buffer = ByteBuffer.wrap(data); buffer.putLong(designatedLeader); QuorumPacket qp = new QuorumPacket(Leader.COMMITANDACTIVATE, zxid, data, null); sendPacket(qp); } /** * Create an inform packet and send it to all observers. */ public void inform(Proposal proposal) { QuorumPacket qp = new QuorumPacket(Leader.INFORM, proposal.request.zxid, proposal.packet.getData(), null); sendObserverPacket(qp); } public static QuorumPacket buildInformAndActivePacket(long zxid, long designatedLeader, byte[] proposalData) { byte[] data = new byte[proposalData.length + 8]; ByteBuffer buffer = ByteBuffer.wrap(data); buffer.putLong(designatedLeader); buffer.put(proposalData); return new QuorumPacket(Leader.INFORMANDACTIVATE, zxid, data, null); } /** * Create an inform and activate packet and send it to all observers. */ public void informAndActivate(Proposal proposal, long designatedLeader) { sendObserverPacket(buildInformAndActivePacket(proposal.request.zxid, designatedLeader, proposal.packet.getData())); } long lastProposed; @Override public synchronized long getLastProposed() { return lastProposed; } /** * Returns the current epoch of the leader. */ public long getEpoch() { return ZxidUtils.getEpochFromZxid(lastProposed); } @SuppressWarnings("serial") public static class XidRolloverException extends Exception { public XidRolloverException(String message) { super(message); } } /** * create a proposal and send it out to all the members * * @param request * @return the proposal that is queued to send to all the members */ public Proposal propose(Request request) throws XidRolloverException { if (request.isThrottled()) { LOG.error("Throttled request send as proposal: {}. Exiting.", request); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } /** * Address the rollover issue. All lower 32bits set indicate a new leader * election. Force a re-election instead. See ZOOKEEPER-1277 */ if ((request.zxid & 0xffffffffL) == 0xffffffffL) { String msg = "zxid lower 32 bits have rolled over, forcing re-election, and therefore new epoch start"; shutdown(msg); throw new XidRolloverException(msg); } byte[] data = request.getSerializeData(); proposalStats.setLastBufferSize(data.length); QuorumPacket pp = new QuorumPacket(Leader.PROPOSAL, request.zxid, data, null); Proposal p = new Proposal(request, pp); synchronized (this) { p.addQuorumVerifier(self.getQuorumVerifier()); if (request.getHdr().getType() == OpCode.reconfig) { self.setLastSeenQuorumVerifier(request.qv, true); } if (self.getQuorumVerifier().getVersion() < self.getLastSeenQuorumVerifier().getVersion()) { p.addQuorumVerifier(self.getLastSeenQuorumVerifier()); } LOG.debug("Proposing:: {}", request); lastProposed = p.packet.getZxid(); outstandingProposals.put(lastProposed, p); sendPacket(pp); } ServerMetrics.getMetrics().PROPOSAL_COUNT.add(1); return p; } /** * Process sync requests * * @param r the request */ public synchronized void processSync(LearnerSyncRequest r) { if (outstandingProposals.isEmpty()) { sendSync(r); } else { pendingSyncs.computeIfAbsent(lastProposed, k -> new ArrayList<>()).add(r); } } /** * Sends a sync message to the appropriate server */ public void sendSync(LearnerSyncRequest r) { QuorumPacket qp = new QuorumPacket(Leader.SYNC, 0, null, null); r.fh.queuePacket(qp); } /** * lets the leader know that a follower is capable of following and is done * syncing * * @param handler handler of the follower * @return last proposed zxid */ @Override public synchronized long startForwarding(LearnerHandler handler, long lastSeenZxid) { // Queue up any outstanding requests enabling the receipt of // new requests if (lastProposed > lastSeenZxid) { for (Proposal p : toBeApplied) { if (p.packet.getZxid() <= lastSeenZxid) { continue; } handler.queuePacket(p.packet); // Since the proposal has been committed we need to send the // commit message also QuorumPacket qp = new QuorumPacket(Leader.COMMIT, p.packet.getZxid(), null, null); handler.queuePacket(qp); } // Only participant need to get outstanding proposals if (handler.getLearnerType() == LearnerType.PARTICIPANT) { List zxids = new ArrayList<>(outstandingProposals.keySet()); Collections.sort(zxids); for (Long zxid : zxids) { if (zxid <= lastSeenZxid) { continue; } handler.queuePacket(outstandingProposals.get(zxid).packet); } } } if (handler.getLearnerType() == LearnerType.PARTICIPANT) { addForwardingFollower(handler); } else { addObserverLearnerHandler(handler); } return lastProposed; } @Override public void waitForStartup() throws InterruptedException { synchronized (zk) { while (!zk.isRunning() && !Thread.currentThread().isInterrupted()) { zk.wait(20); } } } // VisibleForTesting protected final Set connectingFollowers = new HashSet<>(); private volatile boolean quitWaitForEpoch = false; private volatile long timeStartWaitForEpoch = -1; private volatile SyncedLearnerTracker voteSet; public static final String MAX_TIME_TO_WAIT_FOR_EPOCH = "zookeeper.leader.maxTimeToWaitForEpoch"; private static int maxTimeToWaitForEpoch; static { maxTimeToWaitForEpoch = Integer.getInteger(MAX_TIME_TO_WAIT_FOR_EPOCH, -1); LOG.info("{} = {}ms", MAX_TIME_TO_WAIT_FOR_EPOCH, maxTimeToWaitForEpoch); } // visible for test public static void setMaxTimeToWaitForEpoch(int maxTimeToWaitForEpoch) { Leader.maxTimeToWaitForEpoch = maxTimeToWaitForEpoch; LOG.info("Set {} to {}ms", MAX_TIME_TO_WAIT_FOR_EPOCH, Leader.maxTimeToWaitForEpoch); } /** * Quit condition: * * 1 voter goes to looking again and time waitForEpoch > maxTimeToWaitForEpoch * * Note: the voter may go to looking again in case of: * 1. change mind in the last minute when received a different notification * 2. the leader hadn't started leading when it tried to connect to it * 3. connection broken between the voter and leader * 4. voter being shutdown or restarted */ private void quitLeading() { synchronized (connectingFollowers) { quitWaitForEpoch = true; connectingFollowers.notifyAll(); } ServerMetrics.getMetrics().QUIT_LEADING_DUE_TO_DISLOYAL_VOTER.add(1); LOG.info("Quit leading due to voter changed mind."); } public void setLeadingVoteSet(SyncedLearnerTracker voteSet) { this.voteSet = voteSet; } public void reportLookingSid(long sid) { if (maxTimeToWaitForEpoch < 0 || timeStartWaitForEpoch < 0 || !waitingForNewEpoch) { return; } if (voteSet == null || !voteSet.hasSid(sid)) { return; } if (Time.currentElapsedTime() - timeStartWaitForEpoch > maxTimeToWaitForEpoch) { quitLeading(); } } @Override public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException { synchronized (connectingFollowers) { if (!waitingForNewEpoch) { return epoch; } if (lastAcceptedEpoch >= epoch) { epoch = lastAcceptedEpoch + 1; } if (isParticipant(sid)) { connectingFollowers.add(sid); } QuorumVerifier verifier = self.getQuorumVerifier(); if (connectingFollowers.contains(self.getMyId()) && verifier.containsQuorum(connectingFollowers)) { waitingForNewEpoch = false; self.setAcceptedEpoch(epoch); connectingFollowers.notifyAll(); } else { long start = Time.currentElapsedTime(); if (sid == self.getMyId()) { timeStartWaitForEpoch = start; } long cur = start; long end = start + self.getInitLimit() * self.getTickTime(); while (waitingForNewEpoch && cur < end && !quitWaitForEpoch) { connectingFollowers.wait(end - cur); cur = Time.currentElapsedTime(); } if (waitingForNewEpoch) { throw new InterruptedException("Timeout while waiting for epoch from quorum"); } } return epoch; } } @Override public ZKDatabase getZKDatabase() { return zk.getZKDatabase(); } // VisibleForTesting protected final Set electingFollowers = new HashSet<>(); // VisibleForTesting protected boolean electionFinished = false; @Override public void waitForEpochAck(long id, StateSummary ss) throws IOException, InterruptedException { synchronized (electingFollowers) { if (electionFinished) { return; } if (ss.getCurrentEpoch() != -1) { if (ss.isMoreRecentThan(leaderStateSummary)) { throw new IOException("Follower is ahead of the leader, leader summary: " + leaderStateSummary.getCurrentEpoch() + " (current epoch), " + leaderStateSummary.getLastZxid() + " (last zxid)"); } if (ss.getLastZxid() != -1 && isParticipant(id)) { electingFollowers.add(id); } } QuorumVerifier verifier = self.getQuorumVerifier(); if (electingFollowers.contains(self.getMyId()) && verifier.containsQuorum(electingFollowers)) { electionFinished = true; electingFollowers.notifyAll(); } else { long start = Time.currentElapsedTime(); long cur = start; long end = start + self.getInitLimit() * self.getTickTime(); while (!electionFinished && cur < end) { electingFollowers.wait(end - cur); cur = Time.currentElapsedTime(); } if (!electionFinished) { throw new InterruptedException("Timeout while waiting for epoch to be acked by quorum"); } } } } /** * Return a list of sid in set as string */ private String getSidSetString(Set sidSet) { StringBuilder sids = new StringBuilder(); Iterator iter = sidSet.iterator(); while (iter.hasNext()) { sids.append(iter.next()); if (!iter.hasNext()) { break; } sids.append(","); } return sids.toString(); } /** * Start up Leader ZooKeeper server and initialize zxid to the new epoch */ private synchronized void startZkServer() { // Update lastCommitted and Db's zxid to a value representing the new epoch lastCommitted = zk.getZxid(); LOG.info("Have quorum of supporters, sids: [{}]; starting up and setting last processed zxid: 0x{}", newLeaderProposal.ackSetsToString(), Long.toHexString(zk.getZxid())); if (self.isReconfigEnabled()) { /* * ZOOKEEPER-1324. the leader sends the new config it must complete * to others inside a NEWLEADER message (see LearnerHandler where * the NEWLEADER message is constructed), and once it has enough * acks we must execute the following code so that it applies the * config to itself. */ QuorumVerifier newQV = self.getLastSeenQuorumVerifier(); Long designatedLeader = getDesignatedLeader(newLeaderProposal, zk.getZxid()); self.processReconfig(newQV, designatedLeader, zk.getZxid(), true); if (designatedLeader != self.getMyId()) { LOG.warn("This leader is not the designated leader, it will be initialized with allowedToCommit = false"); allowedToCommit = false; } } else { LOG.info("Dynamic reconfig feature is disabled, skip designatedLeader calculation and reconfig processing."); } leaderStartTime = Time.currentElapsedTime(); zk.startup(); /* * Update the election vote here to ensure that all members of the * ensemble report the same vote to new servers that start up and * send leader election notifications to the ensemble. * * @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732 */ self.updateElectionVote(getEpoch()); zk.getZKDatabase().setlastProcessedZxid(zk.getZxid()); } /** * Process NEWLEADER ack of a given sid and wait until the leader receives * sufficient acks. * * @param sid * @throws InterruptedException */ @Override public void waitForNewLeaderAck(long sid, long zxid) throws InterruptedException { synchronized (newLeaderProposal.qvAcksetPairs) { if (quorumFormed) { return; } long currentZxid = newLeaderProposal.packet.getZxid(); if (zxid != currentZxid) { LOG.error( "NEWLEADER ACK from sid: {} is from a different epoch - current 0x{} received 0x{}", sid, Long.toHexString(currentZxid), Long.toHexString(zxid)); return; } /* * Note that addAck already checks that the learner * is a PARTICIPANT. */ newLeaderProposal.addAck(sid); if (newLeaderProposal.hasAllQuorums()) { quorumFormed = true; newLeaderProposal.qvAcksetPairs.notifyAll(); } else { long start = Time.currentElapsedTime(); long cur = start; long end = start + self.getInitLimit() * self.getTickTime(); while (!quorumFormed && cur < end) { newLeaderProposal.qvAcksetPairs.wait(end - cur); cur = Time.currentElapsedTime(); } if (!quorumFormed) { throw new InterruptedException("Timeout while waiting for NEWLEADER to be acked by quorum"); } } } } /** * Get string representation of a given packet type * @param packetType * @return string representing the packet type */ public static String getPacketType(int packetType) { switch (packetType) { case DIFF: return "DIFF"; case TRUNC: return "TRUNC"; case SNAP: return "SNAP"; case OBSERVERINFO: return "OBSERVERINFO"; case NEWLEADER: return "NEWLEADER"; case FOLLOWERINFO: return "FOLLOWERINFO"; case UPTODATE: return "UPTODATE"; case LEADERINFO: return "LEADERINFO"; case ACKEPOCH: return "ACKEPOCH"; case REQUEST: return "REQUEST"; case PROPOSAL: return "PROPOSAL"; case ACK: return "ACK"; case COMMIT: return "COMMIT"; case COMMITANDACTIVATE: return "COMMITANDACTIVATE"; case PING: return "PING"; case REVALIDATE: return "REVALIDATE"; case SYNC: return "SYNC"; case INFORM: return "INFORM"; case INFORMANDACTIVATE: return "INFORMANDACTIVATE"; default: return "UNKNOWN"; } } private boolean isRunning() { return self.isRunning() && zk.isRunning(); } private boolean isParticipant(long sid) { return self.getQuorumVerifier().getVotingMembers().containsKey(sid); } @Override public int getCurrentTick() { return self.tick.get(); } @Override public int syncTimeout() { return self.tickTime * self.syncLimit; } @Override public int getTickOfNextAckDeadline() { return self.tick.get() + self.syncLimit; } @Override public int getTickOfInitialAckDeadline() { return self.tick.get() + self.initLimit + self.syncLimit; } @Override public long getAndDecrementFollowerCounter() { return followerCounter.getAndDecrement(); } @Override public void touch(long sess, int to) { zk.touch(sess, to); } @Override public void submitLearnerRequest(Request si) { zk.submitLearnerRequest(si); } @Override public long getQuorumVerifierVersion() { return self.getQuorumVerifier().getVersion(); } @Override public String getPeerInfo(long sid) { QuorumPeer.QuorumServer server = self.getView().get(sid); return server == null ? "" : server.toString(); } @Override public byte[] getQuorumVerifierBytes() { return self.getLastSeenQuorumVerifier().toString().getBytes(UTF_8); } @Override public QuorumAuthServer getQuorumAuthServer() { return (self == null) ? null : self.authServer; } @Override public void revalidateSession(QuorumPacket qp, LearnerHandler learnerHandler) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); DataInputStream dis = new DataInputStream(bis); long id = dis.readLong(); int to = dis.readInt(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); dos.writeLong(id); boolean valid = zk.checkIfValidGlobalSession(id, to); if (valid) { try { // set the session owner as the follower that owns the session zk.setOwner(id, learnerHandler); } catch (KeeperException.SessionExpiredException e) { LOG.error( "Somehow session 0x{} expired right after being renewed! (impossible)", Long.toHexString(id), e); } } if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "Session 0x" + Long.toHexString(id) + " is valid: " + valid); } dos.writeBoolean(valid); qp.setData(bos.toByteArray()); learnerHandler.queuePacket(qp); } @Override public void registerLearnerHandlerBean(final LearnerHandler learnerHandler, Socket socket) { LearnerHandlerBean bean = new LearnerHandlerBean(learnerHandler, socket); if (zk.registerJMX(bean)) { connectionBeans.put(learnerHandler, bean); } } @Override public void unregisterLearnerHandlerBean(final LearnerHandler learnerHandler) { LearnerHandlerBean bean = connectionBeans.remove(learnerHandler); if (bean != null) { MBeanRegistry.getInstance().unregister(bean); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000162 15051152474 032716 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderBean.0100644 0000000 0000000 00000006227 15051152474 034122 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServerBean; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; /** * Leader MBean interface implementation. */ public class LeaderBean extends ZooKeeperServerBean implements LeaderMXBean { private final Leader leader; public LeaderBean(Leader leader, ZooKeeperServer zks) { super(zks); this.leader = leader; } public String getName() { return "Leader"; } public String getCurrentZxid() { return "0x" + Long.toHexString(zks.getZxid()); } public String followerInfo() { StringBuilder sb = new StringBuilder(); for (LearnerHandler handler : leader.getLearners()) { if (handler.getLearnerType() == LearnerType.PARTICIPANT) { sb.append(handler.toString()).append("\n"); } } return sb.toString(); } @Override public String nonVotingFollowerInfo() { StringBuilder sb = new StringBuilder(); for (LearnerHandler handler : leader.getNonVotingFollowers()) { sb.append(handler.toString()).append("\n"); } return sb.toString(); } @Override public long getElectionTimeTaken() { return leader.self.getElectionTimeTaken(); } @Override public int getLastProposalSize() { return leader.getProposalStats().getLastBufferSize(); } @Override public int getMinProposalSize() { return leader.getProposalStats().getMinBufferSize(); } @Override public int getMaxProposalSize() { return leader.getProposalStats().getMaxBufferSize(); } @Override public void resetProposalStatistics() { leader.getProposalStats().reset(); } @Override public int getMaxConcurrentSnapSyncs() { return leader.getMaxConcurrentSnapSyncs(); } @Override public void setMaxConcurrentSnapSyncs(int maxConcurrentSnapshots) { leader.setMaxConcurrentSnapSyncs(maxConcurrentSnapshots); } @Override public int getMaxConcurrentDiffSyncs() { return leader.getMaxConcurrentDiffSyncs(); } @Override public void setMaxConcurrentDiffSyncs(int maxConcurrentDiffSyncs) { leader.setMaxConcurrentDiffSyncs(maxConcurrentDiffSyncs); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032717 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderElectionBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderElect0100644 0000000 0000000 00000002447 15051152474 034233 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.Date; import org.apache.zookeeper.jmx.ZKMBeanInfo; /** * Leader election MBean interface implementation */ public class LeaderElectionBean implements LeaderElectionMXBean, ZKMBeanInfo { private final Date startTime = new Date(); public String getName() { return "LeaderElection"; } public boolean isHidden() { return false; } public String getStartTime() { return startTime.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032721 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderElectionMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderElect0100644 0000000 0000000 00000002026 15051152474 034224 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * Leader election protocol MBean. */ public interface LeaderElectionMXBean { /** * * @return the time when the leader election started */ String getStartTime(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032720 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderMXBea0100644 0000000 0000000 00000004571 15051152474 034133 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.ZooKeeperServerMXBean; /** * Leader MBean. */ public interface LeaderMXBean extends ZooKeeperServerMXBean { /** * Current zxid of cluster. */ String getCurrentZxid(); /** * @return information on current followers */ String followerInfo(); /** * @return information about current non-voting followers */ String nonVotingFollowerInfo(); /** * @return time taken for leader election in milliseconds. */ long getElectionTimeTaken(); /** * @return size of latest generated proposal */ int getLastProposalSize(); /** * @return size of smallest generated proposal */ int getMinProposalSize(); /** * @return size of largest generated proposal */ int getMaxProposalSize(); /** * Resets statistics of proposal size (min/max/last) */ void resetProposalStatistics(); /** * @return Number of concurrent snapshots permitted to send to observers */ int getMaxConcurrentSnapSyncs(); /** * @param maxConcurrentSnapSyncs Number of concurrent snapshots permitted to send to observers */ void setMaxConcurrentSnapSyncs(int maxConcurrentSnapSyncs); /** * @return Number of concurrent diff syncs permitted to send to observers */ int getMaxConcurrentDiffSyncs(); /** * @param maxConcurrentDiffSyncs Number of concurrent diff syncs permitted to send to observers */ void setMaxConcurrentDiffSyncs(int maxConcurrentDiffSyncs); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032723 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderReque0100644 0000000 0000000 00000005636 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.txn.ErrorTxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Responsible for performing local session upgrade. Only request submitted * directly to the leader should go through this processor. */ public class LeaderRequestProcessor implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(LeaderRequestProcessor.class); private final LeaderZooKeeperServer lzks; private final RequestProcessor nextProcessor; public LeaderRequestProcessor(LeaderZooKeeperServer zks, RequestProcessor nextProcessor) { this.lzks = zks; this.nextProcessor = nextProcessor; } @Override public void processRequest(Request request) throws RequestProcessorException { // Screen quorum requests against ACLs first if (!lzks.authWriteRequest(request)) { return; } // Check if this is a local session and we are trying to create // an ephemeral node, in which case we upgrade the session Request upgradeRequest = null; try { upgradeRequest = lzks.checkUpgradeSession(request); } catch (KeeperException ke) { if (request.getHdr() != null) { LOG.debug("Updating header"); request.getHdr().setType(OpCode.error); request.setTxn(new ErrorTxn(ke.code().intValue())); } request.setException(ke); LOG.warn("Error creating upgrade request", ke); } catch (IOException ie) { LOG.error("Unexpected error in upgrade", ie); } if (upgradeRequest != null) { nextProcessor.processRequest(upgradeRequest); } nextProcessor.processRequest(request); } @Override public void shutdown() { LOG.info("Shutting down"); nextProcessor.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032721 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderSessionTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderSessi0100644 0000000 0000000 00000021235 15051152474 034261 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.PrintWriter; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentMap; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.KeeperException.SessionMovedException; import org.apache.zookeeper.KeeperException.UnknownSessionException; import org.apache.zookeeper.server.SessionTrackerImpl; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The leader session tracker tracks local and global sessions on the leader. */ public class LeaderSessionTracker extends UpgradeableSessionTracker { private static final Logger LOG = LoggerFactory.getLogger(LeaderSessionTracker.class); private final SessionTrackerImpl globalSessionTracker; /** * Server id of the leader */ private final long serverId; public LeaderSessionTracker(SessionExpirer expirer, ConcurrentMap sessionsWithTimeouts, int tickTime, long id, boolean localSessionsEnabled, ZooKeeperServerListener listener) { this.globalSessionTracker = new SessionTrackerImpl(expirer, sessionsWithTimeouts, tickTime, id, listener); this.localSessionsEnabled = localSessionsEnabled; if (this.localSessionsEnabled) { createLocalSessionTracker(expirer, tickTime, id, listener); } serverId = id; } public void removeSession(long sessionId) { if (localSessionTracker != null) { localSessionTracker.removeSession(sessionId); } globalSessionTracker.removeSession(sessionId); } public void start() { globalSessionTracker.start(); if (localSessionTracker != null) { localSessionTracker.start(); } } public void shutdown() { if (localSessionTracker != null) { localSessionTracker.shutdown(); } globalSessionTracker.shutdown(); } public boolean isGlobalSession(long sessionId) { return globalSessionTracker.isTrackingSession(sessionId); } public boolean trackSession(long sessionId, int sessionTimeout) { boolean tracked = globalSessionTracker.trackSession(sessionId, sessionTimeout); if (localSessionsEnabled && tracked) { // Only do extra logging so we know what kind of session this is // if we're supporting both kinds of sessions LOG.info("Tracking global session 0x{}", Long.toHexString(sessionId)); } return tracked; } /** * Synchronized on this to avoid race condition of adding a local session * after committed global session, which may cause the same session being * tracked on this server and leader. */ public synchronized boolean commitSession( long sessionId, int sessionTimeout) { boolean added = globalSessionTracker.commitSession(sessionId, sessionTimeout); if (added) { LOG.info("Committing global session 0x{}", Long.toHexString(sessionId)); } // If the session moved before the session upgrade finished, it's // possible that the session will be added to the local session // again. Need to double check and remove it from local session // tracker when the global session is quorum committed, otherwise the // local session might be tracked both locally and on leader. // // This cannot totally avoid the local session being upgraded again // because there is still race condition between create another upgrade // request and process the createSession commit, and there is no way // to know there is a on flying createSession request because it might // be upgraded by other server which owns the session before move. if (localSessionsEnabled) { removeLocalSession(sessionId); finishedUpgrading(sessionId); } return added; } public boolean touchSession(long sessionId, int sessionTimeout) { if (localSessionTracker != null && localSessionTracker.touchSession(sessionId, sessionTimeout)) { return true; } return globalSessionTracker.touchSession(sessionId, sessionTimeout); } public long createSession(int sessionTimeout) { if (localSessionsEnabled) { return localSessionTracker.createSession(sessionTimeout); } return globalSessionTracker.createSession(sessionTimeout); } // Returns the serverId from the sessionId (the high order byte) public static long getServerIdFromSessionId(long sessionId) { return sessionId >> 56; } public void checkSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException, UnknownSessionException { if (localSessionTracker != null) { try { localSessionTracker.checkSession(sessionId, owner); // A session can both be a local and global session during // upgrade if (!isGlobalSession(sessionId)) { return; } } catch (UnknownSessionException e) { // Ignore. We'll check instead whether it's a global session } } try { globalSessionTracker.checkSession(sessionId, owner); // if we can get here, it is a valid global session return; } catch (UnknownSessionException e) { // Ignore. This may be local session from other servers. } /* * if local session is not enabled or it used to be our local session * throw sessions expires */ if (!localSessionsEnabled || (getServerIdFromSessionId(sessionId) == serverId)) { throw new SessionExpiredException(); } } public void checkGlobalSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException { try { globalSessionTracker.checkSession(sessionId, owner); } catch (UnknownSessionException e) { // For global session, if we don't know it, it is already expired throw new SessionExpiredException(); } } public void setOwner(long sessionId, Object owner) throws SessionExpiredException { if (localSessionTracker != null) { try { localSessionTracker.setOwner(sessionId, owner); return; } catch (SessionExpiredException e) { // Ignore. We'll check instead whether it's a global session } } globalSessionTracker.setOwner(sessionId, owner); } public void dumpSessions(PrintWriter pwriter) { if (localSessionTracker != null) { pwriter.print("Local "); localSessionTracker.dumpSessions(pwriter); pwriter.print("Global "); } globalSessionTracker.dumpSessions(pwriter); } public void setSessionClosing(long sessionId) { // call is no-op if session isn't tracked so safe to call both if (localSessionTracker != null) { localSessionTracker.setSessionClosing(sessionId); } globalSessionTracker.setSessionClosing(sessionId); } public Map> getSessionExpiryMap() { Map> sessionExpiryMap; // combine local and global sessions, getting local first so upgrades // to global are caught if (localSessionTracker != null) { sessionExpiryMap = localSessionTracker.getSessionExpiryMap(); } else { sessionExpiryMap = new TreeMap>(); } sessionExpiryMap.putAll(globalSessionTracker.getSessionExpiryMap()); return sessionExpiryMap; } public Set globalSessions() { return globalSessionTracker.globalSessions(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032722 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LeaderZooKe0100644 0000000 0000000 00000026121 15051152474 034221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.function.Function; import javax.management.JMException; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.server.ContainerManager; import org.apache.zookeeper.server.DataTreeBean; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.PrepRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; /** * * Just like the standard ZooKeeperServer. We just replace the request * processors: PrepRequestProcessor -> ProposalRequestProcessor -> * CommitProcessor -> Leader.ToBeAppliedRequestProcessor -> * FinalRequestProcessor */ public class LeaderZooKeeperServer extends QuorumZooKeeperServer { private ContainerManager containerManager; // guarded by sync CommitProcessor commitProcessor; PrepRequestProcessor prepRequestProcessor; /** * @throws IOException */ public LeaderZooKeeperServer(FileTxnSnapLog logFactory, QuorumPeer self, ZKDatabase zkDb) throws IOException { super(logFactory, self.tickTime, self.minSessionTimeout, self.maxSessionTimeout, self.clientPortListenBacklog, zkDb, self); } public Leader getLeader() { return self.leader; } @Override protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); RequestProcessor toBeAppliedProcessor = new Leader.ToBeAppliedRequestProcessor(finalProcessor, getLeader()); commitProcessor = new CommitProcessor(toBeAppliedProcessor, Long.toString(getServerId()), false, getZooKeeperServerListener()); commitProcessor.start(); ProposalRequestProcessor proposalProcessor = new ProposalRequestProcessor(this, commitProcessor); proposalProcessor.initialize(); prepRequestProcessor = new PrepRequestProcessor(this, proposalProcessor); prepRequestProcessor.start(); firstProcessor = new LeaderRequestProcessor(this, prepRequestProcessor); setupContainerManager(); } private synchronized void setupContainerManager() { containerManager = new ContainerManager( getZKDatabase(), prepRequestProcessor, Integer.getInteger("znode.container.checkIntervalMs", (int) TimeUnit.MINUTES.toMillis(1)), Integer.getInteger("znode.container.maxPerMinute", 10000), Long.getLong("znode.container.maxNeverUsedIntervalMs", 0) ); } @Override public synchronized void startup() { super.startup(); if (containerManager != null) { containerManager.start(); } } @Override protected void registerMetrics() { super.registerMetrics(); MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.registerGauge("learners", gaugeWithLeader( (leader) -> leader.getLearners().size()) ); rootContext.registerGauge("synced_followers", gaugeWithLeader( (leader) -> leader.getForwardingFollowers().size() )); rootContext.registerGauge("synced_non_voting_followers", gaugeWithLeader( (leader) -> leader.getNonVotingFollowers().size() )); rootContext.registerGauge("synced_observers", self::getSynced_observers_metric); rootContext.registerGauge("pending_syncs", gaugeWithLeader( (leader) -> leader.getNumPendingSyncs() )); rootContext.registerGauge("leader_uptime", gaugeWithLeader( (leader) -> leader.getUptime() )); rootContext.registerGauge("last_proposal_size", gaugeWithLeader( (leader) -> leader.getProposalStats().getLastBufferSize() )); rootContext.registerGauge("max_proposal_size", gaugeWithLeader( (leader) -> leader.getProposalStats().getMaxBufferSize() )); rootContext.registerGauge("min_proposal_size", gaugeWithLeader( (leader) -> leader.getProposalStats().getMinBufferSize() )); } private org.apache.zookeeper.metrics.Gauge gaugeWithLeader(Function supplier) { return () -> { final Leader leader = getLeader(); if (leader == null) { return null; } return supplier.apply(leader); }; } @Override protected void unregisterMetrics() { super.unregisterMetrics(); MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.unregisterGauge("learners"); rootContext.unregisterGauge("synced_followers"); rootContext.unregisterGauge("synced_non_voting_followers"); rootContext.unregisterGauge("synced_observers"); rootContext.unregisterGauge("pending_syncs"); rootContext.unregisterGauge("leader_uptime"); rootContext.unregisterGauge("last_proposal_size"); rootContext.unregisterGauge("max_proposal_size"); rootContext.unregisterGauge("min_proposal_size"); } @Override protected synchronized void shutdownComponents() { if (containerManager != null) { containerManager.stop(); } super.shutdownComponents(); } @Override public int getGlobalOutstandingLimit() { int divisor = self.getQuorumSize() > 2 ? self.getQuorumSize() - 1 : 1; int globalOutstandingLimit = super.getGlobalOutstandingLimit() / divisor; return globalOutstandingLimit; } @Override public void createSessionTracker() { sessionTracker = new LeaderSessionTracker( this, getZKDatabase().getSessionWithTimeOuts(), tickTime, self.getMyId(), self.areLocalSessionsEnabled(), getZooKeeperServerListener()); } public boolean touch(long sess, int to) { return sessionTracker.touchSession(sess, to); } public boolean checkIfValidGlobalSession(long sess, int to) { if (self.areLocalSessionsEnabled() && !upgradeableSessionTracker.isGlobalSession(sess)) { return false; } return sessionTracker.touchSession(sess, to); } /** * Requests coming from the learner should go directly to * PrepRequestProcessor * * @param request */ public void submitLearnerRequest(Request request) { /* * Requests coming from the learner should have gone through * submitRequest() on each server which already perform some request * validation, so we don't need to do it again. * * Additionally, LearnerHandler should start submitting requests into * the leader's pipeline only when the leader's server is started, so we * can submit the request directly into PrepRequestProcessor. * * This is done so that requests from learners won't go through * LeaderRequestProcessor which perform local session upgrade. */ prepRequestProcessor.processRequest(request); } @Override protected void registerJMX() { // register with JMX try { jmxDataTreeBean = new DataTreeBean(getZKDatabase().getDataTree()); MBeanRegistry.getInstance().register(jmxDataTreeBean, jmxServerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxDataTreeBean = null; } } public void registerJMX(LeaderBean leaderBean, LocalPeerBean localPeerBean) { // register with JMX if (self.jmxLeaderElectionBean != null) { try { MBeanRegistry.getInstance().unregister(self.jmxLeaderElectionBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); } self.jmxLeaderElectionBean = null; } try { jmxServerBean = leaderBean; MBeanRegistry.getInstance().register(leaderBean, localPeerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxServerBean = null; } } boolean registerJMX(LearnerHandlerBean handlerBean) { try { MBeanRegistry.getInstance().register(handlerBean, jmxServerBean); return true; } catch (JMException e) { LOG.warn("Could not register connection", e); } return false; } @Override protected void unregisterJMX() { // unregister from JMX try { if (jmxDataTreeBean != null) { MBeanRegistry.getInstance().unregister(jmxDataTreeBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxDataTreeBean = null; } protected void unregisterJMX(Leader leader) { // unregister from JMX try { if (jmxServerBean != null) { MBeanRegistry.getInstance().unregister(jmxServerBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxServerBean = null; } @Override public String getState() { return "leader"; } /** * Returns the id of the associated QuorumPeer, which will do for a unique * id of this server. */ @Override public long getServerId() { return self.getMyId(); } @Override protected void revalidateSession(ServerCnxn cnxn, long sessionId, int sessionTimeout) throws IOException { super.revalidateSession(cnxn, sessionId, sessionTimeout); try { // setowner as the leader itself, unless updated // via the follower handlers setOwner(sessionId, ServerCnxn.me); } catch (SessionExpiredException e) { // this is ok, it just means that the session revalidation failed. } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000157 15051152474 032722 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Learner.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Learner.jav0100644 0000000 0000000 00000122457 15051152474 034235 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.Deque; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLSocket; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ConfigUtils; import org.apache.zookeeper.server.util.MessageTracker; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is the superclass of two of the three main actors in a ZK * ensemble: Followers and Observers. Both Followers and Observers share * a good deal of code which is moved into Peer to avoid duplication. */ public class Learner { static class PacketInFlight { TxnHeader hdr; Record rec; TxnDigest digest; Request toRequest() { return new Request(hdr, rec, digest); } } QuorumPeer self; LearnerZooKeeperServer zk; protected BufferedOutputStream bufferedOutput; protected Socket sock; protected MultipleAddresses leaderAddr; protected AtomicBoolean sockBeingClosed = new AtomicBoolean(false); /** * Socket getter */ public Socket getSocket() { return sock; } LearnerSender sender = null; protected InputArchive leaderIs; protected OutputArchive leaderOs; /** the protocol version of the leader */ protected int leaderProtocolVersion = 0x01; private static final int BUFFERED_MESSAGE_SIZE = 10; protected final MessageTracker messageTracker = new MessageTracker(BUFFERED_MESSAGE_SIZE); protected static final Logger LOG = LoggerFactory.getLogger(Learner.class); /** * Time to wait after connection attempt with the Leader or LearnerMaster before this * Learner tries to connect again. */ private static final int leaderConnectDelayDuringRetryMs = Integer.getInteger("zookeeper.leaderConnectDelayDuringRetryMs", 100); private static final boolean nodelay = System.getProperty("follower.nodelay", "true").equals("true"); public static final String LEARNER_ASYNC_SENDING = "zookeeper.learner.asyncSending"; private static boolean asyncSending = Boolean.parseBoolean(ConfigUtils.getPropertyBackwardCompatibleWay(LEARNER_ASYNC_SENDING)); public static final String LEARNER_CLOSE_SOCKET_ASYNC = "zookeeper.learner.closeSocketAsync"; public static final boolean closeSocketAsync = Boolean .parseBoolean(ConfigUtils.getPropertyBackwardCompatibleWay(LEARNER_CLOSE_SOCKET_ASYNC)); static { LOG.info("leaderConnectDelayDuringRetryMs: {}", leaderConnectDelayDuringRetryMs); LOG.info("TCP NoDelay set to: {}", nodelay); LOG.info("{} = {}", LEARNER_ASYNC_SENDING, asyncSending); LOG.info("{} = {}", LEARNER_CLOSE_SOCKET_ASYNC, closeSocketAsync); } final ConcurrentHashMap pendingRevalidations = new ConcurrentHashMap<>(); public int getPendingRevalidationsCount() { return pendingRevalidations.size(); } // for testing protected static void setAsyncSending(boolean newMode) { asyncSending = newMode; LOG.info("{} = {}", LEARNER_ASYNC_SENDING, asyncSending); } protected static boolean getAsyncSending() { return asyncSending; } /** * validate a session for a client * * @param clientId * the client to be revalidated * @param timeout * the timeout for which the session is valid * @throws IOException */ void validateSession(ServerCnxn cnxn, long clientId, int timeout) throws IOException { LOG.info("Revalidating client: 0x{}", Long.toHexString(clientId)); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeLong(clientId); dos.writeInt(timeout); dos.close(); QuorumPacket qp = new QuorumPacket(Leader.REVALIDATE, -1, baos.toByteArray(), null); pendingRevalidations.put(clientId, cnxn); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "To validate session 0x" + Long.toHexString(clientId)); } writePacket(qp, true); } /** * write a packet to the leader. * * This method is called by multiple threads. We need to make sure that only one thread is writing to leaderOs at a time. * When packets are sent synchronously, writing is done within a synchronization block. * When packets are sent asynchronously, sender.queuePacket() is called, which writes to a BlockingQueue, which is thread-safe. * Reading from this BlockingQueue and writing to leaderOs is the learner sender thread only. * So we have only one thread writing to leaderOs at a time in either case. * * @param pp * the proposal packet to be sent to the leader * @throws IOException */ void writePacket(QuorumPacket pp, boolean flush) throws IOException { if (asyncSending) { sender.queuePacket(pp); } else { writePacketNow(pp, flush); } } void writePacketNow(QuorumPacket pp, boolean flush) throws IOException { synchronized (leaderOs) { if (pp != null) { messageTracker.trackSent(pp.getType()); leaderOs.writeRecord(pp, "packet"); } if (flush) { bufferedOutput.flush(); } } } /** * Start thread that will forward any packet in the queue to the leader */ protected void startSendingThread() { sender = new LearnerSender(this); sender.start(); } /** * read a packet from the leader * * @param pp * the packet to be instantiated * @throws IOException */ void readPacket(QuorumPacket pp) throws IOException { synchronized (leaderIs) { leaderIs.readRecord(pp, "packet"); messageTracker.trackReceived(pp.getType()); } if (LOG.isTraceEnabled()) { final long traceMask = (pp.getType() == Leader.PING) ? ZooTrace.SERVER_PING_TRACE_MASK : ZooTrace.SERVER_PACKET_TRACE_MASK; ZooTrace.logQuorumPacket(LOG, traceMask, 'i', pp); } } /** * send a request packet to the leader * * @param request * the request from the client * @throws IOException */ void request(Request request) throws IOException { if (request.isThrottled()) { LOG.error("Throttled request sent to leader: {}. Exiting", request); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream oa = new DataOutputStream(baos); oa.writeLong(request.sessionId); oa.writeInt(request.cxid); oa.writeInt(request.type); byte[] payload = request.readRequestBytes(); if (payload != null) { oa.write(payload); } oa.close(); QuorumPacket qp = new QuorumPacket(Leader.REQUEST, -1, baos.toByteArray(), request.authInfo); writePacket(qp, true); } /** * Returns the address of the node we think is the leader. */ protected QuorumServer findLeader() { QuorumServer leaderServer = null; // Find the leader by id Vote current = self.getCurrentVote(); for (QuorumServer s : self.getView().values()) { if (s.id == current.getId()) { // Ensure we have the leader's correct IP address before // attempting to connect. s.recreateSocketAddresses(); leaderServer = s; break; } } if (leaderServer == null) { LOG.warn("Couldn't find the leader with id = {}", current.getId()); } return leaderServer; } /** * Overridable helper method to return the System.nanoTime(). * This method behaves identical to System.nanoTime(). */ protected long nanoTime() { return System.nanoTime(); } /** * Overridable helper method to simply call sock.connect(). This can be * overridden in tests to fake connection success/failure for connectToLeader. */ protected void sockConnect(Socket sock, InetSocketAddress addr, int timeout) throws IOException { sock.connect(addr, timeout); } /** * Establish a connection with the LearnerMaster found by findLearnerMaster. * Followers only connect to Leaders, Observers can connect to any active LearnerMaster. * Retries until either initLimit time has elapsed or 5 tries have happened. * @param multiAddr - the address of the Peer to connect to. * @throws IOException - if the socket connection fails on the 5th attempt * if there is an authentication failure while connecting to leader */ protected void connectToLeader(MultipleAddresses multiAddr, String hostname) throws IOException { this.leaderAddr = multiAddr; Set addresses; if (self.isMultiAddressReachabilityCheckEnabled()) { // even if none of the addresses are reachable, we want to try to establish connection // see ZOOKEEPER-3758 addresses = multiAddr.getAllReachableAddressesOrAll(); } else { addresses = multiAddr.getAllAddresses(); } ExecutorService executor = Executors.newFixedThreadPool(addresses.size()); CountDownLatch latch = new CountDownLatch(addresses.size()); AtomicReference socket = new AtomicReference<>(null); addresses.stream().map(address -> new LeaderConnector(address, socket, latch)).forEach(executor::submit); try { latch.await(); } catch (InterruptedException e) { LOG.warn("Interrupted while trying to connect to Leader", e); } finally { executor.shutdown(); try { if (!executor.awaitTermination(1, TimeUnit.SECONDS)) { LOG.error("not all the LeaderConnector terminated properly"); } } catch (InterruptedException ie) { LOG.error("Interrupted while terminating LeaderConnector executor.", ie); } } if (socket.get() == null) { throw new IOException("Failed connect to " + multiAddr); } else { sock = socket.get(); sockBeingClosed.set(false); } self.authLearner.authenticate(sock, hostname); leaderIs = BinaryInputArchive.getArchive(new BufferedInputStream(sock.getInputStream())); bufferedOutput = new BufferedOutputStream(sock.getOutputStream()); leaderOs = BinaryOutputArchive.getArchive(bufferedOutput); if (asyncSending) { startSendingThread(); } } class LeaderConnector implements Runnable { private AtomicReference socket; private InetSocketAddress address; private CountDownLatch latch; LeaderConnector(InetSocketAddress address, AtomicReference socket, CountDownLatch latch) { this.address = address; this.socket = socket; this.latch = latch; } @Override public void run() { try { Thread.currentThread().setName("LeaderConnector-" + address); Socket sock = connectToLeader(); if (sock != null && sock.isConnected()) { if (socket.compareAndSet(null, sock)) { LOG.info("Successfully connected to leader, using address: {}", address); } else { LOG.info("Connection to the leader is already established, close the redundant connection"); sock.close(); } } } catch (Exception e) { LOG.error("Failed connect to {}", address, e); } finally { latch.countDown(); } } private Socket connectToLeader() throws IOException, X509Exception, InterruptedException { Socket sock = createSocket(); // leader connection timeout defaults to tickTime * initLimit int connectTimeout = self.tickTime * self.initLimit; // but if connectToLearnerMasterLimit is specified, use that value to calculate // timeout instead of using the initLimit value if (self.connectToLearnerMasterLimit > 0) { connectTimeout = self.tickTime * self.connectToLearnerMasterLimit; } int remainingTimeout; long startNanoTime = nanoTime(); for (int tries = 0; tries < 5 && socket.get() == null; tries++) { try { // recalculate the init limit time because retries sleep for 1000 milliseconds remainingTimeout = connectTimeout - (int) ((nanoTime() - startNanoTime) / 1_000_000); if (remainingTimeout <= 0) { LOG.error("connectToLeader exceeded on retries."); throw new IOException("connectToLeader exceeded on retries."); } sockConnect(sock, address, Math.min(connectTimeout, remainingTimeout)); if (self.isSslQuorum()) { ((SSLSocket) sock).startHandshake(); } sock.setTcpNoDelay(nodelay); break; } catch (IOException e) { remainingTimeout = connectTimeout - (int) ((nanoTime() - startNanoTime) / 1_000_000); if (remainingTimeout <= leaderConnectDelayDuringRetryMs) { LOG.error( "Unexpected exception, connectToLeader exceeded. tries={}, remaining init limit={}, connecting to {}", tries, remainingTimeout, address, e); throw e; } else if (tries >= 4) { LOG.error( "Unexpected exception, retries exceeded. tries={}, remaining init limit={}, connecting to {}", tries, remainingTimeout, address, e); throw e; } else { LOG.warn( "Unexpected exception, tries={}, remaining init limit={}, connecting to {}", tries, remainingTimeout, address, e); sock = createSocket(); } } Thread.sleep(leaderConnectDelayDuringRetryMs); } return sock; } } /** * Creating a simple or and SSL socket. * This can be overridden in tests to fake already connected sockets for connectToLeader. */ protected Socket createSocket() throws X509Exception, IOException { Socket sock; if (self.isSslQuorum()) { sock = self.getX509Util().createSSLSocket(); } else { sock = new Socket(); } sock.setSoTimeout(self.tickTime * self.initLimit); return sock; } /** * Once connected to the leader or learner master, perform the handshake * protocol to establish a following / observing connection. * @param pktType * @return the zxid the Leader sends for synchronization purposes. * @throws IOException */ protected long registerWithLeader(int pktType) throws IOException { /* * Send follower info, including last zxid and sid */ long lastLoggedZxid = self.getLastLoggedZxid(); QuorumPacket qp = new QuorumPacket(); qp.setType(pktType); qp.setZxid(ZxidUtils.makeZxid(self.getAcceptedEpoch(), 0)); /* * Add sid to payload */ LearnerInfo li = new LearnerInfo(self.getMyId(), 0x10000, self.getQuorumVerifier().getVersion()); ByteArrayOutputStream bsid = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(bsid); boa.writeRecord(li, "LearnerInfo"); qp.setData(bsid.toByteArray()); writePacket(qp, true); readPacket(qp); final long newEpoch = ZxidUtils.getEpochFromZxid(qp.getZxid()); if (qp.getType() == Leader.LEADERINFO) { // we are connected to a 1.0 server so accept the new epoch and read the next packet leaderProtocolVersion = ByteBuffer.wrap(qp.getData()).getInt(); byte[] epochBytes = new byte[4]; final ByteBuffer wrappedEpochBytes = ByteBuffer.wrap(epochBytes); if (newEpoch > self.getAcceptedEpoch()) { wrappedEpochBytes.putInt((int) self.getCurrentEpoch()); self.setAcceptedEpoch(newEpoch); } else if (newEpoch == self.getAcceptedEpoch()) { // since we have already acked an epoch equal to the leaders, we cannot ack // again, but we still need to send our lastZxid to the leader so that we can // sync with it if it does assume leadership of the epoch. // the -1 indicates that this reply should not count as an ack for the new epoch wrappedEpochBytes.putInt(-1); } else { throw new IOException("Leaders epoch, " + newEpoch + " is less than accepted epoch, " + self.getAcceptedEpoch()); } QuorumPacket ackNewEpoch = new QuorumPacket(Leader.ACKEPOCH, lastLoggedZxid, epochBytes, null); writePacket(ackNewEpoch, true); return ZxidUtils.makeZxid(newEpoch, 0); } else { if (newEpoch > self.getAcceptedEpoch()) { self.setAcceptedEpoch(newEpoch); } if (qp.getType() != Leader.NEWLEADER) { LOG.error("First packet should have been NEWLEADER"); throw new IOException("First packet should have been NEWLEADER"); } return qp.getZxid(); } } long enforceContinuousProposal(long lastQueued, PacketInFlight pif) throws Exception { if (lastQueued == 0) { LOG.info("DIFF sync got first proposal 0x{}", Long.toHexString(pif.hdr.getZxid())); } else if (pif.hdr.getZxid() != lastQueued + 1) { if (ZxidUtils.getEpochFromZxid(pif.hdr.getZxid()) <= ZxidUtils.getEpochFromZxid(lastQueued)) { String msg = String.format( "DIFF sync got proposal 0x%s, last queued 0x%s, expected 0x%s", Long.toHexString(pif.hdr.getZxid()), Long.toHexString(lastQueued), Long.toHexString(lastQueued + 1)); LOG.error(msg); throw new Exception(msg); } // We can't tell whether it is a data loss. Given that new epoch is rare, // log at warn should not be too verbose. LOG.warn("DIFF sync got new epoch proposal 0x{}, last queued 0x{}, expected 0x{}", Long.toHexString(pif.hdr.getZxid()), Long.toHexString(lastQueued), Long.toHexString(lastQueued + 1)); } return pif.hdr.getZxid(); } /** * Finally, synchronize our history with the Leader (if Follower) * or the LearnerMaster (if Observer). * @param newLeaderZxid * @throws IOException * @throws InterruptedException */ protected void syncWithLeader(long newLeaderZxid) throws Exception { QuorumPacket ack = new QuorumPacket(Leader.ACK, 0, null, null); QuorumPacket qp = new QuorumPacket(); long newEpoch = ZxidUtils.getEpochFromZxid(newLeaderZxid); QuorumVerifier newLeaderQV = null; // In the DIFF case we don't need to do a snapshot because the transactions will sync on top of any existing snapshot // For SNAP and TRUNC the snapshot is needed to save that history boolean snapshotNeeded = true; boolean syncSnapshot = false; readPacket(qp); Deque packetsCommitted = new ArrayDeque<>(); Deque packetsNotLogged = new ArrayDeque<>(); synchronized (zk) { if (qp.getType() == Leader.DIFF) { LOG.info("Getting a diff from the leader 0x{}", Long.toHexString(qp.getZxid())); self.setSyncMode(QuorumPeer.SyncMode.DIFF); if (zk.shouldForceWriteInitialSnapshotAfterLeaderElection()) { LOG.info("Forcing a snapshot write as part of upgrading from an older Zookeeper. This should only happen while upgrading."); snapshotNeeded = true; syncSnapshot = true; } else { snapshotNeeded = false; } } else if (qp.getType() == Leader.SNAP) { self.setSyncMode(QuorumPeer.SyncMode.SNAP); LOG.info("Getting a snapshot from leader 0x{}", Long.toHexString(qp.getZxid())); // The leader is going to dump the database // db is clear as part of deserializeSnapshot() zk.getZKDatabase().deserializeSnapshot(leaderIs); // ZOOKEEPER-2819: overwrite config node content extracted // from leader snapshot with local config, to avoid potential // inconsistency of config node content during rolling restart. if (!self.isReconfigEnabled()) { LOG.debug("Reset config node content from local config after deserialization of snapshot."); zk.getZKDatabase().initConfigInZKDatabase(self.getQuorumVerifier()); } String signature = leaderIs.readString("signature"); if (!signature.equals("BenWasHere")) { LOG.error("Missing signature. Got {}", signature); throw new IOException("Missing signature"); } zk.getZKDatabase().setlastProcessedZxid(qp.getZxid()); // immediately persist the latest snapshot when there is txn log gap syncSnapshot = true; } else if (qp.getType() == Leader.TRUNC) { //we need to truncate the log to the lastzxid of the leader self.setSyncMode(QuorumPeer.SyncMode.TRUNC); LOG.warn("Truncating log to get in sync with the leader 0x{}", Long.toHexString(qp.getZxid())); boolean truncated = zk.getZKDatabase().truncateLog(qp.getZxid()); if (!truncated) { // not able to truncate the log LOG.error("Not able to truncate the log 0x{}", Long.toHexString(qp.getZxid())); ServiceUtils.requestSystemExit(ExitCode.QUORUM_PACKET_ERROR.getValue()); } zk.getZKDatabase().setlastProcessedZxid(qp.getZxid()); } else { LOG.error("Got unexpected packet from leader: {}, exiting ... ", LearnerHandler.packetToString(qp)); ServiceUtils.requestSystemExit(ExitCode.QUORUM_PACKET_ERROR.getValue()); } zk.getZKDatabase().initConfigInZKDatabase(self.getQuorumVerifier()); zk.createSessionTracker(); // TODO: Ideally, this should be lastProcessZxid(a.k.a. QuorumPacket::zxid from above), but currently // LearnerHandler does not guarantee this. So, let's be conservative and keep it unchange for now. long lastQueued = 0; // in Zab V1.0 (ZK 3.4+) we might take a snapshot when we get the NEWLEADER message, but in pre V1.0 // we take the snapshot on the UPDATE message, since Zab V1.0 also gets the UPDATE (after the NEWLEADER) // we need to make sure that we don't take the snapshot twice. boolean isPreZAB1_0 = true; //If we are not going to take the snapshot be sure the transactions are not applied in memory // but written out to the transaction log boolean writeToTxnLog = !snapshotNeeded; TxnLogEntry logEntry; // we are now going to start getting transactions to apply followed by an UPTODATE outerLoop: while (self.isRunning()) { readPacket(qp); switch (qp.getType()) { case Leader.PROPOSAL: PacketInFlight pif = new PacketInFlight(); logEntry = SerializeUtils.deserializeTxn(qp.getData()); pif.hdr = logEntry.getHeader(); pif.rec = logEntry.getTxn(); pif.digest = logEntry.getDigest(); lastQueued = enforceContinuousProposal(lastQueued, pif); if (pif.hdr.getType() == OpCode.reconfig) { SetDataTxn setDataTxn = (SetDataTxn) pif.rec; QuorumVerifier qv = self.configFromString(new String(setDataTxn.getData(), UTF_8)); self.setLastSeenQuorumVerifier(qv, true); } packetsNotLogged.add(pif); break; case Leader.COMMIT: case Leader.COMMITANDACTIVATE: pif = packetsNotLogged.peekFirst(); if (pif.hdr.getZxid() == qp.getZxid() && qp.getType() == Leader.COMMITANDACTIVATE) { QuorumVerifier qv = self.configFromString(new String(((SetDataTxn) pif.rec).getData(), UTF_8)); boolean majorChange = self.processReconfig( qv, ByteBuffer.wrap(qp.getData()).getLong(), qp.getZxid(), true); if (majorChange) { throw new Exception("changes proposed in reconfig"); } } if (!writeToTxnLog) { if (pif.hdr.getZxid() != qp.getZxid()) { LOG.warn( "Committing 0x{}, but next proposal is 0x{}", Long.toHexString(qp.getZxid()), Long.toHexString(pif.hdr.getZxid())); } else { zk.processTxn(pif.toRequest()); packetsNotLogged.remove(); } } else { packetsCommitted.add(qp.getZxid()); } break; case Leader.INFORM: case Leader.INFORMANDACTIVATE: PacketInFlight packet = new PacketInFlight(); if (qp.getType() == Leader.INFORMANDACTIVATE) { ByteBuffer buffer = ByteBuffer.wrap(qp.getData()); long suggestedLeaderId = buffer.getLong(); byte[] remainingdata = new byte[buffer.remaining()]; buffer.get(remainingdata); logEntry = SerializeUtils.deserializeTxn(remainingdata); packet.hdr = logEntry.getHeader(); packet.rec = logEntry.getTxn(); packet.digest = logEntry.getDigest(); QuorumVerifier qv = self.configFromString(new String(((SetDataTxn) packet.rec).getData(), UTF_8)); boolean majorChange = self.processReconfig(qv, suggestedLeaderId, qp.getZxid(), true); if (majorChange) { throw new Exception("changes proposed in reconfig"); } } else { logEntry = SerializeUtils.deserializeTxn(qp.getData()); packet.rec = logEntry.getTxn(); packet.hdr = logEntry.getHeader(); packet.digest = logEntry.getDigest(); lastQueued = enforceContinuousProposal(lastQueued, packet); } if (!writeToTxnLog) { // Apply to db directly if we haven't taken the snapshot zk.processTxn(packet.toRequest()); } else { packetsNotLogged.add(packet); packetsCommitted.add(qp.getZxid()); } break; case Leader.UPTODATE: LOG.info("Learner received UPTODATE message"); if (newLeaderQV != null) { boolean majorChange = self.processReconfig(newLeaderQV, null, null, true); if (majorChange) { throw new Exception("changes proposed in reconfig"); } } if (isPreZAB1_0) { zk.takeSnapshot(syncSnapshot); self.setCurrentEpoch(newEpoch); } self.setZooKeeperServer(zk); self.adminServer.setZooKeeperServer(zk); break outerLoop; case Leader.NEWLEADER: // Getting NEWLEADER here instead of in discovery // means this is Zab 1.0 LOG.info("Learner received NEWLEADER message"); if (qp.getData() != null && qp.getData().length > 1) { try { QuorumVerifier qv = self.configFromString(new String(qp.getData(), UTF_8)); self.setLastSeenQuorumVerifier(qv, true); newLeaderQV = qv; } catch (Exception e) { e.printStackTrace(); } } if (snapshotNeeded) { zk.takeSnapshot(syncSnapshot); } writeToTxnLog = true; //Anything after this needs to go to the transaction log, not applied directly in memory isPreZAB1_0 = false; // ZOOKEEPER-3911: make sure sync the uncommitted logs before commit them (ACK NEWLEADER). if (zk instanceof FollowerZooKeeperServer && !packetsCommitted.isEmpty()) { long startTime = Time.currentElapsedTime(); FollowerZooKeeperServer fzk = (FollowerZooKeeperServer) zk; /* * @see https://github.com/apache/zookeeper/pull/1848 * Persist and process the committed txns in "packetsNotLogged" * according to "packetsCommitted", which have been committed by * the leader. For these committed proposals, there is no need to * reply ack. * * @see https://issues.apache.org/jira/browse/ZOOKEEPER-4394 * Keep the outstanding proposals in "packetsNotLogged" to avoid * NullPointerException when the follower receives COMMIT packet(s) * right after replying NEWLEADER ack. */ while (!packetsCommitted.isEmpty()) { long zxid = packetsCommitted.removeFirst(); pif = packetsNotLogged.peekFirst(); if (pif == null) { LOG.warn("Committing 0x{}, but got no proposal", Long.toHexString(zxid)); continue; } else if (pif.hdr.getZxid() != zxid) { LOG.warn("Committing 0x{}, but next proposal is 0x{}", Long.toHexString(zxid), Long.toHexString(pif.hdr.getZxid())); continue; } packetsNotLogged.removeFirst(); Request request = pif.toRequest(); fzk.appendRequest(request); fzk.processTxn(request); } // @see https://issues.apache.org/jira/browse/ZOOKEEPER-4646 // Make sure to persist the txns to disk before replying NEWLEADER ack. fzk.getZKDatabase().commit(); LOG.info("It took {}ms to persist and commit txns in packetsCommitted. " + "{} outstanding txns left in packetsNotLogged", Time.currentElapsedTime() - startTime, packetsNotLogged.size()); } // @see https://issues.apache.org/jira/browse/ZOOKEEPER-4643 // @see https://issues.apache.org/jira/browse/ZOOKEEPER-4785 // Update current epoch after the committed txns are persisted self.setCurrentEpoch(newEpoch); LOG.info("Set the current epoch to {}", newEpoch); sock.setSoTimeout(self.tickTime * self.syncLimit); self.setSyncMode(QuorumPeer.SyncMode.NONE); // send NEWLEADER ack after the committed txns are persisted writePacket(new QuorumPacket(Leader.ACK, newLeaderZxid, null, null), true); LOG.info("Sent NEWLEADER ack to leader with zxid {}", Long.toHexString(newLeaderZxid)); break; } } } ack.setZxid(ZxidUtils.makeZxid(newEpoch, 0)); writePacket(ack, true); zk.startup(); /* * Update the election vote here to ensure that all members of the * ensemble report the same vote to new servers that start up and * send leader election notifications to the ensemble. * * @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732 */ self.updateElectionVote(newEpoch); // We need to log the stuff that came in between the snapshot and the uptodate if (zk instanceof FollowerZooKeeperServer) { FollowerZooKeeperServer fzk = (FollowerZooKeeperServer) zk; for (PacketInFlight p : packetsNotLogged) { fzk.logRequest(p.toRequest()); } LOG.info("{} txns have been logged asynchronously", packetsNotLogged.size()); for (Long zxid : packetsCommitted) { fzk.commit(zxid); } LOG.info("{} txns have been committed", packetsCommitted.size()); } else if (zk instanceof ObserverZooKeeperServer) { // Similar to follower, we need to log requests between the snapshot // and UPTODATE ObserverZooKeeperServer ozk = (ObserverZooKeeperServer) zk; for (PacketInFlight p : packetsNotLogged) { Long zxid = packetsCommitted.peekFirst(); if (p.hdr.getZxid() != zxid) { // log warning message if there is no matching commit // old leader send outstanding proposal to observer LOG.warn( "Committing 0x{}, but next proposal is 0x{}", Long.toHexString(zxid), Long.toHexString(p.hdr.getZxid())); continue; } packetsCommitted.remove(); Request request = p.toRequest(); ozk.commitRequest(request); } } else { // New server type need to handle in-flight packets throw new UnsupportedOperationException("Unknown server type"); } } protected void revalidate(QuorumPacket qp) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); DataInputStream dis = new DataInputStream(bis); long sessionId = dis.readLong(); boolean valid = dis.readBoolean(); ServerCnxn cnxn = pendingRevalidations.remove(sessionId); if (cnxn == null) { LOG.warn("Missing session 0x{} for validation", Long.toHexString(sessionId)); } else { zk.finishSessionInit(cnxn, valid); } if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "Session 0x" + Long.toHexString(sessionId) + " is valid: " + valid); } } protected void ping(QuorumPacket qp) throws IOException { // Send back the ping with our session data ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); Map touchTable = zk.getTouchSnapshot(); for (Entry entry : touchTable.entrySet()) { dos.writeLong(entry.getKey()); dos.writeInt(entry.getValue()); } QuorumPacket pingReply = new QuorumPacket(qp.getType(), qp.getZxid(), bos.toByteArray(), qp.getAuthinfo()); writePacket(pingReply, true); } /** * Shutdown the Peer */ public void shutdown() { self.setZooKeeperServer(null); self.closeAllConnections(); self.adminServer.setZooKeeperServer(null); if (sender != null) { sender.shutdown(); } closeSocket(); // shutdown previous zookeeper if (zk != null) { // If we haven't finished SNAP sync, force fully shutdown // to avoid potential inconsistency zk.shutdown(self.getSyncMode().equals(QuorumPeer.SyncMode.SNAP)); } } boolean isRunning() { return self.isRunning() && zk.isRunning(); } void closeSocket() { if (sockBeingClosed.compareAndSet(false, true)) { if (sock == null) { // Closing before establishing the connection is a noop return; } Socket socket = sock; sock = null; if (closeSocketAsync) { final Thread closingThread = new Thread(() -> closeSockSync(socket), "CloseSocketThread(sid:" + zk.getServerId()); closingThread.setDaemon(true); closingThread.start(); } else { closeSockSync(socket); } } } private static void closeSockSync(Socket socket) { try { long startTime = Time.currentElapsedTime(); socket.close(); ServerMetrics.getMetrics().SOCKET_CLOSING_TIME.add(Time.currentElapsedTime() - startTime); } catch (IOException e) { LOG.warn("Ignoring error closing connection to leader", e); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032722 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerHandler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerHand0100644 0000000 0000000 00000133012 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.net.Socket; import java.nio.ByteBuffer; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import javax.security.sasl.SaslException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.TxnLogProposalIterator; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.apache.zookeeper.server.util.ConfigUtils; import org.apache.zookeeper.server.util.MessageTracker; import org.apache.zookeeper.server.util.ZxidUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * There will be an instance of this class created by the Leader for each * learner. All communication with a learner is handled by this * class. */ public class LearnerHandler extends ZooKeeperThread { private static final Logger LOG = LoggerFactory.getLogger(LearnerHandler.class); public static final String LEADER_CLOSE_SOCKET_ASYNC = "zookeeper.leader.closeSocketAsync"; public static final boolean closeSocketAsync = Boolean .parseBoolean(ConfigUtils.getPropertyBackwardCompatibleWay(LEADER_CLOSE_SOCKET_ASYNC)); static { LOG.info("{} = {}", LEADER_CLOSE_SOCKET_ASYNC, closeSocketAsync); } protected final Socket sock; public Socket getSocket() { return sock; } AtomicBoolean sockBeingClosed = new AtomicBoolean(false); final LearnerMaster learnerMaster; /** Deadline for receiving the next ack. If we are bootstrapping then * it's based on the initLimit, if we are done bootstrapping it's based * on the syncLimit. Once the deadline is past this learner should * be considered no longer "sync'd" with the leader. */ volatile long tickOfNextAckDeadline; /** * ZooKeeper server identifier of this learner */ protected long sid = 0; long getSid() { return sid; } String getRemoteAddress() { return sock == null ? "" : sock.getRemoteSocketAddress().toString(); } protected int version = 0x1; int getVersion() { return version; } /** * The packets to be sent to the learner */ final LinkedBlockingQueue queuedPackets = new LinkedBlockingQueue<>(); private final AtomicLong queuedPacketsSize = new AtomicLong(); protected final AtomicLong packetsReceived = new AtomicLong(); protected final AtomicLong packetsSent = new AtomicLong(); protected final AtomicLong requestsReceived = new AtomicLong(); protected volatile long lastZxid = -1; public synchronized long getLastZxid() { return lastZxid; } protected final Date established = new Date(); public Date getEstablished() { return (Date) established.clone(); } /** * Marker packets would be added to quorum packet queue after every * markerPacketInterval packets. * It is ok if packetCounter overflows. */ private final int markerPacketInterval = 1000; private AtomicInteger packetCounter = new AtomicInteger(); /** * This class controls the time that the Leader has been * waiting for acknowledgement of a proposal from this Learner. * If the time is above syncLimit, the connection will be closed. * It keeps track of only one proposal at a time, when the ACK for * that proposal arrives, it switches to the last proposal received * or clears the value if there is no pending proposal. */ private class SyncLimitCheck { private boolean started = false; private long currentZxid = 0; private long currentTime = 0; private long nextZxid = 0; private long nextTime = 0; public synchronized void start() { started = true; } public synchronized void updateProposal(long zxid, long time) { if (!started) { return; } if (currentTime == 0) { currentTime = time; currentZxid = zxid; } else { nextTime = time; nextZxid = zxid; } } public synchronized void updateAck(long zxid) { if (currentZxid == zxid) { currentTime = nextTime; currentZxid = nextZxid; nextTime = 0; nextZxid = 0; } else if (nextZxid == zxid) { LOG.warn( "ACK for 0x{} received before ACK for 0x{}", Long.toHexString(zxid), Long.toHexString(currentZxid)); nextTime = 0; nextZxid = 0; } } public synchronized boolean check(long time) { if (currentTime == 0) { return true; } else { long msDelay = (time - currentTime) / 1000000; return (msDelay < learnerMaster.syncTimeout()); } } } private SyncLimitCheck syncLimitCheck = new SyncLimitCheck(); private static class MarkerQuorumPacket extends QuorumPacket { long time; MarkerQuorumPacket(long time) { this.time = time; } @Override public int hashCode() { return Objects.hash(time); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MarkerQuorumPacket that = (MarkerQuorumPacket) o; return time == that.time; } } private BinaryInputArchive ia; private BinaryOutputArchive oa; private final BufferedInputStream bufferedInput; private BufferedOutputStream bufferedOutput; protected final MessageTracker messageTracker; // for test only protected void setOutputArchive(BinaryOutputArchive oa) { this.oa = oa; } protected void setBufferedOutput(BufferedOutputStream bufferedOutput) { this.bufferedOutput = bufferedOutput; } /** * Keep track of whether we have started send packets thread */ private volatile boolean sendingThreadStarted = false; /** * For testing purpose, force learnerMaster to use snapshot to sync with followers */ public static final String FORCE_SNAP_SYNC = "zookeeper.forceSnapshotSync"; private boolean forceSnapSync = false; /** * Keep track of whether we need to queue TRUNC or DIFF into packet queue * that we are going to blast it to the learner */ private boolean needOpPacket = true; /** * Last zxid sent to the learner as part of synchronization */ private long leaderLastZxid; /** * for sync throttling */ private LearnerSyncThrottler syncThrottler = null; LearnerHandler(Socket sock, BufferedInputStream bufferedInput, LearnerMaster learnerMaster) throws IOException { super("LearnerHandler-" + sock.getRemoteSocketAddress()); this.sock = sock; this.learnerMaster = learnerMaster; this.bufferedInput = bufferedInput; if (Boolean.getBoolean(FORCE_SNAP_SYNC)) { forceSnapSync = true; LOG.info("Forcing snapshot sync is enabled"); } try { QuorumAuthServer authServer = learnerMaster.getQuorumAuthServer(); if (authServer != null) { authServer.authenticate(sock, new DataInputStream(bufferedInput)); } } catch (IOException e) { LOG.error("Server failed to authenticate quorum learner, addr: {}, closing connection", sock.getRemoteSocketAddress(), e); closeSocket(); throw new SaslException("Authentication failure: " + e.getMessage()); } this.messageTracker = new MessageTracker(MessageTracker.BUFFERED_MESSAGE_SIZE); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("LearnerHandler ").append(sock); sb.append(" tickOfNextAckDeadline:").append(tickOfNextAckDeadline()); sb.append(" synced?:").append(synced()); sb.append(" queuedPacketLength:").append(queuedPackets.size()); return sb.toString(); } /** * If this packet is queued, the sender thread will exit */ final QuorumPacket proposalOfDeath = new QuorumPacket(); private LearnerType learnerType = LearnerType.PARTICIPANT; public LearnerType getLearnerType() { return learnerType; } /** * This method will use the thread to send packets added to the * queuedPackets list * * @throws InterruptedException */ private void sendPackets() throws InterruptedException { while (true) { try { QuorumPacket p; p = queuedPackets.poll(); if (p == null) { bufferedOutput.flush(); p = queuedPackets.take(); } ServerMetrics.getMetrics().LEARNER_HANDLER_QP_SIZE.add(Long.toString(this.sid), queuedPackets.size()); if (p instanceof MarkerQuorumPacket) { MarkerQuorumPacket m = (MarkerQuorumPacket) p; ServerMetrics.getMetrics().LEARNER_HANDLER_QP_TIME .add(Long.toString(this.sid), (System.nanoTime() - m.time) / 1000000L); continue; } queuedPacketsSize.addAndGet(-packetSize(p)); if (p == proposalOfDeath) { // Packet of death! break; } if (p.getType() == Leader.PROPOSAL) { syncLimitCheck.updateProposal(p.getZxid(), System.nanoTime()); } if (LOG.isTraceEnabled()) { long traceMask = ZooTrace.SERVER_PACKET_TRACE_MASK; if (p.getType() == Leader.PING) { traceMask = ZooTrace.SERVER_PING_TRACE_MASK; } ZooTrace.logQuorumPacket(LOG, traceMask, 'o', p); } // Log the zxid of the last request, if it is a valid zxid. if (p.getZxid() > 0) { lastZxid = p.getZxid(); } oa.writeRecord(p, "packet"); packetsSent.incrementAndGet(); messageTracker.trackSent(p.getType()); } catch (IOException e) { LOG.error("Exception while sending packets in LearnerHandler", e); // this will cause everything to shutdown on // this learner handler and will help notify // the learner/observer instantaneously closeSocket(); break; } } } public static String packetToString(QuorumPacket p) { String type; String mess = null; switch (p.getType()) { case Leader.ACK: type = "ACK"; break; case Leader.COMMIT: type = "COMMIT"; break; case Leader.FOLLOWERINFO: type = "FOLLOWERINFO"; break; case Leader.NEWLEADER: type = "NEWLEADER"; break; case Leader.PING: type = "PING"; break; case Leader.PROPOSAL: type = "PROPOSAL"; break; case Leader.REQUEST: type = "REQUEST"; break; case Leader.REVALIDATE: type = "REVALIDATE"; ByteArrayInputStream bis = new ByteArrayInputStream(p.getData()); DataInputStream dis = new DataInputStream(bis); try { long id = dis.readLong(); mess = " sessionid = " + id; } catch (IOException e) { LOG.warn("Unexpected exception", e); } break; case Leader.UPTODATE: type = "UPTODATE"; break; case Leader.DIFF: type = "DIFF"; break; case Leader.TRUNC: type = "TRUNC"; break; case Leader.SNAP: type = "SNAP"; break; case Leader.ACKEPOCH: type = "ACKEPOCH"; break; case Leader.SYNC: type = "SYNC"; break; case Leader.INFORM: type = "INFORM"; break; case Leader.COMMITANDACTIVATE: type = "COMMITANDACTIVATE"; break; case Leader.INFORMANDACTIVATE: type = "INFORMANDACTIVATE"; break; default: type = "UNKNOWN" + p.getType(); } String entry = null; if (type != null) { entry = type + " " + Long.toHexString(p.getZxid()) + " " + mess; } return entry; } /** * This thread will receive packets from the peer and process them and * also listen to new connections from new peers. */ @Override public void run() { try { learnerMaster.addLearnerHandler(this); tickOfNextAckDeadline = learnerMaster.getTickOfInitialAckDeadline(); ia = BinaryInputArchive.getArchive(bufferedInput); bufferedOutput = new BufferedOutputStream(sock.getOutputStream()); oa = BinaryOutputArchive.getArchive(bufferedOutput); QuorumPacket qp = new QuorumPacket(); ia.readRecord(qp, "packet"); messageTracker.trackReceived(qp.getType()); if (qp.getType() != Leader.FOLLOWERINFO && qp.getType() != Leader.OBSERVERINFO) { LOG.error("First packet {} is not FOLLOWERINFO or OBSERVERINFO!", qp.toString()); return; } if (learnerMaster instanceof ObserverMaster && qp.getType() != Leader.OBSERVERINFO) { throw new IOException("Non observer attempting to connect to ObserverMaster. type = " + qp.getType()); } byte[] learnerInfoData = qp.getData(); if (learnerInfoData != null) { ByteBuffer bbsid = ByteBuffer.wrap(learnerInfoData); if (learnerInfoData.length >= 8) { this.sid = bbsid.getLong(); } if (learnerInfoData.length >= 12) { this.version = bbsid.getInt(); // protocolVersion } if (learnerInfoData.length >= 20) { long configVersion = bbsid.getLong(); if (configVersion > learnerMaster.getQuorumVerifierVersion()) { throw new IOException("Follower is ahead of the leader (has a later activated configuration)"); } } } else { this.sid = learnerMaster.getAndDecrementFollowerCounter(); } String followerInfo = learnerMaster.getPeerInfo(this.sid); if (followerInfo.isEmpty()) { LOG.info( "Follower sid: {} not in the current config {}", this.sid, Long.toHexString(learnerMaster.getQuorumVerifierVersion())); } else { LOG.info("Follower sid: {} : info : {}", this.sid, followerInfo); } if (qp.getType() == Leader.OBSERVERINFO) { learnerType = LearnerType.OBSERVER; } learnerMaster.registerLearnerHandlerBean(this, sock); long lastAcceptedEpoch = ZxidUtils.getEpochFromZxid(qp.getZxid()); long peerLastZxid; StateSummary ss = null; long zxid = qp.getZxid(); long newEpoch = learnerMaster.getEpochToPropose(this.getSid(), lastAcceptedEpoch); long newLeaderZxid = ZxidUtils.makeZxid(newEpoch, 0); if (this.getVersion() < 0x10000) { // we are going to have to extrapolate the epoch information long epoch = ZxidUtils.getEpochFromZxid(zxid); ss = new StateSummary(epoch, zxid); // fake the message learnerMaster.waitForEpochAck(this.getSid(), ss); } else { byte[] ver = new byte[4]; ByteBuffer.wrap(ver).putInt(0x10000); QuorumPacket newEpochPacket = new QuorumPacket(Leader.LEADERINFO, newLeaderZxid, ver, null); oa.writeRecord(newEpochPacket, "packet"); messageTracker.trackSent(Leader.LEADERINFO); bufferedOutput.flush(); QuorumPacket ackEpochPacket = new QuorumPacket(); ia.readRecord(ackEpochPacket, "packet"); messageTracker.trackReceived(ackEpochPacket.getType()); if (ackEpochPacket.getType() != Leader.ACKEPOCH) { LOG.error("{} is not ACKEPOCH", ackEpochPacket.toString()); return; } ByteBuffer bbepoch = ByteBuffer.wrap(ackEpochPacket.getData()); ss = new StateSummary(bbepoch.getInt(), ackEpochPacket.getZxid()); learnerMaster.waitForEpochAck(this.getSid(), ss); } peerLastZxid = ss.getLastZxid(); // Take any necessary action if we need to send TRUNC or DIFF // startForwarding() will be called in all cases boolean needSnap = syncFollower(peerLastZxid, learnerMaster); // syncs between followers and the leader are exempt from throttling because it // is important to keep the state of quorum servers up-to-date. The exempted syncs // are counted as concurrent syncs though boolean exemptFromThrottle = getLearnerType() != LearnerType.OBSERVER; /* if we are not truncating or sending a diff just send a snapshot */ if (needSnap) { syncThrottler = learnerMaster.getLearnerSnapSyncThrottler(); syncThrottler.beginSync(exemptFromThrottle); ServerMetrics.getMetrics().INFLIGHT_SNAP_COUNT.add(syncThrottler.getSyncInProgress()); try { long zxidToSend = learnerMaster.getZKDatabase().getDataTreeLastProcessedZxid(); oa.writeRecord(new QuorumPacket(Leader.SNAP, zxidToSend, null, null), "packet"); messageTracker.trackSent(Leader.SNAP); bufferedOutput.flush(); LOG.info( "Sending snapshot last zxid of peer is 0x{}, zxid of leader is 0x{}, " + "send zxid of db as 0x{}, {} concurrent snapshot sync, " + "snapshot sync was {} from throttle", Long.toHexString(peerLastZxid), Long.toHexString(leaderLastZxid), Long.toHexString(zxidToSend), syncThrottler.getSyncInProgress(), exemptFromThrottle ? "exempt" : "not exempt"); // Dump data to peer learnerMaster.getZKDatabase().serializeSnapshot(oa); oa.writeString("BenWasHere", "signature"); bufferedOutput.flush(); } finally { ServerMetrics.getMetrics().SNAP_COUNT.add(1); } } else { syncThrottler = learnerMaster.getLearnerDiffSyncThrottler(); syncThrottler.beginSync(exemptFromThrottle); ServerMetrics.getMetrics().INFLIGHT_DIFF_COUNT.add(syncThrottler.getSyncInProgress()); ServerMetrics.getMetrics().DIFF_COUNT.add(1); } LOG.debug("Sending NEWLEADER message to {}", sid); // the version of this quorumVerifier will be set by leader.lead() in case // the leader is just being established. waitForEpochAck makes sure that readyToStart is true if // we got here, so the version was set if (getVersion() < 0x10000) { QuorumPacket newLeaderQP = new QuorumPacket(Leader.NEWLEADER, newLeaderZxid, null, null); oa.writeRecord(newLeaderQP, "packet"); } else { QuorumPacket newLeaderQP = new QuorumPacket(Leader.NEWLEADER, newLeaderZxid, learnerMaster.getQuorumVerifierBytes(), null); queuedPackets.add(newLeaderQP); } bufferedOutput.flush(); // Start thread that blast packets in the queue to learner startSendingPackets(); /* * Have to wait for the first ACK, wait until * the learnerMaster is ready, and only then we can * start processing messages. */ qp = new QuorumPacket(); ia.readRecord(qp, "packet"); messageTracker.trackReceived(qp.getType()); if (qp.getType() != Leader.ACK) { LOG.error("Next packet was supposed to be an ACK, but received packet: {}", packetToString(qp)); return; } LOG.debug("Received NEWLEADER-ACK message from {}", sid); learnerMaster.waitForNewLeaderAck(getSid(), qp.getZxid()); syncLimitCheck.start(); // sync ends when NEWLEADER-ACK is received syncThrottler.endSync(); if (needSnap) { ServerMetrics.getMetrics().INFLIGHT_SNAP_COUNT.add(syncThrottler.getSyncInProgress()); } else { ServerMetrics.getMetrics().INFLIGHT_DIFF_COUNT.add(syncThrottler.getSyncInProgress()); } syncThrottler = null; // now that the ack has been processed expect the syncLimit sock.setSoTimeout(learnerMaster.syncTimeout()); /* * Wait until learnerMaster starts up */ learnerMaster.waitForStartup(); // Mutation packets will be queued during the serialize, // so we need to mark when the peer can actually start // using the data // LOG.debug("Sending UPTODATE message to {}", sid); queuedPackets.add(new QuorumPacket(Leader.UPTODATE, -1, null, null)); while (true) { qp = new QuorumPacket(); ia.readRecord(qp, "packet"); messageTracker.trackReceived(qp.getType()); if (LOG.isTraceEnabled()) { long traceMask = ZooTrace.SERVER_PACKET_TRACE_MASK; if (qp.getType() == Leader.PING) { traceMask = ZooTrace.SERVER_PING_TRACE_MASK; } ZooTrace.logQuorumPacket(LOG, traceMask, 'i', qp); } tickOfNextAckDeadline = learnerMaster.getTickOfNextAckDeadline(); packetsReceived.incrementAndGet(); ByteBuffer bb; long sessionId; int cxid; int type; switch (qp.getType()) { case Leader.ACK: if (this.learnerType == LearnerType.OBSERVER) { LOG.debug("Received ACK from Observer {}", this.sid); } syncLimitCheck.updateAck(qp.getZxid()); learnerMaster.processAck(this.sid, qp.getZxid(), sock.getLocalSocketAddress()); break; case Leader.PING: // Process the touches ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); DataInputStream dis = new DataInputStream(bis); while (dis.available() > 0) { long sess = dis.readLong(); int to = dis.readInt(); learnerMaster.touch(sess, to); } break; case Leader.REVALIDATE: ServerMetrics.getMetrics().REVALIDATE_COUNT.add(1); learnerMaster.revalidateSession(qp, this); break; case Leader.REQUEST: bb = ByteBuffer.wrap(qp.getData()); sessionId = bb.getLong(); cxid = bb.getInt(); type = bb.getInt(); bb = bb.slice(); Request si; if (type == OpCode.sync) { si = new LearnerSyncRequest(this, sessionId, cxid, type, RequestRecord.fromBytes(bb), qp.getAuthinfo()); } else { si = new Request(null, sessionId, cxid, type, RequestRecord.fromBytes(bb), qp.getAuthinfo()); } si.setOwner(this); learnerMaster.submitLearnerRequest(si); requestsReceived.incrementAndGet(); break; default: LOG.warn("unexpected quorum packet, type: {}", packetToString(qp)); break; } } } catch (IOException e) { LOG.error("Unexpected exception in LearnerHandler: ", e); closeSocket(); } catch (InterruptedException e) { LOG.error("Unexpected exception in LearnerHandler.", e); } catch (SyncThrottleException e) { LOG.error("too many concurrent sync.", e); syncThrottler = null; } catch (Exception e) { LOG.error("Unexpected exception in LearnerHandler.", e); throw e; } finally { if (syncThrottler != null) { syncThrottler.endSync(); syncThrottler = null; } String remoteAddr = getRemoteAddress(); LOG.warn("******* GOODBYE {} ********", remoteAddr); messageTracker.dumpToLog(remoteAddr); shutdown(); } } /** * Start thread that will forward any packet in the queue to the follower */ protected void startSendingPackets() { if (!sendingThreadStarted) { // Start sending packets new Thread() { public void run() { Thread.currentThread().setName("Sender-" + sock.getRemoteSocketAddress()); try { sendPackets(); } catch (InterruptedException e) { LOG.warn("Unexpected interruption", e); } } }.start(); sendingThreadStarted = true; } else { LOG.error("Attempting to start sending thread after it already started"); } } /** * Tests need not send marker packets as they are only needed to * log quorum packet delays */ protected boolean shouldSendMarkerPacketForLogging() { return true; } /** * Determine if we need to sync with follower using DIFF/TRUNC/SNAP * and setup follower to receive packets from commit processor * * @param peerLastZxid * @param learnerMaster * @return true if snapshot transfer is needed. */ boolean syncFollower(long peerLastZxid, LearnerMaster learnerMaster) { /* * When leader election is completed, the leader will set its * lastProcessedZxid to be (epoch < 32). There will be no txn associated * with this zxid. * * The learner will set its lastProcessedZxid to the same value if * it get DIFF or SNAP from the learnerMaster. If the same learner come * back to sync with learnerMaster using this zxid, we will never find this * zxid in our history. In this case, we will ignore TRUNC logic and * always send DIFF if we have old enough history */ boolean isPeerNewEpochZxid = (peerLastZxid & 0xffffffffL) == 0; // Keep track of the latest zxid which already queued long currentZxid = peerLastZxid; boolean needSnap = true; ZKDatabase db = learnerMaster.getZKDatabase(); boolean txnLogSyncEnabled = db.isTxnLogSyncEnabled(); ReentrantReadWriteLock lock = db.getLogLock(); ReadLock rl = lock.readLock(); try { rl.lock(); long maxCommittedLog = db.getmaxCommittedLog(); long minCommittedLog = db.getminCommittedLog(); long lastProcessedZxid = db.getDataTreeLastProcessedZxid(); LOG.info("Synchronizing with Learner sid: {} maxCommittedLog=0x{}" + " minCommittedLog=0x{} lastProcessedZxid=0x{}" + " peerLastZxid=0x{}", getSid(), Long.toHexString(maxCommittedLog), Long.toHexString(minCommittedLog), Long.toHexString(lastProcessedZxid), Long.toHexString(peerLastZxid)); if (db.getCommittedLog().isEmpty()) { /* * It is possible that committedLog is empty. In that case * setting these value to the latest txn in learnerMaster db * will reduce the case that we need to handle * * Here is how each case handle by the if block below * 1. lastProcessZxid == peerZxid -> Handle by (2) * 2. lastProcessZxid < peerZxid -> Handle by (3) * 3. lastProcessZxid > peerZxid -> Handle by (5) */ minCommittedLog = lastProcessedZxid; maxCommittedLog = lastProcessedZxid; } /* * Here are the cases that we want to handle * * 1. Force sending snapshot (for testing purpose) * 2. Peer and learnerMaster is already sync, send empty diff * 3. Follower has txn that we haven't seen. This may be old leader * so we need to send TRUNC. However, if peer has newEpochZxid, * we cannot send TRUNC since the follower has no txnlog * 4. Follower is within committedLog range or already in-sync. * We may need to send DIFF or TRUNC depending on follower's zxid * We always send empty DIFF if follower is already in-sync * 5. Follower missed the committedLog. We will try to use on-disk * txnlog + committedLog to sync with follower. If that fail, * we will send snapshot */ if (forceSnapSync) { // Force learnerMaster to use snapshot to sync with follower LOG.warn("Forcing snapshot sync - should not see this in production"); } else if (lastProcessedZxid == peerLastZxid) { // Follower is already sync with us, send empty diff LOG.info( "Sending DIFF zxid=0x{} for peer sid: {}", Long.toHexString(peerLastZxid), getSid()); queueOpPacket(Leader.DIFF, peerLastZxid); needOpPacket = false; needSnap = false; } else if (peerLastZxid > maxCommittedLog && !isPeerNewEpochZxid) { // Newer than committedLog, send trunc and done LOG.debug( "Sending TRUNC to follower zxidToSend=0x{} for peer sid:{}", Long.toHexString(maxCommittedLog), getSid()); queueOpPacket(Leader.TRUNC, maxCommittedLog); currentZxid = maxCommittedLog; needOpPacket = false; needSnap = false; } else if ((maxCommittedLog >= peerLastZxid) && (minCommittedLog <= peerLastZxid)) { // Follower is within commitLog range LOG.info("Using committedLog for peer sid: {}", getSid()); Iterator itr = db.getCommittedLog().iterator(); currentZxid = queueCommittedProposals(itr, peerLastZxid, null, maxCommittedLog); needSnap = false; } else if (peerLastZxid < minCommittedLog && txnLogSyncEnabled) { // Use txnlog and committedLog to sync // Calculate sizeLimit that we allow to retrieve txnlog from disk long sizeLimit = db.calculateTxnLogSizeLimit(); // This method can return empty iterator if the requested zxid // is older than on-disk txnlog Iterator txnLogItr = db.getProposalsFromTxnLog(peerLastZxid, sizeLimit); if (txnLogItr.hasNext()) { LOG.info("Use txnlog and committedLog for peer sid: {}", getSid()); currentZxid = queueCommittedProposals(txnLogItr, peerLastZxid, minCommittedLog, maxCommittedLog); if (currentZxid < minCommittedLog) { LOG.info( "Detected gap between end of txnlog: 0x{} and start of committedLog: 0x{}", Long.toHexString(currentZxid), Long.toHexString(minCommittedLog)); currentZxid = peerLastZxid; // Clear out currently queued requests and revert // to sending a snapshot. queuedPackets.clear(); needOpPacket = true; } else { LOG.debug("Queueing committedLog 0x{}", Long.toHexString(currentZxid)); Iterator committedLogItr = db.getCommittedLog().iterator(); currentZxid = queueCommittedProposals(committedLogItr, currentZxid, null, maxCommittedLog); needSnap = false; } } // closing the resources if (txnLogItr instanceof TxnLogProposalIterator) { TxnLogProposalIterator txnProposalItr = (TxnLogProposalIterator) txnLogItr; txnProposalItr.close(); } } else { LOG.warn( "Unhandled scenario for peer sid: {} maxCommittedLog=0x{}" + " minCommittedLog=0x{} lastProcessedZxid=0x{}" + " peerLastZxid=0x{} txnLogSyncEnabled={}", getSid(), Long.toHexString(maxCommittedLog), Long.toHexString(minCommittedLog), Long.toHexString(lastProcessedZxid), Long.toHexString(peerLastZxid), txnLogSyncEnabled); } if (needSnap) { currentZxid = db.getDataTreeLastProcessedZxid(); } LOG.debug("Start forwarding 0x{} for peer sid: {}", Long.toHexString(currentZxid), getSid()); leaderLastZxid = learnerMaster.startForwarding(this, currentZxid); } finally { rl.unlock(); } if (needOpPacket && !needSnap) { // This should never happen, but we should fall back to sending // snapshot just in case. LOG.error("Unhandled scenario for peer sid: {} fall back to use snapshot", getSid()); needSnap = true; } return needSnap; } /** * Queue committed proposals into packet queue. The range of packets which * is going to be queued are (peerLaxtZxid, maxZxid] * * @param itr iterator point to the proposals * @param peerLastZxid last zxid seen by the follower * @param maxZxid max zxid of the proposal to queue, null if no limit * @param lastCommittedZxid when sending diff, we need to send lastCommittedZxid * on the leader to follow Zab 1.0 protocol. * @return last zxid of the queued proposal */ protected long queueCommittedProposals(Iterator itr, long peerLastZxid, Long maxZxid, Long lastCommittedZxid) { boolean isPeerNewEpochZxid = (peerLastZxid & 0xffffffffL) == 0; long queuedZxid = peerLastZxid; // as we look through proposals, this variable keeps track of previous // proposal Id. long prevProposalZxid = -1; while (itr.hasNext()) { Proposal propose = itr.next(); long packetZxid = propose.getZxid(); // abort if we hit the limit if ((maxZxid != null) && (packetZxid > maxZxid)) { break; } // skip the proposals the peer already has if (packetZxid < peerLastZxid) { prevProposalZxid = packetZxid; continue; } // If we are sending the first packet, figure out whether to trunc // or diff if (needOpPacket) { // Send diff when we see the follower's zxid in our history if (packetZxid == peerLastZxid) { LOG.info( "Sending DIFF zxid=0x{} for peer sid: {}", Long.toHexString(lastCommittedZxid), getSid()); queueOpPacket(Leader.DIFF, lastCommittedZxid); needOpPacket = false; continue; } if (isPeerNewEpochZxid) { // Send diff and fall through if zxid is of a new-epoch LOG.info( "Sending DIFF zxid=0x{} for peer sid: {}", Long.toHexString(lastCommittedZxid), getSid()); queueOpPacket(Leader.DIFF, lastCommittedZxid); needOpPacket = false; } else if (packetZxid > peerLastZxid) { // Peer have some proposals that the learnerMaster hasn't seen yet // it may used to be a leader if (ZxidUtils.getEpochFromZxid(packetZxid) != ZxidUtils.getEpochFromZxid(peerLastZxid)) { // We cannot send TRUNC that cross epoch boundary. // The learner will crash if it is asked to do so. // We will send snapshot this those cases. LOG.warn("Cannot send TRUNC to peer sid: " + getSid() + " peer zxid is from different epoch"); return queuedZxid; } LOG.info( "Sending TRUNC zxid=0x{} for peer sid: {}", Long.toHexString(prevProposalZxid), getSid()); queueOpPacket(Leader.TRUNC, prevProposalZxid); needOpPacket = false; } } if (packetZxid <= queuedZxid) { // We can get here, if we don't have op packet to queue // or there is a duplicate txn in a given iterator continue; } // Since this is already a committed proposal, we need to follow // it by a commit packet queuePacket(propose.getQuorumPacket()); queueOpPacket(Leader.COMMIT, packetZxid); queuedZxid = packetZxid; } if (needOpPacket && isPeerNewEpochZxid) { // We will send DIFF for this kind of zxid in any case. This if-block // is the catch when our history older than learner and there is // no new txn since then. So we need an empty diff LOG.info( "Sending DIFF zxid=0x{} for peer sid: {}", Long.toHexString(lastCommittedZxid), getSid()); queueOpPacket(Leader.DIFF, lastCommittedZxid); needOpPacket = false; } return queuedZxid; } public void shutdown() { // Send the packet of death try { queuedPackets.clear(); queuedPackets.put(proposalOfDeath); } catch (InterruptedException e) { LOG.warn("Ignoring unexpected exception", e); } closeSocket(); this.interrupt(); learnerMaster.removeLearnerHandler(this); learnerMaster.unregisterLearnerHandlerBean(this); } public long tickOfNextAckDeadline() { return tickOfNextAckDeadline; } /** * ping calls from the learnerMaster to the peers */ public void ping() { // If learner hasn't sync properly yet, don't send ping packet // otherwise, the learner will crash if (!sendingThreadStarted) { return; } long id; if (syncLimitCheck.check(System.nanoTime())) { id = learnerMaster.getLastProposed(); QuorumPacket ping = new QuorumPacket(Leader.PING, id, null, null); queuePacket(ping); } else { LOG.warn("Closing connection to peer due to transaction timeout."); shutdown(); } } /** * Queue leader packet of a given type * @param type * @param zxid */ private void queueOpPacket(int type, long zxid) { QuorumPacket packet = new QuorumPacket(type, zxid, null, null); queuePacket(packet); } void queuePacket(QuorumPacket p) { queuedPackets.add(p); // Add a MarkerQuorumPacket at regular intervals. if (shouldSendMarkerPacketForLogging() && packetCounter.getAndIncrement() % markerPacketInterval == 0) { queuedPackets.add(new MarkerQuorumPacket(System.nanoTime())); } queuedPacketsSize.addAndGet(packetSize(p)); } static long packetSize(QuorumPacket p) { /* Approximate base size of QuorumPacket: int + long + byte[] + List */ long size = 4 + 8 + 8 + 8; byte[] data = p.getData(); if (data != null) { size += data.length; } return size; } public boolean synced() { return isAlive() && learnerMaster.getCurrentTick() <= tickOfNextAckDeadline; } public synchronized Map getLearnerHandlerInfo() { Map info = new LinkedHashMap<>(9); info.put("remote_socket_address", getRemoteAddress()); info.put("sid", getSid()); info.put("established", getEstablished()); info.put("queued_packets", queuedPackets.size()); info.put("queued_packets_size", queuedPacketsSize.get()); info.put("packets_received", packetsReceived.longValue()); info.put("packets_sent", packetsSent.longValue()); info.put("requests", requestsReceived.longValue()); info.put("last_zxid", getLastZxid()); return info; } public synchronized void resetObserverConnectionStats() { packetsReceived.set(0); packetsSent.set(0); requestsReceived.set(0); lastZxid = -1; } /** * For testing, return packet queue */ public Queue getQueuedPackets() { return queuedPackets; } /** * For testing, we need to reset this value */ public void setFirstPacket(boolean value) { needOpPacket = value; } void closeSocket() { if (sock != null && !sock.isClosed() && sockBeingClosed.compareAndSet(false, true)) { if (closeSocketAsync) { LOG.info("Asynchronously closing socket to learner {}.", getSid()); closeSockAsync(); } else { LOG.info("Synchronously closing socket to learner {}.", getSid()); closeSockSync(); } } } void closeSockAsync() { final Thread closingThread = new Thread(() -> closeSockSync(), "CloseSocketThread(sid:" + this.sid); closingThread.setDaemon(true); closingThread.start(); } void closeSockSync() { try { if (sock != null) { long startTime = Time.currentElapsedTime(); sock.close(); ServerMetrics.getMetrics().SOCKET_CLOSING_TIME.add(Time.currentElapsedTime() - startTime); } } catch (IOException e) { LOG.warn("Ignoring error closing connection to learner {}", getSid(), e); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032717 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerHandlerBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerHand0100644 0000000 0000000 00000004755 15051152474 034251 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.net.InetSocketAddress; import java.net.Socket; import javax.management.ObjectName; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LearnerHandlerBean implements LearnerHandlerMXBean, ZKMBeanInfo { private static final Logger LOG = LoggerFactory.getLogger(LearnerHandlerBean.class); private final LearnerHandler learnerHandler; private final String remoteAddr; public LearnerHandlerBean(final LearnerHandler learnerHandler, final Socket socket) { this.learnerHandler = learnerHandler; InetSocketAddress sockAddr = (InetSocketAddress) socket.getRemoteSocketAddress(); if (sockAddr == null) { this.remoteAddr = "Unknown"; } else { this.remoteAddr = sockAddr.getAddress().getHostAddress() + ":" + sockAddr.getPort(); } } @Override public String getName() { return MBeanRegistry.getInstance() .makeFullPath( "Learner_Connections", ObjectName.quote(remoteAddr), String.format("\"id:%d\"", learnerHandler.getSid())); } @Override public boolean isHidden() { return false; } @Override public void terminateConnection() { LOG.info("terminating learner handler connection on demand {}", toString()); learnerHandler.shutdown(); } @Override public String toString() { return "LearnerHandlerBean{remoteIP=" + remoteAddr + ",ServerId=" + learnerHandler.getSid() + "}"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032721 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerHandlerMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerHand0100644 0000000 0000000 00000002211 15051152474 034232 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * This MBean represents a server connection for a learner. */ public interface LearnerHandlerMXBean { /** * Terminate the connection. The learner will attempt to reconnect to * the leader or to the next ObserverMaster if that feature is enabled */ void terminateConnection(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000165 15051152474 032721 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerMaster.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerMast0100644 0000000 0000000 00000017707 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.net.Socket; import java.net.SocketAddress; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * interface for keeping Observers in sync */ public abstract class LearnerMaster { private static final Logger LOG = LoggerFactory.getLogger(LearnerMaster.class); // Throttle when there are too many concurrent snapshots being sent to observers private static final String MAX_CONCURRENT_SNAPSYNCS = "zookeeper.leader.maxConcurrentSnapSyncs"; private static final int DEFAULT_CONCURRENT_SNAPSYNCS; // Throttle when there are too many concurrent diff syncs being sent to observers private static final String MAX_CONCURRENT_DIFF_SYNCS = "zookeeper.leader.maxConcurrentDiffSyncs"; private static final int DEFAULT_CONCURRENT_DIFF_SYNCS; static { DEFAULT_CONCURRENT_SNAPSYNCS = Integer.getInteger(MAX_CONCURRENT_SNAPSYNCS, 10); LOG.info("{} = {}", MAX_CONCURRENT_SNAPSYNCS, DEFAULT_CONCURRENT_SNAPSYNCS); DEFAULT_CONCURRENT_DIFF_SYNCS = Integer.getInteger(MAX_CONCURRENT_DIFF_SYNCS, 100); LOG.info("{} = {}", MAX_CONCURRENT_DIFF_SYNCS, DEFAULT_CONCURRENT_DIFF_SYNCS); } private volatile int maxConcurrentSnapSyncs = DEFAULT_CONCURRENT_SNAPSYNCS; private volatile int maxConcurrentDiffSyncs = DEFAULT_CONCURRENT_DIFF_SYNCS; private final LearnerSyncThrottler learnerSnapSyncThrottler = new LearnerSyncThrottler(maxConcurrentSnapSyncs, LearnerSyncThrottler.SyncType.SNAP); private final LearnerSyncThrottler learnerDiffSyncThrottler = new LearnerSyncThrottler(maxConcurrentDiffSyncs, LearnerSyncThrottler.SyncType.DIFF); public int getMaxConcurrentSnapSyncs() { return maxConcurrentSnapSyncs; } public void setMaxConcurrentSnapSyncs(int maxConcurrentSnapSyncs) { LOG.info("Set maxConcurrentSnapSyncs to {}", maxConcurrentSnapSyncs); this.maxConcurrentSnapSyncs = maxConcurrentSnapSyncs; learnerSnapSyncThrottler.setMaxConcurrentSyncs(maxConcurrentSnapSyncs); } public int getMaxConcurrentDiffSyncs() { return maxConcurrentDiffSyncs; } public void setMaxConcurrentDiffSyncs(int maxConcurrentDiffSyncs) { LOG.info("Set maxConcurrentDiffSyncs to {}", maxConcurrentDiffSyncs); this.maxConcurrentDiffSyncs = maxConcurrentDiffSyncs; learnerDiffSyncThrottler.setMaxConcurrentSyncs(maxConcurrentDiffSyncs); } /** * snap sync throttler * @return snapshot throttler */ public LearnerSyncThrottler getLearnerSnapSyncThrottler() { return learnerSnapSyncThrottler; } /** * diff sync throttler * @return diff throttler */ public LearnerSyncThrottler getLearnerDiffSyncThrottler() { return learnerDiffSyncThrottler; } /** * start tracking a learner handler * @param learnerHandler to track */ abstract void addLearnerHandler(LearnerHandler learnerHandler); /** * stop tracking a learner handler * @param learnerHandler to drop */ abstract void removeLearnerHandler(LearnerHandler learnerHandler); /** * wait for the leader of the new epoch to be confirmed by followers * @param sid learner id * @param ss * @throws IOException * @throws InterruptedException */ abstract void waitForEpochAck(long sid, StateSummary ss) throws IOException, InterruptedException; /** * wait for server to start * @throws InterruptedException */ abstract void waitForStartup() throws InterruptedException; /** * get the first zxid of the next epoch * @param sid learner id * @param lastAcceptedEpoch * @return the first zxid of the next epoch * @throws InterruptedException * @throws IOException */ abstract long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException; /** * ZKDatabase * @return ZKDatabase */ abstract ZKDatabase getZKDatabase(); /** * wait for new leader to settle * @param sid id of learner * @param zxid zxid at learner * @throws InterruptedException */ abstract void waitForNewLeaderAck(long sid, long zxid) throws InterruptedException; /** * last proposed zxid * @return last proposed zxid */ abstract long getLastProposed(); /** * the current tick * @return the current tick */ abstract int getCurrentTick(); /** * time allowed for sync response * @return time allowed for sync response */ abstract int syncTimeout(); /** * deadline tick marking observer sync (initial) * @return deadline tick marking observer sync (initial) */ abstract int getTickOfNextAckDeadline(); /** * next deadline tick marking observer sync (steady state) * @return next deadline tick marking observer sync (steady state) */ abstract int getTickOfInitialAckDeadline(); /** * decrement follower count * @return previous follower count */ abstract long getAndDecrementFollowerCounter(); /** * handle ack packet * @param sid leader id * @param zxid packet zxid * @param localSocketAddress forwarder's address */ abstract void processAck(long sid, long zxid, SocketAddress localSocketAddress); /** * mark session as alive * @param sess session id * @param to timeout */ abstract void touch(long sess, int to); /** * handle revalidate packet * @param qp session packet * @param learnerHandler learner * @throws IOException */ abstract void revalidateSession(QuorumPacket qp, LearnerHandler learnerHandler) throws IOException; /** * proxy request from learner to server * @param si request */ abstract void submitLearnerRequest(Request si); /** * begin forwarding packets to learner handler * @param learnerHandler learner * @param lastSeenZxid zxid of learner * @return last zxid forwarded */ abstract long startForwarding(LearnerHandler learnerHandler, long lastSeenZxid); /** * version of current quorum verifier * @return version of current quorum verifier */ abstract long getQuorumVerifierVersion(); /** * * @param sid server id * @return server information in the view */ abstract String getPeerInfo(long sid); /** * identifier of current quorum verifier for new leader * @return identifier of current quorum verifier for new leader */ abstract byte[] getQuorumVerifierBytes(); abstract QuorumAuthServer getQuorumAuthServer(); /** * registers the handler's bean * @param learnerHandler handler * @param socket connection to learner */ abstract void registerLearnerHandlerBean(LearnerHandler learnerHandler, Socket socket); /** * unregisters the handler's bean * @param learnerHandler handler */ abstract void unregisterLearnerHandlerBean(LearnerHandler learnerHandler); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000165 15051152474 032721 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSender.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSend0100644 0000000 0000000 00000005331 15051152474 034257 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LearnerSender extends ZooKeeperCriticalThread { private static final Logger LOG = LoggerFactory.getLogger(LearnerSender.class); private final LinkedBlockingQueue queuedPackets = new LinkedBlockingQueue<>(); private final QuorumPacket proposalOfDeath = new QuorumPacket(); Learner learner; public LearnerSender(Learner learner) { super("LearnerSender:" + learner.zk.getServerId(), learner.zk.getZooKeeperServerListener()); this.learner = learner; } @Override public void run() { while (true) { try { QuorumPacket p = queuedPackets.poll(); if (p == null) { learner.bufferedOutput.flush(); p = queuedPackets.take(); } if (p == proposalOfDeath) { // Packet of death! break; } learner.messageTracker.trackSent(p.getType()); learner.leaderOs.writeRecord(p, "packet"); } catch (IOException e) { handleException(this.getName(), e); break; } catch (InterruptedException e) { handleException(this.getName(), e); break; } } LOG.info("LearnerSender exited"); } public void queuePacket(QuorumPacket pp) throws IOException { if (pp == null) { learner.bufferedOutput.flush(); } else { queuedPackets.add(pp); } } public void shutdown() { LOG.info("Shutting down LearnerSender"); queuedPackets.clear(); queuedPackets.add(proposalOfDeath); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032722 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSessionTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSess0100644 0000000 0000000 00000022173 15051152474 034306 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.KeeperException.SessionMovedException; import org.apache.zookeeper.KeeperException.UnknownSessionException; import org.apache.zookeeper.server.SessionTrackerImpl; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The learner session tracker is used by learners (followers and observers) to * track zookeeper sessions which may or may not be echoed to the leader. When * a new session is created it is saved locally in a wrapped * LocalSessionTracker. It can subsequently be upgraded to a global session * as required. If an upgrade is requested the session is removed from local * collections while keeping the same session ID. It is up to the caller to * queue a session creation request for the leader. * A secondary function of the learner session tracker is to remember sessions * which have been touched in this service. This information is passed along * to the leader with a ping. */ public class LearnerSessionTracker extends UpgradeableSessionTracker { private static final Logger LOG = LoggerFactory.getLogger(LearnerSessionTracker.class); private final SessionExpirer expirer; // Touch table for the global sessions private final AtomicReference> touchTable = new AtomicReference<>(); private final long serverId; private final AtomicLong nextSessionId = new AtomicLong(); private final ConcurrentMap globalSessionsWithTimeouts; public LearnerSessionTracker(SessionExpirer expirer, ConcurrentMap sessionsWithTimeouts, int tickTime, long id, boolean localSessionsEnabled, ZooKeeperServerListener listener) { this.expirer = expirer; this.touchTable.set(new ConcurrentHashMap<>()); this.globalSessionsWithTimeouts = sessionsWithTimeouts; this.serverId = id; nextSessionId.set(SessionTrackerImpl.initializeNextSessionId(serverId)); this.localSessionsEnabled = localSessionsEnabled; if (this.localSessionsEnabled) { createLocalSessionTracker(expirer, tickTime, id, listener); } } public void removeSession(long sessionId) { if (localSessionTracker != null) { localSessionTracker.removeSession(sessionId); } globalSessionsWithTimeouts.remove(sessionId); touchTable.get().remove(sessionId); } public void start() { if (localSessionTracker != null) { localSessionTracker.start(); } } public void shutdown() { if (localSessionTracker != null) { localSessionTracker.shutdown(); } } public boolean isGlobalSession(long sessionId) { return globalSessionsWithTimeouts.containsKey(sessionId); } public boolean trackSession(long sessionId, int sessionTimeout) { // Learner doesn't track global session, do nothing here return false; } /** * Synchronized on this to avoid race condition of adding a local session * after committed global session, which may cause the same session being * tracked on this server and leader. */ public synchronized boolean commitSession(long sessionId, int sessionTimeout) { boolean added = globalSessionsWithTimeouts.put(sessionId, sessionTimeout) == null; if (added) { // Only do extra logging so we know what kind of session this is // if we're supporting both kinds of sessions LOG.info("Committing global session 0x{}", Long.toHexString(sessionId)); } // If the session moved before the session upgrade finished, it's // possible that the session will be added to the local session // again. Need to double check and remove it from local session // tracker when the global session is quorum committed, otherwise the // local session might be tracked both locally and on leader. // // This cannot totally avoid the local session being upgraded again // because there is still race condition between create another upgrade // request and process the createSession commit, and there is no way // to know there is a on flying createSession request because it might // be upgraded by other server which owns the session before move. if (localSessionsEnabled) { removeLocalSession(sessionId); finishedUpgrading(sessionId); } touchTable.get().put(sessionId, sessionTimeout); return added; } public boolean touchSession(long sessionId, int sessionTimeout) { if (localSessionsEnabled) { if (localSessionTracker.touchSession(sessionId, sessionTimeout)) { return true; } if (!isGlobalSession(sessionId) && !isUpgradingSession(sessionId)) { return false; } } touchTable.get().put(sessionId, sessionTimeout); return true; } public Map snapshot() { return touchTable.getAndSet(new ConcurrentHashMap<>()); } public long createSession(int sessionTimeout) { if (localSessionsEnabled) { return localSessionTracker.createSession(sessionTimeout); } return nextSessionId.getAndIncrement(); } public void checkSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException { if (localSessionTracker != null) { try { localSessionTracker.checkSession(sessionId, owner); return; } catch (UnknownSessionException e) { // Check whether it's a global session. We can ignore those // because they are handled at the leader, but if not, rethrow. // We check local session status first to avoid race condition // with session upgrading. if (!isGlobalSession(sessionId)) { throw new SessionExpiredException(); } } } } public void setOwner(long sessionId, Object owner) throws SessionExpiredException { if (localSessionTracker != null) { try { localSessionTracker.setOwner(sessionId, owner); return; } catch (SessionExpiredException e) { // Check whether it's a global session. We can ignore those // because they are handled at the leader, but if not, rethrow. // We check local session status first to avoid race condition // with session upgrading. if (!isGlobalSession(sessionId)) { throw e; } } } } public void dumpSessions(PrintWriter pwriter) { if (localSessionTracker != null) { pwriter.print("Local "); localSessionTracker.dumpSessions(pwriter); } pwriter.print("Global Sessions("); pwriter.print(globalSessionsWithTimeouts.size()); pwriter.println("):"); SortedSet sessionIds = new TreeSet<>(globalSessionsWithTimeouts.keySet()); for (long sessionId : sessionIds) { pwriter.print("0x"); pwriter.print(Long.toHexString(sessionId)); pwriter.print("\t"); pwriter.print(globalSessionsWithTimeouts.get(sessionId)); pwriter.println("ms"); } } public void setSessionClosing(long sessionId) { // Global sessions handled on the leader; this call is a no-op if // not tracked as a local session so safe to call in both cases. if (localSessionTracker != null) { localSessionTracker.setSessionClosing(sessionId); } } @Override public Map> getSessionExpiryMap() { return new HashMap<>(); } public Set globalSessions() { return globalSessionsWithTimeouts.keySet(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032717 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSyncRequest.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSync0100644 0000000 0000000 00000002437 15051152474 034306 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.List; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestRecord; public class LearnerSyncRequest extends Request { LearnerHandler fh; public LearnerSyncRequest( LearnerHandler fh, long sessionId, int xid, int type, RequestRecord request, List authInfo) { super(null, sessionId, xid, type, request, authInfo); this.fh = fh; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032721 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSyncThrottler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerSync0100644 0000000 0000000 00000010210 15051152474 034272 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class to limit the number of concurrent syncs from a leader to * observers and followers or from a follower to observers. {@link LearnerHandler} * objects should call {@link #beginSync(boolean)} before sending a sync and * {@link #endSync()} after finishing, successfully or not. * */ public class LearnerSyncThrottler { private static final Logger LOG = LoggerFactory.getLogger(LearnerSyncThrottler.class); private final Object countSyncObject = new Object(); private int syncInProgress; private volatile int maxConcurrentSyncs; public enum SyncType { DIFF, SNAP } private final SyncType syncType; /** * Constructs a new instance limiting the concurrent number of syncs to * maxConcurrentSyncs. * @param maxConcurrentSyncs maximum concurrent number of syncs * @param syncType either a snapshot sync or a txn-based diff sync * @throws java.lang.IllegalArgumentException when maxConcurrentSyncs * is less than 1 */ public LearnerSyncThrottler(int maxConcurrentSyncs, SyncType syncType) throws IllegalArgumentException { if (maxConcurrentSyncs <= 0) { String errorMsg = "maxConcurrentSyncs must be positive, was " + maxConcurrentSyncs; throw new IllegalArgumentException(errorMsg); } this.maxConcurrentSyncs = maxConcurrentSyncs; this.syncType = syncType; synchronized (countSyncObject) { syncInProgress = 0; } } /** * Indicates that a new sync is about to be sent. * * @param essential if true, do not throw an exception even * if throttling limit is reached * @throws SyncThrottleException if throttling limit has been exceeded * and essential == false, * even after waiting for the timeout * period, if any * @throws InterruptedException if thread is interrupted while trying * to start a sync; cannot happen if * timeout is zero */ protected void beginSync(boolean essential) throws SyncThrottleException, InterruptedException { synchronized (countSyncObject) { if (essential || syncInProgress < maxConcurrentSyncs) { syncInProgress++; } else { throw new SyncThrottleException(syncInProgress + 1, maxConcurrentSyncs, syncType); } } } /** * Indicates that a sync has been completed. */ public void endSync() { int newCount; synchronized (countSyncObject) { syncInProgress--; newCount = syncInProgress; countSyncObject.notify(); } if (newCount < 0) { String errorMsg = "endSync() called incorrectly; current sync count is " + newCount; LOG.error(errorMsg); } } public void setMaxConcurrentSyncs(int maxConcurrentSyncs) { this.maxConcurrentSyncs = maxConcurrentSyncs; } public int getSyncInProgress() { return syncInProgress; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032723 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LearnerZooK0100644 0000000 0000000 00000013276 15051152474 034257 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.Collections; import java.util.Map; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.DataTreeBean; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServerBean; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; /** * Parent class for all ZooKeeperServers for Learners */ public abstract class LearnerZooKeeperServer extends QuorumZooKeeperServer { /* * Request processors */ protected CommitProcessor commitProcessor; protected SyncRequestProcessor syncProcessor; public LearnerZooKeeperServer(FileTxnSnapLog logFactory, int tickTime, int minSessionTimeout, int maxSessionTimeout, int listenBacklog, ZKDatabase zkDb, QuorumPeer self) throws IOException { super(logFactory, tickTime, minSessionTimeout, maxSessionTimeout, listenBacklog, zkDb, self); } /** * Abstract method to return the learner associated with this server. * Since the Learner may change under our feet (when QuorumPeer reassigns * it) we can't simply take a reference here. Instead, we need the * subclasses to implement this. */ public abstract Learner getLearner(); /** * Returns the current state of the session tracker. This is only currently * used by a Learner to build a ping response packet. * */ protected Map getTouchSnapshot() { if (sessionTracker != null) { return ((LearnerSessionTracker) sessionTracker).snapshot(); } Map map = Collections.emptyMap(); return map; } /** * Returns the id of the associated QuorumPeer, which will do for a unique * id of this server. */ @Override public long getServerId() { return self.getMyId(); } @Override public void createSessionTracker() { sessionTracker = new LearnerSessionTracker( this, getZKDatabase().getSessionWithTimeOuts(), this.tickTime, self.getMyId(), self.areLocalSessionsEnabled(), getZooKeeperServerListener()); } @Override protected void revalidateSession(ServerCnxn cnxn, long sessionId, int sessionTimeout) throws IOException { if (upgradeableSessionTracker.isLocalSession(sessionId)) { super.revalidateSession(cnxn, sessionId, sessionTimeout); } else { getLearner().validateSession(cnxn, sessionId, sessionTimeout); } } @Override protected void registerJMX() { // register with JMX try { jmxDataTreeBean = new DataTreeBean(getZKDatabase().getDataTree()); MBeanRegistry.getInstance().register(jmxDataTreeBean, jmxServerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxDataTreeBean = null; } } public void registerJMX(ZooKeeperServerBean serverBean, LocalPeerBean localPeerBean) { // register with JMX if (self.jmxLeaderElectionBean != null) { try { MBeanRegistry.getInstance().unregister(self.jmxLeaderElectionBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); } self.jmxLeaderElectionBean = null; } try { jmxServerBean = serverBean; MBeanRegistry.getInstance().register(serverBean, localPeerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxServerBean = null; } } @Override protected void unregisterJMX() { // unregister from JMX try { if (jmxDataTreeBean != null) { MBeanRegistry.getInstance().unregister(jmxDataTreeBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxDataTreeBean = null; } protected void unregisterJMX(Learner peer) { // unregister from JMX try { if (jmxServerBean != null) { MBeanRegistry.getInstance().unregister(jmxServerBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxServerBean = null; } @Override protected void shutdownComponents() { try { if (syncProcessor != null) { syncProcessor.shutdown(); } } catch (Exception e) { LOG.warn("Ignoring unexpected exception in syncprocessor shutdown", e); } try { super.shutdownComponents(); } catch (Exception e) { LOG.warn("Ignoring unexpected exception during shutdown", e); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000165 15051152474 032721 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LocalPeerBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LocalPeerBe0100644 0000000 0000000 00000007040 15051152474 034171 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; import java.util.stream.Collectors; import org.apache.zookeeper.common.NetUtils; import org.apache.zookeeper.server.ServerCnxnHelper; /** * Implementation of the local peer MBean interface. */ public class LocalPeerBean extends ServerBean implements LocalPeerMXBean { private final QuorumPeer peer; public LocalPeerBean(QuorumPeer peer) { this.peer = peer; } public String getName() { return "replica." + peer.getMyId(); } public boolean isHidden() { return false; } public int getTickTime() { return peer.getTickTime(); } public int getMaxClientCnxnsPerHost() { return peer.getMaxClientCnxnsPerHost(); } public int getMinSessionTimeout() { return peer.getMinSessionTimeout(); } public int getMaxSessionTimeout() { return peer.getMaxSessionTimeout(); } public int getInitLimit() { return peer.getInitLimit(); } public int getSyncLimit() { return peer.getSyncLimit(); } public void setInitLimit(int initLimit) { peer.setInitLimit(initLimit); } public void setSyncLimit(int syncLimit) { peer.setSyncLimit(syncLimit); } public int getTick() { return peer.getTick(); } public String getState() { return peer.getServerState(); } public String getQuorumAddress() { return peer.getQuorumAddress().getAllAddresses().stream().map(NetUtils::formatInetAddr) .collect(Collectors.joining("|")); } public int getElectionType() { return peer.getElectionType(); } public String getElectionAddress() { return peer.getElectionAddress().getAllAddresses().stream().map(NetUtils::formatInetAddr) .collect(Collectors.joining("|")); } public String getClientAddress() { if (null != peer.cnxnFactory) { return formatInetAddr(peer.cnxnFactory.getLocalAddress()); } else { return ""; } } public String getLearnerType() { return peer.getLearnerType().toString(); } public long getConfigVersion() { return peer.getQuorumVerifier().getVersion(); } @Override public String getQuorumSystemInfo() { return peer.getQuorumVerifier().toString(); } @Override public boolean isPartOfEnsemble() { return peer.getView().containsKey(peer.getMyId()); } @Override public boolean isLeader() { return peer.isLeader(peer.getMyId()); } @Override public int getMaxCnxns() { return ServerCnxnHelper.getMaxCnxns(peer.secureCnxnFactory, peer.cnxnFactory); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000167 15051152474 032723 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LocalPeerMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LocalPeerMX0100644 0000000 0000000 00000006153 15051152474 034173 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * A local zookeeper server MBean interface. Unlike the remote peer, the local * peer provides complete state/statistics at runtime and can be managed (just * like a standalone zookeeper server). */ public interface LocalPeerMXBean extends ServerMXBean { /** * @return the number of milliseconds of each tick */ int getTickTime(); /** Current maxClientCnxns allowed from a particular host */ int getMaxClientCnxnsPerHost(); /** * @return the minimum number of milliseconds allowed for a session timeout */ int getMinSessionTimeout(); /** * @return the maximum number of milliseconds allowed for a session timeout */ int getMaxSessionTimeout(); /** * @return the number of ticks that the initial sync phase can take */ int getInitLimit(); /** * @return the number of ticks that can pass between sending a request * and getting a acknowledgment */ int getSyncLimit(); /** * Set the number of ticks that the initial sync phase can take */ void setInitLimit(int initLimit); /** * Set the number of ticks that can pass between sending a request * and getting a acknowledgment */ void setSyncLimit(int syncLimit); /** * @return the current tick */ int getTick(); /** * @return the current server state */ String getState(); /** * @return the quorum address */ String getQuorumAddress(); /** * @return the election type */ int getElectionType(); /** * @return the election address */ String getElectionAddress(); /** * @return the client address */ String getClientAddress(); /** * @return the learner type */ String getLearnerType(); /** * @return the config version */ long getConfigVersion(); /** * @return the quorum system information */ String getQuorumSystemInfo(); /** * @return true if quorum peer is part of the ensemble, false otherwise */ boolean isPartOfEnsemble(); /** * @return true if the peer is the current leader */ boolean isLeader(); /** * @return Current maxCnxns allowed to a single ZooKeeper server */ int getMaxCnxns(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000173 15051152474 032720 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LocalSessionTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/LocalSessio0100644 0000000 0000000 00000003466 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.Set; import java.util.concurrent.ConcurrentMap; import org.apache.zookeeper.server.SessionTrackerImpl; import org.apache.zookeeper.server.ZooKeeperServerListener; /** * Local session tracker. */ public class LocalSessionTracker extends SessionTrackerImpl { public LocalSessionTracker(SessionExpirer expirer, ConcurrentMap sessionsWithTimeouts, int tickTime, long id, ZooKeeperServerListener listener) { super(expirer, sessionsWithTimeouts, tickTime, id, listener); } public boolean isLocalSession(long sessionId) { return isTrackingSession(sessionId); } public boolean isGlobalSession(long sessionId) { return false; } public long createSession(int sessionTimeout) { long sessionId = super.createSession(sessionTimeout); commitSession(sessionId, sessionTimeout); return sessionId; } public Set localSessions() { return sessionsWithTimeout.keySet(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000171 15051152474 032716 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/MultipleAddresses.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/MultipleAdd0100644 0000000 0000000 00000020535 15051152474 034264 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.util.Arrays.asList; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NoRouteToHostException; import java.net.UnknownHostException; import java.time.Duration; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * This class allows to store several quorum and electing addresses. * * See ZOOKEEPER-3188 for a discussion of this feature. */ public final class MultipleAddresses { public static final Duration DEFAULT_TIMEOUT = Duration.ofMillis(1000); private static Set newConcurrentHashSet() { return Collections.newSetFromMap(new ConcurrentHashMap<>()); } private Set addresses; private final Duration timeout; public MultipleAddresses() { this(Collections.emptyList()); } public MultipleAddresses(Collection addresses) { this(addresses, DEFAULT_TIMEOUT); } public MultipleAddresses(InetSocketAddress address) { this(asList(address), DEFAULT_TIMEOUT); } public MultipleAddresses(Collection addresses, Duration timeout) { this.addresses = newConcurrentHashSet(); this.addresses.addAll(addresses); this.timeout = timeout; } public boolean isEmpty() { return addresses.isEmpty(); } /** * Returns all addresses in an unmodifiable set. * * @return set of all InetSocketAddress */ public Set getAllAddresses() { return Collections.unmodifiableSet(addresses); } /** * Returns wildcard addresses for all ports * * @return set of InetSocketAddress with wildcards for all ports */ public Set getWildcardAddresses() { return addresses.stream().map(a -> new InetSocketAddress(a.getPort())).collect(Collectors.toSet()); } /** * Returns all ports * * @return list of all ports */ public List getAllPorts() { return addresses.stream().map(InetSocketAddress::getPort).distinct().collect(Collectors.toList()); } /** * Returns distinct list of all host strings * * @return list of all hosts */ public List getAllHostStrings() { return addresses.stream().map(InetSocketAddress::getHostString).distinct().collect(Collectors.toList()); } public void addAddress(InetSocketAddress address) { addresses.add(address); } /** * Returns a reachable address. If none is reachable than throws exception. * The function is nondeterministic in the sense that the result of calling this function * twice with the same set of reachable addresses might lead to different results. * * @return address which is reachable. * @throws NoRouteToHostException if none of the addresses are reachable */ public InetSocketAddress getReachableAddress() throws NoRouteToHostException { // using parallelStream() + findAny() will help to minimize the time spent on network operations return addresses.parallelStream() .filter(this::checkIfAddressIsReachable) .findAny() .orElseThrow(() -> new NoRouteToHostException("No valid address among " + addresses)); } /** * Returns a set of all reachable addresses. If none is reachable than returns empty set. * * @return all addresses which are reachable. */ public Set getAllReachableAddresses() { // using parallelStream() will help to minimize the time spent on network operations return addresses.parallelStream() .filter(this::checkIfAddressIsReachable) .collect(Collectors.toSet()); } /** * Returns a set of all reachable addresses. If none is reachable than returns all addresses. * * @return all reachable addresses, or all addresses if none is reachable. */ public Set getAllReachableAddressesOrAll() { // if there is only a single address provided then we don't need to do any reachability check if (addresses.size() == 1) { return getAllAddresses(); } Set allReachable = getAllReachableAddresses(); if (allReachable.isEmpty()) { return getAllAddresses(); } return allReachable; } /** * Returns a reachable address or an arbitrary one, if none is reachable. It throws an exception * if there are no addresses registered. The function is nondeterministic in the sense that the * result of calling this function twice with the same set of reachable addresses might lead * to different results. * * @return address which is reachable or fist one. * @throws NoSuchElementException if there is no address registered */ public InetSocketAddress getReachableOrOne() { InetSocketAddress address; // if there is only a single address provided then we don't do any reachability check if (addresses.size() == 1) { return getOne(); } try { address = getReachableAddress(); } catch (NoRouteToHostException e) { address = getOne(); } return address; } /** * Performs a parallel DNS lookup for all addresses. * * If the DNS lookup fails, then address remain unmodified. */ public void recreateSocketAddresses() { addresses = addresses.parallelStream() .map(this::recreateSocketAddress) .collect(Collectors.toCollection(MultipleAddresses::newConcurrentHashSet)); } /** * Returns an address from the set. * * @return address from a set. * @throws NoSuchElementException if there is no address registered */ public InetSocketAddress getOne() { return addresses.iterator().next(); } /** * Returns the number of addresses in the set. * * @return the number of addresses. */ public int size() { return addresses.size(); } private boolean checkIfAddressIsReachable(InetSocketAddress address) { if (address.isUnresolved()) { return false; } try { if (address.getAddress().isReachable((int) timeout.toMillis())) { return true; } } catch (IOException ignored) { // ignore, we don't really care if we can't reach it for timeout or for IO problems } return false; } private InetSocketAddress recreateSocketAddress(InetSocketAddress address) { try { return new InetSocketAddress(InetAddress.getByName(address.getHostString()), address.getPort()); } catch (UnknownHostException e) { return address; } } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (o == null || getClass() != o.getClass()) { return false; } MultipleAddresses that = (MultipleAddresses) o; return Objects.equals(addresses, that.addresses); } @Override public int hashCode() { return Objects.hash(addresses); } @Override public String toString() { return addresses.stream().map(InetSocketAddress::toString).collect(Collectors.joining("|")); } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000160 15051152474 032714 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Observer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Observer.ja0100644 0000000 0000000 00000027215 15051152474 034242 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.ByteBuffer; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; import org.apache.jute.Record; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.ObserverBean; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Observers are peers that do not take part in the atomic broadcast protocol. * Instead, they are informed of successful proposals by the Leader. Observers * therefore naturally act as a relay point for publishing the proposal stream * and can relieve Followers of some of the connection load. Observers may * submit proposals, but do not vote in their acceptance. * * See ZOOKEEPER-368 for a discussion of this feature. */ public class Observer extends Learner { private static final Logger LOG = LoggerFactory.getLogger(Observer.class); /** * When observer lost its connection with the leader, it waits for 0 to the * specified value before trying to reconnect with the leader. So that * the entire observer fleet won't try to run leader election and reconnect * to the leader at once. Default value is zero. */ public static final String OBSERVER_RECONNECT_DELAY_MS = "zookeeper.observer.reconnectDelayMs"; /** * Delay the Observer's participation in a leader election upon disconnect * so as to prevent unexpected additional load on the voting peers during * the process. Default value is 200. */ public static final String OBSERVER_ELECTION_DELAY_MS = "zookeeper.observer.election.DelayMs"; private static final long reconnectDelayMs; private static volatile long observerElectionDelayMs; static { reconnectDelayMs = Long.getLong(OBSERVER_RECONNECT_DELAY_MS, 0); LOG.info("{} = {}", OBSERVER_RECONNECT_DELAY_MS, reconnectDelayMs); observerElectionDelayMs = Long.getLong(OBSERVER_ELECTION_DELAY_MS, 200); LOG.info("{} = {}", OBSERVER_ELECTION_DELAY_MS , observerElectionDelayMs); } /** * next learner master to try, when specified */ private static final AtomicReference nextLearnerMaster = new AtomicReference<>(); private QuorumPeer.QuorumServer currentLearnerMaster = null; Observer(QuorumPeer self, ObserverZooKeeperServer observerZooKeeperServer) { this.self = self; this.zk = observerZooKeeperServer; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Observer ").append(sock); sb.append(" pendingRevalidationCount:").append(pendingRevalidations.size()); return sb.toString(); } /** * the main method called by the observer to observe the leader * @throws Exception */ void observeLeader() throws Exception { zk.registerJMX(new ObserverBean(this, zk), self.jmxLocalPeerBean); long connectTime = 0; boolean completedSync = false; try { self.setZabState(QuorumPeer.ZabState.DISCOVERY); QuorumServer master = findLearnerMaster(); try { connectToLeader(master.addr, master.hostname); connectTime = System.currentTimeMillis(); long newLeaderZxid = registerWithLeader(Leader.OBSERVERINFO); if (self.isReconfigStateChange()) { throw new Exception("learned about role change"); } final long startTime = Time.currentElapsedTime(); self.setLeaderAddressAndId(master.addr, master.getId()); self.setZabState(QuorumPeer.ZabState.SYNCHRONIZATION); syncWithLeader(newLeaderZxid); self.setZabState(QuorumPeer.ZabState.BROADCAST); completedSync = true; final long syncTime = Time.currentElapsedTime() - startTime; ServerMetrics.getMetrics().OBSERVER_SYNC_TIME.add(syncTime); QuorumPacket qp = new QuorumPacket(); while (this.isRunning() && nextLearnerMaster.get() == null) { readPacket(qp); processPacket(qp); } } catch (Exception e) { LOG.warn("Exception when observing the leader", e); closeSocket(); // clear pending revalidations pendingRevalidations.clear(); } } finally { currentLearnerMaster = null; zk.unregisterJMX(this); if (connectTime != 0) { long connectionDuration = System.currentTimeMillis() - connectTime; LOG.info( "Disconnected from leader (with address: {}). Was connected for {}ms. Sync state: {}", leaderAddr, connectionDuration, completedSync); messageTracker.dumpToLog(leaderAddr.toString()); } } } private QuorumServer findLearnerMaster() { QuorumPeer.QuorumServer prescribedLearnerMaster = nextLearnerMaster.getAndSet(null); if (prescribedLearnerMaster != null && self.validateLearnerMaster(Long.toString(prescribedLearnerMaster.id)) == null) { LOG.warn("requested next learner master {} is no longer valid", prescribedLearnerMaster); prescribedLearnerMaster = null; } final QuorumPeer.QuorumServer master = (prescribedLearnerMaster == null) ? self.findLearnerMaster(findLeader()) : prescribedLearnerMaster; currentLearnerMaster = master; if (master == null) { LOG.warn("No learner master found"); } else { LOG.info("Observing new leader sid={} addr={}", master.id, master.addr); } return master; } /** * Controls the response of an observer to the receipt of a quorumpacket * @param qp * @throws Exception */ protected void processPacket(QuorumPacket qp) throws Exception { TxnLogEntry logEntry; TxnHeader hdr; TxnDigest digest; Record txn; switch (qp.getType()) { case Leader.PING: ping(qp); break; case Leader.PROPOSAL: LOG.warn("Ignoring proposal"); break; case Leader.COMMIT: LOG.warn("Ignoring commit"); break; case Leader.UPTODATE: LOG.error("Received an UPTODATE message after Observer started"); break; case Leader.REVALIDATE: revalidate(qp); break; case Leader.SYNC: ((ObserverZooKeeperServer) zk).sync(); break; case Leader.INFORM: ServerMetrics.getMetrics().LEARNER_COMMIT_RECEIVED_COUNT.add(1); logEntry = SerializeUtils.deserializeTxn(qp.getData()); Request request = logEntry.toRequest(); request.logLatency(ServerMetrics.getMetrics().COMMIT_PROPAGATION_LATENCY); ObserverZooKeeperServer obs = (ObserverZooKeeperServer) zk; obs.commitRequest(request); break; case Leader.INFORMANDACTIVATE: // get new designated leader from (current) leader's message ByteBuffer buffer = ByteBuffer.wrap(qp.getData()); long suggestedLeaderId = buffer.getLong(); byte[] remainingdata = new byte[buffer.remaining()]; buffer.get(remainingdata); logEntry = SerializeUtils.deserializeTxn(remainingdata); txn = logEntry.getTxn(); QuorumVerifier qv = self.configFromString(new String(((SetDataTxn) txn).getData(), UTF_8)); request = logEntry.toRequest(); obs = (ObserverZooKeeperServer) zk; boolean majorChange = self.processReconfig(qv, suggestedLeaderId, qp.getZxid(), true); obs.commitRequest(request); if (majorChange) { throw new Exception("changes proposed in reconfig"); } break; default: LOG.warn("Unknown packet type: {}", LearnerHandler.packetToString(qp)); break; } } /** * Shutdown the Observer. */ public void shutdown() { LOG.info("shutdown Observer"); super.shutdown(); } static void waitForReconnectDelay() { waitForReconnectDelayHelper(reconnectDelayMs); } static void waitForObserverElectionDelay() { waitForReconnectDelayHelper(observerElectionDelayMs); } private static void waitForReconnectDelayHelper(long delayValueMs) { if (delayValueMs > 0) { long randomDelay = ThreadLocalRandom.current().nextLong(delayValueMs); LOG.info("Waiting for {} ms before reconnecting with the leader", randomDelay); try { Thread.sleep(randomDelay); } catch (InterruptedException e) { LOG.warn("Interrupted while waiting", e); } } } public long getLearnerMasterId() { QuorumPeer.QuorumServer current = currentLearnerMaster; return current == null ? -1 : current.id; } /** * Prompts the Observer to disconnect from its current learner master and reconnect * to the specified server. If that connection attempt fails, the Observer will * fail over to the next available learner master. */ public boolean setLearnerMaster(String learnerMaster) { final QuorumPeer.QuorumServer server = self.validateLearnerMaster(learnerMaster); if (server == null) { return false; } else if (server.equals(currentLearnerMaster)) { LOG.info("Already connected to requested learner master sid={} addr={}", server.id, server.addr); return true; } else { LOG.info("Requesting disconnect and reconnect to new learner master sid={} addr={}", server.id, server.addr); nextLearnerMaster.set(server); return true; } } public QuorumPeer.QuorumServer getCurrentLearnerMaster() { return currentLearnerMaster; } public static long getObserverElectionDelayMs() { return observerElectionDelayMs; } public static void setObserverElectionDelayMs(long electionDelayMs) { observerElectionDelayMs = electionDelayMs; LOG.info("{} = {}", OBSERVER_ELECTION_DELAY_MS, observerElectionDelayMs); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032722 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverMXB0100644 0000000 0000000 00000002770 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.ZooKeeperServerMXBean; /** * Observer MX Bean interface, implemented by ObserverBean * */ public interface ObserverMXBean extends ZooKeeperServerMXBean { /** * @return count of pending revalidations */ int getPendingRevalidationCount(); /** * @return socket address */ String getQuorumAddress(); /** * @return address of the current learner master */ String getLearnerMaster(); /** * requests the Observer switch to a new learner master * * @param learnerMaster address of the desired learner master */ void setLearnerMaster(String learnerMaster); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032722 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverMaster.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverMas0100644 0000000 0000000 00000045664 15051152474 034322 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Used by Followers to host Observers. This reduces the network load on the Leader process by pushing * the responsibility for keeping Observers in sync off the leading peer. * * It is expected that Observers will continue to perform the initial vetting of clients and requests. * Observers send the request to the follower where it is received by an ObserverMaster. * * The ObserverMaster forwards a copy of the request to the ensemble Leader and inserts it into its own * request processor pipeline where it can be matched with the response comes back. All commits received * from the Leader will be forwarded along to every Learner connected to the ObserverMaster. * * New Learners connecting to a Follower will receive a LearnerHandler object and be party to its syncing logic * to be brought up to date. * * The logic is quite a bit simpler than the corresponding logic in Leader because it only hosts observers. */ public class ObserverMaster extends LearnerMaster implements Runnable { private static final Logger LOG = LoggerFactory.getLogger(ObserverMaster.class); //Follower counter private final AtomicLong followerCounter = new AtomicLong(-1); private QuorumPeer self; private FollowerZooKeeperServer zks; private int port; private Set activeObservers = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final ConcurrentHashMap connectionBeans = new ConcurrentHashMap<>(); /** * we want to keep a log of past txns so that observers can sync up with us when we connect, * but we can't keep everything in memory, so this limits how much memory will be dedicated * to keeping recent txns. */ private static final int PKTS_SIZE_LIMIT = 32 * 1024 * 1024; private static volatile int pktsSizeLimit = Integer.getInteger("zookeeper.observerMaster.sizeLimit", PKTS_SIZE_LIMIT); private ConcurrentLinkedQueue proposedPkts = new ConcurrentLinkedQueue<>(); private ConcurrentLinkedQueue committedPkts = new ConcurrentLinkedQueue<>(); private int pktsSize = 0; private long lastProposedZxid; // ensure ordering of revalidations returned to this learner private final Object revalidateSessionLock = new Object(); private final ConcurrentLinkedQueue pendingRevalidations = new ConcurrentLinkedQueue<>(); static class Revalidation { public final long sessionId; public final int timeout; public final LearnerHandler handler; Revalidation(final Long sessionId, final int timeout, final LearnerHandler handler) { this.sessionId = sessionId; this.timeout = timeout; this.handler = handler; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final Revalidation that = (Revalidation) o; return sessionId == that.sessionId && timeout == that.timeout && handler.equals(that.handler); } @Override public int hashCode() { int result = (int) (sessionId ^ (sessionId >>> 32)); result = 31 * result + timeout; result = 31 * result + handler.hashCode(); return result; } } private Thread thread; private ServerSocket ss; private boolean listenerRunning; private ScheduledExecutorService pinger; Runnable ping = new Runnable() { @Override public void run() { for (LearnerHandler lh : activeObservers) { lh.ping(); } } }; ObserverMaster(QuorumPeer self, FollowerZooKeeperServer zks, int port) { this.self = self; this.zks = zks; this.port = port; } @Override public void addLearnerHandler(LearnerHandler learnerHandler) { if (!listenerRunning) { throw new RuntimeException(("ObserverMaster is not running")); } } @Override public void removeLearnerHandler(LearnerHandler learnerHandler) { activeObservers.remove(learnerHandler); } @Override public int syncTimeout() { return self.getSyncLimit() * self.getTickTime(); } @Override public int getTickOfNextAckDeadline() { return self.tick.get() + self.syncLimit; } @Override public int getTickOfInitialAckDeadline() { return self.tick.get() + self.initLimit + self.syncLimit; } @Override public long getAndDecrementFollowerCounter() { return followerCounter.getAndDecrement(); } @Override public void waitForEpochAck(long sid, StateSummary ss) throws IOException, InterruptedException { // since this is done by an active follower, we don't need to wait for anything } @Override public void waitForStartup() throws InterruptedException { // since this is done by an active follower, we don't need to wait for anything } @Override public synchronized long getLastProposed() { return lastProposedZxid; } @Override public long getEpochToPropose(long sid, long lastAcceptedEpoch) throws InterruptedException, IOException { return self.getCurrentEpoch(); } @Override public ZKDatabase getZKDatabase() { return zks.getZKDatabase(); } @Override public void waitForNewLeaderAck(long sid, long zxid) throws InterruptedException { // no need to wait since we are a follower } @Override public int getCurrentTick() { return self.tick.get(); } @Override public void processAck(long sid, long zxid, SocketAddress localSocketAddress) { if ((zxid & 0xffffffffL) == 0) { /* * We no longer process NEWLEADER ack by this method. However, * the learner sends ack back to the leader after it gets UPTODATE * so we just ignore the message. */ return; } throw new RuntimeException("Observers shouldn't send ACKS ack = " + Long.toHexString(zxid)); } @Override public void touch(long sess, int to) { zks.getSessionTracker().touchSession(sess, to); } boolean revalidateLearnerSession(QuorumPacket qp) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); DataInputStream dis = new DataInputStream(bis); long id = dis.readLong(); boolean valid = dis.readBoolean(); Iterator itr = pendingRevalidations.iterator(); if (!itr.hasNext()) { // not a learner session, handle locally return false; } Revalidation revalidation = itr.next(); if (revalidation.sessionId != id) { // not a learner session, handle locally return false; } itr.remove(); LearnerHandler learnerHandler = revalidation.handler; // create a copy here as the qp object is reused by the Follower and may be mutated QuorumPacket deepCopy = new QuorumPacket( qp.getType(), qp.getZxid(), Arrays.copyOf(qp.getData(), qp.getData().length), qp.getAuthinfo() == null ? null : new ArrayList<>(qp.getAuthinfo())); learnerHandler.queuePacket(deepCopy); // To keep consistent as leader, touch the session when it's // revalidating the session, only update if it's a valid session. if (valid) { touch(revalidation.sessionId, revalidation.timeout); } return true; } @Override public void revalidateSession(QuorumPacket qp, LearnerHandler learnerHandler) throws IOException { ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); DataInputStream dis = new DataInputStream(bis); long id = dis.readLong(); int to = dis.readInt(); synchronized (revalidateSessionLock) { pendingRevalidations.add(new Revalidation(id, to, learnerHandler)); Learner learner = zks.getLearner(); if (learner != null) { learner.writePacket(qp, true); } } } @Override public void submitLearnerRequest(Request si) { zks.processObserverRequest(si); } @Override public synchronized long startForwarding(LearnerHandler learnerHandler, long lastSeenZxid) { Iterator itr = committedPkts.iterator(); if (itr.hasNext()) { QuorumPacket packet = itr.next(); if (packet.getZxid() > lastSeenZxid + 1) { LOG.error( "LearnerHandler is too far behind (0x{} < 0x{}), disconnecting {} at {}", Long.toHexString(lastSeenZxid + 1), Long.toHexString(packet.getZxid()), learnerHandler.getSid(), learnerHandler.getRemoteAddress()); learnerHandler.shutdown(); return -1; } else if (packet.getZxid() == lastSeenZxid + 1) { learnerHandler.queuePacket(packet); } long queueHeadZxid = packet.getZxid(); long queueBytesUsed = LearnerHandler.packetSize(packet); while (itr.hasNext()) { packet = itr.next(); if (packet.getZxid() <= lastSeenZxid) { continue; } learnerHandler.queuePacket(packet); queueBytesUsed += LearnerHandler.packetSize(packet); } LOG.info( "finished syncing observer from retained commit queue: sid {}, " + "queue head 0x{}, queue tail 0x{}, sync position 0x{}, num packets used {}, " + "num bytes used {}", learnerHandler.getSid(), Long.toHexString(queueHeadZxid), Long.toHexString(packet.getZxid()), Long.toHexString(lastSeenZxid), packet.getZxid() - lastSeenZxid, queueBytesUsed); } activeObservers.add(learnerHandler); return lastProposedZxid; } @Override public long getQuorumVerifierVersion() { return self.getQuorumVerifier().getVersion(); } @Override public String getPeerInfo(long sid) { QuorumPeer.QuorumServer server = self.getView().get(sid); return server == null ? "" : server.toString(); } @Override public byte[] getQuorumVerifierBytes() { return self.getLastSeenQuorumVerifier().toString().getBytes(UTF_8); } @Override public QuorumAuthServer getQuorumAuthServer() { return (self == null) ? null : self.authServer; } void proposalReceived(QuorumPacket qp) { proposedPkts.add(new QuorumPacket(Leader.INFORM, qp.getZxid(), qp.getData(), null)); } private synchronized QuorumPacket removeProposedPacket(long zxid) { QuorumPacket pkt = proposedPkts.peek(); if (pkt == null || pkt.getZxid() > zxid) { LOG.debug("ignore missing proposal packet for {}", Long.toHexString(zxid)); return null; } if (pkt.getZxid() != zxid) { final String m = String.format( "Unexpected proposal packet on commit ack, expected zxid 0x%d got zxid 0x%d", zxid, pkt.getZxid()); LOG.error(m); throw new RuntimeException(m); } proposedPkts.remove(); return pkt; } private synchronized void cacheCommittedPacket(final QuorumPacket pkt) { committedPkts.add(pkt); pktsSize += LearnerHandler.packetSize(pkt); // remove 5 packets for every one added as we near the size limit for (int i = 0; pktsSize > pktsSizeLimit * 0.8 && i < 5; i++) { QuorumPacket oldPkt = committedPkts.poll(); if (oldPkt == null) { pktsSize = 0; break; } pktsSize -= LearnerHandler.packetSize(oldPkt); } // enforce the size limit as a hard cap while (pktsSize > pktsSizeLimit) { QuorumPacket oldPkt = committedPkts.poll(); if (oldPkt == null) { pktsSize = 0; break; } pktsSize -= LearnerHandler.packetSize(oldPkt); } } private synchronized void sendPacket(final QuorumPacket pkt) { for (LearnerHandler lh : activeObservers) { lh.queuePacket(pkt); } lastProposedZxid = pkt.getZxid(); } synchronized void proposalCommitted(long zxid) { QuorumPacket pkt = removeProposedPacket(zxid); if (pkt == null) { return; } cacheCommittedPacket(pkt); sendPacket(pkt); } synchronized void informAndActivate(long zxid, long suggestedLeaderId) { QuorumPacket pkt = removeProposedPacket(zxid); if (pkt == null) { return; } // Build the INFORMANDACTIVATE packet QuorumPacket informAndActivateQP = Leader.buildInformAndActivePacket(zxid, suggestedLeaderId, pkt.getData()); cacheCommittedPacket(informAndActivateQP); sendPacket(informAndActivateQP); } public synchronized void start() throws IOException { if (thread != null && thread.isAlive()) { return; } listenerRunning = true; int backlog = 10; // dog science InetAddress address = self.getQuorumAddress().getReachableOrOne().getAddress(); if (self.shouldUsePortUnification() || self.isSslQuorum()) { boolean allowInsecureConnection = self.shouldUsePortUnification(); if (self.getQuorumListenOnAllIPs()) { ss = new UnifiedServerSocket(self.getX509Util(), allowInsecureConnection, port, backlog); } else { ss = new UnifiedServerSocket(self.getX509Util(), allowInsecureConnection, port, backlog, address); } } else { if (self.getQuorumListenOnAllIPs()) { ss = new ServerSocket(port, backlog); } else { ss = new ServerSocket(port, backlog, address); } } thread = new Thread(this, "ObserverMaster"); thread.start(); pinger = Executors.newSingleThreadScheduledExecutor(); pinger.scheduleAtFixedRate(ping, self.tickTime / 2, self.tickTime / 2, TimeUnit.MILLISECONDS); } public void run() { ServerSocket ss; synchronized (this) { ss = this.ss; } while (listenerRunning) { try { Socket s = ss.accept(); // start with the initLimit, once the ack is processed // in LearnerHandler switch to the syncLimit s.setSoTimeout(self.tickTime * self.initLimit); BufferedInputStream is = new BufferedInputStream(s.getInputStream()); LearnerHandler lh = new LearnerHandler(s, is, this); lh.start(); } catch (Exception e) { if (listenerRunning) { LOG.debug("Ignoring accept exception (maybe shutting down)", e); } else { LOG.debug("Ignoring accept exception (maybe client closed)", e); } } } /* * we don't need to close ss because we only got here because listenerRunning is * false and that is set and then ss is closed() in stop() */ } public synchronized void stop() { listenerRunning = false; if (pinger != null) { pinger.shutdownNow(); } if (ss != null) { try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } for (LearnerHandler lh : activeObservers) { lh.shutdown(); } } int getNumActiveObservers() { return activeObservers.size(); } public Iterable> getActiveObservers() { Set> info = new HashSet<>(); for (LearnerHandler lh : activeObservers) { info.add(lh.getLearnerHandlerInfo()); } return info; } public void resetObserverConnectionStats() { for (LearnerHandler lh : activeObservers) { lh.resetObserverConnectionStats(); } } int getPktsSizeLimit() { return pktsSizeLimit; } static void setPktsSizeLimit(final int sizeLimit) { pktsSizeLimit = sizeLimit; } @Override public void registerLearnerHandlerBean(final LearnerHandler learnerHandler, Socket socket) { LearnerHandlerBean bean = new LearnerHandlerBean(learnerHandler, socket); if (zks.registerJMX(bean)) { connectionBeans.put(learnerHandler, bean); } } @Override public void unregisterLearnerHandlerBean(final LearnerHandler learnerHandler) { LearnerHandlerBean bean = connectionBeans.remove(learnerHandler); if (bean != null) { MBeanRegistry.getInstance().unregister(bean); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032707 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverReq0100644 0000000 0000000 00000014326 15051152474 034320 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.txn.ErrorTxn; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This RequestProcessor forwards any requests that modify the state of the * system to the Leader. */ public class ObserverRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(ObserverRequestProcessor.class); ObserverZooKeeperServer zks; RequestProcessor nextProcessor; // We keep a queue of requests. As requests get submitted they are // stored here. The queue is drained in the run() method. LinkedBlockingQueue queuedRequests = new LinkedBlockingQueue<>(); boolean finished = false; /** * Constructor - takes an ObserverZooKeeperServer to associate with * and the next processor to pass requests to after we're finished. * @param zks * @param nextProcessor */ public ObserverRequestProcessor(ObserverZooKeeperServer zks, RequestProcessor nextProcessor) { super("ObserverRequestProcessor:" + zks.getServerId(), zks.getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; } @Override public void run() { try { while (!finished) { ServerMetrics.getMetrics().LEARNER_REQUEST_PROCESSOR_QUEUE_SIZE.add(queuedRequests.size()); Request request = queuedRequests.take(); if (LOG.isTraceEnabled()) { ZooTrace.logRequest(LOG, ZooTrace.CLIENT_REQUEST_TRACE_MASK, 'F', request, ""); } if (request == Request.requestOfDeath) { break; } // Screen quorum requests against ACLs first if (!zks.authWriteRequest(request)) { continue; } // We want to queue the request to be processed before we submit // the request to the leader so that we are ready to receive // the response nextProcessor.processRequest(request); if (request.isThrottled()) { continue; } // We now ship the request to the leader. As with all // other quorum operations, sync also follows this code // path, but different from others, we need to keep track // of the sync operations this Observer has pending, so we // add it to pendingSyncs. switch (request.type) { case OpCode.sync: zks.pendingSyncs.add(request); zks.getObserver().request(request); break; case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: case OpCode.delete: case OpCode.deleteContainer: case OpCode.setData: case OpCode.reconfig: case OpCode.setACL: case OpCode.multi: case OpCode.check: zks.getObserver().request(request); break; case OpCode.createSession: case OpCode.closeSession: // Don't forward local sessions to the leader. if (!request.isLocalSession()) { zks.getObserver().request(request); } break; } } } catch (RuntimeException e) { // spotbugs require explicit catch of RuntimeException handleException(this.getName(), e); } catch (Exception e) { handleException(this.getName(), e); } LOG.info("ObserverRequestProcessor exited loop!"); } /** * Simply queue the request, which will be processed in FIFO order. */ public void processRequest(Request request) { if (!finished) { Request upgradeRequest = null; try { upgradeRequest = zks.checkUpgradeSession(request); } catch (KeeperException ke) { if (request.getHdr() != null) { request.getHdr().setType(OpCode.error); request.setTxn(new ErrorTxn(ke.code().intValue())); } request.setException(ke); LOG.info("Error creating upgrade request", ke); } catch (IOException ie) { LOG.error("Unexpected error in upgrade", ie); } if (upgradeRequest != null) { queuedRequests.add(upgradeRequest); } queuedRequests.add(request); } } /** * Shutdown the processor. */ public void shutdown() { LOG.info("Shutting down"); finished = true; queuedRequests.clear(); queuedRequests.add(Request.requestOfDeath); nextProcessor.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032724 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ObserverZoo0100644 0000000 0000000 00000011477 15051152474 034344 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.BiConsumer; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A ZooKeeperServer for the Observer node type. Not much is different, but * we anticipate specializing the request processors in the future. * */ public class ObserverZooKeeperServer extends LearnerZooKeeperServer { private static final Logger LOG = LoggerFactory.getLogger(ObserverZooKeeperServer.class); /** * Enable since request processor for writing txnlog to disk and * take periodic snapshot. Default is ON. */ private final boolean syncRequestProcessorEnabled = this.self.getSyncEnabled(); /* * Pending sync requests */ ConcurrentLinkedQueue pendingSyncs = new ConcurrentLinkedQueue<>(); ObserverZooKeeperServer(FileTxnSnapLog logFactory, QuorumPeer self, ZKDatabase zkDb) throws IOException { super(logFactory, self.tickTime, self.minSessionTimeout, self.maxSessionTimeout, self.clientPortListenBacklog, zkDb, self); LOG.info("syncEnabled ={}", syncRequestProcessorEnabled); } public Observer getObserver() { return self.observer; } @Override public Learner getLearner() { return self.observer; } /** * Unlike a Follower, which sees a full request only during the PROPOSAL * phase, Observers get all the data required with the INFORM packet. * This method commits a request that has been unpacked by from an INFORM * received from the Leader. * * @param request */ public void commitRequest(Request request) { if (syncRequestProcessorEnabled) { // Write to txnlog and take periodic snapshot syncProcessor.processRequest(request); } commitProcessor.commit(request); } /** * Set up the request processors for an Observer: * firstProcesor->commitProcessor->finalProcessor */ @Override protected void setupRequestProcessors() { // We might consider changing the processor behaviour of // Observers to, for example, remove the disk sync requirements. // Currently, they behave almost exactly the same as followers. RequestProcessor finalProcessor = new FinalRequestProcessor(this); commitProcessor = new CommitProcessor(finalProcessor, Long.toString(getServerId()), true, getZooKeeperServerListener()); commitProcessor.start(); firstProcessor = new ObserverRequestProcessor(this, commitProcessor); ((ObserverRequestProcessor) firstProcessor).start(); /* * Observer should write to disk, so that the it won't request * too old txn from the leader which may lead to getting an entire * snapshot. * * However, this may degrade performance as it has to write to disk * and do periodic snapshot which may double the memory requirements */ if (syncRequestProcessorEnabled) { syncProcessor = new SyncRequestProcessor(this, null); syncProcessor.start(); } } /* * Process a sync request */ public synchronized void sync() { if (pendingSyncs.size() == 0) { LOG.warn("Not expecting a sync."); return; } Request r = pendingSyncs.remove(); commitProcessor.commit(r); } @Override public String getState() { return "observer"; } @Override public void dumpMonitorValues(BiConsumer response) { super.dumpMonitorValues(response); response.accept("observer_master_id", getObserver().getLearnerMasterId()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000171 15051152474 032716 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/PrependableSocket.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Prependable0100644 0000000 0000000 00000004564 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.io.InputStream; import java.io.PushbackInputStream; import java.net.Socket; import java.net.SocketImpl; public class PrependableSocket extends Socket { private PushbackInputStream pushbackInputStream; public PrependableSocket(SocketImpl base) throws IOException { super(base); } @Override public InputStream getInputStream() throws IOException { if (pushbackInputStream == null) { return super.getInputStream(); } return pushbackInputStream; } /** * Prepend some bytes that have already been read back to the socket's input stream. Note that this method can be * called at most once with a non-0 length per socket instance. * @param bytes the bytes to prepend. * @param offset offset in the byte array to start at. * @param length number of bytes to prepend. * @throws IOException if this method was already called on the socket instance, or if super.getInputStream() throws. */ public void prependToInputStream(byte[] bytes, int offset, int length) throws IOException { if (length == 0) { return; // nothing to prepend } if (pushbackInputStream != null) { throw new IOException("prependToInputStream() called more than once"); } PushbackInputStream pushbackInputStream = new PushbackInputStream(getInputStream(), length); pushbackInputStream.unread(bytes, offset, length); this.pushbackInputStream = pushbackInputStream; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032707 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ProposalRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ProposalReq0100644 0000000 0000000 00000010507 15051152474 034325 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.quorum.Leader.XidRolloverException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This RequestProcessor simply forwards requests to an AckRequestProcessor and * SyncRequestProcessor. */ public class ProposalRequestProcessor implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(ProposalRequestProcessor.class); LeaderZooKeeperServer zks; RequestProcessor nextProcessor; SyncRequestProcessor syncProcessor; // If this property is set, requests from Learners won't be forwarded // to the CommitProcessor in order to save resources public static final String FORWARD_LEARNER_REQUESTS_TO_COMMIT_PROCESSOR_DISABLED = "zookeeper.forward_learner_requests_to_commit_processor_disabled"; private final boolean forwardLearnerRequestsToCommitProcessorDisabled; public ProposalRequestProcessor(LeaderZooKeeperServer zks, RequestProcessor nextProcessor) { this.zks = zks; this.nextProcessor = nextProcessor; AckRequestProcessor ackProcessor = new AckRequestProcessor(zks.getLeader()); syncProcessor = new SyncRequestProcessor(zks, ackProcessor); forwardLearnerRequestsToCommitProcessorDisabled = Boolean.getBoolean( FORWARD_LEARNER_REQUESTS_TO_COMMIT_PROCESSOR_DISABLED); LOG.info("{} = {}", FORWARD_LEARNER_REQUESTS_TO_COMMIT_PROCESSOR_DISABLED, forwardLearnerRequestsToCommitProcessorDisabled); } /** * initialize this processor */ public void initialize() { syncProcessor.start(); } public void processRequest(Request request) throws RequestProcessorException { /* In the following IF-THEN-ELSE block, we process syncs on the leader. * If the sync is coming from a follower, then the follower * handler adds it to syncHandler. Otherwise, if it is a client of * the leader that issued the sync command, then syncHandler won't * contain the handler. In this case, we add it to syncHandler, and * call processRequest on the next processor. */ if (request instanceof LearnerSyncRequest) { zks.getLeader().processSync((LearnerSyncRequest) request); } else { if (shouldForwardToNextProcessor(request)) { nextProcessor.processRequest(request); } if (request.getHdr() != null) { // We need to sync and get consensus on any transactions try { zks.getLeader().propose(request); } catch (XidRolloverException e) { throw new RequestProcessorException(e.getMessage(), e); } syncProcessor.processRequest(request); } } } public void shutdown() { LOG.info("Shutting down"); nextProcessor.shutdown(); syncProcessor.shutdown(); } private boolean shouldForwardToNextProcessor(Request request) { if (!forwardLearnerRequestsToCommitProcessorDisabled) { return true; } if (request.getOwner() instanceof LearnerHandler) { ServerMetrics.getMetrics().REQUESTS_NOT_FORWARDED_TO_COMMIT_PROCESSOR.add(1); return false; } return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000162 15051152474 032716 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumBean.0100644 0000000 0000000 00000004625 15051152474 034216 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.server.ZooKeeperServer; public class QuorumBean implements QuorumMXBean, ZKMBeanInfo { private final QuorumPeer peer; private final String name; public QuorumBean(QuorumPeer peer) { this.peer = peer; name = "ReplicatedServer_id" + peer.getMyId(); } @Override public String getName() { return name; } @Override public boolean isHidden() { return false; } @Override public int getQuorumSize() { return peer.getQuorumSize(); } public int getSyncLimit() { return peer.getSyncLimit(); } public int getInitLimit() { return peer.getInitLimit(); } public void setInitLimit(int initLimit) { peer.setInitLimit(initLimit); } public void setSyncLimit(int syncLimit) { peer.setSyncLimit(syncLimit); } @Override public boolean isSslQuorum() { return peer.isSslQuorum(); } @Override public boolean isPortUnification() { return peer.shouldUsePortUnification(); } @Override public long getObserverElectionDelayMS() { return Observer.getObserverElectionDelayMs(); } @Override public void setObserverElectionDelayMS(long delayMS) { Observer.setObserverElectionDelayMs(delayMS); } @Override public boolean getDigestEnabled() { return ZooKeeperServer.isDigestEnabled(); } @Override public void disableDigest() { ZooKeeperServer.setDigestEnabled(false); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000170 15051152474 032715 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumCnxMa0100644 0000000 0000000 00000157044 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.channels.UnresolvedAddressException; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.net.ssl.SSLSocket; import org.apache.zookeeper.common.NetUtils; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner; import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ConfigUtils; import org.apache.zookeeper.util.CircularBlockingQueue; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class implements a connection manager for leader election using TCP. It * maintains one connection for every pair of servers. The tricky part is to * guarantee that there is exactly one connection for every pair of servers that * are operating correctly and that can communicate over the network. * * If two servers try to start a connection concurrently, then the connection * manager uses a very simple tie-breaking mechanism to decide which connection * to drop based on the IP addressed of the two parties. * * For every peer, the manager maintains a queue of messages to send. If the * connection to any particular peer drops, then the sender thread puts the * message back on the list. As this implementation currently uses a queue * implementation to maintain messages to send to another peer, we add the * message to the tail of the queue, thus changing the order of messages. * Although this is not a problem for the leader election, it could be a problem * when consolidating peer communication. This is to be verified, though. * */ public class QuorumCnxManager { private static final Logger LOG = LoggerFactory.getLogger(QuorumCnxManager.class); /* * Maximum capacity of thread queues */ static final int RECV_CAPACITY = 100; // Initialized to 1 to prevent sending // stale notifications to peers static final int SEND_CAPACITY = 1; static final int PACKETMAXSIZE = 1024 * 512; /* * Negative counter for observer server ids. */ private AtomicLong observerCounter = new AtomicLong(-1); /* * Protocol identifier used among peers (must be a negative number for backward compatibility reasons) */ // the following protocol version was sent in every connection initiation message since ZOOKEEPER-107 released in 3.5.0 public static final long PROTOCOL_VERSION_V1 = -65536L; // ZOOKEEPER-3188 introduced multiple addresses in the connection initiation message, released in 3.6.0 public static final long PROTOCOL_VERSION_V2 = -65535L; /* * Max buffer size to be read from the network. */ public static final int maxBuffer = 2048; /* * Connection time out value in milliseconds */ private int cnxTO = 5000; final QuorumPeer self; /* * Local IP address */ final long mySid; final int socketTimeout; final Map view; final boolean listenOnAllIPs; private ThreadPoolExecutor connectionExecutor; private final Set inprogressConnections = Collections.synchronizedSet(new HashSet<>()); private QuorumAuthServer authServer; private QuorumAuthLearner authLearner; private boolean quorumSaslAuthEnabled; /* * Counter to count connection processing threads. */ private AtomicInteger connectionThreadCnt = new AtomicInteger(0); /* * Mapping from Peer to Thread number */ final ConcurrentHashMap senderWorkerMap; final ConcurrentHashMap> queueSendMap; final ConcurrentHashMap lastMessageSent; /* * Reception queue */ public final BlockingQueue recvQueue; /* * Shutdown flag */ volatile boolean shutdown = false; /* * Listener thread */ public final Listener listener; /* * Counter to count worker threads */ private AtomicInteger threadCnt = new AtomicInteger(0); /* * Socket options for TCP keepalive */ private final boolean tcpKeepAlive = Boolean.getBoolean("zookeeper.tcpKeepAlive"); /* * Socket factory, allowing the injection of custom socket implementations for testing */ static final Supplier DEFAULT_SOCKET_FACTORY = () -> new Socket(); private static Supplier SOCKET_FACTORY = DEFAULT_SOCKET_FACTORY; static void setSocketFactory(Supplier factory) { SOCKET_FACTORY = factory; } public static class Message { Message(ByteBuffer buffer, long sid) { this.buffer = buffer; this.sid = sid; } ByteBuffer buffer; long sid; } /* * This class parses the initial identification sent out by peers with their * sid & hostname. */ public static class InitialMessage { public Long sid; public List electionAddr; InitialMessage(Long sid, List addresses) { this.sid = sid; this.electionAddr = addresses; } @SuppressWarnings("serial") public static class InitialMessageException extends Exception { InitialMessageException(String message, Object... args) { super(String.format(message, args)); } } public static InitialMessage parse(Long protocolVersion, DataInputStream din) throws InitialMessageException, IOException { Long sid; if (protocolVersion != PROTOCOL_VERSION_V1 && protocolVersion != PROTOCOL_VERSION_V2) { throw new InitialMessageException("Got unrecognized protocol version %s", protocolVersion); } sid = din.readLong(); int remaining = din.readInt(); if (remaining <= 0 || remaining > maxBuffer) { throw new InitialMessageException("Unreasonable buffer length: %s", remaining); } byte[] b = new byte[remaining]; int num_read = din.read(b); if (num_read != remaining) { throw new InitialMessageException("Read only %s bytes out of %s sent by server %s", num_read, remaining, sid); } // in PROTOCOL_VERSION_V1 we expect to get a single address here represented as a 'host:port' string // in PROTOCOL_VERSION_V2 we expect to get multiple addresses like: 'host1:port1|host2:port2|...' String[] addressStrings = new String(b, UTF_8).split("\\|"); List addresses = new ArrayList<>(addressStrings.length); for (String addr : addressStrings) { String[] host_port; try { host_port = ConfigUtils.getHostAndPort(addr); } catch (ConfigException e) { throw new InitialMessageException("Badly formed address: %s", addr); } if (host_port.length != 2) { throw new InitialMessageException("Badly formed address: %s", addr); } int port; try { port = Integer.parseInt(host_port[1]); } catch (NumberFormatException e) { throw new InitialMessageException("Bad port number: %s", host_port[1]); } catch (ArrayIndexOutOfBoundsException e) { throw new InitialMessageException("No port number in: %s", addr); } if (!isWildcardAddress(host_port[0])) { addresses.add(new InetSocketAddress(host_port[0], port)); } } return new InitialMessage(sid, addresses); } /** * Returns true if the specified hostname is a wildcard address, * like 0.0.0.0 for IPv4 or :: for IPv6 * * (the function is package-private to be visible for testing) */ static boolean isWildcardAddress(final String hostname) { try { return InetAddress.getByName(hostname).isAnyLocalAddress(); } catch (UnknownHostException e) { // if we can not resolve, it can not be a wildcard address return false; } } @Override public String toString() { return "InitialMessage{sid=" + sid + ", electionAddr=" + electionAddr + '}'; } } public QuorumCnxManager(QuorumPeer self, final long mySid, Map view, QuorumAuthServer authServer, QuorumAuthLearner authLearner, int socketTimeout, boolean listenOnAllIPs, int quorumCnxnThreadsSize, boolean quorumSaslAuthEnabled) { this.recvQueue = new CircularBlockingQueue<>(RECV_CAPACITY); this.queueSendMap = new ConcurrentHashMap<>(); this.senderWorkerMap = new ConcurrentHashMap<>(); this.lastMessageSent = new ConcurrentHashMap<>(); String cnxToValue = System.getProperty("zookeeper.cnxTimeout"); if (cnxToValue != null) { this.cnxTO = Integer.parseInt(cnxToValue); } this.self = self; this.mySid = mySid; this.socketTimeout = socketTimeout; this.view = view; this.listenOnAllIPs = listenOnAllIPs; this.authServer = authServer; this.authLearner = authLearner; this.quorumSaslAuthEnabled = quorumSaslAuthEnabled; initializeConnectionExecutor(mySid, quorumCnxnThreadsSize); // Starts listener thread that waits for connection requests listener = new Listener(); listener.setName("QuorumPeerListener"); } // we always use the Connection Executor during connection initiation (to handle connection // timeouts), and optionally use it during receiving connections (as the Quorum SASL authentication // can take extra time) private void initializeConnectionExecutor(final long mySid, final int quorumCnxnThreadsSize) { final AtomicInteger threadIndex = new AtomicInteger(1); SecurityManager s = System.getSecurityManager(); final ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); final ThreadFactory daemonThFactory = runnable -> new Thread(group, runnable, String.format("QuorumConnectionThread-[myid=%d]-%d", mySid, threadIndex.getAndIncrement())); this.connectionExecutor = new ThreadPoolExecutor(3, quorumCnxnThreadsSize, 60, TimeUnit.SECONDS, new SynchronousQueue<>(), daemonThFactory); this.connectionExecutor.allowCoreThreadTimeOut(true); } /** * Invokes initiateConnection for testing purposes * * @param sid */ public void testInitiateConnection(long sid) { LOG.debug("Opening channel to server {}", sid); initiateConnection(self.getVotingView().get(sid).electionAddr, sid); } /** * First we create the socket, perform SSL handshake and authentication if needed. * Then we perform the initiation protocol. * If this server has initiated the connection, then it gives up on the * connection if it loses challenge. Otherwise, it keeps the connection. */ public void initiateConnection(final MultipleAddresses electionAddr, final Long sid) { Socket sock = null; try { LOG.debug("Opening channel to server {}", sid); if (self.isSslQuorum()) { sock = self.getX509Util().createSSLSocket(); } else { sock = SOCKET_FACTORY.get(); } setSockOpts(sock); sock.connect(electionAddr.getReachableOrOne(), cnxTO); if (sock instanceof SSLSocket) { SSLSocket sslSock = (SSLSocket) sock; sslSock.startHandshake(); LOG.info("SSL handshake complete with {} - {} - {}", sslSock.getRemoteSocketAddress(), sslSock.getSession().getProtocol(), sslSock.getSession().getCipherSuite()); } LOG.debug("Connected to server {} using election address: {}:{}", sid, sock.getInetAddress(), sock.getPort()); } catch (X509Exception e) { LOG.warn("Cannot open secure channel to {} at election address {}", sid, electionAddr, e); closeSocket(sock); return; } catch (UnresolvedAddressException | IOException e) { LOG.warn("Cannot open channel to {} at election address {}", sid, electionAddr, e); closeSocket(sock); return; } try { startConnection(sock, sid); } catch (IOException e) { LOG.error( "Exception while connecting, id: {}, addr: {}, closing learner connection", sid, sock.getRemoteSocketAddress(), e); closeSocket(sock); } } /** * Server will initiate the connection request to its peer server * asynchronously via separate connection thread. */ public boolean initiateConnectionAsync(final MultipleAddresses electionAddr, final Long sid) { if (!inprogressConnections.add(sid)) { // simply return as there is a connection request to // server 'sid' already in progress. LOG.debug("Connection request to server id: {} is already in progress, so skipping this request", sid); return true; } try { connectionExecutor.execute(new QuorumConnectionReqThread(electionAddr, sid)); connectionThreadCnt.incrementAndGet(); } catch (Throwable e) { // Imp: Safer side catching all type of exceptions and remove 'sid' // from inprogress connections. This is to avoid blocking further // connection requests from this 'sid' in case of errors. inprogressConnections.remove(sid); LOG.error("Exception while submitting quorum connection request", e); return false; } return true; } /** * Thread to send connection request to peer server. */ private class QuorumConnectionReqThread extends ZooKeeperThread { final MultipleAddresses electionAddr; final Long sid; QuorumConnectionReqThread(final MultipleAddresses electionAddr, final Long sid) { super("QuorumConnectionReqThread-" + sid); this.electionAddr = electionAddr; this.sid = sid; } @Override public void run() { try { initiateConnection(electionAddr, sid); } finally { inprogressConnections.remove(sid); } } } private boolean startConnection(Socket sock, Long sid) throws IOException { DataOutputStream dout = null; DataInputStream din = null; LOG.debug("startConnection (myId:{} --> sid:{})", self.getMyId(), sid); try { // Use BufferedOutputStream to reduce the number of IP packets. This is // important for x-DC scenarios. BufferedOutputStream buf = new BufferedOutputStream(sock.getOutputStream()); dout = new DataOutputStream(buf); // Sending id and challenge // First sending the protocol version (in other words - message type). // For backward compatibility reasons we stick to the old protocol version, unless the MultiAddress // feature is enabled. During rolling upgrade, we must make sure that all the servers can // understand the protocol version we use to avoid multiple partitions. see ZOOKEEPER-3720 long protocolVersion = self.isMultiAddressEnabled() ? PROTOCOL_VERSION_V2 : PROTOCOL_VERSION_V1; dout.writeLong(protocolVersion); dout.writeLong(self.getMyId()); // now we send our election address. For the new protocol version, we can send multiple addresses. Collection addressesToSend = protocolVersion == PROTOCOL_VERSION_V2 ? self.getElectionAddress().getAllAddresses() : Arrays.asList(self.getElectionAddress().getOne()); String addr = addressesToSend.stream() .map(NetUtils::formatInetAddr).collect(Collectors.joining("|")); byte[] addr_bytes = addr.getBytes(UTF_8); dout.writeInt(addr_bytes.length); dout.write(addr_bytes); dout.flush(); din = new DataInputStream(new BufferedInputStream(sock.getInputStream())); } catch (IOException e) { LOG.warn("Ignoring exception reading or writing challenge: ", e); closeSocket(sock); return false; } // authenticate learner QuorumPeer.QuorumServer qps = self.getView().get(sid); if (qps != null) { // TODO - investigate why reconfig makes qps null. authLearner.authenticate(sock, qps.hostname); } // If lost the challenge, then drop the new connection if (sid > self.getMyId()) { LOG.info("Have smaller server identifier, so dropping the connection: (myId:{} --> sid:{})", self.getMyId(), sid); closeSocket(sock); // Otherwise proceed with the connection } else { LOG.debug("Have larger server identifier, so keeping the connection: (myId:{} --> sid:{})", self.getMyId(), sid); SendWorker sw = new SendWorker(sock, sid); RecvWorker rw = new RecvWorker(sock, din, sid, sw); sw.setRecv(rw); SendWorker vsw = senderWorkerMap.get(sid); if (vsw != null) { vsw.finish(); } senderWorkerMap.put(sid, sw); queueSendMap.putIfAbsent(sid, new CircularBlockingQueue<>(SEND_CAPACITY)); sw.start(); rw.start(); return true; } return false; } /** * If this server receives a connection request, then it gives up on the new * connection if it wins. Notice that it checks whether it has a connection * to this server already or not. If it does, then it sends the smallest * possible long value to lose the challenge. * */ public void receiveConnection(final Socket sock) { DataInputStream din = null; try { din = new DataInputStream(new BufferedInputStream(sock.getInputStream())); LOG.debug("Sync handling of connection request received from: {}", sock.getRemoteSocketAddress()); handleConnection(sock, din); } catch (IOException e) { LOG.error("Exception handling connection, addr: {}, closing server connection", sock.getRemoteSocketAddress()); LOG.debug("Exception details: ", e); closeSocket(sock); } } /** * Server receives a connection request and handles it asynchronously via * separate thread. */ public void receiveConnectionAsync(final Socket sock) { try { LOG.debug("Async handling of connection request received from: {}", sock.getRemoteSocketAddress()); connectionExecutor.execute(new QuorumConnectionReceiverThread(sock)); connectionThreadCnt.incrementAndGet(); } catch (Throwable e) { LOG.error("Exception handling connection, addr: {}, closing server connection", sock.getRemoteSocketAddress()); LOG.debug("Exception details: ", e); closeSocket(sock); } } /** * Thread to receive connection request from peer server. */ private class QuorumConnectionReceiverThread extends ZooKeeperThread { private final Socket sock; QuorumConnectionReceiverThread(final Socket sock) { super("QuorumConnectionReceiverThread-" + sock.getRemoteSocketAddress()); this.sock = sock; } @Override public void run() { receiveConnection(sock); } } private void handleConnection(Socket sock, DataInputStream din) throws IOException { Long sid = null, protocolVersion = null; MultipleAddresses electionAddr = null; try { protocolVersion = din.readLong(); if (protocolVersion >= 0) { // this is a server id and not a protocol version sid = protocolVersion; } else { try { InitialMessage init = InitialMessage.parse(protocolVersion, din); sid = init.sid; if (!init.electionAddr.isEmpty()) { electionAddr = new MultipleAddresses(init.electionAddr, Duration.ofMillis(self.getMultiAddressReachabilityCheckTimeoutMs())); } LOG.debug("Initial message parsed by {}: {}", self.getMyId(), init.toString()); } catch (InitialMessage.InitialMessageException ex) { LOG.error("Initial message parsing error!", ex); closeSocket(sock); return; } } if (sid == QuorumPeer.OBSERVER_ID) { /* * Choose identifier at random. We need a value to identify * the connection. */ sid = observerCounter.getAndDecrement(); LOG.info("Setting arbitrary identifier to observer: {}", sid); } } catch (IOException e) { LOG.warn("Exception reading or writing challenge", e); closeSocket(sock); return; } // do authenticating learner authServer.authenticate(sock, din); //If wins the challenge, then close the new connection. if (sid < self.getMyId()) { /* * This replica might still believe that the connection to sid is * up, so we have to shut down the workers before trying to open a * new connection. */ SendWorker sw = senderWorkerMap.get(sid); if (sw != null) { sw.finish(); } /* * Now we start a new connection */ LOG.debug("Create new connection to server: {}", sid); closeSocket(sock); if (electionAddr != null) { connectOne(sid, electionAddr); } else { connectOne(sid); } } else if (sid == self.getMyId()) { // we saw this case in ZOOKEEPER-2164 LOG.warn("We got a connection request from a server with our own ID. " + "This should be either a configuration error, or a bug."); } else { // Otherwise start worker threads to receive data. SendWorker sw = new SendWorker(sock, sid); RecvWorker rw = new RecvWorker(sock, din, sid, sw); sw.setRecv(rw); SendWorker vsw = senderWorkerMap.get(sid); if (vsw != null) { vsw.finish(); } senderWorkerMap.put(sid, sw); queueSendMap.putIfAbsent(sid, new CircularBlockingQueue<>(SEND_CAPACITY)); sw.start(); rw.start(); } } /** * Processes invoke this message to queue a message to send. Currently, * only leader election uses it. */ public void toSend(Long sid, ByteBuffer b) { /* * If sending message to myself, then simply enqueue it (loopback). */ if (this.mySid == sid) { b.position(0); addToRecvQueue(new Message(b.duplicate(), sid)); /* * Otherwise send to the corresponding thread to send. */ } else { /* * Start a new connection if doesn't have one already. */ BlockingQueue bq = queueSendMap.computeIfAbsent(sid, serverId -> new CircularBlockingQueue<>(SEND_CAPACITY)); addToSendQueue(bq, b); connectOne(sid); } } /** * Try to establish a connection to server with id sid using its electionAddr. * The function will return quickly and the connection will be established asynchronously. * * VisibleForTesting. * * @param sid server id * @return boolean success indication */ synchronized boolean connectOne(long sid, MultipleAddresses electionAddr) { if (senderWorkerMap.get(sid) != null) { LOG.debug("There is a connection already for server {}", sid); if (self.isMultiAddressEnabled() && electionAddr.size() > 1 && self.isMultiAddressReachabilityCheckEnabled()) { // since ZOOKEEPER-3188 we can use multiple election addresses to reach a server. It is possible, that the // one we are using is already dead and we need to clean-up, so when we will create a new connection // then we will choose an other one, which is actually reachable senderWorkerMap.get(sid).asyncValidateIfSocketIsStillReachable(); } return true; } // we are doing connection initiation always asynchronously, since it is possible that // the socket connection timeouts or the SSL handshake takes too long and don't want // to keep the rest of the connections to wait return initiateConnectionAsync(electionAddr, sid); } /** * Try to establish a connection to server with id sid. * The function will return quickly and the connection will be established asynchronously. * * @param sid server id */ synchronized void connectOne(long sid) { if (senderWorkerMap.get(sid) != null) { LOG.debug("There is a connection already for server {}", sid); if (self.isMultiAddressEnabled() && self.isMultiAddressReachabilityCheckEnabled()) { // since ZOOKEEPER-3188 we can use multiple election addresses to reach a server. It is possible, that the // one we are using is already dead and we need to clean-up, so when we will create a new connection // then we will choose an other one, which is actually reachable senderWorkerMap.get(sid).asyncValidateIfSocketIsStillReachable(); } return; } synchronized (self.QV_LOCK) { boolean knownId = false; // Resolve hostname for the remote server before attempting to // connect in case the underlying ip address has changed. self.recreateSocketAddresses(sid); Map lastCommittedView = self.getView(); QuorumVerifier lastSeenQV = self.getLastSeenQuorumVerifier(); Map lastProposedView = lastSeenQV.getAllMembers(); if (lastCommittedView.containsKey(sid)) { knownId = true; LOG.debug("Server {} knows {} already, it is in the lastCommittedView", self.getMyId(), sid); if (connectOne(sid, lastCommittedView.get(sid).electionAddr)) { return; } } if (lastSeenQV != null && lastProposedView.containsKey(sid) && (!knownId || !lastProposedView.get(sid).electionAddr.equals(lastCommittedView.get(sid).electionAddr))) { knownId = true; LOG.debug("Server {} knows {} already, it is in the lastProposedView", self.getMyId(), sid); if (connectOne(sid, lastProposedView.get(sid).electionAddr)) { return; } } if (!knownId) { LOG.warn("Invalid server id: {} ", sid); } } } /** * Try to establish a connection with each server if one * doesn't exist. */ public void connectAll() { long sid; for (Enumeration en = queueSendMap.keys(); en.hasMoreElements(); ) { sid = en.nextElement(); connectOne(sid); } } /** * Check if all queues are empty, indicating that all messages have been delivered. */ boolean haveDelivered() { for (BlockingQueue queue : queueSendMap.values()) { final int queueSize = queue.size(); LOG.debug("Queue size: {}", queueSize); if (queueSize == 0) { return true; } } return false; } /** * Flag that it is time to wrap up all activities and interrupt the listener. */ public void halt() { shutdown = true; LOG.debug("Halting listener"); listener.halt(); // Wait for the listener to terminate. try { listener.join(); } catch (InterruptedException ex) { LOG.warn("Got interrupted before joining the listener", ex); } softHalt(); // clear data structures used for auth if (connectionExecutor != null) { connectionExecutor.shutdown(); } inprogressConnections.clear(); resetConnectionThreadCount(); } /** * A soft halt simply finishes workers. */ public void softHalt() { for (SendWorker sw : senderWorkerMap.values()) { LOG.debug("Server {} is soft-halting sender towards: {}", self.getMyId(), sw); sw.finish(); } } /** * Helper method to set socket options. * * @param sock * Reference to socket */ private void setSockOpts(Socket sock) throws SocketException { sock.setTcpNoDelay(true); sock.setKeepAlive(tcpKeepAlive); sock.setSoTimeout(this.socketTimeout); } /** * Helper method to close a socket. * * @param sock * Reference to socket */ private void closeSocket(Socket sock) { if (sock == null) { return; } try { sock.close(); } catch (IOException ie) { LOG.error("Exception while closing", ie); } } /** * Return number of worker threads */ public long getThreadCount() { return threadCnt.get(); } /** * Return number of connection processing threads. */ public long getConnectionThreadCount() { return connectionThreadCnt.get(); } /** * Reset the value of connection processing threads count to zero. */ private void resetConnectionThreadCount() { connectionThreadCnt.set(0); } /** * Thread to listen on some ports */ public class Listener extends ZooKeeperThread { private static final String ELECTION_PORT_BIND_RETRY = "zookeeper.electionPortBindRetry"; private static final int DEFAULT_PORT_BIND_MAX_RETRY = 3; private final int portBindMaxRetry; private Runnable socketBindErrorHandler = () -> ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_BIND_QUORUM_PORT.getValue()); private List listenerHandlers; private final AtomicBoolean socketException; public Listener() { // During startup of thread, thread name will be overridden to // specific election address super("ListenerThread"); socketException = new AtomicBoolean(false); // maximum retry count while trying to bind to election port // see ZOOKEEPER-3320 for more details final Integer maxRetry = Integer.getInteger(ELECTION_PORT_BIND_RETRY, DEFAULT_PORT_BIND_MAX_RETRY); if (maxRetry >= 0) { LOG.info("Election port bind maximum retries is {}", maxRetry == 0 ? "infinite" : maxRetry); portBindMaxRetry = maxRetry; } else { LOG.info( "'{}' contains invalid value: {}(must be >= 0). Use default value of {} instead.", ELECTION_PORT_BIND_RETRY, maxRetry, DEFAULT_PORT_BIND_MAX_RETRY); portBindMaxRetry = DEFAULT_PORT_BIND_MAX_RETRY; } } /** * Change socket bind error handler. Used for testing. */ void setSocketBindErrorHandler(Runnable errorHandler) { this.socketBindErrorHandler = errorHandler; } @Override public void run() { if (!shutdown) { LOG.debug("Listener thread started, myId: {}", self.getMyId()); Set addresses; if (self.getQuorumListenOnAllIPs()) { addresses = self.getElectionAddress().getWildcardAddresses(); } else { addresses = self.getElectionAddress().getAllAddresses(); } CountDownLatch latch = new CountDownLatch(addresses.size()); listenerHandlers = addresses.stream().map(address -> new ListenerHandler(address, self.shouldUsePortUnification(), self.isSslQuorum(), latch)) .collect(Collectors.toList()); final ExecutorService executor = Executors.newFixedThreadPool(addresses.size()); try { listenerHandlers.forEach(executor::submit); } finally { // prevent executor's threads to leak after ListenerHandler tasks complete executor.shutdown(); } try { latch.await(); } catch (InterruptedException ie) { LOG.error("Interrupted while sleeping. Ignoring exception", ie); } finally { // Clean up for shutdown. for (ListenerHandler handler : listenerHandlers) { try { handler.close(); } catch (IOException ie) { // Don't log an error for shutdown. LOG.debug("Error closing server socket", ie); } } } } LOG.info("Leaving listener"); if (!shutdown) { LOG.error( "As I'm leaving the listener thread, I won't be able to participate in leader election any longer: {}", self.getElectionAddress().getAllAddresses().stream() .map(NetUtils::formatInetAddr) .collect(Collectors.joining("|"))); if (socketException.get()) { // After leaving listener thread, the host cannot join the quorum anymore, // this is a severe error that we cannot recover from, so we need to exit socketBindErrorHandler.run(); } } } /** * Halts this listener thread. */ void halt() { LOG.debug("Halt called: Trying to close listeners"); if (listenerHandlers != null) { LOG.debug("Closing listener: {}", QuorumCnxManager.this.mySid); for (ListenerHandler handler : listenerHandlers) { try { handler.close(); } catch (IOException e) { LOG.warn("Exception when shutting down listener: ", e); } } } } class ListenerHandler implements Runnable, Closeable { private ServerSocket serverSocket; private InetSocketAddress address; private boolean portUnification; private boolean sslQuorum; private CountDownLatch latch; ListenerHandler(InetSocketAddress address, boolean portUnification, boolean sslQuorum, CountDownLatch latch) { this.address = address; this.portUnification = portUnification; this.sslQuorum = sslQuorum; this.latch = latch; } /** * Sleeps on acceptConnections(). */ @Override public void run() { try { Thread.currentThread().setName("ListenerHandler-" + address); acceptConnections(); try { close(); } catch (IOException e) { LOG.warn("Exception when shutting down listener: ", e); } } catch (Exception e) { // Output of unexpected exception, should never happen LOG.error("Unexpected error ", e); } finally { latch.countDown(); } } @Override public synchronized void close() throws IOException { if (serverSocket != null && !serverSocket.isClosed()) { LOG.debug("Trying to close listeners: {}", serverSocket); serverSocket.close(); } } /** * Sleeps on accept(). */ private void acceptConnections() { int numRetries = 0; Socket client = null; while ((!shutdown) && (portBindMaxRetry == 0 || numRetries < portBindMaxRetry)) { try { serverSocket = createNewServerSocket(); LOG.info("{} is accepting connections now, my election bind port: {}", QuorumCnxManager.this.mySid, address.toString()); while (!shutdown) { try { client = serverSocket.accept(); setSockOpts(client); LOG.info("Received connection request from {}", client.getRemoteSocketAddress()); // Receive and handle the connection request // asynchronously if the quorum sasl authentication is // enabled. This is required because sasl server // authentication process may take few seconds to finish, // this may delay next peer connection requests. if (quorumSaslAuthEnabled) { receiveConnectionAsync(client); } else { receiveConnection(client); } numRetries = 0; } catch (SocketTimeoutException e) { LOG.warn("The socket is listening for the election accepted " + "and it timed out unexpectedly, but will retry." + "see ZOOKEEPER-2836"); } } } catch (IOException e) { if (shutdown) { break; } LOG.error("Exception while listening to address {}", address, e); if (e instanceof SocketException) { socketException.set(true); } numRetries++; try { close(); Thread.sleep(1000); } catch (IOException ie) { LOG.error("Error closing server socket", ie); } catch (InterruptedException ie) { LOG.error("Interrupted while sleeping. Ignoring exception", ie); } closeSocket(client); } } if (!shutdown) { LOG.error( "Leaving listener thread for address {} after {} errors. Use {} property to increase retry count.", formatInetAddr(address), numRetries, ELECTION_PORT_BIND_RETRY); } } private ServerSocket createNewServerSocket() throws IOException { ServerSocket socket; if (portUnification) { LOG.info("Creating TLS-enabled quorum server socket"); socket = new UnifiedServerSocket(self.getX509Util(), true); } else if (sslQuorum) { LOG.info("Creating TLS-only quorum server socket"); socket = new UnifiedServerSocket(self.getX509Util(), false); } else { socket = new ServerSocket(); } socket.setReuseAddress(true); address = new InetSocketAddress(address.getHostString(), address.getPort()); socket.bind(address); return socket; } } } /** * Thread to send messages. Instance waits on a queue, and send a message as * soon as there is one available. If connection breaks, then opens a new * one. */ class SendWorker extends ZooKeeperThread { Long sid; Socket sock; RecvWorker recvWorker; volatile boolean running = true; DataOutputStream dout; AtomicBoolean ongoingAsyncValidation = new AtomicBoolean(false); /** * An instance of this thread receives messages to send * through a queue and sends them to the server sid. * * @param sock * Socket to remote peer * @param sid * Server identifier of remote peer */ SendWorker(Socket sock, Long sid) { super("SendWorker:" + sid); this.sid = sid; this.sock = sock; recvWorker = null; try { dout = new DataOutputStream(sock.getOutputStream()); } catch (IOException e) { LOG.error("Unable to access socket output stream", e); closeSocket(sock); running = false; } LOG.debug("Address of remote peer: {}", this.sid); } synchronized void setRecv(RecvWorker recvWorker) { this.recvWorker = recvWorker; } /** * Returns RecvWorker that pairs up with this SendWorker. * * @return RecvWorker */ synchronized RecvWorker getRecvWorker() { return recvWorker; } synchronized boolean finish() { LOG.debug("Calling SendWorker.finish for {}", sid); if (!running) { /* * Avoids running finish() twice. */ return running; } running = false; closeSocket(sock); this.interrupt(); if (recvWorker != null) { recvWorker.finish(); } LOG.debug("Removing entry from senderWorkerMap sid={}", sid); senderWorkerMap.remove(sid, this); threadCnt.decrementAndGet(); return running; } synchronized void send(ByteBuffer b) throws IOException { byte[] msgBytes = new byte[b.capacity()]; try { b.position(0); b.get(msgBytes); } catch (BufferUnderflowException be) { LOG.error("BufferUnderflowException ", be); return; } dout.writeInt(b.capacity()); dout.write(b.array()); dout.flush(); } @Override public void run() { threadCnt.incrementAndGet(); try { /** * If there is nothing in the queue to send, then we * send the lastMessage to ensure that the last message * was received by the peer. The message could be dropped * in case self or the peer shutdown their connection * (and exit the thread) prior to reading/processing * the last message. Duplicate messages are handled correctly * by the peer. * * If the send queue is non-empty, then we have a recent * message than that stored in lastMessage. To avoid sending * stale message, we should send the message in the send queue. */ BlockingQueue bq = queueSendMap.get(sid); if (bq == null || isSendQueueEmpty(bq)) { ByteBuffer b = lastMessageSent.get(sid); if (b != null) { LOG.debug("Attempting to send lastMessage to sid={}", sid); send(b); } } } catch (IOException e) { LOG.error("Failed to send last message. Shutting down thread.", e); this.finish(); } LOG.debug("SendWorker thread started towards {}. myId: {}", sid, QuorumCnxManager.this.mySid); try { while (running && !shutdown && sock != null) { ByteBuffer b = null; try { BlockingQueue bq = queueSendMap.get(sid); if (bq != null) { b = pollSendQueue(bq, 1000, TimeUnit.MILLISECONDS); } else { LOG.error("No queue of incoming messages for server {}", sid); break; } if (b != null) { lastMessageSent.put(sid, b); send(b); } } catch (InterruptedException e) { LOG.warn("Interrupted while waiting for message on queue", e); } } } catch (Exception e) { LOG.warn( "Exception when using channel: for id {} my id = {}", sid , QuorumCnxManager.this.mySid, e); } this.finish(); LOG.warn("Send worker leaving thread id {} my id = {}", sid, self.getMyId()); } public void asyncValidateIfSocketIsStillReachable() { if (ongoingAsyncValidation.compareAndSet(false, true)) { new Thread(() -> { LOG.debug("validate if destination address is reachable for sid {}", sid); if (sock != null) { InetAddress address = sock.getInetAddress(); try { if (address.isReachable(500)) { LOG.debug("destination address {} is reachable for sid {}", address.toString(), sid); ongoingAsyncValidation.set(false); return; } } catch (NullPointerException | IOException ignored) { } LOG.warn( "destination address {} not reachable anymore, shutting down the SendWorker for sid {}", address.toString(), sid); this.finish(); } }).start(); } else { LOG.debug("validation of destination address for sid {} is skipped (it is already running)", sid); } } } /** * Thread to receive messages. Instance waits on a socket read. If the * channel breaks, then removes itself from the pool of receivers. */ class RecvWorker extends ZooKeeperThread { Long sid; Socket sock; volatile boolean running = true; final DataInputStream din; final SendWorker sw; RecvWorker(Socket sock, DataInputStream din, Long sid, SendWorker sw) { super("RecvWorker:" + sid); this.sid = sid; this.sock = sock; this.sw = sw; this.din = din; try { // OK to wait until socket disconnects while reading. sock.setSoTimeout(0); } catch (IOException e) { LOG.error("Error while accessing socket for {}", sid, e); closeSocket(sock); running = false; } } /** * Shuts down this worker * * @return boolean Value of variable running */ synchronized boolean finish() { LOG.debug("RecvWorker.finish called. sid: {}. myId: {}", sid, QuorumCnxManager.this.mySid); if (!running) { /* * Avoids running finish() twice. */ return running; } running = false; this.interrupt(); threadCnt.decrementAndGet(); return running; } @Override public void run() { threadCnt.incrementAndGet(); try { LOG.debug("RecvWorker thread towards {} started. myId: {}", sid, QuorumCnxManager.this.mySid); while (running && !shutdown && sock != null) { /** * Reads the first int to determine the length of the * message */ int length = din.readInt(); if (length <= 0 || length > PACKETMAXSIZE) { throw new IOException("Received packet with invalid packet: " + length); } /** * Allocates a new ByteBuffer to receive the message */ final byte[] msgArray = new byte[length]; din.readFully(msgArray, 0, length); addToRecvQueue(new Message(ByteBuffer.wrap(msgArray), sid)); } } catch (Exception e) { LOG.warn( "Connection broken for id {}, my id = {}", sid, QuorumCnxManager.this.mySid, e); } finally { LOG.warn("Interrupting SendWorker thread from RecvWorker. sid: {}. myId: {}", sid, QuorumCnxManager.this.mySid); sw.finish(); closeSocket(sock); } } } /** * Inserts an element in the provided {@link BlockingQueue}. This method * assumes that if the Queue is full, an element from the head of the Queue is * removed and the new item is inserted at the tail of the queue. This is done * to prevent a thread from blocking while inserting an element in the queue. * * @param queue Reference to the Queue * @param buffer Reference to the buffer to be inserted in the queue */ private void addToSendQueue(final BlockingQueue queue, final ByteBuffer buffer) { final boolean success = queue.offer(buffer); if (!success) { throw new RuntimeException("Could not insert into receive queue"); } } /** * Returns true if queue is empty. * @param queue * Reference to the queue * @return * true if the specified queue is empty */ private boolean isSendQueueEmpty(final BlockingQueue queue) { return queue.isEmpty(); } /** * Retrieves and removes buffer at the head of this queue, * waiting up to the specified wait time if necessary for an element to * become available. * * {@link BlockingQueue#poll(long, java.util.concurrent.TimeUnit)} */ private ByteBuffer pollSendQueue(final BlockingQueue queue, final long timeout, final TimeUnit unit) throws InterruptedException { return queue.poll(timeout, unit); } /** * Inserts an element in the {@link #recvQueue}. If the Queue is full, this * methods removes an element from the head of the Queue and then inserts the * element at the tail of the queue. * * @param msg Reference to the message to be inserted in the queue */ public void addToRecvQueue(final Message msg) { final boolean success = this.recvQueue.offer(msg); if (!success) { throw new RuntimeException("Could not insert into receive queue"); } } /** * Retrieves and removes a message at the head of this queue, * waiting up to the specified wait time if necessary for an element to * become available. * * {@link BlockingQueue#poll(long, java.util.concurrent.TimeUnit)} */ public Message pollRecvQueue(final long timeout, final TimeUnit unit) throws InterruptedException { return this.recvQueue.poll(timeout, unit); } public boolean connectedToPeer(long peerSid) { return senderWorkerMap.get(peerSid) != null; } public boolean isReconfigEnabled() { return self.isReconfigEnabled(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032720 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumMXBea0100644 0000000 0000000 00000004332 15051152474 034222 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * An MBean representing a zookeeper cluster nodes (aka quorum peers) */ public interface QuorumMXBean { /** * @return the name of the quorum */ String getName(); /** * @return configured number of peers in the quorum */ int getQuorumSize(); /** * @return the number of ticks that the initial synchronization phase can take */ int getInitLimit(); /** * @return the number of ticks that can pass between sending a request and getting an acknowledgment */ int getSyncLimit(); /** * @param initLimit the number of ticks that the initial synchronization phase can take */ void setInitLimit(int initLimit); /** * @param syncLimit the number of ticks that can pass between sending a request and getting an acknowledgment */ void setSyncLimit(int syncLimit); /** * @return SSL communication between quorum members required */ boolean isSslQuorum(); /** * @return SSL communication between quorum members enabled */ boolean isPortUnification(); /** * @return Observer Leader Election Reconnect Delay time in MS */ long getObserverElectionDelayMS(); /** * Set the Observer Leader Election Reconnect Delay time in MS */ void setObserverElectionDelayMS(long delayMS); boolean getDigestEnabled(); void disableDigest(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000162 15051152474 032716 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeer.0100644 0000000 0000000 00000305451 15051152474 034245 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import javax.security.sasl.SaslException; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.KeeperException.BadArgumentsException; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.apache.zookeeper.common.AtomicFileWritingIdiom; import org.apache.zookeeper.common.AtomicFileWritingIdiom.WriterStatement; import org.apache.zookeeper.common.QuorumX509Util; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperThread; import org.apache.zookeeper.server.admin.AdminServer; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.admin.AdminServerFactory; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthLearner; import org.apache.zookeeper.server.quorum.auth.NullQuorumAuthServer; import org.apache.zookeeper.server.quorum.auth.QuorumAuth; import org.apache.zookeeper.server.quorum.auth.QuorumAuthLearner; import org.apache.zookeeper.server.quorum.auth.QuorumAuthServer; import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthLearner; import org.apache.zookeeper.server.quorum.auth.SaslQuorumAuthServer; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumOracleMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.ConfigUtils; import org.apache.zookeeper.server.util.JvmPauseMonitor; import org.apache.zookeeper.server.util.ZxidUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class manages the quorum protocol. There are three states this server * can be in: *
      *
    1. Leader election - each server will elect a leader (proposing itself as a * leader initially).
    2. *
    3. Follower - the server will synchronize with the leader and replicate any * transactions.
    4. *
    5. Leader - the server will process requests and forward them to followers. * A majority of followers must log the request before it can be accepted. *
    * * This class will setup a datagram socket that will always respond with its * view of the current leader. The response will take the form of: * *
     * int xid;
     *
     * long myid;
     *
     * long leader_id;
     *
     * long leader_zxid;
     * 
    * * The request for the current leader will consist solely of an xid: int xid; */ public class QuorumPeer extends ZooKeeperThread implements QuorumStats.Provider { private static final Logger LOG = LoggerFactory.getLogger(QuorumPeer.class); public static final String CONFIG_KEY_KERBEROS_CANONICALIZE_HOST_NAMES = "zookeeper.kerberos.canonicalizeHostNames"; public static final String CONFIG_DEFAULT_KERBEROS_CANONICALIZE_HOST_NAMES = "false"; private QuorumBean jmxQuorumBean; LocalPeerBean jmxLocalPeerBean; private Map jmxRemotePeerBean; LeaderElectionBean jmxLeaderElectionBean; // The QuorumCnxManager is held through an AtomicReference to ensure cross-thread visibility // of updates; see the implementation comment at setLastSeenQuorumVerifier(). private AtomicReference qcmRef = new AtomicReference<>(); QuorumAuthServer authServer; QuorumAuthLearner authLearner; /** * ZKDatabase is a top level member of quorumpeer * which will be used in all the zookeeperservers * instantiated later. Also, it is created once on * bootup and only thrown away in case of a truncate * message from the leader */ private ZKDatabase zkDb; private JvmPauseMonitor jvmPauseMonitor; private final AtomicBoolean suspended = new AtomicBoolean(false); public static final class AddressTuple { public final MultipleAddresses quorumAddr; public final MultipleAddresses electionAddr; public final InetSocketAddress clientAddr; public AddressTuple(MultipleAddresses quorumAddr, MultipleAddresses electionAddr, InetSocketAddress clientAddr) { this.quorumAddr = quorumAddr; this.electionAddr = electionAddr; this.clientAddr = clientAddr; } } private int observerMasterPort; public int getObserverMasterPort() { return observerMasterPort; } public void setObserverMasterPort(int observerMasterPort) { this.observerMasterPort = observerMasterPort; } public static final String CONFIG_KEY_MULTI_ADDRESS_ENABLED = "zookeeper.multiAddress.enabled"; public static final String CONFIG_DEFAULT_MULTI_ADDRESS_ENABLED = "false"; private boolean multiAddressEnabled = true; public boolean isMultiAddressEnabled() { return multiAddressEnabled; } public void setMultiAddressEnabled(boolean multiAddressEnabled) { this.multiAddressEnabled = multiAddressEnabled; LOG.info("multiAddress.enabled set to {}", multiAddressEnabled); } public static final String CONFIG_KEY_MULTI_ADDRESS_REACHABILITY_CHECK_TIMEOUT_MS = "zookeeper.multiAddress.reachabilityCheckTimeoutMs"; private int multiAddressReachabilityCheckTimeoutMs = (int) MultipleAddresses.DEFAULT_TIMEOUT.toMillis(); public int getMultiAddressReachabilityCheckTimeoutMs() { return multiAddressReachabilityCheckTimeoutMs; } public void setMultiAddressReachabilityCheckTimeoutMs(int multiAddressReachabilityCheckTimeoutMs) { this.multiAddressReachabilityCheckTimeoutMs = multiAddressReachabilityCheckTimeoutMs; LOG.info("multiAddress.reachabilityCheckTimeoutMs set to {}", multiAddressReachabilityCheckTimeoutMs); } public static final String CONFIG_KEY_MULTI_ADDRESS_REACHABILITY_CHECK_ENABLED = "zookeeper.multiAddress.reachabilityCheckEnabled"; private boolean multiAddressReachabilityCheckEnabled = true; public boolean isMultiAddressReachabilityCheckEnabled() { return multiAddressReachabilityCheckEnabled; } public void setMultiAddressReachabilityCheckEnabled(boolean multiAddressReachabilityCheckEnabled) { this.multiAddressReachabilityCheckEnabled = multiAddressReachabilityCheckEnabled; LOG.info("multiAddress.reachabilityCheckEnabled set to {}", multiAddressReachabilityCheckEnabled); } public static class QuorumServer { public MultipleAddresses addr = new MultipleAddresses(); public MultipleAddresses electionAddr = new MultipleAddresses(); public InetSocketAddress clientAddr = null; public long id; public String hostname; public LearnerType type = LearnerType.PARTICIPANT; public boolean isClientAddrFromStatic = false; private List myAddrs; public QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr, InetSocketAddress clientAddr) { this(id, addr, electionAddr, clientAddr, LearnerType.PARTICIPANT); } public QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr) { this(id, addr, electionAddr, null, LearnerType.PARTICIPANT); } // VisibleForTesting public QuorumServer(long id, InetSocketAddress addr) { this(id, addr, null, null, LearnerType.PARTICIPANT); } public long getId() { return id; } /** * Performs a DNS lookup for server address and election address. * * If the DNS lookup fails, this.addr and electionAddr remain * unmodified. */ public void recreateSocketAddresses() { if (this.addr.isEmpty()) { LOG.warn("Server address has not been initialized"); return; } if (this.electionAddr.isEmpty()) { LOG.warn("Election address has not been initialized"); return; } this.addr.recreateSocketAddresses(); this.electionAddr.recreateSocketAddresses(); } private LearnerType getType(String s) throws ConfigException { switch (s.trim().toLowerCase()) { case "observer": return LearnerType.OBSERVER; case "participant": return LearnerType.PARTICIPANT; default: throw new ConfigException("Unrecognised peertype: " + s); } } public QuorumServer(long sid, String addressStr) throws ConfigException { this(sid, addressStr, QuorumServer::getInetAddress); } QuorumServer(long sid, String addressStr, Function getInetAddress) throws ConfigException { this.id = sid; initializeWithAddressString(addressStr, getInetAddress); } public QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr, LearnerType type) { this(id, addr, electionAddr, null, type); } public QuorumServer(long id, InetSocketAddress addr, InetSocketAddress electionAddr, InetSocketAddress clientAddr, LearnerType type) { this.id = id; if (addr != null) { this.addr.addAddress(addr); } if (electionAddr != null) { this.electionAddr.addAddress(electionAddr); } this.type = type; this.clientAddr = clientAddr; setMyAddrs(); } private static final String wrongFormat = " does not have the form server_config or server_config;client_config" + " where server_config is the pipe separated list of host:port:port or host:port:port:type" + " and client_config is port or host:port"; private void initializeWithAddressString(String addressStr, Function getInetAddress) throws ConfigException { LearnerType newType = null; String[] serverClientParts = addressStr.split(";"); String[] serverAddresses = serverClientParts[0].split("\\|"); if (serverClientParts.length == 2) { String[] clientParts = ConfigUtils.getHostAndPort(serverClientParts[1]); if (clientParts.length > 2) { throw new ConfigException(addressStr + wrongFormat); } // is client_config a host:port or just a port String clientHostName = (clientParts.length == 2) ? clientParts[0] : "0.0.0.0"; try { clientAddr = new InetSocketAddress(clientHostName, Integer.parseInt(clientParts[clientParts.length - 1])); } catch (NumberFormatException e) { throw new ConfigException("Address unresolved: " + hostname + ":" + clientParts[clientParts.length - 1]); } } boolean multiAddressEnabled = Boolean.parseBoolean( System.getProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, QuorumPeer.CONFIG_DEFAULT_MULTI_ADDRESS_ENABLED)); if (!multiAddressEnabled && serverAddresses.length > 1) { throw new ConfigException("Multiple address feature is disabled, but multiple addresses were specified for sid " + this.id); } boolean canonicalize = Boolean.parseBoolean( System.getProperty( CONFIG_KEY_KERBEROS_CANONICALIZE_HOST_NAMES, CONFIG_DEFAULT_KERBEROS_CANONICALIZE_HOST_NAMES)); for (String serverAddress : serverAddresses) { String serverParts[] = ConfigUtils.getHostAndPort(serverAddress); if ((serverClientParts.length > 2) || (serverParts.length < 3) || (serverParts.length > 4)) { throw new ConfigException(addressStr + wrongFormat); } String serverHostName = serverParts[0]; // server_config should be either host:port:port or host:port:port:type InetSocketAddress tempAddress; InetSocketAddress tempElectionAddress; try { tempAddress = new InetSocketAddress(serverHostName, Integer.parseInt(serverParts[1])); addr.addAddress(tempAddress); } catch (NumberFormatException e) { throw new ConfigException("Address unresolved: " + serverHostName + ":" + serverParts[1]); } try { tempElectionAddress = new InetSocketAddress(serverHostName, Integer.parseInt(serverParts[2])); electionAddr.addAddress(tempElectionAddress); } catch (NumberFormatException e) { throw new ConfigException("Address unresolved: " + serverHostName + ":" + serverParts[2]); } if (tempAddress.getPort() == tempElectionAddress.getPort()) { throw new ConfigException("Client and election port must be different! Please update the " + "configuration file on server." + this.id); } if (canonicalize) { InetAddress ia = getInetAddress.apply(tempAddress); if (ia == null) { throw new ConfigException("Unable to canonicalize address " + serverHostName + " because it's not resolvable"); } String canonicalHostName = ia.getCanonicalHostName(); if (!canonicalHostName.equals(serverHostName) // Avoid using literal IP address when // security check fails && !canonicalHostName.equals(ia.getHostAddress())) { LOG.info("Host name for quorum server {} " + "canonicalized from {} to {}", this.id, serverHostName, canonicalHostName); serverHostName = canonicalHostName; } } if (serverParts.length == 4) { LearnerType tempType = getType(serverParts[3]); if (newType == null) { newType = tempType; } if (newType != tempType) { throw new ConfigException("Multiple addresses should have similar roles: " + type + " vs " + tempType); } } this.hostname = serverHostName; } if (newType != null) { type = newType; } setMyAddrs(); } private static InetAddress getInetAddress(InetSocketAddress addr) { return addr.getAddress(); } private void setMyAddrs() { this.myAddrs = new ArrayList<>(); this.myAddrs.addAll(this.addr.getAllAddresses()); this.myAddrs.add(this.clientAddr); this.myAddrs.addAll(this.electionAddr.getAllAddresses()); this.myAddrs = excludedSpecialAddresses(this.myAddrs); } public static String delimitedHostString(InetSocketAddress addr) { String host = addr.getHostString(); if (host.contains(":")) { return "[" + host + "]"; } else { return host; } } public String toString() { StringWriter sw = new StringWriter(); List addrList = new LinkedList<>(addr.getAllAddresses()); List electionAddrList = new LinkedList<>(electionAddr.getAllAddresses()); if (addrList.size() > 0 && electionAddrList.size() > 0) { addrList.sort(Comparator.comparing(InetSocketAddress::getHostString)); electionAddrList.sort(Comparator.comparing(InetSocketAddress::getHostString)); sw.append(IntStream.range(0, addrList.size()).mapToObj(i -> String.format("%s:%d:%d", delimitedHostString(addrList.get(i)), addrList.get(i).getPort(), electionAddrList.get(i).getPort())) .collect(Collectors.joining("|"))); } if (type == LearnerType.OBSERVER) { sw.append(":observer"); } else if (type == LearnerType.PARTICIPANT) { sw.append(":participant"); } if (clientAddr != null && !isClientAddrFromStatic) { sw.append(";"); sw.append(delimitedHostString(clientAddr)); sw.append(":"); sw.append(String.valueOf(clientAddr.getPort())); } return sw.toString(); } public int hashCode() { assert false : "hashCode not designed"; return 42; // any arbitrary constant will do } private boolean checkAddressesEqual(InetSocketAddress addr1, InetSocketAddress addr2) { return (addr1 != null || addr2 == null) && (addr1 == null || addr2 != null) && (addr1 == null || addr2 == null || addr1.equals(addr2)); } public boolean equals(Object o) { if (!(o instanceof QuorumServer)) { return false; } QuorumServer qs = (QuorumServer) o; if ((qs.id != id) || (qs.type != type)) { return false; } if (!addr.equals(qs.addr)) { return false; } if (!electionAddr.equals(qs.electionAddr)) { return false; } return checkAddressesEqual(clientAddr, qs.clientAddr); } public void checkAddressDuplicate(QuorumServer s) throws BadArgumentsException { List otherAddrs = new ArrayList<>(s.addr.getAllAddresses()); otherAddrs.add(s.clientAddr); otherAddrs.addAll(s.electionAddr.getAllAddresses()); otherAddrs = excludedSpecialAddresses(otherAddrs); for (InetSocketAddress my : this.myAddrs) { for (InetSocketAddress other : otherAddrs) { if (my.equals(other)) { String error = String.format("%s of server.%d conflicts %s of server.%d", my, this.id, other, s.id); throw new BadArgumentsException(error); } } } } private List excludedSpecialAddresses(List addrs) { List included = new ArrayList<>(); for (InetSocketAddress addr : addrs) { if (addr == null) { continue; } InetAddress inetaddr = addr.getAddress(); if (inetaddr == null || inetaddr.isAnyLocalAddress() // wildCard addresses (0.0.0.0 or [::]) || inetaddr.isLoopbackAddress()) { // loopback address(localhost/127.0.0.1) continue; } included.add(addr); } return included; } } public enum ServerState { LOOKING, FOLLOWING, LEADING, OBSERVING } /** * (Used for monitoring) shows the current phase of * Zab protocol that peer is running. */ public enum ZabState { ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST } /** * (Used for monitoring) When peer is in synchronization phase, this shows * which synchronization mechanism is being used */ public enum SyncMode { NONE, DIFF, SNAP, TRUNC } /* * A peer can either be participating, which implies that it is willing to * both vote in instances of consensus and to elect or become a Leader, or * it may be observing in which case it isn't. * * We need this distinction to decide which ServerState to move to when * conditions change (e.g. which state to become after LOOKING). */ public enum LearnerType { PARTICIPANT, OBSERVER } /* * To enable observers to have no identifier, we need a generic identifier * at least for QuorumCnxManager. We use the following constant to as the * value of such a generic identifier. */ static final long OBSERVER_ID = Long.MAX_VALUE; /* * Record leader election time */ public long start_fle, end_fle; // fle = fast leader election public static final String FLE_TIME_UNIT = "MS"; private long unavailableStartTime; /* * Default value of peer is participant */ private LearnerType learnerType = LearnerType.PARTICIPANT; public LearnerType getLearnerType() { return learnerType; } /** * Sets the LearnerType */ public void setLearnerType(LearnerType p) { learnerType = p; } protected synchronized void setConfigFileName(String s) { configFilename = s; } private String configFilename = null; public int getQuorumSize() { return getVotingView().size(); } public void setJvmPauseMonitor(JvmPauseMonitor jvmPauseMonitor) { this.jvmPauseMonitor = jvmPauseMonitor; } /** * QuorumVerifier implementation; default (majority). */ //last committed quorum verifier private QuorumVerifier quorumVerifier; //last proposed quorum verifier private QuorumVerifier lastSeenQuorumVerifier = null; // Lock object that guard access to quorumVerifier and lastSeenQuorumVerifier. final Object QV_LOCK = new Object(); /** * My id */ private long myid; /** * get the id of this quorum peer. */ public long getMyId() { return myid; } // VisibleForTesting void setId(long id) { this.myid = id; } private boolean sslQuorum; private boolean shouldUsePortUnification; public boolean isSslQuorum() { return sslQuorum; } public boolean shouldUsePortUnification() { return shouldUsePortUnification; } private final QuorumX509Util x509Util; QuorumX509Util getX509Util() { return x509Util; } /** * This is who I think the leader currently is. */ private volatile Vote currentVote; public synchronized Vote getCurrentVote() { return currentVote; } public synchronized void setCurrentVote(Vote v) { currentVote = v; } private volatile boolean running = true; private String initialConfig; /** * The number of milliseconds of each tick */ protected int tickTime; /** * Whether learners in this quorum should create new sessions as local. * False by default to preserve existing behavior. */ protected boolean localSessionsEnabled = false; /** * Whether learners in this quorum should upgrade local sessions to * global. Only matters if local sessions are enabled. */ protected boolean localSessionsUpgradingEnabled = true; /** * Minimum number of milliseconds to allow for session timeout. * A value of -1 indicates unset, use default. */ protected int minSessionTimeout = -1; /** * Maximum number of milliseconds to allow for session timeout. * A value of -1 indicates unset, use default. */ protected int maxSessionTimeout = -1; /** * The ZooKeeper server's socket backlog length. The number of connections * that will be queued to be read before new connections are dropped. A * value of one indicates the default backlog will be used. */ protected int clientPortListenBacklog = -1; /** * The number of ticks that the initial synchronization phase can take */ protected volatile int initLimit; /** * The number of ticks that can pass between sending a request and getting * an acknowledgment */ protected volatile int syncLimit; /** * The number of ticks that can pass before retrying to connect to learner master */ protected volatile int connectToLearnerMasterLimit; /** * Enables/Disables sync request processor. This option is enabled * by default and is to be used with observers. */ protected boolean syncEnabled = true; /** * The current tick */ protected AtomicInteger tick = new AtomicInteger(); /** * Whether or not to listen on all IPs for the two quorum ports * (broadcast and fast leader election). */ protected boolean quorumListenOnAllIPs = false; /** * Keeps time taken for leader election in milliseconds. Sets the value to * this variable only after the completion of leader election. */ private long electionTimeTaken = -1; /** * Enable/Disables quorum authentication using sasl. Defaulting to false. */ protected boolean quorumSaslEnableAuth; /** * If this is false, quorum peer server will accept another quorum peer client * connection even if the authentication did not succeed. This can be used while * upgrading ZooKeeper server. Defaulting to false (required). */ protected boolean quorumServerSaslAuthRequired; /** * If this is false, quorum peer learner will talk to quorum peer server * without authentication. This can be used while upgrading ZooKeeper * server. Defaulting to false (required). */ protected boolean quorumLearnerSaslAuthRequired; /** * Kerberos quorum service principal. Defaulting to 'zkquorum/localhost'. */ protected String quorumServicePrincipal; /** * Quorum learner login context name in jaas-conf file to read the kerberos * security details. Defaulting to 'QuorumLearner'. */ protected String quorumLearnerLoginContext; /** * Quorum server login context name in jaas-conf file to read the kerberos * security details. Defaulting to 'QuorumServer'. */ protected String quorumServerLoginContext; // TODO: need to tune the default value of thread size private static final int QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE = 20; /** * The maximum number of threads to allow in the connectionExecutors thread * pool which will be used to initiate quorum server connections. */ protected int quorumCnxnThreadsSize = QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE; public static final String QUORUM_CNXN_TIMEOUT_MS = "zookeeper.quorumCnxnTimeoutMs"; private static int quorumCnxnTimeoutMs; static { quorumCnxnTimeoutMs = Integer.getInteger(QUORUM_CNXN_TIMEOUT_MS, -1); LOG.info("{}={}", QUORUM_CNXN_TIMEOUT_MS, quorumCnxnTimeoutMs); } /** * @deprecated As of release 3.4.0, this class has been deprecated, since * it is used with one of the udp-based versions of leader election, which * we are also deprecating. * * This class simply responds to requests for the current leader of this * node. *

    * The request contains just an xid generated by the requestor. *

    * The response has the xid, the id of this server, the id of the leader, * and the zxid of the leader. * * */ @Deprecated class ResponderThread extends ZooKeeperThread { ResponderThread() { super("ResponderThread"); } volatile boolean running = true; @Override public void run() { try { byte[] b = new byte[36]; ByteBuffer responseBuffer = ByteBuffer.wrap(b); DatagramPacket packet = new DatagramPacket(b, b.length); while (running) { udpSocket.receive(packet); if (packet.getLength() != 4) { LOG.warn("Got more than just an xid! Len = {}", packet.getLength()); } else { responseBuffer.clear(); responseBuffer.getInt(); // Skip the xid responseBuffer.putLong(myid); Vote current = getCurrentVote(); switch (getPeerState()) { case LOOKING: responseBuffer.putLong(current.getId()); responseBuffer.putLong(current.getZxid()); break; case LEADING: responseBuffer.putLong(myid); try { long proposed; synchronized (leader) { proposed = leader.lastProposed; } responseBuffer.putLong(proposed); } catch (NullPointerException npe) { // This can happen in state transitions, // just ignore the request } break; case FOLLOWING: responseBuffer.putLong(current.getId()); try { responseBuffer.putLong(follower.getZxid()); } catch (NullPointerException npe) { // This can happen in state transitions, // just ignore the request } break; case OBSERVING: // Do nothing, Observers keep themselves to // themselves. break; } packet.setData(b); udpSocket.send(packet); } packet.setLength(b.length); } } catch (RuntimeException e) { LOG.warn("Unexpected runtime exception in ResponderThread", e); } catch (IOException e) { LOG.warn("Unexpected IO exception in ResponderThread", e); } finally { LOG.warn("QuorumPeer responder thread exited"); } } } private ServerState state = ServerState.LOOKING; private AtomicReference zabState = new AtomicReference<>(ZabState.ELECTION); private AtomicReference syncMode = new AtomicReference<>(SyncMode.NONE); private AtomicReference leaderAddress = new AtomicReference<>(""); private AtomicLong leaderId = new AtomicLong(-1); private boolean reconfigFlag = false; // indicates that a reconfig just committed public synchronized void setPeerState(ServerState newState) { state = newState; if (newState == ServerState.LOOKING) { setLeaderAddressAndId(null, -1); setZabState(ZabState.ELECTION); } else { LOG.info("Peer state changed: {}", getDetailedPeerState()); } } public void setZabState(ZabState zabState) { if ((zabState == ZabState.BROADCAST) && (unavailableStartTime != 0)) { long unavailableTime = Time.currentElapsedTime() - unavailableStartTime; ServerMetrics.getMetrics().UNAVAILABLE_TIME.add(unavailableTime); if (getPeerState() == ServerState.LEADING) { ServerMetrics.getMetrics().LEADER_UNAVAILABLE_TIME.add(unavailableTime); } unavailableStartTime = 0; } this.zabState.set(zabState); LOG.info("Peer state changed: {}", getDetailedPeerState()); } public void setSyncMode(SyncMode syncMode) { this.syncMode.set(syncMode); LOG.info("Peer state changed: {}", getDetailedPeerState()); } public ZabState getZabState() { return zabState.get(); } public SyncMode getSyncMode() { return syncMode.get(); } public void setLeaderAddressAndId(MultipleAddresses addr, long newId) { if (addr != null) { leaderAddress.set(String.join("|", addr.getAllHostStrings())); } else { leaderAddress.set(null); } leaderId.set(newId); } public String getLeaderAddress() { return leaderAddress.get(); } public long getLeaderId() { return leaderId.get(); } public String getDetailedPeerState() { final StringBuilder sb = new StringBuilder(getPeerState().toString().toLowerCase()); final ZabState zabState = getZabState(); if (!ZabState.ELECTION.equals(zabState)) { sb.append(" - ").append(zabState.toString().toLowerCase()); } final SyncMode syncMode = getSyncMode(); if (!SyncMode.NONE.equals(syncMode)) { sb.append(" - ").append(syncMode.toString().toLowerCase()); } return sb.toString(); } public synchronized void reconfigFlagSet() { reconfigFlag = true; } public synchronized void reconfigFlagClear() { reconfigFlag = false; } public synchronized boolean isReconfigStateChange() { return reconfigFlag; } public synchronized ServerState getPeerState() { return state; } DatagramSocket udpSocket; private final AtomicReference myAddrs = new AtomicReference<>(); /** * Resolves hostname for a given server ID. * * This method resolves hostname for a given server ID in both quorumVerifer * and lastSeenQuorumVerifier. If the server ID matches the local server ID, * it also updates myAddrs. */ public void recreateSocketAddresses(long id) { QuorumVerifier qv = getQuorumVerifier(); if (qv != null) { QuorumServer qs = qv.getAllMembers().get(id); if (qs != null) { qs.recreateSocketAddresses(); if (id == getMyId()) { setAddrs(qs.addr, qs.electionAddr, qs.clientAddr); } } } qv = getLastSeenQuorumVerifier(); if (qv != null) { QuorumServer qs = qv.getAllMembers().get(id); if (qs != null) { qs.recreateSocketAddresses(); } } } private AddressTuple getAddrs() { AddressTuple addrs = myAddrs.get(); if (addrs != null) { return addrs; } try { synchronized (QV_LOCK) { addrs = myAddrs.get(); while (addrs == null) { QV_LOCK.wait(); addrs = myAddrs.get(); } return addrs; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } public MultipleAddresses getQuorumAddress() { return getAddrs().quorumAddr; } public MultipleAddresses getElectionAddress() { return getAddrs().electionAddr; } public InetSocketAddress getClientAddress() { final AddressTuple addrs = myAddrs.get(); return (addrs == null) ? null : addrs.clientAddr; } private void setAddrs(MultipleAddresses quorumAddr, MultipleAddresses electionAddr, InetSocketAddress clientAddr) { synchronized (QV_LOCK) { myAddrs.set(new AddressTuple(quorumAddr, electionAddr, clientAddr)); QV_LOCK.notifyAll(); } } private int electionType; Election electionAlg; ServerCnxnFactory cnxnFactory; ServerCnxnFactory secureCnxnFactory; private FileTxnSnapLog logFactory = null; private final QuorumStats quorumStats; AdminServer adminServer; private final boolean reconfigEnabled; public static QuorumPeer testingQuorumPeer() throws SaslException { return new QuorumPeer(); } public QuorumPeer() throws SaslException { super("QuorumPeer"); quorumStats = new QuorumStats(this); jmxRemotePeerBean = new HashMap<>(); adminServer = AdminServerFactory.createAdminServer(); x509Util = createX509Util(); initialize(); reconfigEnabled = QuorumPeerConfig.isReconfigEnabled(); } // VisibleForTesting QuorumX509Util createX509Util() { return new QuorumX509Util(); } /** * For backward compatibility purposes, we instantiate QuorumMaj by default. */ public QuorumPeer(Map quorumPeers, File dataDir, File dataLogDir, int electionType, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit, ServerCnxnFactory cnxnFactory) throws IOException { this(quorumPeers, dataDir, dataLogDir, electionType, myid, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, false, cnxnFactory, new QuorumMaj(quorumPeers)); } public QuorumPeer(Map quorumPeers, File dataDir, File dataLogDir, int electionType, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit, boolean quorumListenOnAllIPs, ServerCnxnFactory cnxnFactory, QuorumVerifier quorumConfig) throws IOException { this(); this.cnxnFactory = cnxnFactory; this.electionType = electionType; this.myid = myid; this.tickTime = tickTime; this.initLimit = initLimit; this.syncLimit = syncLimit; this.connectToLearnerMasterLimit = connectToLearnerMasterLimit; this.quorumListenOnAllIPs = quorumListenOnAllIPs; this.logFactory = new FileTxnSnapLog(dataLogDir, dataDir); this.zkDb = new ZKDatabase(this.logFactory); if (quorumConfig == null) { quorumConfig = new QuorumMaj(quorumPeers); } setQuorumVerifier(quorumConfig, false); adminServer = AdminServerFactory.createAdminServer(); } public void initialize() throws SaslException { // init quorum auth server & learner if (isQuorumSaslAuthEnabled()) { Set authzHosts = new HashSet<>(); for (QuorumServer qs : getView().values()) { authzHosts.add(qs.hostname); } authServer = new SaslQuorumAuthServer(isQuorumServerSaslAuthRequired(), quorumServerLoginContext, authzHosts); authLearner = new SaslQuorumAuthLearner(isQuorumLearnerSaslAuthRequired(), quorumServicePrincipal, quorumLearnerLoginContext); } else { authServer = new NullQuorumAuthServer(); authLearner = new NullQuorumAuthLearner(); } } QuorumStats quorumStats() { return quorumStats; } @Override public synchronized void start() { if (!getView().containsKey(myid)) { throw new RuntimeException("My id " + myid + " not in the peer list"); } loadDataBase(); startServerCnxnFactory(); try { adminServer.start(); } catch (AdminServerException e) { LOG.warn("Problem starting AdminServer", e); } startLeaderElection(); startJvmPauseMonitor(); super.start(); } private void loadDataBase() { try { zkDb.loadDataBase(); // load the epochs long lastProcessedZxid = zkDb.getDataTree().lastProcessedZxid; long epochOfZxid = ZxidUtils.getEpochFromZxid(lastProcessedZxid); try { currentEpoch = readLongFromFile(CURRENT_EPOCH_FILENAME); } catch (FileNotFoundException e) { // pick a reasonable epoch number // this should only happen once when moving to a // new code version currentEpoch = epochOfZxid; LOG.info( "{} not found! Creating with a reasonable default of {}. " + "This should only happen when you are upgrading your installation", CURRENT_EPOCH_FILENAME, currentEpoch); writeLongToFile(CURRENT_EPOCH_FILENAME, currentEpoch); } if (epochOfZxid > currentEpoch) { // acceptedEpoch.tmp file in snapshot directory File currentTmp = new File(getTxnFactory().getSnapDir(), CURRENT_EPOCH_FILENAME + AtomicFileOutputStream.TMP_EXTENSION); if (currentTmp.exists()) { long epochOfTmp = readLongFromFile(currentTmp.getName()); LOG.info("{} found. Setting current epoch to {}.", currentTmp, epochOfTmp); setCurrentEpoch(epochOfTmp); } else { throw new IOException( "The current epoch, " + ZxidUtils.zxidToString(currentEpoch) + ", is older than the last zxid, " + lastProcessedZxid); } } try { acceptedEpoch = readLongFromFile(ACCEPTED_EPOCH_FILENAME); } catch (FileNotFoundException e) { // pick a reasonable epoch number // this should only happen once when moving to a // new code version acceptedEpoch = epochOfZxid; LOG.info( "{} not found! Creating with a reasonable default of {}. " + "This should only happen when you are upgrading your installation", ACCEPTED_EPOCH_FILENAME, acceptedEpoch); writeLongToFile(ACCEPTED_EPOCH_FILENAME, acceptedEpoch); } if (acceptedEpoch < currentEpoch) { throw new IOException("The accepted epoch, " + ZxidUtils.zxidToString(acceptedEpoch) + " is less than the current epoch, " + ZxidUtils.zxidToString(currentEpoch)); } } catch (IOException ie) { LOG.error("Unable to load database on disk", ie); throw new RuntimeException("Unable to run quorum server ", ie); } } ResponderThread responder; public synchronized void stopLeaderElection() { responder.running = false; responder.interrupt(); } public synchronized void startLeaderElection() { try { if (getPeerState() == ServerState.LOOKING) { currentVote = new Vote(myid, getLastLoggedZxid(), getCurrentEpoch()); } } catch (IOException e) { RuntimeException re = new RuntimeException(e.getMessage()); re.setStackTrace(e.getStackTrace()); throw re; } this.electionAlg = createElectionAlgorithm(electionType); } private void startJvmPauseMonitor() { if (this.jvmPauseMonitor != null) { this.jvmPauseMonitor.serviceStart(); } } /** * Count the number of nodes in the map that could be followers. * @param peers * @return The number of followers in the map */ protected static int countParticipants(Map peers) { int count = 0; for (QuorumServer q : peers.values()) { if (q.type == LearnerType.PARTICIPANT) { count++; } } return count; } /** * This constructor is only used by the existing unit test code. * It defaults to FileLogProvider persistence provider. */ public QuorumPeer(Map quorumPeers, File snapDir, File logDir, int clientPort, int electionAlg, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit) throws IOException { this( quorumPeers, snapDir, logDir, electionAlg, myid, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, false, ServerCnxnFactory.createFactory(getClientAddress(quorumPeers, myid, clientPort), -1), new QuorumMaj(quorumPeers)); } public QuorumPeer(Map quorumPeers, File snapDir, File logDir, int clientPort, int electionAlg, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit, String oraclePath) throws IOException { this( quorumPeers, snapDir, logDir, electionAlg, myid, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, false, ServerCnxnFactory.createFactory(getClientAddress(quorumPeers, myid, clientPort), -1), new QuorumOracleMaj(quorumPeers, oraclePath)); } /** * This constructor is only used by the existing unit test code. * It defaults to FileLogProvider persistence provider. */ public QuorumPeer(Map quorumPeers, File snapDir, File logDir, int clientPort, int electionAlg, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit, QuorumVerifier quorumConfig) throws IOException { this( quorumPeers, snapDir, logDir, electionAlg, myid, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, false, ServerCnxnFactory.createFactory(getClientAddress(quorumPeers, myid, clientPort), -1), quorumConfig); } private static InetSocketAddress getClientAddress(Map quorumPeers, long myid, int clientPort) throws IOException { QuorumServer quorumServer = quorumPeers.get(myid); if (null == quorumServer) { throw new IOException("No QuorumServer correspoding to myid " + myid); } if (null == quorumServer.clientAddr) { return new InetSocketAddress(clientPort); } if (quorumServer.clientAddr.getPort() != clientPort) { throw new IOException("QuorumServer port " + quorumServer.clientAddr.getPort() + " does not match with given port " + clientPort); } return quorumServer.clientAddr; } /** * returns the highest zxid that this host has seen * * @return the highest zxid for this host */ public long getLastLoggedZxid() { if (!zkDb.isInitialized()) { loadDataBase(); } return zkDb.getDataTreeLastProcessedZxid(); } public Follower follower; public Leader leader; public Observer observer; protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.zkDb)); } protected Leader makeLeader(FileTxnSnapLog logFactory) throws IOException, X509Exception { return new Leader(this, new LeaderZooKeeperServer(logFactory, this, this.zkDb)); } protected Observer makeObserver(FileTxnSnapLog logFactory) throws IOException { return new Observer(this, new ObserverZooKeeperServer(logFactory, this, this.zkDb)); } @SuppressWarnings("deprecation") protected Election createElectionAlgorithm(int electionAlgorithm) { Election le = null; //TODO: use a factory rather than a switch switch (electionAlgorithm) { case 1: throw new UnsupportedOperationException("Election Algorithm 1 is not supported."); case 2: throw new UnsupportedOperationException("Election Algorithm 2 is not supported."); case 3: QuorumCnxManager qcm = createCnxnManager(); QuorumCnxManager oldQcm = qcmRef.getAndSet(qcm); if (oldQcm != null) { LOG.warn("Clobbering already-set QuorumCnxManager (restarting leader election?)"); oldQcm.halt(); } QuorumCnxManager.Listener listener = qcm.listener; if (listener != null) { listener.start(); FastLeaderElection fle = new FastLeaderElection(this, qcm); fle.start(); le = fle; } else { LOG.error("Null listener when initializing cnx manager"); } break; default: assert false; } return le; } @SuppressWarnings("deprecation") protected Election makeLEStrategy() { LOG.debug("Initializing leader election protocol..."); return electionAlg; } protected synchronized void setLeader(Leader newLeader) { leader = newLeader; } protected synchronized void setFollower(Follower newFollower) { follower = newFollower; } protected synchronized void setObserver(Observer newObserver) { observer = newObserver; } public synchronized ZooKeeperServer getActiveServer() { if (leader != null) { return leader.zk; } else if (follower != null) { return follower.zk; } else if (observer != null) { return observer.zk; } return null; } boolean shuttingDownLE = false; public void setSuspended(boolean suspended) { this.suspended.set(suspended); } private void checkSuspended() { try { while (suspended.get()) { Thread.sleep(10); } } catch (InterruptedException err) { Thread.currentThread().interrupt(); } } @Override public void run() { updateThreadName(); LOG.debug("Starting quorum peer"); try { jmxQuorumBean = new QuorumBean(this); MBeanRegistry.getInstance().register(jmxQuorumBean, null); for (QuorumServer s : getView().values()) { ZKMBeanInfo p; if (getMyId() == s.id) { p = jmxLocalPeerBean = new LocalPeerBean(this); try { MBeanRegistry.getInstance().register(p, jmxQuorumBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxLocalPeerBean = null; } } else { RemotePeerBean rBean = new RemotePeerBean(this, s); try { MBeanRegistry.getInstance().register(rBean, jmxQuorumBean); jmxRemotePeerBean.put(s.id, rBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); } } } } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxQuorumBean = null; } try { /* * Main loop */ while (running) { if (unavailableStartTime == 0) { unavailableStartTime = Time.currentElapsedTime(); } switch (getPeerState()) { case LOOKING: LOG.info("LOOKING"); ServerMetrics.getMetrics().LOOKING_COUNT.add(1); if (Boolean.getBoolean("readonlymode.enabled")) { LOG.info("Attempting to start ReadOnlyZooKeeperServer"); // Create read-only server but don't start it immediately final ReadOnlyZooKeeperServer roZk = new ReadOnlyZooKeeperServer(logFactory, this, this.zkDb); // Instead of starting roZk immediately, wait some grace // period before we decide we're partitioned. // // Thread is used here because otherwise it would require // changes in each of election strategy classes which is // unnecessary code coupling. Thread roZkMgr = new Thread() { public void run() { try { // lower-bound grace period to 2 secs sleep(Math.max(2000, tickTime)); if (ServerState.LOOKING.equals(getPeerState())) { roZk.startup(); } } catch (InterruptedException e) { LOG.info("Interrupted while attempting to start ReadOnlyZooKeeperServer, not started"); } catch (Exception e) { LOG.error("FAILED to start ReadOnlyZooKeeperServer", e); } } }; try { roZkMgr.start(); reconfigFlagClear(); if (shuttingDownLE) { shuttingDownLE = false; startLeaderElection(); } setCurrentVote(makeLEStrategy().lookForLeader()); checkSuspended(); } catch (Exception e) { LOG.warn("Unexpected exception", e); setPeerState(ServerState.LOOKING); } finally { // If the thread is in the the grace period, interrupt // to come out of waiting. roZkMgr.interrupt(); roZk.shutdown(); } } else { try { reconfigFlagClear(); if (shuttingDownLE) { shuttingDownLE = false; startLeaderElection(); } setCurrentVote(makeLEStrategy().lookForLeader()); } catch (Exception e) { LOG.warn("Unexpected exception", e); setPeerState(ServerState.LOOKING); } } break; case OBSERVING: try { LOG.info("OBSERVING"); setObserver(makeObserver(logFactory)); observer.observeLeader(); } catch (Exception e) { LOG.warn("Unexpected exception", e); } finally { observer.shutdown(); setObserver(null); updateServerState(); // Add delay jitter before we switch to LOOKING // state to reduce the load of ObserverMaster if (isRunning()) { Observer.waitForObserverElectionDelay(); } } break; case FOLLOWING: try { LOG.info("FOLLOWING"); setFollower(makeFollower(logFactory)); follower.followLeader(); } catch (Exception e) { LOG.warn("Unexpected exception", e); } finally { follower.shutdown(); setFollower(null); updateServerState(); } break; case LEADING: LOG.info("LEADING"); try { setLeader(makeLeader(logFactory)); leader.lead(); setLeader(null); } catch (Exception e) { LOG.warn("Unexpected exception", e); } finally { if (leader != null) { leader.shutdown("Forcing shutdown"); setLeader(null); } updateServerState(); } break; } } } finally { LOG.warn("QuorumPeer main thread exited"); MBeanRegistry instance = MBeanRegistry.getInstance(); instance.unregister(jmxQuorumBean); instance.unregister(jmxLocalPeerBean); for (RemotePeerBean remotePeerBean : jmxRemotePeerBean.values()) { instance.unregister(remotePeerBean); } jmxQuorumBean = null; jmxLocalPeerBean = null; jmxRemotePeerBean = null; } } private synchronized void updateServerState() { if (!reconfigFlag) { setPeerState(ServerState.LOOKING); LOG.warn("PeerState set to LOOKING"); return; } if (getMyId() == getCurrentVote().getId()) { setPeerState(ServerState.LEADING); LOG.debug("PeerState set to LEADING"); } else if (getLearnerType() == LearnerType.PARTICIPANT) { setPeerState(ServerState.FOLLOWING); LOG.debug("PeerState set to FOLLOWING"); } else if (getLearnerType() == LearnerType.OBSERVER) { setPeerState(ServerState.OBSERVING); LOG.debug("PeerState set to OBSERVER"); } else { // currently shouldn't happen since there are only 2 learner types setPeerState(ServerState.LOOKING); LOG.debug("Should not be here"); } reconfigFlag = false; } public void shutdown() { running = false; x509Util.close(); if (leader != null) { leader.shutdown("quorum Peer shutdown"); } if (follower != null) { follower.shutdown(); } shutdownServerCnxnFactory(); if (udpSocket != null) { udpSocket.close(); } if (jvmPauseMonitor != null) { jvmPauseMonitor.serviceStop(); } try { adminServer.shutdown(); } catch (AdminServerException e) { LOG.warn("Problem stopping AdminServer", e); } if (getElectionAlg() != null) { this.interrupt(); getElectionAlg().shutdown(); } try { zkDb.close(); } catch (IOException ie) { LOG.warn("Error closing logs ", ie); } } /** * A 'view' is a node's current opinion of the membership of the entire * ensemble. */ public Map getView() { return Collections.unmodifiableMap(getQuorumVerifier().getAllMembers()); } /** * Observers are not contained in this view, only nodes with * PeerType=PARTICIPANT. */ public Map getVotingView() { return getQuorumVerifier().getVotingMembers(); } /** * Returns only observers, no followers. */ public Map getObservingView() { return getQuorumVerifier().getObservingMembers(); } public synchronized Set getCurrentAndNextConfigVoters() { Set voterIds = new HashSet<>(getQuorumVerifier().getVotingMembers().keySet()); if (getLastSeenQuorumVerifier() != null) { voterIds.addAll(getLastSeenQuorumVerifier().getVotingMembers().keySet()); } return voterIds; } /** * Check if a node is in the current view. With static membership, the * result of this check will never change; only when dynamic membership * is introduced will this be more useful. */ public boolean viewContains(Long sid) { return this.getView().containsKey(sid); } /** * Only used by QuorumStats at the moment */ public String[] getQuorumPeers() { List l = new ArrayList<>(); synchronized (this) { if (leader != null) { for (LearnerHandler fh : leader.getLearners()) { if (fh.getSocket() != null) { String s = formatInetAddr((InetSocketAddress) fh.getSocket().getRemoteSocketAddress()); if (leader.isLearnerSynced(fh)) { s += "*"; } l.add(s); } } } else if (follower != null) { l.add(formatInetAddr((InetSocketAddress) follower.sock.getRemoteSocketAddress())); } } return l.toArray(new String[0]); } public String getServerState() { switch (getPeerState()) { case LOOKING: return QuorumStats.Provider.LOOKING_STATE; case LEADING: return QuorumStats.Provider.LEADING_STATE; case FOLLOWING: return QuorumStats.Provider.FOLLOWING_STATE; case OBSERVING: return QuorumStats.Provider.OBSERVING_STATE; } return QuorumStats.Provider.UNKNOWN_STATE; } /** * set the id of this quorum peer. */ public void setMyid(long myid) { this.myid = myid; } public void setInitialConfig(String initialConfig) { this.initialConfig = initialConfig; } public String getInitialConfig() { return initialConfig; } /** * Get the number of milliseconds of each tick */ public int getTickTime() { return tickTime; } /** * Set the number of milliseconds of each tick */ public void setTickTime(int tickTime) { LOG.info("tickTime set to {}", tickTime); this.tickTime = tickTime; } /** Maximum number of connections allowed from particular host (ip) */ public int getMaxClientCnxnsPerHost() { if (cnxnFactory != null) { return cnxnFactory.getMaxClientCnxnsPerHost(); } if (secureCnxnFactory != null) { return secureCnxnFactory.getMaxClientCnxnsPerHost(); } return -1; } /** Whether local sessions are enabled */ public boolean areLocalSessionsEnabled() { return localSessionsEnabled; } /** Whether to enable local sessions */ public void enableLocalSessions(boolean flag) { LOG.info("Local sessions {}", (flag ? "enabled" : "disabled")); localSessionsEnabled = flag; } /** Whether local sessions are allowed to upgrade to global sessions */ public boolean isLocalSessionsUpgradingEnabled() { return localSessionsUpgradingEnabled; } /** Whether to allow local sessions to upgrade to global sessions */ public void enableLocalSessionsUpgrading(boolean flag) { LOG.info("Local session upgrading {}", (flag ? "enabled" : "disabled")); localSessionsUpgradingEnabled = flag; } /** minimum session timeout in milliseconds */ public int getMinSessionTimeout() { return minSessionTimeout; } /** minimum session timeout in milliseconds */ public void setMinSessionTimeout(int min) { LOG.info("minSessionTimeout set to {}", min); this.minSessionTimeout = min; } /** maximum session timeout in milliseconds */ public int getMaxSessionTimeout() { return maxSessionTimeout; } /** maximum session timeout in milliseconds */ public void setMaxSessionTimeout(int max) { LOG.info("maxSessionTimeout set to {}", max); this.maxSessionTimeout = max; } /** The server socket's listen backlog length */ public int getClientPortListenBacklog() { return this.clientPortListenBacklog; } /** Sets the server socket's listen backlog length. */ public void setClientPortListenBacklog(int backlog) { this.clientPortListenBacklog = backlog; } /** * Get the number of ticks that the initial synchronization phase can take */ public int getInitLimit() { return initLimit; } /** * Set the number of ticks that the initial synchronization phase can take */ public void setInitLimit(int initLimit) { LOG.info("initLimit set to {}", initLimit); this.initLimit = initLimit; } /** * Get the current tick */ public int getTick() { return tick.get(); } public QuorumVerifier configFromString(String s) throws IOException, ConfigException { Properties props = new Properties(); props.load(new StringReader(s)); return QuorumPeerConfig.parseDynamicConfig(props, electionType, false, false, getQuorumVerifier().getOraclePath()); } /** * Return QuorumVerifier object for the last committed configuration. */ public QuorumVerifier getQuorumVerifier() { synchronized (QV_LOCK) { return quorumVerifier; } } /** * Return QuorumVerifier object for the last proposed configuration. */ public QuorumVerifier getLastSeenQuorumVerifier() { synchronized (QV_LOCK) { return lastSeenQuorumVerifier; } } public synchronized void restartLeaderElection(QuorumVerifier qvOLD, QuorumVerifier qvNEW) { if (qvOLD == null || !qvOLD.equals(qvNEW)) { LOG.warn("Restarting Leader Election"); getElectionAlg().shutdown(); shuttingDownLE = false; startLeaderElection(); } } public String getNextDynamicConfigFilename() { if (configFilename == null) { LOG.warn("configFilename is null! This should only happen in tests."); return null; } return configFilename + QuorumPeerConfig.nextDynamicConfigFileSuffix; } // On entry to this method, qcm must be non-null and the locks on both qcm and QV_LOCK // must be held. We don't want quorumVerifier/lastSeenQuorumVerifier to change out from // under us, so we have to hold QV_LOCK; and since the call to qcm.connectOne() will take // the lock on qcm (and take QV_LOCK again inside that), the caller needs to have taken // qcm outside QV_LOCK to avoid a deadlock against other callers of qcm.connectOne(). private void connectNewPeers(QuorumCnxManager qcm) { if (quorumVerifier != null && lastSeenQuorumVerifier != null) { Map committedView = quorumVerifier.getAllMembers(); for (Entry e : lastSeenQuorumVerifier.getAllMembers().entrySet()) { if (e.getKey() != getMyId() && !committedView.containsKey(e.getKey())) { qcm.connectOne(e.getKey()); } } } } public void setLastSeenQuorumVerifier(QuorumVerifier qv, boolean writeToDisk) { if (!isReconfigEnabled()) { LOG.info("Dynamic reconfig is disabled, we don't store the last seen config."); return; } // If qcm is non-null, we may call qcm.connectOne(), which will take the lock on qcm // and then take QV_LOCK. Take the locks in the same order to ensure that we don't // deadlock against other callers of connectOne(). If qcmRef gets set in another // thread while we're inside the synchronized block, that does no harm; if we didn't // take a lock on qcm (because it was null when we sampled it), we won't call // connectOne() on it. (Use of an AtomicReference is enough to guarantee visibility // of updates that provably happen in another thread before entering this method.) QuorumCnxManager qcm = qcmRef.get(); Object outerLockObject = (qcm != null) ? qcm : QV_LOCK; synchronized (outerLockObject) { synchronized (QV_LOCK) { if (lastSeenQuorumVerifier != null && lastSeenQuorumVerifier.getVersion() > qv.getVersion()) { LOG.error("setLastSeenQuorumVerifier called with stale config " + qv.getVersion() + ". Current version: " + quorumVerifier.getVersion()); } // assuming that a version uniquely identifies a configuration, so if // version is the same, nothing to do here. if (lastSeenQuorumVerifier != null && lastSeenQuorumVerifier.getVersion() == qv.getVersion()) { return; } lastSeenQuorumVerifier = qv; if (qcm != null) { connectNewPeers(qcm); } if (writeToDisk) { try { String fileName = getNextDynamicConfigFilename(); if (fileName != null) { QuorumPeerConfig.writeDynamicConfig(fileName, qv, true); } } catch (IOException e) { LOG.error("Error writing next dynamic config file to disk", e); } } } } } public QuorumVerifier setQuorumVerifier(QuorumVerifier qv, boolean writeToDisk) { synchronized (QV_LOCK) { if ((quorumVerifier != null) && (quorumVerifier.getVersion() >= qv.getVersion())) { // this is normal. For example - server found out about new config through FastLeaderElection gossiping // and then got the same config in UPTODATE message so its already known LOG.debug( "{} setQuorumVerifier called with known or old config {}. Current version: {}", getMyId(), qv.getVersion(), quorumVerifier.getVersion()); return quorumVerifier; } QuorumVerifier prevQV = quorumVerifier; quorumVerifier = qv; if (lastSeenQuorumVerifier == null || (qv.getVersion() > lastSeenQuorumVerifier.getVersion())) { lastSeenQuorumVerifier = qv; } if (writeToDisk) { // some tests initialize QuorumPeer without a static config file if (configFilename != null) { try { String dynamicConfigFilename = makeDynamicConfigFilename(qv.getVersion()); QuorumPeerConfig.writeDynamicConfig(dynamicConfigFilename, qv, false); QuorumPeerConfig.editStaticConfig(configFilename, dynamicConfigFilename, needEraseClientInfoFromStaticConfig()); } catch (IOException e) { LOG.error("Error closing file", e); } } else { LOG.info("writeToDisk == true but configFilename == null"); } } if (qv.getVersion() == lastSeenQuorumVerifier.getVersion()) { QuorumPeerConfig.deleteFile(getNextDynamicConfigFilename()); } QuorumServer qs = qv.getAllMembers().get(getMyId()); if (qs != null) { setAddrs(qs.addr, qs.electionAddr, qs.clientAddr); } updateObserverMasterList(); return prevQV; } } private String makeDynamicConfigFilename(long version) { return configFilename + ".dynamic." + Long.toHexString(version); } private boolean needEraseClientInfoFromStaticConfig() { QuorumServer server = quorumVerifier.getAllMembers().get(getMyId()); return (server != null && server.clientAddr != null && !server.isClientAddrFromStatic); } /** * Get an instance of LeaderElection */ public Election getElectionAlg() { return electionAlg; } /** * Get the synclimit */ public int getSyncLimit() { return syncLimit; } /** * Set the synclimit */ public void setSyncLimit(int syncLimit) { LOG.info("syncLimit set to {}", syncLimit); this.syncLimit = syncLimit; } /** * Get the connectToLearnerMasterLimit */ public int getConnectToLearnerMasterLimit() { return connectToLearnerMasterLimit; } /** * Set the connectToLearnerMasterLimit */ public void setConnectToLearnerMasterLimit(int connectToLearnerMasterLimit) { LOG.info("connectToLearnerMasterLimit set to {}", connectToLearnerMasterLimit); this.connectToLearnerMasterLimit = connectToLearnerMasterLimit; } /** * The syncEnabled can also be set via a system property. */ public static final String SYNC_ENABLED = "zookeeper.observer.syncEnabled"; /** * Return syncEnabled. */ public boolean getSyncEnabled() { if (System.getProperty(SYNC_ENABLED) != null) { LOG.info("{}={}", SYNC_ENABLED, Boolean.getBoolean(SYNC_ENABLED)); return Boolean.getBoolean(SYNC_ENABLED); } else { return syncEnabled; } } /** * Set syncEnabled. * * @param syncEnabled */ public void setSyncEnabled(boolean syncEnabled) { this.syncEnabled = syncEnabled; } /** * Gets the election type */ public int getElectionType() { return electionType; } /** * Sets the election type */ public void setElectionType(int electionType) { this.electionType = electionType; } public boolean getQuorumListenOnAllIPs() { return quorumListenOnAllIPs; } public void setQuorumListenOnAllIPs(boolean quorumListenOnAllIPs) { this.quorumListenOnAllIPs = quorumListenOnAllIPs; } public void setCnxnFactory(ServerCnxnFactory cnxnFactory) { this.cnxnFactory = cnxnFactory; } public void setSecureCnxnFactory(ServerCnxnFactory secureCnxnFactory) { this.secureCnxnFactory = secureCnxnFactory; } public void setSslQuorum(boolean sslQuorum) { if (sslQuorum) { LOG.info("Using TLS encrypted quorum communication"); } else { LOG.info("Using insecure (non-TLS) quorum communication"); } this.sslQuorum = sslQuorum; } public void setUsePortUnification(boolean shouldUsePortUnification) { LOG.info("Port unification {}", shouldUsePortUnification ? "enabled" : "disabled"); this.shouldUsePortUnification = shouldUsePortUnification; } private void startServerCnxnFactory() { if (cnxnFactory != null) { cnxnFactory.start(); } if (secureCnxnFactory != null) { secureCnxnFactory.start(); } } private void shutdownServerCnxnFactory() { if (cnxnFactory != null) { cnxnFactory.shutdown(); } if (secureCnxnFactory != null) { secureCnxnFactory.shutdown(); } } // Leader and learner will control the zookeeper server and pass it into QuorumPeer. public void setZooKeeperServer(ZooKeeperServer zks) { if (cnxnFactory != null) { cnxnFactory.setZooKeeperServer(zks); } if (secureCnxnFactory != null) { secureCnxnFactory.setZooKeeperServer(zks); } } public void closeAllConnections() { if (cnxnFactory != null) { cnxnFactory.closeAll(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN); } if (secureCnxnFactory != null) { secureCnxnFactory.closeAll(ServerCnxn.DisconnectReason.SERVER_SHUTDOWN); } } public int getClientPort() { if (cnxnFactory != null) { return cnxnFactory.getLocalPort(); } return -1; } public int getSecureClientPort() { if (secureCnxnFactory != null) { return secureCnxnFactory.getLocalPort(); } return -1; } public void setTxnFactory(FileTxnSnapLog factory) { this.logFactory = factory; } public FileTxnSnapLog getTxnFactory() { return this.logFactory; } /** * set zk database for this node * @param database */ public void setZKDatabase(ZKDatabase database) { this.zkDb = database; } protected ZKDatabase getZkDb() { return zkDb; } public synchronized void initConfigInZKDatabase() { if (zkDb != null) { zkDb.initConfigInZKDatabase(getQuorumVerifier()); } } public boolean isRunning() { return running; } /** * get reference to QuorumCnxManager */ public QuorumCnxManager getQuorumCnxManager() { return qcmRef.get(); } private long readLongFromFile(String name) throws IOException { File file = new File(logFactory.getSnapDir(), name); BufferedReader br = new BufferedReader(new FileReader(file)); String line = ""; try { line = br.readLine(); return Long.parseLong(line); } catch (NumberFormatException e) { throw new IOException("Found " + line + " in " + file); } finally { br.close(); } } private long acceptedEpoch = -1; private long currentEpoch = -1; public static final String CURRENT_EPOCH_FILENAME = "currentEpoch"; public static final String ACCEPTED_EPOCH_FILENAME = "acceptedEpoch"; /** * Write a long value to disk atomically. Either succeeds or an exception * is thrown. * @param name file name to write the long to * @param value the long value to write to the named file * @throws IOException if the file cannot be written atomically */ // visibleForTest void writeLongToFile(String name, final long value) throws IOException { File file = new File(logFactory.getSnapDir(), name); new AtomicFileWritingIdiom(file, new WriterStatement() { @Override public void write(Writer bw) throws IOException { bw.write(Long.toString(value)); } }); } public long getCurrentEpoch() throws IOException { if (currentEpoch == -1) { currentEpoch = readLongFromFile(CURRENT_EPOCH_FILENAME); } return currentEpoch; } public long getAcceptedEpoch() throws IOException { if (acceptedEpoch == -1) { acceptedEpoch = readLongFromFile(ACCEPTED_EPOCH_FILENAME); } return acceptedEpoch; } public void setCurrentEpoch(long e) throws IOException { writeLongToFile(CURRENT_EPOCH_FILENAME, e); currentEpoch = e; } public void setAcceptedEpoch(long e) throws IOException { writeLongToFile(ACCEPTED_EPOCH_FILENAME, e); acceptedEpoch = e; } public boolean processReconfig(QuorumVerifier qv, Long suggestedLeaderId, Long zxid, boolean restartLE) { if (!isReconfigEnabled()) { LOG.debug("Reconfig feature is disabled, skip reconfig processing."); return false; } InetSocketAddress oldClientAddr = getClientAddress(); // update last committed quorum verifier, write the new config to disk // and restart leader election if config changed. QuorumVerifier prevQV = setQuorumVerifier(qv, true); // There is no log record for the initial config, thus after syncing // with leader // /zookeeper/config is empty! it is also possible that last committed // config is propagated during leader election // without the propagation the corresponding log records. // so we should explicitly do this (this is not necessary when we're // already a Follower/Observer, only // for Learner): initConfigInZKDatabase(); if (prevQV.getVersion() < qv.getVersion() && !prevQV.equals(qv)) { Map newMembers = qv.getAllMembers(); updateRemotePeerMXBeans(newMembers); if (restartLE) { restartLeaderElection(prevQV, qv); } QuorumServer myNewQS = newMembers.get(getMyId()); if (myNewQS != null && myNewQS.clientAddr != null && !myNewQS.clientAddr.equals(oldClientAddr)) { cnxnFactory.reconfigure(myNewQS.clientAddr); updateThreadName(); } boolean roleChange = updateLearnerType(qv); boolean leaderChange = false; if (suggestedLeaderId != null) { // zxid should be non-null too leaderChange = updateVote(suggestedLeaderId, zxid); } else { long currentLeaderId = getCurrentVote().getId(); QuorumServer myleaderInCurQV = prevQV.getVotingMembers().get(currentLeaderId); QuorumServer myleaderInNewQV = qv.getVotingMembers().get(currentLeaderId); leaderChange = (myleaderInCurQV == null || myleaderInCurQV.addr == null || myleaderInNewQV == null || !myleaderInCurQV.addr.equals(myleaderInNewQV.addr)); // we don't have a designated leader - need to go into leader // election reconfigFlagClear(); } return roleChange || leaderChange; } return false; } private void updateRemotePeerMXBeans(Map newMembers) { Set existingMembers = new HashSet<>(newMembers.keySet()); existingMembers.retainAll(jmxRemotePeerBean.keySet()); for (Long id : existingMembers) { RemotePeerBean rBean = jmxRemotePeerBean.get(id); rBean.setQuorumServer(newMembers.get(id)); } Set joiningMembers = new HashSet<>(newMembers.keySet()); joiningMembers.removeAll(jmxRemotePeerBean.keySet()); joiningMembers.remove(getMyId()); // remove self as it is local bean for (Long id : joiningMembers) { QuorumServer qs = newMembers.get(id); RemotePeerBean rBean = new RemotePeerBean(this, qs); try { MBeanRegistry.getInstance().register(rBean, jmxQuorumBean); jmxRemotePeerBean.put(qs.id, rBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); } } Set leavingMembers = new HashSet<>(jmxRemotePeerBean.keySet()); leavingMembers.removeAll(newMembers.keySet()); for (Long id : leavingMembers) { RemotePeerBean rBean = jmxRemotePeerBean.remove(id); try { MBeanRegistry.getInstance().unregister(rBean); } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } } } private ArrayList observerMasters = new ArrayList<>(); private void updateObserverMasterList() { if (observerMasterPort <= 0) { return; // observer masters not enabled } observerMasters.clear(); StringBuilder sb = new StringBuilder(); for (QuorumServer server : quorumVerifier.getVotingMembers().values()) { InetAddress address = server.addr.getReachableOrOne().getAddress(); InetSocketAddress addr = new InetSocketAddress(address, observerMasterPort); observerMasters.add(new QuorumServer(server.id, addr)); sb.append(addr).append(","); } LOG.info("Updated learner master list to be {}", sb.toString()); Collections.shuffle(observerMasters); // Reset the internal index of the observerMaster when // the observerMaster List is refreshed nextObserverMaster = 0; } private boolean useObserverMasters() { return getLearnerType() == LearnerType.OBSERVER && observerMasters.size() > 0; } private int nextObserverMaster = 0; private QuorumServer nextObserverMaster() { if (nextObserverMaster >= observerMasters.size()) { nextObserverMaster = 0; // Add a reconnect delay only after the observer // has exhausted trying to connect to all the masters // from the observerMasterList if (isRunning()) { Observer.waitForReconnectDelay(); } } return observerMasters.get(nextObserverMaster++); } QuorumServer findLearnerMaster(QuorumServer leader) { if (useObserverMasters()) { return nextObserverMaster(); } else { // Add delay jitter to reduce the load on the leader if (isRunning()) { Observer.waitForReconnectDelay(); } return leader; } } /** * Vet a given learner master's information. * Allows specification by server id, ip only, or ip and port */ QuorumServer validateLearnerMaster(String desiredMaster) { if (useObserverMasters()) { Long sid; try { sid = Long.parseLong(desiredMaster); } catch (NumberFormatException e) { sid = null; } for (QuorumServer server : observerMasters) { if (sid == null) { for (InetSocketAddress address : server.addr.getAllAddresses()) { String serverAddr = address.getAddress().getHostAddress() + ':' + address.getPort(); if (serverAddr.startsWith(desiredMaster)) { return server; } } } else { if (sid.equals(server.id)) { return server; } } } if (sid == null) { LOG.info("could not find learner master address={}", desiredMaster); } else { LOG.warn("could not find learner master sid={}", sid); } } else { LOG.info("cannot validate request, observer masters not enabled"); } return null; } private boolean updateLearnerType(QuorumVerifier newQV) { //check if I'm an observer in new config if (newQV.getObservingMembers().containsKey(getMyId())) { if (getLearnerType() != LearnerType.OBSERVER) { setLearnerType(LearnerType.OBSERVER); LOG.info("Becoming an observer"); reconfigFlagSet(); return true; } else { return false; } } else if (newQV.getVotingMembers().containsKey(getMyId())) { if (getLearnerType() != LearnerType.PARTICIPANT) { setLearnerType(LearnerType.PARTICIPANT); LOG.info("Becoming a voting participant"); reconfigFlagSet(); return true; } else { return false; } } // I'm not in the view if (getLearnerType() != LearnerType.PARTICIPANT) { setLearnerType(LearnerType.PARTICIPANT); LOG.info("Becoming a non-voting participant"); reconfigFlagSet(); return true; } return false; } private boolean updateVote(long designatedLeader, long zxid) { Vote currentVote = getCurrentVote(); if (currentVote != null && designatedLeader != currentVote.getId()) { setCurrentVote(new Vote(designatedLeader, zxid)); reconfigFlagSet(); LOG.warn("Suggested leader: {}", designatedLeader); return true; } return false; } /** * Updates leader election info to avoid inconsistencies when * a new server tries to join the ensemble. * * Here is the inconsistency scenario we try to solve by updating the peer * epoch after following leader: * * Let's say we have an ensemble with 3 servers z1, z2 and z3. * * 1. z1, z2 were following z3 with peerEpoch to be 0xb8, the new epoch is * 0xb9, aka current accepted epoch on disk. * 2. z2 get restarted, which will use 0xb9 as it's peer epoch when loading * the current accept epoch from disk. * 3. z2 received notification from z1 and z3, which is following z3 with * epoch 0xb8, so it started following z3 again with peer epoch 0xb8. * 4. before z2 successfully connected to z3, z3 get restarted with new * epoch 0xb9. * 5. z2 will retry around a few round (default 5s) before giving up, * meanwhile it will report z3 as leader. * 6. z1 restarted, and looking with peer epoch 0xb9. * 7. z1 voted z3, and z3 was elected as leader again with peer epoch 0xb9. * 8. z2 successfully connected to z3 before giving up, but with peer * epoch 0xb8. * 9. z1 get restarted, looking for leader with peer epoch 0xba, but cannot * join, because z2 is reporting peer epoch 0xb8, while z3 is reporting * 0xb9. * * By updating the election vote after actually following leader, we can * avoid this kind of stuck happened. * * Btw, the zxid and electionEpoch could be inconsistent because of the same * reason, it's better to update these as well after syncing with leader, but * that required protocol change which is non trivial. This problem is worked * around by skipping comparing the zxid and electionEpoch when counting for * votes for out of election servers during looking for leader. * * See https://issues.apache.org/jira/browse/ZOOKEEPER-1732 */ protected void updateElectionVote(long newEpoch) { Vote currentVote = getCurrentVote(); if (currentVote != null) { setCurrentVote(new Vote(currentVote.getId(), currentVote.getZxid(), currentVote.getElectionEpoch(), newEpoch, currentVote .getState())); } } private void updateThreadName() { String plain = cnxnFactory != null ? cnxnFactory.getLocalAddress() != null ? formatInetAddr(cnxnFactory.getLocalAddress()) : "disabled" : "disabled"; String secure = secureCnxnFactory != null ? formatInetAddr(secureCnxnFactory.getLocalAddress()) : "disabled"; setName(String.format("QuorumPeer[myid=%d](plain=%s)(secure=%s)", getMyId(), plain, secure)); } /** * Sets the time taken for leader election in milliseconds. * * @param electionTimeTaken time taken for leader election */ void setElectionTimeTaken(long electionTimeTaken) { this.electionTimeTaken = electionTimeTaken; } /** * @return the time taken for leader election in milliseconds. */ long getElectionTimeTaken() { return electionTimeTaken; } void setQuorumServerSaslRequired(boolean serverSaslRequired) { quorumServerSaslAuthRequired = serverSaslRequired; LOG.info("{} set to {}", QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, serverSaslRequired); } void setQuorumLearnerSaslRequired(boolean learnerSaslRequired) { quorumLearnerSaslAuthRequired = learnerSaslRequired; LOG.info("{} set to {}", QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, learnerSaslRequired); } void setQuorumSaslEnabled(boolean enableAuth) { quorumSaslEnableAuth = enableAuth; if (!quorumSaslEnableAuth) { LOG.info("QuorumPeer communication is not secured! (SASL auth disabled)"); } else { LOG.info("{} set to {}", QuorumAuth.QUORUM_SASL_AUTH_ENABLED, enableAuth); } } void setQuorumServicePrincipal(String servicePrincipal) { quorumServicePrincipal = servicePrincipal; LOG.info("{} set to {}", QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, quorumServicePrincipal); } void setQuorumLearnerLoginContext(String learnerContext) { quorumLearnerLoginContext = learnerContext; LOG.info("{} set to {}", QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, quorumLearnerLoginContext); } void setQuorumServerLoginContext(String serverContext) { quorumServerLoginContext = serverContext; LOG.info("{} set to {}", QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT, quorumServerLoginContext); } void setQuorumCnxnThreadsSize(int qCnxnThreadsSize) { if (qCnxnThreadsSize > QUORUM_CNXN_THREADS_SIZE_DEFAULT_VALUE) { quorumCnxnThreadsSize = qCnxnThreadsSize; } LOG.info("quorum.cnxn.threads.size set to {}", quorumCnxnThreadsSize); } boolean isQuorumSaslAuthEnabled() { return quorumSaslEnableAuth; } private boolean isQuorumServerSaslAuthRequired() { return quorumServerSaslAuthRequired; } private boolean isQuorumLearnerSaslAuthRequired() { return quorumLearnerSaslAuthRequired; } public QuorumCnxManager createCnxnManager() { int timeout = quorumCnxnTimeoutMs > 0 ? quorumCnxnTimeoutMs : this.tickTime * this.syncLimit; LOG.info("Using {}ms as the quorum cnxn socket timeout", timeout); return new QuorumCnxManager( this, this.getMyId(), this.getView(), this.authServer, this.authLearner, timeout, this.getQuorumListenOnAllIPs(), this.quorumCnxnThreadsSize, this.isQuorumSaslAuthEnabled()); } boolean isLeader(long id) { Vote vote = getCurrentVote(); return vote != null && id == vote.getId(); } public boolean isReconfigEnabled() { return reconfigEnabled; } @InterfaceAudience.Private /** * This is a metric that depends on the status of the peer. */ public Integer getSynced_observers_metric() { if (leader != null) { return leader.getObservingLearners().size(); } else if (follower != null) { return follower.getSyncedObserverSize(); } else { return null; } } /** * Create a new QuorumPeer and apply all the values per the already-parsed config. * * @param config The appertained quorum peer config. * @return A QuorumPeer instantiated with specified peer config. Note this peer * is not fully initialized; caller should finish initialization through * additional configurations (connection factory settings, etc). * * @throws IOException */ public static QuorumPeer createFromConfig(QuorumPeerConfig config) throws IOException { QuorumPeer quorumPeer = new QuorumPeer(); quorumPeer.setTxnFactory(new FileTxnSnapLog(config.getDataLogDir(), config.getDataDir())); quorumPeer.enableLocalSessions(config.areLocalSessionsEnabled()); quorumPeer.enableLocalSessionsUpgrading(config.isLocalSessionsUpgradingEnabled()); quorumPeer.setElectionType(config.getElectionAlg()); quorumPeer.setMyid(config.getServerId()); quorumPeer.setTickTime(config.getTickTime()); quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout()); quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout()); quorumPeer.setInitLimit(config.getInitLimit()); quorumPeer.setSyncLimit(config.getSyncLimit()); quorumPeer.setConnectToLearnerMasterLimit(config.getConnectToLearnerMasterLimit()); quorumPeer.setObserverMasterPort(config.getObserverMasterPort()); quorumPeer.setConfigFileName(config.getConfigFilename()); quorumPeer.setClientPortListenBacklog(config.getClientPortListenBacklog()); quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory())); quorumPeer.setQuorumVerifier(config.getQuorumVerifier(), false); if (config.getLastSeenQuorumVerifier() != null) { quorumPeer.setLastSeenQuorumVerifier(config.getLastSeenQuorumVerifier(), false); } quorumPeer.initConfigInZKDatabase(); quorumPeer.setSslQuorum(config.isSslQuorum()); quorumPeer.setUsePortUnification(config.shouldUsePortUnification()); quorumPeer.setLearnerType(config.getPeerType()); quorumPeer.setSyncEnabled(config.getSyncEnabled()); quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs()); if (config.sslQuorumReloadCertFiles) { quorumPeer.getX509Util().enableCertFileReloading(); } quorumPeer.setMultiAddressEnabled(config.isMultiAddressEnabled()); quorumPeer.setMultiAddressReachabilityCheckEnabled(config.isMultiAddressReachabilityCheckEnabled()); quorumPeer.setMultiAddressReachabilityCheckTimeoutMs(config.getMultiAddressReachabilityCheckTimeoutMs()); // sets quorum sasl authentication configurations quorumPeer.setQuorumSaslEnabled(config.quorumEnableSasl); if (quorumPeer.isQuorumSaslAuthEnabled()) { quorumPeer.setQuorumServerSaslRequired(config.quorumServerRequireSasl); quorumPeer.setQuorumLearnerSaslRequired(config.quorumLearnerRequireSasl); quorumPeer.setQuorumServicePrincipal(config.quorumServicePrincipal); quorumPeer.setQuorumServerLoginContext(config.quorumServerLoginContext); quorumPeer.setQuorumLearnerLoginContext(config.quorumLearnerLoginContext); } quorumPeer.setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize); if (config.jvmPauseMonitorToRun) { quorumPeer.setJvmPauseMonitor(new JvmPauseMonitor(config)); } return quorumPeer; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000170 15051152474 032715 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerConfig.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerC0100644 0000000 0000000 00000120143 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringReader; import java.io.Writer; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.common.AtomicFileWritingIdiom; import org.apache.zookeeper.common.AtomicFileWritingIdiom.OutputStreamStatement; import org.apache.zookeeper.common.AtomicFileWritingIdiom.WriterStatement; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.metrics.impl.DefaultMetricsProvider; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.auth.QuorumAuth; import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumOracleMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.server.util.JvmPauseMonitor; import org.apache.zookeeper.server.util.VerifyingFileFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @InterfaceAudience.Public public class QuorumPeerConfig { private static final Logger LOG = LoggerFactory.getLogger(QuorumPeerConfig.class); private static final int UNSET_SERVERID = -1; public static final String nextDynamicConfigFileSuffix = ".dynamic.next"; private static boolean standaloneEnabled = true; private static boolean reconfigEnabled = false; protected InetSocketAddress clientPortAddress; protected InetSocketAddress secureClientPortAddress; protected boolean sslQuorum = false; protected boolean shouldUsePortUnification = false; protected int observerMasterPort; protected boolean sslQuorumReloadCertFiles = false; protected File dataDir; protected File dataLogDir; protected String dynamicConfigFileStr = null; protected String configFileStr = null; protected int tickTime = ZooKeeperServer.DEFAULT_TICK_TIME; protected int maxClientCnxns = 60; /** defaults to -1 if not set explicitly */ protected int minSessionTimeout = -1; /** defaults to -1 if not set explicitly */ protected int maxSessionTimeout = -1; protected String metricsProviderClassName = DefaultMetricsProvider.class.getName(); protected Properties metricsProviderConfiguration = new Properties(); protected boolean localSessionsEnabled = false; protected boolean localSessionsUpgradingEnabled = false; /** defaults to -1 if not set explicitly */ protected int clientPortListenBacklog = -1; protected int initLimit; protected int syncLimit; protected int connectToLearnerMasterLimit; protected int electionAlg = 3; protected int electionPort = 2182; protected boolean quorumListenOnAllIPs = false; protected long serverId = UNSET_SERVERID; protected QuorumVerifier quorumVerifier = null, lastSeenQuorumVerifier = null; protected int snapRetainCount = 3; protected int purgeInterval = 0; protected boolean syncEnabled = true; protected String initialConfig; protected LearnerType peerType = LearnerType.PARTICIPANT; /** * Configurations for the quorumpeer-to-quorumpeer sasl authentication */ protected boolean quorumServerRequireSasl = false; protected boolean quorumLearnerRequireSasl = false; protected boolean quorumEnableSasl = false; protected String quorumServicePrincipal = QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE; protected String quorumLearnerLoginContext = QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT_DFAULT_VALUE; protected String quorumServerLoginContext = QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT_DFAULT_VALUE; protected int quorumCnxnThreadsSize; // multi address related configs private boolean multiAddressEnabled = Boolean.parseBoolean( System.getProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, QuorumPeer.CONFIG_DEFAULT_MULTI_ADDRESS_ENABLED)); private boolean multiAddressReachabilityCheckEnabled = Boolean.parseBoolean(System.getProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_REACHABILITY_CHECK_ENABLED, "true")); private int multiAddressReachabilityCheckTimeoutMs = Integer.parseInt(System.getProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_REACHABILITY_CHECK_TIMEOUT_MS, String.valueOf(MultipleAddresses.DEFAULT_TIMEOUT.toMillis()))); protected String oraclePath; /** * Minimum snapshot retain count. * @see org.apache.zookeeper.server.PurgeTxnLog#purge(File, File, int) */ private final int MIN_SNAP_RETAIN_COUNT = 3; /** * JVM Pause Monitor feature switch */ protected boolean jvmPauseMonitorToRun = false; /** * JVM Pause Monitor warn threshold in ms */ protected long jvmPauseWarnThresholdMs = JvmPauseMonitor.WARN_THRESHOLD_DEFAULT; /** * JVM Pause Monitor info threshold in ms */ protected long jvmPauseInfoThresholdMs = JvmPauseMonitor.INFO_THRESHOLD_DEFAULT; /** * JVM Pause Monitor sleep time in ms */ protected long jvmPauseSleepTimeMs = JvmPauseMonitor.SLEEP_TIME_MS_DEFAULT; @SuppressWarnings("serial") public static class ConfigException extends Exception { public ConfigException(String msg) { super(msg); } public ConfigException(String msg, Exception e) { super(msg, e); } } /** * Parse a ZooKeeper configuration file * @param path the patch of the configuration file * @throws ConfigException error processing configuration */ public void parse(String path) throws ConfigException { LOG.info("Reading configuration from: " + path); try { File configFile = (new VerifyingFileFactory.Builder(LOG) .warnForRelativePath() .failForNonExistingPath() .build()).create(path); Properties cfg = new Properties(); try (FileInputStream in = new FileInputStream(configFile)) { cfg.load(in); configFileStr = path; } /* Read entire config file as initial configuration */ initialConfig = new String(Files.readAllBytes(configFile.toPath())); parseProperties(cfg); } catch (IOException e) { throw new ConfigException("Error processing " + path, e); } catch (IllegalArgumentException e) { throw new ConfigException("Error processing " + path, e); } if (dynamicConfigFileStr != null) { try { Properties dynamicCfg = new Properties(); try (FileInputStream inConfig = new FileInputStream(dynamicConfigFileStr)) { dynamicCfg.load(inConfig); if (dynamicCfg.getProperty("version") != null) { throw new ConfigException("dynamic file shouldn't have version inside"); } String version = getVersionFromFilename(dynamicConfigFileStr); // If there isn't any version associated with the filename, // the default version is 0. if (version != null) { dynamicCfg.setProperty("version", version); } } setupQuorumPeerConfig(dynamicCfg, false); } catch (IOException e) { throw new ConfigException("Error processing " + dynamicConfigFileStr, e); } catch (IllegalArgumentException e) { throw new ConfigException("Error processing " + dynamicConfigFileStr, e); } File nextDynamicConfigFile = new File(configFileStr + nextDynamicConfigFileSuffix); if (nextDynamicConfigFile.exists()) { try { Properties dynamicConfigNextCfg = new Properties(); try (FileInputStream inConfigNext = new FileInputStream(nextDynamicConfigFile)) { dynamicConfigNextCfg.load(inConfigNext); } boolean isHierarchical = false; for (Entry entry : dynamicConfigNextCfg.entrySet()) { String key = entry.getKey().toString().trim(); if (key.startsWith("group") || key.startsWith("weight")) { isHierarchical = true; break; } } lastSeenQuorumVerifier = createQuorumVerifier(dynamicConfigNextCfg, isHierarchical); } catch (IOException e) { LOG.warn("NextQuorumVerifier is initiated to null"); } } } } // This method gets the version from the end of dynamic file name. // For example, "zoo.cfg.dynamic.0" returns initial version "0". // "zoo.cfg.dynamic.1001" returns version of hex number "0x1001". // If a dynamic file name doesn't have any version at the end of file, // e.g. "zoo.cfg.dynamic", it returns null. public static String getVersionFromFilename(String filename) { int i = filename.lastIndexOf('.'); if (i < 0 || i >= filename.length()) { return null; } String hexVersion = filename.substring(i + 1); try { long version = Long.parseLong(hexVersion, 16); return Long.toHexString(version); } catch (NumberFormatException e) { return null; } } /** * Parse config from a Properties. * @param zkProp Properties to parse from. * @throws IOException * @throws ConfigException */ public void parseProperties(Properties zkProp) throws IOException, ConfigException { Integer clientPort = null; Integer secureClientPort = null; int observerMasterPort = 0; String clientPortAddress = null; String secureClientPortAddress = null; VerifyingFileFactory vff = new VerifyingFileFactory.Builder(LOG).warnForRelativePath().build(); for (Entry entry : zkProp.entrySet()) { String key = entry.getKey().toString().trim(); String value = entry.getValue().toString().trim(); if (key.equals("dataDir")) { dataDir = vff.create(value); } else if (key.equals("dataLogDir")) { dataLogDir = vff.create(value); } else if (key.equals("clientPort")) { clientPort = Integer.parseInt(value); } else if (key.equals("localSessionsEnabled")) { localSessionsEnabled = parseBoolean(key, value); } else if (key.equals("localSessionsUpgradingEnabled")) { localSessionsUpgradingEnabled = parseBoolean(key, value); } else if (key.equals("clientPortAddress")) { clientPortAddress = value.trim(); } else if (key.equals("secureClientPort")) { secureClientPort = Integer.parseInt(value); } else if (key.equals("secureClientPortAddress")) { secureClientPortAddress = value.trim(); } else if (key.equals("observerMasterPort")) { observerMasterPort = Integer.parseInt(value); } else if (key.equals("clientPortListenBacklog")) { clientPortListenBacklog = Integer.parseInt(value); } else if (key.equals("tickTime")) { tickTime = Integer.parseInt(value); } else if (key.equals("maxClientCnxns")) { maxClientCnxns = Integer.parseInt(value); } else if (key.equals("minSessionTimeout")) { minSessionTimeout = Integer.parseInt(value); } else if (key.equals("maxSessionTimeout")) { maxSessionTimeout = Integer.parseInt(value); } else if (key.equals("initLimit")) { initLimit = Integer.parseInt(value); } else if (key.equals("syncLimit")) { syncLimit = Integer.parseInt(value); } else if (key.equals("connectToLearnerMasterLimit")) { connectToLearnerMasterLimit = Integer.parseInt(value); } else if (key.equals("electionAlg")) { electionAlg = Integer.parseInt(value); if (electionAlg != 3) { throw new ConfigException("Invalid electionAlg value. Only 3 is supported."); } } else if (key.equals("quorumListenOnAllIPs")) { quorumListenOnAllIPs = parseBoolean(key, value); } else if (key.equals("peerType")) { if (value.toLowerCase().equals("observer")) { peerType = LearnerType.OBSERVER; } else if (value.toLowerCase().equals("participant")) { peerType = LearnerType.PARTICIPANT; } else { throw new ConfigException("Unrecognised peertype: " + value); } } else if (key.equals("syncEnabled")) { syncEnabled = parseBoolean(key, value); } else if (key.equals("dynamicConfigFile")) { dynamicConfigFileStr = value; } else if (key.equals("autopurge.snapRetainCount")) { snapRetainCount = Integer.parseInt(value); } else if (key.equals("autopurge.purgeInterval")) { purgeInterval = Integer.parseInt(value); } else if (key.equals("standaloneEnabled")) { setStandaloneEnabled(parseBoolean(key, value)); } else if (key.equals("reconfigEnabled")) { setReconfigEnabled(parseBoolean(key, value)); } else if (key.equals("sslQuorum")) { sslQuorum = parseBoolean(key, value); } else if (key.equals("portUnification")) { shouldUsePortUnification = parseBoolean(key, value); } else if (key.equals("sslQuorumReloadCertFiles")) { sslQuorumReloadCertFiles = parseBoolean(key, value); } else if ((key.startsWith("server.") || key.startsWith("group") || key.startsWith("weight")) && zkProp.containsKey("dynamicConfigFile")) { throw new ConfigException("parameter: " + key + " must be in a separate dynamic config file"); } else if (key.equals(QuorumAuth.QUORUM_SASL_AUTH_ENABLED)) { quorumEnableSasl = parseBoolean(key, value); } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED)) { quorumServerRequireSasl = parseBoolean(key, value); } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED)) { quorumLearnerRequireSasl = parseBoolean(key, value); } else if (key.equals(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT)) { quorumLearnerLoginContext = value; } else if (key.equals(QuorumAuth.QUORUM_SERVER_SASL_LOGIN_CONTEXT)) { quorumServerLoginContext = value; } else if (key.equals(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL)) { quorumServicePrincipal = value; } else if (key.equals("quorum.cnxn.threads.size")) { quorumCnxnThreadsSize = Integer.parseInt(value); } else if (key.equals(JvmPauseMonitor.INFO_THRESHOLD_KEY)) { jvmPauseInfoThresholdMs = Long.parseLong(value); } else if (key.equals(JvmPauseMonitor.WARN_THRESHOLD_KEY)) { jvmPauseWarnThresholdMs = Long.parseLong(value); } else if (key.equals(JvmPauseMonitor.SLEEP_TIME_MS_KEY)) { jvmPauseSleepTimeMs = Long.parseLong(value); } else if (key.equals(JvmPauseMonitor.JVM_PAUSE_MONITOR_FEATURE_SWITCH_KEY)) { jvmPauseMonitorToRun = parseBoolean(key, value); } else if (key.equals("metricsProvider.className")) { metricsProviderClassName = value; } else if (key.startsWith("metricsProvider.")) { String keyForMetricsProvider = key.substring(16); metricsProviderConfiguration.put(keyForMetricsProvider, value); } else if (key.equals("multiAddress.enabled")) { multiAddressEnabled = parseBoolean(key, value); } else if (key.equals("multiAddress.reachabilityCheckTimeoutMs")) { multiAddressReachabilityCheckTimeoutMs = Integer.parseInt(value); } else if (key.equals("multiAddress.reachabilityCheckEnabled")) { multiAddressReachabilityCheckEnabled = parseBoolean(key, value); } else if (key.equals("oraclePath")) { oraclePath = value; } else { System.setProperty("zookeeper." + key, value); } } if (!quorumEnableSasl && quorumServerRequireSasl) { throw new IllegalArgumentException(QuorumAuth.QUORUM_SASL_AUTH_ENABLED + " is disabled, so cannot enable " + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED); } if (!quorumEnableSasl && quorumLearnerRequireSasl) { throw new IllegalArgumentException(QuorumAuth.QUORUM_SASL_AUTH_ENABLED + " is disabled, so cannot enable " + QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED); } // If quorumpeer learner is not auth enabled then self won't be able to // join quorum. So this condition is ensuring that the quorumpeer learner // is also auth enabled while enabling quorum server require sasl. if (!quorumLearnerRequireSasl && quorumServerRequireSasl) { throw new IllegalArgumentException(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED + " is disabled, so cannot enable " + QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED); } // Reset to MIN_SNAP_RETAIN_COUNT if invalid (less than 3) // PurgeTxnLog.purge(File, File, int) will not allow to purge less // than 3. if (snapRetainCount < MIN_SNAP_RETAIN_COUNT) { LOG.warn("Invalid autopurge.snapRetainCount: " + snapRetainCount + ". Defaulting to " + MIN_SNAP_RETAIN_COUNT); snapRetainCount = MIN_SNAP_RETAIN_COUNT; } if (dataDir == null) { throw new IllegalArgumentException("dataDir is not set"); } if (dataLogDir == null) { dataLogDir = dataDir; } if (clientPort == null) { LOG.info("clientPort is not set"); if (clientPortAddress != null) { throw new IllegalArgumentException("clientPortAddress is set but clientPort is not set"); } } else if (clientPortAddress != null) { this.clientPortAddress = new InetSocketAddress(InetAddress.getByName(clientPortAddress), clientPort); LOG.info("clientPortAddress is {}", formatInetAddr(this.clientPortAddress)); } else { this.clientPortAddress = new InetSocketAddress(clientPort); LOG.info("clientPortAddress is {}", formatInetAddr(this.clientPortAddress)); } if (secureClientPort == null) { LOG.info("secureClientPort is not set"); if (secureClientPortAddress != null) { throw new IllegalArgumentException("secureClientPortAddress is set but secureClientPort is not set"); } } else if (secureClientPortAddress != null) { this.secureClientPortAddress = new InetSocketAddress(InetAddress.getByName(secureClientPortAddress), secureClientPort); LOG.info("secureClientPortAddress is {}", formatInetAddr(this.secureClientPortAddress)); } else { this.secureClientPortAddress = new InetSocketAddress(secureClientPort); LOG.info("secureClientPortAddress is {}", formatInetAddr(this.secureClientPortAddress)); } if (this.secureClientPortAddress != null) { configureSSLAuth(); } if (observerMasterPort <= 0) { LOG.info("observerMasterPort is not set"); } else { this.observerMasterPort = observerMasterPort; LOG.info("observerMasterPort is {}", observerMasterPort); } if (tickTime == 0) { throw new IllegalArgumentException("tickTime is not set"); } minSessionTimeout = minSessionTimeout == -1 ? tickTime * 2 : minSessionTimeout; maxSessionTimeout = maxSessionTimeout == -1 ? tickTime * 20 : maxSessionTimeout; if (minSessionTimeout > maxSessionTimeout) { throw new IllegalArgumentException("minSessionTimeout must not be larger than maxSessionTimeout"); } LOG.info("metricsProvider.className is {}", metricsProviderClassName); try { Class.forName(metricsProviderClassName, false, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException error) { throw new IllegalArgumentException("metrics provider class was not found", error); } // backward compatibility - dynamic configuration in the same file as // static configuration params see writeDynamicConfig() if (dynamicConfigFileStr == null) { setupQuorumPeerConfig(zkProp, true); if (isDistributed() && isReconfigEnabled()) { // we don't backup static config for standalone mode. // we also don't backup if reconfig feature is disabled. backupOldConfig(); } } } /** * Configure SSL authentication only if it is not configured. * * @throws ConfigException * If authentication scheme is configured but authentication * provider is not configured. */ public static void configureSSLAuth() throws ConfigException { try (ClientX509Util clientX509Util = new ClientX509Util()) { String sslAuthProp = ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX + System.getProperty(clientX509Util.getSslAuthProviderProperty(), "x509"); if (System.getProperty(sslAuthProp) == null) { if ((ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX + "x509").equals(sslAuthProp)) { System.setProperty(ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX + "x509", "org.apache.zookeeper.server.auth.X509AuthenticationProvider"); } else { throw new ConfigException("No auth provider configured for the SSL authentication scheme '" + System.getProperty(clientX509Util.getSslAuthProviderProperty()) + "'."); } } } } /** * Backward compatibility -- It would backup static config file on bootup * if users write dynamic configuration in "zoo.cfg". */ private void backupOldConfig() throws IOException { new AtomicFileWritingIdiom(new File(configFileStr + ".bak"), new OutputStreamStatement() { @Override public void write(OutputStream output) throws IOException { try (InputStream input = new FileInputStream(new File(configFileStr))) { byte[] buf = new byte[1024]; int bytesRead; while ((bytesRead = input.read(buf)) > 0) { output.write(buf, 0, bytesRead); } } } }); } /** * Writes dynamic configuration file */ public static void writeDynamicConfig(final String dynamicConfigFilename, final QuorumVerifier qv, final boolean needKeepVersion) throws IOException { new AtomicFileWritingIdiom(new File(dynamicConfigFilename), new WriterStatement() { @Override public void write(Writer out) throws IOException { Properties cfg = new Properties(); cfg.load(new StringReader(qv.toString())); List servers = new ArrayList<>(); for (Entry entry : cfg.entrySet()) { String key = entry.getKey().toString().trim(); if (!needKeepVersion && key.startsWith("version")) { continue; } String value = entry.getValue().toString().trim(); servers.add(key.concat("=").concat(value)); } Collections.sort(servers); out.write(StringUtils.joinStrings(servers, "\n")); } }); } /** * Edit static config file. * If there are quorum information in static file, e.g. "server.X", "group", * it will remove them. * If it needs to erase client port information left by the old config, * "eraseClientPortAddress" should be set true. * It should also updates dynamic file pointer on reconfig. */ public static void editStaticConfig(final String configFileStr, final String dynamicFileStr, final boolean eraseClientPortAddress) throws IOException { // Some tests may not have a static config file. if (configFileStr == null) { return; } File configFile = (new VerifyingFileFactory.Builder(LOG).warnForRelativePath().failForNonExistingPath().build()) .create(configFileStr); final File dynamicFile = (new VerifyingFileFactory.Builder(LOG) .warnForRelativePath() .failForNonExistingPath() .build()).create(dynamicFileStr); final Properties cfg = new Properties(); try (FileInputStream in = new FileInputStream(configFile)) { cfg.load(in); } new AtomicFileWritingIdiom(new File(configFileStr), new WriterStatement() { @Override public void write(Writer out) throws IOException { for (Entry entry : cfg.entrySet()) { String key = entry.getKey().toString().trim(); if (key.startsWith("server.") || key.startsWith("group") || key.startsWith("weight") || key.startsWith("dynamicConfigFile") || key.startsWith("peerType") || (eraseClientPortAddress && (key.startsWith("clientPort") || key.startsWith("clientPortAddress")))) { // not writing them back to static file continue; } String value = entry.getValue().toString().trim(); out.write(key.concat("=").concat(value).concat("\n")); } // updates the dynamic file pointer String dynamicConfigFilePath = PathUtils.normalizeFileSystemPath(dynamicFile.getCanonicalPath()); out.write("dynamicConfigFile=".concat(dynamicConfigFilePath).concat("\n")); } }); } public static void deleteFile(String filename) { if (filename == null) { return; } File f = new File(filename); if (f.exists()) { try { f.delete(); } catch (Exception e) { LOG.warn("deleting {} failed", filename); } } } private static QuorumVerifier createQuorumVerifier(Properties dynamicConfigProp, boolean isHierarchical, String oraclePath) throws ConfigException { if (oraclePath == null) { return createQuorumVerifier(dynamicConfigProp, isHierarchical); } else { return new QuorumOracleMaj(dynamicConfigProp, oraclePath); } } private static QuorumVerifier createQuorumVerifier(Properties dynamicConfigProp, boolean isHierarchical) throws ConfigException { if (isHierarchical) { return new QuorumHierarchical(dynamicConfigProp); } else { /* * The default QuorumVerifier is QuorumMaj */ //LOG.info("Defaulting to majority quorums"); return new QuorumMaj(dynamicConfigProp); } } void setupQuorumPeerConfig(Properties prop, boolean configBackwardCompatibilityMode) throws IOException, ConfigException { quorumVerifier = parseDynamicConfig(prop, electionAlg, true, configBackwardCompatibilityMode, oraclePath); setupMyId(); setupClientPort(); setupPeerType(); checkValidity(); } /** * Parse dynamic configuration file and return * quorumVerifier for new configuration. * @param dynamicConfigProp Properties to parse from. * @throws IOException * @throws ConfigException */ public static QuorumVerifier parseDynamicConfig(Properties dynamicConfigProp, int eAlg, boolean warnings, boolean configBackwardCompatibilityMode, String oraclePath) throws IOException, ConfigException { boolean isHierarchical = false; for (Entry entry : dynamicConfigProp.entrySet()) { String key = entry.getKey().toString().trim(); if (key.startsWith("group") || key.startsWith("weight")) { isHierarchical = true; } else if (!configBackwardCompatibilityMode && !key.startsWith("server.") && !key.equals("version")) { LOG.info(dynamicConfigProp.toString()); throw new ConfigException("Unrecognised parameter: " + key); } } QuorumVerifier qv = createQuorumVerifier(dynamicConfigProp, isHierarchical, oraclePath); int numParticipators = qv.getVotingMembers().size(); int numObservers = qv.getObservingMembers().size(); if (numParticipators == 0) { if (!standaloneEnabled) { throw new IllegalArgumentException("standaloneEnabled = false then " + "number of participants should be >0"); } if (numObservers > 0) { throw new IllegalArgumentException("Observers w/o participants is an invalid configuration"); } } else if (numParticipators == 1 && standaloneEnabled) { // HBase currently adds a single server line to the config, for // b/w compatibility reasons we need to keep this here. If standaloneEnabled // is true, the QuorumPeerMain script will create a standalone server instead // of a quorum configuration LOG.error("Invalid configuration, only one server specified (ignoring)"); if (numObservers > 0) { throw new IllegalArgumentException("Observers w/o quorum is an invalid configuration"); } } else { if (warnings) { if (numParticipators <= 2) { LOG.warn("No server failure will be tolerated. You need at least 3 servers."); } else if (numParticipators % 2 == 0) { LOG.warn("Non-optimal configuration, consider an odd number of servers."); } } for (QuorumServer s : qv.getVotingMembers().values()) { if (s.electionAddr == null) { throw new IllegalArgumentException("Missing election port for server: " + s.id); } } } return qv; } private void setupMyId() throws IOException { File myIdFile = new File(dataDir, "myid"); // standalone server doesn't need myid file. if (!myIdFile.isFile()) { return; } BufferedReader br = new BufferedReader(new FileReader(myIdFile)); String myIdString; try { myIdString = br.readLine(); } finally { br.close(); } try { serverId = Long.parseLong(myIdString); MDC.put("myid", myIdString); } catch (NumberFormatException e) { throw new IllegalArgumentException("serverid " + myIdString + " is not a number"); } } private void setupClientPort() throws ConfigException { if (serverId == UNSET_SERVERID) { return; } QuorumServer qs = quorumVerifier.getAllMembers().get(serverId); if (clientPortAddress != null && qs != null && qs.clientAddr != null) { if ((!clientPortAddress.getAddress().isAnyLocalAddress() && !clientPortAddress.equals(qs.clientAddr)) || ( clientPortAddress.getAddress().isAnyLocalAddress() && clientPortAddress.getPort() != qs.clientAddr.getPort())) { throw new ConfigException("client address for this server (id = " + serverId + ") in static config file is " + clientPortAddress + " is different from client address found in dynamic file: " + qs.clientAddr); } } if (qs != null && qs.clientAddr != null) { clientPortAddress = qs.clientAddr; } if (qs != null && qs.clientAddr == null) { qs.clientAddr = clientPortAddress; qs.isClientAddrFromStatic = true; } } private void setupPeerType() { // Warn about inconsistent peer type LearnerType roleByServersList = quorumVerifier.getObservingMembers().containsKey(serverId) ? LearnerType.OBSERVER : LearnerType.PARTICIPANT; if (roleByServersList != peerType) { LOG.warn( "Peer type from servers list ({}) doesn't match peerType ({}). Defaulting to servers list.", roleByServersList, peerType); peerType = roleByServersList; } } public void checkValidity() throws IOException, ConfigException { if (isDistributed()) { if (initLimit == 0) { throw new IllegalArgumentException("initLimit is not set"); } if (syncLimit == 0) { throw new IllegalArgumentException("syncLimit is not set"); } if (serverId == UNSET_SERVERID) { throw new IllegalArgumentException("myid file is missing"); } } } public InetSocketAddress getClientPortAddress() { return clientPortAddress; } public InetSocketAddress getSecureClientPortAddress() { return secureClientPortAddress; } public int getObserverMasterPort() { return observerMasterPort; } public File getDataDir() { return dataDir; } public File getDataLogDir() { return dataLogDir; } public String getInitialConfig() { return initialConfig; } public int getTickTime() { return tickTime; } public int getMaxClientCnxns() { return maxClientCnxns; } public int getMinSessionTimeout() { return minSessionTimeout; } public int getMaxSessionTimeout() { return maxSessionTimeout; } public String getMetricsProviderClassName() { return metricsProviderClassName; } public Properties getMetricsProviderConfiguration() { return metricsProviderConfiguration; } public boolean areLocalSessionsEnabled() { return localSessionsEnabled; } public boolean isLocalSessionsUpgradingEnabled() { return localSessionsUpgradingEnabled; } public boolean isSslQuorum() { return sslQuorum; } public boolean shouldUsePortUnification() { return shouldUsePortUnification; } public int getClientPortListenBacklog() { return clientPortListenBacklog; } public int getInitLimit() { return initLimit; } public int getSyncLimit() { return syncLimit; } public int getConnectToLearnerMasterLimit() { return connectToLearnerMasterLimit; } public int getElectionAlg() { return electionAlg; } public int getElectionPort() { return electionPort; } public int getSnapRetainCount() { return snapRetainCount; } public int getPurgeInterval() { return purgeInterval; } public boolean getSyncEnabled() { return syncEnabled; } public QuorumVerifier getQuorumVerifier() { return quorumVerifier; } public QuorumVerifier getLastSeenQuorumVerifier() { return lastSeenQuorumVerifier; } public Map getServers() { // returns all configuration servers -- participants and observers return Collections.unmodifiableMap(quorumVerifier.getAllMembers()); } public long getJvmPauseInfoThresholdMs() { return jvmPauseInfoThresholdMs; } public long getJvmPauseWarnThresholdMs() { return jvmPauseWarnThresholdMs; } public long getJvmPauseSleepTimeMs() { return jvmPauseSleepTimeMs; } public boolean isJvmPauseMonitorToRun() { return jvmPauseMonitorToRun; } public long getServerId() { return serverId; } public boolean isDistributed() { return quorumVerifier != null && (!standaloneEnabled || quorumVerifier.getVotingMembers().size() > 1); } public LearnerType getPeerType() { return peerType; } public String getConfigFilename() { return configFileStr; } public Boolean getQuorumListenOnAllIPs() { return quorumListenOnAllIPs; } public boolean isMultiAddressEnabled() { return multiAddressEnabled; } public boolean isMultiAddressReachabilityCheckEnabled() { return multiAddressReachabilityCheckEnabled; } public int getMultiAddressReachabilityCheckTimeoutMs() { return multiAddressReachabilityCheckTimeoutMs; } public static boolean isStandaloneEnabled() { return standaloneEnabled; } public static void setStandaloneEnabled(boolean enabled) { standaloneEnabled = enabled; } public static boolean isReconfigEnabled() { return reconfigEnabled; } public static void setReconfigEnabled(boolean enabled) { reconfigEnabled = enabled; } private boolean parseBoolean(String key, String value) throws ConfigException { if (value.equalsIgnoreCase("true")) { return true; } else if (value.equalsIgnoreCase("false")) { return false; } else { throw new ConfigException("Invalid option " + value + " for " + key + ". Choose 'true' or 'false.'"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032722 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerMain.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumPeerM0100644 0000000 0000000 00000030624 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import javax.management.JMException; import javax.security.sasl.SaslException; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.audit.ZKAuditProvider; import org.apache.zookeeper.jmx.ManagedUtil; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException; import org.apache.zookeeper.metrics.impl.MetricsProviderBootstrap; import org.apache.zookeeper.server.DatadirCleanupManager; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.FileTxnSnapLog.DatadirException; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.util.JvmPauseMonitor; import org.apache.zookeeper.util.ServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * *

    Configuration file

    * * When the main() method of this class is used to start the program, the first * argument is used as a path to the config file, which will be used to obtain * configuration information. This file is a Properties file, so keys and * values are separated by equals (=) and the key/value pairs are separated * by new lines. The following is a general summary of keys used in the * configuration file. For full details on this see the documentation in * docs/index.html *
      *
    1. dataDir - The directory where the ZooKeeper data is stored.
    2. *
    3. dataLogDir - The directory where the ZooKeeper transaction log is stored.
    4. *
    5. clientPort - The port used to communicate with clients.
    6. *
    7. tickTime - The duration of a tick in milliseconds. This is the basic * unit of time in ZooKeeper.
    8. *
    9. initLimit - The maximum number of ticks that a follower will wait to * initially synchronize with a leader.
    10. *
    11. syncLimit - The maximum number of ticks that a follower will wait for a * message (including heartbeats) from the leader.
    12. *
    13. server.id - This is the host:port[:port] that the server with the * given id will use for the quorum protocol.
    14. *
    * In addition to the config file. There is a file in the data directory called * "myid" that contains the server id as an ASCII decimal value. * */ @InterfaceAudience.Public public class QuorumPeerMain { private static final Logger LOG = LoggerFactory.getLogger(QuorumPeerMain.class); private static final String USAGE = "Usage: QuorumPeerMain configfile"; protected QuorumPeer quorumPeer; /** * To start the replicated server specify the configuration file name on * the command line. * @param args path to the configfile */ public static void main(String[] args) { QuorumPeerMain main = new QuorumPeerMain(); try { main.initializeAndRun(args); } catch (IllegalArgumentException e) { LOG.error("Invalid arguments, exiting abnormally", e); LOG.info(USAGE); System.err.println(USAGE); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } catch (ConfigException e) { LOG.error("Invalid config, exiting abnormally", e); System.err.println("Invalid config, exiting abnormally"); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.INVALID_INVOCATION.getValue()); } catch (DatadirException e) { LOG.error("Unable to access datadir, exiting abnormally", e); System.err.println("Unable to access datadir, exiting abnormally"); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue()); } catch (AdminServerException e) { LOG.error("Unable to start AdminServer, exiting abnormally", e); System.err.println("Unable to start AdminServer, exiting abnormally"); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.ERROR_STARTING_ADMIN_SERVER.getValue()); } catch (Exception e) { LOG.error("Unexpected exception, exiting abnormally", e); ZKAuditProvider.addServerStartFailureAuditLog(); ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue()); } LOG.info("Exiting normally"); ServiceUtils.requestSystemExit(ExitCode.EXECUTION_FINISHED.getValue()); } protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServerException { QuorumPeerConfig config = new QuorumPeerConfig(); if (args.length == 1) { config.parse(args[0]); } // Start and schedule the the purge task DatadirCleanupManager purgeMgr = new DatadirCleanupManager( config.getDataDir(), config.getDataLogDir(), config.getSnapRetainCount(), config.getPurgeInterval()); purgeMgr.start(); if (args.length == 1 && config.isDistributed()) { runFromConfig(config); } else { LOG.warn("Either no config or no quorum defined in config, running in standalone mode"); // there is only server in the quorum -- run as standalone ZooKeeperServerMain.main(args); } } public void runFromConfig(QuorumPeerConfig config) throws IOException, AdminServerException { try { ManagedUtil.registerLog4jMBeans(); } catch (JMException e) { LOG.warn("Unable to register log4j JMX control", e); } LOG.info("Starting quorum peer, myid=" + config.getServerId()); final MetricsProvider metricsProvider; try { metricsProvider = MetricsProviderBootstrap.startMetricsProvider( config.getMetricsProviderClassName(), config.getMetricsProviderConfiguration()); } catch (MetricsProviderLifeCycleException error) { throw new IOException("Cannot boot MetricsProvider " + config.getMetricsProviderClassName(), error); } try { ServerMetrics.metricsProviderInitialized(metricsProvider); ProviderRegistry.initialize(); ServerCnxnFactory cnxnFactory = null; ServerCnxnFactory secureCnxnFactory = null; if (config.getClientPortAddress() != null) { cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(config.getClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), false); } if (config.getSecureClientPortAddress() != null) { secureCnxnFactory = ServerCnxnFactory.createFactory(); secureCnxnFactory.configure(config.getSecureClientPortAddress(), config.getMaxClientCnxns(), config.getClientPortListenBacklog(), true); } quorumPeer = getQuorumPeer(); quorumPeer.setTxnFactory(new FileTxnSnapLog(config.getDataLogDir(), config.getDataDir())); quorumPeer.enableLocalSessions(config.areLocalSessionsEnabled()); quorumPeer.enableLocalSessionsUpgrading(config.isLocalSessionsUpgradingEnabled()); //quorumPeer.setQuorumPeers(config.getAllMembers()); quorumPeer.setElectionType(config.getElectionAlg()); quorumPeer.setMyid(config.getServerId()); quorumPeer.setTickTime(config.getTickTime()); quorumPeer.setMinSessionTimeout(config.getMinSessionTimeout()); quorumPeer.setMaxSessionTimeout(config.getMaxSessionTimeout()); quorumPeer.setInitLimit(config.getInitLimit()); quorumPeer.setSyncLimit(config.getSyncLimit()); quorumPeer.setConnectToLearnerMasterLimit(config.getConnectToLearnerMasterLimit()); quorumPeer.setObserverMasterPort(config.getObserverMasterPort()); quorumPeer.setConfigFileName(config.getConfigFilename()); quorumPeer.setClientPortListenBacklog(config.getClientPortListenBacklog()); quorumPeer.setZKDatabase(new ZKDatabase(quorumPeer.getTxnFactory())); quorumPeer.setQuorumVerifier(config.getQuorumVerifier(), false); if (config.getLastSeenQuorumVerifier() != null) { quorumPeer.setLastSeenQuorumVerifier(config.getLastSeenQuorumVerifier(), false); } quorumPeer.initConfigInZKDatabase(); quorumPeer.setCnxnFactory(cnxnFactory); quorumPeer.setSecureCnxnFactory(secureCnxnFactory); quorumPeer.setSslQuorum(config.isSslQuorum()); quorumPeer.setUsePortUnification(config.shouldUsePortUnification()); quorumPeer.setLearnerType(config.getPeerType()); quorumPeer.setSyncEnabled(config.getSyncEnabled()); quorumPeer.setQuorumListenOnAllIPs(config.getQuorumListenOnAllIPs()); if (config.sslQuorumReloadCertFiles) { quorumPeer.getX509Util().enableCertFileReloading(); } quorumPeer.setMultiAddressEnabled(config.isMultiAddressEnabled()); quorumPeer.setMultiAddressReachabilityCheckEnabled(config.isMultiAddressReachabilityCheckEnabled()); quorumPeer.setMultiAddressReachabilityCheckTimeoutMs(config.getMultiAddressReachabilityCheckTimeoutMs()); // sets quorum sasl authentication configurations quorumPeer.setQuorumSaslEnabled(config.quorumEnableSasl); if (quorumPeer.isQuorumSaslAuthEnabled()) { quorumPeer.setQuorumServerSaslRequired(config.quorumServerRequireSasl); quorumPeer.setQuorumLearnerSaslRequired(config.quorumLearnerRequireSasl); quorumPeer.setQuorumServicePrincipal(config.quorumServicePrincipal); quorumPeer.setQuorumServerLoginContext(config.quorumServerLoginContext); quorumPeer.setQuorumLearnerLoginContext(config.quorumLearnerLoginContext); } quorumPeer.setQuorumCnxnThreadsSize(config.quorumCnxnThreadsSize); quorumPeer.initialize(); if (config.jvmPauseMonitorToRun) { quorumPeer.setJvmPauseMonitor(new JvmPauseMonitor(config)); } quorumPeer.start(); ZKAuditProvider.addZKStartStopAuditLog(); quorumPeer.join(); } catch (InterruptedException e) { // warn, but generally this is ok LOG.warn("Quorum Peer interrupted", e); } finally { try { metricsProvider.stop(); } catch (Throwable error) { LOG.warn("Error while stopping metrics", error); } } } // @VisibleForTesting protected QuorumPeer getQuorumPeer() throws SaslException { return new QuorumPeer(); } /** * Shutdowns properly the service, this method is not a public API. */ public void close() { if (quorumPeer != null) { try { quorumPeer.shutdown(); } finally { quorumPeer = null; } } } @Override public String toString() { QuorumPeer peer = quorumPeer; return peer == null ? "" : peer.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000163 15051152474 032717 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumStats.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumStats0100644 0000000 0000000 00000004367 15051152474 034374 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; public class QuorumStats { private final Provider provider; public interface Provider { String UNKNOWN_STATE = "unknown"; String LOOKING_STATE = "leaderelection"; String LEADING_STATE = "leading"; String FOLLOWING_STATE = "following"; String OBSERVING_STATE = "observing"; String[] getQuorumPeers(); String getServerState(); } protected QuorumStats(Provider provider) { this.provider = provider; } public String getServerState() { return provider.getServerState(); } public String[] getQuorumPeers() { return provider.getQuorumPeers(); } @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); String state = getServerState(); if (state.equals(Provider.LEADING_STATE)) { sb.append("Followers:"); for (String f : getQuorumPeers()) { sb.append(" ").append(f); } sb.append("\n"); } else if (state.equals(Provider.FOLLOWING_STATE) || state.equals(Provider.OBSERVING_STATE)) { sb.append("Leader: "); String[] ldr = getQuorumPeers(); if (ldr.length > 0) { sb.append(ldr[0]); } else { sb.append("not connected"); } sb.append("\n"); } return sb.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032722 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/QuorumZooKe0100644 0000000 0000000 00000020415 15051152474 034315 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.io.PrintWriter; import java.util.Objects; import java.util.function.BiConsumer; import java.util.stream.Collectors; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.MultiOperationRecord; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.txn.CreateSessionTxn; /** * Abstract base class for all ZooKeeperServers that participate in * a quorum. */ public abstract class QuorumZooKeeperServer extends ZooKeeperServer { public final QuorumPeer self; protected UpgradeableSessionTracker upgradeableSessionTracker; protected QuorumZooKeeperServer(FileTxnSnapLog logFactory, int tickTime, int minSessionTimeout, int maxSessionTimeout, int listenBacklog, ZKDatabase zkDb, QuorumPeer self) { super(logFactory, tickTime, minSessionTimeout, maxSessionTimeout, listenBacklog, zkDb, self.getInitialConfig(), self.isReconfigEnabled()); this.self = self; } @Override protected void startSessionTracker() { upgradeableSessionTracker = (UpgradeableSessionTracker) sessionTracker; upgradeableSessionTracker.start(); } public Request checkUpgradeSession(Request request) throws IOException, KeeperException { if (request.isThrottled()) { return null; } // If this is a request for a local session and it is to // create an ephemeral node, then upgrade the session and return // a new session request for the leader. // This is called by the request processor thread (either follower // or observer request processor), which is unique to a learner. // So will not be called concurrently by two threads. if ((request.type != OpCode.create && request.type != OpCode.create2 && request.type != OpCode.multi) || !upgradeableSessionTracker.isLocalSession(request.sessionId)) { return null; } if (OpCode.multi == request.type) { MultiOperationRecord multiTransactionRecord = request.readRequestRecord(MultiOperationRecord::new); boolean containsEphemeralCreate = false; for (Op op : multiTransactionRecord) { if (op.getType() == OpCode.create || op.getType() == OpCode.create2) { CreateRequest createRequest = (CreateRequest) op.toRequestRecord(); CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags()); if (createMode.isEphemeral()) { containsEphemeralCreate = true; break; } } } if (!containsEphemeralCreate) { return null; } } else { CreateRequest createRequest = request.readRequestRecord(CreateRequest::new); CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags()); if (!createMode.isEphemeral()) { return null; } } // Uh oh. We need to upgrade before we can proceed. if (!self.isLocalSessionsUpgradingEnabled()) { throw new KeeperException.EphemeralOnLocalSessionException(); } return makeUpgradeRequest(request.sessionId); } private Request makeUpgradeRequest(long sessionId) { // Make sure to atomically check local session status, upgrade // session, and make the session creation request. This is to // avoid another thread upgrading the session in parallel. synchronized (upgradeableSessionTracker) { if (upgradeableSessionTracker.isLocalSession(sessionId)) { int timeout = upgradeableSessionTracker.upgradeSession(sessionId); CreateSessionTxn txn = new CreateSessionTxn(timeout); return new Request(null, sessionId, 0, OpCode.createSession, RequestRecord.fromRecord(txn), null); } } return null; } /** * Implements the SessionUpgrader interface, * * @param sessionId */ public void upgrade(long sessionId) { Request request = makeUpgradeRequest(sessionId); if (request != null) { LOG.info("Upgrading session 0x{}", Long.toHexString(sessionId)); // This must be a global request submitRequest(request); } } @Override protected void setLocalSessionFlag(Request si) { // We need to set isLocalSession to tree for these type of request // so that the request processor can process them correctly. switch (si.type) { case OpCode.createSession: if (self.areLocalSessionsEnabled()) { // All new sessions local by default. si.setLocalSession(true); } break; case OpCode.closeSession: String reqType = "global"; if (upgradeableSessionTracker.isLocalSession(si.sessionId)) { si.setLocalSession(true); reqType = "local"; } LOG.info("Submitting {} closeSession request for session 0x{}", reqType, Long.toHexString(si.sessionId)); break; default: break; } } @Override public void dumpConf(PrintWriter pwriter) { super.dumpConf(pwriter); pwriter.print("initLimit="); pwriter.println(self.getInitLimit()); pwriter.print("syncLimit="); pwriter.println(self.getSyncLimit()); pwriter.print("electionAlg="); pwriter.println(self.getElectionType()); pwriter.print("electionPort="); pwriter.println(self.getElectionAddress().getAllPorts() .stream().map(Objects::toString).collect(Collectors.joining("|"))); pwriter.print("quorumPort="); pwriter.println(self.getQuorumAddress().getAllPorts() .stream().map(Objects::toString).collect(Collectors.joining("|"))); pwriter.print("peerType="); pwriter.println(self.getLearnerType().ordinal()); pwriter.println("membership: "); pwriter.print(self.getQuorumVerifier().toString()); } @Override protected void setState(State state) { this.state = state; } @Override protected void registerMetrics() { super.registerMetrics(); MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.registerGauge("quorum_size", () -> { return self.getQuorumSize(); }); } @Override protected void unregisterMetrics() { super.unregisterMetrics(); MetricsContext rootContext = ServerMetrics.getMetrics().getMetricsProvider().getRootContext(); rootContext.unregisterGauge("quorum_size"); } @Override public void dumpMonitorValues(BiConsumer response) { super.dumpMonitorValues(response); response.accept("peer_state", self.getDetailedPeerState()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032720 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ReadOnlyBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ReadOnlyBea0100644 0000000 0000000 00000002322 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServerBean; /** * ReadOnly MX Bean interface, implemented by ReadOnlyBean * */ public class ReadOnlyBean extends ZooKeeperServerBean { public ReadOnlyBean(ZooKeeperServer zks) { super(zks); } public String getName() { return "ReadOnlyServer"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032707 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ReadOnlyRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ReadOnlyReq0100644 0000000 0000000 00000011557 15051152474 034251 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ZooKeeperCriticalThread; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooTrace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This processor is at the beginning of the ReadOnlyZooKeeperServer's * processors chain. All it does is, it passes read-only operations (e.g. * OpCode.getData, OpCode.exists) through to the next processor, but drops * state-changing operations (e.g. OpCode.create, OpCode.setData). */ public class ReadOnlyRequestProcessor extends ZooKeeperCriticalThread implements RequestProcessor { private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyRequestProcessor.class); private final LinkedBlockingQueue queuedRequests = new LinkedBlockingQueue<>(); private volatile boolean finished = false; private final RequestProcessor nextProcessor; private final ZooKeeperServer zks; public ReadOnlyRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { super("ReadOnlyRequestProcessor:" + zks.getServerId(), zks.getZooKeeperServerListener()); this.zks = zks; this.nextProcessor = nextProcessor; } public void run() { try { while (!finished) { Request request = queuedRequests.take(); // log request if (LOG.isTraceEnabled()) { long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK; if (request.type == OpCode.ping) { traceMask = ZooTrace.CLIENT_PING_TRACE_MASK; } ZooTrace.logRequest(LOG, traceMask, 'R', request, ""); } if (Request.requestOfDeath == request) { break; } // filter read requests switch (request.type) { case OpCode.sync: case OpCode.create: case OpCode.create2: case OpCode.createTTL: case OpCode.createContainer: case OpCode.delete: case OpCode.deleteContainer: case OpCode.setData: case OpCode.reconfig: case OpCode.setACL: case OpCode.multi: case OpCode.check: sendErrorResponse(request); continue; case OpCode.closeSession: case OpCode.createSession: if (!request.isLocalSession()) { sendErrorResponse(request); continue; } } // proceed to the next processor if (nextProcessor != null) { nextProcessor.processRequest(request); } } } catch (Exception e) { handleException(this.getName(), e); } LOG.info("ReadOnlyRequestProcessor exited loop!"); } private void sendErrorResponse(Request request) { ReplyHeader hdr = new ReplyHeader( request.cxid, zks.getZKDatabase().getDataTreeLastProcessedZxid(), Code.NOTREADONLY.intValue()); try { request.cnxn.sendResponse(hdr, null, null); } catch (IOException e) { LOG.error("IO exception while sending response", e); } } @Override public void processRequest(Request request) { if (!finished) { queuedRequests.add(request); } } @Override public void shutdown() { finished = true; queuedRequests.clear(); queuedRequests.add(Request.requestOfDeath); nextProcessor.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032724 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ReadOnlyZoo0100644 0000000 0000000 00000017642 15051152474 034272 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.IOException; import java.io.PrintWriter; import java.util.Objects; import java.util.stream.Collectors; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.DataTreeBean; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.PrepRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServerBean; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; /** * A ZooKeeperServer which comes into play when peer is partitioned from the * majority. Handles read-only clients, but drops connections from not-read-only * ones. *

    * The very first processor in the chain of request processors is a * ReadOnlyRequestProcessor which drops state-changing requests. */ public class ReadOnlyZooKeeperServer extends ZooKeeperServer { protected final QuorumPeer self; private volatile boolean shutdown = false; ReadOnlyZooKeeperServer(FileTxnSnapLog logFactory, QuorumPeer self, ZKDatabase zkDb) { super( logFactory, self.tickTime, self.minSessionTimeout, self.maxSessionTimeout, self.clientPortListenBacklog, zkDb, self.getInitialConfig(), self.isReconfigEnabled()); this.self = self; } @Override protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); RequestProcessor prepProcessor = new PrepRequestProcessor(this, finalProcessor); ((PrepRequestProcessor) prepProcessor).start(); firstProcessor = new ReadOnlyRequestProcessor(this, prepProcessor); ((ReadOnlyRequestProcessor) firstProcessor).start(); } @Override public synchronized void startup() { // check to avoid startup follows shutdown if (shutdown) { LOG.warn("Not starting Read-only server as startup follows shutdown!"); return; } registerJMX(new ReadOnlyBean(this), self.jmxLocalPeerBean); super.startup(); self.setZooKeeperServer(this); self.adminServer.setZooKeeperServer(this); LOG.info("Read-only server started"); } @Override public void createSessionTracker() { sessionTracker = new LearnerSessionTracker( this, getZKDatabase().getSessionWithTimeOuts(), this.tickTime, self.getMyId(), self.areLocalSessionsEnabled(), getZooKeeperServerListener()); } @Override protected void startSessionTracker() { ((LearnerSessionTracker) sessionTracker).start(); } @Override protected void setLocalSessionFlag(Request si) { switch (si.type) { case OpCode.createSession: if (self.areLocalSessionsEnabled()) { si.setLocalSession(true); } break; case OpCode.closeSession: if (((UpgradeableSessionTracker) sessionTracker).isLocalSession(si.sessionId)) { si.setLocalSession(true); } else { LOG.warn("Submitting global closeSession request for session 0x{} in ReadOnly mode", Long.toHexString(si.sessionId)); } break; default: break; } } @Override protected void validateSession(ServerCnxn cnxn, long sessionId) throws IOException { if (((LearnerSessionTracker) sessionTracker).isGlobalSession(sessionId)) { String msg = "Refusing global session reconnection in RO mode " + cnxn.getRemoteSocketAddress(); LOG.info(msg); throw new ServerCnxn.CloseRequestException(msg, ServerCnxn.DisconnectReason.RENEW_GLOBAL_SESSION_IN_RO_MODE); } } @Override protected void registerJMX() { // register with JMX try { jmxDataTreeBean = new DataTreeBean(getZKDatabase().getDataTree()); MBeanRegistry.getInstance().register(jmxDataTreeBean, jmxServerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxDataTreeBean = null; } } public void registerJMX(ZooKeeperServerBean serverBean, LocalPeerBean localPeerBean) { // register with JMX try { jmxServerBean = serverBean; MBeanRegistry.getInstance().register(serverBean, localPeerBean); } catch (Exception e) { LOG.warn("Failed to register with JMX", e); jmxServerBean = null; } } @Override protected void unregisterJMX() { // unregister from JMX try { if (jmxDataTreeBean != null) { MBeanRegistry.getInstance().unregister(jmxDataTreeBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxDataTreeBean = null; } protected void unregisterJMX(ZooKeeperServer zks) { // unregister from JMX try { if (jmxServerBean != null) { MBeanRegistry.getInstance().unregister(jmxServerBean); } } catch (Exception e) { LOG.warn("Failed to unregister with JMX", e); } jmxServerBean = null; } @Override public String getState() { return "read-only"; } /** * Returns the id of the associated QuorumPeer, which will do for a unique * id of this server. */ @Override public long getServerId() { return self.getMyId(); } @Override protected void shutdownComponents() { shutdown = true; unregisterJMX(this); // set peer's server to null self.setZooKeeperServer(null); // clear all the connections self.closeAllConnections(); self.adminServer.setZooKeeperServer(null); // shutdown the server itself super.shutdownComponents(); } @Override public void dumpConf(PrintWriter pwriter) { super.dumpConf(pwriter); pwriter.print("initLimit="); pwriter.println(self.getInitLimit()); pwriter.print("syncLimit="); pwriter.println(self.getSyncLimit()); pwriter.print("electionAlg="); pwriter.println(self.getElectionType()); pwriter.print("electionPort="); pwriter.println(self.getElectionAddress().getAllPorts() .stream().map(Objects::toString).collect(Collectors.joining("|"))); pwriter.print("quorumPort="); pwriter.println(self.getQuorumAddress().getAllPorts() .stream().map(Objects::toString).collect(Collectors.joining("|"))); pwriter.print("peerType="); pwriter.println(self.getLearnerType().ordinal()); } @Override protected void setState(State state) { this.state = state; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032722 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/RemotePeerBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/RemotePeerB0100644 0000000 0000000 00000004567 15051152474 034240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.common.NetUtils.formatInetAddr; import java.util.stream.Collectors; import org.apache.zookeeper.common.NetUtils; import org.apache.zookeeper.jmx.ZKMBeanInfo; /** * A remote peer bean only provides limited information about the remote peer, * and the peer cannot be managed remotely. */ public class RemotePeerBean implements RemotePeerMXBean, ZKMBeanInfo { private QuorumPeer.QuorumServer peer; private final QuorumPeer localPeer; public RemotePeerBean(QuorumPeer localPeer, QuorumPeer.QuorumServer peer) { this.peer = peer; this.localPeer = localPeer; } public void setQuorumServer(QuorumPeer.QuorumServer peer) { this.peer = peer; } public String getName() { return "replica." + peer.id; } public boolean isHidden() { return false; } public String getQuorumAddress() { return peer.addr.getAllAddresses().stream().map(NetUtils::formatInetAddr) .collect(Collectors.joining("|")); } public String getElectionAddress() { return peer.electionAddr.getAllAddresses().stream().map(NetUtils::formatInetAddr) .collect(Collectors.joining("|")); } public String getClientAddress() { if (null == peer.clientAddr) { return ""; } return formatInetAddr(peer.clientAddr); } public String getLearnerType() { return peer.type.toString(); } @Override public boolean isLeader() { return localPeer.isLeader(peer.getId()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000170 15051152474 032715 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/RemotePeerMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/RemotePeerM0100644 0000000 0000000 00000002640 15051152474 034241 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * A proxy for a remote quorum peer. */ public interface RemotePeerMXBean { /** * @return name of the peer */ String getName(); /** * @return IP address of the quorum peer */ String getQuorumAddress(); /** * @return the election address */ String getElectionAddress(); /** * @return the client address */ String getClientAddress(); /** * @return the learner type */ String getLearnerType(); /** * @return true if the peer is the current leader */ boolean isLeader(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032724 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/SendAckRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/SendAckRequ0100644 0000000 0000000 00000004401 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.Flushable; import java.io.IOException; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.ServerMetrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SendAckRequestProcessor implements RequestProcessor, Flushable { private static final Logger LOG = LoggerFactory.getLogger(SendAckRequestProcessor.class); Learner learner; SendAckRequestProcessor(Learner peer) { this.learner = peer; } public void processRequest(Request si) { if (si.type != OpCode.sync) { QuorumPacket qp = new QuorumPacket(Leader.ACK, si.getHdr().getZxid(), null, null); try { si.logLatency(ServerMetrics.getMetrics().PROPOSAL_ACK_CREATION_LATENCY); learner.writePacket(qp, false); } catch (IOException e) { LOG.warn("Closing connection to leader, exception during packet send", e); learner.closeSocket(); } } } public void flush() throws IOException { try { learner.writePacket(null, true); } catch (IOException e) { LOG.warn("Closing connection to leader, exception during packet send", e); learner.closeSocket(); } } public void shutdown() { // Nothing needed } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000162 15051152474 032716 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ServerBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ServerBean.0100644 0000000 0000000 00000002346 15051152474 034172 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.Date; import org.apache.zookeeper.jmx.ZKMBeanInfo; /** * An abstract base class for the leader and follower MBeans. */ public abstract class ServerBean implements ServerMXBean, ZKMBeanInfo { private final Date startTime = new Date(); public boolean isHidden() { return false; } public String getStartTime() { return startTime.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032720 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ServerMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/ServerMXBea0100644 0000000 0000000 00000002074 15051152474 034201 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * A quorum server MBean. */ public interface ServerMXBean { /** * @return name of the server MBean */ String getName(); /** * @return the start time the server */ String getStartTime(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032720 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/StateSummary.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/StateSummar0100644 0000000 0000000 00000003521 15051152474 034321 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * This class encapsulates the state comparison logic. Specifically, * how two different states are compared. */ public class StateSummary { private long currentEpoch; private long lastZxid; public StateSummary(long currentEpoch, long lastZxid) { this.currentEpoch = currentEpoch; this.lastZxid = lastZxid; } public long getCurrentEpoch() { return currentEpoch; } public long getLastZxid() { return lastZxid; } public boolean isMoreRecentThan(StateSummary ss) { return (currentEpoch > ss.currentEpoch) || (currentEpoch == ss.currentEpoch && lastZxid > ss.lastZxid); } @Override public boolean equals(Object obj) { if (!(obj instanceof StateSummary)) { return false; } StateSummary ss = (StateSummary) obj; return currentEpoch == ss.currentEpoch && lastZxid == ss.lastZxid; } @Override public int hashCode() { return (int) (currentEpoch ^ lastZxid); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032722 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/SyncThrottleException.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/SyncThrottl0100644 0000000 0000000 00000003247 15051152474 034356 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; /** * Thrown when a {@link Leader} has too many concurrent syncs being sent * to observers. * * @see LearnerSyncThrottler * */ public class SyncThrottleException extends Exception { private static final long serialVersionUID = 1L; public SyncThrottleException(int concurrentSyncNumber, int throttleThreshold, LearnerSyncThrottler.SyncType syncType) { super(getMessage(concurrentSyncNumber, throttleThreshold, syncType)); } private static String getMessage(int concurrentSyncNumber, int throttleThreshold, LearnerSyncThrottler.SyncType syncType) { return String.format("new %s sync would make %d concurrently in progress; maximum is %d", syncType.toString().toLowerCase(), concurrentSyncNumber, throttleThreshold); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032721 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/SyncedLearnerTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/SyncedLearn0100644 0000000 0000000 00000005561 15051152474 034271 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.ArrayList; import java.util.HashSet; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; public class SyncedLearnerTracker { protected ArrayList qvAcksetPairs = new ArrayList<>(); public void addQuorumVerifier(QuorumVerifier qv) { qvAcksetPairs.add(new QuorumVerifierAcksetPair(qv, new HashSet(qv.getVotingMembers().size()))); } public boolean addAck(Long sid) { boolean change = false; for (QuorumVerifierAcksetPair qvAckset : qvAcksetPairs) { if (qvAckset.getQuorumVerifier().getVotingMembers().containsKey(sid)) { qvAckset.getAckset().add(sid); change = true; } } return change; } public boolean hasSid(long sid) { for (QuorumVerifierAcksetPair qvAckset : qvAcksetPairs) { if (!qvAckset.getQuorumVerifier().getVotingMembers().containsKey(sid)) { return false; } } return true; } public boolean hasAllQuorums() { for (QuorumVerifierAcksetPair qvAckset : qvAcksetPairs) { if (!qvAckset.getQuorumVerifier().containsQuorum(qvAckset.getAckset())) { return false; } } return true; } public String ackSetsToString() { StringBuilder sb = new StringBuilder(); for (QuorumVerifierAcksetPair qvAckset : qvAcksetPairs) { sb.append(qvAckset.getAckset().toString()).append(","); } return sb.substring(0, sb.length() - 1); } public static class QuorumVerifierAcksetPair { private final QuorumVerifier qv; private final HashSet ackset; public QuorumVerifierAcksetPair(QuorumVerifier qv, HashSet ackset) { this.qv = qv; this.ackset = ackset; } public QuorumVerifier getQuorumVerifier() { return this.qv; } public HashSet getAckset() { return this.ackset; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000173 15051152474 032720 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/UnifiedServerSocket.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/UnifiedServ0100644 0000000 0000000 00000076753 15051152474 034320 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import io.netty.buffer.Unpooled; import io.netty.handler.ssl.SslHandler; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.nio.channels.SocketChannel; import javax.net.ssl.SSLSocket; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.common.X509Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A ServerSocket that can act either as a regular ServerSocket, as a SSLServerSocket, or as both, depending on * the constructor parameters and on the type of client (TLS or plaintext) that connects to it. * The constructors have the same signature as constructors of ServerSocket, with the addition of two parameters * at the beginning: *

      *
    • X509Util - provides the SSL context to construct a secure socket when a client connects with TLS.
    • *
    • boolean allowInsecureConnection - when true, acts as a hybrid server socket (plaintext / TLS). When * false, acts as a SSLServerSocket (rejects plaintext connections).
    • *
    * The !allowInsecureConnection mode is needed so we can update the SSLContext (in particular, the * key store and/or trust store) without having to re-create the server socket. By starting with a plaintext socket * and delaying the upgrade to TLS until after a client has connected and begins a handshake, we can keep the same * UnifiedServerSocket instance around, and replace the default SSLContext in the provided X509Util when the key store * and/or trust store file changes on disk. */ public class UnifiedServerSocket extends ServerSocket { private static final Logger LOG = LoggerFactory.getLogger(UnifiedServerSocket.class); private X509Util x509Util; private final boolean allowInsecureConnection; /** * Creates an unbound unified server socket by calling {@link ServerSocket#ServerSocket()}. * Secure client connections will be upgraded to TLS once this socket detects the ClientHello message (start of a * TLS handshake). Plaintext client connections will either be accepted or rejected depending on the value of * the allowInsecureConnection parameter. * @param x509Util the X509Util that provides the SSLContext to use for secure connections. * @param allowInsecureConnection if true, accept plaintext connections, otherwise close them. * @throws IOException if {@link ServerSocket#ServerSocket()} throws. */ public UnifiedServerSocket(X509Util x509Util, boolean allowInsecureConnection) throws IOException { super(); this.x509Util = x509Util; this.allowInsecureConnection = allowInsecureConnection; } /** * Creates a unified server socket bound to the specified port by calling {@link ServerSocket#ServerSocket(int)}. * Secure client connections will be upgraded to TLS once this socket detects the ClientHello message (start of a * TLS handshake). Plaintext client connections will either be accepted or rejected depending on the value of * the allowInsecureConnection parameter. * @param x509Util the X509Util that provides the SSLContext to use for secure connections. * @param allowInsecureConnection if true, accept plaintext connections, otherwise close them. * @param port the port number, or {@code 0} to use a port number that is automatically allocated. * @throws IOException if {@link ServerSocket#ServerSocket(int)} throws. */ public UnifiedServerSocket(X509Util x509Util, boolean allowInsecureConnection, int port) throws IOException { super(port); this.x509Util = x509Util; this.allowInsecureConnection = allowInsecureConnection; } /** * Creates a unified server socket bound to the specified port, with the specified backlog, by calling * {@link ServerSocket#ServerSocket(int, int)}. * Secure client connections will be upgraded to TLS once this socket detects the ClientHello message (start of a * TLS handshake). Plaintext client connections will either be accepted or rejected depending on the value of * the allowInsecureConnection parameter. * @param x509Util the X509Util that provides the SSLContext to use for secure connections. * @param allowInsecureConnection if true, accept plaintext connections, otherwise close them. * @param port the port number, or {@code 0} to use a port number that is automatically allocated. * @param backlog requested maximum length of the queue of incoming connections. * @throws IOException if {@link ServerSocket#ServerSocket(int, int)} throws. */ public UnifiedServerSocket(X509Util x509Util, boolean allowInsecureConnection, int port, int backlog) throws IOException { super(port, backlog); this.x509Util = x509Util; this.allowInsecureConnection = allowInsecureConnection; } /** * Creates a unified server socket bound to the specified port, with the specified backlog, and local IP address * to bind to, by calling {@link ServerSocket#ServerSocket(int, int, InetAddress)}. * Secure client connections will be upgraded to TLS once this socket detects the ClientHello message (start of a * TLS handshake). Plaintext client connections will either be accepted or rejected depending on the value of * the allowInsecureConnection parameter. * @param x509Util the X509Util that provides the SSLContext to use for secure connections. * @param allowInsecureConnection if true, accept plaintext connections, otherwise close them. * @param port the port number, or {@code 0} to use a port number that is automatically allocated. * @param backlog requested maximum length of the queue of incoming connections. * @param bindAddr the local InetAddress the server will bind to. * @throws IOException if {@link ServerSocket#ServerSocket(int, int, InetAddress)} throws. */ public UnifiedServerSocket(X509Util x509Util, boolean allowInsecureConnection, int port, int backlog, InetAddress bindAddr) throws IOException { super(port, backlog, bindAddr); this.x509Util = x509Util; this.allowInsecureConnection = allowInsecureConnection; } @Override public Socket accept() throws IOException { if (isClosed()) { throw new SocketException("Socket is closed"); } if (!isBound()) { throw new SocketException("Socket is not bound yet"); } final PrependableSocket prependableSocket = new PrependableSocket(null); implAccept(prependableSocket); return new UnifiedSocket(x509Util, allowInsecureConnection, prependableSocket); } /** * The result of calling accept() on a UnifiedServerSocket. This is a Socket that doesn't know if it's * using plaintext or SSL/TLS at the time when it is created. Calling a method that indicates a desire to * read or write from the socket will cause the socket to detect if the connected client is attempting * to establish a TLS or plaintext connection. This is done by doing a blocking read of 5 bytes off the * socket and checking if the bytes look like the start of a TLS ClientHello message. If it looks like * the client is attempting to connect with TLS, the internal socket is upgraded to a SSLSocket. If not, * any bytes read from the socket are pushed back to the input stream, and the socket continues * to be treated as a plaintext socket. * * The methods that trigger this behavior are: *
      *
    • {@link UnifiedSocket#getInputStream()}
    • *
    • {@link UnifiedSocket#getOutputStream()}
    • *
    • {@link UnifiedSocket#sendUrgentData(int)}
    • *
    * * Calling other socket methods (i.e option setters such as {@link Socket#setTcpNoDelay(boolean)}) does * not trigger mode detection. * * Because detecting the mode is a potentially blocking operation, it should not be done in the * accepting thread. Attempting to read from or write to the socket in the accepting thread opens the * caller up to a denial-of-service attack, in which a client connects and then does nothing. This would * prevent any other clients from connecting. Passing the socket returned by accept() to a separate * thread which handles all read and write operations protects against this DoS attack. * * Callers can check if the socket has been upgraded to TLS by calling {@link UnifiedSocket#isSecureSocket()}, * and can get the underlying SSLSocket by calling {@link UnifiedSocket#getSslSocket()}. */ public static class UnifiedSocket extends Socket { private enum Mode { UNKNOWN, PLAINTEXT, TLS } private final X509Util x509Util; private final boolean allowInsecureConnection; private PrependableSocket prependableSocket; private SSLSocket sslSocket; private Mode mode; /** * Note: this constructor is intentionally private. The only intended caller is * {@link UnifiedServerSocket#accept()}. * * @param x509Util * @param allowInsecureConnection * @param prependableSocket */ private UnifiedSocket(X509Util x509Util, boolean allowInsecureConnection, PrependableSocket prependableSocket) { this.x509Util = x509Util; this.allowInsecureConnection = allowInsecureConnection; this.prependableSocket = prependableSocket; this.sslSocket = null; this.mode = Mode.UNKNOWN; } /** * Returns true if the socket mode has been determined to be TLS. * @return true if the mode is TLS, false if it is UNKNOWN or PLAINTEXT. */ public boolean isSecureSocket() { return mode == Mode.TLS; } /** * Returns true if the socket mode has been determined to be PLAINTEXT. * @return true if the mode is PLAINTEXT, false if it is UNKNOWN or TLS. */ public boolean isPlaintextSocket() { return mode == Mode.PLAINTEXT; } /** * Returns true if the socket mode is not yet known. * @return true if the mode is UNKNOWN, false if it is PLAINTEXT or TLS. */ public boolean isModeKnown() { return mode != Mode.UNKNOWN; } /** * Detects the socket mode, see comments at the top of the class for more details. This operation will block * for up to {@link X509Util#getSslHandshakeTimeoutMillis()} milliseconds and should not be called in the * accept() thread if possible. * @throws IOException */ private void detectMode() throws IOException { byte[] litmus = new byte[5]; int oldTimeout = -1; int bytesRead = 0; int newTimeout = x509Util.getSslHandshakeTimeoutMillis(); try { oldTimeout = prependableSocket.getSoTimeout(); prependableSocket.setSoTimeout(newTimeout); bytesRead = prependableSocket.getInputStream().read(litmus, 0, litmus.length); } catch (SocketTimeoutException e) { // Didn't read anything within the timeout, fallthrough and assume the connection is plaintext. LOG.warn("Socket mode detection timed out after {} ms, assuming PLAINTEXT", newTimeout); } finally { // restore socket timeout to the old value try { if (oldTimeout != -1) { prependableSocket.setSoTimeout(oldTimeout); } } catch (Exception e) { LOG.warn("Failed to restore old socket timeout value of {} ms", oldTimeout, e); } } if (bytesRead < 0) { // Got a EOF right away, definitely not using TLS. Fallthrough. bytesRead = 0; } if (bytesRead == litmus.length && SslHandler.isEncrypted(Unpooled.wrappedBuffer(litmus), false)) { try { sslSocket = x509Util.createSSLSocket(prependableSocket, litmus); } catch (X509Exception e) { throw new IOException("failed to create SSL context", e); } prependableSocket = null; mode = Mode.TLS; LOG.info( "Accepted TLS connection from {} - {} - {}", sslSocket.getRemoteSocketAddress(), sslSocket.getSession().getProtocol(), sslSocket.getSession().getCipherSuite()); } else if (allowInsecureConnection) { prependableSocket.prependToInputStream(litmus, 0, bytesRead); mode = Mode.PLAINTEXT; LOG.info("Accepted plaintext connection from {}", prependableSocket.getRemoteSocketAddress()); } else { prependableSocket.close(); mode = Mode.PLAINTEXT; throw new IOException("Blocked insecure connection attempt"); } } private Socket getSocketAllowUnknownMode() { if (isSecureSocket()) { return sslSocket; } else { // Note: mode is UNKNOWN or PLAINTEXT return prependableSocket; } } /** * Returns the underlying socket, detecting the socket mode if it is not yet known. This is a potentially * blocking operation and should not be called in the accept() thread. * @return the underlying socket, after the socket mode has been determined. * @throws IOException */ private Socket getSocket() throws IOException { if (!isModeKnown()) { detectMode(); } if (mode == Mode.TLS) { return sslSocket; } else { return prependableSocket; } } /** * Returns the underlying SSLSocket if the mode is TLS. If the mode is UNKNOWN, causes mode detection which is a * potentially blocking operation. If the mode ends up being PLAINTEXT, this will throw a SocketException, so * callers are advised to only call this method after checking that {@link UnifiedSocket#isSecureSocket()} * returned true. * @return the underlying SSLSocket if the mode is known to be TLS. * @throws IOException if detecting the socket mode fails * @throws SocketException if the mode is PLAINTEXT. */ public SSLSocket getSslSocket() throws IOException { if (!isModeKnown()) { detectMode(); } if (!isSecureSocket()) { throw new SocketException("Socket mode is not TLS"); } return sslSocket; } /** * See {@link Socket#connect(SocketAddress)}. Calling this method does not trigger mode detection. */ @Override public void connect(SocketAddress endpoint) throws IOException { getSocketAllowUnknownMode().connect(endpoint); } /** * See {@link Socket#connect(SocketAddress, int)}. Calling this method does not trigger mode detection. */ @Override public void connect(SocketAddress endpoint, int timeout) throws IOException { getSocketAllowUnknownMode().connect(endpoint, timeout); } /** * See {@link Socket#bind(SocketAddress)}. Calling this method does not trigger mode detection. */ @Override public void bind(SocketAddress bindpoint) throws IOException { getSocketAllowUnknownMode().bind(bindpoint); } /** * See {@link Socket#getInetAddress()}. Calling this method does not trigger mode detection. */ @Override public InetAddress getInetAddress() { return getSocketAllowUnknownMode().getInetAddress(); } /** * See {@link Socket#getLocalAddress()}. Calling this method does not trigger mode detection. */ @Override public InetAddress getLocalAddress() { return getSocketAllowUnknownMode().getLocalAddress(); } /** * See {@link Socket#getPort()}. Calling this method does not trigger mode detection. */ @Override public int getPort() { return getSocketAllowUnknownMode().getPort(); } /** * See {@link Socket#getLocalPort()}. Calling this method does not trigger mode detection. */ @Override public int getLocalPort() { return getSocketAllowUnknownMode().getLocalPort(); } /** * See {@link Socket#getRemoteSocketAddress()}. Calling this method does not trigger mode detection. */ @Override public SocketAddress getRemoteSocketAddress() { return getSocketAllowUnknownMode().getRemoteSocketAddress(); } /** * See {@link Socket#getLocalSocketAddress()}. Calling this method does not trigger mode detection. */ @Override public SocketAddress getLocalSocketAddress() { return getSocketAllowUnknownMode().getLocalSocketAddress(); } /** * See {@link Socket#getChannel()}. Calling this method does not trigger mode detection. */ @Override public SocketChannel getChannel() { return getSocketAllowUnknownMode().getChannel(); } /** * See {@link Socket#getInputStream()}. If the socket mode has not yet been detected, the first read from the * returned input stream will trigger mode detection, which is a potentially blocking operation. This means * the accept() thread should avoid reading from this input stream if possible. */ @Override public InputStream getInputStream() throws IOException { return new UnifiedInputStream(this); } /** * See {@link Socket#getOutputStream()}. If the socket mode has not yet been detected, the first read from the * returned input stream will trigger mode detection, which is a potentially blocking operation. This means * the accept() thread should avoid reading from this input stream if possible. */ @Override public OutputStream getOutputStream() throws IOException { return new UnifiedOutputStream(this); } /** * See {@link Socket#setTcpNoDelay(boolean)}. Calling this method does not trigger mode detection. */ @Override public void setTcpNoDelay(boolean on) throws SocketException { getSocketAllowUnknownMode().setTcpNoDelay(on); } /** * See {@link Socket#getTcpNoDelay()}. Calling this method does not trigger mode detection. */ @Override public boolean getTcpNoDelay() throws SocketException { return getSocketAllowUnknownMode().getTcpNoDelay(); } /** * See {@link Socket#setSoLinger(boolean, int)}. Calling this method does not trigger mode detection. */ @Override public void setSoLinger(boolean on, int linger) throws SocketException { getSocketAllowUnknownMode().setSoLinger(on, linger); } /** * See {@link Socket#getSoLinger()}. Calling this method does not trigger mode detection. */ @Override public int getSoLinger() throws SocketException { return getSocketAllowUnknownMode().getSoLinger(); } /** * See {@link Socket#sendUrgentData(int)}. Calling this method triggers mode detection, which is a potentially * blocking operation, so it should not be done in the accept() thread. */ @Override public void sendUrgentData(int data) throws IOException { getSocket().sendUrgentData(data); } /** * See {@link Socket#setOOBInline(boolean)}. Calling this method does not trigger mode detection. */ @Override public void setOOBInline(boolean on) throws SocketException { getSocketAllowUnknownMode().setOOBInline(on); } /** * See {@link Socket#getOOBInline()}. Calling this method does not trigger mode detection. */ @Override public boolean getOOBInline() throws SocketException { return getSocketAllowUnknownMode().getOOBInline(); } /** * See {@link Socket#setSoTimeout(int)}. Calling this method does not trigger mode detection. */ @Override public synchronized void setSoTimeout(int timeout) throws SocketException { getSocketAllowUnknownMode().setSoTimeout(timeout); } /** * See {@link Socket#getSoTimeout()}. Calling this method does not trigger mode detection. */ @Override public synchronized int getSoTimeout() throws SocketException { return getSocketAllowUnknownMode().getSoTimeout(); } /** * See {@link Socket#setSendBufferSize(int)}. Calling this method does not trigger mode detection. */ @Override public synchronized void setSendBufferSize(int size) throws SocketException { getSocketAllowUnknownMode().setSendBufferSize(size); } /** * See {@link Socket#getSendBufferSize()}. Calling this method does not trigger mode detection. */ @Override public synchronized int getSendBufferSize() throws SocketException { return getSocketAllowUnknownMode().getSendBufferSize(); } /** * See {@link Socket#setReceiveBufferSize(int)}. Calling this method does not trigger mode detection. */ @Override public synchronized void setReceiveBufferSize(int size) throws SocketException { getSocketAllowUnknownMode().setReceiveBufferSize(size); } /** * See {@link Socket#getReceiveBufferSize()}. Calling this method does not trigger mode detection. */ @Override public synchronized int getReceiveBufferSize() throws SocketException { return getSocketAllowUnknownMode().getReceiveBufferSize(); } /** * See {@link Socket#setKeepAlive(boolean)}. Calling this method does not trigger mode detection. */ @Override public void setKeepAlive(boolean on) throws SocketException { getSocketAllowUnknownMode().setKeepAlive(on); } /** * See {@link Socket#getKeepAlive()}. Calling this method does not trigger mode detection. */ @Override public boolean getKeepAlive() throws SocketException { return getSocketAllowUnknownMode().getKeepAlive(); } /** * See {@link Socket#setTrafficClass(int)}. Calling this method does not trigger mode detection. */ @Override public void setTrafficClass(int tc) throws SocketException { getSocketAllowUnknownMode().setTrafficClass(tc); } /** * See {@link Socket#getTrafficClass()}. Calling this method does not trigger mode detection. */ @Override public int getTrafficClass() throws SocketException { return getSocketAllowUnknownMode().getTrafficClass(); } /** * See {@link Socket#setReuseAddress(boolean)}. Calling this method does not trigger mode detection. */ @Override public void setReuseAddress(boolean on) throws SocketException { getSocketAllowUnknownMode().setReuseAddress(on); } /** * See {@link Socket#getReuseAddress()}. Calling this method does not trigger mode detection. */ @Override public boolean getReuseAddress() throws SocketException { return getSocketAllowUnknownMode().getReuseAddress(); } /** * See {@link Socket#close()}. Calling this method does not trigger mode detection. */ @Override public synchronized void close() throws IOException { getSocketAllowUnknownMode().close(); } /** * See {@link Socket#shutdownInput()}. Calling this method does not trigger mode detection. */ @Override public void shutdownInput() throws IOException { getSocketAllowUnknownMode().shutdownInput(); } /** * See {@link Socket#shutdownOutput()}. Calling this method does not trigger mode detection. */ @Override public void shutdownOutput() throws IOException { getSocketAllowUnknownMode().shutdownOutput(); } /** * See {@link Socket#toString()}. Calling this method does not trigger mode detection. */ @Override public String toString() { return "UnifiedSocket[mode=" + mode.toString() + "socket=" + getSocketAllowUnknownMode().toString() + "]"; } /** * See {@link Socket#isConnected()}. Calling this method does not trigger mode detection. */ @Override public boolean isConnected() { return getSocketAllowUnknownMode().isConnected(); } /** * See {@link Socket#isBound()}. Calling this method does not trigger mode detection. */ @Override public boolean isBound() { return getSocketAllowUnknownMode().isBound(); } /** * See {@link Socket#isClosed()}. Calling this method does not trigger mode detection. */ @Override public boolean isClosed() { return getSocketAllowUnknownMode().isClosed(); } /** * See {@link Socket#isInputShutdown()}. Calling this method does not trigger mode detection. */ @Override public boolean isInputShutdown() { return getSocketAllowUnknownMode().isInputShutdown(); } /** * See {@link Socket#isOutputShutdown()}. Calling this method does not trigger mode detection. */ @Override public boolean isOutputShutdown() { return getSocketAllowUnknownMode().isOutputShutdown(); } /** * See {@link Socket#setPerformancePreferences(int, int, int)}. Calling this method does not trigger * mode detection. */ @Override public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { getSocketAllowUnknownMode().setPerformancePreferences(connectionTime, latency, bandwidth); } } /** * An input stream for a UnifiedSocket. The first read from this stream will trigger mode detection on the * underlying UnifiedSocket. */ private static class UnifiedInputStream extends InputStream { private final UnifiedSocket unifiedSocket; private InputStream realInputStream; private UnifiedInputStream(UnifiedSocket unifiedSocket) { this.unifiedSocket = unifiedSocket; this.realInputStream = null; } @Override public int read() throws IOException { return getRealInputStream().read(); } /** * Note: SocketInputStream has optimized implementations of bulk-read operations, so we need to call them * directly instead of relying on the base-class implementation which just calls the single-byte read() over * and over. Not implementing these results in awful performance. */ @Override public int read(byte[] b) throws IOException { return getRealInputStream().read(b); } @Override public int read(byte[] b, int off, int len) throws IOException { return getRealInputStream().read(b, off, len); } private InputStream getRealInputStream() throws IOException { if (realInputStream == null) { // Note: The first call to getSocket() triggers mode detection which can block realInputStream = unifiedSocket.getSocket().getInputStream(); } return realInputStream; } @Override public long skip(long n) throws IOException { return getRealInputStream().skip(n); } @Override public int available() throws IOException { return getRealInputStream().available(); } @Override public void close() throws IOException { getRealInputStream().close(); } @Override public synchronized void mark(int readlimit) { try { getRealInputStream().mark(readlimit); } catch (IOException e) { throw new RuntimeException(e); } } @Override public synchronized void reset() throws IOException { getRealInputStream().reset(); } @Override public boolean markSupported() { try { return getRealInputStream().markSupported(); } catch (IOException e) { throw new RuntimeException(e); } } } private static class UnifiedOutputStream extends OutputStream { private final UnifiedSocket unifiedSocket; private OutputStream realOutputStream; private UnifiedOutputStream(UnifiedSocket unifiedSocket) { this.unifiedSocket = unifiedSocket; this.realOutputStream = null; } @Override public void write(int b) throws IOException { getRealOutputStream().write(b); } @Override public void write(byte[] b) throws IOException { getRealOutputStream().write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { getRealOutputStream().write(b, off, len); } @Override public void flush() throws IOException { getRealOutputStream().flush(); } @Override public void close() throws IOException { getRealOutputStream().close(); } private OutputStream getRealOutputStream() throws IOException { if (realOutputStream == null) { // Note: The first call to getSocket() triggers mode detection which can block realOutputStream = unifiedSocket.getSocket().getOutputStream(); } return realOutputStream; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032710 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/UpgradeableSessionTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Upgradeable0100644 0000000 0000000 00000011700 15051152474 034265 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.server.SessionTracker; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A session tracker that supports upgradeable local sessions. */ public abstract class UpgradeableSessionTracker implements SessionTracker { private static final Logger LOG = LoggerFactory.getLogger(UpgradeableSessionTracker.class); private ConcurrentMap localSessionsWithTimeouts; private ConcurrentMap upgradingSessions; protected LocalSessionTracker localSessionTracker; protected boolean localSessionsEnabled; public void start() { } public void createLocalSessionTracker(SessionExpirer expirer, int tickTime, long id, ZooKeeperServerListener listener) { this.localSessionsWithTimeouts = new ConcurrentHashMap<>(); this.localSessionTracker = new LocalSessionTracker(expirer, this.localSessionsWithTimeouts, tickTime, id, listener); this.upgradingSessions = new ConcurrentHashMap<>(); } public boolean isTrackingSession(long sessionId) { return isLocalSession(sessionId) || isGlobalSession(sessionId); } public boolean isLocalSession(long sessionId) { return localSessionTracker != null && localSessionTracker.isTrackingSession(sessionId); } @Override public boolean isLocalSessionsEnabled() { return localSessionsEnabled; } public boolean isUpgradingSession(long sessionId) { return upgradingSessions != null && upgradingSessions.containsKey(sessionId); } public void finishedUpgrading(long sessionId) { if (upgradingSessions != null) { upgradingSessions.remove(sessionId); } } public abstract boolean isGlobalSession(long sessionId); /** * Upgrades the session to a global session. * This simply removes the session from the local tracker and marks * it as global. It is up to the caller to actually * queue up a transaction for the session. * * @param sessionId * @return session timeout (-1 if not a local session) */ public int upgradeSession(long sessionId) { if (localSessionsWithTimeouts == null) { return -1; } // We won't race another upgrade attempt because only one thread // will get the timeout from the map Integer timeout = localSessionsWithTimeouts.remove(sessionId); if (timeout != null) { LOG.info("Upgrading session 0x{}", Long.toHexString(sessionId)); // Track global session, which will add to global session tracker // on leader and do nothing on learner. Need to start track global // session in leader now to update the session expire between // LeaderRequestProcessor and PrepRequestProcessor. trackSession(sessionId, timeout); // Track ongoing upgrading sessions, learner will use it to find // other sessions it has which are not in local and global sessions upgradingSessions.put(sessionId, timeout); localSessionTracker.removeSession(sessionId); return timeout; } return -1; } protected void removeLocalSession(long sessionId) { if (localSessionTracker == null) { return; } localSessionTracker.removeSession(sessionId); } public void checkGlobalSession(long sessionId, Object owner) throws KeeperException.SessionExpiredException, KeeperException.SessionMovedException { throw new UnsupportedOperationException(); } public long getLocalSessionCount() { if (localSessionsWithTimeouts == null) { return 0; } return localSessionsWithTimeouts.size(); } public Set localSessions() { return (localSessionTracker == null) ? Collections.emptySet() : localSessionTracker.localSessions(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/Vote.java0100644 0000000 0000000 00000012060 15051152474 033707 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; public class Vote { public Vote(long id, long zxid) { this.version = 0x0; this.id = id; this.zxid = zxid; this.electionEpoch = -1; this.peerEpoch = -1; this.state = ServerState.LOOKING; } public Vote(long id, long zxid, long peerEpoch) { this.version = 0x0; this.id = id; this.zxid = zxid; this.electionEpoch = -1; this.peerEpoch = peerEpoch; this.state = ServerState.LOOKING; } public Vote(long id, long zxid, long electionEpoch, long peerEpoch) { this.version = 0x0; this.id = id; this.zxid = zxid; this.electionEpoch = electionEpoch; this.peerEpoch = peerEpoch; this.state = ServerState.LOOKING; } public Vote(int version, long id, long zxid, long electionEpoch, long peerEpoch, ServerState state) { this.version = version; this.id = id; this.zxid = zxid; this.electionEpoch = electionEpoch; this.state = state; this.peerEpoch = peerEpoch; } public Vote(long id, long zxid, long electionEpoch, long peerEpoch, ServerState state) { this.id = id; this.zxid = zxid; this.electionEpoch = electionEpoch; this.state = state; this.peerEpoch = peerEpoch; this.version = 0x0; } private final int version; private final long id; private final long zxid; private final long electionEpoch; private final long peerEpoch; public int getVersion() { return version; } public long getId() { return id; } public long getZxid() { return zxid; } public long getElectionEpoch() { return electionEpoch; } public long getPeerEpoch() { return peerEpoch; } public ServerState getState() { return state; } private final ServerState state; @Override public boolean equals(Object o) { if (!(o instanceof Vote)) { return false; } Vote other = (Vote) o; if ((state == ServerState.LOOKING) || (other.state == ServerState.LOOKING)) { return id == other.id && zxid == other.zxid && electionEpoch == other.electionEpoch && peerEpoch == other.peerEpoch; } else { /* * There are two things going on in the logic below: * * 1. skip comparing the zxid and electionEpoch for votes for servers * out of election. * * Need to skip those because they can be inconsistent due to * scenarios described in QuorumPeer.updateElectionVote. * * And given that only one ensemble can be running at a single point * in time and that each epoch is used only once, using only id and * epoch to compare the votes is sufficient. * * {@see https://issues.apache.org/jira/browse/ZOOKEEPER-1805} * * 2. skip comparing peerEpoch if if we're running with mixed ensemble * with (version > 0x0) and without the change (version = 0x0) * introduced in ZOOKEEPER-1732. * * {@see https://issues.apache.org/jira/browse/ZOOKEEPER-1732} * * The server running with and without ZOOKEEPER-1732 will return * different peerEpoch. During rolling upgrades, it's possible * that 2/5 servers are returning epoch 1, while the other 2/5 * are returning epoch 2, the other server need to ignore the * peerEpoch to be able to join it. */ if ((version > 0x0) ^ (other.version > 0x0)) { return id == other.id; } else { return (id == other.id && peerEpoch == other.peerEpoch); } } } @Override public int hashCode() { return (int) (id & zxid); } public String toString() { return "(" + id + ", " + Long.toHexString(zxid) + ", " + Long.toHexString(peerEpoch) + ")"; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000202 15051152474 032711 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthLearner.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/NullQu0100644 0000000 0000000 00000002262 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.net.Socket; /** * This class represents no authentication learner, it just return * without performing any authentication. */ public class NullQuorumAuthLearner implements QuorumAuthLearner { @Override public void authenticate(Socket sock, String hostname) { return; // simply return don't require auth } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032710 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/NullQuorumAuthServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/NullQu0100644 0000000 0000000 00000002327 15051152474 034240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.DataInputStream; import java.net.Socket; /** * This class represents no authentication server, it just return * without performing any authentication. */ public class NullQuorumAuthServer implements QuorumAuthServer { @Override public void authenticate(final Socket sock, final DataInputStream din) { // simply return don't require auth } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000167 15051152474 032723 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/QuorumAuth.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000007175 15051152474 034316 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.DataInputStream; import java.io.IOException; import org.apache.jute.BinaryInputArchive; import org.apache.zookeeper.server.quorum.QuorumAuthPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumAuth { private static final Logger LOG = LoggerFactory.getLogger(QuorumAuth.class); public static final String QUORUM_SASL_AUTH_ENABLED = "quorum.auth.enableSasl"; public static final String QUORUM_SERVER_SASL_AUTH_REQUIRED = "quorum.auth.serverRequireSasl"; public static final String QUORUM_LEARNER_SASL_AUTH_REQUIRED = "quorum.auth.learnerRequireSasl"; public static final String QUORUM_KERBEROS_SERVICE_PRINCIPAL = "quorum.auth.kerberos.servicePrincipal"; public static final String QUORUM_KERBEROS_SERVICE_PRINCIPAL_DEFAULT_VALUE = "zkquorum/localhost"; public static final String QUORUM_LEARNER_SASL_LOGIN_CONTEXT = "quorum.auth.learner.saslLoginContext"; public static final String QUORUM_LEARNER_SASL_LOGIN_CONTEXT_DFAULT_VALUE = "QuorumLearner"; public static final String QUORUM_SERVER_SASL_LOGIN_CONTEXT = "quorum.auth.server.saslLoginContext"; public static final String QUORUM_SERVER_SASL_LOGIN_CONTEXT_DFAULT_VALUE = "QuorumServer"; static final String QUORUM_SERVER_PROTOCOL_NAME = "zookeeper-quorum"; static final String QUORUM_SERVER_SASL_DIGEST = "zk-quorum-sasl-md5"; static final String QUORUM_AUTH_MESSAGE_TAG = "qpconnect"; // this is negative, so that if a learner that does auth, connects to a // server, it'll think the received packet is an authentication packet public static final long QUORUM_AUTH_MAGIC_NUMBER = -0xa0dbcafecafe1234L; public enum Status { IN_PROGRESS(0), SUCCESS(1), ERROR(-1); private int status; Status(int status) { this.status = status; } static Status getStatus(int status) { switch (status) { case 0: return IN_PROGRESS; case 1: return SUCCESS; case -1: return ERROR; default: LOG.error("Unknown status:{}!", status); assert false : "Unknown status!"; return ERROR; } } int status() { return status; } } public static QuorumAuthPacket createPacket(Status status, byte[] response) { return new QuorumAuthPacket(QUORUM_AUTH_MAGIC_NUMBER, status.status(), response); } public static boolean nextPacketIsAuth(DataInputStream din) throws IOException { din.mark(32); BinaryInputArchive bia = new BinaryInputArchive(din); boolean firstIsAuth = (bia.readLong("NO_TAG") == QuorumAuth.QUORUM_AUTH_MAGIC_NUMBER); din.reset(); return firstIsAuth; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032723 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/QuorumAuthLearner.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000002627 15051152474 034313 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.IOException; import java.net.Socket; /** * Interface for quorum learner authentication mechanisms. */ public interface QuorumAuthLearner { /** * Performs an authentication step for the given socket connection. * * @param sock * socket connection to other quorum peer server * @param hostname * host name of other quorum peer server * @throws IOException * if there is an authentication failure */ void authenticate(Socket sock, String hostname) throws IOException; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032722 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/QuorumAuthServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000002705 15051152474 034310 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.DataInputStream; import java.io.IOException; import java.net.Socket; /** * Interface for quorum server authentication mechanisms. */ public interface QuorumAuthServer { /** * Performs an authentication step for the given socket connection. * * @param sock * socket connection to other quorum peer * @param din * stream used to read auth data send by the quorum learner * @throws IOException if the server fails to authenticate connecting quorum learner */ void authenticate(Socket sock, DataInputStream din) throws IOException; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000202 15051152474 032711 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthLearner.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQu0100644 0000000 0000000 00000023440 15051152474 034227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.function.Supplier; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.Login; import org.apache.zookeeper.SaslClientCallbackHandler; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.quorum.QuorumAuthPacket; import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SaslQuorumAuthLearner implements QuorumAuthLearner { private static final Logger LOG = LoggerFactory.getLogger(SaslQuorumAuthLearner.class); private final Login learnerLogin; private final boolean quorumRequireSasl; private final String quorumServicePrincipal; public SaslQuorumAuthLearner( boolean quorumRequireSasl, String quorumServicePrincipal, String loginContext) throws SaslException { this.quorumRequireSasl = quorumRequireSasl; this.quorumServicePrincipal = quorumServicePrincipal; try { AppConfigurationEntry[] entries = Configuration.getConfiguration().getAppConfigurationEntry(loginContext); if (entries == null || entries.length == 0) { throw new LoginException(String.format( "SASL-authentication failed because the specified JAAS configuration section '%s' could not be found.", loginContext)); } Supplier callbackSupplier = () -> { return new SaslClientCallbackHandler(null, "QuorumLearner"); }; this.learnerLogin = new Login( loginContext, callbackSupplier, new ZKConfig()); this.learnerLogin.startThreadIfNeeded(); } catch (LoginException e) { throw new SaslException("Failed to initialize authentication mechanism using SASL", e); } } @Override public void authenticate(Socket sock, String hostName) throws IOException { if (!quorumRequireSasl) { // let it through, we don't require auth LOG.info( "Skipping SASL authentication as {}={}", QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, quorumRequireSasl); return; } SaslClient sc = null; String principalConfig = SecurityUtils.getServerPrincipal(quorumServicePrincipal, hostName); try { DataOutputStream dout = new DataOutputStream(sock.getOutputStream()); DataInputStream din = new DataInputStream(sock.getInputStream()); byte[] responseToken = new byte[0]; sc = SecurityUtils.createSaslClient( new ZKConfig(), learnerLogin.getSubject(), principalConfig, QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME, QuorumAuth.QUORUM_SERVER_SASL_DIGEST, LOG, "QuorumLearner"); if (sc.hasInitialResponse()) { responseToken = createSaslToken(new byte[0], sc, learnerLogin); } send(dout, responseToken); QuorumAuthPacket authPacket = receive(din); QuorumAuth.Status qpStatus = QuorumAuth.Status.getStatus(authPacket.getStatus()); while (!sc.isComplete()) { switch (qpStatus) { case SUCCESS: responseToken = createSaslToken(authPacket.getToken(), sc, learnerLogin); // we're done; don't expect to send another BIND if (responseToken != null) { throw new SaslException("Protocol error: attempting to send response after completion"); } break; case IN_PROGRESS: responseToken = createSaslToken(authPacket.getToken(), sc, learnerLogin); send(dout, responseToken); authPacket = receive(din); qpStatus = QuorumAuth.Status.getStatus(authPacket.getStatus()); break; case ERROR: throw new SaslException("Authentication failed against server addr: " + sock.getRemoteSocketAddress()); default: LOG.warn("Unknown status:{}!", qpStatus); throw new SaslException("Authentication failed against server addr: " + sock.getRemoteSocketAddress()); } } // Validate status code at the end of authentication exchange. checkAuthStatus(sock, qpStatus); } finally { if (sc != null) { try { sc.dispose(); } catch (SaslException e) { LOG.error("SaslClient dispose() failed", e); } } } } private void checkAuthStatus(Socket sock, QuorumAuth.Status qpStatus) throws SaslException { if (qpStatus == QuorumAuth.Status.SUCCESS) { LOG.info( "Successfully completed the authentication using SASL. server addr: {}, status: {}", sock.getRemoteSocketAddress(), qpStatus); } else { throw new SaslException("Authentication failed against server addr: " + sock.getRemoteSocketAddress() + ", qpStatus: " + qpStatus); } } private QuorumAuthPacket receive(DataInputStream din) throws IOException { QuorumAuthPacket authPacket = new QuorumAuthPacket(); BinaryInputArchive bia = BinaryInputArchive.getArchive(din); authPacket.deserialize(bia, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); return authPacket; } private void send(DataOutputStream dout, byte[] response) throws IOException { QuorumAuthPacket authPacket; BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(bufferedOutput); authPacket = QuorumAuth.createPacket(QuorumAuth.Status.IN_PROGRESS, response); boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); bufferedOutput.flush(); } // TODO: need to consolidate the #createSaslToken() implementation between ZooKeeperSaslClient#createSaslToken(). private byte[] createSaslToken( final byte[] saslToken, final SaslClient saslClient, final Login login) throws SaslException { if (saslToken == null) { throw new SaslException("Error in authenticating with a Zookeeper Quorum member: the quorum member's saslToken is null."); } if (login.getSubject() != null) { synchronized (login) { try { final byte[] retval = Subject.doAs(login.getSubject(), new PrivilegedExceptionAction() { public byte[] run() throws SaslException { LOG.debug("saslClient.evaluateChallenge(len={})", saslToken.length); return saslClient.evaluateChallenge(saslToken); } }); return retval; } catch (PrivilegedActionException e) { String error = "An error: (" + e + ") occurred when evaluating Zookeeper Quorum Member's received SASL token."; // Try to provide hints to use about what went wrong so they // can fix their configuration. // TODO: introspect about e: look for GSS information. final String UNKNOWN_SERVER_ERROR_TEXT = "(Mechanism level: Server not found in Kerberos database (7) - UNKNOWN_SERVER)"; if (e.toString().indexOf(UNKNOWN_SERVER_ERROR_TEXT) > -1) { error += " This may be caused by Java's being unable to resolve the Zookeeper Quorum Member's" + " hostname correctly. You may want to try to adding" + " '-Dsun.net.spi.nameservice.provider.1=dns,sun' to your server's JVMFLAGS environment."; } LOG.error(error); throw new SaslException(error, e); } } } else { throw new SaslException("Cannot make SASL token without subject defined. " + "For diagnosis, please look for WARNs and ERRORs in your log related to the Login class."); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032710 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQu0100644 0000000 0000000 00000016613 15051152474 034233 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; import java.util.Set; import java.util.function.Supplier; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.Login; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.quorum.QuorumAuthPacket; import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SaslQuorumAuthServer implements QuorumAuthServer { private static final Logger LOG = LoggerFactory.getLogger(SaslQuorumAuthServer.class); private static final int MAX_RETRIES = 5; private final Login serverLogin; private final boolean quorumRequireSasl; public SaslQuorumAuthServer(boolean quorumRequireSasl, String loginContext, Set authzHosts) throws SaslException { this.quorumRequireSasl = quorumRequireSasl; try { AppConfigurationEntry[] entries = Configuration.getConfiguration().getAppConfigurationEntry(loginContext); if (entries == null || entries.length == 0) { throw new LoginException(String.format( "SASL-authentication failed because the specified JAAS configuration section '%s' could not be found.", loginContext)); } Supplier callbackSupplier = () -> { return new SaslQuorumServerCallbackHandler(entries, authzHosts); }; serverLogin = new Login(loginContext, callbackSupplier, new ZKConfig()); serverLogin.startThreadIfNeeded(); } catch (Throwable e) { throw new SaslException("Failed to initialize authentication mechanism using SASL", e); } } @Override public void authenticate(Socket sock, DataInputStream din) throws SaslException { DataOutputStream dout = null; SaslServer ss = null; try { if (!QuorumAuth.nextPacketIsAuth(din)) { if (quorumRequireSasl) { throw new SaslException("Learner not trying to authenticate" + " and authentication is required"); } else { // let it through, we don't require auth return; } } byte[] token = receive(din); int tries = 0; dout = new DataOutputStream(sock.getOutputStream()); byte[] challenge = null; ss = SecurityUtils.createSaslServer( serverLogin.getSubject(), QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME, QuorumAuth.QUORUM_SERVER_SASL_DIGEST, serverLogin.newCallbackHandler(), LOG); while (!ss.isComplete()) { challenge = ss.evaluateResponse(token); if (!ss.isComplete()) { // limited number of retries. if (++tries > MAX_RETRIES) { send(dout, challenge, QuorumAuth.Status.ERROR); LOG.warn( "Failed to authenticate using SASL, server addr: {}, retries={} exceeded.", sock.getRemoteSocketAddress(), tries); break; } send(dout, challenge, QuorumAuth.Status.IN_PROGRESS); token = receive(din); } } // Authentication exchange has completed if (ss.isComplete()) { send(dout, challenge, QuorumAuth.Status.SUCCESS); LOG.info( "Successfully completed the authentication using SASL. learner addr: {}", sock.getRemoteSocketAddress()); } } catch (Exception e) { try { if (dout != null) { // send error message to the learner send(dout, new byte[0], QuorumAuth.Status.ERROR); } } catch (IOException ioe) { LOG.warn("Exception while sending failed status", ioe); } // If sasl is not required, when a server initializes a // connection it will try to log in, but it will also // accept connections that do not start with a sasl // handshake. if (quorumRequireSasl) { LOG.error("Failed to authenticate using SASL", e); throw new SaslException("Failed to authenticate using SASL: " + e.getMessage()); } else { LOG.warn("Failed to authenticate using SASL", e); LOG.warn( "Maintaining learner connection despite SASL authentication failure. server addr: {}, {}: {}", sock.getRemoteSocketAddress(), QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, quorumRequireSasl); // let it through, we don't require auth } } finally { if (ss != null) { try { ss.dispose(); } catch (SaslException e) { LOG.error("SaslServer dispose() failed", e); } } } } private byte[] receive(DataInputStream din) throws IOException { QuorumAuthPacket authPacket = new QuorumAuthPacket(); BinaryInputArchive bia = BinaryInputArchive.getArchive(din); authPacket.deserialize(bia, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); return authPacket.getToken(); } private void send(DataOutputStream dout, byte[] challenge, QuorumAuth.Status s) throws IOException { BufferedOutputStream bufferedOutput = new BufferedOutputStream(dout); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(bufferedOutput); QuorumAuthPacket authPacket; if (challenge == null && s != QuorumAuth.Status.SUCCESS) { authPacket = QuorumAuth.createPacket(QuorumAuth.Status.IN_PROGRESS, null); } else { authPacket = QuorumAuth.createPacket(s, challenge); } boa.writeRecord(authPacket, QuorumAuth.QUORUM_AUTH_MESSAGE_TAG); bufferedOutput.flush(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000214 15051152474 032714 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumServerCallbackHandler.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQu0100644 0000000 0000000 00000014767 15051152474 034243 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.login.AppConfigurationEntry; import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.RealmCallback; import org.apache.zookeeper.server.auth.DigestLoginModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This is used by the SASL mechanisms to get further information to complete * the authentication. For example, a SASL mechanism might use this callback * handler to do verification operation. This is used by the QuorumServer to * perform the mutual quorum peer authentication. */ public class SaslQuorumServerCallbackHandler implements CallbackHandler { private static final String USER_PREFIX = "user_"; private static final Logger LOG = LoggerFactory.getLogger(SaslQuorumServerCallbackHandler.class); private String userName; private final boolean isDigestAuthn; private final Map credentials; private final Set authzHosts; public SaslQuorumServerCallbackHandler( AppConfigurationEntry[] configurationEntries, Set authzHosts) { Map credentials = new HashMap<>(); boolean isDigestAuthn = true; for (AppConfigurationEntry entry : configurationEntries) { if (entry.getLoginModuleName().equals(DigestLoginModule.class.getName())) { Map options = entry.getOptions(); // Populate DIGEST-MD5 user -> password map with JAAS configuration entries from the "QuorumServer" section. // Usernames are distinguished from other options by prefixing the username with a "user_" prefix. for (Map.Entry pair : options.entrySet()) { String key = pair.getKey(); if (key.startsWith(USER_PREFIX)) { String userName = key.substring(USER_PREFIX.length()); credentials.put(userName, (String) pair.getValue()); } } } else { isDigestAuthn = false; } } this.isDigestAuthn = isDigestAuthn; if (isDigestAuthn) { this.credentials = Collections.unmodifiableMap(credentials); LOG.warn("Using DIGEST-MD5 for quorum authorization"); } else { this.credentials = Collections.emptyMap(); } // authorized host lists this.authzHosts = authzHosts; } public void handle(Callback[] callbacks) throws UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { handleNameCallback((NameCallback) callback); } else if (callback instanceof PasswordCallback) { handlePasswordCallback((PasswordCallback) callback); } else if (callback instanceof RealmCallback) { handleRealmCallback((RealmCallback) callback); } else if (callback instanceof AuthorizeCallback) { handleAuthorizeCallback((AuthorizeCallback) callback); } } } private void handleNameCallback(NameCallback nc) { // check to see if this user is in the user password database. if (credentials.get(nc.getDefaultName()) == null) { LOG.warn("User '{}' not found in list of DIGEST-MD5 authenticateable users.", nc.getDefaultName()); return; } nc.setName(nc.getDefaultName()); userName = nc.getDefaultName(); } private void handlePasswordCallback(PasswordCallback pc) { if (credentials.containsKey(userName)) { pc.setPassword(credentials.get(userName).toCharArray()); } else { LOG.warn("No password found for user: {}", userName); } } private void handleRealmCallback(RealmCallback rc) { LOG.debug("QuorumLearner supplied realm: {}", rc.getDefaultText()); rc.setText(rc.getDefaultText()); } private void handleAuthorizeCallback(AuthorizeCallback ac) { String authenticationID = ac.getAuthenticationID(); String authorizationID = ac.getAuthorizationID(); boolean authzFlag = false; // 1. Matches authenticationID and authorizationID authzFlag = authenticationID.equals(authorizationID); // 2. Verify whether the connecting host is present in authorized hosts. // If not exists, then connecting peer is not authorized to join the // ensemble and will reject it. if (!isDigestAuthn && authzFlag) { String[] components = authorizationID.split("[/@]"); if (components.length == 3) { authzFlag = authzHosts.contains(components[1]); } else { authzFlag = false; } if (!authzFlag) { LOG.error("SASL authorization completed, {} is not authorized to connect", authorizationID); } } // Sets authorization flag ac.setAuthorized(authzFlag); if (ac.isAuthorized()) { ac.setAuthorizedID(authorizationID); LOG.info("Successfully authenticated learner: authenticationID={}; authorizationID={}.", authenticationID, authorizationID); } LOG.debug("SASL authorization completed, authorized flag set to {}", ac.isAuthorized()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000203 15051152474 032712 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/QuorumHierarchical.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/Qu0100644 0000000 0000000 00000031366 15051152474 034243 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.flexible; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class implements a validator for hierarchical quorums. With this * construction, zookeeper servers are split into disjoint groups, and * each server has a weight. We obtain a quorum if we get more than half * of the total weight of a group for a majority of groups. * * The configuration of quorums uses two parameters: group and weight. * Groups are sets of ZooKeeper servers, and we set a group by passing * a colon-separated list of server ids. It is also necessary to assign * weights to server. Here is an example of a configuration that creates * three groups and assigns 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 * * Note that it is still necessary to define peers using the server keyword. */ public class QuorumHierarchical implements QuorumVerifier { private static final Logger LOG = LoggerFactory.getLogger(QuorumHierarchical.class); private HashMap serverWeight = new HashMap<>(); private HashMap serverGroup = new HashMap<>(); private HashMap groupWeight = new HashMap<>(); private int numGroups = 0; private Map allMembers = new HashMap<>(); private Map participatingMembers = new HashMap<>(); private Map observingMembers = new HashMap<>(); private long version = 0; public int hashCode() { assert false : "hashCode not designed"; return 42; // any arbitrary constant will do } public boolean equals(Object o) { if (!(o instanceof QuorumHierarchical)) { return false; } QuorumHierarchical qm = (QuorumHierarchical) o; if (qm.getVersion() == version) { return true; } if ((allMembers.size() != qm.getAllMembers().size()) || (serverWeight.size() != qm.serverWeight.size()) || (groupWeight.size() != qm.groupWeight.size()) || (serverGroup.size() != qm.serverGroup.size())) { return false; } for (QuorumServer qs : allMembers.values()) { QuorumServer qso = qm.getAllMembers().get(qs.id); if (qso == null || !qs.equals(qso)) { return false; } } for (Entry entry : serverWeight.entrySet()) { if (!entry.getValue().equals(qm.serverWeight.get(entry.getKey()))) { return false; } } for (Entry entry : groupWeight.entrySet()) { if (!entry.getValue().equals(qm.groupWeight.get(entry.getKey()))) { return false; } } for (Entry entry : serverGroup.entrySet()) { if (!entry.getValue().equals(qm.serverGroup.get(entry.getKey()))) { return false; } } return true; } /** * This constructor requires the quorum configuration * to be declared in a separate file, and it takes the * file as an input parameter. */ public QuorumHierarchical(String filename) throws ConfigException { readConfigFile(filename); } /** * This constructor takes a set of properties. We use * it in the unit test for this feature. */ public QuorumHierarchical(Properties qp) throws ConfigException { parse(qp); LOG.info("{}, {}, {}", serverWeight.size(), serverGroup.size(), groupWeight.size()); } /** * Returns the weight of a server. * * @param id */ public long getWeight(long id) { return serverWeight.get(id); } /** * Reads a configuration file. Called from the constructor * that takes a file as an input. */ private void readConfigFile(String filename) throws ConfigException { File configFile = new File(filename); LOG.info("Reading configuration from: {}", configFile); try { if (!configFile.exists()) { throw new IllegalArgumentException(configFile.toString() + " file is missing"); } Properties cfg = new Properties(); FileInputStream in = new FileInputStream(configFile); try { cfg.load(in); } finally { in.close(); } parse(cfg); } catch (IOException e) { throw new ConfigException("Error processing " + filename, e); } catch (IllegalArgumentException e) { throw new ConfigException("Error processing " + filename, e); } } /** * Parse properties if configuration given in a separate file. * Assumes that allMembers has been already assigned * @throws ConfigException */ private void parse(Properties quorumProp) throws ConfigException { for (Entry entry : quorumProp.entrySet()) { String key = entry.getKey().toString(); String value = entry.getValue().toString(); if (key.startsWith("server.")) { int dot = key.indexOf('.'); long sid = Long.parseLong(key.substring(dot + 1)); QuorumServer qs = new QuorumServer(sid, value); allMembers.put(Long.valueOf(sid), qs); if (qs.type == LearnerType.PARTICIPANT) { participatingMembers.put(Long.valueOf(sid), qs); } else { observingMembers.put(Long.valueOf(sid), qs); } } else if (key.startsWith("group")) { int dot = key.indexOf('.'); long gid = Long.parseLong(key.substring(dot + 1)); numGroups++; String[] parts = value.split(":"); for (String s : parts) { long sid = Long.parseLong(s); if (serverGroup.containsKey(sid)) { throw new ConfigException("Server " + sid + "is in multiple groups"); } else { serverGroup.put(sid, gid); } } } else if (key.startsWith("weight")) { int dot = key.indexOf('.'); long sid = Long.parseLong(key.substring(dot + 1)); serverWeight.put(sid, Long.parseLong(value)); } else if (key.equals("version")) { version = Long.parseLong(value, 16); } } for (QuorumServer qs : allMembers.values()) { Long id = qs.id; if (qs.type == LearnerType.PARTICIPANT) { if (!serverGroup.containsKey(id)) { throw new ConfigException("Server " + id + "is not in a group"); } if (!serverWeight.containsKey(id)) { serverWeight.put(id, (long) 1); } } } computeGroupWeight(); } public Map getAllMembers() { return allMembers; } public String toString() { StringWriter sw = new StringWriter(); for (QuorumServer member : getAllMembers().values()) { String key = "server." + member.id; String value = member.toString(); sw.append(key); sw.append('='); sw.append(value); sw.append('\n'); } Map groups = new HashMap<>(); for (Entry pair : serverGroup.entrySet()) { Long sid = pair.getKey(); Long gid = pair.getValue(); String str = groups.get(gid); if (str == null) { str = sid.toString(); } else { str = str.concat(":").concat(sid.toString()); } groups.put(gid, str); } for (Entry pair : groups.entrySet()) { Long gid = pair.getKey(); String key = "group." + gid.toString(); String value = pair.getValue(); sw.append(key); sw.append('='); sw.append(value); sw.append('\n'); } for (Entry pair : serverWeight.entrySet()) { Long sid = pair.getKey(); String key = "weight." + sid.toString(); String value = pair.getValue().toString(); sw.append(key); sw.append('='); sw.append(value); sw.append('\n'); } sw.append("version=" + Long.toHexString(version)); return sw.toString(); } /** * This method pre-computes the weights of groups to speed up processing * when validating a given set. We compute the weights of groups in * different places, so we have a separate method. */ private void computeGroupWeight() { for (Entry entry : serverGroup.entrySet()) { Long sid = entry.getKey(); Long gid = entry.getValue(); if (!groupWeight.containsKey(gid)) { groupWeight.put(gid, serverWeight.get(sid)); } else { long totalWeight = serverWeight.get(sid) + groupWeight.get(gid); groupWeight.put(gid, totalWeight); } } /* * Do not consider groups with weight zero */ for (long weight : groupWeight.values()) { LOG.debug("Group weight: {}", weight); if (weight == ((long) 0)) { numGroups--; LOG.debug("One zero-weight group: 1, {}", numGroups); } } } /** * Verifies if a given set is a quorum. */ public boolean containsQuorum(Set set) { HashMap expansion = new HashMap<>(); /* * Adds up weights per group */ LOG.debug("Set size: {}", set.size()); if (set.size() == 0) { return false; } for (long sid : set) { Long gid = serverGroup.get(sid); if (gid == null) { continue; } if (!expansion.containsKey(gid)) { expansion.put(gid, serverWeight.get(sid)); } else { long totalWeight = serverWeight.get(sid) + expansion.get(gid); expansion.put(gid, totalWeight); } } /* * Check if all groups have majority */ int majGroupCounter = 0; for (Entry entry : expansion.entrySet()) { Long gid = entry.getKey(); LOG.debug("Group info: {}, {}, {}", entry.getValue(), gid, groupWeight.get(gid)); if (entry.getValue() > (groupWeight.get(gid) / 2)) { majGroupCounter++; } } LOG.debug("Majority group counter: {}, {}", majGroupCounter, numGroups); if ((majGroupCounter > (numGroups / 2))) { LOG.debug("Positive set size: {}", set.size()); return true; } else { LOG.debug("Negative set size: {}", set.size()); return false; } } public Map getVotingMembers() { return participatingMembers; } public Map getObservingMembers() { return observingMembers; } public long getVersion() { return version; } public void setVersion(long ver) { version = ver; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032717 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/QuorumMaj.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/Qu0100644 0000000 0000000 00000012074 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.flexible; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class implements a validator for majority quorums. The implementation is * straightforward. * */ public class QuorumMaj implements QuorumVerifier { private static final Logger LOG = LoggerFactory.getLogger(QuorumMaj.class); private Map allMembers = new HashMap<>(); private Map votingMembers = new HashMap<>(); private Map observingMembers = new HashMap<>(); private long version = 0; protected int half; public int hashCode() { assert false : "hashCode not designed"; return 42; // any arbitrary constant will do } public boolean equals(Object o) { if (!(o instanceof QuorumMaj)) { return false; } QuorumMaj qm = (QuorumMaj) o; if (qm.getVersion() == version) { return true; } if (allMembers.size() != qm.getAllMembers().size()) { return false; } for (QuorumServer qs : allMembers.values()) { QuorumServer qso = qm.getAllMembers().get(qs.id); if (qso == null || !qs.equals(qso)) { return false; } } return true; } /** * Defines a majority to avoid computing it every time. * */ public QuorumMaj(Map allMembers) { this.allMembers = allMembers; for (QuorumServer qs : allMembers.values()) { if (qs.type == LearnerType.PARTICIPANT) { votingMembers.put(Long.valueOf(qs.id), qs); } else { observingMembers.put(Long.valueOf(qs.id), qs); } } half = votingMembers.size() / 2; } public QuorumMaj(Properties props) throws ConfigException { for (Entry entry : props.entrySet()) { String key = entry.getKey().toString(); String value = entry.getValue().toString(); if (key.startsWith("server.")) { int dot = key.indexOf('.'); long sid = Long.parseLong(key.substring(dot + 1)); QuorumServer qs = new QuorumServer(sid, value); allMembers.put(Long.valueOf(sid), qs); if (qs.type == LearnerType.PARTICIPANT) { votingMembers.put(Long.valueOf(sid), qs); } else { observingMembers.put(Long.valueOf(sid), qs); } } else if (key.equals("version")) { version = Long.parseLong(value, 16); } } half = votingMembers.size() / 2; } /** * Returns weight of 1 by default. * * @param id */ public long getWeight(long id) { return 1; } public String toString() { StringBuilder sw = new StringBuilder(); for (QuorumServer member : getAllMembers().values()) { String key = "server." + member.id; String value = member.toString(); sw.append(key); sw.append('='); sw.append(value); sw.append('\n'); } String hexVersion = Long.toHexString(version); sw.append("version="); sw.append(hexVersion); return sw.toString(); } /** * Verifies if a set is a majority. Assumes that ackSet contains acks only * from votingMembers */ public boolean containsQuorum(Set ackSet) { return (ackSet.size() > half); } public Map getAllMembers() { return allMembers; } public Map getVotingMembers() { return votingMembers; } public Map getObservingMembers() { return observingMembers; } public long getVersion() { return version; } public void setVersion(long ver) { version = ver; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032707 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/QuorumOracleMaj.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/Qu0100644 0000000 0000000 00000015467 15051152474 034247 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.flexible; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.io.FilenameUtils; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.SyncedLearnerTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * * QuorumOracleMaj is a subclass of QuorumMaj. * * QuorumOracleMaj is designed to be functional in a 2-nodes configuration. The only method that this class overrides super * class' method is containsQuorum(). Besides the check of oracle, it also checks the number of voting member. Whenever the * number of voting members is greater than 2. QuorumOracleMaj shall function as hook to its super class. * */ public class QuorumOracleMaj extends QuorumMaj { private static final Logger LOG = LoggerFactory.getLogger(QuorumOracleMaj.class); private String oracle = null; private final AtomicBoolean needOracle = new AtomicBoolean(true); public QuorumOracleMaj(Map allMembers, String oraclePath) { super(allMembers); setOracle(oraclePath); } public QuorumOracleMaj(Properties props, String oraclePath) throws QuorumPeerConfig.ConfigException { super(props); setOracle(oraclePath); } private void setOracle(String path) { if (oracle == null) { oracle = path; LOG.info("Oracle is set to {}", path); } else { LOG.warn("Oracle is already set. Ignore:{}", path); } } @Override public boolean updateNeedOracle(List forwardingFollowers) { // Do we have the quorum needOracle.set(forwardingFollowers.isEmpty() && super.getVotingMembers().size() == 2); return needOracle.get(); } @Override public boolean askOracle() { FileReader fr = null; try { int read; fr = new FileReader(FilenameUtils.getFullPath(oracle) + FilenameUtils.getName(oracle)); read = fr.read(); LOG.debug("Oracle says:{}", (char) read); fr.close(); return (char) read == '1'; } catch (Exception e) { e.printStackTrace(); if (oracle == null) { LOG.error("Oracle is not set, return false"); } return false; } finally { if (fr != null) { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } @Override public boolean getNeedOracle() { return needOracle.get(); } @Override public String getOraclePath() { return oracle; } @Override public boolean overrideQuorumDecision(List forwardingFollowers) { return updateNeedOracle(forwardingFollowers) && askOracle(); } @Override public boolean revalidateOutstandingProp(Leader self, ArrayList outstandingProposal, long lastCommitted) { LOG.debug("Start Revalidation outstandingProposals"); try { while (outstandingProposal.size() >= 1) { outstandingProposal.sort((o1, o2) -> (int) (o1.getZxid() - o2.getZxid())); Leader.Proposal p; int i = 0; while (i < outstandingProposal.size()) { p = outstandingProposal.get(i); if (p.getZxid() > lastCommitted) { LOG.debug("Re-validate outstanding proposal: 0x{} size:{} lastCommitted:{}", Long.toHexString(p.getZxid()), outstandingProposal.size(), Long.toHexString(lastCommitted)); if (!self.tryToCommit(p, p.getZxid(), null)) { break; } else { lastCommitted = p.getZxid(); outstandingProposal.remove(p); } } } } } catch (Exception e) { e.printStackTrace(); return false; } LOG.debug("Finish Revalidation outstandingProposals"); return true; } @Override public boolean revalidateVoteset(SyncedLearnerTracker voteSet, boolean timeout) { return voteSet != null && voteSet.hasAllQuorums() && timeout; } @Override public boolean containsQuorum(Set ackSet) { if (oracle == null || getVotingMembers().size() > 2) { return super.containsQuorum(ackSet); } else if (!super.containsQuorum(ackSet)) { if (getNeedOracle()) { LOG.debug("We lose the quorum, but we do not have any valid followers Oracle:{}", askOracle()); return askOracle(); } else { return false; } } else { return true; } } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } QuorumOracleMaj qm = (QuorumOracleMaj) o; if (qm.getVersion() == super.getVersion()) { return true; } if (super.getAllMembers().size() != qm.getAllMembers().size()) { return false; } for (QuorumPeer.QuorumServer qs : super.getAllMembers().values()) { QuorumPeer.QuorumServer qso = qm.getAllMembers().get(qs.id); if (qso == null || !qs.equals(qso)) { return false; } } return true; } @Override public int hashCode() { assert false : "hashCode not designed"; return 43; // any arbitrary constant will do } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032724 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/QuorumVerifier.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/flexible/Qu0100644 0000000 0000000 00000005022 15051152474 034231 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.flexible; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.SyncedLearnerTracker; /** * All quorum validators have to implement a method called * containsQuorum, which verifies if a HashSet of server * identifiers constitutes a quorum. * */ public interface QuorumVerifier { long getWeight(long id); boolean containsQuorum(Set set); long getVersion(); void setVersion(long ver); Map getAllMembers(); Map getVotingMembers(); Map getObservingMembers(); boolean equals(Object o); /* * Only QuorumOracleMaj will implement these methods. Other class will raise warning if the methods are called and * return false always. * */ default boolean updateNeedOracle(List forwardingFollowers) { return false; } default boolean getNeedOracle() { return false; } default boolean askOracle() { return false; } default boolean overrideQuorumDecision(List forwardingFollowers) { return false; } default boolean revalidateOutstandingProp(Leader self, ArrayList outstandingProposal, long lastCommitted) { return false; } default boolean revalidateVoteset(SyncedLearnerTracker voteSet, boolean timeout) { return false; } default String getOraclePath() { return null; }; String toString(); } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/AdHash.java0100644 0000000 0000000 00000004353 15051152474 033555 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; /** * This incremental hash is used to keep track of the hash of * the data tree to that we can quickly validate that things * are in sync. * * See the excellent paper: A New Paradigm for collision-free hashing: * Incrementality at reduced cost, M. Bellare and D. Micciancio */ public class AdHash { /* we use 64 bits so that we can be fast an efficient */ private volatile long hash; /** * Add new digest to the hash value maintained in this class. * * @param digest the value to add on * @return the AdHash itself for chained operations */ public AdHash addDigest(long digest) { hash += digest; return this; } /** * Remove the digest from the hash value. * * @param digest the value to remove * @return the AdHash itself for chained operations */ public AdHash removeDigest(long digest) { hash -= digest; return this; } /** * Return the long value of the hash. */ public long getHash() { return hash; } @Override public boolean equals(Object other) { return other instanceof AdHash && ((AdHash) other).hash == this.hash; } @Override public int hashCode() { return Long.hashCode(hash); } @Override public String toString() { return Long.toHexString(hash); } public void clear() { hash = 0; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000156 15051152474 032724 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/AuthUtil.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/AuthUtil.java0100644 0000000 0000000 00000006255 15051152474 034167 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.apache.zookeeper.data.ClientInfo; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.auth.AuthenticationProvider; import org.apache.zookeeper.server.auth.ProviderRegistry; public final class AuthUtil { private AuthUtil() { //Utility classes should not have public constructors } /** * Gives user name * * @param id contains scheme and authentication info * @return returns null if authentication scheme does not exist or * authentication provider returns null as user */ public static String getUser(Id id) { AuthenticationProvider provider = ProviderRegistry.getProvider(id.getScheme()); return provider == null ? null : provider.getUserName(id.getId()); } /** * Returns a formatted, comma-separated list of the user IDs held * in {@code authInfo}, or {@code null} if no user IDs were found. * * Note that while the result may be easy on the eyes, it is * underspecified: it does not mention the corresponding {@code * scheme}, nor are its components escaped. It is intended for * for logging, and is not a security feature. * * @param authInfo A list of {@code Id} objects, or {@code null}. * @return a comma-separated list of user IDs, or {@code null} if * no user IDs were found. */ public static String getUsers(List authInfo) { if (authInfo == null) { return null; } String formatted = authInfo.stream() .map(AuthUtil::getUser) .filter(name -> name != null && !name.trim().isEmpty()) .collect(Collectors.joining(",")); return formatted.isEmpty() ? null : formatted; } /** * Gets user from id to prepare ClientInfo. * * @param authInfo List of id objects. id contains scheme and authentication info * @return list of client authentication info */ public static List getClientInfos(List authInfo) { List clientAuthInfo = new ArrayList<>(authInfo.size()); authInfo.forEach(id -> { String user = getUser(id); clientAuthInfo.add(new ClientInfo(id.getScheme(), user == null ? "" : user)); }); return clientAuthInfo; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000160 15051152474 032717 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/BitHashSet.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/BitHashSet.ja0100644 0000000 0000000 00000011672 15051152474 034076 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.util.BitSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * Using BitSet to store all the elements, and use HashSet to cache limited * number of elements to find a balance between memory and time complexity. * * Without HashSet, we need to use O(N) time to get the elements, N is * the bit numbers in elementBits. But we need to keep the size small to make * sure it doesn't cost too much in memory, there is a trade off between * memory and time complexity. * * Previously, was deciding to dynamically switch between SparseBitSet and * HashSet based on the memory consumption, but it will take time to copy * data over and may have some herd effect of keep copying data from one * data structure to anther. The current solution can do a very good job * given most of the paths have limited number of elements. */ public class BitHashSet implements Iterable { /** * Change to SparseBitSet if we we want to optimize more, the number of * elements on a single server is usually limited, so BitSet should be * fine. */ private final BitSet elementBits = new BitSet(); /** * HashSet is used to optimize the iterating, if there is a single * element in this BitHashSet, but the bit is very large, without * HashSet we need to go through all the words before return that * element, which is not efficient. */ private final Set cache = new HashSet<>(); private final int cacheSize; // To record how many elements in this set. private int elementCount = 0; public BitHashSet() { this(Integer.getInteger("zookeeper.bitHashCacheSize", 10)); } public BitHashSet(int cacheSize) { this.cacheSize = cacheSize; } public synchronized boolean add(Integer elementBit) { if (elementBit == null || elementBits.get(elementBit)) { return false; } if (cache.size() < cacheSize) { cache.add(elementBit); } elementBits.set(elementBit); elementCount++; return true; } /** * Remove the watches, and return the number of watches being removed. */ public synchronized int remove(Set bitSet, BitSet bits) { cache.removeAll(bitSet); elementBits.andNot(bits); int elementCountBefore = elementCount; elementCount = elementBits.cardinality(); return elementCountBefore - elementCount; } public synchronized boolean remove(Integer elementBit) { if (elementBit == null || !elementBits.get(elementBit)) { return false; } cache.remove(elementBit); elementBits.clear(elementBit); elementCount--; return true; } public synchronized boolean contains(Integer elementBit) { if (elementBit == null) { return false; } return elementBits.get(elementBit); } public synchronized int size() { return elementCount; } /** * This function is not thread-safe, need to synchronized when * iterate through this set. */ @Override public Iterator iterator() { // sample current size at the beginning int currentSize = size(); if (cache.size() == currentSize) { return cache.iterator(); } return new Iterator() { int returnedCount = 0; int bitIndex = 0; @Override public boolean hasNext() { return returnedCount < currentSize; } @Override public Integer next() { int bit = elementBits.nextSetBit(bitIndex); bitIndex = bit + 1; returnedCount++; return bit; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } // visible for test public synchronized int cachedSize() { return cache.size(); } public synchronized boolean isEmpty() { return elementCount == 0; } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/BitMap.java0100644 0000000 0000000 00000010141 15051152474 033571 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.BitSet; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This is a helper class to maintain the bit to specific value and the * reversed value to bit mapping. */ public class BitMap { private final Map value2Bit = new HashMap<>(); private final Map bit2Value = new HashMap<>(); private final BitSet freedBitSet = new BitSet(); private Integer nextBit = Integer.valueOf(0); private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "SpotBugs false positive") public Integer add(T value) { /* * Optimized for code which will add the same value again and again, * more specifically this is used to add new bit for watcher, and * the same watcher may watching thousands or even millions of nodes, * which will call add the same value of this function, check exist * using read lock will optimize the performance here. */ Integer bit = getBit(value); if (bit != null) { return bit; } rwLock.writeLock().lock(); try { bit = value2Bit.get(value); if (bit != null) { return bit; } bit = freedBitSet.nextSetBit(0); if (bit > -1) { freedBitSet.clear(bit); } else { bit = nextBit++; } value2Bit.put(value, bit); bit2Value.put(bit, value); return bit; } finally { rwLock.writeLock().unlock(); } } public T get(int bit) { rwLock.readLock().lock(); try { return bit2Value.get(bit); } finally { rwLock.readLock().unlock(); } } public Integer getBit(T value) { rwLock.readLock().lock(); try { return value2Bit.get(value); } finally { rwLock.readLock().unlock(); } } public int remove(T value) { /* * remove only called once when the session is closed, so use write * lock directly without checking read lock. */ rwLock.writeLock().lock(); try { Integer bit = value2Bit.get(value); if (bit == null) { return -1; } value2Bit.remove(value); bit2Value.remove(bit); freedBitSet.set(bit); return bit; } finally { rwLock.writeLock().unlock(); } } public T remove(int bit) { rwLock.writeLock().lock(); try { T value = bit2Value.get(bit); if (value == null) { return null; } value2Bit.remove(value); bit2Value.remove(bit); freedBitSet.set(bit); return value; } finally { rwLock.writeLock().unlock(); } } public int size() { rwLock.readLock().lock(); try { return value2Bit.size(); } finally { rwLock.readLock().unlock(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000164 15051152474 032723 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/CircularBuffer.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/CircularBuffe0100644 0000000 0000000 00000006327 15051152474 034224 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.lang.reflect.Array; import java.util.concurrent.atomic.AtomicInteger; /** * Thread safe FIFO CircularBuffer implementation. * When the buffer is full write operation overwrites the oldest element. * * Fun thing @todo, make this lock free as this is called on every quorum message */ public class CircularBuffer { private final T[] buffer; private final int capacity; private int oldest; private AtomicInteger numberOfElements = new AtomicInteger(); @SuppressWarnings("unchecked") public CircularBuffer(Class clazz, int capacity) { if (capacity <= 0) { throw new IllegalArgumentException("CircularBuffer capacity should be greater than 0"); } this.buffer = (T[]) Array.newInstance(clazz, capacity); this.capacity = capacity; } /** * Puts elements in the next available index in the array. * If the array is full the oldest element is replaced with * the new value. * @param element */ public synchronized void write(T element) { int newSize = numberOfElements.incrementAndGet(); if (newSize > capacity) { buffer[oldest] = element; oldest = ++oldest % capacity; numberOfElements.decrementAndGet(); } else { int index = (oldest + numberOfElements.get() - 1) % capacity; buffer[index] = element; } } /** * Reads from the buffer in a FIFO manner. * Returns the oldest element in the buffer if the buffer is not empty * Returns null if the buffer is empty * @return the oldest element in the buffer */ public synchronized T take() { int newSize = numberOfElements.decrementAndGet(); if (newSize < 0) { numberOfElements.incrementAndGet(); return null; } T polled = buffer[oldest]; oldest = ++oldest % capacity; return polled; } public synchronized T peek() { if (numberOfElements.get() <= 0) { return null; } return buffer[oldest]; } public int size() { return numberOfElements.get(); } public boolean isEmpty() { return numberOfElements.get() <= 0; } public boolean isFull() { return numberOfElements.get() >= capacity; } public synchronized void reset() { numberOfElements.set(0); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000161 15051152474 032720 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/ConfigUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/ConfigUtils.j0100644 0000000 0000000 00000011140 15051152474 034153 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.io.IOException; import java.io.StringReader; import java.util.Map.Entry; import java.util.Properties; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; public class ConfigUtils { public static String getClientConfigStr(String configData) { Properties props = new Properties(); try { props.load(new StringReader(configData)); } catch (IOException e) { e.printStackTrace(); return ""; } StringBuffer sb = new StringBuffer(); boolean first = true; String version = ""; for (Entry entry : props.entrySet()) { String key = entry.getKey().toString().trim(); String value = entry.getValue().toString().trim(); if (key.equals("version")) { version = value; } if (!key.startsWith("server.")) { continue; } QuorumPeer.QuorumServer qs; try { qs = new QuorumPeer.QuorumServer(-1, value); } catch (ConfigException e) { e.printStackTrace(); continue; } if (!first) { sb.append(","); } else { first = false; } if (null != qs.clientAddr) { sb.append(qs.clientAddr.getHostString() + ":" + qs.clientAddr.getPort()); } } return version + " " + sb.toString(); } /** * Gets host and port by splitting server config * with support for IPv6 literals * @return String[] first element being the * IP address and the next being the port * @param s server config, server:port */ public static String[] getHostAndPort(String s) throws ConfigException { if (s.startsWith("[")) { int i = s.indexOf("]"); if (i < 0) { throw new ConfigException(s + " starts with '[' but has no matching ']:'"); } if (i + 2 == s.length()) { throw new ConfigException(s + " doesn't have a port after colon"); } if (i + 2 < s.length()) { String[] sa = s.substring(i + 2).split(":"); String[] nsa = new String[sa.length + 1]; nsa[0] = s.substring(1, i); System.arraycopy(sa, 0, nsa, 1, sa.length); return nsa; } return new String[]{s.replaceAll("\\[|\\]", "")}; } else { return s.split(":"); } } /** * Some old configuration properties are not configurable in zookeeper configuration file * zoo.cfg. To make these properties configurable in zoo.cfg old properties are prepended * with zookeeper. For example prop.x.y.z changed to zookeeper.prop.x.y.z. But for backward * compatibility both prop.x.y.z and zookeeper.prop.x.y.z should be supported. * This method first gets value from new property, if first property is not configured * then gets value from old property * * @param newPropertyKey new property key which starts with zookeeper. * @return either new or old system property value. Null if none of the properties are set. */ public static String getPropertyBackwardCompatibleWay(String newPropertyKey) { String newKeyValue = System.getProperty(newPropertyKey); if (newKeyValue != null) { return newKeyValue.trim(); } String oldPropertyKey = newPropertyKey.replace("zookeeper.", ""); String oldKeyValue = System.getProperty(oldPropertyKey); if (oldKeyValue != null) { return oldKeyValue.trim(); } return null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000165 15051152474 032724 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/JvmPauseMonitor.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/JvmPauseMonit0100644 0000000 0000000 00000017651 15051152474 034253 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This code is originally from hadoop-common, see: * https://github.com/apache/hadoop/blob/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/JvmPauseMonitor.java * * Class which sets up a simple thread which runs in a loop sleeping * for a short interval of time. If the sleep takes significantly longer * than its target time, it implies that the JVM or host machine has * paused processing, which may cause other problems. If such a pause is * detected, the thread logs a message. */ public class JvmPauseMonitor { private static final Logger LOG = LoggerFactory.getLogger(JvmPauseMonitor.class); public static final String JVM_PAUSE_MONITOR_FEATURE_SWITCH_KEY = "jvm.pause.monitor"; /** The target sleep time */ protected long sleepTimeMs; public static final String SLEEP_TIME_MS_KEY = "jvm.pause.sleep.time.ms"; public static final long SLEEP_TIME_MS_DEFAULT = 500; /** log WARN if we detect a pause longer than this threshold */ protected long warnThresholdMs; public static final String WARN_THRESHOLD_KEY = "jvm.pause.warn-threshold.ms"; public static final long WARN_THRESHOLD_DEFAULT = 10000; /** log INFO if we detect a pause longer than this threshold */ protected long infoThresholdMs; public static final String INFO_THRESHOLD_KEY = "jvm.pause.info-threshold.ms"; public static final long INFO_THRESHOLD_DEFAULT = 1000; private long numGcWarnThresholdExceeded = 0; private long numGcInfoThresholdExceeded = 0; private long totalGcExtraSleepTime = 0; private Thread monitorThread; private volatile boolean shouldRun = true; public JvmPauseMonitor(QuorumPeerConfig config) { this.warnThresholdMs = config.getJvmPauseWarnThresholdMs(); this.infoThresholdMs = config.getJvmPauseInfoThresholdMs(); this.sleepTimeMs = config.getJvmPauseSleepTimeMs(); } public JvmPauseMonitor(ServerConfig config) { this.warnThresholdMs = config.getJvmPauseWarnThresholdMs(); this.infoThresholdMs = config.getJvmPauseInfoThresholdMs(); this.sleepTimeMs = config.getJvmPauseSleepTimeMs(); } public void serviceStart() { monitorThread = new Thread(new JVMMonitor()); monitorThread.setDaemon(true); monitorThread.start(); } public void serviceStop() { shouldRun = false; if (monitorThread != null) { monitorThread.interrupt(); try { monitorThread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } public boolean isStarted() { return monitorThread != null; } public long getNumGcWarnThresholdExceeded() { return numGcWarnThresholdExceeded; } public long getNumGcInfoThresholdExceeded() { return numGcInfoThresholdExceeded; } public long getTotalGcExtraSleepTime() { return totalGcExtraSleepTime; } private String formatMessage(long extraSleepTime, Map gcTimesAfterSleep, Map gcTimesBeforeSleep) { Set gcBeanNames = new HashSet<>(gcTimesAfterSleep.keySet()); gcBeanNames.retainAll(gcTimesBeforeSleep.keySet()); List gcDiffs = new ArrayList<>(); for (String name : gcBeanNames) { GcTimes diff = gcTimesAfterSleep.get(name).subtract(gcTimesBeforeSleep.get(name)); if (diff.gcCount != 0) { gcDiffs.add("GC pool '" + name + "' had collection(s): " + diff.toString()); } } String ret = String.format("Detected pause in JVM or host machine (eg GC): pause of approximately %d ms, " + "total pause: info level: %d, warn level: %d %n", extraSleepTime, numGcInfoThresholdExceeded, numGcWarnThresholdExceeded); if (gcDiffs.isEmpty()) { ret += ("No GCs detected"); } else { ret += String.join("\n", gcDiffs); } return ret; } private Map getGcTimes() { Map map = new HashMap<>(); List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); for (GarbageCollectorMXBean gcBean : gcBeans) { map.put(gcBean.getName(), new GcTimes(gcBean)); } return map; } private static class GcTimes { private long gcCount; private long gcTimeMillis; private GcTimes(GarbageCollectorMXBean gcBean) { gcCount = gcBean.getCollectionCount(); gcTimeMillis = gcBean.getCollectionTime(); } private GcTimes(long count, long time) { this.gcCount = count; this.gcTimeMillis = time; } private GcTimes subtract(GcTimes other) { return new GcTimes(this.gcCount - other.gcCount, this.gcTimeMillis - other.gcTimeMillis); } public String toString() { return "count=" + gcCount + " time=" + gcTimeMillis + "ms"; } } private class JVMMonitor implements Runnable { @Override public void run() { Map gcTimesBeforeSleep = getGcTimes(); LOG.info("Starting JVM Pause Monitor with infoThresholdMs:{} warnThresholdMs:{} and sleepTimeMs:{}", infoThresholdMs, warnThresholdMs, sleepTimeMs); while (shouldRun) { long startTime = Instant.now().toEpochMilli(); try { Thread.sleep(sleepTimeMs); } catch (InterruptedException ie) { return; } long endTime = Instant.now().toEpochMilli(); long extraSleepTime = (endTime - startTime) - sleepTimeMs; if (extraSleepTime >= 0) { ServerMetrics.getMetrics().JVM_PAUSE_TIME.add(extraSleepTime); } Map gcTimesAfterSleep = getGcTimes(); if (extraSleepTime > warnThresholdMs) { ++numGcWarnThresholdExceeded; LOG.warn(formatMessage(extraSleepTime, gcTimesAfterSleep, gcTimesBeforeSleep)); } else if (extraSleepTime > infoThresholdMs) { ++numGcInfoThresholdExceeded; LOG.info(formatMessage(extraSleepTime, gcTimesAfterSleep, gcTimesBeforeSleep)); } totalGcExtraSleepTime += extraSleepTime; gcTimesBeforeSleep = gcTimesAfterSleep; } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000162 15051152474 032721 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/KerberosUtil.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/KerberosUtil.0100644 0000000 0000000 00000002070 15051152474 034167 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import javax.security.auth.kerberos.KerberosPrincipal; public class KerberosUtil { public static String getDefaultRealm() throws IllegalArgumentException { return new KerberosPrincipal("tmp", 1).getRealm(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000160 15051152474 032717 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/LogChopper.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/LogChopper.ja0100644 0000000 0000000 00000014664 15051152474 034146 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.Adler32; import java.util.zip.Checksum; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.yetus.audience.InterfaceAudience; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.persistence.FileHeader; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.util.ServiceUtils; /** * this class will chop the log at the specified zxid */ @InterfaceAudience.Public public class LogChopper { public static void main(String[] args) { ExitCode rc = ExitCode.INVALID_INVOCATION; if (args.length != 3) { System.out.println("Usage: LogChopper zxid_to_chop_to txn_log_to_chop chopped_filename"); System.out.println(" this program will read the txn_log_to_chop file and copy all the transactions"); System.out.println(" from it up to (and including) the given zxid into chopped_filename."); ServiceUtils.requestSystemExit(rc.getValue()); } String txnLog = args[1]; String choppedLog = args[2]; try (InputStream is = new BufferedInputStream(new FileInputStream(txnLog)); OutputStream os = new BufferedOutputStream(new FileOutputStream(choppedLog))) { long zxid = Long.decode(args[0]); if (chop(is, os, zxid)) { rc = ExitCode.EXECUTION_FINISHED; } } catch (Exception e) { System.out.println("Got exception: " + e.getMessage()); } ServiceUtils.requestSystemExit(rc.getValue()); } public static boolean chop(InputStream is, OutputStream os, long zxid) throws IOException { BinaryInputArchive logStream = BinaryInputArchive.getArchive(is); BinaryOutputArchive choppedStream = BinaryOutputArchive.getArchive(os); FileHeader fhdr = new FileHeader(); fhdr.deserialize(logStream, "fileheader"); if (fhdr.getMagic() != FileTxnLog.TXNLOG_MAGIC) { System.err.println("Invalid magic number in txn log file"); return false; } System.out.println("ZooKeeper Transactional Log File with dbid " + fhdr.getDbid() + " txnlog format version " + fhdr.getVersion()); fhdr.serialize(choppedStream, "fileheader"); int count = 0; boolean hasZxid = false; long previousZxid = -1; while (true) { long crcValue; byte[] bytes; try { crcValue = logStream.readLong("crcvalue"); bytes = logStream.readBuffer("txnEntry"); } catch (EOFException e) { System.out.println("EOF reached after " + count + " txns."); // returning false because nothing was chopped return false; } if (bytes.length == 0) { // Since we preallocate, we define EOF to be an // empty transaction System.out.println("EOF reached after " + count + " txns."); // returning false because nothing was chopped return false; } 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()); } TxnLogEntry entry = SerializeUtils.deserializeTxn(bytes); TxnHeader hdr = entry.getHeader(); Record txn = entry.getTxn(); if (logStream.readByte("EOR") != 'B') { System.out.println("Last transaction was partial."); throw new EOFException("Last transaction was partial."); } final long txnZxid = hdr.getZxid(); if (txnZxid == zxid) { hasZxid = true; } // logging the gap to make the inconsistency investigation easier if (previousZxid != -1 && txnZxid != previousZxid + 1) { long txnEpoch = ZxidUtils.getEpochFromZxid(txnZxid); long txnCounter = ZxidUtils.getCounterFromZxid(txnZxid); long previousEpoch = ZxidUtils.getEpochFromZxid(previousZxid); if (txnEpoch == previousEpoch) { System.out.println(String.format("There is intra-epoch gap between %x and %x", previousZxid, txnZxid)); } else if (txnCounter != 1) { System.out.println(String.format("There is inter-epoch gap between %x and %x", previousZxid, txnZxid)); } } previousZxid = txnZxid; if (txnZxid > zxid) { if (count == 0 || !hasZxid) { System.out.println(String.format("This log does not contain zxid %x", zxid)); return false; } System.out.println(String.format("Chopping at %x new log has %d records", zxid, count)); return true; } choppedStream.writeLong(crcValue, "crcvalue"); choppedStream.writeBuffer(bytes, "txnEntry"); choppedStream.writeByte((byte) 'B', "EOR"); count++; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000164 15051152474 032723 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/MessageTracker.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/MessageTracke0100644 0000000 0000000 00000012460 15051152474 034221 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.zookeeper.server.quorum.Leader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provides a way of buffering sentBuffer and receivedBuffer messages in order. * It uses EvictingQueue of size BUFFERED_MESSAGE_SIZE to store the messages. * When the queue is full it overrides the oldest in a circular manner. * This class does doe not provide thread safety. */ public class MessageTracker { private static final Logger LOG = LoggerFactory.getLogger(MessageTracker.class); private final CircularBuffer sentBuffer; private final CircularBuffer receivedBuffer; public static final String MESSAGE_TRACKER_BUFFER_SIZE = "zookeeper.messageTracker.BufferSize"; public static final String MESSAGE_TRACKER_ENABLED = "zookeeper.messageTracker.Enabled"; public static final int BUFFERED_MESSAGE_SIZE; private static final boolean enabled; static { BUFFERED_MESSAGE_SIZE = Integer.getInteger(MESSAGE_TRACKER_BUFFER_SIZE, 10); enabled = Boolean.getBoolean(MESSAGE_TRACKER_ENABLED); } public MessageTracker(int buffer_size) { this.sentBuffer = new CircularBuffer<>(BufferedMessage.class, buffer_size); this.receivedBuffer = new CircularBuffer<>(BufferedMessage.class, buffer_size); } public void trackSent(long timestamp) { if (enabled) { sentBuffer.write(new BufferedMessage(timestamp)); } } public void trackSent(int packetType) { if (enabled) { sentBuffer.write(new BufferedMessage(packetType)); } } public void trackReceived(long timestamp) { if (enabled) { receivedBuffer.write(new BufferedMessage(timestamp)); } } public void trackReceived(int packetType) { if (enabled) { receivedBuffer.write(new BufferedMessage(packetType)); } } public final BufferedMessage peekSent() { return sentBuffer.peek(); } public final BufferedMessage peekReceived() { return receivedBuffer.peek(); } public final long peekSentTimestamp() { return enabled ? sentBuffer.peek().getTimestamp() : 0; } public final long peekReceivedTimestamp() { return enabled ? receivedBuffer.peek().getTimestamp() : 0; } public void dumpToLog(String serverAddress) { if (!enabled) { return; } logMessages(serverAddress, receivedBuffer, Direction.RECEIVED); logMessages(serverAddress, sentBuffer, Direction.SENT); } private static void logMessages( String serverAddr, CircularBuffer messages, Direction direction) { String sentOrReceivedText = direction == Direction.SENT ? "sentBuffer to" : "receivedBuffer from"; if (messages.isEmpty()) { LOG.info("No buffered timestamps for messages {} {}", sentOrReceivedText, serverAddr); } else { LOG.warn("Last {} timestamps for messages {} {}:", messages.size(), sentOrReceivedText, serverAddr); while (!messages.isEmpty()) { LOG.warn("{} {} {}", sentOrReceivedText, serverAddr, messages.take().toString()); } } } /** * Direction for message track. */ private enum Direction { SENT, RECEIVED } private static class BufferedMessage { private long timestamp; private int messageType; private long getTimestamp() { return timestamp; } BufferedMessage(int messageType) { this.messageType = messageType; this.timestamp = System.currentTimeMillis(); } BufferedMessage(long timestamp) { this.messageType = -1; this.timestamp = timestamp; } @Override /** * ToString examples are as follows: * TimeStamp: 2016-06-06 11:07:58,594 Type: PROPOSAL * TimeStamp: 2016-06-06 11:07:58,187 */ public String toString() { if (messageType == -1) { return "TimeStamp: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS") .format(new Date(timestamp)); } else { return "TimeStamp: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS") .format(new Date(timestamp)) + " Type: " + Leader.getPacketType(messageType); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000156 15051152474 032724 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/OSMXBean.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/OSMXBean.java0100644 0000000 0000000 00000014005 15051152474 033774 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; import java.lang.management.RuntimeMXBean; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is a wrapper for the implementation of * com.sun.management.UnixOperatingSystemMXBean * It will decide to use the sun api or its own implementation * depending on the runtime (vendor) used. */ public class OSMXBean { private static final Logger LOG = LoggerFactory.getLogger(OSMXBean.class); private OperatingSystemMXBean osMbean; private static final boolean ibmvendor = System.getProperty("java.vendor").contains("IBM"); private static final boolean windows = System.getProperty("os.name").startsWith("Windows"); private static final boolean linux = System.getProperty("os.name").startsWith("Linux"); /** * Constructor. Get the running Operating System instance */ public OSMXBean() { this.osMbean = ManagementFactory.getOperatingSystemMXBean(); } /** * Check if the OS is unix. If using the IBM java runtime, this * will only work for linux. * * @return whether this is unix or not. */ public boolean getUnix() { if (windows) { return false; } return (!ibmvendor || linux); } /** * Load the implementation of UnixOperatingSystemMXBean for sun jvm * and runs the desired method. * @param mBeanMethodName : method to run from the interface UnixOperatingSystemMXBean * @return the method result */ private Long getOSUnixMXBeanMethod(String mBeanMethodName) { Object unixos; Class classRef; Method mBeanMethod; try { classRef = Class.forName("com.sun.management.UnixOperatingSystemMXBean"); if (classRef.isInstance(osMbean)) { mBeanMethod = classRef.getDeclaredMethod(mBeanMethodName); unixos = classRef.cast(osMbean); return (Long) mBeanMethod.invoke(unixos); } } catch (Exception e) { LOG.warn("Not able to load class or method for com.sun.managment.UnixOperatingSystemMXBean.", e); } return null; } /** * Get the number of opened filed descriptor for the runtime jvm. * If sun java, it will use the com.sun.management interfaces. * Otherwise, this methods implements it (linux only). * @return number of open file descriptors for the jvm */ public long getOpenFileDescriptorCount() { Long ofdc; if (!ibmvendor) { ofdc = getOSUnixMXBeanMethod("getOpenFileDescriptorCount"); return (ofdc != null ? ofdc.longValue() : -1); } try { //need to get the PID number of the process first RuntimeMXBean rtmbean = ManagementFactory.getRuntimeMXBean(); String rtname = rtmbean.getName(); String[] pidhost = rtname.split("@"); //using linux bash commands to retrieve info Process p = Runtime.getRuntime() .exec(new String[]{"bash", "-c", "ls /proc/" + pidhost[0] + "/fdinfo | wc -l"}); InputStream in = p.getInputStream(); BufferedReader output = new BufferedReader(new InputStreamReader(in)); try { String openFileDesCount; if ((openFileDesCount = output.readLine()) != null) { return Long.parseLong(openFileDesCount); } } finally { if (output != null) { output.close(); } } } catch (IOException ie) { LOG.warn("Not able to get the number of open file descriptors", ie); } return -1; } /** * Get the number of the maximum file descriptors the system can use. * If sun java, it will use the com.sun.management interfaces. * Otherwise, this methods implements it (linux only). * @return max number of file descriptors the operating system can use. */ public long getMaxFileDescriptorCount() { Long mfdc; if (!ibmvendor) { mfdc = getOSUnixMXBeanMethod("getMaxFileDescriptorCount"); return (mfdc != null ? mfdc.longValue() : -1); } try { //using linux bash commands to retrieve info Process p = Runtime.getRuntime().exec(new String[]{"bash", "-c", "ulimit -n"}); InputStream in = p.getInputStream(); BufferedReader output = new BufferedReader(new InputStreamReader(in)); try { String maxFileDesCount; if ((maxFileDesCount = output.readLine()) != null) { return Long.parseLong(maxFileDesCount); } } finally { if (output != null) { output.close(); } } } catch (IOException ie) { LOG.warn("Not able to get the max number of file descriptors", ie); } return -1; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000167 15051152474 032726 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/QuotaMetricsUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/QuotaMetricsU0100644 0000000 0000000 00000016524 15051152474 034255 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; public final class QuotaMetricsUtils { public static final String QUOTA_COUNT_LIMIT_PER_NAMESPACE = "quota_count_limit_per_namespace"; public static final String QUOTA_BYTES_LIMIT_PER_NAMESPACE = "quota_bytes_limit_per_namespace"; public static final String QUOTA_COUNT_USAGE_PER_NAMESPACE = "quota_count_usage_per_namespace"; public static final String QUOTA_BYTES_USAGE_PER_NAMESPACE = "quota_bytes_usage_per_namespace"; public static final String QUOTA_EXCEEDED_ERROR_PER_NAMESPACE = "quota_exceeded_error_per_namespace"; enum QUOTA_LIMIT_USAGE_METRIC_TYPE {QUOTA_COUNT_LIMIT, QUOTA_BYTES_LIMIT, QUOTA_COUNT_USAGE, QUOTA_BYTES_USAGE} static final String LIMIT_END_STRING = "/" + Quotas.limitNode; static final String STATS_END_STRING = "/" + Quotas.statNode; private QuotaMetricsUtils() { } /** * Traverse the quota subtree and return per namespace quota count limit * * @param dataTree dataTree that contains the quota limit and usage data * @return a map with top namespace as the key and quota count limit as the value * */ public static Map getQuotaCountLimit(final DataTree dataTree) { return getQuotaLimitOrUsage(dataTree, QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_LIMIT); } /** * Traverse the quota subtree and return per namespace quota bytes limit *` * @param dataTree dataTree that contains the quota limit and usage data * @return a map with top namespace as the key and quota bytes limit as the value * */ public static Map getQuotaBytesLimit(final DataTree dataTree) { return getQuotaLimitOrUsage(dataTree, QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT); } /** * Traverse the quota subtree and return per namespace quota count usage * * @param dataTree dataTree that contains the quota limit and usage data * @return a map with top namespace as the key and quota count usage as the value * */ public static Map getQuotaCountUsage(final DataTree dataTree) { return getQuotaLimitOrUsage(dataTree, QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_USAGE); } /** * Traverse the quota subtree and return per namespace quota bytes usage * * @param dataTree dataTree that contains the quota limit and usage data * @return a map with top namespace as the key and quota bytes usage as the value * */ public static Map getQuotaBytesUsage(final DataTree dataTree) { return getQuotaLimitOrUsage(dataTree, QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_USAGE); } // traverse the quota subtree and read the quota limit or usage data private static Map getQuotaLimitOrUsage(final DataTree dataTree, final QUOTA_LIMIT_USAGE_METRIC_TYPE type) { final Map metricsMap = new ConcurrentHashMap<>(); if (dataTree != null) { getQuotaLimitOrUsage(Quotas.quotaZookeeper, metricsMap, type, dataTree); } return metricsMap; } private static void getQuotaLimitOrUsage(final String path, final Map metricsMap, final QUOTA_LIMIT_USAGE_METRIC_TYPE type, final DataTree dataTree) { final DataNode node = dataTree.getNode(path); if (node == null) { return; } final String[] children; synchronized (node) { children = node.getChildren().toArray(new String[0]); } if (children.length == 0) { if (shouldCollect(path, type)) { collectQuotaLimitOrUsage(path, node, metricsMap, type); } return; } for (final String child : children) { getQuotaLimitOrUsage(path + "/" + child, metricsMap, type, dataTree); } } static boolean shouldCollect(final String path, final QUOTA_LIMIT_USAGE_METRIC_TYPE type) { return path.endsWith(LIMIT_END_STRING) && (QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_LIMIT == type || QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT == type) || path.endsWith(STATS_END_STRING) && (QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_USAGE == type || QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_USAGE == type); } static void collectQuotaLimitOrUsage(final String path, final DataNode node, final Map metricsMap, final QUOTA_LIMIT_USAGE_METRIC_TYPE type) { final String namespace = PathUtils.getTopNamespace(Quotas.trimQuotaPath(path)); if (namespace == null) { return; } final byte[] data = node.getData(); if (data == null) { return; } final StatsTrack statsTrack = new StatsTrack(data); switch (type) { case QUOTA_COUNT_LIMIT: aggregateQuotaLimitOrUsage(namespace, metricsMap, getQuotaLimit(statsTrack.getCountHardLimit(), statsTrack.getCount())); break; case QUOTA_BYTES_LIMIT: aggregateQuotaLimitOrUsage(namespace, metricsMap, getQuotaLimit(statsTrack.getByteHardLimit(), statsTrack.getBytes())); break; case QUOTA_COUNT_USAGE: aggregateQuotaLimitOrUsage(namespace, metricsMap, statsTrack.getCount()); break; case QUOTA_BYTES_USAGE: aggregateQuotaLimitOrUsage(namespace, metricsMap, statsTrack.getBytes()); break; default: } } // hard limit takes precedence if specified static long getQuotaLimit(final long hardLimit, final long limit) { return hardLimit > -1 ? hardLimit : limit; } private static void aggregateQuotaLimitOrUsage(final String namespace, final Map metricsMap, final long limitOrUsage) { metricsMap.put(namespace, metricsMap.getOrDefault(namespace, 0).longValue() + limitOrUsage); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000161 15051152474 032720 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/RateLimiter.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/RateLimiter.j0100644 0000000 0000000 00000004003 15051152474 034146 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.common.Time; /** * A class that provides simple interval-based rate limiting implementation. */ public class RateLimiter { private final int rate; private final long intervalInMs; private long lastTimeReset; private final AtomicInteger remained; public RateLimiter(final int rate, final long interval, final TimeUnit unit) { this.rate = rate; this.intervalInMs = unit.toMillis(interval); this.lastTimeReset = Time.currentElapsedTime(); this.remained = new AtomicInteger(rate); } public boolean allow() { final long now = Time.currentElapsedTime(); // reset the rate if interval passed if (now > lastTimeReset + intervalInMs) { remained.set(rate); lastTimeReset = now; } int value = remained.get(); boolean allowed = false; // to handle race condition while (!allowed && value > 0) { allowed = remained.compareAndSet(value, value - 1); value = remained.get(); } return allowed; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000201 15051152474 032713 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/RequestPathMetricsCollector.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/RequestPathMe0100644 0000000 0000000 00000044151 15051152474 034234 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.apache.zookeeper.ZooDefs.OpCode.checkWatches; import static org.apache.zookeeper.ZooDefs.OpCode.create; import static org.apache.zookeeper.ZooDefs.OpCode.create2; import static org.apache.zookeeper.ZooDefs.OpCode.createContainer; import static org.apache.zookeeper.ZooDefs.OpCode.delete; import static org.apache.zookeeper.ZooDefs.OpCode.deleteContainer; import static org.apache.zookeeper.ZooDefs.OpCode.exists; import static org.apache.zookeeper.ZooDefs.OpCode.getACL; import static org.apache.zookeeper.ZooDefs.OpCode.getChildren; import static org.apache.zookeeper.ZooDefs.OpCode.getChildren2; import static org.apache.zookeeper.ZooDefs.OpCode.getData; import static org.apache.zookeeper.ZooDefs.OpCode.removeWatches; import static org.apache.zookeeper.ZooDefs.OpCode.setACL; import static org.apache.zookeeper.ZooDefs.OpCode.setData; import static org.apache.zookeeper.ZooDefs.OpCode.setWatches2; import static org.apache.zookeeper.ZooDefs.OpCode.sync; import java.io.PrintWriter; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Predicate; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.Request; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class holds the requests path ( up till a certain depth) stats per request type */ public class RequestPathMetricsCollector { private static final Logger LOG = LoggerFactory.getLogger(RequestPathMetricsCollector.class); // How many seconds does each slot represent, default is 15 seconds. private final int REQUEST_STATS_SLOT_DURATION; // How many slots we keep, default is 60 so it's 15 minutes total history. private final int REQUEST_STATS_SLOT_CAPACITY; // How far down the path we keep, default is 6. private final int REQUEST_PREPROCESS_PATH_DEPTH; // Sample rate, default is 0.1 (10%). private final float REQUEST_PREPROCESS_SAMPLE_RATE; private final long COLLECTOR_INITIAL_DELAY; private final long COLLECTOR_DELAY; private final int REQUEST_PREPROCESS_TOPPATH_MAX; private final boolean enabled; public static final String PATH_STATS_SLOT_CAPACITY = "zookeeper.pathStats.slotCapacity"; public static final String PATH_STATS_SLOT_DURATION = "zookeeper.pathStats.slotDuration"; public static final String PATH_STATS_MAX_DEPTH = "zookeeper.pathStats.maxDepth"; public static final String PATH_STATS_SAMPLE_RATE = "zookeeper.pathStats.sampleRate"; public static final String PATH_STATS_COLLECTOR_INITIAL_DELAY = "zookeeper.pathStats.initialDelay"; public static final String PATH_STATS_COLLECTOR_DELAY = "zookeeper.pathStats.delay"; public static final String PATH_STATS_TOP_PATH_MAX = "zookeeper.pathStats.topPathMax"; public static final String PATH_STATS_ENABLED = "zookeeper.pathStats.enabled"; private static final String PATH_SEPERATOR = "/"; private final Map immutableRequestsMap; private final ScheduledThreadPoolExecutor scheduledExecutor; private final boolean accurateMode; public RequestPathMetricsCollector() { this(false); } public RequestPathMetricsCollector(boolean accurateMode) { final Map requestsMap = new HashMap<>(); this.accurateMode = accurateMode; REQUEST_PREPROCESS_TOPPATH_MAX = Integer.getInteger(PATH_STATS_TOP_PATH_MAX, 20); REQUEST_STATS_SLOT_DURATION = Integer.getInteger(PATH_STATS_SLOT_DURATION, 15); REQUEST_STATS_SLOT_CAPACITY = Integer.getInteger(PATH_STATS_SLOT_CAPACITY, 60); REQUEST_PREPROCESS_PATH_DEPTH = Integer.getInteger(PATH_STATS_MAX_DEPTH, 6); REQUEST_PREPROCESS_SAMPLE_RATE = Float.parseFloat(System.getProperty(PATH_STATS_SAMPLE_RATE, "0.1")); COLLECTOR_INITIAL_DELAY = Long.getLong(PATH_STATS_COLLECTOR_INITIAL_DELAY, 5); COLLECTOR_DELAY = Long.getLong(PATH_STATS_COLLECTOR_DELAY, 5); enabled = Boolean.getBoolean(PATH_STATS_ENABLED); LOG.info("{} = {}", PATH_STATS_SLOT_CAPACITY, REQUEST_STATS_SLOT_CAPACITY); LOG.info("{} = {}", PATH_STATS_SLOT_DURATION, REQUEST_STATS_SLOT_DURATION); LOG.info("{} = {}", PATH_STATS_MAX_DEPTH, REQUEST_PREPROCESS_PATH_DEPTH); LOG.info("{} = {}", PATH_STATS_COLLECTOR_INITIAL_DELAY, COLLECTOR_INITIAL_DELAY); LOG.info("{} = {}", PATH_STATS_COLLECTOR_DELAY, COLLECTOR_DELAY); LOG.info("{} = {}", PATH_STATS_ENABLED, enabled); this.scheduledExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); scheduledExecutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); scheduledExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); requestsMap.put(Request.op2String(create), new PathStatsQueue(create)); requestsMap.put(Request.op2String(create2), new PathStatsQueue(create2)); requestsMap.put(Request.op2String(createContainer), new PathStatsQueue(createContainer)); requestsMap.put(Request.op2String(deleteContainer), new PathStatsQueue(deleteContainer)); requestsMap.put(Request.op2String(delete), new PathStatsQueue(delete)); requestsMap.put(Request.op2String(exists), new PathStatsQueue(exists)); requestsMap.put(Request.op2String(setData), new PathStatsQueue(setData)); requestsMap.put(Request.op2String(getData), new PathStatsQueue(getData)); requestsMap.put(Request.op2String(getACL), new PathStatsQueue(getACL)); requestsMap.put(Request.op2String(setACL), new PathStatsQueue(setACL)); requestsMap.put(Request.op2String(getChildren), new PathStatsQueue(getChildren)); requestsMap.put(Request.op2String(getChildren2), new PathStatsQueue(getChildren2)); requestsMap.put(Request.op2String(checkWatches), new PathStatsQueue(checkWatches)); requestsMap.put(Request.op2String(removeWatches), new PathStatsQueue(removeWatches)); requestsMap.put(Request.op2String(setWatches2), new PathStatsQueue(setWatches2)); requestsMap.put(Request.op2String(sync), new PathStatsQueue(sync)); this.immutableRequestsMap = java.util.Collections.unmodifiableMap(requestsMap); } static boolean isWriteOp(int requestType) { switch (requestType) { case ZooDefs.OpCode.sync: case ZooDefs.OpCode.create: case ZooDefs.OpCode.create2: case ZooDefs.OpCode.createContainer: case ZooDefs.OpCode.delete: case ZooDefs.OpCode.deleteContainer: case ZooDefs.OpCode.setData: case ZooDefs.OpCode.reconfig: case ZooDefs.OpCode.setACL: case ZooDefs.OpCode.multi: case ZooDefs.OpCode.check: return true; } return false; } static String trimPathDepth(String path, int maxDepth) { int count = 0; StringBuilder sb = new StringBuilder(); StringTokenizer pathTokenizer = new StringTokenizer(path, PATH_SEPERATOR); while (pathTokenizer.hasMoreElements() && count++ < maxDepth) { sb.append(PATH_SEPERATOR); sb.append(pathTokenizer.nextToken()); } path = sb.toString(); return path; } public void shutdown() { if (!enabled) { return; } LOG.info("shutdown scheduledExecutor"); scheduledExecutor.shutdownNow(); } public void start() { if (!enabled) { return; } LOG.info("Start the RequestPath collector"); immutableRequestsMap.forEach((opType, pathStatsQueue) -> pathStatsQueue.start()); // Schedule to log the top used read/write paths every 5 mins scheduledExecutor.scheduleWithFixedDelay(() -> { LOG.info("%nHere are the top Read paths:"); logTopPaths(aggregatePaths(4, queue -> !queue.isWriteOperation()), entry -> LOG.info("{} : {}", entry.getKey(), entry.getValue())); LOG.info("%nHere are the top Write paths:"); logTopPaths(aggregatePaths(4, queue -> queue.isWriteOperation()), entry -> LOG.info("{} : {}", entry.getKey(), entry.getValue())); }, COLLECTOR_INITIAL_DELAY, COLLECTOR_DELAY, TimeUnit.MINUTES); } /** * The public interface of the buffer. FinalRequestHandler will call into this for * each request that has a path and this needs to be fast. we sample the path so that * we don't have to store too many paths in memory */ public void registerRequest(int type, String path) { if (!enabled) { return; } if (ThreadLocalRandom.current().nextFloat() <= REQUEST_PREPROCESS_SAMPLE_RATE) { PathStatsQueue pathStatsQueue = immutableRequestsMap.get(Request.op2String(type)); if (pathStatsQueue != null) { pathStatsQueue.registerRequest(path); } else { LOG.error("We should not handle {}", type); } } } public void dumpTopRequestPath(PrintWriter pwriter, String requestTypeName, int queryMaxDepth) { if (queryMaxDepth < 1) { return; } PathStatsQueue pathStatsQueue = immutableRequestsMap.get(requestTypeName); if (pathStatsQueue == null) { pwriter.println("Can not find path stats for type: " + requestTypeName); return; } else { pwriter.println("The top requests of type: " + requestTypeName); } Map combinedMap; final int maxDepth = Math.min(queryMaxDepth, REQUEST_PREPROCESS_PATH_DEPTH); combinedMap = pathStatsQueue.collectStats(maxDepth); logTopPaths(combinedMap, entry -> pwriter.println(entry.getKey() + " : " + entry.getValue())); } public void dumpTopReadPaths(PrintWriter pwriter, int queryMaxDepth) { pwriter.println("The top read requests are"); dumpTopAggregatedPaths(pwriter, queryMaxDepth, queue -> !queue.isWriteOperation); } public void dumpTopWritePaths(PrintWriter pwriter, int queryMaxDepth) { pwriter.println("The top write requests are"); dumpTopAggregatedPaths(pwriter, queryMaxDepth, queue -> queue.isWriteOperation); } public void dumpTopPaths(PrintWriter pwriter, int queryMaxDepth) { pwriter.println("The top requests are"); dumpTopAggregatedPaths(pwriter, queryMaxDepth, queue -> true); } /** * Combine all the path Stats Queue that matches the predicate together * and then write to the pwriter */ private void dumpTopAggregatedPaths(PrintWriter pwriter, int queryMaxDepth, final Predicate predicate) { if (!enabled) { return; } final Map combinedMap = aggregatePaths(queryMaxDepth, predicate); logTopPaths(combinedMap, entry -> pwriter.println(entry.getKey() + " : " + entry.getValue())); } Map aggregatePaths(int queryMaxDepth, Predicate predicate) { final Map combinedMap = new HashMap<>(REQUEST_PREPROCESS_TOPPATH_MAX); final int maxDepth = Math.min(queryMaxDepth, REQUEST_PREPROCESS_PATH_DEPTH); immutableRequestsMap.values() .stream() .filter(predicate) .forEach(pathStatsQueue -> pathStatsQueue.collectStats(maxDepth).forEach( (path, count) -> combinedMap.put(path, combinedMap.getOrDefault(path, 0) + count))); return combinedMap; } void logTopPaths(Map combinedMap, final Consumer> output) { combinedMap.entrySet() .stream() // sort by path count .sorted(Comparator.comparing(Map.Entry::getValue).reversed()) .limit(REQUEST_PREPROCESS_TOPPATH_MAX).forEach(output); } class PathStatsQueue { private final String requestTypeName; private final AtomicReference> currentSlot; private final LinkedBlockingQueue> requestPathStats; private final boolean isWriteOperation; public PathStatsQueue(int requestType) { this.requestTypeName = Request.op2String(requestType); this.isWriteOperation = isWriteOp(requestType); requestPathStats = new LinkedBlockingQueue<>(REQUEST_STATS_SLOT_CAPACITY); currentSlot = new AtomicReference<>(new ConcurrentLinkedQueue<>()); } /* * The only write entry into this class, need to be fast. * Just queue up the path to the current slot queue locking free. */ public void registerRequest(String path) { if (!enabled) { return; } currentSlot.get().offer(path); } ConcurrentLinkedQueue getCurrentSlot() { return currentSlot.get(); } /** * Helper function to MR the paths in the queue to map with count * 1. cut each path up to max depth * 2. aggregate the paths based on its count * * @param tobeProcessedSlot queue of paths called * @return a map containing aggregated path in the queue */ Map mapReducePaths(int maxDepth, Collection tobeProcessedSlot) { Map newSlot = new ConcurrentHashMap<>(); tobeProcessedSlot.stream().filter(path -> path != null).forEach((path) -> { path = trimPathDepth(path, maxDepth); newSlot.put(path, newSlot.getOrDefault(path, 0) + 1); }); return newSlot; } /** * The only read point of this class * * @return the aggregated path to count map */ public Map collectStats(int maxDepth) { Map combinedMap; // Take a snapshot of the current slot and convert it to map. // Set the initial size as 0 since we don't want it to padding nulls in the end. Map snapShot = mapReducePaths( maxDepth, Arrays.asList(currentSlot.get().toArray(new String[0]))); // Starting from the snapshot and go through the queue to reduce them into one map // the iterator can run concurrently with write but we want to use a real lock in the test synchronized (accurateMode ? requestPathStats : new Object()) { combinedMap = requestPathStats.stream().reduce(snapShot, (firstMap, secondMap) -> { secondMap.forEach((key, value) -> { String trimmedPath = trimPathDepth(key, maxDepth); firstMap.put(trimmedPath, firstMap.getOrDefault(trimmedPath, 0) + value); }); return firstMap; }); } return combinedMap; } /** * Start to schedule the pre-processing of the current slot */ public void start() { if (!enabled) { return; } // Staggered start and then run every 15 seconds no matter what int delay = ThreadLocalRandom.current().nextInt(REQUEST_STATS_SLOT_DURATION); // We need to use fixed Delay as the fixed rate will start the next one right // after the previous one finishes if it runs overtime instead of overlapping it. scheduledExecutor.scheduleWithFixedDelay(() -> { // Generate new slot so new requests will go here. ConcurrentLinkedQueue tobeProcessedSlot = currentSlot.getAndSet(new ConcurrentLinkedQueue<>()); try { // pre process the last slot and queue it up, only one thread scheduled modified // this but we can mess up the collect part so we put a lock in the test. Map latestSlot = mapReducePaths(REQUEST_PREPROCESS_PATH_DEPTH, tobeProcessedSlot); synchronized (accurateMode ? requestPathStats : new Object()) { if (requestPathStats.remainingCapacity() <= 0) { requestPathStats.poll(); } if (!requestPathStats.offer(latestSlot)) { LOG.error("Failed to insert the new request path stats for {}", requestTypeName); } } } catch (Exception e) { LOG.error("Failed to insert the new request path stats for {} with exception {}", requestTypeName, e); } }, delay, REQUEST_STATS_SLOT_DURATION, TimeUnit.SECONDS); } boolean isWriteOperation() { return isWriteOperation; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000164 15051152474 032723 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/SerializeUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/SerializeUtil0100644 0000000 0000000 00000014571 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooTrace; import org.apache.zookeeper.txn.CloseSessionTxn; import org.apache.zookeeper.txn.CreateContainerTxn; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.CreateTTLTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.CreateTxnV0; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.ErrorTxn; import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.SetACLTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SerializeUtils { private static final Logger LOG = LoggerFactory.getLogger(SerializeUtils.class); public static TxnLogEntry deserializeTxn(byte[] txnBytes) throws IOException { TxnHeader hdr = new TxnHeader(); final ByteArrayInputStream bais = new ByteArrayInputStream(txnBytes); InputArchive ia = BinaryInputArchive.getArchive(bais); hdr.deserialize(ia, "hdr"); bais.mark(bais.available()); Record txn = null; switch (hdr.getType()) { case OpCode.createSession: // This isn't really an error txn; it just has the same // format. The error represents the timeout txn = new CreateSessionTxn(); break; case OpCode.closeSession: txn = ZooKeeperServer.isCloseSessionTxnEnabled() ? new CloseSessionTxn() : null; break; case OpCode.create: case OpCode.create2: txn = new CreateTxn(); break; case OpCode.createTTL: txn = new CreateTTLTxn(); break; case OpCode.createContainer: txn = new CreateContainerTxn(); break; case OpCode.delete: case OpCode.deleteContainer: txn = new DeleteTxn(); break; case OpCode.reconfig: case OpCode.setData: txn = new SetDataTxn(); break; case OpCode.setACL: txn = new SetACLTxn(); break; case OpCode.error: txn = new ErrorTxn(); break; case OpCode.multi: txn = new MultiTxn(); break; default: throw new IOException("Unsupported Txn with type=" + hdr.getType()); } if (txn != null) { try { txn.deserialize(ia, "txn"); } catch (EOFException e) { // perhaps this is a V0 Create if (hdr.getType() == OpCode.create) { CreateTxn create = (CreateTxn) txn; bais.reset(); CreateTxnV0 createv0 = new CreateTxnV0(); createv0.deserialize(ia, "txn"); // cool now make it V1. a -1 parentCVersion will // trigger fixup processing in processTxn create.setPath(createv0.getPath()); create.setData(createv0.getData()); create.setAcl(createv0.getAcl()); create.setEphemeral(createv0.getEphemeral()); create.setParentCVersion(-1); } else if (hdr.getType() == OpCode.closeSession) { // perhaps this is before CloseSessionTxn was added, // ignore it and reset txn to null txn = null; } else { throw e; } } } TxnDigest digest = null; if (ZooKeeperServer.isDigestEnabled()) { digest = new TxnDigest(); try { digest.deserialize(ia, "digest"); } catch (EOFException exception) { // may not have digest in the txn digest = null; } } return new TxnLogEntry(txn, hdr, digest); } public static void deserializeSnapshot(DataTree dt, InputArchive ia, Map sessions) throws IOException { int count = ia.readInt("count"); while (count > 0) { long id = ia.readLong("id"); int to = ia.readInt("timeout"); sessions.put(id, to); if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage( LOG, ZooTrace.SESSION_TRACE_MASK, "loadData --- session in archive: " + id + " with timeout: " + to); } count--; } dt.deserialize(ia, "tree"); } public static void serializeSnapshot(DataTree dt, OutputArchive oa, Map sessions) throws IOException { HashMap sessSnap = new HashMap<>(sessions); oa.writeInt(sessSnap.size(), "count"); for (Entry entry : sessSnap.entrySet()) { oa.writeLong(entry.getKey().longValue(), "id"); oa.writeInt(entry.getValue().intValue(), "timeout"); } dt.serialize(oa, "tree"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000172 15051152474 032722 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/VerifyingFileFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/VerifyingFile0100644 0000000 0000000 00000005444 15051152474 034251 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.io.File; import org.slf4j.Logger; public final class VerifyingFileFactory { private final boolean warnForRelativePath; private final boolean failForNonExistingPath; private final Logger log; public VerifyingFileFactory(Builder builder) { warnForRelativePath = builder.warnForRelativePathOption; failForNonExistingPath = builder.failForNonExistingPathOption; log = builder.log; assert (log != null); } public File create(String path) { File file = new File(path); return validate(file); } public File validate(File file) { if (warnForRelativePath) { doWarnForRelativePath(file); } if (failForNonExistingPath) { doFailForNonExistingPath(file); } return file; } private void doFailForNonExistingPath(File file) { if (!file.exists()) { throw new IllegalArgumentException(file.toString() + " file is missing"); } } private void doWarnForRelativePath(File file) { if (file.isAbsolute()) { return; } if (file.getPath().substring(0, 2).equals("." + File.separator)) { return; } log.warn(file.getPath() + " is relative. Prepend ." + File.separator + " to indicate that you're sure!"); } public static class Builder { private boolean warnForRelativePathOption = false; private boolean failForNonExistingPathOption = false; private final Logger log; public Builder(Logger log) { this.log = log; } public Builder warnForRelativePath() { warnForRelativePathOption = true; return this; } public Builder failForNonExistingPath() { failForNonExistingPathOption = true; return this; } public VerifyingFileFactory build() { return new VerifyingFileFactory(this); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000157 15051152474 032725 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/ZxidUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/util/ZxidUtils.jav0100644 0000000 0000000 00000002401 15051152474 034213 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; public class ZxidUtils { public static long getEpochFromZxid(long zxid) { return zxid >> 32L; } public static long getCounterFromZxid(long zxid) { return zxid & 0xffffffffL; } public static long makeZxid(long epoch, long counter) { return (epoch << 32L) | (counter & 0xffffffffL); } public static String zxidToString(long zxid) { return Long.toHexString(zxid); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000173 15051152474 032702 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/IDeadWatcherListener.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/IDeadWatcher0100644 0000000 0000000 00000002235 15051152474 034117 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.Set; /** * Interface used to process the dead watchers related to closed cnxns. */ public interface IDeadWatcherListener { /** * Process the given dead watchers. * * @param deadWatchers the watchers which have closed cnxn */ void processDeadWatchers(Set deadWatchers); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000164 15051152474 032702 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/IWatchManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/IWatchManage0100644 0000000 0000000 00000013225 15051152474 034124 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.io.PrintWriter; import java.util.List; import javax.annotation.Nullable; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.data.ACL; public interface IWatchManager { /** * Add watch to specific path. * * @param path znode path * @param watcher watcher object reference * * @return true if the watcher added is not already present */ boolean addWatch(String path, Watcher watcher); /** * Add watch to specific path. * * @param path znode path * @param watcher watcher object reference * @param watcherMode the watcher mode to use * * @return true if the watcher added is not already present */ default boolean addWatch(String path, Watcher watcher, WatcherMode watcherMode) { if (watcherMode == WatcherMode.DEFAULT_WATCHER_MODE) { return addWatch(path, watcher); } throw new UnsupportedOperationException(); // custom implementations must defeat this } /** * Checks the specified watcher exists for the given path. * * @param path znode path * @param watcher watcher object reference * * @return true if the watcher exists, false otherwise */ boolean containsWatcher(String path, Watcher watcher); /** * Checks the specified watcher exists for the given path and mode. * * @param path znode path * @param watcher watcher object reference * @param watcherMode watcher mode, null for any mode * @return true if the watcher exists, false otherwise */ default boolean containsWatcher(String path, Watcher watcher, @Nullable WatcherMode watcherMode) { if (watcherMode == null || watcherMode == WatcherMode.DEFAULT_WATCHER_MODE) { return containsWatcher(path, watcher); } throw new UnsupportedOperationException("persistent watch"); } /** * Removes the specified watcher for the given path. * * @param path znode path * @param watcher watcher object reference * * @return true if the watcher successfully removed, false otherwise */ boolean removeWatcher(String path, Watcher watcher); /** * Removes the specified watcher for the given path and mode. * * @param path znode path * @param watcher watcher object reference * @param watcherMode watcher mode, null to remove all modes * @return true if the watcher successfully removed, false otherwise */ default boolean removeWatcher(String path, Watcher watcher, WatcherMode watcherMode) { if (watcherMode == null || watcherMode == WatcherMode.DEFAULT_WATCHER_MODE) { return removeWatcher(path, watcher); } throw new UnsupportedOperationException("persistent watch"); } /** * The entry to remove the watcher when the cnxn is closed. * * @param watcher watcher object reference */ void removeWatcher(Watcher watcher); /** * Distribute the watch event for the given path. * * @param path znode path * @param type the watch event type * @param zxid the zxid for the corresponding change that triggered this event * @param acl ACL of the znode in path * * @return the watchers have been notified */ WatcherOrBitSet triggerWatch(String path, EventType type, long zxid, List acl); /** * Distribute the watch event for the given path, but ignore those * suppressed ones. * * @param path znode path * @param type the watch event type * @param zxid the zxid for the corresponding change that triggered this event * @param suppress the suppressed watcher set * * @return the watchers have been notified */ WatcherOrBitSet triggerWatch(String path, EventType type, long zxid, List acl, WatcherOrBitSet suppress); /** * Get the size of watchers. * * @return the watchers number managed in this class. */ int size(); /** * Clean up the watch manager. */ void shutdown(); /** * Returns a watch summary. * * @return watch summary * @see WatchesSummary */ WatchesSummary getWatchesSummary(); /** * Returns a watch report. * * @return watch report * @see WatchesReport */ WatchesReport getWatches(); /** * Returns a watch report by path. * * @return watch report * @see WatchesPathReport */ WatchesPathReport getWatchesByPath(); /** * String representation of watches. Warning, may be large! * * @param pwriter the writer to dump the watches * @param byPath iff true output watches by paths, otw output * watches by connection * */ void dumpWatches(PrintWriter pwriter, boolean byPath); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000171 15051152474 032700 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/PathParentIterator.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/PathParentIt0100644 0000000 0000000 00000006166 15051152474 034205 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.Iterator; import java.util.NoSuchElementException; /** * Iterates over a ZooKeeper path. Each iteration goes up one parent path. Thus, the * effect of the iterator is to iterate over the initial path and then all of its parents. */ public class PathParentIterator implements Iterator { private String path; private final int maxLevel; private int level = -1; /** * Return a new PathParentIterator that iterates from the * given path to all parents. * * @param path initial path */ public static PathParentIterator forAll(String path) { return new PathParentIterator(path, Integer.MAX_VALUE); } /** * Return a new PathParentIterator that only returns the given path - i.e. * does not iterate to parent paths. * * @param path initial path */ public static PathParentIterator forPathOnly(String path) { return new PathParentIterator(path, 0); } private PathParentIterator(String path, int maxLevel) { // NOTE: asserts that the path has already been validated this.path = path; this.maxLevel = maxLevel; } /** * Return an Iterable view so that this Iterator can be used in for each * statements. IMPORTANT: the returned Iterable is single use only * @return Iterable */ public Iterable asIterable() { return () -> PathParentIterator.this; } @Override public boolean hasNext() { return !path.isEmpty() && (level < maxLevel); } /** * Returns true if this iterator is currently at a parent path as opposed * to the initial path given to the constructor * * @return true/false */ public boolean atParentPath() { return level > 0; } @Override public String next() { if (!hasNext()) { throw new NoSuchElementException(); } String localPath = path; ++level; if (path.equals("/")) { path = ""; } else { path = path.substring(0, path.lastIndexOf('/')); if (path.length() == 0) { path = "/"; } } return localPath; } @Override public void remove() { throw new UnsupportedOperationException(); } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000163 15051152474 032701 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchManager.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchManager0100644 0000000 0000000 00000032212 15051152474 034172 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.io.PrintWriter; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ServerWatcher; import org.apache.zookeeper.server.ZooTrace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class manages watches. It allows watches to be associated with a string * and removes watchers and their watches in addition to managing triggers. */ public class WatchManager implements IWatchManager { private static final Logger LOG = LoggerFactory.getLogger(WatchManager.class); private final Map> watchTable = new HashMap<>(); private final Map> watch2Paths = new HashMap<>(); private int recursiveWatchQty = 0; @Override public synchronized int size() { int result = 0; for (Set watches : watchTable.values()) { result += watches.size(); } return result; } private boolean isDeadWatcher(Watcher watcher) { return watcher instanceof ServerCnxn && ((ServerCnxn) watcher).isStale(); } @Override public boolean addWatch(String path, Watcher watcher) { return addWatch(path, watcher, WatcherMode.DEFAULT_WATCHER_MODE); } @Override public synchronized boolean addWatch(String path, Watcher watcher, WatcherMode watcherMode) { if (isDeadWatcher(watcher)) { LOG.debug("Ignoring addWatch with closed cnxn"); return false; } Set list = watchTable.get(path); if (list == null) { // don't waste memory if there are few watches on a node // rehash when the 4th entry is added, doubling size thereafter // seems like a good compromise list = new HashSet<>(4); watchTable.put(path, list); } list.add(watcher); Map paths = watch2Paths.get(watcher); if (paths == null) { // cnxns typically have many watches, so use default cap here paths = new HashMap<>(); watch2Paths.put(watcher, paths); } WatchStats stats = paths.getOrDefault(path, WatchStats.NONE); WatchStats newStats = stats.addMode(watcherMode); if (newStats != stats) { paths.put(path, newStats); if (watcherMode.isRecursive()) { ++recursiveWatchQty; } return true; } return false; } @Override public synchronized void removeWatcher(Watcher watcher) { Map paths = watch2Paths.remove(watcher); if (paths == null) { return; } for (String p : paths.keySet()) { Set list = watchTable.get(p); if (list != null) { list.remove(watcher); if (list.isEmpty()) { watchTable.remove(p); } } } for (WatchStats stats : paths.values()) { if (stats.hasMode(WatcherMode.PERSISTENT_RECURSIVE)) { --recursiveWatchQty; } } } @Override public WatcherOrBitSet triggerWatch(String path, EventType type, long zxid, List acl) { return triggerWatch(path, type, zxid, acl, null); } @Override public WatcherOrBitSet triggerWatch(String path, EventType type, long zxid, List acl, WatcherOrBitSet supress) { WatchedEvent e = new WatchedEvent(type, KeeperState.SyncConnected, path, zxid); Set watchers = new HashSet<>(); synchronized (this) { PathParentIterator pathParentIterator = getPathParentIterator(path); for (String localPath : pathParentIterator.asIterable()) { Set thisWatchers = watchTable.get(localPath); if (thisWatchers == null || thisWatchers.isEmpty()) { continue; } Iterator iterator = thisWatchers.iterator(); while (iterator.hasNext()) { Watcher watcher = iterator.next(); Map paths = watch2Paths.getOrDefault(watcher, Collections.emptyMap()); WatchStats stats = paths.get(localPath); if (stats == null) { LOG.warn("inconsistent watch table for watcher {}, {} not in path list", watcher, localPath); continue; } if (!pathParentIterator.atParentPath()) { watchers.add(watcher); WatchStats newStats = stats.removeMode(WatcherMode.STANDARD); if (newStats == WatchStats.NONE) { iterator.remove(); paths.remove(localPath); } else if (newStats != stats) { paths.put(localPath, newStats); } } else if (stats.hasMode(WatcherMode.PERSISTENT_RECURSIVE)) { watchers.add(watcher); } } if (thisWatchers.isEmpty()) { watchTable.remove(localPath); } } } if (watchers.isEmpty()) { if (LOG.isTraceEnabled()) { ZooTrace.logTraceMessage(LOG, ZooTrace.EVENT_DELIVERY_TRACE_MASK, "No watchers for " + path); } return null; } for (Watcher w : watchers) { if (supress != null && supress.contains(w)) { continue; } if (w instanceof ServerWatcher) { ((ServerWatcher) w).process(e, acl); } else { w.process(e); } } switch (type) { case NodeCreated: ServerMetrics.getMetrics().NODE_CREATED_WATCHER.add(watchers.size()); break; case NodeDeleted: ServerMetrics.getMetrics().NODE_DELETED_WATCHER.add(watchers.size()); break; case NodeDataChanged: ServerMetrics.getMetrics().NODE_CHANGED_WATCHER.add(watchers.size()); break; case NodeChildrenChanged: ServerMetrics.getMetrics().NODE_CHILDREN_WATCHER.add(watchers.size()); break; default: // Other types not logged. break; } return new WatcherOrBitSet(watchers); } @Override public synchronized String toString() { StringBuilder sb = new StringBuilder(); sb.append(watch2Paths.size()).append(" connections watching ").append(watchTable.size()).append(" paths\n"); int total = 0; for (Map paths : watch2Paths.values()) { total += paths.size(); } sb.append("Total watches:").append(total); return sb.toString(); } @Override public synchronized void dumpWatches(PrintWriter pwriter, boolean byPath) { if (byPath) { for (Entry> e : watchTable.entrySet()) { pwriter.println(e.getKey()); for (Watcher w : e.getValue()) { pwriter.print("\t0x"); pwriter.print(Long.toHexString(((ServerCnxn) w).getSessionId())); pwriter.print("\n"); } } } else { for (Entry> e : watch2Paths.entrySet()) { pwriter.print("0x"); pwriter.println(Long.toHexString(((ServerCnxn) e.getKey()).getSessionId())); for (String path : e.getValue().keySet()) { pwriter.print("\t"); pwriter.println(path); } } } } @Override public synchronized boolean containsWatcher(String path, Watcher watcher) { return containsWatcher(path, watcher, null); } @Override public synchronized boolean containsWatcher(String path, Watcher watcher, WatcherMode watcherMode) { Map paths = watch2Paths.get(watcher); if (paths == null) { return false; } WatchStats stats = paths.get(path); return stats != null && (watcherMode == null || stats.hasMode(watcherMode)); } private WatchStats unwatch(String path, Watcher watcher, Map paths, Set watchers) { WatchStats stats = paths.remove(path); if (stats == null) { return WatchStats.NONE; } if (paths.isEmpty()) { watch2Paths.remove(watcher); } watchers.remove(watcher); if (watchers.isEmpty()) { watchTable.remove(path); } return stats; } @Override public synchronized boolean removeWatcher(String path, Watcher watcher, WatcherMode watcherMode) { Map paths = watch2Paths.get(watcher); Set watchers = watchTable.get(path); if (paths == null || watchers == null) { return false; } WatchStats oldStats; WatchStats newStats; if (watcherMode != null) { oldStats = paths.getOrDefault(path, WatchStats.NONE); newStats = oldStats.removeMode(watcherMode); if (newStats != WatchStats.NONE) { if (newStats != oldStats) { paths.put(path, newStats); } } else if (oldStats != WatchStats.NONE) { unwatch(path, watcher, paths, watchers); } } else { oldStats = unwatch(path, watcher, paths, watchers); newStats = WatchStats.NONE; } if (oldStats.hasMode(WatcherMode.PERSISTENT_RECURSIVE) && !newStats.hasMode(WatcherMode.PERSISTENT_RECURSIVE)) { --recursiveWatchQty; } return oldStats != newStats; } @Override public synchronized boolean removeWatcher(String path, Watcher watcher) { return removeWatcher(path, watcher, null); } // VisibleForTesting Map> getWatch2Paths() { return watch2Paths; } @Override public synchronized WatchesReport getWatches() { Map> id2paths = new HashMap<>(); for (Entry> e : watch2Paths.entrySet()) { Long id = ((ServerCnxn) e.getKey()).getSessionId(); Set paths = new HashSet<>(e.getValue().keySet()); id2paths.put(id, paths); } return new WatchesReport(id2paths); } @Override public synchronized WatchesPathReport getWatchesByPath() { Map> path2ids = new HashMap<>(); for (Entry> e : watchTable.entrySet()) { Set ids = new HashSet<>(e.getValue().size()); path2ids.put(e.getKey(), ids); for (Watcher watcher : e.getValue()) { ids.add(((ServerCnxn) watcher).getSessionId()); } } return new WatchesPathReport(path2ids); } @Override public synchronized WatchesSummary getWatchesSummary() { int totalWatches = 0; for (Map paths : watch2Paths.values()) { totalWatches += paths.size(); } return new WatchesSummary(watch2Paths.size(), watchTable.size(), totalWatches); } @Override public void shutdown() { /* do nothing */ } // VisibleForTesting synchronized int getRecursiveWatchQty() { return recursiveWatchQty; } private PathParentIterator getPathParentIterator(String path) { if (getRecursiveWatchQty() == 0) { return PathParentIterator.forPathOnly(path); } return PathParentIterator.forAll(path); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000172 15051152474 032701 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchManagerFactory.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchManager0100644 0000000 0000000 00000003630 15051152474 034174 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A factory used to produce the actual watch manager based on the * zookeeper.watchManagerName option. */ public class WatchManagerFactory { private static final Logger LOG = LoggerFactory.getLogger(WatchManagerFactory.class); public static final String ZOOKEEPER_WATCH_MANAGER_NAME = "zookeeper.watchManagerName"; public static IWatchManager createWatchManager() throws IOException { String watchManagerName = System.getProperty(ZOOKEEPER_WATCH_MANAGER_NAME); if (watchManagerName == null) { watchManagerName = WatchManager.class.getName(); } try { IWatchManager watchManager = (IWatchManager) Class.forName(watchManagerName).getConstructor().newInstance(); LOG.info("Using {} as watch manager", watchManagerName); return watchManager; } catch (Exception e) { IOException ioe = new IOException("Couldn't instantiate " + watchManagerName, e); throw ioe; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000174 15051152474 032703 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchManagerOptimized.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchManager0100644 0000000 0000000 00000035673 15051152474 034210 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.io.PrintWriter; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ServerWatcher; import org.apache.zookeeper.server.util.BitHashSet; import org.apache.zookeeper.server.util.BitMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Optimized in memory and time complexity, compared to WatchManager, both the * memory consumption and time complexity improved a lot, but it cannot * efficiently remove the watcher when the session or socket is closed, for * majority use case this is not a problem. * * Changed made compared to WatchManager: * * - Use HashSet and BitSet to store the watchers to find a balance between * memory usage and time complexity * - Use ReadWriteLock instead of synchronized to reduce lock retention * - Lazily clean up the closed watchers */ public class WatchManagerOptimized implements IWatchManager, IDeadWatcherListener { private static final Logger LOG = LoggerFactory.getLogger(WatchManagerOptimized.class); private final ConcurrentHashMap pathWatches = new ConcurrentHashMap<>(); // watcher to bit id mapping private final BitMap watcherBitIdMap = new BitMap<>(); // used to lazily remove the dead watchers private final WatcherCleaner watcherCleaner; private final ReentrantReadWriteLock addRemovePathRWLock = new ReentrantReadWriteLock(); public WatchManagerOptimized() { watcherCleaner = new WatcherCleaner(this); watcherCleaner.start(); } @Override public boolean addWatch(String path, Watcher watcher) { boolean result = false; // Need readLock to exclusively lock with removeWatcher, otherwise we // may add a dead watch whose connection was just closed. // // Creating new watcher bit and adding it to the BitHashSet has it's // own lock to minimize the write lock scope addRemovePathRWLock.readLock().lock(); try { // avoid race condition of adding a on flying dead watcher if (isDeadWatcher(watcher)) { LOG.debug("Ignoring addWatch with closed cnxn"); } else { Integer bit = watcherBitIdMap.add(watcher); BitHashSet watchers = pathWatches.get(path); if (watchers == null) { watchers = new BitHashSet(); BitHashSet existingWatchers = pathWatches.putIfAbsent(path, watchers); // it's possible multiple thread might add to pathWatches // while we're holding read lock, so we need this check // here if (existingWatchers != null) { watchers = existingWatchers; } } result = watchers.add(bit); } } finally { addRemovePathRWLock.readLock().unlock(); } return result; } /** * Used in the OpCode.checkWatches, which is a read operation, since read * and write requests are exclusively processed, we don't need to hold * lock here. * * Different from addWatch this method doesn't mutate any state, so we don't * need to hold read lock to avoid dead watcher (cnxn closed) being added * to the watcher manager. * * It's possible that before we lazily clean up the dead watcher, this will * return true, but since the cnxn is closed, the response will dropped as * well, so it doesn't matter. */ @Override public boolean containsWatcher(String path, Watcher watcher) { BitHashSet watchers = pathWatches.get(path); return watchers != null && watchers.contains(watcherBitIdMap.getBit(watcher)); } @Override public boolean removeWatcher(String path, Watcher watcher) { // Hold write lock directly because removeWatcher request is more // likely to be invoked when the watcher is actually exist and // haven't fired yet, so instead of having read lock to check existence // before switching to write one, it's actually cheaper to hold write // lock directly here. addRemovePathRWLock.writeLock().lock(); try { BitHashSet list = pathWatches.get(path); if (list == null || !list.remove(watcherBitIdMap.getBit(watcher))) { return false; } if (list.isEmpty()) { pathWatches.remove(path); } return true; } finally { addRemovePathRWLock.writeLock().unlock(); } } @Override public void removeWatcher(Watcher watcher) { Integer watcherBit; // Use exclusive lock with addWatcher to guarantee that we won't add // watch for a cnxn which is already closed. addRemovePathRWLock.writeLock().lock(); try { // do nothing if the watcher is not tracked watcherBit = watcherBitIdMap.getBit(watcher); if (watcherBit == null) { return; } } finally { addRemovePathRWLock.writeLock().unlock(); } // We can guarantee that when this line is executed, the cnxn of this // watcher has already been marked as stale (this method is only called // from ServerCnxn.close after we set stale), which means no watches // will be added to the watcher manager with this watcher, so that we // can safely clean up this dead watcher. // // So it's not necessary to have this line in the addRemovePathRWLock. // And moving the addDeadWatcher out of the locking block to avoid // holding the write lock while we're blocked on adding dead watchers // into the watcherCleaner. watcherCleaner.addDeadWatcher(watcherBit); } /** * Entry for WatcherCleaner to remove dead watchers * * @param deadWatchers the watchers need to be removed */ @Override public void processDeadWatchers(Set deadWatchers) { // All the watchers being processed here are guaranteed to be dead, // no watches will be added for those dead watchers, that's why I // don't need to have addRemovePathRWLock here. BitSet bits = new BitSet(); for (int dw : deadWatchers) { bits.set(dw); } // The value iterator will reflect the state when it was // created, don't need to synchronize. for (BitHashSet watchers : pathWatches.values()) { watchers.remove(deadWatchers, bits); } // Better to remove the empty path from pathWatches, but it will add // lot of lock contention and affect the throughput of addWatch, // let's rely on the triggerWatch to delete it. for (Integer wbit : deadWatchers) { watcherBitIdMap.remove(wbit); } } @Override public WatcherOrBitSet triggerWatch(String path, EventType type, long zxid, List acl) { return triggerWatch(path, type, zxid, acl, null); } @Override public WatcherOrBitSet triggerWatch(String path, EventType type, long zxid, List acl, WatcherOrBitSet suppress) { WatchedEvent e = new WatchedEvent(type, KeeperState.SyncConnected, path, zxid); BitHashSet watchers = remove(path); if (watchers == null) { return null; } int triggeredWatches = 0; // Avoid race condition between dead watcher cleaner in // WatcherCleaner and iterating here synchronized (watchers) { for (Integer wBit : watchers) { if (suppress != null && suppress.contains(wBit)) { continue; } Watcher w = watcherBitIdMap.get(wBit); // skip dead watcher if (w == null || isDeadWatcher(w)) { continue; } if (w instanceof ServerWatcher) { ((ServerWatcher) w).process(e, acl); } else { w.process(e); } triggeredWatches++; } } updateMetrics(type, triggeredWatches); return new WatcherOrBitSet(watchers); } @Override public int size() { int size = 0; for (BitHashSet watches : pathWatches.values()) { size += watches.size(); } return size; } @Override public void shutdown() { if (watcherCleaner != null) { watcherCleaner.shutdown(); } } private BitHashSet remove(String path) { addRemovePathRWLock.writeLock().lock(); try { return pathWatches.remove(path); } finally { addRemovePathRWLock.writeLock().unlock(); } } void updateMetrics(final EventType type, int size) { switch (type) { case NodeCreated: ServerMetrics.getMetrics().NODE_CREATED_WATCHER.add(size); break; case NodeDeleted: ServerMetrics.getMetrics().NODE_DELETED_WATCHER.add(size); break; case NodeDataChanged: ServerMetrics.getMetrics().NODE_CHANGED_WATCHER.add(size); break; case NodeChildrenChanged: ServerMetrics.getMetrics().NODE_CHILDREN_WATCHER.add(size); break; default: // Other types not logged. break; } } boolean isDeadWatcher(Watcher watcher) { return watcher instanceof ServerCnxn && ((ServerCnxn) watcher).isStale(); } int pathSize() { return pathWatches.size(); } @Override public WatchesSummary getWatchesSummary() { return new WatchesSummary(watcherBitIdMap.size(), pathSize(), size()); } @Override public WatchesReport getWatches() { Map> id2paths = new HashMap<>(); for (Entry> e : getWatcher2PathesMap().entrySet()) { Long id = ((ServerCnxn) e.getKey()).getSessionId(); Set paths = new HashSet<>(e.getValue()); id2paths.put(id, paths); } return new WatchesReport(id2paths); } /** * Iterate through ConcurrentHashMap is 'safe', it will reflect the state * of the map at the time iteration began, may miss update while iterating, * given this is used in the commands to get a general idea of the watches * state, we don't care about missing some update. */ @Override public WatchesPathReport getWatchesByPath() { Map> path2ids = new HashMap<>(); for (Entry e : pathWatches.entrySet()) { BitHashSet watchers = e.getValue(); synchronized (watchers) { Set ids = new HashSet<>(watchers.size()); path2ids.put(e.getKey(), ids); for (Integer wbit : watchers) { Watcher watcher = watcherBitIdMap.get(wbit); if (watcher instanceof ServerCnxn) { ids.add(((ServerCnxn) watcher).getSessionId()); } } } } return new WatchesPathReport(path2ids); } /** * May cause OOM if there are lots of watches, might better to forbid * it in this class. */ public Map> getWatcher2PathesMap() { Map> watcher2paths = new HashMap<>(); for (Entry e : pathWatches.entrySet()) { String path = e.getKey(); BitHashSet watchers = e.getValue(); // avoid race condition with add/remove synchronized (watchers) { for (Integer wbit : watchers) { Watcher w = watcherBitIdMap.get(wbit); if (w == null) { continue; } if (!watcher2paths.containsKey(w)) { watcher2paths.put(w, new HashSet<>()); } watcher2paths.get(w).add(path); } } } return watcher2paths; } @Override public void dumpWatches(PrintWriter pwriter, boolean byPath) { if (byPath) { for (Entry e : pathWatches.entrySet()) { pwriter.println(e.getKey()); BitHashSet watchers = e.getValue(); synchronized (watchers) { for (Integer wbit : watchers) { Watcher w = watcherBitIdMap.get(wbit); if (!(w instanceof ServerCnxn)) { continue; } pwriter.print("\t0x"); pwriter.print(Long.toHexString(((ServerCnxn) w).getSessionId())); pwriter.print("\n"); } } } } else { for (Entry> e : getWatcher2PathesMap().entrySet()) { pwriter.print("0x"); pwriter.println(Long.toHexString(((ServerCnxn) e.getKey()).getSessionId())); for (String path : e.getValue()) { pwriter.print("\t"); pwriter.println(path); } } } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(watcherBitIdMap.size()).append(" connections watching ").append(pathSize()).append(" paths\n"); sb.append("Total watches:").append(size()); return sb.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000161 15051152474 032677 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchStats.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchStats.j0100644 0000000 0000000 00000005662 15051152474 034157 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; /** * Statistics for multiple different watches on one node. */ public final class WatchStats { private static final WatchStats[] WATCH_STATS = new WatchStats[] { new WatchStats(0), // NONE new WatchStats(1), // STANDARD new WatchStats(2), // PERSISTENT new WatchStats(3), // STANDARD + PERSISTENT new WatchStats(4), // PERSISTENT_RECURSIVE new WatchStats(5), // STANDARD + PERSISTENT_RECURSIVE new WatchStats(6), // PERSISTENT + PERSISTENT_RECURSIVE new WatchStats(7), // STANDARD + PERSISTENT + PERSISTENT_RECURSIVE }; /** * Stats that have no watchers attached. * *

    This could be used as start point to compute new stats using {@link #addMode(WatcherMode)}. */ public static final WatchStats NONE = WATCH_STATS[0]; private final int flags; private WatchStats(int flags) { this.flags = flags; } private static int modeToFlag(WatcherMode mode) { return 1 << mode.ordinal(); } /** * Compute stats after given mode attached to node. * * @param mode watcher mode * @return a new stats if given mode is not attached to this node before, otherwise old stats */ public WatchStats addMode(WatcherMode mode) { int flags = this.flags | modeToFlag(mode); return WATCH_STATS[flags]; } /** * Compute stats after given mode removed from node. * * @param mode watcher mode * @return null if given mode is the last attached mode, otherwise a new stats */ public WatchStats removeMode(WatcherMode mode) { int mask = ~modeToFlag(mode); int flags = this.flags & mask; if (flags == 0) { return NONE; } return WATCH_STATS[flags]; } /** * Check whether given mode is attached to this node. * * @param mode watcher mode * @return true if given mode is attached to this node. */ public boolean hasMode(WatcherMode mode) { int flags = modeToFlag(mode); return (this.flags & flags) != 0; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000165 15051152474 032703 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatcherCleaner.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatcherClean0100644 0000000 0000000 00000020267 15051152474 034200 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.RateLogger; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.WorkerService; import org.apache.zookeeper.server.WorkerService.WorkRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Thread used to lazily clean up the closed watcher, it will trigger the * clean up when the dead watchers get certain number or some number of * seconds has elapsed since last clean up. * * Cost of running it: * * - need to go through all the paths even if the watcher may only * watching a single path * - block in the path BitHashSet when we try to check the dead watcher * which won't block other stuff */ public class WatcherCleaner extends Thread { private static final Logger LOG = LoggerFactory.getLogger(WatcherCleaner.class); private final RateLogger RATE_LOGGER = new RateLogger(LOG); private volatile boolean stopped = false; private final Object cleanEvent = new Object(); private final Object processingCompletedEvent = new Object(); private final WorkerService cleaners; private final Set deadWatchers; private final IDeadWatcherListener listener; private final int watcherCleanThreshold; private final int watcherCleanIntervalInSeconds; private final int maxInProcessingDeadWatchers; private final AtomicInteger totalDeadWatchers = new AtomicInteger(); public WatcherCleaner(IDeadWatcherListener listener) { this( listener, Integer.getInteger("zookeeper.watcherCleanThreshold", 1000), Integer.getInteger("zookeeper.watcherCleanIntervalInSeconds", 600), Integer.getInteger("zookeeper.watcherCleanThreadsNum", 2), Integer.getInteger("zookeeper.maxInProcessingDeadWatchers", -1)); } public WatcherCleaner(IDeadWatcherListener listener, int watcherCleanThreshold, int watcherCleanIntervalInSeconds, int watcherCleanThreadsNum, int maxInProcessingDeadWatchers) { this.listener = listener; this.watcherCleanThreshold = watcherCleanThreshold; this.watcherCleanIntervalInSeconds = watcherCleanIntervalInSeconds; int suggestedMaxInProcessingThreshold = watcherCleanThreshold * watcherCleanThreadsNum; if (maxInProcessingDeadWatchers > 0 && maxInProcessingDeadWatchers < suggestedMaxInProcessingThreshold) { maxInProcessingDeadWatchers = suggestedMaxInProcessingThreshold; LOG.info( "The maxInProcessingDeadWatchers config is smaller than the suggested one, change it to use {}", maxInProcessingDeadWatchers); } this.maxInProcessingDeadWatchers = maxInProcessingDeadWatchers; this.deadWatchers = new HashSet<>(); this.cleaners = new WorkerService("DeadWatcherCleanner", watcherCleanThreadsNum, false); LOG.info( "watcherCleanThreshold={}, watcherCleanIntervalInSeconds={}" + ", watcherCleanThreadsNum={}, maxInProcessingDeadWatchers={}", watcherCleanThreshold, watcherCleanIntervalInSeconds, watcherCleanThreadsNum, maxInProcessingDeadWatchers); } public void addDeadWatcher(int watcherBit) { // Wait if there are too many watchers waiting to be closed, // this is will slow down the socket packet processing and // the adding watches in the ZK pipeline. while (maxInProcessingDeadWatchers > 0 && !stopped && totalDeadWatchers.get() >= maxInProcessingDeadWatchers) { try { RATE_LOGGER.rateLimitLog("Waiting for dead watchers cleaning"); long startTime = Time.currentElapsedTime(); synchronized (processingCompletedEvent) { processingCompletedEvent.wait(100); } long latency = Time.currentElapsedTime() - startTime; ServerMetrics.getMetrics().ADD_DEAD_WATCHER_STALL_TIME.add(latency); } catch (InterruptedException e) { LOG.info("Got interrupted while waiting for dead watches queue size"); break; } } synchronized (this) { if (deadWatchers.add(watcherBit)) { totalDeadWatchers.incrementAndGet(); ServerMetrics.getMetrics().DEAD_WATCHERS_QUEUED.add(1); if (deadWatchers.size() >= watcherCleanThreshold) { synchronized (cleanEvent) { cleanEvent.notifyAll(); } } } } } @Override public void run() { while (!stopped) { synchronized (cleanEvent) { try { // add some jitter to avoid cleaning dead watchers at the // same time in the quorum if (!stopped && deadWatchers.size() < watcherCleanThreshold) { int maxWaitMs = (watcherCleanIntervalInSeconds + ThreadLocalRandom.current().nextInt(watcherCleanIntervalInSeconds / 2 + 1)) * 1000; cleanEvent.wait(maxWaitMs); } } catch (InterruptedException e) { LOG.info("Received InterruptedException while waiting for cleanEvent"); break; } } if (deadWatchers.isEmpty()) { continue; } synchronized (this) { // Clean the dead watchers need to go through all the current // watches, which is pretty heavy and may take a second if // there are millions of watches, that's why we're doing lazily // batch clean up in a separate thread with a snapshot of the // current dead watchers. final Set snapshot = new HashSet<>(deadWatchers); deadWatchers.clear(); int total = snapshot.size(); LOG.info("Processing {} dead watchers", total); cleaners.schedule(new WorkRequest() { @Override public void doWork() throws Exception { long startTime = Time.currentElapsedTime(); listener.processDeadWatchers(snapshot); long latency = Time.currentElapsedTime() - startTime; LOG.info("Takes {} to process {} watches", latency, total); ServerMetrics.getMetrics().DEAD_WATCHERS_CLEANER_LATENCY.add(latency); ServerMetrics.getMetrics().DEAD_WATCHERS_CLEARED.add(total); totalDeadWatchers.addAndGet(-total); synchronized (processingCompletedEvent) { processingCompletedEvent.notifyAll(); } } }); } } LOG.info("WatcherCleaner thread exited"); } public void shutdown() { stopped = true; deadWatchers.clear(); cleaners.stop(); this.interrupt(); if (LOG.isInfoEnabled()) { LOG.info("WatcherCleaner thread shutdown is initiated"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000162 15051152474 032700 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatcherMode.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatcherMode.0100644 0000000 0000000 00000003447 15051152474 034121 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import org.apache.zookeeper.ZooDefs; public enum WatcherMode { STANDARD(false, false), PERSISTENT(true, false), PERSISTENT_RECURSIVE(true, true), ; public static final WatcherMode DEFAULT_WATCHER_MODE = WatcherMode.STANDARD; public static WatcherMode fromZooDef(int mode) { switch (mode) { case ZooDefs.AddWatchModes.persistent: return PERSISTENT; case ZooDefs.AddWatchModes.persistentRecursive: return PERSISTENT_RECURSIVE; } throw new IllegalArgumentException("Unsupported mode: " + mode); } private final boolean isPersistent; private final boolean isRecursive; WatcherMode(boolean isPersistent, boolean isRecursive) { this.isPersistent = isPersistent; this.isRecursive = isRecursive; } public boolean isPersistent() { return isPersistent; } public boolean isRecursive() { return isRecursive; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000166 15051152474 032704 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatcherOrBitSet.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatcherOrBit0100644 0000000 0000000 00000003437 15051152474 034175 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.Set; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.server.util.BitHashSet; public class WatcherOrBitSet { private Set watchers; private BitHashSet watcherBits; public WatcherOrBitSet(final Set watchers) { this.watchers = watchers; } public WatcherOrBitSet(final BitHashSet watcherBits) { this.watcherBits = watcherBits; } public boolean contains(Watcher watcher) { if (watchers == null) { return false; } return watchers.contains(watcher); } public boolean contains(int watcherBit) { if (watcherBits == null) { return false; } return watcherBits.contains(watcherBit); } public int size() { if (watchers != null) { return watchers.size(); } if (watcherBits != null) { return watcherBits.size(); } return 0; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000170 15051152474 032677 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchesPathReport.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchesPathR0100644 0000000 0000000 00000005267 15051152474 034200 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * A watch report, essentially a mapping of path to session IDs of sessions that * have set a watch on that path. This class is immutable. */ public class WatchesPathReport { private final Map> path2Ids; /** * Creates a new report. * * @param path2Ids map of paths to session IDs of sessions that have set a * watch on that path */ WatchesPathReport(Map> path2Ids) { this.path2Ids = Collections.unmodifiableMap(deepCopy(path2Ids)); } private static Map> deepCopy(Map> m) { Map> m2 = new HashMap<>(); for (Map.Entry> e : m.entrySet()) { m2.put(e.getKey(), new HashSet<>(e.getValue())); } return m2; } /** * Checks if the given path has watches set. * * @param path path * @return true if path has watch set */ public boolean hasSessions(String path) { return path2Ids.containsKey(path); } /** * Gets the session IDs of sessions that have set watches on the given path. * The returned set is immutable. * * @param path session ID * @return session IDs of sessions that have set watches on the path, or * null if none */ public Set getSessions(String path) { Set s = path2Ids.get(path); return s != null ? Collections.unmodifiableSet(s) : null; } /** * Converts this report to a map. The returned map is mutable, and changes * to it do not reflect back into this report. * * @return map representation of report */ public Map> toMap() { return deepCopy(path2Ids); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000164 15051152474 032702 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchesReport.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchesRepor0100644 0000000 0000000 00000005246 15051152474 034246 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * A watch report, essentially a mapping of session ID to paths that the session * has set a watch on. This class is immutable. */ public class WatchesReport { private final Map> id2paths; /** * Creates a new report. * * @param id2paths map of session IDs to paths that each session has set * a watch on */ WatchesReport(Map> id2paths) { this.id2paths = Collections.unmodifiableMap(deepCopy(id2paths)); } private static Map> deepCopy(Map> m) { Map> m2 = new HashMap<>(); for (Map.Entry> e : m.entrySet()) { m2.put(e.getKey(), new HashSet<>(e.getValue())); } return m2; } /** * Checks if the given session has watches set. * * @param sessionId session ID * @return true if session has paths with watches set */ public boolean hasPaths(long sessionId) { return id2paths.containsKey(sessionId); } /** * Gets the paths that the given session has set watches on. The returned * set is immutable. * * @param sessionId session ID * @return paths that have watches set by the session, or null if none */ public Set getPaths(long sessionId) { Set s = id2paths.get(sessionId); return s != null ? Collections.unmodifiableSet(s) : null; } /** * Converts this report to a map. The returned map is mutable, and changes * to it do not reflect back into this report. * * @return map representation of report */ public Map> toMap() { return deepCopy(id2paths); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000165 15051152474 032703 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchesSummary.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/server/watch/WatchesSumma0100644 0000000 0000000 00000006110 15051152474 034230 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import java.util.LinkedHashMap; import java.util.Map; /** * A summary of watch information. This class is immutable. */ public class WatchesSummary { /** * The key in the map returned by {@link #toMap()} for the number of * connections. */ public static final String KEY_NUM_CONNECTIONS = "num_connections"; /** * The key in the map returned by {@link #toMap()} for the number of paths. */ public static final String KEY_NUM_PATHS = "num_paths"; /** * The key in the map returned by {@link #toMap()} for the total number of * watches. */ public static final String KEY_NUM_TOTAL_WATCHES = "num_total_watches"; private final int numConnections; private final int numPaths; private final int totalWatches; /** * Creates a new summary. * * @param numConnections the number of sessions that have set watches * @param numPaths the number of paths that have watches set on them * @param totalWatches the total number of watches set */ WatchesSummary(int numConnections, int numPaths, int totalWatches) { this.numConnections = numConnections; this.numPaths = numPaths; this.totalWatches = totalWatches; } /** * Gets the number of connections (sessions) that have set watches. * * @return number of connections */ public int getNumConnections() { return numConnections; } /** * Gets the number of paths that have watches set on them. * * @return number of paths */ public int getNumPaths() { return numPaths; } /** * Gets the total number of watches set. * * @return total watches */ public int getTotalWatches() { return totalWatches; } /** * Converts this summary to a map. The returned map is mutable, and changes * to it do not reflect back into this summary. * * @return map representation of summary */ public Map toMap() { Map summary = new LinkedHashMap<>(); summary.put(KEY_NUM_CONNECTIONS, numConnections); summary.put(KEY_NUM_PATHS, numPaths); summary.put(KEY_NUM_TOTAL_WATCHES, totalWatches); return summary; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java_org_apache_zookeeper_util_Circ0100644 0000000 0000000 00000000164 15051152474 032622 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/util/CircularBlockingQueue.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/util/CircularBlockingQueu0100644 0000000 0000000 00000016163 15051152474 034256 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.util.ArrayDeque; import java.util.Collection; import java.util.Iterator; import java.util.Objects; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A bounded blocking queue backed by an array. This queue orders elements FIFO * (first-in-first-out). The head of the queue is that element that has been on * the queue the longest time. The tail of the queue is that element that has * been on the queue the shortest time. New elements are inserted at the tail of * the queue, and the queue retrieval operations obtain elements at the head of * the queue. If the queue is full, the head of the queue (the oldest element) * will be removed to make room for the newest element. */ public class CircularBlockingQueue implements BlockingQueue { private static final Logger LOG = LoggerFactory.getLogger(CircularBlockingQueue.class); /** Main lock guarding all access */ private final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** The array-backed queue */ private final ArrayDeque queue; private final int maxSize; private long droppedCount; public CircularBlockingQueue(int queueSize) { this.queue = new ArrayDeque<>(queueSize); this.maxSize = queueSize; this.lock = new ReentrantLock(); this.notEmpty = this.lock.newCondition(); this.droppedCount = 0L; } /** * This method differs from {@link BlockingQueue#offer(Object)} in that it * will remove the oldest queued element (the element at the front of the * queue) in order to make room for any new elements if the queue is full. * * @param e the element to add * @return true since it will make room for any new elements if required */ @Override public boolean offer(E e) { Objects.requireNonNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { if (this.queue.size() == this.maxSize) { final E discard = this.queue.remove(); this.droppedCount++; LOG.debug("Queue is full. Discarding oldest element [count={}]: {}", this.droppedCount, discard); } this.queue.add(e); this.notEmpty.signal(); } finally { lock.unlock(); } return true; } @Override public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (this.queue.isEmpty()) { if (nanos <= 0) { return null; } nanos = this.notEmpty.awaitNanos(nanos); } return this.queue.poll(); } finally { lock.unlock(); } } @Override public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (this.queue.isEmpty()) { this.notEmpty.await(); } return this.queue.poll(); } finally { lock.unlock(); } } @Override public boolean isEmpty() { final ReentrantLock lock = this.lock; lock.lock(); try { return this.queue.isEmpty(); } finally { lock.unlock(); } } @Override public int size() { final ReentrantLock lock = this.lock; lock.lock(); try { return this.queue.size(); } finally { lock.unlock(); } } /** * Returns the number of elements that were dropped from the queue because the * queue was full when a new element was offered. * * @return The number of elements dropped (lost) from the queue */ public long getDroppedCount() { return this.droppedCount; } /** * For testing purposes only. * * @return True if a thread is blocked waiting for a new element to be offered * to the queue */ boolean isConsumerThreadBlocked() { final ReentrantLock lock = this.lock; lock.lock(); try { return lock.getWaitQueueLength(this.notEmpty) > 0; } finally { lock.unlock(); } } @Override public int drainTo(Collection c) { throw new UnsupportedOperationException(); } @Override public E poll() { throw new UnsupportedOperationException(); } @Override public E element() { throw new UnsupportedOperationException(); } @Override public E peek() { throw new UnsupportedOperationException(); } @Override public E remove() { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public Iterator iterator() { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection arg0) { throw new UnsupportedOperationException(); } @Override public Object[] toArray() { throw new UnsupportedOperationException(); } @Override public T[] toArray(T[] arg0) { throw new UnsupportedOperationException(); } @Override public boolean add(E e) { throw new UnsupportedOperationException(); } @Override public boolean contains(Object o) { throw new UnsupportedOperationException(); } @Override public int drainTo(Collection c, int maxElements) { throw new UnsupportedOperationException(); } @Override public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } @Override public void put(E e) throws InterruptedException { throw new UnsupportedOperationException(); } @Override public int remainingCapacity() { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/util/PemReader.java0100644 0000000 0000000 00000022704 15051152474 032763 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 static java.nio.charset.StandardCharsets.US_ASCII; import static java.util.Base64.getMimeDecoder; import static java.util.regex.Pattern.CASE_INSENSITIVE; import static javax.crypto.Cipher.DECRYPT_MODE; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.security.auth.x500.X500Principal; /** * Note: this class is copied from io.airlift.security.pem.PemReader (see * https://github.com/airlift/airlift/blob/master/security/src/main/java/io/airlift/security/pem/PemReader.java) with * permission of the authors, to avoid adding an extra library dependency to Zookeeper. * The file was copied from commit hash 86348546af43217f4d04a0cdad624b0ae4751c2c. * * The following modifications have been made to the original source code: *

      *
    • imports have been rearranged to match Zookeeper import order style.
    • *
    • The dependency on com.google.common.io.Files.asCharSource has been removed.
    • *
    • A dependency on java.nio.file.Files has been added.
    • *
    */ public final class PemReader { private static final Pattern CERT_PATTERN = Pattern.compile( "-+BEGIN\\s+.*CERTIFICATE[^-]*-+(?:\\s|\\r|\\n)+" // Header + "([a-z0-9+/=\\r\\n]+)" // Base64 text + "-+END\\s+.*CERTIFICATE[^-]*-+", // Footer CASE_INSENSITIVE); private static final Pattern PRIVATE_KEY_PATTERN = Pattern.compile( "-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" // Header + "([a-z0-9+/=\\r\\n]+)" // Base64 text + "-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer CASE_INSENSITIVE); private static final Pattern PUBLIC_KEY_PATTERN = Pattern.compile( "-+BEGIN\\s+.*PUBLIC\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" // Header + "([a-z0-9+/=\\r\\n]+)" // Base64 text + "-+END\\s+.*PUBLIC\\s+KEY[^-]*-+", // Footer CASE_INSENSITIVE); private PemReader() { } public static KeyStore loadTrustStore(File certificateChainFile) throws IOException, GeneralSecurityException { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(null, null); List certificateChain = readCertificateChain(certificateChainFile); for (X509Certificate certificate : certificateChain) { X500Principal principal = certificate.getSubjectX500Principal(); keyStore.setCertificateEntry(principal.getName("RFC2253"), certificate); } return keyStore; } public static KeyStore loadKeyStore(File certificateChainFile, File privateKeyFile, Optional keyPassword) throws IOException, GeneralSecurityException { PrivateKey key = loadPrivateKey(privateKeyFile, keyPassword); List certificateChain = readCertificateChain(certificateChainFile); if (certificateChain.isEmpty()) { throw new CertificateException("Certificate file does not contain any certificates: " + certificateChainFile); } KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(null, null); keyStore.setKeyEntry("key", key, keyPassword.orElse("").toCharArray(), certificateChain.toArray(new Certificate[0])); return keyStore; } public static List readCertificateChain(File certificateChainFile) throws IOException, GeneralSecurityException { String contents = new String(Files.readAllBytes(certificateChainFile.toPath()), US_ASCII); return readCertificateChain(contents); } public static List readCertificateChain(String certificateChain) throws CertificateException { Matcher matcher = CERT_PATTERN.matcher(certificateChain); CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); List certificates = new ArrayList<>(); int start = 0; while (matcher.find(start)) { byte[] buffer = base64Decode(matcher.group(1)); certificates.add((X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(buffer))); start = matcher.end(); } return certificates; } public static PrivateKey loadPrivateKey(File privateKeyFile, Optional keyPassword) throws IOException, GeneralSecurityException { String privateKey = new String(Files.readAllBytes(privateKeyFile.toPath()), US_ASCII); return loadPrivateKey(privateKey, keyPassword); } public static PrivateKey loadPrivateKey(String privateKey, Optional keyPassword) throws IOException, GeneralSecurityException { Matcher matcher = PRIVATE_KEY_PATTERN.matcher(privateKey); if (!matcher.find()) { throw new KeyStoreException("did not find a private key"); } byte[] encodedKey = base64Decode(matcher.group(1)); PKCS8EncodedKeySpec encodedKeySpec; if (keyPassword.isPresent()) { EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); SecretKey secretKey = keyFactory.generateSecret(new PBEKeySpec(keyPassword.get().toCharArray())); Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); cipher.init(DECRYPT_MODE, secretKey, encryptedPrivateKeyInfo.getAlgParameters()); encodedKeySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); } else { encodedKeySpec = new PKCS8EncodedKeySpec(encodedKey); } // this code requires a key in PKCS8 format which is not the default openssl format // to convert to the PKCS8 format you use : openssl pkcs8 -topk8 ... try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(encodedKeySpec); } catch (InvalidKeySpecException ignore) { } try { KeyFactory keyFactory = KeyFactory.getInstance("EC"); return keyFactory.generatePrivate(encodedKeySpec); } catch (InvalidKeySpecException ignore) { } KeyFactory keyFactory = KeyFactory.getInstance("DSA"); return keyFactory.generatePrivate(encodedKeySpec); } public static PublicKey loadPublicKey(File publicKeyFile) throws IOException, GeneralSecurityException { String publicKey = new String(Files.readAllBytes(publicKeyFile.toPath()), US_ASCII); return loadPublicKey(publicKey); } public static PublicKey loadPublicKey(String publicKey) throws GeneralSecurityException { Matcher matcher = PUBLIC_KEY_PATTERN.matcher(publicKey); if (!matcher.find()) { throw new KeyStoreException("did not find a public key"); } String data = matcher.group(1); byte[] encodedKey = base64Decode(data); X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(encodedKey); try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(encodedKeySpec); } catch (InvalidKeySpecException ignore) { } try { KeyFactory keyFactory = KeyFactory.getInstance("EC"); return keyFactory.generatePublic(encodedKeySpec); } catch (InvalidKeySpecException ignore) { } KeyFactory keyFactory = KeyFactory.getInstance("DSA"); return keyFactory.generatePublic(encodedKeySpec); } private static byte[] base64Decode(String base64) { return getMimeDecoder().decode(base64.getBytes(US_ASCII)); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java0100644 0000000 0000000 00000035052 15051152474 033747 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.util; import java.security.Principal; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.Sasl; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.zookeeper.SaslClientCallbackHandler; import org.apache.zookeeper.common.X509Util; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.auth.KerberosName; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; import org.ietf.jgss.Oid; import org.slf4j.Logger; public final class SecurityUtils { public static final String QUORUM_HOSTNAME_PATTERN = "_HOST"; /** * Create an instance of a SaslClient. It will return null if there is an exception. * * @param subject subject * @param servicePrincipal principal * @param protocol name of the protocol for which the authentication is being performed * @param serverName name of the server to authenticate to * @param LOG logger * @param entity can be either zookeeper client or quorum learner * * @return saslclient object * @throws SaslException */ public static SaslClient createSaslClient( ZKConfig config, final Subject subject, final String servicePrincipal, final String protocol, final String serverName, final Logger LOG, final String entity) throws SaslException { SaslClient saslClient; // Use subject.getPrincipals().isEmpty() as an indication of which SASL // mechanism to use: if empty, use DIGEST-MD5; otherwise, use GSSAPI. if (subject.getPrincipals().isEmpty()) { // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism // instead. // FIPS-mode: don't try DIGEST-MD5, just return error if (X509Util.getFipsMode(config)) { LOG.warn("{} will not use DIGEST-MD5 as SASL mechanism, because FIPS mode is enabled.", entity); return null; } LOG.info("{} will use DIGEST-MD5 as SASL mechanism.", entity); String[] mechs = {"DIGEST-MD5"}; String username = (String) (subject.getPublicCredentials().toArray()[0]); String password = (String) (subject.getPrivateCredentials().toArray()[0]); // 'domain' parameter is hard-wired between the server and client saslClient = Sasl.createSaslClient(mechs, username, protocol, serverName, null, new SaslClientCallbackHandler(password, entity)); return saslClient; } else { // GSSAPI. final Object[] principals = subject.getPrincipals().toArray(); // determine client principal from subject. final Principal clientPrincipal = (Principal) principals[0]; boolean usingNativeJgss = Boolean.getBoolean("sun.security.jgss.native"); if (usingNativeJgss) { // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html // """ // In addition, when performing operations as a particular // Subject, e.g. Subject.doAs(...) or // Subject.doAsPrivileged(...), // the to-be-used GSSCredential should be added to Subject's // private credential set. Otherwise, the GSS operations will // fail since no credential is found. // """ try { GSSManager manager = GSSManager.getInstance(); Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); GSSCredential cred = manager.createCredential(null, GSSContext.DEFAULT_LIFETIME, krb5Mechanism, GSSCredential.INITIATE_ONLY); subject.getPrivateCredentials().add(cred); LOG.debug("Added private credential to {} principal name: '{}'", entity, clientPrincipal); } catch (GSSException ex) { LOG.warn("Cannot add private credential to subject; authentication at the server may fail", ex); } } final KerberosName clientKerberosName = new KerberosName(clientPrincipal.getName()); // assume that server and client are in the same realm (by default; // unless the system property // "zookeeper.server.realm" is set). String serverRealm = System.getProperty("zookeeper.server.realm", clientKerberosName.getRealm()); String modifiedServerPrincipal = servicePrincipal; // If service principal does not contain realm, then add it if (!modifiedServerPrincipal.contains("@")) { modifiedServerPrincipal = modifiedServerPrincipal + "@" + serverRealm; } KerberosName serviceKerberosName = new KerberosName(modifiedServerPrincipal); final String serviceName = serviceKerberosName.getServiceName(); final String serviceHostname = serviceKerberosName.getHostName(); final String clientPrincipalName = clientKerberosName.toString(); try { saslClient = Subject.doAs(subject, new PrivilegedExceptionAction() { public SaslClient run() throws SaslException { LOG.info("{} will use GSSAPI as SASL mechanism.", entity); String[] mechs = {"GSSAPI"}; LOG.debug( "creating sasl client: {}={};service={};serviceHostname={}", entity, clientPrincipalName, serviceName, serviceHostname); SaslClient saslClient = Sasl.createSaslClient( mechs, clientPrincipalName, serviceName, serviceHostname, null, new SaslClientCallbackHandler(null, entity)); return saslClient; } }); return saslClient; } catch (Exception e) { LOG.error("Exception while trying to create SASL client", e); return null; } } } /** * Create an instance of a SaslServer. It will return null if there is an exception. * * @param subject subject * @param protocol protocol * @param serverName server name * @param callbackHandler login callback handler * @param LOG logger * @return sasl server object */ public static SaslServer createSaslServer( final Subject subject, final String protocol, final String serverName, final CallbackHandler callbackHandler, final Logger LOG) { if (subject != null) { // server is using a JAAS-authenticated subject: determine service // principal name and hostname from zk server's subject. if (subject.getPrincipals().size() > 0) { try { final Object[] principals = subject.getPrincipals().toArray(); final Principal servicePrincipal = (Principal) principals[0]; // e.g. servicePrincipalNameAndHostname := // "zookeeper/myhost.foo.com@FOO.COM" final String servicePrincipalNameAndHostname = servicePrincipal.getName(); int indexOf = servicePrincipalNameAndHostname.indexOf("/"); // e.g. servicePrincipalName := "zookeeper" final String servicePrincipalName = servicePrincipalNameAndHostname.substring(0, indexOf); // e.g. serviceHostnameAndKerbDomain := // "myhost.foo.com@FOO.COM" final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname.substring(indexOf + 1); indexOf = serviceHostnameAndKerbDomain.indexOf("@"); // e.g. serviceHostname := "myhost.foo.com" final String serviceHostname = serviceHostnameAndKerbDomain.substring(0, indexOf); // TODO: should depend on zoo.cfg specified mechs, but if // subject is non-null, it can be assumed to be GSSAPI. final String mech = "GSSAPI"; LOG.debug("serviceHostname is '{}'", serviceHostname); LOG.debug("servicePrincipalName is '{}'", servicePrincipalName); LOG.debug("SASL mechanism(mech) is '{}'", mech); boolean usingNativeJgss = Boolean.getBoolean("sun.security.jgss.native"); if (usingNativeJgss) { // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html // """ // In addition, when performing operations as a // particular // Subject, e.g. Subject.doAs(...) or // Subject.doAsPrivileged(...), the to-be-used // GSSCredential should be added to Subject's // private credential set. Otherwise, the GSS operations // will fail since no credential is found. // """ try { GSSManager manager = GSSManager.getInstance(); Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); GSSName gssName = manager.createName( servicePrincipalName + "@" + serviceHostname, GSSName.NT_HOSTBASED_SERVICE); GSSCredential cred = manager.createCredential(gssName, GSSContext.DEFAULT_LIFETIME, krb5Mechanism, GSSCredential.ACCEPT_ONLY); subject.getPrivateCredentials().add(cred); LOG.debug( "Added private credential to service principal name: '{}', GSSCredential name: {}", servicePrincipalName, cred.getName()); } catch (GSSException ex) { LOG.warn("Cannot add private credential to subject; clients authentication may fail", ex); } } try { return Subject.doAs(subject, new PrivilegedExceptionAction() { public SaslServer run() { try { SaslServer saslServer; saslServer = Sasl.createSaslServer(mech, servicePrincipalName, serviceHostname, null, callbackHandler); return saslServer; } catch (SaslException e) { LOG.error("Zookeeper Server failed to create a SaslServer to interact with a client during session initiation", e); return null; } } }); } catch (PrivilegedActionException e) { // TODO: exit server at this point(?) LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException exception while creating a SaslServer using a JAAS principal context", e); } } catch (IndexOutOfBoundsException e) { LOG.error("server principal name/hostname determination error", e); } } else { // JAAS non-GSSAPI authentication: assuming and supporting only // DIGEST-MD5 mechanism for now. // TODO: use 'authMech=' value in zoo.cfg. try { SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5", protocol, serverName, null, callbackHandler); return saslServer; } catch (SaslException e) { LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e); } } } return null; } /** * Convert Kerberos principal name pattern to valid Kerberos principal name. * If the principal name contains hostname pattern "_HOST" then it replaces * with the given hostname, which should be fully-qualified domain name. * * @param principalConfig * the Kerberos principal name conf value to convert * @param hostname * the fully-qualified domain name used for substitution * @return converted Kerberos principal name */ public static String getServerPrincipal(String principalConfig, String hostname) { String[] components = getComponents(principalConfig); if (components == null || components.length != 2 || !components[1].equals(QUORUM_HOSTNAME_PATTERN)) { return principalConfig; } else { return replacePattern(components, hostname); } } private static String[] getComponents(String principalConfig) { if (principalConfig == null) { return null; } return principalConfig.split("[/]"); } private static String replacePattern(String[] components, String hostname) { return components[0] + "/" + hostname.toLowerCase(); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/java/org/apache/zookeeper/util/ServiceUtils.java0100644 0000000 0000000 00000005447 15051152474 033545 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Objects; import java.util.function.Consumer; import org.apache.zookeeper.server.ExitCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utilities for service management. */ public abstract class ServiceUtils { private static final Logger LOG = LoggerFactory.getLogger(ServiceUtils.class); private ServiceUtils() { } /** * Default strategy for shutting down the JVM. */ @SuppressFBWarnings("DM_EXIT") public static final Consumer SYSTEM_EXIT = (code) -> { String msg = "Exiting JVM with code {}"; if (code == 0) { // JVM exits normally LOG.info(msg, code); } else { // JVM exits with error LOG.error(msg, code); } System.exit(code); }; /** * No-op strategy, useful for tests. */ public static final Consumer LOG_ONLY = (code) -> { if (code != 0) { LOG.error("Fatal error, JVM should exit with code {}. " + "Actually System.exit is disabled", code); } else { LOG.info("JVM should exit with code {}. Actually System.exit is disabled", code); } }; private static volatile Consumer systemExitProcedure = SYSTEM_EXIT; /** * Override system callback. Useful for preventing the JVM to exit in tests * or in applications that are running an in-process ZooKeeper server. * * @param systemExitProcedure */ public static void setSystemExitProcedure(Consumer systemExitProcedure) { Objects.requireNonNull(systemExitProcedure); ServiceUtils.systemExitProcedure = systemExitProcedure; } /** * Force shutdown of the JVM using System.exit. * * @param code the exit code * @see ExitCode */ public static void requestSystemExit(int code) { systemExitProcedure.accept(code); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java-filtered_org_apache_zookeeper_0100644 0000000 0000000 00000000157 15051152474 032562 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/zookeeper/version/Info.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/zookeeper/version/Info.jav0100644 0000000 0000000 00000002354 15051152474 034154 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.version; public interface Info { int MAJOR=${parsedVersion.majorVersion}; int MINOR=${parsedVersion.minorVersion}; int MICRO=${parsedVersion.incrementalVersion}; String QUALIFIER="${parsedVersion.qualifier}".isEmpty() ? null : "${parsedVersion.qualifier}"; int REVISION=-1; //@deprecated, please use REVISION_HASH String REVISION_HASH="${mvngit.commit.id}"; String BUILD_DATE="${build.time}"; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_java-filtered_org_apache_zookeeper_0100644 0000000 0000000 00000000172 15051152474 032557 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/zookeeper/version/VersionInfoMain.java apache-zookeeper-3.9.4/zookeeper-server/src/main/java-filtered/org/apache/zookeeper/version/VersionI0100644 0000000 0000000 00000002051 15051152474 034232 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.version; public class VersionInfoMain implements org.apache.zookeeper.version.Info { public static void main(String[] args) { System.out.println("Apache ZooKeeper, version ${project.version} ${build.time}"); } } apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/LICENSE.txt0100644 0000000 0000000 00000027063 15051152474 026203 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. This distribution bundles javacc, which is available under the 3-clause BSD License. For details, see a copy of the license in lib/javacc.LICENSE.txt This distribution bundles jline 2.14.6, which is available under the 2-clause BSD License. For details, see a copy of the license in lib/jline-2.14.6.LICENSE.txt This distribution bundles SLF4J 1.7.30, which is available under the MIT License. For details, see a copy of the license in lib/slf4j-1.7.30.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/NOTICE.txt0100644 0000000 0000000 00000021113 15051152474 026070 0ustar00rootroot0000000 0000000 Apache ZooKeeper Copyright 2009-2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). This product includes software components originally developed for Airlift (https://github.com/airlift/airlift), licensed under the Apache 2.0 license. The licensing terms for Airlift code can be found at: https://github.com/airlift/airlift/blob/master/LICENSE This product includes software developed by The Netty Project (http://netty.io/) Copyright 2011 The Netty Project The Netty NOTICE file (https://github.com/netty/netty/blob/4.1/NOTICE.txt) contains the following items: ---------------- start of netty NOTICE file ---------------- This product contains the extensions to Java Collections Framework which has been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: * LICENSE: * license/LICENSE.jsr166y.txt (Public Domain) * HOMEPAGE: * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ This product contains a modified version of Robert Harder's Public Domain Base64 Encoder and Decoder, which can be obtained at: * LICENSE: * license/LICENSE.base64.txt (Public Domain) * HOMEPAGE: * http://iharder.sourceforge.net/current/java/base64/ This product contains a modified portion of 'Webbit', an event based WebSocket and HTTP server, which can be obtained at: * LICENSE: * license/LICENSE.webbit.txt (BSD License) * HOMEPAGE: * https://github.com/joewalnes/webbit This product contains a modified portion of 'SLF4J', a simple logging facade for Java, which can be obtained at: * LICENSE: * license/LICENSE.slf4j.txt (MIT License) * HOMEPAGE: * https://www.slf4j.org/ This product contains a modified portion of 'Apache Harmony', an open source Java SE, which can be obtained at: * NOTICE: * license/NOTICE.harmony.txt * LICENSE: * license/LICENSE.harmony.txt (Apache License 2.0) * HOMEPAGE: * https://archive.apache.org/dist/harmony/ This product contains a modified portion of 'jbzip2', a Java bzip2 compression and decompression library written by Matthew J. Francis. It can be obtained at: * LICENSE: * license/LICENSE.jbzip2.txt (MIT License) * HOMEPAGE: * https://code.google.com/p/jbzip2/ This product contains a modified portion of 'libdivsufsort', a C API library to construct the suffix array and the Burrows-Wheeler transformed string for any input string of a constant-size alphabet written by Yuta Mori. It can be obtained at: * LICENSE: * license/LICENSE.libdivsufsort.txt (MIT License) * HOMEPAGE: * https://github.com/y-256/libdivsufsort This product contains a modified portion of Nitsan Wakart's 'JCTools', Java Concurrency Tools for the JVM, which can be obtained at: * LICENSE: * license/LICENSE.jctools.txt (ASL2 License) * HOMEPAGE: * https://github.com/JCTools/JCTools This product optionally depends on 'JZlib', a re-implementation of zlib in pure Java, which can be obtained at: * LICENSE: * license/LICENSE.jzlib.txt (BSD style License) * HOMEPAGE: * http://www.jcraft.com/jzlib/ This product optionally depends on 'Compress-LZF', a Java library for encoding and decoding data in LZF format, written by Tatu Saloranta. It can be obtained at: * LICENSE: * license/LICENSE.compress-lzf.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/ning/compress This product optionally depends on 'lz4', a LZ4 Java compression and decompression library written by Adrien Grand. It can be obtained at: * LICENSE: * license/LICENSE.lz4.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/jpountz/lz4-java This product optionally depends on 'lzma-java', a LZMA Java compression and decompression library, which can be obtained at: * LICENSE: * license/LICENSE.lzma-java.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/jponge/lzma-java This product optionally depends on 'zstd-jni', a zstd-jni Java compression and decompression library, which can be obtained at: * LICENSE: * license/LICENSE.zstd-jni.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/luben/zstd-jni This product contains a modified portion of 'jfastlz', a Java port of FastLZ compression and decompression library written by William Kinney. It can be obtained at: * LICENSE: * license/LICENSE.jfastlz.txt (MIT License) * HOMEPAGE: * https://code.google.com/p/jfastlz/ This product contains a modified portion of and optionally depends on 'Protocol Buffers', Google's data interchange format, which can be obtained at: * LICENSE: * license/LICENSE.protobuf.txt (New BSD License) * HOMEPAGE: * https://github.com/google/protobuf This product optionally depends on 'Bouncy Castle Crypto APIs' to generate a temporary self-signed X.509 certificate when the JVM does not provide the equivalent functionality. It can be obtained at: * LICENSE: * license/LICENSE.bouncycastle.txt (MIT License) * HOMEPAGE: * https://www.bouncycastle.org/ This product optionally depends on 'Snappy', a compression library produced by Google Inc, which can be obtained at: * LICENSE: * license/LICENSE.snappy.txt (New BSD License) * HOMEPAGE: * https://github.com/google/snappy This product optionally depends on 'JBoss Marshalling', an alternative Java serialization API, which can be obtained at: * LICENSE: * license/LICENSE.jboss-marshalling.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/jboss-remoting/jboss-marshalling This product optionally depends on 'Caliper', Google's micro- benchmarking framework, which can be obtained at: * LICENSE: * license/LICENSE.caliper.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/google/caliper This product optionally depends on 'Apache Commons Logging', a logging framework, which can be obtained at: * LICENSE: * license/LICENSE.commons-logging.txt (Apache License 2.0) * HOMEPAGE: * https://commons.apache.org/logging/ This product optionally depends on 'Apache Log4J', a logging framework, which can be obtained at: * LICENSE: * license/LICENSE.log4j.txt (Apache License 2.0) * HOMEPAGE: * https://logging.apache.org/log4j/ This product optionally depends on 'Aalto XML', an ultra-high performance non-blocking XML processor, which can be obtained at: * LICENSE: * license/LICENSE.aalto-xml.txt (Apache License 2.0) * HOMEPAGE: * https://wiki.fasterxml.com/AaltoHome This product contains a modified version of 'HPACK', a Java implementation of the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at: * LICENSE: * license/LICENSE.hpack.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/twitter/hpack This product contains a modified version of 'HPACK', a Java implementation of the HTTP/2 HPACK algorithm written by Cory Benfield. It can be obtained at: * LICENSE: * license/LICENSE.hyper-hpack.txt (MIT License) * HOMEPAGE: * https://github.com/python-hyper/hpack/ This product contains a modified version of 'HPACK', a Java implementation of the HTTP/2 HPACK algorithm written by Tatsuhiro Tsujikawa. It can be obtained at: * LICENSE: * license/LICENSE.nghttp2-hpack.txt (MIT License) * HOMEPAGE: * https://github.com/nghttp2/nghttp2/ This product contains a modified portion of 'Apache Commons Lang', a Java library provides utilities for the java.lang API, which can be obtained at: * LICENSE: * license/LICENSE.commons-lang.txt (Apache License 2.0) * HOMEPAGE: * https://commons.apache.org/proper/commons-lang/ This product contains the Maven wrapper scripts from 'Maven Wrapper', that provides an easy way to ensure a user has everything necessary to run the Maven build. * LICENSE: * license/LICENSE.mvn-wrapper.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/takari/maven-wrapper This product contains the dnsinfo.h header file, that provides a way to retrieve the system DNS configuration on MacOS. This private header is also used by Apple's open source mDNSResponder (https://opensource.apple.com/tarballs/mDNSResponder/). * LICENSE: * license/LICENSE.dnsinfo.txt (Apple Public Source License 2.0) * HOMEPAGE: * https://www.opensource.apple.com/source/configd/configd-453.19/dnsinfo/dnsinfo.h This product optionally depends on 'Brotli4j', Brotli compression and decompression for Java., which can be obtained at: * LICENSE: * license/LICENSE.brotli4j.txt (Apache License 2.0) * HOMEPAGE: * https://github.com/hyperxpro/Brotli4j ---------------- end of netty NOTICE file ---------------- apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lastRevision.bat0100644 0000000 0000000 00000001756 15051152474 027533 0ustar00rootroot0000000 0000000 echo off rem Licensed to the Apache Software Foundation (ASF) under one rem or more contributor license agreements. See the NOTICE file rem distributed with this work for additional information rem regarding copyright ownership. The ASF licenses this file rem to you under the Apache License, Version 2.0 (the rem "License"); you may not use this file except in compliance rem with 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. rem Find the current revision, store it in a file, for DOS for /f "delims=" %%i in ('git rev-parse HEAD') do set rev=%%i echo lastRevision=%rev% > %1 ) apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lastRevision.sh0100755 0000000 0000000 00000001633 15051152474 027374 0ustar00rootroot0000000 0000000 #!/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. # Find the current revision, store it in a file FILE=$1 LASTREV=$(git rev-parse HEAD) echo "lastRevision=${LASTREV}" > "$FILE" apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/commons-cli-1.5.0.LICENSE.txt0100644 0000000 0000000 00000026137 15051152474 031630 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/commons-io-2.11.0.LICENSE.txt0100644 0000000 0000000 00000026137 15051152474 031546 0ustar00rootroot0000000 0000000 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-client-9.4.57.v0100644 0000000 0000000 00000000162 15051152474 032010 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-client-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-client-9.4.57.v20241219.LICENSE0100644 0000000 0000000 00000055160 15051152474 032377 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-http-9.4.57.v200100644 0000000 0000000 00000000160 15051152474 031651 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-http-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-http-9.4.57.v20241219.LICENSE.t0100644 0000000 0000000 00000055160 15051152474 032342 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-io-9.4.57.v20240100644 0000000 0000000 00000000156 15051152474 031454 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-io-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-io-9.4.57.v20241219.LICENSE.txt0100644 0000000 0000000 00000055160 15051152474 032346 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-security-9.4.570100644 0000000 0000000 00000000164 15051152474 032137 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-security-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-security-9.4.57.v20241219.LICEN0100644 0000000 0000000 00000055160 15051152474 032540 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-server-9.4.57.v0100644 0000000 0000000 00000000162 15051152474 032040 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-server-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-server-9.4.57.v20241219.LICENSE0100644 0000000 0000000 00000055160 15051152474 032427 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-servlet-9.4.57.0100644 0000000 0000000 00000000163 15051152474 032031 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-servlet-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-servlet-9.4.57.v20241219.LICENS0100644 0000000 0000000 00000055160 15051152474 032500 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-util-9.4.57.v200100644 0000000 0000000 00000000160 15051152474 031647 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-util-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-util-9.4.57.v20241219.LICENSE.t0100644 0000000 0000000 00000055160 15051152474 032340 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_jetty-util-ajax-9.4.50100644 0000000 0000000 00000000165 15051152474 032100 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-util-ajax-9.4.57.v20241219.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jetty-util-ajax-9.4.57.v20241219.LICE0100644 0000000 0000000 00000055160 15051152474 032451 0ustar00rootroot0000000 0000000 This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 which is available at https://www.apache.org/licenses/LICENSE-2.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. 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. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/jline-2.14.6.LICENSE.txt0100644 0000000 0000000 00000002777 15051152474 030604 0ustar00rootroot0000000 0000000 Copyright (c) 2002-2012, the original author or authors. All rights reserved. http://www.opensource.org/licenses/bsd-license.php 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 JLine nor the names of its 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. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/logback-classic-1.3.15.LICENSE.txt0100644 0000000 0000000 00000106662 15051152474 032517 0ustar00rootroot0000000 0000000 Logback LICENSE --------------- Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights reserved. This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation or (per the licensee's choosing) under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/logback-core-1.3.15.LICENSE.txt0100644 0000000 0000000 00000106662 15051152474 032026 0ustar00rootroot0000000 0000000 Logback LICENSE --------------- Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights reserved. This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation or (per the licensee's choosing) under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_metrics-core-4.1.12.10100644 0000000 0000000 00000000156 15051152474 031646 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/metrics-core-4.1.12.1.jar_LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/metrics-core-4.1.12.1.jar_LICENSE.txt0100644 0000000 0000000 00000026142 15051152474 033055 0ustar00rootroot0000000 0000000 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 2010-2012 Coda Hale and Yammer, Inc. 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-buffer-4.1.119.0100644 0000000 0000000 00000000157 15051152474 031674 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-buffer-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-buffer-4.1.119.Final.LICENSE.tx0100644 0000000 0000000 00000026150 15051152474 033033 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-codec-4.1.119.F0100644 0000000 0000000 00000000156 15051152474 031605 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-codec-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-codec-4.1.119.Final.LICENSE.txt0100644 0000000 0000000 00000026150 15051152474 033023 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-common-4.1.119.0100644 0000000 0000000 00000000157 15051152474 031713 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-common-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-common-4.1.119.Final.LICENSE.tx0100644 0000000 0000000 00000026150 15051152474 033052 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-handler-4.1.1190100644 0000000 0000000 00000000160 15051152474 031754 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-handler-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-handler-4.1.119.Final.LICENSE.t0100644 0000000 0000000 00000026150 15051152474 033007 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-resolver-4.1.110100644 0000000 0000000 00000000161 15051152474 032110 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-resolver-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-resolver-4.1.119.Final.LICENSE.0100644 0000000 0000000 00000026150 15051152474 033047 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-transport-4.1.10100644 0000000 0000000 00000000162 15051152474 032223 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-transport-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-transport-4.1.119.Final.LICENSE0100644 0000000 0000000 00000026150 15051152474 033164 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-transport-class0100644 0000000 0000000 00000000200 15051152474 032660 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-transport-classes-epoll-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-transport-classes-epoll-4.1.1190100644 0000000 0000000 00000026150 15051152474 033617 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_netty-transport-nativ0100644 0000000 0000000 00000000205 15051152474 032701 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-transport-native-unix-common-4.1.119.Final.LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/netty-transport-native-unix-common-4.0100644 0000000 0000000 00000026150 15051152474 034214 0ustar00rootroot0000000 0000000 Apache License Version 2.0, January 2004 https://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 1999-2005 The Apache Software Foundation 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 https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient-0.9.0.LICENSE.txt0100644 0000000 0000000 00000026146 15051152474 032103 0ustar00rootroot0000000 0000000 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 1999-2005 The Apache Software Foundation 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_simpleclient_common-00100644 0000000 0000000 00000000156 15051152474 032567 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient_common-0.9.0_LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient_common-0.9.0_LICENSE.txt0100644 0000000 0000000 00000026146 15051152474 033534 0ustar00rootroot0000000 0000000 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 1999-2005 The Apache Software Foundation 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_simpleclient_hotspot-0100644 0000000 0000000 00000000157 15051152474 032720 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient_hotspot-0.9.0_LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient_hotspot-0.9.0_LICENSE.tx0100644 0000000 0000000 00000026146 15051152474 033560 0ustar00rootroot0000000 0000000 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 1999-2005 The Apache Software Foundation 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. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_main_resources_lib_simpleclient_servlet-0100644 0000000 0000000 00000000157 15051152474 032704 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient_servlet-0.9.0_LICENSE.txt apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/simpleclient_servlet-0.9.0_LICENSE.tx0100644 0000000 0000000 00000026146 15051152474 033544 0ustar00rootroot0000000 0000000 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 1999-2005 The Apache Software Foundation 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. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/slf4j-2.0.13.LICENSE.txt0100644 0000000 0000000 00000002155 15051152474 030504 0ustar00rootroot0000000 0000000 Copyright (c) 2004-2025 QOS.ch All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/lib/snappy-java-1.1.10.5.jar_LICENSE.txt0100644 0000000 0000000 00000026136 15051152474 032714 0ustar00rootroot0000000 0000000 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. apache-zookeeper-3.9.4/zookeeper-server/src/main/resources/overview.html0100644 0000000 0000000 00000001771 15051152474 027112 0ustar00rootroot0000000 0000000 ZooKeeper ZooKeeper is a service for coordinating processes of distributed applications. ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_ClientCan0100644 0000000 0000000 00000000160 15051152474 032574 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCanonicalizeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCanonicalizeTest.ja0100644 0000000 0000000 00000010503 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.zookeeper.client.ZKClientConfig; import org.junit.jupiter.api.Test; public class ClientCanonicalizeTest extends ZKTestCase { @Test public void testClientCanonicalization() throws IOException, InterruptedException { SaslServerPrincipal.WrapperInetSocketAddress addr = mock(SaslServerPrincipal.WrapperInetSocketAddress.class); SaslServerPrincipal.WrapperInetAddress ia = mock(SaslServerPrincipal.WrapperInetAddress.class); when(addr.getHostName()).thenReturn("zookeeper.apache.org"); when(addr.getAddress()).thenReturn(ia); when(ia.getCanonicalHostName()).thenReturn("zk1.apache.org"); when(ia.getHostAddress()).thenReturn("127.0.0.1"); ZKClientConfig conf = new ZKClientConfig(); String principal = SaslServerPrincipal.getServerPrincipal(addr, conf); assertEquals("zookeeper/zk1.apache.org", principal, "The computed principal does not appear to have been canonicalized"); } @Test public void testClientNoCanonicalization() throws IOException, InterruptedException { SaslServerPrincipal.WrapperInetSocketAddress addr = mock(SaslServerPrincipal.WrapperInetSocketAddress.class); SaslServerPrincipal.WrapperInetAddress ia = mock(SaslServerPrincipal.WrapperInetAddress.class); when(addr.getHostName()).thenReturn("zookeeper.apache.org"); when(addr.getAddress()).thenReturn(ia); when(ia.getCanonicalHostName()).thenReturn("zk1.apache.org"); when(ia.getHostAddress()).thenReturn("127.0.0.1"); ZKClientConfig conf = new ZKClientConfig(); conf.setProperty(ZKClientConfig.ZK_SASL_CLIENT_CANONICALIZE_HOSTNAME, "false"); String principal = SaslServerPrincipal.getServerPrincipal(addr, conf); assertEquals("zookeeper/zookeeper.apache.org", principal, "The computed principal does appears to have been canonicalized incorrectly"); } @Test public void testClientCanonicalizationToIp() throws IOException, InterruptedException { SaslServerPrincipal.WrapperInetSocketAddress addr = mock(SaslServerPrincipal.WrapperInetSocketAddress.class); SaslServerPrincipal.WrapperInetAddress ia = mock(SaslServerPrincipal.WrapperInetAddress.class); when(addr.getHostName()).thenReturn("zookeeper.apache.org"); when(addr.getAddress()).thenReturn(ia); when(ia.getCanonicalHostName()).thenReturn("127.0.0.1"); when(ia.getHostAddress()).thenReturn("127.0.0.1"); ZKClientConfig conf = new ZKClientConfig(); String principal = SaslServerPrincipal.getServerPrincipal(addr, conf); assertEquals("zookeeper/zookeeper.apache.org", principal, "The computed principal does appear to have falled back to the original host name"); } @Test public void testGetServerPrincipalReturnConfiguredPrincipalName() { ZKClientConfig config = new ZKClientConfig(); String configuredPrincipal = "zookeeper/zookeeper.apache.org@APACHE.ORG"; config.setProperty(ZKClientConfig.ZOOKEEPER_SERVER_PRINCIPAL, configuredPrincipal); // Testing the case where server principal is configured, therefore InetSocketAddress is passed as null String serverPrincipal = SaslServerPrincipal.getServerPrincipal((InetSocketAddress) null, config); assertEquals(configuredPrincipal, serverPrincipal); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_ClientCnx0100644 0000000 0000000 00000000167 15051152474 032632 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketFragilityTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketFragility0100644 0000000 0000000 00000030733 15051152474 034347 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ClientCnxn.Packet; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; public class ClientCnxnSocketFragilityTest extends QuorumPeerTestBase { private static final int SERVER_COUNT = 3; private static final int SESSION_TIMEOUT = 40000; public static final int CONNECTION_TIMEOUT = 30000; private final UnsafeCoordinator unsafeCoordinator = new UnsafeCoordinator(); private volatile CustomZooKeeper zk = null; private volatile FragileClientCnxnSocketNIO socket = null; private volatile CustomClientCnxn cnxn = null; private String getCxnString(int[] clientPorts) { StringBuffer hostPortBuffer = new StringBuffer(); for (int i = 0; i < clientPorts.length; i++) { hostPortBuffer.append("127.0.0.1:"); hostPortBuffer.append(clientPorts[i]); if (i != (clientPorts.length - 1)) { hostPortBuffer.append(','); } } return hostPortBuffer.toString(); } private void closeZookeeper(ZooKeeper zk) { Executors.newSingleThreadExecutor().submit(() -> { try { LOG.info("closeZookeeper is fired"); zk.close(); } catch (InterruptedException e) { } }); } @Test public void testClientCnxnSocketFragility() throws Exception { System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, FragileClientCnxnSocketNIO.class.getName()); System.setProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT, "1000"); final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // Ensure server started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } String path = "/testClientCnxnSocketFragility"; String data = "balabala"; ClientWatcher watcher = new ClientWatcher(); zk = new CustomZooKeeper(getCxnString(clientPorts), SESSION_TIMEOUT, watcher); watcher.watchFor(zk); // Let's see some successful operations zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData(path, false, new Stat())), data); assertTrue(!watcher.isSessionExpired()); // Let's make a broken operation socket.mute(); boolean catchKeeperException = false; try { zk.getData(path, false, new Stat()); } catch (KeeperException e) { catchKeeperException = true; assertFalse(e instanceof KeeperException.SessionExpiredException); } socket.unmute(); assertTrue(catchKeeperException); assertTrue(!watcher.isSessionExpired()); GetDataRetryForeverBackgroundTask retryForeverGetData = new GetDataRetryForeverBackgroundTask(zk, path); retryForeverGetData.startTask(); // Let's make a broken network socket.mute(); // Let's attempt to close ZooKeeper cnxn.attemptClose(); // Wait some time to expect continuous reconnecting. // We try to make reconnecting hit the unsafe region. cnxn.waitUntilHitUnsafeRegion(); // close zk with timeout 1000 milli seconds closeZookeeper(zk); TimeUnit.MILLISECONDS.sleep(3000); // Since we already close zookeeper, we expect that the zk should not be alive. assertTrue(!zk.isAlive()); assertTrue(!watcher.isSessionExpired()); retryForeverGetData.syncCloseTask(); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } class GetDataRetryForeverBackgroundTask extends Thread { private volatile boolean alive; private final CustomZooKeeper zk; private final String path; GetDataRetryForeverBackgroundTask(CustomZooKeeper zk, String path) { this.alive = false; this.zk = zk; this.path = path; // marked as daemon to avoid exhausting CPU setDaemon(true); } void startTask() { alive = true; start(); } void syncCloseTask() throws InterruptedException { alive = false; join(); } @Override public void run() { while (alive) { try { zk.getData(path, false, new Stat()); // sleep for a while to avoid exhausting CPU TimeUnit.MILLISECONDS.sleep(500); } catch (Exception e) { LOG.info("zookeeper getData failed on path {}", path); } } } } public static class FragileClientCnxnSocketNIO extends ClientCnxnSocketNIO { private volatile boolean mute; public FragileClientCnxnSocketNIO(ZKClientConfig clientConfig) throws IOException { super(clientConfig); mute = false; } synchronized void mute() { if (!mute) { LOG.info("Fire socket mute"); mute = true; } } synchronized void unmute() { if (mute) { LOG.info("Fire socket unmute"); mute = false; } } @Override void doTransport(int waitTimeOut, Queue pendingQueue, ClientCnxn cnxn) throws IOException, InterruptedException { if (mute) { throw new IOException("Socket is mute"); } super.doTransport(waitTimeOut, pendingQueue, cnxn); } @Override void connect(InetSocketAddress addr) throws IOException { if (mute) { throw new IOException("Socket is mute"); } super.connect(addr); } } class ClientWatcher implements Watcher { private ZooKeeper zk; private boolean sessionExpired = false; void watchFor(ZooKeeper zk) { this.zk = zk; } @Override public void process(WatchedEvent event) { LOG.info("Watcher got {}", event); if (event.getState() == KeeperState.Expired) { sessionExpired = true; } } boolean isSessionExpired() { return sessionExpired; } } // Coordinate to construct the risky scenario. class UnsafeCoordinator { private CountDownLatch syncLatch = new CountDownLatch(2); void sync(boolean closing) { LOG.info("Attempt to sync with {}", closing); if (closing) { syncLatch.countDown(); try { syncLatch.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } class CustomClientCnxn extends ClientCnxn { private volatile boolean closing = false; private volatile boolean hitUnsafeRegion = false; public CustomClientCnxn( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig zkClientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly ) throws IOException { super( chrootPath, hostProvider, sessionTimeout, zkClientConfig, defaultWatcher, clientCnxnSocket, canBeReadOnly); } void attemptClose() { closing = true; } void waitUntilHitUnsafeRegion() { while (!hitUnsafeRegion) { try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { } } } @Override protected void onConnecting(InetSocketAddress addr) { if (closing) { LOG.info("Attempt to connnecting {} {} {}", addr, closing, state); ///////// Unsafe Region //////// // Slow down and zoom out the unsafe point to make risk // The unsafe point is that startConnect happens after sendThread.close hitUnsafeRegion = true; unsafeCoordinator.sync(closing); //////////////////////////////// } } @Override public void disconnect() { assertTrue(closing); LOG.info("Attempt to disconnecting client for session: 0x{} {} {}", Long.toHexString(getSessionId()), closing, state); sendThread.close(); ///////// Unsafe Region //////// unsafeCoordinator.sync(closing); //////////////////////////////// try { sendThread.join(); } catch (InterruptedException ex) { LOG.warn("Got interrupted while waiting for the sender thread to close", ex); } eventThread.queueEventOfDeath(); } } class CustomZooKeeper extends ZooKeeper { public CustomZooKeeper(String connectString, int sessionTimeout, Watcher watcher) throws IOException { super(connectString, sessionTimeout, watcher); } public boolean isAlive() { return cnxn.getState().isAlive(); } ClientCnxn createConnection( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig clientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly ) throws IOException { assertTrue(clientCnxnSocket instanceof FragileClientCnxnSocketNIO); socket = (FragileClientCnxnSocketNIO) clientCnxnSocket; ClientCnxnSocketFragilityTest.this.cnxn = new CustomClientCnxn( chrootPath, hostProvider, sessionTimeout, clientConfig, defaultWatcher, clientCnxnSocket, canBeReadOnly); return ClientCnxnSocketFragilityTest.this.cnxn; } } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_ClientCnx0100644 0000000 0000000 00000000156 15051152474 032630 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientCnxnSocketTest.java0100644 0000000 0000000 00000007641 15051152474 034256 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.test.TestByteBufAllocator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ClientCnxnSocketTest { @BeforeEach public void setUp() { ClientCnxnSocketNetty.setTestAllocator(TestByteBufAllocator.getInstance()); } @AfterEach public void tearDown() { ClientCnxnSocketNetty.clearTestAllocator(); TestByteBufAllocator.checkForLeaks(); } @Test public void testWhenInvalidJuteMaxBufferIsConfiguredIOExceptionIsThrown() { ZKClientConfig clientConfig = new ZKClientConfig(); String value = "SomeInvalidInt"; clientConfig.setProperty(ZKConfig.JUTE_MAXBUFFER, value); // verify ClientCnxnSocketNIO creation try { new ClientCnxnSocketNIO(clientConfig); fail("IOException is expected."); } catch (IOException e) { assertTrue(e.getMessage().contains(value)); } // verify ClientCnxnSocketNetty creation try { new ClientCnxnSocketNetty(clientConfig); fail("IOException is expected."); } catch (IOException e) { assertTrue(e.getMessage().contains(value)); } } /* * Tests readLength(): * 1. successfully read packet if length == jute.maxbuffer; * 2. IOException is thrown if packet length is greater than jute.maxbuffer. */ @Test public void testIOExceptionIsThrownWhenPacketLenExceedsJuteMaxBuffer() throws IOException { ClientCnxnSocket clientCnxnSocket = new ClientCnxnSocketNIO(new ZKClientConfig()); // Should successfully read packet length == jute.maxbuffer int length = ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT; clientCnxnSocket.incomingBuffer.putInt(length); clientCnxnSocket.incomingBuffer.rewind(); clientCnxnSocket.readLength(); // Failed to read packet length > jute.maxbuffer length = ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT + 1; clientCnxnSocket.incomingBuffer.putInt(length); clientCnxnSocket.incomingBuffer.rewind(); try { clientCnxnSocket.readLength(); fail("IOException is expected."); } catch (IOException e) { assertEquals("Packet len " + length + " is out of range!", e.getMessage()); } } @Test public void testClientCanBeClosedWhenNotInitialized() throws IOException { ZKClientConfig clientConfig = new ZKClientConfig(); final ClientCnxnSocketNetty clientCnxnSocket = new ClientCnxnSocketNetty(clientConfig); // Should not throw clientCnxnSocket.close(); // Call onClosing explicitly since it otherwise won't be invoked without more setup. clientCnxnSocket.onClosing(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientReconnectTest.java0100644 0000000 0000000 00000005534 15051152474 034116 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.ZKClientConfig; import org.junit.jupiter.api.Test; public class ClientReconnectTest extends ZKTestCase { private SocketChannel sc; private CountDownLatch countDownLatch = new CountDownLatch(3); class MockCnxn extends ClientCnxnSocketNIO { MockCnxn() throws IOException { super(new ZKClientConfig()); } @Override void registerAndConnect(SocketChannel sock, InetSocketAddress addr) throws IOException { countDownLatch.countDown(); throw new IOException("failed to register"); } @Override SocketChannel createSock() { return sc; } } @Test public void testClientReconnect() throws IOException, InterruptedException { HostProvider hostProvider = mock(HostProvider.class); when(hostProvider.size()).thenReturn(1); InetSocketAddress inaddr = new InetSocketAddress("127.0.0.1", 1111); when(hostProvider.next(anyLong())).thenReturn(inaddr); ZooKeeper zk = mock(ZooKeeper.class); when(zk.getClientConfig()).thenReturn(new ZKClientConfig()); sc = SocketChannel.open(); ClientCnxnSocketNIO nioCnxn = new MockCnxn(); ClientCnxn clientCnxn = new ClientCnxn( "tmp", hostProvider, 5000, zk.getClientConfig(), DummyWatcher.INSTANCE, nioCnxn, false); clientCnxn.start(); countDownLatch.await(5000, TimeUnit.MILLISECONDS); assertTrue(countDownLatch.getCount() == 0); clientCnxn.close(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_ClientReq0100644 0000000 0000000 00000000162 15051152474 032624 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ClientRequestTimeoutTest.0100644 0000000 0000000 00000027174 15051152474 034337 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.apache.jute.Record; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class ClientRequestTimeoutTest extends QuorumPeerTestBase { private static final int SERVER_COUNT = 3; private boolean dropPacket = false; private int dropPacketType = ZooDefs.OpCode.create; private boolean capturePacket = false; private int capturePacketType = ZooDefs.OpCode.create; private ClientCnxn.Packet capturedPacket = null; @Test @Timeout(value = 120) public void testClientRequestTimeout() throws Exception { int requestTimeOut = 15000; System.setProperty("zookeeper.request.timeout", Integer.toString(requestTimeOut)); final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure server started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } CountdownWatcher watch1 = new CountdownWatcher(); CustomZooKeeper zk = new CustomZooKeeper(getCxnString(clientPorts), ClientBase.CONNECTION_TIMEOUT, watch1); watch1.waitForConnected(ClientBase.CONNECTION_TIMEOUT); String data = "originalData"; // lets see one successful operation zk.create("/clientHang1", data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); // now make environment for client hang dropPacket = true; dropPacketType = ZooDefs.OpCode.create; // Test synchronous API try { zk.create("/clientHang2", data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("KeeperException is expected."); } catch (KeeperException exception) { assertEquals(KeeperException.Code.REQUESTTIMEOUT.intValue(), exception.code().intValue()); } // do cleanup zk.close(); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } @Test void testClientRequestTimeoutTime() throws Exception { long requestTimeout = TimeUnit.SECONDS.toMillis(5); System.setProperty("zookeeper.request.timeout", Long.toString(requestTimeout)); CustomZooKeeper zk = null; int clientPort = PortAssignment.unique(); MainThread mainThread = new MainThread(0, clientPort, "", false); mainThread.start(); try { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT), "waiting for server 0 being up"); CountdownWatcher watch = new CountdownWatcher(); zk = new CustomZooKeeper(getCxnString(new int[]{clientPort}), ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); dropPacket = true; dropPacketType = ZooDefs.OpCode.create; String data = "originalData"; long startTime = Time.currentElapsedTime(); try { zk.create("/testClientRequestTimeout", data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); fail("KeeperException is expected."); } catch (KeeperException exception) { long cost = Time.currentElapsedTime() - startTime; assertEquals(KeeperException.Code.REQUESTTIMEOUT, exception.code()); LOG.info("testClientRequestTimeoutTime cost:{}", cost); assertThat(cost, greaterThanOrEqualTo(requestTimeout)); assertThat(cost, lessThan(requestTimeout + 500)); } } finally { mainThread.shutdown(); if (zk != null) { zk.close(); } } } @Test void testClientRequestTimeoutTimeSimulatingSpuriousWakeup() throws Exception { long requestTimeout = TimeUnit.SECONDS.toMillis(5); System.setProperty("zookeeper.request.timeout", Long.toString(requestTimeout)); CustomZooKeeper zk = null; int clientPort = PortAssignment.unique(); MainThread mainThread = new MainThread(0, clientPort, "", false); mainThread.start(); try { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT), "waiting for server 0 being up"); CountdownWatcher watch = new CountdownWatcher(); zk = new CustomZooKeeper(getCxnString(new int[]{clientPort}), ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); dropPacket = true; dropPacketType = ZooDefs.OpCode.create; capturePacket = true; capturePacketType = ZooDefs.OpCode.create; // Simulating spurious wakeup new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(requestTimeout / 2); if (capturedPacket != null) { synchronized (capturedPacket) { capturedPacket.notifyAll(); } } } catch (InterruptedException e) { throw new RuntimeException(e); } }).start(); String data = "originalData"; long startTime = Time.currentElapsedTime(); try { zk.create("/testClientRequestTimeout", data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); fail("KeeperException is expected."); } catch (KeeperException exception) { long cost = Time.currentElapsedTime() - startTime; assertEquals(KeeperException.Code.REQUESTTIMEOUT, exception.code()); LOG.info("testClientRequestTimeoutTimeSimulatingSpuriousWakeup cost:{}", cost); assertThat(cost, greaterThanOrEqualTo(requestTimeout)); assertThat(cost, lessThan(requestTimeout + 500)); } } finally { capturePacket = false; capturedPacket = null; mainThread.shutdown(); if (zk != null) { zk.close(); } } } /** * @return connection string in the form of * 127.0.0.1:port1,127.0.0.1:port2,127.0.0.1:port3 */ private String getCxnString(int[] clientPorts) { StringBuffer hostPortBuffer = new StringBuffer(); for (int i = 0; i < clientPorts.length; i++) { hostPortBuffer.append("127.0.0.1:"); hostPortBuffer.append(clientPorts[i]); if (i != (clientPorts.length - 1)) { hostPortBuffer.append(','); } } return hostPortBuffer.toString(); } class CustomClientCnxn extends ClientCnxn { CustomClientCnxn( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig clientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly ) throws IOException { super( chrootPath, hostProvider, sessionTimeout, clientConfig, defaultWatcher, clientCnxnSocket, canBeReadOnly); } @Override public void finishPacket(Packet p) { if (dropPacket && p.requestHeader.getType() == dropPacketType) { // do nothing, just return, it is the same as packet is dropped // by the network return; } super.finishPacket(p); } @Override public Packet queuePacket( RequestHeader h, ReplyHeader r, Record request, Record response, AsyncCallback cb, String clientPath, String serverPath, Object ctx, ZooKeeper.WatchRegistration watchRegistration, WatchDeregistration watchDeregistration) { Packet packet = super.queuePacket(h, r, request, response, cb, clientPath, serverPath, ctx, watchRegistration, watchDeregistration); if (capturePacket && h != null && h.getType() == capturePacketType) { capturedPacket = packet; } return packet; } } class CustomZooKeeper extends ZooKeeper { public CustomZooKeeper(String connectString, int sessionTimeout, Watcher watcher) throws IOException { super(connectString, sessionTimeout, watcher); } @Override ClientCnxn createConnection( String chrootPath, HostProvider hostProvider, int sessionTimeout, ZKClientConfig clientConfig, Watcher defaultWatcher, ClientCnxnSocket clientCnxnSocket, boolean canBeReadOnly ) throws IOException { return new CustomClientCnxn( chrootPath, hostProvider, sessionTimeout, clientConfig, defaultWatcher, clientCnxnSocket, canBeReadOnly); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_CustomHos0100644 0000000 0000000 00000000160 15051152474 032660 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/CustomHostProviderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/CustomHostProviderTest.ja0100644 0000000 0000000 00000006020 15051152474 034322 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; public class CustomHostProviderTest extends ZKTestCase { private AtomicInteger counter = new AtomicInteger(3); private class SpecialHostProvider implements HostProvider { // ignores its connectstring, and next() always returns localhost:2181 // it will count down when updateServerList() is called @Override public int size() { return 1; } @Override public InetSocketAddress next(long spinDelay) { return new InetSocketAddress("127.0.0.1", 2181); } @Override public void onConnected() { } @Override public boolean updateServerList(Collection serverAddresses, InetSocketAddress currentHost) { counter.decrementAndGet(); return false; } } @Test public void testZooKeeperWithCustomHostProvider() throws IOException, InterruptedException { final int CLIENT_PORT = PortAssignment.unique(); final HostProvider specialHostProvider = new SpecialHostProvider(); int expectedCounter = 3; counter.set(expectedCounter); ZooKeeper zkDefaults = new ZooKeeper( "127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE, false); ZooKeeper zkSpecial = new ZooKeeper( "127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE, false, specialHostProvider); assertTrue(counter.get() == expectedCounter); zkDefaults.updateServerList("127.0.0.1:" + PortAssignment.unique()); assertTrue(counter.get() == expectedCounter); zkSpecial.updateServerList("127.0.0.1:" + PortAssignment.unique()); expectedCounter--; assertTrue(counter.get() == expectedCounter); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/DummyWatcher.java0100644 0000000 0000000 00000002217 15051152474 032603 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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; /** * A dummy implementation of {@link Watcher}. Used in tests. */ public class DummyWatcher implements Watcher { public static final DummyWatcher INSTANCE = new DummyWatcher(); @Override public void process(WatchedEvent event) { // no op } private DummyWatcher() { // singleton } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_EnforceAu0100644 0000000 0000000 00000000163 15051152474 032606 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/EnforceAuthenticationTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/EnforceAuthenticationTest0100644 0000000 0000000 00000026322 15051152474 034376 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.server.AuthenticationHelper; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.ZooKeeperServerMain; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class EnforceAuthenticationTest extends QuorumPeerTestBase { protected static final Logger LOG = LoggerFactory.getLogger(EnforceAuthenticationTest.class); private Servers servers; private int clientPort; @Before public void setUp() { System.setProperty("zookeeper.admin.enableServer", "false"); System.setProperty("zookeeper.4lw.commands.whitelist", "*"); System.clearProperty(AuthenticationHelper.ENFORCE_AUTH_ENABLED); System.clearProperty(AuthenticationHelper.ENFORCE_AUTH_SCHEMES); } @After public void tearDown() throws InterruptedException { if (servers != null) { servers.shutDownAllServers(); } System.clearProperty(AuthenticationHelper.ENFORCE_AUTH_ENABLED); System.clearProperty(AuthenticationHelper.ENFORCE_AUTH_SCHEMES); } /** * When AuthenticationHelper.ENFORCE_AUTH_ENABLED is not set or set to false, behaviour should * be same as the old ie. clients without authentication are allowed to do operations */ @Test public void testEnforceAuthenticationOldBehaviour() throws Exception { Map prop = new HashMap<>(); startServer(prop); testEnforceAuthOldBehaviour(false); } @Test public void testEnforceAuthenticationOldBehaviourWithNetty() throws Exception { Map prop = new HashMap<>(); //setting property false should give the same behaviour as when property is not set prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_ENABLED), "false"); prop.put("serverCnxnFactory", "org.apache.zookeeper.server.NettyServerCnxnFactory"); startServer(prop); testEnforceAuthOldBehaviour(true); } private void testEnforceAuthOldBehaviour(boolean netty) throws Exception { ZKClientConfig config = new ZKClientConfig(); if (netty) { config.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); } ZooKeeper client = ClientBase .createZKClient("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT, config); String path = "/defaultAuth" + System.currentTimeMillis(); String data = "someData"; client.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); byte[] data1 = client.getData(path, false, null); assertEquals(data, new String(data1)); client.close(); } /** * Server start should fail when ZooKeeperServer.ENFORCE_AUTH_ENABLED is set to true but * AuthenticationHelper.ENFORCE_AUTH_SCHEME is not configured */ @Test public void testServerStartShouldFailWhenEnforceAuthSchemeIsNotConfigured() throws Exception { Map prop = new HashMap<>(); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_ENABLED), "true"); testServerCannotStart(prop); } /** * Server start should fail when AuthenticationHelper.ENFORCE_AUTH_ENABLED is set to true, * AuthenticationHelper.ENFORCE_AUTH_SCHEME is configured but authentication provider is not * configured. */ @Test public void testServerStartShouldFailWhenAuthProviderIsNotConfigured() throws Exception { Map prop = new HashMap<>(); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_ENABLED), "true"); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_SCHEMES), "sasl"); testServerCannotStart(prop); } private void testServerCannotStart(Map prop) throws Exception { File confFile = getConfFile(prop); ServerConfig config = new ServerConfig(); config.parse(confFile.toString()); ZooKeeperServerMain serverMain = new ZooKeeperServerMain(); try { serverMain.runFromConfig(config); fail("IllegalArgumentException is expected."); } catch (IllegalArgumentException e) { //do nothing } } @Test public void testEnforceAuthenticationNewBehaviour() throws Exception { Map prop = new HashMap<>(); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_ENABLED), "true"); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_SCHEMES), "digest"); //digest auth provider is started by default, so no need to //prop.put("authProvider.1", DigestAuthenticationProvider.class.getName()); startServer(prop); testEnforceAuthNewBehaviour(false); } @Test public void testEnforceAuthenticationNewBehaviourWithNetty() throws Exception { Map prop = new HashMap<>(); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_ENABLED), "true"); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_SCHEMES), "digest"); prop.put("serverCnxnFactory", "org.apache.zookeeper.server.NettyServerCnxnFactory"); startServer(prop); testEnforceAuthNewBehaviour(true); } /** * Client operations are allowed only after the authentication is done */ private void testEnforceAuthNewBehaviour(boolean netty) throws Exception { ZKClientConfig config = new ZKClientConfig(); if (netty) { config.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); } CountDownLatch countDownLatch = new CountDownLatch(1); ZooKeeper client = new ZooKeeper("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT, getWatcher(countDownLatch), config); countDownLatch.await(); String path = "/newAuth" + System.currentTimeMillis(); String data = "someData"; //try without authentication try { client.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("SessionClosedRequireAuthException is expected."); } catch (KeeperException.SessionClosedRequireAuthException e) { //do nothing } client.close(); countDownLatch = new CountDownLatch(1); client = new ZooKeeper("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT, getWatcher(countDownLatch), config); countDownLatch.await(); // try operations after authentication String idPassword = "user1:pass1"; client.addAuthInfo("digest", idPassword.getBytes()); client.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); byte[] data1 = client.getData(path, false, null); assertEquals(data, new String(data1)); client.close(); } @Test public void testEnforceAuthenticationWithMultipleAuthSchemes() throws Exception { Map prop = new HashMap<>(); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_ENABLED), "true"); prop.put(removeZooKeeper(AuthenticationHelper.ENFORCE_AUTH_SCHEMES), "digest,ip"); startServer(prop); ZKClientConfig config = new ZKClientConfig(); CountDownLatch countDownLatch = new CountDownLatch(1); ZooKeeper client = new ZooKeeper("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT, getWatcher(countDownLatch), config); countDownLatch.await(); // try operation without adding auth info, it should be success as ip auth info is // added automatically by server String path = "/newAuth" + System.currentTimeMillis(); String data = "someData"; client.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); byte[] data1 = client.getData(path, false, null); assertEquals(data, new String(data1)); client.close(); } private String removeZooKeeper(String prop) { return prop.replace("zookeeper.", ""); } private File getConfFile(Map additionalProp) throws IOException { clientPort = PortAssignment.unique(); StringBuilder sb = new StringBuilder(); sb.append("standaloneEnabled=true" + "\n"); if (null != additionalProp) { for (Map.Entry entry : additionalProp.entrySet()) { sb.append(entry.getKey()); sb.append("="); sb.append(entry.getValue()); sb.append("\n"); } } String currentQuorumCfgSection = sb.toString(); return new MainThread(1, clientPort, currentQuorumCfgSection, false).getConfFile(); } private void startServer(Map additionalProp) throws Exception { additionalProp.put("standaloneEnabled", "true"); servers = LaunchServers(1, additionalProp); clientPort = servers.clientPorts[0]; } private Watcher getWatcher(CountDownLatch countDownLatch) { return event -> { Event.EventType type = event.getType(); if (type == Event.EventType.None) { Event.KeeperState state = event.getState(); if (state == Event.KeeperState.SyncConnected) { LOG.info("Event.KeeperState.SyncConnected"); countDownLatch.countDown(); } else if (state == Event.KeeperState.Expired) { LOG.info("Event.KeeperState.Expired"); } else if (state == Event.KeeperState.Disconnected) { LOG.info("Event.KeeperState.Disconnected"); } else if (state == Event.KeeperState.AuthFailed) { LOG.info("Event.KeeperState.AuthFailed"); } } }; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_GetAllChi0100644 0000000 0000000 00000000162 15051152474 032532 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/GetAllChildrenNumberTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/GetAllChildrenNumberTest.0100644 0000000 0000000 00000010101 15051152474 034151 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class GetAllChildrenNumberTest extends ClientBase { private static final String BASE = "/getAllChildrenNumberTest"; private static final String BASE_EXT = BASE + "EXT"; private static final int PERSISTENT_CNT = 2; private static final int EPHEMERAL_CNT = 3; private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); generatePaths(PERSISTENT_CNT, EPHEMERAL_CNT); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); zk.close(); } @Test public void testGetAllChildrenNumberSync() throws KeeperException, InterruptedException { //a bad case try { zk.getAllChildrenNumber(null); fail("the path for getAllChildrenNumber must not be null."); } catch (IllegalArgumentException e) { //expected } assertEquals(EPHEMERAL_CNT, zk.getAllChildrenNumber(BASE + "/0")); assertEquals(0, zk.getAllChildrenNumber(BASE + "/0/ephem0")); assertEquals(0, zk.getAllChildrenNumber(BASE_EXT)); assertEquals(PERSISTENT_CNT + PERSISTENT_CNT * EPHEMERAL_CNT, zk.getAllChildrenNumber(BASE)); // 6(EPHEMERAL) + 2(PERSISTENT) + 3("/zookeeper,/zookeeper/quota,/zookeeper/config") + 1(BASE_EXT) + 1(BASE) = 13 assertEquals(13, zk.getAllChildrenNumber("/")); } @Test public void testGetAllChildrenNumberAsync() throws IOException, KeeperException, InterruptedException { final CountDownLatch doneProcessing = new CountDownLatch(1); zk.getAllChildrenNumber("/", (rc, path, ctx, number) -> { if (path == null) { fail((String.format("the path of getAllChildrenNumber was null."))); } assertEquals(13, number); doneProcessing.countDown(); }, null); long waitForCallbackSecs = 2L; if (!doneProcessing.await(waitForCallbackSecs, TimeUnit.SECONDS)) { fail(String.format("getAllChildrenNumber didn't callback within %d seconds", waitForCallbackSecs)); } } private void generatePaths(int persistantCnt, int ephemeralCnt) throws KeeperException, InterruptedException { zk.create(BASE, BASE.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(BASE_EXT, BASE_EXT.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int p = 0; p < persistantCnt; p++) { String base = BASE + "/" + p; zk.create(base, base.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int e = 0; e < ephemeralCnt; e++) { String ephem = base + "/ephem" + e; zk.create(ephem, ephem.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/GetEphemeralsTest.java0100644 0000000 0000000 00000021022 15051152474 033552 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class GetEphemeralsTest extends ClientBase { private static final String BASE = "/base"; private static final int PERSISTENT_CNT = 2; private static final int EPHEMERAL_CNT = 2; private static final String NEWLINE = System.getProperty("line.separator"); private String[] expected; private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); expected = generatePaths(PERSISTENT_CNT, EPHEMERAL_CNT); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); zk.close(); } @Test public void testGetEphemeralsSync() throws KeeperException, InterruptedException { List actual = zk.getEphemerals(); assertEquals(actual.size(), expected.length, "Expected ephemeral count for allPaths"); for (int i = 0; i < expected.length; i++) { String path = expected[i]; assertTrue(actual.contains(path), String.format("Path=%s exists in get All Ephemerals list ", path)); } } @Test public void testGetEphemeralsSyncByPath() throws KeeperException, InterruptedException { final String prefixPath = BASE + 0; List actual = zk.getEphemerals(prefixPath); assertEquals(actual.size(), EPHEMERAL_CNT, "Expected ephemeral count for allPaths"); for (int i = 0; i < EPHEMERAL_CNT; i++) { String path = expected[i]; assertTrue(actual.contains(path), String.format("Path=%s exists in getEphemerals(%s) list ", path, prefixPath)); } } @Test public void testGetEphemerals() throws IOException, KeeperException, InterruptedException { final CountDownLatch doneProcessing = new CountDownLatch(1); final List unexpectedBehavior = new ArrayList<>(); zk.getEphemerals((rc, ctx, paths) -> { if (paths == null) { unexpectedBehavior.add(String.format("Expected ephemeral count for" + " allPaths to be %d but was null", expected.length)); } else if (paths.size() != expected.length) { unexpectedBehavior.add(String.format("Expected ephemeral count for allPaths to be %d but was %d", expected.length, paths.size())); } for (int i = 0; i < expected.length; i++) { String path = expected[i]; if (!paths.contains(path)) { unexpectedBehavior.add(String.format("Path=%s exists in getEphemerals list ", path)); } } doneProcessing.countDown(); }, null); long waitForCallbackSecs = 2L; if (!doneProcessing.await(waitForCallbackSecs, TimeUnit.SECONDS)) { fail(String.format("getEphemerals didn't callback within %d seconds", waitForCallbackSecs)); } checkForUnexpectedBehavior(unexpectedBehavior); } @Test public void testGetEphemeralsByPath() throws IOException, KeeperException, InterruptedException { final CountDownLatch doneProcessing = new CountDownLatch(1); final String checkPath = BASE + "0"; final List unexpectedBehavior = new ArrayList<>(); zk.getEphemerals(checkPath, (rc, ctx, paths) -> { if (paths == null) { unexpectedBehavior.add(String.format("Expected ephemeral count for %s to be %d but was null", checkPath, expected.length)); } else if (paths.size() != EPHEMERAL_CNT) { unexpectedBehavior.add(String.format("Expected ephemeral count for %s to be %d but was %d", checkPath, EPHEMERAL_CNT, paths.size())); } for (int i = 0; i < EPHEMERAL_CNT; i++) { String path = expected[i]; if (!paths.contains(path)) { unexpectedBehavior.add(String.format("Expected path=%s didn't exist " + "in getEphemerals list.", path)); } } doneProcessing.countDown(); }, null); long waitForCallbackSecs = 2L; if (!doneProcessing.await(waitForCallbackSecs, TimeUnit.SECONDS)) { fail(String.format("getEphemerals(%s) didn't callback within %d seconds", checkPath, waitForCallbackSecs)); } checkForUnexpectedBehavior(unexpectedBehavior); } @Test public void testGetEphemeralsEmpty() throws IOException, KeeperException, InterruptedException { final CountDownLatch doneProcessing = new CountDownLatch(1); final String checkPath = "/unknownPath"; final int expectedSize = 0; final List unexpectedBehavior = new ArrayList<>(); zk.getEphemerals(checkPath, (rc, ctx, paths) -> { if (paths == null) { unexpectedBehavior.add(String.format("Expected ephemeral count for %s to be %d but was null", checkPath, expectedSize)); } else if (paths.size() != expectedSize) { unexpectedBehavior.add(String.format("Expected ephemeral count for %s to be %d but was %d", checkPath, expectedSize, paths.size())); } doneProcessing.countDown(); }, null); long waitForCallbackSecs = 2L; if (!doneProcessing.await(waitForCallbackSecs, TimeUnit.SECONDS)) { fail(String.format("getEphemerals(%s) didn't callback within %d seconds", checkPath, waitForCallbackSecs)); } checkForUnexpectedBehavior(unexpectedBehavior); } @Test public void testGetEphemeralsErrors() throws KeeperException { try { zk.getEphemerals(null, null, null); fail("Should have thrown a IllegalArgumentException for a null prefixPath"); } catch (IllegalArgumentException e) { //pass } try { zk.getEphemerals("no leading slash", null, null); fail("Should have thrown a IllegalArgumentException " + "for a prefix with no leading slash"); } catch (IllegalArgumentException e) { //pass } } private String[] generatePaths(int persistantCnt, int ephemeralCnt) throws KeeperException, InterruptedException { final String[] expected = new String[persistantCnt * ephemeralCnt]; for (int p = 0; p < persistantCnt; p++) { String base = BASE + p; zk.create(base, base.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int e = 0; e < ephemeralCnt; e++) { String ephem = base + "/ephem" + e; zk.create(ephem, ephem.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); expected[p * ephemeralCnt + e] = ephem; } } return expected; } private void checkForUnexpectedBehavior(List unexpectedBehavior) { if (unexpectedBehavior.size() > 0) { StringBuilder b = new StringBuilder("The test failed for the following reasons:"); b.append(NEWLINE); for (String error : unexpectedBehavior) { b.append("ERROR: ").append(error).append(NEWLINE); } fail(b.toString()); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/JaasConfiguration.java0100644 0000000 0000000 00000005326 15051152474 033604 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.util.HashMap; import java.util.Map; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; /** * This helper class allows to programmatically create a JAAS configuration. * Each section must have a name and a login module, and a set of key/values * to describe login options. * * Example: * jaas = new JaasConfiguration(); * jaas.addSection("Server", "org.apache.zookeeper.server.auth.DigestLoginModule", * "username", "passowrd"); */ public class JaasConfiguration extends javax.security.auth.login.Configuration { private final Map sections = new HashMap<>(); public JaasConfiguration() { } /** * Add a section to the jaas.conf * @param name Section name * @param loginModuleName Login module name * @param args login key/value args */ public void addSection(String name, String loginModuleName, String... args) { Map conf = new HashMap<>(); // loop through the args (must be key/value sequence) for (int i = 0; i < args.length - 1; i += 2) { conf.put(args[i], args[i + 1]); } addSection(name, loginModuleName, conf); } /** * Add a section to the jaas.conf * @param name Section name * @param loginModuleName Login module name * @param conf login key/value args */ public void addSection(String name, String loginModuleName, final Map conf) { AppConfigurationEntry[] entries = new AppConfigurationEntry[1]; entries[0] = new AppConfigurationEntry(loginModuleName, LoginModuleControlFlag.REQUIRED, conf); this.sections.put(name, entries); } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String appName) { return sections.get(appName); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_KerberosT0100644 0000000 0000000 00000000163 15051152474 032637 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/KerberosTicketRenewalTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/KerberosTicketRenewalTest0100644 0000000 0000000 00000023157 15051152474 034356 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.apache.zookeeper.server.quorum.auth.MiniKdc.MAX_TICKET_LIFETIME; import static org.apache.zookeeper.server.quorum.auth.MiniKdc.MIN_TICKET_LIFETIME; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTimeout; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.security.Principal; import java.time.Duration; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.quorum.auth.KerberosTestUtils; import org.apache.zookeeper.server.quorum.auth.MiniKdc; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This test class is mainly testing the TGT renewal logic implemented * in the org.apache.zookeeper.Login class. */ public class KerberosTicketRenewalTest { private static final Logger LOG = LoggerFactory.getLogger(KerberosTicketRenewalTest.class); private static final String JAAS_CONFIG_SECTION = "ClientUsingKerberos"; private static final String TICKET_LIFETIME = "5000"; private static File testTempDir; private static MiniKdc kdc; private static File kdcWorkDir; private static String PRINCIPAL = KerberosTestUtils.getClientPrincipal(); TestableKerberosLogin login; @BeforeAll public static void setupClass() throws Exception { // by default, we should wait at least 1 minute between subsequent TGT renewals. // changing it to 500ms. System.setProperty(Login.MIN_TIME_BEFORE_RELOGIN_CONFIG_KEY, "500"); testTempDir = ClientBase.createTmpDir(); startMiniKdcAndAddPrincipal(); String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true); // note: we use "refreshKrb5Config=true" to refresh the kerberos config in the JVM, // making sure that we use the latest config even if other tests already have been executed // and initialized the kerberos client configs before) String jaasEntries = "" + "ClientUsingKerberos {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " storeKey=\"false\"\n" + " useTicketCache=\"false\"\n" + " useKeyTab=\"true\"\n" + " doNotPrompt=\"true\"\n" + " debug=\"true\"\n" + " refreshKrb5Config=\"true\"\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " principal=\"" + PRINCIPAL + "\";\n" + "};\n"; setupJaasConfig(jaasEntries); } @AfterAll public static void tearDownClass() { System.clearProperty(Login.MIN_TIME_BEFORE_RELOGIN_CONFIG_KEY); System.clearProperty("java.security.auth.login.config"); stopMiniKdc(); if (testTempDir != null) { // the testTempDir contains the jaas config file and also the // working folder of the currently running KDC server FileUtils.deleteQuietly(testTempDir); } } @AfterEach public void tearDownTest() throws Exception { if (login != null) { login.shutdown(); login.logout(); } } /** * We extend the regular Login class to be able to properly control the * "sleeping" between the retry attempts of ticket refresh actions. */ private static class TestableKerberosLogin extends Login { private AtomicBoolean refreshFailed = new AtomicBoolean(false); private CountDownLatch continueRefreshThread = new CountDownLatch(1); public TestableKerberosLogin() throws LoginException { super(JAAS_CONFIG_SECTION, () -> { return (callbacks) -> {}; }, new ZKConfig()); } @Override protected void sleepBeforeRetryFailedRefresh() throws InterruptedException { LOG.info("sleep started due to failed refresh"); refreshFailed.set(true); continueRefreshThread.await(20, TimeUnit.SECONDS); LOG.info("sleep due to failed refresh finished"); } public void assertRefreshFailsEventually(Duration timeout) { assertEventually(timeout, () -> refreshFailed.get()); } public void continueWithRetryAfterFailedRefresh() { LOG.info("continue refresh thread"); continueRefreshThread.countDown(); } } @Test public void shouldLoginUsingKerberos() throws Exception { login = new TestableKerberosLogin(); login.startThreadIfNeeded(); assertPrincipalLoggedIn(); } @Test public void shouldRenewTicketUsingKerberos() throws Exception { login = new TestableKerberosLogin(); login.startThreadIfNeeded(); long initialLoginTime = login.getLastLogin(); // ticket lifetime is 5sec, so we will trigger ticket renewal in each ~2-3 sec assertTicketRefreshHappenedUntil(Duration.ofSeconds(15)); assertPrincipalLoggedIn(); assertTrue(initialLoginTime < login.getLastLogin()); } @Test public void shouldRecoverIfKerberosNotAvailableForSomeTime() throws Exception { login = new TestableKerberosLogin(); login.startThreadIfNeeded(); assertTicketRefreshHappenedUntil(Duration.ofSeconds(15)); stopMiniKdc(); // ticket lifetime is 5sec, so we will trigger ticket renewal in each ~2-3 sec // the very next ticket renewal should fail (as KDC is offline) login.assertRefreshFailsEventually(Duration.ofSeconds(15)); // now the ticket thread is "sleeping", it will retry the refresh later // we restart KDC, then terminate the "sleeping" and expecting // that the next retry should succeed startMiniKdcAndAddPrincipal(); login.continueWithRetryAfterFailedRefresh(); assertTicketRefreshHappenedUntil(Duration.ofSeconds(15)); assertPrincipalLoggedIn(); } private void assertPrincipalLoggedIn() { assertEquals(PRINCIPAL, login.getUserName()); assertNotNull(login.getSubject()); assertEquals(1, login.getSubject().getPrincipals().size()); Principal actualPrincipal = login.getSubject().getPrincipals().iterator().next(); assertEquals(PRINCIPAL, actualPrincipal.getName()); } private void assertTicketRefreshHappenedUntil(Duration timeout) { long lastLoginTime = login.getLastLogin(); assertEventually(timeout, () -> login.getLastLogin() != lastLoginTime && login.getSubject() != null && !login.getSubject().getPrincipals().isEmpty()); } private static void assertEventually(Duration timeout, Supplier test) { assertTimeout(timeout, () -> { while (true) { if (test.get()) { return; } Thread.sleep(100); } }); } public static void startMiniKdcAndAddPrincipal() throws Exception { kdcWorkDir = createTmpDirInside(testTempDir); Properties conf = MiniKdc.createConf(); conf.setProperty(MAX_TICKET_LIFETIME, TICKET_LIFETIME); conf.setProperty(MIN_TICKET_LIFETIME, TICKET_LIFETIME); kdc = new MiniKdc(conf, kdcWorkDir); kdc.start(); String principalName = PRINCIPAL.substring(0, PRINCIPAL.lastIndexOf("@")); kdc.createPrincipal(new File(KerberosTestUtils.getKeytabFile()), principalName); } private static void stopMiniKdc() { if (kdc != null) { kdc.stop(); kdc = null; } if (kdcWorkDir != null) { FileUtils.deleteQuietly(kdcWorkDir); kdcWorkDir = null; } } private static File createTmpDirInside(File parentDir) throws IOException { File tmpFile = File.createTempFile("test", ".junit", parentDir); // don't delete tmpFile - this ensures we don't attempt to create // a tmpDir with a duplicate name File tmpDir = new File(tmpFile + ".dir"); // never true if tmpfile does it's job assertFalse(tmpDir.exists()); assertTrue(tmpDir.mkdirs()); return tmpDir; } private static void setupJaasConfig(String jaasEntries) { try { File saslConfFile = new File(testTempDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write(jaasEntries); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException ioe) { LOG.error("Failed to initialize JAAS conf file", ioe); } // refresh the SASL configuration in this JVM (making sure that we use the latest config // even if other tests already have been executed and initialized the SASL configs before) Configuration.getConfiguration().refresh(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/MockPacket.java0100644 0000000 0000000 00000002706 15051152474 032216 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.nio.ByteBuffer; import org.apache.jute.Record; import org.apache.zookeeper.ZooKeeper.WatchRegistration; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; public class MockPacket extends ClientCnxn.Packet { public MockPacket( RequestHeader requestHeader, ReplyHeader replyHeader, Record request, Record response, WatchRegistration watchRegistration) { super(requestHeader, replyHeader, request, response, watchRegistration); } public ByteBuffer createAndReturnBB() { createBB(); return this.bb; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_MultiOper0100644 0000000 0000000 00000000162 15051152474 032656 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/MultiOperationRecordTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/MultiOperationRecordTest.0100644 0000000 0000000 00000005374 15051152474 034311 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.server.ByteBufferInputStream; import org.junit.jupiter.api.Test; public class MultiOperationRecordTest extends ZKTestCase { @Test public void testRoundTrip() throws IOException { MultiOperationRecord request = new MultiOperationRecord(); request.add(Op.check("check", 1)); request.add(Op.create("create", "create data".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL.toFlag())); request.add(Op.delete("delete", 17)); request.add(Op.setData("setData", "set data".getBytes(), 19)); MultiOperationRecord decodedRequest = codeDecode(request); assertEquals(request, decodedRequest); assertEquals(request.hashCode(), decodedRequest.hashCode()); } @Test public void testEmptyRoundTrip() throws IOException { MultiOperationRecord request = new MultiOperationRecord(); MultiOperationRecord decodedRequest = codeDecode(request); assertEquals(request, decodedRequest); assertEquals(request.hashCode(), decodedRequest.hashCode()); } private MultiOperationRecord codeDecode(MultiOperationRecord request) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); request.serialize(boa, "request"); baos.close(); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); bb.rewind(); BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb)); MultiOperationRecord decodedRequest = new MultiOperationRecord(); decodedRequest.deserialize(bia, "request"); return decodedRequest; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/MultiResponseTest.java0100644 0000000 0000000 00000005264 15051152474 033650 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ByteBufferInputStream; import org.junit.jupiter.api.Test; public class MultiResponseTest extends ZKTestCase { public void testRoundTrip() throws IOException { MultiResponse response = new MultiResponse(); response.add(new OpResult.CheckResult()); response.add(new OpResult.CreateResult("foo-bar")); response.add(new OpResult.DeleteResult()); Stat s = new Stat(); s.setCzxid(546); response.add(new OpResult.SetDataResult(s)); MultiResponse decodedResponse = codeDecode(response); assertEquals(response, decodedResponse); assertEquals(response.hashCode(), decodedResponse.hashCode()); } @Test public void testEmptyRoundTrip() throws IOException { MultiResponse result = new MultiResponse(); MultiResponse decodedResult = codeDecode(result); assertEquals(result, decodedResult); assertEquals(result.hashCode(), decodedResult.hashCode()); } private MultiResponse codeDecode(MultiResponse request) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); request.serialize(boa, "result"); baos.close(); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); bb.rewind(); BinaryInputArchive bia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb)); MultiResponse decodedRequest = new MultiResponse(); decodedRequest.deserialize(bia, "result"); return decodedRequest; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignment.java0100644 0000000 0000000 00000021147 15051152474 033152 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.net.ServerSocket; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** Assign ports to tests */ public final class PortAssignment { private static final Logger LOG = LoggerFactory.getLogger(PortAssignment.class); // The available port range that we use stays away from the ephemeral port // range, which the OS will assign to client socket connections. We can't // coordinate with the OS on the assignment of those ports, so it's best to // stay out of that range to avoid conflicts. Typical ranges for ephemeral // ports are: // - IANA suggests 49152 - 65535 // - Linux typically uses 32768 - 61000 // - FreeBSD modern versions typically use the IANA suggested range // - Windows modern versions typically use the IANA suggested range private static final int GLOBAL_BASE_PORT = 11221; private static final int GLOBAL_MAX_PORT = 32767; private static PortRange portRange = null; private static int nextPort; /** * Assign a new, unique port to the test. This method works by assigning * ports from a valid port range as identified by the total number of * concurrent test processes and the ID of this test process. Each * concurrent test process uses an isolated range, so it's not possible for * multiple test processes to collide on the same port. Within the port * range, ports are assigned in monotonic increasing order, wrapping around * to the beginning of the range if needed. As an extra precaution, the * method attempts to bind to the port and immediately close it before * returning it to the caller. If the port cannot be bound, then it tries * the next one in the range. This provides some resiliency in case the port * is otherwise occupied, such as a developer running other servers on the * machine running the tests. * * @return port */ public static synchronized int unique() { if (portRange == null) { Integer threadId = Integer.getInteger("zookeeper.junit.threadid"); portRange = setupPortRange( System.getProperty("test.junit.threads"), threadId != null ? "threadid=" + threadId : System.getProperty("sun.java.command")); nextPort = portRange.getMinimum(); } int candidatePort = nextPort; for (; ; ) { ++candidatePort; if (candidatePort > portRange.getMaximum()) { candidatePort = portRange.getMinimum(); } if (candidatePort == nextPort) { throw new IllegalStateException(String.format( "Could not assign port from range %s. The entire range has been exhausted.", portRange)); } try { ServerSocket s = new ServerSocket(candidatePort); s.close(); nextPort = candidatePort; LOG.info("Assigned port {} from range {}.", nextPort, portRange); return nextPort; } catch (IOException e) { LOG.debug( "Could not bind to port {} from range {}. Attempting next port.", candidatePort, portRange, e); } } } /** * Sets up the port range to be used. In typical usage, Ant invokes JUnit, * possibly using multiple JUnit processes to execute multiple test suites * concurrently. The count of JUnit processes is passed from Ant as a system * property named "test.junit.threads". Ant's JUnit runner receives the * thread ID as a command line argument of the form threadid=N, where N is an * integer in the range [1, ${test.junit.threads}]. It's not otherwise * accessible, so we need to parse it from the command line. This method * uses these 2 pieces of information to split the available ports into * disjoint ranges. Each JUnit process only assigns ports from its own range * in order to prevent bind errors during concurrent test runs. If any of * this information is unavailable or unparseable, then the default behavior * is for this process to use the entire available port range. This is * expected when running tests outside of Ant. * * @param strProcessCount string representation of integer process count, * typically taken from system property test.junit.threads * @param cmdLine command line containing threadid=N argument, typically * taken from system property sun.java.command * @return port range to use */ static PortRange setupPortRange(String strProcessCount, String cmdLine) { Integer processCount = null; if (strProcessCount != null && !strProcessCount.isEmpty()) { try { processCount = Integer.valueOf(strProcessCount); } catch (NumberFormatException e) { LOG.warn("Error parsing test.junit.threads = {}.", strProcessCount, e); } } Integer threadId = null; if (processCount != null) { if (cmdLine != null && !cmdLine.isEmpty()) { Matcher m = Pattern.compile("threadid=(\\d+)").matcher(cmdLine); if (m.find()) { try { threadId = Integer.valueOf(m.group(1)); } catch (NumberFormatException e) { LOG.warn("Error parsing threadid from {}.", cmdLine, e); } } } } final PortRange newPortRange; if (processCount != null && processCount > 1 && threadId != null) { // We know the total JUnit process count and this test process's ID. // Use these values to calculate the valid range for port assignments // within this test process. We lose a few possible ports to the // remainder, but that's acceptable. int portRangeSize = (GLOBAL_MAX_PORT - GLOBAL_BASE_PORT) / processCount; int minPort = GLOBAL_BASE_PORT + ((threadId - 1) * portRangeSize); int maxPort = minPort + portRangeSize - 1; newPortRange = new PortRange(minPort, maxPort); LOG.info("Test process {}/{} using ports from {}.", threadId, processCount, newPortRange); } else { // If running outside the context of Ant or Ant is using a single // test process, then use all valid ports. newPortRange = new PortRange(GLOBAL_BASE_PORT, GLOBAL_MAX_PORT); LOG.info("Single test process using ports from {}.", newPortRange); } return newPortRange; } /** * Contains the minimum and maximum (both inclusive) in a range of ports. */ static final class PortRange { private final int minimum; private final int maximum; /** * Creates a new PortRange. * * @param minimum lower bound port number * @param maximum upper bound port number */ PortRange(int minimum, int maximum) { this.minimum = minimum; this.maximum = maximum; } /** * Returns maximum port in the range. * * @return maximum */ int getMaximum() { return maximum; } /** * Returns minimum port in the range. * * @return minimum */ int getMinimum() { return minimum; } @Override public String toString() { return String.format("%d - %d", minimum, maximum); } } /** * There is no reason to instantiate this class. */ private PortAssignment() { } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/PortAssignmentTest.java0100644 0000000 0000000 00000005345 15051152474 034014 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class PortAssignmentTest { public static Stream data() throws Exception { return Stream.of( Arguments.of("8", "threadid=1", 11221, 13913), Arguments.of("8", "threadid=2", 13914, 16606), Arguments.of("8", "threadid=3", 16607, 19299), Arguments.of("8", "threadid=4", 19300, 21992), Arguments.of("8", "threadid=5", 21993, 24685), Arguments.of("8", "threadid=6", 24686, 27378), Arguments.of("8", "threadid=7", 27379, 30071), Arguments.of("8", "threadid=8", 30072, 32764), Arguments.of("1", "threadid=1", 11221, 32767), Arguments.of("2", "threadid=1", 11221, 21993), Arguments.of("2", "threadid=2", 21994, 32766), Arguments.of(null, null, 11221, 32767), Arguments.of("", "", 11221, 32767)); } @ParameterizedTest @MethodSource("data") public void testSetupPortRange(String strProcessCount, String cmdLine, int expectedMinimumPort, int expectedMaximumPort) { PortAssignment.PortRange portRange = PortAssignment.setupPortRange(strProcessCount, cmdLine); assertEquals(expectedMinimumPort, portRange.getMinimum(), buildAssertionMessage("minimum", strProcessCount, cmdLine)); assertEquals(expectedMaximumPort, portRange.getMaximum(), buildAssertionMessage("maximum", strProcessCount, cmdLine)); } private String buildAssertionMessage(String checkType, String strProcessCount, String cmdLine) { return String.format("strProcessCount = %s, cmdLine = %s, checking %s", strProcessCount, cmdLine, checkType); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_RemoveWat0100644 0000000 0000000 00000000156 15051152474 032652 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesCmdTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesCmdTest.java0100644 0000000 0000000 00000031363 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Testing remove watches using command line */ public class RemoveWatchesCmdTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(RemoveWatchesCmdTest.class); private ZooKeeper zk; private ZooKeeperMain zkMain; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); zkMain = new ZooKeeperMain(zk); } @AfterEach @Override public void tearDown() throws Exception { if (zk != null) { zk.close(); } super.tearDown(); } /** * Test verifies default options. When there is no passed options, * removewatches command will use default options - WatcherType.ANY and * local=false */ @Test @Timeout(value = 30) public void testRemoveWatchesWithNoPassedOptions() throws Exception { List expectedEvents = new ArrayList<>(); expectedEvents.add(EventType.ChildWatchRemoved); expectedEvents.add(EventType.DataWatchRemoved); MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 2); zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/testnode2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("Adding childwatcher to /testnode1 and /testnode2"); zk.getChildren("/testnode1", myWatcher); zk.getChildren("/testnode2", myWatcher); LOG.info("Adding datawatcher to /testnode1 and /testnode2"); zk.getData("/testnode1", myWatcher, null); zk.getData("/testnode2", myWatcher, null); String cmdstring = "removewatches /testnode1"; LOG.info("Remove watchers using shell command : {}", cmdstring); zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove child watches"); LOG.info("Waiting for the DataWatchRemoved event"); myWatcher.matches(); // verifying that other path child watches are not affected assertTrue(zk.getChildWatches().contains("/testnode2"), "Failed to find child watches for the path testnode2"); assertTrue(zk.getDataWatches().contains("/testnode2"), "Failed to find data watches for the path testnode2"); } /** * Test verifies deletion of NodeDataChanged watches */ @Test @Timeout(value = 30) public void testRemoveNodeDataChangedWatches() throws Exception { LOG.info("Adding data watcher using getData()"); List expectedEvents = new ArrayList<>(); expectedEvents.add(EventType.DataWatchRemoved); MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1); zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.getData("/testnode1", myWatcher, null); String cmdstring = "removewatches /testnode1 -d"; LOG.info("Remove watchers using shell command : {}", cmdstring); zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove data watches"); LOG.info("Waiting for the DataWatchRemoved event"); myWatcher.matches(); // verifying that other path data watches are removed assertEquals(0, zk.getDataWatches().size(), "Data watches are not removed : " + zk.getDataWatches()); } /** * Test verifies deletion of NodeCreated data watches */ @Test @Timeout(value = 30) public void testRemoveNodeCreatedWatches() throws Exception { List expectedEvents = new ArrayList<>(); expectedEvents.add(EventType.DataWatchRemoved); MyWatcher myWatcher1 = new MyWatcher("/testnode1", expectedEvents, 1); MyWatcher myWatcher2 = new MyWatcher("/testnode1/testnode2", expectedEvents, 1); // Adding pre-created watcher LOG.info("Adding NodeCreated watcher"); zk.exists("/testnode1", myWatcher1); zk.exists("/testnode1/testnode2", myWatcher2); String cmdstring1 = "removewatches /testnode1 -d"; LOG.info("Remove watchers using shell command : {}", cmdstring1); zkMain.cl.parseCommand(cmdstring1); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove pre-create watches"); myWatcher1.matches(); assertEquals(1, zk.getExistWatches().size(), "Failed to remove pre-create watches :" + zk.getExistWatches()); assertTrue(zk.getExistWatches().contains("/testnode1/testnode2"), "Failed to remove pre-create watches :" + zk.getExistWatches()); String cmdstring2 = "removewatches /testnode1/testnode2 -d"; LOG.info("Remove watchers using shell command : {}", cmdstring2); zkMain.cl.parseCommand(cmdstring2); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove data watches"); myWatcher2.matches(); assertEquals(0, zk.getExistWatches().size(), "Failed to remove pre-create watches : " + zk.getExistWatches()); } /** * Test verifies deletion of NodeChildrenChanged watches */ @Test @Timeout(value = 30) public void testRemoveNodeChildrenChangedWatches() throws Exception { List expectedEvents = new ArrayList<>(); expectedEvents.add(EventType.ChildWatchRemoved); MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1); zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("Adding child changed watcher"); zk.getChildren("/testnode1", myWatcher); String cmdstring = "removewatches /testnode1 -c"; LOG.info("Remove watchers using shell command : {}", cmdstring); zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove child watches"); myWatcher.matches(); assertEquals(0, zk.getChildWatches().size(), "Failed to remove child watches : " + zk.getChildWatches()); } /** * Test verifies deletion of NodeDeleted watches */ @Test @Timeout(value = 30) public void testRemoveNodeDeletedWatches() throws Exception { LOG.info("Adding NodeDeleted watcher"); List expectedEvents = new ArrayList<>(); expectedEvents.add(EventType.ChildWatchRemoved); expectedEvents.add(EventType.NodeDeleted); MyWatcher myWatcher = new MyWatcher("/testnode1", expectedEvents, 1); zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/testnode1/testnode2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.getChildren("/testnode1/testnode2", myWatcher); zk.getChildren("/testnode1", myWatcher); String cmdstring = "removewatches /testnode1 -c"; LOG.info("Remove watchers using shell command : {}", cmdstring); zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove child watches"); LOG.info("Waiting for the ChildWatchRemoved event"); myWatcher.matches(); assertEquals(1, zk.getChildWatches().size(), "Failed to remove child watches : " + zk.getChildWatches()); assertTrue(zk.getChildWatches().contains("/testnode1/testnode2"), "Failed to remove child watches :" + zk.getChildWatches()); // verify node delete watcher zk.delete("/testnode1/testnode2", -1); myWatcher.matches(); } /** * Test verifies deletion of any watches */ @Test @Timeout(value = 30) public void testRemoveAnyWatches() throws Exception { verifyRemoveAnyWatches(false); } /** * Test verifies deletion of watches locally when there is no server * connection */ @Test @Timeout(value = 30) public void testRemoveWatchesLocallyWhenNoServerConnection() throws Exception { verifyRemoveAnyWatches(true); } private void verifyRemoveAnyWatches(boolean local) throws Exception { final Map> pathVsEvent = new HashMap<>(); LOG.info("Adding NodeChildrenChanged, NodeDataChanged watchers"); final CountDownLatch watcherLatch = new CountDownLatch(2); Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { switch (event.getType()) { case ChildWatchRemoved: case DataWatchRemoved: addWatchNotifications(pathVsEvent, event); watcherLatch.countDown(); break; case NodeChildrenChanged: case NodeDataChanged: addWatchNotifications(pathVsEvent, event); break; } } private void addWatchNotifications(Map> pathVsEvent, WatchedEvent event) { pathVsEvent.computeIfAbsent(event.getPath(), k -> new ArrayList<>()) .add(event.getType()); } }; zk.create("/testnode1", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.getChildren("/testnode1", watcher); zk.getData("/testnode1", watcher, null); String cmdstring = "removewatches /testnode1 -a"; if (local) { LOG.info("Stopping ZK server to verify deletion of watches locally"); stopServer(); cmdstring = "removewatches /testnode1 -a -l"; } LOG.info("Remove watchers using shell command : {}", cmdstring); zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Removewatches cmd fails to remove child/data watches"); LOG.info("Waiting for the WatchRemoved events"); watcherLatch.await(10, TimeUnit.SECONDS); assertEquals(1, pathVsEvent.size(), "Didn't receives WatchRemoved events!"); assertTrue(pathVsEvent.get("/testnode1").contains(EventType.DataWatchRemoved), "Didn't receives DataWatchRemoved!"); assertTrue(pathVsEvent.get("/testnode1").contains(EventType.ChildWatchRemoved), "Didn't receives ChildWatchRemoved!"); } private static class MyWatcher implements Watcher { private final String path; private String eventPath; private final CountDownLatch latch; private final List expectedEvents = new ArrayList<>(); MyWatcher(String path, List expectedEvents, int count) { this.path = path; this.latch = new CountDownLatch(count); this.expectedEvents.addAll(expectedEvents); } public void process(WatchedEvent event) { LOG.debug("Event path : {}, eventPath : {}", path, event.getPath()); this.eventPath = event.getPath(); if (expectedEvents.contains(event.getType())) { latch.countDown(); } } public boolean matches() throws InterruptedException { if (!latch.await(CONNECTION_TIMEOUT / 3, TimeUnit.MILLISECONDS)) { LOG.error("Failed to get watch notifications!"); return false; } LOG.debug("Client path : {} eventPath : {}", path, eventPath); return path.equals(eventPath); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/RemoveWatchesTest.java0100644 0000000 0000000 00000177172 15051152474 033623 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.collections4.CollectionUtils; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.WatcherType; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Verifies removing watches using ZooKeeper client apis */ public class RemoveWatchesTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(RemoveWatchesTest.class); private ZooKeeper zk1 = null; private ZooKeeper zk2 = null; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk1 = createClient(); zk2 = createClient(); } @AfterEach @Override public void tearDown() throws Exception { if (zk1 != null) { zk1.close(); } if (zk2 != null) { zk2.close(); } super.tearDown(); } private void removeWatches( ZooKeeper zk, String path, Watcher watcher, WatcherType watcherType, boolean local, KeeperException.Code rc, boolean useAsync) throws InterruptedException, KeeperException { LOG.info("Sending removeWatches req using zk {} path: {} type: {} watcher: {} ", zk, path, watcherType, watcher); if (useAsync) { MyCallback c1 = new MyCallback(rc.intValue(), path); zk.removeWatches(path, watcher, watcherType, local, c1, null); assertTrue(c1.matches(), "Didn't succeeds removeWatch operation"); if (rc.intValue() != c1.rc) { throw KeeperException.create(KeeperException.Code.get(c1.rc)); } } else if (rc != Code.OK) { try { zk.removeWatches(path, watcher, watcherType, local); fail("expect exception code " + rc); } catch (KeeperException ex) { assertEquals(rc, ex.code()); assertEquals(path, ex.getPath()); } } else { zk.removeWatches(path, watcher, watcherType, local); } } private void removeAllWatches( ZooKeeper zk, String path, WatcherType watcherType, boolean local, KeeperException.Code rc, boolean useAsync) throws InterruptedException, KeeperException { LOG.info("Sending removeWatches req using zk {} path: {} type: {} ", zk, path, watcherType); if (useAsync) { MyCallback c1 = new MyCallback(rc.intValue(), path); zk.removeAllWatches(path, watcherType, local, c1, null); assertTrue(c1.matches(), "Didn't succeeds removeWatch operation"); if (rc.intValue() != c1.rc) { throw KeeperException.create(KeeperException.Code.get(c1.rc)); } } else if (rc != Code.OK) { try { zk.removeAllWatches(path, watcherType, local); fail("expect exception code " + rc); } catch (KeeperException ex) { assertEquals(rc, ex.code()); assertEquals(path, ex.getPath()); } } else { zk.removeAllWatches(path, watcherType, local); } } private void assertWatchers(ZooKeeper zk, String path, WatcherType... watcherTypes) { for (WatcherType watcherType : watcherTypes) { String msg = String.format("expect watcher for path %s and type %s", path, watcherType); assertTrue(isServerSessionWatcher(zk.getSessionId(), path, watcherType), msg); } } private void assertNoWatchers(ZooKeeper zk, String path, WatcherType... watcherTypes) { for (WatcherType watcherType : watcherTypes) { String msg = String.format("expect no watcher for path %s and type %s", path, watcherType); assertFalse(isServerSessionWatcher(zk.getSessionId(), path, watcherType), msg); } } private void assertWatchersExcept(ZooKeeper zk, String path, WatcherType... watcherTypes) { List excludes = Arrays.asList(watcherTypes); for (WatcherType watcherType : WatcherType.values()) { if (watcherType == WatcherType.Any) { continue; } if (excludes.contains(watcherType)) { assertNoWatchers(zk, path, watcherType); } else { assertWatchers(zk, path, watcherType); } } } /** * Test verifies removal of single watcher when there is server connection */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveSingleWatcher(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); zk1.create("/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); MyWatcher w1 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); MyWatcher w2 = new MyWatcher("/node2", 1); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node2", w2), "Didn't set data watches"); removeWatches(zk2, "/node1", w1, WatcherType.Data, false, Code.OK, useAsync); assertEquals(1, zk2.getDataWatches().size(), "Didn't find data watcher"); assertEquals("/node2", zk2.getDataWatches().get(0), "Didn't find data watcher"); removeWatches(zk2, "/node2", w2, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove data watcher"); // closing session should remove ephemeral nodes and trigger data // watches if any if (zk1 != null) { zk1.close(); zk1 = null; } List events = w1.getEventsAfterWatchRemoval(); assertFalse(events.contains(EventType.NodeDeleted), "Shouldn't get NodeDeletedEvent after watch removal"); assertEquals(0, events.size(), "Shouldn't get NodeDeletedEvent after watch removal"); } /** * Test verifies removal of multiple data watchers when there is server * connection */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testMultipleDataWatchers(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); MyWatcher w1 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node1", w2), "Didn't set data watches"); removeWatches(zk2, "/node1", w2, WatcherType.Data, false, Code.OK, useAsync); assertEquals(1, zk2.getDataWatches().size(), "Didn't find data watcher"); assertEquals("/node1", zk2.getDataWatches().get(0), "Didn't find data watcher"); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove data watcher"); // closing session should remove ephemeral nodes and trigger data // watches if any if (zk1 != null) { zk1.close(); zk1 = null; } List events = w2.getEventsAfterWatchRemoval(); assertEquals(0, events.size(), "Shouldn't get NodeDeletedEvent after watch removal"); } /** * Test verifies removal of multiple child watchers when there is server * connection */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testMultipleChildWatchers(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 1); LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w1); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w2); removeWatches(zk2, "/node1", w2, WatcherType.Children, false, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove child watcher"); assertEquals(1, zk2.getChildWatches().size(), "Didn't find child watcher"); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w1.matches(), "Didn't remove child watcher"); // create child to see NodeChildren notification zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // waiting for child watchers to be notified int count = 30; while (count > 0) { if (w1.getEventsAfterWatchRemoval().size() > 0) { break; } count--; Thread.sleep(100); } // watcher2 List events = w2.getEventsAfterWatchRemoval(); assertEquals(0, events.size(), "Shouldn't get NodeChildrenChanged event"); } /** * Test verifies null watcher with WatcherType.Any - remove all the watchers * data, child, exists */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllWatchers(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 2); MyWatcher w2 = new MyWatcher("/node1", 2); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node1", w2), "Didn't set data watches"); LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w1); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w2); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK, useAsync); removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK, useAsync); zk1.create("/node1/child", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertTrue(w1.matches(), "Didn't remove data watcher"); assertTrue(w2.matches(), "Didn't remove child watcher"); } /** * Test verifies null watcher with WatcherType.Data - remove all data * watchers. Child watchers shouldn't be removed */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllDataWatchers(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 1); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node1", w2), "Didn't set data watches"); LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w1); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w2); removeWatches(zk2, "/node1", w1, WatcherType.Data, false, Code.OK, useAsync); removeWatches(zk2, "/node1", w2, WatcherType.Data, false, Code.OK, useAsync); zk1.create("/node1/child", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertTrue(w1.matches(), "Didn't remove data watcher"); assertTrue(w2.matches(), "Didn't remove data watcher"); // waiting for child watchers to be notified int count = 10; while (count > 0) { if (w1.getEventsAfterWatchRemoval().size() > 0 && w2.getEventsAfterWatchRemoval().size() > 0) { break; } count--; Thread.sleep(1000); } // watcher1 List events = w1.getEventsAfterWatchRemoval(); assertEquals(1, events.size(), "Didn't get NodeChildrenChanged event"); assertTrue(events.contains(EventType.NodeChildrenChanged), "Didn't get NodeChildrenChanged event"); // watcher2 events = w2.getEventsAfterWatchRemoval(); assertEquals(1, events.size(), "Didn't get NodeChildrenChanged event"); assertTrue(events.contains(EventType.NodeChildrenChanged), "Didn't get NodeChildrenChanged event"); } /** * Test verifies null watcher with WatcherType.Children - remove all child * watchers. Data watchers shouldn't be removed */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllChildWatchers(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 1); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node1", w2), "Didn't set data watches"); LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w1); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w2); removeWatches(zk2, "/node1", w1, WatcherType.Children, false, Code.OK, useAsync); removeWatches(zk2, "/node1", w2, WatcherType.Children, false, Code.OK, useAsync); zk1.setData("/node1", "test".getBytes(), -1); assertTrue(w1.matches(), "Didn't remove child watcher"); assertTrue(w2.matches(), "Didn't remove child watcher"); // waiting for child watchers to be notified int count = 10; while (count > 0) { if (w1.getEventsAfterWatchRemoval().size() > 0 && w2.getEventsAfterWatchRemoval().size() > 0) { break; } count--; Thread.sleep(1000); } // watcher1 List events = w1.getEventsAfterWatchRemoval(); assertEquals(1, events.size(), "Didn't get NodeDataChanged event"); assertTrue(events.contains(EventType.NodeDataChanged), "Didn't get NodeDataChanged event"); // watcher2 events = w2.getEventsAfterWatchRemoval(); assertEquals(1, events.size(), "Didn't get NodeDataChanged event"); assertTrue(events.contains(EventType.NodeDataChanged), "Didn't get NodeDataChanged event"); } /** * Test verifies removing all watcher with WatcherType.Persistent. * *

    All other watchers shouldn't be removed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllPersistentWatchers(boolean useAsync) throws InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque persistentEvents1 = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents2 = new LinkedBlockingDeque<>(); Watcher persistentWatcher1 = persistentEvents1::add; Watcher persistentWatcher2 = persistentEvents2::add; zk2.addWatch("/node1", persistentWatcher1, AddWatchMode.PERSISTENT); zk2.addWatch("/node1", persistentWatcher2, AddWatchMode.PERSISTENT); BlockingDeque dataEvents = new LinkedBlockingDeque<>(); BlockingDeque childrenEvents = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents = new LinkedBlockingDeque<>(); zk2.getData("/node1", dataEvents::add, null); zk2.getChildren("/node1", childrenEvents::add); zk2.addWatch("/node1", recursiveEvents::add, AddWatchMode.PERSISTENT_RECURSIVE); removeWatches(zk2, "/node1", persistentWatcher1, WatcherType.Persistent, false, Code.OK, useAsync); removeWatches(zk2, "/node1", persistentWatcher2, WatcherType.Persistent, false, Code.OK, useAsync); removeWatches(zk2, "/node1", persistentWatcher1, WatcherType.Data, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", persistentWatcher2, WatcherType.Data, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", persistentWatcher1, WatcherType.Children, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", persistentWatcher2, WatcherType.Children, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", persistentWatcher1, WatcherType.PersistentRecursive, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", persistentWatcher2, WatcherType.PersistentRecursive, false, Code.NOWATCHER, useAsync); zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(persistentEvents1, EventType.PersistentWatchRemoved, "/node1"); assertEvent(persistentEvents2, EventType.PersistentWatchRemoved, "/node1"); assertEvent(dataEvents, EventType.NodeDataChanged, "/node1"); assertEvent(childrenEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(recursiveEvents, EventType.NodeCreated, "/node1/node2"); assertEvent(recursiveEvents, EventType.NodeDataChanged, "/node1"); } /** * Test verifies removing all watcher with WatcherType.PersistentRecursive. * *

    All other watchers shouldn't be removed */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllPersistentRecursiveWatchers(boolean useAsync) throws InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque recursiveEvents1 = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents2 = new LinkedBlockingDeque<>(); Watcher recursiveWatcher1 = recursiveEvents1::add; Watcher recursiveWatcher2 = recursiveEvents2::add; zk2.addWatch("/node1", recursiveWatcher1, AddWatchMode.PERSISTENT_RECURSIVE); zk2.addWatch("/node1", recursiveWatcher2, AddWatchMode.PERSISTENT_RECURSIVE); BlockingDeque dataEvents = new LinkedBlockingDeque<>(); BlockingDeque childrenEvents = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents = new LinkedBlockingDeque<>(); zk2.getData("/node1", dataEvents::add, null); zk2.getChildren("/node1", childrenEvents::add); zk2.addWatch("/node1", persistentEvents::add, AddWatchMode.PERSISTENT); removeWatches(zk2, "/node1", recursiveWatcher1, WatcherType.PersistentRecursive, false, Code.OK, useAsync); removeWatches(zk2, "/node1", recursiveWatcher2, WatcherType.PersistentRecursive, false, Code.OK, useAsync); removeWatches(zk2, "/node1", recursiveWatcher1, WatcherType.Data, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", recursiveWatcher2, WatcherType.Data, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", recursiveWatcher1, WatcherType.Children, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", recursiveWatcher2, WatcherType.Children, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", recursiveWatcher1, WatcherType.Persistent, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", recursiveWatcher2, WatcherType.Persistent, false, Code.NOWATCHER, useAsync); assertEvent(recursiveEvents1, EventType.PersistentWatchRemoved, "/node1"); assertEvent(recursiveEvents2, EventType.PersistentWatchRemoved, "/node1"); zk1.create("/node1/child1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", "test".getBytes(), -1); assertEvent(dataEvents, EventType.NodeDataChanged, "/node1"); assertEvent(childrenEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeDataChanged, "/node1"); } /** * Test verifies given watcher doesn't exists! */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testNoWatcherException(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 2); MyWatcher w2 = new MyWatcher("/node1", 2); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNull(zk2.exists("/node2", w2), "Didn't set data watches"); LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w1); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w2); // New Watcher which will be used for removal MyWatcher w3 = new MyWatcher("/node1", 2); removeWatches(zk2, "/node1", w3, WatcherType.Any, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", w3, WatcherType.Children, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/node1", w3, WatcherType.Data, false, Code.NOWATCHER, useAsync); removeWatches(zk2, "/nonexists", w3, WatcherType.Data, false, Code.NOWATCHER, useAsync); } /** * Test verifies WatcherType.Any - removes only the configured data watcher * function */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAnyDataWatcher(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 1); MyWatcher w2 = new MyWatcher("/node1", 2); // Add multiple data watches LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node1", w2), "Didn't set data watches"); // Add child watch LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w2); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w1.matches(), "Didn't remove data watcher"); assertEquals(1, zk2.getChildWatches().size(), "Didn't find child watcher"); assertEquals(1, zk2.getDataWatches().size(), "Didn't find data watcher"); removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove child watcher"); } /** * Test verifies WatcherType.Any - removes only the configured child watcher * function */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAnyChildWatcher(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 2); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); // Add multiple child watches LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w2); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w1); removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove child watcher"); assertEquals(1, zk2.getChildWatches().size(), "Didn't find child watcher"); assertEquals(1, zk2.getDataWatches().size(), "Didn't find data watcher"); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w1.matches(), "Didn't remove watchers"); } /** * Test verifies when there is no server connection. Remove watches when * local=true, otw should retain it */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveWatcherWhenNoConnection(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 2); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); // Add multiple child watches LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w1); LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w2); stopServer(); removeWatches(zk2, "/node1", w2, WatcherType.Any, true, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove child watcher"); assertFalse(w1.matches(), "Shouldn't remove data watcher"); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.CONNECTIONLOSS, useAsync); assertFalse(w1.matches(), "Shouldn't remove data watcher"); // when local=true, here if connection not available, simply removes // from local session removeWatches(zk2, "/node1", w1, WatcherType.Any, true, Code.OK, useAsync); assertTrue(w1.matches(), "Didn't remove data watcher"); } /** * Test verifies many pre-node watchers. Also, verifies internal * datastructure 'watchManager.existWatches' */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testManyPreNodeWatchers(boolean useAsync) throws Exception { int count = 50; List wList = new ArrayList<>(count); MyWatcher w; String path = "/node"; // Exists watcher for (int i = 0; i < count; i++) { final String nodePath = path + i; w = new MyWatcher(nodePath, 1); wList.add(w); LOG.info("Adding pre node watcher {} on path {}", w, nodePath); zk1.exists(nodePath, w); } assertEquals(count, zk1.getExistWatches().size(), "Failed to add watchers!"); for (int i = 0; i < count; i++) { final MyWatcher watcher = wList.get(i); removeWatches(zk1, path + i, watcher, WatcherType.Data, false, Code.OK, useAsync); assertTrue(watcher.matches(), "Didn't remove data watcher"); } assertEquals(0, zk1.getExistWatches().size(), "Didn't remove watch references!"); } /** * Test verifies many child watchers. Also, verifies internal datastructure * 'watchManager.childWatches' */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testManyChildWatchers(boolean useAsync) throws Exception { int count = 50; List wList = new ArrayList<>(count); MyWatcher w; String path = "/node"; // Child watcher for (int i = 0; i < count; i++) { String nodePath = path + i; zk1.create(nodePath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); nodePath += "/"; } for (int i = 0; i < count; i++) { String nodePath = path + i; w = new MyWatcher(path + i, 1); wList.add(w); LOG.info("Adding child watcher {} on path {}", w, nodePath); zk1.getChildren(nodePath, w); nodePath += "/"; } assertEquals(count, zk1.getChildWatches().size(), "Failed to add watchers!"); for (int i = 0; i < count; i++) { final MyWatcher watcher = wList.get(i); removeWatches(zk1, path + i, watcher, WatcherType.Children, false, Code.OK, useAsync); assertTrue(watcher.matches(), "Didn't remove child watcher"); } assertEquals(0, zk1.getChildWatches().size(), "Didn't remove watch references!"); } /** * Test verifies many data watchers. Also, verifies internal datastructure * 'watchManager.dataWatches' */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testManyDataWatchers(boolean useAsync) throws Exception { int count = 50; List wList = new ArrayList<>(count); MyWatcher w; String path = "/node"; // Data watcher for (int i = 0; i < count; i++) { String nodePath = path + i; w = new MyWatcher(path + i, 1); wList.add(w); zk1.create(nodePath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("Adding data watcher {} on path {}", w, nodePath); zk1.getData(nodePath, w, null); nodePath += "/"; } assertEquals(count, zk1.getDataWatches().size(), "Failed to add watchers!"); for (int i = 0; i < count; i++) { final MyWatcher watcher = wList.get(i); removeWatches(zk1, path + i, watcher, WatcherType.Data, false, Code.OK, useAsync); assertTrue(watcher.matches(), "Didn't remove data watcher"); } assertEquals(0, zk1.getDataWatches().size(), "Didn't remove watch references!"); } /** * Test verifies removal of many watchers locally when no connection and * WatcherType#Any. Also, verifies internal watchManager datastructures */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testManyWatchersWhenNoConnection(boolean useAsync) throws Exception { int count = 3; List wList = new ArrayList<>(count); MyWatcher w; String path = "/node"; // Child watcher for (int i = 0; i < count; i++) { String nodePath = path + i; zk1.create(nodePath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); nodePath += "/"; } for (int i = 0; i < count; i++) { String nodePath = path + i; w = new MyWatcher(path + i, 2); wList.add(w); LOG.info("Adding child watcher {} on path {}", w, nodePath); zk1.getChildren(nodePath, w); nodePath += "/"; } assertEquals(count, zk1.getChildWatches().size(), "Failed to add watchers!"); // Data watcher for (int i = 0; i < count; i++) { String nodePath = path + i; w = wList.get(i); LOG.info("Adding data watcher {} on path {}", w, nodePath); zk1.getData(nodePath, w, null); nodePath += "/"; } assertEquals(count, zk1.getDataWatches().size(), "Failed to add watchers!"); stopServer(); for (int i = 0; i < count; i++) { final MyWatcher watcher = wList.get(i); removeWatches(zk1, path + i, watcher, WatcherType.Any, true, Code.OK, useAsync); assertTrue(watcher.matches(), "Didn't remove watcher"); } assertEquals(0, zk1.getChildWatches().size(), "Didn't remove watch references!"); assertEquals(0, zk1.getDataWatches().size(), "Didn't remove watch references!"); } /** * Test verifies removing watcher having namespace */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testChRootRemoveWatcher(boolean useAsync) throws Exception { // creating the subtree for chRoot clients. String chRoot = "/appsX"; zk1.create("/appsX", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); if (zk1 != null) { zk1.close(); } if (zk2 != null) { zk2.close(); } // Creating chRoot client. zk1 = createClient(this.hostPort + chRoot); zk2 = createClient(this.hostPort + chRoot); LOG.info("Creating child znode /node1 using chRoot client"); zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher w1 = new MyWatcher("/node1", 2); MyWatcher w2 = new MyWatcher("/node1", 1); LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); // Add multiple child watches LOG.info("Adding child watcher {} on path {}", w1, "/node1"); zk2.getChildren("/node1", w2); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); zk2.getChildren("/node1", w1); removeWatches(zk2, "/node1", w1, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w1.matches(), "Didn't remove child watcher"); assertEquals(1, zk2.getChildWatches().size(), "Didn't find child watcher"); removeWatches(zk2, "/node1", w2, WatcherType.Any, false, Code.OK, useAsync); assertTrue(w2.matches(), "Didn't remove child watcher"); } /** * Verify that if a given watcher doesn't exist, the server properly * returns an error code for it. * * In our Java client implementation, we check that a given watch exists at * two points: * * 1) before submitting the RemoveWatches request * 2) after a successful server response, when the watcher needs to be * removed * * Since this can be racy (i.e. a watch can fire while a RemoveWatches * request is in-flight), we need to verify that the watch was actually * removed (i.e. from ZKDatabase and DataTree) and return NOWATCHER if * needed. * * Also, other implementations might not do a client side check before * submitting a RemoveWatches request. If we don't do a server side check, * we would just return ZOK even if no watch was removed. * */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testNoWatcherServerException(boolean useAsync) throws KeeperException, InterruptedException, IOException, TimeoutException { CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = spy(new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher)); MyWatchManager watchManager = new MyWatchManager(false, watcher); doReturn(watchManager).when(zk).getWatchManager(); watcher.waitForConnected(CONNECTION_TIMEOUT); removeWatches(zk, "/nowatchhere", watcher, WatcherType.Data, false, Code.NOWATCHER, useAsync); assertThat("Server didn't return NOWATCHER", watchManager.lastReturnCode, is(Code.NOWATCHER.intValue())); } /** * Test verifies given watcher doesn't exists! */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllNoWatcherException(boolean useAsync) throws IOException, InterruptedException, KeeperException { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); removeAllWatches(zk2, "/node1", WatcherType.Any, false, Code.NOWATCHER, useAsync); } /** * Test verifies null watcher */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 30) public void testNullWatcherReference(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { if (useAsync) { zk1.removeWatches("/node1", null, WatcherType.Data, false, null, null); } else { zk1.removeWatches("/node1", null, WatcherType.Data, false); } fail("Must throw IllegalArgumentException as watcher is null!"); } catch (IllegalArgumentException iae) { // expected } } /** * Test verifies WatcherType.Data - removes only the configured data watcher * function */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveWhenMultipleDataWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final CountDownLatch dataWatchCount = new CountDownLatch(1); final CountDownLatch rmWatchCount = new CountDownLatch(1); Watcher w1 = event -> { if (event.getType() == EventType.DataWatchRemoved) { rmWatchCount.countDown(); } }; Watcher w2 = event -> { if (event.getType() == EventType.NodeDataChanged) { dataWatchCount.countDown(); } }; // Add multiple data watches LOG.info("Adding data watcher {} on path {}", w1, "/node1"); assertNotNull(zk2.exists("/node1", w1), "Didn't set data watches"); LOG.info("Adding data watcher {} on path {}", w2, "/node1"); assertNotNull(zk2.exists("/node1", w2), "Didn't set data watches"); removeWatches(zk2, "/node1", w1, WatcherType.Data, false, Code.OK, useAsync); assertTrue(rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Didn't remove data watcher"); zk1.setData("/node1", "test".getBytes(), -1); LOG.info("Waiting for data watchers to be notified"); assertTrue(dataWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Didn't get data watch notification!"); } /** * Test verifies WatcherType.Children - removes only the configured child * watcher function */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveWhenMultipleChildWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final CountDownLatch childWatchCount = new CountDownLatch(1); final CountDownLatch rmWatchCount = new CountDownLatch(1); Watcher w1 = event -> { if (event.getType() == EventType.ChildWatchRemoved) { rmWatchCount.countDown(); } }; Watcher w2 = event -> { if (event.getType() == EventType.NodeChildrenChanged) { childWatchCount.countDown(); } }; // Add multiple child watches LOG.info("Adding child watcher {} on path {}", w1, "/node1"); assertEquals(0, zk2.getChildren("/node1", w1).size(), "Didn't set child watches"); LOG.info("Adding child watcher {} on path {}", w2, "/node1"); assertEquals(0, zk2.getChildren("/node1", w2).size(), "Didn't set child watches"); removeWatches(zk2, "/node1", w1, WatcherType.Children, false, Code.OK, useAsync); assertTrue(rmWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Didn't remove child watcher"); zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("Waiting for child watchers to be notified"); assertTrue(childWatchCount.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Didn't get child watch notification!"); } /** * Test verifies {@link WatcherType#Persistent} - removes only the configured watcher function */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveWhenMultiplePersistentWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque persistentEvents1 = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents2 = new LinkedBlockingDeque<>(); Watcher w1 = persistentEvents1::add; // Add multiple persistent watches zk2.addWatch("/node1", w1, AddWatchMode.PERSISTENT); zk2.addWatch("/node1", persistentEvents2::add, AddWatchMode.PERSISTENT); removeWatches(zk2, "/node1", w1, WatcherType.Persistent, false, Code.OK, useAsync); assertEvent(persistentEvents1, EventType.PersistentWatchRemoved, "/node1"); zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEvent(persistentEvents2, EventType.NodeChildrenChanged, "/node1"); assertNoEvent(persistentEvents1); } /** * Test verifies {@link WatcherType#PersistentRecursive} - removes only the configured watcher function */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveWhenMultiplePersistentRecursiveWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque recursiveEvents1 = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents2 = new LinkedBlockingDeque<>(); Watcher w1 = recursiveEvents1::add; // Add multiple persistent recursive watches zk2.addWatch("/node1", w1, AddWatchMode.PERSISTENT_RECURSIVE); zk2.addWatch("/node1", recursiveEvents2::add, AddWatchMode.PERSISTENT_RECURSIVE); removeWatches(zk2, "/node1", w1, WatcherType.PersistentRecursive, false, Code.OK, useAsync); assertEvent(recursiveEvents1, EventType.PersistentWatchRemoved, "/node1"); zk1.create("/node1/node2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEvent(recursiveEvents2, EventType.NodeCreated, "/node1/node2"); assertNoEvent(recursiveEvents1); } /** * Test verifies {@link OpCode#checkWatches} {@link WatcherType#Persistent} using {@link WatcherType#Data}. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemovePersistentWatchesOnAPathPartially(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque persistentEvents = new LinkedBlockingDeque<>(); Watcher persistentWatcher = persistentEvents::add; zk2.addWatch("/node1", persistentWatcher, AddWatchMode.PERSISTENT); assertWatchers(zk2, "/node1", WatcherType.Persistent); assertNoWatchers(zk2, "/node1", WatcherType.Data); removeWatches(zk2, "/node1", persistentWatcher, WatcherType.Data, false, Code.NOWATCHER, useAsync); assertWatchers(zk2, "/node1", WatcherType.Persistent); assertNoWatchers(zk2, "/node1", WatcherType.Data); zk1.create("/node1/child1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(persistentEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeDataChanged, "/node1"); assertNull(persistentEvents.poll(10, TimeUnit.MILLISECONDS)); } /** * Test verifies {@link OpCode#removeWatches} {@link WatcherType#Data}. * *

    All other watcher types shouldn't be removed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllDataWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque dataEvents1 = new LinkedBlockingDeque<>(); BlockingDeque dataEvents2 = new LinkedBlockingDeque<>(); // Add multiple data watches zk2.getData("/node1", dataEvents1::add, null); zk2.getData("/node1", dataEvents2::add, null); BlockingDeque childrenEvents = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents = new LinkedBlockingDeque<>(); zk2.getChildren("/node1", childrenEvents::add); zk2.addWatch("/node1", persistentEvents::add, AddWatchMode.PERSISTENT); zk2.addWatch("/node1", recursiveEvents::add, AddWatchMode.PERSISTENT_RECURSIVE); assertWatchers(zk2, "/node1", WatcherType.values()); removeAllWatches(zk2, "/node1", WatcherType.Data, false, Code.OK, useAsync); assertEvent(dataEvents1, EventType.DataWatchRemoved, "/node1"); assertEvent(dataEvents2, EventType.DataWatchRemoved, "/node1"); assertWatchersExcept(zk2, "/node1", WatcherType.Data); zk1.create("/node1/child", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(childrenEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeDataChanged, "/node1"); assertEvent(recursiveEvents, EventType.NodeCreated, "/node1/child"); assertEvent(recursiveEvents, EventType.NodeDataChanged, "/node1"); assertNoEvent(dataEvents1); assertNoEvent(dataEvents2); } /** * Test verifies {@link OpCode#removeWatches} {@link WatcherType#Children}. * *

    All other watcher types shouldn't be removed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllChildWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque childrenEvents1 = new LinkedBlockingDeque<>(); BlockingDeque childrenEvents2 = new LinkedBlockingDeque<>(); // Add multiple child watches zk2.getChildren("/node1", childrenEvents1::add); zk2.getChildren("/node1", childrenEvents2::add); BlockingDeque dataEvents = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents = new LinkedBlockingDeque<>(); zk2.getData("/node1", dataEvents::add, null); zk2.addWatch("/node1", persistentEvents::add, AddWatchMode.PERSISTENT); zk2.addWatch("/node1", recursiveEvents::add, AddWatchMode.PERSISTENT_RECURSIVE); assertWatchers(zk2, "/node1", WatcherType.values()); removeAllWatches(zk2, "/node1", WatcherType.Children, false, Code.OK, useAsync); assertEvent(childrenEvents1, EventType.ChildWatchRemoved, "/node1"); assertEvent(childrenEvents2, EventType.ChildWatchRemoved, "/node1"); assertWatchersExcept(zk2, "/node1", WatcherType.Children); zk1.create("/node1/child", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(dataEvents, EventType.NodeDataChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeDataChanged, "/node1"); assertEvent(recursiveEvents, EventType.NodeCreated, "/node1/child"); assertEvent(recursiveEvents, EventType.NodeDataChanged, "/node1"); assertNull(childrenEvents1.poll(10, TimeUnit.MILLISECONDS)); assertNull(childrenEvents2.poll(10, TimeUnit.MILLISECONDS)); } /** * Test verifies {@link OpCode#removeWatches} {@link WatcherType#Persistent}. * *

    All other watcher types shouldn't be removed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllPersistentWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque persistentEvents1 = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents2 = new LinkedBlockingDeque<>(); // Add multiple persistent watches zk2.addWatch("/node1", persistentEvents1::add, AddWatchMode.PERSISTENT); zk2.addWatch("/node1", persistentEvents2::add, AddWatchMode.PERSISTENT); BlockingDeque dataEvents = new LinkedBlockingDeque<>(); BlockingDeque childrenEvents = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents = new LinkedBlockingDeque<>(); zk2.getData("/node1", dataEvents::add, null); zk2.getChildren("/node1", childrenEvents::add, null); zk2.addWatch("/node1", recursiveEvents::add, AddWatchMode.PERSISTENT_RECURSIVE); assertWatchers(zk2, "/node1", WatcherType.values()); removeAllWatches(zk2, "/node1", WatcherType.Persistent, false, Code.OK, useAsync); assertEvent(persistentEvents1, EventType.PersistentWatchRemoved, "/node1"); assertEvent(persistentEvents2, EventType.PersistentWatchRemoved, "/node1"); assertWatchersExcept(zk2, "/node1", WatcherType.Persistent); zk1.create("/node1/child1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(dataEvents, EventType.NodeDataChanged, "/node1"); assertEvent(childrenEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(recursiveEvents, EventType.NodeCreated, "/node1/child1"); assertEvent(recursiveEvents, EventType.NodeDataChanged, "/node1"); assertNull(persistentEvents1.poll(10, TimeUnit.MILLISECONDS)); assertNull(persistentEvents2.poll(10, TimeUnit.MILLISECONDS)); } /** * Test verifies {@link OpCode#removeWatches} {@link WatcherType#Persistent} using {@link WatcherType#Data}. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllPersistentWatchesOnAPathPartially(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque persistentEvents = new LinkedBlockingDeque<>(); zk2.addWatch("/node1", persistentEvents::add, AddWatchMode.PERSISTENT); assertWatchers(zk2, "/node1", WatcherType.Persistent); assertNoWatchers(zk2, "/node1", WatcherType.Data); removeAllWatches(zk2, "/node1", WatcherType.Data, false, Code.NOWATCHER, useAsync); assertWatchers(zk2, "/node1", WatcherType.Persistent); assertNoWatchers(zk2, "/node1", WatcherType.Data); zk1.create("/node1/child1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(persistentEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeDataChanged, "/node1"); assertNull(persistentEvents.poll(10, TimeUnit.MILLISECONDS)); } /** * Test verifies {@link OpCode#removeWatches} {@link WatcherType#PersistentRecursive}. * *

    All other watcher types shouldn't be removed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllPersistentRecursiveWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); BlockingDeque recursiveEvents1 = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents2 = new LinkedBlockingDeque<>(); // Add multiple persistent recursive watches zk2.addWatch("/node1", recursiveEvents1::add, AddWatchMode.PERSISTENT_RECURSIVE); zk2.addWatch("/node1", recursiveEvents2::add, AddWatchMode.PERSISTENT_RECURSIVE); BlockingDeque dataEvents = new LinkedBlockingDeque<>(); BlockingDeque childrenEvents = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents = new LinkedBlockingDeque<>(); zk2.getData("/node1", dataEvents::add, null); zk2.getChildren("/node1", childrenEvents::add, null); zk2.addWatch("/node1", persistentEvents::add, AddWatchMode.PERSISTENT); assertWatchers(zk2, "/node1", WatcherType.values()); removeAllWatches(zk2, "/node1", WatcherType.PersistentRecursive, false, Code.OK, useAsync); assertEvent(recursiveEvents1, EventType.PersistentWatchRemoved, "/node1"); assertEvent(recursiveEvents2, EventType.PersistentWatchRemoved, "/node1"); assertWatchersExcept(zk2, "/node1", WatcherType.PersistentRecursive); zk1.create("/node1/child1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertEvent(dataEvents, EventType.NodeDataChanged, "/node1"); assertEvent(childrenEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeChildrenChanged, "/node1"); assertEvent(persistentEvents, EventType.NodeDataChanged, "/node1"); assertNull(recursiveEvents1.poll(10, TimeUnit.MILLISECONDS)); assertNull(recursiveEvents2.poll(10, TimeUnit.MILLISECONDS)); } /** * Test verifies {@link OpCode#removeWatches} {@link WatcherType#Any}. * *

    All watcher types should be removed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testRemoveAllWatchesOnAPath(boolean useAsync) throws Exception { zk1.create("/node1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // Add multiple child watches BlockingDeque childEvents1 = new LinkedBlockingDeque<>(); BlockingDeque childEvents2 = new LinkedBlockingDeque<>(); zk2.getChildren("/node1", childEvents1::add); zk2.getChildren("/node1", childEvents2::add); // Add multiple data watches BlockingDeque dataEvents1 = new LinkedBlockingDeque<>(); BlockingDeque dataEvents2 = new LinkedBlockingDeque<>(); zk2.getData("/node1", dataEvents1::add, null); zk2.exists("/node1", dataEvents2::add); // Add multiple persistent watches BlockingDeque persistentEvents1 = new LinkedBlockingDeque<>(); BlockingDeque persistentEvents2 = new LinkedBlockingDeque<>(); zk2.addWatch("/node1", persistentEvents1::add, AddWatchMode.PERSISTENT); zk2.addWatch("/node1", persistentEvents2::add, AddWatchMode.PERSISTENT); // Add multiple recursive watches BlockingDeque recursiveEvents1 = new LinkedBlockingDeque<>(); BlockingDeque recursiveEvents2 = new LinkedBlockingDeque<>(); zk2.addWatch("/node1", recursiveEvents1::add, AddWatchMode.PERSISTENT_RECURSIVE); zk2.addWatch("/node1", recursiveEvents2::add, AddWatchMode.PERSISTENT_RECURSIVE); assertWatchers(zk2, "/node1", WatcherType.values()); removeAllWatches(zk2, "/node1", WatcherType.Any, false, Code.OK, useAsync); assertEvent(childEvents1, EventType.ChildWatchRemoved, "/node1"); assertEvent(childEvents2, EventType.ChildWatchRemoved, "/node1"); assertEvent(dataEvents1, EventType.DataWatchRemoved, "/node1"); assertEvent(dataEvents2, EventType.DataWatchRemoved, "/node1"); assertEvent(persistentEvents1, EventType.PersistentWatchRemoved, "/node1"); assertEvent(persistentEvents2, EventType.PersistentWatchRemoved, "/node1"); assertEvent(recursiveEvents1, EventType.PersistentWatchRemoved, "/node1"); assertEvent(recursiveEvents2, EventType.PersistentWatchRemoved, "/node1"); assertNoWatchers(zk2, "/node1", WatcherType.values()); zk1.create("/node1/child1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.setData("/node1", null, -1); assertNoEvent(childEvents1); assertNoEvent(childEvents2); assertNoEvent(dataEvents1); assertNoEvent(dataEvents2); assertNoEvent(persistentEvents1); assertNoEvent(persistentEvents2); assertNoEvent(recursiveEvents1); assertNoEvent(recursiveEvents2); } private static class MyWatchManager extends ZKWatchManager { int lastReturnCode; MyWatchManager(boolean disableAutoWatchReset, Watcher defaultWatcher) { super(disableAutoWatchReset, defaultWatcher); } void containsWatcher(String path, Watcher watcher, WatcherType watcherType) { // prevent contains watcher } @Override protected boolean removeWatches( Map> pathVsWatcher, Watcher watcher, String path, boolean local, int rc, Set removedWatchers) { lastReturnCode = rc; return false; } } private static class MyWatcher implements Watcher { private final String path; private String eventPath; private CountDownLatch latch; private List eventsAfterWatchRemoval = new ArrayList<>(); MyWatcher(String path, int count) { this.path = path; latch = new CountDownLatch(count); } public void process(WatchedEvent event) { LOG.debug("Event path : {}, eventPath : {}", path, event.getPath()); this.eventPath = event.getPath(); // notifies watcher removal if (latch.getCount() == 0) { if (event.getType() != EventType.None) { eventsAfterWatchRemoval.add(event.getType()); } } if (event.getType() == EventType.ChildWatchRemoved || event.getType() == EventType.DataWatchRemoved) { latch.countDown(); } } /** * Returns true if the watcher was triggered. Try to avoid using this * method with assertFalse statements. A false return depends on a timed * out wait on a latch, which makes tests run long. * * @return true if the watcher was triggered, false otherwise * @throws InterruptedException if interrupted while waiting on latch */ public boolean matches() throws InterruptedException { if (!latch.await(CONNECTION_TIMEOUT / 5, TimeUnit.MILLISECONDS)) { LOG.error("Failed waiting to remove the watches"); return false; } LOG.debug("Client path : {} eventPath : {}", path, eventPath); return path.equals(eventPath); } public List getEventsAfterWatchRemoval() { return eventsAfterWatchRemoval; } } private class MyCallback implements AsyncCallback.VoidCallback { private final String path; private final int rc; private String eventPath; int eventRc; private CountDownLatch latch = new CountDownLatch(1); public MyCallback(int rc, String path) { this.rc = rc; this.path = path; } @Override public void processResult(int rc, String eventPath, Object ctx) { System.out.println("latch:" + path + " " + eventPath); this.eventPath = eventPath; this.eventRc = rc; this.latch.countDown(); } /** * Returns true if the callback was triggered. Try to avoid using this * method with assertFalse statements. A false return depends on a timed * out wait on a latch, which makes tests run long. * * @return true if the watcher was triggered, false otherwise * @throws InterruptedException if interrupted while waiting on latch */ public boolean matches() throws InterruptedException { if (!latch.await(CONNECTION_TIMEOUT / 5, TimeUnit.MILLISECONDS)) { return false; } return path.equals(eventPath) && rc == eventRc; } } /** * Checks if a session is registered with the server as a watcher. * * @param sessionId the session ID to check * @param path the path to check for watchers * @param type the type of watcher * @return true if the client session is a watcher on path for the type */ private boolean isServerSessionWatcher(long sessionId, String path, WatcherType type) { Set cnxns = new HashSet<>(); CollectionUtils.addAll(cnxns, serverFactory.getConnections().iterator()); for (ServerCnxn cnxn : cnxns) { if (cnxn.getSessionId() == sessionId) { return serverFactory.getZooKeeperServer().getZKDatabase().getDataTree().containsWatcher(path, type, cnxn); } } return false; } /** * Asserts next event from queue has given event type and path. */ private void assertEvent(BlockingQueue events, Watcher.Event.EventType eventType, String path) throws InterruptedException { WatchedEvent event = events.poll(5, TimeUnit.SECONDS); assertNotNull(event); assertEquals(eventType, event.getType()); assertEquals(path, event.getPath()); } /** * Asserts no event from queue in a short period. */ private void assertNoEvent(BlockingQueue events) throws InterruptedException { // Short timeout so we don't hurt CI too much. It will fail finally given enough run if there are bugs. assertNull(events.poll(10, TimeUnit.MILLISECONDS)); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/SaslAuthTest.java0100644 0000000 0000000 00000026170 15051152474 032562 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ClientCnxn.EventThread; import org.apache.zookeeper.ClientCnxn.SendThread; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.client.ZooKeeperSaslClient; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.test.SaslAuthDigestTestBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SaslAuthTest extends SaslAuthDigestTestBase { @BeforeAll public static void init() { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); try { File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); String jaasContent = getJaasFileContent(); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write(jaasContent); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException e) { // could not create tmp directory to hold JAAS conf file : test will // fail now. } } private static String getJaasFileContent() { StringBuilder jaasContent = new StringBuilder(); String newLine = System.getProperty("line.separator"); jaasContent.append("Server {"); jaasContent.append(newLine); jaasContent.append("org.apache.zookeeper.server.auth.DigestLoginModule required"); jaasContent.append(newLine); jaasContent.append("user_super=\"test\";"); jaasContent.append(newLine); jaasContent.append("};"); jaasContent.append(newLine); jaasContent.append("Client {"); jaasContent.append(newLine); jaasContent.append("org.apache.zookeeper.server.auth.DigestLoginModule required"); jaasContent.append(newLine); jaasContent.append("username=\"super\""); jaasContent.append(newLine); jaasContent.append("password=\"test\";"); jaasContent.append(newLine); jaasContent.append("};"); jaasContent.append(newLine); return jaasContent.toString(); } @AfterAll public static void clean() { System.clearProperty("zookeeper.authProvider.1"); System.clearProperty("java.security.auth.login.config"); } private final CountDownLatch authFailed = new CountDownLatch(1); @Override protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { MyWatcher watcher = new MyWatcher(); return createClient(watcher, hp); } private class MyWatcher extends CountdownWatcher { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { authFailed.countDown(); } else { super.process(event); } } } @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Thread.sleep(1000); } finally { zk.close(); } } @Test public void testValidSaslIds() throws Exception { ZooKeeper zk = createClient(); List validIds = new ArrayList<>(); validIds.add("user"); validIds.add("service/host.name.com"); validIds.add("user@KERB.REALM"); validIds.add("service/host.name.com@KERB.REALM"); int i = 0; for (String validId : validIds) { List aclList = new ArrayList<>(); ACL acl = new ACL(0, new Id("sasl", validId)); aclList.add(acl); zk.create("/valid" + i, null, aclList, CreateMode.PERSISTENT); i++; } } @Test public void testInvalidSaslIds() throws Exception { ZooKeeper zk = createClient(); List invalidIds = new ArrayList<>(); invalidIds.add("user@KERB.REALM/server.com"); invalidIds.add("user@KERB.REALM1@KERB.REALM2"); int i = 0; for (String invalidId : invalidIds) { List aclList = new ArrayList<>(); try { ACL acl = new ACL(0, new Id("sasl", invalidId)); aclList.add(acl); zk.create("/invalid" + i, null, aclList, CreateMode.PERSISTENT); fail("SASLAuthenticationProvider.isValid() failed to catch invalid Id."); } catch (KeeperException.InvalidACLException e) { // ok. } finally { i++; } } } @Test public void testZKOperationsAfterClientSaslAuthFailure() throws Exception { CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); try { setSaslFailureFlag(zk); // try node creation for around 15 second, int totalTry = 10; int tryCount = 0; boolean success = false; while (!success && tryCount++ <= totalTry) { try { zk.create("/saslAuthFail", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); success = true; } catch (KeeperException.ConnectionLossException e) { Thread.sleep(1000); // do nothing } } assertTrue(success, "ZNode creation is failing continuously after Sasl auth failure."); } finally { zk.close(); } } // set saslLoginFailed to true to simulate the LoginException private void setSaslFailureFlag(ZooKeeper zk) throws Exception { Field cnxnField = zk.getClass().getDeclaredField("cnxn"); cnxnField.setAccessible(true); ClientCnxn clientCnxn = (ClientCnxn) cnxnField.get(zk); Field sendThreadField = clientCnxn.getClass().getDeclaredField("sendThread"); sendThreadField.setAccessible(true); SendThread sendThread = (SendThread) sendThreadField.get(clientCnxn); Field saslLoginFailedField = sendThread.getClass().getDeclaredField("saslLoginFailed"); saslLoginFailedField.setAccessible(true); saslLoginFailedField.setBoolean(sendThread, true); } @Test public void testThreadsShutdownOnAuthFailed() throws Exception { MyWatcher watcher = new MyWatcher(); ZooKeeper zk = null; try { zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); try { zk.addAuthInfo("FOO", "BAR".getBytes()); zk.getData("/path1", false, null); fail("Should get auth state error"); } catch (KeeperException.AuthFailedException e) { if (!authFailed.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Should have called my watcher"); } } Field cnxnField = zk.getClass().getDeclaredField("cnxn"); cnxnField.setAccessible(true); ClientCnxn clientCnxn = (ClientCnxn) cnxnField.get(zk); Field sendThreadField = clientCnxn.getClass().getDeclaredField("sendThread"); sendThreadField.setAccessible(true); SendThread sendThread = (SendThread) sendThreadField.get(clientCnxn); Field eventThreadField = clientCnxn.getClass().getDeclaredField("eventThread"); eventThreadField.setAccessible(true); EventThread eventThread = (EventThread) eventThreadField.get(clientCnxn); ZooKeeperSaslClient zooKeeperSaslClient = clientCnxn.getZooKeeperSaslClient(); assertNotNull(zooKeeperSaslClient); sendThread.join(CONNECTION_TIMEOUT); eventThread.join(CONNECTION_TIMEOUT); // If login is null, this means ZooKeeperSaslClient#shutdown method has been called which in turns // means that Login#shutdown has been called. assertNull(sendThread.getLogin()); assertFalse(sendThread.isAlive(), "SendThread did not shutdown after authFail"); assertFalse(eventThread.isAlive(), "EventThread did not shutdown after authFail"); } finally { if (zk != null) { zk.close(); } } } @Test public void testDisconnectNotCreatingLoginThread() throws Exception { MyWatcher watcher = new MyWatcher(); ZooKeeper zk = null; try { zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.getData("/", false, null); Field cnxnField = zk.getClass().getDeclaredField("cnxn"); cnxnField.setAccessible(true); ClientCnxn clientCnxn = (ClientCnxn) cnxnField.get(zk); Field sendThreadField = clientCnxn.getClass().getDeclaredField("sendThread"); sendThreadField.setAccessible(true); SendThread sendThread = (SendThread) sendThreadField.get(clientCnxn); Login l1 = sendThread.getLogin(); assertNotNull(l1); stopServer(); watcher.waitForDisconnected(CONNECTION_TIMEOUT); startServer(); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.getData("/", false, null); assertSame("Login thread should not been recreated on disconnect", l1, sendThread.getLogin()); } finally { if (zk != null) { zk.close(); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ServerConfigTest.java0100644 0000000 0000000 00000006763 15051152474 033440 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; import org.apache.zookeeper.server.ServerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ServerConfigTest { private ServerConfig serverConfig; @BeforeEach public void setUp() { serverConfig = new ServerConfig(); } @Test public void testFewArguments() { assertThrows(IllegalArgumentException.class, () -> { String[] args = {"2181"}; serverConfig.parse(args); }); } @Test public void testValidArguments() { String[] args = {"2181", "/data/dir", "60000", "10000"}; serverConfig.parse(args); assertEquals(2181, serverConfig.getClientPortAddress().getPort()); assertTrue(checkEquality("/data/dir", serverConfig.getDataDir())); assertEquals(60000, serverConfig.getTickTime()); assertEquals(10000, serverConfig.getMaxClientCnxns()); } @Test public void testTooManyArguments() { assertThrows(IllegalArgumentException.class, () -> { String[] args = {"2181", "/data/dir", "60000", "10000", "9999"}; serverConfig.parse(args); }); } @Test public void testJvmPauseMonitorConfigured() { final Long sleepTime = 444L; final Long warnTH = 5555L; final Long infoTH = 555L; QuorumPeerConfig qpConfig = mock(QuorumPeerConfig.class); when(qpConfig.isJvmPauseMonitorToRun()).thenReturn(true); when(qpConfig.getJvmPauseSleepTimeMs()).thenReturn(sleepTime); when(qpConfig.getJvmPauseWarnThresholdMs()).thenReturn(warnTH); when(qpConfig.getJvmPauseInfoThresholdMs()).thenReturn(infoTH); serverConfig.readFrom(qpConfig); assertEquals(sleepTime, Long.valueOf(serverConfig.getJvmPauseSleepTimeMs())); assertEquals(warnTH, Long.valueOf(serverConfig.getJvmPauseWarnThresholdMs())); assertEquals(infoTH, Long.valueOf(serverConfig.getJvmPauseInfoThresholdMs())); assertTrue(serverConfig.isJvmPauseMonitorToRun()); } boolean checkEquality(String a, String b) { assertNotNull(a); assertNotNull(b); return a.equals(b); } boolean checkEquality(String a, File b) { assertNotNull(a); assertNotNull(b); return new File(a).equals(b); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/TestableZooKeeper.java0100644 0000000 0000000 00000007603 15051152474 033565 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import java.io.IOException; import java.net.SocketAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.jute.Record; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; public class TestableZooKeeper extends ZooKeeperAdmin { public TestableZooKeeper(String host, int sessionTimeout, Watcher watcher) throws IOException { super(host, sessionTimeout, watcher); } public void setXid(int xid) { cnxn.xid = xid; } public int checkXid() { return cnxn.xid; } /** * Cause this ZooKeeper object to disconnect from the server. It will then * later attempt to reconnect. */ public void testableConnloss() throws IOException { synchronized (cnxn) { cnxn.sendThread.testableCloseSocket(); } } /** * Cause this ZooKeeper object to stop receiving from the ZooKeeperServer * for the given number of milliseconds. * @param ms the number of milliseconds to pause. * @return true if the connection is paused, otherwise false */ public boolean pauseCnxn(final long ms) { final CountDownLatch initiatedPause = new CountDownLatch(1); new Thread() { public void run() { synchronized (cnxn) { try { try { cnxn.sendThread.testableCloseSocket(); } catch (IOException e) { e.printStackTrace(); } finally { initiatedPause.countDown(); } Thread.sleep(ms); } catch (InterruptedException e) { } } } }.start(); try { return initiatedPause.await(ms, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); return false; } } public SocketAddress testableLocalSocketAddress() { return super.testableLocalSocketAddress(); } public SocketAddress testableRemoteSocketAddress() { return super.testableRemoteSocketAddress(); } /** * @return the last zxid as seen by the client session */ public long testableLastZxid() { return cnxn.getLastZxid(); } public ReplyHeader submitRequest( RequestHeader h, Record request, Record response, WatchRegistration watchRegistration) throws InterruptedException { return cnxn.submitRequest(h, request, response, watchRegistration); } /** Testing only!!! Really!!!! This is only here to test when the client * disconnects from the server w/o sending a session disconnect (ie * ending the session cleanly). The server will eventually notice the * client is no longer pinging and will timeout the session. */ public void disconnect() { cnxn.disconnect(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ZKTestCase.java0100644 0000000 0000000 00000014710 15051152474 032153 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.time.Instant; import java.util.concurrent.CompletableFuture; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.util.ServiceUtils; import org.hamcrest.CustomMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.StringDescription; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for a non-parameterized ZK test. * * Basic utilities shared by all tests. Also logging of various events during * the test execution (start/stop/success/failure/etc...) */ public class ZKTestCase { protected static final File testBaseDir = new File(System.getProperty("build.test.dir", "build")); private static final Logger LOG = LoggerFactory.getLogger(ZKTestCase.class); public static final int DEFAULT_METRIC_TIMEOUT = 30; static { // Disable System.exit in tests. ServiceUtils.setSystemExitProcedure(ServiceUtils.LOG_ONLY); } private String testName; protected String getTestName() { return testName; } public void syncClient(ZooKeeper zk, boolean synchronous) throws KeeperException { if (synchronous) { try { zk.sync("/"); } catch (InterruptedException ex) { throw new RuntimeException(ex); } return; } final CompletableFuture synced = new CompletableFuture<>(); zk.sync("/", (rc, path, ctx) -> { synced.complete(KeeperException.Code.get(rc)); }, null); KeeperException.Code code = synced.join(); if (code != KeeperException.Code.OK) { throw KeeperException.create(code); } } @BeforeAll public static void before() { if (!testBaseDir.exists()) { assertTrue(testBaseDir.mkdirs(), "Cannot properly create test base directory " + testBaseDir.getAbsolutePath()); } else if (!testBaseDir.isDirectory()) { assertTrue(testBaseDir.delete(), "Cannot properly delete file with duplicate name of test base directory " + testBaseDir.getAbsolutePath()); assertTrue(testBaseDir.mkdirs(), "Cannot properly create test base directory " + testBaseDir.getAbsolutePath()); } } @BeforeEach public void starting(TestInfo testInfo) { // By default, disable starting a JettyAdminServer in tests to avoid // accidentally attempting to start multiple admin servers on the // same port. System.setProperty("zookeeper.admin.enableServer", "false"); // disable rate limiting System.setProperty("zookeeper.admin.rateLimiterIntervalInMS", "0"); // ZOOKEEPER-2693 disables all 4lw by default. // Here we enable the 4lw which ZooKeeper tests depends. System.setProperty("zookeeper.4lw.commands.whitelist", "*"); LOG.info("STARTING {}", testInfo.getTestMethod()); } @AfterEach public void finished(TestInfo testInfo) { LOG.info("FINISHED {}", testInfo.getTestMethod()); } public interface WaitForCondition { /** * @return true when success */ boolean evaluate(); } /** * Wait for condition to be true; otherwise fail the test if it exceed * timeout * @param msg error message to print when fail * @param condition condition to evaluate * @param timeout timeout in seconds * @throws InterruptedException */ public static void waitFor(String msg, WaitForCondition condition, int timeout) throws InterruptedException { final Instant deadline = Instant.now().plusSeconds(timeout); while (Instant.now().isBefore(deadline)) { if (condition.evaluate()) { return; } Thread.sleep(100); } fail(msg); } public static void waitForMetric(String metricKey, Matcher matcher) throws InterruptedException { waitForMetric(metricKey, matcher, DEFAULT_METRIC_TIMEOUT); } public static void waitForMetric(String metricKey, Matcher matcher, int timeoutInSeconds) throws InterruptedException { String errorMessage = String.format("metric \"%s\" failed to match after %d seconds", metricKey, timeoutInSeconds); waitFor(errorMessage, () -> { @SuppressWarnings("unchecked") T actual = (T) MetricsUtils.currentServerMetrics().get(metricKey); if (!matcher.matches(actual)) { Description description = new StringDescription(); matcher.describeMismatch(actual, description); LOG.info("match failed for metric {}: {}", metricKey, description); return false; } return true; }, timeoutInSeconds); } /** * Functionally identical to {@link org.hamcrest.Matchers#closeTo} except that it accepts all numerical types * instead of failing if the value is not a {@link Double}. */ public static Matcher closeTo(double operand, double error) { return new CustomMatcher(String.format("A number within %s of %s", error, operand)) { @Override public boolean matches(Object actual) { return Math.abs(operand - ((Number) actual).doubleValue()) <= error; } }; } }apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ZKUtilTest.java0100644 0000000 0000000 00000015467 15051152474 032227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.junit.Assume.assumeTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertIterableEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class ZKUtilTest extends ClientBase { private static final File testData = new File(System.getProperty("test.data.dir", "build/test/data")); @BeforeAll public static void init() { testData.mkdirs(); } @Test public void testValidateFileInput() throws IOException { File file = File.createTempFile("test", ".junit", testData); file.deleteOnExit(); String absolutePath = file.getAbsolutePath(); String error = ZKUtil.validateFileInput(absolutePath); assertNull(error); } @Test public void testValidateFileInputNotExist() { String fileName = UUID.randomUUID().toString(); File file = new File(testData, fileName); String absolutePath = file.getAbsolutePath(); String error = ZKUtil.validateFileInput(absolutePath); assertNotNull(error); String expectedMessage = "File '" + absolutePath + "' does not exist."; assertEquals(expectedMessage, error); } @Test public void testValidateFileInputDirectory() throws Exception { File file = File.createTempFile("test", ".junit", testData); file.deleteOnExit(); // delete file, as we need directory not file file.delete(); file.mkdir(); String absolutePath = file.getAbsolutePath(); String error = ZKUtil.validateFileInput(absolutePath); assertNotNull(error); String expectedMessage = "'" + absolutePath + "' is a directory. it must be a file."; assertEquals(expectedMessage, error); } @Test public void testUnreadableFileInput() throws Exception { //skip this test on Windows, coverage on Linux assumeTrue(!org.apache.zookeeper.Shell.WINDOWS); File file = File.createTempFile("test", ".junit", testData); file.setReadable(false, false); file.deleteOnExit(); String absolutePath = file.getAbsolutePath(); String error = ZKUtil.validateFileInput(absolutePath); assertNotNull(error); String expectedMessage = "Read permission is denied on the file '" + absolutePath + "'"; assertEquals(expectedMessage, error); } @Test public void testListRootPathSuccess() throws IOException, InterruptedException, KeeperException { TestableZooKeeper zk = createClient(); zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", "some".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List list = ZKUtil.listSubTreeBFS(zk, "/"); list.remove(Quotas.procZookeeper); list.remove(Quotas.quotaZookeeper); list.remove(ZooDefs.CONFIG_NODE); assertEquals(3, list.size()); assertIterableEquals(Arrays.asList("/", "/a", "/a/b"), list); } @Test public void testListNoneRootPathSuccess() throws IOException, InterruptedException, KeeperException { TestableZooKeeper zk = createClient(); zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", "some".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List aList = ZKUtil.listSubTreeBFS(zk, "/a"); assertEquals(2, aList.size()); assertIterableEquals(Arrays.asList("/a", "/a/b"), aList); List bList = ZKUtil.listSubTreeBFS(zk, "/a/b"); assertEquals(1, bList.size()); assertIterableEquals(Collections.singletonList("/a/b"), bList); } @Test public void testDeleteRecursiveInAsyncMode() throws Exception { int batchSize = 10; testDeleteRecursiveInSyncAsyncMode(batchSize); } @Test public void testDeleteRecursiveInSyncMode() throws Exception { int batchSize = 0; testDeleteRecursiveInSyncAsyncMode(batchSize); } // batchSize>0 is async mode otherwise it is sync mode private void testDeleteRecursiveInSyncAsyncMode(int batchSize) throws IOException, InterruptedException, KeeperException { TestableZooKeeper zk = createClient(); String parentPath = "/a"; zk.create(parentPath, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int numberOfNodes = 50; List ops = new ArrayList<>(); for (int i = 0; i < numberOfNodes; i++) { ops.add(Op.create(parentPath + "/a" + i, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); } zk.multi(ops); ops.clear(); // check nodes create successfully List children = zk.getChildren(parentPath, false); assertEquals(numberOfNodes, children.size()); // create one more level of z nodes String subNode = "/a/a0"; for (int i = 0; i < numberOfNodes; i++) { ops.add(Op.create(subNode + "/b" + i, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); } zk.multi(ops); // check sub nodes created successfully children = zk.getChildren(subNode, false); assertEquals(numberOfNodes, children.size()); ZKUtil.deleteRecursive(zk, parentPath, batchSize); Stat exists = zk.exists(parentPath, false); assertNull(exists, "ZKUtil.deleteRecursive() could not delete all the z nodes"); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/ZooKeeperTest.java0100644 0000000 0000000 00000101535 15051152474 032740 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper; import static org.apache.zookeeper.KeeperException.Code.NOAUTH; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.cli.CliCommand; import org.apache.zookeeper.cli.CliException; import org.apache.zookeeper.cli.CliWrapperException; import org.apache.zookeeper.cli.LsCommand; import org.apache.zookeeper.cli.MalformedCommandException; import org.apache.zookeeper.cli.MalformedPathException; import org.apache.zookeeper.cli.SyncCommand; import org.apache.zookeeper.cli.WhoAmICommand; import org.apache.zookeeper.client.ConnectStringParser; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.StaticHostProvider; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.ClientInfo; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; /** * * Testing ZooKeeper public methods * */ public class ZooKeeperTest extends ClientBase { private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n"); @Test public void testDeleteRecursive() throws IOException, InterruptedException, KeeperException { final ZooKeeper zk = createClient(); setupDataTree(zk); assertTrue(ZKUtil.deleteRecursive(zk, "/a/c", 1000)); List children = zk.getChildren("/a", false); assertEquals(1, children.size(), "1 children - c should be deleted "); assertTrue(children.contains("b")); assertTrue(ZKUtil.deleteRecursive(zk, "/a", 1000)); assertNull(zk.exists("/a", null)); } @Test public void testDeleteRecursiveFail() throws IOException, InterruptedException, KeeperException { final ZooKeeper zk = createClient(); setupDataTree(zk); ACL deleteProtection = new ACL(ZooDefs.Perms.DELETE, new Id("digest", "user:tl+z3z0vO6PfPfEENfLF96E6pM0="/* password is test */)); List acls = Arrays.asList(new ACL(ZooDefs.Perms.READ, Ids.ANYONE_ID_UNSAFE), deleteProtection); // poison the well zk.create("/a/c/0/surprise", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(1, zk.getACL("/a/c/0", new Stat()).size()); zk.setACL("/a/c/0", acls, -1); assertEquals(2, zk.getACL("/a/c/0", new Stat()).size()); assertFalse(ZKUtil.deleteRecursive(zk, "/a/c", 1000)); List children = zk.getChildren("/a", false); assertEquals(2, children.size(), "2 children - c should fail to be deleted "); assertTrue(children.contains("b")); assertTrue(ZKUtil.deleteRecursive(zk, "/a/b", 1000)); children = zk.getChildren("/a", false); assertEquals(1, children.size(), "1 children - b should be deleted "); // acquire immunity to poison zk.addAuthInfo(deleteProtection.getId().getScheme(), "user:test".getBytes()); assertTrue(ZKUtil.deleteRecursive(zk, "/a", 1000)); assertNull(zk.exists("/a", null)); } private void setupDataTree(ZooKeeper zk) throws KeeperException, InterruptedException { // making sure setdata works on / zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 1000; i < 3000; ++i) { zk.create("/a/b/v/" + i, "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.create("/a/c", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/c/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < 500; ++i) { zk.create("/a/c/" + i, "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } List children = zk.getChildren("/a", false); assertEquals(2, children.size(), "2 children - b & c should be present "); assertTrue(children.contains("b")); assertTrue(children.contains("c")); } @Test public void testDeleteRecursiveCli() throws IOException, InterruptedException, CliException, KeeperException { final ZooKeeper zk = createClient(); // making sure setdata works on / zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v/1", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/c", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/c/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List children = zk.getChildren("/a", false); assertEquals(children.size(), 2, "2 children - b & c should be present "); assertTrue(children.contains("b")); assertTrue(children.contains("c")); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring1 = "deleteall /a"; zkMain.cl.parseCommand(cmdstring1); assertFalse(zkMain.processZKCmd(zkMain.cl)); assertNull(zk.exists("/a", null)); } @Test public void testDeleteRecursiveAsync() throws IOException, InterruptedException, KeeperException { final ZooKeeper zk = createClient(); // making sure setdata works on / zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v/1", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/c", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/c/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < 50; ++i) { zk.create("/a/c/" + i, "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } List children = zk.getChildren("/a", false); assertEquals(children.size(), 2, "2 children - b & c should be present "); assertTrue(children.contains("b")); assertTrue(children.contains("c")); VoidCallback cb = new VoidCallback() { @Override public void processResult(int rc, String path, Object ctx) { synchronized (ctx) { ((AtomicInteger) ctx).set(4); ctx.notify(); } } }; final AtomicInteger ctx = new AtomicInteger(3); ZKUtil.deleteRecursive(zk, "/a", cb, ctx); synchronized (ctx) { ctx.wait(); } assertEquals(4, ctx.get()); } @Test public void testStatWhenPathDoesNotExist() throws IOException, InterruptedException, MalformedCommandException { final ZooKeeper zk = createClient(); ZooKeeperMain main = new ZooKeeperMain(zk); String cmdstring = "stat /invalidPath"; main.cl.parseCommand(cmdstring); try { main.processZKCmd(main.cl); fail("As Node does not exist, command should fail by throwing No Node Exception."); } catch (CliException e) { assertEquals("Node does not exist: /invalidPath", e.getMessage()); } } @Test public void testParseWithExtraSpaces() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring = " ls / "; zkMain.cl.parseCommand(cmdstring); assertEquals(zkMain.cl.getNumArguments(), 2, "Spaces also considered as characters"); assertEquals(zkMain.cl.getCmdArgument(0), "ls", "ls is not taken as first argument"); assertEquals(zkMain.cl.getCmdArgument(1), "/", "/ is not taken as second argument"); } @Test public void testParseWithQuotes() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); for (String quoteChar : new String[]{"'", "\""}) { String cmdstring = String.format("create /node %1$squoted data%1$s", quoteChar); zkMain.cl.parseCommand(cmdstring); assertEquals(zkMain.cl.getNumArguments(), 3, "quotes combine arguments"); assertEquals(zkMain.cl.getCmdArgument(0), "create", "create is not taken as first argument"); assertEquals(zkMain.cl.getCmdArgument(1), "/node", "/node is not taken as second argument"); assertEquals(zkMain.cl.getCmdArgument(2), "quoted data", "quoted data is not taken as third argument"); } } @Test public void testParseWithMixedQuotes() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); for (String[] quoteChars : new String[][]{{"'", "\""}, {"\"", "'"}}) { String outerQuotes = quoteChars[0]; String innerQuotes = quoteChars[1]; String cmdstring = String.format("create /node %1$s%2$squoted data%2$s%1$s", outerQuotes, innerQuotes); zkMain.cl.parseCommand(cmdstring); assertEquals(zkMain.cl.getNumArguments(), 3, "quotes combine arguments"); assertEquals(zkMain.cl.getCmdArgument(0), "create", "create is not taken as first argument"); assertEquals(zkMain.cl.getCmdArgument(1), "/node", "/node is not taken as second argument"); assertEquals(zkMain.cl.getCmdArgument(2), innerQuotes + "quoted data" + innerQuotes, "quoted data is not taken as third argument"); } } @Test public void testParseWithEmptyQuotes() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring = "create /node ''"; zkMain.cl.parseCommand(cmdstring); assertEquals(zkMain.cl.getNumArguments(), 3, "empty quotes should produce arguments"); assertEquals(zkMain.cl.getCmdArgument(0), "create", "create is not taken as first argument"); assertEquals(zkMain.cl.getCmdArgument(1), "/node", "/node is not taken as second argument"); assertEquals(zkMain.cl.getCmdArgument(2), "", "empty string is not taken as third argument"); } @Test public void testParseWithMultipleQuotes() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring = "create /node '' ''"; zkMain.cl.parseCommand(cmdstring); assertEquals(zkMain.cl.getNumArguments(), 4, "expected 5 arguments"); assertEquals(zkMain.cl.getCmdArgument(0), "create", "create is not taken as first argument"); assertEquals(zkMain.cl.getCmdArgument(1), "/node", "/node is not taken as second argument"); assertEquals(zkMain.cl.getCmdArgument(2), "", "empty string is not taken as third argument"); assertEquals(zkMain.cl.getCmdArgument(3), "", "empty string is not taken as fourth argument"); } @Test public void testNonexistantCommand() throws Exception { testInvalidCommand("cret -s /node1", 127); } @Test public void testCreateCommandWithoutPath() throws Exception { testInvalidCommand("create", 1); } @Test public void testCreateEphemeralCommandWithoutPath() throws Exception { testInvalidCommand("create -e ", 1); } @Test public void testCreateSequentialCommandWithoutPath() throws Exception { testInvalidCommand("create -s ", 1); } @Test public void testCreateEphemeralSequentialCommandWithoutPath() throws Exception { testInvalidCommand("create -s -e ", 1); } private void testInvalidCommand(String cmdString, int exitCode) throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); zkMain.cl.parseCommand(cmdString); // Verify that the exit code is set properly zkMain.processCmd(zkMain.cl); assertEquals(exitCode, zkMain.exitCode); // Verify that the correct exception is thrown try { zkMain.processZKCmd(zkMain.cl); fail(); } catch (CliException e) { return; } fail("invalid command should throw CliException"); } @Test public void testCreateNodeWithoutData() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); // create persistent sequential node String cmdstring = "create -s /node "; zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Doesn't create node without data"); // create ephemeral node cmdstring = "create -e /node "; zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Doesn't create node without data"); // create ephemeral sequential node cmdstring = "create -s -e /node "; zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Doesn't create node without data"); // creating ephemeral with wrong option. cmdstring = "create -s y /node"; zkMain.cl.parseCommand(cmdstring); try { assertTrue(zkMain.processZKCmd(zkMain.cl), "Created node with wrong option"); fail("Created the node with wrong option should " + "throw Exception."); } catch (MalformedPathException e) { assertEquals("Path must start with / character", e.getMessage()); } } @Test public void testACLWithExtraAgruments() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); // create persistent sequential node String cmdstring = "create -s /l data ip:10.18.52.144:cdrwa f g h"; zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Not considering the extra arguments after the acls."); } @Test public void testCreatePersistentNode() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring = "create /node2"; zkMain.cl.parseCommand(cmdstring); assertTrue(zkMain.processZKCmd(zkMain.cl), "Not creating Persistent node."); } @Test public void testDelete() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring1 = "create -e /node2 data"; String cmdstring2 = "delete /node2"; String cmdstring3 = "ls /node2"; zkMain.cl.parseCommand(cmdstring1); assertTrue(zkMain.processZKCmd(zkMain.cl)); zkMain.cl.parseCommand(cmdstring2); assertFalse(zkMain.processZKCmd(zkMain.cl)); zkMain.cl.parseCommand(cmdstring3); assertFalse(zkMain.processCmd(zkMain.cl), ""); } @Test public void testDeleteNonexistantNode() throws Exception { testInvalidCommand("delete /blahblahblah", 1); } @Test public void testStatCommand() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring1 = "create -e /node3 data"; String cmdstring2 = "stat /node3"; String cmdstring3 = "delete /node3"; zkMain.cl.parseCommand(cmdstring1); assertTrue(zkMain.processZKCmd(zkMain.cl)); zkMain.cl.parseCommand(cmdstring2); assertFalse(zkMain.processZKCmd(zkMain.cl)); zkMain.cl.parseCommand(cmdstring3); assertFalse(zkMain.processZKCmd(zkMain.cl)); } @Test public void testInvalidStatCommand() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); // node doesn't exists String cmdstring1 = "stat /node123"; zkMain.cl.parseCommand(cmdstring1); try { assertFalse(zkMain.processZKCmd(zkMain.cl)); fail("Path doesn't exists so, command should fail."); } catch (CliWrapperException e) { assertEquals(KeeperException.Code.NONODE, ((KeeperException) e.getCause()).code()); } } @Test public void testSetData() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring1 = "create -e /node4 data"; String cmdstring2 = "set /node4 " + "data"; String cmdstring3 = "delete /node4"; Stat stat = new Stat(); int version = 0; zkMain.cl.parseCommand(cmdstring1); assertTrue(zkMain.processZKCmd(zkMain.cl)); stat = zk.exists("/node4", true); version = stat.getVersion(); zkMain.cl.parseCommand(cmdstring2); assertFalse(zkMain.processZKCmd(zkMain.cl)); stat = zk.exists("/node4", true); assertEquals(version + 1, stat.getVersion()); zkMain.cl.parseCommand(cmdstring3); assertFalse(zkMain.processZKCmd(zkMain.cl)); } @Test public void testCheckInvalidAcls() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring = "create -s -e /node data ip:scheme:gggsd"; //invalid acl's // For Invalid ACls should not throw exception zkMain.executeLine(cmdstring); } @Test public void testDeleteWithInvalidVersionNo() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmdstring = "create -s -e /node1 data "; String cmdstring1 = "delete /node1 2"; //invalid dataversion no zkMain.executeLine(cmdstring); // For Invalid dataversion number should not throw exception zkMain.executeLine(cmdstring1); } @Test public void testCliCommandsNotEchoingUsage() throws Exception { // setup redirect out/err streams to get System.in/err, use this judiciously! final PrintStream systemErr = System.err; // get current err final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); System.setErr(new PrintStream(errContent)); final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmd1 = "printwatches"; zkMain.executeLine(cmd1); String cmd2 = "history"; zkMain.executeLine(cmd2); String cmd3 = "redo"; zkMain.executeLine(cmd3); // revert redirect of out/err streams - important step! System.setErr(systemErr); if (errContent.toString().contains("ZooKeeper -server host:port cmd args")) { fail("CLI commands (history, redo, connect, printwatches) display usage info!"); } } // ZOOKEEPER-2467 : Testing negative number for redo command @Test public void testRedoWithNegativeCmdNumber() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String cmd1 = "redo -1"; String result = executeLine(zkMain, cmd1); assertEquals("Command index out of range", result); } private String executeLine(ZooKeeperMain zkMain, String cmd) throws InterruptedException, IOException { // setup redirect out/err streams to get System.in/err, use this // judiciously! final PrintStream systemErr = System.err; // get current err final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); System.setErr(new PrintStream(errContent)); try { zkMain.executeLine(cmd); return errContent.toString().trim(); } finally { // revert redirect of out/err streams - important step! System.setErr(systemErr); } } private static void runCommandExpect(CliCommand command, List expectedResults) throws Exception { String result = runCommandExpect(command); assertTrue(result.contains(StringUtils.joinStrings(expectedResults, LINE_SEPARATOR)), result); } private static String runCommandExpect(CliCommand command) throws CliException { // call command and put result in byteStream ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); PrintStream out = new PrintStream(byteStream); command.setOut(out); command.exec(); return byteStream.toString(); } @Test public void testSortedLs() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); zkMain.executeLine("create /aa1"); zkMain.executeLine("create /aa2"); zkMain.executeLine("create /aa3"); zkMain.executeLine("create /test1"); zkMain.executeLine("create /zk1"); LsCommand cmd = new LsCommand(); cmd.setZk(zk); cmd.parse("ls /".split(" ")); List expected = new ArrayList<>(); expected.add("[aa1, aa2, aa3, test1, zk1, zookeeper]"); runCommandExpect(cmd, expected); } @Test public void testLsrCommand() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); zkMain.executeLine("create /a"); zkMain.executeLine("create /a/b"); zkMain.executeLine("create /a/c"); zkMain.executeLine("create /a/b/d"); zkMain.executeLine("create /a/c/e"); zkMain.executeLine("create /a/f"); LsCommand cmd = new LsCommand(); cmd.setZk(zk); cmd.parse("ls -R /a".split(" ")); List expected = new ArrayList<>(); expected.add("/a"); expected.add("/a/b"); expected.add("/a/c"); expected.add("/a/f"); expected.add("/a/b/d"); expected.add("/a/c/e"); runCommandExpect(cmd, expected); } @Test public void testLsrRootCommand() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); LsCommand cmd = new LsCommand(); cmd.setZk(zk); cmd.parse("ls -R /".split(" ")); List expected = new ArrayList<>(); expected.add("/"); expected.add("/zookeeper"); runCommandExpect(cmd, expected); } @Test public void testLsrLeafCommand() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); zkMain.executeLine("create /b"); zkMain.executeLine("create /b/c"); LsCommand cmd = new LsCommand(); cmd.setZk(zk); cmd.parse("ls -R /b/c".split(" ")); List expected = new ArrayList<>(); expected.add("/b/c"); runCommandExpect(cmd, expected); } @Test public void testLsrNonexistantZnodeCommand() throws Exception { final ZooKeeper zk = createClient(); ZooKeeperMain zkMain = new ZooKeeperMain(zk); zkMain.executeLine("create /b"); zkMain.executeLine("create /b/c"); LsCommand cmd = new LsCommand(); cmd.setZk(zk); cmd.parse("ls -R /b/c/d".split(" ")); try { runCommandExpect(cmd, new ArrayList<>()); fail("Path doesn't exists so, command should fail."); } catch (CliWrapperException e) { assertEquals(KeeperException.Code.NONODE, ((KeeperException) e.getCause()).code()); } } @Test public void testSetAclRecursive() throws Exception { final ZooKeeper zk = createClient(); final byte[] EMPTY = new byte[0]; zk.setData("/", EMPTY, -1); zk.create("/a", EMPTY, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", EMPTY, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/c", EMPTY, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/d", EMPTY, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/e", EMPTY, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String setAclCommand = "setAcl -R /a world:anyone:r"; zkMain.cl.parseCommand(setAclCommand); assertFalse(zkMain.processZKCmd(zkMain.cl)); assertEquals(Ids.READ_ACL_UNSAFE, zk.getACL("/a", new Stat())); assertEquals(Ids.READ_ACL_UNSAFE, zk.getACL("/a/b", new Stat())); assertEquals(Ids.READ_ACL_UNSAFE, zk.getACL("/a/b/c", new Stat())); assertEquals(Ids.READ_ACL_UNSAFE, zk.getACL("/a/d", new Stat())); // /e is unset, its acl should remain the same. assertEquals(Ids.OPEN_ACL_UNSAFE, zk.getACL("/e", new Stat())); } @Test public void testClientReconnectWithZKClientConfig() throws Exception { ZooKeeper zk = null; ZooKeeper newZKClient = null; try { zk = createClient(); ZKClientConfig clientConfig = new ZKClientConfig(); clientConfig.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); CountdownWatcher watcher = new CountdownWatcher(); HostProvider aHostProvider = new StaticHostProvider(new ConnectStringParser(hostPort).getServerAddresses()); newZKClient = new ZooKeeper( hostPort, zk.getSessionTimeout(), watcher, zk.getSessionId(), zk.getSessionPasswd(), false, aHostProvider, clientConfig); watcher.waitForConnected(CONNECTION_TIMEOUT); assertEquals(zk.getSessionId(), newZKClient.getSessionId(), "Old client session id and new clinet session id must be same"); } finally { zk.close(); newZKClient.close(); } } @Test public void testSyncCommand() throws Exception { final ZooKeeper zk = createClient(); SyncCommand cmd = new SyncCommand(); cmd.setZk(zk); cmd.parse("sync /".split(" ")); List expected = new ArrayList<>(); expected.add("Sync is OK"); runCommandExpect(cmd, expected); } @Test public void testInsufficientPermission() throws Exception { final ZooKeeper zk = createClient(); zk.create("/permZNode", "".getBytes(), Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT); ZooKeeperMain zkMain = new ZooKeeperMain(zk); String zNodeToBeCreated = "/permZNode/child1"; String errorMessage = executeLine(zkMain, "create " + zNodeToBeCreated); assertEquals("Insufficient permission : " + zNodeToBeCreated, errorMessage); // Test Get command error message when there is not read access List writeAcl = Arrays.asList(new ACL(ZooDefs.Perms.WRITE, Ids.ANYONE_ID_UNSAFE)); String noReadPermZNodePath = "/noReadPermZNode"; zk.create(noReadPermZNodePath, "newData".getBytes(), writeAcl, CreateMode.PERSISTENT); errorMessage = executeLine(zkMain, "get " + noReadPermZNodePath); assertEquals("Insufficient permission : " + noReadPermZNodePath, errorMessage); } @Test public void testWhoAmIAPI() throws Exception { final ZooKeeper zk = createClient(); // Check who ami without authentication/without any user into the session List clientInfos = zk.whoAmI(); // By default server adds ip as the authentication info assertEquals(1, clientInfos.size()); assertEquals("ip", clientInfos.get(0).getAuthScheme()); // Add one user into the session zk.addAuthInfo("digest", "user1:abcXYZ".getBytes()); clientInfos = zk.whoAmI(); assertEquals(2, clientInfos.size()); ClientInfo user1 = getClientInfos(clientInfos, "user1"); assertEquals("digest", user1.getAuthScheme()); // Add one more user into the session zk.addAuthInfo("digest", "user2:xyzABC".getBytes()); clientInfos = zk.whoAmI(); assertEquals(3, clientInfos.size()); user1 = getClientInfos(clientInfos, "user1"); assertEquals("digest", user1.getAuthScheme()); ClientInfo user2 = getClientInfos(clientInfos, "user2"); assertEquals("digest", user2.getAuthScheme()); } private ClientInfo getClientInfos(List clientInfos, String user) { for (ClientInfo clientInfo : clientInfos) { if (clientInfo.getUser().equals(user)) { return clientInfo; } } throw new AssertionError("User +" + user + " not found"); } @Test public void testWhoAmICLICommand() throws Exception { final ZooKeeper zk = createClient(); WhoAmICommand cmd = new WhoAmICommand(); cmd.setZk(zk); List expectedResults = new ArrayList<>(); expectedResults.add("Auth scheme: User"); expectedResults.add("ip: 127.0.0.1"); // Check who ami without authentication/without any user into the session cmd.parse(new String[] { "whoami" }); String actualResult = runCommandExpect(cmd); assertClientAuthInfo(expectedResults, actualResult); // Add one user into the session zk.addAuthInfo("digest", "user1:abcXYZ".getBytes()); expectedResults.add("digest: user1"); actualResult = runCommandExpect(cmd); assertClientAuthInfo(expectedResults, actualResult); // Add one more user into the session zk.addAuthInfo("digest", "user2:xyzABC".getBytes()); expectedResults.add("digest: user2"); actualResult = runCommandExpect(cmd); assertClientAuthInfo(expectedResults, actualResult); } private void assertClientAuthInfo(List expected, String actual) { expected.forEach(s -> { assertTrue(actual.contains(s), "Expected result part '" + s + "' not present in actual result '" + actual + "' "); }); } @Test public void testWaitForConnection() throws Exception { // get a wrong port number int invalidPort = PortAssignment.unique(); long timeout = 3000L; // millisecond String[] args1 = {"-server", "localhost:" + invalidPort, "-timeout", Long.toString(timeout), "-waitforconnection", "ls", "/"}; long startTime = System.currentTimeMillis(); // try to connect to a non-existing server so as to wait until wait_timeout try { ZooKeeperMain zkMain = new ZooKeeperMain(args1); fail("IOException was expected"); } catch (IOException e) { // do nothing } long endTime = System.currentTimeMillis(); assertTrue(endTime - startTime >= timeout, "ZooKeeperMain does not wait until the specified timeout"); } @Test public void testKeeperExceptionCreateNPE() { // One existing code KeeperException k1 = KeeperException.create(Code.get(NOAUTH.intValue())); assertTrue(k1 instanceof KeeperException.NoAuthException); // One impossible code assertThrows(IllegalArgumentException.class, () -> KeeperException.create(Code.get(Integer.MAX_VALUE))); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_audit_Aud0100644 0000000 0000000 00000000156 15051152474 032640 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/AuditEventTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/AuditEventTest.java0100644 0000000 0000000 00000003502 15051152474 034206 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.zookeeper.audit.AuditEvent.Result; import org.junit.jupiter.api.Test; public class AuditEventTest { @Test public void testFormat() { AuditEvent auditEvent = new AuditEvent(Result.SUCCESS); auditEvent.addEntry(AuditEvent.FieldName.USER, "Value1"); auditEvent.addEntry(AuditEvent.FieldName.OPERATION, "Value2"); String actual = auditEvent.toString(); String expected = "user=Value1\toperation=Value2\tresult=success"; assertEquals(expected, actual); } @Test public void testFormatShouldIgnoreKeyIfValueIsNull() { AuditEvent auditEvent = new AuditEvent(Result.SUCCESS); auditEvent.addEntry(AuditEvent.FieldName.USER, null); auditEvent.addEntry(AuditEvent.FieldName.OPERATION, "Value2"); String actual = auditEvent.toString(); String expected = "operation=Value2\tresult=success"; assertEquals(expected, actual); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_audit_Aud0100644 0000000 0000000 00000000163 15051152474 032636 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/AuditLogPerfReading.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/AuditLogPerfReading0100644 0000000 0000000 00000004105 15051152474 034175 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; /** * Audit log performance reading */ public final class AuditLogPerfReading { // time taken by create operations private long create; // time taken by setData operations private long setData; // time taken by delete operations private long delete; public long getCreate() { return create; } public void setCreate(long create) { this.create = create; } public long getSetData() { return setData; } public void setSetData(long setData) { this.setData = setData; } public long getDelete() { return delete; } public void setDelete(long delete) { this.delete = delete; } public String report() { StringBuilder builder = new StringBuilder(); builder.append("create="); builder.append(create); builder.append(" ms\n"); builder.append("setData="); builder.append(setData); builder.append(" ms\n"); builder.append("delete="); builder.append(delete); builder.append(" ms\n"); return builder.toString(); } @Override public String toString() { return "create=" + create + ", setData=" + setData + ", delete=" + delete; } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_audit_Slf0100644 0000000 0000000 00000000164 15051152474 032652 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/Slf4JAuditLoggerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/Slf4JAuditLoggerTes0100644 0000000 0000000 00000042544 15051152474 034114 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.Op; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKUtil; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.audit.AuditEvent.Result; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.server.util.AuthUtil; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.apache.zookeeper.test.LoggerTestTool; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Slf4JAuditLoggerTest extends QuorumPeerTestBase { private static final Logger LOG = LoggerFactory.getLogger(Slf4JAuditLoggerTest.class); private static int SERVER_COUNT = 3; private static MainThread[] mt; private static ZooKeeper zk; private static ByteArrayOutputStream os; @BeforeAll public static void setUpBeforeClass() throws Exception { System.setProperty(ZKAuditProvider.AUDIT_ENABLE, "true"); System.setProperty("zookeeper.extendedTypesEnabled", "true"); // setup the logger to capture all logs LoggerTestTool loggerTestTool = new LoggerTestTool(Slf4jAuditLogger.class); os = loggerTestTool.getOutputStream(); mt = startQuorum(); zk = ClientBase.createZKClient("127.0.0.1:" + mt[0].getQuorumPeer().getClientPort()); //Verify start audit log here itself String expectedAuditLog = getStartLog(); List logs = readAuditLog(os, SERVER_COUNT); verifyLogs(expectedAuditLog, logs); } @BeforeEach public void setUp() { os.reset(); } @Test public void testCreateAuditLogs() throws KeeperException, InterruptedException, IOException { String path = "/createPath"; zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // success log String createMode = CreateMode.PERSISTENT.toString().toLowerCase(); verifyLog( getAuditLog(AuditConstants.OP_CREATE, path, Result.SUCCESS, null, createMode), readAuditLog(os)); try { zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException exception) { Code code = exception.code(); assertEquals(Code.NODEEXISTS, code); } // Verify create operation log verifyLog( getAuditLog(AuditConstants.OP_CREATE, path, Result.FAILURE, null, createMode), readAuditLog(os)); } @Test public void testCreateWithTtlAuditLogs() throws KeeperException, InterruptedException, IOException { final CreateMode createMode = CreateMode.PERSISTENT_WITH_TTL; final String path = "/createTtlPath"; zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode, null, 3600); // success log verifyLog( getAuditLog(AuditConstants.OP_CREATE, path, Result.SUCCESS, null, createMode.toString().toLowerCase()), readAuditLog(os)); } @Test public void testCreateSeqWithTtlAuditLogs() throws KeeperException, InterruptedException, IOException { final CreateMode createMode = CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL; String path = "/createTtlPath"; path = zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode, null, 3600); // success log verifyLog( getAuditLog(AuditConstants.OP_CREATE, path, Result.SUCCESS, null, createMode.toString().toLowerCase()), readAuditLog(os)); } @Test public void testDeleteAuditLogs() throws InterruptedException, IOException, KeeperException { String path = "/deletePath"; zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); os.reset(); try { zk.delete(path, -100); } catch (KeeperException exception) { Code code = exception.code(); assertEquals(Code.BADVERSION, code); } verifyLog(getAuditLog(AuditConstants.OP_DELETE, path, Result.FAILURE), readAuditLog(os)); zk.delete(path, -1); verifyLog(getAuditLog(AuditConstants.OP_DELETE, path), readAuditLog(os)); } @Test public void testSetDataAuditLogs() throws InterruptedException, IOException, KeeperException { String path = "/setDataPath"; zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); os.reset(); try { zk.setData(path, "newData".getBytes(), -100); } catch (KeeperException exception) { Code code = exception.code(); assertEquals(Code.BADVERSION, code); } verifyLog(getAuditLog(AuditConstants.OP_SETDATA, path, Result.FAILURE), readAuditLog(os)); zk.setData(path, "newdata".getBytes(), -1); verifyLog(getAuditLog(AuditConstants.OP_SETDATA, path), readAuditLog(os)); } @Test public void testSetACLAuditLogs() throws InterruptedException, IOException, KeeperException { ArrayList openAclUnsafe = ZooDefs.Ids.OPEN_ACL_UNSAFE; String path = "/aclPath"; zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); os.reset(); try { zk.setACL(path, openAclUnsafe, -100); } catch (KeeperException exception) { Code code = exception.code(); assertEquals(Code.BADVERSION, code); } verifyLog( getAuditLog(AuditConstants.OP_SETACL, path, Result.FAILURE, ZKUtil.aclToString(openAclUnsafe), null), readAuditLog(os)); zk.setACL(path, openAclUnsafe, -1); verifyLog( getAuditLog(AuditConstants.OP_SETACL, path, Result.SUCCESS, ZKUtil.aclToString(openAclUnsafe), null), readAuditLog(os)); } @Test public void testMultiOperationAuditLogs() throws InterruptedException, KeeperException, IOException { List ops = new ArrayList<>(); String multiop = "/b"; Op create = Op.create(multiop, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Op setData = Op.setData(multiop, "newData".getBytes(), -1); // check does nothing so it is audit logged Op check = Op.check(multiop, -1); Op delete = Op.delete(multiop, -1); String createMode = CreateMode.PERSISTENT.toString().toLowerCase(); ops.add(create); ops.add(setData); ops.add(check); ops.add(delete); zk.multi(ops); List multiOpLogs = readAuditLog(os, 3); // verify that each multi operation success is logged verifyLog(getAuditLog(AuditConstants.OP_CREATE, multiop, Result.SUCCESS, null, createMode), multiOpLogs.get(0)); verifyLog(getAuditLog(AuditConstants.OP_SETDATA, multiop), multiOpLogs.get(1)); verifyLog(getAuditLog(AuditConstants.OP_DELETE, multiop), multiOpLogs.get(2)); ops = new ArrayList<>(); ops.add(create); ops.add(create); try { zk.multi(ops); } catch (KeeperException exception) { Code code = exception.code(); assertEquals(Code.NODEEXISTS, code); } // Verify that multi operation failure is logged, and there is no path // mentioned in the audit log verifyLog(getAuditLog(AuditConstants.OP_MULTI_OP, null, Result.FAILURE), readAuditLog(os)); } @Test public void testEphemralZNodeAuditLogs() throws Exception { String ephemralPath = "/ephemral"; CountdownWatcher watcher2 = new CountdownWatcher(); ZooKeeper zk2 = new ZooKeeper( "127.0.0.1:" + mt[0].getQuorumPeer().getClientPort(), ClientBase.CONNECTION_TIMEOUT, watcher2); watcher2.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk2.create(ephemralPath, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); String session2 = "0x" + Long.toHexString(zk2.getSessionId()); verifyLog(getAuditLog(AuditConstants.OP_CREATE, ephemralPath, Result.SUCCESS, null, CreateMode.EPHEMERAL.toString().toLowerCase(), session2), readAuditLog(os)); zk2.close(); waitForDeletion(zk, ephemralPath); // verify that ephemeral node deletion on session close are captured // in audit log // Because these operations are done by ZooKeeper server itself, // there are no IP user is zkServer user, not any client user verifyLogs(getAuditLog(AuditConstants.OP_DEL_EZNODE_EXP, ephemralPath, Result.SUCCESS, null, null, session2, ZKAuditProvider.getZKUser(), null), readAuditLog(os, SERVER_COUNT)); } private static String getStartLog() { // user=userName operation=ZooKeeperServer start result=success AuditEvent logEvent = ZKAuditProvider.createLogEvent(ZKAuditProvider.getZKUser(), AuditConstants.OP_START, Result.SUCCESS); return logEvent.toString(); } private String getAuditLog(String operation, String znode) { return getAuditLog(operation, znode, Result.SUCCESS); } private String getAuditLog(String operation, String znode, Result result) { return getAuditLog(operation, znode, result, null, null); } private String getAuditLog(String operation, String znode, Result result, String acl, String createMode) { String session = getSession(); return getAuditLog(operation, znode, result, acl, createMode, session); } private String getAuditLog(String operation, String znode, Result result, String acl, String createMode, String session) { String user = getUser(); String ip = getIp(); return getAuditLog(operation, znode, result, acl, createMode, session, user, ip); } private String getAuditLog(String operation, String znode, Result result, String acl, String createMode, String session, String user, String ip) { AuditEvent logEvent = ZKAuditProvider.createLogEvent(user, operation, znode, acl, createMode, session, ip, result); String auditLog = logEvent.toString(); LOG.info("expected audit log for operation '" + operation + "' is '" + auditLog + "'"); return auditLog; } private String getSession() { return "0x" + Long.toHexString(zk.getSessionId()); } private String getUser() { ServerCnxn next = getServerCnxn(); return AuthUtil.getUsers(next.getAuthInfo()); } private String getIp() { ServerCnxn next = getServerCnxn(); InetSocketAddress remoteSocketAddress = next.getRemoteSocketAddress(); InetAddress address = remoteSocketAddress.getAddress(); return address.getHostAddress(); } private ServerCnxn getServerCnxn() { Iterable connections = mt[0].getQuorumPeer() .getActiveServer() .getServerCnxnFactory().getConnections(); return connections.iterator().next(); } private static void verifyLog(String expectedLog, String log) { String searchString = " - "; int logStartIndex = log.indexOf(searchString); String auditLog = log.substring(logStartIndex + searchString.length()); assertTrue(auditLog.endsWith(expectedLog)); } private static void verifyLogs(String expectedLog, List logs) { for (String log : logs) { verifyLog(expectedLog, log); } } private String readAuditLog(ByteArrayOutputStream os) throws IOException { return readAuditLog(os, 1).get(0); } private static List readAuditLog(ByteArrayOutputStream os, int numberOfLogEntry) throws IOException { return readAuditLog(os, numberOfLogEntry, false); } private static List readAuditLog(ByteArrayOutputStream os, int numberOfLogEntry, boolean skipEphemralDeletion) throws IOException { List logs = new ArrayList<>(); LineNumberReader r = new LineNumberReader( new StringReader(os.toString())); String line; while ((line = r.readLine()) != null) { if (skipEphemralDeletion && line.contains(AuditConstants.OP_DEL_EZNODE_EXP)) { continue; } logs.add(line); } os.reset(); assertEquals(numberOfLogEntry, logs.size(), "Expected number of log entries are not generated. Logs are " + logs); return logs; } private static MainThread[] startQuorum() throws IOException { final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); sb.append("4lw.commands.whitelist=*"); sb.append("\n"); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server); sb.append("\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; // start all the servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure all servers started for (int i = 0; i < SERVER_COUNT; i++) { Assertions.assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } return mt; } private void waitForDeletion(ZooKeeper zooKeeper, String path) throws Exception { long elapsedTime = 0; long waitInterval = 10; int timeout = 100; Stat exists = zooKeeper.exists(path, false); while (exists != null && elapsedTime < timeout) { try { Thread.sleep(waitInterval); } catch (InterruptedException e) { Assertions.fail("CurrentEpoch update failed"); } elapsedTime = elapsedTime + waitInterval; exists = zooKeeper.exists(path, false); } Assertions.assertNull(exists, "Node " + path + " not deleted in " + timeout + " ms"); } @AfterAll public static void tearDownAfterClass() { System.clearProperty("zookeeper.extendedTypesEnabled"); System.clearProperty(ZKAuditProvider.AUDIT_ENABLE); for (int i = 0; i < SERVER_COUNT; i++) { try { if (mt[i] != null) { mt[i].shutdown(); } } catch (InterruptedException e) { e.printStackTrace(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_audit_Sta0100644 0000000 0000000 00000000171 15051152474 032653 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/StandaloneServerAuditTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/StandaloneServerAud0100644 0000000 0000000 00000005515 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.LoggerTestTool; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class StandaloneServerAuditTest extends ClientBase { private static ByteArrayOutputStream os; @BeforeAll public static void setup() { System.setProperty(ZKAuditProvider.AUDIT_ENABLE, "true"); LoggerTestTool loggerTestTool = new LoggerTestTool(Slf4jAuditLogger.class); os = loggerTestTool.getOutputStream(); } @AfterAll public static void teardown() { System.clearProperty(ZKAuditProvider.AUDIT_ENABLE); } @Test public void testCreateAuditLog() throws KeeperException, InterruptedException, IOException { final ZooKeeper zk = createClient(); String path = "/createPath"; zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List logs = readAuditLog(os); assertEquals(1, logs.size()); assertTrue(logs.get(0).endsWith("operation=create\tznode=/createPath\tznode_type=persistent\tresult=success")); } private static List readAuditLog(ByteArrayOutputStream os) throws IOException { List logs = new ArrayList<>(); LineNumberReader r = new LineNumberReader( new StringReader(os.toString())); String line; while ((line = r.readLine()) != null) { logs.add(line); } os.reset(); return logs; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_audit_ZKA0100644 0000000 0000000 00000000170 15051152474 032550 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/ZKAuditLoggerPerformance.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/audit/ZKAuditLoggerPerfor0100644 0000000 0000000 00000012532 15051152474 034212 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.audit; import java.io.IOException; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZKAuditLoggerPerformance { private static final Logger LOG = LoggerFactory .getLogger(ZKAuditLoggerPerformance.class); private ZooKeeper zkClient; private String parentPath; private int numberOfRecords; public ZKAuditLoggerPerformance(ZooKeeper zkClient, String parentPath, int numberOfRecords) { this.zkClient = zkClient; this.parentPath = parentPath; this.numberOfRecords = numberOfRecords; } public void create() throws Exception { for (int i = 0; i < numberOfRecords; i++) { zkClient.create(getPath(i), "0123456789".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } public void setData() throws Exception { for (int i = 0; i < numberOfRecords; i++) { zkClient.setData(getPath(i), "9876543210".getBytes(), -1); } } public void delete() throws Exception { for (int i = 0; i < numberOfRecords; i++) { zkClient.delete(getPath(i), -1); } } public AuditLogPerfReading doOperations() throws Exception { AuditLogPerfReading perfReading = new AuditLogPerfReading(); // create long startTime = Time.currentElapsedTime(); create(); perfReading.setCreate(Time.currentElapsedTime() - startTime); // setData startTime = Time.currentElapsedTime(); setData(); perfReading.setSetData(Time.currentElapsedTime() - startTime); // delete startTime = Time.currentElapsedTime(); delete(); perfReading.setDelete(Time.currentElapsedTime() - startTime); return perfReading; } private String getPath(int i) { return parentPath + "zNode" + i; } public static void main(String[] args) { if (args.length != 3) { System.err.println( "USAGE: ZKAuditLoggerPerformance connectionString parentPath numberOfRecords"); System.exit(1); } String cxnString = args[0]; CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zkClient = null; try { zkClient = new ZooKeeper(cxnString, 60000, watcher); watcher.waitForConnected(30000); } catch (InterruptedException | TimeoutException | IOException e) { String msg = "ZooKeeper client can not connect to " + cxnString; logErrorAndExit(e, msg); } String parentPath = args[1]; try { Stat exists = zkClient.exists(parentPath, false); if (exists == null) { System.err.println( "Parent path '" + parentPath + "' must exist."); System.exit(1); } } catch (KeeperException | InterruptedException e1) { String msg = "Error while checking the existence of parent path"; logErrorAndExit(e1, msg); } int recordCount = 0; try { recordCount = Integer.parseInt(args[2]); } catch (NumberFormatException e) { String msg = "Failed to parse '" + args[2] + "' to integer"; LOG.error(msg, e); System.err.println(msg); System.exit(1); } ZKAuditLoggerPerformance auditLoggingPerf = new ZKAuditLoggerPerformance( zkClient, parentPath, recordCount); AuditLogPerfReading doOperations = null; try { doOperations = auditLoggingPerf.doOperations(); } catch (Exception e) { String msg = "Error while doing operations."; LOG.error(msg, e); System.err.println(msg); System.exit(1); } System.out .println("Time taken for " + recordCount + " operations are:"); System.out.println(doOperations.report()); System.exit(0); } private static void logErrorAndExit(Exception e, String msg) { LOG.error(msg, e); System.err.println(msg + ", error=" + e.getMessage()); System.exit(1); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_cli_Comma0100644 0000000 0000000 00000000160 15051152474 032617 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/cli/CommandFactoryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/cli/CommandFactoryTest.ja0100644 0000000 0000000 00000002470 15051152474 034161 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.cli; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; /** * Unit test for {@link CommandFactory}. */ public class CommandFactoryTest { /** * Verify that the {@code CommandFactory} can create a command instance. */ @Test public void testCommandCreation() { CliCommand cliCommand = CommandFactory.getInstance(CommandFactory.Command.CREATE); assertTrue(cliCommand instanceof CreateCommand); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_client_ZK0100644 0000000 0000000 00000000163 15051152474 032621 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/client/ZKClientConfigTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/client/ZKClientConfigTest0100644 0000000 0000000 00000021223 15051152474 034177 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.client; import static org.apache.zookeeper.client.ZKClientConfig.DISABLE_AUTO_WATCH_RESET; import static org.apache.zookeeper.client.ZKClientConfig.ENABLE_CLIENT_SASL_KEY; import static org.apache.zookeeper.client.ZKClientConfig.LOGIN_CONTEXT_NAME_KEY; import static org.apache.zookeeper.client.ZKClientConfig.SECURE_CLIENT; import static org.apache.zookeeper.client.ZKClientConfig.ZK_SASL_CLIENT_USERNAME; import static org.apache.zookeeper.client.ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET; import static org.apache.zookeeper.client.ZKClientConfig.ZOOKEEPER_SERVER_REALM; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class ZKClientConfigTest { private static final File testData = new File(System.getProperty("test.data.dir", "src/test/resources/data")); @BeforeAll public static void init() { if (!testData.exists()) { testData.mkdirs(); } } @Test @Timeout(value = 10) public void testDefaultConfiguration() { Map properties = new HashMap<>(); properties.put(ZK_SASL_CLIENT_USERNAME, "zookeeper1"); properties.put(LOGIN_CONTEXT_NAME_KEY, "Client1"); properties.put(ENABLE_CLIENT_SASL_KEY, "true"); properties.put(ZOOKEEPER_SERVER_REALM, "zookeeper/hadoop.hadoop.com"); properties.put(DISABLE_AUTO_WATCH_RESET, "true"); properties.put(ZOOKEEPER_CLIENT_CNXN_SOCKET, "ClientCnxnSocketNetty"); properties.put(SECURE_CLIENT, "true"); for (Map.Entry e : properties.entrySet()) { System.setProperty(e.getKey(), e.getValue()); } /** * ZKClientConfig should get initialized with system properties */ ZKClientConfig conf = new ZKClientConfig(); for (Map.Entry e : properties.entrySet()) { assertEquals(e.getValue(), conf.getProperty(e.getKey())); } /** * clear properties */ for (Map.Entry e : properties.entrySet()) { System.clearProperty(e.getKey()); } conf = new ZKClientConfig(); /** * test that all the properties are null */ for (Map.Entry e : properties.entrySet()) { String result = conf.getProperty(e.getKey()); assertNull(result); } } @Test @Timeout(value = 10) public void testSystemPropertyValue() { String clientName = "zookeeper1"; System.setProperty(ZK_SASL_CLIENT_USERNAME, clientName); ZKClientConfig conf = new ZKClientConfig(); assertEquals(conf.getProperty(ZK_SASL_CLIENT_USERNAME), clientName); String newClientName = "zookeeper2"; conf.setProperty(ZK_SASL_CLIENT_USERNAME, newClientName); assertEquals(conf.getProperty(ZK_SASL_CLIENT_USERNAME), newClientName); } @Test @Timeout(value = 10) public void testReadConfigurationFile() throws IOException, ConfigException { File file = File.createTempFile("clientConfig", ".conf", testData); file.deleteOnExit(); Properties clientConfProp = new Properties(); clientConfProp.setProperty(ENABLE_CLIENT_SASL_KEY, "true"); clientConfProp.setProperty(ZK_SASL_CLIENT_USERNAME, "ZK"); clientConfProp.setProperty(LOGIN_CONTEXT_NAME_KEY, "MyClient"); clientConfProp.setProperty(ZOOKEEPER_SERVER_REALM, "HADOOP.COM"); clientConfProp.setProperty("dummyProperty", "dummyValue"); OutputStream io = new FileOutputStream(file); try { clientConfProp.store(io, "Client Configurations"); } finally { io.close(); } ZKClientConfig conf = new ZKClientConfig(); conf.addConfiguration(file.getAbsolutePath()); assertEquals(conf.getProperty(ENABLE_CLIENT_SASL_KEY), "true"); assertEquals(conf.getProperty(ZK_SASL_CLIENT_USERNAME), "ZK"); assertEquals(conf.getProperty(LOGIN_CONTEXT_NAME_KEY), "MyClient"); assertEquals(conf.getProperty(ZOOKEEPER_SERVER_REALM), "HADOOP.COM"); assertEquals(conf.getProperty("dummyProperty"), "dummyValue"); // try to delete it now as we have done with the created file, why to // wait for deleteOnExit() deletion file.delete(); } @Test @Timeout(value = 10) public void testSetConfiguration() { ZKClientConfig conf = new ZKClientConfig(); String defaultValue = conf.getProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, ZKClientConfig.ENABLE_CLIENT_SASL_DEFAULT); if (defaultValue.equals("true")) { conf.setProperty(ENABLE_CLIENT_SASL_KEY, "false"); } else { conf.setProperty(ENABLE_CLIENT_SASL_KEY, "true"); } assertTrue(conf.getProperty(ENABLE_CLIENT_SASL_KEY) != defaultValue); } @Test @Timeout(value = 10) public void testIntegerRetrievalFromProperty() { ZKClientConfig conf = new ZKClientConfig(); String prop = "UnSetProperty" + System.currentTimeMillis(); int defaultValue = 100; // property is not set we should get the default value int result = conf.getInt(prop, defaultValue); assertEquals(defaultValue, result); // property is set but can not be parsed to int, we should get the // NumberFormatException conf.setProperty(ZKConfig.JUTE_MAXBUFFER, "InvlaidIntValue123"); try { result = conf.getInt(ZKConfig.JUTE_MAXBUFFER, defaultValue); fail("NumberFormatException is expected"); } catch (NumberFormatException exception) { // do nothing } assertEquals(defaultValue, result); // property is set to an valid int, we should get the set value int value = ZKClientConfig.CLIENT_MAX_PACKET_LENGTH_DEFAULT; conf.setProperty(ZKConfig.JUTE_MAXBUFFER, Integer.toString(value)); result = conf.getInt(ZKConfig.JUTE_MAXBUFFER, defaultValue); assertEquals(value, result); // property is set but with white spaces value = 12345; conf.setProperty(ZKConfig.JUTE_MAXBUFFER, " " + value + " "); result = conf.getInt(ZKConfig.JUTE_MAXBUFFER, defaultValue); assertEquals(value, result); } @Test @Timeout(value = 10) public void testIntegerRetrievalFromHexadecimalProperty() { int hexaValue = 0x3000000; String wrongValue = "0xwel"; int defaultValue = 100; // property is set in hexadecimal value ZKClientConfig zkClientConfig = new ZKClientConfig(); zkClientConfig.setProperty(ZKConfig.JUTE_MAXBUFFER, Integer.toString(hexaValue)); int result = zkClientConfig.getInt(ZKConfig.JUTE_MAXBUFFER, defaultValue); assertEquals(result, hexaValue); zkClientConfig.setProperty(ZKConfig.JUTE_MAXBUFFER, wrongValue); try { result = zkClientConfig.getInt(ZKConfig.JUTE_MAXBUFFER, defaultValue); fail("NumberFormatException is expected"); } catch (NumberFormatException exception) { // do nothing } zkClientConfig.setProperty(ZKConfig.JUTE_MAXBUFFER, " " + hexaValue + " "); result = zkClientConfig.getInt(ZKConfig.JUTE_MAXBUFFER, defaultValue); assertEquals(result, hexaValue); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_At0100644 0000000 0000000 00000000173 15051152474 032654 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/AtomicFileWritingIdiomTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/AtomicFileWritingI0100644 0000000 0000000 00000034157 15051152474 034243 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.AtomicFileWritingIdiom.OutputStreamStatement; import org.apache.zookeeper.common.AtomicFileWritingIdiom.WriterStatement; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class AtomicFileWritingIdiomTest extends ZKTestCase { @Test public void testOutputStreamSuccess(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes(StandardCharsets.US_ASCII)); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); } }); assertFalse(tmp.exists(), "tmp file should have been deleted"); // content changed assertEquals("after", getContent(target)); } @Test public void testWriterSuccess(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); } }); assertFalse(tmp.exists(), "tmp file should have been deleted"); // content changed assertEquals("after", getContent(target)); } @Test public void testOutputStreamFailure(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes(StandardCharsets.US_ASCII)); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // content preserved assertEquals("before", getContent(target)); } @Test public void testWriterFailure(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // content preserved assertEquals("before", getContent(target)); } @Test public void testOutputStreamFailureIOException(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes(StandardCharsets.US_ASCII)); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new IOException(); } }); } catch (IOException ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // content preserved assertEquals("before", getContent(target)); } @Test public void testWriterFailureIOException(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new IOException(); } }); } catch (IOException ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // content preserved assertEquals("before", getContent(target)); } @Test public void testOutputStreamFailureError(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes(StandardCharsets.US_ASCII)); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new Error(); } }); } catch (Error ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // content preserved assertEquals("before", getContent(target)); } @Test public void testWriterFailureError(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); createFile(target, "before"); assertEquals("before", getContent(target)); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new Error(); } }); } catch (Error ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // content preserved assertEquals("before", getContent(target)); } // ************** target file does not exist @Test public void testOutputStreamSuccessNE(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse(target.exists(), "file should not exist"); new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes(StandardCharsets.US_ASCII)); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); } }); // content changed assertEquals("after", getContent(target)); } @Test public void testWriterSuccessNE(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse(target.exists(), "file should not exist"); new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); } }); assertFalse(tmp.exists(), "tmp file should have been deleted"); // content changed assertEquals("after", getContent(target)); } @Test public void testOutputStreamFailureNE(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse(target.exists(), "file should not exist"); boolean exception = false; try { new AtomicFileWritingIdiom(target, new OutputStreamStatement() { @Override public void write(OutputStream os) throws IOException { os.write("after".getBytes(StandardCharsets.US_ASCII)); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // file should not exist assertFalse(target.exists(), "file should not exist"); } @Test public void testWriterFailureNE(@TempDir File tmpdir) throws IOException { File target = new File(tmpdir, "target.txt"); final File tmp = new File(tmpdir, "target.txt.tmp"); target.delete(); assertFalse(target.exists(), "file should not exist"); boolean exception = false; try { new AtomicFileWritingIdiom(target, new WriterStatement() { @Override public void write(Writer os) throws IOException { os.write("after"); os.flush(); assertTrue(tmp.exists(), "implementation of AtomicFileOutputStream has changed, update the test"); throw new RuntimeException(); } }); } catch (RuntimeException ex) { exception = true; } assertFalse(tmp.exists(), "tmp file should have been deleted"); assertTrue(exception, "should have raised an exception"); // file should not exist assertFalse(target.exists(), "file should not exist"); } private String getContent(File file, Charset encoding) throws IOException { StringBuilder result = new StringBuilder(); FileInputStream fis = new FileInputStream(file); byte[] b = new byte[20]; int nb; while ((nb = fis.read(b)) != -1) { result.append(new String(b, 0, nb, encoding)); } fis.close(); return result.toString(); } private String getContent(File file) throws IOException { return getContent(file, StandardCharsets.US_ASCII); } private void createFile(File file, String content) throws IOException { FileOutputStream fos = new FileOutputStream(file); fos.write(content.getBytes(StandardCharsets.US_ASCII)); fos.close(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_BC0100644 0000000 0000000 00000000164 15051152474 032574 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/BCFKSFileLoaderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/BCFKSFileLoaderTes0100644 0000000 0000000 00000016665 15051152474 034011 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.security.KeyStore; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; public class BCFKSFileLoaderTest extends BaseX509ParameterizedTestCase { @ParameterizedTest @MethodSource("data") public void testLoadKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); KeyStore ks = new BCFKSFileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); assertEquals(1, ks.size()); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); new BCFKSFileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword("wrong password") .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); new BCFKSFileLoader.Builder() .setKeyStorePath(path + ".does_not_exist") .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new BCFKSFileLoader.Builder() .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { // Trying to load a PEM file with BCFKS loader should fail String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new BCFKSFileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); KeyStore ts = new BCFKSFileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); assertEquals(1, ts.size()); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); new BCFKSFileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword("wrong password") .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.BCFKS).getAbsolutePath(); new BCFKSFileLoader.Builder() .setTrustStorePath(path + ".does_not_exist") .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new BCFKSFileLoader.Builder() .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { // Trying to load a PEM file with BCFKS loader should fail String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new BCFKSFileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Ba0100644 0000000 0000000 00000000176 15051152474 032635 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/BaseX509ParameterizedTestCase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/BaseX509Parameteri0100644 0000000 0000000 00000012151 15051152474 034012 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.File; import java.io.IOException; import java.security.Security; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.test.ClientBase; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.provider.Arguments; /** * Base class for parameterized unit tests that use X509TestContext for testing * different X509 parameter combinations (CA key type, cert key type, with/without * a password, with/without hostname verification, etc). * * This base class takes care of setting up / cleaning up the test environment, * and caching the X509TestContext objects used by the tests. */ public abstract class BaseX509ParameterizedTestCase extends ZKTestCase { protected static final String KEY_NON_EMPTY_PASSWORD = "pa$$w0rd"; protected static final String KEY_EMPTY_PASSWORD = ""; /** * Default parameters suitable for most subclasses. See example usage * in {@link X509UtilTest}. * @return a stream of parameter combinations to test with. */ public static Stream data() { ArrayList result = new ArrayList<>(); int paramIndex = 0; for (X509KeyType caKeyType : X509KeyType.values()) { for (X509KeyType certKeyType : X509KeyType.values()) { for (String keyPassword : new String[]{KEY_EMPTY_PASSWORD, KEY_NON_EMPTY_PASSWORD}) { result.add(Arguments.of(caKeyType, certKeyType, keyPassword, paramIndex++)); } } } return result.stream(); } /** * Because key generation and writing / deleting files is kind of expensive, we cache the certs and on-disk files * between test cases. None of the test cases modify any of this data so it's safe to reuse between tests. This * caching makes all test cases after the first one for a given parameter combination complete almost instantly. */ protected static Map cachedTestContexts; protected static File tempDir; protected X509TestContext x509TestContext; @BeforeAll public static void setUpBaseClass() throws Exception { Security.addProvider(new BouncyCastleProvider()); cachedTestContexts = new HashMap<>(); tempDir = ClientBase.createEmptyTestDir(); } @AfterAll public static void cleanUpBaseClass() { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); cachedTestContexts.clear(); cachedTestContexts = null; try { FileUtils.deleteDirectory(tempDir); } catch (IOException e) { // ignore } } /** * Init method. See example usage in {@link X509UtilTest}. * * @param paramIndex the index under which the X509TestContext should be cached. * @param contextSupplier a function that creates and returns the X509TestContext * for the current index if one is not already cached. */ protected void init( Integer paramIndex, java.util.function.Supplier contextSupplier) { if (cachedTestContexts.containsKey(paramIndex)) { x509TestContext = cachedTestContexts.get(paramIndex); } else { x509TestContext = contextSupplier.get(); cachedTestContexts.put(paramIndex, x509TestContext); } } protected void init( final X509KeyType caKeyType, final X509KeyType certKeyType, final String keyPassword, final Integer paramIndex) throws Exception { init(paramIndex, () -> { try { return X509TestContext.newBuilder() .setTempDir(tempDir) .setKeyStorePassword(keyPassword) .setKeyStoreKeyType(certKeyType) .setTrustStorePassword(keyPassword) .setTrustStoreKeyType(caKeyType) .build(); } catch (Exception e) { throw new RuntimeException(e); } }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Ce0100644 0000000 0000000 00000000167 15051152474 032642 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/CertificatesToPlayWith.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/CertificatesToPlay0100644 0000000 0000000 00000106566 15051152474 034314 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * Some X509 certificates to test against. *

    * Note: some of these certificates have Japanese Kanji in the "subjectAlt" * field (UTF8). Not sure how realistic that is since international characters * in DNS names usually get translated into ASCII using "xn--" style DNS * entries. "xn--i8s592g.co.jp" is what FireFox actually uses when trying to * find 花子.co.jp. So would the CN in the certificate contain * "xn--i8s592g.co.jp" in ASCII, or "花子.co.jp" in UTF8? (Both?) *

    * * @since 11-Dec-2006 */ public class CertificatesToPlayWith { /** * CN=foo.com */ public static final byte[] X509_FOO = ("-----BEGIN CERTIFICATE-----\n" + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aQMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzE0MVoXDTI4MTEwNTE1MzE0MVowgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQC3jRmEya6sQCkmieULcvx8zz1euCk9\n" + "fSez7BEtki8+dmfMXe3K7sH0lI8f4jJR0rbSCjpmCQLYmzC3NxBKeJOW0RcjNBpO\n" + "c2JlGO9auXv2GDP4IYiXElLJ6VSqc8WvDikv0JmCCWm0Zga+bZbR/EWN5DeEtFdF\n" + "815CLpJZNcYwiYwGy/CVQ7w2TnXlG+mraZOz+owr+cL6J/ZesbdEWfjoS1+cUEhE\n" + "HwlNrAu8jlZ2UqSgskSWlhYdMTAP9CPHiUv9N7FcT58Itv/I4fKREINQYjDpvQcx\n" + "SaTYb9dr5sB4WLNglk7zxDtM80H518VvihTcP7FHL+Gn6g4j5fkI98+S\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=花子.co.jp */ public static final byte[] X509_HANAKO = ("-----BEGIN CERTIFICATE-----\n" + "MIIESzCCAzOgAwIBAgIJAIz+EYMBU6aTMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1NDIxNVoXDTI4MTEwNTE1NDIxNVowgakx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + "cnRpZmljYXRlczEVMBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkB\n" + "FhZqdWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\n" + "MIIBCgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjU\n" + "g4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQc\n" + "wHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t\n" + "7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAn\n" + "AxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArD\n" + "qUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG\n" + "CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV\n" + "HQ4EFgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLS\n" + "rNuzA1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBALJ27i3okV/KvlDp6KMID3gd\n" + "ITl68PyItzzx+SquF8gahMh016NX73z/oVZoVUNdftla8wPUB1GwIkAnGkhQ9LHK\n" + "spBdbRiCj0gMmLCsX8SrjFvr7cYb2cK6J/fJe92l1tg/7Y4o7V/s4JBe/cy9U9w8\n" + "a0ctuDmEBCgC784JMDtT67klRfr/2LlqWhlOEq7pUFxRLbhpquaAHSOjmIcWnVpw\n" + "9BsO7qe46hidgn39hKh1WjKK2VcL/3YRsC4wUi0PBtFW6ScMCuMhgIRXSPU55Rae\n" + "UIlOdPjjr1SUNWGId1rD7W16Scpwnknn310FNxFMHVI0GTGFkNdkilNCFJcIoRA=\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=foo.com, subjectAlt=bar.com */ public static final byte[] X509_FOO_BAR = ("-----BEGIN CERTIFICATE-----\n" + "MIIEXDCCA0SgAwIBAgIJAIz+EYMBU6aRMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzYyOVoXDTI4MTEwNTE1MzYyOVowgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCG\n" + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + "A1LKh6YNPg0wEgYDVR0RBAswCYIHYmFyLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEA\n" + "dQyprNZBmVnvuVWjV42sey/PTfkYShJwy1j0/jcFZR/ypZUovpiHGDO1DgL3Y3IP\n" + "zVQ26uhUsSw6G0gGRiaBDe/0LUclXZoJzXX1qpS55OadxW73brziS0sxRgGrZE/d\n" + "3g5kkio6IED47OP6wYnlmZ7EKP9cqjWwlnvHnnUcZ2SscoLNYs9rN9ccp8tuq2by\n" + "88OyhKwGjJfhOudqfTNZcDzRHx4Fzm7UsVaycVw4uDmhEHJrAsmMPpj/+XRK9/42\n" + "2xq+8bc6HojdtbCyug/fvBZvZqQXSmU8m8IVcMmWMz0ZQO8ee3QkBHMZfCy7P/kr\n" + "VbWx/uETImUu+NZg22ewEw==\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=foo.com, subjectAlt=bar.com, subjectAlt=花子.co.jp * (hanako.co.jp in kanji) */ public static final byte[] X509_FOO_BAR_HANAKO = ("-----BEGIN CERTIFICATE-----\n" + "MIIEajCCA1KgAwIBAgIJAIz+EYMBU6aSMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE1MzgxM1oXDTI4MTEwNTE1MzgxM1owgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxMHZm9vLmNvbTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaOBnjCBmzAJBgNVHRMEAjAAMCwGCWCG\n" + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + "A1LKh6YNPg0wIAYDVR0RBBkwF4IHYmFyLmNvbYIM6Iqx5a2QLmNvLmpwMA0GCSqG\n" + "SIb3DQEBBQUAA4IBAQBeZs7ZIYyKtdnVxVvdLgwySEPOE4pBSXii7XYv0Q9QUvG/\n" + "++gFGQh89HhABzA1mVUjH5dJTQqSLFvRfqTHqLpxSxSWqMHnvRM4cPBkIRp/XlMK\n" + "PlXadYtJLPTgpbgvulA1ickC9EwlNYWnowZ4uxnfsMghW4HskBqaV+PnQ8Zvy3L0\n" + "12c7Cg4mKKS5pb1HdRuiD2opZ+Hc77gRQLvtWNS8jQvd/iTbh6fuvTKfAOFoXw22\n" + "sWIKHYrmhCIRshUNohGXv50m2o+1w9oWmQ6Dkq7lCjfXfUB4wIbggJjpyEtbNqBt\n" + "j4MC2x5rfsLKKqToKmNE7pFEgqwe8//Aar1b+Qj+\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=*.foo.com */ public static final byte[] X509_WILD_FOO = ("-----BEGIN CERTIFICATE-----\n" + "MIIESDCCAzCgAwIBAgIJAIz+EYMBU6aUMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTU1NVoXDTI4MTEwNTE2MTU1NVowgaYx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG\n" + "SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E\n" + "FgQUnxR3vz86tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuz\n" + "A1LKh6YNPg0wDQYJKoZIhvcNAQEFBQADggEBAH0ipG6J561UKUfgkeW7GvYwW98B\n" + "N1ZooWX+JEEZK7+Pf/96d3Ij0rw9ACfN4bpfnCq0VUNZVSYB+GthQ2zYuz7tf/UY\n" + "A6nxVgR/IjG69BmsBl92uFO7JTNtHztuiPqBn59pt+vNx4yPvno7zmxsfI7jv0ww\n" + "yfs+0FNm7FwdsC1k47GBSOaGw38kuIVWqXSAbL4EX9GkryGGOKGNh0qvAENCdRSB\n" + "G9Z6tyMbmfRY+dLSh3a9JwoEcBUso6EWYBakLbq4nG/nvYdYvG9ehrnLVwZFL82e\n" + "l3Q/RK95bnA6cuRClGusLad0e6bjkBzx/VQ3VarDEpAkTLUGVAa0CLXtnyc=\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=*.co.jp */ public static final byte[] X509_WILD_CO_JP = ("-----BEGIN CERTIFICATE-----\n" + "MIIERjCCAy6gAwIBAgIJAIz+EYMBU6aVMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTYzMFoXDTI4MTEwNTE2MTYzMFowgaQx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAxQHKi5jby5qcDElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAMhjr5aCPoyp0R1iroWAfnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2B\n" + "lYho4O84X244QrZTRl8kQbYtxnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRy\n" + "zerA/ZtrlUqf+lKo0uWcocxeRc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY\n" + "07hNKXAb2odnVqgzcYiDkLV8ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8\n" + "BqnGd87xQU3FVZI4tbtkB+KzjD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiV\n" + "JTxpTKqym93whYk93l3ocEe55c0CAwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgB\n" + "hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE\n" + "FJ8Ud78/OrbKOIJCSBYs2tDLXofYMB8GA1UdIwQYMBaAFHua2o+QmU5S0qzbswNS\n" + "yoemDT4NMA0GCSqGSIb3DQEBBQUAA4IBAQA0sWglVlMx2zNGvUqFC73XtREwii53\n" + "CfMM6mtf2+f3k/d8KXhLNySrg8RRlN11zgmpPaLtbdTLrmG4UdAHHYr8O4y2BBmE\n" + "1cxNfGxxechgF8HX10QV4dkyzp6Z1cfwvCeMrT5G/V1pejago0ayXx+GPLbWlNeZ\n" + "S+Kl0m3p+QplXujtwG5fYcIpaGpiYraBLx3Tadih39QN65CnAh/zRDhLCUzKyt9l\n" + "UGPLEUDzRHMPHLnSqT1n5UU5UDRytbjJPXzF+l/+WZIsanefWLsxnkgAuZe/oMMF\n" + "EJMryEzOjg4Tfuc5qM0EXoPcQ/JlheaxZ40p2IyHqbsWV4MRYuFH4bkM\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * CN=*.foo.com, subjectAlt=*.bar.com, subjectAlt=*.花子.co.jp * (*.hanako.co.jp in kanji) */ public static final byte[] X509_WILD_FOO_BAR_HANAKO = ("-----BEGIN CERTIFICATE-----\n" + "MIIEcDCCA1igAwIBAgIJAIz+EYMBU6aWMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTczMVoXDTI4MTEwNTE2MTczMVowgaYx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhNYXJ5bGFuZDEUMBIGA1UEBxMLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoTDmh0dHBjb21wb25lbnRzMRowGAYDVQQLExF0ZXN0IGNl\n" + "cnRpZmljYXRlczESMBAGA1UEAxQJKi5mb28uY29tMSUwIwYJKoZIhvcNAQkBFhZq\n" + "dWxpdXNkYXZpZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + "CgKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pN\n" + "jYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0\n" + "ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1\n" + "JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6\n" + "q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYx\n" + "qJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABo4GiMIGfMAkGA1UdEwQCMAAwLAYJ\n" + "YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud\n" + "DgQWBBSfFHe/Pzq2yjiCQkgWLNrQy16H2DAfBgNVHSMEGDAWgBR7mtqPkJlOUtKs\n" + "27MDUsqHpg0+DTAkBgNVHREEHTAbggkqLmJhci5jb22CDiou6Iqx5a2QLmNvLmpw\n" + "MA0GCSqGSIb3DQEBBQUAA4IBAQBobWC+D5/lx6YhX64CwZ26XLjxaE0S415ajbBq\n" + "DK7lz+Rg7zOE3GsTAMi+ldUYnhyz0wDiXB8UwKXl0SDToB2Z4GOgqQjAqoMmrP0u\n" + "WB6Y6dpkfd1qDRUzI120zPYgSdsXjHW9q2H77iV238hqIU7qCvEz+lfqqWEY504z\n" + "hYNlknbUnR525ItosEVwXFBJTkZ3Yw8gg02c19yi8TAh5Li3Ad8XQmmSJMWBV4XK\n" + "qFr0AIZKBlg6NZZFf/0dP9zcKhzSriW27bY0XfzA6GSiRDXrDjgXq6baRT6YwgIg\n" + "pgJsDbJtZfHnV1nd3M6zOtQPm1TIQpNmMMMd/DPrGcUQerD3\n" + "-----END CERTIFICATE-----\n") .getBytes(); /** * CN=foo.com, CN=bar.com, CN=花子.co.jp */ public static final byte[] X509_THREE_CNS_FOO_BAR_HANAKO = ("-----BEGIN CERTIFICATE-----\n" + "MIIEbzCCA1egAwIBAgIJAIz+EYMBU6aXMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MTk0NVoXDTI4MTEwNTE2MTk0NVowgc0x\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + "cnRpZmljYXRlczEQMA4GA1UEAwwHZm9vLmNvbTEQMA4GA1UEAwwHYmFyLmNvbTEV\n" + "MBMGA1UEAwwM6Iqx5a2QLmNvLmpwMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGOv\n" + "loI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7PlpgpjUg4pNjYGViGjg7zhf\n" + "bjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc8BQcwHf0ZHLN6sD9m2uV\n" + "Sp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTjHE5t7iu1JVjTuE0pcBva\n" + "h2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindxOSAnAxK6q/wGqcZ3zvFB\n" + "TcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfDHArDqUYxqJUlPGlMqrKb\n" + "3fCFiT3eXehwR7nlzQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQf\n" + "Fh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86\n" + "tso4gkJIFiza0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0w\n" + "DQYJKoZIhvcNAQEFBQADggEBAGuZb8ai1NO2j4v3y9TLZvd5s0vh5/TE7n7RX+8U\n" + "y37OL5k7x9nt0mM1TyAKxlCcY+9h6frue8MemZIILSIvMrtzccqNz0V1WKgA+Orf\n" + "uUrabmn+CxHF5gpy6g1Qs2IjVYWA5f7FROn/J+Ad8gJYc1azOWCLQqSyfpNRLSvY\n" + "EriQFEV63XvkJ8JrG62b+2OT2lqT4OO07gSPetppdlSa8NBSKP6Aro9RIX1ZjUZQ\n" + "SpQFCfo02NO0uNRDPUdJx2huycdNb+AXHaO7eXevDLJ+QnqImIzxWiY6zLOdzjjI\n" + "VBMkLHmnP7SjGSQ3XA4ByrQOxfOUTyLyE7NuemhHppuQPxE=\n" + "-----END CERTIFICATE-----\n") .getBytes(); /** * subjectAlt=foo.com */ public static final byte[] X509_NO_CNS_FOO = ("-----BEGIN CERTIFICATE-----\n" + "MIIESjCCAzKgAwIBAgIJAIz+EYMBU6aYMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxEjAQBgNVBAcTCVZhbmNvdXZlcjEWMBQGA1UE\n" + "ChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9uc19zc2wxHTAbBgNVBAMU\n" + "FGRlbW9faW50ZXJtZWRpYXRlX2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZp\n" + "ZXNAZ21haWwuY29tMB4XDTA2MTIxMTE2MjYxMFoXDTI4MTEwNTE2MjYxMFowgZIx\n" + "CzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEUMBIGA1UEBwwLRm9yZXN0\n" + "IEhpbGwxFzAVBgNVBAoMDmh0dHBjb21wb25lbnRzMRowGAYDVQQLDBF0ZXN0IGNl\n" + "cnRpZmljYXRlczElMCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNv\n" + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMhjr5aCPoyp0R1iroWA\n" + "fnEyBMGYWoCidH96yGPFjYLowez5aYKY1IOKTY2BlYho4O84X244QrZTRl8kQbYt\n" + "xnGh4gSCD+Z8gjZ/gMvLUlhqOb+WXPAUHMB39GRyzerA/ZtrlUqf+lKo0uWcocxe\n" + "Rc771KN8cPH3nHZ0rV0Hx4ZAZy6U4xxObe4rtSVY07hNKXAb2odnVqgzcYiDkLV8\n" + "ilvEmoNWMWrp8UBqkTcpEhYhCYp3cTkgJwMSuqv8BqnGd87xQU3FVZI4tbtkB+Kz\n" + "jD9zz8QCDJAfDjZHR03KNQ5mxOgXwxwKw6lGMaiVJTxpTKqym93whYk93l3ocEe5\n" + "5c0CAwEAAaOBkDCBjTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NM\n" + "IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUnxR3vz86tso4gkJIFiza\n" + "0Mteh9gwHwYDVR0jBBgwFoAUe5raj5CZTlLSrNuzA1LKh6YNPg0wEgYDVR0RBAsw\n" + "CYIHZm9vLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAjl78oMjzFdsMy6F1sGg/IkO8\n" + "tF5yUgPgFYrs41yzAca7IQu6G9qtFDJz/7ehh/9HoG+oqCCIHPuIOmS7Sd0wnkyJ\n" + "Y7Y04jVXIb3a6f6AgBkEFP1nOT0z6kjT7vkA5LJ2y3MiDcXuRNMSta5PYVnrX8aZ\n" + "yiqVUNi40peuZ2R8mAUSBvWgD7z2qWhF8YgDb7wWaFjg53I36vWKn90ZEti3wNCw\n" + "qAVqixM+J0qJmQStgAc53i2aTMvAQu3A3snvH/PHTBo+5UL72n9S1kZyNCsVf1Qo\n" + "n8jKTiRriEM+fMFlcgQP284EBFzYHyCXFb9O/hMjK2+6mY9euMB1U1aFFzM/Bg==\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * Intermediate CA for all of these. */ public static final byte[] X509_INTERMEDIATE_CA = ("-----BEGIN CERTIFICATE-----\n" + "MIIEnDCCA4SgAwIBAgIJAJTNwZ6yNa5cMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDAS\n" + "BgNVBAsUC2NvbW1vbnNfc3NsMRUwEwYDVQQDFAxkZW1vX3Jvb3RfY2ExJTAjBgkq\n" + "hkiG9w0BCQEWFmp1bGl1c2Rhdmllc0BnbWFpbC5jb20wHhcNMDYxMTA1MjE0OTMx\n" + "WhcNMDcxMTA1MjE0OTMxWjCBojELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRIw\n" + "EAYDVQQHEwlWYW5jb3V2ZXIxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDASBgNV\n" + "BAsUC2NvbW1vbnNfc3NsMR0wGwYDVQQDFBRkZW1vX2ludGVybWVkaWF0ZV9jYTEl\n" + "MCMGCSqGSIb3DQEJARYWanVsaXVzZGF2aWVzQGdtYWlsLmNvbTCCASIwDQYJKoZI\n" + "hvcNAQEBBQADggEPADCCAQoCggEBAL0S4y3vUO0EM6lwqOEfK8fvrUprIbsikXaG\n" + "XzejcZ+T3l2Dc7t8WtBfRf78i4JypMqJQSijrUicj3H6mOMIReKaXm6ls4hA5d8w\n" + "Lhmgiqsz/kW+gA8SeWGWRN683BD/RbQmzOls6ynBvap9jZlthXWBrSIlPCQoBLXY\n" + "KVaxGzbL4ezaq+XFMKMQSm2uKwVmHHQNbfmZlPsuendBVomb/ked53Ab9IH6dwwN\n" + "qJH9WIrvIzIVEXWlpvQ5MCqozM7u1akU+G8cazr8theGPCaYkzoXnigWua4OjdpV\n" + "9z5ZDknhfBzG1AjapdG07FIirwWWgIyZXqZSD96ikmLtwT29qnsCAwEAAaOB7jCB\n" + "6zAdBgNVHQ4EFgQUe5raj5CZTlLSrNuzA1LKh6YNPg0wgbsGA1UdIwSBszCBsIAU\n" + "rN8eFIvMiRFXXgDqKumS0/W2AhOhgYykgYkwgYYxCzAJBgNVBAYTAkNBMQswCQYD\n" + "VQQIEwJCQzEWMBQGA1UEChMNd3d3LmN1Y2JjLmNvbTEUMBIGA1UECxQLY29tbW9u\n" + "c19zc2wxFTATBgNVBAMUDGRlbW9fcm9vdF9jYTElMCMGCSqGSIb3DQEJARYWanVs\n" + "aXVzZGF2aWVzQGdtYWlsLmNvbYIJAJTNwZ6yNa5bMAwGA1UdEwQFMAMBAf8wDQYJ\n" + "KoZIhvcNAQEFBQADggEBAIB4KMZvHD20pdKajFtMBpL7X4W4soq6EeTtjml3NYa9\n" + "Qc52bsQEGNccKY9afYSBIndaQvFdtmz6HdoN+B8TjYShw2KhyjtKimGLpWYoi1YF\n" + "e4aHdmA/Gp5xk8pZzR18FmooxC9RqBux+NAM2iTFSLgDtGIIj4sg2rbn6Bb6ZlQT\n" + "1rg6VucXCA1629lNfMeNcu7CBNmUKIdaxHR/YJQallE0KfGRiOIWPrPj/VNk0YA6\n" + "XFg0ocjqXJ2/N0N9rWVshMUaXgOh7m4D/5zga5/nuxDU+PoToA6mQ4bV6eCYqZbh\n" + "aa1kQYtR9B4ZiG6pB82qVc2dCqStOH2FAEWos2gAVkQ=\n" + "-----END CERTIFICATE-----\n") .getBytes(); /** * Root CA for all of these. */ public static final byte[] X509_ROOT_CA = ("-----BEGIN CERTIFICATE-----\n" + "MIIEgDCCA2igAwIBAgIJAJTNwZ6yNa5bMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\n" + "VQQGEwJDQTELMAkGA1UECBMCQkMxFjAUBgNVBAoTDXd3dy5jdWNiYy5jb20xFDAS\n" + "BgNVBAsUC2NvbW1vbnNfc3NsMRUwEwYDVQQDFAxkZW1vX3Jvb3RfY2ExJTAjBgkq\n" + "hkiG9w0BCQEWFmp1bGl1c2Rhdmllc0BnbWFpbC5jb20wHhcNMDYxMTA1MjEzNjQz\n" + "WhcNMjYxMTA1MjEzNjQzWjCBhjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRYw\n" + "FAYDVQQKEw13d3cuY3VjYmMuY29tMRQwEgYDVQQLFAtjb21tb25zX3NzbDEVMBMG\n" + "A1UEAxQMZGVtb19yb290X2NhMSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZpZXNA\n" + "Z21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv+OnocmJ\n" + "79UeO2hlCwK+Cle5uZWnU6uwJl+08z5cvebb5tT64WL9+psDbfgUH/Gm9JsuxKTg\n" + "w1tZO/4duIgnaLNSx4HoqaTjwigd/hR3TsoGEPXTCkz1ikgTCOEDvl+iMid6aOrd\n" + "mViE8HhscxKZ+h5FE7oHZyuT6gFoiaIXhFq+xK2w4ZwDz9L+paiwqywyUJJMnh9U\n" + "jKorY+nua81N0oxpIhHPspCanDU4neMzCzYOZyLR/LqV5xORvHcFY84GWMz5hI25\n" + "JbgaWJsYKuCAvNsnQwVoqKPGa7x1fn7x6oGsXJaCVt8weUwIj2xwg1lxMhrNaisH\n" + "EvKpEAEnGGwWKQIDAQABo4HuMIHrMB0GA1UdDgQWBBSs3x4Ui8yJEVdeAOoq6ZLT\n" + "9bYCEzCBuwYDVR0jBIGzMIGwgBSs3x4Ui8yJEVdeAOoq6ZLT9bYCE6GBjKSBiTCB\n" + "hjELMAkGA1UEBhMCQ0ExCzAJBgNVBAgTAkJDMRYwFAYDVQQKEw13d3cuY3VjYmMu\n" + "Y29tMRQwEgYDVQQLFAtjb21tb25zX3NzbDEVMBMGA1UEAxQMZGVtb19yb290X2Nh\n" + "MSUwIwYJKoZIhvcNAQkBFhZqdWxpdXNkYXZpZXNAZ21haWwuY29tggkAlM3BnrI1\n" + "rlswDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAlPl3/8h1LttR1svC\n" + "S8RXbHpAWIT2BEDhGHUNjSmgDQNkE/itf/FCEXh0tlU4bYdtBSOHzflbnzOyIPId\n" + "VZeSWs33V38xDFy6KoVg1gT8JxkLmE5S1vWkpsHIlpw/U6r7KD0Kx9FYx5AiXjw0\n" + "lzz/zlVNuO2U09KIDwDPVG1mBzQiMiSWj1U1pM4KxINkWQwDy/fvu/I983s8lW5z\n" + "hf2WuFNzQN3fcMK5dpBE9NVIu27oYuGYh2sak34v+7T700W2ooBB71qFXtm9P5rl\n" + "Yp9RCEsg3KEEPNTtCBs8fROeXvLDrP0cmBIqwGYDuRNCxFDTOdjv6YGdA8nLOjaH\n" + "2dDk0g==\n" + "-----END CERTIFICATE-----\n").getBytes(); /** * Below is the private key for all the server certificates above (but * not the intermediate CA or the root CA). All of those server certs * came from the same private key. */ public static final String RSA_PUBLIC_MODULUS = "00c863af96823e8ca9d11d62ae85807e713204c1985a80a2747f7ac863c5" + "8d82e8c1ecf9698298d4838a4d8d81958868e0ef385f6e3842b653465f24" + "41b62dc671a1e204820fe67c82367f80cbcb52586a39bf965cf0141cc077" + "f46472cdeac0fd9b6b954a9ffa52a8d2e59ca1cc5e45cefbd4a37c70f1f7" + "9c7674ad5d07c78640672e94e31c4e6dee2bb52558d3b84d29701bda8767" + "56a83371888390b57c8a5bc49a8356316ae9f1406a913729121621098a77" + "713920270312baabfc06a9c677cef1414dc5559238b5bb6407e2b38c3f73" + "cfc4020c901f0e3647474dca350e66c4e817c31c0ac3a94631a895253c69" + "4caab29bddf085893dde5de87047b9e5cd"; public static final String RSA_PUBLIC_EXPONENT = "65537"; public static final String RSA_PRIVATE_EXPONENT = "577abd3295553d0efd4d38c13b62a6d03fa7b7e40cce4f1d5071877d96c6" + "7a39a63f0f7ab21a89db8acae45587b3ef251309a70f74dc1ac02bde68f3" + "8ed658e54e685ed370a18c054449512ea66a2252ed36e82b565b5159ec83" + "f23df40ae189550a183865b25fd77789e960f0d8cedcd72f32d7a66edb4b" + "a0a2baf3fbeb6c7d75f56ef0af9a7cff1c8c7f297d72eae7982164e50a89" + "d450698cf598d39343201094241d2d180a95882a7111e58f4a5bdbc5c125" + "a967dd6ed9ec614c5853e88e4c71e8b682a7cf89cb1d82b6fe78cc865084" + "c8c5dfbb50c939df2b839c977b0245bfa3615e0592b527b1013d5b675ecb" + "44e6b355c1df581f50997175166eef39"; public static final String RSA_PRIME1 = "00fe759c4f0ce8b763880215e82767e7a937297668f4e4b1e119c6b22a3c" + "a2c7b06c547d88d0aa45f645d7d3aeadaf7f8bc594deae0978529592977c" + "b1ff890f05033a9e9e15551cad9fbf9c41d12139ccd99c1c3ac7b2197eff" + "350d236bb900c1440953b64956e0a058ef824a2e16894af175177c77dbe1" + "fef7d8b532608d2513"; public static final String RSA_PRIME2 = "00c99a45878737a4cf73f9896680b75487f1b669b7686a6ba07103856f31" + "db668c2c440c44cdd116f708f631c37a9adf119f5b5cb58ffe3dc62e20af" + "af72693d936dc6bb3c5194996468389c1f094079b81522e94572b4ad7d39" + "529178e9b8ebaeb1f0fdd83b8731c5223f1dea125341d1d64917f6b1a6ae" + "c18d320510d79f859f"; public static final String RSA_EXPONENT1 = "029febf0d4cd41b7011c2465b4a259bd6118486464c247236f44a169d61e" + "47b9062508f674508d5031003ceabc57e714e600d71b2c75d5443db2da52" + "6bb45a374f0537c5a1aab3150764ce93cf386c84346a6bd01f6732e42075" + "c7a0e9e78a9e73b934e7d871d0f75673820089e129a1604438edcbbeb4e2" + "106467da112ce389"; public static final String RSA_EXPONENT2 = "00827e76650c946afcd170038d32e1f8386ab00d6be78d830efe382e45d4" + "7ad4bd04e6231ee22e66740efbf52838134932c9f8c460cdccdec58a1424" + "4427859192fd6ab6c58b74e97941b0eaf577f2a11713af5e5952af3ae124" + "9a9a892e98410dfa2628d9af668a43b5302fb7d496c9b2fec69f595292b6" + "e997f079b0f6314eb7"; public static final String RSA_COEFFICIENT = "00e6b62add350f1a2a8968903ff76c31cf703b0d7326c4a620aef01225b7" + "1640b3f2ec375208c5f7299863f6005b7799b6e529bb1133c8435bf5fdb5" + "a786f6cd8a19ee7094a384e6557c600a38845a0960ddbfd1df18d0af5740" + "001853788f1b5ccbf9affb4c52c9d2efdb8aab0183d86735b32737fb4e79" + "2b8a9c7d91c7d175ae"; /** * subjectAlt=IP Address:127.0.0.1, email:oleg@ural.ru, DNS:localhost.localdomain */ public static final byte[] X509_MULTIPLE_SUBJECT_ALT = ("-----BEGIN CERTIFICATE-----\n" + "MIIDcTCCAtqgAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMQswCQYDVQQGEwJDSDEL\n" + "MAkGA1UECBMCWkgxDzANBgNVBAcTBlp1cmljaDETMBEGA1UEAxMKTXkgVGVzdCBD\n" + "QTAeFw0wODEwMzExMTU3NDVaFw0wOTEwMzExMTU3NDVaMGkxCzAJBgNVBAYTAkNI\n" + "MRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdV\n" + "bmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggG4\n" + "MIIBLAYHKoZIzjgEATCCAR8CgYEA/X9TgR11EilS30qcLuzk5/YRt1I870QAwx4/\n" + "gLZRJmlFXUAiUftZPY1Y+r/F9bow9subVWzXgTuAHTRv8mZgt2uZUKWkn5/oBHsQ\n" + "IsJPu6nX/rfGG/g7V+fGqKYVDwT7g/bTxR7DAjVUE1oWkTL2dfOuK2HXKu/yIgMZ\n" + "ndFIAccCFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QKBgQD34aCF1ps93su8q1w2uFe5\n" + "eZSvu/o66oL5V0wLPQeCZ1FZV4661FlP5nEHEIGAtEkWcSPoTCgWE7fPCTKMyKbh\n" + "PBZ6i1R8jSjgo64eK7OmdZFuo38L+iE1YvH7YnoBJDvMpPG+qFGQiaiD3+Fa5Z8G\n" + "kotmXoB7VSVkAUw7/s9JKgOBhQACgYEA6ogAb/YLM1Rz9AoXKW4LA70VtFf7Mqqp\n" + "divdu9f72WQc1vMKo1YMf3dQadkMfBYRvAAa1IXDnoiFCHhXnVRkWkoUBJyNebLB\n" + "N92CZc0RVFZiMFgQMEh8UldnvAIi4cBk0/YuN3BGl4MzmquVIGrFovdWGqeaveOu\n" + "Xcu4lKGJNiqjODA2MDQGA1UdEQQtMCuHBH8AAAGBDG9sZWdAdXJhbC5ydYIVbG9j\n" + "YWxob3N0LmxvY2FsZG9tYWluMA0GCSqGSIb3DQEBBQUAA4GBAIgEwIoCSRkU3O7K\n" + "USYaOYyfJB9hsvs6YpClvYXiQ/5kPGARP60pM62v4wC7wI9shEizokIAxY2+O3cC\n" + "vwuJhNYaa2FJMELIwRN3XES8X8R6JHWbPaRjaAAPhczuEd8SZYy8yiVLmJTgw0gH\n" + "BSW775NHlkjsscFVgXkNf0PobqJ9\n" + "-----END CERTIFICATE-----").getBytes(); /** * subject CN=repository.infonotary.com (Multiple AVA in RDN). */ public static final byte[] X509_MULTIPLE_VALUE_AVA = ("-----BEGIN CERTIFICATE-----\n" + "MIIFxzCCBK+gAwIBAgIIRO/2+/XA7z4wDQYJKoZIhvcNAQEFBQAwgZwxgZkwCQYD\n" + "VQQGDAJCRzAVBgNVBAoMDkluZm9Ob3RhcnkgUExDMBcGCgmSJomT8ixkARkWCWRv\n" + "bWFpbi1jYTAtBgNVBAMMJmktTm90YXJ5IFRydXN0UGF0aCBWYWxpZGF0ZWQgRG9t\n" + "YWluIENBMC0GA1UECwwmaS1Ob3RhcnkgVHJ1c3RQYXRoIFZhbGlkYXRlZCBEb21h\n" + "aW4gQ0EwHhcNMTIwNjE4MDg1MzIyWhcNMTMwNjE4MDg1MzIyWjCBxjGBwzAJBgNV\n" + "BAYTAkJHMBUGA1UEChMOSW5mb05vdGFyeSBQTEMwFwYDVQQLExBGaWxlcyBSZXBv\n" + "c2l0b3J5MBcGCgmSJomT8ixkARkWCWRvbWFpbi1jYTAgBgNVBAMTGXJlcG9zaXRv\n" + "cnkuaW5mb25vdGFyeS5jb20wIwYJKoZIhvcNAQkBFhZzdXBwb3J0QGluZm9ub3Rh\n" + "cnkuY29tMCYGCSqGSIb3DQEJAhMZcmVwb3NpdG9yeS5pbmZvbm90YXJ5LmNvbTCC\n" + "ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKWjGpgsuz103xVEW/GSg5I\n" + "tBoLbXPxockabOTHnOh0VO2sImycyhBH78nMj+VMexn4y+kdCOuJqAA5LApxyhTA\n" + "KgKlRN7TfoC90IYHjB1dqLMIseg4YM7Oe0e4Z2nL50bHoqXg7OUHaILUQn7ufpYp\n" + "+VCWxyI43KvaR4+HnST3x47wqeArg/rULGV1a16X+46cxq2eoMAcDfostXHaemvz\n" + "vg/Wd5xcWfPbF/oY1/sBXH+AK+peVBMen82+3GtAWtNWbyPE3bT4RG+WgKUyfLZ1\n" + "7A67rX9DkUEVMPQpa50MpLnrRveiM9w6R3mrMHMHbNnwID0Tqfds5zzOi/7cLD0C\n" + "AwEAAaOCAd8wggHbMA4GA1UdDwEB/wQEAwIDuDATBgNVHSUEDDAKBggrBgEFBQcD\n" + "ATBEBggrBgEFBQcBAQQ4MDYwNAYIKwYBBQUHMAGGKGh0dHA6Ly9vY3NwLmluZm9u\n" + "b3RhcnkuY29tL3Jlc3BvbmRlci5jZ2kwgZAGA1UdIASBiDCBhTCBggYMKwYBBAGB\n" + "rQABAgMBMHIwOAYIKwYBBQUHAgEWLGh0dHA6Ly9yZXBvc2l0b3J5LmluZm9ub3Rh\n" + "cnkuY29tL2RvbWFpbi5odG1sMDYGCCsGAQUFBwICMCoaKGktTm90YXJ5IFZhbGlk\n" + "YXRlZCBEb21haW4gQ2VydGlmaWNhdGUgQ1AwgYkGA1UdHwSBgTB/MDWgL6Athito\n" + "dHRwOi8vY3JsLmluZm9ub3RhcnkuY29tL2NybC9kb21haW4tY2EuY3JsgQIBVjBG\n" + "oECgPoY8bGRhcDovL2xkYXAuaW5mb25vdGFyeS5jb20vZGM9ZG9tYWluLWNhLGRj\n" + "PWluZm9ub3RhcnksZGM9Y29tgQIBVjAPBgNVHRMBAf8EBTADAQEAMB0GA1UdDgQW\n" + "BBTImKJZrgV/8n7mHrA0U5EeGsBvbzAfBgNVHSMEGDAWgBTbkorEK+bPdVPpvyVI\n" + "PTxGFnuOoDANBgkqhkiG9w0BAQUFAAOCAQEAhsMbqsqvkbfVaKZ+wDY9rX3EtuDS\n" + "isdAo4AjmWgTtj/aBGiEiXcIGP312x+0JF+mEEQ75ZOKN+WsM8eLB0F4aqylklk7\n" + "6yRYauRXp8dfbXrT3ozxekt0cpSMqbzze456krI12nL+C00V2Iwq96k5J/yZboNW\n" + "Q+ibCaEAHNiL4tGVHSHm6znkWvIuUTbDgDEsm5RdafO27suz5H6zMnV+VE6onN1J\n" + "I1mQmUs44cg2HZAqnFBpDyJQhNYy8M7yGVaRkbfuVaMqiPa+xDPR5v7NFB3kxRq2\n" + "Za2Snopi52eUxDEhJ0MNqFi3Jfj/ZSmJ+XHra5lU4R8lijCAq8SVLZCmIQ==\n" + "-----END CERTIFICATE-----").getBytes(); public static final byte[] S_GOOGLE_COM = ("-----BEGIN CERTIFICATE-----\n" + "MIICpzCCAY+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAXMRUwEwYDVQQDDAwqLmdv\n" + "b2dsZS5jb20wHhcNMTcwMTEzMjI0OTAzWhcNMTgwMTEzMjI0OTAzWjAXMRUwEwYD\n" + "VQQDDAwqLmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\n" + "AQDHuzznuHdJ5PH344xCyGYnUnIRhyLGBKN3WDLLrXWtr/5Sf3Q1qkiMiJ4BINsh\n" + "3Xy0z7VvHmMFlntgHXtkofBUPvTihxsVIypRkCZb5hpsWLotR10AW2JpVl/oxLP2\n" + "227/36X1zKh33fjImLJl9KzGWHLsbCBleQQJOn7YRsNR/QBZO0XGGkN/R2rRfLF3\n" + "rseRfI5gJjZkO0WDxocnf/iieOe0XNR0NAZaY1aozzPmZ/pRrOKYB8OFH7F73WOC\n" + "lPIUGai/byJ9SpbXdLUcMlGhml/4XzcnV/WVRD2P/mlY+xEFG3UEy3ufhNnKFJul\n" + "yjZrOaKbagamqtOyktzkjnerAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBADaMcwVs\n" + "w5kbnoDJzMBJ01H16T4u8k78i/ybwz7u7krgkU0tABXCRj7S/4Dt3jqQ/rV6evj4\n" + "gIJ/2kZUp/PHKkV7CxWI48XBTAQUu9LEpxj0Hut3AtNMD9y/J6cFn2978tWsHFHI\n" + "mYgvclKUDE4WFMvuxfQVuX3RcGQ5i8khEMczY/KVhZYDcLU1PU0GTTJqqrQm59Z4\n" + "T4UyI3OPBR7Nb/kaU1fcgQ083uxRXcNYRMMZnU6c2oFnR+c6pO6aGoXo0C6rgC4R\n" + "pOj4hPvHCfZO2xg6HAdQ7UPALLX8pu5KGot7GRc8yiJ/Q1nBEuiPKKu0MIwQoFgP\n" + "WUux/APTsgLR7Vc=\n" + "-----END CERTIFICATE-----").getBytes(); public static final byte[] IP_1_1_1_1 = ("-----BEGIN CERTIFICATE-----\n" + "MIICwjCCAaqgAwIBAgIBATANBgkqhkiG9w0BAQUFADAaMRgwFgYDVQQDEw9kdW1t\n" + "eS12YWx1ZS5jb20wHhcNMTcwMTEzMjI1MTQ2WhcNMTgwMTEzMjI1MTQ2WjAaMRgw\n" + "FgYDVQQDEw9kdW1teS12YWx1ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\n" + "ggEKAoIBAQDfrapp3jHLp1RlElzpR/4sF9AcTYwMF1N+adkHRoVtmTlJV2lTIAjn\n" + "QLauy0Kkzv8uxmbID3uROgrFNDQ5RxTTCe+kW/vE6Pyzr5Z5ayjSTKeycTE7mAC4\n" + "6ntoCeEWiD593zlfqVo5PuRSp9Kusd+kexNVjC/BETDPa3yXctcH1ouW9GyGItgQ\n" + "u4GhCE8cipKMuTltgfK+Gh/5e9lFG9/F2fD+wHUVBULLR3JOQoqwgk2zAwKDwLuS\n" + "sEd1CBi35+W3apCKN0SEdTKIAxc/R+O/1j2hpOl9yXCCYyveGwJdFXVZtDcx+9/H\n" + "7NXhOdmw/mTXC5fOQGKciEo2SXt8Wp89AgMBAAGjEzARMA8GA1UdEQQIMAaHBAEB\n" + "AQEwDQYJKoZIhvcNAQEFBQADggEBAEAO6CE8twpcfdjk9oMjI5nX9GdC5Wt6+ujd\n" + "tLj0SbXvMKzCLLkveT0xTEzXfyEo8KW2qYYvPP1h83BIxsbR/J3Swt35UQVofv+4\n" + "JgO0FIdgB+iLEcjUh5+60xslylqWE+9bSWm4f06OXuv78tq5NYPZKku/3i4tqLRp\n" + "gH2rTtjX7Q4olSS7GdAgfiA2AnDZAbMtxtsnTt/QFpYQqhlkqHVDwgkGP7C8aMBD\n" + "RH0UIQCPxUkhwhtNmVyHO42r6oHXselZoVU6XRHuhogrGxPf/pzDUvrKBiJhsZQQ\n" + "oEu+pZCwkFLiNwUoq1G2oDpkkdBWB0JcBXB2Txa536ezFFWZYc0=\n" + "-----END CERTIFICATE-----") .getBytes(); public static final byte[] EMAIL_ALT_SUBJECT_NAME = ("-----BEGIN CERTIFICATE-----\n" + "MIIDpTCCAo2gAwIBAgIJANqkMEtlkelbMA0GCSqGSIb3DQEBCwUAMHAxCzAJBgNV\n" + "BAYTAlVTMQswCQYDVQQIDAJWQTERMA8GA1UEBwwIU29tZUNpdHkxEjAQBgNVBAoM\n" + "CU15Q29tcGFueTETMBEGA1UECwwKTXlEaXZpc2lvbjEYMBYGA1UEAwwPd3d3LmNv\n" + "bXBhbnkuY29tMB4XDTE4MDIxNTA3MjkzMFoXDTIwMDIxNTA3MjkzMFowcDELMAkG\n" + "A1UEBhMCVVMxCzAJBgNVBAgMAlZBMREwDwYDVQQHDAhTb21lQ2l0eTESMBAGA1UE\n" + "CgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9uMRgwFgYDVQQDDA93d3cu\n" + "Y29tcGFueS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4v6Oq\n" + "Ua0goRVn1cmT7MOpJhXFm3A70bTpvJIRpEjtGIz99hb34/9r5AYyf1VhKyWmBq24\n" + "XNcOJ59XOlyjjbm2Tl811ufTOdcNbPadoVBmMt4039OSUFpVb4wAw2XPWLTCG2h1\n" + "HNj9GuFHmwcDsg5EiIRrhDGQm2LLLAGoe5PdReoMZCeeWzNWvKTCV14pyRzwQhJL\n" + "F1OmzLYzovbPfB8LZVhQgDbLsh034FScivf2oKDB+NEzAEagNpnrFR0MFLWGYsu1\n" + "nWD5RiZi78HFGiibmhH7QrEPfGlo2eofuUga6naoBUROqkmMCIL8n1HZ/Ur0oGny\n" + "vQCj1AyrfOhuVC53AgMBAAGjQjBAMAsGA1UdDwQEAwIEMDATBgNVHSUEDDAKBggr\n" + "BgEFBQcDATAcBgNVHREEFTATgRFlbWFpbEBleGFtcGxlLmNvbTANBgkqhkiG9w0B\n" + "AQsFAAOCAQEAZ0IsqRrsEmJ6Fa9Yo6PQtrKJrejN2TTDddVgyLQdokzWh/25JFad\n" + "NCMYPH5KjTUyKf96hJDlDayjbKk1PMMhSZMU5OG9NOuGMH/dQttruG1ojse7KIKg\n" + "yHDQrfq5Exxgfa7CMHRKAoTCY7JZhSLyVbTMVhmGfuUDad/RA86ZisXycp0ZmS97\n" + "qDkAmzFL0sL0ZUWNNUh4ZUWvCUZwiuN08z70NjGqXMTDCf68p3SYxbII0xTfScgf\n" + "aQ/A/hD7IbGGTexeoTwpEj01DNvefbQV6//neo32/R5XD0D5jn3TCgZcMThA6H3a\n" + "VkEghVg+s7uMfL/UEebOBQWXQJ/uVoknMA==\n" + "-----END CERTIFICATE-----").getBytes(); } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Fi0100644 0000000 0000000 00000000166 15051152474 032650 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/FileChangeWatcherTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/FileChangeWatcherT0100644 0000000 0000000 00000025204 15051152474 034172 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileChangeWatcherTest extends ZKTestCase { private static File tempDir; private static File tempFile; private static final Logger LOG = LoggerFactory.getLogger(FileChangeWatcherTest.class); private static final long FS_TIMEOUT = 30000L; @BeforeAll public static void createTempFile() throws IOException { tempDir = ClientBase.createEmptyTestDir(); tempFile = File.createTempFile("zk_test_", "", tempDir); tempFile.deleteOnExit(); } @AfterAll public static void cleanupTempDir() { try { FileUtils.deleteDirectory(tempDir); } catch (IOException e) { // ignore } } @Test public void testCallbackWorksOnFileChanges() throws IOException, InterruptedException { FileChangeWatcher watcher = null; try { final List> events = new ArrayList<>(); watcher = new FileChangeWatcher(tempDir.toPath(), event -> { LOG.info("Got an update: {} {}", event.kind(), event.context()); // Filter out the extra ENTRY_CREATE events that are // sometimes seen at the start. Even though we create the watcher // after the file exists, sometimes we still get a create event. if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind())) { return; } synchronized (events) { events.add(event); events.notifyAll(); } }); watcher.start(); watcher.waitForState(FileChangeWatcher.State.RUNNING); Thread.sleep(1000L); // TODO hack for (int i = 0; i < 3; i++) { LOG.info("Modifying file, attempt {}", (i + 1)); FileUtils.writeStringToFile(tempFile, "Hello world " + i + "\n", StandardCharsets.UTF_8, true); synchronized (events) { if (events.size() < i + 1) { events.wait(FS_TIMEOUT); } assertEquals(i + 1, events.size(), "Wrong number of events"); WatchEvent event = events.get(i); assertEquals(StandardWatchEventKinds.ENTRY_MODIFY, event.kind()); assertEquals(tempFile.getName(), event.context().toString()); } } } finally { if (watcher != null) { watcher.stop(); watcher.waitForState(FileChangeWatcher.State.STOPPED); } } } @Test public void testCallbackWorksOnFileTouched() throws IOException, InterruptedException { FileChangeWatcher watcher = null; try { final List> events = new ArrayList<>(); watcher = new FileChangeWatcher(tempDir.toPath(), event -> { LOG.info("Got an update: {} {}", event.kind(), event.context()); // Filter out the extra ENTRY_CREATE events that are // sometimes seen at the start. Even though we create the watcher // after the file exists, sometimes we still get a create event. if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind())) { return; } synchronized (events) { events.add(event); events.notifyAll(); } }); watcher.start(); watcher.waitForState(FileChangeWatcher.State.RUNNING); Thread.sleep(1000L); // TODO hack LOG.info("Touching file"); FileUtils.touch(tempFile); synchronized (events) { if (events.isEmpty()) { events.wait(FS_TIMEOUT); } assertFalse(events.isEmpty()); WatchEvent event = events.get(0); assertEquals(StandardWatchEventKinds.ENTRY_MODIFY, event.kind()); assertEquals(tempFile.getName(), event.context().toString()); } } finally { if (watcher != null) { watcher.stop(); watcher.waitForState(FileChangeWatcher.State.STOPPED); } } } @Test public void testCallbackWorksOnFileAdded() throws IOException, InterruptedException { FileChangeWatcher watcher = null; try { final List> events = new ArrayList<>(); watcher = new FileChangeWatcher(tempDir.toPath(), event -> { LOG.info("Got an update: {} {}", event.kind(), event.context()); synchronized (events) { events.add(event); events.notifyAll(); } }); watcher.start(); watcher.waitForState(FileChangeWatcher.State.RUNNING); Thread.sleep(1000L); // TODO hack File tempFile2 = File.createTempFile("zk_test_", "", tempDir); tempFile2.deleteOnExit(); synchronized (events) { if (events.isEmpty()) { events.wait(FS_TIMEOUT); } assertFalse(events.isEmpty()); WatchEvent event = events.get(0); assertEquals(StandardWatchEventKinds.ENTRY_CREATE, event.kind()); assertEquals(tempFile2.getName(), event.context().toString()); } } finally { if (watcher != null) { watcher.stop(); watcher.waitForState(FileChangeWatcher.State.STOPPED); } } } @Test public void testCallbackWorksOnFileDeleted() throws IOException, InterruptedException { FileChangeWatcher watcher = null; try { final List> events = new ArrayList<>(); watcher = new FileChangeWatcher(tempDir.toPath(), event -> { LOG.info("Got an update: {} {}", event.kind(), event.context()); // Filter out the extra ENTRY_CREATE events that are // sometimes seen at the start. Even though we create the watcher // after the file exists, sometimes we still get a create event. if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind())) { return; } synchronized (events) { events.add(event); events.notifyAll(); } }); watcher.start(); watcher.waitForState(FileChangeWatcher.State.RUNNING); Thread.sleep(1000L); // TODO hack tempFile.delete(); synchronized (events) { if (events.isEmpty()) { events.wait(FS_TIMEOUT); } assertFalse(events.isEmpty()); WatchEvent event = events.get(0); assertEquals(StandardWatchEventKinds.ENTRY_DELETE, event.kind()); assertEquals(tempFile.getName(), event.context().toString()); } } finally { if (watcher != null) { watcher.stop(); watcher.waitForState(FileChangeWatcher.State.STOPPED); } } } @Test public void testCallbackErrorDoesNotCrashWatcherThread() throws IOException, InterruptedException { FileChangeWatcher watcher = null; try { final AtomicInteger callCount = new AtomicInteger(0); watcher = new FileChangeWatcher(tempDir.toPath(), event -> { LOG.info("Got an update: {} {}", event.kind(), event.context()); int oldValue; synchronized (callCount) { oldValue = callCount.getAndIncrement(); callCount.notifyAll(); } if (oldValue == 0) { throw new RuntimeException("This error should not crash the watcher thread"); } }); watcher.start(); watcher.waitForState(FileChangeWatcher.State.RUNNING); Thread.sleep(1000L); // TODO hack LOG.info("Modifying file"); FileUtils.writeStringToFile(tempFile, "Hello world\n", StandardCharsets.UTF_8, true); synchronized (callCount) { while (callCount.get() == 0) { callCount.wait(FS_TIMEOUT); } } LOG.info("Modifying file again"); FileUtils.writeStringToFile(tempFile, "Hello world again\n", StandardCharsets.UTF_8, true); synchronized (callCount) { if (callCount.get() == 1) { callCount.wait(FS_TIMEOUT); } } // The value of callCount can exceed 1 only if the callback thread // survives the exception thrown by the first callback. assertTrue(callCount.get() > 1); } finally { if (watcher != null) { watcher.stop(); watcher.waitForState(FileChangeWatcher.State.STOPPED); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Fi0100644 0000000 0000000 00000000206 15051152474 032643 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/FileKeyStoreLoaderBuilderProviderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/FileKeyStoreLoader0100644 0000000 0000000 00000004125 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class FileKeyStoreLoaderBuilderProviderTest extends ZKTestCase { @Test public void testGetBuilderForJKSFileType() { FileKeyStoreLoader.Builder builder = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(KeyStoreFileType.JKS); assertTrue(builder instanceof JKSFileLoader.Builder); } @Test public void testGetBuilderForPEMFileType() { FileKeyStoreLoader.Builder builder = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(KeyStoreFileType.PEM); assertTrue(builder instanceof PEMFileLoader.Builder); } @Test public void testGetBuilderForPKCS12FileType() { FileKeyStoreLoader.Builder builder = FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(KeyStoreFileType.PKCS12); assertTrue(builder instanceof PKCS12FileLoader.Builder); } @Test public void testGetBuilderForNullFileType() { assertThrows(NullPointerException.class, () -> { FileKeyStoreLoaderBuilderProvider.getBuilderForKeyStoreFileType(null); }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_JK0100644 0000000 0000000 00000000162 15051152474 032612 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/JKSFileLoaderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/JKSFileLoaderTest.0100644 0000000 0000000 00000016023 15051152474 034036 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.security.KeyStore; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; public class JKSFileLoaderTest extends BaseX509ParameterizedTestCase { @ParameterizedTest @MethodSource("data") public void testLoadKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); KeyStore ks = new JKSFileLoader.Builder().setKeyStorePath(path).setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); assertEquals(1, ks.size()); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); new JKSFileLoader.Builder().setKeyStorePath(path).setKeyStorePassword("wrong password").build().loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); new JKSFileLoader.Builder().setKeyStorePath(path + ".does_not_exist").setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new JKSFileLoader.Builder().setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { // Trying to load a PEM file with JKS loader should fail String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new JKSFileLoader.Builder().setKeyStorePath(path).setKeyStorePassword(x509TestContext.getKeyStorePassword()).build().loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); KeyStore ts = new JKSFileLoader.Builder().setTrustStorePath(path).setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); assertEquals(1, ts.size()); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); new JKSFileLoader.Builder().setTrustStorePath(path).setTrustStorePassword("wrong password").build().loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); new JKSFileLoader.Builder().setTrustStorePath(path + ".does_not_exist").setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new JKSFileLoader.Builder().setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { // Trying to load a PEM file with JKS loader should fail String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new JKSFileLoader.Builder().setTrustStorePath(path).setTrustStorePassword(x509TestContext.getTrustStorePassword()).build().loadTrustStore(); }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Ke0100644 0000000 0000000 00000000165 15051152474 032650 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/KeyStoreFileTypeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/KeyStoreFileTypeTe0100644 0000000 0000000 00000011734 15051152474 034246 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class KeyStoreFileTypeTest extends ZKTestCase { @Test public void testGetPropertyValue() { assertEquals("PEM", KeyStoreFileType.PEM.getPropertyValue()); assertEquals("JKS", KeyStoreFileType.JKS.getPropertyValue()); assertEquals("PKCS12", KeyStoreFileType.PKCS12.getPropertyValue()); assertEquals("BCFKS", KeyStoreFileType.BCFKS.getPropertyValue()); } @Test public void testFromPropertyValue() { assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromPropertyValue("PEM")); assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromPropertyValue("JKS")); assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromPropertyValue("PKCS12")); assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromPropertyValue("BCFKS")); assertNull(KeyStoreFileType.fromPropertyValue("")); assertNull(KeyStoreFileType.fromPropertyValue(null)); } @Test public void testFromPropertyValueIgnoresCase() { assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromPropertyValue("pem")); assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromPropertyValue("jks")); assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromPropertyValue("pkcs12")); assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromPropertyValue("bcfks")); assertNull(KeyStoreFileType.fromPropertyValue("")); assertNull(KeyStoreFileType.fromPropertyValue(null)); } @Test public void testFromPropertyValueThrowsOnBadPropertyValue() { assertThrows(IllegalArgumentException.class, () -> { KeyStoreFileType.fromPropertyValue("foobar"); }); } @Test public void testFromFilename() { assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromFilename("mykey.jks")); assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.jks")); assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromFilename("mykey.pem")); assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.pem")); assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromFilename("mykey.p12")); assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.p12")); assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromFilename("mykey.bcfks")); assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromFilename("/path/to/key/dir/mykey.bcfks")); } @Test public void testFromFilenameThrowsOnBadFileExtension() { assertThrows(IllegalArgumentException.class, () -> { KeyStoreFileType.fromFilename("prod.key"); }); } @Test public void testFromPropertyValueOrFileName() { // Property value takes precedence if provided assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromPropertyValueOrFileName("JKS", "prod.key")); assertEquals(KeyStoreFileType.PEM, KeyStoreFileType.fromPropertyValueOrFileName("PEM", "prod.key")); assertEquals(KeyStoreFileType.PKCS12, KeyStoreFileType.fromPropertyValueOrFileName("PKCS12", "prod.key")); assertEquals(KeyStoreFileType.BCFKS, KeyStoreFileType.fromPropertyValueOrFileName("BCFKS", "prod.key")); // Falls back to filename detection if no property value assertEquals(KeyStoreFileType.JKS, KeyStoreFileType.fromPropertyValueOrFileName("", "prod.jks")); } @Test public void testFromPropertyValueOrFileNameThrowsOnBadPropertyValue() { assertThrows(IllegalArgumentException.class, () -> { KeyStoreFileType.fromPropertyValueOrFileName("foobar", "prod.jks"); }); } @Test public void testFromPropertyValueOrFileNameThrowsOnBadFileExtension() { assertThrows(IllegalArgumentException.class, () -> { KeyStoreFileType.fromPropertyValueOrFileName("", "prod.key"); }); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/NetUtilsTest.java0100644 0000000 0000000 00000011306 15051152474 034070 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.net.InetSocketAddress; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class NetUtilsTest extends ZKTestCase { private Integer port = 1234; private String v4addr = "127.0.0.1"; private String v6addr = "[0:0:0:0:0:0:0:1]"; private String v6addr2 = "[2600:0:0:0:0:0:0:0]"; private String v4local = v4addr + ":" + port.toString(); private String v6local = v6addr + ":" + port.toString(); private String v6ext = v6addr2 + ":" + port.toString(); @Test public void testFormatInetAddrGoodIpv4() { InetSocketAddress isa = new InetSocketAddress(v4addr, port); assertEquals(v4local, NetUtils.formatInetAddr(isa)); } @Test public void testFormatInetAddrGoodIpv6Local() { // Have to use the expanded address here, hence not using v6addr in instantiation InetSocketAddress isa = new InetSocketAddress("::1", port); assertEquals(v6local, NetUtils.formatInetAddr(isa)); } @Test public void testFormatInetAddrGoodIpv6Ext() { // Have to use the expanded address here, hence not using v6addr in instantiation InetSocketAddress isa = new InetSocketAddress("2600::", port); assertEquals(v6ext, NetUtils.formatInetAddr(isa)); } @Test public void testFormatInetAddrGoodHostname() { InetSocketAddress isa = new InetSocketAddress("localhost", 1234); assertThat(NetUtils.formatInetAddr(isa), equalTo("localhost:1234")); } @Test public void testFormatAddrUnresolved() { InetSocketAddress isa = InetSocketAddress.createUnresolved("doesnt.exist.com", 1234); assertEquals("doesnt.exist.com:1234", NetUtils.formatInetAddr(isa)); } @Test public void tetGetIPV6HostAndPort_WhenHostDoesNotEndWithBracket() { assertThrows(IllegalArgumentException.class, () -> { NetUtils.getIPV6HostAndPort("[2001:0db8:85a3:0000:0000:8a2e:0370:7334:443"); }); } @Test public void tetGetIPV6HostAndPort_WhenNoPortAfterColon() { assertThrows(IllegalArgumentException.class, () -> { NetUtils.getIPV6HostAndPort("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:"); }); } @Test public void tetGetIPV6HostAndPort_WhenPortIsNotSeparatedProperly() { assertThrows(IllegalArgumentException.class, () -> { NetUtils.getIPV6HostAndPort("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]2181"); }); } @Test public void tetGetIPV6HostAndPort_WhenHostIsEmpty() { assertThrows(IllegalArgumentException.class, () -> { NetUtils.getIPV6HostAndPort("[]:2181"); }); } @Test public void tetGetIPV6HostAndPort_EmptyStringArrayIfDoesNotStartWithBracket() { String[] ipv6HostAndPort = NetUtils.getIPV6HostAndPort("2001:0db8:85a3:0000:0000:8a2e:0370:7334]"); assertEquals(0, ipv6HostAndPort.length); } @Test public void tetGetIPV6HostAndPort_ReturnHostPort() { String[] ipv6HostAndPort = NetUtils.getIPV6HostAndPort("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2181"); assertEquals(2, ipv6HostAndPort.length); assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", ipv6HostAndPort[0]); assertEquals("2181", ipv6HostAndPort[1]); } @Test public void tetGetIPV6HostAndPort_ReturnHostPortPort() { String[] ipv6HostAndPort = NetUtils.getIPV6HostAndPort("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2181:3181"); assertEquals(2, ipv6HostAndPort.length); assertEquals("2001:0db8:85a3:0000:0000:8a2e:0370:7334", ipv6HostAndPort[0]); assertEquals("2181:3181", ipv6HostAndPort[1]); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_PE0100644 0000000 0000000 00000000162 15051152474 032612 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PEMFileLoaderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PEMFileLoaderTest.0100644 0000000 0000000 00000016035 15051152474 034033 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.security.KeyStore; import java.security.KeyStoreException; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; public class PEMFileLoaderTest extends BaseX509ParameterizedTestCase { @ParameterizedTest @MethodSource("data") public void testLoadKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); KeyStore ks = new PEMFileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); assertEquals(1, ks.size()); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new PEMFileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword("wrong password") .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new PEMFileLoader.Builder() .setKeyStorePath(path + ".does_not_exist") .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new PEMFileLoader.Builder() .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(KeyStoreException.class, () -> { // Trying to load a JKS file with PEM loader should fail String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); new PEMFileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); KeyStore ts = new PEMFileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); assertEquals(1, ts.size()); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new PEMFileLoader.Builder() .setTrustStorePath(path + ".does_not_exist") .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new PEMFileLoader.Builder() .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Trying to load a JKS file with PEM loader should fail String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(); KeyStore ts = new PEMFileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); assertEquals(0, ts.size()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_PK0100644 0000000 0000000 00000000165 15051152474 032623 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PKCS12FileLoaderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PKCS12FileLoaderTe0100644 0000000 0000000 00000017402 15051152474 033667 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.security.KeyStore; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; public class PKCS12FileLoaderTest extends BaseX509ParameterizedTestCase { @ParameterizedTest @MethodSource("data") public void testLoadKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); KeyStore ks = new PKCS12FileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); assertEquals(1, ks.size()); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); new PKCS12FileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword("wrong password") .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); new PKCS12FileLoader.Builder() .setKeyStorePath(path + ".does_not_exist") .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new PKCS12FileLoader.Builder() .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadKeyStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { // Trying to load a PEM file with PKCS12 loader should fail String path = x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new PKCS12FileLoader.Builder() .setKeyStorePath(path) .setKeyStorePassword(x509TestContext.getKeyStorePassword()) .build() .loadKeyStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); KeyStore ts = new PKCS12FileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); assertEquals(1, ts.size()); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(Exception.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); new PKCS12FileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword("wrong password") .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(); new PKCS12FileLoader.Builder() .setTrustStorePath(path + ".does_not_exist") .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithNullFilePath( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { new PKCS12FileLoader.Builder() .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } @ParameterizedTest @MethodSource("data") public void testLoadTrustStoreWithWrongFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { // Trying to load a PEM file with PKCS12 loader should fail String path = x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(); new PKCS12FileLoader.Builder() .setTrustStorePath(path) .setTrustStorePassword(x509TestContext.getTrustStorePassword()) .build() .loadTrustStore(); }); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PathTrieTest.java0100644 0000000 0000000 00000012053 15051152474 034041 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class PathTrieTest { private PathTrie pathTrie; @BeforeEach public void before() { this.pathTrie = new PathTrie(); } @Test public void addNullPath() { assertThrows(NullPointerException.class, () -> { this.pathTrie.addPath(null); }); } @Test public void addIllegalPath() { assertThrows(IllegalArgumentException.class, () -> { this.pathTrie.addPath(""); }); } @Test public void addPathToRoot() { this.pathTrie.addPath("node1"); assertTrue(this.pathTrie.existsNode("/node1")); } @Test public void addPathToRootLeaves() { this.pathTrie.addPath("node1"); this.pathTrie.addPath("node1/node2"); this.pathTrie.addPath("node1/node3"); assertTrue(this.pathTrie.existsNode("/node1")); assertTrue(this.pathTrie.existsNode("/node1/node2")); assertTrue(this.pathTrie.existsNode("/node1/node3")); } @Test public void deleteNullPath() { assertThrows(NullPointerException.class, () -> { this.pathTrie.deletePath(null); }); } @Test public void deleteIllegalPath() { assertThrows(IllegalArgumentException.class, () -> { this.pathTrie.deletePath(""); }); } @Test public void deletePathFromRoot() { this.pathTrie.addPath("node1"); this.pathTrie.deletePath("node1"); assertFalse(this.pathTrie.existsNode("/node1")); } @Test public void deletePathFromRootLeaves() { this.pathTrie.addPath("node1"); this.pathTrie.addPath("node1/node2"); this.pathTrie.addPath("node1/node3"); this.pathTrie.deletePath("node1/node3"); assertTrue(this.pathTrie.existsNode("/node1")); assertTrue(this.pathTrie.existsNode("/node1/node2")); assertFalse(this.pathTrie.existsNode("/node1/node3")); this.pathTrie.deletePath("node1/node2"); assertTrue(this.pathTrie.existsNode("/node1")); assertFalse(this.pathTrie.existsNode("/node1/node2")); this.pathTrie.deletePath("node1"); assertFalse(this.pathTrie.existsNode("/node1")); } @Test public void deletePathDoesNotExist() { this.pathTrie.addPath("node1"); this.pathTrie.addPath("node1/node2"); this.pathTrie.deletePath("node1/node3"); assertTrue(this.pathTrie.existsNode("/node1")); assertTrue(this.pathTrie.existsNode("/node1/node2")); } @Test public void deleteRootPath() { this.pathTrie.addPath("node1"); this.pathTrie.addPath("node1/node2"); this.pathTrie.addPath("node1/node3"); // Nodes are only removed from the trie if they are a leaf node this.pathTrie.deletePath("node1"); assertTrue(this.pathTrie.existsNode("/node1")); assertTrue(this.pathTrie.existsNode("/node1/node2")); assertTrue(this.pathTrie.existsNode("/node1/node3")); } @Test public void findMaxPrefixNullPath() { assertThrows(NullPointerException.class, () -> { this.pathTrie.findMaxPrefix(null); }); } @Test public void findMaxPrefixRootPath() { assertEquals("/", this.pathTrie.findMaxPrefix("/")); } @Test public void findMaxPrefixChildren() { this.pathTrie.addPath("node1"); this.pathTrie.addPath("node1/node2"); this.pathTrie.addPath("node1/node3"); assertEquals("/node1", this.pathTrie.findMaxPrefix("/node1")); assertEquals("/node1/node2", this.pathTrie.findMaxPrefix("/node1/node2")); assertEquals("/node1/node3", this.pathTrie.findMaxPrefix("/node1/node3")); } @Test public void findMaxPrefixChildrenPrefix() { this.pathTrie.addPath("node1"); assertEquals("/node1", this.pathTrie.findMaxPrefix("/node1/node2")); assertEquals("/node1", this.pathTrie.findMaxPrefix("/node1/node3")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Pa0100644 0000000 0000000 00000000156 15051152474 032651 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PathUtilsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/PathUtilsTest.java0100644 0000000 0000000 00000011552 15051152474 034241 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class PathUtilsTest extends ZKTestCase { @Test public void testValidatePath_ValidPath() { PathUtils.validatePath("/this is / a valid/path"); } @Test public void testValidatePath_Null() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath(null); }); } @Test public void testValidatePath_EmptyString() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath(""); }); } @Test public void testValidatePath_NotAbsolutePath() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("not/valid"); }); } @Test public void testValidatePath_EndsWithSlash() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/ends/with/slash/"); }); } @Test public void testValidatePath_ContainsNullCharacter() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\u0000"); }); } @Test public void testValidatePath_DoubleSlash() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/double//slash"); }); } @Test public void testValidatePath_SinglePeriod() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/single/./period"); }); } @Test public void testValidatePath_DoublePeriod() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/double/../period"); }); } @Test public void testValidatePath_NameContainingPeriod() { // A period that isn't on its own is ok PathUtils.validatePath("/name/with.period."); } @Test public void testValidatePath_0x01() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\u0001"); }); } @Test public void testValidatePath_0x1F() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\u001F"); }); } @Test // The first allowable character public void testValidatePath_0x20() { PathUtils.validatePath("/test\u0020"); } @Test public void testValidatePath_0x7e() { // The last valid ASCII character PathUtils.validatePath("/test\u007e"); } @Test public void testValidatePath_0x7f() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\u007f"); }); } @Test public void testValidatePath_0x9f() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\u009f"); }); } @Test public void testValidatePath_ud800() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\ud800"); }); } @Test public void testValidatePath_uf8ff() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\uf8ff"); }); } @Test public void testValidatePath_HighestAllowableChar() { PathUtils.validatePath("/test\uffef"); } @Test public void testValidatePath_SupplementaryChar() { assertThrows(IllegalArgumentException.class, () -> { PathUtils.validatePath("/test\ufff0"); }); } @Test public void testGetTopNamespace() { assertEquals("n0", PathUtils.getTopNamespace("/n0/n1/n2/n3")); assertNull(PathUtils.getTopNamespace("/")); assertNull(PathUtils.getTopNamespace("")); assertNull(PathUtils.getTopNamespace(null)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_Se0100644 0000000 0000000 00000000160 15051152474 032653 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/SecretUtilsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/SecretUtilsTest.ja0100644 0000000 0000000 00000005337 15051152474 034247 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; public class SecretUtilsTest { @ParameterizedTest @ValueSource (strings = {"test secret", ""}) public void testReadSecret(final String secretTxt) throws Exception { final Path secretFile = createSecretFile(secretTxt); final char[] secret = SecretUtils.readSecret(secretFile.toString()); assertEquals(secretTxt, String.valueOf(secret)); } @Test public void tesReadSecret_withLineSeparator() throws Exception { final String secretTxt = "test secret with line separator" + System.lineSeparator(); final Path secretFile = createSecretFile(secretTxt); final char[] secret = SecretUtils.readSecret(secretFile.toString()); assertEquals(secretTxt.substring(0, secretTxt.length() - 1), String.valueOf(secret)); } @Test public void testReadSecret_fileNotExist() { final String pathToFile = "NonExistingFile"; final IllegalStateException exception = assertThrows(IllegalStateException.class, () -> SecretUtils.readSecret(pathToFile)); assertEquals("Exception occurred while reading secret from file " + pathToFile, exception.getMessage()); } public static Path createSecretFile(final String secretTxt) throws IOException { final Path path = Files.createTempFile("test_", ".secrete"); final BufferedWriter writer = new BufferedWriter(new FileWriter(path.toString())); writer.append(secretTxt); writer.close(); path.toFile().deleteOnExit(); return path; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/TimeTest.java0100644 0000000 0000000 00000007255 15051152474 033227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Calendar; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; /** * Command line program for demonstrating robustness to clock * changes. *

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

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

    * As long as there isn't any expired event, the experiment is successful. */ public class TimeTest extends ClientBase { private static final long mt0 = System.currentTimeMillis(); private static final long nt0 = Time.currentElapsedTime(); private static AtomicInteger watchCount = new AtomicInteger(0); public static void main(String[] args) throws Exception { System.out.print("Starting\n"); final TimeTest test = new TimeTest(); System.out.print("After construct\n"); test.setUp(); ZooKeeper zk = test.createClient(); zk.create("/ephemeral", new byte[]{1, 2, 3}, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); while (Time.currentElapsedTime() - nt0 < 100000) { System.out.printf("%d\t%s\n", discrepancy(), zk.exists("/ephemeral", watchCount.get() == 0 ? createWatcher() : null) != null); waitByYielding(500); } } private static Watcher createWatcher() { watchCount.incrementAndGet(); return event -> { watchCount.decrementAndGet(); System.out.printf("%d event = %s\n", discrepancy(), event); }; } private static void waitByYielding(long delay) { long t0 = Time.currentElapsedTime(); while (Time.currentElapsedTime() < t0 + delay) { Thread.yield(); } } private static long discrepancy() { return (System.currentTimeMillis() - mt0) - (Time.currentElapsedTime() - nt0); } @Test public void testElapsedTimeToDate() throws Exception { long walltime = Time.currentWallTime(); long elapsedTime = Time.currentElapsedTime(); Thread.sleep(200); Calendar cal = Calendar.getInstance(); cal.setTime(Time.elapsedTimeToDate(elapsedTime)); int calculatedDate = cal.get(Calendar.HOUR_OF_DAY); cal.setTime(new Date(walltime)); int realDate = cal.get(Calendar.HOUR_OF_DAY); assertEquals(calculatedDate, realDate); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509KeyType.java0100644 0000000 0000000 00000001760 15051152474 033444 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; /** * Represents a type of key pair used for X509 certs in tests. The two options are RSA or EC (elliptic curve). */ public enum X509KeyType { RSA, EC } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_X50100644 0000000 0000000 00000000160 15051152474 032600 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509TestContext.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509TestContext.ja0100644 0000000 0000000 00000056605 15051152474 034017 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static java.util.Objects.requireNonNull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.Security; import java.security.cert.X509Certificate; import java.util.Arrays; import org.apache.commons.io.FileUtils; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.OperatorCreationException; /** * This class simplifies the creation of certificates and private keys for SSL/TLS connections. */ public class X509TestContext { private static final String TRUST_STORE_PREFIX = "zk_test_ca"; private static final String KEY_STORE_PREFIX = "zk_test_key"; private final File tempDir; private final X509KeyType trustStoreKeyType; private final KeyPair trustStoreKeyPair; private final long trustStoreCertExpirationMillis; private final X509Certificate trustStoreCertificate; private final String trustStorePassword; private File trustStoreJksFile; private File trustStorePemFile; private File trustStorePkcs12File; private File trustStoreBcfksFile; private final X509KeyType keyStoreKeyType; private final KeyPair keyStoreKeyPair; private final long keyStoreCertExpirationMillis; private final X509Certificate keyStoreCertificate; private final String keyStorePassword; private File keyStoreJksFile; private File keyStorePemFile; private File keyStorePkcs12File; private File keyStoreBcfksFile; private final Boolean hostnameVerification; /** * Constructor is intentionally private, use the Builder class instead. * @param tempDir the directory in which key store and trust store temp files will be written. * @param trustStoreKeyPair the key pair for the trust store. * @param trustStoreCertExpirationMillis the expiration of the trust store cert, in milliseconds from now. * @param trustStorePassword the password to protect a JKS trust store (ignored for PEM trust stores). * @param keyStoreKeyPair the key pair for the key store. * @param keyStoreCertExpirationMillis the expiration of the key store cert, in milliseconds from now. * @param keyStorePassword the password to protect the key store private key. * @throws IOException * @throws GeneralSecurityException * @throws OperatorCreationException */ private X509TestContext(File tempDir, KeyPair trustStoreKeyPair, long trustStoreCertExpirationMillis, String trustStorePassword, KeyPair keyStoreKeyPair, long keyStoreCertExpirationMillis, String keyStorePassword, Boolean hostnameVerification) throws IOException, GeneralSecurityException, OperatorCreationException { if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { throw new IllegalStateException("BC Security provider was not found"); } this.tempDir = requireNonNull(tempDir); if (!tempDir.isDirectory()) { throw new IllegalArgumentException("Not a directory: " + tempDir); } this.trustStoreKeyPair = requireNonNull(trustStoreKeyPair); this.trustStoreKeyType = keyPairToType(trustStoreKeyPair); this.trustStoreCertExpirationMillis = trustStoreCertExpirationMillis; this.trustStorePassword = requireNonNull(trustStorePassword); this.keyStoreKeyPair = requireNonNull(keyStoreKeyPair); this.keyStoreKeyType = keyPairToType(keyStoreKeyPair); this.keyStoreCertExpirationMillis = keyStoreCertExpirationMillis; this.keyStorePassword = requireNonNull(keyStorePassword); X500NameBuilder caNameBuilder = new X500NameBuilder(BCStyle.INSTANCE); caNameBuilder.addRDN(BCStyle.CN, MethodHandles.lookup().lookupClass().getCanonicalName() + " Root CA"); trustStoreCertificate = X509TestHelpers.newSelfSignedCACert(caNameBuilder.build(), trustStoreKeyPair, trustStoreCertExpirationMillis); X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); nameBuilder.addRDN(BCStyle.CN, MethodHandles.lookup().lookupClass().getCanonicalName() + " Zookeeper Test"); keyStoreCertificate = X509TestHelpers.newCert(trustStoreCertificate, trustStoreKeyPair, nameBuilder.build(), keyStoreKeyPair.getPublic(), keyStoreCertExpirationMillis); trustStorePkcs12File = trustStorePemFile = trustStoreJksFile = null; keyStorePkcs12File = keyStorePemFile = keyStoreJksFile = null; this.hostnameVerification = hostnameVerification; } /** * Returns the X509KeyType of the given key pair. * @param keyPair the key pair. * @return X509KeyType.RSA if given an RSA key pair, and X509KeyType.EC otherwise. */ private X509KeyType keyPairToType(KeyPair keyPair) { if (keyPair.getPrivate().getAlgorithm().contains("RSA")) { return X509KeyType.RSA; } else { return X509KeyType.EC; } } public File getTempDir() { return tempDir; } public X509KeyType getTrustStoreKeyType() { return trustStoreKeyType; } public KeyPair getTrustStoreKeyPair() { return trustStoreKeyPair; } public long getTrustStoreCertExpirationMillis() { return trustStoreCertExpirationMillis; } public X509Certificate getTrustStoreCertificate() { return trustStoreCertificate; } public String getTrustStorePassword() { return trustStorePassword; } /** * Returns the path to the trust store file in the given format (JKS or PEM). Note that the file is created lazily, * the first time this method is called. The trust store file is temporary and will be deleted on exit. * @param storeFileType the store file type (JKS or PEM). * @return the path to the trust store file. * @throws IOException if there is an error creating the trust store file. */ public File getTrustStoreFile(KeyStoreFileType storeFileType) throws IOException { switch (storeFileType) { case JKS: return getTrustStoreJksFile(); case PEM: return getTrustStorePemFile(); case PKCS12: return getTrustStorePkcs12File(); case BCFKS: return getTrustStoreBcfksFile(); default: throw new IllegalArgumentException("Invalid trust store type: " + storeFileType + ", must be one of: " + Arrays.toString(KeyStoreFileType.values())); } } private File getTrustStoreJksFile() throws IOException { if (trustStoreJksFile == null) { File trustStoreJksFile = File.createTempFile(TRUST_STORE_PREFIX, KeyStoreFileType.JKS.getDefaultFileExtension(), tempDir); trustStoreJksFile.deleteOnExit(); try (final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStoreJksFile)) { byte[] bytes = X509TestHelpers.certToJavaTrustStoreBytes(trustStoreCertificate, trustStorePassword); trustStoreOutputStream.write(bytes); trustStoreOutputStream.flush(); } catch (GeneralSecurityException e) { throw new IOException(e); } this.trustStoreJksFile = trustStoreJksFile; } return trustStoreJksFile; } private File getTrustStorePemFile() throws IOException { if (trustStorePemFile == null) { File trustStorePemFile = File.createTempFile(TRUST_STORE_PREFIX, KeyStoreFileType.PEM.getDefaultFileExtension(), tempDir); trustStorePemFile.deleteOnExit(); FileUtils.writeStringToFile(trustStorePemFile, X509TestHelpers.pemEncodeX509Certificate(trustStoreCertificate), StandardCharsets.US_ASCII, false); this.trustStorePemFile = trustStorePemFile; } return trustStorePemFile; } private File getTrustStorePkcs12File() throws IOException { if (trustStorePkcs12File == null) { File trustStorePkcs12File = File.createTempFile(TRUST_STORE_PREFIX, KeyStoreFileType.PKCS12.getDefaultFileExtension(), tempDir); trustStorePkcs12File.deleteOnExit(); try (final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStorePkcs12File)) { byte[] bytes = X509TestHelpers.certToPKCS12TrustStoreBytes(trustStoreCertificate, trustStorePassword); trustStoreOutputStream.write(bytes); trustStoreOutputStream.flush(); } catch (GeneralSecurityException e) { throw new IOException(e); } this.trustStorePkcs12File = trustStorePkcs12File; } return trustStorePkcs12File; } private File getTrustStoreBcfksFile() throws IOException { if (trustStoreBcfksFile == null) { File trustStoreBcfksFile = File.createTempFile( TRUST_STORE_PREFIX, KeyStoreFileType.BCFKS.getDefaultFileExtension(), tempDir); trustStoreBcfksFile.deleteOnExit(); try (final FileOutputStream trustStoreOutputStream = new FileOutputStream(trustStoreBcfksFile)) { byte[] bytes = X509TestHelpers.certToBCFKSTrustStoreBytes(trustStoreCertificate, trustStorePassword); trustStoreOutputStream.write(bytes); trustStoreOutputStream.flush(); } catch (GeneralSecurityException e) { throw new IOException(e); } this.trustStoreBcfksFile = trustStoreBcfksFile; } return trustStoreBcfksFile; } public X509KeyType getKeyStoreKeyType() { return keyStoreKeyType; } public KeyPair getKeyStoreKeyPair() { return keyStoreKeyPair; } public long getKeyStoreCertExpirationMillis() { return keyStoreCertExpirationMillis; } public X509Certificate getKeyStoreCertificate() { return keyStoreCertificate; } public String getKeyStorePassword() { return keyStorePassword; } public boolean isKeyStoreEncrypted() { return keyStorePassword.length() > 0; } /** * Returns the path to the key store file in the given format (JKS, PEM, ...). Note that the file is created lazily, * the first time this method is called. The key store file is temporary and will be deleted on exit. * @param storeFileType the store file type (JKS, PEM, ...). * @return the path to the key store file. * @throws IOException if there is an error creating the key store file. */ public File getKeyStoreFile(KeyStoreFileType storeFileType) throws IOException { switch (storeFileType) { case JKS: return getKeyStoreJksFile(); case PEM: return getKeyStorePemFile(); case PKCS12: return getKeyStorePkcs12File(); case BCFKS: return getKeyStoreBcfksFile(); default: throw new IllegalArgumentException("Invalid key store type: " + storeFileType + ", must be one of: " + Arrays.toString(KeyStoreFileType.values())); } } private File getKeyStoreJksFile() throws IOException { if (keyStoreJksFile == null) { File keyStoreJksFile = File.createTempFile(KEY_STORE_PREFIX, KeyStoreFileType.JKS.getDefaultFileExtension(), tempDir); keyStoreJksFile.deleteOnExit(); try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStoreJksFile)) { byte[] bytes = X509TestHelpers.certAndPrivateKeyToJavaKeyStoreBytes(keyStoreCertificate, keyStoreKeyPair.getPrivate(), keyStorePassword); keyStoreOutputStream.write(bytes); keyStoreOutputStream.flush(); } catch (GeneralSecurityException e) { throw new IOException(e); } this.keyStoreJksFile = keyStoreJksFile; } return keyStoreJksFile; } private File getKeyStorePemFile() throws IOException { if (keyStorePemFile == null) { try { File keyStorePemFile = File.createTempFile(KEY_STORE_PREFIX, KeyStoreFileType.PEM.getDefaultFileExtension(), tempDir); keyStorePemFile.deleteOnExit(); FileUtils.writeStringToFile(keyStorePemFile, X509TestHelpers.pemEncodeCertAndPrivateKey(keyStoreCertificate, keyStoreKeyPair.getPrivate(), keyStorePassword), StandardCharsets.US_ASCII, false); this.keyStorePemFile = keyStorePemFile; } catch (OperatorCreationException e) { throw new IOException(e); } } return keyStorePemFile; } private File getKeyStorePkcs12File() throws IOException { if (keyStorePkcs12File == null) { File keyStorePkcs12File = File.createTempFile(KEY_STORE_PREFIX, KeyStoreFileType.PKCS12.getDefaultFileExtension(), tempDir); keyStorePkcs12File.deleteOnExit(); try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStorePkcs12File)) { byte[] bytes = X509TestHelpers.certAndPrivateKeyToPKCS12Bytes(keyStoreCertificate, keyStoreKeyPair.getPrivate(), keyStorePassword); keyStoreOutputStream.write(bytes); keyStoreOutputStream.flush(); } catch (GeneralSecurityException e) { throw new IOException(e); } this.keyStorePkcs12File = keyStorePkcs12File; } return keyStorePkcs12File; } private File getKeyStoreBcfksFile() throws IOException { if (keyStoreBcfksFile == null) { File keyStoreBcfksFile = File.createTempFile( KEY_STORE_PREFIX, KeyStoreFileType.BCFKS.getDefaultFileExtension(), tempDir); keyStoreBcfksFile.deleteOnExit(); try (final FileOutputStream keyStoreOutputStream = new FileOutputStream(keyStoreBcfksFile)) { byte[] bytes = X509TestHelpers.certAndPrivateKeyToBCFKSBytes( keyStoreCertificate, keyStoreKeyPair.getPrivate(), keyStorePassword); keyStoreOutputStream.write(bytes); keyStoreOutputStream.flush(); } catch (GeneralSecurityException e) { throw new IOException(e); } this.keyStoreBcfksFile = keyStoreBcfksFile; } return keyStoreBcfksFile; } /** * Sets the SSL system properties such that the given X509Util object can be used to create SSL Contexts that * will use the trust store and key store files created by this test context. Example usage: *

         *     X509TestContext testContext = ...; // create the test context
         *     X509Util x509Util = new QuorumX509Util();
         *     testContext.setSystemProperties(x509Util, KeyStoreFileType.JKS, KeyStoreFileType.JKS);
         *     // The returned context will use the key store and trust store created by the test context.
         *     SSLContext ctx = x509Util.getDefaultSSLContext();
         * 
    * @param x509Util the X509Util. * @param keyStoreFileType the store file type to use for the key store (JKS, PEM, ...). * @param trustStoreFileType the store file type to use for the trust store (JKS, PEM, ...). * @throws IOException if there is an error creating the key store file or trust store file. */ public void setSystemProperties(X509Util x509Util, KeyStoreFileType keyStoreFileType, KeyStoreFileType trustStoreFileType) throws IOException { System.setProperty(x509Util.getSslKeystoreLocationProperty(), this.getKeyStoreFile(keyStoreFileType).getAbsolutePath()); System.setProperty(x509Util.getSslKeystorePasswdProperty(), this.getKeyStorePassword()); System.setProperty(x509Util.getSslKeystoreTypeProperty(), keyStoreFileType.getPropertyValue()); System.setProperty(x509Util.getSslTruststoreLocationProperty(), this.getTrustStoreFile(trustStoreFileType).getAbsolutePath()); System.setProperty(x509Util.getSslTruststorePasswdProperty(), this.getTrustStorePassword()); System.setProperty(x509Util.getSslTruststoreTypeProperty(), trustStoreFileType.getPropertyValue()); if (hostnameVerification != null) { System.setProperty(x509Util.getSslHostnameVerificationEnabledProperty(), hostnameVerification.toString()); } else { System.clearProperty(x509Util.getSslHostnameVerificationEnabledProperty()); } } /** * Clears system properties set by * {@link #setSystemProperties(X509Util, KeyStoreFileType, KeyStoreFileType)}. * @param x509Util the X509Util to read property keys from. */ public void clearSystemProperties(X509Util x509Util) { System.clearProperty(x509Util.getSslKeystoreLocationProperty()); System.clearProperty(x509Util.getSslKeystorePasswdProperty()); System.clearProperty(x509Util.getSslKeystorePasswdPathProperty()); System.clearProperty(x509Util.getSslKeystoreTypeProperty()); System.clearProperty(x509Util.getSslTruststoreLocationProperty()); System.clearProperty(x509Util.getSslTruststorePasswdProperty()); System.clearProperty(x509Util.getSslTruststorePasswdPathProperty()); System.clearProperty(x509Util.getSslTruststoreTypeProperty()); System.clearProperty(x509Util.getSslHostnameVerificationEnabledProperty()); } /** * Builder class, used for creating new instances of X509TestContext. */ public static class Builder { public static final long DEFAULT_CERT_EXPIRATION_MILLIS = 1000L * 60 * 60 * 24; // 1 day private File tempDir; private X509KeyType trustStoreKeyType; private String trustStorePassword; private long trustStoreCertExpirationMillis; private X509KeyType keyStoreKeyType; private String keyStorePassword; private long keyStoreCertExpirationMillis; private Boolean hostnameVerification; /** * Creates an empty builder. */ public Builder() { trustStoreKeyType = X509KeyType.EC; trustStorePassword = ""; trustStoreCertExpirationMillis = DEFAULT_CERT_EXPIRATION_MILLIS; keyStoreKeyType = X509KeyType.EC; keyStorePassword = ""; keyStoreCertExpirationMillis = DEFAULT_CERT_EXPIRATION_MILLIS; hostnameVerification = null; } /** * Builds a new X509TestContext from this builder. * @return a new X509TestContext * @throws IOException * @throws GeneralSecurityException * @throws OperatorCreationException */ public X509TestContext build() throws IOException, GeneralSecurityException, OperatorCreationException { KeyPair trustStoreKeyPair = X509TestHelpers.generateKeyPair(trustStoreKeyType); KeyPair keyStoreKeyPair = X509TestHelpers.generateKeyPair(keyStoreKeyType); return new X509TestContext(tempDir, trustStoreKeyPair, trustStoreCertExpirationMillis, trustStorePassword, keyStoreKeyPair, keyStoreCertExpirationMillis, keyStorePassword, hostnameVerification); } /** * Sets the temporary directory. Certificate and private key files will be created in this directory. * @param tempDir the temp directory. * @return this Builder. */ public Builder setTempDir(File tempDir) { this.tempDir = tempDir; return this; } /** * Sets the trust store key type. The CA key generated for the test context will be of this type. * @param keyType the key type. * @return this Builder. */ public Builder setTrustStoreKeyType(X509KeyType keyType) { trustStoreKeyType = keyType; return this; } /** * Sets the trust store password. Ignored for PEM trust stores, JKS trust stores will be encrypted with this * password. * @param password the password. * @return this Builder. */ public Builder setTrustStorePassword(String password) { trustStorePassword = password; return this; } /** * Sets the trust store certificate's expiration, in milliseconds from when build() is called. * @param expirationMillis expiration in milliseconds. * @return this Builder. */ public Builder setTrustStoreCertExpirationMillis(long expirationMillis) { trustStoreCertExpirationMillis = expirationMillis; return this; } /** * Sets the key store key type. The private key generated for the test context will be of this type. * @param keyType the key type. * @return this Builder. */ public Builder setKeyStoreKeyType(X509KeyType keyType) { keyStoreKeyType = keyType; return this; } /** * Sets the key store password. The private key (PEM, JKS) and certificate (JKS only) will be encrypted with * this password. * @param password the password. * @return this Builder. */ public Builder setKeyStorePassword(String password) { keyStorePassword = password; return this; } /** * Sets the key store certificate's expiration, in milliseconds from when build() is called. * @param expirationMillis expiration in milliseconds. * @return this Builder. */ public Builder setKeyStoreCertExpirationMillis(long expirationMillis) { keyStoreCertExpirationMillis = expirationMillis; return this; } /** * Sets the hostname verification behavior. If null is provided, reverts the behavior to the default, otherwise * explicitly sets hostname verification to true or false. * @param hostnameVerification new value for the hostname verification setting. * @return this Builder. */ public Builder setHostnameVerification(Boolean hostnameVerification) { this.hostnameVerification = hostnameVerification; return this; } } /** * Returns a new default-constructed Builder. * @return a new Builder. */ public static Builder newBuilder() { return new Builder(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_X50100644 0000000 0000000 00000000160 15051152474 032600 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509TestHelpers.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509TestHelpers.ja0100644 0000000 0000000 00000057657 15051152474 034005 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringWriter; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.ECGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec; import java.util.Date; import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator; import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.OutputEncryptor; import org.bouncycastle.operator.bc.BcContentSignerBuilder; import org.bouncycastle.operator.bc.BcECContentSignerBuilder; import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class contains helper methods for creating X509 certificates and key pairs, and for serializing them * to JKS, PEM or other keystore type files. */ public class X509TestHelpers { private static final Logger LOG = LoggerFactory.getLogger(X509TestHelpers.class); private static final SecureRandom PRNG = new SecureRandom(); private static final int DEFAULT_RSA_KEY_SIZE_BITS = 2048; private static final BigInteger DEFAULT_RSA_PUB_EXPONENT = RSAKeyGenParameterSpec.F4; // 65537 private static final String DEFAULT_ELLIPTIC_CURVE_NAME = "secp256r1"; // Per RFC 5280 section 4.1.2.2, X509 certificates can use up to 20 bytes == 160 bits for serial numbers. private static final int SERIAL_NUMBER_MAX_BITS = 20 * Byte.SIZE; /** * Uses the private key of the given key pair to create a self-signed CA certificate with the public half of the * key pair and the given subject and expiration. The issuer of the new cert will be equal to the subject. * Returns the new certificate. * The returned certificate should be used as the trust store. The private key of the input key pair should be * used to sign certificates that are used by test peers to establish TLS connections to each other. * @param subject the subject of the new certificate being created. * @param keyPair the key pair to use. The public key will be embedded in the new certificate, and the private key * will be used to self-sign the certificate. * @param expirationMillis expiration of the new certificate, in milliseconds from now. * @return a new self-signed CA certificate. * @throws IOException * @throws OperatorCreationException * @throws GeneralSecurityException */ public static X509Certificate newSelfSignedCACert( X500Name subject, KeyPair keyPair, long expirationMillis) throws IOException, OperatorCreationException, GeneralSecurityException { Date now = new Date(); X509v3CertificateBuilder builder = initCertBuilder(subject, // for self-signed certs, issuer == subject now, new Date(now.getTime() + expirationMillis), subject, keyPair.getPublic()); builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true)); // is a CA builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); return buildAndSignCertificate(keyPair.getPrivate(), builder); } /** * Using the private key of the given CA key pair and the Subject of the given CA cert as the Issuer, issues a * new cert with the given subject and public key. The returned certificate, combined with the private key half * of the certPublicKey, should be used as the key store. * @param caCert the certificate of the CA that's doing the signing. * @param caKeyPair the key pair of the CA. The private key will be used to sign. The public key must match the * public key in the caCert. * @param certSubject the subject field of the new cert being issued. * @param certPublicKey the public key of the new cert being issued. * @param expirationMillis the expiration of the cert being issued, in milliseconds from now. * @return a new certificate signed by the CA's private key. * @throws IOException * @throws OperatorCreationException * @throws GeneralSecurityException */ public static X509Certificate newCert( X509Certificate caCert, KeyPair caKeyPair, X500Name certSubject, PublicKey certPublicKey, long expirationMillis) throws IOException, OperatorCreationException, GeneralSecurityException { if (!caKeyPair.getPublic().equals(caCert.getPublicKey())) { throw new IllegalArgumentException("CA private key does not match the public key in the CA cert"); } Date now = new Date(); X509v3CertificateBuilder builder = initCertBuilder(new X500Name(caCert.getIssuerDN().getName()), now, new Date( now.getTime() + expirationMillis), certSubject, certPublicKey); builder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); // not a CA builder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); builder.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth})); builder.addExtension(Extension.subjectAlternativeName, false, getLocalhostSubjectAltNames()); return buildAndSignCertificate(caKeyPair.getPrivate(), builder); } /** * Returns subject alternative names for "localhost". * @return the subject alternative names for "localhost". */ private static GeneralNames getLocalhostSubjectAltNames() throws UnknownHostException { InetAddress[] localAddresses = InetAddress.getAllByName("localhost"); GeneralName[] generalNames = new GeneralName[localAddresses.length + 1]; for (int i = 0; i < localAddresses.length; i++) { generalNames[i] = new GeneralName(GeneralName.iPAddress, new DEROctetString(localAddresses[i].getAddress())); } generalNames[generalNames.length - 1] = new GeneralName(GeneralName.dNSName, new DERIA5String("localhost")); return new GeneralNames(generalNames); } /** * Helper method for newSelfSignedCACert() and newCert(). Initializes a X509v3CertificateBuilder with * logic that's common to both methods. * @param issuer Issuer field of the new cert. * @param notBefore date before which the new cert is not valid. * @param notAfter date after which the new cert is not valid. * @param subject Subject field of the new cert. * @param subjectPublicKey public key to store in the new cert. * @return a X509v3CertificateBuilder that can be further customized to finish creating the new cert. */ private static X509v3CertificateBuilder initCertBuilder( X500Name issuer, Date notBefore, Date notAfter, X500Name subject, PublicKey subjectPublicKey) { return new X509v3CertificateBuilder(issuer, new BigInteger(SERIAL_NUMBER_MAX_BITS, PRNG), notBefore, notAfter, subject, SubjectPublicKeyInfo.getInstance(subjectPublicKey.getEncoded())); } /** * Signs the certificate being built by the given builder using the given private key and returns the certificate. * @param privateKey the private key to sign the certificate with. * @param builder the cert builder that contains the certificate data. * @return the signed certificate. * @throws IOException * @throws OperatorCreationException * @throws CertificateException */ private static X509Certificate buildAndSignCertificate( PrivateKey privateKey, X509v3CertificateBuilder builder) throws IOException, OperatorCreationException, CertificateException { BcContentSignerBuilder signerBuilder; if (privateKey.getAlgorithm().contains("RSA")) { // a little hacky way to detect key type, but it works AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSAEncryption"); AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(signatureAlgorithm); signerBuilder = new BcRSAContentSignerBuilder(signatureAlgorithm, digestAlgorithm); } else { // if not RSA, assume EC AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withECDSA"); AlgorithmIdentifier digestAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(signatureAlgorithm); signerBuilder = new BcECContentSignerBuilder(signatureAlgorithm, digestAlgorithm); } AsymmetricKeyParameter privateKeyParam = PrivateKeyFactory.createKey(privateKey.getEncoded()); ContentSigner signer = signerBuilder.build(privateKeyParam); return toX509Cert(builder.build(signer)); } /** * Generates a new asymmetric key pair of the given type. * @param keyType the type of key pair to generate. * @return the new key pair. * @throws GeneralSecurityException if your java crypto providers are messed up. */ public static KeyPair generateKeyPair(X509KeyType keyType) throws GeneralSecurityException { switch (keyType) { case RSA: return generateRSAKeyPair(); case EC: return generateECKeyPair(); default: throw new IllegalArgumentException("Invalid X509KeyType"); } } /** * Generates an RSA key pair with a 2048-bit private key and F4 (65537) as the public exponent. * @return the key pair. */ public static KeyPair generateRSAKeyPair() throws GeneralSecurityException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); RSAKeyGenParameterSpec keyGenSpec = new RSAKeyGenParameterSpec(DEFAULT_RSA_KEY_SIZE_BITS, DEFAULT_RSA_PUB_EXPONENT); keyGen.initialize(keyGenSpec, PRNG); return keyGen.generateKeyPair(); } /** * Generates an elliptic curve key pair using the "secp256r1" aka "prime256v1" aka "NIST P-256" curve. * @return the key pair. */ public static KeyPair generateECKeyPair() throws GeneralSecurityException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); keyGen.initialize(new ECGenParameterSpec(DEFAULT_ELLIPTIC_CURVE_NAME), PRNG); return keyGen.generateKeyPair(); } /** * PEM-encodes the given X509 certificate and private key (compatible with OpenSSL), optionally protecting the * private key with a password. Concatenates them both and returns the result as a single string. * This creates the PEM encoding of a key store. * @param cert the X509 certificate to PEM-encode. * @param privateKey the private key to PEM-encode. * @param keyPassword an optional key password. If empty or null, the private key will not be encrypted. * @return a String containing the PEM encodings of the certificate and private key. * @throws IOException if converting the certificate or private key to PEM format fails. * @throws OperatorCreationException if constructing the encryptor from the given password fails. */ public static String pemEncodeCertAndPrivateKey( X509Certificate cert, PrivateKey privateKey, String keyPassword) throws IOException, OperatorCreationException { return pemEncodeX509Certificate(cert) + "\n" + pemEncodePrivateKey(privateKey, keyPassword); } /** * PEM-encodes the given private key (compatible with OpenSSL), optionally protecting it with a password, and * returns the result as a String. * @param key the private key. * @param password an optional key password. If empty or null, the private key will not be encrypted. * @return a String containing the PEM encoding of the private key. * @throws IOException if converting the key to PEM format fails. * @throws OperatorCreationException if constructing the encryptor from the given password fails. */ public static String pemEncodePrivateKey( PrivateKey key, String password) throws IOException, OperatorCreationException { StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter); OutputEncryptor encryptor = null; if (password != null && password.length() > 0) { encryptor = new JceOpenSSLPKCS8EncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC).setProvider(BouncyCastleProvider.PROVIDER_NAME).setRandom(PRNG).setPassword(password.toCharArray()).build(); } pemWriter.writeObject(new JcaPKCS8Generator(key, encryptor)); pemWriter.close(); return stringWriter.toString(); } /** * PEM-encodes the given X509 certificate (compatible with OpenSSL) and returns the result as a String. * @param cert the certificate. * @return a String containing the PEM encoding of the certificate. * @throws IOException if converting the certificate to PEM format fails. */ public static String pemEncodeX509Certificate(X509Certificate cert) throws IOException { StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter); pemWriter.writeObject(cert); pemWriter.close(); return stringWriter.toString(); } /** * Encodes the given X509Certificate as a JKS TrustStore, optionally protecting the cert with a password (though * it's unclear why one would do this since certificates only contain public information and do not need to be * kept secret). Returns the byte array encoding of the trust store, which may be written to a file and loaded to * instantiate the trust store at a later point or in another process. * @param cert the certificate to serialize. * @param keyPassword an optional password to encrypt the trust store. If empty or null, the cert will not be encrypted. * @return the serialized bytes of the JKS trust store. * @throws IOException * @throws GeneralSecurityException */ public static byte[] certToJavaTrustStoreBytes( X509Certificate cert, String keyPassword) throws IOException, GeneralSecurityException { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); return certToTrustStoreBytes(cert, keyPassword, trustStore); } /** * Encodes the given X509Certificate as a PKCS12 TrustStore, optionally protecting the cert with a password (though * it's unclear why one would do this since certificates only contain public information and do not need to be * kept secret). Returns the byte array encoding of the trust store, which may be written to a file and loaded to * instantiate the trust store at a later point or in another process. * @param cert the certificate to serialize. * @param keyPassword an optional password to encrypt the trust store. If empty or null, the cert will not be encrypted. * @return the serialized bytes of the PKCS12 trust store. * @throws IOException * @throws GeneralSecurityException */ public static byte[] certToPKCS12TrustStoreBytes( X509Certificate cert, String keyPassword) throws IOException, GeneralSecurityException { KeyStore trustStore = KeyStore.getInstance("PKCS12"); return certToTrustStoreBytes(cert, keyPassword, trustStore); } /** * Encodes the given X509Certificate as a BCFKS TrustStore, optionally protecting the cert with a password (though * it's unclear why one would do this since certificates only contain public information and do not need to be * kept secret). Returns the byte array encoding of the trust store, which may be written to a file and loaded to * instantiate the trust store at a later point or in another process. * @param cert the certificate to serialize. * @param keyPassword an optional password to encrypt the trust store. If empty or null, the cert will not be encrypted. * @return the serialized bytes of the BCFKS trust store. * @throws IOException * @throws GeneralSecurityException */ public static byte[] certToBCFKSTrustStoreBytes( X509Certificate cert, String keyPassword) throws IOException, GeneralSecurityException { KeyStore trustStore = KeyStore.getInstance("BCFKS"); return certToTrustStoreBytes(cert, keyPassword, trustStore); } private static byte[] certToTrustStoreBytes(X509Certificate cert, String keyPassword, KeyStore trustStore) throws IOException, GeneralSecurityException { char[] keyPasswordChars = keyPassword == null ? new char[0] : keyPassword.toCharArray(); trustStore.load(null, keyPasswordChars); trustStore.setCertificateEntry(cert.getSubjectDN().toString(), cert); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); trustStore.store(outputStream, keyPasswordChars); outputStream.flush(); byte[] result = outputStream.toByteArray(); outputStream.close(); return result; } /** * Encodes the given X509Certificate and private key as a JKS KeyStore, optionally protecting the private key * (and possibly the cert?) with a password. Returns the byte array encoding of the key store, which may be written * to a file and loaded to instantiate the key store at a later point or in another process. * @param cert the X509 certificate to serialize. * @param privateKey the private key to serialize. * @param keyPassword an optional key password. If empty or null, the private key will not be encrypted. * @return the serialized bytes of the JKS key store. * @throws IOException * @throws GeneralSecurityException */ public static byte[] certAndPrivateKeyToJavaKeyStoreBytes( X509Certificate cert, PrivateKey privateKey, String keyPassword) throws IOException, GeneralSecurityException { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); return certAndPrivateKeyToBytes(cert, privateKey, keyPassword, keyStore); } /** * Encodes the given X509Certificate and private key as a PKCS12 KeyStore, optionally protecting the private key * (and possibly the cert?) with a password. Returns the byte array encoding of the key store, which may be written * to a file and loaded to instantiate the key store at a later point or in another process. * @param cert the X509 certificate to serialize. * @param privateKey the private key to serialize. * @param keyPassword an optional key password. If empty or null, the private key will not be encrypted. * @return the serialized bytes of the PKCS12 key store. * @throws IOException * @throws GeneralSecurityException */ public static byte[] certAndPrivateKeyToPKCS12Bytes( X509Certificate cert, PrivateKey privateKey, String keyPassword) throws IOException, GeneralSecurityException { KeyStore keyStore = KeyStore.getInstance("PKCS12"); return certAndPrivateKeyToBytes(cert, privateKey, keyPassword, keyStore); } /** * Encodes the given X509Certificate and private key as a BCFKS KeyStore, optionally protecting the private key * (and possibly the cert?) with a password. Returns the byte array encoding of the key store, which may be written * to a file and loaded to instantiate the key store at a later point or in another process. * @param cert the X509 certificate to serialize. * @param privateKey the private key to serialize. * @param keyPassword an optional key password. If empty or null, the private key will not be encrypted. * @return the serialized bytes of the BCFKS key store. * @throws IOException * @throws GeneralSecurityException */ public static byte[] certAndPrivateKeyToBCFKSBytes( X509Certificate cert, PrivateKey privateKey, String keyPassword) throws IOException, GeneralSecurityException { KeyStore keyStore = KeyStore.getInstance("BCFKS"); return certAndPrivateKeyToBytes(cert, privateKey, keyPassword, keyStore); } private static byte[] certAndPrivateKeyToBytes( X509Certificate cert, PrivateKey privateKey, String keyPassword, KeyStore keyStore) throws IOException, GeneralSecurityException { char[] keyPasswordChars = keyPassword == null ? new char[0] : keyPassword.toCharArray(); keyStore.load(null, keyPasswordChars); keyStore.setKeyEntry("key", privateKey, keyPasswordChars, new Certificate[]{cert}); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); keyStore.store(outputStream, keyPasswordChars); outputStream.flush(); byte[] result = outputStream.toByteArray(); outputStream.close(); return result; } /** * Convenience method to convert a bouncycastle X509CertificateHolder to a java X509Certificate. * @param certHolder a bouncycastle X509CertificateHolder. * @return a java X509Certificate * @throws CertificateException if the conversion fails. */ public static X509Certificate toX509Cert(X509CertificateHolder certHolder) throws CertificateException { return new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certHolder); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java0100644 0000000 0000000 00000127756 15051152474 033645 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.ssl.SslContext; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.file.Path; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; public class X509UtilTest extends BaseX509ParameterizedTestCase { private X509Util x509Util; private static final String[] customCipherSuites = new String[]{ "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA" }; public void init( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { super.init(caKeyType, certKeyType, keyPassword, paramIndex); try (X509Util x509util = new ClientX509Util()) { x509TestContext.setSystemProperties(x509util, KeyStoreFileType.JKS, KeyStoreFileType.JKS); } System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); x509Util = new ClientX509Util(); } @AfterEach public void cleanUp() { x509TestContext.clearSystemProperties(x509Util); System.clearProperty(x509Util.getSslOcspEnabledProperty()); System.clearProperty(x509Util.getSslCrlEnabledProperty()); System.clearProperty(x509Util.getCipherSuitesProperty()); System.clearProperty(x509Util.getSslProtocolProperty()); System.clearProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty()); System.clearProperty("com.sun.net.ssl.checkRevocation"); System.clearProperty("com.sun.security.enableCRLDP"); Security.setProperty("ocsp.enable", Boolean.FALSE.toString()); Security.setProperty("com.sun.security.enableCRLDP", Boolean.FALSE.toString()); System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); x509Util.close(); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContextWithoutCustomProtocol( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); SSLContext sslContext = x509Util.getDefaultSSLContext(); assertEquals(X509Util.DEFAULT_PROTOCOL, sslContext.getProtocol()); // Check that TLSv1.3 is selected in JDKs that support it (OpenJDK 8u272 and later). List supported = Arrays.asList(SSLContext.getDefault().getSupportedSSLParameters().getProtocols()); if (supported.contains(X509Util.TLS_1_3)) { // SSLContext protocol. assertEquals(X509Util.TLS_1_3, sslContext.getProtocol()); // Enabled protocols. List protos = Arrays.asList(sslContext.getDefaultSSLParameters().getProtocols()); assertTrue(protos.contains(X509Util.TLS_1_2)); assertTrue(protos.contains(X509Util.TLS_1_3)); } else { assertEquals(X509Util.TLS_1_2, sslContext.getProtocol()); assertArrayEquals(new String[]{X509Util.TLS_1_2}, sslContext.getDefaultSSLParameters().getProtocols()); } } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContextWithCustomProtocol( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { final String protocol = X509Util.TLS_1_1; init(caKeyType, certKeyType, keyPassword, paramIndex); System.setProperty(x509Util.getSslProtocolProperty(), protocol); SSLContext sslContext = x509Util.getDefaultSSLContext(); assertEquals(protocol, sslContext.getProtocol()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContextWithoutKeyStoreLocation( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); System.clearProperty(x509Util.getSslKeystoreLocationProperty()); x509Util.getDefaultSSLContext(); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContextWithoutKeyStorePassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.SSLContextException.class, () -> { if (!x509TestContext.isKeyStoreEncrypted()) { throw new X509Exception.SSLContextException(""); } System.clearProperty(x509Util.getSslKeystorePasswdProperty()); x509Util.getDefaultSSLContext(); }); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContext_withKeyStorePasswordFromFile(final X509KeyType caKeyType, final X509KeyType certKeyType, final String keyPassword, final Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); testCreateSSLContext_withPasswordFromFile(keyPassword, x509Util.getSslKeystorePasswdProperty(), x509Util.getSslKeystorePasswdPathProperty()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContext_withTrustStorePasswordFromFile(final X509KeyType caKeyType, final X509KeyType certKeyType, final String keyPassword, final Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); testCreateSSLContext_withPasswordFromFile(keyPassword, x509Util.getSslTruststorePasswdProperty(), x509Util.getSslTruststorePasswdPathProperty()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContext_withWrongKeyStorePasswordFromFile(final X509KeyType caKeyType, final X509KeyType certKeyType, final String keyPassword, final Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); testCreateSSLContext_withWrongPasswordFromFile(keyPassword, x509Util.getSslKeystorePasswdPathProperty()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContext_withWrongTrustStorePasswordFromFile(final X509KeyType caKeyType, final X509KeyType certKeyType, final String keyPassword, final Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); testCreateSSLContext_withWrongPasswordFromFile(keyPassword, x509Util.getSslTruststorePasswdPathProperty()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLContextWithCustomCipherSuites( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); setCustomCipherSuites(); SSLSocket sslSocket = x509Util.createSSLSocket(); assertArrayEquals(customCipherSuites, sslSocket.getEnabledCipherSuites()); } // It would be great to test the value of PKIXBuilderParameters#setRevocationEnabled but it does not appear to be // possible @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCRLEnabled( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); System.setProperty(x509Util.getSslCrlEnabledProperty(), "true"); x509Util.getDefaultSSLContext(); assertTrue(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); assertTrue(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCRLDisabled( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); x509Util.getDefaultSSLContext(); assertFalse(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); assertFalse(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); assertFalse(Boolean.valueOf(Security.getProperty("ocsp.enable"))); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testOCSPEnabled( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); System.setProperty(x509Util.getSslOcspEnabledProperty(), "true"); x509Util.getDefaultSSLContext(); assertTrue(Boolean.valueOf(System.getProperty("com.sun.net.ssl.checkRevocation"))); assertTrue(Boolean.valueOf(System.getProperty("com.sun.security.enableCRLDP"))); assertTrue(Boolean.valueOf(Security.getProperty("ocsp.enable"))); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLSocket( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); setCustomCipherSuites(); SSLSocket sslSocket = x509Util.createSSLSocket(); assertArrayEquals(customCipherSuites, sslSocket.getEnabledCipherSuites()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLServerSocketWithoutPort( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); setCustomCipherSuites(); SSLServerSocket sslServerSocket = x509Util.createSSLServerSocket(); assertArrayEquals(customCipherSuites, sslServerSocket.getEnabledCipherSuites()); assertTrue(sslServerSocket.getNeedClientAuth()); } @ParameterizedTest @MethodSource("data") @Timeout(value = 5) public void testCreateSSLServerSocketWithPort( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); setCustomCipherSuites(); int port = PortAssignment.unique(); SSLServerSocket sslServerSocket = x509Util.createSSLServerSocket(port); assertEquals(sslServerSocket.getLocalPort(), port); assertArrayEquals(customCipherSuites, sslServerSocket.getEnabledCipherSuites()); assertTrue(sslServerSocket.getNeedClientAuth()); } @ParameterizedTest @MethodSource("data") public void testLoadPEMKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a key manager from the PEM file on disk X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getKeyStorePassword(), KeyStoreFileType.PEM.getPropertyValue()); } @ParameterizedTest @MethodSource("data") public void testLoadPEMKeyStoreNullPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); if (!x509TestContext.getKeyStorePassword().isEmpty()) { return; } // Make sure that empty password and null password are treated the same X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, KeyStoreFileType.PEM.getPropertyValue()); } @ParameterizedTest @MethodSource("data") public void testLoadPEMKeyStoreAutodetectStoreFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a key manager from the PEM file on disk X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); } @ParameterizedTest @MethodSource("data") public void testLoadPEMKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.KeyManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), "wrong password", // intentionally use the wrong password KeyStoreFileType.PEM.getPropertyValue()); }); } @ParameterizedTest @MethodSource("data") public void testLoadPEMTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a trust manager from the PEM file on disk X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getTrustStorePassword(), KeyStoreFileType.PEM.getPropertyValue(), false, false, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadPEMTrustStoreNullPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); if (!x509TestContext.getTrustStorePassword().isEmpty()) { return; } // Make sure that empty password and null password are treated the same X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), null, KeyStoreFileType.PEM.getPropertyValue(), false, false, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadPEMTrustStoreAutodetectStoreFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a trust manager from the PEM file on disk X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath(), x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from file extension' false, false, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadJKSKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a key manager from the JKS file on disk X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getKeyStorePassword(), KeyStoreFileType.JKS.getPropertyValue()); } @ParameterizedTest @MethodSource("data") public void testLoadJKSKeyStoreNullPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); if (!x509TestContext.getKeyStorePassword().isEmpty()) { return; } // Make sure that empty password and null password are treated the same X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null, KeyStoreFileType.JKS.getPropertyValue()); } @ParameterizedTest @MethodSource("data") public void testLoadJKSKeyStoreAutodetectStoreFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a key manager from the JKS file on disk X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); } @ParameterizedTest @MethodSource("data") public void testLoadJKSKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.KeyManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), "wrong password", KeyStoreFileType.JKS.getPropertyValue()); }); } @ParameterizedTest @MethodSource("data") public void testLoadJKSTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a trust manager from the JKS file on disk X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getTrustStorePassword(), KeyStoreFileType.JKS.getPropertyValue(), true, true, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadJKSTrustStoreNullPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); if (!x509TestContext.getTrustStorePassword().isEmpty()) { return; } // Make sure that empty password and null password are treated the same X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), null, KeyStoreFileType.JKS.getPropertyValue(), false, false, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadJKSTrustStoreAutodetectStoreFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a trust manager from the JKS file on disk X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from file extension' true, true, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadJKSTrustStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.TrustManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.JKS).getAbsolutePath(), "wrong password", KeyStoreFileType.JKS.getPropertyValue(), true, true, true, true, false); }); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12KeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a key manager from the PKCS12 file on disk X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), x509TestContext.getKeyStorePassword(), KeyStoreFileType.PKCS12.getPropertyValue()); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12KeyStoreNullPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); if (!x509TestContext.getKeyStorePassword().isEmpty()) { return; } // Make sure that empty password and null password are treated the same X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null, KeyStoreFileType.PKCS12.getPropertyValue()); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12KeyStoreAutodetectStoreFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a key manager from the PKCS12 file on disk X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), x509TestContext.getKeyStorePassword(), null /* null StoreFileType means 'autodetect from file extension' */); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12KeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.KeyManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509KeyManager km = X509Util.createKeyManager( x509TestContext.getKeyStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), "wrong password", KeyStoreFileType.PKCS12.getPropertyValue()); }); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12TrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a trust manager from the PKCS12 file on disk X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), x509TestContext.getTrustStorePassword(), KeyStoreFileType.PKCS12.getPropertyValue(), true, true, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12TrustStoreNullPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); if (!x509TestContext.getTrustStorePassword().isEmpty()) { return; } // Make sure that empty password and null password are treated the same X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), null, KeyStoreFileType.PKCS12.getPropertyValue(), false, false, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12TrustStoreAutodetectStoreFileType( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // Make sure we can instantiate a trust manager from the PKCS12 file on disk X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), x509TestContext.getTrustStorePassword(), null, // null StoreFileType means 'autodetect from file extension' true, true, true, true, false); } @ParameterizedTest @MethodSource("data") public void testLoadPKCS12TrustStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.TrustManagerException.class, () -> { // Attempting to load with the wrong key password should fail X509TrustManager tm = X509Util.createTrustManager( x509TestContext.getTrustStoreFile(KeyStoreFileType.PKCS12).getAbsolutePath(), "wrong password", KeyStoreFileType.PKCS12.getPropertyValue(), true, true, true, true, false); }); } @ParameterizedTest @MethodSource("data") public void testGetSslHandshakeDetectionTimeoutMillisProperty( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertEquals(X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS, x509Util.getSslHandshakeTimeoutMillis()); // Note: need to create a new ClientX509Util each time to pick up modified property value String newPropertyString = Integer.toString(X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS + 1); System.setProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), newPropertyString); try (X509Util tempX509Util = new ClientX509Util()) { assertEquals(X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS + 1, tempX509Util.getSslHandshakeTimeoutMillis()); } // 0 value not allowed, will return the default System.setProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), "0"); try (X509Util tempX509Util = new ClientX509Util()) { assertEquals(X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS, tempX509Util.getSslHandshakeTimeoutMillis()); } // Negative value not allowed, will return the default System.setProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), "-1"); try (X509Util tempX509Util = new ClientX509Util()) { assertEquals(X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS, tempX509Util.getSslHandshakeTimeoutMillis()); } } @ParameterizedTest @MethodSource("data") public void testCreateSSLContext_invalidCustomSSLContextClass( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(X509Exception.SSLContextException.class, () -> { ZKConfig zkConfig = new ZKConfig(); ClientX509Util clientX509Util = new ClientX509Util(); zkConfig.setProperty(clientX509Util.getSslContextSupplierClassProperty(), String.class.getCanonicalName()); clientX509Util.createSSLContext(zkConfig); }); } @ParameterizedTest @MethodSource("data") public void testCreateSSLContext_validCustomSSLContextClass( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); ZKConfig zkConfig = new ZKConfig(); ClientX509Util clientX509Util = new ClientX509Util(); zkConfig.setProperty(clientX509Util.getSslContextSupplierClassProperty(), SslContextSupplier.class.getName()); final SSLContext sslContext = clientX509Util.createSSLContext(zkConfig); assertEquals(SSLContext.getDefault(), sslContext); } @ParameterizedTest @MethodSource("data") public void testCreateSSLContext_ocspWithJreProvider( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); ZKConfig zkConfig = new ZKConfig(); try (ClientX509Util clientX509Util = new ClientX509Util();) { zkConfig.setProperty(clientX509Util.getSslOcspEnabledProperty(), "true"); // Must not throw IllegalArgumentException clientX509Util.createSSLContext(zkConfig); } } @ParameterizedTest @MethodSource("data") public void testCreateSSLContext_hostnameVerificationNoCustomTrustStore(X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); // No truststore System.clearProperty(x509Util.getSslTruststoreLocationProperty()); // Verify client hostname too System.setProperty(x509Util.getSslClientHostnameVerificationEnabledProperty(), "true"); ZKConfig zkConfig = new ZKConfig(); try (ClientX509Util clientX509Util = new ClientX509Util();) { UnpooledByteBufAllocator byteBufAllocator = new UnpooledByteBufAllocator(false); SslContext clientContext = clientX509Util.createNettySslContextForClient(zkConfig); SSLEngine clientEngine = clientContext.newEngine(byteBufAllocator); assertEquals(clientEngine.getSSLParameters().getEndpointIdentificationAlgorithm(), "HTTPS"); SslContext serverContext = clientX509Util.createNettySslContextForServer(zkConfig); SSLEngine serverEngine = serverContext.newEngine(byteBufAllocator); assertEquals(serverEngine.getSSLParameters().getEndpointIdentificationAlgorithm(), "HTTPS"); } } private static void forceClose(Socket s) { if (s == null || s.isClosed()) { return; } try { s.close(); } catch (IOException e) { } } private static void forceClose(ServerSocket s) { if (s == null || s.isClosed()) { return; } try { s.close(); } catch (IOException e) { } } // This test makes sure that client-initiated TLS renegotiation does not // succeed when using TLSv1.2. We explicitly disable it at the top of X509Util.java. // Force TLSv1.2 since the renegotiation feature is not supported anymore in TLSv1.3 and the test becomes invalid. @ParameterizedTest @MethodSource("data") public void testClientRenegotiationFails( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Throwable { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(SSLHandshakeException.class, () -> { int port = PortAssignment.unique(); ExecutorService workerPool = Executors.newCachedThreadPool(); final SSLServerSocket listeningSocket = x509Util.createSSLServerSocket(); SSLSocket clientSocket = null; SSLSocket serverSocket = null; final AtomicInteger handshakesCompleted = new AtomicInteger(0); final CountDownLatch handshakeCompleted = new CountDownLatch(1); try { InetSocketAddress localServerAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), port); listeningSocket.bind(localServerAddress); Future acceptFuture; acceptFuture = workerPool.submit(new Callable() { @Override public SSLSocket call() throws Exception { SSLSocket sslSocket = (SSLSocket) listeningSocket.accept(); sslSocket.setEnabledProtocols(new String[]{X509Util.TLS_1_2}); sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() { @Override public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) { handshakesCompleted.getAndIncrement(); handshakeCompleted.countDown(); } }); assertEquals(1, sslSocket.getInputStream().read()); try { // 2nd read is after the renegotiation attempt and will fail sslSocket.getInputStream().read(); return sslSocket; } catch (Exception e) { forceClose(sslSocket); throw e; } } }); clientSocket = x509Util.createSSLSocket(); clientSocket.connect(localServerAddress); clientSocket.getOutputStream().write(1); // Attempt to renegotiate after establishing the connection clientSocket.startHandshake(); clientSocket.getOutputStream().write(1); // The exception is thrown on the server side, we need to unwrap it try { serverSocket = acceptFuture.get(); } catch (ExecutionException e) { throw e.getCause(); } } finally { forceClose(serverSocket); forceClose(clientSocket); forceClose(listeningSocket); workerPool.shutdown(); // Make sure the first handshake completed and only the second // one failed. handshakeCompleted.await(5, TimeUnit.SECONDS); assertEquals(1, handshakesCompleted.get()); } }); } @ParameterizedTest @MethodSource("data") public void testGetDefaultCipherSuitesJava8( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("1.8"); // Java 8 default should have the CBC suites first assertTrue(cipherSuites[0].contains("CBC")); } @ParameterizedTest @MethodSource("data") public void testGetDefaultCipherSuitesJava9( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("9"); // Java 9+ default should have the GCM suites first assertTrue(cipherSuites[0].contains("GCM")); } @ParameterizedTest @MethodSource("data") public void testGetDefaultCipherSuitesJava10( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("10"); // Java 9+ default should have the GCM suites first assertTrue(cipherSuites[0].contains("GCM")); } @ParameterizedTest @MethodSource("data") public void testGetDefaultCipherSuitesJava11( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("11"); // Java 9+ default should have the GCM suites first assertTrue(cipherSuites[0].contains("GCM")); } @ParameterizedTest @MethodSource("data") public void testGetDefaultCipherSuitesUnknownVersion( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); String[] cipherSuites = X509Util.getDefaultCipherSuitesForJavaVersion("notaversion"); // If version can't be parsed, use the more conservative Java 8 default assertTrue(cipherSuites[0].contains("CBC")); } @ParameterizedTest @MethodSource("data") public void testGetDefaultCipherSuitesNullVersion( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(NullPointerException.class, () -> { X509Util.getDefaultCipherSuitesForJavaVersion(null); }); } // Warning: this will reset the x509Util private void setCustomCipherSuites() { System.setProperty(x509Util.getCipherSuitesProperty(), customCipherSuites[0] + "," + customCipherSuites[1]); x509Util.close(); // remember to close old instance before replacing it x509Util = new ClientX509Util(); } public static class SslContextSupplier implements Supplier { @Override public SSLContext get() { try { return SSLContext.getDefault(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } } private void testCreateSSLContext_withPasswordFromFile(final String keyPassword, final String propertyName, final String pathPropertyName) throws Exception { final Path secretFile = SecretUtilsTest.createSecretFile(keyPassword); System.clearProperty(propertyName); System.setProperty(pathPropertyName, secretFile.toString()); x509Util.getDefaultSSLContext(); } private void testCreateSSLContext_withWrongPasswordFromFile(final String keyPassword, final String pathPropertyName) throws Exception { final Path secretFile = SecretUtilsTest.createSecretFile(keyPassword + "_wrong"); assertThrows(X509Exception.SSLContextException.class, () -> { System.setProperty(pathPropertyName, secretFile.toString()); x509Util.getDefaultSSLContext(); }); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/ZKConfigTest.java0100644 0000000 0000000 00000007057 15051152474 034003 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class ZKConfigTest { X509Util x509Util = new ClientX509Util(); @AfterEach public void tearDown() throws Exception { System.clearProperty(x509Util.getSslProtocolProperty()); } // property is not set we should get the default value @Test @Timeout(value = 10) public void testBooleanRetrievalFromPropertyDefault() { ZKConfig conf = new ZKConfig(); String prop = "UnSetProperty" + System.currentTimeMillis(); boolean defaultValue = false; boolean result = conf.getBoolean(prop, defaultValue); assertEquals(defaultValue, result); } // property is set to an valid boolean, we should get the set value @Test @Timeout(value = 10) public void testBooleanRetrievalFromProperty() { boolean value = true; boolean defaultValue = false; System.setProperty(x509Util.getSslProtocolProperty(), Boolean.toString(value)); ZKConfig conf = new ZKConfig(); boolean result = conf.getBoolean(x509Util.getSslProtocolProperty(), defaultValue); assertEquals(value, result); } // property is set but with white spaces in the beginning @Test @Timeout(value = 10) public void testBooleanRetrievalFromPropertyWithWhitespacesInBeginning() { boolean value = true; boolean defaultValue = false; System.setProperty(x509Util.getSslProtocolProperty(), " " + value); ZKConfig conf = new ZKConfig(); boolean result = conf.getBoolean(x509Util.getSslProtocolProperty(), defaultValue); assertEquals(value, result); } // property is set but with white spaces at the end @Test @Timeout(value = 10) public void testBooleanRetrievalFromPropertyWithWhitespacesAtEnd() { boolean value = true; boolean defaultValue = false; System.setProperty(x509Util.getSslProtocolProperty(), value + " "); ZKConfig conf = new ZKConfig(); boolean result = conf.getBoolean(x509Util.getSslProtocolProperty(), defaultValue); assertEquals(value, result); } // property is set but with white spaces at the beginning and the end @Test @Timeout(value = 10) public void testBooleanRetrievalFromPropertyWithWhitespacesAtBeginningAndEnd() { boolean value = true; boolean defaultValue = false; System.setProperty(x509Util.getSslProtocolProperty(), " " + value + " "); ZKConfig conf = new ZKConfig(); boolean result = conf.getBoolean(x509Util.getSslProtocolProperty(), defaultValue); assertEquals(value, result); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_ZK0100644 0000000 0000000 00000000167 15051152474 032637 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/ZKHostnameVerifierTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/ZKHostnameVerifier0100644 0000000 0000000 00000015172 15051152474 034265 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import javax.net.ssl.SSLException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Note: These test cases (and resources) have been taken from the Apache HttpComponents project. */ public class ZKHostnameVerifierTest { private ZKHostnameVerifier impl; @BeforeEach public void setup() { impl = new ZKHostnameVerifier(); } @Test public void testVerify() throws Exception { final CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream in; X509Certificate x509; in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); exceptionPlease(impl, "bar.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("\u82b1\u5b50.co.jp", x509); exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); impl.verify("bar.com", x509); exceptionPlease(impl, "a.bar.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); impl.verify("bar.com", x509); exceptionPlease(impl, "a.bar.com", x509); /* Java isn't extracting international subjectAlts properly. (Or OpenSSL isn't storing them properly). */ // DEFAULT.verify("\u82b1\u5b50.co.jp", x509 ); // impl.verify("\u82b1\u5b50.co.jp", x509 ); exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_NO_CNS_FOO); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_THREE_CNS_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "a.foo.com", x509); exceptionPlease(impl, "bar.com", x509); exceptionPlease(impl, "a.bar.com", x509); impl.verify("\u82b1\u5b50.co.jp", x509); exceptionPlease(impl, "a.\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO); x509 = (X509Certificate) cf.generateCertificate(in); exceptionPlease(impl, "foo.com", x509); impl.verify("www.foo.com", x509); impl.verify("\u82b1\u5b50.foo.com", x509); exceptionPlease(impl, "a.b.foo.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_CO_JP); x509 = (X509Certificate) cf.generateCertificate(in); // Silly test because no-one would ever be able to lookup an IP address // using "*.co.jp". impl.verify("*.co.jp", x509); impl.verify("foo.co.jp", x509); impl.verify("\u82b1\u5b50.co.jp", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_WILD_FOO_BAR_HANAKO); x509 = (X509Certificate) cf.generateCertificate(in); // try the foo.com variations exceptionPlease(impl, "foo.com", x509); exceptionPlease(impl, "www.foo.com", x509); exceptionPlease(impl, "\u82b1\u5b50.foo.com", x509); exceptionPlease(impl, "a.b.foo.com", x509); // try the bar.com variations exceptionPlease(impl, "bar.com", x509); impl.verify("www.bar.com", x509); impl.verify("\u82b1\u5b50.bar.com", x509); exceptionPlease(impl, "a.b.bar.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.X509_MULTIPLE_VALUE_AVA); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("repository.infonotary.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("*.google.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.S_GOOGLE_COM); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("*.Google.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("1.1.1.1", x509); exceptionPlease(impl, "1.1.1.2", x509); exceptionPlease(impl, "dummy-value.com", x509); in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME); x509 = (X509Certificate) cf.generateCertificate(in); impl.verify("www.company.com", x509); } private void exceptionPlease(final ZKHostnameVerifier hv, final String host, final X509Certificate x509) { try { hv.verify(host, x509); fail("HostnameVerifier shouldn't allow [" + host + "]"); } catch (final SSLException e) { // whew! we're okay! } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_common_ZK0100644 0000000 0000000 00000000163 15051152474 032633 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/ZKTrustManagerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/common/ZKTrustManagerTest0100644 0000000 0000000 00000031602 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.common; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.math.BigInteger; import java.net.InetAddress; import java.net.Socket; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.X509ExtendedTrustManager; import org.apache.zookeeper.ZKTestCase; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.burningwave.tools.net.DefaultHostResolver; import org.burningwave.tools.net.HostResolutionRequestInterceptor; import org.burningwave.tools.net.MappedHostResolver; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // We can only test calls to ZKTrustManager using Sockets (not SSLEngines). This can be fine since the logic is the same. public class ZKTrustManagerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ZKTrustManagerTest.class); private static KeyPair keyPair; private X509ExtendedTrustManager mockX509ExtendedTrustManager; private static final String IP_ADDRESS = "127.0.0.1"; private static final String HOSTNAME = "localhost"; private Socket mockSocket; @BeforeAll public static void setupDNSMocks() { Map hostAliases = new LinkedHashMap<>(); hostAliases.put(HOSTNAME, IP_ADDRESS); HostResolutionRequestInterceptor.INSTANCE.install( new MappedHostResolver(hostAliases), DefaultHostResolver.INSTANCE ); } @AfterAll public static void clearDNSMocks() { HostResolutionRequestInterceptor.INSTANCE.uninstall(); } @BeforeAll public static void createKeyPair() throws Exception { Security.addProvider(new BouncyCastleProvider()); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); keyPairGenerator.initialize(4096); keyPair = keyPairGenerator.genKeyPair(); } @AfterAll public static void removeBouncyCastleProvider() throws Exception { Security.removeProvider("BC"); } @BeforeEach public void setup() throws Exception { mockX509ExtendedTrustManager = mock(X509ExtendedTrustManager.class); InetAddress mockInetAddress = InetAddress.getByName(HOSTNAME); mockSocket = mock(Socket.class); when(mockSocket.getInetAddress()).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { return mockInetAddress; } }); } private X509Certificate[] createSelfSignedCertifcateChain(String ipAddress, String hostname) throws Exception { X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); nameBuilder.addRDN(BCStyle.CN, "NOT_LOCALHOST"); Date notBefore = new Date(); Calendar cal = Calendar.getInstance(); cal.setTime(notBefore); cal.add(Calendar.YEAR, 1); Date notAfter = cal.getTime(); BigInteger serialNumber = new BigInteger(128, new Random()); X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(nameBuilder.build(), serialNumber, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic()).addExtension(Extension.basicConstraints, true, new BasicConstraints(0)).addExtension(Extension.keyUsage, true, new KeyUsage( KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); List generalNames = new ArrayList<>(); if (ipAddress != null) { generalNames.add(new GeneralName(GeneralName.iPAddress, ipAddress)); } if (hostname != null) { generalNames.add(new GeneralName(GeneralName.dNSName, hostname)); } if (!generalNames.isEmpty()) { certificateBuilder.addExtension(Extension.subjectAlternativeName, true, new GeneralNames(generalNames.toArray(new GeneralName[]{}))); } ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate()); return new X509Certificate[]{new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner))}; } @Test public void testServerHostnameVerificationWithHostnameVerificationDisabled() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, false, false, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(IP_ADDRESS, HOSTNAME); zkTrustManager.checkServerTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(0)).getInetAddress(); assertTrue(hostnameVerifier.hosts.isEmpty()); verify(mockX509ExtendedTrustManager, times(1)).checkServerTrusted(certificateChain, null, mockSocket); } @Test public void testServerHostnameVerificationWithHostnameVerificationDisabledAndClientHostnameVerificationEnabled() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, false, true, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(IP_ADDRESS, HOSTNAME); zkTrustManager.checkServerTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(0)).getInetAddress(); assertTrue(hostnameVerifier.hosts.isEmpty()); verify(mockX509ExtendedTrustManager, times(1)).checkServerTrusted(certificateChain, null, mockSocket); } @Test public void testServerHostnameVerificationWithIPAddress() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, true, false, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(IP_ADDRESS, null); zkTrustManager.checkServerTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(1)).getInetAddress(); assertEquals(Arrays.asList(IP_ADDRESS), hostnameVerifier.hosts); verify(mockX509ExtendedTrustManager, times(1)).checkServerTrusted(certificateChain, null, mockSocket); } @Test public void testServerHostnameVerificationWithHostname() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, true, false, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(null, HOSTNAME); zkTrustManager.checkServerTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(1)).getInetAddress(); assertEquals(Arrays.asList(IP_ADDRESS, HOSTNAME), hostnameVerifier.hosts); verify(mockX509ExtendedTrustManager, times(1)).checkServerTrusted(certificateChain, null, mockSocket); } @Test public void testClientHostnameVerificationWithHostnameVerificationDisabled() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, false, true, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(null, HOSTNAME); zkTrustManager.checkClientTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(1)).getInetAddress(); assertEquals(Arrays.asList(IP_ADDRESS, HOSTNAME), hostnameVerifier.hosts); verify(mockX509ExtendedTrustManager, times(1)).checkClientTrusted(certificateChain, null, mockSocket); } @Test public void testClientHostnameVerificationWithClientHostnameVerificationDisabled() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, true, false, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(null, HOSTNAME); zkTrustManager.checkClientTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(0)).getInetAddress(); assertTrue(hostnameVerifier.hosts.isEmpty()); verify(mockX509ExtendedTrustManager, times(1)).checkClientTrusted(certificateChain, null, mockSocket); } @Test public void testClientHostnameVerificationWithIPAddress() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, true, true, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(IP_ADDRESS, null); zkTrustManager.checkClientTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(1)).getInetAddress(); assertEquals(Arrays.asList(IP_ADDRESS), hostnameVerifier.hosts); verify(mockX509ExtendedTrustManager, times(1)).checkClientTrusted(certificateChain, null, mockSocket); } @Test public void testClientHostnameVerificationWithHostname() throws Exception { VerifiableHostnameVerifier hostnameVerifier = new VerifiableHostnameVerifier(); ZKTrustManager zkTrustManager = new ZKTrustManager(mockX509ExtendedTrustManager, true, true, hostnameVerifier); X509Certificate[] certificateChain = createSelfSignedCertifcateChain(null, HOSTNAME); zkTrustManager.checkClientTrusted(certificateChain, null, mockSocket); verify(mockSocket, times(1)).getInetAddress(); assertEquals(Arrays.asList(IP_ADDRESS, HOSTNAME), hostnameVerifier.hosts); verify(mockX509ExtendedTrustManager, times(1)).checkClientTrusted(certificateChain, null, mockSocket); } static class VerifiableHostnameVerifier extends ZKHostnameVerifier { List hosts = new CopyOnWriteArrayList<>(); @Override public boolean verify(String host, SSLSession session) { throw new IllegalArgumentException("not expected to be called by these tests"); } @Override void verify(String host, X509Certificate cert) throws SSLException { LOG.info("verifyWithX509Certificate {} {}", host, cert); hosts.add(host); super.verify(host, cert); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_compat_Pr0100644 0000000 0000000 00000000164 15051152474 032664 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/compat/ProtocolManagerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/compat/ProtocolManagerTes0100644 0000000 0000000 00000012206 15051152474 034304 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.compat; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ConnectResponse; import org.junit.jupiter.api.Test; public class ProtocolManagerTest { private static byte[] serializeConnectRequest(ConnectRequest request, boolean withReadOnly) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive oa = BinaryOutputArchive.getArchive(baos); request.serialize(oa, "connect"); baos.close(); byte[] bytes = baos.toByteArray(); if (withReadOnly) { return bytes; } else { return Arrays.copyOf(bytes, bytes.length - 1); } } @Test public void testDeserializeConnectRequestWithReadonly() throws IOException { ProtocolManager protocolManager = new ProtocolManager(); ConnectRequest req1 = new ConnectRequest(); req1.setPasswd(new byte[16]); req1.setReadOnly(true); byte[] bytes = serializeConnectRequest(req1, true); // Current protocol. assertEquals(45, bytes.length); InputArchive ia = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes)); ConnectRequest req2 = protocolManager.deserializeConnectRequest(ia); assertEquals(true, protocolManager.isReadonlyAvailable()); assertEquals(true, req2.getReadOnly()); } @Test public void testDeserializeConnectRequestWithoutReadonly() throws IOException { ProtocolManager protocolManager = new ProtocolManager(); ConnectRequest req1 = new ConnectRequest(); req1.setPasswd(new byte[16]); // Should get truncated. req1.setReadOnly(true); byte[] bytes = serializeConnectRequest(req1, false); // 3.4 protocol. assertEquals(44, bytes.length); InputArchive ia = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes)); ConnectRequest req2 = protocolManager.deserializeConnectRequest(ia); assertEquals(false, protocolManager.isReadonlyAvailable()); assertEquals(false, req2.getReadOnly()); } private static ProtocolManager prepareProtocolManager(boolean withReadOnly) throws IOException { ProtocolManager protocolManager = new ProtocolManager(); ConnectRequest req1 = new ConnectRequest(); req1.setPasswd(new byte[16]); req1.setReadOnly(true); byte[] bytes = serializeConnectRequest(req1, withReadOnly); InputArchive ia = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes)); // Detects the current protocol. ConnectRequest req2 = protocolManager.deserializeConnectRequest(ia); return protocolManager; } @Test public void testSerializeConnectResponseWithReadonly() throws IOException { ProtocolManager protocolManager = prepareProtocolManager(true); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive os = BinaryOutputArchive.getArchive(baos); ConnectResponse rsp = new ConnectResponse(); rsp.setPasswd(new byte[16]); rsp.setReadOnly(true); // Should use the current protocol. protocolManager.serializeConnectResponse(rsp, os); baos.close(); byte[] bytes = baos.toByteArray(); assertEquals(37, bytes.length); assertEquals(1, bytes[bytes.length - 1]); } @Test public void testSerializeConnectResponseWithoutReadonly() throws IOException { ProtocolManager protocolManager = prepareProtocolManager(false); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive os = BinaryOutputArchive.getArchive(baos); ConnectResponse rsp = new ConnectResponse(); rsp.setPasswd(new byte[16]); rsp.setReadOnly(true); // Should use the 3.4 protocol. protocolManager.serializeConnectResponse(rsp, os); baos.close(); byte[] bytes = baos.toByteArray(); assertEquals(36, bytes.length); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_metrics_B0100644 0000000 0000000 00000000171 15051152474 032645 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/metrics/BaseTestMetricsProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/metrics/BaseTestMetricsPr0100644 0000000 0000000 00000010647 15051152474 034271 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import org.apache.zookeeper.metrics.impl.NullMetricsProvider; /** * Simple MetricsProvider for tests. */ public abstract class BaseTestMetricsProvider implements MetricsProvider { @Override public void configure(Properties prprts) throws MetricsProviderLifeCycleException { } @Override public void start() throws MetricsProviderLifeCycleException { } @Override public MetricsContext getRootContext() { return NullMetricsProvider.NullMetricsContext.INSTANCE; } @Override public void stop() { } @Override public void dump(BiConsumer sink) { } @Override public void resetAllValues() { } public static final class MetricsProviderCapturingLifecycle extends BaseTestMetricsProvider { public static final AtomicBoolean configureCalled = new AtomicBoolean(); public static final AtomicBoolean startCalled = new AtomicBoolean(); public static final AtomicBoolean stopCalled = new AtomicBoolean(); public static final AtomicBoolean getRootContextCalled = new AtomicBoolean(); public static void reset() { configureCalled.set(false); startCalled.set(false); stopCalled.set(false); getRootContextCalled.set(false); } @Override public void configure(Properties prprts) throws MetricsProviderLifeCycleException { if (!configureCalled.compareAndSet(false, true)) { // called twice throw new IllegalStateException(); } } @Override public void start() throws MetricsProviderLifeCycleException { if (!startCalled.compareAndSet(false, true)) { // called twice throw new IllegalStateException(); } } @Override public MetricsContext getRootContext() { getRootContextCalled.set(true); return NullMetricsProvider.NullMetricsContext.INSTANCE; } @Override public void stop() { if (!stopCalled.compareAndSet(false, true)) { // called twice throw new IllegalStateException(); } } } public static final class MetricsProviderWithErrorInStart extends BaseTestMetricsProvider { @Override public void start() throws MetricsProviderLifeCycleException { throw new MetricsProviderLifeCycleException(); } } public static final class MetricsProviderWithErrorInConfigure extends BaseTestMetricsProvider { @Override public void configure(Properties prprts) throws MetricsProviderLifeCycleException { throw new MetricsProviderLifeCycleException(); } } public static final class MetricsProviderWithConfiguration extends BaseTestMetricsProvider { public static final AtomicInteger httpPort = new AtomicInteger(); @Override public void configure(Properties prprts) throws MetricsProviderLifeCycleException { httpPort.set(Integer.parseInt(prprts.getProperty("httpPort"))); } } public static final class MetricsProviderWithErrorInStop extends BaseTestMetricsProvider { public static final AtomicBoolean stopCalled = new AtomicBoolean(); @Override public void stop() { stopCalled.set(true); throw new RuntimeException(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_metrics_M0100644 0000000 0000000 00000000156 15051152474 032663 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/metrics/MetricsUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/metrics/MetricsUtils.java0100644 0000000 0000000 00000004110 15051152474 034261 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metrics; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.server.ServerMetrics; /** * Utility for Metrics in tests. */ public abstract class MetricsUtils { private MetricsUtils() { } /** * Collect all metrics from a {@link MetricsProvider}. A MetricsProvider * provides a {@link MetricsProvider#dump(java.util.function.BiConsumer) * } * method, that method will in general be more efficient and it does not * impose to the MetricsProvider to waste resources. * * @param metricsProvider * @return a Map which collects one entry per each different key returned by * {@link MetricsProvider#dump(java.util.function.BiConsumer) } */ public static Map collect(MetricsProvider metricsProvider) { Map res = new HashMap<>(); metricsProvider.dump(res::put); return res; } /** * Collect current {@link ServerMetrics} as a Map. * * @return a flattened view of all metrics reported by the MetricsProvider * in use by the current ServerMetrics static instance. */ public static Map currentServerMetrics() { return collect(ServerMetrics.getMetrics().getMetricsProvider()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Bl0100644 0000000 0000000 00000000161 15051152474 032660 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/BlueThrottleTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/BlueThrottleTest.j0100644 0000000 0000000 00000024512 15051152474 034267 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Random; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BlueThrottleTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(BlueThrottleTest.class); private static final int RAPID_TIMEOUT = 10000; class MockRandom extends Random { int flag = 0; BlueThrottle throttle; @Override public double nextDouble() { if (throttle.getDropChance() > 0) { flag = 1 - flag; return flag; } else { return 1; } } } class BlueThrottleWithMockRandom extends BlueThrottle { public BlueThrottleWithMockRandom(MockRandom random) { super(); this.rng = random; random.throttle = this; } } @Test public void testThrottleDisabled() { BlueThrottle throttler = new BlueThrottle(); assertTrue(throttler.checkLimit(1), "Throttle should be disabled by default"); } @Test public void testThrottleWithoutRefill() { BlueThrottle throttler = new BlueThrottle(); throttler.setMaxTokens(1); throttler.setFillTime(2000); assertTrue(throttler.checkLimit(1), "First request should be allowed"); assertFalse(throttler.checkLimit(1), "Second request should be denied"); } @Test public void testThrottleWithRefill() throws InterruptedException { BlueThrottle throttler = new BlueThrottle(); throttler.setMaxTokens(1); throttler.setFillTime(500); assertTrue(throttler.checkLimit(1), "First request should be allowed"); assertFalse(throttler.checkLimit(1), "Second request should be denied"); //wait for the bucket to be refilled Thread.sleep(750); assertTrue(throttler.checkLimit(1), "Third request should be allowed since we've got a new token"); } @Test public void testThrottleWithoutRandomDropping() throws InterruptedException { int maxTokens = 5; BlueThrottle throttler = new BlueThrottleWithMockRandom(new MockRandom()); throttler.setMaxTokens(maxTokens); throttler.setFillCount(maxTokens); throttler.setFillTime(1000); for (int i = 0; i < maxTokens; i++) { throttler.checkLimit(1); } assertEquals(throttler.getMaxTokens(), throttler.getDeficit(), "All tokens should be used up by now"); Thread.sleep(110); throttler.checkLimit(1); assertFalse(throttler.getDropChance() > 0, "Dropping probability should still be zero"); //allow bucket to be refilled Thread.sleep(1500); for (int i = 0; i < maxTokens; i++) { assertTrue(throttler.checkLimit(1), "The first " + maxTokens + " requests should be allowed"); } for (int i = 0; i < maxTokens; i++) { assertFalse(throttler.checkLimit(1), "The latter " + maxTokens + " requests should be denied"); } } @Test public void testThrottleWithRandomDropping() throws InterruptedException { int maxTokens = 5; BlueThrottle throttler = new BlueThrottleWithMockRandom(new MockRandom()); throttler.setMaxTokens(maxTokens); throttler.setFillCount(maxTokens); throttler.setFillTime(1000); throttler.setFreezeTime(100); throttler.setDropIncrease(0.5); for (int i = 0; i < maxTokens; i++) { throttler.checkLimit(1); } assertEquals(throttler.getMaxTokens(), throttler.getDeficit(), "All tokens should be used up by now"); Thread.sleep(120); //this will trigger dropping probability being increased throttler.checkLimit(1); assertTrue(throttler.getDropChance() > 0, "Dropping probability should be increased"); LOG.info("Dropping probability is {}", throttler.getDropChance()); //allow bucket to be refilled Thread.sleep(1100); LOG.info("Bucket is refilled with {} tokens.", maxTokens); int accepted = 0; for (int i = 0; i < maxTokens; i++) { if (throttler.checkLimit(1)) { accepted++; } } LOG.info("Send {} requests, {} are accepted", maxTokens, accepted); assertTrue(accepted < maxTokens, "The dropping should be distributed"); accepted = 0; for (int i = 0; i < maxTokens; i++) { if (throttler.checkLimit(1)) { accepted++; } } LOG.info("Send another {} requests, {} are accepted", maxTokens, accepted); assertTrue(accepted > 0, "Later requests should have a chance"); } private QuorumUtil quorumUtil = new QuorumUtil(1); private ClientBase.CountdownWatcher[] watchers; private ZooKeeper[] zks; private int connect(int n) throws Exception { String connStr = quorumUtil.getConnectionStringForServer(1); int connected = 0; zks = new ZooKeeper[n]; watchers = new ClientBase.CountdownWatcher[n]; for (int i = 0; i < n; i++){ watchers[i] = new ClientBase.CountdownWatcher(); zks[i] = new ZooKeeper(connStr, 3000, watchers[i]); try { watchers[i].waitForConnected(RAPID_TIMEOUT); connected++; } catch (TimeoutException e) { LOG.info("Connection denied by the throttler due to insufficient tokens"); break; } } return connected; } private void shutdownQuorum() throws Exception{ for (ZooKeeper zk : zks) { if (zk != null) { zk.close(); } } quorumUtil.shutdownAll(); } @Test public void testNoThrottling() throws Exception { quorumUtil.startAll(); //disable throttling quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(0); int connected = connect(10); assertEquals(10, connected); shutdownQuorum(); } @Test public void testThrottling() throws Exception { quorumUtil.enableLocalSession(true); quorumUtil.startAll(); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(2); //no refill, makes testing easier quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setFillCount(0); int connected = connect(3); assertEquals(2, connected); shutdownQuorum(); quorumUtil.enableLocalSession(false); quorumUtil.startAll(); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(2); //no refill, makes testing easier quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setFillCount(0); connected = connect(3); assertEquals(2, connected); shutdownQuorum(); } @Test public void testWeighedThrottling() throws Exception { // this test depends on the session weights set to the default values // 3 for global session, 2 for renew sessions, 1 for local sessions BlueThrottle.setConnectionWeightEnabled(true); quorumUtil.enableLocalSession(true); quorumUtil.startAll(); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(10); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setFillCount(0); //try to create 11 local sessions, 10 created, because we have only 10 tokens int connected = connect(11); assertEquals(10, connected); shutdownQuorum(); quorumUtil.enableLocalSession(false); quorumUtil.startAll(); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(10); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setFillCount(0); //tyr to create 11 global sessions, 3 created, because we have 10 tokens and each connection needs 3 connected = connect(11); assertEquals(3, connected); shutdownQuorum(); quorumUtil.startAll(); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(10); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setFillCount(0); connected = connect(2); assertEquals(2, connected); quorumUtil.shutdown(1); watchers[0].waitForDisconnected(RAPID_TIMEOUT); watchers[1].waitForDisconnected(RAPID_TIMEOUT); quorumUtil.restart(1); //client will try to reconnect quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setMaxTokens(3); quorumUtil.getPeer(1).peer.getActiveServer().connThrottle().setFillCount(0); int reconnected = 0; for (int i = 0; i < 2; i++){ try { watchers[i].waitForConnected(RAPID_TIMEOUT); reconnected++; } catch (TimeoutException e) { LOG.info("One reconnect fails due to insufficient tokens"); } } //each reconnect takes two tokens, we have 3, so only one reconnects LOG.info("reconnected {}", reconnected); assertEquals(1, reconnected); shutdownQuorum(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_By0100644 0000000 0000000 00000000172 15051152474 032677 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ByteBufferInputStreamTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ByteBufferInputStr0100644 0000000 0000000 00000012554 15051152474 034333 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ByteBufferInputStreamTest extends ZKTestCase { private static final byte[] DATA_BYTES_0 = "Apache ZooKeeper".getBytes(StandardCharsets.UTF_8); private static byte[] DATA_BYTES; @BeforeAll public static void setUpClass() { int len = DATA_BYTES_0.length + 2; DATA_BYTES = new byte[len]; System.arraycopy(DATA_BYTES_0, 0, DATA_BYTES, 0, DATA_BYTES_0.length); DATA_BYTES[len - 2] = (byte) 0x0; DATA_BYTES[len - 1] = (byte) 0xff; } private ByteBuffer bb; private ByteBufferInputStream in; private byte[] bs; @BeforeEach public void setUp() throws Exception { bb = ByteBuffer.wrap(DATA_BYTES); in = new ByteBufferInputStream(bb); bs = new byte[]{(byte) 1, (byte) 2, (byte) 3, (byte) 4}; } @Test public void testRead() throws Exception { for (int i = 0; i < DATA_BYTES.length; i++) { int b = in.read(); assertEquals(DATA_BYTES[i], (byte) b); } assertEquals(-1, in.read()); } @Test public void testReadArrayOffsetLength() throws Exception { assertEquals(1, in.read(bs, 2, 1)); byte[] expected = new byte[]{(byte) 1, (byte) 2, DATA_BYTES[0], (byte) 4}; assertArrayEquals(expected, bs); } @Test public void testReadArrayOffsetLength_LengthTooLarge() throws Exception { assertThrows(IndexOutOfBoundsException.class, () -> { in.read(bs, 2, 3); }); } @Test public void testReadArrayOffsetLength_HitEndOfStream() throws Exception { for (int i = 0; i < DATA_BYTES.length - 1; i++) { in.read(); } assertEquals(1, in.read(bs, 2, 2)); byte[] expected = new byte[]{(byte) 1, (byte) 2, DATA_BYTES[DATA_BYTES.length - 1], (byte) 4}; assertArrayEquals(expected, bs); } @Test public void testReadArrayOffsetLength_AtEndOfStream() throws Exception { for (int i = 0; i < DATA_BYTES.length; i++) { in.read(); } byte[] expected = Arrays.copyOf(bs, bs.length); assertEquals(-1, in.read(bs, 2, 2)); assertArrayEquals(expected, bs); } @Test public void testReadArrayOffsetLength_0Length() throws Exception { byte[] expected = Arrays.copyOf(bs, bs.length); assertEquals(0, in.read(bs, 2, 0)); assertArrayEquals(expected, bs); } @Test public void testReadArray() throws Exception { byte[] expected = Arrays.copyOf(DATA_BYTES, 4); assertEquals(4, in.read(bs)); assertArrayEquals(expected, bs); } @Test public void testSkip() throws Exception { in.read(); assertEquals(2L, in.skip(2L)); assertEquals(DATA_BYTES[3], in.read()); assertEquals(DATA_BYTES[4], in.read()); } @Test public void testSkip2() throws Exception { for (int i = 0; i < DATA_BYTES.length / 2; i++) { in.read(); } long skipAmount = DATA_BYTES.length / 4; assertEquals(skipAmount, in.skip(skipAmount)); int idx = DATA_BYTES.length / 2 + (int) skipAmount; assertEquals(DATA_BYTES[idx++], in.read()); assertEquals(DATA_BYTES[idx++], in.read()); } @Test public void testNegativeSkip() throws Exception { in.read(); assertEquals(0L, in.skip(-2L)); assertEquals(DATA_BYTES[1], in.read()); assertEquals(DATA_BYTES[2], in.read()); } @Test public void testSkip_HitEnd() throws Exception { for (int i = 0; i < DATA_BYTES.length - 1; i++) { in.read(); } assertEquals(1L, in.skip(2L)); assertEquals(-1, in.read()); } @Test public void testSkip_AtEnd() throws Exception { for (int i = 0; i < DATA_BYTES.length; i++) { in.read(); } assertEquals(0L, in.skip(2L)); assertEquals(-1, in.read()); } @Test public void testAvailable() throws Exception { for (int i = DATA_BYTES.length; i > 0; i--) { assertEquals(i, in.available()); in.read(); } assertEquals(0, in.available()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/CRCTest.java0100644 0000000 0000000 00000014303 15051152474 032746 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.Adler32; import java.util.zip.CheckedInputStream; import org.apache.jute.BinaryInputArchive; import org.apache.jute.InputArchive; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.persistence.FileSnap; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CRCTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(CRCTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); /** * corrupt a file by writing m at 500 b * offset * @param file the file to be corrupted * @throws IOException */ private void corruptFile(File file) throws IOException { // corrupt the logfile RandomAccessFile raf = new RandomAccessFile(file, "rw"); byte[] b = "mahadev".getBytes(); long writeLen = 500L; raf.seek(writeLen); //corrupting the data raf.write(b); raf.close(); } /** return if checksum matches for a snapshot **/ private boolean getCheckSum(File snapFile) throws IOException { DataTree dt = new DataTree(); Map sessions = new ConcurrentHashMap<>(); InputStream snapIS = new BufferedInputStream(new FileInputStream(snapFile)); CheckedInputStream crcIn = new CheckedInputStream(snapIS, new Adler32()); InputArchive ia = BinaryInputArchive.getArchive(crcIn); try { FileSnap.deserialize(dt, sessions, ia); } catch (IOException ie) { // we failed on the most recent snapshot // must be incomplete // try reading the next one // after corrupting snapIS.close(); crcIn.close(); throw ie; } long checksum = crcIn.getChecksum().getValue(); long val = ia.readLong("val"); snapIS.close(); crcIn.close(); return (val != checksum); } /** test checksums for the logs and snapshots. * the reader should fail on reading * a corrupt snapshot and a corrupt log * file * @throws Exception */ @Test public void testChecksums() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(150); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { for (int i = 0; i < 2000; i++) { zk.create("/crctest- " + i, ("/crctest- " + i).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); File versionDir = new File(tmpDir, "version-2"); File[] list = versionDir.listFiles(); //there should be only two files // one the snapshot and the other logFile File snapFile = null; File logFile = null; for (File file : list) { LOG.info("file is {}", file); if (file.getName().startsWith("log")) { logFile = file; corruptFile(logFile); } } FileTxnLog flog = new FileTxnLog(versionDir); TxnIterator itr = flog.read(1); //we will get a checksum failure try { while (itr.next()) { // no op } fail(); } catch (IOException ie) { LOG.warn("crc corruption", ie); } itr.close(); // find the last snapshot FileSnap snap = new FileSnap(versionDir); List snapFiles = snap.findNRecentSnapshots(2); snapFile = snapFiles.get(0); corruptFile(snapFile); boolean cfile; try { cfile = getCheckSum(snapFile); } catch (IOException ie) { //the last snapshot seems incomplete // corrupt the last but one // and use that snapFile = snapFiles.get(1); corruptFile(snapFile); cfile = getCheckSum(snapFile); } assertTrue(cfile); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Cl0100644 0000000 0000000 00000000164 15051152474 032664 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ClientSSLReloadTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ClientSSLReloadTes0100644 0000000 0000000 00000025770 15051152474 034174 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.Security; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import jline.internal.Log; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.KeyStoreFileType; import org.apache.zookeeper.common.X509KeyType; import org.apache.zookeeper.common.X509TestContext; import org.apache.zookeeper.server.embedded.ExitHandler; import org.apache.zookeeper.server.embedded.ZooKeeperServerEmbedded; import org.apache.zookeeper.server.embedded.ZookeeperServeInfo; import org.apache.zookeeper.test.ClientBase; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientSSLReloadTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ClientSSLReloadTest.class); private X509TestContext x509TestContext1; private X509TestContext x509TestContext2; private File dir1; private File dir2; private File keyStoreFile1; private File trustStoreFile1; private File keyStoreFile2; private File trustStoreFile2; @BeforeEach public void setup() throws Exception { dir1 = ClientBase.createEmptyTestDir(); dir2 = ClientBase.createEmptyTestDir(); Security.addProvider(new BouncyCastleProvider()); x509TestContext1 = X509TestContext.newBuilder() .setTempDir(dir1) .setKeyStoreKeyType(X509KeyType.EC) .setTrustStoreKeyType(X509KeyType.EC) .build(); x509TestContext2 = X509TestContext.newBuilder() .setTempDir(dir2) .setKeyStoreKeyType(X509KeyType.EC) .setTrustStoreKeyType(X509KeyType.EC) .build(); keyStoreFile1 = x509TestContext1.getKeyStoreFile(KeyStoreFileType.PEM); trustStoreFile1 = x509TestContext1.getTrustStoreFile(KeyStoreFileType.PEM); keyStoreFile2 = x509TestContext2.getKeyStoreFile(KeyStoreFileType.PEM); trustStoreFile2 = x509TestContext2.getTrustStoreFile(KeyStoreFileType.PEM); String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); } @AfterEach public void teardown() throws Exception { try { FileUtils.deleteDirectory(dir1); FileUtils.deleteDirectory(dir2); } catch (IOException e) { // ignore } Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); } /* * This test performs certificate reload on a ZK server and checks the server presented certificate to the client. * 1) setup() creates two sets of certificates to be used by the test. * 2) Start the ZK server with TLS configuration ("client.certReload" config property will refresh the key and trust store for ClientX509Util at runtime) * 3) ZK client will connect to the server on the secure client port using keyStoreFile1 and trustStoreFile1. * 4) Update the keyStoreFile1 and trustStoreFile1 files in the filesystem with keyStoreFile2 and trustStoreFile2. * 5) Till FileChangeWatcher thread is triggered & SSLContext options are reset, ZK client should continue to connect. * In Junit tests, FileChangeWatcher thread is not triggered immediately upon certifcate update in the filesystem. * 6) Once the certficates are reloaded by the server, ZK client connect will fail. * 7) Next, create a new ZK client with updated keystore & truststore paths (keyStoreFile2 and trustStoreFile2). * 8) Server should accept the connection on the secure client port. */ @Test public void certficateReloadTest() throws Exception { final Properties configZookeeper = getServerConfig(); try (ZooKeeperServerEmbedded zkServer = ZooKeeperServerEmbedded .builder() .baseDir(dir1.toPath()) .configuration(configZookeeper) .exitHandler(ExitHandler.LOG_ONLY) .build()) { zkServer.start(); assertTrue(ClientBase.waitForServerUp(zkServer.getConnectionString(), 60000)); for (int i = 0; i < 100; i++) { ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); if (status.isLeader() && status.isStandaloneMode()) { break; } Thread.sleep(100); } ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); assertTrue(status.isLeader()); assertTrue(status.isStandaloneMode()); CountDownLatch l = new CountDownLatch(1); ZKClientConfig zKClientConfig = getZKClientConfig(); // ZK client object created which will connect with keyStoreFile1 and trustStoreFile1 to the server. try (ZooKeeper zk = new ZooKeeper(zkServer.getSecureConnectionString(), 60000, (WatchedEvent event) -> { switch (event.getState()) { case SyncConnected: l.countDown(); break; } }, zKClientConfig)) { assertTrue(zk.getClientConfig().getBoolean(ZKClientConfig.SECURE_CLIENT)); assertTrue(l.await(10, TimeUnit.SECONDS)); } Log.info("Updating keyStore & trustStore files !!!!"); // Update the keyStoreFile1 and trustStoreFile1 files in the filesystem with keyStoreFile2 & trustStoreFile2 FileUtils.writeStringToFile(keyStoreFile1, FileUtils.readFileToString(keyStoreFile2, StandardCharsets.US_ASCII), StandardCharsets.US_ASCII, false); FileUtils.writeStringToFile(trustStoreFile1, FileUtils.readFileToString(trustStoreFile2, StandardCharsets.US_ASCII), StandardCharsets.US_ASCII, false); // Till FileChangeWatcher thread is triggered & SSLContext options are reset, ZK client should continue connecting. for (int i = 0; i < 5; i++) { CountDownLatch l2 = new CountDownLatch(1); Thread.sleep(5000); try (ZooKeeper zk = new ZooKeeper(zkServer.getSecureConnectionString(), 60000, (WatchedEvent event) -> { switch (event.getState()) { case SyncConnected: l.countDown(); break; } }, zKClientConfig)) { if (!l2.await(5, TimeUnit.SECONDS)) { LOG.error("Unable to connect to zk server"); break; } } } // Use the updated keyStore and trustStore paths when creating the client; Refreshed server should authenticate the client. zKClientConfig.setProperty("zookeeper.ssl.keyStore.location", keyStoreFile2.getAbsolutePath()); zKClientConfig.setProperty("zookeeper.ssl.trustStore.location", trustStoreFile2.getAbsolutePath()); zKClientConfig.setProperty("zookeeper.ssl.keyStore.type", "PEM"); zKClientConfig.setProperty("zookeeper.ssl.trustStore.type", "PEM"); CountDownLatch l3 = new CountDownLatch(1); try (ZooKeeper zk = new ZooKeeper(zkServer.getSecureConnectionString(), 60000, (WatchedEvent event) -> { switch (event.getState()) { case SyncConnected: l3.countDown(); break; } }, zKClientConfig)) { assertTrue(zk.getClientConfig().getBoolean(ZKClientConfig.SECURE_CLIENT)); assertTrue(l3.await(10, TimeUnit.SECONDS)); } } } private Properties getServerConfig() { int clientPort = PortAssignment.unique(); int clientSecurePort = PortAssignment.unique(); final Properties configZookeeper = new Properties(); configZookeeper.put("clientPort", clientPort + ""); configZookeeper.put("secureClientPort", clientSecurePort + ""); configZookeeper.put("host", "localhost"); configZookeeper.put("ticktime", "4000"); configZookeeper.put("client.certReload", "true"); // TLS config fields configZookeeper.put("ssl.keyStore.location", keyStoreFile1.getAbsolutePath()); configZookeeper.put("ssl.trustStore.location", trustStoreFile1.getAbsolutePath()); configZookeeper.put("ssl.keyStore.type", "PEM"); configZookeeper.put("ssl.trustStore.type", "PEM"); // Netty is required for TLS configZookeeper.put("serverCnxnFactory", org.apache.zookeeper.server.NettyServerCnxnFactory.class.getName()); return configZookeeper; } private ZKClientConfig getZKClientConfig() throws IOException { // Saving copies in JKS format to be used for client calls even after writeStringToFile overwrites keyStoreFile1 and trustStoreFile1 File clientKeyStore = x509TestContext1.getKeyStoreFile(KeyStoreFileType.JKS); File clientTrustStore = x509TestContext1.getTrustStoreFile(KeyStoreFileType.JKS); ZKClientConfig zKClientConfig = new ZKClientConfig(); zKClientConfig.setProperty("zookeeper.client.secure", "true"); zKClientConfig.setProperty("zookeeper.ssl.keyStore.location", clientKeyStore.getAbsolutePath()); zKClientConfig.setProperty("zookeeper.ssl.trustStore.location", clientTrustStore.getAbsolutePath()); zKClientConfig.setProperty("zookeeper.ssl.keyStore.type", "JKS"); zKClientConfig.setProperty("zookeeper.ssl.trustStore.type", "JKS"); // only netty supports TLS zKClientConfig.setProperty("zookeeper.clientCnxnSocket", org.apache.zookeeper.ClientCnxnSocketNetty.class.getName()); return zKClientConfig; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Co0100644 0000000 0000000 00000000166 15051152474 032671 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ConnectionMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ConnectionMetricsT0100644 0000000 0000000 00000021014 15051152474 034326 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.server.NIOServerCnxnFactory.ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import io.netty.channel.Channel; import io.netty.channel.EventLoop; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ConnectionMetricsTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ConnectionMetricsTest.class); @Test public void testRevalidateCount() throws Exception { ServerMetrics.getMetrics().resetAll(); QuorumUtil util = new QuorumUtil(1); // create a quorum of 3 servers // disable local session to make sure we create a global session util.enableLocalSession(false); util.startAll(); int follower1 = (int) util.getFollowerQuorumPeers().get(0).getMyId(); int follower2 = (int) util.getFollowerQuorumPeers().get(1).getMyId(); LOG.info("connecting to server: {}", follower1); ClientBase.CountdownWatcher watcher = new ClientBase.CountdownWatcher(); // create a connection to follower ZooKeeper zk = new ZooKeeper(util.getConnectionStringForServer(follower1), ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); LOG.info("connected"); // update the connection to allow to connect to the other follower zk.updateServerList(util.getConnectionStringForServer(follower2)); // follower is shut down and zk should be disconnected util.shutdown(follower1); watcher.waitForDisconnected(ClientBase.CONNECTION_TIMEOUT); LOG.info("disconnected"); // should reconnect to another follower, will ask leader to revalidate watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); LOG.info("reconnected"); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("connection_revalidate_count")); assertEquals(1L, values.get("revalidate_count")); zk.close(); util.shutdownAll(); } private class MockNIOServerCnxn extends NIOServerCnxn { public MockNIOServerCnxn(ZooKeeperServer zk, SocketChannel sock, SelectionKey sk, NIOServerCnxnFactory factory, NIOServerCnxnFactory.SelectorThread selectorThread) throws IOException { super(zk, sock, sk, factory, selectorThread); } @Override protected boolean isSocketOpen() { return true; } } private static class FakeSK extends SelectionKey { @Override public SelectableChannel channel() { return null; } @Override public Selector selector() { return mock(Selector.class); } @Override public boolean isValid() { return true; } @Override public void cancel() { } @Override public int interestOps() { return ops; } private int ops = OP_WRITE + OP_READ; @Override public SelectionKey interestOps(int ops) { this.ops = ops; return this; } @Override public int readyOps() { return ops; } } private NIOServerCnxn createMockNIOCnxn() throws IOException { InetSocketAddress socketAddr = new InetSocketAddress(80); Socket socket = mock(Socket.class); when(socket.getRemoteSocketAddress()).thenReturn(socketAddr); SocketChannel sock = mock(SocketChannel.class); when(sock.socket()).thenReturn(socket); when(sock.read(any(ByteBuffer.class))).thenReturn(-1); return new MockNIOServerCnxn(mock(ZooKeeperServer.class), sock, null, mock(NIOServerCnxnFactory.class), null); } @Test public void testNIOConnectionDropCount() throws Exception { ServerMetrics.getMetrics().resetAll(); NIOServerCnxn cnxn = createMockNIOCnxn(); cnxn.doIO(new FakeSK()); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("connection_drop_count")); } @Test public void testNettyConnectionDropCount() throws Exception { InetSocketAddress socketAddr = new InetSocketAddress(80); Channel channel = mock(Channel.class); when(channel.isOpen()).thenReturn(false); when(channel.remoteAddress()).thenReturn(socketAddr); EventLoop eventLoop = mock(EventLoop.class); when(channel.eventLoop()).thenReturn(eventLoop); ServerMetrics.getMetrics().resetAll(); NettyServerCnxnFactory factory = new NettyServerCnxnFactory(); NettyServerCnxn cnxn = new NettyServerCnxn(channel, mock(ZooKeeperServer.class), factory); // pretend it's connected factory.cnxns.add(cnxn); cnxn.close(); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("connection_drop_count")); } @Test public void testSessionlessConnectionsExpired() throws Exception { ServerCnxnFactory factory = new NIOServerCnxnFactory(); factory.configure(new InetSocketAddress(PortAssignment.unique()), 1000); factory.start(); int timeout = Integer.getInteger(ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT, 10000); ServerMetrics.getMetrics().resetAll(); // add two connections w/o touching them so they will expire ((NIOServerCnxnFactory) factory).touchCnxn(createMockNIOCnxn()); ((NIOServerCnxnFactory) factory).touchCnxn(createMockNIOCnxn()); Map values = MetricsUtils.currentServerMetrics(); int sleptTime = 0; while (values.get("sessionless_connections_expired") == null || sleptTime < 2 * timeout) { Thread.sleep(100); sleptTime += 100; values = MetricsUtils.currentServerMetrics(); } assertEquals(2L, values.get("sessionless_connections_expired")); factory.shutdown(); } @Test public void testStaleSessionsExpired() throws Exception { int tickTime = 1000; SessionTrackerImpl tracker = new SessionTrackerImpl(mock(ZooKeeperServer.class), new ConcurrentHashMap<>(), tickTime, 1L, null); tracker.sessionsById.put(1L, mock(SessionTrackerImpl.SessionImpl.class)); tracker.sessionsById.put(2L, mock(SessionTrackerImpl.SessionImpl.class)); tracker.touchSession(1L, tickTime); tracker.touchSession(2L, tickTime); ServerMetrics.getMetrics().resetAll(); tracker.start(); Map values = MetricsUtils.currentServerMetrics(); int sleptTime = 0; while (values.get("stale_sessions_expired") == null || sleptTime < 2 * tickTime) { Thread.sleep(100); sleptTime += 100; values = MetricsUtils.currentServerMetrics(); } assertEquals(2L, values.get("stale_sessions_expired")); tracker.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Cr0100644 0000000 0000000 00000000164 15051152474 032672 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/CreateContainerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/CreateContainerTes0100644 0000000 0000000 00000036750 15051152474 034313 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DeleteContainerRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class CreateContainerTest extends ClientBase { private ZooKeeper zk; private Semaphore completedContainerDeletions; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); completedContainerDeletions = new Semaphore(0); ZKDatabase testDatabase = new ZKDatabase(serverFactory.zkServer.getZKDatabase().snapLog) { @Override public void addCommittedProposal(Request request) { super.addCommittedProposal(request); if (request.type == ZooDefs.OpCode.deleteContainer) { completedContainerDeletions.release(); } } }; serverFactory.zkServer.setZKDatabase(testDatabase); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); zk.close(); } @Test @Timeout(value = 30) public void testCreate() throws KeeperException, InterruptedException { createNoStatVerifyResult("/foo"); createNoStatVerifyResult("/foo/child"); } @Test @Timeout(value = 30) public void testCreateWithStat() throws KeeperException, InterruptedException { Stat stat = createWithStatVerifyResult("/foo"); Stat childStat = createWithStatVerifyResult("/foo/child"); // Don't expect to get the same stats for different creates. assertNotEquals(stat, childStat); } @SuppressWarnings("ConstantConditions") @Test @Timeout(value = 30) public void testCreateWithNullStat() throws KeeperException, InterruptedException { final String name = "/foo"; assertNull(zk.exists(name, false)); Stat stat = null; // If a null Stat object is passed the create should still // succeed, but no Stat info will be returned. zk.create(name, name.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER, stat); assertNull(stat); assertNotNull(zk.exists(name, false)); } @Test @Timeout(value = 30) public void testSimpleDeletion() throws KeeperException, InterruptedException { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/foo/bar", -1); // should cause "/foo" to get deleted when checkContainers() is called ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100); containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNull(zk.exists("/foo", false), "Container should have been deleted"); } @Test @Timeout(value = 30) public void testMultiWithContainerSimple() throws KeeperException, InterruptedException { Op createContainer = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); zk.multi(Collections.singletonList(createContainer)); DataTree dataTree = serverFactory.getZooKeeperServer().getZKDatabase().getDataTree(); assertEquals(dataTree.getContainers().size(), 1); } @Test @Timeout(value = 30) public void testMultiWithContainer() throws KeeperException, InterruptedException { Op createContainer = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); Op createChild = Op.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.multi(Arrays.asList(createContainer, createChild)); DataTree dataTree = serverFactory.getZooKeeperServer().getZKDatabase().getDataTree(); assertEquals(dataTree.getContainers().size(), 1); zk.delete("/foo/bar", -1); // should cause "/foo" to get deleted when checkContainers() is called ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100); containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNull(zk.exists("/foo", false), "Container should have been deleted"); createContainer = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); createChild = Op.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Op deleteChild = Op.delete("/foo/bar", -1); zk.multi(Arrays.asList(createContainer, createChild, deleteChild)); containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNull(zk.exists("/foo", false), "Container should have been deleted"); } @Test @Timeout(value = 30) public void testSimpleDeletionAsync() throws KeeperException, InterruptedException { final CountDownLatch latch = new CountDownLatch(1); AsyncCallback.Create2Callback cb = (rc, path, ctx, name, stat) -> { assertEquals(ctx, "context"); latch.countDown(); }; zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER, cb, "context"); assertTrue(latch.await(5, TimeUnit.SECONDS)); zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/foo/bar", -1); // should cause "/foo" to get deleted when checkContainers() is called ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100); containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNull(zk.exists("/foo", false), "Container should have been deleted"); } @Test @Timeout(value = 30) public void testCascadingDeletion() throws KeeperException, InterruptedException { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); zk.create("/foo/bar/one", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/foo/bar/one", -1); // should cause "/foo/bar" and "/foo" to get deleted when checkContainers() is called ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100); containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNull(zk.exists("/foo/bar", false), "Container should have been deleted"); assertNull(zk.exists("/foo", false), "Container should have been deleted"); } @Test @Timeout(value = 30) public void testFalseEmpty() throws KeeperException, InterruptedException { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100) { @Override protected Collection getCandidates() { return Collections.singletonList("/foo"); } }; containerManager.checkContainers(); assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNotNull(zk.exists("/foo", false), "Container should have not been deleted"); } @Test @Timeout(value = 30) public void testMaxPerMinute() throws InterruptedException { final BlockingQueue queue = new LinkedBlockingQueue<>(); RequestProcessor processor = new RequestProcessor() { @Override public void processRequest(Request request) { try { queue.add(request.readRequestRecord(DeleteContainerRequest::new).getPath()); } catch (IOException e) { fail(e); } } @Override public void shutdown() { } }; final ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), processor, 1, 2) { @Override protected long getMinIntervalMs() { return 1000; } @Override protected Collection getCandidates() { return Arrays.asList("/one", "/two", "/three", "/four"); } }; Executors.newSingleThreadExecutor().submit(() -> { containerManager.checkContainers(); return null; }); assertEquals("/one", queue.poll(5, TimeUnit.SECONDS)); assertEquals("/two", queue.poll(5, TimeUnit.SECONDS)); assertEquals(0, queue.size()); Thread.sleep(500); assertEquals(0, queue.size()); assertEquals("/three", queue.poll(5, TimeUnit.SECONDS)); assertEquals("/four", queue.poll(5, TimeUnit.SECONDS)); } @Test @Timeout(value = 30) public void testMaxNeverUsedInterval() throws KeeperException, InterruptedException { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); AtomicLong elapsed = new AtomicLong(0); AtomicInteger deletesQty = new AtomicInteger(0); ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100, 1000) { @Override protected void postDeleteRequest(Request request) throws RequestProcessor.RequestProcessorException { deletesQty.incrementAndGet(); super.postDeleteRequest(request); } @Override protected long getElapsed(DataNode node) { return elapsed.get(); } }; containerManager.checkContainers(); // elapsed time will appear to be 0 - container will not get deleted assertEquals(deletesQty.get(), 0); assertNotNull(zk.exists("/foo", false), "Container should not have been deleted"); elapsed.set(10000); containerManager.checkContainers(); // elapsed time will appear to be 10000 - container should get deleted assertTrue(completedContainerDeletions.tryAcquire(1, TimeUnit.SECONDS)); assertNull(zk.exists("/foo", false), "Container should have been deleted"); } @Test @Timeout(value = 30) public void testZeroMaxNeverUsedInterval() throws KeeperException, InterruptedException { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); AtomicInteger deletesQty = new AtomicInteger(0); ContainerManager containerManager = new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100, 0) { @Override protected void postDeleteRequest(Request request) throws RequestProcessor.RequestProcessorException { deletesQty.incrementAndGet(); super.postDeleteRequest(request); } @Override protected long getElapsed(DataNode node) { return 10000; // some number greater than 0 } }; containerManager.checkContainers(); // elapsed time will appear to be 0 - container will not get deleted assertEquals(deletesQty.get(), 0); assertNotNull(zk.exists("/foo", false), "Container should not have been deleted"); } private void createNoStatVerifyResult(String newName) throws KeeperException, InterruptedException { assertNull(zk.exists(newName, false), "Node existed before created"); zk.create(newName, newName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER); assertNotNull(zk.exists(newName, false), "Node was not created as expected"); } private Stat createWithStatVerifyResult(String newName) throws KeeperException, InterruptedException { assertNull(zk.exists(newName, false), "Node existed before created"); Stat stat = new Stat(); zk.create(newName, newName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER, stat); validateCreateStat(stat, newName); Stat referenceStat = zk.exists(newName, false); assertNotNull(referenceStat, "Node was not created as expected"); assertEquals(referenceStat, stat); return stat; } private void validateCreateStat(Stat stat, String name) { assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(0, stat.getNumChildren()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Cr0100644 0000000 0000000 00000000156 15051152474 032673 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/CreateTTLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/CreateTTLTest.java0100644 0000000 0000000 00000032474 15051152474 034137 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.CreateOptions; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.CreateResponse; import org.apache.zookeeper.proto.CreateTTLRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; public class CreateTTLTest extends ClientBase { private TestableZooKeeper zk; private static final Collection disabledTests = Collections.singleton("testDisabled"); @Override public void setUp() throws Exception { // to be able to get the test method name a testInfo object is needed // to override the parent's setUp method we need this empty method } @BeforeEach public void setUp(TestInfo testInfo) throws Exception { System.setProperty( EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY, disabledTests.contains(testInfo.getTestMethod().get().getName()) ? "false" : "true"); super.setUpWithServerId(254); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { System.clearProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY); super.tearDown(); zk.close(); } @Test public void testCreate() throws KeeperException, InterruptedException { Stat stat = new Stat(); zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100); assertEquals(0, stat.getEphemeralOwner()); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists("/foo", false), "Ttl node should not have been deleted yet"); fakeElapsed.set(1000); containerManager.checkContainers(); assertNull(zk.exists("/foo", false), "Ttl node should have been deleted"); } @Test public void testBadTTLs() throws InterruptedException, KeeperException { RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.createTTL); String path = "/bad_ttl"; CreateTTLRequest request = new CreateTTLRequest(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL.toFlag(), -100); CreateResponse response = new CreateResponse(); ReplyHeader r = zk.submitRequest(h, request, response, null); assertEquals(r.getErr(), Code.BADARGUMENTS.intValue(), "An invalid CreateTTLRequest should throw BadArguments"); assertNull(zk.exists(path, false), "An invalid CreateTTLRequest should not result in znode creation"); request = new CreateTTLRequest(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL.toFlag(), EphemeralType.TTL.maxValue() + 1); response = new CreateResponse(); r = zk.submitRequest(h, request, response, null); assertEquals(r.getErr(), Code.BADARGUMENTS.intValue(), "An invalid CreateTTLRequest should throw BadArguments"); assertNull(zk.exists(path, false), "An invalid CreateTTLRequest should not result in znode creation"); } @Test public void testMaxTTLs() throws InterruptedException, KeeperException { RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.createTTL); String path = "/bad_ttl"; CreateTTLRequest request = new CreateTTLRequest(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL.toFlag(), EphemeralType.TTL.maxValue()); CreateResponse response = new CreateResponse(); ReplyHeader r = zk.submitRequest(h, request, response, null); assertEquals(r.getErr(), Code.OK.intValue(), "EphemeralType.getMaxTTL() should succeed"); assertNotNull(zk.exists(path, false), "Node should exist"); } @Test public void testCreateSequential() throws KeeperException, InterruptedException { Stat stat = new Stat(); String path = zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, stat, 100); assertEquals(0, stat.getEphemeralOwner()); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists(path, false), "Ttl node should not have been deleted yet"); fakeElapsed.set(1000); containerManager.checkContainers(); assertNull(zk.exists(path, false), "Ttl node should have been deleted"); } @Test public void testCreateAsync() throws KeeperException, InterruptedException { AsyncCallback.Create2Callback callback = (rc, path, ctx, name, stat) -> { // NOP }; zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, callback, null, 100); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists("/foo", false), "Ttl node should not have been deleted yet"); fakeElapsed.set(1000); containerManager.checkContainers(); assertNull(zk.exists("/foo", false), "Ttl node should have been deleted"); } @Test public void testModifying() throws KeeperException, InterruptedException { Stat stat = new Stat(); zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100); assertEquals(0, stat.getEphemeralOwner()); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists("/foo", false), "Ttl node should not have been deleted yet"); for (int i = 0; i < 10; ++i) { fakeElapsed.set(50); zk.setData("/foo", new byte[i + 1], -1); containerManager.checkContainers(); assertNotNull(zk.exists("/foo", false), "Ttl node should not have been deleted yet"); } fakeElapsed.set(200); containerManager.checkContainers(); assertNull(zk.exists("/foo", false), "Ttl node should have been deleted"); } @Test public void testMulti() throws KeeperException, InterruptedException { CreateOptions options = CreateOptions .newBuilder(ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL) .withTtl(100) .build(); CreateOptions sequentialOptions = CreateOptions .newBuilder(ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL) .withTtl(200) .build(); Op createTtl = Op.create("/a", new byte[0], options.getAcl(), options.getCreateMode(), options.getTtl()); Op createTtl2 = Op.create("/a2", new byte[0], options); Op createTtlSequential = Op.create("/b", new byte[0], sequentialOptions.getAcl(), sequentialOptions.getCreateMode(), sequentialOptions.getTtl()); Op createTtlSequential2 = Op.create("/b2", new byte[0], sequentialOptions); Op createNonTtl = Op.create("/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List results = zk.multi(Arrays.asList(createTtl, createTtl2, createTtlSequential, createTtlSequential2, createNonTtl)); String sequentialPath = ((OpResult.CreateResult) results.get(2)).getPath(); String sequentialPath2 = ((OpResult.CreateResult) results.get(3)).getPath(); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists("/a", false), "node should not have been deleted yet"); assertNotNull(zk.exists("/a2", false), "node should not have been deleted yet"); assertNotNull(zk.exists(sequentialPath, false), "node should not have been deleted yet"); assertNotNull(zk.exists(sequentialPath2, false), "node should not have been deleted yet"); assertNotNull(zk.exists("/c", false), "node should never be deleted"); fakeElapsed.set(110); containerManager.checkContainers(); assertNull(zk.exists("/a", false), "node should have been deleted"); assertNull(zk.exists("/a2", false), "node should have been deleted"); assertNotNull(zk.exists(sequentialPath, false), "node should not have been deleted yet"); assertNotNull(zk.exists(sequentialPath2, false), "node should not have been deleted yet"); assertNotNull(zk.exists("/c", false), "node should never be deleted"); fakeElapsed.set(210); containerManager.checkContainers(); assertNull(zk.exists("/a", false), "node should have been deleted"); assertNull(zk.exists("/a2", false), "node should have been deleted"); assertNull(zk.exists(sequentialPath, false), "node should have been deleted"); assertNull(zk.exists(sequentialPath2, false), "node should have been deleted"); assertNotNull(zk.exists("/c", false), "node should never be deleted"); } @Test public void testBadUsage() throws KeeperException, InterruptedException { for (CreateMode createMode : CreateMode.values()) { try { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode, new Stat(), createMode.isTTL() ? 0 : 100); fail("should have thrown IllegalArgumentException"); } catch (IllegalArgumentException dummy) { // correct } } for (CreateMode createMode : CreateMode.values()) { AsyncCallback.Create2Callback callback = (rc, path, ctx, name, stat) -> { // NOP }; try { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, createMode, callback, null, createMode.isTTL() ? 0 : 100); fail("should have thrown IllegalArgumentException"); } catch (IllegalArgumentException dummy) { // correct } } try { Op op = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, 0); zk.multi(Collections.singleton(op)); fail("should have thrown IllegalArgumentException"); } catch (IllegalArgumentException dummy) { // correct } try { Op op = Op.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, 0); zk.multi(Collections.singleton(op)); fail("should have thrown IllegalArgumentException"); } catch (IllegalArgumentException dummy) { // correct } } @Test public void testDisabled() throws KeeperException, InterruptedException { assertThrows(KeeperException.UnimplementedException.class, () -> { // note, setUp() enables this test based on the test name zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 100); }); } private ContainerManager newContainerManager(final AtomicLong fakeElapsed) { return new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100) { @Override protected long getElapsed(DataNode node) { return fakeElapsed.get(); } }; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/DataNodeTest.java0100644 0000000 0000000 00000004525 15051152474 034023 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import java.util.Set; import org.junit.jupiter.api.Test; public class DataNodeTest { @Test public void testGetChildrenShouldReturnEmptySetWhenThereAreNoChidren() { // create DataNode and call getChildren DataNode dataNode = new DataNode(); Set children = dataNode.getChildren(); assertNotNull(children); assertEquals(0, children.size()); // add child,remove child and then call getChildren String child = "child"; dataNode.addChild(child); dataNode.removeChild(child); children = dataNode.getChildren(); assertNotNull(children); assertEquals(0, children.size()); // Returned empty set must not be modifiable children = dataNode.getChildren(); try { children.add("new child"); fail("UnsupportedOperationException is expected"); } catch (UnsupportedOperationException e) { // do nothing } } @Test public void testGetChildrenReturnsImmutableEmptySet() { DataNode dataNode = new DataNode(); Set children = dataNode.getChildren(); try { children.add("new child"); fail("UnsupportedOperationException is expected"); } catch (UnsupportedOperationException e) { // do nothing } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/DataTreeTest.java0100644 0000000 0000000 00000073306 15051152474 034040 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.Record; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.KeeperException.NodeExistsException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.common.PathTrie; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DataTreeTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(DataTreeTest.class); /** * For ZOOKEEPER-1755 - Test race condition when taking dumpEphemerals and * removing the session related ephemerals from DataTree structure */ @Test @Timeout(value = 60) public void testDumpEphemerals() throws Exception { int count = 1000; long session = 1000; long zxid = 2000; final DataTree dataTree = new DataTree(); LOG.info("Create {} zkclient sessions and its ephemeral nodes", count); createEphemeralNode(session, dataTree, count); final AtomicBoolean exceptionDuringDumpEphemerals = new AtomicBoolean(false); final AtomicBoolean running = new AtomicBoolean(true); Thread thread = new Thread() { public void run() { PrintWriter pwriter = new PrintWriter(new StringWriter()); try { while (running.get()) { dataTree.dumpEphemerals(pwriter); } } catch (Exception e) { LOG.error("Received exception while dumpEphemerals!", e); exceptionDuringDumpEphemerals.set(true); } } }; thread.start(); LOG.debug("Killing {} zkclient sessions and its ephemeral nodes", count); killZkClientSession(session, zxid, dataTree, count); running.set(false); thread.join(); assertFalse(exceptionDuringDumpEphemerals.get(), "Should have got exception while dumpEphemerals!"); } private void killZkClientSession(long session, long zxid, final DataTree dataTree, int count) { for (int i = 0; i < count; i++) { dataTree.killSession(session + i, zxid); } } private void createEphemeralNode(long session, final DataTree dataTree, int count) throws NoNodeException, NodeExistsException { for (int i = 0; i < count; i++) { dataTree.createNode("/test" + i, new byte[0], null, session + i, dataTree.getNode("/").stat.getCversion() + 1, 1, 1); } } @Test @Timeout(value = 60) public void testRootWatchTriggered() throws Exception { DataTree dt = new DataTree(); CompletableFuture fire = new CompletableFuture<>(); // set a watch on the root node dt.getChildren("/", new Stat(), event -> { if (event.getPath().equals("/")) { fire.complete(null); } }); // add a new node, should trigger a watch dt.createNode("/xyz", new byte[0], null, 0, dt.getNode("/").stat.getCversion() + 1, 1, 1); assertTrue(fire.isDone(), "Root node watch not triggered"); } /** * For ZOOKEEPER-1046 test if cversion is getting incremented correctly. */ @Test @Timeout(value = 60) public void testIncrementCversion() throws Exception { try { // digestCalculator gets initialized for the new DataTree constructor based on the system property ZooKeeperServer.setDigestEnabled(true); DataTree dt = new DataTree(); dt.createNode("/test", new byte[0], null, 0, dt.getNode("/").stat.getCversion() + 1, 1, 1); DataNode zk = dt.getNode("/test"); int prevCversion = zk.stat.getCversion(); long prevPzxid = zk.stat.getPzxid(); long digestBefore = dt.getTreeDigest(); dt.setCversionPzxid("/test/", prevCversion + 1, prevPzxid + 1); int newCversion = zk.stat.getCversion(); long newPzxid = zk.stat.getPzxid(); assertTrue((newCversion == prevCversion + 1 && newPzxid == prevPzxid + 1), " verification failed. Expected: <" + (prevCversion + 1) + ", " + (prevPzxid + 1) + ">, found: <" + newCversion + ", " + newPzxid + ">"); assertNotEquals(digestBefore, dt.getTreeDigest()); } finally { ZooKeeperServer.setDigestEnabled(false); } } @Test public void testNoCversionRevert() throws Exception { DataTree dt = new DataTree(); DataNode parent = dt.getNode("/"); dt.createNode("/test", new byte[0], null, 0, parent.stat.getCversion() + 1, 1, 1); int currentCversion = parent.stat.getCversion(); long currentPzxid = parent.stat.getPzxid(); dt.createNode("/test1", new byte[0], null, 0, currentCversion - 1, 1, 1); parent = dt.getNode("/"); int newCversion = parent.stat.getCversion(); long newPzxid = parent.stat.getPzxid(); assertTrue((newCversion >= currentCversion && newPzxid >= currentPzxid), " verification failed. Expected: <" + currentCversion + ", " + currentPzxid + ">, found: <" + newCversion + ", " + newPzxid + ">"); } @Test public void testPzxidUpdatedWhenDeletingNonExistNode() throws Exception { DataTree dt = new DataTree(); DataNode root = dt.getNode("/"); long currentPzxid = root.stat.getPzxid(); // pzxid updated with deleteNode on higher zxid long zxid = currentPzxid + 1; try { dt.deleteNode("/testPzxidUpdatedWhenDeletingNonExistNode", zxid); } catch (NoNodeException e) { /* expected */ } root = dt.getNode("/"); currentPzxid = root.stat.getPzxid(); assertEquals(currentPzxid, zxid); // pzxid not updated with smaller zxid long prevPzxid = currentPzxid; zxid = prevPzxid - 1; try { dt.deleteNode("/testPzxidUpdatedWhenDeletingNonExistNode", zxid); } catch (NoNodeException e) { /* expected */ } root = dt.getNode("/"); currentPzxid = root.stat.getPzxid(); assertEquals(currentPzxid, prevPzxid); } @Test public void testDigestUpdatedWhenReplayCreateTxnForExistNode() { try { // digestCalculator gets initialized for the new DataTree constructor based on the system property ZooKeeperServer.setDigestEnabled(true); DataTree dt = new DataTree(); dt.processTxn(new TxnHeader(13, 1000, 1, 30, ZooDefs.OpCode.create), new CreateTxn("/foo", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1)); // create the same node with a higher cversion to simulate the // scenario when replaying a create txn for an existing node due // to fuzzy snapshot dt.processTxn(new TxnHeader(13, 1000, 1, 30, ZooDefs.OpCode.create), new CreateTxn("/foo", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 2)); // check the current digest value assertEquals(dt.getTreeDigest(), dt.getLastProcessedZxidDigest().getDigest()); } finally { ZooKeeperServer.setDigestEnabled(false); } } @Test @Timeout(value = 60) public void testPathTrieClearOnDeserialize() throws Exception { //Create a DataTree with quota nodes so PathTrie get updated DataTree dserTree = new DataTree(); dserTree.createNode("/bug", new byte[20], null, -1, 1, 1, 1); dserTree.createNode(Quotas.quotaPath("/bug"), null, null, -1, 1, 1, 1); dserTree.createNode(Quotas.limitPath("/bug"), new byte[20], null, -1, 1, 1, 1); dserTree.createNode(Quotas.statPath("/bug"), new byte[20], null, -1, 1, 1, 1); //deserialize a DataTree; this should clear the old /bug nodes and pathTrie DataTree tree = new DataTree(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); tree.serialize(oa, "test"); baos.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); BinaryInputArchive ia = BinaryInputArchive.getArchive(bais); dserTree.deserialize(ia, "test"); Field pfield = DataTree.class.getDeclaredField("pTrie"); pfield.setAccessible(true); PathTrie pTrie = (PathTrie) pfield.get(dserTree); //Check that the node path is removed from pTrie assertEquals("/", pTrie.findMaxPrefix("/bug"), "/bug is still in pTrie"); } /* ZOOKEEPER-3531 - org.apache.zookeeper.server.DataTree#serialize calls the aclCache.serialize when doing * dataree serialization, however, org.apache.zookeeper.server.ReferenceCountedACLCache#serialize * could get stuck at OutputArchieve.writeInt due to potential network/disk issues. * This can cause the system experiences hanging issues similar to ZooKeeper-2201. * This test verifies the fix that we should not hold ACL cache during dumping aclcache to snapshots */ @Test @Timeout(value = 60) public void testSerializeDoesntLockACLCacheWhileWriting() throws Exception { DataTree tree = new DataTree(); tree.createNode("/marker", new byte[] { 42 }, null, -1, 1, 1, 1); final AtomicBoolean ranTestCase = new AtomicBoolean(); DataOutputStream out = new DataOutputStream(new ByteArrayOutputStream()); BinaryOutputArchive oa = new BinaryOutputArchive(out) { @Override public void writeInt(int size, String tag) throws IOException { final Semaphore semaphore = new Semaphore(0); new Thread(new Runnable() { @Override public void run() { synchronized (tree.getReferenceCountedAclCache()) { //When we lock ACLCache, allow writeRecord to continue semaphore.release(); } } }).start(); try { boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS); //This is the real assertion - could another thread lock //the ACLCache assertTrue(acquired, "Couldn't acquire a lock on the ACLCache while we were calling tree.serialize"); } catch (InterruptedException e1) { throw new RuntimeException(e1); } ranTestCase.set(true); super.writeInt(size, tag); } }; tree.serialize(oa, "test"); //Let's make sure that we hit the code that ran the real assertion above assertTrue(ranTestCase.get(), "Didn't find the expected node"); } /* ZOOKEEPER-3531 - similarly for aclCache.deserialize, we should not hold lock either */ @Test @Timeout(value = 60) public void testDeserializeDoesntLockACLCacheWhileReading() throws Exception { DataTree tree = new DataTree(); tree.createNode("/marker", new byte[] { 42 }, null, -1, 1, 1, 1); final AtomicBoolean ranTestCase = new AtomicBoolean(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); BinaryOutputArchive oa = new BinaryOutputArchive(out); tree.serialize(oa, "test"); DataTree tree2 = new DataTree(); DataInputStream in = new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); BinaryInputArchive ia = new BinaryInputArchive(in) { @Override public long readLong(String tag) throws IOException { final Semaphore semaphore = new Semaphore(0); new Thread(new Runnable() { @Override public void run() { synchronized (tree2.getReferenceCountedAclCache()) { //When we lock ACLCache, allow readLong to continue semaphore.release(); } } }).start(); try { boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS); //This is the real assertion - could another thread lock //the ACLCache assertTrue(acquired, "Couldn't acquire a lock on the ACLCache while we were calling tree.deserialize"); } catch (InterruptedException e1) { throw new RuntimeException(e1); } ranTestCase.set(true); return super.readLong(tag); } }; tree2.deserialize(ia, "test"); //Let's make sure that we hit the code that ran the real assertion above assertTrue(ranTestCase.get(), "Didn't find the expected node"); } /* * ZOOKEEPER-2201 - OutputArchive.writeRecord can block for long periods of * time, we must call it outside of the node lock. * We call tree.serialize, which calls our modified writeRecord method that * blocks until it can verify that a separate thread can lock the DataNode * currently being written, i.e. that DataTree.serializeNode does not hold * the DataNode lock while calling OutputArchive.writeRecord. */ @Test @Timeout(value = 60) public void testSerializeDoesntLockDataNodeWhileWriting() throws Exception { DataTree tree = new DataTree(); tree.createNode("/marker", new byte[] { 42 }, null, -1, 1, 1, 1); final DataNode markerNode = tree.getNode("/marker"); final AtomicBoolean ranTestCase = new AtomicBoolean(); DataOutputStream out = new DataOutputStream(new ByteArrayOutputStream()); BinaryOutputArchive oa = new BinaryOutputArchive(out) { @Override public void writeRecord(Record r, String tag) throws IOException { // Need check if the record is a DataNode instance because of changes in ZOOKEEPER-2014 // which adds default ACL to config node. if (r instanceof DataNode) { DataNode node = (DataNode) r; if (node.data.length == 1 && node.data[0] == 42) { final Semaphore semaphore = new Semaphore(0); new Thread(new Runnable() { @Override public void run() { synchronized (markerNode) { //When we lock markerNode, allow writeRecord to continue semaphore.release(); } } }).start(); try { boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS); //This is the real assertion - could another thread lock //the DataNode we're currently writing assertTrue(acquired, "Couldn't acquire a lock on the DataNode while we were calling tree.serialize"); } catch (InterruptedException e1) { throw new RuntimeException(e1); } ranTestCase.set(true); } } super.writeRecord(r, tag); } }; tree.serialize(oa, "test"); //Let's make sure that we hit the code that ran the real assertion above assertTrue(ranTestCase.get(), "Didn't find the expected node"); } @Test @Timeout(value = 60) public void testReconfigACLClearOnDeserialize() throws Exception { DataTree tree = new DataTree(); // simulate the upgrading scenario, where the reconfig znode // doesn't exist and the acl cache is empty tree.deleteNode(ZooDefs.CONFIG_NODE, 1); tree.getReferenceCountedAclCache().aclIndex = 0; assertEquals(0, tree.aclCacheSize(), "expected to have 1 acl in acl cache map"); // serialize the data with one znode with acl tree.createNode("/bug", new byte[20], ZooDefs.Ids.OPEN_ACL_UNSAFE, -1, 1, 1, 1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); tree.serialize(oa, "test"); baos.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); BinaryInputArchive ia = BinaryInputArchive.getArchive(bais); tree.deserialize(ia, "test"); assertEquals(1, tree.aclCacheSize(), "expected to have 1 acl in acl cache map"); assertEquals(ZooDefs.Ids.OPEN_ACL_UNSAFE, tree.getACL("/bug", new Stat()), "expected to have the same acl"); // simulate the upgrading case where the config node will be created // again after leader election tree.addConfigNode(); assertEquals(2, tree.aclCacheSize(), "expected to have 2 acl in acl cache map"); assertEquals(ZooDefs.Ids.OPEN_ACL_UNSAFE, tree.getACL("/bug", new Stat()), "expected to have the same acl"); } @Test public void testCachedApproximateDataSize() throws Exception { DataTree dt = new DataTree(); long initialSize = dt.approximateDataSize(); assertEquals(dt.cachedApproximateDataSize(), dt.approximateDataSize()); // create a node dt.createNode("/testApproximateDataSize", new byte[20], null, -1, 1, 1, 1); dt.createNode("/testApproximateDataSize1", new byte[20], null, -1, 1, 1, 1); assertEquals(dt.cachedApproximateDataSize(), dt.approximateDataSize()); // update data dt.setData("/testApproximateDataSize1", new byte[32], -1, 1, 1); assertEquals(dt.cachedApproximateDataSize(), dt.approximateDataSize()); // delete a node dt.deleteNode("/testApproximateDataSize", -1); assertEquals(dt.cachedApproximateDataSize(), dt.approximateDataSize()); } @Test public void testGetAllChildrenNumber() throws Exception { DataTree dt = new DataTree(); // create a node dt.createNode("/all_children_test", new byte[20], null, -1, 1, 1, 1); dt.createNode("/all_children_test/nodes", new byte[20], null, -1, 1, 1, 1); dt.createNode("/all_children_test/nodes/node1", new byte[20], null, -1, 1, 1, 1); dt.createNode("/all_children_test/nodes/node2", new byte[20], null, -1, 1, 1, 1); dt.createNode("/all_children_test/nodes/node3", new byte[20], null, -1, 1, 1, 1); assertEquals(4, dt.getAllChildrenNumber("/all_children_test")); assertEquals(3, dt.getAllChildrenNumber("/all_children_test/nodes")); assertEquals(0, dt.getAllChildrenNumber("/all_children_test/nodes/node1")); //add these three init nodes:/zookeeper,/zookeeper/quota,/zookeeper/config,so the number is 8. assertEquals(8, dt.getAllChildrenNumber("/")); } @Test public void testDeserializeZxidDigest() throws Exception { try { ZooKeeperServer.setDigestEnabled(true); DataTree dt = new DataTree(); dt.processTxn(new TxnHeader(13, 1000, 1, 30, ZooDefs.OpCode.create), new CreateTxn("/foo", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); dt.serializeZxidDigest(oa); baos.flush(); DataTree.ZxidDigest zd = dt.getLastProcessedZxidDigest(); assertNotNull(zd); // deserialize data tree InputArchive ia = BinaryInputArchive.getArchive( new ByteArrayInputStream(baos.toByteArray())); dt.deserializeZxidDigest(ia, zd.getZxid()); assertNotNull(dt.getDigestFromLoadedSnapshot()); ia = BinaryInputArchive.getArchive(new ByteArrayInputStream(baos.toByteArray())); dt.deserializeZxidDigest(ia, zd.getZxid() + 1); assertNull(dt.getDigestFromLoadedSnapshot()); } finally { ZooKeeperServer.setDigestEnabled(false); } } @Test public void testSerializeLastProcessedZxid_Enabled() throws Exception { testSerializeLastProcessedZxid(true, true); } @Test public void testSerializeLastProcessedZxid_Disabled() throws Exception { testSerializeLastProcessedZxid(false, false); } @Test public void testSerializeLastProcessedZxid_BackwardCompatibility() throws Exception { testSerializeLastProcessedZxid(true, false); } @Test public void testDataTreeMetrics() throws Exception { ServerMetrics.getMetrics().resetAll(); long readBytes1 = 0; long readBytes2 = 0; long writeBytes1 = 0; long writeBytes2 = 0; final String TOP1 = "top1"; final String TOP2 = "ttop2"; final String TOP1PATH = "/" + TOP1; final String TOP2PATH = "/" + TOP2; final String CHILD1 = "child1"; final String CHILD2 = "springishere"; final String CHILD1PATH = TOP1PATH + "/" + CHILD1; final String CHILD2PATH = TOP1PATH + "/" + CHILD2; final int TOP2_LEN = 50; final int CHILD1_LEN = 100; final int CHILD2_LEN = 250; DataTree dt = new DataTree(); dt.createNode(TOP1PATH, null, null, -1, 1, 1, 1); writeBytes1 += TOP1PATH.length(); dt.createNode(TOP2PATH, new byte[TOP2_LEN], null, -1, 1, 1, 1); writeBytes2 += TOP2PATH.length() + TOP2_LEN; dt.createNode(CHILD1PATH, null, null, -1, 1, 1, 1); writeBytes1 += CHILD1PATH.length(); dt.setData(CHILD1PATH, new byte[CHILD1_LEN], 1, -1, 1); writeBytes1 += CHILD1PATH.length() + CHILD1_LEN; dt.createNode(CHILD2PATH, new byte[CHILD2_LEN], null, -1, 1, 1, 1); writeBytes1 += CHILD2PATH.length() + CHILD2_LEN; dt.getData(TOP1PATH, new Stat(), null); readBytes1 += TOP1PATH.length() + DataTree.STAT_OVERHEAD_BYTES; dt.getData(TOP2PATH, new Stat(), null); readBytes2 += TOP2PATH.length() + TOP2_LEN + DataTree.STAT_OVERHEAD_BYTES; dt.statNode(CHILD2PATH, null); readBytes1 += CHILD2PATH.length() + DataTree.STAT_OVERHEAD_BYTES; dt.getChildren(TOP1PATH, new Stat(), null); readBytes1 += TOP1PATH.length() + CHILD1.length() + CHILD2.length() + DataTree.STAT_OVERHEAD_BYTES; dt.deleteNode(TOP1PATH, 1); writeBytes1 += TOP1PATH.length(); Map values = MetricsUtils.currentServerMetrics(); System.out.println("values:" + values); assertEquals(writeBytes1, values.get("sum_" + TOP1 + "_write_per_namespace")); assertEquals(5L, values.get("cnt_" + TOP1 + "_write_per_namespace")); assertEquals(writeBytes2, values.get("sum_" + TOP2 + "_write_per_namespace")); assertEquals(1L, values.get("cnt_" + TOP2 + "_write_per_namespace")); assertEquals(readBytes1, values.get("sum_" + TOP1 + "_read_per_namespace")); assertEquals(3L, values.get("cnt_" + TOP1 + "_read_per_namespace")); assertEquals(readBytes2, values.get("sum_" + TOP2 + "_read_per_namespace")); assertEquals(1L, values.get("cnt_" + TOP2 + "_read_per_namespace")); } /** * Test digest with general ops in DataTree, check that digest are * updated when call different ops. */ @Test public void testDigest() throws Exception { try { // enable diegst check ZooKeeperServer.setDigestEnabled(true); DataTree dt = new DataTree(); // create a node and check the digest is updated long previousDigest = dt.getTreeDigest(); dt.createNode("/digesttest", new byte[0], null, -1, 1, 1, 1); assertNotEquals(dt.getTreeDigest(), previousDigest); // create a child and check the digest is updated previousDigest = dt.getTreeDigest(); dt.createNode("/digesttest/1", "1".getBytes(), null, -1, 2, 2, 2); assertNotEquals(dt.getTreeDigest(), previousDigest); // check the digest is not chhanged when creating the same node previousDigest = dt.getTreeDigest(); try { dt.createNode("/digesttest/1", "1".getBytes(), null, -1, 2, 2, 2); } catch (NodeExistsException e) { /* ignore */ } assertEquals(dt.getTreeDigest(), previousDigest); // check digest with updated data previousDigest = dt.getTreeDigest(); dt.setData("/digesttest/1", "2".getBytes(), 3, 3, 3); assertNotEquals(dt.getTreeDigest(), previousDigest); // check digest with deleted node previousDigest = dt.getTreeDigest(); dt.deleteNode("/digesttest/1", 5); assertNotEquals(dt.getTreeDigest(), previousDigest); } finally { ZooKeeperServer.setDigestEnabled(false); } } @Test public void testCreateNodeFixMissingACL() throws Exception { DataTree dt = new DataTree(); ReferenceCountedACLCache aclCache = dt.getReferenceCountedAclCache(); dt.createNode("/the_parent", new byte[0], ZooDefs.Ids.CREATOR_ALL_ACL, -1, 1, 1, 0); Long aclId = dt.getNode("/the_parent").acl; aclCache.removeUsage(aclId); aclCache.purgeUnused(); // try to re-create the parent -> throws NodeExistsException, but fixes the deleted ACL assertThrows(NodeExistsException.class, () -> dt.createNode("/the_parent", new byte[0], ZooDefs.Ids.CREATOR_ALL_ACL, -1, 1, 1, 0)); dt.createNode("/the_parent/the_child", new byte[0], ZooDefs.Ids.CREATOR_ALL_ACL, -1, 2, 2, 2); } private DataTree buildDataTreeForTest() { final DataTree dt = new DataTree(); assertEquals(dt.lastProcessedZxid, 0); dt.processTxn( new TxnHeader(100, 1000, 1, 30, ZooDefs.OpCode.create), new CreateTxn("/foo", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); assertEquals(dt.lastProcessedZxid, 1); return dt; } private void testSerializeLastProcessedZxid(boolean enableForSerialize, boolean enableForDeserialize) throws Exception{ final DataTree dt = buildDataTreeForTest(); try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(enableForSerialize); final BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); if (enableForSerialize) { assertTrue(dt.serializeLastProcessedZxid(oa)); } else { assertFalse(dt.serializeLastProcessedZxid(oa)); } baos.flush(); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(enableForDeserialize); try (final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())) { final InputArchive ia = BinaryInputArchive.getArchive(bais); if (enableForDeserialize) { assertTrue(dt.deserializeLastProcessedZxid(ia)); } else { assertFalse(dt.deserializeLastProcessedZxid(ia)); } assertEquals(dt.lastProcessedZxid, 1); } } finally { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(true); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Da0100644 0000000 0000000 00000000172 15051152474 032651 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/DatadirCleanupManagerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/DatadirCleanupMana0100644 0000000 0000000 00000006645 15051152474 034246 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.server.DatadirCleanupManager.PurgeTaskStatus.COMPLETED; import static org.apache.zookeeper.server.DatadirCleanupManager.PurgeTaskStatus.NOT_STARTED; import static org.apache.zookeeper.server.DatadirCleanupManager.PurgeTaskStatus.STARTED; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class DatadirCleanupManagerTest extends ZKTestCase { private DatadirCleanupManager purgeMgr; private File snapDir; private File dataLogDir; @BeforeEach public void setUp() throws Exception { File dataDir = ClientBase.createTmpDir(); snapDir = dataDir; dataLogDir = dataDir; } @Test public void testPurgeTask() throws Exception { purgeMgr = new DatadirCleanupManager(snapDir, dataLogDir, 3, 1); purgeMgr.start(); assertEquals(dataLogDir, purgeMgr.getDataLogDir(), "Data log directory is not set as configured"); assertEquals(snapDir, purgeMgr.getSnapDir(), "Snapshot directory is not set as configured"); assertEquals(3, purgeMgr.getSnapRetainCount(), "Snapshot retain count is not set as configured"); assertEquals(STARTED, purgeMgr.getPurgeTaskStatus(), "Purge task is not started"); purgeMgr.shutdown(); assertEquals(COMPLETED, purgeMgr.getPurgeTaskStatus(), "Purge task is still running after shutdown"); } @Test public void testWithZeroPurgeInterval() throws Exception { purgeMgr = new DatadirCleanupManager(snapDir, dataLogDir, 3, 0); purgeMgr.start(); assertEquals(NOT_STARTED, purgeMgr.getPurgeTaskStatus(), "Purge task is scheduled with zero purge interval"); purgeMgr.shutdown(); assertEquals(NOT_STARTED, purgeMgr.getPurgeTaskStatus(), "Purge task is scheduled with zero purge interval"); } @Test public void testWithNegativePurgeInterval() throws Exception { purgeMgr = new DatadirCleanupManager(snapDir, dataLogDir, 3, -1); purgeMgr.start(); assertEquals(NOT_STARTED, purgeMgr.getPurgeTaskStatus(), "Purge task is scheduled with negative purge interval"); purgeMgr.shutdown(); assertEquals(NOT_STARTED, purgeMgr.getPurgeTaskStatus(), "Purge task is scheduled with negative purge interval"); } @AfterEach public void tearDown() throws Exception { if (purgeMgr != null) { purgeMgr.shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_De0100644 0000000 0000000 00000000170 15051152474 032653 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/DeserializationPerfTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/DeserializationPer0100644 0000000 0000000 00000010637 15051152474 034362 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DeserializationPerfTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(DeserializationPerfTest.class); private static void deserializeTree(int depth, int width, int len) throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { BinaryInputArchive ia; int count; { DataTree tree = new DataTree(); SerializationPerfTest.createNodes(tree, "/", depth, width, tree.getNode("/").stat.getCversion(), new byte[len]); count = tree.getNodeCount(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); tree.serialize(oa, "test"); baos.flush(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ia = BinaryInputArchive.getArchive(bais); } DataTree dserTree = new DataTree(); System.gc(); long start = System.nanoTime(); dserTree.deserialize(ia, "test"); long end = System.nanoTime(); long durationms = (end - start) / 1000000L; long pernodeus = ((end - start) / 1000L) / count; assertEquals(count, dserTree.getNodeCount()); LOG.info( "Deserialized {} nodes in {} ms ({}us/node), depth={} width={} datalen={}", count, durationms, pernodeus, depth, width, len); } @Test public void testSingleDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(1, 0, 20); } @Test public void testWideDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(2, 10000, 20); } @Test public void testDeepDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(400, 1, 20); } @Test public void test10Wide5DeepDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(5, 10, 20); } @Test public void test15Wide5DeepDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(5, 15, 20); } @Test public void test25Wide4DeepDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(4, 25, 20); } @Test public void test40Wide4DeepDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(4, 40, 20); } @Test public void test300Wide3DeepDeserialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { deserializeTree(3, 300, 20); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Em0100644 0000000 0000000 00000000162 15051152474 032665 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/Emulate353TTLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/Emulate353TTLTest.0100644 0000000 0000000 00000010737 15051152474 033717 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.concurrent.atomic.AtomicLong; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class Emulate353TTLTest extends ClientBase { private TestableZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { System.setProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY, "true"); System.setProperty(EphemeralType.TTL_3_5_3_EMULATION_PROPERTY, "true"); super.setUp(); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { System.clearProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY); System.clearProperty(EphemeralType.TTL_3_5_3_EMULATION_PROPERTY); super.tearDown(); zk.close(); } @Test public void testCreate() throws KeeperException, InterruptedException { Stat stat = new Stat(); zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100); assertEquals(0, stat.getEphemeralOwner()); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists("/foo", false), "Ttl node should not have been deleted yet"); fakeElapsed.set(1000); containerManager.checkContainers(); assertNull(zk.exists("/foo", false), "Ttl node should have been deleted"); } @Test public void test353TTL() throws KeeperException, InterruptedException { DataTree dataTree = serverFactory.zkServer.getZKDatabase().dataTree; long ephemeralOwner = EphemeralTypeEmulate353.ttlToEphemeralOwner(100); dataTree.createNode("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, ephemeralOwner, dataTree.getNode("/").stat.getCversion() + 1, 1, 1); final AtomicLong fakeElapsed = new AtomicLong(0); ContainerManager containerManager = newContainerManager(fakeElapsed); containerManager.checkContainers(); assertNotNull(zk.exists("/foo", false), "Ttl node should not have been deleted yet"); fakeElapsed.set(1000); containerManager.checkContainers(); assertNull(zk.exists("/foo", false), "Ttl node should have been deleted"); } @Test public void testEphemeralOwner_emulationTTL() { assertThat(EphemeralType.get(-1), equalTo(EphemeralType.TTL)); } @Test public void testEphemeralOwner_emulationContainer() { assertThat(EphemeralType.get(EphemeralType.CONTAINER_EPHEMERAL_OWNER), equalTo(EphemeralType.CONTAINER)); } private ContainerManager newContainerManager(final AtomicLong fakeElapsed) { return new ContainerManager(serverFactory.getZooKeeperServer().getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100) { @Override protected long getElapsed(DataNode node) { return fakeElapsed.get(); } }; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Ep0100644 0000000 0000000 00000000162 15051152474 032670 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/EphemeralTypeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/EphemeralTypeTest.0100644 0000000 0000000 00000007362 15051152474 034250 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class EphemeralTypeTest { @BeforeEach public void setUp() { System.setProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY, "true"); } @AfterEach public void tearDown() { System.clearProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY); } @Test public void testTtls() { long[] ttls = {100, 1, EphemeralType.TTL.maxValue()}; for (long ttl : ttls) { long ephemeralOwner = EphemeralType.TTL.toEphemeralOwner(ttl); assertEquals(EphemeralType.TTL, EphemeralType.get(ephemeralOwner)); assertEquals(ttl, EphemeralType.TTL.getValue(ephemeralOwner)); } EphemeralType.validateTTL(CreateMode.PERSISTENT_WITH_TTL, 100); EphemeralType.validateTTL(CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, 100); try { EphemeralType.validateTTL(CreateMode.EPHEMERAL, 100); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException dummy) { // expected } } @Test public void testContainerValue() { assertEquals(Long.MIN_VALUE, EphemeralType.CONTAINER_EPHEMERAL_OWNER); assertEquals(EphemeralType.CONTAINER, EphemeralType.get(EphemeralType.CONTAINER_EPHEMERAL_OWNER)); } @Test public void testNonSpecial() { assertEquals(EphemeralType.VOID, EphemeralType.get(0)); assertEquals(EphemeralType.NORMAL, EphemeralType.get(1)); assertEquals(EphemeralType.NORMAL, EphemeralType.get(Long.MAX_VALUE)); } @Test public void testServerIds() { for (int i = 0; i <= EphemeralType.MAX_EXTENDED_SERVER_ID; ++i) { EphemeralType.validateServerId(i); } try { EphemeralType.validateServerId(EphemeralType.MAX_EXTENDED_SERVER_ID + 1); fail("Should have thrown RuntimeException"); } catch (RuntimeException e) { // expected } } @Test public void testEphemeralOwner_extendedFeature_TTL() { // 0xff = Extended feature is ON // 0x0000 = Extended type id TTL (0) assertThat(EphemeralType.get(0xff00000000000000L), equalTo(EphemeralType.TTL)); } @Test public void testEphemeralOwner_extendedFeature_extendedTypeUnsupported() { assertThrows(IllegalArgumentException.class, () -> { // 0xff = Extended feature is ON // 0x0001 = Unsupported extended type id (1) EphemeralType.get(0xff00010000000000L); }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Fi0100644 0000000 0000000 00000000172 15051152474 032663 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/FinalRequestProcessorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/FinalRequestProces0100644 0000000 0000000 00000023373 15051152474 034344 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.GetACLRequest; import org.apache.zookeeper.proto.GetACLResponse; import org.apache.zookeeper.proto.ReplyHeader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class FinalRequestProcessorTest { private List testACLs = new ArrayList<>(); private final Record[] responseRecord = new Record[1]; private final ReplyHeader[] replyHeaders = new ReplyHeader[1]; private ServerCnxn cnxn; private ByteBuffer bb; private FinalRequestProcessor processor; @BeforeEach public void setUp() throws KeeperException.NoNodeException, IOException { testACLs.clear(); testACLs.addAll(Arrays.asList(new ACL(ZooDefs.Perms.ALL, new Id("digest", "user:secrethash")), new ACL(ZooDefs.Perms.ADMIN, new Id("digest", "adminuser:adminsecret")), new ACL(ZooDefs.Perms.READ, new Id("world", "anyone")))); ZooKeeperServer zks = new ZooKeeperServer(); ZKDatabase db = mock(ZKDatabase.class); String testPath = "/testPath"; when(db.getNode(eq(testPath))).thenReturn(new DataNode()); when(db.getACL(eq(testPath), any(Stat.class))).thenReturn(testACLs); when(db.aclForNode(any(DataNode.class))).thenReturn(testACLs); zks.setZKDatabase(db); processor = new FinalRequestProcessor(zks); cnxn = mock(ServerCnxn.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) { replyHeaders[0] = invocationOnMock.getArgument(0); responseRecord[0] = invocationOnMock.getArgument(1); return null; } }).when(cnxn).sendResponse(any(), any(), anyString()); GetACLRequest getACLRequest = new GetACLRequest(); getACLRequest.setPath(testPath); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); getACLRequest.serialize(boa, "request"); baos.close(); bb = ByteBuffer.wrap(baos.toByteArray()); } @Test public void testACLDigestHashHiding_NoAuth_WorldCanRead() { // Arrange // Act Request r = new Request(cnxn, 0, 0, ZooDefs.OpCode.getACL, RequestRecord.fromBytes(bb), new ArrayList()); processor.processRequest(r); // Assert assertMasked(true); } @Test public void testACLDigestHashHiding_NoAuth_NoWorld() { // Arrange testACLs.remove(2); // Act Request r = new Request(cnxn, 0, 0, ZooDefs.OpCode.getACL, RequestRecord.fromBytes(bb), new ArrayList()); processor.processRequest(r); // Assert assertThat(KeeperException.Code.get(replyHeaders[0].getErr()), equalTo(KeeperException.Code.NOAUTH)); } @Test public void testACLDigestHashHiding_UserCanRead() { // Arrange List authInfo = new ArrayList<>(); authInfo.add(new Id("digest", "otheruser:somesecrethash")); // Act Request r = new Request(cnxn, 0, 0, ZooDefs.OpCode.getACL, RequestRecord.fromBytes(bb), authInfo); processor.processRequest(r); // Assert assertMasked(true); } @Test public void testACLDigestHashHiding_UserCanAll() { // Arrange List authInfo = new ArrayList<>(); authInfo.add(new Id("digest", "user:secrethash")); // Act Request r = new Request(cnxn, 0, 0, ZooDefs.OpCode.getACL, RequestRecord.fromBytes(bb), authInfo); processor.processRequest(r); // Assert assertMasked(false); } @Test public void testACLDigestHashHiding_AdminUser() { // Arrange List authInfo = new ArrayList<>(); authInfo.add(new Id("digest", "adminuser:adminsecret")); // Act Request r = new Request(cnxn, 0, 0, ZooDefs.OpCode.getACL, RequestRecord.fromBytes(bb), authInfo); processor.processRequest(r); // Assert assertMasked(false); } @Test public void testACLDigestHashHiding_OnlyAdmin() { // Arrange testACLs.clear(); testACLs.addAll(Arrays.asList(new ACL(ZooDefs.Perms.READ, new Id("digest", "user:secrethash")), new ACL(ZooDefs.Perms.ADMIN, new Id("digest", "adminuser:adminsecret")))); List authInfo = new ArrayList<>(); authInfo.add(new Id("digest", "adminuser:adminsecret")); // Act Request r = new Request(cnxn, 0, 0, ZooDefs.OpCode.getACL, RequestRecord.fromBytes(bb), authInfo); processor.processRequest(r); // Assert assertTrue(responseRecord[0] instanceof GetACLResponse, "Not a GetACL response. Auth failed?"); GetACLResponse rsp = (GetACLResponse) responseRecord[0]; assertThat("Number of ACLs in the response are different", rsp.getAcl().size(), equalTo(2)); // Verify ACLs in the response assertThat("Password hash mismatch in the response", rsp.getAcl().get(0).getId().getId(), equalTo("user:secrethash")); assertThat("Password hash mismatch in the response", rsp.getAcl().get(1).getId().getId(), equalTo("adminuser:adminsecret")); } private void assertMasked(boolean masked) { assertTrue(responseRecord[0] instanceof GetACLResponse, "Not a GetACL response. Auth failed?"); GetACLResponse rsp = (GetACLResponse) responseRecord[0]; assertThat("Number of ACLs in the response are different", rsp.getAcl().size(), equalTo(3)); // Verify ACLs in the response assertThat("Invalid ACL list in the response", rsp.getAcl().get(0).getPerms(), equalTo(ZooDefs.Perms.ALL)); assertThat("Invalid ACL list in the response", rsp.getAcl().get(0).getId().getScheme(), equalTo("digest")); if (masked) { assertThat("Password hash is not masked in the response", rsp.getAcl().get(0).getId().getId(), equalTo("user:x")); } else { assertThat("Password hash mismatch in the response", rsp.getAcl().get(0).getId().getId(), equalTo("user:secrethash")); } assertThat("Invalid ACL list in the response", rsp.getAcl().get(1).getPerms(), equalTo(ZooDefs.Perms.ADMIN)); assertThat("Invalid ACL list in the response", rsp.getAcl().get(1).getId().getScheme(), equalTo("digest")); if (masked) { assertThat("Password hash is not masked in the response", rsp.getAcl().get(1).getId().getId(), equalTo("adminuser:x")); } else { assertThat("Password hash mismatch in the response", rsp.getAcl().get(1).getId().getId(), equalTo("adminuser:adminsecret")); } assertThat("Invalid ACL list in the response", rsp.getAcl().get(2).getPerms(), equalTo(ZooDefs.Perms.READ)); assertThat("Invalid ACL list in the response", rsp.getAcl().get(2).getId().getScheme(), equalTo("world")); assertThat("Invalid ACL list in the response", rsp.getAcl().get(2).getId().getId(), equalTo("anyone")); // Verify that FinalRequestProcessor hasn't changed the original ACL objects assertThat("Original ACL list has been modified", testACLs.get(0).getPerms(), equalTo(ZooDefs.Perms.ALL)); assertThat("Original ACL list has been modified", testACLs.get(0).getId().getScheme(), equalTo("digest")); assertThat("Original ACL list has been modified", testACLs.get(0).getId().getId(), equalTo("user:secrethash")); assertThat("Original ACL list has been modified", testACLs.get(1).getPerms(), equalTo(ZooDefs.Perms.ADMIN)); assertThat("Original ACL list has been modified", testACLs.get(1).getId().getScheme(), equalTo("digest")); assertThat("Original ACL list has been modified", testACLs.get(1).getId().getId(), equalTo("adminuser:adminsecret")); assertThat("Original ACL list has been modified", testACLs.get(2).getPerms(), equalTo(ZooDefs.Perms.READ)); assertThat("Original ACL list has been modified", testACLs.get(2).getId().getScheme(), equalTo("world")); assertThat("Original ACL list has been modified", testACLs.get(2).getId().getId(), equalTo("anyone")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_In0100644 0000000 0000000 00000000165 15051152474 032675 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/InvalidSnapCountTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/InvalidSnapCountTe0100644 0000000 0000000 00000007552 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test stand-alone server. * */ public class InvalidSnapCountTest extends ZKTestCase implements Watcher { protected static final Logger LOG = LoggerFactory.getLogger(InvalidSnapCountTest.class); public static class MainThread extends Thread { final File confFile; final TestMain main; public MainThread(int clientPort) throws IOException { super("Standalone server with clientPort:" + clientPort); File tmpDir = ClientBase.createTmpDir(); confFile = new File(tmpDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=2000\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); fwriter.write("snapCount=1\n"); File dataDir = new File(tmpDir, "data"); if (!dataDir.mkdir()) { throw new IOException("unable to mkdir " + dataDir); } // Convert windows path to UNIX to avoid problems with "\" String dir = PathUtils.normalizeFileSystemPath(dataDir.toString()); fwriter.write("dataDir=" + dir + "\n"); fwriter.write("clientPort=" + clientPort + "\n"); fwriter.flush(); fwriter.close(); main = new TestMain(); } public void run() { String[] args = new String[1]; args[0] = confFile.toString(); try { main.initializeAndRun(args); } catch (Exception e) { // test will still fail even though we just log/ignore LOG.error("unexpected exception in run", e); } } public void shutdown() { main.shutdown(); } } public static class TestMain extends ZooKeeperServerMain { public void shutdown() { super.shutdown(); } } /** * Verify the ability to start a standalone server instance. */ @Test public void testInvalidSnapCount() throws Exception { final int CLIENT_PORT = 3181; MainThread main = new MainThread(CLIENT_PORT); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); assertEquals(SyncRequestProcessor.getSnapCount(), 2); main.shutdown(); } public void process(WatchedEvent event) { // ignore for this test } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_In0100644 0000000 0000000 00000000164 15051152474 032674 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/InvalidSnapshotTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/InvalidSnapshotTes0100644 0000000 0000000 00000005374 15051152474 034351 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.RandomAccessFile; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This test checks that the server works even if the last snapshot is * invalidated by corruption or if the server crashes while generating the * snapshot. */ public class InvalidSnapshotTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(InvalidSnapshotTest.class); public InvalidSnapshotTest() { SyncRequestProcessor.setSnapCount(100); } /** * Validate that the server can come up on an invalid snapshot - by * reverting to a prior snapshot + associated logs. */ @Test public void testInvalidSnapshot() throws Exception { ZooKeeper zk = createClient(); try { for (int i = 0; i < 2000; i++) { zk.create("/invalidsnap-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } NIOServerCnxnFactory factory = (NIOServerCnxnFactory) serverFactory; stopServer(); // now corrupt the snapshot File snapFile = factory.zkServer.getTxnLogFactory().findMostRecentSnapshot(); LOG.info("Corrupting {}", snapFile); RandomAccessFile raf = new RandomAccessFile(snapFile, "rws"); raf.setLength(3); raf.close(); // now restart the server startServer(); // verify that the expected data exists and wasn't lost zk = createClient(); try { assertTrue((zk.exists("/invalidsnap-1999", false) != null), "the node should exist"); } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Mo0100644 0000000 0000000 00000000162 15051152474 032677 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MockNIOServerCnxn.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MockNIOServerCnxn.0100644 0000000 0000000 00000003033 15051152474 034110 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import org.apache.zookeeper.server.NIOServerCnxnFactory.SelectorThread; public class MockNIOServerCnxn extends NIOServerCnxn { public MockNIOServerCnxn( ZooKeeperServer zk, SocketChannel sock, SelectionKey sk, NIOServerCnxnFactory factory, SelectorThread selectorThread) throws IOException { super(zk, sock, sk, factory, selectorThread); } /** * Handles read/write IO on connection. */ public void doIO(SelectionKey k) throws InterruptedException { super.doIO(k); } @Override protected boolean isSocketOpen() { return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Mo0100644 0000000 0000000 00000000163 15051152474 032700 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MockSelectorThread.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MockSelectorThread0100644 0000000 0000000 00000002315 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.nio.channels.SelectionKey; public class MockSelectorThread extends NIOServerCnxnFactory.SelectorThread { public MockSelectorThread(NIOServerCnxnFactory fact) throws IOException { fact.super(0); } public boolean addInterestOpsUpdateRequest(SelectionKey sk) { return super.addInterestOpsUpdateRequest(sk); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Mo0100644 0000000 0000000 00000000157 15051152474 032703 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MockServerCnxn.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MockServerCnxn.jav0100644 0000000 0000000 00000005452 15051152474 034252 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.util.List; import org.apache.jute.Record; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.ReplyHeader; public class MockServerCnxn extends ServerCnxn { public Certificate[] clientChain; public boolean secure; public MockServerCnxn() { super(null); } @Override int getSessionTimeout() { return 0; } @Override public void close(DisconnectReason reason) { } @Override public int sendResponse(ReplyHeader h, Record r, String tag, String cacheKey, Stat stat, int opCode) throws IOException { return 0; } @Override public void sendCloseSession() { } @Override public void process(WatchedEvent event, List acl) { } @Override public long getSessionId() { return 0; } @Override void setSessionId(long sessionId) { } @Override public boolean isSecure() { return secure; } @Override public Certificate[] getClientCertificateChain() { return clientChain; } @Override public void setClientCertificateChain(Certificate[] chain) { clientChain = chain; } @Override void sendBuffer(ByteBuffer... closeConn) { } @Override void enableRecv() { } @Override void disableRecv(boolean waitDisableRecv) { } @Override void setSessionTimeout(int sessionTimeout) { } @Override protected ServerStats serverStats() { return null; } @Override public long getOutstandingRequests() { return 0; } @Override public InetSocketAddress getRemoteSocketAddress() { return null; } @Override public int getInterestOps() { return 0; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Mu0100644 0000000 0000000 00000000172 15051152474 032706 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MultiOpSessionUpgradeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/MultiOpSessionUpgr0100644 0000000 0000000 00000015042 15051152474 034353 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumZooKeeperServer; import org.apache.zookeeper.server.quorum.UpgradeableSessionTracker; import org.apache.zookeeper.test.QuorumBase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MultiOpSessionUpgradeTest extends QuorumBase { protected static final Logger LOG = LoggerFactory.getLogger(MultiOpSessionUpgradeTest.class); @BeforeEach @Override public void setUp() throws Exception { localSessionsEnabled = true; localSessionsUpgradingEnabled = true; super.setUp(); } @Test public void ephemeralCreateMultiOpTest() throws KeeperException, InterruptedException, IOException { final ZooKeeper zk = createClient(); String data = "test"; String path = "/ephemeralcreatemultiop"; zk.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); QuorumZooKeeperServer server = getConnectedServer(zk.getSessionId()); assertNotNull(server, "unable to find server interlocutor"); UpgradeableSessionTracker sessionTracker = (UpgradeableSessionTracker) server.getSessionTracker(); assertFalse(sessionTracker.isGlobalSession(zk.getSessionId()), "session already global"); List multi = null; try { multi = zk.multi(Arrays.asList( Op.setData(path, data.getBytes(), 0), Op.create(path + "/e", data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL), Op.create(path + "/p", data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create(path + "/q", data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL))); } catch (KeeperException.SessionExpiredException e) { // the scenario that inspired this unit test fail("received session expired for a session promotion in a multi-op"); } assertNotNull(multi); assertEquals(4, multi.size()); assertEquals(data, new String(zk.getData(path + "/e", false, null))); assertEquals(data, new String(zk.getData(path + "/p", false, null))); assertEquals(data, new String(zk.getData(path + "/q", false, null))); assertTrue(sessionTracker.isGlobalSession(zk.getSessionId()), "session not promoted"); } @Test public void directCheckUpgradeSessionTest() throws IOException, InterruptedException, KeeperException { final ZooKeeper zk = createClient(); String path = "/directcheckupgradesession"; zk.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); QuorumZooKeeperServer server = getConnectedServer(zk.getSessionId()); assertNotNull(server, "unable to find server interlocutor"); Request readRequest = makeGetDataRequest(path, zk.getSessionId()); Request createRequest = makeCreateRequest(path + "/e", zk.getSessionId()); assertNull(server.checkUpgradeSession(readRequest), "tried to upgrade on a read"); assertNotNull(server.checkUpgradeSession(createRequest), "failed to upgrade on a create"); assertNull(server.checkUpgradeSession(createRequest), "tried to upgrade after successful promotion"); } private Request makeGetDataRequest(String path, long sessionId) throws IOException { ByteArrayOutputStream boas = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas); GetDataRequest getDataRequest = new GetDataRequest(path, false); getDataRequest.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray()); return new Request(null, sessionId, 1, ZooDefs.OpCode.getData, RequestRecord.fromBytes(bb), new ArrayList()); } private Request makeCreateRequest(String path, long sessionId) throws IOException { ByteArrayOutputStream boas = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas); CreateRequest createRequest = new CreateRequest(path, "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL.toFlag()); createRequest.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray()); return new Request(null, sessionId, 1, ZooDefs.OpCode.create2, RequestRecord.fromBytes(bb), new ArrayList()); } private QuorumZooKeeperServer getConnectedServer(long sessionId) { for (QuorumPeer peer : getPeerList()) { if (peer.getActiveServer().getSessionTracker().isTrackingSession(sessionId)) { return (QuorumZooKeeperServer) peer.getActiveServer(); } } return null; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_NI0100644 0000000 0000000 00000000171 15051152474 032632 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NIOServerCnxnFactoryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NIOServerCnxnFacto0100644 0000000 0000000 00000004561 15051152474 034204 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.SocketException; import org.apache.zookeeper.PortAssignment; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class NIOServerCnxnFactoryTest { private InetSocketAddress listenAddress; private NIOServerCnxnFactory factory; @BeforeEach public void setUp() throws IOException { listenAddress = new InetSocketAddress(PortAssignment.unique()); factory = new NIOServerCnxnFactory(); factory.configure(listenAddress, 100); } @AfterEach public void tearDown() { if (factory != null) { factory.shutdown(); } } @Test public void testStartupWithoutStart_SocketAlreadyBound() throws IOException { assertThrows(SocketException.class, () -> { ServerSocket ss = new ServerSocket(listenAddress.getPort()); }); } @Test public void testStartupWithStart_SocketAlreadyBound() throws IOException { assertThrows(SocketException.class, () -> { factory.start(); ServerSocket ss = new ServerSocket(listenAddress.getPort()); }); } @Test public void testShutdownWithoutStart_SocketReleased() throws IOException { factory.shutdown(); factory = null; ServerSocket ss = new ServerSocket(listenAddress.getPort()); ss.close(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_NI0100644 0000000 0000000 00000000162 15051152474 032632 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NIOServerCnxnTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NIOServerCnxnTest.0100644 0000000 0000000 00000007410 15051152474 034141 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.BufferStats; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NIOServerCnxnTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(NIOServerCnxnTest.class); /** * Test operations on ServerCnxn after socket closure. */ @Test @Timeout(value = 60) public void testOperationsAfterCnxnClose() throws IOException, InterruptedException, KeeperException { final ZooKeeper zk = createClient(); final String path = "/a"; try { // make sure zkclient works zk.create(path, "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(zk.exists(path, false), "Didn't create znode:" + path); // Defaults ServerCnxnFactory would be instantiated with // NIOServerCnxnFactory assertTrue(serverFactory instanceof NIOServerCnxnFactory, "Didn't instantiate ServerCnxnFactory with NIOServerCnxnFactory!"); Iterable connections = serverFactory.getConnections(); for (ServerCnxn serverCnxn : connections) { serverCnxn.close(ServerCnxn.DisconnectReason.CHANNEL_CLOSED_EXCEPTION); try { serverCnxn.toString(); } catch (Exception e) { LOG.error("Exception while getting connection details!", e); fail("Shouldn't throw exception while " + "getting connection details!"); } } } finally { zk.close(); } } @Test public void testClientResponseStatsUpdate() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient()) { BufferStats clientResponseStats = serverFactory.getZooKeeperServer().serverStats().getClientResponseStats(); assertThat("Last client response size should be initialized with INIT_VALUE", clientResponseStats.getLastBufferSize(), equalTo(BufferStats.INIT_VALUE)); zk.create("/a", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertThat("Last client response size should be greater then zero after client request was performed", clientResponseStats.getLastBufferSize(), greaterThan(0)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Ne0100644 0000000 0000000 00000000173 15051152474 032670 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NettyServerCnxnFactoryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NettyServerCnxnFac0100644 0000000 0000000 00000022630 15051152474 034314 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.net.InetSocketAddress; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.server.metric.SimpleCounter; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.SSLAuthTest; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NettyServerCnxnFactoryTest extends ClientBase { private static final Logger LOG = LoggerFactory .getLogger(NettyServerCnxnFactoryTest.class); ClientX509Util x509Util; final LinkedBlockingQueue zooKeeperClients = new LinkedBlockingQueue<>(); @Override public void setUp() throws Exception { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); // by default, we don't start any ZooKeeper server, as not all the tests are needing it. } @Override public void tearDown() throws Exception { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); if (x509Util != null) { SSLAuthTest.clearSecureSetting(x509Util); } for (ZooKeeper zk : zooKeeperClients) { zk.close(); } //stopping the server only if it was started if (serverFactory != null) { super.tearDown(); } } @Test public void testRebind() throws Exception { InetSocketAddress addr = new InetSocketAddress(PortAssignment.unique()); NettyServerCnxnFactory factory = new NettyServerCnxnFactory(); factory.configure(addr, 100, -1, false); factory.start(); assertTrue(factory.getParentChannel().isActive()); factory.reconfigure(addr); // wait the state change Thread.sleep(100); assertTrue(factory.getParentChannel().isActive()); } @Test public void testRebindIPv4IPv6() throws Exception { int randomPort = PortAssignment.unique(); InetSocketAddress addr = new InetSocketAddress("0.0.0.0", randomPort); NettyServerCnxnFactory factory = new NettyServerCnxnFactory(); factory.configure(addr, 100, -1, false); factory.start(); assertTrue(factory.getParentChannel().isActive()); factory.reconfigure(new InetSocketAddress("[0:0:0:0:0:0:0:0]", randomPort)); // wait the state change Thread.sleep(100); assertTrue(factory.getParentChannel().isActive()); } /* * In this test we are flooding the server with SSL connections, and expecting that not * all the connection will succeed at once. Some of the connections should be closed, * as there is a maximum number of parallel SSL handshake the server is willing to do * for security reasons. */ @Test public void testOutstandingHandshakeLimit() throws Exception { // setting up SSL params, but disable some debug logs x509Util = SSLAuthTest.setUpSecure(); System.clearProperty("javax.net.debug"); // starting a single server (it will be closed in the tearDown) setUpWithServerId(1); // initializing the statistics SimpleCounter tlsHandshakeExceeded = (SimpleCounter) ServerMetrics.getMetrics().TLS_HANDSHAKE_EXCEEDED; tlsHandshakeExceeded.reset(); assertEquals(tlsHandshakeExceeded.get(), 0); // setting the HandshakeLimit to 3, so only 3 SSL handshakes can happen in parallel NettyServerCnxnFactory factory = (NettyServerCnxnFactory) serverFactory; factory.setSecure(true); factory.setOutstandingHandshakeLimit(3); // starting the threads that will try to connect to the server // we will have 3 threads, each of them establishing 3 connections int threadNum = 3; int cnxnPerThread = 3; int cnxnLimit = threadNum * cnxnPerThread; AtomicInteger cnxnCreated = new AtomicInteger(0); CountDownLatch latch = new CountDownLatch(1); Thread[] cnxnWorker = new Thread[threadNum]; for (int i = 0; i < cnxnWorker.length; i++) { cnxnWorker[i] = new ClientConnectionGenerator(i, cnxnPerThread, cnxnCreated, cnxnLimit, latch, zooKeeperClients); cnxnWorker[i].start(); } // we might need to wait potentially for a longer time for all the connection to get established, // as the ZooKeeper Server will close some of the connections and the clients will have to re-try boolean allConnectionsCreatedInTime = latch.await(30, TimeUnit.SECONDS); int actualConnections = cnxnCreated.get(); LOG.info("created {} connections", actualConnections); if (!allConnectionsCreatedInTime) { fail(String.format("Only %d out of %d connections created!", actualConnections, cnxnLimit)); } // Assert the server refused some of the connections because the handshake limit was reached // (throttling should be greater than 0) long handshakeThrottledNum = tlsHandshakeExceeded.get(); LOG.info("TLS_HANDSHAKE_EXCEEDED: {}", handshakeThrottledNum); assertThat("The number of handshake throttled should be " + "greater than 0", handshakeThrottledNum, Matchers.greaterThan(0L)); // Assert there is no outstanding handshake anymore, all the clients connected in the end int outstandingHandshakeNum = factory.getOutstandingHandshakeNum(); LOG.info("outstanding handshake is {}", outstandingHandshakeNum); assertThat("The outstanding handshake number should be 0 " + "after all cnxns established", outstandingHandshakeNum, Matchers.is(0)); } private final class ClientConnectionWatcher implements Watcher { private final AtomicInteger cnxnCreated; private final int cnxnLimit; private final int cnxnThreadId; private final int cnxnId; private final CountDownLatch latch; public ClientConnectionWatcher(AtomicInteger cnxnCreated, int cnxnLimit, int cnxnThreadId, int cnxnId, CountDownLatch latch) { this.cnxnCreated = cnxnCreated; this.cnxnLimit = cnxnLimit; this.cnxnThreadId = cnxnThreadId; this.cnxnId = cnxnId; this.latch = latch; } @Override public void process(WatchedEvent event) { LOG.info(String.format("WATCHER [thread: %d, cnx:%d] - new event: %s", cnxnThreadId, cnxnId, event.toString())); if (event.getState() == Event.KeeperState.SyncConnected) { int created = cnxnCreated.addAndGet(1); if (created == cnxnLimit) { latch.countDown(); } } } } private final class ClientConnectionGenerator extends Thread { private final int cnxnThreadId; private final int cnxnPerThread; private final AtomicInteger cnxnCreated; private final int cnxnLimit; private final CountDownLatch latch; private final LinkedBlockingQueue zks; private ClientConnectionGenerator(int cnxnThreadId, int cnxnPerThread, AtomicInteger cnxnCreated, int cnxnLimit, CountDownLatch latch, LinkedBlockingQueue zks) { this.cnxnThreadId = cnxnThreadId; this.cnxnPerThread = cnxnPerThread; this.cnxnCreated = cnxnCreated; this.cnxnLimit = cnxnLimit; this.latch = latch; this.zks = zks; } @Override public void run() { for (int j = 0; j < cnxnPerThread; j++) { try { zks.add(new ZooKeeper(hostPort, 30000, new ClientConnectionWatcher(cnxnCreated, cnxnLimit, cnxnThreadId, j, latch))); } catch (Exception e) { LOG.info("Error while creating zk client", e); } } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Ne0100644 0000000 0000000 00000000164 15051152474 032670 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NettyServerCnxnTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NettyServerCnxnTes0100644 0000000 0000000 00000051331 15051152474 034356 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelId; import io.netty.channel.ChannelPipeline; import io.netty.util.Attribute; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ProtocolException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.ClientCnxnSocketNetty; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.NettyUtils; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.BufferStats; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.SSLAuthTest; import org.apache.zookeeper.test.TestByteBufAllocator; import org.apache.zookeeper.test.TestUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test verifies the behavior of NettyServerCnxn which represents a connection * from a client to the server. */ public class NettyServerCnxnTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(NettyServerCnxnTest.class); @BeforeEach @Override public void setUp() throws Exception { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); NettyServerCnxnFactory.setTestAllocator(TestByteBufAllocator.getInstance()); super.maxCnxns = 1; super.exceptionOnFailedConnect = true; super.setUp(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); NettyServerCnxnFactory.clearTestAllocator(); TestByteBufAllocator.checkForLeaks(); } /** * Test verifies the channel closure - while closing the channel * servercnxnfactory should remove all channel references to avoid * duplicate channel closure. Duplicate closure may result in indefinite * hanging due to netty open issue. * * @see NETTY-412 */ @Test @Timeout(value = 40) public void testSendCloseSession() throws Exception { assertTrue(serverFactory instanceof NettyServerCnxnFactory, "Didn't instantiate ServerCnxnFactory with NettyServerCnxnFactory!"); final ZooKeeper zk = createClient(); final ZooKeeperServer zkServer = serverFactory.getZooKeeperServer(); final String path = "/a"; try { // make sure zkclient works zk.create(path, "test".getBytes(StandardCharsets.UTF_8), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // set on watch assertNotNull(zk.exists(path, true), "Didn't create znode:" + path); assertEquals(1, zkServer.getZKDatabase().getDataTree().getWatchCount()); Iterable connections = serverFactory.getConnections(); assertEquals(1, serverFactory.getNumAliveConnections(), "Mismatch in number of live connections!"); for (ServerCnxn serverCnxn : connections) { serverCnxn.sendCloseSession(); } LOG.info("Waiting for the channel disconnected event"); int timeout = 0; while (serverFactory.getNumAliveConnections() != 0) { Thread.sleep(1000); timeout += 1000; if (timeout > CONNECTION_TIMEOUT) { fail("The number of live connections should be 0"); } } // make sure the watch is removed when the connection closed assertEquals(0, zkServer.getZKDatabase().getDataTree().getWatchCount()); } finally { zk.close(); } } /** * In the {@link #setUp()} routine, the maximum number of connections per IP * is set to 1. This tests that if more than one connection is attempted, the * connection fails. */ @Test @Timeout(value = 40) public void testMaxConnectionPerIpSurpased() { assertTrue(serverFactory instanceof NettyServerCnxnFactory, "Did not instantiate ServerCnxnFactory with NettyServerCnxnFactory!"); assertThrows(ProtocolException.class, () -> { try (final ZooKeeper zk1 = createClient(); final ZooKeeper zk2 = createClient()) { } }); } @Test public void testClientResponseStatsUpdate() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient()) { BufferStats clientResponseStats = serverFactory.getZooKeeperServer().serverStats().getClientResponseStats(); assertThat("Last client response size should be initialized with INIT_VALUE", clientResponseStats.getLastBufferSize(), equalTo(BufferStats.INIT_VALUE)); zk.create("/a", "test".getBytes(StandardCharsets.UTF_8), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertThat("Last client response size should be greater than 0 after client request was performed", clientResponseStats.getLastBufferSize(), greaterThan(0)); byte[] contents = zk.getData("/a", null, null); assertArrayEquals("test".getBytes(StandardCharsets.UTF_8), contents, "unexpected data"); } } @Test public void testNonMTLSLocalConn() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient()) { ServerStats serverStats = serverFactory.getZooKeeperServer().serverStats(); //2 for local stat connection and this client assertEquals(2, serverStats.getNonMTLSLocalConnCount()); assertEquals(0, serverStats.getNonMTLSRemoteConnCount()); } } @Test public void testNonMTLSRemoteConn() throws Exception { LeaderZooKeeperServer zks = mock(LeaderZooKeeperServer.class); when(zks.isRunning()).thenReturn(true); ServerStats.Provider providerMock = mock(ServerStats.Provider.class); when(zks.serverStats()).thenReturn(new ServerStats(providerMock)); testNonMTLSRemoteConn(zks, false, false); } @Test public void testNonMTLSRemoteConnZookKeeperServerNotReady() throws Exception { testNonMTLSRemoteConn(null, false, false); } @Test public void testNonMTLSRemoteConnZookKeeperServerNotReadyEarlyDropEnabled() throws Exception { testNonMTLSRemoteConn(null, false, true); } @Test public void testMTLSRemoteConnZookKeeperServerNotReadyEarlyDropEnabled() throws Exception { testNonMTLSRemoteConn(null, true, true); } @Test public void testMTLSRemoteConnZookKeeperServerNotReadyEarlyDropDisabled() throws Exception { testNonMTLSRemoteConn(null, true, true); } @SuppressWarnings("unchecked") private void testNonMTLSRemoteConn(ZooKeeperServer zks, boolean secure, boolean earlyDrop) throws Exception { try { System.setProperty(NettyServerCnxnFactory.EARLY_DROP_SECURE_CONNECTION_HANDSHAKES, earlyDrop + ""); Channel channel = mock(Channel.class); ChannelId id = mock(ChannelId.class); ChannelFuture success = mock(ChannelFuture.class); ChannelHandlerContext context = mock(ChannelHandlerContext.class); ChannelPipeline channelPipeline = mock(ChannelPipeline.class); when(context.channel()).thenReturn(channel); when(channel.pipeline()).thenReturn(channelPipeline); when(success.channel()).thenReturn(channel); when(channel.closeFuture()).thenReturn(success); InetSocketAddress address = new InetSocketAddress(0); when(channel.remoteAddress()).thenReturn(address); when(channel.id()).thenReturn(id); NettyServerCnxnFactory factory = new NettyServerCnxnFactory(); factory.setSecure(secure); factory.setZooKeeperServer(zks); Attribute atr = mock(Attribute.class); Mockito.doReturn(atr).when(channel).attr( Mockito.any() ); doNothing().when(atr).set(Mockito.any()); factory.channelHandler.channelActive(context); if (zks != null) { assertEquals(0, zks.serverStats().getNonMTLSLocalConnCount()); assertEquals(1, zks.serverStats().getNonMTLSRemoteConnCount()); } else { if (earlyDrop && secure) { // the channel must have been forcibly closed Mockito.verify(channel, times(1)).close(); } else { Mockito.verify(channel, times(0)).close(); } } } finally { System.clearProperty(NettyServerCnxnFactory.EARLY_DROP_SECURE_CONNECTION_HANDSHAKES); } } @Test public void testServerSideThrottling() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient()) { BufferStats clientResponseStats = serverFactory.getZooKeeperServer().serverStats().getClientResponseStats(); assertThat("Last client response size should be initialized with INIT_VALUE", clientResponseStats.getLastBufferSize(), equalTo(BufferStats.INIT_VALUE)); zk.create("/a", "test".getBytes(StandardCharsets.UTF_8), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertThat("Last client response size should be greater than 0 after client request was performed", clientResponseStats.getLastBufferSize(), greaterThan(0)); for (final ServerCnxn cnxn : serverFactory.cnxns) { final NettyServerCnxn nettyCnxn = ((NettyServerCnxn) cnxn); // Disable receiving data for all open connections ... nettyCnxn.disableRecv(); // ... then force a throttled read after 1 second (this puts the read into queuedBuffer) ... nettyCnxn.getChannel().eventLoop().schedule(new Runnable() { @Override public void run() { nettyCnxn.getChannel().read(); } }, 1, TimeUnit.SECONDS); // ... and finally disable throttling after 2 seconds. nettyCnxn.getChannel().eventLoop().schedule(new Runnable() { @Override public void run() { nettyCnxn.enableRecv(); } }, 2, TimeUnit.SECONDS); } byte[] contents = zk.getData("/a", null, null); assertArrayEquals("test".getBytes(StandardCharsets.UTF_8), contents, "unexpected data"); // As above, but don't do the throttled read. Make the request bytes wait in the socket // input buffer until after throttling is turned off. Need to make sure both modes work. for (final ServerCnxn cnxn : serverFactory.cnxns) { final NettyServerCnxn nettyCnxn = ((NettyServerCnxn) cnxn); // Disable receiving data for all open connections ... nettyCnxn.disableRecv(); // ... then disable throttling after 2 seconds. nettyCnxn.getChannel().eventLoop().schedule(new Runnable() { @Override public void run() { nettyCnxn.enableRecv(); } }, 2, TimeUnit.SECONDS); } contents = zk.getData("/a", null, null); assertArrayEquals("test".getBytes(StandardCharsets.UTF_8), contents, "unexpected data"); } } @Test public void testEnableDisableThrottling_secure_random() throws Exception { runEnableDisableThrottling(true, true); } @Test public void testEnableDisableThrottling_secure_sequentially() throws Exception { runEnableDisableThrottling(true, false); } @Test public void testEnableDisableThrottling_nonSecure_random() throws Exception { runEnableDisableThrottling(false, true); } @Test public void testEnableDisableThrottling_nonSecure_sequentially() throws Exception { runEnableDisableThrottling(false, false); } @Test public void testNettyUsesDaemonThreads() throws Exception { assertTrue(serverFactory instanceof NettyServerCnxnFactory, "Didn't instantiate ServerCnxnFactory with NettyServerCnxnFactory!"); // Use Netty in the client to check the threads on both the client and server side System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, ClientCnxnSocketNetty.class.getName()); try { final ZooKeeperServer zkServer = serverFactory.getZooKeeperServer(); try (ZooKeeper zk = createClient()) { final String path = "/a"; // make sure connection is established zk.create(path, "test".getBytes(StandardCharsets.UTF_8), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List threads = TestUtils.getAllThreads(); boolean foundThread = false; for (Thread t : threads) { if (t.getName().startsWith(NettyUtils.THREAD_POOL_NAME_PREFIX)) { foundThread = true; assertTrue(t.isDaemon(), "All Netty threads started by ZK must daemon threads"); } } assertTrue(foundThread, "Did not find any Netty ZK Threads"); } finally { zkServer.shutdown(); } } finally { System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); } } private void runEnableDisableThrottling(boolean secure, boolean randomDisableEnable) throws Exception { ClientX509Util x509Util = null; if (secure) { x509Util = SSLAuthTest.setUpSecure(); } try { NettyServerCnxnFactory factory = (NettyServerCnxnFactory) serverFactory; factory.setAdvancedFlowControlEnabled(true); if (secure) { factory.setSecure(true); } final String path = "/testEnableDisableThrottling"; try (ZooKeeper zk = createClient()) { zk.create(path, new byte[1], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // meanwhile start another thread to enable and disable recv AtomicBoolean stopped = new AtomicBoolean(false); Random random = new Random(); Thread enableDisableThread = null; if (randomDisableEnable) { enableDisableThread = new Thread() { @Override public void run() { while (!stopped.get()) { for (final ServerCnxn cnxn : serverFactory.cnxns) { boolean shouldDisableEnable = random.nextBoolean(); if (shouldDisableEnable) { cnxn.disableRecv(); } else { cnxn.enableRecv(); } } try { Thread.sleep(10); } catch (InterruptedException e) { /* ignore */ } } // always enable the recv at end for (final ServerCnxn cnxn : serverFactory.cnxns) { cnxn.enableRecv(); } } }; } else { enableDisableThread = new Thread() { @Override public void run() { while (!stopped.get()) { for (final ServerCnxn cnxn : serverFactory.cnxns) { try { cnxn.disableRecv(); Thread.sleep(10); cnxn.enableRecv(); Thread.sleep(10); } catch (InterruptedException e) { /* ignore */ } } } } }; } enableDisableThread.start(); LOG.info("started thread to enable and disable recv"); // start a thread to keep sending requests int totalRequestsNum = 100000; AtomicInteger successResponse = new AtomicInteger(); CountDownLatch responseReceivedLatch = new CountDownLatch(totalRequestsNum); Thread clientThread = new Thread() { @Override public void run() { int requestIssued = 0; while (requestIssued++ < totalRequestsNum) { zk.getData(path, null, new DataCallback() { @Override public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { if (rc == KeeperException.Code.OK.intValue()) { successResponse.addAndGet(1); } else { LOG.info("failed response is {}", rc); } responseReceivedLatch.countDown(); } }, null); } } }; clientThread.start(); LOG.info("started thread to issue {} async requests", totalRequestsNum); // and verify the response received is same as what we issued assertTrue(responseReceivedLatch.await(60, TimeUnit.SECONDS)); LOG.info("received all {} responses", totalRequestsNum); stopped.set(true); enableDisableThread.join(); LOG.info("enable and disable recv thread exited"); // wait another second for the left requests to finish LOG.info("waiting another 1s for the requests to go through"); Thread.sleep(1000); assertEquals(successResponse.get(), totalRequestsNum); } } finally { if (secure) { SSLAuthTest.clearSecureSetting(x509Util); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_No0100644 0000000 0000000 00000000164 15051152474 032702 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NodeHashMapImplTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/NodeHashMapImplTes0100644 0000000 0000000 00000006062 15051152474 034207 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.util.Map; import java.util.Set; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.data.StatPersisted; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class NodeHashMapImplTest extends ZKTestCase { @BeforeEach public void setUp() { ZooKeeperServer.setDigestEnabled(true); } @AfterEach public void tearDown() { ZooKeeperServer.setDigestEnabled(false); } /** * Test all the operations supported in NodeHashMapImpl. */ @Test public void testOperations() { NodeHashMapImpl nodes = new NodeHashMapImpl(new DigestCalculator()); assertEquals(0, nodes.size()); assertEquals(0L, nodes.getDigest()); // add a new node String p1 = "p1"; DataNode n1 = new DataNode(p1.getBytes(), 0L, new StatPersisted()); nodes.put(p1, n1); assertEquals(n1, nodes.get(p1)); assertNotEquals(0L, nodes.getDigest()); assertEquals(1, nodes.size()); // put another node String p2 = "p2"; nodes.put(p2, new DataNode(p2.getBytes(), 0L, new StatPersisted())); Set> entries = nodes.entrySet(); assertEquals(2, entries.size()); // remove a node nodes.remove(p1); assertEquals(1, nodes.size()); nodes.remove(p2); assertEquals(0, nodes.size()); assertEquals(0L, nodes.getDigest()); // test preChange and postChange String p3 = "p3"; DataNode n3 = new DataNode(p3.getBytes(), 0L, new StatPersisted()); nodes.put(p3, n3); long preChangeDigest = nodes.getDigest(); assertNotEquals(0L, preChangeDigest); nodes.preChange(p3, n3); assertEquals(0L, nodes.getDigest()); n3.stat.setMzxid(1); n3.stat.setMtime(1); n3.stat.setVersion(1); nodes.postChange(p3, n3); long postChangeDigest = nodes.getDigest(); assertNotEquals(0, postChangeDigest); assertNotEquals(preChangeDigest, postChangeDigest); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Pr0100644 0000000 0000000 00000000200 15051152474 032676 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/PrepRequestProcessorMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/PrepRequestProcess0100644 0000000 0000000 00000016666 15051152474 034413 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.io.IOException; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.StatPersisted; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.proto.DeleteRequest; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PrepRequestProcessorMetricsTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(PrepRequestProcessorMetricsTest.class); ZooKeeperServer zks; RequestProcessor nextProcessor; @BeforeEach public void setup() { System.setProperty(ZooKeeperServer.SKIP_ACL, "true"); zks = spy(new ZooKeeperServer()); zks.sessionTracker = mock(SessionTracker.class); ZKDatabase db = mock(ZKDatabase.class); when(zks.getZKDatabase()).thenReturn(db); DataNode node = new DataNode(new byte[1], null, mock(StatPersisted.class)); when(db.getNode(anyString())).thenReturn(node); DataTree dataTree = mock(DataTree.class); when(db.getDataTree()).thenReturn(dataTree); Set ephemerals = new HashSet<>(); ephemerals.add("/crystalmountain"); ephemerals.add("/stevenspass"); when(db.getEphemerals(anyLong())).thenReturn(ephemerals); nextProcessor = mock(RequestProcessor.class); ServerMetrics.getMetrics().resetAll(); } @AfterEach public void tearDown() throws Exception { System.clearProperty(ZooKeeperServer.SKIP_ACL); } private Request createRequest(Record record, int opCode) throws IOException { return new Request(null, 1L, 0, opCode, RequestRecord.fromRecord(record), null); } private Request createRequest(String path, int opCode) throws IOException { Record record; switch (opCode) { case ZooDefs.OpCode.setData: record = new SetDataRequest(path, new byte[0], -1); break; case ZooDefs.OpCode.delete: record = new DeleteRequest(path, -1); break; default: record = new DeleteRequest(path, -1); break; } return createRequest(record, opCode); } private Request createRequest(long sessionId, int opCode) { return new Request(null, sessionId, 0, opCode, null, null); } @Test public void testPrepRequestProcessorMetrics() throws Exception { CountDownLatch threeRequests = new CountDownLatch(3); doAnswer(invocationOnMock -> { threeRequests.countDown(); return null; }).when(nextProcessor).processRequest(any(Request.class)); PrepRequestProcessor prepRequestProcessor = new PrepRequestProcessor(zks, nextProcessor); //setData will generate one change prepRequestProcessor.processRequest(createRequest("/foo", ZooDefs.OpCode.setData)); //delete will generate two changes, one for itself, one for its parent prepRequestProcessor.processRequest(createRequest("/foo/bar", ZooDefs.OpCode.delete)); //mocking two ephemeral nodes exists for this session so two changes prepRequestProcessor.processRequest(createRequest(2, ZooDefs.OpCode.closeSession)); Map values = MetricsUtils.currentServerMetrics(); assertEquals(3L, values.get("prep_processor_request_queued")); // the sleep is just to make sure the requests will stay in the queue for some time Thread.sleep(20); prepRequestProcessor.start(); threeRequests.await(500, TimeUnit.MILLISECONDS); values = MetricsUtils.currentServerMetrics(); assertEquals(3L, values.get("max_prep_processor_queue_size")); assertThat((long) values.get("min_prep_processor_queue_time_ms"), greaterThan(20L)); assertEquals(3L, values.get("cnt_prep_processor_queue_time_ms")); assertEquals(3L, values.get("cnt_prep_process_time")); assertThat((long) values.get("max_prep_process_time"), greaterThan(0L)); assertEquals(1L, values.get("cnt_close_session_prep_time")); assertThat((long) values.get("max_close_session_prep_time"), greaterThanOrEqualTo(0L)); // With digest feature, we have two more OUTSTANDING_CHANGES_QUEUED than w/o digest // The expected should 5 in open source until we upstream the digest feature assertEquals(7L, values.get("outstanding_changes_queued")); } private class SimpleWatcher implements Watcher { CountDownLatch created; public SimpleWatcher(CountDownLatch latch) { this.created = latch; } @Override public void process(WatchedEvent e) { created.countDown(); } } @Test public void testOutstandingChangesRemoved() throws Exception { // this metric is currently recorded in FinalRequestProcessor but it is tightly related to the Prep metrics QuorumUtil util = new QuorumUtil(1); util.startAll(); ServerMetrics.getMetrics().resetAll(); ZooKeeper zk = ClientBase.createZKClient(util.getConnString()); zk.create("/test", new byte[50], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); CountDownLatch created = new CountDownLatch(1); zk.exists("/test", new SimpleWatcher(created)); created.await(200, TimeUnit.MILLISECONDS); Map values = MetricsUtils.currentServerMetrics(); assertThat((long) values.get("outstanding_changes_removed"), greaterThan(0L)); util.shutdownAll(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Pr0100644 0000000 0000000 00000000171 15051152474 032705 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/PrepRequestProcessorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/PrepRequestProcess0100644 0000000 0000000 00000036231 15051152474 034401 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; import java.io.PrintWriter; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.KeeperException.SessionMovedException; import org.apache.zookeeper.MultiOperationRecord; import org.apache.zookeeper.Op; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.ReconfigRequest; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.server.ZooKeeperServer.ChangeRecord; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LeaderBeanTest; import org.apache.zookeeper.server.quorum.LeaderZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.ErrorTxn; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class PrepRequestProcessorTest extends ClientBase { private static final int CONNECTION_TIMEOUT = 3000; private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private CountDownLatch pLatch; private ZooKeeperServer zks; private ServerCnxnFactory servcnxnf; private PrepRequestProcessor processor; private Request outcome; private boolean isReconfigEnabledPreviously; private boolean isStandaloneEnabledPreviously; @BeforeEach public void setup() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(100); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); servcnxnf = ServerCnxnFactory.createFactory(PORT, -1); servcnxnf.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); zks.sessionTracker = new MySessionTracker(); isReconfigEnabledPreviously = QuorumPeerConfig.isReconfigEnabled(); isStandaloneEnabledPreviously = QuorumPeerConfig.isStandaloneEnabled(); } @AfterEach public void teardown() throws Exception { if (servcnxnf != null) { servcnxnf.shutdown(); } if (zks != null) { zks.shutdown(); } // reset the reconfig option QuorumPeerConfig.setReconfigEnabled(isReconfigEnabledPreviously); QuorumPeerConfig.setStandaloneEnabled(isStandaloneEnabledPreviously); } @Test public void testPRequest() throws Exception { pLatch = new CountDownLatch(1); processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); Request foo = new Request(null, 1L, 1, OpCode.create, RequestRecord.fromBytes(new byte[3]), null); processor.pRequest(foo); assertEquals(new ErrorTxn(KeeperException.Code.MARSHALLINGERROR.intValue()), outcome.getTxn(), "Request should have marshalling error"); assertTrue(pLatch.await(5, TimeUnit.SECONDS), "request hasn't been processed in chain"); } private Request createRequest(Record record, int opCode) { return createRequest(record, opCode, 1L); } private Request createRequest(Record record, int opCode, long sessionId) { return createRequest(record, opCode, sessionId, false); } private Request createRequest(Record record, int opCode, boolean admin) { return createRequest(record, opCode, 1L, admin); } private Request createRequest(Record record, int opCode, long sessionId, boolean admin) { List ids = Collections.singletonList(admin ? new Id("super", "super user") : Ids.ANYONE_ID_UNSAFE); return new Request(null, sessionId, 0, opCode, RequestRecord.fromRecord(record), ids); } private void process(List ops) throws Exception { pLatch = new CountDownLatch(1); processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); Record record = new MultiOperationRecord(ops); Request req = createRequest(record, OpCode.multi, false); processor.pRequest(req); assertTrue(pLatch.await(5, TimeUnit.SECONDS), "request hasn't been processed in chain"); } /** * This test checks that a successful multi will change outstanding record * and failed multi shouldn't change outstanding record. */ @Test public void testMultiOutstandingChange() throws Exception { zks.getZKDatabase().dataTree.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); assertNull(zks.outstandingChangesForPath.get("/foo")); process(Arrays.asList(Op.setData("/foo", new byte[0], -1))); ChangeRecord cr = zks.outstandingChangesForPath.get("/foo"); assertNotNull(cr, "Change record wasn't set"); assertEquals(1, cr.zxid, "Record zxid wasn't set correctly"); process(Arrays.asList(Op.delete("/foo", -1))); cr = zks.outstandingChangesForPath.get("/foo"); assertEquals(2, cr.zxid, "Record zxid wasn't set correctly"); // It should fail and shouldn't change outstanding record. process(Arrays.asList(Op.delete("/foo", -1))); cr = zks.outstandingChangesForPath.get("/foo"); // zxid should still be previous result because record's not changed. assertEquals(2, cr.zxid, "Record zxid wasn't set correctly"); } @Test public void testReconfigWithAnotherOutstandingChange() throws Exception { QuorumPeerConfig.setReconfigEnabled(true); QuorumPeerConfig.setStandaloneEnabled(false); QuorumPeer qp = new QuorumPeer(); QuorumVerifier quorumVerifierMock = mock(QuorumVerifier.class); when(quorumVerifierMock.getAllMembers()).thenReturn(LeaderBeanTest.getMockedPeerViews(qp.getMyId())); qp.setQuorumVerifier(quorumVerifierMock, false); FileTxnSnapLog snapLog = new FileTxnSnapLog(tmpDir, tmpDir); LeaderZooKeeperServer lzks = new LeaderZooKeeperServer(snapLog, qp, new ZKDatabase(snapLog)); qp.leader = new Leader(qp, lzks); lzks.sessionTracker = new MySessionTracker(); ZooKeeperServer.setDigestEnabled(true); processor = new PrepRequestProcessor(lzks, new MyRequestProcessor()); Record record = new CreateRequest("/foo", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT.toFlag()); pLatch = new CountDownLatch(1); processor.pRequest(createRequest(record, OpCode.create, false)); assertTrue(pLatch.await(5, TimeUnit.SECONDS), "request hasn't been processed in chain"); String newMember = "server.0=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant"; record = new ReconfigRequest(null, null, newMember, 0); pLatch = new CountDownLatch(1); processor.pRequest(createRequest(record, OpCode.reconfig, true)); assertTrue(pLatch.await(5, TimeUnit.SECONDS), "request hasn't been processed in chain"); assertEquals(outcome.getHdr().getType(), OpCode.reconfig); // Verifies that there was no error. } /** * ZOOKEEPER-2052: * This test checks that if a multi operation aborted, and during the multi there is side effect * that changed outstandingChangesForPath, after aborted the side effect should be removed and * everything should be restored correctly. */ @Test public void testMultiRollbackNoLastChange() throws Exception { zks.getZKDatabase().dataTree.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); zks.getZKDatabase().dataTree.createNode("/foo/bar", new byte[0], Ids.OPEN_ACL_UNSAFE, 0, 0, 0, 0); assertNull(zks.outstandingChangesForPath.get("/foo")); // multi record: // set "/foo" => succeed, leave a outstanding change // delete "/foo" => fail, roll back change process(Arrays.asList(Op.setData("/foo", new byte[0], -1), Op.delete("/foo", -1))); // aborting multi shouldn't leave any record. assertNull(zks.outstandingChangesForPath.get("/foo")); } /** * Test ephemerals are deleted when the session is closed with * the newly added CloseSessionTxn in ZOOKEEPER-3145. */ @Test public void testCloseSessionTxn() throws Exception { boolean before = ZooKeeperServer.isCloseSessionTxnEnabled(); ZooKeeperServer.setCloseSessionTxnEnabled(true); try { // create a few ephemerals long ephemeralOwner = 1; DataTree dt = zks.getZKDatabase().dataTree; dt.createNode("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, ephemeralOwner, 0, 0, 0); dt.createNode("/bar", new byte[0], Ids.OPEN_ACL_UNSAFE, ephemeralOwner, 0, 0, 0); // close session RequestHeader header = new RequestHeader(); header.setType(OpCode.closeSession); final FinalRequestProcessor frq = new FinalRequestProcessor(zks); final CountDownLatch latch = new CountDownLatch(1); processor = new PrepRequestProcessor(zks, new RequestProcessor() { @Override public void processRequest(Request request) { frq.processRequest(request); latch.countDown(); } @Override public void shutdown() { // TODO Auto-generated method stub } }); processor.pRequest(createRequest(header, OpCode.closeSession, ephemeralOwner)); assertTrue(latch.await(3, TimeUnit.SECONDS)); // assert ephemerals are deleted assertEquals(null, dt.getNode("/foo")); assertEquals(null, dt.getNode("/bar")); } finally { ZooKeeperServer.setCloseSessionTxnEnabled(before); } } /** * It tests that PrepRequestProcessor will return BadArgument KeeperException * if the request path (if it exists) is not valid, e.g. empty string. */ @Test public void testInvalidPath() throws Exception { pLatch = new CountDownLatch(1); processor = new PrepRequestProcessor(zks, new MyRequestProcessor()); SetDataRequest record = new SetDataRequest("", new byte[0], -1); Request req = createRequest(record, OpCode.setData, false); processor.pRequest(req); pLatch.await(); assertEquals(outcome.getHdr().getType(), OpCode.error); assertEquals(outcome.getException().code(), KeeperException.Code.BADARGUMENTS); } private class MyRequestProcessor implements RequestProcessor { @Override public void processRequest(Request request) { // getting called by PrepRequestProcessor outcome = request; pLatch.countDown(); } @Override public void shutdown() { // TODO Auto-generated method stub } } private class MySessionTracker implements SessionTracker { @Override public boolean trackSession(long id, int to) { // TODO Auto-generated method stub return false; } @Override public boolean commitSession(long id, int to) { // TODO Auto-generated method stub return false; } @Override public void checkSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException { // TODO Auto-generated method stub } @Override public long createSession(int sessionTimeout) { // TODO Auto-generated method stub return 0; } @Override public void dumpSessions(PrintWriter pwriter) { // TODO Auto-generated method stub } @Override public void removeSession(long sessionId) { // TODO Auto-generated method stub } public int upgradeSession(long sessionId) { // TODO Auto-generated method stub return 0; } @Override public void setOwner(long id, Object owner) throws SessionExpiredException { // TODO Auto-generated method stub } @Override public void shutdown() { // TODO Auto-generated method stub } @Override public boolean touchSession(long sessionId, int sessionTimeout) { // TODO Auto-generated method stub return false; } @Override public void setSessionClosing(long sessionId) { // TODO Auto-generated method stub } @Override public boolean isTrackingSession(long sessionId) { // TODO Auto-generated method stub return false; } @Override public void checkGlobalSession(long sessionId, Object owner) throws SessionExpiredException, SessionMovedException { // TODO Auto-generated method stub } @Override public Map> getSessionExpiryMap() { return new HashMap<>(); } @Override public long getLocalSessionCount() { return 0; } @Override public boolean isLocalSessionsEnabled() { return false; } public Set globalSessions() { return Collections.emptySet(); } public Set localSessions() { return Collections.emptySet(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/PurgeTxnTest.java0100644 0000000 0000000 00000066137 15051152474 034127 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.CheckedOutputStream; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.persistence.FileHeader; import org.apache.zookeeper.server.persistence.FileSnap; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PurgeTxnTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(PurgeTxnTest.class); private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final int CONNECTION_TIMEOUT = 3000; private static final long OP_TIMEOUT_IN_MILLIS = 120000; private File tmpDir; @BeforeEach public void setUp() throws Exception { tmpDir = ClientBase.createTmpDir(); } @AfterEach public void teardown() { if (null != tmpDir) { ClientBase.recursiveDelete(tmpDir); } } /** * test the purge * @throws Exception an exception might be thrown here */ @Test public void testPurge() throws Exception { ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(100); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { for (int i = 0; i < 2000; i++) { zk.create("/invalidsnap-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } f.shutdown(); zks.getTxnLogFactory().close(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server to shutdown"); // now corrupt the snapshot PurgeTxnLog.purge(tmpDir, tmpDir, 3); FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpDir, tmpDir); List listLogs = snaplog.findNValidSnapshots(4); int numSnaps = 0; for (File ff : listLogs) { if (ff.getName().startsWith("snapshot")) { numSnaps++; } } assertTrue((numSnaps == 3), "exactly 3 snapshots "); snaplog.close(); zks.shutdown(); } /** * Tests purge when logs are rolling or a new snapshot is created, then * these newer files should alse be excluded in the current cycle. * * For frequent snapshotting, configured SnapCount to 30. There are three * threads which will create 1000 znodes each and simultaneously do purge * call */ @Test public void testPurgeWhenLogRollingInProgress() throws Exception { ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(30); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); final ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); final CountDownLatch doPurge = new CountDownLatch(1); final CountDownLatch purgeFinished = new CountDownLatch(1); final AtomicBoolean opFailed = new AtomicBoolean(false); new Thread() { public void run() { try { doPurge.await(OP_TIMEOUT_IN_MILLIS / 2, TimeUnit.MILLISECONDS); PurgeTxnLog.purge(tmpDir, tmpDir, 3); } catch (IOException ioe) { LOG.error("Exception when purge", ioe); opFailed.set(true); } catch (InterruptedException ie) { LOG.error("Exception when purge", ie); opFailed.set(true); } finally { purgeFinished.countDown(); } } }.start(); final int thCount = 3; List znodes = manyClientOps(zk, doPurge, thCount, "/invalidsnap"); assertTrue(purgeFinished.await(OP_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS), "Purging is not finished!"); assertFalse(opFailed.get(), "Purging failed!"); for (String znode : znodes) { try { zk.getData(znode, false, null); } catch (Exception ke) { LOG.error("Unexpected exception when visiting znode!", ke); fail("Unexpected exception when visiting znode!"); } } zk.close(); f.shutdown(); zks.shutdown(); zks.getTxnLogFactory().close(); } /** * Tests finding n recent valid snapshots from set of snapshots and data logs */ @Test public void testFindNValidSnapshots() throws Exception { int nRecentSnap = 4; // n recent snap shots int nRecentCount = 30; int offset = 0; File version2 = new File(tmpDir.toString(), "version-2"); assertTrue(version2.mkdir(), "Failed to create version_2 dir:" + version2.toString()); // Test that with no snaps, findNValidSnapshots returns empty list FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); List foundSnaps = txnLog.findNValidSnapshots(1); assertEquals(0, foundSnaps.size()); List expectedNRecentSnapFiles = new ArrayList<>(); int counter = offset + (2 * nRecentCount); for (int i = 0; i < nRecentCount; i++) { // simulate log file File logFile = new File(version2 + "/log." + Long.toHexString(--counter)); assertTrue(logFile.createNewFile(), "Failed to create log File:" + logFile.toString()); // simulate snapshot file File snapFile = new File(version2 + "/snapshot." + Long.toHexString(--counter)); assertTrue(snapFile.createNewFile(), "Failed to create snap File:" + snapFile.toString()); makeValidSnapshot(snapFile); // add the n recent snap files for assertion if (i < nRecentSnap) { expectedNRecentSnapFiles.add(snapFile); } } // Test that when we ask for recent snaps we get the number we asked for and // the files we expected List nRecentValidSnapFiles = txnLog.findNValidSnapshots(nRecentSnap); assertEquals(4, nRecentValidSnapFiles.size(), "exactly 4 snapshots "); expectedNRecentSnapFiles.removeAll(nRecentValidSnapFiles); assertEquals(0, expectedNRecentSnapFiles.size(), "Didn't get the recent snap files"); // Test that when asking for more snaps than we created, we still only get snaps // not logs or anything else (per ZOOKEEPER-2420) nRecentValidSnapFiles = txnLog.findNValidSnapshots(nRecentCount + 5); assertEquals(nRecentCount, nRecentValidSnapFiles.size()); for (File f : nRecentValidSnapFiles) { assertTrue((Util.getZxidFromName(f.getName(), "snapshot") != -1), "findNValidSnapshots() returned a non-snapshot: " + f.getPath()); } txnLog.close(); } /** * Tests purge where the data directory contains old snapshots and data * logs, newest snapshots and data logs, (newest + n) snapshots and data * logs */ @Test public void testSnapFilesGreaterThanToRetain() throws Exception { int nRecentCount = 4; int fileAboveRecentCount = 4; int fileToPurgeCount = 2; AtomicInteger offset = new AtomicInteger(0); File version2 = new File(tmpDir.toString(), "version-2"); assertTrue(version2.mkdir(), "Failed to create version_2 dir:" + version2.toString()); List snapsToPurge = new ArrayList<>(); List logsToPurge = new ArrayList<>(); List snaps = new ArrayList<>(); List logs = new ArrayList<>(); List snapsAboveRecentFiles = new ArrayList<>(); List logsAboveRecentFiles = new ArrayList<>(); createDataDirFiles(offset, fileToPurgeCount, false, version2, snapsToPurge, logsToPurge); createDataDirFiles(offset, nRecentCount, false, version2, snaps, logs); logs.add(logsToPurge.remove(0)); // log that precedes first retained snapshot is also retained createDataDirFiles(offset, fileAboveRecentCount, false, version2, snapsAboveRecentFiles, logsAboveRecentFiles); /** * The newest log file preceding the oldest retained snapshot is not removed as it may * contain transactions newer than the oldest snapshot. */ logsToPurge.remove(0); FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1)); txnLog.close(); verifyFilesAfterPurge(snapsToPurge, false); verifyFilesAfterPurge(logsToPurge, false); verifyFilesAfterPurge(snaps, true); verifyFilesAfterPurge(logs, true); verifyFilesAfterPurge(snapsAboveRecentFiles, true); verifyFilesAfterPurge(logsAboveRecentFiles, true); } /** * Tests purge where the data directory contains snap files and log files equals to the * number of files to be retained */ @Test public void testSnapFilesEqualsToRetain() throws Exception { internalTestSnapFilesEqualsToRetain(false); } /** * Tests purge where the data directory contains snap files equals to the * number of files to be retained, and a log file that precedes the earliest snapshot */ @Test public void testSnapFilesEqualsToRetainWithPrecedingLog() throws Exception { internalTestSnapFilesEqualsToRetain(true); } public void internalTestSnapFilesEqualsToRetain(boolean testWithPrecedingLogFile) throws Exception { int nRecentCount = 3; AtomicInteger offset = new AtomicInteger(0); File version2 = new File(tmpDir.toString(), "version-2"); assertTrue(version2.mkdir(), "Failed to create version_2 dir:" + version2.toString()); List snaps = new ArrayList<>(); List logs = new ArrayList<>(); createDataDirFiles(offset, nRecentCount, testWithPrecedingLogFile, version2, snaps, logs); FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1)); txnLog.close(); verifyFilesAfterPurge(snaps, true); verifyFilesAfterPurge(logs, true); } /** * Tests purge where the data directory contains old snapshots and data * logs, newest snapshots and data logs */ @Test public void testSnapFilesLessThanToRetain() throws Exception { int nRecentCount = 4; int fileToPurgeCount = 2; AtomicInteger offset = new AtomicInteger(0); File version2 = new File(tmpDir.toString(), "version-2"); assertTrue(version2.mkdir(), "Failed to create version_2 dir:" + version2.toString()); List snapsToPurge = new ArrayList<>(); List logsToPurge = new ArrayList<>(); List snaps = new ArrayList<>(); List logs = new ArrayList<>(); createDataDirFiles(offset, fileToPurgeCount, false, version2, snapsToPurge, logsToPurge); createDataDirFiles(offset, nRecentCount, false, version2, snaps, logs); logs.add(logsToPurge.remove(0)); // log that precedes first retained snapshot is also retained /** * The newest log file preceding the oldest retained snapshot is not removed as it may * contain transactions newer than the oldest snapshot. */ logsToPurge.remove(0); FileTxnSnapLog txnLog = new FileTxnSnapLog(tmpDir, tmpDir); PurgeTxnLog.purgeOlderSnapshots(txnLog, snaps.get(snaps.size() - 1)); txnLog.close(); verifyFilesAfterPurge(snapsToPurge, false); verifyFilesAfterPurge(logsToPurge, false); verifyFilesAfterPurge(snaps, true); verifyFilesAfterPurge(logs, true); } /** * PurgeTxnLog is called with dataLogDir snapDir -n count This test case * verify these values are parsed properly and functionality works fine */ @Test public void testPurgeTxnLogWithDataDir() throws Exception { File dataDir = new File(tmpDir, "dataDir"); File dataLogDir = new File(tmpDir, "dataLogDir"); File dataDirVersion2 = new File(dataDir, "version-2"); dataDirVersion2.mkdirs(); File dataLogDirVersion2 = new File(dataLogDir, "version-2"); dataLogDirVersion2.mkdirs(); // create dummy log and transaction file int totalFiles = 20; // create transaction and snapshot files in different-different // directories for (int i = 0; i < totalFiles; i++) { // simulate log file File logFile = new File(dataLogDirVersion2, "log." + Long.toHexString(i)); logFile.createNewFile(); // simulate snapshot file File snapFile = new File(dataDirVersion2, "snapshot." + Long.toHexString(i)); snapFile.createNewFile(); makeValidSnapshot(snapFile); } int numberOfSnapFilesToKeep = 10; // scenario where four parameter are passed String[] args = new String[]{dataLogDir.getAbsolutePath(), dataDir.getAbsolutePath(), "-n", Integer.toString(numberOfSnapFilesToKeep)}; PurgeTxnLog.main(args); assertEquals(numberOfSnapFilesToKeep, dataDirVersion2.listFiles().length); // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept assertEquals(numberOfSnapFilesToKeep, dataLogDirVersion2.listFiles().length); } /** * PurgeTxnLog is called with dataLogDir -n count This test case verify * these values are parsed properly and functionality works fine */ @Test public void testPurgeTxnLogWithoutDataDir() throws Exception { File dataDir = new File(tmpDir, "dataDir"); File dataLogDir = new File(tmpDir, "dataLogDir"); File dataDirVersion2 = new File(dataDir, "version-2"); dataDirVersion2.mkdirs(); File dataLogDirVersion2 = new File(dataLogDir, "version-2"); dataLogDirVersion2.mkdirs(); // create dummy log and transaction file int totalFiles = 20; // create transaction and snapshot files in data directory for (int i = 0; i < totalFiles; i++) { // simulate log file File logFile = new File(dataLogDirVersion2, "log." + Long.toHexString(i)); logFile.createNewFile(); // simulate snapshot file File snapFile = new File(dataLogDirVersion2, "snapshot." + Long.toHexString(i)); snapFile.createNewFile(); makeValidSnapshot(snapFile); } int numberOfSnapFilesToKeep = 10; // scenario where only three parameter are passed String[] args = new String[]{dataLogDir.getAbsolutePath(), "-n", Integer.toString(numberOfSnapFilesToKeep)}; PurgeTxnLog.main(args); assertEquals( numberOfSnapFilesToKeep * 2, // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept dataLogDirVersion2.listFiles().length); } /** * Verifies that purge does not delete any log files which started before the oldest retained * snapshot but which might extend beyond it. * @throws Exception an exception might be thrown here */ @Test public void testPurgeDoesNotDeleteOverlappingLogFile() throws Exception { // Setting used for snapRetainCount in this test. final int SNAP_RETAIN_COUNT = 3; // Number of znodes this test creates in each snapshot. final int NUM_ZNODES_PER_SNAPSHOT = 100; /** * Set a sufficiently high snapCount to ensure that we don't rollover the log. Normally, * the default value (100K at time of this writing) would ensure this, but we make that * dependence explicit here to make the test future-proof. Not rolling over the log is * important for this test since we are testing retention of the one and only log file which * predates each retained snapshot. */ SyncRequestProcessor.setSnapCount(SNAP_RETAIN_COUNT * NUM_ZNODES_PER_SNAPSHOT * 10); // Create Zookeeper and connect to it. ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); // Unique identifier for each znode that we create. int unique = 0; try { /** * Create some znodes and take a snapshot. Repeat this until we have SNAP_RETAIN_COUNT * snapshots. Do not rollover the log. */ for (int snapshotCount = 0; snapshotCount < SNAP_RETAIN_COUNT; snapshotCount++) { for (int i = 0; i < 100; i++, unique++) { zk.create("/snap-" + unique, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zks.takeSnapshot(); } // Create some additional znodes without taking a snapshot afterwards. for (int i = 0; i < 100; i++, unique++) { zk.create("/snap-" + unique, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } // Shutdown Zookeeper. f.shutdown(); zks.getTxnLogFactory().close(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server to shutdown"); // Purge snapshot and log files. PurgeTxnLog.purge(tmpDir, tmpDir, SNAP_RETAIN_COUNT); // Initialize Zookeeper again from the same dataDir. zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); zk = ClientBase.createZKClient(HOSTPORT); /** * Verify that the last znode that was created above exists. This znode's creation was * captured by the transaction log which was created before any of the above * SNAP_RETAIN_COUNT snapshots were created, but it's not captured in any of these * snapshots. So for it it exist, the (only) existing log file should not have been purged. */ final String lastZnode = "/snap-" + (unique - 1); final Stat stat = zk.exists(lastZnode, false); assertNotNull(stat, "Last znode does not exist: " + lastZnode); // Shutdown for the last time. f.shutdown(); zks.getTxnLogFactory().close(); zks.shutdown(); } @Test public void testPurgeTxnLogWhenRecentSnapshotsAreAllInvalid() throws Exception { File dataDir = new File(tmpDir, "dataDir"); File dataLogDir = new File(tmpDir, "dataLogDir"); File dataDirVersion2 = new File(dataDir, "version-2"); dataDirVersion2.mkdirs(); File dataLogDirVersion2 = new File(dataLogDir, "version-2"); dataLogDirVersion2.mkdirs(); // create dummy log and transaction file int totalFiles = 10; int numberOfSnapFilesToKeep = 3; // create transaction and snapshot files in different-different // directories for (int i = 0; i < totalFiles; i++) { // simulate log file File logFile = new File(dataLogDirVersion2, "log." + Long.toHexString(i)); logFile.createNewFile(); // simulate snapshot file File snapFile = new File(dataDirVersion2, "snapshot." + Long.toHexString(i)); snapFile.createNewFile(); if (i < (totalFiles - numberOfSnapFilesToKeep)) { makeValidSnapshot(snapFile); } else { makeInvalidSnapshot(snapFile); } } // scenario where four parameter are passed String[] args = new String[]{dataLogDir.getAbsolutePath(), dataDir.getAbsolutePath(), "-n", Integer.toString(numberOfSnapFilesToKeep)}; PurgeTxnLog.main(args); //Since the recent 3 snapshots are all invalid,when purging, we can assert that 6 snapshot files are retained(3 invalid snapshots and 3 retained valid snapshots) assertEquals(numberOfSnapFilesToKeep + numberOfSnapFilesToKeep, dataDirVersion2.listFiles().length); // Since for each snapshot we have a log file with same zxid, expect same # logs as snaps to be kept assertEquals(numberOfSnapFilesToKeep + numberOfSnapFilesToKeep, dataLogDirVersion2.listFiles().length); } private File createDataDirLogFile(File version_2, int Zxid) throws IOException { File logFile = new File(version_2 + "/log." + Long.toHexString(Zxid)); assertTrue(logFile.createNewFile(), "Failed to create log File:" + logFile.toString()); return logFile; } private void createDataDirFiles(AtomicInteger offset, int limit, boolean createPrecedingLogFile, File version_2, List snaps, List logs) throws IOException { int counter = offset.get() + (2 * limit); if (createPrecedingLogFile) { counter++; } offset.set(counter); for (int i = 0; i < limit; i++) { // simulate log file logs.add(createDataDirLogFile(version_2, --counter)); // simulate snapshot file File snapFile = new File(version_2 + "/snapshot." + Long.toHexString(--counter)); assertTrue(snapFile.createNewFile(), "Failed to create snap File:" + snapFile.toString()); snaps.add(snapFile); } if (createPrecedingLogFile) { logs.add(createDataDirLogFile(version_2, --counter)); } } private void verifyFilesAfterPurge(List logs, boolean exists) { for (File file : logs) { assertEquals(exists, file.exists(), "After purging, file " + file); } } private List manyClientOps(final ZooKeeper zk, final CountDownLatch doPurge, int thCount, final String prefix) { Thread[] ths = new Thread[thCount]; final List znodes = Collections.synchronizedList(new ArrayList<>()); final CountDownLatch finished = new CountDownLatch(thCount); final AtomicReference exception = new AtomicReference<>(); for (int indx = 0; indx < thCount; indx++) { final String myprefix = prefix + "-" + indx; Thread th = new Thread(() -> { for (int i = 0; i < 750; i++) { try { String mynode = myprefix + "-" + i; znodes.add(mynode); zk.create(mynode, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (Exception e) { LOG.error("Unexpected exception during ZkClient ops", e); exception.set(e); } if (i == 200) { doPurge.countDown(); } } finished.countDown(); }); ths[indx] = th; } for (Thread thread : ths) { thread.start(); } try { boolean operationsFinishedSuccessfully = finished.await(OP_TIMEOUT_IN_MILLIS, TimeUnit.MILLISECONDS); if (exception.get() != null) { LOG.error("unexpected exception during running ZkClient ops:", exception.get()); fail("unexpected exception during running ZkClient ops, see in the logs above"); } assertTrue(operationsFinishedSuccessfully, "ZkClient ops not finished in time!"); } catch (InterruptedException ie) { LOG.error("Unexpected exception", ie); fail("Unexpected exception occurred!"); } return znodes; } private void makeValidSnapshot(File snapFile) throws IOException { SnapStream.setStreamMode(SnapStream.StreamMode.CHECKED); CheckedOutputStream os = SnapStream.getOutputStream(snapFile, true); OutputArchive oa = BinaryOutputArchive.getArchive(os); FileHeader header = new FileHeader(FileSnap.SNAP_MAGIC, 2, 1); header.serialize(oa, "fileheader"); SnapStream.sealStream(os, oa); os.flush(); os.close(); assertTrue(SnapStream.isValidSnapshot(snapFile)); } private void makeInvalidSnapshot(File snapFile) throws IOException { SnapStream.setStreamMode(SnapStream.StreamMode.CHECKED); OutputStream os = SnapStream.getOutputStream(snapFile, true); os.write(1); os.flush(); os.close(); assertFalse(SnapStream.isValidSnapshot(snapFile)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000175 15051152474 032676 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ReferenceCountedACLCacheTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ReferenceCountedAC0100644 0000000 0000000 00000025146 15051152474 034212 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.junit.jupiter.api.Test; public class ReferenceCountedACLCacheTest { @Test public void testSameACLGivesSameID() { List testACL = createACL("myid"); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); List testACL2 = createACL("myid"); assertEquals(aclId, cache.convertAcls(testACL2)); } @Test public void testWhetherOrderingMatters() { List testACL = new ArrayList<>(); testACL.add(new ACL(ZooDefs.Perms.READ, new Id("scheme", "ro"))); testACL.add(new ACL(ZooDefs.Perms.WRITE, new Id("scheme", "rw"))); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); List testACL2 = new ArrayList<>(); testACL2.add(new ACL(ZooDefs.Perms.WRITE, new Id("scheme", "rw"))); testACL2.add(new ACL(ZooDefs.Perms.READ, new Id("scheme", "ro"))); assertFalse(aclId.equals(cache.convertAcls(testACL2))); } @Test public void testBidirectionality() { List testACL = createACL("myid"); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); assertEquals(testACL, cache.convertLong(aclId)); } @Test public void testCacheSize() { List testACL = createACL("myid"); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); assertEquals(1, cache.size()); List testACL2 = createACL("myid"); assertEquals(aclId, cache.convertAcls(testACL2)); assertEquals(1, cache.size()); List testACL3 = createACL("differentId"); Long aclId3 = cache.convertAcls(testACL3); assertFalse(aclId3.equals(aclId)); assertEquals(2, cache.size()); } @Test public void testAddThenRemove() { List testACL = createACL("myid"); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); assertEquals(1, cache.size()); cache.removeUsage(aclId); assertEquals(0, cache.size()); } @Test public void testMultipleAddsAndRemove() { List testACL = createACL("myid"); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); assertEquals(1, cache.size()); cache.convertAcls(testACL); assertEquals(1, cache.size()); List testACL2 = createACL("anotherId"); cache.convertAcls(testACL2); cache.removeUsage(aclId); assertEquals(2, cache.size()); cache.removeUsage(aclId); assertEquals(1, cache.size()); Long newId = cache.convertAcls(testACL); assertFalse(aclId.equals(newId)); } @Test public void testAddUsage() { List testACL = createACL("myid"); ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); Long aclId = cache.convertAcls(testACL); assertEquals(1, cache.size()); cache.addUsage(aclId); assertEquals(1, cache.size()); cache.removeUsage(aclId); assertEquals(1, cache.size()); cache.removeUsage(aclId); assertEquals(0, cache.size()); } @Test public void testAddNonExistentUsage() { ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); cache.addUsage(1L); assertEquals(0, cache.size()); /* On startup, it's possible that we'll try calling addUsage of an ID not in the cache. This is safe to ignore as it'll be added later when we traverse the tranlog. See discussion here: http://mail-archives.apache.org/mod_mbox/zookeeper-user/201507.mbox/%3CCAB5oV2_ujhvBA1sEkCG2WRakPjCy%2BNR10620WK2G1GGgmEO44g%40mail.gmail.com%3E This test makes sure that we don't add the ID to the cache in this case as that would result in dupes later and consequently incorrect counts and entries that will never be cleaned out. */ } @Test public void testSerializeDeserialize() throws IOException { ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); List acl1 = createACL("one"); List acl2 = createACL("two"); List acl3 = createACL("three"); List acl4 = createACL("four"); List acl5 = createACL("five"); Long aclId1 = convertACLsNTimes(cache, acl1, 1); Long aclId2 = convertACLsNTimes(cache, acl2, 2); Long aclId3 = convertACLsNTimes(cache, acl3, 3); Long aclId4 = convertACLsNTimes(cache, acl4, 4); Long aclId5 = convertACLsNTimes(cache, acl5, 5); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive archive = BinaryOutputArchive.getArchive(baos); cache.serialize(archive); BinaryInputArchive inArchive = BinaryInputArchive.getArchive(new ByteArrayInputStream(baos.toByteArray())); ReferenceCountedACLCache deserializedCache = new ReferenceCountedACLCache(); deserializedCache.deserialize(inArchive); callAddUsageNTimes(deserializedCache, aclId1, 1); callAddUsageNTimes(deserializedCache, aclId2, 2); callAddUsageNTimes(deserializedCache, aclId3, 3); callAddUsageNTimes(deserializedCache, aclId4, 4); callAddUsageNTimes(deserializedCache, aclId5, 5); assertCachesEqual(cache, deserializedCache); } @Test public void testNPEInDeserialize() throws IOException { ReferenceCountedACLCache serializeCache = new ReferenceCountedACLCache() { @Override public synchronized void serialize(OutputArchive oa) throws IOException { oa.writeInt(1, "map"); oa.writeLong(1, "long"); oa.startVector(null, "acls"); oa.endVector(null, "acls"); } }; ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive archive = BinaryOutputArchive.getArchive(baos); serializeCache.serialize(archive); BinaryInputArchive inArchive = BinaryInputArchive.getArchive(new ByteArrayInputStream(baos.toByteArray())); ReferenceCountedACLCache deserializedCache = new ReferenceCountedACLCache(); try { deserializedCache.deserialize(inArchive); } catch (NullPointerException e) { fail("should not throw NPE while do deserialized"); } catch (RuntimeException e) { // do nothing. } } private void assertCachesEqual(ReferenceCountedACLCache expected, ReferenceCountedACLCache actual) { assertEquals(expected.aclIndex, actual.aclIndex); assertEquals(expected.aclKeyMap, actual.aclKeyMap); assertEquals(expected.longKeyMap, actual.longKeyMap); assertEquals(expected.referenceCounter, actual.referenceCounter); } @Test public void testPurgeUnused() throws IOException { ReferenceCountedACLCache cache = new ReferenceCountedACLCache(); List acl1 = createACL("one"); List acl2 = createACL("two"); List acl3 = createACL("three"); List acl4 = createACL("four"); List acl5 = createACL("five"); Long aclId1 = convertACLsNTimes(cache, acl1, 1); Long aclId2 = convertACLsNTimes(cache, acl2, 2); Long aclId3 = convertACLsNTimes(cache, acl3, 3); Long aclId4 = convertACLsNTimes(cache, acl4, 4); Long aclId5 = convertACLsNTimes(cache, acl5, 5); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive archive = BinaryOutputArchive.getArchive(baos); cache.serialize(archive); BinaryInputArchive inArchive = BinaryInputArchive.getArchive(new ByteArrayInputStream(baos.toByteArray())); ReferenceCountedACLCache deserializedCache = new ReferenceCountedACLCache(); deserializedCache.deserialize(inArchive); callAddUsageNTimes(deserializedCache, aclId1, 1); callAddUsageNTimes(deserializedCache, aclId2, 2); deserializedCache.purgeUnused(); assertEquals(2, deserializedCache.size()); assertEquals(aclId1, deserializedCache.convertAcls(acl1)); assertEquals(aclId2, deserializedCache.convertAcls(acl2)); assertFalse(acl3.equals(deserializedCache.convertAcls(acl3))); assertFalse(acl4.equals(deserializedCache.convertAcls(acl4))); assertFalse(acl5.equals(deserializedCache.convertAcls(acl5))); } private void callAddUsageNTimes(ReferenceCountedACLCache deserializedCache, Long aclId, int num) { for (int i = 0; i < num; i++) { deserializedCache.addUsage(aclId); } } private Long convertACLsNTimes(ReferenceCountedACLCache cache, List acl, int num) { if (num <= 0) { return -1L; } for (int i = 0; i < num - 1; i++) { cache.convertAcls(acl); } return cache.convertAcls(acl); } private List createACL(String id) { List acl1 = new ArrayList<>(); acl1.add(new ACL(ZooDefs.Perms.ADMIN, new Id("scheme", id))); return acl1; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Re0100644 0000000 0000000 00000000165 15051152474 032675 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/RequestThrottlerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/RequestThrottlerTe0100644 0000000 0000000 00000035712 15051152474 034417 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RequestThrottlerTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(RequestThrottlerTest.class); private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static String GLOBAL_OUTSTANDING_LIMIT = "1"; private static final int TOTAL_REQUESTS = 5; private static final int STALL_TIME = 5000; // latch to hold requests in the PrepRequestProcessor to // keep them from going down the pipeline to reach the final // request processor, where the number of in process requests // will be decreased CountDownLatch resumeProcess = null; // latch to make sure all requests are submitted CountDownLatch submitted = null; // latch to make sure all requests entered the pipeline CountDownLatch entered = null; // latch to make sure requests finished the pipeline CountDownLatch finished = null; CountDownLatch disconnected = null; CountDownLatch throttled = null; CountDownLatch throttling = null; ZooKeeperServer zks = null; ServerCnxnFactory f = null; ZooKeeper zk = null; int connectionLossCount = 0; @BeforeEach public void setup() throws Exception { // start a server and create a client File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); zks = new TestZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); resumeProcess = null; submitted = null; zk = ClientBase.createZKClient(HOSTPORT); } @AfterEach public void tearDown() throws Exception { // shut down the server and the client if (null != zk) { zk.close(); } if (null != f) { f.shutdown(); } if (null != zks) { zks.shutdown(); } } // TestZooKeeperServer // 1. uses our version of PrepRequestProcessor, which can hold the request as long as we want // 2. count the number of submitted requests class TestZooKeeperServer extends ZooKeeperServer { public TestZooKeeperServer(File snapDir, File logDir, int tickTime) throws IOException { super(snapDir, logDir, tickTime); } @Override protected RequestThrottler createRequestThrottler() { return new TestRequestThrottler(this); } @Override protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); RequestProcessor syncProcessor = new SyncRequestProcessor(this, finalProcessor); ((SyncRequestProcessor) syncProcessor).start(); firstProcessor = new TestPrepRequestProcessor(this, syncProcessor); ((TestPrepRequestProcessor) firstProcessor).start(); } @Override public void submitRequest(Request si) { if (null != submitted) { submitted.countDown(); } super.submitRequest(si); } @Override public void requestFinished(Request request) { if (null != finished){ finished.countDown(); } super.requestFinished(request); } } class TestRequestThrottler extends RequestThrottler { public TestRequestThrottler(ZooKeeperServer zks) { super(zks); } @Override synchronized void throttleSleep(int stallTime) throws InterruptedException { if (throttling != null) { throttling.countDown(); } super.throttleSleep(stallTime); // Defend against unstable timing and potential spurious wakeup. if (throttled != null) { assertTrue(throttled.await(20, TimeUnit.SECONDS)); } } } class TestPrepRequestProcessor extends PrepRequestProcessor { public TestPrepRequestProcessor(ZooKeeperServer zks, RequestProcessor syncProcessor) { super(zks, syncProcessor); } @Override protected void pRequest(Request request) throws RequestProcessorException { // keep the request in the processor as long as we want if (resumeProcess != null) { try { resumeProcess.await(20, TimeUnit.SECONDS); } catch (Exception e) { } } if (entered != null) { entered.countDown(); } super.pRequest(request); } } @Test public void testRequestThrottler() throws Exception { ServerMetrics.getMetrics().resetAll(); // we only allow two requests in the pipeline RequestThrottler.setMaxRequests(2); RequestThrottler.setStallTime(STALL_TIME); RequestThrottler.setDropStaleRequests(false); // no requests can go through the pipeline unless we raise the latch resumeProcess = new CountDownLatch(1); submitted = new CountDownLatch(TOTAL_REQUESTS); entered = new CountDownLatch(TOTAL_REQUESTS); // send 5 requests asynchronously for (int i = 0; i < TOTAL_REQUESTS; i++) { zk.create("/request_throttle_test- " + i, ("/request_throttle_test- " + i).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (rc, path, ctx, name) -> { }, null); } // make sure the server received all 5 requests submitted.await(5, TimeUnit.SECONDS); // but only two requests can get into the pipeline because of the throttler waitForMetric("prep_processor_request_queued", is(2L)); waitForMetric("request_throttle_wait_count", greaterThanOrEqualTo(1L)); // let the requests go through the pipeline and the throttler will be waken up to allow more requests // to enter the pipeline resumeProcess.countDown(); // wait for more than one STALL_TIME to reduce timeout before wakeup assertTrue(entered.await(STALL_TIME + 5000, TimeUnit.MILLISECONDS)); Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(TOTAL_REQUESTS, (long) metrics.get("prep_processor_request_queued")); } @Test public void testDropStaleRequests() throws Exception { ServerMetrics.getMetrics().resetAll(); // we only allow two requests in the pipeline RequestThrottler.setMaxRequests(2); RequestThrottler.setStallTime(STALL_TIME); RequestThrottler.setDropStaleRequests(true); // no requests can go through the pipeline unless we raise the latch resumeProcess = new CountDownLatch(1); submitted = new CountDownLatch(TOTAL_REQUESTS); throttled = new CountDownLatch(1); throttling = new CountDownLatch(1); // send 5 requests asynchronously for (int i = 0; i < TOTAL_REQUESTS; i++) { zk.create("/request_throttle_test- " + i, ("/request_throttle_test- " + i).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (rc, path, ctx, name) -> { }, null); } // make sure the server received all 5 requests assertTrue(submitted.await(5, TimeUnit.SECONDS)); // stale throttled requests assertTrue(throttling.await(5, TimeUnit.SECONDS)); for (ServerCnxn cnxn : f.cnxns) { cnxn.setStale(); } throttled.countDown(); zk = null; // only first three requests are counted as finished finished = new CountDownLatch(3); // let the requests go through the pipeline resumeProcess.countDown(); LOG.info("raise the latch"); while (zks.getInflight() > 0) { Thread.sleep(50); } assertTrue(finished.await(5, TimeUnit.SECONDS)); // assert after all requests processed to avoid concurrent issues as metrics are // counted in different threads. Map metrics = MetricsUtils.currentServerMetrics(); // only two requests can get into the pipeline because of the throttler assertEquals(2L, (long) metrics.get("prep_processor_request_queued")); // the rest of the 3 requests will be dropped // but only the first one for a connection will be counted assertEquals(1L, (long) metrics.get("request_throttle_wait_count")); assertEquals(1, (long) metrics.get("stale_requests_dropped")); } @Test public void testLargeRequestThrottling() throws Exception { ServerMetrics.getMetrics().resetAll(); AsyncCallback.StringCallback createCallback = (rc, path, ctx, name) -> { if (KeeperException.Code.get(rc) == KeeperException.Code.CONNECTIONLOSS) { connectionLossCount++; disconnected.countDown(); } }; // the total length of the request is about 170-180 bytes, so only two requests are allowed byte[] data = new byte[100]; // the third request will incur throttle. We don't send more requests to avoid reconnecting // due to unstable test environment(e.g. slow sending). int number_requests = 3; // we allow more requests in the pipeline RequestThrottler.setMaxRequests(number_requests + 2); // request could become stale in processor threads due to throttle in io thread RequestThrottler.setDropStaleRequests(false); // enable large request throttling zks.setLargeRequestThreshold(150); zks.setLargeRequestMaxBytes(400); // no requests can go through the pipeline unless we raise the latch resumeProcess = new CountDownLatch(1); // the connection will be close when large requests exceed the limit // we can't use the submitted latch because requests after close won't be submitted disconnected = new CountDownLatch(number_requests); // send requests asynchronously for (int i = 0; i < number_requests; i++) { zk.create("/request_throttle_test- " + i , data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, createCallback, null); } // make sure the server received all requests assertTrue(disconnected.await(30, TimeUnit.SECONDS)); finished = new CountDownLatch(2); // let the requests go through the pipeline resumeProcess.countDown(); assertTrue(finished.await(5, TimeUnit.SECONDS)); // assert metrics after finished so metrics in no io threads are set also. Map metrics = MetricsUtils.currentServerMetrics(); // but only two requests can get into the pipeline because they are large requests // the connection will be closed assertEquals(2L, (long) metrics.get("prep_processor_request_queued")); assertEquals(1L, (long) metrics.get("large_requests_rejected")); assertEquals(number_requests, connectionLossCount); // when the two requests finish, they are stale because the connection is closed already assertEquals(2, (long) metrics.get("stale_replies")); } @Test public void testGlobalOutstandingRequestThrottlingWithRequestThrottlerDisabled() throws Exception { try { System.setProperty(ZooKeeperServer.GLOBAL_OUTSTANDING_LIMIT, GLOBAL_OUTSTANDING_LIMIT); ServerMetrics.getMetrics().resetAll(); // Here we disable RequestThrottler and let incoming requests queued at first request processor. RequestThrottler.setMaxRequests(0); resumeProcess = new CountDownLatch(1); int totalRequests = 10; for (int i = 0; i < totalRequests; i++) { zk.create("/request_throttle_test- " + i, ("/request_throttle_test- " + i).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (rc, path, ctx, name) -> { }, null); } // We should start throttling instead of queuing more requests. // // We always allow up to GLOBAL_OUTSTANDING_LIMIT + 1 number of requests coming in request processing pipeline // before throttling. For the next request, we will throttle by disabling receiving future requests but we still // allow this single request coming in. Ideally, the total number of queued requests in processing pipeline would // be GLOBAL_OUTSTANDING_LIMIT + 2. // // But due to leak of consistent view of number of outstanding requests, the number could be larger. waitForMetric("prep_processor_request_queued", greaterThanOrEqualTo(Long.parseLong(GLOBAL_OUTSTANDING_LIMIT) + 2)); resumeProcess.countDown(); } catch (Exception e) { throw e; } finally { System.clearProperty(ZooKeeperServer.GLOBAL_OUTSTANDING_LIMIT); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000166 15051152474 032677 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/SerializationPerfTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/SerializationPerfT0100644 0000000 0000000 00000010763 15051152474 034343 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.IOException; import java.io.OutputStream; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SerializationPerfTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(SerializationPerfTest.class); private static class NullOutputStream extends OutputStream { public void write(int b) { // do nothing - exclude persistence from perf } } static int createNodes(DataTree tree, String path, int depth, int childcount, int parentCVersion, byte[] data) throws KeeperException.NodeExistsException, KeeperException.NoNodeException { path += "node" + depth; tree.createNode(path, data, null, -1, ++parentCVersion, 1, 1); if (--depth == 0) { return 1; } path += "/"; int count = 1; for (int i = 0; i < childcount; i++) { count += createNodes(tree, path + i, depth, childcount, 1, data); } return count; } private static void serializeTree(int depth, int width, int len) throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { DataTree tree = new DataTree(); createNodes(tree, "/", depth, width, tree.getNode("/").stat.getCversion(), new byte[len]); int count = tree.getNodeCount(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(new NullOutputStream()); System.gc(); long start = System.nanoTime(); tree.serialize(oa, "test"); long end = System.nanoTime(); long durationms = (end - start) / 1000000L; long pernodeus = ((end - start) / 1000L) / count; LOG.info( "Serialized {} nodes in {} ms ({}us/node), depth={} width={} datalen={}", count, durationms, pernodeus, depth, width, len); } @Test public void testSingleSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(1, 0, 20); } @Test public void testWideSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(2, 10000, 20); } @Test public void testDeepSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(400, 1, 20); } @Test public void test10Wide5DeepSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(5, 10, 20); } @Test public void test15Wide5DeepSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(5, 15, 20); } @Test public void test25Wide4DeepSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(4, 25, 20); } @Test public void test40Wide4DeepSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(4, 40, 20); } @Test public void test300Wide3DeepSerialize() throws InterruptedException, IOException, KeeperException.NodeExistsException, KeeperException.NoNodeException { serializeTree(3, 300, 20); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerIdTest.java0100644 0000000 0000000 00000007642 15051152474 034072 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class ServerIdTest extends ClientBase { public static Stream data() throws Exception { List testTypes = new ArrayList<>(); for (boolean ttlsEnabled : new boolean[]{true, false}) { for (int serverId = 0; serverId <= 255; ++serverId) { testTypes.add(Arguments.of(ttlsEnabled, serverId)); } } return testTypes.stream(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); System.clearProperty("zookeeper.extendedTypesEnabled"); } @BeforeEach @Override public void setUp() throws Exception { //since parameterized test methods need a parameterized setUp method //the inherited method has to be overridden with an empty function body } public void setUp(boolean ttlsEnabled, int serverId) throws Exception { System.setProperty("zookeeper.extendedTypesEnabled", Boolean.toString(ttlsEnabled)); LOG.info("ttlsEnabled: {} - ServerId: {}", ttlsEnabled, serverId); try { super.setUpWithServerId(serverId); } catch (RuntimeException e) { if (ttlsEnabled && (serverId >= EphemeralType.MAX_EXTENDED_SERVER_ID)) { return; // expected } throw e; } } @ParameterizedTest @MethodSource("data") public void doTest(boolean ttlsEnabled, int serverId) throws Exception { setUp(ttlsEnabled, serverId); if (ttlsEnabled && (serverId >= EphemeralType.MAX_EXTENDED_SERVER_ID)) { return; } TestableZooKeeper zk = null; try { zk = createClient(); zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/foo", -1); if (ttlsEnabled) { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 1000); // should work } else { try { zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 1000); fail("Should have thrown KeeperException.UnimplementedException"); } catch (KeeperException.UnimplementedException e) { // expected } } } finally { if (zk != null) { zk.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000162 15051152474 032673 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerMetricsTest.0100644 0000000 0000000 00000007770 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.metric.AvgMinMaxCounter; import org.apache.zookeeper.server.metric.SimpleCounter; import org.junit.jupiter.api.Test; public class ServerMetricsTest extends ZKTestCase { private static final int RANDOM_TRIALS = 100; private static final int RANDOM_SIZE = 100; private long[] generateRandomValues(int size) { // Clamp range to prevent overflow in metric aggregation final long[] values = new long[size]; if (size == 0) { return values; } final long rangeMin = Long.MIN_VALUE / size; final long rangeMax = Long.MAX_VALUE / size; for (int i = 0; i < size; ++i) { values[i] = ThreadLocalRandom.current().nextLong(rangeMin, rangeMax); } return values; } @Test public void testAvgMinMaxCounter() { final AvgMinMaxCounter metric = new AvgMinMaxCounter("test"); testAvgMinMaxCounter(metric, 0); testAvgMinMaxCounter(metric, 1); for (int i = 0; i < RANDOM_TRIALS; ++i) { testAvgMinMaxCounter(metric, RANDOM_SIZE); } } private void testAvgMinMaxCounter(AvgMinMaxCounter metric, int size) { final long[] values = generateRandomValues(size); for (long value : values) { metric.add(value); } long expectedMin = Arrays.stream(values).min().orElse(0); long expectedMax = Arrays.stream(values).max().orElse(0); long expectedSum = Arrays.stream(values).sum(); long expectedCnt = values.length; double expectedAvg = expectedSum / Math.max(1, expectedCnt); assertEquals(expectedAvg, metric.getAvg(), 200); assertEquals(expectedMin, metric.getMin()); assertEquals(expectedMax, metric.getMax()); assertEquals(expectedCnt, metric.getCount()); assertEquals(expectedSum, metric.getTotal()); final Map results = metric.values(); assertEquals(expectedMax, (long) results.get("max_test")); assertEquals(expectedMin, (long) results.get("min_test")); assertEquals(expectedCnt, (long) results.get("cnt_test")); assertEquals(expectedAvg, (double) results.get("avg_test"), 200); metric.reset(); } @Test public void testSimpleCounter() { SimpleCounter metric = new SimpleCounter("test"); testSimpleCounter(metric, 0); testSimpleCounter(metric, 1); for (int i = 0; i < RANDOM_TRIALS; ++i) { testSimpleCounter(metric, RANDOM_SIZE); } } private void testSimpleCounter(SimpleCounter metric, int size) { final long[] values = generateRandomValues(size); for (long value : values) { metric.add(value); } long expectedCount = Arrays.stream(values).sum(); assertEquals(expectedCount, metric.get()); final Map results = metric.values(); assertEquals(expectedCount, (long) results.get("test")); metric.reset(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000160 15051152474 032671 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerStatsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ServerStatsTest.ja0100644 0000000 0000000 00000011147 15051152474 034300 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ServerStatsTest extends ZKTestCase { private ServerStats.Provider providerMock; @BeforeEach public void setUp() { providerMock = mock(ServerStats.Provider.class); } @Test public void testPacketsMetrics() { // Given ... ServerStats serverStats = new ServerStats(providerMock); int incrementCount = 20; // When increment ... for (int i = 0; i < incrementCount; i++) { serverStats.incrementPacketsSent(); serverStats.incrementPacketsReceived(); serverStats.incrementPacketsReceived(); } // Then ... assertEquals(incrementCount, serverStats.getPacketsSent()); assertEquals(incrementCount * 2, serverStats.getPacketsReceived()); // When reset ... serverStats.resetRequestCounters(); // Then ... assertAllPacketsZero(serverStats); } @Test public void testLatencyMetrics() { // Given ... ServerStats serverStats = new ServerStats(providerMock); // When incremented... Request fakeRequest = new Request(0, 0, 0, null, null, 0); serverStats.updateLatency(fakeRequest, fakeRequest.createTime + 1000); serverStats.updateLatency(fakeRequest, fakeRequest.createTime + 2000); // Then ... assertThat("Max latency check", 2000L, lessThanOrEqualTo(serverStats.getMaxLatency())); assertThat("Min latency check", 1000L, lessThanOrEqualTo(serverStats.getMinLatency())); assertEquals(1500, serverStats.getAvgLatency(), 200); // When reset... serverStats.resetLatency(); // Then ... assertAllLatencyZero(serverStats); } @Test public void testFsyncThresholdExceedMetrics() { // Given ... ServerStats serverStats = new ServerStats(providerMock); int incrementCount = 30; // When increment ... for (int i = 0; i < incrementCount; i++) { serverStats.incrementFsyncThresholdExceedCount(); } // Then ... assertEquals(incrementCount, serverStats.getFsyncThresholdExceedCount()); // When reset ... serverStats.resetFsyncThresholdExceedCount(); // Then ... assertFsyncThresholdExceedCountZero(serverStats); } @Test public void testReset() { // Given ... ServerStats serverStats = new ServerStats(providerMock); assertAllPacketsZero(serverStats); assertAllLatencyZero(serverStats); // When ... Request fakeRequest = new Request(0, 0, 0, null, null, 0); serverStats.incrementPacketsSent(); serverStats.incrementPacketsReceived(); serverStats.updateLatency(fakeRequest, fakeRequest.createTime + 1000); serverStats.reset(); // Then ... assertAllPacketsZero(serverStats); assertAllLatencyZero(serverStats); } private void assertAllPacketsZero(ServerStats serverStats) { assertEquals(0L, serverStats.getPacketsSent()); assertEquals(0L, serverStats.getPacketsReceived()); } private void assertAllLatencyZero(ServerStats serverStats) { assertEquals(0L, serverStats.getMaxLatency()); assertEquals(0L, serverStats.getMinLatency()); assertEquals(0, serverStats.getAvgLatency(), 0.00001); } private void assertFsyncThresholdExceedCountZero(ServerStats serverStats) { assertEquals(0L, serverStats.getFsyncThresholdExceedCount()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Se0100644 0000000 0000000 00000000163 15051152474 032674 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/SessionTrackerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/SessionTrackerTest0100644 0000000 0000000 00000014314 15051152474 034360 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.server.SessionTrackerImpl.SessionImpl; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** * Testing zk client session logic in sessiontracker */ public class SessionTrackerTest extends ZKTestCase { private final long sessionId = 339900; private final int sessionTimeout = 3000; private FirstProcessor firstProcessor; private CountDownLatch latch; /** * Verify the create session call in the Leader.FinalRequestProcessor after * the session expiration. */ @Test @Timeout(value = 20) public void testAddSessionAfterSessionExpiry() throws Exception { RequestThrottler.setMaxRequests(0); ZooKeeperServer zks = setupSessionTracker(); latch = new CountDownLatch(1); zks.sessionTracker.trackSession(sessionId, sessionTimeout); SessionTrackerImpl sessionTrackerImpl = (SessionTrackerImpl) zks.sessionTracker; SessionImpl sessionImpl = sessionTrackerImpl.sessionsById.get(sessionId); assertNotNull(sessionImpl, "Sessionid:" + sessionId + " doesn't exists in sessiontracker"); // verify the session existence Object sessionOwner = new Object(); sessionTrackerImpl.checkSession(sessionId, sessionOwner); // waiting for the session expiry latch.await(sessionTimeout * 2, TimeUnit.MILLISECONDS); // Simulating FinalRequestProcessor logic: create session request has // delayed and now reaches FinalRequestProcessor. Here the leader zk // will do sessionTracker.addSession(id, timeout) sessionTrackerImpl.trackSession(sessionId, sessionTimeout); try { sessionTrackerImpl.checkSession(sessionId, sessionOwner); fail("Should throw session expiry exception " + "as the session has expired and closed"); } catch (KeeperException.SessionExpiredException e) { // expected behaviour } assertTrue(sessionImpl.isClosing(), "Session didn't expired"); assertFalse(sessionTrackerImpl.touchSession(sessionId, sessionTimeout), "Session didn't expired"); assertEquals(1, firstProcessor.getCountOfCloseSessionReq(), "Duplicate session expiry request has been generated"); } /** * Verify the session closure request has reached PrepRequestProcessor soon * after session expiration by the session tracker */ @Test @Timeout(value = 20) public void testCloseSessionRequestAfterSessionExpiry() throws Exception { ZooKeeperServer zks = setupSessionTracker(); latch = new CountDownLatch(1); zks.sessionTracker.trackSession(sessionId, sessionTimeout); SessionTrackerImpl sessionTrackerImpl = (SessionTrackerImpl) zks.sessionTracker; SessionImpl sessionImpl = sessionTrackerImpl.sessionsById.get(sessionId); assertNotNull(sessionImpl, "Sessionid:" + sessionId + " doesn't exists in sessiontracker"); // verify the session existence Object sessionOwner = new Object(); sessionTrackerImpl.checkSession(sessionId, sessionOwner); // waiting for the session expiry latch.await(sessionTimeout * 2, TimeUnit.MILLISECONDS); // Simulating close session request: removeSession() will be executed // while OpCode.closeSession sessionTrackerImpl.removeSession(sessionId); SessionImpl actualSession = sessionTrackerImpl.sessionsById.get(sessionId); assertNull(actualSession, "Session:" + sessionId + " still exists after removal"); } private ZooKeeperServer setupSessionTracker() throws IOException { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); zks.setupRequestProcessors(); firstProcessor = new FirstProcessor(zks, null); zks.firstProcessor = firstProcessor; // setup session tracker zks.createSessionTracker(); zks.startSessionTracker(); zks.startRequestThrottler(); return zks; } // Mock processor used in zookeeper server private class FirstProcessor extends PrepRequestProcessor { private volatile int countOfCloseSessionReq = 0; public FirstProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { super(zks, nextProcessor); } @Override public void processRequest(Request request) { // check session close request if (request.type == OpCode.closeSession) { countOfCloseSessionReq++; latch.countDown(); } } // return number of session expiry calls int getCountOfCloseSessionReq() { return countOfCloseSessionReq; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Sn0100644 0000000 0000000 00000000163 15051152474 032705 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/SnapshotDigestTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/SnapshotDigestTest0100644 0000000 0000000 00000016441 15051152474 034363 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.server.metric.SimpleCounter; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeerMainTest; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SnapshotDigestTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(SnapshotDigestTest.class); private ZooKeeper zk; private ZooKeeperServer server; @BeforeEach public void setUp() throws Exception { super.setUp(); server = serverFactory.getZooKeeperServer(); zk = createClient(); } @AfterEach public void tearDown() throws Exception { // server will be closed in super.tearDown super.tearDown(); if (zk != null) { zk.close(); } } @Override public void setupCustomizedEnv() { ZooKeeperServer.setDigestEnabled(true); System.setProperty(ZooKeeperServer.SNAP_COUNT, "100"); } @Override public void cleanUpCustomizedEnv() { ZooKeeperServer.setDigestEnabled(false); System.clearProperty(ZooKeeperServer.SNAP_COUNT); } /** * Check snapshot digests when loading a fuzzy or non-fuzzy snapshot. */ @Test public void testSnapshotDigest() throws Exception { // take a empty snapshot without creating any txn and make sure // there is no digest mismatch issue server.takeSnapshot(); reloadSnapshotAndCheckDigest(); // trigger various write requests String pathPrefix = "/testSnapshotDigest"; for (int i = 0; i < 1000; i++) { String path = pathPrefix + i; zk.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // update the data of first node String firstNode = pathPrefix + 0; zk.setData(firstNode, "new_setdata".getBytes(), -1); // delete the first node zk.delete(firstNode, -1); // trigger multi op List subTxns = new ArrayList<>(); for (int i = 0; i < 3; i++) { String path = pathPrefix + "-m" + i; subTxns.add(Op.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); } zk.multi(subTxns); reloadSnapshotAndCheckDigest(); // Take a snapshot and test the logic when loading a non-fuzzy snapshot server = serverFactory.getZooKeeperServer(); server.takeSnapshot(); reloadSnapshotAndCheckDigest(); } /** * Make sure the code will skip digest check when it's comparing * digest with different version. * * This enables us to smoonthly add new fields into digest or using * new digest calculation. */ @Test public void testDifferentDigestVersion() throws Exception { // check the current digest version int currentVersion = new DigestCalculator().getDigestVersion(); // create a node String path = "/testDifferentDigestVersion"; zk.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // take a full snapshot server.takeSnapshot(); //increment the digest version int newVersion = currentVersion + 1; DigestCalculator newVersionDigestCalculator = Mockito.spy(DigestCalculator.class); Mockito.when(newVersionDigestCalculator.getDigestVersion()).thenReturn(newVersion); assertEquals(newVersion, newVersionDigestCalculator.getDigestVersion()); // using mock to return different digest value when the way we // calculate digest changed FileTxnSnapLog txnSnapLog = new FileTxnSnapLog(tmpDir, tmpDir); DataTree dataTree = Mockito.spy(new DataTree(newVersionDigestCalculator)); Mockito.when(dataTree.getTreeDigest()).thenReturn(0L); txnSnapLog.restore(dataTree, new ConcurrentHashMap<>(), Mockito.mock(FileTxnSnapLog.PlayBackListener.class)); // make sure the reportDigestMismatch function is never called Mockito.verify(dataTree, Mockito.never()).reportDigestMismatch(Mockito.anyLong()); } /** * Make sure it's backward compatible, and also we can rollback this * feature without corrupt the database. */ @Test public void testBackwardCompatible() throws Exception { testCompatibleHelper(false, true); testCompatibleHelper(true, false); } private void testCompatibleHelper(Boolean enabledBefore, Boolean enabledAfter) throws Exception { ZooKeeperServer.setDigestEnabled(enabledBefore); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(enabledBefore); // restart the server to cache the option change reloadSnapshotAndCheckDigest(); // create a node String path = "/testCompatible" + "-" + enabledBefore + "-" + enabledAfter; zk.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // take a full snapshot server.takeSnapshot(); ZooKeeperServer.setDigestEnabled(enabledAfter); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(enabledAfter); reloadSnapshotAndCheckDigest(); assertEquals(path, new String(zk.getData(path, false, null))); } private void reloadSnapshotAndCheckDigest() throws Exception { stopServer(); QuorumPeerMainTest.waitForOne(zk, States.CONNECTING); ((SimpleCounter) ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT).reset(); startServer(); QuorumPeerMainTest.waitForOne(zk, States.CONNECTED); server = serverFactory.getZooKeeperServer(); // Snapshot digests always match assertEquals(0L, ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT.get()); // reset the digestFromLoadedSnapshot after comparing assertNull(server.getZKDatabase().getDataTree().getDigestFromLoadedSnapshot()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ToStringTest.java0100644 0000000 0000000 00000004000 15051152474 034101 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import java.lang.reflect.Field; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.proto.SetDataRequest; import org.junit.jupiter.api.Test; /** * A misc place to verify toString methods - mainly to make sure they don't * fail. */ public class ToStringTest extends ZKTestCase { /** Verify jute - which we've had particular problems with in the past * wrt null fields */ @Test public void testJuteToString() { SetDataRequest req = new SetDataRequest(null, null, 0); assertNotSame("ERROR", req.toString()); } @Test public void testOpCodeToString() throws Exception { Class clazz = ZooDefs.OpCode.class; Field[] fields = clazz.getFields(); assertNotEquals(0, fields.length); for (Field field : fields) { int opCode = field.getInt(null); String opString = Request.op2String(opCode); assertEquals(field.getName(), opString); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Tx0100644 0000000 0000000 00000000160 15051152474 032715 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/TxnLogCountTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/TxnLogCountTest.ja0100644 0000000 0000000 00000005536 15051152474 034244 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import java.io.File; import java.io.IOException; import org.apache.jute.OutputArchive; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.TxnHeader; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class TxnLogCountTest { /** * Test ZkDatabase's txnCount */ @Test public void testTxnLogCount() throws IOException { File tmpDir = ClientBase.createTmpDir(); FileTxnSnapLog snapLog = new FileTxnSnapLog(tmpDir, tmpDir); ZKDatabase zkDatabase = new ZKDatabase(snapLog); int txnRequestCnt = 10; int nonTxnRequestCnt = 10; for (int i = 0; i < txnRequestCnt && zkDatabase.append(mockTxnRequest()); i++) {} assertEquals(txnRequestCnt, zkDatabase.getTxnCount()); for (int i = 0; i < nonTxnRequestCnt && !zkDatabase.append(mockNonTxnRequest()); i++) {} assertEquals(txnRequestCnt, zkDatabase.getTxnCount()); } private Request mockTxnRequest() throws IOException { TxnHeader header = mock(TxnHeader.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputArchive oa = (OutputArchive) args[0]; oa.writeString("header", "test"); return null; } }).when(header).serialize(any(OutputArchive.class), anyString()); Request request = new Request(1, 2, 3, header, null, 4); return request; } private Request mockNonTxnRequest() { Request request = new Request(0, 0, 0, null, null, 0); return request; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Tx0100644 0000000 0000000 00000000161 15051152474 032716 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/TxnLogDigestTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/TxnLogDigestTest.j0100644 0000000 0000000 00000023073 15051152474 034226 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import mockit.Invocation; import mockit.Mock; import mockit.MockUp; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Op; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.server.metric.SimpleCounter; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.server.quorum.QuorumPeerMainTest; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TxnLogDigestTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(TxnLogDigestTest.class); private ZooKeeper zk; private ZooKeeperServer server; @BeforeEach public void setUp() throws Exception { System.setProperty("zookeeper.test.allowDiscontinuousProposals", "true"); super.setUp(); server = serverFactory.getZooKeeperServer(); zk = createClient(); } @AfterEach public void tearDown() throws Exception { System.clearProperty("zookeeper.test.allowDiscontinuousProposals"); // server will be closed in super.tearDown super.tearDown(); if (zk != null) { zk.close(); } MockedFileTxnLog.reset(); } @Override public void setupCustomizedEnv() { ZooKeeperServer.setDigestEnabled(true); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(true); } @Override public void cleanUpCustomizedEnv() { ZooKeeperServer.setDigestEnabled(false); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(false); } @BeforeAll public static void applyMockUps() { new MockedFileTxnLog(); } /** * Check that the digest stored in the txn matches the digest calculated * from DataTree. */ @Test public void digestFromTxnLogsMatchesTree() throws Exception { // reset the mismatch metrics SimpleCounter digestMistachesCount = (SimpleCounter) ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT; digestMistachesCount.reset(); // trigger some write ops performOperations(createClient(), "/digestFromTxnLogsMatchesTree"); // make sure there is no digest mismatch assertEquals(0, digestMistachesCount.get()); // verify that the digest is wrote to disk with txn TxnDigest lastDigest = getLastTxnLogDigest(); assertNotNull(lastDigest); assertEquals(server.getZKDatabase().getDataTree().getTreeDigest(), lastDigest.getTreeDigest()); } /** * Test the compatible when enable/disable digest: * * * check that txns which were written with digest can be read when * digest is disabled * * check that txns which were written without digest can be read * when digest is enabled. */ @Test public void checkTxnCompatibleWithAndWithoutDigest() throws Exception { // 1. start server with digest disabled restartServerWithDigestFlag(false); // trigger some write ops Map expectedNodes = performOperations(createClient(), "/p1"); // reset the mismatch metrics SimpleCounter digestMistachesCount = (SimpleCounter) ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT; digestMistachesCount.reset(); // 2. restart server with digest enabled restartServerWithDigestFlag(true); // make sure the data wrote when digest was disabled can be // successfully read checkNodes(expectedNodes); Map expectedNodes1 = performOperations(createClient(), "/p2"); // make sure there is no digest mismatch assertEquals(0, digestMistachesCount.get()); // 3. disable the digest again and make sure everything is fine restartServerWithDigestFlag(false); checkNodes(expectedNodes); checkNodes(expectedNodes1); } /** * Simulate the scenario where txn is missing, and make sure the * digest code can catch this issue. */ @Test public void testTxnMissing() throws Exception { // updated MockedFileTxnLog to skip append txn on specific txn MockedFileTxnLog.skipAppendZxid = 3; // trigger some write operations performOperations(createClient(), "/testTxnMissing"); // restart server to load the corrupted txn file SimpleCounter digestMistachesCount = (SimpleCounter) ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT; digestMistachesCount.reset(); restartServerWithDigestFlag(true); // check that digest mismatch is reported assertThat("mismtach should be reported", digestMistachesCount.get(), greaterThan(0L)); // restart server with digest disabled digestMistachesCount.reset(); restartServerWithDigestFlag(false); // check that no digest mismatch is reported assertEquals(0, digestMistachesCount.get()); } private void restartServerWithDigestFlag(boolean digestEnabled) throws Exception { stopServer(); QuorumPeerMainTest.waitForOne(zk, States.CONNECTING); ZooKeeperServer.setDigestEnabled(digestEnabled); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(digestEnabled); startServer(); QuorumPeerMainTest.waitForOne(zk, States.CONNECTED); } private TxnDigest getLastTxnLogDigest() throws IOException { TxnIterator itr = new FileTxnLog(new File(tmpDir, "version-2")).read(1); TxnDigest lastDigest = null; while (itr.next()) { lastDigest = itr.getDigest(); } return lastDigest; } public static void create(ZooKeeper client, String path, CreateMode mode) throws Exception { client.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, mode); } /** * Helper method to trigger various write ops inside ZK. */ public static Map performOperations( ZooKeeper client, String prefix) throws Exception { Map nodes = new HashMap<>(); String path = prefix; create(client, path, CreateMode.PERSISTENT); nodes.put(path, path); path = prefix + "/child1"; create(client, path, CreateMode.PERSISTENT); nodes.put(path, path); path = prefix + "/child2"; create(client, path, CreateMode.PERSISTENT); client.delete(prefix + "/child2", -1); path = prefix + "/child1/leaf"; create(client, path, CreateMode.PERSISTENT); String updatedData = "updated data"; client.setData(path, updatedData.getBytes(), -1); nodes.put(path, updatedData); List subTxns = new ArrayList<>(); for (int i = 0; i < 3; i++) { path = prefix + "/m" + i; subTxns.add(Op.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); nodes.put(path, path); } client.multi(subTxns); client.close(); return nodes; } private void checkNodes(Map expectedNodes) throws Exception { ZooKeeper client = createClient(); try { for (Map.Entry entry: expectedNodes.entrySet()) { assertEquals(entry.getValue(), new String(client.getData(entry.getKey(), false, null))); } } finally { client.close(); } } public static final class MockedFileTxnLog extends MockUp { static long skipAppendZxid = -1; @Mock public synchronized boolean append(Invocation invocation, Request request) throws IOException { TxnHeader hdr = request.getHdr(); if (hdr != null && hdr.getZxid() == skipAppendZxid) { LOG.info("skipping txn {}", skipAppendZxid); return true; } return invocation.proceed(request); } public static void reset() { skipAppendZxid = -1; } }; } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_X50100644 0000000 0000000 00000000164 15051152474 032622 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/X509AuthFailureTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/X509AuthFailureTes0100644 0000000 0000000 00000010704 15051152474 034033 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class X509AuthFailureTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(X509AuthFailureTest.class); private static ClientX509Util clientX509Util; public static final int TIMEOUT = 5000; public static int CONNECTION_TIMEOUT = 30000; @BeforeEach public void setup() throws Exception{ clientX509Util = new ClientX509Util(); String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); System.setProperty(clientX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(clientX509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty("zookeeper.admin.serverPort", "" + PortAssignment.unique()); } @AfterEach public void teardown() throws Exception { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ZKClientConfig.SECURE_CLIENT); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.clearProperty("zookeeper.admin.serverPort"); clientX509Util.close(); } /** * Developers might use standalone mode (which is the default for one server). * This test checks metrics for authz failure in standalone server */ @Test public void testSecureStandaloneServerAuthNFailure() throws Exception { final Integer CLIENT_PORT = PortAssignment.unique(); final Integer SECURE_CLIENT_PORT = PortAssignment.unique(); ZooKeeperServerMainTest.MainThread mt = new ZooKeeperServerMainTest.MainThread(CLIENT_PORT, SECURE_CLIENT_PORT, true, null); mt.start(); assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT)); try { ZooKeeper zk = createZKClnt("127.0.0.1:" + SECURE_CLIENT_PORT); fail("should not be reached"); } catch (Exception e){ //Expected } ServerStats serverStats = mt.getSecureCnxnFactory().getZooKeeperServer().serverStats(); assertTrue(serverStats.getAuthFailedCount() >= 1); mt.shutdown(); } private ZooKeeper createZKClnt(String cxnString) throws Exception { ClientBase.CountdownWatcher watcher = new ClientBase.CountdownWatcher(); ZooKeeper zk = new ZooKeeper(cxnString, TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); return zk; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000203 15051152474 032710 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperCriticalThreadMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperCriticalT0100644 0000000 0000000 00000005764 15051152474 034274 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.metrics.MetricsUtils; import org.junit.jupiter.api.Test; public class ZooKeeperCriticalThreadMetricsTest extends ZKTestCase { CountDownLatch processed; private class MyRequestProcessor implements RequestProcessor { @Override public void processRequest(Request request) throws RequestProcessorException { // use this dummy request processor to trigger a unrecoverable ex throw new RequestProcessorException("test", new Exception()); } @Override public void shutdown() { } } private class MyPrepRequestProcessor extends PrepRequestProcessor { public MyPrepRequestProcessor() { super(new ZooKeeperServer(), new MyRequestProcessor()); } @Override public void run() { super.run(); processed.countDown(); } } @Test public void testUnrecoverableErrorCountFromRequestProcessor() throws Exception { ServerMetrics.getMetrics().resetAll(); processed = new CountDownLatch(1); PrepRequestProcessor processor = new MyPrepRequestProcessor(); processor.start(); processor.processRequest(new Request(null, 1L, 1, ZooDefs.OpCode.setData, RequestRecord.fromBytes(new byte[10]), null)); processed.await(); processor.shutdown(); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("unrecoverable_error_count")); } @Test public void testUnrecoverableErrorCount() { ServerMetrics.getMetrics().resetAll(); ZooKeeperServer zks = new ZooKeeperServer(); ZooKeeperCriticalThread thread = new ZooKeeperCriticalThread("test", zks.getZooKeeperServerListener()); thread.handleException("test", new Exception()); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("unrecoverable_error_count")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000170 15051152474 032713 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerBeanTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerBea0100644 0000000 0000000 00000012065 15051152474 034264 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import org.apache.jute.Record; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ZooKeeperServerBeanTest { @BeforeEach public void setup() { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); } @AfterEach public void teardown() throws Exception { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); } @Test public void testTxnLogElapsedSyncTime() throws IOException { File tmpDir = ClientBase.createEmptyTestDir(); FileTxnSnapLog fileTxnSnapLog = new FileTxnSnapLog(new File(tmpDir, "data"), new File(tmpDir, "data_txnlog")); ZooKeeperServer zks = new ZooKeeperServer(); zks.setTxnLogFactory(fileTxnSnapLog); ZooKeeperServerBean serverBean = new ZooKeeperServerBean(zks); long elapsedTime = serverBean.getTxnLogElapsedSyncTime(); assertEquals(-1, elapsedTime); TxnHeader hdr = new TxnHeader(1, 1, 1, 1, ZooDefs.OpCode.setData); Record txn = new SetDataTxn("/foo", new byte[0], 1); Request req = new Request(0, 0, 0, hdr, txn, 0); try { zks.getTxnLogFactory().append(req); zks.getTxnLogFactory().commit(); elapsedTime = serverBean.getTxnLogElapsedSyncTime(); assertNotEquals(-1, elapsedTime); assertEquals(elapsedTime, serverBean.getTxnLogElapsedSyncTime()); } finally { fileTxnSnapLog.close(); } } @Test public void testGetSecureClientPort() throws IOException { ZooKeeperServer zks = new ZooKeeperServer(); /** * case 1: When secure client is not configured GetSecureClientPort * should return empty string */ ZooKeeperServerBean serverBean = new ZooKeeperServerBean(zks); String result = serverBean.getSecureClientPort(); assertEquals("", result); /** * case 2: When secure client is configured GetSecureClientPort should * return configured port */ ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory(); int secureClientPort = 8443; InetSocketAddress address = new InetSocketAddress(secureClientPort); cnxnFactory.configure(address, 5, -1, true); zks.setSecureServerCnxnFactory(cnxnFactory); result = serverBean.getSecureClientPort(); assertEquals(Integer.toString(secureClientPort), result); // cleanup cnxnFactory.shutdown(); } @Test public void testGetSecureClientAddress() throws IOException { ZooKeeperServer zks = new ZooKeeperServer(); /** * case 1: When secure client is not configured getSecureClientAddress * should return empty string */ ZooKeeperServerBean serverBean = new ZooKeeperServerBean(zks); String result = serverBean.getSecureClientPort(); assertEquals("", result); /** * case 2: When secure client is configured getSecureClientAddress * should return configured SecureClientAddress */ ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory(); int secureClientPort = 8443; InetSocketAddress address = new InetSocketAddress(secureClientPort); cnxnFactory.configure(address, 5, -1, true); zks.setSecureServerCnxnFactory(cnxnFactory); result = serverBean.getSecureClientAddress(); String ipv4 = "0.0.0.0:" + secureClientPort; String ipv6 = "0:0:0:0:0:0:0:0:" + secureClientPort; assertTrue(result.equals(ipv4) || result.equals(ipv6)); // cleanup cnxnFactory.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000170 15051152474 032713 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerConfTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerCon0100644 0000000 0000000 00000005107 15051152474 034313 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ZooKeeperServerConfTest extends ZKTestCase { private ZooKeeperServerConf c; @BeforeEach public void setUp() { c = new ZooKeeperServerConf(1, "a", "b", 2, 3, 4, 5, 6L, 7); } @Test public void testGetters() { assertEquals(1, c.getClientPort()); assertEquals("a", c.getDataDir()); assertEquals("b", c.getDataLogDir()); assertEquals(2, c.getTickTime()); assertEquals(3, c.getMaxClientCnxnsPerHost()); assertEquals(4, c.getMinSessionTimeout()); assertEquals(5, c.getMaxSessionTimeout()); assertEquals(6L, c.getServerId()); assertEquals(7, c.getClientPortListenBacklog()); } @Test public void testToMap() { Map m = c.toMap(); assertEquals(9, m.size()); assertEquals(Integer.valueOf(1), m.get(ZooKeeperServerConf.KEY_CLIENT_PORT)); assertEquals("a", m.get(ZooKeeperServerConf.KEY_DATA_DIR)); assertEquals("b", m.get(ZooKeeperServerConf.KEY_DATA_LOG_DIR)); assertEquals(Integer.valueOf(2), m.get(ZooKeeperServerConf.KEY_TICK_TIME)); assertEquals(Integer.valueOf(3), m.get(ZooKeeperServerConf.KEY_MAX_CLIENT_CNXNS)); assertEquals(Integer.valueOf(4), m.get(ZooKeeperServerConf.KEY_MIN_SESSION_TIMEOUT)); assertEquals(Integer.valueOf(5), m.get(ZooKeeperServerConf.KEY_MAX_SESSION_TIMEOUT)); assertEquals(Long.valueOf(6L), m.get(ZooKeeperServerConf.KEY_SERVER_ID)); assertEquals(Integer.valueOf(7), m.get(ZooKeeperServerConf.KEY_CLIENT_PORT_LISTEN_BACKLOG)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000174 15051152474 032717 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerCreationTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerCre0100644 0000000 0000000 00000003711 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import java.io.File; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; public class ZooKeeperServerCreationTest { /** * Test the default ZooKeeperServer and call processConnectRequest() to make sure * that all needed fields are initialized properly, etc. */ @Test public void testDefaultConstructor() throws Exception { File tmpDir = ClientBase.createEmptyTestDir(); FileTxnSnapLog fileTxnSnapLog = new FileTxnSnapLog(new File(tmpDir, "data"), new File(tmpDir, "data_txnlog")); ZooKeeperServer zks = new ZooKeeperServer() { @Override public void submitRequest(Request si) { // NOP } }; zks.setTxnLogFactory(fileTxnSnapLog); zks.setZKDatabase(new ZKDatabase(fileTxnSnapLog)); zks.createSessionTracker(); ServerCnxn cnxn = new MockServerCnxn(); ConnectRequest connReq = new ConnectRequest(); zks.processConnectRequest(cnxn, connReq); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000170 15051152474 032713 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerMainTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerMai0100644 0000000 0000000 00000065662 15051152474 034316 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.metrics.BaseTestMetricsProvider; import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderCapturingLifecycle; import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithConfiguration; import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithErrorInConfigure; import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithErrorInStart; import org.apache.zookeeper.metrics.BaseTestMetricsProvider.MetricsProviderWithErrorInStop; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test stand-alone server. * */ public class ZooKeeperServerMainTest extends ZKTestCase implements Watcher { protected static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerMainTest.class); private CountDownLatch clientConnected = new CountDownLatch(1); public static class MainThread extends Thread { final File confFile; final TestZKSMain main; final File tmpDir; final File dataDir; final File logDir; public MainThread(int clientPort, boolean preCreateDirs, String configs) throws IOException { this(clientPort, null, preCreateDirs, ClientBase.createTmpDir(), configs); } public MainThread(int clientPort, Integer secureClientPort, boolean preCreateDirs, String configs) throws IOException { this(clientPort, secureClientPort, preCreateDirs, ClientBase.createTmpDir(), configs); } public MainThread(int clientPort, Integer secureClientPort, boolean preCreateDirs, File tmpDir, String configs) throws IOException { super("Standalone server with clientPort:" + clientPort); this.tmpDir = tmpDir; confFile = new File(tmpDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=2000\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); if (configs != null) { fwriter.write(configs); } dataDir = new File(this.tmpDir, "data"); logDir = new File(dataDir.toString() + "_txnlog"); if (preCreateDirs) { if (!dataDir.mkdir()) { throw new IOException("unable to mkdir " + dataDir); } if (!logDir.mkdir()) { throw new IOException("unable to mkdir " + logDir); } ClientBase.createInitializeFile(logDir); } String normalizedDataDir = PathUtils.normalizeFileSystemPath(dataDir.toString()); String normalizedLogDir = PathUtils.normalizeFileSystemPath(logDir.toString()); fwriter.write("dataDir=" + normalizedDataDir + "\n"); fwriter.write("dataLogDir=" + normalizedLogDir + "\n"); fwriter.write("clientPort=" + clientPort + "\n"); if (secureClientPort != null) { fwriter.write("secureClientPort=" + secureClientPort + "\n"); } fwriter.flush(); fwriter.close(); main = new TestZKSMain(); } public void run() { String[] args = new String[1]; args[0] = confFile.toString(); try { main.initializeAndRun(args); } catch (Exception e) { // test will still fail even though we just log/ignore LOG.error("unexpected exception in run", e); } } public void shutdown() throws IOException { main.shutdown(); } void deleteDirs() throws IOException { delete(tmpDir); } void delete(File f) throws IOException { if (f.isDirectory()) { for (File c : f.listFiles()) { delete(c); } } if (!f.delete()) { // double check for the file existence if (f.exists()) { throw new IOException("Failed to delete file: " + f); } } } ServerCnxnFactory getCnxnFactory() { return main.getCnxnFactory(); } public ServerCnxnFactory getSecureCnxnFactory(){ return main.getSecureCnxnFactory(); } } public static class TestZKSMain extends ZooKeeperServerMain { public void shutdown() { super.shutdown(); } } /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2247. * Test to verify that even after non recoverable error (error while * writing transaction log), ZooKeeper is still available. */ @Test @Timeout(value = 30) public void testNonRecoverableError() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); MainThread main = new MainThread(CLIENT_PORT, true, null); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, this); zk.create("/foo1", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo1", null, null)), "foobar"); // inject problem in server ZooKeeperServer zooKeeperServer = main.getCnxnFactory().getZooKeeperServer(); FileTxnSnapLog snapLog = zooKeeperServer.getTxnLogFactory(); FileTxnSnapLog fileTxnSnapLogWithError = new FileTxnSnapLog(snapLog.getDataLogDir(), snapLog.getSnapDir()) { @Override public void commit() throws IOException { throw new IOException("Input/output error"); } }; ZKDatabase newDB = new ZKDatabase(fileTxnSnapLogWithError); zooKeeperServer.setZKDatabase(newDB); try { // do create operation, so that injected IOException is thrown zk.create("/foo2", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("IOException is expected as error is injected in transaction log commit funtionality"); } catch (Exception e) { // do nothing } zk.close(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); fileTxnSnapLogWithError.close(); main.shutdown(); main.deleteDirs(); } /** * Tests that the ZooKeeper server will fail to start if the * snapshot directory is read only. * * This test will fail if it is executed as root user. */ @Test @Timeout(value = 30) public void testReadOnlySnapshotDir() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); // Start up the ZK server to automatically create the necessary directories // and capture the directory where data is stored MainThread main = new MainThread(CLIENT_PORT, true, null); File tmpDir = main.tmpDir; main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT / 2), "waiting for server being up"); main.shutdown(); // Make the snapshot directory read only File snapDir = new File(main.dataDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); snapDir.setWritable(false); // Restart ZK and observe a failure main = new MainThread(CLIENT_PORT, null, false, tmpDir, null); main.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT / 2), "waiting for server being up"); main.shutdown(); snapDir.setWritable(true); main.deleteDirs(); } /** * Tests that the ZooKeeper server will fail to start if the * transaction log directory is read only. * * This test will fail if it is executed as root user. */ @Test @Timeout(value = 30) public void testReadOnlyTxnLogDir() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); // Start up the ZK server to automatically create the necessary directories // and capture the directory where data is stored MainThread main = new MainThread(CLIENT_PORT, true, null); File tmpDir = main.tmpDir; main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT / 2), "waiting for server being up"); main.shutdown(); // Make the transaction log directory read only File logDir = new File(main.logDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); logDir.setWritable(false); // Restart ZK and observe a failure main = new MainThread(CLIENT_PORT, null, false, tmpDir, null); main.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT / 2), "waiting for server being up"); main.shutdown(); logDir.setWritable(true); main.deleteDirs(); } /** * Verify the ability to start a standalone server instance. */ @Test public void testStandalone() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); MainThread main = new MainThread(CLIENT_PORT, true, null); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); clientConnected = new CountDownLatch(1); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, this); assertTrue(clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Failed to establish zkclient connection!"); zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); zk.close(); main.shutdown(); main.join(); main.deleteDirs(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } /** * Test verifies that the server shouldn't allow minsessiontimeout greater than * maxsessiontimeout */ @Test public void testWithMinSessionTimeoutGreaterThanMaxSessionTimeout() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final int tickTime = 2000; final int minSessionTimeout = 20 * tickTime + 1000; // min is higher final int maxSessionTimeout = tickTime * 2 - 100; // max is lower final String configs = "maxSessionTimeout=" + maxSessionTimeout + "\n" + "minSessionTimeout=" + minSessionTimeout + "\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); String[] args = new String[1]; args[0] = main.confFile.toString(); try { main.main.initializeAndRun(args); fail("Must throw exception as " + "minsessiontimeout > maxsessiontimeout"); } catch (ConfigException iae) { // expected } } /** * Test verifies that the server shouldn't boot with an invalid metrics provider */ @Test public void testInvalidMetricsProvider() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final String configs = "metricsProvider.className=BadClass\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); String[] args = new String[1]; args[0] = main.confFile.toString(); try { main.main.initializeAndRun(args); fail("Must throw exception as metrics provider is not " + "well configured"); } catch (ConfigException iae) { // expected } } /** * Test verifies that the server shouldn't boot with a faulty metrics provider */ @Test public void testFaultyMetricsProviderOnStart() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final String configs = "metricsProvider.className=" + MetricsProviderWithErrorInStart.class.getName() + "\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); String[] args = new String[1]; args[0] = main.confFile.toString(); try { main.main.initializeAndRun(args); fail("Must throw exception as metrics provider cannot boot"); } catch (IOException iae) { // expected } } /** * Test verifies that the server shouldn't boot with a faulty metrics provider */ @Test public void testFaultyMetricsProviderOnConfigure() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final String configs = "metricsProvider.className=" + MetricsProviderWithErrorInConfigure.class.getName() + "\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); String[] args = new String[1]; args[0] = main.confFile.toString(); try { main.main.initializeAndRun(args); fail("Must throw exception as metrics provider is cannot boot"); } catch (IOException iae) { // expected } } /** * Test verifies that the server shouldn't be affected but runtime errors on stop() */ @Test public void testFaultyMetricsProviderOnStop() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); MetricsProviderWithErrorInStop.stopCalled.set(false); final String configs = "metricsProvider.className=" + MetricsProviderWithErrorInStop.class.getName() + "\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); clientConnected = new CountDownLatch(1); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, this); assertTrue(clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Failed to establish zkclient connection!"); zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); zk.close(); main.shutdown(); main.join(); main.deleteDirs(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); assertTrue(MetricsProviderWithErrorInStop.stopCalled.get()); } /** * Test verifies that configuration is passed to the MetricsProvider. */ @Test public void testMetricsProviderConfiguration() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); MetricsProviderWithConfiguration.httpPort.set(0); final String configs = "metricsProvider.className=" + MetricsProviderWithConfiguration.class.getName() + "\n" + "metricsProvider.httpPort=1234\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); clientConnected = new CountDownLatch(1); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, this); assertTrue(clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Failed to establish zkclient connection!"); zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); zk.close(); main.shutdown(); main.join(); main.deleteDirs(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); assertEquals(1234, MetricsProviderWithConfiguration.httpPort.get()); } /** * Test verifies that all of the lifecycle methods of the MetricsProvider are called. */ @Test public void testMetricsProviderLifecycle() throws Exception { ClientBase.setupTestEnv(); MetricsProviderCapturingLifecycle.reset(); final int CLIENT_PORT = PortAssignment.unique(); final String configs = "metricsProvider.className=" + MetricsProviderCapturingLifecycle.class.getName() + "\n" + "metricsProvider.httpPort=1234\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); clientConnected = new CountDownLatch(1); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT, this); assertTrue(clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "Failed to establish zkclient connection!"); zk.create("/foo", "foobar".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo", null, null)), "foobar"); zk.close(); main.shutdown(); main.join(); main.deleteDirs(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.configureCalled.get(), "metrics provider lifecycle error"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.startCalled.get(), "metrics provider lifecycle error"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.getRootContextCalled.get(), "metrics provider lifecycle error"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.stopCalled.get(), "metrics provider lifecycle error"); } /** * Test verifies that the server is able to redefine if user configured only * minSessionTimeout limit */ @Test public void testWithOnlyMinSessionTimeout() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final int tickTime = 2000; final int minSessionTimeout = tickTime * 2 - 100; int maxSessionTimeout = 20 * tickTime; final String configs = "minSessionTimeout=" + minSessionTimeout + "\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); main.start(); String HOSTPORT = "127.0.0.1:" + CLIENT_PORT; assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); // create session with min value verifySessionTimeOut(minSessionTimeout, minSessionTimeout, HOSTPORT); verifySessionTimeOut(minSessionTimeout - 2000, minSessionTimeout, HOSTPORT); // create session with max value verifySessionTimeOut(maxSessionTimeout, maxSessionTimeout, HOSTPORT); verifySessionTimeOut(maxSessionTimeout + 2000, maxSessionTimeout, HOSTPORT); main.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } /** * Test verifies that the server is able to redefine the min/max session * timeouts */ @Test public void testMinMaxSessionTimeOut() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final int tickTime = 2000; final int minSessionTimeout = tickTime * 2 - 100; final int maxSessionTimeout = 20 * tickTime + 1000; final String configs = "maxSessionTimeout=" + maxSessionTimeout + "\n" + "minSessionTimeout=" + minSessionTimeout + "\n"; MainThread main = new MainThread(CLIENT_PORT, true, configs); main.start(); String HOSTPORT = "127.0.0.1:" + CLIENT_PORT; assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); // create session with min value verifySessionTimeOut(minSessionTimeout, minSessionTimeout, HOSTPORT); verifySessionTimeOut(minSessionTimeout - 2000, minSessionTimeout, HOSTPORT); // create session with max value verifySessionTimeOut(maxSessionTimeout, maxSessionTimeout, HOSTPORT); verifySessionTimeOut(maxSessionTimeout + 2000, maxSessionTimeout, HOSTPORT); main.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } private void verifySessionTimeOut(int sessionTimeout, int expectedSessionTimeout, String HOSTPORT) throws IOException, KeeperException, InterruptedException { clientConnected = new CountDownLatch(1); ZooKeeper zk = new ZooKeeper(HOSTPORT, sessionTimeout, this); assertTrue(clientConnected.await(sessionTimeout, TimeUnit.MILLISECONDS), "Failed to establish zkclient connection!"); assertEquals(expectedSessionTimeout, zk.getSessionTimeout(), "Not able to configure the sessionTimeout values"); zk.close(); } @Test public void testJMXRegistrationWithNIO() throws Exception { ClientBase.setupTestEnv(); File tmpDir_1 = ClientBase.createTmpDir(); ServerCnxnFactory server_1 = startServer(tmpDir_1); File tmpDir_2 = ClientBase.createTmpDir(); ServerCnxnFactory server_2 = startServer(tmpDir_2); server_1.shutdown(); server_2.shutdown(); deleteFile(tmpDir_1); deleteFile(tmpDir_2); } @Test public void testJMXRegistrationWithNetty() throws Exception { String originalServerCnxnFactory = System.getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, NettyServerCnxnFactory.class.getName()); try { ClientBase.setupTestEnv(); File tmpDir_1 = ClientBase.createTmpDir(); ServerCnxnFactory server_1 = startServer(tmpDir_1); File tmpDir_2 = ClientBase.createTmpDir(); ServerCnxnFactory server_2 = startServer(tmpDir_2); server_1.shutdown(); server_2.shutdown(); deleteFile(tmpDir_1); deleteFile(tmpDir_2); } finally { // setting back if (originalServerCnxnFactory == null || originalServerCnxnFactory.isEmpty()) { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); } else { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, originalServerCnxnFactory); } } } private void deleteFile(File f) throws IOException { if (f.isDirectory()) { for (File c : f.listFiles()) { deleteFile(c); } } if (!f.delete()) { // double check for the file existence if (f.exists()) { throw new IOException("Failed to delete file: " + f); } } } private ServerCnxnFactory startServer(File tmpDir) throws IOException, InterruptedException { final int CLIENT_PORT = PortAssignment.unique(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); ServerCnxnFactory f = ServerCnxnFactory.createFactory(CLIENT_PORT, -1); f.startup(zks); assertNotNull(zks.jmxServerBean, "JMX initialization failed!"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, CONNECTION_TIMEOUT), "waiting for server being up"); return f; } public void process(WatchedEvent event) { if (event.getState() == KeeperState.SyncConnected) { clientConnected.countDown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000174 15051152474 032717 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerMaxCnxnsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerMax0100644 0000000 0000000 00000015613 15051152474 034324 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZooKeeperServerMaxCnxnsTest extends QuorumPeerTestBase { protected static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerMaxCnxnsTest.class); private static int SERVER_COUNT = 3; private MainThread[] mt; private ZooKeeper[] clients; /** *

         * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2238.
         * Support limiting the maximum number of connections/clients to a ZooKeeper server.
         * 
    */ @Test @Timeout(value = 120) public void testMaxZooKeeperClientsWithNIOServerCnxnFactory() throws Exception { String serverCnxnFactory = "org.apache.zookeeper.server.NIOServerCnxnFactory"; testMaxZooKeeperClients(serverCnxnFactory); } @Test @Timeout(value = 120) public void testMaxZooKeeperClientsWithNettyServerCnxnFactory() throws Exception { String serverCnxnFactory = "org.apache.zookeeper.server.NettyServerCnxnFactory"; testMaxZooKeeperClients(serverCnxnFactory); } private void testMaxZooKeeperClients(String serverCnxnFactory) throws Exception { final int clientPorts[] = new int[SERVER_COUNT]; int maxCnxns = 2; StringBuilder sb = new StringBuilder(); sb.append("maxCnxns=" + maxCnxns + "\n"); sb.append("serverCnxnFactory=" + serverCnxnFactory + "\n"); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread mt[] = new MainThread[SERVER_COUNT]; // start 3 servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure all servers started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } int maxAllowedConnection = maxCnxns * SERVER_COUNT; String cxnString = getCxnString(clientPorts); final CountDownLatch countDownLatch = new CountDownLatch(maxAllowedConnection); ZooKeeper[] clients = new ZooKeeper[maxAllowedConnection]; Watcher watcher = new Watcher() { @Override public void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.SyncConnected) { countDownLatch.countDown(); } } }; for (int i = 0; i < maxAllowedConnection; i++) { clients[i] = new ZooKeeper(cxnString, ClientBase.CONNECTION_TIMEOUT, watcher); Thread.sleep(100); } countDownLatch.await(); // reaching this point indicates that all maxAllowedConnection connected // No more client to be allowed to connect now as we have reached the // max connections CountdownWatcher cdw = new CountdownWatcher(); ZooKeeper extraClient = new ZooKeeper(cxnString, ClientBase.CONNECTION_TIMEOUT, cdw); try { cdw.waitForConnected(ClientBase.CONNECTION_TIMEOUT / 2); fail("Client is not supposed to get connected as max connection already reached."); } catch (TimeoutException e) { extraClient.close(); } // lets close one already connected client clients[0].close(); // Now extra client must automatically get connected cdw = new CountdownWatcher(); extraClient = new ZooKeeper(cxnString, ClientBase.CONNECTION_TIMEOUT, cdw); cdw.waitForConnected(ClientBase.CONNECTION_TIMEOUT); // verify some basic operation String create = extraClient.create("/test", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals("/test", create); // cleanup extraClient.close(); } private String getCxnString(int[] clientPorts) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < clientPorts.length; i++) { builder.append("127.0.0.1:" + clientPorts[i]); if (i != clientPorts.length - 1) { builder.append(","); } } return builder.toString(); } @AfterEach public void tearDown() { // stop all clients if (clients != null) { for (ZooKeeper zooKeeper : clients) { try { zooKeeper.close(); } catch (InterruptedException e) { LOG.warn("ZooKeeper interrupted while closing it.", e); } } } // stop all severs if (mt != null) { for (int i = 0; i < SERVER_COUNT; i++) { try { mt[i].shutdown(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted while shutting it down", e); } } } } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000174 15051152474 032717 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerShutdownTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerShu0100644 0000000 0000000 00000006300 15051152474 034327 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Learner; import org.apache.zookeeper.server.quorum.LearnerZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ZooKeeperServerShutdownTest extends ZKTestCase { static class ShutdownTrackRequestProcessor implements RequestProcessor { boolean shutdown = false; @Override public void processRequest(Request request) throws RequestProcessorException { } @Override public void shutdown() { shutdown = true; } } public static class ShutdownTrackLearnerZooKeeperServer extends LearnerZooKeeperServer { public ShutdownTrackLearnerZooKeeperServer(FileTxnSnapLog logFactory, QuorumPeer self) throws IOException { super(logFactory, 2000, 2000, 2000, -1, new ZKDatabase(logFactory), self); } @Override protected void setupRequestProcessors() { firstProcessor = new ShutdownTrackRequestProcessor(); syncProcessor = new SyncRequestProcessor(this, null); syncProcessor.start(); } ShutdownTrackRequestProcessor getFirstProcessor() { return (ShutdownTrackRequestProcessor) firstProcessor; } SyncRequestProcessor getSyncRequestProcessor() { return syncProcessor; } @Override public Learner getLearner() { return null; } } @Test void testLearnerZooKeeperServerShutdown(@TempDir File tmpDir) throws Exception { File tmpFile = File.createTempFile("test", ".dir", tmpDir); tmpFile.delete(); FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpFile, tmpFile); ShutdownTrackLearnerZooKeeperServer zooKeeperServer = new ShutdownTrackLearnerZooKeeperServer(logFactory, new QuorumPeer()); zooKeeperServer.startup(); zooKeeperServer.shutdown(false); assertTrue(zooKeeperServer.getFirstProcessor().shutdown); assertFalse(zooKeeperServer.getSyncRequestProcessor().isAlive()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000173 15051152474 032716 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerStartupTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerSta0100644 0000000 0000000 00000024141 15051152474 034322 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import static org.apache.zookeeper.server.command.AbstractFourLetterCommand.ZK_NOT_SERVING; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class tests the startup behavior of ZooKeeper server. */ public class ZooKeeperServerStartupTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerStartupTest.class); private static int PORT = PortAssignment.unique(); private static String HOST = "127.0.0.1"; private static String HOSTPORT = HOST + ":" + PORT; private ServerCnxnFactory servcnxnf; private ZooKeeperServer zks; private File tmpDir; private CountDownLatch startupDelayLatch = new CountDownLatch(1); @AfterEach public void teardown() throws Exception { // count down to avoid infinite blocking call due to this latch, if // any. startupDelayLatch.countDown(); if (servcnxnf != null) { servcnxnf.shutdown(); } if (zks != null) { zks.shutdown(); } if (zks.getZKDatabase() != null) { zks.getZKDatabase().close(); } ClientBase.recursiveDelete(tmpDir); } /** * Test case for * https://issues.apache.org/jira/browse/ZOOKEEPER-2383 */ @Test @Timeout(value = 30) public void testClientConnectionRequestDuringStartupWithNIOServerCnxn() throws Exception { tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); startSimpleZKServer(startupDelayLatch); SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks; assertTrue(simplezks.waitForStartupInvocation(10), "Failed to invoke zks#startup() method during server startup"); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zkClient = new ZooKeeper(HOSTPORT, ClientBase.CONNECTION_TIMEOUT, watcher); assertFalse(simplezks.waitForSessionCreation(5), "Since server is not fully started, zks#createSession() shouldn't be invoked"); LOG.info("Decrements the count of the latch, so that server will proceed with startup"); startupDelayLatch.countDown(); assertTrue(ClientBase.waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server being up "); assertTrue(simplezks.waitForSessionCreation(5), "Failed to invoke zks#createSession() method during client session creation"); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zkClient.close(); } /** * Test case for * https://issues.apache.org/jira/browse/ZOOKEEPER-2383 */ @Test @Timeout(value = 30) public void testClientConnectionRequestDuringStartupWithNettyServerCnxn() throws Exception { tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); String originalServerCnxnFactory = System.getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); try { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, NettyServerCnxnFactory.class.getName()); startSimpleZKServer(startupDelayLatch); SimpleZooKeeperServer simplezks = (SimpleZooKeeperServer) zks; assertTrue(simplezks.waitForStartupInvocation(10), "Failed to invoke zks#startup() method during server startup"); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zkClient = new ZooKeeper(HOSTPORT, ClientBase.CONNECTION_TIMEOUT, watcher); assertFalse(simplezks.waitForSessionCreation(5), "Since server is not fully started, zks#createSession() shouldn't be invoked"); LOG.info("Decrements the count of the latch, so that server will proceed with startup"); startupDelayLatch.countDown(); assertTrue(ClientBase.waitForServerUp(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server being up "); assertTrue(simplezks.waitForSessionCreation(5), "Failed to invoke zks#createSession() method during client session creation"); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zkClient.close(); } finally { // reset cnxn factory if (originalServerCnxnFactory == null) { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); return; } System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, originalServerCnxnFactory); } } /** * Test case for * https://issues.apache.org/jira/browse/ZOOKEEPER-2383 */ @Test @Timeout(value = 30) public void testFourLetterWords() throws Exception { startSimpleZKServer(startupDelayLatch); verify("conf", ZK_NOT_SERVING); verify("crst", ZK_NOT_SERVING); verify("cons", ZK_NOT_SERVING); verify("dirs", ZK_NOT_SERVING); verify("dump", ZK_NOT_SERVING); verify("mntr", ZK_NOT_SERVING); verify("stat", ZK_NOT_SERVING); verify("srst", ZK_NOT_SERVING); verify("wchp", ZK_NOT_SERVING); verify("wchc", ZK_NOT_SERVING); verify("wchs", ZK_NOT_SERVING); verify("isro", "null"); } private void verify(String cmd, String expected) throws IOException, SSLContextException { String resp = sendRequest(cmd); LOG.info("cmd {} expected {} got {}", cmd, expected, resp); assertTrue(resp.contains(expected), "Unexpected response"); } private String sendRequest(String cmd) throws IOException, SSLContextException { return send4LetterWord(HOST, PORT, cmd); } private void startSimpleZKServer(CountDownLatch startupDelayLatch) throws IOException { zks = new SimpleZooKeeperServer(tmpDir, tmpDir, 3000, startupDelayLatch); SyncRequestProcessor.setSnapCount(100); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); servcnxnf = ServerCnxnFactory.createFactory(PORT, -1); Thread startupThread = new Thread() { public void run() { try { servcnxnf.startup(zks); } catch (IOException e) { LOG.error("Unexcepted exception during server startup", e); // Ignoring exception. If there is an ioexception // then one of the following assertion will fail } catch (InterruptedException e) { LOG.error("Unexcepted exception during server startup", e); // Ignoring exception. If there is an interrupted exception // then one of the following assertion will fail } } }; LOG.info("Starting zk server {}", HOSTPORT); startupThread.start(); } private static class SimpleZooKeeperServer extends ZooKeeperServer { private CountDownLatch startupDelayLatch; private CountDownLatch startupInvokedLatch = new CountDownLatch(1); private CountDownLatch createSessionInvokedLatch = new CountDownLatch(1); public SimpleZooKeeperServer(File snapDir, File logDir, int tickTime, CountDownLatch startupDelayLatch) throws IOException { super(snapDir, logDir, tickTime); this.startupDelayLatch = startupDelayLatch; } @Override public synchronized void startup() { try { startupInvokedLatch.countDown(); // Delaying the zk server startup so that // ZooKeeperServer#sessionTracker reference won't be // initialized. In the defect scenario, while processing the // connection request zkServer needs sessionTracker reference, // but this is not yet initialized and the server is still in // the startup phase, resulting in NPE. startupDelayLatch.await(); } catch (InterruptedException e) { fail("Unexpected InterruptedException while startinng up!"); } super.startup(); } @Override long createSession(ServerCnxn cnxn, byte[] passwd, int timeout) { createSessionInvokedLatch.countDown(); return super.createSession(cnxn, passwd, timeout); } boolean waitForStartupInvocation(long timeout) throws InterruptedException { return startupInvokedLatch.await(timeout, TimeUnit.SECONDS); } boolean waitForSessionCreation(long timeout) throws InterruptedException { return createSessionInvokedLatch.await(timeout, TimeUnit.SECONDS); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000164 15051152474 032716 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperServerTes0100644 0000000 0000000 00000017722 15051152474 034335 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.server.util.QuotaMetricsUtils; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; public class ZooKeeperServerTest extends ZKTestCase { @Test public void testDirSize() throws Exception { ZooKeeperServer zks = null; ServerCnxnFactory cnxnFactory = null; try { final File dataDir = ClientBase.createTmpDir(); final File logDir = ClientBase.createTmpDir(); zks = new ZooKeeperServer(dataDir, logDir, 3000); // validate dir size before server starts assertEquals(0, zks.getDataDirSize()); assertEquals(0, zks.getLogDirSize()); // start server final String hostPort = "127.0.0.1:" + PortAssignment.unique(); final int port = Integer.parseInt(hostPort.split(":")[1]); cnxnFactory = ServerCnxnFactory.createFactory(port, -1); cnxnFactory.startup(zks); assertTrue(ClientBase.waitForServerUp(hostPort, 120000)); // validate data size is greater than 0 as snapshot has been taken when server starts assertTrue(zks.getDataDirSize() > 0); // validate log size is 0 as no txn yet assertEquals(0, zks.getLogDirSize()); } finally { if (cnxnFactory != null) { cnxnFactory.shutdown(); } if (zks != null) { zks.shutdown(); } } } @Test public void testSortDataDirAscending() { File[] files = new File[5]; files[0] = new File("foo.10027c6de"); files[1] = new File("foo.10027c6df"); files[2] = new File("bar.10027c6dd"); files[3] = new File("foo.10027c6dc"); files[4] = new File("foo.20027c6dc"); File[] orig = files.clone(); List filelist = Util.sortDataDir(files, "foo", true); assertEquals(orig[2], filelist.get(0)); assertEquals(orig[3], filelist.get(1)); assertEquals(orig[0], filelist.get(2)); assertEquals(orig[1], filelist.get(3)); assertEquals(orig[4], filelist.get(4)); } @Test public void testSortDataDirDescending() { File[] files = new File[5]; files[0] = new File("foo.10027c6de"); files[1] = new File("foo.10027c6df"); files[2] = new File("bar.10027c6dd"); files[3] = new File("foo.10027c6dc"); files[4] = new File("foo.20027c6dc"); File[] orig = files.clone(); List filelist = Util.sortDataDir(files, "foo", false); assertEquals(orig[4], filelist.get(0)); assertEquals(orig[1], filelist.get(1)); assertEquals(orig[0], filelist.get(2)); assertEquals(orig[3], filelist.get(3)); assertEquals(orig[2], filelist.get(4)); } @Test public void testGetLogFiles() { File[] files = new File[5]; files[0] = new File("log.10027c6de"); files[1] = new File("log.10027c6df"); files[2] = new File("snapshot.10027c6dd"); files[3] = new File("log.10027c6dc"); files[4] = new File("log.20027c6dc"); File[] orig = files.clone(); File[] filelist = FileTxnLog.getLogFiles(files, Long.parseLong("10027c6de", 16)); assertEquals(3, filelist.length); assertEquals(orig[0], filelist[0]); assertEquals(orig[1], filelist[1]); assertEquals(orig[4], filelist[2]); } @Test public void testForceSyncDefaultEnabled() { File file = new File("foo.10027c6de"); FileTxnLog log = new FileTxnLog(file); assertTrue(log.isForceSync()); } @Test public void testForceSyncDefaultDisabled() { try { File file = new File("foo.10027c6de"); System.setProperty("zookeeper.forceSync", "no"); FileTxnLog log = new FileTxnLog(file); assertFalse(log.isForceSync()); } finally { //Reset back to default. System.setProperty("zookeeper.forceSync", "yes"); } } @Test public void testInvalidSnapshot() { File f = null; File tmpFileDir = null; try { tmpFileDir = ClientBase.createTmpDir(); f = new File(tmpFileDir, "snapshot.0"); if (!f.exists()) { f.createNewFile(); } assertFalse(SnapStream.isValidSnapshot(f), "Snapshot file size is greater than 9 bytes"); assertTrue(f.delete(), "Can't delete file"); } catch (IOException e) { } finally { if (null != tmpFileDir) { ClientBase.recursiveDelete(tmpFileDir); } } } @Test public void testClientZxidAhead() { ZooKeeperServer zooKeeperServer = new ZooKeeperServer(); final ZKDatabase zkDatabase = new ZKDatabase(mock(FileTxnSnapLog.class)); zooKeeperServer.setZKDatabase(zkDatabase); final ConnectRequest request = new ConnectRequest(); request.setProtocolVersion(1); request.setLastZxidSeen(99L); request.setTimeOut(500); request.setSessionId(123L); request.setPasswd(new byte[]{ 1 }); request.setReadOnly(true); ServerCnxn.CloseRequestException e = assertThrows( ServerCnxn.CloseRequestException.class, () -> zooKeeperServer.processConnectRequest(new MockServerCnxn(), request)); assertEquals(e.getReason(), ServerCnxn.DisconnectReason.CLIENT_ZXID_AHEAD); } @Test public void testUpdateQuotaExceededMetrics() { final String name = QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; final String namespace = UUID.randomUUID().toString(); final long count = 3L; for (int i = 0; i < count; i++) { ZooKeeperServer.updateQuotaExceededMetrics(namespace); } final Map values = MetricsUtils.currentServerMetrics(); assertEquals(1, values.keySet().stream().filter( key -> key.contains(String.format("%s_%s", namespace, name))).count()); assertEquals(count, values.get(String.format("%s_%s", namespace, name))); } @Test public void testUpdateQuotaExceededMetrics_nullNamespace() { assertDoesNotThrow(() -> ZooKeeperServer.updateQuotaExceededMetrics(null)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000164 15051152474 032716 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperThreadTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZooKeeperThreadTes0100644 0000000 0000000 00000005305 15051152474 034270 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class ZooKeeperThreadTest extends ZKTestCase { private CountDownLatch runningLatch = new CountDownLatch(1); public class MyThread extends ZooKeeperThread { public MyThread(String threadName) { super(threadName); } public void run() { throw new Error(); } @Override protected void handleException(String thName, Throwable e) { runningLatch.countDown(); } } public class MyCriticalThread extends ZooKeeperCriticalThread { public MyCriticalThread(String threadName) { super(threadName, new ZooKeeperServerListener() { @Override public void notifyStopping(String threadName, int erroCode) { } }); } public void run() { throw new Error(); } @Override protected void handleException(String thName, Throwable e) { runningLatch.countDown(); } } /** * Test verifies uncaught exception handling of ZooKeeperThread */ @Test @Timeout(value = 30) public void testUncaughtException() throws Exception { MyThread t1 = new MyThread("Test-Thread"); t1.start(); assertTrue(runningLatch.await(10000, TimeUnit.MILLISECONDS), "Uncaught exception is not properly handled."); runningLatch = new CountDownLatch(1); MyCriticalThread t2 = new MyCriticalThread("Test-Critical-Thread"); t2.start(); assertTrue(runningLatch.await(10000, TimeUnit.MILLISECONDS), "Uncaught exception is not properly handled."); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000173 15051152474 032716 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZookeeperServerRestoreTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZookeeperServerRes0100644 0000000 0000000 00000013751 15051152474 034371 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.server.persistence.FileSnap.SNAPSHOT_FILE_PREFIX; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.Set; import java.util.zip.CheckedInputStream; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ZookeeperServerRestoreTest extends ZKTestCase { private static final String BASE_PATH = "/restoreFromSnapshotTest"; private static final int NODE_COUNT = 10; private static final String HOST_PORT = "127.0.0.1:" + PortAssignment.unique(); @TempDir static File dataDir; @TempDir static File logDir; @Test public void testRestoreFromSnapshot() throws Exception { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(true); final ZooKeeperServer zks = new ZooKeeperServer(dataDir, logDir, 3000); final int port = Integer.parseInt(HOST_PORT.split(":")[1]); final ServerCnxnFactory serverCnxnFactory = ServerCnxnFactory.createFactory(port, -1); ZooKeeper zk1 = null; ZooKeeper zk2 = null; ZooKeeper zk3 = null; try { // start the server serverCnxnFactory.startup(zks); assertTrue(ClientBase.waitForServerUp(HOST_PORT, CONNECTION_TIMEOUT)); // zk1 create test data zk1 = ClientBase.createZKClient(HOST_PORT); for (int i = 0; i < NODE_COUNT; i++) { final String path = BASE_PATH + "-" + i; zk1.create(path, String.valueOf(i).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // take Snapshot final File snapshotFile = zks.takeSnapshot(false, false); final long lastZxidFromSnapshot = Util.getZxidFromName(snapshotFile.getName(), SNAPSHOT_FILE_PREFIX); // zk2 create more test data after snapshotting zk2 = ClientBase.createZKClient(HOST_PORT); for (int i = NODE_COUNT; i < NODE_COUNT * 2; i++) { final String path = BASE_PATH + "-" + i; zk2.create(path, String.valueOf(i).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // restore from snapshot try (final CheckedInputStream is = SnapStream.getInputStream(snapshotFile)) { final long lastZxidFromRestore = zks.restoreFromSnapshot(is); // validate the last processed zxid assertEquals(lastZxidFromSnapshot, lastZxidFromRestore); // validate restored data only contains data from snapshot zk3 = ClientBase.createZKClient(HOST_PORT); for (int i = 0; i < NODE_COUNT; i++) { final String path = BASE_PATH + "-" + i; final String expectedData = String.valueOf(i); assertArrayEquals(expectedData.getBytes(), zk3.getData(path, null, null)); } assertEquals(NODE_COUNT + 3, zk3.getAllChildrenNumber("/")); // validate sessions final SessionTracker sessionTracker = zks.getSessionTracker(); final Set globalSessions = sessionTracker.globalSessions(); assertEquals(2, globalSessions.size()); assertTrue(globalSessions.contains(zk1.getSessionId())); Assertions.assertFalse(globalSessions.contains(zk2.getSessionId())); assertTrue(globalSessions.contains(zk3.getSessionId())); // validate ZookeeperServer state assertEquals(ZooKeeperServer.State.RUNNING, zks.state); // validate being able to create more data after restore zk3.create(BASE_PATH + "_" + "after", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(NODE_COUNT + 4, zk3.getAllChildrenNumber("/")); } } finally { System.clearProperty("zookeeper.serializeLastProcessedZxid.enabled"); if (zk1 != null) { zk1.close(); } if (zk2 != null) { zk2.close(); } if (zk3 != null) { zk3.close(); } zks.shutdown(); serverCnxnFactory.shutdown(); } } @Test public void testRestoreFromSnapshot_nulInputStream() throws Exception { final ZooKeeperServer zks = new ZooKeeperServer(dataDir, logDir, 3000); assertThrows(IllegalArgumentException.class, () -> zks.restoreFromSnapshot(null)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zo0100644 0000000 0000000 00000000174 15051152474 032717 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZookeeperServerSnapshotTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZookeeperServerSna0100644 0000000 0000000 00000007145 15051152474 034361 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ZookeeperServerSnapshotTest extends ZKTestCase { private static final String BASE_PATH = "/takeSnapshotTest"; private static final int NODE_COUNT = 10; private static final String HOST_PORT = "127.0.0.1:" + PortAssignment.unique(); @TempDir static File dataDir; @TempDir static File logDir; @Test public void testTakeSnapshot() throws Exception { ZooKeeperServer zks = new ZooKeeperServer(dataDir, logDir, 3000); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(true); final int port = Integer.parseInt(HOST_PORT.split(":")[1]); final ServerCnxnFactory serverCnxnFactory = ServerCnxnFactory.createFactory(port, -1); ZooKeeper zk = null; try { serverCnxnFactory.startup(zks); assertTrue(ClientBase.waitForServerUp(HOST_PORT, CONNECTION_TIMEOUT)); zk = ClientBase.createZKClient(HOST_PORT); for (int i = 0; i < NODE_COUNT; i++) { final String path = BASE_PATH + "-" + i; zk.create(path, String.valueOf(i).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // takeSnapshot zks.takeSnapshot(false, false); // clean up zk.close(); zks.shutdown(); // restart server and assert the data restored from snapshot zks = new ZooKeeperServer(dataDir, logDir, 3000); ZooKeeperServer.setSerializeLastProcessedZxidEnabled(false); serverCnxnFactory.startup(zks); assertTrue(ClientBase.waitForServerUp(HOST_PORT, CONNECTION_TIMEOUT)); zk = ClientBase.createZKClient(HOST_PORT); for (int i = 0; i < NODE_COUNT; i++) { final String path = BASE_PATH + "-" + i; final String expectedData = String.valueOf(i); assertArrayEquals(expectedData.getBytes(), zk.getData(path, null, null)); } assertEquals(NODE_COUNT + 3, zk.getAllChildrenNumber("/")); } finally { if (zk != null) { zk.close(); } zks.shutdown(); serverCnxnFactory.shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_Zx0100644 0000000 0000000 00000000161 15051152474 032724 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZxidRolloverTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/ZxidRolloverTest.j0100644 0000000 0000000 00000034322 15051152474 034315 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.ConnectionLossException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.apache.zookeeper.test.ClientTest; import org.apache.zookeeper.test.QuorumUtil; import org.apache.zookeeper.test.QuorumUtil.PeerStruct; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Verify ZOOKEEPER-1277 - ensure that we handle epoch rollover correctly. */ public class ZxidRolloverTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(ZxidRolloverTest.class); private QuorumUtil qu; private ZooKeeperServer zksLeader; private ZooKeeper[] zkClients = new ZooKeeper[3]; private CountdownWatcher[] zkClientWatchers = new CountdownWatcher[3]; private int idxLeader; private int idxFollower; private ZooKeeper getClient(int idx) { return zkClients[idx - 1]; } @BeforeEach public void setUp() throws Exception { System.setProperty("zookeeper.admin.enableServer", "false"); System.setProperty("zookeeper.test.allowDiscontinuousProposals", "true"); // set the snap count to something low so that we force log rollover // and verify that is working as part of the epoch rollover. SyncRequestProcessor.setSnapCount(7); qu = new QuorumUtil(1); startAll(); for (int i = 0; i < zkClients.length; i++) { zkClientWatchers[i] = new CountdownWatcher(); PeerStruct peer = qu.getPeer(i + 1); zkClients[i] = new ZooKeeper( "127.0.0.1:" + peer.clientPort, ClientTest.CONNECTION_TIMEOUT, zkClientWatchers[i]); } waitForClientsConnected(); } private void waitForClientsConnected() throws Exception { for (int i = 0; i < zkClients.length; i++) { zkClientWatchers[i].waitForConnected(ClientTest.CONNECTION_TIMEOUT); zkClientWatchers[i].reset(); } } /** * Ensure all clients are able to talk to the service. */ private void checkClientsConnected() throws Exception { for (int i = 0; i < zkClients.length; i++) { checkClientConnected(i + 1); } } /** * Ensure the client is able to talk to the server. * * @param idx the idx of the server the client is talking to */ private void checkClientConnected(int idx) throws Exception { ZooKeeper zk = getClient(idx); if (zk == null) { return; } try { assertNull(zk.exists("/foofoofoo-connected", false)); } catch (ConnectionLossException e) { // second chance... // in some cases, leader change in particular, the timing is // very tricky to get right in order to assure that the client has // disconnected and reconnected. In some cases the client will // disconnect, then attempt to reconnect before the server is // back, in which case we'll see another connloss on the operation // in the try, this catches that case and waits for the server // to come back PeerStruct peer = qu.getPeer(idx); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + peer.clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server down"); assertNull(zk.exists("/foofoofoo-connected", false)); } } /** * Ensure all clients are disconnected from the service. */ private void checkClientsDisconnected() throws Exception { for (int i = 0; i < zkClients.length; i++) { checkClientDisconnected(i + 1); } } /** * Ensure the client is able to talk to the server * * @param idx the idx of the server the client is talking to */ private void checkClientDisconnected(int idx) throws Exception { ZooKeeper zk = getClient(idx); if (zk == null) { return; } try { assertNull(zk.exists("/foofoofoo-disconnected", false)); fail("expected client to be disconnected"); } catch (KeeperException e) { // success } } private void startAll() throws Exception { qu.startAll(); checkLeader(); // all clients should be connected checkClientsConnected(); } private void start(int idx) throws Exception { qu.start(idx); for (String hp : qu.getConnString().split(",")) { assertTrue(ClientBase.waitForServerUp(hp, ClientTest.CONNECTION_TIMEOUT), "waiting for server up"); } checkLeader(); // all clients should be connected checkClientsConnected(); } private void checkLeader() { idxLeader = 1; while (qu.getPeer(idxLeader).peer.leader == null) { idxLeader++; } idxFollower = (idxLeader == 1 ? 2 : 1); zksLeader = qu.getPeer(idxLeader).peer.getActiveServer(); } private void shutdownAll() throws Exception { qu.shutdownAll(); // all clients should be disconnected checkClientsDisconnected(); } private void shutdown(int idx) throws Exception { qu.shutdown(idx); // leader will shutdown, remaining followers will elect a new leader PeerStruct peer = qu.getPeer(idx); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + peer.clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server down"); // if idx is the the leader then everyone will get disconnected, // otherwise if idx is a follower then just that client will get // disconnected if (idx == idxLeader) { checkClientDisconnected(idx); try { checkClientsDisconnected(); } catch (AssertionError e) { // the clients may or may not have already reconnected // to the recovered cluster, force a check, but ignore } } else { checkClientDisconnected(idx); } } /** Reset the next zxid to be near epoch end */ private void adjustEpochNearEnd() { zksLeader.setZxid((zksLeader.getZxid() & 0xffffffff00000000L) | 0xfffffffcL); } @AfterEach public void tearDown() throws Exception { System.clearProperty("zookeeper.test.allowDiscontinuousProposals"); LOG.info("tearDown starting"); for (int i = 0; i < zkClients.length; i++) { zkClients[i].close(); } qu.shutdownAll(); } /** * Create the znodes, this may fail if the lower 32 roll over, if so * wait for the clients to be re-connected after the re-election */ private int createNodes(ZooKeeper zk, int start, int count) throws Exception { LOG.info("Creating nodes {} thru {}", start, (start + count)); int j = 0; try { for (int i = start; i < start + count; i++) { zk.create("/foo" + i, new byte[0], Ids.READ_ACL_UNSAFE, CreateMode.EPHEMERAL); j++; } } catch (ConnectionLossException e) { // this is ok - the leader has dropped leadership waitForClientsConnected(); } return j; } /** * Verify the expected znodes were created and that the last znode, which * caused the roll-over, did not. */ private void checkNodes(ZooKeeper zk, int start, int count) throws Exception { LOG.info("Validating nodes {} thru {}", start, (start + count)); for (int i = start; i < start + count; i++) { assertNotNull(zk.exists("/foo" + i, false)); LOG.error("Exists zxid:{}", Long.toHexString(zk.exists("/foo" + i, false).getCzxid())); } assertNull(zk.exists("/foo" + (start + count), false)); } /** * Prior to the fix this test would hang for a while, then fail with * connection loss. */ @Test public void testSimpleRolloverFollower() throws Exception { adjustEpochNearEnd(); ZooKeeper zk = getClient((idxLeader == 1 ? 2 : 1)); int countCreated = createNodes(zk, 0, 10); checkNodes(zk, 0, countCreated); } /** * Similar to testSimpleRollover, but ensure the cluster comes back, * has the right data, and is able to serve new requests. */ @Test public void testRolloverThenRestart() throws Exception { ZooKeeper zk = getClient(idxFollower); int countCreated = createNodes(zk, 0, 10); adjustEpochNearEnd(); countCreated += createNodes(zk, countCreated, 10); shutdownAll(); startAll(); zk = getClient(idxLeader); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); adjustEpochNearEnd(); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdownAll(); startAll(); zk = getClient(idxFollower); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdownAll(); startAll(); zk = getClient(idxLeader); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); // sanity check assertTrue(countCreated > 0); assertTrue(countCreated < 60); } /** * Similar to testRolloverThenRestart, but ensure a follower comes back, * has the right data, and is able to serve new requests. */ @Test public void testRolloverThenFollowerRestart() throws Exception { ZooKeeper zk = getClient(idxFollower); int countCreated = createNodes(zk, 0, 10); adjustEpochNearEnd(); countCreated += createNodes(zk, countCreated, 10); shutdown(idxFollower); start(idxFollower); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); adjustEpochNearEnd(); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdown(idxFollower); start(idxFollower); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdown(idxFollower); start(idxFollower); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); // sanity check assertTrue(countCreated > 0); assertTrue(countCreated < 60); } /** * Similar to testRolloverThenRestart, but ensure leadership can change, * comes back, has the right data, and is able to serve new requests. */ @Test public void testRolloverThenLeaderRestart() throws Exception { ZooKeeper zk = getClient(idxLeader); int countCreated = createNodes(zk, 0, 10); adjustEpochNearEnd(); checkNodes(zk, 0, countCreated); shutdown(idxLeader); start(idxLeader); zk = getClient(idxLeader); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); adjustEpochNearEnd(); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdown(idxLeader); start(idxLeader); zk = getClient(idxLeader); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdown(idxLeader); start(idxLeader); zk = getClient(idxFollower); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); // sanity check assertTrue(countCreated > 0); assertTrue(countCreated < 50); } /** * Similar to testRolloverThenRestart, but ensure we can survive multiple * epoch rollovers between restarts. */ @Test public void testMultipleRollover() throws Exception { ZooKeeper zk = getClient(idxFollower); int countCreated = createNodes(zk, 0, 10); adjustEpochNearEnd(); countCreated += createNodes(zk, countCreated, 10); adjustEpochNearEnd(); countCreated += createNodes(zk, countCreated, 10); adjustEpochNearEnd(); countCreated += createNodes(zk, countCreated, 10); adjustEpochNearEnd(); countCreated += createNodes(zk, countCreated, 10); shutdownAll(); startAll(); zk = getClient(idxFollower); adjustEpochNearEnd(); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); shutdown(idxLeader); start(idxLeader); zk = getClient(idxFollower); checkNodes(zk, 0, countCreated); countCreated += createNodes(zk, countCreated, 10); // sanity check assertTrue(countCreated > 0); assertTrue(countCreated < 70); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000166 15051152474 032714 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthT0100644 0000000 0000000 00000043646 15051152474 034207 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE; import static org.apache.zookeeper.server.admin.Commands.AUTH_INFO_SEPARATOR; import static org.apache.zookeeper.server.admin.Commands.ROOT_PATH; import static org.apache.zookeeper.server.admin.JettyAdminServerTest.HTTPS_URL_FORMAT; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.QuorumX509Util; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.NettyServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.auth.X509AuthenticationProvider; import org.apache.zookeeper.test.ClientBase; import org.eclipse.jetty.http.HttpHeader; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class CommandAuthTest extends ZKTestCase { private static final String DIGEST_SCHEMA = "digest"; private static final String X509_SCHEMA = "x509"; private static final String IP_SCHEMA = "ip"; private static final String ROOT_USER = "root"; private static final String ROOT_PASSWORD = "root_passwd"; private static final String AUTH_TEST_COMMAND_NAME = "authtest"; private static final String X509_SUBJECT_PRINCIPAL = "CN=localhost,OU=ZooKeeper,O=Apache,L=Unknown,ST=Unknown,C=Unknown"; public enum AuthSchema { DIGEST, X509, IP } private final int jettyAdminPort = PortAssignment.unique(); private final String hostPort = "127.0.0.1:" + PortAssignment.unique(); private final ClientX509Util clientX509Util = new ClientX509Util(); private final QuorumX509Util quorumX509Util = new QuorumX509Util(); private ZooKeeperServer zks; private ServerCnxnFactory cnxnFactory; private JettyAdminServer adminServer; private ZooKeeper zk; @TempDir static File dataDir; @TempDir static File logDir; @BeforeAll public void setup() throws Exception { Commands.registerCommand(new AuthTestCommand(true, ZooDefs.Perms.ALL, ROOT_PATH)); setupTLS(); // start ZookeeperServer System.setProperty("zookeeper.4lw.commands.whitelist", "*"); zks = new ZooKeeperServer(dataDir, logDir, 3000); final int port = Integer.parseInt(hostPort.split(":")[1]); cnxnFactory = ServerCnxnFactory.createFactory(port, -1); cnxnFactory.startup(zks); assertTrue(ClientBase.waitForServerUp(hostPort, 120000)); // start AdminServer System.setProperty("zookeeper.admin.enableServer", "true"); System.setProperty("zookeeper.admin.serverPort", String.valueOf(jettyAdminPort)); adminServer = new JettyAdminServer(); adminServer.setZooKeeperServer(zks); adminServer.start(); } @AfterAll public void tearDown() throws Exception { clearTLS(); System.clearProperty("zookeeper.4lw.commands.whitelist"); System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.admin.serverPort"); if (adminServer != null) { adminServer.shutdown(); } if (cnxnFactory != null) { cnxnFactory.shutdown(); } if (zks != null) { zks.shutdown(); } } @BeforeEach public void setupEach() throws Exception { zk = ClientBase.createZKClient(hostPort); } @AfterEach public void tearDownEach() throws Exception { if (zk != null) { zk.close(); } } @ParameterizedTest @EnumSource(AuthSchema.class) public void testAuthCheck_authorized(final AuthSchema authSchema) throws Exception { setupRootACL(authSchema); try { final HttpURLConnection authTestConn = sendAuthTestCommandRequest(authSchema, true); assertEquals(HttpURLConnection.HTTP_OK, authTestConn.getResponseCode()); } finally { addAuthInfo(zk, authSchema); resetRootACL(zk); } } @ParameterizedTest @EnumSource(value = AuthSchema.class, names = {"DIGEST"}) public void testAuthCheck_notAuthorized(final AuthSchema authSchema) throws Exception { setupRootACL(authSchema); try { final HttpURLConnection authTestConn = sendAuthTestCommandRequest(authSchema, false); assertEquals(HttpURLConnection.HTTP_FORBIDDEN, authTestConn.getResponseCode()); } finally { addAuthInfo(zk, authSchema); resetRootACL(zk); } } @ParameterizedTest @EnumSource(AuthSchema.class) public void testAuthCheck_noACL(final AuthSchema authSchema) throws Exception { final HttpURLConnection authTestConn = sendAuthTestCommandRequest(authSchema, false); assertEquals(HttpURLConnection.HTTP_OK, authTestConn.getResponseCode()); } @ParameterizedTest @EnumSource(value = AuthSchema.class, names = {"DIGEST"}) public void testAuthCheck_noPerms(final AuthSchema authSchema) throws Exception { // The extra ACL entry gives Perms.READ perms to the "invalid" // DIGEST authInfo---but that should not permit access, as // AuthTestCommand requires Perms.ADMIN. setupRootACL(authSchema, ZooDefs.Ids.READ_ACL_UNSAFE); try { final HttpURLConnection authTestConn = sendAuthTestCommandRequest(authSchema, false); assertEquals(HttpURLConnection.HTTP_FORBIDDEN, authTestConn.getResponseCode()); } finally { addAuthInfo(zk, authSchema); resetRootACL(zk); } } @Test public void testAuthCheck_invalidServerRequiredConfig() { assertThrows("An active server is required for auth check", IllegalArgumentException.class, () -> new AuthTestCommand(false, ZooDefs.Perms.ALL, ROOT_PATH)); } @Test public void testAuthCheck_noAuthInfo() { testAuthCheck_invalidAuthInfo(null); } @Test public void testAuthCheck_noAuthInfoSeparator() { final String invalidAuthInfo = String.format("%s%s%s:%s", DIGEST_SCHEMA, "", ROOT_USER, ROOT_PASSWORD); testAuthCheck_invalidAuthInfo(invalidAuthInfo); } @Test public void testAuthCheck_invalidAuthInfoSeparator() { final String invalidAuthInfo = String.format("%s%s%s:%s", DIGEST_SCHEMA, ":", ROOT_USER, ROOT_PASSWORD); testAuthCheck_invalidAuthInfo(invalidAuthInfo); } @Test public void testAuthCheck_invalidAuthSchema() { final String invalidAuthInfo = String.format("%s%s%s:%s", "InvalidAuthSchema", AUTH_INFO_SEPARATOR, ROOT_USER, ROOT_PASSWORD); testAuthCheck_invalidAuthInfo(invalidAuthInfo); } @Test public void testAuthCheck_authProviderNotFound() { final String invalidAuthInfo = String.format("%s%s%s:%s", "sasl", AUTH_INFO_SEPARATOR, ROOT_USER, ROOT_PASSWORD); testAuthCheck_invalidAuthInfo(invalidAuthInfo); } private void testAuthCheck_invalidAuthInfo(final String invalidAuthInfo) { final CommandResponse commandResponse = Commands.runGetCommand(AUTH_TEST_COMMAND_NAME, zks, new HashMap<>(), invalidAuthInfo, null); assertEquals(HttpServletResponse.SC_UNAUTHORIZED, commandResponse.getStatusCode()); } private static class AuthTestCommand extends GetCommand { public AuthTestCommand(final boolean serverRequired, final int perm, final String path) { super(Arrays.asList(AUTH_TEST_COMMAND_NAME, "at"), serverRequired, new AuthRequest(perm, path)); } @Override public CommandResponse runGet(ZooKeeperServer zkServer, Map kwargs) { return initializeResponse(); } } private void setupTLS() throws Exception { System.setProperty("zookeeper.authProvider.x509", "org.apache.zookeeper.server.auth.X509AuthenticationProvider"); String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); System.setProperty(clientX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(clientX509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty(clientX509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(clientX509Util.getSslTruststorePasswdProperty(), "testpass"); // client System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); // server System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(NettyServerCnxnFactory.PORT_UNIFICATION_KEY, Boolean.TRUE.toString()); // admin server System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(quorumX509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty(quorumX509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(quorumX509Util.getSslTruststorePasswdProperty(), "testpass"); System.setProperty("zookeeper.admin.forceHttps", "true"); System.setProperty("zookeeper.admin.needClientAuth", "true"); // create SSLContext final SSLContext sslContext = SSLContext.getInstance(ClientX509Util.DEFAULT_PROTOCOL); final X509AuthenticationProvider authProvider = (X509AuthenticationProvider) ProviderRegistry.getProvider("x509"); if (authProvider == null) { throw new X509Exception.SSLContextException("Could not create SSLContext with x509 auth provider"); } sslContext.init(new X509KeyManager[]{authProvider.getKeyManager()}, new X509TrustManager[]{authProvider.getTrustManager()}, null); // set SSLSocketFactory HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); } public void clearTLS() { System.clearProperty("zookeeper.authProvider.x509"); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); // client side System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ZKClientConfig.SECURE_CLIENT); // server side System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(NettyServerCnxnFactory.PORT_UNIFICATION_KEY); // admin server System.clearProperty(quorumX509Util.getSslKeystoreLocationProperty()); System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); System.clearProperty(quorumX509Util.getSslTruststoreLocationProperty()); System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); System.clearProperty("zookeeper.admin.forceHttps"); System.clearProperty("zookeeper.admin.needClientAuth"); } private void setupRootACL(final AuthSchema authSchema) throws Exception { setupRootACL(authSchema, Collections.emptyList()); } private void setupRootACL(final AuthSchema authSchema, final List extraEntries) throws Exception { final List aclEntries = new ArrayList<>(); switch (authSchema) { case DIGEST: aclEntries.addAll(genACLForDigest()); break; case X509: aclEntries.addAll(genACLForX509()); break; case IP: aclEntries.addAll(genACLForIP()); break; default: throw new IllegalArgumentException("Unknown auth schema"); } aclEntries.addAll(extraEntries); zk.setACL(Commands.ROOT_PATH, aclEntries, -1); } private HttpURLConnection sendAuthTestCommandRequest(final AuthSchema authSchema, final boolean validAuthInfo) throws Exception { final URL authTestURL = new URL(String.format(HTTPS_URL_FORMAT + "/" + AUTH_TEST_COMMAND_NAME, jettyAdminPort)); final HttpURLConnection authTestConn = (HttpURLConnection) authTestURL.openConnection(); addAuthHeader(authTestConn, authSchema, validAuthInfo); authTestConn.setRequestMethod("GET"); return authTestConn; } private void addAuthInfo(final ZooKeeper zk, final AuthSchema authSchema) { switch (authSchema) { case DIGEST: addAuthInfoForDigest(zk); break; case X509: addAuthInfoForX509(zk); break; case IP: addAuthInfoForIP(zk); break; default: throw new IllegalArgumentException("Unknown auth schema"); } } public static void resetRootACL(final ZooKeeper zk) throws Exception { zk.setACL(Commands.ROOT_PATH, OPEN_ACL_UNSAFE, -1); } public static List genACLForDigest() throws Exception { final String idPassword = String.format("%s:%s", ROOT_USER, ROOT_PASSWORD); final String digest = DigestAuthenticationProvider.generateDigest(idPassword); final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(DIGEST_SCHEMA, digest)); return Collections.singletonList(acl); } private static List genACLForX509() throws Exception { final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(X509_SCHEMA, X509_SUBJECT_PRINCIPAL)); return Collections.singletonList(acl); } private static List genACLForIP() throws Exception { final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(IP_SCHEMA, "127.0.0.1")); return Collections.singletonList(acl); } public static void addAuthInfoForDigest(final ZooKeeper zk) { final String idPassword = String.format("%s:%s", ROOT_USER, ROOT_PASSWORD); zk.addAuthInfo(DIGEST_SCHEMA, idPassword.getBytes(StandardCharsets.UTF_8)); } public static void addAuthInfoForX509(final ZooKeeper zk) { zk.addAuthInfo(X509_SCHEMA, X509_SUBJECT_PRINCIPAL.getBytes(StandardCharsets.UTF_8)); } private void addAuthInfoForIP(final ZooKeeper zk) { zk.addAuthInfo(IP_SCHEMA, "127.0.0.1".getBytes(StandardCharsets.UTF_8)); } public static void addAuthHeader(final HttpURLConnection conn, final AuthSchema authSchema, final boolean validAuthInfo) { String authInfo; switch (authSchema) { case DIGEST: authInfo = validAuthInfo ? buildAuthorizationForDigest() : buildInvalidAuthorizationForDigest(); break; case X509: authInfo = buildAuthorizationForX509(); break; case IP: authInfo = buildAuthorizationForIP(); break; default: throw new IllegalArgumentException("Unknown auth schema"); } conn.setRequestProperty(HttpHeader.AUTHORIZATION.asString(), authInfo); } public static String buildAuthorizationForDigest() { return String.format("%s%s%s:%s", DIGEST_SCHEMA, Commands.AUTH_INFO_SEPARATOR, ROOT_USER, ROOT_PASSWORD); } private static String buildInvalidAuthorizationForDigest() { return String.format("%s%s%s:%s", DIGEST_SCHEMA, Commands.AUTH_INFO_SEPARATOR, "InvalidUser", "InvalidPassword"); } private static String buildAuthorizationForX509() { return String.format("%s%s", X509_SCHEMA, Commands.AUTH_INFO_SEPARATOR); } private static String buildAuthorizationForIP() { return String.format("%s%s", IP_SCHEMA, Commands.AUTH_INFO_SEPARATOR); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000172 15051152474 032711 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandResponseTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandRespo0100644 0000000 0000000 00000004522 15051152474 034240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class CommandResponseTest extends ZKTestCase { private CommandResponse r; @BeforeEach public void setUp() throws Exception { r = new CommandResponse("makemeasandwich", "makeityourself", HttpServletResponse.SC_OK); } @Test public void testGetters() { assertEquals("makemeasandwich", r.getCommand()); assertEquals("makeityourself", r.getError()); assertEquals(HttpServletResponse.SC_OK, r.getStatusCode()); assertEquals(new HashMap(), r.getHeaders()); assertNull(r.getInputStream()); } @Test public void testMap() { r.put("missing", "sudo"); Map m = new HashMap<>(); m.put("origin", "xkcd"); m.put("url", "http://xkcd.com/149/"); r.putAll(m); Map rmap = r.toMap(); assertEquals(5, rmap.size()); assertEquals("makemeasandwich", rmap.get(CommandResponse.KEY_COMMAND)); assertEquals("makeityourself", rmap.get(CommandResponse.KEY_ERROR)); assertEquals("sudo", rmap.get("missing")); assertEquals("xkcd", rmap.get("origin")); assertEquals("http://xkcd.com/149/", rmap.get("url")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000163 15051152474 032711 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandsTest0100644 0000000 0000000 00000044227 15051152474 034260 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.apache.zookeeper.server.ZooKeeperServer.ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED; import static org.apache.zookeeper.server.admin.Commands.ADMIN_RATE_LIMITER_INTERVAL; import static org.apache.zookeeper.server.admin.Commands.RestoreCommand.ADMIN_RESTORE_ENABLED; import static org.apache.zookeeper.server.admin.Commands.SnapshotCommand.ADMIN_SNAPSHOT_ENABLED; import static org.apache.zookeeper.server.admin.Commands.SnapshotCommand.REQUEST_QUERY_PARAM_STREAMING; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.BufferStats; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; public class CommandsTest extends ClientBase { /** * Checks that running a given Command returns the expected Map. Asserts * that all specified keys are present with values of the specified types * and that there are no extra entries. * * @param cmdName * - the primary name of the command * @param kwargs * - keyword arguments to the command * @param inputStream * - InputStream to the command * @param authInfo * - authInfo for the command * @param expectedHeaders * - expected HTTP response headers * @param expectedStatusCode * - expected HTTP status code * @param fields * - the fields that are expected in the returned Map * @throws IOException * @throws InterruptedException */ private void testCommand(String cmdName, Map kwargs, InputStream inputStream, String authInfo, Map expectedHeaders, int expectedStatusCode, Field... fields) throws IOException, InterruptedException { ZooKeeperServer zks = serverFactory.getZooKeeperServer(); final CommandResponse commandResponse = inputStream == null ? Commands.runGetCommand(cmdName, zks, kwargs, authInfo, null) : Commands.runPostCommand(cmdName, zks, inputStream, authInfo, null); assertNotNull(commandResponse); assertEquals(expectedStatusCode, commandResponse.getStatusCode()); try (final InputStream responseStream = commandResponse.getInputStream()) { if (Boolean.parseBoolean(kwargs.getOrDefault(REQUEST_QUERY_PARAM_STREAMING, "false"))) { assertNotNull(responseStream, "InputStream in the response of command " + cmdName + " should not be null"); } else { Map result = commandResponse.toMap(); assertTrue(result.containsKey("command")); // This is only true because we're setting cmdName to the primary name assertEquals(cmdName, result.remove("command")); assertTrue(result.containsKey("error")); assertNull(result.remove("error"), "error: " + result.get("error")); for (Field field : fields) { String k = field.key; assertTrue(result.containsKey(k), "Result from command " + cmdName + " missing field \"" + k + "\"" + "\n" + result); Class t = field.type; Object v = result.remove(k); assertTrue(t.isAssignableFrom(v.getClass()), "\"" + k + "\" field from command " + cmdName + " should be of type " + t + ", is actually of type " + v.getClass()); } assertTrue(result.isEmpty(), "Result from command " + cmdName + " contains extra fields: " + result); } } assertEquals(expectedHeaders, commandResponse.getHeaders()); } public void testCommand(String cmdName, Field... fields) throws IOException, InterruptedException { testCommand(cmdName, new HashMap<>(), null, null, new HashMap<>(), HttpServletResponse.SC_OK, fields); } private static class Field { String key; Class type; Field(String key, Class type) { this.key = key; this.type = type; } } @Test public void testConfiguration() throws IOException, InterruptedException { testCommand("configuration", new Field("client_port", Integer.class), new Field("data_dir", String.class), new Field("data_log_dir", String.class), new Field("tick_time", Integer.class), new Field("max_client_cnxns", Integer.class), new Field("min_session_timeout", Integer.class), new Field("max_session_timeout", Integer.class), new Field("server_id", Long.class), new Field("client_port_listen_backlog", Integer.class)); } @Test public void testConnections() throws IOException, InterruptedException { testCommand("connections", new Field("connections", Iterable.class), new Field("secure_connections", Iterable.class)); } @Test public void testObservers() throws IOException, InterruptedException { testCommand("observers", new Field("synced_observers", Integer.class), new Field("observers", Iterable.class)); } @Test public void testObserverConnectionStatReset() throws IOException, InterruptedException { testCommand("observer_connection_stat_reset"); } @Test public void testConnectionStatReset() throws IOException, InterruptedException { testCommand("connection_stat_reset"); } @Test public void testDump() throws IOException, InterruptedException { testCommand("dump", new Field("expiry_time_to_session_ids", Map.class), new Field("session_id_to_ephemeral_paths", Map.class)); } @Test public void testEnvironment() throws IOException, InterruptedException { testCommand("environment", new Field("zookeeper.version", String.class), new Field("host.name", String.class), new Field("java.version", String.class), new Field("java.vendor", String.class), new Field("java.home", String.class), new Field("java.class.path", String.class), new Field("java.library.path", String.class), new Field("java.io.tmpdir", String.class), new Field("java.compiler", String.class), new Field("os.name", String.class), new Field("os.arch", String.class), new Field("os.version", String.class), new Field("user.name", String.class), new Field("user.home", String.class), new Field("user.dir", String.class), new Field("os.memory.free", String.class), new Field("os.memory.max", String.class), new Field("os.memory.total", String.class)); } @Test public void testGetTraceMask() throws IOException, InterruptedException { testCommand("get_trace_mask", new Field("tracemask", Long.class)); } @Test public void testIsReadOnly() throws IOException, InterruptedException { testCommand("is_read_only", new Field("read_only", Boolean.class)); } @Test public void testLastSnapshot() throws IOException, InterruptedException { testCommand("last_snapshot", new Field("zxid", String.class), new Field("timestamp", Long.class)); } @Test public void testMonitor() throws IOException, InterruptedException { ArrayList fields = new ArrayList<>(Arrays.asList( new Field("version", String.class), new Field("avg_latency", Double.class), new Field("max_latency", Long.class), new Field("min_latency", Long.class), new Field("packets_received", Long.class), new Field("packets_sent", Long.class), new Field("num_alive_connections", Integer.class), new Field("outstanding_requests", Long.class), new Field("server_state", String.class), new Field("znode_count", Integer.class), new Field("watch_count", Integer.class), new Field("ephemerals_count", Integer.class), new Field("approximate_data_size", Long.class), new Field("open_file_descriptor_count", Long.class), new Field("max_file_descriptor_count", Long.class), new Field("last_client_response_size", Integer.class), new Field("max_client_response_size", Integer.class), new Field("min_client_response_size", Integer.class), new Field("auth_failed_count", Long.class), new Field("non_mtls_remote_conn_count", Long.class), new Field("non_mtls_local_conn_count", Long.class), new Field("uptime", Long.class), new Field("global_sessions", Long.class), new Field("local_sessions", Long.class), new Field("connection_drop_probability", Double.class), new Field("outstanding_tls_handshake", Integer.class) )); Map metrics = MetricsUtils.currentServerMetrics(); for (String metric : metrics.keySet()) { boolean alreadyDefined = fields.stream().anyMatch(f -> { return f.key.equals(metric); }); if (alreadyDefined) { // known metrics are defined statically in the block above continue; } if (metric.startsWith("avg_")) { fields.add(new Field(metric, Double.class)); } else { fields.add(new Field(metric, Long.class)); } } Field[] fieldsArray = fields.toArray(new Field[0]); testCommand("monitor", fieldsArray); } @Test public void testRuok() throws IOException, InterruptedException { testCommand("ruok"); } @Test public void testRestore_invalidInputStream() throws IOException, InterruptedException { setupForRestoreCommand(); try (final InputStream inputStream = new ByteArrayInputStream("Invalid snapshot data".getBytes())){ final Map kwargs = new HashMap<>(); final Map expectedHeaders = new HashMap<>(); final String authInfo = CommandAuthTest.buildAuthorizationForDigest(); testCommand("restore", kwargs, inputStream, authInfo, expectedHeaders, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } finally { clearForRestoreCommand(); } } @Test public void testRestore_nullInputStream() { setupForRestoreCommand(); final ZooKeeperServer zks = serverFactory.getZooKeeperServer(); try { final String authInfo = CommandAuthTest.buildAuthorizationForDigest(); final CommandResponse commandResponse = Commands.runPostCommand("restore", zks, null, authInfo, null); assertNotNull(commandResponse); assertEquals(HttpServletResponse.SC_BAD_REQUEST, commandResponse.getStatusCode()); } finally { clearForRestoreCommand(); if (zks != null) { zks.shutdown(); } } } @Test public void testSnapshot_streaming() throws IOException, InterruptedException { testSnapshot(true); } @Test public void testSnapshot_nonStreaming() throws IOException, InterruptedException { testSnapshot(false); } @Test public void testServerStats() throws IOException, InterruptedException { testCommand("server_stats", new Field("version", String.class), new Field("read_only", Boolean.class), new Field("server_stats", ServerStats.class), new Field("node_count", Integer.class), new Field("client_response", BufferStats.class)); } @Test public void testSetTraceMask() throws IOException, InterruptedException { Map kwargs = new HashMap<>(); kwargs.put("traceMask", "1"); testCommand("set_trace_mask", kwargs, null, null, new HashMap<>(), HttpServletResponse.SC_OK, new Field("tracemask", Long.class)); } @Test public void testStat() throws IOException, InterruptedException { testCommand("stats", new Field("version", String.class), new Field("read_only", Boolean.class), new Field("server_stats", ServerStats.class), new Field("node_count", Integer.class), new Field("connections", Iterable.class), new Field("secure_connections", Iterable.class), new Field("client_response", BufferStats.class)); } @Test public void testStatReset() throws IOException, InterruptedException { testCommand("stat_reset"); } @Test public void testWatches() throws IOException, InterruptedException { testCommand("watches", new Field("session_id_to_watched_paths", Map.class)); } @Test public void testWatchesByPath() throws IOException, InterruptedException { testCommand("watches_by_path", new Field("path_to_session_ids", Map.class)); } @Test public void testWatchSummary() throws IOException, InterruptedException { testCommand("watch_summary", new Field("num_connections", Integer.class), new Field("num_paths", Integer.class), new Field("num_total_watches", Integer.class)); } @Test public void testVotingViewCommand() throws IOException, InterruptedException { testCommand("voting_view", new Field("current_config", Map.class)); } @Test public void testConsCommandSecureOnly() { // Arrange Commands.ConsCommand cmd = new Commands.ConsCommand(); ZooKeeperServer zkServer = mock(ZooKeeperServer.class); ServerCnxnFactory cnxnFactory = mock(ServerCnxnFactory.class); when(zkServer.getSecureServerCnxnFactory()).thenReturn(cnxnFactory); // Act CommandResponse response = cmd.runGet(zkServer, null); // Assert assertThat(response.toMap().containsKey("connections"), is(true)); assertThat(response.toMap().containsKey("secure_connections"), is(true)); } /** * testing Stat command, when only SecureClientPort is defined by the user and there is no * regular (non-SSL port) open. In this case zkServer.getServerCnxnFactory === null * see: ZOOKEEPER-3633 */ @Test public void testStatCommandSecureOnly() { Commands.StatCommand cmd = new Commands.StatCommand(); ZooKeeperServer zkServer = mock(ZooKeeperServer.class); ServerCnxnFactory cnxnFactory = mock(ServerCnxnFactory.class); ServerStats serverStats = mock(ServerStats.class); ZKDatabase zkDatabase = mock(ZKDatabase.class); when(zkServer.getSecureServerCnxnFactory()).thenReturn(cnxnFactory); when(zkServer.serverStats()).thenReturn(serverStats); when(zkServer.getZKDatabase()).thenReturn(zkDatabase); when(zkDatabase.getNodeCount()).thenReturn(0); CommandResponse response = cmd.runGet(zkServer, null); assertThat(response.toMap().containsKey("connections"), is(true)); assertThat(response.toMap().containsKey("secure_connections"), is(true)); } private void testSnapshot(final boolean streaming) throws IOException, InterruptedException { System.setProperty(ADMIN_SNAPSHOT_ENABLED, "true"); System.setProperty(ADMIN_RATE_LIMITER_INTERVAL, "0"); System.setProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED, "true"); try { final Map kwargs = new HashMap<>(); kwargs.put(REQUEST_QUERY_PARAM_STREAMING, String.valueOf(streaming)); final String autInfo = CommandAuthTest.buildAuthorizationForDigest(); final Map expectedHeaders = new HashMap<>(); expectedHeaders.put(Commands.SnapshotCommand.RESPONSE_HEADER_LAST_ZXID, "0x0"); expectedHeaders.put(Commands.SnapshotCommand.RESPONSE_HEADER_SNAPSHOT_SIZE, "478"); testCommand("snapshot", kwargs, null, autInfo, expectedHeaders, HttpServletResponse.SC_OK); } finally { System.clearProperty(ADMIN_SNAPSHOT_ENABLED); System.clearProperty(ADMIN_RATE_LIMITER_INTERVAL); System.clearProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED); } } private void setupForRestoreCommand() { System.setProperty(ADMIN_RESTORE_ENABLED, "true"); System.setProperty(ADMIN_RATE_LIMITER_INTERVAL, "0"); System.setProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED, "true"); } private void clearForRestoreCommand() { System.clearProperty(ADMIN_RESTORE_ENABLED); System.clearProperty(ADMIN_RATE_LIMITER_INTERVAL); System.clearProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000173 15051152474 032712 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/JettyAdminServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/JettyAdminSe0100644 0000000 0000000 00000032463 15051152474 034216 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.SocketException; import java.net.URL; import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.Security; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.KeyStoreFileType; import org.apache.zookeeper.common.SecretUtilsTest; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.common.X509KeyType; import org.apache.zookeeper.common.X509TestContext; import org.apache.zookeeper.server.ZooKeeperServerMainTest; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JettyAdminServerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(JettyAdminServerTest.class); static final String URL_FORMAT = "http://localhost:%d/commands"; static final String HTTPS_URL_FORMAT = "https://localhost:%d/commands"; private final int jettyAdminPort = PortAssignment.unique(); @BeforeEach public void enableServer() { // Override setting in ZKTestCase System.setProperty("zookeeper.admin.enableServer", "true"); System.setProperty("zookeeper.admin.serverPort", "" + jettyAdminPort); } @BeforeEach public void setupEncryption() { Security.addProvider(new BouncyCastleProvider()); File tmpDir = null; X509TestContext x509TestContext = null; try { tmpDir = ClientBase.createEmptyTestDir(); x509TestContext = X509TestContext.newBuilder() .setTempDir(tmpDir) .setKeyStorePassword("") .setKeyStoreKeyType(X509KeyType.EC) .setTrustStorePassword("") .setTrustStoreKeyType(X509KeyType.EC) .build(); System.setProperty( "zookeeper.ssl.quorum.keyStore.location", x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM).getAbsolutePath()); System.setProperty( "zookeeper.ssl.quorum.trustStore.location", x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM).getAbsolutePath()); } catch (Exception e) { LOG.info("Problems encountered while setting up encryption for Jetty admin server test", e); } System.setProperty("zookeeper.ssl.quorum.keyStore.password", ""); System.setProperty("zookeeper.ssl.quorum.keyStore.type", "PEM"); System.setProperty("zookeeper.ssl.quorum.trustStore.password", ""); System.setProperty("zookeeper.ssl.quorum.trustStore.type", "PEM"); System.setProperty("zookeeper.admin.portUnification", "true"); // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } }}; // Create all-trusting trust manager SSLContext sc = null; try { sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); } catch (Exception e) { LOG.error("Failed to customize encryption for HTTPS", e); } // Create all-trusting hostname verifier HostnameVerifier allValid = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; // This is a temporary fix while we do not yet have certificates set up to make // HTTPS requests correctly. This is equivalent to the "-k" option in curl. HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier(allValid); } @AfterEach public void cleanUp() { Security.removeProvider("BC"); System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.admin.serverPort"); System.clearProperty("zookeeper.ssl.quorum.keyStore.location"); System.clearProperty("zookeeper.ssl.quorum.keyStore.password"); System.clearProperty("zookeeper.ssl.quorum.keyStore.passwordPath"); System.clearProperty("zookeeper.ssl.quorum.keyStore.type"); System.clearProperty("zookeeper.ssl.quorum.trustStore.location"); System.clearProperty("zookeeper.ssl.quorum.trustStore.password"); System.clearProperty("zookeeper.ssl.quorum.trustStore.passwordPath"); System.clearProperty("zookeeper.ssl.quorum.trustStore.type"); System.clearProperty("zookeeper.admin.portUnification"); System.clearProperty("zookeeper.admin.forceHttps"); } /** * Tests that we can start and query a JettyAdminServer. */ @Test public void testJettyAdminServer() throws AdminServerException, IOException, SSLContextException, GeneralSecurityException { JettyAdminServer server = new JettyAdminServer(); try { server.start(); queryAdminServer(jettyAdminPort); traceAdminServer(jettyAdminPort); } finally { server.shutdown(); } } /** * Starts a standalone server and tests that we can query its AdminServer. */ @Test public void testStandalone() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); ZooKeeperServerMainTest.MainThread main = new ZooKeeperServerMainTest.MainThread(CLIENT_PORT, false, null); main.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server being up"); queryAdminServer(jettyAdminPort); main.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } /** * Starts a quorum of two servers and tests that we can query both AdminServers. */ @Test public void testQuorum() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); final int ADMIN_SERVER_PORT1 = PortAssignment.unique(); final int ADMIN_SERVER_PORT2 = PortAssignment.unique(); String quorumCfgSection = String.format( "server.1=127.0.0.1:%d:%d;%d\nserver.2=127.0.0.1:%d:%d;%d", PortAssignment.unique(), PortAssignment.unique(), CLIENT_PORT_QP1, PortAssignment.unique(), PortAssignment.unique(), CLIENT_PORT_QP2); QuorumPeerTestBase.MainThread q1 = new QuorumPeerTestBase.MainThread(1, CLIENT_PORT_QP1, ADMIN_SERVER_PORT1, quorumCfgSection, null); q1.start(); // Since JettyAdminServer reads a system property to determine its port, // make sure it initializes itself before setting the system property // again with the second port number Thread.sleep(500); QuorumPeerTestBase.MainThread q2 = new QuorumPeerTestBase.MainThread(2, CLIENT_PORT_QP2, ADMIN_SERVER_PORT2, quorumCfgSection, null); q2.start(); Thread.sleep(500); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 being up"); queryAdminServer(ADMIN_SERVER_PORT1); queryAdminServer(ADMIN_SERVER_PORT2); q1.shutdown(); q2.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); } @Test public void testForceHttpsPortUnificationEnabled() throws Exception { testForceHttps(true); } @Test public void testForceHttpsPortUnificationDisabled() throws Exception { testForceHttps(false); } @Test public void testForceHttps_withWrongPasswordFromFile() throws Exception { final Path secretFile = SecretUtilsTest.createSecretFile("" + "wrong"); System.setProperty("zookeeper.ssl.quorum.keyStore.passwordPath", secretFile.toString()); System.setProperty("zookeeper.ssl.quorum.trustStore.passwordPath", secretFile.toString()); assertThrows(IOException.class, () -> testForceHttps(false)); } private void testForceHttps(boolean portUnification) throws Exception { System.setProperty("zookeeper.admin.forceHttps", "true"); System.setProperty("zookeeper.admin.portUnification", String.valueOf(portUnification)); boolean httpsPassed = false; JettyAdminServer server = new JettyAdminServer(); try { server.start(); queryAdminServer(String.format(HTTPS_URL_FORMAT, jettyAdminPort), true); httpsPassed = true; queryAdminServer(String.format(URL_FORMAT, jettyAdminPort), false); fail("http call should have failed since forceHttps=true"); } catch (SocketException se) { //good } finally { server.shutdown(); } assertTrue(httpsPassed); } /** * Check that we can load the commands page of an AdminServer running at * localhost:port. (Note that this should work even if no zk server is set.) */ private void queryAdminServer(int port) throws IOException, SSLContextException { queryAdminServer(String.format(URL_FORMAT, port), false); queryAdminServer(String.format(HTTPS_URL_FORMAT, port), true); } /** * Check that loading urlStr results in a non-zero length response. */ private void queryAdminServer(String urlStr, boolean encrypted) throws IOException, SSLContextException { URL url = new URL(urlStr); BufferedReader dis; if (!encrypted) { dis = new BufferedReader(new InputStreamReader((url.openStream()))); } else { HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); dis = new BufferedReader(new InputStreamReader(conn.getInputStream())); } String line = dis.readLine(); assertTrue(line.length() > 0); } /** * Using TRACE method to visit admin server */ private void traceAdminServer(int port) throws IOException { traceAdminServer(String.format(URL_FORMAT, port)); traceAdminServer(String.format(HTTPS_URL_FORMAT, port)); } /** * Using TRACE method to visit admin server, the response should be 403 forbidden */ private void traceAdminServer(String urlStr) throws IOException { HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection(); conn.setRequestMethod("TRACE"); conn.connect(); assertEquals(HttpURLConnection.HTTP_FORBIDDEN, conn.getResponseCode()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000170 15051152474 032707 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/RestoreQuorumTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/RestoreQuoru0100644 0000000 0000000 00000012203 15051152474 034323 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.apache.zookeeper.server.ZooKeeperServer.ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED; import static org.apache.zookeeper.server.admin.Commands.ADMIN_RATE_LIMITER_INTERVAL; import static org.apache.zookeeper.server.admin.Commands.RestoreCommand.ADMIN_RESTORE_ENABLED; import static org.apache.zookeeper.server.admin.Commands.SnapshotCommand.ADMIN_SNAPSHOT_ENABLED; import static org.apache.zookeeper.server.admin.SnapshotAndRestoreCommandTest.performRestoreAndValidate; import static org.apache.zookeeper.server.admin.SnapshotAndRestoreCommandTest.takeSnapshotAndValidate; import java.io.File; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class RestoreQuorumTest extends QuorumPeerTestBase { @Test public void testRestoreAfterQuorumLost() throws Exception { setupAdminServerProperties(); int SERVER_COUNT = 3; final int NODE_COUNT = 10; final String PATH = "/testRestoreAfterQuorumLost"; try { // start up servers servers = LaunchServers(SERVER_COUNT); int leaderId = servers.findLeader(); // create data servers.zk[leaderId].create(PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < NODE_COUNT; i++) { servers.zk[leaderId].create(PATH + "/" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // take snapshot final File snapshotFile = takeSnapshotAndValidate(servers.adminPorts[leaderId], ClientBase.testBaseDir); // create more data for (int i = NODE_COUNT; i < NODE_COUNT * 2; i++) { servers.zk[leaderId].create(PATH + "/" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // shutdown all servers to simulate quorum lost servers.shutDownAllServers(); waitForAll(servers, ZooKeeper.States.CONNECTING); // restart the servers for (int i = 0; i < SERVER_COUNT; i++) { System.setProperty("zookeeper.admin.serverPort", String.valueOf(servers.adminPorts[i])); servers.mt[i].start(); servers.restartClient(i, this); } waitForAll(servers, ZooKeeper.States.CONNECTED); // restore servers for (int i = 0; i < SERVER_COUNT; i++) { performRestoreAndValidate(servers.adminPorts[i], snapshotFile); } // validate all servers are restored for (int i = 0; i < SERVER_COUNT; i++) { servers.restartClient(i, this); Assertions.assertEquals(NODE_COUNT, servers.zk[i].getAllChildrenNumber(PATH)); } // create more data after restore for (int i = NODE_COUNT * 2; i < NODE_COUNT * 3; i++) { servers.zk[leaderId].create(PATH + "/" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // validate all servers have expected data for (int i = 0; i < SERVER_COUNT; i++) { Assertions.assertEquals(NODE_COUNT * 2, servers.zk[i].getAllChildrenNumber(PATH)); } } finally { clearAdminServerProperties(); } } private void setupAdminServerProperties() { System.setProperty("zookeeper.admin.enableServer", "true"); System.setProperty(ADMIN_RATE_LIMITER_INTERVAL, "0"); System.setProperty(ADMIN_SNAPSHOT_ENABLED, "true"); System.setProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED, "true"); System.setProperty(ADMIN_RESTORE_ENABLED, "true"); } private void clearAdminServerProperties() { System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.admin.serverPort"); System.clearProperty(ADMIN_RATE_LIMITER_INTERVAL); System.clearProperty(ADMIN_SNAPSHOT_ENABLED); System.clearProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED); System.clearProperty(ADMIN_RESTORE_ENABLED); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ad0100644 0000000 0000000 00000000204 15051152474 032705 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndRestoreCommandTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndR0100644 0000000 0000000 00000043457 15051152474 034227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.admin; import static org.apache.zookeeper.server.ZooKeeperServer.ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED; import static org.apache.zookeeper.server.admin.CommandAuthTest.addAuthInfoForDigest; import static org.apache.zookeeper.server.admin.CommandAuthTest.genACLForDigest; import static org.apache.zookeeper.server.admin.CommandAuthTest.resetRootACL; import static org.apache.zookeeper.server.admin.Commands.ADMIN_RATE_LIMITER_INTERVAL; import static org.apache.zookeeper.server.admin.Commands.RestoreCommand.ADMIN_RESTORE_ENABLED; import static org.apache.zookeeper.server.admin.Commands.SnapshotCommand.ADMIN_SNAPSHOT_ENABLED; import static org.apache.zookeeper.server.admin.Commands.SnapshotCommand.REQUEST_QUERY_PARAM_STREAMING; import static org.apache.zookeeper.server.admin.JettyAdminServerTest.URL_FORMAT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.zip.CheckedInputStream; import javax.servlet.http.HttpServletResponse; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.IOUtils; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.SnapStream; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SnapshotAndRestoreCommandTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(SnapshotAndRestoreCommandTest.class); private static final String SNAPSHOT_TEST_PATH = "/snapshot_test"; private static final int NODE_COUNT = 10; private final String hostPort = "127.0.0.1:" + PortAssignment.unique(); private final int jettyAdminPort = PortAssignment.unique(); private ServerCnxnFactory cnxnFactory; private JettyAdminServer adminServer; private ZooKeeperServer zks; private ZooKeeper zk; @TempDir static File dataDir; @TempDir static File logDir; @BeforeAll public void setup() throws Exception { // start ZookeeperServer System.setProperty("zookeeper.4lw.commands.whitelist", "*"); zks = new ZooKeeperServer(dataDir, logDir, 3000); final int port = Integer.parseInt(hostPort.split(":")[1]); cnxnFactory = ServerCnxnFactory.createFactory(port, -1); cnxnFactory.startup(zks); assertTrue(ClientBase.waitForServerUp(hostPort, 120000)); // start AdminServer System.setProperty("zookeeper.admin.enableServer", "true"); System.setProperty("zookeeper.admin.serverPort", String.valueOf(jettyAdminPort)); System.setProperty(ADMIN_RATE_LIMITER_INTERVAL, "0"); System.setProperty(ADMIN_SNAPSHOT_ENABLED, "true"); System.setProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED, "true"); System.setProperty(ADMIN_RESTORE_ENABLED, "true"); adminServer = new JettyAdminServer(); adminServer.setZooKeeperServer(zks); adminServer.start(); // create Zookeeper client zk = ClientBase.createZKClient(hostPort); // setup root ACL zk.setACL(Commands.ROOT_PATH, genACLForDigest(), -1); // add auth addAuthInfoForDigest(zk); // create test data createData(zk, SNAPSHOT_TEST_PATH, NODE_COUNT); } @AfterAll public void tearDown() throws Exception { System.clearProperty("zookeeper.4lw.commands.whitelist"); System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.admin.serverPort"); System.clearProperty(ADMIN_RATE_LIMITER_INTERVAL); System.clearProperty(ADMIN_SNAPSHOT_ENABLED); System.clearProperty(ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED); System.clearProperty(ADMIN_RESTORE_ENABLED); resetRootACL(zk); if (zk != null) { zk.close(); } if (adminServer != null) { adminServer.shutdown(); } if (cnxnFactory != null) { cnxnFactory.shutdown(); } if (zks != null) { zks.shutdown(); } } @Test public void testSnapshotAndRestoreCommand_streaming() throws Exception { ServerMetrics.getMetrics().resetAll(); // take snapshot with streaming and validate final File snapshotFile = takeSnapshotAndValidate(jettyAdminPort, dataDir); // validate snapshot metrics validateSnapshotMetrics(); // restore from snapshot and validate performRestoreAndValidate(jettyAdminPort, snapshotFile); // validate creating data after restore try (final ZooKeeper zk = ClientBase.createZKClient(hostPort)) { addAuthInfoForDigest(zk); createData(zk, SNAPSHOT_TEST_PATH, NODE_COUNT + 1); assertEquals(NODE_COUNT + NODE_COUNT + 1, zk.getAllChildrenNumber(SNAPSHOT_TEST_PATH)); } // validate restore metrics validateRestoreMetrics(); } @Test public void testClientRequest_restoreInProgress() throws Exception { final int threadCount = 2; final int nodeCount = 50; final String restoreTestPath = "/restore_test"; // take snapshot final File snapshotFile = takeSnapshotAndValidate(jettyAdminPort, dataDir); final ExecutorService service = Executors.newFixedThreadPool(threadCount); final CountDownLatch latch = new CountDownLatch(threadCount); final AtomicBoolean createSucceeded = new AtomicBoolean(false); final AtomicBoolean restoreSucceeded = new AtomicBoolean(false); // thread 1 creates data service.submit(() -> { try { createData(zk, restoreTestPath, nodeCount); createSucceeded.set(true); } catch (final Exception e) { LOG.error(e.getMessage()); e.printStackTrace(); } finally { latch.countDown(); } }); // thread 2 performs restore operation service.submit(() -> { try { performRestoreAndValidate(jettyAdminPort, snapshotFile); restoreSucceeded.set(true); } catch (final Exception e) { LOG.error(e.getMessage()); e.printStackTrace(); } finally { latch.countDown(); } }); // wait for operations completed latch.await(); // validate all client requests succeeded if (createSucceeded.get() && restoreSucceeded.get()) { assertEquals(nodeCount, zk.getAllChildrenNumber(restoreTestPath)); } } @Test public void testRestores() throws Exception { // take snapshot final File snapshotFile = takeSnapshotAndValidate(jettyAdminPort, dataDir); // perform restores for (int i = 0; i < 3; i++) { performRestoreAndValidate(jettyAdminPort, snapshotFile); } } @Test public void testSnapshotCommand_nonStreaming() throws Exception { // take snapshot without streaming final HttpURLConnection snapshotConn = sendSnapshotRequest(false, jettyAdminPort); // validate snapshot response assertEquals(HttpURLConnection.HTTP_OK, snapshotConn.getResponseCode()); validateResponseHeaders(snapshotConn); displayResponsePayload(snapshotConn); } @Test public void testSnapshotCommand_disabled() throws Exception { System.setProperty(ADMIN_SNAPSHOT_ENABLED, "false"); try { // take snapshot final HttpURLConnection snapshotConn = sendSnapshotRequest(true, jettyAdminPort); // validate snapshot response assertEquals(HttpServletResponse.SC_SERVICE_UNAVAILABLE, snapshotConn.getResponseCode()); } finally { System.setProperty(ADMIN_SNAPSHOT_ENABLED, "true"); } } @Test public void testSnapshotCommand_serializeLastZxidDisabled() throws Exception { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(false); try { // take snapshot final HttpURLConnection snapshotConn = sendSnapshotRequest(true, jettyAdminPort); // validate snapshot response assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, snapshotConn.getResponseCode()); } finally { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(true); } } @Test public void testRestoreCommand_disabled() throws Exception { System.setProperty(ADMIN_RESTORE_ENABLED, "false"); try { final HttpURLConnection restoreConn = sendRestoreRequest(jettyAdminPort); assertEquals(HttpServletResponse.SC_SERVICE_UNAVAILABLE, restoreConn.getResponseCode()); } finally { System.setProperty(ADMIN_RESTORE_ENABLED, "true"); } } @Test public void testRestoreCommand_serializeLastZxidDisabled() throws Exception { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(false); try { final HttpURLConnection restoreConn = sendRestoreRequest(jettyAdminPort); assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, restoreConn.getResponseCode()); } finally { ZooKeeperServer.setSerializeLastProcessedZxidEnabled(true); } } @Test public void testRestoreCommand_invalidSnapshotData() throws Exception { final HttpURLConnection restoreConn = sendRestoreRequest(jettyAdminPort); try (final InputStream inputStream = new ByteArrayInputStream("Invalid snapshot data".getBytes()); final OutputStream outputStream = restoreConn.getOutputStream()) { IOUtils.copyBytes(inputStream, outputStream, 1024, true); } assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, restoreConn.getResponseCode()); } private void createData(final ZooKeeper zk, final String parentPath, final long count) throws Exception { try { zk.create(parentPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (final KeeperException.NodeExistsException ignore) { // ignore } for (int i = 0; i < count; i++) { zk.create(String.format("%s/%s", parentPath, "n_"), new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } } private static HttpURLConnection sendSnapshotRequest(final boolean streaming, final int jettyAdminPort) throws Exception { final String queryParamsStr = buildQueryStringForSnapshotCommand(streaming); final URL snapshotURL = new URL(String.format(URL_FORMAT + "/snapshot", jettyAdminPort) + "?" + queryParamsStr); final HttpURLConnection snapshotConn = (HttpURLConnection) snapshotURL.openConnection(); CommandAuthTest.addAuthHeader(snapshotConn, CommandAuthTest.AuthSchema.DIGEST, true); snapshotConn.setRequestMethod("GET"); return snapshotConn; } private static HttpURLConnection sendRestoreRequest(final int jettyAdminPort) throws Exception { final URL restoreURL = new URL(String.format(URL_FORMAT + "/restore", jettyAdminPort)); final HttpURLConnection restoreConn = (HttpURLConnection) restoreURL.openConnection(); restoreConn.setDoOutput(true); CommandAuthTest.addAuthHeader(restoreConn, CommandAuthTest.AuthSchema.DIGEST, true); restoreConn.setRequestMethod("POST"); return restoreConn; } private static String buildQueryStringForSnapshotCommand(final boolean streaming) throws Exception { final Map parameters = new HashMap<>(); parameters.put(REQUEST_QUERY_PARAM_STREAMING, String.valueOf(streaming)); return getParamsString(parameters); } private static String getParamsString(final Map params) throws UnsupportedEncodingException { final StringBuilder result = new StringBuilder(); for (final Map.Entry entry : params.entrySet()) { result.append(URLEncoder.encode(entry.getKey(), "UTF-8")); result.append("="); result.append(URLEncoder.encode(entry.getValue(), "UTF-8")); result.append("&"); } final String resultString = result.toString(); return resultString.length() > 0 ? resultString.substring(0, resultString.length() - 1) : resultString; } private static void validateResponseHeaders(final HttpURLConnection conn) { LOG.info("Header:{}, Value:{}", Commands.SnapshotCommand.RESPONSE_HEADER_LAST_ZXID, conn.getHeaderField(Commands.SnapshotCommand.RESPONSE_HEADER_LAST_ZXID)); assertNotNull(conn.getHeaderField(Commands.SnapshotCommand.RESPONSE_HEADER_LAST_ZXID)); LOG.info("Header:{}, Value:{}", Commands.SnapshotCommand.RESPONSE_HEADER_SNAPSHOT_SIZE, conn.getHeaderField(Commands.SnapshotCommand.RESPONSE_HEADER_SNAPSHOT_SIZE)); assertNotNull(conn.getHeaderField(Commands.SnapshotCommand.RESPONSE_HEADER_SNAPSHOT_SIZE)); assertTrue(Integer.parseInt(conn.getHeaderField(Commands.SnapshotCommand.RESPONSE_HEADER_SNAPSHOT_SIZE)) > 0); } private static void displayResponsePayload(final HttpURLConnection conn) throws IOException { final StringBuilder sb = new StringBuilder(); try (final BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) { String inputLine; while ((inputLine = in.readLine()) != null) { sb.append(inputLine); } LOG.info("Response payload: {}", sb); } } private void validateSnapshotMetrics() { Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(0, (long) metrics.get("snapshot_error_count")); assertEquals(0, (long) metrics.get("snapshot_rate_limited_count")); assertTrue((Double) metrics.get("avg_snapshottime") > 0.0); } private void validateRestoreMetrics() { Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(0, (long) metrics.get("restore_error_count")); assertEquals(0, (long) metrics.get("restore_rate_limited_count")); assertTrue((Double) metrics.get("avg_restore_time") > 0.0); } public static File takeSnapshotAndValidate(final int jettyAdminPort, final File dataDir) throws Exception { // take snapshot with streaming final HttpURLConnection snapshotConn = sendSnapshotRequest(true, jettyAdminPort); // validate snapshot response assertEquals(HttpURLConnection.HTTP_OK, snapshotConn.getResponseCode()); validateResponseHeaders(snapshotConn); final File snapshotFile = new File(dataDir + "/snapshot." + System.currentTimeMillis()); try (final InputStream inputStream = snapshotConn.getInputStream(); final FileOutputStream outputStream = new FileOutputStream(snapshotFile)) { IOUtils.copyBytes(inputStream, outputStream, 1024, true); final long fileSize = Files.size(snapshotFile.toPath()); assertTrue(fileSize > 0); } return snapshotFile; } public static void performRestoreAndValidate(final int jettyAdminPort, final File snapshotFile) throws Exception { // perform restore final HttpURLConnection restoreConn = sendRestoreRequest(jettyAdminPort); try (final CheckedInputStream is = SnapStream.getInputStream(snapshotFile); final OutputStream outputStream = restoreConn.getOutputStream()) { IOUtils.copyBytes(is, outputStream, 1024, true); } // validate restore response assertEquals(HttpURLConnection.HTTP_OK, restoreConn.getResponseCode()); displayResponsePayload(restoreConn); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_au0100644 0000000 0000000 00000000202 15051152474 032724 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/IPAuthenticationProviderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/IPAuthenticat0100644 0000000 0000000 00000017057 15051152474 034233 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.auth; import static org.apache.zookeeper.server.auth.IPAuthenticationProvider.USE_X_FORWARDED_FOR_KEY; import static org.apache.zookeeper.server.auth.IPAuthenticationProvider.X_FORWARDED_FOR_HEADER_NAME; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class IPAuthenticationProviderTest { private HttpServletRequest request; @Before public void setUp() throws Exception { System.clearProperty(USE_X_FORWARDED_FOR_KEY); request = mock(HttpServletRequest.class); } @After public void tearDown() { System.clearProperty(USE_X_FORWARDED_FOR_KEY); } @Test public void testGetClientIPAddressSkipXForwardedFor() { // Arrange System.setProperty(USE_X_FORWARDED_FOR_KEY, "false"); doReturn("192.168.1.1").when(request).getRemoteAddr(); doReturn("192.168.1.2,192.168.1.3,192.168.1.4").when(request).getHeader(X_FORWARDED_FOR_HEADER_NAME); // Act String clientIp = IPAuthenticationProvider.getClientIPAddress(request); // Assert assertEquals("192.168.1.1", clientIp); } @Test public void testGetClientIPAddressDefaultBehaviour() { // Arrange System.clearProperty(USE_X_FORWARDED_FOR_KEY); doReturn("192.168.1.1").when(request).getRemoteAddr(); doReturn("192.168.1.2,192.168.1.3,192.168.1.4").when(request).getHeader(X_FORWARDED_FOR_HEADER_NAME); // Act String clientIp = IPAuthenticationProvider.getClientIPAddress(request); // Assert assertEquals("192.168.1.1", clientIp); } @Test public void testGetClientIPAddressWithXForwardedFor() { // Arrange System.setProperty(USE_X_FORWARDED_FOR_KEY, "true"); doReturn("192.168.1.1").when(request).getRemoteAddr(); doReturn("192.168.1.2,192.168.1.3,192.168.1.4").when(request).getHeader(X_FORWARDED_FOR_HEADER_NAME); // Act String clientIp = IPAuthenticationProvider.getClientIPAddress(request); // Assert assertEquals("192.168.1.2", clientIp); } @Test public void testGetClientIPAddressMissingXForwardedFor() { // Arrange System.setProperty(USE_X_FORWARDED_FOR_KEY, "false"); doReturn("192.168.1.1").when(request).getRemoteAddr(); // Act String clientIp = IPAuthenticationProvider.getClientIPAddress(request); // Assert assertEquals("192.168.1.1", clientIp); } @Test public void testParsingOfIPv6Address() { //Full IPv6 address String ipv6Full = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; byte[] expectedFull = { (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0x85, (byte) 0xa3, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x8a, (byte) 0x2e, (byte) 0x03, (byte) 0x70, (byte) 0x73, (byte) 0x34 }; byte[] actualFull = IPAuthenticationProvider.v6addr2Bytes(ipv6Full); assertNotNull(actualFull, "Full IPv6 address should not return null"); assertArrayEquals(expectedFull, actualFull, "Full IPv6 address conversion mismatch"); //Compressed IPv6 address (double colon) String ipv6Compressed = "2001:db8::8a2e:370:7334"; byte[] expectedCompressed = { (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x8a, (byte) 0x2e, (byte) 0x03, (byte) 0x70, (byte) 0x73, (byte) 0x34 }; byte[] actualCompressed = IPAuthenticationProvider.v6addr2Bytes(ipv6Compressed); assertNotNull(actualCompressed, "Compressed IPv6 address should not return null"); assertArrayEquals(expectedCompressed, actualCompressed, "Compressed IPv6 address conversion mismatch"); //Shortened IPv6 address String ipv6Shortened = "2001:db8::1"; byte[] expectedShortened = { (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 }; byte[] actualShortened = IPAuthenticationProvider.v6addr2Bytes(ipv6Shortened); assertNotNull(actualShortened, "Shortened IPv6 address should not return null"); assertArrayEquals(expectedShortened, actualShortened, "Shortened IPv6 address conversion mismatch"); //Loopback address String ipv6Loopback = "::1"; byte[] expectedLoopback = { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 }; byte[] actualLoopback = IPAuthenticationProvider.v6addr2Bytes(ipv6Loopback); assertNotNull(actualLoopback, "Loopback IPv6 address should not return null"); assertArrayEquals(expectedLoopback, actualLoopback, "Loopback IPv6 address conversion mismatch"); } private static Stream invalidIPv6Addresses() { return Stream.of( Arguments.of("1", "wrong number of segments"), Arguments.of("1:2", "wrong number of segments"), Arguments.of("1::2:", "empty segment"), Arguments.of(":1::2:", "empty segment"), Arguments.of("1:2:3:4:5:6:7:8:", "wrong number of segments"), Arguments.of("1:2:3:4:5:6:7:8:9", "wrong number of segments"), Arguments.of("1:2::3:4:5:6:7:8", "too many segments"), Arguments.of("1::2::", "too many '::'"), Arguments.of("1:abcdf::", "segment too long"), Arguments.of("efgh::", "invalid hexadecimal characters in segment"), Arguments.of("1:: ", "invalid hexadecimal characters in segment"), Arguments.of(" 1::", "invalid hexadecimal characters in segment") ); } @ParameterizedTest(name = "address = \"{0}\"") @MethodSource("invalidIPv6Addresses") public void testParsingOfInvalidIPv6Address(String ipv6Address, String expectedMessage) { try { IPAuthenticationProvider.parseV6addr(ipv6Address); fail("expect failure"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString(expectedMessage)); } assertNull(IPAuthenticationProvider.v6addr2Bytes(ipv6Address)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000176 15051152474 032732 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/ControlCommandTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000005442 15051152474 034366 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import org.junit.Assert; import org.junit.Test; public class ControlCommandTest { @Test public void verifyGeneratedUri() { Assert.assertEquals("command/ping", ControlCommand.createCommandUri(ControlCommand.Action.PING, null).toLowerCase()); Assert.assertEquals("command/ping", ControlCommand.createCommandUri(ControlCommand.Action.PING, "").toLowerCase()); Assert.assertEquals("command/closeconnection/1234", ControlCommand.createCommandUri(ControlCommand.Action.CLOSECONNECTION, "1234").toLowerCase()); } @Test public void verifyParseChecksForNull() { try { ControlCommand.parseUri(null); Assert.fail("Should have thrown for null."); } catch (IllegalArgumentException ex) { } } @Test public void verifyParseChecksForPrefix() { try { ControlCommand.parseUri("ping"); Assert.fail("Should have thrown for missing command/ prefix."); } catch (IllegalArgumentException ex) { } } @Test public void verifyParseCorrectlyFindsCommandWithNoParameter() { Assert.assertEquals(ControlCommand.Action.PING, ControlCommand.parseUri("command/ping").getAction()); } @Test public void verifyParseCorrectlyFindsCommandWithParameter() { ControlCommand command = ControlCommand.parseUri("command/closeconnection/1234"); Assert.assertEquals(ControlCommand.Action.CLOSECONNECTION, command.getAction()); Assert.assertEquals("1234", command.getParameter()); } @Test public void verifyParseIllegalCommandWithNoParameter() { try { ControlCommand.parseUri("pings"); Assert.fail("Should have thrown for non existing command."); } catch (IllegalArgumentException ex) { } } @Test public void verifyParseIllegalCommandWithParameter() { try { ControlCommand.parseUri("command/close_connection/1234"); Assert.fail("Should have thrown for non existing command."); } catch (IllegalArgumentException ex) { } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000206 15051152474 032724 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/ControllerClientServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000010172 15051152474 034362 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import org.junit.Assert; import org.junit.Test; public class ControllerClientServerTest extends ControllerTestBase { @Test public void verifyPingCommand() { Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.PING)); } @Test public void verifyCloseConnectionCommand() { // Valid long session ids should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.CLOSECONNECTION, "0x1234")); Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.CLOSECONNECTION, "1234")); // Invalid session id format should fail. Assert.assertFalse(commandClient.trySendCommand(ControlCommand.Action.CLOSECONNECTION, "hanm")); // No parameter should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.CLOSECONNECTION)); } @Test public void verifyExpireSessionCommand() { // Valid long session ids should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.EXPIRESESSION, "0x1234")); // Invalid session id format should fail. Assert.assertFalse(commandClient.trySendCommand(ControlCommand.Action.EXPIRESESSION, "hanm")); // No parameter should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.EXPIRESESSION)); } @Test public void verifyAddResetDelayCommands() { // Valid longs should be parsed. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.ADDDELAY, "0x1234")); // Invalid longs should fail. Assert.assertFalse(commandClient.trySendCommand(ControlCommand.Action.ADDDELAY, "hanm")); // No parameter should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.ADDDELAY)); // Reset delay should work. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); } @Test public void verifyBadResponseCommands() { // Valid longs should be parsed. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.FAILREQUESTS, "0x1234")); // Invalid longs should fail. Assert.assertFalse(commandClient.trySendCommand(ControlCommand.Action.FAILREQUESTS, "hanm")); // No parameter should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.FAILREQUESTS)); // Reset should work. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); } @Test public void verifyEatResponseCommands() { // Valid longs should be parsed. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.NORESPONSE, "0x1234")); // Invalid longs should fail. Assert.assertFalse(commandClient.trySendCommand(ControlCommand.Action.NORESPONSE, "hanm")); // No parameter should be accepted. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.NORESPONSE)); // Reset should work. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); } @Test public void verifyLeaderElectionCommand() { Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.ELECTNEWLEADER)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000200 15051152474 032716 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/ControllerConfigTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000012337 15051152474 034367 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.net.ServerSocket; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class ControllerConfigTest { File configFile; private static final int AnyTickTime = 1234; private static final int AnyPort = 1234; private static final String AnyDataDir = "temp"; public static File createTempFile() throws IOException { return File.createTempFile("temp", "cfg", new File(System.getProperty("user.dir"))); } public static List findNAvailablePorts(int n) throws IOException { List openedSockets = new ArrayList<>(); List ports = new ArrayList<>(); for (int i = 0; i < n; i++) { ServerSocket randomSocket = new ServerSocket(0); openedSockets.add(randomSocket); ports.add(randomSocket.getLocalPort()); } for (ServerSocket s : openedSockets) { s.close(); } return ports; } public static void writeRequiredControllerConfig(File file, int controllerPort, int zkServerPort, int adminServerPort) throws IOException { PrintWriter writer = new PrintWriter(file); writer.write("dataDir=anywhere\n"); writer.write("controllerPort=" + controllerPort + "\n"); writer.write("clientPort=" + zkServerPort + "\n"); writer.write("adminPort=" + adminServerPort + "\n"); writer.close(); } @Before public void init() throws IOException { configFile = createTempFile(); } private void writeFile(int portNumber) throws IOException { FileWriter writer = new FileWriter(configFile); writer.write("dataDir=somewhere\n"); writer.write("ignore=me\n"); writer.write("tickTime=" + AnyTickTime + "\n"); writer.write("controllerPort=" + portNumber + "\n"); writer.write("clientPort=" + portNumber + "\n"); writer.flush(); writer.close(); } @After public void cleanup() { if (configFile != null) { configFile.delete(); } } @Test public void parseFileSucceeds() throws Exception { writeFile(AnyPort); ControllerServerConfig config = new ControllerServerConfig(configFile.getAbsolutePath()); Assert.assertEquals(AnyPort, config.getControllerAddress().getPort()); Assert.assertEquals(AnyPort, config.getClientPortAddress().getPort()); Assert.assertEquals(AnyTickTime, config.getTickTime()); } @Test public void parseFileFailsWithMissingPort() throws Exception { FileWriter writer = new FileWriter(configFile); writer.write("dataDir=somewhere\n"); writer.flush(); writer.close(); try { ControllerServerConfig config = new ControllerServerConfig(configFile.getAbsolutePath()); Assert.fail("Should have thrown with missing server config"); } catch (QuorumPeerConfig.ConfigException ex) { } } @Test public void parseMissingFileThrows() { try { ControllerServerConfig config = new ControllerServerConfig("DontLookHere.missing"); Assert.fail("should have thrown"); } catch (QuorumPeerConfig.ConfigException ex) { } } @Test public void parseInvalidPortThrows()throws QuorumPeerConfig.ConfigException { try { ControllerServerConfig config = new ControllerServerConfig(configFile.getAbsolutePath()); Assert.fail("should have thrown"); } catch (QuorumPeerConfig.ConfigException ex) { } } @Test public void validCtor() { ControllerServerConfig config = new ControllerServerConfig(AnyPort, AnyPort, AnyDataDir); Assert.assertEquals(AnyPort, config.getControllerAddress().getPort()); Assert.assertEquals(AnyPort, config.getClientPortAddress().getPort()); Assert.assertEquals(AnyDataDir, config.getDataDir().getName()); } @Test public void invalidCtor() { try { ControllerServerConfig config = new ControllerServerConfig(-10, -10, "no where"); Assert.fail("should have thrown"); } catch (IllegalArgumentException ex) { } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000176 15051152474 032732 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/ControllerTestBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/Control0100644 0000000 0000000 00000005454 15051152474 034371 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.List; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.ZKTestCase; import org.junit.After; import org.junit.Before; public class ControllerTestBase extends ZKTestCase { protected ControllerService controllerService; protected CommandClient commandClient; private File tempDirectory; protected ControllerServerConfig config; @Before public void init() throws Exception { List openPorts = ControllerConfigTest.findNAvailablePorts(2); File tmpFile = File.createTempFile("test", ".junit", testBaseDir); tempDirectory = new File(tmpFile + ".dir"); assertFalse(tempDirectory.exists()); assertTrue(tempDirectory.mkdirs()); config = new ControllerServerConfig(openPorts.get(0), openPorts.get(1), tempDirectory.getAbsolutePath()); controllerService = new ControllerService(); controllerService.start(config); int retries = 50; // The controller needs to hold an election before it is ready to process requests. // Busy-wait until its ready... while (!controllerService.isReady()) { Thread.sleep(100); retries--; if (retries < 0) { throw new TimeoutException("Service didn't start up and finish elections."); } } // Create a client which sends requests to localhost on the configured port. commandClient = new CommandClient(config.getControllerAddress().getPort()); } @After public void cleanup() throws InterruptedException { if (controllerService != null) { controllerService.shutdown(); } if (commandClient != null) { commandClient.close(); } if (tempDirectory != null) { tempDirectory.delete(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_co0100644 0000000 0000000 00000000221 15051152474 032721 xustar000000000 0000000 145 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/ZooKeeperServerControllerEndToEndTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/controller/ZooKeep0100644 0000000 0000000 00000034645 15051152474 034331 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.controller; import java.io.IOException; import java.util.LinkedList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.junit.After; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZooKeeperServerControllerEndToEndTest extends ControllerTestBase { private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperServerControllerEndToEndTest.class); private ZooKeeper zkClient; private static final String AnyPath = "/Any"; private static final byte[] AnyData = new byte[] {0x0, 0x1}; @After @Override public void cleanup() throws InterruptedException { if (zkClient != null) { zkClient.close(); } super.cleanup(); } private void initClient(Watcher watcher) throws IOException { zkClient = new ZooKeeper("localhost:" + config.getClientPortAddress().getPort(), 10000, watcher); } @Test public void verifyClientConnects() throws Exception { // Basic validation: we can connect and get events. BlockingStateWatcher watcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); this.initClient(watcher); watcher.waitForEvent(); } @Test public void verifyClientDisconnectsAndReconnects() throws Exception { // Setup: First connect to the server and wait for connected. BlockingStateWatcher watcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(watcher); watcher.waitForEvent(); // Force a disconnection through the controller and ensure we get the events in order: // 1: Disconnected // 2: SyncConnected watcher.reset( new Watcher.Event.KeeperState[] { Watcher.Event.KeeperState.Disconnected, Watcher.Event.KeeperState.SyncConnected }); Assert.assertTrue(commandClient .trySendCommand(ControlCommand.Action.CLOSECONNECTION, String.valueOf(zkClient.getSessionId()))); watcher.waitForEvent(); } @Test public void verifySessionExpiration() throws Exception { // Setup: First connect to the server and wait for connected. BlockingStateWatcher watcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(watcher); watcher.waitForEvent(); // Force an expiration. // 1: Disconnected // 2: Expired watcher.reset( new Watcher.Event.KeeperState[] { Watcher.Event.KeeperState.Disconnected, Watcher.Event.KeeperState.Expired }); Assert.assertTrue(commandClient .trySendCommand(ControlCommand.Action.EXPIRESESSION, String.valueOf(zkClient.getSessionId()))); watcher.waitForEvent(); } @Test public void verifyGlobalSessionExpiration() throws Exception { // Step 1: Connect. BlockingStateWatcher stateWatcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(stateWatcher); stateWatcher.waitForEvent(); // Step 2: Add an ephemeral node (upgrades session to global). BlockingPathWatcher pathWatcher = new BlockingPathWatcher(AnyPath, Watcher.Event.EventType.NodeCreated); zkClient.exists(AnyPath, pathWatcher); Assert.assertEquals(AnyPath, zkClient.create(AnyPath, AnyData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL)); pathWatcher.waitForEvent(); // Force expire all sessions. stateWatcher.reset(Watcher.Event.KeeperState.Expired); Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.EXPIRESESSION)); stateWatcher.waitForEvent(); } @Ignore public void verifyRejectAcceptSessions() throws Exception { // Tell the server to reject new requests. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.REJECTCONNECTIONS)); EventWaiter watcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(watcher); try { watcher.waitForEvent(100); Assert.fail("should have failed connecting"); } catch (TimeoutException ex) { } // Now accept requests. We should get a connection quickly. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); watcher.waitForEvent(); } private long timedTransaction() throws Exception { long startTime = System.currentTimeMillis(); zkClient.exists(AnyPath, false); return System.currentTimeMillis() - startTime; } @Test public void verifyAddDelay() throws Exception { EventWaiter watcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(watcher); watcher.waitForEvent(); timedTransaction(); // Add 200 ms of delay to each response. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.ADDDELAY, String.valueOf(200))); long delayedDuration = timedTransaction(); Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); long resetDuration = timedTransaction(); Assert.assertTrue(delayedDuration - resetDuration > 200); } @Test public void verifyFailAllRequests() throws Exception { // Step 1: Connect. BlockingStateWatcher stateWatcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(stateWatcher); stateWatcher.waitForEvent(); // Step 2: Tell the server to fail requests. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.FAILREQUESTS)); try { zkClient.exists(AnyPath, null); Assert.fail("should have failed"); } catch (KeeperException ex) { } // 2nd should fail: we haven't reset. try { zkClient.exists(AnyPath, null); Assert.fail("should still fail"); } catch (KeeperException ex) { } // Reset; future requests should succeed. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); zkClient.exists(AnyPath, null); } @Test public void verifyFailRequestCount() throws Exception { // Step 1: Connect. BlockingStateWatcher stateWatcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(stateWatcher); stateWatcher.waitForEvent(); // Step 2: Tell the server to fail 1 request. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.FAILREQUESTS, "1")); try { zkClient.exists(AnyPath, null); Assert.fail("should have failed"); } catch (KeeperException ex) { } // Have not reset; should succeed. zkClient.exists(AnyPath, null); } @Test public void verifyServerEatsAllResponses() throws Exception { // Step 1: Connect. BlockingStateWatcher watcher = new BlockingStateWatcher(Watcher.Event.KeeperState.SyncConnected); initClient(watcher); watcher.waitForEvent(); // No data yet. Assert.assertNull(zkClient.exists(AnyPath, null)); // Step 2: Tell the server to eat responses...nom...nom...nom.... Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.NORESPONSE)); try { BlockingPathWatcher pathWatcher = new BlockingPathWatcher(AnyPath, Watcher.Event.EventType.NodeCreated); // This async call should succeed in setting the data, but never send a response. zkClient.create(AnyPath, AnyData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, pathWatcher, null); pathWatcher.waitForEvent(500); Assert.fail("should time out since the event should never come"); } catch (TimeoutException ex) { } // Re-enable responses. Assert.assertTrue(commandClient.trySendCommand(ControlCommand.Action.RESET)); watcher.reset(Watcher.Event.KeeperState.SyncConnected); try { // Even though we get a good response, the client doesn't know about // the transaction id (xid). This should terminate the connection and // throw a KeeperException. zkClient.exists(AnyPath, false); Assert.fail("should have failed with bad xid"); } catch (KeeperException ex) { // The client believes it has fallen behind so deems this a connection loss. Assert.assertTrue(ex instanceof KeeperException.ConnectionLossException); } // The client should reconnect and be healthy after this. watcher.waitForEvent(); Assert.assertNotNull(zkClient.exists(AnyPath, false)); } /** * Our watcher interface is called back on a potentially separate thread. * Tests should be logically consolidated into a single method in the following format: * for each action in my test * Setup test action * Kick off async action * await state change * verify state * * To enable this logical pattern, the watcher has an ordered set of states to wait on. * When all the states have arrived (in order), the notifier is unblocked. */ private abstract class EventWaiter implements Watcher, AsyncCallback.StringCallback { private final int DEFAULT_WAIT_DURATION = 10000; private CountDownLatch eventNotification; public EventWaiter() { reset(); } protected void reset() { eventNotification = new CountDownLatch(1); } @Override public void process(WatchedEvent event) { // NO-OP. Derived classes should override if required. LOG.info("WatchedEvent: {}", event); } @Override public void processResult(int rc, String path, Object ctx, String name) { // NO-OP. Derived classes to implement if required. LOG.info("StringCallback: {}, {}, {}, {}", rc, path, ctx, name); } public void notifyListener() { eventNotification.countDown(); } public void waitForEvent() throws InterruptedException, TimeoutException { waitForEvent(DEFAULT_WAIT_DURATION); } public void waitForEvent(int waitDurationInMs) throws InterruptedException, TimeoutException { // Wait ten seconds and throw if we time out. if (!eventNotification.await(waitDurationInMs, TimeUnit.MILLISECONDS)) { throw new TimeoutException("Timed out waiting for event"); } } } private class BlockingStateWatcher extends EventWaiter { private Object lockMe = new Object(); private LinkedList statesToWaitFor; public BlockingStateWatcher(Event.KeeperState stateToNotifyOn) { reset(stateToNotifyOn); } @Override public void process(WatchedEvent event) { LOG.info("State transition: {}", event.getState()); boolean shouldNotify = false; synchronized (lockMe) { if (!statesToWaitFor.isEmpty() && statesToWaitFor.getFirst() == event.getState()) { statesToWaitFor.removeFirst(); shouldNotify = statesToWaitFor.isEmpty(); } } if (shouldNotify) { notifyListener(); } } public void reset(Event.KeeperState stateToNotifyOn) { reset(new Event.KeeperState[] {stateToNotifyOn}); } public void reset(Event.KeeperState[] orderedStatesToWaitOn) { if (orderedStatesToWaitOn == null) { throw new IllegalArgumentException("orderedStatesToWaitOn can't be null."); } if (orderedStatesToWaitOn.length <= 0) { throw new IllegalArgumentException("orderedStatesToWaitOn length must be positive."); } synchronized (lockMe) { super.reset(); statesToWaitFor = new LinkedList<>(); for (Event.KeeperState state : orderedStatesToWaitOn) { statesToWaitFor.add(state); } } } } private class BlockingPathWatcher extends EventWaiter { private String pathToNotifyOn; private Event.EventType requiredEventType; public BlockingPathWatcher(String pathToNotifyOn, Event.EventType requiredEventType) { reset(pathToNotifyOn, requiredEventType); } public void reset(String pathToNotifyOn, Event.EventType requiredEventType) { super.reset(); this.pathToNotifyOn = pathToNotifyOn; this.requiredEventType = requiredEventType; } @Override public void process(WatchedEvent event) { LOG.info("WatchEvent {} for path {}", event.getType(), event.getPath()); if (pathToNotifyOn != null && event.getType() == requiredEventType && pathToNotifyOn.equalsIgnoreCase(event.getPath())) { notifyListener(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000174 15051152474 032730 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/ZookeeperServeInfo.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/Zookeeper0100644 0000000 0000000 00000030201 15051152474 034246 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.embedded; import java.lang.management.ManagementFactory; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; import javax.management.InstanceNotFoundException; import javax.management.MBeanServer; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectInstance; import javax.management.ObjectName; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.server.ConnectionMXBean; import org.apache.zookeeper.server.ZooKeeperServerBean; import org.apache.zookeeper.server.quorum.LocalPeerMXBean; import org.apache.zookeeper.server.quorum.QuorumBean; import org.apache.zookeeper.server.quorum.QuorumMXBean; import org.apache.zookeeper.server.quorum.RemotePeerMXBean; public final class ZookeeperServeInfo { private static final MBeanServer localServer = ManagementFactory.getPlatformMBeanServer(); private ZookeeperServeInfo() { } public static class PeerInfo { private final String name; private final String quorumAddress; private final String state; private final boolean leader; public PeerInfo(String name, String quorumAddress, String state, boolean leader) { this.name = name; this.quorumAddress = quorumAddress; this.state = state; this.leader = leader; } public String getName() { return name; } public String getQuorumAddress() { return quorumAddress; } public String getState() { return state; } public boolean isLeader() { return leader; } @Override public String toString() { return "PeerInfo{" + "name=" + name + ", leader=" + leader + ", quorumAddress=" + quorumAddress + ", state=" + state + '}'; } } public static class ConnectionInfo { private final String sourceip; private final String sessionid; private final String lastoperation; private final String lastResponseTime; private final String avgLatency; private final String lastLatency; private final String nodes; public ConnectionInfo(String sourceip, String sessionid, String lastoperation, String lastResponseTime, String avgLatency, String lastLatency, String nodes) { this.sourceip = sourceip; this.sessionid = sessionid; this.lastoperation = lastoperation; this.lastResponseTime = lastResponseTime; this.avgLatency = avgLatency; this.lastLatency = lastLatency; this.nodes = nodes; } public String getLastLatency() { return lastLatency; } public String getSourceip() { return sourceip; } public String getSessionid() { return sessionid; } public String getLastoperation() { return lastoperation; } public String getLastResponseTime() { return lastResponseTime; } public String getAvgLatency() { return avgLatency; } public String getNodes() { return nodes; } @Override public String toString() { return "ConnectionInfo{" + "sourceip=" + sourceip + ", sessionid=" + sessionid + ", lastoperation=" + lastoperation + ", lastResponseTime=" + lastResponseTime + ", avgLatency=" + avgLatency + ", nodes=" + nodes + '}'; } } public static class ServerInfo { private final List connections = new ArrayList<>(); private boolean leader; private boolean standaloneMode; public List peers = new ArrayList<>(); public boolean isStandaloneMode() { return standaloneMode; } public List getConnections() { return connections; } public boolean isLeader() { return leader; } public List getPeers() { return Collections.unmodifiableList(peers); } public void addPeer(PeerInfo peer) { peers.add(peer); } @Override public String toString() { return "ServerInfo{" + "connections=" + connections + ", leader=" + leader + ", standaloneMode=" + standaloneMode + ", peers=" + peers + '}'; } } public static ServerInfo getStatus() throws Exception { return getStatus("*"); } public static ServerInfo getStatus(String beanName) throws Exception { ServerInfo info = new ServerInfo(); boolean standalonemode = false; // org.apache.ZooKeeperService:name0=ReplicatedServer_id1,name1=replica.1,name2=Follower,name3=Connections, // name4=10.168.10.119,name5=0x13e83353764005a // org.apache.ZooKeeperService:name0=ReplicatedServer_id2,name1=replica.2,name2=Leader if (StringUtils.isBlank(beanName)) { beanName = "*"; } ObjectName objectName = new ObjectName("org.apache.ZooKeeperService:name0=" + beanName); Set first_level_beans = localServer.queryMBeans(objectName, null); if (first_level_beans.isEmpty()) { throw new IllegalStateException("No ZooKeeper server found in this JVM with name " + objectName); } String myName = ""; for (ObjectInstance o : first_level_beans) { if (o.getClassName().equalsIgnoreCase(ZooKeeperServerBean.class.getName())) { standalonemode = true; info.leader = true; info.addPeer(new PeerInfo("local", "local", "STANDALONE", true)); } else if (o.getClassName().equalsIgnoreCase(QuorumBean.class.getName())) { standalonemode = false; try { QuorumMXBean quorum = MBeanServerInvocationHandler.newProxyInstance(localServer, o.getObjectName(), QuorumMXBean.class, false); myName = quorum.getName(); } catch (UndeclaredThrowableException err) { if (err.getCause() instanceof javax.management.InstanceNotFoundException) { // maybe server not yet started or already stopped ? } else { throw err; } } } } info.standaloneMode = standalonemode; if (standalonemode) { Set connectionsbeans = localServer.queryMBeans(new ObjectName( "org.apache.ZooKeeperService:name0=*,name1=Connections,name2=*,name3=*"), null); for (ObjectInstance conbean : connectionsbeans) { ConnectionMXBean cc = MBeanServerInvocationHandler. newProxyInstance(localServer, conbean.getObjectName(), ConnectionMXBean.class, false); try { String nodes = ""; if (cc.getEphemeralNodes() != null) { nodes = Arrays.asList(cc.getEphemeralNodes()) + ""; } info.connections.add(new ConnectionInfo(cc.getSourceIP(), cc.getSessionId(), cc.getLastOperation(), cc.getLastResponseTime(), cc.getAvgLatency() + "", cc.getLastLatency() + "", nodes)); } catch (Exception ex) { if (ex instanceof InstanceNotFoundException && ex.getCause() instanceof InstanceNotFoundException) { // SKIP } else { throw ex; } } } } else { if (myName.isEmpty()) { throw new IllegalStateException( "Cannot find local JMX name for current node, in quorum mode, scanned " + first_level_beans); } boolean leader = false; Set replicas = localServer.queryMBeans(new ObjectName( "org.apache.ZooKeeperService:name0=" + myName + ",name1=*"), null); for (ObjectInstance o : replicas) { if (o.getClassName().toLowerCase().contains("local")) { LocalPeerMXBean local = MBeanServerInvocationHandler. newProxyInstance(localServer, o.getObjectName(), LocalPeerMXBean.class, false); info.addPeer(new PeerInfo(local.getName(), local.getQuorumAddress(), local.getState() + "", local.isLeader())); ObjectName asfollowername = new ObjectName(o.getObjectName() + ",name2=Follower"); ObjectName asleadername = new ObjectName(o.getObjectName() + ",name2=Leader"); boolean isleader = localServer.isRegistered(asleadername); Set connectionsbeans = null; if (isleader) { leader = true; ObjectName asleaderconnections = new ObjectName( asleadername + ",name3=Connections,name4=*,name5=*"); connectionsbeans = localServer.queryMBeans(asleaderconnections, null); } else { leader = false; ObjectName asfollowernameconnections = new ObjectName( asfollowername + ",name3=Connections,name4=*,name5=*"); connectionsbeans = localServer.queryMBeans(asfollowernameconnections, null); } for (ObjectInstance conbean : connectionsbeans) { ConnectionMXBean cc = MBeanServerInvocationHandler.newProxyInstance(localServer, conbean.getObjectName(), ConnectionMXBean.class, false); try { String nodes = ""; if (cc.getEphemeralNodes() != null) { nodes = Arrays.asList(cc.getEphemeralNodes()) + ""; } info.connections.add(new ConnectionInfo(cc.getSourceIP(), cc.getSessionId(), cc. getLastOperation(), cc.getLastResponseTime(), cc.getAvgLatency() + "", cc. getLastLatency() + "", nodes)); } catch (Exception ex) { if (ex instanceof InstanceNotFoundException && ex.getCause() instanceof InstanceNotFoundException) { // SKIP } else { throw ex; } } } } else { RemotePeerMXBean remote = MBeanServerInvocationHandler.newProxyInstance(localServer, o. getObjectName(), RemotePeerMXBean.class, false); info.addPeer(new PeerInfo(remote.getName(), remote.getQuorumAddress(), "REMOTE", remote.isLeader())); } } info.leader = leader; } return info; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000216 15051152474 032725 xustar000000000 0000000 142 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/ZookeeperServerClusterMutualAuthTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/Zookeeper0100644 0000000 0000000 00000014647 15051152474 034266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.embedded; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Properties; import javax.security.auth.login.Configuration; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.common.X509Util; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; /** * Test Quorum Mutual Auth with ZooKeeperEmbedded. */ public class ZookeeperServerClusterMutualAuthTest { @BeforeAll public static void setUpEnvironment() { // Need to disable Fips-mode, because we use DIGEST-MD5 mech for Sasl System.setProperty(X509Util.FIPS_MODE_PROPERTY, "false"); System.setProperty("java.security.auth.login.config", new File("src/test/resources/embedded/test_jaas_server_auth.conf") .getAbsolutePath()); Configuration.getConfiguration().refresh(); System.setProperty("zookeeper.admin.enableServer", "false"); System.setProperty("zookeeper.4lw.commands.whitelist", "*"); } @AfterAll public static void cleanUpEnvironment() throws InterruptedException, IOException { System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.4lw.commands.whitelist"); System.clearProperty("java.security.auth.login.config"); Configuration.getConfiguration().refresh(); System.clearProperty(X509Util.FIPS_MODE_PROPERTY); } @TempDir public Path baseDir; @Test public void testStart() throws Exception { Path baseDir1 = baseDir.resolve("server1"); Path baseDir2 = baseDir.resolve("server2"); Path baseDir3 = baseDir.resolve("server3"); int clientport1 = PortAssignment.unique(); int clientport2 = PortAssignment.unique(); int clientport3 = PortAssignment.unique(); int port4 = PortAssignment.unique(); int port5 = PortAssignment.unique(); int port6 = PortAssignment.unique(); int port7 = PortAssignment.unique(); int port8 = PortAssignment.unique(); int port9 = PortAssignment.unique(); Properties config = new Properties(); config.put("host", "localhost"); config.put("ticktime", "10"); config.put("initLimit", "4000"); config.put("syncLimit", "5"); config.put("server.1", "localhost:" + port4 + ":" + port7); config.put("server.2", "localhost:" + port5 + ":" + port8); config.put("server.3", "localhost:" + port6 + ":" + port9); config.put("quorum.auth.enableSasl", "true"); config.put("quorum.auth.learnerRequireSasl", "true"); config.put("quorum.auth.serverRequireSasl", "true"); config.put("quorum.auth.learner.loginContext", "QuorumLearner"); config.put("quorum.auth.server.loginContext", "QuorumServer"); config.put("quorum.auth.kerberos.servicePrincipal", "servicename/_HOST"); config.put("quorum.cnxn.threads.size", "20"); final Properties configZookeeper1 = new Properties(); configZookeeper1.putAll(config); configZookeeper1.put("clientPort", clientport1 + ""); final Properties configZookeeper2 = new Properties(); configZookeeper2.putAll(config); configZookeeper2.put("clientPort", clientport2 + ""); final Properties configZookeeper3 = new Properties(); configZookeeper3.putAll(config); configZookeeper3.put("clientPort", clientport3 + ""); Files.createDirectories(baseDir1.resolve("data")); Files.write(baseDir1.resolve("data").resolve("myid"), "1".getBytes("ASCII")); Files.createDirectories(baseDir2.resolve("data")); Files.write(baseDir2.resolve("data").resolve("myid"), "2".getBytes("ASCII")); Files.createDirectories(baseDir3.resolve("data")); Files.write(baseDir3.resolve("data").resolve("myid"), "3".getBytes("ASCII")); try (ZooKeeperServerEmbedded zkServer1 = ZooKeeperServerEmbedded.builder().configuration(configZookeeper1).baseDir(baseDir1).exitHandler(ExitHandler.LOG_ONLY).build(); ZooKeeperServerEmbedded zkServer2 = ZooKeeperServerEmbedded.builder().configuration(configZookeeper2).baseDir(baseDir2).exitHandler(ExitHandler.LOG_ONLY).build(); ZooKeeperServerEmbedded zkServer3 = ZooKeeperServerEmbedded.builder().configuration(configZookeeper3).baseDir(baseDir3).exitHandler(ExitHandler.LOG_ONLY).build();) { zkServer1.start(); zkServer2.start(); zkServer3.start(); assertTrue(ClientBase.waitForServerUp(zkServer1.getConnectionString(), 60000)); assertTrue(ClientBase.waitForServerUp(zkServer2.getConnectionString(), 60000)); assertTrue(ClientBase.waitForServerUp(zkServer3.getConnectionString(), 60000)); for (int i = 0; i < 100; i++) { ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("ReplicatedServer*"); System.out.println("status:" + status); if (status.isLeader() && !status.isStandaloneMode() && status.getPeers().size() == 3) { break; } Thread.sleep(100); } ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("ReplicatedServer*"); assertTrue(status.isLeader()); assertTrue(!status.isStandaloneMode()); assertEquals(3, status.getPeers().size()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000204 15051152474 032722 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/ZookeeperServerClusterTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/Zookeeper0100644 0000000 0000000 00000012415 15051152474 034255 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.embedded; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Properties; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ZookeeperServerClusterTest { @BeforeAll public static void setUpEnvironment() { System.setProperty("zookeeper.admin.enableServer", "false"); System.setProperty("zookeeper.4lw.commands.whitelist", "*"); } @AfterAll public static void cleanUpEnvironment() throws InterruptedException, IOException { System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.4lw.commands.whitelist"); } @TempDir public Path baseDir; @Test public void testStart() throws Exception { Path baseDir1 = baseDir.resolve("server1"); Path baseDir2 = baseDir.resolve("server2"); Path baseDir3 = baseDir.resolve("server3"); int clientport1 = PortAssignment.unique(); int clientport2 = PortAssignment.unique(); int clientport3 = PortAssignment.unique(); int port4 = PortAssignment.unique(); int port5 = PortAssignment.unique(); int port6 = PortAssignment.unique(); int port7 = PortAssignment.unique(); int port8 = PortAssignment.unique(); int port9 = PortAssignment.unique(); Properties config = new Properties(); config.put("host", "localhost"); config.put("ticktime", "10"); config.put("initLimit", "4000"); config.put("syncLimit", "5"); config.put("server.1", "localhost:" + port4 + ":" + port7); config.put("server.2", "localhost:" + port5 + ":" + port8); config.put("server.3", "localhost:" + port6 + ":" + port9); final Properties configZookeeper1 = new Properties(); configZookeeper1.putAll(config); configZookeeper1.put("clientPort", clientport1 + ""); final Properties configZookeeper2 = new Properties(); configZookeeper2.putAll(config); configZookeeper2.put("clientPort", clientport2 + ""); final Properties configZookeeper3 = new Properties(); configZookeeper3.putAll(config); configZookeeper3.put("clientPort", clientport3 + ""); Files.createDirectories(baseDir1.resolve("data")); Files.write(baseDir1.resolve("data").resolve("myid"), "1".getBytes("ASCII")); Files.createDirectories(baseDir2.resolve("data")); Files.write(baseDir2.resolve("data").resolve("myid"), "2".getBytes("ASCII")); Files.createDirectories(baseDir3.resolve("data")); Files.write(baseDir3.resolve("data").resolve("myid"), "3".getBytes("ASCII")); try (ZooKeeperServerEmbedded zkServer1 = ZooKeeperServerEmbedded.builder().configuration(configZookeeper1).baseDir(baseDir1).exitHandler(ExitHandler.LOG_ONLY).build(); ZooKeeperServerEmbedded zkServer2 = ZooKeeperServerEmbedded.builder().configuration(configZookeeper2).baseDir(baseDir2).exitHandler(ExitHandler.LOG_ONLY).build(); ZooKeeperServerEmbedded zkServer3 = ZooKeeperServerEmbedded.builder().configuration(configZookeeper3).baseDir(baseDir3).exitHandler(ExitHandler.LOG_ONLY).build();) { zkServer1.start(); zkServer2.start(); zkServer3.start(); assertTrue(ClientBase.waitForServerUp(zkServer1.getConnectionString(), 60000)); assertTrue(ClientBase.waitForServerUp(zkServer2.getConnectionString(), 60000)); assertTrue(ClientBase.waitForServerUp(zkServer3.getConnectionString(), 60000)); for (int i = 0; i < 100; i++) { ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("ReplicatedServer*"); System.out.println("status:" + status); if (status.isLeader() && !status.isStandaloneMode() && status.getPeers().size() == 3) { break; } Thread.sleep(100); } ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("ReplicatedServer*"); assertTrue(status.isLeader()); assertTrue(!status.isStandaloneMode()); assertEquals(3, status.getPeers().size()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000205 15051152474 032723 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/ZookeeperServerEmbeddedTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/Zookeeper0100644 0000000 0000000 00000012332 15051152474 034253 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.embedded; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.file.Path; import java.util.Properties; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.test.ClientBase; import org.junit.function.ThrowingRunnable; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ZookeeperServerEmbeddedTest { @BeforeAll public static void setUpEnvironment() { System.setProperty("zookeeper.admin.enableServer", "false"); System.setProperty("zookeeper.4lw.commands.whitelist", "*"); } @AfterAll public static void cleanUpEnvironment() throws InterruptedException, IOException { System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.4lw.commands.whitelist"); } @TempDir public Path baseDir; @Test public void testStart() throws Exception { int clientPort = PortAssignment.unique(); final Properties configZookeeper = new Properties(); configZookeeper.put("clientPort", clientPort + ""); configZookeeper.put("host", "localhost"); configZookeeper.put("ticktime", "4000"); try (ZooKeeperServerEmbedded zkServer = ZooKeeperServerEmbedded .builder() .baseDir(baseDir) .configuration(configZookeeper) .exitHandler(ExitHandler.LOG_ONLY) .build()) { zkServer.start(); assertTrue(ClientBase.waitForServerUp(zkServer.getConnectionString(), 60000)); for (int i = 0; i < 100; i++) { ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); if (status.isLeader() && status.isStandaloneMode()) { break; } Thread.sleep(100); } ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); assertTrue(status.isLeader()); assertTrue(status.isStandaloneMode()); } // restart (all ports should be closed and the restart should always work) try (ZooKeeperServerEmbedded zkServer = ZooKeeperServerEmbedded .builder() .baseDir(baseDir) .configuration(configZookeeper) .exitHandler(ExitHandler.LOG_ONLY) .build()) { zkServer.start(); assertTrue(ClientBase.waitForServerUp(zkServer.getConnectionString(), 60000)); for (int i = 0; i < 100; i++) { ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); if (status.isLeader() && status.isStandaloneMode()) { break; } Thread.sleep(100); } ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); assertTrue(status.isLeader()); assertTrue(status.isStandaloneMode()); } } @Test public void testBindPortZero() throws Exception { final Properties configZookeeper = new Properties(); final ZooKeeperServerEmbedded.ZookKeeperServerEmbeddedBuilder builder = ZooKeeperServerEmbedded.builder() .baseDir(baseDir) .configuration(configZookeeper) .exitHandler(ExitHandler.LOG_ONLY); // Unconfigured client port will still fail try (ZooKeeperServerEmbedded zkServer = builder.build()) { zkServer.start(); assertThrows(IllegalStateException.class, new ThrowingRunnable() { @Override public void run() throws Throwable { zkServer.getConnectionString(); } }); } // Explicit port zero should work configZookeeper.put("clientPort", "0"); try (ZooKeeperServerEmbedded zkServer = builder.build()) { zkServer.start(); assertThat(zkServer.getConnectionString(), not(endsWith(":0"))); assertTrue(ClientBase.waitForServerUp(zkServer.getConnectionString(), 60000)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_em0100644 0000000 0000000 00000000210 15051152474 032717 xustar000000000 0000000 136 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/ZookeeperServerSslEmbeddedTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/embedded/Zookeeper0100644 0000000 0000000 00000012237 15051152474 034257 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.embedded; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; public class ZookeeperServerSslEmbeddedTest { @BeforeAll public static void setUpEnvironment() { System.setProperty("zookeeper.admin.enableServer", "false"); System.setProperty("zookeeper.4lw.commands.whitelist", "*"); } @AfterAll public static void cleanUpEnvironment() throws InterruptedException, IOException { System.clearProperty("zookeeper.admin.enableServer"); System.clearProperty("zookeeper.4lw.commands.whitelist"); System.clearProperty("zookeeper.ssl.trustStore.location"); System.clearProperty("zookeeper.ssl.trustStore.password"); System.clearProperty("zookeeper.ssl.trustStore.type"); } @TempDir public Path baseDir; @Test public void testStart() throws Exception { int clientPort = PortAssignment.unique(); int clientSecurePort = PortAssignment.unique(); final Properties configZookeeper = new Properties(); configZookeeper.put("clientPort", clientPort + ""); configZookeeper.put("secureClientPort", clientSecurePort + ""); configZookeeper.put("host", "localhost"); configZookeeper.put("ticktime", "4000"); // Netty is required for TLS configZookeeper.put("serverCnxnFactory", org.apache.zookeeper.server.NettyServerCnxnFactory.class.getName()); File testKeyStore = new File("src/test/resources/embedded/testKeyStore.jks"); File testTrustStore = new File("src/test/resources/embedded/testTrustStore.jks"); assertTrue(testKeyStore.isFile()); assertTrue(testTrustStore.isFile()); configZookeeper.put("ssl.keyStore.location", testKeyStore.getAbsolutePath()); configZookeeper.put("ssl.keyStore.password", "testpass"); configZookeeper.put("ssl.keyStore.type", "JKS"); System.setProperty("zookeeper.ssl.trustStore.location", testTrustStore.getAbsolutePath()); System.setProperty("zookeeper.ssl.trustStore.password", "testpass"); System.setProperty("zookeeper.ssl.trustStore.type", "JKS"); try (ZooKeeperServerEmbedded zkServer = ZooKeeperServerEmbedded .builder() .baseDir(baseDir) .configuration(configZookeeper) .exitHandler(ExitHandler.LOG_ONLY) .build()) { zkServer.start(); assertTrue(ClientBase.waitForServerUp(zkServer.getConnectionString(), 60000)); for (int i = 0; i < 100; i++) { ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); if (status.isLeader() && status.isStandaloneMode()) { break; } Thread.sleep(100); } ZookeeperServeInfo.ServerInfo status = ZookeeperServeInfo.getStatus("StandaloneServer*"); assertTrue(status.isLeader()); assertTrue(status.isStandaloneMode()); CountDownLatch l = new CountDownLatch(1); ZKClientConfig zKClientConfig = new ZKClientConfig(); zKClientConfig.setProperty("zookeeper.client.secure", "true"); // only netty supports TLS zKClientConfig.setProperty("zookeeper.clientCnxnSocket", org.apache.zookeeper.ClientCnxnSocketNetty.class.getName()); try (ZooKeeper zk = new ZooKeeper(zkServer.getSecureConnectionString(), 60000, (WatchedEvent event) -> { switch (event.getState()) { case SyncConnected: l.countDown(); break; } }, zKClientConfig)) { assertTrue(zk.getClientConfig().getBoolean(ZKClientConfig.SECURE_CLIENT)); assertTrue(l.await(10, TimeUnit.SECONDS)); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000177 15051152474 032733 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/AvgMinMaxCounterSetTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/AvgMinMaxCo0100644 0000000 0000000 00000007126 15051152474 034160 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class AvgMinMaxCounterSetTest extends ZKTestCase { private AvgMinMaxCounterSet testCounterSet; @BeforeEach public void initCounter() { testCounterSet = new AvgMinMaxCounterSet("test"); } private void addDataPoints() { testCounterSet.add("key1", 0); testCounterSet.add("key1", 1); testCounterSet.add("key2", 2); testCounterSet.add("key2", 3); testCounterSet.add("key2", 4); testCounterSet.add("key2", 5); } @Test public void testReset() { addDataPoints(); testCounterSet.reset(); Map values = testCounterSet.values(); assertEquals(10, values.size(), "There should be 10 values in the set"); assertEquals(0D, values.get("avg_key1_test"), "avg_key1_test should =0"); assertEquals(0L, values.get("min_key1_test"), "min_key1_test should =0"); assertEquals(0L, values.get("max_key1_test"), "max_key1_test should =0"); assertEquals(0L, values.get("cnt_key1_test"), "cnt_key1_test should =0"); assertEquals(0L, values.get("sum_key1_test"), "sum_key1_test should =0"); assertEquals(0D, values.get("avg_key2_test"), "avg_key2_test should =0"); assertEquals(0L, values.get("min_key2_test"), "min_key2_test should =0"); assertEquals(0L, values.get("max_key2_test"), "max_key2_test should =0"); assertEquals(0L, values.get("cnt_key2_test"), "cnt_key2_test should =0"); assertEquals(0L, values.get("sum_key2_test"), "sum_key2_test should =0"); } @Test public void testValues() { addDataPoints(); Map values = testCounterSet.values(); assertEquals(10, values.size(), "There should be 10 values in the set"); assertEquals(0.5D, values.get("avg_key1_test"), "avg_key1_test should =0.5"); assertEquals(0L, values.get("min_key1_test"), "min_key1_test should =0"); assertEquals(1L, values.get("max_key1_test"), "max_key1_test should =1"); assertEquals(2L, values.get("cnt_key1_test"), "cnt_key1_test should =2"); assertEquals(1L, values.get("sum_key1_test"), "sum_key1_test should =1"); assertEquals(3.5, values.get("avg_key2_test"), "avg_key2_test should =3.5"); assertEquals(2L, values.get("min_key2_test"), "min_key2_test should =2"); assertEquals(5L, values.get("max_key2_test"), "max_key2_test should =5"); assertEquals(4L, values.get("cnt_key2_test"), "cnt_key2_test should =4"); assertEquals(14L, values.get("sum_key2_test"), "sum_key2_test should =14"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000211 15051152474 032720 xustar000000000 0000000 137 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/AvgMinMaxPercentileCounterSetTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/AvgMinMaxPe0100644 0000000 0000000 00000012007 15051152474 034155 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class AvgMinMaxPercentileCounterSetTest extends ZKTestCase { private AvgMinMaxPercentileCounterSet testCounterSet; @BeforeEach public void initCounter() { testCounterSet = new AvgMinMaxPercentileCounterSet("test"); } private void addDataPoints() { for (int i = 0; i < 1000; i++) { testCounterSet.add("key1", i); } for (int i = 1000; i < 2000; i++) { testCounterSet.add("key2", i); } } @Test public void testReset() { addDataPoints(); testCounterSet.reset(); Map values = testCounterSet.values(); assertEquals(0D, values.get("avg_key1_test"), "avg_key1_test should =0"); assertEquals(0L, values.get("min_key1_test"), "min_key1_test should =0"); assertEquals(0L, values.get("max_key1_test"), "max_key1_test should =0"); assertEquals(0L, values.get("cnt_key1_test"), "cnt_key1_test should =0"); assertEquals(0L, values.get("sum_key1_test"), "sum_key1_test should =0"); assertEquals(0L, values.get("p50_key1_test"), "p50_key1_test should have p50=0"); assertEquals(0L, values.get("p95_key1_test"), "p95_key1_test should have p95=0"); assertEquals(0L, values.get("p99_key1_test"), "p99_key1_test should have p99=0"); assertEquals(0L, values.get("p999_key1_test"), "p999_key1_test should have p999=0"); assertEquals(0D, values.get("avg_key2_test"), "avg_key2_test should =0"); assertEquals(0L, values.get("min_key2_test"), "min_key2_test should =0"); assertEquals(0L, values.get("max_key2_test"), "max_key2_test should =0"); assertEquals(0L, values.get("cnt_key2_test"), "cnt_key2_test should =0"); assertEquals(0L, values.get("sum_key2_test"), "sum_key2_test should =0"); assertEquals(0L, values.get("p50_key2_test"), "p50_key2_test should have p50=0"); assertEquals(0L, values.get("p95_key2_test"), "p95_key2_test should have p95=0"); assertEquals(0L, values.get("p99_key2_test"), "p99_key2_test should have p99=0"); assertEquals(0L, values.get("p999_key2_test"), "p999_key2_test should have p999=0"); } @Test public void testValues() { addDataPoints(); Map values = testCounterSet.values(); assertEquals(18, values.size(), "There should be 18 values in the set"); assertEquals(999D / 2, values.get("avg_key1_test"), "avg_key1_test should =499.5"); assertEquals(0L, values.get("min_key1_test"), "min_key1_test should =0"); assertEquals(999L, values.get("max_key1_test"), "max_key1_test should =999"); assertEquals(1000L, values.get("cnt_key1_test"), "cnt_key1_test should =1000"); assertEquals(999 * 500L, values.get("sum_key1_test"), "sum_key1_test should =999*500"); assertEquals(500L, values.get("p50_key1_test"), "p50_key1_test should have p50=500"); assertEquals(950L, values.get("p95_key1_test"), "p95_key1_test should have p95=950"); assertEquals(990L, values.get("p99_key1_test"), "p99_key1_test should have p99=990"); assertEquals(999L, values.get("p999_key1_test"), "p999_key1_test should have p999=999"); assertEquals(1000 + 999D / 2, values.get("avg_key2_test"), "avg_key2_test should =3.5"); assertEquals(1000L, values.get("min_key2_test"), "min_key2_test should =2"); assertEquals(1999L, values.get("max_key2_test"), "max_key2_test should =5"); assertEquals(1000L, values.get("cnt_key2_test"), "cnt_key2_test should =4"); assertEquals(2999 * 500L, values.get("sum_key2_test"), "sum_key2_test should =14"); assertEquals(1500L, values.get("p50_key2_test"), "p50_key2_test should have p50=1500"); assertEquals(1950L, values.get("p95_key2_test"), "p95_key2_test should have p95=1950"); assertEquals(1990L, values.get("p99_key2_test"), "p99_key2_test should have p99=1990"); assertEquals(1999L, values.get("p999_key2_test"), "p999_key2_test should have p999=1999"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000206 15051152474 032724 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/AvgMinMaxPercentileCounterTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/AvgMinMaxPe0100644 0000000 0000000 00000006170 15051152474 034161 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class AvgMinMaxPercentileCounterTest extends ZKTestCase { private AvgMinMaxPercentileCounter testCounter; @BeforeEach public void initCounter() { testCounter = new AvgMinMaxPercentileCounter("test"); } private void addDataPoints() { for (int i = 0; i < 1000; i++) { testCounter.add(i); } } @Test public void testReset() { addDataPoints(); testCounter.reset(); Map values = testCounter.values(); assertEquals(9, values.size(), "There should be 9 values in the set"); assertEquals(0D, values.get("avg_test"), "should avg=0"); assertEquals(0L, values.get("min_test"), "should have min=0"); assertEquals(0L, values.get("max_test"), "should have max=0"); assertEquals(0L, values.get("cnt_test"), "should have cnt=0"); assertEquals(0L, values.get("sum_test"), "should have sum=0"); assertEquals(0L, values.get("p50_test"), "should have p50=0"); assertEquals(0L, values.get("p95_test"), "should have p95=0"); assertEquals(0L, values.get("p99_test"), "should have p99=0"); assertEquals(0L, values.get("p999_test"), "should have p999=0"); } @Test public void testValues() { addDataPoints(); Map values = testCounter.values(); assertEquals(9, values.size(), "There should be 9 values in the set"); assertEquals(999D / 2, values.get("avg_test"), "should avg=499.5"); assertEquals(0L, values.get("min_test"), "should have min=0"); assertEquals(999L, values.get("max_test"), "should have max=999"); assertEquals(1000L, values.get("cnt_test"), "should have cnt=1000"); assertEquals(999 * 500L, values.get("sum_test"), "should have sum=999*500"); assertEquals(500L, values.get("p50_test"), "should have p50=500"); assertEquals(950L, values.get("p95_test"), "should have p95=950"); assertEquals(990L, values.get("p99_test"), "should have p99=990"); assertEquals(999L, values.get("p999_test"), "should have p999=999"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_me0100644 0000000 0000000 00000000174 15051152474 032730 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/SimpleCounterSetTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/metric/SimpleCount0100644 0000000 0000000 00000004210 15051152474 034300 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.metric; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class SimpleCounterSetTest extends ZKTestCase { @Test public void testValues() { final SimpleCounterSet simpleCounterSet = createSimpleCounterSetAddData("test1"); final Map values = simpleCounterSet.values(); assertEquals(2, values.size()); assertEquals(30L , values.get("key1_test1")); assertEquals(70L , values.get("key2_test1")); } @Test public void testReset() { final SimpleCounterSet simpleCounterSet = createSimpleCounterSetAddData("test2"); simpleCounterSet.reset(); final Map values = simpleCounterSet.values(); assertEquals(2, values.size()); assertEquals(0L , values.get("key1_test2")); assertEquals(0L , values.get("key2_test2")); } private SimpleCounterSet createSimpleCounterSetAddData(final String name) { final SimpleCounterSet simpleCounterSet = new SimpleCounterSet(name); simpleCounterSet.add("key1", 10); simpleCounterSet.add("key1", 20); simpleCounterSet.add("key2", 30); simpleCounterSet.add("key2", 40); return simpleCounterSet; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000176 15051152474 032735 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/EmptySnapshotTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/EmptyS0100644 0000000 0000000 00000006032 15051152474 034324 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.server.DataTree; import org.junit.jupiter.api.Test; /** * This test checks that the server does not create empty snapshot files if the * disk is full. */ public class EmptySnapshotTest { static class MockFileSnap extends FileSnap { MockFileSnap(File snapDir) { super(snapDir); } public synchronized void serialize(DataTree dt, Map sessions, File snapShot, boolean fsync) throws IOException { // Create empty new file. assertTrue(snapShot.createNewFile()); throw new IOException("Created empty snapshot file from " + "MockFileSnap::serialize()"); } } @Test public void testNoEmptySnapshot() throws Exception { File tmpFile = File.createTempFile("empty-snapshot-test", ".junit", new File(System.getProperty("build.test.dir", "build"))); File tmpDataDir = new File(tmpFile + ".dir"); assertFalse(tmpDataDir.exists()); assertTrue(tmpDataDir.mkdirs()); FileTxnSnapLog snapLog = new FileTxnSnapLog(tmpDataDir, tmpDataDir); snapLog.snapLog = new MockFileSnap(snapLog.dataDir); assertEquals(0, ((FileSnap) snapLog.snapLog).findNRecentSnapshots(10).size()); DataTree tree = new DataTree(); tree.createNode("/empty-snapshot-test-1", "data".getBytes(), null, -1, -1, 1, 1); try { snapLog.save(tree, new ConcurrentHashMap<>(), false); fail("Should have thrown an IOException"); } catch (IOException e) { // no op } assertEquals(0, ((FileSnap) snapLog.snapLog).findNRecentSnapshots(10).size()); snapLog.snapLog = new FileSnap(snapLog.dataDir); snapLog.save(tree, new ConcurrentHashMap<>(), false); assertEquals(1, ((FileSnap) snapLog.snapLog).findNRecentSnapshots(10).size()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000173 15051152474 032732 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/FileTxnLogTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/FileTx0100644 0000000 0000000 00000037170 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.stream.Collectors; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileTxnLogTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FileTxnLogTest.class); private static final int KB = 1024; @Test public void testInvalidPreallocSize() { assertEquals(10 * KB, FilePadding.calculateFileSizeWithPadding(7 * KB, 10 * KB, 0), "file should not be padded"); assertEquals(10 * KB, FilePadding.calculateFileSizeWithPadding(7 * KB, 10 * KB, -1), "file should not be padded"); } @Test public void testCalculateFileSizeWithPaddingWhenNotToCurrentSize() { assertEquals(10 * KB, FilePadding.calculateFileSizeWithPadding(5 * KB, 10 * KB, 10 * KB), "file should not be padded"); } @Test public void testCalculateFileSizeWithPaddingWhenCloseToCurrentSize() { assertEquals(20 * KB, FilePadding.calculateFileSizeWithPadding(7 * KB, 10 * KB, 10 * KB), "file should be padded an additional 10 KB"); } @Test public void testFileSizeGreaterThanPosition() { assertEquals(40 * KB, FilePadding.calculateFileSizeWithPadding(31 * KB, 10 * KB, 10 * KB), "file should be padded to 40 KB"); } @Test public void testPreAllocSizeSmallerThanTxnData() throws IOException { File logDir = ClientBase.createTmpDir(); FileTxnLog fileTxnLog = new FileTxnLog(logDir); // Set a small preAllocSize (.5 MB) final int preAllocSize = 500 * KB; FilePadding.setPreallocSize(preAllocSize); // Create dummy txn larger than preAllocSize // Since the file padding inserts a 0, we will fill the data with 0xff to ensure we corrupt the data if we put the 0 in the data byte[] data = new byte[2 * preAllocSize]; Arrays.fill(data, (byte) 0xff); // Append and commit 2 transactions to the log // Prior to ZOOKEEPER-2249, attempting to pad in association with the second transaction will corrupt the first fileTxnLog.append(new Request(0, 0, 0, new TxnHeader(1, 1, 1, 1, ZooDefs.OpCode.create), new CreateTxn("/testPreAllocSizeSmallerThanTxnData1", data, ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 0), 0)); fileTxnLog.commit(); fileTxnLog.append(new Request(0, 0, 0, new TxnHeader(1, 1, 2, 2, ZooDefs.OpCode.create), new CreateTxn("/testPreAllocSizeSmallerThanTxnData2", new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 0), 0)); fileTxnLog.commit(); fileTxnLog.close(); // Read the log back from disk, this will throw a java.io.IOException: CRC check failed prior to ZOOKEEPER-2249 FileTxnLog.FileTxnIterator fileTxnIterator = new FileTxnLog.FileTxnIterator(logDir, 0); // Verify the data in the first transaction CreateTxn createTxn = (CreateTxn) fileTxnIterator.getTxn(); assertTrue(Arrays.equals(createTxn.getData(), data)); // Verify the data in the second transaction fileTxnIterator.next(); createTxn = (CreateTxn) fileTxnIterator.getTxn(); assertTrue(Arrays.equals(createTxn.getData(), new byte[]{})); } @Test public void testSetPreallocSize() { long customPreallocSize = 10101; FileTxnLog.setPreallocSize(customPreallocSize); assertThat(FilePadding.getPreAllocSize(), is(equalTo(customPreallocSize))); } public void testSyncThresholdExceedCount() throws IOException { // Given ... // Set threshold to -1, as after the first commit it takes 0ms to commit to disk. java.lang.System.setProperty(FileTxnLog.ZOOKEEPER_FSYNC_WARNING_THRESHOLD_MS_PROPERTY, "-1"); ServerStats.Provider providerMock = mock(ServerStats.Provider.class); ServerStats serverStats = new ServerStats(providerMock); File logDir = ClientBase.createTmpDir(); FileTxnLog fileTxnLog = new FileTxnLog(logDir); fileTxnLog.setServerStats(serverStats); // Verify serverStats is 0 before any commit assertEquals(0L, serverStats.getFsyncThresholdExceedCount()); // When ... for (int i = 0; i < 50; i++) { fileTxnLog.append(new Request(0, 0, 0, new TxnHeader(1, 1, 1, 1, ZooDefs.OpCode.create), new CreateTxn("/testFsyncThresholdCountIncreased", new byte[]{}, ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 0), 0)); fileTxnLog.commit(); // only 1 commit, otherwise it will be flaky // Then ... verify serverStats is updated to the number of commits (as threshold is set to 0) assertEquals((long) i + 1, serverStats.getFsyncThresholdExceedCount()); } } private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final int CONNECTION_TIMEOUT = 3000; // Overhead is about 150 bytes for txn created in this test private static final int NODE_SIZE = 1024; private final long PREALLOCATE = 512; private final long LOG_SIZE_LIMIT = 1024 * 4; /** * Test that log size get update correctly */ @Test public void testGetCurrentLogSize() throws Exception { FileTxnLog.setTxnLogSizeLimit(-1); File tmpDir = ClientBase.createTmpDir(); FileTxnLog log = new FileTxnLog(tmpDir); FileTxnLog.setPreallocSize(PREALLOCATE); CreateRequest record = new CreateRequest(null, new byte[NODE_SIZE], ZooDefs.Ids.OPEN_ACL_UNSAFE, 0); long logSize = 0; long position = 0; int fileHeaderSize = 16; int zxid = 1; for (int i = 0; i < 4; i++) { if (i == 0) { logSize += fileHeaderSize; position += fileHeaderSize; } log.append(new Request(0, 0, 0, new TxnHeader(0, 0, zxid++, 0, 0), record, 0)); logSize += PREALLOCATE; assertEquals(logSize, log.getCurrentLogSize()); assertEquals(position, log.filePosition); } log.commit(); TxnHeader mockHeader = new TxnHeader(0, 0, 0, 0, 0); int totalSize = fileHeaderSize + calculateSingleRecordLength(mockHeader, record) * 4; assertEquals(totalSize, log.getCurrentLogSize()); assertEquals(totalSize, log.filePosition); assertTrue(log.getCurrentLogSize() > (zxid - 1) * NODE_SIZE); logSize = FilePadding.calculateFileSizeWithPadding(log.filePosition, PREALLOCATE * 4, PREALLOCATE); position = totalSize; boolean recalculate = true; for (int i = 0; i < 4; i++) { log.append(new Request(0, 0, 0, new TxnHeader(0, 0, zxid++, 0, 0), record, 0)); if (recalculate) { recalculate = false; } else { logSize += PREALLOCATE; } assertEquals(logSize, log.getCurrentLogSize()); assertEquals(position, log.filePosition); } log.commit(); totalSize += calculateSingleRecordLength(mockHeader, record) * 4; assertEquals(totalSize, log.getCurrentLogSize()); assertEquals(totalSize, log.filePosition); assertTrue(log.getCurrentLogSize() > (zxid - 1) * NODE_SIZE); } /** * Test that the server can correctly load the data when there are multiple * txnlogs per snapshot */ @Test public void testLogSizeLimit() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); // Need to override preallocate set by setupTestEnv() // We don't need to unset these values since each unit test run in // a separate JVM instance FileTxnLog.setPreallocSize(PREALLOCATE); FileTxnLog.setTxnLogSizeLimit(LOG_SIZE_LIMIT); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); // Generate transactions HashSet zxids = new HashSet<>(); byte[] bytes = new byte[NODE_SIZE]; Random random = new Random(); random.nextBytes(bytes); // We will create enough txn to generate 3 logs long txnCount = LOG_SIZE_LIMIT / NODE_SIZE / 2 * 5; LOG.info("Creating {} txns", txnCount); try { for (long i = 0; i < txnCount; i++) { Stat stat = new Stat(); zk.create("/node-" + i, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.getData("/node-" + i, null, stat); zxids.add(stat.getCzxid()); } } finally { zk.close(); } // shutdown f.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server to shutdown"); File logDir = new File(tmpDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); File[] txnLogs = FileTxnLog.getLogFiles(logDir.listFiles(), 0); assertEquals(3, txnLogs.length, "Unexpected number of logs"); // Log size should not exceed limit by more than one node size; long threshold = LOG_SIZE_LIMIT + NODE_SIZE; LOG.info(txnLogs[0].getAbsolutePath()); assertTrue(threshold > txnLogs[0].length(), "Exceed log size limit: " + txnLogs[0].length()); LOG.info(txnLogs[1].getAbsolutePath()); assertTrue(threshold > txnLogs[1].length(), "Exceed log size limit " + txnLogs[1].length()); // Start database only zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); zks.startdata(); ZKDatabase db = zks.getZKDatabase(); for (long i = 0; i < txnCount; i++) { Stat stat = new Stat(); byte[] data = db.getData("/node-" + i, stat, null); assertArrayEquals(bytes, data, "Missmatch data"); assertTrue(zxids.contains(stat.getMzxid()), "Unknown zxid "); } } private void prepareTxnLogs(File dir, int n) throws IOException { FileTxnLog.setTxnLogSizeLimit(1); FileTxnLog log = new FileTxnLog(dir); CreateRequest record = new CreateRequest(null, new byte[NODE_SIZE], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT.toFlag()); int zxid = 1; for (int i = 0; i < n; i++) { log.append(new Request(0, 0, 0, new TxnHeader(0, 0, zxid, 0, -1), record, zxid)); zxid++; log.commit(); } log.close(); } @Test public void testEmptyTailTxnLog() throws IOException { long limit = FileTxnLog.getTxnLogSizeLimit(); // prepare a database with logs File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); prepareTxnLogs(tmpDir, 4); // find the tail log and clear List files = Arrays. stream(Objects.requireNonNull(tmpDir.listFiles((File f, String name) -> name.startsWith("log.")))). sorted(Comparator.comparing(File::getName)). collect(Collectors.toList()); File toClear = files.get(files.size() - 1); PrintWriter writer = new PrintWriter(toClear); writer.close(); LOG.info("Clear the tail log file {}", toClear.getName()); // open txn log and iterate try { FileTxnLog.FileTxnIterator itr = new FileTxnLog.FileTxnIterator(tmpDir, 0x0, false); while (itr.next()) {} } catch (EOFException ex) {} FileTxnLog.FileTxnIterator itr = new FileTxnLog.FileTxnIterator(tmpDir, 0x0, false); while (itr.next()) {} FileTxnLog.setTxnLogSizeLimit(limit); } @Test public void testEmptyMedianTxnLog() throws IOException { long limit = FileTxnLog.getTxnLogSizeLimit(); // prepare a database with logs File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); prepareTxnLogs(tmpDir, 4); // find the median log and clear List files = Arrays. stream(Objects.requireNonNull(tmpDir.listFiles((File f, String name) -> name.startsWith("log.")))). sorted(Comparator.comparing(File::getName)). collect(Collectors.toList()); File toClear = files.get(files.size() - 2); PrintWriter writer = new PrintWriter(toClear); writer.close(); LOG.info("Clear the median log file {}", toClear.getName()); // open txn log and iterate, should throw EOFException boolean isEof = false; try { FileTxnLog.FileTxnIterator itr = new FileTxnLog.FileTxnIterator(tmpDir, 0x0, false); while (itr.next()) {} } catch (EOFException ex) { isEof = true; } assertTrue(isEof, "Median txn log file empty should throw Exception"); FileTxnLog.setTxnLogSizeLimit(limit); } private int calculateSingleRecordLength(TxnHeader txnHeader, Record record) throws IOException { int crcLength = 8; int dataLength = 4; int recordLength = Util.marshallTxnEntry(txnHeader, record, null).length; int endFlagLength = 1; return crcLength + dataLength + recordLength + endFlagLength; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000206 15051152474 032727 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/FileTxnSnapLogMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/FileTx0100644 0000000 0000000 00000010450 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FileTxnSnapLogMetricsTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(FileTxnSnapLogMetricsTest.class); @TempDir File logDir; @TempDir File snapDir; private ServerCnxnFactory startServer() throws Exception { ZooKeeperServer zkServer = new ZooKeeperServer(snapDir, logDir, 3000); ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory(0, -1); cnxnFactory.startup(zkServer); return cnxnFactory; } @AfterEach public void cleanup() throws Exception { SyncRequestProcessor.setSnapCount(ZooKeeperServer.getSnapCount()); } @Test public void testFileTxnSnapLogMetrics() throws Exception { SyncRequestProcessor.setSnapCount(100); ServerCnxnFactory cnxnFactory = startServer(); String connectString = "127.0.0.1:" + cnxnFactory.getLocalPort(); // Snapshot in load data. assertEquals(1L, MetricsUtils.currentServerMetrics().get("cnt_snapshottime")); byte[] data = new byte[500]; ZooKeeper zk = ClientBase.createZKClient(connectString); for (int i = 0; i < 150; i++) { zk.create("/path" + i, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // It is possible that above writes will trigger more than one snapshot due to randomization. waitForMetric("cnt_snapshottime", greaterThanOrEqualTo(2L), 10); // Pauses snapshot and logs more txns. cnxnFactory.getZooKeeperServer().getTxnLogFactory().snapLog.close(); zk.create("/" + 1000, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/" + 1001, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // Restart server to count startup metrics. cnxnFactory.shutdown(); ServerMetrics.getMetrics().resetAll(); cnxnFactory = startServer(); Map values = MetricsUtils.currentServerMetrics(); LOG.info("txn loaded during start up {}", values.get("max_startup_txns_loaded")); assertEquals(1L, values.get("cnt_startup_txns_loaded")); assertThat((long) values.get("max_startup_txns_loaded"), greaterThan(0L)); assertEquals(1L, values.get("cnt_startup_txns_load_time")); assertThat((long) values.get("max_startup_txns_load_time"), greaterThanOrEqualTo(0L)); assertEquals(1L, values.get("cnt_startup_snap_load_time")); assertThat((long) values.get("max_startup_snap_load_time"), greaterThan(0L)); cnxnFactory.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000177 15051152474 032736 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/FileTxnSnapLogTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/FileTx0100644 0000000 0000000 00000041255 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.TestUtils; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class FileTxnSnapLogTest { private File tmpDir; private File logDir; private File snapDir; private File logVersionDir; private File snapVersionDir; @BeforeEach public void setUp() throws Exception { tmpDir = ClientBase.createEmptyTestDir(); logDir = new File(tmpDir, "logdir"); snapDir = new File(tmpDir, "snapdir"); } @AfterEach public void tearDown() throws Exception { if (tmpDir != null) { TestUtils.deleteFileRecursively(tmpDir); } this.tmpDir = null; this.logDir = null; this.snapDir = null; this.logVersionDir = null; this.snapVersionDir = null; } private File createVersionDir(File parentDir) { File versionDir = new File(parentDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); versionDir.mkdirs(); return versionDir; } private void createLogFile(File dir, long zxid) throws IOException { File file = new File(dir.getPath() + File.separator + Util.makeLogName(zxid)); file.createNewFile(); } private void createSnapshotFile(File dir, long zxid) throws IOException { File file = new File(dir.getPath() + File.separator + Util.makeSnapshotName(zxid)); file.createNewFile(); } private void twoDirSetupWithCorrectFiles() throws IOException { logVersionDir = createVersionDir(logDir); snapVersionDir = createVersionDir(snapDir); // transaction log files in log dir createLogFile(logVersionDir, 1); createLogFile(logVersionDir, 2); // snapshot files in snap dir createSnapshotFile(snapVersionDir, 1); createSnapshotFile(snapVersionDir, 2); } private void singleDirSetupWithCorrectFiles() throws IOException { logVersionDir = createVersionDir(logDir); // transaction log and snapshot files in the same dir createLogFile(logVersionDir, 1); createLogFile(logVersionDir, 2); createSnapshotFile(logVersionDir, 1); createSnapshotFile(logVersionDir, 2); } private FileTxnSnapLog createFileTxnSnapLogWithNoAutoCreateDataDir(File logDir, File snapDir) throws IOException { return createFileTxnSnapLogWithAutoCreateDataDir(logDir, snapDir, "false"); } private FileTxnSnapLog createFileTxnSnapLogWithAutoCreateDataDir( File logDir, File snapDir, String autoCreateValue) throws IOException { String priorAutocreateDirValue = System.getProperty(FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE); System.setProperty(FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE, autoCreateValue); FileTxnSnapLog fileTxnSnapLog; try { fileTxnSnapLog = new FileTxnSnapLog(logDir, snapDir); } finally { if (priorAutocreateDirValue == null) { System.clearProperty(FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE); } else { System.setProperty(FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE, priorAutocreateDirValue); } } return fileTxnSnapLog; } private FileTxnSnapLog createFileTxnSnapLogWithAutoCreateDB( File logDir, File snapDir, String autoCreateValue) throws IOException { String priorAutocreateDBValue = System.getProperty(FileTxnSnapLog.ZOOKEEPER_DB_AUTOCREATE); System.setProperty(FileTxnSnapLog.ZOOKEEPER_DB_AUTOCREATE, autoCreateValue); FileTxnSnapLog fileTxnSnapLog; try { fileTxnSnapLog = new FileTxnSnapLog(logDir, snapDir); } finally { if (priorAutocreateDBValue == null) { System.clearProperty(FileTxnSnapLog.ZOOKEEPER_DB_AUTOCREATE); } else { System.setProperty(FileTxnSnapLog.ZOOKEEPER_DB_AUTOCREATE, priorAutocreateDBValue); } } return fileTxnSnapLog; } /** * Test verifies the auto creation of log dir and snap dir. * Sets "zookeeper.datadir.autocreate" to true. */ @Test public void testWithAutoCreateDataDir() throws IOException { assertFalse(logDir.exists(), "log directory already exists"); assertFalse(snapDir.exists(), "snapshot directory already exists"); FileTxnSnapLog fileTxnSnapLog = createFileTxnSnapLogWithAutoCreateDataDir(logDir, snapDir, "true"); assertTrue(logDir.exists()); assertTrue(snapDir.exists()); assertTrue(fileTxnSnapLog.getDataLogDir().exists()); assertTrue(fileTxnSnapLog.getSnapDir().exists()); } /** * Test verifies server should fail when log dir or snap dir doesn't exist. * Sets "zookeeper.datadir.autocreate" to false. */ @Test public void testWithoutAutoCreateDataDir() throws Exception { assertThrows(FileTxnSnapLog.DatadirException.class, () -> { assertFalse(logDir.exists(), "log directory already exists"); assertFalse(snapDir.exists(), "snapshot directory already exists"); try { createFileTxnSnapLogWithAutoCreateDataDir(logDir, snapDir, "false"); } catch (FileTxnSnapLog.DatadirException e) { assertFalse(logDir.exists()); assertFalse(snapDir.exists()); // rethrow exception throw e; } fail("Expected exception from FileTxnSnapLog"); }); } private void attemptAutoCreateDB( File dataDir, File snapDir, Map sessions, String autoCreateValue, long expectedValue) throws IOException { sessions.clear(); FileTxnSnapLog fileTxnSnapLog = createFileTxnSnapLogWithAutoCreateDB(dataDir, snapDir, autoCreateValue); long zxid = fileTxnSnapLog.restore(new DataTree(), sessions, new FileTxnSnapLog.PlayBackListener() { @Override public void onTxnLoaded(TxnHeader hdr, Record rec, TxnDigest digest) { // empty by default } }); assertEquals(expectedValue, zxid, "unexpected zxid"); } @Test public void testAutoCreateDB() throws IOException { assertTrue(logDir.mkdir(), "cannot create log directory"); assertTrue(snapDir.mkdir(), "cannot create snapshot directory"); File initFile = new File(logDir, "initialize"); assertFalse(initFile.exists(), "initialize file already exists"); Map sessions = new ConcurrentHashMap<>(); attemptAutoCreateDB(logDir, snapDir, sessions, "false", -1L); attemptAutoCreateDB(logDir, snapDir, sessions, "true", 0L); assertTrue(initFile.createNewFile(), "cannot create initialize file"); attemptAutoCreateDB(logDir, snapDir, sessions, "false", 0L); } @Test public void testGetTxnLogSyncElapsedTime() throws IOException { FileTxnSnapLog fileTxnSnapLog = createFileTxnSnapLogWithAutoCreateDataDir(logDir, snapDir, "true"); TxnHeader hdr = new TxnHeader(1, 1, 1, 1, ZooDefs.OpCode.setData); Record txn = new SetDataTxn("/foo", new byte[0], 1); Request req = new Request(0, 0, 0, hdr, txn, 0); try { fileTxnSnapLog.append(req); fileTxnSnapLog.commit(); long syncElapsedTime = fileTxnSnapLog.getTxnLogElapsedSyncTime(); assertNotEquals(-1L, syncElapsedTime, "Did not update syncElapsedTime!"); } finally { fileTxnSnapLog.close(); } } @Test public void testDirCheckWithCorrectFiles() throws IOException { twoDirSetupWithCorrectFiles(); try { createFileTxnSnapLogWithNoAutoCreateDataDir(logDir, snapDir); } catch (FileTxnSnapLog.LogDirContentCheckException | FileTxnSnapLog.SnapDirContentCheckException e) { fail("Should not throw ContentCheckException."); } } @Test public void testDirCheckWithSingleDirSetup() throws IOException { singleDirSetupWithCorrectFiles(); try { createFileTxnSnapLogWithNoAutoCreateDataDir(logDir, logDir); } catch (FileTxnSnapLog.LogDirContentCheckException | FileTxnSnapLog.SnapDirContentCheckException e) { fail("Should not throw ContentCheckException."); } } @Test public void testDirCheckWithSnapFilesInLogDir() throws IOException { assertThrows(FileTxnSnapLog.LogDirContentCheckException.class, () -> { twoDirSetupWithCorrectFiles(); // add snapshot files to the log version dir createSnapshotFile(logVersionDir, 3); createSnapshotFile(logVersionDir, 4); createFileTxnSnapLogWithNoAutoCreateDataDir(logDir, snapDir); }); } @Test public void testDirCheckWithLogFilesInSnapDir() throws IOException { assertThrows(FileTxnSnapLog.SnapDirContentCheckException.class, () -> { twoDirSetupWithCorrectFiles(); // add transaction log files to the snap version dir createLogFile(snapVersionDir, 3); createLogFile(snapVersionDir, 4); createFileTxnSnapLogWithNoAutoCreateDataDir(logDir, snapDir); }); } /** * Make sure the ACL is exist in the ACL map after SNAP syncing. * * ZooKeeper uses ACL reference id and count to save the space in snapshot. * During fuzzy snapshot sync, the reference count may not be updated * correctly in case like the znode is already exist. * * When ACL reference count reaches 0, it will be deleted from the cache, * but actually there might be other nodes still using it. When visiting * a node with the deleted ACL id, it will be rejected because it doesn't * exist anymore. * * Here is the detailed flow for one of the scenario here: * 1. Server A starts to have snap sync with leader * 2. After serializing the ACL map to Server A, there is a txn T1 to * create a node N1 with new ACL_1 which was not exist in ACL map * 3. On leader, after this txn, the ACL map will be ID1 -> (ACL_1, COUNT: 1), * and data tree N1 -> ID1 * 4. On server A, it will be empty ACL map, and N1 -> ID1 in fuzzy snapshot * 5. When replaying the txn T1, it will skip at the beginning since the * node is already exist, which leaves an empty ACL map, and N1 is * referencing to a non-exist ACL ID1 * 6. Node N1 will be not accessible because the ACL not exist, and if it * became leader later then all the write requests will be rejected as * well with marshalling error. */ @Test public void testACLCreatedDuringFuzzySnapshotSync() throws IOException { DataTree leaderDataTree = new DataTree(); // Start the simulated snap-sync by serializing ACL cache. File file = File.createTempFile("snapshot", "zk"); FileOutputStream os = new FileOutputStream(file); OutputArchive oa = BinaryOutputArchive.getArchive(os); leaderDataTree.serializeAcls(oa); // Add couple of transaction in-between. TxnHeader hdr1 = new TxnHeader(1, 2, 2, 2, ZooDefs.OpCode.create); Record txn1 = new CreateTxn("/a1", "foo".getBytes(), ZooDefs.Ids.CREATOR_ALL_ACL, false, -1); leaderDataTree.processTxn(hdr1, txn1); // Finish the snapshot. leaderDataTree.serializeNodes(oa); os.close(); // Simulate restore on follower and replay. FileInputStream is = new FileInputStream(file); InputArchive ia = BinaryInputArchive.getArchive(is); DataTree followerDataTree = new DataTree(); followerDataTree.deserialize(ia, "tree"); followerDataTree.processTxn(hdr1, txn1); DataNode a1 = leaderDataTree.getNode("/a1"); assertNotNull(a1); assertEquals(ZooDefs.Ids.CREATOR_ALL_ACL, leaderDataTree.getACL(a1)); assertEquals(ZooDefs.Ids.CREATOR_ALL_ACL, followerDataTree.getACL(a1)); } @Test public void testEmptySnapshotSerialization() throws IOException { File dataDir = ClientBase.createEmptyTestDir(); FileTxnSnapLog snaplog = new FileTxnSnapLog(dataDir, dataDir); DataTree dataTree = new DataTree(); ConcurrentHashMap sessions = new ConcurrentHashMap<>(); ZooKeeperServer.setDigestEnabled(true); snaplog.save(dataTree, sessions, true); snaplog.restore(dataTree, sessions, (hdr, rec, digest) -> { }); assertNull(dataTree.getDigestFromLoadedSnapshot()); } @Test public void testSnapshotSerializationCompatibility() throws IOException { testSnapshotSerializationCompatibility(true, false); testSnapshotSerializationCompatibility(false, false); testSnapshotSerializationCompatibility(true, true); testSnapshotSerializationCompatibility(false, true); } void testSnapshotSerializationCompatibility(Boolean digestEnabled, Boolean snappyEnabled) throws IOException { File dataDir = ClientBase.createEmptyTestDir(); FileTxnSnapLog snaplog = new FileTxnSnapLog(dataDir, dataDir); DataTree dataTree = new DataTree(); ConcurrentHashMap sessions = new ConcurrentHashMap<>(); SnapStream.setStreamMode(snappyEnabled ? SnapStream.StreamMode.SNAPPY : SnapStream.StreamMode.DEFAULT_MODE); ZooKeeperServer.setDigestEnabled(digestEnabled); // set the flag to be the same as digestEnabled to make sure the last serialized data // (for example, datatree, digest, lastProcessedZxid) is setup as expected for backward // compatibility test. ZooKeeperServer.setSerializeLastProcessedZxidEnabled(digestEnabled); TxnHeader txnHeader = new TxnHeader(1, 1, 1, 1 + 1, ZooDefs.OpCode.create); CreateTxn txn = new CreateTxn("/" + 1, "data".getBytes(), null, false, 1); Request request = new Request(1, 1, 1, txnHeader, txn, 1); dataTree.processTxn(request.getHdr(), request.getTxn()); snaplog.save(dataTree, sessions, true); int expectedNodeCount = dataTree.getNodeCount(); ZooKeeperServer.setDigestEnabled(!digestEnabled); // set the flag to be the same as digestEnabled to make sure the last serialized data // (for example, datatree, digest, lastProcessedZxid) is setup as expected for backward // compatibility test. ZooKeeperServer.setSerializeLastProcessedZxidEnabled(!digestEnabled); snaplog.restore(dataTree, sessions, (hdr, rec, digest) -> { }); assertEquals(expectedNodeCount, dataTree.getNodeCount()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000173 15051152474 032732 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/SnapStreamTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/SnapSt0100644 0000000 0000000 00000013013 15051152474 034310 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.apache.zookeeper.test.ClientBase.createTmpDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.zip.CheckedInputStream; import java.util.zip.CheckedOutputStream; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.server.persistence.SnapStream.StreamMode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class SnapStreamTest { @AfterEach public void tearDown() { System.clearProperty(SnapStream.ZOOKEEPER_SHAPSHOT_STREAM_MODE); SnapStream.setStreamMode(StreamMode.DEFAULT_MODE); } @Test public void testStreamMode() { assertEquals(StreamMode.CHECKED.getName(), ""); assertEquals(StreamMode.CHECKED.getFileExtension(), ""); assertEquals(StreamMode.CHECKED, StreamMode.fromString("name")); assertEquals(StreamMode.GZIP.getName(), "gz"); assertEquals(StreamMode.GZIP.getFileExtension(), ".gz"); assertEquals(StreamMode.GZIP, StreamMode.fromString("gz")); assertEquals(StreamMode.SNAPPY.getName(), "snappy"); assertEquals(StreamMode.SNAPPY.getFileExtension(), ".snappy"); assertEquals(StreamMode.SNAPPY, StreamMode.fromString("snappy")); } @Test public void testGetStreamMode() { assertEquals(StreamMode.CHECKED, SnapStream.getStreamMode("snapshot.180000e3a2"), "expected to return un-compressed stream"); assertEquals(StreamMode.SNAPPY, SnapStream.getStreamMode("snapshot.180000e3a2.snappy"), "expected to return snappy stream"); assertEquals(StreamMode.GZIP, SnapStream.getStreamMode("snapshot.180000e3a2.gz"), "expected to return gzip stream"); } @Test public void testSerializeDeserializeWithChecked() throws IOException { testSerializeDeserialize(StreamMode.CHECKED, ""); } @Test public void testSerializeDeserializeWithSNAPPY() throws IOException { testSerializeDeserialize(StreamMode.SNAPPY, ".snappy"); } @Test public void testSerializeDeserializeWithGZIP() throws IOException { testSerializeDeserialize(StreamMode.GZIP, ".gz"); } private void testSerializeDeserialize(StreamMode mode, String fileSuffix) throws IOException { testSerializeDeserialize(mode, fileSuffix, false); testSerializeDeserialize(mode, fileSuffix, true); } private void testSerializeDeserialize(StreamMode mode, String fileSuffix, boolean fsync) throws IOException { SnapStream.setStreamMode(mode); // serialize with gzip stream File tmpDir = createTmpDir(); File file = new File(tmpDir, "snapshot.180000e3a2" + fileSuffix); CheckedOutputStream os = SnapStream.getOutputStream(file, fsync); OutputArchive oa = BinaryOutputArchive.getArchive(os); FileHeader header = new FileHeader(FileSnap.SNAP_MAGIC, 2, 1); header.serialize(oa, "fileheader"); SnapStream.sealStream(os, oa); os.flush(); os.close(); assertTrue(SnapStream.isValidSnapshot(file)); // deserialize with gzip stream CheckedInputStream is = SnapStream.getInputStream(file); InputArchive ia = BinaryInputArchive.getArchive(is); FileHeader restoredHeader = new FileHeader(); restoredHeader.deserialize(ia, "fileheader"); assertEquals(restoredHeader, header, "magic not the same"); SnapStream.checkSealIntegrity(is, ia); } private void checkInvalidSnapshot(String filename, boolean fsync) throws IOException { // set the output stream mode to CHECKED SnapStream.setStreamMode(StreamMode.CHECKED); // serialize to CHECKED file without magic header File tmpDir = createTmpDir(); File file = new File(tmpDir, filename); OutputStream os = SnapStream.getOutputStream(file, fsync); os.write(1); os.flush(); os.close(); assertFalse(SnapStream.isValidSnapshot(file)); } private void checkInvalidSnapshot(String filename) throws IOException { checkInvalidSnapshot(filename, false); checkInvalidSnapshot(filename, true); } @Test public void testInvalidSnapshot() throws IOException { assertFalse(SnapStream.isValidSnapshot(null)); checkInvalidSnapshot("snapshot.180000e3a2"); checkInvalidSnapshot("snapshot.180000e3a2.gz"); checkInvalidSnapshot("snapshot.180000e3a2.snappy"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_pe0100644 0000000 0000000 00000000176 15051152474 032735 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/TxnLogToolkitTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/persistence/TxnLog0100644 0000000 0000000 00000013515 15051152474 034322 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.persistence; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsNot.not; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class TxnLogToolkitTest { private static final File testData = new File(System.getProperty("test.data.dir", "src/test/resources/data")); private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); private final ByteArrayOutputStream errContent = new ByteArrayOutputStream(); private File mySnapDir; @BeforeEach public void setUp() throws IOException { System.setOut(new PrintStream(outContent)); System.setErr(new PrintStream(errContent)); File snapDir = new File(testData, "invalidsnap"); mySnapDir = ClientBase.createTmpDir(); FileUtils.copyDirectory(snapDir, mySnapDir); } @AfterEach public void tearDown() throws IOException { System.setOut(System.out); System.setErr(System.err); mySnapDir.setWritable(true); FileUtils.deleteDirectory(mySnapDir); } @Test public void testDumpMode() throws Exception { // Arrange File logfile = new File(new File(mySnapDir, "version-2"), "log.274"); TxnLogToolkit lt = new TxnLogToolkit(false, false, logfile.toString(), true); // Act lt.dump(null); // Assert // no exception thrown } @Test public void testInitMissingFile() throws FileNotFoundException, TxnLogToolkit.TxnLogToolkitException { assertThrows(TxnLogToolkit.TxnLogToolkitException.class, () -> { // Arrange & Act File logfile = new File("this_file_should_not_exists"); TxnLogToolkit lt = new TxnLogToolkit(false, false, logfile.toString(), true); }); } @Test public void testInitWithRecoveryFileExists() { assertThrows(TxnLogToolkit.TxnLogToolkitException.class, () -> { // Arrange & Act File logfile = new File(new File(mySnapDir, "version-2"), "log.274"); File recoveryFile = new File(new File(mySnapDir, "version-2"), "log.274.fixed"); recoveryFile.createNewFile(); TxnLogToolkit lt = new TxnLogToolkit(true, false, logfile.toString(), true); }); } @Test public void testDumpWithCrcError() throws Exception { // Arrange File logfile = new File(new File(mySnapDir, "version-2"), "log.42"); TxnLogToolkit lt = new TxnLogToolkit(false, false, logfile.toString(), true); // Act lt.dump(null); // Assert String output = outContent.toString(); Pattern p = Pattern.compile("^CRC ERROR.*session 0x8061fac5ddeb0000 cxid 0x0 zxid 0x8800000002 createSession 30000$", Pattern.MULTILINE); Matcher m = p.matcher(output); assertTrue(m.find(), "Output doesn't indicate CRC error for the broken session id: " + output); } @Test public void testRecoveryFixBrokenFile() throws Exception { // Arrange File logfile = new File(new File(mySnapDir, "version-2"), "log.42"); TxnLogToolkit lt = new TxnLogToolkit(true, false, logfile.toString(), true); // Act lt.dump(null); // Assert String output = outContent.toString(); assertThat(output, containsString("CRC FIXED")); // Should be able to dump the recovered logfile with no CRC error outContent.reset(); logfile = new File(new File(mySnapDir, "version-2"), "log.42.fixed"); lt = new TxnLogToolkit(false, false, logfile.toString(), true); lt.dump(null); output = outContent.toString(); assertThat(output, not(containsString("CRC ERROR"))); } @Test public void testRecoveryInteractiveMode() throws Exception { // Arrange File logfile = new File(new File(mySnapDir, "version-2"), "log.42"); TxnLogToolkit lt = new TxnLogToolkit(true, false, logfile.toString(), false); // Act lt.dump(new Scanner("y\n")); // Assert String output = outContent.toString(); assertThat(output, containsString("CRC ERROR")); // Should be able to dump the recovered logfile with no CRC error outContent.reset(); logfile = new File(new File(mySnapDir, "version-2"), "log.42.fixed"); lt = new TxnLogToolkit(false, false, logfile.toString(), true); lt.dump(null); output = outContent.toString(); assertThat(output, not(containsString("CRC ERROR"))); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000167 15051152474 032756 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/BufferStatsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/BufferStats0100644 0000000 0000000 00000004374 15051152474 034346 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; public class BufferStatsTest { @Test public void testSetProposalSizeSetMinMax() { BufferStats stats = new BufferStats(); assertEquals(-1, stats.getLastBufferSize()); assertEquals(-1, stats.getMinBufferSize()); assertEquals(-1, stats.getMaxBufferSize()); stats.setLastBufferSize(10); assertEquals(10, stats.getLastBufferSize()); assertEquals(10, stats.getMinBufferSize()); assertEquals(10, stats.getMaxBufferSize()); stats.setLastBufferSize(20); assertEquals(20, stats.getLastBufferSize()); assertEquals(10, stats.getMinBufferSize()); assertEquals(20, stats.getMaxBufferSize()); stats.setLastBufferSize(5); assertEquals(5, stats.getLastBufferSize()); assertEquals(5, stats.getMinBufferSize()); assertEquals(20, stats.getMaxBufferSize()); } @Test public void testReset() { BufferStats stats = new BufferStats(); stats.setLastBufferSize(10); assertEquals(10, stats.getLastBufferSize()); assertEquals(10, stats.getMinBufferSize()); assertEquals(10, stats.getMaxBufferSize()); stats.reset(); assertEquals(-1, stats.getLastBufferSize()); assertEquals(-1, stats.getMinBufferSize()); assertEquals(-1, stats.getMaxBufferSize()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000173 15051152474 032753 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CloseSessionTxnTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CloseSessio0100644 0000000 0000000 00000007611 15051152474 034346 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; public class CloseSessionTxnTest extends QuorumPeerTestBase { /** * Test leader/leader compatibility with/without CloseSessionTxn, so that * we can gradually rollout this code and rollback if there is problem. */ @Test public void testCloseSessionTxnCompatile() throws Exception { // Test 4 cases: // 1. leader disabled, follower disabled testCloseSessionWithDifferentConfig(false, false); // 2. leader disabled, follower enabled testCloseSessionWithDifferentConfig(false, true); // 3. leader enabled, follower disabled testCloseSessionWithDifferentConfig(true, false); // 4. leader enabled, follower enabled testCloseSessionWithDifferentConfig(true, true); } private void testCloseSessionWithDifferentConfig( boolean closeSessionEnabledOnLeader, boolean closeSessionEnabledOnFollower) throws Exception { // 1. set up an ensemble with 3 servers final int numServers = 3; servers = LaunchServers(numServers); int leaderId = servers.findLeader(); ZooKeeperServer.setCloseSessionTxnEnabled(closeSessionEnabledOnLeader); // 2. shutdown one of the follower, start it later to pick up the // CloseSessionTxnEnabled config change // // We cannot use different static config in the same JVM, so have to // use this tricky int followerA = (leaderId + 1) % numServers; servers.mt[followerA].shutdown(); waitForOne(servers.zk[followerA], States.CONNECTING); // 3. create an ephemeral node String path = "/testCloseSessionTxnCompatile"; servers.zk[leaderId].create(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); // 3. close the client servers.restartClient(leaderId, this); waitForOne(servers.zk[leaderId], States.CONNECTED); // 4. update the CloseSessionTxnEnabled config before follower A // started System.setProperty("zookeeper.retainZKDatabase", "true"); ZooKeeperServer.setCloseSessionTxnEnabled(closeSessionEnabledOnFollower); // 5. restart follower A servers.mt[followerA].start(); waitForOne(servers.zk[followerA], States.CONNECTED); // 4. verify the ephemeral node is gone for (int i = 0; i < numServers; i++) { final CountDownLatch syncedLatch = new CountDownLatch(1); servers.zk[i].sync(path, (rc, path1, ctx) -> syncedLatch.countDown(), null); assertTrue(syncedLatch.await(3, TimeUnit.SECONDS)); assertNull(servers.zk[i].exists(path, false)); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032755 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CnxManagerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CnxManagerT0100644 0000000 0000000 00000063701 15051152474 034264 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.QuorumX509Util; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.quorum.QuorumCnxManager.InitialMessage; import org.apache.zookeeper.server.quorum.QuorumCnxManager.Message; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.FLENewEpochTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CnxManagerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLENewEpochTest.class); protected static final int THRESHOLD = 4; int count; Map peers; File[] peerTmpdir; int[] peerQuorumPort; int[] peerClientPort; @BeforeEach public void setUp() throws Exception { this.count = 3; this.peers = new HashMap<>(count); peerTmpdir = new File[count]; peerQuorumPort = new int[count]; peerClientPort = new int[count]; for (int i = 0; i < count; i++) { peerQuorumPort[i] = PortAssignment.unique(); peerClientPort[i] = PortAssignment.unique(); peers.put((long) i, new QuorumServer(i, new InetSocketAddress("127.0.0.1", peerQuorumPort[i]), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", peerClientPort[i]))); peerTmpdir[i] = ClientBase.createTmpDir(); } } ByteBuffer createMsg(int state, long leader, long zxid, long epoch) { byte[] requestBytes = new byte[28]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); /* * Building notification packet to send */ requestBuffer.clear(); requestBuffer.putInt(state); requestBuffer.putLong(leader); requestBuffer.putLong(zxid); requestBuffer.putLong(epoch); return requestBuffer; } class CnxManagerThread extends Thread { boolean failed; CnxManagerThread() { failed = false; } public void run() { try { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[0], peerTmpdir[0], peerClientPort[0], 3, 0, 1000, 2, 2, 2); QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if (listener != null) { listener.start(); } else { LOG.error("Null listener when initializing cnx manager"); } long sid = 1; cnxManager.toSend(sid, createMsg(ServerState.LOOKING.ordinal(), 0, -1, 1)); Message m = null; int numRetries = 1; while ((m == null) && (numRetries++ <= THRESHOLD)) { m = cnxManager.pollRecvQueue(3000, TimeUnit.MILLISECONDS); if (m == null) { cnxManager.connectAll(); } } if (numRetries > THRESHOLD) { failed = true; return; } cnxManager.testInitiateConnection(sid); m = cnxManager.pollRecvQueue(3000, TimeUnit.MILLISECONDS); if (m == null) { failed = true; } } catch (Exception e) { LOG.error("Exception while running mock thread", e); fail("Unexpected exception"); } } } @Test public void testCnxManager() throws Exception { CnxManagerThread thread = new CnxManagerThread(); thread.start(); QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2, 2); QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if (listener != null) { listener.start(); } else { LOG.error("Null listener when initializing cnx manager"); } cnxManager.toSend(0L, createMsg(ServerState.LOOKING.ordinal(), 1, -1, 1)); Message m = null; int numRetries = 1; while ((m == null) && (numRetries++ <= THRESHOLD)) { m = cnxManager.pollRecvQueue(3000, TimeUnit.MILLISECONDS); if (m == null) { cnxManager.connectAll(); } } assertTrue(numRetries <= THRESHOLD, "Exceeded number of retries"); thread.join(5000); if (thread.isAlive()) { fail("Thread didn't join"); } else { if (thread.failed) { fail("Did not receive expected message"); } } cnxManager.halt(); assertFalse(cnxManager.listener.isAlive()); } @Test public void testCnxManagerTimeout() throws Exception { int address = ThreadLocalRandom.current().nextInt(1, 255); int deadPort = PortAssignment.unique(); String deadAddress = "10.1.1." + address; LOG.info("This is the dead address I'm trying: {}", deadAddress); peers.put(2L, new QuorumServer(2, new InetSocketAddress(deadAddress, deadPort), new InetSocketAddress(deadAddress, PortAssignment.unique()), new InetSocketAddress(deadAddress, PortAssignment.unique()))); peerTmpdir[2] = ClientBase.createTmpDir(); QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2, 2); QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if (listener != null) { listener.start(); } else { LOG.error("Null listener when initializing cnx manager"); } long begin = Time.currentElapsedTime(); cnxManager.toSend(2L, createMsg(ServerState.LOOKING.ordinal(), 1, -1, 1)); long end = Time.currentElapsedTime(); if ((end - begin) > 10_000) { fail("Waited more than necessary"); } cnxManager.halt(); assertFalse(cnxManager.listener.isAlive()); } /** * Tests a bug in QuorumCnxManager that causes a spin lock * when a negative value is sent. This test checks if the * connection is being closed upon a message with negative * length. * * @throws Exception */ @Test public void testCnxManagerSpinLock() throws Exception { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2, 2); QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if (listener != null) { listener.start(); } else { LOG.error("Null listener when initializing cnx manager"); } InetSocketAddress address = peers.get(peer.getMyId()).electionAddr.getReachableOrOne(); LOG.info("Election port: {}", address.getPort()); Thread.sleep(1000); SocketChannel sc = SocketChannel.open(); sc.socket().connect(address, 5000); InetSocketAddress otherAddr = peers.get(2L).electionAddr.getReachableOrOne(); DataOutputStream dout = new DataOutputStream(sc.socket().getOutputStream()); dout.writeLong(QuorumCnxManager.PROTOCOL_VERSION_V1); dout.writeLong(2); String addr = otherAddr.getHostString() + ":" + otherAddr.getPort(); byte[] addr_bytes = addr.getBytes(); dout.writeInt(addr_bytes.length); dout.write(addr_bytes); dout.flush(); ByteBuffer msgBuffer = ByteBuffer.wrap(new byte[4]); msgBuffer.putInt(-20); msgBuffer.position(0); sc.write(msgBuffer); Thread.sleep(1000); try { /* * Write a number of times until it * detects that the socket is broken. */ for (int i = 0; i < 100; i++) { msgBuffer.position(0); sc.write(msgBuffer); } fail("Socket has not been closed"); } catch (Exception e) { LOG.info("Socket has been closed as expected"); } peer.shutdown(); cnxManager.halt(); assertFalse(cnxManager.listener.isAlive()); } /** * Test for bug described in https://issues.apache.org/jira/browse/ZOOKEEPER-3320. * Test create peer with address which contains unresolvable DNS name, * leader election listener thread should stop after N errors. * * @throws Exception */ @Test public void testCnxManagerListenerThreadConfigurableRetry() throws Exception { final Map unresolvablePeers = new HashMap<>(); final long myid = 1L; unresolvablePeers.put(myid, new QuorumServer(myid, "unresolvable-domain.org:2182:2183;2181")); final QuorumPeer peer = new QuorumPeer(unresolvablePeers, ClientBase.createTmpDir(), ClientBase.createTmpDir(), 2181, 3, myid, 1000, 2, 2, 2); final QuorumCnxManager cnxManager = peer.createCnxnManager(); final QuorumCnxManager.Listener listener = cnxManager.listener; final AtomicBoolean errorHappend = new AtomicBoolean(false); listener.setSocketBindErrorHandler(() -> errorHappend.set(true)); listener.start(); // listener thread should stop and throws error which notify QuorumPeer about error. // QuorumPeer should start shutdown process listener.join(15000); // set wait time, if listener contains bug and thread not stops. assertFalse(listener.isAlive()); assertTrue(errorHappend.get()); assertFalse(listener.isAlive(), QuorumPeer.class.getSimpleName() + " not stopped after " + "listener thread death"); } /** * Tests a bug in QuorumCnxManager that causes a NPE when a 3.4.6 * observer connects to a 3.5.0 server. * see https://issues.apache.org/jira/browse/ZOOKEEPER-1789 * * @throws Exception */ @Test public void testCnxManagerNPE() throws Exception { // the connecting peer (id = 2) is a 3.4.6 observer peers.get(2L).type = LearnerType.OBSERVER; QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 1000, 2, 2, 2); QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if (listener != null) { listener.start(); } else { LOG.error("Null listener when initializing cnx manager"); } InetSocketAddress address = peers.get(peer.getMyId()).electionAddr.getReachableOrOne(); LOG.info("Election port: {}", address.getPort()); Thread.sleep(1000); SocketChannel sc = SocketChannel.open(); sc.socket().connect(address, 5000); /* * Write id (3.4.6 protocol). This previously caused a NPE in * QuorumCnxManager. */ byte[] msgBytes = new byte[8]; ByteBuffer msgBuffer = ByteBuffer.wrap(msgBytes); msgBuffer.putLong(2L); msgBuffer.position(0); sc.write(msgBuffer); msgBuffer = ByteBuffer.wrap(new byte[8]); // write length of message msgBuffer.putInt(4); // write message msgBuffer.putInt(5); msgBuffer.position(0); sc.write(msgBuffer); Message m = cnxManager.pollRecvQueue(1000, TimeUnit.MILLISECONDS); assertNotNull(m); peer.shutdown(); cnxManager.halt(); assertFalse(cnxManager.listener.isAlive()); } /* * Test if a receiveConnection is able to timeout on socket errors */ @Test public void testSocketTimeout() throws Exception { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[1], peerTmpdir[1], peerClientPort[1], 3, 1, 2000, 2, 2, 2); QuorumCnxManager cnxManager = peer.createCnxnManager(); QuorumCnxManager.Listener listener = cnxManager.listener; if (listener != null) { listener.start(); } else { LOG.error("Null listener when initializing cnx manager"); } InetSocketAddress address = peers.get(peer.getMyId()).electionAddr.getReachableOrOne(); LOG.info("Election port: {}", address.getPort()); Thread.sleep(1000); Socket sock = new Socket(); sock.connect(address, 5000); long begin = Time.currentElapsedTime(); // Read without sending data. Verify timeout. cnxManager.receiveConnection(sock); long end = Time.currentElapsedTime(); if ((end - begin) > ((peer.getSyncLimit() * peer.getTickTime()) + 500)) { fail("Waited more than necessary"); } cnxManager.halt(); assertFalse(cnxManager.listener.isAlive()); } /** * Test the SSLSocket is explicitly closed when there is IOException * happened during connect. */ @Test public void testSSLSocketClosedWhenHandshakeTimeout() throws Exception { final CountDownLatch closeLatch = new CountDownLatch(1); QuorumX509Util mockedX509Util = new QuorumX509Util() { @Override public SSLSocket createSSLSocket() { return new SSLSocket() { @Override public void connect(SocketAddress endpoint, int timeout) { } @Override public void startHandshake() throws IOException { throw new IOException(); } @Override public void close() { closeLatch.countDown(); } public String[] getSupportedCipherSuites() { throw new UnsupportedOperationException(); } public String[] getEnabledCipherSuites() { throw new UnsupportedOperationException(); } public String[] getSupportedProtocols() { throw new UnsupportedOperationException(); } public String[] getEnabledProtocols() { throw new UnsupportedOperationException(); } public SSLSession getSession() { throw new UnsupportedOperationException(); } public void setEnabledCipherSuites(String[] suites) { } public void setEnabledProtocols(String[] protocols) { } public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { } public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { } public void setUseClientMode(boolean mode) { } public boolean getUseClientMode() { return true; } public void setNeedClientAuth(boolean need) { } public boolean getNeedClientAuth() { return true; } public void setWantClientAuth(boolean want) { } public boolean getWantClientAuth() { return true; } public void setEnableSessionCreation(boolean flag) { } public boolean getEnableSessionCreation() { return true; } }; } }; QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[0], peerTmpdir[0], peerClientPort[0], 3, 0, 2000, 2, 2, 2) { @Override public QuorumX509Util createX509Util() { return mockedX509Util; } }; peer.setSslQuorum(true); QuorumCnxManager cnxManager = peer.createCnxnManager(); cnxManager.connectOne(1, peers.get(1L).electionAddr); assertTrue(closeLatch.await(1, TimeUnit.SECONDS)); } /* * Test if Worker threads are getting killed after connection loss */ @Test public void testWorkerThreads() throws Exception { ArrayList peerList = new ArrayList<>(); try { for (int sid = 0; sid < 3; sid++) { QuorumPeer peer = new QuorumPeer(peers, peerTmpdir[sid], peerTmpdir[sid], peerClientPort[sid], 3, sid, 1000, 2, 2, 2); LOG.info("Starting peer {}", peer.getMyId()); peer.start(); peerList.add(sid, peer); } String failure = verifyThreadCount(peerList, 4); assertNull(failure, failure); for (int myid = 0; myid < 3; myid++) { for (int i = 0; i < 5; i++) { // halt one of the listeners and verify count QuorumPeer peer = peerList.get(myid); LOG.info("Round {}, halting peer {}", i, peer.getMyId()); peer.shutdown(); peerList.remove(myid); failure = verifyThreadCount(peerList, 2); assertNull(failure, failure); // Restart halted node and verify count peer = new QuorumPeer(peers, peerTmpdir[myid], peerTmpdir[myid], peerClientPort[myid], 3, myid, 1000, 2, 2, 2); LOG.info("Round {}, restarting peer {}", i, peer.getMyId()); peer.start(); peerList.add(myid, peer); failure = verifyThreadCount(peerList, 4); assertNull(failure, failure); } } } finally { for (QuorumPeer quorumPeer : peerList) { quorumPeer.shutdown(); } } } /** * Returns null on success, otw the message assoc with the failure * @throws InterruptedException */ public String verifyThreadCount(ArrayList peerList, long ecnt) throws InterruptedException { String failure = null; for (int i = 0; i < 480; i++) { Thread.sleep(500); failure = _verifyThreadCount(peerList, ecnt); if (failure == null) { return null; } } return failure; } public String _verifyThreadCount(ArrayList peerList, long ecnt) { for (int myid = 0; myid < peerList.size(); myid++) { QuorumPeer peer = peerList.get(myid); QuorumCnxManager cnxManager = peer.getQuorumCnxManager(); long cnt = cnxManager.getThreadCount(); if (cnt != ecnt) { return new Date() + " Incorrect number of Worker threads for sid=" + myid + " expected " + ecnt + " found " + cnt; } } return null; } @Test public void testInitialMessage() throws Exception { InitialMessage msg; ByteArrayOutputStream bos; DataInputStream din; DataOutputStream dout; String hostport; // message with bad protocol version try { // the initial message (without the protocol version) hostport = "10.0.0.2:3888"; bos = new ByteArrayOutputStream(); dout = new DataOutputStream(bos); dout.writeLong(5L); // sid dout.writeInt(hostport.getBytes().length); dout.writeBytes(hostport); // now parse it din = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); msg = InitialMessage.parse(-65530L, din); fail("bad protocol version accepted"); } catch (InitialMessage.InitialMessageException ex) { } // message too long try { hostport = createLongString(1048576); bos = new ByteArrayOutputStream(); dout = new DataOutputStream(bos); dout.writeLong(5L); // sid dout.writeInt(hostport.getBytes().length); dout.writeBytes(hostport); din = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); msg = InitialMessage.parse(QuorumCnxManager.PROTOCOL_VERSION_V1, din); fail("long message accepted"); } catch (InitialMessage.InitialMessageException ex) { } // bad hostport string try { hostport = "what's going on here?"; bos = new ByteArrayOutputStream(); dout = new DataOutputStream(bos); dout.writeLong(5L); // sid dout.writeInt(hostport.getBytes().length); dout.writeBytes(hostport); din = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); msg = InitialMessage.parse(QuorumCnxManager.PROTOCOL_VERSION_V1, din); fail("bad hostport accepted"); } catch (InitialMessage.InitialMessageException ex) { } // good message, single election address try { hostport = "10.0.0.2:3888"; bos = new ByteArrayOutputStream(); dout = new DataOutputStream(bos); dout.writeLong(5L); // sid dout.writeInt(hostport.getBytes().length); dout.writeBytes(hostport); // now parse it din = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); msg = InitialMessage.parse(QuorumCnxManager.PROTOCOL_VERSION_V1, din); assertEquals(Long.valueOf(5), msg.sid); assertEquals(Arrays.asList(new InetSocketAddress("10.0.0.2", 3888)), msg.electionAddr); } catch (InitialMessage.InitialMessageException ex) { fail(ex.toString()); } // good message, multiple election addresses (ZOOKEEPER-3188) try { hostport = "1.1.1.1:9999|2.2.2.2:8888|3.3.3.3:7777"; bos = new ByteArrayOutputStream(); dout = new DataOutputStream(bos); dout.writeLong(5L); // sid dout.writeInt(hostport.getBytes().length); dout.writeBytes(hostport); // now parse it din = new DataInputStream(new ByteArrayInputStream(bos.toByteArray())); msg = InitialMessage.parse(QuorumCnxManager.PROTOCOL_VERSION_V2, din); assertEquals(Long.valueOf(5), msg.sid); assertEquals(Arrays.asList(new InetSocketAddress("1.1.1.1", 9999), new InetSocketAddress("2.2.2.2", 8888), new InetSocketAddress("3.3.3.3", 7777)), msg.electionAddr); } catch (InitialMessage.InitialMessageException ex) { fail(ex.toString()); } } @Test public void testWildcardAddressRecognition() { assertTrue(QuorumCnxManager.InitialMessage.isWildcardAddress("0.0.0.0")); assertTrue(QuorumCnxManager.InitialMessage.isWildcardAddress("::")); assertFalse(QuorumCnxManager.InitialMessage.isWildcardAddress("some.unresolvable.host.com")); assertFalse(QuorumCnxManager.InitialMessage.isWildcardAddress("127.0.0.1")); assertFalse(QuorumCnxManager.InitialMessage.isWildcardAddress("255.255.255.255")); assertFalse(QuorumCnxManager.InitialMessage.isWildcardAddress("1.2.3.4")); assertFalse(QuorumCnxManager.InitialMessage.isWildcardAddress("www.google.com")); } private String createLongString(int size) { StringBuilder sb = new StringBuilder(size); for (int i = 0; i < size; i++) { sb.append('x'); } return sb.toString(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000206 15051152474 032750 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CommitProcessorConcurrencyTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CommitProce0100644 0000000 0000000 00000070605 15051152474 034337 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.proto.SetDataRequest; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.WorkerService; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CommitProcessorConcurrencyTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(CommitProcessorConcurrencyTest.class); BlockingQueue processedRequests; MockCommitProcessor processor; int defaultSizeOfThreadPool = 16; @BeforeEach public void setUp() throws Exception { processedRequests = new LinkedBlockingQueue<>(); processor = new MockCommitProcessor(); CommitProcessor.setMaxReadBatchSize(-1); CommitProcessor.setMaxCommitBatchSize(1); } @AfterEach public void tearDown() throws Exception { processor.shutdown(); } // This queue is infinite if we use "poll" to get requests, but returns a // finite size when asked. class MockRequestsQueue extends LinkedBlockingQueue { private static final long serialVersionUID = 1L; int readReqId = 0; // Always have a request to return. public Request poll() { readReqId++; try { return newRequest(new GetDataRequest("/", false), OpCode.getData, readReqId % 50, readReqId); } catch (IOException e) { e.printStackTrace(); } return null; } // Fixed queue size. public int size() { return 42; } } class MockCommitProcessor extends CommitProcessor { MockCommitProcessor() { super(new RequestProcessor() { public void processRequest(Request request) throws RequestProcessorException { processedRequests.offer(request); } public void shutdown() { } }, "0", false, new ZooKeeperServerListener() { @Override public void notifyStopping(String threadName, int errorCode) { fail("Commit processor crashed " + errorCode); } }); } public void initThreads(int poolSize) { this.stopped = false; this.workerPool = new WorkerService("CommitProcWork", poolSize, true); } } private Request newRequest(Record rec, int type, int sessionId, int xid) throws IOException { ByteArrayOutputStream boas = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas); rec.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray()); return new Request(null, sessionId, xid, type, RequestRecord.fromBytes(bb), new ArrayList()); } /** * We place a read request followed by committed update request of the same * session in queuedRequests. We verify that both requests are processed, * according to the order of the session (first read, then the write). */ @Test public void committedAndUncommittedOfTheSameSessionRaceTest() throws Exception { final String path = "/testCvsUCRace"; Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x0, 0); Request writeReq = newRequest(new SetDataRequest(path, new byte[16], -1), OpCode.setData, 0x0, 1); processor.committedRequests.add(writeReq); processor.queuedRequests.add(readReq); processor.queuedRequests.add(writeReq); processor.queuedWriteRequests.add(writeReq); processor.initThreads(1); processor.stoppedMainLoop = true; processor.run(); assertTrue( processedRequests.peek() != null && processedRequests.peek().equals(readReq), "Request was not processed " + readReq + " instead " + processedRequests.peek()); processedRequests.poll(); assertTrue( processedRequests.peek() != null && processedRequests.peek().equals(writeReq), "Request was not processed " + writeReq + " instead " + processedRequests.peek()); } /** * Here we create the following requests queue structure: R1_1, W1_2, R1_3, * R2_1, R2_2, W2_3, R2_4, R3_1, R3_2, R3_3, W3_4, R3_5, ... , W5_6, R5_7 * i.e., 5 sessions, each has different amount or read requests, followed by * single write and afterwards single read. The idea is to check that all of * the reads that can be processed concurrently do so, and that none of the * uncommited requests, followed by the reads are processed. */ @Test public void processAsMuchUncommittedRequestsAsPossibleTest() throws Exception { final String path = "/testAsMuchAsPossible"; List shouldBeProcessed = new LinkedList<>(); Set shouldNotBeProcessed = new HashSet<>(); for (int sessionId = 1; sessionId <= 5; ++sessionId) { for (int readReqId = 1; readReqId <= sessionId; ++readReqId) { Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, sessionId, readReqId); shouldBeProcessed.add(readReq); processor.queuedRequests.add(readReq); } Request writeReq = newRequest( new CreateRequest( path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, sessionId, sessionId + 1); Request readReq = newRequest( new GetDataRequest(path, false), OpCode.getData, sessionId, sessionId + 2); processor.queuedRequests.add(writeReq); processor.queuedWriteRequests.add(writeReq); processor.queuedRequests.add(readReq); shouldNotBeProcessed.add(writeReq); shouldNotBeProcessed.add(readReq); } processor.initThreads(defaultSizeOfThreadPool); processor.stoppedMainLoop = true; processor.run(); Thread.sleep(1000); shouldBeProcessed.removeAll(processedRequests); for (Request r : shouldBeProcessed) { LOG.error("Did not process {}", r); } assertTrue(shouldBeProcessed.isEmpty(), "Not all requests were processed"); assertFalse(shouldNotBeProcessed.removeAll(processedRequests), "Processed a wrong request"); } /** * In the following test, we add a write request followed by several read * requests of the same session, and we verify several things - 1. The write * is not processed until commit arrives. 2. Once the write is processed, * all the read requests are processed as well. 3. All read requests are * executed after the write, before any other write, along with new reads. */ @Test public void processAllFollowingUncommittedAfterFirstCommitTest() throws Exception { final String path = "/testUncommittedFollowingCommited"; Set shouldBeInPending = new HashSet<>(); Set shouldBeProcessedAfterPending = new HashSet<>(); Request writeReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x1, 1); processor.queuedRequests.add(writeReq); processor.queuedWriteRequests.add(writeReq); shouldBeInPending.add(writeReq); for (int readReqId = 2; readReqId <= 5; ++readReqId) { Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x1, readReqId); processor.queuedRequests.add(readReq); shouldBeInPending.add(readReq); shouldBeProcessedAfterPending.add(readReq); } processor.initThreads(defaultSizeOfThreadPool); processor.stoppedMainLoop = true; processor.run(); assertTrue(processedRequests.isEmpty(), "Processed without waiting for commit"); assertTrue(processor.queuedRequests.isEmpty(), "Did not handled all of queuedRequests' requests"); assertTrue(!processor.queuedWriteRequests.isEmpty(), "Removed from blockedQueuedRequests before commit"); shouldBeInPending.removeAll(processor.pendingRequests.get(writeReq.sessionId)); for (Request r : shouldBeInPending) { LOG.error("Should be in pending {}", r); } assertTrue(shouldBeInPending.isEmpty(), "Not all requests moved to pending from queuedRequests"); processor.committedRequests.add(writeReq); processor.stoppedMainLoop = true; processor.run(); processor.initThreads(defaultSizeOfThreadPool); Thread.sleep(500); assertTrue(processedRequests.peek() == writeReq, "Did not process committed request"); assertTrue(processedRequests.containsAll(shouldBeProcessedAfterPending), "Did not process following read request"); assertTrue(processor.committedRequests.isEmpty(), "Did not process committed request"); assertTrue(processor.pendingRequests.isEmpty(), "Did not process committed request"); assertTrue(processor.queuedWriteRequests.isEmpty(), "Did not remove from blockedQueuedRequests"); } /** * In the following test, we add a write request followed by several read * requests of the same session. We will do this for 2 sessions. For the * second session, we will queue up another write after the reads, and * we verify several things - 1. The writes are not processed until * the commits arrive. 2. Only 2 writes are processed, with maxCommitBatchSize * of 3, due to the blocking reads. 3. Once the writes are processed, * all the read requests are processed as well. 4. All read requests are * executed after the write, before any other write for that session, * along with new reads. 5. Then we add another read for session 1, and * another write and commit for session 2. 6. Only the old write, and the read * are processed, leaving the commit in the queue. 7. Last write is executed * in the last iteration, and all lists are empty. */ @Test public void processAllWritesMaxBatchSize() throws Exception { final String path = "/processAllWritesMaxBatchSize"; HashSet shouldBeProcessedAfterPending = new HashSet<>(); Request writeReq = newRequest( new CreateRequest( path + "_1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x1, 1); processor.queuedRequests.add(writeReq); processor.queuedWriteRequests.add(writeReq); Request writeReq2 = newRequest( new CreateRequest( path + "_2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x2, 1); processor.queuedRequests.add(writeReq2); processor.queuedWriteRequests.add(writeReq2); for (int readReqId = 2; readReqId <= 5; ++readReqId) { Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x1, readReqId); Request readReq2 = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x2, readReqId); processor.queuedRequests.add(readReq); shouldBeProcessedAfterPending.add(readReq); processor.queuedRequests.add(readReq2); shouldBeProcessedAfterPending.add(readReq2); } Request writeReq3 = newRequest( new CreateRequest( path + "_3", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x2, 6); processor.queuedRequests.add(writeReq3); processor.queuedWriteRequests.add(writeReq3); processor.initThreads(defaultSizeOfThreadPool); processor.stoppedMainLoop = true; CommitProcessor.setMaxCommitBatchSize(2); processor.run(); assertTrue(processedRequests.isEmpty(), "Processed without waiting for commit"); assertTrue(processor.queuedRequests.isEmpty(), "Did not handled all of queuedRequests' requests"); assertTrue(!processor.queuedWriteRequests.isEmpty(), "Removed from blockedQueuedRequests before commit"); assertTrue(processor.pendingRequests.containsKey(writeReq.sessionId), "Missing session 1 in pending queue"); assertTrue(processor.pendingRequests.containsKey(writeReq2.sessionId), "Missing session 2 in pending queue"); processor.committedRequests.add(writeReq); processor.committedRequests.add(writeReq2); processor.committedRequests.add(writeReq3); processor.stoppedMainLoop = true; CommitProcessor.setMaxCommitBatchSize(3); processor.run(); processor.initThreads(defaultSizeOfThreadPool); Thread.sleep(500); assertTrue(processedRequests.peek() == writeReq, "Did not process committed request"); assertTrue(processedRequests.containsAll(shouldBeProcessedAfterPending), "Did not process following read request"); assertTrue(!processor.committedRequests.isEmpty(), "Processed committed request"); assertTrue(processor.committedRequests.peek() == writeReq3, "Removed commit for write req 3"); assertTrue(!processor.pendingRequests.isEmpty(), "Processed committed request"); assertTrue(processor.pendingRequests.containsKey(writeReq3.sessionId), "Missing session 2 in pending queue"); assertTrue(processor.pendingRequests.get(writeReq3.sessionId).peek() == writeReq3, "Missing write 3 in pending queue"); assertTrue(!processor.queuedWriteRequests.isEmpty(), "Removed from blockedQueuedRequests"); assertTrue(processor.queuedWriteRequests.peek() == writeReq3, "Removed write req 3 from blockedQueuedRequests"); Request readReq3 = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x1, 7); processor.queuedRequests.add(readReq3); shouldBeProcessedAfterPending.add(readReq3); Request writeReq4 = newRequest( new CreateRequest( path + "_4", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x2, 7); processor.queuedRequests.add(writeReq4); processor.queuedWriteRequests.add(writeReq4); processor.committedRequests.add(writeReq4); processor.stoppedMainLoop = true; CommitProcessor.setMaxCommitBatchSize(3); processor.run(); processor.initThreads(defaultSizeOfThreadPool); Thread.sleep(500); assertTrue(processedRequests.peek() == writeReq, "Did not process committed request"); assertTrue(processedRequests.containsAll(shouldBeProcessedAfterPending), "Did not process following read request"); assertTrue(!processor.committedRequests.isEmpty(), "Processed unexpected committed request"); assertTrue(processor.pendingRequests.isEmpty(), "Unexpected pending request"); assertTrue(!processor.queuedWriteRequests.isEmpty(), "Removed from blockedQueuedRequests"); assertTrue(processor.queuedWriteRequests.peek() == writeReq4, "Removed write req 4 from blockedQueuedRequests"); processor.stoppedMainLoop = true; CommitProcessor.setMaxCommitBatchSize(3); processor.run(); processor.initThreads(defaultSizeOfThreadPool); Thread.sleep(500); assertTrue(processedRequests.peek() == writeReq, "Did not process committed request"); assertTrue(processedRequests.containsAll(shouldBeProcessedAfterPending), "Did not process following read request"); assertTrue(processor.committedRequests.isEmpty(), "Did not process committed request"); assertTrue(processor.pendingRequests.isEmpty(), "Did not process committed request"); assertTrue(processor.queuedWriteRequests.isEmpty(), "Did not remove from blockedQueuedRequests"); } /** * In the following test, we verify that committed requests are processed * even when queuedRequests never gets empty. We add 10 committed request * and use infinite queuedRequests. We verify that the committed request was * processed. */ @Test @Timeout(value = 1) public void noStarvationOfNonLocalCommittedRequestsTest() throws Exception { final String path = "/noStarvationOfCommittedRequests"; processor.queuedRequests = new MockRequestsQueue(); Set nonLocalCommits = new HashSet<>(); for (int i = 0; i < 10; i++) { Request nonLocalCommitReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 51, i + 1); processor.committedRequests.add(nonLocalCommitReq); nonLocalCommits.add(nonLocalCommitReq); } for (int i = 0; i < 10; i++) { processor.initThreads(defaultSizeOfThreadPool); processor.stoppedMainLoop = true; processor.run(); } assertTrue(processedRequests.containsAll(nonLocalCommits), "commit request was not processed"); } /** * In the following test, we verify that committed writes are not causing * reads starvation. We populate the commit processor with the following * order of requests: 1 committed local updated, 1 read request, 100 * committed non-local updates. 50 read requests. We verify that after the * first call to processor.run, only the first write is processed, then * after the second call, all reads are processed along with the second * write. */ @Test public void noStarvationOfReadRequestsTest() throws Exception { final String path = "/noStarvationOfReadRequests"; // +1 committed requests (also head of queuedRequests) Request firstCommittedReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x3, 1); processor.queuedRequests.add(firstCommittedReq); processor.queuedWriteRequests.add(firstCommittedReq); processor.committedRequests.add(firstCommittedReq); Set allReads = new HashSet<>(); // +1 read request to queuedRequests Request firstRead = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x1, 0); allReads.add(firstRead); processor.queuedRequests.add(firstRead); // +1 non local commit Request secondCommittedReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x99, 2); processor.committedRequests.add(secondCommittedReq); Set waitingCommittedRequests = new HashSet<>(); // +99 non local committed requests for (int writeReqId = 3; writeReqId < 102; ++writeReqId) { Request writeReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x8, writeReqId); processor.committedRequests.add(writeReq); waitingCommittedRequests.add(writeReq); } // +50 read requests to queuedRequests for (int readReqId = 1; readReqId <= 50; ++readReqId) { Request readReq = newRequest( new GetDataRequest(path, false), OpCode.getData, 0x5, readReqId); allReads.add(readReq); processor.queuedRequests.add(readReq); } processor.initThreads(defaultSizeOfThreadPool); processor.stoppedMainLoop = true; processor.run(); assertTrue(processedRequests.contains(firstCommittedReq), "Did not process the first write request"); for (Request r : allReads) { assertTrue(!processedRequests.contains(r), "Processed read request"); } processor.run(); assertTrue(processedRequests.containsAll(allReads), "did not processed all reads"); assertTrue(processedRequests.contains(secondCommittedReq), "Did not process the second write request"); for (Request r : waitingCommittedRequests) { assertTrue(!processedRequests.contains(r), "Processed additional committed request"); } } /** * In the following test, we verify that we can handle the case that we got a commit * of a request we never seen since the session that we just established. This can happen * when a session is just established and there is request waiting to be committed in the * session queue but it sees a commit for a request that belongs to the previous connection. */ @Test @Timeout(value = 5) public void noCrashOnCommittedRequestsOfUnseenRequestTest() throws Exception { final String path = "/noCrash/OnCommittedRequests/OfUnseenRequestTest"; final int numberofReads = 10; final int sessionid = 0x123456; final int firstCXid = 0x100; int readReqId = firstCXid; processor.stoppedMainLoop = true; HashSet localRequests = new HashSet<>(); // queue the blocking write request to queuedRequests Request firstCommittedReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, sessionid, readReqId++); processor.queuedRequests.add(firstCommittedReq); processor.queuedWriteRequests.add(firstCommittedReq); localRequests.add(firstCommittedReq); // queue read requests to queuedRequests for (; readReqId <= numberofReads + firstCXid; ++readReqId) { Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, sessionid, readReqId); processor.queuedRequests.add(readReq); localRequests.add(readReq); } //run once assertTrue(processor.queuedRequests.containsAll(localRequests)); processor.initThreads(defaultSizeOfThreadPool); processor.run(); Thread.sleep(1000); //We verify that the processor is waiting for the commit assertTrue(processedRequests.isEmpty()); // We add a commit that belongs to the same session but with smaller cxid, // i.e., commit of an update from previous connection of this session. Request preSessionCommittedReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, sessionid, firstCXid - 2); processor.committedRequests.add(preSessionCommittedReq); processor.committedRequests.add(firstCommittedReq); processor.run(); Thread.sleep(1000); //We verify that the commit processor processed the old commit prior to the newer messages assertTrue(processedRequests.peek() == preSessionCommittedReq); processor.run(); Thread.sleep(1000); //We verify that the commit processor handle all messages. assertTrue(processedRequests.containsAll(localRequests)); } /** * In the following test, we verify if we handle the case in which we get a commit * for a request that has higher Cxid than the one we are waiting. This can happen * when a session connection is lost but there is a request waiting to be committed in the * session queue. However, since the session has moved, new requests can get to * the leader out of order. Hence, the commits can also arrive "out of order" w.r.t. cxid. * We should commit the requests according to the order we receive from the leader, i.e., wait for the relevant commit. */ @Test @Timeout(value = 5) public void noCrashOnOutofOrderCommittedRequestTest() throws Exception { final String path = "/noCrash/OnCommittedRequests/OfUnSeenRequestTest"; final int sessionid = 0x123456; final int lastCXid = 0x100; final int numberofReads = 10; int readReqId = lastCXid; processor.stoppedMainLoop = true; HashSet localRequests = new HashSet<>(); // queue the blocking write request to queuedRequests Request orphanCommittedReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, sessionid, lastCXid); processor.queuedRequests.add(orphanCommittedReq); processor.queuedWriteRequests.add(orphanCommittedReq); localRequests.add(orphanCommittedReq); // queue read requests to queuedRequests for (; readReqId <= numberofReads + lastCXid; ++readReqId) { Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, sessionid, readReqId); processor.queuedRequests.add(readReq); localRequests.add(readReq); } //run once processor.initThreads(defaultSizeOfThreadPool); processor.run(); Thread.sleep(1000); //We verify that the processor is waiting for the commit assertTrue(processedRequests.isEmpty()); // We add a commit that belongs to the same session but with larger cxid, // i.e., commit of an update from the next connection of this session. Request otherSessionCommittedReq = newRequest( new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, sessionid, lastCXid + 10); processor.committedRequests.add(otherSessionCommittedReq); processor.committedRequests.add(orphanCommittedReq); processor.run(); Thread.sleep(1000); //We verify that the commit processor processed the old commit prior to the newer messages assertTrue(processedRequests.size() == 1); assertTrue(processedRequests.contains(otherSessionCommittedReq)); processor.run(); Thread.sleep(1000); //We verify that the commit processor handle all messages. assertTrue(processedRequests.containsAll(localRequests)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000202 15051152474 032744 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CommitProcessorMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CommitProce0100644 0000000 0000000 00000046066 15051152474 034343 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.WorkerService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CommitProcessorMetricsTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(CommitProcessorMetricsTest.class); CommitProcessor commitProcessor; DummyFinalProcessor finalProcessor; CountDownLatch requestScheduled = null; CountDownLatch requestProcessed = null; CountDownLatch commitSeen = null; CountDownLatch poolEmpytied = null; @BeforeEach public void setup() { LOG.info("setup"); ServerMetrics.getMetrics().resetAll(); // ensure no leaked parallelism properties System.clearProperty("zookeeper.commitProcessor.maxReadBatchSize"); System.clearProperty("zookeeper.commitProcessor.maxCommitBatchSize"); } public void setupProcessors(int commitWorkers, int finalProcTime) { finalProcessor = new DummyFinalProcessor(finalProcTime); commitProcessor = new TestCommitProcessor(finalProcessor, commitWorkers); commitProcessor.start(); } @AfterEach public void tearDown() throws Exception { LOG.info("tearDown starting"); commitProcessor.shutdown(); commitProcessor.join(); } private class TestCommitProcessor extends CommitProcessor { int numWorkerThreads; public TestCommitProcessor(RequestProcessor finalProcessor, int numWorkerThreads) { super(finalProcessor, "1", true, null); this.numWorkerThreads = numWorkerThreads; } @Override public void start() { super.workerPool = new TestWorkerService(numWorkerThreads); super.start(); // Since there are two threads--the test thread that puts requests into the queue and the processor // thread (this thread) that removes requests from the queue--the execution order in general is // indeterminate, making it hard to check the test results. // // In some tests, we really want the requests processed one by one. To achieve this, we make sure that // things happen in this order: // processor thread gets into WAITING -> test thread sets requestProcessed latch -> test thread puts // a request into the queue (which wakes up the processor thread in the WAITING state) and waits for // the requestProcessed latch -> the processor thread wakes up and removes the request from the queue and // processes it and opens the requestProcessed latch -> the test thread continues onto the next request // So it is important for the processor thread to get into WAITING before any request is put into the queue. // Otherwise, it would miss the wakeup signal and wouldn't process the request or open the latch and the // test thread waiting on the latch would be stuck Thread.State state = super.getState(); while (state != State.WAITING) { try { Thread.sleep(50); } catch (Exception e) { } state = super.getState(); } LOG.info("numWorkerThreads in Test is {}", numWorkerThreads); } @Override protected void endOfIteration() { if (requestProcessed != null) { requestProcessed.countDown(); } } @Override protected void waitForEmptyPool() throws InterruptedException { if (commitSeen != null) { commitSeen.countDown(); } super.waitForEmptyPool(); if (poolEmpytied != null) { poolEmpytied.countDown(); } } } private class TestWorkerService extends WorkerService { public TestWorkerService(int numWorkerThreads) { super("CommitProcWork", numWorkerThreads, true); } @Override public void schedule(WorkRequest workRequest, long id) { super.schedule(workRequest, id); if (requestScheduled != null) { requestScheduled.countDown(); } } } private class DummyFinalProcessor implements RequestProcessor { int processTime; public DummyFinalProcessor(int processTime) { this.processTime = processTime; } @Override public void processRequest(Request request) { if (processTime > 0) { try { if (commitSeen != null) { commitSeen.await(5, TimeUnit.SECONDS); } Thread.sleep(processTime); } catch (Exception e) { } } } @Override public void shutdown() { } } private void checkMetrics(String metricName, long min, long max, double avg, long cnt, long sum) { Map values = MetricsUtils.currentServerMetrics(); assertEquals(min, values.get("min_" + metricName), "expected min is " + min); assertEquals(max, values.get("max_" + metricName), "expected max is: " + max); assertEquals(avg, (Double) values.get("avg_" + metricName), 0.001, "expected avg is: " + avg); assertEquals(cnt, values.get("cnt_" + metricName), "expected cnt is: " + cnt); assertEquals(sum, values.get("sum_" + metricName), "expected sum is: " + sum); } private void checkTimeMetric(long actual, long lBoundrary, long hBoundrary) { assertThat(actual, greaterThanOrEqualTo(lBoundrary)); assertThat(actual, lessThanOrEqualTo(hBoundrary)); } private Request createReadRequest(long sessionId, int xid) { return new Request(null, sessionId, xid, ZooDefs.OpCode.getData, RequestRecord.fromBytes(new byte[10]), null); } private Request createWriteRequest(long sessionId, int xid) { return new Request(null, sessionId, xid, ZooDefs.OpCode.setData, RequestRecord.fromBytes(new byte[10]), null); } private void processRequestWithWait(Request request) throws Exception { requestProcessed = new CountDownLatch(1); commitProcessor.processRequest(request); requestProcessed.await(5, TimeUnit.SECONDS); } private void commitWithWait(Request request) throws Exception { requestProcessed = new CountDownLatch(1); commitProcessor.commit(request); requestProcessed.await(5, TimeUnit.SECONDS); } @Test public void testRequestsInSessionQueue() throws Exception { setupProcessors(0, 0); Request req1 = createWriteRequest(1L, 1); processRequestWithWait(req1); checkMetrics("requests_in_session_queue", 1L, 1L, 1D, 1L, 1L); //these two read requests will be stuck in the session queue because there is write in front of them processRequestWithWait(createReadRequest(1L, 2)); processRequestWithWait(createReadRequest(1L, 3)); checkMetrics("requests_in_session_queue", 1L, 3L, 2D, 3L, 6); commitWithWait(req1); checkMetrics("requests_in_session_queue", 1L, 3L, 2.25D, 4L, 9); } @Test public void testWriteFinalProcTime() throws Exception { setupProcessors(0, 1000); Request req1 = createWriteRequest(1L, 2); processRequestWithWait(req1); //no request sent to next processor yet Map values = MetricsUtils.currentServerMetrics(); assertEquals(0L, values.get("cnt_write_final_proc_time_ms")); commitWithWait(req1); values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_write_final_proc_time_ms")); checkTimeMetric((long) values.get("max_write_final_proc_time_ms"), 1000L, 2000L); } @Test public void testReadFinalProcTime() throws Exception { setupProcessors(0, 1000); processRequestWithWait(createReadRequest(1L, 1)); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_read_final_proc_time_ms")); checkTimeMetric((long) values.get("max_read_final_proc_time_ms"), 1000L, 2000L); } @Test public void testCommitProcessTime() throws Exception { setupProcessors(0, 0); processRequestWithWait(createReadRequest(1L, 1)); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_commit_process_time")); checkTimeMetric((long) values.get("max_commit_process_time"), 0L, 1000L); } @Test public void testServerWriteCommittedTime() throws Exception { setupProcessors(0, 0); //a commit w/o pending request is a write from other servers commitWithWait(createWriteRequest(1L, 1)); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_server_write_committed_time_ms")); checkTimeMetric((long) values.get("max_server_write_committed_time_ms"), 0L, 1000L); } @Test public void testLocalWriteCommittedTime() throws Exception { setupProcessors(0, 0); Request req1 = createWriteRequest(1L, 2); processRequestWithWait(req1); commitWithWait(req1); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_local_write_committed_time_ms")); checkTimeMetric((long) values.get("max_local_write_committed_time_ms"), 0L, 1000L); Request req2 = createWriteRequest(1L, 2); processRequestWithWait(req2); //the second write will be stuck in the session queue for at least one second //but the LOCAL_WRITE_COMMITTED_TIME is from when the commit is received Thread.sleep(1000); commitWithWait(req2); values = MetricsUtils.currentServerMetrics(); assertEquals(2L, values.get("cnt_local_write_committed_time_ms")); checkTimeMetric((long) values.get("max_local_write_committed_time_ms"), 0L, 1000L); } @Test public void testWriteCommitProcTime() throws Exception { setupProcessors(0, 0); Request req1 = createWriteRequest(1L, 2); processRequestWithWait(req1); commitWithWait(req1); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_write_commitproc_time_ms")); checkTimeMetric((long) values.get("max_write_commitproc_time_ms"), 0L, 1000L); Request req2 = createWriteRequest(1L, 2); processRequestWithWait(req2); //the second write will be stuck in the session queue for at least one second Thread.sleep(1000); commitWithWait(req2); values = MetricsUtils.currentServerMetrics(); assertEquals(2L, values.get("cnt_write_commitproc_time_ms")); checkTimeMetric((long) values.get("max_write_commitproc_time_ms"), 1000L, 2000L); } @Test public void testReadCommitProcTime() throws Exception { setupProcessors(0, 0); processRequestWithWait(createReadRequest(1L, 1)); Map values = MetricsUtils.currentServerMetrics(); assertEquals(1L, values.get("cnt_read_commitproc_time_ms")); checkTimeMetric((long) values.get("max_read_commitproc_time_ms"), 0L, 1000L); Request req1 = createWriteRequest(1L, 2); processRequestWithWait(req1); processRequestWithWait(createReadRequest(1L, 3)); //the second read will be stuck in the session queue for at least one second Thread.sleep(1000); commitWithWait(req1); values = MetricsUtils.currentServerMetrics(); assertEquals(2L, values.get("cnt_read_commitproc_time_ms")); checkTimeMetric((long) values.get("max_read_commitproc_time_ms"), 1000L, 2000L); } @Test public void testTimeWaitingEmptyPoolInCommitProcessorRead() throws Exception { setupProcessors(1, 1000); //three read requests will be scheduled first requestScheduled = new CountDownLatch(3); commitProcessor.processRequest(createReadRequest(0L, 2)); commitProcessor.processRequest(createReadRequest(1L, 3)); commitProcessor.processRequest(createReadRequest(2L, 4)); requestScheduled.await(5, TimeUnit.SECONDS); //add a commit request to trigger waitForEmptyPool poolEmpytied = new CountDownLatch(1); commitProcessor.commit(createWriteRequest(1L, 1)); poolEmpytied.await(5, TimeUnit.SECONDS); long actual = (long) MetricsUtils.currentServerMetrics().get("max_time_waiting_empty_pool_in_commit_processor_read_ms"); //since each request takes 1000ms to process, so the waiting shouldn't be more than three times of that checkTimeMetric(actual, 2500L, 3500L); } @Test public void testConcurrentRequestProcessingInCommitProcessor() throws Exception { setupProcessors(3, 1000); //three read requests will be processed in parallel commitSeen = new CountDownLatch(1); requestScheduled = new CountDownLatch(3); commitProcessor.processRequest(createReadRequest(1L, 2)); commitProcessor.processRequest(createReadRequest(1L, 3)); commitProcessor.processRequest(createReadRequest(1L, 4)); requestScheduled.await(5, TimeUnit.SECONDS); //add a commit request to trigger waitForEmptyPool, which will record number of requests being proccessed poolEmpytied = new CountDownLatch(1); commitProcessor.commit(createWriteRequest(1L, 1)); poolEmpytied.await(5, TimeUnit.SECONDS); //this will change after we upstream batch write in CommitProcessor Map values = MetricsUtils.currentServerMetrics(); assertEquals(3L, values.get("max_concurrent_request_processing_in_commit_processor")); } @Test public void testReadsAfterWriteInSessionQueue() throws Exception { setupProcessors(0, 0); //this read request is before write processRequestWithWait(createReadRequest(1L, 1)); //one write request Request req1 = createWriteRequest(1L, 1); processRequestWithWait(req1); //three read requests after the write processRequestWithWait(createReadRequest(1L, 2)); processRequestWithWait(createReadRequest(1L, 3)); processRequestWithWait(createReadRequest(1L, 4)); //commit the write commitWithWait(req1); checkMetrics("reads_after_write_in_session_queue", 3L, 3L, 3d, 1, 3); } @Test public void testReadsQueuedInCommitProcessor() throws Exception { setupProcessors(0, 0); processRequestWithWait(createReadRequest(1L, 1)); processRequestWithWait(createReadRequest(1L, 2)); //recorded reads in the queue are 1, 1 checkMetrics("read_commit_proc_req_queued", 1L, 1L, 1d, 2, 2); } @Test public void testWritesQueuedInCommitProcessor() throws Exception { setupProcessors(0, 0); Request req1 = createWriteRequest(1L, 1); processRequestWithWait(req1); Request req2 = createWriteRequest(1L, 2); processRequestWithWait(req2); //since we haven't got any commit request, the write request stays in the queue //recorded writes in the queue are 1, 2 checkMetrics("write_commit_proc_req_queued", 1L, 2L, 1.5d, 2, 3); commitWithWait(req1); //recording is done before commit request is processed, so writes in the queue are: 1, 2, 2 checkMetrics("write_commit_proc_req_queued", 1L, 2L, 1.6667d, 3, 5); commitWithWait(req2); //writes in the queue are 1, 2, 2, 1 checkMetrics("write_commit_proc_req_queued", 1L, 2L, 1.5d, 4, 6); //send a read request to trigger the recording, this time the write queue should be empty //writes in the queue are 1, 2, 2, 1, 0 processRequestWithWait(createReadRequest(1L, 1)); checkMetrics("write_commit_proc_req_queued", 0L, 2L, 1.2d, 5, 6); } @Test public void testCommitsQueuedInCommitProcessor() throws Exception { setupProcessors(0, 0); commitWithWait(createWriteRequest(1L, 1)); commitWithWait(createWriteRequest(1L, 2)); //recorded commits in the queue are 1, 1 checkMetrics("commit_commit_proc_req_queued", 1L, 1L, 1d, 2, 2); } @Test public void testCommitsQueued() throws Exception { setupProcessors(0, 0); commitWithWait(createWriteRequest(1L, 1)); commitWithWait(createWriteRequest(1L, 2)); Map values = MetricsUtils.currentServerMetrics(); assertEquals(2L, (long) values.get("request_commit_queued")); } @Test public void testPendingSessionQueueSize() throws Exception { setupProcessors(0, 0); //one write request for session 1 Request req1 = createWriteRequest(1L, 1); processRequestWithWait(req1); //two write requests for session 2 Request req2 = createWriteRequest(2L, 2); processRequestWithWait(req2); Request req3 = createWriteRequest(2L, 3); processRequestWithWait(req3); commitWithWait(req1); //there are two sessions with pending requests checkMetrics("pending_session_queue_size", 2L, 2L, 2d, 1, 2); commitWithWait(req2); //there is on session with pending requests checkMetrics("pending_session_queue_size", 1L, 2L, 1.5d, 2, 3); commitWithWait(req3); //there is one session with pending requests checkMetrics("pending_session_queue_size", 1L, 2L, 1.333d, 3, 4); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000173 15051152474 032753 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CommitProcessorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CommitProce0100644 0000000 0000000 00000050227 15051152474 034335 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.GetDataRequest; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.PrepRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The following are invariant regardless of the particular implementation * of the CommitProcessor, and are tested for: * * 1. For each session, requests are processed and the client sees its * responses in order. * 2. Write requests are processed in zxid order across all sessions. * * The following are also tested for here, but are specific to this * particular implementation. The underlying issue is that watches can be * reset while reading the data. For reads/writes on two different sessions * on different nodes, or with reads that do not set watches, the reads can * happen in any order relative to the writes. For a read in one session that * resets a watch that is triggered by a write on another session, however, * we need to ensure that there is no race condition * * 3. The pipeline needs to be drained before a write request can enter. * 4. No in-flight write requests while processing a read request. */ public class CommitProcessorTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(CommitProcessorTest.class); // The amount of ms each test case should run static final int TEST_RUN_TIME_IN_MS = 5000; private AtomicInteger processedReadRequests = new AtomicInteger(0); private AtomicInteger processedWriteRequests = new AtomicInteger(0); boolean stopped; TestZooKeeperServer zks; File tmpDir; ArrayList testClients = new ArrayList<>(); CommitProcessor commitProcessor; public void setUp(int numCommitThreads, int numClientThreads, int writePercent) throws Exception { stopped = false; System.setProperty(CommitProcessor.ZOOKEEPER_COMMIT_PROC_NUM_WORKER_THREADS, Integer.toString(numCommitThreads)); tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); zks = new TestZooKeeperServer(tmpDir, tmpDir, 4000); zks.startup(); for (int i = 0; i < numClientThreads; ++i) { TestClientThread client = new TestClientThread(writePercent); testClients.add(client); client.start(); } } public void setUp( int numCommitThreads, int numReadOnlyClientThreads, int mixWorkloadClientThreads, int writePercent) throws Exception { stopped = false; System.setProperty(CommitProcessor.ZOOKEEPER_COMMIT_PROC_NUM_WORKER_THREADS, Integer.toString(numCommitThreads)); tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); zks = new TestZooKeeperServer(tmpDir, tmpDir, 4000); zks.startup(); for (int i = 0; i < mixWorkloadClientThreads; ++i) { TestClientThread client = new TestClientThread(writePercent); testClients.add(client); client.start(); } for (int i = 0; i < numReadOnlyClientThreads; ++i) { TestClientThread client = new TestClientThread(0); testClients.add(client); client.start(); } } @AfterEach public void tearDown() throws Exception { LOG.info("tearDown starting"); stopped = true; zks.shutdown(); for (TestClientThread client : testClients) { client.interrupt(); client.join(); } if (tmpDir != null) { assertTrue(ClientBase.recursiveDelete(tmpDir), "delete " + tmpDir.toString()); } processedReadRequests.set(0); processedWriteRequests.set(0); testClients.clear(); commitProcessor.join(); } private class TestClientThread extends Thread { long sessionId; int cxid; int nodeId; int writePercent; public TestClientThread(int writePercent) { sessionId = zks.getSessionTracker().createSession(5000); this.writePercent = writePercent; } public void sendWriteRequest() throws Exception { ByteArrayOutputStream boas = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas); CreateRequest createReq = new CreateRequest("/session" + Long.toHexString(sessionId) + "-" + (++nodeId), new byte[0], Ids.OPEN_ACL_UNSAFE, 1); createReq.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray()); Request req = new Request(null, sessionId, ++cxid, OpCode.create, RequestRecord.fromBytes(bb), new ArrayList()); zks.getFirstProcessor().processRequest(req); } public void sendReadRequest() throws Exception { ByteArrayOutputStream boas = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas); GetDataRequest getDataRequest = new GetDataRequest("/session" + Long.toHexString(sessionId) + "-" + nodeId, false); getDataRequest.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray()); Request req = new Request(null, sessionId, ++cxid, OpCode.getData, RequestRecord.fromBytes(bb), new ArrayList()); zks.getFirstProcessor().processRequest(req); } public void run() { Random rand = new Random(Thread.currentThread().getId()); try { sendWriteRequest(); while (!stopped) { if (rand.nextInt(100) < writePercent) { sendWriteRequest(); } else { sendReadRequest(); } Thread.sleep(5 + rand.nextInt(95)); } } catch (Exception e) { LOG.error("Uncaught exception in test: ", e); } } } @Test public void testNoCommitWorkersReadOnlyWorkload() throws Exception { int numClients = 10; LOG.info("testNoCommitWorkersReadOnlyWorkload"); setUp(0, numClients, 0); synchronized (this) { wait(TEST_RUN_TIME_IN_MS); } assertFalse(fail); assertTrue(processedReadRequests.get() > 0, "No read requests processed"); // processedWriteRequests.get() == numClients since each client performs one write at the beginning (creates a znode) assertTrue(processedWriteRequests.get() == numClients, "Write requests processed"); } @Test public void testNoCommitWorkersMixedWorkload() throws Exception { int numClients = 10; LOG.info("testNoCommitWorkersMixedWorkload 25w/75r workload test"); setUp(0, numClients, 25); synchronized (this) { wait(TEST_RUN_TIME_IN_MS); } assertFalse(fail); checkProcessedRequest(); } @Test public void testOneCommitWorkerReadOnlyWorkload() throws Exception { int numClients = 10; LOG.info("testOneCommitWorkerReadOnlyWorkload"); setUp(1, numClients, 0); synchronized (this) { wait(TEST_RUN_TIME_IN_MS); } assertFalse(fail); assertTrue(processedReadRequests.get() > 0, "No read requests processed"); // processedWriteRequests.get() == numClients since each client performs one write at the beginning (creates a znode) assertTrue(processedWriteRequests.get() == numClients, "Write requests processed"); } @Test public void testOneCommitWorkerMixedWorkload() throws Exception { setUp(1, 10, 25); LOG.info("testOneCommitWorkerMixedWorkload 25w/75r workload test"); synchronized (this) { wait(TEST_RUN_TIME_IN_MS); } assertFalse(fail); checkProcessedRequest(); } @Test public void testManyCommitWorkersReadOnly() throws Exception { int numClients = 10; LOG.info("testManyCommitWorkersReadOnly"); setUp(10, numClients, 0); synchronized (this) { wait(TEST_RUN_TIME_IN_MS); } assertFalse(fail); assertTrue(processedReadRequests.get() > 0, "No read requests processed"); // processedWriteRequests.get() == numClients since each client performs one write at the beginning (creates a znode) assertTrue(processedWriteRequests.get() == numClients, "Write requests processed"); } @Test public void testManyCommitWorkersMixedWorkload() throws Exception { setUp(16, 8, 8, 25); LOG.info("testManyCommitWorkersMixedWorkload 8X0w/100r + 8X25w/75r workload test"); synchronized (this) { wait(TEST_RUN_TIME_IN_MS); } assertFalse(fail); checkProcessedRequest(); } private void checkProcessedRequest() { assertTrue(processedReadRequests.get() > 0, "No read requests processed"); assertTrue(processedWriteRequests.get() > 0, "No write requests processed"); } volatile boolean fail = false; private synchronized void failTest(String reason) { fail = true; notifyAll(); fail(reason); } private class TestZooKeeperServer extends ZooKeeperServer { public TestZooKeeperServer(File snapDir, File logDir, int tickTime) throws IOException { super(snapDir, logDir, tickTime); } public PrepRequestProcessor getFirstProcessor() { return (PrepRequestProcessor) firstProcessor; } // Leader mock: Prep -> MockProposal -> Commit -> validate -> Final // Have side thread call commitProc.commit() @Override protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(zks); // ValidateProcessor is set up in a similar fashion to ToBeApplied // processor, so it can do pre/post validating of requests ValidateProcessor validateProcessor = new ValidateProcessor(finalProcessor); commitProcessor = new CommitProcessor(validateProcessor, "1", true, null); validateProcessor.setCommitProcessor(commitProcessor); commitProcessor.start(); MockProposalRequestProcessor proposalProcessor = new MockProposalRequestProcessor(commitProcessor); proposalProcessor.start(); firstProcessor = new PrepRequestProcessor(zks, proposalProcessor); getFirstProcessor().start(); } } private class MockProposalRequestProcessor extends Thread implements RequestProcessor { private final CommitProcessor commitProcessor; private final LinkedBlockingQueue proposals = new LinkedBlockingQueue<>(); public MockProposalRequestProcessor(CommitProcessor commitProcessor) { this.commitProcessor = commitProcessor; } @Override public void run() { Random rand = new Random(Thread.currentThread().getId()); try { while (true) { // If it is a read-only test, there will be no proposals.. if (!proposals.isEmpty()) { Request request = proposals.take(); Thread.sleep(5 + rand.nextInt(95)); commitProcessor.commit(request); } } } catch (InterruptedException e) { // ignore } } @Override public void processRequest(Request request) throws RequestProcessorException { commitProcessor.processRequest(request); if (request.getHdr() != null) { // fake propose request proposals.add(request); } } @Override public void shutdown() { LOG.info("shutdown MockProposalRequestProcessor"); proposals.clear(); if (commitProcessor != null) { commitProcessor.shutdown(); } } } private class ValidateProcessor implements RequestProcessor { Random rand = new Random(Thread.currentThread().getId()); RequestProcessor nextProcessor; CommitProcessor commitProcessor; AtomicLong expectedZxid = new AtomicLong(1); ConcurrentHashMap cxidMap = new ConcurrentHashMap<>(); AtomicInteger outstandingReadRequests = new AtomicInteger(0); AtomicInteger outstandingWriteRequests = new AtomicInteger(0); public ValidateProcessor(RequestProcessor nextProcessor) { this.nextProcessor = nextProcessor; } public void setCommitProcessor(CommitProcessor commitProcessor) { this.commitProcessor = commitProcessor; } @Override public void processRequest(Request request) throws RequestProcessorException { if (stopped) { return; } if (request.type == OpCode.closeSession) { LOG.debug("ValidateProcessor got closeSession request=" + request); nextProcessor.processRequest(request); return; } boolean isWriteRequest = commitProcessor.needCommit(request); if (isWriteRequest) { outstandingWriteRequests.incrementAndGet(); validateWriteRequestVariant(request); LOG.debug("Starting write request zxid={}", request.zxid); } else { LOG.debug( "Starting read request cxid={} for session 0x{}", request.cxid, Long.toHexString(request.sessionId)); outstandingReadRequests.incrementAndGet(); validateReadRequestVariant(request); } // Insert random delay to test thread race conditions try { Thread.sleep(5 + rand.nextInt(25)); } catch (InterruptedException e) { // ignore } nextProcessor.processRequest(request); /* * The commit workers will have to execute this line before they * wake up the commit processor. So this value is up-to-date when * variant check is performed */ if (isWriteRequest) { outstandingWriteRequests.decrementAndGet(); LOG.debug("Done write request zxid={}", request.zxid); processedWriteRequests.incrementAndGet(); } else { outstandingReadRequests.decrementAndGet(); LOG.debug( "Done read request cxid={} for session 0x{}", request.cxid, Long.toHexString(request.sessionId)); processedReadRequests.incrementAndGet(); } validateRequest(request); } /** * Validate that this is the only request in the pipeline */ private void validateWriteRequestVariant(Request request) { if (stopped) { return; } long zxid = request.getHdr().getZxid(); int readRequests = outstandingReadRequests.get(); if (readRequests != 0) { failTest("There are " + readRequests + " outstanding" + " read requests while issuing a write request zxid=" + zxid); } int writeRequests = outstandingWriteRequests.get(); if (writeRequests > 1) { failTest("There are " + writeRequests + " outstanding" + " write requests while issuing a write request zxid=" + zxid + " (expected one)"); } } /** * Validate that no write request is in the pipeline while working * on a read request */ private void validateReadRequestVariant(Request request) { int writeRequests = outstandingWriteRequests.get(); if (writeRequests != 0) { failTest("There are " + writeRequests + " outstanding" + " write requests while issuing a read request cxid=" + request.cxid + " for session 0x" + Long.toHexString(request.sessionId)); } } private void validateRequest(Request request) { LOG.debug("Got request {}", request); // Zxids should always be in order for write requests if (request.getHdr() != null) { long zxid = request.getHdr().getZxid(); if (!expectedZxid.compareAndSet(zxid, zxid + 1)) { failTest("Write request, expected_zxid=" + expectedZxid.get() + "; req_zxid=" + zxid); } } // Each session should see its cxids in order AtomicInteger sessionCxid = cxidMap.get(request.sessionId); if (sessionCxid == null) { sessionCxid = new AtomicInteger(request.cxid + 1); AtomicInteger existingSessionCxid = cxidMap.putIfAbsent(request.sessionId, sessionCxid); if (existingSessionCxid != null) { failTest("Race condition adding cxid=" + request.cxid + " for session 0x" + Long.toHexString(request.sessionId) + " with other_cxid=" + existingSessionCxid.get()); } } else { if (!sessionCxid.compareAndSet(request.cxid, request.cxid + 1)) { failTest("Expected_cxid=" + sessionCxid.get() + "; req_cxid=" + request.cxid); } } } @Override public void shutdown() { LOG.info("shutdown validateReadRequestVariant"); cxidMap.clear(); expectedZxid = new AtomicLong(1); if (nextProcessor != null) { nextProcessor.shutdown(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000204 15051152474 032746 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CurrentEpochWriteFailureTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/CurrentEpoc0100644 0000000 0000000 00000011640 15051152474 034341 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CurrentEpochWriteFailureTest extends QuorumPeerTestBase { protected static final Logger LOG = LoggerFactory.getLogger(CurrentEpochWriteFailureTest.class); private Servers servers; private int clientPort; @AfterEach public void tearDown() throws InterruptedException { if (servers != null) { servers.shutDownAllServers(); } } /* * ZOOKEEPER-4269: * accepted epoch is first written to temporary file acceptedEpoch.tmp then this file is * renamed to acceptedEpoch. * Failure, either because of exception or power-off, in renaming the acceptedEpoch.tmp file * will cause server startup error with message "The current epoch, x, is older than the last * zxid y" * To handle this scenario we should read accepted epoch from this temp file as well. */ @Test public void testReadCurrentEpochFromAcceptedEpochTmpFile() throws Exception { startServers(); writeSomeData(); restartServers(); writeSomeData(); MainThread firstServer = servers.mt[0]; // As started servers two times, current epoch must be two long currentEpoch = firstServer.getQuorumPeer().getCurrentEpoch(); assertEquals(2, currentEpoch); // Initialize files for later use File snapDir = firstServer.getQuorumPeer().getTxnFactory().getSnapDir(); File currentEpochFile = new File(snapDir, QuorumPeer.CURRENT_EPOCH_FILENAME); File currentEpochTempFile = new File(snapDir, QuorumPeer.CURRENT_EPOCH_FILENAME + AtomicFileOutputStream.TMP_EXTENSION); // Shutdown servers servers.shutDownAllServers(); waitForAll(servers, ZooKeeper.States.CONNECTING); // Create scenario of file currentEpoch.tmp rename to currentEpoch failure. // In this case currentEpoch file will have old epoch and currentEpoch.tmp will have the latest epoch FileUtils.write(currentEpochFile, Long.toString(currentEpoch - 1), "UTF-8"); FileUtils.write(currentEpochTempFile, Long.toString(currentEpoch), "UTF-8"); // Restart the serves, all serves should restart successfully. servers.restartAllServersAndClients(this); // Check the first server where problem was injected. assertTrue(ClientBase .waitForServerUp("127.0.0.1:" + firstServer.getClientPort(), CONNECTION_TIMEOUT), "server " + firstServer.getMyid() + " is not up as file currentEpoch.tmp rename to currentEpoch file was failed" + " which lead current epoch inconsistent state."); } private void restartServers() throws InterruptedException, IOException { servers.shutDownAllServers(); waitForAll(servers, ZooKeeper.States.CONNECTING); servers.restartAllServersAndClients(this); waitForAll(servers, ZooKeeper.States.CONNECTED); } private void writeSomeData() throws Exception { ZooKeeper client = ClientBase.createZKClient("127.0.0.1:" + clientPort); String path = "/somePath" + System.currentTimeMillis(); String data = "someData"; client.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); byte[] data1 = client.getData(path, false, null); assertEquals(data, new String(data1)); client.close(); } private void startServers() throws Exception { servers = LaunchServers(3); clientPort = servers.clientPorts[0]; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032757 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/DIFFSyncConsistencyTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/DIFFSyncCon0100644 0000000 0000000 00000027375 15051152474 034131 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.Map; import javax.security.sasl.SaslException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class DIFFSyncConsistencyTest extends QuorumPeerTestBase { private static int SERVER_COUNT = 3; private MainThread[] mt = new MainThread[SERVER_COUNT]; @Test @Timeout(value = 120) public void testInconsistentDueToUncommittedLog() throws Exception { final int LEADER_TIMEOUT_MS = 10_000; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new MockTestQPMain(); } }; mt[i].start(); } for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } int leader = findLeader(mt); CountdownWatcher watch = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[leader], ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); Map outstanding = mt[leader].main.quorumPeer.leader.outstandingProposals; // Increase the tick time to delay the leader going to looking to allow us proposal a transaction while other // followers are offline. int previousTick = mt[leader].main.quorumPeer.tickTime; mt[leader].main.quorumPeer.tickTime = LEADER_TIMEOUT_MS; // Let the previous tick on the leader exhaust itself so the new tick time takes effect Thread.sleep(previousTick); LOG.info("LEADER ELECTED {}", leader); // Shutdown followers to make sure we don't accidentally send the proposal we are going to make to follower. // In other words, we want to make sure the followers get the proposal later through DIFF sync. for (int i = 0; i < SERVER_COUNT; i++) { if (i != leader) { mt[i].shutdown(); } } // Send a create request to old leader and make sure it's synced to disk. try { zk.create("/zk" + leader, "zk".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("create /zk" + leader + " should have failed"); } catch (KeeperException e) { } // Make sure that we actually did get it in process at the leader; there can be extra sessionClose proposals. assertTrue(outstanding.size() > 0); Proposal p = findProposalOfType(outstanding, OpCode.create); LOG.info("Old leader id: {}. All proposals: {}", leader, outstanding); assertNotNull(p, "Old leader doesn't have 'create' proposal"); // Make sure leader sync the proposal to disk. int sleepTime = 0; Long longLeader = (long) leader; while (!p.qvAcksetPairs.get(0).getAckset().contains(longLeader)) { if (sleepTime > 2000) { fail("Transaction not synced to disk within 1 second " + p.qvAcksetPairs.get(0).getAckset() + " expected " + leader); } Thread.sleep(100); sleepTime += 100; } // Start controlled followers where we deliberately make the follower fail once follower receive the UPTODATE // message from leader. Because followers only persist proposals from DIFF sync after UPTODATE, this can // deterministically simulate the situation where followers ACK NEWLEADER (which makes leader think she has the // quorum support, but actually not afterwards) but immediately fail afterwards without persisting the proposals // from DIFF sync. for (int i = 0; i < SERVER_COUNT; i++) { if (i == leader) { continue; } mt[i].start(); int sleepCount = 0; while (mt[i].getQuorumPeer() == null) { ++sleepCount; if (sleepCount > 100) { fail("Can't start follower " + i + " !"); } Thread.sleep(100); } ((CustomQuorumPeer) mt[i].getQuorumPeer()).setInjectError(true); LOG.info("Follower {} started.", i); } // Verify leader can see it. The fact that leader can see it implies that // leader should, at this point in time, get a quorum of ACK of NEWLEADER // from two followers so leader can start serving requests; this also implies // that DIFF sync from leader to followers are finished at this point in time. // We then verify later that followers should have the same view after we shutdown // this leader, otherwise it's a violation of ZAB / sequential consistency. int c = 0; while (c < 100) { ++c; try { Stat stat = zk.exists("/zk" + leader, false); assertNotNull(stat, "server " + leader + " should have /zk"); break; } catch (KeeperException.ConnectionLossException e) { } Thread.sleep(100); } // Shutdown all servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } waitForOne(zk, States.CONNECTING); // Now restart all servers except the old leader. Only old leader has the transaction sync to disk. // The old followers only had in memory view of the transaction, and they didn't have a chance // to sync to disk because we made them fail at UPTODATE. for (int i = 0; i < SERVER_COUNT; i++) { if (i == leader) { continue; } mt[i].start(); int sleepCount = 0; while (mt[i].getQuorumPeer() == null) { ++sleepCount; if (sleepCount > 100) { fail("Can't start follower " + i + " !"); } Thread.sleep(100); } ((CustomQuorumPeer) mt[i].getQuorumPeer()).setInjectError(false); LOG.info("Follower {} started again.", i); } int newLeader = findLeader(mt); assertNotEquals(newLeader, leader, "new leader is still the old leader " + leader + " !!"); // This simulates the case where clients connected to the old leader had a view of the data // "/zkX", but clients connect to the new leader does not have the same view of data (missing "/zkX"). // This inconsistent view of the quorum exposed from leaders is a violation of ZAB. for (int i = 0; i < SERVER_COUNT; i++) { if (i != newLeader) { continue; } zk.close(); zk = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); Stat val = zk.exists("/zk" + leader, false); assertNotNull(val, "Data inconsistency detected! " + "Server " + i + " should have a view of /zk" + leader + "!"); } zk.close(); } @AfterEach public void tearDown() { for (int i = 0; i < mt.length; i++) { try { mt[i].shutdown(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted while shutting it down", e); } } } static class CustomQuorumPeer extends QuorumPeer { private volatile boolean injectError = false; public CustomQuorumPeer() throws SaslException { } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb())) { @Override void readPacket(QuorumPacket pp) throws IOException { /** * In real scenario got SocketTimeoutException while reading * the packet from leader because of network problem, but * here throwing SocketTimeoutException based on whether * error is injected or not */ super.readPacket(pp); if (injectError && pp.getType() == Leader.UPTODATE) { String type = LearnerHandler.packetToString(pp); throw new SocketTimeoutException("Socket timeout while reading the packet for operation " + type); } } }; } public void setInjectError(boolean injectError) { this.injectError = injectError; } } static class MockTestQPMain extends TestQPMain { @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new CustomQuorumPeer(); } } private Proposal findProposalOfType(Map proposals, int type) { for (Proposal proposal : proposals.values()) { if (proposal.request.getHdr().getType() == type) { return proposal; } } return null; } private int findLeader(MainThread[] mt) { for (int i = 0; i < mt.length; i++) { if (mt[i].main.quorumPeer.leader != null) { return i; } } return -1; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032753 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/DIFFSyncTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/DIFFSyncTes0100644 0000000 0000000 00000030615 15051152474 034134 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.security.sasl.SaslException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class DIFFSyncTest extends QuorumPeerTestBase { private static final int SERVER_COUNT = 3; private static final String PATH_PREFIX = "/test_"; private int[] clientPorts; private MainThread[] mt; private ZooKeeper[] zkClients; @BeforeEach public void start() throws Exception { clientPorts = new int[SERVER_COUNT]; mt = startQuorum(clientPorts); zkClients = new ZooKeeper[SERVER_COUNT]; } @AfterEach public void tearDown() throws Exception{ for (final ZooKeeper zk : zkClients) { try { if (zk != null) { zk.close(); } } catch (final InterruptedException e) { LOG.warn("ZooKeeper interrupted while shutting it down", e); } } for (final MainThread mainThread : mt) { try { mainThread.shutdown(); } catch (final InterruptedException e) { LOG.warn("Quorum Peer interrupted while shutting it down", e); } } } @Test @Timeout(value = 120) public void testTxnLoss_FailToPersistAndCommitTxns() throws Exception { final List paths = new ArrayList<>(); assertEquals(2, mt[2].getQuorumPeer().getLeaderId()); // create a ZK client to the leader (currentEpoch=1, lastLoggedZxid=<1, 1>) createZKClient(2); // create a znode (currentEpoch=1, lastLoggedZxid=<1, 2>) paths.add(createNode(zkClients[2], PATH_PREFIX + "0")); // shut down S0 mt[0].shutdown(); LOG.info("S0 shutdown."); // create a znode (currentEpoch=1, lastLoggedZxid=<1, 3>), so S0 is 1 txn behind paths.add(createNode(zkClients[2], PATH_PREFIX + "1")); logEpochsAndLastLoggedTxnForAllServers(); // shut down S1 mt[1].shutdown(); LOG.info("S1 shutdown."); // restart S0 and trigger a new leader election (currentEpoch=2) // S0 starts with MockSyncRequestProcessor and MockCommitProcessor to simulate it writes the // currentEpoch and sends NEWLEADER ACK but fails to persist and commit txns afterwards // in DIFF sync mt[0].start(new MockTestQPMain()); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[0], CONNECTION_TIMEOUT), "waiting for server 0 being up"); LOG.info("S0 restarted."); logEpochsAndLastLoggedTxnForAllServers(); // validate S2 is still the leader assertEquals(2, mt[2].getQuorumPeer().getLeaderId()); // shut down the leader (i.e. S2). This causes S0 disconnects from leader, performs partial // shutdown, fast forwards its database to the latest persisted tnx (i.e. <1, 3>) and change // its state to LOOKING mt[2].shutdown(); LOG.info("S2 shutdown."); // start S1 and trigger a leader election (currentEpoch=3) mt[1].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[1], CONNECTION_TIMEOUT), "waiting for server 1 being up"); LOG.info("S1 restarted."); logEpochsAndLastLoggedTxnForAllServers(); // validate S0 is the new leader because of it has higher epoch assertEquals(0, mt[0].getQuorumPeer().getLeaderId()); // connect to the new leader (i.e. S0) (currentEpoch=3, lastLoggedZxid=<3, 1> createZKClient(0); // create a znode (currentEpoch=3, lastLoggedZxid=<3, 2>) paths.add(createNode(zkClients[0], PATH_PREFIX + "3")); // start S2 which is the old leader mt[2].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[2], CONNECTION_TIMEOUT), "waiting for server " + 2 + " being up"); LOG.info("S2 restarted."); logEpochsAndLastLoggedTxnForAllServers(); // validate all the znodes exist from all the clients validateDataFromAllClients(paths); } @Test @Timeout(value = 120) public void testLeaderShutdown_AckProposalBeforeAckNewLeader() throws Exception { assertEquals(2, mt[2].getQuorumPeer().getLeaderId()); // create a ZK client to the leader (currentEpoch=1, lastLoggedZxid=<1, 1>) createZKClient(2); // create a znode (currentEpoch=1, lastLoggedZxid=<1, 2>) createNode(zkClients[2], PATH_PREFIX + "0"); // shut down S0 mt[0].shutdown(); LOG.info("S0 shutdown."); // create a znode (currentEpoch=1, lastLoggedZxid=<1, 3>), so S0 is 1 txn behind createNode(zkClients[2], PATH_PREFIX + "1"); logEpochsAndLastLoggedTxnForAllServers(); // shut down S1 mt[1].shutdown(); LOG.info("S1 shutdown."); // restart S0 and trigger a new leader election and DIFF sync (currentEpoch=2) mt[0].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[0], CONNECTION_TIMEOUT), "waiting for server 0 being up"); LOG.info("S0 restarted."); // create a znode (currentEpoch=2, lastLoggedZxid=<2, 1>) createNode(zkClients[2], PATH_PREFIX + "2"); // validate quorum is up without additional round of leader election for (int i = 0; i < SERVER_COUNT; i++) { if (i != 1) { final QuorumPeer qp = mt[i].getQuorumPeer(); assertNotNull(qp); assertEquals(2, qp.getCurrentEpoch()); assertEquals(2, qp.getAcceptedEpoch()); assertEquals("200000001", Long.toHexString(qp.getLastLoggedZxid())); } } } private MainThread[] startQuorum(final int[] clientPorts) throws IOException { final StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server); sb.append("\n"); } final MainThread[] mt = new MainThread[SERVER_COUNT]; // start all the servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], sb.toString(), false); mt[i].start(); } // ensure all servers started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } return mt; } private void createZKClient(final int idx) throws Exception { zkClients[idx] = null; final ClientBase.CountdownWatcher watch = new ClientBase.CountdownWatcher(); zkClients[idx] = new ZooKeeper("127.0.0.1:" + clientPorts[idx], ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); } private String createNode(final ZooKeeper zk, final String path) throws Exception { final String fullPath = zk.create(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(zk.exists(path, false)); return fullPath; } private static class MockTestQPMain extends TestQPMain { @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new TestQuorumPeer(); } } private static class TestQuorumPeer extends QuorumPeer { public TestQuorumPeer() throws SaslException { } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { final FollowerZooKeeperServer followerZookeeperServer = new FollowerZooKeeperServer(logFactory, this, this.getZkDb()) { @Override protected void setupRequestProcessors() { RequestProcessor finalProcessor = new FinalRequestProcessor(this); commitProcessor = new MockCommitProcessor(finalProcessor, Long.toString(getServerId()), true, getZooKeeperServerListener()); commitProcessor.start(); firstProcessor = new FollowerRequestProcessor(this, commitProcessor); ((FollowerRequestProcessor) firstProcessor).start(); syncProcessor = new MockSyncRequestProcessor(this, new SendAckRequestProcessor(getFollower())); syncProcessor.start(); } }; return new Follower(this, followerZookeeperServer); } } private static class MockSyncRequestProcessor extends SyncRequestProcessor { public MockSyncRequestProcessor(final ZooKeeperServer zks, final RequestProcessor nextProcessor) { super(zks, nextProcessor); } @Override public void processRequest(final Request request) { LOG.info("Sync request for zxid {} is dropped", Long.toHexString(request.getHdr().getZxid())); } } private static class MockCommitProcessor extends CommitProcessor { public MockCommitProcessor(final RequestProcessor nextProcessor, final String id, final boolean matchSyncs, final ZooKeeperServerListener listener) { super(nextProcessor, id, matchSyncs, listener); } @Override public void commit(final Request request) { LOG.info("Commit request for zxid {} is dropped", Long.toHexString(request.getHdr().getZxid())); } } private void logEpochsAndLastLoggedTxnForAllServers() throws Exception { for (int i = 0; i < SERVER_COUNT; i++) { final QuorumPeer qp = mt[i].getQuorumPeer(); if (qp != null) { LOG.info(String.format("server id=%d, acceptedEpoch=%d, currentEpoch=%d, lastLoggedTxn=%s", qp.getMyId(), qp.getAcceptedEpoch(), qp.getCurrentEpoch(), Long.toHexString(qp.getLastLoggedZxid()))); } } } private void validateDataFromAllClients(final List paths) throws Exception{ for (int i = 0; i < SERVER_COUNT; i++) { if (zkClients[i] == null) { createZKClient(i); } for (final String path : paths) { assertNotNull(zkClients[i].exists(path, false), "znode " + path + " is missing"); } assertEquals(3, paths.size()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032755 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/DelayRequestProcessor.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/DelayReques0100644 0000000 0000000 00000005231 15051152474 034332 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.concurrent.LinkedBlockingQueue; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; /** * Allows the blocking of the request processor queue on a ZooKeeperServer. * * This is used to simulate arbitrary length delays or to produce delays * in request processing that are maximally inconvenient for a given feature * for the purposes of testing it. */ public class DelayRequestProcessor implements RequestProcessor { private boolean blocking; RequestProcessor next; private LinkedBlockingQueue incomingRequests = new LinkedBlockingQueue<>(); private DelayRequestProcessor(RequestProcessor next) { this.blocking = true; this.next = next; } @Override public void processRequest(Request request) throws RequestProcessorException { if (blocking) { incomingRequests.add(request); } else { next.processRequest(request); } } public void submitRequest(Request request) throws RequestProcessorException { next.processRequest(request); } @Override public void shutdown() { } public void unblockQueue() throws RequestProcessorException { if (blocking) { for (Request request : incomingRequests) { next.processRequest(request); } blocking = false; } } public static DelayRequestProcessor injectDelayRequestProcessor(FollowerZooKeeperServer zooKeeperServer) { RequestProcessor finalRequestProcessor = zooKeeperServer.commitProcessor.nextProcessor; DelayRequestProcessor delayRequestProcessor = new DelayRequestProcessor(finalRequestProcessor); zooKeeperServer.commitProcessor.nextProcessor = delayRequestProcessor; return delayRequestProcessor; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/EagerACLFilterTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/EagerACLFil0100644 0000000 0000000 00000031252 15051152474 034107 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.stream.Stream; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.QuorumBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class EagerACLFilterTest extends QuorumBase { protected boolean complete = false; protected static final String PARENT_PATH = "/foo"; protected static final String CHILD_PATH = "/foo/bar"; protected static final String AUTH_PROVIDER = "digest"; protected static final byte[] AUTH = "hello".getBytes(); protected static final byte[] AUTHB = "goodbye".getBytes(); protected static final byte[] DATA = "Hint Water".getBytes(); protected TestableZooKeeper zkClient; protected TestableZooKeeper zkClientB; protected TestableZooKeeper zkLeaderClient; protected QuorumPeer zkLeader; protected QuorumPeer zkConnected; protected ZooKeeperServer connectedServer; public static Stream data() { return Stream.of( Arguments.of(ServerState.LEADING, true), Arguments.of(ServerState.LEADING, false), Arguments.of(ServerState.FOLLOWING, true), Arguments.of(ServerState.FOLLOWING, false), Arguments.of(ServerState.OBSERVING, true), Arguments.of(ServerState.OBSERVING, false)); } @BeforeEach @Override public void setUp() { //since parameterized test methods need a parameterized setUp method //the inherited method has to be overridden with an empty function body } public void setUp(ServerState serverState, boolean checkEnabled) throws Exception { ensureCheck(checkEnabled); CountdownWatcher leaderWatch = new CountdownWatcher(); CountdownWatcher clientWatch = new CountdownWatcher(); CountdownWatcher clientWatchB = new CountdownWatcher(); super.setUp(true, true); String hostPort = getPeersMatching(serverState).split(",")[0]; int clientPort = Integer.parseInt(hostPort.split(":")[1]); zkLeader = getPeerList().get(getLeaderIndex()); zkConnected = getPeerByClientPort(clientPort); connectedServer = zkConnected.getActiveServer(); zkLeaderClient = createClient(leaderWatch, getPeersMatching(ServerState.LEADING)); zkClient = createClient(clientWatch, hostPort); zkClientB = createClient(clientWatchB, hostPort); zkClient.addAuthInfo(AUTH_PROVIDER, AUTH); zkClientB.addAuthInfo(AUTH_PROVIDER, AUTHB); leaderWatch.waitForConnected(CONNECTION_TIMEOUT); clientWatch.waitForConnected(CONNECTION_TIMEOUT); clientWatchB.waitForConnected(CONNECTION_TIMEOUT); } @AfterEach public void tearDown() throws Exception { if (zkClient != null) { zkClient.close(); } if (zkClientB != null) { zkClientB.close(); } super.tearDown(); } private void ensureCheck(boolean enabled) { ZooKeeperServer.setEnableEagerACLCheck(enabled); } private void assertTransactionState(String operation, QuorumPeer peer, long lastxid) throws Exception { if (peer == zkLeader && peer != zkConnected) { // The operation is performed on no leader, but we are asserting on leader. // There is no happen-before between `zkLeader.getLastLoggedZxid()` and // successful response from other server. The commit and response are routed // to different servers and performed asynchronous in each server. So we have // to sync leader client to go through commit and response path in leader to // build happen-before between `zkLeader.getLastLoggedZxid()` and side effect // of previous operation. syncClient(zkLeaderClient, false); } assertTrue(peer == zkLeader || peer == zkConnected); boolean eagerACL = ZooKeeperServer.isEnableEagerACLCheck(); String assertion = String.format( "Connecting: %s Checking: %s EagerACL: %s Operation: %s", zkConnected.getPeerState(), peer.getPeerState(), eagerACL, operation); if (eagerACL) { assertEquals(lastxid, peer.getLastLoggedZxid(), assertion); } else { assertNotEquals(lastxid, peer.getLastLoggedZxid(), assertion); } } @ParameterizedTest @MethodSource("data") public void testCreateOK(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); ensureCheck(true); zkClient.create(PARENT_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zkClientB.create(CHILD_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); } @ParameterizedTest @MethodSource("data") public void testCreate2OK(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); zkClientB.create(CHILD_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); } @ParameterizedTest @MethodSource("data") public void testCreateFail(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, DATA, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); long lastxid = zkConnected.getLastLoggedZxid(); try { zkClientB.create(CHILD_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("expect no auth"); } catch (KeeperException.NoAuthException e) { } assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); assertTransactionState("failed create", zkConnected, lastxid); assertTransactionState("failed create", zkLeader, lastxid); } @ParameterizedTest @MethodSource("data") public void testCreate2Fail(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, DATA, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, null); long lastxid = zkConnected.getLastLoggedZxid(); try { zkClientB.create(CHILD_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); fail("expect no auth"); } catch (KeeperException.NoAuthException e) { } assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); assertTransactionState("failed create2", zkConnected, lastxid); assertTransactionState("failed create2", zkLeader, lastxid); } @ParameterizedTest @MethodSource("data") public void testDeleteOK(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zkClientB.delete(PARENT_PATH, -1); assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); } @ParameterizedTest @MethodSource("data") public void testDeleteFail(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, DATA, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, null); zkClient.create(CHILD_PATH, DATA, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, null); long lastxid = zkConnected.getLastLoggedZxid(); try { zkClientB.delete(CHILD_PATH, -1); fail("expect no auth"); } catch (KeeperException.NoAuthException e) { } assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); assertTransactionState("failed delete", zkConnected, lastxid); assertTransactionState("failed delete", zkLeader, lastxid); } @ParameterizedTest @MethodSource("data") public void testSetDataOK(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); zkClientB.setData(PARENT_PATH, DATA, -1); } @ParameterizedTest @MethodSource("data") public void testSetDataFail(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, null); long lastxid = zkConnected.getLastLoggedZxid(); try { zkClientB.setData(PARENT_PATH, DATA, -1); fail("expect no auth"); } catch (KeeperException.NoAuthException e) { } assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); assertTransactionState("failed setData", zkConnected, lastxid); assertTransactionState("failed setData", zkLeader, lastxid); } @ParameterizedTest @MethodSource("data") public void testSetACLOK(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); zkClientB.setACL(PARENT_PATH, Ids.READ_ACL_UNSAFE, -1); assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); } @ParameterizedTest @MethodSource("data") public void testSetACLFail(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); zkClient.create(PARENT_PATH, null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, null); long lastxid = zkConnected.getLastLoggedZxid(); try { zkClientB.setACL(PARENT_PATH, Ids.READ_ACL_UNSAFE, -1); fail("expect no auth"); } catch (KeeperException.NoAuthException ignored) { } assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); assertTransactionState("failed setACL", zkConnected, lastxid); assertTransactionState("failed setACL", zkLeader, lastxid); } @ParameterizedTest @MethodSource("data") public void testBadACL(ServerState serverState, boolean checkEnabled) throws Exception { setUp(serverState, checkEnabled); CountdownWatcher cw = new CountdownWatcher(); String addr = String.format("%s:%d", LOCALADDR, zkConnected.getClientPort()); TestableZooKeeper zk = createClient(cw, addr); cw.waitForConnected(CONNECTION_TIMEOUT); long lastxid = zkConnected.getLastLoggedZxid(); try { zk.create("/acltest", new byte[0], Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Should have received an invalid acl error"); } catch (KeeperException.InvalidACLException e) { } assertEquals(0, connectedServer.getInProcess(), "OutstandingRequests not decremented"); assertTransactionState("invalid ACL", zkConnected, lastxid); assertTransactionState("invalid ACL", zkLeader, lastxid); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032743 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/EphemeralNodeDeletionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/EphemeralNo0100644 0000000 0000000 00000022106 15051152474 034306 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.security.sasl.SaslException; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class EphemeralNodeDeletionTest extends QuorumPeerTestBase { private static int SERVER_COUNT = 3; private MainThread[] mt = new MainThread[SERVER_COUNT]; /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2355. * ZooKeeper ephemeral node is never deleted if follower fail while reading * the proposal packet. */ @Test @Timeout(value = 120) public void testEphemeralNodeDeletion() throws Exception { final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); // start all the servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new MockTestQPMain(); } }; mt[i].start(); } // ensure all servers started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } CountdownWatcher watch = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[1], ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); /** * now the problem scenario starts */ Stat firstEphemeralNode = new Stat(); // 1: create ephemeral node String nodePath = "/e1"; zk.create(nodePath, "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, firstEphemeralNode); assertEquals(zk.getSessionId(), firstEphemeralNode.getEphemeralOwner(), "Current session and ephemeral owner should be same"); // 2: inject network problem in one of the follower CustomQuorumPeer follower = (CustomQuorumPeer) getByServerState(mt, ServerState.FOLLOWING); follower.setInjectError(true); // 3: close the session so that ephemeral node is deleted zk.close(); // remove the error follower.setInjectError(false); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + follower.getClientPort(), CONNECTION_TIMEOUT), "Faulted Follower should have joined quorum by now"); QuorumPeer leader = getByServerState(mt, ServerState.LEADING); assertNotNull(leader, "Leader should not be null"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + leader.getClientPort(), CONNECTION_TIMEOUT), "Leader must be running"); watch = new CountdownWatcher(); zk = new ZooKeeper("127.0.0.1:" + leader.getClientPort(), ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); Stat exists = zk.exists(nodePath, false); assertNull(exists, "Node must have been deleted from leader"); CountdownWatcher followerWatch = new CountdownWatcher(); ZooKeeper followerZK = new ZooKeeper( "127.0.0.1:" + follower.getClientPort(), ClientBase.CONNECTION_TIMEOUT, followerWatch); followerWatch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); Stat nodeAtFollower = followerZK.exists(nodePath, false); // Problem 1: Follower had one extra ephemeral node /e1 assertNull(nodeAtFollower, "ephemeral node must not exist"); // Create the node with another session Stat currentEphemeralNode = new Stat(); zk.create(nodePath, "2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, currentEphemeralNode); // close the session and newly created ephemeral node should be deleted zk.close(); SyncCallback cb = new SyncCallback(); followerZK.sync(nodePath, cb, null); cb.sync.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); nodeAtFollower = followerZK.exists(nodePath, false); // Problem 2: Before fix, after session close the ephemeral node // was not getting deleted. But now after the fix after session close // ephemeral node is getting deleted. assertNull(nodeAtFollower, "After session close ephemeral node must be deleted"); followerZK.close(); } @AfterEach public void tearDown() { // stop all severs for (int i = 0; i < mt.length; i++) { try { mt[i].shutdown(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted while shutting it down", e); } } } private QuorumPeer getByServerState(MainThread[] mt, ServerState state) { for (int i = mt.length - 1; i >= 0; i--) { QuorumPeer quorumPeer = mt[i].getQuorumPeer(); if (null != quorumPeer && state == quorumPeer.getPeerState()) { return quorumPeer; } } return null; } static class CustomQuorumPeer extends QuorumPeer { private boolean injectError = false; public CustomQuorumPeer() throws SaslException { } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb())) { @Override void readPacket(QuorumPacket pp) throws IOException { /** * In real scenario got SocketTimeoutException while reading * the packet from leader because of network problem, but * here throwing SocketTimeoutException based on whether * error is injected or not */ super.readPacket(pp); if (injectError && pp.getType() == Leader.PROPOSAL) { String type = LearnerHandler.packetToString(pp); throw new SocketTimeoutException("Socket timeout while reading the packet for operation " + type); } } }; } public void setInjectError(boolean injectError) { this.injectError = injectError; } } static class MockTestQPMain extends TestQPMain { @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new CustomQuorumPeer(); } } private static class SyncCallback implements AsyncCallback.VoidCallback { private final CountDownLatch sync = new CountDownLatch(1); @Override public void processResult(int rc, String path, Object ctx) { sync.countDown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032755 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/EpochWriteFailureTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/EpochWriteF0100644 0000000 0000000 00000014342 15051152474 034271 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class EpochWriteFailureTest extends QuorumPeerTestBase { private static int SERVER_COUNT = 3; private static int[] clientPorts = new int[SERVER_COUNT]; private static MainThread[] mt = new MainThread[SERVER_COUNT]; private static ZooKeeper zk; /* * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2307 * Expectation: During leader election when accepted epoch write to file * fails, it should not complete leader election, also it should not update * run time values of acceptedEpoch, */ @Test @Timeout(value = 120) public void testAcceptedEpochWriteFailure() throws Exception { StringBuilder sb = new StringBuilder(); sb.append("admin.enableServer=false"); sb.append("\n"); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server); sb.append("\n"); } String currentQuorumCfgSection = sb.toString(); for (int i = 0; i < SERVER_COUNT - 1; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure two servers started for (int i = 0; i < SERVER_COUNT - 1; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } CountdownWatcher watch1 = new CountdownWatcher(); zk = new ZooKeeper("127.0.0.1:" + clientPorts[0], ClientBase.CONNECTION_TIMEOUT, watch1); watch1.waitForConnected(ClientBase.CONNECTION_TIMEOUT); String data = "originalData"; zk.create("/epochIssue", data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); //initialize third server mt[2] = new MainThread(2, clientPorts[2], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new MockTestQPMain(); } }; //This server has problem it fails while writing acceptedEpoch. mt[2].start(); /* * Verify that problematic server does not start as acceptedEpoch update * failure is injected and it keeps on trying to join the quorum */ assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[2], CONNECTION_TIMEOUT / 2), "verify server 2 not started"); QuorumPeer quorumPeer = mt[2].getQuorumPeer(); assertEquals(0, quorumPeer.getAcceptedEpoch(), "acceptedEpoch must not have changed"); assertEquals(0, quorumPeer.getCurrentEpoch(), "currentEpoch must not have changed"); } static class CustomQuorumPeer extends QuorumPeer { CustomQuorumPeer(Map quorumPeers, File snapDir, File logDir, int clientPort, int electionAlg, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit) throws IOException { super(quorumPeers, snapDir, logDir, clientPort, electionAlg, myid, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); } @Override protected void writeLongToFile(String name, long value) throws IOException { // initial epoch writing should be successful if (0 != value) { throw new IOException("Input/output error"); } } } private static class MockTestQPMain extends TestQPMain { @Override public void runFromConfig(QuorumPeerConfig config) throws IOException { quorumPeer = new CustomQuorumPeer(config.getQuorumVerifier().getAllMembers(), config.getDataDir(), config.getDataLogDir(), config.getClientPortAddress().getPort(), config.getElectionAlg(), config.getServerId(), config.getTickTime(), config.getInitLimit(), config.getSyncLimit(), config.getSyncLimit()); quorumPeer.start(); try { quorumPeer.join(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted", e); } } } @AfterAll public static void tearDownAfterClass() throws InterruptedException { for (int i = 0; i < SERVER_COUNT; i++) { if (mt[i] != null) { mt[i].shutdown(); } } if (zk != null) { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000204 15051152474 032746 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLEBackwardElectionRoundTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLEBackward0100644 0000000 0000000 00000011665 15051152474 034164 0ustar00rootroot0000000 0000000 /* Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLEBackwardElectionRoundTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLELostMessageTest.class); int count; Map peers; File[] tmpdir; int[] port; QuorumCnxManager[] cnxManagers; @BeforeEach public void setUp() throws Exception { count = 3; peers = new HashMap<>(count); tmpdir = new File[count]; port = new int[count]; cnxManagers = new QuorumCnxManager[count - 1]; } @AfterEach public void tearDown() throws Exception { for (int i = 0; i < (count - 1); i++) { if (cnxManagers[i] != null) { cnxManagers[i].halt(); } } } /** * This test is checking the following case. A server S is * currently LOOKING and it receives notifications from * a quorum indicating they are following S. The election * round E of S is higher than the election round E' in the * notification messages, so S becomes the leader and sets * its epoch back to E'. In the meanwhile, one or more * followers turn to LOOKING and elect S in election round E. * Having leader and followers with different election rounds * might prevent other servers from electing a leader because * they can't get a consistent set of notifications from a * quorum. * * https://issues.apache.org/jira/browse/ZOOKEEPER-1514 * * * @throws Exception */ @Test public void testBackwardElectionRound() throws Exception { LOG.info("TestLE: {}, {}", getTestName(), count); for (int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress(clientport), new InetSocketAddress(PortAssignment.unique()))); tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } ByteBuffer initialMsg0 = getMsg(); ByteBuffer initialMsg1 = getMsg(); /* * Start server 0 */ QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2, 2); peer.startLeaderElection(); FLETestUtils.LEThread thread = new FLETestUtils.LEThread(peer, 0); thread.start(); /* * Start mock server 1 */ QuorumPeer mockPeer = new QuorumPeer(peers, tmpdir[1], tmpdir[1], port[1], 3, 1, 1000, 2, 2, 2); cnxManagers[0] = mockPeer.createCnxnManager(); cnxManagers[0].listener.start(); cnxManagers[0].toSend(0L, initialMsg0); /* * Start mock server 2 */ mockPeer = new QuorumPeer(peers, tmpdir[2], tmpdir[2], port[2], 3, 2, 1000, 2, 2, 2); cnxManagers[1] = mockPeer.createCnxnManager(); cnxManagers[1].listener.start(); cnxManagers[1].toSend(0L, initialMsg1); /* * Run another instance of leader election. */ thread.join(5000); thread = new FLETestUtils.LEThread(peer, 0); thread.start(); /* * Send the same messages, this time should not make 0 the leader. */ cnxManagers[0].toSend(0L, initialMsg0); cnxManagers[1].toSend(0L, initialMsg1); thread.join(5000); if (!thread.isAlive()) { fail("Should not have joined"); } } private ByteBuffer getMsg() { return FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 0, 0, 1); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLELostMessageTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLELostMess0100644 0000000 0000000 00000006546 15051152474 034221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.HashMap; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLELostMessageTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLELostMessageTest.class); int count; HashMap peers; File[] tmpdir; int[] port; QuorumCnxManager cnxManager; @BeforeEach public void setUp() throws Exception { count = 3; peers = new HashMap<>(count); tmpdir = new File[count]; port = new int[count]; } @AfterEach public void tearDown() throws Exception { cnxManager.halt(); } @Test public void testLostMessage() throws Exception { LOG.info("TestLE: {}, {}", getTestName(), count); for (int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress(clientport), new InetSocketAddress(PortAssignment.unique()))); tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } /* * Start server 0 */ QuorumPeer peer = new QuorumPeer(peers, tmpdir[1], tmpdir[1], port[1], 3, 1, 1000, 2, 2, 2); peer.startLeaderElection(); FLETestUtils.LEThread thread = new FLETestUtils.LEThread(peer, 1); thread.start(); /* * Start mock server 1 */ mockServer(); thread.join(5000); if (thread.isAlive()) { fail("Threads didn't join"); } } void mockServer() throws InterruptedException, IOException { QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2, 2); cnxManager = peer.createCnxnManager(); cnxManager.listener.start(); cnxManager.toSend(1L, FLETestUtils.createMsg(ServerState.LOOKING.ordinal(), 0, 0, 0)); cnxManager.recvQueue.take(); cnxManager.toSend(1L, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), 1, 0, 0)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000213 15051152474 032746 xustar000000000 0000000 139 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLEMalformedNotificationMessageTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLEMalforme0100644 0000000 0000000 00000024056 15051152474 034206 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.HashMap; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLEMalformedNotificationMessageTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(FLEMalformedNotificationMessageTest.class); private static final byte[] CONFIG_BYTES = "my very invalid config string".getBytes(); private static final int CONFIG_BYTES_LENGTH = CONFIG_BYTES.length; int count; HashMap peers; File tmpdir[]; int port[]; QuorumCnxManager mockCnxManager; FLETestUtils.LEThread leaderElectionThread; QuorumPeer peerRunningLeaderElection; @BeforeEach public void setUp() throws Exception { count = 3; peers = new HashMap<>(count); tmpdir = new File[count]; port = new int[count]; LOG.info("FLEMalformedNotificationMessageTest: {}, {}", getTestName(), count); for (int i = 0; i < count; i++) { int clientport = PortAssignment.unique(); peers.put((long) i, new QuorumServer(i, new InetSocketAddress(clientport), new InetSocketAddress(PortAssignment.unique()))); tmpdir[i] = ClientBase.createTmpDir(); port[i] = clientport; } /* * Start server 0 */ peerRunningLeaderElection = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2, 2); peerRunningLeaderElection.startLeaderElection(); leaderElectionThread = new FLETestUtils.LEThread(peerRunningLeaderElection, 0); leaderElectionThread.start(); } @AfterEach public void tearDown() throws Exception { peerRunningLeaderElection.shutdown(); mockCnxManager.halt(); } @Test public void testTooShortPartialNotificationMessage() throws Exception { /* * Start mock server 1, send a message too short to be compatible with any protocol version * This simulates the case when only some parts of the whole message is received. */ startMockServer(1); byte requestBytes[] = new byte[12]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); requestBuffer.clear(); requestBuffer.putInt(ServerState.LOOKING.ordinal()); // state requestBuffer.putLong(0); // leader mockCnxManager.toSend(0L, requestBuffer); /* * Assert that the message receiver thread in leader election is still healthy: * we are sending valid votes and waiting for the leader election to be finished. */ sendValidNotifications(1, 0); leaderElectionThread.join(5000); if (leaderElectionThread.isAlive()) { fail("Leader election thread didn't join, something went wrong."); } } @Test public void testNotificationMessageWithNegativeConfigLength() throws Exception { /* * Start mock server 1, send a message with negative configLength field */ startMockServer(1); byte requestBytes[] = new byte[48]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); requestBuffer.clear(); requestBuffer.putInt(ServerState.LOOKING.ordinal()); // state requestBuffer.putLong(0); // leader requestBuffer.putLong(0); // zxid requestBuffer.putLong(0); // electionEpoch requestBuffer.putLong(0); // epoch requestBuffer.putInt(FastLeaderElection.Notification.CURRENTVERSION); // version requestBuffer.putInt(-123); // configData.length mockCnxManager.toSend(0L, requestBuffer); /* * Assert that the message receiver thread in leader election is still healthy: * we are sending valid votes and waiting for the leader election to be finished. */ sendValidNotifications(1, 0); leaderElectionThread.join(5000); if (leaderElectionThread.isAlive()) { fail("Leader election thread didn't join, something went wrong."); } } @Test public void testNotificationMessageWithInvalidConfigLength() throws Exception { /* * Start mock server 1, send a message with an invalid configLength field * (instead of sending CONFIG_BYTES_LENGTH, we send 10000) */ startMockServer(1); byte requestBytes[] = new byte[48 + CONFIG_BYTES_LENGTH]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); requestBuffer.clear(); requestBuffer.putInt(ServerState.LOOKING.ordinal()); // state requestBuffer.putLong(0); // leader requestBuffer.putLong(0); // zxid requestBuffer.putLong(0); // electionEpoch requestBuffer.putLong(0); // epoch requestBuffer.putInt(FastLeaderElection.Notification.CURRENTVERSION); // version requestBuffer.putInt(10000); // configData.length requestBuffer.put(CONFIG_BYTES); // configData mockCnxManager.toSend(0L, requestBuffer); /* * Assert that the message receiver thread in leader election is still healthy: * we are sending valid votes and waiting for the leader election to be finished. */ sendValidNotifications(1, 0); leaderElectionThread.join(5000); if (leaderElectionThread.isAlive()) { fail("Leader election thread didn't join, something went wrong."); } } @Test public void testNotificationMessageWithInvalidConfig() throws Exception { /* * Start mock server 1, send a message with an invalid config field * (the receiver should not be able to parse the config part of the message) */ startMockServer(1); ByteBuffer requestBuffer = FastLeaderElection.buildMsg(ServerState.LOOKING.ordinal(), 1, 0, 0, 0, CONFIG_BYTES); mockCnxManager.toSend(0L, requestBuffer); /* * Assert that the message receiver thread in leader election is still healthy: * we are sending valid votes and waiting for the leader election to be finished. */ sendValidNotifications(1, 0); leaderElectionThread.join(5000); if (leaderElectionThread.isAlive()) { fail("Leader election thread didn't join, something went wrong."); } } @Test public void testNotificationMessageWithBadProtocol() throws Exception { /* * Start mock server 1, send an invalid 30 bytes long message * (the receiver should not be able to parse the message and should skip it) * This simulates the case when only some parts of the whole message is received. */ startMockServer(1); byte requestBytes[] = new byte[30]; ByteBuffer requestBuffer = ByteBuffer.wrap(requestBytes); requestBuffer.clear(); requestBuffer.putInt(ServerState.LOOKING.ordinal()); // state requestBuffer.putLong(1); // leader requestBuffer.putLong(0); // zxid requestBuffer.putLong(0); // electionEpoch requestBuffer.putShort((short) 0); // this is the first two bytes of a proper // 8 bytes Long we should send here mockCnxManager.toSend(0L, requestBuffer); /* * Assert that the message receiver thread in leader election is still healthy: * we are sending valid votes and waiting for the leader election to be finished. */ sendValidNotifications(1, 0); leaderElectionThread.join(5000); if (leaderElectionThread.isAlive()) { fail("Leader election thread didn't join, something went wrong."); } } void startMockServer(int sid) throws IOException { QuorumPeer peer = new QuorumPeer(peers, tmpdir[sid], tmpdir[sid], port[sid], 3, sid, 1000, 2, 2, 2); mockCnxManager = peer.createCnxnManager(); mockCnxManager.listener.start(); } void sendValidNotifications(int fromSid, int toSid) throws InterruptedException { mockCnxManager.toSend((long) toSid, FLETestUtils.createMsg(ServerState.LOOKING.ordinal(), fromSid, 0, 0)); mockCnxManager.recvQueue.take(); mockCnxManager.toSend((long) toSid, FLETestUtils.createMsg(ServerState.FOLLOWING.ordinal(), toSid, 0, 0)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032754 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLEOutOfElectionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLEOutOfEle0100644 0000000 0000000 00000013414 15051152474 034122 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.server.quorum.FastLeaderElection.Notification; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test FastLeaderElection with out of election servers. */ public class FLEOutOfElectionTest { private FastLeaderElection fle; @BeforeEach public void setUp() throws Exception { File tmpdir = ClientBase.createTmpDir(); Map peers = new HashMap<>(); for (int i = 0; i < 5; i++) { peers.put(Long.valueOf(i), new QuorumServer(Long.valueOf(i), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); } QuorumPeer peer = new QuorumPeer(peers, tmpdir, tmpdir, PortAssignment.unique(), 3, 3, 1000, 2, 2, 2); fle = new FastLeaderElection(peer, peer.createCnxnManager()); } @Test public void testIgnoringZxidElectionEpoch() { Map votes = new HashMap<>(); votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 2, ServerState.FOLLOWING)); votes.put(1L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 2), 1, 2, ServerState.FOLLOWING)); votes.put(3L, new Vote(0x1, 4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING)); votes.put(4L, new Vote(0x1, 4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LEADING)); assertTrue(fle.getVoteTracker(votes, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING)).hasAllQuorums()); } @Test public void testElectionWIthDifferentVersion() { Map votes = new HashMap<>(); votes.put(0L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.FOLLOWING)); votes.put(1L, new Vote(0x1, 4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.FOLLOWING)); votes.put(3L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING)); votes.put(4L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LEADING)); assertTrue(fle.getVoteTracker(votes, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.FOLLOWING)).hasAllQuorums()); } @Test public void testLookingNormal() { Map votes = new HashMap<>(); votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); votes.put(1L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); votes.put(3L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)); votes.put(4L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LEADING)); assertTrue(fle.getVoteTracker(votes, new Vote(4L, ZxidUtils.makeZxid(2, 1), 1, 1, ServerState.LOOKING)).hasAllQuorums()); } @Test public void testLookingDiffRounds() { HashMap votes = new HashMap<>(); votes.put(0L, new Vote(4L, ZxidUtils.makeZxid(1, 1), 1, 1, ServerState.LOOKING)); votes.put(1L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LOOKING)); votes.put(3L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 3, 2, ServerState.LOOKING)); votes.put(4L, new Vote(4L, ZxidUtils.makeZxid(2, 1), 3, 2, ServerState.LEADING)); assertFalse(fle.getVoteTracker(votes, new Vote(4L, ZxidUtils.makeZxid(2, 1), 2, 2, ServerState.LOOKING)).hasAllQuorums()); } @Test public void testOutofElection() { HashMap outofelection = new HashMap<>(); outofelection.put(1L, new Vote(0x0, 5, ZxidUtils.makeZxid(15, 0), 0xa, 0x17, ServerState.FOLLOWING)); outofelection.put(2L, new Vote(0x0, 5, ZxidUtils.makeZxid(15, 0), 0xa, 0x17, ServerState.FOLLOWING)); outofelection.put(4L, new Vote(0x1, 5, ZxidUtils.makeZxid(15, 0), 0xa, 0x18, ServerState.FOLLOWING)); Vote vote = new Vote(0x1, 5, ZxidUtils.makeZxid(15, 0), 0xa, 0x18, ServerState.LEADING); outofelection.put(5L, vote); Notification n = new Notification(); n.version = vote.getVersion(); n.leader = vote.getId(); n.zxid = vote.getZxid(); n.electionEpoch = vote.getElectionEpoch(); n.state = vote.getState(); n.peerEpoch = vote.getPeerEpoch(); n.sid = 5L; // Set the logical clock to 1 on fle instance of server 3. fle.logicalclock.set(0x1); assertTrue(fle.getVoteTracker(outofelection, new Vote(n.version, n.leader, n.zxid, n.electionEpoch, n.peerEpoch, n.state)).hasAllQuorums(), "Quorum check failed"); assertTrue(fle.checkLeader(outofelection, n.leader, n.electionEpoch), "Leader check failed"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032753 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLETestUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FLETestUtil0100644 0000000 0000000 00000005315 15051152474 034216 0ustar00rootroot0000000 0000000 /* Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.nio.ByteBuffer; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLETestUtils extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLETestUtils.class); /* * Thread to run an instance of leader election for * a given quorum peer. */ static class LEThread extends Thread { private int i; private QuorumPeer peer; LEThread(QuorumPeer peer, int i) { this.i = i; this.peer = peer; LOG.info("Constructor: {}", getName()); } public void run() { try { Vote v = null; peer.setPeerState(ServerState.LOOKING); LOG.info("Going to call leader election: {}", i); v = peer.getElectionAlg().lookForLeader(); if (v == null) { fail("Thread " + i + " got a null vote"); } /* * A real zookeeper would take care of setting the current vote. Here * we do it manually. */ peer.setCurrentVote(v); LOG.info("Finished election: {}, {}", i, v.getId()); assertTrue(peer.getPeerState() == ServerState.LEADING, "State is not leading."); } catch (Exception e) { e.printStackTrace(); } LOG.info("Joining"); } } /* * Creates a leader election notification message. */ static ByteBuffer createMsg(int state, long leader, long zxid, long epoch) { return FastLeaderElection.buildMsg(state, leader, zxid, 1, epoch); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000204 15051152474 032746 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FollowerRequestProcessorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FollowerReq0100644 0000000 0000000 00000007333 15051152474 034355 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.util.PortForwarder; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ObserverMasterTestBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class FollowerRequestProcessorTest extends ObserverMasterTestBase { private PortForwarder forwarder; @Test public void testFollowerRequestProcessorSkipsLearnerRequestToNextProcessor() throws Exception { setupTestObserverServer("true"); zk.create("/testFollowerSkipNextAProcessor", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals("test", new String(zk.getData("/testFollowerSkipNextAProcessor", null, null))); assertEquals(1L, ServerMetrics.getMetrics().SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR_COUNT.get()); } @Test public void testFollowerRequestProcessorSendsLearnerRequestToNextProcessor() throws Exception { setupTestObserverServer("false"); zk.create("/testFollowerSkipNextAProcessor", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals("test", new String(zk.getData("/testFollowerSkipNextAProcessor", null, null))); assertEquals(0L, ServerMetrics.getMetrics().SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR_COUNT.get()); } private void setupTestObserverServer(String skipLearnerRequestToNextProcessor) throws Exception { System.setProperty(FollowerRequestProcessor.SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR, skipLearnerRequestToNextProcessor); // Setup Ensemble with observer master port so that observer connects with Observer master and not the leader final int OM_PROXY_PORT = PortAssignment.unique(); forwarder = setUp(OM_PROXY_PORT, true); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for server 3 being up"); // Connect with observer zookeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_OBS, ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); // Clear all service metrics collected so far ServerMetrics.getMetrics().resetAll(); } @AfterEach public void cleanup() throws Exception { System.setProperty(FollowerRequestProcessor.SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR, "false"); shutdown(); if (forwarder != null) { forwarder.shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032742 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FuzzySnapshotRelatedTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/FuzzySnapsh0100644 0000000 0000000 00000052343 15051152474 034421 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.security.sasl.SaslException; import org.apache.jute.OutputArchive; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.KeeperException.NodeExistsException; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Test cases used to catch corner cases due to fuzzy snapshot. */ public class FuzzySnapshotRelatedTest extends QuorumPeerTestBase { private static final Logger LOG = LoggerFactory.getLogger(FuzzySnapshotRelatedTest.class); MainThread[] mt = null; ZooKeeper[] zk = null; int[] clientPorts = null; int leaderId; int followerA; @BeforeEach public void setup() throws Exception { ZooKeeperServer.setDigestEnabled(true); LOG.info("Start up a 3 server quorum"); final int ENSEMBLE_SERVERS = 3; clientPorts = new int[ENSEMBLE_SERVERS]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); // start servers mt = new MainThread[ENSEMBLE_SERVERS]; zk = new ZooKeeper[ENSEMBLE_SERVERS]; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new CustomizedQPMain(); } }; mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } QuorumPeerMainTest.waitForAll(zk, States.CONNECTED); LOG.info("all servers started"); leaderId = -1; followerA = -1; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { if (mt[i].main.quorumPeer.leader != null) { leaderId = i; } else if (followerA == -1) { followerA = i; } } } @AfterEach public void tearDown() throws Exception { ZooKeeperServer.setDigestEnabled(false); if (mt != null) { for (MainThread t : mt) { t.shutdown(); } } if (zk != null) { for (ZooKeeper z : zk) { z.close(); } } } @Test public void testMultiOpConsistency() throws Exception { LOG.info("Create a parent node"); final String path = "/testMultiOpConsistency"; createEmptyNode(zk[followerA], path, CreateMode.PERSISTENT); LOG.info("Hook to catch the 2nd sub create node txn in multi-op"); CustomDataTree dt = (CustomDataTree) mt[followerA].main.quorumPeer.getZkDb().getDataTree(); final ZooKeeperServer zkServer = mt[followerA].main.quorumPeer.getActiveServer(); String node1 = path + "/1"; String node2 = path + "/2"; dt.addNodeCreateListener(node2, new NodeCreateListener() { @Override public void process(String path) { LOG.info("Take a snapshot"); try { zkServer.takeSnapshot(true); } catch (final IOException e) { // ignored as it should never reach here because of System.exit() call } } }); LOG.info("Issue a multi op to create 2 nodes"); zk[followerA].multi(Arrays.asList( Op.create(node1, node1.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create(node2, node2.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT))); LOG.info("Restart the server"); mt[followerA].shutdown(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTING); mt[followerA].start(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTED); LOG.info("Make sure the node consistent with leader"); assertEquals( new String(zk[leaderId].getData(node2, null, null)), new String(zk[followerA].getData(node2, null, null))); } /** * It's possibel during SNAP sync, the parent is serialized before the * child get deleted during sending the snapshot over. * * In which case, we need to make sure the pzxid get correctly updated * when applying the txns received. */ @Test public void testPZxidUpdatedDuringSnapSyncing() throws Exception { LOG.info("Enable force snapshot sync"); System.setProperty(LearnerHandler.FORCE_SNAP_SYNC, "true"); final String parent = "/testPZxidUpdatedWhenDeletingNonExistNode"; final String child = parent + "/child"; createEmptyNode(zk[leaderId], parent, CreateMode.PERSISTENT); createEmptyNode(zk[leaderId], child, CreateMode.EPHEMERAL); // create another child to test closeSession createEmptyNode(zk[leaderId], child + "1", CreateMode.EPHEMERAL); LOG.info("shutdown follower {}", followerA); mt[followerA].shutdown(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTING); LOG.info("Set up ZKDatabase to catch the node serializing in DataTree"); addSerializeListener(leaderId, parent, child); LOG.info("Restart follower A to trigger a SNAP sync with leader"); mt[followerA].start(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTED); LOG.info("Check and make sure the pzxid of the parent is the same on leader and follower A"); compareStat(parent, leaderId, followerA); } /** * It's possible during taking fuzzy snapshot, the parent is serialized * before the child get deleted in the fuzzy range. * * In which case, we need to make sure the pzxid get correctly updated * when replaying the txns. */ @Test public void testPZxidUpdatedWhenLoadingSnapshot() throws Exception { final String parent = "/testPZxidUpdatedDuringTakingSnapshot"; final String child = parent + "/child"; createEmptyNode(zk[followerA], parent, CreateMode.PERSISTENT); createEmptyNode(zk[followerA], child, CreateMode.EPHEMERAL); // create another child to test closeSession createEmptyNode(zk[leaderId], child + "1", CreateMode.EPHEMERAL); LOG.info("Set up ZKDatabase to catch the node serializing in DataTree"); addSerializeListener(followerA, parent, child); LOG.info("Take snapshot on follower A"); ZooKeeperServer zkServer = mt[followerA].main.quorumPeer.getActiveServer(); zkServer.takeSnapshot(true); LOG.info("Restarting follower A to load snapshot"); mt[followerA].shutdown(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CLOSED); mt[followerA].start(); // zk[followerA] will be closed in addSerializeListener, re-create it zk[followerA] = new ZooKeeper("127.0.0.1:" + clientPorts[followerA], ClientBase.CONNECTION_TIMEOUT, this); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTED); LOG.info("Check and make sure the pzxid of the parent is the same on leader and follower A"); compareStat(parent, leaderId, followerA); } @Test public void testMultiOpDigestConsistentDuringSnapshot() throws Exception { ServerMetrics.getMetrics().resetAll(); LOG.info("Create some txns"); final String path = "/testMultiOpDigestConsistentDuringSnapshot"; createEmptyNode(zk[followerA], path, CreateMode.PERSISTENT); CustomDataTree dt = (CustomDataTree) mt[followerA].main.quorumPeer.getZkDb().getDataTree(); final CountDownLatch setDataLatch = new CountDownLatch(1); final CountDownLatch continueSetDataLatch = new CountDownLatch(1); final ZooKeeper followerZk = zk[followerA]; dt.setDigestSerializeListener(new DigestSerializeListener() { @Override public void process() { LOG.info("Trigger a multi op in async"); followerZk.multi(Arrays.asList( Op.create("/multi0", "/multi0".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData(path, "new data".getBytes(), -1) ), new MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) {} }, null); LOG.info("Wait for the signal to continue"); try { setDataLatch.await(3, TimeUnit.SECONDS); } catch (Exception e) { LOG.error("Error while waiting for set data txn, {}", e); } } @Override public void finished() { LOG.info("Finished writing digest out, continue"); continueSetDataLatch.countDown(); } }); dt.setDataListener(new SetDataTxnListener() { @Override public void process() { setDataLatch.countDown(); try { continueSetDataLatch.await(3, TimeUnit.SECONDS); } catch (Exception e) { LOG.error("Error while waiting for continue signal, {}", e); } } }); LOG.info("Trigger a snapshot"); ZooKeeperServer zkServer = mt[followerA].main.quorumPeer.getActiveServer(); zkServer.takeSnapshot(true); checkNoMismatchReported(); LOG.info("Restart the server to load the snapshot again"); mt[followerA].shutdown(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTING); mt[followerA].start(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTED); LOG.info("Make sure there is nothing caught in the digest mismatch"); checkNoMismatchReported(); } private void checkNoMismatchReported() { long mismatch = (long) MetricsUtils.currentServerMetrics().get("digest_mismatches_count"); assertFalse(mismatch > 0, "The mismatch count should be zero but is: " + mismatch); } private void addSerializeListener(int sid, String parent, String child) { final ZooKeeper zkClient = zk[sid]; CustomDataTree dt = (CustomDataTree) mt[sid].main.quorumPeer.getZkDb().getDataTree(); dt.addListener(parent, new NodeSerializeListener() { @Override public void nodeSerialized(String path) { try { zkClient.delete(child, -1); zkClient.close(); LOG.info("Deleted the child node after the parent is serialized"); } catch (Exception e) { LOG.error("Error when deleting node {}", e); } } }); } private void compareStat(String path, int sid, int compareWithSid) throws Exception { ZooKeeper[] compareZk = new ZooKeeper[2]; compareZk[0] = new ZooKeeper("127.0.0.1:" + clientPorts[sid], ClientBase.CONNECTION_TIMEOUT, this); compareZk[1] = new ZooKeeper("127.0.0.1:" + clientPorts[compareWithSid], ClientBase.CONNECTION_TIMEOUT, this); QuorumPeerMainTest.waitForAll(compareZk, States.CONNECTED); try { Stat stat1 = new Stat(); compareZk[0].getData(path, null, stat1); Stat stat2 = new Stat(); compareZk[1].getData(path, null, stat2); assertEquals(stat1, stat2); } finally { for (ZooKeeper z: compareZk) { z.close(); } } } @Test public void testGlobalSessionConsistency() throws Exception { LOG.info("Hook to catch the commitSession event on followerA"); CustomizedQPMain followerAMain = (CustomizedQPMain) mt[followerA].main; final ZooKeeperServer zkServer = followerAMain.quorumPeer.getActiveServer(); // only take snapshot for the next global session we're going to create final AtomicBoolean shouldTakeSnapshot = new AtomicBoolean(true); followerAMain.setCommitSessionListener(new CommitSessionListener() { @Override public void process(long sessionId) { LOG.info("Take snapshot"); if (shouldTakeSnapshot.getAndSet(false)) { try { zkServer.takeSnapshot(true); } catch (IOException e) { // ignored as it should never reach here because of System.exit() call } } } }); LOG.info("Create a global session"); ZooKeeper globalClient = new ZooKeeper( "127.0.0.1:" + clientPorts[followerA], ClientBase.CONNECTION_TIMEOUT, this); QuorumPeerMainTest.waitForOne(globalClient, States.CONNECTED); LOG.info("Restart followerA to load the data from disk"); mt[followerA].shutdown(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTING); mt[followerA].start(); QuorumPeerMainTest.waitForOne(zk[followerA], States.CONNECTED); LOG.info("Make sure the global sessions are consistent with leader"); Map globalSessionsOnLeader = mt[leaderId].main.quorumPeer.getZkDb().getSessionWithTimeOuts(); Map globalSessionsOnFollowerA = mt[followerA].main.quorumPeer.getZkDb().getSessionWithTimeOuts(); LOG.info("sessions are {}, {}", globalSessionsOnLeader.keySet(), globalSessionsOnFollowerA.keySet()); assertTrue(globalSessionsOnFollowerA.keySet().containsAll(globalSessionsOnLeader.keySet())); } private void createEmptyNode(ZooKeeper zk, String path, CreateMode mode) throws Exception { zk.create(path, new byte[0], Ids.OPEN_ACL_UNSAFE, mode); } interface NodeCreateListener { void process(String path); } interface DigestSerializeListener { void process(); void finished(); } interface SetDataTxnListener { void process(); } static class CustomDataTree extends DataTree { Map nodeCreateListeners = new HashMap<>(); Map listeners = new HashMap<>(); DigestSerializeListener digestListener; SetDataTxnListener setListener; @Override public void serializeNodeData(OutputArchive oa, String path, DataNode node) throws IOException { super.serializeNodeData(oa, path, node); NodeSerializeListener listener = listeners.get(path); if (listener != null) { listener.nodeSerialized(path); } } public void addListener(String path, NodeSerializeListener listener) { listeners.put(path, listener); } @Override public void createNode( final String path, byte[] data, List acl, long ephemeralOwner, int parentCVersion, long zxid, long time, Stat outputStat) throws NoNodeException, NodeExistsException { NodeCreateListener listener = nodeCreateListeners.get(path); if (listener != null) { listener.process(path); } super.createNode(path, data, acl, ephemeralOwner, parentCVersion, zxid, time, outputStat); } public void addNodeCreateListener(String path, NodeCreateListener listener) { nodeCreateListeners.put(path, listener); } public void setDigestSerializeListener(DigestSerializeListener listener) { this.digestListener = listener; } public void setDataListener(SetDataTxnListener listener) { this.setListener = listener; } @Override public boolean serializeZxidDigest(OutputArchive oa) throws IOException { if (digestListener != null) { digestListener.process(); } boolean result = super.serializeZxidDigest(oa); if (digestListener != null) { digestListener.finished(); } return result; } public Stat setData(String path, byte data[], int version, long zxid, long time) throws NoNodeException { if (setListener != null) { setListener.process(); } return super.setData(path, data, version, zxid, time); } } interface NodeSerializeListener { void nodeSerialized(String path); } interface CommitSessionListener { void process(long sessionId); } static class CustomizedQPMain extends TestQPMain { CommitSessionListener commitSessionListener; public void setCommitSessionListener(CommitSessionListener listener) { this.commitSessionListener = listener; } @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new QuorumPeer() { @Override public void setZKDatabase(ZKDatabase database) { super.setZKDatabase(new ZKDatabase(this.getTxnFactory()) { @Override public DataTree createDataTree() { return new CustomDataTree(); } }); } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb()) { @Override public void createSessionTracker() { sessionTracker = new LearnerSessionTracker( this, getZKDatabase().getSessionWithTimeOuts(), this.tickTime, self.getMyId(), self.areLocalSessionsEnabled(), getZooKeeperServerListener()) { public synchronized boolean commitSession( long sessionId, int sessionTimeout) { if (commitSessionListener != null) { commitSessionListener.process(sessionId); } return super.commitSession(sessionId, sessionTimeout); } }; } }); } }; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032755 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LeaderBeanTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LeaderBeanT0100644 0000000 0000000 00000021211 15051152474 034211 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class LeaderBeanTest { private Leader leader; private LeaderBean leaderBean; private FileTxnSnapLog fileTxnSnapLog; private LeaderZooKeeperServer zks; private QuorumPeer qp; private QuorumVerifier quorumVerifierMock; public static Map getMockedPeerViews(long myId) { int clientPort = PortAssignment.unique(); Map peersView = new HashMap<>(); InetAddress clientIP = InetAddress.getLoopbackAddress(); peersView.put(Long.valueOf(myId), new QuorumServer(myId, new InetSocketAddress(clientIP, PortAssignment.unique()), new InetSocketAddress(clientIP, PortAssignment.unique()), new InetSocketAddress(clientIP, clientPort), LearnerType.PARTICIPANT)); return peersView; } @BeforeEach public void setUp() throws IOException, X509Exception { qp = new QuorumPeer(); quorumVerifierMock = mock(QuorumVerifier.class); when(quorumVerifierMock.getAllMembers()).thenReturn(getMockedPeerViews(qp.getMyId())); qp.setQuorumVerifier(quorumVerifierMock, false); File tmpDir = ClientBase.createEmptyTestDir(); fileTxnSnapLog = new FileTxnSnapLog(new File(tmpDir, "data"), new File(tmpDir, "data_txnlog")); ZKDatabase zkDb = new ZKDatabase(fileTxnSnapLog); zks = new LeaderZooKeeperServer(fileTxnSnapLog, qp, zkDb); leader = new Leader(qp, zks); leaderBean = new LeaderBean(leader, zks); } @AfterEach public void tearDown() throws IOException { fileTxnSnapLog.close(); } @Test public void testCreateServerSocketWillRecreateInetSocketAddr() { Leader spyLeader = Mockito.spy(leader); InetSocketAddress addr = new InetSocketAddress("localhost", PortAssignment.unique()); spyLeader.createServerSocket(addr, false, false); // make sure the address to be bound will be recreated with expected hostString and port Mockito.verify(spyLeader, times(1)).recreateInetSocketAddr(addr.getHostString(), addr.getPort()); } @Test public void testGetName() { assertEquals("Leader", leaderBean.getName()); } @Test public void testGetCurrentZxid() { // Arrange zks.setZxid(1); // Assert assertEquals("0x1", leaderBean.getCurrentZxid()); } @Test public void testGetElectionTimeTaken() { // Arrange qp.setElectionTimeTaken(1); // Assert assertEquals(1, leaderBean.getElectionTimeTaken()); } @Test public void testGetProposalSize() throws IOException, Leader.XidRolloverException { // Arrange Request req = createMockRequest(); // Act leader.propose(req); // Assert byte[] data = req.getSerializeData(); assertEquals(data.length, leaderBean.getLastProposalSize()); assertEquals(data.length, leaderBean.getMinProposalSize()); assertEquals(data.length, leaderBean.getMaxProposalSize()); } @Test public void testResetProposalStats() throws IOException, Leader.XidRolloverException { // Arrange int initialProposalSize = leaderBean.getLastProposalSize(); Request req = createMockRequest(); // Act leader.propose(req); // Assert assertNotEquals(initialProposalSize, leaderBean.getLastProposalSize()); leaderBean.resetProposalStatistics(); assertEquals(initialProposalSize, leaderBean.getLastProposalSize()); assertEquals(initialProposalSize, leaderBean.getMinProposalSize()); assertEquals(initialProposalSize, leaderBean.getMaxProposalSize()); } private Request createMockRequest() throws IOException { TxnHeader header = mock(TxnHeader.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputArchive oa = (OutputArchive) args[0]; oa.writeString("header", "test"); return null; } }).when(header).serialize(any(OutputArchive.class), anyString()); Record txn = mock(Record.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputArchive oa = (OutputArchive) args[0]; oa.writeString("record", "test"); return null; } }).when(txn).serialize(any(OutputArchive.class), anyString()); return new Request(1, 2, 3, header, txn, 4); } @Test public void testFollowerInfo() throws IOException { Map votingMembers = new HashMap<>(); votingMembers.put(1L, null); votingMembers.put(2L, null); votingMembers.put(3L, null); when(quorumVerifierMock.getVotingMembers()).thenReturn(votingMembers); LearnerHandler follower = mock(LearnerHandler.class); when(follower.getLearnerType()).thenReturn(LearnerType.PARTICIPANT); when(follower.toString()).thenReturn("1"); when(follower.getSid()).thenReturn(1L); leader.addLearnerHandler(follower); leader.addForwardingFollower(follower); assertEquals("1\n", leaderBean.followerInfo()); assertEquals("", leaderBean.nonVotingFollowerInfo()); LearnerHandler observer = mock(LearnerHandler.class); when(observer.getLearnerType()).thenReturn(LearnerType.OBSERVER); when(observer.toString()).thenReturn("2"); leader.addLearnerHandler(observer); assertEquals("1\n", leaderBean.followerInfo()); assertEquals("", leaderBean.nonVotingFollowerInfo()); LearnerHandler nonVotingFollower = mock(LearnerHandler.class); when(nonVotingFollower.getLearnerType()).thenReturn(LearnerType.PARTICIPANT); when(nonVotingFollower.toString()).thenReturn("5"); when(nonVotingFollower.getSid()).thenReturn(5L); leader.addLearnerHandler(nonVotingFollower); leader.addForwardingFollower(nonVotingFollower); String followerInfo = leaderBean.followerInfo(); assertTrue(followerInfo.contains("1")); assertTrue(followerInfo.contains("5")); assertEquals("5\n", leaderBean.nonVotingFollowerInfo()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000171 15051152474 032751 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LeaderMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LeaderMetri0100644 0000000 0000000 00000007223 15051152474 034307 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class LeaderMetricsTest extends ZKTestCase { CountDownLatch createdLatch; int oldLoggingFeq; private class MyWatcher implements Watcher { @Override public void process(WatchedEvent e) { createdLatch.countDown(); } } @BeforeEach public void setup() { oldLoggingFeq = Leader.getAckLoggingFrequency(); } @AfterEach public void teardown() { Leader.setAckLoggingFrequency(oldLoggingFeq); } @Test public void testLeaderMetrics() throws Exception { // set the logging frequency to one so we log the ack latency for every ack Leader.setAckLoggingFrequency(1); ServerMetrics.getMetrics().resetAll(); QuorumUtil util = new QuorumUtil(1); //creating a quorum of 3 servers util.startAll(); ZooKeeper zk = ClientBase.createZKClient(util.getConnString()); createdLatch = new CountDownLatch(1); zk.exists("/test", new MyWatcher()); zk.create("/test", new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); createdLatch.await(); Map values = MetricsUtils.currentServerMetrics(); assertEquals(2L, values.get("proposal_count")); // Quorum ack latency is per txn assertEquals(2L, values.get("cnt_quorum_ack_latency")); assertThat((long) values.get("min_quorum_ack_latency"), greaterThan(0L)); int numberOfAckServers = 0; // ack latency is per server for (int sid = 1; sid <= 3; sid++) { String metricName = "min_" + sid + "_ack_latency"; if (values.get(metricName) != null) { numberOfAckServers++; assertThat((long) values.get("min_" + sid + "_ack_latency"), greaterThanOrEqualTo(0L)); } } // at least two servers should have send ACKs assertThat(numberOfAckServers, greaterThanOrEqualTo(2)); zk.close(); util.shutdownAll(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032756 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LeaderWithObserverTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LeaderWithO0100644 0000000 0000000 00000017653 15051152474 034271 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.server.quorum.ZabUtils.createLeader; import static org.apache.zookeeper.server.quorum.ZabUtils.createQuorumPeer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.lang.reflect.Field; import java.net.InetSocketAddress; import java.util.Map; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class LeaderWithObserverTest { QuorumPeer peer; Leader leader; File tmpDir; long participantId; long observerId; @BeforeEach public void setUp() throws Exception { tmpDir = ClientBase.createTmpDir(); peer = createQuorumPeer(tmpDir); participantId = 1; Map peers = peer.getQuorumVerifier().getAllMembers(); observerId = peers.size(); leader = createLeader(tmpDir, peer); peer.leader = leader; peers.put(observerId, new QuorumPeer.QuorumServer(observerId, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), QuorumPeer.LearnerType.OBSERVER)); // these tests are serial, we can speed up InterruptedException peer.tickTime = 1; } @AfterEach public void tearDown() { leader.shutdown("end of test"); tmpDir.delete(); } @Test public void testGetEpochToPropose() throws Exception { long lastAcceptedEpoch = 5; peer.setAcceptedEpoch(5); assertEquals(0, leader.connectingFollowers.size(), "Unexpected vote in connectingFollowers"); assertTrue(leader.waitingForNewEpoch); try { // Leader asks for epoch (mocking Leader.lead behavior) // First add to connectingFollowers leader.getEpochToPropose(peer.getMyId(), lastAcceptedEpoch); } catch (InterruptedException e) { // ignore timeout } assertEquals(1, leader.connectingFollowers.size(), "Unexpected vote in connectingFollowers"); assertEquals(lastAcceptedEpoch, peer.getAcceptedEpoch(), "Leader shouldn't set new epoch until quorum of participants is in connectingFollowers"); assertTrue(leader.waitingForNewEpoch); try { // Observer asks for epoch (mocking LearnerHandler behavior) leader.getEpochToPropose(observerId, lastAcceptedEpoch); } catch (InterruptedException e) { // ignore timeout } assertEquals(1, leader.connectingFollowers.size(), "Unexpected vote in connectingFollowers"); assertEquals(lastAcceptedEpoch, peer.getAcceptedEpoch(), "Leader shouldn't set new epoch after observer asks for epoch"); assertTrue(leader.waitingForNewEpoch); try { // Now participant asks for epoch (mocking LearnerHandler behavior). Second add to connectingFollowers. // Triggers verifier.containsQuorum = true leader.getEpochToPropose(participantId, lastAcceptedEpoch); } catch (Exception e) { fail("Timed out in getEpochToPropose"); } assertEquals(2, leader.connectingFollowers.size(), "Unexpected vote in connectingFollowers"); assertEquals(lastAcceptedEpoch + 1, peer.getAcceptedEpoch(), "Leader should record next epoch"); assertFalse(leader.waitingForNewEpoch); } @Test public void testWaitForEpochAck() throws Exception { // things needed for waitForEpochAck to run (usually in leader.lead(), but we're not running leader here) leader.leaderStateSummary = new StateSummary(leader.self.getCurrentEpoch(), leader.zk.getLastProcessedZxid()); assertEquals(0, leader.electingFollowers.size(), "Unexpected vote in electingFollowers"); assertFalse(leader.electionFinished); try { // leader calls waitForEpochAck, first add to electingFollowers leader.waitForEpochAck(peer.getMyId(), new StateSummary(0, 0)); } catch (InterruptedException e) { // ignore timeout } assertEquals(1, leader.electingFollowers.size(), "Unexpected vote in electingFollowers"); assertFalse(leader.electionFinished); try { // observer calls waitForEpochAck, should fail verifier.containsQuorum leader.waitForEpochAck(observerId, new StateSummary(0, 0)); } catch (InterruptedException e) { // ignore timeout } assertEquals(1, leader.electingFollowers.size(), "Unexpected vote in electingFollowers"); assertFalse(leader.electionFinished); try { // second add to electingFollowers, verifier.containsQuorum=true, waitForEpochAck returns without exceptions leader.waitForEpochAck(participantId, new StateSummary(0, 0)); assertEquals(2, leader.electingFollowers.size(), "Unexpected vote in electingFollowers"); assertTrue(leader.electionFinished); } catch (Exception e) { fail("Timed out in waitForEpochAck"); } } @Test public void testWaitForNewLeaderAck() throws Exception { long zxid = leader.zk.getZxid(); // things needed for waitForNewLeaderAck to run (usually in leader.lead(), but we're not running leader here) Field field = Leader.Proposal.class.getDeclaredField("packet"); field.setAccessible(true); field.set(leader.newLeaderProposal, new QuorumPacket(0, zxid, null, null)); leader.newLeaderProposal.addQuorumVerifier(peer.getQuorumVerifier()); Set ackSet = leader.newLeaderProposal.qvAcksetPairs.get(0).getAckset(); assertEquals(0, ackSet.size(), "Unexpected vote in ackSet"); assertFalse(leader.quorumFormed); try { // leader calls waitForNewLeaderAck, first add to ackSet leader.waitForNewLeaderAck(peer.getMyId(), zxid); } catch (InterruptedException e) { // ignore timeout } assertEquals(1, ackSet.size(), "Unexpected vote in ackSet"); assertFalse(leader.quorumFormed); try { // observer calls waitForNewLeaderAck, should fail verifier.containsQuorum leader.waitForNewLeaderAck(observerId, zxid); } catch (InterruptedException e) { // ignore timeout } assertEquals(1, ackSet.size(), "Unexpected vote in ackSet"); assertFalse(leader.quorumFormed); try { // second add to ackSet, verifier.containsQuorum=true, waitForNewLeaderAck returns without exceptions leader.waitForNewLeaderAck(participantId, zxid); assertEquals(2, ackSet.size(), "Unexpected vote in ackSet"); assertTrue(leader.quorumFormed); } catch (Exception e) { fail("Timed out in waitForEpochAck"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032743 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerHandlerMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerHand0100644 0000000 0000000 00000011157 15051152474 034276 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.BufferedOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerMetrics; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class LearnerHandlerMetricsTest { private MockLearnerHandler learnerHandler; private long sid = 5; private volatile CountDownLatch allSentLatch = null; class MockLearnerHandler extends LearnerHandler { MockLearnerHandler(Socket socket, Leader leader) throws IOException { super(socket, null, leader); } } @BeforeEach public void setup() throws IOException { Leader leader = mock(Leader.class); when(leader.getQuorumAuthServer()).thenReturn(null); Socket socket = mock(Socket.class); when(socket.getRemoteSocketAddress()).thenReturn(new InetSocketAddress(32)); //adding 5ms artificial delay when sending each packet BinaryOutputArchive oa = mock(BinaryOutputArchive.class); doAnswer(invocationOnMock -> { Thread.sleep(5); return null; }).when(oa).writeRecord(any(QuorumPacket.class), anyString()); BufferedOutputStream bos = mock(BufferedOutputStream.class); // flush is called when all packets are sent and the queue is empty doAnswer(invocationOnMock -> { if (allSentLatch != null) { allSentLatch.countDown(); } return null; }).when(bos).flush(); learnerHandler = new MockLearnerHandler(socket, leader); learnerHandler.setOutputArchive(oa); learnerHandler.setBufferedOutput(bos); learnerHandler.sid = sid; } @Test public void testMetrics() throws InterruptedException { ServerMetrics.getMetrics().resetAll(); //adding 1001 packets in the queue, two marker packets will be added since the interval is every 1000 packets for (int i = 0; i < 1001; i++) { learnerHandler.queuePacket(new QuorumPacket()); } allSentLatch = new CountDownLatch(1); learnerHandler.startSendingPackets(); allSentLatch.await(8, TimeUnit.SECONDS); Map values = MetricsUtils.currentServerMetrics(); String sidStr = Long.toString(sid); //we record time for each marker packet and we have two marker packets assertEquals(2L, values.get("cnt_" + sidStr + "_learner_handler_qp_time_ms")); //the second marker has 1000 packets in front of it and each takes 5 ms to send so the time in queue should be //longer than 5*1000 assertThat((long) values.get("max_" + sidStr + "_learner_handler_qp_time_ms"), greaterThan(5000L)); //we send 1001 packets + 2 marker packets so the queue size is recorded 1003 times assertEquals(1003L, values.get("cnt_" + sidStr + "_learner_handler_qp_size")); //the longest queue size is recorded when we are sending the first packet assertEquals(1002L, values.get("max_" + sidStr + "_learner_handler_qp_size")); //this is when the queue is emptied assertEquals(0L, values.get("min_" + sidStr + "_learner_handler_qp_size")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerHandlerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerHand0100644 0000000 0000000 00000047655 15051152474 034312 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.BufferedInputStream; import java.io.IOException; import java.net.Socket; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.TxnLogProposalIterator; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.util.ZxidUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LearnerHandlerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(LearnerHandlerTest.class); class MockLearnerHandler extends LearnerHandler { boolean threadStarted = false; MockLearnerHandler(Socket sock, Leader leader) throws IOException { super(sock, new BufferedInputStream(sock.getInputStream()), leader); } protected void startSendingPackets() { threadStarted = true; } @Override protected boolean shouldSendMarkerPacketForLogging() { return false; } } class MockZKDatabase extends ZKDatabase { long lastProcessedZxid; ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); LinkedList committedLog = new LinkedList<>(); LinkedList txnLog = new LinkedList<>(); public MockZKDatabase(FileTxnSnapLog snapLog) { super(snapLog); } public long getDataTreeLastProcessedZxid() { return lastProcessedZxid; } public long getmaxCommittedLog() { if (!committedLog.isEmpty()) { return committedLog.getLast().getZxid(); } return 0; } public long getminCommittedLog() { if (!committedLog.isEmpty()) { return committedLog.getFirst().getZxid(); } return 0; } public List getCommittedLog() { return committedLog; } public ReentrantReadWriteLock getLogLock() { return lock; } public Iterator getProposalsFromTxnLog(long peerZxid, long limit) { if (peerZxid >= txnLog.peekFirst().getZxid()) { return txnLog.iterator(); } else { return Collections.emptyIterator(); } } public long calculateTxnLogSizeLimit() { return 1; } } private MockLearnerHandler learnerHandler; private Socket sock; // Member variables for mocking Leader private Leader leader; private long currentZxid; // Member variables for mocking ZkDatabase private MockZKDatabase db; @BeforeEach public void setUp() throws Exception { db = new MockZKDatabase(null); sock = mock(Socket.class); // Intercept when startForwarding is called leader = mock(Leader.class); when(leader.startForwarding(ArgumentMatchers.any(LearnerHandler.class), ArgumentMatchers.anyLong())).thenAnswer(new Answer() { public Long answer(InvocationOnMock invocation) { currentZxid = invocation.getArgument(1); return 0L; } }); when(leader.getZKDatabase()).thenReturn(db); learnerHandler = new MockLearnerHandler(sock, leader); } Proposal createProposal(long zxid) { QuorumPacket packet = new QuorumPacket(); packet.setZxid(zxid); packet.setType(Leader.PROPOSAL); Proposal p = new Proposal(packet); return p; } /** * Validate that queued packets contains proposal in the following orders as * a given array of zxids * * @param zxids */ public void queuedPacketMatches(long[] zxids) { int index = 0; for (QuorumPacket qp : learnerHandler.getQueuedPackets()) { if (qp.getType() == Leader.PROPOSAL) { assertZxidEquals(zxids[index++], qp.getZxid()); } } } void reset() { learnerHandler.getQueuedPackets().clear(); learnerHandler.threadStarted = false; learnerHandler.setFirstPacket(true); } /** * Check if op packet (first packet in the queue) match the expected value * @param type - type of packet * @param zxid - zxid in the op packet * @param currentZxid - last packet queued by syncFollower, * before invoking startForwarding() */ public void assertOpType(int type, long zxid, long currentZxid) { Queue packets = learnerHandler.getQueuedPackets(); assertTrue(packets.size() > 0); assertEquals(type, packets.peek().getType()); assertZxidEquals(zxid, packets.peek().getZxid()); assertZxidEquals(currentZxid, this.currentZxid); } void assertZxidEquals(long expected, long value) { assertEquals(expected, value, "Expected 0x" + Long.toHexString(expected) + " but was 0x" + Long.toHexString(value)); } /** * Test cases when leader has empty commitedLog */ @Test public void testEmptyCommittedLog() throws Exception { long peerZxid; // Peer has newer zxid peerZxid = 3; db.lastProcessedZxid = 1; db.committedLog.clear(); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send TRUNC and forward any packet starting lastProcessedZxid assertOpType(Leader.TRUNC, db.lastProcessedZxid, db.lastProcessedZxid); reset(); // Peer is already sync peerZxid = 1; db.lastProcessedZxid = 1; db.committedLog.clear(); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF and forward any packet starting lastProcessedZxid assertOpType(Leader.DIFF, db.lastProcessedZxid, db.lastProcessedZxid); assertEquals(1, learnerHandler.getQueuedPackets().size()); reset(); // Peer has 0 zxid (new machine turn up), txnlog // is disabled peerZxid = 0; db.setSnapshotSizeFactor(-1); db.lastProcessedZxid = 1; db.committedLog.clear(); // We send SNAP assertTrue(learnerHandler.syncFollower(peerZxid, leader)); assertEquals(0, learnerHandler.getQueuedPackets().size()); reset(); } /** * Test cases when leader has committedLog */ @Test public void testCommittedLog() throws Exception { long peerZxid; // Commit proposal may lag behind data tree, but it shouldn't affect // us in any case db.lastProcessedZxid = 6; db.committedLog.add(createProposal(2)); db.committedLog.add(createProposal(3)); db.committedLog.add(createProposal(5)); // Peer has zxid that we have never seen peerZxid = 4; assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send TRUNC to 3 and forward any packet starting 5 assertOpType(Leader.TRUNC, 3, 5); // DIFF + 1 proposals + 1 commit assertEquals(3, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{5}); reset(); // Peer is within committedLog range peerZxid = 2; assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF and forward any packet starting lastProcessedZxid assertOpType(Leader.DIFF, db.getmaxCommittedLog(), db.getmaxCommittedLog()); // DIFF + 2 proposals + 2 commit assertEquals(5, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{3, 5}); reset(); // Peer miss the committedLog and txnlog is disabled peerZxid = 1; db.setSnapshotSizeFactor(-1); // We send SNAP assertTrue(learnerHandler.syncFollower(peerZxid, leader)); assertEquals(0, learnerHandler.getQueuedPackets().size()); reset(); } /** * Test cases when txnlog is enabled */ @Test public void testTxnLog() throws Exception { long peerZxid; db.txnLog.add(createProposal(2)); db.txnLog.add(createProposal(3)); db.txnLog.add(createProposal(5)); db.txnLog.add(createProposal(6)); db.txnLog.add(createProposal(7)); db.txnLog.add(createProposal(8)); db.txnLog.add(createProposal(9)); db.lastProcessedZxid = 9; db.committedLog.add(createProposal(6)); db.committedLog.add(createProposal(7)); db.committedLog.add(createProposal(8)); // Peer has zxid that we have never seen peerZxid = 4; assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send TRUNC to 3 and forward any packet starting at maxCommittedLog assertOpType(Leader.TRUNC, 3, db.getmaxCommittedLog()); // DIFF + 4 proposals + 4 commit assertEquals(9, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{5, 6, 7, 8}); reset(); // Peer zxid is in txnlog range peerZxid = 3; assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF and forward any packet starting at maxCommittedLog assertOpType(Leader.DIFF, db.getmaxCommittedLog(), db.getmaxCommittedLog()); // DIFF + 4 proposals + 4 commit assertEquals(9, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{5, 6, 7, 8}); reset(); } /** * Test case verifying TxnLogProposalIterator closure. */ @Test public void testTxnLogProposalIteratorClosure() throws Exception { long peerZxid; // CommmitedLog is empty, we will use txnlog up to lastProcessZxid db = new MockZKDatabase(null) { @Override public Iterator getProposalsFromTxnLog(long peerZxid, long limit) { return TxnLogProposalIterator.EMPTY_ITERATOR; } }; db.lastProcessedZxid = 7; db.txnLog.add(createProposal(2)); db.txnLog.add(createProposal(3)); when(leader.getZKDatabase()).thenReturn(db); // Peer zxid peerZxid = 4; assertTrue(learnerHandler.syncFollower(peerZxid, leader), "Couldn't identify snapshot transfer!"); reset(); } /** * Test cases when txnlog is enabled and commitedLog is empty */ @Test public void testTxnLogOnly() throws Exception { long peerZxid; // CommmitedLog is empty, we will use txnlog up to lastProcessZxid db.lastProcessedZxid = 7; db.txnLog.add(createProposal(2)); db.txnLog.add(createProposal(3)); db.txnLog.add(createProposal(5)); db.txnLog.add(createProposal(6)); db.txnLog.add(createProposal(7)); db.txnLog.add(createProposal(8)); // Peer has zxid that we have never seen peerZxid = 4; assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send TRUNC to 3 and forward any packet starting at // lastProcessedZxid assertOpType(Leader.TRUNC, 3, db.lastProcessedZxid); // DIFF + 3 proposals + 3 commit assertEquals(7, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{5, 6, 7}); reset(); // Peer has zxid in txnlog range peerZxid = 2; assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF and forward any packet starting at lastProcessedZxid assertOpType(Leader.DIFF, db.lastProcessedZxid, db.lastProcessedZxid); // DIFF + 4 proposals + 4 commit assertEquals(9, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{3, 5, 6, 7}); reset(); // Peer miss the txnlog peerZxid = 1; assertTrue(learnerHandler.syncFollower(peerZxid, leader)); // We send snap assertEquals(0, learnerHandler.getQueuedPackets().size()); reset(); } long getZxid(long epoch, long counter) { return ZxidUtils.makeZxid(epoch, counter); } /** * Test cases with zxids that are negative long */ @Test public void testTxnLogWithNegativeZxid() throws Exception { long peerZxid; db.txnLog.add(createProposal(getZxid(0xf, 2))); db.txnLog.add(createProposal(getZxid(0xf, 3))); db.txnLog.add(createProposal(getZxid(0xf, 5))); db.txnLog.add(createProposal(getZxid(0xf, 6))); db.txnLog.add(createProposal(getZxid(0xf, 7))); db.txnLog.add(createProposal(getZxid(0xf, 8))); db.txnLog.add(createProposal(getZxid(0xf, 9))); db.lastProcessedZxid = getZxid(0xf, 9); db.committedLog.add(createProposal(getZxid(0xf, 6))); db.committedLog.add(createProposal(getZxid(0xf, 7))); db.committedLog.add(createProposal(getZxid(0xf, 8))); // Peer has zxid that we have never seen peerZxid = getZxid(0xf, 4); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send TRUNC to 3 and forward any packet starting at maxCommittedLog assertOpType(Leader.TRUNC, getZxid(0xf, 3), db.getmaxCommittedLog()); // DIFF + 4 proposals + 4 commit assertEquals(9, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{getZxid(0xf, 5), getZxid(0xf, 6), getZxid(0xf, 7), getZxid(0xf, 8)}); reset(); // Peer zxid is in txnlog range peerZxid = getZxid(0xf, 3); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF and forward any packet starting at maxCommittedLog assertOpType(Leader.DIFF, db.getmaxCommittedLog(), db.getmaxCommittedLog()); // DIFF + 4 proposals + 4 commit assertEquals(9, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{getZxid(0xf, 5), getZxid(0xf, 6), getZxid(0xf, 7), getZxid(0xf, 8)}); reset(); } /** * Test cases when peer has new-epoch zxid */ @Test public void testNewEpochZxid() throws Exception { long peerZxid; db.txnLog.add(createProposal(getZxid(0, 1))); db.txnLog.add(createProposal(getZxid(1, 1))); db.txnLog.add(createProposal(getZxid(1, 2))); // After leader election, lastProcessedZxid will point to new epoch db.lastProcessedZxid = getZxid(2, 0); db.committedLog.add(createProposal(getZxid(1, 1))); db.committedLog.add(createProposal(getZxid(1, 2))); // Peer has zxid of epoch 0 peerZxid = getZxid(0, 0); // We should get snap, we can do better here, but the main logic is // that we should never send diff if we have never seen any txn older // than peer zxid assertTrue(learnerHandler.syncFollower(peerZxid, leader)); assertEquals(0, learnerHandler.getQueuedPackets().size()); reset(); // Peer has zxid of epoch 1 peerZxid = getZxid(1, 0); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF to (1, 2) and forward any packet starting at (1, 2) assertOpType(Leader.DIFF, getZxid(1, 2), getZxid(1, 2)); // DIFF + 2 proposals + 2 commit assertEquals(5, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{getZxid(1, 1), getZxid(1, 2)}); reset(); // Peer has zxid of epoch 2, so it is already sync peerZxid = getZxid(2, 0); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF to (2, 0) and forward any packet starting at (2, 0) assertOpType(Leader.DIFF, getZxid(2, 0), getZxid(2, 0)); // DIFF only assertEquals(1, learnerHandler.getQueuedPackets().size()); reset(); } /** * Test cases when there is a duplicate txn in the committedLog. This * should never happen unless there is a bug in initialization code * but the learner should never see duplicate packets */ @Test public void testDuplicatedTxn() throws Exception { long peerZxid; db.txnLog.add(createProposal(getZxid(0, 1))); db.txnLog.add(createProposal(getZxid(1, 1))); db.txnLog.add(createProposal(getZxid(1, 2))); db.txnLog.add(createProposal(getZxid(1, 1))); db.txnLog.add(createProposal(getZxid(1, 2))); // After leader election, lastProcessedZxid will point to new epoch db.lastProcessedZxid = getZxid(2, 0); db.committedLog.add(createProposal(getZxid(1, 1))); db.committedLog.add(createProposal(getZxid(1, 2))); db.committedLog.add(createProposal(getZxid(1, 1))); db.committedLog.add(createProposal(getZxid(1, 2))); // Peer has zxid of epoch 1 peerZxid = getZxid(1, 0); assertFalse(learnerHandler.syncFollower(peerZxid, leader)); // We send DIFF to (1, 2) and forward any packet starting at (1, 2) assertOpType(Leader.DIFF, getZxid(1, 2), getZxid(1, 2)); // DIFF + 2 proposals + 2 commit assertEquals(5, learnerHandler.getQueuedPackets().size()); queuedPacketMatches(new long[]{getZxid(1, 1), getZxid(1, 2)}); reset(); } /** * Test cases when we have to TRUNC learner, but it may cross epoch boundary * so we need to send snap instead */ @Test public void testCrossEpochTrunc() throws Exception { long peerZxid; db.txnLog.add(createProposal(getZxid(1, 1))); db.txnLog.add(createProposal(getZxid(2, 1))); db.txnLog.add(createProposal(getZxid(2, 2))); db.txnLog.add(createProposal(getZxid(4, 1))); // After leader election, lastProcessedZxid will point to new epoch db.lastProcessedZxid = getZxid(6, 0); // Peer has zxid (3, 1) peerZxid = getZxid(3, 1); assertTrue(learnerHandler.syncFollower(peerZxid, leader)); assertEquals(0, learnerHandler.getQueuedPackets().size()); reset(); } /** * Test cases when the leader's disk is slow. There can be a gap * between the txnLog and the committedLog. Make sure we detect this * and send a snap instead of a diff. */ @Test public void testTxnLogGap() throws Exception { long peerZxid; db.txnLog.add(createProposal(2)); db.txnLog.add(createProposal(3)); db.txnLog.add(createProposal(4)); db.lastProcessedZxid = 8; db.committedLog.add(createProposal(7)); db.committedLog.add(createProposal(8)); // Peer zxid is in txnlog range peerZxid = 3; assertTrue(learnerHandler.syncFollower(peerZxid, leader)); reset(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerMetricsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerMetr0100644 0000000 0000000 00000012173 15051152474 034332 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.core.Is.is; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; public class LearnerMetricsTest extends QuorumPeerTestBase { private static final int SERVER_COUNT = 4; // 1 observer, 3 participants private final QuorumPeerTestBase.MainThread[] mt = new QuorumPeerTestBase.MainThread[SERVER_COUNT]; private ZooKeeper zk_client; private static boolean bakAsyncSending; @BeforeAll public static void saveAsyncSendingFlag() { bakAsyncSending = Learner.getAsyncSending(); } @AfterAll public static void resetAsyncSendingFlag() { Learner.setAsyncSending(bakAsyncSending); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testLearnerMetricsTest(boolean asyncSending) throws Exception { Learner.setAsyncSending(asyncSending); ServerMetrics.getMetrics().resetAll(); ClientBase.setupTestEnv(); final String path = "/zk-testLeanerMetrics"; final byte[] data = new byte[512]; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); int observer = 0; clientPorts[observer] = PortAssignment.unique(); sb.append("server." + observer + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":observer\n"); for (int i = 1; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); sb.append("server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + "\n"); } // start the three participants String quorumCfgSection = sb.toString(); for (int i = 1; i < SERVER_COUNT; i++) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts[i], quorumCfgSection); mt[i].start(); } // start the observer Map observerConfig = new HashMap<>(); observerConfig.put("peerType", "observer"); mt[observer] = new QuorumPeerTestBase.MainThread(observer, clientPorts[observer], quorumCfgSection, observerConfig); mt[observer].start(); // connect to the observer node and wait for CONNECTED state // (this way we make sure to wait until the leader election finished and the observer node joined as well) zk_client = new ZooKeeper("127.0.0.1:" + clientPorts[observer], ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk_client, ZooKeeper.States.CONNECTED); // creating a node zk_client.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // there are two proposals by now, one for the global client session creation, one for the create request // there are two followers, each received two PROPOSALs waitForMetric("learner_proposal_received_count", is(4L)); waitForMetric("cnt_proposal_latency", is(4L)); waitForMetric("min_proposal_latency", greaterThanOrEqualTo(0L)); // the two ACKs are processed by the leader and by each of the two followers waitForMetric("cnt_proposal_ack_creation_latency", is(6L)); waitForMetric("min_proposal_ack_creation_latency", greaterThanOrEqualTo(0L)); // two COMMITs are received by each of the two followers, and two INFORMs are received by the single observer // (the INFORM message is also counted into the "commit_received" metrics) waitForMetric("learner_commit_received_count", is(6L)); waitForMetric("cnt_commit_propagation_latency", is(6L)); waitForMetric("min_commit_propagation_latency", greaterThanOrEqualTo(0L)); } @AfterEach public void tearDown() throws Exception { zk_client.close(); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032742 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerSyncThrottlerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerSync0100644 0000000 0000000 00000014016 15051152474 034335 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LearnerSyncThrottlerTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(LearnerSyncThrottlerTest.class); @ParameterizedTest @EnumSource(LearnerSyncThrottler.SyncType.class) public void testTooManySyncsNonessential(LearnerSyncThrottler.SyncType syncType) { assertThrows(SyncThrottleException.class, () -> { LearnerSyncThrottler throttler = new LearnerSyncThrottler(5, syncType); for (int i = 0; i < 6; i++) { throttler.beginSync(false); } }); } @ParameterizedTest @EnumSource(LearnerSyncThrottler.SyncType.class) public void testTooManySyncsEssential(LearnerSyncThrottler.SyncType syncType) { assertThrows(SyncThrottleException.class, () -> { LearnerSyncThrottler throttler = new LearnerSyncThrottler(5, syncType); try { for (int i = 0; i < 6; i++) { throttler.beginSync(true); } } catch (SyncThrottleException ex) { fail("essential syncs should not be throttled"); } throttler.endSync(); throttler.beginSync(false); }); } @ParameterizedTest @EnumSource(LearnerSyncThrottler.SyncType.class) public void testNoThrottle(LearnerSyncThrottler.SyncType syncType) throws Exception { LearnerSyncThrottler throttler = new LearnerSyncThrottler(5, syncType); try { for (int i = 0; i < 6; i++) { throttler.beginSync(true); } } catch (SyncThrottleException ex) { fail("essential syncs should not be throttled"); } throttler.endSync(); for (int i = 0; i < 5; i++) { throttler.endSync(); throttler.beginSync(false); } assertTrue(true, "should get here without exception"); } @ParameterizedTest @EnumSource(LearnerSyncThrottler.SyncType.class) public void testTryWithResourceNoThrottle(LearnerSyncThrottler.SyncType syncType) throws Exception { LearnerSyncThrottler throttler = new LearnerSyncThrottler(1, syncType); for (int i = 0; i < 3; i++) { throttler.beginSync(false); try { assertEquals(1, throttler.getSyncInProgress()); } finally { throttler.endSync(); } } } @ParameterizedTest @EnumSource(LearnerSyncThrottler.SyncType.class) public void testTryWithResourceThrottle(LearnerSyncThrottler.SyncType syncType) throws Exception { LearnerSyncThrottler throttler = new LearnerSyncThrottler(1, syncType); try { throttler.beginSync(true); try { throttler.beginSync(false); fail("shouldn't be able to have both syncs open"); } catch (SyncThrottleException e) { } throttler.endSync(); } catch (SyncThrottleException e) { fail("First sync shouldn't be throttled"); } } @ParameterizedTest @EnumSource(LearnerSyncThrottler.SyncType.class) public void testParallelNoThrottle(LearnerSyncThrottler.SyncType syncType) { final int numThreads = 50; final LearnerSyncThrottler throttler = new LearnerSyncThrottler(numThreads, syncType); ExecutorService threadPool = Executors.newFixedThreadPool(numThreads); final CountDownLatch threadStartLatch = new CountDownLatch(numThreads); final CountDownLatch syncProgressLatch = new CountDownLatch(numThreads); List> results = new ArrayList<>(numThreads); for (int i = 0; i < numThreads; i++) { results.add(threadPool.submit(new Callable() { @Override public Boolean call() { threadStartLatch.countDown(); try { threadStartLatch.await(); throttler.beginSync(false); syncProgressLatch.countDown(); syncProgressLatch.await(); throttler.endSync(); } catch (Exception e) { return false; } return true; } })); } try { for (Future result : results) { assertTrue(result.get()); } } catch (Exception e) { } finally { threadPool.shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000163 15051152474 032752 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LearnerTest0100644 0000000 0000000 00000034330 15051152474 034341 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.server.ExitCode; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.TestUtils; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.TxnHeader; import org.apache.zookeeper.util.ServiceUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class LearnerTest extends ZKTestCase { private static final File testData = new File(System.getProperty("test.data.dir", "src/test/resources/data")); static class SimpleLearnerZooKeeperServer extends LearnerZooKeeperServer { Learner learner; public SimpleLearnerZooKeeperServer(FileTxnSnapLog ftsl, QuorumPeer self) throws IOException { super(ftsl, 2000, 2000, 2000, -1, new ZKDatabase(ftsl), self); } @Override public Learner getLearner() { return learner; } } static class SimpleLearner extends Learner { SimpleLearner(FileTxnSnapLog ftsl) throws IOException { self = new QuorumPeer(); zk = new SimpleLearnerZooKeeperServer(ftsl, self); ((SimpleLearnerZooKeeperServer) zk).learner = this; } } static class TestLearner extends Learner { private int passSocketConnectOnAttempt = 10; private int socketConnectAttempt = 0; private long timeMultiplier = 0; private Socket socketToBeCreated = null; private Set unreachableAddresses = emptySet(); private void setTimeMultiplier(long multiplier) { timeMultiplier = multiplier; } private void setPassConnectAttempt(int num) { passSocketConnectOnAttempt = num; } protected long nanoTime() { return socketConnectAttempt * timeMultiplier; } private int getSockConnectAttempt() { return socketConnectAttempt; } private void setSocketToBeCreated(Socket socketToBeCreated) { this.socketToBeCreated = socketToBeCreated; } private void setUnreachableAddresses(Set unreachableAddresses) { this.unreachableAddresses = unreachableAddresses; } @Override protected void sockConnect(Socket sock, InetSocketAddress addr, int timeout) throws IOException { synchronized (this) { if (++socketConnectAttempt < passSocketConnectOnAttempt || unreachableAddresses.contains(addr)) { throw new IOException("Test injected Socket.connect() error."); } } } @Override protected Socket createSocket() throws X509Exception, IOException { if (socketToBeCreated != null) { return socketToBeCreated; } return super.createSocket(); } } @AfterEach public void cleanup() { System.clearProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED); } @Test public void connectionRetryTimeoutTest() throws Exception { assertThrows(IOException.class, () -> { Learner learner = new TestLearner(); learner.self = new QuorumPeer(); learner.self.setTickTime(2000); learner.self.setInitLimit(5); learner.self.setSyncLimit(2); // this addr won't even be used since we fake the Socket.connect InetSocketAddress addr = new InetSocketAddress(1111); // we expect this to throw an IOException since we're faking socket connect errors every time learner.connectToLeader(new MultipleAddresses(addr), ""); }); } @Test public void connectionInitLimitTimeoutTest() throws Exception { TestLearner learner = new TestLearner(); learner.self = new QuorumPeer(); learner.self.setTickTime(2000); learner.self.setInitLimit(5); learner.self.setSyncLimit(2); // this addr won't even be used since we fake the Socket.connect InetSocketAddress addr = new InetSocketAddress(1111); // pretend each connect attempt takes 4000 milliseconds learner.setTimeMultiplier((long) 4000 * 1000_000); learner.setPassConnectAttempt(5); // we expect this to throw an IOException since we're faking socket connect errors every time try { learner.connectToLeader(new MultipleAddresses(addr), ""); fail("should have thrown IOException!"); } catch (IOException e) { //good, wanted to see that, let's make sure we ran out of time assertTrue(learner.nanoTime() > 2000 * 5 * 1000_000); assertEquals(3, learner.getSockConnectAttempt()); } } @Test public void shouldTryMultipleAddresses() throws Exception { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); TestLearner learner = new TestLearner(); learner.self = new QuorumPeer(); learner.self.setTickTime(2000); learner.self.setInitLimit(5); learner.self.setSyncLimit(2); // this addr won't even be used since we fake the Socket.connect InetSocketAddress addrA = new InetSocketAddress(1111); InetSocketAddress addrB = new InetSocketAddress(2222); InetSocketAddress addrC = new InetSocketAddress(3333); InetSocketAddress addrD = new InetSocketAddress(4444); // we will never pass (don't allow successful socker.connect) during this test learner.setPassConnectAttempt(100); // we expect this to throw an IOException since we're faking socket connect errors every time try { learner.connectToLeader(new MultipleAddresses(asList(addrA, addrB, addrC, addrD)), ""); fail("should have thrown IOException!"); } catch (IOException e) { //good, wanted to see the IOException, let's make sure we tried each address 5 times assertEquals(4 * 5, learner.getSockConnectAttempt()); } } @Test public void multipleAddressesSomeAreFailing() throws Exception { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); TestLearner learner = new TestLearner(); learner.self = new QuorumPeer(); learner.self.setTickTime(2000); learner.self.setInitLimit(5); learner.self.setSyncLimit(2); // these addresses won't even be used since we fake the Socket.connect InetSocketAddress addrWorking = new InetSocketAddress(1111); InetSocketAddress addrBadA = new InetSocketAddress(2222); InetSocketAddress addrBadB = new InetSocketAddress(3333); InetSocketAddress addrBadC = new InetSocketAddress(4444); // we will emulate socket connection error for each 'bad' address learner.setUnreachableAddresses(new HashSet<>(asList(addrBadA, addrBadB, addrBadC))); // all connection attempts should succeed (if it is not an unreachable address) learner.setPassConnectAttempt(0); // initialize a mock socket, created by the Learner Socket mockSocket = mock(Socket.class); when(mockSocket.isConnected()).thenReturn(true); learner.setSocketToBeCreated(mockSocket); // we expect this to not throw an IOException since there is a single working address learner.connectToLeader(new MultipleAddresses(asList(addrBadA, addrBadB, addrBadC, addrWorking)), ""); assertEquals(learner.getSocket(), mockSocket, "Learner connected to the wrong address"); } @Test public void connectToLearnerMasterLimitTest() throws Exception { TestLearner learner = new TestLearner(); learner.self = new QuorumPeer(); learner.self.setTickTime(2000); learner.self.setInitLimit(2); learner.self.setSyncLimit(2); learner.self.setConnectToLearnerMasterLimit(5); InetSocketAddress addr = new InetSocketAddress(1111); learner.setTimeMultiplier((long) 4000 * 1000_000); learner.setPassConnectAttempt(5); try { learner.connectToLeader(new MultipleAddresses(addr), ""); fail("should have thrown IOException!"); } catch (IOException e) { assertTrue(learner.nanoTime() > 2000 * 5 * 1000_000); assertEquals(3, learner.getSockConnectAttempt()); } } @Test public void syncTest() throws Exception { File tmpFile = File.createTempFile("test", ".dir", testData); tmpFile.delete(); try { FileTxnSnapLog ftsl = new FileTxnSnapLog(tmpFile, tmpFile); SimpleLearner sl = new SimpleLearner(ftsl); long startZxid = sl.zk.getLastProcessedZxid(); // Set up bogus streams ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); sl.leaderOs = BinaryOutputArchive.getArchive(new ByteArrayOutputStream()); // make streams and socket do something innocuous sl.bufferedOutput = new BufferedOutputStream(System.out); sl.sock = new Socket(); // fake messages from the server QuorumPacket qp = new QuorumPacket(Leader.SNAP, 0, null, null); oa.writeRecord(qp, null); sl.zk.getZKDatabase().serializeSnapshot(oa); oa.writeString("BenWasHere", "signature"); TxnHeader hdr = new TxnHeader(0, 0, 0, 0, ZooDefs.OpCode.create); CreateTxn txn = new CreateTxn("/foo", new byte[0], new ArrayList(), false, sl.zk.getZKDatabase().getNode("/").stat.getCversion()); ByteArrayOutputStream tbaos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(tbaos); hdr.serialize(boa, "hdr"); txn.serialize(boa, "txn"); tbaos.close(); qp = new QuorumPacket(Leader.PROPOSAL, 1, tbaos.toByteArray(), null); oa.writeRecord(qp, null); // setup the messages to be streamed to follower sl.leaderIs = BinaryInputArchive.getArchive(new ByteArrayInputStream(baos.toByteArray())); try { sl.syncWithLeader(3); } catch (EOFException e) { } sl.zk.shutdown(); sl = new SimpleLearner(ftsl); assertEquals(startZxid, sl.zk.getLastProcessedZxid()); } finally { TestUtils.deleteFileRecursively(tmpFile); } } @Test public void truncFailTest() throws Exception { final boolean[] exitProcCalled = {false}; ServiceUtils.setSystemExitProcedure(new Consumer() { @Override public void accept(Integer exitCode) { exitProcCalled[0] = true; assertThat("System.exit() was called with invalid exit code", exitCode, equalTo(ExitCode.QUORUM_PACKET_ERROR.getValue())); } }); File tmpFile = File.createTempFile("test", ".dir", testData); tmpFile.delete(); try { FileTxnSnapLog txnSnapLog = new FileTxnSnapLog(tmpFile, tmpFile); SimpleLearner sl = new SimpleLearner(txnSnapLog); long startZxid = sl.zk.getLastProcessedZxid(); // Set up bogus streams ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); sl.leaderOs = BinaryOutputArchive.getArchive(new ByteArrayOutputStream()); // make streams and socket do something innocuous sl.bufferedOutput = new BufferedOutputStream(System.out); sl.sock = new Socket(); // fake messages from the server QuorumPacket qp = new QuorumPacket(Leader.TRUNC, 0, null, null); oa.writeRecord(qp, null); // setup the messages to be streamed to follower sl.leaderIs = BinaryInputArchive.getArchive(new ByteArrayInputStream(baos.toByteArray())); try { sl.syncWithLeader(3); } catch (EOFException e) { } sl.zk.shutdown(); assertThat("System.exit() should have been called", exitProcCalled[0], is(true)); } finally { TestUtils.deleteFileRecursively(tmpFile); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000171 15051152474 032751 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LocalPeerBeanTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/LocalPeerBe0100644 0000000 0000000 00000007661 15051152474 034235 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.net.InetAddress; import java.net.InetSocketAddress; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.Test; public class LocalPeerBeanTest { /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2299 */ @Test public void testClientAddress() throws Exception { QuorumPeer quorumPeer = new QuorumPeer(); LocalPeerBean remotePeerBean = new LocalPeerBean(quorumPeer); /** * Case 1: When cnxnFactory is null */ String result = remotePeerBean.getClientAddress(); assertNotNull(result); assertEquals(0, result.length()); /** * Case 2: When only client port is configured */ ServerCnxnFactory cnxnFactory = ServerCnxnFactory.createFactory(); int clientPort = PortAssignment.unique(); InetSocketAddress address = new InetSocketAddress(clientPort); cnxnFactory.configure(address, 5, -1, false); quorumPeer.setCnxnFactory(cnxnFactory); result = remotePeerBean.getClientAddress(); String ipv4 = "0.0.0.0:" + clientPort; String ipv6 = "[0:0:0:0:0:0:0:0]:" + clientPort; assertTrue(result.equals(ipv4) || result.equals(ipv6)); // cleanup cnxnFactory.shutdown(); /** * Case 3: When both client port and client address is configured */ clientPort = PortAssignment.unique(); InetAddress clientIP = InetAddress.getLoopbackAddress(); address = new InetSocketAddress(clientIP, clientPort); cnxnFactory = ServerCnxnFactory.createFactory(); cnxnFactory.configure(address, 5, -1, false); quorumPeer.setCnxnFactory(cnxnFactory); result = remotePeerBean.getClientAddress(); String expectedResult = clientIP.getHostAddress() + ":" + clientPort; assertEquals(expectedResult, result); // cleanup cnxnFactory.shutdown(); } @Test public void testLocalPeerIsLeader() throws Exception { long localPeerId = 7; QuorumPeer peer = mock(QuorumPeer.class); when(peer.getMyId()).thenReturn(localPeerId); when(peer.isLeader(eq(localPeerId))).thenReturn(true); LocalPeerBean localPeerBean = new LocalPeerBean(peer); assertTrue(localPeerBean.isLeader()); } @Test public void testLocalPeerIsNotLeader() throws Exception { long localPeerId = 7; QuorumPeer peer = mock(QuorumPeer.class); when(peer.getMyId()).thenReturn(localPeerId); when(peer.isLeader(eq(localPeerId))).thenReturn(false); LocalPeerBean localPeerBean = new LocalPeerBean(peer); assertFalse(localPeerBean.isLeader()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000175 15051152474 032755 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/MultipleAddressesTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/MultipleAdd0100644 0000000 0000000 00000027550 15051152474 034323 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NoRouteToHostException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.collections4.CollectionUtils; import org.apache.zookeeper.PortAssignment; import org.junit.jupiter.api.Test; public class MultipleAddressesTest { public static final int PORTS_AMOUNT = 10; @Test public void testIsEmpty() { MultipleAddresses multipleAddresses = new MultipleAddresses(); assertTrue(multipleAddresses.isEmpty()); multipleAddresses.addAddress(new InetSocketAddress(22)); assertFalse(multipleAddresses.isEmpty()); } @Test public void testGetAllAddresses() { List addresses = getAddressList(); MultipleAddresses multipleAddresses = new MultipleAddresses(addresses); assertTrue(CollectionUtils.isEqualCollection(addresses, multipleAddresses.getAllAddresses())); multipleAddresses.addAddress(addresses.get(1)); assertTrue(CollectionUtils.isEqualCollection(addresses, multipleAddresses.getAllAddresses())); } @Test public void testGetAllHostStrings() { List addresses = getAddressList(); List hostStrings = getHostStrings(addresses); MultipleAddresses multipleAddresses = new MultipleAddresses(addresses); assertTrue(CollectionUtils.isEqualCollection(hostStrings, multipleAddresses.getAllHostStrings())); multipleAddresses.addAddress(addresses.get(addresses.size() - 1)); assertTrue(CollectionUtils.isEqualCollection(hostStrings, multipleAddresses.getAllHostStrings())); } @Test public void testGetAllPorts() { List ports = getPortList(); MultipleAddresses multipleAddresses = new MultipleAddresses(getAddressList(ports)); assertTrue(CollectionUtils.isEqualCollection(ports, multipleAddresses.getAllPorts())); multipleAddresses.addAddress(new InetSocketAddress("localhost", ports.get(ports.size() - 1))); assertTrue(CollectionUtils.isEqualCollection(ports, multipleAddresses.getAllPorts())); } @Test public void testGetWildcardAddresses() { List ports = getPortList(); List addresses = getAddressList(ports); MultipleAddresses multipleAddresses = new MultipleAddresses(addresses); List allAddresses = ports.stream().map(InetSocketAddress::new).collect(Collectors.toList()); assertTrue(CollectionUtils.isEqualCollection(allAddresses, multipleAddresses.getWildcardAddresses())); multipleAddresses.addAddress(new InetSocketAddress("localhost", ports.get(ports.size() - 1))); assertTrue(CollectionUtils.isEqualCollection(allAddresses, multipleAddresses.getWildcardAddresses())); } @Test public void testGetValidAddress() throws NoRouteToHostException { List addresses = getAddressList(); MultipleAddresses multipleAddresses = new MultipleAddresses(addresses); assertTrue(addresses.contains(multipleAddresses.getReachableAddress())); } @Test public void testGetValidAddressWithNotValid() { assertThrows(NoRouteToHostException.class, () -> { // IP chosen because it is reserved for documentation/examples and should be unreachable (RFC 5737) MultipleAddresses multipleAddresses = new MultipleAddresses(new InetSocketAddress("203.0.113.1", 22)); multipleAddresses.getReachableAddress(); }); } @Test public void testGetReachableOrOneWithSingleReachableAddress() { InetSocketAddress reachableAddress = new InetSocketAddress("127.0.0.1", PortAssignment.unique()); MultipleAddresses multipleAddresses = new MultipleAddresses(Collections.singletonList(reachableAddress)); InetSocketAddress actualReturnedAddress = multipleAddresses.getReachableOrOne(); assertEquals(reachableAddress, actualReturnedAddress); } @Test public void testGetReachableOrOneWithSingleUnreachableAddress() { InetSocketAddress unreachableAddress = new InetSocketAddress("unreachable.address.zookeeper.apache.com", 1234); MultipleAddresses multipleAddresses = new MultipleAddresses(Collections.singletonList(unreachableAddress)); InetSocketAddress actualReturnedAddress = multipleAddresses.getReachableOrOne(); assertEquals(unreachableAddress, actualReturnedAddress); } @Test public void testRecreateSocketAddresses() throws UnknownHostException { List searchedAddresses = Arrays.stream(InetAddress.getAllByName("google.com")) .map(addr -> new InetSocketAddress(addr, 222)).collect(Collectors.toList()); MultipleAddresses multipleAddresses = new MultipleAddresses(searchedAddresses.get(searchedAddresses.size() - 1)); List addresses = new ArrayList<>(multipleAddresses.getAllAddresses()); assertEquals(1, addresses.size()); assertEquals(searchedAddresses.get(searchedAddresses.size() - 1), addresses.get(0)); multipleAddresses.recreateSocketAddresses(); addresses = new ArrayList<>(multipleAddresses.getAllAddresses()); assertEquals(1, addresses.size()); assertEquals(searchedAddresses.get(0), addresses.get(0)); } @Test public void testRecreateSocketAddressesWithWrongAddresses() { InetSocketAddress address = new InetSocketAddress("locahost", 222); MultipleAddresses multipleAddresses = new MultipleAddresses(address); multipleAddresses.recreateSocketAddresses(); assertEquals(address, multipleAddresses.getOne()); } @Test public void testAlwaysGetReachableAddress() throws Exception{ InetSocketAddress reachableHost = new InetSocketAddress("127.0.0.1", 1234); InetSocketAddress unreachableHost1 = new InetSocketAddress("unreachable1.address.zookeeper.apache.com", 1234); InetSocketAddress unreachableHost2 = new InetSocketAddress("unreachable2.address.zookeeper.apache.com", 1234); InetSocketAddress unreachableHost3 = new InetSocketAddress("unreachable3.address.zookeeper.apache.com", 1234); MultipleAddresses multipleAddresses = new MultipleAddresses( Arrays.asList(unreachableHost1, unreachableHost2, unreachableHost3, reachableHost)); // we call the getReachableAddress() function multiple times, to make sure we // always got back a reachable address and not just a random one for (int i = 0; i < 10; i++) { assertEquals(reachableHost, multipleAddresses.getReachableAddress()); } } @Test public void testGetAllReachableAddresses() throws Exception { InetSocketAddress reachableHost1 = new InetSocketAddress("127.0.0.1", 1234); InetSocketAddress reachableHost2 = new InetSocketAddress("127.0.0.1", 2345); InetSocketAddress unreachableHost1 = new InetSocketAddress("unreachable1.address.zookeeper.apache.com", 1234); InetSocketAddress unreachableHost2 = new InetSocketAddress("unreachable2.address.zookeeper.apache.com", 1234); MultipleAddresses multipleAddresses = new MultipleAddresses( Arrays.asList(unreachableHost1, unreachableHost2, reachableHost1, reachableHost2)); Set reachableHosts = new HashSet<>(Arrays.asList(reachableHost1, reachableHost2)); assertEquals(reachableHosts, multipleAddresses.getAllReachableAddresses()); } @Test public void testGetAllReachableAddressesOrAllWhenSomeReachable() throws Exception { InetSocketAddress reachableHost1 = new InetSocketAddress("127.0.0.1", 1234); InetSocketAddress reachableHost2 = new InetSocketAddress("127.0.0.1", 2345); InetSocketAddress unreachableHost1 = new InetSocketAddress("unreachable1.address.zookeeper.apache.com", 1234); InetSocketAddress unreachableHost2 = new InetSocketAddress("unreachable2.address.zookeeper.apache.com", 1234); MultipleAddresses multipleAddresses = new MultipleAddresses( Arrays.asList(unreachableHost1, unreachableHost2, reachableHost1, reachableHost2)); Set reachableHosts = new HashSet<>(Arrays.asList(reachableHost1, reachableHost2)); assertEquals(reachableHosts, multipleAddresses.getAllReachableAddressesOrAll()); } @Test public void testGetAllReachableAddressesOrAllWhenNoneReachable() throws Exception { InetSocketAddress unreachableHost1 = new InetSocketAddress("unreachable1.address.zookeeper.apache.com", 1234); InetSocketAddress unreachableHost2 = new InetSocketAddress("unreachable2.address.zookeeper.apache.com", 1234); InetSocketAddress unreachableHost3 = new InetSocketAddress("unreachable3.address.zookeeper.apache.com", 1234); List allUnreachableAddresses = Arrays.asList(unreachableHost1, unreachableHost2, unreachableHost3); MultipleAddresses multipleAddresses = new MultipleAddresses(allUnreachableAddresses); assertEquals(new HashSet<>(allUnreachableAddresses), multipleAddresses.getAllReachableAddressesOrAll()); } @Test public void testEquals() { List addresses = getAddressList(); MultipleAddresses multipleAddresses = new MultipleAddresses(addresses); MultipleAddresses multipleAddressesEquals = new MultipleAddresses(addresses); assertEquals(multipleAddresses, multipleAddressesEquals); MultipleAddresses multipleAddressesNotEquals = new MultipleAddresses(getAddressList()); assertNotEquals(multipleAddresses, multipleAddressesNotEquals); } @Test public void testSize() { List addresses = getAddressList(); MultipleAddresses multipleAddresses = new MultipleAddresses(addresses); assertEquals(PORTS_AMOUNT, multipleAddresses.size()); } public List getPortList() { return IntStream.range(0, PORTS_AMOUNT).mapToObj(i -> PortAssignment.unique()).collect(Collectors.toList()); } public List getAddressList() { return getAddressList(getPortList()); } public List getAddressList(List ports) { return IntStream.range(0, ports.size()) .mapToObj(i -> new InetSocketAddress("127.0.0." + i, ports.get(i))).collect(Collectors.toList()); } private List getHostStrings(List addresses) { return IntStream.range(0, addresses.size()) .mapToObj(i -> "127.0.0." + i).collect(Collectors.toList()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032755 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumBeanTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumBeanT0100644 0000000 0000000 00000006054 15051152474 034315 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.junit.jupiter.api.Test; public class QuorumBeanTest { @Test public void testGetNameProperty() { QuorumPeer qpMock = mock(QuorumPeer.class); when(qpMock.getMyId()).thenReturn(1L); QuorumBean qb = new QuorumBean(qpMock); assertThat("getName property should return Bean name in the right format", qb.getName(), equalTo("ReplicatedServer_id1")); } @Test public void testIsHiddenProperty() { QuorumPeer qpMock = mock(QuorumPeer.class); QuorumBean qb = new QuorumBean(qpMock); assertThat("isHidden should return false", qb.isHidden(), equalTo(false)); } @Test public void testGetQuorumSizeProperty() { QuorumPeer qpMock = mock(QuorumPeer.class); QuorumBean qb = new QuorumBean(qpMock); when(qpMock.getQuorumSize()).thenReturn(5); assertThat("getQuorumSize property should return value of peet.getQuorumSize()", qb.getQuorumSize(), equalTo(5)); } @Test public void testSslQuorumProperty() { QuorumPeer qpMock = mock(QuorumPeer.class); QuorumBean qb = new QuorumBean(qpMock); when(qpMock.isSslQuorum()).thenReturn(true); assertThat("isSslQuorum property should return value of peer.isSslQuorum()", qb.isSslQuorum(), equalTo(true)); when(qpMock.isSslQuorum()).thenReturn(false); assertThat("isSslQuorum property should return value of peer.isSslQuorum()", qb.isSslQuorum(), equalTo(false)); } @Test public void testPortUnificationProperty() { QuorumPeer qpMock = mock(QuorumPeer.class); QuorumBean qb = new QuorumBean(qpMock); when(qpMock.shouldUsePortUnification()).thenReturn(true); assertThat("isPortUnification property should return value of peer.shouldUsePortUnification()", qb.isPortUnification(), equalTo(true)); when(qpMock.shouldUsePortUnification()).thenReturn(false); assertThat("isPortUnification property should return value of peer.shouldUsePortUnification()", qb.isPortUnification(), equalTo(false)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032756 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumCanonicalizeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumCanon0100644 0000000 0000000 00000010731 15051152474 034357 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.LinkedHashMap; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.burningwave.tools.net.DefaultHostResolver; import org.burningwave.tools.net.HostResolutionRequestInterceptor; import org.burningwave.tools.net.MappedHostResolver; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class QuorumCanonicalizeTest extends ZKTestCase { private static final InetSocketAddress SA_DONT_CARE = InetSocketAddress.createUnresolved("dont.care.invalid", 80); private static final String ZK1_ALIAS = "zookeeper.invalid"; private static final String ZK1_FQDN = "zk1.invalid"; private static final String ZK1_IP = "169.254.0.42"; private static InetAddress IA_MOCK_ZK1; @BeforeAll public static void setupDNSMocks() throws Exception { Map hostAliases = new LinkedHashMap<>(); hostAliases.put(ZK1_FQDN, ZK1_IP); hostAliases.put(ZK1_ALIAS, ZK1_IP); HostResolutionRequestInterceptor.INSTANCE.install( new MappedHostResolver(hostAliases), DefaultHostResolver.INSTANCE ); InetAddress ia = InetAddress.getByName(ZK1_FQDN); IA_MOCK_ZK1 = ia; } @AfterAll public static void clearDNSMocks() { HostResolutionRequestInterceptor.INSTANCE.uninstall(); } private static InetAddress getInetAddress(InetSocketAddress addr) { if (addr.getHostName().equals(ZK1_ALIAS) || addr.getHostName().equals(ZK1_IP)) { return IA_MOCK_ZK1; } return addr.getAddress(); }; @AfterEach public void cleanUpEnvironment() { System.clearProperty(QuorumPeer.CONFIG_KEY_KERBEROS_CANONICALIZE_HOST_NAMES); } private QuorumPeer.QuorumServer createQuorumServer(String hostName) throws ConfigException { return new QuorumPeer.QuorumServer(0, hostName + ":1234:5678", QuorumCanonicalizeTest::getInetAddress); } @Test public void testQuorumDefaultCanonicalization() throws ConfigException { QuorumPeer.QuorumServer qps = createQuorumServer(ZK1_ALIAS); assertEquals(ZK1_ALIAS, qps.hostname, "The host name has been \"changed\" (canonicalized?) despite default settings"); } @Test public void testQuorumNoCanonicalization() throws ConfigException { System.setProperty(QuorumPeer.CONFIG_KEY_KERBEROS_CANONICALIZE_HOST_NAMES, Boolean.FALSE.toString()); QuorumPeer.QuorumServer qps = createQuorumServer(ZK1_ALIAS); assertEquals(ZK1_ALIAS, qps.hostname, "The host name has been \"changed\" (canonicalized?) despite default settings"); } @Test public void testQuorumCanonicalization() throws ConfigException { System.setProperty(QuorumPeer.CONFIG_KEY_KERBEROS_CANONICALIZE_HOST_NAMES, Boolean.TRUE.toString()); QuorumPeer.QuorumServer qps = createQuorumServer(ZK1_ALIAS); assertEquals(ZK1_FQDN, qps.hostname, "The host name hasn't been correctly canonicalized"); } @Test public void testQuorumCanonicalizationFromIp() throws ConfigException { System.setProperty(QuorumPeer.CONFIG_KEY_KERBEROS_CANONICALIZE_HOST_NAMES, Boolean.TRUE.toString()); QuorumPeer.QuorumServer qps = createQuorumServer(ZK1_IP); assertEquals(ZK1_FQDN, qps.hostname, "The host name hasn't been correctly canonicalized"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000223 15051152474 032747 xustar000000000 0000000 147 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumCnxManagerSocketConnectionTimeoutTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumCnxMa0100644 0000000 0000000 00000010352 15051152474 034326 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketTimeoutException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumCnxManagerSocketConnectionTimeoutTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(QuorumCnxManagerSocketConnectionTimeoutTest.class); private QuorumUtil qu; @BeforeEach public void setUp() throws Exception { // starting a 3 node ensemble without observers qu = new QuorumUtil(1, 2); qu.startAll(); } /** * Testing an error case reported in ZOOKEEPER-3756: * * When a new leader election happens after a ZooKeeper server restarted, in Kubernetes * the rest of the servers can not initiate connection to the restarted one. But they * get SocketTimeoutException instead of immediate IOException. The Leader Election was * time-outing quicker than the socket.connect call, so we ended up with cycles of broken * leader elections. * * The fix was to make the connection initiation asynchronous, so one 'broken' connection * doesn't make the whole leader election to be blocked, even in case of SocketTimeoutException. * * @throws Exception */ @Test public void testSocketConnectionTimeoutDuringConnectingToElectionAddress() throws Exception { int leaderId = qu.getLeaderServer(); // use a custom socket factory that will cause timeout instead of connecting to the // leader election port of the current leader final InetSocketAddress leaderElectionAddress = qu.getLeaderQuorumPeer().getElectionAddress().getOne(); QuorumCnxManager.setSocketFactory(() -> new SocketStub(leaderElectionAddress)); qu.shutdown(leaderId); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + qu.getPeer(leaderId).clientPort, ClientBase.CONNECTION_TIMEOUT), "Timeout during waiting for current leader to go down"); String errorMessage = "No new leader was elected"; waitFor(errorMessage, () -> qu.leaderExists() && qu.getLeaderServer() != leaderId, 15); } final class SocketStub extends Socket { private final InetSocketAddress addressToTimeout; SocketStub(InetSocketAddress addressToTimeout) { this.addressToTimeout = addressToTimeout; } @Override public void connect(SocketAddress endpoint, int timeout) throws IOException { if (addressToTimeout.equals(endpoint)) { try { Thread.sleep(timeout); } catch (InterruptedException e) { LOG.warn("interrupted SocketStub.connect", e); } throw new SocketTimeoutException("timeout reached in SocketStub.connect()"); } super.connect(endpoint, timeout); } } @AfterEach public void tearDown() throws Exception { qu.shutdownAll(); QuorumCnxManager.setSocketFactory(QuorumCnxManager.DEFAULT_SOCKET_FACTORY); } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000170 15051152474 032750 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumDigestTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumDiges0100644 0000000 0000000 00000021533 15051152474 034356 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import mockit.Invocation; import mockit.Mock; import mockit.MockUp; import org.apache.jute.Record; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.DataTree.ProcessTxnResult; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.TxnLogDigestTest; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.metric.SimpleCounter; import org.apache.zookeeper.txn.TxnDigest; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumDigestTest extends QuorumPeerTestBase { private static final Logger LOG = LoggerFactory.getLogger(QuorumDigestTest.class); private Servers servers; private String forceSnapSyncValue; @BeforeAll public static void applyMockUps() { new DataTreeMock(); } @BeforeEach public void setup() throws Exception { forceSnapSyncValue = System.getProperty(LearnerHandler.FORCE_SNAP_SYNC); ZooKeeperServer.setDigestEnabled(true); ((SimpleCounter) ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT).reset(); servers = LaunchServers(3, 1, null); } @AfterEach public void tearDown() throws Exception { if (servers != null) { servers.shutDownAllServers(); } ZooKeeperServer.setDigestEnabled(false); System.clearProperty(LearnerHandler.FORCE_SNAP_SYNC); DataTreeMock.reset(); } /** * Check positive case without digest mismatch during diff sync. */ @Test public void testDigestMatchesDuringDiffSync() throws Exception { triggerSync(false); } /** * Check positive case without digest mismatch during snap sync. */ @Test public void testDigestMatchesDuringSnapSync() throws Exception { triggerSync(true); // have some extra txns int leader = servers.findLeader(); TxnLogDigestTest.performOperations(servers.zk[leader], "/testDigestMatchesDuringSnapSync"); assertEquals(0L, getMismatchDigestCount()); } @Test public void testDigestMatchesWithAsyncRequests() throws Exception { int leader = servers.findLeader(); final ZooKeeper client = servers.zk[leader]; final AtomicBoolean stopped = new AtomicBoolean(true); final String prefix = "/testDigestMatchesWithAsyncRequests"; // start a thread to send requests asynchronously, Thread createTrafficThread = new Thread () { @Override public void run() { int i = 0; while (!stopped.get()) { String path = prefix + "-" + i; client.create(path, path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new StringCallback() { @Override public void processResult(int rc, String path, Object ctx, String name) { // ignore the result } }, null); try { Thread.sleep(10); } catch (InterruptedException e) { /* ignore */ } } } }; createTrafficThread.start(); // shutdown a follower and observer List targets = Arrays.asList( servers.findAnyFollower(), servers.findAnyObserver()); stopServers(targets); // start the follower and observer to have a diff sync startServers(targets); // make sure there is no digest mismatch assertEquals(0L, getMismatchDigestCount()); // stop the leader targets = Arrays.asList(leader); stopServers(targets); startServers(targets); // make sure there is no digest mismatch assertEquals(0L, getMismatchDigestCount()); stopped.set(true); } /** * Check negative case by injecting txn miss during syncing. */ @Test public void testDigestMismatchesWhenTxnLost() throws Exception { // make sure there is no mismatch after all servers start up assertEquals(0L, getMismatchDigestCount()); // shutdown a follower and observer List targets = Arrays.asList( servers.findAnyFollower(), servers.findAnyObserver()); stopServers(targets); int leader = servers.findLeader(); triggerOps(leader, "/p1"); assertEquals(0L, getMismatchDigestCount()); DataTreeMock.skipTxnZxid = "100000006"; // start the follower and observer to have a diff sync startServers(targets); long misMatchCount = getMismatchDigestCount(); assertNotEquals(0L, misMatchCount); triggerOps(leader, "/p2"); assertNotEquals(misMatchCount, getMismatchDigestCount()); } private void stopServers(List sids) throws InterruptedException { for (int sid : sids) { if (sid != -1) { servers.mt[sid].shutdown(); waitForOne(servers.zk[sid], States.CONNECTING); } } } private void startServers(List sids) throws InterruptedException { for (int sid : sids) { servers.mt[sid].start(); waitForOne(servers.zk[sid], States.CONNECTED); } } private void triggerOps(int sid, String prefix) throws Exception { TxnLogDigestTest.performOperations(servers.zk[sid], prefix); servers.restartClient(sid, null); waitForOne(servers.zk[sid], States.CONNECTED); } private void triggerSync(boolean snapSync) throws Exception { if (snapSync) { System.setProperty(LearnerHandler.FORCE_SNAP_SYNC, "true"); } // make sure there is no mismatch after all servers start up assertEquals(0L, getMismatchDigestCount()); int leader = servers.findLeader(); triggerOps(leader, "/p1"); assertEquals(0L, getMismatchDigestCount()); // shutdown a follower and observer List targets = Arrays.asList( servers.findAnyFollower(), servers.findAnyObserver()); stopServers(targets); // do some extra writes triggerOps(leader, "/p2"); // start the follower and observer to have a diff sync startServers(targets); assertEquals(0L, getMismatchDigestCount()); } public static long getMismatchDigestCount() { return ((SimpleCounter) ServerMetrics.getMetrics().DIGEST_MISMATCHES_COUNT).get(); } public static final class DataTreeMock extends MockUp { static String skipTxnZxid = ""; @Mock public ProcessTxnResult processTxn(Invocation invocation, TxnHeader header, Record txn, TxnDigest digest) { if (header != null && Long.toHexString(header.getZxid()).equals(skipTxnZxid)) { LOG.info("skip process txn {}", header.getZxid()); ProcessTxnResult rc = new ProcessTxnResult(); rc.path = ""; rc.stat = new Stat(); rc.multiResult = new ArrayList<>(); return rc; } return invocation.proceed(header, txn, digest); } public static void reset() { skipTxnZxid = ""; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032754 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerConfigTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerC0100644 0000000 0000000 00000021226 15051152474 034320 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Properties; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.junit.jupiter.api.Test; public class QuorumPeerConfigTest { /** * test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2264 */ @Test public void testErrorMessageWhensecureClientPortNotSetButsecureClientPortAddressSet() throws IOException, ConfigException { QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); try { Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("secureClientPortAddress", "localhost"); quorumPeerConfig.parseProperties(zkProp); fail("IllegalArgumentException is expected"); } catch (IllegalArgumentException e) { String expectedMessage = "secureClientPortAddress is set but secureClientPort is not set"; assertEquals(expectedMessage, e.getMessage()); } } /** * * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2264 */ @Test public void testErrorMessageWhenclientPortNotSetButclientPortAddressSet() throws IOException, ConfigException { QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); try { Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("clientPortAddress", "localhost"); quorumPeerConfig.parseProperties(zkProp); fail("IllegalArgumentException is expected"); } catch (IllegalArgumentException e) { String expectedMessage = "clientPortAddress is set but clientPort is not set"; assertEquals(expectedMessage, e.getMessage()); } } /** * https://issues.apache.org/jira/browse/ZOOKEEPER-2297 */ @Test public void testConfigureSSLAuthGetsConfiguredIfSecurePortConfigured() throws IOException, ConfigException { String sslAuthProp = "zookeeper.authProvider.x509"; QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("secureClientPort", "12345"); quorumPeerConfig.parseProperties(zkProp); String expected = "org.apache.zookeeper.server.auth.X509AuthenticationProvider"; String result = System.getProperty(sslAuthProp); assertEquals(expected, result); } /** * https://issues.apache.org/jira/browse/ZOOKEEPER-2297 */ @Test public void testCustomSSLAuth() throws IOException { try (ClientX509Util x509Util = new ClientX509Util()) { System.setProperty(x509Util.getSslAuthProviderProperty(), "y509"); QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); try { Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("secureClientPort", "12345"); quorumPeerConfig.parseProperties(zkProp); fail("ConfigException is expected"); } catch (ConfigException e) { assertNotNull(e.getMessage()); } } } /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2873 */ @Test public void testSamePortConfiguredForClientAndElection() { assertThrows(ConfigException.class, () -> { QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("server.1", "localhost:2888:2888"); quorumPeerConfig.parseProperties(zkProp); }); } /** * Extend the existing QuorumPeerConfig to set the server id. */ public static class MockQuorumPeerConfig extends QuorumPeerConfig { public MockQuorumPeerConfig(long serverId) { this.serverId = serverId; } } /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2847 */ @Test public void testClientAddrFromClientPort() throws IOException, ConfigException { long serverId = 1; QuorumPeerConfig quorumPeerConfig = new MockQuorumPeerConfig(serverId); Properties zkProp = getDefaultZKProperties(); int clientPort = 12345; zkProp.setProperty("clientPort", Integer.toString(clientPort)); zkProp.setProperty("server.1", "127.0.0.1:2889:3889:participant"); quorumPeerConfig.parseProperties(zkProp); QuorumServer qs = quorumPeerConfig.getQuorumVerifier().getAllMembers().get(serverId); InetSocketAddress expectedAddress = new InetSocketAddress("0.0.0.0", clientPort); assertEquals(expectedAddress, quorumPeerConfig.getClientPortAddress()); assertEquals(quorumPeerConfig.getClientPortAddress(), qs.clientAddr); } @Test public void testJvmPauseMonitorConfigured() throws IOException, ConfigException { final Long sleepTime = 444L; final Long warnTH = 5555L; final Long infoTH = 555L; QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("dataDir", new File("myDataDir").getAbsolutePath()); zkProp.setProperty("jvm.pause.monitor", "true"); zkProp.setProperty("jvm.pause.sleep.time.ms", sleepTime.toString()); zkProp.setProperty("jvm.pause.warn-threshold.ms", warnTH.toString()); zkProp.setProperty("jvm.pause.info-threshold.ms", infoTH.toString()); quorumPeerConfig.parseProperties(zkProp); assertEquals(sleepTime, Long.valueOf(quorumPeerConfig.getJvmPauseSleepTimeMs())); assertEquals(warnTH, Long.valueOf(quorumPeerConfig.getJvmPauseWarnThresholdMs())); assertEquals(infoTH, Long.valueOf(quorumPeerConfig.getJvmPauseInfoThresholdMs())); assertTrue(quorumPeerConfig.isJvmPauseMonitorToRun()); } /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-3721 */ @Test public void testParseBoolean() throws IOException, ConfigException { QuorumPeerConfig quorumPeerConfig = new QuorumPeerConfig(); Properties zkProp = getDefaultZKProperties(); zkProp.setProperty("localSessionsEnabled", "true"); quorumPeerConfig.parseProperties(zkProp); assertEquals(true, quorumPeerConfig.areLocalSessionsEnabled()); zkProp.setProperty("localSessionsEnabled", "false"); quorumPeerConfig.parseProperties(zkProp); assertEquals(false, quorumPeerConfig.areLocalSessionsEnabled()); zkProp.setProperty("localSessionsEnabled", "True"); quorumPeerConfig.parseProperties(zkProp); assertEquals(true, quorumPeerConfig.areLocalSessionsEnabled()); zkProp.setProperty("localSessionsEnabled", "False"); quorumPeerConfig.parseProperties(zkProp); assertEquals(false, quorumPeerConfig.areLocalSessionsEnabled()); zkProp.setProperty("localSessionsEnabled", "yes"); try { quorumPeerConfig.parseProperties(zkProp); fail("Must throw exception as 'yes' is not accpetable for parseBoolean!"); } catch (ConfigException e) { // expected } } private Properties getDefaultZKProperties() { Properties zkProp = new Properties(); zkProp.setProperty("dataDir", new File("myDataDir").getAbsolutePath()); zkProp.setProperty("oraclePath", new File("mastership").getAbsolutePath()); return zkProp; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000206 15051152474 032750 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerMainMultiAddressTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerM0100644 0000000 0000000 00000033755 15051152474 034344 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Arrays; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class QuorumPeerMainMultiAddressTest extends QuorumPeerTestBase { private static final int FIRST_SERVER = 0; private static final int SECOND_SERVER = 1; private static final int THIRD_SERVER = 2; private static final int FIRST_ADDRESS = 0; private static final int SECOND_ADDRESS = 1; private static final String UNREACHABLE_HOST = "invalid.hostname.unreachable.com"; private static final String IPV6_LOCALHOST = "[0:0:0:0:0:0:0:1]"; // IPv4 by default, change to IPV6_LOCALHOST to test with servers binding to IPv6 private String hostName = "127.0.0.1"; private int zNodeId = 0; @BeforeEach public void setUp() throws Exception { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); ClientBase.setupTestEnv(); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); QuorumPeerConfig.setReconfigEnabled(true); // just to get rid of the unrelated 'InstanceAlreadyExistsException' in the logs System.setProperty("zookeeper.jmx.log4j.disable", "true"); } @AfterEach public void tearDown() throws Exception { super.tearDown(); System.clearProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED); System.clearProperty("zookeeper.jmx.log4j.disable"); } @Test public void shouldStartClusterWithMultipleAddresses() throws Exception { // we have three ZK servers, each server has two quorumPort and two electionPort registered QuorumServerConfigBuilder quorumConfig = new QuorumServerConfigBuilder(hostName, 3, 2); // we launch the three servers, each server having the same configuration QuorumServerConfigBuilder builderForServer1 = new QuorumServerConfigBuilder(quorumConfig); QuorumServerConfigBuilder builderForServer2 = new QuorumServerConfigBuilder(quorumConfig); QuorumServerConfigBuilder builderForServer3 = new QuorumServerConfigBuilder(quorumConfig); launchServers(Arrays.asList(builderForServer1, builderForServer2, builderForServer3)); checkIfZooKeeperQuorumWorks(quorumConfig); } @Test public void shouldStartClusterWithMultipleAddresses_IPv6() throws Exception { hostName = IPV6_LOCALHOST; shouldStartClusterWithMultipleAddresses(); } @Test public void shouldStartClusterWhenSomeAddressesAreUnreachable() throws Exception { // we have three ZK servers, each server has two quorumPort and two electionPort registered // in the config we misconfigure one of the addresses for each servers QuorumServerConfigBuilder quorumConfig = new QuorumServerConfigBuilder(hostName, 3, 2) .changeHostName(FIRST_SERVER, SECOND_ADDRESS, UNREACHABLE_HOST) .changeHostName(SECOND_SERVER, SECOND_ADDRESS, UNREACHABLE_HOST) .changeHostName(THIRD_SERVER, SECOND_ADDRESS, UNREACHABLE_HOST); // we prepare the same initial config for all the three servers QuorumServerConfigBuilder builderForServer1 = new QuorumServerConfigBuilder(quorumConfig); QuorumServerConfigBuilder builderForServer2 = new QuorumServerConfigBuilder(quorumConfig); QuorumServerConfigBuilder builderForServer3 = new QuorumServerConfigBuilder(quorumConfig); // we test here: // - if the Leader can bind to the correct address and not die with BindException or // SocketException for trying to bind to a wrong address / port // - if the ZK server can 'select' the correct address to connect when trying to form a quorum // with the other servers launchServers(Arrays.asList(builderForServer1, builderForServer2, builderForServer3)); checkIfZooKeeperQuorumWorks(quorumConfig); } @Test public void shouldStartClusterWhenSomeAddressesAreUnreachable_IPv6() throws Exception { hostName = IPV6_LOCALHOST; shouldStartClusterWhenSomeAddressesAreUnreachable(); } @Test public void shouldReconfigIncrementallyByAddingMoreAddresses() throws Exception { // we have three ZK servers, each server has two quorumPort and two electionPort registered QuorumServerConfigBuilder initialQuorumConfig = new QuorumServerConfigBuilder(hostName, 3, 2); // we launch the three servers, each server should use the same initial config launchServers(Arrays.asList(initialQuorumConfig, initialQuorumConfig, initialQuorumConfig)); checkIfZooKeeperQuorumWorks(initialQuorumConfig); // we create a new config where we add a new address to each server with random available ports QuorumServerConfigBuilder newQuorumConfig = new QuorumServerConfigBuilder(initialQuorumConfig) .addNewServerAddress(FIRST_SERVER); ZooKeeperAdmin zkAdmin = newZooKeeperAdmin(initialQuorumConfig); // initiating a new incremental reconfig, by using the updated ports ReconfigTest.reconfig(zkAdmin, newQuorumConfig.buildAsStringList(), null, null, -1); checkIfZooKeeperQuorumWorks(newQuorumConfig); } @Test public void shouldReconfigIncrementallyByDeletingSomeAddresses() throws Exception { // we have three ZK servers, each server has three quorumPort and three electionPort registered QuorumServerConfigBuilder initialQuorumConfig = new QuorumServerConfigBuilder(hostName, 3, 3); // we launch the three servers, each server should use the same initial config launchServers(Arrays.asList(initialQuorumConfig, initialQuorumConfig, initialQuorumConfig)); checkIfZooKeeperQuorumWorks(initialQuorumConfig); // we create a new config where we delete a few address from each server QuorumServerConfigBuilder newQuorumConfig = new QuorumServerConfigBuilder(initialQuorumConfig) .deleteLastServerAddress(FIRST_SERVER) .deleteLastServerAddress(SECOND_SERVER) .deleteLastServerAddress(SECOND_SERVER) .deleteLastServerAddress(THIRD_SERVER); ZooKeeperAdmin zkAdmin = newZooKeeperAdmin(initialQuorumConfig); // initiating a new incremental reconfig, by using the updated ports ReconfigTest.reconfig(zkAdmin, newQuorumConfig.buildAsStringList(), null, null, -1); checkIfZooKeeperQuorumWorks(newQuorumConfig); } @Test public void shouldReconfigNonIncrementally() throws Exception { // we have three ZK servers, each server has two quorumPort and two electionPort registered QuorumServerConfigBuilder initialQuorumConfig = new QuorumServerConfigBuilder(hostName, 3, 2); // we launch the three servers, each server should use the same initial config launchServers(Arrays.asList(initialQuorumConfig, initialQuorumConfig, initialQuorumConfig)); checkIfZooKeeperQuorumWorks(initialQuorumConfig); // we create a new config where we delete and add a few address for each server QuorumServerConfigBuilder newQuorumConfig = new QuorumServerConfigBuilder(initialQuorumConfig) .deleteLastServerAddress(FIRST_SERVER) .deleteLastServerAddress(SECOND_SERVER) .deleteLastServerAddress(SECOND_SERVER) .deleteLastServerAddress(THIRD_SERVER) .addNewServerAddress(SECOND_SERVER) .addNewServerAddress(THIRD_SERVER); ZooKeeperAdmin zkAdmin = newZooKeeperAdmin(initialQuorumConfig); // initiating a new non-incremental reconfig, by using the updated ports ReconfigTest.reconfig(zkAdmin, null, null, newQuorumConfig.buildAsStringList(), -1); checkIfZooKeeperQuorumWorks(newQuorumConfig); } @Test public void shouldReconfigIncrementally_IPv6() throws Exception { hostName = IPV6_LOCALHOST; // we have three ZK servers, each server has two quorumPort and two electionPort registered QuorumServerConfigBuilder initialQuorumConfig = new QuorumServerConfigBuilder(hostName, 3, 2); // we launch the three servers, each server should use the same initial config launchServers(Arrays.asList(initialQuorumConfig, initialQuorumConfig, initialQuorumConfig)); checkIfZooKeeperQuorumWorks(initialQuorumConfig); // we create a new config where we delete and add a few address for each server QuorumServerConfigBuilder newQuorumConfig = new QuorumServerConfigBuilder(initialQuorumConfig) .deleteLastServerAddress(FIRST_SERVER) .deleteLastServerAddress(SECOND_SERVER) .deleteLastServerAddress(SECOND_SERVER) .deleteLastServerAddress(THIRD_SERVER) .addNewServerAddress(SECOND_SERVER) .addNewServerAddress(THIRD_SERVER); ZooKeeperAdmin zkAdmin = newZooKeeperAdmin(initialQuorumConfig); // initiating a new incremental reconfig, by using the updated ports ReconfigTest.reconfig(zkAdmin, newQuorumConfig.buildAsStringList(), null, null, -1); checkIfZooKeeperQuorumWorks(newQuorumConfig); } @Test public void shouldFailToReconfigWithMultipleAddressesWhenFeatureIsDisabled() throws Exception { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "false"); // we have three ZK servers, each server has a single quorumPort and single electionPort registered QuorumServerConfigBuilder initialQuorumConfig = new QuorumServerConfigBuilder(hostName, 3, 1); // we launch the three servers, each server should use the same initial config launchServers(Arrays.asList(initialQuorumConfig, initialQuorumConfig, initialQuorumConfig)); checkIfZooKeeperQuorumWorks(initialQuorumConfig); // we create a new config where we add a new address to one of the servers with random available ports QuorumServerConfigBuilder newQuorumConfig = new QuorumServerConfigBuilder(initialQuorumConfig) .addNewServerAddress(FIRST_SERVER); ZooKeeperAdmin zkAdmin = newZooKeeperAdmin(initialQuorumConfig); // initiating a new incremental reconfig by using the updated ports, expecting exceptions here try { ReconfigTest.reconfig(zkAdmin, newQuorumConfig.buildAsStringList(), null, null, -1); fail("Reconfig succeeded with multiple addresses without exception when the MultiAddress feature is disabled"); } catch (KeeperException.BadArgumentsException e) { // do nothing, this is what we expected } catch (Exception e) { fail("Reconfig failed in a wrong way. We expected KeeperException.BadArgumentsException."); } } private void launchServers(List builders) throws IOException, InterruptedException { numServers = builders.size(); servers = new Servers(); servers.clientPorts = new int[numServers]; servers.mt = new MainThread[numServers]; servers.zk = new ZooKeeper[numServers]; for (int i = 0; i < numServers; i++) { QuorumServerConfigBuilder quorumServerConfigBuilder = builders.get(i); String quorumCfgSection = quorumServerConfigBuilder.build(); LOG.info(String.format("starting server %d with quorum config:\n%s", i, quorumCfgSection)); servers.clientPorts[i] = quorumServerConfigBuilder.getClientPort(i); servers.mt[i] = new MainThread(i, servers.clientPorts[i], quorumCfgSection); servers.mt[i].start(); servers.restartClient(i, this); } waitForAll(servers, ZooKeeper.States.CONNECTED); for (int i = 0; i < numServers; i++) { servers.zk[i].close(5000); } } private void checkIfZooKeeperQuorumWorks(QuorumServerConfigBuilder builder) throws IOException, InterruptedException, KeeperException { LOG.info("starting to verify if Quorum works"); zNodeId += 1; String zNodePath = "/foo_" + zNodeId; ZooKeeper zk = connectToZkServer(builder, FIRST_SERVER); zk.create(zNodePath, "foobar1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData(zNodePath, null, null)), "foobar1"); zk.close(1000); zk = connectToZkServer(builder, SECOND_SERVER); assertEquals(new String(zk.getData(zNodePath, null, null)), "foobar1"); zk.close(1000); zk = connectToZkServer(builder, THIRD_SERVER); assertEquals(new String(zk.getData(zNodePath, null, null)), "foobar1"); zk.close(1000); LOG.info("Quorum verification finished successfully"); } private ZooKeeper connectToZkServer(QuorumServerConfigBuilder builder, int serverId) throws IOException, InterruptedException { QuorumServerConfigBuilder.ServerAddress server = builder.getServerAddress(serverId, FIRST_ADDRESS); int clientPort = builder.getClientPort(serverId); ZooKeeper zk = new ZooKeeper(server.getHost() + ":" + clientPort, ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, ZooKeeper.States.CONNECTED); return zk; } private ZooKeeperAdmin newZooKeeperAdmin( QuorumServerConfigBuilder quorumConfig) throws IOException { ZooKeeperAdmin zkAdmin = new ZooKeeperAdmin( hostName + ":" + quorumConfig.getClientPort(FIRST_SERVER), ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdmin.addAuthInfo("digest", "super:test".getBytes()); return zkAdmin; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerMainTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerM0100644 0000000 0000000 00000230611 15051152474 034332 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.apache.zookeeper.test.ClientBase.createEmptyTestDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Pattern; import javax.security.sasl.SaslException; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.metrics.BaseTestMetricsProvider; import org.apache.zookeeper.metrics.impl.NullMetricsProvider; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.LoggerTestTool; import org.junit.jupiter.api.Test; /** * Test stand-alone server. * */ public class QuorumPeerMainTest extends QuorumPeerTestBase { /** * Verify the ability to start a cluster. */ public void testQuorumInternal(String addr) throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String server1 = String.format("server.1=%1$s:%2$s:%3$s;%4$s", addr, PortAssignment.unique(), PortAssignment.unique(), CLIENT_PORT_QP1); String server2 = String.format("server.2=%1$s:%2$s:%3$s;%4$s", addr, PortAssignment.unique(), PortAssignment.unique(), CLIENT_PORT_QP2); String quorumCfgSection = server1 + "\n" + server2; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp(addr + ":" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp(addr + ":" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); QuorumPeer quorumPeer = q1.main.quorumPeer; int tickTime = quorumPeer.getTickTime(); assertEquals(tickTime * 2, quorumPeer.getMinSessionTimeout(), "Default value of minimumSessionTimeOut is not considered"); assertEquals(tickTime * 20, quorumPeer.getMaxSessionTimeout(), "Default value of maximumSessionTimeOut is not considered"); ZooKeeper zk = new ZooKeeper(addr + ":" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); zk.create("/foo_q1", "foobar1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo_q1", null, null)), "foobar1"); zk.close(); zk = new ZooKeeper(addr + ":" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); zk.create("/foo_q2", "foobar2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo_q2", null, null)), "foobar2"); zk.close(); q1.shutdown(); q2.shutdown(); assertTrue(ClientBase.waitForServerDown(addr + ":" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown(addr + ":" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); } /** * Verify the ability to start a cluster. */ @Test public void testQuorum() throws Exception { testQuorumInternal("127.0.0.1"); } /** * Verify the ability to start a cluster. IN V6!!!! */ @Test public void testQuorumV6() throws Exception { testQuorumInternal("[::1]"); } /** * Test early leader abandonment. */ @Test public void testEarlyLeaderAbandonment() throws Exception { ClientBase.setupTestEnv(); final int SERVER_COUNT = 3; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); sb.append("server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + clientPorts[i] + "\n"); } String quorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], quorumCfgSection); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } waitForAll(zk, States.CONNECTED); // we need to shutdown and start back up to make sure that the create session isn't the first transaction since // that is rather innocuous. for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } waitForAll(zk, States.CONNECTING); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].start(); // Recreate a client session since the previous session was not persisted. zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } waitForAll(zk, States.CONNECTED); // ok lets find the leader and kill everything else, we have a few // seconds, so it should be plenty of time int leader = -1; Map outstanding = null; for (int i = 0; i < SERVER_COUNT; i++) { if (mt[i].main.quorumPeer.leader == null) { mt[i].shutdown(); } else { leader = i; outstanding = mt[leader].main.quorumPeer.leader.outstandingProposals; } } try { zk[leader].create("/zk" + leader, "zk".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("create /zk" + leader + " should have failed"); } catch (KeeperException e) { } // just make sure that we actually did get it in process at the // leader assertTrue(outstanding.size() == 1); assertTrue(outstanding.values().iterator().next().request.getHdr().getType() == OpCode.create); // make sure it has a chance to write it to disk Thread.sleep(1000); mt[leader].shutdown(); waitForAll(zk, States.CONNECTING); for (int i = 0; i < SERVER_COUNT; i++) { if (i != leader) { mt[i].start(); } } for (int i = 0; i < SERVER_COUNT; i++) { if (i != leader) { // Recreate a client session since the previous session was not persisted. zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk[i], States.CONNECTED); zk[i].create("/zk" + i, "zk".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } mt[leader].start(); waitForAll(zk, States.CONNECTED); // make sure everything is consistent for (int i = 0; i < SERVER_COUNT; i++) { for (int j = 0; j < SERVER_COUNT; j++) { if (i == leader) { assertTrue(zk[j].exists("/zk" + i, false) == null, (j == leader ? ("Leader (" + leader + ")") : ("Follower " + j)) + " should not have /zk" + i); } else { assertTrue(zk[j].exists("/zk" + i, false) != null, (j == leader ? ("Leader (" + leader + ")") : ("Follower " + j)) + " does not have /zk" + i); } } } for (int i = 0; i < SERVER_COUNT; i++) { zk[i].close(); } for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } /** * Test the case of server with highest zxid not present at leader election and joining later. * This test case is for reproducing the issue and fixing the bug mentioned in ZOOKEEPER-1154 * and ZOOKEEPER-1156. */ @Test public void testHighestZxidJoinLate() throws Exception { numServers = 3; servers = LaunchServers(numServers); String path = "/hzxidtest"; int leader = servers.findLeader(); // make sure there is a leader assertTrue(leader >= 0, "There should be a leader"); int nonleader = (leader + 1) % numServers; byte[] input = new byte[1]; input[0] = 1; byte[] output; // Create a couple of nodes servers.zk[leader].create(path + leader, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); servers.zk[leader].create(path + nonleader, input, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // make sure the updates indeed committed. If it is not // the following statement will throw. output = servers.zk[leader].getData(path + nonleader, false, null); // Shutdown every one else but the leader for (int i = 0; i < numServers; i++) { if (i != leader) { servers.mt[i].shutdown(); } } input[0] = 2; // Update the node on the leader servers.zk[leader].setData(path + leader, input, -1, null, null); // wait some time to let this get written to disk Thread.sleep(500); // shut the leader down servers.mt[leader].shutdown(); System.gc(); waitForAll(servers.zk, States.CONNECTING); // Start everyone but the leader for (int i = 0; i < numServers; i++) { if (i != leader) { servers.mt[i].start(); } } // wait to connect to one of these waitForOne(servers.zk[nonleader], States.CONNECTED); // validate that the old value is there and not the new one output = servers.zk[nonleader].getData(path + leader, false, null); assertEquals(output[0], 1, "Expecting old value 1 since 2 isn't committed yet"); // Do some other update, so we bump the maxCommttedZxid // by setting the value to 2 servers.zk[nonleader].setData(path + nonleader, input, -1); // start the old leader servers.mt[leader].start(); // connect to it waitForOne(servers.zk[leader], States.CONNECTED); // make sure it doesn't have the new value that it alone had logged output = servers.zk[leader].getData(path + leader, false, null); assertEquals(output[0], 1, "Validating that the deposed leader has rolled back that change it had written"); // make sure the leader has the subsequent changes that were made while it was offline output = servers.zk[leader].getData(path + nonleader, false, null); assertEquals(output[0], 2, "Validating that the deposed leader caught up on changes it missed"); } /** * This test validates that if a quorum member determines that it is leader without the support of the rest of the * quorum (the other members do not believe it to be the leader) it will stop attempting to lead and become a follower. * * @throws IOException * @throws InterruptedException */ @Test public void testElectionFraud() throws Exception { numServers = 3; // used for assertions later boolean foundLeading = false; boolean foundLooking = false; boolean foundFollowing = false; try (LoggerTestTool loggerTestTool = new LoggerTestTool(QuorumPeer.class)) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); // spin up a quorum, we use a small ticktime to make the test run faster servers = LaunchServers(numServers, 500); // find the leader int trueLeader = servers.findLeader(); assertTrue(trueLeader >= 0, "There should be a leader"); // find a follower int falseLeader = (trueLeader + 1) % numServers; assertTrue(servers.mt[falseLeader].main.quorumPeer.follower != null, "All servers should join the quorum"); // to keep the quorum peer running and force it to go into the looking state, we kill leader election servers.mt[falseLeader].main.quorumPeer.electionAlg.shutdown(); servers.mt[falseLeader].main.quorumPeer.follower.getSocket().close(); // wait for the falseLeader to disconnect waitForOne(servers.zk[falseLeader], States.CONNECTING); // convince falseLeader that it is the leader servers.mt[falseLeader].main.quorumPeer.setPeerState(QuorumPeer.ServerState.LEADING); // provide time for the falseleader to realize no followers have connected // (this is twice the timeout used in Leader#getEpochToPropose) Thread.sleep(2 * servers.mt[falseLeader].main.quorumPeer.initLimit * servers.mt[falseLeader].main.quorumPeer.tickTime); // Restart leader election servers.mt[falseLeader].main.quorumPeer.startLeaderElection(); // The previous client connection to falseLeader likely closed, create a new one servers.zk[falseLeader] = new ZooKeeper( "127.0.0.1:" + servers.mt[falseLeader].getClientPort(), ClientBase.CONNECTION_TIMEOUT, this); // Wait for falseLeader to rejoin the quorum waitForOne(servers.zk[falseLeader], States.CONNECTED); // and ensure trueLeader is still the leader assertTrue(servers.mt[trueLeader].main.quorumPeer.leader != null); // Look through the logs for output that indicates the falseLeader is LEADING, then LOOKING, then FOLLOWING LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); Pattern leading = Pattern.compile(".*myid=" + falseLeader + ".*LEADING.*"); Pattern looking = Pattern.compile(".*myid=" + falseLeader + ".*LOOKING.*"); Pattern following = Pattern.compile(".*myid=" + falseLeader + ".*FOLLOWING.*"); String line; while ((line = r.readLine()) != null) { if (!foundLeading) { foundLeading = leading.matcher(line).matches(); } else if (!foundLooking) { foundLooking = looking.matcher(line).matches(); } else if (following.matcher(line).matches()) { foundFollowing = true; break; } } } assertTrue(foundLeading, "falseLeader never attempts to become leader"); assertTrue(foundLooking, "falseLeader never gives up on leadership"); assertTrue(foundFollowing, "falseLeader never rejoins the quorum"); } /** * Verify handling of bad quorum address */ @Test public void testBadPeerAddressInQuorum() throws Exception { ClientBase.setupTestEnv(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper.server.quorum")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\nserver.2=fee.fii.foo.fum:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); boolean isup = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 30000); assertFalse(isup, "Server never came up"); q1.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean found = false; Pattern p = Pattern.compile(".*Cannot open channel to .* at election address .*"); while ((line = r.readLine()) != null) { found = p.matcher(line).matches(); if (found) { break; } } assertTrue(found, "complains about host"); } } /** * Verify handling of inconsistent peer type */ @Test public void testInconsistentPeerType() throws Exception { ClientBase.setupTestEnv(); // test the most likely situation only: server is stated as observer in // servers list, but there's no "peerType=observer" token in config try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper.server.quorum")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); final int CLIENT_PORT_QP3 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\nserver.3=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":observer" + ";" + CLIENT_PORT_QP3; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection); MainThread q3 = new MainThread(3, CLIENT_PORT_QP3, quorumCfgSection); q1.start(); q2.start(); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP3, CONNECTION_TIMEOUT), "waiting for server 3 being up"); q1.shutdown(); q2.shutdown(); q3.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP3, ClientBase.CONNECTION_TIMEOUT), "waiting for server 3 down"); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean warningPresent = false; boolean defaultedToObserver = false; Pattern pWarn = Pattern.compile(".*Peer type from servers list.* doesn't match peerType.*"); Pattern pObserve = Pattern.compile(".*OBSERVING.*"); while ((line = r.readLine()) != null) { if (pWarn.matcher(line).matches()) { warningPresent = true; } if (pObserve.matcher(line).matches()) { defaultedToObserver = true; } if (warningPresent && defaultedToObserver) { break; } } assertTrue(warningPresent && defaultedToObserver, "Should warn about inconsistent peer type"); } } /** * verify if bad packets are being handled properly * at the quorum port * @throws Exception */ @Test public void testBadPackets() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); int electionPort1 = PortAssignment.unique(); int electionPort2 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + electionPort1 + ";" + CLIENT_PORT_QP1 + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + electionPort2 + ";" + CLIENT_PORT_QP2; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); byte[] b = new byte[4]; int length = 1024 * 1024 * 1024; ByteBuffer buff = ByteBuffer.wrap(b); buff.putInt(length); buff.position(0); SocketChannel s = SocketChannel.open(new InetSocketAddress("127.0.0.1", electionPort1)); s.write(buff); s.close(); buff.position(0); s = SocketChannel.open(new InetSocketAddress("127.0.0.1", electionPort2)); s.write(buff); s.close(); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); zk.create("/foo_q1", "foobar1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/foo_q1", null, null)), "foobar1"); zk.close(); q1.shutdown(); q2.shutdown(); } /** * Verify handling of quorum defaults * * default electionAlg is fast leader election */ @Test public void testQuorumDefaults() throws Exception { ClientBase.setupTestEnv(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); q1.shutdown(); q2.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); os.close(); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean found = false; Pattern p = Pattern.compile(".*FastLeaderElection.*"); while ((line = r.readLine()) != null) { found = p.matcher(line).matches(); if (found) { break; } } assertTrue(found, "fastleaderelection used"); } } /** * Verifies that QuorumPeer exits immediately */ @Test public void testQuorumPeerExitTime() throws Exception { long maxwait = 3000; final int CLIENT_PORT_QP1 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + PortAssignment.unique(); MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); // Let the notifications timeout Thread.sleep(30000); long start = Time.currentElapsedTime(); q1.shutdown(); long end = Time.currentElapsedTime(); if ((end - start) > maxwait) { fail("QuorumPeer took " + (end - start) + " to shutdown, expected " + maxwait); } } /** * Test verifies that the server is able to redefine the min/max session * timeouts */ @Test public void testMinMaxSessionTimeOut() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique(); final int minSessionTimeOut = 10000; final int maxSessionTimeOut = 15000; final String configs = "maxSessionTimeout=" + maxSessionTimeOut + "\n" + "minSessionTimeout=" + minSessionTimeOut + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection, configs); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection, configs); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); QuorumPeer quorumPeer = q1.main.quorumPeer; assertEquals(minSessionTimeOut, quorumPeer.getMinSessionTimeout(), "minimumSessionTimeOut is not considered"); assertEquals(maxSessionTimeOut, quorumPeer.getMaxSessionTimeout(), "maximumSessionTimeOut is not considered"); } /** * Test verifies that the server is able to redefine if user configured only * minSessionTimeout limit */ @Test public void testWithOnlyMinSessionTimeout() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique(); final int minSessionTimeOut = 15000; final String configs = "minSessionTimeout=" + minSessionTimeOut + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection, configs); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection, configs); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); QuorumPeer quorumPeer = q1.main.quorumPeer; final int maxSessionTimeOut = quorumPeer.tickTime * 20; assertEquals(minSessionTimeOut, quorumPeer.getMinSessionTimeout(), "minimumSessionTimeOut is not considered"); assertEquals(maxSessionTimeOut, quorumPeer.getMaxSessionTimeout(), "maximumSessionTimeOut is wrong"); } @Test public void testFailedTxnAsPartOfQuorumLoss() throws Exception { final int LEADER_TIMEOUT_MS = 10_000; // 1. start up server and wait for leader election to finish ClientBase.setupTestEnv(); final int SERVER_COUNT = 3; servers = LaunchServers(SERVER_COUNT); waitForAll(servers, States.CONNECTED); // we need to shutdown and start back up to make sure that the create session isn't the first transaction since // that is rather innocuous. servers.shutDownAllServers(); waitForAll(servers, States.CONNECTING); servers.restartAllServersAndClients(this); waitForAll(servers, States.CONNECTED); // 2. kill all followers int leader = servers.findLeader(); Map outstanding = servers.mt[leader].main.quorumPeer.leader.outstandingProposals; // increase the tick time to delay the leader going to looking int previousTick = servers.mt[leader].main.quorumPeer.tickTime; servers.mt[leader].main.quorumPeer.tickTime = LEADER_TIMEOUT_MS; // let the previous tick on the leader exhaust itself so the new tick time takes effect Thread.sleep(previousTick); LOG.warn("LEADER {}", leader); for (int i = 0; i < SERVER_COUNT; i++) { if (i != leader) { servers.mt[i].shutdown(); } } // 3. start up the followers to form a new quorum for (int i = 0; i < SERVER_COUNT; i++) { if (i != leader) { servers.mt[i].start(); } } // 4. wait one of the follower to be the new leader for (int i = 0; i < SERVER_COUNT; i++) { if (i != leader) { // Recreate a client session since the previous session was not persisted. servers.restartClient(i, this); waitForOne(servers.zk[i], States.CONNECTED); } } // 5. send a create request to old leader and make sure it's synced to disk, // which means it acked from itself try { servers.zk[leader].create("/zk" + leader, "zk".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("create /zk" + leader + " should have failed"); } catch (KeeperException e) { } // just make sure that we actually did get it in process at the // leader // there can be extra sessionClose proposals assertTrue(outstanding.size() > 0); Proposal p = findProposalOfType(outstanding, OpCode.create); LOG.info("Old leader id: {}. All proposals: {}", leader, outstanding); assertNotNull(p, "Old leader doesn't have 'create' proposal"); // make sure it has a chance to write it to disk int sleepTime = 0; Long longLeader = (long) leader; while (!p.qvAcksetPairs.get(0).getAckset().contains(longLeader)) { if (sleepTime > 2000) { fail("Transaction not synced to disk within 1 second " + p.qvAcksetPairs.get(0).getAckset() + " expected " + leader); } Thread.sleep(100); sleepTime += 100; } // 6. wait for the leader to quit due to not enough followers and come back up as a part of the new quorum LOG.info("Waiting for leader {} to timeout followers", leader); sleepTime = 0; Follower f = servers.mt[leader].main.quorumPeer.follower; while (f == null || !f.isRunning()) { if (sleepTime > LEADER_TIMEOUT_MS * 2) { fail("Took too long for old leader to time out " + servers.mt[leader].main.quorumPeer.getPeerState()); } Thread.sleep(100); sleepTime += 100; f = servers.mt[leader].main.quorumPeer.follower; } int newLeader = servers.findLeader(); // make sure a different leader was elected assertNotEquals(leader, newLeader); // 7. restart the previous leader to force it to replay the edits and possibly come up in a bad state servers.mt[leader].shutdown(); servers.mt[leader].start(); // old client session can expire, restart it servers.restartClient(leader, this); waitForAll(servers, States.CONNECTED); // 8. check the node exist in previous leader but not others // make sure everything is consistent for (int i = 0; i < SERVER_COUNT; i++) { assertNull(servers.zk[i].exists("/zk" + leader, false), "server " + i + " should not have /zk" + leader); } } /** * Verify that a node without the leader in its view will not attempt to connect to the leader. */ @Test public void testLeaderOutOfView() throws Exception { ClientBase.setupTestEnv(); int numServers = 3; // used for assertions later boolean foundLeading = false; boolean foundFollowing = false; try (LoggerTestTool loggerTestTool = new LoggerTestTool(QuorumPeerMainTest.class)) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); Servers svrs = new Servers(); svrs.clientPorts = new int[numServers]; for (int i = 0; i < numServers; i++) { svrs.clientPorts[i] = PortAssignment.unique(); } String quorumCfgIncomplete = getUniquePortCfgForId(1) + "\n" + getUniquePortCfgForId(2); String quorumCfgComplete = quorumCfgIncomplete + "\n" + getUniquePortCfgForId(3); svrs.mt = new MainThread[3]; // Node 1 is started without the leader (3) in its config view svrs.mt[0] = new MainThread(1, svrs.clientPorts[0], quorumCfgIncomplete); for (int i = 1; i < numServers; i++) { svrs.mt[i] = new MainThread(i + 1, svrs.clientPorts[i], quorumCfgComplete); } // Node 1 must be started first, before quorum is formed, to trigger the attempted invalid connection to 3 svrs.mt[0].start(); QuorumPeer quorumPeer1 = waitForQuorumPeer(svrs.mt[0], CONNECTION_TIMEOUT); assertTrue(quorumPeer1.getPeerState() == QuorumPeer.ServerState.LOOKING); // Node 3 started second to avoid 1 and 2 forming a quorum before 3 starts up int highestServerIndex = numServers - 1; svrs.mt[highestServerIndex].start(); QuorumPeer quorumPeer3 = waitForQuorumPeer(svrs.mt[highestServerIndex], CONNECTION_TIMEOUT); assertTrue(quorumPeer3.getPeerState() == QuorumPeer.ServerState.LOOKING); // Node 2 started last, kicks off leader election for (int i = 1; i < highestServerIndex; i++) { svrs.mt[i].start(); } // Nodes 2 and 3 now form quorum and fully start. 1 attempts to vote for 3, fails, returns to LOOKING state for (int i = 1; i < numServers; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + svrs.clientPorts[i], CONNECTION_TIMEOUT), "waiting for server to start"); } // Expecting that only 3 node will be leader is wrong, even 2 node can be leader, when cluster is formed with 1 node boolean firstAndSecondNodeFormedCluster = false; if (QuorumPeer.ServerState.LEADING == svrs.mt[1].getQuorumPeer().getPeerState()) { assertEquals(QuorumPeer.ServerState.FOLLOWING, svrs.mt[0].getQuorumPeer().getPeerState()); assertEquals(QuorumPeer.ServerState.FOLLOWING, svrs.mt[highestServerIndex].getQuorumPeer().getPeerState()); firstAndSecondNodeFormedCluster = true; } else { // Verify leader out of view scenario assertEquals(QuorumPeer.ServerState.LOOKING, svrs.mt[0].getQuorumPeer().getPeerState()); assertEquals(QuorumPeer.ServerState.LEADING, svrs.mt[highestServerIndex].getQuorumPeer().getPeerState()); } for (int i = 1; i < highestServerIndex; i++) { assertTrue( svrs.mt[i].getQuorumPeer().getPeerState() == QuorumPeer.ServerState.FOLLOWING || svrs.mt[i].getQuorumPeer().getPeerState() == QuorumPeer.ServerState.LEADING); } // Look through the logs for output that indicates Node 1 is LEADING or FOLLOWING LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); Pattern leading = Pattern.compile(".*myid=1.*QuorumPeer.*LEADING.*"); Pattern following = Pattern.compile(".*myid=1.*QuorumPeer.*FOLLOWING.*"); String line; while ((line = r.readLine()) != null && !foundLeading && !foundFollowing) { foundLeading = leading.matcher(line).matches(); foundFollowing = following.matcher(line).matches(); } if (firstAndSecondNodeFormedCluster) { assertTrue(foundFollowing, "Corrupt peer should join quorum with servers having same server configuration"); } else { assertFalse(foundLeading, "Corrupt peer should never become leader"); assertFalse(foundFollowing, "Corrupt peer should not attempt connection to out of view leader"); } } } @Test public void testDataDirAndDataLogDir() throws Exception { File dataDir = createEmptyTestDir(); File dataLogDir = createEmptyTestDir(); // Arrange try { QuorumPeerConfig configMock = mock(QuorumPeerConfig.class); when(configMock.getDataDir()).thenReturn(dataDir); when(configMock.getDataLogDir()).thenReturn(dataLogDir); when(configMock.getMetricsProviderClassName()).thenReturn(NullMetricsProvider.class.getName()); QuorumPeer qpMock = mock(QuorumPeer.class); doCallRealMethod().when(qpMock).setTxnFactory(any(FileTxnSnapLog.class)); when(qpMock.getTxnFactory()).thenCallRealMethod(); InjectableQuorumPeerMain qpMain = new InjectableQuorumPeerMain(qpMock); // Act qpMain.runFromConfig(configMock); // Assert FileTxnSnapLog txnFactory = qpMain.getQuorumPeer().getTxnFactory(); assertEquals(Paths.get(dataLogDir.getAbsolutePath(), "version-2").toString(), txnFactory.getDataLogDir().getAbsolutePath()); assertEquals(Paths.get(dataDir.getAbsolutePath(), "version-2").toString(), txnFactory.getSnapDir().getAbsolutePath()); } finally { FileUtils.deleteDirectory(dataDir); FileUtils.deleteDirectory(dataLogDir); } } private class InjectableQuorumPeerMain extends QuorumPeerMain { QuorumPeer qp; InjectableQuorumPeerMain(QuorumPeer qp) { this.qp = qp; } @Override protected QuorumPeer getQuorumPeer() { return qp; } } private String getUniquePortCfgForId(int id) { return String.format("server.%d=127.0.0.1:%d:%d", id, PortAssignment.unique(), PortAssignment.unique()); } private QuorumPeer waitForQuorumPeer(MainThread mainThread, int timeout) throws TimeoutException { long start = Time.currentElapsedTime(); while (true) { QuorumPeer quorumPeer = mainThread.isAlive() ? mainThread.getQuorumPeer() : null; if (quorumPeer != null) { return quorumPeer; } if (Time.currentElapsedTime() > start + timeout) { LOG.error("Timed out while waiting for QuorumPeer"); throw new TimeoutException(); } try { Thread.sleep(250); } catch (InterruptedException e) { // ignore } } } private Proposal findProposalOfType(Map proposals, int type) { for (Proposal proposal : proposals.values()) { if (proposal.request.getHdr().getType() == type) { return proposal; } } return null; } /** * Currently, in SNAP sync, the leader will start queuing the * proposal/commits and the NEWLEADER packet before sending * over the snapshot over wire. So it's possible that the zxid * associated with the snapshot might be higher than all the * packets queued before NEWLEADER. * * When the follower received the snapshot, it will apply all * the txns queued before NEWLEADER, which may not cover all * the txns up to the zxid in the snapshot. After that, it * will write the snapshot out to disk with the zxid associated * with the snapshot. In case the server crashed after writing * this out, when loading the data from disk, it will use zxid * of the snapshot file to sync with leader, and it could cause * data inconsistent, because we only replayed partial of the * historical data during previous syncing. * * This test case is going to cover and simulate this scenario * and make sure there is no data inconsistency issue after fix. */ @Test public void testInconsistentDueToNewLeaderOrder() throws Exception { // 1. set up an ensemble with 3 servers final int ENSEMBLE_SERVERS = 3; final int[] clientPorts = new int[ENSEMBLE_SERVERS]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); // start servers MainThread[] mt = new MainThread[ENSEMBLE_SERVERS]; ZooKeeper[] zk = new ZooKeeper[ENSEMBLE_SERVERS]; Context[] contexts = new Context[ENSEMBLE_SERVERS]; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { final Context context = new Context(); contexts[i] = context; mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new CustomizedQPMain(context); } }; mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } waitForAll(zk, States.CONNECTED); LOG.info("all servers started"); String nodePath = "/testInconsistentDueToNewLeader"; int leaderId = -1; int followerA = -1; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { if (mt[i].main.quorumPeer.leader != null) { leaderId = i; } else if (followerA == -1) { followerA = i; } } LOG.info("shutdown follower {}", followerA); mt[followerA].shutdown(); waitForOne(zk[followerA], States.CONNECTING); try { // 2. set force snapshot to be true LOG.info("force snapshot sync"); System.setProperty(LearnerHandler.FORCE_SNAP_SYNC, "true"); // 3. create a node String initialValue = "1"; final ZooKeeper leaderZk = zk[leaderId]; leaderZk.create(nodePath, initialValue.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("created node {} with value {}", nodePath, initialValue); CustomQuorumPeer leaderQuorumPeer = (CustomQuorumPeer) mt[leaderId].main.quorumPeer; // 4. on the customized leader catch the startForwarding call // (without synchronized), set the node to value v1, then // call the super.startForwarding to generate the ongoing // txn proposal and commit for v1 value update leaderQuorumPeer.setStartForwardingListener(new StartForwardingListener() { @Override public void start() { if (!Boolean.getBoolean(LearnerHandler.FORCE_SNAP_SYNC)) { return; } final String value = "2"; LOG.info("start forwarding, set {} to {}", nodePath, value); // use async, otherwise it will block the logLock in // ZKDatabase and the setData request will timeout try { leaderZk.setData(nodePath, value.getBytes(), -1, (rc, path, ctx, stat) -> { }, null); // wait for the setData txn being populated Thread.sleep(1000); } catch (Exception e) { LOG.error("error when set {} to {}", nodePath, value, e); } } }); // 5. on the customized leader catch the beginSnapshot call in // LearnerSyncThrottler to set the node to value v2, // wait it hit data tree leaderQuorumPeer.setBeginSnapshotListener(new BeginSnapshotListener() { @Override public void start() { String value = "3"; LOG.info("before sending snapshot, set {} to {}", nodePath, value); try { leaderZk.setData(nodePath, value.getBytes(), -1); LOG.info("successfully set {} to {}", nodePath, value); } catch (Exception e) { LOG.error("error when set {} to {}, {}", nodePath, value, e); } } }); // 6. exit follower A after taking snapshot CustomQuorumPeer followerAQuorumPeer = ((CustomQuorumPeer) mt[followerA].main.quorumPeer); LOG.info("set exit when ack new leader packet on {}", followerA); contexts[followerA].exitWhenAckNewLeader = true; CountDownLatch latch = new CountDownLatch(1); final MainThread followerAMT = mt[followerA]; contexts[followerA].newLeaderAckCallback = new NewLeaderAckCallback() { @Override public void start() { try { latch.countDown(); followerAMT.shutdown(); } catch (Exception e) { } } }; // 7. start follower A to do snapshot sync LOG.info("starting follower {}", followerA); mt[followerA].start(); assertTrue(latch.await(30, TimeUnit.SECONDS)); // 8. now we have invalid data on disk, let's load it and verify LOG.info("disable exit when ack new leader packet on {}", followerA); System.setProperty(LearnerHandler.FORCE_SNAP_SYNC, "false"); contexts[followerA].exitWhenAckNewLeader = true; contexts[followerA].newLeaderAckCallback = null; LOG.info("restarting follower {}", followerA); mt[followerA].start(); zk[followerA].close(); zk[followerA] = new ZooKeeper("127.0.0.1:" + clientPorts[followerA], ClientBase.CONNECTION_TIMEOUT, this); // 9. start follower A, after it's in broadcast state, make sure // the node value is same as what we have on leader waitForOne(zk[followerA], States.CONNECTED); assertEquals( new String(zk[followerA].getData(nodePath, null, null)), new String(zk[leaderId].getData(nodePath, null, null))); } finally { System.clearProperty(LearnerHandler.FORCE_SNAP_SYNC); for (int i = 0; i < ENSEMBLE_SERVERS; i++) { mt[i].shutdown(); zk[i].close(); } } } /** * Test leader election finished with 1 disloyal voter and without * majority followers, expecting to see the quorum stablized only * after waiting for maxTimeToWaitForEpoch. */ @Test public void testLeaderElectionWithDisloyalVoter() throws IOException { testLeaderElection(5, 3, 1000, 10000); } /** * Test leader election finished with 1 disloyal voter and majority * followers, expecting to see the quorum stablized immediately even * there is 1 disloyal voter. * * Set the maxTimeToWaitForEpoch to 3s and maxTimeWaitForServerUp to * 2s to confirm this. */ @Test public void testLeaderElectionWithDisloyalVoter_stillHasMajority() throws IOException { testLeaderElection(5, 5, 3000, 20000); } void testLeaderElection(int totalServers, int serversToStart, int maxTimeToWaitForEpoch, int maxTimeWaitForServerUp) throws IOException { Leader.setMaxTimeToWaitForEpoch(maxTimeToWaitForEpoch); // set up config for an ensemble with given number of servers servers = new Servers(); int ENSEMBLE_SERVERS = totalServers; final int[] clientPorts = new int[ENSEMBLE_SERVERS]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); // start servers int SERVERS_TO_START = serversToStart; MainThread[] mt = new MainThread[SERVERS_TO_START]; Context[] contexts = new Context[SERVERS_TO_START]; servers.mt = mt; numServers = SERVERS_TO_START; for (int i = 0; i < SERVERS_TO_START; i++) { // hook the 1st follower to quit following after leader election // simulate the behavior of changing voting during looking final Context context = new Context(); if (i == 0) { context.quitFollowing = true; } contexts[i] = context; mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new CustomizedQPMain(context); } }; mt[i].start(); } // make sure the quorum can be formed within initLimit * tickTime // the default setting is 10 * 4000 = 40000 ms for (int i = 0; i < SERVERS_TO_START; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], maxTimeWaitForServerUp), "Server " + i + " should have joined quorum by now"); } } /** * Verify boot works configuring a MetricsProvider */ @Test public void testMetricsProviderLifecycle() throws Exception { ClientBase.setupTestEnv(); BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.reset(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSectionServer = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\nserver.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\n"; // server 1 boots with a MetricsProvider String quorumCfgSectionServer1 = quorumCfgSectionServer + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.class.getName() + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSectionServer1); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSectionServer); q1.start(); q2.start(); boolean isup1 = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 30000); boolean isup2 = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, 30000); assertTrue(isup1, "Server 1 never came up"); assertTrue(isup2, "Server 2 never came up"); q1.shutdown(); q2.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.configureCalled.get(), "metrics provider lifecycle error"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.startCalled.get(), "metrics provider lifecycle error"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.getRootContextCalled.get(), "metrics provider lifecycle error"); assertTrue(BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.stopCalled.get(), "metrics provider lifecycle error"); } /** * Test verifies that configuration is passed to the MetricsProvider. */ @Test public void testMetricsProviderConfiguration() throws Exception { ClientBase.setupTestEnv(); BaseTestMetricsProvider.MetricsProviderWithConfiguration.httpPort.set(0); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSectionServer = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "server.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\n"; // server 1 boots with a MetricsProvider String quorumCfgSectionServer1 = quorumCfgSectionServer + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithConfiguration.class.getName() + "\n" + "metricsProvider.httpPort=1234"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSectionServer1); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSectionServer); q1.start(); q2.start(); boolean isup1 = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 30000); boolean isup2 = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, 30000); assertTrue(isup1, "Server 1 never came up"); assertTrue(isup2, "Server 2 never came up"); q1.shutdown(); q2.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); assertEquals(1234, BaseTestMetricsProvider.MetricsProviderWithConfiguration.httpPort.get()); } /** * Test verifies that the server shouldn't be affected but runtime errors on stop() */ @Test public void testFaultyMetricsProviderOnStop() throws Exception { ClientBase.setupTestEnv(); BaseTestMetricsProvider.MetricsProviderCapturingLifecycle.reset(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper.server.quorum")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); final int CLIENT_PORT_QP2 = PortAssignment.unique(); String quorumCfgSectionServer = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "server.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP2 + "\n"; // server 1 boots with a MetricsProvider String quorumCfgSectionServer1 = quorumCfgSectionServer + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithErrorInStop.class.getName() + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSectionServer1); MainThread q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSectionServer); q1.start(); q2.start(); boolean isup1 = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 30000); boolean isup2 = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, 30000); assertTrue(isup1, "Server 1 never came up"); assertTrue(isup2, "Server 2 never came up"); q1.shutdown(); q2.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "waiting for server 2 down"); assertTrue(BaseTestMetricsProvider.MetricsProviderWithErrorInStop.stopCalled.get(), "metrics provider lifecycle error"); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean found = false; Pattern p = Pattern.compile(".*Error while stopping metrics.*"); while ((line = r.readLine()) != null) { found = p.matcher(line).matches(); if (found) { break; } } assertTrue(found, "complains about metrics provider"); } } /** * Verify boot fails with a bad MetricsProvider */ @Test public void testInvalidMetricsProvider() throws Exception { ClientBase.setupTestEnv(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper.server.quorum")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "server.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "metricsProvider.className=BadClass\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); boolean isup = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 5000); assertFalse(isup, "Server never came up"); q1.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean found = false; Pattern p = Pattern.compile(".*BadClass.*"); while ((line = r.readLine()) != null) { found = p.matcher(line).matches(); if (found) { break; } } assertTrue(found, "complains about metrics provider"); } } /** * Verify boot fails with a MetricsProvider with fails to start */ @Test public void testFaultyMetricsProviderOnStart() throws Exception { ClientBase.setupTestEnv(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper.server.quorum")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "server.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithErrorInStart.class.getName() + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); boolean isup = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 5000); assertFalse(isup, "Server never came up"); q1.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean found = false; Pattern p = Pattern.compile(".*MetricsProviderLifeCycleException.*"); while ((line = r.readLine()) != null) { found = p.matcher(line).matches(); if (found) { break; } } assertTrue(found, "complains about metrics provider MetricsProviderLifeCycleException"); } } /** * Verify boot fails with a MetricsProvider with fails to start */ @Test public void testFaultyMetricsProviderOnConfigure() throws Exception { ClientBase.setupTestEnv(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper.server.quorum")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "server.2=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ";" + CLIENT_PORT_QP1 + "\n" + "metricsProvider.className=" + BaseTestMetricsProvider.MetricsProviderWithErrorInConfigure.class.getName() + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); boolean isup = ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, 5000); assertFalse(isup, "Server never came up"); q1.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "waiting for server 1 down"); LineNumberReader r = new LineNumberReader(new StringReader(os.toString())); String line; boolean found = false; Pattern p = Pattern.compile(".*MetricsProviderLifeCycleException.*"); while ((line = r.readLine()) != null) { found = p.matcher(line).matches(); if (found) { break; } } assertTrue(found, "complains about metrics provider MetricsProviderLifeCycleException"); } } /** * Test the behavior to skip processing the learner forwarded requests in * Leader's CommitProcessor. */ @Test public void testLearnerRequestForwardBehavior() throws Exception { System.setProperty(ProposalRequestProcessor.FORWARD_LEARNER_REQUESTS_TO_COMMIT_PROCESSOR_DISABLED, "true"); try { // 1. set up an ensemble with 3 servers final int numServers = 3; servers = LaunchServers(numServers); int leaderId = servers.findLeader(); int followerA = (leaderId + 1) % numServers; waitForOne(servers.zk[followerA], States.CONNECTED); // 2. reset all metrics ServerMetrics.getMetrics().resetAll(); // 3. issue a request final String node = "/testLearnerRequestForwardBehavior"; servers.zk[followerA].create(node, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(servers.zk[followerA].exists("/testLearnerRequestForwardBehavior", false), "node " + node + " should exist"); assertEquals(1L, ServerMetrics.getMetrics().REQUESTS_NOT_FORWARDED_TO_COMMIT_PROCESSOR.get()); } finally { //clean up System.setProperty(ProposalRequestProcessor.FORWARD_LEARNER_REQUESTS_TO_COMMIT_PROCESSOR_DISABLED, "false"); } } /** * If learner failed to do SNAP sync with leader before it's writing * the snapshot to disk, it's possible that it might have DIFF sync * with new leader or itself being elected as a leader. * * This test is trying to guarantee there is no data inconsistency for * this case. */ @Test public void testDiffSyncAfterSnap() throws Exception { final int ENSEMBLE_SERVERS = 3; MainThread[] mt = new MainThread[ENSEMBLE_SERVERS]; ZooKeeper[] zk = new ZooKeeper[ENSEMBLE_SERVERS]; try { // 1. start a quorum final int[] clientPorts = new int[ENSEMBLE_SERVERS]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); // start servers Context[] contexts = new Context[ENSEMBLE_SERVERS]; for (int i = 0; i < ENSEMBLE_SERVERS; i++) { final Context context = new Context(); contexts[i] = context; mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new CustomizedQPMain(context); } }; mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } waitForAll(zk, States.CONNECTED); LOG.info("all servers started"); final String nodePath = "/testDiffSyncAfterSnap"; // 2. find leader and a follower int leaderId = -1; int followerA = -1; for (int i = ENSEMBLE_SERVERS - 1; i >= 0; i--) { if (mt[i].main.quorumPeer.leader != null) { leaderId = i; } else if (followerA == -1) { followerA = i; } } // 3. stop follower A LOG.info("shutdown follower {}", followerA); mt[followerA].shutdown(); waitForOne(zk[followerA], States.CONNECTING); // 4. issue some traffic int index = 0; int numOfRequests = 10; for (int i = 0; i < numOfRequests; i++) { zk[leaderId].create(nodePath + index++, new byte[1], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } CustomQuorumPeer leaderQuorumPeer = (CustomQuorumPeer) mt[leaderId].main.quorumPeer; // 5. inject fault to cause the follower exit when received NEWLEADER contexts[followerA].newLeaderReceivedCallback = new NewLeaderReceivedCallback() { boolean processed = false; @Override public void process() throws IOException { if (processed) { return; } processed = true; System.setProperty(LearnerHandler.FORCE_SNAP_SYNC, "false"); throw new IOException("read timedout"); } }; // 6. force snap sync once LOG.info("force snapshot sync"); System.setProperty(LearnerHandler.FORCE_SNAP_SYNC, "true"); // 7. start follower A mt[followerA].start(); waitForOne(zk[followerA], States.CONNECTED); LOG.info("verify the nodes are exist in memory"); for (int i = 0; i < index; i++) { assertNotNull(zk[followerA].exists(nodePath + i, false)); } // 8. issue another request which will be persisted on disk zk[leaderId].create(nodePath + index++, new byte[1], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // wait some time to let this get written to disk Thread.sleep(500); // 9. reload data from disk and make sure it's still consistent LOG.info("restarting follower {}", followerA); mt[followerA].shutdown(); waitForOne(zk[followerA], States.CONNECTING); mt[followerA].start(); waitForOne(zk[followerA], States.CONNECTED); for (int i = 0; i < index; i++) { assertNotNull(zk[followerA].exists(nodePath + i, false), "node " + i + " should exist"); } } finally { System.clearProperty(LearnerHandler.FORCE_SNAP_SYNC); for (int i = 0; i < ENSEMBLE_SERVERS; i++) { mt[i].shutdown(); zk[i].close(); } } } static class Context { boolean quitFollowing = false; boolean exitWhenAckNewLeader = false; NewLeaderAckCallback newLeaderAckCallback = null; NewLeaderReceivedCallback newLeaderReceivedCallback = null; } interface NewLeaderAckCallback { void start(); } interface NewLeaderReceivedCallback { void process() throws IOException; } interface StartForwardingListener { void start(); } interface BeginSnapshotListener { void start(); } static class CustomizedQPMain extends TestQPMain { private Context context; public CustomizedQPMain(Context context) { this.context = context; } @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new CustomQuorumPeer(context); } } static class CustomQuorumPeer extends QuorumPeer { private Context context; private LearnerSyncThrottler throttler = null; private StartForwardingListener startForwardingListener; private BeginSnapshotListener beginSnapshotListener; public CustomQuorumPeer(Context context) throws SaslException { this.context = context; } public void setStartForwardingListener( StartForwardingListener startForwardingListener) { this.startForwardingListener = startForwardingListener; } public void setBeginSnapshotListener( BeginSnapshotListener beginSnapshotListener) { this.beginSnapshotListener = beginSnapshotListener; } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb())) { @Override void followLeader() throws InterruptedException { if (context.quitFollowing) { // reset the flag context.quitFollowing = false; LOG.info("Quit following"); return; } else { super.followLeader(); } } @Override void writePacket(QuorumPacket pp, boolean flush) throws IOException { if (pp != null && pp.getType() == Leader.ACK && context.exitWhenAckNewLeader) { if (context.newLeaderAckCallback != null) { context.newLeaderAckCallback.start(); } } super.writePacket(pp, flush); } @Override void readPacket(QuorumPacket qp) throws IOException { super.readPacket(qp); if (qp.getType() == Leader.NEWLEADER && context.newLeaderReceivedCallback != null) { context.newLeaderReceivedCallback.process(); } } }; } @Override protected Leader makeLeader(FileTxnSnapLog logFactory) throws IOException, X509Exception { return new Leader(this, new LeaderZooKeeperServer(logFactory, this, this.getZkDb())) { @Override public long startForwarding(LearnerHandler handler, long lastSeenZxid) { if (startForwardingListener != null) { startForwardingListener.start(); } return super.startForwarding(handler, lastSeenZxid); } @Override public LearnerSyncThrottler getLearnerSnapSyncThrottler() { if (throttler == null) { throttler = new LearnerSyncThrottler(getMaxConcurrentSnapSyncs(), LearnerSyncThrottler.SyncType.SNAP) { @Override public void beginSync(boolean essential) throws SyncThrottleException, InterruptedException { if (beginSnapshotListener != null) { beginSnapshotListener.start(); } super.beginSync(essential); } }; } return throttler; } }; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032755 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerT0100644 0000000 0000000 00000010711 15051152474 034336 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.Test; public class QuorumPeerTest { private int electionAlg = 3; private int tickTime = 2000; private int initLimit = 3; private int syncLimit = 3; private int connectToLearnerMasterLimit = 3; /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2301 */ @Test public void testQuorumPeerListendOnSpecifiedClientIP() throws IOException { long myId = 1; File dataDir = ClientBase.createTmpDir(); int clientPort = PortAssignment.unique(); Map peersView = new HashMap<>(); InetAddress clientIP = InetAddress.getLoopbackAddress(); peersView.put(Long.valueOf(myId), new QuorumServer(myId, new InetSocketAddress(clientIP, PortAssignment.unique()), new InetSocketAddress(clientIP, PortAssignment.unique()), new InetSocketAddress(clientIP, clientPort), LearnerType.PARTICIPANT)); /** * QuorumPeer constructor without QuorumVerifier */ QuorumPeer peer1 = new QuorumPeer(peersView, dataDir, dataDir, clientPort, electionAlg, myId, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); String hostString1 = peer1.cnxnFactory.getLocalAddress().getHostString(); assertEquals(clientIP.getHostAddress(), hostString1); // cleanup peer1.shutdown(); /** * QuorumPeer constructor with QuorumVerifier */ peersView.clear(); clientPort = PortAssignment.unique(); peersView.put(Long.valueOf(myId), new QuorumServer(myId, new InetSocketAddress(clientIP, PortAssignment.unique()), new InetSocketAddress(clientIP, PortAssignment.unique()), new InetSocketAddress(clientIP, clientPort), LearnerType.PARTICIPANT)); QuorumPeer peer2 = new QuorumPeer(peersView, dataDir, dataDir, clientPort, electionAlg, myId, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); String hostString2 = peer2.cnxnFactory.getLocalAddress().getHostString(); assertEquals(clientIP.getHostAddress(), hostString2); // cleanup peer2.shutdown(); } @Test public void testLocalPeerIsLeader() throws Exception { long localPeerId = 7; QuorumPeer peer = new QuorumPeer(); peer.setId(localPeerId); Vote voteLocalPeerIsLeader = new Vote(localPeerId, 0); peer.setCurrentVote(voteLocalPeerIsLeader); assertTrue(peer.isLeader(localPeerId)); } @Test public void testLocalPeerIsNotLeader() throws Exception { long localPeerId = 7; long otherPeerId = 17; QuorumPeer peer = new QuorumPeer(); peer.setId(localPeerId); Vote voteLocalPeerIsNotLeader = new Vote(otherPeerId, 0); peer.setCurrentVote(voteLocalPeerIsNotLeader); assertFalse(peer.isLeader(localPeerId)); } @Test public void testIsNotLeaderBecauseNoVote() throws Exception { long localPeerId = 7; QuorumPeer peer = new QuorumPeer(); peer.setId(localPeerId); peer.setCurrentVote(null); assertFalse(peer.isLeader(localPeerId)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerTestBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumPeerT0100644 0000000 0000000 00000053606 15051152474 034350 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.apache.zookeeper.server.quorum; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.PathUtils; import org.apache.zookeeper.server.admin.JettyAdminServer; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumBase; import org.junit.jupiter.api.AfterEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Has some common functionality for tests that work with QuorumPeers. Override * process(WatchedEvent) to implement the Watcher interface */ public class QuorumPeerTestBase extends ZKTestCase implements Watcher { protected static final Logger LOG = LoggerFactory.getLogger(QuorumPeerTestBase.class); public static final int TIMEOUT = 5000; protected Servers servers; protected int numServers = 0; @AfterEach public void tearDown() throws Exception { if (servers == null || servers.mt == null) { LOG.info("No servers to shutdown!"); return; } for (int i = 0; i < numServers; i++) { if (i < servers.mt.length) { servers.mt[i].shutdown(); } } } public void process(WatchedEvent event) { // ignore for this test } public static class TestQPMain extends QuorumPeerMain { public void shutdown() { // ensure it closes - in particular wait for thread to exit if (quorumPeer != null) { QuorumBase.shutdown(quorumPeer); } } } public static class MainThread implements Runnable { final File confFile; final File tmpDir; public static final int UNSET_STATIC_CLIENTPORT = -1; // standalone mode doens't need myid public static final int UNSET_MYID = -1; volatile TestQPMain main; File baseDir; private int myid; private int clientPort; private String quorumCfgSection; private Map otherConfigs; /** * Create a MainThread * * @param myid * @param clientPort * @param quorumCfgSection * @param otherConfigs * @param tickTime initLimit will be 10 and syncLimit will be 5 * @throws IOException */ public MainThread(int myid, int clientPort, String quorumCfgSection, Map otherConfigs, int tickTime) throws IOException { baseDir = ClientBase.createTmpDir(); this.myid = myid; this.clientPort = clientPort; this.quorumCfgSection = quorumCfgSection; this.otherConfigs = otherConfigs; LOG.info("id = {} tmpDir = {} clientPort = {}", myid, baseDir, clientPort); confFile = new File(baseDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=" + tickTime + "\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); fwriter.write("connectToLearnerMasterLimit=5\n"); tmpDir = new File(baseDir, "data"); if (!tmpDir.mkdir()) { throw new IOException("Unable to mkdir " + tmpDir); } // Convert windows path to UNIX to avoid problems with "\" String dir = tmpDir.toString(); String osname = java.lang.System.getProperty("os.name"); if (osname.toLowerCase().contains("windows")) { dir = dir.replace('\\', '/'); } fwriter.write("dataDir=" + dir + "\n"); fwriter.write("clientPort=" + clientPort + "\n"); // write extra configurations Set> entrySet = otherConfigs.entrySet(); for (Entry entry : entrySet) { fwriter.write(entry.getKey() + "=" + entry.getValue() + "\n"); } fwriter.write(quorumCfgSection + "\n"); fwriter.flush(); fwriter.close(); File myidFile = new File(tmpDir, "myid"); fwriter = new FileWriter(myidFile); fwriter.write(Integer.toString(myid)); fwriter.flush(); fwriter.close(); } public MainThread(int myid, String quorumCfgSection) throws IOException { this(myid, quorumCfgSection, true); } public MainThread(int myid, String quorumCfgSection, Integer secureClientPort, boolean writeDynamicConfigFile) throws IOException { this(myid, UNSET_STATIC_CLIENTPORT, JettyAdminServer.DEFAULT_PORT, secureClientPort, quorumCfgSection, null, null, writeDynamicConfigFile, null); } public MainThread(int myid, String quorumCfgSection, boolean writeDynamicConfigFile) throws IOException { this(myid, UNSET_STATIC_CLIENTPORT, quorumCfgSection, writeDynamicConfigFile); } public MainThread(int myid, int clientPort, String quorumCfgSection, boolean writeDynamicConfigFile) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, null, writeDynamicConfigFile); } public MainThread(int myid, int clientPort, String quorumCfgSection, String peerType, boolean writeDynamicConfigFile) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, peerType, writeDynamicConfigFile); } public MainThread(int myid, int clientPort, String quorumCfgSection, boolean writeDynamicConfigFile, String version) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, null, null, writeDynamicConfigFile, version); } public MainThread(int myid, int clientPort, String quorumCfgSection, String configs) throws IOException { this(myid, clientPort, JettyAdminServer.DEFAULT_PORT, quorumCfgSection, configs, null, true); } public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection, String configs) throws IOException { this(myid, clientPort, adminServerPort, quorumCfgSection, configs, null, true); } public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection, String configs, String peerType, boolean writeDynamicConfigFile) throws IOException { this(myid, clientPort, adminServerPort, quorumCfgSection, configs, peerType, writeDynamicConfigFile, null); } public MainThread(int myid, int clientPort, int adminServerPort, String quorumCfgSection, String configs, String peerType, boolean writeDynamicConfigFile, String version) throws IOException { this(myid, clientPort, adminServerPort, null, quorumCfgSection, configs, peerType, writeDynamicConfigFile, version); } public MainThread(int myid, int clientPort, int adminServerPort, Integer secureClientPort, String quorumCfgSection, String configs, String peerType, boolean writeDynamicConfigFile, String version) throws IOException { tmpDir = ClientBase.createTmpDir(); LOG.info("id = {} tmpDir = {} clientPort = {} adminServerPort = {}", myid, tmpDir, clientPort, adminServerPort); File dataDir = new File(tmpDir, "data"); if (!dataDir.mkdir()) { throw new IOException("Unable to mkdir " + dataDir); } confFile = new File(tmpDir, "zoo.cfg"); FileWriter fwriter = new FileWriter(confFile); fwriter.write("tickTime=4000\n"); fwriter.write("initLimit=10\n"); fwriter.write("syncLimit=5\n"); fwriter.write("connectToLearnerMasterLimit=5\n"); if (configs != null) { fwriter.write(configs); } // Convert windows path to UNIX to avoid problems with "\" String dir = PathUtils.normalizeFileSystemPath(dataDir.toString()); fwriter.write("dataDir=" + dir + "\n"); fwriter.write("admin.serverPort=" + adminServerPort + "\n"); // For backward compatibility test, some tests create dynamic configuration // without setting client port. // This could happen both in static file or dynamic file. if (clientPort != UNSET_STATIC_CLIENTPORT) { fwriter.write("clientPort=" + clientPort + "\n"); } if (secureClientPort != null) { fwriter.write("secureClientPort=" + secureClientPort + "\n"); } if (peerType != null) { fwriter.write("peerType=" + peerType + "\n"); } if (writeDynamicConfigFile) { String dynamicConfigFilename = createDynamicFile(quorumCfgSection, version); fwriter.write("dynamicConfigFile=" + dynamicConfigFilename + "\n"); } else { fwriter.write(quorumCfgSection); } fwriter.flush(); fwriter.close(); File myidFile = new File(dataDir, "myid"); fwriter = new FileWriter(myidFile); fwriter.write(Integer.toString(myid)); fwriter.flush(); fwriter.close(); ClientBase.createInitializeFile(dataDir); } private String createDynamicFile(String quorumCfgSection, String version) throws IOException { String filename = "zoo.cfg.dynamic"; if (version != null) { filename = filename + "." + version; } File dynamicConfigFile = new File(tmpDir, filename); String dynamicConfigFilename = PathUtils.normalizeFileSystemPath(dynamicConfigFile.toString()); FileWriter fDynamicConfigWriter = new FileWriter(dynamicConfigFile); fDynamicConfigWriter.write(quorumCfgSection); fDynamicConfigWriter.flush(); fDynamicConfigWriter.close(); return dynamicConfigFilename; } public File[] getDynamicFiles() { return getFilesWithPrefix("zoo.cfg.dynamic"); } public File[] getFilesWithPrefix(final String prefix) { return tmpDir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(prefix); } }); } public File getFileByName(String filename) { File f = new File(tmpDir.getPath(), filename); return f.isFile() ? f : null; } public void writeTempDynamicConfigFile(String nextQuorumCfgSection, String version) throws IOException { File nextDynamicConfigFile = new File(tmpDir, "zoo.cfg" + QuorumPeerConfig.nextDynamicConfigFileSuffix); FileWriter fwriter = new FileWriter(nextDynamicConfigFile); fwriter.write(nextQuorumCfgSection + "\n" + "version=" + version); fwriter.flush(); fwriter.close(); } public MainThread(int myid, int clientPort, String quorumCfgSection) throws IOException { this(myid, clientPort, quorumCfgSection, new HashMap<>()); } public MainThread(int myid, int clientPort, String quorumCfgSection, Map otherConfigs) throws IOException { this(myid, clientPort, quorumCfgSection, otherConfigs, 4000); } Thread currentThread; public synchronized void start() { main = getTestQPMain(); currentThread = new Thread(this); currentThread.start(); } /** * start the QuorumPeer with the passed TestQPMain * * @param testQPMain the TestQPMain to use */ public synchronized void start(final TestQPMain testQPMain) { main = testQPMain; currentThread = new Thread(this); currentThread.start(); } public TestQPMain getTestQPMain() { return new TestQPMain(); } public void run() { String[] args = new String[1]; args[0] = confFile.toString(); try { main.initializeAndRun(args); } catch (Exception e) { // test will still fail even though we just log/ignore LOG.error("unexpected exception in run", e); } finally { currentThread = null; } } public void shutdown() throws InterruptedException { Thread t = currentThread; if (t != null && t.isAlive()) { main.shutdown(); t.join(500); } } public void join(long timeout) throws InterruptedException { Thread t = currentThread; if (t != null) { t.join(timeout); } } public boolean isAlive() { Thread t = currentThread; return t != null && t.isAlive(); } public void reinitialize() throws IOException { File dataDir = main.quorumPeer.getTxnFactory().getDataLogDir(); ClientBase.recursiveDelete(dataDir); ClientBase.createInitializeFile(dataDir.getParentFile()); } public boolean isQuorumPeerRunning() { return main.quorumPeer != null; } public String getPropFromStaticFile(String key) throws IOException { Properties props = new Properties(); props.load(new FileReader(confFile)); return props.getProperty(key, ""); } public QuorumPeer getQuorumPeer() { return main.quorumPeer; } public void deleteBaseDir() { ClientBase.recursiveDelete(baseDir); } public int getMyid() { return myid; } public int getClientPort() { return clientPort; } public String getQuorumCfgSection() { return quorumCfgSection; } public Map getOtherConfigs() { return otherConfigs; } public File getConfFile() { return confFile; } } // This class holds the servers and clients for those servers public static class Servers { public MainThread[] mt; public ZooKeeper[] zk; public int[] clientPorts; public int[] adminPorts; public void shutDownAllServers() throws InterruptedException { for (MainThread t : mt) { t.shutdown(); } } public void restartAllServersAndClients(Watcher watcher) throws IOException, InterruptedException { int index = 0; for (MainThread t : mt) { if (!t.isAlive()) { System.setProperty("zookeeper.admin.serverPort", String.valueOf(adminPorts[index])); t.start(); index++; } } for (int i = 0; i < zk.length; i++) { restartClient(i, watcher); } } public void restartClient(int clientIndex, Watcher watcher) throws IOException, InterruptedException { if (zk[clientIndex] != null) { zk[clientIndex].close(); } zk[clientIndex] = new ZooKeeper( "127.0.0.1:" + clientPorts[clientIndex], ClientBase.CONNECTION_TIMEOUT, watcher); } public int findLeader() { for (int i = 0; i < mt.length; i++) { if (mt[i].main.quorumPeer.leader != null) { LOG.info("Leader is {}", i); return i; } } LOG.info("Cannot find Leader"); return -1; } public int findAnyFollower() { for (int i = 0; i < mt.length; i++) { if (mt[i].main.quorumPeer.follower != null) { LOG.info("Follower is {}", i); return i; } } LOG.info("Cannot find any follower"); return -1; } public int findAnyObserver() { for (int i = 0; i < mt.length; i++) { if (mt[i].main.quorumPeer.observer != null) { LOG.info("Observer is {}", i); return i; } } LOG.info("Cannot find any observer"); return -1; } } protected Servers LaunchServers(int numServers) throws IOException, InterruptedException { return LaunchServers(numServers, (Integer) null); } protected Servers LaunchServers(int numServers, Map otherConfigs) throws IOException, InterruptedException { return LaunchServers(numServers, 0, null, otherConfigs); } protected Servers LaunchServers(int numServers, Integer tickTime) throws IOException, InterruptedException { return LaunchServers(numServers, 0, tickTime); } protected Servers LaunchServers(int numServers, int numObservers, Integer tickTime) throws IOException, InterruptedException { return LaunchServers(numServers, numObservers, tickTime, new HashMap<>()); } /** * This is a helper function for launching a set of servers * * @param numServers the number of participant servers * @param numObservers the number of observer servers * @param tickTime A ticktime to pass to MainThread * @param otherConfigs any zoo.cfg configuration * @return * @throws IOException * @throws InterruptedException */ protected Servers LaunchServers(int numServers, int numObservers, Integer tickTime, Map otherConfigs) throws IOException, InterruptedException { int SERVER_COUNT = numServers + numObservers; QuorumPeerMainTest.Servers svrs = new QuorumPeerMainTest.Servers(); svrs.clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < SERVER_COUNT; i++) { svrs.clientPorts[i] = PortAssignment.unique(); String role = i < numServers ? "participant" : "observer"; sb.append(String.format("server.%d=127.0.0.1:%d:%d:%s;127.0.0.1:%d\n", i, PortAssignment.unique(), PortAssignment.unique(), role, svrs.clientPorts[i])); } svrs.adminPorts = new int[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { svrs.adminPorts[i] = PortAssignment.unique(); } String quorumCfgSection = sb.toString(); svrs.mt = new MainThread[SERVER_COUNT]; svrs.zk = new ZooKeeper[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { if (tickTime != null) { svrs.mt[i] = new MainThread(i, svrs.clientPorts[i], quorumCfgSection, otherConfigs, tickTime); } else { svrs.mt[i] = new MainThread(i, svrs.clientPorts[i], quorumCfgSection, otherConfigs); } System.setProperty("zookeeper.admin.serverPort", String.valueOf(svrs.adminPorts[i])); svrs.mt[i].start(); svrs.restartClient(i, this); } waitForAll(svrs, ZooKeeper.States.CONNECTED); return svrs; } public static void waitForOne(ZooKeeper zk, ZooKeeper.States state) throws InterruptedException { int iterations = ClientBase.CONNECTION_TIMEOUT / 500; while (zk.getState() != state) { if (iterations-- == 0) { throw new RuntimeException("Waiting too long " + zk.getState() + " != " + state); } Thread.sleep(500); } } protected void waitForAll(Servers servers, ZooKeeper.States state) throws InterruptedException { waitForAll(servers.zk, state); } public static void waitForAll(ZooKeeper[] zks, ZooKeeper.States state) throws InterruptedException { int iterations = ClientBase.CONNECTION_TIMEOUT / 1000; boolean someoneNotConnected = true; while (someoneNotConnected) { if (iterations-- == 0) { logStates(zks); ClientBase.logAllStackTraces(); throw new RuntimeException("Waiting too long"); } someoneNotConnected = false; for (ZooKeeper zk : zks) { if (zk.getState() != state) { someoneNotConnected = true; break; } } Thread.sleep(1000); } } public static void logStates(ZooKeeper[] zks) { StringBuilder sbBuilder = new StringBuilder("Connection States: {"); for (int i = 0; i < zks.length; i++) { sbBuilder.append(i + " : " + zks[i].getState() + ", "); } sbBuilder.append('}'); LOG.error(sbBuilder.toString()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032743 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumRequestPipelineTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumReque0100644 0000000 0000000 00000020012 15051152474 034373 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.QuorumBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class QuorumRequestPipelineTest extends QuorumBase { protected final CountDownLatch callComplete = new CountDownLatch(1); protected boolean complete = false; protected static final String PARENT_PATH = "/foo"; protected static final Set CHILDREN = new HashSet<>(Arrays.asList("1", "2", "3")); protected static final String AUTH_PROVIDER = "digest"; protected static final byte[] AUTH = "hello".getBytes(); protected static final byte[] DATA = "Hint Water".getBytes(); protected TestableZooKeeper zkClient; public static Stream data() throws Exception { return Stream.of( Arguments.of(ServerState.LEADING), Arguments.of(ServerState.FOLLOWING), Arguments.of(ServerState.OBSERVING)); } @BeforeEach @Override public void setUp() { //since parameterized test methods need a parameterized setUp method //the inherited method has to be overridden with an empty function body } public void setUp(ServerState serverState) throws Exception { CountdownWatcher clientWatch = new CountdownWatcher(); super.setUp(true, true); zkClient = createClient(clientWatch, getPeersMatching(serverState)); zkClient.addAuthInfo(AUTH_PROVIDER, AUTH); clientWatch.waitForConnected(CONNECTION_TIMEOUT); } @AfterEach public void tearDown() throws Exception { zkClient.close(); super.tearDown(); } private Stat create2EmptyNode(TestableZooKeeper zkClient, String path) throws Exception { Stat stat = new Stat(); zkClient.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); return stat; } @ParameterizedTest @MethodSource("data") public void testCreate(ServerState serverState) throws Exception { setUp(serverState); zkClient.create(PARENT_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertArrayEquals(DATA, zkClient.getData(PARENT_PATH, false, null), String.format("%s Node created (create) with expected value", serverState)); } @ParameterizedTest @MethodSource("data") public void testCreate2(ServerState serverState) throws Exception { setUp(serverState); zkClient.create(PARENT_PATH, DATA, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); assertArrayEquals(DATA, zkClient.getData(PARENT_PATH, false, null), String.format("%s Node created (create2) with expected value", serverState)); } @ParameterizedTest @MethodSource("data") public void testDelete(ServerState serverState) throws Exception { setUp(serverState); create2EmptyNode(zkClient, PARENT_PATH); zkClient.delete(PARENT_PATH, -1); assertNull(zkClient.exists(PARENT_PATH, false), String.format("%s Node no longer exists", serverState)); } @ParameterizedTest @MethodSource("data") public void testExists(ServerState serverState) throws Exception { setUp(serverState); Stat stat = create2EmptyNode(zkClient, PARENT_PATH); assertEquals(stat, zkClient.exists(PARENT_PATH, false), String.format("%s Exists returns correct node stat", serverState)); } @ParameterizedTest @MethodSource("data") public void testSetAndGetData(ServerState serverState) throws Exception { setUp(serverState); create2EmptyNode(zkClient, PARENT_PATH); zkClient.setData(PARENT_PATH, DATA, -1); assertArrayEquals(DATA, zkClient.getData(PARENT_PATH, false, null), String.format("%s Node updated with expected value", serverState)); } @ParameterizedTest @MethodSource("data") public void testSetAndGetACL(ServerState serverState) throws Exception { setUp(serverState); create2EmptyNode(zkClient, PARENT_PATH); assertEquals(Ids.OPEN_ACL_UNSAFE, zkClient.getACL(PARENT_PATH, new Stat()), String.format("%s Node has open ACL", serverState)); zkClient.setACL(PARENT_PATH, Ids.READ_ACL_UNSAFE, -1); assertEquals(Ids.READ_ACL_UNSAFE, zkClient.getACL(PARENT_PATH, new Stat()), String.format("%s Node has world read-only ACL", serverState)); } @ParameterizedTest @MethodSource("data") public void testSetAndGetChildren(ServerState serverState) throws Exception { setUp(serverState); create2EmptyNode(zkClient, PARENT_PATH); for (String child : CHILDREN) { create2EmptyNode(zkClient, PARENT_PATH + "/" + child); } assertEquals(CHILDREN, new HashSet(zkClient.getChildren(PARENT_PATH, false)), String.format("%s Parent has expected children", serverState)); } @ParameterizedTest @MethodSource("data") public void testSetAndGetChildren2(ServerState serverState) throws Exception { setUp(serverState); create2EmptyNode(zkClient, PARENT_PATH); for (String child : CHILDREN) { create2EmptyNode(zkClient, PARENT_PATH + "/" + child); } assertEquals(CHILDREN, new HashSet(zkClient.getChildren(PARENT_PATH, false, null)), String.format("%s Parent has expected children", serverState)); } @ParameterizedTest @MethodSource("data") public void testSync(ServerState serverState) throws Exception { setUp(serverState); complete = false; create2EmptyNode(zkClient, PARENT_PATH); VoidCallback onSync = new VoidCallback() { @Override public void processResult(int rc, String path, Object ctx) { complete = true; callComplete.countDown(); } }; zkClient.sync(PARENT_PATH, onSync, null); callComplete.await(30, TimeUnit.SECONDS); assertTrue(complete, String.format("%s Sync completed", serverState)); } @ParameterizedTest @MethodSource("data") public void testSynchronousSync(ServerState serverState) throws Exception { setUp(serverState); create2EmptyNode(zkClient, PARENT_PATH); ForkJoinTask task = ForkJoinPool.commonPool().submit(() -> { zkClient.sync(PARENT_PATH); return null; }); task.get(30, TimeUnit.SECONDS); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000165 15051152474 032754 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumSSLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumSSLTe0100644 0000000 0000000 00000125377 15051152474 034270 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.apache.zookeeper.test.ClientBase.createTmpDir; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.math.BigInteger; import java.net.InetSocketAddress; import java.net.URLDecoder; import java.nio.file.Path; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLServerSocketFactory; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.QuorumX509Util; import org.apache.zookeeper.common.SecretUtilsTest; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.test.ClientBase; import org.bouncycastle.asn1.ocsp.OCSPResponse; import org.bouncycastle.asn1.ocsp.OCSPResponseStatus; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.asn1.x509.AuthorityInformationAccess; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.CRLDistPoint; import org.bouncycastle.asn1.x509.CRLNumber; import org.bouncycastle.asn1.x509.CRLReason; import org.bouncycastle.asn1.x509.DistributionPoint; import org.bouncycastle.asn1.x509.DistributionPointName; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.cert.X509CRLHolder; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509ExtensionUtils; import org.bouncycastle.cert.X509v2CRLBuilder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.bc.BcX509ExtensionUtils; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.cert.ocsp.BasicOCSPResp; import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder; import org.bouncycastle.cert.ocsp.CertificateID; import org.bouncycastle.cert.ocsp.CertificateStatus; import org.bouncycastle.cert.ocsp.OCSPException; import org.bouncycastle.cert.ocsp.OCSPReq; import org.bouncycastle.cert.ocsp.OCSPResp; import org.bouncycastle.cert.ocsp.OCSPRespBuilder; import org.bouncycastle.cert.ocsp.Req; import org.bouncycastle.cert.ocsp.UnknownStatus; import org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder; import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.MiscPEMGenerator; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DigestCalculator; import org.bouncycastle.operator.OperatorException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.util.io.pem.PemWriter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; public class QuorumSSLTest extends QuorumPeerTestBase { @Retention(RetentionPolicy.RUNTIME) @ParameterizedTest(name = "fipsEnabled = {0}") @ValueSource(booleans = { false, true}) private @interface TestBothFipsModes { } @Retention(RetentionPolicy.RUNTIME) @ParameterizedTest(name = "fipsEnabled = {0}") @ValueSource(booleans = { false }) private @interface TestNoFipsOnly { } private static final String SSL_QUORUM_ENABLED = "sslQuorum=true\n"; private static final String PORT_UNIFICATION_ENABLED = "portUnification=true\n"; private static final String PORT_UNIFICATION_DISABLED = "portUnification=false\n"; private static final char[] PASSWORD = "testpass".toCharArray(); private static final String HOSTNAME = "localhost"; private QuorumX509Util quorumX509Util; private MainThread q1; private MainThread q2; private MainThread q3; private int clientPortQp1; private int clientPortQp2; private int clientPortQp3; private String tmpDir; private String quorumConfiguration; private String validKeystorePath; private String truststorePath; private KeyPair rootKeyPair; private X509Certificate rootCertificate; private KeyPair defaultKeyPair; private ContentSigner contentSigner; private Date certStartTime; private Date certEndTime; @BeforeEach public void setup() throws Exception { quorumX509Util = new QuorumX509Util(); ClientBase.setupTestEnv(); tmpDir = createTmpDir().getAbsolutePath(); clientPortQp1 = PortAssignment.unique(); clientPortQp2 = PortAssignment.unique(); clientPortQp3 = PortAssignment.unique(); validKeystorePath = tmpDir + "/valid.jks"; truststorePath = tmpDir + "/truststore.jks"; quorumConfiguration = generateQuorumConfiguration(); Security.addProvider(new BouncyCastleProvider()); certStartTime = new Date(); Calendar cal = Calendar.getInstance(); cal.setTime(certStartTime); cal.add(Calendar.YEAR, 1); certEndTime = cal.getTime(); rootKeyPair = createKeyPair(); contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(rootKeyPair.getPrivate()); rootCertificate = createSelfSignedCertifcate(rootKeyPair); // Write the truststore KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, PASSWORD); trustStore.setCertificateEntry(rootCertificate.getSubjectDN().toString(), rootCertificate); FileOutputStream outputStream = new FileOutputStream(truststorePath); trustStore.store(outputStream, PASSWORD); outputStream.flush(); outputStream.close(); defaultKeyPair = createKeyPair(); X509Certificate validCertificate = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), HOSTNAME, "127.0.0.1", null, null); writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); setSSLSystemProperties(); } private void writeKeystore(X509Certificate certificate, KeyPair entityKeyPair, String path) throws Exception { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null, PASSWORD); keyStore.setKeyEntry("alias", entityKeyPair.getPrivate(), PASSWORD, new Certificate[]{certificate}); FileOutputStream outputStream = new FileOutputStream(path); keyStore.store(outputStream, PASSWORD); outputStream.flush(); outputStream.close(); } private class OCSPHandler implements HttpHandler { private X509Certificate revokedCert; // Builds an OCSPHandler that responds with a good status for all certificates // except revokedCert. public OCSPHandler(X509Certificate revokedCert) { this.revokedCert = revokedCert; } @Override public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException { byte[] responseBytes; try { String uri = httpExchange.getRequestURI().toString(); LOG.info("OCSP request: {} {}", httpExchange.getRequestMethod(), uri); httpExchange.getRequestHeaders().entrySet().forEach((e) -> { LOG.info("OCSP request header: {} {}", e.getKey(), e.getValue()); }); InputStream request = httpExchange.getRequestBody(); byte[] requestBytes = new byte[10000]; int len = request.read(requestBytes); LOG.info("OCSP request size {}", len); if (len < 0) { String removedUriEncoding = URLDecoder.decode(uri.substring(1), "utf-8"); LOG.info("OCSP request from URI no encoding {}", removedUriEncoding); requestBytes = Base64.getDecoder().decode(removedUriEncoding); } OCSPReq ocspRequest = new OCSPReq(requestBytes); Req[] requestList = ocspRequest.getRequestList(); LOG.info("requestList {}", Arrays.toString(requestList)); DigestCalculator digestCalculator = new JcaDigestCalculatorProviderBuilder().build().get(CertificateID.HASH_SHA1); BasicOCSPRespBuilder responseBuilder = new JcaBasicOCSPRespBuilder(rootKeyPair.getPublic(), digestCalculator); for (Req req : requestList) { CertificateID certId = req.getCertID(); CertificateID revokedCertId = new JcaCertificateID(digestCalculator, rootCertificate, revokedCert.getSerialNumber()); CertificateStatus certificateStatus; if (revokedCertId.equals(certId)) { certificateStatus = new UnknownStatus(); } else { certificateStatus = CertificateStatus.GOOD; } LOG.info("addResponse {} {}", certId, certificateStatus); responseBuilder.addResponse(certId, certificateStatus, null); } X509CertificateHolder[] chain = new X509CertificateHolder[]{new JcaX509CertificateHolder(rootCertificate)}; ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(rootKeyPair.getPrivate()); BasicOCSPResp ocspResponse = responseBuilder.build(signer, chain, Calendar.getInstance().getTime()); LOG.info("response {}", ocspResponse); responseBytes = new OCSPRespBuilder().build(OCSPRespBuilder.SUCCESSFUL, ocspResponse).getEncoded(); LOG.error("OCSP server response OK"); } catch (OperatorException | CertificateEncodingException | OCSPException exception) { LOG.error("Internal OCSP server error", exception); responseBytes = new OCSPResp(new OCSPResponse(new OCSPResponseStatus(OCSPRespBuilder.INTERNAL_ERROR), null)).getEncoded(); } catch (Throwable exception) { LOG.error("Internal OCSP server error", exception); responseBytes = new OCSPResp(new OCSPResponse(new OCSPResponseStatus(OCSPRespBuilder.INTERNAL_ERROR), null)).getEncoded(); } Headers rh = httpExchange.getResponseHeaders(); rh.set("Content-Type", "application/ocsp-response"); httpExchange.sendResponseHeaders(200, responseBytes.length); OutputStream os = httpExchange.getResponseBody(); os.write(responseBytes); os.close(); } } private X509Certificate createSelfSignedCertifcate(KeyPair keyPair) throws Exception { X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); nameBuilder.addRDN(BCStyle.CN, HOSTNAME); BigInteger serialNumber = new BigInteger(128, new Random()); JcaX509v3CertificateBuilder jcaX509v3CertificateBuilder = new JcaX509v3CertificateBuilder( nameBuilder.build(), serialNumber, certStartTime, certEndTime, nameBuilder.build(), keyPair.getPublic()); X509v3CertificateBuilder certificateBuilder = jcaX509v3CertificateBuilder .addExtension(Extension.basicConstraints, true, new BasicConstraints(0)) .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner)); } private void buildCRL(X509Certificate x509Certificate, String crlPath) throws Exception { X509v2CRLBuilder builder = new JcaX509v2CRLBuilder(x509Certificate.getIssuerX500Principal(), certStartTime); builder.addCRLEntry(x509Certificate.getSerialNumber(), certStartTime, CRLReason.cACompromise); builder.setNextUpdate(certEndTime); builder.addExtension(Extension.authorityKeyIdentifier, false, new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(rootCertificate)); builder.addExtension(Extension.cRLNumber, false, new CRLNumber(new BigInteger("1000"))); X509CRLHolder cRLHolder = builder.build(contentSigner); PemWriter pemWriter = new PemWriter(new FileWriter(crlPath)); pemWriter.writeObject(new MiscPEMGenerator(cRLHolder)); pemWriter.flush(); pemWriter.close(); } public X509Certificate buildEndEntityCert( KeyPair keyPair, X509Certificate caCert, PrivateKey caPrivateKey, String hostname, String ipAddress, String crlPath, Integer ocspPort) throws Exception { X509CertificateHolder holder = new JcaX509CertificateHolder(caCert); ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPrivateKey); List generalNames = new ArrayList<>(); if (hostname != null) { generalNames.add(new GeneralName(GeneralName.dNSName, hostname)); } if (ipAddress != null) { generalNames.add(new GeneralName(GeneralName.iPAddress, ipAddress)); } SubjectPublicKeyInfo entityKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo( PublicKeyFactory.createKey(keyPair.getPublic().getEncoded())); X509ExtensionUtils extensionUtils = new BcX509ExtensionUtils(); JcaX509v3CertificateBuilder jcaX509v3CertificateBuilder = new JcaX509v3CertificateBuilder( holder.getSubject(), new BigInteger(128, new Random()), certStartTime, certEndTime, new X500Name("CN=Test End Entity Certificate"), keyPair.getPublic()); X509v3CertificateBuilder certificateBuilder = jcaX509v3CertificateBuilder .addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(holder)) .addExtension(Extension.subjectKeyIdentifier, false, extensionUtils.createSubjectKeyIdentifier(entityKeyInfo)) .addExtension(Extension.basicConstraints, true, new BasicConstraints(false)) .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment)); if (!generalNames.isEmpty()) { certificateBuilder.addExtension( Extension.subjectAlternativeName, true, new GeneralNames(generalNames.toArray(new GeneralName[]{}))); } if (crlPath != null) { DistributionPointName distPointOne = new DistributionPointName( new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, "file://" + crlPath))); certificateBuilder.addExtension( Extension.cRLDistributionPoints, false, new CRLDistPoint(new DistributionPoint[]{new DistributionPoint(distPointOne, null, null)})); } if (ocspPort != null) { certificateBuilder.addExtension( Extension.authorityInfoAccess, false, new AuthorityInformationAccess( X509ObjectIdentifiers.ocspAccessMethod, new GeneralName(GeneralName.uniformResourceIdentifier, "http://" + hostname + ":" + ocspPort))); } return new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(signer)); } private KeyPair createKeyPair() throws NoSuchProviderException, NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); keyPairGenerator.initialize(4096); KeyPair keyPair = keyPairGenerator.genKeyPair(); return keyPair; } private String generateQuorumConfiguration() { StringBuilder sb = new StringBuilder(); int portQp1 = PortAssignment.unique(); int portQp2 = PortAssignment.unique(); int portQp3 = PortAssignment.unique(); int portLe1 = PortAssignment.unique(); int portLe2 = PortAssignment.unique(); int portLe3 = PortAssignment.unique(); sb.append(String.format("server.1=127.0.0.1:%d:%d;%d\n", portQp1, portLe1, clientPortQp1)); sb.append(String.format("server.2=127.0.0.1:%d:%d;%d\n", portQp2, portLe2, clientPortQp2)); sb.append(String.format("server.3=127.0.0.1:%d:%d;%d\n", portQp3, portLe3, clientPortQp3)); return sb.toString(); } private String generateMultiAddressQuorumConfiguration() { StringBuilder sb = new StringBuilder(); int portQp1a = PortAssignment.unique(); int portQp1b = PortAssignment.unique(); int portQp2a = PortAssignment.unique(); int portQp2b = PortAssignment.unique(); int portQp3a = PortAssignment.unique(); int portQp3b = PortAssignment.unique(); int portLe1a = PortAssignment.unique(); int portLe1b = PortAssignment.unique(); int portLe2a = PortAssignment.unique(); int portLe2b = PortAssignment.unique(); int portLe3a = PortAssignment.unique(); int portLe3b = PortAssignment.unique(); sb.append(String.format("server.1=127.0.0.1:%d:%d|127.0.0.1:%d:%d;%d\n", portQp1a, portLe1a, portQp1b, portLe1b, clientPortQp1)); sb.append(String.format("server.2=127.0.0.1:%d:%d|127.0.0.1:%d:%d;%d\n", portQp2a, portLe2a, portQp2b, portLe2b, clientPortQp2)); sb.append(String.format("server.3=127.0.0.1:%d:%d|127.0.0.1:%d:%d;%d\n", portQp3a, portLe3a, portQp3b, portLe3b, clientPortQp3)); return sb.toString(); } public void setSSLSystemProperties() { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), validKeystorePath); System.setProperty(quorumX509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty(quorumX509Util.getSslTruststoreLocationProperty(), truststorePath); System.setProperty(quorumX509Util.getSslTruststorePasswdProperty(), "testpass"); } @AfterEach public void cleanUp() throws Exception { System.clearProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED); clearSSLSystemProperties(); if (q1 != null) { q1.shutdown(); } if (q2 != null) { q2.shutdown(); } if (q3 != null) { q3.shutdown(); } Security.removeProvider("BC"); quorumX509Util.close(); } private void clearSSLSystemProperties() { System.clearProperty(quorumX509Util.getSslKeystoreLocationProperty()); System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); System.clearProperty(quorumX509Util.getSslKeystorePasswdPathProperty()); System.clearProperty(quorumX509Util.getSslTruststoreLocationProperty()); System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); System.clearProperty(quorumX509Util.getSslTruststorePasswdPathProperty()); System.clearProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty()); System.clearProperty(quorumX509Util.getSslOcspEnabledProperty()); System.clearProperty(quorumX509Util.getSslCrlEnabledProperty()); System.clearProperty(quorumX509Util.getCipherSuitesProperty()); System.clearProperty(quorumX509Util.getSslProtocolProperty()); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testQuorumSSL(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); clearSSLSystemProperties(); // This server should fail to join the quorum as it is not using ssl. q3 = new MainThread(3, clientPortQp3, quorumConfiguration); q3.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testQuorumSSL_withPasswordFromFile(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); final Path secretFile = SecretUtilsTest.createSecretFile(String.valueOf(PASSWORD)); System.clearProperty(quorumX509Util.getSslKeystorePasswdProperty()); System.setProperty(quorumX509Util.getSslKeystorePasswdPathProperty(), secretFile.toString()); System.clearProperty(quorumX509Util.getSslTruststorePasswdProperty()); System.setProperty(quorumX509Util.getSslTruststorePasswdPathProperty(), secretFile.toString()); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q3 = new MainThread(3, clientPortQp3, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testQuorumSSLWithMultipleAddresses(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); quorumConfiguration = generateMultiAddressQuorumConfiguration(); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); clearSSLSystemProperties(); // This server should fail to join the quorum as it is not using ssl. q3 = new MainThread(3, clientPortQp3, quorumConfiguration); q3.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testRollingUpgrade(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); // Form a quorum without ssl q1 = new MainThread(1, clientPortQp1, quorumConfiguration); q2 = new MainThread(2, clientPortQp2, quorumConfiguration); q3 = new MainThread(3, clientPortQp3, quorumConfiguration); Map members = new HashMap<>(); members.put(clientPortQp1, q1); members.put(clientPortQp2, q2); members.put(clientPortQp3, q3); for (MainThread member : members.values()) { member.start(); } for (int clientPort : members.keySet()) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT)); } // Set SSL system properties and port unification, begin restarting servers setSSLSystemProperties(); stopAppendConfigRestartAll(members, PORT_UNIFICATION_ENABLED); stopAppendConfigRestartAll(members, SSL_QUORUM_ENABLED); stopAppendConfigRestartAll(members, PORT_UNIFICATION_DISABLED); } private void stopAppendConfigRestartAll(Map members, String config) throws Exception { for (Map.Entry entry : members.entrySet()) { int clientPort = entry.getKey(); MainThread member = entry.getValue(); member.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT)); FileWriter fileWriter = new FileWriter(member.getConfFile(), true); fileWriter.write(config); fileWriter.flush(); fileWriter.close(); member.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT)); } } @TestNoFipsOnly @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testHostnameVerificationWithInvalidHostname(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); String badhostnameKeystorePath = tmpDir + "/badhost.jks"; X509Certificate badHostCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), "bleepbloop", null, null, null); writeKeystore(badHostCert, defaultKeyPair, badhostnameKeystorePath); testHostnameVerification(badhostnameKeystorePath, false); } @TestNoFipsOnly @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testHostnameVerificationWithInvalidIPAddress(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); String badhostnameKeystorePath = tmpDir + "/badhost.jks"; X509Certificate badHostCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), null, "140.211.11.105", null, null); writeKeystore(badHostCert, defaultKeyPair, badhostnameKeystorePath); testHostnameVerification(badhostnameKeystorePath, false); } @TestNoFipsOnly @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testHostnameVerificationWithInvalidIpAddressAndInvalidHostname(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); String badhostnameKeystorePath = tmpDir + "/badhost.jks"; X509Certificate badHostCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), "bleepbloop", "140.211.11.105", null, null); writeKeystore(badHostCert, defaultKeyPair, badhostnameKeystorePath); testHostnameVerification(badhostnameKeystorePath, false); } @TestNoFipsOnly @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testHostnameVerificationForInvalidMultiAddressServerConfig(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); quorumConfiguration = generateMultiAddressQuorumConfiguration(); String badhostnameKeystorePath = tmpDir + "/badhost.jks"; X509Certificate badHostCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), "bleepbloop", "140.211.11.105", null, null); writeKeystore(badHostCert, defaultKeyPair, badhostnameKeystorePath); testHostnameVerification(badhostnameKeystorePath, false); } @TestNoFipsOnly @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testHostnameVerificationWithInvalidIpAddressAndValidHostname(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); String badhostnameKeystorePath = tmpDir + "/badhost.jks"; X509Certificate badHostCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), "localhost", "140.211.11.105", null, null); writeKeystore(badHostCert, defaultKeyPair, badhostnameKeystorePath); testHostnameVerification(badhostnameKeystorePath, true); } @TestNoFipsOnly @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testHostnameVerificationWithValidIpAddressAndInvalidHostname(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); String badhostnameKeystorePath = tmpDir + "/badhost.jks"; X509Certificate badHostCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), "bleepbloop", "127.0.0.1", null, null); writeKeystore(badHostCert, defaultKeyPair, badhostnameKeystorePath); testHostnameVerification(badhostnameKeystorePath, true); } /** * @param keystorePath The keystore to use * @param expectSuccess True for expecting the keystore to pass hostname verification, false for expecting failure * @throws Exception */ private void testHostnameVerification(String keystorePath, boolean expectSuccess) throws Exception { System.setProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty(), "false"); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), keystorePath); // This server should join successfully q3 = new MainThread(3, clientPortQp3, quorumConfiguration, SSL_QUORUM_ENABLED); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); q1.shutdown(); q2.shutdown(); q3.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); setSSLSystemProperties(); System.clearProperty(quorumX509Util.getSslHostnameVerificationEnabledProperty()); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), keystorePath); q3.start(); assertEquals( expectSuccess, ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testCertificateRevocationList(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); String revokedInCRLKeystorePath = tmpDir + "/crl_revoked.jks"; String crlPath = tmpDir + "/crl.pem"; X509Certificate revokedInCRLCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), HOSTNAME, null, crlPath, null); writeKeystore(revokedInCRLCert, defaultKeyPair, revokedInCRLKeystorePath); buildCRL(revokedInCRLCert, crlPath); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), revokedInCRLKeystorePath); // This server should join successfully q3 = new MainThread(3, clientPortQp3, quorumConfiguration, SSL_QUORUM_ENABLED); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); q1.shutdown(); q2.shutdown(); q3.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); setSSLSystemProperties(); System.setProperty(quorumX509Util.getSslCrlEnabledProperty(), "true"); X509Certificate validCertificate = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), HOSTNAME, null, crlPath, null); writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), revokedInCRLKeystorePath); q3.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testOCSP(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); Integer ocspPort = PortAssignment.unique(); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); String revokedInOCSPKeystorePath = tmpDir + "/ocsp_revoked.jks"; X509Certificate revokedInOCSPCert = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), HOSTNAME, null, null, ocspPort); writeKeystore(revokedInOCSPCert, defaultKeyPair, revokedInOCSPKeystorePath); HttpServer ocspServer = HttpServer.create(new InetSocketAddress(ocspPort), 0); try { ocspServer.createContext("/", new OCSPHandler(revokedInOCSPCert)); ocspServer.start(); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), revokedInOCSPKeystorePath); // This server should join successfully q3 = new MainThread(3, clientPortQp3, quorumConfiguration, SSL_QUORUM_ENABLED); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); q1.shutdown(); q2.shutdown(); q3.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); setSSLSystemProperties(); System.setProperty(quorumX509Util.getSslOcspEnabledProperty(), "true"); X509Certificate validCertificate = buildEndEntityCert( defaultKeyPair, rootCertificate, rootKeyPair.getPrivate(), HOSTNAME, null, null, ocspPort); writeKeystore(validCertificate, defaultKeyPair, validKeystorePath); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); System.setProperty(quorumX509Util.getSslKeystoreLocationProperty(), revokedInOCSPKeystorePath); q3.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } finally { ocspServer.stop(0); } } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testCipherSuites(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); // Get default cipher suites from JDK SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); List defaultCiphers = new ArrayList<>(); for (String cipher : ssf.getDefaultCipherSuites()) { if (!cipher.matches(".*EMPTY.*") && cipher.startsWith("TLS") && cipher.contains("RSA")) { defaultCiphers.add(cipher); } } if (defaultCiphers.size() < 2) { fail("JDK has to support at least 2 valid (RSA) cipher suites for this test to run"); } // Use them all except one to build the ensemble String suitesOfEnsemble = String.join(",", defaultCiphers.subList(1, defaultCiphers.size())); System.setProperty(quorumX509Util.getCipherSuitesProperty(), suitesOfEnsemble); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); // Use the odd one out for the client String suiteOfClient = defaultCiphers.get(0); System.setProperty(quorumX509Util.getCipherSuitesProperty(), suiteOfClient); // This server should fail to join the quorum as it is not using one of the supported suites from the other // quorum members q3 = new MainThread(3, clientPortQp3, quorumConfiguration, SSL_QUORUM_ENABLED); q3.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } @TestBothFipsModes @Timeout(value = 5, unit = TimeUnit.MINUTES) public void testProtocolVersion(boolean fipsEnabled) throws Exception { System.setProperty(quorumX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); System.setProperty(quorumX509Util.getSslProtocolProperty(), "TLSv1.2"); q1 = new MainThread(1, clientPortQp1, quorumConfiguration, SSL_QUORUM_ENABLED); q2 = new MainThread(2, clientPortQp2, quorumConfiguration, SSL_QUORUM_ENABLED); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp1, CONNECTION_TIMEOUT)); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp2, CONNECTION_TIMEOUT)); System.setProperty(quorumX509Util.getSslProtocolProperty(), "TLSv1.1"); // This server should fail to join the quorum as it is not using TLSv1.2 q3 = new MainThread(3, clientPortQp3, quorumConfiguration, SSL_QUORUM_ENABLED); q3.start(); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPortQp3, CONNECTION_TIMEOUT)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032743 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumServerConfigBuilder.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumServe0100644 0000000 0000000 00000012354 15051152474 034410 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.apache.zookeeper.PortAssignment; /* * Helper class to build / change Quorum Config String, like: * server.1=127.0.0.1:11228:11231|127.0.0.1:11230:11229;11227 * server.2=127.0.0.1:11338:11331|127.0.0.1:11330:11229;11337 * */ public class QuorumServerConfigBuilder { // map of (serverId -> clientPort) private final Map clientIds = new HashMap<>(); // map of (serverId -> (ServerAddress=host,quorumPort,electionPort) ) private final Map> serverAddresses = new HashMap<>(); private final String hostName; private final int numberOfServers; public QuorumServerConfigBuilder(String hostName, int numberOfServers, int numberOfServerAddresses) { this.numberOfServers = numberOfServers; this.hostName = hostName; for (int serverId = 0; serverId < numberOfServers; serverId++) { clientIds.put(serverId, PortAssignment.unique()); List addresses = new ArrayList<>(); serverAddresses.put(serverId, addresses); for (int serverAddressId = 0; serverAddressId < numberOfServerAddresses; serverAddressId++) { addresses.add(new ServerAddress(hostName)); } } } public QuorumServerConfigBuilder(QuorumServerConfigBuilder otherBuilder) { this.numberOfServers = otherBuilder.clientIds.size(); this.clientIds.putAll(otherBuilder.clientIds); this.hostName = otherBuilder.hostName; for (int i : otherBuilder.serverAddresses.keySet()) { List clonedServerAddresses = otherBuilder.serverAddresses.get(i).stream() .map(ServerAddress::clone).collect(Collectors.toList()); this.serverAddresses.put(i, clonedServerAddresses); } } public int getClientPort(int serverId) { return clientIds.get(serverId); } public ServerAddress getServerAddress(int serverId, int addressId) { return serverAddresses.get(serverId).get(addressId); } public QuorumServerConfigBuilder changeHostName(int serverId, int addressId, String hostName) { serverAddresses.get(serverId).get(addressId).setHost(hostName); return this; } public QuorumServerConfigBuilder changeQuorumPort(int serverId, int addressId, int quorumPort) { serverAddresses.get(serverId).get(addressId).setQuorumPort(quorumPort); return this; } public QuorumServerConfigBuilder changeElectionPort(int serverId, int addressId, int electionPort) { serverAddresses.get(serverId).get(addressId).setElectionPort(electionPort); return this; } public QuorumServerConfigBuilder addNewServerAddress(int serverId) { serverAddresses.get(serverId).add(new ServerAddress(hostName)); return this; } public QuorumServerConfigBuilder deleteLastServerAddress(int serverId) { serverAddresses.get(serverId).remove(serverAddresses.get(serverId).size() - 1); return this; } public String build() { return String.join("\n", buildAsStringList()); } public List buildAsStringList() { List result = new ArrayList<>(numberOfServers); for (int serverId = 0; serverId < numberOfServers; serverId++) { String s = serverAddresses.get(serverId).stream() .map(ServerAddress::toString) .collect(Collectors.joining("|")); result.add(String.format("server.%d=%s;%d", serverId, s, clientIds.get(serverId))); } return result; } public static class ServerAddress { private String host; private int quorumPort; private int electionPort; private ServerAddress(String host) { this(host, PortAssignment.unique(), PortAssignment.unique()); } private ServerAddress(String host, int quorumPort, int electionPort) { this.host = host; this.quorumPort = quorumPort; this.electionPort = electionPort; } public String getHost() { return host; } private void setHost(String host) { this.host = host; } private void setQuorumPort(int quorumPort) { this.quorumPort = quorumPort; } private void setElectionPort(int electionPort) { this.electionPort = electionPort; } @Override public ServerAddress clone() { return new ServerAddress(host, quorumPort, electionPort); } @Override public String toString() { return String.format("%s:%d:%d", host, quorumPort, electionPort); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000170 15051152474 032750 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumServe0100644 0000000 0000000 00000015272 15051152474 034412 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.net.InetSocketAddress; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class QuorumServerTest extends ZKTestCase { private String ipv6n1 = "[2500:0:0:0:0:0:1:0]"; private String ipv6n2 = "[2600:0:0:0:0:0:1:0]"; private String ipv4config = "127.0.0.1:1234:1236"; @AfterEach public void tearDown() { System.clearProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED); } @Test public void testToString() throws ConfigException { String provided = ipv4config + ":participant;0.0.0.0:1237"; String expected = ipv4config + ":participant;0.0.0.0:1237"; QuorumServer qs = new QuorumServer(0, provided); assertEquals(expected, qs.toString(), "Use IP address"); provided = ipv4config + ";0.0.0.0:1237"; expected = ipv4config + ":participant;0.0.0.0:1237"; qs = new QuorumServer(0, provided); assertEquals(expected, qs.toString(), "Type unspecified"); provided = ipv4config + ":observer;0.0.0.0:1237"; expected = ipv4config + ":observer;0.0.0.0:1237"; qs = new QuorumServer(0, provided); assertEquals(expected, qs.toString(), "Observer type"); provided = ipv4config + ":participant;1237"; expected = ipv4config + ":participant;0.0.0.0:1237"; qs = new QuorumServer(0, provided); assertEquals(expected, qs.toString(), "Client address unspecified"); provided = ipv4config + ":participant;1.2.3.4:1237"; expected = ipv4config + ":participant;1.2.3.4:1237"; qs = new QuorumServer(0, provided); assertEquals(expected, qs.toString(), "Client address specified"); provided = "example.com:1234:1236:participant;1237"; expected = "example.com:1234:1236:participant;0.0.0.0:1237"; qs = new QuorumServer(0, provided); assertEquals(expected, qs.toString(), "Use hostname"); } @Test public void constructionUnderstandsIpv6LiteralsInServerConfig() throws ConfigException { String config = "[::1]:1234:1236:participant"; QuorumServer qs = new QuorumServer(0, config); assertEquals("[0:0:0:0:0:0:0:1]:1234:1236:participant", qs.toString()); } @Test public void constructionUnderstandsIpv6LiteralsInClientConfig() throws ConfigException { String config = ipv4config + ":participant;[::1]:1237"; QuorumServer qs = new QuorumServer(0, config); assertEquals(ipv4config + ":participant;[0:0:0:0:0:0:0:1]:1237", qs.toString()); } @Test public void unbalancedIpv6LiteralsInServerConfigFailToBeParsed() { assertThrows(ConfigException.class, () -> { new QuorumServer(0, "[::1:1234:1236:participant"); }); } @Test public void unbalancedIpv6LiteralsInClientConfigFailToBeParsed() { assertThrows(ConfigException.class, () -> { new QuorumServer(0, ipv4config + ":participant;[::1:1237"); }); } @Test public void shouldNotAllowMultipleAddressesWhenMultiAddressFeatureIsDisabled() { assertThrows(ConfigException.class, () -> { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "false"); new QuorumServer(0, "127.0.0.1:1234:1236|127.0.0.1:2234:2236"); }); } @Test public void shouldAllowMultipleAddressesWhenMultiAddressFeatureIsEnabled() throws ConfigException { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); QuorumServer qs = new QuorumServer(0, "127.0.0.1:1234:1236|127.0.0.1:2234:2236"); assertEquals("127.0.0.1:1234:1236|127.0.0.1:2234:2236:participant", qs.toString(), "MultiAddress parse error"); } @Test public void testWildcard() throws KeeperException.BadArgumentsException { String[] addrs = new String[]{"127.0.0.1", "[0:0:0:0:0:0:0:1]", "0.0.0.0", "[::]"}; for (int i = 0; i < addrs.length; i++) { for (int j = i; j < addrs.length; j++) { QuorumPeer.QuorumServer server1 = new QuorumPeer.QuorumServer(1, new InetSocketAddress(ipv6n1, 1234), // peer new InetSocketAddress(ipv6n1, 1236), // election new InetSocketAddress(addrs[i], 1237) // client ); QuorumPeer.QuorumServer server2 = new QuorumPeer.QuorumServer(2, new InetSocketAddress(ipv6n2, 1234), // peer new InetSocketAddress(ipv6n2, 1236), // election new InetSocketAddress(addrs[j], 1237) // client ); server1.checkAddressDuplicate(server2); } } } @Test public void testDuplicate() { assertThrows(KeeperException.BadArgumentsException.class, () -> { QuorumPeer.QuorumServer server1 = new QuorumPeer.QuorumServer(1, new InetSocketAddress(ipv6n1, 1234), // peer new InetSocketAddress(ipv6n1, 1236), // election new InetSocketAddress(ipv6n1, 1237) // client ); QuorumPeer.QuorumServer server2 = new QuorumPeer.QuorumServer(2, new InetSocketAddress(ipv6n2, 1234), // peer new InetSocketAddress(ipv6n2, 1236), // election new InetSocketAddress(ipv6n1, 1237) // client ); server1.checkAddressDuplicate(server2); }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000166 15051152474 032755 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumSyncTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/QuorumSyncT0100644 0000000 0000000 00000006653 15051152474 034371 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Comparator; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class QuorumSyncTest extends ZKTestCase { private QuorumUtil qu; @AfterEach public void tearDown() throws Exception { if (qu != null) { qu.shutdownAll(); } } @Test public void testStaleDiffSync() throws Exception { qu = new QuorumUtil(2); qu.startAll(); int[] followerIds = qu.getFollowerQuorumPeers() .stream() .sorted(Comparator.comparingLong(QuorumPeer::getMyId).reversed()) .mapToInt(peer -> (int) peer.getMyId()).toArray(); int follower1 = followerIds[0]; int follower2 = followerIds[1]; String leaderConnectString = qu.getConnectString(qu.getLeaderQuorumPeer()); try (ZooKeeper zk = ClientBase.createZKClient(leaderConnectString)) { qu.shutdown(follower2); for (int i = 0; i < 10; i++) { zk.create("/foo" + i, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } qu.shutdown(follower1); for (int i = 0; i < 10; i++) { zk.create("/bar" + i, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } qu.restart(follower1); } try (ZooKeeper zk = ClientBase.createZKClient(qu.getConnectionStringForServer(follower1))) { for (int i = 0; i < 10; i++) { String path = "/foo" + i; assertNotNull(zk.exists(path, false), path + " not found"); } for (int i = 0; i < 10; i++) { String path = "/bar" + i; assertNotNull(zk.exists(path, false), path + " not found"); } } qu.shutdown(qu.getLeaderServer()); qu.restart(follower2); try (ZooKeeper zk = ClientBase.createZKClient(qu.getConnectionStringForServer(follower2))) { for (int i = 0; i < 10; i++) { String path = "/foo" + i; assertNotNull(zk.exists(path, false), path + " not found"); } for (int i = 0; i < 10; i++) { String path = "/bar" + i; assertNotNull(zk.exists(path, false), path + " not found"); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000171 15051152474 032751 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/RaceConditionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/RaceConditi0100644 0000000 0000000 00000024051 15051152474 034274 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.net.SocketException; import javax.security.sasl.SaslException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.server.FinalRequestProcessor; import org.apache.zookeeper.server.PrepRequestProcessor; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.txn.DeleteTxn; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This test class contains test cases related to race condition in complete * ZooKeeper */ public class RaceConditionTest extends QuorumPeerTestBase { protected static final Logger LOG = LoggerFactory.getLogger(RaceConditionTest.class); private static int SERVER_COUNT = 3; private MainThread[] mt; /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2380. * Deadlock while shutting down the ZooKeeper */ @Test @Timeout(value = 30) public void testRaceConditionBetweenLeaderAndAckRequestProcessor() throws Exception { mt = startQuorum(); // get leader QuorumPeer leader = getLeader(mt); long oldLeaderCurrentEpoch = leader.getCurrentEpoch(); assertNotNull(leader, "Leader should not be null"); // shutdown 2 followers so that leader does not have majority and goes // into looking state or following/leading state. shutdownFollowers(mt); /** *

             * Verify that there is no deadlock in following ways:
             * 1) If leader is in LOOKING or FOLLOWING, we are sure there is no deadlock.
             * 2) If leader in in LEADING state then we have to check that this LEADING state is
             * after the leader election, not the old LEADING state.
             * 
    */ boolean leaderStateChanged = ClientBase .waitForServerState(leader, 15000, QuorumStats.Provider.LOOKING_STATE, QuorumStats.Provider.FOLLOWING_STATE); // Wait for the old leader to start completely assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + leader.getClientPort(), CONNECTION_TIMEOUT), "Failed to bring up the old leader server"); assertTrue(leaderStateChanged || (leader.getCurrentEpoch() > oldLeaderCurrentEpoch), "Leader failed to transition to new state. Current state is " + leader.getServerState()); } @AfterEach public void tearDown() { // stop all severs if (null != mt) { for (int i = 0; i < SERVER_COUNT; i++) { try { // With the defect, leader hangs here also, but with fix // it does not mt[i].shutdown(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted while shutting it down", e); } } } } private MainThread[] startQuorum() throws IOException { final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; // start all the servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false) { @Override public TestQPMain getTestQPMain() { return new MockTestQPMain(); } }; mt[i].start(); } // ensure all servers started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } return mt; } private QuorumPeer getLeader(MainThread[] mt) { for (int i = mt.length - 1; i >= 0; i--) { QuorumPeer quorumPeer = mt[i].getQuorumPeer(); if (quorumPeer != null && ServerState.LEADING == quorumPeer.getPeerState()) { return quorumPeer; } } return null; } private void shutdownFollowers(MainThread[] mt) { for (int i = 0; i < mt.length; i++) { CustomQuorumPeer quorumPeer = (CustomQuorumPeer) mt[i].getQuorumPeer(); if (quorumPeer != null && ServerState.FOLLOWING == quorumPeer.getPeerState()) { quorumPeer.setStopPing(true); } } } private static class CustomQuorumPeer extends QuorumPeer { private boolean stopPing; public CustomQuorumPeer() throws SaslException { } public void setStopPing(boolean stopPing) { this.stopPing = stopPing; } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb())) { @Override protected void processPacket(QuorumPacket qp) throws Exception { if (stopPing && qp.getType() == Leader.PING) { LOG.info("Follower skipped ping"); throw new SocketException("Socket time out while sending the ping response"); } else { super.processPacket(qp); } } }; } @Override protected Leader makeLeader(FileTxnSnapLog logFactory) throws IOException, X509Exception { LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, this, this.getZkDb()) { @Override protected void setupRequestProcessors() { /** * This method is overridden to make a place to inject * MockSyncRequestProcessor */ RequestProcessor finalProcessor = new FinalRequestProcessor(this); RequestProcessor toBeAppliedProcessor = new Leader.ToBeAppliedRequestProcessor(finalProcessor, getLeader()); commitProcessor = new CommitProcessor(toBeAppliedProcessor, Long.toString(getServerId()), false, getZooKeeperServerListener()); commitProcessor.start(); ProposalRequestProcessor proposalProcessor = new MockProposalRequestProcessor(this, commitProcessor); proposalProcessor.initialize(); prepRequestProcessor = new PrepRequestProcessor(this, proposalProcessor); prepRequestProcessor.start(); firstProcessor = new LeaderRequestProcessor(this, prepRequestProcessor); } }; return new Leader(this, zk); } } private static class MockSyncRequestProcessor extends SyncRequestProcessor { public MockSyncRequestProcessor(ZooKeeperServer zks, RequestProcessor nextProcessor) { super(zks, nextProcessor); } @Override public void shutdown() { /** * Add a request so that something is there for SyncRequestProcessor * to process, while we are in shutdown flow */ DeleteTxn deleteTxn = new DeleteTxn("/deadLockIssue"); Request request = new Request(null, 0, 0, ZooDefs.OpCode.delete, RequestRecord.fromRecord(deleteTxn), null); processRequest(request); super.shutdown(); } } private static class MockProposalRequestProcessor extends ProposalRequestProcessor { public MockProposalRequestProcessor(LeaderZooKeeperServer zks, RequestProcessor nextProcessor) { super(zks, nextProcessor); /** * The only purpose here is to inject the mocked * SyncRequestProcessor */ AckRequestProcessor ackProcessor = new AckRequestProcessor(zks.getLeader()); syncProcessor = new MockSyncRequestProcessor(zks, ackProcessor); } } private static class MockTestQPMain extends TestQPMain { @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new CustomQuorumPeer(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000203 15051152474 032745 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReadOnlyZooKeeperServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReadOnlyZoo0100644 0000000 0000000 00000004571 15051152474 034322 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.server.MockServerCnxn; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.junit.jupiter.api.Test; /** * test ReadOnlyZooKeeperServer */ public class ReadOnlyZooKeeperServerTest { /** * test method {@link ZooKeeperServer#processConnectRequest(ServerCnxn, ConnectRequest)} */ @Test public void testReadOnlyZookeeperServer() { ReadOnlyZooKeeperServer readOnlyZooKeeperServer = new ReadOnlyZooKeeperServer( mock(FileTxnSnapLog.class), mock(QuorumPeer.class), mock(ZKDatabase.class)); final ConnectRequest request = new ConnectRequest(); request.setProtocolVersion(1); request.setLastZxidSeen(99L); request.setTimeOut(500); request.setSessionId(123L); request.setPasswd(new byte[]{ 1 }); request.setReadOnly(false); ServerCnxn.CloseRequestException e = assertThrows( ServerCnxn.CloseRequestException.class, () -> readOnlyZooKeeperServer.processConnectRequest(new MockServerCnxn(), request)); assertEquals(e.getReason(), ServerCnxn.DisconnectReason.NOT_READ_ONLY_CLIENT); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigBackupTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigBac0100644 0000000 0000000 00000032671 15051152474 034261 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static java.nio.charset.StandardCharsets.UTF_8; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ReconfigBackupTest extends QuorumPeerTestBase { public static String getVersionFromConfigStr(String config) throws IOException { Properties props = new Properties(); props.load(new StringReader(config)); return props.getProperty("version", ""); } @BeforeEach public void setup() { ClientBase.setupTestEnv(); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); } /** * This test checks that it will backup static file on bootup. */ @Test public void testBackupStatic() throws Exception { final int SERVER_COUNT = 3; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; String[] staticFileContent = new String[SERVER_COUNT]; String[] staticBackupContent = new String[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); // check that a dynamic configuration file doesn't exist assertNull(mt[i].getFileByName("zoo.cfg.bak"), "static file backup shouldn't exist before bootup"); staticFileContent[i] = new String(Files.readAllBytes(mt[i].confFile.toPath()), UTF_8); mt[i].start(); } for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); File backupFile = mt[i].getFileByName("zoo.cfg.bak"); assertNotNull(backupFile, "static file backup should exist"); staticBackupContent[i] = new String(Files.readAllBytes(backupFile.toPath()), UTF_8); assertEquals(staticFileContent[i], staticBackupContent[i]); } for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } /** * This test checks that on reconfig, a new dynamic file will be created with * current version appended to file name. Meanwhile, the dynamic file pointer * in static config file should also be changed. */ @Test public void testReconfigCreateNewVersionFile() throws Exception { final int SERVER_COUNT = 3; final int NEW_SERVER_COUNT = 5; final int[] clientPorts = new int[NEW_SERVER_COUNT]; final int[] quorumPorts = new int[NEW_SERVER_COUNT]; final int[] electionPorts = new int[NEW_SERVER_COUNT]; final String[] servers = new String[NEW_SERVER_COUNT]; StringBuilder sb = new StringBuilder(); ArrayList oldServers = new ArrayList<>(); ArrayList newServers = new ArrayList<>(); for (int i = 0; i < NEW_SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); quorumPorts[i] = PortAssignment.unique(); electionPorts[i] = PortAssignment.unique(); servers[i] = "server." + i + "=localhost:" + quorumPorts[i] + ":" + electionPorts[i] + ":participant;localhost:" + clientPorts[i]; newServers.add(servers[i]); if (i >= SERVER_COUNT) { continue; } oldServers.add(servers[i]); sb.append(servers[i] + "\n"); } String quorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[NEW_SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[NEW_SERVER_COUNT]; ZooKeeperAdmin[] zkAdmin = new ZooKeeperAdmin[NEW_SERVER_COUNT]; // start old cluster for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], quorumCfgSection, "reconfigEnabled=true\n"); mt[i].start(); } String firstVersion = null, secondVersion = null; // test old cluster for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); zk[i] = ClientBase.createZKClient("127.0.0.1:" + clientPorts[i]); zkAdmin[i] = new ZooKeeperAdmin("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); zkAdmin[i].addAuthInfo("digest", "super:test".getBytes()); Properties cfg = ReconfigLegacyTest.readPropertiesFromFile(mt[i].confFile); String filename = cfg.getProperty("dynamicConfigFile", ""); String version = QuorumPeerConfig.getVersionFromFilename(filename); assertNotNull(version); String configStr = ReconfigTest.testServerHasConfig(zk[i], oldServers, null); String configVersion = getVersionFromConfigStr(configStr); // the version appended to filename should be the same as // the one of quorum verifier. assertEquals(version, configVersion); if (i == 0) { firstVersion = version; } else { assertEquals(firstVersion, version); } } ReconfigTest.reconfig(zkAdmin[1], null, null, newServers, -1); // start additional new servers for (int i = SERVER_COUNT; i < NEW_SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], quorumCfgSection + servers[i]); mt[i].start(); } // wait for new servers to be up running for (int i = SERVER_COUNT; i < NEW_SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); zk[i] = ClientBase.createZKClient("127.0.0.1:" + clientPorts[i]); } // test that all servers have: // a different, larger version dynamic file for (int i = 0; i < NEW_SERVER_COUNT; i++) { Properties cfg = ReconfigLegacyTest.readPropertiesFromFile(mt[i].confFile); String filename = cfg.getProperty("dynamicConfigFile", ""); String version = QuorumPeerConfig.getVersionFromFilename(filename); assertNotNull(version); String configStr = ReconfigTest.testServerHasConfig(zk[i], newServers, null); String quorumVersion = getVersionFromConfigStr(configStr); assertEquals(version, quorumVersion); if (i == 0) { secondVersion = version; assertTrue(Long.parseLong(secondVersion, 16) > Long.parseLong(firstVersion, 16)); } else { assertEquals(secondVersion, version); } } for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); zk[i].close(); zkAdmin[i].close(); } } /** * This test checks that if a version is appended to dynamic file, * then peer should use that version as quorum config version. *

    * The scenario: one server has an older version of 3 servers, and * four others have newer version of 5 servers. Finally, the lag-off one * should have server config of 5 servers. */ @Test public void testVersionOfDynamicFilename() throws Exception { final int SERVER_COUNT = 5; final int oldServerCount = 3; final int lagOffServerId = 0; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; StringBuilder oldSb = new StringBuilder(); ArrayList allServers = new ArrayList<>(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts[i]; sb.append(server + "\n"); allServers.add(server); if (i < oldServerCount) { // only take in the first 3 servers as old quorum config. oldSb.append(server + "\n"); } } String currentQuorumCfgSection = sb.toString(); String oldQuorumCfg = oldSb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { if (i == lagOffServerId) { mt[i] = new MainThread(i, clientPorts[i], oldQuorumCfg, true, "100000000"); } else { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, true, "200000000"); } // before connecting to quorum, servers should have set up dynamic file // version and pointer. And the lag-off server is using the older // version dynamic file. if (i == lagOffServerId) { assertNotNull(mt[i].getFileByName("zoo.cfg.dynamic.100000000")); assertNull(mt[i].getFileByName("zoo.cfg.dynamic.200000000")); assertTrue(mt[i].getPropFromStaticFile("dynamicConfigFile").endsWith(".100000000")); } else { assertNotNull(mt[i].getFileByName("zoo.cfg.dynamic.200000000")); assertTrue(mt[i].getPropFromStaticFile("dynamicConfigFile").endsWith(".200000000")); } mt[i].start(); } String dynamicFileContent = null; for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); ZooKeeper zk = ClientBase.createZKClient("127.0.0.1:" + clientPorts[i]); // we should see that now all servers have the same config of 5 servers // including the lag-off server. String configStr = ReconfigTest.testServerHasConfig(zk, allServers, null); assertEquals("200000000", getVersionFromConfigStr(configStr)); List configLines = Arrays.asList(configStr.split("\n")); Collections.sort(configLines); String sortedConfigStr = StringUtils.joinStrings(configLines, "\n"); File dynamicConfigFile = mt[i].getFileByName("zoo.cfg.dynamic.200000000"); assertNotNull(dynamicConfigFile); // All dynamic files created with the same version should have // same configs, and they should be equal to the config we get from QuorumPeer. if (i == 0) { dynamicFileContent = new String(Files.readAllBytes(dynamicConfigFile.toPath()), UTF_8); // last line in file should be version number assertEquals(sortedConfigStr, dynamicFileContent + "\n" + "version=200000000"); } else { String otherDynamicFileContent = new String(Files.readAllBytes(dynamicConfigFile.toPath()), UTF_8); assertEquals(dynamicFileContent + "\n", otherDynamicFileContent); } zk.close(); } // finally, we should also check that the lag-off server has updated // the dynamic file pointer. assertTrue(mt[lagOffServerId].getPropFromStaticFile("dynamicConfigFile").endsWith(".200000000")); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000204 15051152474 032746 xustar000000000 0000000 132 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigDuringLeaderSyncTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigDur0100644 0000000 0000000 00000026575 15051152474 034334 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.admin.AdminServer.AdminServerException; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReconfigDuringLeaderSyncTest extends QuorumPeerTestBase { private static final Logger LOG = LoggerFactory.getLogger(ReconfigDuringLeaderSyncTest.class); private static int SERVER_COUNT = 3; private MainThread[] mt; private static boolean bakAsyncSending; public void setup(boolean asyncSending) { System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); Learner.setAsyncSending(asyncSending); QuorumPeerConfig.setReconfigEnabled(true); } @BeforeAll public static void saveAsyncSendingFlag() { bakAsyncSending = Learner.getAsyncSending(); } @AfterAll public static void resetAsyncSendingFlag() { Learner.setAsyncSending(bakAsyncSending); } /** *

         * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2172.
         * Cluster crashes when reconfig a new node as a participant.
         * 
    * * This issue occurs when reconfig's PROPOSAL and COMMITANDACTIVATE come in * between the snapshot and the UPTODATE. In this case processReconfig was * not invoked on the newly added node, and zoo.cfg.dynamic.next wasn't * deleted. */ @ParameterizedTest @ValueSource(booleans = {true, false}) public void testDuringLeaderSync(boolean asyncSending) throws Exception { setup(asyncSending); final int[] clientPorts = new int[SERVER_COUNT + 1]; StringBuilder sb = new StringBuilder(); String[] serverConfig = new String[SERVER_COUNT + 1]; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); serverConfig[i] = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(serverConfig[i] + "\n"); } String currentQuorumCfgSection = sb.toString(); mt = new MainThread[SERVER_COUNT + 1]; // start 3 servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure all servers started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } CountdownWatcher watch = new CountdownWatcher(); ZooKeeperAdmin preReconfigClient = new ZooKeeperAdmin( "127.0.0.1:" + clientPorts[0], ClientBase.CONNECTION_TIMEOUT, watch); preReconfigClient.addAuthInfo("digest", "super:test".getBytes()); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); // new server joining int joinerId = SERVER_COUNT; clientPorts[joinerId] = PortAssignment.unique(); serverConfig[joinerId] = "server." + joinerId + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[joinerId]; // Find leader id. int leaderId = -1; for (int i = 0; i < SERVER_COUNT; i++) { if (mt[i].main.quorumPeer.leader != null) { leaderId = i; break; } } assertFalse(leaderId == -1); // Joiner initial config consists of itself and the leader. sb = new StringBuilder(); sb.append(serverConfig[leaderId] + "\n").append(serverConfig[joinerId] + "\n"); /** * This server will delay the response to a NEWLEADER message, and run * reconfig command so that message at this processed in bellow order * *
             * NEWLEADER
             * reconfig's PROPOSAL
             * reconfig's COMMITANDACTIVATE
             * UPTODATE
             * 
    */ mt[joinerId] = new MainThread(joinerId, clientPorts[joinerId], sb.toString(), false) { @Override public TestQPMain getTestQPMain() { return new MockTestQPMain(); } }; mt[joinerId].start(); CustomQuorumPeer qp = getCustomQuorumPeer(mt[joinerId]); // delete any already existing .next file String nextDynamicConfigFilename = qp.getNextDynamicConfigFilename(); File nextDynaFile = new File(nextDynamicConfigFilename); nextDynaFile.delete(); // call reconfig API when the new server has received // Leader.NEWLEADER while (true) { if (qp.isNewLeaderMessage()) { preReconfigClient.reconfigure(serverConfig[joinerId], null, null, -1, null, null); break; } else { // sleep for 10 millisecond and then again check Thread.sleep(10); } } watch = new CountdownWatcher(); ZooKeeper postReconfigClient = new ZooKeeper( "127.0.0.1:" + clientPorts[joinerId], ClientBase.CONNECTION_TIMEOUT, watch); watch.waitForConnected(ClientBase.CONNECTION_TIMEOUT); // do one successful operation on the newly added node postReconfigClient.create("/reconfigIssue", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertFalse(nextDynaFile.exists(), "zoo.cfg.dynamic.next is not deleted."); // verify that joiner has up-to-date config, including all four servers. for (long j = 0; j <= SERVER_COUNT; j++) { assertNotNull(qp.getQuorumVerifier().getVotingMembers().get(j), "server " + j + " is not present in the new quorum"); } // close clients preReconfigClient.close(); postReconfigClient.close(); } private static CustomQuorumPeer getCustomQuorumPeer(MainThread mt) { while (true) { QuorumPeer quorumPeer = mt.getQuorumPeer(); if (null != quorumPeer) { return (CustomQuorumPeer) quorumPeer; } else { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } @AfterEach public void tearDown() { // stop all severs if (null != mt) { for (int i = 0; i < mt.length; i++) { try { mt[i].shutdown(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted while shutting it down", e); } } } } private static class CustomQuorumPeer extends QuorumPeer { private boolean newLeaderMessage = false; public CustomQuorumPeer(Map quorumPeers, File snapDir, File logDir, int clientPort, int electionAlg, long myid, int tickTime, int initLimit, int syncLimit, int connectToLearnerMasterLimit) throws IOException { super(quorumPeers, snapDir, logDir, electionAlg, myid, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, false, ServerCnxnFactory.createFactory(new InetSocketAddress(clientPort), -1), new QuorumMaj(quorumPeers)); } /** * If true, after 100 millisecond NEWLEADER response is send to leader * * @return */ public boolean isNewLeaderMessage() { return newLeaderMessage; } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb())) { @Override void writePacket(QuorumPacket pp, boolean flush) throws IOException { if (pp != null && pp.getType() == Leader.ACK) { newLeaderMessage = true; try { /** * Delaying the ACK message, a follower sends as * response to a NEWLEADER message, so that the * leader has a chance to send the reconfig and only * then the UPTODATE message. */ Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } super.writePacket(pp, flush); } }; } } private static class MockTestQPMain extends TestQPMain { @Override public void runFromConfig(QuorumPeerConfig config) throws IOException, AdminServerException { quorumPeer = new CustomQuorumPeer(config.getQuorumVerifier().getAllMembers(), config.getDataDir(), config.getDataLogDir(), config.getClientPortAddress().getPort(), config.getElectionAlg(), config.getServerId(), config.getTickTime(), config.getInitLimit(), config.getSyncLimit(), config.getConnectToLearnerMasterLimit()); quorumPeer.setConfigFileName(config.getConfigFilename()); quorumPeer.start(); try { quorumPeer.join(); } catch (InterruptedException e) { LOG.warn("Quorum Peer interrupted", e); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032742 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigFailureCasesTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigFai0100644 0000000 0000000 00000024260 15051152474 034266 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.NewConfigNoQuorum; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.QuorumUtil; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ReconfigFailureCasesTest extends QuorumPeerTestBase { private QuorumUtil qu; @BeforeEach public void setup() { QuorumPeerConfig.setReconfigEnabled(true); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); } @AfterEach public void tearDown() throws Exception { if (qu != null) { qu.tearDown(); } } /* * Tests that an incremental reconfig fails if the current config is hiearchical. */ @Test public void testIncrementalReconfigInvokedOnHiearchicalQS() throws Exception { qu = new QuorumUtil(2); // create 5 servers qu.disableJMXTest = true; qu.startAll(); ZooKeeper[] zkArr = ReconfigTest.createHandles(qu); ZooKeeperAdmin[] zkAdminArr = ReconfigTest.createAdminHandles(qu); ArrayList members = new ArrayList<>(); members.add("group.1=3:4:5"); members.add("group.2=1:2"); members.add("weight.1=0"); members.add("weight.2=0"); members.add("weight.3=1"); members.add("weight.4=1"); members.add("weight.5=1"); for (int i = 1; i <= 5; i++) { members.add("server." + i + "=127.0.0.1:" + qu.getPeer(i).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(i).peer.getElectionAddress().getAllPorts().get(0) + ";" + "127.0.0.1:" + qu.getPeer(i).peer.getClientPort()); } // Change the quorum system from majority to hierarchical. ReconfigTest.reconfig(zkAdminArr[1], null, null, members, -1); ReconfigTest.testNormalOperation(zkArr[1], zkArr[2]); // Attempt an incremental reconfig. List leavingServers = new ArrayList<>(); leavingServers.add("3"); try { zkAdminArr[1].reconfigure(null, leavingServers, null, -1, null); fail("Reconfig should have failed since the current config isn't Majority QS"); } catch (KeeperException.BadArgumentsException e) { // We expect this to happen. } catch (Exception e) { fail("Should have been BadArgumentsException!"); } ReconfigTest.closeAllHandles(zkArr, zkAdminArr); } /* * Test that a reconfiguration fails if the proposed change would leave the * cluster with less than 2 participants (StandaloneEnabled = true). * StandaloneDisabledTest.java (startSingleServerTest) checks that if * StandaloneEnabled = false its legal to remove all but one remaining * server. */ @Test public void testTooFewRemainingPariticipants() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); ZooKeeper[] zkArr = ReconfigTest.createHandles(qu); ZooKeeperAdmin[] zkAdminArr = ReconfigTest.createAdminHandles(qu); List leavingServers = new ArrayList<>(); leavingServers.add("2"); leavingServers.add("3"); try { zkAdminArr[1].reconfigure(null, leavingServers, null, -1, null); fail("Reconfig should have failed since the current config version is not 8"); } catch (KeeperException.BadArgumentsException e) { // We expect this to happen. } catch (Exception e) { fail("Should have been BadArgumentsException!"); } ReconfigTest.closeAllHandles(zkArr, zkAdminArr); } /* * Tests that a conditional reconfig fails if the specified version doesn't correspond * to the version of the current config. */ @Test public void testReconfigVersionConditionFails() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); ZooKeeper[] zkArr = ReconfigTest.createHandles(qu); ZooKeeperAdmin[] zkAdminArr = ReconfigTest.createAdminHandles(qu); List leavingServers = new ArrayList<>(); leavingServers.add("3"); try { zkAdminArr[1].reconfigure(null, leavingServers, null, 8, null); fail("Reconfig should have failed since the current config version is not 8"); } catch (KeeperException.BadVersionException e) { // We expect this to happen. } catch (Exception e) { fail("Should have been BadVersionException!"); } ReconfigTest.closeAllHandles(zkArr, zkAdminArr); } /* * Converting an observer into a participant may sometimes fail with a * NewConfigNoQuorum exception. This test-case demonstrates the scenario. * Current configuration is (A, B, C, D), where A, B and C are participant * and D is an observer. Suppose that B has crashed (or never booted). If a * reconfiguration is submitted where D is said to become a participant, it * will fail with NewConfigNoQuorum since in this configuration, a majority * of voters in the new configuration (any 3 voters), must be connected and * up-to-date with the leader. An observer cannot acknowledge the history * prefix sent during reconfiguration, and therefore it does not count towards * these 3 required servers and the reconfiguration will be aborted. In case * this happens, a client can achieve the same task by two reconfig commands: * first invoke a reconfig to remove D from the configuration and then invoke a * second command to add it back as a participant (follower). During the * intermediate state D is a non-voting follower and can ACK the state * transfer performed during the second reconfig command. */ @Test public void testObserverToParticipantConversionFails() throws Exception { ClientBase.setupTestEnv(); final int SERVER_COUNT = 4; int[][] ports = ReconfigRecoveryTest.generatePorts(SERVER_COUNT); // generate old config string Set observers = new HashSet<>(); observers.add(3); StringBuilder sb = ReconfigRecoveryTest.generateConfig(SERVER_COUNT, ports, observers); String currentQuorumCfgSection = sb.toString(); String nextQuorumCfgSection = currentQuorumCfgSection.replace("observer", "participant"); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; ZooKeeperAdmin[] zkAdmin = new ZooKeeperAdmin[SERVER_COUNT]; // Server 0 stays down for (int i = 1; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, ports[i][2], currentQuorumCfgSection, true, "100000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + ports[i][2], ClientBase.CONNECTION_TIMEOUT, this); zkAdmin[i] = new ZooKeeperAdmin("127.0.0.1:" + ports[i][2], ClientBase.CONNECTION_TIMEOUT, this); zkAdmin[i].addAuthInfo("digest", "super:test".getBytes()); } for (int i = 1; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + ports[i][2], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); } try { zkAdmin[1].reconfigure("", "", nextQuorumCfgSection, -1, new Stat()); fail("Reconfig should have failed with NewConfigNoQuorum"); } catch (NewConfigNoQuorum e) { // This is expected case since server 0 is down and 3 can't vote // (observer in current role) and we need 3 votes from 0, 1, 2, 3, } catch (Exception e) { fail("Reconfig should have failed with NewConfigNoQuorum"); } // In this scenario to change 3's role to participant we need to remove it first ArrayList leavingServers = new ArrayList<>(); leavingServers.add("3"); ReconfigTest.reconfig(zkAdmin[1], null, leavingServers, null, -1); ReconfigTest.testNormalOperation(zk[2], zk[3]); ReconfigTest.testServerHasConfig(zk[3], null, leavingServers); // Now we're adding it back as a participant and everything should work. List newMembers = Arrays.asList(nextQuorumCfgSection.split("\n")); ReconfigTest.reconfig(zkAdmin[1], null, null, newMembers, -1); ReconfigTest.testNormalOperation(zk[2], zk[3]); for (int i = 1; i < SERVER_COUNT; i++) { ReconfigTest.testServerHasConfig(zk[i], newMembers, null); } for (int i = 1; i < SERVER_COUNT; i++) { zk[i].close(); zkAdmin[i].close(); mt[i].shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigLegacyTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigLeg0100644 0000000 0000000 00000030617 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Properties; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class ReconfigLegacyTest extends QuorumPeerTestBase { private static final int SERVER_COUNT = 3; @BeforeEach public void setup() { ClientBase.setupTestEnv(); QuorumPeerConfig.setReconfigEnabled(true); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); } /** * This test checks that when started with a single static config file the * servers will create a valid dynamic config file. Also checks that when * the static config includes a clientPort but the dynamic definition also * includes it, the static definition is erased. */ @Test public void testConfigFileBackwardCompatibility() throws Exception { final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; ArrayList allServers = new ArrayList<>(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts[i]; allServers.add(server); sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; // Start the servers with a static config file, without a dynamic // config file. for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, "participant", false); // check that a dynamic configuration file doesn't exist assertEquals(mt[i].getDynamicFiles().length, 0); mt[i].start(); } // Check that the servers are up, have the right config and can process operations. // Check that the static config was split into static and dynamic files correctly. for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); zk[i] = ClientBase.createZKClient("127.0.0.1:" + clientPorts[i]); File[] dynamicFiles = mt[i].getDynamicFiles(); assertTrue(dynamicFiles.length == 1); ReconfigTest.testServerHasConfig(zk[i], allServers, null); // check that static config file doesn't include membership info // and has a pointer to the dynamic configuration file // check that static config file doesn't include peerType info Properties cfg = readPropertiesFromFile(mt[i].confFile); for (int j = 0; j < SERVER_COUNT; j++) { assertFalse(cfg.containsKey("server." + j)); } assertFalse(cfg.containsKey("peerType")); assertTrue(cfg.containsKey("dynamicConfigFile")); assertFalse(cfg.containsKey("clientPort")); // check that the dynamic configuration file contains the membership info cfg = readPropertiesFromFile(dynamicFiles[0]); for (int j = 0; j < SERVER_COUNT; j++) { String serverLine = cfg.getProperty("server." + j, ""); assertEquals(allServers.get(j), "server." + j + "=" + serverLine); } assertFalse(cfg.containsKey("dynamicConfigFile")); } ReconfigTest.testNormalOperation(zk[0], zk[1]); // now shut down the servers and restart them for (int i = 0; i < SERVER_COUNT; i++) { zk[i].close(); mt[i].shutdown(); } for (int i = 0; i < SERVER_COUNT; i++) { mt[i].start(); } for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); zk[i] = ClientBase.createZKClient("127.0.0.1:" + clientPorts[i]); ReconfigTest.testServerHasConfig(zk[i], allServers, null); } ReconfigTest.testNormalOperation(zk[0], zk[1]); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); zk[i].close(); } } /** * https://issues.apache.org/jira/browse/ZOOKEEPER-1992 * 1. When a server starts from old style static config, without a client port in the server * specification, it should keep the client port in static config file. * 2. After port reconfig, the old port should be removed from static file * and new port added to dynamic file. * @throws Exception */ @Test public void testReconfigRemoveClientFromStatic() throws Exception { final int[] clientPorts = new int[SERVER_COUNT]; final int[] quorumPorts = new int[SERVER_COUNT]; final int[] electionPorts = new int[SERVER_COUNT]; final int changedServerId = 0; final int newClientPort = PortAssignment.unique(); StringBuilder sb = new StringBuilder(); ArrayList allServers = new ArrayList<>(); ArrayList newServers = new ArrayList<>(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); quorumPorts[i] = PortAssignment.unique(); electionPorts[i] = PortAssignment.unique(); String server = "server." + i + "=localhost:" + quorumPorts[i] + ":" + electionPorts[i] + ":participant"; allServers.add(server); sb.append(server + "\n"); if (i == changedServerId) { newServers.add(server + ";0.0.0.0:" + newClientPort); } else { newServers.add(server); } } String quorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; ZooKeeperAdmin[] zkAdmin = new ZooKeeperAdmin[SERVER_COUNT]; // Start the servers with a static config file, without a dynamic config file. for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], quorumCfgSection, false); mt[i].start(); } // Check that when a server starts from old style config, it should keep the client // port in static config file. for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); zk[i] = ClientBase.createZKClient("127.0.0.1:" + clientPorts[i]); zkAdmin[i] = new ZooKeeperAdmin("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); zkAdmin[i].addAuthInfo("digest", "super:test".getBytes()); ReconfigTest.testServerHasConfig(zk[i], allServers, null); Properties cfg = readPropertiesFromFile(mt[i].confFile); assertTrue(cfg.containsKey("dynamicConfigFile")); assertTrue(cfg.containsKey("clientPort")); } ReconfigTest.testNormalOperation(zk[0], zk[1]); ReconfigTest.reconfig(zkAdmin[1], null, null, newServers, -1); ReconfigTest.testNormalOperation(zk[0], zk[1]); // Sleep since writing the config files may take time. Thread.sleep(1000); // Check that new dynamic config includes the updated client port. // Check that server changedServerId erased clientPort from static config. // Check that other servers still have clientPort in static config. for (int i = 0; i < SERVER_COUNT; i++) { ReconfigTest.testServerHasConfig(zk[i], newServers, null); Properties staticCfg = readPropertiesFromFile(mt[i].confFile); if (i == changedServerId) { assertFalse(staticCfg.containsKey("clientPort")); } else { assertTrue(staticCfg.containsKey("clientPort")); } } for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); zk[i].close(); zkAdmin[i].close(); } } public static Properties readPropertiesFromFile(File file) throws IOException { Properties cfg = new Properties(); FileInputStream in = new FileInputStream(file); try { cfg.load(in); } finally { in.close(); } return cfg; } /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2244 * * @throws Exception */ @Test @Timeout(value = 120) public void testRestartZooKeeperServer() throws Exception { final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure server started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } ZooKeeper zk = ClientBase.createZKClient("127.0.0.1:" + clientPorts[0]); String zNodePath = "/serverRestartTest"; String data = "originalData"; zk.create(zNodePath, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); /** * stop two servers out of three and again start them */ mt[0].shutdown(); mt[1].shutdown(); mt[0].start(); mt[1].start(); // ensure server started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } zk = ClientBase.createZKClient("127.0.0.1:" + clientPorts[0]); byte[] dataBytes = zk.getData(zNodePath, null, null); String receivedData = new String(dataBytes); assertEquals(data, receivedData); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032754 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigRecoveryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigRec0100644 0000000 0000000 00000055531 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ReconfigRecoveryTest extends QuorumPeerTestBase { @BeforeEach public void setup() { QuorumPeerConfig.setReconfigEnabled(true); } /** * Reconfiguration recovery - test that a reconfiguration is completed if * leader has .next file during startup and new config is not running yet */ @Test public void testNextConfigCompletion() throws Exception { ClientBase.setupTestEnv(); // 2 servers in current config, 3 in next config final int SERVER_COUNT = 3; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; ArrayList allServers = new ArrayList<>(); String currentQuorumCfgSection = null, nextQuorumCfgSection; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts[i]; allServers.add(server); sb.append(server + "\n"); if (i == 1) { currentQuorumCfgSection = sb.toString(); } } nextQuorumCfgSection = sb.toString(); // Both servers 0 and 1 will have the .next config file, which means // for them that a reconfiguration was in progress when they failed // and the leader will complete it MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT - 1; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, true, "100000000"); // note that we should run the server, shut it down and only then // simulate a reconfig in progress by writing the temp file, but here no // other server is competing with them in FLE, so we can skip this step // (server 2 is booted after FLE ends) mt[i].writeTempDynamicConfigFile(nextQuorumCfgSection, "200000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[0], CONNECTION_TIMEOUT), "waiting for server 0 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[1], CONNECTION_TIMEOUT), "waiting for server 1 being up"); int leader = mt[0].main.quorumPeer.leader == null ? 1 : 0; // the new server's config is going to include itself and the current leader sb = new StringBuilder(); sb.append(allServers.get(leader) + "\n"); sb.append(allServers.get(2) + "\n"); // suppose that this new server never heard about the reconfig proposal String newServerInitialConfig = sb.toString(); mt[2] = new MainThread(2, clientPorts[2], newServerInitialConfig); mt[2].start(); zk[2] = new ZooKeeper("127.0.0.1:" + clientPorts[2], ClientBase.CONNECTION_TIMEOUT, this); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[2], CONNECTION_TIMEOUT), "waiting for server 2 being up"); ReconfigTest.testServerHasConfig(zk[0], allServers, null); ReconfigTest.testServerHasConfig(zk[1], allServers, null); ReconfigTest.testServerHasConfig(zk[2], allServers, null); ReconfigTest.testNormalOperation(zk[0], zk[2]); ReconfigTest.testNormalOperation(zk[2], zk[1]); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); zk[i].close(); } } /** * Reconfiguration recovery - current config servers discover .next file, * but they're both observers and their ports change in next config. Suppose * that next config wasn't activated yet. Should complete reconfiguration. */ @Test public void testCurrentServersAreObserversInNextConfig() throws Exception { ClientBase.setupTestEnv(); // 2 servers in current config, 5 in next config final int SERVER_COUNT = 5; final int[] clientPorts = new int[SERVER_COUNT]; final int[] oldClientPorts = new int[2]; StringBuilder sb = new StringBuilder(); String server; String currentQuorumCfg, nextQuorumCfgSection; ArrayList allServersNext = new ArrayList<>(); for (int i = 0; i < 2; i++) { oldClientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + oldClientPorts[i]; sb.append(server + "\n"); } currentQuorumCfg = sb.toString(); sb = new StringBuilder(); String role; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); if (i < 2) { role = "observer"; } else { role = "participant"; } server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":" + role + ";localhost:" + clientPorts[i]; allServersNext.add(server); sb.append(server + "\n"); } nextQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; // run servers 0 and 1 normally for (int i = 0; i < 2; i++) { mt[i] = new MainThread(i, oldClientPorts[i], currentQuorumCfg, true, "100000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + oldClientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } for (int i = 0; i < 2; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + oldClientPorts[i], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); } ReconfigTest.testNormalOperation(zk[0], zk[1]); // shut them down and then simulate a reboot with a reconfig in progress for (int i = 0; i < 2; i++) { mt[i].shutdown(); zk[i].close(); } for (int i = 0; i < 2; i++) { assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + oldClientPorts[i], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); } for (int i = 0; i < 2; i++) { mt[i].writeTempDynamicConfigFile(nextQuorumCfgSection, "200000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } // new members are initialized with current config + the new server for (int i = 2; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfg + allServersNext.get(i)); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); ReconfigTest.testServerHasConfig(zk[i], allServersNext, null); } ReconfigTest.testNormalOperation(zk[0], zk[2]); ReconfigTest.testNormalOperation(zk[4], zk[1]); for (int i = 0; i < SERVER_COUNT; i++) { zk[i].close(); mt[i].shutdown(); } } /** * Reconfiguration recovery - test that if servers in old config have a * .next file but no quorum of new config is up then no progress should be * possible (no progress will happen to ensure safety as the new config * might be actually up but partitioned from old config) */ @Test public void testNextConfigUnreachable() throws Exception { ClientBase.setupTestEnv(); // 2 servers in current config, 5 in next config final int SERVER_COUNT = 5; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; String currentQuorumCfgSection = null, nextQuorumCfgSection; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts[i]; sb.append(server + "\n"); if (i == 1) { currentQuorumCfgSection = sb.toString(); } } nextQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; // Both servers 0 and 1 will have the .next config file, which means // for them that a reconfiguration was in progress when they failed for (int i = 0; i < 2; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, true, "100000000"); // note that we should run the server, shut it down and only then // simulate a reconfig in progress by writing the temp file, but here no // other server is competing with them in FLE, so we can skip this step mt[i].writeTempDynamicConfigFile(nextQuorumCfgSection, "200000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } Thread.sleep(CONNECTION_TIMEOUT * 2); // make sure servers 0, 1 don't come online - this should be the case // since they can't complete the reconfig for (int i = 0; i < 2; i++) { assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT / 10), "server " + i + " is up but shouldn't be"); } for (int i = 0; i < 2; i++) { zk[i].close(); mt[i].shutdown(); } } /** * Reconfiguration recovery - test that old config members will join the new * config if its already active, and not try to complete the reconfiguration */ @Test public void testNextConfigAlreadyActive() throws Exception { ClientBase.setupTestEnv(); // 2 servers in current config, 5 in next config final int SERVER_COUNT = 5; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; String currentQuorumCfgSection = null, nextQuorumCfgSection; ArrayList allServers = new ArrayList<>(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts[i]; allServers.add(server); sb.append(server + "\n"); if (i == 1) { currentQuorumCfgSection = sb.toString(); } } nextQuorumCfgSection = sb.toString(); // lets start servers 2, 3, 4 with the new config MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; for (int i = 2; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], nextQuorumCfgSection, true, "200000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } for (int i = 2; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } ReconfigTest.testNormalOperation(zk[2], zk[3]); long epoch = mt[2].main.quorumPeer.getAcceptedEpoch(); // Both servers 0 and 1 will have the .next config file, which means // for them that a reconfiguration was in progress when they failed // and the leader will complete it. for (int i = 0; i < 2; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, true, "100000000"); mt[i].writeTempDynamicConfigFile(nextQuorumCfgSection, "200000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT, this); } // servers 0 and 1 should connect to all servers, including the one in // their .next file during startup, and will find the next config and join it for (int i = 0; i < 2; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); } // make sure they joined the new config without any change to it assertEquals(epoch, mt[0].main.quorumPeer.getAcceptedEpoch()); assertEquals(epoch, mt[1].main.quorumPeer.getAcceptedEpoch()); assertEquals(epoch, mt[2].main.quorumPeer.getAcceptedEpoch()); ReconfigTest.testServerHasConfig(zk[0], allServers, null); ReconfigTest.testServerHasConfig(zk[1], allServers, null); ReconfigTest.testNormalOperation(zk[0], zk[2]); ReconfigTest.testNormalOperation(zk[4], zk[1]); for (int i = 0; i < SERVER_COUNT; i++) { zk[i].close(); mt[i].shutdown(); } } /** * Tests conversion of observer to participant AFTER new config was already * committed. Old config: servers 0 (participant), 1 (participant), 2 * (observer) New config: servers 2 (participant), 3 (participant) We start * server 2 with old config and start server 3 with new config. All other * servers are down. In order to terminate FLE, server 3 must 'convince' * server 2 to adopt the new config and turn into a participant. */ @Test public void testObserverConvertedToParticipantDuringFLE() throws Exception { ClientBase.setupTestEnv(); final int SERVER_COUNT = 4; int[][] ports = generatePorts(SERVER_COUNT); String currentQuorumCfgSection, nextQuorumCfgSection; // generate old config string Set observers = new HashSet<>(); observers.add(2); StringBuilder sb = generateConfig(3, ports, observers); currentQuorumCfgSection = sb.toString(); // generate new config string ArrayList allServersNext = new ArrayList<>(); sb = new StringBuilder(); for (int i = 2; i < SERVER_COUNT; i++) { String server = "server." + i + "=localhost:" + ports[i][0] + ":" + ports[i][1] + ":participant;localhost:" + ports[i][2]; allServersNext.add(server); sb.append(server + "\n"); } nextQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; // start server 2 with old config, where it is an observer mt[2] = new MainThread(2, ports[2][2], currentQuorumCfgSection, true, "100000000"); mt[2].start(); zk[2] = new ZooKeeper("127.0.0.1:" + ports[2][2], ClientBase.CONNECTION_TIMEOUT, this); // start server 3 with new config mt[3] = new MainThread(3, ports[3][2], nextQuorumCfgSection, true, "200000000"); mt[3].start(); zk[3] = new ZooKeeper("127.0.0.1:" + ports[3][2], ClientBase.CONNECTION_TIMEOUT, this); for (int i = 2; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + ports[i][2], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); ReconfigTest.testServerHasConfig(zk[i], allServersNext, null); } assertEquals( nextQuorumCfgSection + "version=200000000", ReconfigTest.testServerHasConfig(zk[2], null, null)); assertEquals( nextQuorumCfgSection + "version=200000000", ReconfigTest.testServerHasConfig(zk[3], null, null)); ReconfigTest.testNormalOperation(zk[2], zk[2]); ReconfigTest.testNormalOperation(zk[3], zk[2]); for (int i = 2; i < SERVER_COUNT; i++) { zk[i].close(); mt[i].shutdown(); } } /** * Tests conversion of observer to participant during reconfig recovery, new * config was not committed yet. Old config: servers 0 (participant), 1 * (participant), 2 (observer) New config: servers 2 (participant), 3 * (participant) We start server servers 0, 1, 2 with old config and a .next * file indicating a reconfig in progress. We start server 3 with old config * + itself in config file. In this scenario server 2 can't be converted to * participant during reconfig since we don't gossip about proposed * configurations, only about committed ones. This tests that new config can * be completed, which requires server 2's ack for the newleader message, * even though its an observer. */ @Test public void testCurrentObserverIsParticipantInNewConfig() throws Exception { ClientBase.setupTestEnv(); final int SERVER_COUNT = 4; int[][] ports = generatePorts(SERVER_COUNT); String currentQuorumCfg, nextQuorumCfgSection; // generate old config string Set observers = new HashSet<>(); observers.add(2); StringBuilder sb = generateConfig(3, ports, observers); currentQuorumCfg = sb.toString(); // Run servers 0..2 for a while MainThread[] mt = new MainThread[SERVER_COUNT]; ZooKeeper[] zk = new ZooKeeper[SERVER_COUNT]; for (int i = 0; i <= 2; i++) { mt[i] = new MainThread(i, ports[i][2], currentQuorumCfg, true, "100000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + ports[i][2], ClientBase.CONNECTION_TIMEOUT, this); } ReconfigTest.testNormalOperation(zk[0], zk[2]); for (int i = 0; i <= 2; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + ports[i][2], CONNECTION_TIMEOUT * 2), "waiting for server " + i + " being up"); } // shut servers 0..2 down for (int i = 0; i <= 2; i++) { mt[i].shutdown(); zk[i].close(); } // generate new config string ArrayList allServersNext = new ArrayList<>(); sb = new StringBuilder(); for (int i = 2; i < SERVER_COUNT; i++) { String server = "server." + i + "=localhost:" + ports[i][0] + ":" + ports[i][1] + ":participant;localhost:" + ports[i][2]; allServersNext.add(server); sb.append(server + "\n"); } nextQuorumCfgSection = sb.toString(); // simulate reconfig in progress - servers 0..2 have a temp reconfig // file when they boot for (int i = 0; i <= 2; i++) { mt[i].writeTempDynamicConfigFile(nextQuorumCfgSection, "200000000"); mt[i].start(); zk[i] = new ZooKeeper("127.0.0.1:" + ports[i][2], ClientBase.CONNECTION_TIMEOUT, this); } // new server 3 has still its invalid joiner config - everyone in old // config + itself mt[3] = new MainThread(3, ports[3][2], currentQuorumCfg + allServersNext.get(1)); mt[3].start(); zk[3] = new ZooKeeper("127.0.0.1:" + ports[3][2], ClientBase.CONNECTION_TIMEOUT, this); for (int i = 2; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + ports[i][2], CONNECTION_TIMEOUT * 3), "waiting for server " + i + " being up"); ReconfigTest.testServerHasConfig(zk[i], allServersNext, null); } ReconfigTest.testNormalOperation(zk[0], zk[2]); ReconfigTest.testNormalOperation(zk[3], zk[1]); assertEquals( nextQuorumCfgSection + "version=200000000", ReconfigTest.testServerHasConfig(zk[2], null, null)); assertEquals( nextQuorumCfgSection + "version=200000000", ReconfigTest.testServerHasConfig(zk[3], null, null)); for (int i = 0; i < SERVER_COUNT; i++) { zk[i].close(); mt[i].shutdown(); } } /* * Generates 3 ports per server */ public static int[][] generatePorts(int numServers) { int[][] ports = new int[numServers][]; for (int i = 0; i < numServers; i++) { ports[i] = new int[3]; for (int j = 0; j < 3; j++) { ports[i][j] = PortAssignment.unique(); } } return ports; } /* * Creates a configuration string for servers 0..numServers-1 Ids in * observerIds correspond to observers, other ids are for participants. */ public static StringBuilder generateConfig(int numServers, int[][] ports, Set observerIds) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < numServers; i++) { String server = "server." + i + "=localhost:" + ports[i][0] + ":" + ports[i][1] + ":" + (observerIds.contains(i) ? "observer" : "participant") + ";localhost:" + ports[i][2]; sb.append(server + "\n"); } return sb; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000217 15051152474 032752 xustar000000000 0000000 143 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigRollingRestartCompatibilityTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ReconfigRol0100644 0000000 0000000 00000042207 15051152474 034324 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** * ReconfigRollingRestartCompatibilityTest - we want to make sure that users * can continue using the rolling restart approach when reconfig feature is disabled. * It is important to stay compatible with rolling restart because dynamic reconfig * has its limitation: it requires a quorum of server to work. When no quorum can be formed, * rolling restart is the only approach to reconfigure the ensemble (e.g. removing bad nodes * such that a new quorum with smaller number of nodes can be formed.). * * See ZOOKEEPER-2819 for more details. */ public class ReconfigRollingRestartCompatibilityTest extends QuorumPeerTestBase { private static final String ZOO_CFG_BAK_FILE = "zoo.cfg.bak"; Map clientPorts = new HashMap<>(5); Map serverAddress = new HashMap<>(5); private String generateNewQuorumConfig(int serverCount) { StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < serverCount; i++) { clientPorts.put(i, PortAssignment.unique()); server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts.get(i); serverAddress.put(i, server); sb.append(server + "\n"); } return sb.toString(); } private String updateExistingQuorumConfig(List sidsToAdd, List sidsToRemove) { StringBuilder sb = new StringBuilder(); for (Integer sid : sidsToAdd) { clientPorts.put(sid, PortAssignment.unique()); serverAddress.put(sid, "server." + sid + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + clientPorts.get(sid)); } for (Integer sid : sidsToRemove) { clientPorts.remove(sid); serverAddress.remove(sid); } for (String server : serverAddress.values()) { sb.append(server + "\n"); } return sb.toString(); } // Verify no zoo.cfg.dynamic and zoo.cfg.bak files existing locally // when reconfig feature flag is off by default. @Test @Timeout(value = 60) public void testNoLocalDynamicConfigAndBackupFiles() throws InterruptedException, IOException { int serverCount = 3; String config = generateNewQuorumConfig(serverCount); QuorumPeerTestBase.MainThread[] mt = new QuorumPeerTestBase.MainThread[serverCount]; String[] staticFileContent = new String[serverCount]; for (int i = 0; i < serverCount; i++) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); } for (int i = 0; i < serverCount; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); assertNull(mt[i].getFileByName(ZOO_CFG_BAK_FILE), "static file backup (zoo.cfg.bak) shouldn't exist!"); assertNull(mt[i].getFileByName(mt[i].getQuorumPeer().getNextDynamicConfigFilename()), "dynamic configuration file (zoo.cfg.dynamic.*) shouldn't exist!"); staticFileContent[i] = Files.readAllLines(mt[i].confFile.toPath(), StandardCharsets.UTF_8).toString(); assertTrue(staticFileContent[i].contains(serverAddress.get(i)), "static config file should contain server entry " + serverAddress.get(i)); } for (int i = 0; i < serverCount; i++) { mt[i].shutdown(); } } // This test simulate the usual rolling restart with no membership change: // 1. A node is shutdown first (e.g. to upgrade software, or hardware, or cleanup local data.). // 2. After upgrade, start the node. // 3. Do this for every node, one at a time. @Test @Timeout(value = 60) public void testRollingRestartWithoutMembershipChange() throws Exception { int serverCount = 3; String config = generateNewQuorumConfig(serverCount); List joiningServers = new ArrayList<>(); QuorumPeerTestBase.MainThread[] mt = new QuorumPeerTestBase.MainThread[serverCount]; for (int i = 0; i < serverCount; ++i) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); joiningServers.add(serverAddress.get(i)); } for (int i = 0; i < serverCount; ++i) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } for (int i = 0; i < serverCount; ++i) { mt[i].shutdown(); mt[i].start(); verifyQuorumConfig(i, joiningServers, null); verifyQuorumMembers(mt[i]); } for (int i = 0; i < serverCount; i++) { mt[i].shutdown(); } } // This test simulate the use case of change of membership by starting new servers // without dynamic reconfig. For a 3 node ensemble we expand it to a 5 node ensemble, verify // during the process each node has the expected configuration setting pushed // via updating local zoo.cfg file. @Test @Timeout(value = 90) public void testExtendingQuorumWithNewMembers() throws Exception { int serverCount = 3; String config = generateNewQuorumConfig(serverCount); QuorumPeerTestBase.MainThread[] mt = new QuorumPeerTestBase.MainThread[serverCount]; List joiningServers = new ArrayList<>(); for (int i = 0; i < serverCount; ++i) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); joiningServers.add(serverAddress.get(i)); } for (int i = 0; i < serverCount; ++i) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } for (int i = 0; i < serverCount; ++i) { verifyQuorumConfig(i, joiningServers, null); verifyQuorumMembers(mt[i]); } Map oldServerAddress = new HashMap<>(serverAddress); List newServers = new ArrayList<>(joiningServers); config = updateExistingQuorumConfig(Arrays.asList(3, 4), new ArrayList<>()); newServers.add(serverAddress.get(3)); newServers.add(serverAddress.get(4)); serverCount = serverAddress.size(); assertEquals(serverCount, 5, "Server count should be 5 after config update."); // We are adding two new servers to the ensemble. These two servers should have the config which includes // all five servers (the old three servers, plus the two servers added). The old three servers should only // have the old three server config, because disabling reconfig will prevent synchronizing configs between // peers. mt = Arrays.copyOf(mt, mt.length + 2); for (int i = 3; i < 5; ++i) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); verifyQuorumConfig(i, newServers, null); verifyQuorumMembers(mt[i]); } Set expectedConfigs = new HashSet<>(); for (String conf : oldServerAddress.values()) { // Remove "server.x=" prefix which quorum peer does not include. expectedConfigs.add(conf.substring(conf.indexOf('=') + 1)); } for (int i = 0; i < 3; ++i) { verifyQuorumConfig(i, joiningServers, null); verifyQuorumMembers(mt[i], expectedConfigs); } for (int i = 0; i < serverCount; ++i) { mt[i].shutdown(); } } @Test public void testRollingRestartWithExtendedMembershipConfig() throws Exception { // in this test we are performing rolling restart with extended quorum config, see ZOOKEEPER-3829 // Start a quorum with 3 members int serverCount = 3; String config = generateNewQuorumConfig(serverCount); QuorumPeerTestBase.MainThread[] mt = new QuorumPeerTestBase.MainThread[serverCount]; List joiningServers = new ArrayList<>(); for (int i = 0; i < serverCount; i++) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); joiningServers.add(serverAddress.get(i)); } for (int i = 0; i < serverCount; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } for (int i = 0; i < serverCount; i++) { verifyQuorumConfig(i, joiningServers, null); verifyQuorumMembers(mt[i]); } // Create updated config with 4 members List newServers = new ArrayList<>(joiningServers); config = updateExistingQuorumConfig(Arrays.asList(3), new ArrayList<>()); newServers.add(serverAddress.get(3)); serverCount = serverAddress.size(); assertEquals(serverCount, 4, "Server count should be 4 after config update."); // We are adding one new server to the ensemble. The new server should be started with the new config mt = Arrays.copyOf(mt, mt.length + 1); mt[3] = new QuorumPeerTestBase.MainThread(3, clientPorts.get(3), config, false); mt[3].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(3), CONNECTION_TIMEOUT), "waiting for server 3 being up"); verifyQuorumConfig(3, newServers, null); verifyQuorumMembers(mt[3]); // Now we restart the first 3 servers, one-by-one with the new config for (int i = 0; i < 3; i++) { mt[i].shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPorts.get(i), ClientBase.CONNECTION_TIMEOUT), String.format("Timeout during waiting for server %d to go down", i)); mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); verifyQuorumConfig(i, newServers, null); verifyQuorumMembers(mt[i]); } // now verify that all nodes can handle traffic for (int i = 0; i < 4; ++i) { ZooKeeper zk = ClientBase.createZKClient("127.0.0.1:" + clientPorts.get(i)); ReconfigTest.testNormalOperation(zk, zk, false); } for (int i = 0; i < 4; ++i) { mt[i].shutdown(); } } @Test public void testRollingRestartWithHostAddedAndRemoved() throws Exception { // in this test we are performing rolling restart with a new quorum config, // contains a deleted node and a new node // Start a quorum with 3 members int serverCount = 3; String config = generateNewQuorumConfig(serverCount); QuorumPeerTestBase.MainThread[] mt = new QuorumPeerTestBase.MainThread[serverCount]; List originalServers = new ArrayList<>(); for (int i = 0; i < serverCount; i++) { mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); originalServers.add(serverAddress.get(i)); } for (int i = 0; i < serverCount; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } for (int i = 0; i < serverCount; i++) { verifyQuorumConfig(i, originalServers, null); verifyQuorumMembers(mt[i]); } // we are stopping the third server (myid=2) mt[2].shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPorts.get(2), ClientBase.CONNECTION_TIMEOUT), String.format("Timeout during waiting for server %d to go down", 2)); String leavingServer = originalServers.get(2); // Create updated config with the first 2 existing members, but we remove 3rd and add one with different myid config = updateExistingQuorumConfig(Arrays.asList(3), Arrays.asList(2)); List newServers = new ArrayList<>(serverAddress.values()); serverCount = serverAddress.size(); assertEquals(serverCount, 3, "Server count should be 3 after config update."); // We are adding one new server to the ensemble. The new server should be started with the new config mt = Arrays.copyOf(mt, mt.length + 1); mt[3] = new QuorumPeerTestBase.MainThread(3, clientPorts.get(3), config, false); mt[3].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(3), CONNECTION_TIMEOUT), "waiting for server 3 being up"); verifyQuorumConfig(3, newServers, Arrays.asList(leavingServer)); verifyQuorumMembers(mt[3]); // Now we restart the first 2 servers, one-by-one with the new config for (int i = 0; i < 2; i++) { mt[i].shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + clientPorts.get(i), ClientBase.CONNECTION_TIMEOUT), String.format("Timeout during waiting for server %d to go down", i)); mt[i] = new QuorumPeerTestBase.MainThread(i, clientPorts.get(i), config, false); mt[i].start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts.get(i), CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); verifyQuorumConfig(i, newServers, null); verifyQuorumMembers(mt[i]); } // now verify that all three nodes can handle traffic for (int i : serverAddress.keySet()) { ZooKeeper zk = ClientBase.createZKClient("127.0.0.1:" + clientPorts.get(i)); ReconfigTest.testNormalOperation(zk, zk, false); } for (int i : serverAddress.keySet()) { mt[i].shutdown(); } } // Verify each quorum peer has expected config in its config zNode. private void verifyQuorumConfig(int sid, List joiningServers, List leavingServers) throws Exception { ZooKeeper zk = ClientBase.createZKClient("127.0.0.1:" + clientPorts.get(sid)); ReconfigTest.testNormalOperation(zk, zk); ReconfigTest.testServerHasConfig(zk, joiningServers, leavingServers); zk.close(); } // Verify each quorum peer has expected quorum member view. private void verifyQuorumMembers(QuorumPeerTestBase.MainThread mt) { Set expectedConfigs = new HashSet<>(); for (String config : serverAddress.values()) { expectedConfigs.add(config.substring(config.indexOf('=') + 1)); } verifyQuorumMembers(mt, expectedConfigs); } private void verifyQuorumMembers(QuorumPeerTestBase.MainThread mt, Set expectedConfigs) { Map members = mt.getQuorumPeer().getQuorumVerifier().getAllMembers(); assertTrue(members.size() == expectedConfigs.size(), "Quorum member should not change."); for (QuorumPeer.QuorumServer qs : members.values()) { String actualConfig = qs.toString(); assertTrue(expectedConfigs.contains(actualConfig), "Unexpected config " + actualConfig + " found!"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000172 15051152474 032752 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/RemotePeerBeanTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/RemotePeerB0100644 0000000 0000000 00000006725 15051152474 034271 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.net.InetSocketAddress; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.junit.jupiter.api.Test; public class RemotePeerBeanTest { /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2269 */ @Test public void testGetClientAddressShouldReturnEmptyStringWhenClientAddressIsNull() { InetSocketAddress peerCommunicationAddress = null; // Here peerCommunicationAddress is null, also clientAddr is null QuorumServer peer = new QuorumServer(1, peerCommunicationAddress); RemotePeerBean remotePeerBean = new RemotePeerBean(null, peer); String clientAddress = remotePeerBean.getClientAddress(); assertNotNull(clientAddress); assertEquals(0, clientAddress.length()); } @Test @SuppressWarnings("unchecked") public void testIsLeader() { long peerId = 7; QuorumPeer.QuorumServer quorumServerMock = mock(QuorumPeer.QuorumServer.class); when(quorumServerMock.getId()).thenReturn(peerId); QuorumPeer peerMock = mock(QuorumPeer.class); RemotePeerBean remotePeerBean = new RemotePeerBean(peerMock, quorumServerMock); when(peerMock.isLeader(eq(peerId))).thenReturn(true); assertTrue(remotePeerBean.isLeader()); when(peerMock.isLeader(eq(peerId))).thenReturn(false); assertFalse(remotePeerBean.isLeader()); } @Test public void testHostPortReturnedWhenIPIsIPV6() { QuorumPeer.QuorumServer quorumServerMock = mock(QuorumPeer.QuorumServer.class); InetSocketAddress address = new InetSocketAddress("127::1", 2181); MultipleAddresses multipleAddresses = new MultipleAddresses(address); quorumServerMock.clientAddr = address; quorumServerMock.electionAddr = multipleAddresses; quorumServerMock.addr = multipleAddresses; QuorumPeer peerMock = mock(QuorumPeer.class); RemotePeerBean remotePeerBean = new RemotePeerBean(peerMock, quorumServerMock); String expectedHostPort = "[127:0:0:0:0:0:0:1]:2181"; assertEquals(expectedHostPort, remotePeerBean.getClientAddress()); assertEquals(expectedHostPort, remotePeerBean.getElectionAddress()); assertEquals(expectedHostPort, remotePeerBean.getQuorumAddress()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000203 15051152474 032745 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/SendAckRequestProcessorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/SendAckRequ0100644 0000000 0000000 00000003352 15051152474 034256 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; class SendAckRequestProcessorTest extends ZKTestCase { static class FakeLearner extends Learner { public FakeLearner() { sock = null; } void writePacket(QuorumPacket pp, boolean flush) throws IOException { throw new IOException(); } } @Test public void learnerSocketCloseTest() { SendAckRequestProcessor processor = new SendAckRequestProcessor(new FakeLearner()); processor.processRequest(new Request(0L, 0, ZooDefs.OpCode.sync, new TxnHeader(), new LearnerInfo(), 0L)); assertTrue(true, "should get here without exception"); } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000200 15051152474 032742 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/SessionUpgradeQuorumTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/SessionUpgr0100644 0000000 0000000 00000034000 15051152474 034364 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import javax.security.sasl.SaslException; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SessionUpgradeQuorumTest extends QuorumPeerTestBase { protected static final Logger LOG = LoggerFactory.getLogger(SessionUpgradeQuorumTest.class); public static final int CONNECTION_TIMEOUT = ClientBase.CONNECTION_TIMEOUT; public static final int SERVER_COUNT = 3; private MainThread[] mt; private int[] clientPorts; private TestQPMainDropSessionUpgrading[] qpMain; @BeforeEach public void setUp() throws Exception { LOG.info("STARTING quorum {}", getClass().getName()); // setup the env with RetainDB and local session upgrading ClientBase.setupTestEnv(); mt = new MainThread[SERVER_COUNT]; clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); sb.append("server.").append(i).append("=127.0.0.1:").append(PortAssignment.unique()).append(":").append(PortAssignment.unique()).append("\n"); } sb.append("localSessionsEnabled=true\n"); sb.append("localSessionsUpgradingEnabled=true\n"); String cfg = sb.toString(); // create a 3 server ensemble qpMain = new TestQPMainDropSessionUpgrading[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { final TestQPMainDropSessionUpgrading qp = new TestQPMainDropSessionUpgrading(); qpMain[i] = qp; mt[i] = new MainThread(i, clientPorts[i], cfg, false) { @Override public TestQPMain getTestQPMain() { return qp; } }; mt[i].start(); } for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } } @AfterEach public void tearDown() throws Exception { LOG.info("STOPPING quorum {}", getClass().getName()); for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } @Test public void testLocalSessionUpgradeSnapshot() throws IOException, InterruptedException { // select the candidate of follower int leader = -1; int followerA = -1; for (int i = SERVER_COUNT - 1; i >= 0; i--) { if (mt[i].main.quorumPeer.leader != null) { leader = i; } else if (followerA == -1) { followerA = i; } } LOG.info("follower A is {}", followerA); qpMain[followerA].setDropCreateSession(true); // create a client, and create an ephemeral node to trigger the // upgrading process final String node = "/node-1"; ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[followerA], ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); // clone the session id and passwd for later usage long sessionId = zk.getSessionId(); // should fail because of the injection try { zk.create(node, new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); fail("expect to failed to upgrade session due to the " + "TestQPMainDropSessionUpgrading is being used"); } catch (KeeperException e) { LOG.info("KeeperException when create ephemeral node.", e); } // force to take snapshot qpMain[followerA].quorumPeer.follower.zk.takeSnapshot(true); // wait snapshot finish Thread.sleep(500); // shutdown all servers for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } ArrayList waitStates = new ArrayList<>(); waitStates.add(States.CONNECTING); waitStates.add(States.CLOSED); waitForOne(zk, waitStates); // start the servers again, start follower A last as we want to // keep it running as follower for (int i = 0; i < SERVER_COUNT; i++) { mt[i].start(); } for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } // check global session not exist on follower A for (int i = 0; i < SERVER_COUNT; i++) { ConcurrentHashMap sessions = mt[i].main.quorumPeer.getZkDb().getSessionWithTimeOuts(); assertFalse(sessions.containsKey(sessionId), "server " + i + " should not have global " + "session " + sessionId); } zk.close(); } @Test public void testOnlyUpgradeSessionOnce() throws IOException, InterruptedException, KeeperException { // create a client, and create an ephemeral node to trigger the // upgrading process final String node = "/node-1"; ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[0], ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); long sessionId = zk.getSessionId(); QuorumZooKeeperServer server = (QuorumZooKeeperServer) mt[0].main.quorumPeer.getActiveServer(); Request create1 = createEphemeralRequest("/data-1", sessionId); Request create2 = createEphemeralRequest("/data-2", sessionId); assertNotNull(server.checkUpgradeSession(create1), "failed to upgrade on a ephemeral create"); assertNull(server.checkUpgradeSession(create2), "tried to upgrade again"); // clean al the setups and close the zk zk.close(); } @Test public void testCloseSessionWhileUpgradeOnLeader() throws IOException, KeeperException, InterruptedException { int leaderId = -1; for (int i = SERVER_COUNT - 1; i >= 0; i--) { if (mt[i].main.quorumPeer.leader != null) { leaderId = i; } } if (leaderId > 0) { makeSureEphemeralIsGone(leaderId); } } @Test public void testCloseSessionWhileUpgradeOnLearner() throws IOException, KeeperException, InterruptedException { int learnerId = -1; for (int i = SERVER_COUNT - 1; i >= 0; i--) { if (mt[i].main.quorumPeer.follower != null) { learnerId = i; } } if (learnerId > 0) { makeSureEphemeralIsGone(learnerId); } } private void makeSureEphemeralIsGone(int sid) throws IOException, KeeperException, InterruptedException { // Delay submit request to simulate the request queued in // RequestThrottler qpMain[sid].setSubmitDelayMs(200); // Create a client and an ephemeral node ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[sid], ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); final String node = "/node-1"; zk.create(node, new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, new StringCallback() { @Override public void processResult(int rc, String path, Object ctx, String name) {} }, null); // close the client zk.close(); // make sure the ephemeral is gone zk = new ZooKeeper("127.0.0.1:" + clientPorts[sid], ClientBase.CONNECTION_TIMEOUT, this); waitForOne(zk, States.CONNECTED); assertNull(zk.exists(node, false)); zk.close(); } private static class TestQPMainDropSessionUpgrading extends TestQPMain { private volatile boolean shouldDrop = false; private volatile int submitDelayMs = 0; public void setDropCreateSession(boolean dropCreateSession) { shouldDrop = dropCreateSession; } public void setSubmitDelayMs(int delay) { this.submitDelayMs = delay; } @Override protected QuorumPeer getQuorumPeer() throws SaslException { return new QuorumPeer() { @Override protected Leader makeLeader(FileTxnSnapLog logFactory) throws IOException { return new Leader(this, new LeaderZooKeeperServer( logFactory, this, this.getZkDb()) { @Override public void submitRequestNow(Request si) { if (submitDelayMs > 0) { try { Thread.sleep(submitDelayMs); } catch (Exception e) {} } super.submitRequestNow(si); } }); } @Override protected Follower makeFollower(FileTxnSnapLog logFactory) throws IOException { return new Follower(this, new FollowerZooKeeperServer(logFactory, this, this.getZkDb()) { @Override public void submitRequestNow(Request si) { if (submitDelayMs > 0) { try { Thread.sleep(submitDelayMs); } catch (Exception e) {} } super.submitRequestNow(si); } }) { @Override protected void request(Request request) throws IOException { if (!shouldDrop) { super.request(request); return; } LOG.info("request is {}, cnxn {}", request.type, request.cnxn); if (request.type == ZooDefs.OpCode.createSession) { LOG.info("drop createSession request {}", request); return; } if (request.type == ZooDefs.OpCode.create && request.cnxn != null) { CreateRequest createRequest = request.readRequestRecord(CreateRequest::new); try { CreateMode createMode = CreateMode.fromFlag(createRequest.getFlags()); if (createMode.isEphemeral()) { request.cnxn.sendCloseSession(); } } catch (KeeperException ignore) { } return; } super.request(request); } }; } }; } } private void waitForOne(ZooKeeper zk, ArrayList states) throws InterruptedException { int iterations = ClientBase.CONNECTION_TIMEOUT / 500; while (!states.contains(zk.getState())) { if (iterations-- == 0) { LOG.info("state is {}", zk.getState()); throw new RuntimeException("Waiting too long"); } Thread.sleep(500); } } private Request createEphemeralRequest(String path, long sessionId) throws IOException { ByteArrayOutputStream boas = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas); CreateRequest createRequest = new CreateRequest(path, "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL.toFlag()); createRequest.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray()); return new Request(null, sessionId, 1, ZooDefs.OpCode.create2, RequestRecord.fromBytes(bb), new ArrayList()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032756 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/StandaloneDisabledTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/StandaloneD0100644 0000000 0000000 00000025711 15051152474 034310 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.client.FourLetterWordMain; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ReconfigTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class StandaloneDisabledTest extends QuorumPeerTestBase { private final int NUM_SERVERS = 5; private MainThread[] peers; private ZooKeeper[] zkHandles; private ZooKeeperAdmin[] zkAdminHandles; private int[] clientPorts; private final int leaderId = 0; private final int follower1 = 1; private final int follower2 = 2; private final int observer1 = 3; private final int observer2 = 4; private ArrayList serverStrings; private ArrayList reconfigServers; /** * Test normal quorum operations work cleanly * with just a single server. */ @Test @Timeout(value = 10, unit = TimeUnit.MINUTES) public void startSingleServerTest() throws Exception { setUpData(); //start one server startServer(leaderId, serverStrings.get(leaderId) + "\n"); ReconfigTest.testServerHasConfig(zkHandles[leaderId], null, null); LOG.info("Initial Configuration:\n{}", new String(zkHandles[leaderId].getConfig(this, new Stat()))); //start and add 2 followers startFollowers(); testReconfig(leaderId, true, reconfigServers); LOG.info("Configuration after adding 2 followers:\n{}", new String(zkHandles[leaderId].getConfig(this, new Stat()))); //shutdown leader- quorum should still exist shutDownServer(leaderId); ReconfigTest.testNormalOperation(zkHandles[follower1], zkHandles[follower2]); //should not be able to remove follower 2 //No quorum in new config (1/2) reconfigServers.clear(); reconfigServers.add(Integer.toString(follower2)); try { ReconfigTest.reconfig(zkAdminHandles[follower1], null, reconfigServers, null, -1); fail("reconfig completed successfully even though there is no quorum up in new config!"); } catch (KeeperException.NewConfigNoQuorum e) { } //reconfigure out leader and follower 1. Remaining follower //2 should elect itself as leader and run by itself reconfigServers.clear(); reconfigServers.add(Integer.toString(leaderId)); reconfigServers.add(Integer.toString(follower1)); testReconfig(follower2, false, reconfigServers); LOG.info("Configuration after removing leader and follower 1:\n{}", new String(zkHandles[follower2].getConfig(this, new Stat()))); // Kill server 1 to avoid it interferences with FLE of the quorum {2, 3, 4}. shutDownServer(follower1); // Try to remove follower2, which is the only remaining server. This should fail. reconfigServers.clear(); reconfigServers.add(Integer.toString(follower2)); try { zkAdminHandles[follower2].reconfigure(null, reconfigServers, null, -1, new Stat()); fail("reconfig completed successfully even though there is no quorum up in new config!"); } catch (KeeperException.BadArgumentsException e) { // This is expected. } catch (Exception e) { fail("Should have been BadArgumentsException!"); } //Add two participants and change them to observers to check //that we can reconfigure down to one participant with observers. ArrayList observerStrings = new ArrayList<>(); startObservers(observerStrings); testReconfig(follower2, true, reconfigServers); //add partcipants testReconfig(follower2, true, observerStrings); //change to observers LOG.info("Configuration after adding two observers:\n{}", new String(zkHandles[follower2].getConfig(this, new Stat()))); shutDownData(); } /** * Initialize private data for test. */ private void setUpData() throws Exception { ClientBase.setupTestEnv(); QuorumPeerConfig.setStandaloneEnabled(false); QuorumPeerConfig.setReconfigEnabled(true); peers = new MainThread[NUM_SERVERS]; zkHandles = new ZooKeeper[NUM_SERVERS]; zkAdminHandles = new ZooKeeperAdmin[NUM_SERVERS]; clientPorts = new int[NUM_SERVERS]; serverStrings = buildServerStrings(); reconfigServers = new ArrayList<>(); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); } /** * Stop server threads. */ private void shutDownData() throws Exception { for (int i = 0; i < NUM_SERVERS; i++) { zkHandles[i].close(); zkAdminHandles[i].close(); } for (int i = 1; i < NUM_SERVERS; i++) { peers[i].shutdown(); } } /** * Create config strings that will be used for * the test servers. */ private ArrayList buildServerStrings() { ArrayList serverStrings = new ArrayList<>(); for (int i = 0; i < NUM_SERVERS; i++) { clientPorts[i] = PortAssignment.unique(); String server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;" + "localhost:" + clientPorts[i]; serverStrings.add(server); } return serverStrings; } /** * Starts a single server in replicated mode, * initializes its client, and waits for it * to be connected. */ private void startServer(int id, String config) throws Exception { peers[id] = new MainThread(id, clientPorts[id], config); peers[id].start(); assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[id], CONNECTION_TIMEOUT), "Server " + id + " is not up"); assertTrue(peers[id].isQuorumPeerRunning(), "Error- Server started in Standalone Mode!"); zkHandles[id] = ClientBase.createZKClient("127.0.0.1:" + clientPorts[id]); zkAdminHandles[id] = new ZooKeeperAdmin("127.0.0.1:" + clientPorts[id], CONNECTION_TIMEOUT, this); zkAdminHandles[id].addAuthInfo("digest", "super:test".getBytes()); String statCommandOut = FourLetterWordMain.send4LetterWord("127.0.0.1", clientPorts[id], "stat"); LOG.info("Started server id {} with config:\n{}\nStat output:\n{}", id, config, statCommandOut); } /** * Shuts down a server, waits for it to disconnect, * and gives enough time for the learner handler * in its ensemble to realize it's been shut down. */ private void shutDownServer(int id) throws Exception { peers[id].shutdown(); ClientBase.waitForServerDown("127.0.0.1:" + clientPorts[id], CONNECTION_TIMEOUT); TimeUnit.SECONDS.sleep(25); } /** * Starts servers 1 and 2 as participants and * adds them to the list to be reconfigured * into the ensemble. */ private void startFollowers() throws Exception { reconfigServers.clear(); for (int i = 1; i <= 2; i++) { String config = serverStrings.get(leaderId) + "\n" + serverStrings.get(i) + "\n" + serverStrings.get(i % 2 + 1) + "\n"; startServer(i, config); reconfigServers.add(serverStrings.get(i)); } } /** * Starts servers 1 and 2 as participants, * adds them to the list to be reconfigured * into the ensemble, and adds an observer * version of their information to a list * so they will be turned into observers later. */ private void startObservers(ArrayList observerStrings) throws Exception { reconfigServers.clear(); for (int i = observer1; i <= observer2; i++) { String config = serverStrings.get(follower2) + "\n" + serverStrings.get(i) + "\n"; startServer(i, config); reconfigServers.add(serverStrings.get(i)); observerStrings.add(serverStrings.get(i).replace("participant", "observer")); } } /** * Calls reconfig on the client corresponding to id to add or remove * the given servers. Tests appropriately to make sure the * reconfig succeeded. */ private void testReconfig(int id, boolean adding, ArrayList servers) throws Exception { if (adding) { ReconfigTest.reconfig(zkAdminHandles[id], servers, null, null, -1); for (String server : servers) { int id2 = Integer.parseInt(server.substring(7, 8)); //server.# ReconfigTest.testNormalOperation(zkHandles[id], zkHandles[id2]); } ReconfigTest.testServerHasConfig(zkHandles[id], servers, null); } else { ReconfigTest.reconfig(zkAdminHandles[id], null, servers, null, -1); ReconfigTest.testServerHasConfig(zkHandles[id], null, servers); } } /** * Ensure observer cannot start by itself **/ @Test public void startObserver() throws Exception { int clientPort = PortAssignment.unique(); String config = "server." + observer1 + "=localhost:" + PortAssignment.unique() + ":" + clientPort + ":observer;" + "localhost:" + PortAssignment.unique(); MainThread observer = new MainThread(observer1, clientPort, config); observer.start(); assertFalse( ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT), "Observer was able to start by itself!"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000167 15051152474 032756 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/StatCommandTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/StatCommand0100644 0000000 0000000 00000007543 15051152474 034331 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.command.FourLetterCommands; import org.apache.zookeeper.server.command.StatCommand; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class StatCommandTest { private StringWriter outputWriter; private StatCommand statCommand; private ServerStats.Provider providerMock; @BeforeEach public void setUp() { outputWriter = new StringWriter(); ServerCnxn serverCnxnMock = mock(ServerCnxn.class); LeaderZooKeeperServer zks = mock(LeaderZooKeeperServer.class); when(zks.isRunning()).thenReturn(true); providerMock = mock(ServerStats.Provider.class); when(zks.serverStats()).thenReturn(new ServerStats(providerMock)); ZKDatabase zkDatabaseMock = mock(ZKDatabase.class); when(zks.getZKDatabase()).thenReturn(zkDatabaseMock); Leader leaderMock = mock(Leader.class); when(leaderMock.getProposalStats()).thenReturn(new BufferStats()); when(zks.getLeader()).thenReturn(leaderMock); ServerCnxnFactory serverCnxnFactory = mock(ServerCnxnFactory.class); ServerCnxn serverCnxn = mock(ServerCnxn.class); List connections = new ArrayList<>(); connections.add(serverCnxn); when(serverCnxnFactory.getConnections()).thenReturn(connections); statCommand = new StatCommand(new PrintWriter(outputWriter), serverCnxnMock, FourLetterCommands.statCmd); statCommand.setZkServer(zks); statCommand.setFactory(serverCnxnFactory); } @Test public void testLeaderStatCommand() { // Arrange when(providerMock.getState()).thenReturn("leader"); // Act statCommand.commandRun(); // Assert String output = outputWriter.toString(); assertCommonStrings(output); assertThat(output, containsString("Mode: leader")); assertThat(output, containsString("Proposal sizes last/min/max:")); } @Test public void testFollowerStatCommand() { // Arrange when(providerMock.getState()).thenReturn("follower"); // Act statCommand.commandRun(); // Assert String output = outputWriter.toString(); assertCommonStrings(output); assertThat(output, containsString("Mode: follower")); } private void assertCommonStrings(String output) { assertThat(output, containsString("Clients:")); assertThat(output, containsString("Zookeeper version:")); assertThat(output, containsString("Node count:")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000174 15051152474 032754 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/StatResetCommandTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/StatResetCo0100644 0000000 0000000 00000007346 15051152474 034320 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.server.command.AbstractFourLetterCommand.ZK_NOT_SERVING; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.PrintWriter; import java.io.StringWriter; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerStats; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.command.StatResetCommand; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class StatResetCommandTest { private StatResetCommand statResetCommand; private StringWriter outputWriter; private ZooKeeperServer zks; private ServerStats serverStats; @BeforeEach public void setUp() { outputWriter = new StringWriter(); ServerCnxn serverCnxnMock = mock(ServerCnxn.class); zks = mock(ZooKeeperServer.class); when(zks.isRunning()).thenReturn(true); serverStats = mock(ServerStats.class); when(zks.serverStats()).thenReturn(serverStats); statResetCommand = new StatResetCommand(new PrintWriter(outputWriter), serverCnxnMock); statResetCommand.setZkServer(zks); } @Test public void testStatResetWithZKNotRunning() { // Arrange when(zks.isRunning()).thenReturn(false); // Act statResetCommand.commandRun(); // Assert String output = outputWriter.toString(); assertEquals(ZK_NOT_SERVING + "\n", output); } @Test public void testStatResetWithFollower() { // Arrange when(zks.isRunning()).thenReturn(true); when(serverStats.getServerState()).thenReturn("follower"); // Act statResetCommand.commandRun(); // Assert String output = outputWriter.toString(); assertEquals("Server stats reset.\n", output); verify(serverStats, times(1)).reset(); } @Test public void testStatResetWithLeader() { // Arrange LeaderZooKeeperServer leaderZks = mock(LeaderZooKeeperServer.class); when(leaderZks.isRunning()).thenReturn(true); when(leaderZks.serverStats()).thenReturn(serverStats); Leader leader = mock(Leader.class); when(leaderZks.getLeader()).thenReturn(leader); statResetCommand.setZkServer(leaderZks); when(serverStats.getServerState()).thenReturn("leader"); BufferStats bufferStats = mock(BufferStats.class); when(leader.getProposalStats()).thenReturn(bufferStats); // Act statResetCommand.commandRun(); // Assert String output = outputWriter.toString(); assertEquals("Server stats reset.\n", output); verify(serverStats, times(1)).reset(); verify(bufferStats, times(1)).reset(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000206 15051152474 032750 xustar000000000 0000000 134 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/SyncRequestProcessorMetricTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/SyncRequest0100644 0000000 0000000 00000010554 15051152474 034400 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestProcessor; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SyncRequestProcessorMetricTest { ZooKeeperServer zks; RequestProcessor nextProcessor; CountDownLatch allRequestsFlushed; @BeforeEach public void setup() throws Exception { ZKDatabase db = mock(ZKDatabase.class); when(db.append(any(Request.class))).thenReturn(true); doAnswer(invocation -> { Thread.sleep(100); return null; }).when(db).commit(); zks = mock(ZooKeeperServer.class); when(zks.getZKDatabase()).thenReturn(db); nextProcessor = mock(RequestProcessor.class); doAnswer(invocationOnMock -> { allRequestsFlushed.countDown(); return null; }).when(nextProcessor).processRequest(any(Request.class)); } private Request createRquest(long sessionId, int xid) { return new Request(null, sessionId, xid, ZooDefs.OpCode.setData, RequestRecord.fromBytes(new byte[10]), null); } @Test public void testSyncProcessorMetrics() throws Exception { SyncRequestProcessor syncProcessor = new SyncRequestProcessor(zks, nextProcessor); for (int i = 0; i < 500; i++) { syncProcessor.processRequest(createRquest(1, i)); } Map values = MetricsUtils.currentServerMetrics(); assertEquals(500L, values.get("sync_processor_request_queued")); allRequestsFlushed = new CountDownLatch(500); syncProcessor.start(); allRequestsFlushed.await(5000, TimeUnit.MILLISECONDS); values = MetricsUtils.currentServerMetrics(); assertEquals(501L, values.get("cnt_sync_processor_queue_size")); assertEquals(500L, values.get("max_sync_processor_queue_size")); assertEquals(0L, values.get("min_sync_processor_queue_size")); assertEquals(500L, values.get("cnt_sync_processor_queue_time_ms")); assertThat((long) values.get("max_sync_processor_queue_time_ms"), greaterThan(0L)); assertEquals(500L, values.get("cnt_sync_processor_queue_and_flush_time_ms")); assertThat((long) values.get("max_sync_processor_queue_and_flush_time_ms"), greaterThan(0L)); assertEquals(500L, values.get("cnt_sync_process_time")); assertThat((long) values.get("max_sync_process_time"), greaterThan(0L)); assertEquals(500L, values.get("max_sync_processor_batch_size")); assertEquals(1L, values.get("cnt_sync_processor_queue_flush_time_ms")); assertThat((long) values.get("max_sync_processor_queue_flush_time_ms"), greaterThanOrEqualTo(100L)); syncProcessor.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000214 15051152474 032747 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/UnifiedServerSocketModeDetectionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/UnifiedServ0100644 0000000 0000000 00000040720 15051152474 034334 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketOptions; import java.security.Security; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.KeyStoreFileType; import org.apache.zookeeper.common.X509KeyType; import org.apache.zookeeper.common.X509TestContext; import org.apache.zookeeper.common.X509Util; import org.apache.zookeeper.test.ClientBase; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This test makes sure that certain operations on a UnifiedServerSocket do not * trigger blocking mode detection. This is necessary to ensure that the * Leader's accept() thread doesn't get blocked. */ public class UnifiedServerSocketModeDetectionTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(UnifiedServerSocketModeDetectionTest.class); private static File tempDir; private static X509TestContext x509TestContext; private X509Util x509Util; private UnifiedServerSocket listeningSocket; private UnifiedServerSocket.UnifiedSocket serverSideSocket; private Socket clientSocket; private ExecutorService workerPool; private int port; private InetSocketAddress localServerAddress; @BeforeAll public static void setUpClass() throws Exception { Security.addProvider(new BouncyCastleProvider()); tempDir = ClientBase.createEmptyTestDir(); x509TestContext = X509TestContext.newBuilder().setTempDir(tempDir).setKeyStoreKeyType(X509KeyType.EC).setTrustStoreKeyType(X509KeyType.EC).build(); } @AfterAll public static void tearDownClass() { try { FileUtils.deleteDirectory(tempDir); } catch (IOException e) { // ignore } Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); } private static void forceClose(Socket s) { if (s == null || s.isClosed()) { return; } try { s.close(); } catch (IOException e) { } } private static void forceClose(ServerSocket s) { if (s == null || s.isClosed()) { return; } try { s.close(); } catch (IOException e) { } } public void init(boolean useSecureClient) throws Exception { x509Util = new ClientX509Util(); x509TestContext.setSystemProperties(x509Util, KeyStoreFileType.JKS, KeyStoreFileType.JKS); System.setProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty(), "100"); workerPool = Executors.newCachedThreadPool(); port = PortAssignment.unique(); localServerAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), port); listeningSocket = new UnifiedServerSocket(x509Util, true); listeningSocket.bind(localServerAddress); Future acceptFuture; acceptFuture = workerPool.submit(new Callable() { @Override public UnifiedServerSocket.UnifiedSocket call() throws Exception { try { return (UnifiedServerSocket.UnifiedSocket) listeningSocket.accept(); } catch (IOException e) { LOG.error("Error in accept()", e); throw e; } } }); if (useSecureClient) { clientSocket = x509Util.createSSLSocket(); clientSocket.connect(localServerAddress); } else { clientSocket = new Socket(); clientSocket.connect(localServerAddress); clientSocket.getOutputStream().write(new byte[]{1, 2, 3, 4, 5}); } serverSideSocket = acceptFuture.get(); } @AfterEach public void tearDown() throws Exception { x509TestContext.clearSystemProperties(x509Util); System.clearProperty(x509Util.getSslHandshakeDetectionTimeoutMillisProperty()); forceClose(listeningSocket); forceClose(serverSideSocket); forceClose(clientSocket); workerPool.shutdown(); workerPool.awaitTermination(1000, TimeUnit.MILLISECONDS); x509Util.close(); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetInetAddress(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getInetAddress(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetLocalAddress(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getLocalAddress(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetPort(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getPort(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetLocalPort(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getLocalPort(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetRemoteSocketAddress(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getRemoteSocketAddress(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetLocalSocketAddress(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getLocalSocketAddress(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetInputStream(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getInputStream(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetOutputStream(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getOutputStream(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetTcpNoDelay(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getTcpNoDelay(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetTcpNoDelay(boolean useSecureClient) throws Exception { init(useSecureClient); boolean tcpNoDelay = serverSideSocket.getTcpNoDelay(); tcpNoDelay = !tcpNoDelay; serverSideSocket.setTcpNoDelay(tcpNoDelay); assertFalse(serverSideSocket.isModeKnown()); assertEquals(tcpNoDelay, serverSideSocket.getTcpNoDelay()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetSoLinger(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getSoLinger(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetSoLinger(boolean useSecureClient) throws Exception { init(useSecureClient); int soLinger = serverSideSocket.getSoLinger(); if (soLinger == -1) { // enable it if disabled serverSideSocket.setSoLinger(true, 1); assertFalse(serverSideSocket.isModeKnown()); assertEquals(1, serverSideSocket.getSoLinger()); } else { // disable it if enabled serverSideSocket.setSoLinger(false, -1); assertFalse(serverSideSocket.isModeKnown()); assertEquals(-1, serverSideSocket.getSoLinger()); } } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetSoTimeout(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getSoTimeout(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetSoTimeout(boolean useSecureClient) throws Exception { init(useSecureClient); int timeout = serverSideSocket.getSoTimeout(); timeout = timeout + 10; serverSideSocket.setSoTimeout(timeout); assertFalse(serverSideSocket.isModeKnown()); assertEquals(timeout, serverSideSocket.getSoTimeout()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetSendBufferSize(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getSendBufferSize(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetSendBufferSize(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.setSendBufferSize(serverSideSocket.getSendBufferSize() + 1024); assertFalse(serverSideSocket.isModeKnown()); // Note: the new buffer size is a hint and socket implementation // is free to ignore it, so we don't verify that we get back the // same value. } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetReceiveBufferSize(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getReceiveBufferSize(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetReceiveBufferSize(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.setReceiveBufferSize(serverSideSocket.getReceiveBufferSize() + 1024); assertFalse(serverSideSocket.isModeKnown()); // Note: the new buffer size is a hint and socket implementation // is free to ignore it, so we don't verify that we get back the // same value. } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetKeepAlive(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getKeepAlive(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetKeepAlive(boolean useSecureClient) throws Exception { init(useSecureClient); boolean keepAlive = serverSideSocket.getKeepAlive(); keepAlive = !keepAlive; serverSideSocket.setKeepAlive(keepAlive); assertFalse(serverSideSocket.isModeKnown()); assertEquals(keepAlive, serverSideSocket.getKeepAlive()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetTrafficClass(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getTrafficClass(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetTrafficClass(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.setTrafficClass(SocketOptions.IP_TOS); assertFalse(serverSideSocket.isModeKnown()); // Note: according to the Socket javadocs, setTrafficClass() may be // ignored by socket implementations, so we don't check that the value // we set is returned. } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetReuseAddress(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.getReuseAddress(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetReuseAddress(boolean useSecureClient) throws Exception { init(useSecureClient); boolean reuseAddress = serverSideSocket.getReuseAddress(); reuseAddress = !reuseAddress; serverSideSocket.setReuseAddress(reuseAddress); assertFalse(serverSideSocket.isModeKnown()); assertEquals(reuseAddress, serverSideSocket.getReuseAddress()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testClose(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.close(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testShutdownInput(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.shutdownInput(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testShutdownOutput(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.shutdownOutput(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testIsConnected(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.isConnected(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testIsBound(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.isBound(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testIsClosed(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.isClosed(); assertFalse(serverSideSocket.isModeKnown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testIsInputShutdown(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.isInputShutdown(); assertFalse(serverSideSocket.isModeKnown()); serverSideSocket.shutdownInput(); assertTrue(serverSideSocket.isInputShutdown()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testIsOutputShutdown(boolean useSecureClient) throws Exception { init(useSecureClient); serverSideSocket.isOutputShutdown(); assertFalse(serverSideSocket.isModeKnown()); serverSideSocket.shutdownOutput(); assertTrue(serverSideSocket.isOutputShutdown()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032757 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/UnifiedServerSocketTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/UnifiedServ0100644 0000000 0000000 00000065555 15051152474 034351 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.BufferedInputStream; import java.io.IOException; import java.net.ConnectException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; import javax.net.ssl.SSLSocket; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.common.BaseX509ParameterizedTestCase; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.KeyStoreFileType; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.common.X509KeyType; import org.apache.zookeeper.common.X509TestContext; import org.apache.zookeeper.common.X509Util; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class UnifiedServerSocketTest extends BaseX509ParameterizedTestCase { public static Stream data() { ArrayList result = new ArrayList<>(); int paramIndex = 0; for (X509KeyType caKeyType : X509KeyType.values()) { for (X509KeyType certKeyType : X509KeyType.values()) { for (Boolean hostnameVerification : new Boolean[]{true, false}) { result.add(Arguments.of(caKeyType, certKeyType, hostnameVerification, paramIndex++)); } } } return result.stream(); } private static final int MAX_RETRIES = 5; private static final int TIMEOUT = 1000; private static final byte[] DATA_TO_CLIENT = "hello client".getBytes(); private static final byte[] DATA_FROM_CLIENT = "hello server".getBytes(); private X509Util x509Util; private InetSocketAddress localServerAddress; private final Object handshakeCompletedLock = new Object(); // access only inside synchronized(handshakeCompletedLock) { ... } blocks private boolean handshakeCompleted = false; public void init( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex) { super.init(paramIndex, () -> { try { return X509TestContext.newBuilder().setTempDir(tempDir).setKeyStoreKeyType(certKeyType).setTrustStoreKeyType(caKeyType).setHostnameVerification(hostnameVerification).build(); } catch (Exception e) { throw new RuntimeException(e); } }); } public void setUp() throws Exception { localServerAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), PortAssignment.unique()); x509Util = new ClientX509Util(); x509TestContext.setSystemProperties(x509Util, KeyStoreFileType.JKS, KeyStoreFileType.JKS); } @AfterEach public void tearDown() throws Exception { x509TestContext.clearSystemProperties(x509Util); x509Util.close(); } private static void forceClose(Socket s) { if (s == null || s.isClosed()) { return; } try { s.close(); } catch (IOException e) { } } private static void forceClose(ServerSocket s) { if (s == null || s.isClosed()) { return; } try { s.close(); } catch (IOException e) { } } private static final class UnifiedServerThread extends Thread { private final byte[] dataToClient; private List dataFromClients; private ExecutorService workerPool; private UnifiedServerSocket serverSocket; UnifiedServerThread(X509Util x509Util, InetSocketAddress bindAddress, boolean allowInsecureConnection, byte[] dataToClient) throws IOException { this.dataToClient = dataToClient; dataFromClients = new ArrayList<>(); workerPool = Executors.newCachedThreadPool(); serverSocket = new UnifiedServerSocket(x509Util, allowInsecureConnection); serverSocket.bind(bindAddress); } @Override public void run() { try { Random rnd = new Random(); while (true) { final Socket unifiedSocket = serverSocket.accept(); final boolean tcpNoDelay = rnd.nextBoolean(); unifiedSocket.setTcpNoDelay(tcpNoDelay); unifiedSocket.setSoTimeout(TIMEOUT); final boolean keepAlive = rnd.nextBoolean(); unifiedSocket.setKeepAlive(keepAlive); // Note: getting the input stream should not block the thread or trigger mode detection. BufferedInputStream bis = new BufferedInputStream(unifiedSocket.getInputStream()); workerPool.submit(new Runnable() { @Override public void run() { try { byte[] buf = new byte[1024]; int bytesRead = unifiedSocket.getInputStream().read(buf, 0, 1024); // Make sure the settings applied above before the socket was potentially upgraded to // TLS still apply. assertEquals(tcpNoDelay, unifiedSocket.getTcpNoDelay()); assertEquals(TIMEOUT, unifiedSocket.getSoTimeout()); assertEquals(keepAlive, unifiedSocket.getKeepAlive()); if (bytesRead > 0) { byte[] dataFromClient = new byte[bytesRead]; System.arraycopy(buf, 0, dataFromClient, 0, bytesRead); synchronized (dataFromClients) { dataFromClients.add(dataFromClient); } } unifiedSocket.getOutputStream().write(dataToClient); unifiedSocket.getOutputStream().flush(); } catch (IOException e) { throw new RuntimeException(e); } finally { forceClose(unifiedSocket); } } }); } } catch (IOException e) { throw new RuntimeException(e); } finally { forceClose(serverSocket); workerPool.shutdown(); } } public void shutdown(long millis) throws InterruptedException { forceClose(serverSocket); // this should break the run() loop workerPool.awaitTermination(millis, TimeUnit.MILLISECONDS); this.join(millis); } synchronized byte[] getDataFromClient(int index) { return dataFromClients.get(index); } synchronized boolean receivedAnyDataFromClient() { return !dataFromClients.isEmpty(); } } private SSLSocket connectWithSSL() throws IOException, X509Exception, InterruptedException { SSLSocket sslSocket = null; int retries = 0; while (retries < MAX_RETRIES) { try { sslSocket = x509Util.createSSLSocket(); sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() { @Override public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) { synchronized (handshakeCompletedLock) { handshakeCompleted = true; handshakeCompletedLock.notifyAll(); } } }); sslSocket.setSoTimeout(TIMEOUT); sslSocket.connect(localServerAddress, TIMEOUT); break; } catch (ConnectException connectException) { connectException.printStackTrace(); forceClose(sslSocket); sslSocket = null; Thread.sleep(TIMEOUT); } retries++; } assertNotNull(sslSocket, "Failed to connect to server with SSL"); return sslSocket; } private Socket connectWithoutSSL() throws IOException, InterruptedException { Socket socket = null; int retries = 0; while (retries < MAX_RETRIES) { try { socket = new Socket(); socket.setSoTimeout(TIMEOUT); socket.connect(localServerAddress, TIMEOUT); break; } catch (ConnectException connectException) { connectException.printStackTrace(); forceClose(socket); socket = null; Thread.sleep(TIMEOUT); } retries++; } assertNotNull(socket, "Failed to connect to server without SSL"); return socket; } // In the tests below, a "Strict" server means a UnifiedServerSocket that // does not allow plaintext connections (in other words, it's SSL-only). // A "Non Strict" server means a UnifiedServerSocket that allows both // plaintext and SSL incoming connections. /** * Attempting to connect to a SSL-or-plaintext server with SSL should work. */ @ParameterizedTest @MethodSource("data") public void testConnectWithSSLToNonStrictServer( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, true, DATA_TO_CLIENT); serverThread.start(); Socket sslSocket = connectWithSSL(); try { sslSocket.getOutputStream().write(DATA_FROM_CLIENT); sslSocket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = sslSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); synchronized (handshakeCompletedLock) { if (!handshakeCompleted) { handshakeCompletedLock.wait(TIMEOUT); } assertTrue(handshakeCompleted); } assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); } finally { forceClose(sslSocket); serverThread.shutdown(TIMEOUT); } } /** * Attempting to connect to a SSL-only server with SSL should work. */ @ParameterizedTest @MethodSource("data") public void testConnectWithSSLToStrictServer( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, false, DATA_TO_CLIENT); serverThread.start(); Socket sslSocket = connectWithSSL(); try { sslSocket.getOutputStream().write(DATA_FROM_CLIENT); sslSocket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = sslSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); synchronized (handshakeCompletedLock) { if (!handshakeCompleted) { handshakeCompletedLock.wait(TIMEOUT); } assertTrue(handshakeCompleted); } assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); } finally { forceClose(sslSocket); serverThread.shutdown(TIMEOUT); } } /** * Attempting to connect to a SSL-or-plaintext server without SSL should work. */ @ParameterizedTest @MethodSource("data") public void testConnectWithoutSSLToNonStrictServer( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, true, DATA_TO_CLIENT); serverThread.start(); Socket socket = connectWithoutSSL(); try { socket.getOutputStream().write(DATA_FROM_CLIENT); socket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = socket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); } finally { forceClose(socket); serverThread.shutdown(TIMEOUT); } } /** * Attempting to connect to a SSL-or-plaintext server without SSL with a * small initial data write should work. This makes sure that sending * less than 5 bytes does not break the logic in the server's initial 5 * byte read. */ @ParameterizedTest @MethodSource("data") public void testConnectWithoutSSLToNonStrictServerPartialWrite( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, true, DATA_TO_CLIENT); serverThread.start(); Socket socket = connectWithoutSSL(); try { // Write only 2 bytes of the message, wait a bit, then write the rest. // This makes sure that writes smaller than 5 bytes don't break the plaintext mode on the server // once it decides that the input doesn't look like a TLS handshake. socket.getOutputStream().write(DATA_FROM_CLIENT, 0, 2); socket.getOutputStream().flush(); Thread.sleep(TIMEOUT / 2); socket.getOutputStream().write(DATA_FROM_CLIENT, 2, DATA_FROM_CLIENT.length - 2); socket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = socket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); } finally { forceClose(socket); serverThread.shutdown(TIMEOUT); } } /** * Attempting to connect to a SSL-only server without SSL should fail. */ @ParameterizedTest @MethodSource("data") public void testConnectWithoutSSLToStrictServer( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, false, DATA_TO_CLIENT); serverThread.start(); Socket socket = connectWithoutSSL(); socket.getOutputStream().write(DATA_FROM_CLIENT); socket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; try { int bytesRead = socket.getInputStream().read(buf, 0, buf.length); if (bytesRead == -1) { // Using the NioSocketImpl after JDK 13, the expected behaviour on the client side // is to reach the end of the stream (bytesRead == -1), without a socket exception. return; } } catch (SocketException e) { // Using the old PlainSocketImpl (prior to JDK 13) we expect to get Socket Exception return; } finally { forceClose(socket); serverThread.shutdown(TIMEOUT); // independently of the client socket implementation details, we always make sure the // server didn't receive any data during the test assertFalse(serverThread.receivedAnyDataFromClient(), "The strict server accepted connection without SSL."); } fail("Expected server to hang up the connection. Read from server succeeded unexpectedly."); } /** * This test makes sure that UnifiedServerSocket used properly (a single * thread accept()-ing connections and handing the resulting sockets to * other threads for processing) is not vulnerable to blocking the * accept() thread while doing mode detection if a misbehaving client * connects. A misbehaving client is one that either disconnects * immediately, or connects but does not send any data. * * This version of the test uses a non-strict server socket (i.e. it * accepts both TLS and plaintext connections). */ @ParameterizedTest @MethodSource("data") public void testTLSDetectionNonBlockingNonStrictServerIdleClient( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); Socket badClientSocket = null; Socket clientSocket = null; Socket secureClientSocket = null; UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, true, DATA_TO_CLIENT); serverThread.start(); try { badClientSocket = connectWithoutSSL(); // Leave the bad client socket idle clientSocket = connectWithoutSSL(); clientSocket.getOutputStream().write(DATA_FROM_CLIENT); clientSocket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = clientSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); synchronized (handshakeCompletedLock) { assertFalse(handshakeCompleted); } secureClientSocket = connectWithSSL(); secureClientSocket.getOutputStream().write(DATA_FROM_CLIENT); secureClientSocket.getOutputStream().flush(); buf = new byte[DATA_TO_CLIENT.length]; bytesRead = secureClientSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(1)); synchronized (handshakeCompletedLock) { if (!handshakeCompleted) { handshakeCompletedLock.wait(TIMEOUT); } assertTrue(handshakeCompleted); } } finally { forceClose(badClientSocket); forceClose(clientSocket); forceClose(secureClientSocket); serverThread.shutdown(TIMEOUT); } } /** * Like the above test, but with a strict server socket (closes non-TLS * connections after seeing that there is no handshake). */ @ParameterizedTest @MethodSource("data") public void testTLSDetectionNonBlockingStrictServerIdleClient( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); Socket badClientSocket = null; Socket secureClientSocket = null; UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, false, DATA_TO_CLIENT); serverThread.start(); try { badClientSocket = connectWithoutSSL(); // Leave the bad client socket idle secureClientSocket = connectWithSSL(); secureClientSocket.getOutputStream().write(DATA_FROM_CLIENT); secureClientSocket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = secureClientSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); synchronized (handshakeCompletedLock) { if (!handshakeCompleted) { handshakeCompletedLock.wait(TIMEOUT); } assertTrue(handshakeCompleted); } assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); } finally { forceClose(badClientSocket); forceClose(secureClientSocket); serverThread.shutdown(TIMEOUT); } } /** * Similar to the tests above, but the bad client disconnects immediately * without sending any data. */ @ParameterizedTest @MethodSource("data") public void testTLSDetectionNonBlockingNonStrictServerDisconnectedClient( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); Socket clientSocket = null; Socket secureClientSocket = null; UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, true, DATA_TO_CLIENT); serverThread.start(); try { Socket badClientSocket = connectWithoutSSL(); forceClose(badClientSocket); // close the bad client socket immediately clientSocket = connectWithoutSSL(); clientSocket.getOutputStream().write(DATA_FROM_CLIENT); clientSocket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = clientSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); synchronized (handshakeCompletedLock) { assertFalse(handshakeCompleted); } secureClientSocket = connectWithSSL(); secureClientSocket.getOutputStream().write(DATA_FROM_CLIENT); secureClientSocket.getOutputStream().flush(); buf = new byte[DATA_TO_CLIENT.length]; bytesRead = secureClientSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(1)); synchronized (handshakeCompletedLock) { if (!handshakeCompleted) { handshakeCompletedLock.wait(TIMEOUT); } assertTrue(handshakeCompleted); } } finally { forceClose(clientSocket); forceClose(secureClientSocket); serverThread.shutdown(TIMEOUT); } } /** * Like the above test, but with a strict server socket (closes non-TLS * connections after seeing that there is no handshake). */ @ParameterizedTest @MethodSource("data") public void testTLSDetectionNonBlockingStrictServerDisconnectedClient( final X509KeyType caKeyType, final X509KeyType certKeyType, final Boolean hostnameVerification, final Integer paramIndex ) throws Exception { init(caKeyType, certKeyType, hostnameVerification, paramIndex); setUp(); Socket secureClientSocket = null; UnifiedServerThread serverThread = new UnifiedServerThread(x509Util, localServerAddress, false, DATA_TO_CLIENT); serverThread.start(); try { Socket badClientSocket = connectWithoutSSL(); forceClose(badClientSocket); // close the bad client socket immediately secureClientSocket = connectWithSSL(); secureClientSocket.getOutputStream().write(DATA_FROM_CLIENT); secureClientSocket.getOutputStream().flush(); byte[] buf = new byte[DATA_TO_CLIENT.length]; int bytesRead = secureClientSocket.getInputStream().read(buf, 0, buf.length); assertEquals(buf.length, bytesRead); assertArrayEquals(DATA_TO_CLIENT, buf); synchronized (handshakeCompletedLock) { if (!handshakeCompleted) { handshakeCompletedLock.wait(TIMEOUT); } assertTrue(handshakeCompleted); } assertArrayEquals(DATA_FROM_CLIENT, serverThread.getDataFromClient(0)); } finally { forceClose(secureClientSocket); serverThread.shutdown(TIMEOUT); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000165 15051152474 032754 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/WatchLeakTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/WatchLeakTe0100644 0000000 0000000 00000030451 15051152474 034245 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.apache.zookeeper.server.quorum; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.MockPacket; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.proto.SetWatches; import org.apache.zookeeper.server.MockNIOServerCnxn; import org.apache.zookeeper.server.MockSelectorThread; import org.apache.zookeeper.server.NIOServerCnxn; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Demonstrate ZOOKEEPER-1382 : Watches leak on expired session */ public class WatchLeakTest { protected static final Logger LOG = LoggerFactory.getLogger(WatchLeakTest.class); final long SESSION_ID = 0xBABEL; @BeforeEach public void setUp() { System.setProperty("zookeeper.admin.enableServer", "false"); } /** * Check that if session has expired then no watch can be set */ @ParameterizedTest @ValueSource(booleans = {true, false}) public void testWatchesLeak(boolean sessionTimedout) throws Exception { NIOServerCnxnFactory serverCnxnFactory = mock(NIOServerCnxnFactory.class); final SelectionKey sk = new FakeSK(); MockSelectorThread selectorThread = mock(MockSelectorThread.class); when(selectorThread.addInterestOpsUpdateRequest(any(SelectionKey.class))).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocation) throws Throwable { SelectionKey sk = (SelectionKey) invocation.getArguments()[0]; NIOServerCnxn nioSrvCnx = (NIOServerCnxn) sk.attachment(); sk.interestOps(nioSrvCnx.getInterestOps()); return true; } }); ZKDatabase database = new ZKDatabase(mock(FileTxnSnapLog.class)); database.setlastProcessedZxid(2L); QuorumPeer quorumPeer = mock(QuorumPeer.class); FileTxnSnapLog logfactory = mock(FileTxnSnapLog.class); // Directories are not used but we need it to avoid NPE when(logfactory.getDataLogDir()).thenReturn(new File("")); when(logfactory.getSnapDir()).thenReturn(new File("")); FollowerZooKeeperServer fzks = null; try { // Create a new follower fzks = new FollowerZooKeeperServer(logfactory, quorumPeer, database); fzks.startup(); fzks.setServerCnxnFactory(serverCnxnFactory); quorumPeer.follower = new MyFollower(quorumPeer, fzks); LOG.info("Follower created"); // Simulate a socket channel between a client and a follower final SocketChannel socketChannel = createClientSocketChannel(); // Create the NIOServerCnxn that will handle the client requests final MockNIOServerCnxn nioCnxn = new MockNIOServerCnxn(fzks, socketChannel, sk, serverCnxnFactory, selectorThread); sk.attach(nioCnxn); // Send the connection request as a client do nioCnxn.doIO(sk); LOG.info("Client connection sent"); // Send the valid or invalid session packet to the follower QuorumPacket qp = createValidateSessionPacketResponse(!sessionTimedout); quorumPeer.follower.processPacket(qp); LOG.info("Session validation sent"); // OK, now the follower knows that the session is valid or invalid, let's try // to send the watches nioCnxn.doIO(sk); // wait for the the request processor to do his job Thread.sleep(1000L); LOG.info("Watches processed"); // If session has not been validated, there must be NO watches int watchCount = database.getDataTree().getWatchCount(); if (sessionTimedout) { // Session has not been re-validated ! LOG.info("session is not valid, watches = {}", watchCount); assertEquals(0, watchCount, "Session is not valid so there should be no watches"); } else { // Session has been re-validated LOG.info("session is valid, watches = {}", watchCount); assertEquals(1, watchCount, "Session is valid so the watch should be there"); } } finally { if (fzks != null) { fzks.shutdown(); } } } /** * A follower with no real leader connection */ public static class MyFollower extends Follower { /** * Create a follower with a mocked leader connection * * @param self * @param zk */ MyFollower(QuorumPeer self, FollowerZooKeeperServer zk) { super(self, zk); leaderOs = mock(OutputArchive.class); leaderIs = mock(InputArchive.class); bufferedOutput = mock(BufferedOutputStream.class); } } /** * Simulate the behavior of a real selection key */ private static class FakeSK extends SelectionKey { @Override public SelectableChannel channel() { return null; } @Override public Selector selector() { return mock(Selector.class); } @Override public boolean isValid() { return true; } @Override public void cancel() { } @Override public int interestOps() { return ops; } private int ops = OP_WRITE + OP_READ; @Override public SelectionKey interestOps(int ops) { this.ops = ops; return this; } @Override public int readyOps() { boolean reading = (ops & OP_READ) != 0; boolean writing = (ops & OP_WRITE) != 0; if (reading && writing) { LOG.info("Channel is ready for reading and writing"); } else if (reading) { LOG.info("Channel is ready for reading only"); } else if (writing) { LOG.info("Channel is ready for writing only"); } return ops; } } /** * Create a watches message with a single watch on / * * @return a message that attempts to set 1 watch on / */ private ByteBuffer createWatchesMessage() { List dataWatches = new ArrayList<>(1); dataWatches.add("/"); List existWatches = Collections.emptyList(); List childWatches = Collections.emptyList(); SetWatches sw = new SetWatches(1L, dataWatches, existWatches, childWatches); RequestHeader h = new RequestHeader(); h.setType(ZooDefs.OpCode.setWatches); h.setXid(ClientCnxn.SET_WATCHES_XID); MockPacket p = new MockPacket(h, new ReplyHeader(), sw, null, null); return p.createAndReturnBB(); } /** * This is the secret that we use to generate passwords, for the moment it * is more of a sanity check. */ private static final long superSecret = 0XB3415C00L; /** * Create a connection request * * @return a serialized connection request */ private ByteBuffer createConnRequest() { Random r = new Random(SESSION_ID ^ superSecret); byte[] p = new byte[16]; r.nextBytes(p); ConnectRequest conReq = new ConnectRequest(0, 1L, 30000, SESSION_ID, p, false); MockPacket packet = new MockPacket(null, null, conReq, null, null); return packet.createAndReturnBB(); } /** * Mock a client channel with a connection request and a watches message * inside. * * @return a socket channel * @throws IOException */ private SocketChannel createClientSocketChannel() throws IOException { SocketChannel socketChannel = mock(SocketChannel.class); Socket socket = mock(Socket.class); InetSocketAddress socketAddress = new InetSocketAddress(1234); when(socket.getRemoteSocketAddress()).thenReturn(socketAddress); when(socketChannel.socket()).thenReturn(socket); // Send watches packet to server connection final ByteBuffer connRequest = createConnRequest(); final ByteBuffer watchesMessage = createWatchesMessage(); final ByteBuffer request = ByteBuffer.allocate(connRequest.limit() + watchesMessage.limit()); request.put(connRequest); request.put(watchesMessage); Answer answer = new Answer() { int i = 0; @Override public Integer answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); ByteBuffer bb = (ByteBuffer) args[0]; for (int k = 0; k < bb.limit(); k++) { bb.put(request.get(i)); i = i + 1; } return bb.limit(); } }; when(socketChannel.read(any(ByteBuffer.class))).thenAnswer(answer); return socketChannel; } /** * Forge an invalid session packet as a LEADER do * * @param valid true to create a valid session message * * @throws Exception */ private QuorumPacket createValidateSessionPacketResponse(boolean valid) throws Exception { QuorumPacket qp = createValidateSessionPacket(); ByteArrayInputStream bis = new ByteArrayInputStream(qp.getData()); DataInputStream dis = new DataInputStream(bis); long id = dis.readLong(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); dos.writeLong(id); // false means that the session has expired dos.writeBoolean(valid); qp.setData(bos.toByteArray()); return qp; } /** * Forge an validate session packet as a LEARNER do * * @return * @throws Exception */ private QuorumPacket createValidateSessionPacket() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeLong(SESSION_ID); dos.writeInt(3000); dos.close(); QuorumPacket qp = new QuorumPacket(Leader.REVALIDATE, -1, baos.toByteArray(), null); return qp; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000162 15051152474 032751 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/Zab1_0Test.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/Zab1_0Test.0100644 0000000 0000000 00000167656 15051152474 034065 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import static org.apache.zookeeper.server.quorum.ZabUtils.MockLeader; import static org.apache.zookeeper.server.quorum.ZabUtils.createLeader; import static org.apache.zookeeper.server.quorum.ZabUtils.createMockLeader; import static org.apache.zookeeper.server.quorum.ZabUtils.createQuorumPeer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentHashMap; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.InputArchive; import org.apache.jute.OutputArchive; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ByteBufferInputStream; import org.apache.zookeeper.server.ByteBufferOutputStream; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.util.ZxidUtils; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.TestUtils; import org.apache.zookeeper.txn.CreateSessionTxn; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.ErrorTxn; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Zab1_0Test extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(Zab1_0Test.class); private static final File testData = new File(System.getProperty("test.data.dir", "src/test/resources/data")); @BeforeEach public void setUp() { System.setProperty("zookeeper.admin.enableServer", "false"); } private static final class LeadThread extends Thread { private final Leader leader; private LeadThread(Leader leader) { this.leader = leader; } public void run() { try { leader.lead(); } catch (InterruptedException e) { LOG.info("Leader thread interrupted", e); } catch (Exception e) { LOG.warn("Unexpected exception in leader thread", e); } finally { leader.shutdown("lead ended"); } } } public static final class FollowerMockThread extends Thread { private final Leader leader; private final long followerSid; public long epoch = -1; public String msg = null; private boolean onlyGetEpochToPropose; private FollowerMockThread(long followerSid, Leader leader, boolean onlyGetEpochToPropose) { this.leader = leader; this.followerSid = followerSid; this.onlyGetEpochToPropose = onlyGetEpochToPropose; } public void run() { if (onlyGetEpochToPropose) { try { epoch = leader.getEpochToPropose(followerSid, 0); } catch (Exception e) { } } else { try { leader.waitForEpochAck(followerSid, new StateSummary(0, 0)); msg = "FollowerMockThread (id = " + followerSid + ") returned from waitForEpochAck"; } catch (Exception e) { } } } } @Test public void testLeaderInConnectingFollowers() throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Leader leader = null; try { QuorumPeer peer = createQuorumPeer(tmpDir); leader = createLeader(tmpDir, peer); peer.leader = leader; peer.setAcceptedEpoch(5); FollowerMockThread f1 = new FollowerMockThread(1, leader, true); FollowerMockThread f2 = new FollowerMockThread(2, leader, true); f1.start(); f2.start(); // wait until followers time out in getEpochToPropose - they shouldn't return // normally because the leader didn't execute getEpochToPropose and so its epoch was not // accounted for f1.join(leader.self.getInitLimit() * leader.self.getTickTime() + 5000); f2.join(leader.self.getInitLimit() * leader.self.getTickTime() + 5000); // even though followers timed out, their ids are in connectingFollowers, and their // epoch were accounted for, so the leader should not block and since it started with // accepted epoch = 5 it should now have 6 try { long epoch = leader.getEpochToPropose(leader.self.getMyId(), leader.self.getAcceptedEpoch()); assertEquals(6, epoch, "leader got wrong epoch from getEpochToPropose"); } catch (Exception e) { fail("leader timed out in getEpochToPropose"); } } finally { if (leader != null) { leader.shutdown("end of test"); } TestUtils.deleteFileRecursively(tmpDir); } } /** * In this test, the leader sets the last accepted epoch to 5. The call * to getEpochToPropose should set epoch to 6 and wait until another * follower executes it. If in getEpochToPropose we don't check if * lastAcceptedEpoch == epoch, then the call from the subsequent * follower with lastAcceptedEpoch = 6 doesn't change the value * of epoch, and the test fails. It passes with the fix to predicate. * * https://issues.apache.org/jira/browse/ZOOKEEPER-1343 * * * @throws Exception */ @Test public void testLastAcceptedEpoch() throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Leader leader = null; LeadThread leadThread = null; try { QuorumPeer peer = createQuorumPeer(tmpDir); leader = createMockLeader(tmpDir, peer); peer.leader = leader; peer.setAcceptedEpoch(5); leadThread = new LeadThread(leader); leadThread.start(); while (((MockLeader) leader).getCurrentEpochToPropose() != 6) { Thread.sleep(20); } try { long epoch = leader.getEpochToPropose(1, 6); assertEquals(7, epoch, "New proposed epoch is wrong"); } catch (Exception e) { fail("Timed out in getEpochToPropose"); } } finally { if (leader != null) { leader.shutdown("end of test"); } if (leadThread != null) { leadThread.interrupt(); leadThread.join(); } TestUtils.deleteFileRecursively(tmpDir); } } @Test public void testLeaderInElectingFollowers() throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Leader leader = null; try { QuorumPeer peer = createQuorumPeer(tmpDir); leader = createLeader(tmpDir, peer); peer.leader = leader; FollowerMockThread f1 = new FollowerMockThread(1, leader, false); FollowerMockThread f2 = new FollowerMockThread(2, leader, false); // things needed for waitForEpochAck to run (usually in leader.lead(), but we're not running leader here) leader.leaderStateSummary = new StateSummary(leader.self.getCurrentEpoch(), leader.zk.getLastProcessedZxid()); f1.start(); f2.start(); // wait until followers time out in waitForEpochAck - they shouldn't return // normally because the leader didn't execute waitForEpochAck f1.join(leader.self.getInitLimit() * leader.self.getTickTime() + 5000); f2.join(leader.self.getInitLimit() * leader.self.getTickTime() + 5000); // make sure that they timed out and didn't return normally assertTrue(f1.msg == null, f1.msg + " without waiting for leader"); assertTrue(f2.msg == null, f2.msg + " without waiting for leader"); } finally { if (leader != null) { leader.shutdown("end of test"); } TestUtils.deleteFileRecursively(tmpDir); } } static Socket[] getSocketPair() throws IOException { ServerSocket ss = new ServerSocket(0, 50, InetAddress.getByName("127.0.0.1")); InetSocketAddress endPoint = (InetSocketAddress) ss.getLocalSocketAddress(); Socket s = new Socket(endPoint.getAddress(), endPoint.getPort()); return new Socket[]{s, ss.accept()}; } static void readPacketSkippingPing(InputArchive ia, QuorumPacket qp) throws IOException { while (true) { ia.readRecord(qp, null); if (qp.getType() != Leader.PING) { return; } } } public interface LeaderConversation { void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws Exception; } public interface PopulatedLeaderConversation { void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l, long zxid) throws Exception; } public interface FollowerConversation { void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception; } public interface ObserverConversation { void converseWithObserver(InputArchive ia, OutputArchive oa, Observer o) throws Exception; } public void testLeaderConversation(LeaderConversation conversation) throws Exception { Socket[] pair = getSocketPair(); Socket leaderSocket = pair[0]; Socket followerSocket = pair[1]; File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); LeadThread leadThread = null; Leader leader = null; try { QuorumPeer peer = createQuorumPeer(tmpDir); leader = createLeader(tmpDir, peer); peer.leader = leader; leadThread = new LeadThread(leader); leadThread.start(); while (leader.cnxAcceptor == null || !leader.cnxAcceptor.isAlive()) { Thread.sleep(20); } LearnerHandler lh = new LearnerHandler(leaderSocket, new BufferedInputStream(leaderSocket.getInputStream()), leader); lh.start(); leaderSocket.setSoTimeout(4000); InputArchive ia = BinaryInputArchive.getArchive(followerSocket.getInputStream()); OutputArchive oa = BinaryOutputArchive.getArchive(followerSocket.getOutputStream()); conversation.converseWithLeader(ia, oa, leader); } finally { if (leader != null) { leader.shutdown("end of test"); } if (leadThread != null) { leadThread.interrupt(); leadThread.join(); } TestUtils.deleteFileRecursively(tmpDir); } } public void testPopulatedLeaderConversation(PopulatedLeaderConversation conversation, int ops) throws Exception { Socket[] pair = getSocketPair(); Socket leaderSocket = pair[0]; Socket followerSocket = pair[1]; File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); LeadThread leadThread = null; Leader leader = null; try { // Setup a database with two znodes FileTxnSnapLog snapLog = new FileTxnSnapLog(tmpDir, tmpDir); ZKDatabase zkDb = new ZKDatabase(snapLog); assertTrue(ops >= 1); long zxid = ZxidUtils.makeZxid(1, 0); for (int i = 1; i <= ops; i++) { zxid = ZxidUtils.makeZxid(1, i); String path = "/foo-" + i; zkDb.processTxn(new TxnHeader(13, 1000 + i, zxid, 30 + i, ZooDefs.OpCode.create), new CreateTxn(path, "fpjwasalsohere".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); Stat stat = new Stat(); assertEquals("fpjwasalsohere", new String(zkDb.getData(path, stat, null))); } assertTrue(zxid > ZxidUtils.makeZxid(1, 0)); // Generate snapshot and close files. snapLog.save(zkDb.getDataTree(), zkDb.getSessionWithTimeOuts(), false); snapLog.close(); QuorumPeer peer = createQuorumPeer(tmpDir); leader = createLeader(tmpDir, peer); peer.leader = leader; // Set the last accepted epoch and current epochs to be 1 peer.setAcceptedEpoch(1); peer.setCurrentEpoch(1); leadThread = new LeadThread(leader); leadThread.start(); while (leader.cnxAcceptor == null || !leader.cnxAcceptor.isAlive()) { Thread.sleep(20); } LearnerHandler lh = new LearnerHandler(leaderSocket, new BufferedInputStream(leaderSocket.getInputStream()), leader); lh.start(); leaderSocket.setSoTimeout(4000); InputArchive ia = BinaryInputArchive.getArchive(followerSocket.getInputStream()); OutputArchive oa = BinaryOutputArchive.getArchive(followerSocket.getOutputStream()); conversation.converseWithLeader(ia, oa, leader, zxid); } finally { if (leader != null) { leader.shutdown("end of test"); } if (leadThread != null) { leadThread.interrupt(); leadThread.join(); } TestUtils.deleteFileRecursively(tmpDir); } } public void testFollowerConversation(FollowerConversation conversation) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Thread followerThread = null; ConversableFollower follower = null; QuorumPeer peer = null; try { peer = createQuorumPeer(tmpDir); follower = createFollower(tmpDir, peer); peer.follower = follower; ServerSocket ss = new ServerSocket(0, 50, InetAddress.getByName("127.0.0.1")); QuorumServer leaderQS = new QuorumServer(1, (InetSocketAddress) ss.getLocalSocketAddress()); follower.setLeaderQuorumServer(leaderQS); final Follower followerForThread = follower; followerThread = new Thread() { public void run() { try { followerForThread.followLeader(); } catch (InterruptedException e) { LOG.info("Follower thread interrupted", e); } catch (Exception e) { LOG.warn("Unexpected exception in follower thread", e); } } }; followerThread.start(); Socket leaderSocket = ss.accept(); InputArchive ia = BinaryInputArchive.getArchive(leaderSocket.getInputStream()); OutputArchive oa = BinaryOutputArchive.getArchive(leaderSocket.getOutputStream()); conversation.converseWithFollower(ia, oa, follower); } finally { if (follower != null) { follower.shutdown(); } if (followerThread != null) { followerThread.interrupt(); followerThread.join(); } if (peer != null) { peer.shutdown(); } TestUtils.deleteFileRecursively(tmpDir); } } public void testObserverConversation(ObserverConversation conversation) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); Thread observerThread = null; ConversableObserver observer = null; QuorumPeer peer = null; try { peer = createQuorumPeer(tmpDir); peer.setSyncEnabled(true); observer = createObserver(tmpDir, peer); peer.observer = observer; ServerSocket ss = new ServerSocket(0, 50, InetAddress.getByName("127.0.0.1")); QuorumServer leaderQS = new QuorumServer(1, (InetSocketAddress) ss.getLocalSocketAddress()); observer.setLeaderQuorumServer(leaderQS); final Observer observerForThread = observer; observerThread = new Thread() { public void run() { try { observerForThread.observeLeader(); } catch (Exception e) { e.printStackTrace(); } } }; observerThread.start(); Socket leaderSocket = ss.accept(); InputArchive ia = BinaryInputArchive.getArchive(leaderSocket.getInputStream()); OutputArchive oa = BinaryOutputArchive.getArchive(leaderSocket.getOutputStream()); conversation.converseWithObserver(ia, oa, observer); } finally { if (observer != null) { observer.shutdown(); } if (observerThread != null) { observerThread.interrupt(); observerThread.join(); } if (peer != null) { peer.shutdown(); } TestUtils.deleteFileRecursively(tmpDir); } } @Test public void testUnnecessarySnap() throws Exception { testPopulatedLeaderConversation(new PopulatedLeaderConversation() { @Override public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l, long zxid) throws Exception { assertEquals(1, l.self.getAcceptedEpoch()); assertEquals(1, l.self.getCurrentEpoch()); /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000, 0); byte[] liBytes = new byte[20]; ByteBufferOutputStream.record2ByteBuffer(li, ByteBuffer.wrap(liBytes)); QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 1, liBytes, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.LEADERINFO, qp.getType()); assertEquals(ZxidUtils.makeZxid(2, 0), qp.getZxid()); assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), 0x10000); assertEquals(2, l.self.getAcceptedEpoch()); assertEquals(1, l.self.getCurrentEpoch()); byte[] epochBytes = new byte[4]; final ByteBuffer wrappedEpochBytes = ByteBuffer.wrap(epochBytes); wrappedEpochBytes.putInt(1); qp = new QuorumPacket(Leader.ACKEPOCH, zxid, epochBytes, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.DIFF, qp.getType()); } }, 2); } // We want to track the change with a callback rather than depending on timing class TrackerWatcher implements Watcher { boolean changed; synchronized void waitForChange() throws InterruptedException { while (!changed) { wait(); } } @Override public void process(WatchedEvent event) { if (event.getType() == EventType.NodeDataChanged) { synchronized (this) { changed = true; notifyAll(); } } } public synchronized boolean changed() { return changed; } } @Test public void testNormalFollowerRun() throws Exception { testFollowerConversation(new FollowerConversation() { @Override public void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataLogDir().getParentFile(); File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); //Spy on ZK so we can check if a snapshot happened or not. f.zk = spy(f.zk); try { assertEquals(0, f.self.getAcceptedEpoch()); assertEquals(0, f.self.getCurrentEpoch()); // Setup a database with a single /foo node ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); final long firstZxid = ZxidUtils.makeZxid(1, 1); zkDb.processTxn(new TxnHeader(13, 1313, firstZxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); Stat stat = new Stat(); assertEquals("data1", new String(zkDb.getData("/foo", stat, null))); QuorumPacket qp = new QuorumPacket(); readPacketSkippingPing(ia, qp); assertEquals(Leader.FOLLOWERINFO, qp.getType()); assertEquals(qp.getZxid(), 0); LearnerInfo learnInfo = new LearnerInfo(); ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo); assertEquals(learnInfo.getProtocolVersion(), 0x10000); assertEquals(learnInfo.getServerid(), 0); // We are simulating an established leader, so the epoch is 1 qp.setType(Leader.LEADERINFO); qp.setZxid(ZxidUtils.makeZxid(1, 0)); byte[] protoBytes = new byte[4]; ByteBuffer.wrap(protoBytes).putInt(0x10000); qp.setData(protoBytes); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.ACKEPOCH, qp.getType()); assertEquals(0, qp.getZxid()); assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer.wrap(qp.getData()).getInt()); assertEquals(1, f.self.getAcceptedEpoch()); assertEquals(0, f.self.getCurrentEpoch()); // Send the snapshot we created earlier qp.setType(Leader.SNAP); qp.setData(new byte[0]); qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); oa.writeRecord(qp, null); zkDb.serializeSnapshot(oa); oa.writeString("BenWasHere", null); Thread.sleep(10); //Give it some time to process the snap //No Snapshot taken yet, the SNAP was applied in memory verify(f.zk, never()).takeSnapshot(); qp.setType(Leader.NEWLEADER); qp.setZxid(ZxidUtils.makeZxid(1, 0)); oa.writeRecord(qp, null); // Get the ack of the new leader readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(1, f.self.getAcceptedEpoch()); assertEquals(1, f.self.getCurrentEpoch()); //Make sure that we did take the snapshot now verify(f.zk).takeSnapshot(true); assertEquals(firstZxid, f.fzk.getLastProcessedZxid()); // Make sure the data was recorded in the filesystem ok ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); long lastZxid = zkDb2.loadDataBase(); assertEquals("data1", new String(zkDb2.getData("/foo", stat, null))); assertEquals(firstZxid, lastZxid); // Propose an update long proposalZxid = ZxidUtils.makeZxid(1, 1000); proposeSetData(qp, proposalZxid, "data2", 2); oa.writeRecord(qp, null); TrackerWatcher watcher = new TrackerWatcher(); // The change should not have happened yet, since we haven't committed assertEquals("data1", new String(f.fzk.getZKDatabase().getData("/foo", stat, watcher))); // The change should happen now qp.setType(Leader.COMMIT); qp.setZxid(proposalZxid); oa.writeRecord(qp, null); qp.setType(Leader.UPTODATE); qp.setZxid(0); oa.writeRecord(qp, null); // Read the uptodate ack readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(proposalZxid, qp.getZxid()); watcher.waitForChange(); assertEquals("data2", new String(f.fzk.getZKDatabase().getData("/foo", stat, null))); // check and make sure the change is persisted zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); lastZxid = zkDb2.loadDataBase(); assertEquals("data2", new String(zkDb2.getData("/foo", stat, null))); assertEquals(proposalZxid, lastZxid); } finally { TestUtils.deleteFileRecursively(tmpDir); } } private void proposeSetData(QuorumPacket qp, long zxid, String data, int version) throws IOException { qp.setType(Leader.PROPOSAL); qp.setZxid(zxid); TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.setData); SetDataTxn sdt = new SetDataTxn("/foo", data.getBytes(), version); ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeRecord(hdr, null); boa.writeRecord(sdt, null); qp.setData(baos.toByteArray()); } }); } @Test public void testNormalFollowerRunWithDiff() throws Exception { testFollowerConversation(new FollowerConversation() { @Override public void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataLogDir().getParentFile(); File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); //Spy on ZK so we can check if a snapshot happened or not. f.zk = spy(f.zk); try { assertEquals(0, f.self.getAcceptedEpoch()); assertEquals(0, f.self.getCurrentEpoch()); // Setup a database with a single /foo node ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); final long firstZxid = ZxidUtils.makeZxid(1, 1); zkDb.processTxn(new TxnHeader(13, 1313, firstZxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); Stat stat = new Stat(); assertEquals("data1", new String(zkDb.getData("/foo", stat, null))); QuorumPacket qp = new QuorumPacket(); readPacketSkippingPing(ia, qp); assertEquals(Leader.FOLLOWERINFO, qp.getType()); assertEquals(qp.getZxid(), 0); LearnerInfo learnInfo = new LearnerInfo(); ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo); assertEquals(learnInfo.getProtocolVersion(), 0x10000); assertEquals(learnInfo.getServerid(), 0); // We are simulating an established leader, so the epoch is 1 qp.setType(Leader.LEADERINFO); qp.setZxid(ZxidUtils.makeZxid(1, 0)); byte[] protoBytes = new byte[4]; ByteBuffer.wrap(protoBytes).putInt(0x10000); qp.setData(protoBytes); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.ACKEPOCH, qp.getType()); assertEquals(ZxidUtils.makeZxid(0, 0), qp.getZxid()); assertEquals(0, ByteBuffer.wrap(qp.getData()).getInt()); assertEquals(1, f.self.getAcceptedEpoch()); assertEquals(0, f.self.getCurrentEpoch()); // Send a diff qp.setType(Leader.DIFF); qp.setData(new byte[0]); qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); oa.writeRecord(qp, null); final long createSessionZxid = ZxidUtils.makeZxid(1, 2); proposeNewSession(qp, createSessionZxid, 0x333); oa.writeRecord(qp, null); qp.setType(Leader.COMMIT); qp.setZxid(createSessionZxid); oa.writeRecord(qp, null); qp.setType(Leader.NEWLEADER); qp.setZxid(ZxidUtils.makeZxid(1, 0)); qp.setData(null); oa.writeRecord(qp, null); qp.setType(Leader.UPTODATE); qp.setZxid(0); oa.writeRecord(qp, null); // Get the ack of the new leader readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(1, f.self.getAcceptedEpoch()); assertEquals(1, f.self.getCurrentEpoch()); assertEquals(createSessionZxid, f.fzk.getLastProcessedZxid()); // Read the uptodate ack readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); // Make sure the data was recorded in the filesystem ok ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); zkDb2.loadDataBase(); LOG.info("zkdb2 sessions:{}", zkDb2.getSessions()); LOG.info("zkdb2 with timeouts:{}", zkDb2.getSessionWithTimeOuts()); assertNotNull(zkDb2.getSessionWithTimeOuts().get(4L)); //Snapshot was never taken during very simple sync verify(f.zk, never()).takeSnapshot(); } finally { TestUtils.deleteFileRecursively(tmpDir); } } private void proposeNewSession(QuorumPacket qp, long zxid, long sessionId) throws IOException { qp.setType(Leader.PROPOSAL); qp.setZxid(zxid); TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.createSession); CreateSessionTxn cst = new CreateSessionTxn(30000); ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeRecord(hdr, null); boa.writeRecord(cst, null); qp.setData(baos.toByteArray()); } }); } @Test public void testNormalFollowerRun_ProcessCommitInSyncAfterAckNewLeader() throws Exception { testFollowerConversation(new FollowerConversation() { @Override public void converseWithFollower(InputArchive ia, OutputArchive oa, Follower f) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = f.fzk.getTxnLogFactory().getDataLogDir().getParentFile(); File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile(); //Spy on ZK so we can check if a snapshot happened or not. f.zk = spy(f.zk); try { assertEquals(0, f.self.getAcceptedEpoch()); assertEquals(0, f.self.getCurrentEpoch()); // Setup a database with a single /foo node ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); final long firstZxid = ZxidUtils.makeZxid(1, 1); zkDb.processTxn(new TxnHeader(13, 1313, firstZxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); Stat stat = new Stat(); assertEquals("data1", new String(zkDb.getData("/foo", stat, null))); QuorumPacket qp = new QuorumPacket(); readPacketSkippingPing(ia, qp); assertEquals(Leader.FOLLOWERINFO, qp.getType()); assertEquals(qp.getZxid(), 0); LearnerInfo learnInfo = new LearnerInfo(); ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo); assertEquals(learnInfo.getProtocolVersion(), 0x10000); assertEquals(learnInfo.getServerid(), 0); // We are simulating an established leader, so the epoch is 1 qp.setType(Leader.LEADERINFO); qp.setZxid(ZxidUtils.makeZxid(1, 0)); byte[] protoBytes = new byte[4]; ByteBuffer.wrap(protoBytes).putInt(0x10000); qp.setData(protoBytes); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.ACKEPOCH, qp.getType()); assertEquals(0, qp.getZxid()); assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer.wrap(qp.getData()).getInt()); assertEquals(1, f.self.getAcceptedEpoch()); assertEquals(0, f.self.getCurrentEpoch()); // Send the snapshot we created earlier qp.setType(Leader.SNAP); qp.setData(new byte[0]); qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); oa.writeRecord(qp, null); zkDb.serializeSnapshot(oa); oa.writeString("BenWasHere", null); Thread.sleep(10); //Give it some time to process the snap //No Snapshot taken yet, the SNAP was applied in memory verify(f.zk, never()).takeSnapshot(); // Leader sends an outstanding proposal long proposalZxid = ZxidUtils.makeZxid(1, 1001); proposeSetData(qp, proposalZxid, "data2", 2); oa.writeRecord(qp, null); qp.setType(Leader.NEWLEADER); qp.setZxid(ZxidUtils.makeZxid(1, 0)); qp.setData(null); oa.writeRecord(qp, null); // Get the ack of the new leader readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(1, f.self.getAcceptedEpoch()); assertEquals(1, f.self.getCurrentEpoch()); //Make sure that we did take the snapshot now verify(f.zk).takeSnapshot(true); assertEquals(firstZxid, f.fzk.getLastProcessedZxid()); // The outstanding proposal has not been persisted yet ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); long lastZxid = zkDb2.loadDataBase(); assertEquals("data1", new String(zkDb2.getData("/foo", stat, null))); assertEquals(firstZxid, lastZxid); TrackerWatcher watcher = new TrackerWatcher(); // The change should not have happened yet assertEquals("data1", new String(f.fzk.getZKDatabase().getData("/foo", stat, watcher))); // Leader commits proposalZxid right after it sends NEWLEADER to follower qp.setType(Leader.COMMIT); qp.setZxid(proposalZxid); qp.setData(null); oa.writeRecord(qp, null); qp.setType(Leader.UPTODATE); qp.setZxid(0); qp.setData(null); oa.writeRecord(qp, null); // Read the uptodate ack readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(proposalZxid, qp.getZxid()); // The change should happen now watcher.waitForChange(); assertEquals("data2", new String(f.fzk.getZKDatabase().getData("/foo", stat, null))); // check and make sure the change is persisted zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); lastZxid = zkDb2.loadDataBase(); assertEquals("data2", new String(zkDb2.getData("/foo", stat, null))); assertEquals(proposalZxid, lastZxid); } finally { TestUtils.deleteFileRecursively(tmpDir); } } private void proposeSetData(QuorumPacket qp, long zxid, String data, int version) throws IOException { qp.setType(Leader.PROPOSAL); qp.setZxid(zxid); TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.setData); SetDataTxn sdt = new SetDataTxn("/foo", data.getBytes(), version); ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeRecord(hdr, null); boa.writeRecord(sdt, null); qp.setData(baos.toByteArray()); } }); } @Test public void testNormalRun() throws Exception { testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException { assertEquals(0, l.self.getAcceptedEpoch()); assertEquals(0, l.self.getCurrentEpoch()); /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000, 0); byte[] liBytes = new byte[20]; ByteBufferOutputStream.record2ByteBuffer(li, ByteBuffer.wrap(liBytes)); QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 0, liBytes, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.LEADERINFO, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), 0x10000); assertEquals(1, l.self.getAcceptedEpoch()); assertEquals(0, l.self.getCurrentEpoch()); qp = new QuorumPacket(Leader.ACKEPOCH, 0, new byte[4], null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.DIFF, qp.getType()); readPacketSkippingPing(ia, qp); assertEquals(Leader.NEWLEADER, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(1, l.self.getAcceptedEpoch()); assertCurrentEpochGotUpdated(1, l.self, ClientBase.CONNECTION_TIMEOUT); qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.UPTODATE, qp.getType()); } }); } @Test public void testTxnTimeout() throws Exception { testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException, InterruptedException, org.apache.zookeeper.server.quorum.Leader.XidRolloverException { assertEquals(0, l.self.getAcceptedEpoch()); assertEquals(0, l.self.getCurrentEpoch()); LearnerInfo li = new LearnerInfo(1, 0x10000, 0); byte[] liBytes = new byte[20]; ByteBufferOutputStream.record2ByteBuffer(li, ByteBuffer.wrap(liBytes)); QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 0, liBytes, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.LEADERINFO, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), 0x10000); assertEquals(1, l.self.getAcceptedEpoch()); assertEquals(0, l.self.getCurrentEpoch()); qp = new QuorumPacket(Leader.ACKEPOCH, 0, new byte[4], null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.DIFF, qp.getType()); readPacketSkippingPing(ia, qp); assertEquals(Leader.NEWLEADER, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(1, l.self.getAcceptedEpoch()); assertCurrentEpochGotUpdated(1, l.self, ClientBase.CONNECTION_TIMEOUT); qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.UPTODATE, qp.getType()); long zxid = l.zk.getZxid(); l.propose(new Request(1, 1, ZooDefs.OpCode.create, new TxnHeader(1, 1, zxid, 1, ZooDefs.OpCode.create), new CreateTxn("/test", "hola".getBytes(), null, true, 0), zxid)); readPacketSkippingPing(ia, qp); assertEquals(Leader.PROPOSAL, qp.getType()); LOG.info("Proposal sent."); for (int i = 0; i < (2 * ZabUtils.SYNC_LIMIT) + 2; i++) { try { ia.readRecord(qp, null); LOG.info("Ping received: {}", i); qp = new QuorumPacket(Leader.PING, qp.getZxid(), "".getBytes(), null); oa.writeRecord(qp, null); } catch (EOFException e) { return; } } fail("Connection hasn't been closed by leader after transaction times out."); } }); } private void deserializeSnapshot(InputArchive ia) throws IOException { ZKDatabase zkdb = new ZKDatabase(null); zkdb.deserializeSnapshot(ia); String signature = ia.readString("signature"); assertEquals("BenWasHere", signature); } @Test public void testNormalObserverRun() throws Exception { testObserverConversation(new ObserverConversation() { @Override public void converseWithObserver(InputArchive ia, OutputArchive oa, Observer o) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = o.zk.getTxnLogFactory().getDataLogDir().getParentFile(); File snapDir = o.zk.getTxnLogFactory().getSnapDir().getParentFile(); try { assertEquals(0, o.self.getAcceptedEpoch()); assertEquals(0, o.self.getCurrentEpoch()); // Setup a database with a single /foo node ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); final long foo1Zxid = ZxidUtils.makeZxid(1, 1); final long foo2Zxid = ZxidUtils.makeZxid(1, 2); zkDb.processTxn(new TxnHeader(13, 1313, foo1Zxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo1", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); zkDb.processTxn(new TxnHeader(13, 1313, foo2Zxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo2", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1), null); Stat stat = new Stat(); assertEquals("data1", new String(zkDb.getData("/foo1", stat, null))); assertEquals("data1", new String(zkDb.getData("/foo2", stat, null))); QuorumPacket qp = new QuorumPacket(); readPacketSkippingPing(ia, qp); assertEquals(Leader.OBSERVERINFO, qp.getType()); assertEquals(qp.getZxid(), 0); LearnerInfo learnInfo = new LearnerInfo(); ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo); assertEquals(learnInfo.getProtocolVersion(), 0x10000); assertEquals(learnInfo.getServerid(), 0); // We are simulating an established leader, so the epoch is 1 qp.setType(Leader.LEADERINFO); qp.setZxid(ZxidUtils.makeZxid(1, 0)); byte[] protoBytes = new byte[4]; ByteBuffer.wrap(protoBytes).putInt(0x10000); qp.setData(protoBytes); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.ACKEPOCH, qp.getType()); assertEquals(0, qp.getZxid()); assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer.wrap(qp.getData()).getInt()); assertEquals(1, o.self.getAcceptedEpoch()); assertEquals(0, o.self.getCurrentEpoch()); // Send the snapshot we created earlier qp.setType(Leader.SNAP); qp.setData(new byte[0]); qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); oa.writeRecord(qp, null); zkDb.serializeSnapshot(oa); oa.writeString("BenWasHere", null); qp.setType(Leader.NEWLEADER); qp.setZxid(ZxidUtils.makeZxid(1, 0)); oa.writeRecord(qp, null); // Get the ack of the new leader readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(1, o.self.getAcceptedEpoch()); assertEquals(1, o.self.getCurrentEpoch()); assertEquals(foo2Zxid, o.zk.getLastProcessedZxid()); // Make sure the data was recorded in the filesystem ok ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); long lastZxid = zkDb2.loadDataBase(); assertEquals("data1", new String(zkDb2.getData("/foo1", stat, null))); assertEquals(foo2Zxid, lastZxid); // Register watch TrackerWatcher watcher = new TrackerWatcher(); assertEquals("data1", new String(o.zk.getZKDatabase().getData("/foo2", stat, watcher))); // Propose /foo1 update long proposalZxid = ZxidUtils.makeZxid(1, 1000); proposeSetData(qp, "/foo1", proposalZxid, "data2", 2); oa.writeRecord(qp, null); // Commit /foo1 update qp.setType(Leader.COMMIT); qp.setZxid(proposalZxid); oa.writeRecord(qp, null); // Inform /foo2 update long informZxid = ZxidUtils.makeZxid(1, 1001); proposeSetData(qp, "/foo2", informZxid, "data2", 2); qp.setType(Leader.INFORM); oa.writeRecord(qp, null); qp.setType(Leader.UPTODATE); qp.setZxid(0); oa.writeRecord(qp, null); // Read the uptodate ack readPacketSkippingPing(ia, qp); assertEquals(Leader.ACK, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); // Data should get updated watcher.waitForChange(); assertEquals("data2", new String(o.zk.getZKDatabase().getData("/foo1", stat, null))); assertEquals("data2", new String(o.zk.getZKDatabase().getData("/foo2", stat, null))); // Shutdown sequence guarantee that all pending requests // in sync request processor get flush to disk o.zk.shutdown(); zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); lastZxid = zkDb2.loadDataBase(); assertEquals("data2", new String(zkDb2.getData("/foo1", stat, null))); assertEquals("data2", new String(zkDb2.getData("/foo2", stat, null))); assertEquals(informZxid, lastZxid); } finally { TestUtils.deleteFileRecursively(tmpDir); } } private void proposeSetData(QuorumPacket qp, String path, long zxid, String data, int version) throws IOException { qp.setType(Leader.PROPOSAL); qp.setZxid(zxid); TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.setData); SetDataTxn sdt = new SetDataTxn(path, data.getBytes(), version); ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeRecord(hdr, null); boa.writeRecord(sdt, null); qp.setData(baos.toByteArray()); } }); } @Test public void testLeaderBehind() throws Exception { testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException { /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000, 0); byte[] liBytes = new byte[20]; ByteBufferOutputStream.record2ByteBuffer(li, ByteBuffer.wrap(liBytes)); /* we are going to say we last acked epoch 20 */ QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, ZxidUtils.makeZxid(20, 0), liBytes, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.LEADERINFO, qp.getType()); assertEquals(ZxidUtils.makeZxid(21, 0), qp.getZxid()); assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), 0x10000); qp = new QuorumPacket(Leader.ACKEPOCH, 0, new byte[4], null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.DIFF, qp.getType()); readPacketSkippingPing(ia, qp); assertEquals(Leader.NEWLEADER, qp.getType()); assertEquals(ZxidUtils.makeZxid(21, 0), qp.getZxid()); qp = new QuorumPacket(Leader.ACK, qp.getZxid(), null, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.UPTODATE, qp.getType()); } }); } /** * Tests that when a quorum of followers send LearnerInfo but do not ack the epoch (which is sent * by the leader upon receipt of LearnerInfo from a quorum), the leader does not start using this epoch * as it would in the normal case (when a quorum do ack the epoch). This tests ZK-1192 * @throws Exception */ @Test public void testAbandonBeforeACKEpoch() throws Exception { testLeaderConversation(new LeaderConversation() { public void converseWithLeader(InputArchive ia, OutputArchive oa, Leader l) throws IOException, InterruptedException { /* we test a normal run. everything should work out well. */ LearnerInfo li = new LearnerInfo(1, 0x10000, 0); byte[] liBytes = new byte[20]; ByteBufferOutputStream.record2ByteBuffer(li, ByteBuffer.wrap(liBytes)); QuorumPacket qp = new QuorumPacket(Leader.FOLLOWERINFO, 0, liBytes, null); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); assertEquals(Leader.LEADERINFO, qp.getType()); assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); assertEquals(ByteBuffer.wrap(qp.getData()).getInt(), 0x10000); Thread.sleep(l.self.getInitLimit() * l.self.getTickTime() + 5000); // The leader didn't get a quorum of acks - make sure that leader's current epoch is not advanced assertEquals(0, l.self.getCurrentEpoch()); } }); } static class ConversableFollower extends Follower { ConversableFollower(QuorumPeer self, FollowerZooKeeperServer zk) { super(self, zk); } QuorumServer leaderQuorumServer; public void setLeaderQuorumServer(QuorumServer quorumServer) { leaderQuorumServer = quorumServer; } @Override protected QuorumServer findLeader() { return leaderQuorumServer; } } private ConversableFollower createFollower(File tmpDir, QuorumPeer peer) throws IOException { FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); peer.setTxnFactory(logFactory); ZKDatabase zkDb = new ZKDatabase(logFactory); FollowerZooKeeperServer zk = new FollowerZooKeeperServer(logFactory, peer, zkDb); peer.setZKDatabase(zkDb); return new ConversableFollower(peer, zk); } static class ConversableObserver extends Observer { ConversableObserver(QuorumPeer self, ObserverZooKeeperServer zk) { super(self, zk); } QuorumServer leaderQuorumServer; public void setLeaderQuorumServer(QuorumServer quorumServer) { leaderQuorumServer = quorumServer; } @Override protected QuorumServer findLeader() { return leaderQuorumServer; } } private ConversableObserver createObserver(File tmpDir, QuorumPeer peer) throws IOException { FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); peer.setTxnFactory(logFactory); ZKDatabase zkDb = new ZKDatabase(logFactory); ObserverZooKeeperServer zk = new ObserverZooKeeperServer(logFactory, peer, zkDb); peer.setZKDatabase(zkDb); return new ConversableObserver(peer, zk); } private String readContentsOfFile(File f) throws IOException { return new BufferedReader(new FileReader(f)).readLine(); } @Test public void testInitialAcceptedCurrent() throws Exception { File tmpDir = File.createTempFile("test", ".dir", testData); tmpDir.delete(); tmpDir.mkdir(); try { FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); File version2 = new File(tmpDir, "version-2"); version2.mkdir(); logFactory.save(new DataTree(), new ConcurrentHashMap<>(), false); long zxid = ZxidUtils.makeZxid(3, 3); logFactory.append(new Request(1, 1, ZooDefs.OpCode.error, new TxnHeader(1, 1, zxid, 1, ZooDefs.OpCode.error), new ErrorTxn(1), zxid)); logFactory.commit(); ZKDatabase zkDb = new ZKDatabase(logFactory); QuorumPeer peer = QuorumPeer.testingQuorumPeer(); peer.setZKDatabase(zkDb); peer.setTxnFactory(logFactory); peer.getLastLoggedZxid(); assertEquals(3, peer.getAcceptedEpoch()); assertEquals(3, peer.getCurrentEpoch()); assertEquals(3, Integer.parseInt(readContentsOfFile(new File(version2, QuorumPeer.CURRENT_EPOCH_FILENAME)))); assertEquals(3, Integer.parseInt(readContentsOfFile(new File(version2, QuorumPeer.ACCEPTED_EPOCH_FILENAME)))); } finally { TestUtils.deleteFileRecursively(tmpDir); } } /* * Epoch is first written to file then updated in memory. Give some time to * write the epoch in file and then go for assert. */ private void assertCurrentEpochGotUpdated(int expected, QuorumPeer self, long timeout) throws IOException { long elapsedTime = 0; long waitInterval = 10; while (self.getCurrentEpoch() != expected && elapsedTime < timeout) { try { Thread.sleep(waitInterval); } catch (InterruptedException e) { fail("CurrentEpoch update failed"); } elapsedTime = elapsedTime + waitInterval; } assertEquals(expected, self.getCurrentEpoch(), "CurrentEpoch update failed"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000160 15051152474 032747 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ZabUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/ZabUtils.ja0100644 0000000 0000000 00000014200 15051152474 034231 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.test.ClientBase; public class ZabUtils { private ZabUtils() { } public static final int SYNC_LIMIT = 2; public static QuorumPeer createQuorumPeer(File tmpDir) throws IOException { HashMap peers = new HashMap<>(); QuorumPeer peer = QuorumPeer.testingQuorumPeer(); peer.syncLimit = SYNC_LIMIT; peer.initLimit = 2; peer.tickTime = 2000; peers.put(0L, new QuorumPeer.QuorumServer(0, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); peers.put(1L, new QuorumPeer.QuorumServer(1, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); peers.put(2L, new QuorumPeer.QuorumServer(2, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); peer.setQuorumVerifier(new QuorumMaj(peers), false); peer.setCnxnFactory(new NullServerCnxnFactory()); File version2 = new File(tmpDir, "version-2"); version2.mkdir(); ClientBase.createInitializeFile(tmpDir); FileOutputStream fos = new FileOutputStream(new File(version2, "currentEpoch")); fos.write("0\n".getBytes()); fos.close(); fos = new FileOutputStream(new File(version2, "acceptedEpoch")); fos.write("0\n".getBytes()); fos.close(); return peer; } public static Leader createLeader(File tmpDir, QuorumPeer peer) throws IOException, NoSuchFieldException, IllegalAccessException, X509Exception { LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer); return new Leader(peer, zk); } public static Leader createMockLeader(File tmpDir, QuorumPeer peer) throws IOException, NoSuchFieldException, IllegalAccessException, X509Exception { LeaderZooKeeperServer zk = prepareLeader(tmpDir, peer); return new MockLeader(peer, zk); } private static LeaderZooKeeperServer prepareLeader(File tmpDir, QuorumPeer peer) throws IOException, NoSuchFieldException, IllegalAccessException { FileTxnSnapLog logFactory = new FileTxnSnapLog(tmpDir, tmpDir); peer.setTxnFactory(logFactory); ZKDatabase zkDb = new ZKDatabase(logFactory); LeaderZooKeeperServer zk = new LeaderZooKeeperServer(logFactory, peer, zkDb); return zk; } private static final class NullServerCnxnFactory extends ServerCnxnFactory { public void startup(ZooKeeperServer zkServer, boolean startServer) throws IOException, InterruptedException { } public void start() { } public void shutdown() { } public void setMaxClientCnxnsPerHost(int max) { } public void join() throws InterruptedException { } public int getMaxClientCnxnsPerHost() { return 0; } public int getSocketListenBacklog() { return -1; } public int getLocalPort() { return 0; } public InetSocketAddress getLocalAddress() { return null; } public Iterable getConnections() { return null; } public void configure(InetSocketAddress addr, int maxcc, int listenBacklog, boolean secure) throws IOException { } @Override public boolean closeSession(long sessionId, ServerCnxn.DisconnectReason reason) { return false; } @Override public void closeAll(ServerCnxn.DisconnectReason reason) { } @Override public int getNumAliveConnections() { return 0; } @Override public void reconfigure(InetSocketAddress addr) { } @Override public void resetAllConnectionStats() { } @Override public Iterable> getAllConnectionInfo(boolean brief) { return null; } } public static final class MockLeader extends Leader { MockLeader(QuorumPeer qp, LeaderZooKeeperServer zk) throws IOException, X509Exception { super(qp, zk); } /** * This method returns the value of the variable that holds the epoch * to be proposed and that has been proposed, depending on the point * of the execution in which it is called. * * @return epoch */ public long getCurrentEpochToPropose() { return epoch; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000203 15051152474 032745 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/DigestSecurityTestcase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Digest0100644 0000000 0000000 00000003147 15051152474 034273 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import org.apache.zookeeper.common.X509Util; import org.apache.zookeeper.test.SaslAuthDigestTestBase; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; /** * Created for test cases which use Digest Auth mech for SASL. * Primary reason is that we have to disable FIPS mode, otherwise DIGEST-MD5 cannot be used. * * @see SaslAuthDigestTestBase */ public class DigestSecurityTestcase extends QuorumAuthTestBase { @BeforeAll public static void setUpClass() throws Exception { // Need to disable Fips-mode, because we use DIGEST-MD5 mech for Sasl System.setProperty(X509Util.FIPS_MODE_PROPERTY, "false"); } @AfterAll public static void tearDownClass() throws Exception { System.clearProperty(X509Util.FIPS_MODE_PROPERTY); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000205 15051152474 032747 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/KerberosSecurityTestcase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Kerber0100644 0000000 0000000 00000007320 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.util.Properties; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; /* * This code is originally from HDFS, see the similarly named file there * in case of bug fixing, history, etc. * * Branch : trunk * Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33 */ /** * KerberosSecurityTestcase provides a base class for using MiniKdc with other * test cases. KerberosSecurityTestcase starts the MiniKdc (@Before) before * running tests, and stop the MiniKdc (@After) after the testcases, using * default settings (working dir and kdc configurations). *

    * Users can directly inherit this class and implement their own test functions * using the default settings, or override functions getTestDir() and * createMiniKdcConf() to provide new settings. */ public class KerberosSecurityTestcase extends QuorumAuthTestBase { private static MiniKdc kdc; private static File workDir; private static Properties conf; @BeforeAll public static void setUpSasl() throws Exception { startMiniKdc(); } @AfterAll public static void tearDownSasl() throws Exception { stopMiniKdc(); FileUtils.deleteQuietly(workDir); } public static void startMiniKdc() throws Exception { createTestDir(); createMiniKdcConf(); kdc = new MiniKdc(conf, workDir); kdc.start(); } /** * Create a working directory, it should be the build directory. Under this * directory an ApacheDS working directory will be created, this directory * will be deleted when the MiniKdc stops. * * @throws IOException */ public static void createTestDir() throws IOException { workDir = createTmpDir(new File(System.getProperty("build.test.dir", "build"))); } static File createTmpDir(File parentDir) throws IOException { File tmpFile = File.createTempFile("test", ".junit", parentDir); // don't delete tmpFile - this ensures we don't attempt to create // a tmpDir with a duplicate name File tmpDir = new File(tmpFile + ".dir"); // never true if tmpfile does it's job assertFalse(tmpDir.exists()); assertTrue(tmpDir.mkdirs()); return tmpDir; } /** * Create a Kdc configuration */ public static void createMiniKdcConf() { conf = MiniKdc.createConf(); } public static void stopMiniKdc() { if (kdc != null) { kdc.stop(); } } public static MiniKdc getKdc() { return kdc; } public static File getWorkDir() { return workDir; } public static Properties getConf() { return conf; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000176 15051152474 032756 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/KerberosTestUtils.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Kerber0100644 0000000 0000000 00000012107 15051152474 034262 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.Configuration; import org.apache.zookeeper.util.SecurityUtils; public class KerberosTestUtils { private static final boolean IBM_JAVA = System.getProperty("java.vendor").contains("IBM"); private static String keytabFile = new File(System.getProperty("build.test.dir", "build"), UUID.randomUUID().toString()).getAbsolutePath(); public static String getRealm() { return "EXAMPLE.COM"; } public static String getLearnerPrincipal() { return "learner@EXAMPLE.COM"; } public static String getServerPrincipal() { return "zkquorum/localhost@EXAMPLE.COM"; } public static String getClientPrincipal() { return getClientUsername() + "/localhost@EXAMPLE.COM"; } public static String getClientUsername() { return "zkclient"; } public static String getHostLearnerPrincipal() { return "learner/_HOST@EXAMPLE.COM"; } public static String getHostServerPrincipal() { return "zkquorum/_HOST@EXAMPLE.COM"; } public static String getHostNamedLearnerPrincipal(String myHostname) { return "learner/" + myHostname + "@EXAMPLE.COM"; } public static String getKeytabFile() { return keytabFile; } public static String replaceHostPattern(String principal) { String[] components = principal.split("[/@]"); if (components == null || components.length < 2 || !components[1].equals(SecurityUtils.QUORUM_HOSTNAME_PATTERN)) { return principal; } else { return replacePattern(components, "localhost"); } } public static String replacePattern(String[] components, String hostname) { if (components.length == 3) { return components[0] + "/" + hostname.toLowerCase() + "@" + components[2]; } else { return components[0] + "/" + hostname.toLowerCase(); } } public static class KerberosConfiguration extends Configuration { private String principal; private String keytab; private boolean isInitiator; private KerberosConfiguration(String principal, File keytab, boolean client) { this.principal = principal; this.keytab = keytab.getAbsolutePath(); this.isInitiator = client; } public static Configuration createClientConfig(String principal, File keytab) { return new KerberosConfiguration(principal, keytab, true); } public static Configuration createServerConfig(String principal, File keytab) { return new KerberosConfiguration(principal, keytab, false); } private static String getKrb5LoginModuleName() { return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.auth.module.Krb5LoginModule" : "com.sun.security.auth.module.Krb5LoginModule"; } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { Map options = new HashMap<>(); options.put("principal", principal); options.put("refreshKrb5Config", "true"); if (IBM_JAVA) { options.put("useKeytab", keytab); options.put("credsType", "both"); } else { options.put("keyTab", keytab); options.put("useKeyTab", "true"); options.put("storeKey", "true"); options.put("doNotPrompt", "true"); options.put("useTicketCache", "true"); options.put("renewTGT", "true"); options.put("isInitiator", Boolean.toString(isInitiator)); } String ticketCache = System.getenv("KRB5CCNAME"); if (ticketCache != null) { options.put("ticketCache", ticketCache); } options.put("debug", "true"); return new AppConfigurationEntry[]{new AppConfigurationEntry(getKrb5LoginModuleName(), AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options)}; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000164 15051152474 032753 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/MiniKdc.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/MiniKd0100644 0000000 0000000 00000036743 15051152474 034237 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.kerby.kerberos.kerb.KrbException; import org.apache.kerby.kerberos.kerb.server.KdcConfigKey; import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; import org.apache.kerby.util.IOUtil; import org.apache.kerby.util.NetworkUtil; import org.apache.zookeeper.server.ExitCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Mini KDC based on Apache Directory Server that can be embedded in testcases * or used from command line as a standalone KDC. *

    * From within testcases: *

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

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

    * MiniKdc default configuration values are: *

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

    * The returned configuration is a copy, it can be customized before using * it to create a MiniKdc. * @return a MiniKdc default configuration. */ public static Properties createConf() { return (Properties) DEFAULT_CONFIG.clone(); } private Properties conf; private SimpleKdcServer simpleKdc; private int port; private String realm; private File workDir; private File krb5conf; private String transport; private boolean krb5Debug; public void setTransport(String transport) { this.transport = transport; } /** * Creates a MiniKdc. * * @param conf MiniKdc configuration. * @param workDir working directory, it should be the build directory. Under * this directory an ApacheDS working directory will be created, this * directory will be deleted when the MiniKdc stops. * @throws Exception thrown if the MiniKdc could not be created. */ public MiniKdc(Properties conf, File workDir) throws Exception { if (!conf.keySet().containsAll(PROPERTIES)) { Set missingProperties = new HashSet<>(PROPERTIES); missingProperties.removeAll(conf.keySet()); throw new IllegalArgumentException("Missing configuration properties: " + missingProperties); } this.workDir = new File(workDir, Long.toString(System.currentTimeMillis())); if (!this.workDir.exists() && !this.workDir.mkdirs()) { throw new RuntimeException("Cannot create directory " + this.workDir); } LOG.info("Configuration:"); LOG.info("---------------------------------------------------------------"); for (Map.Entry entry : conf.entrySet()) { LOG.info(" {}: {}", entry.getKey(), entry.getValue()); } LOG.info("---------------------------------------------------------------"); this.conf = conf; port = Integer.parseInt(conf.getProperty(KDC_PORT)); String orgName = conf.getProperty(ORG_NAME); String orgDomain = conf.getProperty(ORG_DOMAIN); realm = orgName.toUpperCase(Locale.ENGLISH) + "." + orgDomain.toUpperCase(Locale.ENGLISH); } /** * Returns the port of the MiniKdc. * * @return the port of the MiniKdc. */ public int getPort() { return port; } /** * Returns the host of the MiniKdc. * * @return the host of the MiniKdc. */ public String getHost() { return conf.getProperty(KDC_BIND_ADDRESS); } /** * Returns the realm of the MiniKdc. * * @return the realm of the MiniKdc. */ public String getRealm() { return realm; } public File getKrb5conf() { krb5conf = new File(System.getProperty(JAVA_SECURITY_KRB5_CONF)); return krb5conf; } /** * Starts the MiniKdc. * * @throws Exception thrown if the MiniKdc could not be started. */ public synchronized void start() throws Exception { if (simpleKdc != null) { throw new RuntimeException("Already started"); } simpleKdc = new SimpleKdcServer(); prepareKdcServer(); simpleKdc.init(); resetDefaultRealm(); simpleKdc.start(); LOG.info("MiniKdc stated."); } private void resetDefaultRealm() throws IOException { InputStream templateResource = new FileInputStream(getKrb5conf().getAbsolutePath()); String content = IOUtil.readInput(templateResource); content = content.replaceAll("default_realm = .*\n", "default_realm = " + getRealm() + "\n"); IOUtil.writeFile(content, getKrb5conf()); } private void prepareKdcServer() throws Exception { // transport simpleKdc.setWorkDir(workDir); simpleKdc.setKdcHost(getHost()); simpleKdc.setKdcRealm(realm); if (transport == null) { transport = conf.getProperty(TRANSPORT); } if (port == 0) { port = NetworkUtil.getServerPort(); } if (transport != null) { if (transport.trim().equals("TCP")) { simpleKdc.setKdcTcpPort(port); simpleKdc.setAllowUdp(false); } else if (transport.trim().equals("UDP")) { simpleKdc.setKdcUdpPort(port); simpleKdc.setAllowTcp(false); } else { throw new IllegalArgumentException("Invalid transport: " + transport); } } else { throw new IllegalArgumentException("Need to set transport!"); } simpleKdc.getKdcConfig().setString(KdcConfigKey.KDC_SERVICE_NAME, conf.getProperty(INSTANCE)); long minTicketLifetimeConf = Long.parseLong(conf.getProperty(MIN_TICKET_LIFETIME)) / 1000; simpleKdc.getKdcConfig().setLong(KdcConfigKey.MINIMUM_TICKET_LIFETIME, minTicketLifetimeConf); long maxTicketLifetimeConf = Long.parseLong(conf.getProperty(MAX_TICKET_LIFETIME)) / 1000; simpleKdc.getKdcConfig().setLong(KdcConfigKey.MAXIMUM_TICKET_LIFETIME, maxTicketLifetimeConf); if (conf.getProperty(DEBUG) != null) { krb5Debug = getAndSet(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG)); } } /** * Stops the MiniKdc */ public synchronized void stop() { if (simpleKdc != null) { try { simpleKdc.stop(); } catch (KrbException e) { e.printStackTrace(); } finally { if (conf.getProperty(DEBUG) != null) { System.setProperty(SUN_SECURITY_KRB5_DEBUG, Boolean.toString(krb5Debug)); } } } delete(workDir); try { // Will be fixed in next Kerby version. Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } LOG.info("MiniKdc stopped."); } private void delete(File f) { if (f.isFile()) { if (!f.delete()) { LOG.warn("WARNING: cannot delete file {}", f.getAbsolutePath()); } } else { for (File c : f.listFiles()) { delete(c); } if (!f.delete()) { LOG.warn("WARNING: cannot delete directory {}", f.getAbsolutePath()); } } } /** * Creates a principal in the KDC with the specified user and password. * * @param principal principal name, do not include the domain. * @param password password. * @throws Exception thrown if the principal could not be created. */ public synchronized void createPrincipal(String principal, String password) throws Exception { simpleKdc.createPrincipal(principal, password); } /** * Creates multiple principals in the KDC and adds them to a keytab file. * * @param keytabFile keytab file to add the created principals. * @param principals principals to add to the KDC, do not include the domain. * @throws Exception thrown if the principals or the keytab file could not be * created. */ public synchronized void createPrincipal(File keytabFile, String... principals) throws Exception { simpleKdc.createPrincipals(principals); if (keytabFile.exists() && !keytabFile.delete()) { LOG.error("Failed to delete keytab file: {}", keytabFile); } for (String principal : principals) { simpleKdc.getKadmin().exportKeytab(keytabFile, principal); } } /** * Set the System property; return the old value for caching. * * @param sysprop property * @param debug true or false * @return the previous value */ private boolean getAndSet(String sysprop, String debug) { boolean old = Boolean.getBoolean(sysprop); System.setProperty(sysprop, debug); return old; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000170 15051152474 032750 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/MiniKdcTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/MiniKd0100644 0000000 0000000 00000011113 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import java.io.File; import java.security.Principal; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.LoginContext; import org.apache.kerby.kerberos.kerb.keytab.Keytab; import org.apache.kerby.kerberos.kerb.type.base.PrincipalName; import org.apache.zookeeper.server.quorum.auth.KerberosTestUtils.KerberosConfiguration; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /* * This code is originally from HDFS, see the file name TestMiniKdc there * in case of bug fixing, history, etc. * * Branch : trunk * Github Revision: 916140604ffef59466ba30832478311d3e6249bd */ public class MiniKdcTest extends KerberosSecurityTestcase { @Test @Timeout(value = 60) public void testMiniKdcStart() { MiniKdc kdc = getKdc(); assertNotSame(0, kdc.getPort()); } @Test @Timeout(value = 60) public void testKeytabGen() throws Exception { MiniKdc kdc = getKdc(); File workDir = getWorkDir(); kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo"); List principalNameList = Keytab.loadKeytab(new File(workDir, "keytab")).getPrincipals(); Set principals = new HashSet<>(); for (PrincipalName principalName : principalNameList) { principals.add(principalName.getName()); } assertEquals(new HashSet<>(Arrays.asList("foo/bar@" + kdc.getRealm(), "bar/foo@" + kdc.getRealm())), principals); } @Test @Timeout(value = 60) public void testKerberosLogin() throws Exception { MiniKdc kdc = getKdc(); File workDir = getWorkDir(); LoginContext loginContext = null; try { String principal = "foo"; File keytab = new File(workDir, "foo.keytab"); kdc.createPrincipal(keytab, principal); Set principals = new HashSet<>(); principals.add(new KerberosPrincipal(principal)); // client login Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); loginContext = new LoginContext("", subject, null, KerberosConfiguration.createClientConfig(principal, keytab)); loginContext.login(); subject = loginContext.getSubject(); assertEquals(1, subject.getPrincipals().size()); assertEquals(KerberosPrincipal.class, subject.getPrincipals().iterator().next().getClass()); assertEquals(principal + "@" + kdc.getRealm(), subject.getPrincipals().iterator().next().getName()); loginContext.logout(); // server login subject = new Subject(false, principals, new HashSet(), new HashSet()); loginContext = new LoginContext("", subject, null, KerberosConfiguration.createServerConfig(principal, keytab)); loginContext.login(); subject = loginContext.getSubject(); assertEquals(1, subject.getPrincipals().size()); assertEquals(KerberosPrincipal.class, subject.getPrincipals().iterator().next().getClass()); assertEquals(principal + "@" + kdc.getRealm(), subject.getPrincipals().iterator().next().getName()); loginContext.logout(); } finally { if (loginContext != null && loginContext.getSubject() != null && !loginContext.getSubject().getPrincipals().isEmpty()) { loginContext.logout(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000203 15051152474 032745 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/QuorumAuthObserverTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000006631 15051152474 034345 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; public class QuorumAuthObserverTest extends DigestSecurityTestcase { static { String jaasEntries = "QuorumServer {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_test=\"mypassword\";\n" + "};\n" + "QuorumLearner {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"test\"\n" + " password=\"mypassword\";\n" + "};\n"; setupJaasConfig(jaasEntries); } @AfterEach @Override public void tearDown() throws Exception { shutdownAll(); super.tearDown(); } @AfterAll public static void cleanup() { cleanupJaasConfig(); } /** * Test to ensure observer with small myid can join SASL quorum. * peer0 myid:11 participant * peer1 myid:21 participant * peer2 myid:1 observer */ @Test public void testSmallObserverJoinSASLQuorum() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); // create quorum StringBuilder connectStringBuilder = new StringBuilder(); int[] myidList = {11, 21, 1}; String[] roleList = {"participant", "participant", "observer"}; int[] clientPorts = startQuorum(3, connectStringBuilder, authConfigs, 3, false, myidList, roleList); // observer with small myid should have joined the quorum String connectStr = "127.0.0.1:" + clientPorts[2]; CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000177 15051152474 032757 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/QuorumAuthTestBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000017241 15051152474 034344 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.security.auth.login.Configuration; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.test.ClientBase; import org.junit.jupiter.api.AfterEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * QuorumAuthTestBase provides a base class for testing quorum peer mutual * authentication using SASL mechanisms. */ public class QuorumAuthTestBase extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(QuorumAuthTestBase.class); protected List mt = new ArrayList<>(); protected static File jaasConfigDir; public static void setupJaasConfig(String jaasEntries) { try { jaasConfigDir = ClientBase.createTmpDir(); File saslConfFile = new File(jaasConfigDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write(jaasEntries); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException ioe) { LOG.error("Failed to create tmp directory to hold JAAS conf file", ioe); // could not create tmp directory to hold JAAS conf file : test will // fail now. } // refresh the SASL configuration in this JVM (making sure that we use the latest config // even if other tests already have been executed and initialized the SASL configs before) Configuration.getConfiguration().refresh(); } public static void cleanupJaasConfig() { if (jaasConfigDir != null) { FileUtils.deleteQuietly(jaasConfigDir); } } @AfterEach public void tearDown() throws Exception { System.clearProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED); } protected String startQuorum(final int serverCount, Map authConfigs, int authServerCount) throws IOException { return this.startQuorum(serverCount, authConfigs, authServerCount, false); } protected String startMultiAddressQuorum(final int serverCount, Map authConfigs, int authServerCount) throws IOException { System.setProperty(QuorumPeer.CONFIG_KEY_MULTI_ADDRESS_ENABLED, "true"); return this.startQuorum(serverCount, authConfigs, authServerCount, true); } protected String startQuorum( final int serverCount, Map authConfigs, int authServerCount, boolean multiAddress) throws IOException { StringBuilder connectStr = new StringBuilder(); final int[] clientPorts = startQuorum(serverCount, connectStr, authConfigs, authServerCount, multiAddress); for (int i = 0; i < serverCount; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], ClientBase.CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } return connectStr.toString(); } protected int[] startQuorum(final int serverCount, StringBuilder connectStr, Map authConfigs, int authServerCount, boolean multiAddress) throws IOException { int[] defaultMyidList = new int[serverCount]; String[] defaultRoleList = new String[serverCount]; for (int i = 0; i < serverCount; i++) { defaultMyidList[i] = i; defaultRoleList[i] = "participant"; } return startQuorum(serverCount, connectStr, authConfigs, authServerCount, multiAddress, defaultMyidList, defaultRoleList); } protected int[] startQuorum(final int serverCount, StringBuilder connectStr, Map authConfigs, int authServerCount, boolean multiAddress, int[] myidList, String[] roleList) throws IOException { final int[] clientPorts = new int[serverCount]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < serverCount; i++) { clientPorts[i] = PortAssignment.unique(); String server = String.format("server.%d=localhost:%d:%d", myidList[i], PortAssignment.unique(), PortAssignment.unique()); if (multiAddress) { server = server + String.format("|localhost:%d:%d", PortAssignment.unique(), PortAssignment.unique()); } sb.append(server + ":" + roleList[i] + "\n"); connectStr.append("127.0.0.1:" + clientPorts[i]); if (i < serverCount - 1) { connectStr.append(","); } } String quorumCfg = sb.toString(); // servers with authentication interfaces configured int i = 0; for (; i < authServerCount; i++) { startServer(authConfigs, clientPorts, quorumCfg, i, myidList); } // servers without any authentication configured for (int j = 0; j < serverCount - authServerCount; j++, i++) { MainThread mthread = new MainThread(myidList[i], clientPorts[i], quorumCfg); mt.add(mthread); mthread.start(); } return clientPorts; } private void startServer( Map authConfigs, final int[] clientPorts, String quorumCfg, int i, int[] myidList) throws IOException { MainThread mthread = new MainThread(myidList[i], clientPorts[i], quorumCfg, authConfigs); mt.add(mthread); mthread.start(); } protected void startServer(MainThread restartPeer, Map authConfigs) throws IOException { MainThread mthread = new MainThread(restartPeer.getMyid(), restartPeer.getClientPort(), restartPeer.getQuorumCfgSection(), authConfigs); mt.add(mthread); mthread.start(); } protected void startServer( Map authConfigs, final int clientPort, String quorumCfg, int myid) throws IOException { MainThread mthread = new MainThread(myid, clientPort, quorumCfg, authConfigs); mt.add(mthread); mthread.start(); } void shutdownAll() { for (int i = 0; i < mt.size(); i++) { shutdown(i); } } MainThread shutdown(int index) { MainThread mainThread = mt.get(index); try { mainThread.shutdown(); } catch (InterruptedException e) { // no op } finally { mt.remove(index); } mainThread.deleteBaseDir(); return mainThread; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000202 15051152474 032744 xustar000000000 0000000 130 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/QuorumAuthUpgradeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000026705 15051152474 034351 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.apache.zookeeper.test.ClientTest; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** * Rolling upgrade should do in three steps: * * step-1) Stop the server and set the flags and restart the server. * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false and quorum.auth.serverRequireSasl=false * Ensure that all the servers should complete this step. Now, move to next step. * * step-2) Stop the server one by one and change the flags and restart the server. * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=false * Ensure that all the servers should complete this step. Now, move to next step. * * step-3) Stop the server one by one and change the flags and restart the server. * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=true * Now, all the servers are fully upgraded and running in secured mode. */ public class QuorumAuthUpgradeTest extends DigestSecurityTestcase { static { String jaasEntries = "QuorumServer {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_test=\"mypassword\";\n" + "};\n" + "QuorumLearner {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"test\"\n" + " password=\"mypassword\";\n" + "};\n"; setupJaasConfig(jaasEntries); } @AfterEach @Override public void tearDown() throws Exception { shutdownAll(); super.tearDown(); } @AfterAll public static void cleanup() { cleanupJaasConfig(); } /** * Test to verify that servers are able to start without any authentication. * peer0 -> quorum.auth.enableSasl=false * peer1 -> quorum.auth.enableSasl=false */ @Test @Timeout(value = 30) public void testNullAuthLearnerServer() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); String connectStr = startQuorum(2, authConfigs, 0); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); } /** * Test to verify that servers are able to form quorum. * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false * peer1 -> quorum.auth.enableSasl=false, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false */ @Test @Timeout(value = 30) public void testAuthLearnerAgainstNullAuthServer() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); String connectStr = startQuorum(2, authConfigs, 1); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); } /** * Test to verify that servers are able to form quorum. * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false * peer1 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false */ @Test @Timeout(value = 30) public void testAuthLearnerAgainstNoAuthRequiredServer() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); String connectStr = startQuorum(2, authConfigs, 2); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); } /** * Test to verify that servers are able to form quorum. * peer0 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true * peer1 -> quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true */ @Test @Timeout(value = 30) public void testAuthLearnerServer() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); String connectStr = startQuorum(2, authConfigs, 2); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); } /** * Rolling upgrade should do in three steps: * * step-1) Stop the server and set the flags and restart the server. * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=false and quorum.auth.serverRequireSasl=false * Ensure that all the servers should complete this step. Now, move to next step. * * step-2) Stop the server one by one and change the flags and restart the server. * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=false * Ensure that all the servers should complete this step. Now, move to next step. * * step-3) Stop the server one by one and change the flags and restart the server. * quorum.auth.enableSasl=true, quorum.auth.learnerRequireSasl=true and quorum.auth.serverRequireSasl=true * Now, all the servers are fully upgraded and running in secured mode. */ @Test @Timeout(value = 90) public void testRollingUpgrade() throws Exception { // Start peer0,1,2 servers with quorum.auth.enableSasl=false and // quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false // Assume this is an existing cluster. Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); String connectStr = startQuorum(3, authConfigs, 0); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); //1. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and // quorum.auth.learnerRequireSasl=false, quorum.auth.serverRequireSasl=false authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false"); restartServer(authConfigs, 0, zk, watcher); restartServer(authConfigs, 1, zk, watcher); restartServer(authConfigs, 2, zk, watcher); //2. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and // quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=false authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); restartServer(authConfigs, 0, zk, watcher); restartServer(authConfigs, 1, zk, watcher); restartServer(authConfigs, 2, zk, watcher); //3. Upgrade peer0,1,2 with quorum.auth.enableSasl=true and // quorum.auth.learnerRequireSasl=true, quorum.auth.serverRequireSasl=true authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); restartServer(authConfigs, 0, zk, watcher); restartServer(authConfigs, 1, zk, watcher); restartServer(authConfigs, 2, zk, watcher); //4. Restart peer2 with quorum.auth.learnerEnableSasl=false and // quorum.auth.serverRequireSasl=false. It should fail to join the // quorum as this needs auth. authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); MainThread m = shutdown(2); startServer(m, authConfigs); assertFalse(ClientBase.waitForServerUp("127.0.0.1:" + m.getClientPort(), 5000), "waiting for server 2 being up"); } private void restartServer( Map authConfigs, int index, ZooKeeper zk, CountdownWatcher watcher) throws IOException, KeeperException, InterruptedException, TimeoutException { LOG.info("Restarting server myid={}", index); MainThread m = shutdown(index); startServer(m, authConfigs); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + m.getClientPort(), ClientBase.CONNECTION_TIMEOUT), "waiting for server" + index + "being up"); watcher.waitForConnected(ClientTest.CONNECTION_TIMEOUT); zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000201 15051152474 032743 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/QuorumDigestAuthTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000025163 15051152474 034346 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.admin.AdminServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.apache.zookeeper.server.quorum.QuorumPeerMain; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class QuorumDigestAuthTest extends DigestSecurityTestcase { static { String jaasEntries = "QuorumServer {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_test=\"mypassword\";\n" + "};\n" + "QuorumLearner {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"test\"\n" + " password=\"mypassword\";\n" + "};\n" + "QuorumLearnerInvalid {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"test\"\n" + " password=\"invalid\";\n" + "};" + "\n"; setupJaasConfig(jaasEntries); } @AfterEach @Override public void tearDown() throws Exception { for (MainThread mainThread : mt) { mainThread.shutdown(); mainThread.deleteBaseDir(); } super.tearDown(); } @AfterAll public static void cleanup() { cleanupJaasConfig(); } /** * Test to verify that server is able to start with valid credentials */ @Test @Timeout(value = 30) public void testValidCredentials() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } /** * Test to verify that server is able to start with valid credentials * when using multiple Quorum / Election addresses */ @Test @Timeout(value = 30) public void testValidCredentialsWithMultiAddresses() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); String connectStr = startMultiAddressQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } /** * Test to verify that server is able to start with invalid credentials if * the configuration is set to quorum.auth.serverRequireSasl=false. * Quorum will talk each other even if the authentication is not succeeded */ @Test @Timeout(value = 30) public void testSaslNotRequiredWithInvalidCredentials() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid"); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } /** * Test to verify that server shouldn't start with invalid credentials * if the configuration is set to quorum.auth.serverRequireSasl=true, * quorum.auth.learnerRequireSasl=true */ @Test @Timeout(value = 30) public void testSaslRequiredInvalidCredentials() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerInvalid"); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); int serverCount = 2; final int[] clientPorts = startQuorum(serverCount, new StringBuilder(), authConfigs, serverCount, false); for (int i = 0; i < serverCount; i++) { boolean waitForServerUp = ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], QuorumPeerTestBase.TIMEOUT); assertFalse(waitForServerUp, "Shouldn't start server with invalid credentials"); } } /** * If quorumpeer learner is not auth enabled then self won't be able to join * quorum. So this test is ensuring that the quorumpeer learner is also auth * enabled while enabling quorum server require sasl. */ @Test @Timeout(value = 10) public void testEnableQuorumServerRequireSaslWithoutQuorumLearnerRequireSasl() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearner"); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false"); MainThread mthread = new MainThread(1, PortAssignment.unique(), "", authConfigs); String[] args = new String[1]; args[0] = mthread.getConfFile().toString(); try { new QuorumPeerMain() { @Override protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServer.AdminServerException { super.initializeAndRun(args); } }.initializeAndRun(args); fail("Must throw exception as quorumpeer learner is not enabled!"); } catch (ConfigException e) { // expected } } /** * If quorumpeer learner is not auth enabled then self won't be able to join * quorum. So this test is ensuring that the quorumpeer learner is also auth * enabled while enabling quorum server require sasl. */ @Test @Timeout(value = 10) public void testEnableQuorumAuthenticationConfigurations() throws Exception { Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearner"); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "false"); // case-1) 'quorum.auth.enableSasl' is off. Tries to enable server sasl. authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "false"); MainThread mthread = new MainThread(1, PortAssignment.unique(), "", authConfigs); String[] args = new String[1]; args[0] = mthread.getConfFile().toString(); try { new QuorumPeerMain() { @Override protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServer.AdminServerException { super.initializeAndRun(args); } }.initializeAndRun(args); fail("Must throw exception as quorum sasl is not enabled!"); } catch (ConfigException e) { // expected } // case-1) 'quorum.auth.enableSasl' is off. Tries to enable learner sasl. authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "false"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); try { new QuorumPeerMain() { @Override protected void initializeAndRun(String[] args) throws ConfigException, IOException, AdminServer.AdminServerException { super.initializeAndRun(args); } }.initializeAndRun(args); fail("Must throw exception as quorum sasl is not enabled!"); } catch (ConfigException e) { // expected } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000203 15051152474 032745 xustar000000000 0000000 131 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/QuorumKerberosAuthTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000016345 15051152474 034350 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import java.io.File; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class QuorumKerberosAuthTest extends KerberosSecurityTestcase { private static File keytabFile; static { String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true); // note: we use "refreshKrb5Config=true" to refresh the kerberos config in the JVM, // making sure that we use the latest config even if other tests already have been executed // and initialized the kerberos client configs before) String jaasEntries = "" + "QuorumServer {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " useKeyTab=true\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" + " debug=false\n" + " refreshKrb5Config=true\n" + " principal=\"" + KerberosTestUtils.replaceHostPattern(KerberosTestUtils.getHostServerPrincipal()) + "\";\n" + "};\n" + "QuorumLearner {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " useKeyTab=true\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" + " debug=false\n" + " refreshKrb5Config=true\n" + " principal=\"" + KerberosTestUtils.replaceHostPattern(KerberosTestUtils.getHostLearnerPrincipal()) + "\";\n" + "};\n"; setupJaasConfig(jaasEntries); } @BeforeAll public static void setUp() throws Exception { // create keytab keytabFile = new File(KerberosTestUtils.getKeytabFile()); String learnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal(); String serverPrincipal = KerberosTestUtils.getHostServerPrincipal(); learnerPrincipal = KerberosTestUtils.replaceHostPattern(learnerPrincipal.substring(0, learnerPrincipal.lastIndexOf("@"))); serverPrincipal = KerberosTestUtils.replaceHostPattern(serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@"))); getKdc().createPrincipal(keytabFile, learnerPrincipal, serverPrincipal); } @AfterEach @Override public void tearDown() throws Exception { for (MainThread mainThread : mt) { mainThread.shutdown(); mainThread.deleteBaseDir(); } super.tearDown(); } @AfterAll public static void cleanup() { if (keytabFile != null) { FileUtils.deleteQuietly(keytabFile); } cleanupJaasConfig(); } /** * Test to verify that server is able to start with valid credentials */ @Test @Timeout(value = 120) public void testValidCredentials() throws Exception { String serverPrincipal = KerberosTestUtils.getServerPrincipal(); serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@")); Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } /** * Test to verify that server is able to start with valid credentials * when using multiple Quorum / Election addresses */ @Test @Timeout(value = 120) public void testValidCredentialsWithMultiAddresses() throws Exception { String serverPrincipal = KerberosTestUtils.getServerPrincipal(); serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@")); Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); String connectStr = startMultiAddressQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_qu0100644 0000000 0000000 00000000214 15051152474 032747 xustar000000000 0000000 140 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/QuorumKerberosHostBasedAuthTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/quorum/auth/Quorum0100644 0000000 0000000 00000033647 15051152474 034354 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.quorum.auth; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase.MainThread; import org.apache.zookeeper.test.ClientBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class QuorumKerberosHostBasedAuthTest extends KerberosSecurityTestcase { private static File keytabFile; private static String hostServerPrincipal = KerberosTestUtils.getHostServerPrincipal(); private static String hostLearnerPrincipal = KerberosTestUtils.getHostLearnerPrincipal(); private static String hostNamedLearnerPrincipal = KerberosTestUtils.getHostNamedLearnerPrincipal("myHost"); private static String hostlessLearnerPrincipal = KerberosTestUtils.getLearnerPrincipal(); static { setupJaasConfigEntries(hostServerPrincipal, hostLearnerPrincipal, hostNamedLearnerPrincipal, hostlessLearnerPrincipal); } private static void setupJaasConfigEntries( String hostServerPrincipal, String hostLearnerPrincipal, String hostNamedLearnerPrincipal, String hostlessLearnerPrincipal) { String keytabFilePath = FilenameUtils.normalize(KerberosTestUtils.getKeytabFile(), true); // note: we use "refreshKrb5Config=true" to refresh the kerberos config in the JVM, // making sure that we use the latest config even if other tests already have been executed // and initialized the kerberos client configs before) String jaasEntries = "QuorumServer {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " useKeyTab=true\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" + " debug=false\n" + " refreshKrb5Config=true\n" + " principal=\"" + KerberosTestUtils.replaceHostPattern(hostServerPrincipal) + "\";\n" + "};\n" + "QuorumLearner {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " useKeyTab=true\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" + " debug=false\n" + " refreshKrb5Config=true\n" + " principal=\"" + KerberosTestUtils.replaceHostPattern(hostLearnerPrincipal) + "\";\n" + "};\n" + "QuorumLearnerMyHost {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " useKeyTab=true\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" + " debug=false\n" + " refreshKrb5Config=true\n" + " principal=\"" + hostNamedLearnerPrincipal + "\";\n" + "};\n" + "QuorumLearnerMissingHost {\n" + " com.sun.security.auth.module.Krb5LoginModule required\n" + " useKeyTab=true\n" + " keyTab=\"" + keytabFilePath + "\"\n" + " storeKey=true\n" + " useTicketCache=false\n" + " debug=false\n" + " refreshKrb5Config=true\n" + " principal=\"" + hostlessLearnerPrincipal + "\";\n" + "};\n"; setupJaasConfig(jaasEntries); } @BeforeAll public static void setUp() throws Exception { // create keytab keytabFile = new File(KerberosTestUtils.getKeytabFile()); // Creates principals in the KDC and adds them to a keytab file. String learnerPrincipal = hostLearnerPrincipal.substring(0, hostLearnerPrincipal.lastIndexOf("@")); learnerPrincipal = KerberosTestUtils.replaceHostPattern(learnerPrincipal); String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); serverPrincipal = KerberosTestUtils.replaceHostPattern(serverPrincipal); // learner with ipaddress in principal String learnerPrincipal2 = hostNamedLearnerPrincipal.substring(0, hostNamedLearnerPrincipal.lastIndexOf("@")); // learner without host in principal String learnerPrincipal3 = hostlessLearnerPrincipal.substring(0, hostlessLearnerPrincipal.lastIndexOf("@")); getKdc().createPrincipal(keytabFile, learnerPrincipal, learnerPrincipal2, learnerPrincipal3, serverPrincipal); } @AfterEach @Override public void tearDown() throws Exception { for (MainThread mainThread : mt) { mainThread.shutdown(); mainThread.deleteBaseDir(); } super.tearDown(); } @AfterAll public static void cleanup() { if (keytabFile != null) { FileUtils.deleteQuietly(keytabFile); } cleanupJaasConfig(); } /** * Test to verify that server is able to start with valid credentials */ @Test @Timeout(value = 120) public void testValidCredentials() throws Exception { String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } /** * Test to verify that server is able to start with valid credentials * when using multiple Quorum / Election addresses */ @Test @Timeout(value = 120) public void testValidCredentialsWithMultiAddresses() throws Exception { String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); String connectStr = startMultiAddressQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } /** * Test to verify that the bad server connection to the quorum should be rejected. */ @Test @Timeout(value = 120) public void testConnectBadServer() throws Exception { String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); String quorumCfgSection = mt.get(0).getQuorumCfgSection(); StringBuilder sb = new StringBuilder(); sb.append(quorumCfgSection); int myid = mt.size() + 1; final int clientPort = PortAssignment.unique(); String server = String.format("server.%d=localhost:%d:%d:participant", myid, PortAssignment.unique(), PortAssignment.unique()); sb.append(server + "\n"); quorumCfgSection = sb.toString(); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerMyHost"); MainThread badServer = new MainThread(myid, clientPort, quorumCfgSection, authConfigs); badServer.start(); watcher = new CountdownWatcher(); connectStr = "127.0.0.1:" + clientPort; zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); try { watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT / 3); fail("Must throw exception as the myHost is not an authorized one!"); } catch (TimeoutException e) { // expected } finally { zk.close(); badServer.shutdown(); badServer.deleteBaseDir(); } } /** * Test to verify that the bad server connection to the quorum should be rejected. */ @Test @Timeout(value = 120) public void testConnectHostlessPrincipalBadServer() throws Exception { String serverPrincipal = hostServerPrincipal.substring(0, hostServerPrincipal.lastIndexOf("@")); Map authConfigs = new HashMap<>(); authConfigs.put(QuorumAuth.QUORUM_SASL_AUTH_ENABLED, "true"); authConfigs.put(QuorumAuth.QUORUM_SERVER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_AUTH_REQUIRED, "true"); authConfigs.put(QuorumAuth.QUORUM_KERBEROS_SERVICE_PRINCIPAL, serverPrincipal); String connectStr = startQuorum(3, authConfigs, 3); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); String quorumCfgSection = mt.get(0).getQuorumCfgSection(); StringBuilder sb = new StringBuilder(); sb.append(quorumCfgSection); int myid = mt.size() + 1; final int clientPort = PortAssignment.unique(); String server = String.format("server.%d=localhost:%d:%d:participant", myid, PortAssignment.unique(), PortAssignment.unique()); sb.append(server + "\n"); quorumCfgSection = sb.toString(); authConfigs.put(QuorumAuth.QUORUM_LEARNER_SASL_LOGIN_CONTEXT, "QuorumLearnerMissingHost"); MainThread badServer = new MainThread(myid, clientPort, quorumCfgSection, authConfigs); badServer.start(); watcher = new CountdownWatcher(); connectStr = "127.0.0.1:" + clientPort; zk = new ZooKeeper(connectStr, ClientBase.CONNECTION_TIMEOUT, watcher); try { watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT / 3); fail("Must throw exception as the principal does not include an authorized host!"); } catch (TimeoutException e) { // expected } finally { zk.close(); badServer.shutdown(); badServer.deleteBaseDir(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000160 15051152474 032752 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/AdHashTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/AdHashTest.ja0100644 0000000 0000000 00000007121 15051152474 034115 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class AdHashTest extends ZKTestCase { private static Random rand = new Random(); private static List generateRandomHashes(int count) { ArrayList list = new ArrayList<>(count); for (int i = 0; i < count; i++) { list.add(rand.nextLong()); } return list; } private static void addListOfDigests(AdHash hash, List digests) { for (long b : digests) { hash.addDigest(b); } } private static void removeListOfDigests(AdHash hash, List digests) { for (long b : digests) { hash.removeDigest(b); } } /** * Test thhe add and remove digest from AdHash is working as expected. */ @Test public void testAdHash() throws Exception { List bucket1 = generateRandomHashes(50); List bucket2 = generateRandomHashes(3); List bucket3 = generateRandomHashes(30); List bucket4 = generateRandomHashes(10); List bucket5 = generateRandomHashes(5); // adding out of order should result in the same hash AdHash hash12 = new AdHash(); addListOfDigests(hash12, bucket1); addListOfDigests(hash12, bucket2); AdHash hash21 = new AdHash(); addListOfDigests(hash21, bucket2); addListOfDigests(hash21, bucket1); assertEquals(hash12, hash21); AdHash hashall = new AdHash(); addListOfDigests(hashall, bucket1); addListOfDigests(hashall, bucket2); addListOfDigests(hashall, bucket3); addListOfDigests(hashall, bucket4); addListOfDigests(hashall, bucket5); assertFalse(hashall.equals(hash21), "digest of different set not different"); removeListOfDigests(hashall, bucket4); removeListOfDigests(hashall, bucket5); addListOfDigests(hash21, bucket3); assertEquals(hashall, hash21, "hashall with 4 & 5 removed should match hash21 with 3 added"); removeListOfDigests(hashall, bucket3); removeListOfDigests(hashall, bucket2); removeListOfDigests(hashall, bucket1); assertEquals(hashall.toString(), "0", "empty hashall's digest should be 0"); AdHash hash45 = new AdHash(); addListOfDigests(hash45, bucket4); addListOfDigests(hash45, bucket5); addListOfDigests(hashall, bucket4); addListOfDigests(hashall, bucket5); assertEquals(hashall, hash45, "empty hashall + 4&5 should equal hash45"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000162 15051152474 032754 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/AuthUtilTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/AuthUtilTest.0100644 0000000 0000000 00000005250 15051152474 034212 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class AuthUtilTest { @BeforeAll public static void beforeClassSetUp() { ProviderRegistry.reset(); System.setProperty("zookeeper.authProvider.sasl", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty("zookeeper.authProvider.x509", "org.apache.zookeeper.server.auth.X509AuthenticationProvider"); } @AfterAll public static void afterClassTearDown() { System.clearProperty("zookeeper.authProvider.sasl"); System.clearProperty("zookeeper.authProvider.x509"); } @Test public void testGetUserFromAllAuthenticationScheme() { String user = "zkUser"; Id id = new Id("digest", user + ":password"); String result = AuthUtil.getUser(id); assertEquals(user, result); String principal = "zkCli/hadoop.hadoop.com"; id = new Id("sasl", principal); assertEquals(principal, AuthUtil.getUser(id)); String ip = "192.168.1.2"; id = new Id("ip", ip); assertEquals(ip, AuthUtil.getUser(id)); String certificate = "CN=host-192.168.1.2,OU=OrganizationUnit,O=Organization,L=Location,ST=State,C=IN"; id = new Id("x509", certificate); assertEquals(certificate, AuthUtil.getUser(id)); } @Test public void testGetUserShouldReturnNullIfAuthenticationNotConfigured() { Id id = new Id("invalid Authentication Scheme", "user"); String result = AuthUtil.getUser(id); assertNull(result); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000164 15051152474 032756 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/BitHashSetTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/BitHashSetTes0100644 0000000 0000000 00000006437 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class BitHashSetTest extends ZKTestCase { @Test public void testAddWatchBit() { int watcherCacheSize = 1; BitHashSet ws = new BitHashSet(watcherCacheSize); assertTrue(ws.add(1)); assertEquals(1, ws.size()); assertEquals(1, ws.cachedSize()); List actualBits = new ArrayList<>(); for (int bit : ws) { actualBits.add(bit); } assertArrayEquals(new Integer[]{1}, actualBits.toArray(new Integer[actualBits.size()])); // add the same bit again assertFalse(ws.add(1)); assertEquals(1, ws.size()); assertEquals(1, ws.cachedSize()); // add another bit, make sure there there is only 1 bit cached assertTrue(ws.add(2)); assertEquals(2, ws.size()); assertEquals(1, ws.cachedSize()); assertTrue(ws.contains(1)); actualBits.clear(); for (int bit : ws) { actualBits.add(bit); } assertArrayEquals(new Integer[]{1, 2}, actualBits.toArray(new Integer[actualBits.size()])); } @Test public void testRemoveWatchBit() { int watcherCacheSize = 1; BitHashSet ws = new BitHashSet(watcherCacheSize); ws.add(1); ws.add(2); assertTrue(ws.contains(1)); assertTrue(ws.contains(2)); ws.remove(1); assertFalse(ws.contains(1)); assertEquals(1, ws.size()); assertEquals(0, ws.cachedSize()); List actualBits = new ArrayList<>(); for (int bit : ws) { actualBits.add(bit); } assertArrayEquals(new Integer[]{2}, actualBits.toArray(new Integer[actualBits.size()])); ws.add(3); assertEquals(2, ws.size()); assertEquals(1, ws.cachedSize()); actualBits.clear(); for (int bit : ws) { actualBits.add(bit); } assertArrayEquals(new Integer[]{2, 3}, actualBits.toArray(new Integer[actualBits.size()])); ws.remove(2); ws.remove(3); assertEquals(0, ws.size()); assertEquals(0, ws.cachedSize()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000160 15051152474 032752 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/BitMapTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/BitMapTest.ja0100644 0000000 0000000 00000004703 15051152474 034144 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class BitMapTest extends ZKTestCase { @Test public void testAddAndRemove() { BitMap bitMap = new BitMap<>(); String v1 = "v1"; Integer bit = bitMap.add(v1); assertEquals(1, bitMap.size()); assertTrue(bit >= 0); assertEquals(v1, bitMap.get(bit)); assertEquals(bit, bitMap.getBit(v1)); // add the same value again Integer newBit = bitMap.add(v1); assertEquals(bit, newBit); assertEquals(1, bitMap.size()); String v2 = "v2"; Integer v2Bit = bitMap.add(v2); assertEquals(2, bitMap.size()); assertNotEquals(v2Bit, bit); // remove by value bitMap.remove(v1); assertEquals(1, bitMap.size()); assertNull(bitMap.get(bit)); assertNull(bitMap.getBit(v1)); // remove by bit bitMap.remove(v2Bit); assertEquals(0, bitMap.size()); assertNull(bitMap.get(v2Bit)); assertNull(bitMap.getBit(v2)); } @Test public void testBitReuse() { BitMap bitMap = new BitMap<>(); int v1Bit = bitMap.add("v1"); int v2Bit = bitMap.add("v2"); int v3Bit = bitMap.add("v3"); bitMap.remove(v2Bit); int v4Bit = bitMap.add("v4"); assertEquals(v4Bit, v2Bit); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000170 15051152474 032753 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/CircularBufferTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/CircularBuffe0100644 0000000 0000000 00000014143 15051152474 034252 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.junit.jupiter.api.Test; public class CircularBufferTest { @Test public void testCircularBuffer() { final int capacity = 3; CircularBuffer buffer = new CircularBuffer<>(String.class, capacity); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); // write to the buffer buffer.write("A"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); buffer.write("B"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); buffer.write("C"); assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); // Buffer is full. // Read from buffer assertEquals("A", buffer.take()); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals("B", buffer.take()); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals("C", buffer.take()); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); // write to the buffer buffer.write("1"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); buffer.write("2"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); buffer.write("3"); assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); buffer.write("4"); // 4 overwrites 1 assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); // Buffer if full // Read from buffer assertEquals("2", buffer.take()); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals("3", buffer.take()); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals("4", buffer.take()); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); // write to the buffer buffer.write("a"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); buffer.write("b"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); buffer.write("c"); assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); buffer.write("d"); // d overwrites a assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); buffer.write("e"); // e overwrites b assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); buffer.write("f"); // f overwrites c assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); buffer.write("g"); // g overwrites d assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); // Buffer is full. // Read from buffer assertEquals("e", buffer.take()); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals("f", buffer.take()); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals("g", buffer.take()); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); } @Test public void testCircularBufferWithCapacity1() { final int capacity = 1; CircularBuffer buffer = new CircularBuffer<>(String.class, capacity); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); // write to the buffer buffer.write("A"); assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); buffer.write("B"); // B overwrite A assertFalse(buffer.isEmpty()); assertTrue(buffer.isFull()); // Buffer is full. // Read from buffer assertEquals("B", buffer.take()); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); } @Test public void testCircularBufferReset() { final int capacity = 3; CircularBuffer buffer = new CircularBuffer<>(String.class, capacity); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); // write to the buffer buffer.write("A"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals(1, buffer.size()); assertEquals("A", buffer.peek()); buffer.write("B"); assertFalse(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals(2, buffer.size()); assertEquals("A", buffer.peek()); // reset buffer.reset(); assertNull(buffer.peek()); assertTrue(buffer.isEmpty()); assertFalse(buffer.isFull()); assertEquals(0, buffer.size()); } @Test public void testCircularBufferIllegalCapacity() { try { CircularBuffer buffer = new CircularBuffer<>(String.class, 0); fail(); } catch (IllegalArgumentException e) { assertEquals("CircularBuffer capacity should be greater than 0", e.getMessage()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000165 15051152474 032757 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/ConfigUtilsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/ConfigUtilsTe0100644 0000000 0000000 00000010634 15051152474 034256 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException; import org.junit.jupiter.api.Test; public class ConfigUtilsTest { @Test public void testGetHostAndPortWithIPv6() throws ConfigException { String[] nsa = ConfigUtils.getHostAndPort("[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"); assertEquals(nsa[0], "2001:db8:85a3:8d3:1319:8a2e:370:7348"); assertEquals(nsa[1], "443"); nsa = ConfigUtils.getHostAndPort("[2001:db8:1::242:ac11:2]:2888:3888"); assertEquals(nsa[0], "2001:db8:1::242:ac11:2"); assertEquals(nsa[1], "2888"); assertEquals(nsa[2], "3888"); } @Test public void testGetHostAndPortWithIPv4() throws ConfigException { String[] nsa = ConfigUtils.getHostAndPort("127.0.0.1:443"); assertEquals(nsa[0], "127.0.0.1"); assertEquals(nsa[1], "443"); nsa = ConfigUtils.getHostAndPort("127.0.0.1:2888:3888"); assertEquals(nsa[0], "127.0.0.1"); assertEquals(nsa[1], "2888"); assertEquals(nsa[2], "3888"); } @Test public void testGetHostAndPortWithoutBracket() { assertThrows(ConfigException.class, () -> { String[] nsa = ConfigUtils.getHostAndPort("[2001:db8:85a3:8d3:1319:8a2e:370:7348"); }); } @Test public void testGetHostAndPortWithoutPortAfterColon() { assertThrows(ConfigException.class, () -> { String[] nsa = ConfigUtils.getHostAndPort("[2001:db8:1::242:ac11:2]:"); }); } @Test public void testGetHostAndPortWithoutPort() throws ConfigException { String[] nsa = ConfigUtils.getHostAndPort("127.0.0.1"); assertEquals(nsa[0], "127.0.0.1"); assertEquals(nsa.length, 1); nsa = ConfigUtils.getHostAndPort("[2001:db8:1::242:ac11:2]"); assertEquals(nsa[0], "2001:db8:1::242:ac11:2"); assertEquals(nsa.length, 1); } @Test public void testGetPropertyBackwardCompatibleWay() throws ConfigException { String newProp = "zookeeper.prop.x.y.z"; String oldProp = "prop.x.y.z"; // Null as both properties are not set String result = ConfigUtils.getPropertyBackwardCompatibleWay(newProp); assertNull(result); // Return old property value when only old property is set String oldPropValue = "oldPropertyValue"; System.setProperty(oldProp, oldPropValue); result = ConfigUtils.getPropertyBackwardCompatibleWay(newProp); assertEquals(oldPropValue, result); // Return new property value when both properties are set String newPropValue = "newPropertyValue"; System.setProperty(newProp, newPropValue); result = ConfigUtils.getPropertyBackwardCompatibleWay(newProp); assertEquals(newPropValue, result); // cleanUp clearProp(newProp, oldProp); // Return trimmed value System.setProperty(oldProp, oldPropValue + " "); result = ConfigUtils.getPropertyBackwardCompatibleWay(newProp); assertEquals(oldPropValue, result); System.setProperty(newProp, " " + newPropValue); result = ConfigUtils.getPropertyBackwardCompatibleWay(newProp); assertEquals(newPropValue, result); // cleanUp clearProp(newProp, oldProp); } private void clearProp(String newProp, String oldProp) { System.clearProperty(newProp); System.clearProperty(oldProp); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000171 15051152474 032754 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/JvmPauseMonitorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/JvmPauseMonit0100644 0000000 0000000 00000005406 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class JvmPauseMonitorTest { private final Long sleepTime = 100L; private final Long infoTH = -1L; private final Long warnTH = -1L; private JvmPauseMonitor pauseMonitor; @Test @Timeout(value = 5) public void testJvmPauseMonitorExceedInfoThreshold() throws InterruptedException { QuorumPeerConfig qpConfig = mock(QuorumPeerConfig.class); when(qpConfig.getJvmPauseSleepTimeMs()).thenReturn(sleepTime); when(qpConfig.getJvmPauseInfoThresholdMs()).thenReturn(infoTH); pauseMonitor = new JvmPauseMonitor(qpConfig); pauseMonitor.serviceStart(); assertEquals(sleepTime, Long.valueOf(pauseMonitor.sleepTimeMs)); assertEquals(infoTH, Long.valueOf(pauseMonitor.infoThresholdMs)); while (pauseMonitor.getNumGcInfoThresholdExceeded() == 0) { Thread.sleep(200); } } @Test @Timeout(value = 5) public void testJvmPauseMonitorExceedWarnThreshold() throws InterruptedException { QuorumPeerConfig qpConfig = mock(QuorumPeerConfig.class); when(qpConfig.getJvmPauseSleepTimeMs()).thenReturn(sleepTime); when(qpConfig.getJvmPauseWarnThresholdMs()).thenReturn(warnTH); pauseMonitor = new JvmPauseMonitor(qpConfig); pauseMonitor.serviceStart(); assertEquals(sleepTime, Long.valueOf(pauseMonitor.sleepTimeMs)); assertEquals(warnTH, Long.valueOf(pauseMonitor.warnThresholdMs)); while (pauseMonitor.getNumGcWarnThresholdExceeded() == 0) { Thread.sleep(200); } } @AfterEach public void teardown() { pauseMonitor.serviceStop(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000170 15051152474 032753 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/MessageTrackerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/MessageTracke0100644 0000000 0000000 00000011600 15051152474 034247 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MessageTrackerTest { private static final int BUFFERED_MESSAGE_SIZE = 5; private static final Logger LOG = LoggerFactory.getLogger(MessageTrackerTest.class); @BeforeEach public void setup() { System.setProperty(MessageTracker.MESSAGE_TRACKER_ENABLED, "true"); } @AfterEach public void tearDown() throws Exception { System.clearProperty(MessageTracker.MESSAGE_TRACKER_ENABLED); } @Test public void testTrackSend() throws InterruptedException { long timestamp1 = System.currentTimeMillis(); MessageTracker messageTracker = new MessageTracker(BUFFERED_MESSAGE_SIZE); // First timestamp is added messageTracker.trackSent(timestamp1); assertEquals(messageTracker.peekSentTimestamp(), timestamp1); Thread.sleep(2); // Second timestamp is added long timestamp2 = System.currentTimeMillis(); messageTracker.trackSent(timestamp2); assertEquals(messageTracker.peekSentTimestamp(), timestamp1); } @Test public void testTrackReceived() throws InterruptedException { long timestamp1 = System.currentTimeMillis(); MessageTracker messageTracker = new MessageTracker(BUFFERED_MESSAGE_SIZE); // First timestamp is added messageTracker.trackReceived(timestamp1); assertEquals(messageTracker.peekReceivedTimestamp(), timestamp1); Thread.sleep(2); // Second timestamp is added long timestamp2 = System.currentTimeMillis(); messageTracker.trackReceived(timestamp2); assertEquals(messageTracker.peekReceivedTimestamp(), timestamp1); } @Test public void testMessageTrackerFull() throws InterruptedException { MessageTracker messageTracker = new MessageTracker(BUFFERED_MESSAGE_SIZE); // Add up to capacity + 1 long timestampSent = 0; long timestampReceived = 0; for (int i = 0; i <= BUFFERED_MESSAGE_SIZE; i++) { if (i == 1) { timestampSent = System.currentTimeMillis(); messageTracker.trackSent(timestampSent); Thread.sleep(2); timestampReceived = System.currentTimeMillis(); messageTracker.trackReceived(timestampReceived); } else { messageTracker.trackSent(System.currentTimeMillis()); messageTracker.trackReceived(System.currentTimeMillis()); } Thread.sleep(1); } assertEquals(messageTracker.peekSentTimestamp(), timestampSent); assertEquals(messageTracker.peekReceivedTimestamp(), timestampReceived); } @Test public void testDumpToLog() { long timestamp1 = System.currentTimeMillis(); MessageTracker messageTracker = new MessageTracker(BUFFERED_MESSAGE_SIZE); String sid = "127.0.0.1"; // MessageTracker is empty messageTracker.dumpToLog(sid); assertNull(messageTracker.peekSent()); assertNull(messageTracker.peekReceived()); // There is 1 sent and 0 received messageTracker.trackSent(timestamp1); assertEquals(messageTracker.peekSentTimestamp(), timestamp1); assertNull(messageTracker.peekReceived()); messageTracker.dumpToLog(sid); assertNull(messageTracker.peekSent()); assertNull(messageTracker.peekReceived()); // There is 1 sent and 1 received messageTracker.trackSent(timestamp1); messageTracker.trackReceived(timestamp1); assertEquals(messageTracker.peekSentTimestamp(), timestamp1); assertEquals(messageTracker.peekReceivedTimestamp(), timestamp1); messageTracker.dumpToLog(sid); assertNull(messageTracker.peekSent()); assertNull(messageTracker.peekReceived()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000163 15051152474 032755 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/PortForwarder.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/PortForwarder0100644 0000000 0000000 00000022427 15051152474 034342 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A utility that does bi-directional forwarding between two ports. * Useful, for example, to simulate network failures. * Example: * * Server 1 config file: * * server.1=127.0.0.1:7301:7401;8201 * server.2=127.0.0.1:7302:7402;8202 * server.3=127.0.0.1:7303:7403;8203 * * Server 2 and 3 config files: * * server.1=127.0.0.1:8301:8401;8201 * server.2=127.0.0.1:8302:8402;8202 * server.3=127.0.0.1:8303:8403;8203 * * Initially forward traffic between 730x and 830x and between 740x and 830x * This way server 1 can communicate with servers 2 and 3 * .... * * List<PortForwarder> pfs = startForwarding(); * .... * // simulate a network interruption for server 1 * stopForwarding(pfs); * .... * // restore connection * pfs = startForwarding(); * * * private List<PortForwarder> startForwarding() throws IOException { * List<PortForwarder> res = new ArrayList<PortForwarder>(); * res.add(new PortForwarder(8301, 7301)); * res.add(new PortForwarder(8401, 7401)); * res.add(new PortForwarder(7302, 8302)); * res.add(new PortForwarder(7402, 8402)); * res.add(new PortForwarder(7303, 8303)); * res.add(new PortForwarder(7403, 8403)); * return res; * } * * private void stopForwarding(List<PortForwarder> pfs) throws Exception { * for (PortForwarder pf : pfs) { * pf.shutdown(); * } * } * * */ public class PortForwarder extends Thread { private static final Logger LOG = LoggerFactory.getLogger(PortForwarder.class); private static class PortForwardWorker implements Runnable { private final InputStream in; private final OutputStream out; private final Socket toClose; private final Socket toClose2; private boolean isFinished = false; PortForwardWorker(Socket toClose, Socket toClose2, InputStream in, OutputStream out) { this.toClose = toClose; this.toClose2 = toClose2; this.in = in; this.out = out; // LOG.info("starting forward for "+toClose); } public void run() { Thread.currentThread().setName(toClose.toString() + "-->" + toClose2.toString()); byte[] buf = new byte[1024]; try { while (true) { try { int read = this.in.read(buf); if (read > 0) { try { this.out.write(buf, 0, read); } catch (IOException e) { LOG.warn("exception during write", e); break; } } else if (read < 0) { throw new IOException("read " + read); } } catch (SocketTimeoutException e) { LOG.error("socket timeout", e); } } Thread.sleep(1); } catch (InterruptedException e) { LOG.warn("Interrupted", e); } catch (SocketException e) { if (!"Socket closed".equals(e.getMessage())) { LOG.error("Unexpected exception", e); } } catch (IOException e) { LOG.error("Unexpected exception", e); } finally { shutdown(); } LOG.info("Shutting down forward for {}", toClose); isFinished = true; } boolean waitForShutdown(long timeoutMs) throws InterruptedException { synchronized (this) { if (!isFinished) { this.wait(timeoutMs); } } return isFinished; } public void shutdown() { try { toClose.close(); } catch (IOException ex) { // ignore } try { toClose2.close(); } catch (IOException ex) { // ignore silently } } } private volatile boolean stopped = false; private ExecutorService workerExecutor = Executors.newCachedThreadPool(); private List workers = new ArrayList<>(); private ServerSocket serverSocket; private final int to; public PortForwarder(int from, int to) throws IOException { this.to = to; serverSocket = new ServerSocket(from); serverSocket.setSoTimeout(30000); this.start(); } @Override public void run() { try { while (!stopped) { Socket sock = null; try { LOG.info("accepting socket local:{} to:{}", serverSocket.getLocalPort(), to); sock = serverSocket.accept(); LOG.info("accepted: local:{} from:{} to:{}", sock.getLocalPort(), sock.getPort(), to); Socket target = null; int retry = 10; while (sock.isConnected()) { try { target = new Socket("localhost", to); break; } catch (IOException e) { if (retry == 0) { throw e; } LOG.warn( "connection failed, retrying({}): local:{} from:{} to:{}", retry, sock.getLocalPort(), sock.getPort(), to, e); } Thread.sleep(TimeUnit.SECONDS.toMillis(1)); retry--; } LOG.info("connected: local:{} from:{} to:{}", sock.getLocalPort(), sock.getPort(), to); sock.setSoTimeout(30000); target.setSoTimeout(30000); workers.add(new PortForwardWorker(sock, target, sock.getInputStream(), target.getOutputStream())); workers.add(new PortForwardWorker(target, sock, target.getInputStream(), sock.getOutputStream())); for (PortForwardWorker worker : workers) { workerExecutor.submit(worker); } } catch (SocketTimeoutException e) { LOG.warn("socket timed out", e); } catch (ConnectException e) { LOG.warn( "connection exception local:{} from:{} to:{}", sock.getLocalPort(), sock.getPort(), to, e); sock.close(); } catch (IOException e) { if (!"Socket closed".equals(e.getMessage())) { LOG.warn( "unexpected exception local:{} from:{} to:{}", sock.getLocalPort(), sock.getPort(), to, e); throw e; } } } } catch (IOException e) { LOG.error("Unexpected exception to:{}", to, e); } catch (InterruptedException e) { LOG.error("Interrupted to:{}", to, e); } } public void shutdown() throws Exception { this.stopped = true; this.serverSocket.close(); this.join(); this.workerExecutor.shutdownNow(); for (PortForwardWorker worker : workers) { worker.shutdown(); } for (PortForwardWorker worker : workers) { if (!worker.waitForShutdown(5000)) { throw new Exception("Failed to stop forwarding within 5 seconds"); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000173 15051152474 032756 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/QuotaMetricsUtilsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/QuotaMetricsU0100644 0000000 0000000 00000034614 15051152474 034310 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.metrics.MetricsContext; import org.apache.zookeeper.metrics.MetricsProvider; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.ServerMetrics; import org.junit.jupiter.api.Test; public class QuotaMetricsUtilsTest extends ZKTestCase { @Test public void testQuotaMetrics_singleQuotaSubtree() throws Exception { // register the metrics final String nameSuffix = UUID.randomUUID().toString(); final DataTree dt = new DataTree(); registerQuotaMetrics(nameSuffix, dt); // build the data tree final String ns = UUID.randomUUID().toString(); final long countLimit = 10; final long bytesLimit = 100; final long countHardLimit = 5; final long bytesHardLimit = 50; final long countUsage = 5; final long bytesUsage = 40; final StatsTrack limitTrack = buildLimitStatsTrack(countLimit, bytesLimit, countHardLimit, bytesHardLimit); final StatsTrack usageTrack = buildUsageStatsTrack(countUsage, bytesUsage); buildDataTree("/" + ns, limitTrack, usageTrack, dt); // validate the quota metrics validateQuotaMetrics(ns, countHardLimit, bytesHardLimit, countUsage, bytesUsage, nameSuffix); } @Test public void testQuotaMetrics_multipleQuotaSubtrees() throws Exception { // register the metrics final String nameSuffix = UUID.randomUUID().toString(); final DataTree dt = new DataTree(); registerQuotaMetrics(nameSuffix, dt); // build the data tree final String ns = UUID.randomUUID().toString(); final long countLimit1 = 10; final long bytesLimit1 = 100; final long countHardLimit1 = 5; final long bytesHardLimit1 = 50; final long countUsage1 = 5; final long bytesUsage1 = 40; final StatsTrack limitTrack1 = buildLimitStatsTrack(countLimit1, bytesLimit1, countHardLimit1, bytesHardLimit1); final StatsTrack usageTrack1 = buildUsageStatsTrack(countUsage1, bytesUsage1); buildDataTree("/" + ns + "/a/b", limitTrack1, usageTrack1, dt); // validate the quota metrics validateQuotaMetrics(ns, countHardLimit1, bytesHardLimit1, countUsage1, bytesUsage1, nameSuffix); // update the data tree with another quota subtree final long countLimit2 = 20; final long bytesLimit2 = 200; final long countHardLimit2 = 10; final long bytesHardLimit2 = 100; final long countUsage2 = 9; final long bytesUsage2 = 80; final StatsTrack limitTrack2 = buildLimitStatsTrack(countLimit2, bytesLimit2, countHardLimit2, bytesHardLimit2); final StatsTrack usageTrack2 = buildUsageStatsTrack(countUsage2, bytesUsage2); buildDataTree("/" + ns + "/a/c/d", limitTrack2, usageTrack2, dt); // validate the quota metrics validateQuotaMetrics(ns, countHardLimit1 + countHardLimit2, bytesHardLimit1 + bytesHardLimit2, countUsage1 + countUsage2, bytesUsage1 + bytesUsage2, nameSuffix); } @Test public void testQuotaMetrics_noUsage() throws Exception { // register the metrics final String nameSuffix = UUID.randomUUID().toString(); final DataTree dt = new DataTree(); registerQuotaMetrics(nameSuffix, dt); // build the data tree final String ns = UUID.randomUUID().toString(); final long countLimit = 20; final long bytesLimit = 200; final long countHardLimit = -1; final long bytesHardLimit = -1; final long countUsage = 1; // the node itself is always counted final long bytesUsage = 0; final StatsTrack limitTrack = buildLimitStatsTrack(countLimit, bytesLimit, countHardLimit, bytesHardLimit); final StatsTrack usageTrack = buildUsageStatsTrack(countUsage, bytesUsage); buildDataTree("/" + ns, limitTrack, usageTrack, dt); // validate the quota validateQuotaMetrics(ns, countLimit, bytesLimit, countUsage, bytesUsage, nameSuffix); } @Test public void testQuotaMetrics_nullDataTree() { // register the metrics final String nameSuffix = UUID.randomUUID().toString(); registerQuotaMetrics(nameSuffix, null); // validate the quota validateQuotaMetrics(UUID.randomUUID().toString(), null, null, null, null, nameSuffix); } @Test public void testQuotaMetrics_emptyDataTree() { // register the metrics final String nameSuffix = UUID.randomUUID().toString(); registerQuotaMetrics(nameSuffix, new DataTree()); // validate the quota validateQuotaMetrics(UUID.randomUUID().toString(), null, null, null, null, nameSuffix); } @Test public void testShouldCollect_limitPath() { final String limitPath = Quotas.quotaPath("/ns1") + QuotaMetricsUtils.LIMIT_END_STRING; assertTrue(QuotaMetricsUtils.shouldCollect(limitPath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_LIMIT)); assertTrue(QuotaMetricsUtils.shouldCollect(limitPath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT)); assertFalse(QuotaMetricsUtils.shouldCollect(limitPath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_USAGE)); assertFalse(QuotaMetricsUtils.shouldCollect(limitPath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_USAGE)); } @Test public void testShouldCollect_usagePath() { final String usagePath = Quotas.quotaPath("/ns1") + QuotaMetricsUtils.STATS_END_STRING; assertTrue(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_USAGE)); assertTrue(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_USAGE)); assertFalse(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_LIMIT)); assertFalse(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT)); } @Test public void testShouldCollect_notLimitOrUsagePath() { final String usagePath = Quotas.quotaPath("/ns1") + "/notLimitOrUsage"; assertFalse(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_USAGE)); assertFalse(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_USAGE)); assertFalse(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_COUNT_LIMIT)); assertFalse(QuotaMetricsUtils.shouldCollect(usagePath, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT)); } @Test public void testGetQuotaLimit() { assertEquals(0L, QuotaMetricsUtils.getQuotaLimit(0L, -1L)); assertEquals(1L, QuotaMetricsUtils.getQuotaLimit(-1L, 1L)); assertEquals(0L, QuotaMetricsUtils.getQuotaLimit(-2L, 0L)); } @Test public void testCollectQuotaMetrics_noData() { final Map metricsMap = new HashMap<>(); QuotaMetricsUtils.collectQuotaLimitOrUsage(Quotas.quotaPath("/ns1") + QuotaMetricsUtils.LIMIT_END_STRING, new DataNode(new byte[0], null, null), metricsMap, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT); assertEquals(1, metricsMap.size()); final Map.Entry entry = metricsMap.entrySet().iterator().next(); assertEquals("ns1", entry.getKey()); assertEquals(-1L, entry.getValue().longValue()); } @Test public void testCollectQuotaMetrics_nullData() { final Map metricsMap = new HashMap<>(); QuotaMetricsUtils.collectQuotaLimitOrUsage(Quotas.quotaPath("/ns1") + QuotaMetricsUtils.LIMIT_END_STRING, new DataNode(null, null, null), metricsMap, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_LIMIT); assertEquals(0, metricsMap.size()); } @Test public void testCollectQuotaMetrics_noNamespace() { final Map metricsMap = new HashMap<>(); QuotaMetricsUtils.collectQuotaLimitOrUsage("/zookeeper/quota", new DataNode(null, null, null), metricsMap, QuotaMetricsUtils.QUOTA_LIMIT_USAGE_METRIC_TYPE.QUOTA_BYTES_USAGE); assertEquals(0, metricsMap.size()); } private void registerQuotaMetrics(final String nameSuffix, final DataTree dt) { final MetricsProvider metricProvider = ServerMetrics.getMetrics().getMetricsProvider(); final MetricsContext rootContext = metricProvider.getRootContext(); // added random UUID as NAME_SUFFIX to avoid GaugeSet being overwritten when registering with same name rootContext.registerGaugeSet( QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE + nameSuffix, () -> QuotaMetricsUtils.getQuotaCountLimit(dt)); rootContext.registerGaugeSet( QuotaMetricsUtils.QUOTA_BYTES_LIMIT_PER_NAMESPACE + nameSuffix, () -> QuotaMetricsUtils.getQuotaBytesLimit(dt)); rootContext.registerGaugeSet( QuotaMetricsUtils.QUOTA_COUNT_USAGE_PER_NAMESPACE + nameSuffix, () -> QuotaMetricsUtils.getQuotaCountUsage(dt)); rootContext.registerGaugeSet( QuotaMetricsUtils.QUOTA_BYTES_USAGE_PER_NAMESPACE + nameSuffix, () -> QuotaMetricsUtils.getQuotaBytesUsage(dt)); } private StatsTrack buildLimitStatsTrack(final long countLimit, final long bytesLimit, final long countHardLimit, final long bytesHardLimit) { final StatsTrack limitTrack = new StatsTrack(); limitTrack.setCount(countLimit); limitTrack.setBytes(bytesLimit); limitTrack.setCountHardLimit(countHardLimit); limitTrack.setByteHardLimit(bytesHardLimit); return limitTrack; } private StatsTrack buildUsageStatsTrack(final long countUsage, final long bytesUsage) { final StatsTrack usageTrack = new StatsTrack(); usageTrack.setCount(countUsage); usageTrack.setBytes(bytesUsage); return usageTrack; } private void buildDataTree(final String path, final StatsTrack limitTrack, final StatsTrack usageTrack, final DataTree dataTree) throws Exception { // create the ancestor and child data nodes buildAncestors(path, dataTree); int childCount = (int) usageTrack.getCount() - 1; // the node count always includes the top namespace itself if (childCount > 0) { int dataBytes = (int) usageTrack.getBytes() / childCount; for (int i = 0; i < childCount; i++) { dataTree.createNode(path + "/n_" + i, new byte[dataBytes], null, -1, 1, 1, 1); } } // create the quota tree buildAncestors(Quotas.quotaPath(path), dataTree); final String limitPath = Quotas.limitPath(path); dataTree.createNode(limitPath, limitTrack.getStatsBytes(), null, -1, 1, 1, 1); assertEquals(limitTrack, new StatsTrack(dataTree.getNode(limitPath).getData())); final String usagePath = Quotas.statPath(path); dataTree.createNode(usagePath, usageTrack.getStatsBytes(), null, -1, 1, 1, 1); assertEquals(usageTrack, new StatsTrack(dataTree.getNode(usagePath).getData())); } private void buildAncestors(final String path, final DataTree dataTree) throws Exception { final String[] parts = path.split("/"); String nodePath = ""; for (int i = 1; i < parts.length; i++) { nodePath = nodePath + "/" + parts[i]; try { dataTree.createNode(nodePath, null, null, -1, 1, 1, 1); } catch (final KeeperException.NodeExistsException e) { // ignored } } } private void validateQuotaMetrics(final String namespace, final Long countLimit, final Long bytesLimit, final Long countUsage, final Long bytesUsage, final String nameSuffix) { final Map values = MetricsUtils.currentServerMetrics(); assertEquals(countLimit, values.get(namespace + "_" + QuotaMetricsUtils.QUOTA_COUNT_LIMIT_PER_NAMESPACE + nameSuffix)); assertEquals(bytesLimit, values.get(namespace + "_" + QuotaMetricsUtils.QUOTA_BYTES_LIMIT_PER_NAMESPACE + nameSuffix)); assertEquals(countUsage, values.get(namespace + "_" + QuotaMetricsUtils.QUOTA_COUNT_USAGE_PER_NAMESPACE + nameSuffix)); assertEquals(bytesUsage, values.get(namespace + "_" + QuotaMetricsUtils.QUOTA_BYTES_USAGE_PER_NAMESPACE + nameSuffix)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000165 15051152474 032757 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/RateLimiterTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/RateLimiterTe0100644 0000000 0000000 00000004453 15051152474 034253 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; public class RateLimiterTest { @Test public void testAllow_withinInterval() { final int rate = 2; final RateLimiter rateLimiter = new RateLimiter(rate, 5, TimeUnit.SECONDS); for (int i = 0; i < rate; i++) { assertTrue(rateLimiter.allow()); } assertFalse(rateLimiter.allow()); } @Test public void testAllow_withinInterval_multiThreaded() { final int rate = 10; final RateLimiter rateLimiter = new RateLimiter(rate, 5, TimeUnit.SECONDS); final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(rate + 1); for (int i = 0; i < rate; i++) { executor.execute(() -> assertTrue(rateLimiter.allow())); } executor.execute(() -> assertFalse(rateLimiter.allow())); } @Test public void testAllow_exceedInterval() throws Exception { final int interval = 1; final RateLimiter rateLimiter = new RateLimiter(1, interval, TimeUnit.SECONDS); assertTrue(rateLimiter.allow()); assertFalse(rateLimiter.allow()); Thread.sleep(TimeUnit.SECONDS.toMillis(interval + 1)); assertTrue(rateLimiter.allow()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000205 15051152474 032752 xustar000000000 0000000 133 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/RequestPathMetricsCollectorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/RequestPathMe0100644 0000000 0000000 00000054052 15051152474 034270 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.apache.zookeeper.ZooDefs.OpCode.create; import static org.apache.zookeeper.ZooDefs.OpCode.create2; import static org.apache.zookeeper.ZooDefs.OpCode.delete; import static org.apache.zookeeper.ZooDefs.OpCode.exists; import static org.apache.zookeeper.ZooDefs.OpCode.getChildren; import static org.apache.zookeeper.ZooDefs.OpCode.getChildren2; import static org.apache.zookeeper.ZooDefs.OpCode.getData; import static org.apache.zookeeper.ZooDefs.OpCode.setData; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Map; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class RequestPathMetricsCollectorTest { @BeforeEach public void setUp() { System.setProperty("zookeeper.pathStats.enabled", "true"); System.setProperty("zookeeper.pathStats.slotCapacity", "60"); System.setProperty("zookeeper.pathStats.slotDuration", "1"); System.setProperty("zookeeper.pathStats.maxDepth", "6"); System.setProperty("zookeeper.pathStats.sampleRate", "1.0"); } @AfterEach public void tearDown() { System.clearProperty("zookeeper.pathStats.enabled"); System.clearProperty("zookeeper.pathStats.slotCapacity"); System.clearProperty("zookeeper.pathStats.slotDuration"); System.clearProperty("zookeeper.pathStats.maxDepth"); System.clearProperty("zookeeper.pathStats.sampleRate"); } @Test public void testTrimPath() { //normal cases String trimedPath = RequestPathMetricsCollector.trimPathDepth("/p1/p2/p3", 1); assertTrue(trimedPath.equalsIgnoreCase("/p1")); trimedPath = RequestPathMetricsCollector.trimPathDepth("/p1/p2/p3", 2); assertTrue(trimedPath.equalsIgnoreCase("/p1/p2")); trimedPath = RequestPathMetricsCollector.trimPathDepth("/p1/p2/p3", 3); assertTrue(trimedPath.equalsIgnoreCase("/p1/p2/p3")); trimedPath = RequestPathMetricsCollector.trimPathDepth("/p1/p2/p3", 4); assertTrue(trimedPath.equalsIgnoreCase("/p1/p2/p3")); //some extra symbols trimedPath = RequestPathMetricsCollector.trimPathDepth("//p1 next/p2.index/p3:next", 3); assertTrue(trimedPath.equalsIgnoreCase("/p1 next/p2.index/p3:next")); trimedPath = RequestPathMetricsCollector.trimPathDepth("//p1 next/p2.index/p3:next", 2); assertTrue(trimedPath.equalsIgnoreCase("/p1 next/p2.index")); trimedPath = RequestPathMetricsCollector.trimPathDepth("//p1 next/p2.index/p3:next", 6); assertTrue(trimedPath.equalsIgnoreCase("/p1 next/p2.index/p3:next")); } @Test public void testQueueMapReduce() throws InterruptedException { RequestPathMetricsCollector requestPathMetricsCollector = new RequestPathMetricsCollector(); RequestPathMetricsCollector.PathStatsQueue pathStatsQueue = requestPathMetricsCollector.new PathStatsQueue(create2); Thread path7 = new Thread(() -> { for (int i = 0; i < 1000000; i++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5/path6/path7" + "_" + i); } }); path7.start(); Thread path6 = new Thread(() -> { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5/path6"); for (int i = 1; i < 100000; i++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5/path6" + "_" + i); } }); path6.start(); for (int i = 0; i < 1; i++) { pathStatsQueue.registerRequest("/path1"); } for (int i = 0; i < 10; i++) { pathStatsQueue.registerRequest("/path1/path2" + "_" + i); } for (int i = 0; i < 100; i++) { pathStatsQueue.registerRequest("/path1/path2/path3" + "_" + i); } for (int i = 0; i < 1000; i++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4" + "_" + i); } for (int i = 0; i < 10000; i++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5" + "_" + i); } path6.join(); path7.join(); Map newSlot = pathStatsQueue.mapReducePaths(1, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 1); assertTrue(newSlot.get("/path1").compareTo(1111111) == 0); //cut up to 2 newSlot = pathStatsQueue.mapReducePaths(2, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 12); assertTrue(newSlot.get("/path1").compareTo(1) == 0); assertTrue(newSlot.get("/path1/path2").compareTo(1111100) == 0); //cut up to 3 newSlot = pathStatsQueue.mapReducePaths(3, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 112); assertTrue(newSlot.get("/path1").compareTo(1) == 0); assertTrue(newSlot.get("/path1/path2/path3").compareTo(1111000) == 0); //cut up to 4 newSlot = pathStatsQueue.mapReducePaths(4, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 1112); assertTrue(newSlot.get("/path1/path2/path3/path4").compareTo(1110000) == 0); //cut up to 5 newSlot = pathStatsQueue.mapReducePaths(5, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 11112); assertTrue(newSlot.get("/path1/path2/path3/path4/path5").compareTo(1100000) == 0); //cut up to 6 newSlot = pathStatsQueue.mapReducePaths(6, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 111111); assertTrue(newSlot.get("/path1/path2/path3/path4/path5/path6").compareTo(1000001) == 0); //cut up to 7 newSlot = pathStatsQueue.mapReducePaths(7, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.size() == 1111111); } @Test public void testCollectEmptyStats() throws InterruptedException { RequestPathMetricsCollector requestPathMetricsCollector = new RequestPathMetricsCollector(); RequestPathMetricsCollector.PathStatsQueue pathStatsQueue = requestPathMetricsCollector.new PathStatsQueue(getChildren); Thread.sleep(5000); Map newSlot = pathStatsQueue.mapReducePaths(3, pathStatsQueue.getCurrentSlot()); assertTrue(newSlot.isEmpty()); pathStatsQueue.start(); Thread.sleep(15000); newSlot = pathStatsQueue.collectStats(1); assertTrue(newSlot.size() == 0); newSlot = pathStatsQueue.collectStats(2); assertTrue(newSlot.size() == 0); newSlot = pathStatsQueue.collectStats(5); assertTrue(newSlot.size() == 0); } @Test @Disabled public void testCollectStats() throws InterruptedException { RequestPathMetricsCollector requestPathMetricsCollector = new RequestPathMetricsCollector(true); RequestPathMetricsCollector.PathStatsQueue pathStatsQueue = requestPathMetricsCollector.new PathStatsQueue(getChildren); pathStatsQueue.start(); Thread path7 = new Thread(() -> { for (int i = 0; i < 10; i++) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 100000; j++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5/path6/path7" + "_" + i + "_" + j); } } }); path7.start(); Thread path6 = new Thread(() -> { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5/path6"); for (int i = 0; i < 10; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 10000; j++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5/path6" + "_" + i + "_" + j); } } }); path6.start(); for (int i = 0; i < 1; i++) { pathStatsQueue.registerRequest("/path1"); } for (int i = 0; i < 10; i++) { pathStatsQueue.registerRequest("/path1/path2" + "_" + i); } for (int i = 0; i < 100; i++) { pathStatsQueue.registerRequest("/path1/path2/path3" + "_" + i); } for (int i = 0; i < 1000; i++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4" + "_" + i); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10000; i++) { pathStatsQueue.registerRequest("/path1/path2/path3/path4/path5" + "_" + i); } path6.join(); path7.join(); Map newSlot = pathStatsQueue.collectStats(1); assertEquals(newSlot.size(), 1); assertEquals(newSlot.get("/path1").intValue(), 1111112); //cut up to 2 newSlot = pathStatsQueue.collectStats(2); assertEquals(newSlot.size(), 12); assertEquals(newSlot.get("/path1").intValue(), 1); assertEquals(newSlot.get("/path1/path2").intValue(), 1111101); //cut up to 3 newSlot = pathStatsQueue.collectStats(3); assertEquals(newSlot.size(), 112); assertEquals(newSlot.get("/path1").intValue(), 1); assertEquals(newSlot.get("/path1/path2/path3").intValue(), 1111001); //cut up to 4 newSlot = pathStatsQueue.collectStats(4); assertEquals(newSlot.size(), 1112); assertEquals(newSlot.get("/path1/path2/path3/path4").intValue(), 1110001); //cut up to 5 newSlot = pathStatsQueue.collectStats(5); assertEquals(newSlot.size(), 11112); assertEquals(newSlot.get("/path1/path2/path3/path4/path5").intValue(), 1100001); //cut up to 6 newSlot = pathStatsQueue.collectStats(6); assertEquals(newSlot.size(), 111112); assertEquals(newSlot.get("/path1/path2/path3/path4/path5/path6").intValue(), 1000001); } @Test public void testAggregate() throws InterruptedException { RequestPathMetricsCollector requestPathMetricsCollector = new RequestPathMetricsCollector(true); Thread path7 = new Thread(() -> { for (int i = 0; i < 10; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 100000; j++) { requestPathMetricsCollector.registerRequest(getData, "/path1/path2/path3/path4/path5/path6/path7" + "_" + i + "_" + j); } } }); path7.start(); Thread path6 = new Thread(() -> { requestPathMetricsCollector.registerRequest(getChildren2, "/path1/path2/path3/path4/path5/path6"); for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 10000; j++) { requestPathMetricsCollector.registerRequest(getChildren, "/path1/path2/path3/path4/path5/path6" + "_" + i + "_" + j); } } }); path6.start(); for (int i = 0; i < 1; i++) { requestPathMetricsCollector.registerRequest(create2, "/path1"); } for (int i = 0; i < 10; i++) { requestPathMetricsCollector.registerRequest(create, "/path1/path2" + "_" + i); } for (int i = 0; i < 100; i++) { requestPathMetricsCollector.registerRequest(delete, "/path1/path2/path3" + "_" + i); } for (int i = 0; i < 1000; i++) { requestPathMetricsCollector.registerRequest(setData, "/path1/path2/path3/path4" + "_" + i); } for (int i = 0; i < 10000; i++) { requestPathMetricsCollector.registerRequest(exists, "/path1/path2/path3/path4/path5" + "_" + i); } path6.join(); path7.join(); Map newSlot = requestPathMetricsCollector.aggregatePaths(2, queue -> true); assertEquals(newSlot.size(), 12); assertEquals(newSlot.get("/path1").intValue(), 1); assertEquals(newSlot.get("/path1/path2").intValue(), 1111101); //cut up to 3 newSlot = requestPathMetricsCollector.aggregatePaths(3, queue -> true); assertEquals(newSlot.size(), 112); assertEquals(newSlot.get("/path1").intValue(), 1); assertEquals(newSlot.get("/path1/path2/path3").intValue(), 1111001); //cut up to 4 newSlot = requestPathMetricsCollector.aggregatePaths(4, queue -> true); assertEquals(newSlot.size(), 1112); assertEquals(newSlot.get("/path1/path2/path3/path4").intValue(), 1110001); //cut up to 5 newSlot = requestPathMetricsCollector.aggregatePaths(5, queue -> true); assertEquals(newSlot.size(), 11112); assertEquals(newSlot.get("/path1/path2/path3/path4/path5").intValue(), 1100001); //cut up to 6 newSlot = requestPathMetricsCollector.aggregatePaths(6, queue -> true); assertEquals(newSlot.size(), 111112); assertEquals(newSlot.get("/path1/path2/path3/path4/path5/path6").intValue(), 1000001); //cut up to 7 but the initial mapReduce kept only 6 newSlot = requestPathMetricsCollector.aggregatePaths(7, queue -> true); assertEquals(newSlot.size(), 111112); assertEquals(newSlot.get("/path1/path2/path3/path4/path5/path6").intValue(), 1000001); //test predicate //cut up to 4 for all the reads newSlot = requestPathMetricsCollector.aggregatePaths(4, queue -> !queue.isWriteOperation()); assertEquals(newSlot.size(), 1); assertEquals(newSlot.get("/path1/path2/path3/path4").intValue(), 1110001); //cut up to 4 for all the write newSlot = requestPathMetricsCollector.aggregatePaths(4, queue -> queue.isWriteOperation()); assertEquals(newSlot.size(), 1111); //cut up to 3 for all the write newSlot = requestPathMetricsCollector.aggregatePaths(3, queue -> queue.isWriteOperation()); assertEquals(newSlot.size(), 112); assertEquals(newSlot.get("/path1/path2/path3").intValue(), 1000); } @Test public void testTopPath() throws InterruptedException { RequestPathMetricsCollector requestPathMetricsCollector = new RequestPathMetricsCollector(true); Thread path7 = new Thread(() -> { for (int i = 0; i < 10; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 100000; j++) { requestPathMetricsCollector.registerRequest(getData, "/path1/path2/path3/path4/path5/path6/path7" + "_" + i + "_" + j); } } }); path7.start(); Thread path6 = new Thread(() -> { requestPathMetricsCollector.registerRequest(getChildren2, "/path1/path2/path3/path4/path5/path6"); for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } for (int j = 0; j < 10000; j++) { requestPathMetricsCollector.registerRequest(getChildren, "/path1/path2/path3/path4/path5/path6" + "_" + i + "_" + j); } } }); path6.start(); for (int i = 0; i < 1; i++) { requestPathMetricsCollector.registerRequest(create2, "/path1"); } for (int i = 0; i < 10; i++) { requestPathMetricsCollector.registerRequest(create, "/path1/path2" + "_" + i); } for (int i = 0; i < 100; i++) { requestPathMetricsCollector.registerRequest(delete, "/path1/path2/path3" + "_" + i); } for (int i = 0; i < 1000; i++) { requestPathMetricsCollector.registerRequest(setData, "/path1/path2/path3/path4" + "_" + i); } for (int i = 0; i < 10000; i++) { requestPathMetricsCollector.registerRequest(exists, "/path1/path2/path3/path4/path5" + "_" + i); } path6.join(); path7.join(); StringBuilder sb1 = new StringBuilder(); Map newSlot = requestPathMetricsCollector.aggregatePaths(3, queue -> queue.isWriteOperation()); requestPathMetricsCollector.logTopPaths(newSlot, entry -> sb1.append(entry.getKey() + " : " + entry.getValue() + "\n")); assertTrue(sb1.toString().startsWith("/path1/path2/path3 : 1000")); StringBuilder sb2 = new StringBuilder(); newSlot = requestPathMetricsCollector.aggregatePaths(3, queue -> !queue.isWriteOperation()); requestPathMetricsCollector.logTopPaths(newSlot, entry -> sb2.append(entry.getKey() + " : " + entry.getValue() + "\n")); assertTrue(sb2.toString().startsWith("/path1/path2/path3 : 1110001")); StringBuilder sb3 = new StringBuilder(); newSlot = requestPathMetricsCollector.aggregatePaths(4, queue -> true); requestPathMetricsCollector.logTopPaths(newSlot, entry -> sb3.append(entry.getKey() + " : " + entry.getValue() + "\n")); assertTrue(sb3.toString().startsWith("/path1/path2/path3/path4 : 1110001")); } @Test public void testMultiThreadPerf() throws InterruptedException { RequestPathMetricsCollector requestPathMetricsCollector = new RequestPathMetricsCollector(); Random rand = new Random(System.currentTimeMillis()); Long startTime = System.currentTimeMillis(); ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(); //call 100k get Data for (int i = 0; i < 100000; i++) { executor.submit( () -> requestPathMetricsCollector.registerRequest(getData, "/path1/path2/path" + rand.nextInt(10))); } //5K create for (int i = 0; i < 5000; i++) { executor.submit( () -> requestPathMetricsCollector.registerRequest(create2, "/path1/path2/path" + rand.nextInt(10))); } //5K delete for (int i = 0; i < 5000; i++) { executor.submit( () -> requestPathMetricsCollector.registerRequest(delete, "/path1/path2/path" + rand.nextInt(10))); } //40K getChildren for (int i = 0; i < 40000; i++) { executor.submit( () -> requestPathMetricsCollector.registerRequest(getChildren, "/path1/path2/path" + rand.nextInt(10))); } executor.shutdown(); //wait for at most 10 mill seconds executor.awaitTermination(10, TimeUnit.MILLISECONDS); assertTrue(executor.isTerminated()); Long endTime = System.currentTimeMillis(); //less than 2 seconds total time assertTrue(TimeUnit.MILLISECONDS.toSeconds(endTime - startTime) < 3); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000170 15051152474 032753 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/SerializeUtilsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/SerializeUtil0100644 0000000 0000000 00000011326 15051152474 034323 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.OutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; import org.mockito.InOrder; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; public class SerializeUtilsTest { @Test public void testSerializeRequestRequestHeaderIsNull() { Request request = new Request(0, 0, 0, null, null, 0); byte[] data = request.getSerializeData(); assertNull(data); } @Test public void testSerializeRequestWithoutTxn() throws IOException { // Arrange TxnHeader header = mock(TxnHeader.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputArchive oa = (OutputArchive) args[0]; oa.writeString("header", "test"); return null; } }).when(header).serialize(any(OutputArchive.class), anyString()); Request request = new Request(1, 2, 3, header, null, 4); // Act byte[] data = request.getSerializeData(); // Assert assertNotNull(data); verify(header).serialize(any(OutputArchive.class), eq("hdr")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeString("header", "test"); baos.close(); assertArrayEquals(baos.toByteArray(), data); } @Test public void testSerializeRequestWithTxn() throws IOException { // Arrange TxnHeader header = mock(TxnHeader.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputArchive oa = (OutputArchive) args[0]; oa.writeString("header", "test"); return null; } }).when(header).serialize(any(OutputArchive.class), anyString()); Record txn = mock(Record.class); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); OutputArchive oa = (OutputArchive) args[0]; oa.writeString("record", "test"); return null; } }).when(txn).serialize(any(OutputArchive.class), anyString()); Request request = new Request(1, 2, 3, header, txn, 4); // Act byte[] data = request.getSerializeData(); // Assert assertNotNull(data); InOrder inOrder = inOrder(header, txn); inOrder.verify(header).serialize(any(OutputArchive.class), eq("hdr")); inOrder.verify(txn).serialize(any(OutputArchive.class), eq("txn")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeString("header", "test"); boa.writeString("record", "test"); baos.close(); assertArrayEquals(baos.toByteArray(), data); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_ut0100644 0000000 0000000 00000000176 15051152474 032761 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/VerifyingFileFactoryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/util/VerifyingFile0100644 0000000 0000000 00000004602 15051152474 034277 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.server.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.File; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class VerifyingFileFactoryTest extends ZKTestCase { private Logger log; @BeforeEach public void setUp() { log = LoggerFactory.getLogger("TODO: Mock Logging"); } @Test public void testForWarningOnRelativePath() { VerifyingFileFactory vff = new VerifyingFileFactory.Builder(log).warnForRelativePath().build(); vff.create("a/relative/path"); // assertTrue(log.hasWarned); } @Test public void testForNoWarningOnIntendedRelativePath() { VerifyingFileFactory vff = new VerifyingFileFactory.Builder(log).warnForRelativePath().build(); vff.create("./an/intended/relative/path"); // assertFalse(log.hasWarned); } @Test public void testForFailForNonExistingPath() { assertThrows(IllegalArgumentException.class, () -> { VerifyingFileFactory vff = new VerifyingFileFactory.Builder(log).failForNonExistingPath().build(); vff.create("/I/H0p3/this/path/d035/n0t/ex15t"); }); } @Test public void testFileHasCorrectPath() { File file = new File("/some/path"); VerifyingFileFactory vff = new VerifyingFileFactory.Builder(log).build(); assertEquals(file, vff.create(file.getPath())); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000175 15051152474 032737 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/PathParentIteratorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/PathParentIt0100644 0000000 0000000 00000006357 15051152474 034242 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class PathParentIteratorTest { @Test public void testRoot() { PathParentIterator pathParentIterator = PathParentIterator.forAll("/"); assertTrue(pathParentIterator.hasNext()); assertFalse(pathParentIterator.atParentPath()); assertEquals(pathParentIterator.next(), "/"); assertFalse(pathParentIterator.hasNext()); } @Test public void test1Level() { PathParentIterator pathParentIterator = PathParentIterator.forAll("/a"); assertTrue(pathParentIterator.hasNext()); assertFalse(pathParentIterator.atParentPath()); assertEquals(pathParentIterator.next(), "/a"); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/"); assertTrue(pathParentIterator.atParentPath()); assertFalse(pathParentIterator.hasNext()); } @Test public void testLong() { PathParentIterator pathParentIterator = PathParentIterator.forAll("/a/b/c/d"); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/a/b/c/d"); assertFalse(pathParentIterator.atParentPath()); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/a/b/c"); assertTrue(pathParentIterator.atParentPath()); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/a/b"); assertTrue(pathParentIterator.atParentPath()); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/a"); assertTrue(pathParentIterator.atParentPath()); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/"); assertTrue(pathParentIterator.atParentPath()); assertFalse(pathParentIterator.hasNext()); } @Test public void testForPathOnly() { PathParentIterator pathParentIterator = PathParentIterator.forPathOnly("/a/b/c/d"); assertTrue(pathParentIterator.hasNext()); assertEquals(pathParentIterator.next(), "/a/b/c/d"); assertFalse(pathParentIterator.atParentPath()); assertFalse(pathParentIterator.hasNext()); } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000174 15051152474 032736 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/RecursiveWatchQtyTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/RecursiveWat0100644 0000000 0000000 00000015251 15051152474 034313 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class RecursiveWatchQtyTest { private WatchManager watchManager; private static final int clientQty = 25; private static final int iterations = 1000; private static class DummyWatcher implements Watcher { @Override public void process(WatchedEvent event) { // NOP } } @BeforeEach public void setup() { watchManager = new WatchManager(); } @Test public void testAddRemove() { Watcher watcher1 = new DummyWatcher(); Watcher watcher2 = new DummyWatcher(); watchManager.addWatch("/a", watcher1, WatcherMode.PERSISTENT_RECURSIVE); watchManager.addWatch("/b", watcher2, WatcherMode.PERSISTENT_RECURSIVE); assertEquals(2, watchManager.getRecursiveWatchQty()); assertTrue(watchManager.removeWatcher("/a", watcher1)); assertTrue(watchManager.removeWatcher("/b", watcher2)); assertEquals(0, watchManager.getRecursiveWatchQty()); } @Test public void testAddRemoveAlt() { Watcher watcher1 = new DummyWatcher(); Watcher watcher2 = new DummyWatcher(); watchManager.addWatch("/a", watcher1, WatcherMode.PERSISTENT_RECURSIVE); watchManager.addWatch("/b", watcher2, WatcherMode.PERSISTENT_RECURSIVE); assertEquals(2, watchManager.getRecursiveWatchQty()); watchManager.removeWatcher(watcher1); watchManager.removeWatcher(watcher2); assertEquals(0, watchManager.getRecursiveWatchQty()); } @Test public void testDoubleAdd() { Watcher watcher = new DummyWatcher(); watchManager.addWatch("/a", watcher, WatcherMode.PERSISTENT_RECURSIVE); watchManager.addWatch("/a", watcher, WatcherMode.PERSISTENT_RECURSIVE); assertEquals(1, watchManager.getRecursiveWatchQty()); watchManager.removeWatcher(watcher); assertEquals(0, watchManager.getRecursiveWatchQty()); } @Test public void testSameWatcherMultiPath() { Watcher watcher = new DummyWatcher(); watchManager.addWatch("/a", watcher, WatcherMode.PERSISTENT_RECURSIVE); watchManager.addWatch("/a/b", watcher, WatcherMode.PERSISTENT_RECURSIVE); watchManager.addWatch("/a/b/c", watcher, WatcherMode.PERSISTENT_RECURSIVE); assertEquals(3, watchManager.getRecursiveWatchQty()); assertTrue(watchManager.removeWatcher("/a/b", watcher)); assertEquals(2, watchManager.getRecursiveWatchQty()); watchManager.removeWatcher(watcher); assertEquals(0, watchManager.getRecursiveWatchQty()); } @Test public void testDifferentWatchModes() { Watcher watcher = new DummyWatcher(); watchManager.addWatch("/a", watcher, WatcherMode.PERSISTENT); assertEquals(0, watchManager.getRecursiveWatchQty()); watchManager.addWatch("/a", watcher, WatcherMode.PERSISTENT_RECURSIVE); assertEquals(1, watchManager.getRecursiveWatchQty()); watchManager.addWatch("/a", watcher, WatcherMode.STANDARD); assertEquals(1, watchManager.getRecursiveWatchQty()); assertTrue(watchManager.removeWatcher("/a", watcher)); assertEquals(0, watchManager.getRecursiveWatchQty()); } @Test public void testRecursiveQtyConcurrency() throws Exception { WatchManager manager = new WatchManager(); ExecutorService threadPool = Executors.newFixedThreadPool(clientQty); List> tasks = null; CountDownLatch completedLatch = new CountDownLatch(clientQty); try { tasks = IntStream.range(0, clientQty) .mapToObj(__ -> threadPool.submit(() -> iterate(manager, completedLatch))) .collect(Collectors.toList()); completedLatch.await(); } finally { if (tasks != null) { tasks.forEach(t -> t.cancel(true)); } threadPool.shutdownNow(); } int expectedRecursiveQty = (int) manager.getWatch2Paths().values() .stream() .flatMap(paths -> paths.values().stream()) .filter(stats -> stats.hasMode(WatcherMode.PERSISTENT_RECURSIVE)) .count(); assertEquals(expectedRecursiveQty, manager.getRecursiveWatchQty()); } private void iterate(WatchManager manager, CountDownLatch completedLatch) { ThreadLocalRandom random = ThreadLocalRandom.current(); try { for (int i = 0; i < iterations; ++i) { String path = "/" + random.nextInt(clientQty); boolean doSet = random.nextInt(100) > 33; // 2/3 will be sets if (doSet) { WatcherMode mode = WatcherMode.values()[random.nextInt(WatcherMode.values().length)]; manager.addWatch(path, new DummyWatcher(), mode); } else { manager.removeWatcher(path, new DummyWatcher()); } int sleepMillis = random.nextInt(2); if (sleepMillis > 0) { try { Thread.sleep(sleepMillis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } finally { completedLatch.countDown(); } } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000167 15051152474 032740 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchManagerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchManager0100644 0000000 0000000 00000107331 15051152474 034232 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.DumbWatcher; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerMetrics; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WatchManagerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(WatchManagerTest.class); private static final String PATH_PREFIX = "/path"; private ConcurrentHashMap watchers; private Random r; public static Stream data() { return Stream.of( Arguments.of(WatchManager.class.getName()), Arguments.of(WatchManagerOptimized.class.getName())); } @BeforeEach public void setUp() { ServerMetrics.getMetrics().resetAll(); watchers = new ConcurrentHashMap<>(); r = new Random(System.nanoTime()); } public IWatchManager getWatchManager(String className) throws IOException { System.setProperty(WatchManagerFactory.ZOOKEEPER_WATCH_MANAGER_NAME, className); return WatchManagerFactory.createWatchManager(); } public DumbWatcher createOrGetWatcher(int watcherId) { if (!watchers.containsKey(watcherId)) { DumbWatcher watcher = new DumbWatcher(watcherId); watchers.putIfAbsent(watcherId, watcher); } return watchers.get(watcherId); } public class AddWatcherWorker extends Thread { private final IWatchManager manager; private final int paths; private final int watchers; private final AtomicInteger watchesAdded; private volatile boolean stopped = false; public AddWatcherWorker( IWatchManager manager, int paths, int watchers, AtomicInteger watchesAdded) { this.manager = manager; this.paths = paths; this.watchers = watchers; this.watchesAdded = watchesAdded; } @Override public void run() { while (!stopped) { String path = PATH_PREFIX + r.nextInt(paths); Watcher watcher = createOrGetWatcher(r.nextInt(watchers)); if (manager.addWatch(path, watcher)) { watchesAdded.addAndGet(1); } } } public void shutdown() { stopped = true; } } public class WatcherTriggerWorker extends Thread { private final IWatchManager manager; private final int paths; private final AtomicInteger triggeredCount; private volatile boolean stopped = false; public WatcherTriggerWorker( IWatchManager manager, int paths, AtomicInteger triggeredCount) { this.manager = manager; this.paths = paths; this.triggeredCount = triggeredCount; } @Override public void run() { while (!stopped) { String path = PATH_PREFIX + r.nextInt(paths); WatcherOrBitSet s = manager.triggerWatch(path, EventType.NodeDeleted, -1, null); if (s != null) { triggeredCount.addAndGet(s.size()); } try { Thread.sleep(r.nextInt(10)); } catch (InterruptedException e) { } } } public void shutdown() { stopped = true; } } public class RemoveWatcherWorker extends Thread { private final IWatchManager manager; private final int paths; private final int watchers; private final AtomicInteger watchesRemoved; private volatile boolean stopped = false; public RemoveWatcherWorker( IWatchManager manager, int paths, int watchers, AtomicInteger watchesRemoved) { this.manager = manager; this.paths = paths; this.watchers = watchers; this.watchesRemoved = watchesRemoved; } @Override public void run() { while (!stopped) { String path = PATH_PREFIX + r.nextInt(paths); Watcher watcher = createOrGetWatcher(r.nextInt(watchers)); if (manager.removeWatcher(path, watcher)) { watchesRemoved.addAndGet(1); } try { Thread.sleep(r.nextInt(10)); } catch (InterruptedException e) { } } } public void shutdown() { stopped = true; } } public class CreateDeadWatchersWorker extends Thread { private final IWatchManager manager; private final int watchers; private final Set removedWatchers; private volatile boolean stopped = false; public CreateDeadWatchersWorker( IWatchManager manager, int watchers, Set removedWatchers) { this.manager = manager; this.watchers = watchers; this.removedWatchers = removedWatchers; } @Override public void run() { while (!stopped) { DumbWatcher watcher = createOrGetWatcher(r.nextInt(watchers)); watcher.setStale(); manager.removeWatcher(watcher); synchronized (removedWatchers) { removedWatchers.add(watcher); } try { Thread.sleep(r.nextInt(10)); } catch (InterruptedException e) { } } } public void shutdown() { stopped = true; } } /** * Concurrently add and trigger watch, make sure the watches triggered * are the same as the number added. */ @ParameterizedTest @MethodSource("data") @Timeout(value = 90) public void testAddAndTriggerWatcher(String className) throws IOException { IWatchManager manager = getWatchManager(className); int paths = 1; int watchers = 10000; // 1. start 5 workers to trigger watchers on that path // count all the watchers have been fired AtomicInteger watchTriggered = new AtomicInteger(); List triggerWorkers = new ArrayList<>(); for (int i = 0; i < 5; i++) { WatcherTriggerWorker worker = new WatcherTriggerWorker(manager, paths, watchTriggered); triggerWorkers.add(worker); worker.start(); } // 2. start 5 workers to add different watchers on the same path // count all the watchers being added AtomicInteger watchesAdded = new AtomicInteger(); List addWorkers = new ArrayList<>(); for (int i = 0; i < 5; i++) { AddWatcherWorker worker = new AddWatcherWorker(manager, paths, watchers, watchesAdded); addWorkers.add(worker); worker.start(); } while (watchesAdded.get() < 100000) { try { Thread.sleep(100); } catch (InterruptedException e) { } } // 3. stop all the addWorkers for (AddWatcherWorker worker : addWorkers) { worker.shutdown(); } // 4. running the trigger worker a bit longer to make sure // all watchers added are fired try { Thread.sleep(500); } catch (InterruptedException e) { } // 5. stop all triggerWorkers for (WatcherTriggerWorker worker : triggerWorkers) { worker.shutdown(); } // 6. make sure the total watch triggered is same as added assertTrue(watchesAdded.get() > 0); assertEquals(watchesAdded.get(), watchTriggered.get()); } /** * Concurrently add and remove watch, make sure the watches left + * the watches removed are equal to the total added watches. */ @ParameterizedTest @MethodSource("data") @Timeout(value = 90) public void testRemoveWatcherOnPath(String className) throws IOException { IWatchManager manager = getWatchManager(className); int paths = 10; int watchers = 10000; // 1. start 5 workers to remove watchers on those path // record the watchers have been removed AtomicInteger watchesRemoved = new AtomicInteger(); List removeWorkers = new ArrayList<>(); for (int i = 0; i < 5; i++) { RemoveWatcherWorker worker = new RemoveWatcherWorker(manager, paths, watchers, watchesRemoved); removeWorkers.add(worker); worker.start(); } // 2. start 5 workers to add different watchers on different path // record the watchers have been added AtomicInteger watchesAdded = new AtomicInteger(); List addWorkers = new ArrayList<>(); for (int i = 0; i < 5; i++) { AddWatcherWorker worker = new AddWatcherWorker(manager, paths, watchers, watchesAdded); addWorkers.add(worker); worker.start(); } while (watchesAdded.get() < 100000) { try { Thread.sleep(100); } catch (InterruptedException e) { } } // 3. stop all workers for (RemoveWatcherWorker worker : removeWorkers) { worker.shutdown(); } for (AddWatcherWorker worker : addWorkers) { worker.shutdown(); } // 4. sleep for a while to make sure all the thread exited try { Thread.sleep(500); } catch (InterruptedException e) { } // 5. make sure left watches + removed watches = added watches assertTrue(watchesAdded.get() > 0); assertTrue(watchesRemoved.get() > 0); assertTrue(manager.size() > 0); assertEquals(watchesAdded.get(), watchesRemoved.get() + manager.size()); } /** * Test add, contains and remove on generic watch manager. */ @ParameterizedTest @MethodSource("data") public void testAddRemoveWatcher(String className) throws IOException { IWatchManager manager = getWatchManager(className); Watcher watcher1 = new DumbWatcher(); Watcher watcher2 = new DumbWatcher(); // given: add watcher1 to "/node1" manager.addWatch("/node1", watcher1); // then: contains or remove should fail on mismatch path and watcher pair assertFalse(manager.containsWatcher("/node1", watcher2)); assertFalse(manager.containsWatcher("/node2", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher2)); assertFalse(manager.removeWatcher("/node2", watcher1)); // then: contains or remove should succeed on matching path and watcher pair assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.removeWatcher("/node1", watcher1)); // then: contains or remove should fail on removed path and watcher pair assertFalse(manager.containsWatcher("/node1", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher1)); } /** * Test containsWatcher on all pairs, and removeWatcher on mismatch pairs. */ @Test public void testContainsMode() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); Watcher watcher2 = new DumbWatcher(); // given: add watcher1 to "/node1" in persistent mode assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertNotEquals(WatcherMode.PERSISTENT, WatcherMode.DEFAULT_WATCHER_MODE); // then: contains should succeed on watcher1 to "/node1" in persistent and any mode assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.containsWatcher("/node1", watcher1, null)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); // then: contains and remove should fail on mismatch watcher assertFalse(manager.containsWatcher("/node1", watcher2)); assertFalse(manager.containsWatcher("/node1", watcher2, null)); assertFalse(manager.containsWatcher("/node1", watcher2, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher2, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node1", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher2)); assertFalse(manager.removeWatcher("/node1", watcher2, null)); assertFalse(manager.removeWatcher("/node1", watcher2, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher2, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); // then: contains and remove should fail on mismatch path assertFalse(manager.containsWatcher("/node2", watcher1)); assertFalse(manager.containsWatcher("/node2", watcher1, null)); assertFalse(manager.containsWatcher("/node2", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node2", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node2", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node2", watcher1)); assertFalse(manager.removeWatcher("/node2", watcher1, null)); assertFalse(manager.removeWatcher("/node2", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node2", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node2", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // then: contains and remove should fail on mismatch modes assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // when: add watcher1 to "/node1" in remaining modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // then: contains should succeed on watcher to "/node1" in all modes assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.containsWatcher("/node1", watcher1, null)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); } /** * Test repeatedly {@link WatchManager#addWatch(String, Watcher, WatcherMode)}. */ @Test public void testAddModeRepeatedly() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); // given: add watcher1 to "/node1" in all modes manager.addWatch("/node1", watcher1, WatcherMode.STANDARD); manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT); manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE); // when: add watcher1 to "/node1" in these modes repeatedly assertFalse(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // then: contains and remove should work normally on watcher1 to "/node1" assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.containsWatcher("/node1", watcher1, null)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.containsWatcher("/node1", watcher1, null)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.containsWatcher("/node1", watcher1, null)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.containsWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1, null)); assertFalse(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher1, null)); } /** * Test {@link WatchManager#removeWatcher(String, Watcher, WatcherMode)} on one pair should not break others. */ @Test public void testRemoveModeOne() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); Watcher watcher2 = new DumbWatcher(); // given: add watcher1 to "/node1" and watcher2 to "/node2" in all modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.addWatch("/node2", watcher2, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node2", watcher2, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node2", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); // when: remove one pair assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); // then: contains and remove should succeed on other pairs assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.containsWatcher("/node2", watcher2, WatcherMode.STANDARD)); assertTrue(manager.containsWatcher("/node2", watcher2, WatcherMode.PERSISTENT)); assertTrue(manager.containsWatcher("/node2", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.removeWatcher("/node2", watcher2, WatcherMode.STANDARD)); assertTrue(manager.removeWatcher("/node2", watcher2, WatcherMode.PERSISTENT)); assertTrue(manager.removeWatcher("/node2", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); } /** * Test {@link WatchManager#removeWatcher(String, Watcher, WatcherMode)} with {@code null} watcher mode. */ @Test public void testRemoveModeAll() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); // given: add watcher1 to "/node1" in all modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // when: remove watcher1 using null watcher mode assertTrue(manager.removeWatcher("/node1", watcher1, null)); // then: contains and remove should fail on watcher1 to "/node1" in all modes assertFalse(manager.containsWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1, null)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher1, null)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // given: add watcher1 to "/node1" in all modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // then: remove watcher1 without a mode should behave same to removing all modes assertTrue(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1, null)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher1, null)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); } /** * Test {@link WatchManager#removeWatcher(String, Watcher)}. */ @Test public void testRemoveModeAllDefault() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); // given: add watcher1 to "/node1" in all modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // then: remove watcher1 without a mode should behave same to removing all modes assertTrue(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1, null)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher1, null)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); } /** * Test {@link WatchManager#removeWatcher(String, Watcher, WatcherMode)} all modes individually. */ @Test public void testRemoveModeAllIndividually() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); // given: add watcher1 to "/node1" in all modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // when: remove all modes individually assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); // then: contains and remove should fail on watcher1 to "/node1" in all modes assertFalse(manager.containsWatcher("/node1", watcher1)); assertFalse(manager.containsWatcher("/node1", watcher1, null)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertFalse(manager.removeWatcher("/node1", watcher1)); assertFalse(manager.removeWatcher("/node1", watcher1, null)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); } /** * Test {@link WatchManager#removeWatcher(String, Watcher, WatcherMode)} on mismatch pair should break nothing. */ @Test public void testRemoveModeMismatch() { IWatchManager manager = new WatchManager(); Watcher watcher1 = new DumbWatcher(); Watcher watcher2 = new DumbWatcher(); // given: add watcher1 to "/node1" and watcher2 to "/node2" in all modes assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.addWatch("/node2", watcher2, WatcherMode.STANDARD)); assertTrue(manager.addWatch("/node2", watcher2, WatcherMode.PERSISTENT)); assertTrue(manager.addWatch("/node2", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); // when: remove mismatch path and watcher pairs assertFalse(manager.removeWatcher("/node1", watcher2)); assertFalse(manager.removeWatcher("/node1", watcher2, null)); assertFalse(manager.removeWatcher("/node1", watcher2, WatcherMode.STANDARD)); assertFalse(manager.removeWatcher("/node1", watcher2, WatcherMode.PERSISTENT)); assertFalse(manager.removeWatcher("/node1", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); // then: no existing watching pairs should break assertTrue(manager.containsWatcher("/node1", watcher1)); assertTrue(manager.containsWatcher("/node1", watcher1, null)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.STANDARD)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT)); assertTrue(manager.containsWatcher("/node1", watcher1, WatcherMode.PERSISTENT_RECURSIVE)); assertTrue(manager.containsWatcher("/node2", watcher2)); assertTrue(manager.containsWatcher("/node2", watcher2, null)); assertTrue(manager.containsWatcher("/node2", watcher2, WatcherMode.STANDARD)); assertTrue(manager.containsWatcher("/node2", watcher2, WatcherMode.PERSISTENT)); assertTrue(manager.containsWatcher("/node2", watcher2, WatcherMode.PERSISTENT_RECURSIVE)); } /** * Concurrently add watch while close the watcher to simulate the * client connections closed on prod. */ @ParameterizedTest @MethodSource("data") @Timeout(value = 90) public void testDeadWatchers(String className) throws IOException { System.setProperty("zookeeper.watcherCleanThreshold", "10"); System.setProperty("zookeeper.watcherCleanIntervalInSeconds", "1"); IWatchManager manager = getWatchManager(className); int paths = 1; int watchers = 100000; // 1. start 5 workers to randomly mark those watcher as dead // and remove them from watch manager Set deadWatchers = new HashSet<>(); List deadWorkers = new ArrayList<>(); for (int i = 0; i < 5; i++) { CreateDeadWatchersWorker worker = new CreateDeadWatchersWorker(manager, watchers, deadWatchers); deadWorkers.add(worker); worker.start(); } // 2. start 5 workers to add different watchers on the same path AtomicInteger watchesAdded = new AtomicInteger(); List addWorkers = new ArrayList<>(); for (int i = 0; i < 5; i++) { AddWatcherWorker worker = new AddWatcherWorker(manager, paths, watchers, watchesAdded); addWorkers.add(worker); worker.start(); } while (watchesAdded.get() < 50000) { try { Thread.sleep(100); } catch (InterruptedException e) { } } // 3. stop all workers for (CreateDeadWatchersWorker worker : deadWorkers) { worker.shutdown(); } for (AddWatcherWorker worker : addWorkers) { worker.shutdown(); } // 4. sleep for a while to make sure all the thread exited // the cleaner may wait as long as CleanerInterval+CleanerInterval/2+1 // So need to sleep as least that long try { Thread.sleep(2000); } catch (InterruptedException e) { } // 5. make sure the dead watchers are not in the existing watchers WatchesReport existingWatchers = manager.getWatches(); for (Watcher w : deadWatchers) { assertFalse(existingWatchers.hasPaths(((ServerCnxn) w).getSessionId())); } } private void checkMetrics(String metricName, long min, long max, double avg, long cnt, long sum) { Map values = MetricsUtils.currentServerMetrics(); assertEquals(min, values.get("min_" + metricName)); assertEquals(max, values.get("max_" + metricName)); assertEquals(avg, (Double) values.get("avg_" + metricName), 0.000001); assertEquals(cnt, values.get("cnt_" + metricName)); assertEquals(sum, values.get("sum_" + metricName)); } private void checkMostRecentWatchedEvent(DumbWatcher watcher, String path, EventType eventType, long zxid) { assertEquals(path, watcher.getMostRecentPath()); assertEquals(eventType, watcher.getMostRecentEventType()); assertEquals(zxid, watcher.getMostRecentZxid()); } @ParameterizedTest @MethodSource("data") public void testWatcherMetrics(String className) throws IOException { IWatchManager manager = getWatchManager(className); ServerMetrics.getMetrics().resetAll(); DumbWatcher watcher1 = new DumbWatcher(1); DumbWatcher watcher2 = new DumbWatcher(2); final String path1 = "/path1"; final String path2 = "/path2"; final String path3 = "/path3"; //both watcher1 and watcher2 are watching path1 manager.addWatch(path1, watcher1); manager.addWatch(path1, watcher2); //path2 is watched by watcher1 manager.addWatch(path2, watcher1); manager.triggerWatch(path3, EventType.NodeCreated, 1, null); //path3 is not being watched so metric is 0 checkMetrics("node_created_watch_count", 0L, 0L, 0D, 0L, 0L); // Watchers shouldn't have received any events yet so the zxid should be -1. checkMostRecentWatchedEvent(watcher1, null, null, -1); checkMostRecentWatchedEvent(watcher2, null, null, -1); //path1 is watched by two watchers so two fired manager.triggerWatch(path1, EventType.NodeCreated, 2, null); checkMetrics("node_created_watch_count", 2L, 2L, 2D, 1L, 2L); checkMostRecentWatchedEvent(watcher1, path1, EventType.NodeCreated, 2); checkMostRecentWatchedEvent(watcher2, path1, EventType.NodeCreated, 2); //path2 is watched by one watcher so one fired now total is 3 manager.triggerWatch(path2, EventType.NodeCreated, 3, null); checkMetrics("node_created_watch_count", 1L, 2L, 1.5D, 2L, 3L); checkMostRecentWatchedEvent(watcher1, path2, EventType.NodeCreated, 3); checkMostRecentWatchedEvent(watcher2, path1, EventType.NodeCreated, 2); //watches on path1 are no longer there so zero fired manager.triggerWatch(path1, EventType.NodeDataChanged, 4, null); checkMetrics("node_changed_watch_count", 0L, 0L, 0D, 0L, 0L); checkMostRecentWatchedEvent(watcher1, path2, EventType.NodeCreated, 3); checkMostRecentWatchedEvent(watcher2, path1, EventType.NodeCreated, 2); //both watcher and watcher are watching path1 manager.addWatch(path1, watcher1); manager.addWatch(path1, watcher2); //path2 is watched by watcher1 manager.addWatch(path2, watcher1); manager.triggerWatch(path1, EventType.NodeDataChanged, 5, null); checkMetrics("node_changed_watch_count", 2L, 2L, 2D, 1L, 2L); checkMostRecentWatchedEvent(watcher1, path1, EventType.NodeDataChanged, 5); checkMostRecentWatchedEvent(watcher2, path1, EventType.NodeDataChanged, 5); manager.triggerWatch(path2, EventType.NodeDeleted, 6, null); checkMetrics("node_deleted_watch_count", 1L, 1L, 1D, 1L, 1L); checkMostRecentWatchedEvent(watcher1, path2, EventType.NodeDeleted, 6); checkMostRecentWatchedEvent(watcher2, path1, EventType.NodeDataChanged, 5); //make sure that node created watch count is not impacted by the fire of other event types checkMetrics("node_created_watch_count", 1L, 2L, 1.5D, 2L, 3L); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000171 15051152474 032733 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatcherCleanerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatcherClean0100644 0000000 0000000 00000014737 15051152474 034240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerMetrics; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WatcherCleanerTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(WatcherCleanerTest.class); public static class MyDeadWatcherListener implements IDeadWatcherListener { private CountDownLatch latch; private int delayMs; private Set deadWatchers = new HashSet<>(); public void setCountDownLatch(CountDownLatch latch) { this.latch = latch; } public void setDelayMs(int delayMs) { this.delayMs = delayMs; } @Override public void processDeadWatchers(Set deadWatchers) { if (delayMs > 0) { try { Thread.sleep(delayMs); } catch (InterruptedException e) { } } this.deadWatchers.clear(); this.deadWatchers.addAll(deadWatchers); latch.countDown(); } public Set getDeadWatchers() { return deadWatchers; } public boolean wait(int maxWaitMs) { try { return latch.await(maxWaitMs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { } return false; } } @Test public void testProcessDeadWatchersBasedOnThreshold() { MyDeadWatcherListener listener = new MyDeadWatcherListener(); int threshold = 3; WatcherCleaner cleaner = new WatcherCleaner(listener, threshold, 60, 1, 10); cleaner.start(); int i = 0; while (i++ < threshold - 1) { cleaner.addDeadWatcher(i); } // not trigger processDeadWatchers yet assertEquals(0, listener.getDeadWatchers().size()); listener.setCountDownLatch(new CountDownLatch(1)); // add another dead watcher to trigger the process cleaner.addDeadWatcher(i); assertTrue(listener.wait(1000)); assertEquals(threshold, listener.getDeadWatchers().size()); } @Test public void testProcessDeadWatchersBasedOnTime() { MyDeadWatcherListener listener = new MyDeadWatcherListener(); WatcherCleaner cleaner = new WatcherCleaner(listener, 10, 1, 1, 10); cleaner.start(); cleaner.addDeadWatcher(1); // not trigger processDeadWatchers yet assertEquals(0, listener.getDeadWatchers().size()); listener.setCountDownLatch(new CountDownLatch(1)); assertTrue(listener.wait(2000)); assertEquals(1, listener.getDeadWatchers().size()); // won't trigger event if there is no dead watchers listener.setCountDownLatch(new CountDownLatch(1)); assertFalse(listener.wait(2000)); } @Test public void testMaxInProcessingDeadWatchers() { MyDeadWatcherListener listener = new MyDeadWatcherListener(); int delayMs = 1000; listener.setDelayMs(delayMs); WatcherCleaner cleaner = new WatcherCleaner(listener, 1, 60, 1, 1); cleaner.start(); listener.setCountDownLatch(new CountDownLatch(2)); long startTime = Time.currentElapsedTime(); cleaner.addDeadWatcher(1); cleaner.addDeadWatcher(2); long time = Time.currentElapsedTime() - startTime; System.out.println("time used " + time); assertTrue(Time.currentElapsedTime() - startTime >= delayMs); assertTrue(listener.wait(5000)); } @Test public void testDeadWatcherMetrics() throws InterruptedException { ServerMetrics.getMetrics().resetAll(); MyDeadWatcherListener listener = new MyDeadWatcherListener(); WatcherCleaner cleaner = new WatcherCleaner(listener, 1, 1, 1, 1); listener.setDelayMs(20); cleaner.start(); listener.setCountDownLatch(new CountDownLatch(3)); //the dead watchers will be added one by one and cleared one by one because we set both watchCleanThreshold and //maxInProcessingDeadWatchers to 1 cleaner.addDeadWatcher(1); cleaner.addDeadWatcher(2); cleaner.addDeadWatcher(3); assertTrue(listener.wait(5000)); Map values = MetricsUtils.currentServerMetrics(); // Adding dead watcher should be stalled twice waitForMetric("add_dead_watcher_stall_time", greaterThan(0L)); waitForMetric("dead_watchers_queued", is(3L)); waitForMetric("dead_watchers_cleared", is(3L)); waitForMetric("cnt_dead_watchers_cleaner_latency", is(3L)); //Each latency should be a little over 20 ms, allow 5 ms deviation waitForMetric("avg_dead_watchers_cleaner_latency", closeTo(20, 5)); waitForMetric("min_dead_watchers_cleaner_latency", closeTo(20, 5)); waitForMetric("max_dead_watchers_cleaner_latency", closeTo(20, 5)); waitForMetric("p50_dead_watchers_cleaner_latency", closeTo(20, 5)); waitForMetric("p95_dead_watchers_cleaner_latency", closeTo(20, 5)); waitForMetric("p99_dead_watchers_cleaner_latency", closeTo(20, 5)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000172 15051152474 032734 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatcherOrBitSetTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatcherOrBit0100644 0000000 0000000 00000004264 15051152474 034227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.Set; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.DumbWatcher; import org.apache.zookeeper.server.util.BitHashSet; import org.junit.jupiter.api.Test; public class WatcherOrBitSetTest extends ZKTestCase { @Test public void testWatcherSet() { Set wset = new HashSet<>(); WatcherOrBitSet hashSet = new WatcherOrBitSet(wset); assertEquals(0, hashSet.size()); DumbWatcher w1 = new DumbWatcher(); assertFalse(hashSet.contains(w1)); wset.add(w1); assertTrue(hashSet.contains(w1)); assertEquals(1, hashSet.size()); assertFalse(hashSet.contains(1)); } @Test public void testBitSet() { BitHashSet bset = new BitHashSet(0); WatcherOrBitSet bitSet = new WatcherOrBitSet(bset); assertEquals(0, bitSet.size()); Integer bit = 1; assertFalse(bitSet.contains(1)); assertFalse(bitSet.contains(bit)); bset.add(bit); assertTrue(bitSet.contains(1)); assertTrue(bitSet.contains(bit)); assertEquals(1, bitSet.size()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000174 15051152474 032736 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchesPathReportTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchesPathR0100644 0000000 0000000 00000004510 15051152474 034221 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class WatchesPathReportTest extends ZKTestCase { private Map> m; private WatchesPathReport r; @BeforeEach public void setUp() { m = new HashMap>(); Set s = new HashSet<>(); s.add(101L); s.add(102L); m.put("path1", s); s = new HashSet<>(); s.add(201L); m.put("path2", s); r = new WatchesPathReport(m); } @Test public void testHasSessions() { assertTrue(r.hasSessions("path1")); assertTrue(r.hasSessions("path2")); assertFalse(r.hasSessions("path3")); } @Test public void testGetSessions() { Set s = r.getSessions("path1"); assertEquals(2, s.size()); assertTrue(s.contains(101L)); assertTrue(s.contains(102L)); s = r.getSessions("path2"); assertEquals(1, s.size()); assertTrue(s.contains(201L)); assertNull(r.getSessions("path3")); } @Test public void testToMap() { assertEquals(m, r.toMap()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000170 15051152474 032732 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchesReportTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchesRepor0100644 0000000 0000000 00000004430 15051152474 034273 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class WatchesReportTest extends ZKTestCase { private Map> m; private WatchesReport r; @BeforeEach public void setUp() { m = new HashMap>(); Set s = new HashSet<>(); s.add("path1a"); s.add("path1b"); m.put(1L, s); s = new HashSet<>(); s.add("path2a"); m.put(2L, s); r = new WatchesReport(m); } @Test public void testHasPaths() { assertTrue(r.hasPaths(1L)); assertTrue(r.hasPaths(2L)); assertFalse(r.hasPaths(3L)); } @Test public void testGetPaths() { Set s = r.getPaths(1L); assertEquals(2, s.size()); assertTrue(s.contains("path1a")); assertTrue(s.contains("path1b")); s = r.getPaths(2L); assertEquals(1, s.size()); assertTrue(s.contains("path2a")); assertNull(r.getPaths(3L)); } @Test public void testToMap() { assertEquals(m, r.toMap()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_server_wa0100644 0000000 0000000 00000000171 15051152474 032733 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchesSummaryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/server/watch/WatchesSumma0100644 0000000 0000000 00000003366 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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.watch; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Map; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class WatchesSummaryTest extends ZKTestCase { private WatchesSummary s; @BeforeEach public void setUp() { s = new WatchesSummary(1, 2, 3); } @Test public void testGetters() { assertEquals(1, s.getNumConnections()); assertEquals(2, s.getNumPaths()); assertEquals(3, s.getTotalWatches()); } @Test public void testToMap() { Map m = s.toMap(); assertEquals(3, m.size()); assertEquals(Integer.valueOf(1), m.get(WatchesSummary.KEY_NUM_CONNECTIONS)); assertEquals(Integer.valueOf(2), m.get(WatchesSummary.KEY_NUM_PATHS)); assertEquals(Integer.valueOf(3), m.get(WatchesSummary.KEY_NUM_TOTAL_WATCHES)); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLCountTest.java0100644 0000000 0000000 00000010513 15051152474 033417 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ACLCountTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(ACLCountTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); /** * * Create a node and add 4 ACL values to it, but there are only 2 unique ACL values, * and each is repeated once: * * ACL(ZooDefs.Perms.READ,ZooDefs.Ids.ANYONE_ID_UNSAFE); * ACL(ZooDefs.Perms.ALL,ZooDefs.Ids.AUTH_IDS); * ACL(ZooDefs.Perms.READ,ZooDefs.Ids.ANYONE_ID_UNSAFE); * ACL(ZooDefs.Perms.ALL,ZooDefs.Ids.AUTH_IDS); * * Even though we've added 4 ACL values, there should only be 2 ACLs for that node, * since there are only 2 *unique* ACL values. */ @Test public void testAclCount() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(1000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); ZooKeeper zk; final ArrayList CREATOR_ALL_AND_WORLD_READABLE = new ArrayList() { { add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)); add(new ACL(ZooDefs.Perms.ALL, ZooDefs.Ids.AUTH_IDS)); add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)); add(new ACL(ZooDefs.Perms.ALL, ZooDefs.Ids.AUTH_IDS)); } }; try { LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); zk = ClientBase.createZKClient(HOSTPORT); zk.addAuthInfo("digest", "pat:test".getBytes()); zk.setACL("/", Ids.CREATOR_ALL_ACL, -1); String path = "/path"; try { assertEquals(4, CREATOR_ALL_AND_WORLD_READABLE.size()); } catch (Exception e) { LOG.error("Something is fundamentally wrong with ArrayList's add() method. add()ing four times to an empty ArrayList should result in an ArrayList with 4 members."); throw e; } zk.create(path, path.getBytes(), CREATOR_ALL_AND_WORLD_READABLE, CreateMode.PERSISTENT); List acls = zk.getACL("/path", new Stat()); assertEquals(2, acls.size()); } catch (Exception e) { // test failed somehow. assertTrue(false); } f.shutdown(); zks.shutdown(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLRootTest.java0100644 0000000 0000000 00000006667 15051152474 033271 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.Test; public class ACLRootTest extends ClientBase { @Test public void testRootAcl() throws Exception { ZooKeeper zk = createClient(); try { // set auth using digest zk.addAuthInfo("digest", "pat:test".getBytes()); zk.setACL("/", Ids.CREATOR_ALL_ACL, -1); zk.getData("/", false, null); zk.close(); // verify no access zk = createClient(); try { zk.getData("/", false, null); fail("validate auth"); } catch (KeeperException.NoAuthException e) { // expected } try { zk.create("/apps", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("validate auth"); } catch (KeeperException.InvalidACLException e) { // expected } zk.addAuthInfo("digest", "world:anyone".getBytes()); try { zk.create("/apps", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("validate auth"); } catch (KeeperException.NoAuthException e) { // expected } zk.close(); // verify access using original auth zk = createClient(); zk.addAuthInfo("digest", "pat:test".getBytes()); zk.getData("/", false, null); zk.create("/apps", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.delete("/apps", -1); // reset acl (back to open) and verify accessible again zk.setACL("/", Ids.OPEN_ACL_UNSAFE, -1); zk.close(); zk = createClient(); zk.getData("/", false, null); zk.create("/apps", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.create("/apps", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("validate auth"); } catch (KeeperException.InvalidACLException e) { // expected } zk.delete("/apps", -1); zk.addAuthInfo("digest", "world:anyone".getBytes()); zk.create("/apps", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.close(); zk = createClient(); zk.delete("/apps", -1); } finally { zk.close(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ACLTest.java0100644 0000000 0000000 00000062464 15051152474 032422 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.InvalidACLException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.Perms; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.auth.IPAuthenticationProvider; import org.apache.zookeeper.server.embedded.ZooKeeperServerEmbedded; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ACLTest extends ZKTestCase implements Watcher { private static final Logger LOG = LoggerFactory.getLogger(ACLTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private volatile CountDownLatch startSignal; @Test public void testIPAuthenticationIsValidCIDR() throws Exception { IPAuthenticationProvider prov = new IPAuthenticationProvider(); assertTrue(prov.isValid("127.0.0.1"), "testing no netmask"); assertTrue(prov.isValid("127.0.0.1/32"), "testing single ip netmask"); assertTrue(prov.isValid("127.0.0.1/0"), "testing lowest netmask possible"); assertFalse(prov.isValid("127.0.0.1/33"), "testing netmask too high"); assertFalse(prov.isValid("10.0.0.1/-1"), "testing netmask too low"); } @Test public void testIPAuthenticationIsValidIpv6CIDR() throws Exception { IPAuthenticationProvider prov = new IPAuthenticationProvider(); assertTrue(prov.isValid("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), "full address no netmask"); assertTrue(prov.isValid("2001:db8:85a3::8a2e:370:7334"), "compressed zeros"); assertTrue(prov.isValid("::1"), "loopback with compression"); assertTrue(prov.isValid("1::"), "Start with compression"); assertTrue(prov.isValid("2001:db8::/4"), "end with compression"); assertTrue(prov.isValid("0:0:0:0:0:0:0::/8"), "all zeros"); assertTrue(prov.isValid("2001:db8:85a3:0:0:0:0::/32"), "Explicit zeros"); assertTrue(prov.isValid("1234:5678:9abc:def0:1234:5678:9abc:def0"), "max hex value"); assertFalse(prov.isValid("2001:db8:85a3:0000:0000:8a2e:0370:7334:extra"), "too many address segments"); assertFalse(prov.isValid("2001:db8:85a3:0000:0000:8a2e:0370"), "too few address segments"); assertFalse(prov.isValid("2001:db8:85a3::8a2e::0370:7334"), "multiple '::' not valid"); assertFalse(prov.isValid("2001:db8:85a3:G::8a2e:0370:7334"), "Invalid hex character"); assertFalse(prov.isValid(""), "empty string"); assertFalse(prov.isValid("2001:db8:85a3:0:0:0:0:1:2"), "too many segments post compression"); assertFalse(prov.isValid("2001:db8:85a3::8a2e:0370:7334:"), "trailing colon"); assertFalse(prov.isValid(":2001:db8:85a3::8a2e:0370:7334"), "Leading colon"); assertFalse(prov.isValid("::FFFF:192.168.1.1"), "IPv4-mapped"); assertTrue(prov.isValid("2001:db8:1234::/64"), "IPv6 address for multiple clients"); } @Test public void testIPAuthenticationIsValidIpv6Mask() throws Exception { IPAuthenticationProvider prov = new IPAuthenticationProvider(); assertTrue(prov.matches("2001:db8:1234::", "2001:db8:1234::/64")); assertTrue(prov.matches("2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370::/2")); assertFalse(prov.matches("22001:db8:85a3:0:0:0:0::0", "2001:db8:85a3:0:0:0:0::/32")); assertFalse(prov.matches("2001:db8::/4", "2001:db8::/4")); } @Test public void testNettyIpAuthDefault() throws Exception { String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); ClientBase.setupTestEnv(); File tmpDir = ClientBase.createTmpDir(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(1000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); try { LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); ClientBase.createZKClient(HOSTPORT); for (ServerCnxn cnxn : f.getConnections()) { boolean foundID = false; for (Id id : cnxn.getAuthInfo()) { if (id.getScheme().equals("ip")) { foundID = true; break; } } assertTrue(foundID); } } finally { f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server down"); System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); } } @Test public void testAuthWithIPV6Server(@TempDir File tmpDir) throws Exception { Properties properties = new Properties(); properties.setProperty("clientPortAddress", "::1"); properties.setProperty("clientPort", "0"); ZooKeeperServerEmbedded server = ZooKeeperServerEmbedded.builder() .baseDir(tmpDir.toPath()) .configuration(properties) .build(); server.start(); String connectionString = server.getConnectionString(); String port = connectionString.substring(connectionString.lastIndexOf(':') + 1); String hostport = String.format("[::1]:%s", port); assertTrue(ClientBase.waitForServerUp(hostport, CONNECTION_TIMEOUT), "waiting for server being up"); // given: ipv6 client ZooKeeper zk = ClientBase.createZKClient(hostport); // when: invalid ipv6 network acl // then: InvalidACL assertThrows(KeeperException.InvalidACLException.class, () -> zk.create("/invalid-ipv6-network-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "::1/256"))), CreateMode.PERSISTENT)); // given: ipv4 network acl zk.create("/unmatched-ipv4-network-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "127.0.0.1/16"))), CreateMode.PERSISTENT); // when: access with v6 ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/unmatched-ipv4-network-acl", null, -1)); // given: prefix matched ipv4 acl zk.create("/prefix-matched-ipv4-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "0.0.0.1/16"))), CreateMode.PERSISTENT); // when: access with v6 ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/prefix-matched-ipv4-acl", null, -1)); // given: ipv6 with network acl zk.create("/ipv6-network-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "::1/64"))), CreateMode.PERSISTENT); // when: access with valid ip // then: ok zk.setData("/ipv6-network-acl", null, -1); // given: ipv6 acl zk.create("/ipv6-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "::1"))), CreateMode.PERSISTENT); // when: access with valid ip // then: ok zk.setData("/ipv6-acl", null, -1); // given: mismatched ipv6 with network acl zk.create("/mismatched-ipv6-network-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "0000:0001::/32"))), CreateMode.PERSISTENT); // when: access with invalid ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/mismatched-ipv6-network-acl", null, -1)); // given: mismatched ipv6 acl zk.create("/mismatched-ipv6-acl", null, Collections.singletonList(new ACL(Perms.ALL, new Id("ip", "::2"))), CreateMode.PERSISTENT); // when: access with invalid ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/mismatched-ipv6-acl", null, -1)); server.close(); } @Test public void testAuthWithIPV4Server(@TempDir File tmpDir) throws Exception { Properties properties = new Properties(); properties.setProperty("clientPortAddress", "127.0.0.1"); properties.setProperty("clientPort", "0"); ZooKeeperServerEmbedded server = ZooKeeperServerEmbedded.builder() .baseDir(tmpDir.toPath()) .configuration(properties) .build(); server.start(); assertTrue(ClientBase.waitForServerUp(server.getConnectionString(), CONNECTION_TIMEOUT), "waiting for server being up"); // given: ipv4 client ZooKeeper zk = ClientBase.createZKClient(server.getConnectionString()); // when: invalid ipv4 network acl // then: InvalidACL assertThrows(KeeperException.InvalidACLException.class, () -> zk.create("/invalid-ipv4-network-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "127.0.0.1/64"))), CreateMode.PERSISTENT)); // given: ipv6 acl zk.create("/mismatched-ipv6-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "::1"))), CreateMode.PERSISTENT); // when: access with v4 ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/mismatched-ipv6-acl", null, -1)); // given: prefix matched ipv6 network acl zk.create("/prefix-matched-ipv6-network-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "7f::/16"))), CreateMode.PERSISTENT); // when: access with v4 ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/prefix-matched-ipv6-network-acl", null, -1)); // given: ipv4 with network acl zk.create("/matched-ipv4-network-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "127.0.0.1/16"))), CreateMode.PERSISTENT); // when: access with valid ip // then: ok zk.setData("/matched-ipv4-network-acl", null, -1); // given: matched ipv4 acl zk.create("/matched-ipv4-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "127.0.0.1"))), CreateMode.PERSISTENT); // when: access with valid ip // then: ok zk.setData("/matched-ipv4-acl", null, -1); // given: mismatched ipv4 network acl zk.create("/mismatched-ipv4-network-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "192.168.0.2/16"))), CreateMode.PERSISTENT); // when: access with invalid ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/mismatched-ipv4-network-acl", null, -1)); // given: mismatched ipv4 acl zk.create("/mismatched-ipv4-acl", new byte[]{}, Arrays.asList(new ACL(Perms.ALL, new Id("ip", "127.0.0.2"))), CreateMode.PERSISTENT); // when: access with invalid ip // then: NoAuth assertThrows(KeeperException.NoAuthException.class, () -> zk.setData("/mismatched-ipv4-acl", null, -1)); server.close(); } @Test public void testDisconnectedAddAuth() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(1000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); try { LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { zk.addAuthInfo("digest", "pat:test".getBytes()); zk.setACL("/", Ids.CREATOR_ALL_ACL, -1); } finally { zk.close(); } } finally { f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } /** * Verify that acl optimization of storing just * a few acls and there references in the data * node is actually working. */ @Test public void testAcls() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(1000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); ZooKeeper zk; String path; try { LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); zk = ClientBase.createZKClient(HOSTPORT); LOG.info("starting creating acls"); for (int i = 0; i < 100; i++) { path = "/" + i; zk.create(path, path.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } int size = zks.getZKDatabase().getAclSize(); assertTrue((2 == zks.getZKDatabase().getAclSize()), "size of the acl map "); for (int j = 100; j < 200; j++) { path = "/" + j; ACL acl = new ACL(); acl.setPerms(0); Id id = new Id(); id.setId("1.1.1." + j); id.setScheme("ip"); acl.setId(id); List list = new ArrayList<>(); list.add(acl); zk.create(path, path.getBytes(), list, CreateMode.PERSISTENT); } assertTrue((102 == zks.getZKDatabase().getAclSize()), "size of the acl map "); } finally { // now shutdown the server and restart it f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server down"); } zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); try { assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server up"); zk = ClientBase.createZKClient(HOSTPORT); assertTrue((102 == zks.getZKDatabase().getAclSize()), "acl map "); for (int j = 200; j < 205; j++) { path = "/" + j; ACL acl = new ACL(); acl.setPerms(0); Id id = new Id(); id.setId("1.1.1." + j); id.setScheme("ip"); acl.setId(id); ArrayList list = new ArrayList<>(); list.add(acl); zk.create(path, path.getBytes(), list, CreateMode.PERSISTENT); } assertTrue((107 == zks.getZKDatabase().getAclSize()), "acl map "); zk.close(); } finally { f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } /* * (non-Javadoc) * * @see org.apache.zookeeper.Watcher#process(org.apache.zookeeper.WatcherEvent) */ public void process(WatchedEvent event) { LOG.info("Event:{} {} {}", event.getState(), event.getType(), event.getPath()); if (event.getState() == KeeperState.SyncConnected) { if (startSignal != null && startSignal.getCount() > 0) { LOG.info("startsignal.countDown()"); startSignal.countDown(); } else { LOG.warn("startsignal {}", startSignal); } } } @Test public void testNullACL() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { // case 1 : null ACL with create try { zk.create("/foo", "foo".getBytes(), null, CreateMode.PERSISTENT); fail("Expected InvalidACLException for null ACL parameter"); } catch (InvalidACLException e) { // Expected. Do nothing } // case 2 : null ACL with other create API try { zk.create("/foo", "foo".getBytes(), null, CreateMode.PERSISTENT, null); fail("Expected InvalidACLException for null ACL parameter"); } catch (InvalidACLException e) { // Expected. Do nothing } // case 3 : null ACL with setACL try { zk.setACL("/foo", null, 0); fail("Expected InvalidACLException for null ACL parameter"); } catch (InvalidACLException e) { // Expected. Do nothing } } finally { zk.close(); f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } @Test public void testNullValueACL() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { List acls = new ArrayList<>(); acls.add(null); // case 1 : null value in ACL list with create try { zk.create("/foo", "foo".getBytes(), acls, CreateMode.PERSISTENT); fail("Expected InvalidACLException for null value in ACL List"); } catch (InvalidACLException e) { // Expected. Do nothing } // case 2 : null value in ACL list with other create API try { zk.create("/foo", "foo".getBytes(), acls, CreateMode.PERSISTENT, null); fail("Expected InvalidACLException for null value in ACL List"); } catch (InvalidACLException e) { // Expected. Do nothing } // case 3 : null value in ACL list with setACL try { zk.setACL("/foo", acls, -1); fail("Expected InvalidACLException for null value in ACL List"); } catch (InvalidACLException e) { // Expected. Do nothing } } finally { zk.close(); f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } @Test public void testExistACLCheck() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); String path = "/testExistACLCheck"; String data = "/testExistACLCheck-data"; try { LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { Stat stat = zk.exists(path, false); assertNull(stat); zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); stat = zk.exists(path, false); assertNotNull(stat); assertEquals(data.length(), stat.getDataLength()); zk.delete(path, -1); ArrayList acls = new ArrayList<>(); acls.add(new ACL(ZooDefs.Perms.WRITE, Ids.ANYONE_ID_UNSAFE)); zk.create(path, data.getBytes(), acls, CreateMode.PERSISTENT); try { stat = zk.exists(path, false); fail("exists should throw NoAuthException when don't have read permission"); } catch (KeeperException.NoAuthException e) { //expected } zk.delete(path, -1); acls = new ArrayList<>(); acls.add(new ACL(ZooDefs.Perms.READ, Ids.ANYONE_ID_UNSAFE)); zk.create(path, data.getBytes(), acls, CreateMode.PERSISTENT); stat = zk.exists(path, false); assertNotNull(stat); assertEquals(data.length(), stat.getDataLength()); } finally { zk.close(); } } finally { f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } @Test public void testExistACLCheckAtRootPath() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); try { LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { String data = "/testExistACLCheckAtRootPath-data"; zk.create("/a", data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); ArrayList acls = new ArrayList<>(); acls.add(new ACL(0, Ids.ANYONE_ID_UNSAFE)); zk.setACL("/", acls, -1); Stat stat = zk.exists("/a", false); assertNotNull(stat); assertEquals(data.length(), stat.getDataLength()); try { stat = zk.exists("/", false); fail("exists should throw NoAuthException when removing root path's ACL permission"); } catch (KeeperException.NoAuthException e) { //expected } } finally { zk.close(); } } finally { f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Asyn0100644 0000000 0000000 00000000156 15051152474 032712 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncHammerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncHammerTest.java0100644 0000000 0000000 00000020270 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.apache.zookeeper.test.ClientBase.verifyThreadTerminated; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.LinkedList; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AsyncHammerTest extends ZKTestCase implements StringCallback, VoidCallback, DataCallback { private static final Logger LOG = LoggerFactory.getLogger(AsyncHammerTest.class); private QuorumBase qb = new QuorumBase(); private volatile boolean bang; public void setUp(boolean withObservers) throws Exception { qb.setUp(withObservers, false); } protected void restart() throws Exception { LOG.info("RESTARTING {}", getTestName()); qb.tearDown(); // don't call setup - we don't want to reassign ports/dirs, etc... JMXEnv.setUp(); qb.startServers(); } public void tearDown() throws Exception { LOG.info("Test clients shutting down"); qb.tearDown(); } /** * Create /test- sequence nodes asynchronously, max 30 outstanding */ class HammerThread extends Thread implements StringCallback, VoidCallback { private static final int MAX_OUTSTANDING = 30; private TestableZooKeeper zk; private int outstanding; private volatile boolean failed = false; public HammerThread(String name) { super(name); } public void run() { try { CountdownWatcher watcher = new CountdownWatcher(); zk = new TestableZooKeeper(qb.hostPort, CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); while (bang) { incOutstanding(); // before create otw race zk.create("/test-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL, this, null); } } catch (InterruptedException e) { if (bang) { LOG.error("sanity check failed!!!"); // sanity check return; } } catch (Exception e) { LOG.error("Client create operation failed", e); return; } finally { if (zk != null) { try { if (!zk.close(CONNECTION_TIMEOUT)) { failed = true; LOG.error("Client did not shutdown"); } } catch (InterruptedException e) { LOG.info("Interrupted", e); } } } } private synchronized void incOutstanding() throws InterruptedException { outstanding++; while (outstanding > MAX_OUTSTANDING) { wait(); } } private synchronized void decOutstanding() { outstanding--; assertTrue(outstanding >= 0, "outstanding >= 0"); notifyAll(); } public void process(WatchedEvent event) { // ignore for purposes of this test } public void processResult(int rc, String path, Object ctx, String name) { if (rc != KeeperException.Code.OK.intValue()) { if (bang) { failed = true; LOG.error( "Create failed for 0x{} with rc:{} path:{}", Long.toHexString(zk.getSessionId()), rc, path); } decOutstanding(); return; } try { decOutstanding(); zk.delete(name, -1, this, null); } catch (Exception e) { if (bang) { failed = true; LOG.error("Client delete failed", e); } } } public void processResult(int rc, String path, Object ctx) { if (rc != KeeperException.Code.OK.intValue()) { if (bang) { failed = true; LOG.error( "Delete failed for 0x{} with rc:{} path:{}", Long.toHexString(zk.getSessionId()), rc, path); } } } } @Test public void testHammer() throws Exception { setUp(false); bang = true; LOG.info("Starting hammers"); HammerThread[] hammers = new HammerThread[100]; for (int i = 0; i < hammers.length; i++) { hammers[i] = new HammerThread("HammerThread-" + i); hammers[i].start(); } LOG.info("Started hammers"); Thread.sleep(5000); // allow the clients to run for max 5sec bang = false; LOG.info("Stopping hammers"); for (int i = 0; i < hammers.length; i++) { hammers[i].interrupt(); verifyThreadTerminated(hammers[i], 60000); assertFalse(hammers[i].failed); } // before restart LOG.info("Hammers stopped, verifying consistency"); qb.verifyRootOfAllServersMatch(qb.hostPort); restart(); // after restart LOG.info("Verifying hammers 2"); qb.verifyRootOfAllServersMatch(qb.hostPort); tearDown(); } @Test public void testObserversHammer() throws Exception { setUp(true); bang = true; Thread[] hammers = new Thread[100]; for (int i = 0; i < hammers.length; i++) { hammers[i] = new HammerThread("HammerThread-" + i); hammers[i].start(); } Thread.sleep(5000); // allow the clients to run for max 5sec bang = false; for (int i = 0; i < hammers.length; i++) { hammers[i].interrupt(); verifyThreadTerminated(hammers[i], 60000); } // before restart qb.verifyRootOfAllServersMatch(qb.hostPort); tearDown(); } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, String name) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOps.java0100644 0000000 0000000 00000062534 15051152474 032720 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback.ACLCallback; import org.apache.zookeeper.AsyncCallback.Children2Callback; import org.apache.zookeeper.AsyncCallback.ChildrenCallback; import org.apache.zookeeper.AsyncCallback.Create2Callback; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; public class AsyncOps { /** * This is the base class for all of the async callback classes. It will * verify the expected value against the actual value. * * Basic operation is that the subclasses will generate an "expected" value * which is defined by the "toString" method of the subclass. This is * passed through to the verify clause by specifying it as the ctx object * of each async call (processResult methods get the ctx as part of * the callback). Additionally the callback will also overwrite any * instance fields with matching parameter arguments to the processResult * method. The cb instance can then compare the expected to the * actual value by again calling toString and comparing the two. * * The format of each expected value differs (is defined) by subclass. * Generally the expected value starts with the result code (rc) and path * of the node being operated on, followed by the fields specific to * each operation type (cb subclass). For example ChildrenCB specifies * a list of the expected children suffixed onto the rc and path. See * the toString() method of each subclass for details of it's format. */ public abstract static class AsyncCB { protected final ZooKeeper zk; protected long defaultTimeoutMillis = 30000; /** the latch is used to await the results from the server */ CountDownLatch latch; Code rc = Code.OK; String path = "/foo"; String expected; public AsyncCB(ZooKeeper zk, CountDownLatch latch) { this.zk = zk; this.latch = latch; } public void setRC(Code rc) { this.rc = rc; } public void setPath(String path) { this.path = path; } public void processResult(Code rc, String path, Object ctx) { this.rc = rc; this.path = path; this.expected = (String) ctx; latch.countDown(); } /** String format is rc:path:<suffix> where <suffix> is defined by each * subclass individually. */ @Override public String toString() { return rc + ":" + path + ":"; } protected void verify() { try { latch.await(defaultTimeoutMillis, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail("unexpected interrupt"); } // on the lookout for timeout assertSame(0L, latch.getCount()); String actual = toString(); assertEquals(expected, actual); } } public static class StringCB extends AsyncCB implements StringCallback { byte[] data = new byte[10]; List acl = Ids.CREATOR_ALL_ACL; CreateMode flags = CreateMode.PERSISTENT; String name = path; StringCB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } StringCB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); } public void setPath(String path) { super.setPath(path); this.name = path; } public String nodeName() { return path.substring(path.lastIndexOf('/') + 1); } public void processResult(int rc, String path, Object ctx, String name) { this.name = name; super.processResult(Code.get(rc), path, ctx); } public AsyncCB create() { zk.create(path, data, acl, flags, this, toString()); return this; } public AsyncCB createEphemeral() { zk.create(path, data, acl, CreateMode.EPHEMERAL, this, toString()); return this; } public void verifyCreate() { create(); verify(); } public void verifyCreateEphemeral() { createEphemeral(); verify(); } public void verifyCreateFailure_NodeExists() { new StringCB(zk).verifyCreate(); rc = Code.NODEEXISTS; name = null; zk.create(path, data, acl, flags, this, toString()); verify(); } public void verifyCreateFailure_NoNode() { rc = Code.NONODE; name = null; path = path + "/bar"; zk.create(path, data, acl, flags, this, toString()); verify(); } public void verifyCreateFailure_NoChildForEphemeral() { new StringCB(zk).verifyCreateEphemeral(); rc = Code.NOCHILDRENFOREPHEMERALS; name = null; path = path + "/bar"; zk.create(path, data, acl, flags, this, toString()); verify(); } @Override public String toString() { return super.toString() + name; } } public static class ACLCB extends AsyncCB implements ACLCallback { List acl = Ids.CREATOR_ALL_ACL; int version = 0; Stat stat = new Stat(); byte[] data = "testing".getBytes(); ACLCB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } ACLCB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); stat.setAversion(0); stat.setCversion(0); stat.setEphemeralOwner(0); stat.setVersion(0); } public void processResult(int rc, String path, Object ctx, List acl, Stat stat) { this.acl = acl; this.stat = stat; super.processResult(Code.get(rc), path, ctx); } public void verifyGetACL() { new StringCB(zk).verifyCreate(); zk.getACL(path, stat, this, toString()); verify(); } public void verifyGetACLFailure_NoNode() { rc = Code.NONODE; stat = null; acl = null; zk.getACL(path, stat, this, toString()); verify(); } public String toString(List acls) { if (acls == null) { return ""; } StringBuilder result = new StringBuilder(); for (ACL acl : acls) { result.append(acl.getPerms()).append("::"); } return result.toString(); } @Override public String toString() { return super.toString() + toString(acl) + ":" + ":" + version + ":" + new String(data) + ":" + (stat == null ? "null" : stat.getAversion() + ":" + stat.getCversion() + ":" + stat.getEphemeralOwner() + ":" + stat.getVersion()); } } public static class ChildrenCB extends AsyncCB implements ChildrenCallback { List children = new ArrayList<>(); ChildrenCB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } ChildrenCB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); } public void processResult(int rc, String path, Object ctx, List children) { this.children = (children == null ? new ArrayList() : children); Collections.sort(this.children); super.processResult(Code.get(rc), path, ctx); } public StringCB createNode() { StringCB parent = new StringCB(zk); parent.verifyCreate(); return parent; } public StringCB createNode(StringCB parent) { String childName = "bar"; return createNode(parent, childName); } public StringCB createNode(StringCB parent, String childName) { StringCB child = new StringCB(zk); child.setPath(parent.path + "/" + childName); child.verifyCreate(); return child; } public void verifyGetChildrenEmpty() { StringCB parent = createNode(); path = parent.path; verify(); } public void verifyGetChildrenSingle() { StringCB parent = createNode(); StringCB child = createNode(parent); path = parent.path; children.add(child.nodeName()); verify(); } public void verifyGetChildrenTwo() { StringCB parent = createNode(); StringCB child1 = createNode(parent, "child1"); StringCB child2 = createNode(parent, "child2"); path = parent.path; children.add(child1.nodeName()); children.add(child2.nodeName()); verify(); } public void verifyGetChildrenFailure_NoNode() { rc = KeeperException.Code.NONODE; verify(); } @Override public void verify() { zk.getChildren(path, false, this, toString()); super.verify(); } @Override public String toString() { return super.toString() + children.toString(); } } public static class Children2CB extends AsyncCB implements Children2Callback { List children = new ArrayList<>(); Children2CB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } Children2CB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); } public void processResult(int rc, String path, Object ctx, List children, Stat stat) { this.children = (children == null ? new ArrayList() : children); Collections.sort(this.children); super.processResult(Code.get(rc), path, ctx); } public StringCB createNode() { StringCB parent = new StringCB(zk); parent.verifyCreate(); return parent; } public StringCB createNode(StringCB parent) { String childName = "bar"; return createNode(parent, childName); } public StringCB createNode(StringCB parent, String childName) { StringCB child = new StringCB(zk); child.setPath(parent.path + "/" + childName); child.verifyCreate(); return child; } public void verifyGetChildrenEmpty() { StringCB parent = createNode(); path = parent.path; verify(); } public void verifyGetChildrenSingle() { StringCB parent = createNode(); StringCB child = createNode(parent); path = parent.path; children.add(child.nodeName()); verify(); } public void verifyGetChildrenTwo() { StringCB parent = createNode(); StringCB child1 = createNode(parent, "child1"); StringCB child2 = createNode(parent, "child2"); path = parent.path; children.add(child1.nodeName()); children.add(child2.nodeName()); verify(); } public void verifyGetChildrenFailure_NoNode() { rc = KeeperException.Code.NONODE; verify(); } @Override public void verify() { zk.getChildren(path, false, this, toString()); super.verify(); } @Override public String toString() { return super.toString() + children.toString(); } } public static class Create2CB extends AsyncCB implements Create2Callback { byte[] data = new byte[10]; List acl = Ids.CREATOR_ALL_ACL; CreateMode flags = CreateMode.PERSISTENT; String name = path; Stat stat = new Stat(); Create2CB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } Create2CB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); } public void setPath(String path) { super.setPath(path); this.name = path; } public String nodeName() { return path.substring(path.lastIndexOf('/') + 1); } public void processResult(int rc, String path, Object ctx, String name, Stat stat) { this.name = name; this.stat = stat; super.processResult(Code.get(rc), path, ctx); } public AsyncCB create() { zk.create(path, data, acl, flags, this, toString()); return this; } public void verifyCreate() { create(); verify(); } public void verifyCreateFailure_NodeExists() { new Create2CB(zk).verifyCreate(); rc = Code.NODEEXISTS; name = null; stat = null; zk.create(path, data, acl, flags, this, toString()); verify(); } public void verifyCreateFailure_NoNode() { rc = Code.NONODE; name = null; stat = null; path = path + "/bar"; zk.create(path, data, acl, flags, this, toString()); verify(); } public void verifyCreateFailure_NoChildForEphemeral() { new StringCB(zk).verifyCreateEphemeral(); rc = Code.NOCHILDRENFOREPHEMERALS; name = null; stat = null; path = path + "/bar"; zk.create(path, data, acl, flags, this, toString()); verify(); } @Override public String toString() { return super.toString() + name + ":" + (stat == null ? "null" : stat.getAversion() + ":" + stat.getCversion() + ":" + stat.getEphemeralOwner() + ":" + stat.getVersion()); } } public static class DataCB extends AsyncCB implements DataCallback { byte[] data = new byte[10]; Stat stat = new Stat(); DataCB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } DataCB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); stat.setAversion(0); stat.setCversion(0); stat.setEphemeralOwner(0); stat.setVersion(0); } public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { this.data = data; this.stat = stat; super.processResult(Code.get(rc), path, ctx); } public void verifyGetData() { new StringCB(zk).verifyCreate(); zk.getData(path, false, this, toString()); verify(); } public void verifyGetDataFailure_NoNode() { rc = KeeperException.Code.NONODE; data = null; stat = null; zk.getData(path, false, this, toString()); verify(); } @Override public String toString() { return super.toString() + ":" + (data == null ? "null" : new String(data)) + ":" + (stat == null ? "null" : stat.getAversion() + ":" + stat.getCversion() + ":" + stat.getEphemeralOwner() + ":" + stat.getVersion()); } } public static class StatCB extends AsyncCB implements StatCallback { List acl = Ids.CREATOR_ALL_ACL; int version = 0; Stat stat = new Stat(); byte[] data = "testing".getBytes(); StatCB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } StatCB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); stat.setAversion(0); stat.setCversion(0); stat.setEphemeralOwner(0); stat.setVersion(0); } public void processResult(int rc, String path, Object ctx, Stat stat) { this.stat = stat; super.processResult(Code.get(rc), path, ctx); } public void verifySetACL() { stat.setAversion(1); new StringCB(zk).verifyCreate(); zk.setACL(path, acl, version, this, toString()); verify(); } public void verifySetACLFailure_NoNode() { rc = KeeperException.Code.NONODE; stat = null; zk.setACL(path, acl, version, this, toString()); verify(); } public void verifySetACLFailure_BadVersion() { new StringCB(zk).verifyCreate(); rc = Code.BADVERSION; stat = null; zk.setACL(path, acl, version + 1, this, toString()); verify(); } public void setData() { zk.setData(path, data, version, this, toString()); } public void verifySetData() { stat.setVersion(1); new StringCB(zk).verifyCreate(); setData(); verify(); } public void verifySetDataFailure_NoNode() { rc = KeeperException.Code.NONODE; stat = null; zk.setData(path, data, version, this, toString()); verify(); } public void verifySetDataFailure_BadVersion() { new StringCB(zk).verifyCreate(); rc = Code.BADVERSION; stat = null; zk.setData(path, data, version + 1, this, toString()); verify(); } public void verifyExists() { new StringCB(zk).verifyCreate(); zk.exists(path, false, this, toString()); verify(); } public void verifyExistsFailure_NoNode() { rc = KeeperException.Code.NONODE; stat = null; zk.exists(path, false, this, toString()); verify(); } @Override public String toString() { return super.toString() + version + ":" + new String(data) + ":" + (stat == null ? "null" : stat.getAversion() + ":" + stat.getCversion() + ":" + stat.getEphemeralOwner() + ":" + stat.getVersion()); } } public static class VoidCB extends AsyncCB implements VoidCallback { int version = 0; VoidCB(ZooKeeper zk) { this(zk, new CountDownLatch(1)); } VoidCB(ZooKeeper zk, CountDownLatch latch) { super(zk, latch); } public void processResult(int rc, String path, Object ctx) { super.processResult(Code.get(rc), path, ctx); } public void delete() { zk.delete(path, version, this, toString()); } public void verifyDelete() { new StringCB(zk).verifyCreate(); delete(); verify(); } public void verifyDeleteFailure_NoNode() { rc = Code.NONODE; zk.delete(path, version, this, toString()); verify(); } public void verifyDeleteFailure_BadVersion() { new StringCB(zk).verifyCreate(); rc = Code.BADVERSION; zk.delete(path, version + 1, this, toString()); verify(); } public void verifyDeleteFailure_NotEmpty() { StringCB scb = new StringCB(zk); scb.create(); scb.setPath(path + "/bar"); scb.create(); rc = Code.NOTEMPTY; zk.delete(path, version, this, toString()); verify(); } public void sync() { zk.sync(path, this, toString()); } public void verifySync() { sync(); verify(); } @Override public String toString() { return super.toString() + version; } } public static class MultiCB implements MultiCallback { ZooKeeper zk; int rc; List opResults; final CountDownLatch latch = new CountDownLatch(1); MultiCB(ZooKeeper zk) { this.zk = zk; } public void processResult(int rc, String path, Object ctx, List opResults) { this.rc = rc; this.opResults = opResults; latch.countDown(); } void latch_await() { try { latch.await(10000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { fail("unexpected interrupt"); } assertSame(0L, latch.getCount()); } public void verifyMulti() { List ops = Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", -1)); zk.multi(ops, this, null); latch_await(); assertEquals(this.rc, KeeperException.Code.OK.intValue()); assertTrue(this.opResults.get(0) instanceof OpResult.CreateResult); assertTrue(this.opResults.get(1) instanceof OpResult.DeleteResult); } public void verifyMultiFailure_AllErrorResult() { List ops = Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/nonexist1", -1), Op.setData("/multi", "test".getBytes(), -1)); zk.multi(ops, this, null); latch_await(); assertTrue(this.opResults.get(0) instanceof OpResult.ErrorResult); assertTrue(this.opResults.get(1) instanceof OpResult.ErrorResult); assertTrue(this.opResults.get(2) instanceof OpResult.ErrorResult); } public void verifyMultiFailure_NoSideEffect() throws KeeperException, InterruptedException { List ops = Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/nonexist1", -1)); zk.multi(ops, this, null); latch_await(); assertTrue(this.opResults.get(0) instanceof OpResult.ErrorResult); assertNull(zk.exists("/multi", false)); } public void verifyMultiSequential_NoSideEffect() throws Exception { StringCB scb = new StringCB(zk); scb.verifyCreate(); String path = scb.path + "-"; String seqPath = path + "0000000002"; zk.create(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); assertNotNull(zk.exists(path + "0000000001", false)); List ops = Arrays.asList( Op.create(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL), Op.delete("/nonexist", -1)); zk.multi(ops, this, null); latch_await(); assertNull(zk.exists(seqPath, false)); zk.create(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); assertNotNull(zk.exists(seqPath, false)); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncOpsTest.java0100644 0000000 0000000 00000015525 15051152474 033556 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.AsyncOps.ACLCB; import org.apache.zookeeper.test.AsyncOps.Children2CB; import org.apache.zookeeper.test.AsyncOps.ChildrenCB; import org.apache.zookeeper.test.AsyncOps.Create2CB; import org.apache.zookeeper.test.AsyncOps.DataCB; import org.apache.zookeeper.test.AsyncOps.MultiCB; import org.apache.zookeeper.test.AsyncOps.StatCB; import org.apache.zookeeper.test.AsyncOps.StringCB; import org.apache.zookeeper.test.AsyncOps.VoidCB; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AsyncOpsTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(AsyncOpsTest.class); private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); LOG.info("Creating client {}", getTestName()); zk = createClient(); zk.addAuthInfo("digest", "ben:passwd".getBytes()); } @AfterEach @Override public void tearDown() throws Exception { zk.close(); super.tearDown(); LOG.info("Test clients shutting down"); } @Test public void testAsyncCreate() { new StringCB(zk).verifyCreate(); } @Test public void testAsyncCreate2() { new Create2CB(zk).verifyCreate(); } @Test public void testAsyncCreateThree() { CountDownLatch latch = new CountDownLatch(3); StringCB op1 = new StringCB(zk, latch); op1.setPath("/op1"); StringCB op2 = new StringCB(zk, latch); op2.setPath("/op2"); StringCB op3 = new StringCB(zk, latch); op3.setPath("/op3"); op1.create(); op2.create(); op3.create(); op1.verify(); op2.verify(); op3.verify(); } @Test public void testAsyncCreateFailure_NodeExists() { new StringCB(zk).verifyCreateFailure_NodeExists(); } @Test public void testAsyncCreateFailure_NoNode() { new StringCB(zk).verifyCreateFailure_NoNode(); } @Test public void testAsyncCreateFailure_NoChildForEphemeral() { new StringCB(zk).verifyCreateFailure_NoChildForEphemeral(); } @Test public void testAsyncCreate2Failure_NodeExists() { new Create2CB(zk).verifyCreateFailure_NodeExists(); } @Test public void testAsyncCreate2Failure_NoNode() { new Create2CB(zk).verifyCreateFailure_NoNode(); } @Test public void testAsyncCreate2Failure_NoChildForEphemeral() { new Create2CB(zk).verifyCreateFailure_NoChildForEphemeral(); } @Test public void testAsyncDelete() { new VoidCB(zk).verifyDelete(); } @Test public void testAsyncDeleteFailure_NoNode() { new VoidCB(zk).verifyDeleteFailure_NoNode(); } @Test public void testAsyncDeleteFailure_BadVersion() { new VoidCB(zk).verifyDeleteFailure_BadVersion(); } @Test public void testAsyncDeleteFailure_NotEmpty() { new VoidCB(zk).verifyDeleteFailure_NotEmpty(); } @Test public void testAsyncSync() { new VoidCB(zk).verifySync(); } @Test public void testAsyncSetACL() { new StatCB(zk).verifySetACL(); } @Test public void testAsyncSetACLFailure_NoNode() { new StatCB(zk).verifySetACLFailure_NoNode(); } @Test public void testAsyncSetACLFailure_BadVersion() { new StatCB(zk).verifySetACLFailure_BadVersion(); } @Test public void testAsyncSetData() { new StatCB(zk).verifySetData(); } @Test public void testAsyncSetDataFailure_NoNode() { new StatCB(zk).verifySetDataFailure_NoNode(); } @Test public void testAsyncSetDataFailure_BadVersion() { new StatCB(zk).verifySetDataFailure_BadVersion(); } @Test public void testAsyncExists() { new StatCB(zk).verifyExists(); } @Test public void testAsyncExistsFailure_NoNode() { new StatCB(zk).verifyExistsFailure_NoNode(); } @Test public void testAsyncGetACL() { new ACLCB(zk).verifyGetACL(); } @Test public void testAsyncGetACLFailure_NoNode() { new ACLCB(zk).verifyGetACLFailure_NoNode(); } @Test public void testAsyncGetChildrenEmpty() { new ChildrenCB(zk).verifyGetChildrenEmpty(); } @Test public void testAsyncGetChildrenSingle() { new ChildrenCB(zk).verifyGetChildrenSingle(); } @Test public void testAsyncGetChildrenTwo() { new ChildrenCB(zk).verifyGetChildrenTwo(); } @Test public void testAsyncGetChildrenFailure_NoNode() { new ChildrenCB(zk).verifyGetChildrenFailure_NoNode(); } @Test public void testAsyncGetChildren2Empty() { new Children2CB(zk).verifyGetChildrenEmpty(); } @Test public void testAsyncGetChildren2Single() { new Children2CB(zk).verifyGetChildrenSingle(); } @Test public void testAsyncGetChildren2Two() { new Children2CB(zk).verifyGetChildrenTwo(); } @Test public void testAsyncGetChildren2Failure_NoNode() { new Children2CB(zk).verifyGetChildrenFailure_NoNode(); } @Test public void testAsyncGetData() { new DataCB(zk).verifyGetData(); } @Test public void testAsyncGetDataFailure_NoNode() { new DataCB(zk).verifyGetDataFailure_NoNode(); } @Test public void testAsyncMulti() { new MultiCB(zk).verifyMulti(); } @Test public void testAsyncMultiFailure_AllErrorResult() { new MultiCB(zk).verifyMultiFailure_AllErrorResult(); } @Test public void testAsyncMultiFailure_NoSideEffect() throws Exception { new MultiCB(zk).verifyMultiFailure_NoSideEffect(); } @Test public void testAsyncMultiSequential_NoSideEffect() throws Exception { new MultiCB(zk).verifyMultiSequential_NoSideEffect(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AsyncTest.java0100644 0000000 0000000 00000011342 15051152474 033065 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.util.LinkedList; import java.util.List; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AsyncTest extends ZKTestCase implements StringCallback, VoidCallback, DataCallback { private static final Logger LOG = LoggerFactory.getLogger(AsyncTest.class); private QuorumBase qb = new QuorumBase(); @BeforeEach public void setUp() throws Exception { qb.setUp(); } @AfterEach public void tearDown() throws Exception { LOG.info("Test clients shutting down"); qb.tearDown(); } private ZooKeeper createClient() throws Exception { return createClient(qb.hostPort); } private ZooKeeper createClient(String hp) throws Exception { ZooKeeper zk = ClientBase.createZKClient(hp); return zk; } List results = new LinkedList<>(); @Test public void testAsync() throws Exception { ZooKeeper zk = null; zk = createClient(); try { zk.addAuthInfo("digest", "ben:passwd".getBytes()); zk.create("/ben", new byte[0], Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT, this, results); zk.create("/ben/2", new byte[0], Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, this, results); zk.delete("/ben", -1, this, results); zk.create("/ben2", new byte[0], Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT, this, results); zk.getData("/ben2", false, this, results); synchronized (results) { while (results.size() < 5) { results.wait(); } } assertEquals(0, (int) results.get(0)); assertEquals(Code.NOAUTH, Code.get(results.get(1))); assertEquals(0, (int) results.get(2)); assertEquals(0, (int) results.get(3)); assertEquals(0, (int) results.get(4)); } finally { zk.close(); } zk = createClient(); try { zk.addAuthInfo("digest", "ben:passwd2".getBytes()); try { zk.getData("/ben2", false, new Stat()); fail("Should have received a permission error"); } catch (KeeperException e) { assertEquals(Code.NOAUTH, e.code()); } } finally { zk.close(); } zk = createClient(); try { zk.addAuthInfo("digest", "ben:passwd".getBytes()); zk.getData("/ben2", false, new Stat()); } finally { zk.close(); } } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, String name) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } @SuppressWarnings("unchecked") public void processResult( int rc, String path, Object ctx, byte[] data, Stat stat) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Atom0100644 0000000 0000000 00000000171 15051152474 032675 xustar000000000 0000000 121 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AtomicFileOutputStreamTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AtomicFileOutputStre0100644 0000000 0000000 00000015141 15051152474 034324 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.AtomicFileOutputStream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class AtomicFileOutputStreamTest extends ZKTestCase { private static final String TEST_STRING = "hello world"; private static final String TEST_STRING_2 = "goodbye world"; private File testDir; private File dstFile; @BeforeEach public void setupTestDir() throws IOException { testDir = ClientBase.createEmptyTestDir(); dstFile = new File(testDir, "test.txt"); } @AfterEach public void cleanupTestDir() throws IOException { ClientBase.recursiveDelete(testDir); } /** * Test case where there is no existing file */ @Test public void testWriteNewFile() throws IOException { OutputStream fos = new AtomicFileOutputStream(dstFile); assertFalse(dstFile.exists()); fos.write(TEST_STRING.getBytes()); fos.flush(); assertFalse(dstFile.exists()); fos.close(); assertTrue(dstFile.exists()); String readBackData = new String(Files.readAllBytes(dstFile.toPath()), UTF_8); assertEquals(TEST_STRING, readBackData); } /** * Test case where there is no existing file */ @Test public void testOverwriteFile() throws IOException { assertTrue(dstFile.createNewFile(), "Creating empty dst file"); OutputStream fos = new AtomicFileOutputStream(dstFile); assertTrue(dstFile.exists(), "Empty file still exists"); fos.write(TEST_STRING.getBytes()); fos.flush(); // Original contents still in place assertEquals("", new String(Files.readAllBytes(dstFile.toPath()), UTF_8)); fos.close(); // New contents replace original file String readBackData = new String(Files.readAllBytes(dstFile.toPath()), UTF_8); assertEquals(TEST_STRING, readBackData); } /** * Test case where the flush() fails at close time - make sure that we clean * up after ourselves and don't touch any existing file at the destination */ @Test public void testFailToFlush() throws IOException { // Create a file at destination FileOutputStream fos = new FileOutputStream(dstFile); fos.write(TEST_STRING_2.getBytes()); fos.close(); OutputStream failingStream = createFailingStream(); failingStream.write(TEST_STRING.getBytes()); try { failingStream.close(); fail("Close didn't throw exception"); } catch (IOException ioe) { // expected } // Should not have touched original file assertEquals(TEST_STRING_2, new String(Files.readAllBytes(dstFile.toPath()), UTF_8)); assertEquals(dstFile.getName(), String.join(",", testDir.list()), "Temporary file should have been cleaned up"); } /** * Create a stream that fails to flush at close time */ private OutputStream createFailingStream() throws FileNotFoundException { return new AtomicFileOutputStream(dstFile) { @Override public void flush() throws IOException { throw new IOException("injected failure"); } }; } /** * Ensure the tmp file is cleaned up and dstFile is not created when * aborting a new file. */ @Test public void testAbortNewFile() throws IOException { AtomicFileOutputStream fos = new AtomicFileOutputStream(dstFile); fos.abort(); assertEquals(0, testDir.list().length); } /** * Ensure the tmp file is cleaned up and dstFile is not created when * aborting a new file. */ @Test public void testAbortNewFileAfterFlush() throws IOException { AtomicFileOutputStream fos = new AtomicFileOutputStream(dstFile); fos.write(TEST_STRING.getBytes()); fos.flush(); fos.abort(); assertEquals(0, testDir.list().length); } /** * Ensure the tmp file is cleaned up and dstFile is untouched when * aborting an existing file overwrite. */ @Test public void testAbortExistingFile() throws IOException { FileOutputStream fos1 = new FileOutputStream(dstFile); fos1.write(TEST_STRING.getBytes()); fos1.close(); AtomicFileOutputStream fos2 = new AtomicFileOutputStream(dstFile); fos2.abort(); // Should not have touched original file assertEquals(TEST_STRING, new String(Files.readAllBytes(dstFile.toPath()), UTF_8)); assertEquals(1, testDir.list().length); } /** * Ensure the tmp file is cleaned up and dstFile is untouched when * aborting an existing file overwrite. */ @Test public void testAbortExistingFileAfterFlush() throws IOException { FileOutputStream fos1 = new FileOutputStream(dstFile); fos1.write(TEST_STRING.getBytes()); fos1.close(); AtomicFileOutputStream fos2 = new AtomicFileOutputStream(dstFile); fos2.write(TEST_STRING_2.getBytes()); fos2.flush(); fos2.abort(); // Should not have touched original file assertEquals(TEST_STRING, new String(Files.readAllBytes(dstFile.toPath()), UTF_8)); assertEquals(1, testDir.list().length); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Auth0100644 0000000 0000000 00000000201 15051152474 032670 xustar000000000 0000000 129 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthFailX509AuthenticationProvider.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthFailX509Authenti0100644 0000000 0000000 00000003513 15051152474 034016 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.common.X509Exception; import org.apache.zookeeper.server.ServerCnxn; import org.apache.zookeeper.server.auth.X509AuthenticationProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AuthFailX509AuthenticationProvider extends X509AuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(AuthFailX509AuthenticationProvider.class); public AuthFailX509AuthenticationProvider() throws X509Exception { super(); } public AuthFailX509AuthenticationProvider(X509TrustManager trustManager, X509KeyManager keyManager) { super(trustManager, keyManager); } @Override public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { LOG.info("Authentication failed"); return KeeperException.Code.AUTHFAILED; } @Override public String getScheme() { return "authfail"; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthSHA2Test.java0100644 0000000 0000000 00000007660 15051152474 033337 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.security.NoSuchAlgorithmException; import java.security.Security; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class AuthSHA2Test extends AuthTest { @BeforeAll public static void setup() { // use the BouncyCastle's Provider for testing Security.addProvider(new BouncyCastleProvider()); // password is test System.setProperty(DigestAuthenticationProvider.DIGEST_ALGORITHM_KEY, DigestAlgEnum.SHA_256.getName()); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:wjySwxg860UATFtciuZ1lpzrCHrPeov6SPu/ZD56uig="); System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.test.InvalidAuthProvider"); } @AfterAll public static void teardown() { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); System.clearProperty("zookeeper.DigestAuthenticationProvider.superDigest"); System.clearProperty(DigestAuthenticationProvider.DIGEST_ALGORITHM_KEY); } @Test public void testBadAuthNotifiesWatch() throws Exception { super.testBadAuthNotifiesWatch(); } @Test public void testBadAuthThenSendOtherCommands() throws Exception { super.testBadAuthThenSendOtherCommands(); } @Test public void testSuper() throws Exception { super.testSuper(); } @Test public void testSuperACL() throws Exception { super.testSuperACL(); } @Test public void testOrdinaryACL() throws Exception { super.testOrdinaryACL(); } @Test public void testGenerateDigest() throws NoSuchAlgorithmException { assertEquals("super:wjySwxg860UATFtciuZ1lpzrCHrPeov6SPu/ZD56uig=", DigestAuthenticationProvider.generateDigest("super:test")); assertEquals("super:Ie58Fw6KA4ucTEDj23imIltKrXNDxQg8Rwtu0biQFcU=", DigestAuthenticationProvider.generateDigest("super:zookeeper")); assertEquals("super:rVOiTPnqEqlpIRXqSoE6+7h6SzbHUrfAe34i8n/gmRU=", DigestAuthenticationProvider.generateDigest(("super:foo"))); assertEquals("super:vs70GBagNcqIhGR4R6rXP8E3lvJPYhzMpAMx8ghbTUk=", DigestAuthenticationProvider.generateDigest(("super:bar"))); } @Test public void testDigest() throws NoSuchAlgorithmException { assertEquals("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", getGeneratedDigestStr(DigestAuthenticationProvider.digest("test"))); assertEquals("456831beef3fc1500939995d7369695f48642664a02d5eab9d807592a08b2384", getGeneratedDigestStr(DigestAuthenticationProvider.digest("zookeeper"))); assertEquals("2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", getGeneratedDigestStr(DigestAuthenticationProvider.digest(("foo")))); assertEquals("fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9", getGeneratedDigestStr(DigestAuthenticationProvider.digest(("bar")))); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthSHA3Test.java0100644 0000000 0000000 00000007661 15051152474 033341 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.security.NoSuchAlgorithmException; import java.security.Security; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class AuthSHA3Test extends AuthTest { @BeforeAll public static void setup() { // use the BouncyCastle's Provider for testing Security.addProvider(new BouncyCastleProvider()); // password is test System.setProperty(DigestAuthenticationProvider.DIGEST_ALGORITHM_KEY, DigestAlgEnum.SHA3_256.getName()); System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:cRy/KPYuDpW/dtsepniTMpuiuupnWgdU9txltIfv3hA="); System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.test.InvalidAuthProvider"); } @AfterAll public static void teardown() { Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); System.clearProperty("zookeeper.DigestAuthenticationProvider.superDigest"); System.clearProperty(DigestAuthenticationProvider.DIGEST_ALGORITHM_KEY); } @Test public void testBadAuthNotifiesWatch() throws Exception { super.testBadAuthNotifiesWatch(); } @Test public void testBadAuthThenSendOtherCommands() throws Exception { super.testBadAuthThenSendOtherCommands(); } @Test public void testSuper() throws Exception { super.testSuper(); } @Test public void testSuperACL() throws Exception { super.testSuperACL(); } @Test public void testOrdinaryACL() throws Exception { super.testOrdinaryACL(); } @Test public void testGenerateDigest() throws NoSuchAlgorithmException { assertEquals("super:cRy/KPYuDpW/dtsepniTMpuiuupnWgdU9txltIfv3hA=", DigestAuthenticationProvider.generateDigest("super:test")); assertEquals("super:gM3M1QcrKC6b+h4oZ5Ixc4GTVaAsggI+AqkUaF6E1Is=", DigestAuthenticationProvider.generateDigest("super:zookeeper")); assertEquals("super:2Ww7VUqTohd3lX/Vf4Nvw+GxbmOsX1p337L7Bnks4L8=", DigestAuthenticationProvider.generateDigest(("super:foo"))); assertEquals("super:Ft5s2Rtxr8zyz16feKiFR/8yqa6JoNEJ0In73aXojE8=", DigestAuthenticationProvider.generateDigest(("super:bar"))); } @Test public void testDigest() throws NoSuchAlgorithmException { assertEquals("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80", getGeneratedDigestStr(DigestAuthenticationProvider.digest("test"))); assertEquals("af4c1abc2deaa6edffc7ce34edeb8c03ee9a1488b64fd318ddb93b4b7f1c0746", getGeneratedDigestStr(DigestAuthenticationProvider.digest("zookeeper"))); assertEquals("76d3bc41c9f588f7fcd0d5bf4718f8f84b1c41b20882703100b9eb9413807c01", getGeneratedDigestStr(DigestAuthenticationProvider.digest(("foo")))); assertEquals("cceefd7e0545bcf8b6d19f3b5750c8a3ee8350418877bc6fb12e32de28137355", getGeneratedDigestStr(DigestAuthenticationProvider.digest(("bar")))); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/AuthTest.java0100644 0000000 0000000 00000024003 15051152474 032707 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class AuthTest extends ClientBase { @BeforeAll public static void setup() { // password is test // the default digestAlg is: SHA1 System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="); System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.test.InvalidAuthProvider"); } @AfterAll public static void teardown() { System.clearProperty("zookeeper.DigestAuthenticationProvider.superDigest"); System.clearProperty(DigestAuthenticationProvider.DIGEST_ALGORITHM_KEY); } private final CountDownLatch authFailed = new CountDownLatch(1); @Override protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { MyWatcher watcher = new MyWatcher(); return createClient(watcher, hp); } private class MyWatcher extends CountdownWatcher { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { authFailed.countDown(); } else { super.process(event); } } } @Test public void testBadAuthNotifiesWatch() throws Exception { ZooKeeper zk = createClient(); try { zk.addAuthInfo("FOO", "BAR".getBytes()); zk.getData("/path1", false, null); fail("Should get auth state error"); } catch (KeeperException.AuthFailedException e) { if (!authFailed.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Should have called my watcher"); } } finally { zk.close(); } } @Test public void testBadAuthThenSendOtherCommands() throws Exception { ZooKeeper zk = createClient(); try { zk.addAuthInfo("INVALID", "BAR".getBytes()); zk.exists("/foobar", false); zk.getData("/path1", false, null); fail("Should get auth state error"); } catch (KeeperException.AuthFailedException e) { if (!authFailed.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Should have called my watcher"); } } finally { zk.close(); } } @Test public void testSuper() throws Exception { ZooKeeper zk = createClient(); try { zk.addAuthInfo("digest", "pat:pass".getBytes()); zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.close(); // verify no auth zk = createClient(); try { zk.getData("/path1", false, null); fail("auth verification"); } catch (KeeperException.NoAuthException e) { // expected } zk.close(); // verify bad pass fails zk = createClient(); zk.addAuthInfo("digest", "pat:pass2".getBytes()); try { zk.getData("/path1", false, null); fail("auth verification"); } catch (KeeperException.NoAuthException e) { // expected } zk.close(); // verify super with bad pass fails zk = createClient(); zk.addAuthInfo("digest", "super:test2".getBytes()); try { zk.getData("/path1", false, null); fail("auth verification"); } catch (KeeperException.NoAuthException e) { // expected } zk.close(); // verify super with correct pass success zk = createClient(); zk.addAuthInfo("digest", "super:test".getBytes()); zk.getData("/path1", false, null); } finally { zk.close(); } } @Test public void testSuperACL() throws Exception { ZooKeeper zk = createClient(); try { zk.addAuthInfo("digest", "pat:pass".getBytes()); zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.close(); // verify super can do anything and ignores ACLs zk = createClient(); zk.addAuthInfo("digest", "super:test".getBytes()); zk.getData("/path1", false, null); zk.setACL("/path1", Ids.READ_ACL_UNSAFE, -1); zk.create("/path1/foo", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.setACL("/path1", Ids.OPEN_ACL_UNSAFE, -1); } finally { zk.close(); } } @Test public void testOrdinaryACL() throws Exception { ZooKeeper zk = createClient(); try { String path = "/path1"; zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.addAuthInfo("digest", "username1:password1".getBytes()); List list = new ArrayList<>(); int perm = ZooDefs.Perms.ALL; String userPassword = "username1:password1"; Id id = new Id("auth", userPassword); list.add(new ACL(perm, id)); zk.setACL(path, list, -1); zk.close(); zk = createClient(); zk.addAuthInfo("digest", "super:test".getBytes()); zk.getData(path, false, null); zk.close(); zk = createClient(); try { zk.getData(path, false, null); fail("should have NoAuthException"); } catch (KeeperException.NoAuthException e) { // expected } zk.addAuthInfo("digest", "username1:password1".getBytes()); zk.getData(path, false, null); } finally { zk.close(); } } @Test public void testGenerateDigest() throws NoSuchAlgorithmException { assertEquals("super:D/InIHSb7yEEbrWz8b9l71RjZJU=", DigestAuthenticationProvider.generateDigest("super:test")); assertEquals("super:yyuhPKumRtNj4r8GnSbbwuq1vhE=", DigestAuthenticationProvider.generateDigest("super:zookeeper")); assertEquals("super:t6lQTvqID/Gl5Or0n4FYE6kKP8w=", DigestAuthenticationProvider.generateDigest(("super:foo"))); assertEquals("super:hTdNN4QH4isoRvCrQ1Jf7REREQ4=", DigestAuthenticationProvider.generateDigest(("super:bar"))); } // This test is used to check the correctness of the algorithm // For the same digest algorithm and input, the output of digest hash is the constant. @Test public void testDigest() throws NoSuchAlgorithmException { assertEquals("a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", getGeneratedDigestStr(DigestAuthenticationProvider.digest("test"))); assertEquals("8a0444ded963cf1118dd34aa1acaafec268c654d", getGeneratedDigestStr(DigestAuthenticationProvider.digest("zookeeper"))); assertEquals("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33", getGeneratedDigestStr(DigestAuthenticationProvider.digest(("foo")))); assertEquals("62cdb7020ff920e5aa642c3d4066950dd1f01f4d", getGeneratedDigestStr(DigestAuthenticationProvider.digest(("bar")))); } // this method is used to generate the digest String to help us to compare the result generated by some online tool easily protected static String getGeneratedDigestStr(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(""); if (bytes == null || bytes.length <= 0) { return null; } for (int i = 0; i < bytes.length; i++) { int v = bytes[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } public enum DigestAlgEnum { SHA_1("SHA1"), SHA_256("SHA-256"), SHA3_256("SHA3-256"); private String name; DigestAlgEnum(String name) { this.name = name; } public String getName() { return this.name; } public static List getValues() { List digestList = new ArrayList<>(); for (DigestAlgEnum digest : values()) { digestList.add(digest.getName()); } return digestList; } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/BufferSizeTest.java0100644 0000000 0000000 00000011522 15051152474 034054 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import org.apache.jute.BinaryInputArchive; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class BufferSizeTest extends ClientBase { public static final int TEST_MAXBUFFER = 100; private static final File TEST_DATA = new File(System.getProperty("test.data.dir", "src/test/resources/data"), "buffersize"); private ZooKeeper zk; @BeforeEach public void setMaxBuffer() throws IOException, InterruptedException { System.setProperty("jute.maxbuffer", "" + TEST_MAXBUFFER); assertEquals(TEST_MAXBUFFER, BinaryInputArchive.maxBuffer, "Can't set jute.maxbuffer!"); zk = createClient(); } @Test public void testCreatesReqs() throws Exception { testRequests(new ClientOp() { @Override public void execute(byte[] data) throws Exception { zk.create("/create_test", data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } }); } @Test public void testSetReqs() throws Exception { final String path = "/set_test"; zk.create(path, new byte[1], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); testRequests(new ClientOp() { @Override public void execute(byte[] data) throws Exception { zk.setData(path, data, -1); } }); } /** Issues requests containing data smaller, equal, and greater than TEST_MAXBUFFER. */ private void testRequests(ClientOp clientOp) throws Exception { clientOp.execute(new byte[TEST_MAXBUFFER - 60]); try { // This should fail since the buffer size > the data size due to extra fields clientOp.execute(new byte[TEST_MAXBUFFER]); fail("Request exceeding jute.maxbuffer succeeded!"); } catch (KeeperException.ConnectionLossException e) { } try { clientOp.execute(new byte[TEST_MAXBUFFER + 10]); fail("Request exceeding jute.maxbuffer succeeded!"); } catch (KeeperException.ConnectionLossException e) { } } private interface ClientOp { void execute(byte[] data) throws Exception; } @Test public void testStartup() throws Exception { final String path = "/test_node"; zk.create(path, new byte[TEST_MAXBUFFER - 60], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.setData(path, new byte[TEST_MAXBUFFER - 50], -1); stopServer(); startServer(); } @Test public void testStartupFailureCreate() throws Exception { // Empty snapshot and logfile containing a 5000-byte create testStartupFailure(new File(TEST_DATA, "create"), "Server started despite create exceeding jute.maxbuffer!"); } @Test public void testStartupFailureSet() throws Exception { // Empty snapshot and logfile containing a 1-byte create and 5000-byte set testStartupFailure(new File(TEST_DATA, "set"), "Server started despite set exceeding jute.maxbuffer!"); } @Test public void testStartupFailureSnapshot() throws Exception { // Snapshot containing 5000-byte znode and logfile containing create txn testStartupFailure(new File(TEST_DATA, "snapshot"), "Server started despite znode exceeding jute.maxbuffer!"); } private void testStartupFailure(File testDir, String failureMsg) throws Exception { stopServer(); // Point server at testDir File oldTmpDir = tmpDir; tmpDir = testDir; try { startServer(); fail(failureMsg); } catch (IOException e) { LOG.debug("Successfully caught IOException", e); } finally { tmpDir = oldTmpDir; } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/CheckTest.java0100644 0000000 0000000 00000011755 15051152474 033035 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.File; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.CheckVersionRequest; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; public class CheckTest extends ClientBase { @BeforeEach public void setUp(TestInfo testInfo) throws Exception { System.setProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT, "2000"); if (testInfo.getDisplayName().contains("Cluster")) { return; } super.setUp(); } @AfterEach public void tearDown(TestInfo testInfo) throws Exception { System.clearProperty(ZKClientConfig.ZOOKEEPER_REQUEST_TIMEOUT); if (testInfo.getDisplayName().contains("Cluster")) { return; } super.tearDown(); } @Override public void setUp() throws Exception { } @Override public void tearDown() throws Exception { } private static void checkVersion(TestableZooKeeper zk, String path, int version) throws Exception { RequestHeader header = new RequestHeader(); header.setType(ZooDefs.OpCode.check); CheckVersionRequest request = new CheckVersionRequest(path, version); ReplyHeader replyHeader = zk.submitRequest(header, request, null, null); if (replyHeader.getErr() != 0) { throw KeeperException.create(KeeperException.Code.get(replyHeader.getErr()), path); } } private void testOperations(TestableZooKeeper zk) throws Exception { Stat stat = new Stat(); zk.getData("/", false, stat); assertThrows(KeeperException.UnimplementedException.class, () -> checkVersion(zk, "/", -1)); } @Test public void testStandalone() throws Exception { testOperations(createClient()); } @Test public void testStandaloneDatabaseReloadAfterCheck() throws Exception { try { testOperations(createClient()); } catch (Throwable ignored) { // Ignore to test database reload after check } stopServer(); startServer(); } @Test public void testCluster() throws Exception { QuorumBase qb = new QuorumBase(); try { qb.setUp(true, true); testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.OBSERVING)); testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.FOLLOWING)); testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.LEADING)); } finally { try { qb.tearDown(); } catch (Exception ignored) {} } } @Test public void testClusterDatabaseReloadAfterCheck() throws Exception { QuorumBase qb = new QuorumBase(); try { qb.setUp(true, true); // Get leader before possible damaging operations to // reduce chance of leader migration and log truncation. File dataDir = qb.getLeaderDataDir(); QuorumPeer leader = qb.getLeaderQuorumPeer(); try { testOperations(qb.createClient(new CountdownWatcher(), QuorumPeer.ServerState.LEADING)); } catch (Throwable ignored) { // Ignore to test database reload after check } qb.shutdown(leader); FileTxnSnapLog txnSnapLog = new FileTxnSnapLog(dataDir, dataDir); ZKDatabase database = new ZKDatabase(txnSnapLog); database.loadDataBase(); } finally { try { qb.tearDown(); } catch (Exception ignored) {} } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Chro0100644 0000000 0000000 00000000156 15051152474 032673 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootAsyncTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootAsyncTest.java0100644 0000000 0000000 00000003157 15051152474 034251 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ChrootAsyncTest extends AsyncOpsTest { private static final Logger LOG = LoggerFactory.getLogger(ChrootAsyncTest.class); @BeforeEach @Override public void setUp() throws Exception { String hp = hostPort; hostPort = hostPort + "/chrootasynctest"; super.setUp(); LOG.info("Creating client {}", getTestName()); ZooKeeper zk = createClient(hp); try { zk.create("/chrootasynctest", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Chro0100644 0000000 0000000 00000000157 15051152474 032674 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootClientTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootClientTest.jav0100644 0000000 0000000 00000003474 15051152474 034253 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ChrootClientTest extends ClientTest { private static final Logger LOG = LoggerFactory.getLogger(ChrootClientTest.class); @BeforeEach @Override public void setUp() throws Exception { String hp = hostPort; hostPort = hostPort + "/chrootclienttest"; System.out.println(hostPort); super.setUp(); LOG.info("STARTING {}", getTestName()); ZooKeeper zk = createClient(hp); try { zk.create("/chrootclienttest", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } finally { zk.close(); } } @Test public void testPing() throws Exception { // not necessary to repeat this, expensive and not chroot related } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ChrootTest.java0100644 0000000 0000000 00000013366 15051152474 033256 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.Test; public class ChrootTest extends ClientBase { private static class MyWatcher implements Watcher { private final String path; private String eventPath; private CountDownLatch latch = new CountDownLatch(1); public MyWatcher(String path) { this.path = path; } public void process(WatchedEvent event) { System.out.println("latch:" + path + " " + event.getPath()); this.eventPath = event.getPath(); latch.countDown(); } public boolean matches() throws InterruptedException { if (!latch.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("No watch received within timeout period " + path); } return path.equals(eventPath); } } @Test public void testChrootWithZooKeeperPathWatcher() throws Exception { ZooKeeper zk1 = createClient(hostPort + "/chroot"); BlockingQueue events = new LinkedBlockingQueue<>(); byte[] config = zk1.getConfig(events::add, null); ZooKeeper zk2 = createClient(); zk2.addAuthInfo("digest", "super:test".getBytes()); zk2.setData(ZooDefs.CONFIG_NODE, config, -1); waitFor("config watcher receive no event", () -> !events.isEmpty(), 10); WatchedEvent event = events.poll(); assertNotNull(event); assertEquals(Watcher.Event.KeeperState.SyncConnected, event.getState()); assertEquals(Watcher.Event.EventType.NodeDataChanged, event.getType()); assertEquals(ZooDefs.CONFIG_NODE, event.getPath()); } @Test public void testChrootSynchronous() throws IOException, InterruptedException, KeeperException { ZooKeeper zk1 = createClient(); try { zk1.create("/ch1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } finally { if (zk1 != null) { zk1.close(); } } ZooKeeper zk2 = createClient(hostPort + "/ch1"); try { assertEquals("/ch2", zk2.create("/ch2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); } finally { if (zk2 != null) { zk2.close(); } } zk1 = createClient(); zk2 = createClient(hostPort + "/ch1"); try { // check get MyWatcher w1 = new MyWatcher("/ch1"); assertNotNull(zk1.exists("/ch1", w1)); MyWatcher w2 = new MyWatcher("/ch1/ch2"); assertNotNull(zk1.exists("/ch1/ch2", w2)); MyWatcher w3 = new MyWatcher("/ch2"); assertNotNull(zk2.exists("/ch2", w3)); // set watches on child MyWatcher w4 = new MyWatcher("/ch1"); zk1.getChildren("/ch1", w4); MyWatcher w5 = new MyWatcher("/"); zk2.getChildren("/", w5); // check set zk1.setData("/ch1", "1".getBytes(), -1); zk2.setData("/ch2", "2".getBytes(), -1); // check watches assertTrue(w1.matches()); assertTrue(w2.matches()); assertTrue(w3.matches()); // check exceptions try { zk2.setData("/ch3", "3".getBytes(), -1); } catch (KeeperException.NoNodeException e) { assertEquals("/ch3", e.getPath()); } assertTrue(Arrays.equals("1".getBytes(), zk1.getData("/ch1", false, null))); assertTrue(Arrays.equals("2".getBytes(), zk1.getData("/ch1/ch2", false, null))); assertTrue(Arrays.equals("2".getBytes(), zk2.getData("/ch2", false, null))); // check delete zk2.delete("/ch2", -1); assertTrue(w4.matches()); assertTrue(w5.matches()); zk1.delete("/ch1", -1); assertNull(zk1.exists("/ch1", false)); assertNull(zk1.exists("/ch1/ch2", false)); assertNull(zk2.exists("/ch2", false)); } finally { if (zk1 != null) { zk1.close(); } if (zk2 != null) { zk2.close(); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientBase.java0100644 0000000 0000000 00000065472 15051152474 033176 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.ConnectException; import java.net.ProtocolException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FilePadding; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class ClientBase extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ClientBase.class); public static int CONNECTION_TIMEOUT = 30000; protected String hostPort = "127.0.0.1:" + PortAssignment.unique(); protected int maxCnxns = 0; protected ServerCnxnFactory serverFactory = null; protected File tmpDir = null; protected boolean exceptionOnFailedConnect = false; long initialFdCount; public ClientBase() { super(); } public static class CountdownWatcher implements Watcher { // TODO this doesn't need to be volatile! (Should probably be final) volatile CountDownLatch clientConnected; // Set to true when connected to a read-only server, or a read-write (quorum) server. volatile boolean connected; // Set to true when connected to a quorum server. volatile boolean syncConnected; // Set to true when connected to a quorum server in read-only mode volatile boolean readOnlyConnected; public CountdownWatcher() { reset(); } public synchronized void reset() { clientConnected = new CountDownLatch(1); connected = false; syncConnected = false; readOnlyConnected = false; } public synchronized void process(WatchedEvent event) { KeeperState state = event.getState(); if (state == KeeperState.SyncConnected) { connected = true; syncConnected = true; readOnlyConnected = false; } else if (state == KeeperState.ConnectedReadOnly) { connected = true; syncConnected = false; readOnlyConnected = true; } else { connected = false; syncConnected = false; readOnlyConnected = false; } notifyAll(); if (connected) { clientConnected.countDown(); } } public synchronized boolean isConnected() { return connected; } protected synchronized String connectionDescription() { return String.format("connected(%s), syncConnected(%s), readOnlyConnected(%s)", connected, syncConnected, readOnlyConnected); } public synchronized void waitForConnected(long timeout) throws InterruptedException, TimeoutException { long expire = Time.currentElapsedTime() + timeout; long left = timeout; while (!connected && left > 0) { wait(left); left = expire - Time.currentElapsedTime(); } if (!connected) { throw new TimeoutException("Failed to connect to ZooKeeper server: " + connectionDescription()); } } public synchronized void waitForSyncConnected(long timeout) throws InterruptedException, TimeoutException { long expire = Time.currentElapsedTime() + timeout; long left = timeout; while (!syncConnected && left > 0) { wait(left); left = expire - Time.currentElapsedTime(); } if (!syncConnected) { throw new TimeoutException( "Failed to connect to read-write ZooKeeper server: " + connectionDescription()); } } public synchronized void waitForReadOnlyConnected(long timeout) throws InterruptedException, TimeoutException { long expire = Time.currentElapsedTime() + timeout; long left = timeout; while (!readOnlyConnected && left > 0) { wait(left); left = expire - Time.currentElapsedTime(); } if (!readOnlyConnected) { throw new TimeoutException( "Failed to connect in read-only mode to ZooKeeper server: " + connectionDescription()); } } public synchronized void waitForDisconnected(long timeout) throws InterruptedException, TimeoutException { long expire = Time.currentElapsedTime() + timeout; long left = timeout; while (connected && left > 0) { wait(left); left = expire - Time.currentElapsedTime(); } if (connected) { throw new TimeoutException("Did not disconnect: " + connectionDescription()); } } } protected TestableZooKeeper createClient() throws IOException, InterruptedException { return createClient(hostPort); } protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { CountdownWatcher watcher = new CountdownWatcher(); return createClient(watcher, hp); } protected TestableZooKeeper createClient(CountdownWatcher watcher) throws IOException, InterruptedException { return createClient(watcher, hostPort); } private List allClients; private boolean allClientsSetup = false; protected TestableZooKeeper createClient(String hp, int timeout) throws IOException, InterruptedException { return createClient(new CountdownWatcher(), hp, timeout); } protected TestableZooKeeper createClient(CountdownWatcher watcher, String hp) throws IOException, InterruptedException { return createClient(watcher, hp, CONNECTION_TIMEOUT); } protected TestableZooKeeper createClient(CountdownWatcher watcher, String hp, int timeout) throws IOException, InterruptedException { watcher.reset(); TestableZooKeeper zk = new TestableZooKeeper(hp, timeout, watcher); if (!watcher.clientConnected.await(timeout, TimeUnit.MILLISECONDS)) { if (exceptionOnFailedConnect) { throw new ProtocolException("Unable to connect to server"); } fail("Unable to connect to server"); } synchronized (this) { if (!allClientsSetup) { LOG.error("allClients never setup"); fail("allClients never setup"); } if (allClients != null) { allClients.add(zk); JMXEnv.ensureAll(getHexSessionId(zk.getSessionId())); } else { // test done - close the zk, not needed zk.close(); } } return zk; } public static class HostPort { String host; int port; public HostPort(String host, int port) { this.host = host; this.port = port; } } public static List parseHostPortList(String hplist) { ArrayList alist = new ArrayList<>(); for (String hp : hplist.split(",")) { int idx = hp.lastIndexOf(':'); String host = hp.substring(0, idx); int port; try { port = Integer.parseInt(hp.substring(idx + 1)); } catch (RuntimeException e) { throw new RuntimeException("Problem parsing " + hp + e.toString()); } alist.add(new HostPort(host, port)); } return alist; } public static boolean waitForServerUp(String hp, long timeout) { return waitForServerUp(hp, timeout, false); } public static boolean waitForServerUp(String hp, long timeout, boolean secure) { long start = Time.currentElapsedTime(); while (true) { try { // if there are multiple hostports, just take the first one HostPort hpobj = parseHostPortList(hp).get(0); String result = send4LetterWord(hpobj.host, hpobj.port, "stat", secure); if (result.startsWith("Zookeeper version:") && !result.contains("READ-ONLY")) { return true; } } catch (ConnectException e) { // ignore as this is expected, do not log stacktrace LOG.info("server {} not up: {}", hp, e.toString()); } catch (IOException e) { // ignore as this is expected LOG.info("server {} not up", hp, e); } catch (SSLContextException e) { LOG.error("server {} not up", hp, e); } if (Time.currentElapsedTime() > start + timeout) { break; } try { Thread.sleep(250); } catch (InterruptedException e) { // ignore } } return false; } public static boolean waitForServerDown(String hp, long timeout) { return waitForServerDown(hp, timeout, false); } public static boolean waitForServerDown(String hp, long timeout, boolean secure) { long start = Time.currentElapsedTime(); while (true) { try { HostPort hpobj = parseHostPortList(hp).get(0); send4LetterWord(hpobj.host, hpobj.port, "stat", secure); } catch (IOException e) { return true; } catch (SSLContextException e) { return true; } if (Time.currentElapsedTime() > start + timeout) { break; } try { Thread.sleep(250); } catch (InterruptedException e) { // ignore } } return false; } /** * Return true if any of the states is achieved */ public static boolean waitForServerState(QuorumPeer qp, int timeout, String... serverStates) { long start = Time.currentElapsedTime(); while (true) { try { Thread.sleep(250); } catch (InterruptedException e) { // ignore } for (String state : serverStates) { if (qp.getServerState().equals(state)) { return true; } } if (Time.currentElapsedTime() > start + timeout) { return false; } } } static void verifyThreadTerminated(Thread thread, long millis) throws InterruptedException { thread.join(millis); if (thread.isAlive()) { LOG.error("Thread {} : {}", thread.getName(), Arrays.toString(thread.getStackTrace())); assertFalse(true, "thread " + thread.getName() + " still alive after join"); } } public static File createEmptyTestDir() throws IOException { return createTmpDir(testBaseDir, false); } public static File createTmpDir() throws IOException { return createTmpDir(testBaseDir, true); } static File createTmpDir(File parentDir, boolean createInitFile) throws IOException { if (!parentDir.exists()) { parentDir.mkdir(); } File tmpFile = File.createTempFile("test", ".junit", parentDir); // don't delete tmpFile - this ensures we don't attempt to create // a tmpDir with a duplicate name File tmpDir = new File(tmpFile + ".dir"); assertFalse(tmpDir.exists()); // never true if tmpfile does it's job assertTrue(tmpDir.mkdirs()); // todo not every tmp directory needs this file if (createInitFile) { createInitializeFile(tmpDir); } return tmpDir; } public static void createInitializeFile(File dir) throws IOException { File initFile = new File(dir, "initialize"); if (!initFile.exists()) { assertTrue(initFile.createNewFile()); } } private static int getPort(String hostPort) { String[] split = hostPort.split(":"); String portstr = split[split.length - 1]; String[] pc = portstr.split("/"); if (pc.length > 1) { portstr = pc[0]; } return Integer.parseInt(portstr); } /** * Starting the given server instance */ public static void startServerInstance( File dataDir, ServerCnxnFactory factory, String hostPort, int serverId) throws IOException, InterruptedException { final int port = getPort(hostPort); LOG.info("STARTING server instance 127.0.0.1:{}", port); ZooKeeperServer zks = new ZooKeeperServer(dataDir, dataDir, 3000); zks.setCreateSessionTrackerServerId(serverId); factory.startup(zks); assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + port, CONNECTION_TIMEOUT, factory.isSecure()), "waiting for server up"); } /** * This method instantiates a new server. Starting of the server * instance has been moved to a separate method * {@link ClientBase#startServerInstance(File, ServerCnxnFactory, String, int)}. * Because any exception on starting the server would leave the server * running and the caller would not be able to shutdown the instance. This * may affect other test cases. * * @return newly created server instance * * @see ZOOKEEPER-1852 * for more information. */ public static ServerCnxnFactory createNewServerInstance( ServerCnxnFactory factory, String hostPort, int maxCnxns) throws IOException, InterruptedException { final int port = getPort(hostPort); LOG.info("CREATING server instance 127.0.0.1:{}", port); if (factory == null) { factory = ServerCnxnFactory.createFactory(port, maxCnxns); } return factory; } static void shutdownServerInstance(ServerCnxnFactory factory, String hostPort) { if (factory != null) { ZKDatabase zkDb = null; { ZooKeeperServer zs = factory.getZooKeeperServer(); if (zs != null) { zkDb = zs.getZKDatabase(); } } factory.shutdown(); try { if (zkDb != null) { zkDb.close(); } } catch (IOException ie) { LOG.warn("Error closing logs ", ie); } final int PORT = getPort(hostPort); assertTrue( ClientBase.waitForServerDown("127.0.0.1:" + PORT, CONNECTION_TIMEOUT, factory.isSecure()), "waiting for server down"); } } /** * Test specific setup */ public static void setupTestEnv() { // during the tests we run with 100K prealloc in the logs. // on windows systems prealloc of 64M was seen to take ~15seconds // resulting in test failure (client timeout on first session). // set env and directly in order to handle static init/gc issues System.setProperty("zookeeper.preAllocSize", "100"); FilePadding.setPreallocSize(100 * 1024); } protected void setUpAll() throws Exception { allClients = new LinkedList<>(); allClientsSetup = true; } @BeforeEach public void setUp() throws Exception { setUpWithServerId(1); } protected void setUpWithServerId(int serverId) throws Exception { /* some useful information - log the number of fds used before * and after a test is run. Helps to verify we are freeing resources * correctly. Unfortunately this only works on unix systems (the * only place sun has implemented as part of the mgmt bean api. */ OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { initialFdCount = osMbean.getOpenFileDescriptorCount(); LOG.info("Initial fdcount is: {}", initialFdCount); } setupTestEnv(); setupCustomizedEnv(); JMXEnv.setUp(); setUpAll(); tmpDir = createTmpDir(testBaseDir, true); startServer(serverId); LOG.info("Client test setup finished"); } protected void startServer() throws Exception { startServer(1); } /** * Give it a chance to set up customized env before starting the server. */ public void setupCustomizedEnv() { /* do nothing by default */ } private void startServer(int serverId) throws Exception { LOG.info("STARTING server"); serverFactory = createNewServerInstance(serverFactory, hostPort, maxCnxns); startServerInstance(tmpDir, serverFactory, hostPort, serverId); // ensure that server and data bean are registered Set children = JMXEnv.ensureParent("InMemoryDataTree", "StandaloneServer_port"); // Remove beans which are related to zk client sessions. Strong // assertions cannot be done for these client sessions because // registeration of these beans with server will happen only on their // respective reconnection interval verifyUnexpectedBeans(children); } private void verifyUnexpectedBeans(Set children) { if (allClients != null) { for (ZooKeeper zkc : allClients) { Iterator childItr = children.iterator(); while (childItr.hasNext()) { ObjectName clientBean = childItr.next(); if (clientBean.toString().contains(getHexSessionId(zkc.getSessionId()))) { LOG.info("found name:{} client bean:{}", zkc.getSessionId(), clientBean.toString()); childItr.remove(); } } } } for (ObjectName bean : children) { LOG.info("unexpected:{}", bean.toString()); } assertEquals(0, children.size(), "Unexpected bean exists!"); } /** * Returns a string representation of the given long value session id * * @param sessionId * long value of session id * @return string representation of session id */ protected static String getHexSessionId(long sessionId) { return "0x" + Long.toHexString(sessionId); } protected void stopServer() throws Exception { LOG.info("STOPPING server"); shutdownServerInstance(serverFactory, hostPort); serverFactory = null; // ensure no beans are leftover JMXEnv.ensureOnly(); } protected void tearDownAll() throws Exception { synchronized (this) { if (allClients != null) { for (ZooKeeper zk : allClients) { try { if (zk != null) { zk.close(); } } catch (InterruptedException e) { LOG.warn("ignoring interrupt", e); } } } allClients = null; } } @AfterEach public void tearDown() throws Exception { LOG.info("tearDown starting"); tearDownAll(); stopServer(); if (tmpDir != null) { assertTrue(recursiveDelete(tmpDir), "delete " + tmpDir.toString()); } // This has to be set to null when the same instance of this class is reused between test cases serverFactory = null; JMXEnv.tearDown(); /* some useful information - log the number of fds used before * and after a test is run. Helps to verify we are freeing resources * correctly. Unfortunately this only works on unix systems (the * only place sun has implemented as part of the mgmt bean api. */ OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { long fdCount = osMbean.getOpenFileDescriptorCount(); String message = "fdcount after test is: " + fdCount + " at start it was " + initialFdCount; LOG.info(message); if (fdCount > initialFdCount) { LOG.info("sleeping for 20 secs"); //Thread.sleep(60000); //assertTrue(message, fdCount <= initialFdCount); } } cleanUpCustomizedEnv(); } public void cleanUpCustomizedEnv() { /* do nothing by default */ } public static MBeanServerConnection jmxConn() throws IOException { return JMXEnv.conn(); } public static boolean recursiveDelete(File d) { return TestUtils.deleteFileRecursively(d, true); } public static void logAllStackTraces() { StringBuilder sb = new StringBuilder(); sb.append("Starting logAllStackTraces()\n"); Map threads = Thread.getAllStackTraces(); for (Entry e : threads.entrySet()) { sb.append("Thread " + e.getKey().getName() + "\n"); for (StackTraceElement elem : e.getValue()) { sb.append("\tat " + elem + "\n"); } } sb.append("Ending logAllStackTraces()\n"); LOG.error(sb.toString()); } /* * Verify that all of the servers see the same number of nodes * at the root */ void verifyRootOfAllServersMatch(String hostPort) throws InterruptedException, KeeperException, IOException { String[] parts = hostPort.split(","); // run through till the counts no longer change on each server // max 15 tries, with 2 second sleeps, so approx 30 seconds int[] counts = new int[parts.length]; int failed = 0; for (int j = 0; j < 100; j++) { int[] newcounts = new int[parts.length]; int i = 0; for (String hp : parts) { try { ZooKeeper zk = createClient(hp); try { newcounts[i++] = zk.getChildren("/", false).size(); } finally { zk.close(); } } catch (Throwable t) { failed++; // if session creation fails dump the thread stack // and try the next server logAllStackTraces(); } } if (Arrays.equals(newcounts, counts)) { LOG.info("Found match with array:{}", Arrays.toString(newcounts)); counts = newcounts; break; } else { counts = newcounts; Thread.sleep(10000); } // don't keep this up too long, will assert false below if (failed > 10) { break; } } // verify all the servers reporting same number of nodes String logmsg = "node count not consistent{} {}"; for (int i = 1; i < parts.length; i++) { if (counts[i - 1] != counts[i]) { LOG.error(logmsg, counts[i - 1], counts[i]); } else { LOG.info(logmsg, counts[i - 1], counts[i]); } } } public static ZooKeeper createZKClient(String cxnString) throws Exception { return createZKClient(cxnString, CONNECTION_TIMEOUT); } /** * Returns ZooKeeper client after connecting to ZooKeeper Server. Session * timeout is {@link #CONNECTION_TIMEOUT} * * @param cxnString * connection string in the form of host:port * @param sessionTimeout * @throws IOException * in cases of network failure */ public static ZooKeeper createZKClient(String cxnString, int sessionTimeout) throws IOException { return createZKClient(cxnString, sessionTimeout, CONNECTION_TIMEOUT); } public static ZooKeeper createZKClient(String cxnString, int sessionTimeout, long connectionTimeout) throws IOException { return createZKClient(cxnString, sessionTimeout, connectionTimeout, new ZKClientConfig()); } public static ZooKeeper createZKClient(String cxnString, int sessionTimeout, long connectionTimeout, ZKClientConfig config) throws IOException { CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(cxnString, sessionTimeout, watcher, config); try { watcher.waitForConnected(connectionTimeout); } catch (InterruptedException | TimeoutException e) { fail("ZooKeeper client can not connect to " + cxnString); } return zk; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Clie0100644 0000000 0000000 00000000157 15051152474 032655 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientHammerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientHammerTest.jav0100644 0000000 0000000 00000020572 15051152474 034224 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.util.Date; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.Time; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientHammerTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(ClientHammerTest.class); private static final long HAMMERTHREAD_LATENCY = 5; private abstract static class HammerThread extends Thread { protected final int count; protected volatile int current = 0; HammerThread(String name, int count) { super(name); this.count = count; } } private static class BasicHammerThread extends HammerThread { private final ZooKeeper zk; private final String prefix; BasicHammerThread(String name, ZooKeeper zk, String prefix, int count) { super(name, count); this.zk = zk; this.prefix = prefix; } public void run() { byte[] b = new byte[256]; try { for (; current < count; current++) { // Simulate a bit of network latency... Thread.sleep(HAMMERTHREAD_LATENCY); zk.create(prefix + current, b, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } catch (Throwable t) { LOG.error("Client create operation failed", t); } finally { try { zk.close(); } catch (InterruptedException e) { LOG.warn("Unexpected", e); } } } } private static class SuperHammerThread extends HammerThread { private final ClientHammerTest parent; private final String prefix; SuperHammerThread(String name, ClientHammerTest parent, String prefix, int count) { super(name, count); this.parent = parent; this.prefix = prefix; } public void run() { byte[] b = new byte[256]; try { for (; current < count; current++) { ZooKeeper zk = parent.createClient(); try { zk.create(prefix + current, b, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } finally { try { zk.close(); } catch (InterruptedException e) { LOG.warn("Unexpected", e); } } } } catch (Throwable t) { LOG.error("Client create operation failed", t); } } } /** * Separate threads each creating a number of nodes. Each thread * is using a non-shared (owned by thread) client for all node creations. * @throws Throwable */ @Test public void testHammerBasic() throws Throwable { runHammer(10, 1000); } public void runHammer(final int threadCount, final int childCount) throws Throwable { try { HammerThread[] threads = new HammerThread[threadCount]; long start = Time.currentElapsedTime(); for (int i = 0; i < threads.length; i++) { ZooKeeper zk = createClient(); String prefix = "/test-" + i; zk.create(prefix, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); prefix += "/"; HammerThread thread = new BasicHammerThread("BasicHammerThread-" + i, zk, prefix, childCount); thread.start(); threads[i] = thread; } verifyHammer(start, threads, childCount); } catch (Throwable t) { LOG.error("test failed", t); throw t; } } /** * Separate threads each creating a number of nodes. Each thread * is creating a new client for each node creation. * @throws Throwable */ @Test public void testHammerSuper() throws Throwable { try { final int threadCount = 5; final int childCount = 10; HammerThread[] threads = new HammerThread[threadCount]; long start = Time.currentElapsedTime(); for (int i = 0; i < threads.length; i++) { String prefix = "/test-" + i; { ZooKeeper zk = createClient(); try { zk.create(prefix, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } finally { zk.close(); } } prefix += "/"; HammerThread thread = new SuperHammerThread("SuperHammerThread-" + i, this, prefix, childCount); thread.start(); threads[i] = thread; } verifyHammer(start, threads, childCount); } catch (Throwable t) { LOG.error("test failed", t); throw t; } } public void verifyHammer(long start, HammerThread[] threads, int childCount) throws IOException, InterruptedException, KeeperException { // look for the clients to finish their create operations LOG.info("Starting check for completed hammers"); int workingCount = threads.length; for (int i = 0; i < 120; i++) { Thread.sleep(10000); for (HammerThread h : threads) { if (!h.isAlive() || h.current == h.count) { workingCount--; } } if (workingCount == 0) { break; } workingCount = threads.length; } if (workingCount > 0) { for (HammerThread h : threads) { LOG.warn("{} never finished creation, current:{}", h.getName(), h.current); } } else { LOG.info("Hammer threads completed creation operations"); } for (HammerThread h : threads) { final int safetyFactor = 3; verifyThreadTerminated(h, (long) threads.length * (long) childCount * HAMMERTHREAD_LATENCY * (long) safetyFactor); } LOG.info("{} Total time {}", new Date(), (Time.currentElapsedTime() - start)); ZooKeeper zk = createClient(); try { LOG.info("******************* Connected to ZooKeeper{}", new Date()); for (int i = 0; i < threads.length; i++) { LOG.info("Doing thread: {} {}", i, new Date()); List children = zk.getChildren("/test-" + i, false); assertEquals(childCount, children.size()); children = zk.getChildren("/test-" + i, false, null); assertEquals(childCount, children.size()); } for (int i = 0; i < threads.length; i++) { List children = zk.getChildren("/test-" + i, false); assertEquals(childCount, children.size()); children = zk.getChildren("/test-" + i, false, null); assertEquals(childCount, children.size()); } } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Clie0100644 0000000 0000000 00000000161 15051152474 032650 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientPortBindTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientPortBindTest.j0100644 0000000 0000000 00000007571 15051152474 034211 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientPortBindTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ClientPortBindTest.class); /** * Verify that the server binds to the specified address */ @Test public void testBindByAddress() throws Exception { String bindAddress = null; Enumeration intfs = NetworkInterface.getNetworkInterfaces(); // if we have a loopback and it has an address use it while (intfs.hasMoreElements()) { NetworkInterface i = intfs.nextElement(); try { if (i.isLoopback()) { Enumeration addrs = i.getInetAddresses(); while (addrs.hasMoreElements()) { InetAddress a = addrs.nextElement(); if (a.isLoopbackAddress()) { bindAddress = a.getHostAddress(); if (a instanceof Inet6Address) { bindAddress = "[" + bindAddress + "]"; } break; } } } } catch (SocketException se) { LOG.warn("Couldn't find loopback interface", se); } } if (bindAddress == null) { LOG.warn("Unable to determine loop back address, skipping test"); return; } final int PORT = PortAssignment.unique(); LOG.info("Using {} as the bind address", bindAddress); final String HOSTPORT = bindAddress + ":" + PORT; LOG.info("Using {} as the host/port", HOSTPORT); File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); ServerCnxnFactory f = ServerCnxnFactory.createFactory(new InetSocketAddress(bindAddress, PORT), -1); f.startup(zks); LOG.info("starting up the the server, waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server up"); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { zk.close(); } finally { f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server down"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Clie0100644 0000000 0000000 00000000156 15051152474 032654 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientRetryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientRetryTest.java0100644 0000000 0000000 00000005532 15051152474 034260 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertSame; import java.io.IOException; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ClientRetryTest extends ClientBase { @BeforeEach @Override public void setUp() throws Exception { maxCnxns = 1; super.setUp(); } /* * This is a simple test - try to connect two clients to a server * accepting a maximum of one connection from each address. Check that * only one is accepted. Close that connection, and check that the other * eventually connects. * * There is a possibility of a false positive here, as when zk2 is tested * for having connected it might not have been given enough time, and finish * connecting after the test is done. Since the * server doesn't tell the client why it hasn't connected, there's no * obvious way to detect the difference. */ @Test public void testClientRetry() throws IOException, InterruptedException, TimeoutException { CountdownWatcher cdw1 = new CountdownWatcher(); CountdownWatcher cdw2 = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(hostPort, 10000, cdw1); try { cdw1.waitForConnected(CONNECTION_TIMEOUT); ZooKeeper zk2 = new ZooKeeper(hostPort, 10000, cdw2); try { States s1 = zk.getState(); States s2 = zk2.getState(); assertSame(s1, States.CONNECTED); assertSame(s2, States.CONNECTING); cdw1.reset(); zk.close(); cdw1.waitForDisconnected(CONNECTION_TIMEOUT); cdw2.waitForConnected(CONNECTION_TIMEOUT); assertSame(zk2.getState(), States.CONNECTED); } finally { zk2.close(); } } finally { zk.close(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientSSLTest.java0100644 0000000 0000000 00000031325 15051152474 033613 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import io.netty.handler.ssl.SslProvider; import java.io.IOException; import java.net.InetAddress; import java.nio.file.Path; import java.util.ArrayList; import java.util.stream.Stream; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.SecretUtilsTest; import org.apache.zookeeper.server.NettyServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; public class ClientSSLTest extends QuorumPeerTestBase { private ClientX509Util clientX509Util; public static Stream positiveTestData() { ArrayList result = new ArrayList<>(); for (SslProvider sslProvider : SslProvider.values()) { for (String fipsEnabled : new String[] { "true", "false" }) { for (String hostnameverification : new String[] { "true", "false" }) { result.add(Arguments.of(sslProvider, fipsEnabled, hostnameverification)); } } } return result.stream(); } public static Stream negativeTestData() { ArrayList result = new ArrayList<>(); for (SslProvider sslProvider : SslProvider.values()) { for (String fipsEnabled : new String[] { "true", "false" }) { result.add(Arguments.of(sslProvider, fipsEnabled)); } } return result.stream(); } @BeforeEach public void setup() { System.setProperty(NettyServerCnxnFactory.PORT_UNIFICATION_KEY, Boolean.TRUE.toString()); clientX509Util = new ClientX509Util(); String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); System.setProperty(clientX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(clientX509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty(clientX509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(clientX509Util.getSslTruststorePasswdProperty(), "testpass"); } @AfterEach public void teardown() { System.clearProperty(NettyServerCnxnFactory.PORT_UNIFICATION_KEY); System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ZKClientConfig.SECURE_CLIENT); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdPathProperty()); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdPathProperty()); System.clearProperty(clientX509Util.getFipsModeProperty()); System.clearProperty(clientX509Util.getSslHostnameVerificationEnabledProperty()); System.clearProperty(clientX509Util.getSslProviderProperty()); clientX509Util.close(); } /** * This test checks that client SSL connections work in the absence of a * secure port when port unification is set up for the plaintext port. * * This single client port will be tested for handling both plaintext * and SSL traffic. */ @Test public void testClientServerUnifiedPort() throws Exception { testClientServerSSL(false); } @Test public void testClientServerUnifiedPortWithCnxnClassName() throws Exception { System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "ClientCnxnSocketNIO"); testClientServerSSL(false); } @Test public void testClientServerSSLWithCnxnClassName() throws Exception { System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "ClientCnxnSocketNetty"); testClientServerSSL(true); } /** * This test checks that client - server SSL works in cluster setup of ZK servers, which includes: * 1. setting "secureClientPort" in "zoo.cfg" file. * 2. setting jvm flags for serverCnxn, keystore, truststore. * Finally, a zookeeper client should be able to connect to the secure port and * communicate with server via secure connection. *

    * Note that in this test a ZK server has two ports -- clientPort and secureClientPort. *

    * This test covers the positive scenarios for hostname verification. */ @ParameterizedTest(name = "sslProvider={0}, fipsEnabled={1}, hostnameVerification={2}") @MethodSource("positiveTestData") public void testClientServerSSL_positive(SslProvider sslProvider, String fipsEnabled, String hostnameVerification) throws Exception { // Arrange System.setProperty(clientX509Util.getSslProviderProperty(), sslProvider.toString()); System.setProperty(clientX509Util.getFipsModeProperty(), fipsEnabled); System.setProperty(clientX509Util.getSslHostnameVerificationEnabledProperty(), hostnameVerification); // Act & Assert testClientServerSSL(hostnameVerification.equals("true") ? "localhost" : InetAddress.getLocalHost().getHostName(), true, CONNECTION_TIMEOUT); } /** * This test covers the negative scenarios for hostname verification. */ @ParameterizedTest(name = "sslProvider={0}, fipsEnabled={1}") @MethodSource("negativeTestData") public void testClientServerSSL_negative(SslProvider sslProvider, boolean fipsEnabled) { // Arrange System.setProperty(clientX509Util.getSslProviderProperty(), sslProvider.toString()); System.setProperty(clientX509Util.getFipsModeProperty(), Boolean.toString(fipsEnabled)); System.setProperty(clientX509Util.getSslHostnameVerificationEnabledProperty(), "true"); // Act & Assert assertThrows(AssertionError.class, () -> testClientServerSSL(InetAddress.getLocalHost().getHostName(), true, 5000)); } @Test public void testClientServerSSL_withPasswordFromFile() throws Exception { final Path secretFile = SecretUtilsTest.createSecretFile("testpass"); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.setProperty(clientX509Util.getSslKeystorePasswdPathProperty(), secretFile.toString()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.setProperty(clientX509Util.getSslTruststorePasswdPathProperty(), secretFile.toString()); testClientServerSSL(true); } public void testClientServerSSL(boolean useSecurePort) throws Exception { testClientServerSSL("localhost", useSecurePort, CONNECTION_TIMEOUT); } public void testClientServerSSL(String hostname, boolean useSecurePort, long connectTimeout) throws Exception { final int SERVER_COUNT = 3; final int[] clientPorts = new int[SERVER_COUNT]; final Integer[] secureClientPorts = new Integer[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); secureClientPorts[i] = PortAssignment.unique(); String server = String.format("server.%d=127.0.0.1:%d:%d:participant;127.0.0.1:%d%n", i, PortAssignment.unique(), PortAssignment.unique(), clientPorts[i]); sb.append(server); } String quorumCfg = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { if (useSecurePort) { mt[i] = new MainThread(i, quorumCfg, secureClientPorts[i], true); } else { mt[i] = new MainThread(i, quorumCfg, true); } mt[i].start(); } // Add some timing margin for the quorum to elect a leader // (without this margin, timeouts have been observed in parallel test runs) ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[0], 2 * TIMEOUT); // Servers have been set up. Now go test if secure connection is successful. for (int i = 0; i < SERVER_COUNT; i++) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], TIMEOUT), "waiting for server " + i + " being up"); final int port = useSecurePort ? secureClientPorts[i] : clientPorts[i]; try (ZooKeeper zk = ClientBase.createZKClient(hostname + ":" + port, TIMEOUT, connectTimeout)) { // Do a simple operation to make sure the connection is fine. zk.create("/test", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/test", -1); } } for (int i = 0; i < mt.length; i++) { mt[i].shutdown(); } } /** * Developers might use standalone mode (which is the default for one server). * This test checks SSL works in standalone mode of ZK server. *

    * Note that in this test the Zk server has only secureClientPort */ @Test public void testSecureStandaloneServer() throws Exception { Integer secureClientPort = PortAssignment.unique(); MainThread mt = new MainThread(MainThread.UNSET_MYID, "", secureClientPort, false); mt.start(); ZooKeeper zk = ClientBase.createZKClient("127.0.0.1:" + secureClientPort, TIMEOUT); zk.create("/test", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/test", -1); zk.close(); mt.shutdown(); } @Test public void testSecureStandaloneServerAuthFail() throws IOException { try { System.setProperty(ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX + "authfail", AuthFailX509AuthenticationProvider.class.getName()); System.setProperty(clientX509Util.getSslAuthProviderProperty(), "authfail"); Integer secureClientPort = PortAssignment.unique(); MainThread mt = new MainThread(MainThread.UNSET_MYID, "", secureClientPort, false); mt.start(); AssertionError ex = assertThrows("Client should not able to connect when authentication fails", AssertionError.class, () -> { ClientBase.createZKClient("localhost:" + secureClientPort, TIMEOUT, 3000); }); assertThat("Exception message does not match (different exception caught?)", ex.getMessage(), startsWith("ZooKeeper client can not connect to")); } finally { System.clearProperty(ProviderRegistry.AUTHPROVIDER_PROPERTY_PREFIX + "authfail"); System.clearProperty(clientX509Util.getSslAuthProviderProperty()); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Clie0100644 0000000 0000000 00000000160 15051152474 032647 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientSkipACLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientSkipACLTest.ja0100644 0000000 0000000 00000002255 15051152474 034051 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; public class ClientSkipACLTest extends ClientTest { @BeforeAll public static void setup() { System.setProperty("zookeeper.skipACL", "yes"); } @AfterAll public static void teardown() { System.clearProperty("zookeeper.skipACL"); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ClientTest.java0100644 0000000 0000000 00000104611 15051152474 033230 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.KeeperException.InvalidACLException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.Perms; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.ExistsRequest; import org.apache.zookeeper.proto.ExistsResponse; import org.apache.zookeeper.proto.ReplyHeader; import org.apache.zookeeper.proto.RequestHeader; import org.apache.zookeeper.server.PrepRequestProcessor; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ClientTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(ClientTest.class); private boolean skipACL = System.getProperty("zookeeper.skipACL", "no").equals("yes"); /** Verify that pings are sent, keeping the "idle" client alive */ @Test public void testPing() throws Exception { ZooKeeper zkIdle = null; ZooKeeper zkWatchCreator = null; try { CountdownWatcher watcher = new CountdownWatcher(); zkIdle = createClient(watcher, hostPort, 10000); zkWatchCreator = createClient(); for (int i = 0; i < 10; i++) { zkWatchCreator.create("/" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } for (int i = 0; i < 10; i++) { zkIdle.exists("/" + i, true); } for (int i = 0; i < 10; i++) { Thread.sleep(1000); zkWatchCreator.delete("/" + i, -1); } // The bug will manifest itself here because zkIdle will expire zkIdle.exists("/0", false); } finally { if (zkIdle != null) { zkIdle.close(); } if (zkWatchCreator != null) { zkWatchCreator.close(); } } } @Test public void testClientwithoutWatcherObj() throws IOException, InterruptedException, KeeperException { performClientTest(false); } @Test public void testClientWithWatcherObj() throws IOException, InterruptedException, KeeperException { performClientTest(true); } /** Exercise the testable functions, verify tostring, etc... */ @Test public void testTestability() throws Exception { TestableZooKeeper zk = createClient(); try { LOG.info("{}", zk.testableLocalSocketAddress()); LOG.info("{}", zk.testableRemoteSocketAddress()); LOG.info("{}", zk.toString()); } finally { zk.close(CONNECTION_TIMEOUT); LOG.info("{}", zk.testableLocalSocketAddress()); LOG.info("{}", zk.testableRemoteSocketAddress()); LOG.info("{}", zk.toString()); } } @Test public void testACLs() throws Exception { ZooKeeper zk = null; try { zk = createClient(); try { zk.create("/acltest", new byte[0], Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Should have received an invalid acl error"); } catch (InvalidACLException e) { LOG.info("Test successful, invalid acl received : {}", e.getMessage()); } try { ArrayList testACL = new ArrayList<>(); testACL.add(new ACL(Perms.ALL | Perms.ADMIN, Ids.AUTH_IDS)); testACL.add(new ACL(Perms.ALL | Perms.ADMIN, new Id("ip", "127.0.0.1/8"))); zk.create("/acltest", new byte[0], testACL, CreateMode.PERSISTENT); fail("Should have received an invalid acl error"); } catch (InvalidACLException e) { LOG.info("Test successful, invalid acl received : {}", e.getMessage()); } try { ArrayList testACL = new ArrayList<>(); testACL.add(new ACL(Perms.ALL | Perms.ADMIN, new Id())); zk.create("/nullidtest", new byte[0], testACL, CreateMode.PERSISTENT); fail("Should have received an invalid acl error"); } catch (InvalidACLException e) { LOG.info("Test successful, invalid acl received : {}", e.getMessage()); } zk.addAuthInfo("digest", "ben:passwd".getBytes()); ArrayList testACL = new ArrayList<>(); testACL.add(new ACL(Perms.ALL, new Id("auth", ""))); testACL.add(new ACL(Perms.WRITE, new Id("ip", "127.0.0.1"))); zk.create("/acltest", new byte[0], testACL, CreateMode.PERSISTENT); zk.close(); zk = createClient(); zk.addAuthInfo("digest", "ben:passwd2".getBytes()); if (skipACL) { try { zk.getData("/acltest", false, null); } catch (KeeperException e) { fail("Badauth reads should succeed with skipACL."); } } else { try { zk.getData("/acltest", false, null); fail("Should have received a permission error"); } catch (KeeperException e) { assertEquals(Code.NOAUTH, e.code()); } } zk.addAuthInfo("digest", "ben:passwd".getBytes()); zk.getData("/acltest", false, null); zk.setACL("/acltest", Ids.OPEN_ACL_UNSAFE, -1); zk.close(); zk = createClient(); zk.getData("/acltest", false, null); List acls = zk.getACL("/acltest", new Stat()); assertEquals(1, acls.size()); assertEquals(Ids.OPEN_ACL_UNSAFE, acls); // The stat parameter should be optional. acls = zk.getACL("/acltest", null); assertEquals(1, acls.size()); assertEquals(Ids.OPEN_ACL_UNSAFE, acls); zk.close(); } finally { if (zk != null) { zk.close(); } } } @Test public void testNullAuthId() throws Exception { ZooKeeper zk = null; try { zk = createClient(); zk.addAuthInfo("digest", "ben:passwd".getBytes()); ArrayList testACL = new ArrayList<>(); testACL.add(new ACL(Perms.ALL, new Id("auth", null))); zk.create("/acltest", new byte[0], testACL, CreateMode.PERSISTENT); zk.close(); zk = createClient(); zk.addAuthInfo("digest", "ben:passwd2".getBytes()); if (skipACL) { try { zk.getData("/acltest", false, null); } catch (KeeperException e) { fail("Badauth reads should succeed with skipACL."); } } else { try { zk.getData("/acltest", false, null); fail("Should have received a permission error"); } catch (KeeperException e) { assertEquals(Code.NOAUTH, e.code()); } } zk.addAuthInfo("digest", "ben:passwd".getBytes()); zk.getData("/acltest", false, null); zk.setACL("/acltest", Ids.OPEN_ACL_UNSAFE, -1); zk.close(); zk = createClient(); zk.getData("/acltest", false, null); List acls = zk.getACL("/acltest", new Stat()); assertEquals(1, acls.size()); assertEquals(Ids.OPEN_ACL_UNSAFE, acls); } finally { if (zk != null) { zk.close(); } } } private class MyWatcher extends CountdownWatcher { LinkedBlockingQueue events = new LinkedBlockingQueue<>(); public void process(WatchedEvent event) { super.process(event); if (event.getType() != EventType.None) { try { events.put(event); } catch (InterruptedException e) { LOG.warn("ignoring interrupt during event.put"); } } } } /** * Register multiple watchers and verify that they all get notified and * in the right order. */ @Test public void testMutipleWatcherObjs() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = createClient(new CountdownWatcher(), hostPort); try { MyWatcher[] watchers = new MyWatcher[100]; MyWatcher[] watchers2 = new MyWatcher[watchers.length]; for (int i = 0; i < watchers.length; i++) { watchers[i] = new MyWatcher(); watchers2[i] = new MyWatcher(); zk.create("/foo-" + i, ("foodata" + i).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } Stat stat = new Stat(); // // test get/exists with single set of watchers // get all, then exists all // for (int i = 0; i < watchers.length; i++) { assertNotNull(zk.getData("/foo-" + i, watchers[i], stat)); } for (int i = 0; i < watchers.length; i++) { assertNotNull(zk.exists("/foo-" + i, watchers[i])); } // trigger the watches for (int i = 0; i < watchers.length; i++) { zk.setData("/foo-" + i, ("foodata2-" + i).getBytes(), -1); zk.setData("/foo-" + i, ("foodata3-" + i).getBytes(), -1); } for (int i = 0; i < watchers.length; i++) { WatchedEvent event = watchers[i].events.poll(10, TimeUnit.SECONDS); assertEquals("/foo-" + i, event.getPath()); assertEquals(EventType.NodeDataChanged, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); // small chance that an unexpected message was delivered // after this check, but we would catch that next time // we check events assertEquals(0, watchers[i].events.size()); } // // test get/exists with single set of watchers // get/exists together // for (int i = 0; i < watchers.length; i++) { assertNotNull(zk.getData("/foo-" + i, watchers[i], stat)); assertNotNull(zk.exists("/foo-" + i, watchers[i])); } // trigger the watches for (int i = 0; i < watchers.length; i++) { zk.setData("/foo-" + i, ("foodata4-" + i).getBytes(), -1); zk.setData("/foo-" + i, ("foodata5-" + i).getBytes(), -1); } for (int i = 0; i < watchers.length; i++) { WatchedEvent event = watchers[i].events.poll(10, TimeUnit.SECONDS); assertEquals("/foo-" + i, event.getPath()); assertEquals(EventType.NodeDataChanged, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); // small chance that an unexpected message was delivered // after this check, but we would catch that next time // we check events assertEquals(0, watchers[i].events.size()); } // // test get/exists with two sets of watchers // for (int i = 0; i < watchers.length; i++) { assertNotNull(zk.getData("/foo-" + i, watchers[i], stat)); assertNotNull(zk.exists("/foo-" + i, watchers2[i])); } // trigger the watches for (int i = 0; i < watchers.length; i++) { zk.setData("/foo-" + i, ("foodata6-" + i).getBytes(), -1); zk.setData("/foo-" + i, ("foodata7-" + i).getBytes(), -1); } for (int i = 0; i < watchers.length; i++) { WatchedEvent event = watchers[i].events.poll(10, TimeUnit.SECONDS); assertEquals("/foo-" + i, event.getPath()); assertEquals(EventType.NodeDataChanged, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); // small chance that an unexpected message was delivered // after this check, but we would catch that next time // we check events assertEquals(0, watchers[i].events.size()); // watchers2 WatchedEvent event2 = watchers2[i].events.poll(10, TimeUnit.SECONDS); assertEquals("/foo-" + i, event2.getPath()); assertEquals(EventType.NodeDataChanged, event2.getType()); assertEquals(KeeperState.SyncConnected, event2.getState()); // small chance that an unexpected message was delivered // after this check, but we would catch that next time // we check events assertEquals(0, watchers2[i].events.size()); } } finally { if (zk != null) { zk.close(); } } } private void performClientTest(boolean withWatcherObj) throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { MyWatcher watcher = new MyWatcher(); zk = createClient(watcher, hostPort); LOG.info("Before create /benwashere"); zk.create("/benwashere", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("After create /benwashere"); try { zk.setData("/benwashere", "hi".getBytes(), 57); fail("Should have gotten BadVersion exception"); } catch (KeeperException.BadVersionException e) { // expected that } catch (KeeperException e) { fail("Should have gotten BadVersion exception"); } LOG.info("Before delete /benwashere"); zk.delete("/benwashere", 0); LOG.info("After delete /benwashere"); zk.close(); Thread.sleep(2000); zk = createClient(watcher, hostPort); LOG.info("Before delete /"); try { zk.delete("/", -1); fail("deleted root!"); } catch (KeeperException.BadArgumentsException e) { // good, expected that } Stat stat = new Stat(); // Test basic create, ls, and getData zk.create("/pat", "Pat was here".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("Before create /ben"); zk.create("/pat/ben", "Ben was here".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); LOG.info("Before getChildren /pat"); List children = zk.getChildren("/pat", false); assertEquals(1, children.size()); assertEquals("ben", children.get(0)); List children2 = zk.getChildren("/pat", false, null); assertEquals(children, children2); String value = new String(zk.getData("/pat/ben", false, stat)); assertEquals("Ben was here", value); // Test stat and watch of non existent node try { if (withWatcherObj) { assertEquals(null, zk.exists("/frog", watcher)); } else { assertEquals(null, zk.exists("/frog", true)); } LOG.info("Comment: asseting passed for frog setting /"); } catch (KeeperException.NoNodeException e) { // OK, expected that } zk.create("/frog", "hi".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // the first poll is just a session delivery LOG.info("Comment: checking for events length {}", watcher.events.size()); WatchedEvent event = watcher.events.poll(10, TimeUnit.SECONDS); assertEquals("/frog", event.getPath()); assertEquals(EventType.NodeCreated, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); // Test child watch and create with sequence zk.getChildren("/pat/ben", true); for (int i = 0; i < 10; i++) { zk.create("/pat/ben/" + i + "-", Integer.toString(i).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } children = zk.getChildren("/pat/ben", false); Collections.sort(children); assertEquals(10, children.size()); for (int i = 0; i < 10; i++) { final String name = children.get(i); assertTrue(name.startsWith(i + "-"), "starts with -"); byte[] b; if (withWatcherObj) { b = zk.getData("/pat/ben/" + name, watcher, stat); } else { b = zk.getData("/pat/ben/" + name, true, stat); } assertEquals(Integer.toString(i), new String(b)); zk.setData("/pat/ben/" + name, "new".getBytes(), stat.getVersion()); if (withWatcherObj) { stat = zk.exists("/pat/ben/" + name, watcher); } else { stat = zk.exists("/pat/ben/" + name, true); } zk.delete("/pat/ben/" + name, stat.getVersion()); } event = watcher.events.poll(10, TimeUnit.SECONDS); assertEquals("/pat/ben", event.getPath()); assertEquals(EventType.NodeChildrenChanged, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); for (int i = 0; i < 10; i++) { event = watcher.events.poll(10, TimeUnit.SECONDS); final String name = children.get(i); assertEquals("/pat/ben/" + name, event.getPath()); assertEquals(EventType.NodeDataChanged, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); event = watcher.events.poll(10, TimeUnit.SECONDS); assertEquals("/pat/ben/" + name, event.getPath()); assertEquals(EventType.NodeDeleted, event.getType()); assertEquals(KeeperState.SyncConnected, event.getState()); } zk.create("/good\u0040path", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/duplicate", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.create("/duplicate", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("duplicate create allowed"); } catch (KeeperException.NodeExistsException e) { // OK, expected that } } finally { if (zk != null) { zk.close(); } } } // Test that sequential filenames are being created correctly, // with 0-padding in the filename @Test public void testSequentialNodeNames() throws IOException, InterruptedException, KeeperException { String path = "/SEQUENCE"; String file = "TEST"; String filepath = path + "/" + file; ZooKeeper zk = null; try { zk = createClient(); zk.create(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(filepath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); List children = zk.getChildren(path, false); assertEquals(1, children.size()); assertEquals(file + "0000000000", children.get(0)); zk.create(filepath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); children = zk.getChildren(path, false); assertEquals(2, children.size()); assertTrue(children.contains(file + "0000000001"), "contains child 1"); zk.create(filepath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); children = zk.getChildren(path, false); assertEquals(3, children.size()); assertTrue(children.contains(file + "0000000002"), "contains child 2"); // The pattern is holding so far. Let's run the counter a bit // to be sure it continues to spit out the correct answer for (int i = children.size(); i < 105; i++) { zk.create(filepath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } children = zk.getChildren(path, false); assertTrue(children.contains(file + "0000000104"), "contains child 104"); } finally { if (zk != null) { zk.close(); } } } // Test that data provided when // creating sequential nodes is stored properly @Test public void testSequentialNodeData() throws Exception { ZooKeeper zk = null; String queue_handle = "/queue"; try { zk = createClient(); zk.create(queue_handle, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(queue_handle + "/element", "0".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); zk.create(queue_handle + "/element", "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); List children = zk.getChildren(queue_handle, true); assertEquals(children.size(), 2); String child1 = children.get(0); String child2 = children.get(1); int compareResult = child1.compareTo(child2); assertNotSame(compareResult, 0); if (compareResult < 0) { } else { String temp = child1; child1 = child2; child2 = temp; } String child1data = new String(zk.getData(queue_handle + "/" + child1, false, null)); String child2data = new String(zk.getData(queue_handle + "/" + child2, false, null)); assertEquals(child1data, "0"); assertEquals(child2data, "1"); } finally { if (zk != null) { zk.close(); } } } @Test public void testLargeNodeData() throws Exception { ZooKeeper zk = null; String queue_handle = "/large"; try { zk = createClient(); zk.create(queue_handle, new byte[500000], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } finally { if (zk != null) { zk.close(); } } } private void verifyCreateFails(String path, ZooKeeper zk) throws Exception { try { zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (IllegalArgumentException e) { // this is good return; } fail("bad path \"" + path + "\" not caught"); } // Test that the path string is validated @Test public void testPathValidation() throws Exception { ZooKeeper zk = createClient(); verifyCreateFails(null, 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("/\u0001foo", zk); verifyCreateFails("/foo/bar/", zk); verifyCreateFails("/foo//bar", zk); verifyCreateFails("/foo/bar//", zk); verifyCreateFails("foo", zk); verifyCreateFails("a", zk); zk.create("/createseqpar", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // next two steps - related to sequential processing // 1) verify that empty child name fails if not sequential try { zk.create("/createseqpar/", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertTrue(false); } catch (IllegalArgumentException be) { // catch this. } // 2) verify that empty child name success if sequential zk.create("/createseqpar/", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); zk.create("/createseqpar/.", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); zk.create("/createseqpar/..", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); try { zk.create("/createseqpar//", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); assertTrue(false); } catch (IllegalArgumentException be) { // catch this. } try { zk.create("/createseqpar/./", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); assertTrue(false); } catch (IllegalArgumentException be) { // catch this. } try { zk.create("/createseqpar/../", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); assertTrue(false); } catch (IllegalArgumentException be) { // catch this. } //check for the code path that throws at server PrepRequestProcessor.setFailCreate(true); try { zk.create("/m", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertTrue(false); } catch (KeeperException.BadArgumentsException be) { // catch this. } PrepRequestProcessor.setFailCreate(false); zk.create("/.foo", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/.f.", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/..f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/..f..", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f.c", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f\u0040f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f/.f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f/f.", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f/..f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f/f..", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f/.f/f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/f/f./f", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } @Test public void testDeleteWithChildren() throws Exception { ZooKeeper zk = createClient(); zk.create("/parent", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/parent/child", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.delete("/parent", -1); fail("Should have received a not equals message"); } catch (KeeperException e) { assertEquals(KeeperException.Code.NOTEMPTY, e.code()); } zk.delete("/parent/child", -1); zk.delete("/parent", -1); zk.close(); } private class VerifyClientCleanup extends Thread { int count; int current = 0; VerifyClientCleanup(String name, int count) { super(name); this.count = count; } public void run() { try { for (; current < count; current++) { TestableZooKeeper zk = createClient(); // we've asked to close, wait for it to finish closing // all the sub-threads otw the selector may not be // closed when we check (false positive on test failure zk.close(CONNECTION_TIMEOUT); } } catch (Throwable t) { LOG.error("test failed", t); } } } /** * Verify that the client is cleaning up properly. Open/close a large * number of sessions. Essentially looking to see if sockets/selectors * are being cleaned up properly during close. * * @throws Throwable */ @Test public void testClientCleanup() throws Throwable { OSMXBean osMbean = new OSMXBean(); if (!osMbean.getUnix()) { LOG.warn("skipping testClientCleanup, only available on Unix"); return; } final int threadCount = 3; final int clientCount = 10; /* Log the number of fds used before and after a test is run. Verifies * we are freeing resources correctly. Unfortunately this only works * on unix systems (the only place sun has implemented as part of the * mgmt bean api). */ long initialFdCount = osMbean.getOpenFileDescriptorCount(); VerifyClientCleanup[] threads = new VerifyClientCleanup[threadCount]; for (int i = 0; i < threads.length; i++) { threads[i] = new VerifyClientCleanup("VCC" + i, clientCount); threads[i].start(); } for (int i = 0; i < threads.length; i++) { threads[i].join(CONNECTION_TIMEOUT); assertTrue(threads[i].current == threads[i].count); } // if this fails it means we are not cleaning up after the closed // sessions. long currentCount = osMbean.getOpenFileDescriptorCount(); final String logmsg = "open fds after test ({}) are not significantly higher than before ({})"; if (currentCount > initialFdCount + 10) { // consider as error LOG.error(logmsg, currentCount, initialFdCount); } else { LOG.info(logmsg, currentCount, initialFdCount); } } /** * We create a perfectly valid 'exists' request, except that the opcode is wrong. * @throws Exception */ @Test public void testNonExistingOpCode() throws Exception { final CountDownLatch clientDisconnected = new CountDownLatch(1); Watcher watcher = new Watcher() { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.Disconnected) { clientDisconnected.countDown(); } } }; TestableZooKeeper zk = new TestableZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); final String path = "/m1"; RequestHeader h = new RequestHeader(); h.setType(888); // This code does not exists ExistsRequest request = new ExistsRequest(); request.setPath(path); request.setWatch(false); ExistsResponse response = new ExistsResponse(); ReplyHeader r = zk.submitRequest(h, request, response, null); assertEquals(r.getErr(), Code.UNIMPLEMENTED.intValue()); // Sending a nonexisting opcode should cause the server to disconnect assertTrue(clientDisconnected.await(5000, TimeUnit.MILLISECONDS), "failed to disconnect"); zk.close(); } @Test public void testTryWithResources() throws Exception { ZooKeeper zooKeeper; try (ZooKeeper zk = createClient()) { zooKeeper = zk; assertTrue(zooKeeper.getState().isAlive()); } assertFalse(zooKeeper.getState().isAlive()); } @Test public void testCXidRollover() throws Exception { TestableZooKeeper zk = null; try { zk = createClient(); zk.setXid(Integer.MAX_VALUE - 10); zk.create("/testnode", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < 20; ++i) { final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger rc = new AtomicInteger(0); zk.setData("/testnode", "".getBytes(), -1, (retcode, path, ctx, stat) -> { rc.set(retcode); latch.countDown(); }, null); assertTrue(latch.await(zk.getSessionTimeout(), TimeUnit.MILLISECONDS), "setData should complete within 5s"); assertEquals(Code.OK.intValue(), rc.get(), "setData should have succeeded"); } zk.delete("/testnode", -1); assertTrue(zk.checkXid() > 0, "xid should be positive"); } finally { if (zk != null) { zk.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Conn0100644 0000000 0000000 00000000166 15051152474 032676 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ConnectStringParserTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ConnectStringParserT0100644 0000000 0000000 00000012171 15051152474 034312 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.client.ConnectStringParser; import org.junit.jupiter.api.Test; public class ConnectStringParserTest extends ZKTestCase { private static final int DEFAULT_PORT = 2181; @Test public void testSingleServerChrootPath() { String chrootPath = "/hallo/welt"; String servers = "10.10.10.1"; assertChrootPath(chrootPath, new ConnectStringParser(servers + chrootPath)); servers = "[2001:db8:1::242:ac11:2]"; assertChrootPath(chrootPath, new ConnectStringParser(servers + chrootPath)); } @Test public void testMultipleServersChrootPath() { String chrootPath = "/hallo/welt"; String servers = "10.10.10.1,10.10.10.2"; assertChrootPath(chrootPath, new ConnectStringParser(servers + chrootPath)); servers = "[2001:db8:1::242:ac11:2]:2181,[2001:db8:85a3:8d3:1319:8a2e:370:7348]:5678"; assertChrootPath(chrootPath, new ConnectStringParser(servers + chrootPath)); } @Test public void testParseServersWithoutPort() { String servers = "10.10.10.1,10.10.10.2"; ConnectStringParser parser = new ConnectStringParser(servers); assertEquals("10.10.10.1", parser.getServerAddresses().get(0).getHostString()); assertEquals(DEFAULT_PORT, parser.getServerAddresses().get(0).getPort()); assertEquals("10.10.10.2", parser.getServerAddresses().get(1).getHostString()); assertEquals(DEFAULT_PORT, parser.getServerAddresses().get(1).getPort()); servers = "[2001:db8:1::242:ac11:2],[2001:db8:85a3:8d3:1319:8a2e:370:7348]"; parser = new ConnectStringParser(servers); assertEquals("2001:db8:1::242:ac11:2", parser.getServerAddresses().get(0).getHostString()); assertEquals(DEFAULT_PORT, parser.getServerAddresses().get(0).getPort()); assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348", parser.getServerAddresses().get(1).getHostString()); assertEquals(DEFAULT_PORT, parser.getServerAddresses().get(1).getPort()); } @Test public void testParseServersWithPort() { String servers = "10.10.10.1:112,10.10.10.2:110"; ConnectStringParser parser = new ConnectStringParser(servers); assertEquals("10.10.10.1", parser.getServerAddresses().get(0).getHostString()); assertEquals("10.10.10.2", parser.getServerAddresses().get(1).getHostString()); assertEquals(112, parser.getServerAddresses().get(0).getPort()); assertEquals(110, parser.getServerAddresses().get(1).getPort()); servers = "[2001:db8:1::242:ac11:2]:1234,[2001:db8:85a3:8d3:1319:8a2e:370:7348]:5678"; parser = new ConnectStringParser(servers); assertEquals("2001:db8:1::242:ac11:2", parser.getServerAddresses().get(0).getHostString()); assertEquals("2001:db8:85a3:8d3:1319:8a2e:370:7348", parser.getServerAddresses().get(1).getHostString()); assertEquals(1234, parser.getServerAddresses().get(0).getPort()); assertEquals(5678, parser.getServerAddresses().get(1).getPort()); } private void assertChrootPath(String expected, ConnectStringParser parser) { assertEquals(expected, parser.getChrootPath()); } @Test public void testParseIPV6ConnectionString() { String servers = "[127::1],127.0.10.2"; ConnectStringParser parser = new ConnectStringParser(servers); assertEquals("127::1", parser.getServerAddresses().get(0).getHostString()); assertEquals("127.0.10.2", parser.getServerAddresses().get(1).getHostString()); assertEquals(2181, parser.getServerAddresses().get(0).getPort()); assertEquals(2181, parser.getServerAddresses().get(1).getPort()); servers = "[127::1]:2181,[127::2]:2182,[127::3]:2183"; parser = new ConnectStringParser(servers); assertEquals("127::1", parser.getServerAddresses().get(0).getHostString()); assertEquals("127::2", parser.getServerAddresses().get(1).getHostString()); assertEquals("127::3", parser.getServerAddresses().get(2).getHostString()); assertEquals(2181, parser.getServerAddresses().get(0).getPort()); assertEquals(2182, parser.getServerAddresses().get(1).getPort()); assertEquals(2183, parser.getServerAddresses().get(2).getPort()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/CreateModeTest.java0100644 0000000 0000000 00000006332 15051152474 034023 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.EnumSet; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class CreateModeTest extends ZKTestCase { @Test public void testBasicCreateMode() { CreateMode cm = CreateMode.PERSISTENT; assertEquals(cm.toFlag(), 0); assertFalse(cm.isEphemeral()); assertFalse(cm.isSequential()); assertFalse(cm.isContainer()); cm = CreateMode.EPHEMERAL; assertEquals(cm.toFlag(), 1); assertTrue(cm.isEphemeral()); assertFalse(cm.isSequential()); assertFalse(cm.isContainer()); cm = CreateMode.PERSISTENT_SEQUENTIAL; assertEquals(cm.toFlag(), 2); assertFalse(cm.isEphemeral()); assertTrue(cm.isSequential()); assertFalse(cm.isContainer()); cm = CreateMode.EPHEMERAL_SEQUENTIAL; assertEquals(cm.toFlag(), 3); assertTrue(cm.isEphemeral()); assertTrue(cm.isSequential()); assertFalse(cm.isContainer()); cm = CreateMode.CONTAINER; assertEquals(cm.toFlag(), 4); assertFalse(cm.isEphemeral()); assertFalse(cm.isSequential()); assertTrue(cm.isContainer()); } @Test public void testFlagConversion() throws KeeperException { // Ensure we get the same value back after round trip conversion EnumSet allModes = EnumSet.allOf(CreateMode.class); for (CreateMode cm : allModes) { assertEquals(cm, CreateMode.fromFlag(cm.toFlag())); } } @Test public void testInvalidFlagConversion() throws KeeperException { try { CreateMode.fromFlag(99); fail("Shouldn't be able to convert 99 to a CreateMode."); } catch (KeeperException ke) { assertEquals(Code.BADARGUMENTS, ke.code()); } try { CreateMode.fromFlag(-1); fail("Shouldn't be able to convert -1 to a CreateMode."); } catch (KeeperException ke) { assertEquals(Code.BADARGUMENTS, ke.code()); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/CreateTest.java0100644 0000000 0000000 00000010577 15051152474 033224 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Test suite for validating the Create API. */ public class CreateTest extends ClientBase { private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); zk.close(); } @Test public void testCreate() throws IOException, KeeperException, InterruptedException { createNoStatVerifyResult("/foo"); createNoStatVerifyResult("/foo/child"); } @Test public void testCreateWithStat() throws IOException, KeeperException, InterruptedException { String name = "/foo"; Stat stat = createWithStatVerifyResult("/foo"); Stat childStat = createWithStatVerifyResult("/foo/child"); // Don't expect to get the same stats for different creates. assertFalse(stat.equals(childStat)); } @Test public void testCreateWithNullStat() throws IOException, KeeperException, InterruptedException { String name = "/foo"; assertNull(zk.exists(name, false)); Stat stat = null; // If a null Stat object is passed the create should still // succeed, but no Stat info will be returned. String path = zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertNull(stat); assertNotNull(zk.exists(name, false)); } private void createNoStatVerifyResult(String newName) throws KeeperException, InterruptedException { assertNull(zk.exists(newName, false), "Node existed before created"); String path = zk.create(newName, newName.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(path, newName); assertNotNull(zk.exists(newName, false), "Node was not created as expected"); } private Stat createWithStatVerifyResult(String newName) throws KeeperException, InterruptedException { assertNull(zk.exists(newName, false), "Node existed before created"); Stat stat = new Stat(); String path = zk.create(newName, newName.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEquals(path, newName); validateCreateStat(stat, newName); Stat referenceStat = zk.exists(newName, false); assertNotNull(referenceStat, "Node was not created as expected"); assertEquals(referenceStat, stat); return stat; } private void validateCreateStat(Stat stat, String name) { assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(0, stat.getNumChildren()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Dige0100644 0000000 0000000 00000000165 15051152474 032650 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DigestAuthDisabledTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DigestAuthDisabledTe0100644 0000000 0000000 00000005507 15051152474 034220 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class DigestAuthDisabledTest extends ClientBase { @BeforeAll public static void setUpEnvironment() { System.setProperty("zookeeper.DigestAuthenticationProvider.enabled", "false"); } @AfterAll public static void cleanUpEnvironment() { System.clearProperty("zookeeper.DigestAuthenticationProvider.enabled"); } private final CountDownLatch authFailed = new CountDownLatch(1); @Override protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { MyWatcher watcher = new MyWatcher(); return createClient(watcher, hp); } private class MyWatcher extends CountdownWatcher { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { authFailed.countDown(); } else { super.process(event); } } } @Test public void testDigestAuthDisabledTriggersAuthFailed() throws Exception { ZooKeeper zk = createClient(); try { zk.addAuthInfo("digest", "roger:muscadet".getBytes()); zk.getData("/path1", false, null); fail("Should get auth state error"); } catch (KeeperException.AuthFailedException e) { if (!authFailed.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Should have called my watcher"); } } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Disc0100644 0000000 0000000 00000000166 15051152474 032663 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DisconnectableZooKeeper.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DisconnectableZooKee0100644 0000000 0000000 00000004074 15051152474 034266 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import java.io.IOException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; public class DisconnectableZooKeeper extends ZooKeeper { public DisconnectableZooKeeper(String host, int sessionTimeout, Watcher watcher) throws IOException { super(host, sessionTimeout, watcher); } public DisconnectableZooKeeper( String host, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd) throws IOException { super(host, sessionTimeout, watcher, sessionId, sessionPasswd); } /** Testing only!!! Really!!!! This is only here to test when the client * disconnects from the server w/o sending a session disconnect (ie * ending the session cleanly). The server will eventually notice the * client is no longer pinging and will timeout the session. */ public void disconnect() throws IOException { cnxn.disconnect(); } /** * Prevent the client from automatically reconnecting if the connection to the * server is lost */ public void dontReconnect() throws Exception { java.lang.reflect.Field f = cnxn.getClass().getDeclaredField("closing"); f.setAccessible(true); f.setBoolean(cnxn, true); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Disc0100644 0000000 0000000 00000000166 15051152474 032663 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DisconnectedWatcherTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DisconnectedWatcherT0100644 0000000 0000000 00000023242 15051152474 034276 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DisconnectedWatcherTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(DisconnectedWatcherTest.class); final int TIMEOUT = 5000; private class MyWatcher extends CountdownWatcher { LinkedBlockingQueue events = new LinkedBlockingQueue<>(); public void process(WatchedEvent event) { super.process(event); if (event.getType() != Event.EventType.None) { try { events.put(event); } catch (InterruptedException e) { LOG.warn("ignoring interrupt during event.put"); } } } } private CountdownWatcher watcher1; private ZooKeeper zk1; private MyWatcher watcher2; private ZooKeeper zk2; @BeforeEach public void setUp() throws Exception { super.setUp(); watcher1 = new CountdownWatcher(); zk1 = createClient(watcher1); watcher2 = new MyWatcher(); } @AfterEach public void tearDown() throws Exception { if (zk2 != null) { zk2.close(); } if (zk1 != null) { zk1.close(); } super.tearDown(); } // @see jira issue ZOOKEEPER-961 @Test public void testChildWatcherAutoResetWithChroot() throws Exception { zk1.create("/ch1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk2 = createClient(watcher2, hostPort + "/ch1"); zk2.getChildren("/", true); // this call shouldn't trigger any error or watch zk1.create("/youdontmatter1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // this should trigger the watch zk1.create("/ch1/youshouldmatter1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WatchedEvent e = watcher2.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/", e.getPath()); MyWatcher childWatcher = new MyWatcher(); zk2.getChildren("/", childWatcher); stopServer(); watcher2.waitForDisconnected(3000); startServer(); watcher2.waitForConnected(3000); watcher1.waitForConnected(3000); // this should trigger the watch zk1.create("/ch1/youshouldmatter2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/", e.getPath()); } @Test public void testDefaultWatcherAutoResetWithChroot() throws Exception { zk1.create("/ch1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk2 = createClient(watcher2, hostPort + "/ch1"); zk2.getChildren("/", true); // this call shouldn't trigger any error or watch zk1.create("/youdontmatter1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // this should trigger the watch zk1.create("/ch1/youshouldmatter1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WatchedEvent e = watcher2.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/", e.getPath()); zk2.getChildren("/", true); stopServer(); watcher2.waitForDisconnected(3000); startServer(); watcher2.waitForConnected(3000); watcher1.waitForConnected(3000); // this should trigger the watch zk1.create("/ch1/youshouldmatter2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); e = watcher2.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/", e.getPath()); } @Test public void testDeepChildWatcherAutoResetWithChroot() throws Exception { zk1.create("/ch1", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.create("/ch1/here", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.create("/ch1/here/we", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.create("/ch1/here/we/are", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk2 = createClient(watcher2, hostPort + "/ch1/here/we"); zk2.getChildren("/are", true); // this should trigger the watch zk1.create("/ch1/here/we/are/now", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WatchedEvent e = watcher2.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/are", e.getPath()); MyWatcher childWatcher = new MyWatcher(); zk2.getChildren("/are", childWatcher); stopServer(); watcher2.waitForDisconnected(3000); startServer(); watcher2.waitForConnected(3000); watcher1.waitForConnected(3000); // this should trigger the watch zk1.create("/ch1/here/we/are/again", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/are", e.getPath()); } // @see jira issue ZOOKEEPER-706. Test auto reset of a large number of // watches which require multiple SetWatches calls. @Test @Timeout(value = 14, unit = TimeUnit.MINUTES) public void testManyChildWatchersAutoReset() throws Exception { zk2 = createClient(watcher2); // 110 character base path String pathBase = "/long-path-000000000-111111111-222222222-333333333-444444444-" + "555555555-666666666-777777777-888888888-999999999"; zk1.create(pathBase, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // Create 10,000 nodes. This should ensure the length of our // watches set below exceeds 1MB. List paths = new ArrayList<>(); for (int i = 0; i < 10000; i++) { String path = zk1.create(pathBase + "/ch-", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); paths.add(path); } LOG.info("Created 10,000 nodes."); MyWatcher childWatcher = new MyWatcher(); // Set a combination of child/exists/data watches int i = 0; for (String path : paths) { if (i % 3 == 0) { zk2.getChildren(path, childWatcher); } else if (i % 3 == 1) { zk2.exists(path + "/foo", childWatcher); } else if (i % 3 == 2) { zk2.getData(path, childWatcher, null); } i++; } stopServer(); watcher2.waitForDisconnected(30000); startServer(); watcher2.waitForConnected(30000); watcher1.waitForConnected(30000); // Trigger the watches and ensure they properly propagate to the client i = 0; for (String path : paths) { if (i % 3 == 0) { zk1.create(path + "/ch", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WatchedEvent e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals(path, e.getPath()); } else if (i % 3 == 1) { zk1.create(path + "/foo", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WatchedEvent e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeCreated, e.getType()); assertEquals(path + "/foo", e.getPath()); } else if (i % 3 == 2) { zk1.setData(path, new byte[] { 1, 2, 3 }, -1); WatchedEvent e = childWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertNotNull(e); assertEquals(EventType.NodeDataChanged, e.getType()); assertEquals(path, e.getPath()); } i++; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Dupl0100644 0000000 0000000 00000000177 15051152474 032707 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DuplicateLocalSessionUpgradeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/DuplicateLocalSessio0100644 0000000 0000000 00000010327 15051152474 034305 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * When request are route incorrectly, both follower and the leader will perform * local session upgrade. So we saw CreateSession twice in txnlog This doesn't * affect the correctness but cause the ensemble to see more load than * necessary. */ public class DuplicateLocalSessionUpgradeTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(DuplicateLocalSessionUpgradeTest.class); private final QuorumBase qb = new QuorumBase(); private static final int CONNECTION_TIMEOUT = ClientBase.CONNECTION_TIMEOUT; @BeforeEach public void setUp() throws Exception { LOG.info("STARTING quorum {}", getClass().getName()); qb.localSessionsEnabled = true; qb.localSessionsUpgradingEnabled = true; qb.setUp(); ClientBase.waitForServerUp(qb.hostPort, 10000); } @AfterEach public void tearDown() throws Exception { LOG.info("STOPPING quorum {}", getClass().getName()); qb.tearDown(); } @Test public void testLocalSessionUpgradeOnFollower() throws Exception { testLocalSessionUpgrade(false); } @Test public void testLocalSessionUpgradeOnLeader() throws Exception { testLocalSessionUpgrade(true); } private void testLocalSessionUpgrade(boolean testLeader) throws Exception { int leaderIdx = qb.getLeaderIndex(); assertFalse(leaderIdx == -1, "No leader in quorum?"); int followerIdx = (leaderIdx + 1) % 5; int testPeerIdx = testLeader ? leaderIdx : followerIdx; String[] hostPorts = qb.hostPort.split(","); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = qb.createClient(watcher, hostPorts[testPeerIdx], CONNECTION_TIMEOUT); watcher.waitForConnected(CONNECTION_TIMEOUT); final String firstPath = "/first"; final String secondPath = "/ephemeral"; // Just create some node so that we know the current zxid zk.create(firstPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // Now, try an ephemeral node. This will trigger session upgrade // so there will be createSession request inject into the pipeline // prior to this request zk.create(secondPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); Stat firstStat = zk.exists(firstPath, null); assertNotNull(firstStat); Stat secondStat = zk.exists(secondPath, null); assertNotNull(secondStat); long zxidDiff = secondStat.getCzxid() - firstStat.getCzxid(); // If there is only one createSession request in between, zxid diff // will be exactly 2. The alternative way of checking is to actually // read txnlog but this should be sufficient assertEquals(2L, zxidDiff); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Empt0100644 0000000 0000000 00000000172 15051152474 032703 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EmptiedSnapshotRecoveryTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EmptiedSnapshotRecov0100644 0000000 0000000 00000020167 15051152474 034343 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** If snapshots are corrupted to the empty file or deleted, Zookeeper should * not proceed to read its transaction log files * Test that zxid == -1 in the presence of emptied/deleted snapshots */ public class EmptiedSnapshotRecoveryTest extends ZKTestCase implements Watcher { private static final Logger LOG = LoggerFactory.getLogger(RestoreCommittedLogTest.class); private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final int CONNECTION_TIMEOUT = 3000; private static final int N_TRANSACTIONS = 150; private static final int SNAP_COUNT = 100; public void runTest(boolean leaveEmptyFile, boolean trustEmptySnap) throws Exception { File tmpSnapDir = ClientBase.createTmpDir(); File tmpLogDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpSnapDir, tmpLogDir, 3000); SyncRequestProcessor.setSnapCount(SNAP_COUNT); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); try { for (int i = 0; i < N_TRANSACTIONS; i++) { zk.create("/node-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server to shutdown"); // start server again with intact database zks = new ZooKeeperServer(tmpSnapDir, tmpLogDir, 3000); zks.startdata(); long zxid = zks.getZKDatabase().getDataTreeLastProcessedZxid(); LOG.info("After clean restart, zxid = {}", zxid); assertTrue(zxid > 0, "zxid > 0"); zks.shutdown(); // Make all snapshots empty FileTxnSnapLog txnLogFactory = zks.getTxnLogFactory(); List snapshots = txnLogFactory.findNRecentSnapshots(10); assertTrue(snapshots.size() > 0, "We have a snapshot to corrupt"); for (File file : snapshots) { if (leaveEmptyFile) { new PrintWriter(file).close(); } else { file.delete(); } } if (trustEmptySnap) { System.setProperty(FileTxnSnapLog.ZOOKEEPER_SNAPSHOT_TRUST_EMPTY, "true"); } // start server again with corrupted database zks = new ZooKeeperServer(tmpSnapDir, tmpLogDir, 3000); try { zks.startdata(); long currentZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid(); if (!trustEmptySnap) { fail("Should have gotten exception for corrupted database"); } assertEquals(currentZxid, zxid, "zxid mismatch after restoring database"); } catch (IOException e) { // expected behavior if (trustEmptySnap) { fail("Should not get exception for empty database"); } } finally { if (trustEmptySnap) { System.clearProperty(FileTxnSnapLog.ZOOKEEPER_SNAPSHOT_TRUST_EMPTY); } } zks.shutdown(); } /** * Test resilience to empty Snapshots * @throws Exception an exception might be thrown here */ @Test public void testRestoreWithEmptySnapFiles() throws Exception { runTest(true, false); } /** * Test resilience to deletion of Snapshots * @throws Exception an exception might be thrown here */ @Test public void testRestoreWithNoSnapFiles() throws Exception { runTest(false, false); } @Test public void testRestoreWithTrustedEmptySnapFiles() throws Exception { runTest(false, true); } @Test public void testRestoreWithTrustedEmptySnapFilesWhenFollowing() throws Exception { QuorumUtil qu = new QuorumUtil(1); try { qu.startAll(); String connString = qu.getConnectionStringForServer(1); try (ZooKeeper zk = new ZooKeeper(connString, CONNECTION_TIMEOUT, this)) { for (int i = 0; i < N_TRANSACTIONS; i++) { zk.create("/node-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } int leaderIndex = qu.getLeaderServer(); //Shut down the cluster and delete the snapshots from the followers for (int i = 1; i <= qu.ALL; i++) { qu.shutdown(i); if (i != leaderIndex) { FileTxnSnapLog txnLogFactory = qu.getPeer(i).peer.getTxnFactory(); List snapshots = txnLogFactory.findNRecentSnapshots(10); assertTrue(snapshots.size() > 0, "We have a snapshot to corrupt"); for (File file : snapshots) { Files.delete(file.toPath()); } assertEquals(txnLogFactory.findNRecentSnapshots(10).size(), 0); } } //Start while trusting empty snapshots, verify that the followers save snapshots System.setProperty(FileTxnSnapLog.ZOOKEEPER_SNAPSHOT_TRUST_EMPTY, "true"); qu.start(leaderIndex); for (int i = 1; i <= qu.ALL; i++) { if (i != leaderIndex) { qu.restart(i); FileTxnSnapLog txnLogFactory = qu.getPeer(i).peer.getTxnFactory(); List snapshots = txnLogFactory.findNRecentSnapshots(10); assertTrue(snapshots.size() > 0, "A snapshot should have been created on follower " + i); } } //Check that the created nodes are still there try (ZooKeeper zk = new ZooKeeper(connString, CONNECTION_TIMEOUT, this)) { for (int i = 0; i < N_TRANSACTIONS; i++) { assertNotNull(zk.exists("/node-" + i, false)); } } } finally { System.clearProperty(FileTxnSnapLog.ZOOKEEPER_SNAPSHOT_TRUST_EMPTY); qu.tearDown(); } } public void process(WatchedEvent event) { // do nothing } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Enfo0100644 0000000 0000000 00000000157 15051152474 032670 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EnforceQuotaTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EnforceQuotaTest.jav0100644 0000000 0000000 00000006577 15051152474 034260 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.Assert.fail; import java.util.UUID; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.cli.SetQuotaCommand; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * An unit case when Enforce Quota disables by default */ public class EnforceQuotaTest extends ClientBase { private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { System.clearProperty(ZooKeeperServer.ENFORCE_QUOTA); super.tearDown(); zk.close(); } @Test public void testSetQuotaDisableWhenExceedBytesHardQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "12345".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack st = new StatsTrack(); st.setByteHardLimit(5L); SetQuotaCommand.createQuota(zk, path, st); try { zk.setData(path, "123456".getBytes(), -1); ZooKeeperQuotaTest.validateNoQuotaExceededMetrics(namespace); } catch (KeeperException.QuotaExceededException e) { fail("should not throw Byte Quota Exceeded Exception when enforce quota disables"); } } @Test public void testSetQuotaDisableWhenExceedCountHardQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 2; StatsTrack st = new StatsTrack(); st.setCountHardLimit(count); SetQuotaCommand.createQuota(zk, path, st); zk.create(path + "/c2", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.create(path + "/c2" + "/c3", "data".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); ZooKeeperQuotaTest.validateNoQuotaExceededMetrics(namespace); } catch (KeeperException.QuotaExceededException e) { fail("should not throw Count Quota Exceeded Exception when enforce quota disables"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Ense0100644 0000000 0000000 00000000157 15051152474 032673 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EnsembleAuthTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EnsembleAuthTest.jav0100644 0000000 0000000 00000007773 15051152474 034240 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.auth.EnsembleAuthenticationProvider; import org.apache.zookeeper.server.auth.ProviderRegistry; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class EnsembleAuthTest extends ClientBase { @BeforeEach public void setUp() throws Exception { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.EnsembleAuthenticationProvider"); super.setUp(); } @AfterEach public void tearDown() throws Exception { super.tearDown(); System.clearProperty("zookeeper.authProvider.1"); System.clearProperty(EnsembleAuthenticationProvider.ENSEMBLE_PROPERTY); ProviderRegistry.removeProvider("ensemble"); } @Test public void noAuth() throws Exception { resetEnsembleAuth(null, false); connectToEnsemble(null); } @Test public void emptyAuth() throws Exception { resetEnsembleAuth(null, true); connectToEnsemble("foo"); } @Test public void skipAuth() throws Exception { resetEnsembleAuth("woo", true); connectToEnsemble(null); } @Test public void passAuth() throws Exception { resetEnsembleAuth("woo", true); connectToEnsemble("woo"); } @Test public void passAuthCSV() throws Exception { resetEnsembleAuth(" foo,bar, baz ", true); connectToEnsemble("foo"); connectToEnsemble("bar"); connectToEnsemble("baz"); } @Test public void failAuth() { assertThrows(KeeperException.ConnectionLossException.class, () -> { resetEnsembleAuth("woo", true); connectToEnsemble("goo"); }); } @Test public void removeEnsembleAuthProvider() { assertThrows(KeeperException.AuthFailedException.class, () -> { resetEnsembleAuth(null, false); connectToEnsemble("goo"); }); } private void connectToEnsemble(final String auth) throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient()) { // pass auth check if (auth != null) { zk.addAuthInfo("ensemble", auth.getBytes()); } zk.getData("/", false, null); } } private void resetEnsembleAuth(final String auth, final boolean useAuth) throws Exception { stopServer(); if (auth == null) { System.clearProperty(EnsembleAuthenticationProvider.ENSEMBLE_PROPERTY); } else { System.setProperty(EnsembleAuthenticationProvider.ENSEMBLE_PROPERTY, auth); } if (useAuth) { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.EnsembleAuthenticationProvider"); } else { System.clearProperty("zookeeper.authProvider.1"); } ProviderRegistry.removeProvider("ensemble"); ProviderRegistry.initialize(); startServer(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/EventTypeTest.java0100644 0000000 0000000 00000003322 15051152474 033732 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.util.EnumSet; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class EventTypeTest extends ZKTestCase { @Test public void testIntConversion() { // Ensure that we can convert all valid integers to EventTypes EnumSet allTypes = EnumSet.allOf(EventType.class); for (EventType et : allTypes) { assertEquals(et, EventType.fromInt(et.getIntValue())); } } @Test public void testInvalidIntConversion() { try { EventType.fromInt(324242); fail("Was able to create an invalid EventType via an integer"); } catch (RuntimeException re) { // we're good. } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_FLEN0100644 0000000 0000000 00000000156 15051152474 032524 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLENewEpochTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLENewEpochTest.java0100644 0000000 0000000 00000014500 15051152474 034046 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.Semaphore; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.Vote; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLENewEpochTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLENewEpochTest.class); int count; HashMap peers; ArrayList threads; File[] tmpdir; int[] port; volatile int[] round; Semaphore start0; Semaphore finish3, finish0; @BeforeEach public void setUp() throws Exception { count = 3; peers = new HashMap<>(count); threads = new ArrayList<>(count); tmpdir = new File[count]; port = new int[count]; round = new int[3]; round[0] = 0; round[1] = 0; round[2] = 0; start0 = new Semaphore(0); finish0 = new Semaphore(0); finish3 = new Semaphore(0); } @AfterEach public void tearDown() throws Exception { for (int i = 0; i < threads.size(); i++) { threads.get(i).peer.getElectionAlg().shutdown(); } } class LEThread extends Thread { int i; QuorumPeer peer; LEThread(QuorumPeer peer, int i) { this.i = i; this.peer = peer; LOG.info("Constructor: {}", getName()); } public void run() { boolean flag = true; try { while (flag) { Vote v = null; peer.setPeerState(ServerState.LOOKING); LOG.info("Going to call leader election again: {}", i); v = peer.getElectionAlg().lookForLeader(); if (v == null) { fail("Thread " + i + " got a null vote"); } /* * A real zookeeper would take care of setting the current vote. Here * we do it manually. */ peer.setCurrentVote(v); LOG.info("Finished election: {}, {}", i, v.getId()); //votes[i] = v; switch (i) { case 0: LOG.info("First peer, do nothing, just join"); if (finish0.tryAcquire(1000, java.util.concurrent.TimeUnit.MILLISECONDS)) { //if(threads.get(0).peer.getPeerState() == ServerState.LEADING ){ LOG.info("Setting flag to false"); flag = false; } break; case 1: LOG.info("Second entering case"); if (round[1] != 0) { finish0.release(); flag = false; } else { finish3.acquire(); start0.release(); } LOG.info("Second is going to start second round"); round[1]++; break; case 2: LOG.info("Third peer, shutting it down"); QuorumBase.shutdown(peer); flag = false; round[2] = 1; finish3.release(); LOG.info("Third leaving"); break; } } } catch (Exception e) { e.printStackTrace(); } } } @Test public void testLENewEpoch() throws Exception { LOG.info("TestLE: {}, {}", getTestName(), count); for (int i = 0; i < count; i++) { peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } for (int i = 1; i < count; i++) { QuorumPeer peer = new QuorumPeer(peers, tmpdir[i], tmpdir[i], port[i], 3, i, 1000, 2, 2, 2); peer.startLeaderElection(); LEThread thread = new LEThread(peer, i); thread.start(); threads.add(thread); } if (!start0.tryAcquire(4000, java.util.concurrent.TimeUnit.MILLISECONDS)) { fail("First leader election failed"); } QuorumPeer peer = new QuorumPeer(peers, tmpdir[0], tmpdir[0], port[0], 3, 0, 1000, 2, 2, 2); peer.startLeaderElection(); LEThread thread = new LEThread(peer, 0); thread.start(); threads.add(thread); LOG.info("Started threads {}", getTestName()); for (int i = 0; i < threads.size(); i++) { threads.get(i).join(10000); if (threads.get(i).isAlive()) { fail("Threads didn't join"); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_FLEP0100644 0000000 0000000 00000000157 15051152474 032527 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLEPredicateTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLEPredicateTest.jav0100644 0000000 0000000 00000006530 15051152474 034101 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.HashMap; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.FastLeaderElection; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLEPredicateTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLEPredicateTest.class); class MockFLE extends FastLeaderElection { MockFLE(QuorumPeer peer) { super(peer, peer.createCnxnManager()); } boolean predicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) { return this.totalOrderPredicate(newId, newZxid, newEpoch, curId, curZxid, curEpoch); } } HashMap peers; @Test public void testPredicate() throws IOException { peers = new HashMap<>(3); /* * Creates list of peers. */ for (int i = 0; i < 3; i++) { peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); } /* * Creating peer. */ try { File tmpDir = ClientBase.createTmpDir(); QuorumPeer peer = new QuorumPeer(peers, tmpDir, tmpDir, PortAssignment.unique(), 3, 0, 1000, 2, 2, 2); MockFLE mock = new MockFLE(peer); mock.start(); /* * Lower epoch must return false */ assertFalse(mock.predicate(4L, 0L, 0L, 3L, 0L, 2L)); /* * Later epoch */ assertTrue(mock.predicate(0L, 0L, 1L, 1L, 0L, 0L)); /* * Higher zxid */ assertTrue(mock.predicate(0L, 1L, 0L, 1L, 0L, 0L)); /* * Higher id */ assertTrue(mock.predicate(1L, 1L, 0L, 0L, 1L, 0L)); } catch (IOException e) { LOG.error("Exception while creating quorum peer", e); fail("Exception while creating quorum peer"); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLERestartTest.java0100644 0000000 0000000 00000014347 15051152474 033773 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.Vote; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLERestartTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLETest.class); private int count; private Map peers; private List restartThreads; private File[] tmpdir; private int[] port; private Semaphore finish; static class TestVote { long leader; TestVote(int id, long leader) { this.leader = leader; } } int countVotes(HashSet hs, long id) { int counter = 0; for (TestVote v : hs) { if (v.leader == id) { counter++; } } return counter; } @BeforeEach public void setUp() throws Exception { count = 3; peers = new HashMap<>(count); restartThreads = new ArrayList<>(count); tmpdir = new File[count]; port = new int[count]; finish = new Semaphore(0); } @AfterEach public void tearDown() throws Exception { for (int i = 0; i < restartThreads.size(); i++) { restartThreads.get(i).peer.getElectionAlg().shutdown(); } } class FLERestartThread extends Thread { int i; QuorumPeer peer; int peerRound = 0; FLERestartThread(QuorumPeer peer, int i) { this.i = i; this.peer = peer; LOG.info("Constructor: {}", getName()); } public void run() { try { Vote v = null; while (true) { peer.setPeerState(ServerState.LOOKING); LOG.info("Going to call leader election again."); v = peer.getElectionAlg().lookForLeader(); if (v == null) { LOG.info("Thread {} got a null vote", i); break; } /* * A real zookeeper would take care of setting the current vote. Here * we do it manually. */ peer.setCurrentVote(v); LOG.info("Finished election: {}, {}", i, v.getId()); //votes[i] = v; switch (i) { case 0: if (peerRound == 0) { LOG.info("First peer, shutting it down"); QuorumBase.shutdown(peer); restartThreads.get(i).peer.getElectionAlg().shutdown(); peer = new QuorumPeer(peers, tmpdir[i], tmpdir[i], port[i], 3, i, 1000, 2, 2, 2); peer.startLeaderElection(); peerRound++; } else { finish.release(2); return; } break; case 1: LOG.info("Second entering case"); finish.acquire(); //if(threads.get(0).peer.getPeerState() == ServerState.LEADING ){ LOG.info("Release"); return; case 2: LOG.info("First peer, do nothing, just join"); finish.acquire(); //if(threads.get(0).peer.getPeerState() == ServerState.LEADING ){ LOG.info("Release"); return; } } } catch (Exception e) { e.printStackTrace(); } } } @Test public void testLERestart() throws Exception { LOG.info("TestLE: {}, {}", getTestName(), count); for (int i = 0; i < count; i++) { peers.put((long) i, new QuorumServer(i, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); tmpdir[i] = ClientBase.createTmpDir(); port[i] = PortAssignment.unique(); } for (int i = 0; i < count; i++) { QuorumPeer peer = new QuorumPeer(peers, tmpdir[i], tmpdir[i], port[i], 3, i, 1000, 2, 2, 2); peer.startLeaderElection(); FLERestartThread thread = new FLERestartThread(peer, i); thread.start(); restartThreads.add(thread); } LOG.info("Started threads {}", getTestName()); for (int i = 0; i < restartThreads.size(); i++) { restartThreads.get(i).join(10000); if (restartThreads.get(i).isAlive()) { fail("Threads didn't join"); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLETest.java0100644 0000000 0000000 00000043517 15051152474 032427 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.FastLeaderElection; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.Vote; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLETest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(FLETest.class); private final int MAX_LOOP_COUNTER = 300; private FLETest.LEThread leThread; static class TestVote { TestVote(int id, long leader) { this.leader = leader; } long leader; } int countVotes(HashSet hs, long id) { int counter = 0; for (TestVote v : hs) { if (v.leader == id) { counter++; } } return counter; } int count; Map peers; ArrayList threads; Map> voteMap; Map quora; File[] tmpdir; int[] port; int successCount; volatile Vote[] votes; volatile long leader = -1; //volatile int round = 1; Random rand = new Random(); Set joinedThreads; @BeforeEach public void setUp() throws Exception { count = 7; peers = new HashMap<>(count); threads = new ArrayList<>(count); voteMap = new HashMap>(); votes = new Vote[count]; tmpdir = new File[count]; port = new int[count]; successCount = 0; joinedThreads = new HashSet<>(); } @AfterEach public void tearDown() throws Exception { for (int i = 0; i < threads.size(); i++) { leThread = threads.get(i); QuorumBase.shutdown(leThread.peer); } } /** * Implements the behavior of a peer during the leader election rounds * of tests. */ class LEThread extends Thread { FLETest self; int i; QuorumPeer peer; int totalRounds; ConcurrentHashMap> quora; LEThread(FLETest self, QuorumPeer peer, int i, int rounds, ConcurrentHashMap> quora) { this.self = self; this.i = i; this.peer = peer; this.totalRounds = rounds; this.quora = quora; LOG.info("Constructor: {}", getName()); } public void run() { try { Vote v = null; while (true) { /* * Set the state of the peer to LOOKING and look for leader */ peer.setPeerState(ServerState.LOOKING); LOG.info("Going to call leader election again."); v = peer.getElectionAlg().lookForLeader(); if (v == null) { LOG.info("Thread {} got a null vote", i); break; } /* * Done with the election round, so now we set the vote in * the peer. A real zookeeper would take care of setting the * current vote. Here we do it manually. */ peer.setCurrentVote(v); LOG.info("Finished election: {}, {}", i, v.getId()); votes[i] = v; /* * Get the current value of the logical clock for this peer * so that we know in which round this peer has executed. */ int lc = (int) ((FastLeaderElection) peer.getElectionAlg()).getLogicalClock(); /* * The leader executes the following block, which essentially shuts down * the peer if it is not the last round. */ if (v.getId() == i) { LOG.info("I'm the leader: {}", i); if (lc < this.totalRounds) { LOG.info("Leader {} dying", i); FastLeaderElection election = (FastLeaderElection) peer.getElectionAlg(); election.shutdown(); // Make sure the vote is reset to -1 after shutdown. assertEquals(-1, election.getVote().getId()); LOG.info("Leader {} dead", i); break; } } /* * If the peer has done enough rounds, then consider joining. The thread * will only join if it is part of a quorum supporting the current * leader. Otherwise it will try again. */ if (lc >= this.totalRounds) { /* * quora keeps the supporters of a given leader, so * we first update it with the vote of this peer. */ if (quora.get(v.getId()) == null) { quora.put(v.getId(), new HashSet<>()); } quora.get(v.getId()).add(i); /* * we now wait until a quorum supports the same leader. */ if (waitForQuorum(v.getId())) { synchronized (self) { /* * Assert that the state of the thread is the one expected. */ if (v.getId() == i) { assertTrue(peer.getPeerState() == ServerState.LEADING, "Wrong state" + peer.getPeerState()); leader = i; } else { assertTrue(peer.getPeerState() == ServerState.FOLLOWING, "Wrong state" + peer.getPeerState()); } /* * Global variable keeping track of * how many peers have successfully * joined. */ successCount++; joinedThreads.add((long) i); self.notify(); } /* * I'm done so joining. */ break; } else { quora.get(v.getId()).remove(i); } } /* * This sleep time represents the time a follower * would take to declare the leader dead and start * a new leader election. */ Thread.sleep(100); } LOG.debug("Thread {} votes {}", i, v); } catch (InterruptedException e) { fail(e.toString()); } } /** * Auxiliary method to make sure that enough followers terminated. * * @return boolean followers successfully joined. */ boolean waitForQuorum(long id) throws InterruptedException { int loopCounter = 0; while ((quora.get(id).size() <= count / 2) && (loopCounter < MAX_LOOP_COUNTER)) { Thread.sleep(100); loopCounter++; } return (loopCounter < MAX_LOOP_COUNTER) || (quora.get(id).size() > count / 2); } } @Test public void testSingleElection() throws Exception { try { runElection(1); } catch (Exception e) { fail(e.toString()); } } @Test public void testDoubleElection() throws Exception { try { runElection(2); } catch (Exception e) { fail(e.toString()); } } @Test public void testTripleElection() throws Exception { try { runElection(3); } catch (Exception e) { fail(e.toString()); } } /** * Test leader election for a number of rounds. In all rounds but the last one * we kill the leader. * * @param rounds * @throws Exception */ private void runElection(int rounds) throws Exception { ConcurrentHashMap> quora = new ConcurrentHashMap<>(); LOG.info("TestLE: {}, {}", getTestName(), count); /* * Creates list of peers. */ for (int i = 0; i < count; i++) { port[i] = PortAssignment.unique(); peers.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", port[i]))); tmpdir[i] = ClientBase.createTmpDir(); } /* * Start one LEThread for each peer we want to run. */ for (int i = 0; i < count; i++) { QuorumPeer peer = new QuorumPeer(peers, tmpdir[i], tmpdir[i], port[i], 3, i, 1000, 2, 2, 2); peer.startLeaderElection(); LEThread thread = new LEThread(this, peer, i, rounds, quora); thread.start(); threads.add(thread); } LOG.info("Started threads {}", getTestName()); int waitCounter = 0; synchronized (this) { while (((successCount <= count / 2) || (leader == -1)) && (waitCounter < MAX_LOOP_COUNTER)) { this.wait(200); waitCounter++; } } LOG.info("Success count: {}", successCount); /* * Lists what threads haven't joined. A thread doesn't join if * it hasn't decided upon a leader yet. It can happen that a * peer is slow or disconnected, and it can take longer to * nominate and connect to the current leader. */ for (int i = 0; i < threads.size(); i++) { if (threads.get(i).isAlive()) { LOG.info("Threads didn't join: {}", i); } } /* * If we have a majority, then we are good to go. */ if (successCount <= count / 2) { fail("Fewer than a a majority has joined"); } /* * I'm done so joining. */ if (!joinedThreads.contains(leader)) { fail("Leader hasn't joined: " + leader); } } /* * Class to verify of the thread has become a follower */ static class VerifyState extends Thread { private volatile boolean success = false; private QuorumPeer peer; public VerifyState(QuorumPeer peer) { this.peer = peer; } public void run() { setName("VerifyState-" + peer.getMyId()); while (true) { if (peer.getPeerState() == ServerState.FOLLOWING) { LOG.info("I am following"); success = true; break; } else if (peer.getPeerState() == ServerState.LEADING) { LOG.info("I am leading"); success = false; break; } try { Thread.sleep(250); } catch (Exception e) { LOG.warn("Sleep failed ", e); } } } public boolean isSuccess() { return success; } } /* * For ZOOKEEPER-975 verify that a peer joining an established cluster * does not go in LEADING state. */ @Test public void testJoin() throws Exception { int sid; QuorumPeer peer; int waitTime = 10 * 1000; ArrayList peerList = new ArrayList<>(); for (sid = 0; sid < 3; sid++) { port[sid] = PortAssignment.unique(); peers.put(Long.valueOf(sid), new QuorumServer(sid, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", port[sid]))); tmpdir[sid] = ClientBase.createTmpDir(); } // start 2 peers and verify if they form the cluster for (sid = 0; sid < 2; sid++) { peer = new QuorumPeer(peers, tmpdir[sid], tmpdir[sid], port[sid], 3, sid, 2000, 2, 2, 2); LOG.info("Starting peer {}", peer.getMyId()); peer.start(); peerList.add(sid, peer); } peer = peerList.get(0); VerifyState v1 = new VerifyState(peerList.get(0)); v1.start(); v1.join(waitTime); assertFalse(!v1.isSuccess(), "Unable to form cluster in " + waitTime + " ms"); // Start 3rd peer and check if it goes in LEADING state peer = new QuorumPeer(peers, tmpdir[sid], tmpdir[sid], port[sid], 3, sid, 2000, 2, 2, 2); LOG.info("Starting peer {}", peer.getMyId()); peer.start(); peerList.add(sid, peer); v1 = new VerifyState(peer); v1.start(); v1.join(waitTime); if (v1.isAlive()) { fail("Peer " + peer.getMyId() + " failed to join the cluster " + "within " + waitTime + " ms"); } else if (!v1.isSuccess()) { fail("Incorrect LEADING state for peer " + peer.getMyId()); } // cleanup for (int id = 0; id < 3; id++) { peer = peerList.get(id); if (peer != null) { peer.shutdown(); } } } /* * For ZOOKEEPER-1732 verify that it is possible to join an ensemble with * inconsistent election round information. */ @Test public void testJoinInconsistentEnsemble() throws Exception { int sid; QuorumPeer peer; int waitTime = 10 * 1000; ArrayList peerList = new ArrayList<>(); for (sid = 0; sid < 3; sid++) { peers.put(Long.valueOf(sid), new QuorumServer(sid, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()))); tmpdir[sid] = ClientBase.createTmpDir(); port[sid] = PortAssignment.unique(); } // start 2 peers and verify if they form the cluster for (sid = 0; sid < 2; sid++) { peer = new QuorumPeer(peers, tmpdir[sid], tmpdir[sid], port[sid], 3, sid, 2000, 2, 2, 2); LOG.info("Starting peer {}", peer.getMyId()); peer.start(); peerList.add(sid, peer); } peer = peerList.get(0); VerifyState v1 = new VerifyState(peerList.get(0)); v1.start(); v1.join(waitTime); assertFalse(!v1.isSuccess(), "Unable to form cluster in " + waitTime + " ms"); // Change the election round for one of the members of the ensemble long leaderSid = peer.getCurrentVote().getId(); long zxid = peer.getCurrentVote().getZxid(); long electionEpoch = peer.getCurrentVote().getElectionEpoch(); ServerState state = peer.getCurrentVote().getState(); long peerEpoch = peer.getCurrentVote().getPeerEpoch(); Vote newVote = new Vote(leaderSid, zxid + 100, electionEpoch + 100, peerEpoch, state); peer.setCurrentVote(newVote); // Start 3rd peer and check if it joins the quorum peer = new QuorumPeer(peers, tmpdir[2], tmpdir[2], port[2], 3, 2, 2000, 2, 2, 2); LOG.info("Starting peer {}", peer.getMyId()); peer.start(); peerList.add(sid, peer); v1 = new VerifyState(peer); v1.start(); v1.join(waitTime); if (v1.isAlive()) { fail("Peer " + peer.getMyId() + " failed to join the cluster " + "within " + waitTime + " ms"); } // cleanup for (int id = 0; id < 3; id++) { peer = peerList.get(id); if (peer != null) { peer.shutdown(); } } } @Test public void testElectionTimeUnit() throws Exception { assertEquals("MS", QuorumPeer.FLE_TIME_UNIT); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_FLEZ0100644 0000000 0000000 00000000160 15051152474 032533 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLEZeroWeightTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FLEZeroWeightTest.ja0100644 0000000 0000000 00000015022 15051152474 034076 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; import java.io.File; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.Vote; import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FLEZeroWeightTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(HierarchicalQuorumTest.class); Properties qp; int count; Map peers; ArrayList threads; File[] tmpdir; int[] port; volatile Vote[] votes; @BeforeEach public void setUp() throws Exception { count = 9; peers = new HashMap<>(count); threads = new ArrayList<>(count); votes = new Vote[count]; tmpdir = new File[count]; port = new int[count]; String config = "group.1=0:1:2\n" + "group.2=3:4:5\n" + "group.3=6:7:8\n" + "weight.0=1\n" + "weight.1=1\n" + "weight.2=1\n" + "weight.3=0\n" + "weight.4=0\n" + "weight.5=0\n" + "weight.6=0\n" + "weight.7=0\n" + "weight.8=0"; ByteArrayInputStream is = new ByteArrayInputStream(config.getBytes()); this.qp = new Properties(); qp.load(is); } @AfterEach public void tearDown() throws Exception { for (int i = 0; i < threads.size(); i++) { LEThread leThread = threads.get(i); // shutdown() has to be explicitly called for every thread to // make sure that resources are freed properly and all fixed network ports // are available for other test cases QuorumBase.shutdown(leThread.peer); } } class LEThread extends Thread { int i; QuorumPeer peer; boolean fail; LEThread(QuorumPeer peer, int i) { this.i = i; this.peer = peer; LOG.info("Constructor: {}", getName()); } public void run() { try { Vote v = null; fail = false; while (true) { //while(true) { peer.setPeerState(ServerState.LOOKING); LOG.info("Going to call leader election."); v = peer.getElectionAlg().lookForLeader(); if (v == null) { LOG.info("Thread {} got a null vote", i); return; } /* * A real zookeeper would take care of setting the current vote. Here * we do it manually. */ peer.setCurrentVote(v); LOG.info("Finished election: {}, {}", i, v.getId()); votes[i] = v; if ((peer.getPeerState() == ServerState.LEADING) && (peer.getMyId() > 2)) { fail = true; } if ((peer.getPeerState() == ServerState.FOLLOWING) || (peer.getPeerState() == ServerState.LEADING)) { break; } } LOG.debug("Thread {} votes {}", i, v); } catch (InterruptedException e) { e.printStackTrace(); } } } @Test public void testZeroWeightQuorum() throws Exception { LOG.info("TestZeroWeightQuorum: {}, {}", getTestName(), count); for (int i = 0; i < count; i++) { InetSocketAddress addr1 = new InetSocketAddress("127.0.0.1", PortAssignment.unique()); InetSocketAddress addr2 = new InetSocketAddress("127.0.0.1", PortAssignment.unique()); InetSocketAddress addr3 = new InetSocketAddress("127.0.0.1", PortAssignment.unique()); port[i] = addr3.getPort(); qp.setProperty("server." + i, "127.0.0.1:" + addr1.getPort() + ":" + addr2.getPort() + ";" + port[i]); peers.put(Long.valueOf(i), new QuorumServer(i, addr1, addr2, addr3)); tmpdir[i] = ClientBase.createTmpDir(); } for (int i = 0; i < count; i++) { QuorumHierarchical hq = new QuorumHierarchical(qp); QuorumPeer peer = new QuorumPeer(peers, tmpdir[i], tmpdir[i], port[i], 3, i, 1000, 2, 2, 2, hq); peer.startLeaderElection(); LEThread thread = new LEThread(peer, i); thread.start(); threads.add(thread); } LOG.info("Started threads {}", getTestName()); for (int i = 0; i < threads.size(); i++) { threads.get(i).join(15000); if (threads.get(i).isAlive()) { fail("Threads didn't join"); } else { if (threads.get(i).fail) { fail("Elected zero-weight server"); } } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Foll0100644 0000000 0000000 00000000174 15051152474 032674 xustar000000000 0000000 124 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FollowerResyncConcurrencyTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FollowerResyncConcur0100644 0000000 0000000 00000067034 15051152474 034370 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Collection; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FollowerResyncConcurrencyTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(FollowerResyncConcurrencyTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; private AtomicInteger counter = new AtomicInteger(0); private AtomicInteger errors = new AtomicInteger(0); /** * Keep track of pending async operations, we shouldn't start verifying * the state until pending operation is 0 */ private AtomicInteger pending = new AtomicInteger(0); @BeforeEach public void setUp() throws Exception { pending.set(0); errors.set(0); counter.set(0); } @AfterEach public void tearDown() throws Exception { LOG.info("Error count {}", errors.get()); } /** * See ZOOKEEPER-1319 - verify that a lagging follwer resyncs correctly * * 1) start with down quorum * 2) start leader/follower1, add some data * 3) restart leader/follower1 * 4) start follower2 * 5) verify data consistency across the ensemble * * @throws Exception */ @Test public void testLaggingFollowerResyncsUnderNewEpoch() throws Exception { CountdownWatcher watcher1 = new CountdownWatcher(); CountdownWatcher watcher2 = new CountdownWatcher(); CountdownWatcher watcher3 = new CountdownWatcher(); QuorumUtil qu = new QuorumUtil(1); qu.shutdownAll(); qu.start(1); qu.start(2); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(1).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(2).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); ZooKeeper zk1 = createClient(qu.getPeer(1).peer.getClientPort(), watcher1); LOG.info("zk1 has session id 0x{}", Long.toHexString(zk1.getSessionId())); final String resyncPath = "/resyncundernewepoch"; zk1.create(resyncPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.close(); qu.shutdown(1); qu.shutdown(2); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + qu.getPeer(1).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + qu.getPeer(2).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server down"); qu.start(1); qu.start(2); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(1).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(2).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); qu.start(3); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(3).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); zk1 = createClient(qu.getPeer(1).peer.getClientPort(), watcher1); LOG.info("zk1 has session id 0x{}", Long.toHexString(zk1.getSessionId())); assertNotNull(zk1.exists(resyncPath, false), "zk1 has data"); final ZooKeeper zk2 = createClient(qu.getPeer(2).peer.getClientPort(), watcher2); LOG.info("zk2 has session id 0x{}", Long.toHexString(zk2.getSessionId())); assertNotNull(zk2.exists(resyncPath, false), "zk2 has data"); final ZooKeeper zk3 = createClient(qu.getPeer(3).peer.getClientPort(), watcher3); LOG.info("zk3 has session id 0x{}", Long.toHexString(zk3.getSessionId())); assertNotNull(zk3.exists(resyncPath, false), "zk3 has data"); zk1.close(); zk2.close(); zk3.close(); qu.shutdownAll(); } /** * See ZOOKEEPER-962. This tests for one of the bugs hit while fixing this, * setting the ZXID of the SNAP packet * Starts up 3 ZKs. Shut down F1, write a node, restart the one that was shut down * The non-leader ZKs are writing to cluster * Shut down F1 again * Restart after sessions are expired, expect to get a snap file * Shut down, run some transactions through. * Restart to a diff while transactions are running in leader * @throws IOException * @throws InterruptedException * @throws KeeperException */ @Test public void testResyncBySnapThenDiffAfterFollowerCrashes() throws Throwable { followerResyncCrashTest(false); } /** * Same as testResyncBySnapThenDiffAfterFollowerCrashes() but we resync * follower using txnlog * * @throws IOException * @throws InterruptedException * @throws KeeperException */ @Test public void testResyncByTxnlogThenDiffAfterFollowerCrashes() throws Throwable { followerResyncCrashTest(true); } public void followerResyncCrashTest(boolean useTxnLogResync) throws Throwable { final Semaphore sem = new Semaphore(0); QuorumUtil qu = new QuorumUtil(1); qu.startAll(); CountdownWatcher watcher1 = new CountdownWatcher(); CountdownWatcher watcher2 = new CountdownWatcher(); CountdownWatcher watcher3 = new CountdownWatcher(); int index = 1; while (qu.getPeer(index).peer.leader == null) { index++; } Leader leader = qu.getPeer(index).peer.leader; assertNotNull(leader); if (useTxnLogResync) { // Set the factor to high value so that this test case always // resync using txnlog qu.getPeer(index).peer.getActiveServer().getZKDatabase().setSnapshotSizeFactor(1000); } else { // Disable sending DIFF using txnlog, so that this test still // testing the ZOOKEEPER-962 bug qu.getPeer(index).peer.getActiveServer().getZKDatabase().setSnapshotSizeFactor(-1); } /* Reusing the index variable to select a follower to connect to */ index = (index == 1) ? 2 : 1; LOG.info("Connecting to follower: {}", index); qu.shutdown(index); final ZooKeeper zk3 = createClient(qu.getPeer(3).peer.getClientPort(), watcher3); LOG.info("zk3 has session id 0x{}", Long.toHexString(zk3.getSessionId())); zk3.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); qu.restart(index); final ZooKeeper zk1 = createClient(qu.getPeer(index).peer.getClientPort(), watcher1); LOG.info("zk1 has session id 0x{}", Long.toHexString(zk1.getSessionId())); final ZooKeeper zk2 = createClient(qu.getPeer(index).peer.getClientPort(), watcher2); LOG.info("zk2 has session id 0x{}", Long.toHexString(zk2.getSessionId())); zk1.create("/first", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // Prepare a thread that will create znodes. Thread mytestfooThread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 3000; i++) { // Here we create 3000 znodes zk3.create("/mytestfoo", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> { pending.decrementAndGet(); counter.incrementAndGet(); if (rc != 0) { errors.incrementAndGet(); } if (counter.get() == 16200) { sem.release(); } }, null); pending.incrementAndGet(); if (i % 10 == 0) { try { Thread.sleep(100); } catch (Exception e) { } } } } }); // Here we start populating the server and shutdown the follower after // initial data is written. for (int i = 0; i < 13000; i++) { // Here we create 13000 znodes zk3.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> { pending.decrementAndGet(); counter.incrementAndGet(); if (rc != 0) { errors.incrementAndGet(); } if (counter.get() == 16200) { sem.release(); } }, null); pending.incrementAndGet(); if (i == 5000) { qu.shutdown(index); LOG.info("Shutting down s1"); } if (i == 12000) { // Start the prepared thread so that it is writing znodes while // the follower is restarting. On the first restart, the follow // should use txnlog to catchup. For subsequent restart, the // follower should use a diff to catchup. mytestfooThread.start(); LOG.info("Restarting follower: {}", index); qu.restart(index); Thread.sleep(300); LOG.info("Shutdown follower: {}", index); qu.shutdown(index); Thread.sleep(300); LOG.info("Restarting follower: {}", index); qu.restart(index); LOG.info("Setting up server: {}", index); } if ((i % 1000) == 0) { Thread.sleep(1000); } if (i % 50 == 0) { zk2.create("/newbaz", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> { pending.decrementAndGet(); counter.incrementAndGet(); if (rc != 0) { errors.incrementAndGet(); } if (counter.get() == 16200) { sem.release(); } }, null); pending.incrementAndGet(); } } // Wait until all updates return if (!sem.tryAcquire(ClientBase.CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { LOG.warn("Did not aquire semaphore fast enough"); } mytestfooThread.join(ClientBase.CONNECTION_TIMEOUT); if (mytestfooThread.isAlive()) { LOG.error("mytestfooThread is still alive"); } assertTrue(waitForPendingRequests(60)); assertTrue(waitForSync(qu, index, 10)); verifyState(qu, index, leader); zk1.close(); zk2.close(); zk3.close(); qu.shutdownAll(); } /** * This test: * Starts up 3 ZKs. The non-leader ZKs are writing to cluster * Shut down one of the non-leader ZKs. * Restart after sessions have expired but less than 500 txns have taken place (get a diff) * Shut down immediately after restarting, start running separate thread with other transactions * Restart to a diff while transactions are running in leader * * * Before fixes for ZOOKEEPER-962, restarting off of diff could get an inconsistent view of data missing transactions that * completed during diff syncing. Follower would also be considered "restarted" before all forwarded transactions * were completely processed, so restarting would cause a snap file with a too-high zxid to be written, and transactions * would be missed * * This test should pretty reliably catch the failure of restarting the server before all diff messages have been processed, * however, due to the transient nature of the system it may not catch failures due to concurrent processing of transactions * during the leader's diff forwarding. * * @throws IOException * @throws InterruptedException * @throws KeeperException * @throws Throwable */ @Test public void testResyncByDiffAfterFollowerCrashes() throws IOException, InterruptedException, KeeperException, Throwable { final Semaphore sem = new Semaphore(0); QuorumUtil qu = new QuorumUtil(1); qu.startAll(); CountdownWatcher watcher1 = new CountdownWatcher(); CountdownWatcher watcher2 = new CountdownWatcher(); CountdownWatcher watcher3 = new CountdownWatcher(); int index = 1; while (qu.getPeer(index).peer.leader == null) { index++; } Leader leader = qu.getPeer(index).peer.leader; assertNotNull(leader); /* Reusing the index variable to select a follower to connect to */ index = (index == 1) ? 2 : 1; LOG.info("Connecting to follower: {}", index); final ZooKeeper zk1 = createClient(qu.getPeer(index).peer.getClientPort(), watcher1); LOG.info("zk1 has session id 0x{}", Long.toHexString(zk1.getSessionId())); final ZooKeeper zk2 = createClient(qu.getPeer(index).peer.getClientPort(), watcher2); LOG.info("zk2 has session id 0x{}", Long.toHexString(zk2.getSessionId())); final ZooKeeper zk3 = createClient(qu.getPeer(3).peer.getClientPort(), watcher3); LOG.info("zk3 has session id 0x{}", Long.toHexString(zk3.getSessionId())); zk1.create("/first", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk2.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); final AtomicBoolean runNow = new AtomicBoolean(false); Thread mytestfooThread = new Thread(new Runnable() { @Override public void run() { int inSyncCounter = 0; while (inSyncCounter < 400) { if (runNow.get()) { zk3.create("/mytestfoo", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> { pending.decrementAndGet(); counter.incrementAndGet(); if (rc != 0) { errors.incrementAndGet(); } if (counter.get() > 7300) { sem.release(); } }, null); pending.incrementAndGet(); try { Thread.sleep(10); } catch (Exception e) { } inSyncCounter++; } else { Thread.yield(); } } } }); mytestfooThread.start(); for (int i = 0; i < 5000; i++) { zk2.create("/mybar", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> { pending.decrementAndGet(); counter.incrementAndGet(); if (rc != 0) { errors.incrementAndGet(); } if (counter.get() > 7300) { sem.release(); } }, null); pending.incrementAndGet(); if (i == 1000) { qu.shutdown(index); Thread.sleep(1100); LOG.info("Shutting down s1"); } if (i == 1100 || i == 1150 || i == 1200) { Thread.sleep(1000); } if (i == 1200) { qu.startThenShutdown(index); runNow.set(true); qu.restart(index); LOG.info("Setting up server: {}", index); } if (i >= 1000 && i % 2 == 0) { zk3.create("/newbaz", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL, (rc, path, ctx, name) -> { pending.decrementAndGet(); counter.incrementAndGet(); if (rc != 0) { errors.incrementAndGet(); } if (counter.get() > 7300) { sem.release(); } }, null); pending.incrementAndGet(); } if (i == 1050 || i == 1100 || i == 1150) { Thread.sleep(1000); } } // Wait until all updates return if (!sem.tryAcquire(ClientBase.CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { LOG.warn("Did not aquire semaphore fast enough"); } mytestfooThread.join(ClientBase.CONNECTION_TIMEOUT); if (mytestfooThread.isAlive()) { LOG.error("mytestfooThread is still alive"); } assertTrue(waitForPendingRequests(60)); assertTrue(waitForSync(qu, index, 10)); // Verify that server is following and has the same epoch as the leader verifyState(qu, index, leader); zk1.close(); zk2.close(); zk3.close(); qu.shutdownAll(); } private static DisconnectableZooKeeper createClient(int port, CountdownWatcher watcher) throws IOException, TimeoutException, InterruptedException { DisconnectableZooKeeper zk = new DisconnectableZooKeeper( "127.0.0.1:" + port, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); return zk; } /** * Wait for all async operation to return. So we know that we can start * verifying the state */ private boolean waitForPendingRequests(int timeout) throws InterruptedException { LOG.info("Wait for pending requests: {}", pending.get()); for (int i = 0; i < timeout; ++i) { Thread.sleep(1000); if (pending.get() == 0) { return true; } } LOG.info("Timeout waiting for pending requests: {}", pending.get()); return false; } /** * Wait for all server to have the same lastProccessedZxid. Timeout in seconds */ private boolean waitForSync(QuorumUtil qu, int index, int timeout) throws InterruptedException { LOG.info("Wait for server to sync"); int leaderIndex = (index == 1) ? 2 : 1; ZKDatabase restartedDb = qu.getPeer(index).peer.getActiveServer().getZKDatabase(); ZKDatabase cleanDb = qu.getPeer(3).peer.getActiveServer().getZKDatabase(); ZKDatabase leadDb = qu.getPeer(leaderIndex).peer.getActiveServer().getZKDatabase(); long leadZxid = 0; long cleanZxid = 0; long restartedZxid = 0; for (int i = 0; i < timeout; ++i) { leadZxid = leadDb.getDataTreeLastProcessedZxid(); cleanZxid = cleanDb.getDataTreeLastProcessedZxid(); restartedZxid = restartedDb.getDataTreeLastProcessedZxid(); if (leadZxid == cleanZxid && leadZxid == restartedZxid) { return true; } Thread.sleep(1000); } LOG.info( "Timeout waiting for zxid to sync: leader 0x{} clean 0x{} restarted 0x{}", Long.toHexString(leadZxid), Long.toHexString(cleanZxid), Long.toHexString(restartedZxid)); return false; } private static TestableZooKeeper createTestableClient(String hp) throws IOException, TimeoutException, InterruptedException { CountdownWatcher watcher = new CountdownWatcher(); return createTestableClient(watcher, hp); } private static TestableZooKeeper createTestableClient( CountdownWatcher watcher, String hp) throws IOException, TimeoutException, InterruptedException { TestableZooKeeper zk = new TestableZooKeeper(hp, ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); return zk; } private void verifyState(QuorumUtil qu, int index, Leader leader) { LOG.info("Verifying state"); assertTrue(qu.getPeer(index).peer.follower != null, "Not following"); long epochF = (qu.getPeer(index).peer.getActiveServer().getZxid() >> 32L); long epochL = (leader.getEpoch() >> 32L); assertTrue(epochF == epochL, "Zxid: " + qu.getPeer(index).peer.getActiveServer().getZKDatabase().getDataTreeLastProcessedZxid() + "Current epoch: " + epochF); int leaderIndex = (index == 1) ? 2 : 1; Collection sessionsRestarted = qu.getPeer(index).peer.getActiveServer().getZKDatabase().getSessions(); Collection sessionsNotRestarted = qu.getPeer(leaderIndex).peer.getActiveServer().getZKDatabase().getSessions(); for (Long l : sessionsRestarted) { assertTrue(sessionsNotRestarted.contains(l), "Should have same set of sessions in both servers, did not expect: " + l); } assertEquals(sessionsNotRestarted.size(), sessionsRestarted.size(), "Should have same number of sessions"); ZKDatabase restarted = qu.getPeer(index).peer.getActiveServer().getZKDatabase(); ZKDatabase clean = qu.getPeer(3).peer.getActiveServer().getZKDatabase(); ZKDatabase lead = qu.getPeer(leaderIndex).peer.getActiveServer().getZKDatabase(); for (Long l : sessionsRestarted) { LOG.info("Validating ephemeral for session id 0x{}", Long.toHexString(l)); assertTrue(sessionsNotRestarted.contains(l), "Should have same set of sessions in both servers, did not expect: " + l); Set ephemerals = restarted.getEphemerals(l); Set cleanEphemerals = clean.getEphemerals(l); for (String o : cleanEphemerals) { if (!ephemerals.contains(o)) { LOG.info("Restarted follower doesn't contain ephemeral {} zxid 0x{}", o, Long.toHexString(clean.getDataTree().getNode(o).stat.getMzxid())); } } for (String o : ephemerals) { if (!cleanEphemerals.contains(o)) { LOG.info("Restarted follower has extra ephemeral {} zxid 0x{}", o, Long.toHexString(restarted.getDataTree().getNode(o).stat.getMzxid())); } } Set leadEphemerals = lead.getEphemerals(l); for (String o : leadEphemerals) { if (!cleanEphemerals.contains(o)) { LOG.info("Follower doesn't contain ephemeral from leader {} zxid 0x{}", o, Long.toHexString(lead.getDataTree().getNode(o).stat.getMzxid())); } } for (String o : cleanEphemerals) { if (!leadEphemerals.contains(o)) { LOG.info("Leader doesn't contain ephemeral from follower {} zxid 0x{}", o, Long.toHexString(clean.getDataTree().getNode(o).stat.getMzxid())); } } assertEquals(ephemerals.size(), cleanEphemerals.size(), "Should have same number of ephemerals in both followers"); assertEquals(lead.getEphemerals(l).size(), cleanEphemerals.size(), "Leader should equal follower"); } } /** * Verify that the server is sending the proper zxid. See ZOOKEEPER-1412. */ @Test public void testFollowerSendsLastZxid() throws Exception { QuorumUtil qu = new QuorumUtil(1); qu.startAll(); int index = 1; while (qu.getPeer(index).peer.follower == null) { index++; } LOG.info("Connecting to follower: {}", index); TestableZooKeeper zk = createTestableClient("localhost:" + qu.getPeer(index).peer.getClientPort()); assertEquals(0L, zk.testableLastZxid()); zk.exists("/", false); long lzxid = zk.testableLastZxid(); assertTrue(lzxid > 0, "lzxid:" + lzxid + " > 0"); zk.close(); qu.shutdownAll(); } private class MyWatcher extends CountdownWatcher { LinkedBlockingQueue events = new LinkedBlockingQueue<>(); public void process(WatchedEvent event) { super.process(event); if (event.getType() != Event.EventType.None) { try { events.put(event); } catch (InterruptedException e) { LOG.warn("ignoring interrupt during event.put"); } } } } /** * Verify that the server is sending the proper zxid, and as a result * the watch doesn't fire. See ZOOKEEPER-1412. */ @Test public void testFollowerWatcherResync() throws Exception { QuorumUtil qu = new QuorumUtil(1); qu.startAll(); int index = 1; while (qu.getPeer(index).peer.follower == null) { index++; } LOG.info("Connecting to follower: {}", index); TestableZooKeeper zk1 = createTestableClient("localhost:" + qu.getPeer(index).peer.getClientPort()); zk1.create("/foo", "foo".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); MyWatcher watcher = new MyWatcher(); TestableZooKeeper zk2 = createTestableClient(watcher, "localhost:" + qu.getPeer(index).peer.getClientPort()); zk2.exists("/foo", true); watcher.reset(); zk2.testableConnloss(); if (!watcher.clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Unable to connect to server"); } assertArrayEquals("foo".getBytes(), zk2.getData("/foo", false, null)); assertNull(watcher.events.poll(5, TimeUnit.SECONDS)); zk1.close(); zk2.close(); qu.shutdownAll(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Four0100644 0000000 0000000 00000000170 15051152474 032707 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FourLetterWordsQuorumTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FourLetterWordsQuoru0100644 0000000 0000000 00000010212 15051152474 034371 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FourLetterWordsQuorumTest extends QuorumBase { protected static final Logger LOG = LoggerFactory.getLogger(FourLetterWordsQuorumTest.class); /** Test the various four letter words */ @Test public void testFourLetterWords() throws Exception { String[] servers = hostPort.split(","); for (String hp : servers) { verify(hp, "ruok", "imok"); verify(hp, "envi", "java.version"); verify(hp, "conf", "clientPort"); verify(hp, "stat", "Outstanding"); verify(hp, "srvr", "Outstanding"); verify(hp, "cons", "queued"); verify(hp, "dump", "Session"); verify(hp, "wchs", "watches"); verify(hp, "wchp", ""); verify(hp, "wchc", ""); verify(hp, "srst", "reset"); verify(hp, "crst", "reset"); verify(hp, "stat", "Outstanding"); verify(hp, "srvr", "Outstanding"); verify(hp, "cons", "queued"); TestableZooKeeper zk = createClient(hp); String sid = getHexSessionId(zk.getSessionId()); verify(hp, "stat", "queued"); verify(hp, "srvr", "Outstanding"); verify(hp, "cons", sid); verify(hp, "dump", sid); verify(hp, "dirs", "size"); zk.getData("/", true, null); verify(hp, "stat", "queued"); verify(hp, "srvr", "Outstanding"); verify(hp, "cons", sid); verify(hp, "dump", sid); verify(hp, "wchs", "watching 1"); verify(hp, "wchp", sid); verify(hp, "wchc", sid); verify(hp, "dirs", "size"); zk.close(); verify(hp, "ruok", "imok"); verify(hp, "envi", "java.version"); verify(hp, "conf", "clientPort"); verify(hp, "stat", "Outstanding"); verify(hp, "srvr", "Outstanding"); verify(hp, "cons", "queued"); verify(hp, "dump", "Session"); verify(hp, "wchs", "watch"); verify(hp, "wchp", ""); verify(hp, "wchc", ""); verify(hp, "dirs", "size"); verify(hp, "srst", "reset"); verify(hp, "crst", "reset"); verify(hp, "stat", "Outstanding"); verify(hp, "srvr", "Outstanding"); verify(hp, "cons", "queued"); verify(hp, "mntr", "zk_version\t"); } } private void verify(String hp, String cmd, String expected) throws IOException, SSLContextException { for (HostPort hpobj : parseHostPortList(hp)) { String resp = send4LetterWord(hpobj.host, hpobj.port, cmd); LOG.info("cmd {} expected {} got {}", cmd, expected, resp); if (cmd.equals("dump")) { assertTrue(resp.contains(expected) || resp.contains("Sessions with Ephemerals")); } else { assertTrue(resp.contains(expected)); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Four0100644 0000000 0000000 00000000162 15051152474 032710 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FourLetterWordsTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FourLetterWordsTest.0100644 0000000 0000000 00000020650 15051152474 034262 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.StringReader; import java.util.regex.Pattern; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.IOUtils; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FourLetterWordsTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(FourLetterWordsTest.class); /** Test the various four letter words */ @Test @Timeout(value = 30) public void testFourLetterWords() throws Exception { verify("ruok", "imok"); verify("envi", "java.version"); verify("conf", "clientPort"); verify("stat", "Outstanding"); verify("srvr", "Outstanding"); verify("cons", "queued"); verify("dump", "Session"); verify("wchs", "watches"); verify("wchp", ""); verify("wchc", ""); verify("srst", "reset"); verify("crst", "reset"); verify("stat", "Outstanding"); verify("srvr", "Outstanding"); verify("cons", "queued"); verify("gtmk", "306"); verify("isro", "rw"); TestableZooKeeper zk = createClient(); String sid = getHexSessionId(zk.getSessionId()); verify("stat", "queued"); verify("srvr", "Outstanding"); verify("cons", sid); verify("dump", sid); verify("dirs", "size"); zk.getData("/", true, null); verify("stat", "queued"); verify("srvr", "Outstanding"); verify("cons", sid); verify("dump", sid); verify("wchs", "watching 1"); verify("wchp", sid); verify("wchc", sid); verify("dirs", "size"); zk.close(); verify("ruok", "imok"); verify("envi", "java.version"); verify("conf", "clientPort"); verify("stat", "Outstanding"); verify("srvr", "Outstanding"); verify("cons", "queued"); verify("dump", "Session"); verify("wchs", "watch"); verify("wchp", ""); verify("wchc", ""); verify("srst", "reset"); verify("crst", "reset"); verify("stat", "Outstanding"); verify("srvr", "Outstanding"); verify("cons", "queued"); verify("mntr", "zk_server_state\tstandalone"); verify("mntr", "num_alive_connections"); verify("stat", "Connections"); verify("srvr", "Connections"); verify("dirs", "size"); } private String sendRequest(String cmd) throws IOException, SSLContextException { HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); return send4LetterWord(hpobj.host, hpobj.port, cmd); } private String sendRequest(String cmd, int timeout) throws IOException, SSLContextException { HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); return send4LetterWord(hpobj.host, hpobj.port, cmd, false, timeout); } private void verify(String cmd, String expected) throws IOException, SSLContextException { String resp = sendRequest(cmd); LOG.info("cmd {} expected {} got {}", cmd, expected, resp); assertTrue(resp.contains(expected)); } @Test @Timeout(value = 30) public void testValidateStatOutput() throws Exception { ZooKeeper zk1 = createClient(); ZooKeeper zk2 = createClient(); String resp = sendRequest("stat"); BufferedReader in = new BufferedReader(new StringReader(resp)); String line; // first line should be version info line = in.readLine(); assertTrue(Pattern.matches("^.*\\s\\d+\\.\\d+\\.\\d+-.*$", line)); assertTrue(Pattern.matches("^Clients:$", in.readLine())); int count = 0; while ((line = in.readLine()).length() > 0) { count++; assertTrue(Pattern.matches("^ /.*:\\d+\\[\\d+\\]\\(queued=\\d+,recved=\\d+,sent=\\d+\\)$", line)); } // ensure at least the two clients we created are accounted for assertTrue(count >= 2); line = in.readLine(); assertTrue(Pattern.matches("^Latency min/avg/max: \\d+/-?[0-9]*.?[0-9]*/\\d+$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Received: \\d+$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Sent: \\d+$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Connections: \\d+$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Outstanding: \\d+$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Zxid: 0x[\\da-fA-F]+$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Mode: .*$", line)); line = in.readLine(); assertTrue(Pattern.matches("^Node count: \\d+$", line)); zk1.close(); zk2.close(); } @Test @Timeout(value = 30) public void testValidateConsOutput() throws Exception { ZooKeeper zk1 = createClient(); ZooKeeper zk2 = createClient(); String resp = sendRequest("cons"); BufferedReader in = new BufferedReader(new StringReader(resp)); String line; int count = 0; while ((line = in.readLine()) != null && line.length() > 0) { count++; assertTrue(Pattern.matches("^ /.*:\\d+\\[\\d+\\]\\(queued=\\d+,recved=\\d+,sent=\\d+.*\\)$", line), line); } // ensure at least the two clients we created are accounted for assertTrue(count >= 2); zk1.close(); zk2.close(); } @Test @Timeout(value = 60) public void testValidateSocketTimeout() throws Exception { /** * testing positive scenario that even with timeout parameter the * functionality works fine */ String resp = sendRequest("isro", 2000); assertTrue(resp.contains("rw")); } @Test @Timeout(value = 30) public void testSetTraceMask() throws Exception { String gtmkResp = sendRequest("gtmk"); assertNotNull(gtmkResp); gtmkResp = gtmkResp.trim(); assertFalse(gtmkResp.isEmpty()); long formerMask = Long.valueOf(gtmkResp); try { verify(buildSetTraceMaskRequest(0), "0"); verify("gtmk", "0"); } finally { // Restore former value. sendRequest(buildSetTraceMaskRequest(formerMask)); } } /** * Builds a SetTraceMask request to be sent to the server, consisting of * "stmk" followed by the 8-byte long representation of the trace mask. * * @param mask trace mask to set * @return built request * @throws IOException if there is an I/O error */ private String buildSetTraceMaskRequest(long mask) throws IOException { ByteArrayOutputStream baos = null; DataOutputStream dos = null; try { baos = new ByteArrayOutputStream(); dos = new DataOutputStream(baos); dos.writeBytes("stmk"); dos.writeLong(mask); } finally { IOUtils.closeStream(dos); IOUtils.closeStream(baos); } return new String(baos.toByteArray()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Four0100644 0000000 0000000 00000000173 15051152474 032712 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FourLetterWordsWhiteListTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/FourLetterWordsWhite0100644 0000000 0000000 00000023757 15051152474 034360 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.FourLetterWordMain.send4LetterWord; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.common.X509Exception.SSLContextException; import org.apache.zookeeper.server.command.FourLetterCommands; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FourLetterWordsWhiteListTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(FourLetterWordsWhiteListTest.class); /* * ZOOKEEPER-2693: test white list of four letter words. * For 3.5.x default white list is empty. Verify that is * the case (except 'stat' command which is enabled in ClientBase * which other tests depend on.). */ @Test @Timeout(value = 30) public void testFourLetterWordsAllDisabledByDefault() throws Exception { stopServer(); FourLetterCommands.resetWhiteList(); System.setProperty("zookeeper.4lw.commands.whitelist", "stat"); startServer(); // Default white list for 3.5.x is empty, so all command should fail. verifyAllCommandsFail(); TestableZooKeeper zk = createClient(); verifyAllCommandsFail(); zk.getData("/", true, null); verifyAllCommandsFail(); zk.close(); verifyFuzzyMatch("stat", "Outstanding"); verifyAllCommandsFail(); } @Test @Timeout(value = 30) public void testFourLetterWordsEnableSomeCommands() throws Exception { stopServer(); FourLetterCommands.resetWhiteList(); System.setProperty("zookeeper.4lw.commands.whitelist", "stat, ruok, isro"); startServer(); // stat, ruok and isro are white listed. verifyFuzzyMatch("stat", "Outstanding"); verifyExactMatch("ruok", "imok"); verifyExactMatch("isro", "rw"); // Rest of commands fail. verifyExactMatch("conf", generateExpectedMessage("conf")); verifyExactMatch("cons", generateExpectedMessage("cons")); verifyExactMatch("crst", generateExpectedMessage("crst")); verifyExactMatch("dirs", generateExpectedMessage("dirs")); verifyExactMatch("dump", generateExpectedMessage("dump")); verifyExactMatch("envi", generateExpectedMessage("envi")); verifyExactMatch("gtmk", generateExpectedMessage("gtmk")); verifyExactMatch("stmk", generateExpectedMessage("stmk")); verifyExactMatch("srst", generateExpectedMessage("srst")); verifyExactMatch("wchc", generateExpectedMessage("wchc")); verifyExactMatch("wchp", generateExpectedMessage("wchp")); verifyExactMatch("wchs", generateExpectedMessage("wchs")); verifyExactMatch("mntr", generateExpectedMessage("mntr")); } @Test @Timeout(value = 30) public void testISROEnabledWhenReadOnlyModeEnabled() throws Exception { stopServer(); FourLetterCommands.resetWhiteList(); System.setProperty("zookeeper.4lw.commands.whitelist", "stat"); System.setProperty("readonlymode.enabled", "true"); startServer(); verifyExactMatch("isro", "rw"); System.clearProperty("readonlymode.enabled"); } @Test @Timeout(value = 30) public void testFourLetterWordsInvalidConfiguration() throws Exception { stopServer(); FourLetterCommands.resetWhiteList(); System.setProperty("zookeeper.4lw.commands.whitelist", "foo bar" + " foo,,, " + "bar :.,@#$%^&*() , , , , bar, bar, stat, "); startServer(); // Just make sure we are good when admin made some mistakes in config file. verifyAllCommandsFail(); // But still, what's valid in white list will get through. verifyFuzzyMatch("stat", "Outstanding"); } @Test @Timeout(value = 30) public void testFourLetterWordsEnableAllCommandsThroughAsterisk() throws Exception { stopServer(); FourLetterCommands.resetWhiteList(); System.setProperty("zookeeper.4lw.commands.whitelist", "*"); startServer(); verifyAllCommandsSuccess(); } @Test @Timeout(value = 30) public void testFourLetterWordsEnableAllCommandsThroughExplicitList() throws Exception { stopServer(); FourLetterCommands.resetWhiteList(); System.setProperty("zookeeper.4lw.commands.whitelist", "ruok, envi, conf, stat, srvr, cons, dump," + "wchs, wchp, wchc, srst, crst, " + "dirs, mntr, gtmk, isro, stmk"); startServer(); verifyAllCommandsSuccess(); } private void verifyAllCommandsSuccess() throws Exception { verifyExactMatch("ruok", "imok"); verifyFuzzyMatch("envi", "java.version"); verifyFuzzyMatch("conf", "clientPort"); verifyFuzzyMatch("stat", "Outstanding"); verifyFuzzyMatch("srvr", "Outstanding"); verifyFuzzyMatch("cons", "queued"); verifyFuzzyMatch("dump", "Session"); verifyFuzzyMatch("wchs", "watches"); verifyFuzzyMatch("wchp", ""); verifyFuzzyMatch("wchc", ""); verifyFuzzyMatch("srst", "reset"); verifyFuzzyMatch("crst", "reset"); verifyFuzzyMatch("stat", "Outstanding"); verifyFuzzyMatch("srvr", "Outstanding"); verifyFuzzyMatch("cons", "queued"); verifyFuzzyMatch("gtmk", "306"); verifyFuzzyMatch("isro", "rw"); TestableZooKeeper zk = createClient(); String sid = getHexSessionId(zk.getSessionId()); verifyFuzzyMatch("stat", "queued"); verifyFuzzyMatch("srvr", "Outstanding"); verifyFuzzyMatch("cons", sid); verifyFuzzyMatch("dump", sid); verifyFuzzyMatch("dirs", "size"); zk.getData("/", true, null); verifyFuzzyMatch("stat", "queued"); verifyFuzzyMatch("srvr", "Outstanding"); verifyFuzzyMatch("cons", sid); verifyFuzzyMatch("dump", sid); verifyFuzzyMatch("wchs", "watching 1"); verifyFuzzyMatch("wchp", sid); verifyFuzzyMatch("wchc", sid); verifyFuzzyMatch("dirs", "size"); zk.close(); verifyExactMatch("ruok", "imok"); verifyFuzzyMatch("envi", "java.version"); verifyFuzzyMatch("conf", "clientPort"); verifyFuzzyMatch("stat", "Outstanding"); verifyFuzzyMatch("srvr", "Outstanding"); verifyFuzzyMatch("cons", "queued"); verifyFuzzyMatch("dump", "Session"); verifyFuzzyMatch("wchs", "watch"); verifyFuzzyMatch("wchp", ""); verifyFuzzyMatch("wchc", ""); verifyFuzzyMatch("srst", "reset"); verifyFuzzyMatch("crst", "reset"); verifyFuzzyMatch("stat", "Outstanding"); verifyFuzzyMatch("srvr", "Outstanding"); verifyFuzzyMatch("cons", "queued"); verifyFuzzyMatch("mntr", "zk_server_state\tstandalone"); verifyFuzzyMatch("mntr", "num_alive_connections"); verifyFuzzyMatch("stat", "Connections"); verifyFuzzyMatch("srvr", "Connections"); verifyFuzzyMatch("dirs", "size"); } private void verifyAllCommandsFail() throws Exception { verifyExactMatch("ruok", generateExpectedMessage("ruok")); verifyExactMatch("conf", generateExpectedMessage("conf")); verifyExactMatch("cons", generateExpectedMessage("cons")); verifyExactMatch("crst", generateExpectedMessage("crst")); verifyExactMatch("dirs", generateExpectedMessage("dirs")); verifyExactMatch("dump", generateExpectedMessage("dump")); verifyExactMatch("envi", generateExpectedMessage("envi")); verifyExactMatch("gtmk", generateExpectedMessage("gtmk")); verifyExactMatch("stmk", generateExpectedMessage("stmk")); verifyExactMatch("srst", generateExpectedMessage("srst")); verifyExactMatch("wchc", generateExpectedMessage("wchc")); verifyExactMatch("wchp", generateExpectedMessage("wchp")); verifyExactMatch("wchs", generateExpectedMessage("wchs")); verifyExactMatch("mntr", generateExpectedMessage("mntr")); verifyExactMatch("isro", generateExpectedMessage("isro")); // srvr is enabled by default due to the sad fact zkServer.sh uses it. verifyFuzzyMatch("srvr", "Outstanding"); } private String sendRequest(String cmd) throws IOException, SSLContextException { HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); return send4LetterWord(hpobj.host, hpobj.port, cmd); } private void verifyFuzzyMatch(String cmd, String expected) throws IOException, SSLContextException { String resp = sendRequest(cmd); LOG.info("cmd {} expected {} got {}", cmd, expected, resp); assertTrue(resp.contains(expected)); } private String generateExpectedMessage(String command) { return command + " is not executed because it is not in the whitelist."; } private void verifyExactMatch(String cmd, String expected) throws IOException, SSLContextException { String resp = sendRequest(cmd); LOG.info("cmd {} expected an exact match of {}; got {}", cmd, expected, resp); assertTrue(resp.trim().equals(expected)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_GetC0100644 0000000 0000000 00000000157 15051152474 032623 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/GetChildren2Test.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/GetChildren2Test.jav0100644 0000000 0000000 00000011506 15051152474 034123 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class GetChildren2Test extends ClientBase { private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); zk.close(); } @Test public void testChild() throws IOException, KeeperException, InterruptedException { String name = "/foo"; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); String childname = name + "/bar"; zk.create(childname, childname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); Stat stat = new Stat(); List s = zk.getChildren(name, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid() + 1, stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(1, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(1, stat.getNumChildren()); assertEquals(s.size(), stat.getNumChildren()); s = zk.getChildren(childname, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(zk.getSessionId(), stat.getEphemeralOwner()); assertEquals(childname.length(), stat.getDataLength()); assertEquals(0, stat.getNumChildren()); assertEquals(s.size(), stat.getNumChildren()); } @Test public void testChildren() throws IOException, KeeperException, InterruptedException { String name = "/foo"; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List children = new ArrayList<>(); List children_s = new ArrayList<>(); for (int i = 0; i < 10; i++) { String childname = name + "/bar" + i; String childname_s = "bar" + i; children.add(childname); children_s.add(childname_s); } for (int i = 0; i < children.size(); i++) { String childname = children.get(i); zk.create(childname, childname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); Stat stat = new Stat(); List s = zk.getChildren(name, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid() + i + 1, stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(i + 1, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(i + 1, stat.getNumChildren()); assertEquals(s.size(), stat.getNumChildren()); } List p = zk.getChildren(name, false, null); List c_a = children_s; List c_b = p; Collections.sort(c_a); Collections.sort(c_b); assertEquals(c_a.size(), 10); assertEquals(c_a, c_b); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_GetP0100644 0000000 0000000 00000000165 15051152474 032637 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/GetProposalFromTxnTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/GetProposalFromTxnTe0100644 0000000 0000000 00000012326 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.TxnLogEntry; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.util.SerializeUtils; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; /** * Test loading committed proposal from txnlog. Learner uses these proposals to * catch-up with leader */ public class GetProposalFromTxnTest extends ZKTestCase { private static String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final int CONNECTION_TIMEOUT = 3000; private static final int MSG_COUNT = 2000; /** * Test loading proposal from txnlog * * @throws Exception * an exception might be thrown here */ @Test public void testGetProposalFromTxn() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); SyncRequestProcessor.setSnapCount(100); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); // Generate transaction so we will have some txnlog Long[] zxids = new Long[MSG_COUNT]; try { String data = "data"; byte[] bytes = data.getBytes(); for (int i = 0; i < MSG_COUNT; i++) { Stat stat = new Stat(); zk.create("/invalidsnap-" + i, bytes, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.getData("/invalidsnap-" + i, null, stat); zxids[i] = stat.getCzxid(); } } finally { zk.close(); } // shutdown and start zookeeper again f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server to shutdown"); zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); zks.startdata(); ZKDatabase db = zks.getZKDatabase(); // Set sizeLimit to be very high number, so we can pull all transactions // from txnlog Iterator itr = db.getProposalsFromTxnLog(zxids[0], 10000000); int createCount = 0; ArrayList retrievedZxids = new ArrayList<>(MSG_COUNT); // Get zxid of create requests while (itr.hasNext()) { Proposal proposal = itr.next(); TxnLogEntry logEntry = SerializeUtils.deserializeTxn( proposal.getQuorumPacket().getData()); TxnHeader hdr = logEntry.getHeader(); Record rec = logEntry.getTxn(); if (hdr.getType() == OpCode.create) { retrievedZxids.add(hdr.getZxid()); createCount++; } } // All zxid should match what we created assertTrue(Arrays.equals(zxids, retrievedZxids.toArray(new Long[0])), "Zxids missmatches"); // There should be 2000 create requests assertTrue((createCount == MSG_COUNT), "create proposal count == " + MSG_COUNT); // We are requesting half the number of transaction from the snapshot // this should exceed threshold (ZKDatabase.snapshotSizeFactor) db.setSnapshotSizeFactor(0.33); long sizeLimit = db.calculateTxnLogSizeLimit(); itr = db.getProposalsFromTxnLog(zxids[MSG_COUNT / 2], sizeLimit); assertFalse((itr.hasNext()), "Expect empty proposal"); f.shutdown(); zks.shutdown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Hier0100644 0000000 0000000 00000000165 15051152474 032667 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/HierarchicalQuorumTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/HierarchicalQuorumTe0100644 0000000 0000000 00000031140 15051152474 034306 0ustar00rootroot0000000 0000000 /* Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Properties; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HierarchicalQuorumTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(QuorumBase.class); File s1dir, s2dir, s3dir, s4dir, s5dir; QuorumPeer s1, s2, s3, s4, s5; protected int port1; protected int port2; protected int port3; protected int port4; protected int port5; protected int leport1; protected int leport2; protected int leport3; protected int leport4; protected int leport5; protected int clientport1; protected int clientport2; protected int clientport3; protected int clientport4; protected int clientport5; Properties qp; protected final ClientHammerTest cht = new ClientHammerTest(); @BeforeEach @Override public void setUp() throws Exception { setupTestEnv(); JMXEnv.setUp(); setUpAll(); port1 = PortAssignment.unique(); port2 = PortAssignment.unique(); port3 = PortAssignment.unique(); port4 = PortAssignment.unique(); port5 = PortAssignment.unique(); leport1 = PortAssignment.unique(); leport2 = PortAssignment.unique(); leport3 = PortAssignment.unique(); leport4 = PortAssignment.unique(); leport5 = PortAssignment.unique(); clientport1 = PortAssignment.unique(); clientport2 = PortAssignment.unique(); clientport3 = PortAssignment.unique(); clientport4 = PortAssignment.unique(); clientport5 = PortAssignment.unique(); hostPort = "127.0.0.1:" + clientport1 + ",127.0.0.1:" + clientport2 + ",127.0.0.1:" + clientport3 + ",127.0.0.1:" + clientport4 + ",127.0.0.1:" + clientport5; LOG.info("Ports are: {}", hostPort); s1dir = ClientBase.createTmpDir(); s2dir = ClientBase.createTmpDir(); s3dir = ClientBase.createTmpDir(); s4dir = ClientBase.createTmpDir(); s5dir = ClientBase.createTmpDir(); String config = "group.1=1:2:3\n" + "group.2=4:5\n" + "weight.1=1\n" + "weight.2=1\n" + "weight.3=1\n" + "weight.4=0\n" + "weight.5=0\n" + "server.1=127.0.0.1:" + port1 + ":" + leport1 + ";" + clientport1 + "\n" + "server.2=127.0.0.1:" + port2 + ":" + leport2 + ";" + clientport2 + "\n" + "server.3=127.0.0.1:" + port3 + ":" + leport3 + ";" + clientport3 + "\n" + "server.4=127.0.0.1:" + port4 + ":" + leport4 + ";" + clientport4 + "\n" + "server.5=127.0.0.1:" + port5 + ":" + leport5 + ";" + clientport5 + "\n"; ByteArrayInputStream is = new ByteArrayInputStream(config.getBytes()); this.qp = new Properties(); qp.load(is); startServers(); cht.hostPort = hostPort; cht.setUpAll(); LOG.info("Setup finished"); } /** * This method is here to keep backwards compatibility with the test code * written before observers. * @throws Exception */ void startServers() throws Exception { startServers(false); } /** * Starts 5 Learners. When withObservers == false, all 5 are Followers. * When withObservers == true, 3 are Followers and 2 Observers. * @param withObservers * @throws Exception */ void startServers(boolean withObservers) throws Exception { int tickTime = 2000; int initLimit = 3; int syncLimit = 3; int connectToLearnerMasterLimit = 3; HashMap peers = new HashMap<>(); peers.put(Long.valueOf(1), new QuorumServer(1, new InetSocketAddress("127.0.0.1", port1), new InetSocketAddress("127.0.0.1", leport1), new InetSocketAddress("127.0.0.1", clientport1))); peers.put(Long.valueOf(2), new QuorumServer(2, new InetSocketAddress("127.0.0.1", port2), new InetSocketAddress("127.0.0.1", leport2), new InetSocketAddress("127.0.0.1", clientport2))); peers.put(Long.valueOf(3), new QuorumServer(3, new InetSocketAddress("127.0.0.1", port3), new InetSocketAddress("127.0.0.1", leport3), new InetSocketAddress("127.0.0.1", clientport3))); peers.put(Long.valueOf(4), new QuorumServer(4, new InetSocketAddress("127.0.0.1", port4), new InetSocketAddress("127.0.0.1", leport4), new InetSocketAddress("127.0.0.1", clientport4), withObservers ? QuorumPeer.LearnerType.OBSERVER : QuorumPeer.LearnerType.PARTICIPANT)); peers.put(Long.valueOf(5), new QuorumServer(5, new InetSocketAddress("127.0.0.1", port5), new InetSocketAddress("127.0.0.1", leport5), new InetSocketAddress("127.0.0.1", clientport5), withObservers ? QuorumPeer.LearnerType.OBSERVER : QuorumPeer.LearnerType.PARTICIPANT)); LOG.info("creating QuorumPeer 1 port {}", clientport1); if (withObservers) { qp.setProperty("server.4", "127.0.0.1:" + port4 + ":" + leport4 + ":observer" + ";" + clientport4); qp.setProperty("server.5", "127.0.0.1:" + port5 + ":" + leport5 + ":observer" + ";" + clientport5); } QuorumHierarchical hq1 = new QuorumHierarchical(qp); s1 = new QuorumPeer(peers, s1dir, s1dir, clientport1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, hq1); assertEquals(clientport1, s1.getClientPort()); LOG.info("creating QuorumPeer 2 port {}", clientport2); QuorumHierarchical hq2 = new QuorumHierarchical(qp); s2 = new QuorumPeer(peers, s2dir, s2dir, clientport2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, hq2); assertEquals(clientport2, s2.getClientPort()); LOG.info("creating QuorumPeer 3 port {}", clientport3); QuorumHierarchical hq3 = new QuorumHierarchical(qp); s3 = new QuorumPeer(peers, s3dir, s3dir, clientport3, 3, 3, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, hq3); assertEquals(clientport3, s3.getClientPort()); LOG.info("creating QuorumPeer 4 port {}", clientport4); QuorumHierarchical hq4 = new QuorumHierarchical(qp); s4 = new QuorumPeer(peers, s4dir, s4dir, clientport4, 3, 4, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, hq4); if (withObservers) { s4.setLearnerType(QuorumPeer.LearnerType.OBSERVER); } assertEquals(clientport4, s4.getClientPort()); LOG.info("creating QuorumPeer 5 port {}", clientport5); QuorumHierarchical hq5 = new QuorumHierarchical(qp); s5 = new QuorumPeer(peers, s5dir, s5dir, clientport5, 3, 5, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, hq5); if (withObservers) { s5.setLearnerType(QuorumPeer.LearnerType.OBSERVER); } assertEquals(clientport5, s5.getClientPort()); LOG.info("start QuorumPeer 1"); s1.start(); LOG.info("start QuorumPeer 2"); s2.start(); LOG.info("start QuorumPeer 3"); s3.start(); LOG.info("start QuorumPeer 4{}", (withObservers ? "(observer)" : "")); s4.start(); LOG.info("start QuorumPeer 5{}", (withObservers ? "(observer)" : "")); s5.start(); LOG.info("started QuorumPeer 5"); LOG.info("Closing ports {}", hostPort); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerUp(hp, CONNECTION_TIMEOUT), "waiting for server up"); LOG.info("{} is accepting client connections", hp); } final int numberOfPeers = 5; // interesting to see what's there... JMXEnv.dump(); // make sure we have these 5 servers listed Set ensureNames = new LinkedHashSet<>(); for (int i = 1; i <= numberOfPeers; i++) { ensureNames.add("InMemoryDataTree"); } for (int i = 1; i <= numberOfPeers; i++) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + i + ",name2="); } for (int i = 1; i <= numberOfPeers; i++) { for (int j = 1; j <= numberOfPeers; j++) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + j); } } for (int i = 1; i <= numberOfPeers; i++) { ensureNames.add("name0=ReplicatedServer_id" + i); } JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()])); for (int i = 1; i <= numberOfPeers; i++) { // LocalPeerBean String bean = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + i + ",name1=replica." + i; JMXEnv.ensureBeanAttribute(bean, "ConfigVersion"); JMXEnv.ensureBeanAttribute(bean, "LearnerType"); JMXEnv.ensureBeanAttribute(bean, "ClientAddress"); JMXEnv.ensureBeanAttribute(bean, "ElectionAddress"); JMXEnv.ensureBeanAttribute(bean, "QuorumSystemInfo"); JMXEnv.ensureBeanAttribute(bean, "Leader"); } for (int i = 1; i <= numberOfPeers; i++) { for (int j = 1; j <= numberOfPeers; j++) { if (j != i) { // RemotePeerBean String bean = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + i + ",name1=replica." + j; JMXEnv.ensureBeanAttribute(bean, "Name"); JMXEnv.ensureBeanAttribute(bean, "LearnerType"); JMXEnv.ensureBeanAttribute(bean, "ClientAddress"); JMXEnv.ensureBeanAttribute(bean, "ElectionAddress"); JMXEnv.ensureBeanAttribute(bean, "QuorumAddress"); JMXEnv.ensureBeanAttribute(bean, "Leader"); } } } } @AfterEach @Override public void tearDown() throws Exception { LOG.info("TearDown started"); cht.tearDownAll(); LOG.info("Shutting down server 1"); shutdown(s1); LOG.info("Shutting down server 2"); shutdown(s2); LOG.info("Shutting down server 3"); shutdown(s3); LOG.info("Shutting down server 4"); shutdown(s4); LOG.info("Shutting down server 5"); shutdown(s5); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerDown(hp, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); LOG.info("{} is no longer accepting client connections", hp); } JMXEnv.tearDown(); } protected void shutdown(QuorumPeer qp) { QuorumBase.shutdown(qp); } protected TestableZooKeeper createClient() throws IOException, InterruptedException { return createClient(hostPort); } protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { CountdownWatcher watcher = new CountdownWatcher(); return createClient(watcher, hp); } @Test public void testHierarchicalQuorum() throws Throwable { cht.runHammer(5, 10); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/IPAuthTest.java0100644 0000000 0000000 00000005656 15051152474 033155 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.auth.IPAuthenticationProvider; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; public class IPAuthTest { @Before public void setUp() { System.setProperty(IPAuthenticationProvider.USE_X_FORWARDED_FOR_KEY, "true"); } @After public void tearDown() { System.clearProperty(IPAuthenticationProvider.USE_X_FORWARDED_FOR_KEY); } @Test public void testHandleAuthentication_Forwarded() { final IPAuthenticationProvider provider = new IPAuthenticationProvider(); final HttpServletRequest mockRequest = mock(HttpServletRequest.class); final String forwardedForHeader = "fc00:0:0:0:0:0:0:4, 192.168.0.6, 10.0.0.8, 172.16.0.9"; Mockito.doReturn(forwardedForHeader).when(mockRequest).getHeader(IPAuthenticationProvider.X_FORWARDED_FOR_HEADER_NAME); Mockito.doReturn("192.168.0.5").when(mockRequest).getRemoteAddr(); // validate it returns the leftmost IP from the X-Forwarded-For header final List expectedIds = Arrays.asList(new Id(provider.getScheme(), "fc00:0:0:0:0:0:0:4")); assertEquals(expectedIds, provider.handleAuthentication(mockRequest, null)); } @Test public void testHandleAuthentication_NoForwarded() { final IPAuthenticationProvider provider = new IPAuthenticationProvider(); final HttpServletRequest mockRequest = mock(HttpServletRequest.class); Mockito.doReturn(null).when(mockRequest).getHeader(IPAuthenticationProvider.X_FORWARDED_FOR_HEADER_NAME); Mockito.doReturn("192.168.0.6").when(mockRequest).getRemoteAddr(); // validate it returns the remote address final List expectedIds = Arrays.asList(new Id(provider.getScheme(), "192.168.0.6")); assertEquals(expectedIds, provider.handleAuthentication(mockRequest, null)); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/IntegrityCheck.java0100644 0000000 0000000 00000016032 15051152474 034065 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; /** * This is a simple test to check the integrity of ZooKeeper servers. The client * simply cycles through blasting changes to ZooKeeper and the checking what it * gets back. * * The check is very simple. The value of the last successful read or write is * stored in lastValue. When we issue a request, that value becomes a possible * value. The difficulty is that when a communication error happens, the client * doesn't know if the set actually went through. So, our invariant that we * check for is that we always read a value that is greater than or equal to * a value that we have previously read or set. (Each time we set a value, the * value will be one more than the previous set.) */ import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ExitCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class IntegrityCheck implements StatCallback, DataCallback { private static final Logger LOG = LoggerFactory.getLogger(IntegrityCheck.class); ZooKeeper zk; Map lastValue = new HashMap<>(); int count; String path; int iteration; int outstanding; int errorCount; synchronized void incOutstanding() { outstanding++; } synchronized void decOutstanding() { outstanding--; notifyAll(); } synchronized void waitOutstanding() throws InterruptedException { while (outstanding > 0) { wait(); } } IntegrityCheck(String hostPort, String path, int count) throws Exception { zk = ClientBase.createZKClient(hostPort); this.path = path; this.count = count; } public void run() throws InterruptedException, KeeperException { try { LOG.warn("Creating znodes for {}", path); doCreate(); LOG.warn("Staring the test loop for {}", path); while (true) { LOG.warn("Staring write cycle for {}", path); doPopulate(); waitOutstanding(); LOG.warn("Staring read cycle for {}", path); readAll(); waitOutstanding(); } } finally { LOG.warn("Test loop terminated for {}", path); } } void readAll() { for (int i = 0; i < count; i++) { String cpath = path + "/" + i; zk.getData(cpath, false, this, null); incOutstanding(); } } void doCreate() throws InterruptedException, KeeperException { // create top level znode try { zk.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException e) { // ignore duplicate create } iteration++; byte[] v = ("" + iteration).getBytes(); // create child znodes for (int i = 0; i < count; i++) { String cpath = path + "/" + i; try { if (i % 10 == 0) { LOG.warn("Creating znode {}", cpath); } zk.create(cpath, v, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException e) { // ignore duplicate create } lastValue.put(cpath, v); } } void doPopulate() { iteration++; byte[] v = ("" + iteration).getBytes(); for (int i = 0; i < count; i++) { String cpath = path + "/" + i; zk.setData(cpath, v, -1, this, v); incOutstanding(); } } synchronized void ensureConnected() { while (zk.getState() != ZooKeeper.States.CONNECTED) { try { wait(); } catch (InterruptedException e) { return; } } } /** * @param args */ public static void main(String[] args) { if (args.length < 3) { System.err.println("USAGE: IntegrityCheck zookeeperHostPort znode #children"); return; } int childrenCount = 0; try { childrenCount = Integer.parseInt(args[2]); } catch (NumberFormatException e) { e.printStackTrace(); System.exit(ExitCode.UNEXPECTED_ERROR.getValue()); } try { final IntegrityCheck ctest = new IntegrityCheck(args[0], args[1], childrenCount); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { System.out.println(new Date().toString() + ": Error count = " + ctest.errorCount); } }); while (true) { try { ctest.ensureConnected(); ctest.run(); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); System.exit(ExitCode.INVALID_INVOCATION.getValue()); } } public void processResult(int rc, String path, Object ctx, Stat stat) { if (rc == KeeperException.Code.OK.intValue()) { lastValue.put(path, (byte[]) ctx); } decOutstanding(); } public void processResult( int rc, String path, Object ctx, byte[] data, Stat stat) { if (rc == KeeperException.Code.OK.intValue()) { String string = new String(data); String lastString = null; byte[] v = lastValue.get(path); if (v != null) { lastString = new String(v); } if (lastString != null && Integer.parseInt(string) < Integer.parseInt(lastString)) { LOG.error("ERROR: Got {} expected >= {}", string, lastString); errorCount++; } lastValue.put(path, (byte[]) ctx); } decOutstanding(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Inva0100644 0000000 0000000 00000000162 15051152474 032672 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/InvalidSnapshotTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/InvalidSnapshotTest.0100644 0000000 0000000 00000011334 15051152474 034255 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SnapshotFormatter; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class InvalidSnapshotTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(InvalidSnapshotTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final File testData = new File(System.getProperty("test.data.dir", "src/test/resources/data")); /** * Verify the SnapshotFormatter by running it on a known file. */ @Test public void testSnapshotFormatter() throws Exception { File snapDir = new File(testData, "invalidsnap"); File snapfile = new File(new File(snapDir, "version-2"), "snapshot.272"); String[] args = {snapfile.getCanonicalFile().toString()}; SnapshotFormatter.main(args); } /** * Verify the SnapshotFormatter by running it on a known file with one null data. */ @Test public void testSnapshotFormatterWithNull() throws Exception { File snapDir = new File(testData, "invalidsnap"); File snapfile = new File(new File(snapDir, "version-2"), "snapshot.273"); String[] args = {snapfile.getCanonicalFile().toString()}; SnapshotFormatter.main(args); } /** * Verify the SnapshotFormatter fails as expected on corrupted snapshot. */ @Test public void testSnapshotFormatterWithInvalidSnap() throws Exception { File snapDir = new File(testData, "invalidsnap"); // Broken snapshot introduced by ZOOKEEPER-367, and used to // demonstrate recovery in testSnapshot below. File snapfile = new File(new File(snapDir, "version-2"), "snapshot.83f"); String[] args = {snapfile.getCanonicalFile().toString()}; try { SnapshotFormatter.main(args); fail("Snapshot '" + snapfile + "' unexpectedly parsed without error."); } catch (IOException e) { assertTrue(e.getMessage().contains("Unreasonable length = 977468229")); } } /** * test the snapshot * @throws Exception an exception could be expected */ @Test public void testSnapshot() throws Exception { File origSnapDir = new File(testData, "invalidsnap"); // This test otherwise updates the resources directory. File snapDir = ClientBase.createTmpDir(); FileUtils.copyDirectory(origSnapDir, snapDir); ZooKeeperServer zks = new ZooKeeperServer(snapDir, snapDir, 3000); SyncRequestProcessor.setSnapCount(1000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); LOG.info("starting up the zookeeper server .. waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up"); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { // we know this from the data files // this node is the last node in the snapshot assertTrue(zk.exists("/9/9/8", false) != null); } finally { zk.close(); } f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/JMXEnv.java0100644 0000000 0000000 00000031103 15051152474 032254 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import org.apache.zookeeper.jmx.MBeanRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JMXEnv { protected static final Logger LOG = LoggerFactory.getLogger(JMXEnv.class); private static JMXConnectorServer cs; private static JMXConnector cc; public static void setUp() throws IOException { MBeanServer mbs = MBeanRegistry.getInstance().getPlatformMBeanServer(); JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://127.0.0.1"); cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); cs.start(); JMXServiceURL addr = cs.getAddress(); LOG.info("connecting to addr {}", addr); cc = JMXConnectorFactory.connect(addr); } public static void tearDown() { try { if (cc != null) { cc.close(); } } catch (IOException e) { LOG.warn("Unexpected, ignoring", e); } cc = null; try { if (cs != null) { cs.stop(); } } catch (IOException e) { LOG.warn("Unexpected, ignoring", e); } cs = null; } public static MBeanServerConnection conn() throws IOException { return cc.getMBeanServerConnection(); } /** * Ensure that all of the specified names are registered. * Note that these are components of the name, and in particular * order matters - you want the more specific name (leafs) specified * before their parent(s) (since names are hierarchical) * It waits in a loop up to 60 seconds before failing if there is a * mismatch. * @param expectedNames * @return * @throws IOException * @throws InterruptedException */ public static Set ensureAll(String... expectedNames) throws IOException, InterruptedException { Set beans; Set found; int nTry = 0; do { if (nTry++ > 0) { Thread.sleep(100); } try { beans = conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } found = new HashSet<>(); for (String name : expectedNames) { LOG.info("expect:{}", name); for (ObjectName bean : beans) { if (bean.toString().contains(name)) { LOG.info("found:{} {}", name, bean); found.add(bean); break; } } beans.removeAll(found); } } while ((expectedNames.length != found.size()) && (nTry < 600)); assertEquals(expectedNames.length, found.size(), "expected " + Arrays.toString(expectedNames)); return beans; } /** * Ensure that only the specified names are registered. * Note that these are components of the name, and in particular * order matters - you want the more specific name (leafs) specified * before their parent(s) (since names are hierarchical) * @param expectedNames * @return * @throws IOException * @throws InterruptedException */ public static Set ensureOnly(String... expectedNames) throws IOException, InterruptedException { LOG.info("ensureOnly:{}", Arrays.toString(expectedNames)); Set beans = ensureAll(expectedNames); for (ObjectName bean : beans) { LOG.info("unexpected:{}", bean.toString()); } assertEquals(0, beans.size()); return beans; } public static void ensureNone(String... expectedNames) throws IOException, InterruptedException { Set beans; int nTry = 0; boolean foundUnexpected = false; String unexpectedName = ""; do { if (nTry++ > 0) { Thread.sleep(100); } try { beans = conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } foundUnexpected = false; for (String name : expectedNames) { for (ObjectName bean : beans) { if (bean.toString().contains(name)) { LOG.info("didntexpect:{}", name); foundUnexpected = true; unexpectedName = name + " " + bean.toString(); break; } } if (foundUnexpected) { break; } } } while ((foundUnexpected) && (nTry < 600)); if (foundUnexpected) { LOG.info("List of all beans follows:"); for (ObjectName bean : beans) { LOG.info("bean:{}", bean.toString()); } fail(unexpectedName); } } public static void dump() throws IOException { LOG.info("JMXEnv.dump() follows"); Set beans; try { beans = conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } for (ObjectName bean : beans) { LOG.info("bean:{}", bean.toString()); } } /** * Ensure that the specified parent names are registered. Note that these * are components of the name. It waits in a loop up to 60 seconds before * failing if there is a mismatch. This will return the beans which are not * matched. * * https://issues.apache.org/jira/browse/ZOOKEEPER-1858 * * @param expectedNames * - expected beans * @return the beans which are not matched with the given expected names * * @throws IOException * @throws InterruptedException */ public static Set ensureParent(String... expectedNames) throws IOException, InterruptedException { LOG.info("ensureParent:{}", Arrays.toString(expectedNames)); Set beans; int nTry = 0; Set found = new HashSet<>(); do { if (nTry++ > 0) { Thread.sleep(500); } try { beans = conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } found.clear(); for (String name : expectedNames) { LOG.info("expect:{}", name); for (ObjectName bean : beans) { // check the existence of name in bean if (compare(bean.toString(), name)) { LOG.info("found:{} {}", name, bean); found.add(bean); break; } } beans.removeAll(found); } } while (expectedNames.length != found.size() && nTry < 120); assertEquals(expectedNames.length, found.size(), "expected " + Arrays.toString(expectedNames)); return beans; } /** * Ensure that the specified bean name and its attribute is registered. Note * that these are components of the name. It waits in a loop up to 60 * seconds before failing if there is a mismatch. This will return the beans * which are not matched. * * @param expectedName * - expected bean * @param expectedAttribute * - expected attribute * @return the value of the attribute * * @throws Exception */ public static Object ensureBeanAttribute(String expectedName, String expectedAttribute) throws Exception { String value = ""; LOG.info("ensure bean:{}, attribute:{}", expectedName, expectedAttribute); Set beans; int nTry = 0; do { if (nTry++ > 0) { Thread.sleep(500); } try { beans = conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } LOG.info("expect:{}", expectedName); for (ObjectName bean : beans) { // check the existence of name in bean if (bean.toString().equals(expectedName)) { LOG.info("found:{} {}", expectedName, bean); return conn().getAttribute(bean, expectedAttribute); } } } while (nTry < 120); fail("Failed to find bean:" + expectedName + ", attribute:" + expectedAttribute); return value; } /** * Comparing that the given name exists in the bean. For component beans, * the component name will be present at the end of the bean name * * For example 'StandaloneServer' will present in the bean name like * 'org.apache.ZooKeeperService:name0=StandaloneServer_port-1' */ private static boolean compare(String bean, String name) { String[] names = bean.split("="); return names.length > 0 && names[names.length - 1].contains(name); } static Pattern standaloneRegEx = Pattern.compile("^org.apache.ZooKeeperService:name0=StandaloneServer_port-?\\d+$"); static Pattern instanceRegEx = Pattern.compile("^org.apache.ZooKeeperService:name0=ReplicatedServer_id(\\d+)" + ",name1=replica.(\\d+),name2=(Follower|Leader)$"); static Pattern observerRegEx = Pattern.compile("^org.apache.ZooKeeperService:name0=ReplicatedServer_id(-?\\d+)" + ",name1=replica.(-?\\d+),name2=(StandaloneServer_port-?\\d+)$"); static List beanPatterns = Arrays.asList(standaloneRegEx, instanceRegEx, observerRegEx); public static List getServerBeans() throws IOException { ArrayList serverBeans = new ArrayList<>(); Set beans; try { beans = conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null); } catch (MalformedObjectNameException e) { throw new RuntimeException(e); } for (ObjectName bean : beans) { String name = bean.toString(); LOG.info("bean:{}", name); for (Pattern pattern : beanPatterns) { if (pattern.matcher(name).find()) { serverBeans.add(bean); } } } return serverBeans; } public static ObjectName getServerBean() throws Exception { List serverBeans = getServerBeans(); if (serverBeans.size() != 1) { throw new RuntimeException("Unable to find one and only one server bean"); } return serverBeans.get(0); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Keep0100644 0000000 0000000 00000000156 15051152474 032664 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/KeeperStateTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/KeeperStateTest.java0100644 0000000 0000000 00000004674 15051152474 034236 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.EnumSet; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.Test; public class KeeperStateTest extends ZKTestCase { @Test public void testIntConversion() { // Ensure that we can convert all valid integers to KeeperStates EnumSet allStates = EnumSet.allOf(KeeperState.class); for (KeeperState as : allStates) { assertEquals(as, KeeperState.fromInt(as.getIntValue())); } } @Test public void testInvalidIntConversion() { try { KeeperState.fromInt(324142); fail("Was able to create an invalid KeeperState via an integer"); } catch (RuntimeException re) { // we're good. } } /** Validate that the deprecated constant still works. There were issues * found with switch statements - which need compile time constants. */ @Test @SuppressWarnings("deprecation") public void testDeprecatedCodeOkInSwitch() { int test = 1; switch (test) { case Code.Ok: assertTrue(true); break; } } /** Verify the enum works (paranoid) */ @Test public void testCodeOKInSwitch() { Code test = Code.OK; switch (test) { case OK: assertTrue(true); break; } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_KeyA0100644 0000000 0000000 00000000160 15051152474 032624 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/KeyAuthClientTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/KeyAuthClientTest.ja0100644 0000000 0000000 00000011047 15051152474 034174 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class KeyAuthClientTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(KeyAuthClientTest.class); static { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.KeyAuthenticationProvider"); } public void createNodePrintAcl(ZooKeeper zk, String path, String testName) { try { LOG.debug("KeyAuthenticationProvider Creating Test Node:{}\n", path); zk.create(path, null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); List acls = zk.getACL(path, null); LOG.debug("Node:{} Test:{} ACLs:", path, testName); for (ACL acl : acls) { LOG.debug(" {}", acl.toString()); } } catch (Exception e) { LOG.debug(" EXCEPTION THROWN", e); } } public void preAuth() throws Exception { ZooKeeper zk = createClient(); zk.addAuthInfo("key", "25".getBytes()); try { createNodePrintAcl(zk, "/pre", "testPreAuth"); zk.setACL("/", Ids.CREATOR_ALL_ACL, -1); zk.getChildren("/", false); zk.create("/abc", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.setData("/abc", "testData1".getBytes(), -1); zk.create("/key", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); zk.setData("/key", "5".getBytes(), -1); Thread.sleep(1000); } catch (KeeperException e) { fail("test failed :" + e); } finally { zk.close(); } } public void missingAuth() throws Exception { ZooKeeper zk = createClient(); try { zk.getData("/abc", false, null); fail("Should not be able to get data"); } catch (KeeperException correct) { // correct } try { zk.setData("/abc", "testData2".getBytes(), -1); fail("Should not be able to set data"); } catch (KeeperException correct) { // correct } finally { zk.close(); } } public void validAuth() throws Exception { ZooKeeper zk = createClient(); // any multiple of 5 will do... zk.addAuthInfo("key", "25".getBytes()); try { createNodePrintAcl(zk, "/valid", "testValidAuth"); zk.getData("/abc", false, null); zk.setData("/abc", "testData3".getBytes(), -1); } catch (KeeperException.AuthFailedException e) { fail("test failed :" + e); } finally { zk.close(); } } public void validAuth2() throws Exception { ZooKeeper zk = createClient(); // any multiple of 5 will do... zk.addAuthInfo("key", "125".getBytes()); try { createNodePrintAcl(zk, "/valid2", "testValidAuth2"); zk.getData("/abc", false, null); zk.setData("/abc", "testData3".getBytes(), -1); } catch (KeeperException.AuthFailedException e) { fail("test failed :" + e); } finally { zk.close(); } } @Test public void testAuth() throws Exception { // NOTE: the tests need to run in-order, and older versions of // junit don't provide any way to order tests preAuth(); missingAuth(); validAuth(); validAuth2(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Lead0100644 0000000 0000000 00000000167 15051152474 032647 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LeaderSessionTrackerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LeaderSessionTracker0100644 0000000 0000000 00000013520 15051152474 034304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestRecord; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Due to race condition or bad client code, the leader may get request from * expired session. We need to make sure that we never allow ephmeral node * to be created in those case, but we do allow normal node to be created. */ public class LeaderSessionTrackerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(LeaderSessionTrackerTest.class); QuorumUtil qu; @BeforeEach public void setUp() throws Exception { qu = new QuorumUtil(1); } @AfterEach public void tearDown() throws Exception { qu.shutdownAll(); } @Test public void testExpiredSessionWithLocalSession() throws Exception { testCreateEphemeral(true); } @Test public void testExpiredSessionWithoutLocalSession() throws Exception { testCreateEphemeral(false); } /** * When we create ephemeral node, we need to check against global * session, so the leader never accept request from an expired session * (that we no longer track) * * This is not the same as SessionInvalidationTest since session * is not in closing state */ public void testCreateEphemeral(boolean localSessionEnabled) throws Exception { if (localSessionEnabled) { qu.enableLocalSession(true); } qu.startAll(); QuorumPeer leader = qu.getLeaderQuorumPeer(); ZooKeeper zk = ClientBase.createZKClient(qu.getConnectString(leader)); CreateRequest createRequest = new CreateRequest("/impossible", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL.toFlag()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); createRequest.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); // Mimic sessionId generated by follower's local session tracker long sid = qu.getFollowerQuorumPeers().get(0).getActiveServer().getServerId(); long fakeSessionId = (sid << 56) + 1; LOG.info("Fake session Id: {}", Long.toHexString(fakeSessionId)); Request request = new Request(null, fakeSessionId, 0, OpCode.create, RequestRecord.fromBytes(bb), new ArrayList()); // Submit request directly to leader leader.getActiveServer().submitRequest(request); // Make sure that previous request is finished zk.create("/ok", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = zk.exists("/impossible", null); assertEquals(null, stat, "Node from fake session get created"); } /** * When local session is enabled, leader will allow persistent node * to be create for unknown session */ @Test public void testCreatePersistent() throws Exception { qu.enableLocalSession(true); qu.startAll(); QuorumPeer leader = qu.getLeaderQuorumPeer(); ZooKeeper zk = ClientBase.createZKClient(qu.getConnectString(leader)); CreateRequest createRequest = new CreateRequest("/success", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT.toFlag()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); createRequest.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); // Mimic sessionId generated by follower's local session tracker long sid = qu.getFollowerQuorumPeers().get(0).getActiveServer().getServerId(); long locallSession = (sid << 56) + 1; LOG.info("Local session Id: {}", Long.toHexString(locallSession)); Request request = new Request(null, locallSession, 0, OpCode.create, RequestRecord.fromBytes(bb), new ArrayList()); // Submit request directly to leader leader.getActiveServer().submitRequest(request); // Make sure that previous request is finished zk.create("/ok", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = zk.exists("/success", null); assertTrue(stat != null, "Request from local sesson failed"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Load0100644 0000000 0000000 00000000166 15051152474 032660 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LoadFromLogNoServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LoadFromLogNoServerT0100644 0000000 0000000 00000020355 15051152474 034211 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import org.apache.jute.BinaryInputArchive; import org.apache.jute.BinaryOutputArchive; import org.apache.jute.Record; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.server.DataNode; import org.apache.zookeeper.server.DataTree; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileHeader; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.txn.CreateTxn; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.MultiTxn; import org.apache.zookeeper.txn.Txn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoadFromLogNoServerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(LoadFromLogNoServerTest.class); /** * For ZOOKEEPER-1046. Verify if cversion and pzxid if incremented * after create/delete failure during restore. */ @Test public void testTxnFailure() throws Exception { try { ZooKeeperServer.setDigestEnabled(true); long count = 1; File tmpDir = ClientBase.createTmpDir(); FileTxnSnapLog logFile = new FileTxnSnapLog(tmpDir, tmpDir); DataTree dt = new DataTree(); dt.createNode("/test", new byte[0], null, 0, -1, 1, 1); for (count = 1; count <= 3; count++) { dt.createNode("/test/" + count, new byte[0], null, 0, -1, count, Time.currentElapsedTime()); } long digestBefore = dt.getTreeDigest(); DataNode zk = dt.getNode("/test"); // Make create to fail, then verify cversion. LOG.info("Attempting to create /test/{}", (count - 1)); doOp(logFile, ZooDefs.OpCode.create, "/test/" + (count - 1), dt, zk, -1); assertNotEquals(digestBefore, dt.getTreeDigest()); LOG.info("Attempting to create /test/{}", (count - 1)); digestBefore = dt.getTreeDigest(); doOp(logFile, ZooDefs.OpCode.create, "/test/" + (count - 1), dt, zk, zk.stat.getCversion() + 1); assertNotEquals(digestBefore, dt.getTreeDigest()); LOG.info("Attempting to create /test/{}", (count - 1)); digestBefore = dt.getTreeDigest(); doOp(logFile, ZooDefs.OpCode.multi, "/test/" + (count - 1), dt, zk, zk.stat.getCversion() + 1); assertNotEquals(digestBefore, dt.getTreeDigest()); LOG.info("Attempting to create /test/{}", (count - 1)); digestBefore = dt.getTreeDigest(); doOp(logFile, ZooDefs.OpCode.multi, "/test/" + (count - 1), dt, zk, -1); assertNotEquals(digestBefore, dt.getTreeDigest()); // Make delete fo fail, then verify cversion. // this doesn't happen anymore, we only set the cversion on create // LOG.info("Attempting to delete " + "/test/" + (count + 1)); // doOp(logFile, OpCode.delete, "/test/" + (count + 1), dt, zk); } finally { ZooKeeperServer.setDigestEnabled(false); } } /* * Does create/delete depending on the type and verifies * if cversion before the operation is 1 less than cversion afer. */ private void doOp(FileTxnSnapLog logFile, int type, String path, DataTree dt, DataNode parent, int cversion) throws Exception { int lastSlash = path.lastIndexOf('/'); String parentName = path.substring(0, lastSlash); int prevCversion = parent.stat.getCversion(); long prevPzxid = parent.stat.getPzxid(); List child = dt.getChildren(parentName, null, null); StringBuilder childStr = new StringBuilder(); for (String s : child) { childStr.append(s).append(" "); } LOG.info("Children: {} for {}", childStr, parentName); LOG.info("(cverions, pzxid): {}, {}", prevCversion, prevPzxid); Record txn = null; TxnHeader txnHeader = null; if (type == ZooDefs.OpCode.delete) { txn = new DeleteTxn(path); txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, Time.currentElapsedTime(), ZooDefs.OpCode.delete); } else if (type == ZooDefs.OpCode.create) { txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, Time.currentElapsedTime(), ZooDefs.OpCode.create); txn = new CreateTxn(path, new byte[0], null, false, cversion); } else if (type == ZooDefs.OpCode.multi) { txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, Time.currentElapsedTime(), ZooDefs.OpCode.create); txn = new CreateTxn(path, new byte[0], null, false, cversion); List txnList = new ArrayList<>(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); txn.serialize(boa, "request"); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); Txn txact = new Txn(ZooDefs.OpCode.create, bb.array()); txnList.add(txact); txn = new MultiTxn(txnList); txnHeader = new TxnHeader(0xabcd, 0x123, prevPzxid + 1, Time.currentElapsedTime(), ZooDefs.OpCode.multi); } logFile.processTransaction(txnHeader, dt, null, txn); int newCversion = parent.stat.getCversion(); long newPzxid = parent.stat.getPzxid(); child = dt.getChildren(parentName, null, null); childStr = new StringBuilder(); for (String s : child) { childStr.append(s).append(" "); } LOG.info("Children: {} for {}", childStr, parentName); LOG.info("(cverions, pzxid): {}, {}", newCversion, newPzxid); assertTrue((newCversion == prevCversion + 1 && newPzxid == prevPzxid + 1), type + " verification failed. Expected: <" + (prevCversion + 1) + ", " + (prevPzxid + 1) + ">, found: <" + newCversion + ", " + newPzxid + ">"); } /** * Simulates ZOOKEEPER-1069 and verifies that flush() before padLogFile * fixes it. */ @Test public void testPad() throws Exception { File tmpDir = ClientBase.createTmpDir(); FileTxnLog txnLog = new FileTxnLog(tmpDir); TxnHeader txnHeader = new TxnHeader(0xabcd, 0x123, 0x123, Time.currentElapsedTime(), ZooDefs.OpCode.create); Record txn = new CreateTxn("/Test", new byte[0], null, false, 1); txnLog.append(new Request(0, 0, 0, txnHeader, txn, 0)); FileInputStream in = new FileInputStream(tmpDir.getPath() + "/log." + Long.toHexString(txnHeader.getZxid())); BinaryInputArchive ia = BinaryInputArchive.getArchive(in); FileHeader header = new FileHeader(); header.deserialize(ia, "fileheader"); LOG.info("Received magic : {} Expected : {}", header.getMagic(), FileTxnLog.TXNLOG_MAGIC); assertTrue(header.getMagic() == FileTxnLog.TXNLOG_MAGIC, "Missing magic number "); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Load0100644 0000000 0000000 00000000156 15051152474 032657 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LoadFromLogTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LoadFromLogTest.java0100644 0000000 0000000 00000027541 15051152474 034165 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException.NoNodeException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.FileTxnLog.FileTxnIterator; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.server.persistence.Util; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoadFromLogTest extends ClientBase { private static final int NUM_MESSAGES = 300; protected static final Logger LOG = LoggerFactory.getLogger(LoadFromLogTest.class); // setting up the quorum has a transaction overhead for creating and closing the session private static final int TRANSACTION_OVERHEAD = 2; private static final int TOTAL_TRANSACTIONS = NUM_MESSAGES + TRANSACTION_OVERHEAD; @BeforeEach public void setUp() throws Exception { SyncRequestProcessor.setSnapCount(50); super.setUp(); } /** * test that all transactions from the Log are loaded, and only once * @throws Exception an exception might be thrown here */ @Test public void testLoad() throws Exception { // generate some transactions that will get logged ZooKeeper zk = createZKClient(hostPort); try { for (int i = 0; i < NUM_MESSAGES; i++) { zk.create("/invalidsnap-" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } stopServer(); // now verify that the FileTxnLog reads every transaction only once File logDir = new File(tmpDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); FileTxnLog txnLog = new FileTxnLog(logDir); TxnIterator itr = txnLog.read(0); // Check that storage space return some value FileTxnIterator fileItr = (FileTxnIterator) itr; long storageSize = fileItr.getStorageSize(); LOG.info("Txnlog size: {} bytes", storageSize); assertTrue((storageSize > 0), "Storage size is greater than zero "); long expectedZxid = 0; long lastZxid = 0; TxnHeader hdr; do { hdr = itr.getHeader(); expectedZxid++; assertTrue(lastZxid != hdr.getZxid(), "not the same transaction. lastZxid=" + lastZxid + ", zxid=" + hdr.getZxid()); assertTrue((hdr.getZxid() == expectedZxid), "excepting next transaction. expected=" + expectedZxid + ", retrieved=" + hdr.getZxid()); lastZxid = hdr.getZxid(); } while (itr.next()); assertTrue((expectedZxid == TOTAL_TRANSACTIONS), "processed all transactions. " + expectedZxid + " == " + TOTAL_TRANSACTIONS); } /** * test that we fail to load txnlog of a request zxid that is older * than what exist on disk * @throws Exception an exception might be thrown here */ @Test public void testLoadFailure() throws Exception { // generate some transactions that will get logged ZooKeeper zk = createZKClient(hostPort); try { for (int i = 0; i < NUM_MESSAGES; i++) { zk.create("/data-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } } finally { zk.close(); } stopServer(); File logDir = new File(tmpDir, FileTxnSnapLog.version + FileTxnSnapLog.VERSION); File[] logFiles = FileTxnLog.getLogFiles(logDir.listFiles(), 0); // Verify that we have at least NUM_MESSAGES / SNAPCOUNT txnlog assertTrue(logFiles.length > NUM_MESSAGES / 100); // Delete the first log file, so we will fail to read it back from disk assertTrue(logFiles[0].delete(), "delete the first log file"); // Find zxid for the second log long secondStartZxid = Util.getZxidFromName(logFiles[1].getName(), "log"); FileTxnLog txnLog = new FileTxnLog(logDir); TxnIterator itr = txnLog.read(1, false); // Oldest log is already remove, so this should point to the start of // of zxid on the second log assertEquals(secondStartZxid, itr.getHeader().getZxid()); itr = txnLog.read(secondStartZxid, false); assertEquals(secondStartZxid, itr.getHeader().getZxid()); assertTrue(itr.next()); // Trying to get a second txn on second txnlog give us the // the start of second log, since the first one is removed long nextZxid = itr.getHeader().getZxid(); itr = txnLog.read(nextZxid, false); assertEquals(secondStartZxid, itr.getHeader().getZxid()); // Trying to get a first txn on the third give us the // the start of second log, since the first one is removed long thirdStartZxid = Util.getZxidFromName(logFiles[2].getName(), "log"); itr = txnLog.read(thirdStartZxid, false); assertEquals(secondStartZxid, itr.getHeader().getZxid()); assertTrue(itr.next()); nextZxid = itr.getHeader().getZxid(); itr = txnLog.read(nextZxid, false); assertEquals(secondStartZxid, itr.getHeader().getZxid()); } /** * Test we can restore the snapshot that has data ahead of the zxid * of the snapshot file. */ @Test public void testRestore() throws Exception { // generate some transactions ZooKeeper zk = createZKClient(hostPort); String lastPath = null; try { zk.create("/invalidsnap", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < NUM_MESSAGES; i++) { lastPath = zk.create("/invalidsnap/test-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } } finally { zk.close(); } String[] tokens = lastPath.split("-"); String expectedPath = "/invalidsnap/test-" + String.format("%010d", (Integer.parseInt(tokens[1])) + 1); ZooKeeperServer zks = serverFactory.getZooKeeperServer(); long eZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid(); // force the zxid to be behind the content zks.getZKDatabase().setlastProcessedZxid(zks.getZKDatabase().getDataTreeLastProcessedZxid() - 10); LOG.info("Set lastProcessedZxid to {}", zks.getZKDatabase().getDataTreeLastProcessedZxid()); // Force snapshot and restore zks.takeSnapshot(); zks.shutdown(); stopServer(); startServer(); zks = serverFactory.getZooKeeperServer(); long fZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid(); // Verify lastProcessedZxid is set correctly assertTrue(fZxid == eZxid, "Restore failed expected zxid=" + eZxid + " found=" + fZxid); zk = createZKClient(hostPort); // Verify correctness of data and whether sequential znode creation // proceeds correctly after this point String[] children; String path; try { children = zk.getChildren("/invalidsnap", false).toArray(new String[0]); path = zk.create("/invalidsnap/test-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } finally { zk.close(); } LOG.info("Expected {} found {}", expectedPath, path); assertTrue(path.equals(expectedPath), "Error in sequential znode creation expected " + expectedPath + " found " + path); assertTrue((children.length == NUM_MESSAGES), "Unexpected number of children " + children.length + " expected " + NUM_MESSAGES); } /** * Test we can restore a snapshot that has errors and data ahead of the zxid * of the snapshot file. */ @Test public void testRestoreWithTransactionErrors() throws Exception { // generate some transactions ZooKeeper zk = createZKClient(hostPort); try { for (int i = 0; i < NUM_MESSAGES; i++) { try { zk.create("/invaliddir/test-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); } catch (NoNodeException e) { //Expected } } } finally { zk.close(); } // force the zxid to be behind the content ZooKeeperServer zks = serverFactory.getZooKeeperServer(); zks.getZKDatabase().setlastProcessedZxid(zks.getZKDatabase().getDataTreeLastProcessedZxid() - 10); LOG.info("Set lastProcessedZxid to {}", zks.getZKDatabase().getDataTreeLastProcessedZxid()); // Force snapshot and restore zks.takeSnapshot(); zks.shutdown(); stopServer(); zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); startServer(); } /** * Verify snap/log dir create with/without autocreate enabled. */ @Test public void testDatadirAutocreate() throws Exception { stopServer(); try { // now verify autocreate off works System.setProperty(FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE, "false"); tmpDir = createTmpDir(); startServer(); fail("Server should not have started without datadir"); } catch (IOException e) { LOG.debug("Server failed to start - correct behavior", e); } finally { System.setProperty(FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE, FileTxnSnapLog.ZOOKEEPER_DATADIR_AUTOCREATE_DEFAULT); } } /** * ZOOKEEPER-1573: test restoring a snapshot with deleted txns ahead of the * snapshot file's zxid. */ @Test public void testReloadSnapshotWithMissingParent() throws Exception { // create transactions to create the snapshot with create/delete pattern ZooKeeper zk = createZKClient(hostPort); zk.create("/a", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = zk.exists("/a", false); long createZxId = stat.getMzxid(); zk.create("/a/b", "".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.delete("/a/b", -1); zk.delete("/a", -1); // force the zxid to be behind the content ZooKeeperServer zks = serverFactory.getZooKeeperServer(); zks.getZKDatabase().setlastProcessedZxid(createZxId); LOG.info("Set lastProcessedZxid to {}", zks.getZKDatabase().getDataTreeLastProcessedZxid()); // Force snapshot and restore zks.takeSnapshot(); zks.shutdown(); stopServer(); startServer(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Loca0100644 0000000 0000000 00000000166 15051152474 032657 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LocalSessionRequestTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LocalSessionRequestT0100644 0000000 0000000 00000012711 15051152474 034324 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Validate that open/close session request of a local session to not propagate * to other machines in the quorum. We verify this by checking that * these request doesn't show up in committedLog on other machines. */ public class LocalSessionRequestTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(LocalSessionRequestTest.class); // Need to be short since we need to wait for session to expire public static final int CONNECTION_TIMEOUT = 4000; private final QuorumBase qb = new QuorumBase(); @BeforeEach public void setUp() throws Exception { LOG.info("STARTING quorum {}", getClass().getName()); qb.localSessionsEnabled = true; qb.localSessionsUpgradingEnabled = true; qb.setUp(); ClientBase.waitForServerUp(qb.hostPort, 10000); } @AfterEach public void tearDown() throws Exception { LOG.info("STOPPING quorum {}", getClass().getName()); qb.tearDown(); } @Test public void testLocalSessionsOnFollower() throws Exception { testOpenCloseSession(false); } @Test public void testLocalSessionsOnLeader() throws Exception { testOpenCloseSession(true); } /** * Walk through the target peer commmittedLog. * @param sessionId * @param peerId */ private void validateRequestLog(long sessionId, int peerId) { String session = Long.toHexString(sessionId); LOG.info("Searching for txn of session 0x " + session + " on peer " + peerId); String peerType = peerId == qb.getLeaderIndex() ? "leader" : "follower"; QuorumPeer peer = qb.getPeerList().get(peerId); ZKDatabase db = peer.getActiveServer().getZKDatabase(); for (Proposal p : db.getCommittedLog()) { assertFalse(p.getRequest().sessionId == sessionId, "Should not see " + Request.op2String(p.getRequest().type) + " request from local session 0x" + session + " on the " + peerType); } } /** * Test that a CloseSession request generated by both the server (client * disconnect) or by the client (client explicitly issue close()) doesn't * get committed by the ensemble */ public void testOpenCloseSession(boolean onLeader) throws Exception { int leaderIdx = qb.getLeaderIndex(); assertFalse(leaderIdx == -1, "No leader in quorum?"); int followerIdx = (leaderIdx + 1) % 5; int testPeerIdx = onLeader ? leaderIdx : followerIdx; int verifyPeerIdx = onLeader ? followerIdx : leaderIdx; String[] hostPorts = qb.hostPort.split(","); CountdownWatcher watcher = new CountdownWatcher(); DisconnectableZooKeeper client = new DisconnectableZooKeeper(hostPorts[testPeerIdx], CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); long localSessionId1 = client.getSessionId(); // Cut the connection, so the server will create closeSession as part // of expiring the session. client.dontReconnect(); client.disconnect(); watcher.reset(); // We don't validate right away, will do another session create first ZooKeeper zk = qb.createClient(watcher, hostPorts[testPeerIdx], CONNECTION_TIMEOUT); watcher.waitForConnected(CONNECTION_TIMEOUT); long localSessionId2 = zk.getSessionId(); // Send closeSession request. zk.close(); watcher.reset(); // This should be enough time for the first session to expire and for // the closeSession request to propagate to other machines (if there is a bug) // Since it is time sensitive, we have false negative when test // machine is under load Thread.sleep(CONNECTION_TIMEOUT * 2); // Validate that we don't see any txn from the first session validateRequestLog(localSessionId1, verifyPeerIdx); // Validate that we don't see any txn from the second session validateRequestLog(localSessionId2, verifyPeerIdx); qb.shutdownServers(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Loca0100644 0000000 0000000 00000000164 15051152474 032655 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LocalSessionsOnlyTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LocalSessionsOnlyTes0100644 0000000 0000000 00000012160 15051152474 034326 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Tests learners configured to use local sessions only. Expected * behavior is that sessions created on the learner will never be * made global. Operations requiring a global session (e.g. * creation of ephemeral nodes) will fail with an error. */ public class LocalSessionsOnlyTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(LocalSessionsOnlyTest.class); public static final int CONNECTION_TIMEOUT = ClientBase.CONNECTION_TIMEOUT; private final QuorumBase qb = new QuorumBase(); @BeforeEach public void setUp() throws Exception { LOG.info("STARTING quorum {}", getClass().getName()); qb.localSessionsEnabled = true; qb.localSessionsUpgradingEnabled = false; qb.setUp(); ClientBase.waitForServerUp(qb.hostPort, 10000); } @AfterEach public void tearDown() throws Exception { LOG.info("STOPPING quorum {}", getClass().getName()); qb.tearDown(); } @Test public void testLocalSessionsOnFollower() throws Exception { testLocalSessions(false); } @Test public void testLocalSessionsOnLeader() throws Exception { testLocalSessions(true); } private void testLocalSessions(boolean testLeader) throws Exception { String nodePrefix = "/testLocalSessions-" + (testLeader ? "leaderTest-" : "followerTest-"); int leaderIdx = qb.getLeaderIndex(); assertFalse(leaderIdx == -1, "No leader in quorum?"); int followerIdx = (leaderIdx + 1) % 5; int testPeerIdx = testLeader ? leaderIdx : followerIdx; String[] hostPorts = qb.hostPort.split(","); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = qb.createClient(watcher, hostPorts[testPeerIdx], CONNECTION_TIMEOUT); watcher.waitForConnected(CONNECTION_TIMEOUT); long localSessionId = zk.getSessionId(); // Try creating some data. for (int i = 0; i < 5; i++) { zk.create(nodePrefix + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // Now, try an ephemeral node. This should fail since we // cannot create ephemeral nodes on a local session. try { zk.create(nodePrefix + "ephemeral", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); fail("Ephemeral node creation should fail."); } catch (KeeperException.EphemeralOnLocalSessionException e) { } // Close the session. zk.close(); // Validate data on both follower and leader Map peers = new HashMap<>(); peers.put("leader", leaderIdx); peers.put("follower", followerIdx); for (Entry entry : peers.entrySet()) { watcher.reset(); // Try reconnecting with a new session. // The data should be persisted, even though the session was not. zk = qb.createClient(watcher, hostPorts[entry.getValue()], CONNECTION_TIMEOUT); watcher.waitForConnected(CONNECTION_TIMEOUT); long newSessionId = zk.getSessionId(); assertFalse(newSessionId == localSessionId); for (int i = 0; i < 5; i++) { assertNotNull(zk.exists(nodePrefix + i, null), "Data not exists in " + entry.getKey()); } // We may get the correct exception but the txn may go through assertNull(zk.exists(nodePrefix + "ephemeral", null), "Data exists in " + entry.getKey()); zk.close(); } qb.shutdownServers(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LogChopperTest.java0100644 0000000 0000000 00000011062 15051152474 034051 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.TxnLog; import org.apache.zookeeper.server.util.LogChopper; import org.apache.zookeeper.txn.DeleteTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.Test; class Pair { private V1 v1; private V2 v2; Pair(V1 v1, V2 v2) { this.v1 = v1; this.v2 = v2; } public V1 getFirst() { return v1; } public V2 getSecond() { return v2; } } public class LogChopperTest extends ClientBase { void rmr(File dir) throws IOException { Files.walkFileTree(dir.toPath(), new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes a) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); } Pair getFirstLastZxid(File logFile) throws IOException { File tmp = createTmpDir(); Files.copy(logFile.toPath(), new File(tmp, "log.0").toPath()); FileTxnLog txnLog = new FileTxnLog(tmp); TxnLog.TxnIterator it = txnLog.read(0); long firstZxid = it.getHeader().getZxid(); long lastZxid = firstZxid; while (it.next()) { lastZxid = it.getHeader().getZxid(); } txnLog.close(); rmr(tmp); return new Pair<>(firstZxid, lastZxid); } @Test public void testChopper() throws IOException { long clientId = 17; int cxid = 77; long zxid = 1000; long time = 1; int type = ZooDefs.OpCode.delete; DeleteTxn txn = new DeleteTxn("/foo"); File tmpDir = createTmpDir(); FileTxnLog txnLog = new FileTxnLog(tmpDir); for (int i = 0; i < 100; i++) { TxnHeader hdr = new TxnHeader(clientId, cxid, ++zxid, ++time, type); txnLog.append(new Request(0, 0, 0, hdr, txn, 0)); } // append a txn with gap TxnHeader hdr = new TxnHeader(clientId, cxid, zxid + 10, ++time, type); txnLog.append(new Request(0, 0, 0, hdr, txn, 0)); txnLog.commit(); // now find the log we just created. final File logFile = new File(tmpDir, "log." + Integer.toHexString(1001)); Pair firstLast = getFirstLastZxid(logFile); assertEquals(1001, (long) firstLast.getFirst()); assertEquals(1110, (long) firstLast.getSecond()); File choppedFile = new File(tmpDir, "chopped_failed"); assertFalse(LogChopper.chop(new FileInputStream(logFile), new FileOutputStream(choppedFile), 1107)); choppedFile = new File(tmpDir, "chopped"); assertTrue(LogChopper.chop(new FileInputStream(logFile), new FileOutputStream(choppedFile), 1017)); firstLast = getFirstLastZxid(choppedFile); assertEquals(1001, (long) firstLast.getFirst()); assertEquals(1017, (long) firstLast.getSecond()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/LoggerTestTool.java0100644 0000000 0000000 00000006072 15051152474 034071 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.Layout; import ch.qos.logback.core.OutputStreamAppender; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; import java.io.ByteArrayOutputStream; import org.slf4j.LoggerFactory; public class LoggerTestTool implements AutoCloseable { private final ByteArrayOutputStream os; private Appender appender; private Logger qlogger; public LoggerTestTool(Class cls) { os = createLoggingStream(cls); } public LoggerTestTool(String cls) { os = createLoggingStream(cls); } public ByteArrayOutputStream getOutputStream() { return os; } private ByteArrayOutputStream createLoggingStream(Class cls) { ByteArrayOutputStream os = new ByteArrayOutputStream(); appender = getConsoleAppender(os); qlogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(cls); qlogger.addAppender(appender); qlogger.setLevel(Level.INFO); appender.start(); return os; } private ByteArrayOutputStream createLoggingStream(String cls) { ByteArrayOutputStream os = new ByteArrayOutputStream(); appender = getConsoleAppender(os); qlogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(cls); qlogger.addAppender(appender); qlogger.setLevel(Level.INFO); appender.start(); return os; } private OutputStreamAppender getConsoleAppender(ByteArrayOutputStream os) { Logger rootLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); Layout layout = ((LayoutWrappingEncoder) ((OutputStreamAppender) rootLogger.getAppender("CONSOLE")).getEncoder()).getLayout(); OutputStreamAppender appender = new OutputStreamAppender<>(); appender.setContext((LoggerContext) LoggerFactory.getILoggerFactory()); appender.setOutputStream(os); appender.setLayout(layout); return appender; } @Override public void close() throws Exception { qlogger.detachAppender(appender); os.close(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/MaxCnxnsTest.java0100644 0000000 0000000 00000011302 15051152474 033543 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertSame; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.atomic.AtomicInteger; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.proto.ConnectRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class MaxCnxnsTest extends ClientBase { private static final int numCnxns = 30; AtomicInteger numConnected = new AtomicInteger(0); String host; int port; @BeforeEach @Override public void setUp() throws Exception { maxCnxns = numCnxns; super.setUp(); } class CnxnThread extends Thread { public CnxnThread(int i) { super("CnxnThread-" + i); } public void run() { try (SocketChannel sChannel = SocketChannel.open()) { /* * For future unwary socket programmers: although connect 'blocks' it * does not require an accept on the server side to return. Therefore * you can not assume that all the sockets are connected at the end of * this for loop. */ sChannel.connect(new InetSocketAddress(host, port)); // Construct a connection request ConnectRequest conReq = new ConnectRequest(0, 0, 10000, 0, "password".getBytes(), false); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeInt(-1, "len"); conReq.serialize(boa, "connect"); baos.close(); ByteBuffer bb = ByteBuffer.wrap(baos.toByteArray()); bb.putInt(bb.capacity() - 4); bb.rewind(); /* Send a connect request. Any socket that has been closed (or at least * not added to the cnxn list on the server) will not have any bytes to * read and get an eof. * * The trick here was finding a call that caused the server to put * bytes in the input stream without closing the cnxn. None of * the four letter commands do that, so we actually try to create * a session which should send us something back, while maintaining * the connection. */ int eof = sChannel.write(bb); // If the socket times out, we count that as failed - // the server should respond within 10s sChannel.socket().setSoTimeout(10000); if (!sChannel.socket().isClosed()) { eof = sChannel.socket().getInputStream().read(); if (eof != -1) { numConnected.incrementAndGet(); } } } catch (IOException io) { // "Connection reset by peer" } } } /** * Verify the ability to limit the number of concurrent connections. * @throws IOException * @throws InterruptedException */ @Test public void testMaxCnxns() throws IOException, InterruptedException { String[] split = hostPort.split(":"); host = split[0]; port = Integer.parseInt(split[1]); int numThreads = numCnxns + 5; CnxnThread[] threads = new CnxnThread[numThreads]; for (int i = 0; i < numCnxns; ++i) { threads[i] = new CnxnThread(i); } for (int i = 0; i < numCnxns; ++i) { threads[i].start(); } for (int i = 0; i < numCnxns; ++i) { threads[i].join(); } assertSame(numCnxns, numConnected.get()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Mult0100644 0000000 0000000 00000000170 15051152474 032715 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/MultiAsyncTransactionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/MultiAsyncTransactio0100644 0000000 0000000 00000011714 15051152474 034353 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.OpResult.CreateResult; import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class MultiAsyncTransactionTest extends ClientBase { private ZooKeeper zk; private final AtomicInteger pendingOps = new AtomicInteger(0); @BeforeEach public void setUp() throws Exception { super.setUp(); zk = createClient(); pendingOps.set(0); } private static class MultiResult { int rc; List results; } private void finishPendingOps() { if (pendingOps.decrementAndGet() == 0) { synchronized (pendingOps) { pendingOps.notifyAll(); } } } private void waitForPendingOps(int timeout) throws Exception { synchronized (pendingOps) { while (pendingOps.get() > 0) { pendingOps.wait(timeout); } } } /** * ZOOKEEPER-1624: PendingChanges of create sequential node request didn't * get rollbacked correctly when multi-op failed. This cause * create sequential node request in subsequent multi-op to failed because * sequential node name generation is incorrect. * * The check is to make sure that each request in multi-op failed with * the correct reason. */ @Test public void testSequentialNodeCreateInAsyncMulti() throws Exception { final int iteration = 4; final List results = new ArrayList<>(); pendingOps.set(iteration); List ops = Arrays.asList(Op.create("/node-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL), Op.create("/dup", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); for (int i = 0; i < iteration; ++i) { zk.multi(ops, new MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) { MultiResult result = new MultiResult(); result.results = opResults; result.rc = rc; results.add(result); finishPendingOps(); } }, null); } waitForPendingOps(CONNECTION_TIMEOUT); // Check that return code of all request are correct assertEquals(KeeperException.Code.OK.intValue(), results.get(0).rc); assertEquals(KeeperException.Code.NODEEXISTS.intValue(), results.get(1).rc); assertEquals(KeeperException.Code.NODEEXISTS.intValue(), results.get(2).rc); assertEquals(KeeperException.Code.NODEEXISTS.intValue(), results.get(3).rc); // Check that the first operation is successful in all request assertTrue(results.get(0).results.get(0) instanceof CreateResult); assertEquals(KeeperException.Code.OK.intValue(), ((ErrorResult) results.get(1).results.get(0)).getErr()); assertEquals(KeeperException.Code.OK.intValue(), ((ErrorResult) results.get(2).results.get(0)).getErr()); assertEquals(KeeperException.Code.OK.intValue(), ((ErrorResult) results.get(3).results.get(0)).getErr()); // Check that the second operation failed after the first request assertEquals(KeeperException.Code.NODEEXISTS.intValue(), ((ErrorResult) results.get(1).results.get(1)).getErr()); assertEquals(KeeperException.Code.NODEEXISTS.intValue(), ((ErrorResult) results.get(2).results.get(1)).getErr()); assertEquals(KeeperException.Code.NODEEXISTS.intValue(), ((ErrorResult) results.get(3).results.get(1)).getErr()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Mult0100644 0000000 0000000 00000000161 15051152474 032715 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/MultiOperationTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/MultiOperationTest.j0100644 0000000 0000000 00000130200 15051152474 034266 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.AsyncCallback.MultiCallback; import org.apache.zookeeper.ClientCnxn; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.CreateOptions; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.OpResult; import org.apache.zookeeper.OpResult.CheckResult; import org.apache.zookeeper.OpResult.CreateResult; import org.apache.zookeeper.OpResult.DeleteResult; import org.apache.zookeeper.OpResult.ErrorResult; import org.apache.zookeeper.OpResult.SetDataResult; import org.apache.zookeeper.Transaction; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.SyncRequestProcessor; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MultiOperationTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(MultiOperationTest.class); private ZooKeeper zk; private ZooKeeper zk_chroot; @BeforeEach public void setUp() throws Exception { SyncRequestProcessor.setSnapCount(150); super.setUp(); zk = createClient(); } static class MultiResult { int rc; List results; boolean finished = false; } private List multi(ZooKeeper zk, Iterable ops, boolean useAsync) throws KeeperException, InterruptedException { if (useAsync) { final MultiResult res = new MultiResult(); zk.multi(ops, new MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) { if (!ClientCnxn.isInEventThread()) { throw new RuntimeException("not in event thread"); } synchronized (res) { res.rc = rc; res.results = opResults; res.finished = true; res.notifyAll(); } } }, null); synchronized (res) { while (!res.finished) { res.wait(); } } // In case of only OpKind.READ operations, no exception is thrown. Errors only marked in form of ErrorResults. if (KeeperException.Code.OK.intValue() != res.rc && ops.iterator().next().getKind() != Op.OpKind.READ) { KeeperException ke = KeeperException.create(KeeperException.Code.get(res.rc)); throw ke; } return res.results; } else { return zk.multi(ops); } } private void multiHavingErrors(ZooKeeper zk, Iterable ops, List expectedResultCodes, String expectedErr, boolean useAsync) throws KeeperException, InterruptedException { if (useAsync) { final MultiResult res = new MultiResult(); zk.multi(ops, new MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) { synchronized (res) { res.rc = rc; res.results = opResults; res.finished = true; res.notifyAll(); } } }, null); synchronized (res) { while (!res.finished) { res.wait(); } } for (int i = 0; i < res.results.size(); i++) { OpResult opResult = res.results.get(i); assertTrue(opResult instanceof ErrorResult, "Did't receive proper error response"); ErrorResult errRes = (ErrorResult) opResult; assertEquals(expectedResultCodes.get(i).intValue(), errRes.getErr(), "Did't receive proper error code"); } } else { try { zk.multi(ops); fail("Shouldn't have validated in ZooKeeper client!"); } catch (KeeperException e) { assertEquals(expectedErr, e.code().name(), "Wrong exception"); } catch (IllegalArgumentException e) { assertEquals(expectedErr, e.getMessage(), "Wrong exception"); } } } private List commit(Transaction txn, boolean useAsync) throws KeeperException, InterruptedException { if (useAsync) { final MultiResult res = new MultiResult(); txn.commit(new MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) { synchronized (res) { res.rc = rc; res.results = opResults; res.finished = true; res.notifyAll(); } } }, null); synchronized (res) { while (!res.finished) { res.wait(); } } if (KeeperException.Code.OK.intValue() != res.rc) { KeeperException ke = KeeperException.create(KeeperException.Code.get(res.rc)); throw ke; } return res.results; } else { return txn.commit(); } } /** * Test verifies the multi calls with invalid znode path */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testInvalidPath(boolean useAsync) throws Exception { List expectedResultCodes = new ArrayList<>(); expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); // create with CreateMode List opList = Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi1/", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); String expectedErr = "Path must not end with / character"; multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); // create with valid sequential flag opList = Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("multi1/", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL.toFlag()), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); expectedErr = "Path must start with / character"; multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); // check opList = Arrays.asList( Op.check("/multi0", -1), Op.check("/multi1/", 100), Op.check("/multi2", 5)); expectedErr = "Path must not end with / character"; multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); // delete opList = Arrays.asList( Op.delete("/multi0", -1), Op.delete("/multi1/", 100), Op.delete("/multi2", 5)); multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); // Multiple bad arguments expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); // setdata opList = Arrays.asList( Op.setData("/multi0", new byte[0], -1), Op.setData("/multi1/", new byte[0], -1), Op.setData("/multi2", new byte[0], -1), Op.setData("multi3", new byte[0], -1)); multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); } /** * ZOOKEEPER-2052: * Multi abort shouldn't have any side effect. * We fix a bug in rollback and the following scenario should work: * 1. multi delete abort because of not empty directory * 2. ephemeral nodes under that directory are deleted * 3. multi delete should succeed. */ @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiRollback(boolean useAsync) throws Exception { zk.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); ZooKeeper epheZk = createClient(); epheZk.create("/foo/bar", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); List opList = Arrays.asList(Op.delete("/foo", -1)); try { zk.multi(opList); fail("multi delete should failed for not empty directory"); } catch (KeeperException.NotEmptyException e) { } final CountDownLatch latch = new CountDownLatch(1); zk.exists("/foo/bar", event -> { if (event.getType() == Watcher.Event.EventType.NodeDeleted) { latch.countDown(); } }); epheZk.close(); latch.await(); try { zk.getData("/foo/bar", false, null); fail("ephemeral node should have been deleted"); } catch (KeeperException.NoNodeException e) { } zk.multi(opList); try { zk.getData("/foo", false, null); fail("persistent node should have been deleted after multi"); } catch (KeeperException.NoNodeException e) { } } /** * Test verifies the multi calls with blank znode path */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testBlankPath(boolean useAsync) throws Exception { List expectedResultCodes = new ArrayList<>(); expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); // delete String expectedErr = "Path cannot be null"; List opList = Arrays.asList( Op.delete("/multi0", -1), Op.delete(null, 100), Op.delete("/multi2", 5), Op.delete("", -1)); multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); } /** * Test verifies the multi.create with invalid createModeFlag */ @ParameterizedTest @ValueSource(booleans = {true, false}) @Timeout(value = 90) public void testInvalidCreateModeFlag(boolean useAsync) throws Exception { List expectedResultCodes = new ArrayList<>(); expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); expectedResultCodes.add(KeeperException.Code.BADARGUMENTS.intValue()); expectedResultCodes.add(KeeperException.Code.RUNTIMEINCONSISTENCY.intValue()); int createModeFlag = 6789; List opList = Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, createModeFlag), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); String expectedErr = KeeperException.Code.BADARGUMENTS.name(); multiHavingErrors(zk, opList, expectedResultCodes, expectedErr, useAsync); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testChRootCreateDelete(boolean useAsync) throws Exception { // creating the subtree for chRoot clients. String chRoot = createNameSpace(useAsync); // Creating child using chRoot client. zk_chroot = createClient(this.hostPort + chRoot); Op createChild = Op.create("/myid", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); multi(zk_chroot, Arrays.asList(createChild), useAsync); assertNotNull(zk.exists(chRoot + "/myid", false), "zNode is not created under chroot:" + chRoot); assertNotNull(zk_chroot.exists("/myid", false), "zNode is not created under chroot:" + chRoot); assertNull(zk.exists("/myid", false), "zNode is created directly under '/', ignored configured chroot"); // Deleting child using chRoot client. Op deleteChild = Op.delete("/myid", 0); multi(zk_chroot, Arrays.asList(deleteChild), useAsync); assertNull(zk.exists(chRoot + "/myid", false), "zNode exists under chroot:" + chRoot); assertNull(zk_chroot.exists("/myid", false), "zNode exists under chroot:" + chRoot); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testChRootSetData(boolean useAsync) throws Exception { // creating the subtree for chRoot clients. String chRoot = createNameSpace(useAsync); // setData using chRoot client. zk_chroot = createClient(this.hostPort + chRoot); String[] names = {"/multi0", "/multi1", "/multi2"}; List ops = new ArrayList<>(); for (int i = 0; i < names.length; i++) { ops.add(Op.create(names[i], new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); ops.add(Op.setData(names[i], names[i].getBytes(), 0)); } multi(zk_chroot, ops, useAsync); for (int i = 0; i < names.length; i++) { assertArrayEquals(names[i].getBytes(), zk_chroot.getData(names[i], false, null), "zNode data not matching"); } } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testChRootCheck(boolean useAsync) throws Exception { // creating the subtree for chRoot clients. String chRoot = createNameSpace(useAsync); // checking the child version using chRoot client. zk_chroot = createClient(this.hostPort + chRoot); String[] names = {"/multi0", "/multi1", "/multi2"}; List ops = new ArrayList<>(); for (int i = 0; i < names.length; i++) { zk.create(chRoot + names[i], new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } for (int i = 0; i < names.length; i++) { ops.add(Op.check(names[i], 0)); } multi(zk_chroot, ops, useAsync); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testChRootTransaction(boolean useAsync) throws Exception { // creating the subtree for chRoot clients. String chRoot = createNameSpace(useAsync); // checking the child version using chRoot client. zk_chroot = createClient(this.hostPort + chRoot); String childPath = "/myid"; Transaction transaction = zk_chroot.transaction(); transaction.create(childPath, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); transaction.check(childPath, 0); transaction.setData(childPath, childPath.getBytes(), 0); commit(transaction, useAsync); assertNotNull(zk.exists(chRoot + childPath, false), "zNode is not created under chroot:" + chRoot); assertNotNull(zk_chroot.exists(childPath, false), "zNode is not created under chroot:" + chRoot); assertNull(zk.exists(childPath, false), "zNode is created directly under '/', ignored configured chroot"); assertArrayEquals(childPath.getBytes(), zk_chroot.getData(childPath, false, null), "zNode data not matching"); transaction = zk_chroot.transaction(); // Deleting child using chRoot client. transaction.delete(childPath, 1); commit(transaction, useAsync); assertNull(zk.exists(chRoot + "/myid", false), "chroot:" + chRoot + " exists after delete"); assertNull(zk_chroot.exists("/myid", false), "chroot:" + chRoot + " exists after delete"); } private String createNameSpace(boolean useAsync) throws InterruptedException, KeeperException { // creating the subtree for chRoot clients. String chRoot = "/appsX"; Op createChRoot = Op.create(chRoot, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); multi(zk, Arrays.asList(createChRoot), useAsync); return chRoot; } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testCreate(boolean useAsync) throws Exception { multi(zk, Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)), useAsync); zk.getData("/multi0", false, null); zk.getData("/multi1", false, null); zk.getData("/multi2", false, null); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testCreate2(boolean useAsync) throws Exception { CreateOptions options = CreateOptions.newBuilder(Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT).build(); List ops = Arrays.asList( Op.create("/multi0", new byte[0], options), Op.create("/multi1", new byte[0], options), Op.create("/multi2", new byte[0], options)); List results = multi(zk, ops, useAsync); for (int i = 0; i < ops.size(); i++) { CreateResult createResult = (CreateResult) results.get(i); assertEquals(ops.get(i).getPath(), createResult.getPath()); assertEquals(ZooDefs.OpCode.create2, createResult.getType(), createResult.getPath()); assertNotNull(createResult.getStat(), createResult.getPath()); assertNotEquals(0, createResult.getStat().getCzxid(), createResult.getPath()); } zk.getData("/multi0", false, null); zk.getData("/multi1", false, null); zk.getData("/multi2", false, null); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testEmpty(boolean useAsync) throws Exception { multi(zk, Arrays.asList(), useAsync); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testCreateDelete(boolean useAsync) throws Exception { multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 0)), useAsync); // '/multi' should have been deleted assertNull(zk.exists("/multi", null)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testInvalidVersion(boolean useAsync) throws Exception { try { multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 1)), useAsync); fail("delete /multi should have failed"); } catch (KeeperException e) { /* PASS */ } } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testNestedCreate(boolean useAsync) throws Exception { multi(zk, Arrays.asList( /* Create */ Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi/a", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi/a/1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), /* Delete */ Op.delete("/multi/a/1", 0), Op.delete("/multi/a", 0), Op.delete("/multi", 0)), useAsync); //Verify tree deleted assertNull(zk.exists("/multi/a/1", null)); assertNull(zk.exists("/multi/a", null)); assertNull(zk.exists("/multi", null)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSetData(boolean useAsync) throws Exception { String[] names = {"/multi0", "/multi1", "/multi2"}; List ops = new ArrayList<>(); for (int i = 0; i < names.length; i++) { ops.add(Op.create(names[i], new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); ops.add(Op.setData(names[i], names[i].getBytes(), 0)); } multi(zk, ops, useAsync); for (int i = 0; i < names.length; i++) { assertArrayEquals(names[i].getBytes(), zk.getData(names[i], false, null)); } } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testUpdateConflict(boolean useAsync) throws Exception { assertNull(zk.exists("/multi", null)); try { multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData("/multi", "X".getBytes(), 0), Op.setData("/multi", "Y".getBytes(), 0)), useAsync); fail("Should have thrown a KeeperException for invalid version"); } catch (KeeperException e) { //PASS LOG.error("STACKTRACE: ", e); } assertNull(zk.exists("/multi", null)); //Updating version solves conflict -- order matters multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData("/multi", "X".getBytes(), 0), Op.setData("/multi", "Y".getBytes(), 1)), useAsync); assertArrayEquals(zk.getData("/multi", false, null), "Y".getBytes()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testDeleteUpdateConflict(boolean useAsync) throws Exception { /* Delete of a node folowed by an update of the (now) deleted node */ try { multi(zk, Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 0), Op.setData("/multi", "Y".getBytes(), 0)), useAsync); fail("/multi should have been deleted so setData should have failed"); } catch (KeeperException e) { /* PASS */ } // '/multi' should never have been created as entire op should fail assertNull(zk.exists("/multi", null)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testGetResults(boolean useAsync) throws Exception { /* Delete of a node folowed by an update of the (now) deleted node */ Iterable ops = Arrays.asList( Op.create("/multi", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/multi", 0), Op.setData("/multi", "Y".getBytes(), 0), Op.create("/foo", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT)); List results = null; if (useAsync) { final MultiResult res = new MultiResult(); zk.multi(ops, new MultiCallback() { @Override public void processResult(int rc, String path, Object ctx, List opResults) { synchronized (res) { res.rc = rc; res.results = opResults; res.finished = true; res.notifyAll(); } } }, null); synchronized (res) { while (!res.finished) { res.wait(); } } assertFalse(KeeperException.Code.OK.intValue() == res.rc, "/multi should have been deleted so setData should have failed"); assertNull(zk.exists("/multi", null)); results = res.results; } else { try { zk.multi(ops); fail("/multi should have been deleted so setData should have failed"); } catch (KeeperException e) { // '/multi' should never have been created as entire op should fail assertNull(zk.exists("/multi", null)); results = e.getResults(); } } assertNotNull(results); for (OpResult r : results) { LOG.info("RESULT==> {}", r); if (r instanceof ErrorResult) { ErrorResult er = (ErrorResult) r; LOG.info("ERROR RESULT: {} ERR=>{}", er, KeeperException.Code.get(er.getErr())); } } } /** * Exercise the equals methods of OpResult classes. */ @ParameterizedTest @ValueSource(booleans = {true, false}) public void testOpResultEquals(boolean useAsync) { opEquals(new CreateResult("/foo"), new CreateResult("/foo"), new CreateResult("nope")); opEquals(new CreateResult("/foo"), new CreateResult("/foo"), new CreateResult("/foo", new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11))); opEquals(new CreateResult("/foo", new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)), new CreateResult("/foo", new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)), new CreateResult("nope", new Stat(11, 12, 13, 14, 15, 16, 17, 18, 19, 110, 111))); opEquals(new CreateResult("/foo", new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)), new CreateResult("/foo", new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)), new CreateResult("/foo")); opEquals(new CheckResult(), new CheckResult(), null); opEquals(new SetDataResult(new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)), new SetDataResult(new Stat(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)), new SetDataResult(new Stat(11, 12, 13, 14, 15, 16, 17, 18, 19, 110, 111))); opEquals(new ErrorResult(1), new ErrorResult(1), new ErrorResult(2)); opEquals(new DeleteResult(), new DeleteResult(), null); opEquals(new ErrorResult(1), new ErrorResult(1), new ErrorResult(2)); } private void opEquals(OpResult expected, OpResult value, OpResult near) { assertEquals(value, value); assertFalse(value.equals(new Object())); assertFalse(value.equals(near)); assertFalse(value.equals(value instanceof CreateResult ? new ErrorResult(1) : new CreateResult("nope2"))); assertTrue(value.equals(expected)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testWatchesTriggered(boolean useAsync) throws KeeperException, InterruptedException { HasTriggeredWatcher watcher = new HasTriggeredWatcher(); zk.getChildren("/", watcher); multi(zk, Arrays.asList( Op.create("/t", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/t", -1)), useAsync); assertTrue(watcher.triggered.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testNoWatchesTriggeredForFailedMultiRequest(boolean useAsync) throws InterruptedException, KeeperException { HasTriggeredWatcher watcher = new HasTriggeredWatcher(); zk.getChildren("/", watcher); try { multi(zk, Arrays.asList( Op.create("/t", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.delete("/nonexisting", -1)), useAsync); fail("expected previous multi op to fail!"); } catch (KeeperException.NoNodeException e) { // expected } SyncCallback cb = new SyncCallback(); zk.sync("/", cb, null); // by waiting for the callback we're assured that the event queue is flushed cb.done.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(1, watcher.triggered.getCount()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testTransactionBuilder(boolean useAsync) throws Exception { List results = commit( zk.transaction() .create("/t1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) .create("/t1/child", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) .create("/t2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL), useAsync); assertEquals(3, results.size()); for (OpResult r : results) { CreateResult c = (CreateResult) r; assertTrue(c.getPath().startsWith("/t")); assertNotNull(c.toString()); } assertNotNull(zk.exists("/t1", false)); assertNotNull(zk.exists("/t1/child", false)); assertNotNull(zk.exists("/t2", false)); results = commit(zk.transaction().check("/t1", 0).check("/t1/child", 0).check("/t2", 0), useAsync); assertEquals(3, results.size()); for (OpResult r : results) { CheckResult c = (CheckResult) r; assertNotNull(c.toString()); } try { results = commit(zk.transaction().check("/t1", 0).check("/t1/child", 0).check("/t2", 1), useAsync); fail(); } catch (KeeperException.BadVersionException e) { // expected } results = commit(zk.transaction().check("/t1", 0).setData("/t1", new byte[0], 0), useAsync); assertEquals(2, results.size()); for (OpResult r : results) { assertNotNull(r.toString()); } try { results = commit(zk.transaction().check("/t1", 1).setData("/t1", new byte[0], 2), useAsync); fail(); } catch (KeeperException.BadVersionException e) { // expected } results = commit(zk.transaction().check("/t1", 1).check("/t1/child", 0).check("/t2", 0), useAsync); assertEquals(3, results.size()); results = commit(zk.transaction().delete("/t2", -1).delete("/t1/child", -1), useAsync); assertEquals(2, results.size()); for (OpResult r : results) { DeleteResult d = (DeleteResult) r; assertNotNull(d.toString()); } assertNotNull(zk.exists("/t1", false)); assertNull(zk.exists("/t1/child", false)); assertNull(zk.exists("/t2", false)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiGetChildren(boolean useAsync) throws Exception { List topLevelNodes = new ArrayList<>(); Map> childrenNodes = new HashMap<>(); // Creating a database where '/fooX' nodes has 'barXY' named children. for (int i = 0; i < 10; i++) { String name = "/foo" + i; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); topLevelNodes.add(name); childrenNodes.put(name, new ArrayList<>()); for (int j = 0; j < 10; j++) { String childname = name + "/bar" + i + j; String childname_s = "bar" + i + j; zk.create(childname, childname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); childrenNodes.get(name).add(childname_s); } } // Create a multi operation, which queries the children of the nodes in topLevelNodes. List multiChildrenList = multi(zk, topLevelNodes.stream().map(Op::getChildren).collect(Collectors.toList()), useAsync); for (int i = 0; i < topLevelNodes.size(); i++) { String nodeName = topLevelNodes.get(i); assertTrue(multiChildrenList.get(i) instanceof OpResult.GetChildrenResult); List childrenList = ((OpResult.GetChildrenResult) multiChildrenList.get(i)).getChildren(); // In general, we do not demand an order from the children list but to contain every child. assertEquals(new TreeSet(childrenList), new TreeSet(childrenNodes.get(nodeName))); List children = zk.getChildren(nodeName, false); assertEquals(childrenList, children); } } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiGetChildrenSameNode(boolean useAsync) throws Exception { List childrenNodes = new ArrayList<>(); // Creating a database where '/foo' node has 'barX' named children. String topLevelNode = "/foo"; zk.create(topLevelNode, topLevelNode.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < 10; i++) { String childname = topLevelNode + "/bar" + i; String childname_s = "bar" + i; zk.create(childname, childname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); childrenNodes.add(childname_s); } // Check for getting the children of the same node twice. List sameChildrenList = multi(zk, Arrays.asList( Op.getChildren(topLevelNode), Op.getChildren(topLevelNode)), useAsync); // The response should contain two elements which are the same. assertEquals(sameChildrenList.size(), 2); assertEquals(sameChildrenList.get(0), sameChildrenList.get(1)); // Check the actual result. assertTrue(sameChildrenList.get(0) instanceof OpResult.GetChildrenResult); OpResult.GetChildrenResult gcr = (OpResult.GetChildrenResult) sameChildrenList.get(0); // In general, we do not demand an order from the children list but to contain every child. assertEquals(new TreeSet(gcr.getChildren()), new TreeSet(childrenNodes)); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiGetChildrenAuthentication(boolean useAsync) throws KeeperException, InterruptedException { List writeOnly = Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("world", "anyone"))); zk.create("/foo_auth", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/foo_auth/bar", null, Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/foo_no_auth", null, writeOnly, CreateMode.PERSISTENT); // Check for normal behaviour. List multiChildrenList = multi(zk, Arrays.asList(Op.getChildren("/foo_auth")), useAsync); assertEquals(multiChildrenList.size(), 1); assertTrue(multiChildrenList.get(0) instanceof OpResult.GetChildrenResult); List childrenList = ((OpResult.GetChildrenResult) multiChildrenList.get(0)).getChildren(); assertEquals(childrenList.size(), 1); assertEquals(childrenList.get(0), "bar"); // Check for authentication violation. multiChildrenList = multi(zk, Arrays.asList(Op.getChildren("/foo_no_auth")), useAsync); assertEquals(multiChildrenList.size(), 1); assertTrue(multiChildrenList.get(0) instanceof OpResult.ErrorResult); assertEquals(((OpResult.ErrorResult) multiChildrenList.get(0)).getErr(), KeeperException.Code.NOAUTH.intValue(), "Expected NoAuthException for getting the children of a write only node"); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiGetChildrenMixedAuthenticationErrorFirst(boolean useAsync) throws KeeperException, InterruptedException { List writeOnly = Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("world", "anyone"))); zk.create("/foo_auth", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/foo_auth/bar", null, Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/foo_no_auth", null, writeOnly, CreateMode.PERSISTENT); List multiChildrenList; // Mixed nodes, the operation after the error should return RuntimeInconsistency error. multiChildrenList = multi(zk, Arrays.asList(Op.getChildren("/foo_no_auth"), Op.getChildren("/foo_auth")), useAsync); assertEquals(multiChildrenList.size(), 2); assertTrue(multiChildrenList.get(0) instanceof OpResult.ErrorResult); assertEquals(((OpResult.ErrorResult) multiChildrenList.get(0)).getErr(), KeeperException.Code.NOAUTH.intValue(), "Expected NoAuthException for getting the children of a write only node"); assertTrue(multiChildrenList.get(1) instanceof OpResult.GetChildrenResult); List childrenList = ((OpResult.GetChildrenResult) multiChildrenList.get(1)).getChildren(); assertEquals(childrenList.size(), 1); assertEquals(childrenList.get(0), "bar"); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiGetChildrenMixedAuthenticationCorrectFirst(boolean useAsync) throws KeeperException, InterruptedException { List writeOnly = Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("world", "anyone"))); zk.create("/foo_auth", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/foo_auth/bar", null, Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/foo_no_auth", null, writeOnly, CreateMode.PERSISTENT); // Check for getting the children of the nodes with mixed authentication. // The getChildren operation returns GetChildrenResult if it happened before the error. List multiChildrenList; multiChildrenList = multi(zk, Arrays.asList(Op.getChildren("/foo_auth"), Op.getChildren("/foo_no_auth")), useAsync); assertSame(multiChildrenList.size(), 2); assertTrue(multiChildrenList.get(0) instanceof OpResult.GetChildrenResult); List childrenList = ((OpResult.GetChildrenResult) multiChildrenList.get(0)).getChildren(); assertEquals(childrenList.size(), 1); assertEquals(childrenList.get(0), "bar"); assertTrue(multiChildrenList.get(1) instanceof OpResult.ErrorResult); assertEquals(((OpResult.ErrorResult) multiChildrenList.get(1)).getErr(), KeeperException.Code.NOAUTH.intValue(), "Expected NoAuthException for getting the children of a write only node"); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiGetData(boolean useAsync) throws Exception { zk.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/node2", "data2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List multiData = multi(zk, Arrays.asList(Op.getData("/node1"), Op.getData("/node2")), useAsync); assertEquals(multiData.size(), 2); assertArrayEquals(((OpResult.GetDataResult) multiData.get(0)).getData(), "data1".getBytes()); assertArrayEquals(((OpResult.GetDataResult) multiData.get(1)).getData(), "data2".getBytes()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMultiRead(boolean useAsync) throws Exception { zk.create("/node1", "data1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/node2", "data2".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); zk.create("/node1/node1", "data11".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/node1/node2", "data12".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); List multiRead = multi(zk, Arrays.asList( Op.getChildren("/node1"), Op.getData("/node1"), Op.getChildren("/node2"), Op.getData("/node2")), useAsync); assertEquals(multiRead.size(), 4); assertTrue(multiRead.get(0) instanceof OpResult.GetChildrenResult); List childrenList = ((OpResult.GetChildrenResult) multiRead.get(0)).getChildren(); assertEquals(childrenList.size(), 2); assertEquals(new TreeSet(childrenList), new TreeSet(Arrays.asList("node1", "node2"))); assertArrayEquals(((OpResult.GetDataResult) multiRead.get(1)).getData(), "data1".getBytes()); Stat stat = ((OpResult.GetDataResult) multiRead.get(1)).getStat(); assertEquals(stat.getMzxid(), stat.getCzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(2, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(5, stat.getDataLength()); assertEquals(2, stat.getNumChildren()); assertTrue(multiRead.get(2) instanceof OpResult.GetChildrenResult); childrenList = ((OpResult.GetChildrenResult) multiRead.get(2)).getChildren(); assertTrue(childrenList.isEmpty()); assertArrayEquals(((OpResult.GetDataResult) multiRead.get(3)).getData(), "data2".getBytes()); stat = ((OpResult.GetDataResult) multiRead.get(3)).getStat(); assertEquals(stat.getMzxid(), stat.getCzxid()); assertEquals(stat.getMzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(zk.getSessionId(), stat.getEphemeralOwner()); assertEquals(5, stat.getDataLength()); assertEquals(0, stat.getNumChildren()); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testMixedReadAndTransaction(boolean useAsync) throws Exception { zk.create("/node", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { List multiRead = multi(zk, Arrays.asList( Op.setData("/node1", "data1".getBytes(), -1), Op.getData("/node1")), useAsync); fail("Mixed kind of operations are not allowed"); } catch (IllegalArgumentException e) { // expected } } private static class HasTriggeredWatcher implements Watcher { private final CountDownLatch triggered = new CountDownLatch(1); @Override public void process(WatchedEvent event) { triggered.countDown(); } } private static class SyncCallback implements AsyncCallback.VoidCallback { private final CountDownLatch done = new CountDownLatch(1); @Override public void processResult(int rc, String path, Object ctx) { done.countDown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_NIOC0100644 0000000 0000000 00000000175 15051152474 032531 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NIOConnectionFactoryFdLeakTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NIOConnectionFactory0100644 0000000 0000000 00000004621 15051152474 034227 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.InetSocketAddress; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * ZOOKEEPER-1620 - Acceptor and Selector thread don't call selector.close() * causing fd leakage */ public class NIOConnectionFactoryFdLeakTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(NIOConnectionFactoryFdLeakTest.class); @Test public void testFileDescriptorLeak() throws Exception { OSMXBean osMbean = new OSMXBean(); if (!osMbean.getUnix()) { LOG.info("Unable to run test on non-unix system"); return; } long startFdCount = osMbean.getOpenFileDescriptorCount(); LOG.info("Start fdcount is: {}", startFdCount); for (int i = 0; i < 50; ++i) { NIOServerCnxnFactory factory = new NIOServerCnxnFactory(); factory.configure(new InetSocketAddress("127.0.0.1", PortAssignment.unique()), 10); factory.start(); Thread.sleep(100); factory.shutdown(); } long endFdCount = osMbean.getOpenFileDescriptorCount(); LOG.info("End fdcount is: {}", endFdCount); // On my box, if selector.close() is not called fd diff is > 700. assertTrue(((endFdCount - startFdCount) < 50), "Possible fd leakage"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Nett0100644 0000000 0000000 00000000162 15051152474 032707 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NettyNettySuiteBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NettyNettySuiteBase.0100644 0000000 0000000 00000004407 15051152474 034246 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.ClientCnxnSocketNetty; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.server.NettyServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.runner.RunWith; import org.junit.runners.Suite; /** * Run tests with: Netty Client against Netty server */ @RunWith(Suite.class) public class NettyNettySuiteBase { @BeforeAll public static void setUp() { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, NettyServerCnxnFactory.class.getName()); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, ClientCnxnSocketNetty.class.getName()); System.setProperty("zookeeper.admin.enableServer", "false"); } @AfterAll public static void tearDown() { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); } @BeforeEach public void setUpTest() throws Exception { TestByteBufAllocatorTestHelper.setTestAllocator(TestByteBufAllocator.getInstance()); } @AfterEach public void tearDownTest() throws Exception { TestByteBufAllocatorTestHelper.clearTestAllocator(); TestByteBufAllocator.checkForLeaks(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Nett0100644 0000000 0000000 00000000170 15051152474 032706 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NettyNettySuiteHammerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NettyNettySuiteHamme0100644 0000000 0000000 00000002036 15051152474 034341 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.junit.runners.Suite; /** * Run tests with: Netty Client against Netty server */ @Suite.SuiteClasses({AsyncHammerTest.class}) public class NettyNettySuiteHammerTest extends NettyNettySuiteBase { } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Nett0100644 0000000 0000000 00000000162 15051152474 032707 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NettyNettySuiteTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NettyNettySuiteTest.0100644 0000000 0000000 00000002464 15051152474 034314 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.junit.platform.runner.JUnitPlatform; import org.junit.platform.suite.api.SelectClasses; import org.junit.runner.RunWith; /** * Run tests with: Netty Client against Netty server */ @RunWith(JUnitPlatform.class) @SelectClasses({ACLTest.class, AsyncOpsTest.class, ChrootClientTest.class, ClientTest.class, FourLetterWordsTest.class, NullDataTest.class, SessionTest.class, WatcherTest.class, ReconfigTest.class}) public class NettyNettySuiteTest extends NettyNettySuiteBase { } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_NioN0100644 0000000 0000000 00000000160 15051152474 032636 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NioNettySuiteBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NioNettySuiteBase.ja0100644 0000000 0000000 00000003742 15051152474 034204 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.server.NettyServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.runner.RunWith; import org.junit.runners.Suite; /** * Run tests with: Nio Client against Netty server */ @RunWith(Suite.class) public class NioNettySuiteBase { @BeforeAll public static void setUp() { System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, NettyServerCnxnFactory.class.getName()); System.setProperty("zookeeper.admin.enableServer", "false"); } @AfterAll public static void tearDown() { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); } @BeforeEach public void setUpTest() throws Exception { TestByteBufAllocatorTestHelper.setTestAllocator(TestByteBufAllocator.getInstance()); } @AfterEach public void tearDownTest() throws Exception { TestByteBufAllocatorTestHelper.clearTestAllocator(); TestByteBufAllocator.checkForLeaks(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_NioN0100644 0000000 0000000 00000000166 15051152474 032644 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NioNettySuiteHammerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NioNettySuiteHammerT0100644 0000000 0000000 00000002030 15051152474 034263 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.junit.runners.Suite; /** * Run tests with: Nio Client against Netty server */ @Suite.SuiteClasses({AsyncHammerTest.class}) public class NioNettySuiteHammerTest extends NioNettySuiteBase { } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_NioN0100644 0000000 0000000 00000000160 15051152474 032636 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NioNettySuiteTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NioNettySuiteTest.ja0100644 0000000 0000000 00000002261 15051152474 034244 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.junit.runners.Suite; /** * Run tests with: Nio Client against Netty server */ @Suite.SuiteClasses({ACLTest.class, AsyncOpsTest.class, ChrootClientTest.class, ClientTest.class, FourLetterWordsTest.class, NullDataTest.class, SessionTest.class, WatcherTest.class, ReconfigTest.class}) public class NioNettySuiteTest extends NioNettySuiteBase { } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_NonR0100644 0000000 0000000 00000000166 15051152474 032655 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NonRecoverableErrorTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NonRecoverableErrorT0100644 0000000 0000000 00000016507 15051152474 034302 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.UUID; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; /** * This class tests the non-recoverable error behavior of quorum server. */ public class NonRecoverableErrorTest extends QuorumPeerTestBase { private static final String NODE_PATH = "/noLeaderIssue"; /** * Test case for https://issues.apache.org/jira/browse/ZOOKEEPER-2247. * Test to verify that even after non recoverable error (error while * writing transaction log), ZooKeeper is still available. */ @Test @Timeout(value = 30) public void testZooKeeperServiceAvailableOnLeader() throws Exception { int SERVER_COUNT = 3; final int[] clientPorts = new int[SERVER_COUNT]; StringBuilder sb = new StringBuilder(); String server; for (int i = 0; i < SERVER_COUNT; i++) { clientPorts[i] = PortAssignment.unique(); server = "server." + i + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;127.0.0.1:" + clientPorts[i]; sb.append(server + "\n"); } String currentQuorumCfgSection = sb.toString(); MainThread[] mt = new MainThread[SERVER_COUNT]; for (int i = 0; i < SERVER_COUNT; i++) { mt[i] = new MainThread(i, clientPorts[i], currentQuorumCfgSection, false); mt[i].start(); } // ensure server started for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper("127.0.0.1:" + clientPorts[0], ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); String data = "originalData"; zk.create(NODE_PATH, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // get information of current leader QuorumPeer leader = getLeaderQuorumPeer(mt); assertNotNull(leader, "Leader must have been elected by now"); // inject problem in leader FileTxnSnapLog snapLog = leader.getActiveServer().getTxnLogFactory(); FileTxnSnapLog fileTxnSnapLogWithError = new FileTxnSnapLog(snapLog.getDataLogDir(), snapLog.getSnapDir()) { @Override public void commit() throws IOException { throw new IOException("Input/output error"); } }; ZKDatabase originalZKDatabase = leader.getActiveServer().getZKDatabase(); long leaderCurrentEpoch = leader.getCurrentEpoch(); ZKDatabase newDB = new ZKDatabase(fileTxnSnapLogWithError); leader.getActiveServer().setZKDatabase(newDB); try { // do create operation, so that injected IOException is thrown zk.create(uniqueZnode(), data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("IOException is expected due to error injected to transaction log commit"); } catch (Exception e) { // do nothing } // resetting watcher so that this watcher can be again used to ensure // that the zkClient is able to re-establish connection with the // newly elected zookeeper quorum. watcher.reset(); waitForNewLeaderElection(leader, leaderCurrentEpoch); // ensure server started, give enough time, so that new leader election // takes place for (int i = 0; i < SERVER_COUNT; i++) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + clientPorts[i], CONNECTION_TIMEOUT), "waiting for server " + i + " being up"); } // revert back the error leader.getActiveServer().setZKDatabase(originalZKDatabase); // verify that now ZooKeeper service is up and running leader = getLeaderQuorumPeer(mt); assertNotNull(leader, "New leader must have been elected by now"); String uniqueNode = uniqueZnode(); watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); String createNode = zk.create(uniqueNode, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // if node is created successfully then it means that ZooKeeper service // is available assertEquals(uniqueNode, createNode, "Failed to create znode"); zk.close(); // stop all severs for (int i = 0; i < SERVER_COUNT; i++) { mt[i].shutdown(); } } private void waitForNewLeaderElection(QuorumPeer peer, long leaderCurrentEpoch) throws IOException, InterruptedException { LOG.info("Waiting for new LE cycle.."); int count = 100; // giving a grace period of 10seconds while (count > 0) { if (leaderCurrentEpoch == peer.getCurrentEpoch()) { Thread.sleep(100); } count--; } assertNotEquals(leaderCurrentEpoch, peer.getCurrentEpoch(), "New LE cycle must have triggered"); } private QuorumPeer getLeaderQuorumPeer(MainThread[] mt) { for (int i = mt.length - 1; i >= 0; i--) { QuorumPeer quorumPeer = mt[i].getQuorumPeer(); if (null != quorumPeer && ServerState.LEADING == quorumPeer.getPeerState()) { return quorumPeer; } } return null; } private String uniqueZnode() { UUID randomUUID = UUID.randomUUID(); String node = NODE_PATH + "/" + randomUUID.toString(); return node; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/NullDataTest.java0100644 0000000 0000000 00000005204 15051152474 033514 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertSame; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class NullDataTest extends ClientBase implements StatCallback { String snapCount; CountDownLatch cn = new CountDownLatch(1); @BeforeEach @Override public void setUp() throws Exception { // Change the snapcount to happen more often snapCount = System.getProperty("zookeeper.snapCount", "1024"); System.setProperty("zookeeper.snapCount", "10"); super.setUp(); } @AfterEach @Override public void tearDown() throws Exception { System.setProperty("zookeeper.snapCount", snapCount); super.tearDown(); } @Test public void testNullData() throws IOException, InterruptedException, KeeperException { String path = "/SIZE"; ZooKeeper zk = null; zk = createClient(); try { zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // try sync zk exists zk.exists(path, false); zk.exists(path, false, this, null); cn.await(10, TimeUnit.SECONDS); assertSame(0L, cn.getCount()); } finally { if (zk != null) { zk.close(); } } } public void processResult(int rc, String path, Object ctx, Stat stat) { cn.countDown(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/OOMTest.java0100644 0000000 0000000 00000013027 15051152474 032444 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; public class OOMTest extends ZKTestCase { private static final Watcher TEST_WATCHER = event -> System.err.println("Got event: " + event); @Test @Disabled public void testOOM() throws IOException, InterruptedException, KeeperException { File tmpDir = ClientBase.createTmpDir(); // Grab some memory so that it is easier to cause an // OOM condition; List hog = new ArrayList<>(); while (true) { try { hog.add(new byte[1024 * 1024 * 2]); } catch (OutOfMemoryError e) { hog.remove(0); break; } } ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = PortAssignment.unique(); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + PORT, CONNECTION_TIMEOUT), "waiting for server up"); System.err.println("OOM Stage 0"); utestPrep(PORT); System.out.println("Free = " + Runtime.getRuntime().freeMemory() + " total = " + Runtime.getRuntime().totalMemory() + " max = " + Runtime.getRuntime().maxMemory()); System.err.println("OOM Stage 1"); for (int i = 0; i < 1000; i++) { System.out.println(i); utestExists(PORT); } System.out.println("Free = " + Runtime.getRuntime().freeMemory() + " total = " + Runtime.getRuntime().totalMemory() + " max = " + Runtime.getRuntime().maxMemory()); System.err.println("OOM Stage 2"); for (int i = 0; i < 1000; i++) { System.out.println(i); utestGet(PORT); } System.out.println("Free = " + Runtime.getRuntime().freeMemory() + " total = " + Runtime.getRuntime().totalMemory() + " max = " + Runtime.getRuntime().maxMemory()); System.err.println("OOM Stage 3"); for (int i = 0; i < 1000; i++) { System.out.println(i); utestChildren(PORT); } System.out.println("Free = " + Runtime.getRuntime().freeMemory() + " total = " + Runtime.getRuntime().totalMemory() + " max = " + Runtime.getRuntime().maxMemory()); hog.get(0)[0] = (byte) 1; f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + PORT, CONNECTION_TIMEOUT), "waiting for server down"); } private void utestExists(int port) throws IOException, InterruptedException, KeeperException { ZooKeeper zk = new ZooKeeper("127.0.0.1:" + port, CONNECTION_TIMEOUT, TEST_WATCHER); for (int i = 0; i < 10000; i++) { zk.exists("/this/path/doesnt_exist!", true); } zk.close(); } private void utestPrep(int port) throws IOException, InterruptedException, KeeperException { ZooKeeper zk = new ZooKeeper("127.0.0.1:" + port, CONNECTION_TIMEOUT, TEST_WATCHER); for (int i = 0; i < 10000; i++) { zk.create("/" + i, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); } private void utestGet(int port) throws IOException, InterruptedException, KeeperException { ZooKeeper zk = new ZooKeeper("127.0.0.1:" + port, CONNECTION_TIMEOUT, TEST_WATCHER); for (int i = 0; i < 10000; i++) { Stat stat = new Stat(); zk.getData("/" + i, true, stat); } zk.close(); } private void utestChildren(int port) throws IOException, InterruptedException, KeeperException { ZooKeeper zk = new ZooKeeper("127.0.0.1:" + port, CONNECTION_TIMEOUT, TEST_WATCHER); for (int i = 0; i < 10000; i++) { zk.getChildren("/" + i, true); } zk.close(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/OSMXBeanTest.java0100644 0000000 0000000 00000004720 15051152474 033366 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class OSMXBeanTest extends ZKTestCase { private OSMXBean osMbean; private Long ofdc = 0L; private Long mfdc = 0L; protected static final Logger LOG = LoggerFactory.getLogger(OSMXBeanTest.class); @BeforeEach public void initialize() { this.osMbean = new OSMXBean(); assertNotNull(osMbean, "Could not initialize OSMXBean object!"); } @Test public final void testGetUnix() { boolean isUnix = osMbean.getUnix(); if (!isUnix) { LOG.info("Running in a Windows system! Output won't be printed!"); } else { LOG.info("Running in a Unix or Linux system!"); } } @Test public final void testGetOpenFileDescriptorCount() { if (osMbean != null && osMbean.getUnix()) { ofdc = osMbean.getOpenFileDescriptorCount(); LOG.info("open fdcount is: {}", ofdc); } assertFalse((ofdc < 0), "The number of open file descriptor is negative"); } @Test public final void testGetMaxFileDescriptorCount() { if (osMbean != null && osMbean.getUnix()) { mfdc = osMbean.getMaxFileDescriptorCount(); LOG.info("max fdcount is: {}", mfdc); } assertFalse((mfdc < 0), "The max file descriptor number is negative"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Obse0100644 0000000 0000000 00000000175 15051152474 032671 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverHierarchicalQuorumTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverHierarchical0100644 0000000 0000000 00000002524 15051152474 034320 0ustar00rootroot0000000 0000000 /* Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.Test; public class ObserverHierarchicalQuorumTest extends HierarchicalQuorumTest { /** * startServers(true) puts two observers into a 5 peer ensemble */ void startServers() throws Exception { startServers(true); } protected void shutdown(QuorumPeer qp) { QuorumBase.shutdown(qp); } @Test public void testHierarchicalQuorum() throws Throwable { cht.runHammer(5, 10); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverLETest.java0100644 0000000 0000000 00000005460 15051152474 034024 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumStats; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ObserverLETest extends ZKTestCase { final QuorumBase qb = new QuorumBase(); final ClientTest ct = new ClientTest(); @BeforeEach public void establishThreeParticipantOneObserverEnsemble() throws Exception { qb.setUp(true, false); ct.hostPort = qb.hostPort; ct.setUpAll(); qb.s5.shutdown(); } @AfterEach public void shutdownQuorum() throws Exception { ct.tearDownAll(); qb.tearDown(); } /** * See ZOOKEEPER-1294. Confirms that an observer will not support the quorum * of a leader by forming a 5-node, 2-observer ensemble (so quorum size is 2). * When all but the leader and one observer are shut down, the leader should * enter the 'looking' state, not stay in the 'leading' state. */ @Test public void testLEWithObserver() throws Exception { QuorumPeer leader = null; for (QuorumPeer server : Arrays.asList(qb.s1, qb.s2, qb.s3)) { if (server.getServerState().equals(QuorumStats.Provider.FOLLOWING_STATE)) { server.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + server.getClientPort(), ClientBase.CONNECTION_TIMEOUT), "Waiting for server down"); } else { assertNull(leader, "More than one leader found"); leader = server; } } assertTrue(ClientBase.waitForServerState(leader, ClientBase.CONNECTION_TIMEOUT, QuorumStats.Provider.LOOKING_STATE), "Leader is not in Looking state"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Obse0100644 0000000 0000000 00000000161 15051152474 032664 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverMasterTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverMasterTest.j0100644 0000000 0000000 00000057360 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import javax.management.Attribute; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.InvalidAttributeValueException; import javax.management.MBeanException; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import javax.management.RuntimeMBeanException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.ConnectionLossException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.admin.Commands; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.util.PortForwarder; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ObserverMasterTest extends ObserverMasterTestBase { protected static final Logger LOG = LoggerFactory.getLogger(ObserverMasterTest.class); /** * This test ensures two things: * 1. That Observers can successfully proxy requests to the ensemble. * 2. That Observers don't participate in leader elections. * The second is tested by constructing an ensemble where a leader would * be elected if and only if an Observer voted. */ @ParameterizedTest @ValueSource(booleans = {true, false}) public void testObserver(boolean testObserverMaster) throws Exception { // We expect two notifications before we want to continue latch = new CountDownLatch(2); setUp(-1, testObserverMaster); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for server 3 being up"); validateObserverSyncTimeMetrics(); if (testObserverMaster) { int masterPort = q3.getQuorumPeer().observer.getSocket().getPort(); LOG.info("port {} {}", masterPort, OM_PORT); assertEquals(masterPort, OM_PORT, "observer failed to connect to observer master"); } zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_OBS, ClientBase.CONNECTION_TIMEOUT, this); zk.create("/obstest", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // Assert that commands are getting forwarded correctly assertEquals(new String(zk.getData("/obstest", null, null)), "test"); // Now check that other commands don't blow everything up zk.sync("/", null, null); zk.setData("/obstest", "test2".getBytes(), -1); zk.getChildren("/", false); assertEquals(zk.getState(), States.CONNECTED); LOG.info("Shutting down server 2"); // Now kill one of the other real servers q2.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "Waiting for server 2 to shut down"); LOG.info("Server 2 down"); // Now the resulting ensemble shouldn't be quorate latch.await(); assertNotSame(KeeperState.SyncConnected, lastEvent.getState(), "Client is still connected to non-quorate cluster"); LOG.info("Latch returned"); try { assertNotEquals("Shouldn't get a response when cluster not quorate!", "test", new String(zk.getData("/obstest", null, null))); } catch (ConnectionLossException c) { LOG.info("Connection loss exception caught - ensemble not quorate (this is expected)"); } latch = new CountDownLatch(1); LOG.info("Restarting server 2"); // Bring it back //q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection, extraCfgs); q2.start(); LOG.info("Waiting for server 2 to come up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); LOG.info("Server 2 started, waiting for latch"); latch.await(); // It's possible our session expired - but this is ok, shows we // were able to talk to the ensemble assertTrue((KeeperState.SyncConnected == lastEvent.getState() || KeeperState.Expired == lastEvent.getState()), "Client didn't reconnect to quorate ensemble (state was" + lastEvent.getState() + ")"); LOG.info("perform a revalidation test"); int leaderProxyPort = PortAssignment.unique(); int obsProxyPort = PortAssignment.unique(); int leaderPort = q1.getQuorumPeer().leader == null ? CLIENT_PORT_QP2 : CLIENT_PORT_QP1; PortForwarder leaderPF = new PortForwarder(leaderProxyPort, leaderPort); latch = new CountDownLatch(1); ZooKeeper client = new ZooKeeper(String.format("127.0.0.1:%d,127.0.0.1:%d", leaderProxyPort, obsProxyPort), ClientBase.CONNECTION_TIMEOUT, this); latch.await(); client.create("/revalidtest", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertNotNull(client.exists("/revalidtest", null), "Read-after write failed"); latch = new CountDownLatch(2); PortForwarder obsPF = new PortForwarder(obsProxyPort, CLIENT_PORT_OBS); try { leaderPF.shutdown(); } catch (Exception e) { // ignore? } latch.await(); assertEquals(new String(client.getData("/revalidtest", null, null)), "test"); client.close(); obsPF.shutdown(); shutdown(); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testRevalidation(boolean testObserverMaster) throws Exception { setUp(-1, testObserverMaster); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for server 3 being up"); final int leaderProxyPort = PortAssignment.unique(); final int obsProxyPort = PortAssignment.unique(); int leaderPort = q1.getQuorumPeer().leader == null ? CLIENT_PORT_QP2 : CLIENT_PORT_QP1; PortForwarder leaderPF = new PortForwarder(leaderProxyPort, leaderPort); latch = new CountDownLatch(1); zk = new ZooKeeper(String.format("127.0.0.1:%d,127.0.0.1:%d", leaderProxyPort, obsProxyPort), ClientBase.CONNECTION_TIMEOUT, this); latch.await(); zk.create("/revalidtest", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertNotNull(zk.exists("/revalidtest", null), "Read-after write failed"); latch = new CountDownLatch(2); PortForwarder obsPF = new PortForwarder(obsProxyPort, CLIENT_PORT_OBS); try { leaderPF.shutdown(); } catch (Exception e) { // ignore? } latch.await(); assertEquals(new String(zk.getData("/revalidtest", null, null)), "test"); obsPF.shutdown(); shutdown(); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testInOrderCommits(boolean testObserverMaster) throws Exception { setUp(-1, testObserverMaster); zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT, null); for (int i = 0; i < 10; i++) { zk.create("/bulk" + i, ("Initial data of some size").getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for observer to be up"); latch = new CountDownLatch(1); zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT, this); latch.await(); assertEquals(zk.getState(), States.CONNECTED); zk.create("/init", "first".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final long zxid = q1.getQuorumPeer().getLastLoggedZxid(); // wait for change to propagate waitFor("Timeout waiting for observer sync", new WaitForCondition() { public boolean evaluate() { return zxid == q3.getQuorumPeer().getLastLoggedZxid(); } }, 30); ZooKeeper obsZk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_OBS, ClientBase.CONNECTION_TIMEOUT, this); int followerPort = q1.getQuorumPeer().leader == null ? CLIENT_PORT_QP1 : CLIENT_PORT_QP2; ZooKeeper fZk = new ZooKeeper("127.0.0.1:" + followerPort, ClientBase.CONNECTION_TIMEOUT, this); final int numTransactions = 10001; CountDownLatch gate = new CountDownLatch(1); CountDownLatch oAsyncLatch = new CountDownLatch(numTransactions); Thread oAsyncWriteThread = new Thread(new AsyncWriter(obsZk, numTransactions, true, oAsyncLatch, "/obs", gate)); CountDownLatch fAsyncLatch = new CountDownLatch(numTransactions); Thread fAsyncWriteThread = new Thread(new AsyncWriter(fZk, numTransactions, true, fAsyncLatch, "/follower", gate)); LOG.info("ASYNC WRITES"); oAsyncWriteThread.start(); fAsyncWriteThread.start(); gate.countDown(); oAsyncLatch.await(); fAsyncLatch.await(); oAsyncWriteThread.join(ClientBase.CONNECTION_TIMEOUT); if (oAsyncWriteThread.isAlive()) { LOG.error("asyncWriteThread is still alive"); } fAsyncWriteThread.join(ClientBase.CONNECTION_TIMEOUT); if (fAsyncWriteThread.isAlive()) { LOG.error("asyncWriteThread is still alive"); } obsZk.close(); fZk.close(); shutdown(); } @ParameterizedTest @ValueSource(booleans = {true, false}) public void testAdminCommands(boolean testObserverMaster) throws IOException, MBeanException, InstanceNotFoundException, ReflectionException, InterruptedException, MalformedObjectNameException, AttributeNotFoundException, InvalidAttributeValueException, KeeperException { // flush all beans, then start for (ZKMBeanInfo beanInfo : MBeanRegistry.getInstance().getRegisteredBeans()) { MBeanRegistry.getInstance().unregister(beanInfo); } JMXEnv.setUp(); setUp(-1, testObserverMaster); q3.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for observer to be up"); // Assert that commands are getting forwarded correctly zk = new ZooKeeper("127.0.0.1:" + CLIENT_PORT_OBS, ClientBase.CONNECTION_TIMEOUT, this); zk.create("/obstest", "test".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEquals(new String(zk.getData("/obstest", null, null)), "test"); // test stats collection final Map emptyMap = Collections.emptyMap(); Map stats = Commands.runGetCommand("mntr", q3.getQuorumPeer().getActiveServer(), emptyMap, null, null).toMap(); assertTrue(stats.containsKey("observer_master_id"), "observer not emitting observer_master_id"); // check the stats for the first peer if (testObserverMaster) { if (q1.getQuorumPeer().leader == null) { assertEquals(Integer.valueOf(1), q1.getQuorumPeer().getSynced_observers_metric()); } else { assertEquals(Integer.valueOf(0), q1.getQuorumPeer().getSynced_observers_metric()); } } else { if (q1.getQuorumPeer().leader == null) { assertNull(q1.getQuorumPeer().getSynced_observers_metric()); } else { assertEquals(Integer.valueOf(1), q1.getQuorumPeer().getSynced_observers_metric()); } } // check the stats for the second peer if (testObserverMaster) { if (q2.getQuorumPeer().leader == null) { assertEquals(Integer.valueOf(1), q2.getQuorumPeer().getSynced_observers_metric()); } else { assertEquals(Integer.valueOf(0), q2.getQuorumPeer().getSynced_observers_metric()); } } else { if (q2.getQuorumPeer().leader == null) { assertNull(q2.getQuorumPeer().getSynced_observers_metric()); } else { assertEquals(Integer.valueOf(1), q2.getQuorumPeer().getSynced_observers_metric()); } } // test admin commands for disconnection ObjectName connBean = null; for (ObjectName bean : JMXEnv.conn().queryNames(new ObjectName(MBeanRegistry.DOMAIN + ":*"), null)) { if (bean.getCanonicalName().contains("Learner_Connections") && bean.getCanonicalName().contains("id:" + q3.getQuorumPeer().getMyId())) { connBean = bean; break; } } assertNotNull(connBean, "could not find connection bean"); latch = new CountDownLatch(1); JMXEnv.conn().invoke(connBean, "terminateConnection", new Object[0], null); assertTrue(latch.await(CONNECTION_TIMEOUT / 2, TimeUnit.MILLISECONDS), "server failed to disconnect on terminate"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for server 3 being up"); final String obsBeanName = String.format("org.apache.ZooKeeperService:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Observer", q3.getQuorumPeer().getMyId(), q3.getQuorumPeer().getMyId()); Set names = JMXEnv.conn().queryNames(new ObjectName(obsBeanName), null); assertEquals(1, names.size(), "expecting singular observer bean"); ObjectName obsBean = names.iterator().next(); if (testObserverMaster) { // show we can move the observer using the id long observerMasterId = q3.getQuorumPeer().observer.getLearnerMasterId(); latch = new CountDownLatch(1); JMXEnv.conn().setAttribute(obsBean, new Attribute("LearnerMaster", Long.toString(3 - observerMasterId))); assertTrue(latch.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS), "server failed to disconnect on terminate"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_OBS, CONNECTION_TIMEOUT), "waiting for server 3 being up"); } else { // show we get an error final long leaderId = q1.getQuorumPeer().leader == null ? 2 : 1; try { JMXEnv.conn().setAttribute(obsBean, new Attribute("LearnerMaster", Long.toString(3 - leaderId))); fail("should have seen an exception on previous command"); } catch (RuntimeMBeanException e) { assertEquals(IllegalArgumentException.class, e.getCause().getClass(), "mbean failed for the wrong reason"); } } shutdown(); JMXEnv.tearDown(); } private String createServerString(String type, long serverId, int clientPort) { return "server." + serverId + "=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":" + type + ";" + clientPort; } private void waitServerUp(int clientPort) { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT), "waiting for server being up"); } private ZooKeeperAdmin createAdmin(int clientPort) throws IOException { System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); QuorumPeerConfig.setReconfigEnabled(true); ZooKeeperAdmin admin = new ZooKeeperAdmin( "127.0.0.1:" + clientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); admin.addAuthInfo("digest", "super:test".getBytes()); return admin; } // This test is known to be flaky and fail due to "reconfig already in progress". // TODO: Investigate intermittent testDynamicReconfig failures. @ParameterizedTest @ValueSource(booleans = {true, false}) @Disabled public void testDynamicReconfig(boolean testObserverMaster) throws InterruptedException, IOException, KeeperException { if (!testObserverMaster) { return; } ClientBase.setupTestEnv(); // create a quorum running with different observer master port // to make it easier to choose which server the observer is // following with // // we have setObserverMaster function but it's broken, use this // solution before we fixed that int clientPort1 = PortAssignment.unique(); int clientPort2 = PortAssignment.unique(); int omPort1 = PortAssignment.unique(); int omPort2 = PortAssignment.unique(); String quorumCfgSection = createServerString("participant", 1, clientPort1) + "\n" + createServerString("participant", 2, clientPort2); MainThread s1 = new MainThread(1, clientPort1, quorumCfgSection, String.format("observerMasterPort=%d%n", omPort1)); MainThread s2 = new MainThread(2, clientPort2, quorumCfgSection, String.format("observerMasterPort=%d%n", omPort2)); s1.start(); s2.start(); waitServerUp(clientPort1); waitServerUp(clientPort2); // create observer to follow non-leader observer master long nonLeaderOMPort = s1.getQuorumPeer().leader == null ? omPort1 : omPort2; int observerClientPort = PortAssignment.unique(); int observerId = 10; MainThread observer = new MainThread( observerId, observerClientPort, quorumCfgSection + "\n" + createServerString("observer", observerId, observerClientPort), String.format("observerMasterPort=%d%n", nonLeaderOMPort)); LOG.info("starting observer"); observer.start(); waitServerUp(observerClientPort); // create a client to the observer final LinkedBlockingQueue states = new LinkedBlockingQueue<>(); ZooKeeper observerClient = new ZooKeeper( "127.0.0.1:" + observerClientPort, ClientBase.CONNECTION_TIMEOUT, event -> { try { states.put(event.getState()); } catch (InterruptedException ignore) { } }); // wait for connected KeeperState state = states.poll(1000, TimeUnit.MILLISECONDS); assertEquals(KeeperState.SyncConnected, state); // issue reconfig command ArrayList newServers = new ArrayList<>(); String server = "server.3=127.0.0.1:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":participant;localhost:" + PortAssignment.unique(); newServers.add(server); ZooKeeperAdmin admin = createAdmin(clientPort1); ReconfigTest.reconfig(admin, newServers, null, null, -1); // make sure the observer has the new config ReconfigTest.testServerHasConfig(observerClient, newServers, null); // shouldn't be disconnected during reconfig, so expect to not // receive any new event state = states.poll(1000, TimeUnit.MILLISECONDS); assertNull(state); admin.close(); observerClient.close(); observer.shutdown(); s2.shutdown(); s1.shutdown(); } class AsyncWriter implements Runnable { private final ZooKeeper client; private final int numTransactions; private final boolean issueSync; private final CountDownLatch writerLatch; private final String root; private final CountDownLatch gate; AsyncWriter(ZooKeeper client, int numTransactions, boolean issueSync, CountDownLatch writerLatch, String root, CountDownLatch gate) { this.client = client; this.numTransactions = numTransactions; this.issueSync = issueSync; this.writerLatch = writerLatch; this.root = root; this.gate = gate; } @Override public void run() { if (gate != null) { try { gate.await(); } catch (InterruptedException e) { LOG.error("Gate interrupted"); return; } } for (int i = 0; i < numTransactions; i++) { final boolean pleaseLog = i % 100 == 0; client.create(root + i, "inner thread".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (rc, path, ctx, name) -> { writerLatch.countDown(); if (pleaseLog) { LOG.info("wrote {}", path); } }, null); if (pleaseLog) { LOG.info("async wrote {}{}", root, i); if (issueSync) { client.sync(root + "0", null, null); } } } } } private void validateObserverSyncTimeMetrics() { final String name = "observer_sync_time"; final Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(5, metrics.keySet().stream().filter(key -> key.contains(name)).count()); assertNotNull(metrics.get(String.format("avg_%s", name))); assertNotNull(metrics.get(String.format("min_%s", name))); assertNotNull(metrics.get(String.format("max_%s", name))); assertNotNull(metrics.get(String.format("cnt_%s", name))); assertNotNull(metrics.get(String.format("sum_%s", name))); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Obse0100644 0000000 0000000 00000000165 15051152474 032670 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverMasterTestBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverMasterTestBa0100644 0000000 0000000 00000011354 15051152474 034301 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.server.util.PortForwarder; public class ObserverMasterTestBase extends QuorumPeerTestBase implements Watcher { protected CountDownLatch latch; protected ZooKeeper zk; protected int CLIENT_PORT_QP1; protected int CLIENT_PORT_QP2; protected int CLIENT_PORT_OBS; protected int OM_PORT; protected MainThread q1; protected MainThread q2; protected MainThread q3; protected WatchedEvent lastEvent = null; protected PortForwarder setUp(final int omProxyPort, final Boolean testObserverMaster) throws IOException { ClientBase.setupTestEnv(); final int PORT_QP1 = PortAssignment.unique(); final int PORT_QP2 = PortAssignment.unique(); final int PORT_OBS = PortAssignment.unique(); final int PORT_QP_LE1 = PortAssignment.unique(); final int PORT_QP_LE2 = PortAssignment.unique(); final int PORT_OBS_LE = PortAssignment.unique(); CLIENT_PORT_QP1 = PortAssignment.unique(); CLIENT_PORT_QP2 = PortAssignment.unique(); CLIENT_PORT_OBS = PortAssignment.unique(); OM_PORT = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + (PORT_QP1) + ":" + (PORT_QP_LE1) + ";" + CLIENT_PORT_QP1 + "\nserver.2=127.0.0.1:" + (PORT_QP2) + ":" + (PORT_QP_LE2) + ";" + CLIENT_PORT_QP2 + "\nserver.3=127.0.0.1:" + (PORT_OBS) + ":" + (PORT_OBS_LE) + ":observer" + ";" + CLIENT_PORT_OBS; String extraCfgs = testObserverMaster ? String.format("observerMasterPort=%d%n", OM_PORT) : ""; String extraCfgsObs = testObserverMaster ? String.format("observerMasterPort=%d%n", omProxyPort <= 0 ? OM_PORT : omProxyPort) : ""; PortForwarder forwarder = null; if (testObserverMaster && omProxyPort >= 0) { forwarder = new PortForwarder(omProxyPort, OM_PORT); } q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection, extraCfgs); q2 = new MainThread(2, CLIENT_PORT_QP2, quorumCfgSection, extraCfgs); q3 = new MainThread(3, CLIENT_PORT_OBS, quorumCfgSection, extraCfgsObs); q1.start(); q2.start(); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP1, CONNECTION_TIMEOUT), "waiting for server 1 being up"); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + CLIENT_PORT_QP2, CONNECTION_TIMEOUT), "waiting for server 2 being up"); return forwarder; } protected void shutdown() throws InterruptedException { LOG.info("Shutting down all servers"); zk.close(); q1.shutdown(); q2.shutdown(); q3.shutdown(); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP1, ClientBase.CONNECTION_TIMEOUT), "Waiting for server 1 to shut down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_QP2, ClientBase.CONNECTION_TIMEOUT), "Waiting for server 2 to shut down"); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + CLIENT_PORT_OBS, ClientBase.CONNECTION_TIMEOUT), "Waiting for server 3 to shut down"); } /** * Implementation of watcher interface. */ public void process(WatchedEvent event) { lastEvent = event; if (latch != null) { latch.countDown(); } LOG.info("Latch got event :: {}", event); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Obse0100644 0000000 0000000 00000000167 15051152474 032672 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverQuorumHammerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverQuorumHammer0100644 0000000 0000000 00000002476 15051152474 034372 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ObserverQuorumHammerTest extends QuorumHammerTest { public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; @BeforeEach @Override public void setUp() throws Exception { qb.setUp(true, false); cht.hostPort = qb.hostPort; cht.setUpAll(); } @Test public void testHammerBasic() throws Throwable { cht.testHammerBasic(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ObserverTest.java0100644 0000000 0000000 00000005445 15051152474 033606 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ObserverTest extends QuorumPeerTestBase implements Watcher { protected static final Logger LOG = LoggerFactory.getLogger(ObserverTest.class); ZooKeeper zk; /** * This test ensures that an Observer does not elect itself as a leader, or * indeed come up properly, if it is the lone member of an ensemble. * @throws Exception */ @Test public void testObserverOnly() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + (PortAssignment.unique()) + ":" + (PortAssignment.unique()) + ":observer;" + CLIENT_PORT_QP1 + "\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); q1.join(ClientBase.CONNECTION_TIMEOUT); assertFalse(q1.isAlive()); } /** * Ensure that observer only comes up when a proper ensemble is configured. * (and will not come up with standalone server). */ @Test public void testObserverWithStandlone() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT_QP1 = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + (PortAssignment.unique()) + ":" + (PortAssignment.unique()) + ":observer\n" + "server.2=127.0.0.1:" + (PortAssignment.unique()) + ":" + (PortAssignment.unique()) + "\npeerType=observer\n"; MainThread q1 = new MainThread(1, CLIENT_PORT_QP1, quorumCfgSection); q1.start(); q1.join(ClientBase.CONNECTION_TIMEOUT); assertFalse(q1.isAlive()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Pers0100644 0000000 0000000 00000000175 15051152474 032712 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveWatcherTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentRecursiveW0100644 0000000 0000000 00000032542 15051152474 034414 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.AddWatchMode.PERSISTENT; import static org.apache.zookeeper.AddWatchMode.PERSISTENT_RECURSIVE; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PersistentRecursiveWatcherTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(PersistentRecursiveWatcherTest.class); private BlockingQueue events; private Watcher persistentWatcher; @Override @BeforeEach public void setUp() throws Exception { super.setUp(); events = new LinkedBlockingQueue<>(); persistentWatcher = event -> events.add(event); } @Test public void testBasic() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT_RECURSIVE); internalTestBasic(zk); } } @Test public void testBasicAsync() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { final CountDownLatch latch = new CountDownLatch(1); AsyncCallback.VoidCallback cb = (rc, path, ctx) -> { if (rc == KeeperException.Code.OK.intValue()) { latch.countDown(); } }; zk.addWatch("/a/b", persistentWatcher, PERSISTENT_RECURSIVE, cb, null); assertTrue(latch.await(5, TimeUnit.SECONDS)); internalTestBasic(zk); } } private void internalTestBasic(ZooKeeper zk) throws KeeperException, InterruptedException { zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = new Stat(); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b", stat); zk.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b/c", stat); zk.create("/a/b/c/d", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b/c/d", stat); zk.create("/a/b/c/d/e", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b/c/d/e", stat); stat = zk.setData("/a/b/c/d/e", new byte[0], -1); assertEvent(events, EventType.NodeDataChanged, "/a/b/c/d/e", stat); zk.delete("/a/b/c/d/e", -1); assertEvent(events, EventType.NodeDeleted, "/a/b/c/d/e", zk.exists("/a/b/c/d", false).getPzxid()); zk.create("/a/b/c/d/e", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b/c/d/e", stat); } @Test public void testRemoval() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT_RECURSIVE); zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat = new Stat(); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b", stat); zk.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b/c", stat); zk.removeWatches("/a/b", persistentWatcher, Watcher.WatcherType.Any, false); zk.create("/a/b/c/d", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEvent(events, EventType.PersistentWatchRemoved, "/a/b", WatchedEvent.NO_ZXID); } } @Test public void testNoChildEvents() throws Exception { try (ZooKeeper zk = createClient()) { zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.addWatch("/", persistentWatcher, PERSISTENT_RECURSIVE); BlockingQueue childEvents = new LinkedBlockingQueue<>(); zk.getChildren("/a", childEvents::add); Stat createABStat = new Stat(); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, createABStat); Stat createABCStat = new Stat(); zk.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, createABCStat); assertEvent(childEvents, Watcher.Event.EventType.NodeChildrenChanged, "/a", createABStat.getPzxid()); assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b", createABStat); assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b/c", createABCStat); assertTrue(events.isEmpty()); } } @Test public void testDisconnect() throws Exception { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT_RECURSIVE); stopServer(); assertEvent(events, EventType.None, KeeperState.Disconnected, null, WatchedEvent.NO_ZXID); startServer(); assertEvent(events, EventType.None, KeeperState.SyncConnected, null, WatchedEvent.NO_ZXID); internalTestBasic(zk); } } @Test public void testMultiClient() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk1 = createClient(new CountdownWatcher(), hostPort); ZooKeeper zk2 = createClient(new CountdownWatcher(), hostPort)) { zk1.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.addWatch("/a/b", persistentWatcher, PERSISTENT_RECURSIVE); Stat stat = zk1.setData("/a/b/c", "one".getBytes(), -1); assertEvent(events, EventType.NodeDataChanged, "/a/b/c", stat.getMzxid()); stat = zk2.setData("/a/b/c", "two".getBytes(), -1); assertEvent(events, EventType.NodeDataChanged, "/a/b/c", stat.getMzxid()); stat = zk2.setData("/a/b/c", "three".getBytes(), -1); assertEvent(events, EventType.NodeDataChanged, "/a/b/c", stat.getMzxid()); stat = zk2.setData("/a/b/c", "four".getBytes(), -1); assertEvent(events, EventType.NodeDataChanged, "/a/b/c", stat.getMzxid()); } } @Test public void testSamePathWithDifferentWatchModes() throws Exception { try (ZooKeeper zk = createClient()) { BlockingQueue dataEvents = new LinkedBlockingQueue<>(); BlockingQueue childEvents = new LinkedBlockingQueue<>(); BlockingQueue persistentEvents = new LinkedBlockingQueue<>(); BlockingQueue recursiveEvents = new LinkedBlockingQueue<>(); zk.addWatch("/a", persistentEvents::add, PERSISTENT); zk.addWatch("/a", recursiveEvents::add, PERSISTENT_RECURSIVE); zk.exists("/a", dataEvents::add); Stat stat = new Stat(); zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(dataEvents, Watcher.Event.EventType.NodeCreated, "/a", stat); assertEvent(persistentEvents, Watcher.Event.EventType.NodeCreated, "/a", stat); assertEvent(recursiveEvents, Watcher.Event.EventType.NodeCreated, "/a", stat); zk.getData("/a", dataEvents::add, null); stat = zk.setData("/a", new byte[0], -1); assertEvent(dataEvents, Watcher.Event.EventType.NodeDataChanged, "/a", stat); assertEvent(persistentEvents, Watcher.Event.EventType.NodeDataChanged, "/a", stat); assertEvent(recursiveEvents, Watcher.Event.EventType.NodeDataChanged, "/a", stat); zk.getChildren("/a", childEvents::add); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(childEvents, Watcher.Event.EventType.NodeChildrenChanged, "/a", stat); assertEvent(persistentEvents, Watcher.Event.EventType.NodeChildrenChanged, "/a", stat); assertEvent(recursiveEvents, Watcher.Event.EventType.NodeCreated, "/a/b", stat); zk.getChildren("/a", childEvents::add); zk.delete("/a/b", -1); stat = zk.exists("/a", false); assertEvent(childEvents, Watcher.Event.EventType.NodeChildrenChanged, "/a", stat.getPzxid()); assertEvent(persistentEvents, Watcher.Event.EventType.NodeChildrenChanged, "/a", stat.getPzxid()); assertEvent(recursiveEvents, Watcher.Event.EventType.NodeDeleted, "/a/b", stat.getPzxid()); zk.getChildren("/a", childEvents::add); zk.getData("/a", dataEvents::add, null); zk.exists("/a", dataEvents::add); zk.delete("/a", -1); stat = zk.exists("/", false); assertEvent(childEvents, Watcher.Event.EventType.NodeDeleted, "/a", stat.getPzxid()); assertEvent(dataEvents, Watcher.Event.EventType.NodeDeleted, "/a", stat.getPzxid()); assertEvent(dataEvents, Watcher.Event.EventType.NodeDeleted, "/a", stat.getPzxid()); assertEvent(persistentEvents, Watcher.Event.EventType.NodeDeleted, "/a", stat.getPzxid()); assertEvent(recursiveEvents, Watcher.Event.EventType.NodeDeleted, "/a", stat.getPzxid()); } } @Test public void testRootWatcher() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/", persistentWatcher, PERSISTENT_RECURSIVE); Stat stat = new Stat(); zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a", stat.getMzxid()); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/a/b", stat.getMzxid()); zk.create("/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/b", stat.getMzxid()); zk.create("/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertEvent(events, EventType.NodeCreated, "/b/c", stat.getMzxid()); } } private void assertEvent(BlockingQueue events, EventType eventType, String path, Stat stat) throws InterruptedException { assertEvent(events, eventType, path, stat.getMzxid()); } private void assertEvent(BlockingQueue events, EventType eventType, String path, long zxid) throws InterruptedException { assertEvent(events, eventType, KeeperState.SyncConnected, path, zxid); } private void assertEvent(BlockingQueue events, EventType eventType, KeeperState keeperState, String path, long zxid) throws InterruptedException { WatchedEvent actualEvent = events.poll(5, TimeUnit.SECONDS); assertNotNull(actualEvent); WatchedEvent expectedEvent = new WatchedEvent( eventType, keeperState, path, zxid ); TestUtils.assertWatchedEventEquals(expectedEvent, actualEvent); } }./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Pers0100644 0000000 0000000 00000000167 15051152474 032713 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentWatcherACLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentWatcherACL0100644 0000000 0000000 00000056202 15051152474 034232 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.AddWatchMode.PERSISTENT; import static org.apache.zookeeper.AddWatchMode.PERSISTENT_RECURSIVE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class encodes a set of tests corresponding to a "truth table" * of interactions between persistent watchers and znode ACLs: * * https://docs.google.com/spreadsheets/d/1eMH2aimrrMc_b6McU8CHm2yCj2X-w30Fy4fCBOHn7NA/edit#gid=0 */ public class PersistentWatcherACLTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(PersistentWatcherACLTest.class); /** An ACL denying READ. */ private static final List ACL_NO_READ = Collections.singletonList(new ACL(ZooDefs.Perms.ALL & ~ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)); private BlockingQueue events; private Watcher persistentWatcher; @Override @BeforeEach public void setUp() throws Exception { super.setUp(); events = new LinkedBlockingQueue<>(); persistentWatcher = event -> { events.add(event); LOG.info("Added event: {}; total: {}", event, events.size()); }; } /** * This Step class, with the Round class below, is used to encode * the contents of the truth table. * * (These should become Records once we target JDK 14+.) */ private static class Step { Step(int opCode, String target) { this(opCode, target, null, null); } Step(int opCode, String target, EventType eventType, String eventPath) { this.opCode = opCode; this.target = target; this.eventType = eventType; this.eventPath = eventPath; } /** Action: create, setData or delete */ final int opCode; /** Target path */ final String target; /** Expected event type, {@code null} if no event is expected */ final EventType eventType; /** Expected event path, {@code null} if no event is expected */ final String eventPath; } /** * This Round class, with the Step class above, is used to encode * the contents of the truth table. * * (These should become Records once we target JDK 14+.) */ private static class Round { Round(String summary, Boolean allowA, Boolean allowB, Boolean allowC, String watchTarget, AddWatchMode watchMode, Step[] steps) { this.summary = summary; this.allowA = allowA; this.allowB = allowB; this.allowC = allowC; this.watchTarget = watchTarget; this.watchMode = watchMode; this.steps = steps; } /** Notes/summary */ final String summary; /** Should /a's ACL leave it readable? */ final Boolean allowA; /** Should /a/b's ACL leave it readable? */ final Boolean allowB; /** Should /a/b/c's ACL leave it readable? */ final Boolean allowC; /** Watch path */ final String watchTarget; /** Watch mode */ final AddWatchMode watchMode; /** Actions and expected events */ final Step[] steps; } /** * A "round" of tests from the table encoded as Java objects. * * Note that the set of rounds is collected in a {@code ROUNDS} * array below, and that this test class includes a {@code main} * method which produces a "CSV" rendition of the table, for ease * of comparison with the original. * * @see #ROUNDS */ private static final Round roundNothingAsAIsWatchedButDeniedBIsNotWatched = new Round( "Nothing as a is watched but denied. b is not watched", false, true, null, "/a", PERSISTENT, new Step[] { new Step(ZooDefs.OpCode.setData, "/a"), new Step(ZooDefs.OpCode.create, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundNothingAsBothAAndBDenied = new Round( "Nothing as both a and b denied", false, false, null, "/a", PERSISTENT, new Step[] { new Step(ZooDefs.OpCode.setData, "/a"), new Step(ZooDefs.OpCode.create, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundAChangesInclChildrenAreSeen = new Round( "a changes, incl children, are seen", true, false, null, "/a", PERSISTENT, new Step[] { new Step(ZooDefs.OpCode.create, "/a", EventType.NodeCreated, "/a"), new Step(ZooDefs.OpCode.setData, "/a", EventType.NodeDataChanged, "/a"), new Step(ZooDefs.OpCode.create, "/a/b", EventType.NodeChildrenChanged, "/a"), new Step(ZooDefs.OpCode.setData, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a/b", EventType.NodeChildrenChanged, "/a"), new Step(ZooDefs.OpCode.delete, "/a", EventType.NodeDeleted, "/a"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundNothingForAAsItSDeniedBChangesSeen = new Round( "Nothing for a as it's denied, b changes allowed/seen", false, true, null, "/a", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.setData, "/a"), new Step(ZooDefs.OpCode.create, "/a/b", EventType.NodeCreated, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b", EventType.NodeDataChanged, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a/b", EventType.NodeDeleted, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundNothingBothDenied = new Round( "Nothing - both denied", false, false, null, "/a", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.setData, "/a"), new Step(ZooDefs.OpCode.create, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a/b"), new Step(ZooDefs.OpCode.delete, "/a"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundNothingAllDenied = new Round( "Nothing - all denied", false, false, false, "/a", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.create, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b"), new Step(ZooDefs.OpCode.create, "/a/b/c"), new Step(ZooDefs.OpCode.setData, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundADeniesSeeAllChangesForBAndCIncludingBChildren = new Round( "a denies, see all changes for b and c, including b's children", false, true, true, "/a", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.create, "/a/b", EventType.NodeCreated, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b", EventType.NodeDataChanged, "/a/b"), new Step(ZooDefs.OpCode.create, "/a/b/c", EventType.NodeCreated, "/a/b/c"), new Step(ZooDefs.OpCode.setData, "/a/b/c", EventType.NodeDataChanged, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b/c", EventType.NodeDeleted, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b", EventType.NodeDeleted, "/a/b"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundADeniesSeeAllBChangesAndBChildrenNothingForC = new Round( "a denies, see all b changes and b's children, nothing for c", false, true, false, "/a", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.create, "/a/b", EventType.NodeCreated, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b", EventType.NodeDataChanged, "/a/b"), new Step(ZooDefs.OpCode.create, "/a/b/c"), new Step(ZooDefs.OpCode.setData, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b", EventType.NodeDeleted, "/a/b"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundNothingTheWatchIsOnC = new Round( "Nothing - the watch is on c", false, true, false, "/a/b/c", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.create, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b"), new Step(ZooDefs.OpCode.create, "/a/b/c"), new Step(ZooDefs.OpCode.setData, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b"), } ); /** * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round roundTheWatchIsOnlyOnCBAndCAllowed = new Round( "The watch is only on c (b and c allowed)", false, true, true, "/a/b/c", PERSISTENT_RECURSIVE, new Step[] { new Step(ZooDefs.OpCode.create, "/a/b"), new Step(ZooDefs.OpCode.setData, "/a/b"), new Step(ZooDefs.OpCode.create, "/a/b/c", EventType.NodeCreated, "/a/b/c"), new Step(ZooDefs.OpCode.setData, "/a/b/c", EventType.NodeDataChanged, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b/c", EventType.NodeDeleted, "/a/b/c"), new Step(ZooDefs.OpCode.delete, "/a/b"), } ); /** * Transform the "tristate" {@code allow} property to a concrete * ACL which can be passed to the ZooKeeper API. * * @param allow "tristate" value: {@code null}/don't care, {@code * true}, {@code false} * @return the ACL */ private static List selectAcl(Boolean allow) { if (allow == null) { return null; } else if (!allow) { return ACL_NO_READ; } else { return ZooDefs.Ids.OPEN_ACL_UNSAFE; } } /** * Executes one "round" of tests from the Java object encoding of * the table. * * @param round the "round" * * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched * @see PersistentWatcherACLTest.Round * @see PersistentWatcherACLTest.Step */ private void execRound(Round round) throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { List aclForA = selectAcl(round.allowA); List aclForB = selectAcl(round.allowB); List aclForC = selectAcl(round.allowC); boolean firstStepCreatesA = round.steps.length > 0 && round.steps[0].opCode == ZooDefs.OpCode.create && round.steps[0].target.equals("/a"); // Assume /a always exists (except if it's about to be created) if (!firstStepCreatesA) { zk.create("/a", new byte[0], aclForA, CreateMode.PERSISTENT); } zk.addWatch(round.watchTarget, persistentWatcher, round.watchMode); for (int i = 0; i < round.steps.length; i++) { Step step = round.steps[i]; switch (step.opCode) { case ZooDefs.OpCode.create: List acl = step.target.endsWith("/c") ? aclForC : step.target.endsWith("/b") ? aclForB : aclForA; zk.create(step.target, new byte[0], acl, CreateMode.PERSISTENT); break; case ZooDefs.OpCode.delete: zk.delete(step.target, -1); break; case ZooDefs.OpCode.setData: zk.setData(step.target, new byte[0], -1); break; default: fail("Unexpected opCode " + step.opCode + " in step " + i); break; } WatchedEvent actualEvent = events.poll(500, TimeUnit.MILLISECONDS); if (step.eventType == null) { assertNull(actualEvent, "Unexpected event " + actualEvent + " at step " + i); } else { String m = "In event " + actualEvent + " at step " + i; assertNotNull(actualEvent, m); assertEquals(step.eventType, actualEvent.getType(), m); assertEquals(step.eventPath, actualEvent.getPath(), m); } } } } /** * A test method, wrapping the definition of a "round." This * should really use JUnit 5's runtime test case generation * facilities, but that would prevent backporting this suite to * JUnit 4. * * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched * @see JUnit 5 runtime test case generation */ @Test public void testNothingAsAIsWatchedButDeniedBIsNotWatched() throws IOException, InterruptedException, KeeperException { execRound(roundNothingAsAIsWatchedButDeniedBIsNotWatched); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundNothingAsBothAAndBDenied */ @Test public void testNothingAsBothAAndBDenied() throws IOException, InterruptedException, KeeperException { execRound(roundNothingAsBothAAndBDenied); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundAChangesInclChildrenAreSeen */ @Test public void testAChangesInclChildrenAreSeen() throws IOException, InterruptedException, KeeperException { execRound(roundAChangesInclChildrenAreSeen); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundNothingForAAsItSDeniedBChangesSeen */ @Test public void testNothingForAAsItSDeniedBChangesSeen() throws IOException, InterruptedException, KeeperException { execRound(roundNothingForAAsItSDeniedBChangesSeen); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundNothingBothDenied */ @Test public void testNothingBothDenied() throws IOException, InterruptedException, KeeperException { execRound(roundNothingBothDenied); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundNothingAllDenied */ @Test public void testNothingAllDenied() throws IOException, InterruptedException, KeeperException { execRound(roundNothingAllDenied); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundADeniesSeeAllChangesForBAndCIncludingBChildren */ @Test public void testADeniesSeeAllChangesForBAndCIncludingBChildren() throws IOException, InterruptedException, KeeperException { execRound(roundADeniesSeeAllChangesForBAndCIncludingBChildren); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundADeniesSeeAllBChangesAndBChildrenNothingForC */ @Test public void testADeniesSeeAllBChangesAndBChildrenNothingForC() throws IOException, InterruptedException, KeeperException { execRound(roundADeniesSeeAllBChangesAndBChildrenNothingForC); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundNothingTheWatchIsOnC */ @Test public void testNothingTheWatchIsOnC() throws IOException, InterruptedException, KeeperException { execRound(roundNothingTheWatchIsOnC); } /** * @see #testNothingAsAIsWatchedButDeniedBIsNotWatched * @see #roundTheWatchIsOnlyOnCBAndCAllowed */ @Test public void testTheWatchIsOnlyOnCBAndCAllowed() throws IOException, InterruptedException, KeeperException { execRound(roundTheWatchIsOnlyOnCBAndCAllowed); } // The rest of this class is the world's lamest "CSV" encoder. /** * The set of rounds. This array includes one entry for each * {@code private static final Round round*} member variable * defined above. * * @see #roundNothingAsAIsWatchedButDeniedBIsNotWatched */ private static final Round[] ROUNDS = new Round[] { roundNothingAsAIsWatchedButDeniedBIsNotWatched, roundNothingAsBothAAndBDenied, roundAChangesInclChildrenAreSeen, roundNothingForAAsItSDeniedBChangesSeen, roundNothingBothDenied, roundNothingAllDenied, roundADeniesSeeAllChangesForBAndCIncludingBChildren, roundADeniesSeeAllBChangesAndBChildrenNothingForC, roundNothingTheWatchIsOnC, roundTheWatchIsOnlyOnCBAndCAllowed, }; private static String allowString(String prefix, Boolean allow) { if (allow == null) { return ""; } else { return prefix + (allow ? "allow" : "deny"); } } private static String watchModeString(AddWatchMode watchMode) { switch (watchMode) { case PERSISTENT: return "PERSISTENT"; case PERSISTENT_RECURSIVE: return "PRECURSIVE"; default: return "?"; } } private static String actionString(int opCode) { switch (opCode) { case ZooDefs.OpCode.create: return "create"; case ZooDefs.OpCode.delete: return "delete"; case ZooDefs.OpCode.setData: return "modify"; default: return "?"; } } private static String eventPathString(String eventPath) { if (eventPath == null) { return "?"; } else if (eventPath.length() <= 1) { return eventPath; } else { return eventPath.substring(eventPath.lastIndexOf('/') + 1); } } /** * Generates a "CSV" rendition of the table in sb. * * @param sb the target string builder */ private static void genCsv(StringBuilder sb) { sb.append("Initial State,") .append("Action,") .append("NodeCreated,") .append("NodeDeleted,") .append("NodeDataChanged,") .append("NodeChildrenChanged,") .append("Notes/summary\n"); sb.append("Assume /a always exists\n\n"); for (Round round : ROUNDS) { sb.append("\"ACL") .append(allowString(": a ", round.allowA)) .append(allowString(", b ", round.allowB)) .append(allowString(", c ", round.allowC)) .append("\"") .append(",,,,,,\"") .append(round.summary) .append("\"\n"); for (int i = 0; i < round.steps.length; i++) { Step step = round.steps[i]; if (i == 0) { sb.append("\"addWatch(") .append(round.watchTarget) .append(", ") .append(watchModeString(round.watchMode)) .append(")\""); } sb.append(",") .append(actionString(step.opCode)) .append(" ") .append(step.target) .append(","); if (step.eventType == EventType.NodeCreated) { sb.append("y - ") .append(eventPathString(step.eventPath)); } sb.append(","); if (step.eventType == EventType.NodeDeleted) { sb.append("y - ") .append(eventPathString(step.eventPath)); } sb.append(","); if (step.eventType == EventType.NodeDataChanged) { sb.append("y - ") .append(eventPathString(step.eventPath)); } sb.append(","); if (round.watchMode == PERSISTENT_RECURSIVE) { sb.append("n"); } else if (step.eventType == EventType.NodeChildrenChanged) { sb.append("y - ") .append(eventPathString(step.eventPath)); } sb.append("\n"); } sb.append("\n"); } } /** * Generates a "CSV" rendition of the table to standard output. * * @see #ROUNDS */ public static void main(String[] args) { StringBuilder sb = new StringBuilder(); genCsv(sb); System.out.println(sb); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Pers0100644 0000000 0000000 00000000164 15051152474 032710 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentWatcherTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/PersistentWatcherTes0100644 0000000 0000000 00000023337 15051152474 034371 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.AddWatchMode.PERSISTENT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PersistentWatcherTest extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(PersistentWatcherTest.class); private BlockingQueue events; private Watcher persistentWatcher; @Override @BeforeEach public void setUp() throws Exception { super.setUp(); events = new LinkedBlockingQueue<>(); persistentWatcher = event -> events.add(event); } @Test public void testBasic() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT); internalTestBasic(zk); } } @Test public void testNullWatch() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { assertThrows(IllegalArgumentException.class, () -> { zk.addWatch("/a/b", null, PERSISTENT); }); assertThrows(IllegalArgumentException.class, () -> { AsyncCallback.VoidCallback cb = (rc, path, ctx) -> {}; zk.addWatch("/a/b", null, PERSISTENT, cb, null); }); } } @Test public void testDefaultWatcher() throws IOException, InterruptedException, KeeperException { CountdownWatcher watcher = new CountdownWatcher() { @Override public synchronized void process(WatchedEvent event) { super.process(event); events.add(event); } }; try (ZooKeeper zk = createClient(watcher, hostPort)) { zk.addWatch("/a/b", PERSISTENT); events.clear(); // clear any events added during client connection internalTestBasic(zk); } } @Test public void testBasicAsync() throws IOException, InterruptedException, KeeperException { CountdownWatcher watcher = new CountdownWatcher() { @Override public synchronized void process(WatchedEvent event) { super.process(event); events.add(event); } }; try (ZooKeeper zk = createClient(watcher, hostPort)) { final CountDownLatch latch = new CountDownLatch(1); AsyncCallback.VoidCallback cb = (rc, path, ctx) -> { if (rc == KeeperException.Code.OK.intValue()) { latch.countDown(); } }; zk.addWatch("/a/b", persistentWatcher, PERSISTENT, cb, null); assertTrue(latch.await(5, TimeUnit.SECONDS)); events.clear(); // clear any events added during client connection internalTestBasic(zk); } } @Test public void testAsyncDefaultWatcher() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { final CountDownLatch latch = new CountDownLatch(1); AsyncCallback.VoidCallback cb = (rc, path, ctx) -> { if (rc == KeeperException.Code.OK.intValue()) { latch.countDown(); } }; zk.addWatch("/a/b", persistentWatcher, PERSISTENT, cb, null); assertTrue(latch.await(5, TimeUnit.SECONDS)); internalTestBasic(zk); } } private void internalTestBasic(ZooKeeper zk) throws KeeperException, InterruptedException { zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.setData("/a/b", new byte[0], -1); zk.delete("/a/b/c", -1); zk.delete("/a/b", -1); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeChildrenChanged, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeDataChanged, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeChildrenChanged, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeDeleted, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b"); } @Test public void testRemoval() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT); zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEvent(events, Watcher.Event.EventType.NodeCreated, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeChildrenChanged, "/a/b"); zk.removeWatches("/a/b", persistentWatcher, Watcher.WatcherType.Any, false); zk.delete("/a/b/c", -1); zk.delete("/a/b", -1); assertEvent(events, Watcher.Event.EventType.PersistentWatchRemoved, "/a/b"); } } @Test public void testDisconnect() throws Exception { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/a/b", persistentWatcher, PERSISTENT); stopServer(); assertEvent(events, Watcher.Event.EventType.None, null); startServer(); assertEvent(events, Watcher.Event.EventType.None, null); internalTestBasic(zk); } } @Test public void testMultiClient() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk1 = createClient(new CountdownWatcher(), hostPort); ZooKeeper zk2 = createClient(new CountdownWatcher(), hostPort)) { zk1.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.create("/a/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.addWatch("/a/b", persistentWatcher, PERSISTENT); zk1.setData("/a/b", "one".getBytes(), -1); Thread.sleep(1000); // give some time for the event to arrive zk2.setData("/a/b", "two".getBytes(), -1); zk2.setData("/a/b", "three".getBytes(), -1); zk2.setData("/a/b", "four".getBytes(), -1); assertEvent(events, Watcher.Event.EventType.NodeDataChanged, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeDataChanged, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeDataChanged, "/a/b"); assertEvent(events, Watcher.Event.EventType.NodeDataChanged, "/a/b"); } } @Test public void testRootWatcher() throws IOException, InterruptedException, KeeperException { try (ZooKeeper zk = createClient(new CountdownWatcher(), hostPort)) { zk.addWatch("/", persistentWatcher, PERSISTENT); zk.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.setData("/a", new byte[0], -1); zk.create("/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertEvent(events, Watcher.Event.EventType.NodeChildrenChanged, "/"); assertEvent(events, Watcher.Event.EventType.NodeChildrenChanged, "/"); } } private void assertEvent(BlockingQueue events, Watcher.Event.EventType eventType, String path) throws InterruptedException { WatchedEvent event = events.poll(5, TimeUnit.SECONDS); assertNotNull(event); assertEquals(eventType, event.getType()); assertEquals(path, event.getPath()); } }apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumBase.java0100644 0000000 0000000 00000054702 15051152474 033242 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.server.quorum.Election; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumBase extends ClientBase { private static final Logger LOG = LoggerFactory.getLogger(QuorumBase.class); protected static final String LOCALADDR = "127.0.0.1"; private File oracleDir; private static final String oraclePath_0 = "/oraclePath/0/mastership/"; private static final String oraclePath_1 = "/oraclePath/1/mastership/"; private static final String oraclePath_2 = "/oraclePath/0/mastership/"; private static final String oraclePath_3 = "/oraclePath/1/mastership/"; private static final String oraclePath_4 = "/oraclePath/0/mastership/"; private static final String mastership = "value"; File s1dir, s2dir, s3dir, s4dir, s5dir; QuorumPeer s1, s2, s3, s4, s5; protected int port1; protected int port2; protected int port3; protected int port4; protected int port5; protected int portLE1; protected int portLE2; protected int portLE3; protected int portLE4; protected int portLE5; protected int portClient1; protected int portClient2; protected int portClient3; protected int portClient4; protected int portClient5; protected boolean localSessionsEnabled = false; protected boolean localSessionsUpgradingEnabled = false; @BeforeEach @Override public void setUp() throws Exception { setUp(false, true); } protected void setUp(boolean withObservers, boolean withOracle) throws Exception { LOG.info("QuorumBase.setup {}", getTestName()); setupTestEnv(); JMXEnv.setUp(); setUpAll(); port1 = PortAssignment.unique(); port2 = PortAssignment.unique(); port3 = PortAssignment.unique(); port4 = PortAssignment.unique(); port5 = PortAssignment.unique(); portLE1 = PortAssignment.unique(); portLE2 = PortAssignment.unique(); portLE3 = PortAssignment.unique(); portLE4 = PortAssignment.unique(); portLE5 = PortAssignment.unique(); portClient1 = PortAssignment.unique(); portClient2 = PortAssignment.unique(); portClient3 = PortAssignment.unique(); portClient4 = PortAssignment.unique(); portClient5 = PortAssignment.unique(); hostPort = "127.0.0.1:" + portClient1 + ",127.0.0.1:" + portClient2 + ",127.0.0.1:" + portClient3 + ",127.0.0.1:" + portClient4 + ",127.0.0.1:" + portClient5; LOG.info("Ports are: {}", hostPort); s1dir = ClientBase.createTmpDir(); s2dir = ClientBase.createTmpDir(); s3dir = ClientBase.createTmpDir(); s4dir = ClientBase.createTmpDir(); s5dir = ClientBase.createTmpDir(); startServers(withObservers, withOracle); OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { LOG.info("Initial fdcount is: {}", osMbean.getOpenFileDescriptorCount()); } LOG.info("Setup finished"); } private void createOraclePath() throws IOException { oracleDir = ClientBase.createTmpDir(); File directory = new File(oracleDir, oraclePath_0); directory.mkdirs(); FileWriter fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_0 + mastership); fw.write("1"); fw.close(); directory = new File(oracleDir, oraclePath_1); directory.mkdirs(); fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_1 + mastership); fw.write("0"); fw.close(); directory = new File(oracleDir, oraclePath_2); directory.mkdirs(); fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_2 + mastership); fw.write("0"); fw.close(); directory = new File(oracleDir, oraclePath_3); directory.mkdirs(); fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_3 + mastership); fw.write("1"); fw.close(); directory = new File(oracleDir, oraclePath_4); directory.mkdirs(); fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_4 + mastership); fw.write("0"); fw.close(); } void startServers() throws Exception { startServers(false, true); } void startServers(boolean withObservers, boolean withOracle) throws Exception { int tickTime = 2000; int initLimit = 3; int syncLimit = 3; int connectToLearnerMasterLimit = 3; Map peers = new HashMap<>(); peers.put(Long.valueOf(1), new QuorumServer(1, new InetSocketAddress(LOCALADDR, port1), new InetSocketAddress(LOCALADDR, portLE1), new InetSocketAddress(LOCALADDR, portClient1), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(2), new QuorumServer(2, new InetSocketAddress(LOCALADDR, port2), new InetSocketAddress(LOCALADDR, portLE2), new InetSocketAddress(LOCALADDR, portClient2), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(3), new QuorumServer(3, new InetSocketAddress(LOCALADDR, port3), new InetSocketAddress(LOCALADDR, portLE3), new InetSocketAddress(LOCALADDR, portClient3), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(4), new QuorumServer(4, new InetSocketAddress(LOCALADDR, port4), new InetSocketAddress(LOCALADDR, portLE4), new InetSocketAddress(LOCALADDR, portClient4), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(5), new QuorumServer(5, new InetSocketAddress(LOCALADDR, port5), new InetSocketAddress(LOCALADDR, portLE5), new InetSocketAddress(LOCALADDR, portClient5), LearnerType.PARTICIPANT)); if (withObservers) { peers.get(Long.valueOf(4)).type = LearnerType.OBSERVER; peers.get(Long.valueOf(5)).type = LearnerType.OBSERVER; } if (!withOracle) { LOG.info("creating QuorumPeer 1 port {}", portClient1); s1 = new QuorumPeer(peers, s1dir, s1dir, portClient1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient1, s1.getClientPort()); LOG.info("creating QuorumPeer 2 port {}", portClient2); s2 = new QuorumPeer(peers, s2dir, s2dir, portClient2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient2, s2.getClientPort()); LOG.info("creating QuorumPeer 3 port {}", portClient3); s3 = new QuorumPeer(peers, s3dir, s3dir, portClient3, 3, 3, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient3, s3.getClientPort()); LOG.info("creating QuorumPeer 4 port {}", portClient4); s4 = new QuorumPeer(peers, s4dir, s4dir, portClient4, 3, 4, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient4, s4.getClientPort()); LOG.info("creating QuorumPeer 5 port {}", portClient5); s5 = new QuorumPeer(peers, s5dir, s5dir, portClient5, 3, 5, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient5, s5.getClientPort()); } else { createOraclePath(); LOG.info("creating QuorumPeer 1 port {}", portClient1); s1 = new QuorumPeer(peers, s1dir, s1dir, portClient1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_0 + mastership); assertEquals(portClient1, s1.getClientPort()); LOG.info("creating QuorumPeer 2 port {}", portClient2); s2 = new QuorumPeer(peers, s2dir, s2dir, portClient2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_1 + mastership); assertEquals(portClient2, s2.getClientPort()); LOG.info("creating QuorumPeer 3 port {}", portClient3); s3 = new QuorumPeer(peers, s3dir, s3dir, portClient3, 3, 3, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_2 + mastership); assertEquals(portClient3, s3.getClientPort()); LOG.info("creating QuorumPeer 4 port {}", portClient4); s4 = new QuorumPeer(peers, s4dir, s4dir, portClient4, 3, 4, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_3 + mastership); assertEquals(portClient4, s4.getClientPort()); LOG.info("creating QuorumPeer 5 port {}", portClient5); s5 = new QuorumPeer(peers, s5dir, s5dir, portClient5, 3, 5, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_4 + mastership); assertEquals(portClient5, s5.getClientPort()); } if (withObservers) { s4.setLearnerType(LearnerType.OBSERVER); s5.setLearnerType(LearnerType.OBSERVER); } LOG.info("QuorumPeer 1 voting view: {}", s1.getVotingView()); LOG.info("QuorumPeer 2 voting view: {}", s2.getVotingView()); LOG.info("QuorumPeer 3 voting view: {}", s3.getVotingView()); LOG.info("QuorumPeer 4 voting view: {}", s4.getVotingView()); LOG.info("QuorumPeer 5 voting view: {}", s5.getVotingView()); s1.enableLocalSessions(localSessionsEnabled); s2.enableLocalSessions(localSessionsEnabled); s3.enableLocalSessions(localSessionsEnabled); s4.enableLocalSessions(localSessionsEnabled); s5.enableLocalSessions(localSessionsEnabled); s1.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); s2.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); s3.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); s4.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); s5.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); LOG.info("start QuorumPeer 1"); s1.start(); LOG.info("start QuorumPeer 2"); s2.start(); LOG.info("start QuorumPeer 3"); s3.start(); LOG.info("start QuorumPeer 4"); s4.start(); LOG.info("start QuorumPeer 5"); s5.start(); LOG.info("started QuorumPeer 5"); LOG.info("Checking ports {}", hostPort); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerUp(hp, CONNECTION_TIMEOUT), "waiting for server up"); LOG.info("{} is accepting client connections", hp); } // interesting to see what's there... JMXEnv.dump(); // make sure we have these 5 servers listed Set ensureNames = new LinkedHashSet<>(); for (int i = 1; i <= 5; i++) { ensureNames.add("InMemoryDataTree"); } for (int i = 1; i <= 5; i++) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + i + ",name2="); } for (int i = 1; i <= 5; i++) { for (int j = 1; j <= 5; j++) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + j); } } for (int i = 1; i <= 5; i++) { ensureNames.add("name0=ReplicatedServer_id" + i); } JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()])); } public int getLeaderIndex() { if (s1.getPeerState() == ServerState.LEADING) { return 0; } else if (s2.getPeerState() == ServerState.LEADING) { return 1; } else if (s3.getPeerState() == ServerState.LEADING) { return 2; } else if (s4.getPeerState() == ServerState.LEADING) { return 3; } else if (s5.getPeerState() == ServerState.LEADING) { return 4; } return -1; } public int getLeaderClientPort() { if (s1.getPeerState() == ServerState.LEADING) { return portClient1; } else if (s2.getPeerState() == ServerState.LEADING) { return portClient2; } else if (s3.getPeerState() == ServerState.LEADING) { return portClient3; } else if (s4.getPeerState() == ServerState.LEADING) { return portClient4; } else if (s5.getPeerState() == ServerState.LEADING) { return portClient5; } return -1; } public QuorumPeer getLeaderQuorumPeer() { if (s1.getPeerState() == ServerState.LEADING) { return s1; } else if (s2.getPeerState() == ServerState.LEADING) { return s2; } else if (s3.getPeerState() == ServerState.LEADING) { return s3; } else if (s4.getPeerState() == ServerState.LEADING) { return s4; } else if (s5.getPeerState() == ServerState.LEADING) { return s5; } return null; } public File getLeaderDataDir() { if (s1.getPeerState() == ServerState.LEADING) { return s1dir; } else if (s2.getPeerState() == ServerState.LEADING) { return s2dir; } else if (s3.getPeerState() == ServerState.LEADING) { return s3dir; } else if (s4.getPeerState() == ServerState.LEADING) { return s4dir; } else if (s5.getPeerState() == ServerState.LEADING) { return s5dir; } return null; } public QuorumPeer getFirstObserver() { if (s1.getLearnerType() == LearnerType.OBSERVER) { return s1; } else if (s2.getLearnerType() == LearnerType.OBSERVER) { return s2; } else if (s3.getLearnerType() == LearnerType.OBSERVER) { return s3; } else if (s4.getLearnerType() == LearnerType.OBSERVER) { return s4; } else if (s5.getLearnerType() == LearnerType.OBSERVER) { return s5; } return null; } public int getFirstObserverClientPort() { if (s1.getLearnerType() == LearnerType.OBSERVER) { return portClient1; } else if (s2.getLearnerType() == LearnerType.OBSERVER) { return portClient2; } else if (s3.getLearnerType() == LearnerType.OBSERVER) { return portClient3; } else if (s4.getLearnerType() == LearnerType.OBSERVER) { return portClient4; } else if (s5.getLearnerType() == LearnerType.OBSERVER) { return portClient5; } return -1; } public String getPeersMatching(ServerState state) { StringBuilder hosts = new StringBuilder(); for (QuorumPeer p : getPeerList()) { if (p.getPeerState() == state) { hosts.append(String.format("%s:%d,", LOCALADDR, p.getClientAddress().getPort())); } } LOG.info("getPeersMatching ports are {}", hosts); return hosts.toString(); } public ArrayList getPeerList() { ArrayList peers = new ArrayList<>(); peers.add(s1); peers.add(s2); peers.add(s3); peers.add(s4); peers.add(s5); return peers; } public QuorumPeer getPeerByClientPort(int clientPort) { for (QuorumPeer p : getPeerList()) { if (p.getClientAddress().getPort() == clientPort) { return p; } } return null; } public void setupServers() throws IOException { setupServer(1); setupServer(2); setupServer(3); setupServer(4); setupServer(5); } Map peers = null; public void setupServer(int i) throws IOException { int tickTime = 2000; int initLimit = 3; int syncLimit = 3; int connectToLearnerMasterLimit = 3; if (peers == null) { peers = new HashMap<>(); peers.put(Long.valueOf(1), new QuorumServer(1, new InetSocketAddress(LOCALADDR, port1), new InetSocketAddress(LOCALADDR, portLE1), new InetSocketAddress(LOCALADDR, portClient1), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(2), new QuorumServer(2, new InetSocketAddress(LOCALADDR, port2), new InetSocketAddress(LOCALADDR, portLE2), new InetSocketAddress(LOCALADDR, portClient2), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(3), new QuorumServer(3, new InetSocketAddress(LOCALADDR, port3), new InetSocketAddress(LOCALADDR, portLE3), new InetSocketAddress(LOCALADDR, portClient3), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(4), new QuorumServer(4, new InetSocketAddress(LOCALADDR, port4), new InetSocketAddress(LOCALADDR, portLE4), new InetSocketAddress(LOCALADDR, portClient4), LearnerType.PARTICIPANT)); peers.put(Long.valueOf(5), new QuorumServer(5, new InetSocketAddress(LOCALADDR, port5), new InetSocketAddress(LOCALADDR, portLE5), new InetSocketAddress(LOCALADDR, portClient5), LearnerType.PARTICIPANT)); } switch (i) { case 1: LOG.info("creating QuorumPeer 1 port {}", portClient1); s1 = new QuorumPeer(peers, s1dir, s1dir, portClient1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient1, s1.getClientPort()); break; case 2: LOG.info("creating QuorumPeer 2 port {}", portClient2); s2 = new QuorumPeer(peers, s2dir, s2dir, portClient2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient2, s2.getClientPort()); break; case 3: LOG.info("creating QuorumPeer 3 port {}", portClient3); s3 = new QuorumPeer(peers, s3dir, s3dir, portClient3, 3, 3, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient3, s3.getClientPort()); break; case 4: LOG.info("creating QuorumPeer 4 port {}", portClient4); s4 = new QuorumPeer(peers, s4dir, s4dir, portClient4, 3, 4, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient4, s4.getClientPort()); break; case 5: LOG.info("creating QuorumPeer 5 port {}", portClient5); s5 = new QuorumPeer(peers, s5dir, s5dir, portClient5, 3, 5, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient5, s5.getClientPort()); } } @AfterEach @Override public void tearDown() throws Exception { LOG.info("TearDown started"); if (oracleDir != null) { ClientBase.recursiveDelete(oracleDir); } OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { LOG.info("fdcount after test is: {}", osMbean.getOpenFileDescriptorCount()); } shutdownServers(); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerDown(hp, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); LOG.info("{} is no longer accepting client connections", hp); } JMXEnv.tearDown(); } public void shutdownServers() { shutdown(s1); shutdown(s2); shutdown(s3); shutdown(s4); shutdown(s5); } public static void shutdown(QuorumPeer qp) { if (qp == null) { return; } try { LOG.info("Shutting down quorum peer {}", qp.getName()); qp.shutdown(); Election e = qp.getElectionAlg(); if (e != null) { LOG.info("Shutting down leader election {}", qp.getName()); e.shutdown(); } else { LOG.info("No election available to shutdown {}", qp.getName()); } LOG.info("Waiting for {} to exit thread", qp.getName()); long readTimeout = qp.getTickTime() * qp.getInitLimit(); long connectTimeout = qp.getTickTime() * qp.getSyncLimit(); long maxTimeout = Math.max(readTimeout, connectTimeout); maxTimeout = Math.max(maxTimeout, ClientBase.CONNECTION_TIMEOUT); qp.join(maxTimeout * 2); if (qp.isAlive()) { fail("QP failed to shutdown in " + (maxTimeout * 2) + " seconds: " + qp.getName()); } } catch (InterruptedException e) { LOG.debug("QP interrupted: {}", qp.getName(), e); } } protected TestableZooKeeper createClient() throws IOException, InterruptedException { return createClient(hostPort); } protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { CountdownWatcher watcher = new CountdownWatcher(); return createClient(watcher, hp); } protected TestableZooKeeper createClient(CountdownWatcher watcher, ServerState state) throws IOException, InterruptedException { return createClient(watcher, getPeersMatching(state)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000166 15051152474 032727 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumBaseOracle_2Nodes.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumBaseOracle_2No0100644 0000000 0000000 00000031702 15051152474 034161 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.server.quorum.Election; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.util.OSMXBean; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumBaseOracle_2Nodes extends ClientBase{ private static final Logger LOG = LoggerFactory.getLogger(QuorumBase.class); private static final String LOCALADDR = "127.0.0.1"; private File oracleDir; private static String oraclePath_0 = "/oraclePath/0/mastership/"; private static String oraclePath_1 = "/oraclePath/1/mastership/"; private static final String mastership = "value"; File s1dir, s2dir; QuorumPeer s1, s2; protected int port1; protected int port2; protected int portLE1; protected int portLE2; protected int portClient1; protected int portClient2; protected boolean localSessionsEnabled = false; protected boolean localSessionsUpgradingEnabled = false; @BeforeEach @Override public void setUp() throws Exception { LOG.info("QuorumBase.setup {}", getTestName()); setupTestEnv(); JMXEnv.setUp(); setUpAll(); port1 = PortAssignment.unique(); port2 = PortAssignment.unique(); portLE1 = PortAssignment.unique(); portLE2 = PortAssignment.unique(); portClient1 = PortAssignment.unique(); portClient2 = PortAssignment.unique(); hostPort = "127.0.0.1:" + portClient1 + ",127.0.0.1:" + portClient2; LOG.info("Ports are: {}", hostPort); s1dir = ClientBase.createTmpDir(); s2dir = ClientBase.createTmpDir(); createOraclePath(); startServers(); OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { LOG.info("Initial fdcount is: {}", osMbean.getOpenFileDescriptorCount()); } LOG.info("Setup finished"); } private void createOraclePath() throws IOException { oracleDir = ClientBase.createTmpDir(); File directory = new File(oracleDir, oraclePath_0); directory.mkdirs(); FileWriter fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_0 + mastership); fw.write("0"); fw.close(); directory = new File(oracleDir, oraclePath_1); directory.mkdirs(); fw = new FileWriter(oracleDir.getAbsolutePath() + oraclePath_1 + mastership); fw.write("1"); fw.close(); } void startServers() throws Exception { int tickTime = 2000; int initLimit = 3; int syncLimit = 3; int connectToLearnerMasterLimit = 3; Map peers = new HashMap<>(); peers.put(Long.valueOf(1), new QuorumPeer.QuorumServer(1, new InetSocketAddress(LOCALADDR, port1), new InetSocketAddress(LOCALADDR, portLE1), new InetSocketAddress(LOCALADDR, portClient1), QuorumPeer.LearnerType.PARTICIPANT)); peers.put(Long.valueOf(2), new QuorumPeer.QuorumServer(2, new InetSocketAddress(LOCALADDR, port2), new InetSocketAddress(LOCALADDR, portLE2), new InetSocketAddress(LOCALADDR, portClient2), QuorumPeer.LearnerType.PARTICIPANT)); LOG.info("creating QuorumPeer 1 port {}", portClient1); s1 = new QuorumPeer(peers, s1dir, s1dir, portClient1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_0 + mastership); assertEquals(portClient1, s1.getClientPort()); LOG.info("creating QuorumPeer 2 port {}", portClient2); s2 = new QuorumPeer(peers, s2dir, s2dir, portClient2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit, oracleDir .getAbsolutePath() + oraclePath_1 + mastership); assertEquals(portClient2, s2.getClientPort()); LOG.info("QuorumPeer 1 voting view: {}", s1.getVotingView()); LOG.info("QuorumPeer 2 voting view: {}", s2.getVotingView()); s1.enableLocalSessions(localSessionsEnabled); s2.enableLocalSessions(localSessionsEnabled); s1.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); s2.enableLocalSessionsUpgrading(localSessionsUpgradingEnabled); LOG.info("start QuorumPeer 1"); s1.start(); LOG.info("start QuorumPeer 2"); s2.start(); LOG.info("Checking ports {}", hostPort); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerUp(hp, CONNECTION_TIMEOUT), "waiting for server up"); LOG.info("{} is accepting client connections", hp); } // interesting to see what's there... JMXEnv.dump(); // make sure we have these 5 servers listed Set ensureNames = new LinkedHashSet<>(); for (int i = 1; i <= 2; i++) { ensureNames.add("InMemoryDataTree"); } for (int i = 1; i <= 2; i++) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + i + ",name2="); } for (int i = 1; i <= 2; i++) { for (int j = 1; j <= 2; j++) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + j); } } for (int i = 1; i <= 2; i++) { ensureNames.add("name0=ReplicatedServer_id" + i); } JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()])); } public int getLeaderIndex() { if (s1.getPeerState() == QuorumPeer.ServerState.LEADING) { return 0; } else if (s2.getPeerState() == QuorumPeer.ServerState.LEADING) { return 1; } return -1; } public int getLeaderClientPort() { if (s1.getPeerState() == QuorumPeer.ServerState.LEADING) { return portClient1; } else if (s2.getPeerState() == QuorumPeer.ServerState.LEADING) { return portClient2; } return -1; } public QuorumPeer getLeaderQuorumPeer() { if (s1.getPeerState() == QuorumPeer.ServerState.LEADING) { return s1; } else if (s2.getPeerState() == QuorumPeer.ServerState.LEADING) { return s2; } return null; } public QuorumPeer getFirstObserver() { if (s1.getLearnerType() == QuorumPeer.LearnerType.OBSERVER) { return s1; } else if (s2.getLearnerType() == QuorumPeer.LearnerType.OBSERVER) { return s2; } return null; } public int getFirstObserverClientPort() { if (s1.getLearnerType() == QuorumPeer.LearnerType.OBSERVER) { return portClient1; } else if (s2.getLearnerType() == QuorumPeer.LearnerType.OBSERVER) { return portClient2; } return -1; } public String getPeersMatching(QuorumPeer.ServerState state) { StringBuilder hosts = new StringBuilder(); for (QuorumPeer p : getPeerList()) { if (p.getPeerState() == state) { hosts.append(String.format("%s:%d,", LOCALADDR, p.getClientAddress().getPort())); } } LOG.info("getPeersMatching ports are {}", hosts); return hosts.toString(); } public ArrayList getPeerList() { ArrayList peers = new ArrayList<>(); peers.add(s1); peers.add(s2); return peers; } public QuorumPeer getPeerByClientPort(int clientPort) { for (QuorumPeer p : getPeerList()) { if (p.getClientAddress().getPort() == clientPort) { return p; } } return null; } public void setupServers() throws IOException { setupServer(1); setupServer(2); } Map peers = null; public void setupServer(int i) throws IOException { int tickTime = 2000; int initLimit = 3; int syncLimit = 3; int connectToLearnerMasterLimit = 3; if (peers == null) { peers = new HashMap<>(); peers.put(Long.valueOf(1), new QuorumPeer.QuorumServer(1, new InetSocketAddress(LOCALADDR, port1), new InetSocketAddress(LOCALADDR, portLE1), new InetSocketAddress(LOCALADDR, portClient1), QuorumPeer.LearnerType.PARTICIPANT)); peers.put(Long.valueOf(2), new QuorumPeer.QuorumServer(2, new InetSocketAddress(LOCALADDR, port2), new InetSocketAddress(LOCALADDR, portLE2), new InetSocketAddress(LOCALADDR, portClient2), QuorumPeer.LearnerType.PARTICIPANT)); } switch (i) { case 1: LOG.info("creating QuorumPeer 1 port {}", portClient1); s1 = new QuorumPeer(peers, s1dir, s1dir, portClient1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient1, s1.getClientPort()); break; case 2: LOG.info("creating QuorumPeer 2 port {}", portClient2); s2 = new QuorumPeer(peers, s2dir, s2dir, portClient2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(portClient2, s2.getClientPort()); break; } } @AfterEach @Override public void tearDown() throws Exception { LOG.info("TearDown started"); if (oracleDir != null) { ClientBase.recursiveDelete(oracleDir); } OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { LOG.info("fdcount after test is: {}", osMbean.getOpenFileDescriptorCount()); } shutdownServers(); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerDown(hp, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); LOG.info("{} is no longer accepting client connections", hp); } JMXEnv.tearDown(); } public void shutdownServers() { shutdown(s1); shutdown(s2); } public static void shutdown(QuorumPeer qp) { if (qp == null) { return; } try { LOG.info("Shutting down quorum peer {}", qp.getName()); qp.shutdown(); Election e = qp.getElectionAlg(); if (e != null) { LOG.info("Shutting down leader election {}", qp.getName()); e.shutdown(); } else { LOG.info("No election available to shutdown {}", qp.getName()); } LOG.info("Waiting for {} to exit thread", qp.getName()); long readTimeout = qp.getTickTime() * qp.getInitLimit(); long connectTimeout = qp.getTickTime() * qp.getSyncLimit(); long maxTimeout = Math.max(readTimeout, connectTimeout); maxTimeout = Math.max(maxTimeout, ClientBase.CONNECTION_TIMEOUT); qp.join(maxTimeout * 2); if (qp.isAlive()) { fail("QP failed to shutdown in " + (maxTimeout * 2) + " seconds: " + qp.getName()); } } catch (InterruptedException e) { LOG.debug("QP interrupted: {}", qp.getName(), e); } } protected TestableZooKeeper createClient() throws IOException, InterruptedException { return createClient(hostPort); } protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { ClientBase.CountdownWatcher watcher = new ClientBase.CountdownWatcher(); return createClient(watcher, hp); } protected TestableZooKeeper createClient(ClientBase.CountdownWatcher watcher, QuorumPeer.ServerState state) throws IOException, InterruptedException { return createClient(watcher, getPeersMatching(state)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000157 15051152474 032727 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumHammerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumHammerTest.jav0100644 0000000 0000000 00000003351 15051152474 034272 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.ZKTestCase; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumHammerTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(QuorumHammerTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; protected final QuorumBase qb = new QuorumBase(); protected final ClientHammerTest cht = new ClientHammerTest(); @BeforeEach public void setUp() throws Exception { qb.setUp(); cht.hostPort = qb.hostPort; cht.setUpAll(); } @AfterEach public void tearDown() throws Exception { cht.tearDownAll(); qb.tearDown(); } @Test public void testHammerBasic() throws Throwable { cht.testHammerBasic(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000161 15051152474 032722 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumMajorityTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumMajorityTest.j0100644 0000000 0000000 00000010135 15051152474 034326 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumMajorityTest extends QuorumBase { protected static final Logger LOG = LoggerFactory.getLogger(QuorumMajorityTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; /***************************************************************/ /* Test that the majority quorum verifier only counts votes from */ /* followers in its view */ /***************************************************************/ @Test public void testMajQuorums() throws Throwable { LOG.info("Verify QuorumPeer#electionTimeTaken jmx bean attribute"); ArrayList peers = getPeerList(); for (int i = 1; i <= peers.size(); i++) { QuorumPeer qp = peers.get(i - 1); Long electionTimeTaken = -1L; String bean = ""; if (qp.getPeerState() == ServerState.FOLLOWING) { bean = String.format("%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Follower", MBeanRegistry.DOMAIN, i, i); } else if (qp.getPeerState() == ServerState.LEADING) { bean = String.format("%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Leader", MBeanRegistry.DOMAIN, i, i); } electionTimeTaken = (Long) JMXEnv.ensureBeanAttribute(bean, "ElectionTimeTaken"); assertTrue(electionTimeTaken >= 0, "Wrong electionTimeTaken value!"); } //setup servers 1-5 to be followers setUp(false, true); Proposal p = new Proposal(); p.addQuorumVerifier(s1.getQuorumVerifier()); // 2 followers out of 5 is not a majority p.addAck(Long.valueOf(1)); p.addAck(Long.valueOf(2)); assertEquals(false, p.hasAllQuorums()); // 6 is not in the view - its vote shouldn't count p.addAck(Long.valueOf(6)); assertEquals(false, p.hasAllQuorums()); // 3 followers out of 5 are a majority of the voting view p.addAck(Long.valueOf(3)); assertEquals(true, p.hasAllQuorums()); //setup servers 1-3 to be followers and 4 and 5 to be observers setUp(true, true); p = new Proposal(); p.addQuorumVerifier(s1.getQuorumVerifier()); // 1 follower out of 3 is not a majority p.addAck(Long.valueOf(1)); assertEquals(false, p.hasAllQuorums()); // 4 and 5 are observers, their vote shouldn't count p.addAck(Long.valueOf(4)); p.addAck(Long.valueOf(5)); assertEquals(false, p.hasAllQuorums()); // 6 is not in the view - its vote shouldn't count p.addAck(Long.valueOf(6)); assertEquals(false, p.hasAllQuorums()); // 2 followers out of 3 are a majority of the voting view p.addAck(Long.valueOf(2)); assertEquals(true, p.hasAllQuorums()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000162 15051152474 032723 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumOracleMajTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumOracleMajTest.0100644 0000000 0000000 00000010733 15051152474 034217 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumOracleMajTest extends QuorumBaseOracle_2Nodes { protected static final Logger LOG = LoggerFactory.getLogger(QuorumMajorityTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; /***************************************************************/ /* Test that the majority quorum verifier only counts votes from */ /* followers in its view */ /***************************************************************/ @Test public void testMajQuorums() throws Throwable { LOG.info("Verify QuorumPeer#electionTimeTaken jmx bean attribute"); ArrayList peers = getPeerList(); for (int i = 1; i <= peers.size(); i++) { QuorumPeer qp = peers.get(i - 1); Long electionTimeTaken = -1L; String bean = ""; if (qp.getPeerState() == QuorumPeer.ServerState.FOLLOWING) { bean = String.format("%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Follower", MBeanRegistry.DOMAIN, i, i); } else if (qp.getPeerState() == QuorumPeer.ServerState.LEADING) { bean = String.format("%s:name0=ReplicatedServer_id%d,name1=replica.%d,name2=Leader", MBeanRegistry.DOMAIN, i, i); } electionTimeTaken = (Long) JMXEnv.ensureBeanAttribute(bean, "ElectionTimeTaken"); assertTrue(electionTimeTaken >= 0, "Wrong electionTimeTaken value!"); } tearDown(); //setup servers 1-2 to be followers // id=1, oracle is false; id=2, oracle is true setUp(); QuorumPeer s; int leader; if ((leader = getLeaderIndex()) == 1) { s = s1; } else { s = s2; } noDropConectionTest(s); dropConnectionTest(s, leader); } private void noDropConectionTest(QuorumPeer s) { Leader.Proposal p = new Leader.Proposal(); p.addQuorumVerifier(s.getQuorumVerifier()); // 1 followers out of 2 is not a majority p.addAck(Long.valueOf(1)); assertEquals(false, p.hasAllQuorums()); // 6 is not in the view - its vote shouldn't count p.addAck(Long.valueOf(6)); assertEquals(false, p.hasAllQuorums()); // 2 followers out of 2 is good p.addAck(Long.valueOf(2)); assertEquals(true, p.hasAllQuorums()); } private void dropConnectionTest(QuorumPeer s, int leader) { Leader.Proposal p = new Leader.Proposal(); p.addQuorumVerifier(s.getQuorumVerifier()); ArrayList fake = new ArrayList<>(); LearnerHandler f = null; fake.add(f); s.getQuorumVerifier().updateNeedOracle(fake); // still have valid followers, the oracle should not take place assertEquals(false, s.getQuorumVerifier().getNeedOracle()); fake.remove(0); s.getQuorumVerifier().updateNeedOracle(fake); // lose all of followers, the oracle should take place assertEquals(true, s.getQuorumVerifier().getNeedOracle()); // when leader is 1, we expect false. // when leader is 2, we expect true. assertEquals(leader != 1, p.hasAllQuorums()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000156 15051152474 032726 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumQuotaTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumQuotaTest.java0100644 0000000 0000000 00000004666 15051152474 034325 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.cli.SetQuotaCommand; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; public class QuorumQuotaTest extends QuorumBase { @Test public void testQuotaWithQuorum() throws Exception { ZooKeeper zk = createClient(); zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int i = 0; for (i = 0; i < 300; i++) { zk.create("/a/" + i, "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } StatsTrack quota = new StatsTrack(); quota.setCount(1000); quota.setBytes(5000); SetQuotaCommand.createQuota(zk, "/a", quota); String statPath = Quotas.statPath("/a"); byte[] data = zk.getData(statPath, false, new Stat()); StatsTrack st = new StatsTrack(data); assertTrue(st.getBytes() == 1204L, "bytes are set"); assertTrue(st.getCount() == 301, "num count is set"); for (i = 300; i < 600; i++) { zk.create("/a/" + i, "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } data = zk.getData(statPath, false, new Stat()); st = new StatsTrack(data); assertTrue(st.getBytes() == 2404L, "bytes are set"); assertTrue(st.getCount() == 601, "num count is set"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000160 15051152474 032721 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumRestartTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumRestartTest.ja0100644 0000000 0000000 00000012375 15051152474 034325 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumRestartTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(QuorumRestartTest.class); private QuorumUtil qu; @BeforeEach public void setUp() throws Exception { System.setProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); // starting a 3 node ensemble without observers qu = new QuorumUtil(1, 2); qu.startAll(); } /** * A basic test for rolling restart. We are restarting the ZooKeeper servers one by one, * starting from the first server. We always make sure that all the nodes joined to the * Quorum before moving forward. * @throws Exception */ @Test public void testRollingRestart() throws Exception { for (int serverToRestart = 1; serverToRestart <= 3; serverToRestart++) { LOG.info("***** restarting: " + serverToRestart); qu.shutdown(serverToRestart); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + qu.getPeer(serverToRestart).clientPort, ClientBase.CONNECTION_TIMEOUT), String.format("Timeout during waiting for server %d to go down", serverToRestart)); qu.restart(serverToRestart); final String errorMessage = "Not all the quorum members are connected after restarting server " + serverToRestart; waitFor(errorMessage, () -> qu.allPeersAreConnected(), 30); LOG.info("***** Restart {} succeeded", serverToRestart); } } /** * Testing one of the errors reported in ZOOKEEPER-2164, when some servers can not * rejoin to the Quorum after restarting the servers backwards * @throws Exception */ @Test public void testRollingRestartBackwards() throws Exception { for (int serverToRestart = 3; serverToRestart >= 1; serverToRestart--) { LOG.info("***** restarting: " + serverToRestart); qu.shutdown(serverToRestart); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + qu.getPeer(serverToRestart).clientPort, ClientBase.CONNECTION_TIMEOUT), String.format("Timeout during waiting for server %d to go down", serverToRestart)); qu.restart(serverToRestart); final String errorMessage = "Not all the quorum members are connected after restarting server " + serverToRestart; waitFor(errorMessage, () -> qu.allPeersAreConnected(), 30); LOG.info("***** Restart {} succeeded", serverToRestart); } } /** * Testing one of the errors reported in ZOOKEEPER-2164, when some servers can not * rejoin to the Quorum after restarting the current leader multiple times * @throws Exception */ @Test public void testRestartingLeaderMultipleTimes() throws Exception { for (int restartCount = 1; restartCount <= 3; restartCount++) { int leaderId = qu.getLeaderServer(); LOG.info("***** new leader: " + leaderId); qu.shutdown(leaderId); assertTrue(ClientBase.waitForServerDown("127.0.0.1:" + qu.getPeer(leaderId).clientPort, ClientBase.CONNECTION_TIMEOUT), "Timeout during waiting for current leader to go down"); String errorMessage = "No new leader was elected"; waitFor(errorMessage, () -> qu.leaderExists() && qu.getLeaderServer() != leaderId, 30); qu.restart(leaderId); errorMessage = "Not all the quorum members are connected after restarting the old leader"; waitFor(errorMessage, () -> qu.allPeersAreConnected(), 30); LOG.info("***** Leader Restart {} succeeded", restartCount); } } @AfterEach public void tearDown() throws Exception { qu.shutdownAll(); System.clearProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumTest.java0100644 0000000 0000000 00000034332 15051152474 033304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Op; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.quorum.Leader; import org.apache.zookeeper.server.quorum.LearnerHandler; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class QuorumTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(QuorumTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; private final QuorumBase qb = new QuorumBase(); private final ClientTest ct = new ClientTest(); private QuorumUtil qu; @BeforeEach public void setUp() throws Exception { qb.setUp(); ct.hostPort = qb.hostPort; ct.setUpAll(); } @AfterEach public void tearDown() throws Exception { ct.tearDownAll(); qb.tearDown(); if (qu != null) { qu.tearDown(); } } @Test public void testDeleteWithChildren() throws Exception { ct.testDeleteWithChildren(); } @Test public void testPing() throws Exception { ct.testPing(); } @Test public void testSequentialNodeNames() throws IOException, InterruptedException, KeeperException { ct.testSequentialNodeNames(); } @Test public void testACLs() throws Exception { ct.testACLs(); } @Test public void testClientwithoutWatcherObj() throws IOException, InterruptedException, KeeperException { ct.testClientwithoutWatcherObj(); } @Test public void testClientWithWatcherObj() throws IOException, InterruptedException, KeeperException { ct.testClientWithWatcherObj(); } @Test public void testGetView() { assertEquals(5, qb.s1.getView().size()); assertEquals(5, qb.s2.getView().size()); assertEquals(5, qb.s3.getView().size()); assertEquals(5, qb.s4.getView().size()); assertEquals(5, qb.s5.getView().size()); } @Test public void testViewContains() { // Test view contains self assertTrue(qb.s1.viewContains(qb.s1.getMyId())); // Test view contains other servers assertTrue(qb.s1.viewContains(qb.s2.getMyId())); // Test view does not contain non-existant servers assertFalse(qb.s1.viewContains(-1L)); } volatile int counter = 0; volatile int errors = 0; @Test public void testLeaderShutdown() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = new DisconnectableZooKeeper( qb.hostPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zk.create("/blah", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/blah/blah", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Leader leader = qb.s1.leader; if (leader == null) { leader = qb.s2.leader; } if (leader == null) { leader = qb.s3.leader; } if (leader == null) { leader = qb.s4.leader; } if (leader == null) { leader = qb.s5.leader; } assertNotNull(leader); for (int i = 0; i < 5000; i++) { zk.setData("/blah/blah", new byte[0], -1, (rc, path, ctx, stat) -> { counter++; if (rc != 0) { errors++; } }, null); } for (LearnerHandler f : leader.getForwardingFollowers()) { f.getSocket().shutdownInput(); } for (int i = 0; i < 5000; i++) { zk.setData("/blah/blah", new byte[0], -1, (rc, path, ctx, stat) -> { counter++; if (rc != 0) { errors++; } }, null); } // check if all the followers are alive assertTrue(qb.s1.isAlive()); assertTrue(qb.s2.isAlive()); assertTrue(qb.s3.isAlive()); assertTrue(qb.s4.isAlive()); assertTrue(qb.s5.isAlive()); zk.close(); } @Test public void testMultipleWatcherObjs() throws IOException, InterruptedException, KeeperException { ct.testMutipleWatcherObjs(); } /** * Make sure that we can change sessions among servers and maintain consistent view * using {@link ZooKeeper#sync(String)}. */ @Test public void testSessionMovedWithSynchronousSync() throws Exception { testSessionMoved(true); } /** * Make sure that we can change sessions among servers and maintain consistent view * using {@link ZooKeeper#sync(String, AsyncCallback.VoidCallback, Object)}. */ @Test public void testSessionMovedWithAsynchronousSync() throws Exception { testSessionMoved(false); } public void testSessionMoved(boolean synchronous_sync) throws Exception { String[] hostPorts = qb.hostPort.split(","); DisconnectableZooKeeper zk = new DisconnectableZooKeeper( hostPorts[0], ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zk.create("/sessionMoveTest", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); // we want to loop through the list twice for (int i = 0; i < hostPorts.length * 2; i++) { zk.dontReconnect(); // This should stomp the zk handle DisconnectableZooKeeper zknew = new DisconnectableZooKeeper( hostPorts[(i + 1) % hostPorts.length], ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE, zk.getSessionId(), zk.getSessionPasswd()); zknew.setData("/", new byte[1], -1); syncClient(zknew, synchronous_sync); LOG.info("{} Sync succeed", hostPorts[(i + 1) % hostPorts.length]); try { zk.setData("/", new byte[1], -1); fail("Should have lost the connection"); } catch (KeeperException.ConnectionLossException e) { } zk = zknew; } zk.close(); } private static class DiscoWatcher implements Watcher { volatile boolean zkDisco = false; public void process(WatchedEvent event) { if (event.getState() == KeeperState.Disconnected) { zkDisco = true; } } } /** * Make sure the previous connection closed after session move within * multiop. * * @throws IOException * @throws InterruptedException * @throws KeeperException */ @Test public void testSessionMovedWithMultiOp() throws Exception { String[] hostPorts = qb.hostPort.split(","); DisconnectableZooKeeper zk = new DisconnectableZooKeeper( hostPorts[0], ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zk.multi(Arrays.asList(Op.create("/testSessionMovedWithMultiOp", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL))); // session moved to the next server ZooKeeper zknew = new ZooKeeper( hostPorts[1], ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE, zk.getSessionId(), zk.getSessionPasswd()); zknew.multi(Arrays.asList(Op.create("/testSessionMovedWithMultiOp-1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL))); // try to issue the multi op again from the old connection // expect to have ConnectionLossException instead of keep // getting SessionMovedException try { zk.multi(Arrays.asList(Op.create("/testSessionMovedWithMultiOp-Failed", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL))); fail("Should have lost the connection"); } catch (KeeperException.ConnectionLossException e) { } zk.close(); zknew.close(); } /** * Connect to two different servers with two different handles using the same session and * make sure we cannot do any changes. */ @Test @Disabled public void testSessionMove() throws Exception { String[] hps = qb.hostPort.split(","); DiscoWatcher oldWatcher = new DiscoWatcher(); DisconnectableZooKeeper zk = new DisconnectableZooKeeper(hps[0], ClientBase.CONNECTION_TIMEOUT, oldWatcher); zk.create("/t1", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); zk.dontReconnect(); // This should stomp the zk handle DiscoWatcher watcher = new DiscoWatcher(); DisconnectableZooKeeper zknew = new DisconnectableZooKeeper(hps[1], ClientBase.CONNECTION_TIMEOUT, watcher, zk.getSessionId(), zk.getSessionPasswd()); zknew.create("/t2", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); try { zk.create("/t3", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); fail("Should have lost the connection"); } catch (KeeperException.ConnectionLossException e) { // wait up to 30 seconds for the disco to be delivered for (int i = 0; i < 30; i++) { if (oldWatcher.zkDisco) { break; } Thread.sleep(1000); } assertTrue(oldWatcher.zkDisco); } ArrayList toClose = new ArrayList<>(); toClose.add(zknew); // Let's just make sure it can still move for (int i = 0; i < 10; i++) { zknew.dontReconnect(); zknew = new DisconnectableZooKeeper(hps[1], ClientBase.CONNECTION_TIMEOUT, new DiscoWatcher(), zk.getSessionId(), zk.getSessionPasswd()); toClose.add(zknew); zknew.create("/t-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } for (ZooKeeper z : toClose) { z.close(); } zk.close(); } /** * See ZOOKEEPER-790 for details * */ @Test public void testFollowersStartAfterLeader() throws Exception { qu = new QuorumUtil(1); CountdownWatcher watcher = new CountdownWatcher(); qu.startQuorum(); int index = 1; while (qu.getPeer(index).peer.leader == null) { index++; } // break the quorum qu.shutdown(index); // try to reestablish the quorum qu.start(index); // Connect the client after services are restarted (otherwise we would get // SessionExpiredException as the previous local session was not persisted). ZooKeeper zk = new ZooKeeper( "127.0.0.1:" + qu.getPeer((index == 1) ? 2 : 1).peer.getClientPort(), ClientBase.CONNECTION_TIMEOUT, watcher); try { watcher.waitForConnected(CONNECTION_TIMEOUT); } catch (TimeoutException e) { fail("client could not connect to reestablished quorum: giving up after 30+ seconds."); } zk.close(); } // skip superhammer and clientcleanup as they are too expensive for quorum /** * Tests if a multiop submitted to a non-leader propagates to the leader properly * (see ZOOKEEPER-1124). * * The test works as follows. It has a client connect to a follower and submit a multiop * to the follower. It then verifies that the multiop successfully gets committed by the leader. * * Without the fix in ZOOKEEPER-1124, this fails with a ConnectionLoss KeeperException. */ @Test public void testMultiToFollower() throws Exception { qu = new QuorumUtil(1); CountdownWatcher watcher = new CountdownWatcher(); qu.startQuorum(); int index = 1; while (qu.getPeer(index).peer.leader == null) { index++; } ZooKeeper zk = new ZooKeeper( "127.0.0.1:" + qu.getPeer((index == 1) ? 2 : 1).peer.getClientPort(), ClientBase.CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.multi(Arrays.asList( Op.create("/multi0", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi1", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.create("/multi2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT))); zk.getData("/multi0", false, null); zk.getData("/multi1", false, null); zk.getData("/multi2", false, null); zk.close(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumUtil.java0100644 0000000 0000000 00000027233 15051152474 033304 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.server.quorum.Election; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.util.OSMXBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility for quorum testing. Setups 2n+1 peers and allows to start/stop all * peers, particular peer, n peers etc. */ public class QuorumUtil { // TODO partitioning of peers and clients // TODO refactor QuorumBase to be special case of this private static final Logger LOG = LoggerFactory.getLogger(QuorumUtil.class); private static final Set CONNECTED_STATES = new TreeSet<>( Arrays.asList(QuorumPeer.ServerState.LEADING, QuorumPeer.ServerState.FOLLOWING, QuorumPeer.ServerState.OBSERVING)); public static class PeerStruct { public int id; public QuorumPeer peer; public File dataDir; public int clientPort; } private final Map peersView = new HashMap<>(); private final Map peers = new HashMap<>(); public final int N; public final int ALL; private String hostPort; private int tickTime; private int initLimit; private int syncLimit; private int connectToLearnerMasterLimit; private int electionAlg; private boolean localSessionEnabled; /** * Initializes 2n+1 quorum peers which will form a ZooKeeper ensemble. * * @param n * number of peers in the ensemble will be 2n+1 */ public QuorumUtil(int n, int syncLimit) throws RuntimeException { try { ClientBase.setupTestEnv(); JMXEnv.setUp(); N = n; ALL = 2 * N + 1; tickTime = 2000; initLimit = 3; this.syncLimit = syncLimit; connectToLearnerMasterLimit = 3; electionAlg = 3; hostPort = ""; for (int i = 1; i <= ALL; ++i) { PeerStruct ps = new PeerStruct(); ps.id = i; ps.dataDir = ClientBase.createTmpDir(); ps.clientPort = PortAssignment.unique(); peers.put(i, ps); peersView.put(Long.valueOf(i), new QuorumServer(i, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", ps.clientPort), LearnerType.PARTICIPANT)); hostPort += "127.0.0.1:" + ps.clientPort + ((i == ALL) ? "" : ","); } for (int i = 1; i <= ALL; ++i) { PeerStruct ps = peers.get(i); LOG.info("Creating QuorumPeer {}; public port {}", i, ps.clientPort); ps.peer = new QuorumPeer(peersView, ps.dataDir, ps.dataDir, ps.clientPort, electionAlg, ps.id, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); assertEquals(ps.clientPort, ps.peer.getClientPort()); } } catch (Exception e) { throw new RuntimeException(e); } } public QuorumUtil(int n) throws RuntimeException { this(n, 3); } public PeerStruct getPeer(int id) { return peers.get(id); } // This was added to avoid running into the problem of ZOOKEEPER-1539 public boolean disableJMXTest = false; public void enableLocalSession(boolean localSessionEnabled) { this.localSessionEnabled = localSessionEnabled; } public void startAll() throws IOException { shutdownAll(); for (int i = 1; i <= ALL; ++i) { start(i); LOG.info("Started QuorumPeer {}", i); } LOG.info("Checking ports {}", hostPort); for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerUp(hp, ClientBase.CONNECTION_TIMEOUT), "waiting for server " + hp + " up"); LOG.info("{} is accepting client connections", hp); } // This was added to avoid running into the problem of ZOOKEEPER-1539 if (disableJMXTest) { return; } // interesting to see what's there... try { JMXEnv.dump(); // make sure we have all servers listed Set ensureNames = new LinkedHashSet<>(); for (int i = 1; i <= ALL; ++i) { ensureNames.add("InMemoryDataTree"); } for (int i = 1; i <= ALL; ++i) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + i + ",name2="); } for (int i = 1; i <= ALL; ++i) { for (int j = 1; j <= ALL; ++j) { ensureNames.add("name0=ReplicatedServer_id" + i + ",name1=replica." + j); } } for (int i = 1; i <= ALL; ++i) { ensureNames.add("name0=ReplicatedServer_id" + i); } JMXEnv.ensureAll(ensureNames.toArray(new String[ensureNames.size()])); } catch (IOException e) { LOG.warn("IOException during JMXEnv operation", e); } catch (InterruptedException e) { LOG.warn("InterruptedException during JMXEnv operation", e); } } /** * Start first N+1 peers. */ public void startQuorum() throws IOException { shutdownAll(); for (int i = 1; i <= N + 1; ++i) { start(i); } for (int i = 1; i <= N + 1; ++i) { assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + getPeer(i).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); } } public void start(int id) throws IOException { PeerStruct ps = getPeer(id); LOG.info("Creating QuorumPeer {}; public port {}", ps.id, ps.clientPort); ps.peer = new QuorumPeer(peersView, ps.dataDir, ps.dataDir, ps.clientPort, electionAlg, ps.id, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); if (localSessionEnabled) { ps.peer.enableLocalSessions(true); } assertEquals(ps.clientPort, ps.peer.getClientPort()); ps.peer.start(); } public void restart(int id) throws IOException { start(id); assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + getPeer(id).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); } public void startThenShutdown(int id) throws IOException { PeerStruct ps = getPeer(id); LOG.info("Creating QuorumPeer {}; public port {}", ps.id, ps.clientPort); ps.peer = new QuorumPeer(peersView, ps.dataDir, ps.dataDir, ps.clientPort, electionAlg, ps.id, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); if (localSessionEnabled) { ps.peer.enableLocalSessions(true); } assertEquals(ps.clientPort, ps.peer.getClientPort()); ps.peer.start(); assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + getPeer(id).clientPort, ClientBase.CONNECTION_TIMEOUT), "Waiting for server up"); shutdown(id); } public void shutdownAll() { for (int i = 1; i <= ALL; ++i) { shutdown(i); } for (String hp : hostPort.split(",")) { assertTrue(ClientBase.waitForServerDown(hp, ClientBase.CONNECTION_TIMEOUT), "Waiting for server down"); LOG.info("{} is no longer accepting client connections", hp); } } public void shutdown(int id) { QuorumPeer qp = getPeer(id).peer; try { LOG.info("Shutting down quorum peer {} with id {}", qp.getName(), id); qp.shutdown(); Election e = qp.getElectionAlg(); if (e != null) { LOG.info("Shutting down leader election {} with id {}", qp.getName(), id); e.shutdown(); } else { LOG.info("No election available to shutdown {} with id {}", qp.getName(), id); } LOG.info("Waiting for {} with id {} to exit thread", qp.getName(), id); qp.join(30000); if (qp.isAlive()) { fail("QP failed to shutdown in 30 seconds: " + qp.getName() + " " + id); } } catch (InterruptedException e) { LOG.debug("QP interrupted: {} {}", qp.getName(), id, e); } } public String getConnString() { return hostPort; } public String getConnectString(QuorumPeer peer) { return "127.0.0.1:" + peer.getClientPort(); } public boolean allPeersAreConnected() { return peers.values().stream() .map(ps -> ps.peer) .allMatch(peer -> CONNECTED_STATES.contains(peer.getPeerState())); } public QuorumPeer getLeaderQuorumPeer() { for (PeerStruct ps : peers.values()) { if (ps.peer.leader != null) { return ps.peer; } } throw new RuntimeException("Unable to find a leader peer"); } public List getFollowerQuorumPeers() { List peerList = new ArrayList<>(ALL - 1); for (PeerStruct ps : peers.values()) { if (ps.peer.leader == null) { peerList.add(ps.peer); } } return Collections.unmodifiableList(peerList); } public void tearDown() throws Exception { LOG.info("TearDown started"); OSMXBean osMbean = new OSMXBean(); if (osMbean.getUnix()) { LOG.info("fdcount after test is: {}", osMbean.getOpenFileDescriptorCount()); } shutdownAll(); JMXEnv.tearDown(); } public int getLeaderServer() { int index = 0; for (int i = 1; i <= ALL; i++) { if (getPeer(i).peer.leader != null) { index = i; break; } } assertTrue(index > 0, "Leader server not found."); return index; } public boolean leaderExists() { for (int i = 1; i <= ALL; i++) { if (getPeer(i).peer.leader != null) { return true; } } return false; } public String getConnectionStringForServer(final int index) { return "127.0.0.1:" + getPeer(index).clientPort; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumUtilTest.java0100644 0000000 0000000 00000006512 15051152474 034141 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or morecontributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Set; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.jmx.ZKMBeanInfo; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is intented to ensure the correct functionality of * {@link QuorumUtil} helper. */ public class QuorumUtilTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(QuorumUtilTest.class); /** *

    * This test ensures that all JXM beans associated to a {@link QuorumPeer} * are unregistered when shuted down ({@link QuorumUtil#shutdown(int)}). It * allows a successfull restarting of several zookeeper servers ( * {@link QuorumPeer}) running on the same JVM. *

    * See ZOOKEEPER-1214 for details. */ @Test public void validateAllMXBeanAreUnregistered() throws IOException { QuorumUtil qU = new QuorumUtil(1); LOG.info(">-->> Starting up all servers..."); qU.startAll(); LOG.info(">-->> Servers up and running..."); int leaderIndex = qU.getLeaderServer(); int firstFollowerIndex = 0; int secondFollowerIndex = 0; switch (leaderIndex) { case 1: firstFollowerIndex = 2; secondFollowerIndex = 3; break; case 2: firstFollowerIndex = 1; secondFollowerIndex = 3; break; case 3: firstFollowerIndex = 1; secondFollowerIndex = 2; break; default: fail("Unexpected leaderIndex value: " + leaderIndex); break; } LOG.info(">-->> Shuting down server [{}]", firstFollowerIndex); qU.shutdown(firstFollowerIndex); LOG.info(">-->> Shuting down server [{}]", secondFollowerIndex); qU.shutdown(secondFollowerIndex); LOG.info(">-->> Restarting server [{}]", firstFollowerIndex); qU.restart(firstFollowerIndex); LOG.info(">-->> Restarting server [{}]", secondFollowerIndex); qU.restart(secondFollowerIndex); qU.shutdownAll(); Set pending = MBeanRegistry.getInstance().getRegisteredBeans(); assertTrue(pending.isEmpty(), "The following beans should have been unregistered: " + pending); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Quor0100644 0000000 0000000 00000000161 15051152474 032722 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumZxidSyncTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuorumZxidSyncTest.j0100644 0000000 0000000 00000013501 15051152474 034303 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class QuorumZxidSyncTest extends ZKTestCase { QuorumBase qb = new QuorumBase(); @BeforeEach public void setUp() throws Exception { qb.setUp(); } /** * find out what happens when a follower connects to leader that is behind */ @Test public void testBehindLeader() throws Exception { // crank up the epoch numbers ClientBase.waitForServerUp(qb.hostPort, 10000); ClientBase.waitForServerUp(qb.hostPort, 10000); ZooKeeper zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); zk.create("/0", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); zk.create("/1", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); zk.create("/2", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); qb.shutdownServers(); cleanAndInitializeDataDir(qb.s1dir); cleanAndInitializeDataDir(qb.s2dir); cleanAndInitializeDataDir(qb.s3dir); cleanAndInitializeDataDir(qb.s4dir); qb.setupServers(); qb.s1.start(); qb.s2.start(); qb.s3.start(); qb.s4.start(); assertTrue(ClientBase.waitForServerUp(qb.hostPort, 10000), "Servers didn't come up"); qb.s5.start(); String hostPort = "127.0.0.1:" + qb.s5.getClientPort(); assertFalse(ClientBase.waitForServerUp(hostPort, 10000), "Servers came up, but shouldn't have since it's ahead of leader"); } private void cleanAndInitializeDataDir(File f) throws IOException { File v = new File(f, "version-2"); for (File c : v.listFiles()) { c.delete(); } ClientBase.createInitializeFile(f); } /** * find out what happens when the latest state is in the snapshots not * the logs. */ @Test public void testLateLogs() throws Exception { // crank up the epoch numbers ClientBase.waitForServerUp(qb.hostPort, 10000); ClientBase.waitForServerUp(qb.hostPort, 10000); ZooKeeper zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); zk.create("/0", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); zk.create("/1", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); qb.shutdownServers(); deleteLogs(qb.s1dir); deleteLogs(qb.s2dir); deleteLogs(qb.s3dir); deleteLogs(qb.s4dir); deleteLogs(qb.s5dir); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); zk.create("/2", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); qb.shutdownServers(); qb.startServers(); ClientBase.waitForServerUp(qb.hostPort, 10000); zk = new ZooKeeper(qb.hostPort, 10000, DummyWatcher.INSTANCE); boolean saw2 = false; for (String child : zk.getChildren("/", false)) { if (child.equals("2")) { saw2 = true; } } zk.close(); assertTrue(saw2, "Didn't see /2 (went back in time)"); } private void deleteLogs(File f) { File v = new File(f, "version-2"); for (File c : v.listFiles()) { if (c.getName().startsWith("log")) { c.delete(); } } } @AfterEach public void tearDown() throws Exception { qb.tearDown(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/QuotasTest.java0100644 0000000 0000000 00000003460 15051152474 033266 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.Assert.assertEquals; import org.apache.zookeeper.Quotas; import org.junit.Test; public class QuotasTest { @Test public void testStatPath() { assertEquals("/zookeeper/quota/foo/zookeeper_stats", Quotas.statPath("/foo")); assertEquals("/zookeeper/quota/bar/zookeeper_stats", Quotas.statPath("/bar")); } @Test public void testLimitPath() { assertEquals("/zookeeper/quota/foo/zookeeper_limits", Quotas.limitPath("/foo")); assertEquals("/zookeeper/quota/bar/zookeeper_limits", Quotas.limitPath("/bar")); } @Test public void testQuotaPathPath() { assertEquals("/zookeeper/quota/bar", Quotas.quotaPath("/bar")); assertEquals("/zookeeper/quota/foo", Quotas.quotaPath("/foo")); } @Test public void testTrimQuotaPath() { assertEquals("/foo", Quotas.trimQuotaPath("/zookeeper/quota/foo")); assertEquals("/bar", Quotas.trimQuotaPath("/zookeeper/quota/bar")); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Read0100644 0000000 0000000 00000000157 15051152474 032654 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReadOnlyModeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReadOnlyModeTest.jav0100644 0000000 0000000 00000032761 15051152474 034201 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayOutputStream; import java.io.LineNumberReader; import java.io.StringReader; import java.util.concurrent.TimeoutException; import java.util.regex.Pattern; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.NotReadOnlyException; import org.apache.zookeeper.Transaction; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.ZooKeeper.States; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.LoggerFactory; public class ReadOnlyModeTest extends ZKTestCase { private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ReadOnlyModeTest.class); private static int CONNECTION_TIMEOUT = QuorumBase.CONNECTION_TIMEOUT; private QuorumUtil qu = new QuorumUtil(1); @BeforeEach public void setUp() throws Exception { System.setProperty("readonlymode.enabled", "true"); } @AfterEach public void tearDown() throws Exception { System.setProperty("readonlymode.enabled", "false"); qu.tearDown(); } /** * Test write operations using multi request. */ @Test @Timeout(value = 90) public void testMultiTransaction() throws Exception { qu.enableLocalSession(true); qu.startQuorum(); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); // ensure zk got connected final String data = "Data to be read in RO mode"; final String node1 = "/tnode1"; final String node2 = "/tnode2"; zk.create(node1, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); watcher.waitForDisconnected(CONNECTION_TIMEOUT); watcher.reset(); qu.shutdown(2); zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); assertEquals(States.CONNECTEDREADONLY, zk.getState(), "Should be in r-o mode"); // read operation during r/o mode String remoteData = new String(zk.getData(node1, false, null)); assertEquals(data, remoteData, "Failed to read data in r-o mode"); try { Transaction transaction = zk.transaction(); transaction.setData(node1, "no way".getBytes(), -1); transaction.create(node2, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); transaction.commit(); fail("Write operation using multi-transaction" + " api has succeeded during RO mode"); } catch (NotReadOnlyException e) { // ok } assertNull(zk.exists(node2, false), "Should have created the znode:" + node2); } /** * Basic test of read-only client functionality. Tries to read and write * during read-only mode, then regains a quorum and tries to write again. */ @Test @Timeout(value = 90) public void testReadOnlyClient() throws Exception { qu.enableLocalSession(true); qu.startQuorum(); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); // ensure zk got connected final String data = "Data to be read in RO mode"; final String node = "/tnode"; zk.create(node, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); watcher.reset(); qu.shutdown(2); zk.close(); // Re-connect the client (in case we were connected to the shut down // server and the local session was not persisted). zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); // read operation during r/o mode String remoteData = new String(zk.getData(node, false, null)); assertEquals(data, remoteData); try { zk.setData(node, "no way".getBytes(), -1); fail("Write operation has succeeded during RO mode"); } catch (NotReadOnlyException e) { // ok } watcher.reset(); qu.start(2); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(2).clientPort, CONNECTION_TIMEOUT), "waiting for server up"); zk.close(); watcher.reset(); // Re-connect the client (in case we were connected to the shut down // server and the local session was not persisted). zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.setData(node, "We're in the quorum now".getBytes(), -1); zk.close(); } /** * Ensures that upon connection to a read-only server client receives * ConnectedReadOnly state notification. */ @Test @Timeout(value = 90) public void testConnectionEvents() throws Exception { qu.enableLocalSession(true); qu.startQuorum(); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); boolean success = false; for (int i = 0; i < 30; i++) { try { zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); success = true; break; } catch (KeeperException.ConnectionLossException e) { Thread.sleep(1000); } } assertTrue(success, "Did not succeed in connecting in 30s"); assertFalse(watcher.readOnlyConnected, "The connection should not be read-only yet"); // kill peer and wait no more than 5 seconds for read-only server // to be started (which should take one tickTime (2 seconds)) qu.shutdown(2); // Re-connect the client (in case we were connected to the shut down // server and the local session was not persisted). watcher = new CountdownWatcher(); zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); long start = Time.currentElapsedTime(); while (!(zk.getState() == States.CONNECTEDREADONLY)) { Thread.sleep(200); // TODO this was originally 5 seconds, but realistically, on random/slow/virt hosts, there is no way to guarantee this assertTrue(Time.currentElapsedTime() - start < 30000, "Can't connect to the server"); } watcher.waitForReadOnlyConnected(5000); zk.close(); } /** * Tests a situation when client firstly connects to a read-only server and * then connects to a majority server. Transition should be transparent for * the user. */ @Test @Timeout(value = 90) public void testSessionEstablishment() throws Exception { qu.enableLocalSession(true); qu.startQuorum(); qu.shutdown(2); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); assertSame(States.CONNECTEDREADONLY, zk.getState(), "should be in r/o mode"); long fakeId = zk.getSessionId(); LOG.info("Connected as r/o mode with state {} and session id {}", zk.getState(), fakeId); watcher.reset(); qu.start(2); assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + qu.getPeer(2).clientPort, CONNECTION_TIMEOUT), "waiting for server up"); LOG.info("Server 127.0.0.1:{} is up", qu.getPeer(2).clientPort); // ZOOKEEPER-2722: wait until we can connect to a read-write server after the quorum // is formed. Otherwise, it is possible that client first connects to a read-only server, // then drops the connection because of shutting down of the read-only server caused // by leader election / quorum forming between the read-only server and the newly started // server. If we happen to execute the zk.create after the read-only server is shutdown and // before the quorum is formed, we will get a ConnectLossException. watcher.waitForSyncConnected(CONNECTION_TIMEOUT); assertEquals(States.CONNECTED, zk.getState(), "Should be in read-write mode"); LOG.info("Connected as rw mode with state {} and session id {}", zk.getState(), zk.getSessionId()); zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertFalse(zk.getSessionId() == fakeId, "fake session and real session have same id"); zk.close(); } @Test @Timeout(value = 90) public void testGlobalSessionInRO() throws Exception { qu.startQuorum(); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); LOG.info("global session created 0x{}", Long.toHexString(zk.getSessionId())); watcher.reset(); qu.shutdown(2); try { watcher.waitForConnected(CONNECTION_TIMEOUT); fail("Should not be able to renew a global session"); } catch (TimeoutException e) { } zk.close(); watcher.reset(); zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); try { watcher.waitForConnected(CONNECTION_TIMEOUT); fail("Should not be able to create a global session"); } catch (TimeoutException e) { } zk.close(); qu.getPeer(1).peer.enableLocalSessions(true); zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); try { watcher.waitForConnected(CONNECTION_TIMEOUT); } catch (TimeoutException e) { fail("Should be able to create a local session"); } zk.close(); } /** * Ensures that client seeks for r/w servers while it's connected to r/o * server. */ @SuppressWarnings("deprecation") @Test @Timeout(value = 90) public void testSeekForRwServer() throws Exception { qu.enableLocalSession(true); qu.startQuorum(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); qu.shutdown(2); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true); watcher.waitForConnected(CONNECTION_TIMEOUT); // if we don't suspend a peer it will rejoin a quorum qu.getPeer(1).peer .setSuspended(true); // start two servers to form a quorum; client should detect this and // connect to one of them watcher.reset(); qu.start(2); qu.start(3); ClientBase.waitForServerUp(qu.getConnString(), 2000); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // resume poor fellow qu.getPeer(1).peer .setSuspended(false); String log = os.toString(); assertFalse(StringUtils.isEmpty(log), "OutputStream doesn't have any log messages"); LineNumberReader r = new LineNumberReader(new StringReader(log)); String line; Pattern p = Pattern.compile(".*Majority server found.*"); boolean found = false; while ((line = r.readLine()) != null) { if (p.matcher(line).matches()) { found = true; break; } } assertTrue(found, "Majority server wasn't found while connected to r/o server"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Read0100644 0000000 0000000 00000000166 15051152474 032654 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReadOnlyModeWithSSLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReadOnlyModeWithSSLT0100644 0000000 0000000 00000014726 15051152474 034125 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; import java.io.LineNumberReader; import java.io.StringReader; import java.util.regex.Pattern; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.common.StringUtils; import org.apache.zookeeper.server.NettyServerCnxnFactory; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class ReadOnlyModeWithSSLTest extends ZKTestCase { private static int CONNECTION_TIMEOUT = QuorumBase.CONNECTION_TIMEOUT; private QuorumUtil qu = new QuorumUtil(1); private ClientX509Util clientX509Util; private ZKClientConfig clientConfig; @BeforeEach public void setUp() throws Exception { clientX509Util = new ClientX509Util(); String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); clientConfig = new ZKClientConfig(); clientConfig.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); clientConfig.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); clientConfig.setProperty(clientX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); clientConfig.setProperty(clientX509Util.getSslKeystorePasswdProperty(), "testpass"); clientConfig.setProperty(clientX509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); clientConfig.setProperty(clientX509Util.getSslTruststorePasswdProperty(), "testpass"); System.setProperty("readonlymode.enabled", "true"); System.setProperty(NettyServerCnxnFactory.PORT_UNIFICATION_KEY, Boolean.TRUE.toString()); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(clientX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(clientX509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty(clientX509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(clientX509Util.getSslTruststorePasswdProperty(), "testpass"); } @AfterEach public void tearDown() throws Exception { System.clearProperty(NettyServerCnxnFactory.PORT_UNIFICATION_KEY); System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdPathProperty()); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdPathProperty()); System.clearProperty(clientX509Util.getFipsModeProperty()); System.clearProperty(clientX509Util.getSslHostnameVerificationEnabledProperty()); System.clearProperty(clientX509Util.getSslProviderProperty()); clientX509Util.close(); System.setProperty("readonlymode.enabled", "false"); qu.tearDown(); } /** * Ensures that client seeks for r/w servers while it's connected to r/o * server. */ @Test @Timeout(value = 90) public void testSeekForRwServerWithSSL() throws Exception { qu.enableLocalSession(true); qu.startQuorum(); try (LoggerTestTool loggerTestTool = new LoggerTestTool("org.apache.zookeeper")) { ByteArrayOutputStream os = loggerTestTool.getOutputStream(); qu.shutdown(2); ClientBase.CountdownWatcher watcher = new ClientBase.CountdownWatcher(); ZooKeeper zk = new ZooKeeper(qu.getConnString(), CONNECTION_TIMEOUT, watcher, true, clientConfig); watcher.waitForConnected(CONNECTION_TIMEOUT); // if we don't suspend a peer it will rejoin a quorum qu.getPeer(1).peer .setSuspended(true); // start two servers to form a quorum; client should detect this and // connect to one of them watcher.reset(); qu.start(2); qu.start(3); ClientBase.waitForServerUp(qu.getConnString(), 2000); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // resume poor fellow qu.getPeer(1).peer .setSuspended(false); String log = os.toString(); assertFalse(StringUtils.isEmpty(log), "OutputStream doesn't have any log messages"); LineNumberReader r = new LineNumberReader(new StringReader(log)); String line; Pattern p = Pattern.compile(".*Majority server found.*"); boolean found = false; while ((line = r.readLine()) != null) { if (p.matcher(line).matches()) { found = true; break; } } assertTrue(found, "Majority server wasn't found while connected to r/o server"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Reco0100644 0000000 0000000 00000000164 15051152474 032667 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReconfigExceptionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReconfigExceptionTes0100644 0000000 0000000 00000021154 15051152474 034321 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReconfigExceptionTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(ReconfigExceptionTest.class); private static String authProvider = "zookeeper.DigestAuthenticationProvider.superDigest"; // Use DigestAuthenticationProvider.base64Encode or // run ZooKeeper jar with org.apache.zookeeper.server.auth.DigestAuthenticationProvider to generate password. // An example: // java -cp zookeeper.jar:lib/slf4j-api-1.7.30.jar:lib/logback-classic-1.2.10.jar:lib/logback-core-1.2.10.jar:conf // org.apache.zookeeper.server.auth.DigestAuthenticationProvider super:test // The password here is 'test'. private static String superDigest = "super:D/InIHSb7yEEbrWz8b9l71RjZJU="; private QuorumUtil qu; private ZooKeeperAdmin zkAdmin; @BeforeEach public void setup() throws InterruptedException { System.setProperty(authProvider, superDigest); QuorumPeerConfig.setReconfigEnabled(true); // Get a three server quorum. qu = new QuorumUtil(1); qu.disableJMXTest = true; try { qu.startAll(); } catch (IOException e) { fail("Fail to start quorum servers."); } resetZKAdmin(); } @AfterEach public void tearDown() throws Exception { System.clearProperty(authProvider); try { if (qu != null) { qu.tearDown(); } if (zkAdmin != null) { zkAdmin.close(); } } catch (Exception e) { // Ignore. } } @Test @Timeout(value = 10) public void testReconfigDisabled() throws InterruptedException { QuorumPeerConfig.setReconfigEnabled(false); // for this test we need to restart the quorum peers to get the config change, // as in the setup() we started the quorum with reconfigEnabled=true qu.shutdownAll(); try { qu.startAll(); } catch (IOException e) { fail("Fail to start quorum servers."); } try { reconfigPort(); fail("Reconfig should be disabled."); } catch (KeeperException e) { assertTrue(e.code() == KeeperException.Code.RECONFIGDISABLED); } } @Test @Timeout(value = 10) public void testReconfigFailWithoutAuth() throws InterruptedException { try { reconfigPort(); fail("Reconfig should fail without auth."); } catch (KeeperException e) { // However a failure is still expected as user is not authenticated, so ACL check will fail. assertTrue(e.code() == KeeperException.Code.NOAUTH); } } @Test @Timeout(value = 10) public void testReconfigEnabledWithSuperUser() throws InterruptedException { try { zkAdmin.addAuthInfo("digest", "super:test".getBytes()); assertTrue(reconfigPort()); } catch (KeeperException e) { fail("Reconfig should not fail, but failed with exception : " + e.getMessage()); } } @Test @Timeout(value = 10) public void testReconfigFailWithAuthWithNoACL() throws InterruptedException { resetZKAdmin(); try { zkAdmin.addAuthInfo("digest", "user:test".getBytes()); reconfigPort(); fail("Reconfig should fail without a valid ACL associated with user."); } catch (KeeperException e) { // Again failure is expected because no ACL is associated with this user. assertTrue(e.code() == KeeperException.Code.NOAUTH); } } @Test @Timeout(value = 10) public void testReconfigEnabledWithAuthAndWrongACL() throws InterruptedException { resetZKAdmin(); try { zkAdmin.addAuthInfo("digest", "super:test".getBytes()); // There is ACL however the permission is wrong - need WRITE permission at leaste. ArrayList acls = new ArrayList<>(Collections.singletonList(new ACL(ZooDefs.Perms.READ, new Id("digest", "user:tl+z3z0vO6PfPfEENfLF96E6pM0="/* password is test */)))); zkAdmin.setACL(ZooDefs.CONFIG_NODE, acls, -1); resetZKAdmin(); zkAdmin.addAuthInfo("digest", "user:test".getBytes()); reconfigPort(); fail("Reconfig should fail with an ACL that is read only!"); } catch (KeeperException e) { assertTrue(e.code() == KeeperException.Code.NOAUTH); } } @Test @Timeout(value = 10) public void testReconfigEnabledWithAuthAndACL() throws InterruptedException { resetZKAdmin(); try { zkAdmin.addAuthInfo("digest", "super:test".getBytes()); ArrayList acls = new ArrayList<>(Collections.singletonList(new ACL(ZooDefs.Perms.WRITE, new Id("digest", "user:tl+z3z0vO6PfPfEENfLF96E6pM0="/* password is test */)))); zkAdmin.setACL(ZooDefs.CONFIG_NODE, acls, -1); resetZKAdmin(); zkAdmin.addAuthInfo("digest", "user:test".getBytes()); assertTrue(reconfigPort()); } catch (KeeperException e) { fail("Reconfig should not fail, but failed with exception : " + e.getMessage()); } } // Utility method that recreates a new ZooKeeperAdmin handle, and wait for the handle to connect to // quorum servers. private void resetZKAdmin() throws InterruptedException { String cnxString; ClientBase.CountdownWatcher watcher = new ClientBase.CountdownWatcher(); try { cnxString = "127.0.0.1:" + qu.getPeer(1).peer.getClientPort(); if (zkAdmin != null) { zkAdmin.close(); } zkAdmin = new ZooKeeperAdmin(cnxString, ClientBase.CONNECTION_TIMEOUT, watcher); } catch (IOException e) { fail("Fail to create ZooKeeperAdmin handle."); return; } try { watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); } catch (InterruptedException | TimeoutException e) { fail("ZooKeeper admin client can not connect to " + cnxString); } } private boolean reconfigPort() throws KeeperException, InterruptedException { List joiningServers = new ArrayList<>(); int leaderId = 1; while (qu.getPeer(leaderId).peer.leader == null) { leaderId++; } int followerId = leaderId == 1 ? 2 : 1; joiningServers.add("server." + followerId + "=localhost:" + qu.getPeer(followerId).peer.getQuorumAddress().getAllPorts().get(0) /*quorum port*/ + ":" + qu.getPeer(followerId).peer.getElectionAddress().getAllPorts().get(0) /*election port*/ + ":participant;localhost:" + PortAssignment.unique()/* new client port */); zkAdmin.reconfigure(joiningServers, null, null, -1, new Stat()); return true; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Reco0100644 0000000 0000000 00000000164 15051152474 032667 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReconfigMisconfigTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReconfigMisconfigTes0100644 0000000 0000000 00000011757 15051152474 034311 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReconfigMisconfigTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(ReconfigMisconfigTest.class); private QuorumUtil qu; private ZooKeeperAdmin zkAdmin; private static String errorMsg = "Reconfig should fail without configuring the super " + "user's password on server side first."; @BeforeEach public void setup() throws InterruptedException { QuorumPeerConfig.setReconfigEnabled(true); // Get a three server quorum. qu = new QuorumUtil(1); qu.disableJMXTest = true; try { qu.startAll(); } catch (IOException e) { fail("Fail to start quorum servers."); } instantiateZKAdmin(); } @AfterEach public void tearDown() throws Exception { try { if (qu != null) { qu.tearDown(); } if (zkAdmin != null) { zkAdmin.close(); } } catch (Exception e) { // Ignore. } } @Test @Timeout(value = 10) public void testReconfigFailWithoutSuperuserPasswordConfiguredOnServer() throws InterruptedException { // This tests the case where ZK ensemble does not have the super user's password configured. // Reconfig should fail as the super user has to be explicitly configured via // zookeeper.DigestAuthenticationProvider.superDigest. try { reconfigPort(); fail(errorMsg); } catch (KeeperException e) { assertTrue(e.code() == KeeperException.Code.NOAUTH); } try { zkAdmin.addAuthInfo("digest", "super:".getBytes()); reconfigPort(); fail(errorMsg); } catch (KeeperException e) { assertTrue(e.code() == KeeperException.Code.NOAUTH); } } private void instantiateZKAdmin() throws InterruptedException { String cnxString; ClientBase.CountdownWatcher watcher = new ClientBase.CountdownWatcher(); try { cnxString = "127.0.0.1:" + qu.getPeer(1).peer.getClientPort(); zkAdmin = new ZooKeeperAdmin(cnxString, ClientBase.CONNECTION_TIMEOUT, watcher); } catch (IOException e) { fail("Fail to create ZooKeeperAdmin handle."); return; } try { watcher.waitForConnected(ClientBase.CONNECTION_TIMEOUT); } catch (InterruptedException | TimeoutException e) { fail("ZooKeeper admin client can not connect to " + cnxString); } } private boolean reconfigPort() throws KeeperException, InterruptedException { List joiningServers = new ArrayList<>(); int leaderId = 1; while (qu.getPeer(leaderId).peer.leader == null) { leaderId++; } int followerId = leaderId == 1 ? 2 : 1; joiningServers.add("server." + followerId + "=localhost:" + qu.getPeer(followerId).peer.getQuorumAddress().getAllPorts().get(0) /*quorum port*/ + ":" + qu.getPeer(followerId).peer.getElectionAddress().getAllPorts().get(0) /*election port*/ + ":participant;localhost:" + PortAssignment.unique()/* new client port */); zkAdmin.reconfigure(joiningServers, null, null, -1, new Stat()); return true; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ReconfigTest.java0100644 0000000 0000000 00000150231 15051152474 033545 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static java.lang.Integer.parseInt; import static java.lang.String.format; import static java.net.InetAddress.getLoopbackAddress; import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.zookeeper.AsyncCallback.DataCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.jmx.MBeanRegistry; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical; import org.apache.zookeeper.server.quorum.flexible.QuorumMaj; import org.apache.zookeeper.server.quorum.flexible.QuorumVerifier; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ReconfigTest extends ZKTestCase implements DataCallback { private static final Logger LOG = LoggerFactory.getLogger(ReconfigTest.class); private QuorumUtil qu; private ZooKeeper[] zkArr; private ZooKeeperAdmin[] zkAdminArr; @BeforeEach public void setup() { System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); QuorumPeerConfig.setReconfigEnabled(true); } @AfterEach public void tearDown() throws Exception { closeAllHandles(zkArr, zkAdminArr); if (qu != null) { qu.tearDown(); } } public static String reconfig( ZooKeeperAdmin zkAdmin, List joiningServers, List leavingServers, List newMembers, long fromConfig) throws KeeperException, InterruptedException { byte[] config = null; String failure = null; LOG.info("reconfig initiated by the test"); for (int j = 0; j < 30; j++) { try { config = zkAdmin.reconfigure(joiningServers, leavingServers, newMembers, fromConfig, new Stat()); failure = null; break; } catch (KeeperException.ConnectionLossException e) { failure = "client could not connect to reestablished quorum: giving up after 30+ seconds."; } catch (KeeperException.ReconfigInProgress e) { failure = "reconfig still in progress: giving up after 30+ seconds."; } Thread.sleep(1000); } if (failure != null) { fail(failure); } String configStr = new String(config); List currentServerConfigs = Arrays.stream(configStr.split("\n")) .map(String::trim) .filter(s->s.startsWith("server")) .map(ServerConfigLine::new) .collect(toList()); if (joiningServers != null) { for (String joiner : joiningServers) { ServerConfigLine joinerServerConfigLine = new ServerConfigLine(joiner); String errorMessage = format("expected joiner config \"%s\" not found in current config:\n%s", joiner, configStr); assertTrue(currentServerConfigs.stream().anyMatch(c -> c.equals(joinerServerConfigLine)), errorMessage); } } if (leavingServers != null) { for (String leaving : leavingServers) { String errorMessage = format("leaving server \"%s\" not removed from config: \n%s", leaving, configStr); assertFalse(configStr.contains(format("server.%s=", leaving)), errorMessage); } } return configStr; } public static String testServerHasConfig( ZooKeeper zk, List joiningServers, List leavingServers) throws KeeperException, InterruptedException { boolean testNodeExists = false; byte[] config = null; for (int j = 0; j < 30; j++) { try { if (!testNodeExists) { createZNode(zk, "/dummy", "dummy"); testNodeExists = true; } // Use setData instead of sync API to force a view update. // Check ZOOKEEPER-2137 for details. zk.setData("/dummy", "dummy".getBytes(), -1); config = zk.getConfig(false, new Stat()); break; } catch (KeeperException.ConnectionLossException e) { if (j < 29) { Thread.sleep(1000); } else { // test fails if we still can't connect to the quorum after // 30 seconds. fail("client could not connect to reestablished quorum: giving up after 30+ seconds."); } } } String configStr = new String(config); if (joiningServers != null) { for (String joiner : joiningServers) { assertTrue(configStr.contains(joiner), "Config:<" + configStr + ">\n" + joiner); } } if (leavingServers != null) { for (String leaving : leavingServers) { assertFalse(configStr.contains("server.".concat(leaving)), "Config:<" + configStr + ">\n" + leaving); } } return configStr; } public static void testNormalOperation(ZooKeeper writer, ZooKeeper reader) throws KeeperException, InterruptedException { testNormalOperation(writer, reader, true); } public static void testNormalOperation(ZooKeeper writer, ZooKeeper reader, boolean initTestNodes) throws KeeperException, InterruptedException { boolean createNodes = initTestNodes; for (int j = 0; j < 30; j++) { try { if (createNodes) { createZNode(writer, "/test", "test"); createZNode(reader, "/dummy", "dummy"); createNodes = false; } String data = "test" + j; writer.setData("/test", data.getBytes(), -1); // Use setData instead of sync API to force a view update. // Check ZOOKEEPER-2137 for details. reader.setData("/dummy", "dummy".getBytes(), -1); byte[] res = reader.getData("/test", null, new Stat()); assertEquals(data, new String(res)); break; } catch (KeeperException.ConnectionLossException e) { if (j < 29) { Thread.sleep(1000); } else { // test fails if we still can't connect to the quorum after // 30 seconds. fail("client could not connect to reestablished quorum: giving up after 30+ seconds."); } } } } private static void createZNode( ZooKeeper zk, String path, String data) throws KeeperException, InterruptedException { try { zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException e) { } } private int getLeaderId(QuorumUtil qu) { int leaderId = 1; while (qu.getPeer(leaderId).peer.leader == null) { leaderId++; } return leaderId; } public static ZooKeeper[] createHandles(QuorumUtil qu) throws IOException { // create an extra handle, so we can index the handles from 1 to qu.ALL // using the server id. ZooKeeper[] zkArr = new ZooKeeper[qu.ALL + 1]; zkArr[0] = null; // not used. for (int i = 1; i <= qu.ALL; i++) { // server ids are 1, 2 and 3 zkArr[i] = new ZooKeeper( "127.0.0.1:" + qu.getPeer(i).peer.getClientPort(), ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); } return zkArr; } public static ZooKeeperAdmin[] createAdminHandles(QuorumUtil qu) throws IOException { // create an extra handle, so we can index the handles from 1 to qu.ALL // using the server id. ZooKeeperAdmin[] zkAdminArr = new ZooKeeperAdmin[qu.ALL + 1]; zkAdminArr[0] = null; // not used. for (int i = 1; i <= qu.ALL; i++) { // server ids are 1, 2 and 3 zkAdminArr[i] = new ZooKeeperAdmin( "127.0.0.1:" + qu.getPeer(i).peer.getClientPort(), ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdminArr[i].addAuthInfo("digest", "super:test".getBytes()); } return zkAdminArr; } public static void closeAllHandles(ZooKeeper[] zkArr, ZooKeeperAdmin[] zkAdminArr) throws InterruptedException { if (zkArr != null) { for (ZooKeeper zk : zkArr) { if (zk != null) { zk.close(); } } } if (zkAdminArr != null) { for (ZooKeeperAdmin zkAdmin : zkAdminArr) { if (zkAdmin != null) { zkAdmin.close(); } } } } @Test public void testRemoveAddOne() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); List leavingServers = new ArrayList<>(); List joiningServers = new ArrayList<>(); int leaderIndex = getLeaderId(qu); // during first iteration, leavingIndex will correspond to a follower // during second iteration leavingIndex will be the index of the leader int leavingIndex = (leaderIndex == 1) ? 2 : 1; for (int i = 0; i < 2; i++) { // some of the operations will be executed by a client connected to // the removed server // while others are invoked by a client connected to some other // server. // when we're removing the leader, zk1 will be the client connected // to removed server ZooKeeper zk1 = (leavingIndex == leaderIndex) ? zkArr[leaderIndex] : zkArr[(leaderIndex % qu.ALL) + 1]; ZooKeeper zk2 = (leavingIndex == leaderIndex) ? zkArr[(leaderIndex % qu.ALL) + 1] : zkArr[leaderIndex]; ZooKeeperAdmin zkAdmin1 = (leavingIndex == leaderIndex) ? zkAdminArr[leaderIndex] : zkAdminArr[(leaderIndex % qu.ALL) + 1]; ZooKeeperAdmin zkAdmin2 = (leavingIndex == leaderIndex) ? zkAdminArr[(leaderIndex % qu.ALL) + 1] : zkAdminArr[leaderIndex]; leavingServers.add(Integer.toString(leavingIndex)); // remember this server so we can add it back later joiningServers.add("server." + leavingIndex + "=localhost:" + qu.getPeer(leavingIndex).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(leavingIndex).peer.getElectionAddress().getAllPorts().get(0) + ":participant;localhost:" + qu.getPeer(leavingIndex).peer.getClientPort()); String configStr = reconfig(zkAdmin1, null, leavingServers, null, -1); testServerHasConfig(zk2, null, leavingServers); testNormalOperation(zk2, zk1); QuorumVerifier qv = qu.getPeer(1).peer.configFromString(configStr); long version = qv.getVersion(); // checks that conditioning on version works properly try { reconfig(zkAdmin2, joiningServers, null, null, version + 1); fail("reconfig succeeded even though version condition was incorrect!"); } catch (KeeperException.BadVersionException e) { } reconfig(zkAdmin2, joiningServers, null, null, version); testNormalOperation(zk1, zk2); testServerHasConfig(zk1, joiningServers, null); // second iteration of the loop will remove the leader // and add it back (as follower) leavingIndex = leaderIndex = getLeaderId(qu); leavingServers.clear(); joiningServers.clear(); } } /** * 1. removes and adds back two servers (incl leader). One of the servers is added back as observer * 2. tests that reconfig fails if quorum of new config is not up * 3. tests that a server that's not up during reconfig learns the new config when it comes up * @throws Exception */ @Test public void testRemoveAddTwo() throws Exception { qu = new QuorumUtil(2); // create 5 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); List leavingServers = new ArrayList<>(); List joiningServers = new ArrayList<>(); int leaderIndex = getLeaderId(qu); // lets remove the leader and some other server int leavingIndex1 = leaderIndex; int leavingIndex2 = (leaderIndex == 1) ? 2 : 1; // find some server that's staying int stayingIndex1 = 1, stayingIndex2 = 1, stayingIndex3 = 1; while (stayingIndex1 == leavingIndex1 || stayingIndex1 == leavingIndex2) { stayingIndex1++; } while (stayingIndex2 == leavingIndex1 || stayingIndex2 == leavingIndex2 || stayingIndex2 == stayingIndex1) { stayingIndex2++; } while (stayingIndex3 == leavingIndex1 || stayingIndex3 == leavingIndex2 || stayingIndex3 == stayingIndex1 || stayingIndex3 == stayingIndex2) { stayingIndex3++; } leavingServers.add(Integer.toString(leavingIndex1)); leavingServers.add(Integer.toString(leavingIndex2)); // remember these servers so we can add them back later joiningServers.add("server." + leavingIndex1 + "=localhost:" + qu.getPeer(leavingIndex1).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(leavingIndex1).peer.getElectionAddress().getAllPorts().get(0) + ":participant;localhost:" + qu.getPeer(leavingIndex1).peer.getClientPort()); // this server will be added back as an observer joiningServers.add("server." + leavingIndex2 + "=localhost:" + qu.getPeer(leavingIndex2).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(leavingIndex2).peer.getElectionAddress().getAllPorts().get(0) + ":observer;localhost:" + qu.getPeer(leavingIndex2).peer.getClientPort()); qu.shutdown(leavingIndex1); qu.shutdown(leavingIndex2); // 3 servers still up so this should work reconfig(zkAdminArr[stayingIndex2], null, leavingServers, null, -1); qu.shutdown(stayingIndex2); // the following commands would not work in the original // cluster of 5, but now that we've removed 2 servers // we have a cluster of 3 servers and one of them is allowed to fail testServerHasConfig(zkArr[stayingIndex1], null, leavingServers); testServerHasConfig(zkArr[stayingIndex3], null, leavingServers); testNormalOperation(zkArr[stayingIndex1], zkArr[stayingIndex3]); // this is a test that a reconfig will only succeed // if there is a quorum up in new config. Below there is no // quorum so it should fail // the sleep is necessary so that the leader figures out // that the switched off servers are down Thread.sleep(10000); try { reconfig(zkAdminArr[stayingIndex1], joiningServers, null, null, -1); fail("reconfig completed successfully even though there is no quorum up in new config!"); } catch (KeeperException.NewConfigNoQuorum e) { } // now start the third server so that new config has quorum qu.restart(stayingIndex2); reconfig(zkAdminArr[stayingIndex1], joiningServers, null, null, -1); testNormalOperation(zkArr[stayingIndex2], zkArr[stayingIndex3]); testServerHasConfig(zkArr[stayingIndex2], joiningServers, null); // this server wasn't around during the configuration change // we should check that it is able to connect, finds out // about the change and becomes an observer. qu.restart(leavingIndex2); assertTrue(qu.getPeer(leavingIndex2).peer.getPeerState() == ServerState.OBSERVING); testNormalOperation(zkArr[stayingIndex2], zkArr[leavingIndex2]); testServerHasConfig(zkArr[leavingIndex2], joiningServers, null); } @Test public void testBulkReconfig() throws Exception { qu = new QuorumUtil(3); // create 7 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); // new config will have three of the servers as followers // two of the servers as observers, and all ports different ArrayList newServers = new ArrayList<>(); for (int i = 1; i <= 5; i++) { String server = "server." + i + "=localhost:" + PortAssignment.unique() + ":" + PortAssignment.unique() + ":" + ((i == 4 || i == 5) ? "observer" : "participant") + ";localhost:" + qu.getPeer(i).peer.getClientPort(); newServers.add(server); } qu.shutdown(3); qu.shutdown(6); qu.shutdown(7); reconfig(zkAdminArr[1], null, null, newServers, -1); testNormalOperation(zkArr[1], zkArr[2]); testServerHasConfig(zkArr[1], newServers, null); testServerHasConfig(zkArr[2], newServers, null); testServerHasConfig(zkArr[4], newServers, null); testServerHasConfig(zkArr[5], newServers, null); qu.shutdown(5); qu.shutdown(4); testNormalOperation(zkArr[1], zkArr[2]); } @Test public void testRemoveOneAsynchronous() throws Exception { qu = new QuorumUtil(2); qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); List leavingServers = new ArrayList<>(); // lets remove someone who's not the leader leavingServers.add(getLeaderId(qu) == 5 ? "4" : "5"); List results = new LinkedList<>(); zkAdminArr[1].reconfigure(null, leavingServers, null, -1, this, results); synchronized (results) { while (results.size() < 1) { results.wait(); } } assertEquals(0, (int) results.get(0)); testNormalOperation(zkArr[1], zkArr[2]); for (int i = 1; i <= 5; i++) { testServerHasConfig(zkArr[i], null, leavingServers); } } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { synchronized (ctx) { ((LinkedList) ctx).add(rc); ctx.notifyAll(); } } @Test public void testRoleChange() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); // changing a server's role / port is done by "adding" it with the same // id but different role / port List joiningServers = new ArrayList<>(); int leaderIndex = getLeaderId(qu); // during first and second iteration, leavingIndex will correspond to a // follower // during third and fouth iteration leavingIndex will be the index of // the leader int changingIndex = (leaderIndex == 1) ? 2 : 1; // first convert participant to observer, then observer to participant, // and so on String newRole = "observer"; for (int i = 0; i < 4; i++) { // some of the operations will be executed by a client connected to // the removed server // while others are invoked by a client connected to some other // server. // when we're removing the leader, zk1 will be the client connected // to removed server ZooKeeper zk1 = (changingIndex == leaderIndex) ? zkArr[leaderIndex] : zkArr[(leaderIndex % qu.ALL) + 1]; ZooKeeperAdmin zkAdmin1 = (changingIndex == leaderIndex) ? zkAdminArr[leaderIndex] : zkAdminArr[(leaderIndex % qu.ALL) + 1]; // exactly as it is now, except for role change joiningServers.add("server." + changingIndex + "=localhost:" + qu.getPeer(changingIndex).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(changingIndex).peer.getElectionAddress().getAllPorts().get(0) + ":" + newRole + ";localhost:" + qu.getPeer(changingIndex).peer.getClientPort()); reconfig(zkAdmin1, joiningServers, null, null, -1); testNormalOperation(zkArr[changingIndex], zk1); if (newRole.equals("observer")) { assertTrue(qu.getPeer(changingIndex).peer.observer != null && qu.getPeer(changingIndex).peer.follower == null && qu.getPeer(changingIndex).peer.leader == null); assertTrue(qu.getPeer(changingIndex).peer.getPeerState() == ServerState.OBSERVING); } else { assertTrue(qu.getPeer(changingIndex).peer.observer == null && (qu.getPeer(changingIndex).peer.follower != null || qu.getPeer(changingIndex).peer.leader != null)); assertTrue(qu.getPeer(changingIndex).peer.getPeerState() == ServerState.FOLLOWING || qu.getPeer(changingIndex).peer.getPeerState() == ServerState.LEADING); } joiningServers.clear(); if (newRole.equals("observer")) { newRole = "participant"; } else { // lets change leader to observer newRole = "observer"; leaderIndex = getLeaderId(qu); changingIndex = leaderIndex; } } } @Test public void testPortChange() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); List joiningServers = new ArrayList<>(); int leaderIndex = getLeaderId(qu); int followerIndex = leaderIndex == 1 ? 2 : 1; // modify follower's client port int quorumPort = qu.getPeer(followerIndex).peer.getQuorumAddress().getAllPorts().get(0); int electionPort = qu.getPeer(followerIndex).peer.getElectionAddress().getAllPorts().get(0); int oldClientPort = qu.getPeer(followerIndex).peer.getClientPort(); int newClientPort = PortAssignment.unique(); joiningServers.add("server." + followerIndex + "=localhost:" + quorumPort + ":" + electionPort + ":participant;localhost:" + newClientPort); // create a /test znode and check that read/write works before // any reconfig is invoked testNormalOperation(zkArr[followerIndex], zkArr[leaderIndex]); reconfig(zkAdminArr[followerIndex], joiningServers, null, null, -1); try { for (int i = 0; i < 20; i++) { Thread.sleep(1000); zkArr[followerIndex].setData("/test", "teststr".getBytes(), -1); } } catch (KeeperException.ConnectionLossException e) { fail("Existing client disconnected when client port changed!"); } zkArr[followerIndex].close(); zkArr[followerIndex] = new ZooKeeper( "127.0.0.1:" + oldClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdminArr[followerIndex].close(); zkAdminArr[followerIndex] = new ZooKeeperAdmin( "127.0.0.1:" + oldClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdminArr[followerIndex].addAuthInfo("digest", "super:test".getBytes()); for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); zkArr[followerIndex].setData("/test", "teststr".getBytes(), -1); fail("New client connected to old client port!"); } catch (KeeperException.ConnectionLossException e) { } } zkArr[followerIndex].close(); zkArr[followerIndex] = new ZooKeeper( "127.0.0.1:" + newClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdminArr[followerIndex].close(); zkAdminArr[followerIndex] = new ZooKeeperAdmin( "127.0.0.1:" + newClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdminArr[followerIndex].addAuthInfo("digest", "super:test".getBytes()); testNormalOperation(zkArr[followerIndex], zkArr[leaderIndex]); testServerHasConfig(zkArr[followerIndex], joiningServers, null); assertEquals(newClientPort, qu.getPeer(followerIndex).peer.getClientPort()); joiningServers.clear(); // change leader's leading port - should renounce leadership int newQuorumPort = PortAssignment.unique(); joiningServers.add("server." + leaderIndex + "=localhost:" + newQuorumPort + ":" + qu.getPeer(leaderIndex).peer.getElectionAddress().getAllPorts().get(0) + ":participant;localhost:" + qu.getPeer(leaderIndex).peer.getClientPort()); reconfig(zkAdminArr[leaderIndex], joiningServers, null, null, -1); testNormalOperation(zkArr[followerIndex], zkArr[leaderIndex]); assertEquals((int) qu.getPeer(leaderIndex).peer.getQuorumAddress().getAllPorts().get(0), newQuorumPort); joiningServers.clear(); // change everyone's leader election port for (int i = 1; i <= 3; i++) { joiningServers.add("server." + i + "=localhost:" + qu.getPeer(i).peer.getQuorumAddress().getAllPorts().get(0) + ":" + PortAssignment.unique() + ":participant;localhost:" + qu.getPeer(i).peer.getClientPort()); } reconfig(zkAdminArr[1], joiningServers, null, null, -1); leaderIndex = getLeaderId(qu); int follower1 = leaderIndex == 1 ? 2 : 1; int follower2 = 1; while (follower2 == leaderIndex || follower2 == follower1) { follower2++; } // lets kill the leader and see if a new one is elected qu.shutdown(getLeaderId(qu)); testNormalOperation(zkArr[follower2], zkArr[follower1]); testServerHasConfig(zkArr[follower1], joiningServers, null); testServerHasConfig(zkArr[follower2], joiningServers, null); } @Test public void testPortChangeToBlockedPortFollower() throws Exception { testPortChangeToBlockedPort(false); } @Test public void testPortChangeToBlockedPortLeader() throws Exception { testPortChangeToBlockedPort(true); } private void testPortChangeToBlockedPort(boolean testLeader) throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); List joiningServers = new ArrayList<>(); int leaderIndex = getLeaderId(qu); int followerIndex = leaderIndex == 1 ? 2 : 1; int serverIndex = testLeader ? leaderIndex : followerIndex; int reconfigIndex = testLeader ? followerIndex : leaderIndex; // modify server's client port int quorumPort = qu.getPeer(serverIndex).peer.getQuorumAddress().getAllPorts().get(0); int electionPort = qu.getPeer(serverIndex).peer.getElectionAddress().getAllPorts().get(0); int oldClientPort = qu.getPeer(serverIndex).peer.getClientPort(); int newClientPort = PortAssignment.unique(); try (ServerSocket ss = new ServerSocket()) { ss.bind(new InetSocketAddress(getLoopbackAddress(), newClientPort)); joiningServers.add("server." + serverIndex + "=localhost:" + quorumPort + ":" + electionPort + ":participant;localhost:" + newClientPort); // create a /test znode and check that read/write works before // any reconfig is invoked testNormalOperation(zkArr[followerIndex], zkArr[leaderIndex]); // Reconfigure reconfig(zkAdminArr[reconfigIndex], joiningServers, null, null, -1); Thread.sleep(1000); // The follower reconfiguration will have failed zkArr[serverIndex].close(); zkArr[serverIndex] = new ZooKeeper( "127.0.0.1:" + newClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); zkAdminArr[serverIndex].close(); zkAdminArr[serverIndex] = new ZooKeeperAdmin( "127.0.0.1:" + newClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); try { Thread.sleep(1000); zkArr[serverIndex].setData("/test", "teststr".getBytes(), -1); fail("New client connected to new client port!"); } catch (KeeperException.ConnectionLossException | KeeperException.SessionExpiredException e) { // Exception is expected } //The old port should be clear at this stage try (ServerSocket ss2 = new ServerSocket()) { ss2.bind(new InetSocketAddress(getLoopbackAddress(), oldClientPort)); } // Move back to the old port joiningServers.clear(); joiningServers.add("server." + serverIndex + "=localhost:" + quorumPort + ":" + electionPort + ":participant;localhost:" + oldClientPort); reconfig(zkAdminArr[reconfigIndex], joiningServers, null, null, -1); zkArr[serverIndex].close(); zkArr[serverIndex] = new ZooKeeper( "127.0.0.1:" + oldClientPort, ClientBase.CONNECTION_TIMEOUT, DummyWatcher.INSTANCE); testNormalOperation(zkArr[followerIndex], zkArr[leaderIndex]); testServerHasConfig(zkArr[serverIndex], joiningServers, null); assertEquals(oldClientPort, qu.getPeer(serverIndex).peer.getClientPort()); } } @Test public void testUnspecifiedClientAddress() throws Exception { int[] ports = {PortAssignment.unique(), PortAssignment.unique(), PortAssignment.unique()}; String server = "server.0=localhost:" + ports[0] + ":" + ports[1] + ";" + ports[2]; QuorumServer qs = new QuorumServer(0, server); assertEquals(qs.clientAddr.getHostString(), "0.0.0.0"); assertEquals(qs.clientAddr.getPort(), ports[2]); } @Test public void testQuorumSystemChange() throws Exception { qu = new QuorumUtil(3); // create 7 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); ArrayList members = new ArrayList<>(); members.add("group.1=3:4:5"); members.add("group.2=1:2"); members.add("weight.1=0"); members.add("weight.2=0"); members.add("weight.3=1"); members.add("weight.4=1"); members.add("weight.5=1"); for (int i = 1; i <= 5; i++) { members.add("server." + i + "=127.0.0.1:" + qu.getPeer(i).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(i).peer.getElectionAddress().getAllPorts().get(0) + ";" + "127.0.0.1:" + qu.getPeer(i).peer.getClientPort()); } reconfig(zkAdminArr[1], null, null, members, -1); // this should flush the config to servers 2, 3, 4 and 5 testNormalOperation(zkArr[2], zkArr[3]); testNormalOperation(zkArr[4], zkArr[5]); for (int i = 1; i <= 5; i++) { if (!(qu.getPeer(i).peer.getQuorumVerifier() instanceof QuorumHierarchical)) { fail("peer " + i + " doesn't think the quorum system is Hieararchical!"); } } qu.shutdown(1); qu.shutdown(2); qu.shutdown(3); qu.shutdown(7); qu.shutdown(6); // servers 4 and 5 should be able to work independently testNormalOperation(zkArr[4], zkArr[5]); qu.restart(1); qu.restart(2); members.clear(); for (int i = 1; i <= 3; i++) { members.add("server." + i + "=127.0.0.1:" + qu.getPeer(i).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(i).peer.getElectionAddress().getAllPorts().get(0) + ";" + "127.0.0.1:" + qu.getPeer(i).peer.getClientPort()); } reconfig(zkAdminArr[1], null, null, members, -1); // flush the config to server 2 testNormalOperation(zkArr[1], zkArr[2]); qu.shutdown(4); qu.shutdown(5); // servers 1 and 2 should be able to work independently testNormalOperation(zkArr[1], zkArr[2]); for (int i = 1; i <= 2; i++) { if (!(qu.getPeer(i).peer.getQuorumVerifier() instanceof QuorumMaj)) { fail("peer " + i + " doesn't think the quorum system is a majority quorum system!"); } } } @Test public void testInitialConfigHasPositiveVersion() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); testNormalOperation(zkArr[1], zkArr[2]); for (int i = 1; i < 4; i++) { String configStr = testServerHasConfig(zkArr[i], null, null); QuorumVerifier qv = qu.getPeer(i).peer.configFromString(configStr); long version = qv.getVersion(); assertTrue(version == 0x100000000L); } } /** * Tests verifies the jmx attributes of local and remote peer bean - remove * one quorum peer and again adding it back */ @Test public void testJMXBeanAfterRemoveAddOne() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); List leavingServers = new ArrayList<>(); List joiningServers = new ArrayList<>(); // assert remotePeerBean.1 of ReplicatedServer_2 int leavingIndex = 1; int replica2 = 2; QuorumPeer peer2 = qu.getPeer(replica2).peer; QuorumServer leavingQS2 = peer2.getView().get(Long.valueOf(leavingIndex)); String remotePeerBean2 = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + replica2 + ",name1=replica." + leavingIndex; assertRemotePeerMXBeanAttributes(leavingQS2, remotePeerBean2); // assert remotePeerBean.1 of ReplicatedServer_3 int replica3 = 3; QuorumPeer peer3 = qu.getPeer(replica3).peer; QuorumServer leavingQS3 = peer3.getView().get(Long.valueOf(leavingIndex)); String remotePeerBean3 = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + replica3 + ",name1=replica." + leavingIndex; assertRemotePeerMXBeanAttributes(leavingQS3, remotePeerBean3); ZooKeeper zk = zkArr[leavingIndex]; ZooKeeperAdmin zkAdmin = zkAdminArr[leavingIndex]; leavingServers.add(Integer.toString(leavingIndex)); // remember this server so we can add it back later joiningServers.add("server." + leavingIndex + "=127.0.0.1:" + qu.getPeer(leavingIndex).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(leavingIndex).peer.getElectionAddress().getAllPorts().get(0) + ":participant;127.0.0.1:" + qu.getPeer(leavingIndex).peer.getClientPort()); // Remove ReplicatedServer_1 from the ensemble reconfig(zkAdmin, null, leavingServers, null, -1); // localPeerBean.1 of ReplicatedServer_1 QuorumPeer removedPeer = qu.getPeer(leavingIndex).peer; String localPeerBean = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + leavingIndex + ",name1=replica." + leavingIndex; assertLocalPeerMXBeanAttributes(removedPeer, localPeerBean, false); // remotePeerBean.1 shouldn't exists in ReplicatedServer_2 JMXEnv.ensureNone(remotePeerBean2); // remotePeerBean.1 shouldn't exists in ReplicatedServer_3 JMXEnv.ensureNone(remotePeerBean3); // Add ReplicatedServer_1 back to the ensemble reconfig(zkAdmin, joiningServers, null, null, -1); // localPeerBean.1 of ReplicatedServer_1 assertLocalPeerMXBeanAttributes(removedPeer, localPeerBean, true); // assert remotePeerBean.1 of ReplicatedServer_2 leavingQS2 = peer2.getView().get(Long.valueOf(leavingIndex)); assertRemotePeerMXBeanAttributes(leavingQS2, remotePeerBean2); // assert remotePeerBean.1 of ReplicatedServer_3 leavingQS3 = peer3.getView().get(Long.valueOf(leavingIndex)); assertRemotePeerMXBeanAttributes(leavingQS3, remotePeerBean3); } /** * Tests verifies the jmx attributes of local and remote peer bean - change * participant to observer role */ @Test public void testJMXBeanAfterRoleChange() throws Exception { qu = new QuorumUtil(1); // create 3 servers qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); zkAdminArr = createAdminHandles(qu); // changing a server's role / port is done by "adding" it with the same // id but different role / port List joiningServers = new ArrayList<>(); // assert remotePeerBean.1 of ReplicatedServer_2 int changingIndex = 1; int replica2 = 2; QuorumPeer peer2 = qu.getPeer(replica2).peer; QuorumServer changingQS2 = peer2.getView().get(Long.valueOf(changingIndex)); String remotePeerBean2 = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + replica2 + ",name1=replica." + changingIndex; assertRemotePeerMXBeanAttributes(changingQS2, remotePeerBean2); // assert remotePeerBean.1 of ReplicatedServer_3 int replica3 = 3; QuorumPeer peer3 = qu.getPeer(replica3).peer; QuorumServer changingQS3 = peer3.getView().get(Long.valueOf(changingIndex)); String remotePeerBean3 = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + replica3 + ",name1=replica." + changingIndex; assertRemotePeerMXBeanAttributes(changingQS3, remotePeerBean3); String newRole = "observer"; ZooKeeper zk = zkArr[changingIndex]; ZooKeeperAdmin zkAdmin = zkAdminArr[changingIndex]; // exactly as it is now, except for role change joiningServers.add("server." + changingIndex + "=127.0.0.1:" + qu.getPeer(changingIndex).peer.getQuorumAddress().getAllPorts().get(0) + ":" + qu.getPeer(changingIndex).peer.getElectionAddress().getAllPorts().get(0) + ":" + newRole + ";127.0.0.1:" + qu.getPeer(changingIndex).peer.getClientPort()); reconfig(zkAdmin, joiningServers, null, null, -1); testNormalOperation(zkArr[changingIndex], zk); assertTrue(qu.getPeer(changingIndex).peer.observer != null && qu.getPeer(changingIndex).peer.follower == null && qu.getPeer(changingIndex).peer.leader == null); assertTrue(qu.getPeer(changingIndex).peer.getPeerState() == ServerState.OBSERVING); QuorumPeer qp = qu.getPeer(changingIndex).peer; String localPeerBeanName = MBeanRegistry.DOMAIN + ":name0=ReplicatedServer_id" + changingIndex + ",name1=replica." + changingIndex; // localPeerBean.1 of ReplicatedServer_1 assertLocalPeerMXBeanAttributes(qp, localPeerBeanName, true); // assert remotePeerBean.1 of ReplicatedServer_2 changingQS2 = peer2.getView().get(Long.valueOf(changingIndex)); assertRemotePeerMXBeanAttributes(changingQS2, remotePeerBean2); // assert remotePeerBean.1 of ReplicatedServer_3 changingQS3 = peer3.getView().get(Long.valueOf(changingIndex)); assertRemotePeerMXBeanAttributes(changingQS3, remotePeerBean3); } @Test public void testReconfigEnablemntWithRollingRestart() throws Exception { // make sure dynamic reconfig is disabled QuorumPeerConfig.setReconfigEnabled(false); // start a 3 node cluster qu = new QuorumUtil(1); qu.disableJMXTest = true; qu.startAll(); zkArr = createHandles(qu); testNormalOperation(zkArr[1], zkArr[1], true); // enable dynamic reconfig (new servers created after this time will be initialized with reconfigEnabled=true) QuorumPeerConfig.setReconfigEnabled(true); // restart the three servers, one-by-one, now with reconfig enabled // test if we can write / read in the cluster after each rolling restart step for (int i = 1; i < 4; i++) { assertFalse(qu.getPeer(i).peer.isReconfigEnabled(), "dynamic reconfig was not disabled before stopping server " + i); qu.shutdown(i); qu.restart(i); assertTrue(qu.getPeer(i).peer.isReconfigEnabled(), "dynamic reconfig is not enabled for the restarted server " + i); testNormalOperation(zkArr[i], zkArr[(i % 3) + 1], false); } // now we will test dynamic reconfig by remove server 2, then add it back later List leavingServers = new ArrayList<>(); List joiningServers = new ArrayList<>(); leavingServers.add("2"); // remember this server so we can add it back later joiningServers.add(String.format("server.2=localhost:%d:%d:participant;localhost:%d", qu.getPeer(2).peer.getQuorumAddress().getAllPorts().get(0), qu.getPeer(2).peer.getElectionAddress().getAllPorts().get(0), qu.getPeer(2).peer.getClientPort())); // here we remove server 2 zkAdminArr = createAdminHandles(qu); String configStr = reconfig(zkAdminArr[1], null, leavingServers, null, -1); testServerHasConfig(zkArr[3], null, leavingServers); testNormalOperation(zkArr[1], zkArr[3], false); // here we add back server 2 QuorumVerifier qv = qu.getPeer(1).peer.configFromString(configStr); long version = qv.getVersion(); reconfig(zkAdminArr[3], joiningServers, null, null, version); testServerHasConfig(zkArr[1], joiningServers, null); testServerHasConfig(zkArr[2], joiningServers, null); testServerHasConfig(zkArr[3], joiningServers, null); testNormalOperation(zkArr[3], zkArr[1], false); } private void assertLocalPeerMXBeanAttributes( QuorumPeer qp, String beanName, Boolean isPartOfEnsemble) throws Exception { assertEquals(qp.getLearnerType().name(), JMXEnv.ensureBeanAttribute(beanName, "LearnerType"), "Mismatches LearnerType!"); assertEquals(qp.getClientAddress().getHostString() + ":" + qp.getClientAddress().getPort(), JMXEnv.ensureBeanAttribute(beanName, "ClientAddress"), "Mismatches ClientAddress!"); assertEquals(qp.getElectionAddress().getOne().getHostString() + ":" + qp.getElectionAddress().getOne().getPort(), JMXEnv.ensureBeanAttribute(beanName, "ElectionAddress"), "Mismatches LearnerType!"); assertEquals(isPartOfEnsemble, JMXEnv.ensureBeanAttribute(beanName, "PartOfEnsemble"), "Mismatches PartOfEnsemble!"); assertEquals(qp.getQuorumVerifier().getVersion(), JMXEnv.ensureBeanAttribute(beanName, "ConfigVersion"), "Mismatches ConfigVersion!"); assertEquals(qp.getQuorumVerifier().toString(), JMXEnv.ensureBeanAttribute(beanName, "QuorumSystemInfo"), "Mismatches QuorumSystemInfo!"); } String getAddrPortFromBean(String beanName, String attribute) throws Exception { String name = (String) JMXEnv.ensureBeanAttribute(beanName, attribute); if (!name.contains(":")) { return name; } return getNumericalAddrPort(name); } String getNumericalAddrPort(String name) throws UnknownHostException { String port = name.split(":")[1]; String addr = name.split(":")[0]; addr = InetAddress.getByName(addr).getHostAddress(); return addr + ":" + port; } private void assertRemotePeerMXBeanAttributes(QuorumServer qs, String beanName) throws Exception { assertEquals(qs.type.name(), JMXEnv.ensureBeanAttribute(beanName, "LearnerType"), "Mismatches LearnerType!"); assertEquals(getNumericalAddrPort(qs.clientAddr.getHostString() + ":" + qs.clientAddr.getPort()), getAddrPortFromBean(beanName, "ClientAddress"), "Mismatches ClientAddress!"); assertEquals(getNumericalAddrPort(qs.electionAddr.getOne().getHostString() + ":" + qs.electionAddr.getOne().getPort()), getAddrPortFromBean(beanName, "ElectionAddress"), "Mismatches ElectionAddress!"); assertEquals(getNumericalAddrPort(qs.addr.getOne().getHostString() + ":" + qs.addr.getOne().getPort()), getAddrPortFromBean(beanName, "QuorumAddress"), "Mismatches QuorumAddress!"); } /* * A helper class to parse / compare server address config lines. * Example: server.1=127.0.0.1:11228:11231|127.0.0.1:11230:11229:participant;0.0.0.0:11227 */ private static class ServerConfigLine { private final int serverId; private Integer clientPort; // hostName -> private final Map> quorumPorts = new HashMap<>(); // hostName -> private final Map> electionPorts = new HashMap<>(); private ServerConfigLine(String configLine) { String[] parts = configLine.trim().split("="); serverId = parseInt(parts[0].split("\\.")[1]); String[] serverConfig = parts[1].split(";"); String[] serverAddresses = serverConfig[0].split("\\|"); if (serverConfig.length > 1) { String[] clientParts = serverConfig[1].split(":"); if (clientParts.length > 1) { clientPort = parseInt(clientParts[1]); } else { clientPort = parseInt(clientParts[0]); } } for (String addr : serverAddresses) { // addr like: 127.0.0.1:11230:11229:participant or [0:0:0:0:0:0:0:1]:11346:11347 String serverHost; String[] ports; if (addr.contains("[")) { serverHost = addr.substring(1, addr.indexOf("]")); ports = addr.substring(addr.indexOf("]") + 2).split(":"); } else { serverHost = addr.substring(0, addr.indexOf(":")); ports = addr.substring(addr.indexOf(":") + 1).split(":"); } quorumPorts.computeIfAbsent(serverHost, k -> new HashSet<>()).add(parseInt(ports[0])); if (ports.length > 1) { electionPorts.computeIfAbsent(serverHost, k -> new HashSet<>()).add(parseInt(ports[1])); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerConfigLine that = (ServerConfigLine) o; return serverId == that.serverId && Objects.equals(clientPort, that.clientPort) && quorumPorts.equals(that.quorumPorts) && electionPorts.equals(that.electionPorts); } @Override public int hashCode() { return Objects.hash(serverId, clientPort, quorumPorts, electionPorts); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/RecoveryTest.java0100644 0000000 0000000 00000017305 15051152474 033613 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RecoveryTest extends ZKTestCase implements Watcher { protected static final Logger LOG = LoggerFactory.getLogger(RecoveryTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private volatile CountDownLatch startSignal; /** * Verify that if a server goes down that clients will reconnect * automatically after the server is restarted. Note that this requires the * server to restart within the connection timeout period. * * Also note that the client latches are used to eliminate any chance * of spurrious connectionloss exceptions on the read ops. Specifically * a sync operation will throw this exception if the server goes down * (as recognized by the client) during the operation. If the operation * occurs after the server is down, but before the client recognizes * that the server is down (ping) then the op will throw connectionloss. */ @Test public void testRecovery() throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); int oldSnapCount = SyncRequestProcessor.getSnapCount(); SyncRequestProcessor.setSnapCount(1000); try { final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); LOG.info("starting up the the server, waiting"); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server up"); startSignal = new CountDownLatch(1); ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this); startSignal.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); assertTrue(startSignal.getCount() == 0, "count == 0"); String path; LOG.info("starting creating nodes"); for (int i = 0; i < 10; i++) { path = "/" + i; zk.create(path, (path + "!").getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int j = 0; j < 10; j++) { String subpath = path + "/" + j; zk.create(subpath, (subpath + "!").getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int k = 0; k < 20; k++) { String subsubpath = subpath + "/" + k; zk.create(subsubpath, (subsubpath + "!").getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } } f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server down"); zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); f = ServerCnxnFactory.createFactory(PORT, -1); startSignal = new CountDownLatch(1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server up"); startSignal.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); assertTrue(startSignal.getCount() == 0, "count == 0"); Stat stat = new Stat(); for (int i = 0; i < 10; i++) { path = "/" + i; LOG.info("Checking " + path); assertEquals(new String(zk.getData(path, false, stat)), path + "!"); for (int j = 0; j < 10; j++) { String subpath = path + "/" + j; assertEquals(new String(zk.getData(subpath, false, stat)), subpath + "!"); for (int k = 0; k < 20; k++) { String subsubpath = subpath + "/" + k; assertEquals(new String(zk.getData(subsubpath, false, stat)), subsubpath + "!"); } } } f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, ClientBase.CONNECTION_TIMEOUT), "waiting for server down"); zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); f = ServerCnxnFactory.createFactory(PORT, -1); startSignal = new CountDownLatch(1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server up"); startSignal.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); assertTrue(startSignal.getCount() == 0, "count == 0"); stat = new Stat(); LOG.info("Check 2"); for (int i = 0; i < 10; i++) { path = "/" + i; assertEquals(new String(zk.getData(path, false, stat)), path + "!"); for (int j = 0; j < 10; j++) { String subpath = path + "/" + j; assertEquals(new String(zk.getData(subpath, false, stat)), subpath + "!"); for (int k = 0; k < 20; k++) { String subsubpath = subpath + "/" + k; assertEquals(new String(zk.getData(subsubpath, false, stat)), subsubpath + "!"); } } } zk.close(); f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server down"); } finally { SyncRequestProcessor.setSnapCount(oldSnapCount); } } /* * (non-Javadoc) * * @see org.apache.zookeeper.Watcher#process(org.apache.zookeeper.WatcherEvent) */ public void process(WatchedEvent event) { LOG.info("Event:{} {} {}", event.getState(), event.getType(), event.getPath()); if (event.getState() == KeeperState.SyncConnected && startSignal != null && startSignal.getCount() > 0) { startSignal.countDown(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Repe0100644 0000000 0000000 00000000160 15051152474 032666 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/RepeatStartupTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/RepeatStartupTest.ja0100644 0000000 0000000 00000005345 15051152474 034272 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.Test; public class RepeatStartupTest extends ZKTestCase { /** bring up 5 quorum peers and then shut them down * and then bring one of the nodes as server * * @throws Exception might be thrown here */ @Test public void testFail() throws Exception { QuorumBase qb = new QuorumBase(); qb.setUp(); System.out.println("Comment: the servers are at " + qb.hostPort); ZooKeeper zk = qb.createClient(); zk.create("/test", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.close(); QuorumBase.shutdown(qb.s1); QuorumBase.shutdown(qb.s2); QuorumBase.shutdown(qb.s3); QuorumBase.shutdown(qb.s4); QuorumBase.shutdown(qb.s5); String hp = qb.hostPort.split(",")[0]; ZooKeeperServer zks = new ZooKeeperServer(qb.s1.getTxnFactory().getSnapDir(), qb.s1.getTxnFactory().getDataLogDir(), 3000); final int PORT = Integer.parseInt(hp.split(":")[1]); ServerCnxnFactory factory = ServerCnxnFactory.createFactory(PORT, -1); factory.startup(zks); System.out.println("Comment: starting factory"); assertTrue( ClientBase.waitForServerUp("127.0.0.1:" + PORT, QuorumTest.CONNECTION_TIMEOUT), "waiting for server up"); factory.shutdown(); zks.shutdown(); assertTrue( ClientBase.waitForServerDown("127.0.0.1:" + PORT, QuorumTest.CONNECTION_TIMEOUT), "waiting for server down"); System.out.println("Comment: shutting down standalone"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Resp0100644 0000000 0000000 00000000160 15051152474 032704 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ResponseCacheTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ResponseCacheTest.ja0100644 0000000 0000000 00000020061 15051152474 034201 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.fail; import java.util.List; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ServerMetrics; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ResponseCacheTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(ResponseCacheTest.class); @BeforeEach public void setup() throws Exception { System.setProperty(ZooKeeperServer.GET_DATA_RESPONSE_CACHE_SIZE, "32"); System.setProperty(ZooKeeperServer.GET_CHILDREN_RESPONSE_CACHE_SIZE, "64"); super.setUp(); } @AfterEach public void tearDown() throws Exception { System.clearProperty(ZooKeeperServer.GET_DATA_RESPONSE_CACHE_SIZE); System.clearProperty(ZooKeeperServer.GET_CHILDREN_RESPONSE_CACHE_SIZE); } @Test public void testResponseCache() throws Exception { ZooKeeper zk = createClient(); try { performCacheTest(zk, "/cache", true); performCacheTest(zk, "/nocache", false); } finally { zk.close(); } } private void checkCacheStatus(long expectedHits, long expectedMisses, String cacheHitMetricsName, String cacheMissMetricsName) { Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(expectedHits, metrics.get(cacheHitMetricsName)); assertEquals(expectedMisses, metrics.get(cacheMissMetricsName)); } public void performCacheTest(ZooKeeper zk, String path, boolean useCache) throws Exception { ServerMetrics.getMetrics().resetAll(); Stat writeStat = new Stat(); Stat readStat = new Stat(); byte[] readData = null; int cacheSize = Integer.getInteger(ZooKeeperServer.GET_DATA_RESPONSE_CACHE_SIZE); int reads = 10; long expectedHits = 0; long expectedMisses = 0; ZooKeeperServer zks = serverFactory.getZooKeeperServer(); zks.setResponseCachingEnabled(useCache); LOG.info("caching: {}", useCache); if (useCache) { assertEquals(zks.getReadResponseCache().getCacheSize(), cacheSize); assertEquals(zks.getGetChildrenResponseCache().getCacheSize(), 64); } byte[] writeData = "test1".getBytes(); zk.create(path, writeData, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, writeStat); for (int i = 0; i < reads; ++i) { readData = zk.getData(path, false, readStat); assertArrayEquals(writeData, readData); assertEquals(writeStat, readStat); } if (useCache) { expectedMisses += 1; expectedHits += reads - 1; } checkCacheStatus(expectedHits, expectedMisses, "response_packet_cache_hits", "response_packet_cache_misses"); writeData = "test2".getBytes(); writeStat = zk.setData(path, writeData, -1); for (int i = 0; i < 10; ++i) { readData = zk.getData(path, false, readStat); assertArrayEquals(writeData, readData); assertEquals(writeStat, readStat); } if (useCache) { expectedMisses += 1; expectedHits += reads - 1; } checkCacheStatus(expectedHits, expectedMisses, "response_packet_cache_hits", "response_packet_cache_misses"); // Create a child beneath the tested node. This won't change the data of // the tested node, but will change it's pzxid. The next read of the tested // node should miss in the cache. The data should still match what was written // before, but the stat information should not. zk.create(path + "/child", "child".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); readData = zk.getData(path, false, readStat); if (useCache) { expectedMisses++; } assertArrayEquals(writeData, readData); assertNotSame(writeStat, readStat); checkCacheStatus(expectedHits, expectedMisses, "response_packet_cache_hits", "response_packet_cache_misses"); ServerMetrics.getMetrics().resetAll(); expectedHits = 0; expectedMisses = 0; createPath(path + "/a", zk); createPath(path + "/a/b", zk); createPath(path + "/a/c", zk); createPath(path + "/a/b/d", zk); createPath(path + "/a/b/e", zk); createPath(path + "/a/b/e/f", zk); createPath(path + "/a/b/e/g", zk); createPath(path + "/a/b/e/h", zk); createPath(path + "/x", zk); for (int i = 0; i < cacheSize * 2; ++i) { createPath(path + "/x/y" + i, zk); } checkPath(path + "/a", zk, 2); checkPath(path + "/a/b", zk, 2); checkPath(path + "/a/c", zk, 0); checkPath(path + "/a/b/d", zk, 0); checkPath(path + "/a/b/e", zk, 3); checkPath(path + "/a/b/e/h", zk, 0); checkPath(path + "/x", zk, cacheSize * 2); if (useCache) { expectedMisses += 7; } checkCacheStatus(expectedHits, expectedMisses, "response_packet_get_children_cache_hits", "response_packet_get_children_cache_misses"); checkPath(path + "/a", zk, 2); checkPath(path + "/a/b", zk, 2); checkPath(path + "/a/c", zk, 0); if (useCache) { expectedHits += 3; } checkCacheStatus(expectedHits, expectedMisses, "response_packet_get_children_cache_hits", "response_packet_get_children_cache_misses"); for (int i = 0; i < cacheSize * 2; ++i) { checkPath(path + "/a", zk, 2); checkPath(path + "/x/y" + i, zk, 0); if (useCache) { expectedHits += 1; expectedMisses += 1; } checkCacheStatus(expectedHits, expectedMisses, "response_packet_get_children_cache_hits", "response_packet_get_children_cache_misses"); } } private void createPath(String path, ZooKeeper zk) throws Exception { zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, null); } private void checkPath(String path, ZooKeeper zk, int expectedNumberOfChildren) throws Exception { Stat stat = zk.exists(path, false); List c1 = zk.getChildren(path, false); List c2 = zk.getChildren(path, false, stat); if (!c1.equals(c2)) { fail("children lists from getChildren()/getChildren2() do not match"); } assertEquals(c1.size(), expectedNumberOfChildren); if (!stat.equals(stat)) { fail("stats from exists()/getChildren2() do not match"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Rest0100644 0000000 0000000 00000000166 15051152474 032716 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/RestoreCommittedLogTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/RestoreCommittedLogT0100644 0000000 0000000 00000012323 15051152474 034307 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.util.Collection; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.Leader.Proposal; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** After a replica starts, it should load commits in its committedLog list. * This test checks if committedLog != 0 after replica restarted. */ public class RestoreCommittedLogTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(RestoreCommittedLogTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private static final int CONNECTION_TIMEOUT = 3000; /** * Verify the logs can be used to restore when they are rolled * based on the size of the transactions received * * @throws Exception */ @Test public void testRestoreCommittedLogWithSnapSize() throws Exception { final int minExpectedSnapshots = 5; final int minTxnsToSnap = 256; final int numTransactions = minExpectedSnapshots * minTxnsToSnap; final StringBuilder sb = new StringBuilder(); for (int i = 0; i < 4 * 1024; i++) { sb.append("0"); } final byte[] data = sb.toString().getBytes(); SyncRequestProcessor.setSnapCount(numTransactions * 1000 /* just some high number */); // The test breaks if this number is less than the smallest size file // created on the system, as revealed through File::length. // Setting to about 1 Mb. SyncRequestProcessor.setSnapSizeInBytes(minTxnsToSnap * data.length); testRestoreCommittedLog(numTransactions, data, minExpectedSnapshots); } /** * Verify the logs can be used to restore when they are rolled * based on the number of transactions received * * @throws Exception */ @Test public void testRestoreCommittedLogWithSnapCount() throws Exception { final int minExpectedSnapshots = 30; final int snapCount = 100; SyncRequestProcessor.setSnapCount(snapCount); SyncRequestProcessor.setSnapSizeInBytes(4294967296L); testRestoreCommittedLog(minExpectedSnapshots * snapCount, new byte[0], minExpectedSnapshots); } /** * test the purge * @throws Exception an exception might be thrown here */ private void testRestoreCommittedLog(int totalTransactions, byte[] data, int minExpectedSnapshots) throws Exception { File tmpDir = ClientBase.createTmpDir(); ClientBase.setupTestEnv(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); ServerCnxnFactory f = ServerCnxnFactory.createFactory(PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); ZooKeeper zk = ClientBase.createZKClient(HOSTPORT); try { for (int i = 0; i < totalTransactions; i++) { zk.create("/invalidsnap-" + i, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } finally { zk.close(); } final int numSnaps = zks.getTxnLogFactory().findNRecentSnapshots(10 * minExpectedSnapshots).size(); LOG.info("number of snapshots taken {}", numSnaps); f.shutdown(); zks.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server to shutdown"); assertTrue(numSnaps > minExpectedSnapshots, "too few snapshot files"); assertTrue(numSnaps <= minExpectedSnapshots * 2, "too many snapshot files"); // start server again zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); zks.startdata(); Collection committedLog = zks.getZKDatabase().getCommittedLog(); int logsize = committedLog.size(); LOG.info("committedLog size = {}", logsize); assertTrue((logsize != 0), "log size != 0"); zks.shutdown(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SSLAuthTest.java0100644 0000000 0000000 00000013022 15051152474 033270 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SSLAuthTest extends ClientBase { private ClientX509Util clientX509Util; public static ClientX509Util setUpSecure() throws Exception { ClientX509Util x509Util = new ClientX509Util(); String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); System.setProperty(x509Util.getSslAuthProviderProperty(), "x509"); System.setProperty(x509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(x509Util.getSslKeystorePasswdProperty(), "testpass"); System.setProperty(x509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(x509Util.getSslTruststorePasswdProperty(), "testpass"); System.setProperty("javax.net.debug", "ssl"); System.setProperty("zookeeper.authProvider.x509", "org.apache.zookeeper.server.auth.X509AuthenticationProvider"); return x509Util; } public static void clearSecureSetting(ClientX509Util clientX509Util) { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ZKClientConfig.SECURE_CLIENT); System.clearProperty(clientX509Util.getSslAuthProviderProperty()); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.clearProperty("javax.net.debug"); System.clearProperty("zookeeper.authProvider.x509"); clientX509Util.close(); } @BeforeEach public void setUp() throws Exception { clientX509Util = setUpSecure(); String host = "localhost"; int port = PortAssignment.unique(); hostPort = host + ":" + port; serverFactory = ServerCnxnFactory.createFactory(); serverFactory.configure(new InetSocketAddress(host, port), maxCnxns, -1, true); super.setUp(); } @AfterEach public void teardown() throws Exception { clearSecureSetting(clientX509Util); } @Test public void testRejection() throws Exception { String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); // Replace trusted keys with a valid key that is not trusted by the server System.setProperty(clientX509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testUntrustedKeyStore.jks"); System.setProperty(clientX509Util.getSslKeystorePasswdProperty(), "testpass"); CountdownWatcher watcher = new CountdownWatcher(); // Handshake will take place, and then X509AuthenticationProvider should reject the untrusted cert new TestableZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); assertFalse(watcher.clientConnected.await(1000, TimeUnit.MILLISECONDS), "Untrusted certificate should not result in successful connection"); } @Test public void testMisconfiguration() throws Exception { System.clearProperty(clientX509Util.getSslAuthProviderProperty()); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); CountdownWatcher watcher = new CountdownWatcher(); new TestableZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); assertFalse(watcher.clientConnected.await(1000, TimeUnit.MILLISECONDS), "Missing SSL configuration should not result in successful connection"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000173 15051152474 032701 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthDesignatedClientTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthDesignatedCl0100644 0000000 0000000 00000015510 15051152474 034224 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.Perms; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.client.ZooKeeperSaslClient; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.junit.jupiter.api.Test; public class SaslAuthDesignatedClientTest extends SaslAuthDigestTestBase { static { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperClient"); try { File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write("" + "Server {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_myuser=\"mypassword\";\n" + "};\n" + "Client {\n" + /* this 'Client' section has an incorrect password, but we're not configured to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) to use the 'MyZookeeperClient' section below, which has the correct password).*/ " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"myuser\"\n" + " password=\"wrongpassword\";\n" + "};" + "MyZookeeperClient {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"myuser\"\n" + " password=\"mypassword\";\n" + "};" + "\n"); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException e) { // could not create tmp directory to hold JAAS conf file : test will fail now. } } @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Thread.sleep(1000); } catch (KeeperException e) { fail("test failed :" + e); } finally { zk.close(); } } @Test public void testSaslConfig() throws Exception { ZooKeeper zk = createClient(); try { zk.getChildren("/", false); assertFalse(zk.getSaslClient(). clientTunneledAuthenticationInProgress()); assertEquals(zk.getSaslClient().getSaslState(), ZooKeeperSaslClient.SaslState.COMPLETE); assertNotNull(javax.security.auth.login.Configuration.getConfiguration(). getAppConfigurationEntry("MyZookeeperClient")); assertSame(zk.getSaslClient().getLoginContext(), "MyZookeeperClient"); } catch (KeeperException e) { fail("test failed :" + e); } finally { zk.close(); } } @Test public void testReadAccessUser() throws Exception { System.setProperty("zookeeper.letAnySaslUserDoX", "anyone"); ZooKeeper zk = createClient(); List aclList = new ArrayList<>(); ACL acl = new ACL(Perms.ADMIN | Perms.CREATE | Perms.WRITE | Perms.DELETE, new Id("sasl", "fakeuser")); ACL acl1 = new ACL(Perms.READ, new Id("sasl", "anyone")); aclList.add(acl); aclList.add(acl1); try { zk.create("/abc", "testData".getBytes(), aclList, CreateMode.PERSISTENT); } catch (KeeperException e) { fail("Unable to create znode"); } zk.close(); Thread.sleep(100); // try to access it with different user (myuser) zk = createClient(); try { zk.setData("/abc", "testData1".getBytes(), -1); fail("Should not be able to set data"); } catch (KeeperException.NoAuthException e) { // success } try { byte[] bytedata = zk.getData("/abc", null, null); String data = new String(bytedata); assertTrue("testData".equals(data)); } catch (KeeperException e) { fail("failed to get data"); } zk.close(); Thread.sleep(100); // disable Client Sasl System.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, "false"); try { zk = createClient(); try { zk.getData("/abc", null, null); fail("Should not be able to read data when not authenticated"); } catch (KeeperException.NoAuthException e) { // success } zk.close(); } finally { // enable Client Sasl System.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, "true"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000173 15051152474 032701 xustar000000000 0000000 123 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthDesignatedServerTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthDesignatedSe0100644 0000000 0000000 00000007536 15051152474 034246 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.JaasConfiguration; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ZooKeeperSaslServer; import org.junit.jupiter.api.Test; public class SaslAuthDesignatedServerTest extends SaslAuthDigestTestBase { public static int AUTHENTICATION_TIMEOUT = 30000; static { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperServer"); JaasConfiguration conf = new JaasConfiguration(); /* this 'Server' section has an incorrect password, but we're not configured * to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) * to use the 'MyZookeeperServer' section below, which has the correct password). */ conf.addSection("Server", "org.apache.zookeeper.server.auth.DigestLoginModule", "user_myuser", "wrongpassword"); conf.addSection("MyZookeeperServer", "org.apache.zookeeper.server.auth.DigestLoginModule", "user_myuser", "mypassword"); conf.addSection("Client", "org.apache.zookeeper.server.auth.DigestLoginModule", "username", "myuser", "password", "mypassword"); javax.security.auth.login.Configuration.setConfiguration(conf); } private AtomicInteger authFailed = new AtomicInteger(0); private class MyWatcher extends CountdownWatcher { volatile CountDownLatch authCompleted; @Override public synchronized void reset() { authCompleted = new CountDownLatch(1); super.reset(); } @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { authFailed.incrementAndGet(); authCompleted.countDown(); } else if (event.getState() == KeeperState.SaslAuthenticated) { authCompleted.countDown(); } else { super.process(event); } } } @Test public void testAuth() throws Exception { MyWatcher watcher = new MyWatcher(); ZooKeeper zk = createClient(watcher); watcher.authCompleted.await(AUTHENTICATION_TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(authFailed.get(), 0); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); } catch (KeeperException e) { fail("test failed :" + e); } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000165 15051152474 032702 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthDigestTestBase.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthDigestTestBa0100644 0000000 0000000 00000003065 15051152474 034222 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.common.X509Util; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; /** * Created as a base class for Digest Auth based SASL authentication tests. * We need to disable Fips mode, otherwise DIGEST-MD5 cannot be used. * * @see org.apache.zookeeper.server.quorum.auth.DigestSecurityTestcase */ public class SaslAuthDigestTestBase extends ClientBase { @BeforeAll public static void beforeClass() throws Exception { // Need to disable Fips-mode, because we use DIGEST-MD5 mech for Sasl System.setProperty(X509Util.FIPS_MODE_PROPERTY, "false"); } @AfterAll public static void afterClass() throws Exception { System.clearProperty(X509Util.FIPS_MODE_PROPERTY); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000177 15051152474 032705 xustar000000000 0000000 127 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailDesignatedClientTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailDesignat0100644 0000000 0000000 00000007774 15051152474 034245 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.client.ZKClientConfig; import org.junit.jupiter.api.Test; public class SaslAuthFailDesignatedClientTest extends SaslAuthDigestTestBase { static { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperClient"); try { File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write("Server {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_myuser=\"mypassword\";\n" + "};\n" + "Client {\n" + /* this 'Client' section has the correct password, but we're not configured to use it (we're configured by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) to use the 'MyZookeeperClient' section, which has an incorrect password).*/ " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"myuser\"\n" + " password=\"mypassword\";\n" + "};" + "MyZookeeperClient {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"myuser\"\n" + " password=\"wrongpassword\";\n" + "};" + "\n"); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException e) { // could not create tmp directory to hold JAAS conf file : test will fail now. } } @Test public void testAuth() throws Exception { // Cannot use createClient here because server may close session before // JMXEnv.ensureAll is called which will fail the test case CountdownWatcher watcher = new CountdownWatcher(); TestableZooKeeper zk = new TestableZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); if (!watcher.clientConnected.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Unable to connect to server"); } try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Should have gotten exception."); } catch (KeeperException e) { // ok, exception as expected. LOG.debug("Got exception as expected", e); } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000157 15051152474 032703 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailTest.jav0100644 0000000 0000000 00000007177 15051152474 034202 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.Test; public class SaslAuthFailTest extends SaslAuthDigestTestBase { static { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty("zookeeper.allowSaslFailedClients", "true"); try { File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write("Server {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_super=\"test\";\n" + "};\n" + "Client {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"super\"\n" + " password=\"test1\";\n" + // NOTE: wrong password ('test' != 'test1') : this is to test SASL authentication failure. "};" + "\n"); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException e) { // could not create tmp directory to hold JAAS conf file. } } private CountDownLatch authFailed = new CountDownLatch(1); private class MyWatcher extends CountdownWatcher { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { authFailed.countDown(); } else { super.process(event); } } } @Test public void testAuthFail() { try (ZooKeeper zk = createClient()) { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Should have gotten exception."); } catch (Exception e) { // ok, exception as expected. LOG.debug("Got exception as expected", e); } } @Test public void testBadSaslAuthNotifiesWatch() throws Exception { try (ZooKeeper ignored = createClient(new MyWatcher(), hostPort)) { // wait for authFailed event from client's EventThread. authFailed.await(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000176 15051152474 032704 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthMissingClientConfigTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthMissingClien0100644 0000000 0000000 00000007063 15051152474 034266 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.junit.jupiter.api.Test; public class SaslAuthMissingClientConfigTest extends SaslAuthDigestTestBase { static { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); // This configuration section 'MyZookeeperClient', is missing from the JAAS configuration. // As a result, SASL authentication should fail, which is tested by this test (testAuth()). System.setProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, "MyZookeeperClient"); try { File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write("" + "Server {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_myuser=\"mypassword\";\n" + "};\n" + "Client {\n" + /* this 'Client' section has the correct password, but we're not configured to use it - we're configured instead by the above System.setProperty(...LOGIN_CONTEXT_NAME_KEY...) to use the (nonexistent) 'MyZookeeperClient' section. */ " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"myuser\"\n" + " password=\"mypassword\";\n" + "};\n"); fwriter.close(); System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); } catch (IOException e) { // could not create tmp directory to hold JAAS conf file : test will fail now. } } @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Should have gotten exception."); } catch (KeeperException e) { // ok, exception as expected. LOG.debug("Got exception as expected", e); } finally { zk.close(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000175 15051152474 032703 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredFailNoSASLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredFail0100644 0000000 0000000 00000005055 15051152474 034255 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SaslAuthRequiredFailNoSASLTest extends SaslAuthDigestTestBase { @BeforeAll public static void setup() { System.setProperty(SaslTestUtil.requireSASLAuthProperty, "true"); System.setProperty(SaslTestUtil.authProviderProperty, SaslTestUtil.authProvider); } @AfterAll public static void clearSetup() { System.clearProperty(SaslTestUtil.requireSASLAuthProperty); System.clearProperty(SaslTestUtil.authProviderProperty); } @Test public void testClientOpWithoutSASLConfigured() throws Exception { ZooKeeper zk = null; CountdownWatcher watcher = new CountdownWatcher(); try { zk = createClient(watcher); zk.create("/foo", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("Client is not configured with SASL authentication, so zk.create operation should fail."); } catch (KeeperException e) { assertTrue(e.code() == KeeperException.Code.SESSIONCLOSEDREQUIRESASLAUTH); // Verify that "eventually" (within the bound of timeouts) // this client closes the connection between itself and the server. watcher.waitForDisconnected(SaslTestUtil.CLIENT_DISCONNECT_TIMEOUT); } finally { if (zk != null) { zk.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000200 15051152474 032670 xustar000000000 0000000 128 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredFailWrongSASLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredFail0100644 0000000 0000000 00000005226 15051152474 034255 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SaslAuthRequiredFailWrongSASLTest extends SaslAuthDigestTestBase { @BeforeAll public static void setUpBeforeClass() { System.setProperty(SaslTestUtil.requireSASLAuthProperty, "true"); System.setProperty(SaslTestUtil.authProviderProperty, SaslTestUtil.authProvider); System.setProperty(SaslTestUtil.jaasConfig, SaslTestUtil.createJAASConfigFile("jaas_wrong.conf", "test1")); } @AfterAll public static void tearDownAfterClass() { System.clearProperty(SaslTestUtil.requireSASLAuthProperty); System.clearProperty(SaslTestUtil.authProviderProperty); System.clearProperty(SaslTestUtil.jaasConfig); } @Test public void testClientOpWithFailedSASLAuth() throws Exception { ZooKeeper zk = null; CountdownWatcher watcher = new CountdownWatcher(); try { zk = createClient(watcher); zk.create("/bar", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Client with wrong SASL config should not pass SASL authentication."); } catch (KeeperException e) { assertTrue(e.code() == KeeperException.Code.AUTHFAILED); // Verify that "eventually" this client closes the connection between itself and the server. watcher.waitForDisconnected(SaslTestUtil.CLIENT_DISCONNECT_TIMEOUT); } finally { if (zk != null) { zk.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000176 15051152474 032704 xustar000000000 0000000 126 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredMultiClientTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredMult0100644 0000000 0000000 00000007555 15051152474 034332 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import javax.security.auth.login.Configuration; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SaslAuthRequiredMultiClientTest extends SaslAuthDigestTestBase { @BeforeAll public static void setUpBeforeClass() { System.setProperty(SaslTestUtil.requireSASLAuthProperty, "true"); System.setProperty(SaslTestUtil.authProviderProperty, SaslTestUtil.authProvider); System.setProperty(SaslTestUtil.jaasConfig, SaslTestUtil.createJAASConfigFile("jaas.conf", "test")); } @AfterAll public static void tearDownAfterClass() { System.clearProperty(SaslTestUtil.requireSASLAuthProperty); System.clearProperty(SaslTestUtil.authProviderProperty); System.clearProperty(SaslTestUtil.jaasConfig); } @Test public void testClientOpWithInvalidSASLUserAuthAfterSuccessLogin() throws Exception { resetJaasConfiguration("jaas.conf", "super", "test"); try (ZooKeeper zk = createClient()) { zk.create("/foobar", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); } catch (KeeperException e) { fail("Client operation should succeed with valid SASL configuration."); } resetJaasConfiguration("jaas.conf", "super_wrong", "test"); try (ZooKeeper wrongUserZk = createClient()) { wrongUserZk.create("/bar", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Client with wrong SASL config should not pass SASL authentication."); } catch (KeeperException e) { assertEquals(KeeperException.Code.AUTHFAILED, e.code()); } } @Test public void testClientOpWithInvalidSASLPasswordAuthAfterSuccessLogin() throws Exception { resetJaasConfiguration("jaas.conf", "super", "test"); try (ZooKeeper zk = createClient()) { zk.create("/foobar", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); } catch (KeeperException e) { fail("Client operation should succeed with valid SASL configuration."); } resetJaasConfiguration("jaas.conf", "super", "test_wrongong"); try (ZooKeeper wrongPasswordZk = createClient()) { wrongPasswordZk.create("/bar", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); fail("Client with wrong SASL config should not pass SASL authentication."); } catch (KeeperException e) { assertEquals(KeeperException.Code.AUTHFAILED, e.code()); } } protected static void resetJaasConfiguration(String fileName, String userName, String password) { Configuration.setConfiguration(null); System.setProperty(SaslTestUtil.jaasConfig, SaslTestUtil.createJAASConfigFile(fileName, userName, password)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000163 15051152474 032700 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthRequiredTest0100644 0000000 0000000 00000004506 15051152474 034321 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SaslAuthRequiredTest extends SaslAuthDigestTestBase { @BeforeAll public static void setUpBeforeClass() { System.setProperty(SaslTestUtil.requireSASLAuthProperty, "true"); System.setProperty(SaslTestUtil.authProviderProperty, SaslTestUtil.authProvider); System.setProperty(SaslTestUtil.jaasConfig, SaslTestUtil.createJAASConfigFile("jaas.conf", "test")); } @AfterAll public static void tearDownAfterClass() { System.clearProperty(SaslTestUtil.requireSASLAuthProperty); System.clearProperty(SaslTestUtil.authProviderProperty); System.clearProperty(SaslTestUtil.jaasConfig); } @Test public void testClientOpWithValidSASLAuth() throws Exception { ZooKeeper zk = null; CountdownWatcher watcher = new CountdownWatcher(); try { zk = createClient(watcher); zk.create("/foobar", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); } catch (KeeperException e) { fail("Client operation should succeed with valid SASL configuration."); } finally { if (zk != null) { zk.close(); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslClientTest.java0100644 0000000 0000000 00000004545 15051152474 034060 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.client.ZKClientConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SaslClientTest extends ZKTestCase { private String existingPropertyValue = null; @BeforeEach public void setUp() { existingPropertyValue = System.getProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY); } @AfterEach public void tearDown() { // Restore the System property if it was set previously if (existingPropertyValue != null) { System.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, existingPropertyValue); } } @Test public void testSaslClientDisabled() { System.clearProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY); assertTrue(new ZKClientConfig().isSaslClientEnabled(), "SASL client disabled"); for (String value : Arrays.asList("true", "TRUE")) { System.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, value); assertTrue(new ZKClientConfig().isSaslClientEnabled(), "SASL client disabled"); } for (String value : Arrays.asList("false", "FALSE")) { System.setProperty(ZKClientConfig.ENABLE_CLIENT_SASL_KEY, value); assertFalse(new ZKClientConfig().isSaslClientEnabled(), "SASL client disabled"); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000170 15051152474 032676 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslDigestAuthOverSSLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslDigestAuthOverSS0100644 0000000 0000000 00000014323 15051152474 034220 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.ZKClientConfig.LOGIN_CONTEXT_NAME_KEY; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.net.InetSocketAddress; import javax.security.auth.login.Configuration; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Environment; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.server.ServerCnxnFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SaslDigestAuthOverSSLTest extends SaslAuthDigestTestBase { private ClientX509Util clientX509Util; private File saslConfFile; @BeforeEach @Override public void setUp() throws Exception { initSaslConfig(); clientX509Util = setUpSSLWithNoAuth(); String host = "localhost"; int port = PortAssignment.unique(); hostPort = host + ":" + port; serverFactory = ServerCnxnFactory.createFactory(); serverFactory.configure(new InetSocketAddress(host, port), maxCnxns, -1, true); super.setUp(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); clearSslSetting(clientX509Util); clearSaslConfig(); } @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Thread.sleep(1000); } catch (KeeperException e) { fail("test failed :" + e); } finally { zk.close(); } } public void initSaslConfig() { System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty(LOGIN_CONTEXT_NAME_KEY, "ClientUsingDigest"); try { File tmpDir = createTmpDir(); saslConfFile = new File(tmpDir, "jaas.conf"); PrintWriter saslConf = new PrintWriter(new FileWriter(saslConfFile)); saslConf.println("Server {"); saslConf.println("org.apache.zookeeper.server.auth.DigestLoginModule required"); saslConf.println("user_super=\"test\";"); saslConf.println("};"); saslConf.println("ClientUsingDigest {"); saslConf.println("org.apache.zookeeper.server.auth.DigestLoginModule required"); saslConf.println("username=\"super\""); saslConf.println("password=\"test\";"); saslConf.println("};"); saslConf.close(); System.setProperty(Environment.JAAS_CONF_KEY, saslConfFile.getAbsolutePath()); } catch (IOException e) { LOG.error("could not create tmp directory to hold JAAS conf file, test will fail...", e); } // refresh the SASL configuration in this JVM (making sure that we use the latest config // even if other tests already have been executed and initialized the SASL configs before) Configuration.getConfiguration().refresh(); } public void clearSaslConfig() { FileUtils.deleteQuietly(saslConfFile); System.clearProperty(Environment.JAAS_CONF_KEY); System.clearProperty("zookeeper.authProvider.1"); } public ClientX509Util setUpSSLWithNoAuth() { String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); System.setProperty("zookeeper.ssl.clientAuth", "none"); System.setProperty("zookeeper.ssl.quorum.clientAuth", "none"); ClientX509Util x509Util = new ClientX509Util(); System.setProperty(x509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(x509Util.getSslTruststorePasswdProperty(), "testpass"); System.setProperty(x509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(x509Util.getSslKeystorePasswdProperty(), "testpass"); return x509Util; } public void clearSslSetting(ClientX509Util clientX509Util) { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ZKClientConfig.SECURE_CLIENT); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty("zookeeper.ssl.clientAuth"); System.clearProperty("zookeeper.ssl.quorum.clientAuth"); clientX509Util.close(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000172 15051152474 032700 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslKerberosAuthOverSSLTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslKerberosAuthOver0100644 0000000 0000000 00000024270 15051152474 034311 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.client.ZKClientConfig.ENABLE_CLIENT_SASL_KEY; import static org.apache.zookeeper.client.ZKClientConfig.LOGIN_CONTEXT_NAME_KEY; import static org.apache.zookeeper.client.ZKClientConfig.ZK_SASL_CLIENT_USERNAME; import static org.apache.zookeeper.client.ZKClientConfig.ZOOKEEPER_SERVER_PRINCIPAL; import static org.apache.zookeeper.client.ZKClientConfig.ZOOKEEPER_SERVER_REALM; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.util.Properties; import javax.security.auth.login.Configuration; import org.apache.commons.io.FileUtils; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.Environment; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.common.ClientX509Util; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.quorum.auth.KerberosTestUtils; import org.apache.zookeeper.server.quorum.auth.MiniKdc; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class SaslKerberosAuthOverSSLTest extends ClientBase { private ClientX509Util clientX509Util; private File keytabFileForKerberosPrincipals; private File saslConfFile; private static MiniKdc kdc; private static File kdcWorkDir; private static Properties conf; @BeforeAll public static void setupKdc() { startMiniKdc(); } @AfterAll public static void tearDownKdc() { stopMiniKdc(); FileUtils.deleteQuietly(kdcWorkDir); } @BeforeEach @Override public void setUp() throws Exception { initSaslConfig(); clientX509Util = setUpSSLWithNoAuth(); String host = "localhost"; int port = PortAssignment.unique(); hostPort = host + ":" + port; serverFactory = ServerCnxnFactory.createFactory(); serverFactory.configure(new InetSocketAddress(host, port), maxCnxns, -1, true); super.setUp(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); clearSslSetting(clientX509Util); clearSaslConfig(); } @Test public void testAuth() throws Exception { ZooKeeper zk = createClient(); try { zk.create("/path1", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); Thread.sleep(1000); } catch (KeeperException e) { fail("test failed :" + e); } finally { zk.close(); } } public void initSaslConfig() throws Exception { // registering the server and client users in the KDC mini server keytabFileForKerberosPrincipals = new File(KerberosTestUtils.getKeytabFile()); String clientPrincipal = KerberosTestUtils.getClientPrincipal(); String serverPrincipal = KerberosTestUtils.getServerPrincipal(); clientPrincipal = clientPrincipal.substring(0, clientPrincipal.lastIndexOf("@")); serverPrincipal = serverPrincipal.substring(0, serverPrincipal.lastIndexOf("@")); kdc.createPrincipal(keytabFileForKerberosPrincipals, clientPrincipal, serverPrincipal); // client-side SASL config System.setProperty(ZOOKEEPER_SERVER_PRINCIPAL, KerberosTestUtils.getServerPrincipal()); System.setProperty(ENABLE_CLIENT_SASL_KEY, "true"); System.setProperty(ZOOKEEPER_SERVER_REALM, KerberosTestUtils.getRealm()); System.setProperty(LOGIN_CONTEXT_NAME_KEY, "ClientUsingKerberos"); // server side SASL config System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); System.setProperty(SaslTestUtil.requireSASLAuthProperty, "true"); // generating the SASL config to use (contains sections both for the client and the server) // note: we use "refreshKrb5Config=true" to refresh the kerberos config in the JVM, // making sure that we use the latest config even if other tests already have been executed // and initialized the kerberos client configs before) try { File tmpDir = createTmpDir(); saslConfFile = new File(tmpDir, "jaas.conf"); PrintWriter saslConf = new PrintWriter(new FileWriter(saslConfFile)); saslConf.println("Server {"); saslConf.println(" com.sun.security.auth.module.Krb5LoginModule required"); saslConf.println(" storeKey=\"true\""); saslConf.println(" useTicketCache=\"false\""); saslConf.println(" useKeyTab=\"true\""); saslConf.println(" doNotPrompt=\"true\""); saslConf.println(" debug=\"true\""); saslConf.println(" refreshKrb5Config=\"true\""); saslConf.println(" keyTab=\"" + keytabFileForKerberosPrincipals.getAbsolutePath() + "\""); saslConf.println(" principal=\"" + KerberosTestUtils.getServerPrincipal() + "\";"); saslConf.println("};"); saslConf.println("ClientUsingKerberos {"); saslConf.println(" com.sun.security.auth.module.Krb5LoginModule required"); saslConf.println(" storeKey=\"false\""); saslConf.println(" useTicketCache=\"false\""); saslConf.println(" useKeyTab=\"true\""); saslConf.println(" doNotPrompt=\"true\""); saslConf.println(" debug=\"true\""); saslConf.println(" refreshKrb5Config=\"true\""); saslConf.println(" keyTab=\"" + keytabFileForKerberosPrincipals.getAbsolutePath() + "\""); saslConf.println(" principal=\"" + KerberosTestUtils.getClientPrincipal() + "\";"); saslConf.println("};"); saslConf.close(); System.setProperty(Environment.JAAS_CONF_KEY, saslConfFile.getAbsolutePath()); } catch (IOException e) { LOG.error("could not create tmp directory to hold JAAS conf file, test will fail...", e); } // refresh the SASL configuration in this JVM (making sure that we use the latest config // even if other tests already have been executed and initialized the SASL configs before) Configuration.getConfiguration().refresh(); } public void clearSaslConfig() { FileUtils.deleteQuietly(keytabFileForKerberosPrincipals); FileUtils.deleteQuietly(saslConfFile); System.clearProperty(Environment.JAAS_CONF_KEY); System.clearProperty(ZK_SASL_CLIENT_USERNAME); System.clearProperty(ENABLE_CLIENT_SASL_KEY); System.clearProperty(LOGIN_CONTEXT_NAME_KEY); System.clearProperty("zookeeper.authProvider.1"); System.clearProperty(SaslTestUtil.requireSASLAuthProperty); System.clearProperty(ZOOKEEPER_SERVER_PRINCIPAL); } public ClientX509Util setUpSSLWithNoAuth() { String testDataPath = System.getProperty("test.data.dir", "src/test/resources/data"); System.setProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY, "org.apache.zookeeper.server.NettyServerCnxnFactory"); System.setProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET, "org.apache.zookeeper.ClientCnxnSocketNetty"); System.setProperty(ZKClientConfig.SECURE_CLIENT, "true"); System.setProperty("zookeeper.ssl.clientAuth", "none"); System.setProperty("zookeeper.ssl.quorum.clientAuth", "none"); ClientX509Util x509Util = new ClientX509Util(); System.setProperty(x509Util.getSslTruststoreLocationProperty(), testDataPath + "/ssl/testTrustStore.jks"); System.setProperty(x509Util.getSslTruststorePasswdProperty(), "testpass"); System.setProperty(x509Util.getSslKeystoreLocationProperty(), testDataPath + "/ssl/testKeyStore.jks"); System.setProperty(x509Util.getSslKeystorePasswdProperty(), "testpass"); return x509Util; } public void clearSslSetting(ClientX509Util clientX509Util) { System.clearProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY); System.clearProperty(ZKClientConfig.ZOOKEEPER_CLIENT_CNXN_SOCKET); System.clearProperty(ZKClientConfig.SECURE_CLIENT); System.clearProperty(clientX509Util.getSslTruststoreLocationProperty()); System.clearProperty(clientX509Util.getSslTruststorePasswdProperty()); System.clearProperty(clientX509Util.getSslKeystoreLocationProperty()); System.clearProperty(clientX509Util.getSslKeystorePasswdProperty()); System.clearProperty("javax.net.debug"); System.clearProperty("zookeeper.ssl.clientAuth"); System.clearProperty("zookeeper.ssl.quorum.clientAuth"); clientX509Util.close(); } public static void startMiniKdc() { try { kdcWorkDir = createEmptyTestDir(); conf = MiniKdc.createConf(); conf.setProperty("debug", "true"); kdc = new MiniKdc(conf, kdcWorkDir); kdc.start(); } catch (Exception e) { throw new RuntimeException("failed to start MiniKdc", e); } } public static void stopMiniKdc() { if (kdc != null) { kdc.stop(); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sasl0100644 0000000 0000000 00000000160 15051152474 032675 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslSuperUserTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslSuperUserTest.ja0100644 0000000 0000000 00000015702 15051152474 034245 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Perms; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class SaslSuperUserTest extends SaslAuthDigestTestBase { private static Id otherSaslUser = new Id("sasl", "joe"); private static Id otherDigestUser; private static String oldAuthProvider; private static String oldClientConfigSection; private static String oldLoginConfig; private static String oldSuperUser; @BeforeAll public static void setupStatic() throws Exception { oldAuthProvider = System.setProperty("zookeeper.authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"); oldClientConfigSection = System.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY); File tmpDir = createTmpDir(); File saslConfFile = new File(tmpDir, "jaas.conf"); FileWriter fwriter = new FileWriter(saslConfFile); fwriter.write("" + "Server {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " user_super_duper=\"test\"\n" + " user_other_super=\"test\";\n" + "};\n" + "Client {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"super_duper\"\n" + " password=\"test\";\n" + "};" + "OtherClient {\n" + " org.apache.zookeeper.server.auth.DigestLoginModule required\n" + " username=\"other_super\"\n" + " password=\"test\";\n" + "};" + "\n"); fwriter.close(); oldLoginConfig = System.setProperty("java.security.auth.login.config", saslConfFile.getAbsolutePath()); oldSuperUser = System.setProperty(ZooKeeperServer.SASL_SUPER_USER, "super_duper"); otherDigestUser = new Id("digest", DigestAuthenticationProvider.generateDigest("jack:jack")); } @AfterAll public static void cleanupStatic() { restoreProperty("zookeeper.authProvider.1", oldAuthProvider); restoreProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, oldClientConfigSection); restoreProperty("java.security.auth.login.config", oldLoginConfig); restoreProperty(ZooKeeperServer.SASL_SUPER_USER, oldSuperUser); } private static void restoreProperty(String property, String oldValue) { if (oldValue != null) { System.setProperty(property, oldValue); } else { System.clearProperty(property); } } private AtomicInteger authFailed = new AtomicInteger(0); @Override protected TestableZooKeeper createClient(String hp) throws IOException, InterruptedException { MyWatcher watcher = new MyWatcher(); return createClient(watcher, hp); } private class MyWatcher extends CountdownWatcher { @Override public synchronized void process(WatchedEvent event) { if (event.getState() == KeeperState.AuthFailed) { authFailed.incrementAndGet(); } else { super.process(event); } } } private void connectAndPerformSuperOps() throws Exception { ZooKeeper zk = createClient(); try { zk.create("/digest_read", null, Arrays.asList(new ACL(Perms.READ, otherDigestUser)), CreateMode.PERSISTENT); zk.create("/digest_read/sub", null, Arrays.asList(new ACL(Perms.READ, otherDigestUser)), CreateMode.PERSISTENT); zk.create("/sasl_read", null, Arrays.asList(new ACL(Perms.READ, otherSaslUser)), CreateMode.PERSISTENT); zk.create("/sasl_read/sub", null, Arrays.asList(new ACL(Perms.READ, otherSaslUser)), CreateMode.PERSISTENT); zk.delete("/digest_read/sub", -1); zk.delete("/digest_read", -1); zk.delete("/sasl_read/sub", -1); zk.delete("/sasl_read", -1); } finally { zk.close(); } } @Test public void testSuperIsSuper() throws Exception { connectAndPerformSuperOps(); //If the test fails it will most likely fail with a NoAuth exception before it ever gets to this assertion assertEquals(authFailed.get(), 0); } @Test public void testOtherSuperIsSuper() throws Exception { String prevSection = System.setProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, "OtherClient"); // KLUDGE: We do this quite late, as the server has been // started at this point--but the implementation currently // looks at the properties each time a SASL negotiation completes. String superUser1Prop = ZooKeeperServer.SASL_SUPER_USER + ".1"; String prevSuperUser1 = System.setProperty(superUser1Prop, "other_super"); try { connectAndPerformSuperOps(); //If the test fails it will most likely fail with a NoAuth exception before it ever gets to this assertion assertEquals(authFailed.get(), 0); } finally { restoreProperty(superUser1Prop, prevSuperUser1); restoreProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY, prevSection); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslTestUtil.java0100644 0000000 0000000 00000005366 15051152474 033561 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class SaslTestUtil extends ClientBase { // The maximum time (in milliseconds) a client should take to observe // a disconnect event of the same client from server. static Integer CLIENT_DISCONNECT_TIMEOUT = 3000; static String SUPER_USER_NAME = "super"; static String requireSASLAuthProperty = "zookeeper.sessionRequireClientSASLAuth"; static String authProviderProperty = "zookeeper.authProvider.1"; static String authProvider = "org.apache.zookeeper.server.auth.SASLAuthenticationProvider"; static String digestLoginModule = "org.apache.zookeeper.server.auth.DigestLoginModule"; static String jaasConfig = "java.security.auth.login.config"; static String createJAASConfigFile(String fileName, String password) { return createJAASConfigFile(fileName, SUPER_USER_NAME, password); } static String createJAASConfigFile(String fileName, String userName, String password) { String ret = null; try { File tmpDir = createTmpDir(); File jaasFile = new File(tmpDir, fileName); FileWriter fwriter = new FileWriter(jaasFile); fwriter.write("" + "Server {\n" + " " + digestLoginModule + " required\n" + " user_super=\"test\";\n" + "};\n" + "Client {\n" + " " + digestLoginModule + " required\n" + " username=\"" + userName + "\"\n" + " password=\"" + password + "\";\n" + "};" + "\n"); fwriter.close(); ret = jaasFile.getAbsolutePath(); } catch (IOException e) { fail("Unable to create JaaS configuration file!"); } return ret; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ServerCnxnTest.java0100644 0000000 0000000 00000010557 15051152474 034114 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; import org.apache.zookeeper.server.NIOServerCnxnFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServerCnxnTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(ServerCnxnTest.class); private static int cnxnTimeout = 1000; @BeforeEach public void setUp() throws Exception { System.setProperty(NIOServerCnxnFactory.ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT, Integer.toString(cnxnTimeout)); super.setUp(); } @AfterEach public void tearDown() throws Exception { super.tearDown(); System.clearProperty(NIOServerCnxnFactory.ZOOKEEPER_NIO_SESSIONLESS_CNXN_TIMEOUT); } @Test public void testServerCnxnExpiry() throws Exception { verify("ruok", "imok"); // Expiry time is (now/cnxnTimeout + 1)*cnxnTimeout // Range is (now + cnxnTimeout) to (now + 2*cnxnTimeout) // Add 1s buffer to be safe. String resp = sendRequest("ruok", 2 * cnxnTimeout + 1000); assertEquals("", resp, "Connection should have closed"); } private void verify(String cmd, String expected) throws IOException { String resp = sendRequest(cmd, 0); LOG.info("cmd {} expected {} got {}", cmd, expected, resp); assertTrue(resp.contains(expected)); } private String sendRequest(String cmd, int delay) throws IOException { HostPort hpobj = ClientBase.parseHostPortList(hostPort).get(0); return send4LetterWord(hpobj.host, hpobj.port, cmd, delay); } private static String send4LetterWord( String host, int port, String cmd, int delay) throws IOException { LOG.info("connecting to {} {}", host, port); Socket sock = new Socket(host, port); BufferedReader reader = null; try { try { LOG.info("Sleeping for {}ms", delay); Thread.sleep(delay); } catch (InterruptedException e) { // ignore } OutputStream outstream = sock.getOutputStream(); outstream.write(cmd.getBytes()); outstream.flush(); // this replicates NC - close the output stream before reading sock.shutdownOutput(); reader = new BufferedReader(new InputStreamReader(sock.getInputStream())); StringBuilder sb = readLine(reader); return sb.toString(); } finally { sock.close(); if (reader != null) { reader.close(); } } } private static StringBuilder readLine(BufferedReader reader) { StringBuilder sb = new StringBuilder(); String line; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException ioe) { // During connection expiry the server will close the connection. // After the socket is closed, when the client tries to read a // line of text it will throw java.net.SocketException. // @see jira issue ZOOKEEPER-1862 LOG.info("Connnection is expired", ioe); } return sb; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sess0100644 0000000 0000000 00000000166 15051152474 032716 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionInvalidationTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionInvalidationT0100644 0000000 0000000 00000007740 15051152474 034350 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import org.apache.jute.BinaryOutputArchive; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooDefs.OpCode; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.proto.ConnectRequest; import org.apache.zookeeper.proto.CreateRequest; import org.apache.zookeeper.proto.RequestHeader; import org.junit.jupiter.api.Test; public class SessionInvalidationTest extends ClientBase { /** * Test solution for ZOOKEEPER-1208. Verify that operations are not * accepted after a close session. * * We're using our own marshalling here in order to force an operation * after the session is closed (ZooKeeper.class will not allow this). Also * by filling the pipe with operations it increases the likelyhood that * the server will process the create before FinalRequestProcessor * removes the session from the tracker. */ @Test public void testCreateAfterCloseShouldFail() throws Exception { for (int i = 0; i < 10; i++) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BinaryOutputArchive boa = BinaryOutputArchive.getArchive(baos); // open a connection boa.writeInt(44, "len"); ConnectRequest conReq = new ConnectRequest(0, 0, 30000, 0, new byte[16], false); conReq.serialize(boa, "connect"); // close connection boa.writeInt(8, "len"); RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.closeSession); h.serialize(boa, "header"); // create ephemeral znode boa.writeInt(52, "len"); // We'll fill this in later RequestHeader header = new RequestHeader(2, OpCode.create); header.serialize(boa, "header"); CreateRequest createReq = new CreateRequest("/foo" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, 1); createReq.serialize(boa, "request"); baos.close(); System.out.println("Length:" + baos.toByteArray().length); String[] hp = hostPort.split(":"); Socket sock = new Socket(hp[0], Integer.parseInt(hp[1])); InputStream resultStream = null; try { OutputStream outstream = sock.getOutputStream(); byte[] data = baos.toByteArray(); outstream.write(data); outstream.flush(); resultStream = sock.getInputStream(); byte[] b = new byte[10000]; int len; while ((len = resultStream.read(b)) >= 0) { // got results System.out.println("gotlen:" + len); } } finally { if (resultStream != null) { resultStream.close(); } sock.close(); } } ZooKeeper zk = createClient(); assertEquals(1, zk.getChildren("/", false).size()); zk.close(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionTest.java0100644 0000000 0000000 00000034420 15051152474 033435 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SessionTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(SessionTest.class); private static final String HOSTPORT = "127.0.0.1:" + PortAssignment.unique(); private ServerCnxnFactory serverFactory; private ZooKeeperServer zs; private CountDownLatch startSignal; File tmpDir; private final int TICK_TIME = 3000; @BeforeEach public void setUp() throws Exception { if (tmpDir == null) { tmpDir = ClientBase.createTmpDir(); } ClientBase.setupTestEnv(); zs = new ZooKeeperServer(tmpDir, tmpDir, TICK_TIME); final int PORT = Integer.parseInt(HOSTPORT.split(":")[1]); serverFactory = ServerCnxnFactory.createFactory(PORT, -1); serverFactory.startup(zs); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server up"); } @AfterEach public void tearDown() throws Exception { serverFactory.shutdown(); zs.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server down"); } private static class CountdownWatcher implements Watcher { volatile CountDownLatch clientConnected = new CountDownLatch(1); final CountDownLatch sessionTerminated = new CountDownLatch(1); public void process(WatchedEvent event) { switch (event.getState()) { case SyncConnected: clientConnected.countDown(); break; case AuthFailed: case Expired: case Closed: sessionTerminated.countDown(); break; } } } private DisconnectableZooKeeper createClient() throws IOException, InterruptedException { CountdownWatcher watcher = new CountdownWatcher(); return createClient(CONNECTION_TIMEOUT, watcher); } private DisconnectableZooKeeper createClient(int timeout) throws IOException, InterruptedException { CountdownWatcher watcher = new CountdownWatcher(); return createClient(timeout, watcher); } private DisconnectableZooKeeper createClient(int timeout, CountdownWatcher watcher) throws IOException, InterruptedException { DisconnectableZooKeeper zk = new DisconnectableZooKeeper(HOSTPORT, timeout, watcher); if (!watcher.clientConnected.await(timeout, TimeUnit.MILLISECONDS)) { fail("Unable to connect to server"); } return zk; } // TODO this test is failing due to client close race condition fixing in separate patch for ZOOKEEPER-63 // /** // * this test checks to see if the sessionid that was created for the // * first zookeeper client can be reused for the second one immidiately // * after the first client closes and the new client resues them. // * @throws IOException // * @throws InterruptedException // * @throws KeeperException // */ // public void testSessionReuse() throws IOException, InterruptedException { // ZooKeeper zk = createClient(); // // long sessionId = zk.getSessionId(); // byte[] passwd = zk.getSessionPasswd(); // zk.close(); // // zk.close(); // // LOG.info("Closed first session"); // // startSignal = new CountDownLatch(1); // zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, this, // sessionId, passwd); // startSignal.await(); // // LOG.info("Opened reuse"); // // assertEquals(sessionId, zk.getSessionId()); // // zk.close(); // } private class MyWatcher implements Watcher { private String name; public MyWatcher(String name) { this.name = name; } public void process(WatchedEvent event) { LOG.info("{} event:{} {} {}", name, event.getState(), event.getType(), event.getPath()); if (event.getState() == KeeperState.SyncConnected && startSignal != null && startSignal.getCount() > 0) { startSignal.countDown(); } } } /** * This test verifies that when the session id is reused, and the original * client is disconnected, but not session closed, that the server * will remove ephemeral nodes created by the original session. */ @Test public void testSession() throws IOException, InterruptedException, KeeperException { DisconnectableZooKeeper zk = createClient(); zk.create("/e", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); LOG.info("zk with session id 0x{} was destroyed!", Long.toHexString(zk.getSessionId())); // disconnect the client by killing the socket, not sending the // session disconnect to the server as usual. This allows the test // to verify disconnect handling zk.disconnect(); Stat stat = new Stat(); startSignal = new CountDownLatch(1); zk = new DisconnectableZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, new MyWatcher("testSession"), zk.getSessionId(), zk.getSessionPasswd()); startSignal.await(); LOG.info("zk with session id 0x{} was created!", Long.toHexString(zk.getSessionId())); zk.getData("/e", false, stat); LOG.info("After get data /e"); zk.close(); zk = createClient(); assertEquals(null, zk.exists("/e", false)); LOG.info("before close zk with session id 0x{}!", Long.toHexString(zk.getSessionId())); zk.close(); try { zk.getData("/e", false, stat); fail("Should have received a SessionExpiredException"); } catch (KeeperException.SessionExpiredException e) { } AsyncCallback.DataCallback cb = new AsyncCallback.DataCallback() { String status = "not done"; public void processResult(int rc, String p, Object c, byte[] b, Stat s) { synchronized (this) { status = KeeperException.Code.get(rc).toString(); this.notify(); } } public String toString() { return status; } }; zk.getData("/e", false, cb, null); synchronized (cb) { if (cb.toString().equals("not done")) { cb.wait(1000); } } assertEquals(KeeperException.Code.SESSIONEXPIRED.toString(), cb.toString()); } /** * Make sure that we cannot have two connections with the same * session id. * * @throws IOException * @throws InterruptedException * @throws KeeperException */ @Test public void testSessionMove() throws Exception { String[] hostPorts = HOSTPORT.split(","); DisconnectableZooKeeper zk = new DisconnectableZooKeeper(hostPorts[0], CONNECTION_TIMEOUT, new MyWatcher("0")); zk.create("/sessionMoveTest", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); // we want to loop through the list twice for (int i = 0; i < hostPorts.length * 2; i++) { zk.dontReconnect(); // This should stomp the zk handle DisconnectableZooKeeper zknew = new DisconnectableZooKeeper(hostPorts[(i + 1) % hostPorts.length], CONNECTION_TIMEOUT, new MyWatcher(Integer.toString( i + 1)), zk.getSessionId(), zk.getSessionPasswd()); zknew.sync("/"); LOG.info("{} Sync succeed", hostPorts[(i + 1) % hostPorts.length]); zknew.setData("/", new byte[1], -1); try { zk.setData("/", new byte[1], -1); fail("Should have lost the connection"); } catch (KeeperException.ConnectionLossException e) { LOG.info("Got connection loss exception as expected"); } //zk.close(); zk = zknew; } zk.close(); } /** * This test makes sure that duplicate state changes are not communicated * to the client watcher. For example we should not notify state as * "disconnected" if the watch has already been disconnected. In general * we don't consider a dup state notification if the event type is * not "None" (ie non-None communicates an event). */ @Test public void testSessionStateNoDupStateReporting() throws IOException, InterruptedException, KeeperException { final int TIMEOUT = 3000; DupWatcher watcher = new DupWatcher(); ZooKeeper zk = createClient(TIMEOUT, watcher); // shutdown the server serverFactory.shutdown(); watcher.sessionTerminated.await(); // verify that there is no duplicated disconnected event. List states = Arrays.asList( KeeperState.SyncConnected, KeeperState.Disconnected, KeeperState.Expired ); assertEquals(states, watcher.states); zk.close(); } /** * Verify access to the negotiated session timeout. */ @Test public void testSessionTimeoutAccess() throws Exception { // validate typical case - requested == negotiated DisconnectableZooKeeper zk = createClient(TICK_TIME * 4); assertEquals(TICK_TIME * 4, zk.getSessionTimeout()); // make sure tostring works in both cases LOG.info(zk.toString()); zk.close(); LOG.info(zk.toString()); // validate lower limit zk = createClient(TICK_TIME); assertEquals(TICK_TIME * 2, zk.getSessionTimeout()); LOG.info(zk.toString()); zk.close(); LOG.info(zk.toString()); // validate upper limit zk = createClient(TICK_TIME * 30); assertEquals(TICK_TIME * 20, zk.getSessionTimeout()); LOG.info(zk.toString()); zk.close(); LOG.info(zk.toString()); } private class DupWatcher extends CountdownWatcher { public List states = new LinkedList<>(); public void process(WatchedEvent event) { super.process(event); if (event.getType() == EventType.None) { states.add(event.getState()); } } } @Test public void testMinMaxSessionTimeout() throws Exception { // override the defaults final int MINSESS = 20000; final int MAXSESS = 240000; { ZooKeeperServer zs = serverFactory.getZooKeeperServer(); zs.setMinSessionTimeout(MINSESS); zs.setMaxSessionTimeout(MAXSESS); } // validate typical case - requested == negotiated int timeout = 120000; DisconnectableZooKeeper zk = createClient(timeout); assertEquals(timeout, zk.getSessionTimeout()); // make sure tostring works in both cases LOG.info(zk.toString()); zk.close(); LOG.info(zk.toString()); // validate lower limit zk = createClient(MINSESS / 2); assertEquals(MINSESS, zk.getSessionTimeout()); LOG.info(zk.toString()); zk.close(); LOG.info(zk.toString()); // validate upper limit zk = createClient(MAXSESS * 2); assertEquals(MAXSESS, zk.getSessionTimeout()); LOG.info(zk.toString()); zk.close(); LOG.info(zk.toString()); } @Test public void testMaximumCnxnPerIP() throws Exception { final int maxClientCnxnsPerIP = 3; serverFactory.setMaxClientCnxnsPerHost(maxClientCnxnsPerIP); ZooKeeper[] clients = new ZooKeeper[maxClientCnxnsPerIP + 1]; for (int i = 0; i < clients.length; i++) { CountdownWatcher watcher = new CountdownWatcher(); // wait for 3s int timeout = 3000; clients[i] = new DisconnectableZooKeeper(HOSTPORT, timeout, watcher); boolean result = watcher.clientConnected.await(timeout, TimeUnit.MILLISECONDS); if (i >= maxClientCnxnsPerIP) { assertFalse(result); } else { assertTrue(result); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sess0100644 0000000 0000000 00000000161 15051152474 032711 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionTimeoutTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionTimeoutTest.j0100644 0000000 0000000 00000023171 15051152474 034315 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.Time; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SessionTimeoutTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(SessionTimeoutTest.class); private TestableZooKeeper zk; @BeforeEach public void setUp() throws Exception { super.setUp(); zk = createClient(); } private static class ExpiredWatcher implements Watcher { public volatile CompletableFuture expired = new CompletableFuture<>(); synchronized void reset() { expired = new CompletableFuture<>(); } @Override public synchronized void process(WatchedEvent event) { if (event.getState() == Event.KeeperState.Expired) { expired.complete(null); } } } private static class BusyServer implements AutoCloseable { private final ServerSocket server; private final Socket client; public BusyServer() throws IOException { this.server = new ServerSocket(0, 1); this.client = new Socket("127.0.0.1", server.getLocalPort()); } public int getLocalPort() { return server.getLocalPort(); } public String getHostPort() { return String.format("127.0.0.1:%d", getLocalPort()); } @Override public void close() throws Exception { client.close(); server.close(); } } @Test public void testSessionExpiration() throws InterruptedException, KeeperException { final CountDownLatch expirationLatch = new CountDownLatch(1); Watcher watcher = event -> { if (event.getState() == Watcher.Event.KeeperState.Expired) { expirationLatch.countDown(); } }; zk.exists("/foo", watcher); zk.getTestable().injectSessionExpiration(); assertTrue(expirationLatch.await(5, TimeUnit.SECONDS)); boolean gotException = false; try { zk.exists("/foo", false); fail("Should have thrown a SessionExpiredException"); } catch (KeeperException.SessionExpiredException e) { // correct gotException = true; } assertTrue(gotException); } @Test public void testSessionRecoveredAfterMultipleFailedAttempts() throws Exception { // stop client also to gain less distraction zk.close(); try (BusyServer busyServer = new BusyServer()) { List servers = Arrays.asList( busyServer.getHostPort(), busyServer.getHostPort(), hostPort, busyServer.getHostPort(), busyServer.getHostPort(), busyServer.getHostPort() ); String connectString = String.join(",", servers); zk = createClient(new CountdownWatcher(), connectString); stopServer(); // Wait beyond connectTimeout but not sessionTimeout. Thread.sleep(zk.getSessionTimeout() / 2); CompletableFuture connected = new CompletableFuture<>(); zk.register(event -> { if (event.getState() == Watcher.Event.KeeperState.SyncConnected) { connected.complete(null); } else { connected.completeExceptionally(new KeeperException.SessionExpiredException()); } }); startServer(); connected.join(); } } @Test public void testSessionExpirationAfterAllServerDown() throws Exception { // stop client also to gain less distraction zk.close(); // given: established session int sessionTimeout = 3000; // small connection timeout to gain quick ci feedback ExpiredWatcher watcher = new ExpiredWatcher(); zk = createClient(new CountdownWatcher(), hostPort, sessionTimeout); zk.register(watcher); // when: all server down long start = Time.currentElapsedTime(); zk.sync("/"); // touch timeout counts stopServer(); // then: get Expired after session timeout watcher.expired.join(); long elapsed = Time.currentElapsedTime() - start; assertThat(elapsed, greaterThanOrEqualTo((long) zk.getSessionTimeout())); assertThat(elapsed, lessThan(zk.getSessionTimeout() * 10L)); // then: future request will get SessionExpiredException assertThrows(KeeperException.SessionExpiredException.class, () -> zk.exists("/", null)); } @Test public void testSessionExpirationWhenNoServerUp() throws Exception { // stop client also to gain less distraction zk.close(); // given: unavailable cluster stopServer(); // when: try to establish a brand-new session int sessionTimeout = 300; // small connection timeout to gain quick ci feedback ExpiredWatcher watcher = new ExpiredWatcher(); try (ZooKeeper zk = new ZooKeeper(hostPort, sessionTimeout, watcher)) { // then: never Expired assertThrows(TimeoutException.class, () -> watcher.expired.get(3 * sessionTimeout, TimeUnit.MILLISECONDS)); assertThrows(KeeperException.ConnectionLossException.class, () -> zk.exists("/", null)); } } @Test public void testQueueEvent() throws InterruptedException, KeeperException { final CountDownLatch eventLatch = new CountDownLatch(1); Watcher watcher = event -> { if (event.getType() == Watcher.Event.EventType.NodeDataChanged) { if (event.getPath().equals("/foo/bar")) { eventLatch.countDown(); } } }; zk.exists("/foo/bar", watcher); WatchedEvent event = new WatchedEvent(Watcher.Event.EventType.NodeDataChanged, Watcher.Event.KeeperState.SyncConnected, "/foo/bar"); zk.getTestable().queueEvent(event); assertTrue(eventLatch.await(5, TimeUnit.SECONDS)); } /** * Make sure ephemerals get cleaned up when session disconnects. */ @Test public void testSessionDisconnect() throws KeeperException, InterruptedException, IOException { zk.create("/sdisconnect", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertNotNull(zk.exists("/sdisconnect", null), "Ephemeral node has not been created"); zk.close(); zk = createClient(); assertNull(zk.exists("/sdisconnect", null), "Ephemeral node shouldn't exist after client disconnect"); } /** * Make sure ephemerals are kept when session restores. */ @Test public void testSessionRestore() throws KeeperException, InterruptedException, IOException { zk.create("/srestore", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertNotNull(zk.exists("/srestore", null), "Ephemeral node has not been created"); zk.disconnect(); zk.close(); zk = createClient(); assertNotNull(zk.exists("/srestore", null), "Ephemeral node should be present when session is restored"); } /** * Make sure ephemerals are kept when server restarts. */ @Test public void testSessionSurviveServerRestart() throws Exception { zk.create("/sdeath", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); assertNotNull(zk.exists("/sdeath", null), "Ephemeral node has not been created"); zk.disconnect(); stopServer(); startServer(); zk = createClient(); assertNotNull(zk.exists("/sdeath", null), "Ephemeral node should be present when server restarted"); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sess0100644 0000000 0000000 00000000166 15051152474 032716 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionTrackerCheckTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionTrackerCheckT0100644 0000000 0000000 00000016273 15051152474 034261 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.fail; import java.util.concurrent.ConcurrentHashMap; import org.apache.zookeeper.KeeperException.SessionExpiredException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.server.SessionTracker.Session; import org.apache.zookeeper.server.SessionTracker.SessionExpirer; import org.apache.zookeeper.server.ZooKeeperServerListener; import org.apache.zookeeper.server.quorum.LeaderSessionTracker; import org.apache.zookeeper.server.quorum.LearnerSessionTracker; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Validate various type of sessions against leader session tracker and learner * session tracker */ public class SessionTrackerCheckTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(SessionTrackerCheckTest.class); public static final int TICK_TIME = 1000; public static final int CONNECTION_TIMEOUT = TICK_TIME * 10; private ConcurrentHashMap sessionsWithTimeouts = new ConcurrentHashMap<>(); private class Expirer implements SessionExpirer { long sid; public Expirer(long sid) { this.sid = sid; } public void expire(Session session) { } public long getServerId() { return sid; } } @BeforeEach public void setUp() throws Exception { sessionsWithTimeouts.clear(); } @AfterEach public void tearDown() throws Exception { } @Test public void testLearnerSessionTracker() throws Exception { Expirer expirer = new Expirer(1); // With local session on LearnerSessionTracker tracker = new LearnerSessionTracker(expirer, sessionsWithTimeouts, TICK_TIME, expirer.sid, true, testZKSListener()); // Unknown session long sessionId = 0xb100ded; try { tracker.checkSession(sessionId, null); fail("Unknown session should have failed"); } catch (SessionExpiredException e) { // Get expected exception } // Global session sessionsWithTimeouts.put(sessionId, CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Global session should not fail"); } // Local session sessionId = tracker.createSession(CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Local session should not fail"); } // During session upgrade sessionsWithTimeouts.put(sessionId, CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Session during upgrade should not fail"); } // With local session off tracker = new LearnerSessionTracker(expirer, sessionsWithTimeouts, TICK_TIME, expirer.sid, false, testZKSListener()); // Should be noop sessionId = 0xdeadbeef; try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Should not get any exception"); } } @Test public void testLeaderSessionTracker() throws Exception { Expirer expirer = new Expirer(2); // With local session on LeaderSessionTracker tracker = new LeaderSessionTracker(expirer, sessionsWithTimeouts, TICK_TIME, expirer.sid, true, testZKSListener()); // Local session from other server long sessionId = ((expirer.sid + 1) << 56) + 1; try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("local session from other server should not fail"); } // Track global session tracker.trackSession(sessionId, CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Global session should not fail"); } try { tracker.checkGlobalSession(sessionId, null); } catch (Exception e) { fail("Global session should not fail " + e); } // Local session from the leader sessionId = tracker.createSession(CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Local session on the leader should not fail"); } // During session upgrade tracker.trackSession(sessionId, CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Session during upgrade should not fail"); } try { tracker.checkGlobalSession(sessionId, null); } catch (Exception e) { fail("Global session should not fail " + e); } // With local session off tracker = new LeaderSessionTracker(expirer, sessionsWithTimeouts, TICK_TIME, expirer.sid, false, testZKSListener()); // Global session sessionId = 0xdeadbeef; tracker.trackSession(sessionId, CONNECTION_TIMEOUT); try { tracker.checkSession(sessionId, null); } catch (Exception e) { fail("Global session should not fail"); } try { tracker.checkGlobalSession(sessionId, null); } catch (Exception e) { fail("Global session should not fail"); } // Local session from other server sessionId = ((expirer.sid + 1) << 56) + 2; try { tracker.checkSession(sessionId, null); fail("local session from other server should fail"); } catch (SessionExpiredException e) { // Got expected exception } // Local session from the leader sessionId = ((expirer.sid) << 56) + 2; try { tracker.checkSession(sessionId, null); fail("local session from the leader should fail"); } catch (SessionExpiredException e) { // Got expected exception } } ZooKeeperServerListener testZKSListener() { return new ZooKeeperServerListener() { @Override public void notifyStopping(String errMsg, int exitCode) { } }; } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Sess0100644 0000000 0000000 00000000161 15051152474 032711 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionUpgradeTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SessionUpgradeTest.j0100644 0000000 0000000 00000021670 15051152474 034260 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Tests that session upgrade works from local to global sessions. * Expected behavior is that if global-only sessions are unset, * and no upgrade interval is specified, then sessions will be * created locally to the host. They will be upgraded to global * sessions iff an operation is done on that session which requires * persistence, i.e. creating an ephemeral node. */ public class SessionUpgradeTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(SessionUpgradeTest.class); public static final int CONNECTION_TIMEOUT = ClientBase.CONNECTION_TIMEOUT; private final QuorumBase qb = new QuorumBase(); @BeforeEach public void setUp() throws Exception { LOG.info("STARTING quorum {}", getClass().getName()); qb.localSessionsEnabled = true; qb.localSessionsUpgradingEnabled = true; qb.setUp(); ClientBase.waitForServerUp(qb.hostPort, 10000); } @AfterEach public void tearDown() throws Exception { LOG.info("STOPPING quorum {}", getClass().getName()); qb.tearDown(); } @Test public void testLocalSessionsWithoutEphemeralOnFollower() throws Exception { testLocalSessionsWithoutEphemeral(false); } @Test public void testLocalSessionsWithoutEphemeralOnLeader() throws Exception { testLocalSessionsWithoutEphemeral(true); } private void testLocalSessionsWithoutEphemeral(boolean testLeader) throws Exception { String nodePrefix = "/testLocalSessions-" + (testLeader ? "leaderTest-" : "followerTest-"); int leaderIdx = qb.getLeaderIndex(); assertFalse(leaderIdx == -1, "No leader in quorum?"); int followerIdx = (leaderIdx + 1) % 5; int otherFollowerIdx = (leaderIdx + 2) % 5; int testPeerIdx = testLeader ? leaderIdx : followerIdx; String[] hostPorts = qb.hostPort.split(","); CountdownWatcher watcher = new CountdownWatcher(); DisconnectableZooKeeper zk = new DisconnectableZooKeeper(hostPorts[testPeerIdx], CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); // Try creating some data. for (int i = 0; i < 5; i++) { zk.create(nodePrefix + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } long localSessionId = zk.getSessionId(); byte[] localSessionPwd = zk.getSessionPasswd().clone(); // Try connecting with the same session id on a different // server. This should fail since it is a local sesion. try { watcher.reset(); DisconnectableZooKeeper zknew = new DisconnectableZooKeeper(hostPorts[otherFollowerIdx], CONNECTION_TIMEOUT, watcher, localSessionId, localSessionPwd); zknew.create(nodePrefix + "5", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("Connection on the same session ID should fail."); } catch (KeeperException.SessionExpiredException e) { } catch (KeeperException.ConnectionLossException e) { } // If we're testing a follower, also check the session id on the // leader. This should also fail if (!testLeader) { try { watcher.reset(); DisconnectableZooKeeper zknew = new DisconnectableZooKeeper(hostPorts[leaderIdx], CONNECTION_TIMEOUT, watcher, localSessionId, localSessionPwd); zknew.create(nodePrefix + "5", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("Connection on the same session ID should fail."); } catch (KeeperException.SessionExpiredException e) { } catch (KeeperException.ConnectionLossException e) { } } // However, we should be able to disconnect and reconnect to the same // server with the same session id (as long as we do it quickly // before expiration). zk.disconnect(); watcher.reset(); zk = new DisconnectableZooKeeper(hostPorts[testPeerIdx], CONNECTION_TIMEOUT, watcher, localSessionId, localSessionPwd); watcher.waitForConnected(CONNECTION_TIMEOUT); zk.create(nodePrefix + "6", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); // If we explicitly close the session, then the session id should no // longer be valid. zk.close(); try { watcher.reset(); zk = new DisconnectableZooKeeper(hostPorts[testPeerIdx], CONNECTION_TIMEOUT, watcher, localSessionId, localSessionPwd); zk.create(nodePrefix + "7", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("Reconnecting to a closed session ID should fail."); } catch (KeeperException.SessionExpiredException e) { } } @Test public void testUpgradeWithEphemeralOnFollower() throws Exception { testUpgradeWithEphemeral(false); } @Test public void testUpgradeWithEphemeralOnLeader() throws Exception { testUpgradeWithEphemeral(true); } private void testUpgradeWithEphemeral(boolean testLeader) throws Exception { String nodePrefix = "/testUpgrade-" + (testLeader ? "leaderTest-" : "followerTest-"); int leaderIdx = qb.getLeaderIndex(); assertFalse(leaderIdx == -1, "No leader in quorum?"); int followerIdx = (leaderIdx + 1) % 5; int otherFollowerIdx = (leaderIdx + 2) % 5; int testPeerIdx = testLeader ? leaderIdx : followerIdx; String[] hostPorts = qb.hostPort.split(","); CountdownWatcher watcher = new CountdownWatcher(); DisconnectableZooKeeper zk = new DisconnectableZooKeeper(hostPorts[testPeerIdx], CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); // Create some ephemeral nodes. This should force the session to // be propagated to the other servers in the ensemble. for (int i = 0; i < 5; i++) { zk.create(nodePrefix + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } // We should be able to reconnect with the same session id on a // different server, since it has been propagated. long localSessionId = zk.getSessionId(); byte[] localSessionPwd = zk.getSessionPasswd().clone(); zk.disconnect(); watcher.reset(); zk = new DisconnectableZooKeeper(hostPorts[otherFollowerIdx], CONNECTION_TIMEOUT, watcher, localSessionId, localSessionPwd); watcher.waitForConnected(CONNECTION_TIMEOUT); // The created ephemeral nodes are still around. for (int i = 0; i < 5; i++) { assertNotNull(zk.exists(nodePrefix + i, null)); } // When we explicitly close the session, we should not be able to // reconnect with the same session id zk.close(); try { watcher.reset(); zk = new DisconnectableZooKeeper(hostPorts[otherFollowerIdx], CONNECTION_TIMEOUT, watcher, localSessionId, localSessionPwd); zk.exists(nodePrefix + "0", null); fail("Reconnecting to a closed session ID should fail."); } catch (KeeperException.SessionExpiredException e) { } watcher.reset(); // And the ephemeral nodes will be gone since the session died. zk = new DisconnectableZooKeeper(hostPorts[testPeerIdx], CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); for (int i = 0; i < 5; i++) { assertNull(zk.exists(nodePrefix + i, null)); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SledgeHammer.java0100644 0000000 0000000 00000007451 15051152474 033513 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import java.io.IOException; import java.util.Collections; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ExitCode; public class SledgeHammer extends Thread { ZooKeeper zk; int count; int readsPerWrite; public SledgeHammer(String hosts, int count, int readsPerWrite) throws Exception { zk = ClientBase.createZKClient(hosts, 10000); this.count = count; this.readsPerWrite = readsPerWrite; } public void run() { try { Stat stat = new Stat(); String path = zk.create("/hammers/hammer-", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); byte[] tag = (path + " was here!").getBytes(); synchronized (this) { String startPath = "/hammers/start"; System.out.println("Waiting for " + startPath); while (zk.exists(startPath, true) == null) { wait(); } System.out.println("Running"); } for (int i = 0; i < count; i++) { try { System.out.print(i + "\r"); List childs = zk.getChildren("/hammers", false); Collections.shuffle(childs); for (String s : childs) { if (s.startsWith("hammer-")) { s = "/hammers/" + s; zk.setData(s, tag, -1); for (int j = 0; j < readsPerWrite; j++) { zk.getData(s, false, stat); } break; } } } catch (KeeperException.ConnectionLossException e) { // ignore connection loss } catch (KeeperException e) { e.printStackTrace(); } } System.out.println(); zk.close(); } catch (RuntimeException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } } /** * @param args * @throws IOException * @throws KeeperException * @throws NumberFormatException */ public static void main(String[] args) throws Exception { if (args.length != 3) { System.err.println("USAGE: SledgeHammer zookeeper_server reps reads_per_rep"); System.exit(ExitCode.UNABLE_TO_ACCESS_DATADIR.getValue()); } SledgeHammer h = new SledgeHammer(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2])); h.start(); System.exit(ExitCode.EXECUTION_FINISHED.getValue()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/StandaloneTest.java0100644 0000000 0000000 00000014241 15051152474 034101 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.apache.zookeeper.test.ClientBase.CONNECTION_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.admin.ZooKeeperAdmin; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.apache.zookeeper.server.quorum.QuorumPeerTestBase; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Standalone server tests. */ public class StandaloneTest extends QuorumPeerTestBase implements Watcher { protected static final Logger LOG = LoggerFactory.getLogger(StandaloneTest.class); @BeforeEach public void setup() { System.setProperty("zookeeper.DigestAuthenticationProvider.superDigest", "super:D/InIHSb7yEEbrWz8b9l71RjZJU="/* password is 'test'*/); QuorumPeerConfig.setReconfigEnabled(true); } /** * This test wouldn't create any dynamic config. * However, it adds a "clientPort=xxx" in static config file. * It checks the standard way of standalone mode. */ @Test public void testNoDynamicConfig() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); MainThread mt = new MainThread(MainThread.UNSET_MYID, CLIENT_PORT, "", false); verifyStandalone(mt, CLIENT_PORT); } /** * This test creates a dynamic config of new format. * The dynamic config is written in dynamic config file. * It checks that the client port will be read from the dynamic config. * * This handles the case of HBase, which adds a single server line to the config. * Maintain b/w compatibility. */ @Test public void testClientPortInDynamicFile() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + (PortAssignment.unique()) + ":" + (PortAssignment.unique()) + ":participant;" + CLIENT_PORT + "\n"; MainThread mt = new MainThread(1, quorumCfgSection); verifyStandalone(mt, CLIENT_PORT); } /** * This test creates a dynamic config of new format. * The dynamic config is written in static config file. * It checks that the client port will be read from the dynamic config. */ @Test public void testClientPortInStaticFile() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); String quorumCfgSection = "server.1=127.0.0.1:" + (PortAssignment.unique()) + ":" + (PortAssignment.unique()) + ":participant;" + CLIENT_PORT + "\n"; MainThread mt = new MainThread(1, quorumCfgSection, false); verifyStandalone(mt, CLIENT_PORT); } void verifyStandalone(MainThread mt, int clientPort) throws InterruptedException { mt.start(); try { assertTrue(ClientBase.waitForServerUp("127.0.0.1:" + clientPort, CONNECTION_TIMEOUT), "waiting for server 1 being up"); } finally { assertFalse(mt.isQuorumPeerRunning(), "Error- MainThread started in Quorum Mode!"); mt.shutdown(); } } /** * Verify that reconfiguration in standalone mode fails with * KeeperException.UnimplementedException. */ @Test public void testStandaloneReconfigFails() throws Exception { ClientBase.setupTestEnv(); final int CLIENT_PORT = PortAssignment.unique(); final String HOSTPORT = "127.0.0.1:" + CLIENT_PORT; File tmpDir = ClientBase.createTmpDir(); ZooKeeperServer zks = new ZooKeeperServer(tmpDir, tmpDir, 3000); ServerCnxnFactory f = ServerCnxnFactory.createFactory(CLIENT_PORT, -1); f.startup(zks); assertTrue(ClientBase.waitForServerUp(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being up "); CountdownWatcher watcher = new CountdownWatcher(); ZooKeeper zk = new ZooKeeper(HOSTPORT, CONNECTION_TIMEOUT, watcher); ZooKeeperAdmin zkAdmin = new ZooKeeperAdmin(HOSTPORT, CONNECTION_TIMEOUT, watcher); watcher.waitForConnected(CONNECTION_TIMEOUT); List joiners = new ArrayList<>(); joiners.add("server.2=localhost:1234:1235;1236"); // generate some transactions that will get logged try { zkAdmin.addAuthInfo("digest", "super:test".getBytes()); zkAdmin.reconfigure(joiners, null, null, -1, new Stat()); fail("Reconfiguration in standalone should trigger " + "UnimplementedException"); } catch (KeeperException.UnimplementedException ex) { // expected } zk.close(); zks.shutdown(); f.shutdown(); assertTrue(ClientBase.waitForServerDown(HOSTPORT, CONNECTION_TIMEOUT), "waiting for server being down "); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/StatTest.java0100644 0000000 0000000 00000015350 15051152474 032726 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import java.io.IOException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class StatTest extends ClientBase { private ZooKeeper zk; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { super.tearDown(); zk.close(); } /** * Create a new Stat, fill in dummy values trying to catch failure * to copy in client or server code. * * @return a new stat with dummy values */ private Stat newStat() { Stat stat = new Stat(); stat.setAversion(100); stat.setCtime(100); stat.setCversion(100); stat.setCzxid(100); stat.setDataLength(100); stat.setEphemeralOwner(100); stat.setMtime(100); stat.setMzxid(100); stat.setNumChildren(100); stat.setPzxid(100); stat.setVersion(100); return stat; } @Test public void testBasic() throws IOException, KeeperException, InterruptedException { String name = "/foo"; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat; stat = newStat(); zk.getData(name, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(0, stat.getNumChildren()); } @Test public void testChild() throws IOException, KeeperException, InterruptedException { String name = "/foo"; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); String childname = name + "/bar"; zk.create(childname, childname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); Stat stat; stat = newStat(); zk.getData(name, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid() + 1, stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(1, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(1, stat.getNumChildren()); stat = newStat(); zk.getData(childname, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(zk.getSessionId(), stat.getEphemeralOwner()); assertEquals(childname.length(), stat.getDataLength()); assertEquals(0, stat.getNumChildren()); } @Test public void testChildren() throws IOException, KeeperException, InterruptedException { String name = "/foo"; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); for (int i = 0; i < 10; i++) { String childname = name + "/bar" + i; zk.create(childname, childname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); Stat stat; stat = newStat(); zk.getData(name, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid() + i + 1, stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(i + 1, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(i + 1, stat.getNumChildren()); } } @Test public void testDataSizeChange() throws IOException, KeeperException, InterruptedException { String name = "/foo"; zk.create(name, name.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat; stat = newStat(); zk.getData(name, false, stat); assertEquals(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertEquals(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(0, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length(), stat.getDataLength()); assertEquals(0, stat.getNumChildren()); zk.setData(name, (name + name).getBytes(), -1); stat = newStat(); zk.getData(name, false, stat); assertNotSame(stat.getCzxid(), stat.getMzxid()); assertEquals(stat.getCzxid(), stat.getPzxid()); assertNotSame(stat.getCtime(), stat.getMtime()); assertEquals(0, stat.getCversion()); assertEquals(1, stat.getVersion()); assertEquals(0, stat.getAversion()); assertEquals(0, stat.getEphemeralOwner()); assertEquals(name.length() * 2, stat.getDataLength()); assertEquals(0, stat.getNumChildren()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Stat0100644 0000000 0000000 00000000165 15051152474 032713 xustar000000000 0000000 117 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/StaticHostProviderTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/StaticHostProviderTe0100644 0000000 0000000 00000117631 15051152474 034331 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.client.HostProvider; import org.apache.zookeeper.client.StaticHostProvider; import org.apache.zookeeper.common.Time; import org.burningwave.tools.net.DefaultHostResolver; import org.burningwave.tools.net.HostResolutionRequestInterceptor; import org.burningwave.tools.net.MappedHostResolver; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class StaticHostProviderTest extends ZKTestCase { @BeforeAll public static void setupDNSMocks() { Map hostAliases = new LinkedHashMap<>(); hostAliases.put("site1.mock", "192.168.1.1"); hostAliases.put("site2.mock", "192.168.1.2"); hostAliases.put("site3.mock", "192.168.1.3"); hostAliases.put("site4.mock", "192.168.1.4"); HostResolutionRequestInterceptor.INSTANCE.install( new MappedHostResolver(hostAliases), DefaultHostResolver.INSTANCE ); } @AfterAll public static void clearDNSMocks() { HostResolutionRequestInterceptor.INSTANCE.uninstall(); } private Random r = new Random(1); @Test public void testNextGoesRound() { HostProvider hostProvider = getHostProvider((byte) 2); InetSocketAddress first = hostProvider.next(0); assertTrue(first != null); hostProvider.next(0); assertEquals(first, hostProvider.next(0)); } @Test public void testNextGoesRoundAndSleeps() { byte size = 2; HostProvider hostProvider = getHostProvider(size); while (size > 0) { hostProvider.next(0); --size; } long start = Time.currentElapsedTime(); hostProvider.next(1000); long stop = Time.currentElapsedTime(); assertTrue(900 <= stop - start); } @Test public void testNextDoesNotSleepForZero() { byte size = 2; HostProvider hostProvider = getHostProvider(size); while (size > 0) { hostProvider.next(0); --size; } long start = Time.currentElapsedTime(); hostProvider.next(0); long stop = Time.currentElapsedTime(); assertTrue(5 > stop - start); } @Test public void testEmptyServerAddressesList() { assertThrows(IllegalArgumentException.class, () -> { HostProvider hp = new StaticHostProvider(new ArrayList<>()); }); } @Test public void testInvalidHostAddresses() { // Arrange final List invalidAddresses = new ArrayList<>(); InetSocketAddress unresolved = InetSocketAddress.createUnresolved("a", 1234); invalidAddresses.add(unresolved); StaticHostProvider.Resolver resolver = new StaticHostProvider.Resolver() { @Override public InetAddress[] getAllByName(String name) throws UnknownHostException { throw new UnknownHostException(); } }; StaticHostProvider sp = new StaticHostProvider(invalidAddresses, resolver); // Act & Assert InetSocketAddress n1 = sp.next(0); assertTrue(n1.isUnresolved(), "Provider should return unresolved address is host is unresolvable"); assertSame(unresolved, n1, "Provider should return original address is host is unresolvable"); } @Test public void testTwoConsequitiveCallsToNextReturnDifferentElement() { HostProvider hostProvider = getHostProvider((byte) 2); assertNotSame(hostProvider.next(0), hostProvider.next(0)); } @Test public void testOnConnectDoesNotReset() { HostProvider hostProvider = getHostProvider((byte) 2); InetSocketAddress first = hostProvider.next(0); hostProvider.onConnected(); InetSocketAddress second = hostProvider.next(0); assertNotSame(first, second); } /* Reconfig tests with IP addresses */ private final double slackPercent = 10; private final int numClients = 10000; @Test public void testUpdateClientMigrateOrNot() throws UnknownHostException { HostProvider hostProvider = getHostProvider((byte) 4); // 10.10.10.4:1238, 10.10.10.3:1237, 10.10.10.2:1236, 10.10.10.1:1235 Collection newList = getServerAddresses((byte) 3); // 10.10.10.3:1237, 10.10.10.2:1236, 10.10.10.1:1235 InetSocketAddress myServer = new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, 3}), 1237); // Number of machines becomes smaller, my server is in the new cluster boolean disconnectRequired = hostProvider.updateServerList(newList, myServer); assertFalse(disconnectRequired); hostProvider.onConnected(); // Number of machines stayed the same, my server is in the new cluster disconnectRequired = hostProvider.updateServerList(newList, myServer); assertFalse(disconnectRequired); hostProvider.onConnected(); // Number of machines became smaller, my server is not in the new // cluster newList = getServerAddresses((byte) 2); // 10.10.10.2:1236, 10.10.10.1:1235 disconnectRequired = hostProvider.updateServerList(newList, myServer); assertTrue(disconnectRequired); hostProvider.onConnected(); // Number of machines stayed the same, my server is not in the new // cluster disconnectRequired = hostProvider.updateServerList(newList, myServer); assertTrue(disconnectRequired); hostProvider.onConnected(); // Number of machines increased, my server is not in the new cluster newList = new ArrayList<>(3); for (byte i = 4; i > 1; i--) { // 10.10.10.4:1238, 10.10.10.3:1237, 10.10.10.2:1236 newList.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, i}), 1234 + i)); } myServer = new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, 1}), 1235); disconnectRequired = hostProvider.updateServerList(newList, myServer); assertTrue(disconnectRequired); hostProvider.onConnected(); // Number of machines increased, my server is in the new cluster // Here whether to move or not depends on the difference of cluster // sizes // With probability 1 - |old|/|new} the client disconnects // In the test below 1-9/10 = 1/10 chance of disconnecting HostProvider[] hostProviderArray = new HostProvider[numClients]; newList = getServerAddresses((byte) 10); int numDisconnects = 0; for (int i = 0; i < numClients; i++) { hostProviderArray[i] = getHostProvider((byte) 9); disconnectRequired = hostProviderArray[i].updateServerList(newList, myServer); if (disconnectRequired) { numDisconnects++; } } hostProvider.onConnected(); // should be numClients/10 in expectation, we test that its numClients/10 +- slackPercent assertTrue(numDisconnects < upperboundCPS(numClients, 10)); } @Test public void testUpdateMigrationGoesRound() throws UnknownHostException { HostProvider hostProvider = getHostProvider((byte) 4); // old list (just the ports): 1238, 1237, 1236, 1235 Collection newList = new ArrayList<>(10); for (byte i = 12; i > 2; i--) { // 1246, 1245, 1244, 1243, 1242, 1241, // 1240, 1239, 1238, 1237 newList.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, i}), 1234 + i)); } // servers from the old list that appear in the new list Collection oldStaying = new ArrayList<>(2); for (byte i = 4; i > 2; i--) { // 1238, 1237 oldStaying.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, i}), 1234 + i)); } // servers in the new list that are not in the old list Collection newComing = new ArrayList<>(10); for (byte i = 12; i > 4; i--) {// 1246, 1245, 1244, 1243, 1242, 1241, 1240, 1139 newComing.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, i}), 1234 + i)); } // Number of machines increases, my server is not in the new cluster // load on old servers must be decreased, so must connect to one of the // new servers // i.e., pNew = 1. boolean disconnectRequired = hostProvider.updateServerList(newList, new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, 1}), 1235)); assertTrue(disconnectRequired); // This means reconfigMode = true, and nextHostInReconfigMode will be // called from next // Since pNew = 1 we should first try the new servers ArrayList seen = new ArrayList<>(); for (int i = 0; i < newComing.size(); i++) { InetSocketAddress addr = hostProvider.next(0); assertTrue(newComing.contains(addr)); assertTrue(!seen.contains(addr)); seen.add(addr); } // Next the old servers seen.clear(); for (int i = 0; i < oldStaying.size(); i++) { InetSocketAddress addr = hostProvider.next(0); assertTrue(oldStaying.contains(addr)); assertTrue(!seen.contains(addr)); seen.add(addr); } // And now it goes back to normal next() so it should be everything // together like in testNextGoesRound() InetSocketAddress first = hostProvider.next(0); assertTrue(first != null); for (int i = 0; i < newList.size() - 1; i++) { hostProvider.next(0); } assertEquals(first, hostProvider.next(0)); hostProvider.onConnected(); } @Test public void testUpdateLoadBalancing() throws UnknownHostException { // Start with 9 servers and 10000 clients boolean disconnectRequired; HostProvider[] hostProviderArray = new HostProvider[numClients]; InetSocketAddress[] curHostForEachClient = new InetSocketAddress[numClients]; int[] numClientsPerHost = new int[9]; // initialization for (int i = 0; i < numClients; i++) { hostProviderArray[i] = getHostProvider((byte) 9); curHostForEachClient[i] = hostProviderArray[i].next(0); numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } for (int i = 0; i < 9; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 9)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 9)); numClientsPerHost[i] = 0; // prepare for next test } // remove host number 8 (the last one in a list of 9 hosts) Collection newList = getServerAddresses((byte) 8); for (int i = 0; i < numClients; i++) { disconnectRequired = hostProviderArray[i].updateServerList(newList, curHostForEachClient[i]); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } for (int i = 0; i < 8; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 8)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 8)); numClientsPerHost[i] = 0; // prepare for next test } assertTrue(numClientsPerHost[8] == 0); // remove hosts number 6 and 7 (the currently last two in the list) newList = getServerAddresses((byte) 6); for (int i = 0; i < numClients; i++) { disconnectRequired = hostProviderArray[i].updateServerList(newList, curHostForEachClient[i]); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } for (int i = 0; i < 6; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 6)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 6)); numClientsPerHost[i] = 0; // prepare for next test } assertTrue(numClientsPerHost[6] == 0); assertTrue(numClientsPerHost[7] == 0); assertTrue(numClientsPerHost[8] == 0); // remove host number 0 (the first one in the current list) // and add back hosts 6, 7 and 8 newList = new ArrayList<>(8); for (byte i = 9; i > 1; i--) { newList.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, i}), 1234 + i)); } for (int i = 0; i < numClients; i++) { disconnectRequired = hostProviderArray[i].updateServerList(newList, curHostForEachClient[i]); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } assertTrue(numClientsPerHost[0] == 0); for (int i = 1; i < 9; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 8)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 8)); numClientsPerHost[i] = 0; // prepare for next test } // add back host number 0 newList = getServerAddresses((byte) 9); for (int i = 0; i < numClients; i++) { disconnectRequired = hostProviderArray[i].updateServerList(newList, curHostForEachClient[i]); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } for (int i = 0; i < 9; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 9)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 9)); } } @Test public void testNoCurrentHostDuringNormalMode() throws UnknownHostException { // Start with 9 servers and 10000 clients boolean disconnectRequired; StaticHostProvider[] hostProviderArray = new StaticHostProvider[numClients]; InetSocketAddress[] curHostForEachClient = new InetSocketAddress[numClients]; int[] numClientsPerHost = new int[9]; // initialization for (int i = 0; i < numClients; i++) { hostProviderArray[i] = getHostProvider((byte) 9); if (i >= (numClients / 2)) { curHostForEachClient[i] = hostProviderArray[i].next(0); } else { // its supposed to be the first server on serverList. // we'll set it later, see below (*) curHostForEachClient[i] = null; } } // remove hosts 7 and 8 (the last two in a list of 9 hosts) Collection newList = getServerAddresses((byte) 7); for (int i = 0; i < numClients; i++) { // tests the case currentHost == null && lastIndex == -1 // calls next for clients with index < numClients/2 disconnectRequired = hostProviderArray[i].updateServerList(newList, curHostForEachClient[i]); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } else if (curHostForEachClient[i] == null) { // (*) setting it to what it should be curHostForEachClient[i] = hostProviderArray[i].getServerAtIndex(0); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; // sets lastIndex, resets reconfigMode hostProviderArray[i].onConnected(); } for (int i = 0; i < 7; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 7)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 7)); numClientsPerHost[i] = 0; // prepare for next test } assertTrue(numClientsPerHost[7] == 0); assertTrue(numClientsPerHost[8] == 0); // add back server 7 newList = getServerAddresses((byte) 8); for (int i = 0; i < numClients; i++) { InetSocketAddress myServer = (i < (numClients / 2)) ? null : curHostForEachClient[i]; // tests the case currentHost == null && lastIndex >= 0 disconnectRequired = hostProviderArray[i].updateServerList(newList, myServer); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } for (int i = 0; i < 8; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 8)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 8)); } } @Test public void testReconfigDuringReconfigMode() throws UnknownHostException { // Start with 9 servers and 10000 clients boolean disconnectRequired; StaticHostProvider[] hostProviderArray = new StaticHostProvider[numClients]; InetSocketAddress[] curHostForEachClient = new InetSocketAddress[numClients]; int[] numClientsPerHost = new int[9]; // initialization for (int i = 0; i < numClients; i++) { hostProviderArray[i] = getHostProvider((byte) 9); curHostForEachClient[i] = hostProviderArray[i].next(0); } // remove hosts 7 and 8 (the last two in a list of 9 hosts) Collection newList = getServerAddresses((byte) 7); for (int i = 0; i < numClients; i++) { // sets reconfigMode hostProviderArray[i].updateServerList(newList, curHostForEachClient[i]); } // add back servers 7 and 8 while still in reconfigMode (we didn't call // next) newList = getServerAddresses((byte) 9); for (int i = 0; i < numClients; i++) { InetSocketAddress myServer = (i < (numClients / 2)) ? null : curHostForEachClient[i]; // for i < (numClients/2) this tests the case currentHost == null && // reconfigMode = true // for i >= (numClients/2) this tests the case currentHost!=null && // reconfigMode = true disconnectRequired = hostProviderArray[i].updateServerList(newList, myServer); if (disconnectRequired) { curHostForEachClient[i] = hostProviderArray[i].next(0); } else { // currentIndex was set by the call to updateServerList, which // called next curHostForEachClient[i] = hostProviderArray[i].getServerAtCurrentIndex(); } numClientsPerHost[curHostForEachClient[i].getPort() - 1235]++; hostProviderArray[i].onConnected(); } for (int i = 0; i < 9; i++) { assertTrue(numClientsPerHost[i] <= upperboundCPS(numClients, 9)); assertTrue(numClientsPerHost[i] >= lowerboundCPS(numClients, 9)); } } private StaticHostProvider getHostProvider(byte size) { return new StaticHostProvider(getServerAddresses(size), r.nextLong()); } private Collection getServerAddresses(byte size) { ArrayList list = new ArrayList<>(size); while (size > 0) { try { list.add(new InetSocketAddress(InetAddress.getByAddress(new byte[]{10, 10, 10, size}), 1234 + size)); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } --size; } return list; } /* Reconfig test with unresolved hostnames */ /** * Number of machines becomes smaller, my server is in the new cluster */ @Test public void testUpdateServerList_UnresolvedHostnames_NoDisconnection1() { // Arrange // [testhost-4.testdomain.com:1238, testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] HostProvider hostProvider = getHostProviderWithUnresolvedHostnames(4); // [testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] Collection newList = getUnresolvedHostnames(3); InetSocketAddress myServer = InetSocketAddress.createUnresolved("testhost-3.testdomain.com", 1237); // Act boolean disconnectRequired = hostProvider.updateServerList(newList, myServer); // Assert assertFalse(disconnectRequired); hostProvider.onConnected(); } /** * Number of machines stayed the same, my server is in the new cluster */ @Test public void testUpdateServerList_UnresolvedHostnames_NoDisconnection2() { // Arrange // [testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] HostProvider hostProvider = getHostProviderWithUnresolvedHostnames(3); // [testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] Collection newList = getUnresolvedHostnames(3); InetSocketAddress myServer = InetSocketAddress.createUnresolved("testhost-3.testdomain.com", 1237); // Act boolean disconnectRequired = hostProvider.updateServerList(newList, myServer); // Assert assertFalse(disconnectRequired); hostProvider.onConnected(); } /** * Number of machines became smaller, my server is not in the new cluster */ @Test public void testUpdateServerList_UnresolvedHostnames_Disconnection1() { // Arrange // [testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] HostProvider hostProvider = getHostProviderWithUnresolvedHostnames(3); // [testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] Collection newList = getUnresolvedHostnames(2); InetSocketAddress myServer = InetSocketAddress.createUnresolved("testhost-3.testdomain.com", 1237); // Act boolean disconnectRequired = hostProvider.updateServerList(newList, myServer); // Assert assertTrue(disconnectRequired); hostProvider.onConnected(); } /** * Number of machines stayed the same, my server is not in the new cluster */ @Test public void testUpdateServerList_UnresolvedHostnames_Disconnection2() { // Arrange // [testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] HostProvider hostProvider = getHostProviderWithUnresolvedHostnames(3); // [testhost-3.testdomain.com:1237, testhost-2.testdomain.com:1236, testhost-1.testdomain.com:1235] Collection newList = getUnresolvedHostnames(3); InetSocketAddress myServer = InetSocketAddress.createUnresolved("testhost-4.testdomain.com", 1237); // Act boolean disconnectRequired = hostProvider.updateServerList(newList, myServer); // Assert assertTrue(disconnectRequired); hostProvider.onConnected(); } @Test public void testUpdateServerList_ResolvedWithUnResolvedAddress_ForceDisconnect() { // Arrange // Create a HostProvider with a list of unresolved server address(es) List addresses = Collections.singletonList(InetSocketAddress.createUnresolved("testhost-1.resolvable.zk", 1235)); HostProvider hostProvider = new StaticHostProvider(addresses, new TestResolver()); InetSocketAddress currentHost = hostProvider.next(100); assertThat("CurrentHost is which the client is currently connecting to, it should be resolved", currentHost.isUnresolved(), is(false)); // Act InetSocketAddress replaceHost = InetSocketAddress.createUnresolved("testhost-1.resolvable.zk", 1235); assertThat("Replace host must be unresolved in this test case", replaceHost.isUnresolved(), is(true)); boolean disconnect = hostProvider.updateServerList(new ArrayList<>(Collections.singletonList(replaceHost)), currentHost); // Assert assertThat(disconnect, is(false)); } @Test public void testUpdateServerList_ResolvedWithResolvedAddress_NoDisconnect() throws UnknownHostException { // Arrange // Create a HostProvider with a list of unresolved server address(es) List addresses = Collections.singletonList(InetSocketAddress.createUnresolved("testhost-1.resolvable.zk", 1235)); HostProvider hostProvider = new StaticHostProvider(addresses, new TestResolver()); InetSocketAddress currentHost = hostProvider.next(100); assertThat("CurrentHost is which the client is currently connecting to, it should be resolved", currentHost.isUnresolved(), is(false)); // Act InetSocketAddress replaceHost = new InetSocketAddress(InetAddress.getByAddress(currentHost.getHostString(), currentHost.getAddress().getAddress()), currentHost.getPort()); assertThat("Replace host must be resolved in this test case", replaceHost.isUnresolved(), is(false)); boolean disconnect = hostProvider.updateServerList(new ArrayList<>(Collections.singletonList(replaceHost)), currentHost); // Assert assertThat(disconnect, equalTo(false)); } @Test public void testUpdateServerList_UnResolvedWithUnResolvedAddress_ForceDisconnect() { // Arrange // Create a HostProvider with a list of unresolved server address(es) List addresses = Collections.singletonList(InetSocketAddress.createUnresolved("testhost-1.zookeepertest.zk", 1235)); HostProvider hostProvider = new StaticHostProvider(addresses, new TestResolver()); InetSocketAddress currentHost = hostProvider.next(100); assertThat("CurrentHost is not resolvable in this test case", currentHost.isUnresolved(), is(true)); // Act InetSocketAddress replaceHost = InetSocketAddress.createUnresolved("testhost-1.resolvable.zk", 1235); assertThat("Replace host must be unresolved in this test case", replaceHost.isUnresolved(), is(true)); boolean disconnect = hostProvider.updateServerList(new ArrayList<>(Collections.singletonList(replaceHost)), currentHost); // Assert assertThat(disconnect, is(true)); } @Test public void testUpdateServerList_UnResolvedWithResolvedAddress_ForceDisconnect() throws UnknownHostException { // Arrange // Create a HostProvider with a list of unresolved server address(es) List addresses = Collections.singletonList(InetSocketAddress.createUnresolved("testhost-1.zookeepertest.zk", 1235)); HostProvider hostProvider = new StaticHostProvider(addresses, new TestResolver()); InetSocketAddress currentHost = hostProvider.next(100); assertThat("CurrentHost not resolvable in this test case", currentHost.isUnresolved(), is(true)); // Act byte[] addr = new byte[]{10, 0, 0, 1}; InetSocketAddress replaceHost = new InetSocketAddress(InetAddress.getByAddress(currentHost.getHostString(), addr), currentHost.getPort()); assertThat("Replace host must be resolved in this test case", replaceHost.isUnresolved(), is(false)); boolean disconnect = hostProvider.updateServerList(new ArrayList<>(Collections.singletonList(replaceHost)), currentHost); // Assert assertThat(disconnect, equalTo(false)); } private class TestResolver implements StaticHostProvider.Resolver { private byte counter = 1; @Override public InetAddress[] getAllByName(String name) throws UnknownHostException { if (name.contains("resolvable")) { byte[] addr = new byte[]{10, 0, 0, (byte) (counter++ % 10)}; return new InetAddress[]{InetAddress.getByAddress(name, addr)}; } throw new UnknownHostException(); } } private double lowerboundCPS(int numClients, int numServers) { return (1 - slackPercent / 100.0) * numClients / numServers; } private double upperboundCPS(int numClients, int numServers) { return (1 + slackPercent / 100.0) * numClients / numServers; } /* DNS resolution tests */ @Test public void testLiteralIPNoReverseNS() { byte size = 30; HostProvider hostProvider = getHostProviderUnresolved(size); for (int i = 0; i < size; i++) { InetSocketAddress next = hostProvider.next(0); assertThat(next, instanceOf(InetSocketAddress.class)); assertFalse(next.isUnresolved()); assertTrue(next.toString().startsWith("/")); // Do NOT trigger the reverse name service lookup. String hostname = next.getHostString(); // In this case, the hostname equals literal IP address. assertEquals(next.getAddress().getHostAddress(), hostname); } } @Test public void testReResolvingSingle() throws UnknownHostException { // Arrange byte size = 1; ArrayList list = new ArrayList<>(size); // Test a hostname that resolves to a single address list.add(InetSocketAddress.createUnresolved("issues.apache.org", 1234)); final InetAddress issuesApacheOrg = InetAddress.getByName("site1.mock"); StaticHostProvider.Resolver resolver = new StaticHostProvider.Resolver() { @Override public InetAddress[] getAllByName(String name) { return new InetAddress[]{issuesApacheOrg}; } }; StaticHostProvider.Resolver spyResolver = spy(resolver); // Act StaticHostProvider hostProvider = new StaticHostProvider(list, spyResolver); for (int i = 0; i < 10; i++) { InetSocketAddress next = hostProvider.next(0); assertEquals(issuesApacheOrg, next.getAddress()); } // Assert // Resolver called 10 times, because we shouldn't cache the resolved addresses verify(spyResolver, times(10)).getAllByName("issues.apache.org"); // resolution occurred } @Test public void testReResolvingMultiple() throws UnknownHostException { // Arrange byte size = 1; ArrayList list = new ArrayList<>(size); // Test a hostname that resolves to multiple addresses list.add(InetSocketAddress.createUnresolved("www.apache.org", 1234)); final InetAddress apacheOrg1 = InetAddress.getByName("site1.mock"); final InetAddress apacheOrg2 = InetAddress.getByName("site2.mock"); final List resolvedAddresses = new ArrayList<>(); resolvedAddresses.add(apacheOrg1); resolvedAddresses.add(apacheOrg2); StaticHostProvider.Resolver resolver = new StaticHostProvider.Resolver() { @Override public InetAddress[] getAllByName(String name) { return resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]); } }; StaticHostProvider.Resolver spyResolver = spy(resolver); // Act & Assert StaticHostProvider hostProvider = new StaticHostProvider(list, spyResolver); assertEquals(1, hostProvider.size()); // single address not extracted for (int i = 0; i < 10; i++) { InetSocketAddress next = hostProvider.next(0); assertThat("Bad IP address returned", next.getAddress().getHostAddress(), anyOf(equalTo(apacheOrg1.getHostAddress()), equalTo(apacheOrg2.getHostAddress()))); assertEquals(1, hostProvider.size()); // resolve() call keeps the size of provider } // Resolver called 10 times, because we shouldn't cache the resolved addresses verify(spyResolver, times(10)).getAllByName("www.apache.org"); // resolution occurred } @Test public void testReResolveMultipleOneFailing() throws UnknownHostException { // Arrange final List list = new ArrayList<>(); list.add(InetSocketAddress.createUnresolved("www.apache.org", 1234)); final List ipList = new ArrayList<>(); final List resolvedAddresses = new ArrayList<>(); for (int i = 0; i < 3; i++) { ipList.add(String.format("192.168.1.%d", i + 1)); final InetAddress apacheOrg = InetAddress.getByName("site" + (i + 1) + ".mock"); resolvedAddresses.add(apacheOrg); } StaticHostProvider.Resolver resolver = new StaticHostProvider.Resolver() { @Override public InetAddress[] getAllByName(String name) { return resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]); } }; StaticHostProvider.Resolver spyResolver = spy(resolver); StaticHostProvider hostProvider = new StaticHostProvider(list, spyResolver); // Act & Assert InetSocketAddress resolvedFirst = hostProvider.next(0); assertFalse(resolvedFirst.isUnresolved(), "HostProvider should return resolved addresses"); assertThat("Bad IP address returned", ipList, hasItems(resolvedFirst.getAddress().getHostAddress())); hostProvider.onConnected(); // first address worked InetSocketAddress resolvedSecond = hostProvider.next(0); assertFalse(resolvedSecond.isUnresolved(), "HostProvider should return resolved addresses"); assertThat("Bad IP address returned", ipList, hasItems(resolvedSecond.getAddress().getHostAddress())); // Second address doesn't work, so we don't call onConnected() this time // StaticHostProvider should try to re-resolve the address in this case InetSocketAddress resolvedThird = hostProvider.next(0); assertFalse(resolvedThird.isUnresolved(), "HostProvider should return resolved addresses"); assertThat("Bad IP address returned", ipList, hasItems(resolvedThird.getAddress().getHostAddress())); verify(spyResolver, times(3)).getAllByName("www.apache.org"); // resolution occured every time } @Test public void testEmptyResolution() throws UnknownHostException { // Arrange final List list = new ArrayList<>(); list.add(InetSocketAddress.createUnresolved("www.apache.org", 1234)); list.add(InetSocketAddress.createUnresolved("www.google.com", 1234)); final List resolvedAddresses = new ArrayList<>(); final InetAddress apacheOrg1 = InetAddress.getByName("site1.mock"); resolvedAddresses.add(apacheOrg1); StaticHostProvider.Resolver resolver = new StaticHostProvider.Resolver() { @Override public InetAddress[] getAllByName(String name) { if ("www.apache.org".equalsIgnoreCase(name)) { return resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]); } else { return new InetAddress[0]; } } }; StaticHostProvider.Resolver spyResolver = spy(resolver); StaticHostProvider hostProvider = new StaticHostProvider(list, spyResolver); // Act & Assert for (int i = 0; i < 10; i++) { InetSocketAddress resolved = hostProvider.next(0); hostProvider.onConnected(); if (resolved.getHostName().equals("www.google.com")) { assertTrue(resolved.isUnresolved(), "HostProvider should return unresolved address if host is unresolvable"); } else { assertFalse(resolved.isUnresolved(), "HostProvider should return resolved addresses"); assertEquals("192.168.1.1", resolved.getAddress().getHostAddress()); } } verify(spyResolver, times(5)).getAllByName("www.apache.org"); verify(spyResolver, times(5)).getAllByName("www.google.com"); } @Test public void testReResolvingLocalhost() { byte size = 2; ArrayList list = new ArrayList<>(size); // Test a hostname that resolves to multiple addresses list.add(InetSocketAddress.createUnresolved("localhost", 1234)); list.add(InetSocketAddress.createUnresolved("localhost", 1235)); StaticHostProvider hostProvider = new StaticHostProvider(list); int sizeBefore = hostProvider.size(); InetSocketAddress next = hostProvider.next(0); next = hostProvider.next(0); assertTrue(hostProvider.size() == sizeBefore, "Different number of addresses in the list: " + hostProvider.size() + " (after), " + sizeBefore + " (before)"); } private StaticHostProvider getHostProviderUnresolved(byte size) { return new StaticHostProvider(getUnresolvedServerAddresses(size), r.nextLong()); } private Collection getUnresolvedServerAddresses(byte size) { ArrayList list = new ArrayList<>(size); while (size > 0) { list.add(InetSocketAddress.createUnresolved("10.10.10." + size, 1234 + size)); --size; } return list; } private StaticHostProvider getHostProviderWithUnresolvedHostnames(int size) { return new StaticHostProvider(getUnresolvedHostnames(size), r.nextLong()); } private Collection getUnresolvedHostnames(int size) { ArrayList list = new ArrayList<>(size); while (size > 0) { list.add(InetSocketAddress.createUnresolved(String.format("testhost-%d.testdomain.com", size), 1234 + size)); --size; } System.out.println(Arrays.toString(list.toArray())); return list; } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/StatsTrackTest.java0100644 0000000 0000000 00000010304 15051152474 034070 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.StatsTrack; import org.junit.Assert; import org.junit.Test; public class StatsTrackTest { public static class OldStatsTrack { private int count; private long bytes; private String countStr = "count"; private String byteStr = "bytes"; /** * a default constructor for * stats */ public OldStatsTrack() { this(null); } /** * the stat string should be of the form count=int,bytes=long * if stats is called with null the count and bytes are initialized * to -1. * @param stats the stat string to be intialized with */ public OldStatsTrack(String stats) { if (stats == null) { stats = "count=-1,bytes=-1"; } String[] split = stats.split(","); if (split.length != 2) { throw new IllegalArgumentException("invalid string " + stats); } count = Integer.parseInt(split[0].split("=")[1]); bytes = Long.parseLong(split[1].split("=")[1]); } /** * get the count of nodes allowed as part of quota * * @return the count as part of this string */ public int getCount() { return this.count; } /** * set the count for this stat tracker. * * @param count * the count to set with */ public void setCount(int count) { this.count = count; } /** * get the count of bytes allowed as part of quota * * @return the bytes as part of this string */ public long getBytes() { return this.bytes; } /** * set teh bytes for this stat tracker. * * @param bytes * the bytes to set with */ public void setBytes(long bytes) { this.bytes = bytes; } @Override /* * returns the string that maps to this stat tracking. */ public String toString() { return countStr + "=" + count + "," + byteStr + "=" + bytes; } } @Test public void testBackwardCompatibility() { StatsTrack quota = new StatsTrack(); quota.setCount(4); quota.setCountHardLimit(4); quota.setBytes(9L); quota.setByteHardLimit(15L); Assert.assertEquals("count=4,bytes=9=;byteHardLimit=15;countHardLimit=4", quota.toString()); OldStatsTrack ost = new OldStatsTrack(quota.toString()); Assert.assertTrue("bytes are set", ost.getBytes() == 9L); Assert.assertTrue("num count is set", ost.getCount() == 4); Assert.assertEquals("count=4,bytes=9", ost.toString()); } @Test public void testUpwardCompatibility() { OldStatsTrack ost = new OldStatsTrack(null); ost.setCount(2); ost.setBytes(5); Assert.assertEquals("count=2,bytes=5", ost.toString()); StatsTrack st = new StatsTrack(ost.toString()); Assert.assertEquals("count=2,bytes=5", st.toString()); Assert.assertEquals(5, st.getBytes()); Assert.assertEquals(2, st.getCount()); Assert.assertEquals(-1, st.getByteHardLimit()); Assert.assertEquals(-1, st.getCountHardLimit()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/StringUtilTest.java0100644 0000000 0000000 00000004612 15051152474 034116 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Arrays; import java.util.Collections; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.common.StringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class StringUtilTest extends ZKTestCase { @Test public void testStringSplit() { final String s1 = " a , b , "; assertEquals(Arrays.asList("a", "b"), StringUtils.split(s1, ",")); assertEquals(Collections.emptyList(), StringUtils.split("", ",")); final String s3 = "1, , 2"; assertEquals(Arrays.asList("1", "2"), StringUtils.split(s3, ",")); final String s4 = "1, \t , 2"; assertEquals(Arrays.asList("1", "2"), StringUtils.split(s4, ",")); } @Test public void testStringJoinNullDelim() { Assertions.assertThrows(NullPointerException.class, () -> { StringUtils.joinStrings(Collections.emptyList(), null); }); } @Test public void testStringJoinNullListNullDelim() { Assertions.assertThrows(NullPointerException.class, () -> { StringUtils.joinStrings(null, null); }); } @Test public void testStringJoinNullList() { assertNull(StringUtils.joinStrings(null, ",")); } @Test public void testStringJoin() { final String expected = "a,B,null,d"; assertEquals(expected, StringUtils.joinStrings(Arrays.asList("a", "B", null, "d"), ",")); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/SyncCallTest.java0100644 0000000 0000000 00000010676 15051152474 033531 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.AsyncCallback.Children2Callback; import org.apache.zookeeper.AsyncCallback.ChildrenCallback; import org.apache.zookeeper.AsyncCallback.Create2Callback; import org.apache.zookeeper.AsyncCallback.StringCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.DummyWatcher; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.Test; public class SyncCallTest extends ClientBase implements ChildrenCallback, Children2Callback, StringCallback, VoidCallback, Create2Callback { private CountDownLatch opsCount; List results = new LinkedList<>(); Integer limit = 100 + 1 + 100 + 100; @Test public void testSync() throws Exception { try { LOG.info("Starting ZK:{}", (new Date()).toString()); opsCount = new CountDownLatch(limit); ZooKeeper zk = createClient(); LOG.info("Beginning test:{}", (new Date()).toString()); for (int i = 0; i < 50; i++) { zk.create("/test" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (StringCallback) this, results); } for (int i = 50; i < 100; i++) { zk.create("/test" + i, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (Create2Callback) this, results); } zk.sync("/test", this, results); for (int i = 0; i < 100; i++) { zk.delete("/test" + i, 0, this, results); } for (int i = 0; i < 100; i++) { zk.getChildren("/", DummyWatcher.INSTANCE, (ChildrenCallback) this, results); } for (int i = 0; i < 100; i++) { zk.getChildren("/", DummyWatcher.INSTANCE, (Children2Callback) this, results); } LOG.info("Submitted all operations:{}", (new Date()).toString()); if (!opsCount.await(10000, TimeUnit.MILLISECONDS)) { fail("Haven't received all confirmations" + opsCount.getCount()); } for (int i = 0; i < limit; i++) { assertEquals(0, (int) results.get(i)); } } catch (IOException e) { System.out.println(e.toString()); } } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, List children) { ((List) ctx).add(rc); opsCount.countDown(); } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, List children, Stat stat) { ((List) ctx).add(rc); opsCount.countDown(); } @SuppressWarnings("unchecked") public void processResult(int rc, String path, Object ctx, String name) { ((List) ctx).add(rc); opsCount.countDown(); } @SuppressWarnings("unchecked") @Override public void processResult(int rc, String path, Object ctx) { ((List) ctx).add(rc); opsCount.countDown(); } @SuppressWarnings("unchecked") @Override public void processResult(int rc, String path, Object ctx, String name, Stat stat) { ((List) ctx).add(rc); opsCount.countDown(); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Test0100644 0000000 0000000 00000000163 15051152474 032715 xustar000000000 0000000 115 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TestByteBufAllocator.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TestByteBufAllocator0100644 0000000 0000000 00000012615 15051152474 034275 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.PooledByteBufAllocator; import io.netty.util.ResourceLeakDetector; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; /** * This is a custom ByteBufAllocator that tracks outstanding allocations and * crashes the program if any of them are leaked. * * Never use this class in production, it will cause your server to run out * of memory! This is because it holds strong references to all allocated * buffers and doesn't release them until checkForLeaks() is called at the * end of a unit test. * * Note: the original code was copied from https://github.com/airlift/drift, * with the permission and encouragement of airlift's author (dain). Airlift * uses the same apache 2.0 license as Zookeeper so this should be ok. * * However, the code was modified to take advantage of Netty's built-in * leak tracking and make a best effort to print details about buffer leaks. * */ public class TestByteBufAllocator extends PooledByteBufAllocator { private static AtomicReference INSTANCE = new AtomicReference<>(null); /** * Get the singleton testing allocator. * @return the singleton allocator, creating it if one does not exist. */ public static TestByteBufAllocator getInstance() { TestByteBufAllocator result = INSTANCE.get(); if (result == null) { ResourceLeakDetector.Level oldLevel = ResourceLeakDetector.getLevel(); ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID); INSTANCE.compareAndSet(null, new TestByteBufAllocator(oldLevel)); result = INSTANCE.get(); } return result; } /** * Destroys the singleton testing allocator and throws an error if any of the * buffers allocated by it have been leaked. Attempts to print leak details to * standard error before throwing, by using netty's built-in leak tracking. * Note that this might not always work, since it only triggers when a buffer * is garbage-collected and calling System.gc() does not guarantee that a buffer * will actually be GC'ed. * * This should be called at the end of a unit test's tearDown() method. */ public static void checkForLeaks() { TestByteBufAllocator result = INSTANCE.getAndSet(null); if (result != null) { result.checkInstanceForLeaks(); } } private final List trackedBuffers = new ArrayList<>(); private final ResourceLeakDetector.Level oldLevel; private TestByteBufAllocator(ResourceLeakDetector.Level oldLevel) { super(false); this.oldLevel = oldLevel; } @Override protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) { return track(super.newHeapBuffer(initialCapacity, maxCapacity)); } @Override protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) { return track(super.newDirectBuffer(initialCapacity, maxCapacity)); } @Override public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) { return track(super.compositeHeapBuffer(maxNumComponents)); } @Override public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) { return track(super.compositeDirectBuffer(maxNumComponents)); } private synchronized CompositeByteBuf track(CompositeByteBuf byteBuf) { trackedBuffers.add(Objects.requireNonNull(byteBuf)); return byteBuf; } private synchronized ByteBuf track(ByteBuf byteBuf) { trackedBuffers.add(Objects.requireNonNull(byteBuf)); return byteBuf; } private void checkInstanceForLeaks() { try { long referencedBuffersCount = 0; synchronized (this) { referencedBuffersCount = trackedBuffers.stream().filter(byteBuf -> byteBuf.refCnt() > 0).count(); // Make tracked buffers eligible for GC trackedBuffers.clear(); } // Throw an error if there were any leaked buffers if (referencedBuffersCount > 0) { // Trigger a GC. This will hopefully (but not necessarily) print // details about detected leaks to standard error before the error // is thrown. System.gc(); throw new AssertionError("Found a netty ByteBuf leak!"); } } finally { ResourceLeakDetector.setLevel(oldLevel); } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Test0100644 0000000 0000000 00000000175 15051152474 032720 xustar000000000 0000000 125 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TestByteBufAllocatorTestHelper.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TestByteBufAllocator0100644 0000000 0000000 00000004231 15051152474 034270 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import io.netty.buffer.ByteBufAllocator; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.zookeeper.ClientCnxnSocketNetty; import org.apache.zookeeper.server.NettyServerCnxnFactory; /** * Uses reflection to call package-private methods in Netty connection classes * to set/clear the test ByteBufAllocator. */ public class TestByteBufAllocatorTestHelper { public static void setTestAllocator(ByteBufAllocator allocator) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method m1 = NettyServerCnxnFactory.class.getDeclaredMethod("setTestAllocator", ByteBufAllocator.class); m1.setAccessible(true); m1.invoke(null, allocator); Method m2 = ClientCnxnSocketNetty.class.getDeclaredMethod("setTestAllocator", ByteBufAllocator.class); m2.setAccessible(true); m2.invoke(null, allocator); } public static void clearTestAllocator() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method m1 = NettyServerCnxnFactory.class.getDeclaredMethod("clearTestAllocator"); m1.setAccessible(true); m1.invoke(null); Method m2 = ClientCnxnSocketNetty.class.getDeclaredMethod("clearTestAllocator"); m2.setAccessible(true); m2.invoke(null); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TestHammer.java0100644 0000000 0000000 00000004002 15051152474 033214 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.Time; public class TestHammer implements VoidCallback { static int REPS = 50000; public static void main(String[] args) { long startTime = Time.currentElapsedTime(); ZooKeeper zk = null; try { zk = ClientBase.createZKClient(args[0], 10000); } catch (Exception e1) { e1.printStackTrace(); throw new RuntimeException(e1); } for (int i = 0; i < REPS; i++) { try { String name = zk.create("/testFile-", new byte[16], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); zk.delete(name, -1, new TestHammer(), null); } catch (Exception e) { i--; e.printStackTrace(); } } System.out.println("creates/sec=" + (REPS * 1000 / (Time.currentElapsedTime() - startTime))); } public void processResult(int rc, String path, Object ctx) { // TODO Auto-generated method stub } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TestUtils.java0100644 0000000 0000000 00000007353 15051152474 033117 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.zookeeper.WatchedEvent; /** * This class contains test utility methods */ public class TestUtils { /** * deletes a folder recursively * * @param file * folder to be deleted * @param failOnError * if true file deletion success is ensured */ public static boolean deleteFileRecursively( File file, final boolean failOnError) { if (file != null) { if (file.isDirectory()) { File[] files = file.listFiles(); int size = files.length; for (int i = 0; i < size; i++) { File f = files[i]; boolean deleted = deleteFileRecursively(files[i], failOnError); if (!deleted && failOnError) { fail("file '" + f.getAbsolutePath() + "' deletion failed"); } } } return file.delete(); } return true; } public static boolean deleteFileRecursively(File file) { return deleteFileRecursively(file, false); } /** * Asserts that the given {@link WatchedEvent} are semantically equal, i.e. they have the same EventType, path and * zxid. */ public static void assertWatchedEventEquals(WatchedEvent expected, WatchedEvent actual) { // TODO: .hashCode and .equals cannot be added to WatchedEvent without potentially breaking consumers. This // can be changed to `assertEquals(expected, actual)` once WatchedEvent has those methods. Until then, // compare the lists manually. assertEquals(expected.getType(), actual.getType()); assertEquals(expected.getPath(), actual.getPath()); assertEquals(expected.getZxid(), actual.getZxid()); } /** * Return all threads * * Code based on commons-lang3 ThreadUtils * * @return all active threads */ public static List getAllThreads() { ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); while (threadGroup != null && threadGroup.getParent() != null) { threadGroup = threadGroup.getParent(); } int count = threadGroup.activeCount(); Thread[] threads; do { threads = new Thread[count + count / 2 + 1]; //slightly grow the array size count = threadGroup.enumerate(threads, true); //return value of enumerate() must be strictly less than the array size according to javadoc } while (count >= threads.length); return Collections.unmodifiableList(Stream.of(threads).limit(count).collect(Collectors.toList())); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Thro0100644 0000000 0000000 00000000160 15051152474 032707 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpHelper.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpHelper.ja0100644 0000000 0000000 00000022572 15051152474 034240 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.List; import mockit.Mock; import mockit.MockUp; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.RequestThrottler; import org.apache.zookeeper.server.ZooKeeperServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ThrottledOpHelper { protected static final Logger LOG = LoggerFactory.getLogger(ThrottledOpHelper.class); public static final class RequestThrottleMock extends MockUp { public static void throttleEveryNthOp(int n) { everyNthOp = n; opCounter = 0; } private static int everyNthOp = 0; private static int opCounter = 0; @Mock private boolean shouldThrottleOp(Request request, long elapsedTime) { if (everyNthOp > 0 && request.isThrottlable() && (++opCounter % everyNthOp == 0)) { opCounter %= everyNthOp; return true; } return false; } } public static void applyMockUps() { new RequestThrottleMock(); } public void testThrottledOp(ZooKeeper zk, ZooKeeperServer zs) throws IOException, InterruptedException, KeeperException { final int N = 5; // must be greater than 3 final int COUNT = 100; RequestThrottleMock.throttleEveryNthOp(N); LOG.info("Before create /ivailo nodes"); int opCount = 0; for (int i = 0; i < COUNT; i++) { String nodeName = "/ivailo" + i; if (opCount % N == N - 1) { try { zk.create(nodeName, "".getBytes(), Ids.OPEN_ACL_UNSAFE, (i % 2 == 0) ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // anticipated outcome Stat stat = zk.exists(nodeName, null); assertNull(stat); zk.create(nodeName, "".getBytes(), Ids.OPEN_ACL_UNSAFE, (i % 2 == 0) ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL); } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } opCount += 3; // three ops issued } else { zk.create(nodeName, "".getBytes(), Ids.OPEN_ACL_UNSAFE, (i % 2 == 0) ? CreateMode.PERSISTENT : CreateMode.EPHEMERAL); opCount++; // one op issued } if (opCount % N == N - 1) { try { zk.setData(nodeName, nodeName.getBytes(), -1); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // anticipated outcome & retry zk.setData(nodeName, nodeName.getBytes(), -1); } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } opCount += 2; // two ops issued, one for retry } else { zk.setData(nodeName, nodeName.getBytes(), -1); opCount++; // one op issued } } LOG.info("Before delete /ivailo nodes"); for (int i = 0; i < COUNT; i++) { String nodeName = "/ivailo" + i; if (opCount % N == N - 1) { try { zk.exists(nodeName, null); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // anticipated outcome & retry Stat stat = zk.exists(nodeName, null); assertNotNull(stat); opCount += 2; // two ops issued, one is retry } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } } else { Stat stat = zk.exists(nodeName, null); assertNotNull(stat); opCount++; } if (opCount % N == N - 1) { try { zk.getData(nodeName, null, null); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // anticipated outcome & retry byte[] data = zk.getData(nodeName, null, null); assertEquals(nodeName, new String(data)); opCount += 2; // two ops issued, one is retry } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } } else { byte[] data = zk.getData(nodeName, null, null); assertEquals(nodeName, new String(data)); opCount++; } if (opCount % N == N - 1) { try { // version 0 should not trigger BadVersion exception zk.delete(nodeName, 0); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // anticipated outcome & retry zk.delete(nodeName, -1); } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } opCount += 2; // two ops issues, one for retry } else { zk.delete(nodeName, -1); opCount++; // one op only issued } if (opCount % N == N - 1) { try { zk.exists(nodeName, null); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // anticipated outcome & retry Stat stat = zk.exists(nodeName, null); assertNull(stat); opCount += 2; // two ops issued, one is retry } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } } else { Stat stat = zk.exists(nodeName, null); assertNull(stat); opCount++; } } LOG.info("After delete /ivailo"); zk.close(); } public void testThrottledAcl(ZooKeeper zk, ZooKeeperServer zs) throws Exception { RequestThrottleMock.throttleEveryNthOp(0); final ArrayList ACL_PERMS = new ArrayList() { { add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)); add(new ACL(ZooDefs.Perms.WRITE, ZooDefs.Ids.ANYONE_ID_UNSAFE)); add(new ACL(ZooDefs.Perms.ALL, ZooDefs.Ids.AUTH_IDS)); }}; String path = "/path1"; zk.create(path, path.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.addAuthInfo("digest", "pat:test".getBytes()); List defaultAcls = zk.getACL(path, null); assertEquals(1, defaultAcls.size()); RequestThrottleMock.throttleEveryNthOp(2); path = "/path2"; zk.create(path, path.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.setACL(path, ACL_PERMS, -1); fail("Should have gotten ThrottledOp exception"); } catch (KeeperException.ThrottledOpException e) { // expected } catch (KeeperException e) { fail("Should have gotten ThrottledOp exception"); } List acls = zk.getACL(path, null); assertEquals(1, acls.size()); RequestThrottleMock.throttleEveryNthOp(0); path = "/path3"; zk.create(path, path.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.setACL(path, ACL_PERMS, -1); acls = zk.getACL(path, null); assertEquals(3, acls.size()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Thro0100644 0000000 0000000 00000000166 15051152474 032715 xustar000000000 0000000 118 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpObserverTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpObserverT0100644 0000000 0000000 00000004561 15051152474 034341 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import java.io.IOException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ThrottledOpObserverTest extends QuorumBase { @BeforeAll public static void applyMockUps() { ThrottledOpHelper.applyMockUps(); } @BeforeEach @Override public void setUp() throws Exception { super.setUp(true /* withObservers */, false); } @Test public void testThrottledOpObserver() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { zk = createClient("localhost:" + getFirstObserverClientPort()); ZooKeeperServer zs = getFirstObserver().getActiveServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledOp(zk, zs); } finally { if (zk != null) { zk.close(); } } } @Test public void testThrottledAclObserver() throws Exception { ZooKeeper zk = null; try { zk = createClient("localhost:" + getFirstObserverClientPort()); ZooKeeperServer zs = getFirstObserver().getActiveServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledAcl(zk, zs); } finally { if (zk != null) { zk.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Thro0100644 0000000 0000000 00000000164 15051152474 032713 xustar000000000 0000000 116 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpQuorumTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpQuorumTes0100644 0000000 0000000 00000006743 15051152474 034376 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import java.io.IOException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class ThrottledOpQuorumTest extends QuorumBase { @BeforeAll public static void applyMockUps() { ThrottledOpHelper.applyMockUps(); } @Test public void testThrottledOpLeader() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { zk = createClient("localhost:" + getLeaderClientPort()); ZooKeeperServer zs = getLeaderQuorumPeer().getActiveServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledOp(zk, zs); } finally { if (zk != null) { zk.close(); } } } @Test public void testThrottledAclLeader() throws Exception { ZooKeeper zk = null; try { zk = createClient("localhost:" + getLeaderClientPort()); ZooKeeperServer zs = getLeaderQuorumPeer().getActiveServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledAcl(zk, zs); } finally { if (zk != null) { zk.close(); } } } @Test public void testThrottledOpFollower() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { int clientPort = (getLeaderClientPort() == portClient1) ? portClient2 : portClient1; zk = createClient("localhost:" + clientPort); QuorumPeer qp = (getLeaderClientPort() == portClient1) ? s2 : s1; ZooKeeperServer zs = qp.getActiveServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledOp(zk, zs); } finally { if (zk != null) { zk.close(); } } } @Test public void testThrottledAclFollower() throws Exception { ZooKeeper zk = null; try { int clientPort = (getLeaderClientPort() == portClient1) ? portClient2 : portClient1; zk = createClient("localhost:" + clientPort); QuorumPeer qp = (getLeaderClientPort() == portClient1) ? s2 : s1; ZooKeeperServer zs = qp.getActiveServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledAcl(zk, zs); } finally { if (zk != null) { zk.close(); } } } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Thro0100644 0000000 0000000 00000000170 15051152474 032710 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpStandaloneTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ThrottledOpStandalon0100644 0000000 0000000 00000004153 15051152474 034346 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import java.io.IOException; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.ZooKeeperServer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; public class ThrottledOpStandaloneTest extends ClientBase { @BeforeAll public static void applyMockUps() { ThrottledOpHelper.applyMockUps(); } @Test public void testThrottledOp() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { zk = createClient(hostPort); ZooKeeperServer zs = serverFactory.getZooKeeperServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledOp(zk, zs); } finally { if (zk != null) { zk.close(); } } } @Test public void testThrottledAcl() throws Exception { ZooKeeper zk = null; try { zk = createClient(hostPort); ZooKeeperServer zs = serverFactory.getZooKeeperServer(); ThrottledOpHelper test = new ThrottledOpHelper(); test.testThrottledAcl(zk, zs); } finally { if (zk != null) { zk.close(); } } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/TruncateTest.java0100644 0000000 0000000 00000023034 15051152474 033576 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import org.apache.jute.Record; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.PortAssignment; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.server.Request; import org.apache.zookeeper.server.ServerCnxnFactory; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.persistence.FileTxnLog; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.persistence.TxnLog.TxnIterator; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.txn.SetDataTxn; import org.apache.zookeeper.txn.TxnHeader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TruncateTest extends ZKTestCase { private static final Logger LOG = LoggerFactory.getLogger(TruncateTest.class); File dataDir1, dataDir2, dataDir3; @BeforeEach public void setUp() throws IOException { dataDir1 = ClientBase.createTmpDir(); dataDir2 = ClientBase.createTmpDir(); dataDir3 = ClientBase.createTmpDir(); } @AfterEach public void tearDown() { ClientBase.recursiveDelete(dataDir1); ClientBase.recursiveDelete(dataDir2); ClientBase.recursiveDelete(dataDir3); } @Test public void testTruncationStreamReset() throws Exception { File tmpdir = ClientBase.createTmpDir(); FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpdir, tmpdir); ZKDatabase zkdb = new ZKDatabase(snaplog); // make sure to snapshot, so that we have something there when // truncateLog reloads the db snaplog.save(zkdb.getDataTree(), zkdb.getSessionWithTimeOuts(), false); for (int i = 1; i <= 100; i++) { append(zkdb, i); } zkdb.truncateLog(1); append(zkdb, 200); zkdb.close(); // verify that the truncation and subsequent append were processed // correctly FileTxnLog txnlog = new FileTxnLog(new File(tmpdir, "version-2")); TxnIterator iter = txnlog.read(1); TxnHeader hdr = iter.getHeader(); Record txn = iter.getTxn(); assertEquals(1, hdr.getZxid()); assertTrue(txn instanceof SetDataTxn); iter.next(); hdr = iter.getHeader(); txn = iter.getTxn(); assertEquals(200, hdr.getZxid()); assertTrue(txn instanceof SetDataTxn); iter.close(); ClientBase.recursiveDelete(tmpdir); } @Test public void testTruncationNullLog() throws Exception { File tmpdir = ClientBase.createTmpDir(); FileTxnSnapLog snaplog = new FileTxnSnapLog(tmpdir, tmpdir); ZKDatabase zkdb = new ZKDatabase(snaplog); for (int i = 1; i <= 100; i++) { append(zkdb, i); } zkdb.close(); File[] logs = snaplog.getDataLogDir().listFiles(); for (int i = 0; i < logs.length; i++) { LOG.debug("Deleting: {}", logs[i].getName()); assertTrue(logs[i].delete(), "Failed to delete log file: " + logs[i].getName()); } try { assertThat("truncateLog() should return false if truncation fails instead of throwing exception", zkdb.truncateLog(1), is(false)); } catch (NullPointerException npe) { fail("This should not throw NPE!"); } ClientBase.recursiveDelete(tmpdir); } private void append(ZKDatabase zkdb, int i) throws IOException { TxnHeader hdr = new TxnHeader(1, 1, i, 1, ZooDefs.OpCode.setData); Record txn = new SetDataTxn("/foo" + i, new byte[0], 1); Request req = new Request(0, 0, 0, hdr, txn, 0); zkdb.append(req); zkdb.commit(); } @Test public void testTruncate() throws Exception { // Prime the server that is going to come in late with 50 txns String hostPort = "127.0.0.1:" + PortAssignment.unique(); int maxCnxns = 100; ServerCnxnFactory factory = ClientBase.createNewServerInstance(null, hostPort, maxCnxns); ClientBase.startServerInstance(dataDir1, factory, hostPort, 1); ClientBase.shutdownServerInstance(factory, hostPort); // standalone starts with 0 epoch while quorum starts with 1 File origfile = new File(new File(dataDir1, "version-2"), "snapshot.0"); File newfile = new File(new File(dataDir1, "version-2"), "snapshot.100000000"); origfile.renameTo(newfile); factory = ClientBase.createNewServerInstance(null, hostPort, maxCnxns); ClientBase.startServerInstance(dataDir1, factory, hostPort, 1); ZooKeeper zk = ClientBase.createZKClient(hostPort, 15000); for (int i = 0; i < 50; i++) { zk.create("/" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); ZKDatabase zkDb; { ZooKeeperServer zs = factory.getZooKeeperServer(); zkDb = zs.getZKDatabase(); } factory.shutdown(); try { zkDb.close(); } catch (IOException ie) { LOG.warn("Error closing logs ", ie); } int tickTime = 2000; int initLimit = 3; int syncLimit = 3; int connectToLearnerMasterLimit = 3; int port1 = PortAssignment.unique(); int port2 = PortAssignment.unique(); int port3 = PortAssignment.unique(); // Start up two of the quorum and add 10 txns Map peers = new HashMap<>(); peers.put(Long.valueOf(1), new QuorumServer(1, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", port1))); peers.put(Long.valueOf(2), new QuorumServer(2, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", port2))); peers.put(Long.valueOf(3), new QuorumServer(3, new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", PortAssignment.unique()), new InetSocketAddress("127.0.0.1", port3))); QuorumPeer s2 = new QuorumPeer(peers, dataDir2, dataDir2, port2, 3, 2, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); s2.start(); QuorumPeer s3 = new QuorumPeer(peers, dataDir3, dataDir3, port3, 3, 3, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); s3.start(); zk = ClientBase.createZKClient("127.0.0.1:" + port2, 15000); for (int i = 0; i < 10; i++) { zk.create("/" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } zk.close(); final ZooKeeper zk2 = ClientBase.createZKClient("127.0.0.1:" + port2, 15000); zk2.getData("/9", false, new Stat()); try { zk2.getData("/10", false, new Stat()); fail("Should have gotten an error"); } catch (KeeperException.NoNodeException e) { // this is what we want } QuorumPeer s1 = new QuorumPeer(peers, dataDir1, dataDir1, port1, 3, 1, tickTime, initLimit, syncLimit, connectToLearnerMasterLimit); s1.start(); ZooKeeper zk1 = ClientBase.createZKClient("127.0.0.1:" + port1, 15000); zk1.getData("/9", false, new Stat()); try { // /10 wont work because the session expiration // will match the zxid for /10 and so we wont // actually truncate the zxid for /10 creation // due to an artifact of switching the xid of the standalone // /11 is the last entry in the log for the xid // as a result /12 is the first of the truncated znodes to check for zk1.getData("/12", false, new Stat()); fail("Should have gotten an error"); } catch (KeeperException.NoNodeException e) { // this is what we want } zk1.close(); QuorumBase.shutdown(s1); QuorumBase.shutdown(s2); QuorumBase.shutdown(s3); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Unsu0100644 0000000 0000000 00000000170 15051152474 032726 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/UnsupportedAddWatcherTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/UnsupportedAddWatche0100644 0000000 0000000 00000010532 15051152474 034325 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; import java.util.List; import org.apache.zookeeper.AddWatchMode; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.ACL; import org.apache.zookeeper.server.watch.IWatchManager; import org.apache.zookeeper.server.watch.WatchManagerFactory; import org.apache.zookeeper.server.watch.WatcherOrBitSet; import org.apache.zookeeper.server.watch.WatchesPathReport; import org.apache.zookeeper.server.watch.WatchesReport; import org.apache.zookeeper.server.watch.WatchesSummary; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class UnsupportedAddWatcherTest extends ClientBase { public static class StubbedWatchManager implements IWatchManager { @Override public boolean addWatch(String path, Watcher watcher) { return false; } @Override public boolean containsWatcher(String path, Watcher watcher) { return false; } @Override public boolean removeWatcher(String path, Watcher watcher) { return false; } @Override public void removeWatcher(Watcher watcher) { // NOP } @Override public WatcherOrBitSet triggerWatch(String path, Watcher.Event.EventType type, long zxid, List acl) { return new WatcherOrBitSet(Collections.emptySet()); } @Override public WatcherOrBitSet triggerWatch(String path, Watcher.Event.EventType type, long zxid, List acl, WatcherOrBitSet suppress) { return new WatcherOrBitSet(Collections.emptySet()); } @Override public int size() { return 0; } @Override public void shutdown() { // NOP } @Override public WatchesSummary getWatchesSummary() { return null; } @Override public WatchesReport getWatches() { return null; } @Override public WatchesPathReport getWatchesByPath() { return null; } @Override public void dumpWatches(PrintWriter pwriter, boolean byPath) { // NOP } } @BeforeEach public void setUp() throws Exception { System.setProperty(WatchManagerFactory.ZOOKEEPER_WATCH_MANAGER_NAME, StubbedWatchManager.class.getName()); super.setUp(); } @AfterEach public void tearDown() throws Exception { try { super.tearDown(); } finally { System.clearProperty(WatchManagerFactory.ZOOKEEPER_WATCH_MANAGER_NAME); } } @Test public void testBehavior() throws IOException, InterruptedException, KeeperException { assertThrows(KeeperException.MarshallingErrorException.class, () -> { try (ZooKeeper zk = createClient(hostPort)) { // the server will generate an exception as our custom watch manager doesn't implement // the new version of addWatch() zk.create("/foo", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.addWatch("/foo", event -> { }, AddWatchMode.PERSISTENT_RECURSIVE); } }); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Watc0100644 0000000 0000000 00000000172 15051152474 032674 xustar000000000 0000000 122 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatchEventWhenAutoResetTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatchEventWhenAutoRe0100644 0000000 0000000 00000015355 15051152474 034252 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.test.ClientBase.CountdownWatcher; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WatchEventWhenAutoResetTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(WatchEventWhenAutoResetTest.class); // waiting time for expected condition private static final int TIMEOUT = 30000; private QuorumUtil qu; private EventsWatcher watcher; private ZooKeeper zk1, zk2; public static class EventsWatcher extends CountdownWatcher { private LinkedBlockingQueue dataEvents = new LinkedBlockingQueue<>(); @Override public void process(WatchedEvent event) { super.process(event); try { if (event.getType() != Event.EventType.None) { dataEvents.put(event); } } catch (InterruptedException e) { LOG.warn("ignoring interrupt during EventsWatcher process"); } } public void assertEvent(long timeout, EventType eventType) { try { WatchedEvent event = dataEvents.poll(timeout, TimeUnit.MILLISECONDS); assertNotNull(event, "do not receive a " + eventType); assertEquals(eventType, event.getType()); } catch (InterruptedException e) { LOG.warn("ignoring interrupt during EventsWatcher assertEvent"); } } } private ZooKeeper createClient(QuorumUtil qu, int id, EventsWatcher watcher) throws IOException { String hostPort = "127.0.0.1:" + qu.getPeer(id).clientPort; ZooKeeper zk = new ZooKeeper(hostPort, TIMEOUT, watcher); try { watcher.waitForConnected(TIMEOUT); } catch (InterruptedException e) { // ignoring the interrupt } catch (TimeoutException e) { fail("can not connect to " + hostPort); } return zk; } private ZooKeeper createClient(QuorumUtil qu, int id) throws IOException { return createClient(qu, id, new EventsWatcher()); } @BeforeEach public void setUp() throws IOException { System.setProperty("zookeeper.admin.enableServer", "false"); qu = new QuorumUtil(1); qu.startAll(); watcher = new EventsWatcher(); zk1 = createClient(qu, 1, watcher); zk2 = createClient(qu, 2); } @AfterEach public void tearDown() throws InterruptedException { if (zk1 != null) { zk1.close(); zk1 = null; } if (zk2 != null) { zk2.close(); zk2 = null; } if (watcher != null) { watcher = null; } if (qu != null) { qu.shutdownAll(); qu = null; } } @Test public void testNodeDataChanged() throws Exception { String path = "/test-changed"; zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); Stat stat1 = zk1.exists(path, watcher); qu.shutdown(1); zk2.setData(path, new byte[2], stat1.getVersion()); qu.start(1); watcher.waitForConnected(TIMEOUT); watcher.assertEvent(TIMEOUT, EventType.NodeDataChanged); } @Test public void testNodeCreated() throws Exception { String path = "/test1-created"; zk1.exists(path, watcher); qu.shutdown(1); zk2.create(path, new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); qu.start(1); watcher.waitForConnected(TIMEOUT * 1000L); watcher.assertEvent(TIMEOUT, EventType.NodeCreated); } @Test public void testNodeDeleted() throws Exception { String path = "/test-deleted"; zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.getData(path, watcher, null); qu.shutdown(1); zk2.delete(path, -1); qu.start(1); watcher.waitForConnected(TIMEOUT * 1000L); watcher.assertEvent(TIMEOUT, EventType.NodeDeleted); zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.exists(path, watcher); qu.shutdown(1); zk2.delete(path, -1); qu.start(1); watcher.waitForConnected(TIMEOUT * 1000L); watcher.assertEvent(TIMEOUT, EventType.NodeDeleted); zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.getChildren(path, watcher); qu.shutdown(1); zk2.delete(path, -1); qu.start(1); watcher.waitForConnected(TIMEOUT * 1000L); watcher.assertEvent(TIMEOUT, EventType.NodeDeleted); } @Test public void testNodeChildrenChanged() throws Exception { String path = "/test-children-changed"; zk1.create(path, new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk1.getChildren(path, watcher); qu.shutdown(1); zk2.create(path + "/children-1", new byte[2], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); qu.start(1); watcher.waitForConnected(TIMEOUT * 1000L); watcher.assertEvent(TIMEOUT, EventType.NodeChildrenChanged); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Watc0100644 0000000 0000000 00000000157 15051152474 032677 xustar000000000 0000000 111 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatchedEventTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatchedEventTest.jav0100644 0000000 0000000 00000006776 15051152474 034247 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.util.EnumSet; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.proto.WatcherEvent; import org.junit.jupiter.api.Test; public class WatchedEventTest extends ZKTestCase { @Test public void testCreatingWatchedEvent() { // EventWatch is a simple, immutable type, so all we need to do // is make sure we can create all possible combinations of values. EnumSet allTypes = EnumSet.allOf(EventType.class); EnumSet allStates = EnumSet.allOf(KeeperState.class); WatchedEvent we; for (EventType et : allTypes) { for (KeeperState ks : allStates) { we = new WatchedEvent(et, ks, "blah"); assertEquals(et, we.getType()); assertEquals(ks, we.getState()); assertEquals("blah", we.getPath()); } } } @Test public void testCreatingWatchedEventFromWrapper() { // Make sure we can handle any type of correct wrapper EnumSet allTypes = EnumSet.allOf(EventType.class); EnumSet allStates = EnumSet.allOf(KeeperState.class); WatchedEvent we; WatcherEvent wep; for (EventType et : allTypes) { for (KeeperState ks : allStates) { wep = new WatcherEvent(et.getIntValue(), ks.getIntValue(), "blah"); we = new WatchedEvent(wep, WatchedEvent.NO_ZXID); assertEquals(et, we.getType()); assertEquals(ks, we.getState()); assertEquals("blah", we.getPath()); } } } @Test public void testCreatingWatchedEventFromInvalidWrapper() { // Make sure we can't convert from an invalid wrapper try { WatcherEvent wep = new WatcherEvent(-2342, -252352, "foo"); new WatchedEvent(wep, WatchedEvent.NO_ZXID); fail("Was able to create WatchedEvent from bad wrapper"); } catch (RuntimeException re) { // we're good } } @Test public void testConvertingToEventWrapper() { WatchedEvent we = new WatchedEvent(EventType.NodeCreated, KeeperState.Expired, "blah"); WatcherEvent wew = we.getWrapper(); assertEquals(EventType.NodeCreated.getIntValue(), wew.getType()); assertEquals(KeeperState.Expired.getIntValue(), wew.getState()); assertEquals("blah", wew.getPath()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_Watc0100644 0000000 0000000 00000000156 15051152474 032676 xustar000000000 0000000 110 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatcherFuncTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatcherFuncTest.java0100644 0000000 0000000 00000044375 15051152474 034235 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class WatcherFuncTest extends ClientBase { private static class SimpleWatcher implements Watcher { private LinkedBlockingQueue events = new LinkedBlockingQueue<>(); private CountDownLatch latch; public SimpleWatcher(CountDownLatch latch) { this.latch = latch; } public void process(WatchedEvent event) { if (event.getState() == KeeperState.SyncConnected) { if (latch != null) { latch.countDown(); } } if (event.getType() == EventType.None) { return; } try { events.put(event); } catch (InterruptedException e) { assertTrue(false, "interruption unexpected"); } } public void verify(List expected) throws InterruptedException { List actual = new ArrayList<>(); WatchedEvent event; while (actual.size() < expected.size() && (event = events.poll(30, TimeUnit.SECONDS)) != null) { actual.add(event); } assertEquals(expected.size(), actual.size()); for (int i = 0; i < expected.size(); i++) { TestUtils.assertWatchedEventEquals(expected.get(i), actual.get(i)); } events.clear(); } } private SimpleWatcher client_dwatch; private volatile CountDownLatch client_latch; private ZooKeeper client; private SimpleWatcher lsnr_dwatch; private volatile CountDownLatch lsnr_latch; private ZooKeeper lsnr; private List expected; @BeforeEach @Override public void setUp() throws Exception { super.setUp(); client_latch = new CountDownLatch(1); client_dwatch = new SimpleWatcher(client_latch); client = createClient(client_dwatch, client_latch); lsnr_latch = new CountDownLatch(1); lsnr_dwatch = new SimpleWatcher(lsnr_latch); lsnr = createClient(lsnr_dwatch, lsnr_latch); expected = new ArrayList<>(); } @AfterEach @Override public void tearDown() throws Exception { client.close(); lsnr.close(); super.tearDown(); } protected ZooKeeper createClient(Watcher watcher, CountDownLatch latch) throws IOException, InterruptedException { ZooKeeper zk = new ZooKeeper(hostPort, CONNECTION_TIMEOUT, watcher); if (!latch.await(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)) { fail("Unable to connect to server"); } return zk; } private void verify() throws InterruptedException { lsnr_dwatch.verify(expected); expected.clear(); } private void addEvent(List events, EventType eventType, String path, Stat stat) { addEvent(events, eventType, path, stat.getMzxid()); } private void addEvent(List events, EventType eventType, String path, long zxid) { events.add(new WatchedEvent(eventType, KeeperState.SyncConnected, path, zxid)); } private long delete(String path) throws InterruptedException, KeeperException { client.delete(path, -1); int lastSlash = path.lastIndexOf('/'); String parent = (lastSlash == 0) ? "/" : path.substring(0, lastSlash); // the deletion's zxid will be reflected in the parent's Pzxid return client.exists(parent, false).getPzxid(); } @Test public void testExistsSync() throws IOException, InterruptedException, KeeperException { assertNull(lsnr.exists("/foo", true)); assertNull(lsnr.exists("/foo/bar", true)); Stat stat = new Stat(); client.create("/foo", "parent".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); addEvent(expected, EventType.NodeCreated, "/foo", stat); client.create("/foo/bar", "child".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); addEvent(expected, EventType.NodeCreated, "/foo/bar", stat); verify(); assertNotNull(lsnr.exists("/foo", true)); assertNotNull(lsnr.exists("/foo/bar", true)); try { assertNull(lsnr.exists("/car", true)); client.setData("/car", "missing".getBytes(), -1); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/car", e.getPath()); } try { assertNull(lsnr.exists("/foo/car", true)); client.setData("/foo/car", "missing".getBytes(), -1); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo/car", e.getPath()); } stat = client.setData("/foo", "parent".getBytes(), -1); addEvent(expected, EventType.NodeDataChanged, "/foo", stat); stat = client.setData("/foo/bar", "child".getBytes(), -1); addEvent(expected, EventType.NodeDataChanged, "/foo/bar", stat); verify(); assertNotNull(lsnr.exists("/foo", true)); assertNotNull(lsnr.exists("/foo/bar", true)); long deleteZxid = delete("/foo/bar"); addEvent(expected, EventType.NodeDeleted, "/foo/bar", deleteZxid); deleteZxid = delete("/foo"); addEvent(expected, EventType.NodeDeleted, "/foo", deleteZxid); verify(); } @Test public void testGetDataSync() throws IOException, InterruptedException, KeeperException { try { lsnr.getData("/foo", true, null); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo", e.getPath()); } try { lsnr.getData("/foo/bar", true, null); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo/bar", e.getPath()); } client.create("/foo", "parent".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(lsnr.getData("/foo", true, null)); client.create("/foo/bar", "child".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(lsnr.getData("/foo/bar", true, null)); Stat stat = client.setData("/foo", "parent".getBytes(), -1); addEvent(expected, EventType.NodeDataChanged, "/foo", stat); stat = client.setData("/foo/bar", "child".getBytes(), -1); addEvent(expected, EventType.NodeDataChanged, "/foo/bar", stat); verify(); assertNotNull(lsnr.getData("/foo", true, null)); assertNotNull(lsnr.getData("/foo/bar", true, null)); long deleteZxid = delete("/foo/bar"); addEvent(expected, EventType.NodeDeleted, "/foo/bar", deleteZxid); deleteZxid = delete("/foo"); addEvent(expected, EventType.NodeDeleted, "/foo", deleteZxid); verify(); } @Test public void testGetChildrenSync() throws IOException, InterruptedException, KeeperException { try { lsnr.getChildren("/foo", true); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo", e.getPath()); } try { lsnr.getChildren("/foo/bar", true); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo/bar", e.getPath()); } client.create("/foo", "parent".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(lsnr.getChildren("/foo", true)); Stat stat = new Stat(); client.create("/foo/bar", "child".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); addEvent(expected, EventType.NodeChildrenChanged, "/foo", stat); // /foo assertNotNull(lsnr.getChildren("/foo/bar", true)); client.setData("/foo", "parent".getBytes(), -1); client.setData("/foo/bar", "child".getBytes(), -1); assertNotNull(lsnr.exists("/foo", true)); assertNotNull(lsnr.getChildren("/foo", true)); assertNotNull(lsnr.getChildren("/foo/bar", true)); long deleteZxid = delete("/foo/bar"); addEvent(expected, EventType.NodeDeleted, "/foo/bar", deleteZxid); // /foo/bar childwatch addEvent(expected, EventType.NodeChildrenChanged, "/foo", deleteZxid); // /foo deleteZxid = delete("/foo"); addEvent(expected, EventType.NodeDeleted, "/foo", deleteZxid); verify(); } @Test public void testExistsSyncWObj() throws IOException, InterruptedException, KeeperException { SimpleWatcher w1 = new SimpleWatcher(null); SimpleWatcher w2 = new SimpleWatcher(null); SimpleWatcher w3 = new SimpleWatcher(null); SimpleWatcher w4 = new SimpleWatcher(null); List e2 = new ArrayList<>(); assertNull(lsnr.exists("/foo", true)); assertNull(lsnr.exists("/foo", w1)); assertNull(lsnr.exists("/foo/bar", w2)); assertNull(lsnr.exists("/foo/bar", w3)); assertNull(lsnr.exists("/foo/bar", w3)); assertNull(lsnr.exists("/foo/bar", w4)); Stat stat = new Stat(); client.create("/foo", "parent".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); addEvent(expected, EventType.NodeCreated, "/foo", stat); client.create("/foo/bar", "child".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); addEvent(e2, EventType.NodeCreated, "/foo/bar", stat); lsnr_dwatch.verify(expected); w1.verify(expected); w2.verify(e2); w3.verify(e2); w4.verify(e2); expected.clear(); e2.clear(); // default not registered assertNotNull(lsnr.exists("/foo", w1)); assertNotNull(lsnr.exists("/foo/bar", w2)); assertNotNull(lsnr.exists("/foo/bar", w3)); assertNotNull(lsnr.exists("/foo/bar", w4)); assertNotNull(lsnr.exists("/foo/bar", w4)); stat = client.setData("/foo", "parent".getBytes(), -1); addEvent(expected, EventType.NodeDataChanged, "/foo", stat); stat = client.setData("/foo/bar", "child".getBytes(), -1); addEvent(e2, EventType.NodeDataChanged, "/foo/bar", stat); lsnr_dwatch.verify(new ArrayList<>()); // not reg so should = 0 w1.verify(expected); w2.verify(e2); w3.verify(e2); w4.verify(e2); expected.clear(); e2.clear(); assertNotNull(lsnr.exists("/foo", true)); assertNotNull(lsnr.exists("/foo", w1)); assertNotNull(lsnr.exists("/foo", w1)); assertNotNull(lsnr.exists("/foo/bar", w2)); assertNotNull(lsnr.exists("/foo/bar", w2)); assertNotNull(lsnr.exists("/foo/bar", w3)); assertNotNull(lsnr.exists("/foo/bar", w4)); long deleteZxid = delete("/foo/bar"); addEvent(e2, EventType.NodeDeleted, "/foo/bar", deleteZxid); deleteZxid = delete("/foo"); addEvent(expected, EventType.NodeDeleted, "/foo", deleteZxid); lsnr_dwatch.verify(expected); w1.verify(expected); w2.verify(e2); w3.verify(e2); w4.verify(e2); expected.clear(); e2.clear(); } @Test public void testGetDataSyncWObj() throws IOException, InterruptedException, KeeperException { SimpleWatcher w1 = new SimpleWatcher(null); SimpleWatcher w2 = new SimpleWatcher(null); SimpleWatcher w3 = new SimpleWatcher(null); SimpleWatcher w4 = new SimpleWatcher(null); List e2 = new ArrayList<>(); try { lsnr.getData("/foo", w1, null); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo", e.getPath()); } try { lsnr.getData("/foo/bar", w2, null); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo/bar", e.getPath()); } client.create("/foo", "parent".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(lsnr.getData("/foo", true, null)); assertNotNull(lsnr.getData("/foo", w1, null)); client.create("/foo/bar", "child".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(lsnr.getData("/foo/bar", w2, null)); assertNotNull(lsnr.getData("/foo/bar", w3, null)); assertNotNull(lsnr.getData("/foo/bar", w4, null)); assertNotNull(lsnr.getData("/foo/bar", w4, null)); Stat stat = client.setData("/foo", "parent".getBytes(), -1); addEvent(expected, EventType.NodeDataChanged, "/foo", stat); stat = client.setData("/foo/bar", "child".getBytes(), -1); addEvent(e2, EventType.NodeDataChanged, "/foo/bar", stat); lsnr_dwatch.verify(expected); w1.verify(expected); w2.verify(e2); w3.verify(e2); w4.verify(e2); expected.clear(); e2.clear(); assertNotNull(lsnr.getData("/foo", true, null)); assertNotNull(lsnr.getData("/foo", w1, null)); assertNotNull(lsnr.getData("/foo/bar", w2, null)); assertNotNull(lsnr.getData("/foo/bar", w3, null)); assertNotNull(lsnr.getData("/foo/bar", w3, null)); assertNotNull(lsnr.getData("/foo/bar", w4, null)); long deleteZxid = delete("/foo/bar"); addEvent(e2, EventType.NodeDeleted, "/foo/bar", deleteZxid); deleteZxid = delete("/foo"); addEvent(expected, EventType.NodeDeleted, "/foo", deleteZxid); lsnr_dwatch.verify(expected); w1.verify(expected); w2.verify(e2); w3.verify(e2); w4.verify(e2); expected.clear(); e2.clear(); } @Test public void testGetChildrenSyncWObj() throws IOException, InterruptedException, KeeperException { SimpleWatcher w1 = new SimpleWatcher(null); SimpleWatcher w2 = new SimpleWatcher(null); SimpleWatcher w3 = new SimpleWatcher(null); SimpleWatcher w4 = new SimpleWatcher(null); List e2 = new ArrayList<>(); try { lsnr.getChildren("/foo", true); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo", e.getPath()); } try { lsnr.getChildren("/foo/bar", true); fail(); } catch (KeeperException e) { assertEquals(KeeperException.Code.NONODE, e.code()); assertEquals("/foo/bar", e.getPath()); } client.create("/foo", "parent".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); assertNotNull(lsnr.getChildren("/foo", true)); assertNotNull(lsnr.getChildren("/foo", w1)); Stat stat = new Stat(); client.create("/foo/bar", "child".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); addEvent(expected, EventType.NodeChildrenChanged, "/foo", stat); // /foo assertNotNull(lsnr.getChildren("/foo/bar", w2)); assertNotNull(lsnr.getChildren("/foo/bar", w2)); assertNotNull(lsnr.getChildren("/foo/bar", w3)); assertNotNull(lsnr.getChildren("/foo/bar", w4)); client.setData("/foo", "parent".getBytes(), -1); client.setData("/foo/bar", "child".getBytes(), -1); assertNotNull(lsnr.exists("/foo", true)); assertNotNull(lsnr.exists("/foo", w1)); assertNotNull(lsnr.exists("/foo", true)); assertNotNull(lsnr.exists("/foo", w1)); assertNotNull(lsnr.getChildren("/foo", true)); assertNotNull(lsnr.getChildren("/foo", w1)); assertNotNull(lsnr.getChildren("/foo/bar", w2)); assertNotNull(lsnr.getChildren("/foo/bar", w3)); assertNotNull(lsnr.getChildren("/foo/bar", w4)); assertNotNull(lsnr.getChildren("/foo/bar", w4)); long deleteZxid = delete("/foo/bar"); addEvent(e2, EventType.NodeDeleted, "/foo/bar", deleteZxid); addEvent(expected, EventType.NodeChildrenChanged, "/foo", deleteZxid); // /foo deleteZxid = delete("/foo"); addEvent(expected, EventType.NodeDeleted, "/foo", deleteZxid); lsnr_dwatch.verify(expected); w1.verify(expected); w2.verify(e2); w3.verify(e2); w4.verify(e2); expected.clear(); e2.clear(); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/WatcherTest.java0100644 0000000 0000000 00000041754 15051152474 033417 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.zookeeper.AsyncCallback.StatCallback; import org.apache.zookeeper.AsyncCallback.VoidCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.TestableZooKeeper; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.client.ZKClientConfig; import org.apache.zookeeper.data.Stat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WatcherTest extends ClientBase { protected static final Logger LOG = LoggerFactory.getLogger(WatcherTest.class); private long timeOfLastWatcherInvocation; private static final class MyStatCallback implements StatCallback { int rc; public void processResult(int rc, String path, Object ctx, Stat stat) { ((int[]) ctx)[0]++; this.rc = rc; } } private class MyWatcher extends CountdownWatcher { LinkedBlockingQueue events = new LinkedBlockingQueue<>(); public void process(WatchedEvent event) { super.process(event); if (event.getType() != Event.EventType.None) { timeOfLastWatcherInvocation = System.currentTimeMillis(); try { events.put(event); } catch (InterruptedException e) { LOG.warn("ignoring interrupt during event.put"); } } } } @BeforeEach public void setUp() throws Exception { super.setUp(); // Reset to default value since some test cases set this to true. // Needed for JDK7 since unit test can run is random order System.setProperty(ZKClientConfig.DISABLE_AUTO_WATCH_RESET, "false"); } /** * Verify that we get all of the events we expect to get. This particular * case verifies that we see all of the data events on a particular node. * There was a bug (ZOOKEEPER-137) that resulted in events being dropped * in some cases (timing). * * @throws IOException * @throws InterruptedException * @throws KeeperException */ @Test public void testWatcherCorrectness() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { MyWatcher watcher = new MyWatcher(); zk = createClient(watcher, hostPort); StatCallback scb = new StatCallback() { public void processResult(int rc, String path, Object ctx, Stat stat) { // don't do anything } }; VoidCallback vcb = new VoidCallback() { public void processResult(int rc, String path, Object ctx) { // don't do anything } }; String[] names = new String[10]; for (int i = 0; i < names.length; i++) { String name = zk.create("/tc-", "initialvalue".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); names[i] = name; Stat stat = new Stat(); zk.getData(name, watcher, stat); zk.setData(name, "new".getBytes(), stat.getVersion(), scb, null); stat = zk.exists(name, watcher); zk.delete(name, stat.getVersion(), vcb, null); } for (int i = 0; i < names.length; i++) { String name = names[i]; WatchedEvent event = watcher.events.poll(10, TimeUnit.SECONDS); assertEquals(name, event.getPath()); assertEquals(Event.EventType.NodeDataChanged, event.getType()); assertEquals(Event.KeeperState.SyncConnected, event.getState()); event = watcher.events.poll(10, TimeUnit.SECONDS); assertEquals(name, event.getPath()); assertEquals(Event.EventType.NodeDeleted, event.getType()); assertEquals(Event.KeeperState.SyncConnected, event.getState()); } } finally { if (zk != null) { zk.close(); } } } @Test public void testWatcherDisconnectOnClose() throws IOException, InterruptedException, KeeperException { ZooKeeper zk = null; try { final BlockingQueue queue = new LinkedBlockingQueue<>(); MyWatcher connWatcher = new MyWatcher(); Watcher watcher = event -> { try { queue.put(event); } catch (InterruptedException e) { // Oh well, never mind } }; zk = createClient(connWatcher, hostPort); StatCallback scb = new StatCallback() { public void processResult(int rc, String path, Object ctx, Stat stat) { // don't do anything } }; // Register a watch on the node zk.exists("/missing", watcher, scb, null); // Close the client without changing the node zk.close(); WatchedEvent event = queue.poll(10, TimeUnit.SECONDS); assertNotNull(event, "No watch event was received after closing the Zookeeper client. A 'Closed' event should have occurred"); assertEquals(Event.EventType.None, event.getType(), "Closed events are not generated by the server, and so should have a type of 'None'"); assertEquals(Event.KeeperState.Closed, event.getState(), "A 'Closed' event was expected as the Zookeeper client was closed without altering the node it was watching"); } finally { if (zk != null) { zk.close(); } } } @Test public void testWatcherCount() throws IOException, InterruptedException, KeeperException { ZooKeeper zk1 = null, zk2 = null; try { MyWatcher w1 = new MyWatcher(); zk1 = createClient(w1, hostPort); MyWatcher w2 = new MyWatcher(); zk2 = createClient(w2, hostPort); Stat stat = new Stat(); zk1.create("/watch-count-test", "value".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); zk1.create("/watch-count-test-2", "value".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); zk1.getData("/watch-count-test", w1, stat); zk1.getData("/watch-count-test-2", w1, stat); zk2.getData("/watch-count-test", w2, stat); assertEquals(serverFactory.getZooKeeperServer().getZKDatabase().getDataTree().getWatchCount(), 3); } finally { if (zk1 != null) { zk1.close(); } if (zk2 != null) { zk2.close(); } } } static final int COUNT = 100; /** * This test checks that watches for pending requests do not get triggered, * but watches set by previous requests do. * * @throws Exception */ @Test public void testWatchAutoResetWithPending() throws Exception { MyWatcher[] watches = new MyWatcher[COUNT]; MyStatCallback[] cbs = new MyStatCallback[COUNT]; MyWatcher watcher = new MyWatcher(); int[] count = new int[1]; TestableZooKeeper zk = createClient(watcher, hostPort, 6000); ZooKeeper zk2 = createClient(watcher, hostPort, 5000); zk2.create("/test", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); for (int i = 0; i < COUNT / 2; i++) { watches[i] = new MyWatcher(); cbs[i] = new MyStatCallback(); zk.exists("/test", watches[i], cbs[i], count); } zk.exists("/test", false); assertTrue(zk.pauseCnxn(3000), "Failed to pause the connection!"); zk2.close(); stopServer(); watches[0].waitForDisconnected(60000); for (int i = COUNT / 2; i < COUNT; i++) { watches[i] = new MyWatcher(); cbs[i] = new MyStatCallback(); zk.exists("/test", watches[i], cbs[i], count); } startServer(); watches[COUNT / 2 - 1].waitForConnected(60000); assertEquals(null, zk.exists("/test", false)); waitForAllWatchers(); for (int i = 0; i < COUNT / 2; i++) { assertEquals(1, watches[i].events.size(), "For " + i); } for (int i = COUNT / 2; i < COUNT; i++) { if (cbs[i].rc == KeeperException.Code.OK.intValue()) { assertEquals(1, watches[i].events.size(), "For " + i); } else { assertEquals(0, watches[i].events.size(), "For " + i); } } assertEquals(COUNT, count[0]); zk.close(); } /** * Wait until no watcher has been fired in the last second to ensure that all watches * that are waiting to be fired have been fired * @throws Exception */ private void waitForAllWatchers() throws Exception { timeOfLastWatcherInvocation = System.currentTimeMillis(); while (System.currentTimeMillis() - timeOfLastWatcherInvocation < 1000) { Thread.sleep(1000); } } final int TIMEOUT = 5000; @Test public void testWatcherAutoResetWithGlobal() throws Exception { ZooKeeper zk = null; MyWatcher watcher = new MyWatcher(); zk = createClient(watcher, hostPort, TIMEOUT); testWatcherAutoReset(zk, watcher, watcher); zk.close(); } @Test public void testWatcherAutoResetWithLocal() throws Exception { ZooKeeper zk = null; MyWatcher watcher = new MyWatcher(); zk = createClient(watcher, hostPort, TIMEOUT); testWatcherAutoReset(zk, watcher, new MyWatcher()); zk.close(); } @Test public void testWatcherAutoResetDisabledWithGlobal() throws Exception { /** * When ZooKeeper is created this property will get used. */ System.setProperty(ZKClientConfig.DISABLE_AUTO_WATCH_RESET, "true"); testWatcherAutoResetWithGlobal(); } @Test public void testWatcherAutoResetDisabledWithLocal() throws Exception { System.setProperty(ZKClientConfig.DISABLE_AUTO_WATCH_RESET, "true"); testWatcherAutoResetWithLocal(); } private void testWatcherAutoReset(ZooKeeper zk, MyWatcher globalWatcher, MyWatcher localWatcher) throws Exception { boolean isGlobal = (localWatcher == globalWatcher); // First test to see if the watch survives across reconnects zk.create("/watchtest", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/watchtest/child", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); if (isGlobal) { zk.getChildren("/watchtest", true); zk.getData("/watchtest/child", true, new Stat()); zk.exists("/watchtest/child2", true); } else { zk.getChildren("/watchtest", localWatcher); zk.getData("/watchtest/child", localWatcher, new Stat()); zk.exists("/watchtest/child2", localWatcher); } assertTrue(localWatcher.events.isEmpty()); stopServer(); globalWatcher.waitForDisconnected(3000); localWatcher.waitForDisconnected(500); startServer(); globalWatcher.waitForConnected(3000); boolean disableAutoWatchReset = zk.getClientConfig().getBoolean(ZKClientConfig.DISABLE_AUTO_WATCH_RESET); if (!isGlobal && !disableAutoWatchReset) { localWatcher.waitForConnected(500); } assertTrue(localWatcher.events.isEmpty()); zk.setData("/watchtest/child", new byte[1], -1); zk.create("/watchtest/child2", new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); WatchedEvent e; if (!disableAutoWatchReset) { e = localWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(EventType.NodeDataChanged, e.getType(), e.getPath()); assertEquals("/watchtest/child", e.getPath()); } else { // we'll catch this later if it does happen after timeout, so // why waste the time on poll } if (!disableAutoWatchReset) { e = localWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); // The create will trigger the get children and the exist // watches assertEquals(EventType.NodeCreated, e.getType()); assertEquals("/watchtest/child2", e.getPath()); } else { // we'll catch this later if it does happen after timeout, so // why waste the time on poll } if (!disableAutoWatchReset) { e = localWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/watchtest", e.getPath()); } else { // we'll catch this later if it does happen after timeout, so // why waste the time on poll } assertTrue(localWatcher.events.isEmpty()); // ensure no late arrivals stopServer(); globalWatcher.waitForDisconnected(TIMEOUT); try { try { localWatcher.waitForDisconnected(500); if (!isGlobal && !disableAutoWatchReset) { fail("Got an event when I shouldn't have"); } } catch (TimeoutException toe) { if (disableAutoWatchReset) { fail("Didn't get an event when I should have"); } // Else what we are expecting since there are no outstanding watches } } catch (Exception e1) { LOG.error("bad", e1); throw new RuntimeException(e1); } startServer(); globalWatcher.waitForConnected(TIMEOUT); if (isGlobal) { zk.getChildren("/watchtest", true); zk.getData("/watchtest/child", true, new Stat()); zk.exists("/watchtest/child2", true); } else { zk.getChildren("/watchtest", localWatcher); zk.getData("/watchtest/child", localWatcher, new Stat()); zk.exists("/watchtest/child2", localWatcher); } // Do trigger an event to make sure that we do not get // it later zk.delete("/watchtest/child2", -1); e = localWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(EventType.NodeDeleted, e.getType()); assertEquals("/watchtest/child2", e.getPath()); e = localWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(EventType.NodeChildrenChanged, e.getType()); assertEquals("/watchtest", e.getPath()); assertTrue(localWatcher.events.isEmpty()); stopServer(); globalWatcher.waitForDisconnected(TIMEOUT); localWatcher.waitForDisconnected(500); startServer(); globalWatcher.waitForConnected(TIMEOUT); if (!isGlobal && !disableAutoWatchReset) { localWatcher.waitForConnected(500); } zk.delete("/watchtest/child", -1); zk.delete("/watchtest", -1); if (!disableAutoWatchReset) { e = localWatcher.events.poll(TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(EventType.NodeDeleted, e.getType()); assertEquals("/watchtest/child", e.getPath()); } else { // we'll catch this later if it does happen after timeout, so // why waste the time on poll } // Make sure nothing is straggling! Thread.sleep(1000); assertTrue(localWatcher.events.isEmpty()); } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/X509AuthTest.java0100644 0000000 0000000 00000026370 15051152474 033306 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import java.math.BigInteger; import java.net.Socket; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Principal; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Set; import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509TrustManager; import javax.security.auth.x500.X500Principal; import javax.servlet.http.HttpServletRequest; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.MockServerCnxn; import org.apache.zookeeper.server.auth.X509AuthenticationProvider; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; public class X509AuthTest extends ZKTestCase { private static TestCertificate clientCert; private static TestCertificate superCert; private static TestCertificate unknownCert; @BeforeEach public void setUp() { System.setProperty("zookeeper.X509AuthenticationProvider.superUser", "CN=SUPER"); System.setProperty("zookeeper.ssl.keyManager", "org.apache.zookeeper.test.X509AuthTest.TestKeyManager"); System.setProperty("zookeeper.ssl.trustManager", "org.apache.zookeeper.test.X509AuthTest.TestTrustManager"); clientCert = new TestCertificate("CLIENT"); superCert = new TestCertificate("SUPER"); unknownCert = new TestCertificate("UNKNOWN"); } @Test public void testTrustedAuth() { X509AuthenticationProvider provider = createProvider(clientCert); MockServerCnxn cnxn = new MockServerCnxn(); cnxn.clientChain = new X509Certificate[]{clientCert}; assertEquals(KeeperException.Code.OK, provider.handleAuthentication(cnxn, null)); final List ids = Arrays.asList(new Id("x509", "CN=CLIENT")); assertEquals(ids, cnxn.getAuthInfo()); } @Test public void testSuperAuth() { X509AuthenticationProvider provider = createProvider(superCert); MockServerCnxn cnxn = new MockServerCnxn(); cnxn.clientChain = new X509Certificate[]{superCert}; assertEquals(KeeperException.Code.OK, provider.handleAuthentication(cnxn, null)); final List ids = Arrays.asList(new Id("super", "CN=SUPER"), new Id("x509", "CN=SUPER")); assertEquals(ids, cnxn.getAuthInfo()); } @Test public void testUntrustedAuth() { X509AuthenticationProvider provider = createProvider(clientCert); MockServerCnxn cnxn = new MockServerCnxn(); cnxn.clientChain = new X509Certificate[]{unknownCert}; assertEquals(KeeperException.Code.AUTHFAILED, provider.handleAuthentication(cnxn, null)); } @Test public void testTrustedAuth_HttpServletRequest() { final X509AuthenticationProvider provider = createProvider(clientCert); final HttpServletRequest mockRequest = mock(HttpServletRequest.class); Mockito.doReturn(new X509Certificate[]{clientCert}).when(mockRequest).getAttribute(X509AuthenticationProvider.X509_CERTIFICATE_ATTRIBUTE_NAME); final List ids = Arrays.asList(new Id("x509", "CN=CLIENT")); assertEquals(ids, provider.handleAuthentication(mockRequest, null)); } @Test public void testSuperAuth_HttpServletRequest() { final X509AuthenticationProvider provider = createProvider(superCert); final HttpServletRequest mockRequest = mock(HttpServletRequest.class); Mockito.doReturn(new X509Certificate[]{superCert}).when(mockRequest).getAttribute(X509AuthenticationProvider.X509_CERTIFICATE_ATTRIBUTE_NAME); final List ids = Arrays.asList(new Id("super", "CN=SUPER"), new Id("x509", "CN=SUPER")); assertEquals(ids, provider.handleAuthentication(mockRequest, null)); } @Test public void testUntrustedAuth_HttpServletRequest() { final X509AuthenticationProvider provider = createProvider(clientCert); final HttpServletRequest mockRequest = mock(HttpServletRequest.class); Mockito.doReturn(new X509Certificate[]{unknownCert}).when(mockRequest).getAttribute(X509AuthenticationProvider.X509_CERTIFICATE_ATTRIBUTE_NAME); assertTrue(provider.handleAuthentication(mockRequest, null).isEmpty()); } private static class TestPublicKey implements PublicKey { private static final long serialVersionUID = 1L; @Override public String getAlgorithm() { return null; } @Override public String getFormat() { return null; } @Override public byte[] getEncoded() { return null; } } private static class TestCertificate extends X509Certificate { private byte[] encoded; private X500Principal principal; private PublicKey publicKey; public TestCertificate(String name) { encoded = name.getBytes(); principal = new X500Principal("CN=" + name); publicKey = new TestPublicKey(); } @Override public boolean hasUnsupportedCriticalExtension() { return false; } @Override public Set getCriticalExtensionOIDs() { return null; } @Override public Set getNonCriticalExtensionOIDs() { return null; } @Override public byte[] getExtensionValue(String oid) { return null; } @Override public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException { } @Override public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException { } @Override public int getVersion() { return 0; } @Override public BigInteger getSerialNumber() { return null; } @Override public Principal getIssuerDN() { return null; } @Override public Principal getSubjectDN() { return null; } @Override public Date getNotBefore() { return null; } @Override public Date getNotAfter() { return null; } @Override public byte[] getTBSCertificate() throws CertificateEncodingException { return null; } @Override public byte[] getSignature() { return null; } @Override public String getSigAlgName() { return null; } @Override public String getSigAlgOID() { return null; } @Override public byte[] getSigAlgParams() { return null; } @Override public boolean[] getIssuerUniqueID() { return null; } @Override public boolean[] getSubjectUniqueID() { return null; } @Override public boolean[] getKeyUsage() { return null; } @Override public int getBasicConstraints() { return 0; } @Override public byte[] getEncoded() throws CertificateEncodingException { return encoded; } @Override public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } @Override public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException { } @Override public String toString() { return null; } @Override public PublicKey getPublicKey() { return publicKey; } @Override public X500Principal getSubjectX500Principal() { return principal; } } public static class TestKeyManager implements X509KeyManager { @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return null; } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return null; } @Override public X509Certificate[] getCertificateChain(String alias) { return null; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return null; } @Override public PrivateKey getPrivateKey(String alias) { return null; } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return null; } } public static class TestTrustManager implements X509TrustManager { X509Certificate cert; public TestTrustManager(X509Certificate testCert) { cert = testCert; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!Arrays.equals(cert.getEncoded(), chain[0].getEncoded())) { throw new CertificateException("Client cert not trusted"); } } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!Arrays.equals(cert.getEncoded(), chain[0].getEncoded())) { throw new CertificateException("Server cert not trusted"); } } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } protected X509AuthenticationProvider createProvider(X509Certificate trustedCert) { return new X509AuthenticationProvider(new TestTrustManager(trustedCert), new TestKeyManager()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_ZkDa0100644 0000000 0000000 00000000167 15051152474 032633 xustar000000000 0000000 119 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ZkDatabaseCorruptionTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ZkDatabaseCorruption0100644 0000000 0000000 00000014775 15051152474 034343 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Arrays; import org.apache.zookeeper.AsyncCallback; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.server.SyncRequestProcessor; import org.apache.zookeeper.server.ZKDatabase; import org.apache.zookeeper.server.persistence.FileTxnSnapLog; import org.apache.zookeeper.server.quorum.QuorumPeer; import org.apache.zookeeper.server.quorum.QuorumPeer.ServerState; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ZkDatabaseCorruptionTest extends ZKTestCase { protected static final Logger LOG = LoggerFactory.getLogger(ZkDatabaseCorruptionTest.class); public static final long CONNECTION_TIMEOUT = ClientTest.CONNECTION_TIMEOUT; private final QuorumBase qb = new QuorumBase(); @BeforeEach public void setUp() throws Exception { LOG.info("STARTING quorum {}", getClass().getName()); qb.setUp(); } @AfterEach public void tearDown() throws Exception { LOG.info("STOPPING quorum {}", getClass().getName()); } private void corruptFile(File f) throws IOException { RandomAccessFile outFile = new RandomAccessFile(f, "rw"); outFile.write("fail servers".getBytes()); outFile.close(); } private void corruptAllSnapshots(File snapDir) throws IOException { File[] listFiles = snapDir.listFiles(); for (File f : listFiles) { if (f.getName().startsWith("snapshot")) { corruptFile(f); } } } private class NoopStringCallback implements AsyncCallback.StringCallback { @Override public void processResult(int rc, String path, Object ctx, String name) { } } @Test public void testCorruption() throws Exception { ClientBase.waitForServerUp(qb.hostPort, 10000); ClientBase.waitForServerUp(qb.hostPort, 10000); ZooKeeper zk = ClientBase.createZKClient(qb.hostPort, 10000); SyncRequestProcessor.setSnapCount(100); for (int i = 0; i < 2000; i++) { zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new NoopStringCallback(), null); } zk.close(); long leaderSid = 1; QuorumPeer leader = null; //find out who is the leader and kill it for (QuorumPeer quorumPeer : Arrays.asList(qb.s1, qb.s2, qb.s3, qb.s4, qb.s5)) { if (quorumPeer.getPeerState() == ServerState.LEADING) { leader = quorumPeer; break; } ++leaderSid; } assertNotNull(leader, "Cannot find the leader."); leader.shutdown(); // now corrupt the leader's database FileTxnSnapLog snapLog = leader.getTxnFactory(); File snapDir = snapLog.getSnapDir(); //corrupt all the snapshot in the snapshot directory corruptAllSnapshots(snapDir); qb.shutdownServers(); qb.setupServers(); if (leaderSid != 1) { qb.s1.start(); } else { leader = qb.s1; } if (leaderSid != 2) { qb.s2.start(); } else { leader = qb.s2; } if (leaderSid != 3) { qb.s3.start(); } else { leader = qb.s3; } if (leaderSid != 4) { qb.s4.start(); } else { leader = qb.s4; } if (leaderSid != 5) { qb.s5.start(); } else { leader = qb.s5; } try { leader.start(); assertTrue(false); } catch (RuntimeException re) { LOG.info("Got an error: expected", re); } //wait for servers to be up String[] list = qb.hostPort.split(","); for (int i = 0; i < 5; i++) { if (leaderSid != (i + 1)) { String hp = list[i]; assertTrue(ClientBase.waitForServerUp(hp, CONNECTION_TIMEOUT), "waiting for server up"); LOG.info("{} is accepting client connections", hp); } else { LOG.info("Skipping the leader"); } } zk = qb.createClient(); SyncRequestProcessor.setSnapCount(100); for (int i = 2000; i < 4000; i++) { zk.create("/0-" + i, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, new NoopStringCallback(), null); } zk.close(); if (leaderSid != 1) { QuorumBase.shutdown(qb.s1); } if (leaderSid != 2) { QuorumBase.shutdown(qb.s2); } if (leaderSid != 3) { QuorumBase.shutdown(qb.s3); } if (leaderSid != 4) { QuorumBase.shutdown(qb.s4); } if (leaderSid != 5) { QuorumBase.shutdown(qb.s5); } } @Test public void testAbsentRecentSnapshot() throws IOException { ZKDatabase zkDatabase = new ZKDatabase(new FileTxnSnapLog(new File("foo"), new File("bar")) { @Override public File findMostRecentSnapshot() throws IOException { return null; } }); assertEquals(0, zkDatabase.calculateTxnLogSizeLimit()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_ZooK0100644 0000000 0000000 00000000161 15051152474 032656 xustar000000000 0000000 113 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ZooKeeperQuotaTest.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ZooKeeperQuotaTest.j0100644 0000000 0000000 00000056633 15051152474 034251 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.QuotaExceededException; import org.apache.zookeeper.Op; import org.apache.zookeeper.Quotas; import org.apache.zookeeper.StatsTrack; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.cli.DelQuotaCommand; import org.apache.zookeeper.cli.ListQuotaCommand; import org.apache.zookeeper.cli.MalformedPathException; import org.apache.zookeeper.cli.SetQuotaCommand; import org.apache.zookeeper.data.Stat; import org.apache.zookeeper.metrics.MetricsUtils; import org.apache.zookeeper.server.ZooKeeperServer; import org.apache.zookeeper.server.util.QuotaMetricsUtils; import org.apache.zookeeper.test.StatsTrackTest.OldStatsTrack; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class ZooKeeperQuotaTest extends ClientBase { private ZooKeeper zk = null; @BeforeEach @Override public void setUp() throws Exception { System.setProperty(ZooKeeperServer.ENFORCE_QUOTA, "true"); super.setUp(); zk = createClient(); } @AfterEach @Override public void tearDown() throws Exception { System.clearProperty(ZooKeeperServer.ENFORCE_QUOTA); super.tearDown(); zk.close(); } @Test public void testQuota() throws Exception { final String path = "/a/b/v"; // making sure setdata works on / zk.setData("/", "some".getBytes(), -1); zk.create("/a", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/a/b/v/d", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack quota = new StatsTrack(); quota.setCount(4); quota.setCountHardLimit(4); quota.setBytes(9L); quota.setByteHardLimit(15L); SetQuotaCommand.createQuota(zk, path, quota); // see if its set String absolutePath = Quotas.limitPath(path); byte[] data = zk.getData(absolutePath, false, new Stat()); StatsTrack st = new StatsTrack(data); assertTrue(st.getBytes() == 9L, "bytes are set"); assertTrue(st.getByteHardLimit() == 15L, "byte hard limit is set"); assertTrue(st.getCount() == 4, "num count is set"); assertTrue(st.getCountHardLimit() == 4, "count hard limit is set"); // check quota node readable by old servers OldStatsTrack ost = new OldStatsTrack(new String(data)); assertTrue(ost.getBytes() == 9L, "bytes are set"); assertTrue(ost.getCount() == 4, "num count is set"); String statPath = Quotas.statPath(path); byte[] qdata = zk.getData(statPath, false, new Stat()); StatsTrack qst = new StatsTrack(qdata); assertTrue(qst.getBytes() == 8L, "bytes are set"); assertTrue(qst.getCount() == 2, "count is set"); //force server to restart and load from snapshot, not txn log stopServer(); startServer(); stopServer(); startServer(); ZooKeeperServer server = serverFactory.getZooKeeperServer(); assertNotNull(server.getZKDatabase().getDataTree().getMaxPrefixWithQuota(path) != null, "Quota is still set"); } @Test public void testSetQuota() throws IOException, InterruptedException, KeeperException, MalformedPathException { String path = "/c1"; String nodeData = "foo"; zk.create(path, nodeData.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 10; long bytes = 5L; StatsTrack quota = new StatsTrack(); quota.setCount(count); quota.setBytes(bytes); SetQuotaCommand.createQuota(zk, path, quota); //check the limit String absoluteLimitPath = Quotas.limitPath(path); byte[] data = zk.getData(absoluteLimitPath, false, null); StatsTrack st = new StatsTrack(data); assertEquals(bytes, st.getBytes()); assertEquals(count, st.getCount()); //check the stats String absoluteStatPath = Quotas.statPath(path); data = zk.getData(absoluteStatPath, false, null); st = new StatsTrack(data); assertEquals(nodeData.length(), st.getBytes()); assertEquals(1, st.getCount()); //create another node String path2 = "/c1/c2"; String nodeData2 = "bar"; zk.create(path2, nodeData2.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); absoluteStatPath = Quotas.statPath(path); data = zk.getData(absoluteStatPath, false, null); st = new StatsTrack(data); //check the stats assertEquals(nodeData.length() + nodeData2.length(), st.getBytes()); assertEquals(2, st.getCount()); } @Test public void testSetQuotaWhenSetQuotaOnParentOrChildPath() throws IOException, InterruptedException, KeeperException, MalformedPathException { zk.create("/c1", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/c1/c2", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/c1/c2/c3", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/c1/c2/c3/c4", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create("/c1/c2/c3/c4/c5", "some".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); //set the quota on the path:/c1/c2/c3 StatsTrack quota = new StatsTrack(); quota.setCount(5); quota.setBytes(10); SetQuotaCommand.createQuota(zk, "/c1/c2/c3", quota); try { SetQuotaCommand.createQuota(zk, "/c1", quota); fail("should not set quota when child has a quota"); } catch (IllegalArgumentException e) { assertEquals("/c1 has a child /c1/c2/c3 which has a quota", e.getMessage()); } try { SetQuotaCommand.createQuota(zk, "/c1/c2/c3/c4/c5", quota); fail("should not set quota when parent has a quota"); } catch (IllegalArgumentException e) { assertEquals("/c1/c2/c3/c4/c5 has a parent /c1/c2/c3 which has a quota", e.getMessage()); } } @Test public void testSetQuotaWhenExceedBytesSoftQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack st = new StatsTrack(); st.setBytes(5L); SetQuotaCommand.createQuota(zk, path, st); zk.setData(path, "12345".getBytes(), -1); try { zk.setData(path, "123456".getBytes(), -1); validateNoQuotaExceededMetrics(namespace); } catch (Exception e) { fail("should set data which exceeds the soft byte quota"); } } @Test public void testSetQuotaWhenExceedBytesHardQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "12345".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack st = new StatsTrack(); st.setByteHardLimit(5L); SetQuotaCommand.createQuota(zk, path, st); try { zk.setData(path, "123456".getBytes(), -1); fail("should not set data which exceeds the hard byte quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testSetQuotaWhenExceedBytesHardQuotaExtend() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int bytes = 100; StatsTrack st = new StatsTrack(); st.setByteHardLimit(bytes); SetQuotaCommand.createQuota(zk, path, st); StringBuilder sb = new StringBuilder(path); for (int i = 1; i <= bytes; i++) { sb.append("/c" + i); if (i == bytes) { try { zk.create(sb.toString(), "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should not set quota when exceeds hard bytes quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } else { zk.create(sb.toString(), "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } } @Test public void testSetQuotaWhenSetQuotaLessThanExistBytes() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "123456789".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int bytes = 5; StatsTrack st = new StatsTrack(); st.setByteHardLimit(bytes); SetQuotaCommand.createQuota(zk, path, st); try { zk.setData(path, "123456".getBytes(), -1); fail("should not set quota when exceeds hard bytes quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testSetQuotaWhenSetChildDataExceedBytesQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace + "/quota"; zk.create("/" + namespace, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(path, "01234".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(path + "/data", "56789".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack quota = new StatsTrack(); quota.setByteHardLimit(10); SetQuotaCommand.createQuota(zk, path, quota); try { zk.setData(path + "/data", "567891".getBytes(), -1); fail("should not set data when exceed hard byte quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testSetQuotaWhenCreateNodeExceedBytesQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace + "/quota"; zk.create("/" + namespace, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(path, "01234".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack quota = new StatsTrack(); quota.setByteHardLimit(10); SetQuotaCommand.createQuota(zk, path, quota); try { zk.create(path + "/data", "567891".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should not set data when exceed hard byte quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testSetQuotaWhenExceedCountSoftQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 2; StatsTrack st = new StatsTrack(); st.setCount(count); SetQuotaCommand.createQuota(zk, path, st); zk.create(path + "/c2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.create(path + "/c2" + "/c3", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); validateNoQuotaExceededMetrics(namespace); } catch (QuotaExceededException e) { fail("should set quota when exceeds soft count quota"); } } @Test public void testSetQuotaWhenExceedCountHardQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 2; StatsTrack st = new StatsTrack(); st.setCountHardLimit(count); SetQuotaCommand.createQuota(zk, path, st); zk.create(path + "/c2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.create(path + "/c2" + "/c3", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should not set quota when exceeds hard count quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testSetQuotaWhenExceedCountHardQuotaExtend() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 100; StatsTrack st = new StatsTrack(); st.setCountHardLimit(count); SetQuotaCommand.createQuota(zk, path, st); StringBuilder sb = new StringBuilder(path); for (int i = 1; i <= count; i++) { sb.append("/c" + i); if (i == count) { try { zk.create(sb.toString() , "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should not set quota when exceeds hard count quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } else { zk.create(sb.toString(), "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } } @Test public void testSetQuotaWhenSetQuotaLessThanExistCount() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(path + "/c1", "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); zk.create(path + "/c2", "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 2; StatsTrack st = new StatsTrack(); st.setCountHardLimit(count); SetQuotaCommand.createQuota(zk, path, st); try { zk.create(path + "/c3", "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should not set quota when exceeds hard count quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testSetQuotaWhenExceedBothBytesAndCountHardQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "12345".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack st = new StatsTrack(); st.setByteHardLimit(5L); st.setCountHardLimit(1); SetQuotaCommand.createQuota(zk, path, st); try { zk.create(path + "/c2", "1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should give priority to CountQuotaExceededException when both meets the count and bytes quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } } @Test public void testMultiCreateThenSetDataShouldWork() throws Exception { final String path = "/a"; final String subPath = "/a/b"; zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final byte[] data13b = "Hello, World!".getBytes(StandardCharsets.UTF_8); final StatsTrack st = new StatsTrack(); st.setByteHardLimit(data13b.length); SetQuotaCommand.createQuota(zk, path, st); final List ops = Arrays.asList( Op.create(subPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData(subPath, data13b, -1)); zk.multi(ops); } @Test public void testMultiCreateThenSetDataShouldFail() throws Exception { final String path = "/a"; final String subPath = "/a/b"; zk.create(path, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); final byte[] data13b = "Hello, World!".getBytes(StandardCharsets.UTF_8); final StatsTrack st = new StatsTrack(); st.setByteHardLimit(data13b.length - 1); SetQuotaCommand.createQuota(zk, path, st); final List ops = Arrays.asList( Op.create(subPath, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), Op.setData(subPath, data13b, -1)); try { zk.multi(ops); fail("should fail transaction when hard quota is exceeded"); } catch (QuotaExceededException e) { //expected } assertNull(zk.exists(subPath, null)); } @Test public void testDeleteBytesQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "12345".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack st = new StatsTrack(); st.setByteHardLimit(5L); SetQuotaCommand.createQuota(zk, path, st); try { zk.setData(path, "123456".getBytes(), -1); fail("should not set data which exceeds the hard byte quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } //delete the Byte Hard Quota st = new StatsTrack(); st.setByteHardLimit(1); DelQuotaCommand.delQuota(zk, path, st); zk.setData(path, "123456".getBytes(), -1); validateQuotaExceededMetrics(namespace); } @Test public void testDeleteCountQuota() throws Exception { final String namespace = UUID.randomUUID().toString(); final String path = "/" + namespace; zk.create(path, "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); int count = 2; StatsTrack st = new StatsTrack(); st.setCountHardLimit(count); SetQuotaCommand.createQuota(zk, path, st); zk.create(path + "/c2", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); try { zk.create(path + "/c2" + "/c3", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); fail("should not set quota when exceeds hard count quota"); } catch (QuotaExceededException e) { //expected validateQuotaExceededMetrics(namespace); } //delete the Count Hard Quota st = new StatsTrack(); st.setCountHardLimit(1); DelQuotaCommand.delQuota(zk, path, st); zk.create(path + "/c2" + "/c3", "data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); validateQuotaExceededMetrics(namespace); } @Test public void testListQuota() throws Exception { final String path = "/c1"; zk.create(path, "12345".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); StatsTrack st = new StatsTrack(); long bytes = 5L; int count = 10; long byteHardLimit = 6L; int countHardLimit = 12; st.setBytes(bytes); st.setCount(count); st.setByteHardLimit(byteHardLimit); st.setCountHardLimit(countHardLimit); SetQuotaCommand.createQuota(zk, path, st); List statsTracks = ListQuotaCommand.listQuota(zk, path); for (int i = 0; i < statsTracks.size(); i++) { st = statsTracks.get(i); if (i == 0) { assertEquals(count, st.getCount()); assertEquals(countHardLimit, st.getCountHardLimit()); assertEquals(bytes, st.getBytes()); assertEquals(byteHardLimit, st.getByteHardLimit()); } else { assertEquals(1, st.getCount()); assertEquals(-1, st.getCountHardLimit()); assertEquals(5, st.getBytes()); assertEquals(-1, st.getByteHardLimit()); } } //delete the Byte Hard Quota st = new StatsTrack(); st.setByteHardLimit(1); st.setBytes(1); st.setCountHardLimit(1); st.setCount(1); DelQuotaCommand.delQuota(zk, path, st); statsTracks = ListQuotaCommand.listQuota(zk, path); for (int i = 0; i < statsTracks.size(); i++) { st = statsTracks.get(i); if (i == 0) { assertEquals(-1, st.getCount()); assertEquals(-1, st.getCountHardLimit()); assertEquals(-1, st.getBytes()); assertEquals(-1, st.getByteHardLimit()); } else { assertEquals(1, st.getCount()); assertEquals(-1, st.getCountHardLimit()); assertEquals(5, st.getBytes()); assertEquals(-1, st.getByteHardLimit()); } } } private void validateQuotaExceededMetrics(final String namespace) { final String name = QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE; final Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(1, metrics.keySet().stream().filter( key -> key.contains(String.format("%s_%s", namespace, name))).count()); assertEquals(1L, metrics.get(String.format("%s_%s", namespace, name))); } static void validateNoQuotaExceededMetrics(final String namespace) { final Map metrics = MetricsUtils.currentServerMetrics(); assertEquals(0, metrics.keySet().stream().filter( key -> key.contains(String.format("%s_%s", namespace, QuotaMetricsUtils.QUOTA_EXCEEDED_ERROR_PER_NAMESPACE))).count()); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_test_ZooK0100644 0000000 0000000 00000000162 15051152474 032657 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ZooKeeperTestClient.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/test/ZooKeeperTestClient.0100644 0000000 0000000 00000041521 15051152474 034212 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.zookeeper.test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.KeeperException.Code; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.ZKTestCase; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.common.Time; import org.apache.zookeeper.data.Stat; public class ZooKeeperTestClient extends ZKTestCase implements Watcher { protected String hostPort = "127.0.0.1:22801"; protected static final String dirOnZK = "/test_dir"; protected String testDirOnZK = dirOnZK + "/" + Time.currentElapsedTime(); LinkedBlockingQueue events = new LinkedBlockingQueue<>(); private WatchedEvent getEvent(int numTries) throws InterruptedException { WatchedEvent event = null; for (int i = 0; i < numTries; i++) { System.out.println("i = " + i); event = events.poll(10, TimeUnit.SECONDS); if (event != null) { break; } Thread.sleep(5000); } return event; } private void deleteZKDir(ZooKeeper zk, String nodeName) throws IOException, InterruptedException, KeeperException { Stat stat = zk.exists(nodeName, false); if (stat == null) { return; } List children1 = zk.getChildren(nodeName, false); List c2 = zk.getChildren(nodeName, false, stat); if (!children1.equals(c2)) { fail("children lists from getChildren()/getChildren2() do not match"); } if (!stat.equals(stat)) { fail("stats from exists()/getChildren2() do not match"); } if (children1.size() == 0) { zk.delete(nodeName, -1); return; } for (String n : children1) { deleteZKDir(zk, n); } } private void checkRoot() throws IOException, InterruptedException { ZooKeeper zk = new ZooKeeper(hostPort, 10000, this); try { zk.create(dirOnZK, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException ke) { // expected, sort of } catch (KeeperException ke) { fail("Unexpected exception code for create " + dirOnZK + ": " + ke.getMessage()); } try { zk.create(testDirOnZK, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException.NodeExistsException ke) { // expected, sort of } catch (KeeperException ke) { fail("Unexpected exception code for create " + testDirOnZK + ": " + ke.getMessage()); } zk.close(); } private void enode_test_1() throws IOException, InterruptedException, KeeperException { checkRoot(); String parentName = testDirOnZK; String nodeName = parentName + "/enode_abc"; ZooKeeper zk = new ZooKeeper(hostPort, 10000, this); Stat stat = zk.exists(parentName, false); if (stat == null) { try { zk.create(parentName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException ke) { fail("Creating node " + parentName + ke.getMessage()); } } try { zk.create(nodeName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NODEEXISTS; if (!valid) { fail("Unexpected exception code for createin: " + ke.getMessage()); } } stat = zk.exists(nodeName, false); if (stat == null) { fail("node " + nodeName + " should exist"); } System.out.println("Closing client with sessionid: 0x" + Long.toHexString(zk.getSessionId())); zk.close(); zk = new ZooKeeper(hostPort, 10000, this); for (int i = 0; i < 10; i++) { System.out.println("i = " + i); stat = zk.exists(nodeName, false); if (stat != null) { System.out.println("node " + nodeName + " should not exist after reconnection close"); } else { System.out.println("node " + nodeName + " is gone after reconnection close!"); break; } Thread.sleep(5000); } deleteZKDir(zk, nodeName); zk.close(); } private void enode_test_2() throws IOException, InterruptedException, KeeperException { checkRoot(); String parentName = testDirOnZK; String nodeName = parentName + "/enode_abc"; ZooKeeper zk = new ZooKeeper(hostPort, 10000, this); ZooKeeper zk_1 = new ZooKeeper(hostPort, 10000, this); Stat stat_parent = zk_1.exists(parentName, false); if (stat_parent == null) { try { zk.create(parentName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException ke) { fail("Creating node " + parentName + ke.getMessage()); } } Stat stat_node = zk_1.exists(nodeName, false); if (stat_node != null) { try { zk.delete(nodeName, -1); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NONODE || code == KeeperException.Code.NOTEMPTY; if (!valid) { fail("Unexpected exception code for delete: " + ke.getMessage()); } } } List firstGen1 = zk_1.getChildren(parentName, true); Stat stat = new Stat(); List firstGen2 = zk_1.getChildren(parentName, true, stat); if (!firstGen1.equals(firstGen2)) { fail("children lists from getChildren()/getChildren2() do not match"); } if (!stat_parent.equals(stat)) { fail("stat from exists()/getChildren() do not match"); } try { zk.create(nodeName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NODEEXISTS; if (!valid) { fail("Unexpected exception code for createin: " + ke.getMessage()); } } Thread.sleep(5000); WatchedEvent event = events.poll(10, TimeUnit.SECONDS); if (event == null) { throw new IOException("No event was delivered promptly"); } if (event.getType() != EventType.NodeChildrenChanged || !event.getPath().equalsIgnoreCase(parentName)) { fail("Unexpected event was delivered: " + event.toString()); } stat_node = zk_1.exists(nodeName, false); if (stat_node == null) { fail("node " + nodeName + " should exist"); } try { zk.delete(parentName, -1); fail("Should be impossible to delete a non-empty node " + parentName); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NOTEMPTY; if (!valid) { fail("Unexpected exception code for delete: " + code); } } try { zk.create(nodeName + "/def", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); fail("Should be impossible to create child off Ephemeral node " + nodeName); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NOCHILDRENFOREPHEMERALS; if (!valid) { fail("Unexpected exception code for createin: " + code); } } try { List children1 = zk.getChildren(nodeName, false); List children2 = zk.getChildren(nodeName, false, null); if (!children1.equals(children2)) { fail("children lists from getChildren()/getChildren2() does not match"); } if (children1.size() > 0) { fail("ephemeral node " + nodeName + " should not have children"); } } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NONODE; if (!valid) { fail("Unexpected exception code for createin: " + code); } } firstGen1 = zk_1.getChildren(parentName, true); firstGen2 = zk_1.getChildren(parentName, true, null); if (!firstGen1.equals(firstGen2)) { fail("children list from getChildren()/getChildren2() does not match"); } stat_node = zk_1.exists(nodeName, true); if (stat_node == null) { fail("node " + nodeName + " should exist"); } System.out.println("session id of zk: " + zk.getSessionId()); System.out.println("session id of zk_1: " + zk_1.getSessionId()); zk.close(); zk_1.exists("nosuchnode", false); event = this.getEvent(10); if (event == null) { throw new Error("First event was not delivered promptly"); } if (!((event.getType() == EventType.NodeChildrenChanged && event.getPath().equalsIgnoreCase(parentName)) || ( event.getType() == EventType.NodeDeleted && event.getPath().equalsIgnoreCase(nodeName)))) { System.out.print(parentName + " " + EventType.NodeChildrenChanged + " " + nodeName + " " + EventType.NodeDeleted); fail("Unexpected first event was delivered: " + event.toString()); } event = this.getEvent(10); if (event == null) { throw new Error("Second event was not delivered promptly"); } if (!((event.getType() == EventType.NodeChildrenChanged && event.getPath().equalsIgnoreCase(parentName)) || ( event.getType() == EventType.NodeDeleted && event.getPath().equalsIgnoreCase(nodeName)))) { System.out.print(parentName + " " + EventType.NodeChildrenChanged + " " + nodeName + " " + EventType.NodeDeleted); fail("Unexpected second event was delivered: " + event.toString()); } firstGen1 = zk_1.getChildren(parentName, false); stat_node = zk_1.exists(nodeName, false); if (stat_node != null) { fail("node " + nodeName + " should have been deleted"); } if (firstGen1.contains(nodeName)) { fail("node " + nodeName + " should not be a children"); } deleteZKDir(zk_1, nodeName); zk_1.close(); } private void delete_create_get_set_test_1() throws IOException, InterruptedException, KeeperException { checkRoot(); ZooKeeper zk = new ZooKeeper(hostPort, 10000, this); String parentName = testDirOnZK; String nodeName = parentName + "/benwashere"; try { zk.delete(nodeName, -1); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NONODE || code == KeeperException.Code.NOTEMPTY; if (!valid) { fail("Unexpected exception code for delete: " + ke.getMessage()); } } try { zk.create(nodeName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NODEEXISTS; if (!valid) { fail("Unexpected exception code for create: " + ke.getMessage()); } } try { zk.setData(nodeName, "hi".getBytes(), 5700); fail("Should have gotten BadVersion exception"); } catch (KeeperException ke) { if (ke.code() != Code.BADVERSION) { fail("Should have gotten BadVersion exception"); } } zk.setData(nodeName, "hi".getBytes(), -1); Stat st = new Stat(); byte[] bytes = zk.getData(nodeName, false, st); String retrieved = new String(bytes); if (!"hi".equals(retrieved)) { fail("The retrieved data [" + retrieved + "] is differented than the expected [hi]"); } try { zk.delete(nodeName, 6800); fail("Should have gotten BadVersion exception"); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NOTEMPTY || code == KeeperException.Code.BADVERSION; if (!valid) { fail("Unexpected exception code for delete: " + ke.getMessage()); } } try { zk.delete(nodeName, -1); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NOTEMPTY; if (!valid) { fail("Unexpected exception code for delete: " + code); } } deleteZKDir(zk, nodeName); zk.close(); } private void deleteNodeIfExists(ZooKeeper zk, String nodeName) throws InterruptedException { try { zk.delete(nodeName, -1); } catch (KeeperException ke) { Code code = ke.code(); boolean valid = code == KeeperException.Code.NONODE || code == KeeperException.Code.NOTEMPTY; if (!valid) { fail("Unexpected exception code for delete: " + ke.getMessage()); } } } private void create_get_stat_test() throws IOException, InterruptedException, KeeperException { checkRoot(); ZooKeeper zk = new ZooKeeper(hostPort, 10000, this); String parentName = testDirOnZK; String nodeName = parentName + "/create_with_stat_tmp"; deleteNodeIfExists(zk, nodeName); deleteNodeIfExists(zk, nodeName + "_2"); Stat stat = new Stat(); zk.create(nodeName, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat); assertNotNull(stat); assertTrue(stat.getCzxid() > 0); assertTrue(stat.getCtime() > 0); Stat stat2 = new Stat(); zk.create(nodeName + "_2", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, stat2); assertNotNull(stat2); assertTrue(stat2.getCzxid() > stat.getCzxid()); assertTrue(stat2.getCtime() > stat.getCtime()); deleteNodeIfExists(zk, nodeName); deleteNodeIfExists(zk, nodeName + "_2"); zk.close(); } public void my_test_1() throws IOException, InterruptedException, KeeperException { enode_test_1(); enode_test_2(); delete_create_get_set_test_1(); create_get_stat_test(); } public synchronized void process(WatchedEvent event) { try { System.out.println("Got an event " + event.toString()); events.put(event); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { ZooKeeperTestClient zktc = new ZooKeeperTestClient(); try { zktc.my_test_1(); } catch (Exception e) { e.printStackTrace(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/util/PemReaderTest.java0100644 0000000 0000000 00000015173 15051152474 033660 0ustar00rootroot0000000 0000000 /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.List; import java.util.Optional; import org.apache.zookeeper.common.BaseX509ParameterizedTestCase; import org.apache.zookeeper.common.KeyStoreFileType; import org.apache.zookeeper.common.X509KeyType; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; public class PemReaderTest extends BaseX509ParameterizedTestCase { @ParameterizedTest @MethodSource("data") public void testLoadPrivateKeyFromKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); Optional optPassword = x509TestContext.getKeyStorePassword().length() > 0 ? Optional.of(x509TestContext.getKeyStorePassword()) : Optional.empty(); PrivateKey privateKey = PemReader.loadPrivateKey(x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM), optPassword); assertEquals(x509TestContext.getKeyStoreKeyPair().getPrivate(), privateKey); } // Try to load a password-protected private key without providing a password @ParameterizedTest @MethodSource("data") public void testLoadEncryptedPrivateKeyFromKeyStoreWithoutPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(GeneralSecurityException.class, () -> { if (!x509TestContext.isKeyStoreEncrypted()) { throw new GeneralSecurityException(); // this case is not tested so throw the expected exception } PemReader.loadPrivateKey(x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM), Optional.empty()); }); } // Try to load a password-protected private key with the wrong password @ParameterizedTest @MethodSource("data") public void testLoadEncryptedPrivateKeyFromKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(GeneralSecurityException.class, () -> { if (!x509TestContext.isKeyStoreEncrypted()) { throw new GeneralSecurityException(); // this case is not tested so throw the expected exception } PemReader.loadPrivateKey(x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM), Optional.of("wrong password")); }); } // Try to load a non-protected private key while providing a password @ParameterizedTest @MethodSource("data") public void testLoadUnencryptedPrivateKeyFromKeyStoreWithWrongPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(IOException.class, () -> { if (x509TestContext.isKeyStoreEncrypted()) { throw new IOException(); } PemReader.loadPrivateKey(x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM), Optional.of("wrong password")); }); } // Expect this to fail, the trust store does not contain a private key @ParameterizedTest @MethodSource("data") public void testLoadPrivateKeyFromTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(KeyStoreException.class, () -> { PemReader.loadPrivateKey(x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM), Optional.empty()); }); } // Expect this to fail, the trust store does not contain a private key @ParameterizedTest @MethodSource("data") public void testLoadPrivateKeyFromTrustStoreWithPassword( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); assertThrows(KeyStoreException.class, () -> { PemReader.loadPrivateKey(x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM), Optional.of("foobar")); }); } @ParameterizedTest @MethodSource("data") public void testLoadCertificateFromKeyStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); List certs = PemReader.readCertificateChain(x509TestContext.getKeyStoreFile(KeyStoreFileType.PEM)); assertEquals(1, certs.size()); assertEquals(x509TestContext.getKeyStoreCertificate(), certs.get(0)); } @ParameterizedTest @MethodSource("data") public void testLoadCertificateFromTrustStore( X509KeyType caKeyType, X509KeyType certKeyType, String keyPassword, Integer paramIndex) throws Exception { init(caKeyType, certKeyType, keyPassword, paramIndex); List certs = PemReader.readCertificateChain(x509TestContext.getTrustStoreFile(KeyStoreFileType.PEM)); assertEquals(1, certs.size()); assertEquals(x509TestContext.getTrustStoreCertificate(), certs.get(0)); } } ./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_java_org_apache_zookeeper_util_Test0100644 0000000 0000000 00000000170 15051152474 032711 xustar000000000 0000000 120 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/util/TestCircularBlockingQueue.java apache-zookeeper-3.9.4/zookeeper-server/src/test/java/org/apache/zookeeper/util/TestCircularBlocking0100644 0000000 0000000 00000004653 15051152474 034312 0ustar00rootroot0000000 0000000 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at *

    * http://www.apache.org/licenses/LICENSE-2.0 *

    * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS 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 static org.junit.jupiter.api.Assertions.assertEquals; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; public class TestCircularBlockingQueue { @Test public void testCircularBlockingQueue() throws InterruptedException { final CircularBlockingQueue testQueue = new CircularBlockingQueue<>(2); testQueue.offer(1); testQueue.offer(2); testQueue.offer(3); assertEquals(2, testQueue.size()); assertEquals(2, testQueue.take().intValue()); assertEquals(3, testQueue.take().intValue()); assertEquals(1L, testQueue.getDroppedCount()); assertEquals(0, testQueue.size()); assertEquals(true, testQueue.isEmpty()); } @Test @Timeout(value = 10) public void testCircularBlockingQueueTakeBlock() throws InterruptedException, ExecutionException { final CircularBlockingQueue testQueue = new CircularBlockingQueue<>(2); ExecutorService executor = Executors.newSingleThreadExecutor(); try { Future testTake = executor.submit(() -> { return testQueue.take(); }); // Allow the other thread to get into position; waiting for item to be // inserted while (!testQueue.isConsumerThreadBlocked()) { Thread.sleep(50L); } testQueue.offer(10); Integer result = testTake.get(); assertEquals(10, result.intValue()); } finally { executor.shutdown(); } } } apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/check_compatibility.py0100644 0000000 0000000 00000016054 15051152474 030771 0ustar00rootroot0000000 0000000 #!/usr/bin/env python # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # Script which checks Java API compatibility between two revisions of the # Java client. # # Based on the compatibility checker from the HBase project, but ported to # Python for better readability. # Lifted from Kudu: https://github.com/apache/kudu/blob/master/build-support/check_compatibility.py import logging import optparse import os import shutil import subprocess import sys JAVA_ACC_GIT_URL = "https://github.com/lvc/japi-compliance-checker.git" # The annotations for what we consider our public API. PUBLIC_ANNOTATIONS = ["org.apache.yetus.audience.InterfaceAudience.LimitedPrivate", "org.apache.yetus.audience.InterfaceAudience.Public"] # Various relative paths PATH_TO_REPO_DIR = "../../../../" PATH_TO_BUILD_DIR = PATH_TO_REPO_DIR + "build/compat-check" PATH_TO_JACC_DIR = PATH_TO_REPO_DIR + "build/jacc" def check_output(*popenargs, **kwargs): # r"""Run command with arguments and return its output as a byte string. # Backported from Python 2.7 as it's implemented as pure python on stdlib. # >>> check_output(['/usr/bin/python', '--version']) # Python 2.6.2 # """ process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] error = subprocess.CalledProcessError(retcode, cmd) error.output = output raise error return output def get_repo_dir(): """ Return the path to the top of the repo. """ dirname, _ = os.path.split(os.path.abspath(__file__)) return os.path.abspath(os.path.join(dirname, PATH_TO_REPO_DIR)) def get_scratch_dir(): """ Return the path to the scratch dir that we build within. """ dirname, _ = os.path.split(os.path.abspath(__file__)) return os.path.abspath(os.path.join(dirname, PATH_TO_BUILD_DIR)) def get_java_acc_dir(): dirname, _ = os.path.split(os.path.abspath(__file__)) return os.path.abspath(os.path.join(dirname, PATH_TO_JACC_DIR)) def clean_scratch_dir(scratch_dir): """ Clean up and re-create the scratch directory. """ if os.path.exists(scratch_dir): logging.info("Removing scratch dir %s...", scratch_dir) shutil.rmtree(scratch_dir) logging.info("Creating empty scratch dir %s...", scratch_dir) os.makedirs(scratch_dir) def checkout_tree(rev, path): """ Check out the Java source tree for the given revision into the given path. """ logging.info("Checking out %s in %s", rev, path) os.makedirs(path) # Extract java source subprocess.check_call(["bash", '-o', 'pipefail', "-c", ("git archive --format=tar %s | " + "tar -C \"%s\" -xf -") % (rev, path)], cwd=get_repo_dir()) def get_git_hash(revname): """ Convert 'revname' to its SHA-1 hash. """ return check_output(["git", "rev-parse", revname], cwd=get_repo_dir()).strip() def build_tree(path): """ Run the Java build within 'path'. """ logging.info("Building in %s...", path) subprocess.check_call(["ant", "jar"], cwd=path) def checkout_java_acc(force): """ Check out the Java API Compliance Checker. If 'force' is true, will re-download even if the directory exists. """ acc_dir = get_java_acc_dir() if os.path.exists(acc_dir): logging.info("Java JAVA_ACC is already downloaded.") if not force: return logging.info("Forcing re-download.") shutil.rmtree(acc_dir) logging.info("Checking out Java JAVA_ACC...") subprocess.check_call(["git", "clone", "-b", "2.1", "--single-branch", "--depth=1", JAVA_ACC_GIT_URL, acc_dir]) def find_client_jars(path): """ Return a list of jars within 'path' to be checked for compatibility. """ return check_output(["find", path, "-name", "zookeeper*.jar"]).rstrip('\n') def run_java_acc(src_name, src, dst_name, dst): """ Run the compliance checker to compare 'src' and 'dst'. """ src_jar = find_client_jars(src) dst_jar = find_client_jars(dst) logging.info("Will check compatibility between original jars:\n%s\n" + "and new jars:\n%s", src_jar, dst_jar) annotations_path = os.path.join(get_scratch_dir(), "annotations.txt") with file(annotations_path, "w") as f: for ann in PUBLIC_ANNOTATIONS: print >>f, ann java_acc_path = os.path.join(get_java_acc_dir(), "japi-compliance-checker.pl") out_path = os.path.join(get_scratch_dir(), "report.html") subprocess.check_call(["perl", java_acc_path, "-lib", "ZooKeeper", "-v1", src_name, "-v2", dst_name, "-d1", src_jar, "-d2", dst_jar, "-annotations-list", annotations_path, "-report-path", out_path]) def main(argv): logging.basicConfig(level=logging.INFO) parser = optparse.OptionParser( usage="usage: %prog SRC..[DST]") parser.add_option("-f", "--force-download", dest="force_download_deps", help=("Download dependencies (i.e. Java JAVA_ACC) even if they are " + "already present")) opts, args = parser.parse_args() if len(args) != 1: parser.error("no src/dst revision specified") sys.exit(1) src_rev, dst_rev = args[0].split("..", 1) if dst_rev == "": dst_rev = "HEAD" src_rev = get_git_hash(src_rev) dst_rev = get_git_hash(dst_rev) logging.info("Source revision: %s", src_rev) logging.info("Destination revision: %s", dst_rev) # Download deps. checkout_java_acc(opts.force_download_deps) # Set up the build. scratch_dir = get_scratch_dir() clean_scratch_dir(scratch_dir) # Check out the src and dst source trees. src_dir = os.path.join(scratch_dir, "src") dst_dir = os.path.join(scratch_dir, "dst") checkout_tree(src_rev, src_dir) checkout_tree(dst_rev, dst_dir) # Run the build in each. build_tree(src_dir) build_tree(dst_dir) run_java_acc(src_rev, src_dir + "/build", dst_rev, dst_dir + "/build") if __name__ == "__main__": main(sys.argv)apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/checkstyle-noframes-sorted.xsl0100644 0000000 0000000 00000012561 15051152474 032404 0ustar00rootroot0000000 0000000

    CheckStyle Audit

    Designed for use with CheckStyle and Ant.





    Files

    Name Errors

    File

    Error Description Line
    Back to top

    Summary

    Files Errors
    a b apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/checkstyle.xml0100644 0000000 0000000 00000016501 15051152474 027266 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/create/version-2/log.10100644 0000000 0000000 00000144020 15051152474 033413 0ustar00rootroot0000000 0000000 ZKLG…É 6$:piný:pi­Êÿÿÿö'B2(ŒÚ:piný:piÁ /test_nodeˆ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................worldanyoneBb9  :piný:piÕµÿÿÿõB./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_resources_data_buffersize_create_ve0100644 0000000 0000000 00000000160 15051152474 032744 xustar000000000 0000000 112 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/create/version-2/snapshot.0 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/create/version-2/snapshot0100644 0000000 0000000 00000000450 15051152474 034330 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/«+Ò/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/set/version-2/log.10100644 0000000 0000000 00000144020 15051152474 032743 0ustar00rootroot0000000 0000000 ZKLG… S$:ppž®:pp»êÿÿÿö'B>C ïS:ppž®:ppÍ /test_node.worldanyoneBTÿŒ¾:ppž®:ppÜf /test_nodeˆ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................B]y :ppž®:ppãÿÿÿõBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/set/version-2/snapshot.00100644 0000000 0000000 00000000450 15051152474 034016 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/«+Ò/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/version-2/log.10100644 0000000 0000000 00000144020 15051152474 034007 0ustar00rootroot0000000 0000000 ZKLG~(¶$:pÊÃ:pÊ#Ÿÿÿÿö'BG>ÛÚ:pÊÃ:pÊ#× /test_nodeˆ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................worldanyoneB]¿Ø :pÊÃ:pÊ#õÿÿÿõB./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_resources_data_buffersize_snapshot_0100644 0000000 0000000 00000000162 15051152474 033007 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/version-2/snapshot.0 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/version-2/snapsh0100644 0000000 0000000 00000000450 15051152474 034361 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/«+Ò/./PaxHeaders.X/apache-zookeeper-3.9.4_zookeeper-server_src_test_resources_data_buffersize_snapshot_0100644 0000000 0000000 00000000162 15051152474 033007 xustar000000000 0000000 114 path=apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/version-2/snapshot.2 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/buffersize/snapshot/version-2/snapsh0100644 0000000 0000000 00000012465 15051152474 034372 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿ:pÊÃ'worldanyoneÿÿÿÿÿÿÿÿ /test_nodeˆ........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................:pÊ#×:pÊ#× /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/›á¿Z/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/log.10100644 0000000 0000000 00000163740 15051152474 032326 0ustar00rootroot0000000 0000000 ZKLGŠÏ y$ mæu½ mæv†ÿÿÿöN B} ñI mæu½ mæv«/0/0!worldanyoneB± ÀM mæu½ mæv¶/0/0/0/0!worldanyoneBè ŒQ mæu½ mæv¾/0/0/0/0/0/0!worldanyoneBéH ’Q mæu½ mævÀ/0/0/1/0/0/1!worldanyoneBê~ ˜Q mæu½ mævÂ/0/0/2/0/0/2!worldanyoneBë´ žQ mæu½ mævÄ/0/0/3/0/0/3!worldanyoneBí ¥Q mæu½ mævÇ/0/0/4/0/0/4!worldanyoneBîŒ ¬Q mæu½  mævÊ/0/0/5/0/0/5!worldanyoneBï ²Q mæu½  mævÌ/0/0/6/0/0/6!worldanyoneBðø ¸Q mæu½  mævÎ/0/0/7/0/0/7!worldanyoneBò. ¾Q mæu½  mævÐ/0/0/8/0/0/8!worldanyoneBód ÄQ mæu½  mævÒ/0/0/9/0/0/9!worldanyoneB S mæu½  mævÕ/0/0/10/0/0/10!worldanyoneB. #S mæu½ mævÙ/0/0/11/0/0/11!worldanyoneB5 (S mæu½ mævÚ/0/0/12/0/0/12!worldanyoneB< -S mæu½ mævÛ/0/0/13/0/0/13!worldanyoneB{ 3S mæu½ mævÝ/0/0/14/0/0/14!worldanyoneBº 9S mæu½ mævß/0/0/15/0/0/15!worldanyoneBù ?S mæu½ mævá/0/0/16/0/0/16!worldanyoneB8 ES mæu½ mævã/0/0/17/0/0/17!worldanyoneBw KS mæu½ mævå/0/0/18/0/0/18!worldanyoneB¶ QS mæu½ mævç/0/0/19/0/0/19!worldanyoneBÅê !M mæu½ mævë/0/1/0/1!worldanyoneBÿØ ôQ mæu½ mævú/0/1/0/0/1/0!worldanyoneBS ûQ mæu½ mævý/0/1/1/0/1/1!worldanyoneBÌç Q mæu½ mæw/0/1/2/0/1/2!worldanyoneBÎS Q mæu½ mæw/0/1/3/0/1/3!worldanyoneBω Q mæu½ mæw/0/1/4/0/1/4!worldanyoneBп Q mæu½ mæw/0/1/5/0/1/5!worldanyoneBÒ+ Q mæu½ mæw /0/1/6/0/1/6!worldanyoneBÓa #Q mæu½  mæw /0/1/7/0/1/7!worldanyoneBÔÍ *Q mæu½ ! mæw/0/1/8/0/1/8!worldanyoneBÖ 0Q mæu½!" mæw/0/1/9/0/1/9!worldanyoneBï® †S mæu½"# mæw/0/1/10/0/1/10!worldanyoneBñ% S mæu½#$ mæw/0/1/11/0/1/11!worldanyoneBò, ’S mæu½$% mæw/0/1/12/0/1/12!worldanyoneBók ˜S mæu½%& mæw/0/1/13/0/1/13!worldanyoneBôâ ŸS mæu½&' mæw/0/1/14/0/1/14!worldanyoneBõé ¤S mæu½'( mæw/0/1/15/0/1/15!worldanyoneB÷( ªS mæu½() mæw/0/1/16/0/1/16!worldanyoneBøg °S mæu½)* mæw!/0/1/17/0/1/17!worldanyoneBùn µS mæu½*+ mæw"/0/1/18/0/1/18!worldanyoneBúå ¼S mæu½+, mæw%/0/1/19/0/1/19!worldanyoneBªE ŠM mæu½,- mæw'/0/2/0/2!worldanyoneBß PQ mæu½-. mæw)/0/2/0/0/2/0!worldanyoneBàM VQ mæu½./ mæw+/0/2/1/0/2/1!worldanyoneBá¹ ]Q mæu½/0 mæw./0/2/2/0/2/2!worldanyoneBâï cQ mæu½01 mæw0/0/2/3/0/2/3!worldanyoneBä% iQ mæu½12 mæw2/0/2/4/0/2/4!worldanyoneBå‘ pQ mæu½23 mæw5/0/2/5/0/2/5!worldanyoneBæÇ vQ mæu½34 mæw7/0/2/6/0/2/6!worldanyoneBèi ~Q mæu½45 mæw;/0/2/7/0/2/7!worldanyoneBéÕ …Q mæu½56 mæw>/0/2/8/0/2/8!worldanyoneBë ‹Q mæu½67 mæw@/0/2/9/0/2/9!worldanyoneBB àS mæu½78 mæwA/0/2/10/0/2/10!worldanyoneBI åS mæu½89 mæwB/0/2/11/0/2/11!worldanyoneBˆ ëS mæu½9: mæwD/0/2/12/0/2/12!worldanyoneBÇ ñS mæu½:; mæwF/0/2/13/0/2/13!worldanyoneB Î öS mæu½;< mæwG/0/2/14/0/2/14!worldanyoneB üS mæu½<= mæwI/0/2/15/0/2/15!worldanyoneB  S mæu½=> mæwJ/0/2/16/0/2/16!worldanyoneB ‹ S mæu½>? mæwM/0/2/17/0/2/17!worldanyoneBÊ S mæu½?@ mæwO/0/2/18/0/2/18!worldanyoneB S mæu½@A mæwQ/0/2/19/0/2/19!worldanyoneB½M âM mæu½AB mæwS/0/3/0/3!worldanyoneBô «Q mæu½BC mæwX/0/3/0/0/3/0!worldanyoneBõ‹ ²Q mæu½CD mæw[/0/3/1/0/3/1!worldanyoneBöÁ ¸Q mæu½DE mæw]/0/3/2/0/3/2!worldanyoneB÷Á ½Q mæu½EF mæw^/0/3/3/0/3/3!worldanyoneBú; ÉQ mæu½FG mæwf/0/3/4/0/3/4!worldanyoneBûq ÏQ mæu½GH mæwh/0/3/5/0/3/5!worldanyoneBüq ÔQ mæu½HI mæwi/0/3/6/0/3/6!worldanyoneBýq ÙQ mæu½IJ mæwj/0/3/7/0/3/7!worldanyoneBþÝ àQ mæu½JK mæwm/0/3/8/0/3/8!worldanyoneB" æQ mæu½KL mæwo/0/3/9/0/3/9!worldanyoneB7 <S mæu½LM mæwq/0/3/10/0/3/10!worldanyoneB ES mæu½MN mæwv/0/3/11/0/3/11!worldanyoneB• LS mæu½NO mæwy/0/3/12/0/3/12!worldanyoneBÔ RS mæu½OP mæw{/0/3/13/0/3/13!worldanyoneB! XS mæu½PQ mæw}/0/3/14/0/3/14!worldanyoneB" ]S mæu½QR mæw~/0/3/15/0/3/15!worldanyoneB#Y cS mæu½RS mæw€/0/3/16/0/3/16!worldanyoneB$˜ iS mæu½ST mæw‚/0/3/17/0/3/17!worldanyoneB%Ÿ nS mæu½TU mæwƒ/0/3/18/0/3/18!worldanyoneB&Þ tS mæu½UV mæw…/0/3/19/0/3/19!worldanyoneBÑå BM mæu½VW mæw‡/0/4/0/4!worldanyoneB  Q mæu½WX mæw‹/0/4/0/0/4/0!worldanyoneB  Q mæu½XY mæwŒ/0/4/1/0/4/1!worldanyoneB D Q mæu½YZ mæwŽ/0/4/2/0/4/2!worldanyoneB z Q mæu½Z[ mæw/0/4/3/0/4/3!worldanyoneB° !Q mæu½[\ mæw’/0/4/4/0/4/4!worldanyoneBæ 'Q mæu½\] mæw”/0/4/5/0/4/5!worldanyoneB -Q mæu½]^ mæw–/0/4/6/0/4/6!worldanyoneB 2Q mæu½^_ mæw—/0/4/7/0/4/7!worldanyoneBR 8Q mæu½_` mæw™/0/4/8/0/4/8!worldanyoneBR =Q mæu½`a mæwš/0/4/9/0/4/9!worldanyoneB/Ü ’S mæu½ab mæw›/0/4/10/0/4/10!worldanyoneB1à ›S mæu½bc mæw /0/4/11/0/4/11!worldanyoneB3r £S mæu½cd mæw¤/0/4/12/0/4/12!worldanyoneB5Y ¬S mæu½de mæw©/0/4/13/0/4/13!worldanyoneB6˜ ²S mæu½ef mæw«/0/4/14/0/4/14!worldanyoneB7Ÿ ·S mæu½fg mæw¬/0/4/15/0/4/15!worldanyoneB8¦ ¼S mæu½gh mæw­/0/4/16/0/4/16!worldanyoneB9å ÂS mæu½hi mæw¯/0/4/17/0/4/17!worldanyoneB;$ ÈS mæu½ij mæw±/0/4/18/0/4/18!worldanyoneB<› ÏS mæu½jk mæw´/0/4/19/0/4/19!worldanyoneBåµ žM mæu½kl mæw·/0/5/0/5!worldanyoneB eQ mæu½lm mæwº/0/5/0/0/5/0!worldanyoneB ‚ lQ mæu½mn mæw½/0/5/1/0/5/1!worldanyoneB!‚ qQ mæu½no mæw¾/0/5/2/0/5/2!worldanyoneB"‚ vQ mæu½op mæw¿/0/5/3/0/5/3!worldanyoneB#¸ |Q mæu½pq mæwÁ/0/5/4/0/5/4!worldanyoneB%$ ƒQ mæu½qr mæwÄ/0/5/5/0/5/5!worldanyoneB& ŠQ mæu½rs mæwÇ/0/5/6/0/5/6!worldanyoneB'Æ Q mæu½st mæwÉ/0/5/7/0/5/7!worldanyoneB(Æ •Q mæu½tu mæwÊ/0/5/8/0/5/8!worldanyoneB)ü ›Q mæu½uv mæwÌ/0/5/9/0/5/9!worldanyoneBFA ðS mæu½vw mæwÍ/0/5/10/0/5/10!worldanyoneBGH õS mæu½wx mæwÎ/0/5/11/0/5/11!worldanyoneBIg ÿS mæu½xy mæwÔ/0/5/12/0/5/12!worldanyoneBJ¦S mæu½yz mæwÖ/0/5/13/0/5/13!worldanyoneBL S mæu½z{ mæwÙ/0/5/14/0/5/14!worldanyoneBM”S mæu½{| mæwÜ/0/5/15/0/5/15!worldanyoneBN›S mæu½|} mæwÝ/0/5/16/0/5/16!worldanyoneBOÚS mæu½}~ mæwß/0/5/17/0/5/17!worldanyoneBQ$S mæu½~ mæwá/0/5/18/0/5/18!worldanyoneBRX*S mæu½€ mæwã/0/5/19/0/5/19!worldanyoneBùS ùM mæu½€ mæwæ/0/6/0/6!worldanyoneB4T ÁQ mæu½‚ mæwê/0/6/0/0/6/0!worldanyoneB5Š ÇQ mæu½‚ƒ mæwì/0/6/1/0/6/1!worldanyoneB7b ÐQ mæu½ƒ„ mæwñ/0/6/2/0/6/2!worldanyoneB8Î ×Q mæu½„… mæwô/0/6/3/0/6/3!worldanyoneB:Ü áQ mæu½…† mæwú/0/6/4/0/6/4!worldanyoneBË ÙQ mæu½Âà mæxu/0/9/2/0/9/2!worldanyoneB@ ßQ mæu½ÃÄ mæxw/0/9/3/0/9/3!worldanyoneBA7 åQ mæu½ÄÅ mæxy/0/9/4/0/9/4!worldanyoneBB7 êQ mæu½ÅÆ mæxz/0/9/5/0/9/5!worldanyoneBC7 ïQ mæu½ÆÇ mæx{/0/9/6/0/9/6!worldanyoneBD7 ôQ mæu½ÇÈ mæx|/0/9/7/0/9/7!worldanyoneBEm úQ mæu½ÈÉ mæx~/0/9/8/0/9/8!worldanyoneBFm ÿQ mæu½ÉÊ mæx/0/9/9/0/9/9!worldanyoneBcæVS mæu½ÊË mæx‚/0/9/10/0/9/10!worldanyoneBdí[S mæu½ËÌ mæxƒ/0/9/11/0/9/11!worldanyoneBf,aS mæu½ÌÍ mæx…/0/9/12/0/9/12!worldanyoneBg3fS mæu½ÍÎ mæx†/0/9/13/0/9/13!worldanyoneBioS mæu½ÎÏ mæx‹/0/9/14/0/9/14!worldanyoneBjYuS mæu½ÏÐ mæx/0/9/15/0/9/15!worldanyoneBk˜{S mæu½ÐÑ mæx/0/9/16/0/9/16!worldanyoneBl×S mæu½ÑÒ mæx‘/0/9/17/0/9/17!worldanyoneBmÞ†S mæu½ÒÓ mæx’/0/9/18/0/9/18!worldanyoneBnå‹S mæu½ÓÔ mæx“/0/9/19/0/9/19!worldanyoneBÙ „I mæu½ÔÕ mæx”/1/1!worldanyoneBŸ IM mæu½ÕÖ mæx•/1/0/1/0!worldanyoneBM9Q mæu½Ö× mæx–/1/0/0/1/0/0!worldanyoneBN9Q mæu½×Ø mæx—/1/0/1/1/0/1!worldanyoneBO¥Q mæu½ØÙ mæxš/1/0/2/1/0/2!worldanyoneBQ!Q mæu½ÙÚ mæx/1/0/3/1/0/3!worldanyoneBRG'Q mæu½ÚÛ mæxŸ/1/0/4/1/0/4!worldanyoneBS³.Q mæu½ÛÜ mæx¢/1/0/5/1/0/5!worldanyoneBTé4Q mæu½ÜÝ mæx¤/1/0/6/1/0/6!worldanyoneBV:Q mæu½ÝÞ mæx¦/1/0/7/1/0/7!worldanyoneBW‹AQ mæu½Þß mæx©/1/0/8/1/0/8!worldanyoneBX‹FQ mæu½ßà mæxª/1/0/9/1/0/9!worldanyoneBvbœS mæu½àá mæx¬/1/0/10/1/0/10!worldanyoneBwi¡S mæu½áâ mæx­/1/0/11/1/0/11!worldanyoneBxp¦S mæu½âã mæx®/1/0/12/1/0/12!worldanyoneByw«S mæu½ãä mæx¯/1/0/13/1/0/13!worldanyoneBz¶±S mæu½äå mæx±/1/0/14/1/0/14!worldanyoneB{½¶S mæu½åæ mæx²/1/0/15/1/0/15!worldanyoneB}4½S mæu½æç mæxµ/1/0/16/1/0/16!worldanyoneB~;ÂS mæu½çè mæx¶/1/0/17/1/0/17!worldanyoneBzÈS mæu½èé mæx¸/1/0/18/1/0/18!worldanyoneB€¹ÎS mæu½éê mæxº/1/0/19/1/0/19!worldanyoneB#­ œM mæu½êë mæx¼/1/1/1/1!worldanyoneB`ÇbQ mæu½ëì mæx¾/1/1/0/1/1/0!worldanyoneBaÇgQ mæu½ìí mæx¿/1/1/1/1/1/1!worldanyoneBc3nQ mæu½íî mæxÂ/1/1/2/1/1/2!worldanyoneBd3sQ mæu½îï mæxÃ/1/1/3/1/1/3!worldanyoneBeiyQ mæu½ïð mæxÅ/1/1/4/1/1/4!worldanyoneBf3}Q mæu½ðñ mæxÅ/1/1/5/1/1/5!worldanyoneBgiƒQ mæu½ñò mæxÇ/1/1/6/1/1/6!worldanyoneBi­ŽQ mæu½òó mæxÎ/1/1/7/1/1/7!worldanyoneBj­“Q mæu½óô mæxÏ/1/1/8/1/1/8!worldanyoneBkã™Q mæu½ôõ mæxÑ/1/1/9/1/1/9!worldanyoneBŠ_ïS mæu½õö mæxÓ/1/1/10/1/1/10!worldanyoneB‹ÖöS mæu½ö÷ mæxÖ/1/1/11/1/1/11!worldanyoneBüS mæu½÷ø mæxØ/1/1/12/1/1/12!worldanyoneB4S mæu½øù mæxÞ/1/1/13/1/1/13!worldanyoneB; S mæu½ùú mæxß/1/1/14/1/1/14!worldanyoneB‘zS mæu½úû mæxá/1/1/15/1/1/15!worldanyoneB’¹S mæu½ûü mæxã/1/1/16/1/1/16!worldanyoneB“øS mæu½üý mæxå/1/1/17/1/1/17!worldanyoneB”ÿ"S mæu½ýþ mæxæ/1/1/18/1/1/18!worldanyoneB–>(S mæu½þÿ mæxè/1/1/19/1/1/19!worldanyoneBþq ýM mæu½ÿ mæxð/1/2/1/2!worldanyoneBóT ÄQ mæu½ mæxò/1/2/0/1/2/0!worldanyoneBôÀ ËQ mæu½ mæxõ/1/2/1/1/2/1!worldanyoneBõö ÑQ mæu½ mæx÷/1/2/2/1/2/2!worldanyoneBöö ÖQ mæu½ mæxø/1/2/3/1/2/3!worldanyoneBù àQ mæu½ mæxþ/1/2/4/1/2/4!worldanyoneBú åQ mæu½ mæxÿ/1/2/5/1/2/5!worldanyoneBÅ; ëQ mæu½ mæy/1/2/6/1/2/6!worldanyoneBÆ; ðQ mæu½ mæy/1/2/7/1/2/7!worldanyoneBÇq öQ mæu½  mæy/1/2/8/1/2/8!worldanyoneBÈq ûQ mæu½   mæy/1/2/9/1/2/9!worldanyoneBá° QS mæu½   mæy/1/2/10/1/2/10!worldanyoneBâ· VS mæu½   mæy/1/2/11/1/2/11!worldanyoneBã¾ [S mæu½   mæy/1/2/12/1/2/12!worldanyoneBäÅ `S mæu½  mæy /1/2/13/1/2/13!worldanyoneBåÌ eS mæu½ mæy /1/2/14/1/2/14!worldanyoneBæÓ jS mæu½ mæy /1/2/15/1/2/15!worldanyoneBçÚ oS mæu½ mæy /1/2/16/1/2/16!worldanyoneBèá tS mæu½ mæy /1/2/17/1/2/17!worldanyoneBéè yS mæu½ mæy/1/2/18/1/2/18!worldanyoneBë_ €S mæu½ mæy/1/2/19/1/2/19!worldanyoneB›û MM mæu½ mæy/1/3/1/3!worldanyoneBÏŸ Q mæu½ mæy/1/3/0/1/3/0!worldanyoneBП Q mæu½ mæy/1/3/1/1/3/1!worldanyoneBÑÕ Q mæu½ mæy/1/3/2/1/3/2!worldanyoneBÒÕ "Q mæu½ mæy/1/3/3/1/3/3!worldanyoneBÓÕ 'Q mæu½ mæy/1/3/4/1/3/4!worldanyoneBÕA .Q mæu½ mæy/1/3/5/1/3/5!worldanyoneBÖw 4Q mæu½ mæy/1/3/6/1/3/6!worldanyoneB×w 9Q mæu½ mæy/1/3/7/1/3/7!worldanyoneBØw >Q mæu½ mæy/1/3/8/1/3/8!worldanyoneBÙw CQ mæu½ mæy /1/3/9/1/3/9!worldanyoneBó ˜S mæu½  mæy!/1/3/10/1/3/10!worldanyoneBô„ ŸS mæu½ ! mæy$/1/3/11/1/3/11!worldanyoneBõ‹ ¤S mæu½!" mæy%/1/3/12/1/3/12!worldanyoneBö’ ©S mæu½"# mæy&/1/3/13/1/3/13!worldanyoneB÷™ ®S mæu½#$ mæy'/1/3/14/1/3/14!worldanyoneBø  ³S mæu½$% mæy(/1/3/15/1/3/15!worldanyoneBù§ ¸S mæu½%& mæy)/1/3/16/1/3/16!worldanyoneBúæ ¾S mæu½&' mæy+/1/3/17/1/3/17!worldanyoneBûí ÃS mæu½'( mæy,/1/3/18/1/3/18!worldanyoneBýœ ËS mæu½() mæy0/1/3/19/1/3/19!worldanyoneB¬y ˜M mæu½)* mæy1/1/4/1/4!worldanyoneBá} ^Q mæu½*+ mæy3/1/4/0/1/4/0!worldanyoneBãU gQ mæu½+, mæy8/1/4/1/1/4/1!worldanyoneBä‹ mQ mæu½,- mæy:/1/4/2/1/4/2!worldanyoneBå‹ rQ mæu½-. mæy;/1/4/3/1/4/3!worldanyoneBæÁ xQ mæu½./ mæy=/1/4/4/1/4/4!worldanyoneBçÁ }Q mæu½/0 mæy>/1/4/5/1/4/5!worldanyoneBé- „Q mæu½01 mæyA/1/4/6/1/4/6!worldanyoneBê- ‰Q mæu½12 mæyB/1/4/7/1/4/7!worldanyoneBë™ Q mæu½23 mæyE/1/4/8/1/4/8!worldanyoneBìÏ –Q mæu½34 mæyG/1/4/9/1/4/9!worldanyoneB ëS mæu½45 mæyH/1/4/10/1/4/10!worldanyoneB ðS mæu½56 mæyI/1/4/11/1/4/11!worldanyoneB ' õS mæu½67 mæyJ/1/4/12/1/4/12!worldanyoneB ž üS mæu½78 mæyM/1/4/13/1/4/13!worldanyoneB ¥ S mæu½89 mæyN/1/4/14/1/4/14!worldanyoneB ¬ S mæu½9: mæyO/1/4/15/1/4/15!worldanyoneB ë S mæu½:; mæyQ/1/4/16/1/4/16!worldanyoneBò S mæu½;< mæyR/1/4/17/1/4/17!worldanyoneBI S mæu½<= mæyY/1/4/18/1/4/18!worldanyoneBÀ #S mæu½=> mæy\/1/4/19/1/4/19!worldanyoneB¿³ ñM mæu½>? mæy^/1/5/1/5!worldanyoneBõã ¶Q mæu½?@ mæy_/1/5/0/1/5/0!worldanyoneB÷O ½Q mæu½@A mæyb/1/5/1/1/5/1!worldanyoneBøO ÂQ mæu½AB mæyc/1/5/2/1/5/2!worldanyoneBùO ÇQ mæu½BC mæyd/1/5/3/1/5/3!worldanyoneBúO ÌQ mæu½CD mæye/1/5/4/1/5/4!worldanyoneBûO ÑQ mæu½DE mæyf/1/5/5/1/5/5!worldanyoneBüO ÖQ mæu½EF mæyg/1/5/6/1/5/6!worldanyoneBý» ÝQ mæu½FG mæyj/1/5/7/1/5/7!worldanyoneBþ» âQ mæu½GH mæyk/1/5/8/1/5/8!worldanyoneBÿ» çQ mæu½HI mæyl/1/5/9/1/5/9!worldanyoneB¦ <S mæu½IJ mæym/1/5/10/1/5/10!worldanyoneB­ AS mæu½JK mæyn/1/5/11/1/5/11!worldanyoneB´ FS mæu½KL mæyo/1/5/12/1/5/12!worldanyoneB» KS mæu½LM mæyp/1/5/13/1/5/13!worldanyoneB PS mæu½MN mæyq/1/5/14/1/5/14!worldanyoneB 9 WS mæu½NO mæyt/1/5/15/1/5/15!worldanyoneB!x ]S mæu½OP mæyv/1/5/16/1/5/16!worldanyoneB" bS mæu½PQ mæyw/1/5/17/1/5/17!worldanyoneB#† gS mæu½QR mæyx/1/5/18/1/5/18!worldanyoneB$ lS mæu½RS mæyy/1/5/19/1/5/19!worldanyoneBÏ› 9M mæu½ST mæyz/1/6/1/6!worldanyoneB Q mæu½TU mæy€/1/6/0/1/6/0!worldanyoneB  Q mæu½UV mæy/1/6/1/1/6/1!worldanyoneB  Q mæu½VW mæy‚/1/6/2/1/6/2!worldanyoneB  Q mæu½WX mæyƒ/1/6/3/1/6/3!worldanyoneB < Q mæu½XY mæy…/1/6/4/1/6/4!worldanyoneBì %Q mæu½YZ mæyŽ/1/6/5/1/6/5!worldanyoneB" +Q mæu½Z[ mæy/1/6/6/1/6/6!worldanyoneBú 4Q mæu½[\ mæy•/1/6/7/1/6/7!worldanyoneBú 9Q mæu½\] mæy–/1/6/8/1/6/8!worldanyoneBú >Q mæu½]^ mæy—/1/6/9/1/6/9!worldanyoneB/» ”S mæu½^_ mæy™/1/6/10/1/6/10!worldanyoneB0 ™S mæu½_` mæyš/1/6/11/1/6/11!worldanyoneB1É žS mæu½`a mæy›/1/6/12/1/6/12!worldanyoneB2Ð £S mæu½ab mæyœ/1/6/13/1/6/13!worldanyoneB3× ¨S mæu½bc mæy/1/6/14/1/6/14!worldanyoneB5 ®S mæu½cd mæyŸ/1/6/15/1/6/15!worldanyoneB6U ´S mæu½de mæy¡/1/6/16/1/6/16!worldanyoneB7\ ¹S mæu½ef mæy¢/1/6/17/1/6/17!worldanyoneB8› ¿S mæu½fg mæy¤/1/6/18/1/6/18!worldanyoneB9Ú ÅS mæu½gh mæy¦/1/6/19/1/6/19!worldanyoneBâÕ ’M mæu½hi mæy§/1/7/1/7!worldanyoneBÊ XQ mæu½ij mæy©/1/7/0/1/7/0!worldanyoneBÊ ]Q mæu½jk mæyª/1/7/1/1/7/1!worldanyoneBÊ bQ mæu½kl mæy«/1/7/2/1/7/2!worldanyoneBÊ gQ mæu½lm mæy¬/1/7/3/1/7/3!worldanyoneB mQ mæu½mn mæy®/1/7/4/1/7/4!worldanyoneB! rQ mæu½no mæy¯/1/7/5/1/7/5!worldanyoneB"l yQ mæu½op mæy²/1/7/6/1/7/6!worldanyoneB#l ~Q mæu½pq mæy³/1/7/7/1/7/7!worldanyoneB$l ƒQ mæu½qr mæy´/1/7/8/1/7/8!worldanyoneB%¢ ‰Q mæu½rs mæy¶/1/7/9/1/7/9!worldanyoneBAÀ ÞS mæu½st mæy·/1/7/10/1/7/10!worldanyoneBC7 åS mæu½tu mæyº/1/7/11/1/7/11!worldanyoneBDæ íS mæu½uv mæy¾/1/7/12/1/7/12!worldanyoneBF• õS mæu½vw mæyÂ/1/7/13/1/7/13!worldanyoneBI$S mæu½wx mæyÊ/1/7/14/1/7/14!worldanyoneBJcS mæu½xy mæyÌ/1/7/15/1/7/15!worldanyoneBKÚS mæu½yz mæyÏ/1/7/16/1/7/16!worldanyoneBM‰S mæu½z{ mæyÓ/1/7/17/1/7/17!worldanyoneBNÈS mæu½{| mæyÕ/1/7/18/1/7/18!worldanyoneBP¯%S mæu½|} mæyÚ/1/7/19/1/7/19!worldanyoneB÷Ÿ óM mæu½}~ mæyÜ/1/8/1/8!worldanyoneB2 ¹Q mæu½~ mæyÞ/1/8/0/1/8/0!worldanyoneB3L ¿Q mæu½€ mæyà/1/8/1/1/8/1!worldanyoneB4‚ ÅQ mæu½€ mæyâ/1/8/2/1/8/2!worldanyoneB5L ÉQ mæu½‚ mæyâ/1/8/3/1/8/3!worldanyoneB6L ÎQ mæu½‚ƒ mæyã/1/8/4/1/8/4!worldanyoneB7L ÓQ mæu½ƒ„ mæyä/1/8/5/1/8/5!worldanyoneB8L ØQ mæu½„… mæyå/1/8/6/1/8/6!worldanyoneB9L ÝQ mæu½…† mæyæ/1/8/7/1/8/7!worldanyoneB:î åQ mæu½†‡ mæyê/1/8/8/1/8/8!worldanyoneB;î êQ mæu½‡ˆ mæyë/1/8/9/1/8/9!worldanyoneBY=AS mæu½ˆ‰ mæyî/1/8/10/1/8/10!worldanyoneBZDFS mæu½‰Š mæyï/1/8/11/1/8/11!worldanyoneB[KKS mæu½Š‹ mæyð/1/8/12/1/8/12!worldanyoneB\RPS mæu½‹Œ mæyñ/1/8/13/1/8/13!worldanyoneB]‘VS mæu½Œ mæyó/1/8/14/1/8/14!worldanyoneB_]S mæu½Ž mæyö/1/8/15/1/8/15!worldanyoneB`bS mæu½Ž mæy÷/1/8/16/1/8/16!worldanyoneBagS mæu½ mæyø/1/8/17/1/8/17!worldanyoneBblS mæu½‘ mæyù/1/8/18/1/8/18!worldanyoneBc$qS mæu½‘’ mæyú/1/8/19/1/8/19!worldanyoneB, >M mæu½’“ mæyû/1/9/1/9!worldanyoneBCRQ mæu½“” mæyû/1/9/0/1/9/0!worldanyoneBDˆQ mæu½”• mæyý/1/9/1/1/9/1!worldanyoneBEˆ Q mæu½•– mæyþ/1/9/2/1/9/2!worldanyoneBFˆQ mæu½–— mæyÿ/1/9/3/1/9/3!worldanyoneB¿ Q mæu½—˜ mæz/1/9/4/1/9/4!worldanyoneB¿ Q mæu½˜™ mæz/1/9/5/1/9/5!worldanyoneBõ #Q mæu½™š mæz/1/9/6/1/9/6!worldanyoneBõ (Q mæu½š› mæz/1/9/7/1/9/7!worldanyoneBõ -Q mæu½›œ mæz/1/9/8/1/9/8!worldanyoneBõ 2Q mæu½œ mæz/1/9/9/1/9/9!worldanyoneB2› ˆS mæu½ž mæz/1/9/10/1/9/10!worldanyoneB3Ú ŽS mæu½žŸ mæz /1/9/11/1/9/11!worldanyoneB4á “S mæu½Ÿ  mæz /1/9/12/1/9/12!worldanyoneB5è ˜S mæu½ ¡ mæz /1/9/13/1/9/13!worldanyoneB7' žS mæu½¡¢ mæz/1/9/14/1/9/14!worldanyoneB7ö ¢S mæu½¢£ mæz/1/9/15/1/9/15!worldanyoneB8ý §S mæu½£¤ mæz/1/9/16/1/9/16!worldanyoneB:< ­S mæu½¤¥ mæz/1/9/17/1/9/17!worldanyoneB;{ ³S mæu½¥¦ mæz/1/9/18/1/9/18!worldanyoneB<º ¹S mæu½¦§ mæz/1/9/19/1/9/19!worldanyoneB¯, ²I mæu½§¨ mæz/2/2!worldanyoneBäT wM mæu½¨© mæz/2/0/2/0!worldanyoneB} ;Q mæu½©ª mæz/2/0/0/2/0/0!worldanyoneB} @Q mæu½ª« mæz/2/0/1/2/0/1!worldanyoneB} EQ mæu½«¬ mæz/2/0/2/2/0/2!worldanyoneB³ KQ mæu½¬­ mæz/2/0/3/2/0/3!worldanyoneB!U SQ mæu½­® mæz/2/0/4/2/0/4!worldanyoneB"U XQ mæu½®¯ mæz /2/0/5/2/0/5!worldanyoneB#U ]Q mæu½¯° mæz!/2/0/6/2/0/6!worldanyoneB$U bQ mæu½°± mæz"/2/0/7/2/0/7!worldanyoneB*/ ~Q mæu½±² mæz:/2/0/8/2/0/8!worldanyoneB+e „Q mæu½²³ mæz</2/0/9/2/0/9!worldanyoneBG· ÚS mæu½³´ mæz>/2/0/10/2/0/10!worldanyoneBH¾ ßS mæu½´µ mæz?/2/0/11/2/0/11!worldanyoneBIÅ äS mæu½µ¶ mæz@/2/0/12/2/0/12!worldanyoneBJÌ éS mæu½¶· mæzA/2/0/13/2/0/13!worldanyoneBL ïS mæu½·¸ mæzC/2/0/14/2/0/14!worldanyoneBMJ õS mæu½¸¹ mæzE/2/0/15/2/0/15!worldanyoneBO1 þS mæu½¹º mæzJ/2/0/16/2/0/16!worldanyoneBPàS mæu½º» mæzN/2/0/17/2/0/17!worldanyoneBS§S mæu½»¼ mæzW/2/0/18/2/0/18!worldanyoneBTæS mæu½¼½ mæzY/2/0/19/2/0/19!worldanyoneBûÚ æM mæu½½¾ mæzZ/2/1/2/1!worldanyoneB5ó «Q mæu½¾¿ mæz[/2/1/0/2/1/0!worldanyoneB7) ±Q mæu½¿À mæz]/2/1/1/2/1/1!worldanyoneB8) ¶Q mæu½ÀÁ mæz^/2/1/2/2/1/2!worldanyoneB9) »Q mæu½Á mæz_/2/1/3/2/1/3!worldanyoneB:) ÀQ mæu½Âà mæz`/2/1/4/2/1/4!worldanyoneB;_ ÆQ mæu½ÃÄ mæzb/2/1/5/2/1/5!worldanyoneB<_ ËQ mæu½ÄÅ mæzc/2/1/6/2/1/6!worldanyoneB=• ÑQ mæu½ÅÆ mæze/2/1/7/2/1/7!worldanyoneB>• ÖQ mæu½ÆÇ mæzf/2/1/8/2/1/8!worldanyoneB?• ÛQ mæu½ÇÈ mæzg/2/1/9/2/1/9!worldanyoneB\$/S mæu½ÈÉ mæzg/2/1/10/2/1/10!worldanyoneB]+4S mæu½ÉÊ mæzh/2/1/11/2/1/11!worldanyoneB^29S mæu½ÊË mæzi/2/1/12/2/1/12!worldanyoneB_©@S mæu½ËÌ mæzl/2/1/13/2/1/13!worldanyoneB`èFS mæu½ÌÍ mæzn/2/1/14/2/1/14!worldanyoneBb_MS mæu½ÍÎ mæzq/2/1/15/2/1/15!worldanyoneBcfRS mæu½ÎÏ mæzr/2/1/16/2/1/16!worldanyoneBdmWS mæu½ÏÐ mæzs/2/1/17/2/1/17!worldanyoneBet\S mæu½ÐÑ mæzt/2/1/18/2/1/18!worldanyoneBf{aS mæu½ÑÒ mæzu/2/1/19/2/1/19!worldanyoneB Ñ .M mæu½ÒÓ mæzv/2/2/2/2!worldanyoneBFù óQ mæu½ÓÔ mæzw/2/2/0/2/2/0!worldanyoneBH/ ùQ mæu½ÔÕ mæzy/2/2/1/2/2/1!worldanyoneBI/ þQ mæu½ÕÖ mæzz/2/2/2/2/2/2!worldanyoneBJeQ mæu½Ö× mæz|/2/2/3/2/2/3!worldanyoneBKe Q mæu½×Ø mæz}/2/2/4/2/2/4!worldanyoneBL›Q mæu½ØÙ mæz/2/2/5/2/2/5!worldanyoneBM›Q mæu½ÙÚ mæz€/2/2/6/2/2/6!worldanyoneBNÑQ mæu½ÚÛ mæz‚/2/2/7/2/2/7!worldanyoneBOÑQ mæu½ÛÜ mæzƒ/2/2/8/2/2/8!worldanyoneBQ%Q mæu½ÜÝ mæz…/2/2/9/2/2/9!worldanyoneBnÑ|S mæu½ÝÞ mæzˆ/2/2/10/2/2/10!worldanyoneBoØS mæu½Þß mæz‰/2/2/11/2/2/11!worldanyoneBp߆S mæu½ßà mæzŠ/2/2/12/2/2/12!worldanyoneBqæ‹S mæu½àá mæz‹/2/2/13/2/2/13!worldanyoneBs%‘S mæu½áâ mæz/2/2/14/2/2/14!worldanyoneBt,–S mæu½âã mæzŽ/2/2/15/2/2/15!worldanyoneBu3›S mæu½ãä mæz/2/2/16/2/2/16!worldanyoneBv: S mæu½äå mæz/2/2/17/2/2/17!worldanyoneBwA¥S mæu½åæ mæz‘/2/2/18/2/2/18!worldanyoneBx©S mæu½æç mæz‘/2/2/19/2/2/19!worldanyoneBë wM mæu½çè mæz“/2/3/2/3!worldanyoneBX¡>Q mæu½èé mæz–/2/3/0/2/3/0!worldanyoneBZ EQ mæu½éê mæz™/2/3/1/2/3/1!worldanyoneB[ JQ mæu½êë mæzš/2/3/2/2/3/2!worldanyoneB\ OQ mæu½ëì mæz›/2/3/3/2/3/3!worldanyoneB]yVQ mæu½ìí mæzž/2/3/4/2/3/4!worldanyoneB_^Q mæu½íî mæz¢/2/3/5/2/3/5!worldanyoneBa•jQ mæu½îï mæzª/2/3/6/2/3/6!worldanyoneBb•oQ mæu½ïð mæz«/2/3/7/2/3/7!worldanyoneBc•tQ mæu½ðñ mæz¬/2/3/8/2/3/8!worldanyoneBe{Q mæu½ñò mæz¯/2/3/9/2/3/9!worldanyoneB„ÕS mæu½òó mæzµ/2/3/10/2/3/10!worldanyoneB‹EöS mæu½óô mæzÒ/2/3/11/2/3/11!worldanyoneBŽ|S mæu½ôõ mæzÝ/2/3/12/2/3/12!worldanyoneB‘CS mæu½õö mæzæ/2/3/13/2/3/13!worldanyoneB’JS mæu½ö÷ mæzç/2/3/14/2/3/14!worldanyoneB“QS mæu½÷ø mæzè/2/3/15/2/3/15!worldanyoneB”"S mæu½øù mæzê/2/3/16/2/3/16!worldanyoneB•—'S mæu½ùú mæzë/2/3/17/2/3/17!worldanyoneB—F/S mæu½úû mæzï/2/3/18/2/3/18!worldanyoneB˜…5S mæu½ûü mæzñ/2/3/19/2/3/19!worldanyoneB8éM mæu½üý mæzò/2/4/2/4!worldanyoneBw“ÈQ mæu½ýþ mæzô/2/4/0/2/4/0!worldanyoneBx“ÍQ mæu½þÿ mæzõ/2/4/1/2/4/1!worldanyoneB<> ÕQ mæu½ÿ mæzø/2/4/2/2/4/2!worldanyoneB÷¬ ÜQ mæu½ mæzú/2/4/3/2/4/3!worldanyoneBø¬ áQ mæu½ mæzû/2/4/4/2/4/4!worldanyoneBùâ çQ mæu½ mæzý/2/4/5/2/4/5!worldanyoneBû íQ mæu½ mæzÿ/2/4/6/2/4/6!worldanyoneBÆ… ôQ mæu½ mæ{/2/4/7/2/4/7!worldanyoneBÇ» úQ mæu½ mæ{/2/4/8/2/4/8!worldanyoneBÈñ Q mæu½ mæ{/2/4/9/2/4/9!worldanyoneBâo WS mæu½ mæ{/2/4/10/2/4/10!worldanyoneBäV `S mæu½  mæ{ /2/4/11/2/4/11!worldanyoneBå• fS mæu½   mæ{/2/4/12/2/4/12!worldanyoneBæœ kS mæu½   mæ{/2/4/13/2/4/13!worldanyoneBç£ pS mæu½   mæ{/2/4/14/2/4/14!worldanyoneBèª uS mæu½   mæ{/2/4/15/2/4/15!worldanyoneBé± zS mæu½  mæ{/2/4/16/2/4/16!worldanyoneBê€ ~S mæu½ mæ{/2/4/17/2/4/17!worldanyoneBë‡ ƒS mæu½ mæ{/2/4/18/2/4/18!worldanyoneBìŽ ˆS mæu½ mæ{/2/4/19/2/4/19!worldanyoneB5 VM mæu½ mæ{/2/5/2/5!worldanyoneBÑ- Q mæu½ mæ{/2/5/0/2/5/0!worldanyoneBÒ- !Q mæu½ mæ{/2/5/1/2/5/1!worldanyoneBÓ™ (Q mæu½ mæ{/2/5/2/2/5/2!worldanyoneBÔ™ -Q mæu½ mæ{/2/5/3/2/5/3!worldanyoneBÕ™ 2Q mæu½ mæ{/2/5/4/2/5/4!worldanyoneB× 9Q mæu½ mæ{"/2/5/5/2/5/5!worldanyoneBØ; ?Q mæu½ mæ{$/2/5/6/2/5/6!worldanyoneBÙq EQ mæu½ mæ{&/2/5/7/2/5/7!worldanyoneBÚ; IQ mæu½ mæ{&/2/5/8/2/5/8!worldanyoneBÛ; NQ mæu½ mæ{'/2/5/9/2/5/9!worldanyoneBõ ¤S mæu½ mæ{)/2/5/10/2/5/10!worldanyoneBö# ©S mæu½ mæ{*/2/5/11/2/5/11!worldanyoneB÷b ¯S mæu½ mæ{,/2/5/12/2/5/12!worldanyoneBø¡ µS mæu½  mæ{./2/5/13/2/5/13!worldanyoneBùà »S mæu½ ! mæ{0/2/5/14/2/5/14!worldanyoneBû ÁS mæu½!" mæ{2/2/5/15/2/5/15!worldanyoneBü& ÆS mæu½"# mæ{3/2/5/16/2/5/16!worldanyoneBý- ËS mæu½#$ mæ{4/2/5/17/2/5/17!worldanyoneBþ¤ ÒS mæu½$% mæ{7/2/5/18/2/5/18!worldanyoneBÿã ØS mæu½%& mæ{9/2/5/19/2/5/19!worldanyoneB®­ ¦M mæu½&' mæ{;/2/6/2/6!worldanyoneBãã lQ mæu½'( mæ{=/2/6/0/2/6/0!worldanyoneBå rQ mæu½() mæ{?/2/6/1/2/6/1!worldanyoneBæO xQ mæu½)* mæ{A/2/6/2/2/6/2!worldanyoneBç… ~Q mæu½*+ mæ{C/2/6/3/2/6/3!worldanyoneBè… ƒQ mæu½+, mæ{D/2/6/4/2/6/4!worldanyoneBé… ˆQ mæu½,- mæ{E/2/6/5/2/6/5!worldanyoneBê» ŽQ mæu½-. mæ{G/2/6/6/2/6/6!worldanyoneBë» “Q mæu½./ mæ{H/2/6/7/2/6/7!worldanyoneBíÿ žQ mæu½/0 mæ{O/2/6/8/2/6/8!worldanyoneBï5 ¤Q mæu½01 mæ{Q/2/6/9/2/6/9!worldanyoneB Ð úS mæu½12 mæ{S/2/6/10/2/6/10!worldanyoneB × ÿS mæu½23 mæ{T/2/6/11/2/6/11!worldanyoneB † S mæu½34 mæ{X/2/6/12/2/6/12!worldanyoneB S mæu½45 mæ{Y/2/6/13/2/6/13!worldanyoneB” S mæu½56 mæ{Z/2/6/14/2/6/14!worldanyoneB› S mæu½67 mæ{[/2/6/15/2/6/15!worldanyoneBÚ S mæu½78 mæ{]/2/6/16/2/6/16!worldanyoneBá !S mæu½89 mæ{^/2/6/17/2/6/17!worldanyoneBX (S mæu½9: mæ{a/2/6/18/2/6/18!worldanyoneB_ -S mæu½:; mæ{b/2/6/19/2/6/19!worldanyoneBÀí úM mæu½;< mæ{c/2/7/2/7!worldanyoneB÷; ¿Q mæu½<= mæ{d/2/7/0/2/7/0!worldanyoneBø; ÄQ mæu½=> mæ{e/2/7/1/2/7/1!worldanyoneBùq ÊQ mæu½>? mæ{g/2/7/2/2/7/2!worldanyoneBúq ÏQ mæu½?@ mæ{h/2/7/3/2/7/3!worldanyoneBû§ ÕQ mæu½@A mæ{j/2/7/4/2/7/4!worldanyoneBü§ ÚQ mæu½AB mæ{k/2/7/5/2/7/5!worldanyoneBýq ÞQ mæu½BC mæ{k/2/7/6/2/7/6!worldanyoneBþ§ äQ mæu½CD mæ{m/2/7/7/2/7/7!worldanyoneBÿ§ éQ mæu½DE mæ{n/2/7/8/2/7/8!worldanyoneB¶ îQ mæu½EF mæ{o/2/7/9/2/7/9!worldanyoneBÕ DS mæu½FG mæ{q/2/7/10/2/7/10!worldanyoneBÜ IS mæu½GH mæ{r/2/7/11/2/7/11!worldanyoneBã NS mæu½HI mæ{s/2/7/12/2/7/12!worldanyoneBê SS mæu½IJ mæ{t/2/7/13/2/7/13!worldanyoneBñ XS mæu½JK mæ{u/2/7/14/2/7/14!worldanyoneB ø ]S mæu½KL mæ{v/2/7/15/2/7/15!worldanyoneB!ÿ bS mæu½LM mæ{w/2/7/16/2/7/16!worldanyoneB"Î fS mæu½MN mæ{w/2/7/17/2/7/17!worldanyoneB$ lS mæu½NO mæ{y/2/7/18/2/7/18!worldanyoneB% qS mæu½OP mæ{z/2/7/19/2/7/19!worldanyoneBÐ >M mæu½PQ mæ{{/2/8/2/8!worldanyoneB® Q mæu½QR mæ{}/2/8/0/2/8/0!worldanyoneB® Q mæu½RS mæ{~/2/8/1/2/8/1!worldanyoneB ® Q mæu½ST mæ{/2/8/2/2/8/2!worldanyoneB ® Q mæu½TU mæ{€/2/8/3/2/8/3!worldanyoneB ® Q mæu½UV mæ{/2/8/4/2/8/4!worldanyoneB ® Q mæu½VW mæ{‚/2/8/5/2/8/5!worldanyoneB ® "Q mæu½WX mæ{ƒ/2/8/6/2/8/6!worldanyoneBä (Q mæu½XY mæ{…/2/8/7/2/8/7!worldanyoneBä -Q mæu½YZ mæ{†/2/8/8/2/8/8!worldanyoneBä 2Q mæu½Z[ mæ{‡/2/8/9/2/8/9!worldanyoneB,R ‡S mæu½[\ mæ{ˆ/2/8/10/2/8/10!worldanyoneB-Y ŒS mæu½\] mæ{‰/2/8/11/2/8/11!worldanyoneB.` ‘S mæu½]^ mæ{Š/2/8/12/2/8/12!worldanyoneB/Ÿ —S mæu½^_ mæ{Œ/2/8/13/2/8/13!worldanyoneB0¦ œS mæu½_` mæ{/2/8/14/2/8/14!worldanyoneB1å ¢S mæu½`a mæ{/2/8/15/2/8/15!worldanyoneB2´ ¦S mæu½ab mæ{/2/8/16/2/8/16!worldanyoneB3ó ¬S mæu½bc mæ{‘/2/8/17/2/8/17!worldanyoneB4ú ±S mæu½cd mæ{’/2/8/18/2/8/18!worldanyoneB6 ¶S mæu½de mæ{“/2/8/19/2/8/19!worldanyoneBß‘ „M mæu½ef mæ{•/2/9/2/9!worldanyoneB IQ mæu½fg mæ{–/2/9/0/2/9/0!worldanyoneB NQ mæu½gh mæ{—/2/9/1/2/9/1!worldanyoneBH TQ mæu½hi mæ{™/2/9/2/2/9/2!worldanyoneBH YQ mæu½ij mæ{š/2/9/3/2/9/3!worldanyoneB ]Q mæu½jk mæ{š/2/9/4/2/9/4!worldanyoneB bQ mæu½kl mæ{›/2/9/5/2/9/5!worldanyoneBH hQ mæu½lm mæ{/2/9/6/2/9/6!worldanyoneBH mQ mæu½mn mæ{ž/2/9/7/2/9/7!worldanyoneB H rQ mæu½no mæ{Ÿ/2/9/8/2/9/8!worldanyoneB!H wQ mæu½op mæ{ /2/9/9/2/9/9!worldanyoneB=? ÌS mæu½pq mæ{¡/2/9/10/2/9/10!worldanyoneB>F ÑS mæu½qr mæ{¢/2/9/11/2/9/11!worldanyoneB?… ×S mæu½rs mæ{¤/2/9/12/2/9/12!worldanyoneBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/log.2740100644 0000000 0000000 00000262740 15051152474 032502 0ustar00rootroot0000000 0000000 ZKLGDì ðS mæu½st mæ{¹/2/9/13/2/9/13!worldanyoneBF› øS mæu½tu mæ{½/2/9/14/2/9/14!worldanyoneBGÚ þS mæu½uv mæ{¿/2/9/15/2/9/15!worldanyoneBIS mæu½vw mæ{Á/2/9/16/2/9/16!worldanyoneBJ  S mæu½wx mæ{Â/2/9/17/2/9/17!worldanyoneBK_S mæu½xy mæ{Ä/2/9/18/2/9/18!worldanyoneBLfS mæu½yz mæ{Å/2/9/19/2/9/19!worldanyoneB»O I mæu½z{ mæ{Æ/3/3!worldanyoneBñ¯ ÑM mæu½{| mæ{Æ/3/0/3/0!worldanyoneB+t –Q mæu½|} mæ{Ç/3/0/0/3/0/0!worldanyoneB,t ›Q mæu½}~ mæ{È/3/0/1/3/0/1!worldanyoneB-t  Q mæu½~ mæ{É/3/0/2/3/0/2!worldanyoneB.t ¥Q mæu½€ mæ{Ê/3/0/3/3/0/3!worldanyoneB/t ªQ mæu½€ mæ{Ë/3/0/4/3/0/4!worldanyoneB0t ¯Q mæu½‚ mæ{Ì/3/0/5/3/0/5!worldanyoneB1t ´Q mæu½‚ƒ mæ{Í/3/0/6/3/0/6!worldanyoneB2t ¹Q mæu½ƒ„ mæ{Î/3/0/7/3/0/7!worldanyoneB3t ¾Q mæu½„… mæ{Ï/3/0/8/3/0/8!worldanyoneB4t ÃQ mæu½…† mæ{Ð/3/0/9/3/0/9!worldanyoneBQ S mæu½†‡ mæ{Ñ/3/0/10/3/0/10!worldanyoneBRS mæu½‡ˆ mæ{Ò/3/0/11/3/0/11!worldanyoneBS"S mæu½ˆ‰ mæ{Ó/3/0/12/3/0/12!worldanyoneBU+S mæu½‰Š mæ{Ø/3/0/13/3/0/13!worldanyoneBV?1S mæu½Š‹ mæ{Ú/3/0/14/3/0/14!worldanyoneBWF6S mæu½‹Œ mæ{Û/3/0/15/3/0/15!worldanyoneBXM;S mæu½Œ mæ{Ü/3/0/16/3/0/16!worldanyoneBYT@S mæu½Ž mæ{Ý/3/0/17/3/0/17!worldanyoneBZ[ES mæu½Ž mæ{Þ/3/0/18/3/0/18!worldanyoneB[bJS mæu½ mæ{ß/3/0/19/3/0/19!worldanyoneBt M mæu½‘ mæ{á/3/1/3/1!worldanyoneB æQ mæu½“” mæ{ã/3/1/2/3/1/2!worldanyoneB? ëQ mæu½”• mæ{ä/3/1/3/3/1/3!worldanyoneB@ ðQ mæu½•– mæ{å/3/1/4/3/1/4!worldanyoneBAD öQ mæu½–— mæ{ç/3/1/5/3/1/5!worldanyoneBBD ûQ mæu½—˜ mæ{è/3/1/6/3/1/6!worldanyoneBCzQ mæu½˜™ mæ{ê/3/1/7/3/1/7!worldanyoneBDDQ mæu½™š mæ{ê/3/1/8/3/1/8!worldanyoneBED Q mæu½š› mæ{ë/3/1/9/3/1/9!worldanyoneBb `S mæu½›œ mæ{í/3/1/10/3/1/10!worldanyoneBc§eS mæu½œ mæ{î/3/1/11/3/1/11!worldanyoneBd®jS mæu½ž mæ{ï/3/1/12/3/1/12!worldanyoneBeµoS mæu½žŸ mæ{ð/3/1/13/3/1/13!worldanyoneBfôuS mæu½Ÿ  mæ{ò/3/1/14/3/1/14!worldanyoneBgûzS mæu½ ¡ mæ{ó/3/1/15/3/1/15!worldanyoneBi:€S mæu½¡¢ mæ{õ/3/1/16/3/1/16!worldanyoneBj±‡S mæu½¢£ mæ{ø/3/1/17/3/1/17!worldanyoneBk¸ŒS mæu½£¤ mæ{ù/3/1/18/3/1/18!worldanyoneBl¿‘S mæu½¤¥ mæ{ú/3/1/19/3/1/19!worldanyoneB\ `M mæu½¥¦ mæ{ý/3/2/3/2!worldanyoneBMJ%Q mæu½¦§ mæ{þ/3/2/0/3/2/0!worldanyoneBNJ*Q mæu½§¨ mæ{ÿ/3/2/1/3/2/1!worldanyoneB 0Q mæu½¨© mæ|/3/2/2/3/2/2!worldanyoneB 5Q mæu½©ª mæ|/3/2/3/3/2/3!worldanyoneBK 9Q mæu½ª« mæ|/3/2/4/3/2/4!worldanyoneBK >Q mæu½«¬ mæ|/3/2/5/3/2/5!worldanyoneBK CQ mæu½¬­ mæ|/3/2/6/3/2/6!worldanyoneBK HQ mæu½­® mæ|/3/2/7/3/2/7!worldanyoneBí PQ mæu½®¯ mæ|/3/2/8/3/2/8!worldanyoneB!Y WQ mæu½¯° mæ| /3/2/9/3/2/9!worldanyoneB<Þ «S mæu½°± mæ| /3/2/10/3/2/10!worldanyoneB=å °S mæu½±² mæ| /3/2/11/3/2/11!worldanyoneB>ì µS mæu½²³ mæ| /3/2/12/3/2/12!worldanyoneB?ó ºS mæu½³´ mæ|/3/2/13/3/2/13!worldanyoneB@ú ¿S mæu½´µ mæ|/3/2/14/3/2/14!worldanyoneBB9 ÅS mæu½µ¶ mæ|/3/2/15/3/2/15!worldanyoneBC@ ÊS mæu½¶· mæ|/3/2/16/3/2/16!worldanyoneBD ÎS mæu½·¸ mæ|/3/2/17/3/2/17!worldanyoneBE ÓS mæu½¸¹ mæ|/3/2/18/3/2/18!worldanyoneBF ØS mæu½¹º mæ|/3/2/19/3/2/19!worldanyoneBî  ¥M mæu½º» mæ|/3/3/3/3!worldanyoneB'¯ jQ mæu½»¼ mæ|/3/3/0/3/3/0!worldanyoneB(¯ oQ mæu½¼½ mæ|/3/3/1/3/3/1!worldanyoneB)¯ tQ mæu½½¾ mæ|/3/3/2/3/3/2!worldanyoneB*å zQ mæu½¾¿ mæ|/3/3/3/3/3/3!worldanyoneB+å Q mæu½¿À mæ|/3/3/4/3/3/4!worldanyoneB- …Q mæu½ÀÁ mæ|/3/3/5/3/3/5!worldanyoneB.Q ‹Q mæu½Á mæ|/3/3/6/3/3/6!worldanyoneB0• –Q mæu½Âà mæ|&/3/3/7/3/3/7!worldanyoneB1• ›Q mæu½ÃÄ mæ|'/3/3/8/3/3/8!worldanyoneB2•  Q mæu½ÄÅ mæ|(/3/3/9/3/3/9!worldanyoneBOS ÷S mæu½ÅÆ mæ|+/3/3/10/3/3/10!worldanyoneBPZ üS mæu½ÆÇ mæ|,/3/3/11/3/3/11!worldanyoneBQ™S mæu½ÇÈ mæ|./3/3/12/3/3/12!worldanyoneBR S mæu½ÈÉ mæ|//3/3/13/3/3/13!worldanyoneBSß S mæu½ÉÊ mæ|1/3/3/14/3/3/14!worldanyoneBUS mæu½ÊË mæ|3/3/3/15/3/3/15!worldanyoneBV%S mæu½ËÌ mæ|4/3/3/16/3/3/16!worldanyoneBW,S mæu½ÌÍ mæ|5/3/3/17/3/3/17!worldanyoneBX3"S mæu½ÍÎ mæ|6/3/3/18/3/3/18!worldanyoneBY&S mæu½ÎÏ mæ|6/3/3/19/3/3/19!worldanyoneBÿæ ôM mæu½ÏÐ mæ|8/3/4/3/4!worldanyoneB:e ºQ mæu½ÐÑ mæ|:/3/4/0/3/4/0!worldanyoneB;e ¿Q mæu½ÑÒ mæ|;/3/4/1/3/4/1!worldanyoneB/3/4/3/3/4/3!worldanyoneB?= ÒQ mæu½ÔÕ mæ|B/3/4/4/3/4/4!worldanyoneB@s ØQ mæu½ÕÖ mæ|D/3/4/5/3/4/5!worldanyoneBAs ÝQ mæu½Ö× mæ|E/3/4/6/3/4/6!worldanyoneBBs âQ mæu½×Ø mæ|F/3/4/7/3/4/7!worldanyoneBCs çQ mæu½ØÙ mæ|G/3/4/8/3/4/8!worldanyoneBDs ìQ mæu½ÙÚ mæ|H/3/4/9/3/4/9!worldanyoneBaXAS mæu½ÚÛ mæ|I/3/4/10/3/4/10!worldanyoneBb_FS mæu½ÛÜ mæ|J/3/4/11/3/4/11!worldanyoneBcfKS mæu½ÜÝ mæ|K/3/4/12/3/4/12!worldanyoneBdÝRS mæu½ÝÞ mæ|N/3/4/13/3/4/13!worldanyoneBeäWS mæu½Þß mæ|O/3/4/14/3/4/14!worldanyoneBfë\S mæu½ßà mæ|P/3/4/15/3/4/15!worldanyoneBgòaS mæu½àá mæ|Q/3/4/16/3/4/16!worldanyoneBhùfS mæu½áâ mæ|R/3/4/17/3/4/17!worldanyoneBjkS mæu½âã mæ|S/3/4/18/3/4/18!worldanyoneBk?qS mæu½ãä mæ|U/3/4/19/3/4/19!worldanyoneBA >M mæu½äå mæ|V/3/5/3/5!worldanyoneBK×Q mæu½åæ mæ|X/3/5/0/3/5/0!worldanyoneBM  Q mæu½æç mæ|Z/3/5/1/3/5/1!worldanyoneBN Q mæu½çè mæ|[/3/5/2/3/5/2!worldanyoneBOCQ mæu½èé mæ|]/3/5/3/3/5/3!worldanyoneBPåQ mæu½éê mæ|a/3/5/4/3/5/4!worldanyoneBR½&Q mæu½êë mæ|f/3/5/5/3/5/5!worldanyoneBS½+Q mæu½ëì mæ|g/3/5/6/3/5/6!worldanyoneBV6Q mæu½ìí mæ|n/3/5/7/3/5/7!worldanyoneBWÙ?Q mæu½íî mæ|s/3/5/8/3/5/8!worldanyoneBYEFQ mæu½îï mæ|v/3/5/9/3/5/9!worldanyoneBwMS mæu½ïð mæ|y/3/5/10/3/5/10!worldanyoneBxT¢S mæu½ðñ mæ|z/3/5/11/3/5/11!worldanyoneBz;«S mæu½ñò mæ|/3/5/12/3/5/12!worldanyoneB{z±S mæu½òó mæ|/3/5/13/3/5/13!worldanyoneB|¶S mæu½óô mæ|‚/3/5/14/3/5/14!worldanyoneB}ˆ»S mæu½ôõ mæ|ƒ/3/5/15/3/5/15!worldanyoneB~ÀS mæu½õö mæ|„/3/5/16/3/5/16!worldanyoneB–ÅS mæu½ö÷ mæ|…/3/5/17/3/5/17!worldanyoneB€ÕËS mæu½÷ø mæ|‡/3/5/18/3/5/18!worldanyoneB‚ÑS mæu½øù mæ|‰/3/5/19/3/5/19!worldanyoneB$Ù žM mæu½ùú mæ|Š/3/6/3/6!worldanyoneBb#eQ mæu½úû mæ|/3/6/0/3/6/0!worldanyoneBcYkQ mæu½ûü mæ|/3/6/1/3/6/1!worldanyoneBdÅrQ mæu½üý mæ|’/3/6/2/3/6/2!worldanyoneBf1yQ mæu½ýþ mæ|•/3/6/3/3/6/3!worldanyoneBg1~Q mæu½þÿ mæ|–/3/6/4/3/6/4!worldanyoneB*: ƒQ mæu½ÿ mæ|–/3/6/5/3/6/5!worldanyoneBåÞ ‹Q mæu½ mæ|™/3/6/6/3/6/6!worldanyoneBæÞ Q mæu½ mæ|š/3/6/7/3/6/7!worldanyoneBè –Q mæu½ mæ|œ/3/6/8/3/6/8!worldanyoneBé ›Q mæu½ mæ|/3/6/9/3/6/9!worldanyoneBœ ñS mæu½ mæ|Ÿ/3/6/10/3/6/10!worldanyoneBÛ ÷S mæu½ mæ|¡/3/6/11/3/6/11!worldanyoneBª ûS mæu½ mæ|¡/3/6/12/3/6/12!worldanyoneBé S mæu½ mæ|£/3/6/13/3/6/13!worldanyoneBð S mæu½  mæ|¤/3/6/14/3/6/14!worldanyoneB / S mæu½   mæ|¦/3/6/15/3/6/15!worldanyoneB n S mæu½   mæ|¨/3/6/16/3/6/16!worldanyoneB å S mæu½   mæ|«/3/6/17/3/6/17!worldanyoneB ì S mæu½   mæ|¬/3/6/18/3/6/18!worldanyoneB ó #S mæu½  mæ|­/3/6/19/3/6/19!worldanyoneBºò ñM mæu½ mæ|¯/3/7/3/7!worldanyoneBñP ·Q mæu½ mæ|±/3/7/0/3/7/0!worldanyoneBò¼ ¾Q mæu½ mæ|´/3/7/1/3/7/1!worldanyoneBó¼ ÃQ mæu½ mæ|µ/3/7/2/3/7/2!worldanyoneBô¼ ÈQ mæu½ mæ|¶/3/7/3/3/7/3!worldanyoneBõ¼ ÍQ mæu½ mæ|·/3/7/4/3/7/4!worldanyoneB÷( ÔQ mæu½ mæ|º/3/7/5/3/7/5!worldanyoneBø” ÛQ mæu½ mæ|½/3/7/6/3/7/6!worldanyoneBù” àQ mæu½ mæ|¾/3/7/7/3/7/7!worldanyoneBû çQ mæu½ mæ|Á/3/7/8/3/7/8!worldanyoneBü ìQ mæu½ mæ|Â/3/7/9/3/7/9!worldanyoneB) BS mæu½ mæ|Ä/3/7/10/3/7/10!worldanyoneB  IS mæu½ mæ|Ç/3/7/11/3/7/11!worldanyoneB§ NS mæu½ mæ|È/3/7/12/3/7/12!worldanyoneB US mæu½ mæ|Ë/3/7/13/3/7/13!worldanyoneB% ZS mæu½ mæ|Ì/3/7/14/3/7/14!worldanyoneB, _S mæu½ mæ|Í/3/7/15/3/7/15!worldanyoneB3 dS mæu½  mæ|Î/3/7/16/3/7/16!worldanyoneB R nS mæu½ ! mæ|Ô/3/7/17/3/7/17!worldanyoneB!Y sS mæu½!" mæ|Õ/3/7/18/3/7/18!worldanyoneB"( wS mæu½"# mæ|Õ/3/7/19/3/7/19!worldanyoneBÍ DM mæu½#$ mæ|Ö/3/8/3/8!worldanyoneBí Q mæu½$% mæ|Ù/3/8/0/3/8/0!worldanyoneBí Q mæu½%& mæ|Ú/3/8/1/3/8/1!worldanyoneBí Q mæu½&' mæ|Û/3/8/2/3/8/2!worldanyoneBí Q mæu½'( mæ|Ü/3/8/3/3/8/3!worldanyoneBí Q mæu½() mæ|Ý/3/8/4/3/8/4!worldanyoneB í $Q mæu½)* mæ|Þ/3/8/5/3/8/5!worldanyoneB í )Q mæu½*+ mæ|ß/3/8/6/3/8/6!worldanyoneB í .Q mæu½+, mæ|à/3/8/7/3/8/7!worldanyoneB û 8Q mæu½,- mæ|æ/3/8/8/3/8/8!worldanyoneBû =Q mæu½-. mæ|ç/3/8/9/3/8/9!worldanyoneB*~ ’S mæu½./ mæ|è/3/8/10/3/8/10!worldanyoneB+… —S mæu½/0 mæ|é/3/8/11/3/8/11!worldanyoneB,Œ œS mæu½01 mæ|ê/3/8/12/3/8/12!worldanyoneB-Ë ¢S mæu½12 mæ|ì/3/8/13/3/8/13!worldanyoneB.Ò §S mæu½23 mæ|í/3/8/14/3/8/14!worldanyoneB/Ù ¬S mæu½34 mæ|î/3/8/15/3/8/15!worldanyoneB0à ±S mæu½45 mæ|ï/3/8/16/3/8/16!worldanyoneB1ç ¶S mæu½56 mæ|ð/3/8/17/3/8/17!worldanyoneB2î »S mæu½67 mæ|ñ/3/8/18/3/8/18!worldanyoneB3õ ÀS mæu½78 mæ|ò/3/8/19/3/8/19!worldanyoneBÜè ŒM mæu½89 mæ|ò/3/9/3/9!worldanyoneB‡ QQ mæu½9: mæ|ó/3/9/0/3/9/0!worldanyoneB‡ VQ mæu½:; mæ|ô/3/9/1/3/9/1!worldanyoneB½ \Q mæu½;< mæ|ö/3/9/2/3/9/2!worldanyoneB½ aQ mæu½<= mæ|÷/3/9/3/3/9/3!worldanyoneB½ fQ mæu½=> mæ|ø/3/9/4/3/9/4!worldanyoneB½ kQ mæu½>? mæ|ù/3/9/5/3/9/5!worldanyoneBó qQ mæu½?@ mæ|û/3/9/6/3/9/6!worldanyoneBó vQ mæu½@A mæ|ü/3/9/7/3/9/7!worldanyoneBó {Q mæu½AB mæ|ý/3/9/8/3/9/8!worldanyoneBé‡ ƒQ mæu½BC mæ}/3/9/9/3/9/9!worldanyoneBÜ ÙS mæu½CD mæ}/3/9/10/3/9/10!worldanyoneB ßS mæu½DE mæ}/3/9/11/3/9/11!worldanyoneB" äS mæu½EF mæ}/3/9/12/3/9/12!worldanyoneB) éS mæu½FG mæ}/3/9/13/3/9/13!worldanyoneBh ïS mæu½GH mæ}/3/9/14/3/9/14!worldanyoneB o ôS mæu½HI mæ} /3/9/15/3/9/15!worldanyoneB v ùS mæu½IJ mæ} /3/9/16/3/9/16!worldanyoneB } þS mæu½JK mæ} /3/9/17/3/9/17!worldanyoneB , S mæu½KL mæ}/3/9/18/3/9/18!worldanyoneB3 S mæu½LM mæ}/3/9/19/3/9/19!worldanyoneB‡{ I mæu½MN mæ}/4/4!worldanyoneB¹ç ÉM mæu½NO mæ}/4/0/4/0!worldanyoneBï± Q mæu½OP mæ}/4/0/0/4/0/0!worldanyoneBð± ”Q mæu½PQ mæ}/4/0/1/4/0/1!worldanyoneBñ{ ˜Q mæu½QR mæ}/4/0/2/4/0/2!worldanyoneBò± žQ mæu½RS mæ}/4/0/3/4/0/3!worldanyoneBó± £Q mæu½ST mæ}/4/0/4/4/0/4!worldanyoneBô± ¨Q mæu½TU mæ}/4/0/5/4/0/5!worldanyoneBõ± ­Q mæu½UV mæ}/4/0/6/4/0/6!worldanyoneBö± ²Q mæu½VW mæ}/4/0/7/4/0/7!worldanyoneB÷{ ¶Q mæu½WX mæ}/4/0/8/4/0/8!worldanyoneBø{ »Q mæu½XY mæ}/4/0/9/4/0/9!worldanyoneBH S mæu½YZ mæ}/4/0/10/4/0/10!worldanyoneBO S mæu½Z[ mæ}/4/0/11/4/0/11!worldanyoneBV S mæu½[\ mæ} /4/0/12/4/0/12!worldanyoneB% S mæu½\] mæ} /4/0/13/4/0/13!worldanyoneBd %S mæu½]^ mæ}"/4/0/14/4/0/14!worldanyoneB3 )S mæu½^_ mæ}"/4/0/15/4/0/15!worldanyoneB: .S mæu½_` mæ}#/4/0/16/4/0/16!worldanyoneBA 3S mæu½`a mæ}$/4/0/17/4/0/17!worldanyoneBH 8S mæu½ab mæ}%/4/0/18/4/0/18!worldanyoneBO =S mæu½bc mæ}&/4/0/19/4/0/19!worldanyoneBÈÕ M mæu½cd mæ})/4/1/4/1!worldanyoneBÿs ÑQ mæu½de mæ}*/4/1/0/4/1/0!worldanyoneB‚ ÖQ mæu½ef mæ}+/4/1/1/4/1/1!worldanyoneB‚ ÛQ mæu½fg mæ},/4/1/2/4/1/2!worldanyoneB‚ àQ mæu½gh mæ}-/4/1/3/4/1/3!worldanyoneBî çQ mæu½hi mæ}0/4/1/4/4/1/4!worldanyoneBî ìQ mæu½ij mæ}1/4/1/5/4/1/5!worldanyoneBî ñQ mæu½jk mæ}2/4/1/6/4/1/6!worldanyoneBZ øQ mæu½kl mæ}5/4/1/7/4/1/7!worldanyoneBZ ýQ mæu½lm mæ}6/4/1/8/4/1/8!worldanyoneB Z Q mæu½mn mæ}7/4/1/9/4/1/9!worldanyoneB$m WS mæu½no mæ}8/4/1/10/4/1/10!worldanyoneB%t \S mæu½op mæ}9/4/1/11/4/1/11!worldanyoneB&{ aS mæu½pq mæ}:/4/1/12/4/1/12!worldanyoneB'ò hS mæu½qr mæ}=/4/1/13/4/1/13!worldanyoneB)1 nS mæu½rs mæ}?/4/1/14/4/1/14!worldanyoneB* rS mæu½st mæ}?/4/1/15/4/1/15!worldanyoneB+? xS mæu½tu mæ}A/4/1/16/4/1/16!worldanyoneB,~ ~S mæu½uv mæ}C/4/1/17/4/1/17!worldanyoneB-½ „S mæu½vw mæ}E/4/1/18/4/1/18!worldanyoneB/l ŒS mæu½wx mæ}I/4/1/19/4/1/19!worldanyoneBÙ· YM mæu½xy mæ}J/4/2/4/2!worldanyoneB– Q mæu½yz mæ}K/4/2/0/4/2/0!worldanyoneB– #Q mæu½z{ mæ}L/4/2/1/4/2/1!worldanyoneB– (Q mæu½{| mæ}M/4/2/2/4/2/2!worldanyoneB` ,Q mæu½|} mæ}M/4/2/3/4/2/3!worldanyoneB` 1Q mæu½}~ mæ}N/4/2/4/4/2/4!worldanyoneBÌ 8Q mæu½~ mæ}Q/4/2/5/4/2/5!worldanyoneBÌ =Q mæu½€ mæ}R/4/2/6/4/2/6!worldanyoneB– AQ mæu½€ mæ}R/4/2/7/4/2/7!worldanyoneB– FQ mæu½‚ mæ}S/4/2/8/4/2/8!worldanyoneB– KQ mæu½‚ƒ mæ}T/4/2/9/4/2/9!worldanyoneB6:  S mæu½ƒ„ mæ}U/4/2/10/4/2/10!worldanyoneB7A ¥S mæu½„… mæ}V/4/2/11/4/2/11!worldanyoneB8H ªS mæu½…† mæ}W/4/2/12/4/2/12!worldanyoneB9 ®S mæu½†‡ mæ}W/4/2/13/4/2/13!worldanyoneB: ³S mæu½‡ˆ mæ}X/4/2/14/4/2/14!worldanyoneB;] ¹S mæu½ˆ‰ mæ}Z/4/2/15/4/2/15!worldanyoneBr ÈS mæu½‹Œ mæ}]/4/2/18/4/2/18!worldanyoneB?y ÍS mæu½Œ mæ}^/4/2/19/4/2/19!worldanyoneBè ™M mæu½Ž mæ}^/4/3/4/3!worldanyoneB ì ^Q mæu½Ž mæ}_/4/3/0/4/3/0!worldanyoneB!ì cQ mæu½ mæ}`/4/3/1/4/3/1!worldanyoneB"ì hQ mæu½‘ mæ}a/4/3/2/4/3/2!worldanyoneB#ì mQ mæu½‘’ mæ}b/4/3/3/4/3/3!worldanyoneB%" sQ mæu½’“ mæ}d/4/3/4/4/3/4!worldanyoneB&" xQ mæu½“” mæ}e/4/3/5/4/3/5!worldanyoneB&ì |Q mæu½”• mæ}e/4/3/6/4/3/6!worldanyoneB'ì Q mæu½•– mæ}f/4/3/7/4/3/7!worldanyoneB(ì †Q mæu½–— mæ}g/4/3/8/4/3/8!worldanyoneB)ì ‹Q mæu½—˜ mæ}h/4/3/9/4/3/9!worldanyoneBF àS mæu½˜™ mæ}i/4/3/10/4/3/10!worldanyoneBG åS mæu½™š mæ}j/4/3/11/4/3/11!worldanyoneBH êS mæu½š› mæ}k/4/3/12/4/3/12!worldanyoneBI$ ïS mæu½›œ mæ}l/4/3/13/4/3/13!worldanyoneBJ+ ôS mæu½œ mæ}m/4/3/14/4/3/14!worldanyoneBK2 ùS mæu½ž mæ}n/4/3/15/4/3/15!worldanyoneBL9 þS mæu½žŸ mæ}o/4/3/16/4/3/16!worldanyoneBM@S mæu½Ÿ  mæ}p/4/3/17/4/3/17!worldanyoneBNGS mæu½ ¡ mæ}q/4/3/18/4/3/18!worldanyoneBON S mæu½¡¢ mæ}r/4/3/19/4/3/19!worldanyoneBöË ÛM mæu½¢£ mæ}t/4/4/4/4!worldanyoneB1P £Q mæu½£¤ mæ}x/4/4/0/4/4/0!worldanyoneB2P ¨Q mæu½¤¥ mæ}y/4/4/1/4/4/1!worldanyoneB3P ­Q mæu½¥¦ mæ}z/4/4/2/4/4/2!worldanyoneB4P ²Q mæu½¦§ mæ}{/4/4/3/4/4/3!worldanyoneB5P ·Q mæu½§¨ mæ}|/4/4/4/4/4/4!worldanyoneB6 »Q mæu½¨© mæ}|/4/4/5/4/4/5!worldanyoneB7¼ ÃQ mæu½©ª mæ}€/4/4/6/4/4/6!worldanyoneB8¼ ÈQ mæu½ª« mæ}/4/4/7/4/4/7!worldanyoneB9¼ ÍQ mæu½«¬ mæ}‚/4/4/8/4/4/8!worldanyoneB:¼ ÒQ mæu½¬­ mæ}ƒ/4/4/9/4/4/9!worldanyoneBWl'S mæu½­® mæ}„/4/4/10/4/4/10!worldanyoneBXs,S mæu½®¯ mæ}…/4/4/11/4/4/11!worldanyoneBYz1S mæu½¯° mæ}†/4/4/12/4/4/12!worldanyoneBZ6S mæu½°± mæ}‡/4/4/13/4/4/13!worldanyoneB[ˆ;S mæu½±² mæ}ˆ/4/4/14/4/4/14!worldanyoneB\@S mæu½²³ mæ}‰/4/4/15/4/4/15!worldanyoneB]–ES mæu½³´ mæ}Š/4/4/16/4/4/16!worldanyoneB_ LS mæu½´µ mæ}/4/4/17/4/4/17!worldanyoneB`QS mæu½µ¶ mæ}Ž/4/4/18/4/4/18!worldanyoneBaVS mæu½¶· mæ}/4/4/19/4/4/19!worldanyoneB #M mæu½·¸ mæ}/4/5/4/5!worldanyoneBA´ èQ mæu½¸¹ mæ}‘/4/5/0/4/5/0!worldanyoneBB´ íQ mæu½¹º mæ}’/4/5/1/4/5/1!worldanyoneBC´ òQ mæu½º» mæ}“/4/5/2/4/5/2!worldanyoneBD~ öQ mæu½»¼ mæ}“/4/5/3/4/5/3!worldanyoneBE´ üQ mæu½¼½ mæ}•/4/5/4/4/5/4!worldanyoneBF~Q mæu½½¾ mæ}•/4/5/5/4/5/5!worldanyoneBG~Q mæu½¾¿ mæ}–/4/5/6/4/5/6!worldanyoneBH~ Q mæu½¿À mæ}—/4/5/7/4/5/7!worldanyoneBI´Q mæu½ÀÁ mæ}™/4/5/8/4/5/8!worldanyoneBJ´Q mæu½Á mæ}š/4/5/9/4/5/9!worldanyoneBg±iS mæu½Âà mæ}š/4/5/10/4/5/10!worldanyoneBh¸nS mæu½ÃÄ mæ}›/4/5/11/4/5/11!worldanyoneBi¿sS mæu½ÄÅ mæ}œ/4/5/12/4/5/12!worldanyoneBjÆxS mæu½ÅÆ mæ}/4/5/13/4/5/13!worldanyoneBkÍ}S mæu½ÆÇ mæ}ž/4/5/14/4/5/14!worldanyoneBmD„S mæu½ÇÈ mæ}¡/4/5/15/4/5/15!worldanyoneBnK‰S mæu½ÈÉ mæ}¢/4/5/16/4/5/16!worldanyoneBoRŽS mæu½ÉÊ mæ}£/4/5/17/4/5/17!worldanyoneBpY“S mæu½ÊË mæ}¤/4/5/18/4/5/18!worldanyoneBq`˜S mæu½ËÌ mæ}¥/4/5/19/4/5/19!worldanyoneBL dM mæu½ÌÍ mæ}¥/4/6/4/6!worldanyoneBQâ,Q mæu½ÍÎ mæ}©/4/6/0/4/6/0!worldanyoneBR¬0Q mæu½ÎÏ mæ}©/4/6/1/4/6/1!worldanyoneBS¬5Q mæu½ÏÐ mæ}ª/4/6/2/4/6/2!worldanyoneBT¬:Q mæu½ÐÑ mæ}«/4/6/3/4/6/3!worldanyoneBU¬?Q mæu½ÑÒ mæ}¬/4/6/4/4/6/4!worldanyoneBV¬DQ mæu½ÒÓ mæ}­/4/6/5/4/6/5!worldanyoneBW¬IQ mæu½ÓÔ mæ}®/4/6/6/4/6/6!worldanyoneBX¬NQ mæu½ÔÕ mæ}¯/4/6/7/4/6/7!worldanyoneBYvRQ mæu½ÕÖ mæ}¯/4/6/8/4/6/8!worldanyoneBZ¬XQ mæu½Ö× mæ}±/4/6/9/4/6/9!worldanyoneBxž®S mæu½×Ø mæ}³/4/6/10/4/6/10!worldanyoneBy¥³S mæu½ØÙ mæ}´/4/6/11/4/6/11!worldanyoneBz¬¸S mæu½ÙÚ mæ}µ/4/6/12/4/6/12!worldanyoneB{³½S mæu½ÚÛ mæ}¶/4/6/13/4/6/13!worldanyoneB|‚ÁS mæu½ÛÜ mæ}¶/4/6/14/4/6/14!worldanyoneB}‰ÆS mæu½ÜÝ mæ}·/4/6/15/4/6/15!worldanyoneB~ËS mæu½ÝÞ mæ}¸/4/6/16/4/6/16!worldanyoneB—ÐS mæu½Þß mæ}¹/4/6/17/4/6/17!worldanyoneB€žÕS mæu½ßà mæ}º/4/6/18/4/6/18!worldanyoneB¥ÚS mæu½àá mæ}»/4/6/19/4/6/19!worldanyoneB$ ¦M mæu½áâ mæ}»/4/7/4/7!worldanyoneBakQ mæu½âã mæ}¼/4/7/0/4/7/0!worldanyoneBb8qQ mæu½ãä mæ}¾/4/7/1/4/7/1!worldanyoneBc8vQ mæu½äå mæ}¿/4/7/2/4/7/2!worldanyoneBdzQ mæu½åæ mæ}¿/4/7/3/4/7/3!worldanyoneBeQ mæu½æç mæ}À/4/7/4/4/7/4!worldanyoneBf8…Q mæu½çè mæ}Â/4/7/5/4/7/5!worldanyoneBg8ŠQ mæu½èé mæ}Ã/4/7/6/4/7/6!worldanyoneBiF”Q mæu½éê mæ}É/4/7/7/4/7/7!worldanyoneBj˜Q mæu½êë mæ}É/4/7/8/4/7/8!worldanyoneBkè¡Q mæu½ëì mæ}Î/4/7/9/4/7/9!worldanyoneBŠ3öS mæu½ìí mæ}Ï/4/7/10/4/7/10!worldanyoneB‹:ûS mæu½íî mæ}Ð/4/7/11/4/7/11!worldanyoneBŒyS mæu½îï mæ}Ò/4/7/12/4/7/12!worldanyoneB€S mæu½ïð mæ}Ó/4/7/13/4/7/13!worldanyoneBއ S mæu½ðñ mæ}Ô/4/7/14/4/7/14!worldanyoneBŽS mæu½ñò mæ}Õ/4/7/15/4/7/15!worldanyoneB]S mæu½òó mæ}Õ/4/7/16/4/7/16!worldanyoneB‘dS mæu½óô mæ}Ö/4/7/17/4/7/17!worldanyoneB’kS mæu½ôõ mæ}×/4/7/18/4/7/18!worldanyoneB“r#S mæu½õö mæ}Ø/4/7/19/4/7/19!worldanyoneB4T ðM mæu½ö÷ mæ}Ù/4/8/4/8!worldanyoneBr>´Q mæu½÷ø mæ}Ù/4/8/0/4/8/0!worldanyoneBstºQ mæu½øù mæ}Û/4/8/1/4/8/1!worldanyoneBtàÁQ mæu½ùú mæ}Þ/4/8/2/4/8/2!worldanyoneBuàÆQ mæu½úû mæ}ß/4/8/3/4/8/3!worldanyoneBvàËQ mæu½ûü mæ}à/4/8/4/4/8/4!worldanyoneBwàÐQ mæu½üý mæ}á/4/8/5/4/8/5!worldanyoneBxàÕQ mæu½ýþ mæ}â/4/8/6/4/8/6!worldanyoneByàÚQ mæu½þÿ mæ}ã/4/8/7/4/8/7!worldanyoneB= àQ mæu½ÿ mæ}ä/4/8/8/4/8/8!worldanyoneBøW æQ mæu½ mæ}å/4/8/9/4/8/9!worldanyoneB: ;S mæu½ mæ}æ/4/8/10/4/8/10!worldanyoneBA @S mæu½ mæ}ç/4/8/11/4/8/11!worldanyoneBH ES mæu½ mæ}è/4/8/12/4/8/12!worldanyoneBO JS mæu½ mæ}é/4/8/13/4/8/13!worldanyoneBV OS mæu½ mæ}ê/4/8/14/4/8/14!worldanyoneB] TS mæu½ mæ}ë/4/8/15/4/8/15!worldanyoneBd YS mæu½ mæ}ì/4/8/16/4/8/16!worldanyoneBk ^S mæu½  mæ}í/4/8/17/4/8/17!worldanyoneB: bS mæu½   mæ}í/4/8/18/4/8/18!worldanyoneBA gS mæu½   mæ}î/4/8/19/4/8/19!worldanyoneBÇã 6M mæu½   mæ}ñ/4/9/4/9!worldanyoneBþã úQ mæu½   mæ}ñ/4/9/0/4/9/0!worldanyoneBÿã ÿQ mæu½  mæ}ò/4/9/1/4/9/1!worldanyoneBò Q mæu½ mæ}ó/4/9/2/4/9/2!worldanyoneB( Q mæu½ mæ}õ/4/9/3/4/9/3!worldanyoneB( Q mæu½ mæ}ö/4/9/4/4/9/4!worldanyoneB( Q mæu½ mæ}÷/4/9/5/4/9/5!worldanyoneB( Q mæu½ mæ}ø/4/9/6/4/9/6!worldanyoneB( Q mæu½ mæ}ù/4/9/7/4/9/7!worldanyoneB( #Q mæu½ mæ}ú/4/9/8/4/9/8!worldanyoneB( (Q mæu½ mæ}û/4/9/9/4/9/9!worldanyoneB# }S mæu½ mæ}ü/4/9/10/4/9/10!worldanyoneB$† ‚S mæu½ mæ}ý/4/9/11/4/9/11!worldanyoneB%U †S mæu½ mæ}ý/4/9/12/4/9/12!worldanyoneB&\ ‹S mæu½ mæ}þ/4/9/13/4/9/13!worldanyoneB'c S mæu½ mæ}ÿ/4/9/14/4/9/14!worldanyoneBð” –S mæu½ mæ~/4/9/15/4/9/15!worldanyoneBñ› ›S mæu½ mæ~/4/9/16/4/9/16!worldanyoneBò¢  S mæu½ mæ~/4/9/17/4/9/17!worldanyoneBóá ¦S mæu½ mæ~/4/9/18/4/9/18!worldanyoneBôè «S mæu½  mæ~/4/9/19/4/9/19!worldanyoneBr ¤I mæu½ ! mæ~/5/5!worldanyoneB¢î iM mæu½!" mæ~/5/0/5/0!worldanyoneB×6 /Q mæu½"# mæ~ /5/0/0/5/0/0!worldanyoneBØ6 4Q mæu½#$ mæ~ /5/0/1/5/0/1!worldanyoneBÙl :Q mæu½$% mæ~ /5/0/2/5/0/2!worldanyoneBÚl ?Q mæu½%& mæ~ /5/0/3/5/0/3!worldanyoneBÛl DQ mæu½&' mæ~/5/0/4/5/0/4!worldanyoneBÝ LQ mæu½'( mæ~/5/0/5/5/0/5!worldanyoneBÞ QQ mæu½() mæ~/5/0/6/5/0/6!worldanyoneBß VQ mæu½)* mæ~/5/0/7/5/0/7!worldanyoneBà [Q mæu½*+ mæ~/5/0/8/5/0/8!worldanyoneBàØ _Q mæu½+, mæ~/5/0/9/5/0/9!worldanyoneBú¥ ´S mæu½,- mæ~/5/0/10/5/0/10!worldanyoneBûä ºS mæu½-. mæ~/5/0/11/5/0/11!worldanyoneBüë ¿S mæu½./ mæ~/5/0/12/5/0/12!worldanyoneBýò ÄS mæu½/0 mæ~/5/0/13/5/0/13!worldanyoneBþù ÉS mæu½01 mæ~/5/0/14/5/0/14!worldanyoneB ÎS mæu½12 mæ~/5/0/15/5/0/15!worldanyoneB ÓS mæu½23 mæ~/5/0/16/5/0/16!worldanyoneBå ×S mæu½34 mæ~/5/0/17/5/0/17!worldanyoneBì ÜS mæu½45 mæ~/5/0/18/5/0/18!worldanyoneBó áS mæu½56 mæ~/5/0/19/5/0/19!worldanyoneB²@ ®M mæu½67 mæ~ /5/1/5/1!worldanyoneBçš tQ mæu½78 mæ~"/5/1/0/5/1/0!worldanyoneBèš yQ mæu½89 mæ~#/5/1/1/5/1/1!worldanyoneBéš ~Q mæu½9: mæ~$/5/1/2/5/1/2!worldanyoneBêš ƒQ mæu½:; mæ~%/5/1/3/5/1/3!worldanyoneBëš ˆQ mæu½;< mæ~&/5/1/4/5/1/4!worldanyoneBìš Q mæu½<= mæ~'/5/1/5/5/1/5!worldanyoneBíš ’Q mæu½=> mæ~(/5/1/6/5/1/6!worldanyoneBîš —Q mæu½>? mæ~)/5/1/7/5/1/7!worldanyoneBïd ›Q mæu½?@ mæ~)/5/1/8/5/1/8!worldanyoneBðd  Q mæu½@A mæ~*/5/1/9/5/1/9!worldanyoneB Á õS mæu½AB mæ~+/5/1/10/5/1/10!worldanyoneB ûS mæu½BC mæ~-/5/1/11/5/1/11!worldanyoneB  S mæu½CD mæ~./5/1/12/5/1/12!worldanyoneB S mæu½DE mæ~//5/1/13/5/1/13!worldanyoneBÝ S mæu½EF mæ~//5/1/14/5/1/14!worldanyoneB S mæu½FG mæ~1/5/1/15/5/1/15!worldanyoneB# S mæu½GH mæ~2/5/1/16/5/1/16!worldanyoneB* S mæu½HI mæ~3/5/1/17/5/1/17!worldanyoneBù S mæu½IJ mæ~3/5/1/18/5/1/18!worldanyoneB "S mæu½JK mæ~4/5/1/19/5/1/19!worldanyoneBÀÊ ïM mæu½KL mæ~5/5/2/5/2!worldanyoneBöð ´Q mæu½LM mæ~6/5/2/0/5/2/0!worldanyoneB÷ð ¹Q mæu½MN mæ~7/5/2/1/5/2/1!worldanyoneBøº ½Q mæu½NO mæ~7/5/2/2/5/2/2!worldanyoneBú& ÄQ mæu½OP mæ~:/5/2/3/5/2/3!worldanyoneBúð ÈQ mæu½PQ mæ~:/5/2/4/5/2/4!worldanyoneBûð ÍQ mæu½QR mæ~;/5/2/5/5/2/5!worldanyoneBüð ÒQ mæu½RS mæ~</5/2/6/5/2/6!worldanyoneBýð ×Q mæu½ST mæ~=/5/2/7/5/2/7!worldanyoneBþð ÜQ mæu½TU mæ~>/5/2/8/5/2/8!worldanyoneB5 âQ mæu½UV mæ~@/5/2/9/5/2/9!worldanyoneB 7S mæu½VW mæ~A/5/2/10/5/2/10!worldanyoneBE =S mæu½WX mæ~C/5/2/11/5/2/11!worldanyoneB AS mæu½XY mæ~C/5/2/12/5/2/12!worldanyoneB FS mæu½YZ mæ~D/5/2/13/5/2/13!worldanyoneB" KS mæu½Z[ mæ~E/5/2/14/5/2/14!worldanyoneB ) PS mæu½[\ mæ~F/5/2/15/5/2/15!worldanyoneB ø TS mæu½\] mæ~F/5/2/16/5/2/16!worldanyoneB!ÿ YS mæu½]^ mæ~G/5/2/17/5/2/17!worldanyoneB# ^S mæu½^_ mæ~H/5/2/18/5/2/18!worldanyoneB$ cS mæu½_` mæ~I/5/2/19/5/2/19!worldanyoneBÏT 0M mæu½`a mæ~J/5/3/5/3!worldanyoneB‹ õQ mæu½ab mæ~K/5/3/0/5/3/0!worldanyoneB‹ úQ mæu½bc mæ~L/5/3/1/5/3/1!worldanyoneB‹ ÿQ mæu½cd mæ~M/5/3/2/5/3/2!worldanyoneB ‹ Q mæu½de mæ~N/5/3/3/5/3/3!worldanyoneB U Q mæu½ef mæ~N/5/3/4/5/3/4!worldanyoneB c Q mæu½fg mæ~T/5/3/5/5/3/5!worldanyoneB c Q mæu½gh mæ~U/5/3/6/5/3/6!worldanyoneBc Q mæu½hi mæ~V/5/3/7/5/3/7!worldanyoneB- Q mæu½ij mæ~V/5/3/8/5/3/8!worldanyoneB- %Q mæu½jk mæ~W/5/3/9/5/3/9!worldanyoneB+» {S mæu½kl mæ~Y/5/3/10/5/3/10!worldanyoneB,Š S mæu½lm mæ~Y/5/3/11/5/3/11!worldanyoneB-‘ „S mæu½mn mæ~Z/5/3/12/5/3/12!worldanyoneB.Ð ŠS mæu½no mæ~\/5/3/13/5/3/13!worldanyoneB/× S mæu½op mæ~]/5/3/14/5/3/14!worldanyoneB0Þ ”S mæu½pq mæ~^/5/3/15/5/3/15!worldanyoneB1å ™S mæu½qr mæ~_/5/3/16/5/3/16!worldanyoneB2ì žS mæu½rs mæ~`/5/3/17/5/3/17!worldanyoneB4+ ¤S mæu½st mæ~b/5/3/18/5/3/18!worldanyoneB4ú ¨S mæu½tu mæ~b/5/3/19/5/3/19!worldanyoneBÞ¦ uM mæu½uv mæ~c/5/4/5/4!worldanyoneBï :Q mæu½vw mæ~d/5/4/0/5/4/0!worldanyoneBï ?Q mæu½wx mæ~e/5/4/1/5/4/1!worldanyoneBï DQ mæu½xy mæ~f/5/4/2/5/4/2!worldanyoneBï IQ mæu½yz mæ~g/5/4/3/5/4/3!worldanyoneB% OQ mæu½z{ mæ~i/5/4/4/5/4/4!worldanyoneB% TQ mæu½{| mæ~j/5/4/5/5/4/5!worldanyoneB% YQ mæu½|} mæ~k/5/4/6/5/4/6!worldanyoneB% ^Q mæu½}~ mæ~l/5/4/7/5/4/7!worldanyoneB% cQ mæu½~ mæ~m/5/4/8/5/4/8!worldanyoneBï gQ mæu½€ mæ~m/5/4/9/5/4/9!worldanyoneB;È ¼S mæu½€ mæ~n/5/4/10/5/4/10!worldanyoneB<Ï ÁS mæu½‚ mæ~o/5/4/11/5/4/11!worldanyoneB=Ö ÆS mæu½‚ƒ mæ~p/5/4/12/5/4/12!worldanyoneB>Ý ËS mæu½ƒ„ mæ~q/5/4/13/5/4/13!worldanyoneB?ä ÐS mæu½„… mæ~r/5/4/14/5/4/14!worldanyoneB@ë ÕS mæu½…† mæ~s/5/4/15/5/4/15!worldanyoneBAò ÚS mæu½†‡ mæ~t/5/4/16/5/4/16!worldanyoneBC1 àS mæu½‡ˆ mæ~v/5/4/17/5/4/17!worldanyoneBD äS mæu½ˆ‰ mæ~v/5/4/18/5/4/18!worldanyoneBE éS mæu½‰Š mæ~w/5/4/19/5/4/19!worldanyoneBí0 ¶M mæu½Š‹ mæ~x/5/5/5/5!worldanyoneB&{ {Q mæu½‹Œ mæ~y/5/5/0/5/5/0!worldanyoneB'E Q mæu½Œ mæ~y/5/5/1/5/5/1!worldanyoneB(E „Q mæu½Ž mæ~z/5/5/2/5/5/2!worldanyoneB)E ‰Q mæu½Ž mæ~{/5/5/3/5/5/3!worldanyoneB*E ŽQ mæu½ mæ~|/5/5/4/5/5/4!worldanyoneB+E “Q mæu½‘ mæ~}/5/5/5/5/5/5!worldanyoneB,E ˜Q mæu½‘’ mæ~~/5/5/6/5/5/6!worldanyoneB- œQ mæu½’“ mæ~~/5/5/7/5/5/7!worldanyoneB. ¡Q mæu½“” mæ~/5/5/8/5/5/8!worldanyoneB/E §Q mæu½”• mæ~/5/5/9/5/5/9!worldanyoneBK üS mæu½•– mæ~‚/5/5/10/5/5/10!worldanyoneBLlS mæu½–— mæ~‚/5/5/11/5/5/11!worldanyoneBMsS mæu½—˜ mæ~ƒ/5/5/12/5/5/12!worldanyoneBNz S mæu½˜™ mæ~„/5/5/13/5/5/13!worldanyoneBO¹S mæu½™š mæ~†/5/5/14/5/5/14!worldanyoneBPÀS mæu½š› mæ~‡/5/5/15/5/5/15!worldanyoneBQS mæu½›œ mæ~‡/5/5/16/5/5/16!worldanyoneBR–S mæu½œ mæ~ˆ/5/5/17/5/5/17!worldanyoneBSÕ$S mæu½ž mæ~Š/5/5/18/5/5/18!worldanyoneBT¤(S mæu½žŸ mæ~Š/5/5/19/5/5/19!worldanyoneBûV õM mæu½Ÿ  mæ~‹/5/6/5/6!worldanyoneB5› ºQ mæu½ ¡ mæ~Œ/5/6/0/5/6/0!worldanyoneB6› ¿Q mæu½¡¢ mæ~/5/6/1/5/6/1!worldanyoneB7› ÄQ mæu½¢£ mæ~Ž/5/6/2/5/6/2!worldanyoneB8› ÉQ mæu½£¤ mæ~/5/6/3/5/6/3!worldanyoneB9Ñ ÏQ mæu½¤¥ mæ~‘/5/6/4/5/6/4!worldanyoneB;= ÖQ mæu½¥¦ mæ~”/5/6/5/5/6/5!worldanyoneBs æQ mæu½¨© mæ~˜/5/6/8/5/6/8!worldanyoneB?s ëQ mæu½©ª mæ~™/5/6/9/5/6/9!worldanyoneB\R@S mæu½ª« mæ~š/5/6/10/5/6/10!worldanyoneB]YES mæu½«¬ mæ~›/5/6/11/5/6/11!worldanyoneB^(IS mæu½¬­ mæ~›/5/6/12/5/6/12!worldanyoneB_gOS mæu½­® mæ~/5/6/13/5/6/13!worldanyoneB`¦US mæu½®¯ mæ~Ÿ/5/6/14/5/6/14!worldanyoneBauYS mæu½¯° mæ~Ÿ/5/6/15/5/6/15!worldanyoneBb|^S mæu½°± mæ~ /5/6/16/5/6/16!worldanyoneBcƒcS mæu½±² mæ~¡/5/6/17/5/6/17!worldanyoneBdÂiS mæu½²³ mæ~£/5/6/18/5/6/18!worldanyoneBeÉnS mæu½³´ mæ~¤/5/6/19/5/6/19!worldanyoneB é ;M mæu½´µ mæ~¥/5/7/5/7!worldanyoneBF5Q mæu½µ¶ mæ~¦/5/7/0/5/7/0!worldanyoneBG5Q mæu½¶· mæ~§/5/7/1/5/7/1!worldanyoneBH5 Q mæu½·¸ mæ~¨/5/7/2/5/7/2!worldanyoneBI5Q mæu½¸¹ mæ~©/5/7/3/5/7/3!worldanyoneBIÿQ mæu½¹º mæ~©/5/7/4/5/7/4!worldanyoneBJÿQ mæu½º» mæ~ª/5/7/5/5/7/5!worldanyoneBKÿQ mæu½»¼ mæ~«/5/7/6/5/7/6!worldanyoneBLÿ"Q mæu½¼½ mæ~¬/5/7/7/5/7/7!worldanyoneBN5(Q mæu½½¾ mæ~®/5/7/8/5/7/8!worldanyoneBNÿ,Q mæu½¾¿ mæ~®/5/7/9/5/7/9!worldanyoneBl—‚S mæu½¿À mæ~°/5/7/10/5/7/10!worldanyoneBmž‡S mæu½ÀÁ mæ~±/5/7/11/5/7/11!worldanyoneBn¥ŒS mæu½Á mæ~²/5/7/12/5/7/12!worldanyoneBo¬‘S mæu½Âà mæ~³/5/7/13/5/7/13!worldanyoneBp³–S mæu½ÃÄ mæ~´/5/7/14/5/7/14!worldanyoneBqº›S mæu½ÄÅ mæ~µ/5/7/15/5/7/15!worldanyoneBr‰ŸS mæu½ÅÆ mæ~µ/5/7/16/5/7/16!worldanyoneBsÈ¥S mæu½ÆÇ mæ~·/5/7/17/5/7/17!worldanyoneBtϪS mæu½ÇÈ mæ~¸/5/7/18/5/7/18!worldanyoneBuÖ¯S mæu½ÈÉ mæ~¹/5/7/19/5/7/19!worldanyoneBs |M mæu½ÉÊ mæ~º/5/8/5/8!worldanyoneBUÁAQ mæu½ÊË mæ~»/5/8/0/5/8/0!worldanyoneBVÁFQ mæu½ËÌ mæ~¼/5/8/1/5/8/1!worldanyoneBWÁKQ mæu½ÌÍ mæ~½/5/8/2/5/8/2!worldanyoneBXÁPQ mæu½ÍÎ mæ~¾/5/8/3/5/8/3!worldanyoneBYÁUQ mæu½ÎÏ mæ~¿/5/8/4/5/8/4!worldanyoneB[c]Q mæu½ÏÐ mæ~Ã/5/8/5/5/8/5!worldanyoneB\™cQ mæu½ÐÑ mæ~Å/5/8/6/5/8/6!worldanyoneB^qlQ mæu½ÑÒ mæ~Ê/5/8/7/5/8/7!worldanyoneB_qqQ mæu½ÒÓ mæ~Ë/5/8/8/5/8/8!worldanyoneB`§wQ mæu½ÓÔ mæ~Í/5/8/9/5/8/9!worldanyoneB~œÌS mæu½ÔÕ mæ~Î/5/8/10/5/8/10!worldanyoneB£ÑS mæu½ÕÖ mæ~Ï/5/8/11/5/8/11!worldanyoneB€â×S mæu½Ö× mæ~Ñ/5/8/12/5/8/12!worldanyoneBéÜS mæu½×Ø mæ~Ò/5/8/13/5/8/13!worldanyoneB‚¸àS mæu½ØÙ mæ~Ò/5/8/14/5/8/14!worldanyoneBƒ¿åS mæu½ÙÚ mæ~Ó/5/8/15/5/8/15!worldanyoneB„þëS mæu½ÚÛ mæ~Õ/5/8/16/5/8/16!worldanyoneB†ðS mæu½ÛÜ mæ~Ö/5/8/17/5/8/17!worldanyoneB‡ õS mæu½ÜÝ mæ~×/5/8/18/5/8/18!worldanyoneBˆúS mæu½ÝÞ mæ~Ø/5/8/19/5/8/19!worldanyoneB)ñ ÇM mæu½Þß mæ~Ù/5/9/5/9!worldanyoneBgiŒQ mæu½ßà mæ~Ú/5/9/0/5/9/0!worldanyoneBhi‘Q mæu½àá mæ~Û/5/9/1/5/9/1!worldanyoneBi3•Q mæu½áâ mæ~Û/5/9/2/5/9/2!worldanyoneBji›Q mæu½âã mæ~Ý/5/9/3/5/9/3!worldanyoneBk3ŸQ mæu½ãä mæ~Ý/5/9/4/5/9/4!worldanyoneBlÕ§Q mæu½äå mæ~á/5/9/5/5/9/5!worldanyoneBmÕ¬Q mæu½åæ mæ~â/5/9/6/5/9/6!worldanyoneBnÕ±Q mæu½æç mæ~ã/5/9/7/5/9/7!worldanyoneBoÕ¶Q mæu½çè mæ~ä/5/9/8/5/9/8!worldanyoneBpÕ»Q mæu½èé mæ~å/5/9/9/5/9/9!worldanyoneBQS mæu½éê mæ~æ/5/9/10/5/9/10!worldanyoneB S mæu½êë mæ~æ/5/9/11/5/9/11!worldanyoneB‘'S mæu½ëì mæ~ç/5/9/12/5/9/12!worldanyoneB’fS mæu½ìí mæ~é/5/9/13/5/9/13!worldanyoneB“m$S mæu½íî mæ~ê/5/9/14/5/9/14!worldanyoneB”t)S mæu½îï mæ~ë/5/9/15/5/9/15!worldanyoneB•{.S mæu½ïð mæ~ì/5/9/16/5/9/16!worldanyoneB–‚3S mæu½ðñ mæ~í/5/9/17/5/9/17!worldanyoneB—Q7S mæu½ñò mæ~í/5/9/18/5/9/18!worldanyoneB˜X<S mæu½òó mæ~î/5/9/19/5/9/19!worldanyoneBûÀ 5I mæu½óô mæ~ï/6/6!worldanyoneB6û úM mæu½ôõ mæ~ð/6/0/6/0!worldanyoneBuO¿Q mæu½õö mæ~ñ/6/0/0/6/0/0!worldanyoneBv…ÅQ mæu½ö÷ mæ~ó/6/0/1/6/0/1!worldanyoneBwñÌQ mæu½÷ø mæ~ö/6/0/2/6/0/2!worldanyoneBx»ÐQ mæu½øù mæ~ö/6/0/3/6/0/3!worldanyoneBy»ÕQ mæu½ùú mæ~÷/6/0/4/6/0/4!worldanyoneBz»ÚQ mæu½úû mæ~ø/6/0/5/6/0/5!worldanyoneB{»ßQ mæu½ûü mæ~ù/6/0/6/6/0/6!worldanyoneB}'æQ mæu½üý mæ~ü/6/0/7/6/0/7!worldanyoneB~'ëQ mæu½ýþ mæ~ý/6/0/8/6/0/8!worldanyoneB'ðQ mæu½þÿ mæ~þ/6/0/9/6/0/9!worldanyoneB^VFS mæu½ÿ mæ~ÿ/6/0/10/6/0/10!worldanyoneBn KS mæu½ mæ~ÿ/6/0/11/6/0/11!worldanyoneBà× RS mæu½ mæ/6/0/12/6/0/12!worldanyoneBáÞ WS mæu½ mæ/6/0/13/6/0/13!worldanyoneBâå \S mæu½ mæ/6/0/14/6/0/14!worldanyoneBãì aS mæu½ mæ/6/0/15/6/0/15!worldanyoneBå+ gS mæu½ mæ/6/0/16/6/0/16!worldanyoneBæj mS mæu½ mæ/6/0/17/6/0/17!worldanyoneBè‰ wS mæu½ mæ/6/0/18/6/0/18!worldanyoneBê ~S mæu½  mæ/6/0/19/6/0/19!worldanyoneBš± KM mæu½   mæ/6/1/6/1!worldanyoneBÎG Q mæu½   mæ/6/1/0/6/1/0!worldanyoneBÏG Q mæu½   mæ/6/1/1/6/1/1!worldanyoneBÐG Q mæu½   mæ/6/1/2/6/1/2!worldanyoneBÑG Q mæu½  mæ/6/1/3/6/1/3!worldanyoneBÒ #Q mæu½ mæ/6/1/4/6/1/4!worldanyoneBÓ (Q mæu½ mæ/6/1/5/6/1/5!worldanyoneBÔ -Q mæu½ mæ/6/1/6/6/1/6!worldanyoneBÕ 2Q mæu½ mæ/6/1/7/6/1/7!worldanyoneBÕÛ 6Q mæu½ mæ/6/1/8/6/1/8!worldanyoneB× <Q mæu½ mæ/6/1/9/6/1/9!worldanyoneBðÎ ’S mæu½ mæ/6/1/10/6/1/10!worldanyoneBñÕ —S mæu½ mæ/6/1/11/6/1/11!worldanyoneBòÜ œS mæu½ mæ/6/1/12/6/1/12!worldanyoneBó«  S mæu½ mæ/6/1/13/6/1/13!worldanyoneBô² ¥S mæu½ mæ /6/1/14/6/1/14!worldanyoneBõ¹ ªS mæu½ mæ!/6/1/15/6/1/15!worldanyoneBöÀ ¯S mæu½ mæ"/6/1/16/6/1/16!worldanyoneB÷ ³S mæu½ mæ"/6/1/17/6/1/17!worldanyoneBø– ¸S mæu½ mæ#/6/1/18/6/1/18!worldanyoneBù ½S mæu½ mæ$/6/1/19/6/1/19!worldanyoneB¨× ŠM mæu½ mæ%/6/2/6/2!worldanyoneBÝg OQ mæu½  mæ&/6/2/0/6/2/0!worldanyoneBÞg TQ mæu½ ! mæ'/6/2/1/6/2/1!worldanyoneBß1 XQ mæu½!" mæ'/6/2/2/6/2/2!worldanyoneBàg ^Q mæu½"# mæ)/6/2/3/6/2/3!worldanyoneBáÓ eQ mæu½#$ mæ,/6/2/4/6/2/4!worldanyoneBâÓ jQ mæu½$% mæ-/6/2/5/6/2/5!worldanyoneBãÓ oQ mæu½%& mæ./6/2/6/6/2/6!worldanyoneBäÓ tQ mæu½&' mæ//6/2/7/6/2/7!worldanyoneBæ? {Q mæu½'( mæ2/6/2/8/6/2/8!worldanyoneBç? €Q mæu½() mæ3/6/2/9/6/2/9!worldanyoneB" ÔS mæu½)* mæ3/6/2/10/6/2/10!worldanyoneB) ÙS mæu½*+ mæ4/6/2/11/6/2/11!worldanyoneB0 ÞS mæu½+, mæ5/6/2/12/6/2/12!worldanyoneBO èS mæu½,- mæ;/6/2/13/6/2/13!worldanyoneBÆ ïS mæu½-. mæ>/6/2/14/6/2/14!worldanyoneBÍ ôS mæu½./ mæ?/6/2/15/6/2/15!worldanyoneBÔ ùS mæu½/0 mæ@/6/2/16/6/2/16!worldanyoneB Û þS mæu½01 mæA/6/2/17/6/2/17!worldanyoneB â S mæu½12 mæB/6/2/18/6/2/18!worldanyoneB é S mæu½23 mæC/6/2/19/6/2/19!worldanyoneB¹# ÔM mæu½34 mæC/6/3/6/3!worldanyoneBîÙ ™Q mæu½45 mæD/6/3/0/6/3/0!worldanyoneBð ŸQ mæu½56 mæF/6/3/1/6/3/1!worldanyoneBðÙ £Q mæu½67 mæF/6/3/2/6/3/2!worldanyoneBñÙ ¨Q mæu½78 mæG/6/3/3/6/3/3!worldanyoneBòÙ ­Q mæu½89 mæH/6/3/4/6/3/4!worldanyoneBóÙ ²Q mæu½9: mæI/6/3/5/6/3/5!worldanyoneBôÙ ·Q mæu½:; mæJ/6/3/6/6/3/6!worldanyoneBõÙ ¼Q mæu½;< mæK/6/3/7/6/3/7!worldanyoneBö£ ÀQ mæu½<= mæK/6/3/8/6/3/8!worldanyoneB÷Ù ÆQ mæu½=> mæM/6/3/9/6/3/9!worldanyoneB S mæu½>? mæN/6/3/10/6/3/10!worldanyoneB† S mæu½?@ mæO/6/3/11/6/3/11!worldanyoneBÅ &S mæu½@A mæQ/6/3/12/6/3/12!worldanyoneBÌ +S mæu½AB mæR/6/3/13/6/3/13!worldanyoneB› /S mæu½BC mæR/6/3/14/6/3/14!worldanyoneB¢ 4S mæu½CD mæS/6/3/15/6/3/15!worldanyoneB© 9S mæu½DE mæT/6/3/16/6/3/16!worldanyoneB° >S mæu½EF mæU/6/3/17/6/3/17!worldanyoneB BS mæu½FG mæU/6/3/18/6/3/18!worldanyoneBö IS mæu½GH mæX/6/3/19/6/3/19!worldanyoneBÇß M mæu½HI mæY/6/4/6/4!worldanyoneBþ› ÛQ mæu½IJ mæZ/6/4/0/6/4/0!worldanyoneBÿ› àQ mæu½JK mæ[/6/4/1/6/4/1!worldanyoneBª åQ mæu½KL mæ\/6/4/2/6/4/2!worldanyoneBt éQ mæu½LM mæ\/6/4/3/6/4/3!worldanyoneBt îQ mæu½MN mæ]/6/4/4/6/4/4!worldanyoneBt óQ mæu½NO mæ^/6/4/5/6/4/5!worldanyoneBt øQ mæu½OP mæ_/6/4/6/6/4/6!worldanyoneB> üQ mæu½PQ mæ_/6/4/7/6/4/7!worldanyoneBt Q mæu½QR mæa/6/4/8/6/4/8!worldanyoneB> Q mæu½RS mæa/6/4/9/6/4/9!worldanyoneB"ü ^S mæu½ST mæe/6/4/10/6/4/10!worldanyoneB$ cS mæu½TU mæf/6/4/11/6/4/11!worldanyoneB$Ò gS mæu½UV mæf/6/4/12/6/4/12!worldanyoneB%Ù lS mæu½VW mæg/6/4/13/6/4/13!worldanyoneB&à qS mæu½WX mæh/6/4/14/6/4/14!worldanyoneB'ç vS mæu½XY mæi/6/4/15/6/4/15!worldanyoneB(î {S mæu½YZ mæj/6/4/16/6/4/16!worldanyoneB)õ €S mæu½Z[ mæk/6/4/17/6/4/17!worldanyoneB*ü …S mæu½[\ mæl/6/4/18/6/4/18!worldanyoneB, ŠS mæu½\] mæm/6/4/19/6/4/19!worldanyoneBÖi WM mæu½]^ mæn/6/5/6/5!worldanyoneB6 Q mæu½^_ mæo/6/5/0/6/5/0!worldanyoneB6 !Q mæu½_` mæp/6/5/1/6/5/1!worldanyoneB6 &Q mæu½`a mæq/6/5/2/6/5/2!worldanyoneB *Q mæu½ab mæq/6/5/3/6/5/3!worldanyoneB¢ 2Q mæu½bc mæu/6/5/4/6/5/4!worldanyoneB¢ 7Q mæu½cd mæv/6/5/5/6/5/5!worldanyoneB¢ <Q mæu½de mæw/6/5/6/6/5/6!worldanyoneBl @Q mæu½ef mæw/6/5/7/6/5/7!worldanyoneB¢ FQ mæu½fg mæy/6/5/8/6/5/8!worldanyoneB¢ KQ mæu½gh mæz/6/5/9/6/5/9!worldanyoneB3 ŸS mæu½hi mæz/6/5/10/6/5/10!worldanyoneB4 ¤S mæu½ij mæ{/6/5/11/6/5/11!worldanyoneB5 ©S mæu½jk mæ|/6/5/12/6/5/12!worldanyoneB6 ®S mæu½kl mæ}/6/5/13/6/5/13!worldanyoneB6í ²S mæu½lm mæ}/6/5/14/6/5/14!worldanyoneB8, ¸S mæu½mn mæ/6/5/15/6/5/15!worldanyoneB9k ¾S mæu½no mæ/6/5/16/6/5/16!worldanyoneB:r ÃS mæu½op mæ‚/6/5/17/6/5/17!worldanyoneB;A ÇS mæu½pq mæ‚/6/5/18/6/5/18!worldanyoneBö éQ mæu½ž mæ¸/6/8/0/6/8/0!worldanyoneB?ö îQ mæu½žŸ mæ¹/6/8/1/6/8/1!worldanyoneB@ö óQ mæu½Ÿ  mæº/6/8/2/6/8/2!worldanyoneBAö øQ mæu½ ¡ mæ»/6/8/3/6/8/3!worldanyoneBCb ÿQ mæu½¡¢ mæ¾/6/8/4/6/8/4!worldanyoneBDbQ mæu½¢£ mæ¿/6/8/5/6/8/5!worldanyoneBE˜ Q mæu½£¤ mæÁ/6/8/6/6/8/6!worldanyoneBG¦Q mæu½¤¥ mæÇ/6/8/7/6/8/7!worldanyoneBHÜQ mæu½¥¦ mæÉ/6/8/8/6/8/8!worldanyoneBIÜQ mæu½¦§ mæÊ/6/8/9/6/8/9!worldanyoneBg tS mæu½§¨ mæË/6/8/10/6/8/10!worldanyoneBh'yS mæu½¨© mæÌ/6/8/11/6/8/11!worldanyoneBiž€S mæu½©ª mæÏ/6/8/12/6/8/12!worldanyoneBjm„S mæu½ª« mæÏ/6/8/13/6/8/13!worldanyoneBk¬ŠS mæu½«¬ mæÑ/6/8/14/6/8/14!worldanyoneBl³S mæu½¬­ mæÒ/6/8/15/6/8/15!worldanyoneBmò•S mæu½­® mæÔ/6/8/16/6/8/16!worldanyoneBnÁ™S mæu½®¯ mæÔ/6/8/17/6/8/17!worldanyoneBoÈžS mæu½¯° mæÕ/6/8/18/6/8/18!worldanyoneBpÏ£S mæu½°± mæÖ/6/8/19/6/8/19!worldanyoneBº pM mæu½±² mæ×/6/9/6/9!worldanyoneBPÔ5Q mæu½²³ mæØ/6/9/0/6/9/0!worldanyoneBQÔ:Q mæu½³´ mæÙ/6/9/1/6/9/1!worldanyoneBRž>Q mæu½´µ mæÙ/6/9/2/6/9/2!worldanyoneBSžCQ mæu½µ¶ mæÚ/6/9/3/6/9/3!worldanyoneBTžHQ mæu½¶· mæÛ/6/9/4/6/9/4!worldanyoneBUžMQ mæu½·¸ mæÜ/6/9/5/6/9/5!worldanyoneBVžRQ mæu½¸¹ mæÝ/6/9/6/6/9/6!worldanyoneBX YQ mæu½¹º mæà/6/9/7/6/9/7!worldanyoneBYv`Q mæu½º» mæã/6/9/8/6/9/8!worldanyoneBZ¬fQ mæu½»¼ mæå/6/9/9/6/9/9!worldanyoneBx}»S mæu½¼½ mææ/6/9/10/6/9/10!worldanyoneBy„ÀS mæu½½¾ mæç/6/9/11/6/9/11!worldanyoneBz‹ÅS mæu½¾¿ mæè/6/9/12/6/9/12!worldanyoneB{’ÊS mæu½¿À mæé/6/9/13/6/9/13!worldanyoneB|™ÏS mæu½ÀÁ mæê/6/9/14/6/9/14!worldanyoneB} ÔS mæu½Á mæë/6/9/15/6/9/15!worldanyoneB~§ÙS mæu½Âà mæì/6/9/16/6/9/16!worldanyoneB®ÞS mæu½ÃÄ mæí/6/9/17/6/9/17!worldanyoneB€µãS mæu½ÄÅ mæî/6/9/18/6/9/18!worldanyoneB¼èS mæu½ÅÆ mæï/6/9/19/6/9/19!worldanyoneBèq áI mæu½ÆÇ mæð/7/7!worldanyoneB"Z ¦M mæu½ÇÈ mæñ/7/0/7/0!worldanyoneB_\kQ mæu½ÈÉ mæò/7/0/0/7/0/0!worldanyoneB`ÈrQ mæu½ÉÊ mæõ/7/0/1/7/0/1!worldanyoneBa’vQ mæu½ÊË mæõ/7/0/2/7/0/2!worldanyoneBb’{Q mæu½ËÌ mæö/7/0/3/7/0/3!worldanyoneBd4ƒQ mæu½ÌÍ mæú/7/0/4/7/0/4!worldanyoneBe ŠQ mæu½ÍÎ mæý/7/0/5/7/0/5!worldanyoneB1y “Q mæu½ÎÏ mæ€/7/0/6/7/0/6!worldanyoneB2y ˜Q mæu½ÏÐ mæ€/7/0/7/7/0/7!worldanyoneB3y Q mæu½ÐÑ mæ€/7/0/8/7/0/8!worldanyoneB4¯ £Q mæu½ÑÒ mæ€/7/0/9/7/0/9!worldanyoneBQ øS mæu½ÒÓ mæ€/7/0/10/7/0/10!worldanyoneBR ýS mæu½ÓÔ mæ€/7/0/11/7/0/11!worldanyoneBT`S mæu½ÔÕ mæ€/7/0/12/7/0/12!worldanyoneBUg S mæu½ÕÖ mæ€/7/0/13/7/0/13!worldanyoneBVnS mæu½Ö× mæ€/7/0/14/7/0/14!worldanyoneBWuS mæu½×Ø mæ€/7/0/15/7/0/15!worldanyoneBY\ S mæu½ØÙ mæ€/7/0/16/7/0/16!worldanyoneBZÓ'S mæu½ÙÚ mæ€/7/0/17/7/0/17!worldanyoneB\J.S mæu½ÚÛ mæ€/7/0/18/7/0/18!worldanyoneB]‰4S mæu½ÛÜ mæ€/7/0/19/7/0/19!worldanyoneB] M mæu½ÜÝ mæ€!/7/1/7/1!worldanyoneB>Ñ ÈQ mæu½ÝÞ mæ€"/7/1/0/7/1/0!worldanyoneB?Ñ ÍQ mæu½Þß mæ€#/7/1/1/7/1/1!worldanyoneB@› ÑQ mæu½ßà mæ€#/7/1/2/7/1/2!worldanyoneBB ØQ mæu½àá mæ€&/7/1/3/7/1/3!worldanyoneBC= ÞQ mæu½áâ mæ€(/7/1/4/7/1/4!worldanyoneBDs äQ mæu½âã mæ€*/7/1/5/7/1/5!worldanyoneBEs éQ mæu½ãä mæ€+/7/1/6/7/1/6!worldanyoneBFs îQ mæu½äå mæ€,/7/1/7/7/1/7!worldanyoneBGß õQ mæu½åæ mæ€//7/1/8/7/1/8!worldanyoneBHß úQ mæu½æç mæ€0/7/1/9/7/1/9!worldanyoneBfPS mæu½çè mæ€2/7/1/10/7/1/10!worldanyoneBgUS mæu½èé mæ€3/7/1/11/7/1/11!worldanyoneBh][S mæu½éê mæ€5/7/1/12/7/1/12!worldanyoneBiÔbS mæu½êë mæ€8/7/1/13/7/1/13!worldanyoneBjÛgS mæu½ëì mæ€9/7/1/14/7/1/14!worldanyoneBkªkS mæu½ìí mæ€9/7/1/15/7/1/15!worldanyoneBl±pS mæu½íî mæ€:/7/1/16/7/1/16!worldanyoneBm¸uS mæu½îï mæ€;/7/1/17/7/1/17!worldanyoneBn¿zS mæu½ïð mæ€</7/1/18/7/1/18!worldanyoneBoÆS mæu½ðñ mæ€=/7/1/19/7/1/19!worldanyoneBw LM mæu½ñò mæ€>/7/2/7/2!worldanyoneBP Q mæu½òó mæ€?/7/2/0/7/2/0!worldanyoneBQ Q mæu½óô mæ€@/7/2/1/7/2/1!worldanyoneBRCQ mæu½ôõ mæ€B/7/2/2/7/2/2!worldanyoneBSy"Q mæu½õö mæ€D/7/2/3/7/2/3!worldanyoneBT¯(Q mæu½ö÷ mæ€F/7/2/4/7/2/4!worldanyoneBUy,Q mæu½÷ø mæ€F/7/2/5/7/2/5!worldanyoneBVy1Q mæu½øù mæ€G/7/2/6/7/2/6!worldanyoneBWy6Q mæu½ùú mæ€H/7/2/7/7/2/7!worldanyoneBX¯<Q mæu½úû mæ€J/7/2/8/7/2/8!worldanyoneBY¯AQ mæu½ûü mæ€K/7/2/9/7/2/9!worldanyoneBw<–S mæu½üý mæ€L/7/2/10/7/2/10!worldanyoneBxC›S mæu½ýþ mæ€M/7/2/11/7/2/11!worldanyoneByŸS mæu½þÿ mæ€M/7/2/12/7/2/12!worldanyoneB:Z ¥S mæu½ÿ mæ€N/7/2/13/7/2/13!worldanyoneBô{ ¯S mæu½ mæ€S/7/2/14/7/2/14!worldanyoneBõ‚ ´S mæu½ mæ€T/7/2/15/7/2/15!worldanyoneBö‰ ¹S mæu½ mæ€U/7/2/16/7/2/16!worldanyoneB÷ ¾S mæu½ mæ€V/7/2/17/7/2/17!worldanyoneBø_ ÂS mæu½ mæ€V/7/2/18/7/2/18!worldanyoneBùf ÇS mæu½ mæ€W/7/2/19/7/2/19!worldanyoneB¨j ”M mæu½ mæ€X/7/3/7/3!worldanyoneBÜè XQ mæu½ mæ€X/7/3/0/7/3/0!worldanyoneBÝè ]Q mæu½  mæ€Y/7/3/1/7/3/1!worldanyoneBÞè bQ mæu½   mæ€Z/7/3/2/7/3/2!worldanyoneBà hQ mæu½   mæ€\/7/3/3/7/3/3!worldanyoneBá mQ mæu½   mæ€]/7/3/4/7/3/4!worldanyoneBâ rQ mæu½   mæ€^/7/3/5/7/3/5!worldanyoneBã wQ mæu½  mæ€_/7/3/6/7/3/6!worldanyoneBä |Q mæu½ mæ€`/7/3/7/7/3/7!worldanyoneBåT ‚Q mæu½ mæ€b/7/3/8/7/3/8!worldanyoneBæŠ ˆQ mæu½ mæ€d/7/3/9/7/3/9!worldanyoneB³ ÝS mæu½ mæ€e/7/3/10/7/3/10!worldanyoneBº âS mæu½ mæ€f/7/3/11/7/3/11!worldanyoneBù èS mæu½ mæ€h/7/3/12/7/3/12!worldanyoneB8 îS mæu½ mæ€j/7/3/13/7/3/13!worldanyoneB? óS mæu½ mæ€k/7/3/14/7/3/14!worldanyoneB ÷S mæu½ mæ€k/7/3/15/7/3/15!worldanyoneB üS mæu½ mæ€l/7/3/16/7/3/16!worldanyoneBŒ S mæu½ mæ€o/7/3/17/7/3/17!worldanyoneB [ S mæu½ mæ€o/7/3/18/7/3/18!worldanyoneB š S mæu½ mæ€q/7/3/19/7/3/19!worldanyoneB·î ÚM mæu½ mæ€r/7/4/7/4!worldanyoneBíî  Q mæu½ mæ€t/7/4/0/7/4/0!worldanyoneBïZ §Q mæu½ mæ€w/7/4/1/7/4/1!worldanyoneBðÆ ®Q mæu½ mæ€z/7/4/2/7/4/2!worldanyoneBñÆ ³Q mæu½  mæ€{/7/4/3/7/4/3!worldanyoneBòÆ ¸Q mæu½ ! mæ€|/7/4/4/7/4/4!worldanyoneBóÆ ½Q mæu½!" mæ€}/7/4/5/7/4/5!worldanyoneBôÆ ÂQ mæu½"# mæ€~/7/4/6/7/4/6!worldanyoneBöh ÊQ mæu½#$ m怂/7/4/7/7/4/7!worldanyoneB÷h ÏQ mæu½$% m怃/7/4/8/7/4/8!worldanyoneBøh ÔQ mæu½%& m怄/7/4/9/7/4/9!worldanyoneB( )S mæu½&' m怅/7/4/10/7/4/10!worldanyoneBg /S mæu½'( m怇/7/4/11/7/4/11!worldanyoneBn 4S mæu½() m怈/7/4/12/7/4/12!worldanyoneBu 9S mæu½)* m怉/7/4/13/7/4/13!worldanyoneBì @S mæu½*+ m怌/7/4/14/7/4/14!worldanyoneBó ES mæu½+, mæ€/7/4/15/7/4/15!worldanyoneBú JS mæu½,- m怎/7/4/16/7/4/16!worldanyoneB OS mæu½-. mæ€/7/4/17/7/4/17!worldanyoneB TS mæu½./ mæ€/7/4/18/7/4/18!worldanyoneB YS mæu½/0 m怑/7/4/19/7/4/19!worldanyoneBÈž &M mæu½01 m怒/7/5/7/5!worldanyoneBÿÌ ìQ mæu½12 m怔/7/5/0/7/5/0!worldanyoneB òQ mæu½23 m怖/7/5/1/7/5/1!worldanyoneB ÷Q mæu½34 m怗/7/5/2/7/5/2!worldanyoneB üQ mæu½45 m怘/7/5/3/7/5/3!worldanyoneB Q mæu½56 m怙/7/5/4/7/5/4!worldanyoneB Q mæu½67 m怚/7/5/5/7/5/5!worldanyoneBG Q mæu½78 m怜/7/5/6/7/5/6!worldanyoneBG Q mæu½89 mæ€/7/5/7/7/5/7!worldanyoneBG Q mæu½9: m怞/7/5/8/7/5/8!worldanyoneBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/log.420100644 0000000 0000000 00000000270 15051152474 032377 0ustar00rootroot0000000 0000000 ZKLG j €aö ý ˆaúÆWuÿÿÿõBïj -$€aúÅÝëˆaúƃýÿÿÿöu0BùÌ?=€aúÅÝëˆaúƪÎ/andor3 hellobelloBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/log.63b0100644 0000000 0000000 00000137560 15051152474 032561 0ustar00rootroot0000000 0000000 ZKLGe 8Q mæu½:; m怼/7/5/9/7/5/9!worldanyoneB,e ”S mæu½;< mæ€Ä/7/5/10/7/5/10!worldanyoneB-¤ šS mæu½<= mæ€Æ/7/5/11/7/5/11!worldanyoneB/ ¡S mæu½=> mæ€É/7/5/12/7/5/12!worldanyoneB0Ê ©S mæu½>? mæ€Í/7/5/13/7/5/13!worldanyoneB2A °S mæu½?@ mæ€Ð/7/5/14/7/5/14!worldanyoneB3€ ¶S mæu½@A mæ€Ò/7/5/15/7/5/15!worldanyoneB4¿ ¼S mæu½AB mæ€Ô/7/5/16/7/5/16!worldanyoneB5þ ÂS mæu½BC mæ€Ö/7/5/17/7/5/17!worldanyoneB7 ÇS mæu½CD mæ€×/7/5/18/7/5/18!worldanyoneB8 ÌS mæu½DE mæ€Ø/7/5/19/7/5/19!worldanyoneBá šM mæu½EF mæ€Ú/7/6/7/6!worldanyoneB) `Q mæu½FG mæ€Ü/7/6/0/7/6/0!worldanyoneB iQ mæu½GH mæ€á/7/6/1/7/6/1!worldanyoneB nQ mæu½HI mæ€â/7/6/2/7/6/2!worldanyoneB sQ mæu½IJ mæ€ã/7/6/3/7/6/3!worldanyoneBïG –Q mæu½JK mæ/7/6/4/7/6/4!worldanyoneBð} œQ mæu½KL mæ/7/6/5/7/6/5!worldanyoneBò‹ ¦Q mæu½LM mæ /7/6/6/7/6/6!worldanyoneBó÷ ­Q mæu½MN mæ /7/6/7/7/6/7!worldanyoneBõ- ³Q mæu½NO mæ/7/6/8/7/6/8!worldanyoneBö™ ºQ mæu½OP mæ/7/6/9/7/6/9!worldanyoneB# S mæu½PQ mæ/7/6/10/7/6/10!worldanyoneBb S mæu½QR mæ/7/6/11/7/6/11!worldanyoneBi S mæu½RS mæ/7/6/12/7/6/12!worldanyoneBp S mæu½ST mæ/7/6/13/7/6/13!worldanyoneBç &S mæu½TU mæ/7/6/14/7/6/14!worldanyoneBî +S mæu½UV mæ/7/6/15/7/6/15!worldanyoneBõ 0S mæu½VW mæ/7/6/16/7/6/16!worldanyoneBÄ 4S mæu½WX mæ/7/6/17/7/6/17!worldanyoneB :S mæu½XY mæ/7/6/18/7/6/18!worldanyoneBB @S mæu½YZ mæ/7/6/19/7/6/19!worldanyoneBÇm M mæu½Z[ mæ /7/7/7/7!worldanyoneBþ3 ÓQ mæu½[\ mæ"/7/7/0/7/7/0!worldanyoneBÿ3 ØQ mæu½\] mæ#/7/7/1/7/7/1!worldanyoneBB ÝQ mæu½]^ mæ$/7/7/2/7/7/2!worldanyoneB áQ mæu½^_ mæ$/7/7/3/7/7/3!worldanyoneB æQ mæu½_` mæ%/7/7/4/7/7/4!worldanyoneBx íQ mæu½`a mæ(/7/7/5/7/7/5!worldanyoneBx òQ mæu½ab mæ)/7/7/6/7/7/6!worldanyoneBx ÷Q mæu½bc mæ*/7/7/7/7/7/7!worldanyoneBä þQ mæu½cd mæ-/7/7/8/7/7/8!worldanyoneB Q mæu½de mæ//7/7/9/7/7/9!worldanyoneB"ð XS mæu½ef mæ//7/7/10/7/7/10!worldanyoneB%ï fS mæu½fg mæ9/7/7/11/7/7/11!worldanyoneB&ö kS mæu½gh mæ:/7/7/12/7/7/12!worldanyoneB'ý pS mæu½hi mæ;/7/7/13/7/7/13!worldanyoneB) uS mæu½ij mæ</7/7/14/7/7/14!worldanyoneB)Ó yS mæu½jk mæ</7/7/15/7/7/15!worldanyoneB*Ú ~S mæu½kl mæ=/7/7/16/7/7/16!worldanyoneB+á ƒS mæu½lm mæ>/7/7/17/7/7/17!worldanyoneB,è ˆS mæu½mn mæ?/7/7/18/7/7/18!worldanyoneB-ï S mæu½no mæ@/7/7/19/7/7/19!worldanyoneBØO ZM mæu½op mæA/7/8/7/8!worldanyoneB Q mæu½pq mæB/7/8/0/7/8/0!worldanyoneB $Q mæu½qr mæC/7/8/1/7/8/1!worldanyoneB )Q mæu½rs mæD/7/8/2/7/8/2!worldanyoneB .Q mæu½st mæE/7/8/3/7/8/3!worldanyoneB 3Q mæu½tu mæF/7/8/4/7/8/4!worldanyoneB 8Q mæu½uv mæG/7/8/5/7/8/5!worldanyoneB =Q mæu½vw mæH/7/8/6/7/8/6!worldanyoneB BQ mæu½wx mæI/7/8/7/7/8/7!worldanyoneBê FQ mæu½xy mæI/7/8/8/7/8/8!worldanyoneBê KQ mæu½yz mæJ/7/8/9/7/8/9!worldanyoneB4…  S mæu½z{ mæK/7/8/10/7/8/10!worldanyoneB5Œ ¥S mæu½{| mæL/7/8/11/7/8/11!worldanyoneB6“ ªS mæu½|} mæM/7/8/12/7/8/12!worldanyoneB7š ¯S mæu½}~ mæN/7/8/13/7/8/13!worldanyoneB8¡ ´S mæu½~ mæO/7/8/14/7/8/14!worldanyoneB9¨ ¹S mæu½€ mæP/7/8/15/7/8/15!worldanyoneB:¯ ¾S mæu½€ mæQ/7/8/16/7/8/16!worldanyoneB;¶ ÃS mæu½‚ mæR/7/8/17/7/8/17!worldanyoneB<½ ÈS mæu½‚ƒ mæS/7/8/18/7/8/18!worldanyoneB=Ä ÍS mæu½ƒ„ mæT/7/8/19/7/8/19!worldanyoneBæÙ ›M mæu½„… mæV/7/9/7/9!worldanyoneBv _Q mæu½…† mæV/7/9/0/7/9/0!worldanyoneB v dQ mæu½†‡ mæW/7/9/1/7/9/1!worldanyoneB!v iQ mæu½‡ˆ mæX/7/9/2/7/9/2!worldanyoneB"v nQ mæu½ˆ‰ mæY/7/9/3/7/9/3!worldanyoneB#@ rQ mæu½‰Š mæY/7/9/4/7/9/4!worldanyoneB% {Q mæu½Š‹ mæ^/7/9/5/7/9/5!worldanyoneB& €Q mæu½‹Œ mæ_/7/9/6/7/9/6!worldanyoneB'N †Q mæu½Œ mæa/7/9/7/7/9/7!worldanyoneB(„ ŒQ mæu½Ž mæc/7/9/8/7/9/8!worldanyoneB)„ ‘Q mæu½Ž mæd/7/9/9/7/9/9!worldanyoneBEª æS mæu½ mæe/7/9/10/7/9/10!worldanyoneBFé ìS mæu½‘ mæg/7/9/11/7/9/11!worldanyoneBH` óS mæu½‘’ mæj/7/9/12/7/9/12!worldanyoneBIg øS mæu½’“ mæk/7/9/13/7/9/13!worldanyoneBJ6 üS mæu½“” mæk/7/9/14/7/9/14!worldanyoneBK=S mæu½”• mæl/7/9/15/7/9/15!worldanyoneBLDS mæu½•– mæm/7/9/16/7/9/16!worldanyoneBMK S mæu½–— mæn/7/9/17/7/9/17!worldanyoneBNRS mæu½—˜ mæo/7/9/18/7/9/18!worldanyoneBO!S mæu½˜™ mæo/7/9/19/7/9/19!worldanyoneB¾Q I mæu½™š mæq/8/8!worldanyoneBôÝ ÓM mæu½š› mær/8/0/8/0!worldanyoneB.Ö ™Q mæu½›œ mæt/8/0/0/8/0/0!worldanyoneB/Ö žQ mæu½œ mæu/8/0/1/8/0/1!worldanyoneB0  ¢Q mæu½ž mæu/8/0/2/8/0/2!worldanyoneB1  §Q mæu½žŸ mæv/8/0/3/8/0/3!worldanyoneB2  ¬Q mæu½Ÿ  mæw/8/0/4/8/0/4!worldanyoneB3j °Q mæu½ ¡ mæw/8/0/5/8/0/5!worldanyoneB4j µQ mæu½¡¢ mæx/8/0/6/8/0/6!worldanyoneB5j ºQ mæu½¢£ mæy/8/0/7/8/0/7!worldanyoneB64 ¾Q mæu½£¤ mæy/8/0/8/8/0/8!worldanyoneB7j ÄQ mæu½¤¥ mæ{/8/0/9/8/0/9!worldanyoneBSþS mæu½¥¦ mæ|/8/0/10/8/0/10!worldanyoneBTÍS mæu½¦§ mæ|/8/0/11/8/0/11!worldanyoneBV #S mæu½§¨ mæ~/8/0/12/8/0/12!worldanyoneBW(S mæu½¨© mæ/8/0/13/8/0/13!worldanyoneBX-S mæu½©ª mæ€/8/0/14/8/0/14!worldanyoneBY‘4S mæu½ª« mæƒ/8/0/15/8/0/15!worldanyoneBZ˜9S mæu½«¬ mæ„/8/0/16/8/0/16!worldanyoneB[Ÿ>S mæu½¬­ mæ…/8/0/17/8/0/17!worldanyoneB\nBS mæu½­® mæ…/8/0/18/8/0/18!worldanyoneB]­HS mæu½®¯ mæ‡/8/0/19/8/0/19!worldanyoneB¨ M mæu½¯° mæˆ/8/1/8/1!worldanyoneB>b ÚQ mæu½°± mæ‰/8/1/0/8/1/0!worldanyoneB?, ÞQ mæu½±² mæ‰/8/1/1/8/1/1!worldanyoneB@, ãQ mæu½²³ mæŠ/8/1/2/8/1/2!worldanyoneBA, èQ mæu½³´ mæ‹/8/1/3/8/1/3!worldanyoneBBb îQ mæu½´µ mæ/8/1/4/8/1/4!worldanyoneBCb óQ mæu½µ¶ mæŽ/8/1/5/8/1/5!worldanyoneBDb øQ mæu½¶· mæ/8/1/6/8/1/6!worldanyoneBEb ýQ mæu½·¸ mæ/8/1/7/8/1/7!worldanyoneBF˜Q mæu½¸¹ mæ’/8/1/8/8/1/8!worldanyoneBGbQ mæu½¹º mæ’/8/1/9/8/1/9!worldanyoneBd{\S mæu½º» mæ“/8/1/10/8/1/10!worldanyoneBeºbS mæu½»¼ mæ•/8/1/11/8/1/11!worldanyoneBgijS mæu½¼½ mæ™/8/1/12/8/1/12!worldanyoneBh8nS mæu½½¾ mæ™/8/1/13/8/1/13!worldanyoneBi?sS mæu½¾¿ mæš/8/1/14/8/1/14!worldanyoneBjFxS mæu½¿À mæ›/8/1/15/8/1/15!worldanyoneBkM}S mæu½ÀÁ mæœ/8/1/16/8/1/16!worldanyoneBlT‚S mæu½Á mæ/8/1/17/8/1/17!worldanyoneBm[‡S mæu½Âà mæž/8/1/18/8/1/18!worldanyoneBn*‹S mæu½ÃÄ mæž/8/1/19/8/1/19!worldanyoneB– XM mæu½ÄÅ mæŸ/8/2/8/2!worldanyoneBNZQ mæu½ÅÆ mæ /8/2/0/8/2/0!worldanyoneBOZ"Q mæu½ÆÇ mæ¡/8/2/1/8/2/1!worldanyoneBPZ'Q mæu½ÇÈ mæ¢/8/2/2/8/2/2!worldanyoneBQZ,Q mæu½ÈÉ mæ£/8/2/3/8/2/3!worldanyoneBU>Q mæu½ÉÊ mæ±/8/2/4/8/2/4!worldanyoneB`8sQ mæu½ÊË mæâ/8/2/5/8/2/5!worldanyoneBanyQ mæu½ËÌ mæä/8/2/6/8/2/6!worldanyoneBb8}Q mæu½ÌÍ mæä/8/2/7/8/2/7!worldanyoneBc8‚Q mæu½ÍÎ mæå/8/2/8/8/2/8!worldanyoneBd8‡Q mæu½ÎÏ mææ/8/2/9/8/2/9!worldanyoneB‚ÛS mæu½ÏÐ mææ/8/2/10/8/2/10!worldanyoneBƒàS mæu½ÐÑ mæç/8/2/11/8/2/11!worldanyoneBƒîäS mæu½ÑÒ mæç/8/2/12/8/2/12!worldanyoneB„õéS mæu½ÒÓ mæè/8/2/13/8/2/13!worldanyoneB…üîS mæu½ÓÔ mæé/8/2/14/8/2/14!worldanyoneB†ËòS mæu½ÔÕ mæé/8/2/15/8/2/15!worldanyoneB‡Ò÷S mæu½ÕÖ mæê/8/2/16/8/2/16!worldanyoneBˆ¡ûS mæu½Ö× mæê/8/2/17/8/2/17!worldanyoneB‰¨S mæu½×Ø mæë/8/2/18/8/2/18!worldanyoneBŠwS mæu½ØÙ mæë/8/2/19/8/2/19!worldanyoneB,B ÒM mæu½ÙÚ mæí/8/3/8/3!worldanyoneBiì—Q mæu½ÚÛ mæî/8/3/0/8/3/0!worldanyoneBjìœQ mæu½ÛÜ mæï/8/3/1/8/3/1!worldanyoneBkì¡Q mæu½ÜÝ mæð/8/3/2/8/3/2!worldanyoneBlì¦Q mæu½ÝÞ mæñ/8/3/3/8/3/3!worldanyoneBm¶ªQ mæu½Þß mæñ/8/3/4/8/3/4!worldanyoneBn¶¯Q mæu½ßà mæò/8/3/5/8/3/5!worldanyoneBo€³Q mæu½àá mæò/8/3/6/8/3/6!worldanyoneBp€¸Q mæu½áâ mæó/8/3/7/8/3/7!worldanyoneBqJ¼Q mæu½âã mæó/8/3/8/8/3/8!worldanyoneBrJÁQ mæu½ãä mæô/8/3/9/8/3/9!worldanyoneBÕS mæu½äå mæõ/8/3/10/8/3/10!worldanyoneB‘¤S mæu½åæ mæõ/8/3/11/8/3/11!worldanyoneB’«S mæu½æç mæö/8/3/12/8/3/12!worldanyoneB”"&S mæu½çè mæù/8/3/13/8/3/13!worldanyoneB•)+S mæu½èé mæú/8/3/14/8/3/14!worldanyoneB•ø/S mæu½éê mæú/8/3/15/8/3/15!worldanyoneB–ÿ4S mæu½êë mæû/8/3/16/8/3/16!worldanyoneB—Î8S mæu½ëì mæû/8/3/17/8/3/17!worldanyoneB™ >S mæu½ìí mæý/8/3/18/8/3/18!worldanyoneB™ÜBS mæu½íî mæý/8/3/19/8/3/19!worldanyoneB:M mæu½îï mæþ/8/4/8/4!worldanyoneBx ÔQ mæu½ïð mæÿ/8/4/0/8/4/0!worldanyoneByjØQ mæu½ðñ mæÿ/8/4/1/8/4/1!worldanyoneBD¡ ÞQ mæu½ñò mæ‚/8/4/2/8/4/2!worldanyoneBE¡ ãQ mæu½òó mæ‚/8/4/3/8/4/3!worldanyoneBFk çQ mæu½óô mæ‚/8/4/4/8/4/4!worldanyoneBGk ìQ mæu½ôõ mæ‚/8/4/5/8/4/5!worldanyoneBHk ñQ mæu½õö mæ‚/8/4/6/8/4/6!worldanyoneBI5 õQ mæu½ö÷ mæ‚/8/4/7/8/4/7!worldanyoneBJ5 úQ mæu½÷ø mæ‚/8/4/8/8/4/8!worldanyoneBK5 ÿQ mæu½øù mæ‚/8/4/9/8/4/9!worldanyoneBh;TS mæu½ùú mæ‚/8/4/10/8/4/10!worldanyoneBiBYS mæu½úû mæ‚/8/4/11/8/4/11!worldanyoneBj]S mæu½ûü mæ‚/8/4/12/8/4/12!worldanyoneBkbS mæu½üý mæ‚/8/4/13/8/4/13!worldanyoneBkçfS mæu½ýþ mæ‚/8/4/14/8/4/14!worldanyoneBlîkS mæu½þÿ mæ‚ /8/4/15/8/4/15!worldanyoneB-þ pS mæu½ÿ mæ‚ /8/4/16/8/4/16!worldanyoneBç? vS mæu½ mæ‚ /8/4/17/8/4/17!worldanyoneBèF {S mæu½ mæ‚ /8/4/18/8/4/18!worldanyoneBé S mæu½ mæ‚ /8/4/19/8/4/19!worldanyoneBš MM mæu½ mæ‚ /8/5/8/5!worldanyoneBÍ– Q mæu½ mæ‚/8/5/0/8/5/0!worldanyoneBΖ Q mæu½ mæ‚/8/5/1/8/5/1!worldanyoneBÏ` Q mæu½ mæ‚/8/5/2/8/5/2!worldanyoneBÐ` Q mæu½ mæ‚/8/5/3/8/5/3!worldanyoneBÖ ;Q mæu½  mæ‚'/8/5/4/8/5/4!worldanyoneB× @Q mæu½   mæ‚(/8/5/5/8/5/5!worldanyoneBØ EQ mæu½   mæ‚)/8/5/6/8/5/6!worldanyoneBÙ JQ mæu½   mæ‚*/8/5/7/8/5/7!worldanyoneBÚ OQ mæu½   mæ‚+/8/5/8/8/5/8!worldanyoneBÚÎ SQ mæu½  mæ‚+/8/5/9/8/5/9!worldanyoneBô{ ¨S mæu½ mæ‚,/8/5/10/8/5/10!worldanyoneBõ‚ ­S mæu½ mæ‚-/8/5/11/8/5/11!worldanyoneBö‰ ²S mæu½ mæ‚./8/5/12/8/5/12!worldanyoneB÷ ·S mæu½ mæ‚//8/5/13/8/5/13!worldanyoneBø— ¼S mæu½ mæ‚0/8/5/14/8/5/14!worldanyoneB ãS mæu½ mæ‚S/8/5/15/8/5/15!worldanyoneB$ èS mæu½ mæ‚T/8/5/16/8/5/16!worldanyoneBó ìS mæu½ mæ‚T/8/5/17/8/5/17!worldanyoneBú ñS mæu½ mæ‚U/8/5/18/8/5/18!worldanyoneBÉ õS mæu½ mæ‚U/8/5/19/8/5/19!worldanyoneB²¶ ÂM mæu½ mæ‚V/8/6/8/6!worldanyoneBè ‡Q mæu½ mæ‚W/8/6/0/8/6/0!worldanyoneBé ŒQ mæu½ mæ‚X/8/6/1/8/6/1!worldanyoneBéä Q mæu½ mæ‚X/8/6/2/8/6/2!worldanyoneBêä •Q mæu½ mæ‚Y/8/6/3/8/6/3!worldanyoneBëä šQ mæu½ mæ‚Z/8/6/4/8/6/4!worldanyoneBìä ŸQ mæu½ mæ‚[/8/6/5/8/6/5!worldanyoneBíä ¤Q mæu½  mæ‚\/8/6/6/8/6/6!worldanyoneBî® ¨Q mæu½ ! mæ‚\/8/6/7/8/6/7!worldanyoneBï® ­Q mæu½!" mæ‚]/8/6/8/8/6/8!worldanyoneBðx ±Q mæu½"# mæ‚]/8/6/9/8/6/9!worldanyoneB ï S mæu½#$ mæ‚^/8/6/10/8/6/10!worldanyoneB ö S mæu½$% mæ‚_/8/6/11/8/6/11!worldanyoneB ý S mæu½%& mæ‚`/8/6/12/8/6/12!worldanyoneB S mæu½&' mæ‚a/8/6/13/8/6/13!worldanyoneBÓ S mæu½'( mæ‚a/8/6/14/8/6/14!worldanyoneBÚ S mæu½() mæ‚b/8/6/15/8/6/15!worldanyoneBá #S mæu½)* mæ‚c/8/6/16/8/6/16!worldanyoneBX *S mæu½*+ mæ‚f/8/6/17/8/6/17!worldanyoneB_ /S mæu½+, mæ‚g/8/6/18/8/6/18!worldanyoneB. 3S mæu½,- mæ‚g/8/6/19/8/6/19!worldanyoneBÀª M mæu½-. mæ‚h/8/7/8/7!worldanyoneB÷ ÅQ mæu½./ mæ‚i/8/7/0/8/7/0!worldanyoneB÷Î ÉQ mæu½/0 mæ‚i/8/7/1/8/7/1!worldanyoneBøÎ ÎQ mæu½01 mæ‚j/8/7/2/8/7/2!worldanyoneBù˜ ÒQ mæu½12 mæ‚j/8/7/3/8/7/3!worldanyoneBú˜ ×Q mæu½23 mæ‚k/8/7/4/8/7/4!worldanyoneBûÎ ÝQ mæu½34 mæ‚m/8/7/5/8/7/5!worldanyoneBüÎ âQ mæu½45 mæ‚n/8/7/6/8/7/6!worldanyoneBýÎ çQ mæu½56 mæ‚o/8/7/7/8/7/7!worldanyoneBþ˜ ëQ mæu½67 mæ‚o/8/7/8/8/7/8!worldanyoneBÿ˜ ðQ mæu½78 mæ‚p/8/7/9/8/7/9!worldanyoneBT DS mæu½89 mæ‚p/8/7/10/8/7/10!worldanyoneB[ IS mæu½9: mæ‚q/8/7/11/8/7/11!worldanyoneB* MS mæu½:; mæ‚q/8/7/12/8/7/12!worldanyoneB1 RS mæu½;< mæ‚r/8/7/13/8/7/13!worldanyoneB VS mæu½<= mæ‚r/8/7/14/8/7/14!worldanyoneB [S mæu½=> mæ‚s/8/7/15/8/7/15!worldanyoneB F aS mæu½>? mæ‚u/8/7/16/8/7/16!worldanyoneB!M fS mæu½?@ mæ‚v/8/7/17/8/7/17!worldanyoneB" jS mæu½@A mæ‚v/8/7/18/8/7/18!worldanyoneB## oS mæu½AB mæ‚w/8/7/19/8/7/19!worldanyoneBÎ: <M mæu½BC mæ‚x/8/8/8/8!worldanyoneB‘ Q mæu½CD mæ‚y/8/8/0/8/8/0!worldanyoneB‘ Q mæu½DE mæ‚z/8/8/1/8/8/1!worldanyoneB‘ Q mæu½EF mæ‚{/8/8/2/8/8/2!worldanyoneB[ Q mæu½FG mæ‚{/8/8/3/8/8/3!worldanyoneB Ç Q mæu½GH mæ‚~/8/8/4/8/8/4!worldanyoneB Ç Q mæu½HI mæ‚/8/8/5/8/8/5!worldanyoneB Ç Q mæu½IJ mæ‚€/8/8/6/8/8/6!worldanyoneB ‘ $Q mæu½JK mæ‚€/8/8/7/8/8/7!worldanyoneB ‘ )Q mæu½KL mæ‚/8/8/8/8/8/8!worldanyoneB[ -Q mæu½LM mæ‚/8/8/9/8/8/9!worldanyoneB)¹ ‚S mæu½MN mæ‚‚/8/8/10/8/8/10!worldanyoneB*À ‡S mæu½NO m悃/8/8/11/8/8/11!worldanyoneB+ ‹S mæu½OP m悃/8/8/12/8/8/12!worldanyoneB,– S mæu½PQ mæ‚„/8/8/13/8/8/13!worldanyoneB- •S mæu½QR mæ‚…/8/8/14/8/8/14!worldanyoneB.¤ šS mæu½RS m悆/8/8/15/8/8/15!worldanyoneB/s žS mæu½ST m悆/8/8/16/8/8/16!worldanyoneB0z £S mæu½TU m悇/8/8/17/8/8/17!worldanyoneB1I §S mæu½UV m悇/8/8/18/8/8/18!worldanyoneB2ˆ ­S mæu½VW m悉/8/8/19/8/8/19!worldanyoneBÜ. zM mæu½WX m悊/8/9/8/9!worldanyoneBE >Q mæu½XY m悊/8/9/0/8/9/0!worldanyoneBE CQ mæu½YZ mæ‚‹/8/9/1/8/9/1!worldanyoneBE HQ mæu½Z[ m悌/8/9/2/8/9/2!worldanyoneBE MQ mæu½[\ mæ‚/8/9/3/8/9/3!worldanyoneB QQ mæu½\] mæ‚/8/9/4/8/9/4!worldanyoneB VQ mæu½]^ m悎/8/9/5/8/9/5!worldanyoneB [Q mæu½^_ mæ‚/8/9/6/8/9/6!worldanyoneBÙ _Q mæu½_` mæ‚/8/9/7/8/9/7!worldanyoneBÙ dQ mæu½`a mæ‚/8/9/8/8/9/8!worldanyoneBÙ iQ mæu½ab mæ‚‘/8/9/9/8/9/9!worldanyoneB8v ½S mæu½bc mæ‚‘/8/9/10/8/9/10!worldanyoneB9} ÂS mæu½cd mæ‚’/8/9/11/8/9/11!worldanyoneB:„ ÇS mæu½de mæ‚“/8/9/12/8/9/12!worldanyoneB;‹ ÌS mæu½ef mæ‚”/8/9/13/8/9/13!worldanyoneBh ÚS mæu½hi mæ‚–/8/9/16/8/9/16!worldanyoneB?o ßS mæu½ij mæ‚—/8/9/17/8/9/17!worldanyoneB@v äS mæu½jk m悘/8/9/18/8/9/18!worldanyoneBA} éS mæu½kl mæ‚™/8/9/19/8/9/19!worldanyoneB²2 âI mæu½lm m悚/9/9!worldanyoneBçÚ ¦M mæu½mn m悚/9/0/9/0!worldanyoneB ç kQ mæu½no mæ‚›/9/0/0/9/0/0!worldanyoneB!ç pQ mæu½op m悜/9/0/1/9/0/1!worldanyoneB# vQ mæu½pq m悞/9/0/2/9/0/2!worldanyoneB#ç zQ mæu½qr m悞/9/0/3/9/0/3!worldanyoneB$ç Q mæu½rs m悟/9/0/4/9/0/4!worldanyoneB%ç „Q mæu½st mæ‚ /9/0/5/9/0/5!worldanyoneB&ç ‰Q mæu½tu mæ‚¡/9/0/6/9/0/6!worldanyoneB'± Q mæu½uv mæ‚¡/9/0/7/9/0/7!worldanyoneB(± ’Q mæu½vw mæ‚¢/9/0/8/9/0/8!worldanyoneB){ –Q mæu½wx mæ‚¢/9/0/9/9/0/9!worldanyoneBE² ëS mæu½xy mæ‚£/9/0/10/9/0/10!worldanyoneBF¹ ðS mæu½yz m悤/9/0/11/9/0/11!worldanyoneBGˆ ôS mæu½z{ m悤/9/0/12/9/0/12!worldanyoneBH ùS mæu½{| mæ‚¥/9/0/13/9/0/13!worldanyoneBI^ ýS mæu½|} mæ‚¥/9/0/14/9/0/14!worldanyoneBJS mæu½}~ mæ‚§/9/0/15/9/0/15!worldanyoneBK¤S mæu½~ m您/9/0/16/9/0/16!worldanyoneBL« S mæu½€ mæ‚©/9/0/17/9/0/17!worldanyoneBMzS mæu½€ mæ‚©/9/0/18/9/0/18!worldanyoneBNS mæu½‚ m悪/9/0/19/9/0/19!worldanyoneBõj âM mæu½‚ƒ m悪/9/1/9/1!worldanyoneB/e §Q mæu½ƒ„ mæ‚«/9/1/0/9/1/0!worldanyoneB0› ­Q mæu½„… mæ‚­/9/1/1/9/1/1!worldanyoneB2ß ¸Q mæu½…† mæ‚´/9/1/2/9/1/2!worldanyoneB3ß ½Q mæu½†‡ m悵/9/1/3/9/1/3!worldanyoneB4ß ÂQ mæu½‡ˆ mæ‚¶/9/1/4/9/1/4!worldanyoneB5© ÆQ mæu½ˆ‰ mæ‚¶/9/1/5/9/1/5!worldanyoneB6© ËQ mæu½‰Š mæ‚·/9/1/6/9/1/6!worldanyoneB7© ÐQ mæu½Š‹ m悸/9/1/7/9/1/7!worldanyoneB8s ÔQ mæu½‹Œ m悸/9/1/8/9/1/8!worldanyoneB9s ÙQ mæu½Œ m悹/9/1/9/9/1/9!worldanyoneBV/.S mæu½Ž m悺/9/1/10/9/1/10!worldanyoneBW63S mæu½Ž mæ‚»/9/1/11/9/1/11!worldanyoneBX7S mæu½ mæ‚»/9/1/12/9/1/12!worldanyoneBY <S mæu½‘ m悼/9/1/13/9/1/13!worldanyoneBZAS mæu½‘’ m悽/9/1/14/9/1/14!worldanyoneB[FS mæu½’“ m悾/9/1/15/9/1/15!worldanyoneB[éJS mæu½“” m悾/9/1/16/9/1/16!worldanyoneB]`QS mæu½”• mæ‚Á/9/1/17/9/1/17!worldanyoneB^gVS mæu½•– mæ‚Â/9/1/18/9/1/18!worldanyoneB_n[S mæu½–— mæ‚Ã/9/1/19/9/1/19!worldanyoneBË 'M mæu½—˜ mæ‚Ã/9/2/9/2!worldanyoneB?É ìQ mæu½˜™ mæ‚Ä/9/2/0/9/2/0!worldanyoneB@É ñQ mæu½™š mæ‚Å/9/2/1/9/2/1!worldanyoneBAÉ öQ mæu½š› mæ‚Æ/9/2/2/9/2/2!worldanyoneBB“ úQ mæu½›œ mæ‚Æ/9/2/3/9/2/3!worldanyoneBC“ ÿQ mæu½œ mæ‚Ç/9/2/4/9/2/4!worldanyoneBD“Q mæu½ž mæ‚È/9/2/5/9/2/5!worldanyoneBE]Q mæu½žŸ mæ‚È/9/2/6/9/2/6!worldanyoneBF] Q mæu½Ÿ  mæ‚É/9/2/7/9/2/7!worldanyoneBG]Q mæu½ ¡ mæ‚Ê/9/2/8/9/2/8!worldanyoneBH'Q mæu½¡¢ mæ‚Ê/9/2/9/9/2/9!worldanyoneBe\kS mæu½¢£ mæ‚Ë/9/2/10/9/2/10!worldanyoneBfcpS mæu½£¤ mæ‚Ì/9/2/11/9/2/11!worldanyoneBgjuS mæu½¤¥ mæ‚Í/9/2/12/9/2/12!worldanyoneBhqzS mæu½¥¦ mæ‚Î/9/2/13/9/2/13!worldanyoneBi@~S mæu½¦§ mæ‚Î/9/2/14/9/2/14!worldanyoneBjGƒS mæu½§¨ mæ‚Ï/9/2/15/9/2/15!worldanyoneBkNˆS mæu½¨© mæ‚Ð/9/2/16/9/2/16!worldanyoneBlUS mæu½©ª mæ‚Ñ/9/2/17/9/2/17!worldanyoneBm$‘S mæu½ª« mæ‚Ñ/9/2/18/9/2/18!worldanyoneBn+–S mæu½«¬ mæ‚Ò/9/2/19/9/2/19!worldanyoneB[ cM mæu½¬­ mæ‚Ó/9/3/9/3!worldanyoneBN'Q mæu½­® mæ‚Ó/9/3/0/9/3/0!worldanyoneBO,Q mæu½®¯ mæ‚Ô/9/3/1/9/3/1!worldanyoneBP1Q mæu½¯° mæ‚Õ/9/3/2/9/3/2!worldanyoneBQ6Q mæu½°± mæ‚Ö/9/3/3/9/3/3!worldanyoneBQÛ:Q mæu½±² mæ‚Ö/9/3/4/9/3/4!worldanyoneBRÛ?Q mæu½²³ mæ‚×/9/3/5/9/3/5!worldanyoneBSÛDQ mæu½³´ mæ‚Ø/9/3/6/9/3/6!worldanyoneBTÛIQ mæu½´µ mæ‚Ù/9/3/7/9/3/7!worldanyoneBU¥MQ mæu½µ¶ mæ‚Ù/9/3/8/9/3/8!worldanyoneBV¥RQ mæu½¶· mæ‚Ú/9/3/9/9/3/9!worldanyoneBtQ§S mæu½·¸ mæ‚Û/9/3/10/9/3/10!worldanyoneBuX¬S mæu½¸¹ mæ‚Ü/9/3/11/9/3/11!worldanyoneBv'°S mæu½¹º mæ‚Ü/9/3/12/9/3/12!worldanyoneBwf¶S mæu½º» mæ‚Þ/9/3/13/9/3/13!worldanyoneBx5ºS mæu½»¼ mæ‚Þ/9/3/14/9/3/14!worldanyoneBy<¿S mæu½¼½ mæ‚ß/9/3/15/9/3/15!worldanyoneBzCÄS mæu½½¾ mæ‚à/9/3/16/9/3/16!worldanyoneB{ÈS mæu½¾¿ mæ‚à/9/3/17/9/3/17!worldanyoneB|QÎS mæu½¿À mæ‚â/9/3/18/9/3/18!worldanyoneB} ÒS mæu½ÀÁ mæ‚â/9/3/19/9/3/19!worldanyoneBë ŸM mæu½Á mæ‚ã/9/4/9/4!worldanyoneB\cQ mæu½Âà mæ‚ã/9/4/0/9/4/0!worldanyoneB]hQ mæu½ÃÄ mæ‚ä/9/4/1/9/4/1!worldanyoneB_ÓsQ mæu½ÄÅ mæ‚ë/9/4/2/9/4/2!worldanyoneB`ÓxQ mæu½ÅÆ mæ‚ì/9/4/3/9/4/3!worldanyoneBb ~Q mæu½ÆÇ mæ‚î/9/4/4/9/4/4!worldanyoneBbÓ‚Q mæu½ÇÈ mæ‚î/9/4/5/9/4/5!worldanyoneBcÓ‡Q mæu½ÈÉ mæ‚ï/9/4/6/9/4/6!worldanyoneBd‹Q mæu½ÉÊ mæ‚ï/9/4/7/9/4/7!worldanyoneBeQ mæu½ÊË mæ‚ð/9/4/8/9/4/8!worldanyoneBfÓ–Q mæu½ËÌ mæ‚ò/9/4/9/9/4/9!worldanyoneB„ÎêS mæu½ÌÍ mæ‚ò/9/4/10/9/4/10!worldanyoneB…ÕïS mæu½ÍÎ mæ‚ó/9/4/11/9/4/11!worldanyoneB†¤óS mæu½ÎÏ mæ‚ó/9/4/12/9/4/12!worldanyoneB‡«øS mæu½ÏÐ mæ‚ô/9/4/13/9/4/13!worldanyoneBˆ²ýS mæu½ÐÑ mæ‚õ/9/4/14/9/4/14!worldanyoneB‰S mæu½ÑÒ mæ‚õ/9/4/15/9/4/15!worldanyoneBŠÀS mæu½ÒÓ mæ‚÷/9/4/16/9/4/16!worldanyoneB‹ S mæu½ÓÔ mæ‚÷/9/4/17/9/4/17!worldanyoneBŒ–S mæu½ÔÕ mæ‚ø/9/4/18/9/4/18!worldanyoneBS mæu½ÕÖ mæ‚ù/9/4/19/9/4/19!worldanyoneB.§ áM mæu½Ö× mæ‚ù/9/5/9/5!worldanyoneBl‡¦Q mæu½×Ø mæ‚ú/9/5/0/9/5/0!worldanyoneBm‡«Q mæu½ØÙ mæ‚û/9/5/1/9/5/1!worldanyoneBnQ¯Q mæu½ÙÚ mæ‚û/9/5/2/9/5/2!worldanyoneBoQ´Q mæu½ÚÛ mæ‚ü/9/5/3/9/5/3!worldanyoneBpQ¹Q mæu½ÛÜ mæ‚ý/9/5/4/9/5/4!worldanyoneBqQ¾Q mæu½ÜÝ mæ‚þ/9/5/5/9/5/5!worldanyoneBrÂQ mæu½ÝÞ mæ‚þ/9/5/6/9/5/6!worldanyoneBsÇQ mæu½Þß mæ‚ÿ/9/5/7/9/5/7!worldanyoneB>R ÍQ mæu½ßà mæƒ/9/5/8/9/5/8!worldanyoneB?R ÒQ mæu½àá mæƒ/9/5/9/9/5/9!worldanyoneB[Ä&S mæu½áâ mæƒ/9/5/10/9/5/10!worldanyoneB\Ë+S mæu½âã mæƒ/9/5/11/9/5/11!worldanyoneB]Ò0S mæu½ãä mæƒ/9/5/12/9/5/12!worldanyoneB^¡4S mæu½äå mæƒ/9/5/13/9/5/13!worldanyoneB_¨9S mæu½åæ mæƒ/9/5/14/9/5/14!worldanyoneB`¯>S mæu½æç mæƒ/9/5/15/9/5/15!worldanyoneBa¶CS mæu½çè mæƒ/9/5/16/9/5/16!worldanyoneBb½HS mæu½èé mæƒ/9/5/17/9/5/17!worldanyoneBcŒLS mæu½éê mæƒ/9/5/18/9/5/18!worldanyoneBd“QS mæu½êë mæƒ/9/5/19/9/5/19!worldanyoneB j M mæu½ëì mæƒ /9/6/9/6!worldanyoneBE< ãQ mæu½ìí mæƒ /9/6/0/9/6/0!worldanyoneBF< èQ mæu½íî mæƒ /9/6/1/9/6/1!worldanyoneBG< íQ mæu½îï mæƒ /9/6/2/9/6/2!worldanyoneBH ñQ mæu½ïð mæƒ /9/6/3/9/6/3!worldanyoneBI öQ mæu½ðñ mæƒ /9/6/4/9/6/4!worldanyoneBIÐ úQ mæu½ñò mæƒ /9/6/5/9/6/5!worldanyoneBJÐ ÿQ mæu½òó mæƒ/9/6/6/9/6/6!worldanyoneBKšQ mæu½óô mæƒ/9/6/7/9/6/7!worldanyoneBLšQ mæu½ôõ mæƒ/9/6/8/9/6/8!worldanyoneBMš Q mæu½õö mæƒ/9/6/9/9/6/9!worldanyoneBj¹bS mæu½ö÷ mæƒ/9/6/10/9/6/10!worldanyoneBkˆfS mæu½÷ø mæƒ/9/6/11/9/6/11!worldanyoneBlkS mæu½øù mæƒ/9/6/12/9/6/12!worldanyoneBm^oS mæu½ùú mæƒ/9/6/13/9/6/13!worldanyoneBnetS mæu½úû mæƒ/9/6/14/9/6/14!worldanyoneBolyS mæu½ûü mæƒ/9/6/15/9/6/15!worldanyoneBps~S mæu½üý mæƒ/9/6/16/9/6/16!worldanyoneBqB‚S mæu½ýþ mæƒ/9/6/17/9/6/17!worldanyoneBrI‡S mæu½þÿ mæƒ/9/6/18/9/6/18!worldanyoneB3‘ S mæu½ÿ mæƒ/9/6/19/9/6/19!worldanyoneBœ ZM mæu½ mæƒ/9/7/9/7!worldanyoneBÏÅ Q mæu½ mæƒ/9/7/0/9/7/0!worldanyoneBÐÅ $Q mæu½ mæƒ/9/7/1/9/7/1!worldanyoneBÓ /Q mæu½ mæƒ /9/7/2/9/7/2!worldanyoneBÔ 4Q mæu½ mæƒ!/9/7/3/9/7/3!worldanyoneBÕ 9Q mæu½ mæƒ"/9/7/4/9/7/4!worldanyoneBÕÓ =Q mæu½ mæƒ"/9/7/5/9/7/5!worldanyoneBÖÓ BQ mæu½ mæƒ#/9/7/6/9/7/6!worldanyoneBØ« KQ mæu½  mæƒ(/9/7/7/9/7/7!worldanyoneBÚ RQ mæu½   mæƒ+/9/7/8/9/7/8!worldanyoneBÚá VQ mæu½   mæƒ+/9/7/9/9/7/9!worldanyoneBô‘ «S mæu½   mæƒ,/9/7/10/9/7/10!worldanyoneBõ˜ °S mæu½   mæƒ-/9/7/11/9/7/11!worldanyoneBöŸ µS mæu½  mæƒ./9/7/12/9/7/12!worldanyoneB÷n ¹S mæu½ mæƒ./9/7/13/9/7/13!worldanyoneBøu ¾S mæu½ mæƒ//9/7/14/9/7/14!worldanyoneBùD ÂS mæu½ mæƒ//9/7/15/9/7/15!worldanyoneBúK ÇS mæu½ mæƒ0/9/7/16/9/7/16!worldanyoneBûŠ ÍS mæu½ mæƒ2/9/7/17/9/7/17!worldanyoneBüY ÑS mæu½ mæƒ2/9/7/18/9/7/18!worldanyoneBý` ÖS mæu½ mæƒ3/9/7/19/9/7/19!worldanyoneB«í ¢M mæu½ mæƒ3/9/8/9/8!worldanyoneBá hQ mæu½ mæƒ5/9/8/0/9/8/0!worldanyoneBâ mQ mæu½ mæƒ6/9/8/1/9/8/1!worldanyoneBâË qQ mæu½ mæƒ6/9/8/2/9/8/2!worldanyoneBãË vQ mæu½ mæƒ7/9/8/3/9/8/3!worldanyoneBäË {Q mæu½ mæƒ8/9/8/4/9/8/4!worldanyoneBåË €Q mæu½ mæƒ9/9/8/5/9/8/5!worldanyoneBæ• „Q mæu½ mæƒ9/9/8/6/9/8/6!worldanyoneBç• ‰Q mæu½ mæƒ:/9/8/7/9/8/7!worldanyoneBè_ Q mæu½ mæƒ:/9/8/8/9/8/8!worldanyoneBé_ ’Q mæu½  mæƒ;/9/8/9/9/8/9!worldanyoneBÍ èS mæu½ ! mæƒ=/9/8/10/9/8/10!worldanyoneBÔ íS mæu½!" mæƒ>/9/8/11/9/8/11!worldanyoneBÛ òS mæu½"# mæƒ?/9/8/12/9/8/12!worldanyoneBª öS mæu½#$ mæƒ?/9/8/13/9/8/13!worldanyoneB± ûS mæu½$% mæƒ@/9/8/14/9/8/14!worldanyoneB¸ S mæu½%& mæƒA/9/8/15/9/8/15!worldanyoneB ¿ S mæu½&' mæƒB/9/8/16/9/8/16!worldanyoneB Ž S mæu½'( mæƒB/9/8/17/9/8/17!worldanyoneB • S mæu½() mæƒC/9/8/18/9/8/18!worldanyoneB œ S mæu½)* mæƒD/9/8/19/9/8/19!worldanyoneB¹á àM mæu½*+ mæƒE/9/9/9/9!worldanyoneBï ¤Q mæu½+, mæƒE/9/9/0/9/9/0!worldanyoneBð ©Q mæu½,- mæƒF/9/9/1/9/9/1!worldanyoneBñ ®Q mæu½-. mæƒG/9/9/2/9/9/2!worldanyoneBòI ²Q mæu½./ mæƒG/9/9/3/9/9/3!worldanyoneBóI ·Q mæu½/0 mæƒH/9/9/4/9/9/4!worldanyoneBôI ¼Q mæu½01 mæƒI/9/9/5/9/9/5!worldanyoneBõ ÀQ mæu½12 mæƒI/9/9/6/9/9/6!worldanyoneBö ÅQ mæu½23 mæƒJ/9/9/7/9/9/7!worldanyoneB÷ ÊQ mæu½34 mæƒK/9/9/8/9/9/8!worldanyoneB÷Ý ÎQ mæu½45 mæƒK/9/9/9/9/9/9!worldanyoneBŠ #S mæu½56 mæƒL/9/9/10/9/9/10!worldanyoneB‘ (S mæu½67 mæƒM/9/9/11/9/9/11!worldanyoneB˜ -S mæu½78 mæƒN/9/9/12/9/9/12!worldanyoneBg 1S mæu½89 mæƒN/9/9/13/9/9/13!worldanyoneBn 6S mæu½9: mæƒO/9/9/14/9/9/14!worldanyoneBu ;S mæu½:; mæƒP/9/9/15/9/9/15!worldanyoneB| @S mæu½;< mæƒQ/9/9/16/9/9/16!worldanyoneBK DS mæu½<= mæƒQ/9/9/17/9/9/17!worldanyoneBR IS mæu½=> mæƒR/9/9/18/9/9/18!worldanyoneB! MS mæu½>? mæƒR/9/9/19/9/9/19!worldanyoneBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/snapshot.00100644 0000000 0000000 00000000450 15051152474 033367 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/«+Ò/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/snapshot.2720100644 0000000 0000000 00000156130 15051152474 033551 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿ mæu½N worldanyoneÿÿÿÿÿÿÿÿ¨/2/2!¨¨ mæz mæz f/2/3/2/3!èè mæz“ mæz“ü/2/3/19/2/3/19!üü mæzñ mæzñü/2/3/17/2/3/17!úú mæzë mæzëú/2/3/18/2/3/18!ûû mæzï mæzïû/2/3/15/2/3/15!øø mæzè mæzèø/2/3/16/2/3/16!ùù mæzê mæzêù/2/3/13/2/3/13!öö mæzæ mæzæö/2/3/14/2/3/14!÷÷ mæzç mæzç÷/2/3/11/2/3/11!ôô mæzÒ mæzÒô/2/3/12/2/3/12!õõ mæzÝ mæzÝõ/2/3/3/2/3/3!ìì mæz› mæz›ì/2/3/2/2/3/2!ëë mæzš mæzšë/2/3/1/2/3/1!êê mæz™ mæz™ê/2/3/10/2/3/10!óó mæzµ mæzµó/2/3/0/2/3/0!éé mæz– mæz–é/2/3/7/2/3/7!ðð mæz« mæz«ð/2/3/6/2/3/6!ïï mæzª mæzªï/2/3/5/2/3/5!îî mæz¢ mæz¢î/2/3/4/2/3/4!íí mæzž mæzží/2/3/9/2/3/9!òò mæz¯ mæz¯ò/2/3/8/2/3/8!ññ mæz¬ mæz¬ñ/2/2/2/2!ÓÓ mæzv mæzvç/2/2/19/2/2/19!çç mæz‘ mæz‘ç/2/2/17/2/2/17!åå mæz mæzå/2/2/18/2/2/18!ææ mæz‘ mæz‘æ/2/2/15/2/2/15!ãã mæzŽ mæzŽã/2/2/16/2/2/16!ää mæz mæzä/2/2/13/2/2/13!áá mæz‹ mæz‹á/2/2/14/2/2/14!ââ mæz mæzâ/2/2/11/2/2/11!ßß mæz‰ mæz‰ß/2/2/12/2/2/12!àà mæzŠ mæzŠà/2/2/3/2/2/3!×× mæz| mæz|×/2/2/2/2/2/2!ÖÖ mæzz mæzzÖ/2/2/1/2/2/1!ÕÕ mæzy mæzyÕ/2/2/10/2/2/10!ÞÞ mæzˆ mæzˆÞ/2/2/0/2/2/0!ÔÔ mæzw mæzwÔ/2/2/7/2/2/7!ÛÛ mæz‚ mæz‚Û/2/2/6/2/2/6!ÚÚ mæz€ mæz€Ú/2/2/5/2/2/5!ÙÙ mæz mæzÙ/2/2/4/2/2/4!ØØ mæz} mæz}Ø/2/2/9/2/2/9!ÝÝ mæz… mæz…Ý/2/2/8/2/2/8!ÜÜ mæzƒ mæzƒÜ/2/1/2/1!¾¾ mæzZ mæzZÒ/2/1/19/2/1/19!ÒÒ mæzu mæzuÒ/2/1/17/2/1/17!ÐÐ mæzs mæzsÐ/2/1/18/2/1/18!ÑÑ mæzt mæztÑ/2/1/15/2/1/15!ÎÎ mæzq mæzqÎ/2/1/16/2/1/16!ÏÏ mæzr mæzrÏ/2/1/13/2/1/13!ÌÌ mæzl mæzlÌ/2/1/14/2/1/14!ÍÍ mæzn mæznÍ/2/1/11/2/1/11!ÊÊ mæzh mæzhÊ/2/1/12/2/1/12!ËË mæzi mæziË/2/1/3/2/1/3! mæz_ mæz_Â/2/1/2/2/1/2!ÁÁ mæz^ mæz^Á/2/1/1/2/1/1!ÀÀ mæz] mæz]À/2/1/10/2/1/10!ÉÉ mæzg mæzgÉ/2/1/0/2/1/0!¿¿ mæz[ mæz[¿/2/1/7/2/1/7!ÆÆ mæze mæzeÆ/2/1/6/2/1/6!ÅÅ mæzc mæzcÅ/2/1/5/2/1/5!ÄÄ mæzb mæzbÄ/2/1/4/2/1/4!Ãà mæz` mæz`Ã/2/1/9/2/1/9!ÈÈ mæzg mæzgÈ/2/1/8/2/1/8!ÇÇ mæzf mæzfÇ/2/0/2/0!©© mæz mæz½/2/0/19/2/0/19!½½ mæzY mæzY½/2/0/17/2/0/17!»» mæzN mæzN»/2/0/18/2/0/18!¼¼ mæzW mæzW¼/2/0/15/2/0/15!¹¹ mæzE mæzE¹/2/0/16/2/0/16!ºº mæzJ mæzJº/2/0/13/2/0/13!·· mæzA mæzA·/2/0/14/2/0/14!¸¸ mæzC mæzC¸/2/0/11/2/0/11!µµ mæz? mæz?µ/2/0/12/2/0/12!¶¶ mæz@ mæz@¶/2/0/3/2/0/3!­­ mæz mæz­/2/0/2/2/0/2!¬¬ mæz mæz¬/2/0/1/2/0/1!«« mæz mæz«/2/0/10/2/0/10!´´ mæz> mæz>´/2/0/0/2/0/0!ªª mæz mæzª/2/0/7/2/0/7!±± mæz" mæz"±/2/0/6/2/0/6!°° mæz! mæz!°/2/0/5/2/0/5!¯¯ mæz  mæz ¯/2/0/4/2/0/4!®® mæz mæz®/2/0/9/2/0/9!³³ mæz< mæz<³/2/0/8/2/0/8!²² mæz: mæz:²/2/7/2/7!<< mæ{c mæ{cP/2/7/19/2/7/19!PP mæ{z mæ{zP/2/7/17/2/7/17!NN mæ{w mæ{wN/2/7/18/2/7/18!OO mæ{y mæ{yO/2/7/15/2/7/15!LL mæ{v mæ{vL/2/7/16/2/7/16!MM mæ{w mæ{wM/2/7/13/2/7/13!JJ mæ{t mæ{tJ/2/7/14/2/7/14!KK mæ{u mæ{uK/2/7/11/2/7/11!HH mæ{r mæ{rH/2/7/12/2/7/12!II mæ{s mæ{sI/2/7/3/2/7/3!@@ mæ{h mæ{h@/2/7/2/2/7/2!?? mæ{g mæ{g?/2/7/1/2/7/1!>> mæ{e mæ{e>/2/7/10/2/7/10!GG mæ{q mæ{qG/2/7/0/2/7/0!== mæ{d mæ{d=/2/7/7/2/7/7!DD mæ{m mæ{mD/2/7/6/2/7/6!CC mæ{k mæ{kC/2/7/5/2/7/5!BB mæ{k mæ{kB/2/7/4/2/7/4!AA mæ{j mæ{jA/2/7/9/2/7/9!FF mæ{o mæ{oF/2/7/8/2/7/8!EE mæ{n mæ{nE/2/6/2/6!'' mæ{; mæ{;;/2/6/19/2/6/19!;; mæ{b mæ{b;/2/6/17/2/6/17!99 mæ{^ mæ{^9/2/6/18/2/6/18!:: mæ{a mæ{a:/2/6/15/2/6/15!77 mæ{[ mæ{[7/2/6/16/2/6/16!88 mæ{] mæ{]8/2/6/13/2/6/13!55 mæ{Y mæ{Y5/2/6/14/2/6/14!66 mæ{Z mæ{Z6/2/6/11/2/6/11!33 mæ{T mæ{T3/2/6/12/2/6/12!44 mæ{X mæ{X4/2/6/3/2/6/3!++ mæ{C mæ{C+/2/6/2/2/6/2!** mæ{A mæ{A*/2/6/1/2/6/1!)) mæ{? mæ{?)/2/6/10/2/6/10!22 mæ{S mæ{S2/2/6/0/2/6/0!(( mæ{= mæ{=(/2/6/7/2/6/7!// mæ{H mæ{H//2/6/6/2/6/6!.. mæ{G mæ{G./2/6/5/2/6/5!-- mæ{E mæ{E-/2/6/4/2/6/4!,, mæ{D mæ{D,/2/6/9/2/6/9!11 mæ{Q mæ{Q1/2/6/8/2/6/8!00 mæ{O mæ{O0/2/5/2/5! mæ{ mæ{&/2/5/19/2/5/19!&& mæ{9 mæ{9&/2/5/17/2/5/17!$$ mæ{4 mæ{4$/2/5/18/2/5/18!%% mæ{7 mæ{7%/2/5/15/2/5/15!"" mæ{2 mæ{2"/2/5/16/2/5/16!## mæ{3 mæ{3#/2/5/13/2/5/13!   mæ{. mæ{. /2/5/14/2/5/14!!! mæ{0 mæ{0!/2/5/11/2/5/11! mæ{* mæ{*/2/5/12/2/5/12! mæ{, mæ{,/2/5/3/2/5/3! mæ{ mæ{/2/5/2/2/5/2! mæ{ mæ{/2/5/1/2/5/1! mæ{ mæ{/2/5/10/2/5/10! mæ{) mæ{)/2/5/0/2/5/0! mæ{ mæ{/2/5/7/2/5/7! mæ{& mæ{&/2/5/6/2/5/6! mæ{$ mæ{$/2/5/5/2/5/5! mæ{" mæ{"/2/5/4/2/5/4! mæ{ mæ{/2/5/9/2/5/9! mæ{' mæ{'/2/5/8/2/5/8! mæ{& mæ{&/2/4/2/4!ýý mæzò mæzò/2/4/19/2/4/19! mæ{ mæ{/2/4/17/2/4/17! mæ{ mæ{/2/4/18/2/4/18! mæ{ mæ{/2/4/15/2/4/15!   mæ{ mæ{ /2/4/16/2/4/16! mæ{ mæ{/2/4/13/2/4/13!   mæ{ mæ{ /2/4/14/2/4/14!   mæ{ mæ{ /2/4/11/2/4/11!   mæ{  mæ{  /2/4/12/2/4/12!   mæ{ mæ{ /2/4/3/2/4/3! mæzú mæzú/2/4/2/2/4/2! mæzø mæzø/2/4/1/2/4/1!ÿÿ mæzõ mæzõÿ/2/4/10/2/4/10! mæ{ mæ{/2/4/0/2/4/0!þþ mæzô mæzôþ/2/4/7/2/4/7! mæ{ mæ{/2/4/6/2/4/6! mæzÿ mæzÿ/2/4/5/2/4/5! mæzý mæzý/2/4/4/2/4/4! mæzû mæzû/2/4/9/2/4/9! mæ{ mæ{/2/4/8/2/4/8! mæ{ mæ{/2/9/2/9!ff mæ{• mæ{• r/2/9/3/2/9/3!jj mæ{š mæ{šj/2/9/2/2/9/2!ii mæ{™ mæ{™i/2/9/10/2/9/10!qq mæ{¡ mæ{¡q/2/9/1/2/9/1!hh mæ{— mæ{—h/2/9/0/2/9/0!gg mæ{– mæ{–g/2/9/7/2/9/7!nn mæ{ž mæ{žn/2/9/6/2/9/6!mm mæ{ mæ{m/2/9/5/2/9/5!ll mæ{› mæ{›l/2/9/4/2/9/4!kk mæ{š mæ{šk/2/9/9/2/9/9!pp mæ{  mæ{ p/2/9/8/2/9/8!oo mæ{Ÿ mæ{Ÿo/2/9/11/2/9/11!rr mæ{¢ mæ{¢r/2/8/2/8!QQ mæ{{ mæ{{e/2/8/19/2/8/19!ee mæ{“ mæ{“e/2/8/17/2/8/17!cc mæ{‘ mæ{‘c/2/8/18/2/8/18!dd mæ{’ mæ{’d/2/8/15/2/8/15!aa mæ{ mæ{a/2/8/16/2/8/16!bb mæ{ mæ{b/2/8/13/2/8/13!__ mæ{Œ mæ{Œ_/2/8/14/2/8/14!`` mæ{ mæ{`/2/8/11/2/8/11!]] mæ{‰ mæ{‰]/2/8/12/2/8/12!^^ mæ{Š mæ{Š^/2/8/3/2/8/3!UU mæ{€ mæ{€U/2/8/2/2/8/2!TT mæ{ mæ{T/2/8/1/2/8/1!SS mæ{~ mæ{~S/2/8/10/2/8/10!\\ mæ{ˆ mæ{ˆ\/2/8/0/2/8/0!RR mæ{} mæ{}R/2/8/7/2/8/7!YY mæ{… mæ{…Y/2/8/6/2/8/6!XX mæ{ƒ mæ{ƒX/2/8/5/2/8/5!WW mæ{‚ mæ{‚W/2/8/4/2/8/4!VV mæ{ mæ{V/2/8/9/2/8/9![[ mæ{‡ mæ{‡[/2/8/8/2/8/8!ZZ mæ{† mæ{†Z/1/1!ÕÕ mæx” mæx” “/1/3/1/3! mæy mæy)/1/3/19/1/3/19!)) mæy0 mæy0)/1/3/17/1/3/17!'' mæy+ mæy+'/1/3/18/1/3/18!(( mæy, mæy,(/1/3/15/1/3/15!%% mæy( mæy(%/1/3/16/1/3/16!&& mæy) mæy)&/1/3/13/1/3/13!## mæy& mæy&#/1/3/14/1/3/14!$$ mæy' mæy'$/1/3/11/1/3/11!!! mæy$ mæy$!/1/3/12/1/3/12!"" mæy% mæy%"/1/3/3/1/3/3! mæy mæy/1/3/2/1/3/2! mæy mæy/1/3/1/1/3/1! mæy mæy/1/3/10/1/3/10!   mæy! mæy! /1/3/0/1/3/0! mæy mæy/1/3/7/1/3/7! mæy mæy/1/3/6/1/3/6! mæy mæy/1/3/5/1/3/5! mæy mæy/1/3/4/1/3/4! mæy mæy/1/3/9/1/3/9! mæy  mæy /1/3/8/1/3/8! mæy mæy/1/2/1/2! mæxð mæxð/1/2/19/1/2/19! mæy mæy/1/2/17/1/2/17! mæy  mæy /1/2/18/1/2/18! mæy mæy/1/2/15/1/2/15! mæy  mæy /1/2/16/1/2/16! mæy  mæy /1/2/13/1/2/13! mæy  mæy /1/2/14/1/2/14! mæy  mæy /1/2/11/1/2/11!   mæy mæy /1/2/12/1/2/12!   mæy mæy /1/2/3/1/2/3! mæxø mæxø/1/2/2/1/2/2! mæx÷ mæx÷/1/2/1/1/2/1! mæxõ mæxõ/1/2/10/1/2/10!   mæy mæy /1/2/0/1/2/0! mæxò mæxò/1/2/7/1/2/7! mæy mæy/1/2/6/1/2/6! mæy mæy/1/2/5/1/2/5! mæxÿ mæxÿ/1/2/4/1/2/4! mæxþ mæxþ/1/2/9/1/2/9!   mæy mæy /1/2/8/1/2/8!   mæy mæy /1/1/1/1!ëë mæx¼ mæx¼ÿ/1/1/19/1/1/19!ÿÿ mæxè mæxèÿ/1/1/17/1/1/17!ýý mæxå mæxåý/1/1/18/1/1/18!þþ mæxæ mæxæþ/1/1/15/1/1/15!ûû mæxá mæxáû/1/1/16/1/1/16!üü mæxã mæxãü/1/1/13/1/1/13!ùù mæxÞ mæxÞù/1/1/14/1/1/14!úú mæxß mæxßú/1/1/11/1/1/11!÷÷ mæxÖ mæxÖ÷/1/1/12/1/1/12!øø mæxØ mæxØø/1/1/3/1/1/3!ïï mæxà mæxÃï/1/1/2/1/1/2!îî mæx mæxÂî/1/1/1/1/1/1!íí mæx¿ mæx¿í/1/1/10/1/1/10!öö mæxÓ mæxÓö/1/1/0/1/1/0!ìì mæx¾ mæx¾ì/1/1/7/1/1/7!óó mæxÎ mæxÎó/1/1/6/1/1/6!òò mæxÇ mæxÇò/1/1/5/1/1/5!ññ mæxÅ mæxÅñ/1/1/4/1/1/4!ðð mæxÅ mæxÅð/1/1/9/1/1/9!õõ mæxÑ mæxÑõ/1/1/8/1/1/8!ôô mæxÏ mæxÏô/1/0/1/0!ÖÖ mæx• mæx•ê/1/0/19/1/0/19!êê mæxº mæxºê/1/0/17/1/0/17!èè mæx¶ mæx¶è/1/0/18/1/0/18!éé mæx¸ mæx¸é/1/0/15/1/0/15!ææ mæx² mæx²æ/1/0/16/1/0/16!çç mæxµ mæxµç/1/0/13/1/0/13!ää mæx¯ mæx¯ä/1/0/14/1/0/14!åå mæx± mæx±å/1/0/11/1/0/11!ââ mæx­ mæx­â/1/0/12/1/0/12!ãã mæx® mæx®ã/1/0/3/1/0/3!ÚÚ mæx mæxÚ/1/0/2/1/0/2!ÙÙ mæxš mæxšÙ/1/0/1/1/0/1!ØØ mæx— mæx—Ø/1/0/10/1/0/10!áá mæx¬ mæx¬á/1/0/0/1/0/0!×× mæx– mæx–×/1/0/7/1/0/7!ÞÞ mæx¦ mæx¦Þ/1/0/6/1/0/6!ÝÝ mæx¤ mæx¤Ý/1/0/5/1/0/5!ÜÜ mæx¢ mæx¢Ü/1/0/4/1/0/4!ÛÛ mæxŸ mæxŸÛ/1/0/9/1/0/9!àà mæxª mæxªà/1/0/8/1/0/8!ßß mæx© mæx©ß/1/7/1/7!ii mæy§ mæy§}/1/7/19/1/7/19!}} mæyÚ mæyÚ}/1/7/17/1/7/17!{{ mæyÓ mæyÓ{/1/7/18/1/7/18!|| mæyÕ mæyÕ|/1/7/15/1/7/15!yy mæyÌ mæyÌy/1/7/16/1/7/16!zz mæyÏ mæyÏz/1/7/13/1/7/13!ww mæy mæyÂw/1/7/14/1/7/14!xx mæyÊ mæyÊx/1/7/11/1/7/11!uu mæyº mæyºu/1/7/12/1/7/12!vv mæy¾ mæy¾v/1/7/3/1/7/3!mm mæy¬ mæy¬m/1/7/2/1/7/2!ll mæy« mæy«l/1/7/1/1/7/1!kk mæyª mæyªk/1/7/10/1/7/10!tt mæy· mæy·t/1/7/0/1/7/0!jj mæy© mæy©j/1/7/7/1/7/7!qq mæy³ mæy³q/1/7/6/1/7/6!pp mæy² mæy²p/1/7/5/1/7/5!oo mæy¯ mæy¯o/1/7/4/1/7/4!nn mæy® mæy®n/1/7/9/1/7/9!ss mæy¶ mæy¶s/1/7/8/1/7/8!rr mæy´ mæy´r/1/6/1/6!TT mæyz mæyzh/1/6/19/1/6/19!hh mæy¦ mæy¦h/1/6/17/1/6/17!ff mæy¢ mæy¢f/1/6/18/1/6/18!gg mæy¤ mæy¤g/1/6/15/1/6/15!dd mæyŸ mæyŸd/1/6/16/1/6/16!ee mæy¡ mæy¡e/1/6/13/1/6/13!bb mæyœ mæyœb/1/6/14/1/6/14!cc mæy mæyc/1/6/11/1/6/11!`` mæyš mæyš`/1/6/12/1/6/12!aa mæy› mæy›a/1/6/3/1/6/3!XX mæyƒ mæyƒX/1/6/2/1/6/2!WW mæy‚ mæy‚W/1/6/1/1/6/1!VV mæy mæyV/1/6/10/1/6/10!__ mæy™ mæy™_/1/6/0/1/6/0!UU mæy€ mæy€U/1/6/7/1/6/7!\\ mæy• mæy•\/1/6/6/1/6/6![[ mæy mæy[/1/6/5/1/6/5!ZZ mæyŽ mæyŽZ/1/6/4/1/6/4!YY mæy… mæy…Y/1/6/9/1/6/9!^^ mæy— mæy—^/1/6/8/1/6/8!]] mæy– mæy–]/1/5/1/5!?? mæy^ mæy^S/1/5/19/1/5/19!SS mæyy mæyyS/1/5/17/1/5/17!QQ mæyw mæywQ/1/5/18/1/5/18!RR mæyx mæyxR/1/5/15/1/5/15!OO mæyt mæytO/1/5/16/1/5/16!PP mæyv mæyvP/1/5/13/1/5/13!MM mæyp mæypM/1/5/14/1/5/14!NN mæyq mæyqN/1/5/11/1/5/11!KK mæyn mæynK/1/5/12/1/5/12!LL mæyo mæyoL/1/5/3/1/5/3!CC mæyd mæydC/1/5/2/1/5/2!BB mæyc mæycB/1/5/1/1/5/1!AA mæyb mæybA/1/5/10/1/5/10!JJ mæym mæymJ/1/5/0/1/5/0!@@ mæy_ mæy_@/1/5/7/1/5/7!GG mæyj mæyjG/1/5/6/1/5/6!FF mæyg mæygF/1/5/5/1/5/5!EE mæyf mæyfE/1/5/4/1/5/4!DD mæye mæyeD/1/5/9/1/5/9!II mæyl mæylI/1/5/8/1/5/8!HH mæyk mæykH/1/4/1/4!** mæy1 mæy1>/1/4/19/1/4/19!>> mæy\ mæy\>/1/4/17/1/4/17!<< mæyR mæyR</1/4/18/1/4/18!== mæyY mæyY=/1/4/15/1/4/15!:: mæyO mæyO:/1/4/16/1/4/16!;; mæyQ mæyQ;/1/4/13/1/4/13!88 mæyM mæyM8/1/4/14/1/4/14!99 mæyN mæyN9/1/4/11/1/4/11!66 mæyI mæyI6/1/4/12/1/4/12!77 mæyJ mæyJ7/1/4/3/1/4/3!.. mæy; mæy;./1/4/2/1/4/2!-- mæy: mæy:-/1/4/1/1/4/1!,, mæy8 mæy8,/1/4/10/1/4/10!55 mæyH mæyH5/1/4/0/1/4/0!++ mæy3 mæy3+/1/4/7/1/4/7!22 mæyB mæyB2/1/4/6/1/4/6!11 mæyA mæyA1/1/4/5/1/4/5!00 mæy> mæy>0/1/4/4/1/4/4!// mæy= mæy=//1/4/9/1/4/9!44 mæyG mæyG4/1/4/8/1/4/8!33 mæyE mæyE3/1/9/1/9!““ mæyû mæyû§/1/9/19/1/9/19!§§ mæz mæz§/1/9/17/1/9/17!¥¥ mæz mæz¥/1/9/18/1/9/18!¦¦ mæz mæz¦/1/9/15/1/9/15!££ mæz mæz£/1/9/16/1/9/16!¤¤ mæz mæz¤/1/9/13/1/9/13!¡¡ mæz  mæz ¡/1/9/14/1/9/14!¢¢ mæz mæz¢/1/9/11/1/9/11!ŸŸ mæz  mæz Ÿ/1/9/12/1/9/12!   mæz  mæz  /1/9/3/1/9/3!—— mæyÿ mæyÿ—/1/9/2/1/9/2!–– mæyþ mæyþ–/1/9/1/1/9/1!•• mæyý mæyý•/1/9/10/1/9/10!žž mæz mæzž/1/9/0/1/9/0!”” mæyû mæyû”/1/9/7/1/9/7!›› mæz mæz›/1/9/6/1/9/6!šš mæz mæzš/1/9/5/1/9/5!™™ mæz mæz™/1/9/4/1/9/4!˜˜ mæz mæz˜/1/9/9/1/9/9! mæz mæz/1/9/8/1/9/8!œœ mæz mæzœ/1/8/1/8!~~ mæyÜ mæyÜ’/1/8/19/1/8/19!’’ mæyú mæyú’/1/8/17/1/8/17! mæyø mæyø/1/8/18/1/8/18!‘‘ mæyù mæyù‘/1/8/15/1/8/15!ŽŽ mæyö mæyöŽ/1/8/16/1/8/16! mæy÷ mæy÷/1/8/13/1/8/13!ŒŒ mæyñ mæyñŒ/1/8/14/1/8/14! mæyó mæyó/1/8/11/1/8/11!ŠŠ mæyï mæyïŠ/1/8/12/1/8/12!‹‹ mæyð mæyð‹/1/8/3/1/8/3!‚‚ mæyâ mæyâ‚/1/8/2/1/8/2! mæyâ mæyâ/1/8/1/1/8/1!€€ mæyà mæyà€/1/8/10/1/8/10!‰‰ mæyî mæyî‰/1/8/0/1/8/0! mæyÞ mæyÞ/1/8/7/1/8/7!†† mæyæ mæyæ†/1/8/6/1/8/6!…… mæyå mæyå…/1/8/5/1/8/5!„„ mæyä mæyä„/1/8/4/1/8/4!ƒƒ mæyã mæyãƒ/1/8/9/1/8/9!ˆˆ mæyë mæyëˆ/1/8/8/1/8/8!‡‡ mæyê mæyê‡/0/0! mæv« mæv« À/0/3/0/3!BB mæwS mæwSV/0/3/19/0/3/19!VV mæw… mæw…V/0/3/17/0/3/17!TT mæw‚ mæw‚T/0/3/18/0/3/18!UU mæwƒ mæwƒU/0/3/15/0/3/15!RR mæw~ mæw~R/0/3/16/0/3/16!SS mæw€ mæw€S/0/3/13/0/3/13!PP mæw{ mæw{P/0/3/14/0/3/14!QQ mæw} mæw}Q/0/3/11/0/3/11!NN mæwv mæwvN/0/3/12/0/3/12!OO mæwy mæwyO/0/3/3/0/3/3!FF mæw^ mæw^F/0/3/2/0/3/2!EE mæw] mæw]E/0/3/1/0/3/1!DD mæw[ mæw[D/0/3/10/0/3/10!MM mæwq mæwqM/0/3/0/0/3/0!CC mæwX mæwXC/0/3/7/0/3/7!JJ mæwj mæwjJ/0/3/6/0/3/6!II mæwi mæwiI/0/3/5/0/3/5!HH mæwh mæwhH/0/3/4/0/3/4!GG mæwf mæwfG/0/3/9/0/3/9!LL mæwo mæwoL/0/3/8/0/3/8!KK mæwm mæwmK/0/2/0/2!-- mæw' mæw'A/0/2/19/0/2/19!AA mæwQ mæwQA/0/2/17/0/2/17!?? mæwM mæwM?/0/2/18/0/2/18!@@ mæwO mæwO@/0/2/15/0/2/15!== mæwI mæwI=/0/2/16/0/2/16!>> mæwJ mæwJ>/0/2/13/0/2/13!;; mæwF mæwF;/0/2/14/0/2/14!<< mæwG mæwG</0/2/11/0/2/11!99 mæwB mæwB9/0/2/12/0/2/12!:: mæwD mæwD:/0/2/3/0/2/3!11 mæw0 mæw01/0/2/2/0/2/2!00 mæw. mæw.0/0/2/1/0/2/1!// mæw+ mæw+//0/2/10/0/2/10!88 mæwA mæwA8/0/2/0/0/2/0!.. mæw) mæw)./0/2/7/0/2/7!55 mæw; mæw;5/0/2/6/0/2/6!44 mæw7 mæw74/0/2/5/0/2/5!33 mæw5 mæw53/0/2/4/0/2/4!22 mæw2 mæw22/0/2/9/0/2/9!77 mæw@ mæw@7/0/2/8/0/2/8!66 mæw> mæw>6/0/1/0/1! mævë mævë,/0/1/19/0/1/19!,, mæw% mæw%,/0/1/17/0/1/17!** mæw! mæw!*/0/1/18/0/1/18!++ mæw" mæw"+/0/1/15/0/1/15!(( mæw mæw(/0/1/16/0/1/16!)) mæw mæw)/0/1/13/0/1/13!&& mæw mæw&/0/1/14/0/1/14!'' mæw mæw'/0/1/11/0/1/11!$$ mæw mæw$/0/1/12/0/1/12!%% mæw mæw%/0/1/3/0/1/3! mæw mæw/0/1/2/0/1/2! mæw mæw/0/1/1/0/1/1! mævý mævý/0/1/10/0/1/10!## mæw mæw#/0/1/0/0/1/0! mævú mævú/0/1/7/0/1/7!  mæw  mæw /0/1/6/0/1/6! mæw  mæw /0/1/5/0/1/5! mæw mæw/0/1/4/0/1/4! mæw mæw/0/1/9/0/1/9!"" mæw mæw"/0/1/8/0/1/8!!! mæw mæw!/0/0/0/0! mæv¶ mæv¶/0/0/19/0/0/19! mævç mævç/0/0/17/0/0/17! mævã mævã/0/0/18/0/0/18! mævå mævå/0/0/15/0/0/15! mævß mævß/0/0/16/0/0/16! mævá mævá/0/0/13/0/0/13! mævÛ mævÛ/0/0/14/0/0/14! mævÝ mævÝ/0/0/11/0/0/11! mævÙ mævÙ/0/0/12/0/0/12! mævÚ mævÚ/0/0/3/0/0/3! mævÄ mævÄ/0/0/2/0/0/2! mæv mævÂ/0/0/1/0/0/1! mævÀ mævÀ/0/0/10/0/0/10! mævÕ mævÕ/0/0/0/0/0/0! mæv¾ mæv¾/0/0/7/0/0/7!  mævÎ mævÎ /0/0/6/0/0/6!  mævÌ mævÌ /0/0/5/0/0/5!  mævÊ mævÊ /0/0/4/0/0/4! mævÇ mævÇ/0/0/9/0/0/9!  mævÒ mævÒ /0/0/8/0/0/8!  mævÐ mævÐ /0/7/0/7!–– mæx mæxª/0/7/19/0/7/19!ªª mæx= mæx=ª/0/7/17/0/7/17!¨¨ mæx9 mæx9¨/0/7/18/0/7/18!©© mæx; mæx;©/0/7/15/0/7/15!¦¦ mæx6 mæx6¦/0/7/16/0/7/16!§§ mæx8 mæx8§/0/7/13/0/7/13!¤¤ mæx2 mæx2¤/0/7/14/0/7/14!¥¥ mæx5 mæx5¥/0/7/11/0/7/11!¢¢ mæx. mæx.¢/0/7/12/0/7/12!££ mæx0 mæx0£/0/7/3/0/7/3!šš mæx  mæx š/0/7/2/0/7/2!™™ mæx mæx™/0/7/1/0/7/1!˜˜ mæx mæx˜/0/7/10/0/7/10!¡¡ mæx- mæx-¡/0/7/0/0/7/0!—— mæx mæx—/0/7/7/0/7/7!žž mæx' mæx'ž/0/7/6/0/7/6! mæx% mæx%/0/7/5/0/7/5!œœ mæx$ mæx$œ/0/7/4/0/7/4!›› mæx" mæx"›/0/7/9/0/7/9!   mæx+ mæx+ /0/7/8/0/7/8!ŸŸ mæx) mæx)Ÿ/0/6/0/6! mæwæ mæwæ•/0/6/19/0/6/19!•• mæx mæx•/0/6/17/0/6/17!““ mæx mæx“/0/6/18/0/6/18!”” mæx mæx”/0/6/15/0/6/15!‘‘ mæx mæx‘/0/6/16/0/6/16!’’ mæx mæx’/0/6/13/0/6/13! mæx  mæx /0/6/14/0/6/14! mæx  mæx /0/6/11/0/6/11! mæx mæx/0/6/12/0/6/12!ŽŽ mæx  mæx Ž/0/6/3/0/6/3!…… mæwô mæwô…/0/6/2/0/6/2!„„ mæwñ mæwñ„/0/6/1/0/6/1!ƒƒ mæwì mæwìƒ/0/6/10/0/6/10!ŒŒ mæx mæxŒ/0/6/0/0/6/0!‚‚ mæwê mæwê‚/0/6/7/0/6/7!‰‰ mæx mæx‰/0/6/6/0/6/6!ˆˆ mæx mæxˆ/0/6/5/0/6/5!‡‡ mæwý mæwý‡/0/6/4/0/6/4!†† mæwú mæwú†/0/6/9/0/6/9!‹‹ mæx mæx‹/0/6/8/0/6/8!ŠŠ mæx mæxŠ/0/5/0/5!ll mæw· mæw·€/0/5/19/0/5/19!€€ mæwã mæwã€/0/5/17/0/5/17!~~ mæwß mæwß~/0/5/18/0/5/18! mæwá mæwá/0/5/15/0/5/15!|| mæwÜ mæwÜ|/0/5/16/0/5/16!}} mæwÝ mæwÝ}/0/5/13/0/5/13!zz mæwÖ mæwÖz/0/5/14/0/5/14!{{ mæwÙ mæwÙ{/0/5/11/0/5/11!xx mæwÎ mæwÎx/0/5/12/0/5/12!yy mæwÔ mæwÔy/0/5/3/0/5/3!pp mæw¿ mæw¿p/0/5/2/0/5/2!oo mæw¾ mæw¾o/0/5/1/0/5/1!nn mæw½ mæw½n/0/5/10/0/5/10!ww mæwÍ mæwÍw/0/5/0/0/5/0!mm mæwº mæwºm/0/5/7/0/5/7!tt mæwÉ mæwÉt/0/5/6/0/5/6!ss mæwÇ mæwÇs/0/5/5/0/5/5!rr mæwÄ mæwÄr/0/5/4/0/5/4!qq mæwÁ mæwÁq/0/5/9/0/5/9!vv mæwÌ mæwÌv/0/5/8/0/5/8!uu mæwÊ mæwÊu/0/4/0/4!WW mæw‡ mæw‡k/0/4/19/0/4/19!kk mæw´ mæw´k/0/4/17/0/4/17!ii mæw¯ mæw¯i/0/4/18/0/4/18!jj mæw± mæw±j/0/4/15/0/4/15!gg mæw¬ mæw¬g/0/4/16/0/4/16!hh mæw­ mæw­h/0/4/13/0/4/13!ee mæw© mæw©e/0/4/14/0/4/14!ff mæw« mæw«f/0/4/11/0/4/11!cc mæw  mæw c/0/4/12/0/4/12!dd mæw¤ mæw¤d/0/4/3/0/4/3![[ mæw mæw[/0/4/2/0/4/2!ZZ mæwŽ mæwŽZ/0/4/1/0/4/1!YY mæwŒ mæwŒY/0/4/10/0/4/10!bb mæw› mæw›b/0/4/0/0/4/0!XX mæw‹ mæw‹X/0/4/7/0/4/7!__ mæw— mæw—_/0/4/6/0/4/6!^^ mæw– mæw–^/0/4/5/0/4/5!]] mæw” mæw”]/0/4/4/0/4/4!\\ mæw’ mæw’\/0/4/9/0/4/9!aa mæwš mæwša/0/4/8/0/4/8!`` mæw™ mæw™`/0/9/0/9!ÀÀ mæxn mæxnÔ/0/9/19/0/9/19!ÔÔ mæx“ mæx“Ô/0/9/17/0/9/17!ÒÒ mæx‘ mæx‘Ò/0/9/18/0/9/18!ÓÓ mæx’ mæx’Ó/0/9/15/0/9/15!ÐÐ mæx mæxÐ/0/9/16/0/9/16!ÑÑ mæx mæxÑ/0/9/13/0/9/13!ÎÎ mæx† mæx†Î/0/9/14/0/9/14!ÏÏ mæx‹ mæx‹Ï/0/9/11/0/9/11!ÌÌ mæxƒ mæxƒÌ/0/9/12/0/9/12!ÍÍ mæx… mæx…Í/0/9/3/0/9/3!ÄÄ mæxw mæxwÄ/0/9/2/0/9/2!Ãà mæxu mæxuÃ/0/9/1/0/9/1! mæxt mæxtÂ/0/9/10/0/9/10!ËË mæx‚ mæx‚Ë/0/9/0/0/9/0!ÁÁ mæxr mæxrÁ/0/9/7/0/9/7!ÈÈ mæx| mæx|È/0/9/6/0/9/6!ÇÇ mæx{ mæx{Ç/0/9/5/0/9/5!ÆÆ mæxz mæxzÆ/0/9/4/0/9/4!ÅÅ mæxy mæxyÅ/0/9/9/0/9/9!ÊÊ mæx mæxÊ/0/9/8/0/9/8!ÉÉ mæx~ mæx~É/0/8/0/8!«« mæx? mæx?¿/0/8/19/0/8/19!¿¿ mæxm mæxm¿/0/8/17/0/8/17!½½ mæxj mæxj½/0/8/18/0/8/18!¾¾ mæxl mæxl¾/0/8/15/0/8/15!»» mæxf mæxf»/0/8/16/0/8/16!¼¼ mæxg mæxg¼/0/8/13/0/8/13!¹¹ mæx^ mæx^¹/0/8/14/0/8/14!ºº mæx` mæx`º/0/8/11/0/8/11!·· mæxW mæxW·/0/8/12/0/8/12!¸¸ mæxZ mæxZ¸/0/8/3/0/8/3!¯¯ mæxI mæxI¯/0/8/2/0/8/2!®® mæxD mæxD®/0/8/1/0/8/1!­­ mæxB mæxB­/0/8/10/0/8/10!¶¶ mæxV mæxV¶/0/8/0/0/8/0!¬¬ mæx@ mæx@¬/0/8/7/0/8/7!³³ mæxP mæxP³/0/8/6/0/8/6!²² mæxM mæxM²/0/8/5/0/8/5!±± mæxL mæxL±/0/8/4/0/8/4!°° mæxK mæxK°/0/8/9/0/8/9!µµ mæxT mæxTµ/0/8/8/0/8/8!´´ mæxS mæxS´ /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/îf˜G/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/snapshot.2730100644 0000000 0000000 00000156311 15051152474 033553 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿ mæu½N worldanyoneworldanyoneÿÿÿÿÿÿÿÿ/2/2!¨¨ mæz mæz f/2/3/2/3!èè mæz“ mæz“ü/2/3/19/2/3/19!üü mæzñ mæzñü/2/3/17/2/3/17!úú mæzë mæzëú/2/3/18/2/3/18!ûû mæzï mæzïû/2/3/15/2/3/15!øø mæzè mæzèø/2/3/16/2/3/16!ùù mæzê mæzêù/2/3/13/2/3/13!öö mæzæ mæzæö/2/3/14/2/3/14!÷÷ mæzç mæzç÷/2/3/11/2/3/11!ôô mæzÒ mæzÒô/2/3/12/2/3/12!õõ mæzÝ mæzÝõ/2/3/3/2/3/3!ìì mæz› mæz›ì/2/3/2/2/3/2!ëë mæzš mæzšë/2/3/1/2/3/1!êê mæz™ mæz™ê/2/3/10/2/3/10!óó mæzµ mæzµó/2/3/0/2/3/0!éé mæz– mæz–é/2/3/7/2/3/7!ðð mæz« mæz«ð/2/3/6/2/3/6!ïï mæzª mæzªï/2/3/5/2/3/5!îî mæz¢ mæz¢î/2/3/4/2/3/4!íí mæzž mæzží/2/3/9/2/3/9!òò mæz¯ mæz¯ò/2/3/8/2/3/8!ññ mæz¬ mæz¬ñ/2/2/2/2!ÓÓ mæzv mæzvç/2/2/19/2/2/19!çç mæz‘ mæz‘ç/2/2/17/2/2/17!åå mæz mæzå/2/2/18/2/2/18!ææ mæz‘ mæz‘æ/2/2/15/2/2/15!ãã mæzŽ mæzŽã/2/2/16/2/2/16!ää mæz mæzä/2/2/13/2/2/13!áá mæz‹ mæz‹á/2/2/14/2/2/14!ââ mæz mæzâ/2/2/11/2/2/11!ßß mæz‰ mæz‰ß/2/2/12/2/2/12!àà mæzŠ mæzŠà/2/2/3/2/2/3!×× mæz| mæz|×/2/2/2/2/2/2!ÖÖ mæzz mæzzÖ/2/2/1/2/2/1!ÕÕ mæzy mæzyÕ/2/2/10/2/2/10!ÞÞ mæzˆ mæzˆÞ/2/2/0/2/2/0!ÔÔ mæzw mæzwÔ/2/2/7/2/2/7!ÛÛ mæz‚ mæz‚Û/2/2/6/2/2/6!ÚÚ mæz€ mæz€Ú/2/2/5/2/2/5!ÙÙ mæz mæzÙ/2/2/4/2/2/4!ØØ mæz} mæz}Ø/2/2/9/2/2/9!ÝÝ mæz… mæz…Ý/2/2/8/2/2/8!ÜÜ mæzƒ mæzƒÜ/2/1/2/1!¾¾ mæzZ mæzZÒ/2/1/19/2/1/19!ÒÒ mæzu mæzuÒ/2/1/17/2/1/17!ÐÐ mæzs mæzsÐ/2/1/18/2/1/18!ÑÑ mæzt mæztÑ/2/1/15/2/1/15!ÎÎ mæzq mæzqÎ/2/1/16/2/1/16!ÏÏ mæzr mæzrÏ/2/1/13/2/1/13!ÌÌ mæzl mæzlÌ/2/1/14/2/1/14!ÍÍ mæzn mæznÍ/2/1/11/2/1/11!ÊÊ mæzh mæzhÊ/2/1/12/2/1/12!ËË mæzi mæziË/2/1/3/2/1/3! mæz_ mæz_Â/2/1/2/2/1/2!ÁÁ mæz^ mæz^Á/2/1/1/2/1/1!ÀÀ mæz] mæz]À/2/1/10/2/1/10!ÉÉ mæzg mæzgÉ/2/1/0/2/1/0!¿¿ mæz[ mæz[¿/2/1/7/2/1/7!ÆÆ mæze mæzeÆ/2/1/6/2/1/6!ÅÅ mæzc mæzcÅ/2/1/5/2/1/5!ÄÄ mæzb mæzbÄ/2/1/4/2/1/4!Ãà mæz` mæz`Ã/2/1/9/2/1/9!ÈÈ mæzg mæzgÈ/2/1/8/2/1/8!ÇÇ mæzf mæzfÇ/2/0/2/0!©© mæz mæz½/2/0/19/2/0/19!½½ mæzY mæzY½/2/0/17/2/0/17!»» mæzN mæzN»/2/0/18/2/0/18!¼¼ mæzW mæzW¼/2/0/15/2/0/15!¹¹ mæzE mæzE¹/2/0/16/2/0/16!ºº mæzJ mæzJº/2/0/13/2/0/13!·· mæzA mæzA·/2/0/14/2/0/14!¸¸ mæzC mæzC¸/2/0/11/2/0/11!µµ mæz? mæz?µ/2/0/12/2/0/12!¶¶ mæz@ mæz@¶/2/0/3/2/0/3!­­ mæz mæz­/2/0/2/2/0/2!¬¬ mæz mæz¬/2/0/1/2/0/1!«« mæz mæz«/2/0/10/2/0/10!´´ mæz> mæz>´/2/0/0/2/0/0!ªª mæz mæzª/2/0/7/2/0/7!±± mæz" mæz"±/2/0/6/2/0/6!°° mæz! mæz!°/2/0/5/2/0/5!¯¯ mæz  mæz ¯/2/0/4/2/0/4!®® mæz mæz®/2/0/9/2/0/9!³³ mæz< mæz<³/2/0/8/2/0/8!²² mæz: mæz:²/2/7/2/7!<< mæ{c mæ{cP/2/7/19/2/7/19!PP mæ{z mæ{zP/2/7/17/2/7/17!NN mæ{w mæ{wN/2/7/18/2/7/18!OO mæ{y mæ{yO/2/7/15/2/7/15!LL mæ{v mæ{vL/2/7/16/2/7/16!MM mæ{w mæ{wM/2/7/13/2/7/13!JJ mæ{t mæ{tJ/2/7/14/2/7/14!KK mæ{u mæ{uK/2/7/11/2/7/11!HH mæ{r mæ{rH/2/7/12/2/7/12!II mæ{s mæ{sI/2/7/3/2/7/3!@@ mæ{h mæ{h@/2/7/2/2/7/2!?? mæ{g mæ{g?/2/7/1/2/7/1!>> mæ{e mæ{e>/2/7/10/2/7/10!GG mæ{q mæ{qG/2/7/0/2/7/0!== mæ{d mæ{d=/2/7/7/2/7/7!DD mæ{m mæ{mD/2/7/6/2/7/6!CC mæ{k mæ{kC/2/7/5/2/7/5!BB mæ{k mæ{kB/2/7/4/2/7/4!AA mæ{j mæ{jA/2/7/9/2/7/9!FF mæ{o mæ{oF/2/7/8/2/7/8!EE mæ{n mæ{nE/2/6/2/6!'' mæ{; mæ{;;/2/6/19/2/6/19!;; mæ{b mæ{b;/2/6/17/2/6/17!99 mæ{^ mæ{^9/2/6/18/2/6/18!:: mæ{a mæ{a:/2/6/15/2/6/15!77 mæ{[ mæ{[7/2/6/16/2/6/16!88 mæ{] mæ{]8/2/6/13/2/6/13!55 mæ{Y mæ{Y5/2/6/14/2/6/14!66 mæ{Z mæ{Z6/2/6/11/2/6/11!33 mæ{T mæ{T3/2/6/12/2/6/12!44 mæ{X mæ{X4/2/6/3/2/6/3!++ mæ{C mæ{C+/2/6/2/2/6/2!** mæ{A mæ{A*/2/6/1/2/6/1!)) mæ{? mæ{?)/2/6/10/2/6/10!22 mæ{S mæ{S2/2/6/0/2/6/0!(( mæ{= mæ{=(/2/6/7/2/6/7!// mæ{H mæ{H//2/6/6/2/6/6!.. mæ{G mæ{G./2/6/5/2/6/5!-- mæ{E mæ{E-/2/6/4/2/6/4!,, mæ{D mæ{D,/2/6/9/2/6/9!11 mæ{Q mæ{Q1/2/6/8/2/6/8!00 mæ{O mæ{O0/2/5/2/5! mæ{ mæ{&/2/5/19/2/5/19!&& mæ{9 mæ{9&/2/5/17/2/5/17!$$ mæ{4 mæ{4$/2/5/18/2/5/18!%% mæ{7 mæ{7%/2/5/15/2/5/15!"" mæ{2 mæ{2"/2/5/16/2/5/16!## mæ{3 mæ{3#/2/5/13/2/5/13!   mæ{. mæ{. /2/5/14/2/5/14!!! mæ{0 mæ{0!/2/5/11/2/5/11! mæ{* mæ{*/2/5/12/2/5/12! mæ{, mæ{,/2/5/3/2/5/3! mæ{ mæ{/2/5/2/2/5/2! mæ{ mæ{/2/5/1/2/5/1! mæ{ mæ{/2/5/10/2/5/10! mæ{) mæ{)/2/5/0/2/5/0! mæ{ mæ{/2/5/7/2/5/7! mæ{& mæ{&/2/5/6/2/5/6! mæ{$ mæ{$/2/5/5/2/5/5! mæ{" mæ{"/2/5/4/2/5/4! mæ{ mæ{/2/5/9/2/5/9! mæ{' mæ{'/2/5/8/2/5/8! mæ{& mæ{&/2/4/2/4!ýý mæzò mæzò/2/4/19/2/4/19! mæ{ mæ{/2/4/17/2/4/17! mæ{ mæ{/2/4/18/2/4/18! mæ{ mæ{/2/4/15/2/4/15!   mæ{ mæ{ /2/4/16/2/4/16! mæ{ mæ{/2/4/13/2/4/13!   mæ{ mæ{ /2/4/14/2/4/14!   mæ{ mæ{ /2/4/11/2/4/11!   mæ{  mæ{  /2/4/12/2/4/12!   mæ{ mæ{ /2/4/3/2/4/3! mæzú mæzú/2/4/2/2/4/2! mæzø mæzø/2/4/1/2/4/1!ÿÿ mæzõ mæzõÿ/2/4/10/2/4/10! mæ{ mæ{/2/4/0/2/4/0!þþ mæzô mæzôþ/2/4/7/2/4/7! mæ{ mæ{/2/4/6/2/4/6! mæzÿ mæzÿ/2/4/5/2/4/5! mæzý mæzý/2/4/4/2/4/4! mæzû mæzû/2/4/9/2/4/9! mæ{ mæ{/2/4/8/2/4/8! mæ{ mæ{/2/9/2/9!ff mæ{• mæ{• r/2/9/3/2/9/3!jj mæ{š mæ{šj/2/9/2/2/9/2!ii mæ{™ mæ{™i/2/9/10/2/9/10!qq mæ{¡ mæ{¡q/2/9/1/2/9/1!hh mæ{— mæ{—h/2/9/0/2/9/0!gg mæ{– mæ{–g/2/9/7/2/9/7!nn mæ{ž mæ{žn/2/9/6/2/9/6!mm mæ{ mæ{m/2/9/5/2/9/5!ll mæ{› mæ{›l/2/9/4/2/9/4!kk mæ{š mæ{šk/2/9/9/2/9/9!pp mæ{  mæ{ p/2/9/8/2/9/8!oo mæ{Ÿ mæ{Ÿo/2/9/11/2/9/11!rr mæ{¢ mæ{¢r/2/8/2/8!QQ mæ{{ mæ{{e/2/8/19/2/8/19!ee mæ{“ mæ{“e/2/8/17/2/8/17!cc mæ{‘ mæ{‘c/2/8/18/2/8/18!dd mæ{’ mæ{’d/2/8/15/2/8/15!aa mæ{ mæ{a/2/8/16/2/8/16!bb mæ{ mæ{b/2/8/13/2/8/13!__ mæ{Œ mæ{Œ_/2/8/14/2/8/14!`` mæ{ mæ{`/2/8/11/2/8/11!]] mæ{‰ mæ{‰]/2/8/12/2/8/12!^^ mæ{Š mæ{Š^/2/8/3/2/8/3!UU mæ{€ mæ{€U/2/8/2/2/8/2!TT mæ{ mæ{T/2/8/1/2/8/1!SS mæ{~ mæ{~S/2/8/10/2/8/10!\\ mæ{ˆ mæ{ˆ\/2/8/0/2/8/0!RR mæ{} mæ{}R/2/8/7/2/8/7!YY mæ{… mæ{…Y/2/8/6/2/8/6!XX mæ{ƒ mæ{ƒX/2/8/5/2/8/5!WW mæ{‚ mæ{‚W/2/8/4/2/8/4!VV mæ{ mæ{V/2/8/9/2/8/9![[ mæ{‡ mæ{‡[/2/8/8/2/8/8!ZZ mæ{† mæ{†Z/1/1!ÕÕ mæx” mæx” “/1/3/1/3! mæy mæy)/1/3/19/1/3/19!)) mæy0 mæy0)/1/3/17/1/3/17!'' mæy+ mæy+'/1/3/18/1/3/18!(( mæy, mæy,(/1/3/15/1/3/15!%% mæy( mæy(%/1/3/16/1/3/16!&& mæy) mæy)&/1/3/13/1/3/13!## mæy& mæy&#/1/3/14/1/3/14!$$ mæy' mæy'$/1/3/11/1/3/11!!! mæy$ mæy$!/1/3/12/1/3/12!"" mæy% mæy%"/1/3/3/1/3/3! mæy mæy/1/3/2/1/3/2! mæy mæy/1/3/1/1/3/1! mæy mæy/1/3/10/1/3/10!   mæy! mæy! /1/3/0/1/3/0! mæy mæy/1/3/7/1/3/7! mæy mæy/1/3/6/1/3/6! mæy mæy/1/3/5/1/3/5! mæy mæy/1/3/4/1/3/4! mæy mæy/1/3/9/1/3/9! mæy  mæy /1/3/8/1/3/8! mæy mæy/1/2/1/2! mæxð mæxð/1/2/19/1/2/19! mæy mæy/1/2/17/1/2/17! mæy  mæy /1/2/18/1/2/18! mæy mæy/1/2/15/1/2/15! mæy  mæy /1/2/16/1/2/16! mæy  mæy /1/2/13/1/2/13! mæy  mæy /1/2/14/1/2/14! mæy  mæy /1/2/11/1/2/11!   mæy mæy /1/2/12/1/2/12!   mæy mæy /1/2/3/1/2/3! mæxø mæxø/1/2/2/1/2/2! mæx÷ mæx÷/1/2/1/1/2/1! mæxõ mæxõ/1/2/10/1/2/10!   mæy mæy /1/2/0/1/2/0! mæxò mæxò/1/2/7/1/2/7! mæy mæy/1/2/6/1/2/6! mæy mæy/1/2/5/1/2/5! mæxÿ mæxÿ/1/2/4/1/2/4! mæxþ mæxþ/1/2/9/1/2/9!   mæy mæy /1/2/8/1/2/8!   mæy mæy /1/1/1/1!ëë mæx¼ mæx¼ÿ/1/1/19/1/1/19!ÿÿ mæxè mæxèÿ/1/1/17/1/1/17!ýý mæxå mæxåý/1/1/18/1/1/18!þþ mæxæ mæxæþ/1/1/15/1/1/15!ûû mæxá mæxáû/1/1/16/1/1/16!üü mæxã mæxãü/1/1/13/1/1/13!ùù mæxÞ mæxÞù/1/1/14/1/1/14!úú mæxß mæxßú/1/1/11/1/1/11!÷÷ mæxÖ mæxÖ÷/1/1/12/1/1/12!øø mæxØ mæxØø/1/1/3/1/1/3!ïï mæxà mæxÃï/1/1/2/1/1/2!îî mæx mæxÂî/1/1/1/1/1/1!íí mæx¿ mæx¿í/1/1/10/1/1/10!öö mæxÓ mæxÓö/1/1/0/1/1/0!ìì mæx¾ mæx¾ì/1/1/7/1/1/7!óó mæxÎ mæxÎó/1/1/6/1/1/6!òò mæxÇ mæxÇò/1/1/5/1/1/5!ññ mæxÅ mæxÅñ/1/1/4/1/1/4!ðð mæxÅ mæxÅð/1/1/9/1/1/9!õõ mæxÑ mæxÑõ/1/1/8/1/1/8!ôô mæxÏ mæxÏô/1/0/1/0!ÖÖ mæx• mæx•ê/1/0/19/1/0/19!êê mæxº mæxºê/1/0/17/1/0/17!èè mæx¶ mæx¶è/1/0/18/1/0/18!éé mæx¸ mæx¸é/1/0/15/1/0/15!ææ mæx² mæx²æ/1/0/16/1/0/16!çç mæxµ mæxµç/1/0/13/1/0/13!ää mæx¯ mæx¯ä/1/0/14/1/0/14!åå mæx± mæx±å/1/0/11/1/0/11!ââ mæx­ mæx­â/1/0/12/1/0/12!ãã mæx® mæx®ã/1/0/3/1/0/3!ÚÚ mæx mæxÚ/1/0/2/1/0/2!ÙÙ mæxš mæxšÙ/1/0/1/1/0/1!ØØ mæx— mæx—Ø/1/0/10/1/0/10!áá mæx¬ mæx¬á/1/0/0/1/0/0!×× mæx– mæx–×/1/0/7/1/0/7!ÞÞ mæx¦ mæx¦Þ/1/0/6/1/0/6!ÝÝ mæx¤ mæx¤Ý/1/0/5/1/0/5!ÜÜ mæx¢ mæx¢Ü/1/0/4/1/0/4!ÛÛ mæxŸ mæxŸÛ/1/0/9/1/0/9!àà mæxª mæxªà/1/0/8/1/0/8!ßß mæx© mæx©ß/1/7/1/7!ii mæy§ mæy§}/1/7/19/1/7/19!}} mæyÚ mæyÚ}/1/7/17/1/7/17!{{ mæyÓ mæyÓ{/1/7/18/1/7/18!|| mæyÕ mæyÕ|/1/7/15/1/7/15!yy mæyÌ mæyÌy/1/7/16/1/7/16!zz mæyÏ mæyÏz/1/7/13/1/7/13!ww mæy mæyÂw/1/7/14/1/7/14!xx mæyÊ mæyÊx/1/7/11/1/7/11!uu mæyº mæyºu/1/7/12/1/7/12!vv mæy¾ mæy¾v/1/7/3/1/7/3!mm mæy¬ mæy¬m/1/7/2/1/7/2!ll mæy« mæy«l/1/7/1/1/7/1!kk mæyª mæyªk/1/7/10/1/7/10!tt mæy· mæy·t/1/7/0/1/7/0!jj mæy© mæy©j/1/7/7/1/7/7!qq mæy³ mæy³q/1/7/6/1/7/6!pp mæy² mæy²p/1/7/5/1/7/5!oo mæy¯ mæy¯o/1/7/4/1/7/4!nn mæy® mæy®n/1/7/9/1/7/9!ss mæy¶ mæy¶s/1/7/8/1/7/8!rr mæy´ mæy´r/1/6/1/6!TT mæyz mæyzh/1/6/19/1/6/19!hh mæy¦ mæy¦h/1/6/17/1/6/17!ff mæy¢ mæy¢f/1/6/18/1/6/18!gg mæy¤ mæy¤g/1/6/15/1/6/15!dd mæyŸ mæyŸd/1/6/16/1/6/16!ee mæy¡ mæy¡e/1/6/13/1/6/13!bb mæyœ mæyœb/1/6/14/1/6/14!cc mæy mæyc/1/6/11/1/6/11!`` mæyš mæyš`/1/6/12/1/6/12!aa mæy› mæy›a/1/6/3/1/6/3!XX mæyƒ mæyƒX/1/6/2/1/6/2!WW mæy‚ mæy‚W/1/6/1/1/6/1!VV mæy mæyV/1/6/10/1/6/10!__ mæy™ mæy™_/1/6/0/1/6/0!UU mæy€ mæy€U/1/6/7/1/6/7!\\ mæy• mæy•\/1/6/6/1/6/6![[ mæy mæy[/1/6/5/1/6/5!ZZ mæyŽ mæyŽZ/1/6/4/1/6/4!YY mæy… mæy…Y/1/6/9/1/6/9!^^ mæy— mæy—^/1/6/8/1/6/8!]] mæy– mæy–]/1/5/1/5!?? mæy^ mæy^S/1/5/19/1/5/19!SS mæyy mæyyS/1/5/17/1/5/17!QQ mæyw mæywQ/1/5/18/1/5/18!RR mæyx mæyxR/1/5/15/1/5/15!OO mæyt mæytO/1/5/16/1/5/16!PP mæyv mæyvP/1/5/13/1/5/13!MM mæyp mæypM/1/5/14/1/5/14!NN mæyq mæyqN/1/5/11/1/5/11!KK mæyn mæynK/1/5/12/1/5/12!LL mæyo mæyoL/1/5/3/1/5/3!CC mæyd mæydC/1/5/2/1/5/2!BB mæyc mæycB/1/5/1/1/5/1!AA mæyb mæybA/1/5/10/1/5/10!JJ mæym mæymJ/1/5/0/1/5/0!@@ mæy_ mæy_@/1/5/7/1/5/7!GG mæyj mæyjG/1/5/6/1/5/6!FF mæyg mæygF/1/5/5/1/5/5!EE mæyf mæyfE/1/5/4/1/5/4!DD mæye mæyeD/1/5/9/1/5/9!II mæyl mæylI/1/5/8/1/5/8!HH mæyk mæykH/1/4/1/4!** mæy1 mæy1>/1/4/19/1/4/19!>> mæy\ mæy\>/1/4/17/1/4/17!<< mæyR mæyR</1/4/18/1/4/18!== mæyY mæyY=/1/4/15/1/4/15!:: mæyO mæyO:/1/4/16/1/4/16!;; mæyQ mæyQ;/1/4/13/1/4/13!88 mæyM mæyM8/1/4/14/1/4/14!99 mæyN mæyN9/1/4/11/1/4/11!66 mæyI mæyI6/1/4/12/1/4/12!77 mæyJ mæyJ7/1/4/3/1/4/3!.. mæy; mæy;./1/4/2/1/4/2!-- mæy: mæy:-/1/4/1/1/4/1!,, mæy8 mæy8,/1/4/10/1/4/10!55 mæyH mæyH5/1/4/0/1/4/0!++ mæy3 mæy3+/1/4/7/1/4/7!22 mæyB mæyB2/1/4/6/1/4/6!11 mæyA mæyA1/1/4/5/1/4/5!00 mæy> mæy>0/1/4/4/1/4/4!// mæy= mæy=//1/4/9/1/4/9!44 mæyG mæyG4/1/4/8/1/4/8!33 mæyE mæyE3/1/9/1/9!““ mæyû mæyû§/1/9/19/1/9/19!§§ mæz mæz§/1/9/17/1/9/17!¥¥ mæz mæz¥/1/9/18/1/9/18!¦¦ mæz mæz¦/1/9/15/1/9/15!££ mæz mæz£/1/9/16/1/9/16!¤¤ mæz mæz¤/1/9/13/1/9/13!¡¡ mæz  mæz ¡/1/9/14/1/9/14!¢¢ mæz mæz¢/1/9/11/1/9/11!ŸŸ mæz  mæz Ÿ/1/9/12/1/9/12!   mæz  mæz  /1/9/3/1/9/3!—— mæyÿ mæyÿ—/1/9/2/1/9/2!–– mæyþ mæyþ–/1/9/1/1/9/1!•• mæyý mæyý•/1/9/10/1/9/10!žž mæz mæzž/1/9/0/1/9/0!”” mæyû mæyû”/1/9/7/1/9/7!›› mæz mæz›/1/9/6/1/9/6!šš mæz mæzš/1/9/5/1/9/5!™™ mæz mæz™/1/9/4/1/9/4!˜˜ mæz mæz˜/1/9/9/1/9/9! mæz mæz/1/9/8/1/9/8!œœ mæz mæzœ/1/8/1/8!~~ mæyÜ mæyÜ’/1/8/19/1/8/19!’’ mæyú mæyú’/1/8/17/1/8/17! mæyø mæyø/1/8/18/1/8/18!‘‘ mæyù mæyù‘/1/8/15/1/8/15!ŽŽ mæyö mæyöŽ/1/8/16/1/8/16! mæy÷ mæy÷/1/8/13/1/8/13!ŒŒ mæyñ mæyñŒ/1/8/14/1/8/14! mæyó mæyó/1/8/11/1/8/11!ŠŠ mæyï mæyïŠ/1/8/12/1/8/12!‹‹ mæyð mæyð‹/1/8/3/1/8/3!‚‚ mæyâ mæyâ‚/1/8/2/1/8/2! mæyâ mæyâ/1/8/1/1/8/1!€€ mæyà mæyà€/1/8/10/1/8/10!‰‰ mæyî mæyî‰/1/8/0/1/8/0! mæyÞ mæyÞ/1/8/7/1/8/7!†† mæyæ mæyæ†/1/8/6/1/8/6!…… mæyå mæyå…/1/8/5/1/8/5!„„ mæyä mæyä„/1/8/4/1/8/4!ƒƒ mæyã mæyãƒ/1/8/9/1/8/9!ˆˆ mæyë mæyëˆ/1/8/8/1/8/8!‡‡ mæyê mæyê‡/0/0! mæv« mæv« À/0/3/0/3!BB mæwS mæwSV/0/3/19/0/3/19!VV mæw… mæw…V/0/3/17/0/3/17!TT mæw‚ mæw‚T/0/3/18/0/3/18!UU mæwƒ mæwƒU/0/3/15/0/3/15!RR mæw~ mæw~R/0/3/16/0/3/16!SS mæw€ mæw€S/0/3/13/0/3/13!PP mæw{ mæw{P/0/3/14/0/3/14!QQ mæw} mæw}Q/0/3/11/0/3/11!NN mæwv mæwvN/0/3/12/0/3/12!OO mæwy mæwyO/0/3/3/0/3/3!FF mæw^ mæw^F/0/3/2/0/3/2!EE mæw] mæw]E/0/3/1/0/3/1!DD mæw[ mæw[D/0/3/10/0/3/10!MM mæwq mæwqM/0/3/0/0/3/0!CC mæwX mæwXC/0/3/7/0/3/7!JJ mæwj mæwjJ/0/3/6/0/3/6!II mæwi mæwiI/0/3/5/0/3/5!HH mæwh mæwhH/0/3/4/0/3/4!GG mæwf mæwfG/0/3/9/0/3/9!LL mæwo mæwoL/0/3/8/0/3/8!KK mæwm mæwmK/0/2/0/2!-- mæw' mæw'A/0/2/19/0/2/19!AA mæwQ mæwQA/0/2/17/0/2/17!?? mæwM mæwM?/0/2/18/0/2/18!@@ mæwO mæwO@/0/2/15/0/2/15!== mæwI mæwI=/0/2/16/0/2/16!>> mæwJ mæwJ>/0/2/13/0/2/13!;; mæwF mæwF;/0/2/14/0/2/14!<< mæwG mæwG</0/2/11/0/2/11!99 mæwB mæwB9/0/2/12/0/2/12!:: mæwD mæwD:/0/2/3/0/2/3!11 mæw0 mæw01/0/2/2/0/2/2!00 mæw. mæw.0/0/2/1/0/2/1!// mæw+ mæw+//0/2/10/0/2/10!88 mæwA mæwA8/0/2/0/0/2/0!.. mæw) mæw)./0/2/7/0/2/7!55 mæw; mæw;5/0/2/6/0/2/6!44 mæw7 mæw74/0/2/5/0/2/5!33 mæw5 mæw53/0/2/4/0/2/4!22 mæw2 mæw22/0/2/9/0/2/9!77 mæw@ mæw@7/0/2/8/0/2/8!66 mæw> mæw>6/0/1/0/1! mævë mævë,/0/1/19/0/1/19!,, mæw% mæw%,/0/1/17/0/1/17!** mæw! mæw!*/0/1/18/0/1/18!++ mæw" mæw"+/0/1/15/0/1/15!(( mæw mæw(/0/1/16/0/1/16!)) mæw mæw)/0/1/13/0/1/13!&& mæw mæw&/0/1/14/0/1/14!'' mæw mæw'/0/1/11/0/1/11!$$ mæw mæw$/0/1/12/0/1/12!%% mæw mæw%/0/1/3/0/1/3! mæw mæw/0/1/2/0/1/2! mæw mæw/0/1/1/0/1/1! mævý mævý/0/1/10/0/1/10!## mæw mæw#/0/1/0/0/1/0! mævú mævú/0/1/7/0/1/7!  mæw  mæw /0/1/6/0/1/6! mæw  mæw /0/1/5/0/1/5! mæw mæw/0/1/4/0/1/4! mæw mæw/0/1/9/0/1/9!"" mæw mæw"/0/1/8/0/1/8!!! mæw mæw!/0/0/0/0! mæv¶ mæv¶/0/0/19/0/0/19! mævç mævç/0/0/17/0/0/17! mævã mævã/0/0/18/0/0/18! mævå mævå/0/0/15/0/0/15! mævß mævß/0/0/16/0/0/16! mævá mævá/0/0/13/0/0/13! mævÛ mævÛ/0/0/14/0/0/14! mævÝ mævÝ/0/0/11/0/0/11! mævÙ mævÙ/0/0/12/0/0/12! mævÚ mævÚ/0/0/3/0/0/3! mævÄ mævÄ/0/0/2/0/0/2! mæv mævÂ/0/0/1/0/0/1! mævÀ mævÀ/0/0/10/0/0/10! mævÕ mævÕ/0/0/0/0/0/0! mæv¾ mæv¾/0/0/7/0/0/7!  mævÎ mævÎ /0/0/6/0/0/6!  mævÌ mævÌ /0/0/5/0/0/5!  mævÊ mævÊ /0/0/4/0/0/4! mævÇ mævÇ/0/0/9/0/0/9!  mævÒ mævÒ /0/0/8/0/0/8!  mævÐ mævÐ /0/7/0/7!–– mæx mæxª/0/7/19/0/7/19!ªª mæx= mæx=ª/0/7/17/0/7/17!¨¨ mæx9 mæx9¨/0/7/18/0/7/18!©© mæx; mæx;©/0/7/15/0/7/15!¦¦ mæx6 mæx6¦/0/7/16/0/7/16!§§ mæx8 mæx8§/0/7/13/0/7/13!¤¤ mæx2 mæx2¤/0/7/14/0/7/14!¥¥ mæx5 mæx5¥/0/7/11/0/7/11!¢¢ mæx. mæx.¢/0/7/12/0/7/12!££ mæx0 mæx0£/0/7/3/0/7/3!šš mæx  mæx š/0/7/2/0/7/2!™™ mæx mæx™/0/7/1/0/7/1!˜˜ mæx mæx˜/0/7/10/0/7/10!¡¡ mæx- mæx-¡/0/7/0/0/7/0!—— mæx mæx—/0/7/7/0/7/7!žž mæx' mæx'ž/0/7/6/0/7/6! mæx% mæx%/0/7/5/0/7/5!œœ mæx$ mæx$œ/0/7/4/0/7/4!›› mæx" mæx"›/0/7/9/0/7/9!   mæx+ mæx+ /0/7/8/0/7/8!ŸŸ mæx) mæx)Ÿ/0/6/0/6! mæwæ mæwæ•/0/6/19/0/6/19!•• mæx mæx•/0/6/17/0/6/17!““ mæx mæx“/0/6/18/0/6/18!”” mæx mæx”/0/6/15/0/6/15!‘‘ mæx mæx‘/0/6/16/0/6/16!’’ mæx mæx’/0/6/13/0/6/13! mæx  mæx /0/6/14/0/6/14! mæx  mæx /0/6/11/0/6/11! mæx mæx/0/6/12/0/6/12!ŽŽ mæx  mæx Ž/0/6/3/0/6/3!…… mæwô mæwô…/0/6/2/0/6/2!„„ mæwñ mæwñ„/0/6/1/0/6/1!ƒƒ mæwì mæwìƒ/0/6/10/0/6/10!ŒŒ mæx mæxŒ/0/6/0/0/6/0!‚‚ mæwê mæwê‚/0/6/7/0/6/7!‰‰ mæx mæx‰/0/6/6/0/6/6!ˆˆ mæx mæxˆ/0/6/5/0/6/5!‡‡ mæwý mæwý‡/0/6/4/0/6/4!†† mæwú mæwú†/0/6/9/0/6/9!‹‹ mæx mæx‹/0/6/8/0/6/8!ŠŠ mæx mæxŠ/0/5/0/5!ll mæw· mæw·€/0/5/19/0/5/19!€€ mæwã mæwã€/0/5/17/0/5/17!~~ mæwß mæwß~/0/5/18/0/5/18! mæwá mæwá/0/5/15/0/5/15!|| mæwÜ mæwÜ|/0/5/16/0/5/16!}} mæwÝ mæwÝ}/0/5/13/0/5/13!zz mæwÖ mæwÖz/0/5/14/0/5/14!{{ mæwÙ mæwÙ{/0/5/11/0/5/11!xx mæwÎ mæwÎx/0/5/12/0/5/12!yy mæwÔ mæwÔy/0/5/3/0/5/3!pp mæw¿ mæw¿p/0/5/2/0/5/2!oo mæw¾ mæw¾o/0/5/1/0/5/1!nn mæw½ mæw½n/0/5/10/0/5/10!ww mæwÍ mæwÍw/0/5/0/0/5/0!mm mæwº mæwºm/0/5/7/0/5/7!tt mæwÉ mæwÉt/0/5/6/0/5/6!ss mæwÇ mæwÇs/0/5/5/0/5/5!rr mæwÄ mæwÄr/0/5/4/0/5/4!qq mæwÁ mæwÁq/0/5/9/0/5/9!vv mæwÌ mæwÌv/0/5/8/0/5/8!uu mæwÊ mæwÊu/0/4/0/4!WW mæw‡ mæw‡k/0/4/19/0/4/19!kk mæw´ mæw´k/0/4/17/0/4/17!ii mæw¯ mæw¯i/0/4/18/0/4/18!jj mæw± mæw±j/0/4/15/0/4/15!gg mæw¬ mæw¬g/0/4/16/0/4/16!hh mæw­ mæw­h/0/4/13/0/4/13!ee mæw© mæw©e/0/4/14/0/4/14!ff mæw« mæw«f/0/4/11/0/4/11!cc mæw  mæw c/0/4/12/0/4/12!dd mæw¤ mæw¤d/0/4/3/0/4/3![[ mæw mæw[/0/4/2/0/4/2!ZZ mæwŽ mæwŽZ/0/4/1/0/4/1!YY mæwŒ mæwŒY/0/4/10/0/4/10!bb mæw› mæw›b/0/4/0/0/4/0!XX mæw‹ mæw‹X/0/4/7/0/4/7!__ mæw— mæw—_/0/4/6/0/4/6!^^ mæw– mæw–^/0/4/5/0/4/5!]] mæw” mæw”]/0/4/4/0/4/4!\\ mæw’ mæw’\/0/4/9/0/4/9!aa mæwš mæwša/0/4/8/0/4/8!`` mæw™ mæw™`/0/9/0/9!ÀÀ mæxn mæxnÔ/0/9/19/0/9/19!ÔÔ mæx“ mæx“Ô/0/9/17/0/9/17!ÒÒ mæx‘ mæx‘Ò/0/9/18/0/9/18!ÓÓ mæx’ mæx’Ó/0/9/15/0/9/15!ÐÐ mæx mæxÐ/0/9/16/0/9/16!ÑÑ mæx mæxÑ/0/9/13/0/9/13!ÎÎ mæx† mæx†Î/0/9/14/0/9/14!ÏÏ mæx‹ mæx‹Ï/0/9/11/0/9/11!ÌÌ mæxƒ mæxƒÌ/0/9/12/0/9/12!ÍÍ mæx… mæx…Í/0/9/3/0/9/3!ÄÄ mæxw mæxwÄ/0/9/2/0/9/2!Ãà mæxu mæxuÃ/0/9/1/0/9/1! mæxt mæxtÂ/0/9/10/0/9/10!ËË mæx‚ mæx‚Ë/0/9/0/0/9/0!ÁÁ mæxr mæxrÁ/0/9/7/0/9/7!ÈÈ mæx| mæx|È/0/9/6/0/9/6!ÇÇ mæx{ mæx{Ç/0/9/5/0/9/5!ÆÆ mæxz mæxzÆ/0/9/4/0/9/4!ÅÅ mæxy mæxyÅ/0/9/9/0/9/9!ÊÊ mæx mæxÊ/0/9/8/0/9/8!ÉÉ mæx~ mæx~É/0/8/0/8!«« mæx? mæx?¿/0/8/19/0/8/19!¿¿ mæxm mæxm¿/0/8/17/0/8/17!½½ mæxj mæxj½/0/8/18/0/8/18!¾¾ mæxl mæxl¾/0/8/15/0/8/15!»» mæxf mæxf»/0/8/16/0/8/16!¼¼ mæxg mæxg¼/0/8/13/0/8/13!¹¹ mæx^ mæx^¹/0/8/14/0/8/14!ºº mæx` mæx`º/0/8/11/0/8/11!·· mæxW mæxW·/0/8/12/0/8/12!¸¸ mæxZ mæxZ¸/0/8/3/0/8/3!¯¯ mæxI mæxI¯/0/8/2/0/8/2!®® mæxD mæxD®/0/8/1/0/8/1!­­ mæxB mæxB­/0/8/10/0/8/10!¶¶ mæxV mæxV¶/0/8/0/0/8/0!¬¬ mæx@ mæx@¬/0/8/7/0/8/7!³³ mæxP mæxP³/0/8/6/0/8/6!²² mæxM mæxM²/0/8/5/0/8/5!±± mæxL mæxL±/0/8/4/0/8/4!°° mæxK mæxK°/0/8/9/0/8/9!µµ mæxT mæxTµ/0/8/8/0/8/8!´´ mæxS mæxS´/7ÿÿÿÿAs­èøAs­èø /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/r§u/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/snapshot.6390100644 0000000 0000000 00000427473 15051152474 033573 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿ mæu½N worldanyoneÿÿÿÿÿÿÿÿÇ/3/3!{{ mæ{Æ mæ{Æ 9/3/3/3/3!»» mæ| mæ|Ï/3/3/19/3/3/19!ÏÏ mæ|6 mæ|6Ï/3/3/17/3/3/17!ÍÍ mæ|5 mæ|5Í/3/3/18/3/3/18!ÎÎ mæ|6 mæ|6Î/3/3/15/3/3/15!ËË mæ|3 mæ|3Ë/3/3/16/3/3/16!ÌÌ mæ|4 mæ|4Ì/3/3/13/3/3/13!ÉÉ mæ|/ mæ|/É/3/3/14/3/3/14!ÊÊ mæ|1 mæ|1Ê/3/3/11/3/3/11!ÇÇ mæ|, mæ|,Ç/3/3/12/3/3/12!ÈÈ mæ|. mæ|.È/3/3/3/3/3/3!¿¿ mæ| mæ|¿/3/3/2/3/3/2!¾¾ mæ| mæ|¾/3/3/1/3/3/1!½½ mæ| mæ|½/3/3/10/3/3/10!ÆÆ mæ|+ mæ|+Æ/3/3/0/3/3/0!¼¼ mæ| mæ|¼/3/3/7/3/3/7!Ãà mæ|& mæ|&Ã/3/3/6/3/3/6! mæ| mæ|Â/3/3/5/3/3/5!ÁÁ mæ| mæ|Á/3/3/4/3/3/4!ÀÀ mæ| mæ|À/3/3/9/3/3/9!ÅÅ mæ|( mæ|(Å/3/3/8/3/3/8!ÄÄ mæ|' mæ|'Ä/3/2/3/2!¦¦ mæ{ý mæ{ýº/3/2/19/3/2/19!ºº mæ| mæ|º/3/2/17/3/2/17!¸¸ mæ| mæ|¸/3/2/18/3/2/18!¹¹ mæ| mæ|¹/3/2/15/3/2/15!¶¶ mæ| mæ|¶/3/2/16/3/2/16!·· mæ| mæ|·/3/2/13/3/2/13!´´ mæ| mæ|´/3/2/14/3/2/14!µµ mæ| mæ|µ/3/2/11/3/2/11!²² mæ|  mæ| ²/3/2/12/3/2/12!³³ mæ|  mæ| ³/3/2/3/3/2/3!ªª mæ| mæ|ª/3/2/2/3/2/2!©© mæ| mæ|©/3/2/1/3/2/1!¨¨ mæ{ÿ mæ{ÿ¨/3/2/10/3/2/10!±± mæ|  mæ| ±/3/2/0/3/2/0!§§ mæ{þ mæ{þ§/3/2/7/3/2/7!®® mæ| mæ|®/3/2/6/3/2/6!­­ mæ| mæ|­/3/2/5/3/2/5!¬¬ mæ| mæ|¬/3/2/4/3/2/4!«« mæ| mæ|«/3/2/9/3/2/9!°° mæ|  mæ| °/3/2/8/3/2/8!¯¯ mæ| mæ|¯/3/1/3/1!‘‘ mæ{á mæ{á¥/3/1/19/3/1/19!¥¥ mæ{ú mæ{ú¥/3/1/17/3/1/17!££ mæ{ø mæ{ø£/3/1/18/3/1/18!¤¤ mæ{ù mæ{ù¤/3/1/15/3/1/15!¡¡ mæ{ó mæ{ó¡/3/1/16/3/1/16!¢¢ mæ{õ mæ{õ¢/3/1/13/3/1/13!ŸŸ mæ{ð mæ{ðŸ/3/1/14/3/1/14!   mæ{ò mæ{ò /3/1/11/3/1/11! mæ{î mæ{î/3/1/12/3/1/12!žž mæ{ï mæ{ïž/3/1/3/3/1/3!•• mæ{ä mæ{ä•/3/1/2/3/1/2!”” mæ{ã mæ{ã”/3/1/1/3/1/1!““ mæ{ã mæ{ã“/3/1/10/3/1/10!œœ mæ{í mæ{íœ/3/1/0/3/1/0!’’ mæ{â mæ{â’/3/1/7/3/1/7!™™ mæ{ê mæ{ê™/3/1/6/3/1/6!˜˜ mæ{è mæ{è˜/3/1/5/3/1/5!—— mæ{ç mæ{ç—/3/1/4/3/1/4!–– mæ{å mæ{å–/3/1/9/3/1/9!›› mæ{ë mæ{ë›/3/1/8/3/1/8!šš mæ{ê mæ{êš/3/0/3/0!|| mæ{Æ mæ{Æ/3/0/19/3/0/19! mæ{ß mæ{ß/3/0/17/3/0/17!ŽŽ mæ{Ý mæ{ÝŽ/3/0/18/3/0/18! mæ{Þ mæ{Þ/3/0/15/3/0/15!ŒŒ mæ{Û mæ{ÛŒ/3/0/16/3/0/16! mæ{Ü mæ{Ü/3/0/13/3/0/13!ŠŠ mæ{Ø mæ{ØŠ/3/0/14/3/0/14!‹‹ mæ{Ú mæ{Ú‹/3/0/11/3/0/11!ˆˆ mæ{Ò mæ{Òˆ/3/0/12/3/0/12!‰‰ mæ{Ó mæ{Ó‰/3/0/3/3/0/3!€€ mæ{Ê mæ{Ê€/3/0/2/3/0/2! mæ{É mæ{É/3/0/1/3/0/1!~~ mæ{È mæ{È~/3/0/10/3/0/10!‡‡ mæ{Ñ mæ{ч/3/0/0/3/0/0!}} mæ{Ç mæ{Ç}/3/0/7/3/0/7!„„ mæ{Î mæ{΄/3/0/6/3/0/6!ƒƒ mæ{Í mæ{̓/3/0/5/3/0/5!‚‚ mæ{Ì mæ{Ì‚/3/0/4/3/0/4! mæ{Ë mæ{Ë/3/0/9/3/0/9!†† mæ{Ð mæ{І/3/0/8/3/0/8!…… mæ{Ï mæ{Ï…/3/7/3/7! mæ|¯ mæ|¯#/3/7/19/3/7/19!## mæ|Õ mæ|Õ#/3/7/17/3/7/17!!! mæ|Ô mæ|Ô!/3/7/18/3/7/18!"" mæ|Õ mæ|Õ"/3/7/15/3/7/15! mæ|Í mæ|Í/3/7/16/3/7/16!   mæ|Î mæ|Î /3/7/13/3/7/13! mæ|Ë mæ|Ë/3/7/14/3/7/14! mæ|Ì mæ|Ì/3/7/11/3/7/11! mæ|Ç mæ|Ç/3/7/12/3/7/12! mæ|È mæ|È/3/7/3/3/7/3! mæ|¶ mæ|¶/3/7/2/3/7/2! mæ|µ mæ|µ/3/7/1/3/7/1! mæ|´ mæ|´/3/7/10/3/7/10! mæ|Ä mæ|Ä/3/7/0/3/7/0! mæ|± mæ|±/3/7/7/3/7/7! mæ|¾ mæ|¾/3/7/6/3/7/6! mæ|½ mæ|½/3/7/5/3/7/5! mæ|º mæ|º/3/7/4/3/7/4! mæ|· mæ|·/3/7/9/3/7/9! mæ| mæ|Â/3/7/8/3/7/8! mæ|Á mæ|Á/3/6/3/6!úú mæ|Š mæ|Š/3/6/19/3/6/19! mæ|­ mæ|­/3/6/17/3/6/17!   mæ|« mæ|« /3/6/18/3/6/18!   mæ|¬ mæ|¬ /3/6/15/3/6/15!   mæ|¦ mæ|¦ /3/6/16/3/6/16!   mæ|¨ mæ|¨ /3/6/13/3/6/13! mæ|£ mæ|£/3/6/14/3/6/14!   mæ|¤ mæ|¤ /3/6/11/3/6/11! mæ|¡ mæ|¡/3/6/12/3/6/12! mæ|¡ mæ|¡/3/6/3/3/6/3!þþ mæ|• mæ|•þ/3/6/2/3/6/2!ýý mæ|’ mæ|’ý/3/6/1/3/6/1!üü mæ| mæ|ü/3/6/10/3/6/10! mæ|Ÿ mæ|Ÿ/3/6/0/3/6/0!ûû mæ| mæ|û/3/6/7/3/6/7! mæ|š mæ|š/3/6/6/3/6/6! mæ|™ mæ|™/3/6/5/3/6/5! mæ|– mæ|–/3/6/4/3/6/4!ÿÿ mæ|– mæ|–ÿ/3/6/9/3/6/9! mæ| mæ|/3/6/8/3/6/8! mæ|œ mæ|œ/3/5/3/5!åå mæ|V mæ|Vù/3/5/19/3/5/19!ùù mæ|‰ mæ|‰ù/3/5/17/3/5/17!÷÷ mæ|… mæ|…÷/3/5/18/3/5/18!øø mæ|‡ mæ|‡ø/3/5/15/3/5/15!õõ mæ|ƒ mæ|ƒõ/3/5/16/3/5/16!öö mæ|„ mæ|„ö/3/5/13/3/5/13!óó mæ| mæ|ó/3/5/14/3/5/14!ôô mæ|‚ mæ|‚ô/3/5/11/3/5/11!ññ mæ|z mæ|zñ/3/5/12/3/5/12!òò mæ| mæ|ò/3/5/3/3/5/3!éé mæ|] mæ|]é/3/5/2/3/5/2!èè mæ|[ mæ|[è/3/5/1/3/5/1!çç mæ|Z mæ|Zç/3/5/10/3/5/10!ðð mæ|y mæ|yð/3/5/0/3/5/0!ææ mæ|X mæ|Xæ/3/5/7/3/5/7!íí mæ|n mæ|ní/3/5/6/3/5/6!ìì mæ|g mæ|gì/3/5/5/3/5/5!ëë mæ|f mæ|fë/3/5/4/3/5/4!êê mæ|a mæ|aê/3/5/9/3/5/9!ïï mæ|v mæ|vï/3/5/8/3/5/8!îî mæ|s mæ|sî/3/4/3/4!ÐÐ mæ|8 mæ|8ä/3/4/19/3/4/19!ää mæ|U mæ|Uä/3/4/17/3/4/17!ââ mæ|R mæ|Râ/3/4/18/3/4/18!ãã mæ|S mæ|Sã/3/4/15/3/4/15!àà mæ|P mæ|Pà/3/4/16/3/4/16!áá mæ|Q mæ|Qá/3/4/13/3/4/13!ÞÞ mæ|N mæ|NÞ/3/4/14/3/4/14!ßß mæ|O mæ|Oß/3/4/11/3/4/11!ÜÜ mæ|J mæ|JÜ/3/4/12/3/4/12!ÝÝ mæ|K mæ|KÝ/3/4/3/3/4/3!ÔÔ mæ|> mæ|>Ô/3/4/2/3/4/2!ÓÓ mæ|< mæ|<Ó/3/4/1/3/4/1!ÒÒ mæ|; mæ|;Ò/3/4/10/3/4/10!ÛÛ mæ|I mæ|IÛ/3/4/0/3/4/0!ÑÑ mæ|: mæ|:Ñ/3/4/7/3/4/7!ØØ mæ|F mæ|FØ/3/4/6/3/4/6!×× mæ|E mæ|E×/3/4/5/3/4/5!ÖÖ mæ|D mæ|DÖ/3/4/4/3/4/4!ÕÕ mæ|B mæ|BÕ/3/4/9/3/4/9!ÚÚ mæ|H mæ|HÚ/3/4/8/3/4/8!ÙÙ mæ|G mæ|GÙ/3/9/3/9!99 mæ|ò mæ|òM/3/9/19/3/9/19!MM mæ} mæ}M/3/9/17/3/9/17!KK mæ}  mæ} K/3/9/18/3/9/18!LL mæ} mæ}L/3/9/15/3/9/15!II mæ}  mæ} I/3/9/16/3/9/16!JJ mæ}  mæ} J/3/9/13/3/9/13!GG mæ} mæ}G/3/9/14/3/9/14!HH mæ} mæ}H/3/9/11/3/9/11!EE mæ} mæ}E/3/9/12/3/9/12!FF mæ} mæ}F/3/9/3/3/9/3!== mæ|÷ mæ|÷=/3/9/2/3/9/2!<< mæ|ö mæ|ö</3/9/1/3/9/1!;; mæ|ô mæ|ô;/3/9/10/3/9/10!DD mæ} mæ}D/3/9/0/3/9/0!:: mæ|ó mæ|ó:/3/9/7/3/9/7!AA mæ|ü mæ|üA/3/9/6/3/9/6!@@ mæ|û mæ|û@/3/9/5/3/9/5!?? mæ|ù mæ|ù?/3/9/4/3/9/4!>> mæ|ø mæ|ø>/3/9/9/3/9/9!CC mæ} mæ}C/3/9/8/3/9/8!BB mæ|ý mæ|ýB/3/8/3/8!$$ mæ|Ö mæ|Ö8/3/8/19/3/8/19!88 mæ|ò mæ|ò8/3/8/17/3/8/17!66 mæ|ð mæ|ð6/3/8/18/3/8/18!77 mæ|ñ mæ|ñ7/3/8/15/3/8/15!44 mæ|î mæ|î4/3/8/16/3/8/16!55 mæ|ï mæ|ï5/3/8/13/3/8/13!22 mæ|ì mæ|ì2/3/8/14/3/8/14!33 mæ|í mæ|í3/3/8/11/3/8/11!00 mæ|é mæ|é0/3/8/12/3/8/12!11 mæ|ê mæ|ê1/3/8/3/3/8/3!(( mæ|Ü mæ|Ü(/3/8/2/3/8/2!'' mæ|Û mæ|Û'/3/8/1/3/8/1!&& mæ|Ú mæ|Ú&/3/8/10/3/8/10!// mæ|è mæ|è//3/8/0/3/8/0!%% mæ|Ù mæ|Ù%/3/8/7/3/8/7!,, mæ|à mæ|à,/3/8/6/3/8/6!++ mæ|ß mæ|ß+/3/8/5/3/8/5!** mæ|Þ mæ|Þ*/3/8/4/3/8/4!)) mæ|Ý mæ|Ý)/3/8/9/3/8/9!.. mæ|ç mæ|ç./3/8/8/3/8/8!-- mæ|æ mæ|æ-/2/2!¨¨ mæz mæz f/2/3/2/3!èè mæz“ mæz“ü/2/3/19/2/3/19!üü mæzñ mæzñü/2/3/17/2/3/17!úú mæzë mæzëú/2/3/18/2/3/18!ûû mæzï mæzïû/2/3/15/2/3/15!øø mæzè mæzèø/2/3/16/2/3/16!ùù mæzê mæzêù/2/3/13/2/3/13!öö mæzæ mæzæö/2/3/14/2/3/14!÷÷ mæzç mæzç÷/2/3/11/2/3/11!ôô mæzÒ mæzÒô/2/3/12/2/3/12!õõ mæzÝ mæzÝõ/2/3/3/2/3/3!ìì mæz› mæz›ì/2/3/2/2/3/2!ëë mæzš mæzšë/2/3/1/2/3/1!êê mæz™ mæz™ê/2/3/10/2/3/10!óó mæzµ mæzµó/2/3/0/2/3/0!éé mæz– mæz–é/2/3/7/2/3/7!ðð mæz« mæz«ð/2/3/6/2/3/6!ïï mæzª mæzªï/2/3/5/2/3/5!îî mæz¢ mæz¢î/2/3/4/2/3/4!íí mæzž mæzží/2/3/9/2/3/9!òò mæz¯ mæz¯ò/2/3/8/2/3/8!ññ mæz¬ mæz¬ñ/2/2/2/2!ÓÓ mæzv mæzvç/2/2/19/2/2/19!çç mæz‘ mæz‘ç/2/2/17/2/2/17!åå mæz mæzå/2/2/18/2/2/18!ææ mæz‘ mæz‘æ/2/2/15/2/2/15!ãã mæzŽ mæzŽã/2/2/16/2/2/16!ää mæz mæzä/2/2/13/2/2/13!áá mæz‹ mæz‹á/2/2/14/2/2/14!ââ mæz mæzâ/2/2/11/2/2/11!ßß mæz‰ mæz‰ß/2/2/12/2/2/12!àà mæzŠ mæzŠà/2/2/3/2/2/3!×× mæz| mæz|×/2/2/2/2/2/2!ÖÖ mæzz mæzzÖ/2/2/1/2/2/1!ÕÕ mæzy mæzyÕ/2/2/10/2/2/10!ÞÞ mæzˆ mæzˆÞ/2/2/0/2/2/0!ÔÔ mæzw mæzwÔ/2/2/7/2/2/7!ÛÛ mæz‚ mæz‚Û/2/2/6/2/2/6!ÚÚ mæz€ mæz€Ú/2/2/5/2/2/5!ÙÙ mæz mæzÙ/2/2/4/2/2/4!ØØ mæz} mæz}Ø/2/2/9/2/2/9!ÝÝ mæz… mæz…Ý/2/2/8/2/2/8!ÜÜ mæzƒ mæzƒÜ/2/1/2/1!¾¾ mæzZ mæzZÒ/2/1/19/2/1/19!ÒÒ mæzu mæzuÒ/2/1/17/2/1/17!ÐÐ mæzs mæzsÐ/2/1/18/2/1/18!ÑÑ mæzt mæztÑ/2/1/15/2/1/15!ÎÎ mæzq mæzqÎ/2/1/16/2/1/16!ÏÏ mæzr mæzrÏ/2/1/13/2/1/13!ÌÌ mæzl mæzlÌ/2/1/14/2/1/14!ÍÍ mæzn mæznÍ/2/1/11/2/1/11!ÊÊ mæzh mæzhÊ/2/1/12/2/1/12!ËË mæzi mæziË/2/1/3/2/1/3! mæz_ mæz_Â/2/1/2/2/1/2!ÁÁ mæz^ mæz^Á/2/1/1/2/1/1!ÀÀ mæz] mæz]À/2/1/10/2/1/10!ÉÉ mæzg mæzgÉ/2/1/0/2/1/0!¿¿ mæz[ mæz[¿/2/1/7/2/1/7!ÆÆ mæze mæzeÆ/2/1/6/2/1/6!ÅÅ mæzc mæzcÅ/2/1/5/2/1/5!ÄÄ mæzb mæzbÄ/2/1/4/2/1/4!Ãà mæz` mæz`Ã/2/1/9/2/1/9!ÈÈ mæzg mæzgÈ/2/1/8/2/1/8!ÇÇ mæzf mæzfÇ/2/0/2/0!©© mæz mæz½/2/0/19/2/0/19!½½ mæzY mæzY½/2/0/17/2/0/17!»» mæzN mæzN»/2/0/18/2/0/18!¼¼ mæzW mæzW¼/2/0/15/2/0/15!¹¹ mæzE mæzE¹/2/0/16/2/0/16!ºº mæzJ mæzJº/2/0/13/2/0/13!·· mæzA mæzA·/2/0/14/2/0/14!¸¸ mæzC mæzC¸/2/0/11/2/0/11!µµ mæz? mæz?µ/2/0/12/2/0/12!¶¶ mæz@ mæz@¶/2/0/3/2/0/3!­­ mæz mæz­/2/0/2/2/0/2!¬¬ mæz mæz¬/2/0/1/2/0/1!«« mæz mæz«/2/0/10/2/0/10!´´ mæz> mæz>´/2/0/0/2/0/0!ªª mæz mæzª/2/0/7/2/0/7!±± mæz" mæz"±/2/0/6/2/0/6!°° mæz! mæz!°/2/0/5/2/0/5!¯¯ mæz  mæz ¯/2/0/4/2/0/4!®® mæz mæz®/2/0/9/2/0/9!³³ mæz< mæz<³/2/0/8/2/0/8!²² mæz: mæz:²/2/7/2/7!<< mæ{c mæ{cP/2/7/19/2/7/19!PP mæ{z mæ{zP/2/7/17/2/7/17!NN mæ{w mæ{wN/2/7/18/2/7/18!OO mæ{y mæ{yO/2/7/15/2/7/15!LL mæ{v mæ{vL/2/7/16/2/7/16!MM mæ{w mæ{wM/2/7/13/2/7/13!JJ mæ{t mæ{tJ/2/7/14/2/7/14!KK mæ{u mæ{uK/2/7/11/2/7/11!HH mæ{r mæ{rH/2/7/12/2/7/12!II mæ{s mæ{sI/2/7/3/2/7/3!@@ mæ{h mæ{h@/2/7/2/2/7/2!?? mæ{g mæ{g?/2/7/1/2/7/1!>> mæ{e mæ{e>/2/7/10/2/7/10!GG mæ{q mæ{qG/2/7/0/2/7/0!== mæ{d mæ{d=/2/7/7/2/7/7!DD mæ{m mæ{mD/2/7/6/2/7/6!CC mæ{k mæ{kC/2/7/5/2/7/5!BB mæ{k mæ{kB/2/7/4/2/7/4!AA mæ{j mæ{jA/2/7/9/2/7/9!FF mæ{o mæ{oF/2/7/8/2/7/8!EE mæ{n mæ{nE/2/6/2/6!'' mæ{; mæ{;;/2/6/19/2/6/19!;; mæ{b mæ{b;/2/6/17/2/6/17!99 mæ{^ mæ{^9/2/6/18/2/6/18!:: mæ{a mæ{a:/2/6/15/2/6/15!77 mæ{[ mæ{[7/2/6/16/2/6/16!88 mæ{] mæ{]8/2/6/13/2/6/13!55 mæ{Y mæ{Y5/2/6/14/2/6/14!66 mæ{Z mæ{Z6/2/6/11/2/6/11!33 mæ{T mæ{T3/2/6/12/2/6/12!44 mæ{X mæ{X4/2/6/3/2/6/3!++ mæ{C mæ{C+/2/6/2/2/6/2!** mæ{A mæ{A*/2/6/1/2/6/1!)) mæ{? mæ{?)/2/6/10/2/6/10!22 mæ{S mæ{S2/2/6/0/2/6/0!(( mæ{= mæ{=(/2/6/7/2/6/7!// mæ{H mæ{H//2/6/6/2/6/6!.. mæ{G mæ{G./2/6/5/2/6/5!-- mæ{E mæ{E-/2/6/4/2/6/4!,, mæ{D mæ{D,/2/6/9/2/6/9!11 mæ{Q mæ{Q1/2/6/8/2/6/8!00 mæ{O mæ{O0/2/5/2/5! mæ{ mæ{&/2/5/19/2/5/19!&& mæ{9 mæ{9&/2/5/17/2/5/17!$$ mæ{4 mæ{4$/2/5/18/2/5/18!%% mæ{7 mæ{7%/2/5/15/2/5/15!"" mæ{2 mæ{2"/2/5/16/2/5/16!## mæ{3 mæ{3#/2/5/13/2/5/13!   mæ{. mæ{. /2/5/14/2/5/14!!! mæ{0 mæ{0!/2/5/11/2/5/11! mæ{* mæ{*/2/5/12/2/5/12! mæ{, mæ{,/2/5/3/2/5/3! mæ{ mæ{/2/5/2/2/5/2! mæ{ mæ{/2/5/1/2/5/1! mæ{ mæ{/2/5/10/2/5/10! mæ{) mæ{)/2/5/0/2/5/0! mæ{ mæ{/2/5/7/2/5/7! mæ{& mæ{&/2/5/6/2/5/6! mæ{$ mæ{$/2/5/5/2/5/5! mæ{" mæ{"/2/5/4/2/5/4! mæ{ mæ{/2/5/9/2/5/9! mæ{' mæ{'/2/5/8/2/5/8! mæ{& mæ{&/2/4/2/4!ýý mæzò mæzò/2/4/19/2/4/19! mæ{ mæ{/2/4/17/2/4/17! mæ{ mæ{/2/4/18/2/4/18! mæ{ mæ{/2/4/15/2/4/15!   mæ{ mæ{ /2/4/16/2/4/16! mæ{ mæ{/2/4/13/2/4/13!   mæ{ mæ{ /2/4/14/2/4/14!   mæ{ mæ{ /2/4/11/2/4/11!   mæ{  mæ{  /2/4/12/2/4/12!   mæ{ mæ{ /2/4/3/2/4/3! mæzú mæzú/2/4/2/2/4/2! mæzø mæzø/2/4/1/2/4/1!ÿÿ mæzõ mæzõÿ/2/4/10/2/4/10! mæ{ mæ{/2/4/0/2/4/0!þþ mæzô mæzôþ/2/4/7/2/4/7! mæ{ mæ{/2/4/6/2/4/6! mæzÿ mæzÿ/2/4/5/2/4/5! mæzý mæzý/2/4/4/2/4/4! mæzû mæzû/2/4/9/2/4/9! mæ{ mæ{/2/4/8/2/4/8! mæ{ mæ{/2/9/2/9!ff mæ{• mæ{•z/2/9/19/2/9/19!zz mæ{Å mæ{Åz/2/9/17/2/9/17!xx mæ{ mæ{Âx/2/9/18/2/9/18!yy mæ{Ä mæ{Äy/2/9/15/2/9/15!vv mæ{¿ mæ{¿v/2/9/16/2/9/16!ww mæ{Á mæ{Áw/2/9/13/2/9/13!tt mæ{¹ mæ{¹t/2/9/14/2/9/14!uu mæ{½ mæ{½u/2/9/11/2/9/11!rr mæ{¢ mæ{¢r/2/9/12/2/9/12!ss mæ{¤ mæ{¤s/2/9/3/2/9/3!jj mæ{š mæ{šj/2/9/2/2/9/2!ii mæ{™ mæ{™i/2/9/1/2/9/1!hh mæ{— mæ{—h/2/9/10/2/9/10!qq mæ{¡ mæ{¡q/2/9/0/2/9/0!gg mæ{– mæ{–g/2/9/7/2/9/7!nn mæ{ž mæ{žn/2/9/6/2/9/6!mm mæ{ mæ{m/2/9/5/2/9/5!ll mæ{› mæ{›l/2/9/4/2/9/4!kk mæ{š mæ{šk/2/9/9/2/9/9!pp mæ{  mæ{ p/2/9/8/2/9/8!oo mæ{Ÿ mæ{Ÿo/2/8/2/8!QQ mæ{{ mæ{{e/2/8/19/2/8/19!ee mæ{“ mæ{“e/2/8/17/2/8/17!cc mæ{‘ mæ{‘c/2/8/18/2/8/18!dd mæ{’ mæ{’d/2/8/15/2/8/15!aa mæ{ mæ{a/2/8/16/2/8/16!bb mæ{ mæ{b/2/8/13/2/8/13!__ mæ{Œ mæ{Œ_/2/8/14/2/8/14!`` mæ{ mæ{`/2/8/11/2/8/11!]] mæ{‰ mæ{‰]/2/8/12/2/8/12!^^ mæ{Š mæ{Š^/2/8/3/2/8/3!UU mæ{€ mæ{€U/2/8/2/2/8/2!TT mæ{ mæ{T/2/8/1/2/8/1!SS mæ{~ mæ{~S/2/8/10/2/8/10!\\ mæ{ˆ mæ{ˆ\/2/8/0/2/8/0!RR mæ{} mæ{}R/2/8/7/2/8/7!YY mæ{… mæ{…Y/2/8/6/2/8/6!XX mæ{ƒ mæ{ƒX/2/8/5/2/8/5!WW mæ{‚ mæ{‚W/2/8/4/2/8/4!VV mæ{ mæ{V/2/8/9/2/8/9![[ mæ{‡ mæ{‡[/2/8/8/2/8/8!ZZ mæ{† mæ{†Z/1/1!ÕÕ mæx” mæx” “/1/3/1/3! mæy mæy)/1/3/19/1/3/19!)) mæy0 mæy0)/1/3/17/1/3/17!'' mæy+ mæy+'/1/3/18/1/3/18!(( mæy, mæy,(/1/3/15/1/3/15!%% mæy( mæy(%/1/3/16/1/3/16!&& mæy) mæy)&/1/3/13/1/3/13!## mæy& mæy&#/1/3/14/1/3/14!$$ mæy' mæy'$/1/3/11/1/3/11!!! mæy$ mæy$!/1/3/12/1/3/12!"" mæy% mæy%"/1/3/3/1/3/3! mæy mæy/1/3/2/1/3/2! mæy mæy/1/3/1/1/3/1! mæy mæy/1/3/10/1/3/10!   mæy! mæy! /1/3/0/1/3/0! mæy mæy/1/3/7/1/3/7! mæy mæy/1/3/6/1/3/6! mæy mæy/1/3/5/1/3/5! mæy mæy/1/3/4/1/3/4! mæy mæy/1/3/9/1/3/9! mæy  mæy /1/3/8/1/3/8! mæy mæy/1/2/1/2! mæxð mæxð/1/2/19/1/2/19! mæy mæy/1/2/17/1/2/17! mæy  mæy /1/2/18/1/2/18! mæy mæy/1/2/15/1/2/15! mæy  mæy /1/2/16/1/2/16! mæy  mæy /1/2/13/1/2/13! mæy  mæy /1/2/14/1/2/14! mæy  mæy /1/2/11/1/2/11!   mæy mæy /1/2/12/1/2/12!   mæy mæy /1/2/3/1/2/3! mæxø mæxø/1/2/2/1/2/2! mæx÷ mæx÷/1/2/1/1/2/1! mæxõ mæxõ/1/2/10/1/2/10!   mæy mæy /1/2/0/1/2/0! mæxò mæxò/1/2/7/1/2/7! mæy mæy/1/2/6/1/2/6! mæy mæy/1/2/5/1/2/5! mæxÿ mæxÿ/1/2/4/1/2/4! mæxþ mæxþ/1/2/9/1/2/9!   mæy mæy /1/2/8/1/2/8!   mæy mæy /1/1/1/1!ëë mæx¼ mæx¼ÿ/1/1/19/1/1/19!ÿÿ mæxè mæxèÿ/1/1/17/1/1/17!ýý mæxå mæxåý/1/1/18/1/1/18!þþ mæxæ mæxæþ/1/1/15/1/1/15!ûû mæxá mæxáû/1/1/16/1/1/16!üü mæxã mæxãü/1/1/13/1/1/13!ùù mæxÞ mæxÞù/1/1/14/1/1/14!úú mæxß mæxßú/1/1/11/1/1/11!÷÷ mæxÖ mæxÖ÷/1/1/12/1/1/12!øø mæxØ mæxØø/1/1/3/1/1/3!ïï mæxà mæxÃï/1/1/2/1/1/2!îî mæx mæxÂî/1/1/1/1/1/1!íí mæx¿ mæx¿í/1/1/10/1/1/10!öö mæxÓ mæxÓö/1/1/0/1/1/0!ìì mæx¾ mæx¾ì/1/1/7/1/1/7!óó mæxÎ mæxÎó/1/1/6/1/1/6!òò mæxÇ mæxÇò/1/1/5/1/1/5!ññ mæxÅ mæxÅñ/1/1/4/1/1/4!ðð mæxÅ mæxÅð/1/1/9/1/1/9!õõ mæxÑ mæxÑõ/1/1/8/1/1/8!ôô mæxÏ mæxÏô/1/0/1/0!ÖÖ mæx• mæx•ê/1/0/19/1/0/19!êê mæxº mæxºê/1/0/17/1/0/17!èè mæx¶ mæx¶è/1/0/18/1/0/18!éé mæx¸ mæx¸é/1/0/15/1/0/15!ææ mæx² mæx²æ/1/0/16/1/0/16!çç mæxµ mæxµç/1/0/13/1/0/13!ää mæx¯ mæx¯ä/1/0/14/1/0/14!åå mæx± mæx±å/1/0/11/1/0/11!ââ mæx­ mæx­â/1/0/12/1/0/12!ãã mæx® mæx®ã/1/0/3/1/0/3!ÚÚ mæx mæxÚ/1/0/2/1/0/2!ÙÙ mæxš mæxšÙ/1/0/1/1/0/1!ØØ mæx— mæx—Ø/1/0/10/1/0/10!áá mæx¬ mæx¬á/1/0/0/1/0/0!×× mæx– mæx–×/1/0/7/1/0/7!ÞÞ mæx¦ mæx¦Þ/1/0/6/1/0/6!ÝÝ mæx¤ mæx¤Ý/1/0/5/1/0/5!ÜÜ mæx¢ mæx¢Ü/1/0/4/1/0/4!ÛÛ mæxŸ mæxŸÛ/1/0/9/1/0/9!àà mæxª mæxªà/1/0/8/1/0/8!ßß mæx© mæx©ß/1/7/1/7!ii mæy§ mæy§}/1/7/19/1/7/19!}} mæyÚ mæyÚ}/1/7/17/1/7/17!{{ mæyÓ mæyÓ{/1/7/18/1/7/18!|| mæyÕ mæyÕ|/1/7/15/1/7/15!yy mæyÌ mæyÌy/1/7/16/1/7/16!zz mæyÏ mæyÏz/1/7/13/1/7/13!ww mæy mæyÂw/1/7/14/1/7/14!xx mæyÊ mæyÊx/1/7/11/1/7/11!uu mæyº mæyºu/1/7/12/1/7/12!vv mæy¾ mæy¾v/1/7/3/1/7/3!mm mæy¬ mæy¬m/1/7/2/1/7/2!ll mæy« mæy«l/1/7/1/1/7/1!kk mæyª mæyªk/1/7/10/1/7/10!tt mæy· mæy·t/1/7/0/1/7/0!jj mæy© mæy©j/1/7/7/1/7/7!qq mæy³ mæy³q/1/7/6/1/7/6!pp mæy² mæy²p/1/7/5/1/7/5!oo mæy¯ mæy¯o/1/7/4/1/7/4!nn mæy® mæy®n/1/7/9/1/7/9!ss mæy¶ mæy¶s/1/7/8/1/7/8!rr mæy´ mæy´r/1/6/1/6!TT mæyz mæyzh/1/6/19/1/6/19!hh mæy¦ mæy¦h/1/6/17/1/6/17!ff mæy¢ mæy¢f/1/6/18/1/6/18!gg mæy¤ mæy¤g/1/6/15/1/6/15!dd mæyŸ mæyŸd/1/6/16/1/6/16!ee mæy¡ mæy¡e/1/6/13/1/6/13!bb mæyœ mæyœb/1/6/14/1/6/14!cc mæy mæyc/1/6/11/1/6/11!`` mæyš mæyš`/1/6/12/1/6/12!aa mæy› mæy›a/1/6/3/1/6/3!XX mæyƒ mæyƒX/1/6/2/1/6/2!WW mæy‚ mæy‚W/1/6/1/1/6/1!VV mæy mæyV/1/6/10/1/6/10!__ mæy™ mæy™_/1/6/0/1/6/0!UU mæy€ mæy€U/1/6/7/1/6/7!\\ mæy• mæy•\/1/6/6/1/6/6![[ mæy mæy[/1/6/5/1/6/5!ZZ mæyŽ mæyŽZ/1/6/4/1/6/4!YY mæy… mæy…Y/1/6/9/1/6/9!^^ mæy— mæy—^/1/6/8/1/6/8!]] mæy– mæy–]/1/5/1/5!?? mæy^ mæy^S/1/5/19/1/5/19!SS mæyy mæyyS/1/5/17/1/5/17!QQ mæyw mæywQ/1/5/18/1/5/18!RR mæyx mæyxR/1/5/15/1/5/15!OO mæyt mæytO/1/5/16/1/5/16!PP mæyv mæyvP/1/5/13/1/5/13!MM mæyp mæypM/1/5/14/1/5/14!NN mæyq mæyqN/1/5/11/1/5/11!KK mæyn mæynK/1/5/12/1/5/12!LL mæyo mæyoL/1/5/3/1/5/3!CC mæyd mæydC/1/5/2/1/5/2!BB mæyc mæycB/1/5/1/1/5/1!AA mæyb mæybA/1/5/10/1/5/10!JJ mæym mæymJ/1/5/0/1/5/0!@@ mæy_ mæy_@/1/5/7/1/5/7!GG mæyj mæyjG/1/5/6/1/5/6!FF mæyg mæygF/1/5/5/1/5/5!EE mæyf mæyfE/1/5/4/1/5/4!DD mæye mæyeD/1/5/9/1/5/9!II mæyl mæylI/1/5/8/1/5/8!HH mæyk mæykH/1/4/1/4!** mæy1 mæy1>/1/4/19/1/4/19!>> mæy\ mæy\>/1/4/17/1/4/17!<< mæyR mæyR</1/4/18/1/4/18!== mæyY mæyY=/1/4/15/1/4/15!:: mæyO mæyO:/1/4/16/1/4/16!;; mæyQ mæyQ;/1/4/13/1/4/13!88 mæyM mæyM8/1/4/14/1/4/14!99 mæyN mæyN9/1/4/11/1/4/11!66 mæyI mæyI6/1/4/12/1/4/12!77 mæyJ mæyJ7/1/4/3/1/4/3!.. mæy; mæy;./1/4/2/1/4/2!-- mæy: mæy:-/1/4/1/1/4/1!,, mæy8 mæy8,/1/4/10/1/4/10!55 mæyH mæyH5/1/4/0/1/4/0!++ mæy3 mæy3+/1/4/7/1/4/7!22 mæyB mæyB2/1/4/6/1/4/6!11 mæyA mæyA1/1/4/5/1/4/5!00 mæy> mæy>0/1/4/4/1/4/4!// mæy= mæy=//1/4/9/1/4/9!44 mæyG mæyG4/1/4/8/1/4/8!33 mæyE mæyE3/1/9/1/9!““ mæyû mæyû§/1/9/19/1/9/19!§§ mæz mæz§/1/9/17/1/9/17!¥¥ mæz mæz¥/1/9/18/1/9/18!¦¦ mæz mæz¦/1/9/15/1/9/15!££ mæz mæz£/1/9/16/1/9/16!¤¤ mæz mæz¤/1/9/13/1/9/13!¡¡ mæz  mæz ¡/1/9/14/1/9/14!¢¢ mæz mæz¢/1/9/11/1/9/11!ŸŸ mæz  mæz Ÿ/1/9/12/1/9/12!   mæz  mæz  /1/9/3/1/9/3!—— mæyÿ mæyÿ—/1/9/2/1/9/2!–– mæyþ mæyþ–/1/9/1/1/9/1!•• mæyý mæyý•/1/9/10/1/9/10!žž mæz mæzž/1/9/0/1/9/0!”” mæyû mæyû”/1/9/7/1/9/7!›› mæz mæz›/1/9/6/1/9/6!šš mæz mæzš/1/9/5/1/9/5!™™ mæz mæz™/1/9/4/1/9/4!˜˜ mæz mæz˜/1/9/9/1/9/9! mæz mæz/1/9/8/1/9/8!œœ mæz mæzœ/1/8/1/8!~~ mæyÜ mæyÜ’/1/8/19/1/8/19!’’ mæyú mæyú’/1/8/17/1/8/17! mæyø mæyø/1/8/18/1/8/18!‘‘ mæyù mæyù‘/1/8/15/1/8/15!ŽŽ mæyö mæyöŽ/1/8/16/1/8/16! mæy÷ mæy÷/1/8/13/1/8/13!ŒŒ mæyñ mæyñŒ/1/8/14/1/8/14! mæyó mæyó/1/8/11/1/8/11!ŠŠ mæyï mæyïŠ/1/8/12/1/8/12!‹‹ mæyð mæyð‹/1/8/3/1/8/3!‚‚ mæyâ mæyâ‚/1/8/2/1/8/2! mæyâ mæyâ/1/8/1/1/8/1!€€ mæyà mæyà€/1/8/10/1/8/10!‰‰ mæyî mæyî‰/1/8/0/1/8/0! mæyÞ mæyÞ/1/8/7/1/8/7!†† mæyæ mæyæ†/1/8/6/1/8/6!…… mæyå mæyå…/1/8/5/1/8/5!„„ mæyä mæyä„/1/8/4/1/8/4!ƒƒ mæyã mæyãƒ/1/8/9/1/8/9!ˆˆ mæyë mæyëˆ/1/8/8/1/8/8!‡‡ mæyê mæyê‡/0/0! mæv« mæv« À/0/3/0/3!BB mæwS mæwSV/0/3/19/0/3/19!VV mæw… mæw…V/0/3/17/0/3/17!TT mæw‚ mæw‚T/0/3/18/0/3/18!UU mæwƒ mæwƒU/0/3/15/0/3/15!RR mæw~ mæw~R/0/3/16/0/3/16!SS mæw€ mæw€S/0/3/13/0/3/13!PP mæw{ mæw{P/0/3/14/0/3/14!QQ mæw} mæw}Q/0/3/11/0/3/11!NN mæwv mæwvN/0/3/12/0/3/12!OO mæwy mæwyO/0/3/3/0/3/3!FF mæw^ mæw^F/0/3/2/0/3/2!EE mæw] mæw]E/0/3/1/0/3/1!DD mæw[ mæw[D/0/3/10/0/3/10!MM mæwq mæwqM/0/3/0/0/3/0!CC mæwX mæwXC/0/3/7/0/3/7!JJ mæwj mæwjJ/0/3/6/0/3/6!II mæwi mæwiI/0/3/5/0/3/5!HH mæwh mæwhH/0/3/4/0/3/4!GG mæwf mæwfG/0/3/9/0/3/9!LL mæwo mæwoL/0/3/8/0/3/8!KK mæwm mæwmK/0/2/0/2!-- mæw' mæw'A/0/2/19/0/2/19!AA mæwQ mæwQA/0/2/17/0/2/17!?? mæwM mæwM?/0/2/18/0/2/18!@@ mæwO mæwO@/0/2/15/0/2/15!== mæwI mæwI=/0/2/16/0/2/16!>> mæwJ mæwJ>/0/2/13/0/2/13!;; mæwF mæwF;/0/2/14/0/2/14!<< mæwG mæwG</0/2/11/0/2/11!99 mæwB mæwB9/0/2/12/0/2/12!:: mæwD mæwD:/0/2/3/0/2/3!11 mæw0 mæw01/0/2/2/0/2/2!00 mæw. mæw.0/0/2/1/0/2/1!// mæw+ mæw+//0/2/10/0/2/10!88 mæwA mæwA8/0/2/0/0/2/0!.. mæw) mæw)./0/2/7/0/2/7!55 mæw; mæw;5/0/2/6/0/2/6!44 mæw7 mæw74/0/2/5/0/2/5!33 mæw5 mæw53/0/2/4/0/2/4!22 mæw2 mæw22/0/2/9/0/2/9!77 mæw@ mæw@7/0/2/8/0/2/8!66 mæw> mæw>6/0/1/0/1! mævë mævë,/0/1/19/0/1/19!,, mæw% mæw%,/0/1/17/0/1/17!** mæw! mæw!*/0/1/18/0/1/18!++ mæw" mæw"+/0/1/15/0/1/15!(( mæw mæw(/0/1/16/0/1/16!)) mæw mæw)/0/1/13/0/1/13!&& mæw mæw&/0/1/14/0/1/14!'' mæw mæw'/0/1/11/0/1/11!$$ mæw mæw$/0/1/12/0/1/12!%% mæw mæw%/0/1/3/0/1/3! mæw mæw/0/1/2/0/1/2! mæw mæw/0/1/1/0/1/1! mævý mævý/0/1/10/0/1/10!## mæw mæw#/0/1/0/0/1/0! mævú mævú/0/1/7/0/1/7!  mæw  mæw /0/1/6/0/1/6! mæw  mæw /0/1/5/0/1/5! mæw mæw/0/1/4/0/1/4! mæw mæw/0/1/9/0/1/9!"" mæw mæw"/0/1/8/0/1/8!!! mæw mæw!/0/0/0/0! mæv¶ mæv¶/0/0/19/0/0/19! mævç mævç/0/0/17/0/0/17! mævã mævã/0/0/18/0/0/18! mævå mævå/0/0/15/0/0/15! mævß mævß/0/0/16/0/0/16! mævá mævá/0/0/13/0/0/13! mævÛ mævÛ/0/0/14/0/0/14! mævÝ mævÝ/0/0/11/0/0/11! mævÙ mævÙ/0/0/12/0/0/12! mævÚ mævÚ/0/0/3/0/0/3! mævÄ mævÄ/0/0/2/0/0/2! mæv mævÂ/0/0/1/0/0/1! mævÀ mævÀ/0/0/10/0/0/10! mævÕ mævÕ/0/0/0/0/0/0! mæv¾ mæv¾/0/0/7/0/0/7!  mævÎ mævÎ /0/0/6/0/0/6!  mævÌ mævÌ /0/0/5/0/0/5!  mævÊ mævÊ /0/0/4/0/0/4! mævÇ mævÇ/0/0/9/0/0/9!  mævÒ mævÒ /0/0/8/0/0/8!  mævÐ mævÐ /0/7/0/7!–– mæx mæxª/0/7/19/0/7/19!ªª mæx= mæx=ª/0/7/17/0/7/17!¨¨ mæx9 mæx9¨/0/7/18/0/7/18!©© mæx; mæx;©/0/7/15/0/7/15!¦¦ mæx6 mæx6¦/0/7/16/0/7/16!§§ mæx8 mæx8§/0/7/13/0/7/13!¤¤ mæx2 mæx2¤/0/7/14/0/7/14!¥¥ mæx5 mæx5¥/0/7/11/0/7/11!¢¢ mæx. mæx.¢/0/7/12/0/7/12!££ mæx0 mæx0£/0/7/3/0/7/3!šš mæx  mæx š/0/7/2/0/7/2!™™ mæx mæx™/0/7/1/0/7/1!˜˜ mæx mæx˜/0/7/10/0/7/10!¡¡ mæx- mæx-¡/0/7/0/0/7/0!—— mæx mæx—/0/7/7/0/7/7!žž mæx' mæx'ž/0/7/6/0/7/6! mæx% mæx%/0/7/5/0/7/5!œœ mæx$ mæx$œ/0/7/4/0/7/4!›› mæx" mæx"›/0/7/9/0/7/9!   mæx+ mæx+ /0/7/8/0/7/8!ŸŸ mæx) mæx)Ÿ/0/6/0/6! mæwæ mæwæ•/0/6/19/0/6/19!•• mæx mæx•/0/6/17/0/6/17!““ mæx mæx“/0/6/18/0/6/18!”” mæx mæx”/0/6/15/0/6/15!‘‘ mæx mæx‘/0/6/16/0/6/16!’’ mæx mæx’/0/6/13/0/6/13! mæx  mæx /0/6/14/0/6/14! mæx  mæx /0/6/11/0/6/11! mæx mæx/0/6/12/0/6/12!ŽŽ mæx  mæx Ž/0/6/3/0/6/3!…… mæwô mæwô…/0/6/2/0/6/2!„„ mæwñ mæwñ„/0/6/1/0/6/1!ƒƒ mæwì mæwìƒ/0/6/10/0/6/10!ŒŒ mæx mæxŒ/0/6/0/0/6/0!‚‚ mæwê mæwê‚/0/6/7/0/6/7!‰‰ mæx mæx‰/0/6/6/0/6/6!ˆˆ mæx mæxˆ/0/6/5/0/6/5!‡‡ mæwý mæwý‡/0/6/4/0/6/4!†† mæwú mæwú†/0/6/9/0/6/9!‹‹ mæx mæx‹/0/6/8/0/6/8!ŠŠ mæx mæxŠ/0/5/0/5!ll mæw· mæw·€/0/5/19/0/5/19!€€ mæwã mæwã€/0/5/17/0/5/17!~~ mæwß mæwß~/0/5/18/0/5/18! mæwá mæwá/0/5/15/0/5/15!|| mæwÜ mæwÜ|/0/5/16/0/5/16!}} mæwÝ mæwÝ}/0/5/13/0/5/13!zz mæwÖ mæwÖz/0/5/14/0/5/14!{{ mæwÙ mæwÙ{/0/5/11/0/5/11!xx mæwÎ mæwÎx/0/5/12/0/5/12!yy mæwÔ mæwÔy/0/5/3/0/5/3!pp mæw¿ mæw¿p/0/5/2/0/5/2!oo mæw¾ mæw¾o/0/5/1/0/5/1!nn mæw½ mæw½n/0/5/10/0/5/10!ww mæwÍ mæwÍw/0/5/0/0/5/0!mm mæwº mæwºm/0/5/7/0/5/7!tt mæwÉ mæwÉt/0/5/6/0/5/6!ss mæwÇ mæwÇs/0/5/5/0/5/5!rr mæwÄ mæwÄr/0/5/4/0/5/4!qq mæwÁ mæwÁq/0/5/9/0/5/9!vv mæwÌ mæwÌv/0/5/8/0/5/8!uu mæwÊ mæwÊu/0/4/0/4!WW mæw‡ mæw‡k/0/4/19/0/4/19!kk mæw´ mæw´k/0/4/17/0/4/17!ii mæw¯ mæw¯i/0/4/18/0/4/18!jj mæw± mæw±j/0/4/15/0/4/15!gg mæw¬ mæw¬g/0/4/16/0/4/16!hh mæw­ mæw­h/0/4/13/0/4/13!ee mæw© mæw©e/0/4/14/0/4/14!ff mæw« mæw«f/0/4/11/0/4/11!cc mæw  mæw c/0/4/12/0/4/12!dd mæw¤ mæw¤d/0/4/3/0/4/3![[ mæw mæw[/0/4/2/0/4/2!ZZ mæwŽ mæwŽZ/0/4/1/0/4/1!YY mæwŒ mæwŒY/0/4/10/0/4/10!bb mæw› mæw›b/0/4/0/0/4/0!XX mæw‹ mæw‹X/0/4/7/0/4/7!__ mæw— mæw—_/0/4/6/0/4/6!^^ mæw– mæw–^/0/4/5/0/4/5!]] mæw” mæw”]/0/4/4/0/4/4!\\ mæw’ mæw’\/0/4/9/0/4/9!aa mæwš mæwša/0/4/8/0/4/8!`` mæw™ mæw™`/0/9/0/9!ÀÀ mæxn mæxnÔ/0/9/19/0/9/19!ÔÔ mæx“ mæx“Ô/0/9/17/0/9/17!ÒÒ mæx‘ mæx‘Ò/0/9/18/0/9/18!ÓÓ mæx’ mæx’Ó/0/9/15/0/9/15!ÐÐ mæx mæxÐ/0/9/16/0/9/16!ÑÑ mæx mæxÑ/0/9/13/0/9/13!ÎÎ mæx† mæx†Î/0/9/14/0/9/14!ÏÏ mæx‹ mæx‹Ï/0/9/11/0/9/11!ÌÌ mæxƒ mæxƒÌ/0/9/12/0/9/12!ÍÍ mæx… mæx…Í/0/9/3/0/9/3!ÄÄ mæxw mæxwÄ/0/9/2/0/9/2!Ãà mæxu mæxuÃ/0/9/1/0/9/1! mæxt mæxtÂ/0/9/10/0/9/10!ËË mæx‚ mæx‚Ë/0/9/0/0/9/0!ÁÁ mæxr mæxrÁ/0/9/7/0/9/7!ÈÈ mæx| mæx|È/0/9/6/0/9/6!ÇÇ mæx{ mæx{Ç/0/9/5/0/9/5!ÆÆ mæxz mæxzÆ/0/9/4/0/9/4!ÅÅ mæxy mæxyÅ/0/9/9/0/9/9!ÊÊ mæx mæxÊ/0/9/8/0/9/8!ÉÉ mæx~ mæx~É/0/8/0/8!«« mæx? mæx?¿/0/8/19/0/8/19!¿¿ mæxm mæxm¿/0/8/17/0/8/17!½½ mæxj mæxj½/0/8/18/0/8/18!¾¾ mæxl mæxl¾/0/8/15/0/8/15!»» mæxf mæxf»/0/8/16/0/8/16!¼¼ mæxg mæxg¼/0/8/13/0/8/13!¹¹ mæx^ mæx^¹/0/8/14/0/8/14!ºº mæx` mæx`º/0/8/11/0/8/11!·· mæxW mæxW·/0/8/12/0/8/12!¸¸ mæxZ mæxZ¸/0/8/3/0/8/3!¯¯ mæxI mæxI¯/0/8/2/0/8/2!®® mæxD mæxD®/0/8/1/0/8/1!­­ mæxB mæxB­/0/8/10/0/8/10!¶¶ mæxV mæxV¶/0/8/0/0/8/0!¬¬ mæx@ mæx@¬/0/8/7/0/8/7!³³ mæxP mæxP³/0/8/6/0/8/6!²² mæxM mæxM²/0/8/5/0/8/5!±± mæxL mæxL±/0/8/4/0/8/4!°° mæxK mæxK°/0/8/9/0/8/9!µµ mæxT mæxTµ/0/8/8/0/8/8!´´ mæxS mæxS´/7/7!ÇÇ mæð mæð1/7/3/7/3! mæ€X mæ€X/7/3/19/7/3/19! mæ€q mæ€q/7/3/17/7/3/17! mæ€o mæ€o/7/3/18/7/3/18! mæ€o mæ€o/7/3/15/7/3/15! mæ€k mæ€k/7/3/16/7/3/16! mæ€l mæ€l/7/3/13/7/3/13! mæ€j mæ€j/7/3/14/7/3/14! mæ€k mæ€k/7/3/11/7/3/11! mæ€f mæ€f/7/3/12/7/3/12! mæ€h mæ€h/7/3/3/7/3/3!   mæ€\ mæ€\ /7/3/2/7/3/2!   mæ€Z mæ€Z /7/3/1/7/3/1!   mæ€Y mæ€Y /7/3/10/7/3/10! mæ€e mæ€e/7/3/0/7/3/0! mæ€X mæ€X/7/3/7/7/3/7! mæ€` mæ€`/7/3/6/7/3/6! mæ€_ mæ€_/7/3/5/7/3/5!   mæ€^ mæ€^ /7/3/4/7/3/4!   mæ€] mæ€] /7/3/9/7/3/9! mæ€d mæ€d/7/3/8/7/3/8! mæ€b mæ€b/7/2/7/2!òò mæ€> mæ€>/7/2/19/7/2/19! mæ€W mæ€W/7/2/17/7/2/17! mæ€V mæ€V/7/2/18/7/2/18! mæ€V mæ€V/7/2/15/7/2/15! mæ€T mæ€T/7/2/16/7/2/16! mæ€U mæ€U/7/2/13/7/2/13! mæ€N mæ€N/7/2/14/7/2/14! mæ€S mæ€S/7/2/11/7/2/11!þþ mæ€M mæ€Mþ/7/2/12/7/2/12!ÿÿ mæ€M mæ€Mÿ/7/2/3/7/2/3!öö mæ€D mæ€Dö/7/2/2/7/2/2!õõ mæ€B mæ€Bõ/7/2/1/7/2/1!ôô mæ€@ mæ€@ô/7/2/10/7/2/10!ýý mæ€L mæ€Lý/7/2/0/7/2/0!óó mæ€? mæ€?ó/7/2/7/7/2/7!úú mæ€H mæ€Hú/7/2/6/7/2/6!ùù mæ€G mæ€Gù/7/2/5/7/2/5!øø mæ€F mæ€Fø/7/2/4/7/2/4!÷÷ mæ€F mæ€F÷/7/2/9/7/2/9!üü mæ€K mæ€Kü/7/2/8/7/2/8!ûû mæ€J mæ€Jû/7/1/7/1!ÝÝ mæ€! mæ€!ñ/7/1/19/7/1/19!ññ mæ€= mæ€=ñ/7/1/17/7/1/17!ïï mæ€; mæ€;ï/7/1/18/7/1/18!ðð mæ€< mæ€<ð/7/1/15/7/1/15!íí mæ€9 mæ€9í/7/1/16/7/1/16!îî mæ€: mæ€:î/7/1/13/7/1/13!ëë mæ€8 mæ€8ë/7/1/14/7/1/14!ìì mæ€9 mæ€9ì/7/1/11/7/1/11!éé mæ€3 mæ€3é/7/1/12/7/1/12!êê mæ€5 mæ€5ê/7/1/3/7/1/3!áá mæ€& mæ€&á/7/1/2/7/1/2!àà mæ€# mæ€#à/7/1/1/7/1/1!ßß mæ€# mæ€#ß/7/1/10/7/1/10!èè mæ€2 mæ€2è/7/1/0/7/1/0!ÞÞ mæ€" mæ€"Þ/7/1/7/7/1/7!åå mæ€, mæ€,å/7/1/6/7/1/6!ää mæ€+ mæ€+ä/7/1/5/7/1/5!ãã mæ€* mæ€*ã/7/1/4/7/1/4!ââ mæ€( mæ€(â/7/1/9/7/1/9!çç mæ€0 mæ€0ç/7/1/8/7/1/8!ææ mæ€/ mæ€/æ/7/0/7/0!ÈÈ mæñ mæñÜ/7/0/19/7/0/19!ÜÜ mæ€ mæ€Ü/7/0/17/7/0/17!ÚÚ mæ€ mæ€Ú/7/0/18/7/0/18!ÛÛ mæ€ mæ€Û/7/0/15/7/0/15!ØØ mæ€ mæ€Ø/7/0/16/7/0/16!ÙÙ mæ€ mæ€Ù/7/0/13/7/0/13!ÖÖ mæ€ mæ€Ö/7/0/14/7/0/14!×× mæ€ mæ€×/7/0/11/7/0/11!ÔÔ mæ€ mæ€Ô/7/0/12/7/0/12!ÕÕ mæ€ mæ€Õ/7/0/3/7/0/3!ÌÌ mæö mæöÌ/7/0/2/7/0/2!ËË mæõ mæõË/7/0/1/7/0/1!ÊÊ mæõ mæõÊ/7/0/10/7/0/10!ÓÓ mæ€ mæ€Ó/7/0/0/7/0/0!ÉÉ mæò mæòÉ/7/0/7/7/0/7!ÐÐ mæ€ mæ€Ð/7/0/6/7/0/6!ÏÏ mæ€ mæ€Ï/7/0/5/7/0/5!ÎÎ mæý mæýÎ/7/0/4/7/0/4!ÍÍ mæú mæúÍ/7/0/9/7/0/9!ÒÒ mæ€ mæ€Ò/7/0/8/7/0/8!ÑÑ mæ€ mæ€Ñ/7/5/7/5!11 m怒 m怒9/7/5/3/7/5/3!55 m怘 m怘5/7/5/2/7/5/2!44 m怗 m怗4/7/5/1/7/5/1!33 m怖 m怖3/7/5/0/7/5/0!22 m怔 m怔2/7/5/7/7/5/7!99 mæ€ mæ€9/7/5/6/7/5/6!88 m怜 m怜8/7/5/5/7/5/5!77 m怚 m怚7/7/5/4/7/5/4!66 m怙 m怙6/7/4/7/4! mæ€r mæ€r0/7/4/19/7/4/19!00 m怑 m怑0/7/4/17/7/4/17!.. mæ€ mæ€./7/4/18/7/4/18!// mæ€ mæ€//7/4/15/7/4/15!,, mæ€ mæ€,/7/4/16/7/4/16!-- m怎 m怎-/7/4/13/7/4/13!** m怉 m怉*/7/4/14/7/4/14!++ m怌 m怌+/7/4/11/7/4/11!(( m怇 m怇(/7/4/12/7/4/12!)) m怈 m怈)/7/4/3/7/4/3!   mæ€{ mæ€{ /7/4/2/7/4/2! mæ€z mæ€z/7/4/1/7/4/1! mæ€w mæ€w/7/4/10/7/4/10!'' m怅 m怅'/7/4/0/7/4/0! mæ€t mæ€t/7/4/7/7/4/7!$$ m怂 m怂$/7/4/6/7/4/6!## mæ€~ mæ€~#/7/4/5/7/4/5!"" mæ€} mæ€}"/7/4/4/7/4/4!!! mæ€| mæ€|!/7/4/9/7/4/9!&& m怄 m怄&/7/4/8/7/4/8!%% m怃 m怃%/6/6!ôô mæ~ï mæ~ï ²/6/3/6/3!44 mæC mæCH/6/3/19/6/3/19!HH mæX mæXH/6/3/17/6/3/17!FF mæU mæUF/6/3/18/6/3/18!GG mæU mæUG/6/3/15/6/3/15!DD mæS mæSD/6/3/16/6/3/16!EE mæT mæTE/6/3/13/6/3/13!BB mæR mæRB/6/3/14/6/3/14!CC mæR mæRC/6/3/11/6/3/11!@@ mæO mæO@/6/3/12/6/3/12!AA mæQ mæQA/6/3/3/6/3/3!88 mæG mæG8/6/3/2/6/3/2!77 mæF mæF7/6/3/1/6/3/1!66 mæF mæF6/6/3/10/6/3/10!?? mæN mæN?/6/3/0/6/3/0!55 mæD mæD5/6/3/7/6/3/7!<< mæK mæK</6/3/6/6/3/6!;; mæJ mæJ;/6/3/5/6/3/5!:: mæI mæI:/6/3/4/6/3/4!99 mæH mæH9/6/3/9/6/3/9!>> mæM mæM>/6/3/8/6/3/8!== mæK mæK=/6/2/6/2! mæ% mæ%3/6/2/19/6/2/19!33 mæC mæC3/6/2/17/6/2/17!11 mæA mæA1/6/2/18/6/2/18!22 mæB mæB2/6/2/15/6/2/15!// mæ? mæ?//6/2/16/6/2/16!00 mæ@ mæ@0/6/2/13/6/2/13!-- mæ; mæ;-/6/2/14/6/2/14!.. mæ> mæ>./6/2/11/6/2/11!++ mæ4 mæ4+/6/2/12/6/2/12!,, mæ5 mæ5,/6/2/3/6/2/3!## mæ) mæ)#/6/2/2/6/2/2!"" mæ' mæ'"/6/2/1/6/2/1!!! mæ' mæ'!/6/2/10/6/2/10!** mæ3 mæ3*/6/2/0/6/2/0!   mæ& mæ& /6/2/7/6/2/7!'' mæ/ mæ/'/6/2/6/6/2/6!&& mæ. mæ.&/6/2/5/6/2/5!%% mæ- mæ-%/6/2/4/6/2/4!$$ mæ, mæ,$/6/2/9/6/2/9!)) mæ3 mæ3)/6/2/8/6/2/8!(( mæ2 mæ2(/6/1/6/1!   mæ mæ/6/1/19/6/1/19! mæ$ mæ$/6/1/17/6/1/17! mæ" mæ"/6/1/18/6/1/18! mæ# mæ#/6/1/15/6/1/15! mæ! mæ!/6/1/16/6/1/16! mæ" mæ"/6/1/13/6/1/13! mæ mæ/6/1/14/6/1/14! mæ  mæ /6/1/11/6/1/11! mæ mæ/6/1/12/6/1/12! mæ mæ/6/1/3/6/1/3! mæ mæ/6/1/2/6/1/2!   mæ mæ /6/1/1/6/1/1!   mæ mæ /6/1/10/6/1/10! mæ mæ/6/1/0/6/1/0!   mæ mæ /6/1/7/6/1/7! mæ mæ/6/1/6/6/1/6! mæ mæ/6/1/5/6/1/5! mæ mæ/6/1/4/6/1/4! mæ mæ/6/1/9/6/1/9! mæ mæ/6/1/8/6/1/8! mæ mæ/6/0/6/0!õõ mæ~ð mæ~ð /6/0/19/6/0/19!   mæ mæ /6/0/17/6/0/17! mæ mæ/6/0/18/6/0/18! mæ mæ/6/0/15/6/0/15! mæ mæ/6/0/16/6/0/16! mæ mæ/6/0/13/6/0/13! mæ mæ/6/0/14/6/0/14! mæ mæ/6/0/11/6/0/11! mæ~ÿ mæ~ÿ/6/0/12/6/0/12! mæ mæ/6/0/3/6/0/3!ùù mæ~ö mæ~öù/6/0/2/6/0/2!øø mæ~ö mæ~öø/6/0/1/6/0/1!÷÷ mæ~ó mæ~ó÷/6/0/10/6/0/10! mæ~ÿ mæ~ÿ/6/0/0/6/0/0!öö mæ~ñ mæ~ñö/6/0/7/6/0/7!ýý mæ~ü mæ~üý/6/0/6/6/0/6!üü mæ~ù mæ~ùü/6/0/5/6/0/5!ûû mæ~ø mæ~øû/6/0/4/6/0/4!úú mæ~÷ mæ~÷ú/6/0/9/6/0/9!ÿÿ mæ~þ mæ~þÿ/6/0/8/6/0/8!þþ mæ~ý mæ~ýþ/6/7/6/7!ˆˆ mæ› m曜/6/7/19/6/7/19!œœ mæ¶ mæ¶œ/6/7/17/6/7/17!šš mæµ m浚/6/7/18/6/7/18!›› mæµ mæµ›/6/7/15/6/7/15!˜˜ mæ¯ m毘/6/7/16/6/7/16!™™ mæ² mæ²™/6/7/13/6/7/13!–– mæª m檖/6/7/14/6/7/14!—— mæ« mæ«—/6/7/11/6/7/11!”” mæ§ mæ§”/6/7/12/6/7/12!•• mæ© mæ©•/6/7/3/6/7/3!ŒŒ mæŸ m柌/6/7/2/6/7/2!‹‹ mæž mæž‹/6/7/1/6/7/1!ŠŠ mæž m枊/6/7/10/6/7/10!““ mæ¦ m榓/6/7/0/6/7/0!‰‰ mæœ m有/6/7/7/6/7/7! mæ¤ mæ¤/6/7/6/6/7/6! mæ£ mæ£/6/7/5/6/7/5!ŽŽ mæ¢ m梎/6/7/4/6/7/4! mæ¡ mæ¡/6/7/9/6/7/9!’’ mæ¥ m楒/6/7/8/6/7/8!‘‘ mæ¥ m楑/6/6/6/6!ss mæ„ m愇/6/6/19/6/6/19!‡‡ mæš m暇/6/6/17/6/6/17!…… mæ— mæ—…/6/6/18/6/6/18!†† mæ˜ m昆/6/6/15/6/6/15!ƒƒ mæ– mæ–ƒ/6/6/16/6/6/16!„„ mæ— mæ—„/6/6/13/6/6/13! mæ“ mæ“/6/6/14/6/6/14!‚‚ mæ• mæ•‚/6/6/11/6/6/11! mæ mæ/6/6/12/6/6/12!€€ mæ’ mæ’€/6/6/3/6/6/3!ww mæ‡ mæ‡w/6/6/2/6/6/2!vv mæ† mæ†v/6/6/1/6/6/1!uu mæ… mæ…u/6/6/10/6/6/10!~~ mæŽ mæŽ~/6/6/0/6/6/0!tt mæ… mæ…t/6/6/7/6/6/7!{{ mæ‹ mæ‹{/6/6/6/6/6/6!zz mæŠ mæŠz/6/6/5/6/6/5!yy mæ‰ mæ‰y/6/6/4/6/6/4!xx mæˆ mæˆx/6/6/9/6/6/9!}} mæ mæ}/6/6/8/6/6/8!|| mæ mæ|/6/5/6/5!^^ mæn mænr/6/5/19/6/5/19!rr mæƒ mæƒr/6/5/17/6/5/17!pp mæ‚ mæ‚p/6/5/18/6/5/18!qq mæ‚ mæ‚q/6/5/15/6/5/15!nn mæ mæn/6/5/16/6/5/16!oo mæ mæo/6/5/13/6/5/13!ll mæ} mæ}l/6/5/14/6/5/14!mm mæ} mæ}m/6/5/11/6/5/11!jj mæ{ mæ{j/6/5/12/6/5/12!kk mæ| mæ|k/6/5/3/6/5/3!bb mæq mæqb/6/5/2/6/5/2!aa mæq mæqa/6/5/1/6/5/1!`` mæp mæp`/6/5/10/6/5/10!ii mæz mæzi/6/5/0/6/5/0!__ mæo mæo_/6/5/7/6/5/7!ff mæw mæwf/6/5/6/6/5/6!ee mæw mæwe/6/5/5/6/5/5!dd mæv mævd/6/5/4/6/5/4!cc mæu mæuc/6/5/9/6/5/9!hh mæz mæzh/6/5/8/6/5/8!gg mæy mæyg/6/4/6/4!II mæY mæY]/6/4/19/6/4/19!]] mæm mæm]/6/4/17/6/4/17![[ mæk mæk[/6/4/18/6/4/18!\\ mæl mæl\/6/4/15/6/4/15!YY mæi mæiY/6/4/16/6/4/16!ZZ mæj mæjZ/6/4/13/6/4/13!WW mæg mægW/6/4/14/6/4/14!XX mæh mæhX/6/4/11/6/4/11!UU mæf mæfU/6/4/12/6/4/12!VV mæf mæfV/6/4/3/6/4/3!MM mæ\ mæ\M/6/4/2/6/4/2!LL mæ\ mæ\L/6/4/1/6/4/1!KK mæ[ mæ[K/6/4/10/6/4/10!TT mæe mæeT/6/4/0/6/4/0!JJ mæZ mæZJ/6/4/7/6/4/7!QQ mæ_ mæ_Q/6/4/6/6/4/6!PP mæ_ mæ_P/6/4/5/6/4/5!OO mæ^ mæ^O/6/4/4/6/4/4!NN mæ] mæ]N/6/4/9/6/4/9!SS mæa mæaS/6/4/8/6/4/8!RR mæa mæaR/6/9/6/9!²² mæ× mæ×Æ/6/9/19/6/9/19!ÆÆ mæï mæïÆ/6/9/17/6/9/17!ÄÄ mæí mæíÄ/6/9/18/6/9/18!ÅÅ mæî mæîÅ/6/9/15/6/9/15! mæë mæëÂ/6/9/16/6/9/16!Ãà mæì mæìÃ/6/9/13/6/9/13!ÀÀ mæé mæéÀ/6/9/14/6/9/14!ÁÁ mæê mæêÁ/6/9/11/6/9/11!¾¾ mæç mæç¾/6/9/12/6/9/12!¿¿ mæè mæè¿/6/9/3/6/9/3!¶¶ mæÚ mæÚ¶/6/9/2/6/9/2!µµ mæÙ mæÙµ/6/9/1/6/9/1!´´ mæÙ mæÙ´/6/9/10/6/9/10!½½ mææ mææ½/6/9/0/6/9/0!³³ mæØ mæØ³/6/9/7/6/9/7!ºº mæà mæàº/6/9/6/6/9/6!¹¹ mæÝ mæÝ¹/6/9/5/6/9/5!¸¸ mæÜ mæÜ¸/6/9/4/6/9/4!·· mæÛ mæÛ·/6/9/9/6/9/9!¼¼ mæå mæå¼/6/9/8/6/9/8!»» mæã mæã»/6/8/6/8! mæ· mæ·±/6/8/19/6/8/19!±± mæÖ mæÖ±/6/8/17/6/8/17!¯¯ mæÔ mæÔ¯/6/8/18/6/8/18!°° mæÕ mæÕ°/6/8/15/6/8/15!­­ mæÒ mæÒ­/6/8/16/6/8/16!®® mæÔ mæÔ®/6/8/13/6/8/13!«« mæÏ mæÏ«/6/8/14/6/8/14!¬¬ mæÑ mæÑ¬/6/8/11/6/8/11!©© mæÌ mæÌ©/6/8/12/6/8/12!ªª mæÏ mæÏª/6/8/3/6/8/3!¡¡ mæ» m满/6/8/2/6/8/2!   mæº m溠/6/8/1/6/8/1!ŸŸ mæ¹ m湟/6/8/10/6/8/10!¨¨ mæË mæË¨/6/8/0/6/8/0!žž mæ¸ m渞/6/8/7/6/8/7!¥¥ mæÇ mæÇ¥/6/8/6/6/8/6!¤¤ mæÁ mæÁ¤/6/8/5/6/8/5!££ mæ¿ mæ¿£/6/8/4/6/8/4!¢¢ mæ¾ mæ¾¢/6/8/9/6/8/9!§§ mæÊ mæÊ§/6/8/8/6/8/8!¦¦ mæÉ mæÉ¦/5/5!!! mæ~ mæ~ ß/5/3/5/3!aa mæ~J mæ~Ju/5/3/19/5/3/19!uu mæ~b mæ~bu/5/3/17/5/3/17!ss mæ~` mæ~`s/5/3/18/5/3/18!tt mæ~b mæ~bt/5/3/15/5/3/15!qq mæ~^ mæ~^q/5/3/16/5/3/16!rr mæ~_ mæ~_r/5/3/13/5/3/13!oo mæ~\ mæ~\o/5/3/14/5/3/14!pp mæ~] mæ~]p/5/3/11/5/3/11!mm mæ~Y mæ~Ym/5/3/12/5/3/12!nn mæ~Z mæ~Zn/5/3/3/5/3/3!ee mæ~N mæ~Ne/5/3/2/5/3/2!dd mæ~M mæ~Md/5/3/1/5/3/1!cc mæ~L mæ~Lc/5/3/10/5/3/10!ll mæ~Y mæ~Yl/5/3/0/5/3/0!bb mæ~K mæ~Kb/5/3/7/5/3/7!ii mæ~V mæ~Vi/5/3/6/5/3/6!hh mæ~U mæ~Uh/5/3/5/5/3/5!gg mæ~T mæ~Tg/5/3/4/5/3/4!ff mæ~N mæ~Nf/5/3/9/5/3/9!kk mæ~W mæ~Wk/5/3/8/5/3/8!jj mæ~V mæ~Vj/5/2/5/2!LL mæ~5 mæ~5`/5/2/19/5/2/19!`` mæ~I mæ~I`/5/2/17/5/2/17!^^ mæ~G mæ~G^/5/2/18/5/2/18!__ mæ~H mæ~H_/5/2/15/5/2/15!\\ mæ~F mæ~F\/5/2/16/5/2/16!]] mæ~F mæ~F]/5/2/13/5/2/13!ZZ mæ~D mæ~DZ/5/2/14/5/2/14![[ mæ~E mæ~E[/5/2/11/5/2/11!XX mæ~C mæ~CX/5/2/12/5/2/12!YY mæ~C mæ~CY/5/2/3/5/2/3!PP mæ~: mæ~:P/5/2/2/5/2/2!OO mæ~7 mæ~7O/5/2/1/5/2/1!NN mæ~7 mæ~7N/5/2/10/5/2/10!WW mæ~A mæ~AW/5/2/0/5/2/0!MM mæ~6 mæ~6M/5/2/7/5/2/7!TT mæ~= mæ~=T/5/2/6/5/2/6!SS mæ~< mæ~<S/5/2/5/5/2/5!RR mæ~; mæ~;R/5/2/4/5/2/4!QQ mæ~: mæ~:Q/5/2/9/5/2/9!VV mæ~@ mæ~@V/5/2/8/5/2/8!UU mæ~> mæ~>U/5/1/5/1!77 mæ~  mæ~ K/5/1/19/5/1/19!KK mæ~4 mæ~4K/5/1/17/5/1/17!II mæ~3 mæ~3I/5/1/18/5/1/18!JJ mæ~3 mæ~3J/5/1/15/5/1/15!GG mæ~1 mæ~1G/5/1/16/5/1/16!HH mæ~2 mæ~2H/5/1/13/5/1/13!EE mæ~/ mæ~/E/5/1/14/5/1/14!FF mæ~/ mæ~/F/5/1/11/5/1/11!CC mæ~- mæ~-C/5/1/12/5/1/12!DD mæ~. mæ~.D/5/1/3/5/1/3!;; mæ~% mæ~%;/5/1/2/5/1/2!:: mæ~$ mæ~$:/5/1/1/5/1/1!99 mæ~# mæ~#9/5/1/10/5/1/10!BB mæ~+ mæ~+B/5/1/0/5/1/0!88 mæ~" mæ~"8/5/1/7/5/1/7!?? mæ~) mæ~)?/5/1/6/5/1/6!>> mæ~( mæ~(>/5/1/5/5/1/5!== mæ~' mæ~'=/5/1/4/5/1/4!<< mæ~& mæ~&</5/1/9/5/1/9!AA mæ~* mæ~*A/5/1/8/5/1/8!@@ mæ~) mæ~)@/5/0/5/0!"" mæ~ mæ~6/5/0/19/5/0/19!66 mæ~ mæ~6/5/0/17/5/0/17!44 mæ~ mæ~4/5/0/18/5/0/18!55 mæ~ mæ~5/5/0/15/5/0/15!22 mæ~ mæ~2/5/0/16/5/0/16!33 mæ~ mæ~3/5/0/13/5/0/13!00 mæ~ mæ~0/5/0/14/5/0/14!11 mæ~ mæ~1/5/0/11/5/0/11!.. mæ~ mæ~./5/0/12/5/0/12!// mæ~ mæ~//5/0/3/5/0/3!&& mæ~  mæ~ &/5/0/2/5/0/2!%% mæ~  mæ~ %/5/0/1/5/0/1!$$ mæ~  mæ~ $/5/0/10/5/0/10!-- mæ~ mæ~-/5/0/0/5/0/0!## mæ~  mæ~ #/5/0/7/5/0/7!** mæ~ mæ~*/5/0/6/5/0/6!)) mæ~ mæ~)/5/0/5/5/0/5!(( mæ~ mæ~(/5/0/4/5/0/4!'' mæ~ mæ~'/5/0/9/5/0/9!,, mæ~ mæ~,/5/0/8/5/0/8!++ mæ~ mæ~+/5/7/5/7!µµ mæ~¥ mæ~¥É/5/7/19/5/7/19!ÉÉ mæ~¹ mæ~¹É/5/7/17/5/7/17!ÇÇ mæ~· mæ~·Ç/5/7/18/5/7/18!ÈÈ mæ~¸ mæ~¸È/5/7/15/5/7/15!ÅÅ mæ~µ mæ~µÅ/5/7/16/5/7/16!ÆÆ mæ~µ mæ~µÆ/5/7/13/5/7/13!Ãà mæ~³ mæ~³Ã/5/7/14/5/7/14!ÄÄ mæ~´ mæ~´Ä/5/7/11/5/7/11!ÁÁ mæ~± mæ~±Á/5/7/12/5/7/12! mæ~² mæ~²Â/5/7/3/5/7/3!¹¹ mæ~© mæ~©¹/5/7/2/5/7/2!¸¸ mæ~¨ mæ~¨¸/5/7/1/5/7/1!·· mæ~§ mæ~§·/5/7/10/5/7/10!ÀÀ mæ~° mæ~°À/5/7/0/5/7/0!¶¶ mæ~¦ mæ~¦¶/5/7/7/5/7/7!½½ mæ~¬ mæ~¬½/5/7/6/5/7/6!¼¼ mæ~« mæ~«¼/5/7/5/5/7/5!»» mæ~ª mæ~ª»/5/7/4/5/7/4!ºº mæ~© mæ~©º/5/7/9/5/7/9!¿¿ mæ~® mæ~®¿/5/7/8/5/7/8!¾¾ mæ~® mæ~®¾/5/6/5/6!   mæ~‹ mæ~‹´/5/6/19/5/6/19!´´ mæ~¤ mæ~¤´/5/6/17/5/6/17!²² mæ~¡ mæ~¡²/5/6/18/5/6/18!³³ mæ~£ mæ~£³/5/6/15/5/6/15!°° mæ~Ÿ mæ~Ÿ°/5/6/16/5/6/16!±± mæ~  mæ~ ±/5/6/13/5/6/13!®® mæ~ mæ~®/5/6/14/5/6/14!¯¯ mæ~Ÿ mæ~Ÿ¯/5/6/11/5/6/11!¬¬ mæ~› mæ~›¬/5/6/12/5/6/12!­­ mæ~› mæ~›­/5/6/3/5/6/3!¤¤ mæ~ mæ~¤/5/6/2/5/6/2!££ mæ~Ž mæ~Ž£/5/6/1/5/6/1!¢¢ mæ~ mæ~¢/5/6/10/5/6/10!«« mæ~š mæ~š«/5/6/0/5/6/0!¡¡ mæ~Œ mæ~Œ¡/5/6/7/5/6/7!¨¨ mæ~— mæ~—¨/5/6/6/5/6/6!§§ mæ~– mæ~–§/5/6/5/5/6/5!¦¦ mæ~” mæ~”¦/5/6/4/5/6/4!¥¥ mæ~‘ mæ~‘¥/5/6/9/5/6/9!ªª mæ~™ mæ~™ª/5/6/8/5/6/8!©© mæ~˜ mæ~˜©/5/5/5/5!‹‹ mæ~x mæ~xŸ/5/5/19/5/5/19!ŸŸ mæ~Š mæ~ŠŸ/5/5/17/5/5/17! mæ~ˆ mæ~ˆ/5/5/18/5/5/18!žž mæ~Š mæ~Šž/5/5/15/5/5/15!›› mæ~‡ mæ~‡›/5/5/16/5/5/16!œœ mæ~‡ mæ~‡œ/5/5/13/5/5/13!™™ mæ~„ mæ~„™/5/5/14/5/5/14!šš mæ~† mæ~†š/5/5/11/5/5/11!—— mæ~‚ mæ~‚—/5/5/12/5/5/12!˜˜ mæ~ƒ mæ~ƒ˜/5/5/3/5/5/3! mæ~{ mæ~{/5/5/2/5/5/2!ŽŽ mæ~z mæ~zŽ/5/5/1/5/5/1! mæ~y mæ~y/5/5/10/5/5/10!–– mæ~‚ mæ~‚–/5/5/0/5/5/0!ŒŒ mæ~y mæ~yŒ/5/5/7/5/5/7!““ mæ~~ mæ~~“/5/5/6/5/5/6!’’ mæ~~ mæ~~’/5/5/5/5/5/5!‘‘ mæ~} mæ~}‘/5/5/4/5/5/4! mæ~| mæ~|/5/5/9/5/5/9!•• mæ~ mæ~•/5/5/8/5/5/8!”” mæ~ mæ~”/5/4/5/4!vv mæ~c mæ~cŠ/5/4/19/5/4/19!ŠŠ mæ~w mæ~wŠ/5/4/17/5/4/17!ˆˆ mæ~v mæ~vˆ/5/4/18/5/4/18!‰‰ mæ~v mæ~v‰/5/4/15/5/4/15!†† mæ~s mæ~s†/5/4/16/5/4/16!‡‡ mæ~t mæ~t‡/5/4/13/5/4/13!„„ mæ~q mæ~q„/5/4/14/5/4/14!…… mæ~r mæ~r…/5/4/11/5/4/11!‚‚ mæ~o mæ~o‚/5/4/12/5/4/12!ƒƒ mæ~p mæ~pƒ/5/4/3/5/4/3!zz mæ~g mæ~gz/5/4/2/5/4/2!yy mæ~f mæ~fy/5/4/1/5/4/1!xx mæ~e mæ~ex/5/4/10/5/4/10! mæ~n mæ~n/5/4/0/5/4/0!ww mæ~d mæ~dw/5/4/7/5/4/7!~~ mæ~l mæ~l~/5/4/6/5/4/6!}} mæ~k mæ~k}/5/4/5/5/4/5!|| mæ~j mæ~j|/5/4/4/5/4/4!{{ mæ~i mæ~i{/5/4/9/5/4/9!€€ mæ~m mæ~m€/5/4/8/5/4/8! mæ~m mæ~m/5/9/5/9!ßß mæ~Ù mæ~Ùó/5/9/19/5/9/19!óó mæ~î mæ~îó/5/9/17/5/9/17!ññ mæ~í mæ~íñ/5/9/18/5/9/18!òò mæ~í mæ~íò/5/9/15/5/9/15!ïï mæ~ë mæ~ëï/5/9/16/5/9/16!ðð mæ~ì mæ~ìð/5/9/13/5/9/13!íí mæ~é mæ~éí/5/9/14/5/9/14!îî mæ~ê mæ~êî/5/9/11/5/9/11!ëë mæ~æ mæ~æë/5/9/12/5/9/12!ìì mæ~ç mæ~çì/5/9/3/5/9/3!ãã mæ~Ý mæ~Ýã/5/9/2/5/9/2!ââ mæ~Û mæ~Ûâ/5/9/1/5/9/1!áá mæ~Û mæ~Ûá/5/9/10/5/9/10!êê mæ~æ mæ~æê/5/9/0/5/9/0!àà mæ~Ú mæ~Úà/5/9/7/5/9/7!çç mæ~ã mæ~ãç/5/9/6/5/9/6!ææ mæ~â mæ~âæ/5/9/5/5/9/5!åå mæ~á mæ~áå/5/9/4/5/9/4!ää mæ~Ý mæ~Ýä/5/9/9/5/9/9!éé mæ~å mæ~åé/5/9/8/5/9/8!èè mæ~ä mæ~äè/5/8/5/8!ÊÊ mæ~º mæ~ºÞ/5/8/19/5/8/19!ÞÞ mæ~Ø mæ~ØÞ/5/8/17/5/8/17!ÜÜ mæ~Ö mæ~ÖÜ/5/8/18/5/8/18!ÝÝ mæ~× mæ~×Ý/5/8/15/5/8/15!ÚÚ mæ~Ó mæ~ÓÚ/5/8/16/5/8/16!ÛÛ mæ~Õ mæ~ÕÛ/5/8/13/5/8/13!ØØ mæ~Ò mæ~ÒØ/5/8/14/5/8/14!ÙÙ mæ~Ò mæ~ÒÙ/5/8/11/5/8/11!ÖÖ mæ~Ï mæ~ÏÖ/5/8/12/5/8/12!×× mæ~Ñ mæ~Ñ×/5/8/3/5/8/3!ÎÎ mæ~¾ mæ~¾Î/5/8/2/5/8/2!ÍÍ mæ~½ mæ~½Í/5/8/1/5/8/1!ÌÌ mæ~¼ mæ~¼Ì/5/8/10/5/8/10!ÕÕ mæ~Î mæ~ÎÕ/5/8/0/5/8/0!ËË mæ~» mæ~»Ë/5/8/7/5/8/7!ÒÒ mæ~Ê mæ~ÊÒ/5/8/6/5/8/6!ÑÑ mæ~Å mæ~ÅÑ/5/8/5/5/8/5!ÐÐ mæ~à mæ~ÃÐ/5/8/4/5/8/4!ÏÏ mæ~¿ mæ~¿Ï/5/8/9/5/8/9!ÔÔ mæ~Í mæ~ÍÔ/5/8/8/5/8/8!ÓÓ mæ~Ë mæ~ËÓ /zookeeperÿÿÿÿÿÿÿÿ/zookeeper/quotaÿÿÿÿÿÿÿÿ/4/4!NN mæ} mæ}  /4/3/4/3!ŽŽ mæ}^ mæ}^¢/4/3/19/4/3/19!¢¢ mæ}r mæ}r¢/4/3/17/4/3/17!   mæ}p mæ}p /4/3/18/4/3/18!¡¡ mæ}q mæ}q¡/4/3/15/4/3/15!žž mæ}n mæ}nž/4/3/16/4/3/16!ŸŸ mæ}o mæ}oŸ/4/3/13/4/3/13!œœ mæ}l mæ}lœ/4/3/14/4/3/14! mæ}m mæ}m/4/3/11/4/3/11!šš mæ}j mæ}jš/4/3/12/4/3/12!›› mæ}k mæ}k›/4/3/3/4/3/3!’’ mæ}b mæ}b’/4/3/2/4/3/2!‘‘ mæ}a mæ}a‘/4/3/1/4/3/1! mæ}` mæ}`/4/3/10/4/3/10!™™ mæ}i mæ}i™/4/3/0/4/3/0! mæ}_ mæ}_/4/3/7/4/3/7!–– mæ}f mæ}f–/4/3/6/4/3/6!•• mæ}e mæ}e•/4/3/5/4/3/5!”” mæ}e mæ}e”/4/3/4/4/3/4!““ mæ}d mæ}d“/4/3/9/4/3/9!˜˜ mæ}h mæ}h˜/4/3/8/4/3/8!—— mæ}g mæ}g—/4/2/4/2!yy mæ}J mæ}J/4/2/19/4/2/19! mæ}^ mæ}^/4/2/17/4/2/17!‹‹ mæ}\ mæ}\‹/4/2/18/4/2/18!ŒŒ mæ}] mæ}]Œ/4/2/15/4/2/15!‰‰ mæ}Z mæ}Z‰/4/2/16/4/2/16!ŠŠ mæ}[ mæ}[Š/4/2/13/4/2/13!‡‡ mæ}W mæ}W‡/4/2/14/4/2/14!ˆˆ mæ}X mæ}Xˆ/4/2/11/4/2/11!…… mæ}V mæ}V…/4/2/12/4/2/12!†† mæ}W mæ}W†/4/2/3/4/2/3!}} mæ}M mæ}M}/4/2/2/4/2/2!|| mæ}M mæ}M|/4/2/1/4/2/1!{{ mæ}L mæ}L{/4/2/10/4/2/10!„„ mæ}U mæ}U„/4/2/0/4/2/0!zz mæ}K mæ}Kz/4/2/7/4/2/7! mæ}R mæ}R/4/2/6/4/2/6!€€ mæ}R mæ}R€/4/2/5/4/2/5! mæ}Q mæ}Q/4/2/4/4/2/4!~~ mæ}N mæ}N~/4/2/9/4/2/9!ƒƒ mæ}T mæ}Tƒ/4/2/8/4/2/8!‚‚ mæ}S mæ}S‚/4/1/4/1!dd mæ}) mæ})x/4/1/19/4/1/19!xx mæ}I mæ}Ix/4/1/17/4/1/17!vv mæ}C mæ}Cv/4/1/18/4/1/18!ww mæ}E mæ}Ew/4/1/15/4/1/15!tt mæ}? mæ}?t/4/1/16/4/1/16!uu mæ}A mæ}Au/4/1/13/4/1/13!rr mæ}= mæ}=r/4/1/14/4/1/14!ss mæ}? mæ}?s/4/1/11/4/1/11!pp mæ}9 mæ}9p/4/1/12/4/1/12!qq mæ}: mæ}:q/4/1/3/4/1/3!hh mæ}- mæ}-h/4/1/2/4/1/2!gg mæ}, mæ},g/4/1/1/4/1/1!ff mæ}+ mæ}+f/4/1/10/4/1/10!oo mæ}8 mæ}8o/4/1/0/4/1/0!ee mæ}* mæ}*e/4/1/7/4/1/7!ll mæ}5 mæ}5l/4/1/6/4/1/6!kk mæ}2 mæ}2k/4/1/5/4/1/5!jj mæ}1 mæ}1j/4/1/4/4/1/4!ii mæ}0 mæ}0i/4/1/9/4/1/9!nn mæ}7 mæ}7n/4/1/8/4/1/8!mm mæ}6 mæ}6m/4/0/4/0!OO mæ} mæ}c/4/0/19/4/0/19!cc mæ}& mæ}&c/4/0/17/4/0/17!aa mæ}$ mæ}$a/4/0/18/4/0/18!bb mæ}% mæ}%b/4/0/15/4/0/15!__ mæ}" mæ}"_/4/0/16/4/0/16!`` mæ}# mæ}#`/4/0/13/4/0/13!]] mæ}  mæ} ]/4/0/14/4/0/14!^^ mæ}" mæ}"^/4/0/11/4/0/11![[ mæ} mæ}[/4/0/12/4/0/12!\\ mæ}  mæ} \/4/0/3/4/0/3!SS mæ} mæ}S/4/0/2/4/0/2!RR mæ} mæ}R/4/0/1/4/0/1!QQ mæ} mæ}Q/4/0/10/4/0/10!ZZ mæ} mæ}Z/4/0/0/4/0/0!PP mæ} mæ}P/4/0/7/4/0/7!WW mæ} mæ}W/4/0/6/4/0/6!VV mæ} mæ}V/4/0/5/4/0/5!UU mæ} mæ}U/4/0/4/4/0/4!TT mæ} mæ}T/4/0/9/4/0/9!YY mæ} mæ}Y/4/0/8/4/0/8!XX mæ} mæ}X/4/7/4/7!ââ mæ}» mæ}»ö/4/7/19/4/7/19!öö mæ}Ø mæ}Øö/4/7/17/4/7/17!ôô mæ}Ö mæ}Öô/4/7/18/4/7/18!õõ mæ}× mæ}×õ/4/7/15/4/7/15!òò mæ}Õ mæ}Õò/4/7/16/4/7/16!óó mæ}Õ mæ}Õó/4/7/13/4/7/13!ðð mæ}Ó mæ}Óð/4/7/14/4/7/14!ññ mæ}Ô mæ}Ôñ/4/7/11/4/7/11!îî mæ}Ð mæ}Ðî/4/7/12/4/7/12!ïï mæ}Ò mæ}Òï/4/7/3/4/7/3!ææ mæ}¿ mæ}¿æ/4/7/2/4/7/2!åå mæ}¿ mæ}¿å/4/7/1/4/7/1!ää mæ}¾ mæ}¾ä/4/7/10/4/7/10!íí mæ}Ï mæ}Ïí/4/7/0/4/7/0!ãã mæ}¼ mæ}¼ã/4/7/7/4/7/7!êê mæ}É mæ}Éê/4/7/6/4/7/6!éé mæ}à mæ}Ãé/4/7/5/4/7/5!èè mæ} mæ}Âè/4/7/4/4/7/4!çç mæ}À mæ}Àç/4/7/9/4/7/9!ìì mæ}Î mæ}Îì/4/7/8/4/7/8!ëë mæ}É mæ}Éë/4/6/4/6!ÍÍ mæ}¥ mæ}¥á/4/6/19/4/6/19!áá mæ}» mæ}»á/4/6/17/4/6/17!ßß mæ}¹ mæ}¹ß/4/6/18/4/6/18!àà mæ}º mæ}ºà/4/6/15/4/6/15!ÝÝ mæ}· mæ}·Ý/4/6/16/4/6/16!ÞÞ mæ}¸ mæ}¸Þ/4/6/13/4/6/13!ÛÛ mæ}¶ mæ}¶Û/4/6/14/4/6/14!ÜÜ mæ}¶ mæ}¶Ü/4/6/11/4/6/11!ÙÙ mæ}´ mæ}´Ù/4/6/12/4/6/12!ÚÚ mæ}µ mæ}µÚ/4/6/3/4/6/3!ÑÑ mæ}« mæ}«Ñ/4/6/2/4/6/2!ÐÐ mæ}ª mæ}ªÐ/4/6/1/4/6/1!ÏÏ mæ}© mæ}©Ï/4/6/10/4/6/10!ØØ mæ}³ mæ}³Ø/4/6/0/4/6/0!ÎÎ mæ}© mæ}©Î/4/6/7/4/6/7!ÕÕ mæ}¯ mæ}¯Õ/4/6/6/4/6/6!ÔÔ mæ}® mæ}®Ô/4/6/5/4/6/5!ÓÓ mæ}­ mæ}­Ó/4/6/4/4/6/4!ÒÒ mæ}¬ mæ}¬Ò/4/6/9/4/6/9!×× mæ}± mæ}±×/4/6/8/4/6/8!ÖÖ mæ}¯ mæ}¯Ö/4/5/4/5!¸¸ mæ} mæ}Ì/4/5/19/4/5/19!ÌÌ mæ}¥ mæ}¥Ì/4/5/17/4/5/17!ÊÊ mæ}£ mæ}£Ê/4/5/18/4/5/18!ËË mæ}¤ mæ}¤Ë/4/5/15/4/5/15!ÈÈ mæ}¡ mæ}¡È/4/5/16/4/5/16!ÉÉ mæ}¢ mæ}¢É/4/5/13/4/5/13!ÆÆ mæ} mæ}Æ/4/5/14/4/5/14!ÇÇ mæ}ž mæ}žÇ/4/5/11/4/5/11!ÄÄ mæ}› mæ}›Ä/4/5/12/4/5/12!ÅÅ mæ}œ mæ}œÅ/4/5/3/4/5/3!¼¼ mæ}“ mæ}“¼/4/5/2/4/5/2!»» mæ}“ mæ}“»/4/5/1/4/5/1!ºº mæ}’ mæ}’º/4/5/10/4/5/10!Ãà mæ}š mæ}šÃ/4/5/0/4/5/0!¹¹ mæ}‘ mæ}‘¹/4/5/7/4/5/7!ÀÀ mæ}— mæ}—À/4/5/6/4/5/6!¿¿ mæ}– mæ}–¿/4/5/5/4/5/5!¾¾ mæ}• mæ}•¾/4/5/4/4/5/4!½½ mæ}• mæ}•½/4/5/9/4/5/9! mæ}š mæ}šÂ/4/5/8/4/5/8!ÁÁ mæ}™ mæ}™Á/4/4/4/4!££ mæ}t mæ}t·/4/4/19/4/4/19!·· mæ} mæ}·/4/4/17/4/4/17!µµ mæ} mæ}µ/4/4/18/4/4/18!¶¶ mæ}Ž mæ}޶/4/4/15/4/4/15!³³ mæ}‰ mæ}‰³/4/4/16/4/4/16!´´ mæ}Š mæ}Š´/4/4/13/4/4/13!±± mæ}‡ mæ}‡±/4/4/14/4/4/14!²² mæ}ˆ mæ}ˆ²/4/4/11/4/4/11!¯¯ mæ}… mæ}…¯/4/4/12/4/4/12!°° mæ}† mæ}†°/4/4/3/4/4/3!§§ mæ}{ mæ}{§/4/4/2/4/4/2!¦¦ mæ}z mæ}z¦/4/4/1/4/4/1!¥¥ mæ}y mæ}y¥/4/4/10/4/4/10!®® mæ}„ mæ}„®/4/4/0/4/4/0!¤¤ mæ}x mæ}x¤/4/4/7/4/4/7!«« mæ} mæ}«/4/4/6/4/4/6!ªª mæ}€ mæ}€ª/4/4/5/4/4/5!©© mæ}| mæ}|©/4/4/4/4/4/4!¨¨ mæ}| mæ}|¨/4/4/9/4/4/9!­­ mæ}ƒ mæ}ƒ­/4/4/8/4/4/8!¬¬ mæ}‚ mæ}‚¬/4/9/4/9!   mæ}ñ mæ}ñ /4/9/19/4/9/19!   mæ~ mæ~ /4/9/17/4/9/17! mæ~ mæ~/4/9/18/4/9/18! mæ~ mæ~/4/9/15/4/9/15! mæ~ mæ~/4/9/16/4/9/16! mæ~ mæ~/4/9/13/4/9/13! mæ}þ mæ}þ/4/9/14/4/9/14! mæ}ÿ mæ}ÿ/4/9/11/4/9/11! mæ}ý mæ}ý/4/9/12/4/9/12! mæ}ý mæ}ý/4/9/3/4/9/3! mæ}õ mæ}õ/4/9/2/4/9/2! mæ}ó mæ}ó/4/9/1/4/9/1! mæ}ò mæ}ò/4/9/10/4/9/10! mæ}ü mæ}ü/4/9/0/4/9/0!   mæ}ñ mæ}ñ /4/9/7/4/9/7! mæ}ù mæ}ù/4/9/6/4/9/6! mæ}ø mæ}ø/4/9/5/4/9/5! mæ}÷ mæ}÷/4/9/4/4/9/4! mæ}ö mæ}ö/4/9/9/4/9/9! mæ}û mæ}û/4/9/8/4/9/8! mæ}ú mæ}ú/4/8/4/8!÷÷ mæ}Ù mæ}Ù /4/8/19/4/8/19!   mæ}î mæ}î /4/8/17/4/8/17!   mæ}í mæ}í /4/8/18/4/8/18!   mæ}í mæ}í /4/8/15/4/8/15! mæ}ë mæ}ë/4/8/16/4/8/16! mæ}ì mæ}ì/4/8/13/4/8/13! mæ}é mæ}é/4/8/14/4/8/14! mæ}ê mæ}ê/4/8/11/4/8/11! mæ}ç mæ}ç/4/8/12/4/8/12! mæ}è mæ}è/4/8/3/4/8/3!ûû mæ}ß mæ}ßû/4/8/2/4/8/2!úú mæ}Þ mæ}Þú/4/8/1/4/8/1!ùù mæ}Û mæ}Ûù/4/8/10/4/8/10! mæ}æ mæ}æ/4/8/0/4/8/0!øø mæ}Ù mæ}Ùø/4/8/7/4/8/7!ÿÿ mæ}ã mæ}ãÿ/4/8/6/4/8/6!þþ mæ}â mæ}âþ/4/8/5/4/8/5!ýý mæ}á mæ}áý/4/8/4/4/8/4!üü mæ}à mæ}àü/4/8/9/4/8/9! mæ}å mæ}å/4/8/8/4/8/8! mæ}ä mæ}ä/) y/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/invalidsnap/version-2/snapshot.83f0100644 0000000 0000000 00000011330 15051152474 033627 0ustar00rootroot0000000 0000000 ZKSNÿÿÿÿÿÿÿÿ mæu½N worldanyoneÿÿÿÿÿÿÿÿ m/3/3!{{ mæ{Æ mæ{Æ 9/3/3/3/3!»» mæ| mæ|Ï/3/3/19/3/3/19!ÏÏ mæ|6 mæ|6Ï/3/3/17/3/3/17!ÍÍ mæ|5 mæ|5Í/3/3/18/3/3/18!ÎÎ mæ|6 mæ|6Î/3/3/15/3/3/15!ËË mæ|3 mæ|3Ë/3/3/16/3/3/16!ÌÌ mæ|4 mæ|4Ì/3/3/13/3/3/13!ÉÉ mæ|/ mæ|/É/3/3/14/3/3/14!ÊÊ mæ|1 mæ|1Ê/3/3/11/3/3/11!ÇÇ mæ|, mæ|,Ç/3/3/12/3/3/12!ÈÈ mæ|. mæ|.È/3/3/3/3/3/3!¿¿ mæ| mæ|¿/3/3/2/3/3/2!¾¾ mæ| mæ|¾/3/3/1/3/3/1!½½ mæ| mæ|½/3/3/10/3/3/10!ÆÆ mæ|+ mæ|+Æ/3/3/0/3/3/0!¼¼ mæ| mæ|¼/3/3/7/3/3/7!Ãà mæ|& mæ|&Ã/3/3/6/3/3/6! mæ| mæ|Â/3/3/5/3/3/5!ÁÁ mæ| mæ|Á/3/3/4/3/3/4!ÀÀ mæ| mæ|À/3/3/9/3/3/9!ÅÅ mæ|( mæ|(Å/3/3/8/3/3/8!ÄÄ mæ|' mæ|'Ä/3/2/3/2!¦¦ mæ{ý mæ{ýº/3/2/19/3/2/19!ºº mæ| mæ|º/3/2/17/3/2/17!¸¸ mæ| mæ|¸/3/2/18/3/2/18!¹¹ mæ| mæ|¹/3/2/15/3/2/15!¶¶ mæ| mæ|¶/3/2/16/3/2/16!·· mæ| mæ|·/3/2/13/3/2/13!´´ mæ| mæ|´/3/2/14/3/2/14!µµ mæ| mæ|µ/3/2/11/3/2/11!²² mæ|  mæ| ²/3/2/12/3/2/12!³³ mæ|  mæ| ³/3/2/3/3/2/3!ªª mæ| mæ|ª/3/2/2/3/2/2!©© mæ| mæ|©/3/2/1/3/2/1!¨¨ mæ{ÿ mæ{ÿ¨/3/2/10/3/2/10!±± mæ|  mæ| ±/3/2/0/3/2/0!§§ mæ{þ mæ{þ§/3/2/7/3/2/7!®® mæ| mæ|®/3/2/6/3/2/6!­­ mæ| mæ|­/3/2/5/3/2/5!¬¬ mæ| mæ|¬/3/2/4/3/2/4!«« mæ| mæ|«/3/2/9/3/2/9!°° mæ|  mæ| °/3/2/8/3/2/8!¯¯ mæ| mæ|¯/3/1/3/1!‘‘ mæ{á mæ{á¥/3/1/19/3/1/19!¥¥ mæ{ú mæ{ú¥/3/1/17/3/1/17!££ mæ{ø mæ{ø£/3/1/18/3/1/18!¤¤ mæ{ù mæ{ù¤/3/1/15/3/1/15!¡¡ mæ{ó mæ{ó¡/3/1/16/3/1/16!¢¢ mæ{õ mæ{õ¢/3/1/13/3/1/13!ŸŸ mæ{ð mæ{ðŸ/3/1/14/3/1/14!   mæ{ò mæ{ò /3/1/11/3/1/11! mæ{î mæ{î:BûE/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/kerberos/minikdc-krb5.conf0100644 0000000 0000000 00000002117 15051152474 032257 0ustar00rootroot0000000 0000000 # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This resource is originally from HDFS, see the similarly named files there # in case of bug fixing, history, etc. # Branch : trunk # Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33 # [libdefaults] default_realm = {0} udp_preference_limit = 1 [realms] {0} = '{' kdc = {1}:{2} '}'apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/kerberos/minikdc.ldiff0100644 0000000 0000000 00000003136 15051152474 031557 0ustar00rootroot0000000 0000000 # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This resource is originally from HDFS, see the similarly named files there # in case of bug fixing, history, etc. # Branch : trunk # Github Revision: 1d1ab587e4e92ce3aea4cb144811f69145cb3b33 # dn: ou=users,dc=${0},dc=${1} objectClass: organizationalUnit objectClass: top ou: users dn: uid=krbtgt,ou=users,dc=${0},dc=${1} objectClass: top objectClass: person objectClass: inetOrgPerson objectClass: krb5principal objectClass: krb5kdcentry cn: KDC Service sn: Service uid: krbtgt userPassword: secret krb5PrincipalName: krbtgt/${2}.${3}@${2}.${3} krb5KeyVersionNumber: 0 dn: uid=ldap,ou=users,dc=${0},dc=${1} objectClass: top objectClass: person objectClass: inetOrgPerson objectClass: krb5principal objectClass: krb5kdcentry cn: LDAP sn: Service uid: ldap userPassword: secret krb5PrincipalName: ldap/${4}@${2}.${3} krb5KeyVersionNumber: 0apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/ssl/README.md0100644 0000000 0000000 00000000246 15051152474 027376 0ustar00rootroot0000000 0000000 SSL test data =================== testKeyStore.jks --- Testing keystore, password is "testpass". testTrustStore.jks --- Testing truststore, password is "testpass". apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/ssl/testKeyStore.jks0100644 0000000 0000000 00000004312 15051152474 031273 0ustar00rootroot0000000 0000000 þíþítestK<4Œ´0‚þ0 +*‚ê1"¼od?!&PÏ<Àp̉4{Ì1Ù,V*ÜžÓŽrod…,Òf3pMÃi¼{š×Kg¾Hî`ýå×e!¼åØjÙõKò˜1q6Sñeâ<…ìÊ¿#G}eƒÎÂUgÁÑ"½ˆþ‘ ¥»ªOði–ª©71p޼üèÔ |”Á£DÎ(Rˆ|ýc\$0· ¹ÏâžíïÀT½=Ö²SBi%>Çk—§„KYvî¢;žBybrbÑgli!*2Ècù(þ?ÕADû„]n``$W¡FW ÖéäŠ:g]‹,ü6•=tRŽuü}>ÂnÖ£­­.E,IÌ.ˆpºÑ]„hJ˜&ßË_h¦/årÉeX•³ +!®)ù,”SÔÄ.ÐgU´wXD£Ò•š·y¿uÞ-_!ÉÔ¹a"!C0Ì2Q„LƒþžÉÚq´ ½äMU! yr¤Ë± \6g;î/<Ö!.÷WðHö,Wö»â˜ßo”v£ T"%KWÿûn«…ŸäÓõu¹&ëþ°š! tó8-Ÿ#$ÙÔk˜L¹ än*^ÁwS°(WÄâáœ#µ²Îê;«8Èf®znH‹{%9¹ÇÆŽ™°U¿§èÜ…–Ë×v®wÍhàç ¼ê†¾`Ðõ?¨J¤xªoÓñr°ÝÑ nPìRf‡)䈗„)wÄú/œÄ»éç»0vV¾Oç”Ó‘ÆB)Õ]mo†‚ATî» î=‰ŸÔ£ôͦLãËÏÏî²öZ‰µš¿^H\‡gó{»]$[y¡ ¬­†¥!µtS±šöß_ÝRJ<`ñ¤íç›”A‹ˆöå‡0ZU+ЛëJl Œ4,iæ;“¨V(­Plà4Cs• ßÅ[+MܶQ*í:DÖ†?CZÆêO¶Ñ‡ª“Çæçv ¡™¦-Õ:ió峞 wêMïˆ YFÚk¾ùF|Ï¿Oï‹ê_re~ò¯$Ñ.R.²#Áò±èð¨Jçø£±é/OÓк̶ö«2•ÌFëy™мò½¹£Ì†Ü.oÚ7õI瘪«Ã­5߯0¬¨M ÷ê "«*Ûlö:ÈŒM…øÏKÈþœƒ|¤JY>aûƒdÇñ›gtÀ+w˜yyÚ˦)ǰ,ñ1e•È·I»ew¸u •n%É®µ‡ÖÙûuGC0ë NÝɘ«• °Š]ÊJh©f—Ë[àEUõ*¡eþ§aêFÍH/ž ¿6–žíì±-Ó¹ÊÀ‚´î¬Þ´„· ´Á4$´è9›ÿdLæ9ãðw¨„ˆÔa‚BºdIêHŠoe×Àµ˜lV†ñùMYP®ýâ‘yn3nq P«Ÿš%.Žßtþ72â„>OS nµt2 Pêˆ@7+A‰åFŽÎ¼J©–V„÷‹¤–¾®]ç?ý¨àRŸJPÎ]oÕs®Åõzr7S»uIuÀÿ3õ53Ý|b‡%ðóÿ÷åÝí8qª¿ôÖ¤Þ®Â@ê¨/ä{ówuöµ¸½å­Z©Uë\QÅL^w1tñbXÒ'‘ ÆóCH¤Ù؈"¹™#°+œ¬¡Ì.Ës7\=à ‘Âæ³)9ÿ#Õݲß9d¥•Aµ LûM°üÌ?$Ž´“ oPŸX.509ƒ0‚0‚g s4µ0  *†H†÷  0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0  150130185730Z30150207185730Z0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0‚"0  *†H†÷ ‚0‚ ‚¢!^@0†Él{g¹é~79¹üB:Ûn Å†RÖ“häN²1кÌQtÚ_Vµnƒ>¼]È7ŽšW0©ìë7ÍtåôÒ¬ÃÖ‚üÓ<0Ú,ŨðƒJL¥ÞzÕ `Òœš¨0QeV[ `Ôò6»ÇG}äcð‘Áç™óÎî²eLp=,Sœ~k_T@ûBD)çCçåÍÍHE‰±/.µŸß®’É?] —¿F>ô6°2¿Š­Ô¾¬gñõ²oîéÓ%~ÿ_V?*±»ýç‡|660p›á+Ö#ŸÕŒ>,Õ›‚AðuSÇ?"m ví•€ee±“£!00Ue×´à'EzX/­Õ d-wˆ÷ªõ0  *†H†÷  ‚Èk6ƒ(7\}öÆ„ ¿ÊÀGÏÅ4pzÏkê4„¾z˜æ=íâoÛ#«A­Õß’ôý|î¾J‡¸ñþô[yÜ­÷D=gz;‘×ZyD–×¾AÈdã‡Ã(«Ü6õ°¬½/n¡R%Zìo­ qZfÔ ¦I·uCÄ„ K¬½vX+…âñDgÑ<sZ-Ö ¡Ó ~ïˆùäGêe]Íð9ê/Y¼ùÌ[É»/ —µ!»`¤9lA*Q±Ã£ŒøígÐîìR*äÒ´4sÜ~4$©Nw­ý³w ÂzÀeö̪Yí_aæÂðå¡IAµïž‘ 5œ'$A.ÂÜ‘ñÈ$)é©V ¨dapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/ssl/testTrustStore.jks0100644 0000000 0000000 00000001700 15051152474 031662 0ustar00rootroot0000000 0000000 þíþítestK<5›_X.509ƒ0‚0‚g s4µ0  *†H†÷  0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0  150130185730Z30150207185730Z0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0‚"0  *†H†÷ ‚0‚ ‚¢!^@0†Él{g¹é~79¹üB:Ûn Å†RÖ“häN²1кÌQtÚ_Vµnƒ>¼]È7ŽšW0©ìë7ÍtåôÒ¬ÃÖ‚üÓ<0Ú,ŨðƒJL¥ÞzÕ `Òœš¨0QeV[ `Ôò6»ÇG}äcð‘Áç™óÎî²eLp=,Sœ~k_T@ûBD)çCçåÍÍHE‰±/.µŸß®’É?] —¿F>ô6°2¿Š­Ô¾¬gñõ²oîéÓ%~ÿ_V?*±»ýç‡|660p›á+Ö#ŸÕŒ>,Õ›‚AðuSÇ?"m ví•€ee±“£!00Ue×´à'EzX/­Õ d-wˆ÷ªõ0  *†H†÷  ‚Èk6ƒ(7\}öÆ„ ¿ÊÀGÏÅ4pzÏkê4„¾z˜æ=íâoÛ#«A­Õß’ôý|î¾J‡¸ñþô[yÜ­÷D=gz;‘×ZyD–×¾AÈdã‡Ã(«Ü6õ°¬½/n¡R%Zìo­ qZfÔ ¦I·uCÄ„ K¬½vX+…âñDgÑ<sZ-Ö ¡Ó ~ïˆùäGêe]Íð9ê/Y¼ùÌ[É»/ —µ!»`¤9lA*Q±Ã£ŒøígÐîìR*äÒ´4sÜ~4$©Nw­ý³w ÂzÀeö̪Yí_aæÂðå¡IAµïž‘ 5œîK1¨ÎÓhëhiN ,ÂÅ\5apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/upgrade/log.1000000010100644 0000000 0000000 00011302000 15051152474 030404 0ustar00rootroot0000000 0000000 $ý“c~ý“j[ÿÿÿöN BKý“c~ý“jf/test-0worldanyoneB$ý“baý“jpÿÿÿöN BMý“c~ý“js /test-0/0worldanyoneBKý“baý“jx/test-1worldanyoneBMý“c~ý“j~ /test-0/1worldanyoneB$ý“c~ý“j„ÿÿÿöN BMý“baý“j… /test-1/0worldanyoneBKý“c~ ý“j‰/test-2worldanyoneBMý“c~ ý“jŒ /test-0/2worldanyoneBMý“ba ý“j‘ /test-1/1worldanyoneB$ý“cz ý“j•ÿÿÿöN BMý“c~ ý“j– /test-2/0worldanyoneBMý“c~ý“j˜ /test-0/3worldanyoneBKý“czý“jš/test-3worldanyoneBMý“baý“jœ /test-1/2worldanyoneBMý“c~ý“j¢ /test-2/1worldanyoneBMý“c~ý“j§ /test-0/4worldanyoneB$ý“c~ý“j©ÿÿÿöN BMý“baý“j© /test-1/3worldanyoneBMý“czý“j« /test-3/0worldanyoneBMý“c~ý“j® /test-2/2worldanyoneBKý“c~ý“j°/test-4worldanyoneBMý“c~ý“j³ /test-0/5worldanyoneBMý“baý“j¹ /test-1/4worldanyoneBMý“czý“j» /test-3/1worldanyoneBMý“c~ý“j¼ /test-2/3worldanyoneBMý“c~ý“jÀ /test-4/0worldanyoneBMý“c~ý“jà /test-0/6worldanyoneB$ý“czý“jÅÿÿÿöN BMý“baý“jÇ /test-1/5worldanyoneBMý“cz ý“jÍ /test-3/2worldanyoneBKý“cz!ý“jÏ/test-5worldanyoneBMý“c~"ý“jÐ /test-4/1worldanyoneBMý“c~#ý“jÐ /test-2/4worldanyoneBMý“c~ $ý“jÒ /test-0/7worldanyoneBMý“ba%ý“jÛ /test-1/6worldanyoneBMý“cz&ý“jâ /test-3/3worldanyoneBMý“ba 'ý“jè /test-1/7worldanyoneB$ý“ba(ý“jíÿÿÿöN BMý“c~ )ý“jí /test-0/8worldanyoneBMý“c~*ý“jí /test-2/5worldanyoneBMý“c~+ý“jï /test-4/2worldanyoneBMý“cz,ý“jó /test-5/0worldanyoneBMý“ba -ý“j÷ /test-1/8worldanyoneBKý“ba.ý“jþ/test-6worldanyoneBMý“cz/ý“k /test-3/4worldanyoneBMý“c~0ý“k /test-2/6worldanyoneBMý“c~1ý“k  /test-4/3worldanyoneBMý“ba 2ý“k  /test-1/9worldanyoneBMý“c~ 3ý“k  /test-0/9worldanyoneB$ý“cz4ý“kÿÿÿöN BMý“ba5ý“k /test-6/0worldanyoneBMý“cz6ý“k /test-5/1worldanyoneBMý“cz7ý“k /test-3/5worldanyoneBMý“c~ 8ý“k /test-2/7worldanyoneBNý“ba 9ý“k  /test-1/10worldanyoneBKý“cz:ý“k%/test-7worldanyoneBMý“c~;ý“k% /test-4/4worldanyoneBNý“c~ <ý“k& /test-0/10worldanyoneBMý“ba=ý“k' /test-6/1worldanyoneBMý“c~ >ý“k+ /test-2/8worldanyoneBMý“cz?ý“k- /test-3/6worldanyoneBMý“cz@ý“k. /test-5/2worldanyoneBNý“ba Aý“k0 /test-1/11worldanyoneBNý“baBý“k@ /test-1/12worldanyoneB$ý“cŠCý“k@ÿÿÿöN BMý“baDý“kA /test-6/2worldanyoneBMý“c~Eý“kD /test-4/5worldanyoneBMý“c~ Fý“kF /test-2/9worldanyoneBMý“cz Gý“kG /test-3/7worldanyoneBNý“c~ Hý“kG /test-0/11worldanyoneBMý“czIý“kH /test-5/3worldanyoneBMý“czJý“kL /test-7/0worldanyoneBNý“baKý“kV /test-1/13worldanyoneBKý“cŠLý“kY/test-8worldanyoneBMý“cz Mý“k] /test-3/8worldanyoneBNý“c~ Ný“k^ /test-2/10worldanyoneBMý“c~Oý“k_ /test-4/6worldanyoneBMý“baPý“k_ /test-6/3worldanyoneBNý“c~Qý“k` /test-0/12worldanyoneBMý“czRý“kc /test-5/4worldanyoneBMý“czSý“kf /test-7/1worldanyoneBNý“baTý“kl /test-1/14worldanyoneB$ý“cŠUý“ktÿÿÿöN BMý“cŠVý“kt /test-8/0worldanyoneBMý“baWý“kw /test-6/4worldanyoneBMý“cz Xý“ky /test-3/9worldanyoneBNý“c~ Yý“kz /test-2/11worldanyoneBNý“c~Zý“kz /test-0/13worldanyoneBMý“c~ [ý“k‚ /test-4/7worldanyoneBMý“cz\ý“kƒ /test-5/5worldanyoneBNý“ba]ý“k‡ /test-1/15worldanyoneBMý“cz^ý“kˆ /test-7/2worldanyoneBKý“cŠ_ý“k“/test-9worldanyoneBNý“c~`ý“k— /test-0/14worldanyoneBMý“baaý“k— /test-6/5worldanyoneBNý“cz bý“k˜ /test-3/10worldanyoneBMý“czcý“k™ /test-5/6worldanyoneBMý“cŠdý“k› /test-8/1worldanyoneBNý“c~eý“k /test-2/12worldanyoneBMý“c~ fý“k /test-4/8worldanyoneBNý“bagý“k  /test-1/16worldanyoneBMý“czhý“k¢ /test-7/3worldanyoneBMý“cŠiý“k¦ /test-9/0worldanyoneBMý“bajý“k© /test-6/6worldanyoneBNý“cz ký“k¬ /test-3/11worldanyoneBMý“cz lý“k­ /test-5/7worldanyoneBMý“cŠmý“k­ /test-8/2worldanyoneBNý“c~ný“k³ /test-0/15worldanyoneBNý“c~oý“k³ /test-2/13worldanyoneBMý“c~ pý“k´ /test-4/9worldanyoneBNý“baqý“k´ /test-1/17worldanyoneBMý“czrý“kµ /test-7/4worldanyoneBMý“ba sý“k¶ /test-6/7worldanyoneBMý“cŠtý“k¹ /test-9/1worldanyoneBMý“cŠuý“k¼ /test-8/3worldanyoneBNý“czvý“k½ /test-3/12worldanyoneBMý“cz wý“k¿ /test-5/8worldanyoneBNý“c~xý“kÄ /test-0/16worldanyoneBNý“bayý“kÈ /test-1/18worldanyoneBMý“ba zý“kÈ /test-6/8worldanyoneBNý“c~{ý“kÉ /test-2/14worldanyoneBMý“cŠ|ý“kÉ /test-9/2worldanyoneBNý“c~ }ý“kÊ /test-4/10worldanyoneBMý“cz~ý“kË /test-7/5worldanyoneBMý“cŠý“kÍ /test-8/4worldanyoneBNý“cz€ý“kÎ /test-3/13worldanyoneBMý“cz ý“kÎ /test-5/9worldanyoneBNý“c~‚ý“kÓ /test-0/17worldanyoneBNý“baƒý“kÝ /test-1/19worldanyoneBMý“ba „ý“kÝ /test-6/9worldanyoneBMý“cŠ…ý“kß /test-8/5worldanyoneBMý“cІý“kß /test-9/3worldanyoneBMý“cz‡ý“kà /test-7/6worldanyoneBNý“c~ˆý“kà /test-2/15worldanyoneBNý“cz‰ý“kà /test-3/14worldanyoneBNý“cz Šý“ká /test-5/10worldanyoneBNý“c~ ‹ý“kã /test-4/11worldanyoneBNý“c~Œý“kë /test-0/18worldanyoneBNý“baý“kï /test-1/20worldanyoneBNý“ba Žý“kï /test-6/10worldanyoneBMý“cz ý“kò /test-7/7worldanyoneBNý“cz ý“kó /test-5/11worldanyoneBNý“c~‘ý“kõ /test-4/12worldanyoneBNý“c~’ý“kõ /test-2/16worldanyoneBMý“cŠ“ý“kõ /test-8/6worldanyoneBMý“cŠ”ý“kö /test-9/4worldanyoneBNý“cz•ý“kö /test-3/15worldanyoneBNý“c~–ý“k÷ /test-0/19worldanyoneBNý“ba—ý“kû /test-1/21worldanyoneBNý“ba ˜ý“ký /test-6/11worldanyoneBMý“cz ™ý“l /test-7/8worldanyoneBNý“czšý“l /test-5/12worldanyoneBNý“ba›ý“l /test-1/22worldanyoneBMý“cŠ œý“l /test-8/7worldanyoneBMý“cŠý“l /test-9/5worldanyoneBNý“c~žý“l /test-2/17worldanyoneBNý“baŸý“l /test-6/12worldanyoneBNý“c~ ý“l /test-4/13worldanyoneBNý“c~¡ý“l /test-0/20worldanyoneBNý“cz¢ý“l /test-3/16worldanyoneBMý“cz £ý“l /test-7/9worldanyoneBNý“cz¤ý“l /test-5/13worldanyoneBNý“ba¥ý“l /test-1/23worldanyoneBNý“ba¦ý“l" /test-6/13worldanyoneBNý“c~§ý“l" /test-2/18worldanyoneBMý“cЍý“l# /test-9/6worldanyoneBMý“cŠ ©ý“l& /test-8/8worldanyoneBNý“c~ªý“l& /test-0/21worldanyoneBNý“c~«ý“l& /test-4/14worldanyoneBNý“cz ¬ý“l( /test-7/10worldanyoneBNý“cz­ý“l( /test-5/14worldanyoneBNý“cz®ý“l) /test-3/17worldanyoneBNý“ba¯ý“l) /test-1/24worldanyoneBNý“c~°ý“l4 /test-2/19worldanyoneBMý“cŠ ±ý“l6 /test-9/7worldanyoneBNý“ba²ý“l7 /test-6/14worldanyoneBNý“cz ³ý“l8 /test-7/11worldanyoneBNý“c~´ý“l: /test-4/15worldanyoneBMý“cŠ µý“l: /test-8/9worldanyoneBNý“c~¶ý“l< /test-0/22worldanyoneBNý“ba·ý“l? /test-1/25worldanyoneBNý“cz¸ý“l@ /test-5/15worldanyoneBNý“cz¹ý“l@ /test-3/18worldanyoneBNý“c~ºý“lD /test-2/20worldanyoneBNý“ba»ý“lE /test-6/15worldanyoneBMý“cŠ ¼ý“lF /test-9/8worldanyoneBNý“cz½ý“lH /test-7/12worldanyoneBNý“c~¾ý“lJ /test-0/23worldanyoneBNý“cŠ ¿ý“lJ /test-8/10worldanyoneBNý“c~Àý“lK /test-4/16worldanyoneBNý“baÁý“lL /test-1/26worldanyoneBNý“czÂý“lN /test-3/19worldanyoneBNý“czÃý“lP /test-5/16worldanyoneBNý“c~Äý“lV /test-2/21worldanyoneBNý“baÅý“lW /test-6/16worldanyoneBMý“cŠ Æý“lY /test-9/9worldanyoneBNý“czÇý“lZ /test-7/13worldanyoneBNý“c~Èý“l] /test-0/24worldanyoneBNý“baÉý“l] /test-1/27worldanyoneBNý“czÊý“l^ /test-3/20worldanyoneBNý“czËý“l^ /test-5/17worldanyoneBNý“cŠ Ìý“l` /test-8/11worldanyoneBNý“c~Íý“l` /test-4/17worldanyoneBNý“c~Îý“lb /test-2/22worldanyoneBNý“baÏý“ld /test-6/17worldanyoneBNý“cŠ Ðý“li /test-9/10worldanyoneBNý“czÑý“ll /test-7/14worldanyoneBNý“baÒý“ln /test-1/28worldanyoneBNý“cŠÓý“lp /test-8/12worldanyoneBNý“czÔý“lp /test-3/21worldanyoneBNý“baÕý“ls /test-6/18worldanyoneBNý“c~Öý“lt /test-0/25worldanyoneBNý“c~×ý“lt /test-4/18worldanyoneBNý“c~Øý“lt /test-2/23worldanyoneBNý“cŠ Ùý“lu /test-9/11worldanyoneBNý“czÚý“lv /test-5/18worldanyoneBNý“czÛý“l /test-7/15worldanyoneBNý“c~Üý“l„ /test-0/26worldanyoneBNý“c~Ýý“l„ /test-4/19worldanyoneBNý“baÞý“l… /test-6/19worldanyoneBNý“baßý“l… /test-1/29worldanyoneBNý“c~àý“l† /test-2/24worldanyoneBNý“cŠáý“lˆ /test-8/13worldanyoneBNý“cŠâý“lˆ /test-9/12worldanyoneBNý“czãý“lˆ /test-3/22worldanyoneBNý“czäý“l‰ /test-5/19worldanyoneBNý“czåý“l‹ /test-7/16worldanyoneBNý“c~æý“l“ /test-0/27worldanyoneBNý“c~çý“l– /test-4/20worldanyoneBNý“c~èý“l– /test-2/25worldanyoneBNý“baéý“l˜ /test-6/20worldanyoneBNý“cŠêý“l˜ /test-9/13worldanyoneBNý“ba ëý“l˜ /test-1/30worldanyoneBNý“czìý“lš /test-5/20worldanyoneBNý“czíý“l› /test-3/23worldanyoneBNý“cŠîý“l› /test-8/14worldanyoneBNý“czïý“lœ /test-7/17worldanyoneBNý“c~ðý“lŸ /test-0/28worldanyoneBNý“c~ñý“l© /test-2/26worldanyoneBNý“c~òý“l© /test-4/21worldanyoneBNý“baóý“lª /test-6/21worldanyoneBNý“ba!ôý“l« /test-1/31worldanyoneBNý“cŠõý“l« /test-8/15worldanyoneBNý“cŠöý“l¬ /test-9/14worldanyoneBNý“cz÷ý“l¯ /test-5/21worldanyoneBNý“czøý“l° /test-7/18worldanyoneBNý“czùý“l° /test-3/24worldanyoneBNý“c~úý“l° /test-0/29worldanyoneBNý“ba"ûý“l½ /test-1/32worldanyoneBNý“baüý“l¾ /test-6/22worldanyoneBNý“cŠýý“l¾ /test-8/16worldanyoneBNý“c~þý“lÀ /test-2/27worldanyoneBNý“c~ÿý“lÁ /test-4/22worldanyoneBNý“czý“l /test-3/25worldanyoneBNý“cŠý“l /test-9/15worldanyoneBNý“czý“l /test-7/19worldanyoneBNý“czý“là /test-5/22worldanyoneBNý“c~ ý“là /test-0/30worldanyoneBNý“ba#ý“lÎ /test-1/33worldanyoneBNý“baý“lÐ /test-6/23worldanyoneBNý“cŠý“lÑ /test-8/17worldanyoneBNý“c~ý“lÔ /test-2/28worldanyoneBNý“cz ý“lÕ /test-5/23worldanyoneBNý“cz ý“lÖ /test-3/26worldanyoneBNý“cŠ ý“lÖ /test-9/16worldanyoneBNý“c~ ý“l× /test-4/23worldanyoneBNý“c~! ý“lÙ /test-0/31worldanyoneBNý“czý“lÜ /test-7/20worldanyoneBNý“ba$ý“lÞ /test-1/34worldanyoneBNý“baý“lß /test-6/24worldanyoneBNý“cŠý“lê /test-8/18worldanyoneBNý“cŠý“lê /test-9/17worldanyoneBNý“c~ý“lê /test-2/29worldanyoneBNý“c~ý“lë /test-4/24worldanyoneBNý“czý“lì /test-5/24worldanyoneBNý“czý“lí /test-3/27worldanyoneBNý“c~"ý“lñ /test-0/32worldanyoneBNý“czý“lø /test-7/21worldanyoneBNý“cŠý“lû /test-9/18worldanyoneBNý“cŠý“lû /test-8/19worldanyoneBNý“czý“lü /test-5/25worldanyoneBNý“baý“lÿ /test-6/25worldanyoneBNý“ba%ý“m /test-1/35worldanyoneBNý“czý“m /test-3/28worldanyoneBNý“c~ý“m /test-4/25worldanyoneBNý“c~  ý“m /test-2/30worldanyoneBNý“c~#!ý“m /test-0/33worldanyoneBNý“cz"ý“m /test-7/22worldanyoneBNý“cŠ#ý“m  /test-9/19worldanyoneBNý“cŠ$ý“m  /test-8/20worldanyoneBNý“cz%ý“m  /test-5/26worldanyoneBNý“ba&ý“m /test-6/26worldanyoneBNý“ba&'ý“m /test-1/36worldanyoneBNý“cz(ý“m /test-7/23worldanyoneBNý“c~)ý“m /test-4/26worldanyoneBNý“cz*ý“m /test-3/29worldanyoneBNý“c~$+ý“m /test-0/34worldanyoneBNý“c~!,ý“m /test-2/31worldanyoneBNý“cŠ-ý“m /test-9/20worldanyoneBNý“cŠ.ý“m /test-8/21worldanyoneBNý“cz/ý“m /test-5/27worldanyoneBNý“ba'0ý“m" /test-1/37worldanyoneBNý“ba1ý“m$ /test-6/27worldanyoneBNý“cz 2ý“m% /test-3/30worldanyoneBNý“cz3ý“m% /test-7/24worldanyoneBNý“c~4ý“m' /test-4/27worldanyoneBNý“c~%5ý“m( /test-0/35worldanyoneBNý“c~"6ý“m) /test-2/32worldanyoneBNý“cz7ý“m* /test-5/28worldanyoneBNý“cŠ8ý“m* /test-9/21worldanyoneBNý“cŠ9ý“m* /test-8/22worldanyoneBNý“ba(:ý“m6 /test-1/38worldanyoneBNý“ba;ý“m7 /test-6/28worldanyoneBNý“cŠ<ý“m: /test-8/23worldanyoneBNý“cŠ=ý“m; /test-9/22worldanyoneBNý“c~>ý“m= /test-4/28worldanyoneBNý“cz!?ý“m= /test-3/31worldanyoneBNý“cz@ý“m> /test-7/25worldanyoneBNý“c~&Aý“m@ /test-0/36worldanyoneBNý“c~#Bý“mA /test-2/33worldanyoneBNý“ba)Cý“mB /test-1/39worldanyoneBNý“czDý“mB /test-5/29worldanyoneBNý“baEý“mD /test-6/29worldanyoneBNý“cŠFý“mP /test-8/24worldanyoneBNý“cŠGý“mP /test-9/23worldanyoneBNý“ba*Hý“mR /test-1/40worldanyoneBNý“cz"Iý“mS /test-3/32worldanyoneBNý“czJý“mS /test-7/26worldanyoneBNý“cz Ký“mS /test-5/30worldanyoneBNý“c~'Lý“mT /test-0/37worldanyoneBNý“c~Mý“mU /test-4/29worldanyoneBNý“c~$Ný“mU /test-2/34worldanyoneBNý“ba Oý“mV /test-6/30worldanyoneBNý“cŠPý“m^ /test-9/24worldanyoneBNý“cŠQý“m^ /test-8/25worldanyoneBNý“ba+Rý“mc /test-1/41worldanyoneBNý“czSý“md /test-7/27worldanyoneBNý“cz#Tý“md /test-3/33worldanyoneBNý“c~ Uý“mg /test-4/30worldanyoneBNý“c~(Vý“mg /test-0/38worldanyoneBNý“ba!Wý“mh /test-6/31worldanyoneBNý“c~%Xý“mj /test-2/35worldanyoneBNý“cz!Yý“mj /test-5/31worldanyoneBNý“cŠZý“mk /test-9/25worldanyoneBNý“cŠ[ý“mk /test-8/26worldanyoneBNý“ba,\ý“mo /test-1/42worldanyoneBNý“cz]ý“ms /test-7/28worldanyoneBNý“cz$^ý“mv /test-3/34worldanyoneBNý“ba"_ý“mz /test-6/32worldanyoneBNý“cŠ`ý“m| /test-8/27worldanyoneBNý“cŠaý“m| /test-9/26worldanyoneBNý“c~)bý“m| /test-0/39worldanyoneBNý“c~!cý“m} /test-4/31worldanyoneBNý“c~&dý“m} /test-2/36worldanyoneBNý“ba-eý“m} /test-1/43worldanyoneBNý“cz"fý“m~ /test-5/32worldanyoneBNý“czgý“m€ /test-7/29worldanyoneBNý“cz%hý“m‚ /test-3/35worldanyoneBNý“ba#iý“m‡ /test-6/33worldanyoneBNý“ba.jý“mŒ /test-1/44worldanyoneBNý“cŠký“mŽ /test-9/27worldanyoneBNý“c~*lý“m“ /test-0/40worldanyoneBNý“c~'mý“m• /test-2/37worldanyoneBNý“cz&ný“m• /test-3/36worldanyoneBNý“ba$oý“m– /test-6/34worldanyoneBNý“c~"pý“m˜ /test-4/32worldanyoneBNý“cz qý“m™ /test-7/30worldanyoneBNý“ba/rý“m™ /test-1/45worldanyoneBNý“cz#sý“mš /test-5/33worldanyoneBNý“cŠtý“mš /test-8/28worldanyoneBNý“c~+uý“m¤ /test-0/41worldanyoneBNý“cŠvý“m¥ /test-9/28worldanyoneBNý“ba%wý“m§ /test-6/35worldanyoneBNý“c~(xý“m© /test-2/38worldanyoneBNý“cz'yý“mª /test-3/37worldanyoneBNý“ba0zý“m« /test-1/46worldanyoneBNý“c~#{ý“m« /test-4/33worldanyoneBNý“cz!|ý“m« /test-7/31worldanyoneBNý“cz$}ý“m­ /test-5/34worldanyoneBNý“cŠ~ý“m­ /test-8/29worldanyoneBNý“cŠý“m° /test-9/29worldanyoneBNý“c~,€ý“m± /test-0/42worldanyoneBNý“ba&ý“m³ /test-6/36worldanyoneBNý“ba1‚ý“m¼ /test-1/47worldanyoneBNý“cz(ƒý“m¾ /test-3/38worldanyoneBNý“cz"„ý“m¾ /test-7/32worldanyoneBNý“c~)…ý“m¿ /test-2/39worldanyoneBNý“c~$†ý“mÀ /test-4/34worldanyoneBNý“cŠ ‡ý“mÁ /test-9/30worldanyoneBNý“cŠ ˆý“mÁ /test-8/30worldanyoneBNý“ba'‰ý“mÁ /test-6/37worldanyoneBNý“cz%Šý“m /test-5/35worldanyoneBNý“c~-‹ý“m /test-0/43worldanyoneBNý“ba2Œý“mÑ /test-1/48worldanyoneBNý“cz&ý“mÜ /test-5/36worldanyoneBNý“cz)Žý“mÝ /test-3/39worldanyoneBNý“cz#ý“mÝ /test-7/33worldanyoneBNý“cŠ!ý“mß /test-9/31worldanyoneBNý“ba3‘ý“mß /test-1/49worldanyoneBNý“c~.’ý“mà /test-0/44worldanyoneBNý“ba(“ý“má /test-6/38worldanyoneBNý“cŠ!”ý“mâ /test-8/31worldanyoneBNý“c~%•ý“mâ /test-4/35worldanyoneBNý“c~*–ý“mã /test-2/40worldanyoneBNý“cz'—ý“më /test-5/37worldanyoneBNý“cŠ"˜ý“mò /test-9/32worldanyoneBNý“ba)™ý“mò /test-6/39worldanyoneBNý“ba4šý“mò /test-1/50worldanyoneBNý“c~/›ý“mò /test-0/45worldanyoneBNý“cz*œý“mó /test-3/40worldanyoneBNý“cz$ý“mó /test-7/34worldanyoneBNý“c~+žý“mô /test-2/41worldanyoneBNý“c~&Ÿý“mö /test-4/36worldanyoneBNý“cŠ" ý“mö /test-8/32worldanyoneBNý“cz(¡ý“m÷ /test-5/38worldanyoneBNý“ba5¢ý“n /test-1/51worldanyoneBNý“c~0£ý“n /test-0/46worldanyoneBNý“cŠ#¤ý“n /test-9/33worldanyoneBNý“ba*¥ý“n /test-6/40worldanyoneBNý“cz%¦ý“n /test-7/35worldanyoneBNý“cz+§ý“n /test-3/41worldanyoneBNý“c~,¨ý“n /test-2/42worldanyoneBNý“cz)©ý“n /test-5/39worldanyoneBNý“c~'ªý“n /test-4/37worldanyoneBNý“cŠ#«ý“n /test-8/33worldanyoneBNý“ba6¬ý“n /test-1/52worldanyoneBNý“cŠ$­ý“n /test-9/34worldanyoneBNý“ba+®ý“n /test-6/41worldanyoneBNý“cz&¯ý“n /test-7/36worldanyoneBNý“cz,°ý“n /test-3/42worldanyoneBNý“c~1±ý“n /test-0/47worldanyoneBNý“c~-²ý“n /test-2/43worldanyoneBNý“cŠ$³ý“n /test-8/34worldanyoneBNý“cz*´ý“n /test-5/40worldanyoneBNý“c~(µý“n /test-4/38worldanyoneBNý“ba7¶ý“n) /test-1/53worldanyoneBNý“cŠ%·ý“n* /test-9/35worldanyoneBNý“ba,¸ý“n+ /test-6/42worldanyoneBNý“c~)¹ý“n. /test-4/39worldanyoneBNý“c~.ºý“n/ /test-2/44worldanyoneBNý“cŠ%»ý“n/ /test-8/35worldanyoneBNý“c~2¼ý“n/ /test-0/48worldanyoneBNý“cz-½ý“n0 /test-3/43worldanyoneBNý“cz'¾ý“n0 /test-7/37worldanyoneBNý“cz+¿ý“n0 /test-5/41worldanyoneBNý“ba8Àý“n5 /test-1/54worldanyoneBNý“cŠ&Áý“n5 /test-9/36worldanyoneBNý“ba-Âý“n9 /test-6/43worldanyoneBNý“c~/Ãý“n? /test-2/45worldanyoneBNý“cz(Äý“n@ /test-7/38worldanyoneBNý“c~*Åý“n@ /test-4/40worldanyoneBNý“c~3Æý“n@ /test-0/49worldanyoneBNý“cŠ&Çý“n@ /test-8/36worldanyoneBNý“cz.Èý“nB /test-3/44worldanyoneBNý“cz,Éý“nB /test-5/42worldanyoneBNý“cŠ'Êý“nC /test-9/37worldanyoneBNý“ba9Ëý“nC /test-1/55worldanyoneBNý“ba.Ìý“nC /test-6/44worldanyoneBNý“cz)Íý“nN /test-7/39worldanyoneBNý“c~+Îý“nN /test-4/41worldanyoneBNý“c~0Ïý“nO /test-2/46worldanyoneBNý“cz/Ðý“nP /test-3/45worldanyoneBNý“c~4Ñý“nP /test-0/50worldanyoneBNý“cz-Òý“nQ /test-5/43worldanyoneBNý“ba/Óý“nR /test-6/45worldanyoneBNý“cŠ'Ôý“nR /test-8/37worldanyoneBNý“ba:Õý“nR /test-1/56worldanyoneBNý“cŠ(Öý“nT /test-9/38worldanyoneBNý“c~,×ý“n^ /test-4/42worldanyoneBNý“cz*Øý“n^ /test-7/40worldanyoneBNý“c~1Ùý“n^ /test-2/47worldanyoneBNý“cz0Úý“n` /test-3/46worldanyoneBNý“cz.Ûý“nb /test-5/44worldanyoneBNý“ba0Üý“nd /test-6/46worldanyoneBNý“ba;Ýý“nd /test-1/57worldanyoneBNý“cŠ(Þý“nd /test-8/38worldanyoneBNý“cŠ)ßý“ng /test-9/39worldanyoneBNý“c~5àý“ng /test-0/51worldanyoneBNý“c~-áý“nk /test-4/43worldanyoneBNý“c~2âý“nl /test-2/48worldanyoneBNý“cz+ãý“nr /test-7/41worldanyoneBNý“cŠ)äý“nw /test-8/39worldanyoneBNý“cz1åý“nw /test-3/47worldanyoneBNý“cz/æý“nz /test-5/45worldanyoneBNý“cŠ*çý“n| /test-9/40worldanyoneBNý“ba<èý“n| /test-1/58worldanyoneBNý“ba1éý“n} /test-6/47worldanyoneBNý“c~3êý“n /test-2/49worldanyoneBNý“c~.ëý“n€ /test-4/44worldanyoneBNý“c~6ìý“n€ /test-0/52worldanyoneBNý“cz,íý“n‚ /test-7/42worldanyoneBNý“cŠ*îý“n /test-8/40worldanyoneBNý“cz2ïý“nŽ /test-3/48worldanyoneBNý“cz0ðý“n /test-5/46worldanyoneBNý“ba=ñý“n’ /test-1/59worldanyoneBNý“ba2òý“n’ /test-6/48worldanyoneBNý“cŠ+óý“n’ /test-9/41worldanyoneBNý“cz-ôý“n“ /test-7/43worldanyoneBNý“c~7õý“n• /test-0/53worldanyoneBNý“c~/öý“n• /test-4/45worldanyoneBNý“c~4÷ý“n– /test-2/50worldanyoneBNý“cŠ+øý“n˜ /test-8/41worldanyoneBNý“cz3ùý“nŸ /test-3/49worldanyoneBNý“cz1úý“n¡ /test-5/47worldanyoneBNý“ba>ûý“n¥ /test-1/60worldanyoneBNý“cŠ,üý“n¥ /test-9/42worldanyoneBNý“ba3ýý“nª /test-6/49worldanyoneBNý“cz.þý“n¬ /test-7/44worldanyoneBNý“c~0ÿý“n° /test-4/46worldanyoneBNý“cŠ,ý“n± /test-8/42worldanyoneBNý“c~5ý“n² /test-2/51worldanyoneBNý“c~8ý“nµ /test-0/54worldanyoneBNý“cz4ý“n¶ /test-3/50worldanyoneBNý“cz2ý“n¶ /test-5/48worldanyoneBNý“ba?ý“nÁ /test-1/61worldanyoneBNý“ba4ý“nÁ /test-6/50worldanyoneBNý“c~1ý“nÇ /test-4/47worldanyoneBNý“c~6ý“nÇ /test-2/52worldanyoneBNý“cŠ- ý“nÇ /test-8/43worldanyoneBNý“cŠ- ý“nÈ /test-9/43worldanyoneBNý“cz/ ý“nÈ /test-7/45worldanyoneBNý“cz5 ý“nÊ /test-3/51worldanyoneBNý“cz3 ý“nË /test-5/49worldanyoneBNý“c~9ý“nÏ /test-0/55worldanyoneBNý“ba5ý“nØ /test-6/51worldanyoneBNý“ba@ý“nÚ /test-1/62worldanyoneBNý“cŠ.ý“nÜ /test-8/44worldanyoneBNý“cŠ.ý“nÜ /test-9/44worldanyoneBNý“cz6ý“nÞ /test-3/52worldanyoneBNý“c~2ý“nß /test-4/48worldanyoneBNý“c~7ý“nß /test-2/53worldanyoneBNý“cz4ý“nß /test-5/50worldanyoneBNý“cz0ý“nß /test-7/46worldanyoneBNý“c~:ý“nå /test-0/56worldanyoneBNý“ba6ý“nð /test-6/52worldanyoneBNý“cz7ý“nò /test-3/53worldanyoneBNý“cz5ý“nô /test-5/51worldanyoneBNý“cz1ý“nô /test-7/47worldanyoneBNý“baAý“nõ /test-1/63worldanyoneBNý“cŠ/ý“nø /test-8/45worldanyoneBNý“cŠ/ý“nø /test-9/45worldanyoneBNý“c~8 ý“nû /test-2/54worldanyoneBNý“c~3!ý“nû /test-4/49worldanyoneBNý“c~;"ý“nû /test-0/57worldanyoneBNý“ba7#ý“o  /test-6/53worldanyoneBNý“cz8$ý“o  /test-3/54worldanyoneBNý“cz6%ý“o  /test-5/52worldanyoneBNý“cz2&ý“o /test-7/48worldanyoneBNý“baB'ý“o /test-1/64worldanyoneBNý“cŠ0(ý“o /test-9/46worldanyoneBNý“cŠ0)ý“o /test-8/46worldanyoneBNý“c~9*ý“o /test-2/55worldanyoneBNý“c~4+ý“o /test-4/50worldanyoneBNý“c~<,ý“o /test-0/58worldanyoneBNý“cz9-ý“o /test-3/55worldanyoneBNý“ba8.ý“o /test-6/54worldanyoneBNý“cz7/ý“o /test-5/53worldanyoneBNý“baC0ý“o  /test-1/65worldanyoneBNý“cŠ11ý“o! /test-9/47worldanyoneBNý“cŠ12ý“o" /test-8/47worldanyoneBNý“cz33ý“o$ /test-7/49worldanyoneBNý“c~=4ý“o( /test-0/59worldanyoneBNý“c~55ý“o( /test-4/51worldanyoneBNý“c~:6ý“o) /test-2/56worldanyoneBNý“cz:7ý“o/ /test-3/56worldanyoneBNý“cz88ý“o0 /test-5/54worldanyoneBNý“ba99ý“o1 /test-6/55worldanyoneBNý“baD:ý“o1 /test-1/66worldanyoneBNý“cz4;ý“o3 /test-7/50worldanyoneBNý“cŠ2<ý“o4 /test-8/48worldanyoneBNý“cŠ2=ý“o5 /test-9/48worldanyoneBNý“c~>>ý“o7 /test-0/60worldanyoneBNý“c~6?ý“o9 /test-4/52worldanyoneBNý“c~;@ý“o9 /test-2/57worldanyoneBNý“cz;Aý“o@ /test-3/57worldanyoneBNý“c~?Bý“oF /test-0/61worldanyoneBNý“cz9Cý“oH /test-5/55worldanyoneBNý“baEDý“oK /test-1/67worldanyoneBNý“ba:Eý“oL /test-6/56worldanyoneBNý“cŠ3Fý“oN /test-8/49worldanyoneBNý“c~7Gý“oN /test-4/53worldanyoneBNý“cŠ3Hý“oO /test-9/49worldanyoneBNý“c~<Iý“oO /test-2/58worldanyoneBNý“cz<Jý“oQ /test-3/58worldanyoneBNý“cz5Ký“oQ /test-7/51worldanyoneBNý“c~@Lý“oj /test-0/62worldanyoneBNý“baFMý“om /test-1/68worldanyoneBNý“cŠ4Ný“on /test-8/50worldanyoneBNý“cz:Oý“on /test-5/56worldanyoneBNý“c~8Pý“oq /test-4/54worldanyoneBNý“c~=Qý“or /test-2/59worldanyoneBNý“ba;Rý“or /test-6/57worldanyoneBNý“cz=Sý“ot /test-3/59worldanyoneBNý“cz6Tý“ot /test-7/52worldanyoneBNý“cŠ4Uý“oz /test-9/50worldanyoneBNý“c~AVý“o{ /test-0/63worldanyoneBNý“baGWý“o~ /test-1/69worldanyoneBNý“cŠ5Xý“o~ /test-8/51worldanyoneBNý“ba<Yý“o /test-6/58worldanyoneBNý“c~9Zý“o„ /test-4/55worldanyoneBNý“c~>[ý“o‡ /test-2/60worldanyoneBNý“cz7\ý“o‡ /test-7/53worldanyoneBNý“cz>]ý“o‡ /test-3/60worldanyoneBNý“cz;^ý“oˆ /test-5/57worldanyoneBNý“cŠ5_ý“o‹ /test-9/51worldanyoneBNý“c~B`ý“o /test-0/64worldanyoneBNý“baHaý“o /test-1/70worldanyoneBNý“ba=bý“oŽ /test-6/59worldanyoneBNý“cŠ6cý“o‘ /test-8/52worldanyoneBNý“cz<dý“o™ /test-5/58worldanyoneBNý“cz8eý“o™ /test-7/54worldanyoneBNý“cz?fý“o™ /test-3/61worldanyoneBNý“c~?gý“oš /test-2/61worldanyoneBNý“c~:hý“oš /test-4/56worldanyoneBNý“cŠ6iý“o /test-9/52worldanyoneBNý“ba>jý“oŸ /test-6/60worldanyoneBNý“baIký“oŸ /test-1/71worldanyoneBNý“c~Clý“oŸ /test-0/65worldanyoneBNý“cŠ7mý“o£ /test-8/53worldanyoneBNý“cŠ7ný“o® /test-9/53worldanyoneBNý“c~;oý“o° /test-4/57worldanyoneBNý“c~@pý“o° /test-2/62worldanyoneBNý“c~Dqý“o± /test-0/66worldanyoneBNý“cz=rý“o± /test-5/59worldanyoneBNý“cz9sý“o± /test-7/55worldanyoneBNý“ba?tý“o³ /test-6/61worldanyoneBNý“cz@uý“o³ /test-3/62worldanyoneBNý“cŠ8vý“o³ /test-8/54worldanyoneBNý“baJwý“o³ /test-1/72worldanyoneBNý“cŠ8xý“o¼ /test-9/54worldanyoneBNý“c~<yý“o¾ /test-4/58worldanyoneBNý“c~Azý“o¿ /test-2/63worldanyoneBNý“c~E{ý“oÀ /test-0/67worldanyoneBNý“cŠ9|ý“oÀ /test-8/55worldanyoneBNý“ba@}ý“oÁ /test-6/62worldanyoneBNý“cz>~ý“oÁ /test-5/60worldanyoneBNý“cz:ý“oà /test-7/56worldanyoneBNý“czA€ý“oà /test-3/63worldanyoneBNý“baKý“oà /test-1/73worldanyoneBNý“cŠ9‚ý“oÅ /test-9/55worldanyoneBNý“c~=ƒý“oÍ /test-4/59worldanyoneBNý“c~F„ý“oÐ /test-0/68worldanyoneBNý“c~B…ý“oÐ /test-2/64worldanyoneBNý“baL†ý“oÑ /test-1/74worldanyoneBNý“baA‡ý“oÑ /test-6/63worldanyoneBNý“cŠ:ˆý“oÒ /test-8/56worldanyoneBNý“cŠ:‰ý“oÒ /test-9/56worldanyoneBNý“cz;Šý“oÔ /test-7/57worldanyoneBNý“czB‹ý“oÔ /test-3/64worldanyoneBNý“cz?Œý“oÕ /test-5/61worldanyoneBNý“c~>ý“oÚ /test-4/60worldanyoneBNý“c~GŽý“oà /test-0/69worldanyoneBNý“c~Cý“oá /test-2/65worldanyoneBNý“cŠ;ý“oâ /test-8/57worldanyoneBNý“baB‘ý“oâ /test-6/64worldanyoneBNý“baM’ý“oã /test-1/75worldanyoneBNý“cŠ;“ý“oæ /test-9/57worldanyoneBNý“c~?”ý“oæ /test-4/61worldanyoneBNý“cz<•ý“oæ /test-7/58worldanyoneBNý“cz@–ý“oæ /test-5/62worldanyoneBNý“czC—ý“oç /test-3/65worldanyoneBNý“baN˜ý“oó /test-1/76worldanyoneBNý“baC™ý“oó /test-6/65worldanyoneBNý“cŠ<šý“oõ /test-8/58worldanyoneBNý“c~@›ý“oö /test-4/62worldanyoneBNý“cŠ<œý“o÷ /test-9/58worldanyoneBNý“czAý“o÷ /test-5/63worldanyoneBNý“c~Džý“o÷ /test-2/66worldanyoneBNý“czDŸý“o÷ /test-3/66worldanyoneBNý“cz= ý“o÷ /test-7/59worldanyoneBNý“c~H¡ý“oø /test-0/70worldanyoneBNý“baD¢ý“p /test-6/66worldanyoneBNý“baO£ý“p /test-1/77worldanyoneBNý“cŠ=¤ý“p /test-8/59worldanyoneBNý“c~A¥ý“p /test-4/63worldanyoneBNý“cŠ=¦ý“p /test-9/59worldanyoneBNý“c~E§ý“p /test-2/67worldanyoneBNý“c~I¨ý“p /test-0/71worldanyoneBNý“cz>©ý“p  /test-7/60worldanyoneBNý“czBªý“p  /test-5/64worldanyoneBNý“czE«ý“p  /test-3/67worldanyoneBNý“baP¬ý“p /test-1/78worldanyoneBNý“baE­ý“p /test-6/67worldanyoneBNý“cŠ>®ý“p /test-8/60worldanyoneBNý“c~B¯ý“p /test-4/64worldanyoneBNý“cŠ>°ý“p /test-9/60worldanyoneBNý“cz?±ý“p /test-7/61worldanyoneBNý“czF²ý“p /test-3/68worldanyoneBNý“czC³ý“p /test-5/65worldanyoneBNý“c~F´ý“p /test-2/68worldanyoneBNý“c~Jµý“p /test-0/72worldanyoneBNý“baQ¶ý“p /test-1/79worldanyoneBNý“baF·ý“p /test-6/68worldanyoneBNý“cŠ?¸ý“p! /test-8/61worldanyoneBNý“c~C¹ý“p& /test-4/65worldanyoneBNý“cŠ?ºý“p' /test-9/61worldanyoneBNý“c~G»ý“p+ /test-2/69worldanyoneBNý“c~K¼ý“p, /test-0/73worldanyoneBNý“czG½ý“p. /test-3/69worldanyoneBNý“cz@¾ý“p. /test-7/62worldanyoneBNý“czD¿ý“p. /test-5/66worldanyoneBNý“baGÀý“p1 /test-6/69worldanyoneBNý“baRÁý“p2 /test-1/80worldanyoneBNý“cŠ@Âý“p6 /test-9/62worldanyoneBNý“c~DÃý“p7 /test-4/66worldanyoneBNý“cŠ@Äý“p8 /test-8/62worldanyoneBNý“czAÅý“pA /test-7/63worldanyoneBNý“czHÆý“pA /test-3/70worldanyoneBNý“czEÇý“pA /test-5/67worldanyoneBNý“baHÈý“pA /test-6/70worldanyoneBNý“c~HÉý“pB /test-2/70worldanyoneBNý“c~LÊý“pB /test-0/74worldanyoneBNý“baSËý“pB /test-1/81worldanyoneBNý“cŠAÌý“pD /test-9/63worldanyoneBNý“c~EÍý“pE /test-4/67worldanyoneBNý“cŠAÎý“pF /test-8/63worldanyoneBNý“czBÏý“pM /test-7/64worldanyoneBNý“czIÐý“pO /test-3/71worldanyoneBNý“c~IÑý“pP /test-2/71worldanyoneBNý“baTÒý“pQ /test-1/82worldanyoneBNý“baIÓý“pQ /test-6/71worldanyoneBNý“czFÔý“pR /test-5/68worldanyoneBNý“c~MÕý“pR /test-0/75worldanyoneBNý“cŠBÖý“pS /test-9/64worldanyoneBNý“c~F×ý“pT /test-4/68worldanyoneBNý“cŠBØý“pT /test-8/64worldanyoneBNý“czCÙý“p\ /test-7/65worldanyoneBNý“baUÚý“pa /test-1/83worldanyoneBNý“baJÛý“pb /test-6/72worldanyoneBNý“c~JÜý“pc /test-2/72worldanyoneBNý“c~NÝý“pc /test-0/76worldanyoneBNý“c~GÞý“pc /test-4/69worldanyoneBNý“czJßý“pd /test-3/72worldanyoneBNý“cŠCàý“pd /test-9/65worldanyoneBNý“czGáý“pe /test-5/69worldanyoneBNý“cŠCâý“pe /test-8/65worldanyoneBNý“czDãý“pf /test-7/66worldanyoneBNý“baVäý“pp /test-1/84worldanyoneBNý“baKåý“pt /test-6/73worldanyoneBNý“cŠDæý“pu /test-9/66worldanyoneBNý“c~Kçý“pu /test-2/73worldanyoneBNý“c~Oèý“pv /test-0/77worldanyoneBNý“czKéý“pv /test-3/73worldanyoneBNý“czEêý“pv /test-7/67worldanyoneBNý“czHëý“pv /test-5/70worldanyoneBNý“c~Hìý“pv /test-4/70worldanyoneBNý“cŠDíý“pw /test-8/66worldanyoneBNý“baWîý“p{ /test-1/85worldanyoneBNý“baLïý“pƒ /test-6/74worldanyoneBNý“c~Iðý“p… /test-4/71worldanyoneBNý“c~Pñý“p† /test-0/78worldanyoneBNý“c~Lòý“p† /test-2/74worldanyoneBNý“cŠEóý“p† /test-9/67worldanyoneBNý“czLôý“p‡ /test-3/74worldanyoneBNý“czFõý“pˆ /test-7/68worldanyoneBNý“cŠEöý“pˆ /test-8/67worldanyoneBNý“baX÷ý“pˆ /test-1/86worldanyoneBNý“czIøý“p‰ /test-5/71worldanyoneBNý“baMùý“p /test-6/75worldanyoneBNý“cŠFúý“p” /test-9/68worldanyoneBNý“c~Qûý“p• /test-0/79worldanyoneBNý“c~Müý“p• /test-2/75worldanyoneBNý“c~Jýý“p• /test-4/72worldanyoneBNý“baYþý“p– /test-1/87worldanyoneBNý“czMÿý“p– /test-3/75worldanyoneBNý“cŠFý“p— /test-8/68worldanyoneBNý“czJý“p˜ /test-5/72worldanyoneBNý“czGý“p˜ /test-7/69worldanyoneBNý“baNý“p™ /test-6/76worldanyoneBNý“cŠGý“p¢ /test-9/69worldanyoneBNý“c~Rý“p¤ /test-0/80worldanyoneBNý“baZý“p¦ /test-1/88worldanyoneBNý“czNý“p¦ /test-3/76worldanyoneBNý“baOý“p§ /test-6/77worldanyoneBNý“c~K ý“p© /test-4/73worldanyoneBNý“c~N ý“p© /test-2/76worldanyoneBNý“czK ý“p© /test-5/73worldanyoneBNý“czH ý“p© /test-7/70worldanyoneBNý“cŠG ý“p« /test-8/69worldanyoneBNý“cŠHý“q /test-9/70worldanyoneBNý“baPý“q /test-6/78worldanyoneBNý“ba[ý“q  /test-1/89worldanyoneBNý“c~Oý“q" /test-2/77worldanyoneBNý“cŠHý“q$ /test-8/70worldanyoneBNý“czOý“q$ /test-3/77worldanyoneBNý“czIý“q$ /test-7/71worldanyoneBNý“czLý“q$ /test-5/74worldanyoneBNý“c~Sý“q% /test-0/81worldanyoneBNý“c~Lý“q% /test-4/74worldanyoneBNý“cŠIý“q& /test-9/71worldanyoneBNý“baQý“q) /test-6/79worldanyoneBNý“ba\ý“q, /test-1/90worldanyoneBNý“c~Pý“q0 /test-2/78worldanyoneBNý“czPý“q3 /test-3/78worldanyoneBNý“czJý“q4 /test-7/72worldanyoneBNý“cŠIý“q5 /test-8/71worldanyoneBNý“c~Tý“q5 /test-0/82worldanyoneBNý“czM ý“q5 /test-5/75worldanyoneBNý“cŠJ!ý“q6 /test-9/72worldanyoneBNý“c~M"ý“q7 /test-4/75worldanyoneBNý“baR#ý“q8 /test-6/80worldanyoneBNý“ba]$ý“q: /test-1/91worldanyoneBNý“c~Q%ý“q; /test-2/79worldanyoneBNý“czQ&ý“q@ /test-3/79worldanyoneBNý“cŠJ'ý“qB /test-8/72worldanyoneBNý“c~U(ý“qC /test-0/83worldanyoneBNý“czK)ý“qD /test-7/73worldanyoneBNý“cŠK*ý“qD /test-9/73worldanyoneBNý“c~N+ý“qE /test-4/76worldanyoneBNý“czN,ý“qF /test-5/76worldanyoneBNý“c~R-ý“qI /test-2/80worldanyoneBNý“baS.ý“qI /test-6/81worldanyoneBNý“ba^/ý“qI /test-1/92worldanyoneBNý“czR0ý“qO /test-3/80worldanyoneBNý“cŠK1ý“qP /test-8/73worldanyoneBNý“c~V2ý“qQ /test-0/84worldanyoneBNý“czL3ý“qR /test-7/74worldanyoneBNý“cŠL4ý“qS /test-9/74worldanyoneBNý“czO5ý“qS /test-5/77worldanyoneBNý“c~O6ý“qS /test-4/77worldanyoneBNý“baT7ý“qU /test-6/82worldanyoneBNý“c~S8ý“qU /test-2/81worldanyoneBNý“ba_9ý“qY /test-1/93worldanyoneBNý“czS:ý“q] /test-3/81worldanyoneBNý“cŠL;ý“q^ /test-8/74worldanyoneBNý“cŠM<ý“q_ /test-9/75worldanyoneBNý“c~W=ý“q_ /test-0/85worldanyoneBNý“c~P>ý“q` /test-4/78worldanyoneBNý“czP?ý“q` /test-5/78worldanyoneBNý“czM@ý“qa /test-7/75worldanyoneBNý“baUAý“qb /test-6/83worldanyoneBNý“c~TBý“qb /test-2/82worldanyoneBNý“ba`Cý“qd /test-1/94worldanyoneBNý“c~XDý“qo /test-0/86worldanyoneBNý“czTEý“qo /test-3/82worldanyoneBNý“cŠMFý“qp /test-8/75worldanyoneBNý“c~QGý“qq /test-4/79worldanyoneBNý“baaHý“qr /test-1/95worldanyoneBNý“c~UIý“qr /test-2/83worldanyoneBNý“cŠNJý“qr /test-9/76worldanyoneBNý“czNKý“qr /test-7/76worldanyoneBNý“czQLý“qr /test-5/79worldanyoneBNý“baVMý“qr /test-6/84worldanyoneBNý“c~YNý“qz /test-0/87worldanyoneBNý“babOý“q~ /test-1/96worldanyoneBNý“cŠNPý“q~ /test-8/76worldanyoneBNý“czUQý“q~ /test-3/83worldanyoneBNý“cŠORý“q€ /test-9/77worldanyoneBNý“baWSý“q€ /test-6/85worldanyoneBNý“czOTý“q€ /test-7/77worldanyoneBNý“c~RUý“q /test-4/80worldanyoneBNý“c~VVý“q‚ /test-2/84worldanyoneBNý“czRWý“q‚ /test-5/80worldanyoneBNý“c~ZXý“qƒ /test-0/88worldanyoneBNý“baXYý“qŽ /test-6/86worldanyoneBNý“bacZý“qŽ /test-1/97worldanyoneBNý“cŠO[ý“q /test-8/77worldanyoneBNý“cŠP\ý“q /test-9/78worldanyoneBNý“c~S]ý“q /test-4/81worldanyoneBNý“c~[^ý“q‘ /test-0/89worldanyoneBNý“c~W_ý“q‘ /test-2/85worldanyoneBNý“czS`ý“q‘ /test-5/81worldanyoneBNý“czPaý“q‘ /test-7/78worldanyoneBNý“czVbý“q‘ /test-3/84worldanyoneBNý“badcý“q™ /test-1/98worldanyoneBNý“baYdý“q™ /test-6/87worldanyoneBNý“cŠPeý“q /test-8/78worldanyoneBNý“c~\fý“q /test-0/90worldanyoneBNý“c~Tgý“qž /test-4/82worldanyoneBNý“c~Xhý“qŸ /test-2/86worldanyoneBNý“czTiý“qŸ /test-5/82worldanyoneBNý“czWjý“qŸ /test-3/85worldanyoneBNý“czQký“qŸ /test-7/79worldanyoneBNý“cŠQlý“q  /test-9/79worldanyoneBNý“baZmý“q¢ /test-6/88worldanyoneBNý“baený“q¢ /test-1/99worldanyoneBNý“cŠQoý“q§ /test-8/79worldanyoneBNý“c~]pý“qª /test-0/91worldanyoneBNý“czRqý“q¬ /test-7/80worldanyoneBNý“cŠRrý“q­ /test-9/80worldanyoneBNý“ba[sý“q® /test-6/89worldanyoneBOý“baftý“q® /test-1/100worldanyoneBNý“czXuý“q® /test-3/86worldanyoneBNý“czUvý“q¯ /test-5/83worldanyoneBNý“c~Uwý“q¯ /test-4/83worldanyoneBNý“c~Yxý“q¯ /test-2/87worldanyoneBNý“cŠRyý“q° /test-8/80worldanyoneBNý“c~^zý“q´ /test-0/92worldanyoneBNý“czS{ý“q¶ /test-7/81worldanyoneBNý“czY|ý“qº /test-3/87worldanyoneBNý“czV}ý“q» /test-5/84worldanyoneBOý“bag~ý“q» /test-1/101worldanyoneBNý“ba\ý“q¼ /test-6/90worldanyoneBNý“c~Z€ý“q¼ /test-2/88worldanyoneBNý“c~Vý“q½ /test-4/84worldanyoneBNý“cŠS‚ý“q½ /test-8/81worldanyoneBNý“c~_ƒý“qÀ /test-0/93worldanyoneBNý“cŠS„ý“qÁ /test-9/81worldanyoneBNý“czT…ý“qà /test-7/82worldanyoneBNý“czZ†ý“qÇ /test-3/88worldanyoneBNý“czW‡ý“qÉ /test-5/85worldanyoneBNý“cŠTˆý“qÊ /test-8/82worldanyoneBNý“c~W‰ý“qË /test-4/85worldanyoneBNý“c~[Šý“qË /test-2/89worldanyoneBNý“ba]‹ý“qÌ /test-6/91worldanyoneBOý“bahŒý“qÌ /test-1/102worldanyoneBNý“c~`ý“qÍ /test-0/94worldanyoneBNý“cŠTŽý“qÏ /test-9/82worldanyoneBNý“czUý“qÐ /test-7/83worldanyoneBNý“cz[ý“qÒ /test-3/89worldanyoneBNý“czX‘ý“qÔ /test-5/86worldanyoneBNý“cŠU’ý“qØ /test-8/83worldanyoneBNý“c~X“ý“qÚ /test-4/86worldanyoneBNý“c~a”ý“qÞ /test-0/95worldanyoneBOý“bai•ý“qß /test-1/103worldanyoneBNý“ba^–ý“qß /test-6/92worldanyoneBNý“c~\—ý“qß /test-2/90worldanyoneBNý“cŠU˜ý“qá /test-9/83worldanyoneBNý“cŠV™ý“qâ /test-8/84worldanyoneBNý“c~Yšý“qä /test-4/87worldanyoneBNý“czV›ý“qä /test-7/84worldanyoneBNý“czYœý“qä /test-5/87worldanyoneBNý“cz\ý“qå /test-3/90worldanyoneBNý“c~bžý“qê /test-0/96worldanyoneBOý“bajŸý“qì /test-1/104worldanyoneBNý“ba_ ý“qì /test-6/93worldanyoneBNý“c~]¡ý“qì /test-2/91worldanyoneBNý“cŠV¢ý“qï /test-9/84worldanyoneBNý“c~Z£ý“qñ /test-4/88worldanyoneBNý“cŠW¤ý“qñ /test-8/85worldanyoneBNý“czW¥ý“qò /test-7/85worldanyoneBNý“czZ¦ý“qô /test-5/88worldanyoneBNý“cz]§ý“qõ /test-3/91worldanyoneBNý“c~c¨ý“qö /test-0/97worldanyoneBNý“ba`©ý“qú /test-6/94worldanyoneBNý“c~^ªý“qû /test-2/92worldanyoneBOý“bak«ý“qû /test-1/105worldanyoneBNý“cŠW¬ý“qþ /test-9/85worldanyoneBNý“czX­ý“qÿ /test-7/86worldanyoneBNý“c~[®ý“r /test-4/89worldanyoneBNý“cŠX¯ý“r /test-8/86worldanyoneBNý“c~d°ý“r /test-0/98worldanyoneBNý“cz[±ý“r /test-5/89worldanyoneBNý“cz^²ý“r /test-3/92worldanyoneBNý“baa³ý“r  /test-6/95worldanyoneBNý“cŠX´ý“r  /test-9/86worldanyoneBOý“balµý“r /test-1/106worldanyoneBNý“c~_¶ý“r /test-2/93worldanyoneBNý“c~e·ý“r /test-0/99worldanyoneBNý“cŠY¸ý“r /test-8/87worldanyoneBNý“czY¹ý“r /test-7/87worldanyoneBNý“c~\ºý“r /test-4/90worldanyoneBNý“cz_»ý“r /test-3/93worldanyoneBNý“cz\¼ý“r /test-5/90worldanyoneBNý“bab½ý“r /test-6/96worldanyoneBNý“cŠY¾ý“r /test-9/87worldanyoneBOý“bam¿ý“r /test-1/107worldanyoneBNý“c~`Àý“r /test-2/94worldanyoneBNý“cŠZÁý“r  /test-8/88worldanyoneBNý“czZÂý“r  /test-7/88worldanyoneBNý“cz]Ãý“r" /test-5/91worldanyoneBNý“cz`Äý“r# /test-3/94worldanyoneBNý“bacÅý“r$ /test-6/97worldanyoneBOý“c~fÆý“r$ /test-0/100worldanyoneBNý“c~]Çý“r$ /test-4/91worldanyoneBNý“c~aÈý“r- /test-2/95worldanyoneBNý“cŠZÉý“r- /test-9/88worldanyoneBOý“banÊý“r. /test-1/108worldanyoneBNý“cŠ[Ëý“r. /test-8/89worldanyoneBNý“cz[Ìý“r/ /test-7/89worldanyoneBNý“cz^Íý“r0 /test-5/92worldanyoneBOý“c~gÎý“r1 /test-0/101worldanyoneBNý“badÏý“r3 /test-6/98worldanyoneBNý“czaÐý“r4 /test-3/95worldanyoneBNý“c~^Ñý“r4 /test-4/92worldanyoneBNý“cŠ\Òý“r= /test-8/90worldanyoneBNý“cŠ[Óý“r= /test-9/89worldanyoneBNý“c~bÔý“r> /test-2/96worldanyoneBOý“c~hÕý“rA /test-0/102worldanyoneBNý“c~_Öý“rB /test-4/93worldanyoneBNý“cz\×ý“rC /test-7/90worldanyoneBNý“czbØý“rE /test-3/96worldanyoneBOý“baoÙý“rG /test-1/109worldanyoneBNý“baeÚý“rG /test-6/99worldanyoneBNý“cz_Ûý“rH /test-5/93worldanyoneBNý“cŠ]Üý“rJ /test-8/91worldanyoneBNý“cŠ\Ýý“rN /test-9/90worldanyoneBNý“c~cÞý“rS /test-2/97worldanyoneBOý“bafßý“rV /test-6/100worldanyoneBOý“bapàý“rW /test-1/110worldanyoneBNý“cz]áý“rX /test-7/91worldanyoneBOý“c~iâý“rX /test-0/103worldanyoneBNý“c~`ãý“rY /test-4/94worldanyoneBNý“czcäý“rY /test-3/97worldanyoneBNý“cz`åý“rZ /test-5/94worldanyoneBNý“cŠ^æý“r[ /test-8/92worldanyoneBNý“cŠ]çý“r_ /test-9/91worldanyoneBNý“c~dèý“rf /test-2/98worldanyoneBOý“baqéý“rl /test-1/111worldanyoneBNý“cŠ_êý“rn /test-8/93worldanyoneBOý“bagëý“rn /test-6/101worldanyoneBNý“cz^ìý“rn /test-7/92worldanyoneBNý“czaíý“rn /test-5/95worldanyoneBNý“czdîý“rn /test-3/98worldanyoneBNý“cŠ^ïý“ro /test-9/92worldanyoneBOý“c~jðý“rq /test-0/104worldanyoneBNý“c~añý“rr /test-4/95worldanyoneBNý“c~eòý“rt /test-2/99worldanyoneBNý“cŠ`óý“r~ /test-8/94worldanyoneBOý“bahôý“r /test-6/102worldanyoneBNý“cz_õý“r /test-7/93worldanyoneBNý“czböý“r /test-5/96worldanyoneBOý“bar÷ý“r‚ /test-1/112worldanyoneBNý“czeøý“rƒ /test-3/99worldanyoneBNý“cŠ_ùý“r„ /test-9/93worldanyoneBOý“c~kúý“r… /test-0/105worldanyoneBNý“c~bûý“r /test-4/96worldanyoneBOý“c~füý“r /test-2/100worldanyoneBOý“basýý“r’ /test-1/113worldanyoneBOý“baiþý“r“ /test-6/103worldanyoneBOý“c~lÿý“r“ /test-0/106worldanyoneBNý“cŠaý“r˜ /test-8/95worldanyoneBNý“cz`ý“r˜ /test-7/94worldanyoneBNý“czcý“r™ /test-5/97worldanyoneBOý“czfý“r™ /test-3/100worldanyoneBNý“cŠ`ý“r™ /test-9/94worldanyoneBNý“c~cý“r  /test-4/97worldanyoneBOý“c~gý“r  /test-2/101worldanyoneBOý“batý“r¤ /test-1/114worldanyoneBOý“bajý“r¥ /test-6/104worldanyoneBNý“czd ý“r¬ /test-5/98worldanyoneBNý“cza ý“r­ /test-7/95worldanyoneBOý“czg ý“r­ /test-3/101worldanyoneBOý“c~m ý“r® /test-0/107worldanyoneBNý“cŠb ý“r± /test-8/96worldanyoneBNý“cŠaý“r± /test-9/95worldanyoneBNý“c~dý“r³ /test-4/98worldanyoneBOý“c~hý“r´ /test-2/102worldanyoneBOý“bauý“r´ /test-1/115worldanyoneBOý“baký“r¸ /test-6/105worldanyoneBOý“c~ný“r¿ /test-0/108worldanyoneBNý“czeý“rÀ /test-5/99worldanyoneBOý“czhý“rÄ /test-3/102worldanyoneBNý“czbý“rÄ /test-7/96worldanyoneBNý“cŠbý“rÅ /test-9/96worldanyoneBNý“cŠcý“rÅ /test-8/97worldanyoneBNý“c~eý“rÈ /test-4/99worldanyoneBOý“c~iý“rÈ /test-2/103worldanyoneBOý“balý“rÌ /test-6/106worldanyoneBOý“bavý“rÍ /test-1/116worldanyoneBOý“c~oý“rÒ /test-0/109worldanyoneBOý“czfý“rÓ /test-5/100worldanyoneBOý“c~fý“rÝ /test-4/100worldanyoneBOý“c~j ý“rß /test-2/104worldanyoneBOý“czi!ý“rß /test-3/103worldanyoneBNý“czc"ý“rà /test-7/97worldanyoneBOý“bam#ý“rà /test-6/107worldanyoneBOý“baw$ý“rã /test-1/117worldanyoneBNý“cŠc%ý“rã /test-9/97worldanyoneBNý“cŠd&ý“rå /test-8/98worldanyoneBOý“czg'ý“ræ /test-5/101worldanyoneBOý“c~p(ý“ræ /test-0/110worldanyoneBOý“bax)ý“rò /test-1/118worldanyoneBOý“ban*ý“rò /test-6/108worldanyoneBOý“c~g+ý“rò /test-4/101worldanyoneBOý“c~k,ý“rò /test-2/105worldanyoneBNý“czd-ý“rô /test-7/98worldanyoneBOý“czj.ý“rô /test-3/104worldanyoneBOý“c~q/ý“rø /test-0/111worldanyoneBNý“cŠe0ý“rú /test-8/99worldanyoneBNý“cŠd1ý“rú /test-9/98worldanyoneBOý“czh2ý“rÿ /test-5/102worldanyoneBOý“c~h3ý“s /test-4/102worldanyoneBOý“c~l4ý“s /test-2/106worldanyoneBOý“bao5ý“s /test-6/109worldanyoneBOý“bay6ý“s /test-1/119worldanyoneBOý“c~r7ý“s  /test-0/112worldanyoneBOý“cŠf8ý“s  /test-8/100worldanyoneBNý“cŠe9ý“s  /test-9/99worldanyoneBOý“czi:ý“s /test-5/103worldanyoneBOý“czk;ý“s /test-3/105worldanyoneBNý“cze<ý“s /test-7/99worldanyoneBOý“c~m=ý“s: /test-2/107worldanyoneBOý“c~i>ý“s: /test-4/103worldanyoneBOý“bap?ý“s< /test-6/110worldanyoneBOý“baz@ý“s< /test-1/120worldanyoneBOý“c~sAý“s= /test-0/113worldanyoneBOý“cŠgBý“s@ /test-8/101worldanyoneBOý“czjCý“s@ /test-5/104worldanyoneBOý“cŠfDý“s@ /test-9/100worldanyoneBOý“czlEý“s@ /test-3/106worldanyoneBOý“czfFý“s@ /test-7/100worldanyoneBOý“c~nGý“sL /test-2/108worldanyoneBOý“baqHý“sO /test-6/111worldanyoneBOý“czkIý“sP /test-5/105worldanyoneBOý“czmJý“sP /test-3/107worldanyoneBOý“czgKý“sR /test-7/101worldanyoneBOý“ba{Lý“sS /test-1/121worldanyoneBOý“c~jMý“sS /test-4/104worldanyoneBOý“c~tNý“sS /test-0/114worldanyoneBOý“cŠhOý“sS /test-8/102worldanyoneBOý“cŠgPý“sT /test-9/101worldanyoneBOý“barQý“sa /test-6/112worldanyoneBOý“c~oRý“sc /test-2/109worldanyoneBOý“czlSý“sc /test-5/106worldanyoneBOý“cznTý“sc /test-3/108worldanyoneBOý“ba|Uý“sf /test-1/122worldanyoneBOý“cŠiVý“sg /test-8/103worldanyoneBOý“cŠhWý“sg /test-9/102worldanyoneBOý“c~uXý“sh /test-0/115worldanyoneBOý“c~kYý“sh /test-4/105worldanyoneBOý“czhZý“si /test-7/102worldanyoneBOý“czm[ý“sr /test-5/107worldanyoneBOý“czo\ý“sr /test-3/109worldanyoneBOý“bas]ý“ss /test-6/113worldanyoneBOý“cŠj^ý“su /test-8/104worldanyoneBOý“ba}_ý“su /test-1/123worldanyoneBOý“c~p`ý“su /test-2/110worldanyoneBOý“cŠiaý“sv /test-9/103worldanyoneBOý“czibý“sw /test-7/103worldanyoneBOý“c~lcý“sx /test-4/106worldanyoneBOý“c~vdý“sx /test-0/116worldanyoneBOý“czneý“s… /test-5/108worldanyoneBOý“batfý“s† /test-6/114worldanyoneBOý“c~mgý“s‡ /test-4/107worldanyoneBOý“c~qhý“s‡ /test-2/111worldanyoneBOý“ba~iý“sˆ /test-1/124worldanyoneBOý“c~wjý“s‰ /test-0/117worldanyoneBOý“czpký“sŠ /test-3/110worldanyoneBOý“czjlý“s‹ /test-7/104worldanyoneBOý“cŠjmý“sŒ /test-9/104worldanyoneBOý“cŠkný“sŒ /test-8/105worldanyoneBOý“bauoý“s’ /test-6/115worldanyoneBOý“czopý“s” /test-5/109worldanyoneBOý“baqý“s— /test-1/125worldanyoneBOý“c~rrý“s™ /test-2/112worldanyoneBOý“c~xsý“sš /test-0/118worldanyoneBOý“czqtý“sš /test-3/111worldanyoneBOý“czkuý“sš /test-7/105worldanyoneBOý“cŠlvý“sœ /test-8/106worldanyoneBOý“c~nwý“sœ /test-4/108worldanyoneBOý“cŠkxý“sœ /test-9/105worldanyoneBOý“bavyý“s /test-6/116worldanyoneBOý“czpzý“sž /test-5/110worldanyoneBOý“ba€{ý“s¡ /test-1/126worldanyoneBOý“c~s|ý“s¨ /test-2/113worldanyoneBOý“c~y}ý“s¨ /test-0/119worldanyoneBOý“cŠm~ý“sª /test-8/107worldanyoneBOý“bawý“s« /test-6/117worldanyoneBOý“czr€ý“s¬ /test-3/112worldanyoneBOý“czqý“s¬ /test-5/111worldanyoneBOý“ba‚ý“s­ /test-1/127worldanyoneBOý“czlƒý“s­ /test-7/106worldanyoneBOý“cŠl„ý“s­ /test-9/106worldanyoneBOý“c~o…ý“s® /test-4/109worldanyoneBOý“c~z†ý“s¶ /test-0/120worldanyoneBOý“c~t‡ý“s¶ /test-2/114worldanyoneBOý“cŠnˆý“s· /test-8/108worldanyoneBOý“bax‰ý“s» /test-6/118worldanyoneBOý“ba‚Šý“s» /test-1/128worldanyoneBOý“czr‹ý“s¾ /test-5/112worldanyoneBOý“czsŒý“s¿ /test-3/113worldanyoneBOý“czmý“s¿ /test-7/107worldanyoneBOý“cŠmŽý“s¿ /test-9/107worldanyoneBOý“c~pý“sÁ /test-4/110worldanyoneBOý“baƒý“sÆ /test-1/129worldanyoneBOý“cŠo‘ý“sÇ /test-8/109worldanyoneBOý“c~u’ý“sÇ /test-2/115worldanyoneBOý“c~{“ý“sÇ /test-0/121worldanyoneBOý“bay”ý“sÊ /test-6/119worldanyoneBOý“czs•ý“sÍ /test-5/113worldanyoneBOý“czt–ý“sÎ /test-3/114worldanyoneBOý“cŠn—ý“sÏ /test-9/108worldanyoneBOý“c~q˜ý“sÐ /test-4/111worldanyoneBOý“czn™ý“sÐ /test-7/108worldanyoneBOý“ba„šý“sÒ /test-1/130worldanyoneBOý“cŠp›ý“sÒ /test-8/110worldanyoneBOý“c~|œý“sÓ /test-0/122worldanyoneBOý“c~vý“sÔ /test-2/116worldanyoneBOý“bazžý“sÕ /test-6/120worldanyoneBOý“cztŸý“sØ /test-5/114worldanyoneBOý“c~r ý“sÜ /test-4/112worldanyoneBOý“cŠo¡ý“sÝ /test-9/109worldanyoneBOý“czu¢ý“sÝ /test-3/115worldanyoneBOý“cŠq£ý“sß /test-8/111worldanyoneBOý“ba…¤ý“sß /test-1/131worldanyoneBOý“czo¥ý“sà /test-7/109worldanyoneBOý“c~w¦ý“sâ /test-2/117worldanyoneBOý“c~}§ý“sâ /test-0/123worldanyoneBOý“ba{¨ý“sâ /test-6/121worldanyoneBOý“czu©ý“sä /test-5/115worldanyoneBOý“ba†ªý“sì /test-1/132worldanyoneBOý“czv«ý“sì /test-3/116worldanyoneBOý“c~s¬ý“sì /test-4/113worldanyoneBOý“cŠp­ý“sí /test-9/110worldanyoneBOý“czp®ý“sí /test-7/110worldanyoneBOý“cŠr¯ý“sî /test-8/112worldanyoneBOý“ba|°ý“sï /test-6/122worldanyoneBOý“czv±ý“sï /test-5/116worldanyoneBOý“c~~²ý“sï /test-0/124worldanyoneBOý“c~x³ý“sð /test-2/118worldanyoneBOý“ba‡´ý“sø /test-1/133worldanyoneBOý“c~tµý“sù /test-4/114worldanyoneBOý“cŠq¶ý“sù /test-9/111worldanyoneBOý“czw·ý“sú /test-3/117worldanyoneBOý“czq¸ý“sú /test-7/111worldanyoneBOý“ba}¹ý“sû /test-6/123worldanyoneBOý“czwºý“sü /test-5/117worldanyoneBOý“c~y»ý“sü /test-2/119worldanyoneBOý“c~¼ý“sý /test-0/125worldanyoneBOý“cŠs½ý“sý /test-8/113worldanyoneBOý“baˆ¾ý“t /test-1/134worldanyoneBOý“ba~¿ý“t  /test-6/124worldanyoneBOý“c~uÀý“t  /test-4/115worldanyoneBOý“czxÁý“t  /test-3/118worldanyoneBOý“cŠrÂý“t  /test-9/112worldanyoneBOý“czxÃý“t  /test-5/118worldanyoneBOý“c~zÄý“t  /test-2/120worldanyoneBOý“c~€Åý“t  /test-0/126worldanyoneBOý“czrÆý“t  /test-7/112worldanyoneBOý“cŠtÇý“t /test-8/114worldanyoneBOý“ba‰Èý“t /test-1/135worldanyoneBOý“baÉý“t /test-6/125worldanyoneBOý“c~vÊý“t /test-4/116worldanyoneBOý“czsËý“t /test-7/113worldanyoneBOý“czyÌý“t /test-3/119worldanyoneBOý“baŠÍý“t /test-1/136worldanyoneBOý“cŠuÎý“t /test-8/115worldanyoneBOý“cŠsÏý“t /test-9/113worldanyoneBOý“czyÐý“t /test-5/119worldanyoneBOý“c~Ñý“t /test-0/127worldanyoneBOý“c~{Òý“t /test-2/121worldanyoneBOý“ba€Óý“t /test-6/126worldanyoneBOý“c~wÔý“t! /test-4/117worldanyoneBOý“cztÕý“t% /test-7/114worldanyoneBOý“baÖý“t' /test-6/127worldanyoneBOý“ba‹×ý“t' /test-1/137worldanyoneBOý“cŠvØý“t( /test-8/116worldanyoneBOý“cŠtÙý“t) /test-9/114worldanyoneBOý“czzÚý“t) /test-3/120worldanyoneBOý“czzÛý“t* /test-5/120worldanyoneBOý“c~‚Üý“t, /test-0/128worldanyoneBOý“c~|Ýý“t, /test-2/122worldanyoneBOý“c~xÞý“t, /test-4/118worldanyoneBOý“czußý“t1 /test-7/115worldanyoneBOý“cŠwàý“t4 /test-8/117worldanyoneBOý“ba‚áý“t5 /test-6/128worldanyoneBOý“cŠuâý“t5 /test-9/115worldanyoneBOý“baŒãý“t6 /test-1/138worldanyoneBOý“cz{äý“t7 /test-3/121worldanyoneBOý“cz{åý“t: /test-5/121worldanyoneBOý“c~ƒæý“t: /test-0/129worldanyoneBOý“c~}çý“t: /test-2/123worldanyoneBOý“c~yèý“t; /test-4/119worldanyoneBOý“czvéý“t; /test-7/116worldanyoneBOý“baƒêý“tA /test-6/129worldanyoneBOý“cŠxëý“tA /test-8/118worldanyoneBOý“baìý“tA /test-1/139worldanyoneBOý“cŠvíý“tB /test-9/116worldanyoneBOý“cz|îý“tF /test-3/122worldanyoneBOý“czwïý“tG /test-7/117worldanyoneBOý“cz|ðý“tG /test-5/122worldanyoneBOý“c~~ñý“tG /test-2/124worldanyoneBOý“c~„òý“tH /test-0/130worldanyoneBOý“c~zóý“tH /test-4/120worldanyoneBOý“cŠyôý“tM /test-8/119worldanyoneBOý“cŠwõý“tN /test-9/117worldanyoneBOý“ba„öý“tN /test-6/130worldanyoneBOý“baŽ÷ý“tN /test-1/140worldanyoneBOý“cz}øý“tR /test-3/123worldanyoneBOý“cz}ùý“tW /test-5/123worldanyoneBOý“czxúý“tX /test-7/118worldanyoneBOý“c~…ûý“tY /test-0/131worldanyoneBOý“c~üý“tY /test-2/125worldanyoneBOý“c~{ýý“tY /test-4/121worldanyoneBOý“ba…þý“t[ /test-6/131worldanyoneBOý“cŠzÿý“t\ /test-8/120worldanyoneBOý“baý“t^ /test-1/141worldanyoneBOý“cŠxý“t_ /test-9/118worldanyoneBOý“cz~ý“tg /test-3/124worldanyoneBOý“cz~ý“ti /test-5/124worldanyoneBOý“czyý“ti /test-7/119worldanyoneBOý“c~|ý“tj /test-4/122worldanyoneBOý“c~€ý“tj /test-2/126worldanyoneBOý“c~†ý“tj /test-0/132worldanyoneBOý“cŠ{ý“tl /test-8/121worldanyoneBOý“ba† ý“tl /test-6/132worldanyoneBOý“ba ý“tm /test-1/142worldanyoneBOý“cŠy ý“ts /test-9/119worldanyoneBOý“c~ ý“tx /test-2/127worldanyoneBOý“cz ý“ty /test-5/125worldanyoneBOý“czý“ty /test-3/125worldanyoneBOý“c~}ý“tz /test-4/123worldanyoneBOý“czzý“tz /test-7/120worldanyoneBOý“c~‡ý“t{ /test-0/133worldanyoneBOý“ba‡ý“t| /test-6/133worldanyoneBOý“cŠ|ý“t| /test-8/122worldanyoneBOý“ba‘ý“t| /test-1/143worldanyoneBOý“cŠzý“t~ /test-9/120worldanyoneBOý“cŠ}ý“tŒ /test-8/123worldanyoneBOý“cŠ{ý“tŒ /test-9/121worldanyoneBOý“ba’ý“t /test-1/144worldanyoneBOý“baˆý“t /test-6/134worldanyoneBOý“cz€ý“t /test-5/126worldanyoneBOý“cz€ý“t /test-3/126worldanyoneBOý“cz{ý“t /test-7/121worldanyoneBOý“c~ˆý“t‘ /test-0/134worldanyoneBOý“c~~ý“t‘ /test-4/124worldanyoneBOý“c~‚ý“t’ /test-2/128worldanyoneBOý“cŠ~ ý“tœ /test-8/124worldanyoneBOý“ba‰!ý“tŸ /test-6/135worldanyoneBOý“ba“"ý“t  /test-1/145worldanyoneBOý“cŠ|#ý“t  /test-9/122worldanyoneBOý“cz$ý“t¢ /test-3/127worldanyoneBOý“cz%ý“t¢ /test-5/127worldanyoneBOý“cz|&ý“t¢ /test-7/122worldanyoneBOý“c~'ý“t£ /test-4/125worldanyoneBOý“c~ƒ(ý“t£ /test-2/129worldanyoneBOý“c~‰)ý“t£ /test-0/135worldanyoneBOý“cŠ*ý“t¦ /test-8/125worldanyoneBOý“baŠ+ý“t« /test-6/136worldanyoneBOý“ba”,ý“t¬ /test-1/146worldanyoneBOý“cŠ}-ý“t¬ /test-9/123worldanyoneBOý“c~€.ý“t® /test-4/126worldanyoneBOý“c~„/ý“t¯ /test-2/130worldanyoneBOý“cz}0ý“t¯ /test-7/123worldanyoneBOý“cz‚1ý“t¯ /test-5/128worldanyoneBOý“cz‚2ý“t¯ /test-3/128worldanyoneBOý“cŠ€3ý“t° /test-8/126worldanyoneBOý“c~Š4ý“t± /test-0/136worldanyoneBOý“ba‹5ý“t¶ /test-6/137worldanyoneBOý“ba•6ý“t· /test-1/147worldanyoneBOý“cŠ~7ý“t· /test-9/124worldanyoneBOý“czƒ8ý“t¼ /test-3/129worldanyoneBOý“c~…9ý“t¼ /test-2/131worldanyoneBOý“c~‹:ý“t¼ /test-0/137worldanyoneBOý“c~;ý“t¼ /test-4/127worldanyoneBOý“czƒ<ý“t½ /test-5/129worldanyoneBOý“cŠ=ý“t½ /test-8/127worldanyoneBOý“cz~>ý“t½ /test-7/124worldanyoneBOý“baŒ?ý“tÀ /test-6/138worldanyoneBOý“ba–@ý“tÁ /test-1/148worldanyoneBOý“cŠAý“tà /test-9/125worldanyoneBOý“cz„Bý“tÅ /test-3/130worldanyoneBOý“c~†Cý“tÅ /test-2/132worldanyoneBOý“c~‚Dý“tÇ /test-4/128worldanyoneBOý“c~ŒEý“tÉ /test-0/138worldanyoneBOý“cŠ‚Fý“tÉ /test-8/128worldanyoneBOý“czGý“tÉ /test-7/125worldanyoneBOý“cz„Hý“tÊ /test-5/130worldanyoneBOý“ba—Iý“tË /test-1/149worldanyoneBOý“baJý“tË /test-6/139worldanyoneBOý“cŠ€Ký“tÍ /test-9/126worldanyoneBOý“c~‡Lý“tÏ /test-2/133worldanyoneBOý“cz…Mý“tÐ /test-3/131worldanyoneBOý“c~ƒNý“tÑ /test-4/129worldanyoneBOý“cŠƒOý“tÕ /test-8/129worldanyoneBOý“cz€Pý“tÖ /test-7/126worldanyoneBOý“c~Qý“tÖ /test-0/139worldanyoneBOý“ba˜Rý“tØ /test-1/150worldanyoneBOý“cz…Sý“tØ /test-5/131worldanyoneBOý“baŽTý“tØ /test-6/140worldanyoneBOý“cŠUý“tÙ /test-9/127worldanyoneBOý“cz†Vý“tÞ /test-3/132worldanyoneBOý“c~ˆWý“tß /test-2/134worldanyoneBOý“c~„Xý“tß /test-4/130worldanyoneBOý“c~ŽYý“tä /test-0/140worldanyoneBOý“ba™Zý“tå /test-1/151worldanyoneBOý“ba[ý“tå /test-6/141worldanyoneBOý“cz†\ý“tæ /test-5/132worldanyoneBOý“cz]ý“tæ /test-7/127worldanyoneBOý“cŠ„^ý“tæ /test-8/130worldanyoneBOý“cŠ‚_ý“tæ /test-9/128worldanyoneBOý“cz‡`ý“tê /test-3/133worldanyoneBOý“c~‰aý“tí /test-2/135worldanyoneBOý“c~…bý“tî /test-4/131worldanyoneBOý“bašcý“tï /test-1/152worldanyoneBOý“c~dý“tï /test-0/141worldanyoneBOý“baeý“tð /test-6/142worldanyoneBOý“cz‡fý“tñ /test-5/133worldanyoneBOý“cz‚gý“tò /test-7/128worldanyoneBOý“cŠƒhý“tô /test-9/129worldanyoneBOý“czˆiý“tõ /test-3/134worldanyoneBOý“cŠ…jý“tõ /test-8/131worldanyoneBOý“c~Šký“tú /test-2/136worldanyoneBOý“c~†lý“tû /test-4/132worldanyoneBOý“ba‘mý“tü /test-6/143worldanyoneBOý“ba›ný“tþ /test-1/153worldanyoneBOý“czƒoý“tþ /test-7/129worldanyoneBOý“czˆpý“u /test-5/134worldanyoneBOý“c~qý“u /test-0/142worldanyoneBOý“cІrý“u /test-8/132worldanyoneBOý“cŠ„sý“u /test-9/130worldanyoneBOý“cz‰tý“u /test-3/135worldanyoneBOý“c~‹uý“u /test-2/137worldanyoneBOý“c~‡vý“u /test-4/133worldanyoneBOý“cz„wý“u /test-7/130worldanyoneBOý“baœxý“u /test-1/154worldanyoneBOý“c~‘yý“u /test-0/143worldanyoneBOý“cz‰zý“u /test-5/135worldanyoneBOý“ba’{ý“u /test-6/144worldanyoneBOý“cЇ|ý“u! /test-8/133worldanyoneBOý“cŠ…}ý“u" /test-9/131worldanyoneBOý“czŠ~ý“u" /test-3/136worldanyoneBOý“c~Œý“u$ /test-2/138worldanyoneBOý“c~ˆ€ý“u$ /test-4/134worldanyoneBOý“cz…ý“u' /test-7/131worldanyoneBOý“czŠ‚ý“u' /test-5/136worldanyoneBOý“baƒý“u' /test-1/155worldanyoneBOý“ba“„ý“u( /test-6/145worldanyoneBOý“c~’…ý“u( /test-0/144worldanyoneBOý“cŠˆ†ý“u* /test-8/134worldanyoneBOý“cІ‡ý“u. /test-9/132worldanyoneBOý“cz‹ˆý“u/ /test-3/137worldanyoneBOý“c~‰ý“u1 /test-2/139worldanyoneBOý“c~‰Šý“u1 /test-4/135worldanyoneBOý“cz‹‹ý“u3 /test-5/137worldanyoneBOý“ba”Œý“u3 /test-6/146worldanyoneBOý“bažý“u3 /test-1/156worldanyoneBOý“c~“Žý“u3 /test-0/145worldanyoneBOý“cz†ý“u4 /test-7/132worldanyoneBOý“cЉý“u5 /test-8/135worldanyoneBOý“cЇ‘ý“u: /test-9/133worldanyoneBOý“czŒ’ý“u; /test-3/138worldanyoneBOý“ba•“ý“u? /test-6/147worldanyoneBOý“baŸ”ý“u@ /test-1/157worldanyoneBOý“cŠŠ•ý“uA /test-8/136worldanyoneBOý“c~”–ý“uF /test-0/146worldanyoneBOý“c~Š—ý“uF /test-4/136worldanyoneBOý“c~Ž˜ý“uF /test-2/140worldanyoneBOý“cz‡™ý“uF /test-7/133worldanyoneBOý“czŒšý“uF /test-5/138worldanyoneBOý“cŠˆ›ý“uF /test-9/134worldanyoneBOý“czœý“uF /test-3/139worldanyoneBOý“ba–ý“uH /test-6/148worldanyoneBOý“ba žý“uJ /test-1/158worldanyoneBOý“c~•Ÿý“uS /test-0/147worldanyoneBOý“c~ ý“uT /test-2/141worldanyoneBOý“c~‹¡ý“uU /test-4/137worldanyoneBOý“czŽ¢ý“uX /test-3/140worldanyoneBOý“ba—£ý“uX /test-6/149worldanyoneBOý“ba¡¤ý“uY /test-1/159worldanyoneBOý“cz¥ý“uY /test-5/139worldanyoneBOý“cЉ¦ý“uY /test-9/135worldanyoneBOý“cŠ‹§ý“uY /test-8/137worldanyoneBOý“czˆ¨ý“uY /test-7/134worldanyoneBOý“c~–©ý“ua /test-0/148worldanyoneBOý“ba¢ªý“uf /test-1/160worldanyoneBOý“ba˜«ý“ug /test-6/150worldanyoneBOý“c~¬ý“ui /test-2/142worldanyoneBOý“cz‰­ý“ui /test-7/135worldanyoneBOý“cŠŒ®ý“uk /test-8/138worldanyoneBOý“c~Œ¯ý“uk /test-4/138worldanyoneBOý“cz°ý“ul /test-3/141worldanyoneBOý“czޱý“ul /test-5/140worldanyoneBOý“cŠŠ²ý“um /test-9/136worldanyoneBOý“c~—³ý“um /test-0/149worldanyoneBOý“ba£´ý“up /test-1/161worldanyoneBOý“ba™µý“uq /test-6/151worldanyoneBOý“czжý“uv /test-7/136worldanyoneBOý“c~‘·ý“uw /test-2/143worldanyoneBOý“cz¸ý“u{ /test-3/142worldanyoneBOý“cz¹ý“u{ /test-5/141worldanyoneBOý“cŠºý“u{ /test-8/139worldanyoneBOý“c~»ý“u{ /test-4/139worldanyoneBOý“c~˜¼ý“u| /test-0/150worldanyoneBOý“cŠ‹½ý“u} /test-9/137worldanyoneBOý“ba¤¾ý“u~ /test-1/162worldanyoneBOý“baš¿ý“u~ /test-6/152worldanyoneBOý“cz‹Àý“u /test-7/137worldanyoneBOý“c~’Áý“u /test-2/144worldanyoneBOý“cz‘Âý“uŠ /test-3/143worldanyoneBOý“ba›Ãý“uŠ /test-6/153worldanyoneBOý“cŠŽÄý“uŒ /test-8/140worldanyoneBOý“ba¥Åý“uŒ /test-1/163worldanyoneBOý“c~™Æý“u /test-0/151worldanyoneBOý“c~ŽÇý“u /test-4/140worldanyoneBOý“c~“Èý“uŽ /test-2/145worldanyoneBOý“cŠŒÉý“uŽ /test-9/138worldanyoneBOý“czÊý“u /test-5/142worldanyoneBOý“czŒËý“u /test-7/138worldanyoneBOý“baœÌý“u— /test-6/154worldanyoneBOý“cz’Íý“u— /test-3/144worldanyoneBOý“ba¦Îý“uœ /test-1/164worldanyoneBOý“c~šÏý“u /test-0/152worldanyoneBOý“c~”Ðý“u /test-2/146worldanyoneBOý“c~Ñý“u /test-4/141worldanyoneBOý“cŠÒý“už /test-8/141worldanyoneBOý“cŠÓý“už /test-9/139worldanyoneBOý“czÔý“uŸ /test-7/139worldanyoneBOý“cz‘Õý“uŸ /test-5/143worldanyoneBOý“baÖý“u  /test-6/155worldanyoneBOý“cz“×ý“u  /test-3/145worldanyoneBOý“bažØý“u­ /test-6/156worldanyoneBOý“ba§Ùý“u® /test-1/165worldanyoneBOý“cŠÚý“u® /test-8/142worldanyoneBOý“cŠŽÛý“u¯ /test-9/140worldanyoneBOý“c~•Üý“u¯ /test-2/147worldanyoneBOý“cz”Ýý“u¯ /test-3/146worldanyoneBOý“czŽÞý“u± /test-7/140worldanyoneBOý“cz’ßý“u² /test-5/144worldanyoneBOý“c~›àý“u² /test-0/153worldanyoneBOý“c~áý“uµ /test-4/142worldanyoneBOý“cz•âý“u½ /test-3/147worldanyoneBOý“c~–ãý“u½ /test-2/148worldanyoneBOý“ba¨äý“u¾ /test-1/166worldanyoneBOý“baŸåý“u¾ /test-6/157worldanyoneBOý“cŠ‘æý“u¿ /test-8/143worldanyoneBOý“cŠçý“u¿ /test-9/141worldanyoneBOý“c~œèý“uÁ /test-0/154worldanyoneBOý“cz“éý“u /test-5/145worldanyoneBOý“czêý“uÄ /test-7/141worldanyoneBOý“c~‘ëý“uÄ /test-4/143worldanyoneBOý“cz–ìý“uÇ /test-3/148worldanyoneBOý“c~—íý“uÍ /test-2/149worldanyoneBOý“ba îý“uÍ /test-6/158worldanyoneBOý“cŠ’ïý“uÍ /test-8/144worldanyoneBOý“ba©ðý“uÍ /test-1/167worldanyoneBOý“cŠñý“uÏ /test-9/142worldanyoneBOý“cz—òý“uÓ /test-3/149worldanyoneBOý“c~óý“uÓ /test-0/155worldanyoneBOý“czôý“uÕ /test-7/142worldanyoneBOý“cz”õý“uÕ /test-5/146worldanyoneBOý“c~’öý“uÖ /test-4/144worldanyoneBOý“ba¡÷ý“uØ /test-6/159worldanyoneBOý“baªøý“uØ /test-1/168worldanyoneBOý“c~˜ùý“uØ /test-2/150worldanyoneBOý“cŠ“úý“uÙ /test-8/145worldanyoneBOý“cŠ‘ûý“uÚ /test-9/143worldanyoneBOý“cz˜üý“uß /test-3/150worldanyoneBOý“c~žýý“uß /test-0/156worldanyoneBOý“cz‘þý“uâ /test-7/143worldanyoneBOý“cz•ÿý“uâ /test-5/147worldanyoneBOý“c~“ý“uã /test-4/145worldanyoneBOý“c~™ý“uæ /test-2/151worldanyoneBOý“cŠ”ý“uç /test-8/146worldanyoneBOý“cŠ’ý“uç /test-9/144worldanyoneBOý“ba¢ý“uè /test-6/160worldanyoneBOý“ba«ý“uè /test-1/169worldanyoneBOý“c~Ÿý“uí /test-0/157worldanyoneBOý“cz™ý“uñ /test-3/151worldanyoneBOý“ba¬ý“uô /test-1/170worldanyoneBOý“ba£ ý“uö /test-6/161worldanyoneBOý“cz’ ý“u÷ /test-7/144worldanyoneBOý“c~š ý“u÷ /test-2/152worldanyoneBOý“c~” ý“uø /test-4/146worldanyoneBOý“cŠ“ ý“uø /test-9/145worldanyoneBOý“cŠ•ý“uù /test-8/147worldanyoneBOý“c~ ý“uú /test-0/158worldanyoneBOý“cz–ý“uû /test-5/148worldanyoneBOý“czšý“v /test-3/152worldanyoneBOý“c~›ý“v /test-2/153worldanyoneBOý“cz“ý“v /test-7/145worldanyoneBOý“cŠ–ý“v /test-8/148worldanyoneBOý“cŠ”ý“v /test-9/146worldanyoneBOý“ba­ý“v  /test-1/171worldanyoneBOý“ba¤ý“v  /test-6/162worldanyoneBOý“c~•ý“v  /test-4/147worldanyoneBOý“c~¡ý“v  /test-0/159worldanyoneBOý“cz›ý“v  /test-3/153worldanyoneBOý“cz—ý“v  /test-5/149worldanyoneBOý“cz”ý“v /test-7/146worldanyoneBOý“cŠ—ý“v /test-8/149worldanyoneBOý“c~œý“v /test-2/154worldanyoneBOý“cŠ•ý“v /test-9/147worldanyoneBOý“ba® ý“v /test-1/172worldanyoneBOý“ba¥!ý“v /test-6/163worldanyoneBOý“c~–"ý“v /test-4/148worldanyoneBOý“c~¢#ý“v /test-0/160worldanyoneBOý“czœ$ý“v /test-3/154worldanyoneBOý“cz˜%ý“v! /test-5/150worldanyoneBOý“cŠ˜&ý“v! /test-8/150worldanyoneBOý“cz•'ý“v! /test-7/147worldanyoneBOý“cŠ–(ý“v! /test-9/148worldanyoneBOý“c~)ý“v! /test-2/155worldanyoneBOý“ba¦*ý“v( /test-6/164worldanyoneBOý“cz+ý“v( /test-3/155worldanyoneBOý“c~—,ý“v( /test-4/149worldanyoneBOý“ba¯-ý“v) /test-1/173worldanyoneBOý“c~£.ý“v- /test-0/161worldanyoneBOý“c~ž/ý“v. /test-2/156worldanyoneBOý“cŠ—0ý“v/ /test-9/149worldanyoneBOý“cz–1ý“v0 /test-7/148worldanyoneBOý“cz™2ý“v0 /test-5/151worldanyoneBOý“cŠ™3ý“v1 /test-8/151worldanyoneBOý“ba§4ý“v3 /test-6/165worldanyoneBOý“c~˜5ý“v3 /test-4/150worldanyoneBOý“ba°6ý“v4 /test-1/174worldanyoneBOý“czž7ý“v5 /test-3/156worldanyoneBOý“c~¤8ý“v9 /test-0/162worldanyoneBOý“ba¨9ý“v> /test-6/166worldanyoneBOý“ba±:ý“v> /test-1/175worldanyoneBOý“cŠ˜;ý“v? /test-9/150worldanyoneBOý“cŠš<ý“v@ /test-8/152worldanyoneBOý“czš=ý“v@ /test-5/152worldanyoneBOý“c~™>ý“v@ /test-4/151worldanyoneBOý“c~Ÿ?ý“v@ /test-2/157worldanyoneBOý“cz—@ý“vA /test-7/149worldanyoneBOý“czŸAý“vA /test-3/157worldanyoneBOý“c~¥Bý“vB /test-0/163worldanyoneBOý“cŠ™Cý“vL /test-9/151worldanyoneBOý“cŠ›Dý“vM /test-8/153worldanyoneBOý“cz›Eý“vP /test-5/153worldanyoneBOý“cz˜Fý“vP /test-7/150worldanyoneBOý“c~šGý“vP /test-4/152worldanyoneBOý“ba²Hý“vP /test-1/176worldanyoneBOý“c~ Iý“vP /test-2/158worldanyoneBOý“cz Jý“vQ /test-3/158worldanyoneBOý“c~¦Ký“vQ /test-0/164worldanyoneBOý“ba©Lý“vR /test-6/167worldanyoneBOý“cŠšMý“vU /test-9/152worldanyoneBOý“cŠœNý“vW /test-8/154worldanyoneBOý“czœOý“vZ /test-5/154worldanyoneBOý“c~›Pý“v\ /test-4/153worldanyoneBOý“cz™Qý“v\ /test-7/151worldanyoneBOý“ba³Rý“v] /test-1/177worldanyoneBOý“cz¡Sý“v] /test-3/159worldanyoneBOý“baªTý“v^ /test-6/168worldanyoneBOý“c~¡Uý“v^ /test-2/159worldanyoneBOý“cŠ›Vý“v_ /test-9/153worldanyoneBOý“c~§Wý“v_ /test-0/165worldanyoneBOý“cŠXý“v` /test-8/155worldanyoneBOý“czYý“vb /test-5/155worldanyoneBOý“ba´Zý“vj /test-1/178worldanyoneBOý“czš[ý“vj /test-7/152worldanyoneBOý“cz¢\ý“vk /test-3/160worldanyoneBOý“ba«]ý“vk /test-6/169worldanyoneBOý“cŠž^ý“vl /test-8/156worldanyoneBOý“c~œ_ý“vl /test-4/154worldanyoneBOý“c~¢`ý“vm /test-2/160worldanyoneBOý“c~¨aý“vm /test-0/166worldanyoneBOý“cŠœbý“vm /test-9/154worldanyoneBOý“czžcý“vm /test-5/156worldanyoneBOý“baµdý“vu /test-1/179worldanyoneBOý“cz£eý“vu /test-3/161worldanyoneBOý“ba¬fý“vu /test-6/170worldanyoneBOý“cz›gý“vu /test-7/153worldanyoneBOý“cŠŸhý“vy /test-8/157worldanyoneBOý“czŸiý“vy /test-5/157worldanyoneBOý“c~£jý“vz /test-2/161worldanyoneBOý“c~ký“vz /test-4/155worldanyoneBOý“c~©lý“vz /test-0/167worldanyoneBOý“cŠmý“v{ /test-9/155worldanyoneBOý“ba¶ný“v} /test-1/180worldanyoneBOý“ba­oý“v€ /test-6/171worldanyoneBOý“cz¤pý“v€ /test-3/162worldanyoneBOý“czœqý“v /test-7/154worldanyoneBOý“cŠ rý“v„ /test-8/158worldanyoneBOý“cŠžsý“v… /test-9/156worldanyoneBOý“c~žtý“v† /test-4/156worldanyoneBOý“c~¤uý“v† /test-2/162worldanyoneBOý“c~ªvý“v† /test-0/168worldanyoneBOý“cz wý“v‡ /test-5/158worldanyoneBOý“ba·xý“vˆ /test-1/181worldanyoneBOý“ba®yý“vŠ /test-6/172worldanyoneBOý“czzý“v‹ /test-7/155worldanyoneBOý“cz¥{ý“v‹ /test-3/163worldanyoneBOý“cŠ¡|ý“vŽ /test-8/159worldanyoneBOý“cŠŸ}ý“v /test-9/157worldanyoneBOý“ba¸~ý“v’ /test-1/182worldanyoneBOý“c~«ý“v“ /test-0/169worldanyoneBOý“c~¥€ý“v“ /test-2/163worldanyoneBOý“c~Ÿý“v” /test-4/157worldanyoneBOý“cz¡‚ý“v” /test-5/159worldanyoneBOý“ba¯ƒý“v– /test-6/173worldanyoneBOý“cz¦„ý“v— /test-3/164worldanyoneBOý“czž…ý“v— /test-7/156worldanyoneBOý“cŠ¢†ý“v› /test-8/160worldanyoneBOý“cŠ ‡ý“v› /test-9/158worldanyoneBOý“ba°ˆý“vŸ /test-6/174worldanyoneBOý“ba¹‰ý“vŸ /test-1/183worldanyoneBOý“c~¦Šý“v  /test-2/164worldanyoneBOý“cz¢‹ý“v  /test-5/160worldanyoneBOý“c~ Œý“v  /test-4/158worldanyoneBOý“c~¬ý“v¡ /test-0/170worldanyoneBOý“cz§Žý“v£ /test-3/165worldanyoneBOý“czŸý“v£ /test-7/157worldanyoneBOý“cŠ£ý“v¦ /test-8/161worldanyoneBOý“cŠ¡‘ý“v¦ /test-9/159worldanyoneBOý“c~­’ý“v« /test-0/171worldanyoneBOý“baº“ý“v¬ /test-1/184worldanyoneBOý“cz£”ý“v¬ /test-5/161worldanyoneBOý“ba±•ý“v­ /test-6/175worldanyoneBOý“c~§–ý“v­ /test-2/165worldanyoneBOý“c~¡—ý“v­ /test-4/159worldanyoneBOý“cz ˜ý“v¯ /test-7/158worldanyoneBOý“cz¨™ý“v¯ /test-3/166worldanyoneBOý“cФšý“v° /test-8/162worldanyoneBOý“cŠ¢›ý“v± /test-9/160worldanyoneBOý“c~®œý“v¶ /test-0/172worldanyoneBOý“ba»ý“v¹ /test-1/185worldanyoneBOý“cz¤žý“v¹ /test-5/162worldanyoneBOý“ba²Ÿý“vº /test-6/176worldanyoneBOý“cz¡ ý“v½ /test-7/159worldanyoneBOý“c~¨¡ý“v¿ /test-2/166worldanyoneBOý“cŠ¥¢ý“v¿ /test-8/163worldanyoneBOý“cŠ££ý“v¿ /test-9/161worldanyoneBOý“c~¢¤ý“vÀ /test-4/160worldanyoneBOý“cz©¥ý“vÀ /test-3/167worldanyoneBOý“c~¯¦ý“vÁ /test-0/173worldanyoneBOý“cz¥§ý“vÄ /test-5/163worldanyoneBOý“ba¼¨ý“vÅ /test-1/186worldanyoneBOý“ba³©ý“vÅ /test-6/177worldanyoneBOý“cz¢ªý“vÈ /test-7/160worldanyoneBOý“czª«ý“vË /test-3/168worldanyoneBOý“c~©¬ý“vË /test-2/167worldanyoneBOý“c~£­ý“vÌ /test-4/161worldanyoneBOý“cФ®ý“vÍ /test-9/162worldanyoneBOý“c~°¯ý“vÍ /test-0/174worldanyoneBOý“cЦ°ý“vÍ /test-8/164worldanyoneBOý“cz¦±ý“vÏ /test-5/164worldanyoneBOý“ba´²ý“vÏ /test-6/178worldanyoneBOý“ba½³ý“vÏ /test-1/187worldanyoneBOý“cz£´ý“vÑ /test-7/161worldanyoneBOý“cz«µý“vÔ /test-3/169worldanyoneBOý“c~ª¶ý“vÖ /test-2/168worldanyoneBOý“cŠ¥·ý“vÖ /test-9/163worldanyoneBOý“cЧ¸ý“v× /test-8/165worldanyoneBOý“c~¤¹ý“v× /test-4/162worldanyoneBOý“c~±ºý“vØ /test-0/175worldanyoneBOý“baµ»ý“vÙ /test-6/179worldanyoneBOý“ba¾¼ý“vÙ /test-1/188worldanyoneBOý“cz§½ý“vÙ /test-5/165worldanyoneBOý“cz¤¾ý“vÚ /test-7/162worldanyoneBOý“cz¬¿ý“vÜ /test-3/170worldanyoneBOý“c~«Àý“vá /test-2/169worldanyoneBOý“ba¶Áý“vä /test-6/180worldanyoneBOý“ba¿Âý“vä /test-1/189worldanyoneBOý“cЍÃý“vä /test-8/166worldanyoneBOý“cЦÄý“vä /test-9/164worldanyoneBOý“c~¥Åý“vå /test-4/163worldanyoneBOý“c~²Æý“vå /test-0/176worldanyoneBOý“cz¥Çý“vå /test-7/163worldanyoneBOý“cz¨Èý“vå /test-5/166worldanyoneBOý“cz­Éý“væ /test-3/171worldanyoneBOý“c~¬Êý“vé /test-2/170worldanyoneBOý“ba·Ëý“vð /test-6/181worldanyoneBOý“baÀÌý“vð /test-1/190worldanyoneBOý“c~¦Íý“vñ /test-4/164worldanyoneBOý“cz¦Îý“vñ /test-7/164worldanyoneBOý“cz®Ïý“vò /test-3/172worldanyoneBOý“cz©Ðý“vò /test-5/167worldanyoneBOý“cЧÑý“vó /test-9/165worldanyoneBOý“cŠ©Òý“vó /test-8/167worldanyoneBOý“c~³Óý“vô /test-0/177worldanyoneBOý“c~­Ôý“vô /test-2/171worldanyoneBOý“ba¸Õý“vû /test-6/182worldanyoneBOý“baÁÖý“vû /test-1/191worldanyoneBOý“cz§×ý“w /test-7/165worldanyoneBOý“c~®Øý“w /test-2/172worldanyoneBOý“c~§Ùý“w /test-4/165worldanyoneBOý“c~´Úý“w /test-0/178worldanyoneBOý“cŠªÛý“w /test-8/168worldanyoneBOý“cЍÜý“w /test-9/166worldanyoneBOý“cz¯Ýý“w /test-3/173worldanyoneBOý“czªÞý“w /test-5/168worldanyoneBOý“ba¹ßý“w /test-6/183worldanyoneBOý“baÂàý“w /test-1/192worldanyoneBOý“cz¨áý“w  /test-7/166worldanyoneBOý“c~¯âý“w  /test-2/173worldanyoneBOý“cŠ©ãý“w  /test-9/167worldanyoneBOý“c~µäý“w /test-0/179worldanyoneBOý“cŠ«åý“w /test-8/169worldanyoneBOý“c~¨æý“w /test-4/166worldanyoneBOý“baºçý“w /test-6/184worldanyoneBOý“baÃèý“w /test-1/193worldanyoneBOý“cz°éý“w /test-3/174worldanyoneBOý“cz«êý“w /test-5/169worldanyoneBOý“cz©ëý“w /test-7/167worldanyoneBOý“c~°ìý“w /test-2/174worldanyoneBOý“c~¶íý“w /test-0/180worldanyoneBOý“c~©îý“w /test-4/167worldanyoneBOý“baÄïý“w /test-1/194worldanyoneBOý“cЬðý“w /test-8/170worldanyoneBOý“cŠªñý“w /test-9/168worldanyoneBOý“cz±òý“w /test-3/175worldanyoneBOý“cz¬óý“w /test-5/170worldanyoneBOý“ba»ôý“w /test-6/185worldanyoneBOý“czªõý“w /test-7/168worldanyoneBOý“c~±öý“w" /test-2/175worldanyoneBOý“ba¼÷ý“w( /test-6/186worldanyoneBOý“baÅøý“w( /test-1/195worldanyoneBOý“cŠ­ùý“w( /test-8/171worldanyoneBOý“cŠ«úý“w( /test-9/169worldanyoneBOý“c~ªûý“w( /test-4/168worldanyoneBOý“c~·üý“w) /test-0/181worldanyoneBOý“cz­ýý“w) /test-5/171worldanyoneBOý“cz²þý“w* /test-3/176worldanyoneBOý“cz«ÿý“w* /test-7/169worldanyoneBOý“c~²ý“w+ /test-2/176worldanyoneBOý“ba½ý“w1 /test-6/187worldanyoneBOý“baÆý“w3 /test-1/196worldanyoneBOý“c~«ý“w3 /test-4/169worldanyoneBOý“cЬý“w3 /test-9/170worldanyoneBOý“cŠ®ý“w3 /test-8/172worldanyoneBOý“c~¸ý“w8 /test-0/182worldanyoneBOý“c~³ý“w8 /test-2/177worldanyoneBOý“cz®ý“w8 /test-5/172worldanyoneBOý“cz¬ ý“w8 /test-7/170worldanyoneBOý“cz³ ý“w8 /test-3/177worldanyoneBOý“ba¾ ý“w; /test-6/188worldanyoneBOý“baÇ ý“w> /test-1/197worldanyoneBOý“c~¬ ý“w> /test-4/170worldanyoneBOý“cНý“w> /test-8/173worldanyoneBOý“cŠ­ý“w? /test-9/171worldanyoneBOý“c~¹ý“wD /test-0/183worldanyoneBOý“c~´ý“wD /test-2/178worldanyoneBOý“cz´ý“wD /test-3/178worldanyoneBOý“cz¯ý“wD /test-5/173worldanyoneBOý“cz­ý“wD /test-7/171worldanyoneBOý“ba¿ý“wE /test-6/189worldanyoneBOý“baÈý“wH /test-1/198worldanyoneBOý“cаý“wL /test-8/174worldanyoneBOý“cŠ®ý“wL /test-9/172worldanyoneBOý“c~­ý“wN /test-4/171worldanyoneBOý“baÀý“wP /test-6/190worldanyoneBOý“c~ºý“wQ /test-0/184worldanyoneBOý“c~µý“wR /test-2/179worldanyoneBOý“cz°ý“wR /test-5/174worldanyoneBOý“cz®ý“wS /test-7/172worldanyoneBOý“czµý“wS /test-3/179worldanyoneBOý“baÉ ý“wU /test-1/199worldanyoneBOý“baÁ!ý“wa /test-6/191worldanyoneBOý“baÊ"ý“wa /test-1/200worldanyoneBOý“cz¶#ý“wb /test-3/180worldanyoneBOý“cz±$ý“wb /test-5/175worldanyoneBOý“cz¯%ý“wb /test-7/173worldanyoneBOý“cб&ý“wb /test-8/175worldanyoneBOý“cН'ý“wc /test-9/173worldanyoneBOý“c~»(ý“wc /test-0/185worldanyoneBOý“c~®)ý“wc /test-4/172worldanyoneBOý“c~¶*ý“wc /test-2/180worldanyoneBOý“c~¼+ý“wn /test-0/186worldanyoneBOý“baÂ,ý“wp /test-6/192worldanyoneBOý“baË-ý“wp /test-1/201worldanyoneBOý“cz°.ý“wp /test-7/174worldanyoneBOý“cz·/ý“wp /test-3/181worldanyoneBOý“cв0ý“wp /test-8/176worldanyoneBOý“cа1ý“wq /test-9/174worldanyoneBOý“cz²2ý“wq /test-5/176worldanyoneBOý“c~¯3ý“ws /test-4/173worldanyoneBOý“c~·4ý“ws /test-2/181worldanyoneBOý“cг5ý“x /test-8/177worldanyoneBOý“baÌ6ý“x /test-1/202worldanyoneBOý“c~¸7ý“x /test-2/182worldanyoneBOý“c~°8ý“x /test-4/174worldanyoneBOý“c~½9ý“x /test-0/187worldanyoneBOý“baÃ:ý“x /test-6/193worldanyoneBOý“cб;ý“x /test-9/175worldanyoneBOý“cz¸<ý“x% /test-3/182worldanyoneBOý“cz³=ý“x% /test-5/177worldanyoneBOý“cz±>ý“x% /test-7/175worldanyoneBOý“cŠ´?ý“x' /test-8/178worldanyoneBOý“c~¹@ý“x) /test-2/183worldanyoneBOý“c~±Aý“x* /test-4/175worldanyoneBOý“c~¾Bý“x* /test-0/188worldanyoneBOý“baÄCý“x+ /test-6/194worldanyoneBOý“baÍDý“x+ /test-1/203worldanyoneBOý“cвEý“x+ /test-9/176worldanyoneBOý“cz¹Fý“x. /test-3/183worldanyoneBOý“cz´Gý“x2 /test-5/178worldanyoneBOý“cz²Hý“x2 /test-7/176worldanyoneBOý“cеIý“x3 /test-8/179worldanyoneBOý“c~²Jý“x5 /test-4/176worldanyoneBOý“c~ºKý“x5 /test-2/184worldanyoneBOý“c~¿Lý“x6 /test-0/189worldanyoneBOý“cгMý“x7 /test-9/177worldanyoneBOý“baÅNý“x9 /test-6/195worldanyoneBOý“czºOý“x9 /test-3/184worldanyoneBOý“baÎPý“x: /test-1/204worldanyoneBOý“cжQý“x@ /test-8/180worldanyoneBOý“cz³Rý“x@ /test-7/177worldanyoneBOý“czµSý“x@ /test-5/179worldanyoneBOý“c~³Tý“xC /test-4/177worldanyoneBOý“c~»Uý“xC /test-2/185worldanyoneBOý“c~ÀVý“xE /test-0/190worldanyoneBOý“baÆWý“xF /test-6/196worldanyoneBOý“cŠ´Xý“xF /test-9/178worldanyoneBOý“baÏYý“xG /test-1/205worldanyoneBOý“cz»Zý“xH /test-3/185worldanyoneBOý“cŠ·[ý“xK /test-8/181worldanyoneBOý“cz´\ý“xL /test-7/178worldanyoneBOý“cz¶]ý“xL /test-5/180worldanyoneBOý“c~¼^ý“xO /test-2/186worldanyoneBOý“c~´_ý“xO /test-4/178worldanyoneBOý“c~Á`ý“xQ /test-0/191worldanyoneBOý“baÇaý“xR /test-6/197worldanyoneBOý“baÐbý“xR /test-1/206worldanyoneBOý“cеcý“xR /test-9/179worldanyoneBOý“cz¼dý“xT /test-3/186worldanyoneBOý“cz·eý“xX /test-5/181worldanyoneBOý“czµfý“xY /test-7/179worldanyoneBOý“cЏgý“xY /test-8/182worldanyoneBOý“baÈhý“x^ /test-6/198worldanyoneBOý“baÑiý“x_ /test-1/207worldanyoneBOý“c~µjý“x_ /test-4/179worldanyoneBOý“cжký“x_ /test-9/180worldanyoneBOý“c~½lý“x_ /test-2/187worldanyoneBOý“cz½mý“x` /test-3/187worldanyoneBOý“c~Âný“xa /test-0/192worldanyoneBOý“cz¸oý“xb /test-5/182worldanyoneBOý“cz¶pý“xb /test-7/180worldanyoneBOý“cйqý“xc /test-8/183worldanyoneBOý“baÉrý“xj /test-6/199worldanyoneBOý“baÒsý“xk /test-1/208worldanyoneBOý“cz·tý“xn /test-7/181worldanyoneBOý“cŠºuý“xo /test-8/184worldanyoneBOý“cŠ·vý“xo /test-9/181worldanyoneBOý“cz¾wý“xp /test-3/188worldanyoneBOý“c~¶xý“xp /test-4/180worldanyoneBOý“c~¾yý“xr /test-2/188worldanyoneBOý“c~Ãzý“xr /test-0/193worldanyoneBOý“baÊ{ý“xs /test-6/200worldanyoneBOý“cz¹|ý“xs /test-5/183worldanyoneBOý“baÓ}ý“xt /test-1/209worldanyoneBOý“cz¸~ý“xy /test-7/182worldanyoneBOý“cz¿ý“x{ /test-3/189worldanyoneBOý“cЏ€ý“x| /test-9/182worldanyoneBOý“cŠ»ý“x| /test-8/185worldanyoneBOý“baË‚ý“x /test-6/201worldanyoneBOý“c~ăý“x€ /test-0/194worldanyoneBOý“c~·„ý“x€ /test-4/181worldanyoneBOý“c~¿…ý“x /test-2/189worldanyoneBOý“czº†ý“x‚ /test-5/184worldanyoneBOý“baÔ‡ý“xƒ /test-1/210worldanyoneBOý“cz¹ˆý“x„ /test-7/183worldanyoneBOý“cм‰ý“x‰ /test-8/186worldanyoneBOý“baÌŠý“xŠ /test-6/202worldanyoneBOý“czÀ‹ý“x‹ /test-3/190worldanyoneBOý“cйŒý“xŒ /test-9/183worldanyoneBOý“baÕý“x /test-1/211worldanyoneBOý“c~ÅŽý“x“ /test-0/195worldanyoneBOý“c~Àý“x” /test-2/190worldanyoneBOý“c~¸ý“x” /test-4/182worldanyoneBOý“czº‘ý“x” /test-7/184worldanyoneBOý“cz»’ý“x” /test-5/185worldanyoneBOý“baÍ“ý“x” /test-6/203worldanyoneBOý“cн”ý“x• /test-8/187worldanyoneBOý“baÖ•ý“x™ /test-1/212worldanyoneBOý“czÁ–ý“xœ /test-3/191worldanyoneBOý“cŠº—ý“x /test-9/184worldanyoneBOý“baΘý“x¡ /test-6/204worldanyoneBOý“c~Æ™ý“x¡ /test-0/196worldanyoneBOý“cz»šý“x¢ /test-7/185worldanyoneBOý“c~¹›ý“x¢ /test-4/183worldanyoneBOý“cоœý“x£ /test-8/188worldanyoneBOý“c~Áý“x¤ /test-2/191worldanyoneBOý“baמý“x¤ /test-1/213worldanyoneBOý“cz¼Ÿý“x¤ /test-5/186worldanyoneBOý“cz ý“x¦ /test-3/192worldanyoneBOý“cŠ»¡ý“x¦ /test-9/185worldanyoneBOý“baÏ¢ý“x¯ /test-6/205worldanyoneBOý“c~Ç£ý“x° /test-0/197worldanyoneBOý“cz¼¤ý“x° /test-7/186worldanyoneBOý“baØ¥ý“x² /test-1/214worldanyoneBOý“czæý“x² /test-3/193worldanyoneBOý“c~º§ý“x² /test-4/184worldanyoneBOý“c~¨ý“x³ /test-2/192worldanyoneBOý“cz½©ý“x´ /test-5/187worldanyoneBOý“cмªý“xµ /test-9/186worldanyoneBOý“cŠ¿«ý“xµ /test-8/189worldanyoneBOý“baЬý“x¸ /test-6/206worldanyoneBOý“c~È­ý“x¾ /test-0/198worldanyoneBOý“cz½®ý“x¾ /test-7/187worldanyoneBOý“baÙ¯ý“x /test-1/215worldanyoneBOý“cн°ý“xÄ /test-9/187worldanyoneBOý“c~»±ý“xÄ /test-4/185worldanyoneBOý“c~òý“xÄ /test-2/193worldanyoneBOý“cŠÀ³ý“xÅ /test-8/190worldanyoneBOý“cz¾´ý“xÅ /test-5/188worldanyoneBOý“czĵý“xÅ /test-3/194worldanyoneBOý“baѶý“xÆ /test-6/207worldanyoneBOý“c~É·ý“xÈ /test-0/199worldanyoneBOý“cz¾¸ý“xÈ /test-7/188worldanyoneBOý“cо¹ý“xÒ /test-9/188worldanyoneBOý“baÚºý“xÒ /test-1/216worldanyoneBOý“c~Ê»ý“xÔ /test-0/200worldanyoneBOý“baÒ¼ý“xÔ /test-6/208worldanyoneBOý“c~Ľý“xÕ /test-2/194worldanyoneBOý“c~¼¾ý“xÖ /test-4/186worldanyoneBOý“cŠÁ¿ý“xÖ /test-8/191worldanyoneBOý“cz¿Àý“xØ /test-7/189worldanyoneBOý“cz¿Áý“xØ /test-5/189worldanyoneBOý“czÅÂý“xØ /test-3/195worldanyoneBOý“cŠ¿Ãý“xÜ /test-9/189worldanyoneBOý“baÛÄý“xÞ /test-1/217worldanyoneBOý“c~½Åý“xâ /test-4/187worldanyoneBOý“c~ËÆý“xâ /test-0/201worldanyoneBOý“c~ÅÇý“xâ /test-2/195worldanyoneBOý“baÓÈý“xä /test-6/209worldanyoneBOý“cŠÂÉý“xé /test-8/192worldanyoneBOý“cŠÀÊý“xì /test-9/190worldanyoneBOý“baÜËý“xì /test-1/218worldanyoneBOý“czÀÌý“xî /test-7/190worldanyoneBOý“czÀÍý“xï /test-5/190worldanyoneBOý“baÔÎý“xï /test-6/210worldanyoneBOý“czÆÏý“xð /test-3/196worldanyoneBOý“c~¾Ðý“xò /test-4/188worldanyoneBOý“c~ÆÑý“xò /test-2/196worldanyoneBOý“c~ÌÒý“xò /test-0/202worldanyoneBOý“baÝÓý“xø /test-1/219worldanyoneBOý“cŠÁÔý“xø /test-9/191worldanyoneBOý“cŠÃÕý“xú /test-8/193worldanyoneBOý“baÕÖý“xý /test-6/211worldanyoneBOý“c~Ç×ý“xþ /test-2/197worldanyoneBOý“c~¿Øý“xÿ /test-4/189worldanyoneBOý“c~ÍÙý“xÿ /test-0/203worldanyoneBOý“czÁÚý“y /test-7/191worldanyoneBOý“baÞÛý“y /test-1/220worldanyoneBOý“czÁÜý“y /test-5/191worldanyoneBOý“czÇÝý“y /test-3/197worldanyoneBOý“cŠÂÞý“y  /test-9/192worldanyoneBOý“baÖßý“y  /test-6/212worldanyoneBOý“c~Èàý“y  /test-2/198worldanyoneBOý“cŠÄáý“y  /test-8/194worldanyoneBOý“c~Àâý“y /test-4/190worldanyoneBOý“czÂãý“y /test-7/192worldanyoneBOý“baßäý“y /test-1/221worldanyoneBOý“c~Îåý“y /test-0/204worldanyoneBOý“czÂæý“y /test-5/192worldanyoneBOý“czÈçý“y /test-3/198worldanyoneBOý“ba×èý“y /test-6/213worldanyoneBOý“cŠÃéý“y /test-9/193worldanyoneBOý“cŠÅêý“y /test-8/195worldanyoneBOý“c~Éëý“y /test-2/199worldanyoneBOý“c~Ïìý“y /test-0/205worldanyoneBOý“c~Áíý“y /test-4/191worldanyoneBOý“baàîý“y /test-1/222worldanyoneBOý“czÃïý“y" /test-7/193worldanyoneBOý“czÉðý“y" /test-3/199worldanyoneBOý“czÃñý“y" /test-5/193worldanyoneBOý“baØòý“y% /test-6/214worldanyoneBOý“c~Êóý“y( /test-2/200worldanyoneBOý“baáôý“y- /test-1/223worldanyoneBOý“cŠÆõý“y- /test-8/196worldanyoneBOý“cŠÄöý“y- /test-9/194worldanyoneBOý“c~Ð÷ý“y/ /test-0/206worldanyoneBOý“c~Âøý“y/ /test-4/192worldanyoneBOý“baÙùý“y1 /test-6/215worldanyoneBOý“czÊúý“y8 /test-3/200worldanyoneBOý“czÄûý“y8 /test-5/194worldanyoneBOý“czÄüý“y8 /test-7/194worldanyoneBOý“c~Ëýý“y9 /test-2/201worldanyoneBOý“baâþý“y9 /test-1/224worldanyoneBOý“c~Ñÿý“yD /test-0/207worldanyoneBOý“baÚý“yD /test-6/216worldanyoneBOý“cŠÇý“yD /test-8/197worldanyoneBOý“cŠÅý“yD /test-9/195worldanyoneBOý“c~Ãý“yD /test-4/193worldanyoneBOý“c~Ìý“yI /test-2/202worldanyoneBOý“baãý“yK /test-1/225worldanyoneBOý“czÅý“yK /test-5/195worldanyoneBOý“czÅý“yM /test-7/195worldanyoneBOý“czËý“yM /test-3/201worldanyoneBOý“c~Ä ý“yS /test-4/194worldanyoneBOý“c~Ò ý“yU /test-0/208worldanyoneBOý“baÛ ý“yU /test-6/217worldanyoneBOý“cŠÈ ý“yW /test-8/198worldanyoneBOý“cŠÆ ý“y\ /test-9/196worldanyoneBOý“c~Íý“y^ /test-2/203worldanyoneBOý“baäý“y` /test-1/226worldanyoneBOý“czÆý“yb /test-5/196worldanyoneBOý“czÌý“yc /test-3/202worldanyoneBOý“czÆý“yc /test-7/196worldanyoneBOý“c~Åý“ye /test-4/195worldanyoneBOý“c~Óý“yf /test-0/209worldanyoneBOý“baÜý“yh /test-6/218worldanyoneBOý“cŠÉý“yi /test-8/199worldanyoneBOý“cŠÇý“yi /test-9/197worldanyoneBOý“baåý“yn /test-1/227worldanyoneBOý“c~Îý“yp /test-2/204worldanyoneBOý“czÇý“yq /test-7/197worldanyoneBOý“czÇý“yr /test-5/197worldanyoneBOý“czÍý“yr /test-3/203worldanyoneBOý“baÝý“yt /test-6/219worldanyoneBOý“c~Æý“yu /test-4/196worldanyoneBOý“c~Ôý“yu /test-0/210worldanyoneBOý“cŠÈ ý“yw /test-9/198worldanyoneBOý“cŠÊ!ý“yw /test-8/200worldanyoneBOý“baæ"ý“y{ /test-1/228worldanyoneBOý“c~Ï#ý“y{ /test-2/205worldanyoneBOý“czÈ$ý“y€ /test-5/198worldanyoneBOý“czÎ%ý“y€ /test-3/204worldanyoneBOý“czÈ&ý“y /test-7/198worldanyoneBOý“baÞ'ý“yƒ /test-6/220worldanyoneBOý“cŠË(ý“y… /test-8/201worldanyoneBOý“cŠÉ)ý“y… /test-9/199worldanyoneBOý“c~Ç*ý“yŒ /test-4/197worldanyoneBOý“baç+ý“y /test-1/229worldanyoneBOý“c~Õ,ý“y /test-0/211worldanyoneBOý“c~Ð-ý“yŽ /test-2/206worldanyoneBOý“czÉ.ý“y /test-5/199worldanyoneBOý“czÏ/ý“y /test-3/205worldanyoneBOý“czÉ0ý“y /test-7/199worldanyoneBOý“baß1ý“y‘ /test-6/221worldanyoneBOý“cŠÌ2ý“y’ /test-8/202worldanyoneBOý“cŠÊ3ý“y’ /test-9/200worldanyoneBOý“baè4ý“y /test-1/230worldanyoneBOý“cŠÍ5ý“yŸ /test-8/203worldanyoneBOý“czÐ6ý“yŸ /test-3/206worldanyoneBOý“czÊ7ý“y¡ /test-5/200worldanyoneBOý“czÊ8ý“y¢ /test-7/200worldanyoneBOý“cŠË9ý“y¢ /test-9/201worldanyoneBOý“baà:ý“y¢ /test-6/222worldanyoneBOý“c~È;ý“y£ /test-4/198worldanyoneBOý“c~Ñ<ý“y£ /test-2/207worldanyoneBOý“c~Ö=ý“y¤ /test-0/212worldanyoneBOý“baé>ý“y® /test-1/231worldanyoneBOý“cŠÎ?ý“y¯ /test-8/204worldanyoneBOý“cŠÌ@ý“y° /test-9/202worldanyoneBOý“czËAý“y± /test-7/201worldanyoneBOý“czÑBý“y± /test-3/207worldanyoneBOý“czËCý“y² /test-5/201worldanyoneBOý“baáDý“y³ /test-6/223worldanyoneBOý“c~ÒEý“y´ /test-2/208worldanyoneBOý“c~ÉFý“yµ /test-4/199worldanyoneBOý“c~×Gý“y· /test-0/213worldanyoneBOý“cŠÏHý“y» /test-8/205worldanyoneBOý“cŠÍIý“y» /test-9/203worldanyoneBOý“baêJý“y¼ /test-1/232worldanyoneBOý“czÒKý“y¿ /test-3/208worldanyoneBOý“czÌLý“yÀ /test-5/202worldanyoneBOý“czÌMý“yÀ /test-7/202worldanyoneBOý“baâNý“yÄ /test-6/224worldanyoneBOý“c~ÓOý“yÆ /test-2/209worldanyoneBOý“c~ÊPý“yÇ /test-4/200worldanyoneBOý“cŠÐQý“yÉ /test-8/206worldanyoneBOý“cŠÎRý“yÊ /test-9/204worldanyoneBOý“c~ØSý“yË /test-0/214worldanyoneBOý“baëTý“yË /test-1/233worldanyoneBOý“czÍUý“yÑ /test-5/203worldanyoneBOý“czÓVý“yÑ /test-3/209worldanyoneBOý“czÍWý“yÒ /test-7/203worldanyoneBOý“baãXý“yÓ /test-6/225worldanyoneBOý“cŠÏYý“yÕ /test-9/205worldanyoneBOý“cŠÑZý“yÕ /test-8/207worldanyoneBOý“c~Ô[ý“yØ /test-2/210worldanyoneBOý“c~Ë\ý“yÙ /test-4/201worldanyoneBOý“c~Ù]ý“yÙ /test-0/215worldanyoneBOý“baì^ý“yÚ /test-1/234worldanyoneBOý“cŠÐ_ý“yá /test-9/206worldanyoneBOý“cŠÒ`ý“yâ /test-8/208worldanyoneBOý“baäaý“yã /test-6/226worldanyoneBOý“czÔbý“yä /test-3/210worldanyoneBOý“czÎcý“yä /test-5/204worldanyoneBOý“czÎdý“yä /test-7/204worldanyoneBOý“c~Õeý“yç /test-2/211worldanyoneBOý“c~Ìfý“yè /test-4/202worldanyoneBOý“baígý“yê /test-1/235worldanyoneBOý“c~Úhý“yê /test-0/216worldanyoneBOý“cŠÑiý“yï /test-9/207worldanyoneBOý“cŠÓjý“yò /test-8/209worldanyoneBOý“baåký“yó /test-6/227worldanyoneBOý“c~Ílý“yõ /test-4/203worldanyoneBOý“c~Ömý“yõ /test-2/212worldanyoneBOý“czÕný“yõ /test-3/211worldanyoneBOý“c~Ûoý“y÷ /test-0/217worldanyoneBOý“czÏpý“yø /test-7/205worldanyoneBOý“czÏqý“yù /test-5/205worldanyoneBOý“baîrý“yù /test-1/236worldanyoneBOý“cŠÒsý“yú /test-9/208worldanyoneBOý“cŠÔtý“yþ /test-8/210worldanyoneBOý“baæuý“yÿ /test-6/228worldanyoneBOý“c~Îvý“z /test-4/204worldanyoneBOý“c~×wý“z /test-2/213worldanyoneBOý“czÖxý“z /test-3/212worldanyoneBOý“c~Üyý“z /test-0/218worldanyoneBOý“czÐzý“z /test-7/206worldanyoneBOý“baï{ý“z  /test-1/237worldanyoneBOý“czÐ|ý“z  /test-5/206worldanyoneBOý“cŠÓ}ý“z  /test-9/209worldanyoneBOý“cŠÕ~ý“z  /test-8/211worldanyoneBOý“baçý“z  /test-6/229worldanyoneBOý“c~Ø€ý“z /test-2/214worldanyoneBOý“c~Ïý“z /test-4/205worldanyoneBOý“czׂý“z /test-3/213worldanyoneBOý“cŠÔƒý“z /test-9/210worldanyoneBOý“cŠÖ„ý“z /test-8/212worldanyoneBOý“czÑ…ý“z /test-7/207worldanyoneBOý“c~݆ý“z /test-0/219worldanyoneBOý“bað‡ý“z /test-1/238worldanyoneBOý“baèˆý“z /test-6/230worldanyoneBOý“czщý“z  /test-5/207worldanyoneBOý“c~Њý“z' /test-4/206worldanyoneBOý“c~Ù‹ý“z( /test-2/215worldanyoneBOý“czØŒý“z( /test-3/214worldanyoneBOý“cŠÕý“z* /test-9/211worldanyoneBOý“bañŽý“z* /test-1/239worldanyoneBOý“cŠ×ý“z, /test-8/213worldanyoneBOý“czÒý“z- /test-7/208worldanyoneBOý“czÒ‘ý“z. /test-5/208worldanyoneBOý“baé’ý“z0 /test-6/231worldanyoneBOý“c~Þ“ý“z0 /test-0/220worldanyoneBOý“c~Ñ”ý“z3 /test-4/207worldanyoneBOý“c~Ú•ý“z6 /test-2/216worldanyoneBOý“czÙ–ý“z: /test-3/215worldanyoneBOý“baê—ý“z= /test-6/232worldanyoneBOý“czÓ˜ý“z= /test-7/209worldanyoneBOý“baò™ý“z= /test-1/240worldanyoneBOý“czÓšý“z> /test-5/209worldanyoneBOý“cŠÖ›ý“z@ /test-9/212worldanyoneBOý“c~ßœý“z@ /test-0/221worldanyoneBOý“cŠØý“zA /test-8/214worldanyoneBOý“c~Òžý“zH /test-4/208worldanyoneBOý“c~ÛŸý“zI /test-2/217worldanyoneBOý“baë ý“zL /test-6/233worldanyoneBOý“baó¡ý“zM /test-1/241worldanyoneBOý“cŠ×¢ý“zS /test-9/213worldanyoneBOý“c~à£ý“zS /test-0/222worldanyoneBOý“czÚ¤ý“zS /test-3/216worldanyoneBOý“czÔ¥ý“zS /test-7/210worldanyoneBOý“czÔ¦ý“zT /test-5/210worldanyoneBOý“cŠÙ§ý“zT /test-8/215worldanyoneBOý“baì¨ý“zW /test-6/234worldanyoneBOý“baô©ý“z[ /test-1/242worldanyoneBOý“c~Óªý“z[ /test-4/209worldanyoneBOý“c~Ü«ý“z[ /test-2/218worldanyoneBOý“czÛ¬ý“za /test-3/217worldanyoneBOý“c~á­ý“za /test-0/223worldanyoneBOý“cŠØ®ý“zd /test-9/214worldanyoneBOý“baí¯ý“zd /test-6/235worldanyoneBOý“czÕ°ý“zd /test-7/211worldanyoneBOý“czÕ±ý“zd /test-5/211worldanyoneBOý“baõ²ý“ze /test-1/243worldanyoneBOý“cŠÚ³ý“zh /test-8/216worldanyoneBOý“c~Ý´ý“zm /test-2/219worldanyoneBOý“c~Ôµý“zm /test-4/210worldanyoneBOý“czܶý“zr /test-3/218worldanyoneBOý“czÖ·ý“zr /test-7/212worldanyoneBOý“c~â¸ý“zs /test-0/224worldanyoneBOý“czÖ¹ý“zs /test-5/212worldanyoneBOý“baîºý“zs /test-6/236worldanyoneBOý“baö»ý“zx /test-1/244worldanyoneBOý“cŠÙ¼ý“zx /test-9/215worldanyoneBOý“cŠÛ½ý“zy /test-8/217worldanyoneBOý“c~Þ¾ý“zy /test-2/220worldanyoneBOý“c~Õ¿ý“zy /test-4/211worldanyoneBOý“czÝÀý“z} /test-3/219worldanyoneBOý“baïÁý“z /test-6/237worldanyoneBOý“c~ãÂý“z /test-0/225worldanyoneBOý“cz×Ãý“z /test-7/213worldanyoneBOý“cz×Äý“z€ /test-5/213worldanyoneBOý“cŠÚÅý“z„ /test-9/216worldanyoneBOý“cŠÜÆý“z… /test-8/218worldanyoneBOý“ba÷Çý“z… /test-1/245worldanyoneBOý“c~ßÈý“z† /test-2/221worldanyoneBOý“c~ÖÉý“z† /test-4/212worldanyoneBOý“czÞÊý“zˆ /test-3/220worldanyoneBOý“baðËý“z‰ /test-6/238worldanyoneBOý“czØÌý“z‰ /test-7/214worldanyoneBOý“czØÍý“z‰ /test-5/214worldanyoneBOý“c~äÎý“z‹ /test-0/226worldanyoneBOý“baøÏý“z /test-1/246worldanyoneBOý“cŠÝÐý“z’ /test-8/219worldanyoneBOý“cŠÛÑý“z’ /test-9/217worldanyoneBOý“c~×Òý“z“ /test-4/213worldanyoneBOý“c~àÓý“z“ /test-2/222worldanyoneBOý“czßÔý“z” /test-3/221worldanyoneBOý“bañÕý“z• /test-6/239worldanyoneBOý“c~åÖý“z• /test-0/227worldanyoneBOý“czÙ×ý“z• /test-7/215worldanyoneBOý“czÙØý“z– /test-5/215worldanyoneBOý“baùÙý“z™ /test-1/247worldanyoneBOý“cŠÞÚý“z /test-8/220worldanyoneBOý“cŠÜÛý“zž /test-9/218worldanyoneBOý“c~áÜý“zŸ /test-2/223worldanyoneBOý“c~ØÝý“zŸ /test-4/214worldanyoneBOý“czàÞý“z  /test-3/222worldanyoneBOý“baòßý“z  /test-6/240worldanyoneBOý“baúàý“z¡ /test-1/248worldanyoneBOý“c~æáý“z¢ /test-0/228worldanyoneBOý“czÚâý“z¢ /test-7/216worldanyoneBOý“czÚãý“z£ /test-5/216worldanyoneBOý“cŠßäý“z§ /test-8/221worldanyoneBOý“cŠÝåý“z© /test-9/219worldanyoneBOý“c~âæý“zª /test-2/224worldanyoneBOý“czáçý“zª /test-3/223worldanyoneBOý“c~Ùèý“zª /test-4/215worldanyoneBOý“baóéý“z­ /test-6/241worldanyoneBOý“baûêý“z­ /test-1/249worldanyoneBOý“czÛëý“z­ /test-7/217worldanyoneBOý“c~çìý“z® /test-0/229worldanyoneBOý“czÛíý“z¯ /test-5/217worldanyoneBOý“cŠàîý“z° /test-8/222worldanyoneBOý“cŠÞïý“z³ /test-9/220worldanyoneBOý“c~ãðý“z´ /test-2/225worldanyoneBOý“czâñý“z´ /test-3/224worldanyoneBOý“c~Úòý“zµ /test-4/216worldanyoneBOý“c~èóý“z¸ /test-0/230worldanyoneBOý“baüôý“z¹ /test-1/250worldanyoneBOý“baôõý“zº /test-6/242worldanyoneBOý“czÜöý“zº /test-5/218worldanyoneBOý“czÜ÷ý“zº /test-7/218worldanyoneBOý“cŠáøý“z» /test-8/223worldanyoneBOý“cŠßùý“z½ /test-9/221worldanyoneBOý“c~äúý“z¾ /test-2/226worldanyoneBOý“czãûý“z¿ /test-3/225worldanyoneBOý“c~Ûüý“z¿ /test-4/217worldanyoneBOý“c~éýý“zÀ /test-0/231worldanyoneBOý“baõþý“zÝ /test-6/243worldanyoneBOý“baýÿý“zÝ /test-1/251worldanyoneBOý“cŠâ ý“zÝ /test-8/224worldanyoneBOý“czÝ ý“zÞ /test-7/219worldanyoneBOý“czÝ ý“zÞ /test-5/219worldanyoneBOý“cŠà ý“zâ /test-9/222worldanyoneBOý“c~ê ý“zã /test-0/232worldanyoneBOý“czä ý“zä /test-3/226worldanyoneBOý“c~å ý“zå /test-2/227worldanyoneBOý“c~Ü ý“zå /test-4/218worldanyoneBOý“baö ý“zæ /test-6/244worldanyoneBOý“czÞ ý“zç /test-7/220worldanyoneBOý“baþ ý“zè /test-1/252worldanyoneBOý“cŠã ý“zè /test-8/225worldanyoneBOý“czÞ ý“zè /test-5/220worldanyoneBOý“cŠá ý“zë /test-9/223worldanyoneBOý“czå ý“zï /test-3/227worldanyoneBOý“c~ë ý“zð /test-0/233worldanyoneBOý“c~Ý ý“zð /test-4/219worldanyoneBOý“c~æ ý“zð /test-2/228worldanyoneBOý“ba÷ ý“zñ /test-6/245worldanyoneBOý“czß ý“zñ /test-7/221worldanyoneBOý“baÿ ý“zò /test-1/253worldanyoneBOý“czß ý“zô /test-5/221worldanyoneBOý“cŠä ý“zô /test-8/226worldanyoneBOý“cŠâ ý“zõ /test-9/224worldanyoneBOý“baø ý“zû /test-6/246worldanyoneBOý“czæ ý“zü /test-3/228worldanyoneBOý“c~Þ ý“zü /test-4/220worldanyoneBOý“czà ý“zý /test-7/222worldanyoneBOý“c~ç ý“zý /test-2/229worldanyoneBOý“c~ì ý“zý /test-0/234worldanyoneBOý“cŠã ý“zÿ /test-9/225worldanyoneBOý“czà ý“zÿ /test-5/222worldanyoneBOý“ba ý“zÿ /test-1/254worldanyoneBOý“cŠå !ý“zÿ /test-8/227worldanyoneBOý“czç "ý“{ /test-3/229worldanyoneBOý“baù #ý“{ /test-6/247worldanyoneBOý“c~ß $ý“{ /test-4/221worldanyoneBOý“czá %ý“{  /test-7/223worldanyoneBOý“c~è &ý“{  /test-2/230worldanyoneBOý“ba 'ý“{  /test-1/255worldanyoneBOý“czá (ý“{  /test-5/223worldanyoneBOý“cŠæ )ý“{  /test-8/228worldanyoneBOý“cŠä *ý“{  /test-9/226worldanyoneBOý“c~í +ý“{  /test-0/235worldanyoneBOý“baú ,ý“{ /test-6/248worldanyoneBOý“czè -ý“{ /test-3/230worldanyoneBOý“c~à .ý“{ /test-4/222worldanyoneBOý“ba /ý“{ /test-1/256worldanyoneBOý“c~é 0ý“{ /test-2/231worldanyoneBOý“czâ 1ý“{ /test-5/224worldanyoneBOý“c~î 2ý“{ /test-0/236worldanyoneBOý“czâ 3ý“{ /test-7/224worldanyoneBOý“cŠå 4ý“{ /test-9/227worldanyoneBOý“cŠç 5ý“{ /test-8/229worldanyoneBOý“baû 6ý“{ /test-6/249worldanyoneBOý“czé 7ý“{ /test-3/231worldanyoneBOý“c~á 8ý“{ /test-4/223worldanyoneBOý“ba 9ý“{  /test-1/257worldanyoneBOý“c~ê :ý“{# /test-2/232worldanyoneBOý“baü ;ý“{% /test-6/250worldanyoneBOý“czã <ý“{% /test-5/225worldanyoneBOý“czã =ý“{% /test-7/225worldanyoneBOý“c~ï >ý“{& /test-0/237worldanyoneBOý“cŠè ?ý“{& /test-8/230worldanyoneBOý“cŠæ @ý“{' /test-9/228worldanyoneBOý“czê Aý“{' /test-3/232worldanyoneBOý“c~â Bý“{' /test-4/224worldanyoneBOý“ba Cý“{) /test-1/258worldanyoneBOý“c~ë Dý“{, /test-2/233worldanyoneBOý“baý Eý“{. /test-6/251worldanyoneBOý“czä Fý“{0 /test-7/226worldanyoneBOý“czä Gý“{0 /test-5/226worldanyoneBOý“c~ð Hý“{2 /test-0/238worldanyoneBOý“czë Iý“{2 /test-3/233worldanyoneBOý“cŠé Jý“{2 /test-8/231worldanyoneBOý“cŠç Ký“{2 /test-9/229worldanyoneBOý“c~ã Lý“{3 /test-4/225worldanyoneBOý“ba Mý“{4 /test-1/259worldanyoneBOý“c~ì Ný“{6 /test-2/234worldanyoneBOý“baþ Oý“{7 /test-6/252worldanyoneBOý“czå Pý“{; /test-7/227worldanyoneBOý“czå Qý“{< /test-5/227worldanyoneBOý“c~ñ Rý“{= /test-0/239worldanyoneBOý“ba Sý“{? /test-1/260worldanyoneBOý“cŠê Tý“{? /test-8/232worldanyoneBOý“cŠè Uý“{? /test-9/230worldanyoneBOý“c~ä Vý“{@ /test-4/226worldanyoneBOý“czì Wý“{@ /test-3/234worldanyoneBOý“c~í Xý“{@ /test-2/235worldanyoneBOý“baÿ Yý“{@ /test-6/253worldanyoneBOý“czæ Zý“{B /test-7/228worldanyoneBOý“czæ [ý“{F /test-5/228worldanyoneBOý“c~ò \ý“{G /test-0/240worldanyoneBOý“cŠë ]ý“{L /test-8/233worldanyoneBOý“cŠé ^ý“{L /test-9/231worldanyoneBOý“ba _ý“{M /test-6/254worldanyoneBOý“ba `ý“{M /test-1/261worldanyoneBOý“czí aý“{M /test-3/235worldanyoneBOý“c~å bý“{M /test-4/227worldanyoneBOý“c~î cý“{M /test-2/236worldanyoneBOý“czç dý“{N /test-7/229worldanyoneBOý“c~ó eý“{P /test-0/241worldanyoneBOý“czç fý“{P /test-5/229worldanyoneBOý“ba gý“{W /test-6/255worldanyoneBOý“czî hý“{X /test-3/236worldanyoneBOý“czè iý“{X /test-7/230worldanyoneBOý“cŠì jý“{Y /test-8/234worldanyoneBOý“cŠê ký“{Z /test-9/232worldanyoneBOý“c~æ lý“{Z /test-4/228worldanyoneBOý“ba mý“{Z /test-1/262worldanyoneBOý“c~ï ný“{[ /test-2/237worldanyoneBOý“c~ô oý“{\ /test-0/242worldanyoneBOý“czè pý“{\ /test-5/230worldanyoneBOý“ba qý“{_ /test-6/256worldanyoneBOý“czï rý“{c /test-3/237worldanyoneBOý“cŠí sý“{d /test-8/235worldanyoneBOý“cŠë tý“{e /test-9/233worldanyoneBOý“c~ç uý“{e /test-4/229worldanyoneBOý“c~ð vý“{g /test-2/238worldanyoneBOý“ba  wý“{h /test-1/263worldanyoneBOý“c~õ xý“{h /test-0/243worldanyoneBOý“czé yý“{h /test-5/231worldanyoneBOý“czé zý“{h /test-7/231worldanyoneBOý“ba {ý“{h /test-6/257worldanyoneBOý“czð |ý“{m /test-3/238worldanyoneBOý“cŠì }ý“{s /test-9/234worldanyoneBOý“c~è ~ý“{t /test-4/230worldanyoneBOý“c~ñ ý“{t /test-2/239worldanyoneBOý“c~ö €ý“{u /test-0/244worldanyoneBOý“czê ý“{u /test-5/232worldanyoneBOý“czê ‚ý“{u /test-7/232worldanyoneBOý“ba  ƒý“{v /test-1/264worldanyoneBOý“ba „ý“{v /test-6/258worldanyoneBOý“cŠî …ý“{v /test-8/236worldanyoneBOý“czñ †ý“{w /test-3/239worldanyoneBOý“cŠí ‡ý“{ /test-9/235worldanyoneBOý“ba  ˆý“{‚ /test-1/265worldanyoneBOý“c~ò ‰ý“{ƒ /test-2/240worldanyoneBOý“c~é Šý“{ƒ /test-4/231worldanyoneBOý“czë ‹ý“{ƒ /test-5/233worldanyoneBOý“czë Œý“{„ /test-7/233worldanyoneBOý“c~÷ ý“{„ /test-0/245worldanyoneBOý“ba Žý“{„ /test-6/259worldanyoneBOý“czò ý“{„ /test-3/240worldanyoneBOý“cŠï ý“{… /test-8/237worldanyoneBOý“cŠî ‘ý“{‹ /test-9/236worldanyoneBOý“ba  ’ý“{Ž /test-1/266worldanyoneBOý“c~ó “ý“{ /test-2/241worldanyoneBOý“ba ”ý“{‘ /test-6/260worldanyoneBOý“c~ê •ý“{‘ /test-4/232worldanyoneBOý“cŠð –ý“{‘ /test-8/238worldanyoneBOý“czì —ý“{‘ /test-5/234worldanyoneBOý“czó ˜ý“{‘ /test-3/241worldanyoneBOý“czì ™ý“{’ /test-7/234worldanyoneBOý“c~ø šý“{’ /test-0/246worldanyoneBOý“cŠï ›ý“{• /test-9/237worldanyoneBOý“ba  œý“{— /test-1/267worldanyoneBOý“c~ô ý“{› /test-2/242worldanyoneBOý“czí žý“{ /test-7/235worldanyoneBOý“c~ë Ÿý“{ž /test-4/233worldanyoneBOý“czí  ý“{ž /test-5/235worldanyoneBOý“czô ¡ý“{ž /test-3/242worldanyoneBOý“ba ¢ý“{ž /test-6/261worldanyoneBOý“cŠñ £ý“{Ÿ /test-8/239worldanyoneBOý“ba ¤ý“{  /test-1/268worldanyoneBOý“c~ù ¥ý“{¡ /test-0/247worldanyoneBOý“cŠð ¦ý“{¡ /test-9/238worldanyoneBOý“c~õ §ý“{¥ /test-2/243worldanyoneBOý“ba ¨ý“{« /test-6/262worldanyoneBOý“czõ ©ý“{­ /test-3/243worldanyoneBOý“czî ªý“{­ /test-5/236worldanyoneBOý“czî «ý“{® /test-7/236worldanyoneBOý“c~ì ¬ý“{® /test-4/234worldanyoneBOý“ba ­ý“{¯ /test-1/269worldanyoneBOý“cŠñ ®ý“{¯ /test-9/239worldanyoneBOý“cŠò ¯ý“{° /test-8/240worldanyoneBOý“c~ú °ý“{° /test-0/248worldanyoneBOý“c~ö ±ý“{± /test-2/244worldanyoneBOý“ba  ²ý“{µ /test-6/263worldanyoneBOý“ba ³ý“{½ /test-1/270worldanyoneBOý“cŠò ´ý“{½ /test-9/240worldanyoneBOý“c~í µý“{¾ /test-4/235worldanyoneBOý“cŠó ¶ý“{¾ /test-8/241worldanyoneBOý“c~÷ ·ý“{¾ /test-2/245worldanyoneBOý“c~û ¸ý“{¾ /test-0/249worldanyoneBOý“czï ¹ý“{¾ /test-5/237worldanyoneBOý“czï ºý“{¿ /test-7/237worldanyoneBOý“czö »ý“{¿ /test-3/244worldanyoneBOý“ba  ¼ý“{À /test-6/264worldanyoneBOý“ba ½ý“{Ë /test-1/271worldanyoneBOý“czð ¾ý“{Í /test-5/238worldanyoneBOý“ba  ¿ý“{Í /test-6/265worldanyoneBOý“cŠó Àý“{Ï /test-9/241worldanyoneBOý“czð Áý“{Ï /test-7/238worldanyoneBOý“cz÷ Âý“{Ï /test-3/245worldanyoneBOý“cŠô Ãý“{Ï /test-8/242worldanyoneBOý“c~î Äý“{Ð /test-4/236worldanyoneBOý“c~ø Åý“{Ð /test-2/246worldanyoneBOý“c~ü Æý“{Ð /test-0/250worldanyoneBOý“ba Çý“{Ø /test-1/272worldanyoneBOý“ba  Èý“{Ü /test-6/266worldanyoneBOý“czñ Éý“{Ý /test-5/239worldanyoneBOý“c~ù Êý“{á /test-2/247worldanyoneBOý“c~ï Ëý“{á /test-4/237worldanyoneBOý“c~ý Ìý“{á /test-0/251worldanyoneBOý“czñ Íý“{á /test-7/239worldanyoneBOý“czø Îý“{á /test-3/246worldanyoneBOý“cŠô Ïý“{á /test-9/242worldanyoneBOý“cŠõ Ðý“{â /test-8/243worldanyoneBOý“ba Ñý“{ä /test-1/273worldanyoneBOý“ba  Òý“{ê /test-6/267worldanyoneBOý“czò Óý“{ì /test-5/240worldanyoneBOý“c~þ Ôý“{ï /test-0/252worldanyoneBOý“c~ú Õý“{ï /test-2/248worldanyoneBOý“c~ð Öý“{ï /test-4/238worldanyoneBOý“ba ×ý“{ó /test-1/274worldanyoneBOý“czò Øý“{ó /test-7/240worldanyoneBOý“czù Ùý“{ô /test-3/247worldanyoneBOý“ba Úý“{÷ /test-6/268worldanyoneBOý“cŠõ Ûý“{ú /test-9/243worldanyoneBOý“cŠö Üý“{ú /test-8/244worldanyoneBOý“czó Ýý“{ü /test-5/241worldanyoneBOý“c~û Þý“| /test-2/249worldanyoneBOý“c~ñ ßý“| /test-4/239worldanyoneBOý“ba àý“| /test-1/275worldanyoneBOý“c~ÿ áý“| /test-0/253worldanyoneBOý“czú âý“| /test-3/248worldanyoneBOý“czó ãý“| /test-7/241worldanyoneBOý“ba äý“| /test-6/269worldanyoneBOý“cŠ÷ åý“| /test-8/245worldanyoneBOý“cŠö æý“|  /test-9/244worldanyoneBOý“czô çý“|  /test-5/242worldanyoneBOý“c~ü èý“| /test-2/250worldanyoneBOý“c~ò éý“| /test-4/240worldanyoneBOý“ba êý“| /test-1/276worldanyoneBOý“czû ëý“| /test-3/249worldanyoneBOý“c~ ìý“| /test-0/254worldanyoneBOý“czô íý“| /test-7/242worldanyoneBOý“cŠø îý“| /test-8/246worldanyoneBOý“ba ïý“| /test-6/270worldanyoneBOý“czõ ðý“| /test-5/243worldanyoneBOý“cŠ÷ ñý“| /test-9/245worldanyoneBOý“c~ý òý“| /test-2/251worldanyoneBOý“c~ó óý“|# /test-4/241worldanyoneBOý“cŠù ôý“|# /test-8/247worldanyoneBOý“czü õý“|& /test-3/250worldanyoneBOý“czõ öý“|' /test-7/243worldanyoneBOý“c~ ÷ý“|' /test-0/255worldanyoneBOý“ba øý“|' /test-6/271worldanyoneBOý“ba ùý“|* /test-1/277worldanyoneBOý“czö úý“|+ /test-5/244worldanyoneBOý“cŠø ûý“|+ /test-9/246worldanyoneBOý“c~þ üý“|, /test-2/252worldanyoneBOý“c~ô ýý“|0 /test-4/242worldanyoneBOý“cŠú þý“|2 /test-8/248worldanyoneBOý“c~ ÿý“|4 /test-0/256worldanyoneBOý“czö ý“|7 /test-7/244worldanyoneBOý“ba ý“|8 /test-6/272worldanyoneBOý“cŠù ý“|8 /test-9/247worldanyoneBOý“ba ý“|8 /test-1/278worldanyoneBOý“c~ÿ ý“|8 /test-2/253worldanyoneBOý“czý ý“|9 /test-3/251worldanyoneBOý“cz÷ ý“|: /test-5/245worldanyoneBOý“c~õ ý“|; /test-4/243worldanyoneBOý“cŠû ý“|> /test-8/249worldanyoneBOý“c~ ý“|A /test-0/257worldanyoneBOý“cz÷ ý“|C /test-7/245worldanyoneBOý“cŠú ý“|E /test-9/248worldanyoneBOý“ba ý“|F /test-6/273worldanyoneBOý“ba ý“|F /test-1/279worldanyoneBOý“czþ ý“|G /test-3/252worldanyoneBOý“czø ý“|G /test-5/246worldanyoneBOý“c~ö ý“|H /test-4/244worldanyoneBOý“c~ ý“|H /test-2/254worldanyoneBOý“cŠü ý“|I /test-8/250worldanyoneBOý“c~ ý“|I /test-0/258worldanyoneBOý“czø ý“|N /test-7/246worldanyoneBOý“ba ý“|S /test-1/280worldanyoneBOý“ba ý“|S /test-6/274worldanyoneBOý“cŠû ý“|S /test-9/249worldanyoneBOý“cŠý ý“|T /test-8/251worldanyoneBOý“czù ý“|V /test-5/247worldanyoneBOý“c~ ý“|V /test-2/255worldanyoneBOý“c~÷ ý“|W /test-4/245worldanyoneBOý“czÿ ý“|X /test-3/253worldanyoneBOý“c~ ý“|X /test-0/259worldanyoneBOý“czù ý“|Y /test-7/247worldanyoneBOý“ba ý“|^ /test-6/275worldanyoneBOý“ba ý“|_ /test-1/281worldanyoneBOý“cŠþ !ý“|_ /test-8/252worldanyoneBOý“cŠü "ý“|` /test-9/250worldanyoneBOý“czú #ý“|a /test-5/248worldanyoneBOý“c~ø $ý“|d /test-4/246worldanyoneBOý“c~ %ý“|d /test-2/256worldanyoneBOý“c~ &ý“|f /test-0/260worldanyoneBOý“cz 'ý“|f /test-3/254worldanyoneBOý“czú (ý“|f /test-7/248worldanyoneBOý“ba )ý“|k /test-6/276worldanyoneBOý“ba *ý“|l /test-1/282worldanyoneBOý“cŠý +ý“|m /test-9/251worldanyoneBOý“cŠÿ ,ý“|m /test-8/253worldanyoneBOý“czû -ý“|m /test-5/249worldanyoneBOý“c~ù .ý“|n /test-4/247worldanyoneBOý“c~ /ý“|o /test-2/257worldanyoneBOý“c~ 0ý“|r /test-0/261worldanyoneBOý“czû 1ý“|s /test-7/249worldanyoneBOý“cz 2ý“|s /test-3/255worldanyoneBOý“czü 3ý“|x /test-5/250worldanyoneBOý“ba 4ý“|y /test-6/277worldanyoneBOý“cŠþ 5ý“|y /test-9/252worldanyoneBOý“cŠ 6ý“|y /test-8/254worldanyoneBOý“ba 7ý“|y /test-1/283worldanyoneBOý“c~ 8ý“|{ /test-2/258worldanyoneBOý“c~ú 9ý“|| /test-4/248worldanyoneBOý“c~ :ý“|} /test-0/262worldanyoneBOý“czü ;ý“|~ /test-7/250worldanyoneBOý“cz <ý“|~ /test-3/256worldanyoneBOý“czý =ý“|‚ /test-5/251worldanyoneBOý“ba >ý“|… /test-6/278worldanyoneBOý“ba ?ý“|… /test-1/284worldanyoneBOý“cŠ @ý“|† /test-8/255worldanyoneBOý“cŠÿ Aý“|‡ /test-9/253worldanyoneBOý“c~ Bý“|Š /test-2/259worldanyoneBOý“czý Cý“|‹ /test-7/251worldanyoneBOý“c~û Dý“| /test-4/249worldanyoneBOý“cz Eý“|Ž /test-3/257worldanyoneBOý“c~  Fý“|Ž /test-0/263worldanyoneBOý“czþ Gý“| /test-5/252worldanyoneBOý“ba Hý“| /test-6/279worldanyoneBOý“cŠ Iý“|” /test-9/254worldanyoneBOý“ba Jý“|” /test-1/285worldanyoneBOý“cŠ Ký“|• /test-8/256worldanyoneBOý“c~ Lý“|– /test-2/260worldanyoneBOý“czþ Mý“|˜ /test-7/252worldanyoneBOý“ba Ný“|› /test-6/280worldanyoneBOý“c~  Oý“|› /test-0/264worldanyoneBOý“c~ü Pý“|› /test-4/250worldanyoneBOý“czÿ Qý“|› /test-5/253worldanyoneBOý“cz Rý“|œ /test-3/258worldanyoneBOý“ba  Sý“|  /test-1/286worldanyoneBOý“cŠ Tý“|¡ /test-8/257worldanyoneBOý“cŠ Uý“|¡ /test-9/255worldanyoneBOý“c~ Vý“|¢ /test-2/261worldanyoneBOý“czÿ Wý“|£ /test-7/253worldanyoneBOý“ba Xý“|¥ /test-6/281worldanyoneBOý“c~  Yý“|¦ /test-0/265worldanyoneBOý“c~ý Zý“|¦ /test-4/251worldanyoneBOý“cz [ý“|§ /test-5/254worldanyoneBOý“cz \ý“|§ /test-3/259worldanyoneBOý“ba! ]ý“|« /test-1/287worldanyoneBOý“cŠ ^ý“|¬ /test-8/258worldanyoneBOý“cŠ _ý“|¬ /test-9/256worldanyoneBOý“cz `ý“|¯ /test-7/254worldanyoneBOý“c~ aý“|¯ /test-2/262worldanyoneBOý“ba bý“|² /test-6/282worldanyoneBOý“c~þ cý“|³ /test-4/252worldanyoneBOý“c~  dý“|³ /test-0/266worldanyoneBOý“cz eý“|´ /test-5/255worldanyoneBOý“cz fý“|´ /test-3/260worldanyoneBOý“ba" gý“|µ /test-1/288worldanyoneBOý“cŠ hý“|· /test-8/259worldanyoneBOý“cŠ iý“|· /test-9/257worldanyoneBOý“cz jý“|¸ /test-7/255worldanyoneBOý“c~  ký“|¹ /test-2/263worldanyoneBOý“ba lý“|½ /test-6/283worldanyoneBOý“ba# mý“|À /test-1/289worldanyoneBOý“cz ný“| /test-5/256worldanyoneBOý“c~ÿ oý“| /test-4/253worldanyoneBOý“c~  pý“|à /test-0/267worldanyoneBOý“cŠ qý“|à /test-8/260worldanyoneBOý“cŠ rý“|à /test-9/258worldanyoneBOý“cz sý“|Ä /test-3/261worldanyoneBOý“c~  tý“|Ä /test-2/264worldanyoneBOý“cz uý“|Ä /test-7/256worldanyoneBOý“ba vý“|Æ /test-6/284worldanyoneBOý“ba$ wý“|Ê /test-1/290worldanyoneBOý“ba xý“|Ï /test-6/285worldanyoneBOý“cz yý“|Ñ /test-7/257worldanyoneBOý“cz zý“|Ñ /test-5/257worldanyoneBOý“c~ {ý“|Ñ /test-0/268worldanyoneBOý“c~ |ý“|Ñ /test-4/254worldanyoneBOý“c~  }ý“|Ñ /test-2/265worldanyoneBOý“cŠ ~ý“|Ó /test-9/259worldanyoneBOý“cŠ ý“|Ó /test-8/261worldanyoneBOý“cz €ý“|Ó /test-3/262worldanyoneBOý“ba% ý“|Ô /test-1/291worldanyoneBOý“ba  ‚ý“|Ú /test-6/286worldanyoneBOý“cz ƒý“|ß /test-5/258worldanyoneBOý“ba& „ý“|à /test-1/292worldanyoneBOý“c~ …ý“|à /test-4/255worldanyoneBOý“c~  †ý“|á /test-2/266worldanyoneBOý“c~ ‡ý“|á /test-0/269worldanyoneBOý“cŠ ˆý“|á /test-9/260worldanyoneBOý“cŠ ‰ý“|â /test-8/262worldanyoneBOý“cz  Šý“|â /test-3/263worldanyoneBOý“cz ‹ý“|â /test-7/258worldanyoneBOý“ba! Œý“|ä /test-6/287worldanyoneBOý“ba' ý“|î /test-1/293worldanyoneBOý“ba" Žý“|ï /test-6/288worldanyoneBOý“cz ý“|ï /test-5/259worldanyoneBOý“c~ ý“|ï /test-4/256worldanyoneBOý“cŠ ‘ý“|ð /test-9/261worldanyoneBOý“cz  ’ý“|ð /test-3/264worldanyoneBOý“c~ “ý“|ð /test-0/270worldanyoneBOý“c~  ”ý“|ñ /test-2/267worldanyoneBOý“cŠ  •ý“|ò /test-8/263worldanyoneBOý“cz –ý“|ò /test-7/259worldanyoneBOý“cz —ý“|û /test-5/260worldanyoneBOý“ba( ˜ý“|ü /test-1/294worldanyoneBOý“ba# ™ý“|ü /test-6/289worldanyoneBOý“cz  šý“|þ /test-3/265worldanyoneBOý“c~ ›ý“|þ /test-4/257worldanyoneBOý“cz œý“|þ /test-7/260worldanyoneBOý“cŠ  ý“|ÿ /test-8/264worldanyoneBOý“cŠ žý“|ÿ /test-9/262worldanyoneBOý“c~ Ÿý“} /test-0/271worldanyoneBOý“c~  ý“} /test-2/268worldanyoneBOý“ba) ¡ý“} /test-1/295worldanyoneBOý“cz ¢ý“} /test-5/261worldanyoneBOý“ba$ £ý“} /test-6/290worldanyoneBOý“c~ ¤ý“}  /test-4/258worldanyoneBOý“cz  ¥ý“}  /test-3/266worldanyoneBOý“cŠ  ¦ý“}  /test-9/263worldanyoneBOý“cŠ  §ý“}  /test-8/265worldanyoneBOý“cz ¨ý“}  /test-7/261worldanyoneBOý“c~ ©ý“}  /test-0/272worldanyoneBOý“c~ ªý“} /test-2/269worldanyoneBOý“ba% «ý“} /test-6/291worldanyoneBOý“ba* ¬ý“} /test-1/296worldanyoneBOý“cz ­ý“} /test-5/262worldanyoneBOý“c~ ®ý“} /test-4/259worldanyoneBOý“cz  ¯ý“} /test-3/267worldanyoneBOý“cŠ  °ý“} /test-8/266worldanyoneBOý“cŠ  ±ý“} /test-9/264worldanyoneBOý“cz ²ý“} /test-7/262worldanyoneBOý“c~ ³ý“} /test-2/270worldanyoneBOý“c~ ´ý“} /test-0/273worldanyoneBOý“cz  µý“} /test-5/263worldanyoneBOý“ba& ¶ý“} /test-6/292worldanyoneBOý“ba+ ·ý“}  /test-1/297worldanyoneBOý“cz ¸ý“}# /test-3/268worldanyoneBOý“cz  ¹ý“}# /test-7/263worldanyoneBOý“cŠ  ºý“}$ /test-8/267worldanyoneBOý“c~ »ý“}$ /test-4/260worldanyoneBOý“c~ ¼ý“}% /test-0/274worldanyoneBOý“cŠ  ½ý“}& /test-9/265worldanyoneBOý“c~ ¾ý“}& /test-2/271worldanyoneBOý“cz  ¿ý“}+ /test-5/264worldanyoneBOý“ba' Àý“}+ /test-6/293worldanyoneBOý“ba, Áý“}. /test-1/298worldanyoneBOý“cz Âý“}1 /test-3/269worldanyoneBOý“c~ Ãý“}2 /test-4/261worldanyoneBOý“cz  Äý“}2 /test-7/264worldanyoneBOý“c~ Åý“}2 /test-0/275worldanyoneBOý“c~ Æý“}3 /test-2/272worldanyoneBOý“cŠ  Çý“}4 /test-9/266worldanyoneBOý“cŠ Èý“}4 /test-8/268worldanyoneBOý“ba( Éý“}4 /test-6/294worldanyoneBOý“cz  Êý“}6 /test-5/265worldanyoneBOý“ba- Ëý“}7 /test-1/299worldanyoneBOý“cz Ìý“}= /test-3/270worldanyoneBOý“c~ Íý“}? /test-4/262worldanyoneBOý“cz  Îý“}A /test-7/265worldanyoneBOý“ba) Ïý“}B /test-6/295worldanyoneBOý“cŠ Ðý“}C /test-8/269worldanyoneBOý“cŠ  Ñý“}C /test-9/267worldanyoneBOý“c~ Òý“}C /test-0/276worldanyoneBOý“c~ Óý“}C /test-2/273worldanyoneBOý“ba. Ôý“}D /test-1/300worldanyoneBOý“cz  Õý“}D /test-5/266worldanyoneBOý“cz Öý“}F /test-3/271worldanyoneBOý“c~  ×ý“}K /test-4/263worldanyoneBOý“cz  Øý“}O /test-7/266worldanyoneBOý“c~ Ùý“}S /test-0/277worldanyoneBOý“c~ Úý“}S /test-2/274worldanyoneBOý“cz  Ûý“}U /test-5/267worldanyoneBOý“cz Üý“}V /test-3/272worldanyoneBOý“ba* Ýý“}V /test-6/296worldanyoneBOý“ba/ Þý“}V /test-1/301worldanyoneBOý“cŠ ßý“}W /test-8/270worldanyoneBOý“cŠ àý“}W /test-9/268worldanyoneBOý“c~  áý“}X /test-4/264worldanyoneBOý“cz  âý“}Y /test-7/267worldanyoneBOý“c~ ãý“}b /test-0/278worldanyoneBOý“c~ äý“}b /test-2/275worldanyoneBOý“cz åý“}f /test-5/268worldanyoneBOý“cŠ æý“}f /test-8/271worldanyoneBOý“cz çý“}f /test-3/273worldanyoneBOý“ba0 èý“}f /test-1/302worldanyoneBOý“cŠ éý“}h /test-9/269worldanyoneBOý“cz êý“}h /test-7/268worldanyoneBOý“ba+ ëý“}h /test-6/297worldanyoneBOý“c~  ìý“}h /test-4/265worldanyoneBOý“c~ íý“}m /test-2/276worldanyoneBOý“c~ îý“}m /test-0/279worldanyoneBOý“ba, ïý“}s /test-6/298worldanyoneBOý“cŠ ðý“}t /test-9/270worldanyoneBOý“ba1 ñý“}t /test-1/303worldanyoneBOý“cz òý“}t /test-5/269worldanyoneBOý“cŠ óý“}u /test-8/272worldanyoneBOý“c~  ôý“}v /test-4/266worldanyoneBOý“cz õý“}w /test-3/274worldanyoneBOý“cz öý“}w /test-7/269worldanyoneBOý“c~ ÷ý“}w /test-0/280worldanyoneBOý“c~ øý“}w /test-2/277worldanyoneBOý“ba- ùý“}‚ /test-6/299worldanyoneBOý“ba2 úý“}‚ /test-1/304worldanyoneBOý“cŠ ûý“}„ /test-9/271worldanyoneBOý“cŠ üý“}„ /test-8/273worldanyoneBOý“cz ýý“}„ /test-7/270worldanyoneBOý“cz þý“}„ /test-3/275worldanyoneBOý“cz ÿý“}„ /test-5/270worldanyoneBOý“c~ ý“}… /test-0/281worldanyoneBOý“c~  ý“}… /test-4/267worldanyoneBOý“c~ ý“}… /test-2/278worldanyoneBOý“ba3 ý“} /test-1/305worldanyoneBOý“cz ý“}‘ /test-3/276worldanyoneBOý“cz ý“}‘ /test-5/271worldanyoneBOý“cz ý“}‘ /test-7/271worldanyoneBOý“ba. ý“}‘ /test-6/300worldanyoneBOý“c~ ý“}’ /test-0/282worldanyoneBOý“cŠ ý“}’ /test-9/272worldanyoneBOý“cŠ ý“}’ /test-8/274worldanyoneBOý“c~ ý“}” /test-4/268worldanyoneBOý“c~ ý“}” /test-2/279worldanyoneBOý“ba4 ý“}£ /test-1/306worldanyoneBOý“c~ ý“}£ /test-0/283worldanyoneBOý“ba/ ý“}£ /test-6/301worldanyoneBOý“cŠ ý“}¤ /test-9/273worldanyoneBOý“cz ý“}¥ /test-5/272worldanyoneBOý“c~ ý“}¥ /test-2/280worldanyoneBOý“cŠ ý“}¥ /test-8/275worldanyoneBOý“cz ý“}¦ /test-3/277worldanyoneBOý“cz ý“}¨ /test-7/272worldanyoneBOý“c~ ý“}¨ /test-4/269worldanyoneBOý“ba5 ý“}² /test-1/307worldanyoneBOý“ba0 ý“}² /test-6/302worldanyoneBOý“cŠ ý“}³ /test-8/276worldanyoneBOý“cz ý“}´ /test-5/273worldanyoneBOý“cŠ ý“}´ /test-9/274worldanyoneBOý“cz ý“}´ /test-7/273worldanyoneBOý“cz ý“}´ /test-3/278worldanyoneBOý“c~ ý“}´ /test-0/284worldanyoneBOý“c~ ý“}µ /test-2/281worldanyoneBOý“c~ ý“}µ /test-4/270worldanyoneBOý“ba6 !ý“}½ /test-1/308worldanyoneBOý“ba1 "ý“}½ /test-6/303worldanyoneBOý“cŠ #ý“}¾ /test-8/277worldanyoneBOý“c~ $ý“}À /test-0/285worldanyoneBOý“cz %ý“}À /test-5/274worldanyoneBOý“cz &ý“}À /test-3/279worldanyoneBOý“cz 'ý“}À /test-7/274worldanyoneBOý“c~ (ý“}Á /test-2/282worldanyoneBOý“c~ )ý“}Á /test-4/271worldanyoneBOý“cŠ *ý“}Á /test-9/275worldanyoneBOý“ba7 +ý“}Ç /test-1/309worldanyoneBOý“ba2 ,ý“}Ç /test-6/304worldanyoneBOý“cŠ -ý“}Ê /test-8/278worldanyoneBOý“cŠ .ý“}Ë /test-9/276worldanyoneBOý“cz /ý“}Í /test-5/275worldanyoneBOý“cz 0ý“}Í /test-7/275worldanyoneBOý“c~ 1ý“}Í /test-4/272worldanyoneBOý“c~ 2ý“}Í /test-2/283worldanyoneBOý“c~  3ý“}Í /test-0/286worldanyoneBOý“cz 4ý“}Î /test-3/280worldanyoneBOý“ba8 5ý“}Ð /test-1/310worldanyoneBOý“ba3 6ý“}Ò /test-6/305worldanyoneBOý“cŠ 7ý“}Ó /test-8/279worldanyoneBOý“ba9 8ý“}ï /test-1/311worldanyoneBOý“cŠ 9ý“}ð /test-9/277worldanyoneBOý“c~ :ý“}ñ /test-2/284worldanyoneBOý“c~ ;ý“}ñ /test-4/273worldanyoneBOý“cz <ý“}ñ /test-7/276worldanyoneBOý“c~! =ý“}ñ /test-0/287worldanyoneBOý“cz >ý“}ò /test-5/276worldanyoneBOý“cŠ ?ý“}ò /test-8/280worldanyoneBOý“cz @ý“}ò /test-3/281worldanyoneBOý“ba4 Aý“}ò /test-6/306worldanyoneBOý“ba: Bý“}ù /test-1/312worldanyoneBOý“cŠ Cý“}û /test-9/278worldanyoneBOý“ba5 Dý“}ý /test-6/307worldanyoneBOý“cz Eý“}þ /test-7/277worldanyoneBOý“c~ Fý“}þ /test-2/285worldanyoneBOý“c~" Gý“}þ /test-0/288worldanyoneBOý“c~ Hý“}ÿ /test-4/274worldanyoneBOý“cz Iý“}ÿ /test-5/277worldanyoneBOý“cz Jý“}ÿ /test-3/282worldanyoneBOý“cŠ Ký“}ÿ /test-8/281worldanyoneBOý“ba; Lý“~ /test-1/313worldanyoneBOý“cŠ Mý“~ /test-9/279worldanyoneBOý“ba6 Ný“~  /test-6/308worldanyoneBOý“c~  Oý“~  /test-2/286worldanyoneBOý“c~ Pý“~  /test-4/275worldanyoneBOý“c~# Qý“~  /test-0/289worldanyoneBOý“cz Rý“~  /test-5/278worldanyoneBOý“cz Sý“~  /test-7/278worldanyoneBOý“cz Tý“~  /test-3/283worldanyoneBOý“cŠ Uý“~  /test-8/282worldanyoneBOý“ba< Vý“~  /test-1/314worldanyoneBOý“cŠ Wý“~ /test-9/280worldanyoneBOý“c~! Xý“~ /test-2/287worldanyoneBOý“cz Yý“~ /test-7/279worldanyoneBOý“cz Zý“~ /test-5/279worldanyoneBOý“cz [ý“~ /test-3/284worldanyoneBOý“ba7 \ý“~ /test-6/309worldanyoneBOý“ba= ]ý“~ /test-1/315worldanyoneBOý“c~$ ^ý“~ /test-0/290worldanyoneBOý“c~ _ý“~ /test-4/276worldanyoneBOý“cŠ `ý“~ /test-9/281worldanyoneBOý“cŠ aý“~ /test-8/283worldanyoneBOý“ba8 bý“~& /test-6/310worldanyoneBOý“ba> cý“~& /test-1/316worldanyoneBOý“c~% dý“~& /test-0/291worldanyoneBOý“cŠ eý“~' /test-8/284worldanyoneBOý“cŠ fý“~' /test-9/282worldanyoneBOý“c~" gý“~' /test-2/288worldanyoneBOý“cz hý“~' /test-3/285worldanyoneBOý“cz iý“~' /test-5/280worldanyoneBOý“c~ jý“~' /test-4/277worldanyoneBOý“cz ký“~' /test-7/280worldanyoneBOý“ba9 lý“~/ /test-6/311worldanyoneBOý“ba? mý“~3 /test-1/317worldanyoneBOý“cŠ ný“~3 /test-9/283worldanyoneBOý“cz  oý“~3 /test-3/286worldanyoneBOý“cz pý“~3 /test-5/281worldanyoneBOý“c~ qý“~4 /test-4/278worldanyoneBOý“cŠ rý“~4 /test-8/285worldanyoneBOý“c~# sý“~4 /test-2/289worldanyoneBOý“c~& tý“~4 /test-0/292worldanyoneBOý“cz uý“~4 /test-7/281worldanyoneBOý“ba: vý“~9 /test-6/312worldanyoneBOý“ba@ wý“~> /test-1/318worldanyoneBOý“c~' xý“~? /test-0/293worldanyoneBOý“c~ yý“~? /test-4/279worldanyoneBOý“c~$ zý“~? /test-2/290worldanyoneBOý“cŠ  {ý“~@ /test-8/286worldanyoneBOý“cz! |ý“~@ /test-3/287worldanyoneBOý“cŠ }ý“~@ /test-9/284worldanyoneBOý“cz ~ý“~@ /test-5/282worldanyoneBOý“cz ý“~@ /test-7/282worldanyoneBOý“ba; €ý“~B /test-6/313worldanyoneBOý“baA ý“~K /test-1/319worldanyoneBOý“c~ ‚ý“~M /test-4/280worldanyoneBOý“cz" ƒý“~M /test-3/288worldanyoneBOý“ba< „ý“~M /test-6/314worldanyoneBOý“cz …ý“~M /test-5/283worldanyoneBOý“cz †ý“~M /test-7/283worldanyoneBOý“c~( ‡ý“~N /test-0/294worldanyoneBOý“c~% ˆý“~N /test-2/291worldanyoneBOý“cŠ! ‰ý“~N /test-8/287worldanyoneBOý“cŠ Šý“~N /test-9/285worldanyoneBOý“baB ‹ý“~U /test-1/320worldanyoneBOý“ba= Œý“~X /test-6/315worldanyoneBOý“cz# ý“~X /test-3/289worldanyoneBOý“cz Žý“~Z /test-5/284worldanyoneBOý“c~ ý“~Z /test-4/281worldanyoneBOý“cŠ" ý“~Z /test-8/288worldanyoneBOý“cŠ  ‘ý“~[ /test-9/286worldanyoneBOý“cz ’ý“~[ /test-7/284worldanyoneBOý“c~& “ý“~[ /test-2/292worldanyoneBOý“c~) ”ý“~[ /test-0/295worldanyoneBOý“baC •ý“~^ /test-1/321worldanyoneBOý“ba> –ý“~b /test-6/316worldanyoneBOý“cz —ý“~d /test-5/285worldanyoneBOý“c~ ˜ý“~d /test-4/282worldanyoneBOý“cz$ ™ý“~e /test-3/290worldanyoneBOý“cŠ# šý“~g /test-8/289worldanyoneBOý“cŠ! ›ý“~g /test-9/287worldanyoneBOý“cz œý“~g /test-7/285worldanyoneBOý“c~' ý“~h /test-2/293worldanyoneBOý“c~* žý“~h /test-0/296worldanyoneBOý“baD Ÿý“~h /test-1/322worldanyoneBOý“ba?  ý“~j /test-6/317worldanyoneBOý“cz  ¡ý“~m /test-5/286worldanyoneBOý“cz% ¢ý“~n /test-3/291worldanyoneBOý“c~ £ý“~o /test-4/283worldanyoneBOý“cŠ" ¤ý“~r /test-9/288worldanyoneBOý“cŠ$ ¥ý“~r /test-8/290worldanyoneBOý“cz  ¦ý“~s /test-7/286worldanyoneBOý“c~+ §ý“~t /test-0/297worldanyoneBOý“c~( ¨ý“~t /test-2/294worldanyoneBOý“ba@ ©ý“~t /test-6/318worldanyoneBOý“baE ªý“~t /test-1/323worldanyoneBOý“cz! «ý“~x /test-5/287worldanyoneBOý“cz& ¬ý“~x /test-3/292worldanyoneBOý“c~ ­ý“~x /test-4/284worldanyoneBOý“cz! ®ý“~€ /test-7/287worldanyoneBOý“c~, ¯ý“~€ /test-0/298worldanyoneBOý“c~) °ý“~€ /test-2/295worldanyoneBOý“baA ±ý“~€ /test-6/319worldanyoneBOý“baF ²ý“~€ /test-1/324worldanyoneBOý“cŠ% ³ý“~€ /test-8/291worldanyoneBOý“cŠ# ´ý“~€ /test-9/289worldanyoneBOý“c~ µý“~ /test-4/285worldanyoneBOý“cz" ¶ý“~‚ /test-5/288worldanyoneBOý“cz' ·ý“~‚ /test-3/293worldanyoneBOý“cz" ¸ý“~‰ /test-7/288worldanyoneBOý“c~- ¹ý“~Œ /test-0/299worldanyoneBOý“cŠ& ºý“~Ž /test-8/292worldanyoneBOý“cz( »ý“~Ž /test-3/294worldanyoneBOý“baB ¼ý“~Ž /test-6/320worldanyoneBOý“cŠ$ ½ý“~Ž /test-9/290worldanyoneBOý“c~  ¾ý“~Ž /test-4/286worldanyoneBOý“c~* ¿ý“~Ž /test-2/296worldanyoneBOý“baG Àý“~Ž /test-1/325worldanyoneBOý“cz# Áý“~ /test-5/289worldanyoneBOý“cz# Âý“~’ /test-7/289worldanyoneBOý“c~. Ãý“~• /test-0/300worldanyoneBOý“baC Äý“~š /test-6/321worldanyoneBOý“baH Åý“~› /test-1/326worldanyoneBOý“cz) Æý“~› /test-3/295worldanyoneBOý“c~! Çý“~› /test-4/287worldanyoneBOý“cŠ% Èý“~› /test-9/291worldanyoneBOý“cŠ' Éý“~› /test-8/293worldanyoneBOý“c~+ Êý“~œ /test-2/297worldanyoneBOý“cz$ Ëý“~œ /test-5/290worldanyoneBOý“cz$ Ìý“~œ /test-7/290worldanyoneBOý“c~/ Íý“~ž /test-0/301worldanyoneBOý“baD Îý“~¨ /test-6/322worldanyoneBOý“cz% Ïý“~¨ /test-7/291worldanyoneBOý“cz% Ðý“~¨ /test-5/291worldanyoneBOý“cŠ( Ñý“~¨ /test-8/294worldanyoneBOý“cŠ& Òý“~© /test-9/292worldanyoneBOý“cz* Óý“~© /test-3/296worldanyoneBOý“c~" Ôý“~ª /test-4/288worldanyoneBOý“baI Õý“~ª /test-1/327worldanyoneBOý“c~, Öý“~« /test-2/298worldanyoneBOý“c~0 ×ý“~« /test-0/302worldanyoneBOý“baE Øý“~· /test-6/323worldanyoneBOý“cŠ) Ùý“~· /test-8/295worldanyoneBOý“cŠ' Úý“~· /test-9/293worldanyoneBOý“baJ Ûý“~¸ /test-1/328worldanyoneBOý“cz& Üý“~¸ /test-5/292worldanyoneBOý“cz+ Ýý“~¸ /test-3/297worldanyoneBOý“cz& Þý“~¸ /test-7/292worldanyoneBOý“c~1 ßý“~¹ /test-0/303worldanyoneBOý“c~# àý“~¹ /test-4/289worldanyoneBOý“c~- áý“~º /test-2/299worldanyoneBOý“baF âý“~Å /test-6/324worldanyoneBOý“baK ãý“~Æ /test-1/329worldanyoneBOý“cz, äý“~È /test-3/298worldanyoneBOý“cz' åý“~È /test-5/293worldanyoneBOý“cz' æý“~È /test-7/293worldanyoneBOý“c~$ çý“~É /test-4/290worldanyoneBOý“c~2 èý“~É /test-0/304worldanyoneBOý“c~. éý“~É /test-2/300worldanyoneBOý“cŠ* êý“~É /test-8/296worldanyoneBOý“cŠ( ëý“~É /test-9/294worldanyoneBOý“baG ìý“~Ñ /test-6/325worldanyoneBOý“baL íý“~Ò /test-1/330worldanyoneBOý“cz( îý“~× /test-5/294worldanyoneBOý“cz( ïý“~× /test-7/294worldanyoneBOý“cz- ðý“~Ù /test-3/299worldanyoneBOý“cŠ) ñý“~Ù /test-9/295worldanyoneBOý“c~3 òý“~Ù /test-0/305worldanyoneBOý“c~/ óý“~Ù /test-2/301worldanyoneBOý“c~% ôý“~Ù /test-4/291worldanyoneBOý“cŠ+ õý“~Ù /test-8/297worldanyoneBOý“baH öý“~Û /test-6/326worldanyoneBOý“baM ÷ý“~Ý /test-1/331worldanyoneBOý“cz) øý“~ä /test-5/295worldanyoneBOý“cz) ùý“~å /test-7/295worldanyoneBOý“cz. úý“~å /test-3/300worldanyoneBOý“cŠ* ûý“~æ /test-9/296worldanyoneBOý“cŠ, üý“~ç /test-8/298worldanyoneBOý“baI ýý“~è /test-6/327worldanyoneBOý“c~& þý“~è /test-4/292worldanyoneBOý“c~4 ÿý“~è /test-0/306worldanyoneBOý“c~0 ý“~é /test-2/302worldanyoneBOý“baN ý“~é /test-1/332worldanyoneBOý“cz* ý“~î /test-5/296worldanyoneBOý“cz* ý“~ñ /test-7/296worldanyoneBOý“baJ ý“~ô /test-6/328worldanyoneBOý“cz/ ý“~õ /test-3/301worldanyoneBOý“baO ý“~õ /test-1/333worldanyoneBOý“c~1 ý“~õ /test-2/303worldanyoneBOý“c~5 ý“~õ /test-0/307worldanyoneBOý“c~' ý“~õ /test-4/293worldanyoneBOý“cŠ- ý“~ö /test-8/299worldanyoneBOý“cŠ+ ý“~ö /test-9/297worldanyoneBOý“cz+ ý“~ø /test-5/297worldanyoneBOý“cz+ ý“~û /test-7/297worldanyoneBOý“baK ý“~þ /test-6/329worldanyoneBOý“baP ý“ /test-1/334worldanyoneBOý“cz0 ý“ /test-3/302worldanyoneBOý“c~( ý“ /test-4/294worldanyoneBOý“c~6 ý“ /test-0/308worldanyoneBOý“c~2 ý“ /test-2/304worldanyoneBOý“cŠ. ý“ /test-8/300worldanyoneBOý“cz, ý“ /test-5/298worldanyoneBOý“cŠ, ý“ /test-9/298worldanyoneBOý“cz, ý“ /test-7/298worldanyoneBOý“baL ý“ /test-6/330worldanyoneBOý“baQ ý“  /test-1/335worldanyoneBOý“cz1 ý“  /test-3/303worldanyoneBOý“c~3 ý“ /test-2/305worldanyoneBOý“c~7 ý“ /test-0/309worldanyoneBOý“c~) ý“ /test-4/295worldanyoneBOý“cz- ý“ /test-7/299worldanyoneBOý“cz- ý“ /test-5/299worldanyoneBOý“cŠ/ ý“ /test-8/301worldanyoneBOý“cŠ- !ý“ /test-9/299worldanyoneBOý“baM "ý“ /test-6/331worldanyoneBOý“baR #ý“ /test-1/336worldanyoneBOý“cz2 $ý“ /test-3/304worldanyoneBOý“c~8 %ý“ /test-0/310worldanyoneBOý“c~4 &ý“ /test-2/306worldanyoneBOý“baS 'ý“ /test-1/337worldanyoneBOý“baN (ý“ /test-6/332worldanyoneBOý“cz. )ý“  /test-7/300worldanyoneBOý“cŠ0 *ý“  /test-8/302worldanyoneBOý“cz. +ý“! /test-5/300worldanyoneBOý“cz3 ,ý“! /test-3/305worldanyoneBOý“cŠ. -ý“" /test-9/300worldanyoneBOý“c~* .ý“" /test-4/296worldanyoneBOý“c~5 /ý“$ /test-2/307worldanyoneBOý“c~9 0ý“$ /test-0/311worldanyoneBOý“baT 1ý“( /test-1/338worldanyoneBOý“baO 2ý“) /test-6/333worldanyoneBOý“cz/ 3ý“* /test-7/301worldanyoneBOý“c~+ 4ý“- /test-4/297worldanyoneBOý“cŠ1 5ý“. /test-8/303worldanyoneBOý“cŠ/ 6ý“. /test-9/301worldanyoneBOý“cz4 7ý“. /test-3/306worldanyoneBOý“cz/ 8ý“. /test-5/301worldanyoneBOý“c~6 9ý“0 /test-2/308worldanyoneBOý“c~: :ý“1 /test-0/312worldanyoneBOý“baU ;ý“3 /test-1/339worldanyoneBOý“baP <ý“3 /test-6/334worldanyoneBOý“cz0 =ý“6 /test-7/302worldanyoneBOý“c~, >ý“9 /test-4/298worldanyoneBOý“cŠ2 ?ý“: /test-8/304worldanyoneBOý“cŠ0 @ý“; /test-9/302worldanyoneBOý“c~7 Aý“< /test-2/309worldanyoneBOý“baQ Bý“= /test-6/335worldanyoneBOý“cz5 Cý“= /test-3/307worldanyoneBOý“cz0 Dý“> /test-5/302worldanyoneBOý“baV Eý“> /test-1/340worldanyoneBOý“c~; Fý“> /test-0/313worldanyoneBOý“cz1 Gý“@ /test-7/303worldanyoneBOý“c~- Hý“D /test-4/299worldanyoneBOý“cŠ3 Iý“E /test-8/305worldanyoneBOý“c~8 Jý“F /test-2/310worldanyoneBOý“cŠ1 Ký“H /test-9/303worldanyoneBOý“baR Lý“J /test-6/336worldanyoneBOý“baW Mý“K /test-1/341worldanyoneBOý“c~< Ný“L /test-0/314worldanyoneBOý“cz6 Oý“L /test-3/308worldanyoneBOý“cz2 Pý“L /test-7/304worldanyoneBOý“cz1 Qý“L /test-5/303worldanyoneBOý“cŠ4 Rý“O /test-8/306worldanyoneBOý“c~. Sý“O /test-4/300worldanyoneBOý“c~9 Tý“Q /test-2/311worldanyoneBOý“cŠ2 Uý“R /test-9/304worldanyoneBOý“baS Vý“T /test-6/337worldanyoneBOý“baX Wý“T /test-1/342worldanyoneBOý“c~= Xý“V /test-0/315worldanyoneBOý“cz7 Yý“W /test-3/309worldanyoneBOý“cz3 Zý“X /test-7/305worldanyoneBOý“cz2 [ý“Y /test-5/304worldanyoneBOý“cŠ5 \ý“Y /test-8/307worldanyoneBOý“c~/ ]ý“Z /test-4/301worldanyoneBOý“c~: ^ý“Z /test-2/312worldanyoneBOý“cŠ3 _ý“[ /test-9/305worldanyoneBOý“baT `ý“] /test-6/338worldanyoneBOý“baY aý“] /test-1/343worldanyoneBOý“c~> bý“` /test-0/316worldanyoneBOý“cz4 cý“b /test-7/306worldanyoneBOý“cz8 dý“c /test-3/310worldanyoneBOý“cz3 eý“e /test-5/305worldanyoneBOý“cŠ6 fý“f /test-8/308worldanyoneBOý“c~; gý“g /test-2/313worldanyoneBOý“c~0 hý“g /test-4/302worldanyoneBOý“cŠ4 iý“g /test-9/306worldanyoneBOý“baU jý“h /test-6/339worldanyoneBOý“baZ ký“h /test-1/344worldanyoneBOý“c~? lý“j /test-0/317worldanyoneBOý“cz5 mý“j /test-7/307worldanyoneBOý“cz9 ný“k /test-3/311worldanyoneBOý“cz4 oý“u /test-5/306worldanyoneBOý“ba[ pý“w /test-1/345worldanyoneBOý“baV qý“w /test-6/340worldanyoneBOý“cz: rý“w /test-3/312worldanyoneBOý“cŠ7 sý“x /test-8/309worldanyoneBOý“cŠ5 tý“x /test-9/307worldanyoneBOý“cz6 uý“x /test-7/308worldanyoneBOý“c~1 vý“y /test-4/303worldanyoneBOý“c~< wý“y /test-2/314worldanyoneBOý“c~@ xý“y /test-0/318worldanyoneBOý“cz5 yý“€ /test-5/307worldanyoneBOý“ba\ zý“ƒ /test-1/346worldanyoneBOý“baW {ý“ƒ /test-6/341worldanyoneBOý“cz; |ý“„ /test-3/313worldanyoneBOý“cŠ8 }ý“… /test-8/310worldanyoneBOý“c~= ~ý“… /test-2/315worldanyoneBOý“c~A ý“† /test-0/319worldanyoneBOý“cŠ6 €ý“‡ /test-9/308worldanyoneBOý“cz7 ý“‡ /test-7/309worldanyoneBOý“c~2 ‚ý“ˆ /test-4/304worldanyoneBOý“cz6 ƒý“‰ /test-5/308worldanyoneBOý“ba] „ý“Ž /test-1/347worldanyoneBOý“baX …ý“Ž /test-6/342worldanyoneBOý“cz< †ý“ /test-3/314worldanyoneBOý“cz7 ‡ý““ /test-5/309worldanyoneBOý“cz8 ˆý“” /test-7/310worldanyoneBOý“cŠ9 ‰ý“” /test-8/311worldanyoneBOý“c~> Šý“” /test-2/316worldanyoneBOý“c~B ‹ý“” /test-0/320worldanyoneBOý“c~3 Œý“” /test-4/305worldanyoneBOý“cŠ7 ý“• /test-9/309worldanyoneBOý“ba^ Žý“— /test-1/348worldanyoneBOý“baY ý“— /test-6/343worldanyoneBOý“cz= ý“˜ /test-3/315worldanyoneBOý“cz8 ‘ý“ /test-5/310worldanyoneBOý“c~? ’ý“¡ /test-2/317worldanyoneBOý“c~C “ý“¡ /test-0/321worldanyoneBOý“c~4 ”ý“¡ /test-4/306worldanyoneBOý“cŠ: •ý“¡ /test-8/312worldanyoneBOý“cŠ8 –ý“¡ /test-9/310worldanyoneBOý“cz9 —ý“¢ /test-7/311worldanyoneBOý“baZ ˜ý“¢ /test-6/344worldanyoneBOý“ba_ ™ý“£ /test-1/349worldanyoneBOý“cz> šý“¤ /test-3/316worldanyoneBOý“cz9 ›ý“§ /test-5/311worldanyoneBOý“ba` œý“® /test-1/350worldanyoneBOý“ba[ ý“¯ /test-6/345worldanyoneBOý“cŠ; žý“¯ /test-8/313worldanyoneBOý“cŠ9 Ÿý“¯ /test-9/311worldanyoneBOý“cz?  ý“¯ /test-3/317worldanyoneBOý“cz: ¡ý“° /test-7/312worldanyoneBOý“c~5 ¢ý“± /test-4/307worldanyoneBOý“c~@ £ý“± /test-2/318worldanyoneBOý“c~D ¤ý“± /test-0/322worldanyoneBOý“cz: ¥ý“± /test-5/312worldanyoneBOý“baa ¦ý“¹ /test-1/351worldanyoneBOý“cŠ< §ý“¼ /test-8/314worldanyoneBOý“cz; ¨ý“¾ /test-5/313worldanyoneBOý“c~A ©ý“¾ /test-2/319worldanyoneBOý“c~6 ªý“¾ /test-4/308worldanyoneBOý“ba\ «ý“¾ /test-6/346worldanyoneBOý“cz; ¬ý“¿ /test-7/313worldanyoneBOý“cz@ ­ý“¿ /test-3/318worldanyoneBOý“c~E ®ý“¿ /test-0/323worldanyoneBOý“cŠ: ¯ý“¿ /test-9/312worldanyoneBOý“bab °ý“ /test-1/352worldanyoneBOý“cŠ= ±ý“Æ /test-8/315worldanyoneBOý“ba] ²ý“È /test-6/347worldanyoneBOý“cz< ³ý“È /test-5/314worldanyoneBOý“c~B ´ý“Ê /test-2/320worldanyoneBOý“c~7 µý“Ê /test-4/309worldanyoneBOý“cŠ; ¶ý“Ë /test-9/313worldanyoneBOý“czA ·ý“Ë /test-3/319worldanyoneBOý“c~F ¸ý“Ë /test-0/324worldanyoneBOý“bac ¹ý“Ë /test-1/353worldanyoneBOý“cz< ºý“Ì /test-7/314worldanyoneBOý“cŠ> »ý“Ñ /test-8/316worldanyoneBOý“ba^ ¼ý“Ô /test-6/348worldanyoneBOý“cz= ½ý“Ô /test-5/315worldanyoneBOý“c~C ¾ý“Õ /test-2/321worldanyoneBOý“cŠ< ¿ý“× /test-9/314worldanyoneBOý“c~8 Àý“× /test-4/310worldanyoneBOý“c~G Áý“× /test-0/325worldanyoneBOý“czB Âý“× /test-3/320worldanyoneBOý“bad Ãý“× /test-1/354worldanyoneBOý“cz= Äý“Ø /test-7/315worldanyoneBOý“cŠ? Åý“Ú /test-8/317worldanyoneBOý“ba_ Æý“ß /test-6/349worldanyoneBOý“cz> Çý“à /test-5/316worldanyoneBOý“c~D Èý“â /test-2/322worldanyoneBOý“cz> Éý“ä /test-7/316worldanyoneBOý“bae Êý“ä /test-1/355worldanyoneBOý“cŠ= Ëý“å /test-9/315worldanyoneBOý“c~H Ìý“å /test-0/326worldanyoneBOý“c~9 Íý“å /test-4/311worldanyoneBOý“czC Îý“å /test-3/321worldanyoneBOý“cŠ@ Ïý“æ /test-8/318worldanyoneBOý“ba` Ðý“è /test-6/350worldanyoneBOý“cz? Ñý“ê /test-5/317worldanyoneBOý“c~E Òý“ë /test-2/323worldanyoneBOý“baf Óý“ï /test-1/356worldanyoneBOý“cz? Ôý“ð /test-7/317worldanyoneBOý“cŠ> Õý“ò /test-9/316worldanyoneBOý“c~: Öý“ò /test-4/312worldanyoneBOý“c~I ×ý“ò /test-0/327worldanyoneBOý“czD Øý“ò /test-3/322worldanyoneBOý“baa Ùý“ô /test-6/351worldanyoneBOý“cŠA Úý“ô /test-8/319worldanyoneBOý“cz@ Ûý“ö /test-5/318worldanyoneBOý“c~F Üý“ö /test-2/324worldanyoneBOý“bag Ýý“ù /test-1/357worldanyoneBOý“cz@ Þý“þ /test-7/318worldanyoneBOý“cŠ? ßý“€ /test-9/317worldanyoneBOý“c~J àý“€ /test-0/328worldanyoneBOý“czE áý“€ /test-3/323worldanyoneBOý“c~; âý“€ /test-4/313worldanyoneBOý“bab ãý“€ /test-6/352worldanyoneBOý“c~G äý“€ /test-2/325worldanyoneBOý“bah åý“€ /test-1/358worldanyoneBOý“czA æý“€ /test-5/319worldanyoneBOý“cŠB çý“€ /test-8/320worldanyoneBOý“czA èý“€  /test-7/319worldanyoneBOý“c~K éý“€  /test-0/329worldanyoneBOý“czF êý“€  /test-3/324worldanyoneBOý“c~< ëý“€ /test-4/314worldanyoneBOý“cŠ@ ìý“€ /test-9/318worldanyoneBOý“bac íý“€ /test-6/353worldanyoneBOý“c~H îý“€ /test-2/326worldanyoneBOý“bai ïý“€ /test-1/359worldanyoneBOý“czB ðý“€ /test-5/320worldanyoneBOý“cŠC ñý“€ /test-8/321worldanyoneBOý“czB òý“€ /test-7/320worldanyoneBOý“c~L óý“€ /test-0/330worldanyoneBOý“bad ôý“€ /test-6/354worldanyoneBOý“c~= õý“€ /test-4/315worldanyoneBOý“cŠA öý“€ /test-9/319worldanyoneBOý“baj ÷ý“€ /test-1/360worldanyoneBOý“czG øý“€ /test-3/325worldanyoneBOý“cŠD ùý“€ /test-8/322worldanyoneBOý“czC úý“€ /test-5/321worldanyoneBOý“c~I ûý“€ /test-2/327worldanyoneBOý“czC üý“€  /test-7/321worldanyoneBOý“c~M ýý“€  /test-0/331worldanyoneBOý“bae þý“€% /test-6/355worldanyoneBOý“c~> ÿý“€' /test-4/316worldanyoneBOý“bak ý“€* /test-1/361worldanyoneBOý“czH ý“€* /test-3/326worldanyoneBOý“cŠB ý“€, /test-9/320worldanyoneBOý“cŠE ý“€, /test-8/323worldanyoneBOý“c~J ý“€- /test-2/328worldanyoneBOý“c~N ý“€- /test-0/332worldanyoneBOý“czD ý“€. /test-7/322worldanyoneBOý“baf ý“€0 /test-6/356worldanyoneBOý“czD ý“€0 /test-5/322worldanyoneBOý“c~? ý“€1 /test-4/317worldanyoneBOý“bal ý“€8 /test-1/362worldanyoneBOý“cŠC ý“€; /test-9/321worldanyoneBOý“czE ý“€; /test-7/323worldanyoneBOý“czI ý“€< /test-3/327worldanyoneBOý“bag ý“€= /test-6/357worldanyoneBOý“c~K ý“€> /test-2/329worldanyoneBOý“c~O ý“€> /test-0/333worldanyoneBOý“cŠF ý“€> /test-8/324worldanyoneBOý“czE ý“€> /test-5/323worldanyoneBOý“c~@ ý“€> /test-4/318worldanyoneBOý“bam ý“€A /test-1/363worldanyoneBOý“cŠD ý“€F /test-9/322worldanyoneBOý“czJ ý“€G /test-3/328worldanyoneBOý“czF ý“€H /test-7/324worldanyoneBOý“bah ý“€I /test-6/358worldanyoneBOý“c~L ý“€K /test-2/330worldanyoneBOý“cŠG ý“€L /test-8/325worldanyoneBOý“ban ý“€M /test-1/364worldanyoneBOý“czF ý“€M /test-5/324worldanyoneBOý“c~A ý“€O /test-4/319worldanyoneBOý“c~P ý“€O /test-0/334worldanyoneBOý“cŠE ý“€P /test-9/323worldanyoneBOý“czK ý“€T /test-3/329worldanyoneBOý“czG !ý“€U /test-7/325worldanyoneBOý“bai "ý“€V /test-6/359worldanyoneBOý“cŠH #ý“€Z /test-8/326worldanyoneBOý“c~M $ý“€Z /test-2/331worldanyoneBOý“czG %ý“€[ /test-5/325worldanyoneBOý“bao &ý“€[ /test-1/365worldanyoneBOý“cŠF 'ý“€\ /test-9/324worldanyoneBOý“c~B (ý“€] /test-4/320worldanyoneBOý“c~Q )ý“€^ /test-0/335worldanyoneBOý“czH *ý“€a /test-7/326worldanyoneBOý“czL +ý“€b /test-3/330worldanyoneBOý“baj ,ý“€e /test-6/360worldanyoneBOý“cŠI -ý“€g /test-8/327worldanyoneBOý“cŠG .ý“€h /test-9/325worldanyoneBOý“bap /ý“€h /test-1/366worldanyoneBOý“c~N 0ý“€i /test-2/332worldanyoneBOý“c~C 1ý“€j /test-4/321worldanyoneBOý“czH 2ý“€k /test-5/326worldanyoneBOý“c~R 3ý“€n /test-0/336worldanyoneBOý“czI 4ý“€o /test-7/327worldanyoneBOý“bak 5ý“€q /test-6/361worldanyoneBOý“czM 6ý“€r /test-3/331worldanyoneBOý“c~O 7ý“€u /test-2/333worldanyoneBOý“cŠH 8ý“€v /test-9/326worldanyoneBOý“cŠJ 9ý“€w /test-8/328worldanyoneBOý“c~D :ý“€w /test-4/322worldanyoneBOý“baq ;ý“€x /test-1/367worldanyoneBOý“czI <ý“€x /test-5/327worldanyoneBOý“czJ =ý“€{ /test-7/328worldanyoneBOý“c~S >ý“€{ /test-0/337worldanyoneBOý“bal ?ý“€} /test-6/362worldanyoneBOý“czN @ý“€ /test-3/332worldanyoneBOý“c~P Aý“€€ /test-2/334worldanyoneBOý“c~E Bý“€ƒ /test-4/323worldanyoneBOý“cŠK Cý“€… /test-8/329worldanyoneBOý“cŠI Dý“€† /test-9/327worldanyoneBOý“czJ Eý“€† /test-5/328worldanyoneBOý“bar Fý“€† /test-1/368worldanyoneBOý“c~T Gý“€‹ /test-0/338worldanyoneBOý“bam Hý“€Œ /test-6/363worldanyoneBOý“czK Iý“€Œ /test-7/329worldanyoneBOý“c~Q Jý“€ /test-2/335worldanyoneBOý“czO Ký“€ /test-3/333worldanyoneBOý“c~F Lý“€ /test-4/324worldanyoneBOý“bas Mý“€‘ /test-1/369worldanyoneBOý“cŠJ Ný“€‘ /test-9/328worldanyoneBOý“czK Oý“€’ /test-5/329worldanyoneBOý“cŠL Pý“€’ /test-8/330worldanyoneBOý“ban Qý“€— /test-6/364worldanyoneBOý“czL Rý“€˜ /test-7/330worldanyoneBOý“c~U Sý“€˜ /test-0/339worldanyoneBOý“czP Tý“€˜ /test-3/334worldanyoneBOý“c~R Uý“€™ /test-2/336worldanyoneBOý“bat Vý“€œ /test-1/370worldanyoneBOý“cŠK Wý“€ /test-9/329worldanyoneBOý“cŠM Xý“€ /test-8/331worldanyoneBOý“czL Yý“€ /test-5/330worldanyoneBOý“c~G Zý“€ /test-4/325worldanyoneBOý“bao [ý“€¢ /test-6/365worldanyoneBOý“c~V \ý“€¢ /test-0/340worldanyoneBOý“czM ]ý“€£ /test-7/331worldanyoneBOý“c~S ^ý“€£ /test-2/337worldanyoneBOý“czQ _ý“€£ /test-3/335worldanyoneBOý“bau `ý“€§ /test-1/371worldanyoneBOý“czM aý“€§ /test-5/331worldanyoneBOý“cŠL bý“€¨ /test-9/330worldanyoneBOý“c~H cý“€¨ /test-4/326worldanyoneBOý“cŠN dý“€¨ /test-8/332worldanyoneBOý“bap eý“€¬ /test-6/366worldanyoneBOý“czN fý“€¬ /test-7/332worldanyoneBOý“czR gý“€­ /test-3/336worldanyoneBOý“c~W hý“€­ /test-0/341worldanyoneBOý“c~T iý“€­ /test-2/338worldanyoneBOý“bav jý“€² /test-1/372worldanyoneBOý“c~I ký“€² /test-4/327worldanyoneBOý“cŠM lý“€² /test-9/331worldanyoneBOý“cŠO mý“€² /test-8/333worldanyoneBOý“czN ný“€² /test-5/332worldanyoneBOý“baq oý“€¶ /test-6/367worldanyoneBOý“c~U pý“€¸ /test-2/339worldanyoneBOý“c~X qý“€¸ /test-0/342worldanyoneBOý“czS rý“€¸ /test-3/337worldanyoneBOý“czO sý“€¹ /test-7/333worldanyoneBOý“baw tý“€» /test-1/373worldanyoneBOý“c~J uý“€» /test-4/328worldanyoneBOý“cŠN vý“€½ /test-9/332worldanyoneBOý“cŠP wý“€½ /test-8/334worldanyoneBOý“czO xý“€½ /test-5/333worldanyoneBOý“bar yý“€¾ /test-6/368worldanyoneBOý“czT zý“€Ã /test-3/338worldanyoneBOý“c~Y {ý“€Ã /test-0/343worldanyoneBOý“c~V |ý“€Ä /test-2/340worldanyoneBOý“czP }ý“€Ä /test-7/334worldanyoneBOý“bax ~ý“€Æ /test-1/374worldanyoneBOý“cŠQ ý“€Æ /test-8/335worldanyoneBOý“c~K €ý“€Ç /test-4/329worldanyoneBOý“czP ý“€Ç /test-5/334worldanyoneBOý“cŠO ‚ý“€Ç /test-9/333worldanyoneBOý“bas ƒý“€Ç /test-6/369worldanyoneBOý“czU „ý“€Ì /test-3/339worldanyoneBOý“c~W …ý“€Í /test-2/341worldanyoneBOý“c~Z †ý“€Í /test-0/344worldanyoneBOý“czQ ‡ý“€Î /test-7/335worldanyoneBOý“bay ˆý“€Ñ /test-1/375worldanyoneBOý“czQ ‰ý“€Ò /test-5/335worldanyoneBOý“c~L Šý“€Ò /test-4/330worldanyoneBOý“cŠR ‹ý“€Ò /test-8/336worldanyoneBOý“bat Œý“€Ò /test-6/370worldanyoneBOý“cŠP ý“€Ó /test-9/334worldanyoneBOý“czV Žý“€Õ /test-3/340worldanyoneBOý“czR ý“€Ø /test-7/336worldanyoneBOý“c~X ý“€Ø /test-2/342worldanyoneBOý“c~[ ‘ý“€Ù /test-0/345worldanyoneBOý“baz ’ý“€Ý /test-1/376worldanyoneBOý“bau “ý“€Ý /test-6/371worldanyoneBOý“c~M ”ý“€Þ /test-4/331worldanyoneBOý“czR •ý“€Þ /test-5/336worldanyoneBOý“cŠQ –ý“€Þ /test-9/335worldanyoneBOý“cŠS —ý“€Þ /test-8/337worldanyoneBOý“czW ˜ý“€ß /test-3/341worldanyoneBOý“c~Y ™ý“€â /test-2/343worldanyoneBOý“c~\ šý“€ã /test-0/346worldanyoneBOý“czS ›ý“€ã /test-7/337worldanyoneBOý“bav œý“€ç /test-6/372worldanyoneBOý“ba{ ý“€ç /test-1/377worldanyoneBOý“c~N žý“€è /test-4/332worldanyoneBOý“czS Ÿý“€è /test-5/337worldanyoneBOý“czX  ý“€é /test-3/342worldanyoneBOý“cŠT ¡ý“€é /test-8/338worldanyoneBOý“cŠR ¢ý“€ê /test-9/336worldanyoneBOý“czT £ý“€ì /test-7/338worldanyoneBOý“c~Z ¤ý“€ì /test-2/344worldanyoneBOý“c~] ¥ý“€ì /test-0/347worldanyoneBOý“baw ¦ý“€ñ /test-6/373worldanyoneBOý“ba| §ý“€ñ /test-1/378worldanyoneBOý“c~O ¨ý“€ò /test-4/333worldanyoneBOý“czT ©ý“€ò /test-5/338worldanyoneBOý“cŠS ªý“€ô /test-9/337worldanyoneBOý“czY «ý“€ô /test-3/343worldanyoneBOý“cŠU ¬ý“€ô /test-8/339worldanyoneBOý“czU ­ý“€ö /test-7/339worldanyoneBOý“c~^ ®ý“€ö /test-0/348worldanyoneBOý“c~[ ¯ý“€÷ /test-2/345worldanyoneBOý“bax °ý“€ú /test-6/374worldanyoneBOý“ba} ±ý“€ú /test-1/379worldanyoneBOý“c~P ²ý“€ü /test-4/334worldanyoneBOý“czU ³ý“€ü /test-5/339worldanyoneBOý“cŠT ´ý“€ÿ /test-9/338worldanyoneBOý“czZ µý“€ÿ /test-3/344worldanyoneBOý“cŠV ¶ý“€ÿ /test-8/340worldanyoneBOý“czV ·ý“ /test-7/340worldanyoneBOý“c~_ ¸ý“ /test-0/349worldanyoneBOý“c~\ ¹ý“ /test-2/346worldanyoneBOý“ba~ ºý“ /test-1/380worldanyoneBOý“bay »ý“ /test-6/375worldanyoneBOý“c~Q ¼ý“ /test-4/335worldanyoneBOý“czV ½ý“ /test-5/340worldanyoneBOý“cz[ ¾ý“  /test-3/345worldanyoneBOý“cŠU ¿ý“  /test-9/339worldanyoneBOý“cŠW Àý“  /test-8/341worldanyoneBOý“czW Áý“  /test-7/341worldanyoneBOý“c~] Âý“  /test-2/347worldanyoneBOý“c~` Ãý“  /test-0/350worldanyoneBOý“baz Äý“ /test-6/376worldanyoneBOý“ba Åý“ /test-1/381worldanyoneBOý“c~R Æý“ /test-4/336worldanyoneBOý“czW Çý“ /test-5/341worldanyoneBOý“cz\ Èý“ /test-3/346worldanyoneBOý“cŠV Éý“ /test-9/340worldanyoneBOý“czX Êý“ /test-7/342worldanyoneBOý“cŠX Ëý“ /test-8/342worldanyoneBOý“c~a Ìý“ /test-0/351worldanyoneBOý“c~^ Íý“ /test-2/348worldanyoneBOý“ba{ Îý“ /test-6/377worldanyoneBOý“ba€ Ïý“‚ /test-1/382worldanyoneBOý“czX Ðý“‚ /test-5/342worldanyoneBOý“cŠW Ñý“‚  /test-9/341worldanyoneBOý“c~S Òý“‚  /test-4/337worldanyoneBOý“c~b Óý“‚  /test-0/352worldanyoneBOý“c~_ Ôý“‚  /test-2/349worldanyoneBOý“cŠY Õý“‚  /test-8/343worldanyoneBOý“czY Öý“‚  /test-7/343worldanyoneBOý“cz] ×ý“‚  /test-3/347worldanyoneBOý“ba| Øý“‚ /test-6/378worldanyoneBOý“c~T Ùý“‚ /test-4/338worldanyoneBOý“cŠX Úý“‚ /test-9/342worldanyoneBOý“c~c Ûý“‚ /test-0/353worldanyoneBOý“ba Üý“‚ /test-1/383worldanyoneBOý“c~` Ýý“‚ /test-2/350worldanyoneBOý“czY Þý“‚ /test-5/343worldanyoneBOý“ba} ßý“‚ /test-6/379worldanyoneBOý“cŠZ àý“‚ /test-8/344worldanyoneBOý“cz^ áý“‚ /test-3/348worldanyoneBOý“czZ âý“‚ /test-7/344worldanyoneBOý“cŠY ãý“‚  /test-9/343worldanyoneBOý“c~U äý“‚  /test-4/339worldanyoneBOý“c~d åý“‚! /test-0/354worldanyoneBOý“czZ æý“‚! /test-5/344worldanyoneBOý“c~a çý“‚! /test-2/351worldanyoneBOý“ba‚ èý“‚" /test-1/384worldanyoneBOý“ba~ éý“‚" /test-6/380worldanyoneBOý“cŠ[ êý“‚& /test-8/345worldanyoneBOý“cz_ ëý“‚& /test-3/349worldanyoneBOý“cz[ ìý“‚' /test-7/345worldanyoneBOý“baƒ íý“‚, /test-1/385worldanyoneBOý“ba îý“‚- /test-6/381worldanyoneBOý“c~b ïý“‚- /test-2/352worldanyoneBOý“cz[ ðý“‚- /test-5/345worldanyoneBOý“c~V ñý“‚- /test-4/340worldanyoneBOý“cŠZ òý“‚- /test-9/344worldanyoneBOý“c~e óý“‚- /test-0/355worldanyoneBOý“cŠ\ ôý“‚1 /test-8/346worldanyoneBOý“cz` õý“‚1 /test-3/350worldanyoneBOý“cz\ öý“‚1 /test-7/346worldanyoneBOý“ba„ ÷ý“‚7 /test-1/386worldanyoneBOý“cz\ øý“‚7 /test-5/346worldanyoneBOý“cŠ[ ùý“‚7 /test-9/345worldanyoneBOý“ba€ úý“‚8 /test-6/382worldanyoneBOý“c~W ûý“‚8 /test-4/341worldanyoneBOý“c~c üý“‚9 /test-2/353worldanyoneBOý“c~f ýý“‚9 /test-0/356worldanyoneBOý“cza þý“‚: /test-3/351worldanyoneBOý“cŠ] ÿý“‚; /test-8/347worldanyoneBOý“cz]ý“‚< /test-7/347worldanyoneBOý“ba…ý“‚@ /test-1/387worldanyoneBOý“cz]ý“‚A /test-5/347worldanyoneBOý“cŠ\ý“‚A /test-9/346worldanyoneBOý“baý“‚B /test-6/383worldanyoneBOý“c~Xý“‚C /test-4/342worldanyoneBOý“czbý“‚C /test-3/352worldanyoneBOý“c~dý“‚C /test-2/354worldanyoneBOý“c~gý“‚D /test-0/357worldanyoneBOý“cz^ ý“‚D /test-7/348worldanyoneBOý“cŠ^ ý“‚D /test-8/348worldanyoneBOý“ba† ý“‚H /test-1/388worldanyoneBOý“ba‚ ý“‚L /test-6/384worldanyoneBOý“cŠ] ý“‚L /test-9/347worldanyoneBOý“cz^ý“‚L /test-5/348worldanyoneBOý“c~Yý“‚N /test-4/343worldanyoneBOý“c~eý“‚O /test-2/355worldanyoneBOý“c~hý“‚P /test-0/358worldanyoneBOý“cŠ_ý“‚P /test-8/349worldanyoneBOý“czcý“‚P /test-3/353worldanyoneBOý“cz_ý“‚P /test-7/349worldanyoneBOý“ba‡ý“‚Q /test-1/389worldanyoneBOý“baƒý“‚W /test-6/385worldanyoneBOý“cz_ý“‚W /test-5/349worldanyoneBOý“cŠ^ý“‚W /test-9/348worldanyoneBOý“c~Zý“‚X /test-4/344worldanyoneBOý“cŠ`ý“‚Z /test-8/350worldanyoneBOý“cz`ý“‚Z /test-7/350worldanyoneBOý“baˆý“‚[ /test-1/390worldanyoneBOý“c~iý“‚[ /test-0/359worldanyoneBOý“czdý“‚[ /test-3/354worldanyoneBOý“c~fý“‚\ /test-2/356worldanyoneBOý“ba„ ý“‚` /test-6/386worldanyoneBOý“cz`!ý“‚` /test-5/350worldanyoneBOý“cŠ_"ý“‚a /test-9/349worldanyoneBOý“c~[#ý“‚a /test-4/345worldanyoneBOý“ba‰$ý“‚e /test-1/391worldanyoneBOý“cŠa%ý“‚e /test-8/351worldanyoneBOý“cze&ý“‚f /test-3/355worldanyoneBOý“c~j'ý“‚f /test-0/360worldanyoneBOý“cza(ý“‚f /test-7/351worldanyoneBOý“c~g)ý“‚g /test-2/357worldanyoneBOý“ba…*ý“‚i /test-6/387worldanyoneBOý“c~\+ý“‚j /test-4/346worldanyoneBOý“cza,ý“‚j /test-5/351worldanyoneBOý“cŠ`-ý“‚j /test-9/350worldanyoneBOý“baŠ.ý“‚n /test-1/392worldanyoneBOý“cŠb/ý“‚o /test-8/352worldanyoneBOý“c~h0ý“‚r /test-2/358worldanyoneBOý“czf1ý“‚r /test-3/356worldanyoneBOý“c~k2ý“‚s /test-0/361worldanyoneBOý“czb3ý“‚s /test-7/352worldanyoneBOý“cŠa4ý“‚u /test-9/351worldanyoneBOý“czb5ý“‚v /test-5/352worldanyoneBOý“c~]6ý“‚v /test-4/347worldanyoneBOý“ba†7ý“‚v /test-6/388worldanyoneBOý“ba‹8ý“‚w /test-1/393worldanyoneBOý“cŠc9ý“‚w /test-8/353worldanyoneBOý“czc:ý“‚| /test-7/353worldanyoneBOý“c~i;ý“‚| /test-2/359worldanyoneBOý“czg<ý“‚| /test-3/357worldanyoneBOý“c~l=ý“‚} /test-0/362worldanyoneBOý“c~^>ý“‚ /test-4/348worldanyoneBOý“czc?ý“‚€ /test-5/353worldanyoneBOý“ba‡@ý“‚€ /test-6/389worldanyoneBOý“cŠbAý“‚€ /test-9/352worldanyoneBOý“baŒBý“‚ /test-1/394worldanyoneBOý“cŠdCý“‚ /test-8/354worldanyoneBOý“czhDý“‚† /test-3/358worldanyoneBOý“czdEý“‚‡ /test-7/354worldanyoneBOý“c~mFý“‚‡ /test-0/363worldanyoneBOý“c~jGý“‚‡ /test-2/360worldanyoneBOý“c~_Hý“‚Š /test-4/349worldanyoneBOý“baIý“‚Œ /test-1/395worldanyoneBOý“baˆJý“‚Œ /test-6/390worldanyoneBOý“cŠeKý“‚Œ /test-8/355worldanyoneBOý“cŠcLý“‚Œ /test-9/353worldanyoneBOý“czdMý“‚ /test-5/354worldanyoneBOý“cziNý“‚ /test-3/359worldanyoneBOý“c~kOý“‚ /test-2/361worldanyoneBOý“c~nPý“‚‘ /test-0/364worldanyoneBOý“czeQý“‚‘ /test-7/355worldanyoneBOý“c~`Rý“‚“ /test-4/350worldanyoneBOý“ba‰Sý“‚— /test-6/391worldanyoneBOý“baŽTý“‚— /test-1/396worldanyoneBOý“cŠfUý“‚— /test-8/356worldanyoneBOý“cŠdVý“‚˜ /test-9/354worldanyoneBOý“czeWý“‚˜ /test-5/355worldanyoneBOý“c~oXý“‚› /test-0/365worldanyoneBOý“c~lYý“‚› /test-2/362worldanyoneBOý“czjZý“‚œ /test-3/360worldanyoneBOý“czf[ý“‚œ /test-7/356worldanyoneBOý“c~a\ý“‚œ /test-4/351worldanyoneBOý“baŠ]ý“‚¡ /test-6/392worldanyoneBOý“ba^ý“‚¡ /test-1/397worldanyoneBOý“cŠg_ý“‚¢ /test-8/357worldanyoneBOý“cŠe`ý“‚£ /test-9/355worldanyoneBOý“czfaý“‚£ /test-5/356worldanyoneBOý“c~mbý“‚¦ /test-2/363worldanyoneBOý“c~pcý“‚¦ /test-0/366worldanyoneBOý“czgdý“‚¦ /test-7/357worldanyoneBOý“czkeý“‚§ /test-3/361worldanyoneBOý“c~bfý“‚§ /test-4/352worldanyoneBOý“bagý“‚ª /test-1/398worldanyoneBOý“ba‹hý“‚ª /test-6/393worldanyoneBOý“cŠhiý“‚¬ /test-8/358worldanyoneBOý“czgjý“‚¬ /test-5/357worldanyoneBOý“cŠfký“‚¬ /test-9/356worldanyoneBOý“c~nlý“‚° /test-2/364worldanyoneBOý“czlmý“‚± /test-3/362worldanyoneBOý“c~qný“‚± /test-0/367worldanyoneBOý“c~coý“‚± /test-4/353worldanyoneBOý“czhpý“‚² /test-7/358worldanyoneBOý“baŒqý“‚´ /test-6/394worldanyoneBOý“ba‘rý“‚´ /test-1/399worldanyoneBOý“czhsý“‚¶ /test-5/358worldanyoneBOý“cŠitý“‚¶ /test-8/359worldanyoneBOý“cŠguý“‚¶ /test-9/357worldanyoneBOý“c~ový“‚¹ /test-2/365worldanyoneBOý“ba’wý“‚¾ /test-1/400worldanyoneBOý“baxý“‚¾ /test-6/395worldanyoneBOý“c~ryý“‚¾ /test-0/368worldanyoneBOý“c~dzý“‚¿ /test-4/354worldanyoneBOý“czi{ý“‚¿ /test-7/359worldanyoneBOý“czm|ý“‚¿ /test-3/363worldanyoneBOý“c~p}ý“‚Á /test-2/366worldanyoneBOý“czi~ý“‚ /test-5/359worldanyoneBOý“cŠjý“‚ /test-8/360worldanyoneBOý“cŠh€ý“‚ /test-9/358worldanyoneBOý“ba“ý“‚È /test-1/401worldanyoneBOý“baŽ‚ý“‚É /test-6/396worldanyoneBOý“czjƒý“‚Ê /test-7/360worldanyoneBOý“c~s„ý“‚Ê /test-0/369worldanyoneBOý“czn…ý“‚Ë /test-3/364worldanyoneBOý“c~e†ý“‚Ë /test-4/355worldanyoneBOý“c~q‡ý“‚Ì /test-2/367worldanyoneBOý“cŠkˆý“‚Ì /test-8/361worldanyoneBOý“cŠi‰ý“‚Í /test-9/359worldanyoneBOý“czjŠý“‚Í /test-5/360worldanyoneBOý“ba”‹ý“‚Ð /test-1/402worldanyoneBOý“baŒý“‚Ó /test-6/397worldanyoneBOý“c~tý“‚Ô /test-0/370worldanyoneBOý“czkŽý“‚Ô /test-7/361worldanyoneBOý“czoý“‚Õ /test-3/365worldanyoneBOý“c~fý“‚Õ /test-4/356worldanyoneBOý“c~r‘ý“‚× /test-2/368worldanyoneBOý“czk’ý“‚Ø /test-5/361worldanyoneBOý“cŠl“ý“‚Ø /test-8/362worldanyoneBOý“cŠj”ý“‚Ù /test-9/360worldanyoneBOý“ba••ý“‚Ù /test-1/403worldanyoneBOý“ba–ý“‚Ü /test-6/398worldanyoneBOý“c~u—ý“‚ß /test-0/371worldanyoneBOý“czp˜ý“‚ß /test-3/366worldanyoneBOý“czl™ý“‚ß /test-7/362worldanyoneBOý“c~gšý“‚à /test-4/357worldanyoneBOý“czl›ý“‚â /test-5/362worldanyoneBOý“c~sœý“‚ã /test-2/369worldanyoneBOý“cŠmý“‚ã /test-8/363worldanyoneBOý“ba–žý“‚ã /test-1/404worldanyoneBOý“cŠkŸý“‚ä /test-9/361worldanyoneBOý“ba‘ ý“‚å /test-6/399worldanyoneBOý“c~v¡ý“‚è /test-0/372worldanyoneBOý“c~h¢ý“‚é /test-4/358worldanyoneBOý“czq£ý“‚é /test-3/367worldanyoneBOý“czm¤ý“‚ê /test-7/363worldanyoneBOý“c~t¥ý“‚í /test-2/370worldanyoneBOý“czm¦ý“‚í /test-5/363worldanyoneBOý“ba—§ý“‚î /test-1/405worldanyoneBOý“cŠn¨ý“‚î /test-8/364worldanyoneBOý“cŠl©ý“‚î /test-9/362worldanyoneBOý“ba’ªý“‚ï /test-6/400worldanyoneBOý“c~w«ý“‚ñ /test-0/373worldanyoneBOý“c~i¬ý“‚ó /test-4/359worldanyoneBOý“czr­ý“‚ó /test-3/368worldanyoneBOý“czn®ý“‚ó /test-7/364worldanyoneBOý“ba“¯ý“‚ø /test-6/401worldanyoneBOý“ba˜°ý“‚ø /test-1/406worldanyoneBOý“cŠm±ý“‚ø /test-9/363worldanyoneBOý“cŠo²ý“‚ø /test-8/365worldanyoneBOý“c~u³ý“‚ù /test-2/371worldanyoneBOý“czn´ý“‚ù /test-5/364worldanyoneBOý“c~xµý“‚û /test-0/374worldanyoneBOý“c~j¶ý“‚ý /test-4/360worldanyoneBOý“czo·ý“‚ý /test-7/365worldanyoneBOý“czs¸ý“‚ý /test-3/369worldanyoneBOý“ba”¹ý“ƒ /test-6/402worldanyoneBOý“cŠnºý“ƒ /test-9/364worldanyoneBOý“cŠp»ý“ƒ /test-8/366worldanyoneBOý“ba™¼ý“ƒ /test-1/407worldanyoneBOý“c~v½ý“ƒ /test-2/372worldanyoneBOý“czo¾ý“ƒ /test-5/365worldanyoneBOý“c~y¿ý“ƒ /test-0/375worldanyoneBOý“czpÀý“ƒ /test-7/366worldanyoneBOý“cztÁý“ƒ /test-3/370worldanyoneBOý“c~kÂý“ƒ /test-4/361worldanyoneBOý“ba•Ãý“ƒ  /test-6/403worldanyoneBOý“cŠoÄý“ƒ  /test-9/365worldanyoneBOý“c~wÅý“ƒ /test-2/373worldanyoneBOý“c~zÆý“ƒ /test-0/376worldanyoneBOý“bašÇý“ƒ /test-1/408worldanyoneBOý“cŠqÈý“ƒ /test-8/367worldanyoneBOý“czpÉý“ƒ /test-5/366worldanyoneBOý“c~lÊý“ƒ /test-4/362worldanyoneBOý“czqËý“ƒ /test-7/367worldanyoneBOý“czuÌý“ƒ /test-3/371worldanyoneBOý“ba–Íý“ƒ /test-6/404worldanyoneBOý“cŠpÎý“ƒ /test-9/366worldanyoneBOý“ba›Ïý“ƒ /test-1/409worldanyoneBOý“c~xÐý“ƒ /test-2/374worldanyoneBOý“c~{Ñý“ƒ /test-0/377worldanyoneBOý“czqÒý“ƒ /test-5/367worldanyoneBOý“cŠrÓý“ƒ /test-8/368worldanyoneBOý“czvÔý“ƒ /test-3/372worldanyoneBOý“c~mÕý“ƒ /test-4/363worldanyoneBOý“czrÖý“ƒ /test-7/368worldanyoneBOý“ba—×ý“ƒ /test-6/405worldanyoneBOý“cŠqØý“ƒ /test-9/367worldanyoneBOý“baœÙý“ƒ# /test-1/410worldanyoneBOý“c~yÚý“ƒ# /test-2/375worldanyoneBOý“c~|Ûý“ƒ# /test-0/378worldanyoneBOý“czrÜý“ƒ$ /test-5/368worldanyoneBOý“cŠsÝý“ƒ$ /test-8/369worldanyoneBOý“czwÞý“ƒ% /test-3/373worldanyoneBOý“czsßý“ƒ% /test-7/369worldanyoneBOý“c~nàý“ƒ& /test-4/364worldanyoneBOý“ba˜áý“ƒ& /test-6/406worldanyoneBOý“cŠrâý“ƒ' /test-9/368worldanyoneBOý“baãý“ƒ, /test-1/411worldanyoneBOý“ba™äý“ƒ0 /test-6/407worldanyoneBOý“cŠtåý“ƒ0 /test-8/370worldanyoneBOý“czxæý“ƒ1 /test-3/374worldanyoneBOý“cztçý“ƒ1 /test-7/370worldanyoneBOý“c~zèý“ƒ1 /test-2/376worldanyoneBOý“cŠséý“ƒ1 /test-9/369worldanyoneBOý“czsêý“ƒ1 /test-5/369worldanyoneBOý“c~}ëý“ƒ2 /test-0/379worldanyoneBOý“c~oìý“ƒ2 /test-4/365worldanyoneBOý“bažíý“ƒ5 /test-1/412worldanyoneBOý“bašîý“ƒ; /test-6/408worldanyoneBOý“czyïý“ƒ< /test-3/375worldanyoneBOý“cŠuðý“ƒ= /test-8/371worldanyoneBOý“cŠtñý“ƒ= /test-9/370worldanyoneBOý“c~{òý“ƒ> /test-2/377worldanyoneBOý“czuóý“ƒ> /test-7/371worldanyoneBOý“baŸôý“ƒ> /test-1/413worldanyoneBOý“c~~õý“ƒ? /test-0/380worldanyoneBOý“c~pöý“ƒ? /test-4/366worldanyoneBOý“czt÷ý“ƒ? /test-5/370worldanyoneBOý“ba›øý“ƒC /test-6/409worldanyoneBOý“czzùý“ƒF /test-3/376worldanyoneBOý“ba úý“ƒI /test-1/414worldanyoneBOý“cŠvûý“ƒI /test-8/372worldanyoneBOý“czvüý“ƒI /test-7/372worldanyoneBOý“czuýý“ƒI /test-5/371worldanyoneBOý“c~þý“ƒJ /test-0/381worldanyoneBOý“cŠuÿý“ƒJ /test-9/371worldanyoneBOý“c~|ý“ƒK /test-2/378worldanyoneBOý“c~qý“ƒK /test-4/367worldanyoneBOý“baœý“ƒL /test-6/410worldanyoneBOý“cz{ý“ƒO /test-3/377worldanyoneBOý“ba¡ý“ƒR /test-1/415worldanyoneBOý“cŠwý“ƒU /test-8/373worldanyoneBOý“czwý“ƒU /test-7/373worldanyoneBOý“czvý“ƒU /test-5/372worldanyoneBOý“baý“ƒV /test-6/411worldanyoneBOý“cŠv ý“ƒV /test-9/372worldanyoneBOý“c~} ý“ƒW /test-2/379worldanyoneBOý“c~€ ý“ƒW /test-0/382worldanyoneBOý“c~r ý“ƒW /test-4/368worldanyoneBOý“cz| ý“ƒX /test-3/378worldanyoneBOý“ba¢ý“ƒZ /test-1/416worldanyoneBOý“cŠxý“ƒa /test-8/374worldanyoneBOý“czwý“ƒa /test-5/373worldanyoneBOý“czxý“ƒa /test-7/374worldanyoneBOý“bažý“ƒb /test-6/412worldanyoneBOý“c~ý“ƒb /test-0/383worldanyoneBOý“cŠwý“ƒb /test-9/373worldanyoneBOý“cz}ý“ƒc /test-3/379worldanyoneBOý“c~~ý“ƒc /test-2/380worldanyoneBOý“c~sý“ƒd /test-4/369worldanyoneBOý“ba£ý“ƒd /test-1/417worldanyoneBOý“cŠyý“ƒj /test-8/375worldanyoneBOý“czyý“ƒn /test-7/375worldanyoneBOý“czxý“ƒn /test-5/374worldanyoneBOý“cz~ý“ƒn /test-3/380worldanyoneBOý“baŸý“ƒo /test-6/413worldanyoneBOý“ba¤ý“ƒo /test-1/418worldanyoneBOý“cŠxý“ƒp /test-9/374worldanyoneBOý“c~t ý“ƒp /test-4/370worldanyoneBOý“c~!ý“ƒp /test-2/381worldanyoneBOý“c~‚"ý“ƒp /test-0/384worldanyoneBOý“cŠz#ý“ƒt /test-8/376worldanyoneBOý“czz$ý“ƒy /test-7/376worldanyoneBOý“ba %ý“ƒz /test-6/414worldanyoneBOý“c~ƒ&ý“ƒ{ /test-0/385worldanyoneBOý“cz'ý“ƒ{ /test-3/381worldanyoneBOý“czy(ý“ƒ{ /test-5/375worldanyoneBOý“ba¥)ý“ƒ| /test-1/419worldanyoneBOý“cŠy*ý“ƒ} /test-9/375worldanyoneBOý“cŠ{+ý“ƒ} /test-8/377worldanyoneBOý“c~u,ý“ƒ~ /test-4/371worldanyoneBOý“c~€-ý“ƒ~ /test-2/382worldanyoneBOý“cz{.ý“ƒ‚ /test-7/377worldanyoneBOý“ba¡/ý“ƒ† /test-6/415worldanyoneBOý“c~„0ý“ƒ‡ /test-0/386worldanyoneBOý“cŠz1ý“ƒ‡ /test-9/376worldanyoneBOý“ba¦2ý“ƒˆ /test-1/420worldanyoneBOý“c~v3ý“ƒˆ /test-4/372worldanyoneBOý“c~4ý“ƒˆ /test-2/383worldanyoneBOý“cŠ|5ý“ƒ‰ /test-8/378worldanyoneBOý“cz€6ý“ƒŠ /test-3/382worldanyoneBOý“czz7ý“ƒŠ /test-5/376worldanyoneBOý“cz|8ý“ƒŒ /test-7/378worldanyoneBOý“ba¢9ý“ƒ‘ /test-6/416worldanyoneBOý“c~…:ý“ƒ’ /test-0/387worldanyoneBOý“cŠ{;ý“ƒ“ /test-9/377worldanyoneBOý“ba§<ý“ƒ” /test-1/421worldanyoneBOý“c~w=ý“ƒ” /test-4/373worldanyoneBOý“c~‚>ý“ƒ• /test-2/384worldanyoneBOý“cŠ}?ý“ƒ• /test-8/379worldanyoneBOý“cz@ý“ƒ• /test-3/383worldanyoneBOý“cz{Aý“ƒ– /test-5/377worldanyoneBOý“cz}Bý“ƒ– /test-7/379worldanyoneBOý“ba£Cý“ƒ™ /test-6/417worldanyoneBOý“c~†Dý“ƒ› /test-0/388worldanyoneBOý“ba¨Eý“ƒž /test-1/422worldanyoneBOý“cŠ|Fý“ƒŸ /test-9/378worldanyoneBOý“c~xGý“ƒŸ /test-4/374worldanyoneBOý“c~ƒHý“ƒ  /test-2/385worldanyoneBOý“cz~Iý“ƒ  /test-7/380worldanyoneBOý“cz‚Jý“ƒ¡ /test-3/384worldanyoneBOý“cz|Ký“ƒ¡ /test-5/378worldanyoneBOý“cŠ~Lý“ƒ¡ /test-8/380worldanyoneBOý“ba¤Mý“ƒ¢ /test-6/418worldanyoneBOý“c~‡Ný“ƒ¤ /test-0/389worldanyoneBOý“ba©Oý“ƒ¨ /test-1/423worldanyoneBOý“cŠ}Pý“ƒ¨ /test-9/379worldanyoneBOý“ba¥Qý“ƒ¬ /test-6/419worldanyoneBOý“c~yRý“ƒ¬ /test-4/375worldanyoneBOý“c~„Sý“ƒ¬ /test-2/386worldanyoneBOý“czTý“ƒ¬ /test-7/381worldanyoneBOý“cŠUý“ƒ­ /test-8/381worldanyoneBOý“czƒVý“ƒ­ /test-3/385worldanyoneBOý“c~ˆWý“ƒ­ /test-0/390worldanyoneBOý“cz}Xý“ƒ® /test-5/379worldanyoneBOý“cŠ~Yý“ƒ± /test-9/380worldanyoneBOý“baªZý“ƒ± /test-1/424worldanyoneBOý“ba¦[ý“ƒ· /test-6/420worldanyoneBOý“c~z\ý“ƒ¹ /test-4/376worldanyoneBOý“cz€]ý“ƒ¹ /test-7/382worldanyoneBOý“c~…^ý“ƒ¹ /test-2/387worldanyoneBOý“c~‰_ý“ƒº /test-0/391worldanyoneBOý“cŠ€`ý“ƒº /test-8/382worldanyoneBOý“cz~aý“ƒº /test-5/380worldanyoneBOý“cz„bý“ƒº /test-3/386worldanyoneBOý“cŠcý“ƒ» /test-9/381worldanyoneBOý“ba«dý“ƒ» /test-1/425worldanyoneBOý“ba§eý“ƒ¿ /test-6/421worldanyoneBOý“czfý“ƒÄ /test-7/383worldanyoneBOý“ba¬gý“ƒÅ /test-1/426worldanyoneBOý“cz…hý“ƒÅ /test-3/387worldanyoneBOý“c~Šiý“ƒÆ /test-0/392worldanyoneBOý“c~†jý“ƒÆ /test-2/388worldanyoneBOý“c~{ký“ƒÆ /test-4/377worldanyoneBOý“cŠlý“ƒÆ /test-8/383worldanyoneBOý“cŠ€mý“ƒÆ /test-9/382worldanyoneBOý“czný“ƒÇ /test-5/381worldanyoneBOý“ba¨oý“ƒÈ /test-6/422worldanyoneBOý“cz‚pý“ƒÎ /test-7/384worldanyoneBOý“ba©qý“ƒÒ /test-6/423worldanyoneBOý“ba­rý“ƒÓ /test-1/427worldanyoneBOý“cŠsý“ƒÓ /test-9/383worldanyoneBOý“cŠ‚tý“ƒÓ /test-8/384worldanyoneBOý“cz†uý“ƒÓ /test-3/388worldanyoneBOý“c~‡vý“ƒÓ /test-2/389worldanyoneBOý“cz€wý“ƒÓ /test-5/382worldanyoneBOý“c~|xý“ƒÓ /test-4/378worldanyoneBOý“c~‹yý“ƒÓ /test-0/393worldanyoneBOý“czƒzý“ƒÖ /test-7/385worldanyoneBOý“baª{ý“ƒÜ /test-6/424worldanyoneBOý“ba®|ý“ƒÞ /test-1/428worldanyoneBOý“cz‡}ý“ƒÞ /test-3/389worldanyoneBOý“cŠ‚~ý“ƒÞ /test-9/384worldanyoneBOý“cŠƒý“ƒÞ /test-8/385worldanyoneBOý“c~ˆ€ý“ƒß /test-2/390worldanyoneBOý“czý“ƒß /test-5/383worldanyoneBOý“cz„‚ý“ƒà /test-7/386worldanyoneBOý“c~Œƒý“ƒà /test-0/394worldanyoneBOý“c~}„ý“ƒà /test-4/379worldanyoneBOý“ba«…ý“ƒå /test-6/425worldanyoneBOý“ba¯†ý“ƒë /test-1/429worldanyoneBOý“cŠƒ‡ý“ƒë /test-9/385worldanyoneBOý“cŠ„ˆý“ƒë /test-8/386worldanyoneBOý“czˆ‰ý“ƒì /test-3/390worldanyoneBOý“cz…Šý“ƒì /test-7/387worldanyoneBOý“cz‚‹ý“ƒì /test-5/384worldanyoneBOý“c~‰Œý“ƒì /test-2/391worldanyoneBOý“c~ý“ƒì /test-0/395worldanyoneBOý“c~~Žý“ƒí /test-4/380worldanyoneBOý“ba¬ý“ƒí /test-6/426worldanyoneBOý“ba°ý“ƒö /test-1/430worldanyoneBOý“ba­‘ý“ƒ÷ /test-6/427worldanyoneBOý“cŠ„’ý“ƒ÷ /test-9/386worldanyoneBOý“cŠ…“ý“ƒ÷ /test-8/387worldanyoneBOý“cz‰”ý“ƒø /test-3/391worldanyoneBOý“c~•ý“ƒø /test-4/381worldanyoneBOý“c~Š–ý“ƒø /test-2/392worldanyoneBOý“c~Ž—ý“ƒø /test-0/396worldanyoneBOý“cz†˜ý“ƒø /test-7/388worldanyoneBOý“czƒ™ý“ƒø /test-5/385worldanyoneBOý“ba±šý“„ /test-1/431worldanyoneBOý“ba®›ý“„ /test-6/428worldanyoneBOý“c~œý“„ /test-0/397worldanyoneBOý“c~‹ý“„ /test-2/393worldanyoneBOý“cŠ…žý“„ /test-9/387worldanyoneBOý“czŠŸý“„ /test-3/392worldanyoneBOý“cz„ ý“„ /test-5/386worldanyoneBOý“c~€¡ý“„ /test-4/382worldanyoneBOý“cІ¢ý“„ /test-8/388worldanyoneBOý“cz‡£ý“„ /test-7/389worldanyoneBOý“ba²¤ý“„ /test-1/432worldanyoneBOý“ba¯¥ý“„ /test-6/429worldanyoneBOý“c~¦ý“„ /test-0/398worldanyoneBOý“cІ§ý“„ /test-9/388worldanyoneBOý“cz…¨ý“„ /test-5/387worldanyoneBOý“cz‹©ý“„ /test-3/393worldanyoneBOý“czˆªý“„ /test-7/390worldanyoneBOý“c~«ý“„ /test-4/383worldanyoneBOý“c~Œ¬ý“„ /test-2/394worldanyoneBOý“cЇ­ý“„ /test-8/389worldanyoneBOý“ba³®ý“„ /test-1/433worldanyoneBOý“ba°¯ý“„ /test-6/430worldanyoneBOý“ba´°ý“„ /test-1/434worldanyoneBOý“c~‘±ý“„ /test-0/399worldanyoneBOý“cz†²ý“„ /test-5/388worldanyoneBOý“czŒ³ý“„ /test-3/394worldanyoneBOý“cЇ´ý“„ /test-9/389worldanyoneBOý“c~µý“„ /test-2/395worldanyoneBOý“c~‚¶ý“„ /test-4/384worldanyoneBOý“cz‰·ý“„ /test-7/391worldanyoneBOý“cŠˆ¸ý“„ /test-8/390worldanyoneBOý“ba±¹ý“„ /test-6/431worldanyoneBOý“baµºý“„& /test-1/435worldanyoneBOý“c~’»ý“„' /test-0/400worldanyoneBOý“c~޼ý“„) /test-2/396worldanyoneBOý“c~ƒ½ý“„) /test-4/385worldanyoneBOý“cz‡¾ý“„) /test-5/389worldanyoneBOý“cŠˆ¿ý“„* /test-9/390worldanyoneBOý“czŠÀý“„+ /test-7/392worldanyoneBOý“czÁý“„+ /test-3/395worldanyoneBOý“cЉÂý“„- /test-8/391worldanyoneBOý“ba²Ãý“„/ /test-6/432worldanyoneBOý“ba¶Äý“„0 /test-1/436worldanyoneBOý“c~“Åý“„0 /test-0/401worldanyoneBOý“czˆÆý“„4 /test-5/390worldanyoneBOý“c~Çý“„5 /test-2/397worldanyoneBOý“c~„Èý“„6 /test-4/386worldanyoneBOý“cz‹Éý“„7 /test-7/393worldanyoneBOý“cЉÊý“„7 /test-9/391worldanyoneBOý“czŽËý“„8 /test-3/396worldanyoneBOý“ba·Ìý“„9 /test-1/437worldanyoneBOý“ba³Íý“„9 /test-6/433worldanyoneBOý“cŠŠÎý“„9 /test-8/392worldanyoneBOý“c~”Ïý“„= /test-0/402worldanyoneBOý“cz‰Ðý“„? /test-5/391worldanyoneBOý“ba¸Ñý“„D /test-1/438worldanyoneBOý“ba´Òý“„E /test-6/434worldanyoneBOý“cŠŠÓý“„E /test-9/392worldanyoneBOý“cŠ‹Ôý“„E /test-8/393worldanyoneBOý“c~…Õý“„F /test-4/387worldanyoneBOý“czÖý“„F /test-3/397worldanyoneBOý“czŒ×ý“„F /test-7/394worldanyoneBOý“c~Øý“„F /test-2/398worldanyoneBOý“c~•Ùý“„G /test-0/403worldanyoneBOý“czŠÚý“„H /test-5/392worldanyoneBOý“baµÛý“„Q /test-6/435worldanyoneBOý“ba¹Üý“„Q /test-1/439worldanyoneBOý“cŠ‹Ýý“„R /test-9/393worldanyoneBOý“cŠŒÞý“„R /test-8/394worldanyoneBOý“c~‘ßý“„S /test-2/399worldanyoneBOý“c~†àý“„S /test-4/388worldanyoneBOý“c~–áý“„T /test-0/404worldanyoneBOý“czâý“„T /test-3/398worldanyoneBOý“cz‹ãý“„T /test-5/393worldanyoneBOý“czäý“„T /test-7/395worldanyoneBOý“ba¶åý“„\ /test-6/436worldanyoneBOý“baºæý“„_ /test-1/440worldanyoneBOý“c~‡çý“„` /test-4/389worldanyoneBOý“c~’èý“„a /test-2/400worldanyoneBOý“cŠéý“„a /test-8/395worldanyoneBOý“c~—êý“„a /test-0/405worldanyoneBOý“cŠŒëý“„b /test-9/394worldanyoneBOý“czŽìý“„b /test-7/396worldanyoneBOý“cz‘íý“„b /test-3/399worldanyoneBOý“czŒîý“„b /test-5/394worldanyoneBOý“ba·ïý“„d /test-6/437worldanyoneBOý“ba»ðý“„h /test-1/441worldanyoneBOý“cŠŽñý“„l /test-8/396worldanyoneBOý“cŠòý“„l /test-9/395worldanyoneBOý“c~“óý“„l /test-2/401worldanyoneBOý“c~ˆôý“„m /test-4/390worldanyoneBOý“c~˜õý“„m /test-0/406worldanyoneBOý“czöý“„n /test-5/395worldanyoneBOý“cz÷ý“„n /test-7/397worldanyoneBOý“ba¸øý“„n /test-6/438worldanyoneBOý“cz’ùý“„n /test-3/400worldanyoneBOý“ba¼úý“„q /test-1/442worldanyoneBOý“cŠûý“„v /test-8/397worldanyoneBOý“c~‰üý“„x /test-4/391worldanyoneBOý“ba¹ýý“„x /test-6/439worldanyoneBOý“c~”þý“„y /test-2/402worldanyoneBOý“c~™ÿý“„z /test-0/407worldanyoneBOý“cŠŽý“„z /test-9/396worldanyoneBOý“czý“„z /test-7/398worldanyoneBOý“czŽý“„z /test-5/396worldanyoneBOý“cz“ý“„z /test-3/401worldanyoneBOý“ba½ý“„z /test-1/443worldanyoneBOý“cŠý“„€ /test-8/398worldanyoneBOý“baºý“„‚ /test-6/440worldanyoneBOý“c~Šý“„ƒ /test-4/392worldanyoneBOý“cŠý“„† /test-9/397worldanyoneBOý“ba¾ ý“„† /test-1/444worldanyoneBOý“cz‘ ý“„‡ /test-7/399worldanyoneBOý“cz” ý“„‡ /test-3/402worldanyoneBOý“cz ý“„‡ /test-5/397worldanyoneBOý“c~š ý“„‡ /test-0/408worldanyoneBOý“c~•ý“„ˆ /test-2/403worldanyoneBOý“cŠ‘ý“„ˆ /test-8/399worldanyoneBOý“ba»ý“„Š /test-6/441worldanyoneBOý“c~‹ý“„‹ /test-4/393worldanyoneBOý“cŠý“„’ /test-9/398worldanyoneBOý“cz’ý“„“ /test-7/400worldanyoneBOý“czý“„“ /test-5/398worldanyoneBOý“cz•ý“„” /test-3/403worldanyoneBOý“ba¿ý“„” /test-1/445worldanyoneBOý“c~›ý“„” /test-0/409worldanyoneBOý“c~–ý“„” /test-2/404worldanyoneBOý“cŠ’ý“„” /test-8/400worldanyoneBOý“ba¼ý“„• /test-6/442worldanyoneBOý“c~Œý“„• /test-4/394worldanyoneBOý“cŠ‘ý“„› /test-9/399worldanyoneBOý“baÀý“„  /test-1/446worldanyoneBOý“cz“ý“„  /test-7/401worldanyoneBOý“ba½ý“„  /test-6/443worldanyoneBOý“cz‘ ý“„¡ /test-5/399worldanyoneBOý“cz–!ý“„¡ /test-3/404worldanyoneBOý“cŠ“"ý“„¡ /test-8/401worldanyoneBOý“c~œ#ý“„¡ /test-0/410worldanyoneBOý“c~—$ý“„¡ /test-2/405worldanyoneBOý“c~%ý“„¡ /test-4/395worldanyoneBOý“cŠ’&ý“„£ /test-9/400worldanyoneBOý“ba¾'ý“„¬ /test-6/444worldanyoneBOý“c~˜(ý“„¬ /test-2/406worldanyoneBOý“baÁ)ý“„­ /test-1/447worldanyoneBOý“c~Ž*ý“„­ /test-4/396worldanyoneBOý“cz—+ý“„® /test-3/405worldanyoneBOý“c~,ý“„® /test-0/411worldanyoneBOý“cz’-ý“„® /test-5/400worldanyoneBOý“cŠ“.ý“„® /test-9/401worldanyoneBOý“cŠ”/ý“„® /test-8/402worldanyoneBOý“cz”0ý“„® /test-7/402worldanyoneBOý“ba¿1ý“„¶ /test-6/445worldanyoneBOý“c~™2ý“„¶ /test-2/407worldanyoneBOý“baÂ3ý“„¸ /test-1/448worldanyoneBOý“c~4ý“„¸ /test-4/397worldanyoneBOý“cz˜5ý“„¹ /test-3/406worldanyoneBOý“cŠ”6ý“„¹ /test-9/402worldanyoneBOý“c~ž7ý“„º /test-0/412worldanyoneBOý“cŠ•8ý“„º /test-8/403worldanyoneBOý“cz“9ý“„º /test-5/401worldanyoneBOý“cz•:ý“„º /test-7/403worldanyoneBOý“baÀ;ý“„¿ /test-6/446worldanyoneBOý“c~š<ý“„¿ /test-2/408worldanyoneBOý“baÃ=ý“„à /test-1/449worldanyoneBOý“c~>ý“„Ä /test-4/398worldanyoneBOý“cŠ•?ý“„Å /test-9/403worldanyoneBOý“cz–@ý“„Å /test-7/404worldanyoneBOý“cz”Aý“„Æ /test-5/402worldanyoneBOý“c~ŸBý“„Æ /test-0/413worldanyoneBOý“cz™Cý“„Æ /test-3/407worldanyoneBOý“cŠ–Dý“„Æ /test-8/404worldanyoneBOý“baÁEý“„È /test-6/447worldanyoneBOý“c~›Fý“„È /test-2/409worldanyoneBOý“baÄGý“„Ë /test-1/450worldanyoneBOý“c~‘Hý“„Î /test-4/399worldanyoneBOý“c~ Iý“„Ñ /test-0/414worldanyoneBOý“baÂJý“„Ò /test-6/448worldanyoneBOý“cŠ–Ký“„Ò /test-9/404worldanyoneBOý“cŠ—Lý“„Ó /test-8/405worldanyoneBOý“c~œMý“„Ó /test-2/410worldanyoneBOý“czšNý“„Ó /test-3/408worldanyoneBOý“cz—Oý“„Ó /test-7/405worldanyoneBOý“cz•Pý“„Ó /test-5/403worldanyoneBOý“baÅQý“„Ô /test-1/451worldanyoneBOý“c~’Rý“„Ö /test-4/400worldanyoneBOý“c~¡Sý“„Ú /test-0/415worldanyoneBOý“cŠ—Tý“„Ý /test-9/405worldanyoneBOý“baÃUý“„Ý /test-6/449worldanyoneBOý“baÆVý“„Þ /test-1/452worldanyoneBOý“c~Wý“„Þ /test-2/411worldanyoneBOý“cz›Xý“„Þ /test-3/409worldanyoneBOý“cŠ˜Yý“„ß /test-8/406worldanyoneBOý“cz–Zý“„ß /test-5/404worldanyoneBOý“cz˜[ý“„ß /test-7/406worldanyoneBOý“c~“\ý“„ñ /test-4/401worldanyoneBOý“baÄ]ý“„ò /test-6/450worldanyoneBOý“c~¢^ý“„ò /test-0/416worldanyoneBOý“cŠ˜_ý“„ø /test-9/406worldanyoneBOý“baÇ`ý“„ú /test-1/453worldanyoneBOý“c~žaý“„ú /test-2/412worldanyoneBOý“czœbý“„û /test-3/410worldanyoneBOý“cŠ™cý“„û /test-8/407worldanyoneBOý“cz—dý“„ü /test-5/405worldanyoneBOý“cz™eý“„ü /test-7/407worldanyoneBOý“baÅfý“„ý /test-6/451worldanyoneBOý“c~”gý“„ý /test-4/402worldanyoneBOý“c~£hý“„ý /test-0/417worldanyoneBOý“cŠ™iý“… /test-9/407worldanyoneBOý“baÈjý“… /test-1/454worldanyoneBOý“c~Ÿký“… /test-2/413worldanyoneBOý“czlý“… /test-3/411worldanyoneBOý“cŠšmý“… /test-8/408worldanyoneBOý“cz˜ný“… /test-5/406worldanyoneBOý“baÆoý“… /test-6/452worldanyoneBOý“c~¤pý“… /test-0/418worldanyoneBOý“czšqý“… /test-7/408worldanyoneBOý“c~•rý“… /test-4/403worldanyoneBOý“cŠšsý“…  /test-9/408worldanyoneBOý“baÉtý“… /test-1/455worldanyoneBOý“c~ uý“… /test-2/414worldanyoneBOý“czžvý“… /test-3/412worldanyoneBOý“cŠ›wý“… /test-8/409worldanyoneBOý“baÇxý“… /test-6/453worldanyoneBOý“cz›yý“… /test-7/409worldanyoneBOý“cz™zý“… /test-5/407worldanyoneBOý“c~¥{ý“… /test-0/419worldanyoneBOý“c~–|ý“… /test-4/404worldanyoneBOý“cŠ›}ý“… /test-9/409worldanyoneBOý“baÊ~ý“… /test-1/456worldanyoneBOý“cŠœý“… /test-8/410worldanyoneBOý“c~¡€ý“… /test-2/415worldanyoneBOý“czŸý“… /test-3/413worldanyoneBOý“baÈ‚ý“… /test-6/454worldanyoneBOý“czœƒý“… /test-7/410worldanyoneBOý“czš„ý“… /test-5/408worldanyoneBOý“cŠœ…ý“… /test-9/410worldanyoneBOý“c~¦†ý“… /test-0/420worldanyoneBOý“c~—‡ý“… /test-4/405worldanyoneBOý“baˈý“…" /test-1/457worldanyoneBOý“cЉý“…# /test-8/411worldanyoneBOý“c~¢Šý“…% /test-2/416worldanyoneBOý“cz ‹ý“…% /test-3/414worldanyoneBOý“baÉŒý“…( /test-6/455worldanyoneBOý“czý“…( /test-7/411worldanyoneBOý“cz›Žý“…( /test-5/409worldanyoneBOý“c~§ý“…) /test-0/421worldanyoneBOý“c~˜ý“…) /test-4/406worldanyoneBOý“cŠ‘ý“…) /test-9/411worldanyoneBOý“baÌ’ý“…+ /test-1/458worldanyoneBOý“cŠž“ý“…, /test-8/412worldanyoneBOý“c~£”ý“…. /test-2/417worldanyoneBOý“cz¡•ý“…. /test-3/415worldanyoneBOý“baÊ–ý“…2 /test-6/456worldanyoneBOý“c~¨—ý“…3 /test-0/422worldanyoneBOý“czž˜ý“…4 /test-7/412worldanyoneBOý“czœ™ý“…4 /test-5/410worldanyoneBOý“cŠžšý“…5 /test-9/412worldanyoneBOý“c~™›ý“…5 /test-4/407worldanyoneBOý“baÍœý“…5 /test-1/459worldanyoneBOý“cŠŸý“…6 /test-8/413worldanyoneBOý“c~¤žý“…8 /test-2/418worldanyoneBOý“cz¢Ÿý“…8 /test-3/416worldanyoneBOý“baË ý“…= /test-6/457worldanyoneBOý“c~©¡ý“…= /test-0/423worldanyoneBOý“c~š¢ý“…A /test-4/408worldanyoneBOý“cŠ £ý“…A /test-8/414worldanyoneBOý“baΤý“…B /test-1/460worldanyoneBOý“czŸ¥ý“…B /test-7/413worldanyoneBOý“cz¦ý“…B /test-5/411worldanyoneBOý“cŠŸ§ý“…B /test-9/413worldanyoneBOý“cz£¨ý“…C /test-3/417worldanyoneBOý“c~¥©ý“…C /test-2/419worldanyoneBOý“ba̪ý“…F /test-6/458worldanyoneBOý“c~ª«ý“…F /test-0/424worldanyoneBOý“baͬý“…V /test-6/459worldanyoneBOý“baÏ­ý“…W /test-1/461worldanyoneBOý“cz¤®ý“…W /test-3/418worldanyoneBOý“czž¯ý“…W /test-5/412worldanyoneBOý“cz °ý“…W /test-7/414worldanyoneBOý“c~«±ý“…W /test-0/425worldanyoneBOý“cŠ ²ý“…X /test-9/414worldanyoneBOý“c~¦³ý“…X /test-2/420worldanyoneBOý“c~›´ý“…Y /test-4/409worldanyoneBOý“cŠ¡µý“…Y /test-8/415worldanyoneBOý“baζý“…h /test-6/460worldanyoneBOý“c~œ·ý“…i /test-4/410worldanyoneBOý“c~§¸ý“…i /test-2/421worldanyoneBOý“c~¬¹ý“…i /test-0/426worldanyoneBOý“baкý“…j /test-1/462worldanyoneBOý“cŠ¡»ý“…j /test-9/415worldanyoneBOý“czŸ¼ý“…k /test-5/413worldanyoneBOý“cŠ¢½ý“…k /test-8/416worldanyoneBOý“cz¥¾ý“…k /test-3/419worldanyoneBOý“cz¡¿ý“…k /test-7/415worldanyoneBOý“baÏÀý“…r /test-6/461worldanyoneBOý“baÑÁý“…v /test-1/463worldanyoneBOý“c~Âý“…v /test-4/411worldanyoneBOý“c~¨Ãý“…v /test-2/422worldanyoneBOý“c~­Äý“…w /test-0/427worldanyoneBOý“cz Åý“…w /test-5/414worldanyoneBOý“cz¦Æý“…w /test-3/420worldanyoneBOý“cz¢Çý“…w /test-7/416worldanyoneBOý“cŠ¢Èý“…w /test-9/416worldanyoneBOý“cŠ£Éý“…w /test-8/417worldanyoneBOý“baÐÊý“…z /test-6/462worldanyoneBOý“baÒËý“… /test-1/464worldanyoneBOý“c~žÌý“… /test-4/412worldanyoneBOý“c~©Íý“… /test-2/423worldanyoneBOý“c~®Îý“…‚ /test-0/428worldanyoneBOý“cŠ£Ïý“…ƒ /test-9/417worldanyoneBOý“cФÐý“…ƒ /test-8/418worldanyoneBOý“cz¡Ñý“…ƒ /test-5/415worldanyoneBOý“cz£Òý“…ƒ /test-7/417worldanyoneBOý“cz§Óý“…„ /test-3/421worldanyoneBOý“baÑÔý“…„ /test-6/463worldanyoneBOý“baÓÕý“…‡ /test-1/465worldanyoneBOý“baÒÖý“…’ /test-6/464worldanyoneBOý“baÔ×ý“…’ /test-1/466worldanyoneBOý“cz¤Øý“…“ /test-7/418worldanyoneBOý“cŠ¥Ùý“…“ /test-8/419worldanyoneBOý“cz¢Úý“…“ /test-5/416worldanyoneBOý“cФÛý“…” /test-9/418worldanyoneBOý“cz¨Üý“…” /test-3/422worldanyoneBOý“c~¯Ýý“…” /test-0/429worldanyoneBOý“c~ªÞý“…” /test-2/424worldanyoneBOý“c~Ÿßý“…” /test-4/413worldanyoneBOý“baÕàý“… /test-1/467worldanyoneBOý“baÓáý“… /test-6/465worldanyoneBOý“cz¥âý“…Ÿ /test-7/419worldanyoneBOý“cŠ¥ãý“…Ÿ /test-9/419worldanyoneBOý“cЦäý“…Ÿ /test-8/420worldanyoneBOý“cz£åý“…  /test-5/417worldanyoneBOý“cz©æý“…  /test-3/423worldanyoneBOý“c~°çý“…  /test-0/430worldanyoneBOý“c~«èý“…  /test-2/425worldanyoneBOý“c~ éý“…¡ /test-4/414worldanyoneBOý“baÔêý“…¬ /test-6/466worldanyoneBOý“baÖëý“…¬ /test-1/468worldanyoneBOý“c~±ìý“…¬ /test-0/431worldanyoneBOý“c~¡íý“…¬ /test-4/415worldanyoneBOý“cЧîý“…­ /test-8/421worldanyoneBOý“cz¦ïý“…­ /test-7/420worldanyoneBOý“cz¤ðý“…® /test-5/418worldanyoneBOý“czªñý“…® /test-3/424worldanyoneBOý“c~¬òý“…® /test-2/426worldanyoneBOý“cЦóý“…® /test-9/420worldanyoneBOý“cz«ôý“…à /test-3/425worldanyoneBOý“ba×õý“…Ä /test-1/469worldanyoneBOý“baÕöý“…Ä /test-6/467worldanyoneBOý“cz¥÷ý“…Ä /test-5/419worldanyoneBOý“cЍøý“…Ä /test-8/422worldanyoneBOý“cЧùý“…Ä /test-9/421worldanyoneBOý“c~²úý“…Ä /test-0/432worldanyoneBOý“cz§ûý“…Å /test-7/421worldanyoneBOý“c~­üý“…Å /test-2/427worldanyoneBOý“c~¢ýý“…Å /test-4/416worldanyoneBOý“baØþý“…Ð /test-1/470worldanyoneBOý“baÖÿý“…Ð /test-6/468worldanyoneBOý“cz¦ý“…Ð /test-5/420worldanyoneBOý“cz¨ý“…Ñ /test-7/422worldanyoneBOý“cz¬ý“…Ò /test-3/426worldanyoneBOý“cŠ©ý“…Ò /test-8/423worldanyoneBOý“cЍý“…Ò /test-9/422worldanyoneBOý“c~³ý“…Ò /test-0/433worldanyoneBOý“c~£ý“…Ò /test-4/417worldanyoneBOý“c~®ý“…Ó /test-2/428worldanyoneBOý“baÙý“…Ú /test-1/471worldanyoneBOý“ba× ý“…Ú /test-6/469worldanyoneBOý“cz§ ý“…Ú /test-5/421worldanyoneBOý“c~´ ý“…Þ /test-0/434worldanyoneBOý“cz© ý“…Þ /test-7/423worldanyoneBOý“cz­ ý“…Þ /test-3/427worldanyoneBOý“cŠªý“…ß /test-8/424worldanyoneBOý“cŠ©ý“…ß /test-9/423worldanyoneBOý“c~¯ý“…ß /test-2/429worldanyoneBOý“c~¤ý“…ß /test-4/418worldanyoneBOý“baÚý“…ä /test-1/472worldanyoneBOý“baØý“…å /test-6/470worldanyoneBOý“cz¨ý“…å /test-5/422worldanyoneBOý“c~µý“…ë /test-0/435worldanyoneBOý“cz®ý“…ì /test-3/428worldanyoneBOý“czªý“…ì /test-7/424worldanyoneBOý“cŠªý“…ì /test-9/424worldanyoneBOý“cŠ«ý“…ì /test-8/425worldanyoneBOý“c~°ý“…ì /test-2/430worldanyoneBOý“c~¥ý“…ì /test-4/419worldanyoneBOý“baÛý“…ï /test-1/473worldanyoneBOý“baÙý“…ï /test-6/471worldanyoneBOý“cz©ý“…ð /test-5/423worldanyoneBOý“cz«ý“…÷ /test-7/425worldanyoneBOý“cz¯ ý“…÷ /test-3/429worldanyoneBOý“cЬ!ý“…÷ /test-8/426worldanyoneBOý“c~¶"ý“…÷ /test-0/436worldanyoneBOý“c~±#ý“…ø /test-2/431worldanyoneBOý“c~¦$ý“…ø /test-4/420worldanyoneBOý“cŠ«%ý“…ø /test-9/425worldanyoneBOý“baÜ&ý“…ù /test-1/474worldanyoneBOý“baÚ'ý“…ú /test-6/472worldanyoneBOý“czª(ý“…ú /test-5/424worldanyoneBOý“baÝ)ý“† /test-1/475worldanyoneBOý“cz°*ý“† /test-3/430worldanyoneBOý“cŠ­+ý“† /test-8/427worldanyoneBOý“cЬ,ý“† /test-9/426worldanyoneBOý“cz¬-ý“† /test-7/426worldanyoneBOý“c~·.ý“† /test-0/437worldanyoneBOý“c~§/ý“† /test-4/421worldanyoneBOý“c~²0ý“† /test-2/432worldanyoneBOý“baÛ1ý“† /test-6/473worldanyoneBOý“cz«2ý“† /test-5/425worldanyoneBOý“baÞ3ý“†  /test-1/476worldanyoneBOý“cz±4ý“† /test-3/431worldanyoneBOý“baÜ5ý“† /test-6/474worldanyoneBOý“c~¸6ý“† /test-0/438worldanyoneBOý“c~³7ý“† /test-2/433worldanyoneBOý“c~¨8ý“† /test-4/422worldanyoneBOý“cŠ­9ý“† /test-9/427worldanyoneBOý“cŠ®:ý“† /test-8/428worldanyoneBOý“cz­;ý“† /test-7/427worldanyoneBOý“cz¬<ý“† /test-5/426worldanyoneBOý“baß=ý“† /test-1/477worldanyoneBOý“cz²>ý“† /test-3/432worldanyoneBOý“baÝ?ý“† /test-6/475worldanyoneBOý“c~¹@ý“† /test-0/439worldanyoneBOý“baàAý“† /test-1/478worldanyoneBOý“cŠ®Bý“† /test-9/428worldanyoneBOý“cНCý“† /test-8/429worldanyoneBOý“c~©Dý“† /test-4/423worldanyoneBOý“cz®Eý“† /test-7/428worldanyoneBOý“c~´Fý“† /test-2/434worldanyoneBOý“cz­Gý“† /test-5/427worldanyoneBOý“cz³Hý“† /test-3/433worldanyoneBOý“baÞIý“†' /test-6/476worldanyoneBOý“baáJý“†* /test-1/479worldanyoneBOý“cz¯Ký“†+ /test-7/429worldanyoneBOý“cz´Lý“†+ /test-3/434worldanyoneBOý“cz®Mý“†+ /test-5/428worldanyoneBOý“c~ªNý“†+ /test-4/424worldanyoneBOý“c~µOý“†+ /test-2/435worldanyoneBOý“c~ºPý“†+ /test-0/440worldanyoneBOý“cНQý“†, /test-9/429worldanyoneBOý“cаRý“†, /test-8/430worldanyoneBOý“baßSý“†/ /test-6/477worldanyoneBOý“baâTý“†3 /test-1/480worldanyoneBOý“cz°Uý“†6 /test-7/430worldanyoneBOý“cаVý“†7 /test-9/430worldanyoneBOý“cбWý“†7 /test-8/431worldanyoneBOý“c~»Xý“†: /test-0/441worldanyoneBOý“c~«Yý“†: /test-4/425worldanyoneBOý“c~¶Zý“†: /test-2/436worldanyoneBOý“baà[ý“†: /test-6/478worldanyoneBOý“czµ\ý“†: /test-3/435worldanyoneBOý“cz¯]ý“†: /test-5/429worldanyoneBOý“baã^ý“†< /test-1/481worldanyoneBOý“cz±_ý“†B /test-7/431worldanyoneBOý“cб`ý“†B /test-9/431worldanyoneBOý“cвaý“†C /test-8/432worldanyoneBOý“c~¼bý“†E /test-0/442worldanyoneBOý“c~¬cý“†G /test-4/426worldanyoneBOý“baädý“†G /test-1/482worldanyoneBOý“c~·eý“†H /test-2/437worldanyoneBOý“baáfý“†H /test-6/479worldanyoneBOý“cz¶gý“†I /test-3/436worldanyoneBOý“cz°hý“†I /test-5/430worldanyoneBOý“cz²iý“†K /test-7/432worldanyoneBOý“cвjý“†O /test-9/432worldanyoneBOý“c~½ký“†P /test-0/443worldanyoneBOý“cгlý“†P /test-8/433worldanyoneBOý“baâmý“†U /test-6/480worldanyoneBOý“c~¸ný“†U /test-2/438worldanyoneBOý“baåoý“†V /test-1/483worldanyoneBOý“c~­pý“†V /test-4/427worldanyoneBOý“cz·qý“†V /test-3/437worldanyoneBOý“cz±rý“†W /test-5/431worldanyoneBOý“cz³sý“†W /test-7/433worldanyoneBOý“cгtý“†X /test-9/433worldanyoneBOý“c~¾uý“†Z /test-0/444worldanyoneBOý“cŠ´vý“†Z /test-8/434worldanyoneBOý“baãwý“†e /test-6/481worldanyoneBOý“cŠ´xý“†e /test-9/434worldanyoneBOý“cеyý“†f /test-8/435worldanyoneBOý“c~®zý“†g /test-4/428worldanyoneBOý“c~¿{ý“†g /test-0/445worldanyoneBOý“baæ|ý“†g /test-1/484worldanyoneBOý“cz´}ý“†g /test-7/434worldanyoneBOý“cz²~ý“†g /test-5/432worldanyoneBOý“cz¸ý“†g /test-3/438worldanyoneBOý“c~¹€ý“†g /test-2/439worldanyoneBOý“cеý“†s /test-9/435worldanyoneBOý“czµ‚ý“†u /test-7/435worldanyoneBOý“cz³ƒý“†v /test-5/433worldanyoneBOý“cz¹„ý“†v /test-3/439worldanyoneBOý“baç…ý“†w /test-1/485worldanyoneBOý“c~¯†ý“†w /test-4/429worldanyoneBOý“c~º‡ý“†w /test-2/440worldanyoneBOý“c~Àˆý“†w /test-0/446worldanyoneBOý“cж‰ý“†w /test-8/436worldanyoneBOý“baäŠý“†w /test-6/482worldanyoneBOý“cж‹ý“†~ /test-9/436worldanyoneBOý“cŠ·Œý“†‚ /test-8/437worldanyoneBOý“cz¶ý“†ƒ /test-7/436worldanyoneBOý“czºŽý“†„ /test-3/440worldanyoneBOý“baåý“†„ /test-6/483worldanyoneBOý“baèý“†„ /test-1/486worldanyoneBOý“c~°‘ý“†„ /test-4/430worldanyoneBOý“cz´’ý“†„ /test-5/434worldanyoneBOý“c~Á“ý“†… /test-0/447worldanyoneBOý“c~»”ý“†… /test-2/441worldanyoneBOý“cŠ·•ý“†‡ /test-9/437worldanyoneBOý“cЏ–ý“† /test-8/438worldanyoneBOý“cz·—ý“† /test-7/437worldanyoneBOý“baé˜ý“† /test-1/487worldanyoneBOý“baæ™ý“† /test-6/484worldanyoneBOý“c~±šý“†‘ /test-4/431worldanyoneBOý“cz»›ý“†‘ /test-3/441worldanyoneBOý“czµœý“†’ /test-5/435worldanyoneBOý“c~Âý“†’ /test-0/448worldanyoneBOý“c~¼žý“†’ /test-2/442worldanyoneBOý“cЏŸý“†“ /test-9/438worldanyoneBOý“cй ý“†™ /test-8/439worldanyoneBOý“cz¸¡ý“† /test-7/438worldanyoneBOý“baç¢ý“†ž /test-6/485worldanyoneBOý“baê£ý“†ž /test-1/488worldanyoneBOý“cz¶¤ý“†ž /test-5/436worldanyoneBOý“cz¼¥ý“†ž /test-3/442worldanyoneBOý“cй¦ý“†Ÿ /test-9/439worldanyoneBOý“c~²§ý“†  /test-4/432worldanyoneBOý“c~½¨ý“†  /test-2/443worldanyoneBOý“c~éý“†  /test-0/449worldanyoneBOý“cŠºªý“†¢ /test-8/440worldanyoneBOý“cz¹«ý“†§ /test-7/439worldanyoneBOý“baë¬ý“†ª /test-1/489worldanyoneBOý“baè­ý“†ª /test-6/486worldanyoneBOý“cŠº®ý“†ª /test-9/440worldanyoneBOý“cz·¯ý“†« /test-5/437worldanyoneBOý“cz½°ý“†« /test-3/443worldanyoneBOý“c~¾±ý“†« /test-2/444worldanyoneBOý“c~³²ý“†« /test-4/433worldanyoneBOý“c~ijý“†« /test-0/450worldanyoneBOý“cŠ»´ý“†¬ /test-8/441worldanyoneBOý“czºµý“†¯ /test-7/440worldanyoneBOý“baé¶ý“†¹ /test-6/487worldanyoneBOý“baì·ý“†º /test-1/490worldanyoneBOý“cŠ»¸ý“†º /test-9/441worldanyoneBOý“cм¹ý“†º /test-8/442worldanyoneBOý“cz¸ºý“†º /test-5/438worldanyoneBOý“cz¾»ý“†º /test-3/444worldanyoneBOý“c~´¼ý“†º /test-4/434worldanyoneBOý“cz»½ý“†º /test-7/441worldanyoneBOý“c~žý“†» /test-0/451worldanyoneBOý“c~¿¿ý“†» /test-2/445worldanyoneBOý“baíÀý“†Å /test-1/491worldanyoneBOý“baêÁý“†Å /test-6/488worldanyoneBOý“c~µÂý“†Æ /test-4/435worldanyoneBOý“cz¹Ãý“†Æ /test-5/439worldanyoneBOý“cz¿Äý“†Ç /test-3/445worldanyoneBOý“c~ÆÅý“†Ç /test-0/452worldanyoneBOý“cz¼Æý“†Ç /test-7/442worldanyoneBOý“c~ÀÇý“†Ç /test-2/446worldanyoneBOý“cнÈý“†Ç /test-8/443worldanyoneBOý“cмÉý“†Ç /test-9/442worldanyoneBOý“baîÊý“†Ñ /test-1/492worldanyoneBOý“baëËý“†Ñ /test-6/489worldanyoneBOý“czºÌý“†Ò /test-5/440worldanyoneBOý“c~ÇÍý“†Ò /test-0/453worldanyoneBOý“c~ÁÎý“†Ó /test-2/447worldanyoneBOý“c~¶Ïý“†Ó /test-4/436worldanyoneBOý“czÀÐý“†Ó /test-3/446worldanyoneBOý“cz½Ñý“†Ó /test-7/443worldanyoneBOý“cоÒý“†Ó /test-8/444worldanyoneBOý“cнÓý“†Ó /test-9/443worldanyoneBOý“baïÔý“†Û /test-1/493worldanyoneBOý“baìÕý“†Û /test-6/490worldanyoneBOý“c~ÈÖý“†Þ /test-0/454worldanyoneBOý“cо×ý“†ß /test-9/444worldanyoneBOý“cŠ¿Øý“†à /test-8/445worldanyoneBOý“czÁÙý“†à /test-3/447worldanyoneBOý“cz»Úý“†à /test-5/441worldanyoneBOý“c~·Ûý“†à /test-4/437worldanyoneBOý“c~ÂÜý“†à /test-2/448worldanyoneBOý“cz¾Ýý“†á /test-7/444worldanyoneBOý“baðÞý“†ä /test-1/494worldanyoneBOý“baíßý“†å /test-6/491worldanyoneBOý“c~Éàý“†ç /test-0/455worldanyoneBOý“cŠ¿áý“†è /test-9/445worldanyoneBOý“cŠÀâý“†ê /test-8/446worldanyoneBOý“czÂãý“†ê /test-3/448worldanyoneBOý“c~¸äý“†ë /test-4/438worldanyoneBOý“c~Ãåý“†ë /test-2/449worldanyoneBOý“cz¼æý“†ì /test-5/442worldanyoneBOý“cz¿çý“†ì /test-7/445worldanyoneBOý“bañèý“†î /test-1/495worldanyoneBOý“baîéý“†î /test-6/492worldanyoneBOý“c~Êêý“†ð /test-0/456worldanyoneBOý“cŠÀëý“†ò /test-9/446worldanyoneBOý“cŠÁìý“†õ /test-8/447worldanyoneBOý“czÃíý“†õ /test-3/449worldanyoneBOý“cz½îý“†ö /test-5/443worldanyoneBOý“c~Äïý“†ö /test-2/450worldanyoneBOý“c~¹ðý“†ö /test-4/439worldanyoneBOý“czÀñý“†÷ /test-7/446worldanyoneBOý“baïòý“†÷ /test-6/493worldanyoneBOý“baòóý“†÷ /test-1/496worldanyoneBOý“c~Ëôý“†ü /test-0/457worldanyoneBOý“cŠÁõý“†ü /test-9/447worldanyoneBOý“baóöý“‡ /test-1/497worldanyoneBOý“c~º÷ý“‡ /test-4/440worldanyoneBOý“baðøý“‡ /test-6/494worldanyoneBOý“cz¾ùý“‡ /test-5/444worldanyoneBOý“czÁúý“‡ /test-7/447worldanyoneBOý“czÄûý“‡ /test-3/450worldanyoneBOý“cŠÂüý“‡ /test-8/448worldanyoneBOý“c~Åýý“‡ /test-2/451worldanyoneBOý“c~Ìþý“‡ /test-0/458worldanyoneBOý“cŠÂÿý“‡ /test-9/448worldanyoneBOý“cz¿ý“‡  /test-5/445worldanyoneBOý“bañý“‡  /test-6/495worldanyoneBOý“baôý“‡ /test-1/498worldanyoneBOý“cŠÃý“‡ /test-8/449worldanyoneBOý“czÅý“‡ /test-3/451worldanyoneBOý“c~Æý“‡ /test-2/452worldanyoneBOý“c~»ý“‡ /test-4/441worldanyoneBOý“czÂý“‡ /test-7/448worldanyoneBOý“c~Íý“‡ /test-0/459worldanyoneBOý“cŠÃ ý“‡ /test-9/449worldanyoneBOý“czÀ ý“‡ /test-5/446worldanyoneBOý“baõ ý“‡ /test-1/499worldanyoneBOý“cŠÄ ý“‡ /test-8/450worldanyoneBOý“c~Ç ý“‡ /test-2/453worldanyoneBOý“c~¼ý“‡ /test-4/442worldanyoneBOý“baòý“‡ /test-6/496worldanyoneBOý“c~Îý“‡ /test-0/460worldanyoneBOý“cŠÄý“‡ /test-9/450worldanyoneBOý“czÆý“‡ /test-3/452worldanyoneBOý“czÃý“‡ /test-7/449worldanyoneBOý“czÁý“‡ /test-5/447worldanyoneBOý“baöý“‡& /test-1/500worldanyoneBOý“cŠÅý“‡( /test-9/451worldanyoneBOý“c~Ïý“‡( /test-0/461worldanyoneBOý“c~Èý“‡( /test-2/454worldanyoneBOý“baóý“‡) /test-6/497worldanyoneBOý“czÂý“‡) /test-5/448worldanyoneBOý“czÇý“‡) /test-3/453worldanyoneBOý“cŠÅý“‡) /test-8/451worldanyoneBOý“c~½ý“‡) /test-4/443worldanyoneBOý“czÄý“‡* /test-7/450worldanyoneBOý“ba÷ý“‡/ /test-1/501worldanyoneBOý“baô ý“‡3 /test-6/498worldanyoneBOý“c~É!ý“‡3 /test-2/455worldanyoneBOý“cŠÆ"ý“‡3 /test-9/452worldanyoneBOý“c~Ð#ý“‡3 /test-0/462worldanyoneBOý“cŠÆ$ý“‡4 /test-8/452worldanyoneBOý“czÅ%ý“‡5 /test-7/451worldanyoneBOý“czÈ&ý“‡5 /test-3/454worldanyoneBOý“czÃ'ý“‡5 /test-5/449worldanyoneBOý“c~¾(ý“‡5 /test-4/444worldanyoneBOý“baø)ý“‡7 /test-1/502worldanyoneBOý“baõ*ý“‡= /test-6/499worldanyoneBOý“c~Ê+ý“‡= /test-2/456worldanyoneBOý“cŠÇ,ý“‡= /test-9/453worldanyoneBOý“c~Ñ-ý“‡> /test-0/463worldanyoneBOý“cŠÇ.ý“‡@ /test-8/453worldanyoneBOý“c~¿/ý“‡@ /test-4/445worldanyoneBOý“czÉ0ý“‡@ /test-3/455worldanyoneBOý“czÆ1ý“‡A /test-7/452worldanyoneBOý“baù2ý“‡A /test-1/503worldanyoneBOý“czÄ3ý“‡A /test-5/450worldanyoneBOý“baö4ý“‡H /test-6/500worldanyoneBOý“cŠÈ5ý“‡I /test-9/454worldanyoneBOý“c~Ë6ý“‡I /test-2/457worldanyoneBOý“c~Ò7ý“‡I /test-0/464worldanyoneBOý“baú8ý“‡L /test-1/504worldanyoneBOý“cŠÈ9ý“‡L /test-8/454worldanyoneBOý“czÊ:ý“‡M /test-3/456worldanyoneBOý“c~À;ý“‡M /test-4/446worldanyoneBOý“czÇ<ý“‡N /test-7/453worldanyoneBOý“czÅ=ý“‡N /test-5/451worldanyoneBOý“ba÷>ý“‡P /test-6/501worldanyoneBOý“cŠÉ?ý“‡S /test-9/455worldanyoneBOý“c~Ì@ý“‡S /test-2/458worldanyoneBOý“c~ÓAý“‡S /test-0/465worldanyoneBOý“baûBý“‡V /test-1/505worldanyoneBOý“cŠÉCý“‡V /test-8/455worldanyoneBOý“czÆDý“‡X /test-5/452worldanyoneBOý“czÈEý“‡X /test-7/454worldanyoneBOý“czËFý“‡X /test-3/457worldanyoneBOý“c~ÁGý“‡X /test-4/447worldanyoneBOý“baøHý“‡[ /test-6/502worldanyoneBOý“cŠÊIý“‡\ /test-9/456worldanyoneBOý“c~ÔJý“‡\ /test-0/466worldanyoneBOý“c~ÍKý“‡\ /test-2/459worldanyoneBOý“baüLý“‡a /test-1/506worldanyoneBOý“cŠÊMý“‡a /test-8/456worldanyoneBOý“czÇNý“‡b /test-5/453worldanyoneBOý“czÌOý“‡b /test-3/458worldanyoneBOý“czÉPý“‡b /test-7/455worldanyoneBOý“c~ÂQý“‡c /test-4/448worldanyoneBOý“baùRý“‡d /test-6/503worldanyoneBOý“cŠËSý“‡g /test-9/457worldanyoneBOý“c~ÎTý“‡g /test-2/460worldanyoneBOý“c~ÕUý“‡g /test-0/467worldanyoneBOý“baýVý“‡j /test-1/507worldanyoneBOý“cŠËWý“‡j /test-8/457worldanyoneBOý“baúXý“‡n /test-6/504worldanyoneBOý“czÍYý“‡n /test-3/459worldanyoneBOý“czÈZý“‡n /test-5/454worldanyoneBOý“c~Ã[ý“‡n /test-4/449worldanyoneBOý“czÊ\ý“‡o /test-7/456worldanyoneBOý“cŠÌ]ý“‡q /test-9/458worldanyoneBOý“c~Ö^ý“‡q /test-0/468worldanyoneBOý“c~Ï_ý“‡q /test-2/461worldanyoneBOý“baþ`ý“‡v /test-1/508worldanyoneBOý“cŠÌaý“‡v /test-8/458worldanyoneBOý“czÉbý“‡z /test-5/455worldanyoneBOý“baûcý“‡{ /test-6/505worldanyoneBOý“czÎdý“‡{ /test-3/460worldanyoneBOý“czËeý“‡{ /test-7/457worldanyoneBOý“c~Äfý“‡{ /test-4/450worldanyoneBOý“c~×gý“‡| /test-0/469worldanyoneBOý“c~Ðhý“‡| /test-2/462worldanyoneBOý“cŠÍiý“‡| /test-9/459worldanyoneBOý“baÿjý“‡ /test-1/509worldanyoneBOý“cŠÍký“‡€ /test-8/459worldanyoneBOý“czÊlý“‡… /test-5/456worldanyoneBOý“baümý“‡† /test-6/506worldanyoneBOý“c~Åný“‡† /test-4/451worldanyoneBOý“czÏoý“‡† /test-3/461worldanyoneBOý“czÌpý“‡† /test-7/458worldanyoneBOý“c~Øqý“‡‡ /test-0/470worldanyoneBOý“c~Ñrý“‡ˆ /test-2/463worldanyoneBOý“cŠÎsý“‡ˆ /test-9/460worldanyoneBOý“batý“‡‰ /test-1/510worldanyoneBOý“cŠÎuý“‡‰ /test-8/460worldanyoneBOý“baývý“‡‘ /test-6/507worldanyoneBOý“czËwý“‡‘ /test-5/457worldanyoneBOý“c~Æxý“‡‘ /test-4/452worldanyoneBOý“bayý“‡“ /test-1/511worldanyoneBOý“czÐzý“‡“ /test-3/462worldanyoneBOý“c~Ù{ý“‡“ /test-0/471worldanyoneBOý“c~Ò|ý“‡” /test-2/464worldanyoneBOý“cŠÏ}ý“‡” /test-8/461worldanyoneBOý“cŠÏ~ý“‡” /test-9/461worldanyoneBOý“czÍý“‡• /test-7/459worldanyoneBOý“baþ€ý“‡› /test-6/508worldanyoneBOý“c~Çý“‡› /test-4/453worldanyoneBOý“czÌ‚ý“‡œ /test-5/458worldanyoneBOý“c~Úƒý“‡  /test-0/472worldanyoneBOý“cŠÐ„ý“‡  /test-8/462worldanyoneBOý“cŠÐ…ý“‡  /test-9/462worldanyoneBOý“czΆý“‡  /test-7/460worldanyoneBOý“czчý“‡¡ /test-3/463worldanyoneBOý“c~Óˆý“‡¡ /test-2/465worldanyoneBOý“ba‰ý“‡¡ /test-1/512worldanyoneBOý“czÍŠý“‡¥ /test-5/459worldanyoneBOý“c~È‹ý“‡¥ /test-4/454worldanyoneBOý“baÿŒý“‡¥ /test-6/509worldanyoneBOý“c~Ûý“‡« /test-0/473worldanyoneBOý“cŠÑŽý“‡« /test-8/463worldanyoneBOý“baý“‡¬ /test-1/513worldanyoneBOý“cŠÑý“‡¬ /test-9/463worldanyoneBOý“c~Ô‘ý“‡­ /test-2/466worldanyoneBOý“czÏ’ý“‡­ /test-7/461worldanyoneBOý“czÒ“ý“‡­ /test-3/464worldanyoneBOý“ba”ý“‡¯ /test-6/510worldanyoneBOý“c~É•ý“‡¯ /test-4/455worldanyoneBOý“czΖý“‡¯ /test-5/460worldanyoneBOý“ba—ý“‡µ /test-1/514worldanyoneBOý“c~ܘý“‡µ /test-0/474worldanyoneBOý“cŠÒ™ý“‡¶ /test-8/464worldanyoneBOý“cŠÒšý“‡¶ /test-9/464worldanyoneBOý“ba›ý“‡¹ /test-6/511worldanyoneBOý“czÏœý“‡¹ /test-5/461worldanyoneBOý“czÐý“‡¹ /test-7/462worldanyoneBOý“czÓžý“‡¹ /test-3/465worldanyoneBOý“c~ÊŸý“‡º /test-4/456worldanyoneBOý“c~Õ ý“‡º /test-2/467worldanyoneBOý“ba¡ý“‡Á /test-1/515worldanyoneBOý“c~Ý¢ý“‡Á /test-0/475worldanyoneBOý“cŠÓ£ý“‡Á /test-9/465worldanyoneBOý“cŠÓ¤ý“‡Á /test-8/465worldanyoneBOý“ba¥ý“‡Å /test-6/512worldanyoneBOý“czÔ¦ý“‡Å /test-3/466worldanyoneBOý“czѧý“‡Å /test-7/463worldanyoneBOý“czШý“‡Å /test-5/462worldanyoneBOý“c~Ë©ý“‡Æ /test-4/457worldanyoneBOý“c~Öªý“‡Æ /test-2/468worldanyoneBOý“c~Þ«ý“‡Ê /test-0/476worldanyoneBOý“ba¬ý“‡Ê /test-1/516worldanyoneBOý“cŠÔ­ý“‡Ê /test-8/466worldanyoneBOý“cŠÔ®ý“‡Ê /test-9/466worldanyoneBOý“c~̯ý“‡Ð /test-4/458worldanyoneBOý“ba°ý“‡Ð /test-6/513worldanyoneBOý“czÒ±ý“‡Ð /test-7/464worldanyoneBOý“czѲý“‡Ð /test-5/463worldanyoneBOý“czÕ³ý“‡Ð /test-3/467worldanyoneBOý“c~×´ý“‡Ñ /test-2/469worldanyoneBOý“baµý“‡Ô /test-1/517worldanyoneBOý“c~ß¶ý“‡Õ /test-0/477worldanyoneBOý“cŠÕ·ý“‡Õ /test-8/467worldanyoneBOý“cŠÕ¸ý“‡Õ /test-9/467worldanyoneBOý“ba¹ý“‡Û /test-6/514worldanyoneBOý“c~ͺý“‡Û /test-4/459worldanyoneBOý“czÒ»ý“‡Ü /test-5/464worldanyoneBOý“czÓ¼ý“‡Ü /test-7/465worldanyoneBOý“czÖ½ý“‡Ü /test-3/468worldanyoneBOý“c~ؾý“‡Ü /test-2/470worldanyoneBOý“ba¿ý“‡à /test-1/518worldanyoneBOý“c~àÀý“‡á /test-0/478worldanyoneBOý“cŠÖÁý“‡á /test-8/468worldanyoneBOý“cŠÖÂý“‡á /test-9/468worldanyoneBOý“baÃý“‡å /test-6/515worldanyoneBOý“czÓÄý“‡å /test-5/465worldanyoneBOý“c~ÎÅý“‡å /test-4/460worldanyoneBOý“czׯý“‡ç /test-3/469worldanyoneBOý“czÔÇý“‡ç /test-7/466worldanyoneBOý“c~ÙÈý“‡ç /test-2/471worldanyoneBOý“ba Éý“‡ê /test-1/519worldanyoneBOý“cŠ×Êý“‡ë /test-8/469worldanyoneBOý“c~áËý“‡ë /test-0/479worldanyoneBOý“cŠ×Ìý“‡ë /test-9/469worldanyoneBOý“baÍý“‡ï /test-6/516worldanyoneBOý“c~ÏÎý“‡ï /test-4/461worldanyoneBOý“czÔÏý“‡ï /test-5/466worldanyoneBOý“c~ÚÐý“‡ñ /test-2/472worldanyoneBOý“czÕÑý“‡ò /test-7/467worldanyoneBOý“czØÒý“‡ò /test-3/470worldanyoneBOý“ba Óý“‡ó /test-1/520worldanyoneBOý“cŠØÔý“‡õ /test-8/470worldanyoneBOý“c~âÕý“‡õ /test-0/480worldanyoneBOý“cŠØÖý“‡õ /test-9/470worldanyoneBOý“ba×ý“‡ù /test-6/517worldanyoneBOý“czÕØý“‡ú /test-5/467worldanyoneBOý“c~ÐÙý“‡ú /test-4/462worldanyoneBOý“c~ÛÚý“‡û /test-2/473worldanyoneBOý“ba Ûý“‡ý /test-1/521worldanyoneBOý“czÖÜý“‡ý /test-7/468worldanyoneBOý“czÙÝý“‡ý /test-3/471worldanyoneBOý“cŠÙÞý“‡ÿ /test-8/471worldanyoneBOý“cŠÙßý“‡ÿ /test-9/471worldanyoneBOý“c~ãàý“‡ÿ /test-0/481worldanyoneBOý“baáý“ˆ /test-6/518worldanyoneBOý“czÖâý“ˆ /test-5/468worldanyoneBOý“c~Ñãý“ˆ /test-4/463worldanyoneBOý“c~Üäý“ˆ /test-2/474worldanyoneBOý“ba åý“ˆ  /test-1/522worldanyoneBOý“cŠÚæý“ˆ  /test-9/472worldanyoneBOý“c~äçý“ˆ  /test-0/482worldanyoneBOý“cŠÚèý“ˆ  /test-8/472worldanyoneBOý“cz×éý“ˆ  /test-7/469worldanyoneBOý“czÚêý“ˆ  /test-3/472worldanyoneBOý“ba ëý“ˆ /test-6/519worldanyoneBOý“cz×ìý“ˆ /test-5/469worldanyoneBOý“c~Ýíý“ˆ /test-2/475worldanyoneBOý“c~Òîý“ˆ /test-4/464worldanyoneBOý“ba ïý“ˆ /test-1/523worldanyoneBOý“cŠÛðý“ˆ /test-9/473worldanyoneBOý“czÛñý“ˆ4 /test-3/473worldanyoneBOý“c~åòý“ˆ4 /test-0/483worldanyoneBOý“cŠÛóý“ˆ5 /test-8/473worldanyoneBOý“ba ôý“ˆ5 /test-6/520worldanyoneBOý“baõý“ˆ5 /test-1/524worldanyoneBOý“c~Þöý“ˆ5 /test-2/476worldanyoneBOý“c~Ó÷ý“ˆ5 /test-4/465worldanyoneBOý“czØøý“ˆ5 /test-7/470worldanyoneBOý“czØùý“ˆ6 /test-5/470worldanyoneBOý“cŠÜúý“ˆ= /test-9/474worldanyoneBOý“ba ûý“ˆA /test-6/521worldanyoneBOý“baüý“ˆA /test-1/525worldanyoneBOý“cŠÜýý“ˆA /test-8/474worldanyoneBOý“c~ßþý“ˆA /test-2/477worldanyoneBOý“c~æÿý“ˆB /test-0/484worldanyoneBOý“c~Ôý“ˆB /test-4/466worldanyoneBOý“czÙý“ˆB /test-7/471worldanyoneBOý“czÙý“ˆB /test-5/471worldanyoneBOý“czÜý“ˆB /test-3/474worldanyoneBOý“cŠÝý“ˆE /test-9/475worldanyoneBOý“ba ý“ˆL /test-6/522worldanyoneBOý“baý“ˆN /test-1/526worldanyoneBOý“c~çý“ˆO /test-0/485worldanyoneBOý“c~Õý“ˆO /test-4/467worldanyoneBOý“c~à ý“ˆO /test-2/478worldanyoneBOý“cŠÝ ý“ˆO /test-8/475worldanyoneBOý“czÝ ý“ˆP /test-3/475worldanyoneBOý“czÚ ý“ˆP /test-5/472worldanyoneBOý“czÚ ý“ˆP /test-7/472worldanyoneBOý“cŠÞý“ˆQ /test-9/476worldanyoneBOý“ba ý“ˆT /test-6/523worldanyoneBOý“baý“ˆV /test-1/527worldanyoneBOý“c~èý“ˆ[ /test-0/486worldanyoneBOý“cŠÞý“ˆ[ /test-8/476worldanyoneBOý“c~áý“ˆ[ /test-2/479worldanyoneBOý“c~Öý“ˆ[ /test-4/468worldanyoneBOý“cŠßý“ˆ\ /test-9/477worldanyoneBOý“baý“ˆ] /test-6/524worldanyoneBOý“czÞý“ˆ] /test-3/476worldanyoneBOý“czÛý“ˆ] /test-5/473worldanyoneBOý“czÛý“ˆ] /test-7/473worldanyoneBOý“baý“ˆ_ /test-1/528worldanyoneBOý“c~éý“ˆe /test-0/487worldanyoneBOý“c~âý“ˆf /test-2/480worldanyoneBOý“c~×ý“ˆf /test-4/469worldanyoneBOý“cŠßý“ˆf /test-8/477worldanyoneBOý“cŠàý“ˆf /test-9/478worldanyoneBOý“ba ý“ˆg /test-6/525worldanyoneBOý“ba!ý“ˆi /test-1/529worldanyoneBOý“czß"ý“ˆi /test-3/477worldanyoneBOý“czÜ#ý“ˆj /test-5/474worldanyoneBOý“czÜ$ý“ˆj /test-7/474worldanyoneBOý“c~ê%ý“ˆn /test-0/488worldanyoneBOý“ba&ý“ˆq /test-6/526worldanyoneBOý“c~Ø'ý“ˆq /test-4/470worldanyoneBOý“cŠà(ý“ˆq /test-8/478worldanyoneBOý“cŠá)ý“ˆq /test-9/479worldanyoneBOý“c~ã*ý“ˆr /test-2/481worldanyoneBOý“ba+ý“ˆt /test-1/530worldanyoneBOý“czÝ,ý“ˆt /test-7/475worldanyoneBOý“czà-ý“ˆu /test-3/478worldanyoneBOý“czÝ.ý“ˆu /test-5/475worldanyoneBOý“c~ë/ý“ˆw /test-0/489worldanyoneBOý“ba0ý“ˆz /test-6/527worldanyoneBOý“c~ä1ý“ˆ{ /test-2/482worldanyoneBOý“cŠâ2ý“ˆ{ /test-9/480worldanyoneBOý“cŠá3ý“ˆ{ /test-8/479worldanyoneBOý“c~Ù4ý“ˆ{ /test-4/471worldanyoneBOý“ba5ý“ˆ} /test-1/531worldanyoneBOý“czÞ6ý“ˆ~ /test-7/476worldanyoneBOý“czá7ý“ˆ~ /test-3/479worldanyoneBOý“czÞ8ý“ˆ /test-5/476worldanyoneBOý“c~ì9ý“ˆ /test-0/490worldanyoneBOý“c~å:ý“ˆ† /test-2/483worldanyoneBOý“ba;ý“ˆ† /test-6/528worldanyoneBOý“c~Ú<ý“ˆ‡ /test-4/472worldanyoneBOý“cŠã=ý“ˆ‡ /test-9/481worldanyoneBOý“cŠâ>ý“ˆ‡ /test-8/480worldanyoneBOý“ba?ý“ˆˆ /test-1/532worldanyoneBOý“czß@ý“ˆˆ /test-7/477worldanyoneBOý“czßAý“ˆ‰ /test-5/477worldanyoneBOý“czâBý“ˆ‰ /test-3/480worldanyoneBOý“c~íCý“ˆŠ /test-0/491worldanyoneBOý“baDý“ˆ /test-6/529worldanyoneBOý“c~æEý“ˆ /test-2/484worldanyoneBOý“baFý“ˆ“ /test-1/533worldanyoneBOý“czàGý“ˆ“ /test-7/478worldanyoneBOý“cŠäHý“ˆ” /test-9/482worldanyoneBOý“cŠãIý“ˆ” /test-8/481worldanyoneBOý“czàJý“ˆ” /test-5/478worldanyoneBOý“c~ÛKý“ˆ” /test-4/473worldanyoneBOý“czãLý“ˆ• /test-3/481worldanyoneBOý“c~îMý“ˆ• /test-0/492worldanyoneBOý“baNý“ˆ™ /test-6/530worldanyoneBOý“c~çOý“ˆ™ /test-2/485worldanyoneBOý“baPý“ˆ /test-1/534worldanyoneBOý“czáQý“ˆ /test-7/479worldanyoneBOý“c~ïRý“ˆž /test-0/493worldanyoneBOý“czäSý“ˆž /test-3/482worldanyoneBOý“czáTý“ˆž /test-5/479worldanyoneBOý“c~ÜUý“ˆŸ /test-4/474worldanyoneBOý“cŠåVý“ˆŸ /test-9/483worldanyoneBOý“cŠäWý“ˆŸ /test-8/482worldanyoneBOý“baXý“ˆ¢ /test-6/531worldanyoneBOý“c~èYý“ˆ¢ /test-2/486worldanyoneBOý“baZý“ˆ¨ /test-1/535worldanyoneBOý“czâ[ý“ˆ¨ /test-7/480worldanyoneBOý“c~ð\ý“ˆª /test-0/494worldanyoneBOý“c~Ý]ý“ˆª /test-4/475worldanyoneBOý“cŠå^ý“ˆ« /test-8/483worldanyoneBOý“cŠæ_ý“ˆ« /test-9/484worldanyoneBOý“czå`ý“ˆ« /test-3/483worldanyoneBOý“czâaý“ˆ« /test-5/480worldanyoneBOý“babý“ˆ¬ /test-6/532worldanyoneBOý“c~écý“ˆ­ /test-2/487worldanyoneBOý“badý“ˆ± /test-1/536worldanyoneBOý“czãeý“ˆ± /test-7/481worldanyoneBOý“c~ñfý“ˆµ /test-0/495worldanyoneBOý“c~êgý“ˆ· /test-2/488worldanyoneBOý“c~Þhý“ˆ· /test-4/476worldanyoneBOý“baiý“ˆ· /test-6/533worldanyoneBOý“cŠçjý“ˆ· /test-9/485worldanyoneBOý“cŠæký“ˆ¸ /test-8/484worldanyoneBOý“czãlý“ˆ¸ /test-5/481worldanyoneBOý“czæmý“ˆ¸ /test-3/484worldanyoneBOý“baný“ˆº /test-1/537worldanyoneBOý“czäoý“ˆº /test-7/482worldanyoneBOý“c~òpý“ˆ½ /test-0/496worldanyoneBOý“c~ëqý“ˆÁ /test-2/489worldanyoneBOý“barý“ˆÂ /test-6/534worldanyoneBOý“c~ßsý“ˆÂ /test-4/477worldanyoneBOý“cŠçtý“ˆÂ /test-8/485worldanyoneBOý“cŠèuý“ˆÂ /test-9/486worldanyoneBOý“czävý“ˆÃ /test-5/482worldanyoneBOý“bawý“ˆÃ /test-1/538worldanyoneBOý“czçxý“ˆÄ /test-3/485worldanyoneBOý“czåyý“ˆÄ /test-7/483worldanyoneBOý“c~ózý“ˆÆ /test-0/497worldanyoneBOý“c~ì{ý“ˆÊ /test-2/490worldanyoneBOý“ba|ý“ˆÎ /test-1/539worldanyoneBOý“cŠé}ý“ˆÎ /test-9/487worldanyoneBOý“cŠè~ý“ˆÏ /test-8/486worldanyoneBOý“baý“ˆÏ /test-6/535worldanyoneBOý“c~à€ý“ˆÏ /test-4/478worldanyoneBOý“czåý“ˆÏ /test-5/483worldanyoneBOý“czæ‚ý“ˆÏ /test-7/484worldanyoneBOý“c~ôƒý“ˆÐ /test-0/498worldanyoneBOý“czè„ý“ˆÐ /test-3/486worldanyoneBOý“c~í…ý“ˆÖ /test-2/491worldanyoneBOý“ba†ý“ˆØ /test-1/540worldanyoneBOý“ba‡ý“ˆÝ /test-6/536worldanyoneBOý“czçˆý“ˆÞ /test-7/485worldanyoneBOý“czæ‰ý“ˆß /test-5/484worldanyoneBOý“czéŠý“ˆß /test-3/487worldanyoneBOý“cŠê‹ý“ˆß /test-9/488worldanyoneBOý“c~áŒý“ˆà /test-4/479worldanyoneBOý“c~õý“ˆà /test-0/499worldanyoneBOý“baŽý“ˆá /test-1/541worldanyoneBOý“cŠéý“ˆâ /test-8/487worldanyoneBOý“c~îý“ˆâ /test-2/492worldanyoneBOý“ba ‘ý“ˆî /test-1/542worldanyoneBOý“ba’ý“ˆð /test-6/537worldanyoneBOý“czè“ý“ˆñ /test-7/486worldanyoneBOý“cŠë”ý“ˆñ /test-9/489worldanyoneBOý“c~â•ý“ˆò /test-4/480worldanyoneBOý“cŠê–ý“ˆò /test-8/488worldanyoneBOý“czê—ý“ˆó /test-3/488worldanyoneBOý“czç˜ý“ˆó /test-5/485worldanyoneBOý“c~ï™ý“ˆó /test-2/493worldanyoneBOý“c~öšý“ˆó /test-0/500worldanyoneBOý“ba!›ý“ˆû /test-1/543worldanyoneBOý“baœý“ˆü /test-6/538worldanyoneBOý“cŠìý“‰ /test-9/490worldanyoneBOý“cŠëžý“‰ /test-8/489worldanyoneBOý“czèŸý“‰ /test-5/486worldanyoneBOý“czé ý“‰ /test-7/487worldanyoneBOý“c~ã¡ý“‰ /test-4/481worldanyoneBOý“c~ð¢ý“‰ /test-2/494worldanyoneBOý“c~÷£ý“‰ /test-0/501worldanyoneBOý“czë¤ý“‰ /test-3/489worldanyoneBOý“ba"¥ý“‰ /test-1/544worldanyoneBOý“ba¦ý“‰ /test-6/539worldanyoneBOý“czé§ý“‰  /test-5/487worldanyoneBOý“czê¨ý“‰ /test-7/488worldanyoneBOý“cŠì©ý“‰ /test-8/490worldanyoneBOý“cŠíªý“‰ /test-9/491worldanyoneBOý“ba#«ý“‰ /test-1/545worldanyoneBOý“ba¬ý“‰ /test-6/540worldanyoneBOý“c~ñ­ý“‰ /test-2/495worldanyoneBOý“c~ä®ý“‰ /test-4/482worldanyoneBOý“czì¯ý“‰ /test-3/490worldanyoneBOý“czê°ý“‰ /test-5/488worldanyoneBOý“c~ø±ý“‰ /test-0/502worldanyoneBOý“czë²ý“‰ /test-7/489worldanyoneBOý“cŠí³ý“‰ /test-8/491worldanyoneBOý“cŠî´ý“‰ /test-9/492worldanyoneBOý“ba$µý“‰" /test-1/546worldanyoneBOý“ba¶ý“‰" /test-6/541worldanyoneBOý“c~ò·ý“‰# /test-2/496worldanyoneBOý“c~å¸ý“‰# /test-4/483worldanyoneBOý“c~ù¹ý“‰% /test-0/503worldanyoneBOý“czíºý“‰% /test-3/491worldanyoneBOý“czë»ý“‰& /test-5/489worldanyoneBOý“cŠî¼ý“‰( /test-8/492worldanyoneBOý“czì½ý“‰( /test-7/490worldanyoneBOý“cŠï¾ý“‰, /test-9/493worldanyoneBOý“c~æ¿ý“‰0 /test-4/484worldanyoneBOý“c~úÀý“‰0 /test-0/504worldanyoneBOý“c~óÁý“‰0 /test-2/497worldanyoneBOý“czîÂý“‰1 /test-3/492worldanyoneBOý“ba%Ãý“‰2 /test-1/547worldanyoneBOý“ba Äý“‰2 /test-6/542worldanyoneBOý“czìÅý“‰3 /test-5/490worldanyoneBOý“cŠïÆý“‰5 /test-8/493worldanyoneBOý“czíÇý“‰5 /test-7/491worldanyoneBOý“cŠðÈý“‰6 /test-9/494worldanyoneBOý“c~ûÉý“‰? /test-0/505worldanyoneBOý“c~çÊý“‰@ /test-4/485worldanyoneBOý“ba!Ëý“‰@ /test-6/543worldanyoneBOý“ba&Ìý“‰A /test-1/548worldanyoneBOý“c~ôÍý“‰A /test-2/498worldanyoneBOý“cŠñÎý“‰B /test-9/495worldanyoneBOý“cŠðÏý“‰B /test-8/494worldanyoneBOý“czïÐý“‰C /test-3/493worldanyoneBOý“czíÑý“‰C /test-5/491worldanyoneBOý“czîÒý“‰D /test-7/492worldanyoneBOý“c~üÓý“‰O /test-0/506worldanyoneBOý“c~èÔý“‰P /test-4/486worldanyoneBOý“cŠñÕý“‰P /test-8/495worldanyoneBOý“cŠòÖý“‰Q /test-9/496worldanyoneBOý“c~õ×ý“‰Q /test-2/499worldanyoneBOý“czïØý“‰Q /test-7/493worldanyoneBOý“ba"Ùý“‰Q /test-6/544worldanyoneBOý“ba'Úý“‰Q /test-1/549worldanyoneBOý“czðÛý“‰Q /test-3/494worldanyoneBOý“czîÜý“‰R /test-5/492worldanyoneBOý“c~ýÝý“‰] /test-0/507worldanyoneBOý“ba#Þý“‰^ /test-6/545worldanyoneBOý“ba(ßý“‰_ /test-1/550worldanyoneBOý“c~éàý“‰_ /test-4/487worldanyoneBOý“c~öáý“‰_ /test-2/500worldanyoneBOý“cŠòâý“‰_ /test-8/496worldanyoneBOý“cŠóãý“‰` /test-9/497worldanyoneBOý“czñäý“‰` /test-3/495worldanyoneBOý“czðåý“‰` /test-7/494worldanyoneBOý“czïæý“‰a /test-5/493worldanyoneBOý“c~þçý“‰g /test-0/508worldanyoneBOý“ba$èý“‰k /test-6/546worldanyoneBOý“ba)éý“‰l /test-1/551worldanyoneBOý“cŠóêý“‰l /test-8/497worldanyoneBOý“cŠôëý“‰l /test-9/498worldanyoneBOý“c~÷ìý“‰l /test-2/501worldanyoneBOý“c~êíý“‰l /test-4/488worldanyoneBOý“czòîý“‰m /test-3/496worldanyoneBOý“czðïý“‰m /test-5/494worldanyoneBOý“czñðý“‰m /test-7/495worldanyoneBOý“c~ÿñý“‰o /test-0/509worldanyoneBOý“ba%òý“‰u /test-6/547worldanyoneBOý“ba*óý“‰x /test-1/552worldanyoneBOý“cŠôôý“‰y /test-8/498worldanyoneBOý“cŠõõý“‰z /test-9/499worldanyoneBOý“czóöý“‰z /test-3/497worldanyoneBOý“czñ÷ý“‰z /test-5/495worldanyoneBOý“czòøý“‰z /test-7/496worldanyoneBOý“c~ùý“‰{ /test-0/510worldanyoneBOý“c~øúý“‰{ /test-2/502worldanyoneBOý“c~ëûý“‰{ /test-4/489worldanyoneBOý“ba&üý“‰} /test-6/548worldanyoneBOý“ba+ýý“‰ /test-1/553worldanyoneBOý“cŠõþý“‰… /test-8/499worldanyoneBOý“cŠöÿý“‰† /test-9/500worldanyoneBOý“c~ìý“‰† /test-4/490worldanyoneBOý“c~ùý“‰† /test-2/503worldanyoneBOý“czòý“‰‡ /test-5/496worldanyoneBOý“czôý“‰‡ /test-3/498worldanyoneBOý“czóý“‰‡ /test-7/497worldanyoneBOý“c~ý“‰‡ /test-0/511worldanyoneBOý“ba'ý“‰‡ /test-6/549worldanyoneBOý“ba,ý“‰Š /test-1/554worldanyoneBOý“cŠöý“‰ /test-8/500worldanyoneBOý“c~í ý“‰‘ /test-4/491worldanyoneBOý“cŠ÷ ý“‰’ /test-9/501worldanyoneBOý“czõ ý“‰’ /test-3/499worldanyoneBOý“ba( ý“‰’ /test-6/550worldanyoneBOý“czô ý“‰’ /test-7/498worldanyoneBOý“czóý“‰’ /test-5/497worldanyoneBOý“ba-ý“‰” /test-1/555worldanyoneBOý“c~ý“‰” /test-0/512worldanyoneBOý“c~úý“‰” /test-2/504worldanyoneBOý“cŠ÷ý“‰™ /test-8/501worldanyoneBOý“ba)ý“‰ž /test-6/551worldanyoneBOý“c~îý“‰ž /test-4/492worldanyoneBOý“cŠøý“‰Ÿ /test-9/502worldanyoneBOý“ba.ý“‰Ÿ /test-1/556worldanyoneBOý“c~ûý“‰Ÿ /test-2/505worldanyoneBOý“czöý“‰  /test-3/500worldanyoneBOý“czôý“‰  /test-5/498worldanyoneBOý“czõý“‰  /test-7/499worldanyoneBOý“c~ý“‰¡ /test-0/513worldanyoneBOý“cŠøý“‰¢ /test-8/502worldanyoneBOý“ba*ý“‰ª /test-6/552worldanyoneBOý“c~ïý“‰ª /test-4/493worldanyoneBOý“ba/ý“‰« /test-1/557worldanyoneBOý“cŠù ý“‰¬ /test-9/503worldanyoneBOý“cŠù!ý“‰­ /test-8/503worldanyoneBOý“czõ"ý“‰­ /test-5/499worldanyoneBOý“czö#ý“‰­ /test-7/500worldanyoneBOý“cz÷$ý“‰­ /test-3/501worldanyoneBOý“c~ü%ý“‰® /test-2/506worldanyoneBOý“c~&ý“‰® /test-0/514worldanyoneBOý“ba+'ý“‰µ /test-6/553worldanyoneBOý“c~ð(ý“‰µ /test-4/494worldanyoneBOý“ba0)ý“‰¶ /test-1/558worldanyoneBOý“c~ý*ý“‰¹ /test-2/507worldanyoneBOý“c~+ý“‰¹ /test-0/515worldanyoneBOý“czø,ý“‰¹ /test-3/502worldanyoneBOý“czö-ý“‰¹ /test-5/500worldanyoneBOý“cz÷.ý“‰¹ /test-7/501worldanyoneBOý“cŠú/ý“‰º /test-9/504worldanyoneBOý“cŠú0ý“‰º /test-8/504worldanyoneBOý“ba11ý“‰¾ /test-1/559worldanyoneBOý“ba,2ý“‰¾ /test-6/554worldanyoneBOý“c~ñ3ý“‰¾ /test-4/495worldanyoneBOý“cŠû4ý“‰Ä /test-9/505worldanyoneBOý“c~þ5ý“‰Ä /test-2/508worldanyoneBOý“c~6ý“‰Ä /test-0/516worldanyoneBOý“czù7ý“‰Ä /test-3/503worldanyoneBOý“cz÷8ý“‰Å /test-5/501worldanyoneBOý“cŠû9ý“‰Å /test-8/505worldanyoneBOý“czø:ý“‰Å /test-7/502worldanyoneBOý“ba2;ý“‰Ç /test-1/560worldanyoneBOý“c~ò<ý“‰Ç /test-4/496worldanyoneBOý“ba-=ý“‰Ç /test-6/555worldanyoneBOý“c~>ý“‰Ñ /test-0/517worldanyoneBOý“c~ÿ?ý“‰Ñ /test-2/509worldanyoneBOý“ba3@ý“‰Ò /test-1/561worldanyoneBOý“ba.Aý“‰Ò /test-6/556worldanyoneBOý“cŠüBý“‰Ó /test-9/506worldanyoneBOý“c~óCý“‰Ó /test-4/497worldanyoneBOý“czùDý“‰Ó /test-7/503worldanyoneBOý“czúEý“‰Ó /test-3/504worldanyoneBOý“cŠüFý“‰Ô /test-8/506worldanyoneBOý“czøGý“‰Ô /test-5/502worldanyoneBOý“c~Hý“‰Û /test-2/510worldanyoneBOý“c~Iý“‰Û /test-0/518worldanyoneBOý“ba/Jý“‰Ý /test-6/557worldanyoneBOý“ba4Ký“‰Ý /test-1/562worldanyoneBOý“cŠýLý“‰Ý /test-9/507worldanyoneBOý“cŠýMý“‰Þ /test-8/507worldanyoneBOý“czùNý“‰Þ /test-5/503worldanyoneBOý“czûOý“‰Þ /test-3/505worldanyoneBOý“c~ôPý“‰ß /test-4/498worldanyoneBOý“czúQý“‰ß /test-7/504worldanyoneBOý“c~Rý“‰ä /test-2/511worldanyoneBOý“c~ Sý“‰ä /test-0/519worldanyoneBOý“ba0Tý“‰ê /test-6/558worldanyoneBOý“ba5Uý“‰ê /test-1/563worldanyoneBOý“cŠþVý“‰ê /test-8/508worldanyoneBOý“cŠþWý“‰ê /test-9/508worldanyoneBOý“czüXý“‰ê /test-3/506worldanyoneBOý“czûYý“‰ê /test-7/505worldanyoneBOý“czúZý“‰ë /test-5/504worldanyoneBOý“c~õ[ý“‰ë /test-4/499worldanyoneBOý“c~ \ý“‰í /test-0/520worldanyoneBOý“c~]ý“‰í /test-2/512worldanyoneBOý“ba1^ý“‰ô /test-6/559worldanyoneBOý“ba6_ý“‰ô /test-1/564worldanyoneBOý“czû`ý“‰ô /test-5/505worldanyoneBOý“cŠÿaý“‰õ /test-8/509worldanyoneBOý“czýbý“‰õ /test-3/507worldanyoneBOý“cŠÿcý“‰ö /test-9/509worldanyoneBOý“czüdý“‰÷ /test-7/506worldanyoneBOý“c~öeý“‰÷ /test-4/500worldanyoneBOý“c~fý“‰ø /test-2/513worldanyoneBOý“c~ gý“‰ø /test-0/521worldanyoneBOý“ba2hý“‰ý /test-6/560worldanyoneBOý“ba7iý“‰ý /test-1/565worldanyoneBOý“czüjý“‰þ /test-5/506worldanyoneBOý“cŠký“‰þ /test-8/510worldanyoneBOý“cŠlý“Š /test-9/510worldanyoneBOý“c~ mý“Š /test-0/522worldanyoneBOý“czþný“Š /test-3/508worldanyoneBOý“czýoý“Š /test-7/507worldanyoneBOý“c~÷pý“Š /test-4/501worldanyoneBOý“c~qý“Š /test-2/514worldanyoneBOý“czýrý“Š /test-5/507worldanyoneBOý“ba3sý“Š /test-6/561worldanyoneBOý“ba8tý“Š /test-1/566worldanyoneBOý“cŠuý“Š /test-8/511worldanyoneBOý“cŠvý“Š  /test-9/511worldanyoneBOý“czþwý“Š  /test-7/508worldanyoneBOý“c~xý“Š  /test-2/515worldanyoneBOý“c~ yý“Š  /test-0/523worldanyoneBOý“c~øzý“Š  /test-4/502worldanyoneBOý“czÿ{ý“Š  /test-3/509worldanyoneBOý“ba9|ý“Š /test-1/567worldanyoneBOý“ba4}ý“Š /test-6/562worldanyoneBOý“czþ~ý“Š /test-5/508worldanyoneBOý“cŠý“Š /test-8/512worldanyoneBOý“cŠ€ý“Š /test-9/512worldanyoneBOý“czÿý“Š /test-7/509worldanyoneBOý“c~ù‚ý“Š /test-4/503worldanyoneBOý“c~ƒý“Š /test-0/524worldanyoneBOý“cz„ý“Š /test-3/510worldanyoneBOý“c~…ý“Š /test-2/516worldanyoneBOý“ba5†ý“Š /test-6/563worldanyoneBOý“ba:‡ý“Š /test-1/568worldanyoneBOý“czÿˆý“Š /test-5/509worldanyoneBOý“cЉý“Š /test-8/513worldanyoneBOý“cŠŠý“Š /test-9/513worldanyoneBOý“cz‹ý“Š  /test-7/510worldanyoneBOý“czŒý“Š! /test-3/511worldanyoneBOý“c~úý“Š" /test-4/504worldanyoneBOý“c~Žý“Š" /test-0/525worldanyoneBOý“c~ý“Š" /test-2/517worldanyoneBOý“czý“Š$ /test-5/510worldanyoneBOý“cŠ‘ý“Š$ /test-8/514worldanyoneBOý“ba;’ý“Š$ /test-1/569worldanyoneBOý“ba6“ý“Š% /test-6/564worldanyoneBOý“cŠ”ý“Š' /test-9/514worldanyoneBOý“cz•ý“Š( /test-7/511worldanyoneBOý“cz–ý“Š* /test-3/512worldanyoneBOý“ba<—ý“Š- /test-1/570worldanyoneBOý“ba7˜ý“Š/ /test-6/565worldanyoneBOý“c~û™ý“Š/ /test-4/505worldanyoneBOý“c~šý“Š/ /test-0/526worldanyoneBOý“cz›ý“Š/ /test-5/511worldanyoneBOý“cŠœý“Š/ /test-8/515worldanyoneBOý“c~ý“Š0 /test-2/518worldanyoneBOý“cŠžý“Š0 /test-9/515worldanyoneBOý“czŸý“Š1 /test-7/512worldanyoneBOý“cz ý“Š2 /test-3/513worldanyoneBOý“ba=¡ý“Š6 /test-1/571worldanyoneBOý“ba8¢ý“Š; /test-6/566worldanyoneBOý“c~ü£ý“Š; /test-4/506worldanyoneBOý“c~¤ý“Š; /test-0/527worldanyoneBOý“cŠ¥ý“Š; /test-8/516worldanyoneBOý“c~ ¦ý“Š; /test-2/519worldanyoneBOý“cz§ý“Š; /test-7/513worldanyoneBOý“cz¨ý“Š< /test-5/512worldanyoneBOý“cz©ý“Š< /test-3/514worldanyoneBOý“cŠªý“Š< /test-9/516worldanyoneBOý“ba>«ý“Š? /test-1/572worldanyoneBOý“ba9¬ý“ŠD /test-6/567worldanyoneBOý“cŠ­ý“ŠG /test-8/517worldanyoneBOý“c~ý®ý“ŠG /test-4/507worldanyoneBOý“c~¯ý“ŠG /test-0/528worldanyoneBOý“c~ °ý“ŠG /test-2/520worldanyoneBOý“cz±ý“ŠG /test-5/513worldanyoneBOý“cz²ý“ŠG /test-7/514worldanyoneBOý“cz³ý“ŠG /test-3/515worldanyoneBOý“ba?´ý“ŠH /test-1/573worldanyoneBOý“cеý“ŠH /test-9/517worldanyoneBOý“ba:¶ý“ŠL /test-6/568worldanyoneBOý“ba@·ý“ŠQ /test-1/574worldanyoneBOý“cЏý“ŠR /test-8/518worldanyoneBOý“cz¹ý“ŠS /test-7/515worldanyoneBOý“cŠºý“ŠS /test-9/518worldanyoneBOý“cz»ý“ŠS /test-5/514worldanyoneBOý“cz¼ý“ŠS /test-3/516worldanyoneBOý“c~þ½ý“ŠS /test-4/508worldanyoneBOý“c~¾ý“ŠS /test-0/529worldanyoneBOý“c~ ¿ý“ŠS /test-2/521worldanyoneBOý“ba;Àý“ŠU /test-6/569worldanyoneBOý“baAÁý“Š[ /test-1/575worldanyoneBOý“cŠ Âý“Š\ /test-8/519worldanyoneBOý“czÃý“Š^ /test-7/516worldanyoneBOý“cŠ Äý“Š^ /test-9/519worldanyoneBOý“ba<Åý“Š^ /test-6/570worldanyoneBOý“czÆý“Š` /test-3/517worldanyoneBOý“czÇý“Š` /test-5/515worldanyoneBOý“c~ÿÈý“Š` /test-4/509worldanyoneBOý“c~ Éý“Š` /test-2/522worldanyoneBOý“c~Êý“Š` /test-0/530worldanyoneBOý“baBËý“Šd /test-1/576worldanyoneBOý“cŠ Ìý“Še /test-8/520worldanyoneBOý“ba=Íý“Šh /test-6/571worldanyoneBOý“cŠ Îý“Šh /test-9/520worldanyoneBOý“czÏý“Šh /test-7/517worldanyoneBOý“c~Ðý“Šk /test-0/531worldanyoneBOý“czÑý“Šk /test-5/516worldanyoneBOý“c~ Òý“Šk /test-2/523worldanyoneBOý“c~Óý“Šk /test-4/510worldanyoneBOý“czÔý“Šk /test-3/518worldanyoneBOý“baCÕý“Šm /test-1/577worldanyoneBOý“cŠ Öý“Šm /test-8/521worldanyoneBOý“ba>×ý“Šo /test-6/572worldanyoneBOý“czØý“Šq /test-7/518worldanyoneBOý“cŠ Ùý“Šq /test-9/521worldanyoneBOý“czÚý“Šu /test-5/517worldanyoneBOý“cz Ûý“Šu /test-3/519worldanyoneBOý“c~Üý“Šu /test-0/532worldanyoneBOý“c~Ýý“Šu /test-4/511worldanyoneBOý“c~Þý“Šu /test-2/524worldanyoneBOý“baDßý“Šz /test-1/578worldanyoneBOý“cŠ àý“Š{ /test-8/522worldanyoneBOý“ba?áý“Š{ /test-6/573worldanyoneBOý“cŠ âý“Š| /test-9/522worldanyoneBOý“cz ãý“Š| /test-7/519worldanyoneBOý“c~äý“Š /test-2/525worldanyoneBOý“czåý“Š /test-5/518worldanyoneBOý“cz æý“Š /test-3/520worldanyoneBOý“c~çý“Š /test-0/533worldanyoneBOý“c~èý“Š€ /test-4/512worldanyoneBOý“baEéý“Š„ /test-1/579worldanyoneBOý“cŠ êý“Š… /test-8/523worldanyoneBOý“cz ëý“І /test-7/520worldanyoneBOý“ba@ìý“Ї /test-6/574worldanyoneBOý“cŠ íý“Ї /test-9/523worldanyoneBOý“cz îý“ŠŠ /test-3/521worldanyoneBOý“c~ïý“ŠŠ /test-4/513worldanyoneBOý“c~ðý“ŠŠ /test-2/526worldanyoneBOý“c~ñý“Š‹ /test-0/534worldanyoneBOý“cz òý“Š‹ /test-5/519worldanyoneBOý“baFóý“ŠŽ /test-1/580worldanyoneBOý“cz ôý“Š /test-7/521worldanyoneBOý“cŠõý“Š /test-8/524worldanyoneBOý“baAöý“Š /test-6/575worldanyoneBOý“cŠ÷ý“Š‘ /test-9/524worldanyoneBOý“c~øý“Š” /test-0/535worldanyoneBOý“c~ùý“Š” /test-2/527worldanyoneBOý“cz úý“Š” /test-3/522worldanyoneBOý“cz ûý“Š• /test-5/520worldanyoneBOý“c~üý“Š• /test-4/514worldanyoneBOý“baGýý“Š– /test-1/581worldanyoneBOý“cz þý“Š™ /test-7/522worldanyoneBOý“baBÿý“Šš /test-6/576worldanyoneBOý“cŠý“Šš /test-8/525worldanyoneBOý“cŠý“Š› /test-9/525worldanyoneBOý“c~ý“ŠŸ /test-2/528worldanyoneBOý“c~ý“ŠŸ /test-0/536worldanyoneBOý“cz ý“ŠŸ /test-5/521worldanyoneBOý“cz ý“ŠŸ /test-3/523worldanyoneBOý“c~ý“ŠŸ /test-4/515worldanyoneBOý“baHý“Š  /test-1/582worldanyoneBOý“cz ý“Š¢ /test-7/523worldanyoneBOý“baC ý“Ф /test-6/577worldanyoneBOý“cŠ ý“Ф /test-9/526worldanyoneBOý“cŠ ý“Š¥ /test-8/526worldanyoneBOý“c~ ý“Š© /test-2/529worldanyoneBOý“baI ý“Šª /test-1/583worldanyoneBOý“czý“Š« /test-3/524worldanyoneBOý“czý“Š« /test-7/524worldanyoneBOý“cz ý“Š« /test-5/522worldanyoneBOý“c~ý“Ь /test-0/537worldanyoneBOý“c~ý“Ь /test-4/516worldanyoneBOý“cŠý“Š® /test-8/527worldanyoneBOý“cŠý“Š® /test-9/527worldanyoneBOý“baDý“Š® /test-6/578worldanyoneBOý“c~ý“б /test-2/530worldanyoneBOý“c~ý“ж /test-0/538worldanyoneBOý“baJý“ж /test-1/584worldanyoneBOý“cz ý“ж /test-5/523worldanyoneBOý“czý“ж /test-3/525worldanyoneBOý“czý“ж /test-7/525worldanyoneBOý“c~ý“Š· /test-4/517worldanyoneBOý“cŠý“Џ /test-8/528worldanyoneBOý“baEý“Џ /test-6/579worldanyoneBOý“cŠý“Џ /test-9/528worldanyoneBOý“c~ ý“Šº /test-2/531worldanyoneBOý“baK!ý“ŠÀ /test-1/585worldanyoneBOý“c~"ý“ŠÀ /test-0/539worldanyoneBOý“cz#ý“ŠÀ /test-3/526worldanyoneBOý“cz$ý“ŠÀ /test-5/524worldanyoneBOý“c~%ý“ŠÁ /test-4/518worldanyoneBOý“cz&ý“ŠÁ /test-7/526worldanyoneBOý“baF'ý“ŠÃ /test-6/580worldanyoneBOý“cŠ(ý“ŠÃ /test-9/529worldanyoneBOý“c~)ý“ŠÃ /test-2/532worldanyoneBOý“cŠ*ý“ŠÄ /test-8/529worldanyoneBOý“baL+ý“ŠÈ /test-1/586worldanyoneBOý“c~,ý“ŠÉ /test-0/540worldanyoneBOý“c~ -ý“ŠË /test-4/519worldanyoneBOý“cz.ý“ŠË /test-5/525worldanyoneBOý“cz/ý“ŠË /test-3/527worldanyoneBOý“cz0ý“ŠË /test-7/527worldanyoneBOý“baG1ý“ŠÎ /test-6/581worldanyoneBOý“c~2ý“ŠÎ /test-2/533worldanyoneBOý“cŠ3ý“ŠÎ /test-9/530worldanyoneBOý“cŠ4ý“ŠÏ /test-8/530worldanyoneBOý“baM5ý“ŠÐ /test-1/587worldanyoneBOý“c~6ý“ŠÒ /test-0/541worldanyoneBOý“c~ 7ý“ŠÔ /test-4/520worldanyoneBOý“cz8ý“ŠÕ /test-5/526worldanyoneBOý“cz9ý“ŠÕ /test-3/528worldanyoneBOý“cz:ý“ŠÕ /test-7/528worldanyoneBOý“baH;ý“Š× /test-6/582worldanyoneBOý“c~<ý“ŠØ /test-2/534worldanyoneBOý“cŠ=ý“ŠØ /test-9/531worldanyoneBOý“cŠ>ý“ŠØ /test-8/531worldanyoneBOý“baN?ý“ŠÜ /test-1/588worldanyoneBOý“c~ @ý“ŠÝ /test-0/542worldanyoneBOý“czAý“Šß /test-5/527worldanyoneBOý“czBý“Šà /test-3/529worldanyoneBOý“c~ Cý“Šà /test-4/521worldanyoneBOý“czDý“Šà /test-7/529worldanyoneBOý“baIEý“Šà /test-6/583worldanyoneBOý“cŠFý“Šâ /test-9/532worldanyoneBOý“c~Gý“Šâ /test-2/535worldanyoneBOý“cŠHý“Šâ /test-8/532worldanyoneBOý“baOIý“Šå /test-1/589worldanyoneBOý“c~!Jý“Šæ /test-0/543worldanyoneBOý“czKý“Šé /test-5/528worldanyoneBOý“cŠLý“Šì /test-9/533worldanyoneBOý“cŠMý“Šì /test-8/533worldanyoneBOý“baJNý“Šì /test-6/584worldanyoneBOý“c~Oý“Šì /test-2/536worldanyoneBOý“c~ Pý“Šì /test-4/522worldanyoneBOý“czQý“Šì /test-3/530worldanyoneBOý“czRý“Šì /test-7/530worldanyoneBOý“baPSý“Šî /test-1/590worldanyoneBOý“c~"Tý“Šï /test-0/544worldanyoneBOý“czUý“Šñ /test-5/529worldanyoneBOý“cŠVý“Š÷ /test-9/534worldanyoneBOý“baKWý“Šø /test-6/585worldanyoneBOý“czXý“Šø /test-3/531worldanyoneBOý“czYý“Šø /test-7/531worldanyoneBOý“cŠZý“Šø /test-8/534worldanyoneBOý“c~ [ý“Šø /test-4/523worldanyoneBOý“c~\ý“Šø /test-2/537worldanyoneBOý“c~#]ý“Šù /test-0/545worldanyoneBOý“baQ^ý“Šù /test-1/591worldanyoneBOý“cz_ý“Šú /test-5/530worldanyoneBOý“cŠ`ý“Šÿ /test-9/535worldanyoneBOý“baLaý“‹ /test-6/586worldanyoneBOý“czbý“‹ /test-7/532worldanyoneBOý“czcý“‹ /test-5/531worldanyoneBOý“baRdý“‹ /test-1/592worldanyoneBOý“cŠeý“‹ /test-8/535worldanyoneBOý“c~fý“‹ /test-2/538worldanyoneBOý“czgý“‹ /test-3/532worldanyoneBOý“c~hý“‹ /test-4/524worldanyoneBOý“c~$iý“‹ /test-0/546worldanyoneBOý“cŠjý“‹ /test-9/536worldanyoneBOý“czký“‹ /test-7/533worldanyoneBOý“baMlý“‹ /test-6/587worldanyoneBOý“baSmý“‹ /test-1/593worldanyoneBOý“czný“‹ /test-5/532worldanyoneBOý“c~oý“‹ /test-2/539worldanyoneBOý“c~%pý“‹ /test-0/547worldanyoneBOý“cŠqý“‹ /test-8/536worldanyoneBOý“cŠrý“‹ /test-9/537worldanyoneBOý“czsý“‹ /test-3/533worldanyoneBOý“c~tý“‹ /test-4/525worldanyoneBOý“czuý“‹ /test-7/534worldanyoneBOý“baNvý“‹ /test-6/588worldanyoneBOý“baTwý“‹ /test-1/594worldanyoneBOý“czxý“‹ /test-3/534worldanyoneBOý“czyý“‹ /test-5/533worldanyoneBOý“cŠzý“‹ /test-8/537worldanyoneBOý“cŠ{ý“‹ /test-9/538worldanyoneBOý“c~|ý“‹ /test-4/526worldanyoneBOý“c~}ý“‹ /test-2/540worldanyoneBOý“c~&~ý“‹ /test-0/548worldanyoneBOý“czý“‹  /test-7/535worldanyoneBOý“baO€ý“‹& /test-6/589worldanyoneBOý“baUý“‹& /test-1/595worldanyoneBOý“cz‚ý“‹& /test-5/534worldanyoneBOý“czƒý“‹& /test-3/535worldanyoneBOý“c~„ý“‹) /test-4/527worldanyoneBOý“c~…ý“‹) /test-2/541worldanyoneBOý“c~'†ý“‹) /test-0/549worldanyoneBOý“cЇý“‹* /test-9/539worldanyoneBOý“cŠˆý“‹* /test-8/538worldanyoneBOý“cz‰ý“‹* /test-7/536worldanyoneBOý“baPŠý“‹- /test-6/590worldanyoneBOý“baV‹ý“‹/ /test-1/596worldanyoneBOý“czŒý“‹0 /test-5/535worldanyoneBOý“czý“‹1 /test-3/536worldanyoneBOý“c~Žý“‹2 /test-4/528worldanyoneBOý“cŠý“‹3 /test-9/540worldanyoneBOý“c~ ý“‹3 /test-2/542worldanyoneBOý“c~(‘ý“‹3 /test-0/550worldanyoneBOý“cŠ’ý“‹3 /test-8/539worldanyoneBOý“cz“ý“‹3 /test-7/537worldanyoneBOý“baQ”ý“‹6 /test-6/591worldanyoneBOý“cz•ý“‹; /test-3/537worldanyoneBOý“baW–ý“‹; /test-1/597worldanyoneBOý“cz—ý“‹; /test-5/536worldanyoneBOý“c~˜ý“‹; /test-4/529worldanyoneBOý“cŠ™ý“‹= /test-9/541worldanyoneBOý“cŠšý“‹= /test-8/540worldanyoneBOý“c~!›ý“‹= /test-2/543worldanyoneBOý“c~)œý“‹= /test-0/551worldanyoneBOý“czý“‹= /test-7/538worldanyoneBOý“baRžý“‹? /test-6/592worldanyoneBOý“baXŸý“‹F /test-1/598worldanyoneBOý“c~ ý“‹F /test-4/530worldanyoneBOý“cz¡ý“‹F /test-3/538worldanyoneBOý“cz¢ý“‹F /test-5/537worldanyoneBOý“baS£ý“‹H /test-6/593worldanyoneBOý“c~"¤ý“‹H /test-2/544worldanyoneBOý“c~*¥ý“‹H /test-0/552worldanyoneBOý“cЦý“‹H /test-8/541worldanyoneBOý“cz§ý“‹H /test-7/539worldanyoneBOý“cŠ ¨ý“‹H /test-9/542worldanyoneBOý“baY©ý“‹O /test-1/599worldanyoneBOý“c~ªý“‹P /test-4/531worldanyoneBOý“cz«ý“‹Q /test-5/538worldanyoneBOý“cz¬ý“‹Q /test-3/539worldanyoneBOý“baT­ý“‹S /test-6/594worldanyoneBOý“c~+®ý“‹S /test-0/553worldanyoneBOý“c~#¯ý“‹S /test-2/545worldanyoneBOý“cŠ!°ý“‹S /test-9/543worldanyoneBOý“cŠ ±ý“‹S /test-8/542worldanyoneBOý“cz²ý“‹T /test-7/540worldanyoneBOý“baZ³ý“‹X /test-1/600worldanyoneBOý“c~´ý“‹Y /test-4/532worldanyoneBOý“czµý“‹[ /test-5/539worldanyoneBOý“cz¶ý“‹[ /test-3/540worldanyoneBOý“baU·ý“‹^ /test-6/595worldanyoneBOý“cŠ!¸ý“‹^ /test-8/543worldanyoneBOý“cŠ"¹ý“‹^ /test-9/544worldanyoneBOý“c~$ºý“‹^ /test-2/546worldanyoneBOý“c~,»ý“‹_ /test-0/554worldanyoneBOý“cz¼ý“‹_ /test-7/541worldanyoneBOý“ba[½ý“‹a /test-1/601worldanyoneBOý“c~¾ý“‹b /test-4/533worldanyoneBOý“cz¿ý“‹d /test-3/541worldanyoneBOý“czÀý“‹d /test-5/540worldanyoneBOý“baVÁý“‹j /test-6/596worldanyoneBOý“cŠ#Âý“‹Ž /test-9/545worldanyoneBOý“c~-Ãý“‹Ž /test-0/555worldanyoneBOý“c~%Äý“‹Ž /test-2/547worldanyoneBOý“cz Åý“‹Ž /test-7/542worldanyoneBOý“cŠ"Æý“‹Ž /test-8/544worldanyoneBOý“ba\Çý“‹Ž /test-1/602worldanyoneBOý“c~Èý“‹Ž /test-4/534worldanyoneBOý“czÉý“‹ /test-5/541worldanyoneBOý“cz Êý“‹ /test-3/542worldanyoneBOý“baWËý“‹˜ /test-6/597worldanyoneBOý“cŠ$Ìý“‹˜ /test-9/546worldanyoneBOý“ba]Íý“‹› /test-1/603worldanyoneBOý“c~&Îý“‹› /test-2/548worldanyoneBOý“c~.Ïý“‹› /test-0/556worldanyoneBOý“cŠ#Ðý“‹› /test-8/545worldanyoneBOý“cz Ñý“‹œ /test-5/542worldanyoneBOý“c~Òý“‹œ /test-4/535worldanyoneBOý“cz!Óý“‹œ /test-7/543worldanyoneBOý“cz!Ôý“‹œ /test-3/543worldanyoneBOý“baXÕý“‹  /test-6/598worldanyoneBOý“cŠ%Öý“‹¡ /test-9/547worldanyoneBOý“ba^×ý“‹¦ /test-1/604worldanyoneBOý“c~/Øý“‹¦ /test-0/557worldanyoneBOý“c~'Ùý“‹¦ /test-2/549worldanyoneBOý“cŠ$Úý“‹¦ /test-8/546worldanyoneBOý“cz!Ûý“‹¦ /test-5/543worldanyoneBOý“c~Üý“‹§ /test-4/536worldanyoneBOý“cz"Ýý“‹§ /test-3/544worldanyoneBOý“cz"Þý“‹§ /test-7/544worldanyoneBOý“baYßý“‹© /test-6/599worldanyoneBOý“cŠ&àý“‹ª /test-9/548worldanyoneBOý“ba_áý“‹² /test-1/605worldanyoneBOý“cŠ%âý“‹² /test-8/547worldanyoneBOý“cz#ãý“‹² /test-3/545worldanyoneBOý“cz"äý“‹² /test-5/544worldanyoneBOý“cz#åý“‹² /test-7/545worldanyoneBOý“c~(æý“‹³ /test-2/550worldanyoneBOý“c~0çý“‹³ /test-0/558worldanyoneBOý“c~èý“‹³ /test-4/537worldanyoneBOý“baZéý“‹´ /test-6/600worldanyoneBOý“cŠ'êý“‹µ /test-9/549worldanyoneBOý“ba`ëý“‹¼ /test-1/606worldanyoneBOý“cŠ&ìý“‹¼ /test-8/548worldanyoneBOý“c~)íý“‹¾ /test-2/551worldanyoneBOý“cz$îý“‹¾ /test-3/546worldanyoneBOý“ba[ïý“‹¿ /test-6/601worldanyoneBOý“c~1ðý“‹¿ /test-0/559worldanyoneBOý“c~ñý“‹À /test-4/538worldanyoneBOý“cŠ(òý“‹À /test-9/550worldanyoneBOý“cz#óý“‹À /test-5/545worldanyoneBOý“cz$ôý“‹À /test-7/546worldanyoneBOý“baaõý“‹Ä /test-1/607worldanyoneBOý“cŠ'öý“‹Å /test-8/549worldanyoneBOý“c~*÷ý“‹È /test-2/552worldanyoneBOý“cz%øý“‹È /test-3/547worldanyoneBOý“ba\ùý“‹È /test-6/602worldanyoneBOý“c~2úý“‹Ê /test-0/560worldanyoneBOý“c~ûý“‹Ê /test-4/539worldanyoneBOý“cz%üý“‹Ê /test-7/547worldanyoneBOý“cŠ)ýý“‹Ë /test-9/551worldanyoneBOý“cz$þý“‹Ë /test-5/546worldanyoneBOý“babÿý“‹Í /test-1/608worldanyoneBOý“cŠ(ý“‹Î /test-8/550worldanyoneBOý“ba]ý“‹Ñ /test-6/603worldanyoneBOý“cz&ý“‹Ò /test-3/548worldanyoneBOý“c~+ý“‹Ò /test-2/553worldanyoneBOý“cz&ý“‹Ô /test-7/548worldanyoneBOý“c~3ý“‹Ô /test-0/561worldanyoneBOý“c~ý“‹Ô /test-4/540worldanyoneBOý“cŠ*ý“‹Õ /test-9/552worldanyoneBOý“cz%ý“‹Õ /test-5/547worldanyoneBOý“bac ý“‹Ö /test-1/609worldanyoneBOý“cŠ) ý“‹× /test-8/551worldanyoneBOý“ba^ ý“‹Û /test-6/604worldanyoneBOý“cz' ý“‹Ü /test-3/549worldanyoneBOý“c~, ý“‹Ü /test-2/554worldanyoneBOý“cz'ý“‹Ý /test-7/549worldanyoneBOý“c~4ý“‹Ý /test-0/562worldanyoneBOý“c~ý“‹Ý /test-4/541worldanyoneBOý“cŠ+ý“‹ß /test-9/553worldanyoneBOý“cz&ý“‹ß /test-5/548worldanyoneBOý“badý“‹ß /test-1/610worldanyoneBOý“cŠ*ý“‹à /test-8/552worldanyoneBOý“ba_ý“‹ä /test-6/605worldanyoneBOý“c~-ý“‹å /test-2/555worldanyoneBOý“cz(ý“‹å /test-3/550worldanyoneBOý“c~5ý“‹é /test-0/563worldanyoneBOý“cŠ,ý“‹é /test-9/554worldanyoneBOý“cz(ý“‹é /test-7/550worldanyoneBOý“c~ ý“‹é /test-4/542worldanyoneBOý“cz'ý“‹é /test-5/549worldanyoneBOý“cŠ+ý“‹ê /test-8/553worldanyoneBOý“baeý“‹ê /test-1/611worldanyoneBOý“ba`ý“‹í /test-6/606worldanyoneBOý“cz) ý“‹î /test-3/551worldanyoneBOý“c~.!ý“‹î /test-2/556worldanyoneBOý“baf"ý“‹ó /test-1/612worldanyoneBOý“cŠ-#ý“‹ô /test-9/555worldanyoneBOý“c~6$ý“‹ô /test-0/564worldanyoneBOý“c~!%ý“‹ô /test-4/543worldanyoneBOý“cz)&ý“‹ô /test-7/551worldanyoneBOý“cŠ,'ý“‹ô /test-8/554worldanyoneBOý“cz((ý“‹õ /test-5/550worldanyoneBOý“baa)ý“‹ö /test-6/607worldanyoneBOý“c~/*ý“‹÷ /test-2/557worldanyoneBOý“cz*+ý“‹÷ /test-3/552worldanyoneBOý“bag,ý“‹ÿ /test-1/613worldanyoneBOý“c~"-ý“‹ÿ /test-4/544worldanyoneBOý“c~7.ý“‹ÿ /test-0/565worldanyoneBOý“cz*/ý“‹ÿ /test-7/552worldanyoneBOý“cz)0ý“‹ÿ /test-5/551worldanyoneBOý“cŠ.1ý“‹ÿ /test-9/556worldanyoneBOý“cŠ-2ý“‹ÿ /test-8/555worldanyoneBOý“bab3ý“Œ /test-6/608worldanyoneBOý“c~04ý“Œ /test-2/558worldanyoneBOý“cz+5ý“Œ /test-3/553worldanyoneBOý“bah6ý“Œ  /test-1/614worldanyoneBOý“c~87ý“Œ  /test-0/566worldanyoneBOý“cz*8ý“Œ  /test-5/552worldanyoneBOý“cz+9ý“Œ  /test-7/553worldanyoneBOý“c~#:ý“Œ  /test-4/545worldanyoneBOý“bac;ý“Œ  /test-6/609worldanyoneBOý“cŠ.<ý“Œ  /test-8/556worldanyoneBOý“cŠ/=ý“Œ  /test-9/557worldanyoneBOý“c~1>ý“Œ  /test-2/559worldanyoneBOý“cz,?ý“Œ /test-3/554worldanyoneBOý“bai@ý“Œ /test-1/615worldanyoneBOý“badAý“Œ /test-6/610worldanyoneBOý“c~$Bý“Œ /test-4/546worldanyoneBOý“c~2Cý“Œ /test-2/560worldanyoneBOý“cŠ/Dý“Œ /test-8/557worldanyoneBOý“cŠ0Eý“Œ /test-9/558worldanyoneBOý“c~9Fý“Œ /test-0/567worldanyoneBOý“cz+Gý“Œ /test-5/553worldanyoneBOý“cz-Hý“Œ /test-3/555worldanyoneBOý“cz,Iý“Œ /test-7/554worldanyoneBOý“bajJý“Œ /test-1/616worldanyoneBOý“baeKý“Œ  /test-6/611worldanyoneBOý“c~%Lý“Œ! /test-4/547worldanyoneBOý“c~3Mý“Œ$ /test-2/561worldanyoneBOý“c~:Ný“Œ$ /test-0/568worldanyoneBOý“cz-Oý“Œ$ /test-7/555worldanyoneBOý“cz,Pý“Œ$ /test-5/554worldanyoneBOý“cŠ0Qý“Œ$ /test-8/558worldanyoneBOý“cŠ1Rý“Œ$ /test-9/559worldanyoneBOý“cz.Sý“Œ% /test-3/556worldanyoneBOý“bakTý“Œ& /test-1/617worldanyoneBOý“bafUý“Œ( /test-6/612worldanyoneBOý“c~&Vý“Œ* /test-4/548worldanyoneBOý“c~4Wý“Œ. /test-2/562worldanyoneBOý“balXý“Œ/ /test-1/618worldanyoneBOý“cz/Yý“Œ/ /test-3/557worldanyoneBOý“cŠ2Zý“Œ/ /test-9/560worldanyoneBOý“cŠ1[ý“Œ/ /test-8/559worldanyoneBOý“cz-\ý“Œ/ /test-5/555worldanyoneBOý“cz.]ý“Œ0 /test-7/556worldanyoneBOý“c~;^ý“Œ0 /test-0/569worldanyoneBOý“bag_ý“Œ0 /test-6/613worldanyoneBOý“c~'`ý“Œ4 /test-4/549worldanyoneBOý“c~5aý“Œ6 /test-2/563worldanyoneBOý“bahbý“Œ: /test-6/614worldanyoneBOý“bamcý“Œ: /test-1/619worldanyoneBOý“c~<dý“Œ: /test-0/570worldanyoneBOý“cŠ3eý“Œ: /test-9/561worldanyoneBOý“cŠ2fý“Œ: /test-8/560worldanyoneBOý“cz/gý“Œ: /test-7/557worldanyoneBOý“cz0hý“Œ: /test-3/558worldanyoneBOý“cz.iý“Œ; /test-5/556worldanyoneBOý“c~(jý“Œ; /test-4/550worldanyoneBOý“c~6ký“Œ? /test-2/564worldanyoneBOý“banlý“ŒE /test-1/620worldanyoneBOý“baimý“ŒF /test-6/615worldanyoneBOý“c~=ný“ŒF /test-0/571worldanyoneBOý“cŠ3oý“ŒG /test-8/561worldanyoneBOý“cŠ4pý“ŒG /test-9/562worldanyoneBOý“c~)qý“ŒG /test-4/551worldanyoneBOý“cz/rý“ŒG /test-5/557worldanyoneBOý“cz1sý“ŒH /test-3/559worldanyoneBOý“c~7tý“ŒH /test-2/565worldanyoneBOý“cz0uý“ŒH /test-7/558worldanyoneBOý“baový“ŒN /test-1/621worldanyoneBOý“bajwý“ŒO /test-6/616worldanyoneBOý“c~>xý“ŒP /test-0/572worldanyoneBOý“c~*yý“ŒQ /test-4/552worldanyoneBOý“cz0zý“ŒQ /test-5/558worldanyoneBOý“cŠ4{ý“ŒQ /test-8/562worldanyoneBOý“cŠ5|ý“ŒQ /test-9/563worldanyoneBOý“cz2}ý“ŒR /test-3/560worldanyoneBOý“c~8~ý“ŒS /test-2/566worldanyoneBOý“cz1ý“ŒS /test-7/559worldanyoneBOý“bap€ý“ŒW /test-1/622worldanyoneBOý“baký“ŒX /test-6/617worldanyoneBOý“c~?‚ý“ŒY /test-0/573worldanyoneBOý“c~+ƒý“Œ[ /test-4/553worldanyoneBOý“cŠ6„ý“Œ] /test-9/564worldanyoneBOý“cŠ5…ý“Œ] /test-8/563worldanyoneBOý“c~9†ý“Œ] /test-2/567worldanyoneBOý“cz1‡ý“Œ] /test-5/559worldanyoneBOý“cz3ˆý“Œ] /test-3/561worldanyoneBOý“cz2‰ý“Œ] /test-7/560worldanyoneBOý“baqŠý“Œ_ /test-1/623worldanyoneBOý“bal‹ý“Œa /test-6/618worldanyoneBOý“c~@Œý“Œb /test-0/574worldanyoneBOý“c~,ý“Œb /test-4/554worldanyoneBOý“cŠ7Žý“Œh /test-9/565worldanyoneBOý“cz4ý“Œh /test-3/562worldanyoneBOý“cŠ6ý“Œh /test-8/564worldanyoneBOý“cz2‘ý“Œh /test-5/560worldanyoneBOý“c~:’ý“Œh /test-2/568worldanyoneBOý“cz3“ý“Œh /test-7/561worldanyoneBOý“bar”ý“Œi /test-1/624worldanyoneBOý“bam•ý“Œk /test-6/619worldanyoneBOý“c~A–ý“Œl /test-0/575worldanyoneBOý“c~-—ý“Œl /test-4/555worldanyoneBOý“cŠ8˜ý“Œr /test-9/566worldanyoneBOý“bas™ý“Œt /test-1/625worldanyoneBOý“cŠ7šý“Œt /test-8/565worldanyoneBOý“c~;›ý“Œt /test-2/569worldanyoneBOý“cz3œý“Œt /test-5/561worldanyoneBOý“cz5ý“Œt /test-3/563worldanyoneBOý“cz4žý“Œt /test-7/562worldanyoneBOý“banŸý“Œu /test-6/620worldanyoneBOý“c~B ý“Œv /test-0/576worldanyoneBOý“c~.¡ý“Œv /test-4/556worldanyoneBOý“cŠ9¢ý“Œ{ /test-9/567worldanyoneBOý“bat£ý“Œ} /test-1/626worldanyoneBOý“cŠ8¤ý“Œ~ /test-8/566worldanyoneBOý“bao¥ý“Œ€ /test-6/621worldanyoneBOý“c~/¦ý“Œ€ /test-4/557worldanyoneBOý“c~<§ý“Œ€ /test-2/570worldanyoneBOý“c~C¨ý“Œ€ /test-0/577worldanyoneBOý“cz6©ý“Œ /test-3/564worldanyoneBOý“cz4ªý“Œ /test-5/562worldanyoneBOý“cz5«ý“Œ /test-7/563worldanyoneBOý“cŠ:¬ý“Œ„ /test-9/568worldanyoneBOý“bau­ý“Œ† /test-1/627worldanyoneBOý“cŠ9®ý“Œ‡ /test-8/567worldanyoneBOý“bap¯ý“Œ‰ /test-6/622worldanyoneBOý“c~0°ý“ŒŠ /test-4/558worldanyoneBOý“cz5±ý“ŒŠ /test-5/563worldanyoneBOý“cz7²ý“Œ‹ /test-3/565worldanyoneBOý“cz6³ý“Œ‹ /test-7/564worldanyoneBOý“c~D´ý“ŒŒ /test-0/578worldanyoneBOý“c~=µý“ŒŒ /test-2/571worldanyoneBOý“cŠ;¶ý“Œ /test-9/569worldanyoneBOý“bav·ý“Œ /test-1/628worldanyoneBOý“cŠ:¸ý“Œ /test-8/568worldanyoneBOý“baq¹ý“Œ’ /test-6/623worldanyoneBOý“c~1ºý“Œ“ /test-4/559worldanyoneBOý“cŠ<»ý“Œ– /test-9/570worldanyoneBOý“cz8¼ý“Œ– /test-3/566worldanyoneBOý“cz6½ý“Œ– /test-5/564worldanyoneBOý“cz7¾ý“Œ– /test-7/565worldanyoneBOý“c~E¿ý“Œ– /test-0/579worldanyoneBOý“c~>Àý“Œ– /test-2/572worldanyoneBOý“bawÁý“Œ™ /test-1/629worldanyoneBOý“cŠ;Âý“Œš /test-8/569worldanyoneBOý“barÃý“Œš /test-6/624worldanyoneBOý“c~2Äý“Œ› /test-4/560worldanyoneBOý“cz9Åý“Œ  /test-3/567worldanyoneBOý“c~?Æý“Œ  /test-2/573worldanyoneBOý“cz8Çý“Œ  /test-7/566worldanyoneBOý“cz7Èý“Œ  /test-5/565worldanyoneBOý“c~FÉý“Œ  /test-0/580worldanyoneBOý“cŠ=Êý“Œ  /test-9/571worldanyoneBOý“baxËý“Œ¡ /test-1/630worldanyoneBOý“basÌý“Œ¤ /test-6/625worldanyoneBOý“c~3Íý“Œ¥ /test-4/561worldanyoneBOý“cŠ<Îý“Œ¥ /test-8/570worldanyoneBOý“cz:Ïý“Œ¨ /test-3/568worldanyoneBOý“cz8Ðý“Œª /test-5/566worldanyoneBOý“c~@Ñý“Œª /test-2/574worldanyoneBOý“cz9Òý“Œª /test-7/567worldanyoneBOý“c~GÓý“Œª /test-0/581worldanyoneBOý“cŠ>Ôý“Œª /test-9/572worldanyoneBOý“bayÕý“Œ« /test-1/631worldanyoneBOý“batÖý“Œ® /test-6/626worldanyoneBOý“c~4×ý“Œ® /test-4/562worldanyoneBOý“cŠ=Øý“Œ® /test-8/571worldanyoneBOý“cz;Ùý“Œ³ /test-3/569worldanyoneBOý“c~AÚý“Œ´ /test-2/575worldanyoneBOý“c~HÛý“Œ´ /test-0/582worldanyoneBOý“cz:Üý“Œ´ /test-7/568worldanyoneBOý“bazÝý“Œ´ /test-1/632worldanyoneBOý“cŠ?Þý“Œµ /test-9/573worldanyoneBOý“cz9ßý“Œµ /test-5/567worldanyoneBOý“bauàý“Œ¶ /test-6/627worldanyoneBOý“c~5áý“Œ· /test-4/563worldanyoneBOý“cŠ>âý“Œ· /test-8/572worldanyoneBOý“ba{ãý“Œ¾ /test-1/633worldanyoneBOý“cz;äý“Œ¿ /test-7/569worldanyoneBOý“cŠ@åý“Œ¿ /test-9/574worldanyoneBOý“cz<æý“Œ¿ /test-3/570worldanyoneBOý“cz:çý“ŒÀ /test-5/568worldanyoneBOý“c~Ièý“ŒÀ /test-0/583worldanyoneBOý“c~Béý“ŒÀ /test-2/576worldanyoneBOý“bavêý“ŒÀ /test-6/628worldanyoneBOý“c~6ëý“ŒÁ /test-4/564worldanyoneBOý“cŠ?ìý“ŒÁ /test-8/573worldanyoneBOý“ba|íý“ŒÉ /test-1/634worldanyoneBOý“cz=îý“ŒÊ /test-3/571worldanyoneBOý“cz<ïý“ŒÊ /test-7/570worldanyoneBOý“cŠAðý“ŒÊ /test-9/575worldanyoneBOý“bawñý“ŒË /test-6/629worldanyoneBOý“cz;òý“ŒË /test-5/569worldanyoneBOý“c~Jóý“ŒÌ /test-0/584worldanyoneBOý“c~Côý“ŒÌ /test-2/577worldanyoneBOý“c~7õý“ŒÌ /test-4/565worldanyoneBOý“cŠ@öý“ŒÌ /test-8/574worldanyoneBOý“ba}÷ý“ŒÒ /test-1/635worldanyoneBOý“baxøý“ŒÔ /test-6/630worldanyoneBOý“cz>ùý“ŒÕ /test-3/572worldanyoneBOý“cz<úý“ŒÕ /test-5/570worldanyoneBOý“cz=ûý“ŒÕ /test-7/571worldanyoneBOý“cŠBüý“ŒÕ /test-9/576worldanyoneBOý“c~Dýý“Œ× /test-2/578worldanyoneBOý“c~8þý“Œ× /test-4/566worldanyoneBOý“c~Kÿý“Œ× /test-0/585worldanyoneBOý“cŠAý“ŒØ /test-8/575worldanyoneBOý“ba~ý“ŒÚ /test-1/636worldanyoneBOý“bayý“ŒÞ /test-6/631worldanyoneBOý“cz?ý“Œß /test-3/573worldanyoneBOý“cŠCý“Œß /test-9/577worldanyoneBOý“cz>ý“Œß /test-7/572worldanyoneBOý“cz=ý“Œß /test-5/571worldanyoneBOý“c~Lý“Œâ /test-0/586worldanyoneBOý“c~Eý“Œâ /test-2/579worldanyoneBOý“c~9 ý“Œã /test-4/567worldanyoneBOý“cŠB ý“Œã /test-8/576worldanyoneBOý“ba ý“Œã /test-1/637worldanyoneBOý“baz ý“Œè /test-6/632worldanyoneBOý“cŠD ý“Œé /test-9/578worldanyoneBOý“cz@ý“Œê /test-3/574worldanyoneBOý“cz>ý“Œê /test-5/572worldanyoneBOý“cz?ý“Œê /test-7/573worldanyoneBOý“ba€ý“Œì /test-1/638worldanyoneBOý“cŠCý“Œí /test-8/577worldanyoneBOý“c~Mý“Œí /test-0/587worldanyoneBOý“c~Fý“Œî /test-2/580worldanyoneBOý“c~:ý“Œî /test-4/568worldanyoneBOý“ba{ý“Œñ /test-6/633worldanyoneBOý“cŠEý“Œò /test-9/579worldanyoneBOý“czAý“Œó /test-3/575worldanyoneBOý“cz?ý“Œó /test-5/573worldanyoneBOý“cz@ý“Œó /test-7/574worldanyoneBOý“baý“Œö /test-1/639worldanyoneBOý“c~;ý“Œ÷ /test-4/569worldanyoneBOý“cŠDý“Œ÷ /test-8/578worldanyoneBOý“c~Ný“Œ÷ /test-0/588worldanyoneBOý“c~Gý“Œ÷ /test-2/581worldanyoneBOý“ba| ý“Œû /test-6/634worldanyoneBOý“cŠF!ý“Œû /test-9/580worldanyoneBOý“cz@"ý“Œÿ /test-5/574worldanyoneBOý“czB#ý“Œÿ /test-3/576worldanyoneBOý“czA$ý“Œÿ /test-7/575worldanyoneBOý“ba‚%ý“Œÿ /test-1/640worldanyoneBOý“cŠE&ý“ /test-8/579worldanyoneBOý“c~<'ý“ /test-4/570worldanyoneBOý“c~O(ý“ /test-0/589worldanyoneBOý“c~H)ý“ /test-2/582worldanyoneBOý“ba}*ý“ /test-6/635worldanyoneBOý“cŠG+ý“ /test-9/581worldanyoneBOý“czA,ý“ /test-5/575worldanyoneBOý“baƒ-ý“  /test-1/641worldanyoneBOý“c~P.ý“  /test-0/590worldanyoneBOý“c~=/ý“  /test-4/571worldanyoneBOý“czC0ý“  /test-3/577worldanyoneBOý“czB1ý“  /test-7/576worldanyoneBOý“c~I2ý“  /test-2/583worldanyoneBOý“cŠF3ý“  /test-8/580worldanyoneBOý“ba~4ý“  /test-6/636worldanyoneBOý“cŠH5ý“ /test-9/582worldanyoneBOý“czB6ý“ /test-5/576worldanyoneBOý“ba„7ý“ /test-1/642worldanyoneBOý“c~Q8ý“ /test-0/591worldanyoneBOý“ba9ý“ /test-6/637worldanyoneBOý“czD:ý“ /test-3/578worldanyoneBOý“czC;ý“ /test-7/577worldanyoneBOý“c~><ý“ /test-4/572worldanyoneBOý“c~J=ý“ /test-2/584worldanyoneBOý“cŠI>ý“ /test-9/583worldanyoneBOý“cŠG?ý“ /test-8/581worldanyoneBOý“czC@ý“ /test-5/577worldanyoneBOý“ba…Aý“ /test-1/643worldanyoneBOý“c~RBý“ /test-0/592worldanyoneBOý“ba€Cý“  /test-6/638worldanyoneBOý“czEDý“" /test-3/579worldanyoneBOý“czDEý“# /test-7/578worldanyoneBOý“c~?Fý“# /test-4/573worldanyoneBOý“c~KGý“$ /test-2/585worldanyoneBOý“cŠHHý“$ /test-8/582worldanyoneBOý“cŠJIý“$ /test-9/584worldanyoneBOý“czDJý“$ /test-5/578worldanyoneBOý“ba†Ký“% /test-1/644worldanyoneBOý“c~SLý“& /test-0/593worldanyoneBOý“baMý“) /test-6/639worldanyoneBOý“czFNý“+ /test-3/580worldanyoneBOý“czEOý“0 /test-5/579worldanyoneBOý“czEPý“0 /test-7/579worldanyoneBOý“cŠIQý“0 /test-8/583worldanyoneBOý“cŠKRý“0 /test-9/585worldanyoneBOý“c~@Sý“1 /test-4/574worldanyoneBOý“c~LTý“1 /test-2/586worldanyoneBOý“ba‡Uý“1 /test-1/645worldanyoneBOý“c~TVý“1 /test-0/594worldanyoneBOý“ba‚Wý“2 /test-6/640worldanyoneBOý“czGXý“4 /test-3/581worldanyoneBOý“czFYý“9 /test-5/580worldanyoneBOý“cŠJZý“; /test-8/584worldanyoneBOý“czF[ý“; /test-7/580worldanyoneBOý“cŠL\ý“; /test-9/586worldanyoneBOý“c~U]ý“< /test-0/595worldanyoneBOý“c~A^ý“< /test-4/575worldanyoneBOý“c~M_ý“= /test-2/587worldanyoneBOý“baˆ`ý“= /test-1/646worldanyoneBOý“baƒaý“= /test-6/641worldanyoneBOý“czHbý“= /test-3/582worldanyoneBOý“czGcý“B /test-5/581worldanyoneBOý“cŠKdý“C /test-8/585worldanyoneBOý“cŠMeý“D /test-9/587worldanyoneBOý“czGfý“E /test-7/581worldanyoneBOý“czIgý“G /test-3/583worldanyoneBOý“c~Bhý“G /test-4/576worldanyoneBOý“c~Niý“G /test-2/588worldanyoneBOý“ba„jý“H /test-6/642worldanyoneBOý“ba‰ký“H /test-1/647worldanyoneBOý“c~Vlý“H /test-0/596worldanyoneBOý“czHmý“J /test-5/582worldanyoneBOý“czHný“M /test-7/582worldanyoneBOý“cŠLoý“N /test-8/586worldanyoneBOý“cŠNpý“N /test-9/588worldanyoneBOý“c~Oqý“S /test-2/589worldanyoneBOý“c~Crý“S /test-4/577worldanyoneBOý“c~Wsý“S /test-0/597worldanyoneBOý“ba…tý“S /test-6/643worldanyoneBOý“baŠuý“S /test-1/648worldanyoneBOý“czJvý“S /test-3/584worldanyoneBOý“czIwý“T /test-5/583worldanyoneBOý“czIxý“V /test-7/583worldanyoneBOý“cŠMyý“V /test-8/587worldanyoneBOý“cŠOzý“W /test-9/589worldanyoneBOý“c~P{ý“\ /test-2/590worldanyoneBOý“c~D|ý“] /test-4/578worldanyoneBOý“czJ}ý“] /test-5/584worldanyoneBOý“c~X~ý“^ /test-0/598worldanyoneBOý“ba‹ý“^ /test-1/649worldanyoneBOý“czK€ý“^ /test-3/585worldanyoneBOý“ba†ý“_ /test-6/644worldanyoneBOý“czJ‚ý“b /test-7/584worldanyoneBOý“cŠNƒý“b /test-8/588worldanyoneBOý“cŠP„ý“b /test-9/590worldanyoneBOý“czK…ý“h /test-5/585worldanyoneBOý“c~Q†ý“h /test-2/591worldanyoneBOý“c~E‡ý“h /test-4/579worldanyoneBOý“c~Yˆý“i /test-0/599worldanyoneBOý“baŒ‰ý“i /test-1/650worldanyoneBOý“czLŠý“i /test-3/586worldanyoneBOý“ba‡‹ý“i /test-6/645worldanyoneBOý“czKŒý“l /test-7/585worldanyoneBOý“cŠQý“m /test-9/591worldanyoneBOý“cŠOŽý“m /test-8/589worldanyoneBOý“czLý“r /test-5/586worldanyoneBOý“baˆý“s /test-6/646worldanyoneBOý“ba‘ý“t /test-1/651worldanyoneBOý“czM’ý“t /test-3/587worldanyoneBOý“c~Z“ý“u /test-0/600worldanyoneBOý“c~F”ý“u /test-4/580worldanyoneBOý“c~R•ý“u /test-2/592worldanyoneBOý“cŠP–ý“v /test-8/590worldanyoneBOý“cŠR—ý“v /test-9/592worldanyoneBOý“czL˜ý“v /test-7/586worldanyoneBOý“czM™ý“{ /test-5/587worldanyoneBOý“ba‰šý“ /test-6/647worldanyoneBOý“baŽ›ý“ /test-1/652worldanyoneBOý“czNœý“ /test-3/588worldanyoneBOý“cŠSý“ /test-9/593worldanyoneBOý“cŠQžý“ /test-8/591worldanyoneBOý“c~[Ÿý“ /test-0/601worldanyoneBOý“c~G ý“ /test-4/581worldanyoneBOý“c~S¡ý“ /test-2/593worldanyoneBOý“czM¢ý“‚ /test-7/587worldanyoneBOý“czN£ý“ƒ /test-5/588worldanyoneBOý“baФý“ˆ /test-6/648worldanyoneBOý“ba¥ý“‰ /test-1/653worldanyoneBOý“czO¦ý“Š /test-3/589worldanyoneBOý“c~T§ý“ /test-2/594worldanyoneBOý“c~H¨ý“ /test-4/582worldanyoneBOý“c~\©ý“ /test-0/602worldanyoneBOý“czNªý“ /test-7/588worldanyoneBOý“czO«ý“ /test-5/589worldanyoneBOý“cŠT¬ý“Ž /test-9/594worldanyoneBOý“cŠR­ý“Ž /test-8/592worldanyoneBOý“ba‹®ý“‘ /test-6/649worldanyoneBOý“ba¯ý“’ /test-1/654worldanyoneBOý“czP°ý“• /test-3/590worldanyoneBOý“cŠU±ý“— /test-9/595worldanyoneBOý“cŠS²ý“˜ /test-8/593worldanyoneBOý“c~I³ý“™ /test-4/583worldanyoneBOý“czO´ý“™ /test-7/589worldanyoneBOý“c~]µý“™ /test-0/603worldanyoneBOý“czP¶ý“™ /test-5/590worldanyoneBOý“c~U·ý“™ /test-2/595worldanyoneBOý“ba‘¸ý“› /test-1/655worldanyoneBOý“baŒ¹ý“› /test-6/650worldanyoneBOý“czQºý“ /test-3/591worldanyoneBOý“cŠV»ý“  /test-9/596worldanyoneBOý“cŠT¼ý“¡ /test-8/594worldanyoneBOý“c~J½ý“£ /test-4/584worldanyoneBOý“c~^¾ý“¤ /test-0/604worldanyoneBOý“czQ¿ý“¤ /test-5/591worldanyoneBOý“c~VÀý“¤ /test-2/596worldanyoneBOý“czPÁý“¤ /test-7/590worldanyoneBOý“baÂý“¥ /test-6/651worldanyoneBOý“ba’Ãý“¦ /test-1/656worldanyoneBOý“czRÄý“¦ /test-3/592worldanyoneBOý“cŠWÅý“ª /test-9/597worldanyoneBOý“cŠUÆý“ª /test-8/595worldanyoneBOý“c~KÇý“¬ /test-4/585worldanyoneBOý“c~_Èý“® /test-0/605worldanyoneBOý“c~WÉý“¯ /test-2/597worldanyoneBOý“czQÊý“¯ /test-7/591worldanyoneBOý“czRËý“¯ /test-5/592worldanyoneBOý“ba“Ìý“¯ /test-1/657worldanyoneBOý“czSÍý“° /test-3/593worldanyoneBOý“baŽÎý“° /test-6/652worldanyoneBOý“cŠXÏý“² /test-9/598worldanyoneBOý“cŠVÐý“³ /test-8/596worldanyoneBOý“c~LÑý“³ /test-4/586worldanyoneBOý“czRÒý“¸ /test-7/592worldanyoneBOý“czSÓý“¹ /test-5/593worldanyoneBOý“c~`Ôý“¹ /test-0/606worldanyoneBOý“c~XÕý“¹ /test-2/598worldanyoneBOý“baÖý“¹ /test-6/653worldanyoneBOý“ba”×ý“º /test-1/658worldanyoneBOý“czTØý“» /test-3/594worldanyoneBOý“cŠYÙý“¼ /test-9/599worldanyoneBOý“c~MÚý“½ /test-4/587worldanyoneBOý“cŠWÛý“½ /test-8/597worldanyoneBOý“c~aÜý“Á /test-0/607worldanyoneBOý“c~YÝý“ /test-2/599worldanyoneBOý“czSÞý“ /test-7/593worldanyoneBOý“czTßý“ /test-5/594worldanyoneBOý“baàý“Å /test-6/654worldanyoneBOý“cŠZáý“Å /test-9/600worldanyoneBOý“c~Nâý“Æ /test-4/588worldanyoneBOý“ba•ãý“Æ /test-1/659worldanyoneBOý“czUäý“Ç /test-3/595worldanyoneBOý“cŠXåý“Ç /test-8/598worldanyoneBOý“czUæý“Ë /test-5/595worldanyoneBOý“czTçý“Ë /test-7/594worldanyoneBOý“c~Zèý“Ë /test-2/600worldanyoneBOý“c~béý“Ë /test-0/608worldanyoneBOý“ba–êý“Ñ /test-1/660worldanyoneBOý“czVëý“Ò /test-3/596worldanyoneBOý“ba‘ìý“Ò /test-6/655worldanyoneBOý“c~Oíý“Ò /test-4/589worldanyoneBOý“cŠYîý“Ò /test-8/599worldanyoneBOý“cŠ[ïý“Ò /test-9/601worldanyoneBOý“czUðý“Õ /test-7/595worldanyoneBOý“c~[ñý“Õ /test-2/601worldanyoneBOý“c~còý“Õ /test-0/609worldanyoneBOý“czVóý“Ö /test-5/596worldanyoneBOý“ba’ôý“Û /test-6/656worldanyoneBOý“czWõý“Ü /test-3/597worldanyoneBOý“c~Pöý“Ü /test-4/590worldanyoneBOý“ba—÷ý“Ü /test-1/661worldanyoneBOý“cŠZøý“Ü /test-8/600worldanyoneBOý“cŠ\ùý“Ý /test-9/602worldanyoneBOý“c~\úý“à /test-2/602worldanyoneBOý“c~dûý“à /test-0/610worldanyoneBOý“czVüý“à /test-7/596worldanyoneBOý“czWýý“à /test-5/597worldanyoneBOý“ba“þý“ä /test-6/657worldanyoneBOý“czXÿý“ä /test-3/598worldanyoneBOý“ba˜ý“å /test-1/662worldanyoneBOý“cŠ[ý“æ /test-8/601worldanyoneBOý“c~Qý“æ /test-4/591worldanyoneBOý“cŠ]ý“æ /test-9/603worldanyoneBOý“c~]ý“é /test-2/603worldanyoneBOý“czWý“ê /test-7/597worldanyoneBOý“c~eý“ë /test-0/611worldanyoneBOý“czXý“ë /test-5/598worldanyoneBOý“ba”ý“í /test-6/658worldanyoneBOý“czY ý“í /test-3/599worldanyoneBOý“cŠ\ ý“î /test-8/602worldanyoneBOý“ba™ ý“ï /test-1/663worldanyoneBOý“c~R ý“ï /test-4/592worldanyoneBOý“cŠ^ ý“ð /test-9/604worldanyoneBOý“c~^ý“ñ /test-2/604worldanyoneBOý“czXý“ó /test-7/598worldanyoneBOý“c~fý“ô /test-0/612worldanyoneBOý“czYý“ô /test-5/599worldanyoneBOý“ba•ý“÷ /test-6/659worldanyoneBOý“czZý“ú /test-3/600worldanyoneBOý“c~Sý“ú /test-4/593worldanyoneBOý“bašý“ú /test-1/664worldanyoneBOý“cŠ]ý“ú /test-8/603worldanyoneBOý“cŠ_ý“û /test-9/605worldanyoneBOý“c~_ý“ü /test-2/605worldanyoneBOý“czYý“ü /test-7/599worldanyoneBOý“c~gý“ý /test-0/613worldanyoneBOý“czZý“þ /test-5/600worldanyoneBOý“ba–ý“ÿ /test-6/660worldanyoneBOý“cz[ý“Ž /test-3/601worldanyoneBOý“ba›ý“Ž /test-1/665worldanyoneBOý“c~Tý“Ž /test-4/594worldanyoneBOý“cŠ^ ý“Ž /test-8/604worldanyoneBOý“cz[!ý“Ž /test-5/601worldanyoneBOý“cŠ`"ý“Ž /test-9/606worldanyoneBOý“czZ#ý“Ž /test-7/600worldanyoneBOý“c~`$ý“Ž /test-2/606worldanyoneBOý“c~h%ý“Ž  /test-0/614worldanyoneBOý“ba—&ý“Ž  /test-6/661worldanyoneBOý“baœ'ý“Ž /test-1/666worldanyoneBOý“cŠ_(ý“Ž /test-8/605worldanyoneBOý“cz\)ý“Ž /test-3/602worldanyoneBOý“c~U*ý“Ž /test-4/595worldanyoneBOý“cz\+ý“Ž /test-5/602worldanyoneBOý“cz[,ý“Ž /test-7/601worldanyoneBOý“ba˜-ý“Ž /test-6/662worldanyoneBOý“c~i.ý“Ž /test-0/615worldanyoneBOý“cŠa/ý“Ž /test-9/607worldanyoneBOý“c~a0ý“Ž /test-2/607worldanyoneBOý“ba1ý“Ž /test-1/667worldanyoneBOý“cŠ`2ý“Ž /test-8/606worldanyoneBOý“cz]3ý“Ž /test-3/603worldanyoneBOý“c~V4ý“Ž /test-4/596worldanyoneBOý“cz]5ý“Ž /test-5/603worldanyoneBOý“ba™6ý“Ž /test-6/663worldanyoneBOý“cz\7ý“Ž /test-7/602worldanyoneBOý“c~j8ý“Ž /test-0/616worldanyoneBOý“cŠb9ý“Ž /test-9/608worldanyoneBOý“c~b:ý“Ž /test-2/608worldanyoneBOý“baž;ý“Ž /test-1/668worldanyoneBOý“cŠa<ý“Ž! /test-8/607worldanyoneBOý“c~W=ý“Ž" /test-4/597worldanyoneBOý“cz^>ý“Ž# /test-3/604worldanyoneBOý“cz^?ý“Ž$ /test-5/604worldanyoneBOý“baš@ý“Ž' /test-6/664worldanyoneBOý“cz]Aý“Ž' /test-7/603worldanyoneBOý“c~kBý“Ž( /test-0/617worldanyoneBOý“baŸCý“Ž( /test-1/669worldanyoneBOý“cŠcDý“Ž) /test-9/609worldanyoneBOý“cŠbEý“Ž) /test-8/608worldanyoneBOý“c~cFý“Ž* /test-2/609worldanyoneBOý“cz_Gý“Ž+ /test-3/605worldanyoneBOý“cz_Hý“Ž+ /test-5/605worldanyoneBOý“c~XIý“Ž, /test-4/598worldanyoneBOý“ba›Jý“Ž0 /test-6/665worldanyoneBOý“cŠcKý“Ž5 /test-8/609worldanyoneBOý“ba Lý“Ž5 /test-1/670worldanyoneBOý“cŠdMý“Ž5 /test-9/610worldanyoneBOý“c~lNý“Ž5 /test-0/618worldanyoneBOý“cz`Oý“Ž5 /test-3/606worldanyoneBOý“cz`Pý“Ž5 /test-5/606worldanyoneBOý“cz^Qý“Ž5 /test-7/604worldanyoneBOý“c~dRý“Ž6 /test-2/610worldanyoneBOý“c~YSý“Ž6 /test-4/599worldanyoneBOý“baœTý“Ž8 /test-6/666worldanyoneBOý“cŠdUý“Ž= /test-8/610worldanyoneBOý“ba¡Vý“Ž> /test-1/671worldanyoneBOý“baWý“ŽA /test-6/667worldanyoneBOý“cŠeXý“ŽA /test-9/611worldanyoneBOý“cz_Yý“ŽA /test-7/605worldanyoneBOý“c~eZý“ŽA /test-2/611worldanyoneBOý“c~Z[ý“ŽA /test-4/600worldanyoneBOý“cza\ý“ŽA /test-5/607worldanyoneBOý“c~m]ý“ŽA /test-0/619worldanyoneBOý“cza^ý“ŽB /test-3/607worldanyoneBOý“cŠe_ý“ŽF /test-8/611worldanyoneBOý“ba¢`ý“ŽF /test-1/672worldanyoneBOý“bažaý“ŽK /test-6/668worldanyoneBOý“cŠfbý“ŽM /test-9/612worldanyoneBOý“cz`cý“ŽM /test-7/606worldanyoneBOý“c~[dý“ŽM /test-4/601worldanyoneBOý“czbeý“ŽM /test-3/608worldanyoneBOý“c~ffý“ŽM /test-2/612worldanyoneBOý“c~ngý“ŽM /test-0/620worldanyoneBOý“czbhý“ŽN /test-5/608worldanyoneBOý“cŠfiý“ŽN /test-8/612worldanyoneBOý“ba£jý“ŽO /test-1/673worldanyoneBOý“baŸký“ŽU /test-6/669worldanyoneBOý“cŠglý“ŽU /test-9/613worldanyoneBOý“ba¤mý“ŽX /test-1/674worldanyoneBOý“cŠgný“ŽX /test-8/613worldanyoneBOý“czaoý“ŽY /test-7/607worldanyoneBOý“c~\pý“ŽZ /test-4/602worldanyoneBOý“c~gqý“ŽZ /test-2/613worldanyoneBOý“c~orý“ŽZ /test-0/621worldanyoneBOý“czcsý“ŽZ /test-5/609worldanyoneBOý“czctý“ŽZ /test-3/609worldanyoneBOý“ba uý“Ž^ /test-6/670worldanyoneBOý“cŠhvý“Ž^ /test-9/614worldanyoneBOý“ba¥wý“Ža /test-1/675worldanyoneBOý“cŠhxý“Ža /test-8/614worldanyoneBOý“czbyý“Žb /test-7/608worldanyoneBOý“c~]zý“Žd /test-4/603worldanyoneBOý“c~h{ý“Žd /test-2/614worldanyoneBOý“c~p|ý“Že /test-0/622worldanyoneBOý“czd}ý“Že /test-5/610worldanyoneBOý“czd~ý“Že /test-3/610worldanyoneBOý“ba¡ý“Žg /test-6/671worldanyoneBOý“cŠi€ý“Žh /test-9/615worldanyoneBOý“ba¦ý“Žj /test-1/676worldanyoneBOý“cŠi‚ý“Žk /test-8/615worldanyoneBOý“czcƒý“Žk /test-7/609worldanyoneBOý“c~i„ý“Žn /test-2/615worldanyoneBOý“c~^…ý“Žn /test-4/604worldanyoneBOý“c~q†ý“Žn /test-0/623worldanyoneBOý“cze‡ý“Žn /test-5/611worldanyoneBOý“czeˆý“Žn /test-3/611worldanyoneBOý“ba¢‰ý“Žp /test-6/672worldanyoneBOý“cŠjŠý“Žq /test-9/616worldanyoneBOý“ba§‹ý“Žt /test-1/677worldanyoneBOý“cŠjŒý“Žt /test-8/616worldanyoneBOý“czdý“Žu /test-7/610worldanyoneBOý“czfŽý“Žw /test-3/612worldanyoneBOý“c~rý“Žx /test-0/624worldanyoneBOý“c~jý“Žx /test-2/616worldanyoneBOý“czf‘ý“Žx /test-5/612worldanyoneBOý“c~_’ý“Žx /test-4/605worldanyoneBOý“ba¨“ý“Ž¢ /test-1/678worldanyoneBOý“ba£”ý“Ž¢ /test-6/673worldanyoneBOý“cŠk•ý“Ž¢ /test-9/617worldanyoneBOý“cŠk–ý“Ž¢ /test-8/617worldanyoneBOý“cze—ý“ލ /test-7/611worldanyoneBOý“c~k˜ý“Žª /test-2/617worldanyoneBOý“c~s™ý“Ž« /test-0/625worldanyoneBOý“czgšý“Ž« /test-3/613worldanyoneBOý“c~`›ý“Ž« /test-4/606worldanyoneBOý“ba©œý“ެ /test-1/679worldanyoneBOý“cŠlý“ެ /test-9/618worldanyoneBOý“ba¤žý“ެ /test-6/674worldanyoneBOý“czgŸý“ެ /test-5/613worldanyoneBOý“cŠl ý“ެ /test-8/618worldanyoneBOý“czf¡ý“ޱ /test-7/612worldanyoneBOý“cŠm¢ý“޶ /test-9/619worldanyoneBOý“ba¥£ý“Ž· /test-6/675worldanyoneBOý“czh¤ý“ޏ /test-5/614worldanyoneBOý“czh¥ý“ޏ /test-3/614worldanyoneBOý“cŠm¦ý“޹ /test-8/619worldanyoneBOý“c~t§ý“޹ /test-0/626worldanyoneBOý“c~l¨ý“޹ /test-2/618worldanyoneBOý“c~a©ý“޹ /test-4/607worldanyoneBOý“baªªý“޹ /test-1/680worldanyoneBOý“czg«ý“Žº /test-7/613worldanyoneBOý“ba¦¬ý“ŽÂ /test-6/676worldanyoneBOý“cŠn­ý“ŽÃ /test-9/620worldanyoneBOý“c~m®ý“ŽÃ /test-2/619worldanyoneBOý“c~u¯ý“ŽÄ /test-0/627worldanyoneBOý“ba«°ý“ŽÄ /test-1/681worldanyoneBOý“c~b±ý“ŽÅ /test-4/608worldanyoneBOý“czi²ý“ŽÅ /test-3/615worldanyoneBOý“czh³ý“ŽÅ /test-7/614worldanyoneBOý“czi´ý“ŽÅ /test-5/615worldanyoneBOý“cŠnµý“ŽÅ /test-8/620worldanyoneBOý“ba§¶ý“ŽÊ /test-6/677worldanyoneBOý“cŠo·ý“ŽË /test-9/621worldanyoneBOý“c~v¸ý“ŽÐ /test-0/628worldanyoneBOý“c~c¹ý“ŽÐ /test-4/609worldanyoneBOý“c~nºý“ŽÐ /test-2/620worldanyoneBOý“cŠo»ý“ŽÐ /test-8/621worldanyoneBOý“czj¼ý“ŽÐ /test-3/616worldanyoneBOý“czj½ý“ŽÐ /test-5/616worldanyoneBOý“czi¾ý“ŽÐ /test-7/615worldanyoneBOý“ba¬¿ý“ŽÐ /test-1/682worldanyoneBOý“ba¨Àý“ŽÒ /test-6/678worldanyoneBOý“cŠpÁý“ŽÓ /test-9/622worldanyoneBOý“czjÂý“ŽÞ /test-7/616worldanyoneBOý“cŠqÃý“ŽÞ /test-9/623worldanyoneBOý“ba©Äý“ŽÞ /test-6/679worldanyoneBOý“czkÅý“ŽÞ /test-5/617worldanyoneBOý“czkÆý“ŽÞ /test-3/617worldanyoneBOý“c~dÇý“Žß /test-4/610worldanyoneBOý“c~wÈý“Žß /test-0/629worldanyoneBOý“c~oÉý“Žß /test-2/621worldanyoneBOý“ba­Êý“Žß /test-1/683worldanyoneBOý“cŠpËý“Žß /test-8/622worldanyoneBOý“czkÌý“Žé /test-7/617worldanyoneBOý“baªÍý“Žé /test-6/680worldanyoneBOý“cŠrÎý“Žê /test-9/624worldanyoneBOý“czlÏý“Žê /test-5/618worldanyoneBOý“c~eÐý“Žê /test-4/611worldanyoneBOý“c~pÑý“Žê /test-2/622worldanyoneBOý“cŠqÒý“Žë /test-8/623worldanyoneBOý“c~xÓý“Žë /test-0/630worldanyoneBOý“czlÔý“Žë /test-3/618worldanyoneBOý“ba®Õý“Žë /test-1/684worldanyoneBOý“ba¯Öý“Žö /test-1/685worldanyoneBOý“ba«×ý“Žö /test-6/681worldanyoneBOý“c~qØý“Ž÷ /test-2/623worldanyoneBOý“c~fÙý“Ž÷ /test-4/612worldanyoneBOý“cŠsÚý“Ž÷ /test-9/625worldanyoneBOý“cŠrÛý“Ž÷ /test-8/624worldanyoneBOý“czmÜý“Žø /test-3/619worldanyoneBOý“czmÝý“Žø /test-5/619worldanyoneBOý“czlÞý“Žø /test-7/618worldanyoneBOý“c~yßý“Žø /test-0/631worldanyoneBOý“ba¬àý“Žÿ /test-6/682worldanyoneBOý“ba°áý“ /test-1/686worldanyoneBOý“cŠtâý“ /test-9/626worldanyoneBOý“cŠsãý“ /test-8/625worldanyoneBOý“c~gäý“ /test-4/613worldanyoneBOý“c~zåý“ /test-0/632worldanyoneBOý“c~ræý“ /test-2/624worldanyoneBOý“cznçý“ /test-5/620worldanyoneBOý“czmèý“ /test-7/619worldanyoneBOý“cznéý“ /test-3/620worldanyoneBOý“ba­êý“ /test-6/683worldanyoneBOý“ba±ëý“  /test-1/687worldanyoneBOý“cŠuìý“  /test-9/627worldanyoneBOý“c~híý“ /test-4/614worldanyoneBOý“c~{îý“ /test-0/633worldanyoneBOý“c~sïý“ /test-2/625worldanyoneBOý“cŠtðý“ /test-8/626worldanyoneBOý“cznñý“ /test-7/620worldanyoneBOý“czoòý“ /test-5/621worldanyoneBOý“czoóý“ /test-3/621worldanyoneBOý“ba®ôý“ /test-6/684worldanyoneBOý“ba²õý“ /test-1/688worldanyoneBOý“cŠvöý“ /test-9/628worldanyoneBOý“c~i÷ý“ /test-4/615worldanyoneBOý“c~|øý“ /test-0/634worldanyoneBOý“cŠuùý“ /test-8/627worldanyoneBOý“czpúý“ /test-5/622worldanyoneBOý“czoûý“ /test-7/621worldanyoneBOý“ba³üý“ /test-1/689worldanyoneBOý“ba¯ýý“ /test-6/685worldanyoneBOý“czpþý“ /test-3/622worldanyoneBOý“c~tÿý“ /test-2/626worldanyoneBOý“cŠwý“ /test-9/629worldanyoneBOý“c~jý“ /test-4/616worldanyoneBOý“c~}ý“# /test-0/635worldanyoneBOý“ba´ý“% /test-1/690worldanyoneBOý“czpý“& /test-7/622worldanyoneBOý“czqý“& /test-5/623worldanyoneBOý“cŠvý“& /test-8/628worldanyoneBOý“ba°ý“' /test-6/686worldanyoneBOý“cŠxý“' /test-9/630worldanyoneBOý“czq ý“( /test-3/623worldanyoneBOý“c~u ý“( /test-2/627worldanyoneBOý“c~k ý“) /test-4/617worldanyoneBOý“c~~ ý“, /test-0/636worldanyoneBOý“baµ ý“. /test-1/691worldanyoneBOý“ba±ý“1 /test-6/687worldanyoneBOý“czqý“3 /test-7/623worldanyoneBOý“cŠyý“3 /test-9/631worldanyoneBOý“cŠwý“3 /test-8/629worldanyoneBOý“czrý“3 /test-5/624worldanyoneBOý“czrý“3 /test-3/624worldanyoneBOý“c~lý“3 /test-4/618worldanyoneBOý“c~vý“3 /test-2/628worldanyoneBOý“c~ý“4 /test-0/637worldanyoneBOý“ba¶ý“6 /test-1/692worldanyoneBOý“ba²ý“: /test-6/688worldanyoneBOý“czrý“; /test-7/624worldanyoneBOý“cŠzý“? /test-9/632worldanyoneBOý“ba·ý“@ /test-1/693worldanyoneBOý“cŠxý“A /test-8/630worldanyoneBOý“czsý“A /test-5/625worldanyoneBOý“czsý“A /test-3/625worldanyoneBOý“c~€ý“A /test-0/638worldanyoneBOý“c~w ý“A /test-2/629worldanyoneBOý“c~m!ý“B /test-4/619worldanyoneBOý“ba³"ý“C /test-6/689worldanyoneBOý“czs#ý“D /test-7/625worldanyoneBOý“cŠ{$ý“H /test-9/633worldanyoneBOý“ba¸%ý“M /test-1/694worldanyoneBOý“ba´&ý“N /test-6/690worldanyoneBOý“c~x'ý“N /test-2/630worldanyoneBOý“c~n(ý“N /test-4/620worldanyoneBOý“c~)ý“N /test-0/639worldanyoneBOý“cŠy*ý“N /test-8/631worldanyoneBOý“czt+ý“O /test-7/626worldanyoneBOý“czt,ý“O /test-5/626worldanyoneBOý“czt-ý“O /test-3/626worldanyoneBOý“cŠ|.ý“P /test-9/634worldanyoneBOý“ba¹/ý“V /test-1/695worldanyoneBOý“czu0ý“[ /test-7/627worldanyoneBOý“czu1ý“[ /test-5/627worldanyoneBOý“czu2ý“[ /test-3/627worldanyoneBOý“c~y3ý“[ /test-2/631worldanyoneBOý“c~‚4ý“\ /test-0/640worldanyoneBOý“c~o5ý“\ /test-4/621worldanyoneBOý“baµ6ý“\ /test-6/691worldanyoneBOý“cŠ}7ý“\ /test-9/635worldanyoneBOý“cŠz8ý“\ /test-8/632worldanyoneBOý“baº9ý“^ /test-1/696worldanyoneBOý“c~z:ý“h /test-2/632worldanyoneBOý“czv;ý“h /test-7/628worldanyoneBOý“ba»<ý“h /test-1/697worldanyoneBOý“cŠ~=ý“h /test-9/636worldanyoneBOý“cŠ{>ý“h /test-8/633worldanyoneBOý“ba¶?ý“h /test-6/692worldanyoneBOý“czv@ý“i /test-3/628worldanyoneBOý“czvAý“i /test-5/628worldanyoneBOý“c~pBý“i /test-4/622worldanyoneBOý“c~ƒCý“i /test-0/641worldanyoneBOý“ba¼Dý“s /test-1/698worldanyoneBOý“cŠEý“t /test-9/637worldanyoneBOý“cŠ|Fý“t /test-8/634worldanyoneBOý“ba·Gý“t /test-6/693worldanyoneBOý“czwHý“u /test-7/629worldanyoneBOý“c~{Iý“u /test-2/633worldanyoneBOý“c~qJý“u /test-4/623worldanyoneBOý“c~„Ký“u /test-0/642worldanyoneBOý“czwLý“u /test-5/629worldanyoneBOý“czwMý“u /test-3/629worldanyoneBOý“ba¸Ný“€ /test-6/694worldanyoneBOý“ba½Oý“ /test-1/699worldanyoneBOý“c~rPý“ /test-4/624worldanyoneBOý“c~|Qý“ /test-2/634worldanyoneBOý“czxRý“‚ /test-5/630worldanyoneBOý“czxSý“‚ /test-3/630worldanyoneBOý“cŠ€Tý“‚ /test-9/638worldanyoneBOý“cŠ}Uý“‚ /test-8/635worldanyoneBOý“c~…Vý“‚ /test-0/643worldanyoneBOý“czxWý“ƒ /test-7/630worldanyoneBOý“czyXý“ /test-5/631worldanyoneBOý“cŠYý“Ž /test-9/639worldanyoneBOý“ba¾Zý“Ž /test-1/700worldanyoneBOý“ba¹[ý“Ž /test-6/695worldanyoneBOý“c~}\ý“Ž /test-2/635worldanyoneBOý“czy]ý“ /test-7/631worldanyoneBOý“czy^ý“ /test-3/631worldanyoneBOý“cŠ~_ý“ /test-8/636worldanyoneBOý“c~†`ý“’ /test-0/644worldanyoneBOý“c~saý“’ /test-4/625worldanyoneBOý“czzbý“˜ /test-5/632worldanyoneBOý“cŠ‚cý“š /test-9/640worldanyoneBOý“ba¿dý“› /test-1/701worldanyoneBOý“cŠeý“› /test-8/637worldanyoneBOý“c~~fý“œ /test-2/636worldanyoneBOý“czzgý“œ /test-7/632worldanyoneBOý“czzhý“ /test-3/632worldanyoneBOý“c~‡iý“ /test-0/645worldanyoneBOý“baºjý“ž /test-6/696worldanyoneBOý“c~tký“ž /test-4/626worldanyoneBOý“cz{lý“¢ /test-5/633worldanyoneBOý“cŠƒmý“¤ /test-9/641worldanyoneBOý“ba»ný“ª /test-6/697worldanyoneBOý“cz{oý“« /test-7/633worldanyoneBOý“baÀpý“« /test-1/702worldanyoneBOý“cŠ€qý“« /test-8/638worldanyoneBOý“cz{rý“« /test-3/633worldanyoneBOý“cz|sý“® /test-5/634worldanyoneBOý“c~ˆtý“® /test-0/646worldanyoneBOý“c~uuý“¯ /test-4/627worldanyoneBOý“c~vý“¯ /test-2/637worldanyoneBOý“cŠ„wý“¯ /test-9/642worldanyoneBOý“baÁxý“º /test-1/703worldanyoneBOý“ba¼yý“º /test-6/698worldanyoneBOý“cz|zý“º /test-3/634worldanyoneBOý“cz|{ý“º /test-7/634worldanyoneBOý“cŠ|ý“º /test-8/639worldanyoneBOý“c~€}ý“¿ /test-2/638worldanyoneBOý“cŠ…~ý“Á /test-9/643worldanyoneBOý“cz}ý“ /test-5/635worldanyoneBOý“c~‰€ý“à /test-0/647worldanyoneBOý“c~vý“Å /test-4/628worldanyoneBOý“cŠ‚‚ý“Å /test-8/640worldanyoneBOý“ba½ƒý“É /test-6/699worldanyoneBOý“ba„ý“É /test-1/704worldanyoneBOý“cz}…ý“Ê /test-7/635worldanyoneBOý“c~†ý“Ë /test-2/639worldanyoneBOý“cz}‡ý“Ì /test-3/635worldanyoneBOý“cz~ˆý“Î /test-5/636worldanyoneBOý“cІ‰ý“Ð /test-9/644worldanyoneBOý“c~ŠŠý“Ò /test-0/648worldanyoneBOý“cŠƒ‹ý“Ó /test-8/641worldanyoneBOý“c~wŒý“Ó /test-4/629worldanyoneBOý“ba¾ý“Õ /test-6/700worldanyoneBOý“baÃŽý“Ö /test-1/705worldanyoneBOý“cz~ý“× /test-7/636worldanyoneBOý“cz~ý“Ø /test-3/636worldanyoneBOý“c~‚‘ý“Ø /test-2/640worldanyoneBOý“cz’ý“Ù /test-5/637worldanyoneBOý“cЇ“ý“Ü /test-9/645worldanyoneBOý“c~‹”ý“Ü /test-0/649worldanyoneBOý“c~x•ý“Þ /test-4/630worldanyoneBOý“cŠ„–ý“ß /test-8/642worldanyoneBOý“baÄ—ý“â /test-1/706worldanyoneBOý“ba¿˜ý“â /test-6/701worldanyoneBOý“cz™ý“â /test-7/637worldanyoneBOý“cz€šý“ã /test-5/638worldanyoneBOý“cz›ý“ã /test-3/637worldanyoneBOý“c~ƒœý“ä /test-2/641worldanyoneBOý“cŠˆý“ç /test-9/646worldanyoneBOý“c~Œžý“ç /test-0/650worldanyoneBOý“cŠ…Ÿý“ê /test-8/643worldanyoneBOý“c~y ý“ë /test-4/631worldanyoneBOý“cz€¡ý“î /test-7/638worldanyoneBOý“cz¢ý“î /test-5/639worldanyoneBOý“baÀ£ý“ï /test-6/702worldanyoneBOý“baŤý“ï /test-1/707worldanyoneBOý“c~„¥ý“ð /test-2/642worldanyoneBOý“cz€¦ý“ð /test-3/638worldanyoneBOý“c~§ý“ð /test-0/651worldanyoneBOý“cЉ¨ý“ñ /test-9/647worldanyoneBOý“cІ©ý“õ /test-8/644worldanyoneBOý“c~zªý“ö /test-4/632worldanyoneBOý“cz«ý“ø /test-7/639worldanyoneBOý“cz‚¬ý“ü /test-5/640worldanyoneBOý“cz­ý“ý /test-3/639worldanyoneBOý“cŠŠ®ý“ý /test-9/648worldanyoneBOý“baÁ¯ý“ý /test-6/703worldanyoneBOý“baưý“þ /test-1/708worldanyoneBOý“c~…±ý“þ /test-2/643worldanyoneBOý“c~޲ý“þ /test-0/652worldanyoneBOý“c~{³ý“ÿ /test-4/633worldanyoneBOý“cЇ´ý“ÿ /test-8/645worldanyoneBOý“cz‚µý“ /test-7/640worldanyoneBOý“czƒ¶ý“  /test-5/641worldanyoneBOý“ba·ý“  /test-6/704worldanyoneBOý“baǸý“  /test-1/709worldanyoneBOý“c~†¹ý“  /test-2/644worldanyoneBOý“c~ºý“  /test-0/653worldanyoneBOý“cz‚»ý“ /test-3/640worldanyoneBOý“cŠˆ¼ý“ /test-8/646worldanyoneBOý“cŠ‹½ý“ /test-9/649worldanyoneBOý“c~|¾ý“ /test-4/634worldanyoneBOý“czƒ¿ý“ /test-7/641worldanyoneBOý“baÃÀý“ /test-6/705worldanyoneBOý“cz„Áý“ /test-5/642worldanyoneBOý“c~‡Âý“ /test-2/645worldanyoneBOý“cz„Ãý“ /test-7/642worldanyoneBOý“czƒÄý“ /test-3/641worldanyoneBOý“cЉÅý“ /test-8/647worldanyoneBOý“cŠŒÆý“ /test-9/650worldanyoneBOý“c~Çý“ /test-0/654worldanyoneBOý“c~}Èý“ /test-4/635worldanyoneBOý“baÈÉý“ /test-1/710worldanyoneBOý“baÄÊý“ /test-6/706worldanyoneBOý“cz…Ëý“! /test-5/643worldanyoneBOý“c~ˆÌý“K /test-2/646worldanyoneBOý“cz…Íý“t /test-7/643worldanyoneBOý“cz„Îý“t /test-3/642worldanyoneBOý“cz†Ïý“u /test-5/644worldanyoneBOý“cŠŠÐý“u /test-8/648worldanyoneBOý“cŠÑý“v /test-9/651worldanyoneBOý“c~~Òý“v /test-4/636worldanyoneBOý“c~‘Óý“w /test-0/655worldanyoneBOý“baÉÔý“w /test-1/711worldanyoneBOý“baÅÕý“w /test-6/707worldanyoneBOý“c~‰Öý“w /test-2/647worldanyoneBOý“cz†×ý“| /test-7/644worldanyoneBOý“cz…Øý“ /test-3/643worldanyoneBOý“cz‡Ùý“ /test-5/645worldanyoneBOý“cŠ‹Úý“€ /test-8/649worldanyoneBOý“cŠŽÛý“ /test-9/652worldanyoneBOý“c~Üý“ /test-4/637worldanyoneBOý“c~’Ýý“ /test-0/656worldanyoneBOý“baÊÞý“‚ /test-1/712worldanyoneBOý“baÆßý“ƒ /test-6/708worldanyoneBOý“c~Šàý“ƒ /test-2/648worldanyoneBOý“cz‡áý“„ /test-7/645worldanyoneBOý“czˆâý“Š /test-5/646worldanyoneBOý“cz†ãý“‹ /test-3/644worldanyoneBOý“cŠŒäý“ /test-8/650worldanyoneBOý“cŠåý“ /test-9/653worldanyoneBOý“baËæý“ /test-1/713worldanyoneBOý“baÇçý“ /test-6/709worldanyoneBOý“czˆèý“ /test-7/646worldanyoneBOý“c~“éý“‘ /test-0/657worldanyoneBOý“c~€êý“‘ /test-4/638worldanyoneBOý“c~‹ëý“‘ /test-2/649worldanyoneBOý“cz‡ìý“” /test-3/645worldanyoneBOý“cz‰íý“” /test-5/647worldanyoneBOý“baÌîý“œ /test-1/714worldanyoneBOý“baÈïý“ /test-6/710worldanyoneBOý“cŠðý“ /test-8/651worldanyoneBOý“c~Œñý“ /test-2/650worldanyoneBOý“cŠòý“ /test-9/654worldanyoneBOý“c~”óý“ /test-0/658worldanyoneBOý“cz‰ôý“ž /test-7/647worldanyoneBOý“czŠõý“Ÿ /test-5/648worldanyoneBOý“c~öý“Ÿ /test-4/639worldanyoneBOý“czˆ÷ý“  /test-3/646worldanyoneBOý“baÍøý“¦ /test-1/715worldanyoneBOý“baÉùý“§ /test-6/711worldanyoneBOý“cŠŽúý“¨ /test-8/652worldanyoneBOý“cŠ‘ûý“© /test-9/655worldanyoneBOý“c~•üý“ª /test-0/659worldanyoneBOý“c~ýý“« /test-2/651worldanyoneBOý“cz‰þý“« /test-3/647worldanyoneBOý“c~‚ÿý“¬ /test-4/640worldanyoneBOý“cz‹ý“¬ /test-5/649worldanyoneBOý“czŠý“¬ /test-7/648worldanyoneBOý“baÎý“® /test-1/716worldanyoneBOý“baÊý“± /test-6/712worldanyoneBOý“cŠý“³ /test-8/653worldanyoneBOý“cŠ’ý“µ /test-9/656worldanyoneBOý“c~ƒý“¸ /test-4/641worldanyoneBOý“c~Žý“¸ /test-2/652worldanyoneBOý“c~–ý“¸ /test-0/660worldanyoneBOý“baÏ ý“¹ /test-1/717worldanyoneBOý“czŒ ý“¹ /test-5/650worldanyoneBOý“cz‹ ý“¹ /test-7/649worldanyoneBOý“czŠ ý“¹ /test-3/648worldanyoneBOý“baË ý“º /test-6/713worldanyoneBOý“cŠý“½ /test-8/654worldanyoneBOý“cŠ“ý“¾ /test-9/657worldanyoneBOý“c~„ý“ /test-4/642worldanyoneBOý“czý“à /test-5/651worldanyoneBOý“baÐý“Ä /test-1/718worldanyoneBOý“c~ý“Æ /test-2/653worldanyoneBOý“czŒý“Æ /test-7/650worldanyoneBOý“cz‹ý“Ç /test-3/649worldanyoneBOý“c~—ý“Ç /test-0/661worldanyoneBOý“baÌý“Ç /test-6/714worldanyoneBOý“cŠ‘ý“Ç /test-8/655worldanyoneBOý“cŠ”ý“É /test-9/658worldanyoneBOý“czŽý“Í /test-5/652worldanyoneBOý“baÑý“Î /test-1/719worldanyoneBOý“c~…ý“Î /test-4/643worldanyoneBOý“baÍý“Ñ /test-6/715worldanyoneBOý“c~ý“Ó /test-2/654worldanyoneBOý“cŠ•ý“Ó /test-9/659worldanyoneBOý“c~˜ ý“Ó /test-0/662worldanyoneBOý“cŠ’!ý“Ô /test-8/656worldanyoneBOý“czŒ"ý“Ô /test-3/650worldanyoneBOý“cz#ý“Ô /test-7/651worldanyoneBOý“cz$ý“Ø /test-5/653worldanyoneBOý“baÒ%ý“Ù /test-1/720worldanyoneBOý“c~†&ý“Ú /test-4/644worldanyoneBOý“baÎ'ý“Ú /test-6/716worldanyoneBOý“cŠ–(ý“Ü /test-9/660worldanyoneBOý“c~™)ý“Þ /test-0/663worldanyoneBOý“c~‘*ý“Þ /test-2/655worldanyoneBOý“cŠ“+ý“Þ /test-8/657worldanyoneBOý“cz,ý“ß /test-3/651worldanyoneBOý“czŽ-ý“ß /test-7/652worldanyoneBOý“cz.ý“á /test-5/654worldanyoneBOý“baÏ/ý“ã /test-6/717worldanyoneBOý“baÓ0ý“ä /test-1/721worldanyoneBOý“c~‡1ý“å /test-4/645worldanyoneBOý“cŠ—2ý“å /test-9/661worldanyoneBOý“c~’3ý“è /test-2/656worldanyoneBOý“c~š4ý“é /test-0/664worldanyoneBOý“cz‘5ý“ê /test-5/655worldanyoneBOý“czŽ6ý“ê /test-3/652worldanyoneBOý“cŠ”7ý“ê /test-8/658worldanyoneBOý“cz8ý“ê /test-7/653worldanyoneBOý“baÐ9ý“ë /test-6/718worldanyoneBOý“baÔ:ý“î /test-1/722worldanyoneBOý“c~ˆ;ý“î /test-4/646worldanyoneBOý“cŠ˜<ý“ð /test-9/662worldanyoneBOý“cŠ•=ý“ô /test-8/659worldanyoneBOý“baÑ>ý“õ /test-6/719worldanyoneBOý“c~“?ý“ö /test-2/657worldanyoneBOý“cz’@ý“ö /test-5/656worldanyoneBOý“czAý“ö /test-3/653worldanyoneBOý“czBý“ö /test-7/654worldanyoneBOý“c~›Cý“÷ /test-0/665worldanyoneBOý“baÕDý“÷ /test-1/723worldanyoneBOý“c~‰Eý“÷ /test-4/647worldanyoneBOý“cŠ™Fý“ø /test-9/663worldanyoneBOý“baÒGý“ÿ /test-6/720worldanyoneBOý“cŠ–Hý“‘ /test-8/660worldanyoneBOý“cz“Iý“‘ /test-5/657worldanyoneBOý“c~”Jý“‘ /test-2/658worldanyoneBOý“baÖKý“‘ /test-1/724worldanyoneBOý“czLý“‘ /test-3/654worldanyoneBOý“cz‘Mý“‘ /test-7/655worldanyoneBOý“c~ŠNý“‘ /test-4/648worldanyoneBOý“c~œOý“‘ /test-0/666worldanyoneBOý“cŠšPý“‘ /test-9/664worldanyoneBOý“baÓQý“‘ /test-6/721worldanyoneBOý“cŠ—Rý“‘  /test-8/661worldanyoneBOý“cz”Sý“‘  /test-5/658worldanyoneBOý“ba×Tý“‘  /test-1/725worldanyoneBOý“cz‘Uý“‘ /test-3/655worldanyoneBOý“cz’Vý“‘ /test-7/656worldanyoneBOý“c~Wý“‘ /test-0/667worldanyoneBOý“c~‹Xý“‘ /test-4/649worldanyoneBOý“cŠ›Yý“‘ /test-9/665worldanyoneBOý“c~•Zý“‘ /test-2/659worldanyoneBOý“baÔ[ý“‘ /test-6/722worldanyoneBOý“cŠ˜\ý“‘ /test-8/662worldanyoneBOý“cz•]ý“‘ /test-5/659worldanyoneBOý“baØ^ý“‘ /test-1/726worldanyoneBOý“cz’_ý“‘ /test-3/656worldanyoneBOý“cz“`ý“‘ /test-7/657worldanyoneBOý“baÕaý“‘ /test-6/723worldanyoneBOý“c~Œbý“‘ /test-4/650worldanyoneBOý“c~–cý“‘ /test-2/660worldanyoneBOý“c~ždý“‘ /test-0/668worldanyoneBOý“cŠœeý“‘ /test-9/666worldanyoneBOý“cŠ™fý“‘ /test-8/663worldanyoneBOý“cz–gý“‘  /test-5/660worldanyoneBOý“baÙhý“‘! /test-1/727worldanyoneBOý“cz“iý“‘! /test-3/657worldanyoneBOý“cz”jý“‘" /test-7/658worldanyoneBOý“baÖký“‘$ /test-6/724worldanyoneBOý“c~lý“‘& /test-4/651worldanyoneBOý“c~—mý“‘' /test-2/661worldanyoneBOý“cŠný“‘' /test-9/667worldanyoneBOý“cŠšoý“‘' /test-8/664worldanyoneBOý“c~Ÿpý“‘( /test-0/669worldanyoneBOý“cz—qý“‘( /test-5/661worldanyoneBOý“baÚrý“‘* /test-1/728worldanyoneBOý“cz”sý“‘* /test-3/658worldanyoneBOý“cz•tý“‘, /test-7/659worldanyoneBOý“ba×uý“‘, /test-6/725worldanyoneBOý“cŠžvý“‘2 /test-9/668worldanyoneBOý“c~Žwý“‘3 /test-4/652worldanyoneBOý“cŠ›xý“‘3 /test-8/665worldanyoneBOý“c~ yý“‘3 /test-0/670worldanyoneBOý“c~˜zý“‘3 /test-2/662worldanyoneBOý“cz˜{ý“‘3 /test-5/662worldanyoneBOý“baÛ|ý“‘3 /test-1/729worldanyoneBOý“cz•}ý“‘4 /test-3/659worldanyoneBOý“cz–~ý“‘4 /test-7/660worldanyoneBOý“baØý“‘5 /test-6/726worldanyoneBOý“cŠŸ€ý“‘; /test-9/669worldanyoneBOý“baÜý“‘> /test-1/730worldanyoneBOý“baÙ‚ý“‘? /test-6/727worldanyoneBOý“cz—ƒý“‘? /test-7/661worldanyoneBOý“cŠœ„ý“‘? /test-8/666worldanyoneBOý“c~™…ý“‘? /test-2/663worldanyoneBOý“c~†ý“‘? /test-4/653worldanyoneBOý“c~¡‡ý“‘? /test-0/671worldanyoneBOý“cz–ˆý“‘@ /test-3/660worldanyoneBOý“cz™‰ý“‘@ /test-5/663worldanyoneBOý“cŠ Šý“‘D /test-9/670worldanyoneBOý“cŠ‹ý“‘H /test-8/667worldanyoneBOý“baÚŒý“‘I /test-6/728worldanyoneBOý“baÝý“‘I /test-1/731worldanyoneBOý“cz˜Žý“‘J /test-7/662worldanyoneBOý“c~ý“‘K /test-4/654worldanyoneBOý“cz—ý“‘K /test-3/661worldanyoneBOý“czš‘ý“‘K /test-5/664worldanyoneBOý“c~š’ý“‘K /test-2/664worldanyoneBOý“c~¢“ý“‘K /test-0/672worldanyoneBOý“cŠ¡”ý“‘L /test-9/671worldanyoneBOý“cŠž•ý“‘Q /test-8/668worldanyoneBOý“baÛ–ý“‘W /test-6/729worldanyoneBOý“baÞ—ý“‘W /test-1/732worldanyoneBOý“c~‘˜ý“‘W /test-4/655worldanyoneBOý“cŠ¢™ý“‘W /test-9/672worldanyoneBOý“c~›šý“‘X /test-2/665worldanyoneBOý“cz™›ý“‘X /test-7/663worldanyoneBOý“cz˜œý“‘X /test-3/662worldanyoneBOý“cz›ý“‘X /test-5/665worldanyoneBOý“c~£žý“‘X /test-0/673worldanyoneBOý“cŠŸŸý“‘Z /test-8/669worldanyoneBOý“cŠ£ ý“‘a /test-9/673worldanyoneBOý“baÜ¡ý“‘a /test-6/730worldanyoneBOý“baߢý“‘b /test-1/733worldanyoneBOý“c~’£ý“‘b /test-4/656worldanyoneBOý“czœ¤ý“‘c /test-5/666worldanyoneBOý“cŠ ¥ý“‘e /test-8/670worldanyoneBOý“czš¦ý“‘e /test-7/664worldanyoneBOý“cz™§ý“‘f /test-3/663worldanyoneBOý“c~¤¨ý“‘f /test-0/674worldanyoneBOý“c~œ©ý“‘f /test-2/666worldanyoneBOý“baݪý“‘k /test-6/731worldanyoneBOý“baà«ý“‘k /test-1/734worldanyoneBOý“cФ¬ý“‘l /test-9/674worldanyoneBOý“c~“­ý“‘l /test-4/657worldanyoneBOý“cz®ý“‘m /test-5/667worldanyoneBOý“cz›¯ý“‘p /test-7/665worldanyoneBOý“c~¥°ý“‘p /test-0/675worldanyoneBOý“czš±ý“‘p /test-3/664worldanyoneBOý“c~²ý“‘p /test-2/667worldanyoneBOý“cŠ¡³ý“‘p /test-8/671worldanyoneBOý“baÞ´ý“‘t /test-6/732worldanyoneBOý“baáµý“‘u /test-1/735worldanyoneBOý“czž¶ý“‘v /test-5/668worldanyoneBOý“cŠ¥·ý“‘v /test-9/675worldanyoneBOý“c~”¸ý“‘w /test-4/658worldanyoneBOý“czœ¹ý“‘z /test-7/666worldanyoneBOý“cz›ºý“‘z /test-3/665worldanyoneBOý“cŠ¢»ý“‘z /test-8/672worldanyoneBOý“c~ž¼ý“‘z /test-2/668worldanyoneBOý“c~¦½ý“‘z /test-0/676worldanyoneBOý“baß¾ý“‘| /test-6/733worldanyoneBOý“baâ¿ý“‘} /test-1/736worldanyoneBOý“cЦÀý“‘€ /test-9/676worldanyoneBOý“c~•Áý“‘€ /test-4/659worldanyoneBOý“czŸÂý“‘ /test-5/669worldanyoneBOý“czÃý“‘ƒ /test-7/667worldanyoneBOý“czœÄý“‘ƒ /test-3/666worldanyoneBOý“c~§Åý“‘„ /test-0/677worldanyoneBOý“cŠ£Æý“‘„ /test-8/673worldanyoneBOý“baàÇý“‘„ /test-6/734worldanyoneBOý“c~ŸÈý“‘„ /test-2/669worldanyoneBOý“baãÉý“‘… /test-1/737worldanyoneBOý“cЧÊý“‘ˆ /test-9/677worldanyoneBOý“c~–Ëý“‘‰ /test-4/660worldanyoneBOý“cz Ìý“‘Š /test-5/670worldanyoneBOý“baáÍý“‘Ž /test-6/735worldanyoneBOý“baäÎý“‘Ž /test-1/738worldanyoneBOý“czžÏý“‘ /test-7/668worldanyoneBOý“c~¨Ðý“‘ /test-0/678worldanyoneBOý“c~ Ñý“‘ /test-2/670worldanyoneBOý“czÒý“‘ /test-3/667worldanyoneBOý“cФÓý“‘ /test-8/674worldanyoneBOý“cЍÔý“‘‘ /test-9/678worldanyoneBOý“cz¡Õý“‘“ /test-5/671worldanyoneBOý“c~—Öý“‘“ /test-4/661worldanyoneBOý“czŸ×ý“‘™ /test-7/669worldanyoneBOý“czžØý“‘™ /test-3/668worldanyoneBOý“c~¡Ùý“‘š /test-2/671worldanyoneBOý“baåÚý“‘› /test-1/739worldanyoneBOý“baâÛý“‘› /test-6/736worldanyoneBOý“c~©Üý“‘› /test-0/679worldanyoneBOý“cŠ©Ýý“‘› /test-9/679worldanyoneBOý“cŠ¥Þý“‘› /test-8/675worldanyoneBOý“c~˜ßý“‘œ /test-4/662worldanyoneBOý“cz¢àý“‘œ /test-5/672worldanyoneBOý“cz áý“‘¢ /test-7/670worldanyoneBOý“czŸâý“‘¢ /test-3/669worldanyoneBOý“baæãý“‘¦ /test-1/740worldanyoneBOý“baãäý“‘¦ /test-6/737worldanyoneBOý“c~¢åý“‘¦ /test-2/672worldanyoneBOý“c~ªæý“‘¦ /test-0/680worldanyoneBOý“cЦçý“‘¦ /test-8/676worldanyoneBOý“cŠªèý“‘¦ /test-9/680worldanyoneBOý“c~™éý“‘§ /test-4/663worldanyoneBOý“cz£êý“‘§ /test-5/673worldanyoneBOý“cz¡ëý“‘« /test-7/671worldanyoneBOý“cz ìý“‘¬ /test-3/670worldanyoneBOý“baçíý“‘° /test-1/741worldanyoneBOý“cŠ«îý“‘° /test-9/681worldanyoneBOý“cЧïý“‘° /test-8/677worldanyoneBOý“baäðý“‘² /test-6/738worldanyoneBOý“c~šñý“‘² /test-4/664worldanyoneBOý“c~£òý“‘² /test-2/673worldanyoneBOý“c~«óý“‘² /test-0/681worldanyoneBOý“cz¤ôý“‘² /test-5/674worldanyoneBOý“cz¢õý“‘³ /test-7/672worldanyoneBOý“cz¡öý“‘µ /test-3/671worldanyoneBOý“baè÷ý“‘¹ /test-1/742worldanyoneBOý“cЬøý“‘¹ /test-9/682worldanyoneBOý“cЍùý“‘¹ /test-8/678worldanyoneBOý“baåúý“‘½ /test-6/739worldanyoneBOý“c~›ûý“‘½ /test-4/665worldanyoneBOý“cz£üý“‘¾ /test-7/673worldanyoneBOý“cz¥ýý“‘¾ /test-5/675worldanyoneBOý“cz¢þý“‘¿ /test-3/672worldanyoneBOý“c~¬ÿý“‘¿ /test-0/682worldanyoneBOý“c~¤ý“‘¿ /test-2/674worldanyoneBOý“baéý“‘ /test-1/743worldanyoneBOý“cŠ©ý“‘ /test-8/679worldanyoneBOý“cŠ­ý“‘ /test-9/683worldanyoneBOý“baæý“‘Æ /test-6/740worldanyoneBOý“c~œý“‘È /test-4/666worldanyoneBOý“cz¤ý“‘Ì /test-7/674worldanyoneBOý“cz¦ý“‘Ì /test-5/676worldanyoneBOý“cz£ý“‘Ì /test-3/673worldanyoneBOý“baê ý“‘Í /test-1/744worldanyoneBOý“cŠª ý“‘Í /test-8/680worldanyoneBOý“cŠ® ý“‘Î /test-9/684worldanyoneBOý“baç ý“‘Ï /test-6/741worldanyoneBOý“c~­ ý“‘Ï /test-0/683worldanyoneBOý“c~¥ý“‘Ï /test-2/675worldanyoneBOý“c~ý“‘Ñ /test-4/667worldanyoneBOý“c~®ý“‘Ú /test-0/684worldanyoneBOý“c~¦ý“‘Û /test-2/676worldanyoneBOý“cz¥ý“‘Ü /test-7/675worldanyoneBOý“c~žý“‘Ü /test-4/668worldanyoneBOý“cŠ«ý“‘Ý /test-8/681worldanyoneBOý“baëý“‘ß /test-1/745worldanyoneBOý“cНý“‘ß /test-9/685worldanyoneBOý“cz§ý“‘á /test-5/677worldanyoneBOý“baèý“‘ã /test-6/742worldanyoneBOý“cz¤ý“‘ã /test-3/674worldanyoneBOý“c~¯ý“‘ä /test-0/685worldanyoneBOý“c~§ý“‘ç /test-2/677worldanyoneBOý“c~Ÿý“‘ç /test-4/669worldanyoneBOý“cz¦ý“‘ç /test-7/676worldanyoneBOý“baìý“‘é /test-1/746worldanyoneBOý“cЬý“‘ê /test-8/682worldanyoneBOý“cа ý“‘ë /test-9/686worldanyoneBOý“cz¥!ý“‘ð /test-3/675worldanyoneBOý“cz¨"ý“‘ð /test-5/678worldanyoneBOý“c~°#ý“‘ò /test-0/686worldanyoneBOý“cz§$ý“‘ó /test-7/677worldanyoneBOý“c~¨%ý“‘ô /test-2/678worldanyoneBOý“c~ &ý“‘ô /test-4/670worldanyoneBOý“baí'ý“‘õ /test-1/747worldanyoneBOý“baé(ý“‘÷ /test-6/743worldanyoneBOý“cб)ý“‘÷ /test-9/687worldanyoneBOý“cŠ­*ý“‘÷ /test-8/683worldanyoneBOý“cz¦+ý“‘û /test-3/676worldanyoneBOý“cz©,ý“‘ü /test-5/679worldanyoneBOý“c~±-ý“‘ý /test-0/687worldanyoneBOý“cz¨.ý“‘ÿ /test-7/678worldanyoneBOý“c~©/ý“‘ÿ /test-2/679worldanyoneBOý“c~¡0ý“’ /test-4/671worldanyoneBOý“cв1ý“’ /test-9/688worldanyoneBOý“cŠ®2ý“’ /test-8/684worldanyoneBOý“baê3ý“’ /test-6/744worldanyoneBOý“baî4ý“’ /test-1/748worldanyoneBOý“czª5ý“’ /test-5/680worldanyoneBOý“cz§6ý“’ /test-3/677worldanyoneBOý“c~²7ý“’ /test-0/688worldanyoneBOý“cz©8ý“’ /test-7/679worldanyoneBOý“cг9ý“’  /test-9/689worldanyoneBOý“cН:ý“’  /test-8/685worldanyoneBOý“c~¢;ý“’  /test-4/672worldanyoneBOý“c~ª<ý“’  /test-2/680worldanyoneBOý“baë=ý“’  /test-6/745worldanyoneBOý“baï>ý“’  /test-1/749worldanyoneBOý“cz«?ý“’ /test-5/681worldanyoneBOý“cz¨@ý“’ /test-3/678worldanyoneBOý“czªAý“’ /test-7/680worldanyoneBOý“c~³Bý“’ /test-0/689worldanyoneBOý“cŠ´Cý“’ /test-9/690worldanyoneBOý“baðDý“’ /test-1/750worldanyoneBOý“baìEý“’ /test-6/746worldanyoneBOý“cаFý“’ /test-8/686worldanyoneBOý“c~«Gý“’ /test-2/681worldanyoneBOý“c~£Hý“’ /test-4/673worldanyoneBOý“cz¬Iý“’ /test-5/682worldanyoneBOý“cz©Jý“’ /test-3/679worldanyoneBOý“c~´Ký“’ /test-0/690worldanyoneBOý“cz«Lý“’ /test-7/681worldanyoneBOý“cеMý“’ /test-9/691worldanyoneBOý“baíNý“’! /test-6/747worldanyoneBOý“bañOý“’! /test-1/751worldanyoneBOý“cz­Pý“’" /test-5/683worldanyoneBOý“cбQý“’" /test-8/687worldanyoneBOý“c~¬Rý“’# /test-2/682worldanyoneBOý“czªSý“’# /test-3/680worldanyoneBOý“c~¤Tý“’$ /test-4/674worldanyoneBOý“c~µUý“’$ /test-0/691worldanyoneBOý“cz¬Vý“’$ /test-7/682worldanyoneBOý“cжWý“’% /test-9/692worldanyoneBOý“baòXý“’* /test-1/752worldanyoneBOý“baîYý“’* /test-6/748worldanyoneBOý“cz­Zý“’/ /test-7/683worldanyoneBOý“cz«[ý“’0 /test-3/681worldanyoneBOý“cв\ý“’0 /test-8/688worldanyoneBOý“cŠ·]ý“’0 /test-9/693worldanyoneBOý“c~­^ý“’0 /test-2/683worldanyoneBOý“c~¶_ý“’0 /test-0/692worldanyoneBOý“c~¥`ý“’0 /test-4/675worldanyoneBOý“cz®aý“’0 /test-5/684worldanyoneBOý“baïbý“’3 /test-6/749worldanyoneBOý“baócý“’3 /test-1/753worldanyoneBOý“cЏdý“’: /test-9/694worldanyoneBOý“cz®eý“’: /test-7/684worldanyoneBOý“cz¬fý“’; /test-3/682worldanyoneBOý“cгgý“’; /test-8/689worldanyoneBOý“cz¯hý“’; /test-5/685worldanyoneBOý“c~®iý“’< /test-2/684worldanyoneBOý“c~·jý“’< /test-0/693worldanyoneBOý“baôký“’< /test-1/754worldanyoneBOý“baðlý“’= /test-6/750worldanyoneBOý“c~¦mý“’= /test-4/676worldanyoneBOý“cz­ný“’D /test-3/683worldanyoneBOý“cŠ´oý“’D /test-8/690worldanyoneBOý“cz°pý“’E /test-5/686worldanyoneBOý“cz¯qý“’E /test-7/685worldanyoneBOý“cйrý“’E /test-9/695worldanyoneBOý“c~¸sý“’F /test-0/694worldanyoneBOý“c~¯tý“’F /test-2/685worldanyoneBOý“bañuý“’G /test-6/751worldanyoneBOý“baõvý“’G /test-1/755worldanyoneBOý“c~§wý“’G /test-4/677worldanyoneBOý“cz®xý“’M /test-3/684worldanyoneBOý“cz±yý“’N /test-5/687worldanyoneBOý“cеzý“’N /test-8/691worldanyoneBOý“cŠº{ý“’O /test-9/696worldanyoneBOý“cz°|ý“’P /test-7/686worldanyoneBOý“c~¹}ý“’P /test-0/695worldanyoneBOý“c~°~ý“’Q /test-2/686worldanyoneBOý“baöý“’Q /test-1/756worldanyoneBOý“baò€ý“’Q /test-6/752worldanyoneBOý“c~¨ý“’Q /test-4/678worldanyoneBOý“cz¯‚ý“’V /test-3/685worldanyoneBOý“cz²ƒý“’W /test-5/688worldanyoneBOý“cж„ý“’W /test-8/692worldanyoneBOý“cŠ»…ý“’Z /test-9/697worldanyoneBOý“cz±†ý“’Z /test-7/687worldanyoneBOý“baó‡ý“’[ /test-6/753worldanyoneBOý“ba÷ˆý“’[ /test-1/757worldanyoneBOý“c~º‰ý“’\ /test-0/696worldanyoneBOý“c~±Šý“’\ /test-2/687worldanyoneBOý“c~©‹ý“’\ /test-4/679worldanyoneBOý“cz°Œý“’^ /test-3/686worldanyoneBOý“baôý“”) /test-6/754worldanyoneBOý“baøŽý“”) /test-1/758worldanyoneBOý“cŠ·ý“”) /test-8/693worldanyoneBOý“c~²ý“”* /test-2/688worldanyoneBOý“cм‘ý“”* /test-9/698worldanyoneBOý“cz³’ý“”* /test-5/689worldanyoneBOý“cz²“ý“”* /test-7/688worldanyoneBOý“c~ª”ý“”* /test-4/680worldanyoneBOý“c~»•ý“”, /test-0/697worldanyoneBOý“cz±–ý“”0 /test-3/687worldanyoneBOý“baõ—ý“”5 /test-6/755worldanyoneBOý“baù˜ý“”6 /test-1/759worldanyoneBOý“cЏ™ý“”6 /test-8/694worldanyoneBOý“cz³šý“”7 /test-7/689worldanyoneBOý“cz´›ý“”7 /test-5/690worldanyoneBOý“c~³œý“”7 /test-2/689worldanyoneBOý“c~«ý“”8 /test-4/681worldanyoneBOý“c~¼žý“”8 /test-0/698worldanyoneBOý“cz²Ÿý“”8 /test-3/688worldanyoneBOý“baö ý“”= /test-6/756worldanyoneBOý“cн¡ý“”= /test-9/699worldanyoneBOý“cz´¢ý“”B /test-7/690worldanyoneBOý“czµ£ý“”B /test-5/691worldanyoneBOý“cй¤ý“”C /test-8/695worldanyoneBOý“c~´¥ý“”C /test-2/690worldanyoneBOý“c~¬¦ý“”C /test-4/682worldanyoneBOý“baú§ý“”C /test-1/760worldanyoneBOý“c~½¨ý“”I /test-0/699worldanyoneBOý“ba÷©ý“”J /test-6/757worldanyoneBOý“cоªý“”J /test-9/700worldanyoneBOý“cz³«ý“”J /test-3/689worldanyoneBOý“baû¬ý“”L /test-1/761worldanyoneBOý“c~µ­ý“”L /test-2/691worldanyoneBOý“cŠº®ý“”L /test-8/696worldanyoneBOý“czµ¯ý“”L /test-7/691worldanyoneBOý“cz¶°ý“”M /test-5/692worldanyoneBOý“c~­±ý“”M /test-4/683worldanyoneBOý“c~¾²ý“”R /test-0/700worldanyoneBOý“baø³ý“”S /test-6/758worldanyoneBOý“cŠ¿´ý“”T /test-9/701worldanyoneBOý“cz´µý“”T /test-3/690worldanyoneBOý“baü¶ý“”V /test-1/762worldanyoneBOý“cŠ»·ý“”V /test-8/697worldanyoneBOý“c~¶¸ý“”V /test-2/692worldanyoneBOý“c~®¹ý“”V /test-4/684worldanyoneBOý“cz¶ºý“”W /test-7/692worldanyoneBOý“cz·»ý“”W /test-5/693worldanyoneBOý“c~¿¼ý“”Z /test-0/701worldanyoneBOý“baù½ý“”[ /test-6/759worldanyoneBOý“cŠÀ¾ý“”^ /test-9/702worldanyoneBOý“czµ¿ý“”_ /test-3/691worldanyoneBOý“baýÀý“”` /test-1/763worldanyoneBOý“c~·Áý“”` /test-2/693worldanyoneBOý“cмÂý“”` /test-8/698worldanyoneBOý“c~¯Ãý“”` /test-4/685worldanyoneBOý“cz·Äý“”b /test-7/693worldanyoneBOý“cz¸Åý“”b /test-5/694worldanyoneBOý“c~ÀÆý“”c /test-0/702worldanyoneBOý“baúÇý“”c /test-6/760worldanyoneBOý“cŠÁÈý“”h /test-9/703worldanyoneBOý“cz¶Éý“”h /test-3/692worldanyoneBOý“baþÊý“”j /test-1/764worldanyoneBOý“cнËý“”j /test-8/699worldanyoneBOý“c~°Ìý“”j /test-4/686worldanyoneBOý“c~¸Íý“”k /test-2/694worldanyoneBOý“c~ÁÎý“”l /test-0/703worldanyoneBOý“baûÏý“”m /test-6/761worldanyoneBOý“cz¸Ðý“”n /test-7/694worldanyoneBOý“cz¹Ñý“”n /test-5/695worldanyoneBOý“cz·Òý“”q /test-3/693worldanyoneBOý“cŠÂÓý“”q /test-9/704worldanyoneBOý“baÿÔý“”t /test-1/765worldanyoneBOý“cоÕý“”t /test-8/700worldanyoneBOý“c~±Öý“”t /test-4/687worldanyoneBOý“c~¹×ý“”t /test-2/695worldanyoneBOý“c~ÂØý“”v /test-0/704worldanyoneBOý“cz¹Ùý“”w /test-7/695worldanyoneBOý“baüÚý“”x /test-6/762worldanyoneBOý“czºÛý“”x /test-5/696worldanyoneBOý“cz¸Üý“”y /test-3/694worldanyoneBOý“cŠÃÝý“”z /test-9/705worldanyoneBOý“baÞý“”} /test-1/766worldanyoneBOý“c~²ßý“”~ /test-4/688worldanyoneBOý“c~ºàý“”~ /test-2/696worldanyoneBOý“cŠ¿áý“”~ /test-8/701worldanyoneBOý“c~Ãâý“”€ /test-0/705worldanyoneBOý“baýãý“” /test-6/763worldanyoneBOý“cz¹äý“” /test-3/695worldanyoneBOý“czºåý“”‚ /test-7/696worldanyoneBOý“cz»æý“”‚ /test-5/697worldanyoneBOý“cŠÄçý“”‚ /test-9/706worldanyoneBOý“baèý“”… /test-1/767worldanyoneBOý“cŠÀéý“”ˆ /test-8/702worldanyoneBOý“c~»êý“”‰ /test-2/697worldanyoneBOý“c~³ëý“”‰ /test-4/689worldanyoneBOý“c~Äìý“”Š /test-0/706worldanyoneBOý“baþíý“”‹ /test-6/764worldanyoneBOý“cŠÅîý“”‹ /test-9/707worldanyoneBOý“cz»ïý“”‹ /test-7/697worldanyoneBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/upgrade/log.100001bf00100644 0000000 0000000 00003722000 15051152474 030563 0ustar00rootroot0000000 0000000 Oý“cz¼ðý“”‹ /test-5/698worldanyoneBOý“czºñý“”Œ /test-3/696worldanyoneBOý“baòý“” /test-1/768worldanyoneBOý“cŠÁóý“”’ /test-8/703worldanyoneBOý“c~´ôý“”• /test-4/690worldanyoneBOý“c~¼õý“”• /test-2/698worldanyoneBOý“c~Åöý“”• /test-0/707worldanyoneBOý“ba÷ý“”˜ /test-1/769worldanyoneBOý“baÿøý“”™ /test-6/765worldanyoneBOý“cŠÆùý“”™ /test-9/708worldanyoneBOý“cz»úý“”™ /test-3/697worldanyoneBOý“cz½ûý“”™ /test-5/699worldanyoneBOý“cz¼üý“”™ /test-7/698worldanyoneBOý“cŠÂýý“” /test-8/704worldanyoneBOý“c~½þý“”  /test-2/699worldanyoneBOý“c~Æÿý“”¡ /test-0/708worldanyoneBOý“c~µý“”¡ /test-4/691worldanyoneBOý“baý“”¥ /test-6/766worldanyoneBOý“baý“”¥ /test-1/770worldanyoneBOý“cŠÇý“”¦ /test-9/709worldanyoneBOý“cz½ý“”¦ /test-7/699worldanyoneBOý“cz¼ý“”¦ /test-3/698worldanyoneBOý“cz¾ý“”¦ /test-5/700worldanyoneBOý“cŠÃý“”§ /test-8/705worldanyoneBOý“c~¾ý“”« /test-2/700worldanyoneBOý“c~Ç ý“”¬ /test-0/709worldanyoneBOý“c~¶ ý“”¬ /test-4/692worldanyoneBOý“ba ý“”¯ /test-1/771worldanyoneBOý“ba ý“”¯ /test-6/767worldanyoneBOý“cŠÈ ý“”¯ /test-9/710worldanyoneBOý“cz½ý“”° /test-3/699worldanyoneBOý“cz¿ý“”± /test-5/701worldanyoneBOý“cz¾ý“”± /test-7/700worldanyoneBOý“cŠÄý“”² /test-8/706worldanyoneBOý“c~·ý“”µ /test-4/693worldanyoneBOý“c~Èý“”µ /test-0/710worldanyoneBOý“c~¿ý“”¶ /test-2/701worldanyoneBOý“baý“”¸ /test-6/768worldanyoneBOý“baý“”¸ /test-1/772worldanyoneBOý“cŠÉý“”¹ /test-9/711worldanyoneBOý“cz¾ý“”¼ /test-3/700worldanyoneBOý“cz¿ý“”¼ /test-7/701worldanyoneBOý“cŠÅý“”½ /test-8/707worldanyoneBOý“czÀý“”½ /test-5/702worldanyoneBOý“c~¸ý“”¿ /test-4/694worldanyoneBOý“c~Éý“”¿ /test-0/711worldanyoneBOý“c~Àý“”À /test-2/702worldanyoneBOý“baý“”Ä /test-1/773worldanyoneBOý“ba ý“”Ä /test-6/769worldanyoneBOý“cŠÊ!ý“”Ä /test-9/712worldanyoneBOý“cŠÆ"ý“”Æ /test-8/708worldanyoneBOý“cz¿#ý“”Æ /test-3/701worldanyoneBOý“czÀ$ý“”Ç /test-7/702worldanyoneBOý“czÁ%ý“”È /test-5/703worldanyoneBOý“c~¹&ý“”È /test-4/695worldanyoneBOý“c~Ê'ý“”È /test-0/712worldanyoneBOý“c~Á(ý“”È /test-2/703worldanyoneBOý“ba)ý“”Î /test-6/770worldanyoneBOý“ba*ý“”Î /test-1/774worldanyoneBOý“cŠË+ý“”Ï /test-9/713worldanyoneBOý“czÀ,ý“”Ï /test-3/702worldanyoneBOý“cŠÇ-ý“”Ñ /test-8/709worldanyoneBOý“c~Ë.ý“”Ñ /test-0/713worldanyoneBOý“czÁ/ý“”Ò /test-7/703worldanyoneBOý“c~Â0ý“”Ò /test-2/704worldanyoneBOý“c~º1ý“”Ò /test-4/696worldanyoneBOý“czÂ2ý“”Ó /test-5/704worldanyoneBOý“ba3ý“”Ú /test-6/771worldanyoneBOý“cŠÌ4ý“”Û /test-9/714worldanyoneBOý“ba 5ý“”Û /test-1/775worldanyoneBOý“cŠÈ6ý“”Ü /test-8/710worldanyoneBOý“czÁ7ý“”Ü /test-3/703worldanyoneBOý“c~Ì8ý“”Ü /test-0/714worldanyoneBOý“czÃ9ý“”Ý /test-5/705worldanyoneBOý“c~Ã:ý“”Ý /test-2/705worldanyoneBOý“c~»;ý“”Þ /test-4/697worldanyoneBOý“czÂ<ý“”Þ /test-7/704worldanyoneBOý“ba=ý“”å /test-6/772worldanyoneBOý“ba >ý“”æ /test-1/776worldanyoneBOý“czÂ?ý“”æ /test-3/704worldanyoneBOý“c~Í@ý“”ç /test-0/715worldanyoneBOý“cŠÉAý“”ç /test-8/711worldanyoneBOý“cŠÍBý“”ç /test-9/715worldanyoneBOý“c~ÄCý“”è /test-2/706worldanyoneBOý“c~¼Dý“”é /test-4/698worldanyoneBOý“czÄEý“”é /test-5/706worldanyoneBOý“czÃFý“”é /test-7/705worldanyoneBOý“baGý“”ï /test-6/773worldanyoneBOý“ba Hý“”ò /test-1/777worldanyoneBOý“czÃIý“”ò /test-3/705worldanyoneBOý“c~ÎJý“”ò /test-0/716worldanyoneBOý“cŠÎKý“”ó /test-9/716worldanyoneBOý“c~ÅLý“”ó /test-2/707worldanyoneBOý“cŠÊMý“”ó /test-8/712worldanyoneBOý“czÅNý“”ó /test-5/707worldanyoneBOý“c~½Oý“”ô /test-4/699worldanyoneBOý“czÄPý“”õ /test-7/706worldanyoneBOý“baQý“”ø /test-6/774worldanyoneBOý“ba Rý“”þ /test-1/778worldanyoneBOý“c~ÆSý“”ÿ /test-2/708worldanyoneBOý“cŠËTý“”ÿ /test-8/713worldanyoneBOý“cŠÏUý“”ÿ /test-9/717worldanyoneBOý“c~ÏVý“• /test-0/717worldanyoneBOý“czÆWý“• /test-5/708worldanyoneBOý“c~¾Xý“• /test-4/700worldanyoneBOý“czÅYý“• /test-7/707worldanyoneBOý“ba Zý“• /test-6/775worldanyoneBOý“czÄ[ý“• /test-3/706worldanyoneBOý“ba \ý“•  /test-1/779worldanyoneBOý“c~Ç]ý“•  /test-2/709worldanyoneBOý“c~Ð^ý“•  /test-0/718worldanyoneBOý“c~¿_ý“•  /test-4/701worldanyoneBOý“cŠÌ`ý“• /test-8/714worldanyoneBOý“cŠÐaý“• /test-9/718worldanyoneBOý“czÇbý“• /test-5/709worldanyoneBOý“czÆcý“• /test-7/708worldanyoneBOý“czÅdý“• /test-3/707worldanyoneBOý“ba eý“• /test-6/776worldanyoneBOý“bafý“• /test-1/780worldanyoneBOý“c~Ègý“• /test-2/710worldanyoneBOý“c~Ñhý“• /test-0/719worldanyoneBOý“c~Àiý“• /test-4/702worldanyoneBOý“ba jý“• /test-6/777worldanyoneBOý“czÇký“• /test-7/709worldanyoneBOý“cŠÑlý“• /test-9/719worldanyoneBOý“cŠÍmý“• /test-8/715worldanyoneBOý“czÆný“• /test-3/708worldanyoneBOý“czÈoý“• /test-5/710worldanyoneBOý“c~Épý“•$ /test-2/711worldanyoneBOý“c~Òqý“•$ /test-0/720worldanyoneBOý“c~Árý“•% /test-4/703worldanyoneBOý“basý“•& /test-1/781worldanyoneBOý“ba tý“•& /test-6/778worldanyoneBOý“cŠÎuý“•( /test-8/716worldanyoneBOý“cŠÒvý“•) /test-9/720worldanyoneBOý“czÈwý“•) /test-7/710worldanyoneBOý“czÇxý“•* /test-3/709worldanyoneBOý“czÉyý“•+ /test-5/711worldanyoneBOý“c~Êzý“•/ /test-2/712worldanyoneBOý“c~Ó{ý“•/ /test-0/721worldanyoneBOý“ba|ý“•/ /test-1/782worldanyoneBOý“c~Â}ý“•/ /test-4/704worldanyoneBOý“ba ~ý“•0 /test-6/779worldanyoneBOý“czÊý“•5 /test-5/712worldanyoneBOý“cŠÏ€ý“•6 /test-8/717worldanyoneBOý“cŠÓý“•6 /test-9/721worldanyoneBOý“czÉ‚ý“•7 /test-7/711worldanyoneBOý“czȃý“•7 /test-3/710worldanyoneBOý“c~Ô„ý“•9 /test-0/722worldanyoneBOý“c~Ë…ý“•: /test-2/713worldanyoneBOý“c~Æý“•: /test-4/705worldanyoneBOý“ba‡ý“•; /test-6/780worldanyoneBOý“baˆý“•; /test-1/783worldanyoneBOý“czˉý“•? /test-5/713worldanyoneBOý“czÊŠý“•C /test-7/712worldanyoneBOý“czÉ‹ý“•C /test-3/711worldanyoneBOý“cŠÐŒý“•E /test-8/718worldanyoneBOý“cŠÔý“•E /test-9/722worldanyoneBOý“c~ÕŽý“•E /test-0/723worldanyoneBOý“c~Ìý“•E /test-2/714worldanyoneBOý“baý“•F /test-6/781worldanyoneBOý“ba‘ý“•F /test-1/784worldanyoneBOý“c~Ä’ý“•G /test-4/706worldanyoneBOý“czÌ“ý“•J /test-5/714worldanyoneBOý“czË”ý“•Q /test-7/713worldanyoneBOý“czÊ•ý“•Q /test-3/712worldanyoneBOý“c~Ö–ý“•R /test-0/724worldanyoneBOý“ba—ý“•S /test-1/785worldanyoneBOý“ba˜ý“•T /test-6/782worldanyoneBOý“cŠÕ™ý“•T /test-9/723worldanyoneBOý“c~Åšý“•T /test-4/707worldanyoneBOý“c~Í›ý“•T /test-2/715worldanyoneBOý“czÍœý“•T /test-5/715worldanyoneBOý“cŠÑý“•U /test-8/719worldanyoneBOý“czÌžý“•\ /test-7/714worldanyoneBOý“czËŸý“•] /test-3/713worldanyoneBOý“cŠÒ ý“•d /test-8/720worldanyoneBOý“cŠÖ¡ý“•d /test-9/724worldanyoneBOý“c~Æ¢ý“•e /test-4/708worldanyoneBOý“c~×£ý“•e /test-0/725worldanyoneBOý“czͤý“•h /test-7/715worldanyoneBOý“czÌ¥ý“•k /test-3/714worldanyoneBOý“c~Φý“•l /test-2/716worldanyoneBOý“czΧý“•m /test-5/716worldanyoneBOý“ba¨ý“•m /test-1/786worldanyoneBOý“ba©ý“•m /test-6/783worldanyoneBOý“c~Ǫý“•q /test-4/709worldanyoneBOý“czΫý“•r /test-7/716worldanyoneBOý“cŠÓ¬ý“•s /test-8/721worldanyoneBOý“c~Ø­ý“•s /test-0/726worldanyoneBOý“cŠ×®ý“•t /test-9/725worldanyoneBOý“czͯý“•v /test-3/715worldanyoneBOý“ba°ý“•w /test-6/784worldanyoneBOý“ba±ý“•x /test-1/787worldanyoneBOý“c~ϲý“•y /test-2/717worldanyoneBOý“czϳý“•z /test-5/717worldanyoneBOý“c~È´ý“•{ /test-4/710worldanyoneBOý“c~Ùµý“•} /test-0/727worldanyoneBOý“cŠÔ¶ý“•~ /test-8/722worldanyoneBOý“cŠØ·ý“• /test-9/726worldanyoneBOý“czϸý“• /test-7/717worldanyoneBOý“czιý“• /test-3/716worldanyoneBOý“baºý“•ƒ /test-1/788worldanyoneBOý“c~лý“•„ /test-2/718worldanyoneBOý“ba¼ý“•… /test-6/785worldanyoneBOý“czнý“•‡ /test-5/718worldanyoneBOý“c~ɾý“•‡ /test-4/711worldanyoneBOý“c~Ú¿ý“•Š /test-0/728worldanyoneBOý“cŠÕÀý“•Š /test-8/723worldanyoneBOý“czÐÁý“•Œ /test-7/718worldanyoneBOý“baÂý“• /test-1/789worldanyoneBOý“cŠÙÃý“• /test-9/727worldanyoneBOý“baÄý“• /test-6/786worldanyoneBOý“czÏÅý“• /test-3/717worldanyoneBOý“czÑÆý“•’ /test-5/719worldanyoneBOý“c~ÑÇý“•“ /test-2/719worldanyoneBOý“c~ÊÈý“•” /test-4/712worldanyoneBOý“cŠÖÉý“•– /test-8/724worldanyoneBOý“czÑÊý“•š /test-7/719worldanyoneBOý“c~ÛËý“•š /test-0/729worldanyoneBOý“baÌý“•š /test-1/790worldanyoneBOý“baÍý“•œ /test-6/787worldanyoneBOý“czÐÎý“•œ /test-3/718worldanyoneBOý“cŠÚÏý“•œ /test-9/728worldanyoneBOý“c~ÒÐý“•Ÿ /test-2/720worldanyoneBOý“c~ËÑý“•Ÿ /test-4/713worldanyoneBOý“czÒÒý“•Ÿ /test-5/720worldanyoneBOý“cŠ×Óý“•  /test-8/725worldanyoneBOý“baÔý“•¦ /test-1/791worldanyoneBOý“czÒÕý“•¦ /test-7/720worldanyoneBOý“c~ÜÖý“•§ /test-0/730worldanyoneBOý“czÑ×ý“•§ /test-3/719worldanyoneBOý“baØý“•© /test-6/788worldanyoneBOý“cŠÛÙý“•ª /test-9/729worldanyoneBOý“c~ÓÚý“•ª /test-2/721worldanyoneBOý“czÓÛý“•ª /test-5/721worldanyoneBOý“c~ÌÜý“•« /test-4/714worldanyoneBOý“cŠØÝý“•¬ /test-8/726worldanyoneBOý“baÞý“•¸ /test-1/792worldanyoneBOý“cŠÜßý“•¸ /test-9/730worldanyoneBOý“czÓàý“•º /test-7/721worldanyoneBOý“czÒáý“•» /test-3/720worldanyoneBOý“czÔâý“•» /test-5/722worldanyoneBOý“c~Ôãý“•» /test-2/722worldanyoneBOý“c~Íäý“•» /test-4/715worldanyoneBOý“baåý“•¼ /test-6/789worldanyoneBOý“cŠÙæý“•¼ /test-8/727worldanyoneBOý“c~Ýçý“•¼ /test-0/731worldanyoneBOý“czÓèý“•È /test-3/721worldanyoneBOý“czÕéý“•É /test-5/723worldanyoneBOý“baêý“•É /test-6/790worldanyoneBOý“baëý“•É /test-1/793worldanyoneBOý“czÔìý“•Ê /test-7/722worldanyoneBOý“cŠÝíý“•Ë /test-9/731worldanyoneBOý“c~Õîý“•Ë /test-2/723worldanyoneBOý“c~Îïý“•Ë /test-4/716worldanyoneBOý“cŠÚðý“•Ë /test-8/728worldanyoneBOý“c~Þñý“•Ë /test-0/732worldanyoneBOý“baòý“•Ø /test-1/794worldanyoneBOý“baóý“•Ø /test-6/791worldanyoneBOý“czÖôý“•Ø /test-5/724worldanyoneBOý“czÔõý“•Ø /test-3/722worldanyoneBOý“cŠÞöý“•Ù /test-9/732worldanyoneBOý“c~Ö÷ý“•Ù /test-2/724worldanyoneBOý“c~Ïøý“•Ú /test-4/717worldanyoneBOý“c~ßùý“•Ú /test-0/733worldanyoneBOý“cŠÛúý“•Ú /test-8/729worldanyoneBOý“czÕûý“•Ú /test-7/723worldanyoneBOý“baüý“•ä /test-6/792worldanyoneBOý“baýý“•ä /test-1/795worldanyoneBOý“cŠßþý“•ä /test-9/733worldanyoneBOý“cz×ÿý“•å /test-5/725worldanyoneBOý“czÖý“•æ /test-7/724worldanyoneBOý“cŠÜý“•æ /test-8/730worldanyoneBOý“c~×ý“•æ /test-2/725worldanyoneBOý“c~Ðý“•æ /test-4/718worldanyoneBOý“c~àý“•æ /test-0/734worldanyoneBOý“czÕý“•æ /test-3/723worldanyoneBOý“baý“–0 /test-6/793worldanyoneBOý“baý“–> /test-1/796worldanyoneBOý“baý“–? /test-6/794worldanyoneBOý“cŠà ý“–? /test-9/734worldanyoneBOý“cŠÝ ý“–? /test-8/731worldanyoneBOý“czØ ý“–@ /test-5/726worldanyoneBOý“czÖ ý“–@ /test-3/724worldanyoneBOý“cz× ý“–@ /test-7/725worldanyoneBOý“c~Ñý“–@ /test-4/719worldanyoneBOý“c~áý“–@ /test-0/735worldanyoneBOý“c~Øý“–@ /test-2/726worldanyoneBOý“baý“–G /test-1/797worldanyoneBOý“baý“–J /test-6/795worldanyoneBOý“c~Ùý“–J /test-2/727worldanyoneBOý“c~âý“–K /test-0/736worldanyoneBOý“c~Òý“–K /test-4/720worldanyoneBOý“cŠÞý“–K /test-8/732worldanyoneBOý“czÙý“–K /test-5/727worldanyoneBOý“cŠáý“–K /test-9/735worldanyoneBOý“cz×ý“–K /test-3/725worldanyoneBOý“czØý“–K /test-7/726worldanyoneBOý“ba ý“–O /test-1/798worldanyoneBOý“baý“–S /test-6/796worldanyoneBOý“cŠßý“–T /test-8/733worldanyoneBOý“c~Úý“–T /test-2/728worldanyoneBOý“cŠâý“–U /test-9/736worldanyoneBOý“czÙ ý“–V /test-7/727worldanyoneBOý“czÚ!ý“–V /test-5/728worldanyoneBOý“c~ã"ý“–V /test-0/737worldanyoneBOý“c~Ó#ý“–V /test-4/721worldanyoneBOý“czØ$ý“–V /test-3/726worldanyoneBOý“ba!%ý“–W /test-1/799worldanyoneBOý“ba&ý“–[ /test-6/797worldanyoneBOý“cŠà'ý“–_ /test-8/734worldanyoneBOý“cŠã(ý“–_ /test-9/737worldanyoneBOý“czÚ)ý“–_ /test-7/728worldanyoneBOý“czÛ*ý“–_ /test-5/729worldanyoneBOý“c~Û+ý“–` /test-2/729worldanyoneBOý“c~ä,ý“–` /test-0/738worldanyoneBOý“czÙ-ý“–a /test-3/727worldanyoneBOý“c~Ô.ý“–b /test-4/722worldanyoneBOý“ba"/ý“–b /test-1/800worldanyoneBOý“ba 0ý“–c /test-6/798worldanyoneBOý“cŠá1ý“–i /test-8/735worldanyoneBOý“czÜ2ý“–m /test-5/730worldanyoneBOý“cŠä3ý“–m /test-9/738worldanyoneBOý“ba#4ý“–m /test-1/801worldanyoneBOý“ba!5ý“–n /test-6/799worldanyoneBOý“c~Õ6ý“–n /test-4/723worldanyoneBOý“czÚ7ý“–n /test-3/728worldanyoneBOý“c~å8ý“–n /test-0/739worldanyoneBOý“czÛ9ý“–n /test-7/729worldanyoneBOý“c~Ü:ý“–n /test-2/730worldanyoneBOý“cŠâ;ý“–q /test-8/736worldanyoneBOý“czÝ<ý“–v /test-5/731worldanyoneBOý“ba$=ý“–x /test-1/802worldanyoneBOý“c~Ý>ý“–y /test-2/731worldanyoneBOý“czÜ?ý“–y /test-7/730worldanyoneBOý“c~æ@ý“–y /test-0/740worldanyoneBOý“cŠåAý“–z /test-9/739worldanyoneBOý“czÛBý“–z /test-3/729worldanyoneBOý“ba"Cý“–z /test-6/800worldanyoneBOý“cŠãDý“–{ /test-8/737worldanyoneBOý“c~ÖEý“–{ /test-4/724worldanyoneBOý“czÞFý“–~ /test-5/732worldanyoneBOý“ba%Gý“– /test-1/803worldanyoneBOý“czÝHý“–ƒ /test-7/731worldanyoneBOý“c~ÞIý“–ƒ /test-2/732worldanyoneBOý“c~çJý“–ƒ /test-0/741worldanyoneBOý“ba#Ký“–„ /test-6/801worldanyoneBOý“c~×Lý“–„ /test-4/725worldanyoneBOý“czÜMý“–… /test-3/730worldanyoneBOý“cŠäNý“–… /test-8/738worldanyoneBOý“cŠæOý“–… /test-9/740worldanyoneBOý“czßPý“–‡ /test-5/733worldanyoneBOý“ba&Qý“–‰ /test-1/804worldanyoneBOý“ba$Rý“– /test-6/802worldanyoneBOý“cŠåSý“– /test-8/739worldanyoneBOý“cŠçTý“– /test-9/741worldanyoneBOý“czÞUý“– /test-7/732worldanyoneBOý“c~ßVý“– /test-2/733worldanyoneBOý“c~èWý“– /test-0/742worldanyoneBOý“c~ØXý“– /test-4/726worldanyoneBOý“czàYý“– /test-5/734worldanyoneBOý“czÝZý“–‘ /test-3/731worldanyoneBOý“ba'[ý“–‘ /test-1/805worldanyoneBOý“ba%\ý“–š /test-6/803worldanyoneBOý“ba(]ý“–› /test-1/806worldanyoneBOý“cŠè^ý“–› /test-9/742worldanyoneBOý“cŠæ_ý“–› /test-8/740worldanyoneBOý“c~à`ý“–œ /test-2/734worldanyoneBOý“c~Ùaý“–œ /test-4/727worldanyoneBOý“c~ébý“–œ /test-0/743worldanyoneBOý“czácý“–œ /test-5/735worldanyoneBOý“czßdý“–œ /test-7/733worldanyoneBOý“czÞeý“–œ /test-3/732worldanyoneBOý“ba&fý“–£ /test-6/804worldanyoneBOý“ba)gý“–¦ /test-1/807worldanyoneBOý“cŠéhý“–§ /test-9/743worldanyoneBOý“cŠçiý“–§ /test-8/741worldanyoneBOý“c~Újý“–¨ /test-4/728worldanyoneBOý“czàký“–© /test-7/734worldanyoneBOý“czâlý“–© /test-5/736worldanyoneBOý“c~êmý“–© /test-0/744worldanyoneBOý“c~áný“–© /test-2/735worldanyoneBOý“czßoý“–© /test-3/733worldanyoneBOý“ba'pý“–« /test-6/805worldanyoneBOý“ba*qý“–® /test-1/808worldanyoneBOý“cŠèrý“–° /test-8/742worldanyoneBOý“cŠêsý“–° /test-9/744worldanyoneBOý“czátý“–± /test-7/735worldanyoneBOý“c~Ûuý“–± /test-4/729worldanyoneBOý“czàvý“–² /test-3/734worldanyoneBOý“czãwý“–² /test-5/737worldanyoneBOý“c~ëxý“–² /test-0/745worldanyoneBOý“c~âyý“–² /test-2/736worldanyoneBOý“ba(zý“–³ /test-6/806worldanyoneBOý“ba+{ý“–¶ /test-1/809worldanyoneBOý“cŠë|ý“–¼ /test-9/745worldanyoneBOý“cŠé}ý“–¼ /test-8/743worldanyoneBOý“ba)~ý“–¾ /test-6/807worldanyoneBOý“czáý“–¾ /test-3/735worldanyoneBOý“czâ€ý“–¾ /test-7/736worldanyoneBOý“czäý“–¿ /test-5/738worldanyoneBOý“c~ã‚ý“–¿ /test-2/737worldanyoneBOý“c~ìƒý“–¿ /test-0/746worldanyoneBOý“c~Ü„ý“–¿ /test-4/730worldanyoneBOý“ba,…ý“–¿ /test-1/810worldanyoneBOý“cŠê†ý“–Æ /test-8/744worldanyoneBOý“cŠì‡ý“–Æ /test-9/746worldanyoneBOý“ba*ˆý“–È /test-6/808worldanyoneBOý“ba-‰ý“–É /test-1/811worldanyoneBOý“czâŠý“–É /test-3/736worldanyoneBOý“czã‹ý“–Ê /test-7/737worldanyoneBOý“czåŒý“–Ê /test-5/739worldanyoneBOý“c~Ýý“–Ê /test-4/731worldanyoneBOý“c~äŽý“–Ê /test-2/738worldanyoneBOý“c~íý“–Ê /test-0/747worldanyoneBOý“cŠëý“–Ï /test-8/745worldanyoneBOý“cŠí‘ý“–Ï /test-9/747worldanyoneBOý“ba+’ý“–Ñ /test-6/809worldanyoneBOý“ba.“ý“–Ô /test-1/812worldanyoneBOý“czä”ý“–Ô /test-7/738worldanyoneBOý“czã•ý“–Ô /test-3/737worldanyoneBOý“czæ–ý“–Ô /test-5/740worldanyoneBOý“c~î—ý“–Õ /test-0/748worldanyoneBOý“c~å˜ý“–Õ /test-2/739worldanyoneBOý“c~Þ™ý“–Õ /test-4/732worldanyoneBOý“cŠìšý“–× /test-8/746worldanyoneBOý“cŠî›ý“–× /test-9/748worldanyoneBOý“ba,œý“–Ù /test-6/810worldanyoneBOý“ba/ý“–Ý /test-1/813worldanyoneBOý“czåžý“–Þ /test-7/739worldanyoneBOý“c~ïŸý“–ß /test-0/749worldanyoneBOý“c~æ ý“–ß /test-2/740worldanyoneBOý“c~ß¡ý“–ß /test-4/733worldanyoneBOý“czä¢ý“–à /test-3/738worldanyoneBOý“czç£ý“–à /test-5/741worldanyoneBOý“cŠí¤ý“–à /test-8/747worldanyoneBOý“cŠï¥ý“–á /test-9/749worldanyoneBOý“ba-¦ý“–á /test-6/811worldanyoneBOý“ba0§ý“–å /test-1/814worldanyoneBOý“czæ¨ý“–ç /test-7/740worldanyoneBOý“cŠð©ý“–ê /test-9/750worldanyoneBOý“c~çªý“–ê /test-2/741worldanyoneBOý“cŠî«ý“–ë /test-8/748worldanyoneBOý“czå¬ý“–ë /test-3/739worldanyoneBOý“c~ð­ý“–ë /test-0/750worldanyoneBOý“czè®ý“–ì /test-5/742worldanyoneBOý“ba.¯ý“–ì /test-6/812worldanyoneBOý“c~à°ý“–ì /test-4/734worldanyoneBOý“ba1±ý“–í /test-1/815worldanyoneBOý“czç²ý“–ï /test-7/741worldanyoneBOý“cŠñ³ý“–ó /test-9/751worldanyoneBOý“c~è´ý“–ó /test-2/742worldanyoneBOý“cŠïµý“–õ /test-8/749worldanyoneBOý“c~ñ¶ý“–õ /test-0/751worldanyoneBOý“czæ·ý“–õ /test-3/740worldanyoneBOý“ba/¸ý“–ö /test-6/813worldanyoneBOý“ba2¹ý“–ö /test-1/816worldanyoneBOý“czéºý“–ö /test-5/743worldanyoneBOý“c~á»ý“–ö /test-4/735worldanyoneBOý“czè¼ý“–ø /test-7/742worldanyoneBOý“cŠò½ý“–û /test-9/752worldanyoneBOý“c~é¾ý“–ü /test-2/743worldanyoneBOý“czê¿ý“— /test-5/744worldanyoneBOý“czçÀý“— /test-3/741worldanyoneBOý“ba0Áý“— /test-6/814worldanyoneBOý“c~âÂý“— /test-4/736worldanyoneBOý“cŠðÃý“— /test-8/750worldanyoneBOý“ba3Äý“— /test-1/817worldanyoneBOý“c~òÅý“— /test-0/752worldanyoneBOý“czéÆý“— /test-7/743worldanyoneBOý“cŠóÇý“— /test-9/753worldanyoneBOý“c~êÈý“— /test-2/744worldanyoneBOý“ba1Éý“—  /test-6/815worldanyoneBOý“cŠñÊý“—  /test-8/751worldanyoneBOý“czëËý“—  /test-5/745worldanyoneBOý“c~ãÌý“—  /test-4/737worldanyoneBOý“czèÍý“—  /test-3/742worldanyoneBOý“czêÎý“—  /test-7/744worldanyoneBOý“ba4Ïý“—  /test-1/818worldanyoneBOý“cŠôÐý“—  /test-9/754worldanyoneBOý“c~óÑý“— /test-0/753worldanyoneBOý“c~ëÒý“— /test-2/745worldanyoneBOý“ba2Óý“— /test-6/816worldanyoneBOý“czìÔý“— /test-5/746worldanyoneBOý“cŠòÕý“— /test-8/752worldanyoneBOý“c~äÖý“— /test-4/738worldanyoneBOý“ba5×ý“— /test-1/819worldanyoneBOý“c~ôØý“— /test-0/754worldanyoneBOý“czëÙý“— /test-7/745worldanyoneBOý“czéÚý“— /test-3/743worldanyoneBOý“cŠõÛý“— /test-9/755worldanyoneBOý“c~ìÜý“— /test-2/746worldanyoneBOý“ba3Ýý“— /test-6/817worldanyoneBOý“czíÞý“— /test-5/747worldanyoneBOý“c~åßý“—  /test-4/739worldanyoneBOý“cŠóàý“—  /test-8/753worldanyoneBOý“ba6áý“—" /test-1/820worldanyoneBOý“c~õâý“—" /test-0/755worldanyoneBOý“czìãý“—" /test-7/746worldanyoneBOý“czêäý“—" /test-3/744worldanyoneBOý“cŠöåý“—# /test-9/756worldanyoneBOý“c~íæý“—# /test-2/747worldanyoneBOý“ba4çý“—( /test-6/818worldanyoneBOý“czîèý“—( /test-5/748worldanyoneBOý“cŠôéý“—+ /test-8/754worldanyoneBOý“cŠ÷êý“—, /test-9/757worldanyoneBOý“ba7ëý“—, /test-1/821worldanyoneBOý“c~öìý“—, /test-0/756worldanyoneBOý“c~æíý“—, /test-4/740worldanyoneBOý“c~îîý“—- /test-2/748worldanyoneBOý“czíïý“—- /test-7/747worldanyoneBOý“czëðý“—- /test-3/745worldanyoneBOý“ba5ñý“—1 /test-6/819worldanyoneBOý“czïòý“—1 /test-5/749worldanyoneBOý“cŠõóý“—3 /test-8/755worldanyoneBOý“ba8ôý“—7 /test-1/822worldanyoneBOý“czìõý“—7 /test-3/746worldanyoneBOý“czîöý“—7 /test-7/748worldanyoneBOý“cŠø÷ý“—7 /test-9/758worldanyoneBOý“c~çøý“—8 /test-4/741worldanyoneBOý“c~÷ùý“—8 /test-0/757worldanyoneBOý“c~ïúý“—8 /test-2/749worldanyoneBOý“ba6ûý“—: /test-6/820worldanyoneBOý“czðüý“—: /test-5/750worldanyoneBOý“cŠöýý“—< /test-8/756worldanyoneBOý“ba9þý“—A /test-1/823worldanyoneBOý“cŠùÿý“—A /test-9/759worldanyoneBOý“czïý“—A /test-7/749worldanyoneBOý“czíý“—A /test-3/747worldanyoneBOý“ba7ý“—D /test-6/821worldanyoneBOý“czñý“—E /test-5/751worldanyoneBOý“cŠ÷ý“—E /test-8/757worldanyoneBOý“c~øý“—E /test-0/758worldanyoneBOý“c~èý“—E /test-4/742worldanyoneBOý“c~ðý“—F /test-2/750worldanyoneBOý“czîý“—J /test-3/748worldanyoneBOý“czð ý“—J /test-7/750worldanyoneBOý“ba: ý“—J /test-1/824worldanyoneBOý“cŠú ý“—K /test-9/760worldanyoneBOý“ba8 ý“—M /test-6/822worldanyoneBOý“czò ý“—O /test-5/752worldanyoneBOý“c~ùý“—P /test-0/759worldanyoneBOý“cŠøý“—P /test-8/758worldanyoneBOý“c~éý“—Q /test-4/743worldanyoneBOý“c~ñý“—Q /test-2/751worldanyoneBOý“ba;ý“—S /test-1/825worldanyoneBOý“czïý“—S /test-3/749worldanyoneBOý“czñý“—S /test-7/751worldanyoneBOý“cŠûý“—T /test-9/761worldanyoneBOý“ba9ý“—W /test-6/823worldanyoneBOý“czóý“—Y /test-5/753worldanyoneBOý“c~êý“—Z /test-4/744worldanyoneBOý“c~úý“—Z /test-0/760worldanyoneBOý“cŠùý“—Z /test-8/759worldanyoneBOý“c~òý“—[ /test-2/752worldanyoneBOý“ba<ý“—] /test-1/826worldanyoneBOý“cŠüý“—] /test-9/762worldanyoneBOý“czòý“—] /test-7/752worldanyoneBOý“czðý“—] /test-3/750worldanyoneBOý“ba: ý“—_ /test-6/824worldanyoneBOý“czô!ý“—a /test-5/754worldanyoneBOý“cŠú"ý“—e /test-8/760worldanyoneBOý“c~ó#ý“—e /test-2/753worldanyoneBOý“c~ë$ý“—e /test-4/745worldanyoneBOý“c~û%ý“—e /test-0/761worldanyoneBOý“ba=&ý“—g /test-1/827worldanyoneBOý“czó'ý“—g /test-7/753worldanyoneBOý“czñ(ý“—g /test-3/751worldanyoneBOý“ba;)ý“—h /test-6/825worldanyoneBOý“cŠý*ý“—h /test-9/763worldanyoneBOý“czõ+ý“—j /test-5/755worldanyoneBOý“cŠû,ý“—o /test-8/761worldanyoneBOý“c~ì-ý“—o /test-4/746worldanyoneBOý“c~ü.ý“—o /test-0/762worldanyoneBOý“c~ô/ý“—p /test-2/754worldanyoneBOý“ba<0ý“—r /test-6/826worldanyoneBOý“czò1ý“—s /test-3/752worldanyoneBOý“ba>2ý“—s /test-1/828worldanyoneBOý“czô3ý“—s /test-7/754worldanyoneBOý“cŠþ4ý“—s /test-9/764worldanyoneBOý“czö5ý“—s /test-5/756worldanyoneBOý“cŠü6ý“—x /test-8/762worldanyoneBOý“c~í7ý“—y /test-4/747worldanyoneBOý“c~ý8ý“—y /test-0/763worldanyoneBOý“c~õ9ý“—y /test-2/755worldanyoneBOý“ba?:ý“—| /test-1/829worldanyoneBOý“ba=;ý“—| /test-6/827worldanyoneBOý“cz÷<ý“—} /test-5/757worldanyoneBOý“czó=ý“—} /test-3/753worldanyoneBOý“czõ>ý“—} /test-7/755worldanyoneBOý“cŠÿ?ý“—~ /test-9/765worldanyoneBOý“cŠý@ý“—µ /test-8/763worldanyoneBOý“c~îAý“—¶ /test-4/748worldanyoneBOý“c~öBý“—¶ /test-2/756worldanyoneBOý“c~þCý“—¶ /test-0/764worldanyoneBOý“ba@Dý“—¶ /test-1/830worldanyoneBOý“ba>Eý“—» /test-6/828worldanyoneBOý“cŠFý“—¿ /test-9/766worldanyoneBOý“czôGý“—¿ /test-3/754worldanyoneBOý“czöHý“—¿ /test-7/756worldanyoneBOý“czøIý“—¿ /test-5/758worldanyoneBOý“cŠþJý“—À /test-8/764worldanyoneBOý“baAKý“—À /test-1/831worldanyoneBOý“c~ÿLý“—Á /test-0/765worldanyoneBOý“c~ïMý“—Á /test-4/749worldanyoneBOý“c~÷Ný“—Á /test-2/757worldanyoneBOý“ba?Oý“—à /test-6/829worldanyoneBOý“baBPý“—Ë /test-1/832worldanyoneBOý“czõQý“—Ë /test-3/755worldanyoneBOý“cz÷Rý“—Ë /test-7/757worldanyoneBOý“c~Sý“—Ì /test-0/766worldanyoneBOý“c~ðTý“—Ì /test-4/750worldanyoneBOý“c~øUý“—Ì /test-2/758worldanyoneBOý“czùVý“—Ì /test-5/759worldanyoneBOý“ba@Wý“—Ì /test-6/830worldanyoneBOý“cŠXý“—Ì /test-9/767worldanyoneBOý“cŠÿYý“—Í /test-8/765worldanyoneBOý“baCZý“—Ö /test-1/833worldanyoneBOý“czö[ý“—Ö /test-3/756worldanyoneBOý“czú\ý“—Ö /test-5/760worldanyoneBOý“czø]ý“—Ö /test-7/758worldanyoneBOý“baA^ý“—× /test-6/831worldanyoneBOý“cŠ_ý“—× /test-9/768worldanyoneBOý“c~`ý“—× /test-0/767worldanyoneBOý“c~ùaý“—Ø /test-2/759worldanyoneBOý“c~ñbý“—Ø /test-4/751worldanyoneBOý“cŠcý“—Ø /test-8/766worldanyoneBOý“baDdý“—á /test-1/834worldanyoneBOý“cz÷eý“—á /test-3/757worldanyoneBOý“czùfý“—á /test-7/759worldanyoneBOý“czûgý“—á /test-5/761worldanyoneBOý“cŠhý“—â /test-9/769worldanyoneBOý“baBiý“—â /test-6/832worldanyoneBOý“cŠjý“—â /test-8/767worldanyoneBOý“c~ký“—â /test-0/768worldanyoneBOý“c~òlý“—ã /test-4/752worldanyoneBOý“c~úmý“—ã /test-2/760worldanyoneBOý“baEný“—ì /test-1/835worldanyoneBOý“baCoý“—í /test-6/833worldanyoneBOý“cŠpý“—í /test-9/770worldanyoneBOý“czüqý“—í /test-5/762worldanyoneBOý“czúrý“—í /test-7/760worldanyoneBOý“c~sý“—î /test-0/769worldanyoneBOý“czøtý“—î /test-3/758worldanyoneBOý“cŠuý“—î /test-8/768worldanyoneBOý“c~ûvý“—î /test-2/761worldanyoneBOý“c~ówý“—ï /test-4/753worldanyoneBOý“baFxý“—õ /test-1/836worldanyoneBOý“baDyý“—ø /test-6/834worldanyoneBOý“czùzý“—ù /test-3/759worldanyoneBOý“cŠ{ý“—ù /test-8/769worldanyoneBOý“c~ô|ý“—ú /test-4/754worldanyoneBOý“czû}ý“—ú /test-7/761worldanyoneBOý“c~ü~ý“—ú /test-2/762worldanyoneBOý“c~ý“—ú /test-0/770worldanyoneBOý“cŠ€ý“—ú /test-9/771worldanyoneBOý“czýý“—ú /test-5/763worldanyoneBOý“baG‚ý“—ý /test-1/837worldanyoneBOý“baEƒý“˜ /test-6/835worldanyoneBOý“cŠ„ý“˜ /test-9/772worldanyoneBOý“cŠ…ý“˜ /test-8/770worldanyoneBOý“czü†ý“˜ /test-7/762worldanyoneBOý“c~õ‡ý“˜ /test-4/755worldanyoneBOý“czúˆý“˜ /test-3/760worldanyoneBOý“c~‰ý“˜ /test-0/771worldanyoneBOý“baHŠý“˜ /test-1/838worldanyoneBOý“czþ‹ý“˜ /test-5/764worldanyoneBOý“c~ýŒý“˜ /test-2/763worldanyoneBOý“baFý“˜ /test-6/836worldanyoneBOý“cŠŽý“˜  /test-9/773worldanyoneBOý“cŠý“˜ /test-8/771worldanyoneBOý“baIý“˜ /test-1/839worldanyoneBOý“baG‘ý“˜ /test-6/837worldanyoneBOý“czý’ý“˜ /test-7/763worldanyoneBOý“c~þ“ý“˜ /test-2/764worldanyoneBOý“czû”ý“˜ /test-3/761worldanyoneBOý“czÿ•ý“˜ /test-5/765worldanyoneBOý“c~–ý“˜ /test-0/772worldanyoneBOý“c~ö—ý“˜ /test-4/756worldanyoneBOý“cŠ˜ý“˜ /test-9/774worldanyoneBOý“cŠ™ý“˜ /test-8/772worldanyoneBOý“baJšý“˜ /test-1/840worldanyoneBOý“czü›ý“˜ /test-3/762worldanyoneBOý“baHœý“˜ /test-6/838worldanyoneBOý“czþý“˜ /test-7/764worldanyoneBOý“czžý“˜ /test-5/766worldanyoneBOý“c~ÿŸý“˜ /test-2/765worldanyoneBOý“c~ ý“˜ /test-0/773worldanyoneBOý“c~÷¡ý“˜ /test-4/757worldanyoneBOý“cŠ ¢ý“˜ /test-9/775worldanyoneBOý“cŠ£ý“˜  /test-8/773worldanyoneBOý“baI¤ý“˜) /test-6/839worldanyoneBOý“baK¥ý“˜) /test-1/841worldanyoneBOý“c~¦ý“˜) /test-2/766worldanyoneBOý“cz§ý“˜) /test-5/767worldanyoneBOý“czÿ¨ý“˜) /test-7/765worldanyoneBOý“czý©ý“˜) /test-3/763worldanyoneBOý“cŠ ªý“˜* /test-9/776worldanyoneBOý“c~ø«ý“˜* /test-4/758worldanyoneBOý“c~¬ý“˜+ /test-0/774worldanyoneBOý“cŠ­ý“˜, /test-8/774worldanyoneBOý“baJ®ý“˜3 /test-6/840worldanyoneBOý“cŠ ¯ý“˜5 /test-9/777worldanyoneBOý“baL°ý“˜6 /test-1/842worldanyoneBOý“cz±ý“˜6 /test-5/768worldanyoneBOý“cz²ý“˜6 /test-7/766worldanyoneBOý“czþ³ý“˜6 /test-3/764worldanyoneBOý“cŠ ´ý“˜7 /test-8/775worldanyoneBOý“c~ µý“˜7 /test-0/775worldanyoneBOý“c~ù¶ý“˜7 /test-4/759worldanyoneBOý“c~·ý“˜7 /test-2/767worldanyoneBOý“baK¸ý“˜; /test-6/841worldanyoneBOý“cŠ ¹ý“˜> /test-9/778worldanyoneBOý“baMºý“˜A /test-1/843worldanyoneBOý“czÿ»ý“˜B /test-3/765worldanyoneBOý“cz¼ý“˜B /test-7/767worldanyoneBOý“cz½ý“˜B /test-5/769worldanyoneBOý“cŠ ¾ý“˜B /test-8/776worldanyoneBOý“baL¿ý“˜C /test-6/842worldanyoneBOý“c~Àý“˜C /test-2/768worldanyoneBOý“c~úÁý“˜D /test-4/760worldanyoneBOý“c~ Âý“˜D /test-0/776worldanyoneBOý“cŠ Ãý“˜G /test-9/779worldanyoneBOý“baNÄý“˜J /test-1/844worldanyoneBOý“baMÅý“˜M /test-6/843worldanyoneBOý“czÆý“˜M /test-7/768worldanyoneBOý“czÇý“˜M /test-3/766worldanyoneBOý“czÈý“˜M /test-5/770worldanyoneBOý“c~ûÉý“˜N /test-4/761worldanyoneBOý“c~Êý“˜N /test-2/769worldanyoneBOý“c~ Ëý“˜N /test-0/777worldanyoneBOý“cŠ Ìý“˜N /test-8/777worldanyoneBOý“cŠÍý“˜P /test-9/780worldanyoneBOý“baOÎý“˜R /test-1/845worldanyoneBOý“baNÏý“˜Y /test-6/844worldanyoneBOý“cŠ Ðý“˜Y /test-8/778worldanyoneBOý“c~Ñý“˜Y /test-2/770worldanyoneBOý“c~üÒý“˜Y /test-4/762worldanyoneBOý“czÓý“˜Y /test-7/769worldanyoneBOý“czÔý“˜Y /test-5/771worldanyoneBOý“czÕý“˜Z /test-3/767worldanyoneBOý“c~ Öý“˜Z /test-0/778worldanyoneBOý“cŠ×ý“˜Z /test-9/781worldanyoneBOý“baPØý“˜\ /test-1/846worldanyoneBOý“baQÙý“˜e /test-1/847worldanyoneBOý“baOÚý“˜e /test-6/845worldanyoneBOý“czÛý“˜e /test-7/770worldanyoneBOý“cŠ Üý“˜f /test-8/779worldanyoneBOý“czÝý“˜f /test-3/768worldanyoneBOý“czÞý“˜f /test-5/772worldanyoneBOý“cŠßý“˜f /test-9/782worldanyoneBOý“c~ àý“˜f /test-0/779worldanyoneBOý“c~áý“˜f /test-2/771worldanyoneBOý“c~ýâý“˜f /test-4/763worldanyoneBOý“baRãý“˜n /test-1/848worldanyoneBOý“baPäý“˜p /test-6/846worldanyoneBOý“czåý“˜p /test-7/771worldanyoneBOý“c~æý“˜q /test-0/780worldanyoneBOý“c~çý“˜q /test-2/772worldanyoneBOý“czèý“˜q /test-5/773worldanyoneBOý“czéý“˜r /test-3/769worldanyoneBOý“cŠêý“˜r /test-8/780worldanyoneBOý“cŠëý“˜r /test-9/783worldanyoneBOý“c~þìý“˜r /test-4/764worldanyoneBOý“baSíý“˜v /test-1/849worldanyoneBOý“baQîý“˜y /test-6/847worldanyoneBOý“czïý“˜y /test-7/772worldanyoneBOý“c~ðý“˜{ /test-0/781worldanyoneBOý“czñý“˜| /test-5/774worldanyoneBOý“czòý“˜| /test-3/770worldanyoneBOý“c~óý“˜| /test-2/773worldanyoneBOý“cŠôý“˜} /test-8/781worldanyoneBOý“cŠõý“˜} /test-9/784worldanyoneBOý“c~ÿöý“˜} /test-4/765worldanyoneBOý“baT÷ý“˜ /test-1/850worldanyoneBOý“baRøý“˜ /test-6/848worldanyoneBOý“czùý“˜‚ /test-7/773worldanyoneBOý“c~úý“˜ƒ /test-0/782worldanyoneBOý“baUûý“˜ˆ /test-1/851worldanyoneBOý“cŠüý“˜ˆ /test-8/782worldanyoneBOý“cŠýý“˜ˆ /test-9/785worldanyoneBOý“c~þý“˜ˆ /test-4/766worldanyoneBOý“c~ÿý“˜ˆ /test-2/774worldanyoneBOý“czý“˜‰ /test-3/771worldanyoneBOý“cz ý“˜‰ /test-5/775worldanyoneBOý“baSý“˜‹ /test-6/849worldanyoneBOý“c~ý“˜‹ /test-0/783worldanyoneBOý“czý“˜‹ /test-7/774worldanyoneBOý“baVý“˜“ /test-1/852worldanyoneBOý“cŠý“˜“ /test-8/783worldanyoneBOý“cz ý“˜“ /test-5/776worldanyoneBOý“czý“˜“ /test-3/772worldanyoneBOý“c~  ý“˜“ /test-2/775worldanyoneBOý“c~ ý“˜” /test-4/767worldanyoneBOý“cŠ ý“˜” /test-9/786worldanyoneBOý“cz  ý“˜” /test-7/775worldanyoneBOý“baT ý“˜” /test-6/850worldanyoneBOý“c~ý“˜• /test-0/784worldanyoneBOý“baWý“˜œ /test-1/853worldanyoneBOý“cz ý“˜ /test-5/777worldanyoneBOý“czý“˜ /test-3/773worldanyoneBOý“cŠý“˜ /test-8/784worldanyoneBOý“c~ ý“˜ /test-2/776worldanyoneBOý“baUý“˜Ÿ /test-6/851worldanyoneBOý“cz ý“˜Ÿ /test-7/776worldanyoneBOý“cŠý“˜  /test-9/787worldanyoneBOý“c~ý“˜  /test-4/768worldanyoneBOý“c~ý“˜  /test-0/785worldanyoneBOý“baXý“˜¤ /test-1/854worldanyoneBOý“czý“˜§ /test-3/774worldanyoneBOý“cz ý“˜§ /test-5/778worldanyoneBOý“baVý“˜§ /test-6/852worldanyoneBOý“cŠý“˜§ /test-8/785worldanyoneBOý“c~ ý“˜§ /test-2/777worldanyoneBOý“baWý“˜» /test-6/853worldanyoneBOý“c~ ý“˜» /test-4/769worldanyoneBOý“baY!ý“˜» /test-1/855worldanyoneBOý“c~"ý“˜¼ /test-0/786worldanyoneBOý“cŠ#ý“˜¼ /test-9/788worldanyoneBOý“cz $ý“˜¼ /test-7/777worldanyoneBOý“cz %ý“˜½ /test-3/775worldanyoneBOý“c~ &ý“˜½ /test-2/778worldanyoneBOý“cŠ'ý“˜½ /test-8/786worldanyoneBOý“cz (ý“˜½ /test-5/779worldanyoneBOý“baZ)ý“˜Ç /test-1/856worldanyoneBOý“baX*ý“˜Ç /test-6/854worldanyoneBOý“cz +ý“˜Ç /test-3/776worldanyoneBOý“c~ ,ý“˜Ç /test-2/779worldanyoneBOý“cz-ý“˜Ç /test-5/780worldanyoneBOý“c~.ý“˜È /test-4/770worldanyoneBOý“cz /ý“˜È /test-7/778worldanyoneBOý“cŠ0ý“˜È /test-8/787worldanyoneBOý“cŠ1ý“˜É /test-9/789worldanyoneBOý“c~2ý“˜É /test-0/787worldanyoneBOý“cz 3ý“˜Ó /test-3/777worldanyoneBOý“cŠ4ý“˜Ó /test-9/790worldanyoneBOý“ba[5ý“˜Ô /test-1/857worldanyoneBOý“baY6ý“˜Ô /test-6/855worldanyoneBOý“cŠ7ý“˜Ô /test-8/788worldanyoneBOý“c~8ý“˜Ô /test-2/780worldanyoneBOý“c~9ý“˜Ô /test-0/788worldanyoneBOý“c~:ý“˜Ô /test-4/771worldanyoneBOý“cz ;ý“˜Ô /test-7/779worldanyoneBOý“cz<ý“˜Ô /test-5/781worldanyoneBOý“cz =ý“˜Ü /test-3/778worldanyoneBOý“cŠ>ý“˜Þ /test-9/791worldanyoneBOý“baZ?ý“˜ß /test-6/856worldanyoneBOý“ba\@ý“˜à /test-1/858worldanyoneBOý“cŠAý“˜à /test-8/789worldanyoneBOý“czBý“˜à /test-7/780worldanyoneBOý“c~Cý“˜à /test-0/789worldanyoneBOý“c~Dý“˜à /test-2/781worldanyoneBOý“czEý“˜à /test-5/782worldanyoneBOý“c~Fý“˜à /test-4/772worldanyoneBOý“cz Gý“˜ä /test-3/779worldanyoneBOý“cŠHý“˜ç /test-9/792worldanyoneBOý“ba[Iý“˜é /test-6/857worldanyoneBOý“ba]Jý“˜ê /test-1/859worldanyoneBOý“czKý“˜ê /test-7/781worldanyoneBOý“czLý“˜ê /test-5/783worldanyoneBOý“cŠMý“˜ë /test-8/790worldanyoneBOý“c~Ný“˜ë /test-4/773worldanyoneBOý“c~Oý“˜ë /test-2/782worldanyoneBOý“c~Pý“˜ë /test-0/790worldanyoneBOý“czQý“˜í /test-3/780worldanyoneBOý“cŠRý“˜ï /test-9/793worldanyoneBOý“ba\Sý“˜ò /test-6/858worldanyoneBOý“czTý“˜ó /test-7/782worldanyoneBOý“ba^Uý“˜ô /test-1/860worldanyoneBOý“c~Vý“˜õ /test-0/791worldanyoneBOý“cŠWý“˜ö /test-8/791worldanyoneBOý“c~Xý“˜ö /test-2/783worldanyoneBOý“czYý“˜ö /test-5/784worldanyoneBOý“c~Zý“˜ö /test-4/774worldanyoneBOý“cz[ý“˜÷ /test-3/781worldanyoneBOý“cŠ\ý“˜÷ /test-9/794worldanyoneBOý“ba]]ý“˜ú /test-6/859worldanyoneBOý“cz^ý“˜ü /test-7/783worldanyoneBOý“ba__ý“˜ý /test-1/861worldanyoneBOý“cŠ`ý“™ /test-9/795worldanyoneBOý“c~aý“™ /test-0/792worldanyoneBOý“cŠbý“™ /test-8/792worldanyoneBOý“c~cý“™ /test-2/784worldanyoneBOý“czdý“™ /test-3/782worldanyoneBOý“czeý“™ /test-5/785worldanyoneBOý“c~ fý“™ /test-4/775worldanyoneBOý“ba^gý“™ /test-6/860worldanyoneBOý“czhý“™ /test-7/784worldanyoneBOý“ba`iý“™ /test-1/862worldanyoneBOý“cŠjý“™  /test-9/796worldanyoneBOý“c~ký“™  /test-0/793worldanyoneBOý“cŠlý“™  /test-8/793worldanyoneBOý“czmý“™  /test-3/783worldanyoneBOý“czný“™  /test-5/786worldanyoneBOý“ba_oý“™  /test-6/861worldanyoneBOý“c~ pý“™  /test-4/776worldanyoneBOý“c~qý“™  /test-2/785worldanyoneBOý“baarý“™  /test-1/863worldanyoneBOý“czsý“™  /test-7/785worldanyoneBOý“cŠtý“™ /test-9/797worldanyoneBOý“c~uý“™ /test-0/794worldanyoneBOý“cŠvý“™ /test-8/794worldanyoneBOý“c~ wý“™ /test-4/777worldanyoneBOý“ba`xý“™ /test-6/862worldanyoneBOý“babyý“™ /test-1/864worldanyoneBOý“czzý“™ /test-5/787worldanyoneBOý“cz{ý“™ /test-3/784worldanyoneBOý“cz|ý“™ /test-7/786worldanyoneBOý“c~}ý“™ /test-2/786worldanyoneBOý“cŠ ~ý“™ /test-9/798worldanyoneBOý“c~ý“™ /test-0/795worldanyoneBOý“cŠ€ý“™ /test-8/795worldanyoneBOý“c~ ý“™  /test-4/778worldanyoneBOý“baa‚ý“™! /test-6/863worldanyoneBOý“bacƒý“™! /test-1/865worldanyoneBOý“cŠ!„ý“™$ /test-9/799worldanyoneBOý“c~…ý“™$ /test-0/796worldanyoneBOý“c~†ý“™$ /test-2/787worldanyoneBOý“cz‡ý“™$ /test-7/787worldanyoneBOý“czˆý“™% /test-5/788worldanyoneBOý“cz‰ý“™% /test-3/785worldanyoneBOý“cŠŠý“™& /test-8/796worldanyoneBOý“c~ ‹ý“™( /test-4/779worldanyoneBOý“babŒý“™) /test-6/864worldanyoneBOý“badý“™* /test-1/866worldanyoneBOý“czŽý“™/ /test-7/788worldanyoneBOý“czý“™/ /test-3/786worldanyoneBOý“czý“™/ /test-5/789worldanyoneBOý“c~‘ý“™/ /test-0/797worldanyoneBOý“c~’ý“™/ /test-2/788worldanyoneBOý“cŠ"“ý“™0 /test-9/800worldanyoneBOý“cŠ”ý“™0 /test-8/797worldanyoneBOý“c~•ý“™1 /test-4/780worldanyoneBOý“bac–ý“™2 /test-6/865worldanyoneBOý“bae—ý“™3 /test-1/867worldanyoneBOý“baf˜ý“™< /test-1/868worldanyoneBOý“bad™ý“™< /test-6/866worldanyoneBOý“cŠ šý“™< /test-8/798worldanyoneBOý“cz›ý“™< /test-5/790worldanyoneBOý“czœý“™< /test-3/787worldanyoneBOý“czý“™< /test-7/789worldanyoneBOý“cŠ#žý“™= /test-9/801worldanyoneBOý“c~ Ÿý“™= /test-0/798worldanyoneBOý“c~ ý“™= /test-4/781worldanyoneBOý“c~¡ý“™= /test-2/789worldanyoneBOý“bag¢ý“™F /test-1/869worldanyoneBOý“bae£ý“™G /test-6/867worldanyoneBOý“cz¤ý“™G /test-3/788worldanyoneBOý“cŠ!¥ý“™G /test-8/799worldanyoneBOý“cz¦ý“™G /test-5/791worldanyoneBOý“c~§ý“™H /test-4/782worldanyoneBOý“c~¨ý“™H /test-2/790worldanyoneBOý“cz©ý“™H /test-7/790worldanyoneBOý“c~!ªý“™H /test-0/799worldanyoneBOý“cŠ$«ý“™I /test-9/802worldanyoneBOý“bah¬ý“™N /test-1/870worldanyoneBOý“baf­ý“™Q /test-6/868worldanyoneBOý“cŠ"®ý“™R /test-8/800worldanyoneBOý“c~"¯ý“™S /test-0/800worldanyoneBOý“c~°ý“™S /test-4/783worldanyoneBOý“c~±ý“™T /test-2/791worldanyoneBOý“cŠ%²ý“™T /test-9/803worldanyoneBOý“cz³ý“™T /test-5/792worldanyoneBOý“cz´ý“™T /test-7/791worldanyoneBOý“czµý“™U /test-3/789worldanyoneBOý“bai¶ý“™V /test-1/871worldanyoneBOý“cŠ#·ý“™[ /test-8/801worldanyoneBOý“bag¸ý“™[ /test-6/869worldanyoneBOý“cŠ&¹ý“™_ /test-9/804worldanyoneBOý“c~ºý“™` /test-2/792worldanyoneBOý“c~»ý“™` /test-4/784worldanyoneBOý“c~#¼ý“™` /test-0/801worldanyoneBOý“baj½ý“™` /test-1/872worldanyoneBOý“cz¾ý“™` /test-7/792worldanyoneBOý“cz¿ý“™` /test-3/790worldanyoneBOý“czÀý“™a /test-5/793worldanyoneBOý“bahÁý“™d /test-6/870worldanyoneBOý“cŠ$Âý“™d /test-8/802worldanyoneBOý“cŠ'Ãý“™l /test-9/805worldanyoneBOý“bakÄý“™l /test-1/873worldanyoneBOý“czÅý“™l /test-7/793worldanyoneBOý“czÆý“™m /test-3/791worldanyoneBOý“czÇý“™m /test-5/794worldanyoneBOý“c~Èý“™m /test-2/793worldanyoneBOý“baiÉý“™m /test-6/871worldanyoneBOý“c~$Êý“™m /test-0/802worldanyoneBOý“c~Ëý“™m /test-4/785worldanyoneBOý“cŠ%Ìý“™m /test-8/803worldanyoneBOý“cŠ(Íý“™x /test-9/806worldanyoneBOý“czÎý“™y /test-3/792worldanyoneBOý“c~%Ïý“™y /test-0/803worldanyoneBOý“c~Ðý“™y /test-4/786worldanyoneBOý“bajÑý“™y /test-6/872worldanyoneBOý“c~Òý“™z /test-2/794worldanyoneBOý“balÓý“™z /test-1/874worldanyoneBOý“cŠ&Ôý“™z /test-8/804worldanyoneBOý“czÕý“™z /test-5/795worldanyoneBOý“czÖý“™z /test-7/794worldanyoneBOý“cŠ)×ý“™‚ /test-9/807worldanyoneBOý“bakØý“™„ /test-6/873worldanyoneBOý“bamÙý“™„ /test-1/875worldanyoneBOý“c~&Úý“™„ /test-0/804worldanyoneBOý“cŠ'Ûý“™… /test-8/805worldanyoneBOý“czÜý“™… /test-7/795worldanyoneBOý“czÝý“™… /test-3/793worldanyoneBOý“c~Þý“™† /test-4/787worldanyoneBOý“czßý“™† /test-5/796worldanyoneBOý“c~àý“™† /test-2/795worldanyoneBOý“cŠ*áý“™‹ /test-9/808worldanyoneBOý“balâý“™ /test-6/874worldanyoneBOý“banãý“™ /test-1/876worldanyoneBOý“c~'äý“™ /test-0/805worldanyoneBOý“c~åý“™ /test-4/788worldanyoneBOý“cŠ(æý“™ /test-8/806worldanyoneBOý“czçý“™ /test-7/796worldanyoneBOý“czèý“™ /test-5/797worldanyoneBOý“czéý“™‘ /test-3/794worldanyoneBOý“c~êý“™‘ /test-2/796worldanyoneBOý“cŠ+ëý“™” /test-9/809worldanyoneBOý“bamìý“™› /test-6/875worldanyoneBOý“baoíý“™› /test-1/877worldanyoneBOý“c~îý“™› /test-4/789worldanyoneBOý“cŠ)ïý“™› /test-8/807worldanyoneBOý“cz ðý“™œ /test-5/798worldanyoneBOý“czñý“™œ /test-7/797worldanyoneBOý“czòý“™œ /test-3/795worldanyoneBOý“c~(óý“™ /test-0/806worldanyoneBOý“c~ôý“™ /test-2/797worldanyoneBOý“cŠ,õý“™ /test-9/810worldanyoneBOý“banöý“™¤ /test-6/876worldanyoneBOý“bap÷ý“™¤ /test-1/878worldanyoneBOý“c~øý“™¤ /test-4/790worldanyoneBOý“cŠ*ùý“™¥ /test-8/808worldanyoneBOý“cz úý“™§ /test-7/798worldanyoneBOý“czûý“™§ /test-3/796worldanyoneBOý“cŠ-üý“™¨ /test-9/811worldanyoneBOý“c~ ýý“™¨ /test-2/798worldanyoneBOý“cz!þý“™¨ /test-5/799worldanyoneBOý“c~)ÿý“™¨ /test-0/807worldanyoneBOý“bao ý“™­ /test-6/877worldanyoneBOý“baq ý“™¯ /test-1/879worldanyoneBOý“c~ ý“™¯ /test-4/791worldanyoneBOý“cŠ+ ý“™¯ /test-8/809worldanyoneBOý“cz ý“™± /test-3/797worldanyoneBOý“cz! ý“™± /test-7/799worldanyoneBOý“c~* ý“™³ /test-0/808worldanyoneBOý“cŠ. ý“™³ /test-9/812worldanyoneBOý“c~! ý“™´ /test-2/799worldanyoneBOý“cz" ý“™´ /test-5/800worldanyoneBOý“bap ý“™¶ /test-6/878worldanyoneBOý“bar ý“™¹ /test-1/880worldanyoneBOý“c~ ý“™¹ /test-4/792worldanyoneBOý“cŠ, ý“™¹ /test-8/810worldanyoneBOý“cz  ý“™º /test-3/798worldanyoneBOý“cz" ý“™º /test-7/800worldanyoneBOý“cz# ý“™½ /test-5/801worldanyoneBOý“cŠ/ ý“™½ /test-9/813worldanyoneBOý“c~+ ý“™½ /test-0/809worldanyoneBOý“c~" ý“™¾ /test-2/800worldanyoneBOý“baq ý“™¾ /test-6/879worldanyoneBOý“bas ý“™Á /test-1/881worldanyoneBOý“c~ ý“™Á /test-4/793worldanyoneBOý“cŠ- ý“™Â /test-8/811worldanyoneBOý“cz# ý“™Ã /test-7/801worldanyoneBOý“cz! ý“™Ã /test-3/799worldanyoneBOý“cz$ ý“™Ç /test-5/802worldanyoneBOý“bar ý“™È /test-6/880worldanyoneBOý“cŠ0 ý“™È /test-9/814worldanyoneBOý“c~# ý“™È /test-2/801worldanyoneBOý“c~, ý“™È /test-0/810worldanyoneBOý“bat ý“™Ê /test-1/882worldanyoneBOý“c~ ý“™Ê /test-4/794worldanyoneBOý“cŠ. !ý“™Ë /test-8/812worldanyoneBOý“cz$ "ý“™Ë /test-7/802worldanyoneBOý“cz" #ý“™Ì /test-3/800worldanyoneBOý“cz% $ý“™Ï /test-5/803worldanyoneBOý“bas %ý“™Ò /test-6/881worldanyoneBOý“c~$ &ý“™Ò /test-2/802worldanyoneBOý“c~- 'ý“™Ò /test-0/811worldanyoneBOý“cŠ1 (ý“™Ò /test-9/815worldanyoneBOý“cŠ/ )ý“™Õ /test-8/813worldanyoneBOý“bau *ý“™Ö /test-1/883worldanyoneBOý“cz# +ý“™Ö /test-3/801worldanyoneBOý“c~ ,ý“™Ö /test-4/795worldanyoneBOý“cz% -ý“™Ö /test-7/803worldanyoneBOý“cz& .ý“™Ø /test-5/804worldanyoneBOý“bat /ý“™Û /test-6/882worldanyoneBOý“cŠ2 0ý“™Ü /test-9/816worldanyoneBOý“c~% 1ý“™Ü /test-2/803worldanyoneBOý“c~. 2ý“™Ü /test-0/812worldanyoneBOý“bav 3ý“™à /test-1/884worldanyoneBOý“cŠ0 4ý“™à /test-8/814worldanyoneBOý“cz$ 5ý“™à /test-3/802worldanyoneBOý“cz& 6ý“™à /test-7/804worldanyoneBOý“c~ 7ý“™à /test-4/796worldanyoneBOý“cz' 8ý“™á /test-5/805worldanyoneBOý“bau 9ý“™ã /test-6/883worldanyoneBOý“cŠ3 :ý“™å /test-9/817worldanyoneBOý“c~& ;ý“™å /test-2/804worldanyoneBOý“c~/ <ý“™æ /test-0/813worldanyoneBOý“cz( =ý“™ê /test-5/806worldanyoneBOý“cŠ1 >ý“™ê /test-8/815worldanyoneBOý“baw ?ý“™ê /test-1/885worldanyoneBOý“cz' @ý“™ê /test-7/805worldanyoneBOý“cz% Aý“™ë /test-3/803worldanyoneBOý“bav Bý“™ë /test-6/884worldanyoneBOý“c~ Cý“™ë /test-4/797worldanyoneBOý“c~' Dý“™î /test-2/805worldanyoneBOý“cŠ4 Eý“™î /test-9/818worldanyoneBOý“c~0 Fý“™ï /test-0/814worldanyoneBOý“cz) Gý“™ó /test-5/807worldanyoneBOý“cŠ2 Hý“™ó /test-8/816worldanyoneBOý“bax Iý“™õ /test-1/886worldanyoneBOý“baw Jý“™õ /test-6/885worldanyoneBOý“c~  Ký“™õ /test-4/798worldanyoneBOý“cz& Lý“™ö /test-3/804worldanyoneBOý“cz( Mý“™ö /test-7/806worldanyoneBOý“cŠ5 Ný“™ø /test-9/819worldanyoneBOý“c~1 Oý“™ù /test-0/815worldanyoneBOý“c~( Pý“™ù /test-2/806worldanyoneBOý“cŠ3 Qý“™û /test-8/817worldanyoneBOý“cz* Rý“™ü /test-5/808worldanyoneBOý“bax Sý“™ÿ /test-6/886worldanyoneBOý“bay Tý“™ÿ /test-1/887worldanyoneBOý“c~! Uý“™ÿ /test-4/799worldanyoneBOý“cz) Vý“™ÿ /test-7/807worldanyoneBOý“cz' Wý“™ÿ /test-3/805worldanyoneBOý“cŠ6 Xý“š /test-9/820worldanyoneBOý“c~) Yý“š /test-2/807worldanyoneBOý“c~2 Zý“š /test-0/816worldanyoneBOý“cz+ [ý“š /test-5/809worldanyoneBOý“cŠ4 \ý“š /test-8/818worldanyoneBOý“bay ]ý“š /test-6/887worldanyoneBOý“baz ^ý“š  /test-1/888worldanyoneBOý“cz* _ý“š  /test-7/808worldanyoneBOý“cz( `ý“š  /test-3/806worldanyoneBOý“c~" aý“š  /test-4/800worldanyoneBOý“cŠ7 bý“š  /test-9/821worldanyoneBOý“c~* cý“š  /test-2/808worldanyoneBOý“cŠ5 dý“š  /test-8/819worldanyoneBOý“cz, eý“š  /test-5/810worldanyoneBOý“c~3 fý“š  /test-0/817worldanyoneBOý“baz gý“š /test-6/888worldanyoneBOý“ba{ hý“š /test-1/889worldanyoneBOý“cŠ8 iý“š /test-9/822worldanyoneBOý“c~# jý“š /test-4/801worldanyoneBOý“cz+ ký“š /test-7/809worldanyoneBOý“cz) lý“š /test-3/807worldanyoneBOý“c~+ mý“š /test-2/809worldanyoneBOý“cz- ný“š /test-5/811worldanyoneBOý“cŠ6 oý“š /test-8/820worldanyoneBOý“c~4 pý“š /test-0/818worldanyoneBOý“ba{ qý“š /test-6/889worldanyoneBOý“ba| rý“š /test-1/890worldanyoneBOý“cŠ9 sý“š /test-9/823worldanyoneBOý“cz, tý“š /test-7/810worldanyoneBOý“cz* uý“š /test-3/808worldanyoneBOý“c~$ vý“š /test-4/802worldanyoneBOý“c~, wý“š /test-2/810worldanyoneBOý“ba| xý“š  /test-6/890worldanyoneBOý“c~5 yý“š  /test-0/819worldanyoneBOý“cz. zý“š  /test-5/812worldanyoneBOý“cŠ7 {ý“š  /test-8/821worldanyoneBOý“ba} |ý“š& /test-1/891worldanyoneBOý“cz- }ý“š& /test-7/811worldanyoneBOý“cŠ: ~ý“š& /test-9/824worldanyoneBOý“cz+ ý“š' /test-3/809worldanyoneBOý“ba} €ý“š* /test-6/891worldanyoneBOý“cz/ ý“š* /test-5/813worldanyoneBOý“cŠ8 ‚ý“š* /test-8/822worldanyoneBOý“c~% ƒý“š* /test-4/803worldanyoneBOý“c~- „ý“š* /test-2/811worldanyoneBOý“c~6 …ý“š* /test-0/820worldanyoneBOý“ba~ †ý“š/ /test-1/892worldanyoneBOý“cŠ; ‡ý“š/ /test-9/825worldanyoneBOý“cz, ˆý“š/ /test-3/810worldanyoneBOý“cz. ‰ý“š/ /test-7/812worldanyoneBOý“ba~ Šý“š4 /test-6/892worldanyoneBOý“cz0 ‹ý“š4 /test-5/814worldanyoneBOý“cŠ9 Œý“š4 /test-8/823worldanyoneBOý“c~& ý“š4 /test-4/804worldanyoneBOý“c~7 Žý“š4 /test-0/821worldanyoneBOý“c~. ý“š4 /test-2/812worldanyoneBOý“ba ý“š8 /test-1/893worldanyoneBOý“cŠ< ‘ý“š8 /test-9/826worldanyoneBOý“cz- ’ý“š8 /test-3/811worldanyoneBOý“cz/ “ý“š9 /test-7/813worldanyoneBOý“ba ”ý“š= /test-6/893worldanyoneBOý“cz1 •ý“š> /test-5/815worldanyoneBOý“c~' –ý“š> /test-4/805worldanyoneBOý“c~8 —ý“š> /test-0/822worldanyoneBOý“c~/ ˜ý“š> /test-2/813worldanyoneBOý“cŠ: ™ý“š? /test-8/824worldanyoneBOý“ba€ šý“šA /test-1/894worldanyoneBOý“cŠ= ›ý“šA /test-9/827worldanyoneBOý“cz0 œý“šA /test-7/814worldanyoneBOý“cz. ý“šA /test-3/812worldanyoneBOý“ba€ žý“šE /test-6/894worldanyoneBOý“cz2 Ÿý“šH /test-5/816worldanyoneBOý“c~(  ý“šJ /test-4/806worldanyoneBOý“c~9 ¡ý“šJ /test-0/823worldanyoneBOý“cŠ; ¢ý“šJ /test-8/825worldanyoneBOý“c~0 £ý“šJ /test-2/814worldanyoneBOý“ba ¤ý“šK /test-1/895worldanyoneBOý“cŠ> ¥ý“šK /test-9/828worldanyoneBOý“cz1 ¦ý“šL /test-7/815worldanyoneBOý“cz/ §ý“šL /test-3/813worldanyoneBOý“ba ¨ý“šM /test-6/895worldanyoneBOý“cz3 ©ý“šP /test-5/817worldanyoneBOý“ba‚ ªý“šU /test-1/896worldanyoneBOý“cŠ? «ý“šU /test-9/829worldanyoneBOý“cŠ< ¬ý“šU /test-8/826worldanyoneBOý“c~: ­ý“šU /test-0/824worldanyoneBOý“c~) ®ý“šU /test-4/807worldanyoneBOý“c~1 ¯ý“šV /test-2/815worldanyoneBOý“ba‚ °ý“šW /test-6/896worldanyoneBOý“cz2 ±ý“šW /test-7/816worldanyoneBOý“cz0 ²ý“šW /test-3/814worldanyoneBOý“cz4 ³ý“šX /test-5/818worldanyoneBOý“baƒ ´ý“š_ /test-1/897worldanyoneBOý“cŠ= µý“š_ /test-8/827worldanyoneBOý“cŠ@ ¶ý“š` /test-9/830worldanyoneBOý“c~; ·ý“š` /test-0/825worldanyoneBOý“c~* ¸ý“š` /test-4/808worldanyoneBOý“c~2 ¹ý“š` /test-2/816worldanyoneBOý“baƒ ºý“ša /test-6/897worldanyoneBOý“cz3 »ý“ša /test-7/817worldanyoneBOý“cz5 ¼ý“šb /test-5/819worldanyoneBOý“cz1 ½ý“šb /test-3/815worldanyoneBOý“ba„ ¾ý“šl /test-1/898worldanyoneBOý“cz6 ¿ý“šl /test-5/820worldanyoneBOý“ba„ Àý“šl /test-6/898worldanyoneBOý“cz4 Áý“šl /test-7/818worldanyoneBOý“cz2 Âý“šm /test-3/816worldanyoneBOý“cŠ> Ãý“šm /test-8/828worldanyoneBOý“cŠA Äý“šm /test-9/831worldanyoneBOý“c~3 Åý“šm /test-2/817worldanyoneBOý“c~+ Æý“šm /test-4/809worldanyoneBOý“c~< Çý“šm /test-0/826worldanyoneBOý“cŠB Èý“šv /test-9/832worldanyoneBOý“cŠ? Éý“šv /test-8/829worldanyoneBOý“ba… Êý“šw /test-6/899worldanyoneBOý“ba… Ëý“šw /test-1/899worldanyoneBOý“c~= Ìý“šw /test-0/827worldanyoneBOý“c~, Íý“šw /test-4/810worldanyoneBOý“cz5 Îý“šw /test-7/819worldanyoneBOý“cz3 Ïý“šw /test-3/817worldanyoneBOý“c~4 Ðý“šx /test-2/818worldanyoneBOý“cz7 Ñý“šx /test-5/821worldanyoneBOý“cŠC Òý“š /test-9/833worldanyoneBOý“cŠ@ Óý“š /test-8/830worldanyoneBOý“ba† Ôý“š‚ /test-1/900worldanyoneBOý“ba† Õý“š‚ /test-6/900worldanyoneBOý“cz8 Öý“šƒ /test-5/822worldanyoneBOý“cz6 ×ý“šƒ /test-7/820worldanyoneBOý“cz4 Øý“šƒ /test-3/818worldanyoneBOý“c~5 Ùý“šƒ /test-2/819worldanyoneBOý“c~- Úý“šƒ /test-4/811worldanyoneBOý“c~> Ûý“šƒ /test-0/828worldanyoneBOý“cŠD Üý“šŠ /test-9/834worldanyoneBOý“cŠA Ýý“šŠ /test-8/831worldanyoneBOý“ba‡ Þý“šŒ /test-1/901worldanyoneBOý“ba‡ ßý“š /test-6/901worldanyoneBOý“cz9 àý“š /test-5/823worldanyoneBOý“cz7 áý“š /test-7/821worldanyoneBOý“c~. âý“š /test-4/812worldanyoneBOý“c~6 ãý“šŽ /test-2/820worldanyoneBOý“c~? äý“šŽ /test-0/829worldanyoneBOý“cz5 åý“šŽ /test-3/819worldanyoneBOý“cŠE æý“š“ /test-9/835worldanyoneBOý“cŠB çý“š“ /test-8/832worldanyoneBOý“baˆ èý“š” /test-1/902worldanyoneBOý“baˆ éý“š– /test-6/902worldanyoneBOý“c~/ êý“š™ /test-4/813worldanyoneBOý“c~@ ëý“š™ /test-0/830worldanyoneBOý“cz: ìý“š™ /test-5/824worldanyoneBOý“cz8 íý“š™ /test-7/822worldanyoneBOý“c~7 îý“š™ /test-2/821worldanyoneBOý“cz6 ïý“šš /test-3/820worldanyoneBOý“ba‰ ðý“š /test-1/903worldanyoneBOý“cŠF ñý“š /test-9/836worldanyoneBOý“cŠC òý“š /test-8/833worldanyoneBOý“ba‰ óý“šž /test-6/903worldanyoneBOý“c~0 ôý“š¢ /test-4/814worldanyoneBOý“c~A õý“š¤ /test-0/831worldanyoneBOý“c~8 öý“š¥ /test-2/822worldanyoneBOý“cz; ÷ý“š¥ /test-5/825worldanyoneBOý“cz7 øý“š¥ /test-3/821worldanyoneBOý“cz9 ùý“š¥ /test-7/823worldanyoneBOý“cŠG úý“š§ /test-9/837worldanyoneBOý“baŠ ûý“š§ /test-1/904worldanyoneBOý“cŠD üý“š§ /test-8/834worldanyoneBOý“baŠ ýý“š§ /test-6/904worldanyoneBOý“c~1 þý“šª /test-4/815worldanyoneBOý“c~B ÿý“š® /test-0/832worldanyoneBOý“c~9!ý“š® /test-2/823worldanyoneBOý“cz<!ý“š¯ /test-5/826worldanyoneBOý“cz:!ý“š¯ /test-7/824worldanyoneBOý“cz8!ý“š¯ /test-3/822worldanyoneBOý“ba‹!ý“š² /test-6/905worldanyoneBOý“ba‹!ý“š² /test-1/905worldanyoneBOý“cŠH!ý“š³ /test-9/838worldanyoneBOý“cŠE!ý“š³ /test-8/835worldanyoneBOý“c~2!ý“š´ /test-4/816worldanyoneBOý“c~C! ý“š· /test-0/833worldanyoneBOý“c~:! ý“š· /test-2/824worldanyoneBOý“cz;! ý“š¹ /test-7/825worldanyoneBOý“cz=! ý“š¹ /test-5/827worldanyoneBOý“cz9! ý“š¹ /test-3/823worldanyoneBOý“baŒ!ý“š½ /test-1/906worldanyoneBOý“baŒ!ý“š½ /test-6/906worldanyoneBOý“c~3!ý“š½ /test-4/817worldanyoneBOý“cŠF!ý“š½ /test-8/836worldanyoneBOý“cŠI!ý“š¾ /test-9/839worldanyoneBOý“c~D!ý“šÀ /test-0/834worldanyoneBOý“c~;!ý“šÀ /test-2/825worldanyoneBOý“cz<!ý“šÃ /test-7/826worldanyoneBOý“cz:!ý“šÃ /test-3/824worldanyoneBOý“cz>!ý“šÄ /test-5/828worldanyoneBOý“ba!ý“šÆ /test-1/907worldanyoneBOý“ba!ý“šÆ /test-6/907worldanyoneBOý“cŠG!ý“šÈ /test-8/837worldanyoneBOý“cŠJ!ý“šÈ /test-9/840worldanyoneBOý“c~4!ý“šÈ /test-4/818worldanyoneBOý“c~E!ý“šÊ /test-0/835worldanyoneBOý“c~<!ý“šÊ /test-2/826worldanyoneBOý“cz=!ý“šË /test-7/827worldanyoneBOý“cz;! ý“šË /test-3/825worldanyoneBOý“cz?!!ý“šÍ /test-5/829worldanyoneBOý“baŽ!"ý“šÏ /test-6/908worldanyoneBOý“baŽ!#ý“šÏ /test-1/908worldanyoneBOý“cŠH!$ý“šÑ /test-8/838worldanyoneBOý“cŠK!%ý“šÑ /test-9/841worldanyoneBOý“c~5!&ý“šÑ /test-4/819worldanyoneBOý“cz<!'ý“šÕ /test-3/826worldanyoneBOý“cz>!(ý“šÕ /test-7/828worldanyoneBOý“cz@!)ý“šÕ /test-5/830worldanyoneBOý“c~F!*ý“šÕ /test-0/836worldanyoneBOý“c~=!+ý“šÕ /test-2/827worldanyoneBOý“ba!,ý“šØ /test-1/909worldanyoneBOý“ba!-ý“šØ /test-6/909worldanyoneBOý“cŠL!.ý“šÚ /test-9/842worldanyoneBOý“c~6!/ý“šÚ /test-4/820worldanyoneBOý“cŠI!0ý“šÚ /test-8/839worldanyoneBOý“cz=!1ý“šß /test-3/827worldanyoneBOý“c~G!2ý“šß /test-0/837worldanyoneBOý“c~>!3ý“šß /test-2/828worldanyoneBOý“cz?!4ý“šà /test-7/829worldanyoneBOý“czA!5ý“šà /test-5/831worldanyoneBOý“ba!6ý“šá /test-1/910worldanyoneBOý“ba!7ý“šâ /test-6/910worldanyoneBOý“cŠM!8ý“šã /test-9/843worldanyoneBOý“c~7!9ý“šã /test-4/821worldanyoneBOý“cŠJ!:ý“šä /test-8/840worldanyoneBOý“ba‘!;ý“šê /test-1/911worldanyoneBOý“cz>!<ý“šê /test-3/828worldanyoneBOý“cz@!=ý“šê /test-7/830worldanyoneBOý“czB!>ý“šê /test-5/832worldanyoneBOý“c~H!?ý“šê /test-0/838worldanyoneBOý“c~?!@ý“šê /test-2/829worldanyoneBOý“ba‘!Aý“šì /test-6/911worldanyoneBOý“c~8!Bý“šì /test-4/822worldanyoneBOý“cŠK!Cý“ší /test-8/841worldanyoneBOý“cŠN!Dý“ší /test-9/844worldanyoneBOý“czA!Eý“š÷ /test-7/831worldanyoneBOý“ba’!Fý“šø /test-1/912worldanyoneBOý“ba’!Gý“šø /test-6/912worldanyoneBOý“czC!Hý“šø /test-5/833worldanyoneBOý“cz?!Iý“šø /test-3/829worldanyoneBOý“c~I!Jý“šø /test-0/839worldanyoneBOý“c~9!Ký“šù /test-4/823worldanyoneBOý“c~@!Lý“šù /test-2/830worldanyoneBOý“cŠL!Mý“šù /test-8/842worldanyoneBOý“cŠO!Ný“šù /test-9/845worldanyoneBOý“czB!Oý“› /test-7/832worldanyoneBOý“ba“!Pý“› /test-6/913worldanyoneBOý“ba“!Qý“› /test-1/913worldanyoneBOý“c~J!Rý“› /test-0/840worldanyoneBOý“c~:!Sý“› /test-4/824worldanyoneBOý“c~A!Tý“› /test-2/831worldanyoneBOý“cŠP!Uý“› /test-9/846worldanyoneBOý“cŠM!Vý“› /test-8/843worldanyoneBOý“czD!Wý“› /test-5/834worldanyoneBOý“cz@!Xý“› /test-3/830worldanyoneBOý“czC!Yý“› /test-7/833worldanyoneBOý“ba”!Zý“› /test-6/914worldanyoneBOý“ba”![ý“› /test-1/914worldanyoneBOý“c~;!\ý“› /test-4/825worldanyoneBOý“c~B!]ý“› /test-2/832worldanyoneBOý“c~K!^ý“› /test-0/841worldanyoneBOý“cŠN!_ý“› /test-8/844worldanyoneBOý“cŠQ!`ý“› /test-9/847worldanyoneBOý“czE!aý“› /test-5/835worldanyoneBOý“czA!bý“› /test-3/831worldanyoneBOý“czD!cý“› /test-7/834worldanyoneBOý“ba•!dý“› /test-6/915worldanyoneBOý“ba•!eý“› /test-1/915worldanyoneBOý“c~<!fý“› /test-4/826worldanyoneBOý“cŠO!gý“›H /test-8/845worldanyoneBOý“cŠR!hý“›H /test-9/848worldanyoneBOý“c~C!iý“›H /test-2/833worldanyoneBOý“c~L!jý“›H /test-0/842worldanyoneBOý“czF!ký“›H /test-5/836worldanyoneBOý“czB!lý“›H /test-3/832worldanyoneBOý“czE!mý“›H /test-7/835worldanyoneBOý“ba–!ný“›M /test-6/916worldanyoneBOý“ba–!oý“›S /test-1/916worldanyoneBOý“cŠP!pý“›S /test-8/846worldanyoneBOý“czG!qý“›S /test-5/837worldanyoneBOý“c~M!rý“›S /test-0/843worldanyoneBOý“c~D!sý“›T /test-2/834worldanyoneBOý“cŠS!tý“›T /test-9/849worldanyoneBOý“czF!uý“›T /test-7/836worldanyoneBOý“czC!vý“›T /test-3/833worldanyoneBOý“c~=!wý“›T /test-4/827worldanyoneBOý“ba—!xý“›V /test-6/917worldanyoneBOý“ba—!yý“›] /test-1/917worldanyoneBOý“cŠQ!zý“›] /test-8/847worldanyoneBOý“czH!{ý“›] /test-5/838worldanyoneBOý“c~N!|ý“›] /test-0/844worldanyoneBOý“czG!}ý“›_ /test-7/837worldanyoneBOý“czD!~ý“›_ /test-3/834worldanyoneBOý“ba˜!ý“›` /test-6/918worldanyoneBOý“c~E!€ý“›` /test-2/835worldanyoneBOý“c~>!ý“›` /test-4/828worldanyoneBOý“cŠT!‚ý“›` /test-9/850worldanyoneBOý“ba˜!ƒý“›g /test-1/918worldanyoneBOý“c~O!„ý“›g /test-0/845worldanyoneBOý“cŠR!…ý“›g /test-8/848worldanyoneBOý“czI!†ý“›g /test-5/839worldanyoneBOý“ba™!‡ý“›j /test-6/919worldanyoneBOý“czE!ˆý“›j /test-3/835worldanyoneBOý“czH!‰ý“›k /test-7/838worldanyoneBOý“cŠU!Šý“›k /test-9/851worldanyoneBOý“c~F!‹ý“›k /test-2/836worldanyoneBOý“c~?!Œý“›l /test-4/829worldanyoneBOý“ba™!ý“›p /test-1/919worldanyoneBOý“cŠS!Žý“›p /test-8/849worldanyoneBOý“c~P!ý“›p /test-0/846worldanyoneBOý“czJ!ý“›q /test-5/840worldanyoneBOý“baš!‘ý“›s /test-6/920worldanyoneBOý“cŠV!’ý“›u /test-9/852worldanyoneBOý“czF!“ý“›u /test-3/836worldanyoneBOý“czI!”ý“›u /test-7/839worldanyoneBOý“c~@!•ý“›u /test-4/830worldanyoneBOý“c~G!–ý“›v /test-2/837worldanyoneBOý“baš!—ý“›y /test-1/920worldanyoneBOý“c~Q!˜ý“›y /test-0/847worldanyoneBOý“czK!™ý“›z /test-5/841worldanyoneBOý“cŠT!šý“›z /test-8/850worldanyoneBOý“ba›!›ý“›{ /test-6/921worldanyoneBOý“czG!œý“› /test-3/837worldanyoneBOý“cŠW!ý“› /test-9/853worldanyoneBOý“c~A!žý“› /test-4/831worldanyoneBOý“czJ!Ÿý“›€ /test-7/840worldanyoneBOý“c~H! ý“›€ /test-2/838worldanyoneBOý“ba›!¡ý“› /test-1/921worldanyoneBOý“czL!¢ý“›ƒ /test-5/842worldanyoneBOý“cŠU!£ý“›ƒ /test-8/851worldanyoneBOý“c~R!¤ý“›„ /test-0/848worldanyoneBOý“baœ!¥ý“›„ /test-6/922worldanyoneBOý“c~B!¦ý“›ˆ /test-4/832worldanyoneBOý“cŠX!§ý“›ˆ /test-9/854worldanyoneBOý“czH!¨ý“›ˆ /test-3/838worldanyoneBOý“baœ!©ý“›Š /test-1/922worldanyoneBOý“czK!ªý“›Š /test-7/841worldanyoneBOý“c~I!«ý“›Š /test-2/839worldanyoneBOý“ba!¬ý“› /test-6/923worldanyoneBOý“c~S!­ý“› /test-0/849worldanyoneBOý“czM!®ý“› /test-5/843worldanyoneBOý“cŠV!¯ý“›Ž /test-8/852worldanyoneBOý“czI!°ý“›‘ /test-3/839worldanyoneBOý“cŠY!±ý“›‘ /test-9/855worldanyoneBOý“ba!²ý“›’ /test-1/923worldanyoneBOý“c~C!³ý“›’ /test-4/833worldanyoneBOý“czL!´ý“›“ /test-7/842worldanyoneBOý“c~J!µý“›” /test-2/840worldanyoneBOý“baž!¶ý“›— /test-6/924worldanyoneBOý“c~T!·ý“›— /test-0/850worldanyoneBOý“cŠW!¸ý“›— /test-8/853worldanyoneBOý“czN!¹ý“›— /test-5/844worldanyoneBOý“czJ!ºý“›š /test-3/840worldanyoneBOý“baž!»ý“›› /test-1/924worldanyoneBOý“c~D!¼ý“›› /test-4/834worldanyoneBOý“czM!½ý“›› /test-7/843worldanyoneBOý“cŠZ!¾ý“›› /test-9/856worldanyoneBOý“c~K!¿ý“› /test-2/841worldanyoneBOý“baŸ!Àý“›  /test-6/925worldanyoneBOý“c~U!Áý“›  /test-0/851worldanyoneBOý“czO!Âý“›  /test-5/845worldanyoneBOý“cŠX!Ãý“›  /test-8/854worldanyoneBOý“czK!Äý“›£ /test-3/841worldanyoneBOý“baŸ!Åý“›¤ /test-1/925worldanyoneBOý“c~E!Æý“›¤ /test-4/835worldanyoneBOý“cŠ[!Çý“›¤ /test-9/857worldanyoneBOý“czN!Èý“›¤ /test-7/844worldanyoneBOý“c~L!Éý“›¥ /test-2/842worldanyoneBOý“ba !Êý“›© /test-6/926worldanyoneBOý“c~V!Ëý“›© /test-0/852worldanyoneBOý“cŠY!Ìý“›© /test-8/855worldanyoneBOý“czP!Íý“›© /test-5/846worldanyoneBOý“ba !Îý“›® /test-1/926worldanyoneBOý“cŠ\!Ïý“›® /test-9/858worldanyoneBOý“c~M!Ðý“›® /test-2/843worldanyoneBOý“c~F!Ñý“›® /test-4/836worldanyoneBOý“czO!Òý“›¯ /test-7/845worldanyoneBOý“czL!Óý“›¯ /test-3/842worldanyoneBOý“ba¡!Ôý“›² /test-6/927worldanyoneBOý“cŠZ!Õý“›² /test-8/856worldanyoneBOý“czQ!Öý“›³ /test-5/847worldanyoneBOý“c~W!×ý“›³ /test-0/853worldanyoneBOý“ba¡!Øý“›¶ /test-1/927worldanyoneBOý“c~N!Ùý“›· /test-2/844worldanyoneBOý“cŠ]!Úý“›· /test-9/859worldanyoneBOý“c~G!Ûý“›¸ /test-4/837worldanyoneBOý“czP!Üý“›¸ /test-7/846worldanyoneBOý“czM!Ýý“›¸ /test-3/843worldanyoneBOý“ba¢!Þý“›º /test-6/928worldanyoneBOý“cŠ[!ßý“›» /test-8/857worldanyoneBOý“czR!àý“›¼ /test-5/848worldanyoneBOý“c~X!áý“›¼ /test-0/854worldanyoneBOý“ba¢!âý“›¾ /test-1/928worldanyoneBOý“cŠ^!ãý“›Á /test-9/860worldanyoneBOý“czN!äý“›Á /test-3/844worldanyoneBOý“czQ!åý“›Á /test-7/847worldanyoneBOý“c~O!æý“›Á /test-2/845worldanyoneBOý“c~H!çý“›Á /test-4/838worldanyoneBOý“ba£!èý“›Ã /test-6/929worldanyoneBOý“cŠ\!éý“›Ä /test-8/858worldanyoneBOý“c~Y!êý“›Å /test-0/855worldanyoneBOý“czS!ëý“›Å /test-5/849worldanyoneBOý“ba£!ìý“›Æ /test-1/929worldanyoneBOý“cŠ_!íý“›Ê /test-9/861worldanyoneBOý“c~I!îý“›Ë /test-4/839worldanyoneBOý“c~P!ïý“›Ë /test-2/846worldanyoneBOý“czR!ðý“›Ë /test-7/848worldanyoneBOý“czO!ñý“›Ì /test-3/845worldanyoneBOý“ba¤!òý“›Ï /test-6/930worldanyoneBOý“czT!óý“›Ï /test-5/850worldanyoneBOý“cŠ]!ôý“›Ï /test-8/859worldanyoneBOý“ba¤!õý“›Ï /test-1/930worldanyoneBOý“c~Z!öý“›Ð /test-0/856worldanyoneBOý“cŠ`!÷ý“›Ò /test-9/862worldanyoneBOý“czS!øý“›Õ /test-7/849worldanyoneBOý“czP!ùý“›Õ /test-3/846worldanyoneBOý“c~J!úý“›Õ /test-4/840worldanyoneBOý“c~Q!ûý“›Õ /test-2/847worldanyoneBOý“ba¥!üý“›Ù /test-6/931worldanyoneBOý“ba¥!ýý“›Ù /test-1/931worldanyoneBOý“cŠ^!þý“›Ù /test-8/860worldanyoneBOý“czU!ÿý“›Ù /test-5/851worldanyoneBOý“c~["ý“›Ù /test-0/857worldanyoneBOý“cŠa"ý“›Ú /test-9/863worldanyoneBOý“czQ"ý“›Þ /test-3/847worldanyoneBOý“c~R"ý“›Þ /test-2/848worldanyoneBOý“czT"ý“›Þ /test-7/850worldanyoneBOý“c~K"ý“›Þ /test-4/841worldanyoneBOý“ba¦"ý“›â /test-1/932worldanyoneBOý“ba¦"ý“›â /test-6/932worldanyoneBOý“cŠ_"ý“›â /test-8/861worldanyoneBOý“czV" ý“›â /test-5/852worldanyoneBOý“cŠb" ý“›ã /test-9/864worldanyoneBOý“c~\" ý“›ã /test-0/858worldanyoneBOý“czR" ý“›ç /test-3/848worldanyoneBOý“c~S" ý“›é /test-2/849worldanyoneBOý“czU"ý“›é /test-7/851worldanyoneBOý“c~L"ý“›é /test-4/842worldanyoneBOý“ba§"ý“›î /test-1/933worldanyoneBOý“cŠ`"ý“›î /test-8/862worldanyoneBOý“cŠc"ý“›î /test-9/865worldanyoneBOý“czW"ý“›î /test-5/853worldanyoneBOý“ba§"ý“›ï /test-6/933worldanyoneBOý“c~]"ý“›ï /test-0/859worldanyoneBOý“czS"ý“›ð /test-3/849worldanyoneBOý“czV"ý“›ó /test-7/852worldanyoneBOý“c~T"ý“›ó /test-2/850worldanyoneBOý“c~M"ý“›ó /test-4/843worldanyoneBOý“ba¨"ý“›ø /test-1/934worldanyoneBOý“cŠd"ý“›ø /test-9/866worldanyoneBOý“cŠa"ý“›ø /test-8/863worldanyoneBOý“czX"ý“›ø /test-5/854worldanyoneBOý“ba¨"ý“›ù /test-6/934worldanyoneBOý“c~^"ý“›ù /test-0/860worldanyoneBOý“czT" ý“›ù /test-3/850worldanyoneBOý“czW"!ý“›û /test-7/853worldanyoneBOý“c~N""ý“›ü /test-4/844worldanyoneBOý“c~U"#ý“›ý /test-2/851worldanyoneBOý“cŠe"$ý“œ /test-9/867worldanyoneBOý“cŠb"%ý“œ /test-8/864worldanyoneBOý“ba©"&ý“œ /test-1/935worldanyoneBOý“ba©"'ý“œ /test-6/935worldanyoneBOý“czU"(ý“œ /test-3/851worldanyoneBOý“czY")ý“œ /test-5/855worldanyoneBOý“czX"*ý“œ /test-7/854worldanyoneBOý“c~_"+ý“œ /test-0/861worldanyoneBOý“c~V",ý“œ /test-2/852worldanyoneBOý“c~O"-ý“œ /test-4/845worldanyoneBOý“baª".ý“œ /test-1/936worldanyoneBOý“cŠc"/ý“œ /test-8/865worldanyoneBOý“cŠf"0ý“œ /test-9/868worldanyoneBOý“baª"1ý“œ /test-6/936worldanyoneBOý“czZ"2ý“œ /test-5/856worldanyoneBOý“c~W"3ý“œ /test-2/853worldanyoneBOý“czY"4ý“œ /test-7/855worldanyoneBOý“c~P"5ý“œ /test-4/846worldanyoneBOý“c~`"6ý“œ /test-0/862worldanyoneBOý“czV"7ý“œ /test-3/852worldanyoneBOý“ba«"8ý“œ /test-1/937worldanyoneBOý“cŠg"9ý“œ /test-9/869worldanyoneBOý“cŠd":ý“œ /test-8/866worldanyoneBOý“ba«";ý“œ /test-6/937worldanyoneBOý“cz["<ý“œ /test-5/857worldanyoneBOý“c~Q"=ý“œ /test-4/847worldanyoneBOý“c~X">ý“œ /test-2/854worldanyoneBOý“c~a"?ý“œ /test-0/863worldanyoneBOý“czZ"@ý“œ /test-7/856worldanyoneBOý“czW"Aý“œ /test-3/853worldanyoneBOý“ba¬"Bý“œ  /test-1/938worldanyoneBOý“cŠe"Cý“œ  /test-8/867worldanyoneBOý“cŠh"Dý“œ  /test-9/870worldanyoneBOý“ba¬"Eý“œ$ /test-6/938worldanyoneBOý“cz\"Fý“œ$ /test-5/858worldanyoneBOý“cz["Gý“œ% /test-7/857worldanyoneBOý“czX"Hý“œ% /test-3/854worldanyoneBOý“c~b"Iý“œ% /test-0/864worldanyoneBOý“c~R"Jý“œ% /test-4/848worldanyoneBOý“c~Y"Ký“œ& /test-2/855worldanyoneBOý“ba­"Lý“œ( /test-1/939worldanyoneBOý“cŠi"Mý“œ) /test-9/871worldanyoneBOý“cŠf"Ný“œ) /test-8/868worldanyoneBOý“ba­"Oý“œ- /test-6/939worldanyoneBOý“cz]"Pý“œ- /test-5/859worldanyoneBOý“cz\"Qý“œ- /test-7/858worldanyoneBOý“czY"Rý“œ- /test-3/855worldanyoneBOý“c~c"Sý“œ/ /test-0/865worldanyoneBOý“ba®"Tý“œ0 /test-1/940worldanyoneBOý“cŠg"Uý“œ1 /test-8/869worldanyoneBOý“cŠj"Vý“œ1 /test-9/872worldanyoneBOý“c~S"Wý“œ2 /test-4/849worldanyoneBOý“c~Z"Xý“œ2 /test-2/856worldanyoneBOý“ba®"Yý“œ6 /test-6/940worldanyoneBOý“ba¯"Zý“œ8 /test-1/941worldanyoneBOý“cz]"[ý“œ8 /test-7/859worldanyoneBOý“cz^"\ý“œ8 /test-5/860worldanyoneBOý“czZ"]ý“œ8 /test-3/856worldanyoneBOý“c~d"^ý“œ9 /test-0/866worldanyoneBOý“c~T"_ý“œ; /test-4/850worldanyoneBOý“cŠh"`ý“œ; /test-8/870worldanyoneBOý“cŠk"aý“œ; /test-9/873worldanyoneBOý“c~["bý“œ= /test-2/857worldanyoneBOý“ba¯"cý“œ> /test-6/941worldanyoneBOý“cz_"dý“œB /test-5/861worldanyoneBOý“cz^"eý“œB /test-7/860worldanyoneBOý“c~e"fý“œB /test-0/867worldanyoneBOý“ba°"gý“œC /test-1/942worldanyoneBOý“cz["hý“œC /test-3/857worldanyoneBOý“c~U"iý“œE /test-4/851worldanyoneBOý“cŠi"jý“œE /test-8/871worldanyoneBOý“cŠl"ký“œF /test-9/874worldanyoneBOý“ba°"lý“œG /test-6/942worldanyoneBOý“c~\"mý“œG /test-2/858worldanyoneBOý“ba±"ný“œL /test-1/943worldanyoneBOý“cz_"oý“œL /test-7/861worldanyoneBOý“cz`"pý“œL /test-5/862worldanyoneBOý“cz\"qý“œL /test-3/858worldanyoneBOý“c~f"rý“œM /test-0/868worldanyoneBOý“cŠj"sý“œN /test-8/872worldanyoneBOý“c~V"tý“œN /test-4/852worldanyoneBOý“ba±"uý“œP /test-6/943worldanyoneBOý“c~]"vý“œP /test-2/859worldanyoneBOý“cŠm"wý“œP /test-9/875worldanyoneBOý“ba²"xý“œU /test-1/944worldanyoneBOý“cza"yý“œW /test-5/863worldanyoneBOý“cz`"zý“œW /test-7/862worldanyoneBOý“cz]"{ý“œW /test-3/859worldanyoneBOý“c~g"|ý“œX /test-0/869worldanyoneBOý“ba²"}ý“œY /test-6/944worldanyoneBOý“cŠk"~ý“œY /test-8/873worldanyoneBOý“cŠn"ý“œZ /test-9/876worldanyoneBOý“c~^"€ý“œZ /test-2/860worldanyoneBOý“c~W"ý“œZ /test-4/853worldanyoneBOý“ba³"‚ý“œ] /test-1/945worldanyoneBOý“cza"ƒý“œa /test-7/863worldanyoneBOý“czb"„ý“œa /test-5/864worldanyoneBOý“cz^"…ý“œa /test-3/860worldanyoneBOý“c~h"†ý“œc /test-0/870worldanyoneBOý“ba³"‡ý“œd /test-6/945worldanyoneBOý“cŠo"ˆý“œd /test-9/877worldanyoneBOý“c~_"‰ý“œd /test-2/861worldanyoneBOý“c~X"Šý“œd /test-4/854worldanyoneBOý“cŠl"‹ý“œd /test-8/874worldanyoneBOý“ba´"Œý“œf /test-1/946worldanyoneBOý“czb"ý“œi /test-7/864worldanyoneBOý“czc"Žý“œj /test-5/865worldanyoneBOý“cz_"ý“œj /test-3/861worldanyoneBOý“ba´"ý“œo /test-6/946worldanyoneBOý“baµ"‘ý“œo /test-1/947worldanyoneBOý“c~i"’ý“œo /test-0/871worldanyoneBOý“c~`"“ý“œo /test-2/862worldanyoneBOý“c~Y"”ý“œo /test-4/855worldanyoneBOý“cŠp"•ý“œo /test-9/878worldanyoneBOý“cŠm"–ý“œo /test-8/875worldanyoneBOý“czc"—ý“œq /test-7/865worldanyoneBOý“czd"˜ý“œs /test-5/866worldanyoneBOý“cz`"™ý“œs /test-3/862worldanyoneBOý“baµ"šý“œx /test-6/947worldanyoneBOý“czd"›ý“œz /test-7/866worldanyoneBOý“ba¶"œý“œz /test-1/948worldanyoneBOý“c~j"ý“œz /test-0/872worldanyoneBOý“c~a"žý“œz /test-2/863worldanyoneBOý“cŠq"Ÿý“œz /test-9/879worldanyoneBOý“cŠn" ý“œz /test-8/876worldanyoneBOý“c~Z"¡ý“œ{ /test-4/856worldanyoneBOý“cza"¢ý“œ| /test-3/863worldanyoneBOý“cze"£ý“œ| /test-5/867worldanyoneBOý“ba¶"¤ý“œ€ /test-6/948worldanyoneBOý“ba·"¥ý“œ… /test-1/949worldanyoneBOý“c~b"¦ý“œ… /test-2/864worldanyoneBOý“c~k"§ý“œ… /test-0/873worldanyoneBOý“cze"¨ý“œ… /test-7/867worldanyoneBOý“c~["©ý“œ… /test-4/857worldanyoneBOý“czb"ªý“œ† /test-3/864worldanyoneBOý“cŠr"«ý“œ† /test-9/880worldanyoneBOý“czf"¬ý“œ† /test-5/868worldanyoneBOý“cŠo"­ý“œ† /test-8/877worldanyoneBOý“ba·"®ý“œˆ /test-6/949worldanyoneBOý“ba¸"¯ý“œ /test-1/950worldanyoneBOý“ba¸"°ý“œ /test-6/950worldanyoneBOý“cŠs"±ý“œ‘ /test-9/881worldanyoneBOý“cŠp"²ý“œ‘ /test-8/878worldanyoneBOý“czf"³ý“œ‘ /test-7/868worldanyoneBOý“czg"´ý“œ‘ /test-5/869worldanyoneBOý“czc"µý“œ‘ /test-3/865worldanyoneBOý“c~\"¶ý“œ‘ /test-4/858worldanyoneBOý“c~c"·ý“œ’ /test-2/865worldanyoneBOý“c~l"¸ý“œ’ /test-0/874worldanyoneBOý“ba¹"¹ý“œ— /test-1/951worldanyoneBOý“ba¹"ºý“œœ /test-6/951worldanyoneBOý“czg"»ý“œœ /test-7/869worldanyoneBOý“czd"¼ý“œœ /test-3/866worldanyoneBOý“c~d"½ý“œœ /test-2/866worldanyoneBOý“czh"¾ý“œœ /test-5/870worldanyoneBOý“c~m"¿ý“œ /test-0/875worldanyoneBOý“c~]"Àý“œ /test-4/859worldanyoneBOý“cŠt"Áý“œ /test-9/882worldanyoneBOý“cŠq"Âý“œ /test-8/879worldanyoneBOý“baº"Ãý“œŸ /test-1/952worldanyoneBOý“baº"Äý“œ¨ /test-6/952worldanyoneBOý“cŠu"Åý“œ¨ /test-9/883worldanyoneBOý“c~e"Æý“œ¨ /test-2/867worldanyoneBOý“c~n"Çý“œ¨ /test-0/876worldanyoneBOý“c~^"Èý“œ¨ /test-4/860worldanyoneBOý“cŠr"Éý“œ© /test-8/880worldanyoneBOý“ba»"Êý“œ© /test-1/953worldanyoneBOý“cze"Ëý“œ© /test-3/867worldanyoneBOý“czi"Ìý“œ© /test-5/871worldanyoneBOý“czh"Íý“œª /test-7/870worldanyoneBOý“ba¼"Îý“œ³ /test-1/954worldanyoneBOý“ba»"Ïý“œ³ /test-6/953worldanyoneBOý“cŠs"Ðý“œ³ /test-8/881worldanyoneBOý“cŠv"Ñý“œ´ /test-9/884worldanyoneBOý“c~f"Òý“œ´ /test-2/868worldanyoneBOý“c~_"Óý“œ´ /test-4/861worldanyoneBOý“c~o"Ôý“œ´ /test-0/877worldanyoneBOý“czf"Õý“œ´ /test-3/868worldanyoneBOý“czj"Öý“œ´ /test-5/872worldanyoneBOý“czi"×ý“œµ /test-7/871worldanyoneBOý“ba½"Øý“œ¼ /test-1/955worldanyoneBOý“ba¼"Ùý“œ¾ /test-6/954worldanyoneBOý“c~`"Úý“œ¾ /test-4/862worldanyoneBOý“cŠt"Ûý“œ¾ /test-8/882worldanyoneBOý“cŠw"Üý“œ¾ /test-9/885worldanyoneBOý“c~p"Ýý“œ¿ /test-0/878worldanyoneBOý“c~g"Þý“œ¿ /test-2/869worldanyoneBOý“czj"ßý“œ¿ /test-7/872worldanyoneBOý“czg"àý“œ¿ /test-3/869worldanyoneBOý“czk"áý“œ¿ /test-5/873worldanyoneBOý“ba¾"âý“œÄ /test-1/956worldanyoneBOý“ba½"ãý“œÈ /test-6/955worldanyoneBOý“czh"äý“œÉ /test-3/870worldanyoneBOý“czk"åý“œÉ /test-7/873worldanyoneBOý“cŠx"æý“œÉ /test-9/886worldanyoneBOý“czl"çý“œÊ /test-5/874worldanyoneBOý“c~q"èý“œÊ /test-0/879worldanyoneBOý“c~h"éý“œÊ /test-2/870worldanyoneBOý“c~a"êý“œÊ /test-4/863worldanyoneBOý“cŠu"ëý“œÊ /test-8/883worldanyoneBOý“ba¿"ìý“œÌ /test-1/957worldanyoneBOý“ba¾"íý“œÐ /test-6/956worldanyoneBOý“c~r"îý“œÕ /test-0/880worldanyoneBOý“c~i"ïý“œÕ /test-2/871worldanyoneBOý“baÀ"ðý“œÕ /test-1/958worldanyoneBOý“czm"ñý“œÕ /test-5/875worldanyoneBOý“cŠv"òý“œÕ /test-8/884worldanyoneBOý“cŠy"óý“œÖ /test-9/887worldanyoneBOý“czl"ôý“œÖ /test-7/874worldanyoneBOý“c~b"õý“œÖ /test-4/864worldanyoneBOý“czi"öý“œÖ /test-3/871worldanyoneBOý“ba¿"÷ý“œØ /test-6/957worldanyoneBOý“baÁ"øý“œß /test-1/959worldanyoneBOý“c~s"ùý“œß /test-0/881worldanyoneBOý“c~j"úý“œß /test-2/872worldanyoneBOý“czj"ûý“œà /test-3/872worldanyoneBOý“c~c"üý“œà /test-4/865worldanyoneBOý“cŠz"ýý“œà /test-9/888worldanyoneBOý“cŠw"þý“œà /test-8/885worldanyoneBOý“czm"ÿý“œà /test-7/875worldanyoneBOý“czn#ý“œá /test-5/876worldanyoneBOý“baÀ#ý“œá /test-6/958worldanyoneBOý“baÂ#ý“œê /test-1/960worldanyoneBOý“c~t#ý“œê /test-0/882worldanyoneBOý“czk#ý“œê /test-3/873worldanyoneBOý“cŠ{#ý“œë /test-9/889worldanyoneBOý“cŠx#ý“œë /test-8/886worldanyoneBOý“c~d#ý“œë /test-4/866worldanyoneBOý“baÁ#ý“œì /test-6/959worldanyoneBOý“czn# ý“œì /test-7/876worldanyoneBOý“czo# ý“œì /test-5/877worldanyoneBOý“c~k# ý“œì /test-2/873worldanyoneBOý“c~u# ý“œô /test-0/883worldanyoneBOý“czl# ý“œô /test-3/874worldanyoneBOý“baÃ#ý“œô /test-1/961worldanyoneBOý“c~e#ý“œö /test-4/867worldanyoneBOý“baÂ#ý“œö /test-6/960worldanyoneBOý“cŠ|#ý“œö /test-9/890worldanyoneBOý“c~l#ý“œ÷ /test-2/874worldanyoneBOý“cŠy#ý“œ÷ /test-8/887worldanyoneBOý“czo#ý“œ÷ /test-7/877worldanyoneBOý“czp#ý“œ÷ /test-5/878worldanyoneBOý“c~v#ý“œü /test-0/884worldanyoneBOý“baÄ#ý“œý /test-1/962worldanyoneBOý“czm#ý“œþ /test-3/875worldanyoneBOý“baÃ#ý“ /test-6/961worldanyoneBOý“cŠz#ý“ /test-8/888worldanyoneBOý“c~m#ý“ /test-2/875worldanyoneBOý“c~f#ý“ /test-4/868worldanyoneBOý“cŠ}#ý“ /test-9/891worldanyoneBOý“czp#ý“ /test-7/878worldanyoneBOý“czq#ý“ /test-5/879worldanyoneBOý“c~w# ý“ /test-0/885worldanyoneBOý“baÅ#!ý“ /test-1/963worldanyoneBOý“czn#"ý“ /test-3/876worldanyoneBOý“baÄ##ý“ /test-6/962worldanyoneBOý“cŠ{#$ý“ /test-8/889worldanyoneBOý“baÆ#%ý“ /test-1/964worldanyoneBOý“c~n#&ý“ /test-2/876worldanyoneBOý“c~x#'ý“ /test-0/886worldanyoneBOý“czq#(ý“ /test-7/879worldanyoneBOý“cŠ~#)ý“ /test-9/892worldanyoneBOý“c~g#*ý“ /test-4/869worldanyoneBOý“czr#+ý“ /test-5/880worldanyoneBOý“czo#,ý“ /test-3/877worldanyoneBOý“baÅ#-ý“ /test-6/963worldanyoneBOý“cŠ|#.ý“ /test-8/890worldanyoneBOý“cŠ#/ý“ /test-9/893worldanyoneBOý“czs#0ý“ /test-5/881worldanyoneBOý“baÇ#1ý“ /test-1/965worldanyoneBOý“czr#2ý“ /test-7/880worldanyoneBOý“czp#3ý“ /test-3/878worldanyoneBOý“c~y#4ý“ /test-0/887worldanyoneBOý“c~h#5ý“ /test-4/870worldanyoneBOý“c~o#6ý“ /test-2/877worldanyoneBOý“baÆ#7ý“  /test-6/964worldanyoneBOý“cŠ}#8ý“! /test-8/891worldanyoneBOý“cŠ€#9ý“$ /test-9/894worldanyoneBOý“czt#:ý“$ /test-5/882worldanyoneBOý“baÈ#;ý“% /test-1/966worldanyoneBOý“czs#<ý“% /test-7/881worldanyoneBOý“c~z#=ý“& /test-0/888worldanyoneBOý“c~p#>ý“& /test-2/878worldanyoneBOý“czq#?ý“& /test-3/879worldanyoneBOý“c~i#@ý“& /test-4/871worldanyoneBOý“baÇ#Aý“( /test-6/965worldanyoneBOý“cŠ~#Bý“) /test-8/892worldanyoneBOý“cŠ#Cý“, /test-9/895worldanyoneBOý“baÉ#Dý“. /test-1/967worldanyoneBOý“czu#Eý“. /test-5/883worldanyoneBOý“czt#Fý“. /test-7/882worldanyoneBOý“czr#Gý“0 /test-3/880worldanyoneBOý“c~{#Hý“0 /test-0/889worldanyoneBOý“c~j#Iý“0 /test-4/872worldanyoneBOý“c~q#Jý“0 /test-2/879worldanyoneBOý“baÈ#Ký“2 /test-6/966worldanyoneBOý“cŠ#Lý“3 /test-8/893worldanyoneBOý“cŠ‚#Mý“4 /test-9/896worldanyoneBOý“czu#Ný“6 /test-7/883worldanyoneBOý“baÊ#Oý“6 /test-1/968worldanyoneBOý“czv#Pý“7 /test-5/884worldanyoneBOý“czs#Qý“: /test-3/881worldanyoneBOý“baÉ#Rý“= /test-6/967worldanyoneBOý“cŠ€#Sý“= /test-8/894worldanyoneBOý“c~|#Tý“= /test-0/890worldanyoneBOý“c~r#Uý“= /test-2/880worldanyoneBOý“c~k#Vý“> /test-4/873worldanyoneBOý“cŠƒ#Wý“> /test-9/897worldanyoneBOý“czv#Xý“? /test-7/884worldanyoneBOý“baË#Yý“@ /test-1/969worldanyoneBOý“czw#Zý“@ /test-5/885worldanyoneBOý“czt#[ý“B /test-3/882worldanyoneBOý“baÊ#\ý“E /test-6/968worldanyoneBOý“czw#]ý“I /test-7/885worldanyoneBOý“cŠ„#^ý“I /test-9/898worldanyoneBOý“cŠ#_ý“I /test-8/895worldanyoneBOý“c~}#`ý“I /test-0/891worldanyoneBOý“c~l#aý“I /test-4/874worldanyoneBOý“c~s#bý“I /test-2/881worldanyoneBOý“baÌ#cý“L /test-1/970worldanyoneBOý“czu#dý“L /test-3/883worldanyoneBOý“czx#eý“L /test-5/886worldanyoneBOý“baË#fý“N /test-6/969worldanyoneBOý“czx#gý“R /test-7/886worldanyoneBOý“cŠ…#hý“S /test-9/899worldanyoneBOý“c~m#iý“S /test-4/875worldanyoneBOý“c~~#jý“T /test-0/892worldanyoneBOý“cŠ‚#ký“T /test-8/896worldanyoneBOý“c~t#lý“T /test-2/882worldanyoneBOý“baÍ#mý“U /test-1/971worldanyoneBOý“czy#ný“U /test-5/887worldanyoneBOý“czv#oý“U /test-3/884worldanyoneBOý“baÌ#pý“V /test-6/970worldanyoneBOý“czy#qý“Z /test-7/887worldanyoneBOý“c~n#rý“\ /test-4/876worldanyoneBOý“cІ#sý“\ /test-9/900worldanyoneBOý“baÎ#tý“_ /test-1/972worldanyoneBOý“baÍ#uý“_ /test-6/971worldanyoneBOý“c~u#vý“_ /test-2/883worldanyoneBOý“c~#wý“_ /test-0/893worldanyoneBOý“czz#xý“` /test-5/888worldanyoneBOý“cŠƒ#yý“` /test-8/897worldanyoneBOý“czw#zý“a /test-3/885worldanyoneBOý“czz#{ý“b /test-7/888worldanyoneBOý“c~o#|ý“d /test-4/877worldanyoneBOý“cЇ#}ý“e /test-9/901worldanyoneBOý“baÏ#~ý“g /test-1/973worldanyoneBOý“baÎ#ý“i /test-6/972worldanyoneBOý“c~v#€ý“j /test-2/884worldanyoneBOý“c~€#ý“j /test-0/894worldanyoneBOý“cŠ„#‚ý“k /test-8/898worldanyoneBOý“cz{#ƒý“k /test-7/889worldanyoneBOý“cz{#„ý“k /test-5/889worldanyoneBOý“czx#…ý“k /test-3/886worldanyoneBOý“cŠˆ#†ý“n /test-9/902worldanyoneBOý“c~p#‡ý“n /test-4/878worldanyoneBOý“baÐ#ˆý“o /test-1/974worldanyoneBOý“baÏ#‰ý“s /test-6/973worldanyoneBOý“c~#Šý“t /test-0/895worldanyoneBOý“c~w#‹ý“t /test-2/885worldanyoneBOý“cŠ…#Œý“u /test-8/899worldanyoneBOý“czy#ý“v /test-3/887worldanyoneBOý“cz|#Žý“v /test-5/890worldanyoneBOý“cz|#ý“v /test-7/890worldanyoneBOý“baÑ#ý“x /test-1/975worldanyoneBOý“c~q#‘ý“x /test-4/879worldanyoneBOý“cЉ#’ý“x /test-9/903worldanyoneBOý“baÐ#“ý“{ /test-6/974worldanyoneBOý“c~‚#”ý“} /test-0/896worldanyoneBOý“c~x#•ý“} /test-2/886worldanyoneBOý“czz#–ý“ /test-3/888worldanyoneBOý“cІ#—ý“ /test-8/900worldanyoneBOý“cz}#˜ý“ /test-5/891worldanyoneBOý“cz}#™ý“ /test-7/891worldanyoneBOý“baÒ#šý“ /test-1/976worldanyoneBOý“cŠŠ#›ý“‚ /test-9/904worldanyoneBOý“baÑ#œý“ƒ /test-6/975worldanyoneBOý“c~r#ý“ƒ /test-4/880worldanyoneBOý“c~ƒ#žý“† /test-0/897worldanyoneBOý“c~y#Ÿý“† /test-2/887worldanyoneBOý“cЇ# ý“ˆ /test-8/901worldanyoneBOý“cz~#¡ý“ˆ /test-5/892worldanyoneBOý“cz~#¢ý“ˆ /test-7/892worldanyoneBOý“cz{#£ý“ˆ /test-3/889worldanyoneBOý“baÓ#¤ý“‰ /test-1/977worldanyoneBOý“baÒ#¥ý“Œ /test-6/976worldanyoneBOý“c~s#¦ý“Œ /test-4/881worldanyoneBOý“cŠ‹#§ý“Œ /test-9/905worldanyoneBOý“c~z#¨ý“ /test-2/888worldanyoneBOý“c~„#©ý“ /test-0/898worldanyoneBOý“baÔ#ªý“‘ /test-1/978worldanyoneBOý“cz#«ý“‘ /test-5/893worldanyoneBOý“cz#¬ý“‘ /test-7/893worldanyoneBOý“cz|#­ý“‘ /test-3/890worldanyoneBOý“cŠˆ#®ý“‘ /test-8/902worldanyoneBOý“baÓ#¯ý“” /test-6/977worldanyoneBOý“cŠŒ#°ý“• /test-9/906worldanyoneBOý“c~t#±ý“• /test-4/882worldanyoneBOý“c~{#²ý“˜ /test-2/889worldanyoneBOý“c~…#³ý“˜ /test-0/899worldanyoneBOý“baÕ#´ý“š /test-1/979worldanyoneBOý“cz}#µý“š /test-3/891worldanyoneBOý“cz€#¶ý“› /test-5/894worldanyoneBOý“cЉ#·ý“› /test-8/903worldanyoneBOý“cz€#¸ý“› /test-7/894worldanyoneBOý“baÔ#¹ý“ž /test-6/978worldanyoneBOý“c~u#ºý“ž /test-4/883worldanyoneBOý“cŠ#»ý“ž /test-9/907worldanyoneBOý“c~†#¼ý“  /test-0/900worldanyoneBOý“c~|#½ý“  /test-2/890worldanyoneBOý“baÖ#¾ý“£ /test-1/980worldanyoneBOý“cz~#¿ý“£ /test-3/892worldanyoneBOý“cz#Àý“¥ /test-7/895worldanyoneBOý“cz#Áý“¥ /test-5/895worldanyoneBOý“cŠŠ#Âý“¥ /test-8/904worldanyoneBOý“baÕ#Ãý“§ /test-6/979worldanyoneBOý“c~v#Äý“§ /test-4/884worldanyoneBOý“cŠŽ#Åý“§ /test-9/908worldanyoneBOý“c~}#Æý“¨ /test-2/891worldanyoneBOý“c~‡#Çý“¨ /test-0/901worldanyoneBOý“ba×#Èý“« /test-1/981worldanyoneBOý“cz#Éý“¬ /test-3/893worldanyoneBOý“cŠ‹#Êý“¯ /test-8/905worldanyoneBOý“cz‚#Ëý“¯ /test-7/896worldanyoneBOý“cz‚#Ìý“¯ /test-5/896worldanyoneBOý“baÖ#Íý“± /test-6/980worldanyoneBOý“cŠ#Îý“± /test-9/909worldanyoneBOý“c~ˆ#Ïý“± /test-0/902worldanyoneBOý“c~~#Ðý“± /test-2/892worldanyoneBOý“c~w#Ñý“± /test-4/885worldanyoneBOý“baØ#Òý“³ /test-1/982worldanyoneBOý“cz€#Óý“µ /test-3/894worldanyoneBOý“cŠŒ#Ôý“¸ /test-8/906worldanyoneBOý“czƒ#Õý“¹ /test-5/897worldanyoneBOý“czƒ#Öý“¹ /test-7/897worldanyoneBOý“ba×#×ý“» /test-6/981worldanyoneBOý“cŠ#Øý“» /test-9/910worldanyoneBOý“c~‰#Ùý“» /test-0/903worldanyoneBOý“c~x#Úý“¼ /test-4/886worldanyoneBOý“c~#Ûý“¼ /test-2/893worldanyoneBOý“baÙ#Üý“¼ /test-1/983worldanyoneBOý“cz#Ýý“½ /test-3/895worldanyoneBOý“cŠ#Þý“À /test-8/907worldanyoneBOý“cz„#ßý“ /test-7/898worldanyoneBOý“cz„#àý“ /test-5/898worldanyoneBOý“baØ#áý“Æ /test-6/982worldanyoneBOý“baÚ#âý“Æ /test-1/984worldanyoneBOý“c~Š#ãý“Ç /test-0/904worldanyoneBOý“c~€#äý“Ç /test-2/894worldanyoneBOý“cŠ‘#åý“È /test-9/911worldanyoneBOý“cz‚#æý“È /test-3/896worldanyoneBOý“c~y#çý“È /test-4/887worldanyoneBOý“cŠŽ#èý“É /test-8/908worldanyoneBOý“cz…#éý“Ê /test-5/899worldanyoneBOý“cz…#êý“Ê /test-7/899worldanyoneBOý“baÙ#ëý“Ï /test-6/983worldanyoneBOý“baÛ#ìý“Ï /test-1/985worldanyoneBOý“c~z#íý“Ó /test-4/888worldanyoneBOý“cŠ’#îý“Ó /test-9/912worldanyoneBOý“cŠ#ïý“Ó /test-8/909worldanyoneBOý“cz†#ðý“Ô /test-7/900worldanyoneBOý“c~‹#ñý“Ô /test-0/905worldanyoneBOý“cz†#òý“Ô /test-5/900worldanyoneBOý“c~#óý“Ô /test-2/895worldanyoneBOý“czƒ#ôý“Ô /test-3/897worldanyoneBOý“baÜ#õý“× /test-1/986worldanyoneBOý“baÚ#öý“× /test-6/984worldanyoneBOý“c~{#÷ý“Ü /test-4/889worldanyoneBOý“cŠ“#øý“Ü /test-9/913worldanyoneBOý“cŠ#ùý“Ü /test-8/910worldanyoneBOý“cz‡#úý“Þ /test-5/901worldanyoneBOý“cz‡#ûý“ß /test-7/901worldanyoneBOý“c~Œ#üý“ß /test-0/906worldanyoneBOý“cz„#ýý“ß /test-3/898worldanyoneBOý“c~‚#þý“ß /test-2/896worldanyoneBOý“baÝ#ÿý“à /test-1/987worldanyoneBOý“baÛ$ý“à /test-6/985worldanyoneBOý“c~|$ý“ä /test-4/890worldanyoneBOý“cŠ‘$ý“å /test-8/911worldanyoneBOý“cŠ”$ý“å /test-9/914worldanyoneBOý“czˆ$ý“è /test-5/902worldanyoneBOý“baÞ$ý“é /test-1/988worldanyoneBOý“c~$ý“é /test-0/907worldanyoneBOý“c~ƒ$ý“é /test-2/897worldanyoneBOý“baÜ$ý“ê /test-6/986worldanyoneBOý“czˆ$ ý“ê /test-7/902worldanyoneBOý“cz…$ ý“ê /test-3/899worldanyoneBOý“c~}$ ý“ì /test-4/891worldanyoneBOý“cŠ•$ ý“î /test-9/915worldanyoneBOý“cŠ’$ ý“î /test-8/912worldanyoneBOý“cz‰$ý“ð /test-5/903worldanyoneBOý“baß$ý“ó /test-1/989worldanyoneBOý“baÝ$ý“ó /test-6/987worldanyoneBOý“c~Ž$ý“ô /test-0/908worldanyoneBOý“c~„$ý“ô /test-2/898worldanyoneBOý“cz‰$ý“ô /test-7/903worldanyoneBOý“cz†$ý“ô /test-3/900worldanyoneBOý“c~~$ý“õ /test-4/892worldanyoneBOý“cŠ–$ý“ö /test-9/916worldanyoneBOý“cŠ“$ý“÷ /test-8/913worldanyoneBOý“czŠ$ý“ù /test-5/904worldanyoneBOý“baà$ý“û /test-1/990worldanyoneBOý“baÞ$ý“û /test-6/988worldanyoneBOý“c~$ý“ž /test-4/893worldanyoneBOý“c~…$ý“ž /test-2/899worldanyoneBOý“c~$ý“ž /test-0/909worldanyoneBOý“cŠ”$ý“ž /test-8/914worldanyoneBOý“czŠ$ý“ž /test-7/904worldanyoneBOý“cŠ—$ ý“ž /test-9/917worldanyoneBOý“cz‡$!ý“ž /test-3/901worldanyoneBOý“cz‹$"ý“ž /test-5/905worldanyoneBOý“baá$#ý“ž /test-1/991worldanyoneBOý“baß$$ý“ž /test-6/989worldanyoneBOý“c~$%ý“ž  /test-0/910worldanyoneBOý“c~€$&ý“ž  /test-4/894worldanyoneBOý“c~†$'ý“ž  /test-2/900worldanyoneBOý“cŠ•$(ý“ž  /test-8/915worldanyoneBOý“cŠ˜$)ý“ž  /test-9/918worldanyoneBOý“cz‹$*ý“ž  /test-7/905worldanyoneBOý“czˆ$+ý“ž  /test-3/902worldanyoneBOý“czŒ$,ý“ž  /test-5/906worldanyoneBOý“baâ$-ý“ž  /test-1/992worldanyoneBOý“baà$.ý“ž  /test-6/990worldanyoneBOý“c~‘$/ý“ž /test-0/911worldanyoneBOý“c~$0ý“ž /test-4/895worldanyoneBOý“c~‡$1ý“ž /test-2/901worldanyoneBOý“baã$2ý“ž /test-1/993worldanyoneBOý“baá$3ý“ž /test-6/991worldanyoneBOý“cŠ–$4ý“ž /test-8/916worldanyoneBOý“czŒ$5ý“ž /test-7/906worldanyoneBOý“cz$6ý“ž /test-5/907worldanyoneBOý“cŠ™$7ý“ž /test-9/919worldanyoneBOý“cz‰$8ý“ž /test-3/903worldanyoneBOý“c~‚$9ý“ž /test-4/896worldanyoneBOý“c~ˆ$:ý“ž /test-2/902worldanyoneBOý“c~’$;ý“ž /test-0/912worldanyoneBOý“baâ$<ý“ž /test-6/992worldanyoneBOý“baä$=ý“ž /test-1/994worldanyoneBOý“cz$>ý“ž /test-7/907worldanyoneBOý“cŠ—$?ý“ž /test-8/917worldanyoneBOý“czŽ$@ý“ž  /test-5/908worldanyoneBOý“cŠš$Aý“ž  /test-9/920worldanyoneBOý“czŠ$Bý“ž  /test-3/904worldanyoneBOý“c~ƒ$Cý“ž# /test-4/897worldanyoneBOý“c~“$Dý“ž$ /test-0/913worldanyoneBOý“c~‰$Eý“ž$ /test-2/903worldanyoneBOý“baå$Fý“ž% /test-1/995worldanyoneBOý“baã$Gý“ž& /test-6/993worldanyoneBOý“cŠ˜$Hý“ž) /test-8/918worldanyoneBOý“cŠ›$Iý“ž) /test-9/921worldanyoneBOý“czŽ$Jý“ž) /test-7/908worldanyoneBOý“cz‹$Ký“ž) /test-3/905worldanyoneBOý“cz$Lý“ž* /test-5/909worldanyoneBOý“c~„$Mý“ž+ /test-4/898worldanyoneBOý“c~”$Ný“ž- /test-0/914worldanyoneBOý“c~Š$Oý“ž- /test-2/904worldanyoneBOý“baä$Pý“ž/ /test-6/994worldanyoneBOý“baæ$Qý“ž/ /test-1/996worldanyoneBOý“c~…$Rý“ž3 /test-4/899worldanyoneBOý“cŠ™$Sý“ž3 /test-8/919worldanyoneBOý“cŠœ$Tý“ž3 /test-9/922worldanyoneBOý“cz$Uý“ž3 /test-7/909worldanyoneBOý“cz$Vý“ž4 /test-5/910worldanyoneBOý“czŒ$Wý“ž4 /test-3/906worldanyoneBOý“c~•$Xý“ž5 /test-0/915worldanyoneBOý“c~‹$Yý“ž6 /test-2/905worldanyoneBOý“baç$Zý“ž9 /test-1/997worldanyoneBOý“baå$[ý“ž9 /test-6/995worldanyoneBOý“c~†$\ý“ž= /test-4/900worldanyoneBOý“cz$]ý“ž= /test-7/910worldanyoneBOý“cz‘$^ý“ž> /test-5/911worldanyoneBOý“cz$_ý“ž> /test-3/907worldanyoneBOý“cŠš$`ý“ž> /test-8/920worldanyoneBOý“cŠ$aý“ž> /test-9/923worldanyoneBOý“c~–$bý“ž> /test-0/916worldanyoneBOý“c~Œ$cý“ž? /test-2/906worldanyoneBOý“baè$dý“žB /test-1/998worldanyoneBOý“baæ$eý“žB /test-6/996worldanyoneBOý“c~‡$fý“žI /test-4/901worldanyoneBOý“c~$gý“žI /test-2/907worldanyoneBOý“cŠ›$hý“žI /test-8/921worldanyoneBOý“cŠž$iý“žI /test-9/924worldanyoneBOý“c~—$jý“žI /test-0/917worldanyoneBOý“cz‘$ký“žI /test-7/911worldanyoneBOý“cz’$lý“žI /test-5/912worldanyoneBOý“czŽ$mý“žJ /test-3/908worldanyoneBOý“baç$ný“žK /test-6/997worldanyoneBOý“baé$oý“žK /test-1/999worldanyoneB ý“baê$pý“žOÿÿÿõBOý“c~Ž$qý“žT /test-2/908worldanyoneBOý“c~ˆ$rý“žT /test-4/902worldanyoneBOý“baè$sý“žU /test-6/998worldanyoneBOý“c~˜$tý“žU /test-0/918worldanyoneBOý“cŠŸ$uý“žV /test-9/925worldanyoneBOý“cz“$vý“žV /test-5/913worldanyoneBOý“cz$wý“žV /test-3/909worldanyoneBOý“cz’$xý“žV /test-7/912worldanyoneBOý“cŠœ$yý“žV /test-8/922worldanyoneBOý“c~$zý“ž] /test-2/909worldanyoneBOý“baé${ý“ž` /test-6/999worldanyoneBOý“cŠ $|ý“ž` /test-9/926worldanyoneBOý“cŠ$}ý“ž` /test-8/923worldanyoneBOý“c~™$~ý“ž` /test-0/919worldanyoneBOý“c~‰$ý“ž` /test-4/903worldanyoneBOý“cz“$€ý“ža /test-7/913worldanyoneBOý“cz$ý“ža /test-3/910worldanyoneBOý“cz”$‚ý“ža /test-5/914worldanyoneB ý“baê$ƒý“ždÿÿÿõBOý“c~$„ý“žf /test-2/910worldanyoneBOý“c~š$…ý“žl /test-0/920worldanyoneBOý“c~Š$†ý“žl /test-4/904worldanyoneBOý“cŠ¡$‡ý“žl /test-9/927worldanyoneBOý“cŠž$ˆý“žl /test-8/924worldanyoneBOý“cz•$‰ý“žm /test-5/915worldanyoneBOý“cz”$Šý“žm /test-7/914worldanyoneBOý“cz‘$‹ý“žm /test-3/911worldanyoneBOý“c~‘$Œý“žo /test-2/911worldanyoneBOý“c~›$ý“žu /test-0/921worldanyoneBOý“c~‹$Žý“žu /test-4/905worldanyoneBOý“cŠ¢$ý“ž² /test-9/928worldanyoneBOý“cz’$ý“ž² /test-3/912worldanyoneBOý“cz–$‘ý“ž² /test-5/916worldanyoneBOý“cz•$’ý“ž² /test-7/915worldanyoneBOý“cŠŸ$“ý“ž² /test-8/925worldanyoneBOý“c~’$”ý“ž³ /test-2/912worldanyoneBOý“c~œ$•ý“ž¸ /test-0/922worldanyoneBOý“c~Œ$–ý“ž¸ /test-4/906worldanyoneBOý“cŠ£$—ý“ž¼ /test-9/929worldanyoneBOý“cz“$˜ý“ž¼ /test-3/913worldanyoneBOý“cŠ $™ý“ž¼ /test-8/926worldanyoneBOý“c~“$šý“ž¼ /test-2/913worldanyoneBOý“cz—$›ý“ž¼ /test-5/917worldanyoneBOý“cz–$œý“ž½ /test-7/916worldanyoneBOý“c~$ý“žÁ /test-0/923worldanyoneBOý“c~$žý“žÁ /test-4/907worldanyoneBOý“cŠ¡$Ÿý“žÆ /test-8/927worldanyoneBOý“c~”$ ý“žÆ /test-2/914worldanyoneBOý“cz˜$¡ý“žÆ /test-5/918worldanyoneBOý“cz—$¢ý“žÆ /test-7/917worldanyoneBOý“cz”$£ý“žÆ /test-3/914worldanyoneBOý“cФ$¤ý“žÇ /test-9/930worldanyoneBOý“c~ž$¥ý“žÊ /test-0/924worldanyoneBOý“c~Ž$¦ý“žÊ /test-4/908worldanyoneBOý“c~•$§ý“žÐ /test-2/915worldanyoneBOý“cz˜$¨ý“žÐ /test-7/918worldanyoneBOý“cz•$©ý“žÐ /test-3/915worldanyoneBOý“cz™$ªý“žÐ /test-5/919worldanyoneBOý“cŠ¢$«ý“žÑ /test-8/928worldanyoneBOý“cŠ¥$¬ý“žÑ /test-9/931worldanyoneBOý“c~$­ý“žÒ /test-4/909worldanyoneBOý“c~Ÿ$®ý“žÒ /test-0/925worldanyoneBOý“c~–$¯ý“žÙ /test-2/916worldanyoneBOý“cЦ$°ý“žÜ /test-9/932worldanyoneBOý“cŠ£$±ý“žÜ /test-8/929worldanyoneBOý“c~ $²ý“žÜ /test-0/926worldanyoneBOý“c~$³ý“žÜ /test-4/910worldanyoneBOý“cz™$´ý“žÜ /test-7/919worldanyoneBOý“czš$µý“žÜ /test-5/920worldanyoneBOý“cz–$¶ý“žÝ /test-3/916worldanyoneBOý“c~—$·ý“žá /test-2/917worldanyoneBOý“czš$¸ý“žæ /test-7/920worldanyoneBOý“cЧ$¹ý“žæ /test-9/933worldanyoneBOý“c~‘$ºý“žç /test-4/911worldanyoneBOý“cФ$»ý“žç /test-8/930worldanyoneBOý“cz—$¼ý“žç /test-3/917worldanyoneBOý“cz›$½ý“žç /test-5/921worldanyoneBOý“c~¡$¾ý“žç /test-0/927worldanyoneBOý“c~˜$¿ý“žé /test-2/918worldanyoneBOý“cz›$Àý“žï /test-7/921worldanyoneBOý“cЍ$Áý“žò /test-9/934worldanyoneBOý“c~’$Âý“žó /test-4/912worldanyoneBOý“c~™$Ãý“žó /test-2/919worldanyoneBOý“cŠ¥$Äý“žó /test-8/931worldanyoneBOý“c~¢$Åý“žó /test-0/928worldanyoneBOý“cz˜$Æý“žó /test-3/918worldanyoneBOý“czœ$Çý“žô /test-5/922worldanyoneBOý“czœ$Èý“ž÷ /test-7/922worldanyoneBOý“cŠ©$Éý“žú /test-9/935worldanyoneBOý“c~£$Êý“žý /test-0/929worldanyoneBOý“cЦ$Ëý“žý /test-8/932worldanyoneBOý“c~“$Ìý“žý /test-4/913worldanyoneBOý“cz™$Íý“žý /test-3/919worldanyoneBOý“cz$Îý“žý /test-5/923worldanyoneBOý“c~š$Ïý“žþ /test-2/920worldanyoneBOý“cz$Ðý“žÿ /test-7/923worldanyoneBOý“cŠª$Ñý“Ÿ /test-9/936worldanyoneBOý“c~¤$Òý“Ÿ /test-0/930worldanyoneBOý“c~”$Óý“Ÿ  /test-4/914worldanyoneBOý“cЧ$Ôý“Ÿ  /test-8/933worldanyoneBOý“czž$Õý“Ÿ  /test-7/924worldanyoneBOý“czž$Öý“Ÿ  /test-5/924worldanyoneBOý“czš$×ý“Ÿ  /test-3/920worldanyoneBOý“c~›$Øý“Ÿ  /test-2/921worldanyoneBOý“cŠ«$Ùý“Ÿ  /test-9/937worldanyoneBOý“c~¥$Úý“Ÿ /test-0/931worldanyoneBOý“c~•$Ûý“Ÿ /test-4/915worldanyoneBOý“c~œ$Üý“Ÿ /test-2/922worldanyoneBOý“cЍ$Ýý“Ÿ /test-8/934worldanyoneBOý“cЬ$Þý“Ÿ /test-9/938worldanyoneBOý“czŸ$ßý“Ÿ /test-7/925worldanyoneBOý“czŸ$àý“Ÿ /test-5/925worldanyoneBOý“cz›$áý“Ÿ /test-3/921worldanyoneBOý“c~¦$âý“Ÿ /test-0/932worldanyoneBOý“cz $ãý“Ÿ /test-5/926worldanyoneBOý“c~–$äý“Ÿ /test-4/916worldanyoneBOý“czœ$åý“Ÿ /test-3/922worldanyoneBOý“cz $æý“Ÿ /test-7/926worldanyoneBOý“cŠ­$çý“Ÿ /test-9/939worldanyoneBOý“cŠ©$èý“Ÿ /test-8/935worldanyoneBOý“c~$éý“Ÿ /test-2/923worldanyoneBOý“c~§$êý“Ÿ /test-0/933worldanyoneBOý“cŠª$ëý“Ÿ( /test-8/936worldanyoneBOý“cz$ìý“Ÿ( /test-3/923worldanyoneBOý“cz¡$íý“Ÿ( /test-5/927worldanyoneBOý“cŠ®$îý“Ÿ) /test-9/940worldanyoneBOý“cz¡$ïý“Ÿ) /test-7/927worldanyoneBOý“c~ž$ðý“Ÿ) /test-2/924worldanyoneBOý“c~—$ñý“Ÿ* /test-4/917worldanyoneBOý“c~¨$òý“Ÿ* /test-0/934worldanyoneBOý“cŠ«$óý“Ÿ1 /test-8/937worldanyoneBOý“czž$ôý“Ÿ1 /test-3/924worldanyoneBOý“cz¢$õý“Ÿ1 /test-5/928worldanyoneBOý“cz¢$öý“Ÿ4 /test-7/928worldanyoneBOý“c~˜$÷ý“Ÿ4 /test-4/918worldanyoneBOý“c~Ÿ$øý“Ÿ4 /test-2/925worldanyoneBOý“c~©$ùý“Ÿ4 /test-0/935worldanyoneBOý“cН$úý“Ÿ4 /test-9/941worldanyoneBOý“czŸ$ûý“Ÿ: /test-3/925worldanyoneBOý“cЬ$üý“Ÿ: /test-8/938worldanyoneBOý“cz£$ýý“Ÿ: /test-5/929worldanyoneBOý“c~™$þý“Ÿ= /test-4/919worldanyoneBOý“c~ª$ÿý“Ÿ= /test-0/936worldanyoneBOý“cz£%ý“Ÿ= /test-7/929worldanyoneBOý“c~ %ý“Ÿ= /test-2/926worldanyoneBOý“cа%ý“Ÿ= /test-9/942worldanyoneBOý“cz %ý“ŸB /test-3/926worldanyoneBOý“cz¤%ý“ŸC /test-5/930worldanyoneBOý“cŠ­%ý“ŸC /test-8/939worldanyoneBOý“cz¤%ý“ŸF /test-7/930worldanyoneBOý“c~š%ý“ŸF /test-4/920worldanyoneBOý“c~«%ý“ŸF /test-0/937worldanyoneBOý“cб% ý“ŸF /test-9/943worldanyoneBOý“c~¡% ý“ŸF /test-2/927worldanyoneBOý“cz¡% ý“ŸJ /test-3/927worldanyoneBOý“cz¥% ý“ŸK /test-5/931worldanyoneBOý“cŠ®% ý“ŸK /test-8/940worldanyoneBOý“c~›%ý“ŸP /test-4/921worldanyoneBOý“cz¥%ý“ŸP /test-7/931worldanyoneBOý“c~¢%ý“ŸP /test-2/928worldanyoneBOý“c~¬%ý“ŸP /test-0/938worldanyoneBOý“cв%ý“ŸP /test-9/944worldanyoneBOý“cz¢%ý“ŸR /test-3/928worldanyoneBOý“cz¦%ý“ŸT /test-5/932worldanyoneBOý“cН%ý“ŸT /test-8/941worldanyoneBOý“c~œ%ý“ŸY /test-4/922worldanyoneBOý“c~£%ý“ŸZ /test-2/929worldanyoneBOý“c~­%ý“ŸZ /test-0/939worldanyoneBOý“cz¦%ý“ŸZ /test-7/932worldanyoneBOý“cг%ý“ŸZ /test-9/945worldanyoneBOý“cz£%ý“Ÿ[ /test-3/929worldanyoneBOý“cz§%ý“Ÿ] /test-5/933worldanyoneBOý“cа%ý“Ÿ] /test-8/942worldanyoneBOý“c~%ý“Ÿa /test-4/923worldanyoneBOý“c~¤%ý“Ÿd /test-2/930worldanyoneBOý“c~®% ý“Ÿd /test-0/940worldanyoneBOý“cz¤%!ý“Ÿd /test-3/930worldanyoneBOý“cz§%"ý“Ÿd /test-7/933worldanyoneBOý“cŠ´%#ý“Ÿe /test-9/946worldanyoneBOý“cz¨%$ý“Ÿf /test-5/934worldanyoneBOý“cб%%ý“Ÿf /test-8/943worldanyoneBOý“c~ž%&ý“Ÿi /test-4/924worldanyoneBOý“cz¨%'ý“Ÿn /test-7/934worldanyoneBOý“cе%(ý“Ÿn /test-9/947worldanyoneBOý“c~¥%)ý“Ÿn /test-2/931worldanyoneBOý“c~¯%*ý“Ÿn /test-0/941worldanyoneBOý“cz¥%+ý“Ÿn /test-3/931worldanyoneBOý“cz©%,ý“Ÿo /test-5/935worldanyoneBOý“cв%-ý“Ÿp /test-8/944worldanyoneBOý“c~Ÿ%.ý“Ÿq /test-4/925worldanyoneBOý“c~¦%/ý“Ÿy /test-2/932worldanyoneBOý“czª%0ý“Ÿy /test-5/936worldanyoneBOý“cz¦%1ý“Ÿy /test-3/932worldanyoneBOý“cz©%2ý“Ÿy /test-7/935worldanyoneBOý“cг%3ý“Ÿy /test-8/945worldanyoneBOý“cж%4ý“Ÿy /test-9/948worldanyoneBOý“c~°%5ý“Ÿy /test-0/942worldanyoneBOý“c~ %6ý“Ÿz /test-4/926worldanyoneBOý“c~§%7ý“Ÿ‚ /test-2/933worldanyoneBOý“cŠ´%8ý“Ÿƒ /test-8/946worldanyoneBOý“c~±%9ý“Ÿƒ /test-0/943worldanyoneBOý“cŠ·%:ý“Ÿƒ /test-9/949worldanyoneBOý“cz§%;ý“Ÿƒ /test-3/933worldanyoneBOý“c~¡%<ý“Ÿƒ /test-4/927worldanyoneBOý“czª%=ý“Ÿ„ /test-7/936worldanyoneBOý“cz«%>ý“Ÿ„ /test-5/937worldanyoneBOý“c~¨%?ý“Ÿ‹ /test-2/934worldanyoneBOý“cЏ%@ý“ŸŽ /test-9/950worldanyoneBOý“cе%Aý“ŸŽ /test-8/947worldanyoneBOý“cz«%Bý“ŸŽ /test-7/937worldanyoneBOý“cz¨%Cý“Ÿ /test-3/934worldanyoneBOý“cz¬%Dý“Ÿ /test-5/938worldanyoneBOý“c~²%Eý“Ÿ /test-0/944worldanyoneBOý“c~¢%Fý“Ÿ /test-4/928worldanyoneBOý“c~©%Gý“Ÿ” /test-2/935worldanyoneBOý“cй%Hý“Ÿ™ /test-9/951worldanyoneBOý“cz¬%Iý“Ÿš /test-7/938worldanyoneBOý“cz­%Jý“Ÿš /test-5/939worldanyoneBOý“c~³%Ký“Ÿš /test-0/945worldanyoneBOý“c~£%Lý“Ÿš /test-4/929worldanyoneBOý“cж%Mý“Ÿš /test-8/948worldanyoneBOý“cz©%Ný“Ÿ› /test-3/935worldanyoneBOý“c~ª%Oý“Ÿœ /test-2/936worldanyoneBOý“cŠº%Pý“Ÿ¢ /test-9/952worldanyoneBOý“cŠ·%Qý“Ÿ¤ /test-8/949worldanyoneBOý“cz­%Rý“Ÿ¥ /test-7/939worldanyoneBOý“cz®%Sý“Ÿ¥ /test-5/940worldanyoneBOý“czª%Tý“Ÿ¥ /test-3/936worldanyoneBOý“c~´%Uý“Ÿ¦ /test-0/946worldanyoneBOý“c~¤%Vý“Ÿ¦ /test-4/930worldanyoneBOý“c~«%Wý“Ÿ§ /test-2/937worldanyoneBOý“cŠ»%Xý“Ÿª /test-9/953worldanyoneBOý“cЏ%Yý“Ÿ­ /test-8/950worldanyoneBOý“cz®%Zý“Ÿ° /test-7/940worldanyoneBOý“c~¥%[ý“Ÿ° /test-4/931worldanyoneBOý“cz¯%\ý“Ÿ± /test-5/941worldanyoneBOý“cz«%]ý“Ÿ² /test-3/937worldanyoneBOý“c~¬%^ý“Ÿ² /test-2/938worldanyoneBOý“c~µ%_ý“Ÿ² /test-0/947worldanyoneBOý“cм%`ý“Ÿ³ /test-9/954worldanyoneBOý“cй%aý“Ÿµ /test-8/951worldanyoneBOý“cz¯%bý“Ÿ¸ /test-7/941worldanyoneBOý“c~¦%cý“Ÿº /test-4/932worldanyoneBOý“cн%dý“Ÿ½ /test-9/955worldanyoneBOý“c~¶%eý“Ÿ½ /test-0/948worldanyoneBOý“cz¬%fý“Ÿ½ /test-3/938worldanyoneBOý“cz°%gý“Ÿ½ /test-5/942worldanyoneBOý“c~­%hý“Ÿ¾ /test-2/939worldanyoneBOý“cŠº%iý“Ÿ¾ /test-8/952worldanyoneBOý“cz°%jý“ŸÀ /test-7/942worldanyoneBOý“c~§%ký“ŸÂ /test-4/933worldanyoneBOý“c~®%lý“ŸÈ /test-2/940worldanyoneBOý“cо%mý“ŸÈ /test-9/956worldanyoneBOý“cz­%ný“ŸÈ /test-3/939worldanyoneBOý“cz±%oý“ŸÈ /test-5/943worldanyoneBOý“c~·%pý“ŸÉ /test-0/949worldanyoneBOý“cz±%qý“ŸÉ /test-7/943worldanyoneBOý“cŠ»%rý“ŸÉ /test-8/953worldanyoneBOý“c~¨%sý“ŸÌ /test-4/934worldanyoneBOý“cŠ¿%tý“ŸÒ /test-9/957worldanyoneBOý“c~¯%uý“ŸÒ /test-2/941worldanyoneBOý“cz®%vý“ŸÒ /test-3/940worldanyoneBOý“cz²%wý“ŸÒ /test-5/944worldanyoneBOý“cz²%xý“ŸÓ /test-7/944worldanyoneBOý“cм%yý“ŸÓ /test-8/954worldanyoneBOý“c~¸%zý“ŸÔ /test-0/950worldanyoneBOý“c~©%{ý“ŸÕ /test-4/935worldanyoneBOý“cŠÀ%|ý“ŸÚ /test-9/958worldanyoneBOý“cz³%}ý“ŸÞ /test-5/945worldanyoneBOý“cz¯%~ý“ŸÞ /test-3/941worldanyoneBOý“c~°%ý“ŸÞ /test-2/942worldanyoneBOý“c~¹%€ý“Ÿß /test-0/951worldanyoneBOý“c~ª%ý“Ÿß /test-4/936worldanyoneBOý“cz³%‚ý“Ÿß /test-7/945worldanyoneBOý“cн%ƒý“Ÿà /test-8/955worldanyoneBOý“cŠÁ%„ý“Ÿâ /test-9/959worldanyoneBOý“cz°%…ý“Ÿç /test-3/942worldanyoneBOý“cz´%†ý“Ÿç /test-5/946worldanyoneBOý“cz´%‡ý“Ÿê /test-7/946worldanyoneBOý“c~±%ˆý“Ÿê /test-2/943worldanyoneBOý“c~«%‰ý“Ÿê /test-4/937worldanyoneBOý“c~º%Šý“Ÿê /test-0/952worldanyoneBOý“cо%‹ý“Ÿë /test-8/956worldanyoneBOý“cŠÂ%Œý“Ÿë /test-9/960worldanyoneBOý“cz±%ý“Ÿï /test-3/943worldanyoneBOý“czµ%Žý“Ÿð /test-5/947worldanyoneBOý“czµ%ý“Ÿó /test-7/947worldanyoneBOý“cŠÃ%ý“Ÿö /test-9/961worldanyoneBOý“c~²%‘ý“Ÿö /test-2/944worldanyoneBOý“c~¬%’ý“Ÿ÷ /test-4/938worldanyoneBOý“c~»%“ý“Ÿ÷ /test-0/953worldanyoneBOý“cŠ¿%”ý“Ÿ÷ /test-8/957worldanyoneBOý“cz²%•ý“Ÿø /test-3/944worldanyoneBOý“cz¶%–ý“Ÿø /test-5/948worldanyoneBOý“cz¶%—ý“Ÿü /test-7/948worldanyoneBOý“cŠÄ%˜ý“Ÿÿ /test-9/962worldanyoneBOý“c~³%™ý“  /test-2/945worldanyoneBOý“cz³%šý“  /test-3/945worldanyoneBOý“c~­%›ý“  /test-4/939worldanyoneBOý“c~¼%œý“  /test-0/954worldanyoneBOý“cz·%ý“  /test-5/949worldanyoneBOý“cŠÀ%žý“  /test-8/958worldanyoneBOý“cz·%Ÿý“  /test-7/949worldanyoneBOý“cŠÅ% ý“  /test-9/963worldanyoneBOý“c~´%¡ý“   /test-2/946worldanyoneBOý“cz´%¢ý“  /test-3/946worldanyoneBOý“cz¸%£ý“  /test-5/950worldanyoneBOý“c~½%¤ý“  /test-0/955worldanyoneBOý“c~®%¥ý“  /test-4/940worldanyoneBOý“cŠÁ%¦ý“  /test-8/959worldanyoneBOý“cz¸%§ý“  /test-7/950worldanyoneBOý“cŠÆ%¨ý“  /test-9/964worldanyoneBOý“c~µ%©ý“  /test-2/947worldanyoneBOý“czµ%ªý“  /test-3/947worldanyoneBOý“cŠÇ%«ý“  /test-9/965worldanyoneBOý“c~¯%¬ý“  /test-4/941worldanyoneBOý“c~¾%­ý“  /test-0/956worldanyoneBOý“cŠÂ%®ý“  /test-8/960worldanyoneBOý“cz¹%¯ý“  /test-7/951worldanyoneBOý“c~¶%°ý“  /test-2/948worldanyoneBOý“cz¹%±ý“  /test-5/951worldanyoneBOý“cz¶%²ý“ # /test-3/948worldanyoneBOý“cŠÈ%³ý“ % /test-9/966worldanyoneBOý“cŠÃ%´ý“ ' /test-8/961worldanyoneBOý“c~·%µý“ ' /test-2/949worldanyoneBOý“c~¿%¶ý“ ' /test-0/957worldanyoneBOý“czº%·ý“ ' /test-7/952worldanyoneBOý“czº%¸ý“ ' /test-5/952worldanyoneBOý“c~°%¹ý“ ( /test-4/942worldanyoneBOý“cz·%ºý“ + /test-3/949worldanyoneBOý“cŠÉ%»ý“ - /test-9/967worldanyoneBOý“c~¸%¼ý“ 1 /test-2/950worldanyoneBOý“c~À%½ý“ 1 /test-0/958worldanyoneBOý“cŠÄ%¾ý“ 1 /test-8/962worldanyoneBOý“cz»%¿ý“ 2 /test-7/953worldanyoneBOý“cz»%Àý“ 2 /test-5/953worldanyoneBOý“c~±%Áý“ 2 /test-4/943worldanyoneBOý“cz¸%Âý“ 4 /test-3/950worldanyoneBOý“cŠÊ%Ãý“ 5 /test-9/968worldanyoneBOý“c~¹%Äý“ 9 /test-2/951worldanyoneBOý“c~Á%Åý“ ; /test-0/959worldanyoneBOý“cz¼%Æý“ ; /test-7/954worldanyoneBOý“cŠÅ%Çý“ ; /test-8/963worldanyoneBOý“cz¼%Èý“ < /test-5/954worldanyoneBOý“c~²%Éý“ = /test-4/944worldanyoneBOý“cŠË%Êý“ = /test-9/969worldanyoneBOý“cz¹%Ëý“ = /test-3/951worldanyoneBOý“c~º%Ìý“ B /test-2/952worldanyoneBOý“cz½%Íý“ F /test-7/955worldanyoneBOý“c~Â%Îý“ F /test-0/960worldanyoneBOý“cŠÆ%Ïý“ F /test-8/964worldanyoneBOý“cz½%Ðý“ F /test-5/955worldanyoneBOý“cŠÌ%Ñý“ G /test-9/970worldanyoneBOý“czº%Òý“ G /test-3/952worldanyoneBOý“c~³%Óý“ G /test-4/945worldanyoneBOý“c~»%Ôý“ J /test-2/953worldanyoneBOý“cz»%Õý“ Q /test-3/953worldanyoneBOý“cŠÇ%Öý“ Q /test-8/965worldanyoneBOý“cŠÍ%×ý“ Q /test-9/971worldanyoneBOý“cz¾%Øý“ Q /test-7/956worldanyoneBOý“cz¾%Ùý“ Q /test-5/956worldanyoneBOý“c~Ã%Úý“ Q /test-0/961worldanyoneBOý“c~´%Ûý“ Q /test-4/946worldanyoneBOý“c~¼%Üý“ S /test-2/954worldanyoneBOý“cz¼%Ýý“ Y /test-3/954worldanyoneBOý“c~½%Þý“ \ /test-2/955worldanyoneBOý“c~µ%ßý“ \ /test-4/947worldanyoneBOý“c~Ä%àý“ \ /test-0/962worldanyoneBOý“cŠÎ%áý“ \ /test-9/972worldanyoneBOý“cŠÈ%âý“ \ /test-8/966worldanyoneBOý“cz¿%ãý“ ] /test-5/957worldanyoneBOý“cz¿%äý“ ] /test-7/957worldanyoneBOý“cz½%åý“ a /test-3/955worldanyoneBOý“c~¾%æý“ c /test-2/956worldanyoneBOý“cŠÉ%çý“ f /test-8/967worldanyoneBOý“cŠÏ%èý“ f /test-9/973worldanyoneBOý“czÀ%éý“ f /test-7/958worldanyoneBOý“c~¶%êý“ f /test-4/948worldanyoneBOý“c~Å%ëý“ f /test-0/963worldanyoneBOý“czÀ%ìý“ f /test-5/958worldanyoneBOý“cz¾%íý“ j /test-3/956worldanyoneBOý“c~¿%îý“ k /test-2/957worldanyoneBOý“cŠÊ%ïý“ m /test-8/968worldanyoneBOý“cŠÐ%ðý“ p /test-9/974worldanyoneBOý“czÁ%ñý“ p /test-5/959worldanyoneBOý“czÁ%òý“ p /test-7/959worldanyoneBOý“c~·%óý“ p /test-4/949worldanyoneBOý“c~Æ%ôý“ q /test-0/964worldanyoneBOý“cz¿%õý“ s /test-3/957worldanyoneBOý“c~À%öý“ t /test-2/958worldanyoneBOý“cŠË%÷ý“ v /test-8/969worldanyoneBOý“cŠÑ%øý“ x /test-9/975worldanyoneBOý“c~Ç%ùý“ z /test-0/965worldanyoneBOý“czÂ%úý“ z /test-7/960worldanyoneBOý“czÂ%ûý“ z /test-5/960worldanyoneBOý“c~¸%üý“ z /test-4/950worldanyoneBOý“czÀ%ýý“ { /test-3/958worldanyoneBOý“c~Á%þý“ { /test-2/959worldanyoneBOý“cŠÌ%ÿý“ } /test-8/970worldanyoneBOý“cŠÒ&ý“  /test-9/976worldanyoneBOý“c~È&ý“ … /test-0/966worldanyoneBOý“c~Â&ý“ … /test-2/960worldanyoneBOý“c~¹&ý“ … /test-4/951worldanyoneBOý“czÃ&ý“ … /test-7/961worldanyoneBOý“czÁ&ý“ … /test-3/959worldanyoneBOý“czÃ&ý“ … /test-5/961worldanyoneBOý“cŠÍ&ý“ † /test-8/971worldanyoneBOý“cŠÓ&ý“ Š /test-9/977worldanyoneBOý“cŠÎ& ý“  /test-8/972worldanyoneBOý“czÂ& ý“  /test-3/960worldanyoneBOý“czÄ& ý“  /test-7/962worldanyoneBOý“czÄ& ý“  /test-5/962worldanyoneBOý“c~Ã& ý“  /test-2/961worldanyoneBOý“c~º&ý“  /test-4/952worldanyoneBOý“c~É&ý“  /test-0/967worldanyoneBOý“cŠÔ&ý“ ’ /test-9/978worldanyoneBOý“czÃ&ý“ › /test-3/961worldanyoneBOý“cŠÏ&ý“ › /test-8/973worldanyoneBOý“cŠÕ&ý“ › /test-9/979worldanyoneBOý“c~Ê&ý“ œ /test-0/968worldanyoneBOý“c~Ä&ý“ œ /test-2/962worldanyoneBOý“c~»&ý“ œ /test-4/953worldanyoneBOý“czÅ&ý“ œ /test-5/963worldanyoneBOý“czÅ&ý“ œ /test-7/963worldanyoneBOý“cŠÖ&ý“ ¤ /test-9/980worldanyoneBOý“cŠÐ&ý“ ¤ /test-8/974worldanyoneBOý“czÄ&ý“ ¤ /test-3/962worldanyoneBOý“czÆ&ý“ ¦ /test-7/964worldanyoneBOý“czÆ&ý“ ¦ /test-5/964worldanyoneBOý“c~Ë&ý“ ¦ /test-0/969worldanyoneBOý“c~¼&ý“ ¦ /test-4/954worldanyoneBOý“c~Å& ý“ ¦ /test-2/963worldanyoneBOý“cŠ×&!ý“ « /test-9/981worldanyoneBOý“cŠÑ&"ý“ ¬ /test-8/975worldanyoneBOý“czÅ&#ý“ ­ /test-3/963worldanyoneBOý“czÇ&$ý“ ° /test-5/965worldanyoneBOý“c~Æ&%ý“ ± /test-2/964worldanyoneBOý“c~Ì&&ý“ ± /test-0/970worldanyoneBOý“c~½&'ý“ ± /test-4/955worldanyoneBOý“czÇ&(ý“ ± /test-7/965worldanyoneBOý“cŠØ&)ý“ ´ /test-9/982worldanyoneBOý“cŠÒ&*ý“ µ /test-8/976worldanyoneBOý“czÆ&+ý“ ¶ /test-3/964worldanyoneBOý“czÈ&,ý“ · /test-5/966worldanyoneBOý“c~Ç&-ý“ º /test-2/965worldanyoneBOý“c~¾&.ý“ » /test-4/956worldanyoneBOý“c~Í&/ý“ » /test-0/971worldanyoneBOý“czÈ&0ý“ » /test-7/966worldanyoneBOý“cŠÙ&1ý“ ¼ /test-9/983worldanyoneBOý“czÇ&2ý“ ¾ /test-3/965worldanyoneBOý“cŠÓ&3ý“ ¾ /test-8/977worldanyoneBOý“czÉ&4ý“ À /test-5/967worldanyoneBOý“c~È&5ý“ Â /test-2/966worldanyoneBOý“czÉ&6ý“ Ä /test-7/967worldanyoneBOý“c~¿&7ý“ Ä /test-4/957worldanyoneBOý“c~Î&8ý“ Ä /test-0/972worldanyoneBOý“cŠÚ&9ý“ Å /test-9/984worldanyoneBOý“czÈ&:ý“ Æ /test-3/966worldanyoneBOý“cŠÔ&;ý“ Æ /test-8/978worldanyoneBOý“czÊ&<ý“ É /test-5/968worldanyoneBOý“c~É&=ý“ Ë /test-2/967worldanyoneBOý“czÊ&>ý“ Î /test-7/968worldanyoneBOý“c~À&?ý“ Î /test-4/958worldanyoneBOý“c~Ï&@ý“ Î /test-0/973worldanyoneBOý“cŠÛ&Aý“ Î /test-9/985worldanyoneBOý“cŠÕ&Bý“ Ð /test-8/979worldanyoneBOý“czÉ&Cý“ Ð /test-3/967worldanyoneBOý“czË&Dý“ Ñ /test-5/969worldanyoneBOý“c~Ê&Eý“ Ô /test-2/968worldanyoneBOý“cŠÜ&Fý“ Ø /test-9/986worldanyoneBOý“czË&Gý“ Ø /test-7/969worldanyoneBOý“c~Ð&Hý“ Ø /test-0/974worldanyoneBOý“c~Á&Iý“ Ø /test-4/959worldanyoneBOý“czÊ&Jý“ Ù /test-3/968worldanyoneBOý“cŠÖ&Ký“ Ù /test-8/980worldanyoneBOý“czÌ&Lý“ Ù /test-5/970worldanyoneBOý“c~Ë&Mý“ Ü /test-2/969worldanyoneBOý“cŠÝ&Ný“ á /test-9/987worldanyoneBOý“czÌ&Oý“ á /test-7/970worldanyoneBOý“cŠ×&Pý“ ã /test-8/981worldanyoneBOý“czÍ&Qý“ ã /test-5/971worldanyoneBOý“czË&Rý“ ã /test-3/969worldanyoneBOý“c~Ñ&Sý“ ã /test-0/975worldanyoneBOý“c~Â&Tý“ ã /test-4/960worldanyoneBOý“c~Ì&Uý“ å /test-2/970worldanyoneBOý“cŠÞ&Vý“ è /test-9/988worldanyoneBOý“cŠØ&Wý“ ì /test-8/982worldanyoneBOý“c~Ò&Xý“ í /test-0/976worldanyoneBOý“czÍ&Yý“ î /test-7/971worldanyoneBOý“czÌ&Zý“ î /test-3/970worldanyoneBOý“czÎ&[ý“ î /test-5/972worldanyoneBOý“c~Ã&\ý“ î /test-4/961worldanyoneBOý“c~Í&]ý“ î /test-2/971worldanyoneBOý“cŠß&^ý“ ñ /test-9/989worldanyoneBOý“cŠÙ&_ý“ ö /test-8/983worldanyoneBOý“c~Ó&`ý“ ö /test-0/977worldanyoneBOý“czÍ&aý“ ø /test-3/971worldanyoneBOý“c~Ä&bý“ ø /test-4/962worldanyoneBOý“c~Î&cý“ ø /test-2/972worldanyoneBOý“czÏ&dý“ ù /test-5/973worldanyoneBOý“cŠà&eý“ ù /test-9/990worldanyoneBOý“czÎ&fý“ ù /test-7/972worldanyoneBOý“c~Ô&gý“ ý /test-0/978worldanyoneBOý“cŠÚ&hý“ þ /test-8/984worldanyoneBOý“c~Ï&iý“¡ /test-2/973worldanyoneBOý“c~Å&jý“¡ /test-4/963worldanyoneBOý“cŠá&ký“¡ /test-9/991worldanyoneBOý“czÏ&lý“¡ /test-7/973worldanyoneBOý“czÐ&mý“¡ /test-5/974worldanyoneBOý“czÎ&ný“¡ /test-3/972worldanyoneBOý“c~Õ&oý“¡ /test-0/979worldanyoneBOý“cŠÛ&pý“¡ /test-8/985worldanyoneBOý“c~Ð&qý“¡  /test-2/974worldanyoneBOý“cŠâ&rý“¡  /test-9/992worldanyoneBOý“czÏ&sý“¡  /test-3/973worldanyoneBOý“czÐ&tý“¡  /test-7/974worldanyoneBOý“czÑ&uý“¡  /test-5/975worldanyoneBOý“c~Æ&vý“¡  /test-4/964worldanyoneBOý“c~Ö&wý“¡ /test-0/980worldanyoneBOý“cŠÜ&xý“¡ /test-8/986worldanyoneBOý“c~Ñ&yý“¡ /test-2/975worldanyoneBOý“c~×&zý“¡ /test-0/981worldanyoneBOý“czÑ&{ý“¡ /test-7/975worldanyoneBOý“c~Ç&|ý“¡ /test-4/965worldanyoneBOý“cŠã&}ý“¡ /test-9/993worldanyoneBOý“czÐ&~ý“¡ /test-3/974worldanyoneBOý“czÒ&ý“¡ /test-5/976worldanyoneBOý“cŠÝ&€ý“¡ /test-8/987worldanyoneBOý“c~Ò&ý“¡ /test-2/976worldanyoneBOý“c~È&‚ý“¡" /test-4/966worldanyoneBOý“c~Ø&ƒý“¡" /test-0/982worldanyoneBOý“cŠä&„ý“¡" /test-9/994worldanyoneBOý“cŠÞ&…ý“¡" /test-8/988worldanyoneBOý“czÓ&†ý“¡" /test-5/977worldanyoneBOý“czÑ&‡ý“¡" /test-3/975worldanyoneBOý“czÒ&ˆý“¡" /test-7/976worldanyoneBOý“c~Ó&‰ý“¡$ /test-2/977worldanyoneBOý“c~É&Šý“¡* /test-4/967worldanyoneBOý“c~Ù&‹ý“¡, /test-0/983worldanyoneBOý“cŠå&Œý“¡, /test-9/995worldanyoneBOý“cŠß&ý“¡, /test-8/989worldanyoneBOý“czÒ&Žý“¡- /test-3/976worldanyoneBOý“czÓ&ý“¡- /test-7/977worldanyoneBOý“czÔ&ý“¡- /test-5/978worldanyoneBOý“c~Ô&‘ý“¡. /test-2/978worldanyoneBOý“c~Ê&’ý“¡2 /test-4/968worldanyoneBOý“cŠà&“ý“¡6 /test-8/990worldanyoneBOý“c~Ú&”ý“¡6 /test-0/984worldanyoneBOý“cŠæ&•ý“¡6 /test-9/996worldanyoneBOý“c~Õ&–ý“¡7 /test-2/979worldanyoneBOý“czÓ&—ý“¡7 /test-3/977worldanyoneBOý“czÕ&˜ý“¡7 /test-5/979worldanyoneBOý“czÔ&™ý“¡7 /test-7/978worldanyoneBOý“c~Ë&šý“¡: /test-4/969worldanyoneBOý“c~Û&›ý“¡? /test-0/985worldanyoneBOý“cŠá&œý“¡@ /test-8/991worldanyoneBOý“cŠç&ý“¡@ /test-9/997worldanyoneBOý“czÕ&žý“¡A /test-7/979worldanyoneBOý“c~Ö&Ÿý“¡A /test-2/980worldanyoneBOý“czÖ& ý“¡A /test-5/980worldanyoneBOý“czÔ&¡ý“¡A /test-3/978worldanyoneBOý“c~Ì&¢ý“¡C /test-4/970worldanyoneBOý“c~Ü&£ý“¡I /test-0/986worldanyoneBOý“cŠè&¤ý“¡I /test-9/998worldanyoneBOý“cŠâ&¥ý“¡I /test-8/992worldanyoneBOý“cz×&¦ý“¡K /test-5/981worldanyoneBOý“czÕ&§ý“¡K /test-3/979worldanyoneBOý“czÖ&¨ý“¡K /test-7/980worldanyoneBOý“c~×&©ý“¡L /test-2/981worldanyoneBOý“c~Í&ªý“¡L /test-4/971worldanyoneBOý“cŠé&«ý“¡R /test-9/999worldanyoneBOý“c~Ý&¬ý“¡R /test-0/987worldanyoneBOý“cŠã&­ý“¡R /test-8/993worldanyoneBOý“czØ&®ý“¡U /test-5/982worldanyoneBOý“czÖ&¯ý“¡U /test-3/980worldanyoneBOý“cz×&°ý“¡U /test-7/981worldanyoneBOý“c~Î&±ý“¡V /test-4/972worldanyoneBOý“c~Ø&²ý“¡V /test-2/982worldanyoneB ý“cŠê&³ý“¡VÿÿÿõBOý“cŠä&´ý“¡\ /test-8/994worldanyoneBOý“c~Þ&µý“¡\ /test-0/988worldanyoneBOý“czØ&¶ý“¡` /test-7/982worldanyoneBOý“czÙ&·ý“¡` /test-5/983worldanyoneBOý“cz×&¸ý“¡` /test-3/981worldanyoneBOý“c~Ï&¹ý“¡` /test-4/973worldanyoneBOý“c~Ù&ºý“¡` /test-2/983worldanyoneBOý“c~ß&»ý“¡d /test-0/989worldanyoneBOý“cŠå&¼ý“¡e /test-8/995worldanyoneBOý“czÙ&½ý“¡g /test-7/983worldanyoneBOý“czÚ&¾ý“¡j /test-5/984worldanyoneBOý“czØ&¿ý“¡j /test-3/982worldanyoneBOý“c~Ð&Àý“¡k /test-4/974worldanyoneBOý“c~Ú&Áý“¡k /test-2/984worldanyoneBOý“c~à&Âý“¡m /test-0/990worldanyoneBOý“cŠæ&Ãý“¡m /test-8/996worldanyoneBOý“czÚ&Äý“¡o /test-7/984worldanyoneBOý“czÛ&Åý“¡q /test-5/985worldanyoneBOý“czÙ&Æý“¡r /test-3/983worldanyoneBOý“c~Ñ&Çý“¡s /test-4/975worldanyoneBOý“c~Û&Èý“¡t /test-2/985worldanyoneBOý“cŠç&Éý“¡u /test-8/997worldanyoneBOý“c~á&Êý“¡v /test-0/991worldanyoneBOý“czÛ&Ëý“¡x /test-7/985worldanyoneBOý“czÜ&Ìý“¡y /test-5/986worldanyoneBOý“czÚ&Íý“¡z /test-3/984worldanyoneBOý“cŠè&Îý“¡ /test-8/998worldanyoneBOý“c~Ü&Ïý“¡ /test-2/986worldanyoneBOý“c~Ò&Ðý“¡ /test-4/976worldanyoneBOý“c~â&Ñý“¡ /test-0/992worldanyoneBOý“czÜ&Òý“¡€ /test-7/986worldanyoneBOý“czÝ&Óý“¡‚ /test-5/987worldanyoneBOý“czÛ&Ôý“¡ƒ /test-3/985worldanyoneBOý“cŠé&Õý“¡ˆ /test-8/999worldanyoneBOý“c~ã&Öý“¡ˆ /test-0/993worldanyoneBOý“c~Ý&×ý“¡ˆ /test-2/987worldanyoneBOý“c~Ó&Øý“¡ˆ /test-4/977worldanyoneBOý“czÝ&Ùý“¡‰ /test-7/987worldanyoneBOý“czÜ&Úý“¡‹ /test-3/986worldanyoneBOý“czÞ&Ûý“¡‹ /test-5/988worldanyoneB ý“cŠê&Üý“¡ŒÿÿÿõBOý“c~Þ&Ýý“¡ /test-2/988worldanyoneBOý“c~ä&Þý“¡‘ /test-0/994worldanyoneBOý“c~Ô&ßý“¡’ /test-4/978worldanyoneBOý“czÝ&àý“¡” /test-3/987worldanyoneBOý“czß&áý“¡” /test-5/989worldanyoneBOý“czÞ&âý“¡” /test-7/988worldanyoneBOý“c~ß&ãý“¡˜ /test-2/989worldanyoneBOý“c~Õ&äý“¡› /test-4/979worldanyoneBOý“c~å&åý“¡œ /test-0/995worldanyoneBOý“czÞ&æý“¡œ /test-3/988worldanyoneBOý“czà&çý“¡ /test-5/990worldanyoneBOý“czß&èý“¡ /test-7/989worldanyoneBOý“c~à&éý“¡  /test-2/990worldanyoneBOý“c~Ö&êý“¡£ /test-4/980worldanyoneBOý“czß&ëý“£ /test-3/989worldanyoneBOý“c~á&ìý“£ /test-2/991worldanyoneBOý“c~×&íý“£ /test-4/981worldanyoneBOý“czá&îý“£ /test-5/991worldanyoneBOý“czà&ïý“£ /test-7/990worldanyoneBOý“c~æ&ðý“£ /test-0/996worldanyoneBOý“czà&ñý“£ /test-3/990worldanyoneBOý“czá&òý“£ž /test-7/991worldanyoneBOý“czâ&óý“£Ÿ /test-5/992worldanyoneBOý“c~Ø&ôý“£Ÿ /test-4/982worldanyoneBOý“c~ç&õý“£  /test-0/997worldanyoneBOý“c~â&öý“£  /test-2/992worldanyoneBOý“czá&÷ý“£¥ /test-3/991worldanyoneBOý“czâ&øý“£¦ /test-7/992worldanyoneBOý“c~Ù&ùý“£© /test-4/983worldanyoneBOý“c~ã&úý“£© /test-2/993worldanyoneBOý“czã&ûý“£© /test-5/993worldanyoneBOý“c~è&üý“£© /test-0/998worldanyoneBOý“czâ&ýý“£­ /test-3/992worldanyoneBOý“czã&þý“£® /test-7/993worldanyoneBOý“czä&ÿý“£² /test-5/994worldanyoneBOý“c~Ú'ý“£³ /test-4/984worldanyoneBOý“c~ä'ý“£³ /test-2/994worldanyoneBOý“c~é'ý“£³ /test-0/999worldanyoneBOý“czã'ý“£¶ /test-3/993worldanyoneBOý“czä'ý“£¶ /test-7/994worldanyoneB ý“c~ê'ý“£·ÿÿÿõBOý“czå'ý“£» /test-5/995worldanyoneBOý“c~å'ý“£» /test-2/995worldanyoneBOý“c~Û'ý“£¼ /test-4/985worldanyoneBOý“czä' ý“£¾ /test-3/994worldanyoneBOý“czå' ý“£À /test-7/995worldanyoneBOý“czæ' ý“£Ã /test-5/996worldanyoneBOý“c~Ü' ý“£Å /test-4/986worldanyoneBOý“c~æ' ý“£Å /test-2/996worldanyoneBOý“czå'ý“£È /test-3/995worldanyoneBOý“czæ'ý“£Ê /test-7/996worldanyoneBOý“czç'ý“£Í /test-5/997worldanyoneBOý“c~ç'ý“£Ð /test-2/997worldanyoneBOý“c~Ý'ý“£Ð /test-4/987worldanyoneBOý“czæ'ý“£Ò /test-3/996worldanyoneBOý“czç'ý“£Ó /test-7/997worldanyoneBOý“czè'ý“£Ö /test-5/998worldanyoneBOý“c~è'ý“£Ù /test-2/998worldanyoneBOý“c~Þ'ý“£Ú /test-4/988worldanyoneBOý“czç'ý“£Û /test-3/997worldanyoneBOý“czè'ý“£Ý /test-7/998worldanyoneBOý“czé'ý“£ß /test-5/999worldanyoneB ý“czê'ý“£âÿÿÿõBOý“c~é'ý“£ã /test-2/999worldanyoneBOý“c~ß'ý“£ä /test-4/989worldanyoneBOý“czè'ý“£å /test-3/998worldanyoneB ý“c~ê'ý“£îÿÿÿõBOý“czé' ý“£ï /test-7/999worldanyoneBOý“czé'!ý“£ö /test-3/999worldanyoneB ý“czê'"ý“£öÿÿÿõBOý“c~à'#ý“£÷ /test-4/990worldanyoneB ý“czê'$ý“£üÿÿÿõBOý“c~á'%ý“¤ /test-4/991worldanyoneBOý“c~â'&ý“¤  /test-4/992worldanyoneBOý“c~ã''ý“¤ /test-4/993worldanyoneBOý“c~ä'(ý“¤ /test-4/994worldanyoneBOý“c~å')ý“¤$ /test-4/995worldanyoneBOý“c~æ'*ý“¤- /test-4/996worldanyoneBOý“c~ç'+ý“¤6 /test-4/997worldanyoneBOý“c~è',ý“¤@ /test-4/998worldanyoneBOý“c~é'-ý“¤G /test-4/999worldanyoneB ý“c~ê'.ý“¤JÿÿÿõB$ý“ba'/ý“»LÿÿÿöN B ý“ba'0ý“»ÃÿÿÿõBapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/upgrade/snapshot.1000000000100644 0000000 0000000 00000000111 15051152474 031455 0ustar00rootroot0000000 0000000 ÿÿÿÿ/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/data/upgrade/snapshot.100001bec0100644 0000000 0000000 00011626653 15051152474 031743 0ustar00rootroot0000000 0000000 ý“baN ý“baN ý“cŠN ý“c~N ý“cŠN ý“c~N ý“c~N ý“czN ý“czN ý“czN ÿÿÿÿ /test-9worldanyone__ý“k“ý“k“Ä /test-9/338worldanyone ´ ´ý“€ÿý“€ÿ /test-9/339worldanyone ¿ ¿ý“ ý“ /test-9/332worldanyone v vý“€½ý“€½ /test-9/333worldanyone ‚ ‚ý“€Çý“€Ç /test-9/159worldanyone‘‘ý“v¦ý“v¦ /test-9/330worldanyone b bý“€¨ý“€¨ /test-9/158worldanyone‡‡ý“v›ý“v› /test-9/331worldanyone l lý“€²ý“€² /test-9/157worldanyone}}ý“vý“v /test-9/336worldanyone ¢ ¢ý“€êý“€ê /test-9/156worldanyonessý“v…ý“v… /test-9/337worldanyone ª ªý“€ôý“€ô /test-9/155worldanyonemmý“v{ý“v{ /test-9/334worldanyone  ý“€Óý“€Ó /test-9/154worldanyonebbý“vmý“vm /test-9/335worldanyone – –ý“€Þý“€Þ /test-9/601worldanyoneïïý“Òý“Ò /test-9/531worldanyone==ý“ŠØý“ŠØ /test-9/152worldanyoneMMý“vUý“vU /test-9/600worldanyoneááý“Åý“Å /test-9/530worldanyone33ý“ŠÎý“ŠÎ /test-9/153worldanyoneVVý“v_ý“v_ /test-9/150worldanyone;;ý“v?ý“v? /test-9/151worldanyoneCCý“vLý“vL /test-9/535worldanyone``ý“Šÿý“Šÿ /test-9/534worldanyoneVVý“Š÷ý“Š÷ /test-9/533worldanyoneLLý“Šìý“Šì /test-9/532worldanyoneFFý“Šâý“Šâ /test-9/609worldanyoneDDý“Ž)ý“Ž) /test-9/539worldanyone‡‡ý“‹*ý“‹* /test-9/608worldanyone99ý“Žý“Ž /test-9/538worldanyone{{ý“‹ý“‹ /test-9/607worldanyone//ý“Žý“Ž /test-9/537worldanyonerrý“‹ý“‹ /test-9/606worldanyone""ý“Žý“Ž /test-9/536worldanyonejjý“‹ý“‹ /test-9/605worldanyoneý“ûý“û /test-9/604worldanyone  ý“ðý“ð /test-9/603worldanyoneý“æý“æ /test-9/602worldanyoneùùý“Ýý“Ý /test-9/202worldanyone@@ý“y°ý“y° /test-9/349worldanyone""ý“‚aý“‚a /test-9/203worldanyoneIIý“y»ý“y» /test-9/204worldanyoneRRý“yÊý“yÊ /test-9/205worldanyoneYYý“yÕý“yÕ /test-9/200worldanyone33ý“y’ý“y’ /test-9/201worldanyone99ý“y¢ý“y¢ /test-9/341worldanyone Ñ Ñý“‚ ý“‚ /test-9/169worldanyoneúúý“w(ý“w( /test-9/342worldanyone Ú Úý“‚ý“‚ /test-9/343worldanyone ã ãý“‚ ý“‚ /test-9/344worldanyone ò òý“‚-ý“‚- /test-9/166worldanyoneÜÜý“wý“w /test-9/206worldanyone__ý“yáý“yá /test-9/345worldanyone ù ùý“‚7ý“‚7 /test-9/165worldanyoneÑÑý“vóý“vó /test-9/207worldanyoneiiý“yïý“yï /test-9/346worldanyoneý“‚Aý“‚A /test-9/168worldanyoneññý“wý“w /test-9/208worldanyonessý“yúý“yú /test-9/347worldanyone  ý“‚Lý“‚L /test-9/167worldanyoneããý“w ý“w /test-9/209worldanyone}}ý“z ý“z /test-9/348worldanyoneý“‚Wý“‚W /test-9/540worldanyoneý“‹3ý“‹3 /test-9/409worldanyone}}ý“…ý“… /test-9/161worldanyone££ý“v¿ý“v¿ /test-9/3worldanyone††ý“kßý“kß /test-9/408worldanyonessý“… ý“… /test-9/162worldanyone®®ý“vÍý“vÍ /test-9/2worldanyone||ý“kÉý“kÉ /test-9/542worldanyone¨¨ý“‹Hý“‹H /test-9/163worldanyone··ý“vÖý“vÖ /test-9/1worldanyonettý“k¹ý“k¹ /test-9/541worldanyone™™ý“‹=ý“‹= /test-9/164worldanyoneÄÄý“väý“vä /test-9/0worldanyoneiiý“k¦ý“k¦ /test-9/544worldanyone¹¹ý“‹^ý“‹^ /test-9/405worldanyoneTTý“„Ýý“„Ý /test-9/7worldanyone±±ý“l6ý“l6 /test-9/543worldanyone°°ý“‹Sý“‹S /test-9/404worldanyoneKKý“„Òý“„Ò /test-9/6worldanyone¨¨ý“l#ý“l# /test-9/546worldanyoneÌÌý“‹˜ý“‹˜ /test-9/407worldanyoneiiý“…ý“… /test-9/5worldanyoneý“lý“l /test-9/340worldanyone É Éý“ý“ /test-9/545worldanyoneÂÂý“‹Žý“‹Ž /test-9/406worldanyone__ý“„øý“„ø /test-9/160worldanyone››ý“v±ý“v± /test-9/4worldanyone””ý“köý“kö /test-9/548worldanyoneààý“‹ªý“‹ª /test-9/401worldanyone..ý“„®ý“„® /test-9/547worldanyoneÖÖý“‹¡ý“‹¡ /test-9/400worldanyone&&ý“„£ý“„£ /test-9/403worldanyone??ý“„Åý“„Å /test-9/9worldanyoneÆÆý“lYý“lY /test-9/549worldanyoneêêý“‹µý“‹µ /test-9/402worldanyone66ý“„¹ý“„¹ /test-9/8worldanyone¼¼ý“lFý“lF /test-9/211worldanyoneý“z*ý“z* /test-9/212worldanyone››ý“z@ý“z@ /test-9/210worldanyoneƒƒý“zý“z /test-9/215worldanyone¼¼ý“zxý“zx /test-9/318worldanyone ì ìý“€ý“€ /test-9/216worldanyoneÅÅý“z„ý“z„ /test-9/319worldanyone ö öý“€ý“€ /test-9/213worldanyone¢¢ý“zSý“zS /test-9/316worldanyone Õ Õý“òý“ò /test-9/214worldanyone®®ý“zdý“zd /test-9/317worldanyone ß ßý“€ý“€ /test-9/179worldanyoneccý“xRý“xR /test-9/219worldanyoneååý“z©ý“z© /test-9/314worldanyone ¿ ¿ý“×ý“× /test-9/178worldanyoneXXý“xFý“xF /test-9/315worldanyone Ë Ëý“åý“å /test-9/177worldanyoneMMý“x7ý“x7 /test-9/217worldanyoneÑÑý“z’ý“z’ /test-9/312worldanyone ¯ ¯ý“¿ý“¿ /test-9/176worldanyoneEEý“x+ý“x+ /test-9/218worldanyoneÛÛý“zžý“zž /test-9/313worldanyone ¶ ¶ý“Ëý“Ë /test-9/310worldanyone – –ý“¡ý“¡ /test-9/311worldanyone Ÿ Ÿý“¯ý“¯ /test-9/513worldanyoneŠŠý“Šý“Š /test-9/418worldanyoneÛÛý“…”ý“…” /test-9/170worldanyoneý“w3ý“w3 /test-9/512worldanyone€€ý“Šý“Š /test-9/417worldanyoneÏÏý“…ƒý“…ƒ /test-9/171worldanyoneý“w?ý“w? /test-9/511worldanyonevvý“Š ý“Š /test-9/416worldanyoneÈÈý“…wý“…w /test-9/510worldanyonellý“Šý“Š /test-9/415worldanyone»»ý“…jý“…j /test-9/623worldanyoneÃÃý“ŽÞý“ŽÞ /test-9/174worldanyone11ý“wqý“wq /test-9/622worldanyoneÁÁý“ŽÓý“ŽÓ /test-9/175worldanyone;;ý“xý“x /test-9/621worldanyone··ý“ŽËý“ŽË /test-9/172worldanyoneý“wLý“wL /test-9/620worldanyone­­ý“ŽÃý“ŽÃ /test-9/419worldanyoneããý“…Ÿý“…Ÿ /test-9/173worldanyone''ý“wcý“wc /test-9/627worldanyoneììý“ ý“ /test-9/410worldanyone……ý“…ý“… /test-9/626worldanyoneââý“ý“ /test-9/625worldanyoneÚÚý“Ž÷ý“Ž÷ /test-9/519worldanyoneÄÄý“Š^ý“Š^ /test-9/624worldanyoneÎÎý“Žêý“Žê /test-9/518worldanyoneººý“ŠSý“ŠS /test-9/517worldanyoneµµý“ŠHý“ŠH /test-9/414worldanyone²²ý“…Xý“…X /test-9/516worldanyoneªªý“Š<ý“Š< /test-9/413worldanyone§§ý“…Bý“…B /test-9/629worldanyoneý“ý“ /test-9/515worldanyonežžý“Š0ý“Š0 /test-9/412worldanyoneššý“…5ý“…5 /test-9/628worldanyoneööý“ý“ /test-9/514worldanyone””ý“Š'ý“Š' /test-9/411worldanyone‘‘ý“…)ý“…) /test-9/220worldanyoneïïý“z³ý“z³ /test-9/221worldanyoneùùý“z½ý“z½ /test-9/222worldanyone  ý“zâý“zâ /test-9/223worldanyone  ý“zëý“zë /test-9/224worldanyone  ý“zõý“zõ /test-9/327worldanyone D Dý“€†ý“€† /test-9/225worldanyone  ý“zÿý“zÿ /test-9/328worldanyone N Ný“€‘ý“€‘ /test-9/226worldanyone * *ý“{ ý“{ /test-9/329worldanyone W Wý“€ý“€ /test-9/227worldanyone 4 4ý“{ý“{ /test-9/188worldanyone¹¹ý“xÒý“xÒ /test-9/228worldanyone @ @ý“{'ý“{' /test-9/323worldanyone  ý“€Pý“€P /test-9/187worldanyone°°ý“xÄý“xÄ /test-9/229worldanyone K Ký“{2ý“{2 /test-9/324worldanyone ' 'ý“€\ý“€\ /test-9/325worldanyone . .ý“€hý“€h /test-9/189worldanyoneÃÃý“xÜý“xÜ /test-9/326worldanyone 8 8ý“€vý“€v /test-9/320worldanyone  ý“€,ý“€, /test-9/321worldanyone  ý“€;ý“€; /test-9/322worldanyone  ý“€Fý“€F /test-9/522worldanyoneââý“Š|ý“Š| /test-9/427worldanyone99ý“†ý“† /test-9/521worldanyoneÙÙý“Šqý“Šq /test-9/426worldanyone,,ý“†ý“† /test-9/180worldanyonekký“x_ý“x_ /test-9/524worldanyone÷÷ý“Š‘ý“Š‘ /test-9/429worldanyoneQQý“†,ý“†, /test-9/181worldanyonevvý“xoý“xo /test-9/523worldanyoneííý“Їý“Ї /test-9/428worldanyoneBBý“†ý“† /test-9/182worldanyone€€ý“x|ý“x| /test-9/610worldanyoneMMý“Ž5ý“Ž5 /test-9/183worldanyoneŒŒý“xŒý“xŒ /test-9/184worldanyone——ý“xý“x /test-9/612worldanyonebbý“ŽMý“ŽM /test-9/520worldanyoneÎÎý“Šhý“Šh /test-9/185worldanyone¡¡ý“x¦ý“x¦ /test-9/611worldanyoneXXý“ŽAý“ŽA /test-9/186worldanyoneªªý“xµý“xµ /test-9/614worldanyonevvý“Ž^ý“Ž^ /test-9/613worldanyonellý“ŽUý“ŽU /test-9/529worldanyone((ý“ŠÃý“ŠÃ /test-9/616worldanyoneŠŠý“Žqý“Žq /test-9/421worldanyoneùùý“…Äý“…Ä /test-9/615worldanyone€€ý“Žhý“Žh /test-9/420worldanyoneóóý“…®ý“…® /test-9/618worldanyoneý“ެý“ެ /test-9/526worldanyone  ý“Фý“Ф /test-9/423worldanyoneý“…ßý“…ß /test-9/617worldanyone••ý“Ž¢ý“Ž¢ /test-9/525worldanyoneý“Š›ý“Š› /test-9/422worldanyoneý“…Òý“…Ò /test-9/528worldanyoneý“Џý“Џ /test-9/425worldanyone%%ý“…øý“…ø /test-9/619worldanyone¢¢ý“޶ý“޶ /test-9/527worldanyoneý“Š®ý“Š® /test-9/424worldanyoneý“…ìý“…ì /test-9/116worldanyoneííý“tBý“tB /test-9/117worldanyoneõõý“tNý“tN /test-9/114worldanyoneÙÙý“t)ý“t) /test-9/115worldanyoneââý“t5ý“t5 /test-9/571worldanyoneÊÊý“Œ ý“Œ  /test-9/112worldanyoneÂÂý“t ý“t /test-9/570worldanyone»»ý“Œ–ý“Œ– /test-9/113worldanyoneÏÏý“tý“t /test-9/110worldanyone­­ý“síý“sí /test-9/111worldanyone¶¶ý“sùý“sù /test-9/118worldanyoneý“t_ý“t_ /test-9/119worldanyone  ý“tsý“ts /test-9/435worldanyoneý“†sý“†s /test-9/436worldanyone‹‹ý“†~ý“†~ /test-9/433worldanyonettý“†Xý“†X /test-9/434worldanyonexxý“†eý“†e /test-9/431worldanyone``ý“†Bý“†B /test-9/700worldanyoneªªý“”Jý“”J /test-9/432worldanyonejjý“†Oý“†O /test-9/430worldanyoneVVý“†7ý“†7 /test-9/703worldanyoneÈÈý“”hý“”h /test-9/574worldanyoneååý“Œ¿ý“Œ¿ /test-9/704worldanyoneÓÓý“”qý“”q /test-9/575worldanyoneððý“ŒÊý“ŒÊ /test-9/701worldanyone´´ý“”Tý“”T /test-9/572worldanyoneÔÔý“Œªý“Œª /test-9/702worldanyone¾¾ý“”^ý“”^ /test-9/573worldanyoneÞÞý“Œµý“Œµ /test-9/707worldanyoneîîý“”‹ý“”‹ /test-9/578worldanyone  ý“Œéý“Œé /test-9/439worldanyone¦¦ý“†Ÿý“†Ÿ /test-9/579worldanyoneý“Œòý“Œò /test-9/705worldanyoneÝÝý“”zý“”z /test-9/576worldanyoneüüý“ŒÕý“ŒÕ /test-9/437worldanyone••ý“†‡ý“†‡ /test-9/706worldanyoneççý“”‚ý“”‚ /test-9/577worldanyoneý“Œßý“Œß /test-9/438worldanyoneŸŸý“†“ý“†“ /test-9/125worldanyoneAAý“tÃý“tà /test-9/126worldanyoneKKý“tÍý“tÍ /test-9/127worldanyoneUUý“tÙý“tÙ /test-9/300worldanyone - -ý“"ý“" /test-9/128worldanyone__ý“tæý“tæ /test-9/580worldanyone!!ý“Œûý“Œû /test-9/121worldanyoneý“tŒý“tŒ /test-9/302worldanyone @ @ý“;ý“; /test-9/122worldanyone##ý“t ý“t  /test-9/301worldanyone 6 6ý“.ý“. /test-9/582worldanyone55ý“ý“ /test-9/123worldanyone--ý“t¬ý“t¬ /test-9/304worldanyone U Uý“Rý“R /test-9/581worldanyone++ý“ý“ /test-9/124worldanyone77ý“t·ý“t· /test-9/303worldanyone K Ký“Hý“H /test-9/306worldanyone i iý“gý“g /test-9/305worldanyone _ _ý“[ý“[ /test-9/308worldanyone € €ý“‡ý“‡ /test-9/307worldanyone t tý“xý“x /test-9/129worldanyonehhý“tôý“tô /test-9/309worldanyone  ý“•ý“• /test-9/444worldanyone××ý“†ßý“†ß /test-9/445worldanyoneááý“†èý“†è /test-9/446worldanyoneëëý“†òý“†ò /test-9/447worldanyoneõõý“†üý“†ü /test-9/440worldanyone®®ý“†ªý“†ª /test-9/441worldanyone¸¸ý“†ºý“†º /test-9/442worldanyoneÉÉý“†Çý“†Ç /test-9/443worldanyoneÓÓý“†Óý“†Ó /test-9/583worldanyone>>ý“ý“ /test-9/584worldanyoneIIý“$ý“$ /test-9/585worldanyoneRRý“0ý“0 /test-9/120worldanyoneý“t~ý“t~ /test-9/586worldanyone\\ý“;ý“; /test-9/587worldanyoneeeý“Dý“D /test-9/448worldanyoneÿÿý“‡ý“‡ /test-9/588worldanyoneppý“Ný“N /test-9/449worldanyone  ý“‡ý“‡ /test-9/589worldanyonezzý“Wý“W /test-9/134worldanyone››ý“uFý“uF /test-9/135worldanyone¦¦ý“uYý“uY /test-9/132worldanyone‡‡ý“u.ý“u. /test-9/133worldanyone‘‘ý“u:ý“u: /test-9/138worldanyoneÉÉý“uŽý“uŽ /test-9/139worldanyoneÓÓý“užý“už /test-9/136worldanyone²²ý“umý“um /test-9/137worldanyone½½ý“u}ý“u} /test-9/450worldanyoneý“‡ý“‡ /test-9/453worldanyone,,ý“‡=ý“‡= /test-9/454worldanyone55ý“‡Iý“‡I /test-9/451worldanyoneý“‡(ý“‡( /test-9/452worldanyone""ý“‡3ý“‡3 /test-9/457worldanyoneSSý“‡gý“‡g /test-9/458worldanyone]]ý“‡qý“‡q /test-9/558worldanyoneEEý“Œý“Œ /test-9/455worldanyone??ý“‡Sý“‡S /test-9/559worldanyoneRRý“Œ$ý“Œ$ /test-9/456worldanyoneIIý“‡\ý“‡\ /test-9/556worldanyone11ý“‹ÿý“‹ÿ /test-9/557worldanyone==ý“Œ ý“Œ /test-9/554worldanyoneý“‹éý“‹é /test-9/459worldanyoneiiý“‡|ý“‡| /test-9/555worldanyone##ý“‹ôý“‹ô /test-9/552worldanyoneý“‹Õý“‹Õ /test-9/131worldanyone}}ý“u"ý“u" /test-9/553worldanyoneý“‹ßý“‹ß /test-9/130worldanyonessý“uý“u /test-9/550worldanyoneòòý“‹Àý“‹À /test-9/551worldanyoneýýý“‹Ëý“‹Ë /test-9/143worldanyoneûûý“uÚý“uÚ /test-9/144worldanyoneý“uçý“uç /test-9/560worldanyoneZZý“Œ/ý“Œ/ /test-9/145worldanyone  ý“uøý“uø /test-9/146worldanyoneý“vý“v /test-9/147worldanyoneý“vý“v /test-9/148worldanyone((ý“v!ý“v! /test-9/149worldanyone00ý“v/ý“v/ /test-9/461worldanyone~~ý“‡”ý“‡” /test-9/460worldanyonessý“‡ˆý“‡ˆ /test-9/462worldanyone……ý“‡ ý“‡  /test-9/463worldanyoneý“‡¬ý“‡¬ /test-9/464worldanyoneššý“‡¶ý“‡¶ /test-9/465worldanyone££ý“‡Áý“‡Á /test-9/569worldanyone¶¶ý“Œý“Œ /test-9/466worldanyone®®ý“‡Êý“‡Ê /test-9/467worldanyone¸¸ý“‡Õý“‡Õ /test-9/468worldanyoneÂÂý“‡áý“‡á /test-9/469worldanyoneÌÌý“‡ëý“‡ë /test-9/565worldanyoneŽŽý“Œhý“Œh /test-9/566worldanyone˜˜ý“Œrý“Œr /test-9/567worldanyone¢¢ý“Œ{ý“Œ{ /test-9/568worldanyone¬¬ý“Œ„ý“Œ„ /test-9/561worldanyoneeeý“Œ:ý“Œ: /test-9/140worldanyoneÛÛý“u¯ý“u¯ /test-9/562worldanyoneppý“ŒGý“ŒG /test-9/563worldanyone||ý“ŒQý“ŒQ /test-9/142worldanyoneññý“uÏý“uÏ /test-9/564worldanyone„„ý“Œ]ý“Œ] /test-9/141worldanyoneççý“u¿ý“u¿ /test-9/687worldanyone))ý“‘÷ý“‘÷ /test-9/686worldanyone  ý“‘ëý“‘ë /test-9/689worldanyone99ý“’ ý“’ /test-9/688worldanyone11ý“’ý“’ /test-9/683worldanyoneý“‘Âý“‘ /test-9/682worldanyoneøøý“‘¹ý“‘¹ /test-9/685worldanyoneý“‘ßý“‘ß /test-9/684worldanyone  ý“‘Îý“‘Î /test-9/680worldanyoneèèý“‘¦ý“‘¦ /test-9/681worldanyoneîîý“‘°ý“‘° /test-9/678worldanyoneÔÔý“‘‘ý“‘‘ /test-9/677worldanyoneÊÊý“‘ˆý“‘ˆ /test-9/676worldanyoneÀÀý“‘€ý“‘€ /test-9/675worldanyone··ý“‘vý“‘v /test-9/674worldanyone¬¬ý“‘lý“‘l /test-9/673worldanyone  ý“‘aý“‘a /test-9/672worldanyone™™ý“‘Wý“‘W /test-9/671worldanyone””ý“‘Lý“‘L /test-9/679worldanyoneÝÝý“‘›ý“‘› /test-9/670worldanyoneŠŠý“‘Dý“‘D /test-9/696worldanyone{{ý“’Oý“’O /test-9/695worldanyonerrý“’Eý“’E /test-9/694worldanyoneddý“’:ý“’: /test-9/693worldanyone]]ý“’0ý“’0 /test-9/699worldanyone¡¡ý“”=ý“”= /test-9/698worldanyone‘‘ý“”*ý“”* /test-9/697worldanyone……ý“’Zý“’Z /test-9/691worldanyoneMMý“’ý“’ /test-9/692worldanyoneWWý“’%ý“’% /test-9/690worldanyoneCCý“’ý“’ /test-9/646worldanyoneý“çý“ç /test-9/647worldanyone¨¨ý“ñý“ñ /test-9/648worldanyone®®ý“ýý“ý /test-9/649worldanyone½½ý“ý“ /test-9/642worldanyonewwý“¯ý“¯ /test-9/195worldanyoneý“yDý“yD /test-9/643worldanyone~~ý“Áý“Á /test-9/194worldanyoneööý“y-ý“y- /test-9/644worldanyone‰‰ý“Ðý“Ð /test-9/197worldanyoneý“yiý“yi /test-9/645worldanyone““ý“Üý“Ü /test-9/196worldanyone  ý“y\ý“y\ /test-9/191worldanyoneÔÔý“xøý“xø /test-9/190worldanyoneÊÊý“xìý“xì /test-9/640worldanyoneccý“šý“š /test-9/193worldanyoneééý“yý“y /test-9/641worldanyonemmý“¤ý“¤ /test-9/192worldanyoneÞÞý“y ý“y /test-9/198worldanyone  ý“ywý“yw /test-9/199worldanyone))ý“y…ý“y… /test-9/505worldanyone44ý“‰Äý“‰Ä /test-9/506worldanyoneBBý“‰Óý“‰Ó /test-9/639worldanyoneYYý“Žý“Ž /test-9/503worldanyone  ý“‰¬ý“‰¬ /test-9/504worldanyone//ý“‰ºý“‰º /test-9/637worldanyoneEEý“tý“t /test-9/509worldanyoneccý“‰öý“‰ö /test-9/638worldanyoneTTý“‚ý“‚ /test-9/635worldanyone77ý“\ý“\ /test-9/507worldanyoneLLý“‰Ýý“‰Ý /test-9/636worldanyone==ý“hý“h /test-9/508worldanyoneWWý“‰êý“‰ê /test-9/633worldanyone$$ý“Hý“H /test-9/634worldanyone..ý“Pý“P /test-9/631worldanyoneý“3ý“3 /test-9/632worldanyoneý“?ý“? /test-9/501worldanyone  ý“‰’ý“‰’ /test-9/630worldanyoneý“'ý“' /test-9/502worldanyoneý“‰Ÿý“‰Ÿ /test-9/500worldanyoneÿÿý“‰†ý“‰† /test-9/668worldanyonevvý“‘2ý“‘2 /test-9/669worldanyone€€ý“‘;ý“‘; /test-9/660worldanyone((ý“Üý“Ü /test-9/661worldanyone22ý“åý“å /test-9/662worldanyone<<ý“ðý“ð /test-9/663worldanyoneFFý“øý“ø /test-9/664worldanyonePPý“‘ý“‘ /test-9/665worldanyoneYYý“‘ý“‘ /test-9/666worldanyoneeeý“‘ý“‘ /test-9/667worldanyonenný“‘'ý“‘' /test-9/659worldanyoneý“Óý“Ó /test-9/657worldanyoneý“¾ý“¾ /test-9/658worldanyoneý“Éý“É /test-9/651worldanyoneÑÑý“vý“v /test-9/652worldanyoneÛÛý“ý“ /test-9/650worldanyoneÆÆý“ý“ /test-9/655worldanyoneûûý“©ý“© /test-9/656worldanyoneý“µý“µ /test-9/653worldanyoneååý“ý“ /test-9/654worldanyoneòòý“ý“ /test-9/35worldanyone··ý“n*ý“n* /test-9/36worldanyoneÁÁý“n5ý“n5 /test-9/33worldanyone¤¤ý“ný“n /test-9/34worldanyone­­ý“ný“n /test-9/39worldanyoneßßý“ngý“ng /test-9/37worldanyoneÊÊý“nCý“nC /test-9/38worldanyoneÖÖý“nTý“nT /test-9/43worldanyone  ý“nÈý“nÈ /test-9/42worldanyoneüüý“n¥ý“n¥ /test-9/41worldanyoneóóý“n’ý“n’ /test-9/40worldanyoneççý“n|ý“n| /test-9/22worldanyone==ý“m;ý“m; /test-9/23worldanyoneGGý“mPý“mP /test-9/24worldanyonePPý“m^ý“m^ /test-9/25worldanyoneZZý“mký“mk /test-9/26worldanyoneaaý“m|ý“m| /test-9/27worldanyonekký“mŽý“mŽ /test-9/28worldanyonevvý“m¥ý“m¥ /test-9/29worldanyoneý“m°ý“m° /test-9/30worldanyone‡‡ý“mÁý“mÁ /test-9/32worldanyone˜˜ý“mòý“mò /test-9/31worldanyoneý“mßý“mß /test-9/19worldanyone##ý“m ý“m /test-9/17worldanyoneý“lêý“lê /test-9/18worldanyoneý“lûý“lû /test-9/15worldanyoneý“lÂý“l /test-9/16worldanyone  ý“lÖý“lÖ /test-9/13worldanyoneêêý“l˜ý“l˜ /test-9/14worldanyoneööý“l¬ý“l¬ /test-9/11worldanyoneÙÙý“luý“lu /test-9/12worldanyoneââý“lˆý“lˆ /test-9/21worldanyone88ý“m*ý“m* /test-9/20worldanyone--ý“mý“m /test-9/10worldanyoneÐÐý“liý“li /test-9/79worldanyonellý“q ý“q  /test-9/78worldanyone\\ý“qý“q /test-9/77worldanyoneRRý“q€ý“q€ /test-9/82worldanyoneŽŽý“qÏý“qÏ /test-9/83worldanyone˜˜ý“qáý“qá /test-9/80worldanyonerrý“q­ý“q­ /test-9/81worldanyone„„ý“qÁý“qÁ /test-9/86worldanyone´´ý“r ý“r /test-9/87worldanyone¾¾ý“rý“r /test-9/84worldanyone¢¢ý“qïý“qï /test-9/85worldanyone¬¬ý“qþý“qþ /test-9/67worldanyoneóóý“p†ý“p† /test-9/66worldanyoneææý“puý“pu /test-9/69worldanyoneý“p¢ý“p¢ /test-9/68worldanyoneúúý“p”ý“p” /test-9/70worldanyoneý“qý“q /test-9/71worldanyoneý“q&ý“q& /test-9/72worldanyone!!ý“q6ý“q6 /test-9/73worldanyone**ý“qDý“qD /test-9/74worldanyone44ý“qSý“qS /test-9/75worldanyone<<ý“q_ý“q_ /test-9/76worldanyoneJJý“qrý“qr /test-9/59worldanyone¦¦ý“pý“p /test-9/58worldanyoneœœý“o÷ý“o÷ /test-9/57worldanyone““ý“oæý“oæ /test-9/56worldanyone‰‰ý“oÒý“oÒ /test-9/55worldanyone‚‚ý“oÅý“oÅ /test-9/64worldanyoneÖÖý“pSý“pS /test-9/65worldanyoneààý“pdý“pd /test-9/62worldanyoneÂÂý“p6ý“p6 /test-9/63worldanyoneÌÌý“pDý“pD /test-9/60worldanyone°°ý“pý“p /test-9/61worldanyoneººý“p'ý“p' /test-9/49worldanyoneHHý“oOý“oO /test-9/48worldanyone==ý“o5ý“o5 /test-9/45worldanyoneý“nøý“nø /test-9/44worldanyoneý“nÜý“nÜ /test-9/47worldanyone11ý“o!ý“o! /test-9/46worldanyone((ý“oý“o /test-9/51worldanyone__ý“o‹ý“o‹ /test-9/52worldanyoneiiý“oý“o /test-9/53worldanyonenný“o®ý“o® /test-9/54worldanyonexxý“o¼ý“o¼ /test-9/50worldanyoneUUý“ozý“oz /test-9/487worldanyone}}ý“ˆÎý“ˆÎ /test-9/281worldanyone ` `ý“~ý“~ /test-9/486worldanyoneuuý“ˆÂý“ˆÂ /test-9/280worldanyone W Wý“~ý“~ /test-9/485worldanyonejjý“ˆ·ý“ˆ· /test-9/484worldanyone__ý“ˆ«ý“ˆ« /test-9/285worldanyone Š Šý“~Ný“~N /test-9/284worldanyone } }ý“~@ý“~@ /test-9/489worldanyone””ý“ˆñý“ˆñ /test-9/283worldanyone n ný“~3ý“~3 /test-9/488worldanyone‹‹ý“ˆßý“ˆß /test-9/282worldanyone f fý“~'ý“~' /test-9/288worldanyone ¤ ¤ý“~rý“~r /test-9/289worldanyone ´ ´ý“~€ý“~€ /test-9/286worldanyone ‘ ‘ý“~[ý“~[ /test-9/287worldanyone › ›ý“~gý“~g /test-9/482worldanyoneHHý“ˆ”ý“ˆ” /test-9/483worldanyoneVVý“ˆŸý“ˆŸ /test-9/480worldanyone22ý“ˆ{ý“ˆ{ /test-9/481worldanyone==ý“ˆ‡ý“ˆ‡ /test-9/474worldanyoneúúý“ˆ=ý“ˆ= /test-9/473worldanyoneððý“ˆý“ˆ /test-9/476worldanyoneý“ˆQý“ˆQ /test-9/270worldanyone ð ðý“}tý“}t /test-9/475worldanyoneý“ˆEý“ˆE /test-9/478worldanyoneý“ˆfý“ˆf /test-9/272worldanyone  ý“}’ý“}’ /test-9/477worldanyoneý“ˆ\ý“ˆ\ /test-9/271worldanyone û ûý“}„ý“}„ /test-9/274worldanyone  ý“}´ý“}´ /test-9/479worldanyone))ý“ˆqý“ˆq /test-9/273worldanyone  ý“}¤ý“}¤ /test-9/275worldanyone * *ý“}Áý“}Á /test-9/276worldanyone . .ý“}Ëý“}Ë /test-9/277worldanyone 9 9ý“}ðý“}ð /test-9/278worldanyone C Cý“}ûý“}û /test-9/279worldanyone M Mý“~ý“~ /test-9/470worldanyoneÖÖý“‡õý“‡õ /test-9/471worldanyoneßßý“‡ÿý“‡ÿ /test-9/472worldanyoneææý“ˆ ý“ˆ /test-9/109worldanyone¡¡ý“sÝý“sÝ /test-9/108worldanyone——ý“sÏý“sÏ /test-9/107worldanyoneŽŽý“s¿ý“s¿ /test-9/106worldanyone„„ý“s­ý“s­ /test-9/105worldanyonexxý“sœý“sœ /test-9/104worldanyonemmý“sŒý“sŒ /test-9/103worldanyoneaaý“svý“sv /test-9/102worldanyoneWWý“sgý“sg /test-9/99worldanyone99ý“s ý“s /test-9/101worldanyonePPý“sTý“sT /test-9/100worldanyoneDDý“s@ý“s@ /test-9/595worldanyone±±ý“—ý“— /test-9/98worldanyone11ý“rúý“rú /test-9/594worldanyone¬¬ý“Žý“Ž /test-9/97worldanyone%%ý“rãý“rã /test-9/597worldanyoneÅÅý“ªý“ª /test-9/391worldanyoneÊÊý“„7ý“„7 /test-9/96worldanyoneý“rÅý“rÅ /test-9/596worldanyone»»ý“ ý“  /test-9/390worldanyone¿¿ý“„*ý“„* /test-9/95worldanyoneý“r±ý“r± /test-9/599worldanyoneÙÙý“¼ý“¼ /test-9/393worldanyoneÝÝý“„Rý“„R /test-9/94worldanyoneý“r™ý“r™ /test-9/598worldanyoneÏÏý“²ý“² /test-9/392worldanyoneÓÓý“„Eý“„E /test-9/93worldanyoneùùý“r„ý“r„ /test-9/395worldanyoneòòý“„lý“„l /test-9/92worldanyoneïïý“roý“ro /test-9/394worldanyoneëëý“„bý“„b /test-9/91worldanyoneççý“r_ý“r_ /test-9/90worldanyoneÝÝý“rNý“rN /test-9/294worldanyone ë ëý“~Éý“~É /test-9/499worldanyoneõõý“‰zý“‰z /test-9/293worldanyone Ú Úý“~·ý“~· /test-9/296worldanyone û ûý“~æý“~æ /test-9/295worldanyone ñ ñý“~Ùý“~Ù /test-9/496worldanyoneÖÖý“‰Qý“‰Q /test-9/290worldanyone ½ ½ý“~Žý“~Ž /test-9/495worldanyoneÎÎý“‰Bý“‰B /test-9/498worldanyoneëëý“‰lý“‰l /test-9/292worldanyone Ò Òý“~©ý“~© /test-9/497worldanyoneããý“‰`ý“‰` /test-9/291worldanyone È Èý“~›ý“~› /test-9/491worldanyoneªªý“‰ý“‰ /test-9/492worldanyone´´ý“‰ý“‰ /test-9/493worldanyone¾¾ý“‰,ý“‰, /test-9/494worldanyoneÈÈý“‰6ý“‰6 /test-9/297worldanyone  ý“~öý“~ö /test-9/298worldanyone  ý“ý“ /test-9/299worldanyone ! !ý“ý“ /test-9/490worldanyoneý“‰ý“‰ /test-9/396worldanyoneý“„zý“„z /test-9/397worldanyoneý“„†ý“„† /test-9/398worldanyoneý“„’ý“„’ /test-9/399worldanyoneý“„›ý“„› /test-9/590worldanyone„„ý“bý“b /test-9/591worldanyoneý“mý“m /test-9/592worldanyone——ý“vý“v /test-9/88worldanyoneÉÉý“r-ý“r- /test-9/593worldanyoneý“ý“ /test-9/89worldanyoneÓÓý“r=ý“r= /test-9/240worldanyone ´ ´ý“{½ý“{½ /test-9/241worldanyone À Àý“{Ïý“{Ï /test-9/383worldanyonessý“ƒÓý“ƒÓ /test-9/384worldanyone~~ý“ƒÞý“ƒÞ /test-9/381worldanyoneccý“ƒ»ý“ƒ» /test-9/382worldanyonemmý“ƒÆý“ƒÆ /test-9/380worldanyoneYYý“ƒ±ý“ƒ± /test-9/389worldanyone´´ý“„ý“„ /test-9/388worldanyone§§ý“„ý“„ /test-9/387worldanyonežžý“„ý“„ /test-9/386worldanyone’’ý“ƒ÷ý“ƒ÷ /test-9/385worldanyone‡‡ý“ƒëý“ƒë /test-9/245worldanyone ñ ñý“|ý“| /test-9/244worldanyone æ æý“| ý“| /test-9/243worldanyone Û Ûý“{úý“{ú /test-9/242worldanyone Ï Ïý“{áý“{á /test-9/249worldanyone  ý“|Sý“|S /test-9/248worldanyone  ý“|Eý“|E /test-9/247worldanyone  ý“|8ý“|8 /test-9/246worldanyone û ûý“|+ý“|+ /test-9/230worldanyone U Uý“{?ý“{? /test-9/370worldanyoneññý“ƒ=ý“ƒ= /test-9/371worldanyoneÿÿý“ƒJý“ƒJ /test-9/372worldanyone  ý“ƒVý“ƒV /test-9/373worldanyoneý“ƒbý“ƒb /test-9/379worldanyonePPý“ƒ¨ý“ƒ¨ /test-9/239worldanyone ® ®ý“{¯ý“{¯ /test-9/378worldanyoneFFý“ƒŸý“ƒŸ /test-9/375worldanyone**ý“ƒ}ý“ƒ} /test-9/374worldanyoneý“ƒpý“ƒp /test-9/377worldanyone;;ý“ƒ“ý“ƒ“ /test-9/376worldanyone11ý“ƒ‡ý“ƒ‡ /test-9/232worldanyone k ký“{Zý“{Z /test-9/231worldanyone ^ ^ý“{Lý“{L /test-9/234worldanyone } }ý“{sý“{s /test-9/233worldanyone t tý“{eý“{e /test-9/236worldanyone ‘ ‘ý“{‹ý“{‹ /test-9/235worldanyone ‡ ‡ý“{ý“{ /test-9/238worldanyone ¦ ¦ý“{¡ý“{¡ /test-9/237worldanyone › ›ý“{•ý“{• /test-9/262worldanyone ž žý“|ÿý“|ÿ /test-9/263worldanyone ¦ ¦ý“} ý“} /test-9/260worldanyone ˆ ˆý“|áý“|á /test-9/261worldanyone ‘ ‘ý“|ðý“|ð /test-9/361worldanyoneŸŸý“‚äý“‚ä /test-9/362worldanyone©©ý“‚îý“‚î /test-9/360worldanyone””ý“‚Ùý“‚Ù /test-9/366worldanyoneÎÎý“ƒý“ƒ /test-9/365worldanyoneÄÄý“ƒ ý“ƒ /test-9/364worldanyoneººý“ƒý“ƒ /test-9/363worldanyone±±ý“‚øý“‚ø /test-9/369worldanyoneééý“ƒ1ý“ƒ1 /test-9/368worldanyoneââý“ƒ'ý“ƒ' /test-9/367worldanyoneØØý“ƒý“ƒ /test-9/269worldanyone é éý“}hý“}h /test-9/268worldanyone à àý“}Wý“}W /test-9/267worldanyone Ñ Ñý“}Cý“}C /test-9/266worldanyone Ç Çý“}4ý“}4 /test-9/265worldanyone ½ ½ý“}&ý“}& /test-9/264worldanyone ± ±ý“}ý“} /test-9/250worldanyone " "ý“|`ý“|` /test-9/251worldanyone + +ý“|mý“|m /test-9/252worldanyone 5 5ý“|yý“|y /test-9/350worldanyone--ý“‚jý“‚j /test-9/351worldanyone44ý“‚uý“‚u /test-9/353worldanyoneLLý“‚Œý“‚Œ /test-9/352worldanyoneAAý“‚€ý“‚€ /test-9/355worldanyone``ý“‚£ý“‚£ /test-9/354worldanyoneVVý“‚˜ý“‚˜ /test-9/357worldanyoneuuý“‚¶ý“‚¶ /test-9/356worldanyonekký“‚¬ý“‚¬ /test-9/359worldanyone‰‰ý“‚Íý“‚Í /test-9/358worldanyone€€ý“‚Âý“‚ /test-9/258worldanyone r rý“|Ãý“|à /test-9/257worldanyone i iý“|·ý“|· /test-9/259worldanyone ~ ~ý“|Óý“|Ó /test-9/254worldanyone I Iý“|”ý“|” /test-9/253worldanyone A Aý“|‡ý“|‡ /test-9/256worldanyone _ _ý“|¬ý“|¬ /test-9/255worldanyone U Uý“|¡ý“|¡/test-7worldanyone::ý“k%ý“k%½ /test-7/338worldanyone £ £ý“€ìý“€ì /test-7/339worldanyone ­ ­ý“€öý“€ö /test-7/332worldanyone f fý“€¬ý“€¬ /test-7/333worldanyone s sý“€¹ý“€¹ /test-7/159worldanyone  ý“v½ý“v½ /test-7/330worldanyone R Rý“€˜ý“€˜ /test-7/158worldanyone˜˜ý“v¯ý“v¯ /test-7/331worldanyone ] ]ý“€£ý“€£ /test-7/157worldanyoneý“v£ý“v£ /test-7/336worldanyone  ý“€Øý“€Ø /test-7/156worldanyone……ý“v—ý“v— /test-7/337worldanyone › ›ý“€ãý“€ã /test-7/155worldanyonezzý“v‹ý“v‹ /test-7/334worldanyone } }ý“€Äý“€Ä /test-7/154worldanyoneqqý“vý“v /test-7/335worldanyone ‡ ‡ý“€Îý“€Î /test-7/601worldanyone,,ý“Žý“Ž /test-7/531worldanyoneYYý“Šøý“Šø /test-7/152worldanyone[[ý“vjý“vj /test-7/600worldanyone##ý“Žý“Ž /test-7/530worldanyoneRRý“Šìý“Šì /test-7/153worldanyoneggý“vuý“vu /test-7/150worldanyoneFFý“vPý“vP /test-7/151worldanyoneQQý“v\ý“v\ /test-7/535worldanyoneý“‹ ý“‹ /test-7/534worldanyoneuuý“‹ý“‹ /test-7/533worldanyonekký“‹ý“‹ /test-7/532worldanyonebbý“‹ý“‹ /test-7/609worldanyoneƒƒý“Žký“Žk /test-7/539worldanyone§§ý“‹Hý“‹H /test-7/608worldanyoneyyý“Žbý“Žb /test-7/538worldanyoneý“‹=ý“‹= /test-7/607worldanyoneooý“ŽYý“ŽY /test-7/537worldanyone““ý“‹3ý“‹3 /test-7/606worldanyoneccý“ŽMý“ŽM /test-7/536worldanyone‰‰ý“‹*ý“‹* /test-7/605worldanyoneYYý“ŽAý“ŽA /test-7/604worldanyoneQQý“Ž5ý“Ž5 /test-7/603worldanyoneAAý“Ž'ý“Ž' /test-7/602worldanyone77ý“Žý“Ž /test-7/202worldanyoneMMý“yÀý“yÀ /test-7/349worldanyoneý“‚Pý“‚P /test-7/203worldanyoneWWý“yÒý“yÒ /test-7/204worldanyoneddý“yäý“yä /test-7/205worldanyoneppý“yøý“yø /test-7/200worldanyone88ý“y¢ý“y¢ /test-7/201worldanyoneAAý“y±ý“y± /test-7/341worldanyone Á Áý“ ý“ /test-7/169worldanyoneÿÿý“w*ý“w* /test-7/342worldanyone Ê Êý“ý“ /test-7/343worldanyone Ö Öý“‚ ý“‚ /test-7/344worldanyone â âý“‚ý“‚ /test-7/166worldanyoneááý“w ý“w /test-7/206worldanyonezzý“zý“z /test-7/345worldanyone ì ìý“‚'ý“‚' /test-7/165worldanyone××ý“wý“w /test-7/207worldanyone……ý“zý“z /test-7/346worldanyone ö öý“‚1ý“‚1 /test-7/168worldanyoneõõý“wý“w /test-7/208worldanyoneý“z-ý“z- /test-7/347worldanyoneý“‚<ý“‚< /test-7/167worldanyoneëëý“wý“w /test-7/209worldanyone˜˜ý“z=ý“z= /test-7/348worldanyone  ý“‚Dý“‚D /test-7/540worldanyone²²ý“‹Tý“‹T /test-7/409worldanyoneyyý“…ý“… /test-7/161worldanyone´´ý“vÑý“vÑ /test-7/3worldanyonehhý“k¢ý“k¢ /test-7/408worldanyoneqqý“…ý“… /test-7/162worldanyone¾¾ý“vÚý“vÚ /test-7/2worldanyone^^ý“kˆý“kˆ /test-7/542worldanyoneÅÅý“‹Žý“‹Ž /test-7/163worldanyoneÇÇý“våý“vå /test-7/1worldanyoneSSý“kfý“kf /test-7/541worldanyone¼¼ý“‹_ý“‹_ /test-7/164worldanyoneÎÎý“vñý“vñ /test-7/0worldanyoneJJý“kLý“kL /test-7/544worldanyoneÞÞý“‹§ý“‹§ /test-7/405worldanyoneOOý“„Óý“„Ó /test-7/7worldanyoneý“kòý“kò /test-7/543worldanyoneÓÓý“‹œý“‹œ /test-7/404worldanyone@@ý“„Åý“„Å /test-7/6worldanyone‡‡ý“kàý“kà /test-7/546worldanyoneôôý“‹Àý“‹À /test-7/407worldanyoneeeý“„üý“„ü /test-7/5worldanyone~~ý“kËý“kË /test-7/340worldanyone · ·ý“ý“ /test-7/545worldanyoneååý“‹²ý“‹² /test-7/406worldanyone[[ý“„ßý“„ß /test-7/160worldanyoneªªý“vÈý“vÈ /test-7/4worldanyonerrý“kµý“kµ /test-7/548worldanyoneý“‹Ôý“‹Ô /test-7/401worldanyoneý“„ ý“„  /test-7/547worldanyoneüüý“‹Êý“‹Ê /test-7/400worldanyoneý“„“ý“„“ /test-7/403worldanyone::ý“„ºý“„º /test-7/9worldanyone££ý“lý“l /test-7/549worldanyoneý“‹Ýý“‹Ý /test-7/402worldanyone00ý“„®ý“„® /test-7/8worldanyone™™ý“lý“l /test-7/211worldanyone°°ý“zdý“zd /test-7/212worldanyone··ý“zrý“zr /test-7/210worldanyone¥¥ý“zSý“zS /test-7/215worldanyone××ý“z•ý“z• /test-7/318worldanyone Þ Þý“þý“þ /test-7/216worldanyoneââý“z¢ý“z¢ /test-7/319worldanyone è èý“€ ý“€ /test-7/213worldanyoneÃÃý“zý“z /test-7/316worldanyone É Éý“äý“ä /test-7/214worldanyoneÌÌý“z‰ý“z‰ /test-7/317worldanyone Ô Ôý“ðý“ð /test-7/179worldanyoneffý“xYý“xY /test-7/219worldanyone  ý“zÞý“zÞ /test-7/314worldanyone º ºý“Ìý“Ì /test-7/178worldanyone\\ý“xLý“xL /test-7/315worldanyone Ä Äý“Øý“Ø /test-7/177worldanyoneRRý“x@ý“x@ /test-7/217worldanyoneëëý“z­ý“z­ /test-7/312worldanyone ¡ ¡ý“°ý“° /test-7/176worldanyoneHHý“x2ý“x2 /test-7/218worldanyone÷÷ý“zºý“zº /test-7/313worldanyone ¬ ¬ý“¿ý“¿ /test-7/310worldanyone ˆ ˆý“”ý“” /test-7/311worldanyone — —ý“¢ý“¢ /test-7/513worldanyone§§ý“Š;ý“Š; /test-7/418worldanyoneØØý“…“ý“…“ /test-7/170worldanyone  ý“w8ý“w8 /test-7/512worldanyoneŸŸý“Š1ý“Š1 /test-7/417worldanyoneÒÒý“…ƒý“…ƒ /test-7/171worldanyoneý“wDý“wD /test-7/511worldanyone••ý“Š(ý“Š( /test-7/416worldanyoneÇÇý“…wý“…w /test-7/510worldanyone‹‹ý“Š ý“Š /test-7/415worldanyone¿¿ý“…ký“…k /test-7/623worldanyoneý“3ý“3 /test-7/174worldanyone..ý“wpý“wp /test-7/622worldanyoneý“&ý“& /test-7/175worldanyone>>ý“x%ý“x% /test-7/621worldanyoneûûý“ý“ /test-7/172worldanyoneý“wSý“wS /test-7/620worldanyoneññý“ý“ /test-7/419worldanyoneââý“…Ÿý“…Ÿ /test-7/173worldanyone%%ý“wbý“wb /test-7/627worldanyone00ý“[ý“[ /test-7/410worldanyoneƒƒý“…ý“… /test-7/626worldanyone++ý“Oý“O /test-7/625worldanyone##ý“Dý“D /test-7/519worldanyoneããý“Š|ý“Š| /test-7/624worldanyoneý“;ý“; /test-7/518worldanyoneØØý“Šqý“Šq /test-7/517worldanyoneÏÏý“Šhý“Šh /test-7/414worldanyone°°ý“…Wý“…W /test-7/516worldanyoneÃÃý“Š^ý“Š^ /test-7/413worldanyone¥¥ý“…Bý“…B /test-7/629worldanyoneHHý“uý“u /test-7/515worldanyone¹¹ý“ŠSý“ŠS /test-7/412worldanyone˜˜ý“…4ý“…4 /test-7/628worldanyone;;ý“hý“h /test-7/514worldanyone²²ý“ŠGý“ŠG /test-7/411worldanyoneý“…(ý“…( /test-7/220worldanyone  ý“zçý“zç /test-7/221worldanyone  ý“zñý“zñ /test-7/222worldanyone  ý“zýý“zý /test-7/223worldanyone % %ý“{ ý“{ /test-7/224worldanyone 3 3ý“{ý“{ /test-7/327worldanyone 4 4ý“€oý“€o /test-7/225worldanyone = =ý“{%ý“{% /test-7/328worldanyone = =ý“€{ý“€{ /test-7/226worldanyone F Fý“{0ý“{0 /test-7/329worldanyone I Iý“€Œý“€Œ /test-7/227worldanyone P Pý“{;ý“{; /test-7/188worldanyone¸¸ý“xÈý“xÈ /test-7/228worldanyone Z Zý“{Bý“{B /test-7/323worldanyone  ý“€;ý“€; /test-7/187worldanyone®®ý“x¾ý“x¾ /test-7/229worldanyone d dý“{Ný“{N /test-7/324worldanyone  ý“€Hý“€H /test-7/325worldanyone ! !ý“€Uý“€U /test-7/189worldanyoneÀÀý“xØý“xØ /test-7/326worldanyone * *ý“€aý“€a /test-7/320worldanyone ò òý“€ý“€ /test-7/321worldanyone ü üý“€ ý“€ /test-7/322worldanyone  ý“€.ý“€. /test-7/522worldanyoneþþý“Š™ý“Š™ /test-7/427worldanyone;;ý“†ý“† /test-7/521worldanyoneôôý“Šý“Š /test-7/426worldanyone--ý“†ý“† /test-7/180worldanyoneppý“xbý“xb /test-7/524worldanyoneý“Š«ý“Š« /test-7/429worldanyoneKKý“†+ý“†+ /test-7/181worldanyonettý“xný“xn /test-7/523worldanyoneý“Š¢ý“Š¢ /test-7/428worldanyoneEEý“†ý“† /test-7/182worldanyone~~ý“xyý“xy /test-7/610worldanyoneý“Žuý“Žu /test-7/183worldanyoneˆˆý“x„ý“x„ /test-7/184worldanyone‘‘ý“x”ý“x” /test-7/612worldanyone¡¡ý“ޱý“ޱ /test-7/520worldanyoneëëý“Іý“І /test-7/185worldanyoneššý“x¢ý“x¢ /test-7/611worldanyone——ý“ލý“ލ /test-7/186worldanyone¤¤ý“x°ý“x° /test-7/614worldanyone³³ý“ŽÅý“ŽÅ /test-7/613worldanyone««ý“Žºý“Žº /test-7/529worldanyoneDDý“Šàý“Šà /test-7/616worldanyoneÂÂý“ŽÞý“ŽÞ /test-7/421worldanyoneûûý“…Åý“…Å /test-7/615worldanyone¾¾ý“ŽÐý“ŽÐ /test-7/420worldanyoneïïý“…­ý“…­ /test-7/618worldanyoneÞÞý“Žøý“Žø /test-7/526worldanyone&&ý“ŠÁý“ŠÁ /test-7/423worldanyone  ý“…Þý“…Þ /test-7/617worldanyoneÌÌý“Žéý“Žé /test-7/525worldanyoneý“жý“ж /test-7/422worldanyoneý“…Ñý“…Ñ /test-7/528worldanyone::ý“ŠÕý“ŠÕ /test-7/425worldanyoneý“…÷ý“…÷ /test-7/619worldanyoneèèý“ý“ /test-7/527worldanyone00ý“ŠËý“ŠË /test-7/424worldanyoneý“…ìý“…ì /test-7/116worldanyoneééý“t;ý“t; /test-7/117worldanyoneïïý“tGý“tG /test-7/114worldanyoneÕÕý“t%ý“t% /test-7/115worldanyoneßßý“t1ý“t1 /test-7/571worldanyoneûûý“ŒÕý“ŒÕ /test-7/112worldanyoneÆÆý“t ý“t /test-7/570worldanyoneïïý“ŒÊý“ŒÊ /test-7/113worldanyoneËËý“tý“t /test-7/110worldanyone®®ý“síý“sí /test-7/111worldanyone¸¸ý“súý“sú /test-7/118worldanyoneúúý“tXý“tX /test-7/119worldanyoneý“tiý“ti /test-7/435worldanyone‚‚ý“†uý“†u /test-7/436worldanyoneý“†ƒý“†ƒ /test-7/433worldanyonessý“†Wý“†W /test-7/434worldanyone}}ý“†gý“†g /test-7/431worldanyone__ý“†Bý“†B /test-7/700worldanyoneý“”±ý“”± /test-7/432worldanyoneiiý“†Ký“†K /test-7/430worldanyoneUUý“†6ý“†6 /test-7/574worldanyoneý“Œóý“Œó /test-7/575worldanyone$$ý“Œÿý“Œÿ /test-7/572worldanyoneý“Œßý“Œß /test-7/573worldanyoneý“Œêý“Œê /test-7/578worldanyoneEEý“#ý“# /test-7/439worldanyone««ý“†§ý“†§ /test-7/579worldanyonePPý“0ý“0 /test-7/576worldanyone11ý“ ý“ /test-7/437worldanyone——ý“†ý“† /test-7/577worldanyone;;ý“ý“ /test-7/438worldanyone¡¡ý“†ý“† /test-7/125worldanyoneGGý“tÉý“tÉ /test-7/126worldanyonePPý“tÖý“tÖ /test-7/127worldanyone]]ý“tæý“tæ /test-7/300worldanyone ) )ý“ ý“ /test-7/128worldanyoneggý“tòý“tò /test-7/580worldanyone[[ý“;ý“; /test-7/121worldanyoneý“tý“t /test-7/302worldanyone = =ý“6ý“6 /test-7/122worldanyone&&ý“t¢ý“t¢ /test-7/301worldanyone 3 3ý“*ý“* /test-7/582worldanyonenný“Mý“M /test-7/123worldanyone00ý“t¯ý“t¯ /test-7/304worldanyone P Pý“Lý“L /test-7/581worldanyoneffý“Eý“E /test-7/124worldanyone>>ý“t½ý“t½ /test-7/303worldanyone G Gý“@ý“@ /test-7/306worldanyone c cý“bý“b /test-7/305worldanyone Z Zý“Xý“X /test-7/308worldanyone u uý“xý“x /test-7/307worldanyone m mý“jý“j /test-7/129worldanyoneooý“tþý“tþ /test-7/309worldanyone  ý“‡ý“‡ /test-7/444worldanyoneÝÝý“†áý“†á /test-7/445worldanyoneççý“†ìý“†ì /test-7/446worldanyoneññý“†÷ý“†÷ /test-7/447worldanyoneúúý“‡ý“‡ /test-7/440worldanyoneµµý“†¯ý“†¯ /test-7/441worldanyone½½ý“†ºý“†º /test-7/442worldanyoneÆÆý“†Çý“†Ç /test-7/443worldanyoneÑÑý“†Óý“†Ó /test-7/583worldanyonexxý“Vý“V /test-7/584worldanyone‚‚ý“bý“b /test-7/585worldanyoneŒŒý“lý“l /test-7/120worldanyoneý“tzý“tz /test-7/586worldanyone˜˜ý“vý“v /test-7/587worldanyone¢¢ý“‚ý“‚ /test-7/448worldanyoneý“‡ý“‡ /test-7/588worldanyoneªªý“ý“ /test-7/449worldanyoneý“‡ý“‡ /test-7/589worldanyone´´ý“™ý“™ /test-7/134worldanyone¨¨ý“uYý“uY /test-7/135worldanyone­­ý“uiý“ui /test-7/132worldanyoneý“u4ý“u4 /test-7/133worldanyone™™ý“uFý“uF /test-7/138worldanyoneËËý“uý“u /test-7/139worldanyoneÔÔý“uŸý“uŸ /test-7/136worldanyone¶¶ý“uvý“uv /test-7/137worldanyoneÀÀý“uý“u /test-7/450worldanyoneý“‡*ý“‡* /test-7/453worldanyone<<ý“‡Ný“‡N /test-7/454worldanyoneEEý“‡Xý“‡X /test-7/451worldanyone%%ý“‡5ý“‡5 /test-7/452worldanyone11ý“‡Aý“‡A /test-7/457worldanyoneeeý“‡{ý“‡{ /test-7/458worldanyoneppý“‡†ý“‡† /test-7/558worldanyoneuuý“ŒHý“ŒH /test-7/455worldanyonePPý“‡bý“‡b /test-7/559worldanyoneý“ŒSý“ŒS /test-7/456worldanyone\\ý“‡oý“‡o /test-7/556worldanyone]]ý“Œ0ý“Œ0 /test-7/557worldanyoneggý“Œ:ý“Œ: /test-7/554worldanyoneIIý“Œý“Œ /test-7/459worldanyoneý“‡•ý“‡• /test-7/555worldanyoneOOý“Œ$ý“Œ$ /test-7/552worldanyone//ý“‹ÿý“‹ÿ /test-7/131worldanyoneý“u'ý“u' /test-7/553worldanyone99ý“Œ ý“Œ /test-7/130worldanyonewwý“uý“u /test-7/550worldanyoneý“‹éý“‹é /test-7/551worldanyone&&ý“‹ôý“‹ô /test-7/143worldanyoneþþý“uâý“uâ /test-7/144worldanyone  ý“u÷ý“u÷ /test-7/560worldanyone‰‰ý“Œ]ý“Œ] /test-7/145worldanyoneý“vý“v /test-7/146worldanyoneý“vý“v /test-7/147worldanyone''ý“v!ý“v! /test-7/148worldanyone11ý“v0ý“v0 /test-7/149worldanyone@@ý“vAý“vA /test-7/461worldanyone’’ý“‡­ý“‡­ /test-7/460worldanyone††ý“‡ ý“‡  /test-7/462worldanyoneý“‡¹ý“‡¹ /test-7/463worldanyone§§ý“‡Åý“‡Å /test-7/464worldanyone±±ý“‡Ðý“‡Ð /test-7/465worldanyone¼¼ý“‡Üý“‡Ü /test-7/569worldanyoneääý“Œ¿ý“Œ¿ /test-7/466worldanyoneÇÇý“‡çý“‡ç /test-7/467worldanyoneÑÑý“‡òý“‡ò /test-7/468worldanyoneÜÜý“‡ýý“‡ý /test-7/469worldanyoneééý“ˆ ý“ˆ /test-7/565worldanyone¾¾ý“Œ–ý“Œ– /test-7/566worldanyoneÇÇý“Œ ý“Œ  /test-7/567worldanyoneÒÒý“Œªý“Œª /test-7/568worldanyoneÜÜý“Œ´ý“Œ´ /test-7/561worldanyone““ý“Œhý“Œh /test-7/140worldanyoneÞÞý“u±ý“u± /test-7/562worldanyonežžý“Œtý“Œt /test-7/563worldanyone««ý“Œý“Œ /test-7/142worldanyoneôôý“uÕý“uÕ /test-7/564worldanyone³³ý“Œ‹ý“Œ‹ /test-7/141worldanyoneêêý“uÄý“uÄ /test-7/687worldanyone††ý“’Zý“’Z /test-7/686worldanyone||ý“’Pý“’P /test-7/689worldanyoneššý“”7ý“”7 /test-7/688worldanyone““ý“”*ý“”* /test-7/683worldanyoneZZý“’/ý“’/ /test-7/682worldanyoneVVý“’$ý“’$ /test-7/685worldanyoneqqý“’Eý“’E /test-7/684worldanyoneeeý“’:ý“’: /test-7/680worldanyoneAAý“’ý“’ /test-7/681worldanyoneLLý“’ý“’ /test-7/678worldanyone..ý“‘ÿý“‘ÿ /test-7/677worldanyone$$ý“‘óý“‘ó /test-7/676worldanyoneý“‘çý“‘ç /test-7/675worldanyoneý“‘Üý“‘Ü /test-7/674worldanyoneý“‘Ìý“‘Ì /test-7/673worldanyoneüüý“‘¾ý“‘¾ /test-7/672worldanyoneõõý“‘³ý“‘³ /test-7/671worldanyoneëëý“‘«ý“‘« /test-7/679worldanyone88ý“’ý“’ /test-7/670worldanyoneááý“‘¢ý“‘¢ /test-7/696worldanyoneååý“”‚ý“”‚ /test-7/695worldanyoneÙÙý“”wý“”w /test-7/694worldanyoneÐÐý“”ný“”n /test-7/693worldanyoneÄÄý“”bý“”b /test-7/699worldanyoneý“”¦ý“”¦ /test-7/698worldanyoneüüý“”™ý“”™ /test-7/697worldanyoneïïý“”‹ý“”‹ /test-7/691worldanyone¯¯ý“”Lý“”L /test-7/692worldanyoneººý“”Wý“”W /test-7/690worldanyone¢¢ý“”Bý“”B /test-7/646worldanyoneèèý“ý“ /test-7/647worldanyoneôôý“žý“ž /test-7/648worldanyoneý“¬ý“¬ /test-7/649worldanyone  ý“¹ý“¹ /test-7/642worldanyoneÃÃý“ý“ /test-7/195worldanyoneý“yMý“yM /test-7/643worldanyoneÍÍý“tý“t /test-7/194worldanyoneüüý“y8ý“y8 /test-7/644worldanyone××ý“|ý“| /test-7/197worldanyoneý“yqý“yq /test-7/645worldanyoneááý“„ý“„ /test-7/196worldanyoneý“ycý“yc /test-7/191worldanyoneÚÚý“yý“y /test-7/190worldanyoneÌÌý“xîý“xî /test-7/640worldanyoneµµý“ý“ /test-7/193worldanyoneïïý“y"ý“y" /test-7/641worldanyone¿¿ý“ý“ /test-7/192worldanyoneããý“yý“y /test-7/198worldanyone&&ý“yý“y /test-7/199worldanyone00ý“yý“y /test-7/505worldanyoneYYý“‰êý“‰ê /test-7/506worldanyoneddý“‰÷ý“‰÷ /test-7/639worldanyone««ý“øý“ø /test-7/503worldanyoneDDý“‰Óý“‰Ó /test-7/504worldanyoneQQý“‰ßý“‰ß /test-7/637worldanyone™™ý“âý“â /test-7/509worldanyoneý“Šý“Š /test-7/638worldanyone¡¡ý“îý“î /test-7/635worldanyone……ý“Êý“Ê /test-7/507worldanyoneooý“Šý“Š /test-7/636worldanyoneý“×ý“× /test-7/508worldanyonewwý“Š ý“Š /test-7/633worldanyoneooý“«ý“« /test-7/634worldanyone{{ý“ºý“º /test-7/631worldanyone]]ý“ý“ /test-7/632worldanyoneggý“œý“œ /test-7/501worldanyone..ý“‰¹ý“‰¹ /test-7/630worldanyoneWWý“ƒý“ƒ /test-7/502worldanyone::ý“‰Åý“‰Å /test-7/500worldanyone##ý“‰­ý“‰­ /test-7/668worldanyoneÏÏý“‘ý“‘ /test-7/669worldanyone××ý“‘™ý“‘™ /test-7/660worldanyone~~ý“‘4ý“‘4 /test-7/661worldanyoneƒƒý“‘?ý“‘? /test-7/662worldanyoneŽŽý“‘Jý“‘J /test-7/663worldanyone››ý“‘Xý“‘X /test-7/664worldanyone¦¦ý“‘eý“‘e /test-7/665worldanyone¯¯ý“‘pý“‘p /test-7/666worldanyone¹¹ý“‘zý“‘z /test-7/667worldanyoneÃÃý“‘ƒý“‘ƒ /test-7/659worldanyonettý“‘,ý“‘, /test-7/657worldanyone``ý“‘ý“‘ /test-7/658worldanyonejjý“‘"ý“‘" /test-7/651worldanyone##ý“Ôý“Ô /test-7/652worldanyone--ý“ßý“ß /test-7/650worldanyoneý“Æý“Æ /test-7/655worldanyoneMMý“‘ý“‘ /test-7/656worldanyoneVVý“‘ý“‘ /test-7/653worldanyone88ý“êý“ê /test-7/654worldanyoneBBý“öý“ö /test-7/35worldanyone¦¦ý“ný“n /test-7/36worldanyone¯¯ý“ný“n /test-7/33worldanyoneý“mÝý“mÝ /test-7/34worldanyoneý“móý“mó /test-7/39worldanyoneÍÍý“nNý“nN /test-7/37worldanyone¾¾ý“n0ý“n0 /test-7/38worldanyoneÄÄý“n@ý“n@ /test-7/43worldanyoneôôý“n“ý“n“ /test-7/42worldanyoneííý“n‚ý“n‚ /test-7/41worldanyoneããý“nrý“nr /test-7/40worldanyoneØØý“n^ý“n^ /test-7/22worldanyone""ý“mý“m /test-7/23worldanyone((ý“mý“m /test-7/24worldanyone33ý“m%ý“m% /test-7/25worldanyone@@ý“m>ý“m> /test-7/26worldanyoneJJý“mSý“mS /test-7/27worldanyoneSSý“mdý“md /test-7/28worldanyone]]ý“msý“ms /test-7/29worldanyoneggý“m€ý“m€ /test-7/30worldanyoneqqý“m™ý“m™ /test-7/32worldanyone„„ý“m¾ý“m¾ /test-7/31worldanyone||ý“m«ý“m« /test-7/19worldanyoneý“lÂý“l /test-7/17worldanyoneïïý“lœý“lœ /test-7/18worldanyoneøøý“l°ý“l° /test-7/15worldanyoneÛÛý“lý“l /test-7/16worldanyoneååý“l‹ý“l‹ /test-7/13worldanyoneÇÇý“lZý“lZ /test-7/14worldanyoneÑÑý“llý“ll /test-7/11worldanyone³³ý“l8ý“l8 /test-7/12worldanyone½½ý“lHý“lH /test-7/21worldanyoneý“løý“lø /test-7/20worldanyoneý“lÜý“lÜ /test-7/10worldanyone¬¬ý“l(ý“l( /test-7/79worldanyonekký“qŸý“qŸ /test-7/78worldanyoneaaý“q‘ý“q‘ /test-7/77worldanyoneTTý“q€ý“q€ /test-7/82worldanyone……ý“qÃý“qà /test-7/83worldanyoneý“qÐý“qÐ /test-7/80worldanyoneqqý“q¬ý“q¬ /test-7/81worldanyone{{ý“q¶ý“q¶ /test-7/86worldanyone­­ý“qÿý“qÿ /test-7/87worldanyone¹¹ý“rý“r /test-7/84worldanyone››ý“qäý“qä /test-7/85worldanyone¥¥ý“qòý“qò /test-7/67worldanyoneêêý“pvý“pv /test-7/66worldanyoneããý“pfý“pf /test-7/69worldanyoneý“p˜ý“p˜ /test-7/68worldanyoneõõý“pˆý“pˆ /test-7/70worldanyone  ý“p©ý“p© /test-7/71worldanyoneý“q$ý“q$ /test-7/72worldanyoneý“q4ý“q4 /test-7/73worldanyone))ý“qDý“qD /test-7/74worldanyone33ý“qRý“qR /test-7/75worldanyone@@ý“qaý“qa /test-7/76worldanyoneKKý“qrý“qr /test-7/59worldanyone  ý“o÷ý“o÷ /test-7/58worldanyone••ý“oæý“oæ /test-7/57worldanyoneŠŠý“oÔý“oÔ /test-7/56worldanyoneý“oÃý“oà /test-7/55worldanyonessý“o±ý“o± /test-7/64worldanyoneÏÏý“pMý“pM /test-7/65worldanyoneÙÙý“p\ý“p\ /test-7/62worldanyone¾¾ý“p.ý“p. /test-7/63worldanyoneÅÅý“pAý“pA /test-7/60worldanyone©©ý“p ý“p /test-7/61worldanyone±±ý“pý“p /test-7/49worldanyone33ý“o$ý“o$ /test-7/48worldanyone&&ý“oý“o /test-7/45worldanyone  ý“nÈý“nÈ /test-7/44worldanyoneþþý“n¬ý“n¬ /test-7/47worldanyoneý“nôý“nô /test-7/46worldanyoneý“nßý“nß /test-7/51worldanyoneKKý“oQý“oQ /test-7/52worldanyoneTTý“otý“ot /test-7/53worldanyone\\ý“o‡ý“o‡ /test-7/54worldanyoneeeý“o™ý“o™ /test-7/50worldanyone;;ý“o3ý“o3 /test-7/487worldanyone  ý“‰ý“‰ /test-7/281worldanyone u uý“~4ý“~4 /test-7/486worldanyone““ý“ˆñý“ˆñ /test-7/280worldanyone k ký“~'ý“~' /test-7/485worldanyoneˆˆý“ˆÞý“ˆÞ /test-7/484worldanyone‚‚ý“ˆÏý“ˆÏ /test-7/285worldanyone œ œý“~gý“~g /test-7/284worldanyone ’ ’ý“~[ý“~[ /test-7/489worldanyone²²ý“‰ý“‰ /test-7/283worldanyone † †ý“~Mý“~M /test-7/488worldanyone¨¨ý“‰ý“‰ /test-7/282worldanyone  ý“~@ý“~@ /test-7/288worldanyone ¸ ¸ý“~‰ý“~‰ /test-7/289worldanyone  Âý“~’ý“~’ /test-7/286worldanyone ¦ ¦ý“~sý“~s /test-7/287worldanyone ® ®ý“~€ý“~€ /test-7/482worldanyoneooý“ˆºý“ˆº /test-7/483worldanyoneyyý“ˆÄý“ˆÄ /test-7/480worldanyone[[ý“ˆ¨ý“ˆ¨ /test-7/481worldanyoneeeý“ˆ±ý“ˆ± /test-7/474worldanyone$$ý“ˆjý“ˆj /test-7/473worldanyoneý“ˆ]ý“ˆ] /test-7/476worldanyone66ý“ˆ~ý“ˆ~ /test-7/270worldanyone ý ýý“}„ý“}„ /test-7/475worldanyone,,ý“ˆtý“ˆt /test-7/478worldanyoneGGý“ˆ“ý“ˆ“ /test-7/272worldanyone  ý“}¨ý“}¨ /test-7/477worldanyone@@ý“ˆˆý“ˆˆ /test-7/271worldanyone  ý“}‘ý“}‘ /test-7/274worldanyone ' 'ý“}Àý“}À /test-7/479worldanyoneQQý“ˆý“ˆ /test-7/273worldanyone  ý“}´ý“}´ /test-7/275worldanyone 0 0ý“}Íý“}Í /test-7/276worldanyone < <ý“}ñý“}ñ /test-7/277worldanyone E Eý“}þý“}þ /test-7/278worldanyone S Sý“~ ý“~ /test-7/279worldanyone Y Yý“~ý“~ /test-7/470worldanyoneøøý“ˆ5ý“ˆ5 /test-7/471worldanyoneý“ˆBý“ˆB /test-7/472worldanyone  ý“ˆPý“ˆP /test-7/109worldanyone¥¥ý“sàý“sà /test-7/108worldanyone™™ý“sÐý“sÐ /test-7/107worldanyoneý“s¿ý“s¿ /test-7/106worldanyoneƒƒý“s­ý“s­ /test-7/105worldanyoneuuý“sšý“sš /test-7/104worldanyonellý“s‹ý“s‹ /test-7/103worldanyonebbý“swý“sw /test-7/102worldanyoneZZý“siý“si /test-7/99worldanyone<<ý“sý“s /test-7/101worldanyoneKKý“sRý“sR /test-7/100worldanyoneFFý“s@ý“s@ /test-7/595worldanyoneððý“Õý“Õ /test-7/98worldanyone--ý“rôý“rô /test-7/594worldanyoneççý“Ëý“Ë /test-7/97worldanyone""ý“ràý“rà /test-7/597worldanyoneý“êý“ê /test-7/391worldanyone··ý“„ý“„ /test-7/96worldanyoneý“rÄý“rÄ /test-7/596worldanyoneüüý“àý“à /test-7/390worldanyoneªªý“„ý“„ /test-7/95worldanyone  ý“r­ý“r­ /test-7/599worldanyoneý“üý“ü /test-7/393worldanyoneÉÉý“„7ý“„7 /test-7/94worldanyoneý“r˜ý“r˜ /test-7/598worldanyoneý“óý“ó /test-7/392worldanyoneÀÀý“„+ý“„+ /test-7/93worldanyoneõõý“rý“r /test-7/395worldanyoneääý“„Tý“„T /test-7/92worldanyoneììý“rný“rn /test-7/394worldanyone××ý“„Fý“„F /test-7/91worldanyoneááý“rXý“rX /test-7/90worldanyone××ý“rCý“rC /test-7/294worldanyone ï ïý“~×ý“~× /test-7/499worldanyoneý“‰ ý“‰  /test-7/293worldanyone æ æý“~Èý“~È /test-7/296worldanyone  ý“~ñý“~ñ /test-7/295worldanyone ù ùý“~åý“~å /test-7/496worldanyoneøøý“‰zý“‰z /test-7/290worldanyone Ì Ìý“~œý“~œ /test-7/495worldanyoneððý“‰mý“‰m /test-7/498worldanyone  ý“‰’ý“‰’ /test-7/292worldanyone Þ Þý“~¸ý“~¸ /test-7/497worldanyoneý“‰‡ý“‰‡ /test-7/291worldanyone Ï Ïý“~¨ý“~¨ /test-7/491worldanyoneÇÇý“‰5ý“‰5 /test-7/492worldanyoneÒÒý“‰Dý“‰D /test-7/493worldanyoneØØý“‰Qý“‰Q /test-7/494worldanyoneååý“‰`ý“‰` /test-7/297worldanyone  ý“~ûý“~û /test-7/298worldanyone  ý“ý“ /test-7/299worldanyone  ý“ý“ /test-7/490worldanyone½½ý“‰(ý“‰( /test-7/396worldanyoneììý“„bý“„b /test-7/397worldanyone÷÷ý“„ný“„n /test-7/398worldanyoneý“„zý“„z /test-7/399worldanyone  ý“„‡ý“„‡ /test-7/590worldanyoneÁÁý“¤ý“¤ /test-7/591worldanyoneÊÊý“¯ý“¯ /test-7/592worldanyoneÒÒý“¸ý“¸ /test-7/88worldanyoneÂÂý“r ý“r /test-7/593worldanyoneÞÞý“Âý“ /test-7/89worldanyoneÌÌý“r/ý“r/ /test-7/240worldanyone Ø Øý“{óý“{ó /test-7/241worldanyone ã ãý“|ý“| /test-7/383worldanyoneffý“ƒÄý“ƒÄ /test-7/384worldanyoneppý“ƒÎý“ƒÎ /test-7/381worldanyoneTTý“ƒ¬ý“ƒ¬ /test-7/382worldanyone]]ý“ƒ¹ý“ƒ¹ /test-7/380worldanyoneIIý“ƒ ý“ƒ  /test-7/389worldanyone££ý“„ý“„ /test-7/388worldanyone˜˜ý“ƒøý“ƒø /test-7/387worldanyoneŠŠý“ƒìý“ƒì /test-7/386worldanyone‚‚ý“ƒàý“ƒà /test-7/385worldanyonezzý“ƒÖý“ƒÖ /test-7/245worldanyone  ý“|Cý“|C /test-7/244worldanyone  ý“|7ý“|7 /test-7/243worldanyone ö öý“|'ý“|' /test-7/242worldanyone í íý“|ý“| /test-7/249worldanyone 1 1ý“|sý“|s /test-7/248worldanyone ( (ý“|fý“|f /test-7/247worldanyone  ý“|Yý“|Y /test-7/246worldanyone  ý“|Ný“|N /test-7/230worldanyone i iý“{Xý“{X /test-7/370worldanyoneççý“ƒ1ý“ƒ1 /test-7/371worldanyoneóóý“ƒ>ý“ƒ> /test-7/372worldanyoneüüý“ƒIý“ƒI /test-7/373worldanyoneý“ƒUý“ƒU /test-7/379worldanyoneBBý“ƒ–ý“ƒ– /test-7/239worldanyone Í Íý“{áý“{á /test-7/378worldanyone88ý“ƒŒý“ƒŒ /test-7/375worldanyoneý“ƒný“ƒn /test-7/374worldanyoneý“ƒaý“ƒa /test-7/377worldanyone..ý“ƒ‚ý“ƒ‚ /test-7/376worldanyone$$ý“ƒyý“ƒy /test-7/232worldanyone ‚ ‚ý“{uý“{u /test-7/231worldanyone z zý“{hý“{h /test-7/234worldanyone ™ ™ý“{’ý“{’ /test-7/233worldanyone Œ Œý“{„ý“{„ /test-7/236worldanyone « «ý“{®ý“{® /test-7/235worldanyone ž žý“{ý“{ /test-7/238worldanyone Á Áý“{Ïý“{Ï /test-7/237worldanyone º ºý“{¿ý“{¿ /test-7/262worldanyone ² ²ý“}ý“} /test-7/263worldanyone ¹ ¹ý“}#ý“}# /test-7/260worldanyone œ œý“|þý“|þ /test-7/261worldanyone ¨ ¨ý“} ý“} /test-7/361worldanyoneŽŽý“‚Ôý“‚Ô /test-7/362worldanyone™™ý“‚ßý“‚ß /test-7/360worldanyoneƒƒý“‚Êý“‚Ê /test-7/366worldanyoneÀÀý“ƒý“ƒ /test-7/365worldanyone··ý“‚ýý“‚ý /test-7/364worldanyone®®ý“‚óý“‚ó /test-7/363worldanyone¤¤ý“‚êý“‚ê /test-7/369worldanyoneßßý“ƒ%ý“ƒ% /test-7/368worldanyoneÖÖý“ƒý“ƒ /test-7/367worldanyoneËËý“ƒý“ƒ /test-7/269worldanyone ö öý“}wý“}w /test-7/268worldanyone ê êý“}hý“}h /test-7/267worldanyone â âý“}Yý“}Y /test-7/266worldanyone Ø Øý“}Oý“}O /test-7/265worldanyone Î Îý“}Aý“}A /test-7/264worldanyone Ä Äý“}2ý“}2 /test-7/250worldanyone ; ;ý“|~ý“|~ /test-7/251worldanyone C Cý“|‹ý“|‹ /test-7/252worldanyone M Mý“|˜ý“|˜ /test-7/350worldanyoneý“‚Zý“‚Z /test-7/351worldanyone((ý“‚fý“‚f /test-7/353worldanyone::ý“‚|ý“‚| /test-7/352worldanyone33ý“‚sý“‚s /test-7/355worldanyoneQQý“‚‘ý“‚‘ /test-7/354worldanyoneEEý“‚‡ý“‚‡ /test-7/357worldanyoneddý“‚¦ý“‚¦ /test-7/356worldanyone[[ý“‚œý“‚œ /test-7/359worldanyone{{ý“‚¿ý“‚¿ /test-7/358worldanyoneppý“‚²ý“‚² /test-7/258worldanyone ‹ ‹ý“|âý“|â /test-7/257worldanyone y yý“|Ñý“|Ñ /test-7/259worldanyone – –ý“|òý“|ò /test-7/254worldanyone ` `ý“|¯ý“|¯ /test-7/253worldanyone W Wý“|£ý“|£ /test-7/256worldanyone u uý“|Äý“|Ä /test-7/255worldanyone j jý“|¸ý“|¸/test-8worldanyoneLLý“kYý“kYÅ /test-8/338worldanyone ¡ ¡ý“€éý“€é /test-8/339worldanyone ¬ ¬ý“€ôý“€ô /test-8/332worldanyone d dý“€¨ý“€¨ /test-8/333worldanyone m mý“€²ý“€² /test-8/159worldanyone||ý“vŽý“vŽ /test-8/330worldanyone P Pý“€’ý“€’ /test-8/158worldanyonerrý“v„ý“v„ /test-8/331worldanyone X Xý“€ý“€ /test-8/157worldanyonehhý“vyý“vy /test-8/336worldanyone ‹ ‹ý“€Òý“€Ò /test-8/156worldanyone^^ý“vlý“vl /test-8/337worldanyone — —ý“€Þý“€Þ /test-8/155worldanyoneXXý“v`ý“v` /test-8/334worldanyone w wý“€½ý“€½ /test-8/154worldanyoneNNý“vWý“vW /test-8/335worldanyone  ý“€Æý“€Æ /test-8/601worldanyoneý“æý“æ /test-8/531worldanyone>>ý“ŠØý“ŠØ /test-8/152worldanyone<<ý“v@ý“v@ /test-8/600worldanyoneøøý“Üý“Ü /test-8/530worldanyone44ý“ŠÏý“ŠÏ /test-8/153worldanyoneDDý“vMý“vM /test-8/150worldanyone&&ý“v!ý“v! /test-8/151worldanyone33ý“v1ý“v1 /test-8/535worldanyoneeeý“‹ý“‹ /test-8/534worldanyoneZZý“Šøý“Šø /test-8/533worldanyoneMMý“Šìý“Šì /test-8/532worldanyoneHHý“Šâý“Šâ /test-8/609worldanyoneKKý“Ž5ý“Ž5 /test-8/539worldanyone’’ý“‹3ý“‹3 /test-8/608worldanyoneEEý“Ž)ý“Ž) /test-8/538worldanyoneˆˆý“‹*ý“‹* /test-8/607worldanyone<<ý“Ž!ý“Ž! /test-8/537worldanyonezzý“‹ý“‹ /test-8/606worldanyone22ý“Žý“Ž /test-8/536worldanyoneqqý“‹ý“‹ /test-8/605worldanyone((ý“Žý“Ž /test-8/604worldanyone  ý“Žý“Ž /test-8/603worldanyoneý“úý“ú /test-8/602worldanyone  ý“îý“î /test-8/202worldanyone22ý“y’ý“y’ /test-8/349worldanyoneý“‚Pý“‚P /test-8/203worldanyone55ý“yŸý“yŸ /test-8/204worldanyone??ý“y¯ý“y¯ /test-8/205worldanyoneHHý“y»ý“y» /test-8/200worldanyone!!ý“ywý“yw /test-8/201worldanyone((ý“y…ý“y… /test-8/341worldanyone À Àý“ ý“ /test-8/169worldanyoneååý“wý“w /test-8/342worldanyone Ë Ëý“ý“ /test-8/343worldanyone Õ Õý“‚ ý“‚ /test-8/344worldanyone à àý“‚ý“‚ /test-8/166worldanyoneÃÃý“väý“vä /test-8/206worldanyoneQQý“yÉý“yÉ /test-8/345worldanyone ê êý“‚&ý“‚& /test-8/165worldanyone¸¸ý“v×ý“v× /test-8/207worldanyoneZZý“yÕý“yÕ /test-8/346worldanyone ô ôý“‚1ý“‚1 /test-8/168worldanyoneÛÛý“wý“w /test-8/208worldanyone``ý“yâý“yâ /test-8/347worldanyone ÿ ÿý“‚;ý“‚; /test-8/167worldanyoneÒÒý“vóý“vó /test-8/209worldanyonejjý“yòý“yò /test-8/348worldanyone  ý“‚Dý“‚D /test-8/540worldanyoneššý“‹=ý“‹= /test-8/409worldanyonewwý“…ý“… /test-8/161worldanyoneý“v¦ý“v¦ /test-8/3worldanyoneuuý“k¼ý“k¼ /test-8/408worldanyonemmý“…ý“… /test-8/162worldanyoneššý“v°ý“v° /test-8/2worldanyonemmý“k­ý“k­ /test-8/542worldanyone±±ý“‹Sý“‹S /test-8/163worldanyone¢¢ý“v¿ý“v¿ /test-8/1worldanyoneddý“k›ý“k› /test-8/541worldanyone¦¦ý“‹Hý“‹H /test-8/164worldanyone°°ý“vÍý“vÍ /test-8/0worldanyoneVVý“ktý“kt /test-8/544worldanyoneÆÆý“‹Žý“‹Ž /test-8/405worldanyoneLLý“„Óý“„Ó /test-8/7worldanyoneœœý“lý“l /test-8/543worldanyone¸¸ý“‹^ý“‹^ /test-8/404worldanyoneDDý“„Æý“„Æ /test-8/6worldanyone““ý“kõý“kõ /test-8/546worldanyoneÚÚý“‹¦ý“‹¦ /test-8/407worldanyoneccý“„ûý“„û /test-8/5worldanyone……ý“kßý“kß /test-8/340worldanyone ¶ ¶ý“€ÿý“€ÿ /test-8/545worldanyoneÐÐý“‹›ý“‹› /test-8/406worldanyoneYYý“„ßý“„ß /test-8/160worldanyone††ý“v›ý“v› /test-8/4worldanyoneý“kÍý“kÍ /test-8/548worldanyoneììý“‹¼ý“‹¼ /test-8/401worldanyone""ý“„¡ý“„¡ /test-8/547worldanyoneââý“‹²ý“‹² /test-8/400worldanyoneý“„”ý“„” /test-8/403worldanyone88ý“„ºý“„º /test-8/9worldanyoneµµý“l:ý“l: /test-8/549worldanyoneööý“‹Åý“‹Å /test-8/402worldanyone//ý“„®ý“„® /test-8/8worldanyone©©ý“l&ý“l& /test-8/211worldanyone~~ý“z ý“z /test-8/212worldanyone„„ý“zý“z /test-8/210worldanyonettý“yþý“yþ /test-8/215worldanyone§§ý“zTý“zT /test-8/318worldanyone Ï Ïý“æý“æ /test-8/216worldanyone³³ý“zhý“zh /test-8/319worldanyone Ú Úý“ôý“ô /test-8/213worldanyoneý“z,ý“z, /test-8/316worldanyone » »ý“Ñý“Ñ /test-8/214worldanyoneý“zAý“zA /test-8/317worldanyone Å Åý“Úý“Ú /test-8/179worldanyoneIIý“x3ý“x3 /test-8/219worldanyoneÐÐý“z’ý“z’ /test-8/314worldanyone § §ý“¼ý“¼ /test-8/178worldanyone??ý“x'ý“x' /test-8/315worldanyone ± ±ý“Æý“Æ /test-8/177worldanyone55ý“xý“x /test-8/217worldanyone½½ý“zyý“zy /test-8/312worldanyone • •ý“¡ý“¡ /test-8/176worldanyone00ý“wpý“wp /test-8/218worldanyoneÆÆý“z…ý“z… /test-8/313worldanyone ž žý“¯ý“¯ /test-8/310worldanyone } }ý“…ý“… /test-8/311worldanyone ‰ ‰ý“”ý“” /test-8/513worldanyone‰‰ý“Šý“Š /test-8/418worldanyoneÐÐý“…ƒý“…ƒ /test-8/170worldanyoneððý“wý“w /test-8/512worldanyoneý“Šý“Š /test-8/417worldanyoneÉÉý“…wý“…w /test-8/171worldanyoneùùý“w(ý“w( /test-8/511worldanyoneuuý“Šý“Š /test-8/416worldanyone½½ý“…ký“…k /test-8/510worldanyonekký“‰þý“‰þ /test-8/415worldanyoneµµý“…Yý“…Y /test-8/623worldanyoneÒÒý“Žëý“Žë /test-8/174worldanyoneý“wLý“wL /test-8/622worldanyoneËËý“Žßý“Žß /test-8/175worldanyone&&ý“wbý“wb /test-8/621worldanyone»»ý“ŽÐý“ŽÐ /test-8/172worldanyoneý“w3ý“w3 /test-8/620worldanyoneµµý“ŽÅý“ŽÅ /test-8/419worldanyoneÙÙý“…“ý“…“ /test-8/173worldanyoneý“w>ý“w> /test-8/627worldanyoneùùý“ý“ /test-8/410worldanyoneý“…ý“… /test-8/626worldanyoneððý“ý“ /test-8/625worldanyoneããý“ý“ /test-8/519worldanyoneÂÂý“Š\ý“Š\ /test-8/624worldanyoneÛÛý“Ž÷ý“Ž÷ /test-8/518worldanyone¸¸ý“ŠRý“ŠR /test-8/517worldanyone­­ý“ŠGý“ŠG /test-8/414worldanyone££ý“…Aý“…A /test-8/516worldanyone¥¥ý“Š;ý“Š; /test-8/413worldanyoneý“…6ý“…6 /test-8/629worldanyoneý“3ý“3 /test-8/515worldanyoneœœý“Š/ý“Š/ /test-8/412worldanyone““ý“…,ý“…, /test-8/628worldanyoneý“&ý“& /test-8/514worldanyone‘‘ý“Š$ý“Š$ /test-8/411worldanyone‰‰ý“…#ý“…# /test-8/220worldanyoneÚÚý“zý“z /test-8/221worldanyoneääý“z§ý“z§ /test-8/222worldanyoneîîý“z°ý“z° /test-8/223worldanyoneøøý“z»ý“z» /test-8/224worldanyone  ý“zÝý“zÝ /test-8/327worldanyone - -ý“€gý“€g /test-8/225worldanyone  ý“zèý“zè /test-8/328worldanyone 9 9ý“€wý“€w /test-8/226worldanyone  ý“zôý“zô /test-8/329worldanyone C Cý“€…ý“€… /test-8/227worldanyone ! !ý“zÿý“zÿ /test-8/188worldanyoneœœý“x£ý“x£ /test-8/228worldanyone ) )ý“{ ý“{ /test-8/323worldanyone  ý“€,ý“€, /test-8/187worldanyone””ý“x•ý“x• /test-8/229worldanyone 5 5ý“{ý“{ /test-8/324worldanyone  ý“€>ý“€> /test-8/325worldanyone  ý“€Lý“€L /test-8/189worldanyone««ý“xµý“xµ /test-8/326worldanyone # #ý“€Zý“€Z /test-8/320worldanyone ç çý“€ý“€ /test-8/321worldanyone ñ ñý“€ý“€ /test-8/322worldanyone ù ùý“€ý“€ /test-8/522worldanyoneààý“Š{ý“Š{ /test-8/427worldanyone++ý“†ý“† /test-8/521worldanyoneÖÖý“Šmý“Šm /test-8/426worldanyone!!ý“…÷ý“…÷ /test-8/180worldanyoneQQý“x@ý“x@ /test-8/524worldanyoneõõý“Šý“Š /test-8/429worldanyoneCCý“†ý“† /test-8/181worldanyone[[ý“xKý“xK /test-8/523worldanyoneêêý“Š…ý“Š… /test-8/428worldanyone::ý“†ý“† /test-8/182worldanyoneggý“xYý“xY /test-8/610worldanyoneUUý“Ž=ý“Ž= /test-8/183worldanyoneqqý“xcý“xc /test-8/184worldanyoneuuý“xoý“xo /test-8/612worldanyoneiiý“ŽNý“ŽN /test-8/520worldanyoneÌÌý“Šeý“Še /test-8/185worldanyoneý“x|ý“x| /test-8/611worldanyone__ý“ŽFý“ŽF /test-8/186worldanyone‰‰ý“x‰ý“x‰ /test-8/614worldanyonexxý“Žaý“Ža /test-8/613worldanyonenný“ŽXý“ŽX /test-8/529worldanyone**ý“ŠÄý“ŠÄ /test-8/616worldanyoneŒŒý“Žtý“Žt /test-8/421worldanyoneîîý“…­ý“…­ /test-8/615worldanyone‚‚ý“Žký“Žk /test-8/420worldanyoneääý“…Ÿý“…Ÿ /test-8/618worldanyone  ý“ެý“ެ /test-8/526worldanyone  ý“Š¥ý“Š¥ /test-8/423worldanyoneý“…Òý“…Ò /test-8/617worldanyone––ý“Ž¢ý“Ž¢ /test-8/525worldanyoneý“Ššý“Šš /test-8/422worldanyoneøøý“…Äý“…Ä /test-8/528worldanyoneý“Џý“Џ /test-8/425worldanyoneý“…ìý“…ì /test-8/619worldanyone¦¦ý“޹ý“޹ /test-8/527worldanyoneý“Š®ý“Š® /test-8/424worldanyoneý“…ßý“…ß /test-8/116worldanyoneØØý“t(ý“t( /test-8/117worldanyoneààý“t4ý“t4 /test-8/114worldanyoneÇÇý“tý“t /test-8/115worldanyoneÎÎý“tý“t /test-8/571worldanyoneØØý“Œ®ý“Œ® /test-8/112worldanyone¯¯ý“sîý“sî /test-8/570worldanyoneÎÎý“Œ¥ý“Œ¥ /test-8/113worldanyone½½ý“sýý“sý /test-8/110worldanyone››ý“sÒý“sÒ /test-8/111worldanyone££ý“sßý“sß /test-8/118worldanyoneëëý“tAý“tA /test-8/119worldanyoneôôý“tMý“tM /test-8/435worldanyoneyyý“†fý“†f /test-8/436worldanyone‰‰ý“†wý“†w /test-8/433worldanyonellý“†Pý“†P /test-8/434worldanyonevvý“†Zý“†Z /test-8/431worldanyoneWWý“†7ý“†7 /test-8/700worldanyoneÕÕý“”tý“”t /test-8/432worldanyoneaaý“†Cý“†C /test-8/430worldanyoneRRý“†,ý“†, /test-8/703worldanyoneóóý“”’ý“”’ /test-8/574worldanyoneööý“ŒÌý“ŒÌ /test-8/704worldanyoneýýý“”ý“” /test-8/575worldanyoneý“ŒØý“ŒØ /test-8/701worldanyoneááý“”~ý“”~ /test-8/572worldanyoneââý“Œ·ý“Œ· /test-8/702worldanyoneééý“”ˆý“”ˆ /test-8/573worldanyoneììý“ŒÁý“ŒÁ /test-8/707worldanyoneý“”½ý“”½ /test-8/578worldanyoneý“Œ÷ý“Œ÷ /test-8/439worldanyone  ý“†™ý“†™ /test-8/708worldanyone""ý“”Æý“”Æ /test-8/579worldanyone&&ý“ý“ /test-8/705worldanyoneý“”§ý“”§ /test-8/576worldanyone  ý“Œãý“Œã /test-8/437worldanyoneŒŒý“†‚ý“†‚ /test-8/706worldanyoneý“”²ý“”² /test-8/577worldanyoneý“Œíý“Œí /test-8/438worldanyone––ý“†ý“† /test-8/125worldanyone**ý“t¦ý“t¦ /test-8/126worldanyone33ý“t°ý“t° /test-8/127worldanyone==ý“t½ý“t½ /test-8/300worldanyone  ý“ý“ /test-8/128worldanyoneFFý“tÉý“tÉ /test-8/580worldanyone33ý“ ý“ /test-8/121worldanyoneý“tlý“tl /test-8/302worldanyone * *ý“ ý“ /test-8/122worldanyoneý“t|ý“t| /test-8/301worldanyone  ý“ý“ /test-8/582worldanyoneHHý“$ý“$ /test-8/123worldanyoneý“tŒý“tŒ /test-8/304worldanyone ? ?ý“:ý“: /test-8/581worldanyone??ý“ý“ /test-8/124worldanyone  ý“tœý“tœ /test-8/303worldanyone 5 5ý“.ý“. /test-8/306worldanyone R Rý“Oý“O /test-8/305worldanyone I Iý“Eý“E /test-8/308worldanyone f fý“fý“f /test-8/307worldanyone \ \ý“Yý“Y /test-8/129worldanyoneOOý“tÕý“tÕ /test-8/309worldanyone s sý“xý“x /test-8/444worldanyoneÒÒý“†Óý“†Ó /test-8/445worldanyoneØØý“†àý“†à /test-8/446worldanyoneââý“†êý“†ê /test-8/447worldanyoneììý“†õý“†õ /test-8/440worldanyoneªªý“†¢ý“†¢ /test-8/441worldanyone´´ý“†¬ý“†¬ /test-8/442worldanyone¹¹ý“†ºý“†º /test-8/443worldanyoneÈÈý“†Çý“†Ç /test-8/583worldanyoneQQý“0ý“0 /test-8/584worldanyoneZZý“;ý“; /test-8/585worldanyoneddý“Cý“C /test-8/120worldanyoneÿÿý“t\ý“t\ /test-8/586worldanyoneooý“Ný“N /test-8/587worldanyoneyyý“Vý“V /test-8/448worldanyoneüüý“‡ý“‡ /test-8/588worldanyoneƒƒý“bý“b /test-8/449worldanyoneý“‡ý“‡ /test-8/589worldanyoneŽŽý“mý“m /test-8/134worldanyone††ý“u*ý“u* /test-8/135worldanyoneý“u5ý“u5 /test-8/132worldanyonerrý“uý“u /test-8/133worldanyone||ý“u!ý“u! /test-8/138worldanyone®®ý“uký“uk /test-8/139worldanyoneººý“u{ý“u{ /test-8/136worldanyone••ý“uAý“uA /test-8/137worldanyone§§ý“uYý“uY /test-8/450worldanyone  ý“‡ý“‡ /test-8/453worldanyone..ý“‡@ý“‡@ /test-8/454worldanyone99ý“‡Lý“‡L /test-8/451worldanyoneý“‡)ý“‡) /test-8/452worldanyone$$ý“‡4ý“‡4 /test-8/457worldanyoneWWý“‡jý“‡j /test-8/458worldanyoneaaý“‡vý“‡v /test-8/558worldanyoneQQý“Œ$ý“Œ$ /test-8/455worldanyoneCCý“‡Vý“‡V /test-8/559worldanyone[[ý“Œ/ý“Œ/ /test-8/456worldanyoneMMý“‡aý“‡a /test-8/556worldanyone<<ý“Œ ý“Œ /test-8/557worldanyoneDDý“Œý“Œ /test-8/554worldanyone''ý“‹ôý“‹ô /test-8/459worldanyonekký“‡€ý“‡€ /test-8/555worldanyone22ý“‹ÿý“‹ÿ /test-8/552worldanyoneý“‹àý“‹à /test-8/131worldanyonejjý“tõý“tõ /test-8/553worldanyoneý“‹êý“‹ê /test-8/130worldanyone^^ý“tæý“tæ /test-8/550worldanyoneý“‹Îý“‹Î /test-8/551worldanyone  ý“‹×ý“‹× /test-8/143worldanyoneææý“u¿ý“u¿ /test-8/144worldanyoneïïý“uÍý“uÍ /test-8/560worldanyoneffý“Œ:ý“Œ: /test-8/145worldanyoneúúý“uÙý“uÙ /test-8/146worldanyoneý“uçý“uç /test-8/147worldanyoneý“uùý“uù /test-8/148worldanyoneý“vý“v /test-8/149worldanyoneý“vý“v /test-8/461worldanyone}}ý“‡”ý“‡” /test-8/460worldanyoneuuý“‡‰ý“‡‰ /test-8/462worldanyone„„ý“‡ ý“‡  /test-8/463worldanyoneŽŽý“‡«ý“‡« /test-8/464worldanyone™™ý“‡¶ý“‡¶ /test-8/465worldanyone¤¤ý“‡Áý“‡Á /test-8/569worldanyoneÂÂý“Œšý“Œš /test-8/466worldanyone­­ý“‡Êý“‡Ê /test-8/467worldanyone··ý“‡Õý“‡Õ /test-8/468worldanyoneÁÁý“‡áý“‡á /test-8/469worldanyoneÊÊý“‡ëý“‡ë /test-8/565worldanyoneššý“Œtý“Œt /test-8/566worldanyone¤¤ý“Œ~ý“Œ~ /test-8/567worldanyone®®ý“Œ‡ý“Œ‡ /test-8/568worldanyone¸¸ý“Œý“Œ /test-8/561worldanyoneooý“ŒGý“ŒG /test-8/140worldanyoneÄÄý“uŒý“uŒ /test-8/562worldanyone{{ý“ŒQý“ŒQ /test-8/563worldanyone……ý“Œ]ý“Œ] /test-8/142worldanyoneÚÚý“u®ý“u® /test-8/564worldanyoneý“Œhý“Œh /test-8/141worldanyoneÒÒý“užý“už /test-8/687worldanyoneQQý“’"ý“’" /test-8/686worldanyoneFFý“’ý“’ /test-8/689worldanyoneggý“’;ý“’; /test-8/688worldanyone\\ý“’0ý“’0 /test-8/683worldanyone**ý“‘÷ý“‘÷ /test-8/682worldanyoneý“‘êý“‘ê /test-8/685worldanyone::ý“’ ý“’ /test-8/684worldanyone22ý“’ý“’ /test-8/680worldanyone  ý“‘Íý“‘Í /test-8/681worldanyoneý“‘Ýý“‘Ý /test-8/678worldanyoneùùý“‘¹ý“‘¹ /test-8/677worldanyoneïïý“‘°ý“‘° /test-8/676worldanyoneççý“‘¦ý“‘¦ /test-8/675worldanyoneÞÞý“‘›ý“‘› /test-8/674worldanyoneÓÓý“‘ý“‘ /test-8/673worldanyoneÆÆý“‘„ý“‘„ /test-8/672worldanyone»»ý“‘zý“‘z /test-8/671worldanyone³³ý“‘pý“‘p /test-8/679worldanyoneý“‘Âý“‘ /test-8/670worldanyone¥¥ý“‘eý“‘e /test-8/696worldanyone®®ý“”Lý“”L /test-8/695worldanyone¤¤ý“”Cý“”C /test-8/694worldanyone™™ý“”6ý“”6 /test-8/693worldanyoneý“”)ý“”) /test-8/699worldanyoneËËý“”jý“”j /test-8/698worldanyoneÂÂý“”`ý“”` /test-8/697worldanyone··ý“”Vý“”V /test-8/691worldanyonezzý“’Ný“’N /test-8/692worldanyone„„ý“’Wý“’W /test-8/690worldanyoneooý“’Dý“’D /test-8/646worldanyone¼¼ý“ý“ /test-8/647worldanyoneÅÅý“ý“ /test-8/648worldanyoneÐÐý“uý“u /test-8/649worldanyoneÚÚý“€ý“€ /test-8/642worldanyone––ý“ßý“ß /test-8/195worldanyoneêêý“yý“y /test-8/643worldanyoneŸŸý“êý“ê /test-8/194worldanyoneááý“y ý“y /test-8/644worldanyone©©ý“õý“õ /test-8/197worldanyoneý“yDý“yD /test-8/645worldanyone´´ý“ÿý“ÿ /test-8/196worldanyoneõõý“y-ý“y- /test-8/191worldanyone¿¿ý“xÖý“xÖ /test-8/190worldanyone³³ý“xÅý“xÅ /test-8/640worldanyone‚‚ý“Åý“Å /test-8/193worldanyoneÕÕý“xúý“xú /test-8/641worldanyone‹‹ý“Óý“Ó /test-8/192worldanyoneÉÉý“xéý“xé /test-8/198worldanyone  ý“yWý“yW /test-8/199worldanyoneý“yiý“yi /test-8/505worldanyone99ý“‰Åý“‰Å /test-8/506worldanyoneFFý“‰Ôý“‰Ô /test-8/639worldanyone||ý“ºý“º /test-8/503worldanyone!!ý“‰­ý“‰­ /test-8/504worldanyone00ý“‰ºý“‰º /test-8/637worldanyoneeeý“›ý“› /test-8/509worldanyoneaaý“‰õý“‰õ /test-8/638worldanyoneqqý“«ý“« /test-8/635worldanyoneUUý“‚ý“‚ /test-8/507worldanyoneMMý“‰Þý“‰Þ /test-8/636worldanyone__ý“ý“ /test-8/508worldanyoneVVý“‰êý“‰ê /test-8/633worldanyone>>ý“hý“h /test-8/634worldanyoneFFý“tý“t /test-8/631worldanyone**ý“Ný“N /test-8/632worldanyone88ý“\ý“\ /test-8/501worldanyoneý“‰™ý“‰™ /test-8/630worldanyoneý“Aý“A /test-8/502worldanyoneý“‰¢ý“‰¢ /test-8/500worldanyoneý“‰ý“‰ /test-8/668worldanyone••ý“‘Qý“‘Q /test-8/669worldanyoneŸŸý“‘Zý“‘Z /test-8/660worldanyoneHHý“‘ý“‘ /test-8/661worldanyoneRRý“‘ ý“‘ /test-8/662worldanyone\\ý“‘ý“‘ /test-8/663worldanyoneffý“‘ý“‘ /test-8/664worldanyoneooý“‘'ý“‘' /test-8/665worldanyonexxý“‘3ý“‘3 /test-8/666worldanyone„„ý“‘?ý“‘? /test-8/667worldanyone‹‹ý“‘Hý“‘H /test-8/659worldanyone==ý“ôý“ô /test-8/657worldanyone++ý“Þý“Þ /test-8/658worldanyone77ý“êý“ê /test-8/651worldanyoneððý“ý“ /test-8/652worldanyoneúúý“¨ý“¨ /test-8/650worldanyoneääý“ý“ /test-8/655worldanyoneý“Çý“Ç /test-8/656worldanyone!!ý“Ôý“Ô /test-8/653worldanyoneý“³ý“³ /test-8/654worldanyoneý“½ý“½ /test-8/35worldanyone»»ý“n/ý“n/ /test-8/36worldanyoneÇÇý“n@ý“n@ /test-8/33worldanyone««ý“ný“n /test-8/34worldanyone³³ý“ný“n /test-8/39worldanyoneääý“nwý“nw /test-8/37worldanyoneÔÔý“nRý“nR /test-8/38worldanyoneÞÞý“ndý“nd /test-8/43worldanyone  ý“nÇý“nÇ /test-8/42worldanyoneý“n±ý“n± /test-8/41worldanyoneøøý“n˜ý“n˜ /test-8/40worldanyoneîîý“ný“n /test-8/22worldanyone99ý“m*ý“m* /test-8/23worldanyone<<ý“m:ý“m: /test-8/24worldanyoneFFý“mPý“mP /test-8/25worldanyoneQQý“m^ý“m^ /test-8/26worldanyone[[ý“mký“mk /test-8/27worldanyone``ý“m|ý“m| /test-8/28worldanyonettý“mšý“mš /test-8/29worldanyone~~ý“m­ý“m­ /test-8/30worldanyoneˆˆý“mÁý“mÁ /test-8/32worldanyone  ý“möý“mö /test-8/31worldanyone””ý“mâý“mâ /test-8/19worldanyoneý“lûý“lû /test-8/17worldanyoneý“lÑý“lÑ /test-8/18worldanyoneý“lêý“lê /test-8/15worldanyoneõõý“l«ý“l« /test-8/16worldanyoneýýý“l¾ý“l¾ /test-8/13worldanyoneááý“lˆý“lˆ /test-8/14worldanyoneîîý“l›ý“l› /test-8/11worldanyoneÌÌý“l`ý“l` /test-8/12worldanyoneÓÓý“lpý“lp /test-8/21worldanyone..ý“mý“m /test-8/20worldanyone$$ý“m ý“m /test-8/10worldanyone¿¿ý“lJý“lJ /test-8/79worldanyoneooý“q§ý“q§ /test-8/78worldanyoneeeý“qý“q /test-8/77worldanyone[[ý“qý“q /test-8/82worldanyoneˆˆý“qÊý“qÊ /test-8/83worldanyone’’ý“qØý“qØ /test-8/80worldanyoneyyý“q°ý“q° /test-8/81worldanyone‚‚ý“q½ý“q½ /test-8/86worldanyone¯¯ý“rý“r /test-8/87worldanyone¸¸ý“rý“r /test-8/84worldanyone™™ý“qâý“qâ /test-8/85worldanyone¤¤ý“qñý“qñ /test-8/67worldanyoneööý“pˆý“pˆ /test-8/66worldanyoneííý“pwý“pw /test-8/69worldanyone  ý“p«ý“p« /test-8/68worldanyoneý“p—ý“p— /test-8/70worldanyoneý“q$ý“q$ /test-8/71worldanyoneý“q5ý“q5 /test-8/72worldanyone''ý“qBý“qB /test-8/73worldanyone11ý“qPý“qP /test-8/74worldanyone;;ý“q^ý“q^ /test-8/75worldanyoneFFý“qpý“qp /test-8/76worldanyonePPý“q~ý“q~ /test-8/59worldanyone¤¤ý“pý“p /test-8/58worldanyoneššý“oõý“oõ /test-8/57worldanyoneý“oâý“oâ /test-8/56worldanyoneˆˆý“oÒý“oÒ /test-8/55worldanyone||ý“oÀý“oÀ /test-8/64worldanyoneØØý“pTý“pT /test-8/65worldanyoneââý“peý“pe /test-8/62worldanyoneÄÄý“p8ý“p8 /test-8/63worldanyoneÎÎý“pFý“pF /test-8/60worldanyone®®ý“pý“p /test-8/61worldanyone¸¸ý“p!ý“p! /test-8/49worldanyoneFFý“oNý“oN /test-8/48worldanyone<<ý“o4ý“o4 /test-8/45worldanyoneý“nøý“nø /test-8/44worldanyoneý“nÜý“nÜ /test-8/47worldanyone22ý“o"ý“o" /test-8/46worldanyone))ý“oý“o /test-8/51worldanyoneXXý“o~ý“o~ /test-8/52worldanyoneccý“o‘ý“o‘ /test-8/53worldanyonemmý“o£ý“o£ /test-8/54worldanyonevvý“o³ý“o³ /test-8/50worldanyoneNNý“oný“on /test-8/487worldanyoneý“ˆâý“ˆâ /test-8/281worldanyone K Ký“}ÿý“}ÿ /test-8/486worldanyone~~ý“ˆÏý“ˆÏ /test-8/280worldanyone ? ?ý“}òý“}ò /test-8/485worldanyonettý“ˆÂý“ˆÂ /test-8/484worldanyonekký“ˆ¸ý“ˆ¸ /test-8/285worldanyone r rý“~4ý“~4 /test-8/284worldanyone e eý“~'ý“~' /test-8/489worldanyonežžý“‰ý“‰ /test-8/283worldanyone a aý“~ý“~ /test-8/488worldanyone––ý“ˆòý“ˆò /test-8/282worldanyone U Uý“~ ý“~ /test-8/288worldanyone  ý“~Zý“~Z /test-8/289worldanyone š šý“~gý“~g /test-8/286worldanyone { {ý“~@ý“~@ /test-8/287worldanyone ‰ ‰ý“~Ný“~N /test-8/482worldanyoneWWý“ˆŸý“ˆŸ /test-8/483worldanyone^^ý“ˆ«ý“ˆ« /test-8/480worldanyone>>ý“ˆ‡ý“ˆ‡ /test-8/481worldanyoneIIý“ˆ”ý“ˆ” /test-8/474worldanyoneýýý“ˆAý“ˆA /test-8/473worldanyoneóóý“ˆ5ý“ˆ5 /test-8/476worldanyoneý“ˆ[ý“ˆ[ /test-8/270worldanyone ß ßý“}Wý“}W /test-8/475worldanyone  ý“ˆOý“ˆO /test-8/478worldanyone((ý“ˆqý“ˆq /test-8/272worldanyone ó óý“}uý“}u /test-8/477worldanyoneý“ˆfý“ˆf /test-8/271worldanyone æ æý“}fý“}f /test-8/274worldanyone  ý“}’ý“}’ /test-8/479worldanyone33ý“ˆ{ý“ˆ{ /test-8/273worldanyone ü üý“}„ý“}„ /test-8/275worldanyone  ý“}¥ý“}¥ /test-8/276worldanyone  ý“}³ý“}³ /test-8/277worldanyone # #ý“}¾ý“}¾ /test-8/278worldanyone - -ý“}Êý“}Ê /test-8/279worldanyone 7 7ý“}Óý“}Ó /test-8/470worldanyoneÔÔý“‡õý“‡õ /test-8/471worldanyoneÞÞý“‡ÿý“‡ÿ /test-8/472worldanyoneèèý“ˆ ý“ˆ /test-8/109worldanyone‘‘ý“sÇý“sÇ /test-8/108worldanyoneˆˆý“s·ý“s· /test-8/107worldanyone~~ý“sªý“sª /test-8/106worldanyonevvý“sœý“sœ /test-8/105worldanyonenný“sŒý“sŒ /test-8/104worldanyone^^ý“suý“su /test-8/103worldanyoneVVý“sgý“sg /test-8/102worldanyoneOOý“sSý“sS /test-8/99worldanyone00ý“rúý“rú /test-8/101worldanyoneBBý“s@ý“s@ /test-8/100worldanyone88ý“s ý“s /test-8/595worldanyoneÆÆý“ªý“ª /test-8/98worldanyone&&ý“råý“rå /test-8/594worldanyone¼¼ý“¡ý“¡ /test-8/97worldanyoneý“rÅý“rÅ /test-8/597worldanyoneÛÛý“½ý“½ /test-8/391worldanyoneÂÂý“„-ý“„- /test-8/96worldanyone  ý“r±ý“r± /test-8/596worldanyoneÐÐý“³ý“³ /test-8/390worldanyone¸¸ý“„ý“„ /test-8/95worldanyoneý“r˜ý“r˜ /test-8/599worldanyoneîîý“Òý“Ò /test-8/393worldanyoneÔÔý“„Eý“„E /test-8/94worldanyoneóóý“r~ý“r~ /test-8/598worldanyoneååý“Çý“Ç /test-8/392worldanyoneÎÎý“„9ý“„9 /test-8/93worldanyoneêêý“rný“rn /test-8/395worldanyoneééý“„aý“„a /test-8/92worldanyoneææý“r[ý“r[ /test-8/394worldanyoneÞÞý“„Rý“„R /test-8/91worldanyoneÜÜý“rJý“rJ /test-8/90worldanyoneÒÒý“r=ý“r= /test-8/294worldanyone Ñ Ñý“~¨ý“~¨ /test-8/499worldanyoneþþý“‰…ý“‰… /test-8/293worldanyone É Éý“~›ý“~› /test-8/296worldanyone ê êý“~Éý“~É /test-8/295worldanyone Ù Ùý“~·ý“~· /test-8/496worldanyoneââý“‰_ý“‰_ /test-8/290worldanyone ¥ ¥ý“~rý“~r /test-8/495worldanyoneÕÕý“‰Pý“‰P /test-8/498worldanyoneôôý“‰yý“‰y /test-8/292worldanyone º ºý“~Žý“~Ž /test-8/497worldanyoneêêý“‰lý“‰l /test-8/291worldanyone ³ ³ý“~€ý“~€ /test-8/491worldanyone³³ý“‰ý“‰ /test-8/492worldanyone¼¼ý“‰(ý“‰( /test-8/493worldanyoneÆÆý“‰5ý“‰5 /test-8/494worldanyoneÏÏý“‰Bý“‰B /test-8/297worldanyone õ õý“~Ùý“~Ù /test-8/298worldanyone ü üý“~çý“~ç /test-8/299worldanyone  ý“~öý“~ö /test-8/490worldanyone©©ý“‰ý“‰ /test-8/396worldanyoneññý“„lý“„l /test-8/397worldanyoneûûý“„vý“„v /test-8/398worldanyoneý“„€ý“„€ /test-8/399worldanyoneý“„ˆý“„ˆ /test-8/590worldanyone––ý“vý“v /test-8/591worldanyonežžý“ý“ /test-8/592worldanyone­­ý“Žý“Ž /test-8/88worldanyoneÁÁý“r ý“r /test-8/593worldanyone²²ý“˜ý“˜ /test-8/89worldanyoneËËý“r.ý“r. /test-8/240worldanyone ¯ ¯ý“{°ý“{° /test-8/241worldanyone ¶ ¶ý“{¾ý“{¾ /test-8/383worldanyonellý“ƒÆý“ƒÆ /test-8/384worldanyonettý“ƒÓý“ƒÓ /test-8/381worldanyoneUUý“ƒ­ý“ƒ­ /test-8/382worldanyone``ý“ƒºý“ƒº /test-8/380worldanyoneLLý“ƒ¡ý“ƒ¡ /test-8/389worldanyone­­ý“„ý“„ /test-8/388worldanyone¢¢ý“„ý“„ /test-8/387worldanyone““ý“ƒ÷ý“ƒ÷ /test-8/386worldanyoneˆˆý“ƒëý“ƒë /test-8/385worldanyoneý“ƒÞý“ƒÞ /test-8/245worldanyone å åý“|ý“| /test-8/244worldanyone Ü Üý“{úý“{ú /test-8/243worldanyone Ð Ðý“{âý“{â /test-8/242worldanyone à Ãý“{Ïý“{Ï /test-8/249worldanyone  ý“|>ý“|> /test-8/248worldanyone þ þý“|2ý“|2 /test-8/247worldanyone ô ôý“|#ý“|# /test-8/246worldanyone î îý“|ý“| /test-8/230worldanyone ? ?ý“{&ý“{& /test-8/370worldanyoneååý“ƒ0ý“ƒ0 /test-8/371worldanyoneððý“ƒ=ý“ƒ= /test-8/372worldanyoneûûý“ƒIý“ƒI /test-8/373worldanyoneý“ƒUý“ƒU /test-8/379worldanyone??ý“ƒ•ý“ƒ• /test-8/239worldanyone £ £ý“{Ÿý“{Ÿ /test-8/378worldanyone55ý“ƒ‰ý“ƒ‰ /test-8/375worldanyoneý“ƒjý“ƒj /test-8/374worldanyoneý“ƒaý“ƒa /test-8/377worldanyone++ý“ƒ}ý“ƒ} /test-8/376worldanyone##ý“ƒtý“ƒt /test-8/232worldanyone T Tý“{?ý“{? /test-8/231worldanyone J Jý“{2ý“{2 /test-8/234worldanyone j jý“{Yý“{Y /test-8/233worldanyone ] ]ý“{Lý“{L /test-8/236worldanyone … …ý“{vý“{v /test-8/235worldanyone s sý“{dý“{d /test-8/238worldanyone – –ý“{‘ý“{‘ /test-8/237worldanyone  ý“{…ý“{… /test-8/262worldanyone ‰ ‰ý“|âý“|â /test-8/263worldanyone • •ý“|òý“|ò /test-8/260worldanyone q qý“|Ãý“|à /test-8/261worldanyone  ý“|Óý“|Ó /test-8/361worldanyoneˆˆý“‚Ìý“‚Ì /test-8/362worldanyone““ý“‚Øý“‚Ø /test-8/360worldanyoneý“‚Âý“‚ /test-8/366worldanyone»»ý“ƒý“ƒ /test-8/365worldanyone²²ý“‚øý“‚ø /test-8/364worldanyone¨¨ý“‚îý“‚î /test-8/363worldanyoneý“‚ãý“‚ã /test-8/369worldanyoneÝÝý“ƒ$ý“ƒ$ /test-8/368worldanyoneÓÓý“ƒý“ƒ /test-8/367worldanyoneÈÈý“ƒý“ƒ /test-8/269worldanyone Ð Ðý“}Cý“}C /test-8/268worldanyone È Èý“}4ý“}4 /test-8/267worldanyone º ºý“}$ý“}$ /test-8/266worldanyone ° °ý“}ý“} /test-8/265worldanyone § §ý“} ý“} /test-8/264worldanyone  ý“|ÿý“|ÿ /test-8/250worldanyone  ý“|Iý“|I /test-8/251worldanyone  ý“|Tý“|T /test-8/252worldanyone ! !ý“|_ý“|_ /test-8/350worldanyoneý“‚Zý“‚Z /test-8/351worldanyone%%ý“‚eý“‚e /test-8/353worldanyone99ý“‚wý“‚w /test-8/352worldanyone//ý“‚oý“‚o /test-8/355worldanyoneKKý“‚Œý“‚Œ /test-8/354worldanyoneCCý“‚ý“‚ /test-8/357worldanyone__ý“‚¢ý“‚¢ /test-8/356worldanyoneUUý“‚—ý“‚— /test-8/359worldanyonettý“‚¶ý“‚¶ /test-8/358worldanyoneiiý“‚¬ý“‚¬ /test-8/258worldanyone ^ ^ý“|¬ý“|¬ /test-8/257worldanyone T Tý“|¡ý“|¡ /test-8/259worldanyone h hý“|·ý“|· /test-8/254worldanyone 6 6ý“|yý“|y /test-8/253worldanyone , ,ý“|mý“|m /test-8/256worldanyone K Ký“|•ý“|• /test-8/255worldanyone @ @ý“|†ý“|†/test-5worldanyone!!ý“jÏý“jÏ /test-5/338worldanyone © ©ý“€òý“€ò /test-5/339worldanyone ³ ³ý“€üý“€ü /test-5/332worldanyone n ný“€²ý“€² /test-5/333worldanyone x xý“€½ý“€½ /test-5/159worldanyone‚‚ý“v”ý“v” /test-5/330worldanyone Y Yý“€ý“€ /test-5/158worldanyonewwý“v‡ý“v‡ /test-5/331worldanyone a aý“€§ý“€§ /test-5/157worldanyoneiiý“vyý“vy /test-5/336worldanyone • •ý“€Þý“€Þ /test-5/156worldanyoneccý“vmý“vm /test-5/337worldanyone Ÿ Ÿý“€èý“€è /test-5/155worldanyoneYYý“vbý“vb /test-5/334worldanyone  ý“€Çý“€Ç /test-5/154worldanyoneOOý“vZý“vZ /test-5/335worldanyone ‰ ‰ý“€Òý“€Ò /test-5/601worldanyone!!ý“Žý“Ž /test-5/531worldanyoneccý“‹ý“‹ /test-5/152worldanyone==ý“v@ý“v@ /test-5/600worldanyoneý“þý“þ /test-5/530worldanyone__ý“Šúý“Šú /test-5/153worldanyoneEEý“vPý“vP /test-5/150worldanyone%%ý“v!ý“v! /test-5/151worldanyone22ý“v0ý“v0 /test-5/535worldanyoneŒŒý“‹0ý“‹0 /test-5/534worldanyone‚‚ý“‹&ý“‹& /test-5/533worldanyoneyyý“‹ý“‹ /test-5/532worldanyonenný“‹ý“‹ /test-5/609worldanyonessý“ŽZý“ŽZ /test-5/539worldanyoneµµý“‹[ý“‹[ /test-5/608worldanyonehhý“ŽNý“ŽN /test-5/538worldanyone««ý“‹Qý“‹Q /test-5/607worldanyone\\ý“ŽAý“ŽA /test-5/537worldanyone¢¢ý“‹Fý“‹F /test-5/606worldanyonePPý“Ž5ý“Ž5 /test-5/536worldanyone——ý“‹;ý“‹; /test-5/605worldanyoneHHý“Ž+ý“Ž+ /test-5/604worldanyone??ý“Ž$ý“Ž$ /test-5/603worldanyone55ý“Žý“Ž /test-5/602worldanyone++ý“Žý“Ž /test-5/202worldanyoneLLý“yÀý“yÀ /test-5/349worldanyoneý“‚Wý“‚W /test-5/203worldanyoneUUý“yÑý“yÑ /test-5/204worldanyoneccý“yäý“yä /test-5/205worldanyoneqqý“yùý“yù /test-5/200worldanyone77ý“y¡ý“y¡ /test-5/201worldanyoneCCý“y²ý“y² /test-5/341worldanyone Ç Çý“ý“ /test-5/169worldanyoneêêý“wý“w /test-5/342worldanyone Ð Ðý“‚ý“‚ /test-5/343worldanyone Þ Þý“‚ý“‚ /test-5/344worldanyone æ æý“‚!ý“‚! /test-5/166worldanyoneÈÈý“våý“vå /test-5/206worldanyone||ý“z ý“z /test-5/345worldanyone ð ðý“‚-ý“‚- /test-5/165worldanyone½½ý“vÙý“vÙ /test-5/207worldanyone‰‰ý“z ý“z /test-5/346worldanyone ø øý“‚7ý“‚7 /test-5/168worldanyoneÞÞý“wý“w /test-5/208worldanyone‘‘ý“z.ý“z. /test-5/347worldanyoneý“‚Aý“‚A /test-5/167worldanyoneÐÐý“vòý“vò /test-5/209worldanyoneššý“z>ý“z> /test-5/348worldanyoneý“‚Lý“‚L /test-5/540worldanyoneÀÀý“‹dý“‹d /test-5/409worldanyoneŽŽý“…(ý“…( /test-5/161worldanyone””ý“v¬ý“v¬ /test-5/3worldanyoneIIý“kHý“kH /test-5/408worldanyone„„ý“…ý“… /test-5/162worldanyonežžý“v¹ý“v¹ /test-5/2worldanyone@@ý“k.ý“k. /test-5/542worldanyoneÑÑý“‹œý“‹œ /test-5/163worldanyone§§ý“vÄý“vÄ /test-5/1worldanyone66ý“ký“k /test-5/541worldanyoneÉÉý“‹ý“‹ /test-5/164worldanyone±±ý“vÏý“vÏ /test-5/0worldanyone,,ý“jóý“jó /test-5/544worldanyoneääý“‹²ý“‹² /test-5/405worldanyoneddý“„üý“„ü /test-5/7worldanyonellý“k­ý“k­ /test-5/543worldanyoneÛÛý“‹¦ý“‹¦ /test-5/404worldanyoneZZý“„ßý“„ß /test-5/6worldanyoneccý“k™ý“k™ /test-5/546worldanyoneþþý“‹Ëý“‹Ë /test-5/407worldanyonezzý“…ý“… /test-5/5worldanyone\\ý“kƒý“kƒ /test-5/340worldanyone ½ ½ý“ý“ /test-5/545worldanyoneóóý“‹Àý“‹À /test-5/406worldanyonenný“…ý“… /test-5/160worldanyone‹‹ý“v ý“v  /test-5/4worldanyoneRRý“kcý“kc /test-5/548worldanyoneý“‹ßý“‹ß /test-5/401worldanyone99ý“„ºý“„º /test-5/547worldanyoneý“‹Õý“‹Õ /test-5/400worldanyone--ý“„®ý“„® /test-5/403worldanyonePPý“„Óý“„Ó /test-5/9worldanyoneý“kÎý“kÎ /test-5/549worldanyoneý“‹éý“‹é /test-5/402worldanyoneAAý“„Æý“„Æ /test-5/8worldanyonewwý“k¿ý“k¿ /test-5/211worldanyone±±ý“zdý“zd /test-5/212worldanyone¹¹ý“zsý“zs /test-5/210worldanyone¦¦ý“zTý“zT /test-5/215worldanyoneØØý“z–ý“z– /test-5/318worldanyone Û Ûý“öý“ö /test-5/216worldanyoneããý“z£ý“z£ /test-5/319worldanyone æ æý“€ý“€ /test-5/213worldanyoneÄÄý“z€ý“z€ /test-5/316worldanyone Ç Çý“àý“à /test-5/214worldanyoneÍÍý“z‰ý“z‰ /test-5/317worldanyone Ñ Ñý“êý“ê /test-5/179worldanyoneSSý“x@ý“x@ /test-5/219worldanyone  ý“zÞý“zÞ /test-5/314worldanyone ³ ³ý“Èý“È /test-5/178worldanyoneGGý“x2ý“x2 /test-5/315worldanyone ½ ½ý“Ôý“Ô /test-5/177worldanyone==ý“x%ý“x% /test-5/217worldanyoneííý“z¯ý“z¯ /test-5/312worldanyone ¥ ¥ý“±ý“± /test-5/176worldanyone22ý“wqý“wq /test-5/218worldanyoneööý“zºý“zº /test-5/313worldanyone ¨ ¨ý“¾ý“¾ /test-5/310worldanyone ‘ ‘ý“ý“ /test-5/311worldanyone › ›ý“§ý“§ /test-5/513worldanyone±±ý“ŠGý“ŠG /test-5/418worldanyoneððý“…®ý“…® /test-5/170worldanyoneóóý“wý“w /test-5/512worldanyone¨¨ý“Š<ý“Š< /test-5/417worldanyoneååý“… ý“…  /test-5/171worldanyoneýýý“w)ý“w) /test-5/511worldanyone››ý“Š/ý“Š/ /test-5/416worldanyoneÚÚý“…“ý“…“ /test-5/510worldanyoneý“Š$ý“Š$ /test-5/415worldanyoneÑÑý“…ƒý“…ƒ /test-5/623worldanyoneý“&ý“& /test-5/174worldanyoneý“wRý“wR /test-5/622worldanyoneúúý“ý“ /test-5/175worldanyone$$ý“wbý“wb /test-5/621worldanyoneòòý“ý“ /test-5/172worldanyoneý“w8ý“w8 /test-5/620worldanyoneççý“ý“ /test-5/419worldanyone÷÷ý“…Äý“…Ä /test-5/173worldanyoneý“wDý“wD /test-5/627worldanyone11ý“[ý“[ /test-5/410worldanyone™™ý“…4ý“…4 /test-5/626worldanyone,,ý“Oý“O /test-5/625worldanyoneý“Aý“A /test-5/519worldanyoneòòý“Š‹ý“Š‹ /test-5/624worldanyoneý“3ý“3 /test-5/518worldanyoneååý“Šý“Š /test-5/517worldanyoneÚÚý“Šuý“Šu /test-5/414worldanyoneÅÅý“…wý“…w /test-5/516worldanyoneÑÑý“Šký“Šk /test-5/413worldanyone¼¼ý“…ký“…k /test-5/629worldanyoneLLý“uý“u /test-5/515worldanyoneÇÇý“Š`ý“Š` /test-5/412worldanyone¯¯ý“…Wý“…W /test-5/628worldanyoneAAý“iý“i /test-5/514worldanyone»»ý“ŠSý“ŠS /test-5/411worldanyone¦¦ý“…Bý“…B /test-5/220worldanyone  ý“zèý“zè /test-5/221worldanyone  ý“zôý“zô /test-5/222worldanyone  ý“zÿý“zÿ /test-5/223worldanyone ( (ý“{ ý“{ /test-5/224worldanyone 1 1ý“{ý“{ /test-5/327worldanyone < <ý“€xý“€x /test-5/225worldanyone < <ý“{%ý“{% /test-5/328worldanyone E Eý“€†ý“€† /test-5/226worldanyone G Gý“{0ý“{0 /test-5/329worldanyone O Oý“€’ý“€’ /test-5/227worldanyone Q Qý“{<ý“{< /test-5/188worldanyone´´ý“xÅý“xÅ /test-5/228worldanyone [ [ý“{Fý“{F /test-5/323worldanyone  ý“€>ý“€> /test-5/187worldanyone©©ý“x´ý“x´ /test-5/229worldanyone f fý“{Pý“{P /test-5/324worldanyone  ý“€Mý“€M /test-5/325worldanyone % %ý“€[ý“€[ /test-5/189worldanyoneÁÁý“xØý“xØ /test-5/326worldanyone 2 2ý“€ký“€k /test-5/320worldanyone ð ðý“€ý“€ /test-5/321worldanyone ú úý“€ý“€ /test-5/322worldanyone  ý“€0ý“€0 /test-5/522worldanyoneý“Š«ý“Š« /test-5/427worldanyoneGGý“†ý“† /test-5/521worldanyoneý“ŠŸý“ŠŸ /test-5/426worldanyone<<ý“†ý“† /test-5/180worldanyone]]ý“xLý“xL /test-5/524worldanyone$$ý“ŠÀý“ŠÀ /test-5/429worldanyone]]ý“†:ý“†: /test-5/181worldanyoneeeý“xXý“xX /test-5/523worldanyoneý“жý“ж /test-5/428worldanyoneMMý“†+ý“†+ /test-5/182worldanyoneooý“xbý“xb /test-5/610worldanyone}}ý“Žeý“Že /test-5/183worldanyone||ý“xsý“xs /test-5/184worldanyone††ý“x‚ý“x‚ /test-5/612worldanyone‘‘ý“Žxý“Žx /test-5/520worldanyoneûûý“Š•ý“Š• /test-5/185worldanyone’’ý“x”ý“x” /test-5/611worldanyone‡‡ý“Žný“Žn /test-5/186worldanyoneŸŸý“x¤ý“x¤ /test-5/614worldanyone¤¤ý“ޏý“ޏ /test-5/613worldanyoneŸŸý“ެý“ެ /test-5/529worldanyoneUUý“Šñý“Šñ /test-5/616worldanyone½½ý“ŽÐý“ŽÐ /test-5/421worldanyone  ý“…Úý“…Ú /test-5/615worldanyone´´ý“ŽÅý“ŽÅ /test-5/420worldanyoneý“…Ðý“…Ð /test-5/618worldanyoneÏÏý“Žêý“Žê /test-5/526worldanyone88ý“ŠÕý“ŠÕ /test-5/423worldanyoneý“…ðý“…ð /test-5/617worldanyoneÅÅý“ŽÞý“ŽÞ /test-5/525worldanyone..ý“ŠËý“ŠË /test-5/422worldanyoneý“…åý“…å /test-5/528worldanyoneKKý“Šéý“Šé /test-5/425worldanyone22ý“†ý“† /test-5/619worldanyoneÝÝý“Žøý“Žø /test-5/527worldanyoneAAý“Šßý“Šß /test-5/424worldanyone((ý“…úý“…ú /test-5/116worldanyone±±ý“sïý“sï /test-5/117worldanyoneººý“süý“sü /test-5/114worldanyoneŸŸý“sØý“sØ /test-5/115worldanyone©©ý“säý“sä /test-5/571worldanyoneý“Œßý“Œß /test-5/112worldanyone‹‹ý“s¾ý“s¾ /test-5/570worldanyoneúúý“ŒÕý“ŒÕ /test-5/113worldanyone••ý“sÍý“sÍ /test-5/110worldanyonezzý“sžý“sž /test-5/111worldanyoneý“s¬ý“s¬ /test-5/118worldanyoneÃÃý“t ý“t /test-5/119worldanyoneÐÐý“tý“t /test-5/435worldanyoneœœý“†’ý“†’ /test-5/436worldanyone¤¤ý“†žý“†ž /test-5/433worldanyoneƒƒý“†vý“†v /test-5/434worldanyone’’ý“†„ý“†„ /test-5/431worldanyonerrý“†Wý“†W /test-5/700worldanyoneý“”¦ý“”¦ /test-5/432worldanyone~~ý“†gý“†g /test-5/430worldanyonehhý“†Iý“†I /test-5/703worldanyone%%ý“”Èý“”È /test-5/574worldanyone""ý“Œÿý“Œÿ /test-5/704worldanyone22ý“”Óý“”Ó /test-5/575worldanyone,,ý“ý“ /test-5/701worldanyoneý“”±ý“”± /test-5/572worldanyoneý“Œêý“Œê /test-5/702worldanyoneý“”½ý“”½ /test-5/573worldanyoneý“Œóý“Œó /test-5/578worldanyoneJJý“$ý“$ /test-5/439worldanyoneÃÃý“†Æý“†Æ /test-5/579worldanyoneOOý“0ý“0 /test-5/705worldanyone99ý“”Ýý“”Ý /test-5/576worldanyone66ý“ý“ /test-5/437worldanyone¯¯ý“†«ý“†« /test-5/577worldanyone@@ý“ý“ /test-5/438worldanyoneººý“†ºý“†º /test-5/125worldanyone  ý“tyý“ty /test-5/126worldanyoneý“tý“t /test-5/127worldanyone%%ý“t¢ý“t¢ /test-5/300worldanyone + +ý“!ý“! /test-5/128worldanyone11ý“t¯ý“t¯ /test-5/580worldanyoneYYý“9ý“9 /test-5/121worldanyoneååý“t:ý“t: /test-5/302worldanyone D Dý“>ý“> /test-5/122worldanyoneððý“tGý“tG /test-5/301worldanyone 8 8ý“.ý“. /test-5/582worldanyonemmý“Jý“J /test-5/123worldanyoneùùý“tWý“tW /test-5/304worldanyone [ [ý“Yý“Y /test-5/581worldanyoneccý“Bý“B /test-5/124worldanyoneý“tiý“ti /test-5/303worldanyone Q Qý“Lý“L /test-5/306worldanyone o oý“uý“u /test-5/305worldanyone e eý“eý“e /test-5/308worldanyone ƒ ƒý“‰ý“‰ /test-5/307worldanyone y yý“€ý“€ /test-5/129worldanyone<<ý“t½ý“t½ /test-5/309worldanyone ‡ ‡ý““ý““ /test-5/444worldanyoneùùý“‡ý“‡ /test-5/445worldanyoneý“‡ ý“‡ /test-5/446worldanyone  ý“‡ý“‡ /test-5/447worldanyoneý“‡ý“‡ /test-5/440worldanyoneÌÌý“†Òý“†Ò /test-5/441worldanyoneÚÚý“†àý“†à /test-5/442worldanyoneææý“†ìý“†ì /test-5/443worldanyoneîîý“†öý“†ö /test-5/583worldanyonewwý“Tý“T /test-5/584worldanyone}}ý“]ý“] /test-5/585worldanyone……ý“hý“h /test-5/120worldanyoneÛÛý“t*ý“t* /test-5/586worldanyoneý“rý“r /test-5/587worldanyone™™ý“{ý“{ /test-5/448worldanyoneý“‡)ý“‡) /test-5/588worldanyone££ý“ƒý“ƒ /test-5/449worldanyone''ý“‡5ý“‡5 /test-5/589worldanyone««ý“ý“ /test-5/134worldanyoneppý“uý“u /test-5/135worldanyonezzý“uý“u /test-5/132worldanyone\\ý“tæý“tæ /test-5/133worldanyoneffý“tñý“tñ /test-5/138worldanyoneššý“uFý“uF /test-5/139worldanyone¥¥ý“uYý“uY /test-5/136worldanyone‚‚ý“u'ý“u' /test-5/137worldanyone‹‹ý“u3ý“u3 /test-5/450worldanyone33ý“‡Aý“‡A /test-5/453worldanyoneNNý“‡bý“‡b /test-5/454worldanyoneZZý“‡ný“‡n /test-5/451worldanyone==ý“‡Ný“‡N /test-5/452worldanyoneDDý“‡Xý“‡X /test-5/457worldanyonewwý“‡‘ý“‡‘ /test-5/458worldanyone‚‚ý“‡œý“‡œ /test-5/558worldanyonezzý“ŒQý“ŒQ /test-5/455worldanyonebbý“‡zý“‡z /test-5/559worldanyone‡‡ý“Œ]ý“Œ] /test-5/456worldanyonellý“‡…ý“‡… /test-5/556worldanyoneiiý“Œ;ý“Œ; /test-5/557worldanyonerrý“ŒGý“ŒG /test-5/554worldanyonePPý“Œ$ý“Œ$ /test-5/459worldanyoneŠŠý“‡¥ý“‡¥ /test-5/555worldanyone\\ý“Œ/ý“Œ/ /test-5/552worldanyone88ý“Œ ý“Œ /test-5/131worldanyoneSSý“tØý“tØ /test-5/553worldanyoneGGý“Œý“Œ /test-5/130worldanyoneHHý“tÊý“tÊ /test-5/550worldanyone((ý“‹õý“‹õ /test-5/551worldanyone00ý“‹ÿý“‹ÿ /test-5/143worldanyoneÕÕý“uŸý“uŸ /test-5/144worldanyoneßßý“u²ý“u² /test-5/560worldanyone‘‘ý“Œhý“Œh /test-5/145worldanyoneééý“uÂý“u /test-5/146worldanyoneõõý“uÕý“uÕ /test-5/147worldanyoneÿÿý“uâý“uâ /test-5/148worldanyoneý“uûý“uû /test-5/149worldanyoneý“v ý“v /test-5/461worldanyoneœœý“‡¹ý“‡¹ /test-5/460worldanyone––ý“‡¯ý“‡¯ /test-5/462worldanyone¨¨ý“‡Åý“‡Å /test-5/463worldanyone²²ý“‡Ðý“‡Ð /test-5/464worldanyone»»ý“‡Üý“‡Ü /test-5/465worldanyoneÄÄý“‡åý“‡å /test-5/569worldanyoneòòý“ŒËý“ŒË /test-5/466worldanyoneÏÏý“‡ïý“‡ï /test-5/467worldanyoneØØý“‡úý“‡ú /test-5/468worldanyoneââý“ˆý“ˆ /test-5/469worldanyoneììý“ˆý“ˆ /test-5/565worldanyoneÈÈý“Œ ý“Œ  /test-5/566worldanyoneÐÐý“Œªý“Œª /test-5/567worldanyoneßßý“Œµý“Œµ /test-5/568worldanyoneççý“ŒÀý“ŒÀ /test-5/561worldanyoneœœý“Œtý“Œt /test-5/140worldanyone±±ý“ulý“ul /test-5/562worldanyoneªªý“Œý“Œ /test-5/563worldanyone±±ý“ŒŠý“ŒŠ /test-5/142worldanyoneÊÊý“uý“u /test-5/564worldanyone½½ý“Œ–ý“Œ– /test-5/141worldanyone¹¹ý“u{ý“u{ /test-5/687worldanyoneyyý“’Ný“’N /test-5/686worldanyoneppý“’Eý“’E /test-5/689worldanyone’’ý“”*ý“”* /test-5/688worldanyoneƒƒý“’Wý“’W /test-5/683worldanyonePPý“’"ý“’" /test-5/682worldanyoneIIý“’ý“’ /test-5/685worldanyonehhý“’;ý“’; /test-5/684worldanyoneaaý“’0ý“’0 /test-5/680worldanyone55ý“’ý“’ /test-5/681worldanyone??ý“’ý“’ /test-5/678worldanyone""ý“‘ðý“‘ð /test-5/677worldanyoneý“‘áý“‘á /test-5/676worldanyoneý“‘Ìý“‘Ì /test-5/675worldanyoneýýý“‘¾ý“‘¾ /test-5/674worldanyoneôôý“‘²ý“‘² /test-5/673worldanyoneêêý“‘§ý“‘§ /test-5/672worldanyoneààý“‘œý“‘œ /test-5/671worldanyoneÕÕý“‘“ý“‘“ /test-5/679worldanyone,,ý“‘üý“‘ü /test-5/670worldanyoneÌÌý“‘Šý“‘Š /test-5/696worldanyoneÛÛý“”xý“”x /test-5/695worldanyoneÑÑý“”ný“”n /test-5/694worldanyoneÅÅý“”bý“”b /test-5/693worldanyone»»ý“”Wý“”W /test-5/699worldanyoneûûý“”™ý“”™ /test-5/698worldanyoneððý“”‹ý“”‹ /test-5/697worldanyoneææý“”‚ý“”‚ /test-5/691worldanyone££ý“”Bý“”B /test-5/692worldanyone°°ý“”Mý“”M /test-5/690worldanyone››ý“”7ý“”7 /test-5/646worldanyoneââý“Šý“Š /test-5/647worldanyoneííý“”ý“” /test-5/648worldanyoneõõý“Ÿý“Ÿ /test-5/649worldanyoneý“¬ý“¬ /test-5/642worldanyoneÁÁý“ý“ /test-5/195worldanyoneý“yKý“yK /test-5/643worldanyoneËËý“!ý“! /test-5/194worldanyoneûûý“y8ý“y8 /test-5/644worldanyoneÏÏý“uý“u /test-5/197worldanyoneý“yrý“yr /test-5/645worldanyoneÙÙý“ý“ /test-5/196worldanyoneý“ybý“yb /test-5/191worldanyoneÜÜý“yý“y /test-5/190worldanyoneÍÍý“xïý“xï /test-5/640worldanyone¬¬ý“üý“ü /test-5/193worldanyoneññý“y"ý“y" /test-5/641worldanyone¶¶ý“ ý“ /test-5/192worldanyoneææý“yý“y /test-5/198worldanyone$$ý“y€ý“y€ /test-5/199worldanyone..ý“yý“y /test-5/505worldanyone``ý“‰ôý“‰ô /test-5/506worldanyonejjý“‰þý“‰þ /test-5/639worldanyone¢¢ý“îý“î /test-5/503worldanyoneNNý“‰Þý“‰Þ /test-5/504worldanyoneZZý“‰ëý“‰ë /test-5/637worldanyone’’ý“Ùý“Ù /test-5/509worldanyoneˆˆý“Šý“Š /test-5/638worldanyoneššý“ãý“ã /test-5/635worldanyoneý“Âý“ /test-5/507worldanyonerrý“Šý“Š /test-5/636worldanyoneˆˆý“Îý“Î /test-5/508worldanyone~~ý“Šý“Š /test-5/633worldanyonellý“¢ý“¢ /test-5/634worldanyonessý“®ý“® /test-5/631worldanyoneXXý“ý“ /test-5/632worldanyonebbý“˜ý“˜ /test-5/501worldanyone88ý“‰Åý“‰Å /test-5/630worldanyoneRRý“‚ý“‚ /test-5/502worldanyoneGGý“‰Ôý“‰Ô /test-5/500worldanyone--ý“‰¹ý“‰¹ /test-5/668worldanyone¶¶ý“‘vý“‘v /test-5/669worldanyoneÂÂý“‘ý“‘ /test-5/660worldanyoneggý“‘ ý“‘ /test-5/661worldanyoneqqý“‘(ý“‘( /test-5/662worldanyone{{ý“‘3ý“‘3 /test-5/663worldanyone‰‰ý“‘@ý“‘@ /test-5/664worldanyone‘‘ý“‘Ký“‘K /test-5/665worldanyoneý“‘Xý“‘X /test-5/666worldanyone¤¤ý“‘cý“‘c /test-5/667worldanyone®®ý“‘mý“‘m /test-5/659worldanyone]]ý“‘ý“‘ /test-5/657worldanyoneIIý“‘ý“‘ /test-5/658worldanyoneSSý“‘ ý“‘ /test-5/651worldanyoneý“Ãý“à /test-5/652worldanyoneý“Íý“Í /test-5/650worldanyone  ý“¹ý“¹ /test-5/655worldanyone55ý“êý“ê /test-5/656worldanyone@@ý“öý“ö /test-5/653worldanyone$$ý“Øý“Ø /test-5/654worldanyone..ý“áý“á /test-5/35worldanyoneŠŠý“mÂý“m /test-5/36worldanyoneý“mÜý“mÜ /test-5/33worldanyonessý“mšý“mš /test-5/34worldanyone}}ý“m­ý“m­ /test-5/39worldanyone©©ý“ný“n /test-5/37worldanyone——ý“mëý“më /test-5/38worldanyone¡¡ý“m÷ý“m÷ /test-5/43worldanyoneÒÒý“nQý“nQ /test-5/42worldanyoneÉÉý“nBý“nB /test-5/41worldanyone¿¿ý“n0ý“n0 /test-5/40worldanyone´´ý“ný“n /test-5/22worldanyoneý“lÃý“là /test-5/23worldanyone  ý“lÕý“lÕ /test-5/24worldanyoneý“lìý“lì /test-5/25worldanyoneý“lüý“lü /test-5/26worldanyone%%ý“m ý“m /test-5/27worldanyone//ý“mý“m /test-5/28worldanyone77ý“m*ý“m* /test-5/29worldanyoneDDý“mBý“mB /test-5/30worldanyoneKKý“mSý“mS /test-5/32worldanyoneffý“m~ý“m~ /test-5/31worldanyoneYYý“mjý“mj /test-5/19worldanyoneääý“l‰ý“l‰ /test-5/17worldanyoneËËý“l^ý“l^ /test-5/18worldanyoneÚÚý“lvý“lv /test-5/15worldanyone¸¸ý“l@ý“l@ /test-5/16worldanyoneÃÃý“lPý“lP /test-5/13worldanyone¤¤ý“lý“l /test-5/14worldanyone­­ý“l(ý“l( /test-5/11worldanyoneý“kóý“kó /test-5/12worldanyoneššý“lý“l /test-5/21worldanyone÷÷ý“l¯ý“l¯ /test-5/20worldanyoneììý“lšý“lš /test-5/10worldanyoneŠŠý“káý“ká /test-5/79worldanyoneLLý“qrý“qr /test-5/78worldanyone??ý“q`ý“q` /test-5/77worldanyone55ý“qSý“qS /test-5/82worldanyoneiiý“qŸý“qŸ /test-5/83worldanyonevvý“q¯ý“q¯ /test-5/80worldanyoneWWý“q‚ý“q‚ /test-5/81worldanyone``ý“q‘ý“q‘ /test-5/86worldanyone‘‘ý“qÔý“qÔ /test-5/87worldanyoneœœý“qäý“qä /test-5/84worldanyone}}ý“q»ý“q» /test-5/85worldanyone‡‡ý“qÉý“qÉ /test-5/67worldanyoneÇÇý“pAý“pA /test-5/66worldanyone¿¿ý“p.ý“p. /test-5/69worldanyoneááý“peý“pe /test-5/68worldanyoneÔÔý“pRý“pR /test-5/70worldanyoneëëý“pvý“pv /test-5/71worldanyoneøøý“p‰ý“p‰ /test-5/72worldanyoneý“p˜ý“p˜ /test-5/73worldanyone  ý“p©ý“p© /test-5/74worldanyoneý“q$ý“q$ /test-5/75worldanyone  ý“q5ý“q5 /test-5/76worldanyone,,ý“qFý“qF /test-5/59worldanyonerrý“o±ý“o± /test-5/58worldanyoneddý“o™ý“o™ /test-5/57worldanyone^^ý“oˆý“oˆ /test-5/56worldanyoneOOý“oný“on /test-5/55worldanyoneCCý“oHý“oH /test-5/64worldanyoneªªý“p ý“p /test-5/65worldanyone³³ý“pý“p /test-5/62worldanyone––ý“oæý“oæ /test-5/63worldanyoneý“o÷ý“o÷ /test-5/60worldanyone~~ý“oÁý“oÁ /test-5/61worldanyoneŒŒý“oÕý“oÕ /test-5/49worldanyone  ý“nËý“nË /test-5/48worldanyoneý“n¶ý“n¶ /test-5/45worldanyoneææý“nzý“nz /test-5/44worldanyoneÛÛý“nbý“nb /test-5/47worldanyoneúúý“n¡ý“n¡ /test-5/46worldanyoneððý“ný“n /test-5/51worldanyoneý“nôý“nô /test-5/52worldanyone%%ý“o ý“o /test-5/53worldanyone//ý“oý“o /test-5/54worldanyone88ý“o0ý“o0 /test-5/50worldanyoneý“nßý“nß /test-5/487worldanyone§§ý“‰ ý“‰ /test-5/281worldanyone p pý“~3ý“~3 /test-5/486worldanyoneŸŸý“‰ý“‰ /test-5/280worldanyone i iý“~'ý“~' /test-5/485worldanyone˜˜ý“ˆóý“ˆó /test-5/484worldanyone‰‰ý“ˆßý“ˆß /test-5/285worldanyone — —ý“~dý“~d /test-5/284worldanyone Ž Žý“~Zý“~Z /test-5/489worldanyone»»ý“‰&ý“‰& /test-5/283worldanyone … …ý“~Mý“~M /test-5/488worldanyone°°ý“‰ý“‰ /test-5/282worldanyone ~ ~ý“~@ý“~@ /test-5/288worldanyone ¶ ¶ý“~‚ý“~‚ /test-5/289worldanyone Á Áý“~ý“~ /test-5/286worldanyone ¡ ¡ý“~mý“~m /test-5/287worldanyone « «ý“~xý“~x /test-5/482worldanyonevvý“ˆÃý“ˆÃ /test-5/483worldanyoneý“ˆÏý“ˆÏ /test-5/480worldanyoneaaý“ˆ«ý“ˆ« /test-5/481worldanyonellý“ˆ¸ý“ˆ¸ /test-5/474worldanyone##ý“ˆjý“ˆj /test-5/473worldanyoneý“ˆ]ý“ˆ] /test-5/476worldanyone88ý“ˆý“ˆ /test-5/270worldanyone ÿ ÿý“}„ý“}„ /test-5/475worldanyone..ý“ˆuý“ˆu /test-5/478worldanyoneJJý“ˆ”ý“ˆ” /test-5/272worldanyone  ý“}¥ý“}¥ /test-5/477worldanyoneAAý“ˆ‰ý“ˆ‰ /test-5/271worldanyone  ý“}‘ý“}‘ /test-5/274worldanyone % %ý“}Àý“}À /test-5/479worldanyoneTTý“ˆžý“ˆž /test-5/273worldanyone  ý“}´ý“}´ /test-5/275worldanyone / /ý“}Íý“}Í /test-5/276worldanyone > >ý“}òý“}ò /test-5/277worldanyone I Iý“}ÿý“}ÿ /test-5/278worldanyone R Rý“~ ý“~ /test-5/279worldanyone Z Zý“~ý“~ /test-5/470worldanyoneùùý“ˆ6ý“ˆ6 /test-5/471worldanyoneý“ˆBý“ˆB /test-5/472worldanyone  ý“ˆPý“ˆP /test-5/109worldanyoneppý“s”ý“s” /test-5/108worldanyoneeeý“s…ý“s… /test-5/107worldanyone[[ý“srý“sr /test-5/106worldanyoneSSý“scý“sc /test-5/105worldanyoneIIý“sPý“sP /test-5/104worldanyoneCCý“s@ý“s@ /test-5/103worldanyone::ý“sý“s /test-5/102worldanyone22ý“rÿý“rÿ /test-5/99worldanyoneý“rÀý“rÀ /test-5/101worldanyone''ý“ræý“ræ /test-5/100worldanyoneý“rÓý“rÓ /test-5/595worldanyoneææý“Ëý“Ë /test-5/98worldanyone  ý“r¬ý“r¬ /test-5/594worldanyoneßßý“Âý“ /test-5/97worldanyoneý“r™ý“r™ /test-5/597worldanyoneýýý“àý“à /test-5/391worldanyoneÐÐý“„?ý“„? /test-5/96worldanyoneööý“rý“r /test-5/596worldanyoneóóý“Öý“Ö /test-5/390worldanyoneÆÆý“„4ý“„4 /test-5/95worldanyoneííý“rný“rn /test-5/599worldanyoneý“ôý“ô /test-5/393worldanyoneããý“„Tý“„T /test-5/94worldanyoneååý“rZý“rZ /test-5/598worldanyoneý“ëý“ë /test-5/392worldanyoneÚÚý“„Hý“„H /test-5/93worldanyoneÛÛý“rHý“rH /test-5/395worldanyoneööý“„ný“„n /test-5/92worldanyoneÍÍý“r0ý“r0 /test-5/394worldanyoneîîý“„bý“„b /test-5/91worldanyoneÃÃý“r"ý“r" /test-5/90worldanyone¼¼ý“rý“r /test-5/294worldanyone î îý“~×ý“~× /test-5/499worldanyone""ý“‰­ý“‰­ /test-5/293worldanyone å åý“~Èý“~È /test-5/296worldanyone  ý“~îý“~î /test-5/295worldanyone ø øý“~äý“~ä /test-5/496worldanyoneý“‰‡ý“‰‡ /test-5/290worldanyone Ë Ëý“~œý“~œ /test-5/495worldanyone÷÷ý“‰zý“‰z /test-5/498worldanyoneý“‰ ý“‰  /test-5/292worldanyone Ü Üý“~¸ý“~¸ /test-5/497worldanyoneý“‰’ý“‰’ /test-5/291worldanyone Ð Ðý“~¨ý“~¨ /test-5/491worldanyoneÑÑý“‰Cý“‰C /test-5/492worldanyoneÜÜý“‰Rý“‰R /test-5/493worldanyoneææý“‰aý“‰a /test-5/494worldanyoneïïý“‰mý“‰m /test-5/297worldanyone  ý“~øý“~ø /test-5/298worldanyone  ý“ý“ /test-5/299worldanyone  ý“ý“ /test-5/490worldanyoneÅÅý“‰3ý“‰3 /test-5/396worldanyoneý“„zý“„z /test-5/397worldanyone  ý“„‡ý“„‡ /test-5/398worldanyoneý“„“ý“„“ /test-5/399worldanyone  ý“„¡ý“„¡ /test-5/590worldanyone¶¶ý“™ý“™ /test-5/591worldanyone¿¿ý“¤ý“¤ /test-5/592worldanyoneËËý“¯ý“¯ /test-5/88worldanyone¦¦ý“qôý“qô /test-5/593worldanyoneÓÓý“¹ý“¹ /test-5/89worldanyone±±ý“rý“r /test-5/240worldanyone Ó Óý“{ìý“{ì /test-5/241worldanyone Ý Ýý“{üý“{ü /test-5/383worldanyoneý“ƒßý“ƒß /test-5/384worldanyone‹‹ý“ƒìý“ƒì /test-5/381worldanyonenný“ƒÇý“ƒÇ /test-5/382worldanyonewwý“ƒÓý“ƒÓ /test-5/380worldanyoneaaý“ƒºý“ƒº /test-5/389worldanyone¾¾ý“„)ý“„) /test-5/388worldanyone²²ý“„ý“„ /test-5/387worldanyone¨¨ý“„ý“„ /test-5/386worldanyone  ý“„ý“„ /test-5/385worldanyone™™ý“ƒøý“ƒø /test-5/245worldanyone  ý“|:ý“|: /test-5/244worldanyone ú úý“|+ý“|+ /test-5/243worldanyone ð ðý“|ý“| /test-5/242worldanyone ç çý“| ý“| /test-5/249worldanyone - -ý“|mý“|m /test-5/248worldanyone # #ý“|aý“|a /test-5/247worldanyone  ý“|Vý“|V /test-5/246worldanyone  ý“|Gý“|G /test-5/230worldanyone p pý“{\ý“{\ /test-5/370worldanyone÷÷ý“ƒ?ý“ƒ? /test-5/371worldanyoneýýý“ƒIý“ƒI /test-5/372worldanyoneý“ƒUý“ƒU /test-5/373worldanyoneý“ƒaý“ƒa /test-5/379worldanyoneXXý“ƒ®ý“ƒ® /test-5/239worldanyone É Éý“{Ýý“{Ý /test-5/378worldanyoneKKý“ƒ¡ý“ƒ¡ /test-5/375worldanyone((ý“ƒ{ý“ƒ{ /test-5/374worldanyoneý“ƒný“ƒn /test-5/377worldanyoneAAý“ƒ–ý“ƒ– /test-5/376worldanyone77ý“ƒŠý“ƒŠ /test-5/232worldanyone  ý“{uý“{u /test-5/231worldanyone y yý“{hý“{h /test-5/234worldanyone — —ý“{‘ý“{‘ /test-5/233worldanyone ‹ ‹ý“{ƒý“{ƒ /test-5/236worldanyone ª ªý“{­ý“{­ /test-5/235worldanyone    ý“{žý“{ž /test-5/238worldanyone ¾ ¾ý“{Íý“{Í /test-5/237worldanyone ¹ ¹ý“{¾ý“{¾ /test-5/262worldanyone ­ ­ý“}ý“} /test-5/263worldanyone µ µý“}ý“} /test-5/260worldanyone — —ý“|ûý“|û /test-5/261worldanyone ¢ ¢ý“}ý“} /test-5/361worldanyone’’ý“‚Øý“‚Ø /test-5/362worldanyone››ý“‚âý“‚â /test-5/360worldanyoneŠŠý“‚Íý“‚Í /test-5/366worldanyoneÉÉý“ƒý“ƒ /test-5/365worldanyone¾¾ý“ƒý“ƒ /test-5/364worldanyone´´ý“‚ùý“‚ù /test-5/363worldanyone¦¦ý“‚íý“‚í /test-5/369worldanyoneêêý“ƒ1ý“ƒ1 /test-5/368worldanyoneÜÜý“ƒ$ý“ƒ$ /test-5/367worldanyoneÒÒý“ƒý“ƒ /test-5/269worldanyone ò òý“}tý“}t /test-5/268worldanyone å åý“}fý“}f /test-5/267worldanyone Û Ûý“}Uý“}U /test-5/266worldanyone Õ Õý“}Dý“}D /test-5/265worldanyone Ê Êý“}6ý“}6 /test-5/264worldanyone ¿ ¿ý“}+ý“}+ /test-5/250worldanyone 3 3ý“|xý“|x /test-5/251worldanyone = =ý“|‚ý“|‚ /test-5/252worldanyone G Gý“|ý“| /test-5/350worldanyone!!ý“‚`ý“‚` /test-5/351worldanyone,,ý“‚jý“‚j /test-5/353worldanyone??ý“‚€ý“‚€ /test-5/352worldanyone55ý“‚vý“‚v /test-5/355worldanyoneWWý“‚˜ý“‚˜ /test-5/354worldanyoneMMý“‚ý“‚ /test-5/357worldanyonejjý“‚¬ý“‚¬ /test-5/356worldanyoneaaý“‚£ý“‚£ /test-5/359worldanyone~~ý“‚Âý“‚ /test-5/358worldanyonessý“‚¶ý“‚¶ /test-5/258worldanyone ƒ ƒý“|ßý“|ß /test-5/257worldanyone z zý“|Ñý“|Ñ /test-5/259worldanyone  ý“|ïý“|ï /test-5/254worldanyone [ [ý“|§ý“|§ /test-5/253worldanyone Q Qý“|›ý“|› /test-5/256worldanyone n ný“|Âý“| /test-5/255worldanyone e eý“|´ý“|´/test-6worldanyone..ý“jþý“jþ /test-6/338worldanyone ` `ý“]ý“] /test-6/339worldanyone j jý“hý“h /test-6/332worldanyone ( (ý“ý“ /test-6/333worldanyone 2 2ý“)ý“) /test-6/330worldanyone  ý“ý“ /test-6/331worldanyone " "ý“ý“ /test-6/336worldanyone L Lý“Jý“J /test-6/337worldanyone V Vý“Tý“T /test-6/334worldanyone < <ý“3ý“3 /test-6/335worldanyone B Bý“=ý“= /test-6/531worldanyoneXXý“ˆ¢ý“ˆ¢ /test-6/748worldanyoneYYý“’*ý“’* /test-6/530worldanyoneNNý“ˆ™ý“ˆ™ /test-6/747worldanyoneNNý“’!ý“’! /test-6/746worldanyoneEEý“’ý“’ /test-6/745worldanyone==ý“’ ý“’ /test-6/535worldanyoneý“ˆÏý“ˆÏ /test-6/534worldanyonerrý“ˆÂý“ˆÂ /test-6/533worldanyoneiiý“ˆ·ý“ˆ· /test-6/532worldanyonebbý“ˆ¬ý“ˆ¬ /test-6/749worldanyonebbý“’3ý“’3 /test-6/539worldanyone¦¦ý“‰ý“‰ /test-6/740worldanyoneý“‘Æý“‘Æ /test-6/538worldanyoneœœý“ˆüý“ˆü /test-6/537worldanyone’’ý“ˆðý“ˆð /test-6/536worldanyone‡‡ý“ˆÝý“ˆÝ /test-6/744worldanyone33ý“’ý“’ /test-6/743worldanyone((ý“‘÷ý“‘÷ /test-6/742worldanyoneý“‘ãý“‘ã /test-6/741worldanyone  ý“‘Ïý“‘Ï /test-6/349worldanyone Æ Æý“ßý“ß /test-6/341worldanyone { {ý“ƒý“ƒ /test-6/342worldanyone … …ý“Žý“Ž /test-6/343worldanyone  ý“—ý“— /test-6/344worldanyone ˜ ˜ý“¢ý“¢ /test-6/345worldanyone  ý“¯ý“¯ /test-6/346worldanyone « «ý“¾ý“¾ /test-6/347worldanyone ² ²ý“Èý“È /test-6/348worldanyone ¼ ¼ý“Ôý“Ô /test-6/3worldanyonePPý“k_ý“k_ /test-6/540worldanyone¬¬ý“‰ý“‰ /test-6/735worldanyoneÍÍý“‘Žý“‘Ž /test-6/2worldanyoneDDý“kAý“kA /test-6/734worldanyoneÇÇý“‘„ý“‘„ /test-6/1worldanyone==ý“k'ý“k' /test-6/542worldanyoneÄÄý“‰2ý“‰2 /test-6/737worldanyoneääý“‘¦ý“‘¦ /test-6/0worldanyone55ý“ký“k /test-6/541worldanyone¶¶ý“‰"ý“‰" /test-6/736worldanyoneÛÛý“‘›ý“‘› /test-6/7worldanyonessý“k¶ý“k¶ /test-6/544worldanyoneÙÙý“‰Qý“‰Q /test-6/739worldanyoneúúý“‘½ý“‘½ /test-6/6worldanyonejjý“k©ý“k© /test-6/543worldanyoneËËý“‰@ý“‰@ /test-6/738worldanyoneððý“‘²ý“‘² /test-6/340worldanyone q qý“wý“w /test-6/5worldanyoneaaý“k—ý“k— /test-6/546worldanyoneèèý“‰ký“‰k /test-6/4worldanyoneWWý“kwý“kw /test-6/545worldanyoneÞÞý“‰^ý“‰^ /test-6/548worldanyoneüüý“‰}ý“‰} /test-6/547worldanyoneòòý“‰uý“‰u /test-6/9worldanyone„„ý“kÝý“kÝ /test-6/8worldanyonezzý“kÈý“kÈ /test-6/549worldanyoneý“‰‡ý“‰‡ /test-6/731worldanyoneªªý“‘ký“‘k /test-6/730worldanyone¡¡ý“‘aý“‘a /test-6/733worldanyone¾¾ý“‘|ý“‘| /test-6/732worldanyone´´ý“‘tý“‘t /test-6/318worldanyone © ©ý“~tý“~t /test-6/319worldanyone ± ±ý“~€ý“~€ /test-6/316worldanyone – –ý“~bý“~b /test-6/317worldanyone    ý“~jý“~j /test-6/314worldanyone „ „ý“~Mý“~M /test-6/315worldanyone Œ Œý“~Xý“~X /test-6/312worldanyone v vý“~9ý“~9 /test-6/313worldanyone € €ý“~Bý“~B /test-6/310worldanyone b bý“~&ý“~& /test-6/311worldanyone l lý“~/ý“~/ /test-6/513worldanyone°°ý“‡Ðý“‡Ð /test-6/512worldanyone¥¥ý“‡Åý“‡Å /test-6/729worldanyone––ý“‘Wý“‘W /test-6/511worldanyone››ý“‡¹ý“‡¹ /test-6/728worldanyoneŒŒý“‘Iý“‘I /test-6/510worldanyone””ý“‡¯ý“‡¯ /test-6/727worldanyone‚‚ý“‘?ý“‘? /test-6/726worldanyoneý“‘5ý“‘5 /test-6/725worldanyoneuuý“‘,ý“‘, /test-6/724worldanyonekký“‘$ý“‘$ /test-6/723worldanyoneaaý“‘ý“‘ /test-6/722worldanyone[[ý“‘ý“‘ /test-6/721worldanyoneQQý“‘ý“‘ /test-6/519worldanyoneëëý“ˆý“ˆ /test-6/720worldanyoneGGý“ÿý“ÿ /test-6/518worldanyoneááý“ˆý“ˆ /test-6/517worldanyone××ý“‡ùý“‡ù /test-6/516worldanyoneÍÍý“‡ïý“‡ï /test-6/515worldanyoneÃÃý“‡åý“‡å /test-6/514worldanyone¹¹ý“‡Ûý“‡Û /test-6/327worldanyone ý ýý“~èý“~è /test-6/328worldanyone  ý“~ôý“~ô /test-6/329worldanyone  ý“~þý“~þ /test-6/323worldanyone Ø Øý“~·ý“~· /test-6/324worldanyone â âý“~Åý“~Å /test-6/325worldanyone ì ìý“~Ñý“~Ñ /test-6/326worldanyone ö öý“~Ûý“~Û /test-6/709worldanyoneççý“ý“ /test-6/320worldanyone ¼ ¼ý“~Žý“~Ž /test-6/321worldanyone Ä Äý“~šý“~š /test-6/322worldanyone Î Îý“~¨ý“~¨ /test-6/522worldanyoneý“ˆLý“ˆL /test-6/717worldanyone//ý“ãý“ã /test-6/521worldanyoneûûý“ˆAý“ˆA /test-6/716worldanyone''ý“Úý“Ú /test-6/524worldanyoneý“ˆ]ý“ˆ] /test-6/719worldanyone>>ý“õý“õ /test-6/523worldanyoneý“ˆTý“ˆT /test-6/718worldanyone99ý“ëý“ë /test-6/713worldanyone  ý“ºý“º /test-6/712worldanyoneý“±ý“± /test-6/520worldanyoneôôý“ˆ5ý“ˆ5 /test-6/715worldanyoneý“Ñý“Ñ /test-6/714worldanyoneý“Çý“Ç /test-6/529worldanyoneDDý“ˆý“ˆ /test-6/711worldanyoneùùý“§ý“§ /test-6/710worldanyoneïïý“ý“ /test-6/526worldanyone&&ý“ˆqý“ˆq /test-6/525worldanyone  ý“ˆgý“ˆg /test-6/528worldanyone;;ý“ˆ†ý“ˆ† /test-6/527worldanyone00ý“ˆzý“ˆz /test-6/571worldanyoneÍÍý“Šhý“Šh /test-6/570worldanyoneÅÅý“Š^ý“Š^ /test-6/700worldanyoneý“Õý“Õ /test-6/574worldanyoneììý“Їý“Ї /test-6/703worldanyone¯¯ý“ýý“ý /test-6/575worldanyoneööý“Šý“Š /test-6/704worldanyone··ý“ ý“ /test-6/572worldanyone××ý“Šoý“Šo /test-6/701worldanyone˜˜ý“âý“â /test-6/573worldanyoneááý“Š{ý“Š{ /test-6/702worldanyone££ý“ïý“ï /test-6/578worldanyoneý“Š®ý“Š® /test-6/707worldanyoneÕÕý“wý“w /test-6/579worldanyoneý“Џý“Џ /test-6/708worldanyoneßßý“ƒý“ƒ /test-6/576worldanyoneÿÿý“Ššý“Šš /test-6/705worldanyoneÀÀý“ý“ /test-6/577worldanyone  ý“Фý“Ф /test-6/706worldanyoneÊÊý“ý“ /test-6/300worldanyone  ý“}‘ý“}‘ /test-6/302worldanyone  ý“}²ý“}² /test-6/580worldanyone''ý“ŠÃý“ŠÃ /test-6/301worldanyone  ý“}£ý“}£ /test-6/304worldanyone , ,ý“}Çý“}Ç /test-6/582worldanyone;;ý“Š×ý“Š× /test-6/303worldanyone " "ý“}½ý“}½ /test-6/581worldanyone11ý“ŠÎý“ŠÎ /test-6/306worldanyone A Aý“}òý“}ò /test-6/305worldanyone 6 6ý“}Òý“}Ò /test-6/308worldanyone N Ný“~ ý“~ /test-6/307worldanyone D Dý“}ýý“}ý /test-6/309worldanyone \ \ý“~ý“~ /test-6/583worldanyoneEEý“Šàý“Šà /test-6/584worldanyoneNNý“Šìý“Šì /test-6/585worldanyoneWWý“Šøý“Šø /test-6/586worldanyoneaaý“‹ý“‹ /test-6/587worldanyonellý“‹ý“‹ /test-6/588worldanyonevvý“‹ý“‹ /test-6/589worldanyone€€ý“‹&ý“‹& /test-6/558worldanyoneTTý“‰êý“‰ê /test-6/559worldanyone^^ý“‰ôý“‰ô /test-6/556worldanyoneAAý“‰Òý“‰Ò /test-6/557worldanyoneJJý“‰Ýý“‰Ý /test-6/554worldanyone22ý“‰¾ý“‰¾ /test-6/555worldanyone==ý“‰Çý“‰Ç /test-6/552worldanyoneý“‰ªý“‰ª /test-6/553worldanyone''ý“‰µý“‰µ /test-6/550worldanyone  ý“‰’ý“‰’ /test-6/551worldanyoneý“‰žý“‰ž /test-6/560worldanyonehhý“‰ýý“‰ý /test-6/569worldanyoneÀÀý“ŠUý“ŠU /test-6/565worldanyone˜˜ý“Š/ý“Š/ /test-6/566worldanyone¢¢ý“Š;ý“Š; /test-6/567worldanyone¬¬ý“ŠDý“ŠD /test-6/568worldanyone¶¶ý“ŠLý“ŠL /test-6/561worldanyonessý“Šý“Š /test-6/562worldanyone}}ý“Šý“Š /test-6/563worldanyone††ý“Šý“Š /test-6/564worldanyone““ý“Š%ý“Š% /test-6/772worldanyone==ý“”åý“”å /test-6/505worldanyoneccý“‡{ý“‡{ /test-6/773worldanyoneGGý“”ïý“”ï /test-6/506worldanyonemmý“‡†ý“‡† /test-6/770worldanyone))ý“”Îý“”Î /test-6/503worldanyoneRRý“‡dý“‡d /test-6/771worldanyone33ý“”Úý“”Ú /test-6/504worldanyoneXXý“‡ný“‡n /test-6/509worldanyoneŒŒý“‡¥ý“‡¥ /test-6/774worldanyoneQQý“”øý“”ø /test-6/507worldanyonevvý“‡‘ý“‡‘ /test-6/508worldanyone€€ý“‡›ý“‡› /test-6/501worldanyone>>ý“‡Pý“‡P /test-6/502worldanyoneHHý“‡[ý“‡[ /test-6/500worldanyone44ý“‡Hý“‡H /test-6/763worldanyoneããý“”ý“” /test-6/764worldanyoneííý“”‹ý“”‹ /test-6/765worldanyoneøøý“”™ý“”™ /test-6/766worldanyoneý“”¥ý“”¥ /test-6/760worldanyoneÇÇý“”cý“”c /test-6/761worldanyoneÏÏý“”mý“”m /test-6/762worldanyoneÚÚý“”xý“”x /test-6/767worldanyone  ý“”¯ý“”¯ /test-6/768worldanyoneý“”¸ý“”¸ /test-6/769worldanyone  ý“”Äý“”Ä /test-6/754worldanyoneý“”)ý“”) /test-6/755worldanyone——ý“”5ý“”5 /test-6/752worldanyone€€ý“’Qý“’Q /test-6/753worldanyone‡‡ý“’[ý“’[ /test-6/750worldanyonellý“’=ý“’= /test-6/751worldanyoneuuý“’Gý“’G /test-6/758worldanyone³³ý“”Sý“”S /test-6/759worldanyone½½ý“”[ý“”[ /test-6/756worldanyone  ý“”=ý“”= /test-6/757worldanyone©©ý“”Jý“”J /test-6/595worldanyone··ý“‹^ý“‹^ /test-6/594worldanyone­­ý“‹Sý“‹S /test-6/391worldanyoneSSý“‚—ý“‚— /test-6/597worldanyoneËËý“‹˜ý“‹˜ /test-6/390worldanyoneJJý“‚Œý“‚Œ /test-6/596worldanyoneÁÁý“‹jý“‹j /test-6/393worldanyonehhý“‚ªý“‚ª /test-6/599worldanyoneßßý“‹©ý“‹© /test-6/392worldanyone]]ý“‚¡ý“‚¡ /test-6/598worldanyoneÕÕý“‹ ý“‹  /test-6/395worldanyonexxý“‚¾ý“‚¾ /test-6/394worldanyoneqqý“‚´ý“‚´ /test-6/396worldanyone‚‚ý“‚Éý“‚É /test-6/397worldanyoneŒŒý“‚Óý“‚Ó /test-6/398worldanyone––ý“‚Üý“‚Ü /test-6/399worldanyone  ý“‚åý“‚å /test-6/590worldanyoneŠŠý“‹-ý“‹- /test-6/591worldanyone””ý“‹6ý“‹6 /test-6/592worldanyonežžý“‹?ý“‹? /test-6/593worldanyone££ý“‹Hý“‹H /test-6/383worldanyoneý“‚Bý“‚B /test-6/384worldanyone  ý“‚Lý“‚L /test-6/381worldanyone î îý“‚-ý“‚- /test-6/382worldanyone ú úý“‚8ý“‚8 /test-6/380worldanyone é éý“‚"ý“‚" /test-6/389worldanyone@@ý“‚€ý“‚€ /test-6/388worldanyone77ý“‚vý“‚v /test-6/387worldanyone**ý“‚iý“‚i /test-6/386worldanyone  ý“‚`ý“‚` /test-6/385worldanyoneý“‚Wý“‚W /test-6/370worldanyone Œ Œý“€Òý“€Ò /test-6/371worldanyone “ “ý“€Ýý“€Ý /test-6/372worldanyone œ œý“€çý“€ç /test-6/373worldanyone ¦ ¦ý“€ñý“€ñ /test-6/379worldanyone ß ßý“‚ý“‚ /test-6/378worldanyone Ø Øý“‚ý“‚ /test-6/375worldanyone » »ý“ý“ /test-6/374worldanyone ° °ý“€úý“€ú /test-6/377worldanyone Î Îý“ý“ /test-6/376worldanyone Ä Äý“ý“ /test-6/361worldanyone 5 5ý“€qý“€q /test-6/362worldanyone ? ?ý“€}ý“€} /test-6/360worldanyone , ,ý“€eý“€e /test-6/366worldanyone e eý“€¬ý“€¬ /test-6/365worldanyone [ [ý“€¢ý“€¢ /test-6/364worldanyone Q Qý“€—ý“€— /test-6/363worldanyone H Hý“€Œý“€Œ /test-6/369worldanyone ƒ ƒý“€Çý“€Ç /test-6/368worldanyone y yý“€¾ý“€¾ /test-6/367worldanyone o oý“€¶ý“€¶ /test-6/350worldanyone Ð Ðý“èý“è /test-6/351worldanyone Ù Ùý“ôý“ô /test-6/353worldanyone í íý“€ý“€ /test-6/352worldanyone ã ãý“€ý“€ /test-6/355worldanyone þ þý“€%ý“€% /test-6/354worldanyone ô ôý“€ý“€ /test-6/357worldanyone  ý“€=ý“€= /test-6/356worldanyone  ý“€0ý“€0 /test-6/359worldanyone " "ý“€Vý“€V /test-6/358worldanyone  ý“€Iý“€I /test-6/159worldanyone÷÷ý“uØý“uØ /test-6/158worldanyoneîîý“uÍý“uÍ /test-6/157worldanyoneååý“u¾ý“u¾ /test-6/156worldanyoneØØý“u­ý“u­ /test-6/155worldanyoneÖÖý“u ý“u  /test-6/154worldanyoneÌÌý“u—ý“u— /test-6/152worldanyone¿¿ý“u~ý“u~ /test-6/601worldanyoneïïý“‹¿ý“‹¿ /test-6/153worldanyoneÃÃý“uŠý“uŠ /test-6/600worldanyoneééý“‹´ý“‹´ /test-6/150worldanyone««ý“ugý“ug /test-6/151worldanyoneµµý“uqý“uq /test-6/609worldanyone;;ý“Œ ý“Œ /test-6/608worldanyone33ý“Œý“Œ /test-6/607worldanyone))ý“‹öý“‹ö /test-6/606worldanyoneý“‹íý“‹í /test-6/605worldanyoneý“‹äý“‹ä /test-6/604worldanyone  ý“‹Ûý“‹Û /test-6/603worldanyoneý“‹Ñý“‹Ñ /test-6/602worldanyoneùùý“‹Èý“‹È /test-6/202worldanyoneŠŠý“xŠý“xŠ /test-6/203worldanyone““ý“x”ý“x” /test-6/204worldanyone˜˜ý“x¡ý“x¡ /test-6/205worldanyone¢¢ý“x¯ý“x¯ /test-6/200worldanyone{{ý“xsý“xs /test-6/201worldanyone‚‚ý“xý“x /test-6/169worldanyone]]ý“vký“vk /test-6/206worldanyone¬¬ý“x¸ý“x¸ /test-6/166worldanyone99ý“v>ý“v> /test-6/207worldanyone¶¶ý“xÆý“xÆ /test-6/165worldanyone44ý“v3ý“v3 /test-6/208worldanyone¼¼ý“xÔý“xÔ /test-6/168worldanyoneTTý“v^ý“v^ /test-6/209worldanyoneÈÈý“xäý“xä /test-6/167worldanyoneLLý“vRý“vR /test-6/161worldanyone  ý“uöý“uö /test-6/409worldanyoneøøý“ƒCý“ƒC /test-6/162worldanyoneý“v ý“v /test-6/408worldanyoneîîý“ƒ;ý“ƒ; /test-6/163worldanyone!!ý“vý“v /test-6/164worldanyone**ý“v(ý“v( /test-6/405worldanyone××ý“ƒý“ƒ /test-6/404worldanyoneÍÍý“ƒý“ƒ /test-6/407worldanyoneääý“ƒ0ý“ƒ0 /test-6/160worldanyoneý“uèý“uè /test-6/406worldanyoneááý“ƒ&ý“ƒ& /test-6/401worldanyone¯¯ý“‚øý“‚ø /test-6/400worldanyoneªªý“‚ïý“‚ï /test-6/403worldanyoneÃÃý“ƒ ý“ƒ /test-6/402worldanyone¹¹ý“ƒý“ƒ /test-6/211worldanyoneÖÖý“xýý“xý /test-6/212worldanyoneßßý“y ý“y /test-6/210worldanyoneÎÎý“xïý“xï /test-6/215worldanyoneùùý“y1ý“y1 /test-6/216worldanyoneý“yDý“yD /test-6/213worldanyoneèèý“yý“y /test-6/214worldanyoneòòý“y%ý“y% /test-6/219worldanyoneý“ytý“yt /test-6/179worldanyone»»ý“vÙý“vÙ /test-6/178worldanyone²²ý“vÏý“vÏ /test-6/217worldanyone  ý“yUý“yU /test-6/177worldanyone©©ý“vÅý“vÅ /test-6/218worldanyoneý“yhý“yh /test-6/176worldanyoneŸŸý“vºý“vº /test-6/170worldanyoneffý“vuý“vu /test-6/418worldanyoneMMý“ƒ¢ý“ƒ¢ /test-6/171worldanyoneooý“v€ý“v€ /test-6/417worldanyoneCCý“ƒ™ý“ƒ™ /test-6/416worldanyone99ý“ƒ‘ý“ƒ‘ /test-6/415worldanyone//ý“ƒ†ý“ƒ† /test-6/174worldanyoneˆˆý“vŸý“vŸ /test-6/623worldanyone¹¹ý“Œ’ý“Œ’ /test-6/175worldanyone••ý“v­ý“v­ /test-6/622worldanyone¯¯ý“Œ‰ý“Œ‰ /test-6/172worldanyoneyyý“vŠý“vŠ /test-6/621worldanyone¥¥ý“Œ€ý“Œ€ /test-6/173worldanyoneƒƒý“v–ý“v– /test-6/419worldanyoneQQý“ƒ¬ý“ƒ¬ /test-6/620worldanyoneŸŸý“Œuý“Œu /test-6/410worldanyoneý“ƒLý“ƒL /test-6/627worldanyoneààý“Œ¶ý“Œ¶ /test-6/626worldanyoneÖÖý“Œ®ý“Œ® /test-6/625worldanyoneÌÌý“Œ¤ý“Œ¤ /test-6/624worldanyoneÃÃý“Œšý“Œš /test-6/414worldanyone%%ý“ƒzý“ƒz /test-6/413worldanyoneý“ƒoý“ƒo /test-6/412worldanyoneý“ƒbý“ƒb /test-6/629worldanyoneññý“ŒËý“ŒË /test-6/411worldanyoneý“ƒVý“ƒV /test-6/628worldanyoneêêý“ŒÀý“ŒÀ /test-6/220worldanyone''ý“yƒý“yƒ /test-6/221worldanyone11ý“y‘ý“y‘ /test-6/222worldanyone::ý“y¢ý“y¢ /test-6/223worldanyoneDDý“y³ý“y³ /test-6/224worldanyoneNNý“yÄý“yÄ /test-6/225worldanyoneXXý“yÓý“yÓ /test-6/226worldanyoneaaý“yãý“yã /test-6/227worldanyonekký“yóý“yó /test-6/228worldanyoneuuý“yÿý“yÿ /test-6/188worldanyone  ý“w;ý“w; /test-6/229worldanyoneý“z ý“z /test-6/187worldanyoneý“w1ý“w1 /test-6/189worldanyoneý“wEý“wE /test-6/427worldanyone‘‘ý“ƒ÷ý“ƒ÷ /test-6/180worldanyoneÁÁý“väý“vä /test-6/426worldanyoneý“ƒíý“ƒí /test-6/181worldanyoneËËý“vðý“vð /test-6/429worldanyone¥¥ý“„ý“„ /test-6/182worldanyoneÕÕý“vûý“vû /test-6/428worldanyone››ý“„ý“„ /test-6/183worldanyoneßßý“wý“w /test-6/610worldanyoneAAý“Œý“Œ /test-6/184worldanyoneççý“wý“w /test-6/185worldanyoneôôý“wý“w /test-6/612worldanyoneUUý“Œ(ý“Œ( /test-6/186worldanyone÷÷ý“w(ý“w( /test-6/611worldanyoneKKý“Œ ý“Œ /test-6/614worldanyonebbý“Œ:ý“Œ: /test-6/613worldanyone__ý“Œ0ý“Œ0 /test-6/421worldanyoneeeý“ƒ¿ý“ƒ¿ /test-6/616worldanyonewwý“ŒOý“ŒO /test-6/420worldanyone[[ý“ƒ·ý“ƒ· /test-6/615worldanyonemmý“ŒFý“ŒF /test-6/423worldanyoneqqý“ƒÒý“ƒÒ /test-6/618worldanyone‹‹ý“Œaý“Œa /test-6/422worldanyoneooý“ƒÈý“ƒÈ /test-6/617worldanyoneý“ŒXý“ŒX /test-6/425worldanyone……ý“ƒåý“ƒå /test-6/424worldanyone{{ý“ƒÜý“ƒÜ /test-6/619worldanyone••ý“Œký“Œk /test-6/116worldanyoneyyý“sý“s /test-6/117worldanyoneý“s«ý“s« /test-6/114worldanyoneffý“s†ý“s† /test-6/115worldanyoneooý“s’ý“s’ /test-6/112worldanyoneQQý“saý“sa /test-6/113worldanyone]]ý“ssý“ss /test-6/110worldanyone??ý“s<ý“s< /test-6/111worldanyoneHHý“sOý“sO /test-6/118worldanyone‰‰ý“s»ý“s» /test-6/119worldanyone””ý“sÊý“sÊ /test-6/435worldanyoneÛÛý“„Qý“„Q /test-6/436worldanyoneååý“„\ý“„\ /test-6/433worldanyoneÍÍý“„9ý“„9 /test-6/434worldanyoneÒÒý“„Eý“„E /test-6/431worldanyone¹¹ý“„ý“„ /test-6/432worldanyoneÃÃý“„/ý“„/ /test-6/430worldanyone¯¯ý“„ý“„ /test-6/439worldanyoneýýý“„xý“„x /test-6/437worldanyoneïïý“„dý“„d /test-6/438worldanyoneøøý“„ný“„n /test-6/125worldanyoneÉÉý“tý“t /test-6/126worldanyoneÓÓý“tý“t /test-6/127worldanyoneÖÖý“t'ý“t' /test-6/128worldanyoneááý“t5ý“t5 /test-6/121worldanyone¨¨ý“sâý“sâ /test-6/122worldanyone°°ý“sïý“sï /test-6/123worldanyone¹¹ý“sûý“sû /test-6/124worldanyone¿¿ý“t ý“t /test-6/129worldanyoneêêý“tAý“tA /test-6/444worldanyone''ý“„¬ý“„¬ /test-6/445worldanyone11ý“„¶ý“„¶ /test-6/446worldanyone;;ý“„¿ý“„¿ /test-6/447worldanyoneEEý“„Èý“„È /test-6/440worldanyoneý“„‚ý“„‚ /test-6/441worldanyoneý“„Šý“„Š /test-6/442worldanyoneý“„•ý“„• /test-6/443worldanyoneý“„ ý“„  /test-6/120worldanyonežžý“sÕý“sÕ /test-6/448worldanyoneJJý“„Òý“„Ò /test-6/449worldanyoneUUý“„Ýý“„Ý /test-6/134worldanyoneý“tý“t /test-6/135worldanyone!!ý“tŸý“tŸ /test-6/132worldanyone  ý“tlý“tl /test-6/133worldanyoneý“t|ý“t| /test-6/138worldanyone??ý“tÀý“tÀ /test-6/139worldanyoneJJý“tËý“tË /test-6/136worldanyone++ý“t«ý“t« /test-6/137worldanyone55ý“t¶ý“t¶ /test-6/450worldanyone]]ý“„òý“„ò /test-6/453worldanyonexxý“…ý“… /test-6/454worldanyone‚‚ý“…ý“… /test-6/451worldanyoneffý“„ýý“„ý /test-6/452worldanyoneooý“…ý“… /test-6/457worldanyone  ý“…=ý“…= /test-6/458worldanyoneªªý“…Fý“…F /test-6/455worldanyoneŒŒý“…(ý“…( /test-6/456worldanyone––ý“…2ý“…2 /test-6/459worldanyone¬¬ý“…Vý“…V /test-6/131worldanyoneþþý“t[ý“t[ /test-6/130worldanyoneööý“tNý“tN /test-6/143worldanyonemmý“tüý“tü /test-6/144worldanyone{{ý“uý“u /test-6/145worldanyone„„ý“u(ý“u( /test-6/146worldanyoneŒŒý“u3ý“u3 /test-6/147worldanyone““ý“u?ý“u? /test-6/148worldanyoneý“uHý“uH /test-6/149worldanyone££ý“uXý“uX /test-6/461worldanyoneÀÀý“…rý“…r /test-6/460worldanyone¶¶ý“…hý“…h /test-6/462worldanyoneÊÊý“…zý“…z /test-6/463worldanyoneÔÔý“…„ý“…„ /test-6/464worldanyoneÖÖý“…’ý“…’ /test-6/465worldanyoneááý“…ý“… /test-6/466worldanyoneêêý“…¬ý“…¬ /test-6/467worldanyoneööý“…Äý“…Ä /test-6/468worldanyoneÿÿý“…Ðý“…Ð /test-6/469worldanyone  ý“…Úý“…Ú /test-6/140worldanyoneTTý“tØý“tØ /test-6/142worldanyoneeeý“tðý“tð /test-6/141worldanyone[[ý“tåý“tå /test-6/687worldanyoneý“1ý“1 /test-6/686worldanyoneý“'ý“' /test-6/689worldanyone""ý“Cý“C /test-6/688worldanyoneý“:ý“: /test-6/683worldanyoneêêý“ý“ /test-6/682worldanyoneààý“Žÿý“Žÿ /test-6/685worldanyoneýýý“ý“ /test-6/684worldanyoneôôý“ý“ /test-6/680worldanyoneÍÍý“Žéý“Žé /test-6/681worldanyone××ý“Žöý“Žö /test-6/678worldanyoneÀÀý“ŽÒý“ŽÒ /test-6/677worldanyone¶¶ý“ŽÊý“ŽÊ /test-6/676worldanyone¬¬ý“ŽÂý“ŽÂ /test-6/675worldanyone££ý“Ž·ý“Ž· /test-6/674worldanyonežžý“ެý“ެ /test-6/673worldanyone””ý“Ž¢ý“Ž¢ /test-6/672worldanyone‰‰ý“Žpý“Žp /test-6/671worldanyoneý“Žgý“Žg /test-6/679worldanyoneÄÄý“ŽÞý“ŽÞ /test-6/670worldanyoneuuý“Ž^ý“Ž^ /test-6/696worldanyonejjý“žý“ž /test-6/695worldanyone[[ý“Žý“Ž /test-6/694worldanyoneNNý“€ý“€ /test-6/693worldanyoneGGý“tý“t /test-6/699worldanyoneƒƒý“Éý“É /test-6/698worldanyoneyyý“ºý“º /test-6/697worldanyonenný“ªý“ª /test-6/691worldanyone66ý“\ý“\ /test-6/692worldanyone??ý“hý“h /test-6/690worldanyone&&ý“Ný“N /test-6/646worldanyoneý“sý“s /test-6/647worldanyoneššý“ý“ /test-6/648worldanyone¤¤ý“ˆý“ˆ /test-6/649worldanyone®®ý“‘ý“‘ /test-6/195worldanyoneNNý“x9ý“x9 /test-6/642worldanyonejjý“Hý“H /test-6/194worldanyoneCCý“x+ý“x+ /test-6/643worldanyonettý“Sý“S /test-6/197worldanyoneaaý“xRý“xR /test-6/644worldanyoneý“_ý“_ /test-6/196worldanyoneWWý“xFý“xF /test-6/645worldanyone‹‹ý“iý“i /test-6/191worldanyone!!ý“waý“wa /test-6/190worldanyoneý“wPý“wP /test-6/193worldanyone::ý“xý“x /test-6/640worldanyoneWWý“2ý“2 /test-6/192worldanyone,,ý“wpý“wp /test-6/641worldanyoneaaý“=ý“= /test-6/198worldanyonehhý“x^ý“x^ /test-6/199worldanyonerrý“xjý“xj /test-6/639worldanyoneMMý“)ý“) /test-6/637worldanyone99ý“ý“ /test-6/638worldanyoneCCý“ ý“ /test-6/635worldanyone**ý“ý“ /test-6/636worldanyone44ý“ ý“ /test-6/633worldanyoneý“Œñý“Œñ /test-6/634worldanyone  ý“Œûý“Œû /test-6/631worldanyoneý“ŒÞý“ŒÞ /test-6/632worldanyone  ý“Œèý“Œè /test-6/630worldanyoneøøý“ŒÔý“ŒÔ /test-6/668worldanyoneaaý“ŽKý“ŽK /test-6/669worldanyonekký“ŽUý“ŽU /test-6/660worldanyoneý“ÿý“ÿ /test-6/661worldanyone&&ý“Ž ý“Ž /test-6/662worldanyone--ý“Žý“Ž /test-6/663worldanyone66ý“Žý“Ž /test-6/664worldanyone@@ý“Ž'ý“Ž' /test-6/665worldanyoneJJý“Ž0ý“Ž0 /test-6/666worldanyoneTTý“Ž8ý“Ž8 /test-6/667worldanyoneWWý“ŽAý“ŽA /test-6/659worldanyoneý“÷ý“÷ /test-6/657worldanyoneþþý“äý“ä /test-6/658worldanyoneý“íý“í /test-6/651worldanyoneÂÂý“¥ý“¥ /test-6/652worldanyoneÎÎý“°ý“° /test-6/650worldanyone¹¹ý“›ý“› /test-6/655worldanyoneììý“Òý“Ò /test-6/656worldanyoneôôý“Ûý“Û /test-6/653worldanyoneÖÖý“¹ý“¹ /test-6/654worldanyoneààý“Åý“Å /test-6/35worldanyonewwý“m§ý“m§ /test-6/36worldanyoneý“m³ý“m³ /test-6/33worldanyoneiiý“m‡ý“m‡ /test-6/34worldanyoneooý“m–ý“m– /test-6/39worldanyone™™ý“mòý“mò /test-6/37worldanyone‰‰ý“mÁý“mÁ /test-6/38worldanyone““ý“máý“má /test-6/43worldanyoneÂÂý“n9ý“n9 /test-6/42worldanyone¸¸ý“n+ý“n+ /test-6/41worldanyone®®ý“ný“n /test-6/40worldanyone¥¥ý“ný“n /test-6/22worldanyoneüüý“l¾ý“l¾ /test-6/23worldanyoneý“lÐý“lÐ /test-6/24worldanyoneý“lßý“lß /test-6/25worldanyoneý“lÿý“lÿ /test-6/26worldanyone&&ý“mý“m /test-6/27worldanyone11ý“m$ý“m$ /test-6/28worldanyone;;ý“m7ý“m7 /test-6/29worldanyoneEEý“mDý“mD /test-6/30worldanyoneOOý“mVý“mV /test-6/32worldanyone__ý“mzý“mz /test-6/31worldanyoneWWý“mhý“mh /test-6/19worldanyoneÞÞý“l…ý“l… /test-6/17worldanyoneÏÏý“ldý“ld /test-6/18worldanyoneÕÕý“lsý“ls /test-6/15worldanyone»»ý“lEý“lE /test-6/16worldanyoneÅÅý“lWý“lW /test-6/13worldanyone¦¦ý“l"ý“l" /test-6/14worldanyone²²ý“l7ý“l7 /test-6/11worldanyone˜˜ý“kýý“ký /test-6/12worldanyoneŸŸý“lý“l /test-6/21worldanyoneóóý“lªý“lª /test-6/20worldanyoneééý“l˜ý“l˜ /test-6/10worldanyoneŽŽý“kïý“kï /test-6/79worldanyoneý“q)ý“q) /test-6/78worldanyoneý“qý“q /test-6/77worldanyoneý“p§ý“p§ /test-6/82worldanyone77ý“qUý“qU /test-6/83worldanyoneAAý“qbý“qb /test-6/80worldanyone##ý“q8ý“q8 /test-6/81worldanyone..ý“qIý“qI /test-6/86worldanyoneYYý“qŽý“qŽ /test-6/87worldanyoneddý“q™ý“q™ /test-6/84worldanyoneMMý“qrý“qr /test-6/85worldanyoneSSý“q€ý“q€ /test-6/67worldanyone­­ý“pý“p /test-6/66worldanyone¢¢ý“pý“p /test-6/69worldanyoneÀÀý“p1ý“p1 /test-6/68worldanyone··ý“pý“p /test-6/70worldanyoneÈÈý“pAý“pA /test-6/71worldanyoneÓÓý“pQý“pQ /test-6/72worldanyoneÛÛý“pbý“pb /test-6/73worldanyoneååý“ptý“pt /test-6/74worldanyoneïïý“pƒý“pƒ /test-6/75worldanyoneùùý“pý“p /test-6/76worldanyoneý“p™ý“p™ /test-6/59worldanyonebbý“oŽý“oŽ /test-6/58worldanyoneYYý“oý“o /test-6/57worldanyoneRRý“orý“or /test-6/56worldanyoneEEý“oLý“oL /test-6/55worldanyone99ý“o1ý“o1 /test-6/64worldanyone‘‘ý“oâý“oâ /test-6/65worldanyone™™ý“oóý“oó /test-6/62worldanyone}}ý“oÁý“oÁ /test-6/63worldanyone‡‡ý“oÑý“oÑ /test-6/60worldanyonejjý“oŸý“oŸ /test-6/61worldanyonettý“o³ý“o³ /test-6/49worldanyoneýýý“nªý“nª /test-6/48worldanyoneòòý“n’ý“n’ /test-6/45worldanyoneÓÓý“nRý“nR /test-6/44worldanyoneÌÌý“nCý“nC /test-6/47worldanyoneééý“n}ý“n} /test-6/46worldanyoneÜÜý“ndý“nd /test-6/51worldanyoneý“nØý“nØ /test-6/52worldanyoneý“nðý“nð /test-6/53worldanyone##ý“o ý“o /test-6/54worldanyone..ý“oý“o /test-6/50worldanyoneý“nÁý“nÁ /test-6/281worldanyone X Xý“|¥ý“|¥ /test-6/487worldanyone¶¶ý“†¹ý“†¹ /test-6/280worldanyone N Ný“|›ý“|› /test-6/486worldanyone­­ý“†ªý“†ª /test-6/485worldanyone¢¢ý“†žý“†ž /test-6/484worldanyone™™ý“†ý“† /test-6/285worldanyone x xý“|Ïý“|Ï /test-6/284worldanyone v vý“|Æý“|Æ /test-6/283worldanyone l lý“|½ý“|½ /test-6/489worldanyoneËËý“†Ñý“†Ñ /test-6/282worldanyone b bý“|²ý“|² /test-6/488worldanyoneÁÁý“†Åý“†Å /test-6/288worldanyone Ž Žý“|ïý“|ï /test-6/289worldanyone ™ ™ý“|üý“|ü /test-6/286worldanyone ‚ ‚ý“|Úý“|Ú /test-6/287worldanyone Œ Œý“|äý“|ä /test-6/482worldanyoneŠŠý“†wý“†w /test-6/483worldanyoneý“†„ý“†„ /test-6/480worldanyonemmý“†Uý“†U /test-6/481worldanyonewwý“†eý“†e /test-6/474worldanyone55ý“†ý“† /test-6/473worldanyone11ý“†ý“† /test-6/270worldanyone ï ïý“|ý“| /test-6/476worldanyoneIIý“†'ý“†' /test-6/475worldanyone??ý“†ý“† /test-6/272worldanyone  ý“|8ý“|8 /test-6/478worldanyone[[ý“†:ý“†: /test-6/271worldanyone ø øý“|'ý“|' /test-6/477worldanyoneSSý“†/ý“†/ /test-6/274worldanyone  ý“|Sý“|S /test-6/273worldanyone  ý“|Fý“|F /test-6/479worldanyoneffý“†Hý“†H /test-6/275worldanyone  ý“|^ý“|^ /test-6/276worldanyone ) )ý“|ký“|k /test-6/277worldanyone 4 4ý“|yý“|y /test-6/278worldanyone > >ý“|…ý“|… /test-6/279worldanyone H Hý“|ý“| /test-6/470worldanyoneý“…åý“…å /test-6/471worldanyoneý“…ïý“…ï /test-6/472worldanyone''ý“…úý“…ú /test-6/109worldanyone55ý“sý“s /test-6/108worldanyone**ý“ròý“rò /test-6/107worldanyone##ý“ràý“rà /test-6/106worldanyoneý“rÌý“rÌ /test-6/105worldanyoneý“r¸ý“r¸ /test-6/104worldanyoneý“r¥ý“r¥ /test-6/103worldanyoneþþý“r“ý“r“ /test-6/99worldanyoneÚÚý“rGý“rG /test-6/102worldanyoneôôý“rý“r /test-6/101worldanyoneëëý“rný“rn /test-6/100worldanyoneßßý“rVý“rV /test-6/98worldanyoneÏÏý“r3ý“r3 /test-6/97worldanyoneÅÅý“r$ý“r$ /test-6/96worldanyone½½ý“rý“r /test-6/95worldanyone³³ý“r ý“r /test-6/94worldanyone©©ý“qúý“qú /test-6/93worldanyone  ý“qìý“qì /test-6/92worldanyone––ý“qßý“qß /test-6/91worldanyone‹‹ý“qÌý“qÌ /test-6/294worldanyone É Éý“}4ý“}4 /test-6/90worldanyoneý“q¼ý“q¼ /test-6/293worldanyone À Àý“}+ý“}+ /test-6/499worldanyone**ý“‡=ý“‡= /test-6/296worldanyone Ý Ýý“}Vý“}V /test-6/295worldanyone Ï Ïý“}Bý“}B /test-6/290worldanyone £ £ý“}ý“} /test-6/496worldanyoneý“‡ý“‡ /test-6/495worldanyoneý“‡ ý“‡ /test-6/292worldanyone ¶ ¶ý“}ý“} /test-6/498worldanyone  ý“‡3ý“‡3 /test-6/291worldanyone « «ý“}ý“} /test-6/497worldanyoneý“‡)ý“‡) /test-6/491worldanyoneßßý“†åý“†å /test-6/492worldanyoneééý“†îý“†î /test-6/493worldanyoneòòý“†÷ý“†÷ /test-6/494worldanyoneøøý“‡ý“‡ /test-6/297worldanyone ë ëý“}hý“}h /test-6/298worldanyone ï ïý“}sý“}s /test-6/299worldanyone ù ùý“}‚ý“}‚ /test-6/490worldanyoneÕÕý“†Ûý“†Û /test-6/88worldanyonemmý“q¢ý“q¢ /test-6/89worldanyonessý“q®ý“q® /test-6/240worldanyoneßßý“z ý“z  /test-6/241worldanyoneééý“z­ý“z­ /test-6/245worldanyone  ý“zñý“zñ /test-6/244worldanyone  ý“zæý“zæ /test-6/243worldanyoneþþý“zÝý“zÝ /test-6/242worldanyoneõõý“zºý“zº /test-6/249worldanyone 6 6ý“{ý“{ /test-6/248worldanyone , ,ý“{ý“{ /test-6/247worldanyone # #ý“{ý“{ /test-6/246worldanyone  ý“zûý“zû /test-6/230worldanyoneˆˆý“zý“z /test-6/239worldanyoneÕÕý“z•ý“z• /test-6/232worldanyone——ý“z=ý“z= /test-6/231worldanyone’’ý“z0ý“z0 /test-6/234worldanyone¨¨ý“zWý“zW /test-6/233worldanyone  ý“zLý“zL /test-6/236worldanyoneººý“zsý“zs /test-6/235worldanyone¯¯ý“zdý“zd /test-6/238worldanyoneËËý“z‰ý“z‰ /test-6/237worldanyoneÁÁý“zý“z /test-6/262worldanyone ¨ ¨ý“{«ý“{« /test-6/263worldanyone ² ²ý“{µý“{µ /test-6/260worldanyone ” ”ý“{‘ý“{‘ /test-6/261worldanyone ¢ ¢ý“{žý“{ž /test-6/269worldanyone ä äý“|ý“| /test-6/268worldanyone Ú Úý“{÷ý“{÷ /test-6/267worldanyone Ò Òý“{êý“{ê /test-6/266worldanyone È Èý“{Üý“{Ü /test-6/265worldanyone ¿ ¿ý“{Íý“{Í /test-6/264worldanyone ¼ ¼ý“{Àý“{À /test-6/250worldanyone ; ;ý“{%ý“{% /test-6/251worldanyone E Eý“{.ý“{. /test-6/252worldanyone O Oý“{7ý“{7 /test-6/258worldanyone „ „ý“{vý“{v /test-6/257worldanyone { {ý“{hý“{h /test-6/259worldanyone Ž Žý“{„ý“{„ /test-6/254worldanyone _ _ý“{Mý“{M /test-6/253worldanyone Y Yý“{@ý“{@ /test-6/256worldanyone q qý“{_ý“{_ /test-6/255worldanyone g gý“{Wý“{W/test-3worldanyoneý“jšý“jšÄ /test-3/338worldanyone z zý“€Ãý“€Ã /test-3/339worldanyone „ „ý“€Ìý“€Ì /test-3/332worldanyone @ @ý“€ý“€ /test-3/333worldanyone K Ký“€ý“€ /test-3/159worldanyoneSSý“v]ý“v] /test-3/330worldanyone + +ý“€bý“€b /test-3/158worldanyoneJJý“vQý“vQ /test-3/331worldanyone 6 6ý“€rý“€r /test-3/157worldanyoneAAý“vAý“vA /test-3/336worldanyone g gý“€­ý“€­ /test-3/156worldanyone77ý“v5ý“v5 /test-3/337worldanyone r rý“€¸ý“€¸ /test-3/155worldanyone++ý“v(ý“v( /test-3/334worldanyone T Tý“€˜ý“€˜ /test-3/154worldanyone$$ý“vý“v /test-3/335worldanyone _ _ý“€£ý“€£ /test-3/601worldanyoneý“Žý“Ž /test-3/531worldanyoneXXý“Šøý“Šø /test-3/152worldanyoneý“vý“v /test-3/600worldanyoneý“úý“ú /test-3/530worldanyoneQQý“Šìý“Šì /test-3/153worldanyoneý“v ý“v /test-3/150worldanyoneüüý“ußý“uß /test-3/151worldanyoneý“uñý“uñ /test-3/535worldanyoneƒƒý“‹&ý“‹& /test-3/534worldanyonexxý“‹ý“‹ /test-3/533worldanyonessý“‹ý“‹ /test-3/532worldanyoneggý“‹ý“‹ /test-3/609worldanyonettý“ŽZý“ŽZ /test-3/539worldanyone¬¬ý“‹Qý“‹Q /test-3/608worldanyoneeeý“ŽMý“ŽM /test-3/538worldanyone¡¡ý“‹Fý“‹F /test-3/607worldanyone^^ý“ŽBý“ŽB /test-3/537worldanyone••ý“‹;ý“‹; /test-3/606worldanyoneOOý“Ž5ý“Ž5 /test-3/536worldanyoneý“‹1ý“‹1 /test-3/605worldanyoneGGý“Ž+ý“Ž+ /test-3/604worldanyone>>ý“Ž#ý“Ž# /test-3/603worldanyone33ý“Žý“Ž /test-3/602worldanyone))ý“Žý“Ž /test-3/202worldanyoneý“ycý“yc /test-3/349worldanyone ë ëý“‚&ý“‚& /test-3/203worldanyoneý“yrý“yr /test-3/204worldanyone%%ý“y€ý“y€ /test-3/205worldanyone//ý“yý“y /test-3/200worldanyoneúúý“y8ý“y8 /test-3/201worldanyoneý“yMý“yM /test-3/341worldanyone ˜ ˜ý“€ßý“€ß /test-3/169worldanyoneµµý“vÔý“vÔ /test-3/342worldanyone    ý“€éý“€é /test-3/343worldanyone « «ý“€ôý“€ô /test-3/344worldanyone µ µý“€ÿý“€ÿ /test-3/166worldanyone™™ý“v¯ý“v¯ /test-3/206worldanyone66ý“yŸý“yŸ /test-3/345worldanyone ¾ ¾ý“ ý“ /test-3/165worldanyoneŽŽý“v£ý“v£ /test-3/207worldanyoneBBý“y±ý“y± /test-3/346worldanyone È Èý“ý“ /test-3/168worldanyone««ý“vËý“vË /test-3/208worldanyoneKKý“y¿ý“y¿ /test-3/347worldanyone × ×ý“‚ ý“‚ /test-3/167worldanyone¥¥ý“vÀý“vÀ /test-3/209worldanyoneVVý“yÑý“yÑ /test-3/348worldanyone á áý“‚ý“‚ /test-3/540worldanyone¶¶ý“‹[ý“‹[ /test-3/409worldanyoneXXý“„Þý“„Þ /test-3/161worldanyoneeeý“vuý“vu /test-3/3worldanyone&&ý“jâý“jâ /test-3/408worldanyoneNNý“„Óý“„Ó /test-3/162worldanyoneppý“v€ý“v€ /test-3/2worldanyone  ý“jÍý“jÍ /test-3/542worldanyoneÊÊý“‹ý“‹ /test-3/163worldanyone{{ý“v‹ý“v‹ /test-3/1worldanyoneý“j»ý“j» /test-3/541worldanyone¿¿ý“‹dý“‹d /test-3/164worldanyone„„ý“v—ý“v— /test-3/0worldanyoneý“j«ý“j« /test-3/544worldanyoneÝÝý“‹§ý“‹§ /test-3/405worldanyone++ý“„®ý“„® /test-3/7worldanyoneGGý“kGý“kG /test-3/543worldanyoneÔÔý“‹œý“‹œ /test-3/404worldanyone!!ý“„¡ý“„¡ /test-3/6worldanyone??ý“k-ý“k- /test-3/546worldanyoneîîý“‹¾ý“‹¾ /test-3/407worldanyoneCCý“„Æý“„Æ /test-3/5worldanyone77ý“ký“k /test-3/340worldanyone Ž Žý“€Õý“€Õ /test-3/545worldanyoneããý“‹²ý“‹² /test-3/406worldanyone55ý“„¹ý“„¹ /test-3/160worldanyone\\ý“vký“vk /test-3/4worldanyone//ý“ký“k /test-3/548worldanyoneý“‹Òý“‹Ò /test-3/401worldanyoneý“„zý“„z /test-3/547worldanyoneøøý“‹Èý“‹È /test-3/400worldanyoneùùý“„ný“„n /test-3/403worldanyoneý“„”ý“„” /test-3/9worldanyoneXXý“kyý“ky /test-3/549worldanyone  ý“‹Üý“‹Ü /test-3/402worldanyone  ý“„‡ý“„‡ /test-3/8worldanyoneMMý“k]ý“k] /test-3/211worldanyonenný“yõý“yõ /test-3/212worldanyonexxý“zý“z /test-3/210worldanyonebbý“yäý“yä /test-3/215worldanyone––ý“z:ý“z: /test-3/318worldanyone ­ ­ý“¿ý“¿ /test-3/216worldanyone¤¤ý“zSý“zS /test-3/319worldanyone · ·ý“Ëý“Ë /test-3/213worldanyone‚‚ý“zý“z /test-3/316worldanyone š šý“¤ý“¤ /test-3/214worldanyoneŒŒý“z(ý“z( /test-3/317worldanyone    ý“¯ý“¯ /test-3/179worldanyoneý“wSý“wS /test-3/219worldanyoneÀÀý“z}ý“z} /test-3/314worldanyone † †ý“ý“ /test-3/178worldanyoneý“wDý“wD /test-3/315worldanyone  ý“˜ý“˜ /test-3/177worldanyone  ý“w8ý“w8 /test-3/217worldanyone¬¬ý“zaý“za /test-3/312worldanyone r rý“wý“w /test-3/176worldanyoneþþý“w*ý“w* /test-3/218worldanyone¶¶ý“zrý“zr /test-3/313worldanyone | |ý“„ý“„ /test-3/310worldanyone d dý“cý“c /test-3/311worldanyone n ný“ký“k /test-3/513worldanyone  ý“Š2ý“Š2 /test-3/418worldanyone®®ý“…Wý“…W /test-3/170worldanyone¿¿ý“vÜý“vÜ /test-3/512worldanyone––ý“Š*ý“Š* /test-3/417worldanyone¨¨ý“…Cý“…C /test-3/171worldanyoneÉÉý“væý“væ /test-3/511worldanyoneŒŒý“Š!ý“Š! /test-3/416worldanyoneŸŸý“…8ý“…8 /test-3/510worldanyone„„ý“Šý“Š /test-3/415worldanyone••ý“….ý“…. /test-3/623worldanyone  ý“(ý“( /test-3/174worldanyoneééý“wý“w /test-3/622worldanyoneþþý“ý“ /test-3/175worldanyoneòòý“wý“w /test-3/621worldanyoneóóý“ý“ /test-3/172worldanyoneÏÏý“vòý“vò /test-3/620worldanyoneééý“ý“ /test-3/419worldanyone¾¾ý“…ký“…k /test-3/173worldanyoneÝÝý“wý“w /test-3/627worldanyone22ý“[ý“[ /test-3/410worldanyonebbý“„ûý“„û /test-3/626worldanyone--ý“Oý“O /test-3/625worldanyoneý“Aý“A /test-3/519worldanyoneÛÛý“Šuý“Šu /test-3/624worldanyoneý“3ý“3 /test-3/518worldanyoneÔÔý“Šký“Šk /test-3/517worldanyoneÆÆý“Š`ý“Š` /test-3/414worldanyone‹‹ý“…%ý“…% /test-3/516worldanyone¼¼ý“ŠSý“ŠS /test-3/413worldanyoneý“…ý“… /test-3/629worldanyoneMMý“uý“u /test-3/515worldanyone³³ý“ŠGý“ŠG /test-3/412worldanyonevvý“…ý“… /test-3/628worldanyone@@ý“iý“i /test-3/514worldanyone©©ý“Š<ý“Š< /test-3/411worldanyonellý“…ý“… /test-3/220worldanyoneÊÊý“zˆý“zˆ /test-3/221worldanyoneÔÔý“z”ý“z” /test-3/222worldanyoneÞÞý“z ý“z  /test-3/223worldanyoneççý“zªý“zª /test-3/224worldanyoneññý“z´ý“z´ /test-3/327worldanyone  ý“€<ý“€< /test-3/225worldanyoneûûý“z¿ý“z¿ /test-3/328worldanyone  ý“€Gý“€G /test-3/226worldanyone  ý“zäý“zä /test-3/329worldanyone  ý“€Tý“€T /test-3/227worldanyone  ý“zïý“zï /test-3/188worldanyonewwý“xpý“xp /test-3/228worldanyone  ý“züý“zü /test-3/323worldanyone á áý“€ý“€ /test-3/187worldanyonemmý“x`ý“x` /test-3/229worldanyone " "ý“{ý“{ /test-3/324worldanyone ê êý“€ ý“€ /test-3/325worldanyone ø øý“€ý“€ /test-3/189worldanyoneý“x{ý“x{ /test-3/326worldanyone  ý“€*ý“€* /test-3/320worldanyone  Âý“×ý“× /test-3/321worldanyone Î Îý“åý“å /test-3/322worldanyone Ø Øý“òý“ò /test-3/522worldanyoneúúý“Š”ý“Š” /test-3/427worldanyone  ý“…Þý“…Þ /test-3/521worldanyoneîîý“ŠŠý“ŠŠ /test-3/426worldanyoneý“…Òý“…Ò /test-3/180worldanyone##ý“wbý“wb /test-3/524worldanyoneý“Š«ý“Š« /test-3/429worldanyone  ý“…÷ý“…÷ /test-3/181worldanyone//ý“wpý“wp /test-3/523worldanyoneý“ŠŸý“ŠŸ /test-3/428worldanyoneý“…ìý“…ì /test-3/182worldanyone<<ý“x%ý“x% /test-3/610worldanyone~~ý“Žeý“Že /test-3/183worldanyoneFFý“x.ý“x. /test-3/184worldanyoneOOý“x9ý“x9 /test-3/612worldanyoneŽŽý“Žwý“Žw /test-3/520worldanyoneææý“Šý“Š /test-3/185worldanyoneZZý“xHý“xH /test-3/611worldanyoneˆˆý“Žný“Žn /test-3/186worldanyoneddý“xTý“xT /test-3/614worldanyone¥¥ý“ޏý“ޏ /test-3/613worldanyoneššý“Ž«ý“Ž« /test-3/529worldanyoneBBý“Šàý“Šà /test-3/616worldanyone¼¼ý“ŽÐý“ŽÐ /test-3/421worldanyoneÓÓý“…„ý“…„ /test-3/615worldanyone²²ý“ŽÅý“ŽÅ /test-3/420worldanyoneÆÆý“…wý“…w /test-3/618worldanyoneÔÔý“Žëý“Žë /test-3/526worldanyone##ý“ŠÀý“ŠÀ /test-3/423worldanyoneææý“… ý“…  /test-3/617worldanyoneÆÆý“ŽÞý“ŽÞ /test-3/525worldanyoneý“жý“ж /test-3/422worldanyoneÜÜý“…”ý“…” /test-3/528worldanyone99ý“ŠÕý“ŠÕ /test-3/425worldanyoneôôý“…Ãý“…à /test-3/619worldanyoneÜÜý“Žøý“Žø /test-3/527worldanyone//ý“ŠËý“ŠË /test-3/424worldanyoneññý“…®ý“…® /test-3/116worldanyone««ý“sìý“sì /test-3/117worldanyone··ý“súý“sú /test-3/114worldanyone––ý“sÎý“sÎ /test-3/115worldanyone¢¢ý“sÝý“sÝ /test-3/571worldanyoneîîý“ŒÊý“ŒÊ /test-3/112worldanyone€€ý“s¬ý“s¬ /test-3/570worldanyoneææý“Œ¿ý“Œ¿ /test-3/113worldanyoneŒŒý“s¿ý“s¿ /test-3/110worldanyonekký“sŠý“sŠ /test-3/111worldanyonettý“sšý“sš /test-3/118worldanyoneÁÁý“t ý“t /test-3/119worldanyoneÌÌý“tý“t /test-3/435worldanyone\\ý“†:ý“†: /test-3/436worldanyoneggý“†Iý“†I /test-3/433worldanyoneHHý“†ý“† /test-3/434worldanyoneLLý“†+ý“†+ /test-3/431worldanyone44ý“†ý“† /test-3/700worldanyoneý“”¼ý“”¼ /test-3/432worldanyone>>ý“†ý“† /test-3/430worldanyone**ý“†ý“† /test-3/703worldanyone77ý“”Üý“”Ü /test-3/574worldanyoneý“Œêý“Œê /test-3/704worldanyone??ý“”æý“”æ /test-3/575worldanyoneý“Œóý“Œó /test-3/701worldanyone##ý“”Æý“”Æ /test-3/572worldanyoneùùý“ŒÕý“ŒÕ /test-3/702worldanyone,,ý“”Ïý“”Ï /test-3/573worldanyoneý“Œßý“Œß /test-3/707worldanyoneddý“•ý“• /test-3/578worldanyone::ý“ý“ /test-3/439worldanyone„„ý“†vý“†v /test-3/579worldanyoneDDý“"ý“" /test-3/705worldanyoneIIý“”òý“”ò /test-3/576worldanyone##ý“Œÿý“Œÿ /test-3/437worldanyoneqqý“†Vý“†V /test-3/706worldanyone[[ý“•ý“• /test-3/577worldanyone00ý“ ý“ /test-3/438worldanyoneý“†gý“†g /test-3/125worldanyoneý“tyý“ty /test-3/126worldanyoneý“tý“t /test-3/127worldanyone$$ý“t¢ý“t¢ /test-3/300worldanyone ú úý“~åý“~å /test-3/128worldanyone22ý“t¯ý“t¯ /test-3/580worldanyoneNNý“+ý“+ /test-3/121worldanyoneääý“t7ý“t7 /test-3/302worldanyone  ý“ý“ /test-3/122worldanyoneîîý“tFý“tF /test-3/301worldanyone  ý“~õý“~õ /test-3/582worldanyonebbý“=ý“= /test-3/123worldanyoneøøý“tRý“tR /test-3/304worldanyone $ $ý“ý“ /test-3/581worldanyoneXXý“4ý“4 /test-3/124worldanyoneý“tgý“tg /test-3/303worldanyone  ý“ ý“ /test-3/306worldanyone 7 7ý“.ý“. /test-3/305worldanyone , ,ý“!ý“! /test-3/308worldanyone O Oý“Lý“L /test-3/307worldanyone C Cý“=ý“= /test-3/129worldanyone88ý“t¼ý“t¼ /test-3/309worldanyone Y Yý“Wý“W /test-3/444worldanyone»»ý“†ºý“†º /test-3/445worldanyoneÄÄý“†Çý“†Ç /test-3/446worldanyoneÐÐý“†Óý“†Ó /test-3/447worldanyoneÙÙý“†àý“†à /test-3/440worldanyoneŽŽý“†„ý“†„ /test-3/441worldanyone››ý“†‘ý“†‘ /test-3/442worldanyone¥¥ý“†žý“†ž /test-3/443worldanyone°°ý“†«ý“†« /test-3/583worldanyoneggý“Gý“G /test-3/584worldanyonevvý“Sý“S /test-3/585worldanyone€€ý“^ý“^ /test-3/120worldanyoneÚÚý“t)ý“t) /test-3/586worldanyoneŠŠý“iý“i /test-3/587worldanyone’’ý“tý“t /test-3/448worldanyoneããý“†êý“†ê /test-3/588worldanyoneœœý“ý“ /test-3/449worldanyoneííý“†õý“†õ /test-3/589worldanyone¦¦ý“Šý“Š /test-3/134worldanyoneiiý“tõý“tõ /test-3/135worldanyonettý“uý“u /test-3/132worldanyoneVVý“tÞý“tÞ /test-3/133worldanyone``ý“têý“tê /test-3/138worldanyone’’ý“u;ý“u; /test-3/139worldanyoneœœý“uFý“uF /test-3/136worldanyone~~ý“u"ý“u" /test-3/137worldanyoneˆˆý“u/ý“u/ /test-3/450worldanyoneûûý“‡ý“‡ /test-3/453worldanyoneý“‡)ý“‡) /test-3/454worldanyone&&ý“‡5ý“‡5 /test-3/451worldanyoneý“‡ý“‡ /test-3/452worldanyoneý“‡ý“‡ /test-3/457worldanyoneFFý“‡Xý“‡X /test-3/458worldanyoneOOý“‡bý“‡b /test-3/558worldanyonehhý“Œ:ý“Œ: /test-3/455worldanyone00ý“‡@ý“‡@ /test-3/559worldanyonessý“ŒHý“ŒH /test-3/456worldanyone::ý“‡Mý“‡M /test-3/556worldanyoneSSý“Œ%ý“Œ% /test-3/557worldanyoneYYý“Œ/ý“Œ/ /test-3/554worldanyone??ý“Œý“Œ /test-3/459worldanyoneYYý“‡ný“‡n /test-3/555worldanyoneHHý“Œý“Œ /test-3/552worldanyone++ý“‹÷ý“‹÷ /test-3/131worldanyoneMMý“tÐý“tÐ /test-3/553worldanyone55ý“Œý“Œ /test-3/130worldanyoneBBý“tÅý“tÅ /test-3/550worldanyoneý“‹åý“‹å /test-3/551worldanyone  ý“‹îý“‹î /test-3/143worldanyoneÂÂý“uŠý“uŠ /test-3/144worldanyoneÍÍý“u—ý“u— /test-3/560worldanyone}}ý“ŒRý“ŒR /test-3/145worldanyone××ý“u ý“u  /test-3/146worldanyoneÝÝý“u¯ý“u¯ /test-3/147worldanyoneââý“u½ý“u½ /test-3/148worldanyoneììý“uÇý“uÇ /test-3/149worldanyoneòòý“uÓý“uÓ /test-3/461worldanyoneooý“‡†ý“‡† /test-3/460worldanyoneddý“‡{ý“‡{ /test-3/462worldanyonezzý“‡“ý“‡“ /test-3/463worldanyone‡‡ý“‡¡ý“‡¡ /test-3/464worldanyone““ý“‡­ý“‡­ /test-3/465worldanyonežžý“‡¹ý“‡¹ /test-3/569worldanyoneÙÙý“Œ³ý“Œ³ /test-3/466worldanyone¦¦ý“‡Åý“‡Å /test-3/467worldanyone³³ý“‡Ðý“‡Ð /test-3/468worldanyone½½ý“‡Üý“‡Ü /test-3/469worldanyoneÆÆý“‡çý“‡ç /test-3/565worldanyone²²ý“Œ‹ý“Œ‹ /test-3/566worldanyone¼¼ý“Œ–ý“Œ– /test-3/567worldanyoneÅÅý“Œ ý“Œ  /test-3/568worldanyoneÏÏý“Œ¨ý“Œ¨ /test-3/561worldanyoneˆˆý“Œ]ý“Œ] /test-3/140worldanyone¢¢ý“uXý“uX /test-3/562worldanyoneý“Œhý“Œh /test-3/563worldanyoneý“Œtý“Œt /test-3/142worldanyone¸¸ý“u{ý“u{ /test-3/564worldanyone©©ý“Œý“Œ /test-3/141worldanyone°°ý“ulý“ul /test-3/687worldanyone––ý“”0ý“”0 /test-3/686worldanyoneŒŒý“’^ý“’^ /test-3/689worldanyone««ý“”Jý“”J /test-3/688worldanyoneŸŸý“”8ý“”8 /test-3/683worldanyonenný“’Dý“’D /test-3/682worldanyoneffý“’;ý“’; /test-3/685worldanyone‚‚ý“’Vý“’V /test-3/684worldanyonexxý“’Mý“’M /test-3/680worldanyoneSSý“’#ý“’# /test-3/681worldanyone[[ý“’0ý“’0 /test-3/678worldanyone@@ý“’ý“’ /test-3/677worldanyone66ý“’ý“’ /test-3/676worldanyone++ý“‘ûý“‘û /test-3/675worldanyone!!ý“‘ðý“‘ð /test-3/674worldanyoneý“‘ãý“‘ã /test-3/673worldanyoneý“‘Ìý“‘Ì /test-3/672worldanyoneþþý“‘¿ý“‘¿ /test-3/671worldanyoneööý“‘µý“‘µ /test-3/679worldanyoneJJý“’ý“’ /test-3/670worldanyoneììý“‘¬ý“‘¬ /test-3/696worldanyoneññý“”Œý“”Œ /test-3/695worldanyoneääý“”ý“” /test-3/694worldanyoneÜÜý“”yý“”y /test-3/693worldanyoneÒÒý“”qý“”q /test-3/699worldanyoneý“”°ý“”° /test-3/698worldanyoneý“”¦ý“”¦ /test-3/697worldanyoneúúý“”™ý“”™ /test-3/691worldanyone¿¿ý“”_ý“”_ /test-3/692worldanyoneÉÉý“”hý“”h /test-3/690worldanyoneµµý“”Tý“”T /test-3/646worldanyone÷÷ý“ ý“  /test-3/647worldanyoneþþý“«ý“« /test-3/648worldanyone  ý“¹ý“¹ /test-3/649worldanyoneý“Çý“Ç /test-3/642worldanyoneÎÎý“tý“t /test-3/195worldanyoneÂÂý“xØý“xØ /test-3/643worldanyoneØØý“ý“ /test-3/194worldanyoneµµý“xÅý“xÅ /test-3/644worldanyoneããý“‹ý“‹ /test-3/197worldanyoneÝÝý“yý“y /test-3/645worldanyoneììý“”ý“” /test-3/196worldanyoneÏÏý“xðý“xð /test-3/191worldanyone––ý“xœý“xœ /test-3/190worldanyone‹‹ý“x‹ý“x‹ /test-3/640worldanyone»»ý“ý“ /test-3/193worldanyone¦¦ý“x²ý“x² /test-3/641worldanyoneÄÄý“ý“ /test-3/192worldanyone  ý“x¦ý“x¦ /test-3/198worldanyoneççý“yý“y /test-3/199worldanyoneððý“y"ý“y" /test-3/505worldanyoneOOý“‰Þý“‰Þ /test-3/506worldanyoneXXý“‰êý“‰ê /test-3/639worldanyone­­ý“ýý“ý /test-3/503worldanyone77ý“‰Äý“‰Ä /test-3/504worldanyoneEEý“‰Óý“‰Ó /test-3/637worldanyone››ý“ãý“ã /test-3/509worldanyone{{ý“Š ý“Š /test-3/638worldanyone¦¦ý“ðý“ð /test-3/635worldanyone‡‡ý“Ìý“Ì /test-3/507worldanyonebbý“‰õý“‰õ /test-3/636worldanyoneý“Øý“Ø /test-3/508worldanyonenný“Šý“Š /test-3/633worldanyonerrý“«ý“« /test-3/634worldanyonezzý“ºý“º /test-3/631worldanyone^^ý“ý“ /test-3/632worldanyonehhý“ý“ /test-3/501worldanyone$$ý“‰­ý“‰­ /test-3/630worldanyoneSSý“‚ý“‚ /test-3/502worldanyone,,ý“‰¹ý“‰¹ /test-3/500worldanyoneý“‰ ý“‰  /test-3/668worldanyoneØØý“‘™ý“‘™ /test-3/669worldanyoneââý“‘¢ý“‘¢ /test-3/660worldanyoneˆˆý“‘@ý“‘@ /test-3/661worldanyoneý“‘Ký“‘K /test-3/662worldanyoneœœý“‘Xý“‘X /test-3/663worldanyone§§ý“‘fý“‘f /test-3/664worldanyone±±ý“‘pý“‘p /test-3/665worldanyoneººý“‘zý“‘z /test-3/666worldanyoneÄÄý“‘ƒý“‘ƒ /test-3/667worldanyoneÒÒý“‘ý“‘ /test-3/659worldanyone}}ý“‘4ý“‘4 /test-3/657worldanyoneiiý“‘!ý“‘! /test-3/658worldanyonessý“‘*ý“‘* /test-3/651worldanyone,,ý“ßý“ß /test-3/652worldanyone66ý“êý“ê /test-3/650worldanyone""ý“Ôý“Ô /test-3/655worldanyoneUUý“‘ý“‘ /test-3/656worldanyone__ý“‘ý“‘ /test-3/653worldanyoneAAý“öý“ö /test-3/654worldanyoneLLý“‘ý“‘ /test-3/35worldanyonehhý“m‚ý“m‚ /test-3/36worldanyonenný“m•ý“m• /test-3/33worldanyoneTTý“mdý“md /test-3/34worldanyone^^ý“mvý“mv /test-3/39worldanyoneŽŽý“mÝý“mÝ /test-3/37worldanyoneyyý“mªý“mª /test-3/38worldanyoneƒƒý“m¾ý“m¾ /test-3/43worldanyone½½ý“n0ý“n0 /test-3/42worldanyone°°ý“ný“n /test-3/41worldanyone§§ý“ný“n /test-3/40worldanyoneœœý“móý“mó /test-3/22worldanyoneããý“lˆý“lˆ /test-3/23worldanyoneííý“l›ý“l› /test-3/24worldanyoneùùý“l°ý“l° /test-3/25worldanyoneý“lÂý“l /test-3/26worldanyone  ý“lÖý“lÖ /test-3/27worldanyoneý“líý“lí /test-3/28worldanyoneý“mý“m /test-3/29worldanyone**ý“mý“m /test-3/30worldanyone22ý“m%ý“m% /test-3/32worldanyoneIIý“mSý“mS /test-3/31worldanyone??ý“m=ý“m= /test-3/19worldanyoneÂÂý“lNý“lN /test-3/17worldanyone®®ý“l)ý“l) /test-3/18worldanyone¹¹ý“l@ý“l@ /test-3/15worldanyone••ý“köý“kö /test-3/16worldanyone¢¢ý“lý“l /test-3/13worldanyone€€ý“kÎý“kÎ /test-3/14worldanyone‰‰ý“kàý“kà /test-3/11worldanyonekký“k¬ý“k¬ /test-3/12worldanyonevvý“k½ý“k½ /test-3/21worldanyoneÔÔý“lpý“lp /test-3/20worldanyoneÊÊý“l^ý“l^ /test-3/10worldanyonebbý“k˜ý“k˜ /test-3/79worldanyone&&ý“q@ý“q@ /test-3/78worldanyoneý“q3ý“q3 /test-3/77worldanyoneý“q$ý“q$ /test-3/82worldanyoneEEý“qoý“qo /test-3/83worldanyoneQQý“q~ý“q~ /test-3/80worldanyone00ý“qOý“qO /test-3/81worldanyone::ý“q]ý“q] /test-3/86worldanyoneuuý“q®ý“q® /test-3/87worldanyone||ý“qºý“qº /test-3/84worldanyonebbý“q‘ý“q‘ /test-3/85worldanyonejjý“qŸý“qŸ /test-3/67worldanyone««ý“p ý“p /test-3/66worldanyoneŸŸý“o÷ý“o÷ /test-3/69worldanyone½½ý“p.ý“p. /test-3/68worldanyone²²ý“pý“p /test-3/70worldanyoneÆÆý“pAý“pA /test-3/71worldanyoneÐÐý“pOý“pO /test-3/72worldanyoneßßý“pdý“pd /test-3/73worldanyoneééý“pvý“pv /test-3/74worldanyoneôôý“p‡ý“p‡ /test-3/75worldanyoneÿÿý“p–ý“p– /test-3/76worldanyoneý“p¦ý“p¦ /test-3/59worldanyoneSSý“otý“ot /test-3/58worldanyoneJJý“oQý“oQ /test-3/57worldanyoneAAý“o@ý“o@ /test-3/56worldanyone77ý“o/ý“o/ /test-3/55worldanyone--ý“oý“o /test-3/64worldanyone‹‹ý“oÔý“oÔ /test-3/65worldanyone——ý“oçý“oç /test-3/62worldanyoneuuý“o³ý“o³ /test-3/63worldanyone€€ý“oÃý“oà /test-3/60worldanyone]]ý“o‡ý“o‡ /test-3/61worldanyoneffý“o™ý“o™ /test-3/49worldanyoneùùý“nŸý“nŸ /test-3/48worldanyoneïïý“nŽý“nŽ /test-3/45worldanyoneÐÐý“nPý“nP /test-3/44worldanyoneÈÈý“nBý“nB /test-3/47worldanyoneååý“nwý“nw /test-3/46worldanyoneÚÚý“n`ý“n` /test-3/51worldanyone  ý“nÊý“nÊ /test-3/52worldanyoneý“nÞý“nÞ /test-3/53worldanyoneý“nòý“nò /test-3/54worldanyone$$ý“o ý“o /test-3/50worldanyoneý“n¶ý“n¶ /test-3/487worldanyoneŠŠý“ˆßý“ˆß /test-3/281worldanyone @ @ý“}òý“}ò /test-3/486worldanyone„„ý“ˆÐý“ˆÐ /test-3/280worldanyone 4 4ý“}Îý“}Î /test-3/485worldanyonexxý“ˆÄý“ˆÄ /test-3/484worldanyonemmý“ˆ¸ý“ˆ¸ /test-3/285worldanyone h hý“~'ý“~' /test-3/284worldanyone [ [ý“~ý“~ /test-3/489worldanyone¤¤ý“‰ý“‰ /test-3/283worldanyone T Tý“~ ý“~ /test-3/488worldanyone——ý“ˆóý“ˆó /test-3/282worldanyone J Jý“}ÿý“}ÿ /test-3/288worldanyone ƒ ƒý“~Mý“~M /test-3/289worldanyone  ý“~Xý“~X /test-3/286worldanyone o oý“~3ý“~3 /test-3/287worldanyone | |ý“~@ý“~@ /test-3/482worldanyoneSSý“ˆžý“ˆž /test-3/483worldanyone``ý“ˆ«ý“ˆ« /test-3/480worldanyoneBBý“ˆ‰ý“ˆ‰ /test-3/481worldanyoneLLý“ˆ•ý“ˆ• /test-3/474worldanyoneý“ˆBý“ˆB /test-3/473worldanyoneññý“ˆ4ý“ˆ4 /test-3/476worldanyoneý“ˆ]ý“ˆ] /test-3/270worldanyone Ì Ìý“}=ý“}= /test-3/475worldanyone  ý“ˆPý“ˆP /test-3/478worldanyone--ý“ˆuý“ˆu /test-3/272worldanyone Ü Üý“}Vý“}V /test-3/477worldanyone""ý“ˆiý“ˆi /test-3/271worldanyone Ö Öý“}Fý“}F /test-3/274worldanyone õ õý“}wý“}w /test-3/479worldanyone77ý“ˆ~ý“ˆ~ /test-3/273worldanyone ç çý“}fý“}f /test-3/275worldanyone þ þý“}„ý“}„ /test-3/276worldanyone  ý“}‘ý“}‘ /test-3/277worldanyone  ý“}¦ý“}¦ /test-3/278worldanyone  ý“}´ý“}´ /test-3/279worldanyone & &ý“}Àý“}À /test-3/470worldanyoneÒÒý“‡òý“‡ò /test-3/471worldanyoneÝÝý“‡ýý“‡ý /test-3/472worldanyoneêêý“ˆ ý“ˆ /test-3/109worldanyone\\ý“srý“sr /test-3/108worldanyoneTTý“scý“sc /test-3/107worldanyoneJJý“sPý“sP /test-3/106worldanyoneEEý“s@ý“s@ /test-3/105worldanyone;;ý“sý“s /test-3/104worldanyone..ý“rôý“rô /test-3/103worldanyone!!ý“rßý“rß /test-3/102worldanyoneý“rÄý“rÄ /test-3/99worldanyoneøøý“rƒý“rƒ /test-3/101worldanyone  ý“r­ý“r­ /test-3/100worldanyoneý“r™ý“r™ /test-3/595worldanyoneääý“Çý“Ç /test-3/98worldanyoneîîý“rný“rn /test-3/594worldanyoneØØý“»ý“» /test-3/97worldanyoneääý“rYý“rY /test-3/597worldanyoneõõý“Üý“Ü /test-3/391worldanyone””ý“ƒøý“ƒø /test-3/96worldanyoneØØý“rEý“rE /test-3/596worldanyoneëëý“Òý“Ò /test-3/390worldanyone‰‰ý“ƒìý“ƒì /test-3/95worldanyoneÐÐý“r4ý“r4 /test-3/599worldanyone  ý“íý“í /test-3/393worldanyone©©ý“„ý“„ /test-3/94worldanyoneÄÄý“r#ý“r# /test-3/598worldanyoneÿÿý“äý“ä /test-3/392worldanyoneŸŸý“„ý“„ /test-3/93worldanyone»»ý“rý“r /test-3/395worldanyoneÁÁý“„+ý“„+ /test-3/92worldanyone²²ý“rý“r /test-3/394worldanyone³³ý“„ý“„ /test-3/91worldanyone§§ý“qõý“qõ /test-3/90worldanyoneý“qåý“qå /test-3/294worldanyone » »ý“~Žý“~Ž /test-3/499worldanyone  ý“‰’ý“‰’ /test-3/293worldanyone · ·ý“~‚ý“~‚ /test-3/296worldanyone Ó Óý“~©ý“~© /test-3/295worldanyone Æ Æý“~›ý“~› /test-3/496worldanyoneîîý“‰mý“‰m /test-3/290worldanyone ™ ™ý“~eý“~e /test-3/495worldanyoneääý“‰`ý“‰` /test-3/498worldanyoneý“‰‡ý“‰‡ /test-3/292worldanyone ¬ ¬ý“~xý“~x /test-3/497worldanyoneööý“‰zý“‰z /test-3/291worldanyone ¢ ¢ý“~ný“~n /test-3/491worldanyoneººý“‰%ý“‰% /test-3/492worldanyoneÂÂý“‰1ý“‰1 /test-3/493worldanyoneÐÐý“‰Cý“‰C /test-3/494worldanyoneÛÛý“‰Qý“‰Q /test-3/297worldanyone Ý Ýý“~¸ý“~¸ /test-3/298worldanyone ä äý“~Èý“~È /test-3/299worldanyone ð ðý“~Ùý“~Ù /test-3/490worldanyone¯¯ý“‰ý“‰ /test-3/396worldanyoneËËý“„8ý“„8 /test-3/397worldanyoneÖÖý“„Fý“„F /test-3/398worldanyoneââý“„Tý“„T /test-3/399worldanyoneííý“„bý“„b /test-3/590worldanyone°°ý“•ý“• /test-3/591worldanyoneººý“ý“ /test-3/592worldanyoneÄÄý“¦ý“¦ /test-3/88worldanyone††ý“qÇý“qÇ /test-3/593worldanyoneÍÍý“°ý“° /test-3/89worldanyoneý“qÒý“qÒ /test-3/240worldanyone  ý“{„ý“{„ /test-3/241worldanyone ˜ ˜ý“{‘ý“{‘ /test-3/383worldanyone@@ý“ƒ•ý“ƒ• /test-3/384worldanyoneJJý“ƒ¡ý“ƒ¡ /test-3/381worldanyone''ý“ƒ{ý“ƒ{ /test-3/382worldanyone66ý“ƒŠý“ƒŠ /test-3/380worldanyoneý“ƒný“ƒn /test-3/389worldanyone}}ý“ƒÞý“ƒÞ /test-3/388worldanyoneuuý“ƒÓý“ƒÓ /test-3/387worldanyonehhý“ƒÅý“ƒÅ /test-3/386worldanyonebbý“ƒºý“ƒº /test-3/385worldanyoneVVý“ƒ­ý“ƒ­ /test-3/245worldanyone  Âý“{Ïý“{Ï /test-3/244worldanyone » »ý“{¿ý“{¿ /test-3/243worldanyone © ©ý“{­ý“{­ /test-3/242worldanyone ¡ ¡ý“{žý“{ž /test-3/249worldanyone ë ëý“|ý“| /test-3/248worldanyone â âý“|ý“| /test-3/247worldanyone Ù Ùý“{ôý“{ô /test-3/246worldanyone Î Îý“{áý“{á /test-3/230worldanyone - -ý“{ý“{ /test-3/370worldanyoneÁÁý“ƒý“ƒ /test-3/371worldanyoneÌÌý“ƒý“ƒ /test-3/372worldanyoneÔÔý“ƒý“ƒ /test-3/373worldanyoneÞÞý“ƒ%ý“ƒ% /test-3/379worldanyoneý“ƒcý“ƒc /test-3/239worldanyone † †ý“{wý“{w /test-3/378worldanyone  ý“ƒXý“ƒX /test-3/375worldanyoneïïý“ƒ<ý“ƒ< /test-3/374worldanyoneææý“ƒ1ý“ƒ1 /test-3/377worldanyoneý“ƒOý“ƒO /test-3/376worldanyoneùùý“ƒFý“ƒF /test-3/232worldanyone A Aý“{'ý“{' /test-3/231worldanyone 7 7ý“{ý“{ /test-3/234worldanyone W Wý“{@ý“{@ /test-3/233worldanyone I Iý“{2ý“{2 /test-3/236worldanyone h hý“{Xý“{X /test-3/235worldanyone a aý“{Mý“{M /test-3/238worldanyone | |ý“{mý“{m /test-3/237worldanyone r rý“{cý“{c /test-3/262worldanyone € €ý“|Óý“|Ó /test-3/263worldanyone Š Šý“|âý“|â /test-3/260worldanyone f fý“|´ý“|´ /test-3/261worldanyone s sý“|Äý“|Ä /test-3/361worldanyoneeeý“‚§ý“‚§ /test-3/362worldanyonemmý“‚±ý“‚± /test-3/360worldanyoneZZý“‚œý“‚œ /test-3/366worldanyone˜˜ý“‚ßý“‚ß /test-3/365worldanyoneý“‚Õý“‚Õ /test-3/364worldanyone……ý“‚Ëý“‚Ë /test-3/363worldanyone||ý“‚¿ý“‚¿ /test-3/369worldanyone¸¸ý“‚ýý“‚ý /test-3/368worldanyone­­ý“‚óý“‚ó /test-3/367worldanyone££ý“‚éý“‚é /test-3/269worldanyone  Âý“}1ý“}1 /test-3/268worldanyone ¸ ¸ý“}#ý“}# /test-3/267worldanyone ¯ ¯ý“}ý“} /test-3/266worldanyone ¥ ¥ý“} ý“} /test-3/265worldanyone š šý“|þý“|þ /test-3/264worldanyone ’ ’ý“|ðý“|ð /test-3/250worldanyone õ õý“|&ý“|& /test-3/251worldanyone  ý“|9ý“|9 /test-3/252worldanyone  ý“|Gý“|G /test-3/350worldanyone õ õý“‚1ý“‚1 /test-3/351worldanyone þ þý“‚:ý“‚: /test-3/353worldanyoneý“‚Pý“‚P /test-3/352worldanyoneý“‚Cý“‚C /test-3/355worldanyone&&ý“‚fý“‚f /test-3/354worldanyoneý“‚[ý“‚[ /test-3/357worldanyone<<ý“‚|ý“‚| /test-3/356worldanyone11ý“‚rý“‚r /test-3/359worldanyoneNNý“‚ý“‚ /test-3/358worldanyoneDDý“‚†ý“‚† /test-3/258worldanyone R Rý“|œý“|œ /test-3/257worldanyone E Eý“|Žý“|Ž /test-3/259worldanyone \ \ý“|§ý“|§ /test-3/254worldanyone ' 'ý“|fý“|f /test-3/253worldanyone  ý“|Xý“|X /test-3/256worldanyone < <ý“|~ý“|~ /test-3/255worldanyone 2 2ý“|sý“|s/test-4worldanyoneý“j°ý“j°Á /test-4/338worldanyone Ù Ùý“‚ý“‚ /test-4/339worldanyone ä äý“‚ ý“‚ /test-4/332worldanyone ž žý“€èý“€è /test-4/333worldanyone ¨ ¨ý“€òý“€ò /test-4/159worldanyone——ý“v­ý“v­ /test-4/330worldanyone Š Šý“€Òý“€Ò /test-4/158worldanyoneŒŒý“v ý“v  /test-4/331worldanyone ” ”ý“€Þý“€Þ /test-4/157worldanyoneý“v”ý“v” /test-4/336worldanyone Æ Æý“ý“ /test-4/156worldanyonettý“v†ý“v† /test-4/337worldanyone Ò Òý“‚ ý“‚ /test-4/155worldanyonekký“vzý“vz /test-4/334worldanyone ² ²ý“€üý“€ü /test-4/154worldanyone__ý“vlý“vl /test-4/335worldanyone ¼ ¼ý“ý“ /test-4/601worldanyoneddý“ŽMý“ŽM /test-4/531worldanyoneªªý“‹Pý“‹P /test-4/152worldanyoneGGý“vPý“vP /test-4/600worldanyone[[ý“ŽAý“ŽA /test-4/530worldanyone  ý“‹Fý“‹F /test-4/153worldanyonePPý“v\ý“v\ /test-4/150worldanyone55ý“v3ý“v3 /test-4/151worldanyone>>ý“v@ý“v@ /test-4/535worldanyoneÒÒý“‹œý“‹œ /test-4/534worldanyoneÈÈý“‹Žý“‹Ž /test-4/533worldanyone¾¾ý“‹bý“‹b /test-4/532worldanyone´´ý“‹Yý“‹Y /test-4/609worldanyone¹¹ý“ŽÐý“ŽÐ /test-4/539worldanyoneûûý“‹Êý“‹Ê /test-4/608worldanyone±±ý“ŽÅý“ŽÅ /test-4/538worldanyoneññý“‹Àý“‹À /test-4/607worldanyone©©ý“޹ý“޹ /test-4/537worldanyoneèèý“‹³ý“‹³ /test-4/606worldanyone››ý“Ž«ý“Ž« /test-4/536worldanyoneÜÜý“‹§ý“‹§ /test-4/605worldanyone’’ý“Žxý“Žx /test-4/604worldanyone……ý“Žný“Žn /test-4/603worldanyonezzý“Ždý“Žd /test-4/602worldanyoneppý“ŽZý“ŽZ /test-4/202worldanyoneffý“yèý“yè /test-4/349worldanyoneHHý“‚Šý“‚Š /test-4/203worldanyonellý“yõý“yõ /test-4/204worldanyonevvý“zý“z /test-4/205worldanyoneý“zý“z /test-4/200worldanyonePPý“yÇý“yÇ /test-4/201worldanyone\\ý“yÙý“yÙ /test-4/341worldanyone û ûý“‚8ý“‚8 /test-4/169worldanyoneý“w3ý“w3 /test-4/342worldanyoneý“‚Cý“‚C /test-4/343worldanyoneý“‚Ný“‚N /test-4/344worldanyoneý“‚Xý“‚X /test-4/166worldanyoneææý“wý“w /test-4/206worldanyoneŠŠý“z'ý“z' /test-4/345worldanyone##ý“‚aý“‚a /test-4/165worldanyoneÙÙý“wý“w /test-4/207worldanyone””ý“z3ý“z3 /test-4/346worldanyone++ý“‚jý“‚j /test-4/168worldanyoneûûý“w(ý“w( /test-4/208worldanyonežžý“zHý“zH /test-4/347worldanyone66ý“‚vý“‚v /test-4/167worldanyoneîîý“wý“w /test-4/209worldanyoneªªý“z[ý“z[ /test-4/348worldanyone>>ý“‚ý“‚ /test-4/540worldanyoneý“‹Ôý“‹Ô /test-4/409worldanyone´´ý“…Yý“…Y /test-4/161worldanyone­­ý“vÌý“vÌ /test-4/3worldanyone11ý“k ý“k /test-4/408worldanyone¢¢ý“…Aý“…A /test-4/162worldanyone¹¹ý“v×ý“v× /test-4/2worldanyone++ý“jïý“jï /test-4/542worldanyoneý“‹éý“‹é /test-4/163worldanyoneÅÅý“våý“vå /test-4/1worldanyone""ý“jÐý“jÐ /test-4/541worldanyoneý“‹Ýý“‹Ý /test-4/164worldanyoneÍÍý“vñý“vñ /test-4/0worldanyoneý“jÀý“jÀ /test-4/544worldanyone--ý“‹ÿý“‹ÿ /test-4/405worldanyone‡‡ý“…ý“… /test-4/7worldanyone[[ý“k‚ý“k‚ /test-4/543worldanyone%%ý“‹ôý“‹ô /test-4/404worldanyone||ý“…ý“… /test-4/6worldanyoneOOý“k_ý“k_ /test-4/546worldanyoneBBý“Œý“Œ /test-4/407worldanyone››ý“…5ý“…5 /test-4/5worldanyoneEEý“kDý“kD /test-4/340worldanyone ñ ñý“‚-ý“‚- /test-4/545worldanyone::ý“Œ ý“Œ /test-4/406worldanyoneý“…)ý“…) /test-4/160worldanyone¤¤ý“vÀý“vÀ /test-4/4worldanyone;;ý“k%ý“k% /test-4/548worldanyoneVVý“Œ*ý“Œ* /test-4/401worldanyone\\ý“„ñý“„ñ /test-4/547worldanyoneLLý“Œ!ý“Œ! /test-4/400worldanyoneRRý“„Öý“„Ö /test-4/403worldanyonerrý“…ý“… /test-4/9worldanyoneppý“k´ý“k´ /test-4/549worldanyone``ý“Œ4ý“Œ4 /test-4/402worldanyoneggý“„ýý“„ý /test-4/8worldanyoneffý“ký“k /test-4/211worldanyone¿¿ý“zyý“zy /test-4/212worldanyoneÉÉý“z†ý“z† /test-4/210worldanyoneµµý“zmý“zm /test-4/215worldanyoneèèý“zªý“zª /test-4/318worldanyone  ý“€>ý“€> /test-4/216worldanyoneòòý“zµý“zµ /test-4/319worldanyone  ý“€Oý“€O /test-4/213worldanyoneÒÒý“z“ý“z“ /test-4/316worldanyone ÿ ÿý“€'ý“€' /test-4/214worldanyoneÝÝý“zŸý“zŸ /test-4/317worldanyone  ý“€1ý“€1 /test-4/179worldanyonejjý“x_ý“x_ /test-4/219worldanyone  ý“zðý“zð /test-4/314worldanyone ë ëý“€ý“€ /test-4/178worldanyone__ý“xOý“xO /test-4/315worldanyone õ õý“€ý“€ /test-4/177worldanyoneTTý“xCý“xC /test-4/217worldanyoneüüý“z¿ý“z¿ /test-4/312worldanyone Ö Öý“òý“ò /test-4/176worldanyoneJJý“x5ý“x5 /test-4/218worldanyone  ý“zåý“zå /test-4/313worldanyone â âý“€ý“€ /test-4/310worldanyone À Àý“×ý“× /test-4/311worldanyone Í Íý“åý“å /test-4/513worldanyoneïïý“ŠŠý“ŠŠ /test-4/418worldanyoneý“…ßý“…ß /test-4/170worldanyone  ý“w>ý“w> /test-4/512worldanyoneèèý“Š€ý“Š€ /test-4/417worldanyoneý“…Òý“…Ò /test-4/171worldanyoneý“wNý“wN /test-4/511worldanyoneÝÝý“Šuý“Šu /test-4/416worldanyoneýýý“…Åý“…Å /test-4/510worldanyoneÓÓý“Šký“Šk /test-4/415worldanyoneííý“…¬ý“…¬ /test-4/623worldanyoneJJý“uý“u /test-4/174worldanyone88ý“xý“x /test-4/622worldanyoneBBý“iý“i /test-4/175worldanyoneAAý“x*ý“x* /test-4/621worldanyone55ý“\ý“\ /test-4/172worldanyone))ý“wcý“wc /test-4/620worldanyone((ý“Ný“N /test-4/419worldanyoneý“…ìý“…ì /test-4/173worldanyone33ý“wsý“ws /test-4/627worldanyoneuuý“¯ý“¯ /test-4/410worldanyone··ý“…iý“…i /test-4/626worldanyonekký“žý“ž /test-4/625worldanyoneaaý“’ý“’ /test-4/519worldanyone--ý“ŠËý“ŠË /test-4/624worldanyonePPý“ý“ /test-4/518worldanyone%%ý“ŠÁý“ŠÁ /test-4/517worldanyoneý“Š·ý“Š· /test-4/414worldanyoneééý“…¡ý“…¡ /test-4/516worldanyoneý“Ьý“Ь /test-4/413worldanyoneßßý“…”ý“…” /test-4/629worldanyoneŒŒý“Óý“Ó /test-4/515worldanyoneý“ŠŸý“ŠŸ /test-4/412worldanyoneÌÌý“…ý“… /test-4/628worldanyoneý“Åý“Å /test-4/514worldanyoneüüý“Š•ý“Š• /test-4/411worldanyoneÂÂý“…vý“…v /test-4/220worldanyone  ý“züý“zü /test-4/221worldanyone $ $ý“{ý“{ /test-4/222worldanyone . .ý“{ý“{ /test-4/223worldanyone 8 8ý“{ý“{ /test-4/224worldanyone B Bý“{'ý“{' /test-4/327worldanyone k ký“€²ý“€² /test-4/225worldanyone L Lý“{3ý“{3 /test-4/328worldanyone u uý“€»ý“€» /test-4/226worldanyone V Vý“{@ý“{@ /test-4/329worldanyone € €ý“€Çý“€Ç /test-4/227worldanyone b bý“{Mý“{M /test-4/188worldanyoneÐÐý“xòý“xò /test-4/228worldanyone l lý“{Zý“{Z /test-4/323worldanyone B Bý“€ƒý“€ƒ /test-4/187worldanyoneÅÅý“xâý“xâ /test-4/229worldanyone u uý“{eý“{e /test-4/324worldanyone L Lý“€ý“€ /test-4/325worldanyone Z Zý“€ý“€ /test-4/189worldanyoneØØý“xÿý“xÿ /test-4/326worldanyone c cý“€¨ý“€¨ /test-4/320worldanyone ( (ý“€]ý“€] /test-4/321worldanyone 1 1ý“€jý“€j /test-4/322worldanyone : :ý“€wý“€w /test-4/522worldanyonePPý“Šìý“Šì /test-4/427worldanyoneppý“†Vý“†V /test-4/521worldanyoneCCý“Šàý“Šà /test-4/426worldanyoneccý“†Gý“†G /test-4/180worldanyonexxý“xpý“xp /test-4/524worldanyonehhý“‹ý“‹ /test-4/429worldanyone††ý“†wý“†w /test-4/181worldanyone„„ý“x€ý“x€ /test-4/523worldanyone[[ý“Šøý“Šø /test-4/428worldanyonezzý“†gý“†g /test-4/182worldanyoneý“x”ý“x” /test-4/610worldanyoneÇÇý“Žßý“Žß /test-4/183worldanyone››ý“x¢ý“x¢ /test-4/184worldanyone§§ý“x²ý“x² /test-4/612worldanyoneÙÙý“Ž÷ý“Ž÷ /test-4/520worldanyone77ý“ŠÔý“ŠÔ /test-4/185worldanyone±±ý“xÄý“xÄ /test-4/611worldanyoneÐÐý“Žêý“Žê /test-4/186worldanyone¾¾ý“xÖý“xÖ /test-4/614worldanyoneííý“ý“ /test-4/613worldanyoneääý“ý“ /test-4/529worldanyone˜˜ý“‹;ý“‹; /test-4/616worldanyoneý“ý“ /test-4/421worldanyone//ý“†ý“† /test-4/615worldanyone÷÷ý“ý“ /test-4/420worldanyone$$ý“…øý“…ø /test-4/618worldanyoneý“3ý“3 /test-4/526worldanyone||ý“‹ý“‹ /test-4/423worldanyoneDDý“†ý“† /test-4/617worldanyone  ý“)ý“) /test-4/525worldanyonettý“‹ý“‹ /test-4/422worldanyone88ý“†ý“† /test-4/528worldanyoneŽŽý“‹2ý“‹2 /test-4/425worldanyoneYYý“†:ý“†: /test-4/619worldanyone!!ý“Bý“B /test-4/527worldanyone„„ý“‹)ý“‹) /test-4/424worldanyoneNNý“†+ý“†+ /test-4/116worldanyoneÊÊý“tý“t /test-4/117worldanyoneÔÔý“t!ý“t! /test-4/114worldanyoneµµý“sùý“sù /test-4/115worldanyoneÀÀý“t ý“t /test-4/571worldanyone//ý“ ý“ /test-4/112worldanyone  ý“sÜý“sÜ /test-4/570worldanyone''ý“ý“ /test-4/113worldanyone¬¬ý“sìý“sì /test-4/110worldanyoneý“sÁý“sÁ /test-4/111worldanyone˜˜ý“sÐý“sÐ /test-4/118worldanyoneÞÞý“t,ý“t, /test-4/119worldanyoneèèý“t;ý“t; /test-4/435worldanyoneÂÂý“†Æý“†Æ /test-4/436worldanyoneÏÏý“†Óý“†Ó /test-4/433worldanyone²²ý“†«ý“†« /test-4/434worldanyone¼¼ý“†ºý“†º /test-4/431worldanyoneššý“†‘ý“†‘ /test-4/700worldanyoneXXý“•ý“• /test-4/432worldanyone§§ý“† ý“†  /test-4/430worldanyone‘‘ý“†„ý“†„ /test-4/703worldanyonerrý“•%ý“•% /test-4/574worldanyoneSSý“1ý“1 /test-4/704worldanyone}}ý“•/ý“•/ /test-4/575worldanyone^^ý“<ý“< /test-4/701worldanyone__ý“• ý“• /test-4/572worldanyone<<ý“ý“ /test-4/702worldanyoneiiý“•ý“• /test-4/573worldanyoneFFý“#ý“# /test-4/578worldanyone||ý“]ý“] /test-4/439worldanyoneððý“†öý“†ö /test-4/579worldanyone‡‡ý“hý“h /test-4/576worldanyonehhý“Gý“G /test-4/437worldanyoneÛÛý“†àý“†à /test-4/577worldanyonerrý“Sý“S /test-4/438worldanyoneääý“†ëý“†ë /test-4/125worldanyone''ý“t£ý“t£ /test-4/126worldanyone..ý“t®ý“t® /test-4/127worldanyone;;ý“t¼ý“t¼ /test-4/300worldanyone S Sý“Oý“O /test-4/128worldanyoneDDý“tÇý“tÇ /test-4/580worldanyone””ý“uý“u /test-4/121worldanyoneýýý“tYý“tY /test-4/302worldanyone h hý“gý“g /test-4/122worldanyoneý“tjý“tj /test-4/301worldanyone ] ]ý“Zý“Z /test-4/582worldanyone¨¨ý“ý“ /test-4/123worldanyoneý“tzý“tz /test-4/304worldanyone ‚ ‚ý“ˆý“ˆ /test-4/581worldanyone  ý“ý“ /test-4/124worldanyoneý“t‘ý“t‘ /test-4/303worldanyone v vý“yý“y /test-4/306worldanyone ” ”ý“¡ý“¡ /test-4/305worldanyone Œ Œý“”ý“” /test-4/308worldanyone ª ªý“¾ý“¾ /test-4/307worldanyone ¢ ¢ý“±ý“± /test-4/129worldanyoneNNý“tÑý“tÑ /test-4/309worldanyone µ µý“Êý“Ê /test-4/444worldanyone((ý“‡5ý“‡5 /test-4/445worldanyone//ý“‡@ý“‡@ /test-4/446worldanyone;;ý“‡Mý“‡M /test-4/447worldanyoneGGý“‡Xý“‡X /test-4/440worldanyone÷÷ý“‡ý“‡ /test-4/441worldanyoneý“‡ý“‡ /test-4/442worldanyoneý“‡ý“‡ /test-4/443worldanyoneý“‡)ý“‡) /test-4/583worldanyone³³ý“™ý“™ /test-4/584worldanyone½½ý“£ý“£ /test-4/585worldanyoneÇÇý“¬ý“¬ /test-4/120worldanyoneóóý“tHý“tH /test-4/586worldanyoneÑÑý“³ý“³ /test-4/587worldanyoneÚÚý“½ý“½ /test-4/448worldanyoneQQý“‡cý“‡c /test-4/588worldanyoneââý“Æý“Æ /test-4/449worldanyone[[ý“‡ný“‡n /test-4/589worldanyoneííý“Òý“Ò /test-4/134worldanyone€€ý“u$ý“u$ /test-4/135worldanyoneŠŠý“u1ý“u1 /test-4/132worldanyonellý“tûý“tû /test-4/133worldanyonevvý“uý“u /test-4/138worldanyone¯¯ý“uký“uk /test-4/139worldanyone»»ý“u{ý“u{ /test-4/136worldanyone——ý“uFý“uF /test-4/137worldanyone¡¡ý“uUý“uU /test-4/450worldanyoneffý“‡{ý“‡{ /test-4/453worldanyoneý“‡›ý“‡› /test-4/454worldanyone‹‹ý“‡¥ý“‡¥ /test-4/451worldanyonenný“‡†ý“‡† /test-4/452worldanyonexxý“‡‘ý“‡‘ /test-4/457worldanyone©©ý“‡Æý“‡Æ /test-4/458worldanyone¯¯ý“‡Ðý“‡Ð /test-4/558worldanyone°°ý“ŒŠý“ŒŠ /test-4/455worldanyone••ý“‡¯ý“‡¯ /test-4/559worldanyoneººý“Œ“ý“Œ“ /test-4/456worldanyoneŸŸý“‡ºý“‡º /test-4/556worldanyone¡¡ý“Œvý“Œv /test-4/557worldanyone¦¦ý“Œ€ý“Œ€ /test-4/554worldanyoneý“Œbý“Œb /test-4/459worldanyoneººý“‡Ûý“‡Û /test-4/555worldanyone——ý“Œlý“Œl /test-4/552worldanyoneyyý“ŒQý“ŒQ /test-4/131worldanyonebbý“tîý“tî /test-4/553worldanyoneƒƒý“Œ[ý“Œ[ /test-4/130worldanyoneXXý“tßý“tß /test-4/550worldanyonejjý“Œ;ý“Œ; /test-4/551worldanyoneqqý“ŒGý“ŒG /test-4/143worldanyoneëëý“uÄý“uÄ /test-4/144worldanyoneööý“uÖý“uÖ /test-4/560worldanyoneÄÄý“Œ›ý“Œ› /test-4/145worldanyoneý“uãý“uã /test-4/146worldanyone  ý“uøý“uø /test-4/147worldanyoneý“v ý“v /test-4/148worldanyone""ý“vý“v /test-4/149worldanyone,,ý“v(ý“v( /test-4/461worldanyoneÎÎý“‡ïý“‡ï /test-4/460worldanyoneÅÅý“‡åý“‡å /test-4/462worldanyoneÙÙý“‡úý“‡ú /test-4/463worldanyoneããý“ˆý“ˆ /test-4/464worldanyoneîîý“ˆý“ˆ /test-4/465worldanyone÷÷ý“ˆ5ý“ˆ5 /test-4/569worldanyoneý“Œ÷ý“Œ÷ /test-4/466worldanyoneý“ˆBý“ˆB /test-4/467worldanyoneý“ˆOý“ˆO /test-4/468worldanyoneý“ˆ[ý“ˆ[ /test-4/469worldanyoneý“ˆfý“ˆf /test-4/565worldanyoneõõý“ŒÌý“ŒÌ /test-4/566worldanyoneþþý“Œ×ý“Œ× /test-4/567worldanyone  ý“Œãý“Œã /test-4/568worldanyoneý“Œîý“Œî /test-4/561worldanyoneÍÍý“Œ¥ý“Œ¥ /test-4/140worldanyoneÇÇý“uý“u /test-4/562worldanyone××ý“Œ®ý“Œ® /test-4/563worldanyoneááý“Œ·ý“Œ· /test-4/142worldanyoneááý“uµý“uµ /test-4/564worldanyoneëëý“ŒÁý“ŒÁ /test-4/141worldanyoneÑÑý“uý“u /test-4/687worldanyoneÖÖý“”tý“”t /test-4/686worldanyoneÌÌý“”jý“”j /test-4/689worldanyoneëëý“”‰ý“”‰ /test-4/688worldanyoneßßý“”~ý“”~ /test-4/683worldanyone±±ý“”Mý“”M /test-4/682worldanyone¦¦ý“”Cý“”C /test-4/685worldanyoneÃÃý“”`ý“”` /test-4/684worldanyone¹¹ý“”Vý“”V /test-4/680worldanyone””ý“”*ý“”* /test-4/681worldanyoneý“”8ý“”8 /test-4/678worldanyoneý“’Qý“’Q /test-4/677worldanyonewwý“’Gý“’G /test-4/676worldanyonemmý“’=ý“’= /test-4/675worldanyone``ý“’0ý“’0 /test-4/674worldanyoneTTý“’$ý“’$ /test-4/673worldanyoneHHý“’ý“’ /test-4/672worldanyone;;ý“’ ý“’ /test-4/671worldanyone00ý“’ý“’ /test-4/679worldanyone‹‹ý“’\ý“’\ /test-4/670worldanyone&&ý“‘ôý“‘ô /test-4/696worldanyone11ý“”Òý“”Ò /test-4/695worldanyone&&ý“”Èý“”È /test-4/694worldanyoneý“”¿ý“”¿ /test-4/693worldanyoneý“”µý“”µ /test-4/699worldanyoneOOý“”ôý“”ô /test-4/698worldanyoneDDý“”éý“”é /test-4/697worldanyone;;ý“”Þý“”Þ /test-4/691worldanyoneý“”¡ý“”¡ /test-4/692worldanyone  ý“”¬ý“”¬ /test-4/690worldanyoneôôý“”•ý“”• /test-4/646worldanyone;;ý“îý“î /test-4/647worldanyoneEEý“÷ý“÷ /test-4/648worldanyoneNNý“‘ý“‘ /test-4/649worldanyoneXXý“‘ý“‘ /test-4/642worldanyoneý“Âý“ /test-4/195worldanyoneý“yeý“ye /test-4/643worldanyoneý“Îý“Î /test-4/194worldanyone  ý“ySý“yS /test-4/644worldanyone&&ý“Úý“Ú /test-4/197worldanyone**ý“yŒý“yŒ /test-4/645worldanyone11ý“åý“å /test-4/196worldanyoneý“yuý“yu /test-4/191worldanyoneííý“yý“y /test-4/190worldanyoneââý“yý“y /test-4/640worldanyoneÿÿý“¬ý“¬ /test-4/193worldanyoneý“yDý“yD /test-4/641worldanyoneý“¸ý“¸ /test-4/192worldanyoneøøý“y/ý“y/ /test-4/198worldanyone;;ý“y£ý“y£ /test-4/199worldanyoneFFý“yµý“yµ /test-4/505worldanyone™™ý“Š/ý“Š/ /test-4/506worldanyone££ý“Š;ý“Š; /test-4/639worldanyoneööý“Ÿý“Ÿ /test-4/503worldanyone‚‚ý“Šý“Š /test-4/504worldanyoneý“Š"ý“Š" /test-4/637worldanyoneÜÜý“ý“ /test-4/509worldanyoneÈÈý“Š`ý“Š` /test-4/638worldanyoneêêý“‘ý“‘ /test-4/635worldanyoneÈÈý“ý“ /test-4/507worldanyone®®ý“ŠGý“ŠG /test-4/636worldanyoneÒÒý“vý“v /test-4/508worldanyone½½ý“ŠSý“ŠS /test-4/633worldanyone³³ý“ÿý“ÿ /test-4/634worldanyone¾¾ý“ý“ /test-4/631worldanyone  ý“ëý“ë /test-4/632worldanyoneªªý“öý“ö /test-4/501worldanyoneppý“Šý“Š /test-4/630worldanyone••ý“Þý“Þ /test-4/502worldanyonezzý“Š ý“Š /test-4/500worldanyoneeeý“‰÷ý“‰÷ /test-4/668worldanyoneý“‘Üý“‘Ü /test-4/669worldanyoneý“‘çý“‘ç /test-4/660worldanyoneËËý“‘‰ý“‘‰ /test-4/661worldanyoneÖÖý“‘“ý“‘“ /test-4/662worldanyoneßßý“‘œý“‘œ /test-4/663worldanyoneééý“‘§ý“‘§ /test-4/664worldanyoneññý“‘²ý“‘² /test-4/665worldanyoneûûý“‘½ý“‘½ /test-4/666worldanyoneý“‘Èý“‘È /test-4/667worldanyoneý“‘Ñý“‘Ñ /test-4/659worldanyoneÁÁý“‘€ý“‘€ /test-4/657worldanyone­­ý“‘lý“‘l /test-4/658worldanyone¸¸ý“‘wý“‘w /test-4/651worldanyonellý“‘&ý“‘& /test-4/652worldanyonewwý“‘3ý“‘3 /test-4/650worldanyonebbý“‘ý“‘ /test-4/655worldanyone˜˜ý“‘Wý“‘W /test-4/656worldanyone££ý“‘bý“‘b /test-4/653worldanyone††ý“‘?ý“‘? /test-4/654worldanyoneý“‘Ký“‘K /test-4/35worldanyone••ý“mâý“mâ /test-4/36worldanyoneŸŸý“möý“mö /test-4/33worldanyone{{ý“m«ý“m« /test-4/34worldanyone††ý“mÀý“mÀ /test-4/39worldanyone¹¹ý“n.ý“n. /test-4/37worldanyoneªªý“ný“n /test-4/38worldanyoneµµý“ný“n /test-4/43worldanyoneááý“nký“nk /test-4/42worldanyone××ý“n^ý“n^ /test-4/41worldanyoneÎÎý“nNý“nN /test-4/40worldanyoneÅÅý“n@ý“n@ /test-4/22worldanyoneÿÿý“lÁý“lÁ /test-4/23worldanyone  ý“l×ý“l× /test-4/24worldanyoneý“lëý“lë /test-4/25worldanyoneý“mý“m /test-4/26worldanyone))ý“mý“m /test-4/27worldanyone44ý“m'ý“m' /test-4/28worldanyone>>ý“m=ý“m= /test-4/29worldanyoneMMý“mUý“mU /test-4/30worldanyoneUUý“mgý“mg /test-4/32worldanyoneppý“m˜ý“m˜ /test-4/31worldanyoneccý“m}ý“m} /test-4/19worldanyoneÝÝý“l„ý“l„ /test-4/17worldanyoneÍÍý“l`ý“l` /test-4/18worldanyone××ý“ltý“lt /test-4/15worldanyone´´ý“l:ý“l: /test-4/16worldanyoneÀÀý“lKý“lK /test-4/13worldanyone  ý“lý“l /test-4/14worldanyone««ý“l&ý“l& /test-4/11worldanyone‹‹ý“kãý“kã /test-4/12worldanyone‘‘ý“kõý“kõ /test-4/21worldanyoneòòý“l©ý“l© /test-4/20worldanyoneççý“l–ý“l– /test-4/10worldanyone}}ý“kÊý“kÊ /test-4/79worldanyoneGGý“qqý“qq /test-4/78worldanyone>>ý“q`ý“q` /test-4/77worldanyone66ý“qSý“qS /test-4/82worldanyoneggý“qžý“qž /test-4/83worldanyonewwý“q¯ý“q¯ /test-4/80worldanyoneUUý“qý“q /test-4/81worldanyone]]ý“qý“q /test-4/86worldanyone““ý“qÚý“qÚ /test-4/87worldanyoneššý“qäý“qä /test-4/84worldanyoneý“q½ý“q½ /test-4/85worldanyone‰‰ý“qËý“qË /test-4/67worldanyoneÍÍý“pEý“pE /test-4/66worldanyoneÃÃý“p7ý“p7 /test-4/69worldanyoneÞÞý“pcý“pc /test-4/68worldanyone××ý“pTý“pT /test-4/70worldanyoneììý“pvý“pv /test-4/71worldanyoneððý“p…ý“p… /test-4/72worldanyoneýýý“p•ý“p• /test-4/73worldanyone  ý“p©ý“p© /test-4/74worldanyoneý“q%ý“q% /test-4/75worldanyone""ý“q7ý“q7 /test-4/76worldanyone++ý“qEý“qE /test-4/59worldanyoneƒƒý“oÍý“oÍ /test-4/58worldanyoneyyý“o¾ý“o¾ /test-4/57worldanyoneooý“o°ý“o° /test-4/56worldanyonehhý“ošý“oš /test-4/55worldanyoneZZý“o„ý“o„ /test-4/64worldanyone¯¯ý“pý“p /test-4/65worldanyone¹¹ý“p&ý“p& /test-4/62worldanyone››ý“oöý“oö /test-4/63worldanyone¥¥ý“pý“p /test-4/60worldanyoneý“oÚý“oÚ /test-4/61worldanyone””ý“oæý“oæ /test-4/49worldanyone!!ý“nûý“nû /test-4/48worldanyoneý“nßý“nß /test-4/45worldanyoneööý“n•ý“n• /test-4/44worldanyoneëëý“n€ý“n€ /test-4/47worldanyoneý“nÇý“nÇ /test-4/46worldanyoneÿÿý“n°ý“n° /test-4/51worldanyone55ý“o(ý“o( /test-4/52worldanyone??ý“o9ý“o9 /test-4/53worldanyoneGGý“oNý“oN /test-4/54worldanyonePPý“oqý“oq /test-4/50worldanyone++ý“oý“o /test-4/487worldanyoneààý“‰_ý“‰_ /test-4/281worldanyone  ý“~Zý“~Z /test-4/486worldanyoneÔÔý“‰Pý“‰P /test-4/280worldanyone ‚ ‚ý“~Mý“~M /test-4/485worldanyoneÊÊý“‰@ý“‰@ /test-4/484worldanyone¿¿ý“‰0ý“‰0 /test-4/285worldanyone µ µý“~ý“~ /test-4/284worldanyone ­ ­ý“~xý“~x /test-4/489worldanyoneûûý“‰{ý“‰{ /test-4/283worldanyone £ £ý“~oý“~o /test-4/488worldanyoneííý“‰lý“‰l /test-4/282worldanyone ˜ ˜ý“~dý“~d /test-4/288worldanyone Ô Ôý“~ªý“~ª /test-4/289worldanyone à àý“~¹ý“~¹ /test-4/286worldanyone ¾ ¾ý“~Žý“~Ž /test-4/287worldanyone Ç Çý“~›ý“~› /test-4/482worldanyone®®ý“‰ý“‰ /test-4/483worldanyone¸¸ý“‰#ý“‰# /test-4/480worldanyone••ý“ˆòý“ˆò /test-4/481worldanyone¡¡ý“‰ý“‰ /test-4/474worldanyoneUUý“ˆŸý“ˆŸ /test-4/473worldanyoneKKý“ˆ”ý“ˆ” /test-4/476worldanyonehhý“ˆ·ý“ˆ· /test-4/270worldanyone  ý“}µý“}µ /test-4/475worldanyone]]ý“ˆªý“ˆª /test-4/478worldanyone€€ý“ˆÏý“ˆÏ /test-4/272worldanyone 1 1ý“}Íý“}Í /test-4/477worldanyonessý“ˆÂý“ˆÂ /test-4/271worldanyone ) )ý“}Áý“}Á /test-4/274worldanyone H Hý“}ÿý“}ÿ /test-4/479worldanyoneŒŒý“ˆàý“ˆà /test-4/273worldanyone ; ;ý“}ñý“}ñ /test-4/275worldanyone P Pý“~ ý“~ /test-4/276worldanyone _ _ý“~ý“~ /test-4/277worldanyone j jý“~'ý“~' /test-4/278worldanyone q qý“~4ý“~4 /test-4/279worldanyone y yý“~?ý“~? /test-4/470worldanyone''ý“ˆqý“ˆq /test-4/471worldanyone44ý“ˆ{ý“ˆ{ /test-4/472worldanyone<<ý“ˆ‡ý“ˆ‡ /test-4/109worldanyone……ý“s®ý“s® /test-4/108worldanyonewwý“sœý“sœ /test-4/107worldanyoneggý“s‡ý“s‡ /test-4/106worldanyoneccý“sxý“sx /test-4/105worldanyoneYYý“shý“sh /test-4/104worldanyoneMMý“sSý“sS /test-4/103worldanyone>>ý“s:ý“s: /test-4/102worldanyone33ý“sý“s /test-4/99worldanyoneý“rÈý“rÈ /test-4/101worldanyone++ý“ròý“rò /test-4/100worldanyoneý“rÝý“rÝ /test-4/595worldanyone**ý“Žý“Ž /test-4/98worldanyoneý“r³ý“r³ /test-4/594worldanyoneý“Žý“Ž /test-4/97worldanyoneý“r ý“r  /test-4/597worldanyone==ý“Ž"ý“Ž" /test-4/391worldanyoneüüý“„xý“„x /test-4/96worldanyoneûûý“rý“r /test-4/596worldanyone44ý“Žý“Ž /test-4/390worldanyoneôôý“„mý“„m /test-4/95worldanyoneññý“rrý“rr /test-4/599worldanyoneSSý“Ž6ý“Ž6 /test-4/393worldanyoneý“„‹ý“„‹ /test-4/94worldanyoneããý“rYý“rY /test-4/598worldanyoneIIý“Ž,ý“Ž, /test-4/392worldanyoneý“„ƒý“„ƒ /test-4/93worldanyoneÖÖý“rBý“rB /test-4/395worldanyone%%ý“„¡ý“„¡ /test-4/92worldanyoneÑÑý“r4ý“r4 /test-4/394worldanyoneý“„•ý“„• /test-4/91worldanyoneÇÇý“r$ý“r$ /test-4/90worldanyoneººý“rý“r /test-4/294worldanyone  ý“ý“ /test-4/499worldanyone[[ý“‰ëý“‰ë /test-4/293worldanyone  ý“~õý“~õ /test-4/296worldanyone . .ý“"ý“" /test-4/295worldanyone  ý“ý“ /test-4/496worldanyone<<ý“‰Çý“‰Ç /test-4/290worldanyone ç çý“~Éý“~É /test-4/495worldanyone33ý“‰¾ý“‰¾ /test-4/498worldanyonePPý“‰ßý“‰ß /test-4/292worldanyone þ þý“~èý“~è /test-4/497worldanyoneCCý“‰Óý“‰Ó /test-4/291worldanyone ô ôý“~Ùý“~Ù /test-4/491worldanyone  ý“‰‘ý“‰‘ /test-4/492worldanyoneý“‰žý“‰ž /test-4/493worldanyoneý“‰ªý“‰ª /test-4/494worldanyone((ý“‰µý“‰µ /test-4/297worldanyone 4 4ý“-ý“- /test-4/298worldanyone > >ý“9ý“9 /test-4/299worldanyone H Hý“Dý“D /test-4/490worldanyoneý“‰†ý“‰† /test-4/396worldanyone**ý“„­ý“„­ /test-4/397worldanyone44ý“„¸ý“„¸ /test-4/398worldanyone>>ý“„Äý“„Ä /test-4/399worldanyoneHHý“„Îý“„Î /test-4/590worldanyoneööý“Üý“Ü /test-4/591worldanyoneý“æý“æ /test-4/592worldanyone  ý“ïý“ï /test-4/88worldanyone££ý“qñý“qñ /test-4/593worldanyoneý“úý“ú /test-4/89worldanyone®®ý“rý“r /test-4/240worldanyone é éý“|ý“| /test-4/241worldanyone ó óý“|#ý“|# /test-4/383worldanyone««ý“„ý“„ /test-4/384worldanyone¶¶ý“„ý“„ /test-4/381worldanyone••ý“ƒøý“ƒø /test-4/382worldanyone¡¡ý“„ý“„ /test-4/380worldanyoneŽŽý“ƒíý“ƒí /test-4/389worldanyoneççý“„`ý“„` /test-4/388worldanyoneààý“„Sý“„S /test-4/387worldanyoneÕÕý“„Fý“„F /test-4/386worldanyoneÈÈý“„6ý“„6 /test-4/385worldanyone½½ý“„)ý“„) /test-4/245worldanyone  ý“|Wý“|W /test-4/244worldanyone  ý“|Hý“|H /test-4/243worldanyone  ý“|;ý“|; /test-4/242worldanyone ý ýý“|0ý“|0 /test-4/249worldanyone D Dý“|ý“| /test-4/248worldanyone 9 9ý“||ý“|| /test-4/247worldanyone . .ý“|ný“|n /test-4/246worldanyone $ $ý“|dý“|d /test-4/230worldanyone ~ ~ý“{tý“{t /test-4/370worldanyone  ý“ƒpý“ƒp /test-4/371worldanyone,,ý“ƒ~ý“ƒ~ /test-4/372worldanyone33ý“ƒˆý“ƒˆ /test-4/373worldanyone==ý“ƒ”ý“ƒ” /test-4/379worldanyone„„ý“ƒàý“ƒà /test-4/239worldanyone ß ßý“|ý“| /test-4/378worldanyonexxý“ƒÓý“ƒÓ /test-4/375worldanyoneRRý“ƒ¬ý“ƒ¬ /test-4/374worldanyoneGGý“ƒŸý“ƒŸ /test-4/377worldanyonekký“ƒÆý“ƒÆ /test-4/376worldanyone\\ý“ƒ¹ý“ƒ¹ /test-4/232worldanyone • •ý“{‘ý“{‘ /test-4/231worldanyone Š Šý“{ƒý“{ƒ /test-4/234worldanyone ¬ ¬ý“{®ý“{® /test-4/233worldanyone Ÿ Ÿý“{žý“{ž /test-4/236worldanyone Ä Äý“{Ðý“{Ð /test-4/235worldanyone µ µý“{¾ý“{¾ /test-4/238worldanyone Ö Öý“{ïý“{ï /test-4/237worldanyone Ë Ëý“{áý“{á /test-4/262worldanyone Í Íý“}?ý“}? /test-4/263worldanyone × ×ý“}Ký“}K /test-4/260worldanyone » »ý“}$ý“}$ /test-4/261worldanyone à Ãý“}2ý“}2 /test-4/361worldanyoneÂÂý“ƒý“ƒ /test-4/362worldanyoneÊÊý“ƒý“ƒ /test-4/360worldanyone¶¶ý“‚ýý“‚ý /test-4/366worldanyoneööý“ƒ?ý“ƒ? /test-4/365worldanyoneììý“ƒ2ý“ƒ2 /test-4/364worldanyoneààý“ƒ&ý“ƒ& /test-4/363worldanyoneÕÕý“ƒý“ƒ /test-4/369worldanyoneý“ƒdý“ƒd /test-4/368worldanyone  ý“ƒWý“ƒW /test-4/367worldanyoneý“ƒKý“ƒK /test-4/269worldanyone  ý“}¨ý“}¨ /test-4/268worldanyone  ý“}”ý“}” /test-4/267worldanyone  ý“}…ý“}… /test-4/266worldanyone ô ôý“}vý“}v /test-4/265worldanyone ì ìý“}hý“}h /test-4/264worldanyone á áý“}Xý“}X /test-4/250worldanyone P Pý“|›ý“|› /test-4/251worldanyone Z Zý“|¦ý“|¦ /test-4/252worldanyone c cý“|³ý“|³ /test-4/350worldanyoneRRý“‚“ý“‚“ /test-4/351worldanyone\\ý“‚œý“‚œ /test-4/353worldanyoneooý“‚±ý“‚± /test-4/352worldanyoneffý“‚§ý“‚§ /test-4/355worldanyone††ý“‚Ëý“‚Ë /test-4/354worldanyonezzý“‚¿ý“‚¿ /test-4/357worldanyoneššý“‚àý“‚à /test-4/356worldanyoneý“‚Õý“‚Õ /test-4/359worldanyone¬¬ý“‚óý“‚ó /test-4/358worldanyone¢¢ý“‚éý“‚é /test-4/258worldanyone ¤ ¤ý“} ý“} /test-4/257worldanyone › ›ý“|þý“|þ /test-4/259worldanyone ® ®ý“}ý“} /test-4/254worldanyone | |ý“|Ñý“|Ñ /test-4/253worldanyone o oý“|Âý“| /test-4/256worldanyone  ý“|ïý“|ï /test-4/255worldanyone … …ý“|àý“|à/test-1worldanyoneý“jxý“jx /test-1/338worldanyone 1 1ý“(ý“( /test-1/339worldanyone ; ;ý“3ý“3 /test-1/332worldanyone  ý“~éý“~é /test-1/333worldanyone  ý“~õý“~õ /test-1/330worldanyone í íý“~Òý“~Ò /test-1/331worldanyone ÷ ÷ý“~Ýý“~Ý /test-1/336worldanyone # #ý“ý“ /test-1/337worldanyone ' 'ý“ý“ /test-1/334worldanyone  ý“ý“ /test-1/335worldanyone  ý“ ý“ /test-1/531worldanyone55ý“ˆ}ý“ˆ} /test-1/748worldanyone44ý“’ý“’ /test-1/530worldanyone++ý“ˆtý“ˆt /test-1/747worldanyone''ý“‘õý“‘õ /test-1/746worldanyoneý“‘éý“‘é /test-1/745worldanyoneý“‘ßý“‘ß /test-1/535worldanyoneZZý“ˆ¨ý“ˆ¨ /test-1/534worldanyonePPý“ˆý“ˆ /test-1/533worldanyoneFFý“ˆ“ý“ˆ“ /test-1/532worldanyone??ý“ˆˆý“ˆˆ /test-1/749worldanyone>>ý“’ ý“’ /test-1/539worldanyone||ý“ˆÎý“ˆÎ /test-1/740worldanyoneããý“‘¦ý“‘¦ /test-1/538worldanyonewwý“ˆÃý“ˆÃ /test-1/537worldanyonenný“ˆºý“ˆº /test-1/536worldanyoneddý“ˆ±ý“ˆ± /test-1/744worldanyone  ý“‘Íý“‘Í /test-1/743worldanyoneý“‘Âý“‘ /test-1/742worldanyone÷÷ý“‘¹ý“‘¹ /test-1/741worldanyoneííý“‘°ý“‘° /test-1/349worldanyone ™ ™ý“£ý“£ /test-1/341worldanyone M Mý“Ký“K /test-1/342worldanyone W Wý“Tý“T /test-1/343worldanyone a aý“]ý“] /test-1/344worldanyone k ký“hý“h /test-1/345worldanyone p pý“wý“w /test-1/346worldanyone z zý“ƒý“ƒ /test-1/347worldanyone „ „ý“Žý“Ž /test-1/348worldanyone Ž Žý“—ý“— /test-1/3worldanyoneý“j©ý“j© /test-1/540worldanyone††ý“ˆØý“ˆØ /test-1/735worldanyoneµµý“‘uý“‘u /test-1/2worldanyoneý“jœý“jœ /test-1/734worldanyone««ý“‘ký“‘k /test-1/1worldanyone  ý“j‘ý“j‘ /test-1/542worldanyone‘‘ý“ˆîý“ˆî /test-1/737worldanyoneÉÉý“‘…ý“‘… /test-1/0worldanyoneý“j…ý“j… /test-1/541worldanyoneŽŽý“ˆáý“ˆá /test-1/736worldanyone¿¿ý“‘}ý“‘} /test-1/7worldanyone''ý“jèý“jè /test-1/544worldanyone¥¥ý“‰ý“‰ /test-1/739worldanyoneÚÚý“‘›ý“‘› /test-1/6worldanyone%%ý“jÛý“jÛ /test-1/543worldanyone››ý“ˆûý“ˆû /test-1/738worldanyoneÎÎý“‘Žý“‘Ž /test-1/340worldanyone E Eý“>ý“> /test-1/5worldanyoneý“jÇý“jÇ /test-1/546worldanyoneµµý“‰"ý“‰" /test-1/4worldanyoneý“j¹ý“j¹ /test-1/545worldanyone««ý“‰ý“‰ /test-1/548worldanyoneÌÌý“‰Aý“‰A /test-1/547worldanyoneÃÃý“‰2ý“‰2 /test-1/9worldanyone22ý“k ý“k /test-1/8worldanyone--ý“j÷ý“j÷ /test-1/549worldanyoneÚÚý“‰Qý“‰Q /test-1/731worldanyoneý“‘Iý“‘I /test-1/730worldanyoneý“‘>ý“‘> /test-1/733worldanyone¢¢ý“‘bý“‘b /test-1/732worldanyone——ý“‘Wý“‘W /test-1/318worldanyone w wý“~>ý“~> /test-1/319worldanyone  ý“~Ký“~K /test-1/316worldanyone c cý“~&ý“~& /test-1/317worldanyone m mý“~3ý“~3 /test-1/314worldanyone V Vý“~ ý“~ /test-1/315worldanyone ] ]ý“~ý“~ /test-1/312worldanyone B Bý“}ùý“}ù /test-1/313worldanyone L Lý“~ý“~ /test-1/310worldanyone 5 5ý“}Ðý“}Ð /test-1/311worldanyone 8 8ý“}ïý“}ï /test-1/513worldanyoneý“‡¬ý“‡¬ /test-1/512worldanyone‰‰ý“‡¡ý“‡¡ /test-1/729worldanyone||ý“‘3ý“‘3 /test-1/511worldanyoneyyý“‡“ý“‡“ /test-1/728worldanyonerrý“‘*ý“‘* /test-1/510worldanyonettý“‡‰ý“‡‰ /test-1/727worldanyonehhý“‘!ý“‘! /test-1/726worldanyone^^ý“‘ý“‘ /test-1/725worldanyoneTTý“‘ ý“‘ /test-1/724worldanyoneKKý“‘ý“‘ /test-1/723worldanyoneDDý“÷ý“÷ /test-1/722worldanyone::ý“îý“î /test-1/721worldanyone00ý“äý“ä /test-1/519worldanyoneÉÉý“‡êý“‡ê /test-1/720worldanyone%%ý“Ùý“Ù /test-1/518worldanyone¿¿ý“‡àý“‡à /test-1/517worldanyoneµµý“‡Ôý“‡Ô /test-1/516worldanyone¬¬ý“‡Êý“‡Ê /test-1/515worldanyone¡¡ý“‡Áý“‡Á /test-1/514worldanyone——ý“‡µý“‡µ /test-1/327worldanyone Õ Õý“~ªý“~ª /test-1/328worldanyone Û Ûý“~¸ý“~¸ /test-1/329worldanyone ã ãý“~Æý“~Æ /test-1/323worldanyone ª ªý“~tý“~t /test-1/324worldanyone ² ²ý“~€ý“~€ /test-1/325worldanyone À Àý“~Žý“~Ž /test-1/326worldanyone Å Åý“~›ý“~› /test-1/709worldanyone¸¸ý“ ý“ /test-1/320worldanyone ‹ ‹ý“~Uý“~U /test-1/321worldanyone • •ý“~^ý“~^ /test-1/322worldanyone Ÿ Ÿý“~hý“~h /test-1/522worldanyoneååý“ˆ ý“ˆ /test-1/717worldanyone  ý“¹ý“¹ /test-1/521worldanyoneÛÛý“‡ýý“‡ý /test-1/716worldanyoneý“®ý“® /test-1/524worldanyoneõõý“ˆ5ý“ˆ5 /test-1/719worldanyoneý“Îý“Î /test-1/523worldanyoneïïý“ˆý“ˆ /test-1/718worldanyoneý“Äý“Ä /test-1/713worldanyoneææý“ý“ /test-1/712worldanyoneÞÞý“‚ý“‚ /test-1/520worldanyoneÓÓý“‡óý“‡ó /test-1/715worldanyoneøøý“¦ý“¦ /test-1/714worldanyoneîîý“œý“œ /test-1/529worldanyone!!ý“ˆiý“ˆi /test-1/711worldanyoneÔÔý“wý“w /test-1/710worldanyoneÉÉý“ý“ /test-1/526worldanyoneý“ˆNý“ˆN /test-1/525worldanyoneüüý“ˆAý“ˆA /test-1/528worldanyoneý“ˆ_ý“ˆ_ /test-1/527worldanyoneý“ˆVý“ˆV /test-1/571worldanyone¡¡ý“Š6ý“Š6 /test-1/570worldanyone——ý“Š-ý“Š- /test-1/700worldanyoneZZý“Žý“Ž /test-1/574worldanyone··ý“ŠQý“ŠQ /test-1/703worldanyonexxý“ºý“º /test-1/575worldanyoneÁÁý“Š[ý“Š[ /test-1/704worldanyone„„ý“Éý“É /test-1/572worldanyone««ý“Š?ý“Š? /test-1/701worldanyoneddý“›ý“› /test-1/573worldanyone´´ý“ŠHý“ŠH /test-1/702worldanyoneppý“«ý“« /test-1/578worldanyoneßßý“Šzý“Šz /test-1/707worldanyone¤¤ý“ïý“ï /test-1/579worldanyoneééý“Š„ý“Š„ /test-1/708worldanyone°°ý“þý“þ /test-1/576worldanyoneËËý“Šdý“Šd /test-1/705worldanyoneŽŽý“Öý“Ö /test-1/577worldanyoneÕÕý“Šmý“Šm /test-1/706worldanyone——ý“âý“â /test-1/300worldanyone Ô Ôý“}Dý“}D /test-1/302worldanyone è èý“}fý“}f /test-1/580worldanyoneóóý“ŠŽý“ŠŽ /test-1/301worldanyone Þ Þý“}Vý“}V /test-1/304worldanyone ú úý“}‚ý“}‚ /test-1/582worldanyoneý“Š ý“Š  /test-1/303worldanyone ñ ñý“}tý“}t /test-1/581worldanyoneýýý“Š–ý“Š– /test-1/306worldanyone  ý“}£ý“}£ /test-1/305worldanyone  ý“}ý“} /test-1/308worldanyone ! !ý“}½ý“}½ /test-1/307worldanyone  ý“}²ý“}² /test-1/309worldanyone + +ý“}Çý“}Ç /test-1/583worldanyone  ý“Šªý“Šª /test-1/584worldanyoneý“жý“ж /test-1/585worldanyone!!ý“ŠÀý“ŠÀ /test-1/586worldanyone++ý“ŠÈý“ŠÈ /test-1/587worldanyone55ý“ŠÐý“ŠÐ /test-1/588worldanyone??ý“ŠÜý“ŠÜ /test-1/589worldanyoneIIý“Šåý“Šå /test-1/558worldanyone))ý“‰¶ý“‰¶ /test-1/559worldanyone11ý“‰¾ý“‰¾ /test-1/556worldanyoneý“‰Ÿý“‰Ÿ /test-1/557worldanyoneý“‰«ý“‰« /test-1/554worldanyoneý“‰Šý“‰Š /test-1/555worldanyoneý“‰”ý“‰” /test-1/552worldanyoneóóý“‰xý“‰x /test-1/553worldanyoneýýý“‰ý“‰ /test-1/550worldanyoneßßý“‰_ý“‰_ /test-1/551worldanyoneééý“‰lý“‰l /test-1/560worldanyone;;ý“‰Çý“‰Ç /test-1/569worldanyone’’ý“Š$ý“Š$ /test-1/565worldanyoneiiý“‰ýý“‰ý /test-1/566worldanyonettý“Šý“Š /test-1/567worldanyone||ý“Šý“Š /test-1/568worldanyone‡‡ý“Šý“Š /test-1/561worldanyone@@ý“‰Òý“‰Ò /test-1/562worldanyoneKKý“‰Ýý“‰Ý /test-1/563worldanyoneUUý“‰êý“‰ê /test-1/564worldanyone__ý“‰ôý“‰ô /test-1/781worldanyonessý“•&ý“•& /test-1/782worldanyone||ý“•/ý“•/ /test-1/783worldanyoneˆˆý“•;ý“•; /test-1/784worldanyone‘‘ý“•Fý“•F /test-1/785worldanyone——ý“•Sý“•S /test-1/780worldanyoneffý“•ý“• /test-1/772worldanyoneý“”¸ý“”¸ /test-1/505worldanyoneBBý“‡Vý“‡V /test-1/773worldanyoneý“”Äý“”Ä /test-1/506worldanyoneLLý“‡aý“‡a /test-1/770worldanyoneý“”¥ý“”¥ /test-1/503worldanyone22ý“‡Aý“‡A /test-1/771worldanyone  ý“”¯ý“”¯ /test-1/504worldanyone88ý“‡Lý“‡L /test-1/776worldanyone>>ý“”æý“”æ /test-1/509worldanyonejjý“‡ý“‡ /test-1/777worldanyoneHHý“”òý“”ò /test-1/774worldanyone**ý“”Îý“”Î /test-1/507worldanyoneVVý“‡jý“‡j /test-1/775worldanyone55ý“”Ûý“”Û /test-1/508worldanyone``ý“‡vý“‡v /test-1/778worldanyoneRRý“”þý“”þ /test-1/779worldanyone\\ý“• ý“• /test-1/501worldanyoneý“‡/ý“‡/ /test-1/502worldanyone))ý“‡7ý“‡7 /test-1/500worldanyoneý“‡&ý“‡& /test-1/763worldanyoneÀÀý“”`ý“”` /test-1/764worldanyoneÊÊý“”jý“”j /test-1/765worldanyoneÔÔý“”tý“”t /test-1/766worldanyoneÞÞý“”}ý“”} /test-1/760worldanyone§§ý“”Cý“”C /test-1/761worldanyone¬¬ý“”Lý“”L /test-1/762worldanyone¶¶ý“”Vý“”V /test-1/767worldanyoneèèý“”…ý“”… /test-1/768worldanyoneòòý“”ý“” /test-1/769worldanyone÷÷ý“”˜ý“”˜ /test-1/754worldanyonekký“’<ý“’< /test-1/755worldanyonevvý“’Gý“’G /test-1/752worldanyoneXXý“’*ý“’* /test-1/753worldanyoneccý“’3ý“’3 /test-1/750worldanyoneDDý“’ý“’ /test-1/751worldanyoneOOý“’!ý“’! /test-1/758worldanyoneŽŽý“”)ý“”) /test-1/759worldanyone˜˜ý“”6ý“”6 /test-1/756worldanyoneý“’Qý“’Q /test-1/757worldanyoneˆˆý“’[ý“’[ /test-1/595worldanyoneý“‹&ý“‹& /test-1/594worldanyonewwý“‹ý“‹ /test-1/391worldanyone$$ý“‚eý“‚e /test-1/597worldanyone––ý“‹;ý“‹; /test-1/390worldanyoneý“‚[ý“‚[ /test-1/596worldanyone‹‹ý“‹/ý“‹/ /test-1/393worldanyone88ý“‚wý“‚w /test-1/599worldanyone©©ý“‹Oý“‹O /test-1/392worldanyone..ý“‚ný“‚n /test-1/598worldanyoneŸŸý“‹Fý“‹F /test-1/395worldanyoneIIý“‚Œý“‚Œ /test-1/394worldanyoneBBý“‚ý“‚ /test-1/396worldanyoneTTý“‚—ý“‚— /test-1/397worldanyone^^ý“‚¡ý“‚¡ /test-1/398worldanyoneggý“‚ªý“‚ª /test-1/399worldanyonerrý“‚´ý“‚´ /test-1/590worldanyoneSSý“Šîý“Šî /test-1/591worldanyone^^ý“Šùý“Šù /test-1/592worldanyoneddý“‹ý“‹ /test-1/593worldanyonemmý“‹ý“‹ /test-1/383worldanyone Ü Üý“‚ý“‚ /test-1/384worldanyone è èý“‚"ý“‚" /test-1/381worldanyone Å Åý“ý“ /test-1/382worldanyone Ï Ïý“‚ý“‚ /test-1/380worldanyone º ºý“ý“ /test-1/389worldanyoneý“‚Qý“‚Q /test-1/388worldanyone  ý“‚Hý“‚H /test-1/387worldanyoneý“‚@ý“‚@ /test-1/386worldanyone ÷ ÷ý“‚7ý“‚7 /test-1/385worldanyone í íý“‚,ý“‚, /test-1/370worldanyone V Vý“€œý“€œ /test-1/371worldanyone ` `ý“€§ý“€§ /test-1/372worldanyone j jý“€²ý“€² /test-1/373worldanyone t tý“€»ý“€» /test-1/379worldanyone ± ±ý“€úý“€ú /test-1/378worldanyone § §ý“€ñý“€ñ /test-1/375worldanyone ˆ ˆý“€Ñý“€Ñ /test-1/374worldanyone ~ ~ý“€Æý“€Æ /test-1/377worldanyone  ý“€çý“€ç /test-1/376worldanyone ’ ’ý“€Ýý“€Ý /test-1/361worldanyone  ý“€*ý“€* /test-1/362worldanyone  ý“€8ý“€8 /test-1/360worldanyone ÷ ÷ý“€ý“€ /test-1/366worldanyone / /ý“€hý“€h /test-1/365worldanyone & &ý“€[ý“€[ /test-1/364worldanyone  ý“€Mý“€M /test-1/363worldanyone  ý“€Aý“€A /test-1/369worldanyone M Mý“€‘ý“€‘ /test-1/368worldanyone F Fý“€†ý“€† /test-1/367worldanyone ; ;ý“€xý“€x /test-1/350worldanyone œ œý“®ý“® /test-1/351worldanyone ¦ ¦ý“¹ý“¹ /test-1/353worldanyone ¹ ¹ý“Ëý“Ë /test-1/352worldanyone ° °ý“Âý“ /test-1/355worldanyone Ê Êý“äý“ä /test-1/354worldanyone à Ãý“×ý“× /test-1/357worldanyone Ý Ýý“ùý“ù /test-1/356worldanyone Ó Óý“ïý“ï /test-1/359worldanyone ï ïý“€ý“€ /test-1/358worldanyone å åý“€ý“€ /test-1/159worldanyone¤¤ý“uYý“uY /test-1/158worldanyonežžý“uJý“uJ /test-1/157worldanyone””ý“u@ý“u@ /test-1/156worldanyoneý“u3ý“u3 /test-1/155worldanyoneƒƒý“u'ý“u' /test-1/154worldanyonexxý“uý“u /test-1/152worldanyoneccý“tïý“tï /test-1/601worldanyone½½ý“‹aý“‹a /test-1/153worldanyonenný“tþý“tþ /test-1/600worldanyone³³ý“‹Xý“‹X /test-1/150worldanyoneRRý“tØý“tØ /test-1/151worldanyoneZZý“tåý“tå /test-1/609worldanyone  ý“‹Öý“‹Ö /test-1/608worldanyoneÿÿý“‹Íý“‹Í /test-1/607worldanyoneõõý“‹Äý“‹Ä /test-1/606worldanyoneëëý“‹¼ý“‹¼ /test-1/605worldanyoneááý“‹²ý“‹² /test-1/604worldanyone××ý“‹¦ý“‹¦ /test-1/603worldanyoneÍÍý“‹›ý“‹› /test-1/602worldanyoneÇÇý“‹Žý“‹Ž /test-1/202worldanyone66ý“xý“x /test-1/203worldanyoneDDý“x+ý“x+ /test-1/204worldanyonePPý“x:ý“x: /test-1/205worldanyoneYYý“xGý“xG /test-1/200worldanyone""ý“waý“wa /test-1/201worldanyone--ý“wpý“wp /test-1/169worldanyoneý“uèý“uè /test-1/206worldanyonebbý“xRý“xR /test-1/166worldanyoneääý“u¾ý“u¾ /test-1/207worldanyoneiiý“x_ý“x_ /test-1/165worldanyoneÙÙý“u®ý“u® /test-1/208worldanyonessý“xký“xk /test-1/168worldanyoneøøý“uØý“uØ /test-1/209worldanyone}}ý“xtý“xt /test-1/167worldanyoneððý“uÍý“uÍ /test-1/161worldanyone´´ý“upý“up /test-1/409worldanyoneÏÏý“ƒý“ƒ /test-1/162worldanyone¾¾ý“u~ý“u~ /test-1/408worldanyoneÇÇý“ƒý“ƒ /test-1/163worldanyoneÅÅý“uŒý“uŒ /test-1/164worldanyoneÎÎý“uœý“uœ /test-1/405worldanyone§§ý“‚îý“‚î /test-1/404worldanyonežžý“‚ãý“‚ã /test-1/407worldanyone¼¼ý“ƒý“ƒ /test-1/160worldanyoneªªý“ufý“uf /test-1/406worldanyone°°ý“‚øý“‚ø /test-1/401worldanyoneý“‚Èý“‚È /test-1/400worldanyonewwý“‚¾ý“‚¾ /test-1/403worldanyone••ý“‚Ùý“‚Ù /test-1/402worldanyone‹‹ý“‚Ðý“‚Ð /test-1/211worldanyoneý“xý“x /test-1/212worldanyone••ý“x™ý“x™ /test-1/210worldanyone‡‡ý“xƒý“xƒ /test-1/215worldanyone¯¯ý“xÂý“x /test-1/216worldanyoneººý“xÒý“xÒ /test-1/213worldanyonežžý“x¤ý“x¤ /test-1/214worldanyone¥¥ý“x²ý“x² /test-1/219worldanyoneÓÓý“xøý“xø /test-1/179worldanyoneddý“vuý“vu /test-1/178worldanyoneZZý“vjý“vj /test-1/217worldanyoneÄÄý“xÞý“xÞ /test-1/177worldanyoneRRý“v]ý“v] /test-1/218worldanyoneËËý“xìý“xì /test-1/176worldanyoneHHý“vPý“vP /test-1/170worldanyoneý“uôý“uô /test-1/418worldanyoneý“ƒoý“ƒo /test-1/171worldanyoneý“v ý“v /test-1/417worldanyoneý“ƒdý“ƒd /test-1/416worldanyoneý“ƒZý“ƒZ /test-1/415worldanyoneý“ƒRý“ƒR /test-1/174worldanyone66ý“v4ý“v4 /test-1/623worldanyoneŠŠý“Œ_ý“Œ_ /test-1/175worldanyone::ý“v>ý“v> /test-1/622worldanyone€€ý“ŒWý“ŒW /test-1/172worldanyone  ý“vý“v /test-1/621worldanyonevvý“ŒNý“ŒN /test-1/173worldanyone--ý“v)ý“v) /test-1/419worldanyone))ý“ƒ|ý“ƒ| /test-1/620worldanyonellý“ŒEý“ŒE /test-1/410worldanyoneÙÙý“ƒ#ý“ƒ# /test-1/627worldanyone­­ý“Œ†ý“Œ† /test-1/626worldanyone££ý“Œ}ý“Œ} /test-1/625worldanyone™™ý“Œtý“Œt /test-1/624worldanyone””ý“Œiý“Œi /test-1/414worldanyoneúúý“ƒIý“ƒI /test-1/413worldanyoneôôý“ƒ>ý“ƒ> /test-1/412worldanyoneííý“ƒ5ý“ƒ5 /test-1/629worldanyoneÁÁý“Œ™ý“Œ™ /test-1/411worldanyoneããý“ƒ,ý“ƒ, /test-1/628worldanyone··ý“Œý“Œ /test-1/220worldanyoneÛÛý“yý“y /test-1/221worldanyoneääý“yý“y /test-1/222worldanyoneîîý“yý“y /test-1/223worldanyoneôôý“y-ý“y- /test-1/224worldanyoneþþý“y9ý“y9 /test-1/225worldanyoneý“yKý“yK /test-1/226worldanyoneý“y`ý“y` /test-1/227worldanyoneý“yný“yn /test-1/228worldanyone""ý“y{ý“y{ /test-1/188worldanyone¼¼ý“vÙý“vÙ /test-1/229worldanyone++ý“yý“y /test-1/187worldanyone³³ý“vÏý“vÏ /test-1/189worldanyoneÂÂý“väý“vä /test-1/427worldanyonerrý“ƒÓý“ƒÓ /test-1/180worldanyonenný“v}ý“v} /test-1/426worldanyoneggý“ƒÅý“ƒÅ /test-1/181worldanyonexxý“vˆý“vˆ /test-1/429worldanyone††ý“ƒëý“ƒë /test-1/182worldanyone~~ý“v’ý“v’ /test-1/428worldanyone||ý“ƒÞý“ƒÞ /test-1/183worldanyone‰‰ý“vŸý“vŸ /test-1/610worldanyoneý“‹ßý“‹ß /test-1/184worldanyone““ý“v¬ý“v¬ /test-1/185worldanyoneý“v¹ý“v¹ /test-1/612worldanyone""ý“‹óý“‹ó /test-1/186worldanyone¨¨ý“vÅý“vÅ /test-1/611worldanyoneý“‹êý“‹ê /test-1/614worldanyone66ý“Œ ý“Œ /test-1/613worldanyone,,ý“‹ÿý“‹ÿ /test-1/421worldanyone<<ý“ƒ”ý“ƒ” /test-1/616worldanyoneJJý“Œý“Œ /test-1/420worldanyone22ý“ƒˆý“ƒˆ /test-1/615worldanyone@@ý“Œý“Œ /test-1/423worldanyoneOOý“ƒ¨ý“ƒ¨ /test-1/618worldanyoneXXý“Œ/ý“Œ/ /test-1/422worldanyoneEEý“ƒžý“ƒž /test-1/617worldanyoneTTý“Œ&ý“Œ& /test-1/425worldanyoneddý“ƒ»ý“ƒ» /test-1/424worldanyoneZZý“ƒ±ý“ƒ± /test-1/619worldanyoneccý“Œ:ý“Œ: /test-1/116worldanyoneý“rÍý“rÍ /test-1/117worldanyone$$ý“rãý“rã /test-1/114worldanyoneý“r¤ý“r¤ /test-1/115worldanyoneý“r´ý“r´ /test-1/112worldanyone÷÷ý“r‚ý“r‚ /test-1/113worldanyoneýýý“r’ý“r’ /test-1/110worldanyoneààý“rWý“rW /test-1/111worldanyoneééý“rlý“rl /test-1/118worldanyone))ý“ròý“rò /test-1/119worldanyone66ý“sý“s /test-1/435worldanyoneººý“„&ý“„& /test-1/436worldanyoneÄÄý“„0ý“„0 /test-1/433worldanyone®®ý“„ý“„ /test-1/434worldanyone°°ý“„ý“„ /test-1/431worldanyoneššý“„ý“„ /test-1/432worldanyone¤¤ý“„ý“„ /test-1/430worldanyoneý“ƒöý“ƒö /test-1/439worldanyoneÜÜý“„Qý“„Q /test-1/437worldanyoneÌÌý“„9ý“„9 /test-1/438worldanyoneÑÑý“„Dý“„D /test-1/125worldanyoneqqý“s—ý“s— /test-1/126worldanyone{{ý“s¡ý“s¡ /test-1/127worldanyone‚‚ý“s­ý“s­ /test-1/128worldanyoneŠŠý“s»ý“s» /test-1/121worldanyoneLLý“sSý“sS /test-1/122worldanyoneUUý“sfý“sf /test-1/123worldanyone__ý“suý“su /test-1/124worldanyoneiiý“sˆý“sˆ /test-1/129worldanyoneý“sÆý“sÆ /test-1/444worldanyone  ý“„†ý“„† /test-1/445worldanyoneý“„”ý“„” /test-1/446worldanyoneý“„ ý“„  /test-1/447worldanyone))ý“„­ý“„­ /test-1/440worldanyoneææý“„_ý“„_ /test-1/441worldanyoneððý“„hý“„h /test-1/442worldanyoneúúý“„qý“„q /test-1/443worldanyoneý“„zý“„z /test-1/120worldanyone@@ý“s<ý“s< /test-1/448worldanyone33ý“„¸ý“„¸ /test-1/449worldanyone==ý“„Ãý“„à /test-1/134worldanyone¾¾ý“tý“t /test-1/135worldanyoneÈÈý“tý“t /test-1/132worldanyoneªªý“sìý“sì /test-1/133worldanyone´´ý“søý“sø /test-1/138worldanyoneããý“t6ý“t6 /test-1/139worldanyoneììý“tAý“tA /test-1/136worldanyoneÍÍý“tý“t /test-1/137worldanyone××ý“t'ý“t' /test-1/450worldanyoneGGý“„Ëý“„Ë /test-1/453worldanyone``ý“„úý“„ú /test-1/454worldanyonejjý“…ý“… /test-1/451worldanyoneQQý“„Ôý“„Ô /test-1/452worldanyoneVVý“„Þý“„Þ /test-1/457worldanyoneˆˆý“…"ý“…" /test-1/458worldanyone’’ý“…+ý“…+ /test-1/455worldanyonettý“…ý“… /test-1/456worldanyone~~ý“…ý“… /test-1/459worldanyoneœœý“…5ý“…5 /test-1/131worldanyone¤¤ý“sßý“sß /test-1/130worldanyoneššý“sÒý“sÒ /test-1/143worldanyoneý“t|ý“t| /test-1/144worldanyoneý“tý“t /test-1/145worldanyone""ý“t ý“t  /test-1/146worldanyone,,ý“t¬ý“t¬ /test-1/147worldanyone66ý“t·ý“t· /test-1/148worldanyone@@ý“tÁý“tÁ /test-1/149worldanyoneIIý“tËý“tË /test-1/461worldanyone­­ý“…Wý“…W /test-1/460worldanyone¤¤ý“…Bý“…B /test-1/462worldanyoneººý“…jý“…j /test-1/463worldanyoneÁÁý“…vý“…v /test-1/464worldanyoneËËý“…ý“… /test-1/465worldanyoneÕÕý“…‡ý“…‡ /test-1/466worldanyone××ý“…’ý“…’ /test-1/467worldanyoneààý“…ý“… /test-1/468worldanyoneëëý“…¬ý“…¬ /test-1/469worldanyoneõõý“…Äý“…Ä /test-1/140worldanyone÷÷ý“tNý“tN /test-1/142worldanyone  ý“tmý“tm /test-1/141worldanyoneý“t^ý“t^ /test-1/687worldanyoneëëý“ ý“ /test-1/686worldanyoneááý“ý“ /test-1/689worldanyoneüüý“ý“ /test-1/688worldanyoneõõý“ý“ /test-1/683worldanyoneÊÊý“Žßý“Žß /test-1/682worldanyone¿¿ý“ŽÐý“ŽÐ /test-1/685worldanyoneÖÖý“Žöý“Žö /test-1/684worldanyoneÕÕý“Žëý“Žë /test-1/680worldanyoneªªý“޹ý“޹ /test-1/681worldanyone°°ý“ŽÄý“ŽÄ /test-1/678worldanyone““ý“Ž¢ý“Ž¢ /test-1/677worldanyone‹‹ý“Žtý“Žt /test-1/676worldanyoneý“Žjý“Žj /test-1/675worldanyonewwý“Žaý“Ža /test-1/674worldanyonemmý“ŽXý“ŽX /test-1/673worldanyonejjý“ŽOý“ŽO /test-1/672worldanyone``ý“ŽFý“ŽF /test-1/671worldanyoneVVý“Ž>ý“Ž> /test-1/679worldanyoneœœý“ެý“ެ /test-1/670worldanyoneLLý“Ž5ý“Ž5 /test-1/696worldanyone99ý“^ý“^ /test-1/695worldanyone//ý“Vý“V /test-1/694worldanyone%%ý“Mý“M /test-1/693worldanyoneý“@ý“@ /test-1/699worldanyoneOOý“ý“ /test-1/698worldanyoneDDý“sý“s /test-1/697worldanyone<<ý“hý“h /test-1/691worldanyone  ý“.ý“. /test-1/692worldanyoneý“6ý“6 /test-1/690worldanyoneý“%ý“% /test-1/646worldanyone``ý“=ý“= /test-1/647worldanyonekký“Hý“H /test-1/648worldanyoneuuý“Sý“S /test-1/649worldanyoneý“^ý“^ /test-1/195worldanyoneøøý“w(ý“w( /test-1/642worldanyone77ý“ý“ /test-1/194worldanyoneïïý“wý“w /test-1/643worldanyoneAAý“ý“ /test-1/197worldanyone  ý“w>ý“w> /test-1/644worldanyoneKKý“%ý“% /test-1/196worldanyoneý“w3ý“w3 /test-1/645worldanyoneUUý“1ý“1 /test-1/191worldanyoneÖÖý“vûý“vû /test-1/190worldanyoneÌÌý“vðý“vð /test-1/193worldanyoneèèý“wý“w /test-1/640worldanyone%%ý“Œÿý“Œÿ /test-1/192worldanyoneààý“wý“w /test-1/641worldanyone--ý“ ý“ /test-1/198worldanyoneý“wHý“wH /test-1/199worldanyone  ý“wUý“wU /test-1/639worldanyoneý“Œöý“Œö /test-1/637worldanyone  ý“Œãý“Œã /test-1/638worldanyoneý“Œìý“Œì /test-1/635worldanyone÷÷ý“ŒÒý“ŒÒ /test-1/636worldanyoneý“ŒÚý“ŒÚ /test-1/633worldanyoneããý“Œ¾ý“Œ¾ /test-1/634worldanyoneííý“ŒÉý“ŒÉ /test-1/631worldanyoneÕÕý“Œ«ý“Œ« /test-1/632worldanyoneÝÝý“Œ´ý“Œ´ /test-1/630worldanyoneËËý“Œ¡ý“Œ¡ /test-1/668worldanyone;;ý“Žý“Ž /test-1/669worldanyoneCCý“Ž(ý“Ž( /test-1/660worldanyoneêêý“Ñý“Ñ /test-1/661worldanyone÷÷ý“Üý“Ü /test-1/662worldanyoneý“åý“å /test-1/663worldanyone  ý“ïý“ï /test-1/664worldanyoneý“úý“ú /test-1/665worldanyoneý“Žý“Ž /test-1/666worldanyone''ý“Žý“Ž /test-1/667worldanyone11ý“Žý“Ž /test-1/659worldanyoneããý“Æý“Æ /test-1/657worldanyoneÌÌý“¯ý“¯ /test-1/658worldanyone××ý“ºý“º /test-1/651worldanyone‘‘ý“tý“t /test-1/652worldanyone››ý“ý“ /test-1/650worldanyone‰‰ý“iý“i /test-1/655worldanyone¸¸ý“›ý“› /test-1/656worldanyoneÃÃý“¦ý“¦ /test-1/653worldanyone¥¥ý“‰ý“‰ /test-1/654worldanyone¯¯ý“’ý“’ /test-1/35worldanyoneý“mý“m /test-1/36worldanyone''ý“mý“m /test-1/33worldanyoneý“lÎý“lÎ /test-1/34worldanyoneý“lÞý“lÞ /test-1/39worldanyoneCCý“mBý“mB /test-1/37worldanyone00ý“m"ý“m" /test-1/38worldanyone::ý“m6ý“m6 /test-1/43worldanyoneeeý“m}ý“m} /test-1/42worldanyone\\ý“moý“mo /test-1/41worldanyoneRRý“mcý“mc /test-1/40worldanyoneHHý“mRý“mR /test-1/22worldanyone››ý“lý“l /test-1/23worldanyone¥¥ý“lý“l /test-1/24worldanyone¯¯ý“l)ý“l) /test-1/25worldanyone··ý“l?ý“l? /test-1/26worldanyoneÁÁý“lLý“lL /test-1/27worldanyoneÉÉý“l]ý“l] /test-1/28worldanyoneÒÒý“lný“ln /test-1/29worldanyoneßßý“l…ý“l… /test-1/30worldanyoneëëý“l˜ý“l˜ /test-1/32worldanyoneûûý“l½ý“l½ /test-1/31worldanyoneôôý“l«ý“l« /test-1/19worldanyoneƒƒý“kÝý“kÝ /test-1/17worldanyoneqqý“k´ý“k´ /test-1/18worldanyoneyyý“kÈý“kÈ /test-1/15worldanyone]]ý“k‡ý“k‡ /test-1/16worldanyoneggý“k ý“k  /test-1/13worldanyoneKKý“kVý“kV /test-1/14worldanyoneTTý“klý“kl /test-1/11worldanyoneAAý“k0ý“k0 /test-1/12worldanyoneBBý“k@ý“k@ /test-1/21worldanyone——ý“kûý“kû /test-1/20worldanyoneý“kïý“kï /test-1/10worldanyone99ý“k ý“k /test-1/79worldanyone¶¶ý“pý“p /test-1/78worldanyone¬¬ý“pý“p /test-1/77worldanyone££ý“pý“p /test-1/82worldanyoneÒÒý“pQý“pQ /test-1/83worldanyoneÚÚý“paý“pa /test-1/80worldanyoneÁÁý“p2ý“p2 /test-1/81worldanyoneËËý“pBý“pB /test-1/86worldanyone÷÷ý“pˆý“pˆ /test-1/87worldanyoneþþý“p–ý“p– /test-1/84worldanyoneääý“ppý“pp /test-1/85worldanyoneîîý“p{ý“p{ /test-1/67worldanyoneDDý“oKý“oK /test-1/66worldanyone::ý“o1ý“o1 /test-1/69worldanyoneWWý“o~ý“o~ /test-1/68worldanyoneMMý“omý“om /test-1/70worldanyoneaaý“oý“o /test-1/71worldanyonekký“oŸý“oŸ /test-1/72worldanyonewwý“o³ý“o³ /test-1/73worldanyoneý“oÃý“oà /test-1/74worldanyone††ý“oÑý“oÑ /test-1/75worldanyone’’ý“oãý“oã /test-1/76worldanyone˜˜ý“oóý“oó /test-1/59worldanyoneññý“n’ý“n’ /test-1/58worldanyoneèèý“n|ý“n| /test-1/57worldanyoneÝÝý“ndý“nd /test-1/56worldanyoneÕÕý“nRý“nR /test-1/55worldanyoneËËý“nCý“nC /test-1/64worldanyone''ý“oý“o /test-1/65worldanyone00ý“o ý“o /test-1/62worldanyoneý“nÚý“nÚ /test-1/63worldanyoneý“nõý“nõ /test-1/60worldanyoneûûý“n¥ý“n¥ /test-1/61worldanyoneý“nÁý“nÁ /test-1/49worldanyone‘‘ý“mßý“mß /test-1/48worldanyoneŒŒý“mÑý“mÑ /test-1/45worldanyonerrý“m™ý“m™ /test-1/44worldanyonejjý“mŒý“mŒ /test-1/47worldanyone‚‚ý“m¼ý“m¼ /test-1/46worldanyonezzý“m«ý“m« /test-1/51worldanyone¢¢ý“ný“n /test-1/52worldanyone¬¬ý“ný“n /test-1/53worldanyone¶¶ý“n)ý“n) /test-1/54worldanyoneÀÀý“n5ý“n5 /test-1/50worldanyoneššý“mòý“mò /test-1/281worldanyone  ý“|_ý“|_ /test-1/487worldanyone˜˜ý“†ý“† /test-1/280worldanyone  ý“|Sý“|S /test-1/486worldanyoneý“†„ý“†„ /test-1/485worldanyone……ý“†wý“†w /test-1/484worldanyone||ý“†gý“†g /test-1/285worldanyone J Jý“|”ý“|” /test-1/284worldanyone ? ?ý“|…ý“|… /test-1/283worldanyone 7 7ý“|yý“|y /test-1/489worldanyone¬¬ý“†ªý“†ª /test-1/282worldanyone * *ý“|lý“|l /test-1/488worldanyone££ý“†žý“†ž /test-1/288worldanyone g gý“|µý“|µ /test-1/289worldanyone m mý“|Àý“|À /test-1/286worldanyone S Sý“| ý“|  /test-1/287worldanyone ] ]ý“|«ý“|« /test-1/482worldanyoneddý“†Gý“†G /test-1/483worldanyoneooý“†Vý“†V /test-1/480worldanyoneTTý“†3ý“†3 /test-1/481worldanyone^^ý“†<ý“†< /test-1/474worldanyone&&ý“…ùý“…ù /test-1/473worldanyoneý“…ïý“…ï /test-1/270worldanyone ³ ³ý“{½ý“{½ /test-1/476worldanyone33ý“† ý“† /test-1/475worldanyone))ý“†ý“† /test-1/272worldanyone Ç Çý“{Øý“{Ø /test-1/478worldanyoneAAý“†ý“† /test-1/271worldanyone ½ ½ý“{Ëý“{Ë /test-1/477worldanyone==ý“†ý“† /test-1/274worldanyone × ×ý“{óý“{ó /test-1/273worldanyone Ñ Ñý“{äý“{ä /test-1/479worldanyoneJJý“†*ý“†* /test-1/275worldanyone à àý“|ý“| /test-1/276worldanyone ê êý“|ý“| /test-1/277worldanyone ù ùý“|*ý“|* /test-1/278worldanyone  ý“|8ý“|8 /test-1/279worldanyone  ý“|Fý“|F /test-1/470worldanyoneþþý“…Ðý“…Ð /test-1/471worldanyoneý“…Úý“…Ú /test-1/472worldanyoneý“…äý“…ä /test-1/109worldanyoneÙÙý“rGý“rG /test-1/108worldanyoneÊÊý“r.ý“r. /test-1/107worldanyone¿¿ý“rý“r /test-1/106worldanyoneµµý“rý“r /test-1/105worldanyone««ý“qûý“qû /test-1/104worldanyoneŸŸý“qìý“qì /test-1/103worldanyone••ý“qßý“qß /test-1/99worldanyonenný“q¢ý“q¢ /test-1/102worldanyoneŒŒý“qÌý“qÌ /test-1/101worldanyone~~ý“q»ý“q» /test-1/100worldanyonettý“q®ý“q® /test-1/98worldanyoneccý“q™ý“q™ /test-1/97worldanyoneZZý“qŽý“qŽ /test-1/96worldanyoneOOý“q~ý“q~ /test-1/95worldanyoneHHý“qrý“qr /test-1/94worldanyoneCCý“qdý“qd /test-1/93worldanyone99ý“qYý“qY /test-1/92worldanyone//ý“qIý“qI /test-1/91worldanyone$$ý“q:ý“q: /test-1/294worldanyone ˜ ˜ý“|üý“|ü /test-1/90worldanyoneý“q,ý“q, /test-1/293worldanyone  ý“|îý“|î /test-1/499worldanyone  ý“‡ý“‡ /test-1/296worldanyone ¬ ¬ý“}ý“} /test-1/295worldanyone ¡ ¡ý“}ý“} /test-1/290worldanyone w wý“|Êý“|Ê /test-1/496worldanyoneóóý“†÷ý“†÷ /test-1/495worldanyoneèèý“†îý“†î /test-1/292worldanyone „ „ý“|àý“|à /test-1/498worldanyoneý“‡ý“‡ /test-1/291worldanyone  ý“|Ôý“|Ô /test-1/497worldanyoneööý“‡ý“‡ /test-1/491worldanyoneÀÀý“†Åý“†Å /test-1/492worldanyoneÊÊý“†Ñý“†Ñ /test-1/493worldanyoneÔÔý“†Ûý“†Û /test-1/494worldanyoneÞÞý“†äý“†ä /test-1/297worldanyone · ·ý“} ý“} /test-1/298worldanyone Á Áý“}.ý“}. /test-1/299worldanyone Ë Ëý“}7ý“}7 /test-1/490worldanyone··ý“†ºý“†º /test-1/88worldanyoneý“p¦ý“p¦ /test-1/89worldanyoneý“q ý“q /test-1/240worldanyone™™ý“z=ý“z= /test-1/241worldanyone¡¡ý“zMý“zM /test-1/245worldanyoneÇÇý“z…ý“z… /test-1/244worldanyone»»ý“zxý“zx /test-1/243worldanyone²²ý“zeý“ze /test-1/242worldanyone©©ý“z[ý“z[ /test-1/249worldanyoneêêý“z­ý“z­ /test-1/248worldanyoneààý“z¡ý“z¡ /test-1/247worldanyoneÙÙý“z™ý“z™ /test-1/246worldanyoneÏÏý“zý“z /test-1/230worldanyone44ý“yý“y /test-1/239worldanyoneŽŽý“z*ý“z* /test-1/232worldanyoneJJý“y¼ý“y¼ /test-1/231worldanyone>>ý“y®ý“y® /test-1/234worldanyone^^ý“yÚý“yÚ /test-1/233worldanyoneTTý“yËý“yË /test-1/236worldanyonerrý“yùý“yù /test-1/235worldanyoneggý“yêý“yê /test-1/238worldanyone‡‡ý“zý“z /test-1/237worldanyone{{ý“z ý“z /test-1/262worldanyone m mý“{Zý“{Z /test-1/263worldanyone w wý“{hý“{h /test-1/260worldanyone S Sý“{?ý“{? /test-1/261worldanyone ` `ý“{Mý“{M /test-1/269worldanyone ­ ­ý“{¯ý“{¯ /test-1/268worldanyone ¤ ¤ý“{ ý“{  /test-1/267worldanyone œ œý“{—ý“{— /test-1/266worldanyone ’ ’ý“{Žý“{Ž /test-1/265worldanyone ˆ ˆý“{‚ý“{‚ /test-1/264worldanyone ƒ ƒý“{vý“{v /test-1/250worldanyoneôôý“z¹ý“z¹ /test-1/251worldanyoneÿÿý“zÝý“zÝ /test-1/252worldanyone  ý“zèý“zè /test-1/258worldanyone C Cý“{)ý“{) /test-1/257worldanyone 9 9ý“{ ý“{ /test-1/259worldanyone M Mý“{4ý“{4 /test-1/254worldanyone  ý“zÿý“zÿ /test-1/253worldanyone  ý“zòý“zò /test-1/256worldanyone / /ý“{ý“{ /test-1/255worldanyone ' 'ý“{ ý“{ /test-2worldanyone  ý“j‰ý“j‰Ò /test-2/338worldanyone i iý“€­ý“€­ /test-2/339worldanyone p pý“€¸ý“€¸ /test-2/332worldanyone 0 0ý“€iý“€i /test-2/333worldanyone 7 7ý“€uý“€u /test-2/159worldanyoneUUý“v^ý“v^ /test-2/330worldanyone  ý“€Ký“€K /test-2/158worldanyoneIIý“vPý“vP /test-2/331worldanyone $ $ý“€Zý“€Z /test-2/157worldanyone??ý“v@ý“v@ /test-2/336worldanyone U Uý“€™ý“€™ /test-2/156worldanyone//ý“v.ý“v. /test-2/337worldanyone ^ ^ý“€£ý“€£ /test-2/155worldanyone))ý“v!ý“v! /test-2/334worldanyone A Aý“€€ý“€€ /test-2/154worldanyoneý“vý“v /test-2/335worldanyone J Jý“€ý“€ /test-2/601worldanyoneññý“Õý“Õ /test-2/531worldanyone  ý“Šºý“Šº /test-2/152worldanyone  ý“u÷ý“u÷ /test-2/600worldanyoneèèý“Ëý“Ë /test-2/530worldanyoneý“бý“б /test-2/153worldanyoneý“vý“v /test-2/150worldanyoneùùý“uØý“uØ /test-2/151worldanyoneý“uæý“uæ /test-2/535worldanyoneGGý“Šâý“Šâ /test-2/534worldanyone<<ý“ŠØý“ŠØ /test-2/533worldanyone22ý“ŠÎý“ŠÎ /test-2/532worldanyone))ý“ŠÃý“ŠÃ /test-2/609worldanyoneFFý“Ž*ý“Ž* /test-2/539worldanyoneooý“‹ý“‹ /test-2/608worldanyone::ý“Žý“Ž /test-2/538worldanyoneffý“‹ý“‹ /test-2/607worldanyone00ý“Žý“Ž /test-2/537worldanyone\\ý“Šøý“Šø /test-2/606worldanyone$$ý“Žý“Ž /test-2/536worldanyoneOOý“Šìý“Šì /test-2/605worldanyoneý“üý“ü /test-2/604worldanyoneý“ñý“ñ /test-2/603worldanyoneý“éý“é /test-2/602worldanyoneúúý“àý“à /test-2/202worldanyoneý“yIý“yI /test-2/349worldanyone Ô Ôý“‚ ý“‚ /test-2/203worldanyoneý“y^ý“y^ /test-2/204worldanyoneý“ypý“yp /test-2/205worldanyone##ý“y{ý“y{ /test-2/200worldanyoneóóý“y(ý“y( /test-2/201worldanyoneýýý“y9ý“y9 /test-2/341worldanyone … …ý“€Íý“€Í /test-2/169worldanyoneÀÀý“váý“vá /test-2/342worldanyone  ý“€Øý“€Ø /test-2/343worldanyone ™ ™ý“€âý“€â /test-2/344worldanyone ¤ ¤ý“€ìý“€ì /test-2/166worldanyone¡¡ý“v¿ý“v¿ /test-2/206worldanyone--ý“yŽý“yŽ /test-2/345worldanyone ¯ ¯ý“€÷ý“€÷ /test-2/165worldanyone––ý“v­ý“v­ /test-2/207worldanyone<<ý“y£ý“y£ /test-2/346worldanyone ¹ ¹ý“ý“ /test-2/168worldanyone¶¶ý“vÖý“vÖ /test-2/208worldanyoneEEý“y´ý“y´ /test-2/347worldanyone  Âý“ ý“ /test-2/167worldanyone¬¬ý“vËý“vË /test-2/209worldanyoneOOý“yÆý“yÆ /test-2/348worldanyone Í Íý“ý“ /test-2/540worldanyone}}ý“‹ý“‹ /test-2/409worldanyoneFFý“„Èý“„È /test-2/161worldanyonejjý“vzý“vz /test-2/3worldanyoneý“j¼ý“j¼ /test-2/408worldanyone<<ý“„¿ý“„¿ /test-2/162worldanyoneuuý“v†ý“v† /test-2/2worldanyoneý“j®ý“j® /test-2/542worldanyoneý“‹3ý“‹3 /test-2/163worldanyone€€ý“v“ý“v“ /test-2/1worldanyoneý“j¢ý“j¢ /test-2/541worldanyone……ý“‹)ý“‹) /test-2/164worldanyoneŠŠý“v ý“v  /test-2/0worldanyone  ý“j–ý“j– /test-2/544worldanyone¤¤ý“‹Hý“‹H /test-2/405worldanyone$$ý“„¡ý“„¡ /test-2/7worldanyone88ý“ký“k /test-2/543worldanyone››ý“‹=ý“‹= /test-2/404worldanyoneý“„”ý“„” /test-2/6worldanyone00ý“ký“k /test-2/546worldanyoneººý“‹^ý“‹^ /test-2/407worldanyone22ý“„¶ý“„¶ /test-2/5worldanyone**ý“jíý“jí /test-2/340worldanyone | |ý“€Äý“€Ä /test-2/545worldanyone¯¯ý“‹Sý“‹S /test-2/406worldanyone((ý“„¬ý“„¬ /test-2/160worldanyone``ý“vmý“vm /test-2/4worldanyone##ý“jÐý“jÐ /test-2/548worldanyoneÎÎý“‹›ý“‹› /test-2/401worldanyoneóóý“„lý“„l /test-2/547worldanyoneÄÄý“‹Žý“‹Ž /test-2/400worldanyoneèèý“„aý“„a /test-2/403worldanyoneý“„ˆý“„ˆ /test-2/9worldanyoneFFý“kFý“kF /test-2/549worldanyoneÙÙý“‹¦ý“‹¦ /test-2/402worldanyoneþþý“„yý“„y /test-2/8worldanyone>>ý“k+ý“k+ /test-2/211worldanyoneeeý“yçý“yç /test-2/212worldanyonemmý“yõý“yõ /test-2/210worldanyone[[ý“yØý“yØ /test-2/215worldanyone‹‹ý“z(ý“z( /test-2/318worldanyone £ £ý“±ý“± /test-2/216worldanyone••ý“z6ý“z6 /test-2/319worldanyone © ©ý“¾ý“¾ /test-2/213worldanyonewwý“zý“z /test-2/316worldanyone Š Šý“”ý“” /test-2/214worldanyone€€ý“zý“z /test-2/317worldanyone ’ ’ý“¡ý“¡ /test-2/179worldanyoneý“wRý“wR /test-2/219worldanyone´´ý“zmý“zm /test-2/314worldanyone w wý“yý“y /test-2/178worldanyoneý“wDý“wD /test-2/315worldanyone ~ ~ý“…ý“… /test-2/177worldanyoneý“w8ý“w8 /test-2/217worldanyoneŸŸý“zIý“zI /test-2/312worldanyone ^ ^ý“Zý“Z /test-2/176worldanyoneý“w+ý“w+ /test-2/218worldanyone««ý“z[ý“z[ /test-2/313worldanyone g gý“gý“g /test-2/310worldanyone J Jý“Fý“F /test-2/311worldanyone T Tý“Qý“Q /test-2/513worldanyoneffý“‰øý“‰ø /test-2/418worldanyonežžý“…8ý“…8 /test-2/170worldanyoneÊÊý“véý“vé /test-2/512worldanyone]]ý“‰íý“‰í /test-2/417worldanyone””ý“….ý“…. /test-2/171worldanyoneÔÔý“vôý“vô /test-2/511worldanyoneRRý“‰äý“‰ä /test-2/416worldanyoneŠŠý“…%ý“…% /test-2/510worldanyoneHHý“‰Ûý“‰Û /test-2/415worldanyone€€ý“…ý“… /test-2/623worldanyoneØØý“Ž÷ý“Ž÷ /test-2/174worldanyoneììý“wý“w /test-2/622worldanyoneÑÑý“Žêý“Žê /test-2/175worldanyoneööý“w"ý“w" /test-2/621worldanyoneÉÉý“Žßý“Žß /test-2/172worldanyoneØØý“wý“w /test-2/620worldanyoneººý“ŽÐý“ŽÐ /test-2/419worldanyone©©ý“…Cý“…C /test-2/173worldanyoneââý“w ý“w /test-2/627worldanyone  ý“(ý“( /test-2/410worldanyoneMMý“„Óý“„Ó /test-2/721worldanyoneÚÚý“•ªý“•ª /test-2/626worldanyoneÿÿý“ý“ /test-2/720worldanyoneÐÐý“•Ÿý“•Ÿ /test-2/625worldanyoneïïý“ý“ /test-2/519worldanyone¦¦ý“Š;ý“Š; /test-2/624worldanyoneææý“ý“ /test-2/518worldanyoneý“Š0ý“Š0 /test-2/517worldanyoneý“Š"ý“Š" /test-2/414worldanyoneuuý“…ý“… /test-2/516worldanyone……ý“Šý“Š /test-2/413worldanyonekký“…ý“… /test-2/629worldanyone  ý“Aý“A /test-2/515worldanyonexxý“Š ý“Š /test-2/412worldanyoneaaý“„úý“„ú /test-2/628worldanyoneý“3ý“3 /test-2/514worldanyoneqqý“Šý“Š /test-2/411worldanyoneWWý“„Þý“„Þ /test-2/220worldanyone¾¾ý“zyý“zy /test-2/221worldanyoneÈÈý“z†ý“z† /test-2/222worldanyoneÓÓý“z“ý“z“ /test-2/223worldanyoneÜÜý“zŸý“zŸ /test-2/224worldanyoneææý“zªý“zª /test-2/327worldanyone û ûý“€ý“€ /test-2/225worldanyoneððý“z´ý“z´ /test-2/328worldanyone  ý“€-ý“€- /test-2/226worldanyoneúúý“z¾ý“z¾ /test-2/329worldanyone  ý“€>ý“€> /test-2/227worldanyone  ý“zåý“zå /test-2/188worldanyoneyyý“xrý“xr /test-2/228worldanyone  ý“zðý“zð /test-2/323worldanyone Ò Òý“ëý“ë /test-2/187worldanyonellý“x_ý“x_ /test-2/229worldanyone  ý“zýý“zý /test-2/324worldanyone Ü Üý“öý“ö /test-2/325worldanyone ä äý“€ý“€ /test-2/189worldanyone……ý“xý“x /test-2/326worldanyone î îý“€ý“€ /test-2/709worldanyone]]ý“• ý“• /test-2/320worldanyone ´ ´ý“Êý“Ê /test-2/321worldanyone ¾ ¾ý“Õý“Õ /test-2/322worldanyone È Èý“âý“â /test-2/717worldanyone²²ý“•yý“•y /test-2/522worldanyoneÉÉý“Š`ý“Š` /test-2/427worldanyoneüüý“…Åý“…Å /test-2/716worldanyone¦¦ý“•lý“•l /test-2/521worldanyone¿¿ý“ŠSý“ŠS /test-2/426worldanyoneòòý“…®ý“…® /test-2/180worldanyone**ý“wcý“wc /test-2/719worldanyoneÇÇý“•“ý“•“ /test-2/524worldanyoneÞÞý“Šuý“Šu /test-2/429worldanyoneý“…ßý“…ß /test-2/181worldanyone44ý“wsý“ws /test-2/718worldanyone»»ý“•„ý“•„ /test-2/523worldanyoneÒÒý“Šký“Šk /test-2/428worldanyoneý“…Óý“…Ó /test-2/182worldanyone77ý“xý“x /test-2/713worldanyone……ý“•:ý“•: /test-2/610worldanyoneRRý“Ž6ý“Ž6 /test-2/183worldanyone@@ý“x)ý“x) /test-2/712worldanyonezzý“•/ý“•/ /test-2/184worldanyoneKKý“x5ý“x5 /test-2/715worldanyone››ý“•Tý“•T /test-2/612worldanyoneffý“ŽMý“ŽM /test-2/520worldanyone°°ý“ŠGý“ŠG /test-2/185worldanyoneUUý“xCý“xC /test-2/714worldanyoneý“•Eý“•E /test-2/611worldanyoneZZý“ŽAý“ŽA /test-2/186worldanyone^^ý“xOý“xO /test-2/614worldanyone{{ý“Ždý“Žd /test-2/613worldanyoneqqý“ŽZý“ŽZ /test-2/529worldanyone  ý“Š©ý“Š© /test-2/711worldanyoneppý“•$ý“•$ /test-2/616worldanyoneý“Žxý“Žx /test-2/421worldanyone¸¸ý“…iý“…i /test-2/710worldanyoneggý“•ý“• /test-2/615worldanyone„„ý“Žný“Žn /test-2/420worldanyone³³ý“…Xý“…X /test-2/618worldanyone¨¨ý“޹ý“޹ /test-2/526worldanyoneððý“ŠŠý“ŠŠ /test-2/423worldanyoneÍÍý“…ý“… /test-2/617worldanyone˜˜ý“Žªý“Žª /test-2/525worldanyoneääý“Šý“Š /test-2/422worldanyoneÃÃý“…vý“…v /test-2/528worldanyoneý“ŠŸý“ŠŸ /test-2/425worldanyoneèèý“… ý“…  /test-2/619worldanyone®®ý“ŽÃý“ŽÃ /test-2/527worldanyoneùùý“Š”ý“Š” /test-2/424worldanyoneÞÞý“…”ý“…” /test-2/116worldanyoneý“sÔý“sÔ /test-2/117worldanyone¦¦ý“sâý“sâ /test-2/114worldanyone‡‡ý“s¶ý“s¶ /test-2/115worldanyone’’ý“sÇý“sÇ /test-2/571worldanyoneµµý“ŒŒý“ŒŒ /test-2/112worldanyonerrý“s™ý“s™ /test-2/570worldanyone§§ý“Œ€ý“Œ€ /test-2/113worldanyone||ý“s¨ý“s¨ /test-2/110worldanyone``ý“suý“su /test-2/111worldanyonehhý“s‡ý“s‡ /test-2/118worldanyone³³ý“sðý“sð /test-2/119worldanyone»»ý“süý“sü /test-2/435worldanyoneOOý“†+ý“†+ /test-2/436worldanyoneZZý“†:ý“†: /test-2/433worldanyone77ý“†ý“† /test-2/434worldanyoneFFý“†ý“† /test-2/431worldanyone##ý“…øý“…ø /test-2/700worldanyoneý“”«ý“”« /test-2/432worldanyone00ý“†ý“† /test-2/430worldanyoneý“…ìý“…ì /test-2/703worldanyone((ý“”Èý“”È /test-2/574worldanyoneÑÑý“Œªý“Œª /test-2/704worldanyone00ý“”Òý“”Ò /test-2/575worldanyoneÚÚý“Œ´ý“Œ´ /test-2/701worldanyoneý“”¶ý“”¶ /test-2/572worldanyoneÀÀý“Œ–ý“Œ– /test-2/702worldanyoneý“”Àý“”À /test-2/573worldanyoneÆÆý“Œ ý“Œ  /test-2/707worldanyoneLLý“”óý“”ó /test-2/578worldanyoneýýý“Œ×ý“Œ× /test-2/439worldanyone€€ý“†gý“†g /test-2/708worldanyoneSSý“”ÿý“”ÿ /test-2/579worldanyoneý“Œâý“Œâ /test-2/705worldanyone::ý“”Ýý“”Ý /test-2/576worldanyoneééý“ŒÀý“ŒÀ /test-2/437worldanyoneeeý“†Hý“†H /test-2/706worldanyoneCCý“”èý“”è /test-2/577worldanyoneôôý“ŒÌý“ŒÌ /test-2/438worldanyonenný“†Uý“†U /test-2/125worldanyoneüüý“tYý“tY /test-2/126worldanyoneý“tjý“tj /test-2/127worldanyone  ý“txý“tx /test-2/300worldanyone é éý“~Éý“~É /test-2/128worldanyoneý“t’ý“t’ /test-2/580worldanyoneý“Œîý“Œî /test-2/121worldanyoneÒÒý“tý“t /test-2/302worldanyone  ý“~éý“~é /test-2/122worldanyoneÝÝý“t,ý“t, /test-2/301worldanyone ó óý“~Ùý“~Ù /test-2/582worldanyone))ý“ý“ /test-2/123worldanyoneççý“t:ý“t: /test-2/304worldanyone  ý“ý“ /test-2/581worldanyoneý“Œ÷ý“Œ÷ /test-2/124worldanyoneññý“tGý“tG /test-2/303worldanyone  ý“~õý“~õ /test-2/306worldanyone & &ý“ý“ /test-2/305worldanyone  ý“ý“ /test-2/308worldanyone 9 9ý“0ý“0 /test-2/307worldanyone / /ý“$ý“$ /test-2/129worldanyone((ý“t£ý“t£ /test-2/309worldanyone A Aý“<ý“< /test-2/444worldanyone±±ý“†«ý“†« /test-2/445worldanyone¿¿ý“†»ý“†» /test-2/446worldanyoneÇÇý“†Çý“†Ç /test-2/447worldanyoneÎÎý“†Óý“†Ó /test-2/440worldanyone‡‡ý“†wý“†w /test-2/441worldanyone””ý“†…ý“†… /test-2/442worldanyonežžý“†’ý“†’ /test-2/443worldanyone¨¨ý“† ý“†  /test-2/583worldanyone22ý“ ý“ /test-2/584worldanyone==ý“ý“ /test-2/585worldanyoneGGý“$ý“$ /test-2/120worldanyoneÄÄý“t ý“t /test-2/586worldanyoneTTý“1ý“1 /test-2/587worldanyone__ý“=ý“= /test-2/448worldanyoneÜÜý“†àý“†à /test-2/588worldanyoneiiý“Gý“G /test-2/449worldanyoneååý“†ëý“†ë /test-2/589worldanyoneqqý“Sý“S /test-2/134worldanyoneWWý“tßý“tß /test-2/135worldanyoneaaý“tíý“tí /test-2/132worldanyoneCCý“tÅý“tÅ /test-2/133worldanyoneLLý“tÏý“tÏ /test-2/138worldanyoneý“u$ý“u$ /test-2/139worldanyone‰‰ý“u1ý“u1 /test-2/136worldanyonekký“túý“tú /test-2/137worldanyoneuuý“uý“u /test-2/450worldanyoneïïý“†öý“†ö /test-2/453worldanyone  ý“‡ý“‡ /test-2/454worldanyoneý“‡(ý“‡( /test-2/451worldanyoneýýý“‡ý“‡ /test-2/452worldanyoneý“‡ý“‡ /test-2/457worldanyone66ý“‡Iý“‡I /test-2/458worldanyone@@ý“‡Sý“‡S /test-2/558worldanyone44ý“Œý“Œ /test-2/455worldanyone!!ý“‡3ý“‡3 /test-2/559worldanyone>>ý“Œ ý“Œ /test-2/456worldanyone++ý“‡=ý“‡= /test-2/556worldanyone!!ý“‹îý“‹î /test-2/557worldanyone**ý“‹÷ý“‹÷ /test-2/554worldanyone  ý“‹Üý“‹Ü /test-2/459worldanyoneKKý“‡\ý“‡\ /test-2/555worldanyoneý“‹åý“‹å /test-2/552worldanyone÷÷ý“‹Èý“‹È /test-2/131worldanyone99ý“t¼ý“t¼ /test-2/553worldanyoneý“‹Òý“‹Ò /test-2/130worldanyone//ý“t¯ý“t¯ /test-2/550worldanyoneææý“‹³ý“‹³ /test-2/551worldanyoneííý“‹¾ý“‹¾ /test-2/143worldanyone··ý“uwý“uw /test-2/144worldanyoneÁÁý“uý“u /test-2/560worldanyoneCCý“Œý“Œ /test-2/145worldanyoneÈÈý“uŽý“uŽ /test-2/146worldanyoneÐÐý“uý“u /test-2/147worldanyoneÜÜý“u¯ý“u¯ /test-2/148worldanyoneããý“u½ý“u½ /test-2/149worldanyoneííý“uÍý“uÍ /test-2/461worldanyone__ý“‡qý“‡q /test-2/460worldanyoneTTý“‡gý“‡g /test-2/462worldanyonehhý“‡|ý“‡| /test-2/463worldanyonerrý“‡ˆý“‡ˆ /test-2/464worldanyone||ý“‡”ý“‡” /test-2/465worldanyoneˆˆý“‡¡ý“‡¡ /test-2/569worldanyone››ý“Œtý“Œt /test-2/466worldanyone‘‘ý“‡­ý“‡­ /test-2/467worldanyone  ý“‡ºý“‡º /test-2/468worldanyoneªªý“‡Æý“‡Æ /test-2/469worldanyone´´ý“‡Ñý“‡Ñ /test-2/565worldanyonettý“ŒHý“ŒH /test-2/566worldanyone~~ý“ŒSý“ŒS /test-2/567worldanyone††ý“Œ]ý“Œ] /test-2/568worldanyone’’ý“Œhý“Œh /test-2/561worldanyoneMMý“Œ$ý“Œ$ /test-2/140worldanyone˜˜ý“uFý“uF /test-2/562worldanyoneWWý“Œ.ý“Œ. /test-2/563worldanyoneaaý“Œ6ý“Œ6 /test-2/142worldanyone¬¬ý“uiý“ui /test-2/564worldanyonekký“Œ?ý“Œ? /test-2/141worldanyone  ý“uTý“uT /test-2/687worldanyoneŠŠý“’\ý“’\ /test-2/686worldanyone~~ý“’Qý“’Q /test-2/689worldanyoneœœý“”7ý“”7 /test-2/688worldanyoneý“”*ý“”* /test-2/683worldanyone^^ý“’0ý“’0 /test-2/682worldanyoneRRý“’#ý“’# /test-2/685worldanyonettý“’Fý“’F /test-2/684worldanyoneiiý“’<ý“’< /test-2/680worldanyone<<ý“’ ý“’ /test-2/681worldanyoneGGý“’ý“’ /test-2/678worldanyone%%ý“‘ôý“‘ô /test-2/677worldanyoneý“‘çý“‘ç /test-2/676worldanyoneý“‘Ûý“‘Û /test-2/675worldanyoneý“‘Ïý“‘Ï /test-2/674worldanyoneý“‘¿ý“‘¿ /test-2/673worldanyoneòòý“‘²ý“‘² /test-2/672worldanyoneååý“‘¦ý“‘¦ /test-2/671worldanyoneÙÙý“‘šý“‘š /test-2/679worldanyone//ý“‘ÿý“‘ÿ /test-2/670worldanyoneÑÑý“‘ý“‘ /test-2/696worldanyoneààý“”~ý“”~ /test-2/695worldanyone××ý“”tý“”t /test-2/694worldanyoneÍÍý“”ký“”k /test-2/693worldanyoneÁÁý“”`ý“”` /test-2/699worldanyoneþþý“” ý“”  /test-2/698worldanyoneõõý“”•ý“”• /test-2/697worldanyoneêêý“”‰ý“”‰ /test-2/691worldanyone­­ý“”Lý“”L /test-2/692worldanyone¸¸ý“”Vý“”V /test-2/690worldanyone¥¥ý“”Cý“”C /test-2/646worldanyoneÌÌý“Ký“K /test-2/647worldanyoneÖÖý“wý“w /test-2/648worldanyoneààý“ƒý“ƒ /test-2/649worldanyoneëëý“‘ý“‘ /test-2/642worldanyone¥¥ý“ðý“ð /test-2/195worldanyoneÇÇý“xâý“xâ /test-2/643worldanyone±±ý“þý“þ /test-2/194worldanyone½½ý“xÕý“xÕ /test-2/644worldanyone¹¹ý“ ý“ /test-2/197worldanyone××ý“xþý“xþ /test-2/645worldanyoneÂÂý“ý“ /test-2/196worldanyoneÑÑý“xòý“xò /test-2/191worldanyoneý“x¤ý“x¤ /test-2/190worldanyoneý“x”ý“x” /test-2/640worldanyone‘‘ý“Øý“Ø /test-2/193worldanyone²²ý“xÄý“xÄ /test-2/641worldanyoneœœý“äý“ä /test-2/192worldanyone¨¨ý“x³ý“x³ /test-2/198worldanyoneààý“y ý“y /test-2/199worldanyoneëëý“yý“y /test-2/505worldanyoneý“‰Ÿý“‰Ÿ /test-2/506worldanyone%%ý“‰®ý“‰® /test-2/639worldanyone††ý“Ëý“Ë /test-2/503worldanyoneý“‰†ý“‰† /test-2/504worldanyoneý“‰”ý“‰” /test-2/637worldanyonevvý“¯ý“¯ /test-2/509worldanyone??ý“‰Ñý“‰Ñ /test-2/638worldanyone}}ý“¿ý“¿ /test-2/635worldanyone\\ý“Žý“Ž /test-2/507worldanyone**ý“‰¹ý“‰¹ /test-2/636worldanyoneffý“œý“œ /test-2/508worldanyone55ý“‰Äý“‰Ä /test-2/633worldanyoneIIý“uý“u /test-2/634worldanyoneQQý“ý“ /test-2/631worldanyone33ý“[ý“[ /test-2/632worldanyone::ý“hý“h /test-2/501worldanyoneììý“‰lý“‰l /test-2/630worldanyone''ý“Ný“N /test-2/502worldanyoneúúý“‰{ý“‰{ /test-2/500worldanyoneááý“‰_ý“‰_ /test-2/668worldanyone¼¼ý“‘zý“‘z /test-2/669worldanyoneÈÈý“‘„ý“‘„ /test-2/660worldanyoneccý“‘ý“‘ /test-2/661worldanyonemmý“‘'ý“‘' /test-2/662worldanyonezzý“‘3ý“‘3 /test-2/663worldanyone……ý“‘?ý“‘? /test-2/664worldanyone’’ý“‘Ký“‘K /test-2/665worldanyoneššý“‘Xý“‘X /test-2/666worldanyone©©ý“‘fý“‘f /test-2/667worldanyone²²ý“‘pý“‘p /test-2/659worldanyoneZZý“‘ý“‘ /test-2/657worldanyone??ý“öý“ö /test-2/658worldanyoneJJý“‘ý“‘ /test-2/651worldanyoneýýý“«ý“« /test-2/652worldanyoneý“¸ý“¸ /test-2/650worldanyoneññý“ý“ /test-2/655worldanyone**ý“Þý“Þ /test-2/656worldanyone33ý“èý“è /test-2/653worldanyoneý“Æý“Æ /test-2/654worldanyoneý“Óý“Ó /test-2/35worldanyoneXXý“mjý“mj /test-2/36worldanyoneddý“m}ý“m} /test-2/33worldanyoneBBý“mAý“mA /test-2/34worldanyoneNNý“mUý“mU /test-2/39worldanyone……ý“m¿ý“m¿ /test-2/37worldanyonemmý“m•ý“m• /test-2/38worldanyonexxý“m©ý“m© /test-2/43worldanyone²²ý“ný“n /test-2/42worldanyone¨¨ý“ný“n /test-2/41worldanyonežžý“môý“mô /test-2/40worldanyone––ý“mãý“mã /test-2/22worldanyoneÎÎý“lbý“lb /test-2/23worldanyoneØØý“ltý“lt /test-2/24worldanyoneààý“l†ý“l† /test-2/25worldanyoneèèý“l–ý“l– /test-2/26worldanyoneññý“l©ý“l© /test-2/27worldanyoneþþý“lÀý“lÀ /test-2/28worldanyoneý“lÔý“lÔ /test-2/29worldanyoneý“lêý“lê /test-2/30worldanyone  ý“mý“m /test-2/32worldanyone66ý“m)ý“m) /test-2/31worldanyone,,ý“mý“m /test-2/19worldanyone°°ý“l4ý“l4 /test-2/17worldanyonežžý“lý“l /test-2/18worldanyone§§ý“l"ý“l" /test-2/15worldanyoneˆˆý“kàý“kà /test-2/16worldanyone’’ý“kõý“kõ /test-2/13worldanyoneooý“k³ý“k³ /test-2/14worldanyone{{ý“kÉý“kÉ /test-2/11worldanyoneYYý“kzý“kz /test-2/12worldanyoneeeý“ký“k /test-2/21worldanyoneÄÄý“lVý“lV /test-2/20worldanyoneººý“lDý“lD /test-2/10worldanyoneNNý“k^ý“k^ /test-2/79worldanyone%%ý“q;ý“q; /test-2/78worldanyoneý“q0ý“q0 /test-2/77worldanyoneý“q"ý“q" /test-2/82worldanyoneBBý“qbý“qb /test-2/83worldanyoneIIý“qrý“qr /test-2/80worldanyone--ý“qIý“qI /test-2/81worldanyone88ý“qUý“qU /test-2/86worldanyonehhý“qŸý“qŸ /test-2/87worldanyonexxý“q¯ý“q¯ /test-2/84worldanyoneVVý“q‚ý“q‚ /test-2/85worldanyone__ý“q‘ý“q‘ /test-2/67worldanyone§§ý“pý“p /test-2/66worldanyonežžý“o÷ý“o÷ /test-2/69worldanyone»»ý“p+ý“p+ /test-2/68worldanyone´´ý“pý“p /test-2/70worldanyoneÉÉý“pBý“pB /test-2/71worldanyoneÑÑý“pPý“pP /test-2/72worldanyoneÜÜý“pcý“pc /test-2/73worldanyoneççý“puý“pu /test-2/74worldanyoneòòý“p†ý“p† /test-2/75worldanyoneüüý“p•ý“p• /test-2/76worldanyone  ý“p©ý“p© /test-2/59worldanyoneQQý“orý“or /test-2/58worldanyoneIIý“oOý“oO /test-2/57worldanyone@@ý“o9ý“o9 /test-2/56worldanyone66ý“o)ý“o) /test-2/55worldanyone**ý“oý“o /test-2/64worldanyone……ý“oÐý“oÐ /test-2/65worldanyoneý“oáý“oá /test-2/62worldanyoneppý“o°ý“o° /test-2/63worldanyonezzý“o¿ý“o¿ /test-2/60worldanyone[[ý“o‡ý“o‡ /test-2/61worldanyoneggý“ošý“oš /test-2/49worldanyoneêêý“ný“n /test-2/48worldanyoneââý“nlý“nl /test-2/45worldanyoneÃÃý“n?ý“n? /test-2/44worldanyoneººý“n/ý“n/ /test-2/47worldanyoneÙÙý“n^ý“n^ /test-2/46worldanyoneÏÏý“nOý“nO /test-2/51worldanyoneý“n²ý“n² /test-2/52worldanyoneý“nÇý“nÇ /test-2/53worldanyoneý“nßý“nß /test-2/54worldanyone  ý“nûý“nû /test-2/50worldanyone÷÷ý“n–ý“n– /test-2/487worldanyoneccý“ˆ­ý“ˆ­ /test-2/281worldanyone  ý“}µý“}µ /test-2/486worldanyoneYYý“ˆ¢ý“ˆ¢ /test-2/280worldanyone  ý“}¥ý“}¥ /test-2/485worldanyoneOOý“ˆ™ý“ˆ™ /test-2/484worldanyoneEEý“ˆý“ˆ /test-2/285worldanyone F Fý“}þý“}þ /test-2/284worldanyone : :ý“}ñý“}ñ /test-2/489worldanyoneqqý“ˆÁý“ˆÁ /test-2/283worldanyone 2 2ý“}Íý“}Í /test-2/488worldanyoneggý“ˆ·ý“ˆ· /test-2/282worldanyone ( (ý“}Áý“}Á /test-2/288worldanyone g gý“~'ý“~' /test-2/289worldanyone s sý“~4ý“~4 /test-2/286worldanyone O Oý“~ ý“~ /test-2/287worldanyone X Xý“~ý“~ /test-2/482worldanyone11ý“ˆ{ý“ˆ{ /test-2/483worldanyone::ý“ˆ†ý“ˆ† /test-2/480worldanyoneý“ˆfý“ˆf /test-2/481worldanyone**ý“ˆrý“ˆr /test-2/474worldanyoneääý“ˆý“ˆ /test-2/473worldanyoneÚÚý“‡ûý“‡û /test-2/476worldanyoneööý“ˆ5ý“ˆ5 /test-2/270worldanyone ³ ³ý“}ý“} /test-2/475worldanyoneííý“ˆý“ˆ /test-2/478worldanyone  ý“ˆOý“ˆO /test-2/272worldanyone Æ Æý“}3ý“}3 /test-2/477worldanyoneþþý“ˆAý“ˆA /test-2/271worldanyone ¾ ¾ý“}&ý“}& /test-2/274worldanyone Ú Úý“}Sý“}S /test-2/479worldanyoneý“ˆ[ý“ˆ[ /test-2/273worldanyone Ó Óý“}Cý“}C /test-2/275worldanyone ä äý“}bý“}b /test-2/276worldanyone í íý“}mý“}m /test-2/277worldanyone ø øý“}wý“}w /test-2/278worldanyone  ý“}…ý“}… /test-2/279worldanyone  ý“}”ý“}” /test-2/470worldanyone¾¾ý“‡Üý“‡Ü /test-2/471worldanyoneÈÈý“‡çý“‡ç /test-2/472worldanyoneÐÐý“‡ñý“‡ñ /test-2/109worldanyoneRRý“scý“sc /test-2/108worldanyoneGGý“sLý“sL /test-2/107worldanyone==ý“s:ý“s: /test-2/106worldanyone44ý“sý“s /test-2/105worldanyone,,ý“ròý“rò /test-2/104worldanyone  ý“rßý“rß /test-2/103worldanyoneý“rÈý“rÈ /test-2/102worldanyoneý“r´ý“r´ /test-2/99worldanyoneòòý“rtý“rt /test-2/101worldanyoneý“r ý“r  /test-2/100worldanyoneüüý“rý“r /test-2/595worldanyone··ý“™ý“™ /test-2/98worldanyoneèèý“rfý“rf /test-2/594worldanyone§§ý“ý“ /test-2/97worldanyoneÞÞý“rSý“rS /test-2/597worldanyoneÉÉý“¯ý“¯ /test-2/391worldanyoneŒŒý“ƒìý“ƒì /test-2/96worldanyoneÔÔý“r>ý“r> /test-2/596worldanyoneÀÀý“¤ý“¤ /test-2/390worldanyone€€ý“ƒßý“ƒß /test-2/95worldanyoneÈÈý“r-ý“r- /test-2/599worldanyoneÝÝý“Âý“ /test-2/393worldanyoneý“„ý“„ /test-2/94worldanyoneÀÀý“rý“r /test-2/598worldanyoneÕÕý“¹ý“¹ /test-2/392worldanyone––ý“ƒøý“ƒø /test-2/93worldanyone¶¶ý“rý“r /test-2/395worldanyoneµµý“„ý“„ /test-2/92worldanyoneªªý“qûý“qû /test-2/394worldanyone¬¬ý“„ý“„ /test-2/91worldanyone¡¡ý“qìý“qì /test-2/90worldanyone——ý“qßý“qß /test-2/294worldanyone ¨ ¨ý“~tý“~t /test-2/499worldanyone××ý“‰Qý“‰Q /test-2/293worldanyone  ý“~hý“~h /test-2/296worldanyone ¿ ¿ý“~Žý“~Ž /test-2/295worldanyone ° °ý“~€ý“~€ /test-2/496worldanyone··ý“‰#ý“‰# /test-2/290worldanyone z zý“~?ý“~? /test-2/495worldanyone­­ý“‰ý“‰ /test-2/498worldanyoneÍÍý“‰Aý“‰A /test-2/292worldanyone “ “ý“~[ý“~[ /test-2/497worldanyoneÁÁý“‰0ý“‰0 /test-2/291worldanyone ˆ ˆý“~Ný“~N /test-2/491worldanyone……ý“ˆÖý“ˆÖ /test-2/492worldanyoneý“ˆâý“ˆâ /test-2/493worldanyone™™ý“ˆóý“ˆó /test-2/494worldanyone¢¢ý“‰ý“‰ /test-2/297worldanyone Ê Êý“~œý“~œ /test-2/298worldanyone Ö Öý“~«ý“~« /test-2/299worldanyone á áý“~ºý“~º /test-2/490worldanyone{{ý“ˆÊý“ˆÊ /test-2/396worldanyone¼¼ý“„)ý“„) /test-2/397worldanyoneÇÇý“„5ý“„5 /test-2/398worldanyoneØØý“„Fý“„F /test-2/399worldanyoneßßý“„Sý“„S /test-2/590worldanyone{{ý“\ý“\ /test-2/591worldanyone††ý“hý“h /test-2/592worldanyone••ý“uý“u /test-2/88worldanyone€€ý“q¼ý“q¼ /test-2/593worldanyone¡¡ý“ý“ /test-2/89worldanyoneŠŠý“qËý“qË /test-2/240worldanyone ‰ ‰ý“{ƒý“{ƒ /test-2/241worldanyone “ “ý“{ý“{ /test-2/383worldanyone44ý“ƒˆý“ƒˆ /test-2/384worldanyone>>ý“ƒ•ý“ƒ• /test-2/381worldanyone!!ý“ƒpý“ƒp /test-2/382worldanyone--ý“ƒ~ý“ƒ~ /test-2/380worldanyoneý“ƒcý“ƒc /test-2/389worldanyonevvý“ƒÓý“ƒÓ /test-2/388worldanyonejjý“ƒÆý“ƒÆ /test-2/387worldanyone^^ý“ƒ¹ý“ƒ¹ /test-2/386worldanyoneSSý“ƒ¬ý“ƒ¬ /test-2/385worldanyoneHHý“ƒ ý“ƒ  /test-2/245worldanyone · ·ý“{¾ý“{¾ /test-2/244worldanyone ± ±ý“{±ý“{± /test-2/243worldanyone § §ý“{¥ý“{¥ /test-2/242worldanyone  ý“{›ý“{› /test-2/249worldanyone Þ Þý“|ý“| /test-2/248worldanyone Õ Õý“{ïý“{ï /test-2/247worldanyone Ê Êý“{áý“{á /test-2/246worldanyone Å Åý“{Ðý“{Ð /test-2/230worldanyone & &ý“{ ý“{ /test-2/370worldanyone¥¥ý“‚íý“‚í /test-2/371worldanyone³³ý“‚ùý“‚ù /test-2/372worldanyone½½ý“ƒý“ƒ /test-2/373worldanyoneÅÅý“ƒý“ƒ /test-2/379worldanyone  ý“ƒWý“ƒW /test-2/239worldanyone  ý“{tý“{t /test-2/378worldanyoneý“ƒKý“ƒK /test-2/375worldanyoneÚÚý“ƒ#ý“ƒ# /test-2/374worldanyoneÐÐý“ƒý“ƒ /test-2/377worldanyoneòòý“ƒ>ý“ƒ> /test-2/376worldanyoneèèý“ƒ1ý“ƒ1 /test-2/232worldanyone : :ý“{#ý“{# /test-2/231worldanyone 0 0ý“{ý“{ /test-2/234worldanyone N Ný“{6ý“{6 /test-2/233worldanyone D Dý“{,ý“{, /test-2/236worldanyone c cý“{Mý“{M /test-2/235worldanyone X Xý“{@ý“{@ /test-2/238worldanyone v vý“{gý“{g /test-2/237worldanyone n ný“{[ý“{[ /test-2/262worldanyone a aý“|¯ý“|¯ /test-2/263worldanyone k ký“|¹ý“|¹ /test-2/260worldanyone L Lý“|–ý“|– /test-2/261worldanyone V Vý“|¢ý“|¢ /test-2/361worldanyoneOOý“‚ý“‚ /test-2/362worldanyoneYYý“‚›ý“‚› /test-2/360worldanyoneGGý“‚‡ý“‚‡ /test-2/366worldanyone}}ý“‚Áý“‚Á /test-2/365worldanyonevvý“‚¹ý“‚¹ /test-2/364worldanyonellý“‚°ý“‚° /test-2/363worldanyonebbý“‚¦ý“‚¦ /test-2/369worldanyoneœœý“‚ãý“‚ã /test-2/368worldanyone‘‘ý“‚×ý“‚× /test-2/367worldanyone‡‡ý“‚Ìý“‚Ì /test-2/269worldanyone ª ªý“}ý“} /test-2/268worldanyone    ý“}ý“} /test-2/267worldanyone ” ”ý“|ñý“|ñ /test-2/266worldanyone † †ý“|áý“|á /test-2/265worldanyone } }ý“|Ñý“|Ñ /test-2/264worldanyone t tý“|Äý“|Ä /test-2/250worldanyone è èý“|ý“| /test-2/251worldanyone ò òý“|ý“| /test-2/252worldanyone ü üý“|,ý“|, /test-2/350worldanyone Ý Ýý“‚ý“‚ /test-2/351worldanyone ç çý“‚!ý“‚! /test-2/353worldanyone ü üý“‚9ý“‚9 /test-2/352worldanyone ï ïý“‚-ý“‚- /test-2/355worldanyoneý“‚Oý“‚O /test-2/354worldanyoneý“‚Cý“‚C /test-2/357worldanyone))ý“‚gý“‚g /test-2/356worldanyoneý“‚\ý“‚\ /test-2/359worldanyone;;ý“‚|ý“‚| /test-2/358worldanyone00ý“‚rý“‚r /test-2/258worldanyone 8 8ý“|{ý“|{ /test-2/257worldanyone / /ý“|oý“|o /test-2/259worldanyone B Bý“|Šý“|Š /test-2/254worldanyone  ý“|Hý“|H /test-2/253worldanyone  ý“|8ý“|8 /test-2/256worldanyone % %ý“|dý“|d /test-2/255worldanyone  ý“|Vý“|V/test-0worldanyoneý“jfý“jfÝ /test-0/338worldanyone G Gý“€‹ý“€‹ /test-0/339worldanyone S Sý“€˜ý“€˜ /test-0/332worldanyone  ý“€-ý“€- /test-0/333worldanyone  ý“€>ý“€> /test-0/159worldanyoneý“v ý“v /test-0/330worldanyone ó óý“€ý“€ /test-0/158worldanyoneý“uúý“uú /test-0/331worldanyone ý ýý“€ ý“€ /test-0/157worldanyoneý“uíý“uí /test-0/336worldanyone 3 3ý“€ný“€n /test-0/156worldanyoneýýý“ußý“uß /test-0/337worldanyone > >ý“€{ý“€{ /test-0/155worldanyoneóóý“uÓý“uÓ /test-0/334worldanyone  ý“€Oý“€O /test-0/154worldanyoneèèý“uÁý“uÁ /test-0/335worldanyone ) )ý“€^ý“€^ /test-0/601worldanyoneŸŸý“ý“ /test-0/531worldanyoneÐÐý“Šký“Šk /test-0/152worldanyoneÏÏý“uý“u /test-0/600worldanyone““ý“uý“u /test-0/530worldanyoneÊÊý“Š`ý“Š` /test-0/153worldanyoneààý“u²ý“u² /test-0/150worldanyone¼¼ý“u|ý“u| /test-0/151worldanyoneÆÆý“uý“u /test-0/535worldanyoneøøý“Š”ý“Š” /test-0/534worldanyoneññý“Š‹ý“Š‹ /test-0/533worldanyoneççý“Šý“Š /test-0/532worldanyoneÜÜý“Šuý“Šu /test-0/609worldanyoneòòý“Õý“Õ /test-0/539worldanyone""ý“ŠÀý“ŠÀ /test-0/608worldanyoneééý“Ëý“Ë /test-0/538worldanyoneý“жý“ж /test-0/607worldanyoneÜÜý“Áý“Á /test-0/537worldanyoneý“Ьý“Ь /test-0/606worldanyoneÔÔý“¹ý“¹ /test-0/536worldanyoneý“ŠŸý“ŠŸ /test-0/605worldanyoneÈÈý“®ý“® /test-0/604worldanyone¾¾ý“¤ý“¤ /test-0/603worldanyoneµµý“™ý“™ /test-0/602worldanyone©©ý“ý“ /test-0/202worldanyoneÒÒý“xòý“xò /test-0/349worldanyone ¸ ¸ý“ý“ /test-0/203worldanyoneÙÙý“xÿý“xÿ /test-0/204worldanyoneååý“yý“y /test-0/205worldanyoneììý“yý“y /test-0/200worldanyone»»ý“xÔý“xÔ /test-0/201worldanyoneÆÆý“xâý“xâ /test-0/341worldanyone h hý“€­ý“€­ /test-0/169worldanyoneý“v“ý“v“ /test-0/342worldanyone q qý“€¸ý“€¸ /test-0/343worldanyone { {ý“€Ãý“€Ã /test-0/344worldanyone † †ý“€Íý“€Í /test-0/166worldanyoneaaý“vmý“vm /test-0/206worldanyone÷÷ý“y/ý“y/ /test-0/345worldanyone ‘ ‘ý“€Ùý“€Ù /test-0/165worldanyoneWWý“v_ý“v_ /test-0/207worldanyoneÿÿý“yDý“yD /test-0/346worldanyone š šý“€ãý“€ã /test-0/168worldanyonevvý“v†ý“v† /test-0/208worldanyone  ý“yUý“yU /test-0/347worldanyone ¥ ¥ý“€ìý“€ì /test-0/167worldanyonellý“vzý“vz /test-0/209worldanyoneý“yfý“yf /test-0/348worldanyone ® ®ý“€öý“€ö /test-0/540worldanyone,,ý“ŠÉý“ŠÉ /test-0/409worldanyoneý“„”ý“„” /test-0/161worldanyone..ý“v-ý“v- /test-0/3worldanyoneý“j˜ý“j˜ /test-0/408worldanyone  ý“„‡ý“„‡ /test-0/162worldanyone88ý“v9ý“v9 /test-0/2worldanyone  ý“jŒý“jŒ /test-0/542worldanyone@@ý“ŠÝý“ŠÝ /test-0/163worldanyoneBBý“vBý“vB /test-0/1worldanyoneý“j~ý“j~ /test-0/541worldanyone66ý“ŠÒý“ŠÒ /test-0/164worldanyoneKKý“vQý“vQ /test-0/0worldanyoneý“jsý“js /test-0/544worldanyoneTTý“Šïý“Šï /test-0/405worldanyoneêêý“„aý“„a /test-0/7worldanyone$$ý“jÒý“jÒ /test-0/543worldanyoneJJý“Šæý“Šæ /test-0/404worldanyoneááý“„Tý“„T /test-0/6worldanyoneý“jÃý“jà /test-0/546worldanyoneiiý“‹ý“‹ /test-0/407worldanyoneÿÿý“„zý“„z /test-0/5worldanyoneý“j³ý“j³ /test-0/340worldanyone \ \ý“€¢ý“€¢ /test-0/545worldanyone]]ý“Šùý“Šù /test-0/406worldanyoneõõý“„mý“„m /test-0/160worldanyone##ý“vý“v /test-0/4worldanyoneý“j§ý“j§ /test-0/548worldanyone~~ý“‹ý“‹ /test-0/401worldanyoneÅÅý“„0ý“„0 /test-0/547worldanyoneppý“‹ý“‹ /test-0/400worldanyone»»ý“„'ý“„' /test-0/403worldanyoneÙÙý“„Gý“„G /test-0/9worldanyone33ý“k ý“k /test-0/549worldanyone††ý“‹)ý“‹) /test-0/402worldanyoneÏÏý“„=ý“„= /test-0/8worldanyone))ý“jíý“jí /test-0/731worldanyoneççý“•¼ý“•¼ /test-0/730worldanyoneÖÖý“•§ý“•§ /test-0/732worldanyoneññý“•Ëý“•Ë /test-0/211worldanyone,,ý“yý“y /test-0/212worldanyone==ý“y¤ý“y¤ /test-0/210worldanyoneý“yuý“yu /test-0/215worldanyone]]ý“yÙý“yÙ /test-0/318worldanyone x xý“yý“y /test-0/216worldanyonehhý“yêý“yê /test-0/319worldanyone  ý“†ý“† /test-0/213worldanyoneGGý“y·ý“y· /test-0/316worldanyone b bý“`ý“` /test-0/214worldanyoneSSý“yËý“yË /test-0/317worldanyone l lý“jý“j /test-0/179worldanyoneääý“wý“w /test-0/219worldanyone††ý“zý“z /test-0/314worldanyone N Ný“Lý“L /test-0/178worldanyoneÚÚý“wý“w /test-0/315worldanyone X Xý“Vý“V /test-0/177worldanyoneÓÓý“vôý“vô /test-0/217worldanyoneooý“y÷ý“y÷ /test-0/312worldanyone : :ý“1ý“1 /test-0/176worldanyoneÆÆý“våý“vå /test-0/218worldanyoneyyý“zý“z /test-0/313worldanyone F Fý“>ý“> /test-0/310worldanyone % %ý“ý“ /test-0/311worldanyone 0 0ý“$ý“$ /test-0/513worldanyoneý“‰¡ý“‰¡ /test-0/418worldanyoneppý“…ý“… /test-0/170worldanyoneý“v¡ý“v¡ /test-0/729worldanyoneËËý“•šý“•š /test-0/512worldanyoneý“‰”ý“‰” /test-0/417worldanyonehhý“„ýý“„ý /test-0/171worldanyone’’ý“v«ý“v« /test-0/728worldanyone¿¿ý“•Šý“•Š /test-0/511worldanyoneý“‰‡ý“‰‡ /test-0/416worldanyone^^ý“„òý“„ò /test-0/727worldanyoneµµý“•}ý“•} /test-0/510worldanyoneùùý“‰{ý“‰{ /test-0/415worldanyoneSSý“„Úý“„Ú /test-0/726worldanyone­­ý“•sý“•s /test-0/623worldanyone††ý“Žný“Žn /test-0/174worldanyone¯¯ý“vÍý“vÍ /test-0/725worldanyone££ý“•eý“•e /test-0/622worldanyone||ý“Žeý“Že /test-0/175worldanyoneººý“vØý“vØ /test-0/724worldanyone––ý“•Rý“•R /test-0/621worldanyonerrý“ŽZý“ŽZ /test-0/172worldanyoneœœý“v¶ý“v¶ /test-0/723worldanyoneŽŽý“•Eý“•E /test-0/620worldanyoneggý“ŽMý“ŽM /test-0/419worldanyone{{ý“…ý“… /test-0/173worldanyone¦¦ý“vÁý“vÁ /test-0/722worldanyone„„ý“•9ý“•9 /test-0/627worldanyone¯¯ý“ŽÄý“ŽÄ /test-0/410worldanyone##ý“„¡ý“„¡ /test-0/721worldanyone{{ý“•/ý“•/ /test-0/626worldanyone§§ý“޹ý“޹ /test-0/720worldanyoneqqý“•$ý“•$ /test-0/625worldanyone™™ý“Ž«ý“Ž« /test-0/519worldanyoneSSý“‰äý“‰ä /test-0/624worldanyoneý“Žxý“Žx /test-0/518worldanyoneIIý“‰Ûý“‰Û /test-0/517worldanyone>>ý“‰Ñý“‰Ñ /test-0/414worldanyoneIIý“„Ñý“„Ñ /test-0/516worldanyone66ý“‰Äý“‰Ä /test-0/413worldanyoneBBý“„Æý“„Æ /test-0/629worldanyoneÈÈý“Žßý“Žß /test-0/515worldanyone++ý“‰¹ý“‰¹ /test-0/412worldanyone77ý“„ºý“„º /test-0/628worldanyone¸¸ý“ŽÐý“ŽÐ /test-0/514worldanyone&&ý“‰®ý“‰® /test-0/411worldanyone,,ý“„®ý“„® /test-0/220worldanyone““ý“z0ý“z0 /test-0/221worldanyoneœœý“z@ý“z@ /test-0/222worldanyone££ý“zSý“zS /test-0/223worldanyone­­ý“zaý“za /test-0/224worldanyone¸¸ý“zsý“zs /test-0/327worldanyone × ×ý“òý“ò /test-0/225worldanyoneÂÂý“zý“z /test-0/328worldanyone à àý“€ý“€ /test-0/226worldanyoneÎÎý“z‹ý“z‹ /test-0/329worldanyone é éý“€ ý“€ /test-0/227worldanyoneÖÖý“z•ý“z• /test-0/188worldanyoneBBý“x*ý“x* /test-0/228worldanyoneááý“z¢ý“z¢ /test-0/323worldanyone ® ®ý“¿ý“¿ /test-0/187worldanyone99ý“xý“x /test-0/229worldanyoneììý“z®ý“z® /test-0/324worldanyone ¸ ¸ý“Ëý“Ë /test-0/325worldanyone Á Áý“×ý“× /test-0/189worldanyoneLLý“x6ý“x6 /test-0/326worldanyone Ì Ìý“åý“å /test-0/709worldanyone  ý“”¬ý“”¬ /test-0/320worldanyone ‹ ‹ý“”ý“” /test-0/321worldanyone “ “ý“¡ý“¡ /test-0/322worldanyone ¤ ¤ý“±ý“± /test-0/717worldanyoneVVý“•ý“• /test-0/522worldanyonemmý“Šý“Š /test-0/427worldanyoneÄÄý“…wý“…w /test-0/716worldanyoneJJý“”òý“”ò /test-0/521worldanyoneggý“‰øý“‰ø /test-0/426worldanyone¹¹ý“…iý“…i /test-0/180worldanyoneííý“wý“w /test-0/719worldanyonehhý“•ý“• /test-0/524worldanyoneƒƒý“Šý“Š /test-0/429worldanyoneÝÝý“…”ý“…” /test-0/181worldanyoneüüý“w)ý“w) /test-0/718worldanyone^^ý“• ý“• /test-0/523worldanyoneyyý“Š ý“Š /test-0/428worldanyoneÎÎý“…‚ý“…‚ /test-0/182worldanyoneý“w8ý“w8 /test-0/713worldanyone..ý“”Ñý“”Ñ /test-0/610worldanyoneûûý“àý“à /test-0/183worldanyoneý“wDý“wD /test-0/712worldanyone''ý“”Èý“”È /test-0/184worldanyoneý“wQý“wQ /test-0/715worldanyone@@ý“”çý“”ç /test-0/612worldanyoneý“ôý“ô /test-0/520worldanyone\\ý“‰íý“‰í /test-0/185worldanyone((ý“wcý“wc /test-0/714worldanyone88ý“”Üý“”Ü /test-0/611worldanyoneý“ëý“ë /test-0/186worldanyone++ý“wný“wn /test-0/614worldanyone%%ý“Ž ý“Ž /test-0/613worldanyoneý“ýý“ý /test-0/529worldanyone¾¾ý“ŠSý“ŠS /test-0/711worldanyoneý“”¿ý“”¿ /test-0/616worldanyone88ý“Žý“Ž /test-0/421worldanyoneý“…)ý“…) /test-0/710worldanyoneý“”µý“”µ /test-0/615worldanyone..ý“Žý“Ž /test-0/420worldanyone††ý“…ý“… /test-0/618worldanyoneNNý“Ž5ý“Ž5 /test-0/526worldanyoneššý“Š/ý“Š/ /test-0/423worldanyone¡¡ý“…=ý“…= /test-0/617worldanyoneBBý“Ž(ý“Ž( /test-0/525worldanyoneŽŽý“Š"ý“Š" /test-0/422worldanyone——ý“…3ý“…3 /test-0/528worldanyone¯¯ý“ŠGý“ŠG /test-0/425worldanyone±±ý“…Wý“…W /test-0/619worldanyone]]ý“ŽAý“ŽA /test-0/527worldanyone¤¤ý“Š;ý“Š; /test-0/424worldanyone««ý“…Fý“…F /test-0/116worldanyoneddý“sxý“sx /test-0/117worldanyonejjý“s‰ý“s‰ /test-0/114worldanyoneNNý“sSý“sS /test-0/115worldanyoneXXý“shý“sh /test-0/571worldanyonenný“ŒFý“ŒF /test-0/112worldanyone77ý“s ý“s /test-0/570worldanyoneddý“Œ:ý“Œ: /test-0/113worldanyoneAAý“s=ý“s= /test-0/110worldanyone((ý“ræý“ræ /test-0/111worldanyone//ý“røý“rø /test-0/118worldanyonessý“sšý“sš /test-0/119worldanyone}}ý“s¨ý“s¨ /test-0/435worldanyoneý“…ëý“…ë /test-0/436worldanyone""ý“…÷ý“…÷ /test-0/433worldanyoneý“…Òý“…Ò /test-0/434worldanyone  ý“…Þý“…Þ /test-0/431worldanyoneììý“…¬ý“…¬ /test-0/700worldanyone²²ý“”Rý“”R /test-0/432worldanyoneúúý“…Äý“…Ä /test-0/430worldanyoneççý“… ý“…  /test-0/703worldanyoneÎÎý“”lý“”l /test-0/574worldanyoneŒŒý“Œbý“Œb /test-0/704worldanyoneØØý“”vý“”v /test-0/575worldanyone––ý“Œlý“Œl /test-0/701worldanyone¼¼ý“”Zý“”Z /test-0/572worldanyonexxý“ŒPý“ŒP /test-0/702worldanyoneÆÆý“”cý“”c /test-0/573worldanyone‚‚ý“ŒYý“ŒY /test-0/707worldanyoneööý“”•ý“”• /test-0/578worldanyone´´ý“ŒŒý“ŒŒ /test-0/439worldanyone@@ý“†ý“† /test-0/708worldanyoneÿÿý“”¡ý“”¡ /test-0/579worldanyone¿¿ý“Œ–ý“Œ– /test-0/705worldanyoneââý“”€ý“”€ /test-0/576worldanyone  ý“Œvý“Œv /test-0/437worldanyone..ý“†ý“† /test-0/706worldanyoneììý“”Šý“”Š /test-0/577worldanyone¨¨ý“Œ€ý“Œ€ /test-0/438worldanyone66ý“†ý“† /test-0/125worldanyone¼¼ý“sýý“sý /test-0/126worldanyoneÅÅý“t ý“t /test-0/127worldanyoneÑÑý“tý“t /test-0/300worldanyone à Ãý“~•ý“~• /test-0/128worldanyoneÜÜý“t,ý“t, /test-0/580worldanyoneÉÉý“Œ ý“Œ  /test-0/121worldanyone““ý“sÇý“sÇ /test-0/302worldanyone × ×ý“~«ý“~« /test-0/122worldanyoneœœý“sÓý“sÓ /test-0/301worldanyone Í Íý“~žý“~ž /test-0/582worldanyoneÛÛý“Œ´ý“Œ´ /test-0/123worldanyone§§ý“sâý“sâ /test-0/304worldanyone è èý“~Éý“~É /test-0/581worldanyoneÓÓý“Œªý“Œª /test-0/124worldanyone²²ý“sïý“sï /test-0/303worldanyone ß ßý“~¹ý“~¹ /test-0/306worldanyone ÿ ÿý“~èý“~è /test-0/305worldanyone ò òý“~Ùý“~Ù /test-0/308worldanyone  ý“ý“ /test-0/307worldanyone  ý“~õý“~õ /test-0/129worldanyoneææý“t:ý“t: /test-0/309worldanyone  ý“ý“ /test-0/444worldanyoneuuý“†Zý“†Z /test-0/445worldanyone{{ý“†gý“†g /test-0/446worldanyoneˆˆý“†wý“†w /test-0/447worldanyone““ý“†…ý“†… /test-0/440worldanyonePPý“†+ý“†+ /test-0/441worldanyoneXXý“†:ý“†: /test-0/442worldanyonebbý“†Eý“†E /test-0/443worldanyonekký“†Pý“†P /test-0/583worldanyoneèèý“ŒÀý“ŒÀ /test-0/584worldanyoneóóý“ŒÌý“ŒÌ /test-0/585worldanyoneÿÿý“Œ×ý“Œ× /test-0/120worldanyone††ý“s¶ý“s¶ /test-0/586worldanyoneý“Œâý“Œâ /test-0/587worldanyoneý“Œíý“Œí /test-0/448worldanyoneý“†’ý“†’ /test-0/588worldanyoneý“Œ÷ý“Œ÷ /test-0/449worldanyone©©ý“† ý“†  /test-0/589worldanyone((ý“ý“ /test-0/134worldanyoneý“t‘ý“t‘ /test-0/135worldanyone))ý“t£ý“t£ /test-0/132worldanyoneý“tjý“tj /test-0/133worldanyoneý“t{ý“t{ /test-0/138worldanyoneEEý“tÉý“tÉ /test-0/139worldanyoneQQý“tÖý“tÖ /test-0/136worldanyone44ý“t±ý“t± /test-0/137worldanyone::ý“t¼ý“t¼ /test-0/450worldanyone³³ý“†«ý“†« /test-0/453worldanyoneÍÍý“†Òý“†Ò /test-0/454worldanyoneÖÖý“†Þý“†Þ /test-0/451worldanyone¾¾ý“†»ý“†» /test-0/452worldanyoneÅÅý“†Çý“†Ç /test-0/457worldanyoneôôý“†üý“†ü /test-0/458worldanyoneþþý“‡ý“‡ /test-0/558worldanyoneççý“‹³ý“‹³ /test-0/455worldanyoneààý“†çý“†ç /test-0/559worldanyoneððý“‹¿ý“‹¿ /test-0/456worldanyoneêêý“†ðý“†ð /test-0/556worldanyoneÏÏý“‹›ý“‹› /test-0/557worldanyoneØØý“‹¦ý“‹¦ /test-0/554worldanyone»»ý“‹_ý“‹_ /test-0/459worldanyoneý“‡ý“‡ /test-0/555worldanyoneÃÃý“‹Žý“‹Ž /test-0/552worldanyone¥¥ý“‹Hý“‹H /test-0/131worldanyoneûûý“tYý“tY /test-0/553worldanyone®®ý“‹Sý“‹S /test-0/130worldanyoneòòý“tHý“tH /test-0/550worldanyone‘‘ý“‹3ý“‹3 /test-0/551worldanyoneœœý“‹=ý“‹= /test-0/143worldanyoneyyý“uý“u /test-0/144worldanyone……ý“u(ý“u( /test-0/560worldanyoneúúý“‹Êý“‹Ê /test-0/145worldanyoneŽŽý“u3ý“u3 /test-0/146worldanyone––ý“uFý“uF /test-0/147worldanyoneŸŸý“uSý“uS /test-0/148worldanyone©©ý“uaý“ua /test-0/149worldanyone³³ý“umý“um /test-0/461worldanyoneý“‡(ý“‡( /test-0/460worldanyoneý“‡ý“‡ /test-0/462worldanyone##ý“‡3ý“‡3 /test-0/463worldanyone--ý“‡>ý“‡> /test-0/464worldanyone77ý“‡Iý“‡I /test-0/465worldanyoneAAý“‡Sý“‡S /test-0/569worldanyone^^ý“Œ0ý“Œ0 /test-0/466worldanyoneJJý“‡\ý“‡\ /test-0/467worldanyoneUUý“‡gý“‡g /test-0/468worldanyone^^ý“‡qý“‡q /test-0/469worldanyoneggý“‡|ý“‡| /test-0/565worldanyone..ý“‹ÿý“‹ÿ /test-0/566worldanyone77ý“Œ ý“Œ /test-0/567worldanyoneFFý“Œý“Œ /test-0/568worldanyoneNNý“Œ$ý“Œ$ /test-0/561worldanyoneý“‹Ôý“‹Ô /test-0/140worldanyoneYYý“täý“tä /test-0/562worldanyoneý“‹Ýý“‹Ý /test-0/563worldanyoneý“‹éý“‹é /test-0/142worldanyoneqqý“uý“u /test-0/564worldanyone$$ý“‹ôý“‹ô /test-0/141worldanyoneddý“tïý“tï /test-0/687worldanyone--ý“‘ýý“‘ý /test-0/686worldanyone##ý“‘òý“‘ò /test-0/689worldanyoneBBý“’ý“’ /test-0/688worldanyone77ý“’ý“’ /test-0/683worldanyone  ý“‘Ïý“‘Ï /test-0/682worldanyoneÿÿý“‘¿ý“‘¿ /test-0/685worldanyoneý“‘äý“‘ä /test-0/684worldanyoneý“‘Úý“‘Ú /test-0/680worldanyoneææý“‘¦ý“‘¦ /test-0/681worldanyoneóóý“‘²ý“‘² /test-0/678worldanyoneÐÐý“‘ý“‘ /test-0/677worldanyoneÅÅý“‘„ý“‘„ /test-0/676worldanyone½½ý“‘zý“‘z /test-0/675worldanyone°°ý“‘pý“‘p /test-0/674worldanyone¨¨ý“‘fý“‘f /test-0/673worldanyonežžý“‘Xý“‘X /test-0/672worldanyone““ý“‘Ký“‘K /test-0/671worldanyone‡‡ý“‘?ý“‘? /test-0/679worldanyoneÜÜý“‘›ý“‘› /test-0/670worldanyoneyyý“‘3ý“‘3 /test-0/696worldanyone‰‰ý“’\ý“’\ /test-0/695worldanyone}}ý“’Pý“’P /test-0/694worldanyonessý“’Fý“’F /test-0/693worldanyonejjý“’<ý“’< /test-0/699worldanyone¨¨ý“”Iý“”I /test-0/698worldanyonežžý“”8ý“”8 /test-0/697worldanyone••ý“”,ý“”, /test-0/691worldanyoneUUý“’$ý“’$ /test-0/692worldanyone__ý“’0ý“’0 /test-0/690worldanyoneKKý“’ý“’ /test-0/646worldanyonettý“®ý“® /test-0/647worldanyone€€ý“Ãý“à /test-0/648worldanyoneŠŠý“Òý“Ò /test-0/649worldanyone””ý“Üý“Ü /test-0/642worldanyoneKKý“uý“u /test-0/195worldanyoneŽŽý“x“ý“x“ /test-0/643worldanyoneVVý“‚ý“‚ /test-0/194worldanyoneƒƒý“x€ý“x€ /test-0/644worldanyone``ý“’ý“’ /test-0/197worldanyone££ý“x°ý“x° /test-0/645worldanyoneiiý“ý“ /test-0/196worldanyone™™ý“x¡ý“x¡ /test-0/191worldanyone``ý“xQý“xQ /test-0/190worldanyoneVVý“xEý“xE /test-0/640worldanyone44ý“\ý“\ /test-0/193worldanyonezzý“xrý“xr /test-0/641worldanyoneCCý“iý“i /test-0/192worldanyonenný“xaý“xa /test-0/198worldanyone­­ý“x¾ý“x¾ /test-0/199worldanyone··ý“xÈý“xÈ /test-0/505worldanyoneÉÉý“‰?ý“‰? /test-0/506worldanyoneÓÓý“‰Oý“‰O /test-0/639worldanyone))ý“Ný“N /test-0/503worldanyone¹¹ý“‰%ý“‰% /test-0/504worldanyoneÀÀý“‰0ý“‰0 /test-0/637worldanyoneý“4ý“4 /test-0/509worldanyoneññý“‰oý“‰o /test-0/638worldanyoneý“Aý“A /test-0/635worldanyoneý“#ý“# /test-0/507worldanyoneÝÝý“‰]ý“‰] /test-0/636worldanyone  ý“,ý“, /test-0/508worldanyoneççý“‰gý“‰g /test-0/633worldanyoneîîý“ý“ /test-0/634worldanyoneøøý“ý“ /test-0/631worldanyoneßßý“Žøý“Žø /test-0/632worldanyoneååý“ý“ /test-0/501worldanyone££ý“‰ý“‰ /test-0/630worldanyoneÓÓý“Žëý“Žë /test-0/502worldanyone±±ý“‰ý“‰ /test-0/500worldanyoneššý“ˆóý“ˆó /test-0/668worldanyoneddý“‘ý“‘ /test-0/669worldanyoneppý“‘(ý“‘( /test-0/660worldanyoneý“¸ý“¸ /test-0/661worldanyoneý“Çý“Ç /test-0/662worldanyone  ý“Óý“Ó /test-0/663worldanyone))ý“Þý“Þ /test-0/664worldanyone44ý“éý“é /test-0/665worldanyoneCCý“÷ý“÷ /test-0/666worldanyoneOOý“‘ý“‘ /test-0/667worldanyoneWWý“‘ý“‘ /test-0/659worldanyoneüüý“ªý“ª /test-0/657worldanyoneééý“‘ý“‘ /test-0/658worldanyoneóóý“ý“ /test-0/651worldanyone§§ý“ðý“ð /test-0/652worldanyone²²ý“þý“þ /test-0/650worldanyonežžý“çý“ç /test-0/655worldanyoneÓÓý“wý“w /test-0/656worldanyoneÝÝý“ý“ /test-0/653worldanyoneººý“ ý“ /test-0/654worldanyoneÇÇý“ý“ /test-0/35worldanyone55ý“m(ý“m( /test-0/36worldanyoneAAý“m@ý“m@ /test-0/33worldanyone!!ý“mý“m /test-0/34worldanyone++ý“mý“m /test-0/39worldanyonebbý“m|ý“m| /test-0/37worldanyoneLLý“mTý“mT /test-0/38worldanyoneVVý“mgý“mg /test-0/43worldanyone‹‹ý“mÂý“m /test-0/42worldanyone€€ý“m±ý“m± /test-0/41worldanyoneuuý“m¤ý“m¤ /test-0/40worldanyonellý“m“ý“m“ /test-0/22worldanyone¶¶ý“l<ý“l< /test-0/23worldanyone¾¾ý“lJý“lJ /test-0/24worldanyoneÈÈý“l]ý“l] /test-0/25worldanyoneÖÖý“ltý“lt /test-0/26worldanyoneÜÜý“l„ý“l„ /test-0/27worldanyoneææý“l“ý“l“ /test-0/28worldanyoneððý“lŸý“lŸ /test-0/29worldanyoneúúý“l°ý“l° /test-0/30worldanyoneý“lÃý“là /test-0/32worldanyoneý“lñý“lñ /test-0/31worldanyone  ý“lÙý“lÙ /test-0/19worldanyone––ý“k÷ý“k÷ /test-0/17worldanyone‚‚ý“kÓý“kÓ /test-0/18worldanyoneŒŒý“këý“kë /test-0/15worldanyonenný“k³ý“k³ /test-0/16worldanyonexxý“kÄý“kÄ /test-0/13worldanyoneZZý“kzý“kz /test-0/14worldanyone``ý“k—ý“k— /test-0/11worldanyoneHHý“kGý“kG /test-0/12worldanyoneQQý“k`ý“k` /test-0/21worldanyoneªªý“l&ý“l& /test-0/20worldanyone¡¡ý“lý“l /test-0/10worldanyone<<ý“k&ý“k& /test-0/79worldanyoneûûý“p•ý“p• /test-0/78worldanyoneññý“p†ý“p† /test-0/77worldanyoneèèý“pvý“pv /test-0/82worldanyoneý“q5ý“q5 /test-0/83worldanyone((ý“qCý“qC /test-0/80worldanyoneý“p¤ý“p¤ /test-0/81worldanyoneý“q%ý“q% /test-0/86worldanyoneDDý“qoý“qo /test-0/87worldanyoneNNý“qzý“qz /test-0/84worldanyone22ý“qQý“qQ /test-0/85worldanyone==ý“q_ý“q_ /test-0/67worldanyone{{ý“oÀý“oÀ /test-0/66worldanyoneqqý“o±ý“o± /test-0/69worldanyoneŽŽý“oàý“oà /test-0/68worldanyone„„ý“oÐý“oÐ /test-0/70worldanyone¡¡ý“oøý“oø /test-0/71worldanyone¨¨ý“pý“p /test-0/72worldanyoneµµý“pý“p /test-0/73worldanyone¼¼ý“p,ý“p, /test-0/74worldanyoneÊÊý“pBý“pB /test-0/75worldanyoneÕÕý“pRý“pR /test-0/76worldanyoneÝÝý“pcý“pc /test-0/59worldanyone44ý“o(ý“o( /test-0/58worldanyone,,ý“oý“o /test-0/57worldanyone""ý“nûý“nû /test-0/56worldanyoneý“nåý“nå /test-0/55worldanyoneý“nÏý“nÏ /test-0/64worldanyone``ý“oý“o /test-0/65worldanyonellý“oŸý“oŸ /test-0/62worldanyoneLLý“ojý“oj /test-0/63worldanyoneVVý“o{ý“o{ /test-0/60worldanyone>>ý“o7ý“o7 /test-0/61worldanyoneBBý“oFý“oF /test-0/49worldanyoneÆÆý“n@ý“n@ /test-0/48worldanyone¼¼ý“n/ý“n/ /test-0/45worldanyone››ý“mòý“mò /test-0/44worldanyone’’ý“màý“mà /test-0/47worldanyone±±ý“ný“n /test-0/46worldanyone££ý“ný“n /test-0/51worldanyoneààý“ngý“ng /test-0/52worldanyoneììý“n€ý“n€ /test-0/53worldanyoneõõý“n•ý“n• /test-0/54worldanyoneý“nµý“nµ /test-0/50worldanyoneÑÑý“nPý“nP /test-0/487worldanyoneý“ˆeý“ˆe /test-0/281worldanyone  ý“}…ý“}… /test-0/486worldanyoneý“ˆ[ý“ˆ[ /test-0/280worldanyone ÷ ÷ý“}wý“}w /test-0/485worldanyoneý“ˆOý“ˆO /test-0/484worldanyoneÿÿý“ˆBý“ˆB /test-0/285worldanyone $ $ý“}Àý“}À /test-0/284worldanyone  ý“}´ý“}´ /test-0/489worldanyone//ý“ˆwý“ˆw /test-0/283worldanyone  ý“}£ý“}£ /test-0/488worldanyone%%ý“ˆný“ˆn /test-0/282worldanyone  ý“}’ý“}’ /test-0/288worldanyone G Gý“}þý“}þ /test-0/289worldanyone Q Qý“~ ý“~ /test-0/286worldanyone 3 3ý“}Íý“}Í /test-0/287worldanyone = =ý“}ñý“}ñ /test-0/482worldanyoneççý“ˆ ý“ˆ /test-0/483worldanyoneòòý“ˆ4ý“ˆ4 /test-0/480worldanyoneÕÕý“‡õý“‡õ /test-0/481worldanyoneààý“‡ÿý“‡ÿ /test-0/474worldanyone˜˜ý“‡µý“‡µ /test-0/473worldanyoneý“‡«ý“‡« /test-0/476worldanyone««ý“‡Êý“‡Ê /test-0/270worldanyone “ “ý“|ðý“|ð /test-0/475worldanyone¢¢ý“‡Áý“‡Á /test-0/478worldanyoneÀÀý“‡áý“‡á /test-0/272worldanyone © ©ý“} ý“} /test-0/477worldanyone¶¶ý“‡Õý“‡Õ /test-0/271worldanyone Ÿ Ÿý“}ý“} /test-0/274worldanyone ¼ ¼ý“}%ý“}% /test-0/479worldanyoneËËý“‡ëý“‡ë /test-0/273worldanyone ´ ´ý“}ý“} /test-0/275worldanyone Å Åý“}2ý“}2 /test-0/276worldanyone Ò Òý“}Cý“}C /test-0/277worldanyone Ù Ùý“}Sý“}S /test-0/278worldanyone ã ãý“}bý“}b /test-0/279worldanyone î îý“}mý“}m /test-0/470worldanyoneqqý“‡‡ý“‡‡ /test-0/471worldanyone{{ý“‡“ý“‡“ /test-0/472worldanyoneƒƒý“‡ ý“‡  /test-0/109worldanyoneý“rÒý“rÒ /test-0/108worldanyoneý“r¿ý“r¿ /test-0/107worldanyone  ý“r®ý“r® /test-0/106worldanyoneÿÿý“r“ý“r“ /test-0/105worldanyoneúúý“r…ý“r… /test-0/104worldanyoneððý“rqý“rq /test-0/103worldanyoneââý“rXý“rX /test-0/102worldanyoneÕÕý“rAý“rA /test-0/99worldanyone··ý“rý“r /test-0/101worldanyoneÎÎý“r1ý“r1 /test-0/100worldanyoneÆÆý“r$ý“r$ /test-0/595worldanyone]]ý“<ý“< /test-0/98worldanyone°°ý“rý“r /test-0/594worldanyoneVVý“1ý“1 /test-0/97worldanyone¨¨ý“qöý“qö /test-0/597worldanyonessý“Sý“S /test-0/391worldanyone__ý“ƒºý“ƒº /test-0/96worldanyonežžý“qêý“qê /test-0/596worldanyonellý“Hý“H /test-0/390worldanyoneWWý“ƒ­ý“ƒ­ /test-0/95worldanyone””ý“qÞý“qÞ /test-0/599worldanyoneˆˆý“iý“i /test-0/393worldanyoneyyý“ƒÓý“ƒÓ /test-0/94worldanyoneý“qÍý“qÍ /test-0/598worldanyone~~ý“^ý“^ /test-0/392worldanyoneiiý“ƒÆý“ƒÆ /test-0/93worldanyoneƒƒý“qÀý“qÀ /test-0/395worldanyoneý“ƒìý“ƒì /test-0/92worldanyonezzý“q´ý“q´ /test-0/394worldanyoneƒƒý“ƒàý“ƒà /test-0/91worldanyoneppý“qªý“qª /test-0/90worldanyoneffý“qý“q /test-0/294worldanyone ‡ ‡ý“~Ný“~N /test-0/499worldanyoneý“ˆàý“ˆà /test-0/293worldanyone x xý“~?ý“~? /test-0/296worldanyone ž žý“~hý“~h /test-0/295worldanyone ” ”ý“~[ý“~[ /test-0/496worldanyoneppý“ˆ½ý“ˆ½ /test-0/290worldanyone ^ ^ý“~ý“~ /test-0/495worldanyoneffý“ˆµý“ˆµ /test-0/498worldanyoneƒƒý“ˆÐý“ˆÐ /test-0/292worldanyone t tý“~4ý“~4 /test-0/497worldanyonezzý“ˆÆý“ˆÆ /test-0/291worldanyone d dý“~&ý“~& /test-0/491worldanyoneCCý“ˆŠý“ˆŠ /test-0/492worldanyoneMMý“ˆ•ý“ˆ• /test-0/493worldanyoneRRý“ˆžý“ˆž /test-0/494worldanyone\\ý“ˆªý“ˆª /test-0/297worldanyone § §ý“~tý“~t /test-0/298worldanyone ¯ ¯ý“~€ý“~€ /test-0/299worldanyone ¹ ¹ý“~Œý“~Œ /test-0/490worldanyone99ý“ˆý“ˆ /test-0/396worldanyone——ý“ƒøý“ƒø /test-0/397worldanyoneœœý“„ý“„ /test-0/398worldanyone¦¦ý“„ý“„ /test-0/399worldanyone±±ý“„ý“„ /test-0/590worldanyone..ý“ ý“ /test-0/591worldanyone88ý“ý“ /test-0/592worldanyoneBBý“ý“ /test-0/88worldanyoneXXý“qƒý“qƒ /test-0/593worldanyoneLLý“&ý“& /test-0/89worldanyone^^ý“q‘ý“q‘ /test-0/240worldanyone \ \ý“{Gý“{G /test-0/241worldanyone e eý“{Pý“{P /test-0/383worldanyoneý“ƒbý“ƒb /test-0/384worldanyone""ý“ƒpý“ƒp /test-0/381worldanyoneþþý“ƒJý“ƒJ /test-0/382worldanyone  ý“ƒWý“ƒW /test-0/380worldanyoneõõý“ƒ?ý“ƒ? /test-0/389worldanyoneNNý“ƒ¤ý“ƒ¤ /test-0/388worldanyoneDDý“ƒ›ý“ƒ› /test-0/387worldanyone::ý“ƒ’ý“ƒ’ /test-0/386worldanyone00ý“ƒ‡ý“ƒ‡ /test-0/385worldanyone&&ý“ƒ{ý“ƒ{ /test-0/245worldanyone  ý“{„ý“{„ /test-0/244worldanyone € €ý“{uý“{u /test-0/243worldanyone x xý“{hý“{h /test-0/242worldanyone o oý“{\ý“{\ /test-0/249worldanyone ¸ ¸ý“{¾ý“{¾ /test-0/248worldanyone ° °ý“{°ý“{° /test-0/247worldanyone ¥ ¥ý“{¡ý“{¡ /test-0/246worldanyone š šý“{’ý“{’ /test-0/230worldanyoneóóý“z¸ý“z¸ /test-0/370worldanyoneý“‚Ôý“‚Ô /test-0/371worldanyone——ý“‚ßý“‚ß /test-0/372worldanyone¡¡ý“‚èý“‚è /test-0/373worldanyone««ý“‚ñý“‚ñ /test-0/379worldanyoneëëý“ƒ2ý“ƒ2 /test-0/239worldanyone R Rý“{=ý“{= /test-0/378worldanyoneÛÛý“ƒ#ý“ƒ# /test-0/375worldanyone¿¿ý“ƒý“ƒ /test-0/374worldanyoneµµý“‚ûý“‚û /test-0/377worldanyoneÑÑý“ƒý“ƒ /test-0/376worldanyoneÆÆý“ƒý“ƒ /test-0/232worldanyone  ý“zãý“zã /test-0/231worldanyoneýýý“zÀý“zÀ /test-0/234worldanyone  ý“zýý“zý /test-0/233worldanyone  ý“zðý“zð /test-0/236worldanyone 2 2ý“{ý“{ /test-0/235worldanyone + +ý“{ ý“{ /test-0/238worldanyone H Hý“{2ý“{2 /test-0/237worldanyone > >ý“{&ý“{& /test-0/262worldanyone : :ý“|}ý“|} /test-0/263worldanyone F Fý“|Žý“|Ž /test-0/260worldanyone & &ý“|fý“|f /test-0/261worldanyone 0 0ý“|rý“|r /test-0/361worldanyone22ý“‚sý“‚s /test-0/362worldanyone==ý“‚}ý“‚} /test-0/360worldanyone''ý“‚fý“‚f /test-0/366worldanyoneccý“‚¦ý“‚¦ /test-0/365worldanyoneXXý“‚›ý“‚› /test-0/364worldanyonePPý“‚‘ý“‚‘ /test-0/363worldanyoneFFý“‚‡ý“‚‡ /test-0/369worldanyone„„ý“‚Êý“‚Ê /test-0/368worldanyoneyyý“‚¾ý“‚¾ /test-0/367worldanyonenný“‚±ý“‚± /test-0/269worldanyone ‡ ‡ý“|áý“|á /test-0/268worldanyone { {ý“|Ñý“|Ñ /test-0/267worldanyone p pý“|Ãý“|à /test-0/266worldanyone d dý“|³ý“|³ /test-0/265worldanyone Y Yý“|¦ý“|¦ /test-0/264worldanyone O Oý“|›ý“|› /test-0/250worldanyone Æ Æý“{Ðý“{Ð /test-0/251worldanyone Ì Ìý“{áý“{á /test-0/252worldanyone Ô Ôý“{ïý“{ï /test-0/350worldanyone à Ãý“ ý“ /test-0/351worldanyone Ì Ìý“ý“ /test-0/353worldanyone Û Ûý“‚ý“‚ /test-0/352worldanyone Ó Óý“‚ ý“‚ /test-0/355worldanyone ó óý“‚-ý“‚- /test-0/354worldanyone å åý“‚!ý“‚! /test-0/357worldanyoneý“‚Dý“‚D /test-0/356worldanyone ý ýý“‚9ý“‚9 /test-0/359worldanyoneý“‚[ý“‚[ /test-0/358worldanyoneý“‚Pý“‚P /test-0/258worldanyone  ý“|Iý“|I /test-0/257worldanyone  ý“|Aý“|A /test-0/259worldanyone  ý“|Xý“|X /test-0/254worldanyone ì ìý“|ý“| /test-0/253worldanyone á áý“|ý“| /test-0/256worldanyone ÿ ÿý“|4ý“|4 /test-0/255worldanyone ÷ ÷ý“|'ý“|'/apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/embedded/testKeyStore.jks0100644 0000000 0000000 00000004312 15051152474 031312 0ustar00rootroot0000000 0000000 þíþítestK<4Œ´0‚þ0 +*‚ê1"¼od?!&PÏ<Àp̉4{Ì1Ù,V*ÜžÓŽrod…,Òf3pMÃi¼{š×Kg¾Hî`ýå×e!¼åØjÙõKò˜1q6Sñeâ<…ìÊ¿#G}eƒÎÂUgÁÑ"½ˆþ‘ ¥»ªOði–ª©71p޼üèÔ |”Á£DÎ(Rˆ|ýc\$0· ¹ÏâžíïÀT½=Ö²SBi%>Çk—§„KYvî¢;žBybrbÑgli!*2Ècù(þ?ÕADû„]n``$W¡FW ÖéäŠ:g]‹,ü6•=tRŽuü}>ÂnÖ£­­.E,IÌ.ˆpºÑ]„hJ˜&ßË_h¦/årÉeX•³ +!®)ù,”SÔÄ.ÐgU´wXD£Ò•š·y¿uÞ-_!ÉÔ¹a"!C0Ì2Q„LƒþžÉÚq´ ½äMU! yr¤Ë± \6g;î/<Ö!.÷WðHö,Wö»â˜ßo”v£ T"%KWÿûn«…ŸäÓõu¹&ëþ°š! tó8-Ÿ#$ÙÔk˜L¹ än*^ÁwS°(WÄâáœ#µ²Îê;«8Èf®znH‹{%9¹ÇÆŽ™°U¿§èÜ…–Ë×v®wÍhàç ¼ê†¾`Ðõ?¨J¤xªoÓñr°ÝÑ nPìRf‡)䈗„)wÄú/œÄ»éç»0vV¾Oç”Ó‘ÆB)Õ]mo†‚ATî» î=‰ŸÔ£ôͦLãËÏÏî²öZ‰µš¿^H\‡gó{»]$[y¡ ¬­†¥!µtS±šöß_ÝRJ<`ñ¤íç›”A‹ˆöå‡0ZU+ЛëJl Œ4,iæ;“¨V(­Plà4Cs• ßÅ[+MܶQ*í:DÖ†?CZÆêO¶Ñ‡ª“Çæçv ¡™¦-Õ:ió峞 wêMïˆ YFÚk¾ùF|Ï¿Oï‹ê_re~ò¯$Ñ.R.²#Áò±èð¨Jçø£±é/OÓк̶ö«2•ÌFëy™мò½¹£Ì†Ü.oÚ7õI瘪«Ã­5߯0¬¨M ÷ê "«*Ûlö:ÈŒM…øÏKÈþœƒ|¤JY>aûƒdÇñ›gtÀ+w˜yyÚ˦)ǰ,ñ1e•È·I»ew¸u •n%É®µ‡ÖÙûuGC0ë NÝɘ«• °Š]ÊJh©f—Ë[àEUõ*¡eþ§aêFÍH/ž ¿6–žíì±-Ó¹ÊÀ‚´î¬Þ´„· ´Á4$´è9›ÿdLæ9ãðw¨„ˆÔa‚BºdIêHŠoe×Àµ˜lV†ñùMYP®ýâ‘yn3nq P«Ÿš%.Žßtþ72â„>OS nµt2 Pêˆ@7+A‰åFŽÎ¼J©–V„÷‹¤–¾®]ç?ý¨àRŸJPÎ]oÕs®Åõzr7S»uIuÀÿ3õ53Ý|b‡%ðóÿ÷åÝí8qª¿ôÖ¤Þ®Â@ê¨/ä{ówuöµ¸½å­Z©Uë\QÅL^w1tñbXÒ'‘ ÆóCH¤Ù؈"¹™#°+œ¬¡Ì.Ës7\=à ‘Âæ³)9ÿ#Õݲß9d¥•Aµ LûM°üÌ?$Ž´“ oPŸX.509ƒ0‚0‚g s4µ0  *†H†÷  0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0  150130185730Z30150207185730Z0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0‚"0  *†H†÷ ‚0‚ ‚¢!^@0†Él{g¹é~79¹üB:Ûn Å†RÖ“häN²1кÌQtÚ_Vµnƒ>¼]È7ŽšW0©ìë7ÍtåôÒ¬ÃÖ‚üÓ<0Ú,ŨðƒJL¥ÞzÕ `Òœš¨0QeV[ `Ôò6»ÇG}äcð‘Áç™óÎî²eLp=,Sœ~k_T@ûBD)çCçåÍÍHE‰±/.µŸß®’É?] —¿F>ô6°2¿Š­Ô¾¬gñõ²oîéÓ%~ÿ_V?*±»ýç‡|660p›á+Ö#ŸÕŒ>,Õ›‚AðuSÇ?"m ví•€ee±“£!00Ue×´à'EzX/­Õ d-wˆ÷ªõ0  *†H†÷  ‚Èk6ƒ(7\}öÆ„ ¿ÊÀGÏÅ4pzÏkê4„¾z˜æ=íâoÛ#«A­Õß’ôý|î¾J‡¸ñþô[yÜ­÷D=gz;‘×ZyD–×¾AÈdã‡Ã(«Ü6õ°¬½/n¡R%Zìo­ qZfÔ ¦I·uCÄ„ K¬½vX+…âñDgÑ<sZ-Ö ¡Ó ~ïˆùäGêe]Íð9ê/Y¼ùÌ[É»/ —µ!»`¤9lA*Q±Ã£ŒøígÐîìR*äÒ´4sÜ~4$©Nw­ý³w ÂzÀeö̪Yí_aæÂðå¡IAµïž‘ 5œ'$A.ÂÜ‘ñÈ$)é©V ¨dapache-zookeeper-3.9.4/zookeeper-server/src/test/resources/embedded/testTrustStore.jks0100644 0000000 0000000 00000001700 15051152474 031701 0ustar00rootroot0000000 0000000 þíþítestK<5›_X.509ƒ0‚0‚g s4µ0  *†H†÷  0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0  150130185730Z30150207185730Z0o10UUnknown10UUnknown10UUnknown10 U Apache10U  ZooKeeper10U localhost0‚"0  *†H†÷ ‚0‚ ‚¢!^@0†Él{g¹é~79¹üB:Ûn Å†RÖ“häN²1кÌQtÚ_Vµnƒ>¼]È7ŽšW0©ìë7ÍtåôÒ¬ÃÖ‚üÓ<0Ú,ŨðƒJL¥ÞzÕ `Òœš¨0QeV[ `Ôò6»ÇG}äcð‘Áç™óÎî²eLp=,Sœ~k_T@ûBD)çCçåÍÍHE‰±/.µŸß®’É?] —¿F>ô6°2¿Š­Ô¾¬gñõ²oîéÓ%~ÿ_V?*±»ýç‡|660p›á+Ö#ŸÕŒ>,Õ›‚AðuSÇ?"m ví•€ee±“£!00Ue×´à'EzX/­Õ d-wˆ÷ªõ0  *†H†÷  ‚Èk6ƒ(7\}öÆ„ ¿ÊÀGÏÅ4pzÏkê4„¾z˜æ=íâoÛ#«A­Õß’ôý|î¾J‡¸ñþô[yÜ­÷D=gz;‘×ZyD–×¾AÈdã‡Ã(«Ü6õ°¬½/n¡R%Zìo­ qZfÔ ¦I·uCÄ„ K¬½vX+…âñDgÑ<sZ-Ö ¡Ó ~ïˆùäGêe]Íð9ê/Y¼ùÌ[É»/ —µ!»`¤9lA*Q±Ã£ŒøígÐîìR*äÒ´4sÜ~4$©Nw­ý³w ÂzÀeö̪Yí_aæÂðå¡IAµïž‘ 5œîK1¨ÎÓhëhiN ,ÂÅ\5apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/embedded/test_jaas_server_auth.conf0100644 0000000 0000000 00000000724 15051152474 033372 0ustar00rootroot0000000 0000000 Server { org.apache.zookeeper.server.auth.DigestLoginModule required user_foo="bar"; }; Client { org.apache.zookeeper.server.auth.DigestLoginModule required username="foo" password="bar"; }; QuorumServer { org.apache.zookeeper.server.auth.DigestLoginModule required user_test="test"; }; QuorumLearner { org.apache.zookeeper.server.auth.DigestLoginModule required username="test" password="test"; };apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/findbugsExcludeFile.xml0100644 0000000 0000000 00000014462 15051152474 031047 0ustar00rootroot0000000 0000000 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/logback.xml0100644 0000000 0000000 00000002430 15051152474 026526 0ustar00rootroot0000000 0000000 %d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n INFO apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/test-github-pr.sh0100755 0000000 0000000 00000057737 15051152474 027643 0ustar00rootroot0000000 0000000 #!/usr/bin/env bash # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #set -x ### Setup some variables. ### GIT_COMMIT and BUILD_URL are set by Hudson if it is run by patch process ### Read variables from properties file . `dirname $0`/test-patch.properties ############################################################################### parseArgs() { case "$1" in QABUILD) ### Set QABUILD to true to indicate that this script is being run by Hudson QABUILD=true if [[ $# != 13 ]] ; then echo "ERROR: usage $0 QABUILD " cleanupAndExit 0 fi PATCH_DIR=$2 PS=$3 WGET=$4 JIRACLI=$5 GIT=$6 GREP=$7 PATCH=$8 FINDBUGS_HOME=$9 BASEDIR=${10} JIRA_PASSWD=${11} JAVA5_HOME=${12} CURL=${13} if [ ! -e "$PATCH_DIR" ] ; then mkdir -p $PATCH_DIR fi ## Obtain PR number and title PULLREQUEST_ID=${GIT_PR_NUMBER} PULLREQUEST_TITLE="${GIT_PR_TITLE}" ## Extract jira number from PR title local prefix=${PULLREQUEST_TITLE%ZOOKEEPER\-[0-9]*} local noprefix=${PULLREQUEST_TITLE#$prefix} local regex='\(ZOOKEEPER-.[0-9]*\)' defect=$(expr "$noprefix" : ${regex}) echo "Pull request id: ${PULLREQUEST_ID}" echo "Pull request title: ${PULLREQUEST_TITLE}" echo "Defect number: ${defect}" JIRA_COMMENT="GitHub Pull Request ${PULLREQUEST_NUMBER} Build " ;; DEVELOPER) ### Set QABUILD to false to indicate that this script is being run by a developer QABUILD=false if [[ $# != 9 ]] ; then echo "ERROR: usage $0 DEVELOPER " cleanupAndExit 0 fi PATCH_DIR=$3 PATCH_FILE=${PATCH_DIR}/patch curl -L $2.diff > ${PATCH_FILE} ### PATCH_FILE contains the location of the patchfile if [[ ! -e "$PATCH_FILE" ]] ; then echo "Unable to locate the patch file $PATCH_FILE" cleanupAndExit 0 fi ### Check if $PATCH_DIR exists. If it does not exist, create a new directory if [[ ! -e "$PATCH_DIR" ]] ; then mkdir "$PATCH_DIR" if [[ $? == 0 ]] ; then echo "$PATCH_DIR has been created" else echo "Unable to create $PATCH_DIR" cleanupAndExit 0 fi fi GIT=$4 GREP=$5 PATCH=$6 FINDBUGS_HOME=$7 BASEDIR=$8 JAVA5_HOME=${9} ### Obtain the patch filename to append it to the version number local subject=`grep "Subject:" ${PATCH_FILE}` local length=`expr match ${subject} ZOOKEEPER-[0-9]*` local position=`expr index ${subject} ZOOKEEPER-` defect=${${subject:$position:$length}#ZOOKEEPER-} ;; *) echo "ERROR: usage $0 QABUILD [args] | DEVELOPER [args]" cleanupAndExit 0 ;; esac } ############################################################################### checkout () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Testing patch for pull request ${PULLREQUEST_ID}." echo "======================================================================" echo "======================================================================" echo "" echo "" ### When run by a developer, if the workspace contains modifications, do not continue # Ref http://stackoverflow.com/a/2659808 for details on checking dirty status ${GIT} diff-index --quiet HEAD if [[ $? -ne 0 ]] ; then uncommitted=`${GIT} diff --name-only HEAD` uncommitted="You have the following files with uncommitted changes:${NEWLINE}${uncommitted}" fi untracked="$(${GIT} ls-files --exclude-standard --others)" && test -z "${untracked}" if [[ $? -ne 0 ]] ; then untracked="You have untracked and unignored files:${NEWLINE}${untracked}" fi if [[ $QABUILD == "false" ]] ; then if [[ $uncommitted || $untracked ]] ; then echo "ERROR: can't run in a workspace that contains the following modifications" echo "" echo "${uncommitted}" echo "" echo "${untracked}" cleanupAndExit 1 fi else # I don't believe we need to do anything here - the jenkins job will # cleanup the environment for us ("cleanup before checkout" action) # on the precommit jenkins job echo fi return $? } ############################################################################### setup () { ### exit if warnings are NOT defined in the properties file if [ -z "$OK_FINDBUGS_WARNINGS" ] || [[ -z "$OK_JAVADOC_WARNINGS" ]] || [[ -z $OK_RELEASEAUDIT_WARNINGS ]]; then echo "Please define the following properties in test-patch.properties file" echo "OK_FINDBUGS_WARNINGS" echo "OK_RELEASEAUDIT_WARNINGS" echo "OK_JAVADOC_WARNINGS" cleanupAndExit 1 fi ### get pull request diff ${CURL} -L ${GIT_PR_URL}.diff > $PATCH_DIR/patch echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Pre-build trunk to verify trunk stability and javac warnings" echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1" $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1 if [[ $? != 0 ]] ; then echo "Trunk compilation is broken?" cleanupAndExit 1 fi } ############################################################################### ### Check for @author tags in the patch checkAuthor () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Checking there are no @author tags in the patch." echo "======================================================================" echo "======================================================================" echo "" echo "" authorTags=`$GREP -c -i '@author' $PATCH_DIR/patch` echo "There appear to be $authorTags @author tags in the patch." if [[ $authorTags != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 @author. The patch appears to contain $authorTags @author tags which the Zookeeper community has agreed to not allow in code contributions." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 @author. The patch does not contain any @author tags." return 0 } ############################################################################### ### Check for tests in the patch checkTests () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Checking there are new or changed tests in the patch." echo "======================================================================" echo "======================================================================" echo "" echo "" testReferences=`$GREP -c -i '/test' $PATCH_DIR/patch` echo "There appear to be $testReferences test files referenced in the patch." if [[ $testReferences == 0 ]] ; then if [[ $QABUILD == "true" ]] ; then patchIsDoc=`$GREP -c -i 'title="documentation' $PATCH_DIR/jira` if [[ $patchIsDoc != 0 ]] ; then echo "The patch appears to be a documentation patch that doesn't require tests." JIRA_COMMENT="$JIRA_COMMENT +0 tests included. The patch appears to be a documentation patch that doesn't require tests." return 0 fi fi JIRA_COMMENT="$JIRA_COMMENT -1 tests included. The patch doesn't appear to include any new or modified tests. Please justify why no new tests are needed for this patch. Also please list what manual steps were performed to verify this patch." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 tests included. The patch appears to include $testReferences new or modified tests." return 0 } ############################################################################### ### Check there are no javadoc warnings checkJavadocWarnings () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched javadoc warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt" $ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt javadocWarnings=`$GREP -o '\[javadoc\] [0-9]* warning' $PATCH_DIR/patchJavadocWarnings.txt | awk '{total += $2} END {print total}'` echo "" echo "" echo "There appear to be $javadocWarnings javadoc warnings generated by the patched build." ### if current warnings greater than OK_JAVADOC_WARNINGS if [[ $javadocWarnings > $OK_JAVADOC_WARNINGS ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 javadoc. The javadoc tool appears to have generated `expr $(($javadocWarnings-$OK_JAVADOC_WARNINGS))` warning messages." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 javadoc. The javadoc tool did not generate any warning messages." return 0 } ############################################################################### ### Check there are no changes in the number of Javac warnings checkJavacWarnings () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched javac warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1" $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1 if [[ $? != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 javac. The patch appears to cause tar ant target to fail." return 1 fi ### Compare trunk and patch javac warning numbers if [[ -f $PATCH_DIR/patchJavacWarnings.txt ]] ; then trunkJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/trunkJavacWarnings.txt | awk '{total += $2} END {print total}'` patchJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/patchJavacWarnings.txt | awk '{total += $2} END {print total}'` echo "There appear to be $trunkJavacWarnings javac compiler warnings before the patch and $patchJavacWarnings javac compiler warnings after applying the patch." if [[ $patchJavacWarnings != "" && $trunkJavacWarnings != "" ]] ; then if [[ $patchJavacWarnings -gt $trunkJavacWarnings ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 javac. The applied patch generated $patchJavacWarnings javac compiler warnings (more than the trunk's current $trunkJavacWarnings warnings)." return 1 fi fi fi JIRA_COMMENT="$JIRA_COMMENT +1 javac. The applied patch does not increase the total number of javac compiler warnings." return 0 } ############################################################################### ### Check there are no changes in the number of release audit (RAT) warnings checkReleaseAuditWarnings () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched release audit warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1" $ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1 ### Compare trunk and patch release audit warning numbers if [[ -f $PATCH_DIR/patchReleaseAuditWarnings.txt ]] ; then patchReleaseAuditWarnings=`$GREP -c '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt` echo "" echo "" echo "There appear to be $OK_RELEASEAUDIT_WARNINGS release audit warnings before the patch and $patchReleaseAuditWarnings release audit warnings after applying the patch." if [[ $patchReleaseAuditWarnings != "" && $OK_RELEASEAUDIT_WARNINGS != "" ]] ; then if [[ $patchReleaseAuditWarnings -gt $OK_RELEASEAUDIT_WARNINGS ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 release audit. The applied patch generated $patchReleaseAuditWarnings release audit warnings (more than the trunk's current $OK_RELEASEAUDIT_WARNINGS warnings)." $GREP '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt > $PATCH_DIR/patchReleaseAuditProblems.txt echo "Lines that start with ????? in the release audit report indicate files that do not have an Apache license header." >> $PATCH_DIR/patchReleaseAuditProblems.txt JIRA_COMMENT_FOOTER="Release audit warnings: $BUILD_URL/artifact/trunk/patchprocess/patchReleaseAuditProblems.txt $JIRA_COMMENT_FOOTER" return 1 fi fi fi JIRA_COMMENT="$JIRA_COMMENT +1 release audit. The applied patch does not increase the total number of release audit warnings." return 0 } ############################################################################### ### Check there are no changes in the number of Checkstyle warnings checkStyle () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched checkstyle warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "THIS IS NOT IMPLEMENTED YET" echo "" echo "" echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle" $ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle JIRA_COMMENT_FOOTER="Checkstyle results: $BUILD_URL/artifact/trunk/build/test/checkstyle-errors.html $JIRA_COMMENT_FOOTER" ### TODO: calculate actual patchStyleErrors # patchStyleErrors=0 # if [[ $patchStyleErrors != 0 ]] ; then # JIRA_COMMENT="$JIRA_COMMENT # # -1 checkstyle. The patch generated $patchStyleErrors code style errors." # return 1 # fi # JIRA_COMMENT="$JIRA_COMMENT # # +1 checkstyle. The patch generated 0 code style errors." return 0 } ############################################################################### ### Check there are no changes in the number of Findbugs warnings checkFindbugsWarnings () { findbugs_version=`${FINDBUGS_HOME}/bin/findbugs -version` echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched Findbugs warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= findbugs" $ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= findbugs if [ $? != 0 ] ; then JIRA_COMMENT="$JIRA_COMMENT -1 findbugs. The patch appears to cause Findbugs (version ${findbugs_version}) to fail." return 1 fi JIRA_COMMENT_FOOTER="Findbugs warnings: $BUILD_URL/artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html $JIRA_COMMENT_FOOTER" cp $BASEDIR/build/test/findbugs/*.xml $PATCH_DIR/patchFindbugsWarnings.xml $FINDBUGS_HOME/bin/setBugDatabaseInfo -timestamp "01/01/2000" \ $PATCH_DIR/patchFindbugsWarnings.xml \ $PATCH_DIR/patchFindbugsWarnings.xml findbugsWarnings=`$FINDBUGS_HOME/bin/filterBugs -first "01/01/2000" $PATCH_DIR/patchFindbugsWarnings.xml \ $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml | /usr/bin/awk '{print $1}'` $FINDBUGS_HOME/bin/convertXmlToText -html \ $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml \ $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html $PATCH_DIR/newPatchFindbugsWarnings.html cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml $PATCH_DIR/newPatchFindbugsWarnings.xml ### if current warnings greater than OK_FINDBUGS_WARNINGS if [[ $findbugsWarnings > $OK_FINDBUGS_WARNINGS ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 findbugs. The patch appears to introduce `expr $(($findbugsWarnings-$OK_FINDBUGS_WARNINGS))` new Findbugs (version ${findbugs_version}) warnings." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 findbugs. The patch does not introduce any new Findbugs (version ${findbugs_version}) warnings." return 0 } ############################################################################### ### Run the test-core target runCoreTests () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Running core tests." echo "======================================================================" echo "======================================================================" echo "" echo "" ### Kill any rogue build processes from the last attempt $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=no -Dtest.junit.threads=4 -Dcompile.c++=yes -Djava5.home=$JAVA5_HOME test-core" $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=no -Dtest.junit.threads=4 -Dcompile.c++=yes -Djava5.home=$JAVA5_HOME test-core if [[ $? != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 core tests. The patch failed core unit tests." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 core tests. The patch passed core unit tests." return 0 } ############################################################################### ### Run the test-contrib target runContribTests () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Running contrib tests." echo "======================================================================" echo "======================================================================" echo "" echo "" ### Kill any rogue build processes from the last attempt $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=no test-contrib" $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=no test-contrib if [[ $? != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 contrib tests. The patch failed contrib unit tests." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 contrib tests. The patch passed contrib unit tests." return 0 } ############################################################################### ### Submit a comment to the defect's Jira submitJiraComment () { local result=$1 ### Do not output the value of JIRA_COMMENT_FOOTER when run by a developer if [[ $QABUILD == "false" ]] ; then JIRA_COMMENT_FOOTER="" fi if [[ $result == 0 ]] ; then comment="+1 overall. $JIRA_COMMENT $JIRA_COMMENT_FOOTER" else comment="-1 overall. $JIRA_COMMENT $JIRA_COMMENT_FOOTER" fi ### Output the test result to the console echo " $comment" if [[ $QABUILD == "true" ]] ; then echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Adding comment to Jira." echo "======================================================================" echo "======================================================================" echo "" echo "" ### Update Jira with a comment export USER=jenkins $JIRACLI -s https://issues.apache.org/jira -a addcomment -u hadoopqa -p $JIRA_PASSWD --comment "$comment" --issue $defect $JIRACLI -s https://issues.apache.org/jira -a logout -u hadoopqa -p $JIRA_PASSWD fi } ############################################################################### ### Cleanup files cleanupAndExit () { local result=$1 if [[ $QABUILD == "true" ]] ; then if [ -e "$PATCH_DIR" ] ; then mv $PATCH_DIR $BASEDIR fi fi echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Finished build." echo "======================================================================" echo "======================================================================" echo "" echo "" exit $result } ############################################################################### ############################################################################### ############################################################################### JIRA_COMMENT="" JIRA_COMMENT_FOOTER="Console output: $BUILD_URL/console This message is automatically generated." ### Check if arguments to the script have been specified properly or not echo "----- Going to parser args -----" parseArgs $@ cd $BASEDIR echo "----- Parsed args, going to checkout -----" checkout RESULT=$? if [[ $QABUILD == "true" ]] ; then if [[ $RESULT != 0 ]] ; then exit 100 fi fi setup checkAuthor (( RESULT = RESULT + $? )) checkTests checkTestsResult=$? (( RESULT = RESULT + $checkTestsResult )) if [[ $checkTestsResult != 0 ]] ; then submitJiraComment 1 cleanupAndExit 1 fi checkJavadocWarnings (( RESULT = RESULT + $? )) checkJavacWarnings (( RESULT = RESULT + $? )) ### Checkstyle not implemented yet #checkStyle #(( RESULT = RESULT + $? )) checkFindbugsWarnings (( RESULT = RESULT + $? )) checkReleaseAuditWarnings (( RESULT = RESULT + $? )) ### Do not call these when run by a developer if [[ $QABUILD == "true" ]] ; then runCoreTests (( RESULT = RESULT + $? )) runContribTests (( RESULT = RESULT + $? )) fi JIRA_COMMENT_FOOTER="Test results: $BUILD_URL/testReport/ $JIRA_COMMENT_FOOTER" submitJiraComment $RESULT cleanupAndExit $RESULT apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/test-patch.properties0100644 0000000 0000000 00000001526 15051152474 030601 0ustar00rootroot0000000 0000000 # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. OK_RELEASEAUDIT_WARNINGS=0 OK_FINDBUGS_WARNINGS=0 OK_JAVADOC_WARNINGS=0 apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/test-patch.sh0100755 0000000 0000000 00000062015 15051152474 027022 0ustar00rootroot0000000 0000000 #!/usr/bin/env bash # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #set -x ### Setup some variables. ### GIT_COMMIT and BUILD_URL are set by Hudson if it is run by patch process ### Read variables from properties file . `dirname $0`/test-patch.properties ############################################################################### parseArgs() { case "$1" in HUDSON) ### Set HUDSON to true to indicate that this script is being run by Hudson HUDSON=true if [[ $# != 14 ]] ; then echo "ERROR: usage $0 HUDSON " cleanupAndExit 0 fi PATCH_DIR=$2 PS=$3 WGET=$4 JIRACLI=$5 GIT=$6 GREP=$7 PATCH=$8 FINDBUGS_HOME=$9 BASEDIR=${10} JIRA_PASSWD=${11} JAVA5_HOME=${12} CURL=${13} defect=${14} ### Retrieve the defect number if [ -z "$defect" ] ; then echo "Could not determine the patch to test. Exiting." cleanupAndExit 0 fi if [ ! -e "$PATCH_DIR" ] ; then mkdir -p $PATCH_DIR fi ;; DEVELOPER) ### Set HUDSON to false to indicate that this script is being run by a developer HUDSON=false if [[ $# != 9 ]] ; then echo "ERROR: usage $0 DEVELOPER " cleanupAndExit 0 fi ### PATCH_FILE contains the location of the patchfile PATCH_FILE=$2 if [[ ! -e "$PATCH_FILE" ]] ; then echo "Unable to locate the patch file $PATCH_FILE" cleanupAndExit 0 fi PATCH_DIR=$3 ### Check if $PATCH_DIR exists. If it does not exist, create a new directory if [[ ! -e "$PATCH_DIR" ]] ; then mkdir "$PATCH_DIR" if [[ $? == 0 ]] ; then echo "$PATCH_DIR has been created" else echo "Unable to create $PATCH_DIR" cleanupAndExit 0 fi fi GIT=$4 GREP=$5 PATCH=$6 FINDBUGS_HOME=$7 BASEDIR=$8 JAVA5_HOME=${9} ### Obtain the patch filename to append it to the version number defect=`basename $PATCH_FILE` ;; *) echo "ERROR: usage $0 HUDSON [args] | DEVELOPER [args]" cleanupAndExit 0 ;; esac } ############################################################################### checkout () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Testing patch for ${defect}." echo "======================================================================" echo "======================================================================" echo "" echo "" ### When run by a developer, if the workspace contains modifications, do not continue # Ref http://stackoverflow.com/a/2659808 for details on checking dirty status ${GIT} diff-index --quiet HEAD if [[ $? -ne 0 ]] ; then uncommitted=`${GIT} diff --name-only HEAD` uncommitted="You have the following files with uncommitted changes:${NEWLINE}${uncommitted}" fi untracked="$(${GIT} ls-files --exclude-standard --others)" && test -z "${untracked}" if [[ $? -ne 0 ]] ; then untracked="You have untracked and unignored files:${NEWLINE}${untracked}" fi if [[ $HUDSON == "false" ]] ; then if [[ $uncommitted || $untracked ]] ; then echo "ERROR: can't run in a workspace that contains the following modifications" echo "" echo "${uncommitted}" echo "" echo "${untracked}" cleanupAndExit 1 fi else # I don't believe we need to do anything here - the jenkins job will # cleanup the environment for us ("cleanup before checkout" action) # on the precommit jenkins job echo fi return $? } ############################################################################### setup () { ### Download latest patch file (ignoring .htm and .html) when run from patch process if [[ $HUDSON == "true" ]] ; then $WGET -q -O $PATCH_DIR/jira http://issues.apache.org/jira/browse/$defect if [[ `$GREP -c 'Patch Available' $PATCH_DIR/jira` == 0 ]] ; then echo "$defect is not \"Patch Available\". Exiting." cleanupAndExit 0 fi relativePatchURL=`$GREP -o '"/jira/secure/attachment/[0-9]*/[^"]*' $PATCH_DIR/jira | $GREP -v -e 'htm[l]*$' | sort | tail -1 | $GREP -o '/jira/secure/attachment/[0-9]*/[^"]*'` patchURL="http://issues.apache.org${relativePatchURL}" patchNum=`echo $patchURL | $GREP -o '[0-9]*/' | $GREP -o '[0-9]*'` echo "$defect patch is being downloaded at `date` from" echo "$patchURL" $WGET -q -O $PATCH_DIR/patch $patchURL JIRA_COMMENT="Here are the results of testing the latest attachment $patchURL against trunk revision ${GIT_COMMIT}." ### Copy the patch file to $PATCH_DIR else cp $PATCH_FILE $PATCH_DIR/patch if [[ $? == 0 ]] ; then echo "Patch file $PATCH_FILE copied to $PATCH_DIR" else echo "Could not copy $PATCH_FILE to $PATCH_DIR" cleanupAndExit 0 fi fi ### exit if warnings are NOT defined in the properties file if [ -z "$OK_FINDBUGS_WARNINGS" ] || [[ -z "$OK_JAVADOC_WARNINGS" ]] || [[ -z $OK_RELEASEAUDIT_WARNINGS ]]; then echo "Please define the following properties in test-patch.properties file" echo "OK_FINDBUGS_WARNINGS" echo "OK_RELEASEAUDIT_WARNINGS" echo "OK_JAVADOC_WARNINGS" cleanupAndExit 1 fi echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Pre-build trunk to verify trunk stability and javac warnings" echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1" $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/trunkJavacWarnings.txt 2>&1 if [[ $? != 0 ]] ; then echo "Trunk compilation is broken?" cleanupAndExit 1 fi } ############################################################################### ### Check for @author tags in the patch checkAuthor () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Checking there are no @author tags in the patch." echo "======================================================================" echo "======================================================================" echo "" echo "" authorTags=`$GREP -c -i '@author' $PATCH_DIR/patch` echo "There appear to be $authorTags @author tags in the patch." if [[ $authorTags != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 @author. The patch appears to contain $authorTags @author tags which the Zookeeper community has agreed to not allow in code contributions." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 @author. The patch does not contain any @author tags." return 0 } ############################################################################### ### Check for tests in the patch checkTests () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Checking there are new or changed tests in the patch." echo "======================================================================" echo "======================================================================" echo "" echo "" testReferences=`$GREP -c -i '/test' $PATCH_DIR/patch` echo "There appear to be $testReferences test files referenced in the patch." if [[ $testReferences == 0 ]] ; then if [[ $HUDSON == "true" ]] ; then patchIsDoc=`$GREP -c -i 'title="documentation' $PATCH_DIR/jira` if [[ $patchIsDoc != 0 ]] ; then echo "The patch appears to be a documentation patch that doesn't require tests." JIRA_COMMENT="$JIRA_COMMENT +0 tests included. The patch appears to be a documentation patch that doesn't require tests." return 0 fi fi JIRA_COMMENT="$JIRA_COMMENT -1 tests included. The patch doesn't appear to include any new or modified tests. Please justify why no new tests are needed for this patch. Also please list what manual steps were performed to verify this patch." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 tests included. The patch appears to include $testReferences new or modified tests." return 0 } ############################################################################### ### Attempt to apply the patch applyPatch () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Applying patch." echo "======================================================================" echo "======================================================================" echo "" echo "" $PATCH -E -p0 < $PATCH_DIR/patch if [[ $? != 0 ]] ; then echo "PATCH APPLICATION FAILED" JIRA_COMMENT="$JIRA_COMMENT -1 patch. The patch command could not apply the patch." return 1 fi return 0 } ############################################################################### ### Check there are no javadoc warnings checkJavadocWarnings () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched javadoc warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt" $ANT_HOME/bin/ant -DZookeeperPatchProcess= clean javadoc | tee $PATCH_DIR/patchJavadocWarnings.txt javadocWarnings=`$GREP -o '\[javadoc\] [0-9]* warning' $PATCH_DIR/patchJavadocWarnings.txt | awk '{total += $2} END {print total}'` echo "" echo "" echo "There appear to be $javadocWarnings javadoc warnings generated by the patched build." ### if current warnings greater than OK_JAVADOC_WARNINGS if [[ $javadocWarnings > $OK_JAVADOC_WARNINGS ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 javadoc. The javadoc tool appears to have generated `expr $(($javadocWarnings-$OK_JAVADOC_WARNINGS))` warning messages." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 javadoc. The javadoc tool did not generate any warning messages." return 0 } ############################################################################### ### Check there are no changes in the number of Javac warnings checkJavacWarnings () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched javac warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1" $ANT_HOME/bin/ant -Djavac.args="-Xlint -Xmaxwarns 1000" -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= clean tar > $PATCH_DIR/patchJavacWarnings.txt 2>&1 if [[ $? != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 javac. The patch appears to cause tar ant target to fail." return 1 fi ### Compare trunk and patch javac warning numbers if [[ -f $PATCH_DIR/patchJavacWarnings.txt ]] ; then trunkJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/trunkJavacWarnings.txt | awk '{total += $2} END {print total}'` patchJavacWarnings=`$GREP -o '\[javac\] [0-9]* warning' $PATCH_DIR/patchJavacWarnings.txt | awk '{total += $2} END {print total}'` echo "There appear to be $trunkJavacWarnings javac compiler warnings before the patch and $patchJavacWarnings javac compiler warnings after applying the patch." if [[ $patchJavacWarnings != "" && $trunkJavacWarnings != "" ]] ; then if [[ $patchJavacWarnings -gt $trunkJavacWarnings ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 javac. The applied patch generated $patchJavacWarnings javac compiler warnings (more than the trunk's current $trunkJavacWarnings warnings)." return 1 fi fi fi JIRA_COMMENT="$JIRA_COMMENT +1 javac. The applied patch does not increase the total number of javac compiler warnings." return 0 } ############################################################################### ### Check there are no changes in the number of release audit (RAT) warnings checkReleaseAuditWarnings () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched release audit warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1" $ANT_HOME/bin/ant -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= releaseaudit > $PATCH_DIR/patchReleaseAuditWarnings.txt 2>&1 ### Compare trunk and patch release audit warning numbers if [[ -f $PATCH_DIR/patchReleaseAuditWarnings.txt ]] ; then patchReleaseAuditWarnings=`$GREP -c '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt` echo "" echo "" echo "There appear to be $OK_RELEASEAUDIT_WARNINGS release audit warnings before the patch and $patchReleaseAuditWarnings release audit warnings after applying the patch." if [[ $patchReleaseAuditWarnings != "" && $OK_RELEASEAUDIT_WARNINGS != "" ]] ; then if [[ $patchReleaseAuditWarnings -gt $OK_RELEASEAUDIT_WARNINGS ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 release audit. The applied patch generated $patchReleaseAuditWarnings release audit warnings (more than the trunk's current $OK_RELEASEAUDIT_WARNINGS warnings)." $GREP '\!?????' $PATCH_DIR/patchReleaseAuditWarnings.txt > $PATCH_DIR/patchReleaseAuditProblems.txt echo "Lines that start with ????? in the release audit report indicate files that do not have an Apache license header." >> $PATCH_DIR/patchReleaseAuditProblems.txt JIRA_COMMENT_FOOTER="Release audit warnings: $BUILD_URL/artifact/trunk/patchprocess/patchReleaseAuditProblems.txt $JIRA_COMMENT_FOOTER" return 1 fi fi fi JIRA_COMMENT="$JIRA_COMMENT +1 release audit. The applied patch does not increase the total number of release audit warnings." return 0 } ############################################################################### ### Check there are no changes in the number of Checkstyle warnings checkStyle () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched checkstyle warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "THIS IS NOT IMPLEMENTED YET" echo "" echo "" echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle" $ANT_HOME/bin/ant -DZookeeperPatchProcess= checkstyle JIRA_COMMENT_FOOTER="Checkstyle results: $BUILD_URL/artifact/trunk/build/test/checkstyle-errors.html $JIRA_COMMENT_FOOTER" ### TODO: calculate actual patchStyleErrors # patchStyleErrors=0 # if [[ $patchStyleErrors != 0 ]] ; then # JIRA_COMMENT="$JIRA_COMMENT # # -1 checkstyle. The patch generated $patchStyleErrors code style errors." # return 1 # fi # JIRA_COMMENT="$JIRA_COMMENT # # +1 checkstyle. The patch generated 0 code style errors." return 0 } ############################################################################### ### Check there are no changes in the number of Findbugs warnings checkFindbugsWarnings () { findbugs_version=`${FINDBUGS_HOME}/bin/findbugs -version` echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Determining number of patched Findbugs warnings." echo "======================================================================" echo "======================================================================" echo "" echo "" echo "$ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= findbugs" $ANT_HOME/bin/ant -Dfindbugs.home=$FINDBUGS_HOME -Djava5.home=${JAVA5_HOME} -DZookeeperPatchProcess= findbugs if [ $? != 0 ] ; then JIRA_COMMENT="$JIRA_COMMENT -1 findbugs. The patch appears to cause Findbugs (version ${findbugs_version}) to fail." return 1 fi JIRA_COMMENT_FOOTER="Findbugs warnings: $BUILD_URL/artifact/trunk/build/test/findbugs/newPatchFindbugsWarnings.html $JIRA_COMMENT_FOOTER" cp $BASEDIR/build/test/findbugs/*.xml $PATCH_DIR/patchFindbugsWarnings.xml $FINDBUGS_HOME/bin/setBugDatabaseInfo -timestamp "01/01/2000" \ $PATCH_DIR/patchFindbugsWarnings.xml \ $PATCH_DIR/patchFindbugsWarnings.xml findbugsWarnings=`$FINDBUGS_HOME/bin/filterBugs -first "01/01/2000" $PATCH_DIR/patchFindbugsWarnings.xml \ $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml | /usr/bin/awk '{print $1}'` $FINDBUGS_HOME/bin/convertXmlToText -html \ $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml \ $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.html $PATCH_DIR/newPatchFindbugsWarnings.html cp $BASEDIR/build/test/findbugs/newPatchFindbugsWarnings.xml $PATCH_DIR/newPatchFindbugsWarnings.xml ### if current warnings greater than OK_FINDBUGS_WARNINGS if [[ $findbugsWarnings > $OK_FINDBUGS_WARNINGS ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 findbugs. The patch appears to introduce `expr $(($findbugsWarnings-$OK_FINDBUGS_WARNINGS))` new Findbugs (version ${findbugs_version}) warnings." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 findbugs. The patch does not introduce any new Findbugs (version ${findbugs_version}) warnings." return 0 } ############################################################################### ### Run the test-core target runCoreTests () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Running core tests." echo "======================================================================" echo "======================================================================" echo "" echo "" ### Kill any rogue build processes from the last attempt $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes -Dtest.junit.threads=8 -Dcompile.c++=yes -Djava5.home=$JAVA5_HOME test-core" $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes -Dtest.junit.threads=8 -Dcompile.c++=yes -Djava5.home=$JAVA5_HOME test-core if [[ $? != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 core tests. The patch failed core unit tests." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 core tests. The patch passed core unit tests." return 0 } ############################################################################### ### Run the test-contrib target runContribTests () { echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Running contrib tests." echo "======================================================================" echo "======================================================================" echo "" echo "" ### Kill any rogue build processes from the last attempt $PS auxwww | $GREP ZookeeperPatchProcess | /usr/bin/nawk '{print $2}' | /usr/bin/xargs -t -I {} /bin/kill -9 {} > /dev/null echo "$ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes test-contrib" $ANT_HOME/bin/ant -DZookeeperPatchProcess= -Dtest.junit.output.format=xml -Dtest.output=yes test-contrib if [[ $? != 0 ]] ; then JIRA_COMMENT="$JIRA_COMMENT -1 contrib tests. The patch failed contrib unit tests." return 1 fi JIRA_COMMENT="$JIRA_COMMENT +1 contrib tests. The patch passed contrib unit tests." return 0 } ############################################################################### ### Submit a comment to the defect's Jira submitJiraComment () { local result=$1 ### Do not output the value of JIRA_COMMENT_FOOTER when run by a developer if [[ $HUDSON == "false" ]] ; then JIRA_COMMENT_FOOTER="" fi if [[ $result == 0 ]] ; then comment="+1 overall. $JIRA_COMMENT $JIRA_COMMENT_FOOTER" else comment="-1 overall. $JIRA_COMMENT $JIRA_COMMENT_FOOTER" fi ### Output the test result to the console echo " $comment" if [[ $HUDSON == "true" ]] ; then echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Adding comment to Jira." echo "======================================================================" echo "======================================================================" echo "" echo "" ### Update Jira with a comment export USER=jenkins $JIRACLI -s https://issues.apache.org/jira -a addcomment -u hadoopqa -p $JIRA_PASSWD --comment "$comment" --issue $defect $JIRACLI -s https://issues.apache.org/jira -a logout -u hadoopqa -p $JIRA_PASSWD fi } ############################################################################### ### Cleanup files cleanupAndExit () { local result=$1 if [[ $HUDSON == "true" ]] ; then if [ -e "$PATCH_DIR" ] ; then mv $PATCH_DIR $BASEDIR fi fi echo "" echo "" echo "======================================================================" echo "======================================================================" echo " Finished build." echo "======================================================================" echo "======================================================================" echo "" echo "" exit $result } ############################################################################### ############################################################################### ############################################################################### JIRA_COMMENT="" JIRA_COMMENT_FOOTER="Console output: $BUILD_URL/console This message is automatically generated." ### Check if arguments to the script have been specified properly or not parseArgs $@ cd $BASEDIR checkout RESULT=$? if [[ $HUDSON == "true" ]] ; then if [[ $RESULT != 0 ]] ; then exit 100 fi fi setup checkAuthor RESULT=$? checkTests (( RESULT = RESULT + $? )) applyPatch if [[ $? != 0 ]] ; then submitJiraComment 1 cleanupAndExit 1 fi checkJavadocWarnings (( RESULT = RESULT + $? )) checkJavacWarnings (( RESULT = RESULT + $? )) ### Checkstyle not implemented yet #checkStyle #(( RESULT = RESULT + $? )) checkFindbugsWarnings (( RESULT = RESULT + $? )) checkReleaseAuditWarnings (( RESULT = RESULT + $? )) ### Do not call these when run by a developer if [[ $HUDSON == "true" ]] ; then runCoreTests (( RESULT = RESULT + $? )) runContribTests (( RESULT = RESULT + $? )) fi JIRA_COMMENT_FOOTER="Test results: $BUILD_URL/testReport/ $JIRA_COMMENT_FOOTER" submitJiraComment $RESULT cleanupAndExit $RESULT apache-zookeeper-3.9.4/zookeeper-server/src/test/resources/test-scripts.sh0100755 0000000 0000000 00000010322 15051152474 027404 0ustar00rootroot0000000 0000000 #!/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. ZKS=bin/zkServer.sh ZKSI=bin/zkServer-initialize.sh if [ ! -d "conf" ]; then echo "run this from the toplevel directory" exit 1 fi DATADIR=test-scripts_datadir DATALOGDIR=test-scripts_datalogdir case "`uname`" in CYGWIN*) cygwin=true ;; *) cygwin=false ;; esac if $cygwin then ZOOCFG=`cygpath -wp "$ZOOCFG"` # cygwin has a "kill" in the shell itself, gets confused KILL=/bin/kill else KILL=kill fi fail() { # don't run clear_tmp to allow debugging echo "FAIL $1" $KILL -9 $(cat "$ZOOPIDFILE") $KILL -9 $$ } #generate a minimal config genconfig1() { cat > test-scripts.cfg <> test-scripts.cfg </dev/null` [ "$TEST_PRINT_VERSION" != "" ] || fail $LINENO #done, cleanup and exit clear_tmp echo "SUCCESS" apache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/Zab.tla0100644 0000000 0000000 00000205676 15051152474 026347 0ustar00rootroot0000000 0000000 (* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *) -------------------------------- MODULE Zab --------------------------------- (* This is the formal specification for the Zab consensus algorithm, in DSN'2011, which represents protocol specification in our work.*) EXTENDS Integers, FiniteSets, Sequences, Naturals, TLC ----------------------------------------------------------------------------- \* The set of servers CONSTANT Server \* States of server CONSTANTS LOOKING, FOLLOWING, LEADING \* Zab states of server CONSTANTS ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST \* Message types CONSTANTS CEPOCH, NEWEPOCH, ACKEPOCH, NEWLEADER, ACKLD, COMMITLD, PROPOSE, ACK, COMMIT \* [MaxTimeoutFailures, MaxTransactionNum, MaxEpoch, MaxRestarts] CONSTANT Parameters MAXEPOCH == 10 NullPoint == CHOOSE p: p \notin Server Quorums == {Q \in SUBSET Server: Cardinality(Q)*2 > Cardinality(Server)} ----------------------------------------------------------------------------- \* Variables that all servers use. VARIABLES state, \* State of server, in {LOOKING, FOLLOWING, LEADING}. zabState, \* Current phase of server, in \* {ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST}. acceptedEpoch, \* Epoch of the last LEADERINFO packet accepted, \* namely f.p in paper. currentEpoch, \* Epoch of the last NEWLEADER packet accepted, \* namely f.a in paper. history, \* History of servers: sequence of transactions, \* containing: [zxid, value, ackSid, epoch]. lastCommitted \* Maximum index and zxid known to be committed, \* namely 'lastCommitted' in Leader. Starts from 0, \* and increases monotonically before restarting. \* Variables only used for leader. VARIABLES learners, \* Set of servers leader connects. cepochRecv, \* Set of learners leader has received CEPOCH from. \* Set of record [sid, connected, epoch], \* where epoch means f.p from followers. ackeRecv, \* Set of learners leader has received ACKEPOCH from. \* Set of record \* [sid, connected, peerLastEpoch, peerHistory], \* to record f.a and h(f) from followers. ackldRecv, \* Set of learners leader has received ACKLD from. \* Set of record [sid, connected]. sendCounter \* Count of txns leader has broadcast. \* Variables only used for follower. VARIABLES connectInfo \* If follower has connected with leader. \* If follower lost connection, then null. \* Variable representing oracle of leader. VARIABLE leaderOracle \* Current oracle. \* Variables about network channel. VARIABLE msgs \* Simulates network channel. \* msgs[i][j] means the input buffer of server j \* from server i. \* Variables only used in verifying properties. VARIABLES epochLeader, \* Set of leaders in every epoch. proposalMsgsLog, \* Set of all broadcast messages. violatedInvariants \* Check whether there are conditions \* contrary to the facts. \* Variable used for recording critical data, \* to constrain state space or update values. VARIABLE recorder \* Consists: members of Parameters and pc, values. \* Form is record: \* [pc, nTransaction, maxEpoch, nTimeout, nRestart, nClientRequest] serverVars == <> leaderVars == <> followerVars == connectInfo electionVars == leaderOracle msgVars == msgs verifyVars == <> vars == <> ----------------------------------------------------------------------------- \* Return the maximum value from the set S Maximum(S) == IF S = {} THEN -1 ELSE CHOOSE n \in S: \A m \in S: n >= m \* Return the minimum value from the set S Minimum(S) == IF S = {} THEN -1 ELSE CHOOSE n \in S: \A m \in S: n <= m \* Check server state IsLeader(s) == state[s] = LEADING IsFollower(s) == state[s] = FOLLOWING IsLooking(s) == state[s] = LOOKING \* Check if s is a quorum IsQuorum(s) == s \in Quorums IsMyLearner(i, j) == j \in learners[i] IsMyLeader(i, j) == connectInfo[i] = j HasNoLeader(i) == connectInfo[i] = NullPoint HasLeader(i) == connectInfo[i] /= NullPoint ----------------------------------------------------------------------------- \* FALSE: zxid1 <= zxid2; TRUE: zxid1 > zxid2 ZxidCompare(zxid1, zxid2) == \/ zxid1[1] > zxid2[1] \/ /\ zxid1[1] = zxid2[1] /\ zxid1[2] > zxid2[2] ZxidEqual(zxid1, zxid2) == zxid1[1] = zxid2[1] /\ zxid1[2] = zxid2[2] TxnZxidEqual(txn, z) == txn.zxid[1] = z[1] /\ txn.zxid[2] = z[2] TxnEqual(txn1, txn2) == /\ ZxidEqual(txn1.zxid, txn2.zxid) /\ txn1.value = txn2.value EpochPrecedeInTxn(txn1, txn2) == txn1.zxid[1] < txn2.zxid[1] ----------------------------------------------------------------------------- \* Actions about recorder GetParameter(p) == IF p \in DOMAIN Parameters THEN Parameters[p] ELSE 0 GetRecorder(p) == IF p \in DOMAIN recorder THEN recorder[p] ELSE 0 RecorderGetHelper(m) == (m :> recorder[m]) RecorderIncHelper(m) == (m :> recorder[m] + 1) RecorderIncTimeout == RecorderIncHelper("nTimeout") RecorderGetTimeout == RecorderGetHelper("nTimeout") RecorderIncRestart == RecorderIncHelper("nRestart") RecorderGetRestart == RecorderGetHelper("nRestart") RecorderSetTransactionNum(pc) == ("nTransaction" :> IF pc[1] = "LeaderProcessRequest" THEN LET s == CHOOSE i \in Server: \A j \in Server: Len(history'[i]) >= Len(history'[j]) IN Len(history'[s]) ELSE recorder["nTransaction"]) RecorderSetMaxEpoch(pc) == ("maxEpoch" :> IF pc[1] = "LeaderProcessCEPOCH" THEN LET s == CHOOSE i \in Server: \A j \in Server: acceptedEpoch'[i] >= acceptedEpoch'[j] IN acceptedEpoch'[s] ELSE recorder["maxEpoch"]) RecorderSetRequests(pc) == ("nClientRequest" :> IF pc[1] = "LeaderProcessRequest" THEN recorder["nClientRequest"] + 1 ELSE recorder["nClientRequest"] ) RecorderSetPc(pc) == ("pc" :> pc) RecorderSetFailure(pc) == CASE pc[1] = "Timeout" -> RecorderIncTimeout @@ RecorderGetRestart [] pc[1] = "LeaderTimeout" -> RecorderIncTimeout @@ RecorderGetRestart [] pc[1] = "FollowerTimeout" -> RecorderIncTimeout @@ RecorderGetRestart [] pc[1] = "Restart" -> RecorderIncTimeout @@ RecorderIncRestart [] OTHER -> RecorderGetTimeout @@ RecorderGetRestart UpdateRecorder(pc) == recorder' = RecorderSetFailure(pc) @@ RecorderSetTransactionNum(pc) @@ RecorderSetMaxEpoch(pc) @@ RecorderSetPc(pc) @@ RecorderSetRequests(pc) @@ recorder UnchangeRecorder == UNCHANGED recorder CheckParameterHelper(n, p, Comp(_,_)) == IF p \in DOMAIN Parameters THEN Comp(n, Parameters[p]) ELSE TRUE CheckParameterLimit(n, p) == CheckParameterHelper(n, p, LAMBDA i, j: i < j) CheckTimeout == CheckParameterLimit(recorder.nTimeout, "MaxTimeoutFailures") CheckTransactionNum == CheckParameterLimit(recorder.nTransaction, "MaxTransactionNum") CheckEpoch == CheckParameterLimit(recorder.maxEpoch, "MaxEpoch") CheckRestart == /\ CheckTimeout /\ CheckParameterLimit(recorder.nRestart, "MaxRestarts") CheckStateConstraints == CheckTimeout /\ CheckTransactionNum /\ CheckEpoch /\ CheckRestart ----------------------------------------------------------------------------- \* Actions about network PendingCEPOCH(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = CEPOCH PendingNEWEPOCH(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = NEWEPOCH PendingACKEPOCH(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = ACKEPOCH PendingNEWLEADER(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = NEWLEADER PendingACKLD(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = ACKLD PendingCOMMITLD(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = COMMITLD PendingPROPOSE(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = PROPOSE PendingACK(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = ACK PendingCOMMIT(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = COMMIT \* Add a message to msgs - add a message m to msgs. Send(i, j, m) == msgs' = [msgs EXCEPT ![i][j] = Append(msgs[i][j], m)] \* Remove a message from msgs - discard head of msgs. Discard(i, j) == msgs' = IF msgs[i][j] /= << >> THEN [msgs EXCEPT ![i][j] = Tail(msgs[i][j])] ELSE msgs \* Combination of Send and Discard - discard head of msgs[j][i] and add m into msgs. Reply(i, j, m) == msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i][j] = Append(msgs[i][j], m)] \* Shuffle input buffer. Clean(i, j) == msgs' = [msgs EXCEPT ![j][i] = << >>, ![i][j] = << >>] CleanInputBuffer(S) == msgs' = [s \in Server |-> [v \in Server |-> IF v \in S THEN << >> ELSE msgs[s][v] ] ] \* Leader broadcasts a message PROPOSE to all other servers in Q. \* Note: In paper, Q is fuzzy. We think servers who leader broadcasts NEWLEADER to \* should receive every PROPOSE. So we consider ackeRecv as Q. \* Since we let ackeRecv = Q, there may exist some follower receiving COMMIT before \* COMMITLD, and zxid in COMMIT later than zxid in COMMITLD. To avoid this situation, \* if f \in ackeRecv but \notin ackldRecv, f should not receive COMMIT until \* f \in ackldRecv and receives COMMITLD. Broadcast(i, m) == LET ackeRecv_quorum == {a \in ackeRecv[i]: a.connected = TRUE } sid_ackeRecv == { a.sid: a \in ackeRecv_quorum } IN msgs' = [msgs EXCEPT ![i] = [v \in Server |-> IF /\ v \in sid_ackeRecv /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] \* Since leader decides to broadcasts message COMMIT when processing ACK, so \* we need to discard ACK and broadcast COMMIT. \* Here Q is ackldRecv, because we assume that f should not receive COMMIT until \* f receives COMMITLD. DiscardAndBroadcast(i, j, m) == LET ackldRecv_quorum == {a \in ackldRecv[i]: a.connected = TRUE } sid_ackldRecv == { a.sid: a \in ackldRecv_quorum } IN msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in sid_ackldRecv /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] \* Leader broadcasts LEADERINFO to all other servers in cepochRecv. DiscardAndBroadcastNEWEPOCH(i, j, m) == LET new_cepochRecv_quorum == {c \in cepochRecv'[i]: c.connected = TRUE } new_sid_cepochRecv == { c.sid: c \in new_cepochRecv_quorum } IN msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in new_sid_cepochRecv /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] \* Leader broadcasts NEWLEADER to all other servers in ackeRecv. DiscardAndBroadcastNEWLEADER(i, j, m) == LET new_ackeRecv_quorum == {a \in ackeRecv'[i]: a.connected = TRUE } new_sid_ackeRecv == { a.sid: a \in new_ackeRecv_quorum } IN msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in new_sid_ackeRecv /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] \* Leader broadcasts COMMITLD to all other servers in ackldRecv. DiscardAndBroadcastCOMMITLD(i, j, m) == LET new_ackldRecv_quorum == {a \in ackldRecv'[i]: a.connected = TRUE } new_sid_ackldRecv == { a.sid: a \in new_ackldRecv_quorum } IN msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in new_sid_ackldRecv /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] ----------------------------------------------------------------------------- \* Define initial values for all variables InitServerVars == /\ state = [s \in Server |-> LOOKING] /\ zabState = [s \in Server |-> ELECTION] /\ acceptedEpoch = [s \in Server |-> 0] /\ currentEpoch = [s \in Server |-> 0] /\ history = [s \in Server |-> << >>] /\ lastCommitted = [s \in Server |-> [ index |-> 0, zxid |-> <<0, 0>> ] ] InitLeaderVars == /\ learners = [s \in Server |-> {}] /\ cepochRecv = [s \in Server |-> {}] /\ ackeRecv = [s \in Server |-> {}] /\ ackldRecv = [s \in Server |-> {}] /\ sendCounter = [s \in Server |-> 0] InitFollowerVars == connectInfo = [s \in Server |-> NullPoint] InitElectionVars == leaderOracle = NullPoint InitMsgVars == msgs = [s \in Server |-> [v \in Server |-> << >>] ] InitVerifyVars == /\ proposalMsgsLog = {} /\ epochLeader = [i \in 1..MAXEPOCH |-> {} ] /\ violatedInvariants = [stateInconsistent |-> FALSE, proposalInconsistent |-> FALSE, commitInconsistent |-> FALSE, ackInconsistent |-> FALSE, messageIllegal |-> FALSE ] InitRecorder == recorder = [nTimeout |-> 0, nTransaction |-> 0, maxEpoch |-> 0, nRestart |-> 0, pc |-> <<"Init">>, nClientRequest |-> 0] Init == /\ InitServerVars /\ InitLeaderVars /\ InitFollowerVars /\ InitElectionVars /\ InitVerifyVars /\ InitMsgVars /\ InitRecorder ----------------------------------------------------------------------------- \* Utils in state switching FollowerShutdown(i) == /\ state' = [state EXCEPT ![i] = LOOKING] /\ zabState' = [zabState EXCEPT ![i] = ELECTION] /\ connectInfo' = [connectInfo EXCEPT ![i] = NullPoint] LeaderShutdown(i) == /\ LET S == learners[i] IN /\ state' = [s \in Server |-> IF s \in S THEN LOOKING ELSE state[s] ] /\ zabState' = [s \in Server |-> IF s \in S THEN ELECTION ELSE zabState[s] ] /\ connectInfo' = [s \in Server |-> IF s \in S THEN NullPoint ELSE connectInfo[s] ] /\ CleanInputBuffer(S) /\ learners' = [learners EXCEPT ![i] = {}] SwitchToFollower(i) == /\ state' = [state EXCEPT ![i] = FOLLOWING] /\ zabState' = [zabState EXCEPT ![i] = DISCOVERY] SwitchToLeader(i) == /\ state' = [state EXCEPT ![i] = LEADING] /\ zabState' = [zabState EXCEPT ![i] = DISCOVERY] /\ learners' = [learners EXCEPT ![i] = {i}] /\ cepochRecv' = [cepochRecv EXCEPT ![i] = { [ sid |-> i, connected |-> TRUE, epoch |-> acceptedEpoch[i] ] }] /\ ackeRecv' = [ackeRecv EXCEPT ![i] = { [ sid |-> i, connected |-> TRUE, peerLastEpoch |-> currentEpoch[i], peerHistory |-> history[i] ] }] /\ ackldRecv' = [ackldRecv EXCEPT ![i] = { [ sid |-> i, connected |-> TRUE ] }] /\ sendCounter' = [sendCounter EXCEPT ![i] = 0] RemoveCepochRecv(set, sid) == LET sid_cepochRecv == {s.sid: s \in set} IN IF sid \notin sid_cepochRecv THEN set ELSE LET info == CHOOSE s \in set: s.sid = sid new_info == [ sid |-> sid, connected |-> FALSE, epoch |-> info.epoch ] IN (set \ {info}) \union {new_info} RemoveAckeRecv(set, sid) == LET sid_ackeRecv == {s.sid: s \in set} IN IF sid \notin sid_ackeRecv THEN set ELSE LET info == CHOOSE s \in set: s.sid = sid new_info == [ sid |-> sid, connected |-> FALSE, peerLastEpoch |-> info.peerLastEpoch, peerHistory |-> info.peerHistory ] IN (set \ {info}) \union {new_info} RemoveAckldRecv(set, sid) == LET sid_ackldRecv == {s.sid: s \in set} IN IF sid \notin sid_ackldRecv THEN set ELSE LET info == CHOOSE s \in set: s.sid = sid new_info == [ sid |-> sid, connected |-> FALSE ] IN (set \ {info}) \union {new_info} RemoveLearner(i, j) == /\ learners' = [learners EXCEPT ![i] = @ \ {j}] /\ cepochRecv' = [cepochRecv EXCEPT ![i] = RemoveCepochRecv(@, j) ] /\ ackeRecv' = [ackeRecv EXCEPT ![i] = RemoveAckeRecv(@, j) ] /\ ackldRecv' = [ackldRecv EXCEPT ![i] = RemoveAckldRecv(@, j) ] ----------------------------------------------------------------------------- \* Actions of election UpdateLeader(i) == /\ IsLooking(i) /\ leaderOracle /= i /\ leaderOracle' = i /\ SwitchToLeader(i) /\ UNCHANGED <> /\ UpdateRecorder(<<"UpdateLeader", i>>) FollowLeader(i) == /\ IsLooking(i) /\ leaderOracle /= NullPoint /\ \/ /\ leaderOracle = i /\ SwitchToLeader(i) \/ /\ leaderOracle /= i /\ SwitchToFollower(i) /\ UNCHANGED leaderVars /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowLeader", i>>) ----------------------------------------------------------------------------- (* Actions of situation error. Situation error in protocol spec is different from latter specs. This is for compressing state space, we focus on results from external events (e.g. network partition, node failure, etc.), so we do not need to add variables and actions about network conditions and node conditions. It is reasonable that we have action 'Restart' but no 'Crash', because when a node does not execute any internal events after restarting, this is equivalent to executing a crash. *) \* Timeout between leader and follower. Timeout(i, j) == /\ CheckTimeout \* test restrictions of timeout /\ IsLeader(i) /\ IsMyLearner(i, j) /\ IsFollower(j) /\ IsMyLeader(j, i) /\ LET newLearners == learners[i] \ {j} IN \/ /\ IsQuorum(newLearners) \* just remove this learner /\ RemoveLearner(i, j) /\ FollowerShutdown(j) /\ Clean(i, j) \/ /\ ~IsQuorum(newLearners) \* leader switches to looking /\ LeaderShutdown(i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"Timeout", i, j>>) Restart(i) == /\ CheckRestart \* test restrictions of restart /\ \/ /\ IsLooking(i) /\ UNCHANGED <> \/ /\ IsFollower(i) /\ LET connectedWithLeader == HasLeader(i) IN \/ /\ connectedWithLeader /\ LET leader == connectInfo[i] newLearners == learners[leader] \ {i} IN \/ /\ IsQuorum(newLearners) \* leader remove learner i /\ RemoveLearner(leader, i) /\ FollowerShutdown(i) /\ Clean(leader, i) \/ /\ ~IsQuorum(newLearners) \* leader switches to looking /\ LeaderShutdown(leader) /\ UNCHANGED <> \/ /\ ~connectedWithLeader /\ FollowerShutdown(i) /\ CleanInputBuffer({i}) /\ UNCHANGED <> \/ /\ IsLeader(i) /\ LeaderShutdown(i) /\ UNCHANGED <> /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> 0, zxid |-> <<0, 0>> ] ] /\ UNCHANGED <> /\ UpdateRecorder(<<"Restart", i>>) ----------------------------------------------------------------------------- (* Establish connection between leader and follower. *) ConnectAndFollowerSendCEPOCH(i, j) == /\ IsLeader(i) /\ \lnot IsMyLearner(i, j) /\ IsFollower(j) /\ HasNoLeader(j) /\ leaderOracle = i /\ learners' = [learners EXCEPT ![i] = @ \union {j}] /\ connectInfo' = [connectInfo EXCEPT ![j] = i] /\ Send(j, i, [ mtype |-> CEPOCH, mepoch |-> acceptedEpoch[j] ]) \* contains f.p /\ UNCHANGED <> /\ UpdateRecorder(<<"ConnectAndFollowerSendCEPOCH", i, j>>) CepochRecvQuorumFormed(i) == LET sid_cepochRecv == {c.sid: c \in cepochRecv[i]} IN IsQuorum(sid_cepochRecv) CepochRecvBecomeQuorum(i) == LET sid_cepochRecv == {c.sid: c \in cepochRecv'[i]} IN IsQuorum(sid_cepochRecv) UpdateCepochRecv(oldSet, sid, peerEpoch) == LET sid_set == {s.sid: s \in oldSet} IN IF sid \in sid_set THEN LET old_info == CHOOSE info \in oldSet: info.sid = sid new_info == [ sid |-> sid, connected |-> TRUE, epoch |-> peerEpoch ] IN ( oldSet \ {old_info} ) \union {new_info} ELSE LET follower_info == [ sid |-> sid, connected |-> TRUE, epoch |-> peerEpoch ] IN oldSet \union {follower_info} \* Determine new e' in this round from a quorum of CEPOCH. DetermineNewEpoch(i) == LET epoch_cepochRecv == {c.epoch: c \in cepochRecv'[i]} IN Maximum(epoch_cepochRecv) + 1 (* Leader waits for receiving FOLLOWERINFO from a quorum including itself, and chooses a new epoch e' as its own epoch and broadcasts NEWEPOCH. *) LeaderProcessCEPOCH(i, j) == /\ CheckEpoch \* test restrictions of max epoch /\ IsLeader(i) /\ PendingCEPOCH(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) IN /\ infoOk /\ \/ \* 1. has not broadcast NEWEPOCH /\ ~CepochRecvQuorumFormed(i) /\ \/ /\ zabState[i] = DISCOVERY /\ UNCHANGED violatedInvariants \/ /\ zabState[i] /= DISCOVERY /\ PrintT("Exception: CepochRecvQuorumFormed false," \o " while zabState not DISCOVERY.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ cepochRecv' = [cepochRecv EXCEPT ![i] = UpdateCepochRecv(@, j, msg.mepoch) ] /\ \/ \* 1.1. cepochRecv becomes quorum, \* then determine e' and broadcasts NEWEPOCH in Q. /\ CepochRecvBecomeQuorum(i) /\ acceptedEpoch' = [acceptedEpoch EXCEPT ![i] = DetermineNewEpoch(i)] /\ LET m == [ mtype |-> NEWEPOCH, mepoch |-> acceptedEpoch'[i] ] IN DiscardAndBroadcastNEWEPOCH(i, j, m) \/ \* 1.2. cepochRecv still not quorum. /\ ~CepochRecvBecomeQuorum(i) /\ Discard(j, i) /\ UNCHANGED acceptedEpoch \/ \* 2. has broadcast NEWEPOCH /\ CepochRecvQuorumFormed(i) /\ cepochRecv' = [cepochRecv EXCEPT ![i] = UpdateCepochRecv(@, j, msg.mepoch) ] /\ Reply(i, j, [ mtype |-> NEWEPOCH, mepoch |-> acceptedEpoch[i] ]) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessCEPOCH", i, j>>) (* Follower receives LEADERINFO. If newEpoch >= acceptedEpoch, then follower updates acceptedEpoch and sends ACKEPOCH back, containing currentEpoch and history. After this, zabState turns to SYNC. *) FollowerProcessNEWEPOCH(i, j) == /\ IsFollower(i) /\ PendingNEWEPOCH(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) stateOk == zabState[i] = DISCOVERY epochOk == msg.mepoch >= acceptedEpoch[i] IN /\ infoOk /\ \/ \* 1. Normal case /\ epochOk /\ \/ /\ stateOk /\ acceptedEpoch' = [acceptedEpoch EXCEPT ![i] = msg.mepoch] /\ LET m == [ mtype |-> ACKEPOCH, mepoch |-> currentEpoch[i], mhistory |-> history[i] ] IN Reply(i, j, m) /\ zabState' = [zabState EXCEPT ![i] = SYNCHRONIZATION] /\ UNCHANGED violatedInvariants \/ /\ ~stateOk /\ PrintT("Exception: Follower receives NEWEPOCH," \o " whileZabState not DISCOVERY.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED <> \/ \* 2. Abnormal case - go back to election /\ ~epochOk /\ FollowerShutdown(i) /\ LET leader == connectInfo[i] IN /\ Clean(i, leader) /\ RemoveLearner(leader, i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessNEWEPOCH", i, j>>) AckeRecvQuorumFormed(i) == LET sid_ackeRecv == {a.sid: a \in ackeRecv[i]} IN IsQuorum(sid_ackeRecv) AckeRecvBecomeQuorum(i) == LET sid_ackeRecv == {a.sid: a \in ackeRecv'[i]} IN IsQuorum(sid_ackeRecv) UpdateAckeRecv(oldSet, sid, peerEpoch, peerHistory) == LET sid_set == {s.sid: s \in oldSet} follower_info == [ sid |-> sid, connected |-> TRUE, peerLastEpoch |-> peerEpoch, peerHistory |-> peerHistory ] IN IF sid \in sid_set THEN LET old_info == CHOOSE info \in oldSet: info.sid = sid IN (oldSet \ {old_info}) \union {follower_info} ELSE oldSet \union {follower_info} \* for checking invariants RECURSIVE SetPacketsForChecking(_,_,_,_,_,_) SetPacketsForChecking(set, src, ep, his, cur, end) == IF cur > end THEN set ELSE LET m_proposal == [ source |-> src, epoch |-> ep, zxid |-> his[cur].zxid, data |-> his[cur].value ] IN SetPacketsForChecking((set \union {m_proposal}), src, ep, his, cur + 1, end) LastZxidOfHistory(his) == IF Len(his) = 0 THEN <<0, 0>> ELSE his[Len(his)].zxid \* TRUE: f1.a > f2.a or (f1.a = fa.a and f1.zxid >= f2.zxid) MoreResentOrEqual(ss1, ss2) == \/ ss1.currentEpoch > ss2.currentEpoch \/ /\ ss1.currentEpoch = ss2.currentEpoch /\ ~ZxidCompare(ss2.lastZxid, ss1.lastZxid) \* Determine initial history Ie' in this round from a quorum of ACKEPOCH. DetermineInitialHistory(i) == LET set == ackeRecv'[i] ss_set == { [ sid |-> a.sid, currentEpoch |-> a.peerLastEpoch, lastZxid |-> LastZxidOfHistory(a.peerHistory) ] : a \in set } selected == CHOOSE ss \in ss_set: \A ss1 \in (ss_set \ {ss}): MoreResentOrEqual(ss, ss1) info == CHOOSE f \in set: f.sid = selected.sid IN info.peerHistory RECURSIVE InitAcksidHelper(_,_) InitAcksidHelper(txns, src) == IF Len(txns) = 0 THEN << >> ELSE LET oldTxn == txns[1] newTxn == [ zxid |-> oldTxn.zxid, value |-> oldTxn.value, ackSid |-> {src}, epoch |-> oldTxn.epoch ] IN <> \o InitAcksidHelper( Tail(txns), src) \* Atomically let all txns in initial history contain self's acks. InitAcksid(i, his) == InitAcksidHelper(his, i) (* Leader waits for receiving ACKEPOPCH from a quorum, and determines initialHistory according to history of whom has most recent state summary from them. After this, leader's zabState turns to SYNCHRONIZATION. *) LeaderProcessACKEPOCH(i, j) == /\ IsLeader(i) /\ PendingACKEPOCH(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) IN /\ infoOk /\ \/ \* 1. has broadcast NEWLEADER /\ AckeRecvQuorumFormed(i) /\ ackeRecv' = [ackeRecv EXCEPT ![i] = UpdateAckeRecv(@, j, msg.mepoch, msg.mhistory) ] /\ LET toSend == history[i] \* contains (Ie', Be') m == [ mtype |-> NEWLEADER, mepoch |-> acceptedEpoch[i], mhistory |-> toSend ] set_forChecking == SetPacketsForChecking({ }, i, acceptedEpoch[i], toSend, 1, Len(toSend)) IN /\ Reply(i, j, m) /\ proposalMsgsLog' = proposalMsgsLog \union set_forChecking /\ UNCHANGED <> \/ \* 2. has not broadcast NEWLEADER /\ ~AckeRecvQuorumFormed(i) /\ \/ /\ zabState[i] = DISCOVERY /\ UNCHANGED violatedInvariants \/ /\ zabState[i] /= DISCOVERY /\ PrintT("Exception: AckeRecvQuorumFormed false," \o " while zabState not DISCOVERY.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ ackeRecv' = [ackeRecv EXCEPT ![i] = UpdateAckeRecv(@, j, msg.mepoch, msg.mhistory) ] /\ \/ \* 2.1. ackeRecv becomes quorum, determine Ie' \* and broacasts NEWLEADER in Q. (l.1.2 + l.2.1) /\ AckeRecvBecomeQuorum(i) /\ \* Update f.a LET newLeaderEpoch == acceptedEpoch[i] IN /\ currentEpoch' = [currentEpoch EXCEPT ![i] = newLeaderEpoch] /\ epochLeader' = [epochLeader EXCEPT ![newLeaderEpoch] = @ \union {i} ] \* for checking invariants /\ \* Determine initial history Ie' LET initialHistory == DetermineInitialHistory(i) IN history' = [history EXCEPT ![i] = InitAcksid(i, initialHistory) ] /\ \* Update zabState zabState' = [zabState EXCEPT ![i] = SYNCHRONIZATION] /\ \* Broadcast NEWLEADER with (e', Ie') LET toSend == history'[i] m == [ mtype |-> NEWLEADER, mepoch |-> acceptedEpoch[i], mhistory |-> toSend ] set_forChecking == SetPacketsForChecking({ }, i, acceptedEpoch[i], toSend, 1, Len(toSend)) IN /\ DiscardAndBroadcastNEWLEADER(i, j, m) /\ proposalMsgsLog' = proposalMsgsLog \union set_forChecking \/ \* 2.2. ackeRecv still not quorum. /\ ~AckeRecvBecomeQuorum(i) /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessACKEPOCH", i, j>>) ----------------------------------------------------------------------------- (* Follower receives NEWLEADER. Update f.a and history. *) FollowerProcessNEWLEADER(i, j) == /\ IsFollower(i) /\ PendingNEWLEADER(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) epochOk == acceptedEpoch[i] = msg.mepoch stateOk == zabState[i] = SYNCHRONIZATION IN /\ infoOk /\ \/ \* 1. f.p not equals e', starts a new iteration. /\ ~epochOk /\ FollowerShutdown(i) /\ LET leader == connectInfo[i] IN /\ Clean(i, leader) /\ RemoveLearner(leader, i) /\ UNCHANGED <> \/ \* 2. f.p equals e'. /\ epochOk /\ \/ /\ stateOk /\ UNCHANGED violatedInvariants \/ /\ ~stateOk /\ PrintT("Exception: Follower receives NEWLEADER," \o " whileZabState not SYNCHRONIZATION.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ currentEpoch' = [currentEpoch EXCEPT ![i] = acceptedEpoch[i]] /\ history' = [history EXCEPT ![i] = msg.mhistory] \* no need to care ackSid /\ LET m == [ mtype |-> ACKLD, mzxid |-> LastZxidOfHistory(history'[i]) ] IN Reply(i, j, m) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessNEWLEADER", i, j>>) AckldRecvQuorumFormed(i) == LET sid_ackldRecv == {a.sid: a \in ackldRecv[i]} IN IsQuorum(sid_ackldRecv) AckldRecvBecomeQuorum(i) == LET sid_ackldRecv == {a.sid: a \in ackldRecv'[i]} IN IsQuorum(sid_ackldRecv) UpdateAckldRecv(oldSet, sid) == LET sid_set == {s.sid: s \in oldSet} follower_info == [ sid |-> sid, connected |-> TRUE ] IN IF sid \in sid_set THEN LET old_info == CHOOSE info \in oldSet: info.sid = sid IN (oldSet \ {old_info}) \union {follower_info} ELSE oldSet \union {follower_info} LastZxid(i) == LastZxidOfHistory(history[i]) RECURSIVE UpdateAcksidHelper(_,_,_) UpdateAcksidHelper(txns, target, endZxid) == IF Len(txns) = 0 THEN << >> ELSE LET oldTxn == txns[1] IN IF ZxidCompare(oldTxn.zxid, endZxid) THEN txns ELSE LET newTxn == [ zxid |-> oldTxn.zxid, value |-> oldTxn.value, ackSid |-> IF target \in oldTxn.ackSid THEN oldTxn.ackSid ELSE oldTxn.ackSid \union {target}, epoch |-> oldTxn.epoch ] IN <> \o UpdateAcksidHelper( Tail(txns), target, endZxid) \* Atomically add ackSid of one learner according to zxid in ACKLD. UpdateAcksid(his, target, endZxid) == UpdateAcksidHelper(his, target, endZxid) (* Leader waits for receiving ACKLD from a quorum including itself, and broadcasts COMMITLD and turns to BROADCAST. *) LeaderProcessACKLD(i, j) == /\ IsLeader(i) /\ PendingACKLD(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) IN /\ infoOk /\ \/ \* 1. has not broadcast COMMITLD /\ ~AckldRecvQuorumFormed(i) /\ \/ /\ zabState[i] = SYNCHRONIZATION /\ UNCHANGED violatedInvariants \/ /\ zabState[i] /= SYNCHRONIZATION /\ PrintT("Exception: AckldRecvQuorumFormed false," \o " while zabState not SYNCHRONIZATION.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ ackldRecv' = [ackldRecv EXCEPT ![i] = UpdateAckldRecv(@, j) ] /\ history' = [history EXCEPT ![i] = UpdateAcksid(@, j, msg.mzxid)] /\ \/ \* 1.1. ackldRecv becomes quorum, \* then broadcasts COMMITLD and turns to BROADCAST. /\ AckldRecvBecomeQuorum(i) /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> Len(history[i]), zxid |-> LastZxid(i) ] ] /\ zabState' = [zabState EXCEPT ![i] = BROADCAST] /\ LET m == [ mtype |-> COMMITLD, mzxid |-> LastZxid(i) ] IN DiscardAndBroadcastCOMMITLD(i, j, m) \/ \* 1.2. ackldRecv still not quorum. /\ ~AckldRecvBecomeQuorum(i) /\ Discard(j, i) /\ UNCHANGED <> \/ \* 2. has broadcast COMMITLD /\ AckldRecvQuorumFormed(i) /\ \/ /\ zabState[i] = BROADCAST /\ UNCHANGED violatedInvariants \/ /\ zabState[i] /= BROADCAST /\ PrintT("Exception: AckldRecvQuorumFormed true," \o " while zabState not BROADCAST.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ ackldRecv' = [ackldRecv EXCEPT ![i] = UpdateAckldRecv(@, j) ] /\ history' = [history EXCEPT ![i] = UpdateAcksid(@, j, msg.mzxid)] /\ Reply(i, j, [ mtype |-> COMMITLD, mzxid |-> lastCommitted[i].zxid ]) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessACKLD", i, j>>) RECURSIVE ZxidToIndexHepler(_,_,_,_) ZxidToIndexHepler(his, zxid, cur, appeared) == IF cur > Len(his) THEN cur ELSE IF TxnZxidEqual(his[cur], zxid) THEN CASE appeared = TRUE -> -1 [] OTHER -> Minimum( { cur, ZxidToIndexHepler(his, zxid, cur + 1, TRUE) } ) ELSE ZxidToIndexHepler(his, zxid, cur + 1, appeared) \* return -1: this zxid appears at least twice. Len(his) + 1: does not exist. \* 1 - Len(his): exists and appears just once. ZxidToIndex(his, zxid) == IF ZxidEqual( zxid, <<0, 0>> ) THEN 0 ELSE IF Len(his) = 0 THEN 1 ELSE LET len == Len(his) IN IF \E idx \in 1..len: TxnZxidEqual(his[idx], zxid) THEN ZxidToIndexHepler(his, zxid, 1, FALSE) ELSE len + 1 (* Follower receives COMMITLD. Commit all txns. *) FollowerProcessCOMMITLD(i, j) == /\ IsFollower(i) /\ PendingCOMMITLD(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) index == IF ZxidEqual(msg.mzxid, <<0, 0>>) THEN 0 ELSE ZxidToIndex(history[i], msg.mzxid) logOk == index >= 0 /\ index <= Len(history[i]) IN /\ infoOk /\ \/ /\ logOk /\ UNCHANGED violatedInvariants \/ /\ ~logOk /\ PrintT("Exception: zxid in COMMITLD not exists in history.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.proposalInconsistent = TRUE] /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> index, zxid |-> msg.mzxid ] ] /\ zabState' = [zabState EXCEPT ![i] = BROADCAST] /\ Discard(j, i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessCOMMITLD", i, j>>) ---------------------------------------------------------------------------- IncZxid(s, zxid) == IF currentEpoch[s] = zxid[1] THEN <> ELSE <> (* Leader receives client request. Note: In production, any server in traffic can receive requests and forward it to leader if necessary. We choose to let leader be the sole one who can receive write requests, to simplify spec and keep correctness at the same time. *) LeaderProcessRequest(i) == /\ CheckTransactionNum \* test restrictions of transaction num /\ IsLeader(i) /\ zabState[i] = BROADCAST /\ LET request_value == GetRecorder("nClientRequest") \* unique value newTxn == [ zxid |-> IncZxid(i, LastZxid(i)), value |-> request_value, ackSid |-> {i}, epoch |-> currentEpoch[i] ] IN history' = [history EXCEPT ![i] = Append(@, newTxn) ] /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessRequest", i>>) \* Latest counter existing in history. CurrentCounter(i) == IF LastZxid(i)[1] = currentEpoch[i] THEN LastZxid(i)[2] ELSE 0 (* Leader broadcasts PROPOSE when sendCounter < currentCounter. *) LeaderBroadcastPROPOSE(i) == /\ IsLeader(i) /\ zabState[i] = BROADCAST /\ sendCounter[i] < CurrentCounter(i) \* there exists proposal to be sent /\ LET toSendCounter == sendCounter[i] + 1 toSendZxid == <> toSendIndex == ZxidToIndex(history[i], toSendZxid) toSendTxn == history[i][toSendIndex] m_proposal == [ mtype |-> PROPOSE, mzxid |-> toSendTxn.zxid, mdata |-> toSendTxn.value ] m_proposal_forChecking == [ source |-> i, epoch |-> currentEpoch[i], zxid |-> toSendTxn.zxid, data |-> toSendTxn.value ] IN /\ sendCounter' = [sendCounter EXCEPT ![i] = toSendCounter] /\ Broadcast(i, m_proposal) /\ proposalMsgsLog' = proposalMsgsLog \union {m_proposal_forChecking} /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderBroadcastPROPOSE", i>>) IsNextZxid(curZxid, nextZxid) == \/ \* first PROPOSAL in this epoch /\ nextZxid[2] = 1 /\ curZxid[1] < nextZxid[1] \/ \* not first PROPOSAL in this epoch /\ nextZxid[2] > 1 /\ curZxid[1] = nextZxid[1] /\ curZxid[2] + 1 = nextZxid[2] (* Follower processes PROPOSE, saves it in history and replies ACK. *) FollowerProcessPROPOSE(i, j) == /\ IsFollower(i) /\ PendingPROPOSE(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) isNext == IsNextZxid(LastZxid(i), msg.mzxid) newTxn == [ zxid |-> msg.mzxid, value |-> msg.mdata, ackSid |-> {}, epoch |-> currentEpoch[i] ] m_ack == [ mtype |-> ACK, mzxid |-> msg.mzxid ] IN /\ infoOk /\ \/ /\ isNext /\ history' = [history EXCEPT ![i] = Append(@, newTxn)] /\ Reply(i, j, m_ack) /\ UNCHANGED violatedInvariants \/ /\ ~isNext /\ LET index == ZxidToIndex(history[i], msg.mzxid) exist == index > 0 /\ index <= Len(history[i]) IN \/ /\ exist /\ UNCHANGED violatedInvariants \/ /\ ~exist /\ PrintT("Exception: Follower receives PROPOSE, while" \o " txn is neither the next nor exists in history.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.proposalInconsistent = TRUE] /\ Discard(j, i) /\ UNCHANGED history /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessPROPOSE", i, j>>) LeaderTryToCommit(s, index, zxid, newTxn, follower) == LET allTxnsBeforeCommitted == lastCommitted[s].index >= index - 1 \* Only when all proposals before zxid has been committed, \* this proposal can be permitted to be committed. hasAllQuorums == IsQuorum(newTxn.ackSid) \* In order to be committed, a proposal must be accepted \* by a quorum. ordered == lastCommitted[s].index + 1 = index \* Commit proposals in order. IN \/ /\ \* Current conditions do not satisfy committing the proposal. \/ ~allTxnsBeforeCommitted \/ ~hasAllQuorums /\ Discard(follower, s) /\ UNCHANGED <> \/ /\ allTxnsBeforeCommitted /\ hasAllQuorums /\ \/ /\ ~ordered /\ PrintT("Warn: Committing zxid " \o ToString(zxid) \o " not first.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE] \/ /\ ordered /\ UNCHANGED violatedInvariants /\ lastCommitted' = [lastCommitted EXCEPT ![s] = [ index |-> index, zxid |-> zxid ] ] /\ LET m_commit == [ mtype |-> COMMIT, mzxid |-> zxid ] IN DiscardAndBroadcast(s, follower, m_commit) LastAckIndexFromFollower(i, j) == LET set_index == {idx \in 1..Len(history[i]): j \in history[i][idx].ackSid } IN Maximum(set_index) (* Leader Keeps a count of acks for a particular proposal, and try to commit the proposal. If committed, COMMIT of proposal will be broadcast. *) LeaderProcessACK(i, j) == /\ IsLeader(i) /\ PendingACK(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) index == ZxidToIndex(history[i], msg.mzxid) exist == index >= 1 /\ index <= Len(history[i]) \* proposal exists in history outstanding == lastCommitted[i].index < Len(history[i]) \* outstanding not null hasCommitted == ~ZxidCompare(msg.mzxid, lastCommitted[i].zxid) ackIndex == LastAckIndexFromFollower(i, j) monotonicallyInc == \/ ackIndex = -1 \/ ackIndex + 1 = index IN /\ infoOk /\ \/ /\ exist /\ monotonicallyInc /\ LET txn == history[i][index] txnAfterAddAck == [ zxid |-> txn.zxid, value |-> txn.value, ackSid |-> txn.ackSid \union {j} , epoch |-> txn.epoch ] IN /\ history' = [history EXCEPT ![i][index] = txnAfterAddAck ] /\ \/ /\ \* Note: outstanding is 0. \* / proposal has already been committed. \/ ~outstanding \/ hasCommitted /\ Discard(j, i) /\ UNCHANGED <> \/ /\ outstanding /\ ~hasCommitted /\ LeaderTryToCommit(i, index, msg.mzxid, txnAfterAddAck, j) \/ /\ \/ ~exist \/ ~monotonicallyInc /\ PrintT("Exception: No such zxid. " \o " / ackIndex doesn't inc monotonically.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.ackInconsistent = TRUE] /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessACK", i, j>>) (* Follower processes COMMIT. *) FollowerProcessCOMMIT(i, j) == /\ IsFollower(i) /\ PendingCOMMIT(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) pending == lastCommitted[i].index < Len(history[i]) IN /\ infoOk /\ \/ /\ ~pending /\ PrintT("Warn: Committing zxid without seeing txn.") /\ UNCHANGED <> \/ /\ pending /\ LET firstElement == history[i][lastCommitted[i].index + 1] match == ZxidEqual(firstElement.zxid, msg.mzxid) IN \/ /\ ~match /\ PrintT("Exception: Committing zxid not equals" \o " next pending txn zxid.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE] /\ UNCHANGED lastCommitted \/ /\ match /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> lastCommitted[i].index + 1, zxid |-> firstElement.zxid ] ] /\ UNCHANGED violatedInvariants /\ Discard(j, i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessCOMMIT", i, j>>) ---------------------------------------------------------------------------- (* Used to discard some messages which should not exist in network channel. This action should not be triggered. *) FilterNonexistentMessage(i) == /\ \E j \in Server \ {i}: /\ msgs[j][i] /= << >> /\ LET msg == msgs[j][i][1] IN \/ /\ IsLeader(i) /\ LET infoOk == IsMyLearner(i, j) IN \/ msg.mtype = NEWEPOCH \/ msg.mtype = NEWLEADER \/ msg.mtype = COMMITLD \/ msg.mtype = PROPOSE \/ msg.mtype = COMMIT \/ /\ ~infoOk /\ \/ msg.mtype = CEPOCH \/ msg.mtype = ACKEPOCH \/ msg.mtype = ACKLD \/ msg.mtype = ACK \/ /\ IsFollower(i) /\ LET infoOk == IsMyLeader(i, j) IN \/ msg.mtype = CEPOCH \/ msg.mtype = ACKEPOCH \/ msg.mtype = ACKLD \/ msg.mtype = ACK \/ /\ ~infoOk /\ \/ msg.mtype = NEWEPOCH \/ msg.mtype = NEWLEADER \/ msg.mtype = COMMITLD \/ msg.mtype = PROPOSE \/ msg.mtype = COMMIT \/ IsLooking(i) /\ Discard(j, i) /\ violatedInvariants' = [violatedInvariants EXCEPT !.messageIllegal = TRUE] /\ UNCHANGED <> /\ UnchangeRecorder ---------------------------------------------------------------------------- \* Defines how the variables may transition. Next == (* Election *) \/ \E i \in Server: UpdateLeader(i) \/ \E i \in Server: FollowLeader(i) (* Abnormal situations like failure, network disconnection *) \/ \E i, j \in Server: Timeout(i, j) \/ \E i \in Server: Restart(i) (* Zab module - Discovery and Synchronization part *) \/ \E i, j \in Server: ConnectAndFollowerSendCEPOCH(i, j) \/ \E i, j \in Server: LeaderProcessCEPOCH(i, j) \/ \E i, j \in Server: FollowerProcessNEWEPOCH(i, j) \/ \E i, j \in Server: LeaderProcessACKEPOCH(i, j) \/ \E i, j \in Server: FollowerProcessNEWLEADER(i, j) \/ \E i, j \in Server: LeaderProcessACKLD(i, j) \/ \E i, j \in Server: FollowerProcessCOMMITLD(i, j) (* Zab module - Broadcast part *) \/ \E i \in Server: LeaderProcessRequest(i) \/ \E i \in Server: LeaderBroadcastPROPOSE(i) \/ \E i, j \in Server: FollowerProcessPROPOSE(i, j) \/ \E i, j \in Server: LeaderProcessACK(i, j) \/ \E i, j \in Server: FollowerProcessCOMMIT(i, j) (* An action used to judge whether there are redundant messages in network *) \/ \E i \in Server: FilterNonexistentMessage(i) Spec == Init /\ [][Next]_vars ---------------------------------------------------------------------------- \* Define safety properties of Zab. ShouldNotBeTriggered == \A p \in DOMAIN violatedInvariants: violatedInvariants[p] = FALSE \* There is most one established leader for a certain epoch. Leadership1 == \A i, j \in Server: /\ IsLeader(i) /\ zabState[i] \in {SYNCHRONIZATION, BROADCAST} /\ IsLeader(j) /\ zabState[j] \in {SYNCHRONIZATION, BROADCAST} /\ currentEpoch[i] = currentEpoch[j] => i = j Leadership2 == \A epoch \in 1..MAXEPOCH: Cardinality(epochLeader[epoch]) <= 1 \* PrefixConsistency: The prefix that have been committed \* in history in any process is the same. PrefixConsistency == \A i, j \in Server: LET smaller == Minimum({lastCommitted[i].index, lastCommitted[j].index}) IN \/ smaller = 0 \/ /\ smaller > 0 /\ \A index \in 1..smaller: TxnEqual(history[i][index], history[j][index]) \* Integrity: If some follower delivers one transaction, then some primary has broadcast it. Integrity == \A i \in Server: /\ IsFollower(i) /\ lastCommitted[i].index > 0 => \A idx \in 1..lastCommitted[i].index: \E proposal \in proposalMsgsLog: LET txn_proposal == [ zxid |-> proposal.zxid, value |-> proposal.data ] IN TxnEqual(history[i][idx], txn_proposal) \* Agreement: If some follower f delivers transaction a and some follower f' delivers transaction b, \* then f' delivers a or f delivers b. Agreement == \A i, j \in Server: /\ IsFollower(i) /\ lastCommitted[i].index > 0 /\ IsFollower(j) /\ lastCommitted[j].index > 0 => \A idx1 \in 1..lastCommitted[i].index, idx2 \in 1..lastCommitted[j].index : \/ \E idx_j \in 1..lastCommitted[j].index: TxnEqual(history[j][idx_j], history[i][idx1]) \/ \E idx_i \in 1..lastCommitted[i].index: TxnEqual(history[i][idx_i], history[j][idx2]) \* Total order: If some follower delivers a before b, then any process that delivers b \* must also deliver a and deliver a before b. TotalOrder == \A i, j \in Server: LET committed1 == lastCommitted[i].index committed2 == lastCommitted[j].index IN committed1 >= 2 /\ committed2 >= 2 => \A idx_i1 \in 1..(committed1 - 1) : \A idx_i2 \in (idx_i1 + 1)..committed1 : LET logOk == \E idx \in 1..committed2 : TxnEqual(history[i][idx_i2], history[j][idx]) IN \/ ~logOk \/ /\ logOk /\ \E idx_j2 \in 1..committed2 : /\ TxnEqual(history[i][idx_i2], history[j][idx_j2]) /\ \E idx_j1 \in 1..(idx_j2 - 1): TxnEqual(history[i][idx_i1], history[j][idx_j1]) \* Local primary order: If a primary broadcasts a before it broadcasts b, then a follower that \* delivers b must also deliver a before b. LocalPrimaryOrder == LET p_set(i, e) == {p \in proposalMsgsLog: /\ p.source = i /\ p.epoch = e } txn_set(i, e) == { [ zxid |-> p.zxid, value |-> p.data ] : p \in p_set(i, e) } IN \A i \in Server: \A e \in 1..currentEpoch[i]: \/ Cardinality(txn_set(i, e)) < 2 \/ /\ Cardinality(txn_set(i, e)) >= 2 /\ \E txn1, txn2 \in txn_set(i, e): \/ TxnEqual(txn1, txn2) \/ /\ ~TxnEqual(txn1, txn2) /\ LET TxnPre == IF ZxidCompare(txn1.zxid, txn2.zxid) THEN txn2 ELSE txn1 TxnNext == IF ZxidCompare(txn1.zxid, txn2.zxid) THEN txn1 ELSE txn2 IN \A j \in Server: /\ lastCommitted[j].index >= 2 /\ \E idx \in 1..lastCommitted[j].index: TxnEqual(history[j][idx], TxnNext) => \E idx2 \in 1..lastCommitted[j].index: /\ TxnEqual(history[j][idx2], TxnNext) /\ idx2 > 1 /\ \E idx1 \in 1..(idx2 - 1): TxnEqual(history[j][idx1], TxnPre) \* Global primary order: A follower f delivers both a with epoch e and b with epoch e', and e < e', \* then f must deliver a before b. GlobalPrimaryOrder == \A i \in Server: lastCommitted[i].index >= 2 => \A idx1, idx2 \in 1..lastCommitted[i].index: \/ ~EpochPrecedeInTxn(history[i][idx1], history[i][idx2]) \/ /\ EpochPrecedeInTxn(history[i][idx1], history[i][idx2]) /\ idx1 < idx2 \* Primary integrity: If primary p broadcasts a and some follower f delivers b such that b has epoch \* smaller than epoch of p, then p must deliver b before it broadcasts a. PrimaryIntegrity == \A i, j \in Server: /\ IsLeader(i) /\ IsMyLearner(i, j) /\ IsFollower(j) /\ IsMyLeader(j, i) /\ zabState[i] = BROADCAST /\ zabState[j] = BROADCAST /\ lastCommitted[j].index >= 1 => \A idx_j \in 1..lastCommitted[j].index: \/ history[j][idx_j].zxid[1] >= currentEpoch[i] \/ /\ history[j][idx_j].zxid[1] < currentEpoch[i] /\ \E idx_i \in 1..lastCommitted[i].index: TxnEqual(history[i][idx_i], history[j][idx_j]) ============================================================================= \* Modification History \* Last modified Tue Jan 31 20:40:11 CST 2023 by huangbinyu \* Last modified Sat Dec 11 22:31:08 CST 2021 by Dell \* Created Thu Dec 02 20:49:23 CST 2021 by Dell apache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/doc.md0100644 0000000 0000000 00000022113 15051152474 026177 0ustar00rootroot0000000 0000000 # ZooKeeper's Protocol Specification of TLA+ ## Overview ZooKeeper's Protocol Specification focuses on the Zookeeper Atomic Broadcast (Zab) consensus protocol proposed in *Junqueira F P, Reed B C, Serafini M. Zab: High-performance broadcast for primary-backup systems[C]//2011 IEEE/IFIP 41st International Conference on Dependable Systems & Networks (DSN). IEEE, 2011: 245-256*. We have completed the the [protocol specification](Zab.tla) for Zab using TLA+ toolbox, and we have done a certain scale of model checking to verify the correctness of Zab. From the experience, we have found some subtle [issues](issues.md) related to the protocol specification and the Zab informal description. To handle the ambiguities and omissions of the informal description in the paper, we supplement the specification with some further details. If you have any question, please let us know. ## Specification Development ### Requirements TLA+ toolbox version 1.7.0 ### Run Create specification [Zab.tla](Zab.tla) and run models in the following way. We can clearly divide spec into five modules, which are: - Phase0. Leader Election - Phase1. Discovery - Phase2. Synchronization - Phase3. Broadcast - Abnormal situations like failure, network disconnection #### Assign constants After creating this new model and choosing *Temporal formula* with value *Spec*, we first assign most of constants. We should set CONSTANTS about server states as model value, including *FOLLOWING*, *LEADING*, and *LOOKING*. We should set CONSTANTS about server zabStates as model value, including *ELECTION*, *DISCOVERY*, *SYNCHRONIZATION*, and *BROADCAST*. We should set CONSTANTS about message types as model value, including *CEPOCH*, *NEWEPOCH*, *ACKE*, *NEWLEADER*, *ACKLD*, *COMMITLD*, *PROPOSE*, *ACK*, and *COMMIT*. #### Assign left constants affecting state space Then we should assign CONSTANTS *Server* as a symmetrical model value(such as {s1, s2, s3}). To compress state space, we need to assign CONSTANT *Parameters* as a record, whose domain contains *MaxTimeoutFailures*, *MaxTransactionNum*, *MaxEpoch*, and *MaxRestarts*. For example, we can assign it to format like [MaxTimeoutFailures |-> 3, MaxTransactionNum |-> 5, MaxEpoch |-> 3, MaxRestarts |-> 2]. #### Assign invariants We remove *'Deadlock'* option. We add invariants defined in spec into *'Invariants'* to check whether the model will reach an illogical state, including *ShouldNotBeTriggered*, *Leadership1*, *Leadership2*, *PrefixConsistency*, *Integrity*, *Agreement*, *TotalOrder*, *LocalPrimaryOrder*, *GlobalPriamryOrder*, and *PrimaryIntegrity*. Here the meanings of these invariants are described in the following. Except for the first four, all invariants are defined in paper. - **ShouldNotBeTriggered**: Some conditions should not be triggered when we are running the model. For example, follower should not receive NEWLEADER when its zabState is not SYNCHRONIZATION. - **Lerdership**: There is most one established leader in a certain epoch.(Established means leader has updated its f.a and switched its zabState to SYNCHRONIZATION.) - **PrefixConsistency**: Transactions that have been committed in history are the same in any server. - **Integrity**: If some follower delivers one transaction, some primary must have broadcast it. - **Agreement**: If some follower *f1* delivers transaction *a* and some follower *f2* delivers transaction *b*, then *f2* delivers *a* or *f1* delivers *b*. - **TotalOrder**: If some server delivers *a* before *b*, then any server that delivers *b* must also deliver *a* and deliver *a* before *b*. - **LocalPrimaryOrder**: If a primary broadcasts *a* before it broadcasts *b*, then a follower that delivers *b* must also deliver *a* before *b*. - **GlobalPrimaryOrder**: A server *f* delivers both *a* with epoch *e* and *b* with epoch *e'*, and *e* < *e'*, then *f* must deliver *a* before *b*. - **PrimaryIntegrity**: If primary *p* broadcasts *a* and some follower *f* delivers *b* such that *b* has epoch smaller than epoch of *p*, then *p* must deliver *b* before it broadcasts *a*. #### Assign additional TLC options We set number of worker threads as 10(if unavailable on your system, just decrease it). We can choose checking mode from *Model-checking mode* and *simulation mode*. - Model-checking mode: It is a traverse method like BFS. Diameter in results represent the maximum depth when traversing. All intermediate results will be saved as binary files locally and occupy a large space if running time is long. - Simulation mode: Everytime TLC randomly chooses a path and run through it until reaching termination or reaching maximum length of the trace, and randomly chooses another path. Currently we set *Maximum length of the trace* as 100. Here we mainly use simulation mode to discover if there exists deep bugs, which is hard to be found in model-checking mode. ### Results You can find our [result](verification-statistics.md) of verification using TLC model checking. ## Adjustments in protocol spec from paper >Because the pursuit of the paper is to describe the Zab protocol to others in a concise manner, which will lead to insufficient description of the protocol, there are missing or vague places in the paper. As a mathematical language, no ambiguity is allowed in the TLA+ specification, and this is why we need adjustment. Overall, we categorize the flaws of the original paper into two classes: abstraction and vagueness. ### Abstraction There is a missing part in the paper, in which the pseudocode uses the **Discovery** stage as the initial stage of each round, and omits the **Election** stage. On the one hand, in spec, **Election** helps advance the state of the system, and is also related to the liveness and strong consistency of the system, so we cannot omit it. On the other hand, our focus is on Zab, so the **Election** module should be expressed with a small number of variables and actions to reduce the state space of the model. We use one variable *leaderOracle* and two actions *UpdateLeader* and *FollowerLeader* to express the **election** module streamlined. ### Vagueness We categorize vagueness in the paper into two classes: vagueness in variables and vagueness in actions. #### Vagueness in variables First, the character **Q** is used everywhere in the pseudocode to represent the set of Followers perceived by the Leader in the current term. We divide the set **Q** specifically into variables *learners*, *cepochRecv*, *ackeRecv* and *ackldRecv*. We use *cepochRecv* to let Leader broadcast *NEWEPOCH*, *ackeRecv* to let Leader broadcast *NEWLEADER* and *PROPOSE*, *ackldRecv* to let Leader broadcast *COMMIT-LD* and *COMMIT*. We will explain the reason why we use these sets when Leader broadcasts *PROPOSE* and *COMMIT* in the [issues](issues.md). Second, *zxid* in *COMMIT-LD* is omitted in hte paper. We will explain the value of the *zxid* in the [issues](issues.md). #### Vagueness in actions Totally, adjustment on vagueness in actions can be divided into two classes: Completing the missing description and Adjusting the protocol structure. * For completing the missing description, we categorize four classes: 1. Complete the branch of action where after the Leader node processes the message *m* of type *t*, the receiving set of messages of type *t* still does not satisfy the quorum. 2. Complete the branch of action where before the Leader node processes the message *m* of type *t*, the receiving set of messages of type *t* has already satisfied the quorum. 3. Supplement the logical action that the Leader receives the request from the client, encapsulates the request as a log entry, and persists the log entry. 4. Supplement the logical action that the Leader tries to commit log entries based on confirmation information from followers. * For adjusting the protocol structure, in order to improve the readability of the spec, we impose standardized and unified restrictions on the spec. That is, the division unit is to one node receiving and processing one message. Each action, except actions in election module and environment error module, makes a node receiving a certain message a trigger condition, and then produces a subsequent state change. See example when Leader processes message *CEPOCH* from one follower: ![case_leader_process_cepoch](pic/case_leader_process_cepoch.png) apache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/issues.md0100644 0000000 0000000 00000014341 15051152474 026751 0ustar00rootroot0000000 0000000 # Issues >This document describes issues related to the ambiguous description of the Zab protocol. ## (Issue 1)Issue introduced by vague explanation of broadcast object set Q Because the Leader will only accept the client request when it reaches the *Broadcast* stage, thus when Leader broadcasting messages from *Broadcast* stage, we naturally use the set *ackldRecv* as the object set when Leader broadcasting *Propose* and *Commit* messages in the initial protocol. On this basis, the model checking found that there was an error in the protocol. After analyzing the execution path and the violated invariant, we located the error in the action *FollowerProcessCommit*. The Follower received an illegal *COMMIT* message, as shown in the figure below. In the *COMMIT* message, the log entry corresponding to the *zxid* does not exist in the local log. ![protocol-spec-mck-bug1](pic/protocol-spec-mck-bug1.png) A quorum of nodes including node *A* and node *B* completed the log recovery, and entered the *Broadcast* stage. Leader node *B* sent *NEWLEADER*(1, <[1, 1, v1]>) to node *C*, which joined the cluster. Then Leader *B* received client request and broadcast *PROPOSE*([1, 2, v2]). But node *C* did not receive the message because *C* is not in *B*'s *ackldRecv*. Later, the Leader firstly processes *ACK-LD* from *C* and then *ACK* from *A*. So, the object of Leader *B*'s broadcast *COMMIT*([1, 2]) contains *C*, and there is no log entry with *zxid* [1, 2] locally in *C*. We found that because the set of objects when the Leader broadcast *PROPOSE* is *ackldRecv*, the Follower will not receive messages with type *PROPOSE* until receiving the *COMMIT-LD*. If a log entry is committed between the Leader sending *NEWLEADER* and *COMMIT-LD*, the corresponding receiver will permanently lose the log entry. Therefore, we have made amendments in the spec. When the Leader broadcasts *PROPOSE*, the set of sending objects has been changed from *ackldRecv* to *ackeRecv*. We successfully discovered errors introduced by the vague description of **Q** in the protocol through model checking, and fixed them in the specification. ## (Issue 2) Issue introduced by lack of client request logic Since there is not discriptions about client request, we need to add a process that describes the Leader receives a client request and encapsulates the request as a new log entry and appends it to the local log. This process are represented as action *LeaderProcessRequest* in the spec. In our original specification, when Follower receives the message *PROPOSE*, the *zxid* of the proposed log entry should be the successor *zxid* of the latest local log entry‘s *zxid*, otherwise it will be regarded as an illegal *PROPOSE*. On this basis, model checking told us that there was an error in the model, as shown in the figure below, where Follower receives redundant *PROPOSE* from the same log entry. ![protocol-spec-mck-bug2](pic/protocol-spec-mck-bug2.png) A quorum of nodes including node *A* and *B* completed log recovery first and entered the *Broadcast* stage. When receiving a client request, Leader B encapsulated the request as a log entry [1, 1, v1]. Before B broadcast the proposal of the log entry, node *C* that joined the cluster later sent *ACKEPOCH* to *B*, and *B* replied with *NEWLEADER*(1, <[1, 1, v1]>), and added *C* to the set *ackeRecv*. So the *PROPOSE*([1, 1, v1]) broadcast by *B* was also sent to *C*, so *C* received the redundant proposal for the log entry [1, 1, v1]. We have analyzed two repair schemes. The first scheme is to atomize the actions *LeaderProcessRequest* and *LeaderBroadcastPROPOSE* and merge them into one action. The second scheme is to relax the constraints, where if the Follower receives redundant *PROPOSE*, it will be release this message directly. We chose the second scheme, because we think the situation is not serious and will not affect the correctness of Zab. ## (Issue 3) Issue introduced by ambiguity of zxid in the message COMMIT-LD When the Leader sends *COMMIT-LD*, the paper does not express the specific value of the *zxid* carried in the message. Therefore, we naturally set the *zxid* carried in *COMMIT-LD* to the *zxid* in *ACK-LD*. Therefore, the model checking found an invariant violation, as shown in the figure below, the Follower received an illegal message *COMMIT*. ![protocol-spec-mck-bug3](pic/protocol-spec-mck-bug3.png) A quorum of nodes including node *A* and *B* completed log recovery first and entered the *Broadcast* stage. When the node *C* that joined the cluster later sent *ACKEPOCH* to Leader *B*, *B* sent *NEWLEADER*(1, <[1, 1, v1]>) to *C*, and then received the client request to broadcast *PROPOSE*([1, 2, v2]). Then, *B* received the message *ACK*([1, 2]) from *A* before receiving *ACK-LD* from *C*, so Leader *B* committed the log entry [1, 2, v2] and broadcasts *COMMIT*([1, 2]). Because the object set when broadcasting *COMMIT* is *ackldRecv*, *C* did not receive the commit message. Afterwards, *B* processed the *ACK-LD*([1, 1]) from *C*, and replied with *COMMIT-LD*([1, 1]). Therefore, when Leader *B* reached a consensus on the new log entry [1, 3, v3], it sendt *COMMIT*([1, 3]) to *C*, and *C* found that entry [1, 2, v2] in the local log has not been committed yet, which was treated as an illegal *COMMIT*. The repair solution we analyzed is that in the action *LeaderProcessACKLD*, the *zxid* carried by *COMMIT-LD* is changed from the *zxid* in *ACK-LD* to the latest *zxid* committed locally by Leader, thus making up for the missing commit information. apache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/pic/case_leader_process_cepoch.png0100644 0000000 0000000 00001431637 15051152474 033717 0ustar00rootroot0000000 0000000 ÿØÿàJFIFHHÿá@ExifMM*‡i  HÿâÐICC_PROFILEÀapplmntrRGB XYZ æ5+acspAPPLAPPLöÖÓ-appldescPbdscm´œcprtP#wtpttrXYZˆgXYZœbXYZ°rTRCÄ aargÐ vcgtð0ndin >mmod`(vcgpˆ8bTRCÄ gTRCÄ aabgÐ aaggÐ descDisplaymluc& hrHRØkoKR ìnbNOøid huHUcsCZ0daDKFnlNLbfiFIxitITˆesES roRO¶frCAÈarÞukUAòheILzhTW $viVN.skSK;L>@>289 LCD LCD æÑâÕàÙ_i‚rLCDLCD MàuFarebný LCD&25B=>9 -48A?;59Colour LCDLCD couleurWarna LCD 0   @ ( LCDLCD *5LCD en colorFarb-LCDColor LCDLCD ColoridoKolor LCDˆ³ÇÁɼ· ¿¸Ì½· LCDFärg-LCDRenkli LCDLCD a cores0«0é0üLCDtextCopyright Apple Inc., 2022XYZ óQÌXYZ ƒß=¿ÿÿÿ»XYZ J¿±7 ¹XYZ (8 ȹcurv #(-26;@EJOTY^chmrw|†‹•šŸ£¨­²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿparaffò§ YÐ [vcgtndin6®QìC×°¤&f\P T9333333mmod Rýbmbvcgpffffff334334334ÿÀH"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÛC  ÿÛC ÿÝrÿÚ ?ýü®CDñ¶‡â/ëžÒŒ“\øu¡ŽîMŸ¹YfRÂ5|òê.1ÆEtz…ذÓîoŠ—Ñ<…GS±IÇé_<~ÊVÏ'ÂH©¬x~ãÃÑHµŠí—íÁ´$‘¯ú¦'?#@Æpx Á¥q)+Øí+*Ó[ѵ ûÍ*Æú ‹Ý8 ¹†9䄸ʉWpäg­Jù‡áü—o‹ÿõñ§è“J1ºclúzŠòßÂ[E÷×wYZ,ìV<Ù!å#ˆªÌØçàÞøý­éþ<ðï‡5ÿèÞ2±ñ4ßefÓ!he°¹l¸<!økÃÐí6–70™îdR€æçæ,›8*´ÃûAë7ßtoiºtÄšýìZE¼ XÛ é$d/×w–3œöÉëG³aΤuh:^±¦xP¼H5dʶp¶wLaC$›p1ò¨$ç»_ÞÛüOÓ¾?|-Ó¾"_Ùjê[S–ÞêÒnC5œ‚HrA ÁV:óNñoÇíkSñÆ¿á½ÅZ7ƒ,<9/Ù„º”Mq=õÂç~|©°Û‘ówÏ8Ù>‚ç>Ö¢¾vð7ÆMKÆ¿õ¬PA¬éÞG(‹/nתHtäÆü0ô8ÏzÃøs­~о=Ò<3㩯t}7H¼[g–ÄÀí5ŹÚ&˜Éœ#H7t}KE¼kÇþ8×¼!ñÀZd~YÐ!²òÑæH^;Pg¸¬bÛcG¹#¨†éíg¸sn{à_ 5_ë—CSÔ¼m¡ø¿E’Ê4ø‚Mo;`¢ƒ·Èp§kßj$¬ì4ů?ƒâ§Ã»¯ÿÂk¯[\k›Ú?²ÆÆGŠY”•C§ F+gÅÖž,½ÑÌ Ô-´ÝKz‘5Ô&xö¼6S“Øæ¾Pý—|=ãŸì¶×N£¦ÿd¾«©}ª/±¶K*ÈèÌ'Ýò‚À0p¼UÆ*ͱ7­µh¯–ŸÆ~#xŸÅP|1¼Ó´}'ÂWFÁ~× žKëÈÐ4ŠÇ Fƒ d`ûõÇIáÿŒ‡Šþjÿìíã³Ö4Ý;P‘áæH’îÊ7<A(J†9ÁÆsÍ'M‚‘ô%|ÇðïVý ¼oaáÏÞßhúvz°I.ž ‘¦–Ù±¾C.~Ye‘Fdœý9JQ°Ó¸Q\‡ÄnûÃ^ñ/ˆôÍ¿lÒ´Ë˸wÉæÁ È›†FFTdf¼?áÖ¯ûAxçNðçŽnïô}?G¼É-‡‘#M5»`<†L’0Ë"®dÍ 7WúOÑ_x›ö€×uox‹Gðÿ‹4OXønêK(Æ¥O=õÄ$¬„ㄇx*ù¸Î};‹?ÚB)~7Åi,]V)M´È‰ï·ì[“å°"O]¼džj½“'RÑ^á 7ãý¶¯a¨øËYÒ/4û‘›Ë(-ž&¶Êä%Üwl¼‘ŒãÖ¼3Ä?´.»­x£Ä6~ñn‡á]Iio£Ï=ü°®íŽ"À…ÛócœóÀ©·°ÜϹkGñ&…â u4kÄ»“J¸kK „æ)ÕCÛ=À`ó‡~?Öþ-ü&ƒÅ>k}+\¸Y!c"4öñ\Ã&Ç;A‘€Ü¼ç 3ÐÖ_À}o^Õá5³ñ"XCI×e´–k ³%Ã,1‘×$³ØÉ9Àµ.MØ÷Ú+ÀüñCSþ!i~>’½ð=̲—|¥}9£ó­ä+–Á(NzñÚµ~k_|iðŠÓÄw—¶!Ö£{›Rð†d|ÀÇÊÁ<ŽOµ HôÍ#Äš¿>£m£^%ܺMÁµºTÎaT1FÈêøÖÝxÀo_Õ%ñ½‰VÀêN¶ö²Ïamöd¸e‚&2:å‹1Î2Np®߯ŸX£©ô¯<øá üMÓ~#x³ÆZ|Z¥æµ­^Ù»\ vŽÞ$R‰l” cÒ¯‘Ìϯ5=wEÑl—QÖ/à±´‘‘i¥Xã/!Â(f Ç€;ö­JüàŸS¾Ôÿd®£1ž[nÚÌ;wŽÚüÇ× ¯Ñú™ÂÃŒ®eiúî‹«Ïyk¥_Áy6'“rÈ®ÐJ?‚@¤•n­ß_Yi¶’ßê7ÚÛ@¥ä–Vˆ£©fl>µò÷ìïøXìaýZ»ûI(Ö.>ø蓦ø‡_oøf‚.böY±‘ßù=ë ›Kžé¢xëÁ^&´¹¾ðî½c©ÛYg’Úæ9R 9%Ê1 0ZÝÓµ-?X±ƒTÒnc½³¹Q$SBâHäCÑ•”AõòO¼7¤xKã߇†mbÓbñM–£¥ßCn‹sF°“uQƒµŠœÿ³^qðëãf³ðÿÂ? <emosi%ÝÕ–¯,Êåím“Q6°º•u Ãç,qŒwªöWWAÏÜû~÷Ç~ Ó ººÔ5û hl'·%Ìh°Ü¸Dä° øçiçSHø•ðï_Ô!Òt/éš…õÆï. {Èe•ö©fÚŠÄœ($àt×À¾#„Kð–óÄlu®|Gk‘ÚMÄqÎÐ,cw8hÏóÅ}Màí'ñUš§Â ü(ÀHF¥$v*°Ÿ-¸Ì.Î7—ßž3D©¤„¦îzˆøŸðÜß,x§Kûb¹ŒÃöÈ|Ààà®ÝÙÈk—Zh¾%ÓoçŠ'™Ò ¸¤eŠ?¼ä+sÉè+wIñƒ¯ißÛ£o¨X凟o*KSï|êHã¿뺄:N‹â2þöà‘^C,®T;UX“€ àtð;à~Æ~Ì?iÛߺýçú[|œñótçŠú—áóÙ\øš×w¼(Ñ#ºj2Çdlg#õ¥*i& Mž¹­|@ð/†õ´ø‡OÓo§ÁŽ ›¨¢•ƒtÂ;sÚºÕ`ÊAäЊø¿à¿€ü5ñ7¿µÿXèj>"Öu+f¸™¥Š@H• Î͇,1Žqè+Òÿerû]ø# M¨HÒËj%¶Ä’R)NO¢àaJPI Hú¹_xßÂ>±Rñ~«•o3ùq´ï´ÈøÎÔ^¬qÎ5ÕWÄ4‰·ÿü gq­i’´ÇW—Kó,KÇlŠ‘!Þ1]¡X‚ ïSÝê9;cè:ö‘â}"Û^ÐnVòÂìŠd+€JœêéZõä>0Ö]išf‡á«»«K[K«vž]A¬‡ïYÜ0Ø ·çø‰¨ücñß4¿†2|:kM"ïÇk¨%ÇÛ"7 –Qåö`‚v²¾ÒGÍòäc"Ÿ²bçG×WÅŸ ~.ü]ÕõŸê^.¸Ó¤Ñ|Qy}¤-àd•g°ŽCö‚äždx˜m(`f¾Ó©œ)JâÒWš|VÕüC¢xao¼;­i^=â÷WÏ“[>XÜ Ê_hPr!ëÞ ³Ðt?ÁÞ"ñUüz}›\‚`‡v7ÊáH-°žùçLÅ]ØmØöºJù{Ã~4ø½¢üfÒ~øîëNÔlï4Û‹å¼´·h^m‡hVˆVF8 ¯Cšê< ñÄ~ ð÷Ä-OPò|ï 꺵¦ÄÀò¬óåo;ž3íTé±)óE|›'Æ\ü0øy>¬¾0ñü‰rJŒ-`êe˜Æ­’y>§œm=7„¤¯—üE㟋ڷÆ-gáw€ŸOµ·µÓío íäLÿeWÈ|*ŸÞ;±]€ü $*÷ˆücñCÀ6~ÒüIya}©ë~ ƒM¼žYR[IŸª’6I´òGè(öl9¤¨¯Õ¼y¯Y|vÑ~Cåd_hó_K”&_9Ôaó€0£Œ~5óo†>,~ÐÞ&Ám§£Ä<~·ðÚ–´ôF°bÏ+Çs2©r½23ÔTÛš>ü¢¼¯à§Œõo|6Ò¼G¯Ƥísosä±´–³Ée8Ü6;gêµ YØ¤Ì gÅ>ðõΛg­ßGg>¯8µ´I iÛ¢/¹­êù{⦵ã|KðXº}*ÿÃúÖ·mmm¶Eî­IT"Ì_ÉÜT…ëŠê~8ø÷Æž oZx+iï¼C«Ç§”ºV1‘*6 *APˆçâ«“asn{Í%|ãà|JÑþ-Oð»â=í–¬/t¿íK;«8 ¹M²ùm)'+ÃO#“ž3añÅߊ>'ñ%¯Ã‹ûè^½}8\][›©¯. â\ íHÃp1É9çöaÌ{ߊ¼aá¿iƒXñMêØY´‹‘•ØolàaÞ•7†"Ö,¯494[/²[AjÑMbŒÇ¾Bä6ÔàáFO(üLñç¼C£h,м)6¨Éai¤_þæïÊ)¾{«Mbîæ(Í¢ËÆÉ£ei#8;Snpqœ€)èÿ|yìÿ¥xJHá¶ñg‡5í‹8ä‘^)e†WeVt$$nèÁ#8æ¾Ö¤ªö¬\ˆù6ã?þ0øÆþ(ð´Ѽ9öÔ–!Ի綒?0íÛò–*ª$rOà߈ ükâmOÂÞ³ñ¾ƒâ{¯·¤os ¥ÅÀ$ReF Œzc8'9úΊ^ÓÈ9M3Æž øS®iºÞ‡i¢ëz•½äqXÚJ¯‰¬A¤á ‘Ì0>•Ôü0Ñu |7𯇵xÄ7úf•empƒ–UnRAÃÈ85ÝRT¹t*Á^ûAx#Ä~8øöo.ÿéW¶×úαþú&ÚÇs£»‘“ֽŠQvw¯¡ñ·†gÝsÃü'©Ú¢7†4»n¯>tùõˆ-ž×ÌÙÙpÂBÀcp$œâiàŸŠž ñ—Œ?±<¦ø‰¼Q~÷všåÕÌHÖi2ªí–7F‘Ò"7m\dç²1ö=§µ}IäGÍß|5ñáWÁ´‡ÐRñœÒ:iÿlŠK0ÿ–ÿ:.–ïœc©®Oá­—Æ¿ øóĺæ¥àÖÏÆ:´ó¸Õ퉱‰rŽÀ&mªÅ°6“ŒwÍ}{E/i¾›‡!à?|)ãƒã¯|Kð·sáä¼·¸°šàZ´±]ª¨hå`TÁÎ}FæºýWYøsàõK? Û7ˆe`³èÓÞÇ$f!WQrÆKGó Œ àƒŠôú*y¶*ÇÈ~ø¥þ/ÁñOZü<Ó-lf·šÖÚê9Íô²‘‚RTULgîŽyç·×”QD¥pJÁ^3ð'Âÿ‚<6‹âKqmvú–¡p:È<©çgC”$r§8ÎGzözJWÒÁcå(ô?Œ? üOãø@¼5oâ#Åw©[È×±Ú½ÜɶA*È?x»”0 Ž?‹'ï ü#Ö¼'û;ë_chïuýSMÔ„ž[ï/cpYñò‚U763ŒœtGÑVê1rgíÿÃþðþ‡ªÆ"¼°±‚0`®ˆ Ëp{Šìéi*)gÄ}"ÿÄ?hÚ‡‡|áí VŒE{ae3 `Á]”yî vÔQ},+%Ãàÿˆ_ üUâi<3àË?è^#¿“S„µÔ6—“\Ëù¨Á“w+´p:œñ]w‹>xŸâ—Á©|9¯ÙÙxgÄrÊn †Ý·ÛA4R“w@rZ<+²ƒ‚Iãô5~Ñî.SÃ|!⿎–©§i^+ðE®“mÚ€Ô#dÚ‡˜`@K6,ÁFAõ¯:²ðÄO…^ ñ¿†¼eãm\¿—Qµfº†ÒâÑîNd…üÔmÈî‘Ðu98[ÑKŸÈ9O-²Ôþ"i_­õá+IüJÎÆM*Òí- Ei[n'ue,#Ú[Ž[8ãä_  øËá_ëÇWð Ecâ­eõ§þ×·cg±Ç]Š„Ë·fr6ç8ÀÆOÕôP§¾Ê|‡ñÓàÿŽü[ãÔ< 4ÿÙE¥kÌdXÊÛÃr’¬˜,¥ŽÝÈv‚v–äWÑÚüÚ߆<2‰à­ uË›EŠlÊZáÖº² tÇ5×QC샔ùCálü-âm~][À)—Šõƒ¨M0ÕíØÙÇ$qÆFÅBeÛ³9sœ`c'¹økàxw\ø•{«[¡ñ«%Õ‘#y‘4{A!IÛÏfÁ¯v¢‡;‚Œ­¾xãLø'à{(~ÍcãjGP··¹™<‰¤3Ê|†‘N?x†ñ‘ÔPÓõx‡ö¤ð猴›}{=&ñŽȼx!*éæM"¨\É# tÏ5õÇ‹ÛéútÒ¢Ü\.¯Í DáœF-´sÖ½ÚŠ§Rû‹—±ñ¿†´oŽ? ümãÍK@ðL퉵io!•õ8-ˆ{•ùNãÈló‚=+²ñç…¾%|Ið6¯>o øÏÃZ¢jV–Mt·È!%|¶™0•¶2GZúZ–‡S­…ÉÐù‹ÃÞø“ãß‹GĈz$>±ð½¬ñYY%Ú^I5ÍÀ1¼¬è„ NÎÜddטÙ~ξ)k‹qÝÛygZ.€Ñê·wFýÞé»NzWÝTPª¾ÈŒµo„Ÿ-þø7Âú^˜—¾#Ñu[]NêÝ®"@ϲJù”¶ÎI ž¹¯`ðÇ‹þ6j:õ—‰þÛé\¬Â{¤Õa¸h”) ˆÔe²À=s^ÛE¥÷CQ>oøGð¦ÿNðW¼#ñOAmâ=jþàE½$ßkqJŽ ÚÙRFpÊ@¡Ê|qðgÂ_<-à-Sᧈü)¥œöºƒÅ|·ðÈÍqp>H¼¤'äüŰ1Í]›à¿Š¯¿g/xCÊ[?xe#¹³Ä¨v]DÍ”ó)‡F+×óÆkëª*W{‰Al|WÁψ û:ø{áùÓÐkz~«mu4|XXc¹23oݰáNpûMFàS©*%6÷)FÇÇpøWãoÂ÷ñ‡…~húî‘â[Ë‹ë ×½Kw°–íq ’7åö ã<äçh÷ß„žÿ…gð÷GðkÌ·ØÄLò&v¼Ò1wÛœ œ) $^E9M´%x׌¼!¯êÿ|â» q&™¡Eª-Ü›ÕLfå"X°¤†l•=ÇzöjJ”ì69ø¿áíWÅŸ üQá­ !>¡©ióÁT #© 1 9îHÔø^ÆãLðÖ“¦Þ.Éím ŠEÈ8t@¤dpy«vŠ/¥‚ÝOm¼7ñ·á´>,ðW‚ü9m¯iZýíÝæŸ¨ØíÍ‘¾Éu–'p„åq«gÿõÿ^|°´Ù{mà᪶¥p®ªKè”VÚ̦V*¸\íœWÔ”•~Õ‹‘#xGáWŽ4­3áµ½õФžñ­z<èÏ—otn¼¦o›>bð¹#<Ž }sE2“{+;ü~ðŠDxÖá zÇãŠügunIÔôÍ>ÚÞ]êKKs ØጎHÁíT~;x3Å>(Ò4 cÁvñ^ëÕ­u8­eD·Ë&ó€ ã©g8Ý(©çw¸récåOxsâψþ8韼aáè4-2ßIšÉ`Žö;©"%˜0€¹g,Oʸ €~`k+áïÂè0|MNÅb>mlêXš6òE긇XïÝ‘÷sŽø¯¯èªöŒ\‡| ðž»à¯‡vúˆà×ÑÝßÊÈd'º’T;‘ʰ=xïÍ{%ww-#äOŠö|_âßÝé>ì|!¬ýº ޝn ôQ6(Ê ;ÀÅsŒì<[ |Añû|9×/ü>º=拯-î¡iöÈ®ñï@âQ°>á†Ú£#8í_EÑWí<‰åx§ÄÃÁ~‡Æñ%ôšœ*/£²žÎâàæXÛÌR7}Ý£…'' åv’[k6Bà Íd|Q³øÓâ¯øzÿJð oeá ]ï ”êöêo¢LªŒ Ã¼a¹ÜFqŠúâŠ=¦·°ù:=x«BñÿGýnÿÃë£Þèú𽿴ûdWEºG4aÄ£`}Û”áFFqŽ n^x7]¸ø÷gão³+h‘ø~{”ºgÏ’á\&ÌîÁPyÆ=ëÚh¥Î§ÀÚÞñWáŸÂøN¶Óo¼$‰©4ZËÞ¢5½´áÚkf·³NÅŠ FÆHÆ>­ø1o5¯Â Ap¥i¶Äƒ×æ@GèkPýžþêÞ ŸÄú—‡#¹¿º¸7SžvŠI™·3<O)²ÜS¸¯dDXÕQ*¨0•SšhQ˜êù/âD?¼Mã/ êšoÃøÞÓÂ:¤·p¿öŰ7‘˜Þ%ùY‹ †çq1Þ¾´¢¢2±M\óWLñ/ÄO…ú¦‹¯é‰áÍ[W´¸·6æån’'`U –5ƒÁ8\Bkžø9/Äý+IÓ¼ã C¦ZhZ|V±êPßÇp·-l$Ä ¡Ór Ä–ê1œo¢Žm,>(ø±á‰mµ/ß|+Ó$Ônd1Ûkâþ1į„‘—o sþïO¡üBøS⻽Àzׇž gÄžTS ãm‹Q¢HçVfÎ×cef8ääæ¾”¢«Ú²yâ>Ô~!êž$â‡Ö>Ò¡‚LÌ.Ẹyò¡ByJ›S·xÅTøÁáßøÁß|i©©xRk ö3Ê ^"£…ð¬¥ýzy¢—>·.–89ÿá%ñ?ûøµm iÖ¡cu² p#‘ÑÑÎPªsÁÎöªŸ<=«xOᇆ|7®Ä Ô4Û¡ž0ÊádQÈܤ©úƒŠôz)siaØùkQñ÷Ž5Ú‚Ïáÿ‡.Éðþ™§¬ú¤; ̬ÀïÁn|È€ç­}KYVš…c©ÝëV:uµ¾¡¨m71‰4á8_2@>ÑÓq8íZ´I§°$´”TŒ=袊(¢Š(¢Š(¢Š(¢Š(¥¤ Š( Š( ¥{PKIK@ EPEPKIERÒPEPEPE´”QEQÖŠ)i( –’Š)h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(£š(À ÿÑýü¤¥¢€¥PÒQ^ ã|bÒµËØ|;á t‹#ˆç½Ô’ /~PÇÉBÌ·çàã9§ÜMØ÷š+çM[ö‚±ƒà­ŸÆ=#J’æ;‰­á{7|H$âT0b§;x¸Î3Äžø·ã¤ø‡£ø+â„ãÐcñ43˧K عe{t2¼Sá@ݰ‘€Îr+Ù±s#芭kyi}ÜÙL—¶pñ°u8ààŒŠùÓTøÑãmCÄþ Ó~øEuí+Â2y:Ä—" %™A2El…Næ@\äñ•,¿²QSð7Ddèdº#ñ™¨pi]‚–¶>’¢–¼ Å84¿|Cð´Zñ¼óé²Cv.Nëuó¶€ÉÈÀã9ȯfíqsj} G½|Ù«ücñö­â}DøWáõëO ¸†úæâèAæÜcsCn¸;™G|ž{ ®íKÚãáŸÅ[;nP‘--ôàÀL÷Îû<Ý8 ’Øûƒ;sòÒöl9‘ïôWÏžø±ã{Oi^ø§á˜´)üC¯§\ZÜ‹ˆšHW{Á'+…äàôW\ø·ãÝOâµà/†´Õ%ðÜp½ì÷×bÜ3N»•"@2q‚ r3×fÙFÑô¯"ñoÅ6øðâxËIkMRs ¥Å2Jïy)!aYGÊG‹cA<ã‹ðïÆ[x¿Fð·Ä­ÓI_³Çaqe|—j“¢îò'î±Àöà䤩+æí[â÷Ä cÇš÷‚>øjÓS> Ù¯®þϽæRU"@3ØÄ‘ž¸ÈϽè𦡢Øßkv?Ù—óÂ=¯˜%òd#æMêm§ŒŽ´œZÜiÜÖ¢Šðxûã«êØ^°—G°'ËšïRHf½UPI…1òzÙÎ(Œn Ø÷úJâ~øçNø“à½3ÆzT/o¢ŒLRc|rFí¡#ƒ†SƒÜ`àgÛÒjÚ 1(¯˜uõø¿Âþðl:£øJuY®f»FÑ´BEU\ni[æà(’w`mÜ~Ðz$4Š#M–[qÅ­¦š®<É/·´F/0€‡FË•û£;rBÕ{6O:>„¤¯›4Œþ2‡Åšƒ|{áû-:ó_ŠvÓ&²¾[¨ZâÞmæÚ FÇñt=³ÉÏÃ_‹:?Ž<¡x—Txt‹ÝjY-ÒÑå¾Ñ²˜“vÒÍ€1žhph“=jŠó‡®ø»ãK_éøáhü?ÿ 2Júdð] •ó!š ¸p§¨ã8c$fê¿ü}ªkž#¶øeáµÍ+Â3½µõÍÅØ·iî!š+tÁÉB$ç$p:e{6ÈúbŠñ[ŸŽž¶ø?oñ„Å3X]ľU°Çž×%ÌFMË"²“Óžj/xÃã>¥­X¯Œ<o¥húˆcæÃ|%žÐí,¢höÛ±—'ŸJ\Œ9‘ìð^Z]4±ÛN’½»l#(ßÝ`ØÕŠøOÁÞ,ø‰ xÇâÊxÂñkŸg×gº¹šâåmãUUâ(ÇW‘‚“Ô1ž¢¾ºøyã;?ˆ~ Ñüia‚V/”Ç&6ÉWBp3µÇ8Í9BÁ\ìé)kæ]k㇋5kš/Ã?ZjÖ¾˜ÛÞ\ß_% šå?ÖCn§’TåK·í‚TbÞÃnÇÓ5VÎúÇPŒÍ§ÜGs±BÑ8pz‚T‘‘ÜW!ðÛǺgÄ¿Xx¿K‰íÒì:Éœ¼3DÅ$}¬¸Áï^yû:xľðUþâ›Agw>©wrˆ$I3¬ 6cfúg"Ž]î=ö’ŠäÔn<§ÅzæÖëíIrmžÞ! 62–u<€úоM‰æ=ZŠùörñÅ _Áž_épÉ¢½›2êo|ÓÝÌÁŽÂñ²““ÐåÎ1øT—Ÿ¾!ø‡Æ¾"ð¯Âï ÚjøVU†îkÛÁnòÊÙÊĘã’2q)ºnöm.}1Egh×W÷ºMæ©gýŸy±¢¹/øºÏǾÒ©ˆul>ÆÒyrÇ#¬¥— 0,=£±>Õ~ɓΦ>”WxWâ·Œ—Ç–^ø›á¸´ÍnÞk6[k‘s¾@Ý$.p1"¯Í‘ÁÁö®gQøåãíJãÄZ·ÃßG¬xk·[]]Ov –æKa™Åºm?sÔƒžÃ?(^͇:>£¢¼_Wøáám3áM—Å[hf»¶ÕV$³´\ 溘”ýÀe`ÁˆÈXØw‚üUñ‹P× ¶ñǃm´­2ò7užÞôNöî£+ÉŽKz¯—#Ü|Èõë{ËKÏ3쓤ÞK˜ßcÚ㪜gwš±_xÇ?ü)ÄÉ<áXõ›M3ÄZ•õíÍÅÈ1D¸%ä…Û°x$_fxÅv>:ðŽ“âý9(5[tœFÇ-aó!= VÊ’:âœáaFW:š)kçOüJøÃáÛÝNúÏÁ¶ ¡é†FV¹ÔÒ+«¸bi"M¤.@8VñÆx¥ÜmØú*ŠùÓÅ´¾—àOxóÃZ<º¼,»†Ù-‹ˆçC mÈ*dWR˜ÎÒ{㚿ொž2»ø†ÿ>"ør P¹±:“Û\‹˜ä…_c#0æSÔñÓ¦%û7¸¹‘ï2IQ´²°D@Y™Ž©'ÐS-î-îáK›YVhe‘у+Ђ8"¾FñoÆox›ÃÞ2¾ð„WPðv–—ºt×Ít#¸•Ñ rËDa£ŒŸøä_ýŸ‡üY?ú†Aÿ ÐàÒ»+»ÃU­îí.üϲ̓y.c}Œk¯U8Îî5b¾ øw㉻ø–<á8µ»-3ĺµÕäóÝ‹rÙ•‰†ÚK8UÜON@Æq’0¸JV>ô¢¼SUøÍ§§Á)>3x~ÐÝÛý•nÞVØC™RFìa£}Êq‘‘ÇÖ_‚þ#üXñv£¤j’x,<)ª¨?i–õ>×2YŒ?Üc€ï`ƒœQÈ÷d}I_,Þ|uñî¨Úö¿ðÿÁ©¬øWÃSÍoqu%؆{¦¶ÿ\ÖÑàåW±9Ü:sÀôÝC⻬xGñ—ÂýuöÖÖ9#IîÕ ‰Ð³<¬Ù!YAÎz{Ž›AÌY¦É"D,¬YŽ’I=«Á<ñw^Ôüpß ü}£ÛišÌ–†úÖk¥»µ¸‰X«®GÌŽ¤réòîóüjñŸŠ|7ã OÂMGÁzZÞXM|×A.&òЬ³Ã1¦s‚rø9ªnöt}Äp%ͬ‹42¨dt!•”ô Ž>¢¥¯˜ü+ñNø_û6x/ÄW¶Ò_ÌúfŸmiiúË›™cUŽ5<ã=IÁÀpmàßüc½×mìüsàëm/M¼Gaqmz³µ»*îT™1Én™^¤àÁHöj+æ¯|Tø¿á‰µ]bOXKyÈYuDKÛ«h>$Æå]ÁÝØdã=¾6Xø{áÞƒãOmNO½¤:tȶàÉzŒÊí€gžÞàrfÙåEy€µÏŠz…íÍŸÄ? Úi1$K$Vwbx݉ÁŒ¡ÙÁí_:·Œ>:FÕt Syý€î4ê·Ù _iAöŒíÛæƒòãfv“Í #íª+ƾ#|PÕ>x#LÖõ .uíV{k(¬’à,+wqÆvSüD ûF§€µÏŠš…õÍŸÄ? ÚiP¬K$Vwbtv'&B7;ºÔ¹®>n‡¨ÑK_žß´ïŠtoÚ"ÃÅ~f’ èêrÛ† Om ÄÂtÀã˜Øäàà ö§s; R²?B(¯®üCeâOÚoáæ·¢Ü´íO×H ’ ˜e}Gpycµ|íà+ogIJüR¶Ônua­ßÚÚ ù“ìù] µ²²gvî ÏኵH\çê=%|K&¡áÿ‡<-âŸG2xfãÁ·¢åó#vM8µÎL7†*Ê>ažœU+¯‰mñ·\ø=sidÚY›\½¸¸µ2ù¥˜Šü¶ÔÈ(ÄýÑKÙ9÷=ùwàkW“ø¦OŠvÚά5Ûá2 |®ÐZÙYÝ¿‚søb½£ãf›à‰~ü)Ò4ï2ß“ëÖ‘;K W†|ù†]²/çv¦éka)ésíÚJøSòx_Àô?| Ö%¾Ð®ôûɵ›(îšîÖ NÑÉ’\FÅÂŒõä üՇᯆÖ>ø‡ãwˆ®î¦ñ…éÔ¯tëÔ¸’6³/"CAH »¢9ÀèxÆ(ö^cç?A诇µÿˆ>%ñïÃÏ„¾MB[ ¿ˆryzÝ»l˜ÛÚ…a÷ZRÃ$cÁ¯xðÇÀ߇Ÿõ üEàëI´û“g5¼ª³ÈñÌ ºEbApWƒÛ'Ö¥ÂÛJûÓE~z|1øÉàíö¿ð~©=ðÕŒ:Ê.Æêh÷O,íïÒ6£ Ø^øÁ¯¦ÿfÆgøárX›C’NOú×¢tÚgsÜ(úQKY–%æÞ6ø£á¿Þçx‰¦±;Á{$-ö4d,O0ùQÏPñÎHŠøUñËGñ݇´¹‹^x‡R·i¯Ê{k-¡ˆóäåcÞ ¤–$Ž9ªäv¸¹•ì{Ÿ‰4-CZ¿ðåìrêzP®mÁýäK0Ü„ƒÙ‡9·_3|G_øDþ>|:ñnž¾YñÝè—ä (šß#¹VÝÏÓÐWÓTIl ‰Eµ#ŠZ(¤¥¢€ŠZJZJ)h(¥¢€ŠZJ(¢–€ŠZ((¢–€Š( ŠZ(¤¥¢€Š)h¤¢–€Š)h¤¥¢€ŠZ((¢–€Š)h(¢–€ŠZ(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€ ? J_€?ÿÒýû¢–ŠJ(¥ ¯€o´5µø…ãFø—ðóXñ¶µ¨_;h²Çͧ›FÿQ™¼E ®Fæ9Ø:ã?ÒUÂv&Q¹ð&àÏÁû+é¸ÐïWV‡\¶’KO³Kç,k¨«³ù{w ógÛÎqÍ{ïÄmX¾øÃð³S²±žâÏO›S73ǼP -J©‘ÀÚq< ÷êZn¦·ñάø³à·Š|qáé<ªëéâNMSH¸Óà3[Ê÷(Êž\€1nƒ-¸'ÐÿeýYðïÁI×ìgÓ¯a{ð\ÄÐȹ•ˆ%29t¯ é(”òuÍLj> |^ñgŠn|;¨ëÞñšZÌ·\î[k‹Ud1Ë ªÄ†é‚ dä¬h©Œ¬6®|·ñ^÷_ø“ð§N¿Ó|5©ÙÌúîžëg=»}¬[Ãt¹šH”‹´n9è9'×QãíW¼øåð»V³±ž{=µo´ÜG4Py–R"yŽÔÜÄÜFOš÷Ê)ó‹”øóDñ‰>øŸÇ:V¡á _^²×µ)5m2çK¶k¤•§Œ)†V\ùl `r êB‘‚ysð{Ǻ_ì÷ Û¥¸ñ&‹­Çâ94ä*]þwÝà‘¼#îg‘´dâ¾ë¢«Ú‹ù)µ]wã?Å/ê6>Õt-Â\_^\j¶ÆÐ´ï!ˆ1Ëܶ:ræ¾6Úø'Yñ¬ÚÃï@ž]ޝ£[K¶å‘‰¼È˜+`áNåfP0A_mÑIT³SãO|8ø•ãÙã¶~&·mgÅ: Ô•Í”ò-ÜqTÀïœ|™I$–R2IÍMà{o…Z—‹ôXtƒú®“âW½¼ÓšÚ‰K«™Ž[pF9¾Ä¢h‡Ãß­<¬øƒYyþøš@ v:¶m6Ë—Dó#`Œ`ÈY@À=};ðšÛÆ64 oˆ´Þ ŽØ ¦v&rJaÃ:¦Ðç', Éê}’”§ua¨ëp¯€5 Eã_Çñáî­ã/ê7òåb’m;ìlª-Ó~õŠ%C÷ØŽ;ã¥}ÿE‚Q¹óÿìâëø5£iZõ„úeìR]—·¹¡‘Näeƒ‘Ç#šú–’¦NîãJÊÇ€ü/ѵ}?ÆŸnïìg¶‡QÕ£’ÚIbdY㨥£f:†ÈÊägŠùî…>+Õ¿g,žšïRð¾¯>¡q£Ý#A-Õ¯Ú¥2E²M§,„,¹Û’@? TUªŒžCä?Cð¶ÿÆzOü#? 5]úgkûÝ=­"²eCÎ÷c–'åæ¼£PøUããân›r¶^ ¹¸Öü:V'U¹¾¾¸Šé’$P‘ßø®ÛYÔçÔtíKO³kÐb»bæ 6°(èÄž[¯J›ãžŸcðD›Â~“†çÄ6óE¥Î‚)|ÝÒ"4ˆOÊe §ð¾ó®âÃíâF“g£ësÜ[Ãc{òfEs%¹%U‹£§<€ô"µUuW!ñà3ê¾#øÇñKÀ×VžÕt 7ÁòÜ__Ϫ[pgxÄk DŸŸœäŽ ƒ€:åhºï‰¾ ê>:ð½ï„5}mu­RóRÒntÛV¹†u½Ëåp~FCÃd€N1Œý›EO´éaòŸ]|ñ·ü3.á h¼Q¥Ý cì…—kLn$¸6û‰Û•Y6õÚXuÁÍ{ƒþ2]x»X±Ð‡‚uý*âPMÜ·Ömµ®±Vûù`ð >ÕíÔ”9ßq¨Ûcæÿ…Z·§jÿ¥Ô4û‹dÔµ«‰mZXÄmÄX êO®Et?³~“ªè_ü7¥kvSi÷°-Ï™Äm©ºæV‘ÀaAkÛè¤çpQ ø xÃü[â›?‰/üWkªjS_麖Ÿf×Á¡¹mÞD›Xt<|ßxäŽ0kïÚJ!;£sÎ>Yèöž µ}ÃøBÚâI¥tÉåËóCÈŸÂÒ*‡Áäæ½½T¶R ùÓö”Ò¼CªøSF]2Â÷VÒ Õm¥Öl´òÂæçO]ÛÑxvù¶’ƒœŒd}ID]ÄÕÕ‚|áÛ[^ ñ'þj>ð͵½òKsui$&IZÚ@\–Ø¿2ª#y'«ßí|­kÚÜÿð¯¼Qkã(KGeªéÒ„º’%Äc`Œ¹ (YT`º(­=®·'“K7Ã+Zü?Ð-üo#K¯Gg¼g`ÌeÇ;˜uaÜ÷5ÜÑEfÙhøû㯋µ-gWðþ¥x7Ä—‡Ã>"±¿¸¸ƒK–Ky`µ}Ò$Rw’>ï@}E{ÌZ¬¿>êëg¥ßè’ê6×vi«nÖ“†xÊ1’HBO½zE-S––&ÇÌÿ³÷ˆõ'ÃZ/ÂïøOZÒu="ÞH¦ºžÐÿg“6܆*ÅÆA9Áï^Oñ’×ÂzÞµ®Ü[ü?ñE‡ái#±Õ4›y–+©aA1’6ÊH]ÌP¸Q€Ã}ßE5S[‰ÇKOÃkÚø B·ñ´†]v;HÅÛ3c.9ÜÂÀ`1ÉÉÉÉë]µT6Yñw…uïüºñ§….ü¬k§RÕ.uM&çN¶k›{rŠ9]9Œ©ŒnÎXN0·#¯|(øƒà^›Ãút·ž0ðÕäŽÖöàÌñÇ©,‰*þïwÆ2Ä|£i9ÀÍ}ÿEiíz‘È|5ðëáˆü!ñ·IðãÚÏ'„|3k.­ivñ±‰ï®í¢´™ „mß½^P åFé¿ ´sOðÏÅX/ôÛ‹iuwVšÙe…ѧŠKh•0ÀV …+H WÓ´¥Q°P±â߳ƕ©èŸü-¥ë6sX^ÛÛ¸– ˆÚ)P™œ€Èà089äW´RÒTIÝܤ¬~~éÞ ð¯Ã­gÄ:7Ä߆š‡ŠïRžóOÕ4û&½I­î[rÆåXlt9O'®1‚uþ2Co¤üðÏ…<''‡ÉñM¤ðèò(ŽPû.v«)áZLƒÓ85÷]pþ:ðñ ßXžâÑu58M»"–šÝ]U_z>P‡9¦磌­Ùe¨ëŸþ3xGÄ–¾Õ4Á°ÞËs6«lmZK›¨Œ @œ¶ÐŘL8Ï#¡kÞ/øA£xÃá½Ïƒ5}bòöþþãHº±µk‹Kˆï‰dó$!B~|äÔ ûv’—´ò)ñ®­ðsƺwìïáiöéwâ?ÞÁ¬0뉤G•ä·WÎÝʳ8f^9¯fðgÅé¼g­Ûè±x;^ÒIÞæ}FÌÛA(Mì~fcÐ`qϵ{%-K÷¶>\øqáízÃÁmotÛ›yõ-kY–Õ$…ÑçŽ[xÖ7ˆ «B•È'¥w¿³æ—©è¿¼/¥ë6“XÞÛÛºÉÄm¨L®pÈà089äW²ÑD§pQ°WçEׇY.|m§øãáαâ¿j—Ía|a’km&E³$¥Äq¤jG9À¢ô´Bv FçÂ:/ƒüY‚ZlÚ%êÝé^$·žòm(’Ú¹™Œ“.ÜÆ€K0öýwFÖ&ý¤|1®Åc;é¶ú ô2],L`I^U* CÈä׿QMÔð–•ã/†>ñwÁ±àÍcVÔg›Rþͺµ¶3YÜ[ÞË#̨™y9ÀêkéÚv¡¤|!𖙪ÛKgym§Â’Ã:4rÆÀr¬ŒR=¯U¢‰Nèq‚¾ð—‹¼]ðÚ_‰:F¡à]vüëzî©s¥ÍkbòE9™ÙT9જWÁ ¤‘ÓŸºi)FVGÇڇßxwöB¹ð–RÞk¿ciÖÙ Òy·hh•Svã}§nAÁ#ŠúƒÂ6òÚxSEµž3 Ù[##¬Œ±(*AäxÅt4Q)Ül|3á½SÅŸ¼)â„óø3YÖo§º¿m*êÊØÍiuÞ|¶yÂÏÎHx¿áljü)ð“ៅõM6û]Ñ4I•¼Ca¥³´ò,€¾Œ†tŽF?tŽUê>礪ö¾Dò ü:ðâ·í£x›Â~Ô<'áuÒî"Y.m6_›/(Ëye‰ ÎX Â*¶‡sãO…¿Z®øËFÔþ |5ømâà½[RÕ]ïít›Ë;c%„°_³2Êóçjl2– ôà6Óœ}íISÛq¸Ÿx—á'Šü-ðïá¶©á›Q«ëÿ Ýg’ÍÍÌS(ûTq콎™À¯Yð·Å‹jshv¾×t˜–Òi&¹Ô¬Í¬I*í ç;Ù·Ût¯j¢‡;î 6Øù_á‡5í7ög¿Ðu2æ×S{}mVÖX;‚f–àÆlà¼s‘޵{À’xÛÀß³„ºF‘3x›KÓdhl'¶•¥3 „hÙ ç¾›¤¦ê\O0ø;â?ø¯À6:ßÄ7û'[™æ[›ymvªHʇ˔³Œ¨“ÏQ^ŸE ê4y'Ä߇º×Ä(ÿ²å×f±ðïÙÜ\ØZ"¤×’òU^àå–.€ªžrpx󟀟 Õ±èÀŽõôÕU†ÂÆÞê{Ø-ãŽâëo" W“`Âï`2ÛAÀÏJµJNöAETŒ(¢Š(¥¢€Š( Š( Š( ŠZJ=袖€Š( Š( Š( Š( Š)h(£½-%´”QEQEQEQEQEQKE%Q@-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEsEÿÓýû¢¾`ý±üUâ?þÏž$ñ„õôNÔÛùW6îc•7LªpæAÅ}<¹* ô®Ê˜9F„+ßI9/üEÿíÆ1®ISì“ûïþBÑ_<øËö‰Ñ¼=ã ïx[ÃZϵ4›RG¶¥’È7"ÈîÊ Œ¿0EÉ#ߊèlþ8øOYøXŸ<1g¨kÚ|¸E³±¶2ß™üÏ) 0‘"?3Æ äV²Ê1*1›ƒ´­ožÞ—é}ú¬m+¸ómúo÷u=š’¾zð‡í ¦kž2ÓüâÏ ë> Öu˜å“MMVH¯+¾DŽHÙÔHª7lqî@9¾,ý§¼-áïë~Ò¼?­x’? *6·}¥Ú‰í´íà±6àY‘Ag=J°,“çìÔ5µúZ×µï¶úzé¹/0£ËÌå¥íóÿ†×Ðú^Šø¯özø±¥x;öOÒ¾%|EÕgžÚÜÜ—žFyî&fºtŠ4ÜK;¹!PgÓ äz_ƒ¿hÍÄ>.Ó|â k^ ÔuèÞ]+ûbØB—¢%Þ茬Ûe Éðqî@:ârD'V12ƒi¿ðïoE«ì·"ŽcJQƒnÎI;zì}ExÄïÚ# u4Úµú;;§m¾áC1…¥í=ד¿žÛùE|ûí iß5Ÿˆ~:Ð5â\-¬–z®Ë©å”'”¶è¤‰|ÂáTƒŒç'šOþк?‹|eoà ÃzǃuÝBÙîì ÕíÄ"òù)•˜oAó20Jæy6)FSäÒ;íÓWëdîí{-ÍV:•ÔyµgÐtWxóö‚Ðü#ãøW^Ðu_x¢(ÕÍ–‘“ì66µÄŽÊˆXT’é¹s¯/Ç/ i çø«â»KÿØZ3E5ý¹ŽùnžP„B ,îø ƒ‚9$OöN'–ä~õ­Ýßm7×§qýv•ÚæÛ–úžÍE|óàÏÚ#Iñ'‹´ïx“Ã׃5=r)&ÒÆ±n°¥êÄ»Ý#efĪŸ1°@¨|]ûEiú'Ž5/‡¾ð–·ã=_CŠu/츢´®èÕÞGP]—£¯@I ÿbâ¹ýŸ&¶¿KZö½öµôß}7'ëôyy¹´½¾~›ŸFQ^W­|_ðφ~?Åï[^éT6ñÏ%µÕ¹Žö6•Äi @ND­# HäòE`|<øÑ¨øã]‡DÔ¼â %Ý»ÜÛÝê6ȶΈWågGo.B*Œ JÉe•Ü%S—H¶žÛ­ýmÖÆMIFú³Ý)+áÛaãÿÚKâgôë_jž ðO¯F‘hr‹kËÛå@óÉ-Æ‚¦T*Ž#Œ‚ZÚ6Oüýœ-­ ñÖ¡uªA¬Z[iÙ`¼û$óŸ–G^ D;YÞÆO¥zpÈ/VžÚ¯i&´³Ó™]]ÚÝ®—S’Y•¡*¼•_]5±÷MñwÂý7Á#ÇšTš7íwãk¸šF]µ«+´ºS(‰vþ:mÍqžðïÄO‹_~.ÛÅñC^ðÅ¿…uhm¬a´•Ö4’ÿ4R è`Çš|Õl¢“mÆKw˵¯Ô?´]•¡vÝ­uÚþ‡è-%~eiÿ´¯Å]_öo±’ÛP·›ÆºÏŠG„-uˆ£O-÷~Ú±€#ݰí\ ¹!±ÔW©ÚAãá›ïj¾4ð×ÄucršÌ‹<Ö×ðEçG,`Gå|¾@÷àjðµZ|Ñ©4¤¹¬µ÷¹ÛZi¦×ßb!›ÂVq‹¶—}¯¢ÿƒcî*+âÿ ÷íñOÆÚ%—Œµ?ø+À—I¥"è’­µííþÁ$Ï%Æ•#È@Á¦rMŒZŸŒþh ôÍSÇ×—¶ÇÆê¥ä‹nòéo)vŽîL…dHøwb2N;er© Ñ{G¯-ž—\ÛÚ×·O?[\³+FU9_*ë§{wTöµÅÔ« IÕÝ‚¨Ï$ð9¯Œüyñ¿Bñǃžøcã‹=VËP¿Ô“U·Ò¯á¹I#[BÑ Ön7WwpqXÿ´æ¥/¾8üý›ÄÍ•®^Ïâ eW?¾µÑÐÏoãþYÉ2Ùþ%N½+ÍÆeµ(F›©§2½º­Z×î:¨â£QÉG£·àŸê}ÕœŒŠ+ÅÔ¾ðæ¡®ÛiwZÌ–´«ebŠ÷3•þ‘Š‚Ç°$WÍ¿²÷Œ´_ßüMÕtÉç^%*òC»kÈÎwúö³áφ߳-׈?g}cS]Uñ¾¡¬ºÉquomq:Ç}yœ€å[n2Kg½~…\ÚZÞGå]“  íu 2:—b”òö¸Æ1Æ=1^­^'Sp”àÝœn¹—+åVÛ—óm.Ö8á”r©(´¯{;j¯çÑ“¯­|5Ô¿hŸƒ ð߯:Ï¡P»ûMö¡u-ÕºÊöëˆâgTE—hÝ" àÈ+Õ>üSð_À|_ðoÅ9ÇZÔ¼Ew¬éð´/$º½­ò/¶ÁT‰]™JíÄÛz†Çè\öö±,Ñ,1§E@GÐ(x ’D–HÕÞ,”b+ž éOÄ´ªÅR6ãËo‰_I9'~[uµ­ó,¦P|ñ’½ï¶›[kù_sòOIð¶©â?ØOáíí½Ýͯ‡5èu=B-?wÚ¾Åos2LÐ…Ëo‹xc‘·=«Ø|&?e_øÿÁ‡Dø‡®x¿Äw©{¥ÛKuv!š!æ•Y1a0ûˆ¡ê+ô@Ux­--ä’h Hä˜åÙT)cêHäþ4ëñS¨ª{®7”Ú´­ñt~ë½¼­u§¡O'QåÕ;$×nÚéøŸ&xŽ y¿m¯É*†’ß:gøIºÚHü ßÛš8¦ýž5Hf8Ž]KGFÁÚp×ðƒ‚9é_^ãš+Ê£œòWÃÖåþ´¾ömöÓ3²xju)ßã¿ÊêÇxökøAàmzÓÆZF“5Ö·j…`»Ô/.oå„0ÁòÍÌ’là‘Æ:ןx¢ y¿m¯<ªH<'¨‡zÔ0[[Z«-´)bX„P¹cÔœw>µÓƒÎ£JŠƒ…å4ì½õgummÓUæe_çQÉKGk«+º³¿è|¡xãÃ?h¯Š3|\¹þññÃX_i¥Â?Ù®#·Ò[5Aã,0§œOðæÚ~ûMøÓð;Ãþ;ð^kÐüGi©]µŠH—2ØÛyÐM-¸*¶«ÑK•÷LöÖ÷qn¢I£=UÔ08õŠ•T*…P8V‘Ï «SÅ*oÚG•=tj+—Ek¦Ò]]ŸB]'Nt\½×~šêï½ûùŸ ÿ†YñŸÄo·‡þ!k~/ñ­Ù¼Ó-æÔ.®Ö #ÚUtÄjUJ¶â3Â÷—ñÃYø-áx»Å^ø«}ðßâQF/­bV{mBKxó™m$dKv©Àä’ ÎE!³´·’ImàHžS—eP¥«×ñ¤šÊÎâH渷ŽY!9Fu Ê}‰VÐâ(ªÊ£SqJÖr‹ë{;ÂÖò¶ú܉eÓåVN÷½Ÿk5ïç#ä†ñ&‘ãŸÙ&Ë^ý¨ }.ÓX¶ƒûPÇü]øÙñÊ_Åwr,µÛxDV÷÷6±:Il ,±Á",™ÿl8¯Ñ¬Í.*!Ÿ{/hðÜÉÉ%w;µi'£Q]­oË–\§ËílÒw²VOKwgÃß´ïÃk/|(ðn§ðãAé ¼Ac¬Ief„°³…˜Ìê£%˜3brq¹JÅÖþ#øGö‹øñð¦ÓáEçöö™á îµ­Zî(äXmCCåÛÄÌÊ?xîOËØ ûò †ÞÞÙY-ãX•˜±‚Ç’N;žæŒ>ËI*‘ršç´¯üêÎêÎíjÖ«W­ÅS-¼Û‹´]®­ü»[·Ü|àÿøkökø»ñ/Ã?.ƒ£øÃUþßÑõ9£sisçÆ«q˜ª@’6Uù{Œûg?ö„ñ¯‚¾/iߵߺê¾Ô¼yaj ð•ŠåS˜I@ßrF{Wèŵ½ÜF ¨–h›ªº†SPjUUU ^J¸gÔ•hb½›ö‰YûÚ;G–öåº{=ß㢖]7G›Ýô×{÷ý†~.x3Ážý¡þ7…ôK îµ=XHlíâ·2²È åªîÆN3Tþ<Ú·¿kïÿïœÅ¢ê‰¨xVîb>H纊I,щéæH̓þÁÍ}çŽk‘ñÇ€ü%ñ#@ øÛNMSLy ¸ò™ Ím"ˈñ²º²:‚ °=º^^32•xRŒ·‚j÷½ï&ÿ[|ŽºUNSqêïø%ú ñçÄ|0ðÅÏŒü«C¢h–mÍupHW ;A?30ŽõùÕû&~Ñÿâ·ÅÝ$øÊÇíž9ñ³Ï¢Å¹÷_E4GEòôg ã‘_¨¡B¨QÐqÏ?­•ç&t´|Åñ¯ö›Ñ¾ üGðÃ{Í çW¼ñõȶ†KyQE¹2ÇgF˜&xô5ôõx6½û>øWÄÿ´zõÜ÷z—†4ù,tû öXd‘˜›Ž›Œ€;“€HaÊ‚=æ‡` øöêÿ‘Cáý” ùO_pWȾ6ýž~#üSø‘¢k<{myà 뉮iº–”-¥ó­÷ u¹»iä2 A+nð8ÃŽàÏ­¥–("yçqq©ff8  d’O@|IûÁ6·á¿ˆ¿ÎMŸÄ_êzžžÄó4ø¶[A&÷ü¦aìE}‘âJñVƒ¨øc]„Üiºµ´¶—1xŒN†9|e]w)#*ÀŽÆ™á¿h~Ð4ï xjÍ4ý+I‚;k[xó¶8¢]ª£9'É$’y$ži\ ªò¿Š:_Ä­NÒÁ>Ûøzât‘ÍÀñsÉB>_(AÈlõÏjõJ+:æV:°x§F¢ª’vèõGÌžð×ímâ-:ãÄvKŽtk–°·¾[±?1„¸Úww¬‹ þÓÍq+A¦ü91m»íµ åsÆì gkë:Jçúšµ¹ŸÞ{ë5Nw?eßüϾØxëMðô|B‡FƒS7ʺM§’Uv–üÞfwdôÆ=ëÐh¢ºaUcÂÄ×ujJ£I_¶ÁEUQ@´”QE-%Q@Q@RÐQEQKI@RÐQKI@Q@Q@Q@´”QEQKI@Q@´”QEQERÒQ@Q@RÐQEQERÒRÐQEQKI@Q@Q@RÐQEQEQEQERÑE%Q@´”QEQKE%Q@Q@ IEQEQEQKI@Q@Q@Q@Q@-%QEQERÒQ@Q@-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEsEÿÕýû¢ŠZJ(¢€ (¢€ (¥ ’–’€ (¥ ¢ŠùŽ_Šž+ñ/íEÂ/Ë øwÂ:QÔ颾øçãߎÿÿf´ñŒ5ý8x¾Ó]Óm%Ôôø|»y¬n/£Œ»G0"6h˜‡aNv’0O¼xö•ø%ñCÅ“x#Á'‡RÖ"„ܬ!%ÎN °4ˆ«*üHHü)ò…Ïv¤¯ø‹ûOü øUâð¯|Q®­By­¢Š[™-á=$œBå/|¾8ÁèEzµ§¼!}áñõ¦³i/†ÞØÞ @L¿eû2®ã!“;B€I+ë—Ÿþmìüí‘GiÅ´ò\3I€pJKŒqE…sëZ+â/Ú;ö”½Ñ¿g;Œµ(gŽ©cekyqjí‘ÜK廢J²ú0àö5ÝxÁÿµ®âÝ6÷Çÿô _ÃÑ;ËKM­ç™ 0P’™R©'éò…Ϩ诛ü_û\~Ï~Öµ/ ø“ÅÐÁªé´–±Ã4Ó@Ȩîᣬ‹—û£8Îs^¥ñSáΙðüüU¾ñœ^û:Ý LÊ »Bø C¤“´(ù‹|¸Ï¬;ý%x‡ÃÚ7àׯRçBðˆâ¾Ôíb½¤‘Éo9…ºJ‘̨̇ûÊéëOø—ûE|øEªÛh9ñvšµÜfXìaŽ[«£þ3 îÜE˜®{eñíûDGaðCÁÿ¾x–ìsĺm›ê ªb{6šX®•¼Õùv´l¬HJžkÛ~þÒ>*x¦çÁžñ4:ޱm¸ò6K–pe„ÈŠ%LŸ¼„Žý(åasÜé(¥¤1(¢¼Sá¿õûßø»á׌äõ]à\ÙȈ#ûF™qÌ/´`‡är?‹ð¦®{e%D÷ñ˼’ªK6v!`öŒ£©ÀëŠæ|7ã= Å:tš®Ÿ)Šïntÿßb2׳4ª ç.‡o¨¢Ã:º(®ÇÞ>Ð>è«k·?gH3ÈUBÁHŒÇ^>´%p;šJùÿàÏÇâF‹¢ÚêWÅâ{ø]終–%h÷œ0ÆÑŸ¾¥už/ø×ðÏÀÚ©ÐüI­$ñ¨’XQf…d4¢5mƒ¶8ÁèE7{ Inz¥Ê_øçÂ:_…‡¯µh#ÐÚ5•n÷æ6Gû¥HÎIÏs\ï‚~0ü=ø…©K£ø[S7ÐÂnL2C,.` ¨dQ"®åÜÀdzŠ\¬.N¢¾]ÖiŸ iŸôï ÇyÐÚÖèÞÎÖ÷twP°XÑ09Sógå?Q_FhzÞ›â=&×\Ñåóì¯I•dܧ¾ÖÄS”Ü“5h¢¼ëžxP“Jñ^µwº]yrnÜÑHæ4*;‹2Éã¦)$ÞÃlôjJó­'âÏÃÝkÂ>;³Öb]ÉÚ)î% ŠDÆQ•À`ß0ÀÆNEWðgÆ/‡^?Ô$Ò|/«¥Íìiæù. ÷ÑdU,¾ëš|¯°®M¢¼Å?>x7Z›ÃÚî²þØž8¢–o$2Õ‚pA䊹㯊¾ð‚‡‹ ê:_ÛI.œÞ\’G<žI–0v …lIwr0æG¨ÒWƒüøã üJÑ´›k«˜ÓÄ—vÍ5Å´0Ì‘#!!¶³‚1Œ®ËÅþøP—JñNµŸw ܘ¤ ¸Ç+²&ÐÜK)x¡ÁÞÀ¤·=Šóí'âŸu¯ÜxóMÔ„º-¡ešo.@cd 2²ÞÈã…ð“ÅCÅÑø‹SƒÄøŠÈêRý”E Bm <¥»îU,Ê:š9X\õúJó?|`øwà+øôŸêé ü«¼[D<Á?¼R5b£¾HéÍex«ãW‚tO',u8î Ô­îNq’G4Ñ!![`Ȱqõ¡Aöd{ áßþ6øâ^¤ÛËrƒÄw6bâêÚ(eH‘×@¬àŒF>cLøƒñ#Hµñ^‡à­3ÅPèšÂê–"æ mä“íPJTýiPÒ†ñê)ò;Ø9•®{§Z+ Äž&Ð|£\xƒÄ×Ñéú}¨I¥8Q“€rIàÉ®[Á?|ñæâËšªÝ]Ú¨’H)•öHªÅyŽG¨©åv¸]ExÖµû@ü"ðü÷6šŸˆ#K‹;‰-¦c‘Þ9"Û¼²ª’qùsÀ9»ÝKƾÒ<-ÿ ¶£ªA‡å$âì·îÚ91°©wd`Niò¾Átu4•æ~ ø¿ðÿâ üºW…õ#=ä1yÆ)!–1gÔH«¹rG#Ö½2“Mn …âoxÁº4þ ñ=ôzu…¾7Ë)ÀÉèêIìMs^ ø¯àˆ“\ZxSUK»›@X)•FÙ VÚ}@ÇOZ9]®=’¾;øÉñæh~ð‡‰F„&¸ºMZÿìOq%°· #F\6ö-’¹ÆÑÎ?H|<½]CÁšeâë2x€HŽ~Ý,?gyðì2cÀÛ»Ó¶jœWbR»±ÚQExK~ÒÿÒi­Ûıo…Yî¥ùŠ_“æ|ÿæ¥E½†ÚG»Q^iuñƒáÍ—ƒ,¾ \ëQ&…¨8ŠÞ}®L’ÃËT »~Q¾\gƒV|ñSÀŸ¦¹´ð¶¨·7v€4Öî è­ÑŒr*¶zãÓÔSåatzÆiŸ¼«é:®·e©ÆlôI&Šùß1›w€fE‘X¤yö­¯øƒIñV‰iâcq§ß'™¥7¦pàã ã‘ÈàÒi…ͪJ+É|QñÏáoƒµ™t]ŽÛ}¦tD’Qn†VX&Ú"…öÏZ¢¸¯|EðO…´;^×uxm´íUãKk‚wE!•w! ¹Jó¸ñŽõ™àŸ‹Ÿ¾!Þ\iÞÕ’îêÕD #Å'–z8Y–S‘ÈÈäzÑÊ÷ £Ò(¯"ñÇ…>×eðæµ¯Å í³*NÒn‚WE*„ú+Ñu/èZ>‰/‰5Køm´¸c5˸ÛBF1ÔZ9X]W•øCãWÃ_jãBðÖ®./¤F–8Þ)"2ÆŸy£.ªäf¼ƒÁŸ´?†´WñMŸÄ¿,w6~ ÔmmÓÊfhlâœÇÿ)NÔÀfëT©±s#ëJJ¯gwk¨ZC}c*Ïop‹$r!ʺ0È`{‚:UŠ‚‚Š)h¤¥¤ Š)h¤¢Š(¢ŠZJ( Š( ¤¢Š(¢Š(¢Š(¢–€ J( Š( ¤¢–€Š( ¤¢Š(¢Š(¥¤ ¥´”QE}(¢€’Š(¢ŠZ)(¢€ )i(i(¢€ (¢€ (¥ ¢ŠZ))i(¢ŠZ)(¢€ (¢€ (¢€ (¥ Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Š(ü)) ÿÖýû¢–’€ (¥ ¢–’€ (¢€ )i(¢Š(‚ø£ñEøUðóÄ|@ál4 9n¤ãyEù{»aG¹¯ ý¾k¾øg7Žñˆ‘,c±ÝêkìÍ3R°ÖtëMcI¸K»+裞 £;’X¥PÈê{†R=ÅC®hšW‰t[ÿë¶Ëy§j–òÚÜÂÿvXfRއ™IÅ|!øicð{áÞðÛJÕ/u‹ 3´úƒF÷Å’6h’5!Ú¿(€:EôÒkó×ölñ‹ðÛöøñð·Æ—‘i:¾«¯'ˆtów*GöÛ¸B€ÞVѸu½›¡uæ_> ü(øµ´<%¦ø—ìy5õ²LñÔ#°Ü ÷Á¡0gÊ?·N»áoþÍ7/¡ßÚëZsx“E³™íäI¢.ºŒ),e”•Êç ; m|k±²ÒÿjßÙ°éð%¹ø†ÐlP¸·L¬#þȧ8¯¦aø?ð®ßÁ¶ßá𞘞´–9áÓE¬dIbq*H"ÆÝÊà0lgw=k¦Ôü%ák[Ò²Öíîo®m²&ý¡^@¢q²' Ò ð»÷p ~…ü@øðW⮡­ñÁ:Wˆo­×ËK‹»T’`ŸÝÞFí¾ÄãÚ»}Á¾ð·†âðw‡4kM7B‚6‰,máHí„oÊ"Q· “‘ŽsÍ>arœÇ‡|Wð—PÓ|1‡5=&kkÄOìXâ–X, @·Ps•„6vò9ã5ù+ñoá¿Ä¯üný õ?Hš®ƒá[Ï jš¿†2¥¼28·yP†‘¬­åò$r¹(¯Ôÿ~Ο|âoøL¼à=EÖÆð·v¶qÅ*yƒ°¨ù7wmÆ{×¢é^𶇭êþ$Ñ´›[-W_0¶¡u J“]›u+™ÀËìV!sœqB•†ÕÏÍßÛ‾/~ÄÚ'Œ<è|;ªjº2@`³l’ÚE\Þ 2Ž>SŒï g_Ø÷áÿŽtßü4·°ƒÄ–†E´hõ›‹—̈ÈÁb’æEbTŸá>µîsü ø1sáýGÂwÑäÑu{Ïí Ë#e ·žó9óä‹nÖ“?ÄFkß³/ìïàývËÄþømáý#Wӟ̶»µÓ Šx_w#ªS‚FAïG6–•?g¿ü4Ñÿh¿Úv^XÙê­«ZK9¼hо˜–˜o¿÷£Wßæ7.´˜šßö>øSªø’7ÿ…wÅ®gYCWBkÉ@2G’\É×å,ùýð¯ì—á{Çÿ¼Eñ³Â:7‰,üIâ%Ôôf¹‰.¤ŽßìÂá·.c,ñœ¦H#¯ZúÞï¾ðÃx*÷G´Ÿ@x©ÓÞkSü¢6m `SæH,|KñoUðæ»ûdüOÝ[^ëv–ÚäÚ“ÙºÈɤ=ºˆÄÍû6L{¸Îüu9Oº§†t/ÚËãíŸî ³ñeõÖ™>ž×ŽåѪ„ÆB3ÊÌ Æí¥¿†¾«øyðGáÂi®®>ø?LðÜ×À,òY[$RH ä+8ŠŽËœÂñàŸÂ?‹-lÿ¼!¦xŽ[0D2^Û$²FP®Fàp)s!Øøÿöäoø¯àO…#ðô¶z†‡¨xßM‚F²dh$su ×ü7ñÓIRG‡åZº/üµÒîØ+3zùNCBC¾˜¬½oG°ñ} ê±‰¬õd‚d?Ä’)Vý T]˜š¹âß´ |k¥è¶¾ðãëz¤îe°Ôã»û鄨"àÜ'ï@<ª­»2|Ýð;Àþ)´Õl¼Aâý*OØA­jÐL—D 2ö+¹#žé¬äÛ dÌ€–\òÕÏÜÞ ð´^ ð®›á;{Ùõt¸„MrTÌcRv+UbáAÇ@3““[Öv6Z|M„ o»ÈV5 ÈňÙ‰$÷5j¥•‘<—w-VN½ÜhzŒ.ù$¶™T¤” õ­j+"Ïž?eý[Hºø)á;;{¸dº†ÝãxÕ×ÌWYW9 ðÿ†z7‰îü]ñ*Í|i‡5f×.åµÅ”3I,Äà iÈo)ü€|¡q޵öðÓáÿÂU¥xoO³Ö2çíq[F“æ@Cê üÀyæ«ø«ágßÝ¥ÿ‹|9cªÝF¬³Â­&Ñü;±’=‰Åmíß™Ÿ.Ç‚/ÂÏð‡ãky,¢òØjJaòäNì!UÜQ‚˼®GjÛð/Ä_ZüXO…Þ9—J×gšÂKÈ5=-YX“ÆK,W<63·×n¹ø}àkÏ GàÛ­ÊM RÄÀŸgR l„ÆÐrIÈç'5„¾xÀfcàí ÓH{€o£¸ᛩÙ¤ê&ÇËØñï^éú_í)àkJhía¸ÑµHQå`ŠÒ–¶äñ’8¯¤a–¢YmÝd†U”‚¤{Åsþ%ðw…„×Ô“ý¢pø”XÃý¬Ð Sw°yÆÅÄ{ñ¡‰8Î2sDe`jçÌßµm‹ øYÖtÓ´ÏøH¬äÔ.%’«ªK:•Ñ_fàütϬEðþêÿâ'„|Sâ?‰šž¥¦I/Ø`ŠÞÚÞ[˜Þ2eLm¹ÔÆãqîké½GNÓõ{)´ÝVÚ;»K…)$2¨tu=C)È"¸o ü#øcàýOûkðÓoÆà³Ã¬Š`…leAj¦–%Ç[Ÿ<ø—L×>jÞ8ø™ðÃÅz5å­ÔÒ^jº^£†q=¨c,QÏå_;‚£/S‚GZö¸õ÷ñÏÀËCbläÕô;‰Ø ìi `xg§+KUø-ð›\Ö$×õ é×z„Îd’i-дŽNK?cžç5éqE$ Ž8ÀUUUP0  ¥)«‰àÿ³^­¤Þ|ðµ­Ü2Ï¡G]K«$Œê1ô®q´;Qý­¤¾½&—Oð¼@X±ÞæT,3Ðí$g® õ¯iÒ¾|>еÇñ6‹áÍ>ÇVpá® ¶Ž9ˆ½ó¨žõ¾ºŠºÛø‘la«À-šë`óŒ ÅÄeúí I8É¡ÏVÐí¢-ÈÖ6I4¦;xÙ²ìÅP©|6ñÞx.m¥°Ôµ±V¤ò䂜ߜûç<Õ¿|5ðޤŠ_hz´° ±½Ä*î«×ºíœWK¤hÚN¦A¢è–qXØ[.È …F‹èp)ÊIê [CÈ?g][I¾ø9àø,®ášhtè£tGRêñü® ƒU† rß´=þö‡Ñ‹˜¼È|[¥õÜ€IÉaœ€;æ½—FømðÿúԞ#Ð|;a§ê’««ÝAms2ÈA`]@8$k+Tø5ðŸZÔnu}_ÂUåõã—šym"y$fêÌÅrI÷¡Is\Vv±ä´}Ö*|=Ôõc›ÃvÞ%µ7ïñ/ÈáÈà(9ñ’)®ÙèXÜ®Ù`‘¸ô*Ù°<%ðóÀþY×ÁÚ¦öœy¦Þ%F|tÜÃ’`MRš±.:žEñ)WþïÂ^?åž¹Ÿûó}VMÞƒ¢j¥Ž·}c ú†˜$·¥€L“ËcÊïޏ­j†î‘Ié_^³¶6¿å1©a®ëdw,Äþª?*ý®V×À¾ ±]9,ôK8WH–I¬ÂBŠ-ä›ýcÇò³d䎴á; Q¹ðÏ„ü>ð¬Ö÷>ð펛=¢IRA#¢Kƒ ~l ÜóÞ´uº Ï‡>1Çi®üHñˆ<'auwàýìQøÓì¶æ†PÛúÇ…9”¯E3ó~‚èZ†‘ªèÖ:–,sé·0£Û<$yf"¿&ÜqŒUm¾ðö–ú…¥ÛXiò—g·†%HØÉ÷Ë(%»ç­O¡xCðÆ›áÛtÛ‹2An‚8Ô±ÜØUàdœŸz‰ÎêÃŒlk×ç÷Â?x“SÓ¼i¥Íã‹mP“WÔW²¸²·–Vi1Þb‘×t5ú^yâo„ß üe¨ WÅ>°Ôï@Κi›aìr(„ì9FçÈ?ü?áo|!øa¢\ëQx‹Ã–#„Ëx6´RZ™¥yWå,  ,œƒ±^µâËÍ#Tý¥>·…æ†âú ;RmAà`ø³eO³‡eã¼Â¹õ÷×|OøXþ#³ð>á{ Ht¯k–W—Ä,p‹8[2* R§ Ÿ—9¯Eð§ÃŸxî$ðvƒg£½Þ<Ö¶…cgä@ÎaÓÚ¯Ú+}䨻Ÿ'ü&Ô¼+¦üø‹eãYí¢Õ#¿Ö®— ZWÈÃ|ÇwðõÍsž ·¾¶ý™¾Kâô‘´k]CL“UVV8Ó¼ÆÙ¼vùEGä8¯±5¿„ß ¼I­kÞÓïõ!ƒö‰­Ñœ•èX‘óc¶sŠí®ôë û4Ëëhî-%O-áu 'M¥HÁÔ½ªÜ9 h5ŸÝÝéÐÚÞØÏs2³Y*I» O˜Ã‚N6u+Æ=«æ†:V7~2Í-ºHךçˆRÊõBáAÏa“_BxcáOÃoj/«øSÃv:UìŠTÍ ŽõPG@}t–^ðîke§é¶ööú¤²ÏuFª³Ë?ú×ó3ÿ={Ô©%±Vló߀24¿üîwì›Aø,`Ò½z¨iZV›¡éÖÚFk•š¡‚ h¼U;Wê$îî4‚ŠZJC (¢€ (¢€ )i(¢Š(¢–’€ (¢€ )i(¢Š(¢Š(¢ŠZJ(¢€ (¢€ (¥ ¢Š(¥¢’€ (¢€ )i(¢ŠZJ)i(¢Š(¢ŠZJ(¢€ (¢€ (¢€ )i(¢Š(¢–’€ (¢€ )i(¢Š(¢ŠZJ(¢€ )i(¢Š(¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Žh£€?ÿ×ýû¢ŠZJ(¥ ’–’€ (¢€’ŠZJ(´”Q^ª|dºÿ†Ñ¾xsMKöþÊ›XÖïB£O¶Ï—j¡@!¤ž^ŠHÂÜâ‹ï}+À>ükŸÇ>?øƒð·ÄÚjh¾$ð5ò(…d2-Þ›tínã,üü‡\¿.OÌ{ý6€(¢¾øãûC||øIãM#C°øy¥êšGеˆ´méõc·FÒ/ˆùCÜäô´%q6}ÃEq~Õ¼]wàëM[âV›káíkl­ymor.mà í´‰ÈPA@œ Gj›Ãß|â멬¼+â=7Y¸·É’;;¸nàîXÙˆçÖξ’¼áOÆ«¯ˆÿþ)|=ŸKK(þßYYÇ:È]®EÔO!fR]»1€Ns^ù,±AÏ;ˆãŒfb¨’IàNÀ>Šæ|=ã_ø±î#ð¶»a¬=¡Û2ÙÜÅpc>Ž#fÚ~µOXø‹ðÿÃÚ”z7ˆðEæ›®èž5þÙwvó­Á…ôØb‘6‰ŠKÁ²qŠiôÍÌxƒÆþ ðœCâzÃG{£ˆ–òê+s!4ü ñOÃO j1éšž³)f."o"â9žJ|Â9• mŽÌx#ƒó'Á cþøÍῆ_> éŸ~Ì_¾øÇþßë^&ñ,víiþ½©Ï©Mmnÿz8 ÌBÀÎOârÓÒÂhñŸÙ{þNSöšÿ°Öÿ¤ÓU?ø(~¡â1ð·ÂÑ¦Ž ø¯MÒõ>f‚ÞkiY§ÎÈè¥ÙyÂã¡ úOŠ?c„ž)ñ¦»ãÙ5é:·‰&Y­]ØG4ˆ»T”Ô£¦sŒœu5Ýéÿ³‡ÃK…úŸÂ i/üKáÍZS4ˬßÏq¼í*RyXÈ›“k §‘Þ‹«Ü,|·áÙ¿ãñ›ÀŸtß ø7À–^i­µðì×(÷útñ”0KBˆûk¡círúý–Ÿñ›Å?5/‡ßüy¤húÖ™­ëþ&…Ååݤ+ö†DŠ'x’$Ú‹Ž™WÕ~ý•|àé>0M{ÄÚõÞ¿û:c[»¿¶´2DГ2¹LùnÊ €qØVˆ?c¯ƒþ"ñ~³âÙ_X°Ä³ _L±Õnmt½JQŒµÍ¬LMØÃÃsž¦Ÿ0Xüð²×5O~Ãü+âFgÐ|Mãk]X•¦oÞik}u‹wrså…EÀ'€Š:q_IxÏáwÃO†ß¶ÿÀù>höš Ú®¯‹Ë[(Ö™m``•¢LÄ;®ìe‚Ÿ–¾¥³ý˜~Ú|ÿ†~}.KŸ)‘Ò§vž9cp$I²]$mÊAã§# óÞý‘>øÇZOÄÈnu­kÅ:*K †­ªÜ_ÍäI–!c3åÆ Q€¥˜õ4s‡¼à?ˆ¾,ülñEǃ|#ã[«]èl|Q5Á¸³°¶Eð[Çn‘ÄñŸ3rᙉÉ;E}¢|1ø‰ðöø•ð÷âUý¶£s¦h>#FÚY&H¬$²‘â„¼Š¬|¶.£ŒÚÛüyû,ü8ñÏŒnþ [_k~ñ¥C}wáíVãK{ÔŒaÀ€¨à2>µÚèŸ< áÿ„úÁ›îŸÃÚ½µí­ÙžêI®¦]D8¸w¸—2>ö;‰ÎhrŸ¿eßÙ«àÆŸðsá|-e¨ø•¬ôýYõ[˜Ä·¦÷bÈ’ Ûç8TPvª€ b±>*Ã%íÿðSUÔµþ…­ÚY18T¼Š)^R=IÕqþЯ¶¼)á+ÁžÒ¼#¡«¦£[Ein®ÅÜE „PÌy'­ywÇ‚ñ|_°ðíņ«ÿÿˆü#«[júV¤ ûA†HXy±¼{ã/ÑåwŽv·;pg›Qµ¡ëºÖ‹¤xJºÐõû8u:õ SÛ΂H¥Fꮌ úø›ö2ÑtøËãþ‰ ÙC§éö~8ž8màAQ¢ÚÃ…UPÐW×~:ð]ŸÄ ÜøWSÔu.£5Æ“{6v¦76\@Ë"Fäd x‚¿cφßüJ¢¼×á/ůümðMŸÄ‡×¦÷J»gçC±KÃÇ"U׌BàŠôªMQEQô¯!ñoÅË/ üXð7©´é..+ñUÏ„o>Ã}q¥ÙÁ4 1EÍ:žUä ®V+Ÿ`Q_1xÓö Ð¾|±ø¿ã/ kÚT7×é§.—=¼K©,²;¢ŒË³kìÈùó‚8ªÞý¬§Áôøï&´‰àÉ-ð^nQØ M€nó<ÃåìÆwü½h³ ž»E|±àÚ€øëÄ:6•'Ã?h:Wˆ¿ãÃWÔ,cK)2†D2yr¼‡Qò™Aàwé_ þ3xkâÞ—âM[ÃÖ×VðøcV¾ÑÏ`Ûdhö³e û¤àúE˜îzíå~-ø{ã€m~"x^Þâ×O»žîÝc»UICYÎöîHFq‚ÈHç¦:W+ðâþ«ñª/ø¢m<)a¬O¦hw XË —îçºl¡l¬{{)$œŠ,+žýEyïÄk>ÓtËýþ*’ÿQ¶±’o·ŠrÁî¤Ïü³‹w~Ez!‹IKI@Wàë>+¸ñ:¿†îü<º.§5…»ÝEü*•»‡ý[– gž wÔ•à,øÇ¨ø#ãÏ„>x‚Æü9ã›;”ÓµXHºµ§ïÚPNݲÃÌds¼mçpǿӰRÒ¤¢Š(÷¢Š^´”Q@JZJZJ)h(¢Š(¥¤ Š)h¤¢–€Š)h¤¢Š(¢Š(¢Š(¢ŠZJ)h(¢ŠZJZ((¢Š(¢Š(¢–€ J( Š( Š( Š( Š)h(¢Š(¢Š(¢–€ J( Š-”Q@´”´”Q@Q@RÐQEQEQE-”Q@RÐIEQKE%RÐQE´”Q@Q@´”QE-”RÐQKI@Q@Q@ IE-%RÐEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE~”¿…ÿÒýû¢ŠZJZJ(¢ŠZJ(¢€ )i(¢ŠZ£©j6:>u«ês-µ”O<Ò¹ÂÇjYØŸ@5ñoì‡c©üGÔüeûUø¢†ëâ%ÏÙô8dûÖ¾ÓØÇj gƒ3n‘±Ã88zõŸÚsÀŸ~)|2o†¾–4ñ-ݽž¯w$Æ'·Ò·]˜@S¾WQå…ã†'< ÷ DÓ<5¢iþÑ`[]?K·ŠÖÚXá…"èUÐ]OŠþ3BŸ?iþÐV‡ìþñ·—á?veìÛÇì ºˆ]ÏEÚ8³÷My—Æ_†?Æo†"øi­k– JFL3š@È9Ž@­ŒŒãê¿Á O‰:w¯i?DÅšuªZßKo1ž9Ú‘fó ©-"€ÏÀù‰íCØêµù7ðûÆ?ÑqqªÁ§Åe"Á/•¾9~yUòIØAqÜWë%xÃ/‚–Ÿ ¾"|HøoªÉ}'ÄKÛ[É-Ú!Ú›XÝ«%ÃoÎHÅ ƒGç÷Ž|?ñ‡öUý•í|;o~ÇÅâ¯ÙÇÆ_~Çð÷âwÄ«^íYêÑêm¥ÚZ:¥›«­¿‘måÆA üçæç¾z~ÚøÏ⇀>&Í©=¬¾mE£¶XÃ-Ïö„+ ÜåMr0}©'Ð,|Má‡Ó~Ñþ"øÍñ'Æž(Öl5O xƒQÐü<–:„öpé1éq€“,Q²«<Žw¹pC)Á5óÝî«©kÿ°ÿì÷/u µ=>ÿDZǫ\]Ìò¼°®§~¸•ܒɰàm‚½WãÅçÁ߇þ éºgÅxÿÅ‘¤Ú߆tý)î£Öç’ªÖWD‚ÞIÆ#wF Nr@}ð#ön²×?b | øÇ¦Mjo-f¸žÆ+»)noe¾€†Æcž128`U\ƒWê#îjü°øWá¯ZïÇ_Ú_ƒþ6Ò¼+gŠc[¨µ $êO4¦Î"OÀŒ`äó_Qü9øñÂ^%Ño|]ñ³Vñ_‡ôÂßK’ÂÖ×Î3ýªæÞÎ9BÙ`ó\mçì£ñ+Nøã?ü5øÏ¨x6/êQº²ƒI³ºA(b=Ábp«Ø¥J²<§öæ²ø¢~ÊžµñÖ§kâÛøŸNi®m-ÿ³íî\Í;B«<ž^¢\ä‚Üg±—Á?´‡Çß‹ß_ÙŸáUÇÀHg+›_ÂPZ­ºbL],ªþwÚD q1˜™KcibAR¤©|ÈV>€¯ÈïÙ»Âß´F³§|W¼øYãÍ#ÃZ"ø×ÄK%­öŒu šQ9ÞâQqm8Æs_`|;øñÂ^!ÐçñOÆÝ[Åðñ?gÒä°µ¶iÀBˆ·WQ~úp¹Îœ äWðgà•§ÁíÅš=®­&¦¾*×5iÝâ[P}æ 6àgÐR½‡cåߨÒ]jÓöº›JrÚ¼1ø¡ d&ånîÊ$|øÀ¯]ý„c°‹öIøl4ìΜK×C+ïÏãšõ€->ü2³økk©¾± ¥ÅíÇÚ$ˆBÍöÛ™. ” Ãå2mëÎ3\¿ìáð§Å?4¯|?¾’Þ A¬\^xlÅ#4ÐØ^9­eB /‘)`„3„1É'{‚G‚~ÛžƒA›ÀÿtMSU°ÖîüW¡irˆ5¨­^ÕžRÈm’A-¸íÉèkô(œrzWÆ¿¿f?‰_5Ï:çâõÖ‘áÛMFÓTÓt„Ñl&K+«4 Ž.,Òüûß1_›zx°øðÏàÏ‹.ðÕß…uæ¸KÝža´¸–Òo‘ƒ²ÂÊëÈçdqÒ¾Pý‹m¥Ò¢ø¹á¥¾¼¾²Ð¼o¨XÙý¶ê[¹b·Š6 ’ffÀÏ­}!ñKâïÃï‚þO|HÔΕ¥É2Û¬«o=É2¸%We¼r7!O$êkç_Ø¥5-Wß>!Üi×:n›ãjz¾˜—q43Ibâ8┣r„$3ÖšØ]Nöî&+ƒwZv¶¢øƒ¢ >ñ%Ÿ }X%}ç_6øóáˆþ"ü~ð'Œu×¶ÿ„'À\_Û[ofžç[Ÿ÷q;Ç´*Çoθ“&8ý%CØQE# (¢€ ô´”QEQEQK@ EPEPEPE-%QERÒPEPEPEPE-%QEQK@ EPE-%w¢Š(¢Š(¢–€Š( Š( Š)h(¢Š(¢–€Š( ŠZJ(¢Š(¥¤ Š( –’Š(ïEQKI@Q@´”QEQKI@Q@Q@´”QEQEQE-%Q@Q@Q@´”QE-%Q@Q@Q@Q@-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEsEÿÓýü¤¥¢€Š)h¤¢–€ŠZ(¤¢Š(¢–€ J+‹Õþ"x3Añ–…ðûTÔ–ø•.$ÓìÂHòMªo™ÉE*Šƒ«9PO“Åv”Wàψ¾ ø‚ÚÌ~Ô×P“Ã×òéš‚l’)-¯!¼N’ª·HèMvÔ”Q\¶‹ão ø‡_Öü/£_-Î©áÆ…uB:˜ Àfˆe Û‚7Ý'§4œ’²f°£9)J)µ_’½µí«KÕMÊxÓÇøw Kâ_;KãG™‘ä¥`ˆ6ƬܒJóß~ÑŸ¼[®YøoÃþ ûV£~å ‹ì—i½‚–Æç…TpR+9W„eË)+úx|£V“­J”¥{µÒ¶®í+iÔöÚ+çYk/ÙþÍâ°ŒNÐ •àËÃ÷שøâ?ƒ>%i÷§‚µí<©É–¯€ØÄÈ„ðzS M9;FI¿S\VEŽ¡i^„ãî-/½£·£éE-nyAIE-%RÐQERÒRÐQE-%Q@´PQE´•ówÂÚ·Åø›Ãšoïl´ j7úTºÜ—Ví—–2hÖÞpÜàví Ízĉ¾øY¦iš·Š¾ÑäjÚ¶—Ù¡iÛídˆ÷û©òœ±àS°\ô*+™ñ޳­x{Ã7úχt9¼I¨Ú htø%Š n°VI™c\[,zZóŸ€ß£øßá=CÄŸØsøvãJÕ/t‹›;™cšHîl_Ë”‹ä8|Ž pMÛi+Àþ=|s?­<0m¼7sâGź´:=•´ñ[»\Ζ Í„‘Œ’ÎI½Ãº†£«hVž¯¦É£^ÝB’Me+¤¯o# ´lñ’ŒTðJœÔX š+„ðWÄo xú÷Ävi̾Ô¤Ò¯|èZ .bUvòË}ôà 0⼛ǟ´§ðÿâÿ…¾ë ½m+ÆÑiöâÝ[ýî/5×ÈÌ<¼`–PcE‚çÒ´”WÍß¾?j¿üuâÏ éþ½Óô jZž‘&·-Õ»A5î›p h–o8pJí`œÑ`>‘¢¼³Å?þx+ÄVñ/‰ìí5½Nhíà³ e¤•‚ dŒ1@Ì@ð=ëªñ®·®øwÃWšÇ†´¼O©[…0éðM¼“’À²NËàüǵkS R 2œZOk­ý;™Æ¬dÚ‹½·:š+Äþühµøñà7ñ½®>„a¾ºÓ䵸‘%‘%´}–O”óé^ÛY4h%J(¤E-%QK@%-”QK@%PE´”QEQK@ EPEPEPE´RQEQK@%PE-RQK@ EPEPE-%QK@ E´”QEQK@%-%QK@%PE-”QK@ E´”RÒPE´RQERÑ@%-%RÑ@Ò’ŠZJ)h ’Š(¢Š(¢–ŠJ(¥ ’ŠZJ)h ’Š(¢ŠZ)(¥ ¢Š(¢ŠZJ(¥ Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Š(ü)) ÿÔýû¢–ŠJ(¢€ )i(¢Š(¢Š(¢Š(’É1¼Ó8H㙘à*ŽI$ô¾ý™Þ|sûTj·öMÓ·†¼&&ÂCö‹…qö›€Iî6”?tW«þÕGâ.¡ð–ëÁ? ,..µï\C¢ý¦ÊiÖ·d­Õä§# !€ÿm—¶kÙ<à½áÇ‚´?øjo¦h6pÙÀ£®ÈT.â{³™RI'š¥¢òŠ ?¿kÇvçÈð‡Æ¸ãÑuEé> ´Rlf=7îŒz¾ârXcîºñÚ3á*üjøA¯øÞO³ê²F.´»€ÛßQµ>m´ÿ‡(‡ G5³ðCÄþ2ñ‡Â¯kŸ4kÄòZ¤z•¥Êyn·Q|’8QB7¨€Àv¡ì«WÉ_äá¾:ÿ×}ÿDÜ×Öµñü¿ ÿh/ |Rñ¿Ž>ÞxhÙxÂ[GdÕZñ¥Ai*àCQ“#gænÝ9® ZjP’W³éèÑõ<:éÊŽ.„ê(¹Á%Ì쮪B_’fŸíže¯ *$j:fÕ'·Ú£À'°&»/ënõí>ßÅžÑô!÷y×vÚ™¸š0#b…bòvX(?0À$öÅr?~ümø¥ðnûÂ+»Ð!ñ<š…­Å¼–†é,D6Ò¤˜rèÒo;OEÇNEt>·ýª#×´ñâùü Ú"È¢ìY ÿ´˜€çÊó&ïMÜW;æöÜöi4»w{žÌ}ŠÊÖžœ¥TÞR½œag4ì÷OTrŸ´e¥ªxÏààHQCx¦0@QÈò_­}_0À Š€öPò¯ø±ðßZñçˆ<«i7ðÅáMi5+‘;:³Â±²mˆ*°/“ÑŠŒw¯j®º4Ú©6ÖöüÌñpž N2»Š•ü¯6×à%~X¯Š~>j÷š/ˆ->&^ÙÛøŸÇz¯„–ÔZ[-ø ¾<‹Á:–‘ˆ|EâÍG^±k±4¶Ÿd½™\CqµÕÊ`;OF5àƒuoV~-i·z5ŒövöÞŽ}×MqÃIu,à3mäõíÎ}Zøœ$éWIÆ7m¦’»vZYÅé{ÙÅ«]³Ž*Êt—[-^·¾öÞéßC‘ÓõŒßüCñÅžñÔÞÓ¼©ÝiFom °ÜO`ƒÍ’ðÈ¥ds·£‘‚9ö?ÙCÆ>(ñÿÀ/ x·ÆwÍ©k7ÆüOpʪ_ɾž$áTa@Àíë_=øÒÇYøW⯈Ðxâo†t=IJK¨êÖZ³7öž›s4#Î’Ê5u4ˆPü#ž¾ßûi†‰û3ø&ËS‰ášHnîTH6±Šêòiâb?ÛÕ‡±¥Ó§õx(Ûš¶š\’ºnɽm}^ºõA€œ¾³Ë+ÞÒ¾·WæVëÛÓ·Céêù¦ë]ý¦–êeµ‡Á¾Hv︻³'nà\u÷¯¥ëçëÙ_ö{»¸–êãÁ/,ÎÎìL¹fc’~ÿrkó¼L'+r~výúG‹ÂÒsúÒ½íoqKóœ-ø„ºÏí<7mço3Ü«ã¯Ú³^øoâ„þøwâ©ü#'ŒõùtëË»hašO³ Y%`«22†Â§‚jû¼âßÂÍ{ÇÞ<øYâ"æÚ _ëRêW‰;:É,2ZÉXB£ûœ¨ÆyÏèÇsâ™ðÃ/ˆ´“ªøÅþ+ø“w¯éíñ÷À—l¶¶ñÃqe¸Uº‘Ñwƒ$@’zŒðwu>,øŽßöŒñG‚~*üWÕ¾ ø^Ê;/øEä³±_²j~l!®'šíâ‘O—1ØUŠ ªž¾Å¦þËÞ:³Ñ|=§I¨é¦M#âUߌ¥"I¶µ…ÃÜ2Ä¿¹æp%\©8?9ã=Æ?~Ö~.»ñ7„ü4¾Õ¼®.ÔkÞ5Ýœo£ïHÑ¡«îhÉ‚Fx­.‰±—ñÓâ?Ä_ü+øcàxºßUñwÄMKLÐcñG‘ FË2ŸPŠóçP]BåFNÞÄq:=ÏÇ?„ÿµ‡Ã„^ øyã/ø“MÕ¯OÛ­­£ºií %£–HDp’FrÞÊK+câìå£x'ö`ð€ÛÇvþÖ¾Ýé÷ºGˆu jºœ2–Q23°ÊîPNÑ·;€*Þ3à;ÿˆ^>ý¸¼®x»Å~ñn£¡xT–ò/ %Ò´ËYPà 3HÌÍ5IJû±€ŠK`.üYø³âDý¢¼[ோ5oƒ Ø×Âßd°Ae¨Ç$ Ó\Íxñ:¾ÙIVW*‹Œdsú[à¡v¾ÑEþ¸ž%¸pyš¤k%ól¸U‹1'Þ>^xâ¾TøÇàÚׯÒxŸÁzü š§‚µíñÚ>³ ã^ÙÃ$aNøÑZ6Á9ÆkèO‚ÿ âø?ð£Â¿ !¾mI|7a ™¹uØehÇÌÛrv‚s…ÉÀÀÍ)l5¹ó÷ìeÿ_ì¡xƒÿF%rÿµÝßÄï‡:—…~"xSÇúžŸ¬x›EÒ$Ñf!œ°•ƒ¼M.çÙýþ2qŠÚý–£ñ·‚ügñ'À¾(ðF³§[ë>)Öµ»MfXàþÌ–ÞyPDÄÆo1À,•·Åž+Žý¶üqàïAðãᧇu«=SÅ2ø÷FNµ¸Žk¨„ '˜e´a wŒúCêÐêø¿ö#ÿ‘?â7ý”ÿés×Õ~1ñ Ï…<5â=ûÄ3Y u°ÓR7»¸%‚í‰e’$$g's¯óÚ¾jý|7âïxÅW>1Ðo<7u¯x³\Õá³¾T[„¶¿¹3E¼FΡ¶¶Œƒ‚G5+aõ9ÛþF¿€öP´ý××Þ0Ñu_xjÿEÐõ¹ü;}vbÔ-R9&·!Ü‹2²@#æSÁ¯”ÿl?øËS—áG‰|!á­CÅ+áXê÷¶ºbF÷?e¶œ¢Ë$HXã$dÍ{÷‰uoŠZïè5Ÿ…zuŽ“â‹Ô‚T³ñ@•"·WÁ’;dÒ0‘WŒ#ÝÜŠ: )øoÇ>#ŸÅ3xKÅ—:UµåÌ0C+A°Þ¶è‹’X’qXßµWü•ÏÙÓþÇÿÒW¬?‚ k߆ž5×u=nO¢øÓÄ ­ëBÙõFºŒL±Ç*YïEA…L “w$å±ÀÓøùkãþÐtmÁ:ĺWƒ5øõ;ílÇöa‚kr¤Fâc)dc‡ÝØš®¢è}É_~Åßñéñ‹þÊOŠô±«íJøöU‹Æþ ñ§Åx«ÁΙm¬x·ÄZõž³4p 2{k›ßÜ¢8˜ÍæH½Aˆ.ÐrÙÀ©[ ™_µOÃïøa|â]F¶³ÕõÏhÓ^Þ*qpí#¾FËmás´v¾õ¯‹ÿh/…?´gÅM~Î×Â÷¾¶ðÎ…ªXêÚh¼kÕ¾3Ú"’·\oC)|ÁÙ·s^éiâ/‰^øi&¿ñEOxšÐ»KcáT/ç£KˆÅºÞÉX!÷ºŒƒŽÕôy¤”ðT/UJJ÷W»Wµ¾ë[Èòðk–½KA¤íÓMžÿ`/ù"ºÏýZïþ”šû~¾@ý‰<#âÿü¼°ñ®‰sáýB÷^Õo–Òð*̰ÝMæFX#2äƒØšúþ¾r[ž¤v )i*FQEQEQEQERÒPEPEPEPEPGÒ–’€ (¢€ (¢€ )i(¢Š(¢ŠZJ(¥ ¢Š(¢Š(¢Š(¢Š(¢ŠZJ(¢€ )i(¢Š(¢–’€ (ëK@ EPEPEPEPE´”QEQEQEQEQE¢Š(¥¤¢€ (¢€ (¢€ (¥ ¢Š(¢–’€ (¢€ )i(¢Š(¢Š(¢Š(¢ŠZJ(¥ ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Žh£€?ÿÕýü¤¢Š(¢ŠZJ( Š( Š)h( Q@ IEQE-%RÐQE´”Q@Vˆïï |½¹ãµwaB€ª0 çüQâï x#H}Ƶ¶§FÊ­qw*ÃæûªˆËÀr{Qá_økÇ$>#ðŽ¥­¥ÜXî-Û|ncb¬ö`A®™ªÒ¦§+¸­ÖËË·ÈÊ<ŠN*×üNŠ’Šåußx#Â×–Úw‰üC§iw¼A åÜ6òK“‘de-Ï 5:r›´UÙr’Š»gUEsÞ&ñg†¼£IâjPézlLŠ÷°XÃJÁPþÓt4œ$’“Z™^ÂÒQEHŠ(  oxsÃÞ-ÒgÐðÖ™á«k–,zmœ6i#€Î°ª† w<×aK@ Eyö¥ñoáN‹¨O¤ë4Ñl/­X¤ÐO¨ÛE,n:«£ÈO±ÙiZ¶•®éÐjú%ì…Òï†âÞEš¦QЕaî hW §|.øg¤x¦çÇ:O„t‹/^3¼úœ6ñßJÒ}ö{…A#þ"[žõÜ;¤hÒHÁQ%‰Àu$Ö“⯠kÚdºÖ…¬Yê:|–K›kˆæ… ÌDb ¨ œžZÞ¢²´M{Cñ.Ÿ¯áÍFÛU±”°K‹I’xX©ÚÀñN½ñ»áO‹´›´im\êù‘¯.­Õ"ùƒ¾æ|ŠØï^žjtªÏÚ]Ý7¤S…švßko±ÉZUeQβ䲳[¿{OO¸ùÃö¥Ñ~+k?þè÷7ºÖ‹}â&m&ÚæÎi%Žßƒz¦M²¨Ë`(^µíŸ¾!øçàoÂ}" ¬t½cÆúö§‘¦Áa ÚiÍuy#yLÑ–,T|Øa¹¸Êƒ‘¹ñ£Àž+ñgÄO„Úç‡ì>×cá­j[½FO6(üˆö»«>[Œ cíKûJ|5ñ_Ä h𗀄2ø›ÁÚÍž¹aÃ㹒щ0x'ãž¡ñ‡Æô¨5ÍCRñ££ÛËt‚G´²±ýÌ ìùd˜•ÁÍwZf‡ñ{âÿÆ_øïÇž ÿ„@ð w’Å Åô·7—בù$¯ÙËŽ5[ÏÏËÎÛèþø›â™ðãÁKã=ÆÚ•Ʊ§]&¡ohl//T™’tœ©dY>e+Æ8ÎN\eÆP£8´£ÚI+©»¤ïË~^VÒ}S›jU"åÝ®›k_{¥¡óΧâ}KÄ?ðNç]fáîeÑuHtá4¬YšmEYc×deTE¯¶<ñÄŸ¼s'ÃX­WáLJä’ÖóVKͨިÅf¡”$q‚ HàîÏÊ:ã~+ý›üi¤þÇüðí²kÞ'’âÞîé"–8c’i/Vâ};F»Qr£$ Ó'èÐ|&ñGÁߌ–~-ø5£­Ç‚üV#µño$ÑÙKÛ¡n’¼k€8’4Ë’²6íÄ`«Rª¡%ÌçUÂöµŸ'ÜÚO’ú_MílðÔ±ç$íË.ÿkòëm_­ëÎþ!ø»Å~µ³ŸÂ¾¹ñl—Ë,vÓÅ…@Èf2õÉãŠôJó¯ˆaø‰igi7ˆu¯ 'g£_5“˸cl…AÜ£¨~i[›•òî}¾Xèªñx‹ru½ûu§÷3ƒÐ>,|QÕu»7SøO¨éV—3$rÝÉ}lé1Á‘•~b®5—'Æ_‹É+¢|ÕUˆ ý¡kÈÿÓÐ?g»M[±Ö×Çþ1¿63$ÂÞóZy­¥Øs²XÊÈ{®y—'ìÏe$'ü,Ÿ®òN^žpΕÃˈ¶ïÿ%ÿ#ëUlŸÚ?v6²éW}oÿ//Ûú¹ì>ñ'ˆ|S¢6§âo Ïák±3F-.&Žg( &èøÃdŒuâ»jâ|à˜ü¢6‡µªkªÓ<ßhÕîÍåÈÞl0 ÛÀìIõ®Ú»é_•sn|v=Òu¤èü7Ò×ÛæÛûÛ?0kÿ„Ÿ _ã¿À«Æð¾œÓø³ÅR&®æÝ7_®Ø¸¸8ýàöl×Ó_´?ÄXeoÙîûÄ¿ü?h±è¦ÞÒÆÏaŽÊÛí„"Ç·©là’@ÈÎkç¯ÚGKý¦|uñ‹Àzç„>Í©h¿ µçÔ »æ—Õ!"06Ç,É$'å?}IöõúÃÅ^$ø¥}ðz×ZO†k"¿®£á[­BÐ’&í,ZÚF ƒ‚vœâºC‹¹ç_ 5Žú厩©xÓÅþñŸ…n4«’n<>²¬ö÷»AHÁÞñ¼E ä¶×È_” ×åÀÝcľøkð[ÃÍ7Úhx’ßM‘C2ÛOý«u§jÌqжFù ’Ý«ìïƒß|wÿ æû⧇~¯Á_ ÿ`ÝØ]ißÛJ5kÙÃÊÛZ1†ãÊœü£rÜÄ}û$|Õ>|ð/‡~&xvÞÓÅþ:©Íû=Ìöfòöåóñ4оd2.ï-ùkwïan|‡ð+â^¹û<þȾ ñ^…o þÒ|g©iúñ™å‡I›T¸·7²Dv±È!‡M}«ãŒ^&‹öˆðÁEkrºÞµ¯Ï*4†ÛL‡jCå”`¦””ÉÎ2‡?ø)ðSƒöT×þ üa±3kW>!óÕ¥‚o& ëÙæ‚à:~Òþþ*·ÿÑ ^¹ûRü}oÙÿÀV:Îoks®x‡QƒHÓþo"É..2|ë™8Û H¬ïÈàc#­|áá-?öšø+ñã½á¯ƒ’øËFñθ—öwqëš]òbŒ >\ÓyŸ6ˆ)é]÷Åï|aøûð¿Ã-»ðM·…üyàG¬Úhõµõ½ôVÁ£h$¸·/‹ˆÝ‚äáX Ø­C¡Æ|1ý«üYiñ£Âÿ ¾!x«Â~8²ñÄw gá‰5…íºò.#w}ÑÈØß†-ÉÆ‹ŸüâÝtGñáß…ììY›KÒ5Y%7ׅ܆éüÅòZOáØÆ:s]ÇÃMkâ^»ã­*ÛVýŸ-<¥Û™Z÷U¸¾Óg’&X›Ë[T´ÜîL›An)'9ë󷇾üsøq⿉º§Â 3ǯu»íCNñõå‚ [Ì–ê9·]m·9cjrI ‘†£@>¡Ò?h[ÿ~ÈóþÑ:Ÿޤt I-&&H£¹·GÜ„¥“zCŠàþ øßöÀø­¤ø'âÌѼ%¬Gc-Ι"Ý6¡=¤Š‚âé$ÆŒçt°Gœ*–Îjü5ø3ñ'Ãß°T¿µ]Áã# jV+`n-Ü›‰Ì¾ZyË!ƒæÜ9ó03É×Ôü;¬øCà·€<'â;o±êÚ'‡ô«+È7¤žUŵ¤qJ›ãfFÚêFUŠž ‘Í'dÆ?kO‰:‡Å¸¾[økOÓþëÚ„ßÅpój+fÇe²ª¶Ä`‹—•,ê¨ ÕÖøÇöÉ“Eý™¼ñ‡MÓì¢ñÄw´²±‚úsŸmw:±šk‰rJÛÂ#v<䫜œ×kðᇎüoñ½&|XøÁðB/ˆ_'¼Õ/î~Á$P-¸’Æ ±«lN9•džHéŒýI« ŸŽ-ümsá‹È~ÝØØø¶}šmF)'µ_œoÞ‘29Ên 0pzWÍ_±ÿŠõ¯é¿ ñ&™¤XjúŒµ]:ö]Ù­`½¹·(%ºuvv/+ä’NqŒó_`ô¯‡ÿbi¢¼5GÚÝ|Hñ Å"ò®¥ÑSÜ`ƒøÓ[1uû_³x[Æÿ>'éªRÓ|gi£;ƒ†{mM´ñŸQ¸FøÿgŠû޾#øíe?ÅÚ?á/ÂKi$°ðuÉñ®±qƒåD-CÁaî…ÞbÌW¨ §9¯·({ AETŒ(¢Š(¢Š(£½QEQEQEQEQEQEQKI@RÐQEQEQEQEQEQEQKI@Q@Q@RÐQEQEQEQERÒRÐv¢Š(¢–’€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢Š(¢–’€ (¢€ (¢€ (¥ ¢Š(¢Š(¢Š(¢Š(¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Žh£€?ÿ×ýü¤¥¢€Š)h¤¢Š(¢–€ JZJ(¢–€Š( Š)h(¢Š(¥¢€Š( Š( ¯Í­Fø¹ñûâ×ÄÛ?‰ºŸ„mVã[š¬b줚Õ;lðÊÿ¿è½ëß÷Í×ÿ'Ñÿ ­ñ‹þ‹Þ½ù]ò}}»4ð[ ’âE‰I –!FOd÷=ªZÓý[Â+ÿÀ¥þg/üE|óþ~CÿÑÿågÿðÊß¿è½ëß÷Í×ÿ'Ñÿ ­ñ‹þ‹Þ½ù]ò}}ÇQM46ñ´ÓºÇ ³1 ÷'Š?Õ¼'ò¿ü üÄWÏ?çä?ðMþV|Cÿ ­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß•×ÿ'×Û–×6׭Ť©¾â¨n.m¬â3ÝÊF¸‚¨Ï“Gú·„þWÿOÿ’øŠùçüü‡þ £ÿÊψÿᕾ1Ñ{׿þO£þ[ãý½{òºÿäúûŽŽ´«xOåø¿Ì?â+çŸóòø&ÿ+>ÿ†VøÅÿEï^ÿ¾n¿ù>øeoŒ_ô^õïûæëÿ“ëî*Z?Õ¼'ò¿ü üÄWÏ?çä?ðMþV|9ÿ ­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß•×ÿ'×ÜTQþ­á?•ÿàRÿ0ÿˆ¯žÏÈàš?ü¬ù/ö1ñ׊¼yð’æóÅÚ„º¥Ö›ªOeó±yš†THç,ìFù˜“ŒÕõ¥|;ûÉÖ?ì=qÿ¤¶µ÷_Ô”°4¥'wcð´èñ6¨ÅMÙ%e÷sñ;áOþ0øu|)ñÎký,L³b»¹³ÞÊÚÚHÙЂrŒJžà×Uá¿ è>ÐlßÝÚÏfš­Í¢És,,¶ø» #Kî`Tg8Î:W ø¯â:xÿÄ?³ç‹ô;‰ µ×µÚxUÊ"öXdòc2{Š×ñuΩãïŒÿüs¤h:­¾™jÆàÝXÍÚþìÆ¿hV_Ýo+òo#p ޵äÚßÃx'ö𦑡éWW~¸×_·š$’ ®¢1ÝD5.¡”R¸çuyµŒ¥(|-Åé-?MÿîðU)U£Bž%¥VêÊî×mº±”_÷¯Ë%èûŸ¤´R×þÓÁƒÚ׋,Ï×nBiÚ5°û÷:¥áòí£QÔá‰vÇ!júD~$xß‚µ}CãŸíc¯ø¶ÒâQàŸƒÐÉ¡ÙvÞk·`ÙHWûøÂ*Õõ]+_еf±ðýýÕ“[Ú]Eq)Žæ(š7*€ð¤üÜu§þÒÞ&»øëð—Â:‡ü)â,ØxçE3Û_h·¶—K%šIÌÆ$òT0̘ØAøŸKñšm¿3Ý[XÈZT‰?ú½HÎ8Í&ñN¥ûL~ÑŸ µÿøW]Ò<3ðßûKQÔµkNŸK sy‚+H’áU¤eÁ2mÈsÅ$´‰ðw‹ÛÆ?.ø?Ä ñ½kâ~÷Ä^ºÑ´¹¯í ¦ÕÞÂ'Š_”ïÇËót®Åÿ¾ øCþ Åuà½îûÅÚ½ü:¥Þc Ý\F÷zŠLc vc[C7aš±uhÿbñ§Åû…ÿ ´fñŸáðGˆ5Ñ(ŽÂÂ||–±¶Öóîñ"¹àôP¯Ïx Ä?²Æ;? ø#JÔu¯ƒŸ¤Uò­ážþmZÍ)@î-®Bò7 ßx®ßÞ~ƒÖOȤRÒRQEùû |`ðß߆šŽ®ÙjWq«Mp’ÙÙ½Ä8k{u(]::íË)è žõõŒ?µ_À—A?ˆÚÖcÖ9¬®Ð¯~I‡hÈäsú×’~À_òGuû\é-­}½,0ÜFa¸eº«AÇ<ƒ_!ÃÔqR¤éÔ­³‹Š’?WñJ®q25©ÉËÝ©%}º8¿Ìò{_5 PxËLVæ¸XG~òí¿ÏØXø÷Àºžßìßé·{¶ãʼ†Lîû¸ÚǯoZŽ÷áç€5(ü­GÃ:eÔcøe²ÇäÈ}q÷ß³ÿÁmGwÚ<§&íÙòa}î¸ò¶ãÛÓ¶+Ùÿn_ÈþõþgÀÂ{þuÿ€¿þDõÈf†â1-¼‹"7FS¸pEI_´§áÿí cÿ? ¾Uû©w¢Û¦ôãlœÿëÜQý¡]|Xy|œ_þ܃û:ƒøq1ù©¯ýµ£èº+çA§þÕV?-¶­áML3uä$ƒÎqqИç­'ü$¿´å™ÿIðf‡¨lëömEâߟîù£ŒwÏ\v£ûY/Š”×ý»ÊáýŽßÁVþÞKÿJ±ôeó™ø­ñ¦Ï÷šÇÁû¨â= ¦­ixÿ÷Â(=Hïê{P>?ê–#ÿÃÚ°äµ½ŠÝÄ rK:8À§¨íGöÞí6½c%ù¤Ø8—ð¤ý%ùIŸFQ_9ÿÃP|:·ãX±Ö´–u¦N¤Ãä ÉjÒ±ý§¾jû?‹ ]¹ÿ] Ä=1ÓÍs×üàÓŽyƒnÞÚ?6—æ)dä¯ìeo$ßä{Õæv?>j(kã=#ç *½ì1¹' Úì­ßÓÛ­uö^(ðΦ雫ÙÝ»€TEq– +ç"»iã)Oàš4pÕÁV‡Ç½S7h¢Šè9‚Š( Š( Š)h(¢Š(¢Š(¥¢€Š( ŠZJ(¢Š(¥¤ Š( Š( Š( Š)h(¢Š(¥¤ Š( ŠZJ(¢Š(¢Š=袊(¢Š(¢Š(¥¤ ŠZ((¢Š(¢–€Š)h(¢Š(¥¤ ÞŠ( ŠZ((¢Š(¥¢€Š( ŠZ((¢Š(¥¤ Š( ŠZJ(¥¤ Š)h(¢Š(¢Š(¢Š(¢Š(¢Š(¥¢€–’–€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ 9¢Œ ÿÑýû¢ŠZJ(¢€ (¢€ (¥ ’ŠZJ(¥ ¢Š(¢Š(¢Š(¢Š(kž×<'áÏ¦ÜøHµÔåÑîVòÉîaIµÊ}Ù¢.ɳ.ìk ¢€ k*º”pX`ƒÈ ö§Q@>𷆼¤EáÿéVº.™vŽÖ΂21g+£sN$“_þÊßòXþ=ÿØyôªþ¾â¯ƒïgÿÚ'Â|a⟃~2ÒôÛÞµôëweÜÒK*ÆUí®W™XV‡$ð³uR5hW„”[ºV¾±k«GéS S˜eõ«Æ”ªÂ .wQ¼jFNí)[E¦‡ÞWÿð‚~ÝßôQ´ûñÿ+hÿ„öîÿ¢ ÿ߈ÿù[GöÔÿè§ÝþHŸø‡ôèi†ÿÀªò£î:JøwþOÛ»þŠ6ƒÿ~#ÿåmð‚~ÝßôQ´ûñÿ+hþÚŸýÔû£ÿÉüCú?ô4ÃàU?ùQ÷ðïü Ÿ·wýmþüGÿÊÚ?áý»¿è£h?÷â?þVÑýµ?ú©÷Gÿ’ø‡ôèi†ÿÀªò£î:JøwþOÛ»þŠ6ƒÿ~#ÿåmð‚~ÝßôQ´ûñÿ+hþÚŸýÔû£ÿÉüCú?ô4ÃàU?ùQ÷ðèðíÝÿEAÿ¿ÿò¶øA?nïú(Úýøÿ•´mOþê}Ñÿäƒþ!ýúa¿ð*Ÿü¨û‹­-|9ÿ'íÝÿEAÿ¿ÿò¶øA?nïú(Úýøÿ•´mOþê}Ñÿäƒþ!ýúa¿ð*Ÿü¨ûŠŠøwþOÛ»þŠ6ƒÿ~#ÿåmð‚~ÝßôQ´ûñÿ+hþÚŸýÔû£ÿÉüCú?ô4ÃàU?ùP~À_òGuŸû\é-­}Å^û6üÔ¾ |; kW°Þê—³^ÎÖûŒ(Ή¤lê¬ÃdJI*9$t׿Öùt°t©ÔVi‰¥ n{‹Åa¥Í MÙ÷[\(¢Šõ‰’–’€ (¥ ’Š(¢ŒRÐVmþ¤j¸þÔ±‚ón1çD²cé¸SùÖ•¥Õ™Q“Nèà/~|.Ô™ä¿ð~‘<Ž.öç9ù¶g<“Áë\}ïìÛð:ý'ð…¢‡$Ÿ(É çÐÆêG^ƒ§n‚½¾Šâ©–a§ñÒ‹õHí¥šâ¡ðU’ôoüÏœÏì±ðŠ4‹}CI^Â×Q¹Pqó»pO'Þ—þÚ;_›Gø‡âû…®ø‰èIFŒä㎾•ô]Ïý…ƒû4ÒôÓò±Óþ°c~ÕVýuüî|âß¾,ZúGÅíJ)2n¬mî—ƒÇÊÅ}ò;ñž˜§~ÒV?»´ø‹§jjŽïHH},G|~Þ¾Œ¢ìZ+ár^“ŸùØÛµŸÅ?XCÿ‘¹ó¦ïÚºÄm)á Mƒ} ‡<äçäÀéÏCëHø»KU$<²éLЌ㮝F9È^€ãßèê(ú–%m]üãÑ!ý þ,:ùJKólùÌ~Õ? ÿÕåöŸ»öÍ:éwzcdoןÈ×CeûF|¿;`ñ}’’HýéxG=dUÿGðEu–Z¶•©Úmä7@‚AŠE~Á?)9}˜?›_ûkL½ý¹¯ûv/ÿn‰ïô•óŸü3Ãè9Òµ-sLÇÜû6©2ìõÛ»w^sœõ4§à&¹iÎñOÅq7Qö«Ô»<Œt4}o¾*t—ê}Oþÿ|Züœ¢è¯ÂßÖ_ò ø½9QÑ.ô‹[Á~è.X0ÿhŽOZcxö¢³V6ž+ðþ¢ÀQsc,!½˜³‚{2}¨þÒ¨¾*ÿÉ_å þˤþDþ¿8ŸGÒWÎgSýªtÿøúѼ+ªç§Øî.àÛŸ_#‹{ì‘Åy÷~ö62çÃýîÔÚ_ÂÖß&³áiöŽïI‘þ Ì:`õî(þÜÂý©ÛÕ5ù¤Ø·ðÂþ?ɳèÚJùÚßö¬ø,Ÿg¸ñ ÙÌ6Ogt‡¦rO”@tÉʺë_5 <«iˆÃþ{\,#¿y6ŽÞµ¥<ç ?†´_ý¼¿ÌΦI†³£%ÿn¿ò=nŠäì|{à]OoöoˆôÛ½ÛqäÞC&w}Ümc×·­uÍ Äbh$YcnŒ¤qÇQ]ÔëB ¹çÔ£8|I¢ZJ(­ Š( ¤ü(¢€ (¥ ’Š(¢ŠZ)(¢€ (¥ ¢ŠZJ(¢€ (¢€ (¢€’Š(¢ŠZ)(¥ £é@¢€ (¢€ )i(i(¥ ¢ŠZJ(¢€ (¥ ¢Š(¢ŠZ)(¢€ (¥ ¢Š(¢Š(¢Š(¢Š(¢–’€ (¥ ’ŠZJ)h ¢Š(¢Š(i(¢€ (¥ Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Š(ü)) ÿÒýû¢Š(¢Š(¢–’€ ;ÑEQEQEQEQERÒPEPEå~)iŸþx“âN¦<Ïì{VkxGÞ¸»ˆí¡Qݤ••G×Ò€#Ôm»g1|ëž…Hà3Ué{}ŸEcøs_ÒÐÒi“—€€¹û4£åêJ왯®kãÛ#æ• øwö‰ðl /‰¾^ÿi‹ïÝéâ=FØã®è¾u÷R8 kÕ~*|xðÇ€~_üpÓ§MBÆm6+­)PÿÇì÷ª¢Ê4ïûÇtƒµrÄ`«\W<ïBø…㊵n±áŸ ê2Zøá}€·Õ„@l¿Öï¹XYº•¶r@èûƒuZúö¾vý–~Þü&ø=¦i~ &oë’K¬ë³·ß›T¿"I‹gûƒlCÑP ú&”/ñÖóÆZ‚¿á2ðM˥׆çŽúæÙ~åÝœG3ÄãÀO›Žp9"½CÃúîâ}ÃÄ:Lžmž¥sÄÞ©"ägÇzÕ–(ç‰á™C¤€«)5óGÀë™| âoü Ô‰ ¢Ju ›?¼Ó.›p@O_%É^½ñü&©klÏHÔ>-øZ1áÙ´)ãÖí¼Cª%fµ•!”£¹/ôÙÓ¯ Ž:öÚ‰¼=âe»“ÃÚŒŠXÌmæhH©*¨b¤Ž2óšøâßÁ¯xŸÅ:¿­[Xx^ãÄzÒØý›LW›ä)+ ›¹ƲHvãåT;Xå²p>ËøW¤kÞðÇö½¢éº+iò´P®”OÙ§ˆDÁ …‰;ƒÙ“È&§•У'sÒk㌚çÆÏ øH×_[·Ó<;wâ M2ÖÆÒ<Í4‡v–âFÎ òñµxÁìG?cׯµ?Ä/D<1á©5«eÕ4i÷w–ÛÇ™ ¤ÈëÙ@u?ˆ¥Kp©±ö}ÁÏñ÷Ôþ"xjæ-oMÓí.î‘ “ä”Ú+—@ø8å çµsŸþ,Ãñ—Âx² 4ékÔ–ÞQ˜OŸ-TîÜ:î鯧•Úå\õúùïö“ñ%ç…<£ë—òiÉ»¦‹‰cb‡ìþa2†Ç%JŽGzú¾mý¨#†oøz … ¾$ÒQ•º0iH ýE:ö:ÿ |nð·‹ Ôè 0:ñU>øwZ±ý›m4kËW‚òòÊýâ†RéåxS¾VQTÿfø6_†¾ð'öŲxŽÒ;ˆåÓ^@—jRYþå°ØwgÇãCгÝÑõ-|éûIø²ëÁžðÆ· ôº}´^!°û[ÄX¶ÚU!ye*9ëèºùgö±Ô-4ŸøSTÔÉkgâ]6iT.òÑÆ]˜þ"@8úTÒ^ò*{‘àïñˆÏ„ÚËPÑ5g„ÜÁo©Û5«ÜÀ:¼[‰ÝŽã†¨àÓ¢«‘u'™¹øÓáKOOãÍvÚÿFµ·Ÿì­kylñÞ‚@XÒK–ÈÛ· õÎ;Á<9ã~O 5Ž¡¡k+Úc´Ôí´“@ Ç’CÜg#ÓƒŽö˜ÍŽ“àß]ÂÓé>ñæ 7ˆíÂIœÀdŒÃ Ï5Íßx£ÃŸ¿h¿]øòjZj…å£ b†+ˆZ(Ѥ]Ìì>Pr3œP š¸9;Øô­㶇 ê:£x{]½µÒD»½¶Óä{XÌ_|‡$UÁÉ@@®—]ø¹à}Á'½k+R1¥ŸÙѤ–æY³²(£1sƒòöÁÏJùëâ*ëòxÚçâõêzMõÕ¥—‡l;ye‰B&Ã$Í+¤ç©ÂóXš<ƒJø9ðKÆúŽN…áý]žýÕw¤I+º¤¬DR0Om«Ù!sžÛ£|T—Æ´‡´‹õ-"t{æ¼Óo¢kvó7!ŠFBJ·ÚÀžâ½7Å?|1áßÏá KÄZÅš,—6ú]«\u`ó[*ªH99öÍxýŸŽˆ×><øoKñ£á'GÕüEu£[æÓ,Úâ;ga®Ù\¶áA<JÚñ¿Ä¤Ð<Š4Í3Q¼þÓ³yíͽ£Hðn‡ÌF022pAWÍŸu_ø+\ñ‡Œ>üMOxŒJòj5ÀŽhï/ äJ€È~Pñ’9È8éô¦‹«kÞ2ø3¯¬Y][VÑäy U#÷’BØÂžFP╘)7tr_¾-j><ðÖ…o®iz‘Ôî,„ׄ–~M”®½JH¸S»?.Í}_5þÍ~>ðn¡ðßÂþ ¶Õíÿá!Ó¬DWsHî6€í“t'=ñŒkéJŠŠÌ¨=Šh?Š>ðŧ†ne}&×OmGP²LšÞ [Ï' ž#ñÐ ö¯JñŽõI~4ü0ÓtEŽâ;FæXÓ;…HVHXý3‘ƒ\ßmmuÚ¯ÂZuâ `¹ðõür!èÈâU`}ˆ5áþƒYð·í#àÏ…:ÖéÁçWM>v<˧^@e·õØ/\¹ü5²Škägws±ÐukÅÞ ñ“kÿåð‡ö^¹uemfÒÚÇ›x²º‰Š¶2Å}>^¹Íz%‡‰µÏ üNðƒ'Šåñ6ƒ¬iZŸ™vÍ¥Ìöå*J"ì=­y/Ãí[àˆ>!Eñe´_íCâ[Óö”q¼¿gÛK©!wïã×5¡ûEj^¶ð/|oð¢KS¦Ú^^i–FÈ*@¡‘@26@®ì+ésÒ|WñšÛÆöž¹ø]ªÊ-µ¯Á§Üº¡‰Ì02áq |ç‚ yö¬ë^-ñ??·¾/Ká¥k—VV¶m-¬`ÛÆ•”LU±–+Çzæ°|à«~ÒZ/È"ò´2#â tÏñË`¶s6=åVÇÒ¬xVøcâˆÑüYmûHø–ðÁý¥rKö}©¥Ô»÷qëšVK`»êz7Å[Äþð€´¯ øÞâþ]g[ŠÆ]f‰äž+†—S(vd(Ç÷F{ÕËSâÂÿŒ^ð¹â§ñvâø®ö‹¨R;«Y-S~òÉÕ[ è#<·Ç#à_ø#ᵟ® OÝø–ÚÚÓBˆ¬dWòJƒg‘Ðó^÷à¯þð>ºþ)±[½KZt1 ÍFæK©ÑUVsÆ}zöÏZ–ÒZ•g}Ó¯~*|WÑü[ñ;BñÎk¤Ý^C£Ø[G‚Xìsó\o±”ƒÇoqÅnë{dޤ ǽðG‰<ð?á?‹nôùf›Áššê×öÑ©3%­ÕÁ¸9R2ª°ÆT“ž5N*ú“}§|)ðÃÆš³m¨k^<¾ñ%“Fëye} M ŽFUâÚŒ+ àîÏ­x?Âý{ᦹðÏZoˆ“h3ë^êi z‚Y‰U”98xçô·‡~4|.ñv«c¡ø_ÄVº¥þ¡Ë6ìduD]Ìd~ï³í9㯚~|?ðˆ¾ø‹\×sõKáŠíXrZÞÅnâP9%`ƒÓ×Òšj/‡vÌ««éúÞ–IÁûV™2í8$´7QÈÆkèê(ú¦-|5¾ø¯Ñ úæ üTÊM~jGÙþÔïŽØÝGzï/ômUÇö¥Œ›q:%“ÏMÀúšå/¾ü1Ô·}¿Â:EÁmß3ØÀ[-Ô†ÙO¨9£“¾Ôɯý¹‹Ÿ/fkþÞ‹ÿÛbt^(ðΦ雫Ùݼ€\FåÀcœä~u»Þ¼B÷ömø¨+¤þ´Pä“å!<ç¡Ô޽ºVý•þÁÆ‘o¨i# ºÊ€;Û‚y>ô{lrÞœ_ý¼×þÛú‡±À=ªÍÛ‰ÿíÿ¡ôeó’~ÎpXÆ©¡üBñvœ±¦©¾Û&6Ž:ñéIÁÏŠv*£Iø»ª#¨ÝYÛÝŒ¯ÝÈr3ï“ów£ë¸•ñP)GõhP¿‡¾q’ü”£¨¯œÏƒ?i+ÝÚ|EÓµ5?Çw¤$?XŽøúïK¿ö®±ùJxCSAÀ ßC!'œ¶~L˜ô>´jI|Tf¾IþM‹û&/à¯ókÿJŠ>‹¢¾q_þÒv.ZïáÆŸ©¦ i«Çs‘ŒÌô=:{ô?árüP³s³ð‹Vˆ ¾Éuàã;¶”Úà¾ÜQýµE|JKÖÿ+û »øeé8~\×>Ž¢¾tÿ†Š·³×¾ø·KUûòÉ¥…{ýôsž9ê[~Áâí"à¶ß•/ -ótoÈ'ÐŒ×M<Ï ?‚¤_£G5\¯Ž”—ªäw´U -[JÔ€m:ò A Å"ÉÀ8'å'¡«õÙ&®ŽE§fQE1Q@Q@Q@RÐQERÒQ@Q@Q@Q@´”QEQEQEQEQEQEQEQKI@u¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ Z( Š( ¥¢Šøö ý½4Ÿ€?4߃~ ð­ñ?ÇÚ…¨¼:^—¹|¨\7– $SÈÎÅsµ"m©óÐÿ‡ŽþÔßôeþ3ÿ¾õþSR|ÿ”É|vÿ±2ÏÿDh5úÿ@ðñßÚ›þŒ¿Æ÷Þ£ÿÊj?á㿵7ýŒÿï½Gÿ”ÕúÿE~@ÃÇjoú2ÿÿßzÿ)©á㿵7ýŒÿï½Gÿ”ÕúýE~@ÃÇjoú2ÿÿßzÿ)¨ÿ‡ŽþÔßôeþ3ÿ¾õþSWëýùÿý©¿èËügÿ}ê?ü¦¥ÿ‡ŽþÔßôeþ3ÿ¾õþSWëõùÿý©¿èËügÿ}ê?ü¦£þ;ûSÑ—øÏþûÔùM_¯ôPäü¤ÝJÓÜÙA4Õž5f8õ$ZT”Z;;8mÍœ0FATÃu@Ç=ém­-,ãòlàH#Îv¢…õÀ«´•Öö÷*â5•Uƒà0 :žãµKEDööòJ—´±gc•—w\£=ñT¥Ñ´‰ïWRžÆ»NÍ™ŒFZÒ¢€)>›§Iiö -bkntQL|ò~\c¿¥3OÒt­&6‡J³†Íä¬1¬`Ÿp V\¡šÞ €¢â%”#]À6t#=ìjZ(¶·y’åâFš,„r ²çƒƒÔf©ÞhÚF£4w…ŒRÅ÷XÕÙ~…"´©h©¦§¥Ñ½KX–àä™(sžÍŒÕÊ(¨g¶·¹U[ˆ–PŒPØaÐŒ÷©¨  ZV—¦ù‡N´†×Î;ŸÊSqõ;@É«BÞßÏ7B%óŠí/¸¨9Æzã=ªjJd‘Ç4mÈ`« ‚bZ§a¥izR4z]œ6ˆç,!cú f¯Š(9ôm"[á©Éc^/c™=ýjqcd-Mˆ·ìÄbØ6`öÛÓ…[¢€3lô'O¶PÛ 審´7P03ÞCHÒueUÕ,¡¼ ʉ£Y1ôÜ+F–‹NM>Â{3§ÍmÚ‘·ÊdSm¤c…Gk¤évB%³³†ÜBËWh<¸gâ¯ÑEÀǸðöwt·÷Zm´×*r%xQœ]Ägõ­¥´J-7O†á®áµŠ9Û9‘QCœõËžjå-%@ÖÖÍ:Ý4Hg@Ud*7z€z⑬íåoZÚáÕ¨Þ ldMX¥  ¹4MiItûww9,bBI=É"¦þÍÓŒ jmbòcmÊ›j·¨À<õ«Ô”\ µ±¸†$3…Ø$Ú7íÎqž¸öªrhš,®ÒËanîç%š$$“Ü’+NЦ4í9b޵ˆG oE»U½TcûйE-fÜèúEåÌw—–POq)$‘«:ý‚Ehax ô4êJ˳Ðô]:w¹Óì-í¦“ïCΕ©kšfß¹ömReÙž»wëÎ~¦¾Œ¢¸å‘àÞ¾Æ?$—ävÇ?Ç%om/›oó>t?5ËNtŠ~+‰ºµ^¥ØÉàðȹè;i‰ð¯ãnž¸Ò>/\—šMµÎ쪗fÜ= ‡@+èê)ba÷פ¤¿&?íìOÚå~°ƒüâ|àžý¨lø»AÔpHæÂH² á“ß¶:}içSýªtÿøúÑ|+ªîéö;‹¸1Ÿ_VnHä{T°þÕ_ZA Ö¿%”­Ñn,®ã$zçÊ#¹=k芊ha¸ŒÃ:,±¶2¬ŽyÞ«ãVÕ¢ýaþRAõœ ø¨ÉzOüâÏ(±ø÷ðcPÅŒ´ÄaÞk…„wþ)6ŽÞ¿ÌWWeñÀ”~vâm2éñE{ƒÓª¹ô4^ü<𥕨øgLºŒ ¶P8õèÈ}r7¾ Þ¿™7ƒtÕ9'÷p‡>ÑíONÔ·/äzÿ1ÿÂ{þuÿ€¿þDõ¨f†â5š HÛ8e ƒØàŠ’¾x›öUøò­t ,¥leíïnãÈôÇšF:Zˆ~Í^¶çGñgŠ4“Ô]YÔƒÜüÊÜ‘Áö£ëÕ½¿IÿœP¾­ i/X”™ôeóŸü(ÿÙüº7ÅÅr‚í¢»`GMÌÊ»‡Lƒ×ŸZCð÷ö†²`ö¡¾EîôKdchg·ô'\fí ëâÃËäâÿöäÙÔÉÍMí¯ó>¢¾sgíS§ÿÇ®³á]Ww_¶[ÝÁ§‘×ïê}¨ÿ„öž±æë¦8µÔ$„xÇïäu'¦8Ñý¬—ÅJkþÝ¿åpþÇoà«ÿo[ÿJ±ôm%|éÿ Sã^ž3­ü"¹Ø8ßeªÚÝ#‚D`œc<ã>”Æý 5m=ë¿ üY¨$ýšÅ.ãrIu‘@㯥Ûxuñ6½c%ù þÁÄ¿…'é(¿ÊLú:ŠùÏþáôjšn¹¦cïý§L™vg¦í»ºñ¨­ ?Ú‡à=ùB¹$~ö ˜z ÿËH—óü)Ç<Á½=´~m/ÌRÈ1É_ØËä›ü}¤¯1±øÕð‡QÚ-|g¤å¶à=äQ1ÝÐvSŸn£½v6>(ðΦ雫Ùݼ€\Få逬sœŠí§Œ£?‚iú4pÕÁV‡Ç½S7h¢–º`¤¢–€Š( Š( Š)h¤¢Š(¢–€ J)h(¢–€ J( úQE-%RÐQô¢ŠZJ( –’–€ (¢€–Š(¢Š(òàßü¦Gã·ý‰–ú#A¯×úüø7ÿ)‘øíÿbeŸþˆÐkõú€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Šá'Ô­´}*Å<Ë‹»É’ÞÞn’Y ¢r@®gÀ?¾üT°¹Õ>ø«LñU”¾Lòé—q]¤Rã!¢f HäÔr8 ò–Š(¢Šä øà{¯\ü6¶×¬¥ñ] ¿›JYÐÞGhYPLðƒ¼FYÔn#aê(®¥¢Š(¬{Ä…t{¯øŸR¶Ñô«ó..ï&K{xPqºId*Š=ɼ®ÚGö~¹ð•çm~#ø~i×)is¨Ç©[½¬7ýȤ•\ª³uPO#‘Å{U- €AÈ=éh¢Š()h¢€ +ñ?Äø&óEÓ¼_¯Yh×^#»M?MŠît…ï.ä!RÈ2HÄ€rrEuô”´Q@Q@ _?ðZ¯ù5Ÿ Øçcÿ¦íF¿_ëòþ Uÿ&³áoûìôݨÐëýQ@…%/á@ÿÔýû¢Š(¢Š(¢Š(¢Š(¢–’€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢Š(¢–’€ ù˜üañ/ˆ¿iáðcÁPÛÉ¡x[J:‡‰¯$FwŽâìei¨26s•<`©¯_ø›ñBøWðÿ_ø‹âY<½;@´–êN›œ ÂD™Æ^G*ˆ;³^ûøXðÇà ¼{ãHÿâ³ø•w'ˆµ‡<²5ßÍonPCµ@þñc€XÕ-®&mü"ø¹â]{â¯Äoƒã··×|)uæšöèÑ­î‡z3m6ÖfËÆÙŽR8݃€WÒ•ðßíc§Þ|0ñ_‚k£gÁWMñGœÏáýAÂJÌÞû4„H03¼ýÁ_nÚ][_ZÃ{g š „Y#u9 Ž2¤Bh}ÁÑQ\ Ú E«*ÎU¼²à• Ž A#=@"¾>·ø/ñKáÊiÿ¼¯Mâ_‡v×,®îYlõHåb̰ï qž>ëzyn•u%:ÊÙ'Õë»Ú+K]õk¥ÚÒOvzoíñ¶€¿ fñŒZskZ½ÝÌv—§«7w×M¶$ÈàrÇ'F ¹O‡¶Ÿ¶ ·‰tCân£áKíô7ö…ŽŸosÕ‰hÉ_*w‘Ò}¯…lªq’3_3~ÔñüOÓ~ø3â÷Å8À·ð·ôvëN¶Ù2éºLSª¬nèÍ!€Ësó8ú~[xÇÂWi7VšÕ”Ñkà9’â2/C'î>oÞü€·Ëž=rb°þÊnŸ2•º­WÉõþ­tD•8øûF|øY¬§‡|ã=?GÔÙCo,…¥Ž6èòª1¡þóíõ×kÿ~ø[ÃzwŒ|Aâ++- W–ÞKé&_³Î÷_êBH S¿±Î=ëóàG€þ+øËÇßãÒ|c¢hÚëø·R‹V²ÕtEÔ¯Õ˜‹Vó$š6û+C ãnÐ0qY¿¾h ÿb¯xÄ(µñ‡†tÿYù÷¶ŠºØ½äísn$—ˆI–=¡²¸Û€F+TMÙúOðóã÷Á‹¥Þ‰ðçÅöíýŠy²Áo'ï<¢qæ ` ÇŸãL¯#žji õõð·|i§èú±E‘­¥™"Gû¯0@ÞR·f“h>µó/Ƙ¼3oûY~Î_ð€Gk¯ ÕŒßa3¢›dÚ\EÖ#‡ò‰ùx}½ë'öj·ðDú×í-'Äh­ZÅš»j¦ü)eÒŸ²2ò-üvòìǵ.^¡sôüGáøôøJ_R¶]@.~ÚfAmäÝæù¹Ù³o;³ŒW’øö˜ø ñ+ĉáøÛOÕõ‰ÕÞxƒN±)g0UYB¨,|²Øž‚¿1®¿´?áÚ¾ þØ_ðŠÂI¿Ç›æÂ5ý³6ÍÀ|Þ_“åíÿgf9Å~·éö¿ !¸Ò£Ò#óAþÂx¸Þ¦Ý‰û_¼>ϼŸ/?»Éû¹¡« 3äo‡¿µÿ…4/|QÓ¾:øÇOÒ#Ð<[¥i1È¡$6ìK,A•Iù¤aÜŠî?ioÞ5ðn‹ðÊ_ƒzUÔÿ|Cg¤Ayxsiä^A,‰2ù.¥†UNA9ä_³?‚ü-¬x“ö›Ô5m.Þò{ÿêös4Ѭ…­Ô9üÀárä‘Þ¾^–ËTñ?ì•û é6šµÆ}yâ½:Þøií[Ê»D’0ÜAŒgŠ®Uro¡÷‚þ7üfð—ÇýàÇ{-öoX\Þèú®‡çG=¢¼’Å<3’Wäˆ*O8çåöïþÑß~ëká¿øÏOÑõFE•­¥´±ÆßuåT bCÙŸh÷¯‹<áMCà7í‹¡Áñ·[ŸÇ÷8Óf´ð·‰µdžÂâLú’À¾j¸Û"(bÌpìû=x⿌¼[ñ±tŸhz'ˆÅšŒ:Í–«¢&¥xöç2I4möfRËí+…È8"“Š Ÿ¬+âO>€ÍäÝæù¹Ù³o;³ŒW’ø#öšø ñ#ÄÑx;Àþ7Óõ}fàHÐ[ÄìàD¥ÜÀYUfÚ ±òË`O×Â_>„¿°u—€WÄÉâï éšýšê—ºpaÑŸTßsˆ¤ì‡&27Ž£úA¥Úü,Š/ \hÑèé ÿ`<@ »“ö"¿{6ûÉòóû½Çîæ¥¢®jx;Ǿø…¤K¯x+W·Ö4ø'–ÖI­ßr¤ðIt!”õWοiÝ;Aøg ø—ଖ^.Ö\"7,G¦¯Ž¾-xïUý–|_ñÏᯇ·¤¿míµÅÞþÕÖì«ýbH|ཬc¿>ƒñÇÀoÀOþÌJËðûÄÚu®¥>qMsnñµÄŒqµ<â~cÓw5J(MžÍàŸ‹ß|ñÛDøñõô}TøÏO¹¼Ñu] ­Sí`É=¤ÑJòtŒ3+†Àe°>¨Ð|yàïlx{W·¿ÿ„nîk GÊlý–êÜfX¤ôd‘_|h½¶ñWí½ðÃZ Ës{áûmgV¿òH³ÚKlR6—nv¬Ž.z–µ“û=ø«ÃÞ‹ö¥¸ñüV+¤xÇ\¿ºó\)ŠÚh¿w!œ>Æ ýâ02i4>Ô‹âÏÃI~/Åuñ%Šø=áûBê¯2¥©‹vÝÞc`cpÀîO²>|vøCñz{ËO†þ*³×.l<ðD̳ƌpÅ WØOFÆÓØ×åœ f¿±o츞( <&ÇEð߆¾êVöði¶6¿t;«¹(×r\Fd€%”?C¹õõïü'§x®ÃÀ×Ú¬ëú¥´×v¶LØšk{r²*÷T,2}ëÅdý±?f®n¬ßâ>“çYÇ$’¯šISùnƒ óHË\¿ íÁ¼‡ÇšÎ•{û~|;Ò-.£šóNð~¸÷#h„ïx7$g¨æ¾ZøCá½m3ö\ºšÂ %ŸÆ^2i£ViýE†òF[é´c ¡DMŸjþдedžþé¿~jv¶ºÞ§¦ØÙÝL­5£%íÀ˜„dl©È à‚#5ÕøKý­`ñV?ÄM{ÂW~VsyeyÛ©¶yo$Œ€‰6“‘÷Akó‡ÅÖ·°ýþƒ"í²§³Xã&=qÐ*Á\)ÀÆ1Ú¿J>þ˾øžx2×R‹TŠ!VºÖ5 ÈöJlÅq<‘“ÇnGjm$3oÆß´¿À_‡#>ñ¿Ž4Ý'W@†KyeËB$û¦b¡–{ רêþ,ðփዯêú”Ú ±½–õœÙWy—xÈ)·œŽÕùáû4AðÞo~ÐS|TŽÑõ8üS¯Цd²(÷üâ#o/ ÞoÍšÈøvÚÐÿ‚Wê'[Þ%_ ë>O™ßg'ïs·f6öÛŒqŠ9BçÚ6´ßÀGÆÓüu¦\ëú™-íc›{<’ tr‚‹#)cfr0+ŸÛö_Iî­Ä$ËgË"‰Xœ#me\/Ï =c\¿û8«³ÿÃ_xcàG,4½Ñ>ϤX]ù†25Û²½Ã9Œ­!,[9ɯÏoƒÞÐ&´ý•î¦Óà’Y5ï³³F¤¹&fË9åAô¤’Ùú¡ů†wSâÂx–Àx>HMÀÕ^uK_,1BK¶!R!¸Æx¬Ÿ†ÿ¾ü]¸»³øqâ›=ræÅCÏLÉ2#1HöÑÀÚ{üsÕ­5{‚´h¯í´ Ãñ/W‚æâöq§ÛÊ—r/´ÂÌ‘¼#÷¹V`+ï¿üñõ‡Çß|OñïÄïRÒm¯í`°°Ò£Ó'Ôm'€†Œí¸c"Dû%+mÁéšR ž­ñcö›øCà4ñ„ƺm—Œìtû™ ´’@ÍÈžࢻmG`[ ´>üLÕ¼Yû7ø_â·nR[ë­ êÒ¢,JJg!G…é_|*·ð þÊ?´þ=ŽÌë±ë>+Û]óÖ켟bÜ_æ_+ÉìN6óš÷z-ï‰?àŸº7‡ôÜý¯Rð…Õ¼8ëæKª¸üM gwû ë¾8ñ÷ÃŒ:Õ&¼›Ç×÷:žŸdî|7KÞc´·‰: ¢ùŒz’ü’FkSâׯm Jñ‡‡>øoâáÏ6±aöë@&º±™Xµ´*Û4»£gŒ‡ö/Ö,õ¿Ù_á•ŘUº-½Œª¿Á=ˆ6Ó!™dz× ûgÚZ¬nÖ ñCS Q¼.çÝqGPè}WãÍSñ?‚µÏhšœú.£¨YÏ µõ«˜ç¶ÐˆåFõFÁô=㿲Å]KâÿÁMÄ>"eoi²\iÀ^ŸÚ|†[é U{8¯¢ä’8cie`ˆ€³ÀrI¯…ÿàŸö·ü/ñ§‹¼¡‡‹¼k®j–~ë[nŠÔ2ú‚öï‚8¤¶Sîº(¢¤aE-%QEQK@ EPE-%¢Š(¢–’€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢ŠZJZJ(¢Š(¢–’€ (¢€ (¢€ ϽҴ­HÔláºD±«ð@ùèy­ )J)«1ÆM;£‚¾øWðÇRÝöÿé·d½Œ²ÝHm™úƒšãofß×êé?„-9$ùFHO>†7R:ô+Ü)+ަY†ŸÇN/Õ#º–kЇÁVKÑ¿ó>q²¯ÁûbÇEµ¿ÒŸ²j7+Û|ÎÜȧÿÃ:ÛÙñ ü@ñn–«÷bT/ öûއ"éÚšÿ~ïHHþù…ˆïÀzšú2Š?±h¯…ÉzN_çanVbýaÏ–ÿ‰ó“7íadTð~¥8$}º)NAäçäôsã—Ùƒùµÿ¶°äËßÚšÿ·bÿöèžùE|çÿ Áðú+R×4Ì}ϳjs.Ï]»‹uç?SJ~k¼Ñ¾)x¶)O{»õ»Oûá£QÓ=úàö£ëxµñQû¤¿Tƒêx7ð×·¬Zü›>‹¢¾t þ5iühŸnvv^éV·Eˆä!!€'9#œcÒ“þÏÚzÄâ×Åú¦º°’Aç?¹=GAÛžhþÒª¾,<¿òWùKôìºOáÄAÿàkó¿èÊ+ç3©~Õ6ñõ£xWUÏO±Ü]Á·ëçõÆ;zCKÿ öƒ±/¾E}³––Ó[¶U uÛ‹¼ž2¸hþØ‚ø¡5ÿnIþI‡ö-GðÔƒÿ·â¿6¢è¯œÇÇYÚÏÂÅðæÑb»`G]ª¬»‡\×Zí-á»n5 ø£I#‚.´—RP>VnHä{Qý¹…ûS·ªkóH?°1oá…ý“gÑ”WÏþÕ?^A Ö¿%”­œ-Å•Üdß>V1Ôrz×YcñïàΣ!ŠiˆÃ¼× õë&ÑÛÖ´§œa'¤kEÿÛËüÌêd˜Øk*2_öëÿ#Öè®>ËâÃýJ?7Nñ6™tƒø¢½‚AéÕ\ŽÆºÈf†â14²ÆÝH`qÇq]ÔëB Là©Fpøâש%RÖ†BQEQERÑEQE%-V¶¼³¼ó~Ç5Ó,…õ•í½ÈP‚êO—î€[hfe.ÊcS’À°ß ~)ü<øÃá+ü2×­¼C¡\³¢Ý[±Ú?¾Ž¬ÆëÆUÀ`8ÁùsÆÿðQÿØ×À"¸ð¶µñ+«û9LWÙöw—ðÂËÃnžÚ"m§ƒ±˜ƒÆ3_!þÐ~6ýŸ~~À~ ~ÆPÙéŸõ8­ï&°šäˆî¯åD¼SorÄÚ;[îO-R0ªÈU@Úkôöpøà€ßßp±§›}tѯ<îdglõ$…\(€;…>|rðÐñwÂoZx“Kå¼–ìÁâ“û“E YblrERF ×—ø¿öÍý™üž->,ñžŸ'ïaÓuX¤‚çÍŽòty#†$n¸fTcûàK+âëo hÿ³ßü3ÚÃktѼ?ñ—Ã7sêšu²,v¿n²[™¼å‰@T'ìàä¼ò|×;û/ü$ð?ࣴ‡ÄXE¨Üø*úÉtèçQ$p\êJû®X%T·*­ÕC¶9Ár|ý·?f?޳ê–ükÞh¶Ò^ÝÁÚ|‘ZÃÌ“ÿ¥$jÑ åÙIØ9lW¦ÁJ¿bÍWÅ‘ø>Ûâ41ÜÏ0·Žæk;ÈlZLíÇÚ¤…aUÏü´fãØÅ|‘ÿø)á/~Õ³s›±KãÍRëD×e¶& otø¤´ÌR:`ÑM4dœü¬AŠú÷öðøWà]Wö+ñÿ‡—D³¶²ð¶Ž÷Ú\pÀ‘¥”ºxFmÂå|ªPíÆP•<(î’4ŒÌî5‹€\çÒ¾ñ'ü·ö-ð¿‰fð¾¡ñ;‰í¤1M=íå¤n1öˆ xÜgø£,=ëæÚ#âÇŠô?ø$Ÿ†=øXøÏáˆíüE¤¤†^ ñÉ  gdÐÊ©,MŽ@uA¯ÏO‡_´wì‡ð#Ä.ð_ì¿à/|NÕ/õ¯5›¯ i“ë{®¤;Yd¾¹•Yã¥YãË3)%˜×Œ~ʯ7Ž?j¯Ú‡Àþ ðÆ©ð¶Ëdžâ=U·76ÓÀ‘yíeÕ7És$ÊW?+ŒÔö÷ˆ?ॱŸ‡‹ñü·ö-ð¿‰&ðÆ¡ñ;‰í¤1M=íå¤n8ÇÚ ãqŸâŒ°÷¯­¼?ñ/á÷м Äßx†ÆûÂ’[=çöœs¯ÙV”´ŽòÀ;Ã`¡0üÇø5ûNüøKð{GøM |øŒtˆ,"·¼OøC÷¥ü†0³Íq™q+LÙ,[9Î:`WϾüBøð¯ö°øðÓÂÚÿƒ<%â™ Õ|e¯ØË¥ˆÞwv¸´S((¢EŠ(p€ #$Ðè?ðS?ز}xhcâÅi¼}:ý,ZE8*.˜ÿkîcØÆ~ê±¾²Õ,­õ=2â;»K¸Òhf…đˀ2:2’XAr+ñÀ_µŸìÿ üÑ?dïÛ'ឯðÎm. î-GL™t›£ÌðÏnÈÒ0ó|ÅO‘Žñ)#}~¶ü1Ð< ᇞðÿÃ,øJÆÆÒ¼›—»‹ì{A‡ËžG‘¤M„m%ÏÁÆ(¹$(,NîkáïÿÁGÿc_xŠãÂÚ×Ä8®¯ìå1\gÙÞ_à / º{hd‰¶žÆbÍdÁLþ#k¿ c¯_øjåìõ hÚé <y!½™RãøwÁæ&zÜsŠ÷¯ÙÃàO€~| ðÿ€¼!¥[[ˆì`û}ÂÆžmõÓF¾t󸑳Ôp  é>|wøCñwÁW?þx¢Ó_Ðl–F¹žÜ¶û-K0šUš6Ú t G `Šü@ñŸíGû|aý¾į‹Þ ¶ñ¯øHZéoÒ¯®¬ßViÔ²=“Ú³“²Y[sŰ”^r¾©ƒÂú/ìûÿKð[&áïŒþ¼ŸUÓmÑRÔÞÙ-ÌÞrÄ *öprÞy?¾k£ðf—¥Ÿø+?- œ&øk üµÚí¶ŒgÞ€?Hü „"ðfˆ~Ú[XøjK8%Óa³€ZÛ¥¬¨!PF»X»F=¬/Š¿¾|ð„þ;ø«¯[ø{CÖ3<û˜¼¬ XâŽ5i$…$"+1œ`ôeUU  (^ù¿ÿø]ñ#Å–_ >(ü?ð³xú/†"M_Qðâ|ÒjÙŒ‘mo1—Ë+´+9;(×>þß²¯Æ?ÛxÁ¾4Uׯœ%¥¥ý¥Õƒ]Q \Dˆå¿…wn=‡"½oãWíð_öxÑ­õߌ>(¶ðüŒËm‰&¹¸+ÞU¼*ò¸\Ä.ÕÈÜFkâ3ö˜ý‰¿kÏøGÀß4ÿ |@ðö£ Þ§xŠ&ö AȰÜÛHªràmŠIÌ`¿»,1ß|dð·üsâ·~*xÄ¿ÿᷲѼ;k¢iWK!žwŒ²ˆÜHÑ792HF€?M¾ þÛß³Çí|xKá¯"º×™ÓO»·¸±¸‘S“å-Ìq‰H1XË2Hù¿þ #ÿ%öXÿ²™¤èøkäÿÛoãM‡Ç}Ã>0ø?ðkâñWÁZ½¦¡¥j÷^–±BÅž%¤vMÛ]T©—°-Ÿ©?à fÿÆ¿²uù‰à7?ôY r®›å¶°<‚3‚zðOø)¯í!ð/ÅÞ'ø}û?jÞ)cc¡øÎÂ_Ù¥µêýŸMŒ/˜YÖ³¬Ûagl¸ ý:ý™ußٯŞ›Äÿ²æŸ¥YøZæíà™ô­,é)-Õº*±’'‚vU*7²œŽ„×È?ðP»ø[?²«ýž=ÓüJÒ–C°eÁ¸·á¸ä}kô7Çþ6ð·Âk>=ñRA¢è05ÍÂÙÀe“` ‘ ˑҀ;Úk2¢—rTd“ÀWæïü=WöUÿ©‹ÿ·«ÿø¯¨øgöñ7‰üq-¤ž,†ÂÆ)v”‘mu7O8yRöåÐ÷½hªñ/üö/ð§Š%ð–§ñní¥hg–ÒÎöîÖ'S·›ˆ x˜gÈÌ=HóÿÁ¯xWâü#ÅÞ3ðF«o­èš¯Ã%µ»µI©öÛpø «ʰ €A÷Ào¾ø/ðwBøcá­"Ö;(,aŽõ¼¤&úá£t÷¼i$ç# (~jþÌßü)ð[þ ­ñkÂ^´OÐî¼úµ¤@,vÿl»Ó$’4Q ”¾ÅRàPéŸÆ¯Ú+à¿ìñ¢Ûë¿|Qmáø/–Ú7MspS¼«xUåp¹˜&#qçÿmïÙãö¾<%ðׯ‘]k̬é§ÝÛÜXÜH©Éò–æ8Ĥ˜¬eˆ$?2o~2øNßþ 9ñ[Çßü â_ˆð[ÙhÞ¶Ñ4ƒ«Ç¥¤ã,¢'w4MÏ/! «ûmüi°øï¡xgÆþ |BѾ*ø+W´Ô4­^ëÂÒÀV(X³Ã$±´ŽÉ»kª•#rö²ÔÿÁMi~.ñ?ÃïÙûVñKÆvøÞÍ-¯Wìúla|Âΰ…˜åfÛ ;d ÀWÕ:—‰ÿàœ> ý˜u6£Zü¸Õ`‚ñ¬4{Ý:)u(6¬FH--á¹w\¨ÞPŒu8Íp¿·ÊYj?d½I¬–Ô>"èï*IY0óÛ’ÈÎ5¯ÿl¶¶´ý‹õhmaHcÆ–v¢…óOaŠûËÇŸ¾|.Ô<-£øëZM&çÆW«¦é*ñLëqt@! ÆŒ±ŒóHUG­|·¨ÿÁL¿b/Äòx^çâ$o,R˜ZêÙ¬„€ãâ8Z6_öÔ”Ç;±Í|ñÿRðŒ^?‹ö}ð$Ò½ºx—Æ–Ú[KmtKÐ1SØ€üWè?Œþ|/Ô¾kàð寅KžÎ(àEŠDBHƒJ‡Y>øq»;¹ aÒum/^ÒìõÍòCNÔ!ŽâÚæÞE–¡•C$‘º’¬¬¤ G"¾>ø™ÿ ý‘¾ø²ãÁ.ñÜm¬XÈb»†ÆÖêýmNe’Ú)# §†@ÅÔðT+ãه⿊|!ÿÕ¼k¦ÝHº¿†t­~ÖÂãq2Bßi™`u=G’e;£°Årß±?íð—à/ìÿáÏéßþ jZÆ«j/5}ZÃÃê=Nâà—2-Ïš Ð…`±ÆÀ8É9ýzøYñ{á§ÆÏ Eão…~!¶ñ+üërAIªK…’'kª¶8Áã¿mÙ›ö×G…~&øÒ=t¢9Óí`¸¾¹E»æ-´r‰0$`€Aüôý—¼Y/…¿kÿ‹ž7øoðëÄþø[â K«Ëc«è²évɬé¾[’Š7FÕ§*¡ÆL„{Oü¿ÀF£ðFïöŽñ$1ê¾>ø™«ê—Ú†«2+\ìŽéáòQñ”C$o!QŒ–ç (SûRþÐ?h/þË ø=â‹oZY|MÒc¸ "žyíʉ`™c–=À¥ƒŒàãöŠ¿oƒ ð¿íû0ü]ðÖ™ªø‹Æº]†¨`EŒ]5½ý¤Lê f@Õœä•Ø ùE~ÓPEPRÑEùÿªÿ“Yð·ýŽv?únÔkõú¿ àµ_òk>ÿ±ÎÇÿMÚ~¾ÒÑEsEÿÕýû¢Š(¢Š(i(¢€ (¥ ’Š(¢ŠZ)(¢€ (¥ ’Š(¢ŠZ)(¢€ (¥ ’Š(¢ŠZ)(¢€Æ¹¯„ÿÇ¿‡Zøõ{­vÛ‹X.ïv†ùHåTùiµÆHPNNMz-(¯‰>)üZø¥ãŠ2|øñY\iꯪjò!+lë–h÷uTj“´³9Ú0&¬²ßƹIyñß[ŽVû˺(=0~Ú½…xsÎ')Ê8zNi;7t•û+µsôZ< F•us\l(:‰J1qœåÊömB/–û«ën‡Û–›§k:}Γ«ÚÅ{cyC<¢É±¸ÚÈèÀ«)‚+Â< û)~Ï? üVž7ð/ìtn-þ]Ì~c4~`Ãlì«OA^Cÿ ­ñ‹þ‹Þ½ù]ò}ðÊß¿è½ëß•×ÿ'Ñý§Œÿ WÿGüÇþªdô8‡þ ­ÿÈ×ñ+öcø ñ{Z_üEðeޝª¬b#tÁâ™ã‘âd.1ÇÍž8é^]û@þÏQë¿ üð·á?†ìÓDÐ ±Ö5€¨rá’I?¸%1²ùp1»8€ ñoøeoŒ_ô^õïûæëÿ“èÿ†VøÅÿEï^ÿ¾n¿ù>—öž3þŸþóõO#ÿ¡¼?ðUoþ@ûO xj_Â.•júÙůØZ6¿g ´EåcfͼmÆ1^9ðÿöXýŸ¾ø¥ðo„ä×fðæ•ƒøšî[ýHÇ»ý&êa‰%|“ËwÆsVÿþÚè𽿆í£Ò¼wö‹n ì²¹ˆ0I#ù³•ÞØÜHæ¾sÿ†VøÅÿEï^ü®¿ù>øeoŒ_ô^õïûæëÿ“èþÒÆÐ+ÿÀ£þaþªdô8‡þ ­ÿÈVø×áÏ‚~"G¥ÇãM&-Ph—±ê6FBÊÖ÷qTd*À€Ç¾qÒ¼óâGìÃðâî¶¼ð¯‹ôØ5}#PO.{k„‹×{ƒÈ#y×ÇßðÊß¿è½ëß÷Í×ÿ'Ñÿ ­ñ‹þ‹Þ½ÿ|Ýò}ÚXÏúøÌê¦GÿCˆàªßüÙü"ý’< ð'âÆ³ãï…“B×´ôµ¹ÑºÊ‹)ý˜þøÛÇ+ñ'ÅžÓµO‚…®¦Œ±vŒF’<ìvP”œè1âðÊß¿è½ëß÷Í×ÿ'Ñÿ ­ñ‹þ‹Þ½ù]ò}ÚxÏúøÌ_êžGÿCxàªßüôÔ>Gðå~ÿÂ9i'ƒ’³®™" î߀“Çp9È<‚ aü.ýŸ> üžòóᇅ,ôADsÍg™ãSždŒÌ`| Àã^ÿ ­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß•×ÿ'Ñý§Œÿ WÿGüÃýSÈÿèqü[ÿ>·_xMÓõ]hìrêèÒìû¾h”HG«x ëZ¿ƒ¼-¯xJçÀ𦙾»´6XªùpRž_’6íM¿( Œ|…ÿ ­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß÷Í×ÿ'Ñý§Œÿ WÿGüÅþ©äô8‡þ ­ÿÈiišV£iVº&—·±±…-á‰sµ"B"Œó€  àô¯ƒ ´Hü9•áë{dðŒ×7PRÿè’ÞgÏdË_qÎs׊ù§þ[ãý½{òºÿäú?ᕾ1Ñ{׿+¯þO£ûKÿ@¯ÿùýTÈÿèqü[ÿ>˜àïÂøüðàøjÎ_ êrÍ=Í„©æÁ$·e‘ÈrÇqs¸rp@®;á¿ì½ðá¾|UðïÁ¶z>¯å´+t†I$HŸª!‘Ûj㌠q^1ÿ ­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß•×ÿ'Ñý§Œÿ WÿGüÅþ©äô7‡þ ­ÿÈÉâÏÙgö{ñ׋æñç‹< §jZíÀÄ×Fß½;vî‘vø™Iïœâ½{žð÷ü;§øKÂv)¦èú\Bkhó²(Ç!Wq'÷5ñïü2·Æ/ú/z÷åuÿÉôÃ+|bÿ¢÷¯~W_üŸGöž3þ_þóõO#ÿ¡¼?ðUoþ@úáOÁÏüÿ„–×ÂwwGLñ«6®,fhÚÞÊ{<äµ ŠË°Ý±™€9Æ2k‰ñ_ìû4øçĺŒâ¢¾ÿ†VøÅÿEï^ÿ¾n¿ù>øeoŒ_ô^õïÊëÿ“èþÒÅÿÐ+ÿÀ£þaþªdô8‡þ ­ÿÈqQ_ÿÃ+|cÿ¢÷¯ß7_üŸGü2·Æ/ú/z÷åuÿÉôibÿèÿàQÿ0ÿU2?úCÿVÿ丨¯‡Gì­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß•×ÿ'Ñý¥‹ÿ WÿGüÃýTÈÿèqü[ÿ>㤯‡ᕾ1ÿÑ{׿+¯þO£þ[ãý½{þùºÿäú?´±ô ÿð(ÿ˜ª™ý!ÿ‚«òÜTWÿðÊß¿è½ëß÷Í×ÿ'Ñÿ ­ñ‹þ‹Þ½ÿ|Ýò}ÚX¿úøÌ?ÕLþ‡ÿÁU¿ùî*+áßøeoŒ_ô^õïûæëÿ“èÿ†VøÅÿEï^ü®¿ù>í,_ý¿ü ?æê¦GÿCˆàªßü÷ðïü2·Æ/ú/z÷ýóuÿÉôÃ+|bÿ¢÷¯~W_üŸGö–/þ_þóõS#ÿ¡Ä?ðUoþ@ûŠŠøwþ[ãý½{þùºÿäú?ᕾ1Ñ{׿+¯þO£ûKÿ@¯ÿù‡ú©‘ÿÐâø*·ÿ }ÅE|;ÿ ­ñ‹þ‹Þ½ÿ|Ýò}ðÊß¿è½ëß÷Í×ÿ'Ñý¥‹ÿ WÿGüÃýTÈÿèqü[ÿ>㤯‡ᕾ1Ñ{׿+¯þO£þ[ãý½{òºÿäú?´±ô ÿð(ÿ˜ª™ý!ÿ‚«òÜTWÿðÊß¿è½ëß÷Í×ÿ'Ñÿ ­ñ‹þ‹Þ½ÿ|Ýò}ÚX¿úøÌ?ÕLþ‡ÿÁU¿ùî*+áßøeoŒô^õïûæëÿ“èÿ†VøÅÿEï^ü®¿ù>í,_ý¿ü ?æê¦GÿCˆàªßü÷ðïü2·Æ/ú/z÷ýóuÿÉôÃ+|bÿ¢÷¯~W_üŸGö–/þ_þóõS#ÿ¡Ä?ðUoþ@ûŽ’¾ÿ†VøÅÓþÞ½ÿ|Ýò}ðÊß¿è½ëß•×ÿ'Ñý¥‹ÿ WÿGüÃýTÈÿèqü[ÿ>⢾ÿ†VøÅÿEï^ÿ¾n¿ù>øeoŒ_ô^õïûæëÿ“èþÒÅÿÐ+ÿÀ£þaþªdô8‡þ ­ÿÈqQ_ÿÃ+|cÿ¢÷¯ß7_üŸGü2·Æ/ú/z÷åuÿÉôibÿèÿàQÿ0ÿU2?úCÿVÿ丨¯‡ᕾ1Ñ{׿þO£þ[ãý½{òºÿäú?´±ô ÿð(ÿ˜ª™ý!ÿ‚«òÜTWÿðÊßÿè½ëß•×ÿ'Ñÿ ­ñ‹þ‹Þ½ù]ò}ÚX¿úøÌ?ÕLþ‡ÿÁU¿ùî*+áßøeoŒ_ô^õïûæëÿ“èÿ†VøÅÿEï^ü®¿ù>í,_ý¿ü ?æê¦GÿCˆàªßü÷%|;ÿ ­ñþ‹Þ½ù]ò}ðÊß¿è½ëß•×ÿ'Ñý¥‹ÿ WÿGüÃýTÈÿèqü[ÿ>⢾ÿ†VøÅÿEï^ü®¿ù>øeoŒ_ô^õïÊëÿ“èþÒÅÿÐ+ÿÀ£þaþªdô8‡þ ­ÿÈqQ_ÿÃ+|cÿ¢÷¯~W_üŸGü2·Æ/ú/z÷åuÿÉôibÿèÿàQÿ0ÿU2?úCÿVÿ丨¯Îëÿü|ý—µÝ.ÿâN¹ÿ ·µI㶸»pÏ=³ßss ›v²7+Øjý ³»¶Ô- ¿³Kor‹,n:28Ê‘õ5Ù€Ìc]Ê.2Žéï®ÏMPJ)h ¢ŠZJ)i(£ëKE%Q@Q@Q@ IEQE-”Q@Í 7˜n#Y#nªÀqÏC\ïÃÏjQùZ†tˤ[8v= ®ÀQYÔ£ üi3Zu§µèy%÷À_ƒ„‚Yü¦!ô†Ýa#Ú;z:äæý•~¼5¯‡ä²•º½½íÜdLy¤c¡àu¯¡ék†¦O„ŸÅF/þÝ_äwÓÎñ°Ò5¤¿íçþgÎCöjðÝ¿:?‹µôe-gý‡…û0·£kòhÓûþ)ßÕ'ù¦|çÿ ÷ö„±ì~+Å}³…ŠïC¶U tÝ$m¸ž0O¹=i™ûTéÿñë¬øWUÏ_¶[ÝÁŒúyqŽ3ê}«èÊ(þÇ‚øg5ÿoÉþm‡öÕGñBþÜŠü’>s>#ý§¬¹ºð†ƒ©Î-oä„óÆ?|:ާ¶8Ñÿ SãVž3­ü"¹Ø8ßeªÚÝLjÀ 8À<ã>•ôe-ÙµWÃ^_ù+üâÚ”ŸÅ‡ƒÿÀ×å+~Îcãî±gòk? |[½…¥‚Þ'ýö²(éŽÞ£µ'ü4ÿÃèüMtÝsLÇßûN™2ìôÝ´7^1Q_FÑGÕ1ká­÷Å~×0oâ¡÷I¯Í3À¬ÿj€÷ä,,…I$~ö ˜z ÿËH—üñÖºë_u¿eñž“–Û…{Ø£c»  ì}±‘]ýî“¥j@®£g Ø æÆ® |Àô<×x»à×ÿè:–˜|7¥Ãwyo4q\ýŠ!$2ʤ ÕC‚ ƒÇZ™¬|SjP—ɯý¹• eÒi8Î?öôeÿ¶Äü¶ñ7Æ¿x7ãGŒ•ô틦jZ×ì¯ñ[IÑí&¿¾»ðæ£0@,²»BÀ*"‚ÌO`ké:(óX~Ï:ßÇOø&o†~ Mé>"ŸÂšSÚÇt†‹P±XçŠ)•ÆcñùnHÊ«ŒŠá¾ÁAõ?…þÒüûEü'ñ®ŸñÃð&Ÿø½cð÷KÒ5ý&ÅÍÔ{ƒ˜ây.à ˜REŒà‘•à‘Í}EE|áû[| ÿ†ýŸ|]ð’ …´Ô5X[ Ÿ„KÛIx7œ#ºr!‘Î+ã?†?·†¿ðÁZ_Ã/Úwá_Œ´¿xrôÙ&Ó´£gª›d“ÛÌŽ¨Í(`¹Lò¯ƒú·E~[þÏ> ø³ûBþÕ—¶wÅ_ ^øÚÚ'„4}IDz‹Å!Kus£È’S†Ÿ1B–DÜý'ƒü3âHà©þ8ñdÚMÚhsü;†Ú;ö‚Ahóý²Å¼¥˜®ÂøV;AÎãƒ_¤”PWÇ_µÆoŽÿüAàŸøÀ¯ã¿‡rÏ,^)‹Lµšï[´ÊšÙP¥,ͺ22›YÐ8aö5øƒûTüOÒ?o+?|*øðÿÄ^&¶×m/$ñ£¤Iao¡ÚBXLÍs Ü2YIAÁÚ1¹Â)õŸŠ6?¿dÛÄ_´·‡|ªxëáÅKoC¢Â.o´ËëEXÒu~fM±îÉ!I‘Ã2¿Yh Ì ¯ÛƒâŸÇ?èý‘~ëy¯ám__ñ^™%¦cb­™GÉ(g‘†xÞÁ«±Êíþß^ñ'ˆ<û3\hMÞ§™ñJ¹»{h$™màYâ-,¥„@%›c­~’Q@›·§†|Iâ‰ÿ²ý΃¤ÝêPé_t˛Ƕ‚I–Ú¸€´³R–læ¿I(¢€¾cý±¾Ý~Ñß³§‹þiR¬µü1ÜiÎä*}¶ÎEžf9Ú²2yl߬OjúvŠü©ø_ûkðV“ðãã¿Â?Û|KÐíÓOž×MÑšö=NkUùöÒ+*°—nã€Pò3. y¿ì‘§|z×?à¢9øÅñŸÁ—ž4ðT“Ú[¼nñXÛý²Æ;KI§±‹‘ ¾çL†É%•[r¯ìõù5ñBÇâwìûax‹ö–ðïƒuO|1ø£cimâtXEÍö™}h«N°/Ìɵ7d„Èಛºk¯ÛƒâŸÇ?è>ý‘>ky¯ám__ñ^˜öš=ŠœÊ>YC;°Ï‘¸"5v9_Óú(óoöôðω ê~/ÿ‚vÝüñÝ燧ñ,zý„‹unðÜ@·’H‰(ŠP§åÈuÈÁÅpŸÿjïþÈ ìÿgßÚ‹á§Š#Õ| Ž›ªèºqÔl5K%bmü©ƒ"oT! È~Çܵû!E|Gû8üYøïûCø‹Æ^&ñ×Àßîí’ÓA³Õàx5Û·n%¸•wíHY ùJIO-Ø+³|oðâ?àž6º÷ìíñÃÀÞ"Ö¼a©\Þxcĺ-‰¿·žÎé÷g•ɸ+’Á”¨PŒß´”Pàçǯ|rý¥hŸÙûâv™ðÃÄøcá/éÙÿjYI¥w,×pOu}-²+mcŽUwm½Hc–Tý㢊JZ( Š( ¯Èø-WüšÏ…¿ìs±ÿÓv£_¯õùÿªÿ“Yð·ýŽv?únÔhõþŠ( Â’—ð ÿÖýû¢–’€ (¢€ )i(¢ŠZJ(¢€ (¢€ (¢€ (¢€ (¥ ¢Š(¢Š(¢Š(¢–’€ (¥ ¢ŠZJð­Kãd)ñ÷Jø  iGT¼“K›WÕ¯Á#Ó-Ãíà V.ó?ðåv­Èa^±âhþ ðÖ«âïÜ-¦™¢ÚÍys+tH`BîÇ诔c jº‡†üAûAøÎ‹ÅïN¨É'ßµÒ£ÊiÖ£Ól?9÷|rSHG­|*øÎŸ<[ãßjÚIмAà=HZOlÓyÂ{9×Ì´¼¶!òç@N0v°*I ׸ן´:OðGãwÿiÝ8ôKæO ø´/ÝûäƒìWOÆ?q>cÏ*£š¾êVWPèC+ ‚9|9û.*Ü|løï{p·kB$•¹uŒÝ^‚!p‹Àã傽ÏáÆañ+_ñǃµm è"ð&ªÖv†o#¶kÏjVÚ¥º6Ó%¬É2‹!#?xÂOÚk¿t¿ýƒA‡ÁÞ'¹ðýœpíbºÇÍ yýÕÍ|AðÿÃþ*øWûm|5ÒÁÚÃtñ~•ªÅ}¦øzúK˜omím¥šº£‰Ç*|Œ ç Ï\ëüøð‡âÿÅ?Ú>ûâNˆ. ñeý¤1Ü?dŠV‘šH€#cÈGß7È0iò¡\ûÓã¯Æ?à—„ìµ¹4«jÚÝüV“¦Z²¬··÷Yòâÿ* +31àMs_ >&üx×¼UƒñGá0ð–Ÿwo,Ñê6šÌ:œ1¼x"ÔE £°'\‚28¯–¾|-OÚ_ö5ðVâÞØê~Õ®¤Ðu¸^ê4‹Éà²wðø„m õ‚8®ÓŸ?h‚<ðcã_ˆ4¼MÕí­>Á}ÅŒ^k áVd*ÙàŸ½ÃiRrô ŸGêŸ|¥|`ÑþÍ*=Ö­¥Þj‚ø\B-¡rdžL¾á#2£¯bmWLM<êÏy Ø…Þn Š" ýíùÛ|â¿7Ï¢–£—[ç y „Pú+óÒ}gá·Ú%Ýñâ"ͰjG'þÓÒ¾³ø1q£Ýx)%Ðõ]_YµûD£íÚÊ—{R&DmƒøxÇ¥}&kòÂÒö­Ë{k GñfӥʮzÅ|MaûYxÂïOñ‹Sáuí焼5¨ÞØ]ßÙ_Ã<ȶ/¶i³¬oµWç8'>™¯¶«áßÙ†ÿJÒþ |NÔµÇHôëoxšK†r6ˆ•²ùÏlVy=>Æ­Z´ùÚqI]­Û½¬Öºi{ú>6sç„!.[§Û¥»Ÿ@k_uGáÞ‘ñႾ6‡WòåH~Ø–;m^7f¼ŠÃr2ª4xÈ$ÿtŠàþüvñÇÆÈc×n|ýƒá©–åSP:”w n-¤òš? FŒ2Á¾lãzÀýŠí/íe¯ -ò•óc½’ F‰î%*~ËPsNý‡äÞt¿ûêßú]5wc0XzqPPRpšŠ•å{>~Î×÷WNûœô+Ô©:2riJ7kOîù_©2þÐ|]¬kKð{á¡ñFƒ ÞMa6£sªE§‹‹›c¶d¶¢¸Và3צø×ãF—ðãáU¯Ä¿é—:uÍÜvÈšOË%ã_\¶Ñpv´€ç$aKtázïÀÏ‹ fñ'Œ?gÏGg§_Os«OáýVÕg´{‡ýäÞMÀ!¢ßŒ·êíy_Äï‰ÏñWáÇìùñYµ];LŸÅÖm©ÅËC°Êð³dôŒ˜¤+’Nsší§”áq)º :w³iËšü­òÉ=.ììâ¬sÏZ”fª7Ïk«ÚÛ¥un×[ŸXxâ?Æ}sÄvZŽ> é„né{«ã@Ê»•.aX£([§ÊÌàúׯ~Ó^,“Sñlß ~Ëâ¿xy-µ]GûB;Rf·]× k #™ŒCïe—ÐW×Õñì£ugcðwâ z³*˦ø‹ÄCPÝÆÖW.ÅóÓä óÚ¼ÌÃÔ…LD¨/w•r§+jÝßÅ{Ù[{k±Ù_ÚFQ¤ª=nï¥ôKM­ç±é?ðÒ~¹Õ~¾—bn<=ñ@M¾¢Ò„6·1¦V "Úrí&"?8Ãg®+µñÅqáŠ> ø]e¦FóÅÂòY%ùbÎÚÍ4¬»[væ!TeyÏ ü~ñ·€4ÿÚ]øOâÝÍdº_öœj¶ÒÆFÐn<¢ŒH9À¥xÀ߆^0ñ¦¹ñ_RðÿÄ]cÂñx×W‰­tøíZ)2“#á‘·@àãqÖµÿi­ÅúG‰>h:.¶5/Ûk3E¥ªFK8Hñ$é@}Â]ËpO2ŒmfÚ÷ï¤ÕÞÖ¾üº™ýk¨:®O§òÿ2Zi}»žõà?š¦¿ãë„þ:ð…ǃ¼]ö¨YÁ5ÌwV·T˜î"˜å8Áî1Z~?hž*ð?‰¼Wâ»#á{ïÏum­ØM(•íÔÈ| utå·­|ùðÂßŰþÖQþÐW1OãX4B<96Ÿû½*m=œ›2,žz±;·Æ{-‘ûHü0ÓõÚÀšMµìö:Wņk_YÃ…Žú= ¢¸„¿ûLa?ÝòEsË)Á˨5Ëx)Þ-µ¢nJ7Ý8§¾ªJÛ\Ñckª^Ñki8ÙÙ=l•üÓíÑÜû'à﵉þÓ¼w«h-áÕÕÃMkm$ÞtjO6™æ ΃œœO¨á†hc··AQ(TU ª0‚¤¯ŠÄN©)S*oEÙvÔ÷éE¨¥'wÜ1E-%bXQEQEQEQEQEQE-%Q@RÐQEQKI@Q@(¢Š)i( Š( Š( Š( Š( ŠZJ(¢Š(¥¤ ˜ÿlh"›öwñD’.æ¬HÃØS?“^©ðjG—á¥åŸBÓ >æÖ:óÛþMÓÅ¿öáÿ¥Öõé¿äŽxþÀ:_þ’Ç^?äg/úö¿ô©£Wׄ¨ÿØLÿôÕ3Ò袊÷OÎBŠ( Š( Š( Š( Š( ŠZJ(¢Š(¥¤ Š( Š( Š( Š( Š( Š( ŠÎÕµ#A²“S×/ Ó¬âûó\ʰƹé—r~uà·Ÿ´f«ß]è¿ t+ïê6˜ÜÖ`Cb§œîº“å8!Hl¤×+0£Fʤ¬ÞËvýÕüŽì&[^½Ý(Ý-ÞÉz·¢ù³èºáüeñ/À_íšëÆå¶š»vß3g¦ØP4ø)¯!ü~ñûÊÞ0ñD> Ò¦ RÇBÌ—Š?»%Û€U½Ld©Çç=Ï„þ|/ð…Ãj6z2_êr•ioµkË™$Såæ,‰’y®?­âjÿŸ*ï?þEk÷¸ŸSÂÒþ=NgÚ¬žŸr‘Ã\|dø‹ã$0üð-Ìñ3𦶠‰Œã¤e–YTó÷p@ç¥:ëào‹7¸øko¯YIâ»[A.”³¡¼KBÊ‚v‡;Ä{™Fìc$z×a_5é—_³™ýªµk=5bÿ…Ú<6¯zDw~göŸ\¹dÇ›åp§ÌöÆhé:Zù“ŶGìÓàtñ{x¯ÇÚkøö;VŽX.D‘^\#ê¾+‡O𤡭u¨n-ÌùfEX­ån˜£QfQ¹A^kÅ|ÿý¾$x®ÏÁzŽ…¾©©H±Y‹û+»(®ÎRiâHÁc‡e,H'ŠûŠŠó/ˆ?þ|+Ô¼1¤øûZ]"ëÆZ‚iZR´3H./c-:ÇÔ|ÒAÝ«ÊþþÙ_³·Æÿ‰:Ÿ…ž) ×t»inæòm®ØÃ‰ÃƱÈH b©#š÷ü@ð?ÄKKëÿkÖZý¾™w-…Ô–S¤ëÜ!LHP²(e%O##Ö» ù¯öeºýœ®´Ù°Dº\~$¾MkÊŽî1ý¸/µgí€1;|¾cÌÝ=k„ø©ÿý“þx®óÀÞ1ñª¾»¦±K»[ K›ãlë÷–Y ‰ãV_âMÛ”ð@4ö}åþ8|(øùásã/„^#·ñ”’dxƒÇ$2“C*¤±6@ts^3â/ÛËöOð·„äñž¯ãèOMFëIT[[³s%í–Ï´G·“ç:ǽs O/æ?4õå-|Ýð'ö·ýŸi)¯,~ø²-[QÓ£ÜXË Ö—qÆH]þMÂF΀Ór‚@$|{ûSÿÁC|ð›ã§€>xwÆKco¤ë²Eãµ:tó}–ÅbFH÷=»._9¶,Ãn êãß~>|%ý¢<3wヺèñaxöN-®mv\¤i+&˨¢s„•B‘Î3@õ _U°Ð´«ÝoU—ȲÓá’ây,(T»¶p œO   ZøÃÅðPÙÂ>Ð|i©øþ,tKæFêT@ÅNÒkÓþ~Ô¿¿hè.ßà÷‹mõÙôõV¹µ1Ëmw ·š „ŽB™ãz©Lñ»4ôà6ý©>|8ñ'ˆ|#ãŸÁ£jÞÒãÖu§†àlf‘!Ä‚3ŒòHбFÍ!,J—à·í1ðoö€ð^¯ñ ᦹö¯èW2Ú^]ÝC-ŠDðÄ“9o´*an¬Xð\`мÒ×Á·üãö%Ñõ·Ñ'øŠ· ´r\[i×÷¨ÀãýtVìŽfBÊG9Å}à?ˆ> ø¡á{?|=Ö­|A¡ß®aº´Icªœr®½SÃx ÆŠù^ý¼?dï øFãÆú·íâÓ-õ+­f»ûL—ÖA ñGmäùÒüÄÝ"¡Œn?5±ðGöÏý›?hm^O ü-ñŒWúäHÒ6ŸsöWEïã¹HüÝ£–òËmœPÔtµÊø×Çøqá›ïøïW¶Ð´=57Ü]ÝÈ"Š0N$õf$Q’Ä€$ øóÃðRÏØ·Åž"‡Ã:Ä8í®.¥X`šöÆöÎÖVcþ‘<  ÿŒƒÞ€>í¢¼ÏâŸÆ†ÿ|!ÿ ïÄÍet}ϰºòf¹[“¶% n’9Ü{íÀêHóÿ‰?à ?²o…> j? õo#kz:^=ð·³»¹‚ÜØBóÜ+M NŒñ¤o•BÇp+÷¸ ²ëÔþ xFñ~‘à [^²³ñ.¾’˧éÒΉuv)yZ‰ áIl€ ¯Ê¯‚ßðTß‚—^;ø¦~0|BK? G­*xD &ñ‹éh…K“mhÒ|ìb|8Î0:WÝþ7½ýœ?á£>ÅãQ|YžÓQo –Žñ¤û8‚Cyµ£fQäïÿ\A?ÃÎ(éj+;)ü_øgðK²øÛ⯈m¼9£DÂ15Á%¤‚DqF¤•È„EfÀ'¾wøcÿý‘þ.x¦×Á^ñÜk­_ÉåÚÁiu`.ýÑ—1$e˜ð¨X3’E}¡K^+ñ¯ö‡ø7û;hv¾ øÁâXt k÷h­P¤³Ü\ºãpŠI_nḅڹˆÈ®à§íŸû8~Ð:äžøiâÔº×âI$:mݼöWe#åŠGq~f'abIÆ }IE柾1|2ø!áY8F×oŸË´¶½´º±ûKº±=ÄHŒÍü)¸;vZùsTøà¯…¿ðT?ˆ9ø…¬[è:™ð¸»¹}¨™¿²U©fb@UPY‰hõŠø¯áŸü3öFøµãOøCÇIýµ©J!³ŠöÎîÅn¤nb–â$Œ³ž bp ž+ì-_XÒ¼?¥]ëšíä:vaÏqsq"Å 1F7;»± ª d’@£K_ø)ÏìKý¾Úøˆ «ù_jþοû!|ãao·íýÌs»÷.›­húÆ‘oâ &ú Ý.êVH$…†å‘$RUŽCŒPqþ7øà†šñ7Ä-zËÚH•!7wó¥¼I8DÞä.[ øûÄðSدÂþ"—ÃWÿÒâ{yZ§³°½»µÔãý|0@tB™gƒ€µdZZó|bøið»Qð¶ã­i4‹Ÿ^®›¤«Å3­ÅÑ„/2Æ0GÍ!UÍ|·¨ÿÁL¿b­/ÄÒx^çâ$o,R˜^êÙ¬„€ãâ86_öÔ”ï»ÐÞ4µŸ¤êÚ^½¥Ùëšä:†¨B—×6ò,°Í ª$Ô•ee ‚äWÇßࡲ7ÂO]x'ÅÞ<µ­>CÕ½¥Õø·uûË$–ÑÑöÌ·+#¶Û¼h )9½ȯ£()h¢€ üÿ‚Õɬø[þÇ;ý7j5úý_?ðZ¯ù5Ÿ Øçcÿ¦íF€?_ih¢€ 9¢Œ ÿ×ýü¤¢–€Š)h(¢Š(¢ŠZJ( Š( Š( Š( ¤¢Š(¢Š)i)h(¢ŠZJ( Š( ¤¢ŠùûöŽøCâŽ^ °øuc«Ç¤èWº•¤ÚæUŒ×Zu¼‚G¶„ޤe1㎄+Þlìítû84û– khÖ(£A…D@UG`ÀbŠw†ø›àâŸÃï|;ñ ÓüAg5¤¹Ûæ.Ǻ6È Ïø=á¯ø7á—‡|%ãíV-s\Ñ­VÒ{èU‘nVR) ¾Hs]ý‹äŽ zM\‡eoù,_ÿì<¿úU_Q|Uøw¢üYøsâ†þ ]Ö> ³–Õ›©˜f9Ñ£p®§±×Ë¿²·ü–/ö_ý*¿¯¸«Áá¿÷HúËÿK‘ú7Šÿò<©þ ?úf™ñçgÿ‰?þx#à·|Qm,VW6 ⋨#•T°°}Â(¹Ìo6Ä.Ç£ ¯W×¶öööEikÁ ©hª"ŒU•5ï6~s`¯Ÿÿh¿ƒú×ÇOé¿íõdÒü?w©ÚO®®λӭÜHöÑ2‘±¤e_˜ôÆE}E µµ½´Vvq,@‹q¢…DEUUÀ ®3âo€4?ŠŸ¼AðëÄqïÓ¼Ag-¬ž¨\|’/£Æøu=˜]Í€ó„ñwƒ~xsÂ^;Õ!Öµ½Ñ-'½àC”ŽM¬I P.ïö³Ž+Ò(¢€>9Õ¿bßßøçÄ¿tøÛÃ:Ÿ‹.ÝúèÚרbwþÂC¸ª36ÑÀ5è^2ý›|ñávð¿Åº¾µ©Ï ÝŽµ5ñ}j»rLw"ì®L£$n+Óß} KO™ŠÇÉÞ ý~ø7â>ƒñv}Ä^#ñ~‚·(º†±¨›Ù.æ·òåÞŸr4v1ªÁb[usšçì3ð×TñŠ|U¥x¯Å~¿ñ­åÅÖ®t­Wì±Ý%Ë{wcÚa¶ÐAl1ˆ8¯´è§ÌÂÇÏž(ý™þø‹á‡‡¾éòj>Ò<)$3ish÷iyk, È®“a‰b·$ç® aü4ý”|ðûÇ0|MÕuïxçÅVVïke¨x“QmBK(e\[‚!ub¥ŽN @fÏÓôR» ñö}ð_ÆMCE×õ{ý[@×ü<&[[C¾}>þîƲ¨o‘öŒ‚;qÞ¨x×öoðoÄ/†:ÃOêúÕóxrhnì5¹/˜ëPÝÁ¸%È»+“.”±^‡×}Ec±òŸd‡¾ø‘¤|^“^ñˆ¼_¤Åqö†±¨›Ù.#¸ˆÅ²]é÷cR|µM dçuw_?gïüf½Ñu½bûUÐ5ïùÂÃVÑ/^Âþ¸MÊ¡¾GÚ2íÆ9¯r¢‹±Xñ]{à7‚¼a𖃾:¸Ô|O¦@‰¶ûQ»iµ?:&/Áº[ÎFå_°AŒøû.èÞñ–›ã{ÿxÃÅ÷š(˜XA®ë/ykoçÄг¶(gòÝ—sgé+éÚ(» 8þÑ?|añbßø[²ÐõŸkëPI¨[Iwo#Áª£Ç‘1lœ8â»|=×>!|:µð‹|Q¨èz» y.õ/ ÜI¥LÓÅˈX™^8œõBÌqÁ5êÔQqØù á·ìsáo…ž'ÅøƒãiXßÉ©ÝYÜë[ì¯îæÿ[%ä+‰ÚC˳ÌpI­oüøŸãÏŽþ ø¨xJ‡ÁþÔ$¿²Ó㱘jóÙ5´«-ÉœÄÃ{–\D¸'šúžŠ9˜¬QE!…J( ¯‘#ý‹>=ýüÚ¦¥¯jZf¥}6£>•>¤É§5ÄòyŽL¬yñÉ'M}uEw`ó*ø{û ¸ß{õð´êÛÚFö<¿Çß txFÃÁvú®§áM?M’&„è#O‘c†'… Ê«$+}Àʯ¥p?f üÕÿ…¼IâËeŠh×O¾¿X©ƒ¼‚I¸gw¹õ¯£)j¡šâ#IÐS|¯uÜRÁÒsUuGÉÚ¿ì‰á-NïTû7<[¦i:ÌóÜ]éVš»Ga#\¹yWÊ(ØWbw ž+Ù/>ü7¿øf¿î4H›ÂqÛ¥²Yå°© ¬%üÀã™»~ÿ˜’rkÓ(«­œâªróÔ~ëºõïëç¹4ð4c~X­«z;ü?ýœt?x’ÇÄ‹âïx€éQÉ•®­©µÕ­²È›ȶ(/ÐqŠÇñ¯ì™ð÷Æ~#Ö<@š¾¹ GâB­«ØéZƒZÙj,£¸ˆ+n,8<Žø’kê ZµžâÕOjª>k[å{þzúêK˨8ò8èrñx7ÃvÞ³±KmlŽž–±Œ"[y~VÁœÿçß5Æ|ø/àÿ>ø)®d³–êKÉ%»u’i&‘U fDA¢¨ùz^µK\_]«É*\Ï–Níwk«ûÎae;j´GÉW±ÿƒä×5­wIñ¯‹´7ׯ§Ôn`Óuaknn.s°D‡ð$àšïö|ð¬ø5=gXÕgø}{=õÍíÚÏq<³¹r.dhó"®v¨HPx¯x¢»jg¸¹Ûš£ÓüšüŽxåÔ#{Gú½ÿ3̓¯7ûQÔÿv¦åæýØýïWòLõ?²U?÷ªŠ>KÞ—Ü´_öóGGâ_Úᮇvt}*òOëL»¢Óôhšúi2:ˆÇ©óŽq\ÀÕ?h¯ˆŽbÒôëO‡-Ä`‹«À/µ1œýÈU–4>« gƒž·ø_Á^ðUŠiÞÒm´¸vâ³ý÷ûÌ}Ø’k§¥õ,E_ãÔ²í>÷ñ}Ü¡õü5/÷zW}ç¯Ý‡ïæ>|ÑÿgÿkÿÂOãû»¯k%@2êì²Û¡î"¶F©è­¸Õïvvvš}¬VZ|Û[@¡cŠ%ˆ£ U\=…X¥®Ü.ý”R¿Þý^ïæpâó ÕÚö²nÛv^‹eò (¢ºÎ1)h¢€ (¢€? > ÿÊd~;Ø™gÿ¢4ý¯Èƒò™Žßö&Yÿè¿_¨¢Š(+ó¿ò–ÿÿÙ3‹ÿK¬ëôþ¿6ü3áŸEÿPño‹%Ò®ÓD›áÌVÉ~`Z¼âöÐùK6Ý…ð¤í œÇá_³7Áÿxïþ 5ûFøÿÅ6jrx"úËû>+…Y"†ïQVÿIÀ5Ý•øw’9Áßü{ÃzO†¾ ~Îß´kXíøVÃÂ:$zŒÀó]d¹¼URd1ï0ùÀdíàë/Ú—Eý~|:økoû@¡7€§|/¥FÓÌ÷SXÇ$bʲäTV2¡E݆eß þß´W‡þ:þÌÚÕ·À¯詤Écw¥x“YÐVÆÆÁ…ÌJγy¬Ë±3D6ŒË‘ÀÇÐß·‡ü]à_Úsà—íNÞÔ%øû;ü4ñ<¾„[ÝkÚ¾§§Ih ·ºêCµ–Kë™C<`‚T«ªÛ +›k¥‘íÑ™Íyå•p~ëöéKÿ¶ø?áÏ|eø×ªXE{®ÛxËRÑl&™VCe $\J`È;S:‡aÉ9Ñý‹tÿŽ:ÏíÏñ{âŸÆ]xVçÅ~²¸†I h]­…µ©œ¨Fž;tQ*ƒá²ªAQì¿ðLÿ ø“Ãþ,Ûø—J»Òe¼ø­ÜÀ·pInÒÀðÚ•Š¥‘ˆ 0È88ƒðÓ_»›Y‡K€ÜÜÅoyö\:Ƥ byUf]€?P-ímlãò­!H9ÚŠd÷À®/â ðÇÅêFAÑõ ÿà;×ð/㿆¾?ørûÅÑu½ÎÊçìØÖìÂI–²o‰›|6ÝßÞv®Ïâü“/Ø"ÿÿIÞ€?3¿àÿüáïÙ¶ÇãÓá¹ñG‹nムç·´´¸{d¶‰ÎJF^&‘€ÆælœápŸµÇ…ôoƒŸ¶§ìáñ»ÀvÑé¿uãá­oìȨ·ð]´0+ʪwT™Áb ùcî‹_7Á=ÿj~γ¶—§|Uøâ_áî¯5î¡¡xƒB²:Œ0s%½Í¥Ú¡_$‰â’D$’ÁúmÁ¯£tWø‡ûsþÔ¿~*Ià½cÁ?>É6¡e6¹n,ïu}VPŒ “å«GÈ,¡Q²Áä  §~xgâïü¶ÓLñ…¢jZ>‡àûmf{)@k{©-œÅ̇‡D–T“iÈ% ŒŠõ?ø)ååëü4ømð'ÃsËâ§Œ4Íýí•cȰ<ãy†&÷ ƒòäWI£xgĉÿQ×|VúUÚè’|:Ku¿0H-¶@|±66À'nsÒ½3öòýžü]ñÿàݤ_ æX|uà­V×ÄfDY®­7)„¼Ÿ*îW,¥°¾b b$}3àß…¼à‹o‡ðõ–Ÿá»X¸²HÃ"cÍR?xÏÉv|–$–$“_šÿ²Ž‰eð3öÿøåû;x(}Á:–“iâ›=9ò-.Û,‹ŽstÊcäTÂ+°Ñश:n‡‘ñ7àçŽôˆÆc¸Ñí4Y'Žk”ùXÚÊÌ…¢cÈfQ€xÝÁ:ÿ±w‹¿ÅO‰¶Ç]ü+â?‰ =/C‘ƒM§èöêžZÜp‘–8”©ÙFgP_j€|ñÿ¹ø-à½KÅ¿~6kZ|:Ž·eã GHÓžtY>‘Ÿ:i /œŠÎ9Ú˜À÷ðT¿ èÞð×€j ÛG¦øçÀþ&ÓÕo EY®-$ó¡”™:.ÐÙY×s_,~ÄŸ<û>kŸüC¬x[ñwÂÝsÆ:¬ßh¦úçJÕ,ʼ=ºá¼™¡š!¼°¦'"½ÿ⇋ü?ð>½áÏ„~ÕíõßëÚíŸØßÙ·*[Z#–ß•fw0fETËGÿ/ñ–sñçàÂÿhÚ¯‰|n®õÝWFÒ-šòëS{\,1 pËæ„ qDr9ô®‹ã‡í-ðcã7Áoá©ðâ7Ø®¬%ƒNSàò‰ar#+m4eýщöPtÁƒì·Â_ŠwÚ×ÃÚ_à~”Þ!ñoÂBk™txÈjeÐ_´Çš@#ÚAb²1PX*žC^ÿ‚” g@“Ãÿ > øãTø“y—m¤Þhí ·n0 Ô¡ËyH~bU~`>c%”ä/Œšïàµø›a¦ø‹D¿³ÓÞ=F -®V=AâµÊJªøâ5Žqœ×ì_ìýðKÀÿþxoÁZ“n’[ØÀon%3Þ^<`Ü\O!ärÄ’NÚ0  øgöððÿÇÿÁ= Ó|¤Gª|Fž}&]NÏA·–xÖo´eŽ42±ò×FRP°b¸R~¨ijɦZ#‚¬°Æ<BŽ´ù{ÿþÒ´Éþ1þÕâk8d|D¿Tݪ..pGz Ñøûÿ)5ý™?ìâOý º¯øOñŠ÷ö9ý¤hOüUðŠo­<{âyuýóEÒžþ+¨.%žM¡ƒ('l¨Ü€ÁÕö•ýñŸBñ·ÿýšüc§h÷ÒhÖšF¾nn…´†c5…ÎÄž@ Fİ1“Šù§ö±øáËø(¿„ôŠ>Öü{á/‡~þдÐôm8ê¬úÛ“ö©mK(h×1庎0AÉ©lߎŸÿi‚¯‚ôßߢñe¢Ç>qáI"6WQ:·$Œé¨(áAã ￵‚þ,| ý¥|ûjü%ðÅߎ,4í.MÅ:-‚‡¼}=ž9àŒy2ÁAÁ‰3„ge¥âÿø(?‹¾#éÿðƒ~Ê üU«øûQxâŽ}sI6zf™¹€’K·2 0ÅS'%ø À=ñ:Ë㥎½û3~ÙúÇÃÝOâ"xWÂÑZx“EŽÝ¿µ-5íÈ–ùm ‰<Ç/Ÿ)CÈqôÂÏŽ_±/ímñ«Ã^,¶³ŸLøÃàäŸû:ÓWŽãJÕ!R§ÍB±H ¹Ú»¿vÏ)E.v…,OEñoö‹ý¡?gï‰~Ÿâ€Ï‹>ê*kê¾Ó®®®ôíi@3ÆÓ>-7 ¦åVØÿ}Þ2­òÿŒ\ßÏ¢ÿÂUe|‹¨Î·B× w¤l¨€G•Üä±$æ½WÀÿðQ GÀßtÿü^øEãhþ)è–ic6k¤<°j7–ê"Á88ÌÀ1!.NÏ0[É?jM/ö¥ø™ÿìŠëãf‡-ïuŸÙÞ&•¦Ø±¸³ÓË SCcæ(Ÿ#*+aCAø™¢ÁLivøsã-N ü$ðl1ëg†¦[^x¯ BÝÌxBŒ6ÀwØmäÈéôüNÓôØŽ÷IÒmb²±²Ôôˆ`‚XâŠ(äÚˆˆ Uzí­û/k~=дŒßÕt?‹Ÿ ÜhRZªÇö»H2[Ne)R¹ò‘†Ì–Œ€’14~×ÿ¼]ûRÁ:‡ˆôïëwФÕtØu-´û‘sÜ1Š6Mï 6° C¡ÿ‚©xF?Åû>xižÝîDÕü-¥x‚ßO¸Ég…ÅÄËlÃ?óŤ]£  AŠú‹þ Õð_Á ¿ek:NÖüg¦Á¬j—ìŠ×7R_/œ‰$˜ÜR$pйÀÁ8ÜÌOŒþÂ_5OÿÁ:ï> xêÆïÃ×%MzÂE»·xg€]É"Ç/•(Sò’r0q\ìñûUøïöMøycû;~Ó "xRçÁV«¥ëqh>¡…Ô¡Ó-´Ûñö‹Ø@ýÌ“Í#Œr äfýÞ Š( ¯Èø-WüšÏ…¿ìs±ÿÓv£_¯õùÿªÿ“Yð·ýŽv?únÔhõþŠ( Â’—ð ÿÐýû¢Š(¢Š(¢–’€ (¢€ )i(¢ŠZJ(¢€ )i(¢Š(¢Š(¢–’€ (¢€ )i(¢Š(¢–’€ (¢€ (¢€>ý•¿ä±|{ÿ°òÿéUý}Å_þÊßòXþ=ÿØyôªþ¾â¯ †ÿÝ#ë/ý*GèÞ+ÿÈò§ø(ÿéšaEµîŸœ‰EKRÔlt:ëVÕ'K[+(žyæíHâ‰K;±=¨$ŸJócã„ô‹:Áo.êóÄºí•Æ¢Þ0ðÚÙÛ¾uË–ß(˜ –ã#+ðÓâÿ…~)\øŸNТº³Ô|©Ë¤ê6—±¬SÅ<@à+81Èc|üÞ•ó·ì‡¦Ü|@Ô|gûTkð2^üF»ò4t”aí¼?`Æ+TøLÌ ®;ü¿Ýª_§_€µG†>,ÿ¨ðŸÅt‹Ã:ë#‡U„¦]?`]A…‰87š·Bn}ÓET”yç>&øwâ%Ž·¨h)p‘h:…Λp'@„Ïkå0Í•ç‚qôåp~Ö &ÿ„9¦7Ö±øÞIc±y UDhn ±óˆs°㓊åÿeq·ÃAê<]¬ÿ(ëâÿø Nø›§ü ð6¨vGªèÞ(HäÆLS ¹ZGO¸áO^FGzðê㪨AÆ×æ—ê~¯€á,¾XŒLk¹(SkTõKÙÔ›~zÅ~Gêw‰>%øsÂÞ3ðÏ55œê^+7ÌÆ¢ÙC?˜Ä‚¼0 qþ7øûá/x–OYéš·ŠuëxÖk‹-ÓírÛFài‹ñu¯Å»Úà×ü-ýô5Þ§öOì›yàÛˆÌó<é9ùvãç5Uq“ƒqµìâ¯ëk˜`8o ˆ§Nº|ªQ«.VÝýÅ'i­¬¯µõ>Ÿñ÷Çü7ñ†¼âY'ŠeX­ŒQ†Š2ò,JÓ1a±K°àô'µ7Ç?< ðïÇð‰Þâ CÄ¥E´‹h»ùj$}À®[އ­~}üfñ¯ÃïxÇã-LjuÈìu-Î×JðônŽ]æÓdk‰¼¶E .Ž}k©ñÍžûG|EøYÛ„oøBþA(D7¨’ã.uíÎsO3›rP³wVûìïýu=Ì?aa 51jQ$œßŸ³U"㦶M¦»ÅŸ~ëß¼;áßxsáæ ³WÅ+pÖ…€¶C#ù¸à`ŸJàü]ûDø?Ã>&»ð~•¥k-Õ´ÕV¾‡C³û_ØÃt;:(oöA,;_*xcÇú~3üo‰´®iZÄM÷ÒöÎÙãv Ï@ñÆI¥z'ìêkZ_ÂOø«CÑÄž'¸ñ5û\Ú,ñ[Í4»âR¦Yʨؿ GSŽO:Ã*’´]•ûkd“Û½ÙçbxF†’©^<ÒQI®eå*•"Ÿ3Ú<°¿MÓ½xñÇ¿xsáÖ—ñ6âßP¸Ó5{ˆ­`†+cö¿>VdÐÈP‚J‘ž½3Px?ã¾›ã/YøjßÂ>%Ó$¼ó1q¨i¦ hü¸ÚOžMçnváxåˆëæßŒî>-|ð4K£Þ]ø·OŠnÎHç†åãRå6î]ã'$t=ëéÏØþÐPø‚ <}«xvëE þlz}ÔW%¶›^Y@ Œär:V´ñ3D¢î¬ºwùèqc2<.)Ö‚NjŠÎnë–ÖJÑjM_ºLá£ý®>ý¢ûí¶ZÍž§]Éc>£&žïcñ0V ,FLI `M}+§j6½…¶«¥Ü%Ýäk,2ÆÁ’HÜnVR8 ƒkäÙbËLÔ¾|FÓõ¤I4ûŸëQÜ,œ¡…âˆ8löÛœ×Mû\_\~ξ7Ä“Ú<œþíf}´ðxš’qS×™7éf¿Ìˉ2<%V–8ºSŒ]ÝÓæŒškEf¹]÷Ýl}KE=ëóKÃ?uÙóß´'‚|m©O©j_.eÖ4)oeyäžÃZPÖ1nbY’+† Ç8Pá„ר•Ï„lý-¯;ðn |(×?áš4‡ß5­Z]kTÓÒ}Nð_M£ÅÑóÝãq‘2|¾cŽ y÷ìOq«ÇeñgÚ–±¨kVþñÖ±¥YI©]Ky:ZZŽ$2JYŽçܓނçÛ•Å|@ñ'ˆ¼)᩵Ÿ øfçÅÚ„rD‹§ÚÍ ¼®®ÁYƒÎË>c“Ès_þøsâÚªßâwÅ_xóÄ.§§ëú¦áØ4ÍB[+]*=0ŠCeC»9%ËrWœî;‡Ö²ÄÏ|]ý<ã¯Içë7vòEu6Оt–Ò¼>i—°ÜNPÕ3©øñ¦ÇãßÃñãÛ"ãCQyudö·.’H’Z?–ùhþ\f½²¾ÿ‚|ÿɼž$×?ô¨×Ìf𧌵?ˆž+ønÿ êÚœZÕ·‡on¥“M¼“O[«ˆâxÙî#€ªHÄ¡] ³8®“ö(ø9áï|øuñ2ÏVÖ®5 WÃ6-%­Î§q>žŸi‚)Ê´v1FŒ QòŽ­ î}ÇE~+ØêßhCÇ<×4ˆzœ«¬j:w‡äðÆ¡ Ž›¥Ea!†"±ágŸr“+8ç8<×­øÃÇŸ´³ð‹àgÂ/ˆ—7þ ñÄ=léýô _6;ŠIeŽ[˜Ê32‘’ŠÓä1÷ž±ñoLÑþ3xwàĶIâ-.ûTŽéY|˜ã±xÑ‘;‹1Ž85â>:ý¶<ð÷Æ«à=wÁž/“R¸ºšÒÌÁ¤‡ŠúK|o6¬fS*€Àä„q^#ៅ ð‡öíðG‡ô¯jšÎ…uá-^âÎßV»{Ùl¤óaIÒ9dËùRmG I÷‘Ô×¢~ÓÿòsŸ³/ý…µ¯ýoG* ³ìÿx’/øcLñE½Þ™§N¶×ñyP‡þ£ÉØã¸ÍtuðÏíÃwâå³øK¡ø7ÄWž¼×üq¥éÍye!I.Eä}×ᶸ*H¹ßø^ûàO탤|4ð÷‰µ­gÃ~4ð½æ£qm¬ßÉ~ÑßXΫç#Êr Šß7¾yÆ®^£¹úE~gøã>³ðáÇíà¯êw:޵ðÎökK©^{™ìuÀ[KUy 3”™Œ@{/÷…nxçMÒ>þÌ üñƒÅž,›ÄZÍ·›‡n¥}kYÔ®I&±WÉvd›ÈùQ!¶ÑÊÇè¥ùað Ä*ðíO¤|2Ó,‹©ê÷úÌzŽ5m:Ò]JêKË„µ·†ÔG–RX’qÓ$žæ‹hÚ4Wã·Äk x¾÷â¾ÉñCÅZž•y©2xOÔ¾§\ÀÌæ E–XÒKkrÙQòœk½ø™ñâWÄo€ß³&·eâ[¿øƒÇZ΃ýþžÞK»‹lÒycäa’\#¹ÀÆ)ò1ú™\+|HðŠ|Iá#]¿ü$óirk+oäɰØÅ2[´žnß/"GQ·vîsŒWÅ¿ü5¨| ý±ìþø{ĺֳáøVmJ{mbþKöŽþÚà¡O˜Uù½K6x Í®ÿfÿÉûkZø ëþ&“ø"ïR7\»‚UÔmãòÖã˜!Ã’bÎÝÀÔr…ÏÕZ+ã_Ú£Â?ô¿ÙëOðÁ9u›ÇÒî4ø5at[[¸ÑíÆ.7$— ¡rF]ù`H<_ì«ü ¸ñ¦·¥ü7ñ‹ì¼Ame·Pð¿Šî.|ØTHŸéb Ø|ŒÈç€*»†U´¸_[QE# (¢€ ;ÑEQEQERÑ@ EPE-%QEQEQK@ EPEPE´”QEQEQEQE|Ñû`ÿɹø·þÜ?ô¾Þ½3à·ü‘ߨKÿÒXëÌÿlù7OÿÛ‡þ—[צ|ÿ’;àOûéúKxPÿ‘œ¿ëÚÿÒ¤~[þI_ö?ý5Lôº)i+Ý?9 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢ŠkºF$ŒTI8¤šuàþ"ý¢þi±èþ’ãÅÚ´Îc[=?µ¸nGÌàˆ×ˆœsŒVïûHøþH…´V? ô½Ø¼‰©êN¸ê /’ ŽÄ†SÞ¼©ç®ãJó}£¯Þþóhõé䵬¥ZÔãÞNßrøŸÉ3Þµßx{ßoñ§m¦[ó‰.eH”• “€N5árþаxŽqcðƒÃZ‡$rPݤOg§G í%ÌêÇSÇ#¡5©¢~ξµ¿‹\ñkÞxÓX…‰K½jáî¶ ä*ÄH‹hì ˜¯vŠmãÛÆ±"ôU@Ï'Tòã*îÕ5åïKïz/¹•Ï‚£ð§Qùû±û—¼þøú8Åà?Ž< 7ÄOGá›I4ÏÆUÙ ÏÍy)gV8ÁØÆpFMw¾ø)ðÓáÚE'‡´XZþ2[í×*'¼f=[Îq¹Iîhö¯U¢µ¡”Ñ„•F¹¥ÞNïå}¾V2¯œWœ]4ùbúEY|í¿ÎáEW¤yaE´”´”PKEQE”´Q@ÿå2?¿ìL³ÿÑ ~¿×äÁ¿ùL—Çoû,ÿôFƒ_¯ô”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@rž;ÒïuÏx‡EÓPIw¨i×vð©!CI,,Š2pIž+«¢€>?ýƒ>øëà—ì©à¿†_´ñ¥øGmHÝ[,Ñ\ûF£sqï!g³ŠxcŒàò¯°(¢€ (¢€–Š(áOØ/à¯Ä‚ ø“£üJÓKºñŽ5mfÉVâ-ÔVɹ…Ü.ã|­†ä Šû®Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š(']Òι¢j(»žÀêòÛý¢ÖCÄ>jó"r’&r¬9_˜^³ÿ‚—~Ïš|ßmô-ã–m<ͧk×úÁ´Ô¼™\¸†ê@ò' ÞWî‰BãõNŠüûøû;|hÕ>:^þÕ¿µ5þšÞ2]=´DÑ˽ŽdÌÅÉ•Îd•Ã0<°Ø–9UôŠ()h¢€ üÿ‚Õɬø[þÇ;ý7j5úý_?ðZ¯ù5Ÿ Øçcÿ¦íF€?_ih¢€ 9¢Œ ÿÑýü¤¢–€Š( ¤¢Š(¢ŠZJ( Š)h¤¢Š(¥¢€ J( ŠZJZJ)h(¢–€ JZJ(¢ŠZJ( Š)h¤¢ŠøwöVÿ’ÅñïþÃËÿ¥Wõ÷|;û+Ébø÷ÿaåÿÒ«úûм.ÿt¬¿ô¹£x¯ÿ#ÊŸà£ÿ¦i‹IE-{§ç"WÍ_µ?þ#|RøsÂ߇ø¶·ñ]ýµ–·æ¤mg£–Ýtñ«»º@9ƒÁ&¾–¤¦˜Z‡¥xcCÓü7¡[­¦¥ÛÅkm ýØá…B"Œó€9¯8øíð«NøÕðŸÄ_5"}RÜý–â·¼ˆ‰-¦SØÇ*«{ŒƒÁ5ë”´€óOƒÒxþO†>_ŠvkcâÈ-IU™âÝ´ŠèH"M»Ç9°yÍzUPÊÚ§ìûã7]ñß¿ˆrøKHñmÄ——ö-§Å{²æpD²[Êîüç¡èp ¿~Îz?…eÕ¿fÍ6÷ãþ›ñÛOÕþÃ%¨Ýsaömëq7”Ðù‚_1v¥sò6võç‹>&øâXo16l+ž»§k×èªú¬,Õ»wé±Î³ìR’š–«™-#d§~ek[[¿N–ñÎÜ÷ÅywïÙqþøãÃÞ*Åm±ðÈÕ"³²û–VßQwu‹ÍóÛýQ󳿖¾´¥©ú•/wO‡oëäl¸›gí?‹~m·MvÓI5¥­}™¯?fÝ2OÚÃãÞ›«›7·V7:p·Ü·5»Ûy¢_0l;Yr<¶ÎÞ¼ðÝWà/‹4ë'ø9ã—ðd~#—í•”–j6Ïr~ôñ,Œ¾[7Rr{Å}5E/¨ÒÖÊ×wÝîZâ¬uãÍ;¨ÅA'µÊÒi¦žÍ¦×så]KödWø7¡ü)мQ-Ɖ©Eª®¥5ªÜ3ÜÇ#LO’$@.Ù±À;º×_ᇟ´oÙj~(øª+iŸíxÇö¦’WÚÈ¡NÏšú³ÂþÑ|áí?¾¶šn— Á`“„_Ry$žI<’I5½EU-:üÿSψ1xÄ£ˆÕï¢JîÖ»åJîÝ]ØWÇd ãů|O»×_J‡CC«iëoæÇ¬ÚÚ\­Ý¼¿š$Ý»)&àØÀÚ }‹I])ØñZ8O‰޼Iái´¯‡^*OkO$Lš‹éñêa[.Ÿg•ãS¼qß/Q_.üý™>6|#ñUö°>2Ūé:þ·q®kVð[Ào®nÎgÛ?Ú¤hCظ^Ë_nÒИXøg_ý“>!Øk^1Oƒ_çð?…üs-æ«¥¶—ùŽêå6\Kg;KBePÊ‘Ç Õ þxoáÃýá¿„REÒ´q&V +òYä€y–l2N½¢†ÂÇÉ ¿fÏü"øƒ©øâ,ÖŸõmRëV»ð½Æ™Æf»B!¾.²E}¬Bp»I$–>vcŸˆZu—оøKâíÖ‰ð×ÅwW×sé ¥Á-äGQ,óÃóI¹bwbX©+ÁbÇïŠZ|Ì,x—‚ö_>i_5=Dëv–éó],?f3G;9b#ß.ÇÀù›Ö¸Ÿ_¾&ü’ÃÃsüP“ľѬÞËNÑ®4‹xn @WÈ-~’D ¨ Ž˜¾¢¥¥p±ñ}×ìÏñGÁÞ%ñ§û?|Uoè¾)½“R¼Ò®ôx5h!¾Ÿý|ö­$‘˜Ä„cÁ²wt§ñïìÕ¨|CøYᯠkÞ9¾“ƾ¾M[MñQ·ŒÜG¨$Žë#Û‚¢Ãìòƒ•WæÊ澨¢Ÿ3 ü>ý˜¼}£|qÓ~>üMøšþ2׬ô«%­Æ—²Á3+F Xåo/aÞ_!Œ…ù6óÌ|Rý”~6|Iø™¤üFO«¦ ÞÝ]è6ßðÁ7öxº ¬†Oµ'ò¢ŒÈ½³“_xÒÑÌÂÇÍ^5ø âˆZ'ÂÛ_øÇíšÇÃÍ{N×n¯ÿ³Ö?íIl7O%& oæg¨.û¦º}kàçöÇÇß |rþ×òáѯ´ìÿ³îó¾Ù"Iæùþ`Û³n6ùg9Îá^ÛE+…Ž>2þÈ7Åïø·/ˆJƒK[Hõ]1-¼ØõxìnEÕºÉ •6lJ?EÀç¿ý ~]|eƒÂúdžüI/„5ð7ì»ã]'ã†ñ÷â/Ĺ¼aâ?MºÓf„é±ÙÛgÇ”¶É¤B±å²ÈÍ’W¯]Ñ~cü}ñ'Çí;þÏIþÏû>ß'ì’ó|ÿ0îÝœmò×Þ5í´Qp±çßf<7Wø3ý©ûAh?¿¶<¯ìMãGþÏû>ï7í™<ß?Ìvç|³ž»‡Jå¾.üñ¾!èß>xæoxÃIÓçÒšäXC©Û\XÏ ”Ç%¼ÍÈu6îÃŒ€kéª)\,x—‹üño\øw¡è>øt/ibÝ®µÄÒ¡–+ù#Œ¤ÆKF‰+ûUÈCŒd à>~ÎÞ)ðÇÅ;ÿß¼pÞ7ñ…Κ4‹w†Â-6ÒÒÈH%*±FÎ]˾HÀ$`ç5õm% QHbÒQEQK@ EPEPÒQK@ E´RQK@ EPEPE´”QEP)h¤¢Š(¢ŠZJ)h(úQK@%PͶü›Ÿ‹íÃÿK­ëÓ> Éð'ý€t¿ý%޼ÏöÁÿ“tñoý¸éu½zgÁoù#žÿ°—ÿ¤±×…ùËþ½¯ý*GèÕ¿ä’¥ÿa3ÿÓTÏK¢–’½Óó¢ŠZJ(¥ ¢ŠZ)(¢€ (¢€’ŠZJ+žñ/‹<3àí8êÞ)Ôíô»@p$¸ fôPycì5áÍñþ÷ųÍaðkÂþ,xC^ͶÃNÚpIY¦!˜ã¢íŽFG^NeB‹åœ½îËW÷+³¿ •×­xGÝîô_{²üO¤«Ì|cñ›á€¤kox‚ÞÅ`¿eˆ›‹­Äp 0‡qžÅ€õçqü2øÃãie›âoŽ$Ò,\†M7Ãè¡r9Wºu2°ê òPGJô¯ü"øsðð;xSD†ÖyH2\Iº{‡9Ï2ÊYúó€@Ï8®o¬âªÿ ‹¼·ÿÀSüÚô:þ­„¥üjŽo´6ÿÀšü¢ýO0þ5xè²|8ð@Ь¼Í£Pñ!xwFGßKDÄžàä¯ORÇìñ'ФŽïã‹u´rùËh§ì:zŸîùNñn§P~”RþÇŒõÄÉÏÉè¿ðe÷Ü?¶§OL,U?5¬¿ð'w÷[ÐÃÐ<1áÏ ÙgxcK¶Òm‰ÉŽÖ…Ié’ ŸsÍnRÒW­(®X«#Çœå&å'vQKTHRQERÑ@%PE-QE”´Q@Q@‡?¾ ^~Ä_ðQOþÓ^?ðΧ«øâ?‡"Ó…Þ™LñMVQ2#Ãu’Î<«È2’nROÊ=þWû,ÿЭã?üÓ¿ùc_¯ôPäü>¯öYÿ¡[Æø§òÆ“þUû,ÿЭã?üÓ¿ùc_¯ôPäü>«öYÿ¡[Æø§òÆø}_ì³ÿB·ŒÿðNÿå~¿Ñ@?ðú¿Ùgþ…oÿàÿËOø}Wì³ÿB·ŒÿðNÿå~¿Ñ@ðú¯Ùgþ…oÿàÿË?áõ²Ïý Þ3ÿÀ;ÿ–5úÿE~@ÿÃêÿeŸú¼gÿ€:wÿ,i?áõ_²Ïý Þ3ÿÀ;ÿ–5úÿE~@Ãê¿eŸú¼gÿ€:wÿ,hÿ‡ÕþË?ô+xÏÿtïþX×ëýùÿ«ý–èVñŸþéßü±¤ÿ‡Õ~Ë?ô+xÏÿtïþX×ëýùÿªý–èVñŸþéßü±£þWû,ÿЭã?üÓ¿ùc_¯ôPäü>¯öYÿ¡[Æø§òÆ“þUû,ÿЭã?üÓ¿ùc_¯ôPäü>«öYÿ¡[Æø§òÆø}_ì³ÿB·ŒÿðNÿå~¿Ñ@?ðú¿Ùgþ…oÿàÿËOø}Wì³ÿB·ŒÿðNÿå~¿Ñ@ðú¯Ùgþ…oÿàÿË?áõ²Ïý Þ3ÿÀ;ÿ–5úÿE~@ÿÃêÿeŸú¼gÿ€:wÿ,i?áõ_²Ïý Þ3ÿÀ;ÿ–5úÿE~@Ãê¿eŸú¼gÿ€:wÿ,hÿ‡ÕþË?ô+xÏÿtïþX×ëýùÿ«ý–èVñŸþéßü±¤ÿ‡Õ~Ë?ô+xÏÿtïþX×ëýùÿªý–èVñŸþéßü±£þWû,ÿЭã?üÓ¿ùc_¯ôPäü>¯öYÿ¡[Æø§òÆ“þUû,ÿЭã?üÓ¿ùc_¯ôPäü>«öYÿ¡[Æø§òÆø}_ì³ÿB·ŒÿðNÿå~¿Ñ@?ðú¿Ùgþ…oÿàÿËOø}Wì³ÿB·ŒÿðNÿå~¿Ñ@ðú¯Ùgþ…oÿàÿË?áõ²Ïý Þ3ÿÀ;ÿ–5úÿE~@ÿÃêÿeŸú¼gÿ€:wÿ,i?áõ_²Ïý Þ3ÿÀ;ÿ–5úÿE~@Ãê¿eŸú¼gÿ€:wÿ,hÿ‡ÕþË?ô+xÏÿtïþX×ëýùÿ«ý–èVñŸþéßü±¤ÿ‡Õ~Ë?ô+xÏÿtïþX×ëýùÿªý–èVñŸþéßü±£þWû,ÿЭã?üÓ¿ùc_¯ôPäü>¯öYÿ¡[Æø§òÆ“þUû,ÿЭã?üÓ¿ùc_¯ôPäü>«öYÿ¡[Æø§òÆø}_ì³ÿB·ŒÿðNÿå~¿Ñ@?ðú¿Ùgþ…oÿàÿËOø}Wì³ÿB·ŒÿðNÿå~¿Ñ@ðú¯Ùgþ…oÿàÿË?áõ²Ïý Þ3ÿÀ;ÿ–5úÿE~@ÿÃêÿeŸú¼gÿ€:wÿ,i?áõ_²Ïý Þ3ÿÀ;ÿ–5úÿE~@Ãê¿eŸú¼gÿ€:wÿ,kæÚ“ö¦ð÷ü§Aðoìëû;ø/ÄͪÚêw·wÖ–Ë ¥¬V÷í#y7R¨Qö‚å¤xÔlÚ ,1ýQ@Q@…%/á@ÿÒýû¢–’€ (¢€ )i(¢Š(ÅQ@Q@RÐQÞŠZJ(¥ ¢Š(¢–ŠJ(¥ ¢–’€ (¢€ QKI@Q@´”ðïì­ÿ%‹ãßý‡—ÿJ¯ëëü@ðwÄ+Kûïêqêqiw“é÷[Ñ »¶m²ÂèáYY_'~ÊßòXþ=ÿØyôªþ /ÀÛ¡”}ŸÁÿb ­Ò(X{Gß5î¤~r}ÍEŽËrTd“Àu9¤3ŽÕ¾ ø3Cñ†‹à SUŠx‰'–ÂË Òϲî•ÀPBªŽrØÁÁàψ> ø…¥qàíR=It{Ù´ëÀÑ »·8–'GU`ÊNò_ìÏn>1|UñßíU¨ƒ5ôá¿ îèš=ƒŸ:á3Ð]\eþêŸïmÔ‘|ý¯âº`-¼!ñÖ…ÏH ñ-Šâ2{µÂvú´¸õ5\¢¹÷EŠZ‘‰E~~þÕ:¯ÅO…ö÷>#ðÇÅmMu]­—†¼-g¥ØÎ÷7²© Èêdò”üòÈsµsŒ’ úoõˆ~ýœ4{ˆÿl|â{dµLje´Šæ=ÌIš( ;Q¤lìB£¶à;UrŠçÖµçþøáˆwþ%Ó|/t÷ԥҵѴb;¸~ú)`7ýá‘_üý¡¼KmûExgá^>ºø™áŸXÞÈ—:Ž’tË» »(žã(þ\KxÿFÑìõoøWÄQ_êÚ̱Zj²h³ÍkqÍ” #¯˜ ñè:íŸ ¿hxËâN“à|7¾øgñZJtë}RÒ,M®fŽÊò!†P«’¸L…èqG.sëi¼Oá»ÛxJ}VÖ=rò . °i]Ko’T„ìˆÌ0€O5¹_—0øeñUÿlÿ i ñ^ù5ß ë76×ÿÙ¶[í-–êÖ«ÝŽ¬Xì7 ¼u5õwÆKí{áßÁ;_ퟋ0øBòÎKx¯ñN™yqÖ¥¤¶—{eyf ˜WòãY¡’0ßÂH8Á;²“} fXä"Dh&#fÆåI<|Ë—¨î}•ñ³ãeÁ{?É6…}â+ïjqéV6v!,—2£:ŒÌè ¤rkÕ¼=©Ýë:Ž­§M¤\ÝÂ’ÉgpPÍnÌ2cs2n^‡kèkäÚÛþG€ö?iÿú*Zú¯ÆF¹¯xn÷IðÞ·'‡5+„ üPÇpðA,#—älŽ9õ£ >ø“á/ˆ¯¯Çá[§¹o j·Z5öèÞ?.öͶ̃x€?Ä2jóO|Oücð÷Â]oÂ:¤x¦èÙéºÎ`67%©º(FÀ¥Nä0ã#šá¿døÏĺgÄ+Æúªkw¾ñ–­£%âÚÃhÓÅdË’DUw¹˜œœœdÖí#ÿ%çöpÿ±—QÿÓ\ôí­‚çÙô´RT ­{{i§YÜjò¬Ö±´²Èçj¤h 31ì“Yžñ.‰âý×Ä~ºšmê–†`¬ÕX© 8VÆAê+Â?h­Nû[²Ðþ ø~f‹TñÝÏ‘3§-o¦Á‡»˜ÿÀp£=I#¦k¶ñ|<#¤hz'ÁÝLº³³ˆÁ$wó¼"¡TXD{OÍÆìçÐz×» ¢.…)9ZuµÚIEum÷wKÑ÷Fªž‹»=vŠùÿÃz÷ís¯XÁâh6ÚKÊÌ–÷r<ÉrŠN ö5ôyøì ¨IFR‹¿ò´ÿ"%wñ âÏÿ…6öWµ¸´XµxíÚUv:X ŠÝkOÁ_ü ñO“Uð.»g®ZÂÁ${YVO-ÈÈWPr„ŽpÀùgö¦†)þ+|†tY#°e` ‘ˆ¸ Ôzþ‡£ü<ý²¼7ƒ¬£ÓÆÚ6«oªCj‚(¦ûY¢•Ñ@]ÁŽ7c'Š÷(äÔ'†„“~ÒQœº[ÜoKZû-ï¿CÆž:¢«%eÊœWž¶×ïg­kŸµGìýá­bóÃúçŒím5>V‚xZ9ËG"2’## úô[¯Šì| Ä»ývÚ×ÃB“Ç}3¢t“îmÜc¨‰àkæOÚJÒ¿á¢>±A‹CY2þíyˆ 9~9çÖºoڛᗋücáïê^ÒmµÖðnµ©.‡3$P_C Æ7™ã`«62p¦–W‚“Ã+¸ûDÛm«+9+l­v·nÊúì'‹®½®‰ò謟dû¾ÿ3Ó~~Ð_~,ê3èÿüK«}n†VƒËš |°@.©:FYA#%rMñãß„÷éßünðoŒüy„|Kà‹Ïxò7k{}JÖ5im—bÚܧß@@Êü½3·Šä?dý'Nñ&«ñKâ^¹m߈5/êV <È­,VvL±Cn ûªÉ¯è0ªåiº•*ÂQ„RÓ™6Ü•¤•­£w³ìÆÔšŒa$Ûo[;+y^÷ò¹ôÄþÍàAñ9ÆÇõÇÁ­cC¶K[ŸZkZué‰BùÖöð$±o „bp{d›:JIËšjrŽÖJÑé«vz«[M§«µ¥¢âžûÊÛy+£ê¯øÏÂþ?ðõ·Šü¨Gªi7›¼«ˆ²¶1Vá€# WŸÝ~Ðßl|!®¼Qo‡î.ÚÆ;Â’ùor€–p™8 yÆ8ë_"ø{Å‹ð7á¯íàE&ðmíÍÞ—ü¥muÔ-h±ÿ»)eô‡ñ‡À÷Ÿ ÿg¯^°ÓÓRÕ4ßéÉhòÒæþa,óFd`ÁUæv]Ää`b»(ðÕ_’R|²’QÕ+Ç—šíÚ׳–¬Æ¦kQSæI]+½÷½¿F}£àÏÚ/à§Ä=~ ø3ÅVú¦«p®ñÁL¬ËÜÇ.Š8õ¬¤ýªg“«I¡Éã›/"• uœ¼(²#mei$EA‚1Ëb«ü8×þ*ê^)ŽÛÅÿ,|§yr1Ô`Ö-¯]\•QAý3»Šüõð÷Åøgà?Ä øÀ·ú캆¹­Ã lQ´Ø¥¹œ¤M-ãœE属ªqŽj°\9B¼çÆNܪÊp—ÄÝÝÒ¶–ÛGæ,FiRœSmuÞ2[[£wùŸ¬Z—ü¤x‡D𮥪E©â5‘´è›v.„K½ü·a!yÆì‘Ј¬?ÙŠ?ürñM×í=ñ Ùm¥Ž¢èV!·%ª[ü—³F–pê3È%væÿcὌ몗Œ.Ÿw+¾KyIjûrËÈêúí^xÓåÖVkÉ}«ù¯ÕyŸuÑKI_0zÁE´”RÒPEPE-%QERÒPE´”RÒPGz)h(¥¢€Š)h(¢Š(¥¤ Š( Š( Š)h(ö¢Š(¥¢€Š( ŠZJù£öÁÿ“sñoý¸éu½zgÁoù#¾ÿ°—ÿ¤±×š~Ø?ònž-ÿ·ý.·¯Kø-ÿ$sÀŸöÒÿô–:ð¡ÿ#9×µÿ¥Hý·ü’4¿ì&új™étt¢Š÷OÎBŠ)h(¢–€Š( ŠZòümømà+–Òõ­YfÕB’¶Š×7L{/—;Xç€å}zXb1TèÇž¬’^zl-ZÒä¥'Ù+ž¯U¯/,ôûYooçŽÚÚ/$²°DEK3÷5ó“øãã¿Äaðÿ‘øOL¸Œ˜µ}€¸ÏªÙÄ]”úo ½ýªÄ³µ‡ˆ]oþ.øRñµÛ/³M8:ò¦;k}q“Õˆ9Uó•ü‹º÷íà[yæÒ< ׎u¸‡š4/r£=§Pb êÊ[••-‡íñŠúîÃá¶—p‚ÚçPÕ=Q¤ÊDœs¹0Ã#¾kè=DÑü;§Å¤h6PéöP $0"Çý@÷=ëN›ËëUÿx«§hû«ïøŸÞ½ûF…/÷jJýåï?»H¯¹úžáßÙßᶨ>»­ÚÉâ½f`<ËÝiþÛ#0îÇ–¼ôÂät½É#UŽ5¨ @:ŠîÃ`éQV¥½?^矊ÇV®ù«M¿^ž‚Š(®“”(¢Š1E-%QK@ E´”QERÑ@ E´”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQÍ`PÿÓýû¢ŠZJ(¢€ (¢€ (¥ ’Š(¢ŠZ)(¢€ (¥ ¢ŠZJ(¢€ (¥ ¢Š(i(¢€ (¢€’Š(¢ŠZ)(¢€>ý•¿ä±|{ÿ°òÿéUýzïíCðžçâÿÁýSDÑ“â]!ãÕô+÷àÕ,I’¤r7üÑ6?…Î9¯"ý•¿ä±|{ÿ°òÿéUý}Å^ ?öHúËÿK‘ú7Šÿò<©þ ?úf™ð¿Åÿ|\ø™û2øoCð†u='Æ_¶‹~²ÚMÑRã÷zŒ÷?(1FŠ®ÎÜ«C¹úóÀ~ Ðþø3Eð'†âòtÍ Ò+H%b]»›˜å˜÷$×YK^ëgç¾^ý­î~!ß|,?>Ø\ÜëÞ>»‡Bû\1;ŧZ]·Wsº#D‡pÜH<ü§pýCE Œä<àá·‚tOxj/'LÐm"´€`V%ÁfÀsœ³rI5åÿ´çÂ9>3üÕü1¦7ÙüC`STÑ.AÃ[ê–DÉnêG#qÌlG;]±Í{ý_¨Xó_ƒ¾.×¼wðÃÃ~*ñV‘s ëWÖˆo¬o!ky ºO’e1¸Rõ%N0T‚8"½*Š)ùó¬|ý«cý¢µßú}·‚üF‘ÆÖM[PÔam+O?dPÙ:,óÿËY7±Á*Þ+Ñ~8ü!øÃñoáï€õHßB²ø…àjÓ_6m$óh·3ÛV€Êñ ¶|«˜²GØT•\±ð^‘ð_ö‘ñí!ð÷ãÏÅKŸiú…-µ+gÒt¹î¥xVîÖH‘ÒI`Q,$ƒÌ¢¢¨Ú\æ¹íà·íqð£Æÿ3|'ø«¨üZð—ÆïƒwZ;kžÓ¯ô›‹q®"µ¹µ½häܳ[$®Ž#ä ç­e|røEñƒâ¿~ë–­¡ÚüBð6±m®µ“É<Ú%Ìñ$‘´"VˆL”sAr>À¥¥p±ð‡þ ~Ѿ(ý¤¼ñïâµ×‡ll<9a¨Y'JžêVn¡tFY%D®Îù|” ¾½ãOÂoŠz¯Åo|kø9u£¶»á›CKžÇ\kˆ­.moŒo‘5²JèèÑä|„ö¯ª¨§Ì>Xø»ð{â/Æ?†¾¹¿½Ó<;ñ3•¾¹e5©šëJKû}èbc"G3A$nUŽÀÀòÆ—€nkMGÆ{|LÓ|!¢øf×Íû`Òî¯//.˜ÄÂ?'Ί$‰…X–Ë 㜥©)\,|[ûcZø–>x³ÃþÕ¼S…|]i©ÞÚèÖyv-¡ŠM̱‚RÜÊ2z׿kž!ø“¯|9²ñ'­Ö×Ä‚[Ζ)iìIƒ"\ d¸t™@#w÷¯T¥¢ácàïÙóáíUð«Äþ!>$³ðdúŒüK}â NKMGQ{»s¨6ù#¶I,ãömÞÃ#©§|eÔ¼Kã?Úá‡toø‡ì>Öî//µ·ÓÏö3Cu¥ÈªbºVl•wÛ•@`@'¿ÝÔ´ùµ¸XJ)i*F|Ùð·Ãú߉þ'ø·ã‹lf±;ÿ±´K{˜ÌrGanróa&’½?‹³ ôÿ|(ðű_éÚcMó>Ïûé¡Ùçmßþ©Ó9ؽsŒq^‰K^¶'9­*ëI¸8¤£ÊÚ²JÖO}·ïwÜÑÔwº ø_Z²ñ…áÑm¨iÒ  —íWO²Eèv¼¬§„^çKI\¸ÌÈ’–"£›]Û™2›{³æÚ#áOÄoˆçÃï|7“Jþ ÔäÔ=Ziá†\„Ø Á¬ySŸ»ìj_|ñüßÏÆoŒÚÅŽ£¯ZX¶¦ØéqÈ–60JÛ¥pÒüòHç‚ÅG†>𥮏çu£Aaãk$Õí­›»Wóÿ€p<7QÔw½ÓßK­™ñÅ/†?´÷~)xcÇ:xF+?Þ^Í¥¤÷wë,ñ]ª&.BÛ0 Â62O&½ÓÅ1þÐáóÂáøüSnCj¶·MptùÁB ™SÍ\6 ³'#‚{Uêç3œiÆP¡¢Ó¦®Ï^í¿P†1rjOÞß_ë±òo…þ|cñ‡ÅíâÏÆOì])<%iwo¦ØhòMpÏ%è ,“Ë2'TmUÏ>•I¾üløSã_xƒàk躮㠳©\ézÌ“Û5­ü€,²Á$(á–LÁ°xÍ}E_öõ^o†<¶åå·»kß×}o{“ý nï{ÞúÞÖü•~#|ø™ñCំd×5]2Ëâg…u$Öm.­’_ìÑs¬ÑÀC3ÊòÊ+>ÝÛ“vܵƒþü\ñOÅÝ+âïÆÙ4‹7ðઓ¦èòO|3ƒáÿo­594W–×Nº¶’GgÓ‡ü{‰„‘¦Ù#S³ ¸ ó’Söqøgâ„ ¬|ây­®5 k½Bv{Gy!)uw,èH‘¶B¸ òðs‚G5îtTb3:µUE+{òRv]UÿÍ•K rÛì«/M?È(¢ŠóŽ¢Š(¢–’€ (¥ ’ŠZJ)i(¢Š(¢Š(ëE´”QERÒPGÒŠ(i(¢€ (¥ ’Š(¢Š(¢ŠZJ(¢€’ŠZJ(¢€ )i(æÛþMÓÅ¿öáÿ¥Öõ響äŽxþÀ:_þ’Ç^gû`ÿɺx·þÜ?ôºÞ½3à·ü‘ÏØKÿÒXë‡üŒåÿ^×þ•#ôjßòIRÿ°™ÿéªg¦RQE{§ç!E^òòÏOµ–ûPž;kh´’ÊÁu,Í€¹¤ÚJìi_DY¤¯Ÿ/ÿhÏ ]]+áÞ¨xçP,cÆ—6ÑÉη2mDS‚w Ãô¬äÑh¯ùrx‡W´øy¦>å–ÓM y¨2Æå·Gz4g޾ÕåK8§'j ÔÝÛÿÒ?Ï^9-X®lCT×÷·ÿÀUåø[ÌöŸxïÁ¾µû_‹µ«]% –A<ª ^¾\yÞçÙA5âƒã¿‰…±è+¿¢º0ù] röŠ7—w«ûÞÞ‹CŸ›×©fåhöZ/¹oêîŠ(¯@óBŠ)h(¢Š(¥¤ Š)h(¢Š(¢–€ J( Š( ¤¢–€Š)h¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€ ? J_€?ÿÔýû¢Š(÷¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(áßÙ[þKÇ¿û/þ•_×ÜUðïì­ÿ%ãßý‡—ÿJ¯ëî*ð¸oýÒ>²ÿÒ¤~â¿ü*‚þ™¦QE{§ç!EPSL³»¶Óîîá‚ê÷w‘ È«$»-å© ¶ÐFp3I§êº^¯ Üi7ÞÅ´LÐH²*ºýå%I‡qÔWÄ¿ ÿ…çûJxËãýÙóü9à_7Â~cy£9Ôï¶K“ ¸û˸íZ‚¼¿€ßµ¦½ðíóoáOŒ‘˯içË‹\¶ñ0·^ÀΟ¿ÔéUÊ+ŸsQE#9i|sà˜/ßK›Ä|w±¹ k¸Dªã‚¥ nzc5Ô×âW†ö?“â_ퟴ¬šBêGÅš€²në¨2û¾È!>q;ºyc9¯¨ÿfÏx÷áÏìwãíb=g¬^èñj›„ϧ[É#Y,ØÃ`Æþ cŒU¸¤~‰V&¹â_xb+[jVúlW·Ú@×,b[‰²#‰ —l rkä_„??jÏŠºgƒþ$xoHðv¶–r\ÚOqtuG¶•TMuaPNç†6$´{w0cŠ£ûXø£Æ~Ö|s­xwÃÞ#ðE÷Št;;hï~Òu mFiH~èÙÉ9<Š\ºØw>㢾mý¨þ3ø›àg€´¿øKG‹^Ô/õ½7K[)\Çç Ù|²¨ã…v襲9x£×õ{=1§Æ.§Žáz•à dgù·âóûJÃgøJ8¥ðÇöÑð¾²lAûoÙ>Â. ßç¿çgfÝ¿.7{WÚßÁ|YÑô+¯žÑüGâ2FtÞÚ'”ƒ ƒÌ!¶ÕûÜñM«ÏC¶øà+ˈ¬ìüK¦O<Ì#Žòwf8T9$“Ð ƒÇèVQ¾£®H$ q¨\ ’+KAÀÌJÀÎÌ2­”Æy®fçãOÆÏŠ|_à_€z^k¤øálu_]k‚—žÞÞ(?»ä;èG ãYuNgiew~ž¾~[›}f6ºÖçÙôWÈÿh|øKáÍOÆš6š~!øšý4‹kï ibéÝÿ~÷RRÝbA# ÁpHä¾þÒ¾0OŒšÂ‰—Þ×?á/¶¹—LÔ|/xóÇÍ¢¤¶¹ŽBÅwD¬c˜Œ`ÅG,ªàæ–Šÿ;o`x¨)(¿êçÜ´WçÖ‰ñãö£ø—¬üI´ø]áŸýáæ±w`²_Ér$ÔŒ -¡Xò]¹,ìBåã}â;mGâÇŽ¾8~Ìÿ>A¦XG¬iWï©Ûê¦fhV(¤ŽT¡êáÕ¶–‚@æªY]HµÌÖéoµÕÕýQ+³þ•¬x‹BðûY.¹¨A`u+„´¶ȱù×V$ÜFç`rq_~Äð¹Ûà×Ã××ßCo ²ùiþÓû£ÊóKþë¦wcÛ{ö‡ñ/ü9ñáâø‡@ðþ»àíOÅzežœgûIÔ¬ïd‰ÏÚ@‹rb@œžÈëGö}«º<×µÿÔ>³ûµ;jÑ_;þÑŸµOƒú‡ìü%¥G­ø·Æz´.i<†(>Ñ>I–g"49%‡l‘ÇøâÿÅÍã>ŸðK㦤ý¯ÄzlúŽ©è7Ùæ6lż±Ïó+ `Àƒ‚=Î0ÀÔ•?h¶×Ö˸¹b"¥Ê}qE~xÇûBþÓ2µø¡ªü:ðÓ>jÚ”“j\ù·ëd‹1†ãY3–vm¤º*Ž©üñRëâ'À;âï‡4ÆkíWF:„6t§íB"LoÌÿ½-è ÅU|º¥4œ­½·Úúê*x˜ÉÙÏExìÕñ âoÄï†ø§âç…¤ðˆêâ&°–Þ{fF@Gòî›'ž‡µ{õsV¤éÍÂ[£ZsRŠ’™Tb$“ÐW5£x×Á¾#¼ŸOðö½aª][g͆Öê)¤ÊŒJþ"¾:ý·õ½[R¶øiðGN¾›M´øŸâKm3SšÙÌs6šŒŸh‰\€û×pÏÌS•b¼ø7öjøðóÄ:oŠü àë-VÒ¡’Þ+›5h¤x¥]®& þø‘šMÌCÖº¾­ÑU&Ýå{Yvï¯s/k'7­·=ÊŠüãÔ¿kˆ¾-¿ñ~³ðºãÁÚo‡¼%yueoˆ5‡QÖ$±È˜Ã!F`V"à–=€æºO~ØºÙø%ð»âßÂßŪ]|BÖ-ô£¦ÝÈÊÉ;´Ë H¼nÆQ\Œ†ÛÎ+W“WºVßMöv¾¤}vžºì}ñE|‡àÏŒ?|7ñ£Iø5ñÛLÑÖ_i÷7ú6£¢<æ’̯Ú-¥K€r+© 8 Žä…ádøûûCxøøëÆ|=¡Kàßß^X"êrÜ í^M7þ>ŒPÙäì–=qP²Êî­£½ô×Eø•õ¨Û©÷µò/‰ÿk=Kýœ<=ñã@ÑæÔîü\m,ô½$1&§tÍٙœyn’Øù¶`r»‡w_µ*ø–Ãþ†A½ŠF¸þÉ–ä]XÌtkûá²e-ò³.<€G5”°5#)YZëWÕob–"-¤µÿ‚{†‰â=Ä©w/‡õ5°¹–ÎàÁ Esm–'Ûœ:O ÓµèHä×µ;]5f$!¹™!GP¥ÈÉúWåwÂ/þОðçÆÝoáN¢M£xgƾ'¾¸mViüûæK§’XmÒ "ØåŽ0ÍiþÓÞ=øqñÏödø‘ñÞ<â;¶Ôoà¼S,QÁsb²yr„¶Ö`äW¡ýŒý·%ô×m^×ÛÌçú÷¹ÍmàØýGÓu]3XµºEä7ÖäàI‹*;nRE_¯Ë/Ù¶_~Öúγû+Å$ £ÐE¾ºð¬ñé¬y¬Ñ¬ >>uB‡å øù[ž¶ïö¼ø…ãK¿kÿ .¼§xkÂ×w6vÐëú‹C¨ëgŸ5áJˆcsÄEÁ-Üq\ž|ü°ÚÉ륯ÑùÿÕlyo/Ã[Ÿ¤WÇzßís¤Cû8xgã—…ôIu]KÆrÛiÚN‘æy5[‰ZÜÛ´€#•KómÙçÃ˯ڎ/Ù'ÅK/ M ÞÃ!´‰nVæÊ`»‘Hœm™ü¥—in2k’X ‘‹”ì­u«ê·6Xˆ·e¯ü芯wyi§ÛK{2[[ I$¬GRÌÄ=Í|«ðÇöŒ¼Õ>|HñÄë[m'\ø[}©ÚjÐÛ–òLvHeŠdßóš>WžG#­|Ýñ'ÄÞ+øó¥þÍ~øŸhš]·Å FãUÖtÛG–8å³²¯ µ“» …<Á‘ódŒ`cjY\ÜÜg¢[¿“zwÑ~DO[ÇWÿÇé^‰â/øšÌêÔíukPÅ ¶“¤ñîWtdŒLÖÅ~sXxOÃÿÿn_ xcá…„Z†¾#øzõu 2Ôí>Õ§ %Žu…~E|(P@èÏýã^áð¿ãψ|sð¿âgŽu >Ú ¿ëömeü¹cÒT4m&Nw6~l¥*ù{IN›¼ZOïm~i…üBñn‚×_Ú^0ÒWSÒlÜ5Á‚ya¹¶ C.ÆDBAbÀg½{§ì÷§ü¿øǾxƒáæ³§ZÏq««èÙÐ(;ax’o9ÿxë)Â㕠ϼò¾J.s½ÕÖ–¶Ÿ2/šj1ÛOÄûǵùëoûG~Ñ<;ã‹¿¼1¡ÍàO Ý^Coo¨Kr5=^;þ>%„ƾ\yìV–s‘“ìÞ8ý¢¾Çû)]þÒ³ŽrÚD:•µµÙo,4®ŠÉ!]¤ì,FF3Œ×4òʱj.×nÛìßFiToçò>¤¢¾GðŽ¿j/ÇúLJ|=£ø#V€Ý¸¹mfÞÖHZHe“äùŒvoˆ¨8ÎàE|íá?Úö¨ñœ¾‹Kð÷… _Š0êÙ/,·x³m)›Ì–èw ÄLòFâ9ÅÃ)©+Ù­7××ôLRÆEZéëåýw?Ph¯Ï›ÏÛ?Wƒà×ív-/N‡Ç¿ï.tèmïn®•k.Ÿ!ŽòæiÛ$CÛ…ûĸ85×|$ý¥ËÔõ-?FÓ®õZâ;;ž{‰å`‘ÅJYÝØðTIà M/SÓµ­:ÛXÒ.c¼²½f‚h˜&üHð§‡4tøe5–·¤ÃóN5{‹4Ilç¿B Â'ÜÞYäª0ÎB–û[öhÿ“zøoÿbþ›ÿ¤éF'.t©sÉë{[¶|ÇK§+-­sÛë–ñgŽ<à;ÕƒÒ~0|,×¼S/‚4_é·Þ äì!¹îáϘ `äÁÏ¥z=|EñCHÒ´ßÛ7àtúu”6²Ýéþ*yš(ÕVZà¹P O'Ô×¢þÔ_5¯þÐ/<7egs«x£Y¶Ñm¦Ô¦h4ûW¸Ws=ÌŠ ¡ÀÆsœàt– šTãKí+ëê×èJ¯e'>ü¿Ìúb–¼GáEÿÆé¥¿?åðåþå#ÙÞè/qó>O˜’G6@`«+œó+ãÛÛâOŠ´¿|Eøy'ƒ¬¼+ Ov–šv±©4zέ ‰+,‘Æ„,>aVòUfãŒJ¥–ÔœšƒNÖÖúk°O¤åÔý/£ñ¯<øMñ#Fø½ðßÃß´h¬µûT¸XÜ‚Ñ?+$LGÆá‘ÔŠô&©¢¸§8ËttFI«£‡o‰¿—A“Å â]8hðÜýîÍÌ~BÜîÙå7m¸ãnsž+¹÷ù—uûüV›ö_Ö> %æ5ëÿ¶½›‰¾Ê- âÜgò7‰6»°Œÿzý3Q…ÐW^2…(wÕýÊÖ=LhÔœ¾%m„wHѤ‘‚ª‚I'Ô“PYÞÙêÑÞéóÇso(ÊIF2I¬à ñ_.|—þ¿¼Qð"ï÷vp»k:xSarß½Iÿž2ÎAnÊkLV£Vq~ôvïÙ¿•×Êï¡Õ]6}IK^%ñ{â^»àÛ¿xOÁz|Z—‰¼YrÖö‹rÌ–ð¤KºY¥)–Ú‹Î`Ol¯|IñÄ_î¾|P³±V6+©Y]éÍ!·¹·ÞQIåe`s“ÛЂv§‘W–ë Ö³vº»Švm.ÉþO°Õ'k™?¶ü›Ÿ‹íÃÿK­ëÓ> Éð'ý€t¿ý%޼×ö¿Rÿ³·‹y$ØÿöõÃü5ø£ñ+Tøiá/ ü/ð-ÅÌún™§ZÜj:Ã-€1ÛÆ¬ñþdèqS8"¾Œ…ÊNWÖ É&Û÷¥Ñ¥RÃJ¿ Ó„ºÄM»´¬½=ugÙµã¾,øõð»Â7k¥Üë)©j’;F–:h7·&Eþ‘nÚÄð•çñ®=¾ xÓÆ§Ìø¿ã›½BÜ0uÓt€4û0ù’FPe•s Øç<à{„>ø+ÀV¯iàýÛJI1¼ÂŸ;ã¦÷9vǹ5è{l]_‚ ¼µø vÿɾGÅ{ âMÔ}£¢ÿÀš¿Ý™ãqx«öƒø“áoÚøMÈnµ¦iï]9ãµEzâBTö$TÚ_ìß _O©ñOZ¿ñî§ †Ukù]-#'´vŠÅOu$ƒŽ€q_GQBÉéÉób¨ÿ½·þ­Âàóº‘N8d©¯îïÿ;Ëñ·‘¥hÚFƒe™¡ØÁ§YÅ[F°Æ¹ë„@ü«FŠ+ÖŒRVG‘)6î÷ (¢™!EPEPEPEPEPEPE-%QEQEQEQEQE´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQÍ`PÿÕýü¤¥¢€Š( ¤¥¤ Š-%Q@RÐQEQô¢–€Š)h(¥¤ ¤¥¢€Š)h¤¢Š(¢–€ŠZJ(¢–€­RÐß²·ü–/ö_ý*¿¯¸«áßÙ[þKÇ¿û/þ•_×Üuápßû¤}eÿ¥ÈýÅùTÿý3LJ(¥¯tüäJùƒö¶ø¡­ü9øO.™àdñ¯®bðþHo¯Ï–$SÛÉBÒnè¾\×ÔÅŒ¬è¡Ê’ úJh3ø/ðÃIø5ð»ÃŸ 4bÑ!’P1çN~i¥9Éùä,Ü“Œâ¼«öºøa«üBøVÚïƒƾº‹Ä: «÷þ×bw´ úMdÁã~Âr+êJ(¾·8øCñ'Gø¿ðÏß´&Ó^³Žãhÿ–RýÙb?íG d ó‘^LŠ(¡@ Eá@žO¤¤3â_ÙÓà¶©áÿˆ¿|Cñ°Gˆ*Òu¿í8î´Äµ½·±äXVKµ”1/´oESÎ0Oè]- ëp±ñÆ›‰¿¾x2êËÀŸ‡õ=3ÇíÆ=“\Gccw²Ý‚âHŠÉÚ¿å鞣â‡Ãß뿵‡ÁoˆN–÷>𞼺•àxÂ[5Ý“ÅefÛÜ€6©Ç| úÞ’Ÿ0XøÛãg†þ%øsãÿ‚>;xÂSøÚÃIÑõ=þÂÎâÞÞñäËÑ‹§Š7ÆC⾨𞳩x‡Ãš~µ«è×>½¼ˆI.ŸvÑ=ųà‘ y#$°ì=뢢“acã¿ØsáϾ|o øÿI“FÕµõ"GŽFòf|Äî¸aïŸQX_´j/Ã_?ÿhk”#Eµ–ãÂÚÔÃ¥½¶®Wì·{G€äàÙë€~࢟6· ´TŒüÙøq?Çÿ~/Ü[üÕüU§xÃÅZ¥Ýö 5»U%g¸Wù†Èu¯nø•⯾<øQ§è^ðE焼Oã‰4û¹ï®-%]ȶÙnä1JÂGhÉòR=Ç'-Œ`ýoEzU3)ªŽšº·~ŠÛ^Ç4pÖ‹Š“·Èøà¯Áÿ~Ì?ÇÃïY]x‡á?‰ [Ÿ´I4Mq£êÑF©3Ê‘¤†ì(oÆÐµ‹¢iß¿f¿Š?n´o_|Að_µC®ÚM£Íl—vW³ Yàž™bÊ6c©8 “÷ˆ_¾©hyœ¤ÛœSº³ß[ußpXT’Qv¶ÞGÃ~|DøÝðçÀ?—ÁÖñx»Áººká}Bâ)£º¶’K7˜'Ìxö?9@À¦OÞ­…2ǪxûL³£ø ÞÙfyõ›äÓ!{ilmÖÕ¥’Bäí, €¤œž•ö]-Oöƒö~Ï—Mm«Òÿ=~w«.nkþGåÁOˆß|­üt‹À.üqg¨x×USY\[D-õE†Év³É›º8#¥}iðàfµðÏö\±ø7«ÍÖ¥Ó/"¹tmÑGs|$%Cwï Hë‚GZöü3ð¯Ã_øH¿áŠX¿á(Õ®5»ß6V“uåÊGŒ»¾ê•‰p£ø×[cs.vÕ5etüÝ•—ë±0¼ªò}ÿã¯ÙNïâ¿‚¼ែ?~_èÿð‹éæÑµÅº³ŸM¸û1ÛEI¾ÐEÁÁ‹ƒ“Ò¸/Ú[QøÃãoøGLð¯Â-sQ³ð7Šìõ†¾[­1!¾¶´Iý^ídüÁ·ÌUèsŠý¥¬£˜Z³­È®ýzüËx{Ã’ÿ‘ð¯Æÿ|Tø¹áO|Zð¿‚.ôŸü:ñz¢ø{S¸µ[‹ËXþYQ&‚Ya à)L¸àpqSx_Hø¡ñŸöŽð¿Åÿx&óÀ~ð•mg§5»ÞÞßj[RFÛÉ"¤Q¢pKd“Ӿ㥡fMC•EuK}ÝoùƒÃ.k·Ûð>4øAðÛÇ>ðÇ '[Òd´»ñGˆüA{¦FÒDÆæÞîÖ$‚E*ä(vRbÇ WSû?x[âïÙOÃÞ»Ó ŸŒ4}áRÎFÊ݃#DŒUÌg$¯ñãžµõ%E\|¦šijÓû•‡ :¬úXùÿöiÖ>9k¿ !¿ý¡´Èô›«…x"X•E¸#ÊlC$‹ÈÏñf¾€¢–¹«Ôç›’I_¢ØÚ²J÷>Uý«~ø·âo†¼5â_‡ã/‡úŶ»¥ÃrvÃröç/nÍÑwáH'Œ®$lü9øÁñkÆÞ*³Ð¼Að‡Tðvž°È÷ú†£yhñG2¨ÙºÁ$0fþ"Ï^+é ZÝbÿt©Ê)ÚözéŸægì}îdì~^øKáv«ðçÄ~ ×þŠuÆ­y£ë:|zt²½½ì†U·º[É#’7„¡†TŽ˜©j1â;á§À‰ü?àËO øŒxãKš×@Žh¾Ï ãI#Å M¬c{í.À`=qšý<¯;ñ÷Âï |K¸ðÕÏŠ¡–Wðž­m­X¥höÞZÑ3íûÊU<拏ެjTŽÛï®–Úö9çƒ÷bÏ—t 3â§ÆÏÚ/Áÿ|[à[ÏøoáÖ¨ÅzœöòÝÞßê~ZHm¤‘DQ¬JCóñÈÇŸø{OøóðDø‡ð‹Ã? /|_ø¥ã_Yhž øE«x>ÌE#ßßê7voRªec·I#ϹøÝµ^O?-}IYUǺ‘j¤Swnúé}ö*u¹]¶üŠþü2ñ߇¾|hðþµ¤Ik¨x—Ä^+¼Ó¡/›{÷slêUÈQ #ˆ#¸æ6?~$·‡?dý6ûÃnçÀF?øH"w…"#æä?ÏÇî÷óí_¤t•¢Í&›•–¿ä×êOÕ#d¿­î|câ…>+ømûMøsâ×Â=Îðç‹ m#ŶV†(#ˆÇóZê>[2e?+ì°8{ð_|(Õ¿gã¯x^øÿ CL:ÕÞ‹¬ØG§K#Úܶô·ºo‘¼]3ó/¡éŸÔŠ)Ã5š,•ôK¯M¶}/oAK ÝW>&ø»ð“Æÿ~x2óÀ~±ðŒ|«Xx–Ç@2Fmcº´¦kS,A#Ë“¸°-‘žwW©|:ø½ñCÆþ$´Ñµß„z¿ƒ¬V)úûS»³h¢•WäŽÜ[É#M¹¸Ý„s׊úŠÆXÞhrJ)ïmô¿Ï󹢡i]3ó'ö‘øñ{Äß5 ;áÆ˜Òøââh°ø²í%‰>Æ4«^MŽá˜ËTmªw*í=kÞio†>4»—á¿Ä¿„šJjú×ÂýP]G¤ù©¼°šoqRIò«ùdm,q__QZÿjÔ÷4^îž·V×å§¡T½çøuÓç©ù¡ð§ÄúíûiÜø×ÄÚL¾Ÿán†ÖVÚ¥" RY¯÷ .ŒhJHT²;®|¾›ªž™áÿÚᎃñ“àdžþÝkßð™êúÞ¥£kqÝÚǧ,º`}£Ì‘eDùçã!~zý(MGV—_ŽÆÕ&…mÞìD¢áàF,±´˜ÜQY‰ N$ÍiV³Íµ÷`¹l•ô¶¾]o÷°zk-u×Ôüã½ø7ñkBøû=ø“þûŒ¾­œ×šÏrÏÚ[¸#”±ˆJ¡²¤¶ÒGZí<ΈŸ?i#ã§Œ¼wàOø/G¹°Ómu)`’úîòð•–B–òH©ÆÌ9l’ ÇÝ4VrÍfÓ¼UÝõ×D÷[ù¿¼¿ª+­tÓð>^ð‡|Y§þÖŸ¾ ßi¯‡µè¶v—…¬³Û<Æd ¸*rJ€sÁ5Kö±ðgŽ|E x/ÅŸtsâWÀž%°×š’$RÝÁøæHÚB>Ç$dÿú¶ŠÂ8é*‘©m’_r±£ œ\{Ÿú~‰ñ«âWí9gño\ø{yáO ÅàÝCI·—6¯sçË!uY£†WØÎIÚ#n7Iƒßþ'xroÙáõ½[Aàý+Äú¾éao²KxŒ VÛ!ݼž6n¾+ôbŠè–k+r¨¤­n½šïýã%ƒW»ÕÓýÌAð;âÏü3çÁ¿Â;7öφü}e«j6Þl­ìbÔææfó6#`ØR[ž™â¿NYC¬2ZuÍŠÆÊ·Äº·÷êkF‚†Þ_ù‡á-+ö‰øð߯_³ï‡þÝø±¯®õOøGµÛk›Xôñoª32½âË*ËBÎÎʪw}ÀF7TñßÀo蟰mßÀO [wÄ–º 6I,‰ç܉I4…îÁb2á_sÑ]3ͦ䦢“º“ßV»ëùw2Ž$Õú[ѽ…Ô^ ƒK1m¹M=a1ñÈvíôëÇ¥|%ð‹àßÄßÙÏûk@–×þÛ¦±ºX[ìmx® ¶C»~F6nÇ|Wè­-sQÆÊš”RßüšýMjPRi¾ŸæŸè~R/ì×ñ2×áÂïÏàè5í{áÖ·¯Oyá«Ù-ñ¦ê×,Íå»3Ãæª¤osŽNH#ôßÂ$·×x—Gð»øûÃ>Õ'¹Öü6’"5òJˆ°JfH¥6å\„cÎþ˜Íp¿>xúOÚºЧáZ|6ðPðµÞ›o µ˜œÜ5Õ¼›®cµv òm€nSæ`NÑú-K]´óIÆ—²²Ù®»?¾v0–9óÜùö‰ð—Ä›Š? þ5ü9ðññiðQÕmu .)â·¹–ÛTŽ%@Ó²ÆZ3*Ì3¸r0k¦ø•âoøƒán“y®ü»ñt̬š¯‡n&°’îÊ%B¯#[Ìr ( 6sE}3Eb±ºA8§Ëëæú>ì·GY4÷?>¿eo…>(ðÇÅø§IðeÿÃ/‡:µ½½—‡ïîÖwkÕbeºH#–T·B§hPß@Î>|3Ö~h· ¼Iû?‰K§Þ]6“®éñé² »IåiaK£w$rE$A¶C.ÁõýKíK]/7›”œ–ŽÝ_Mµ½Ì– )+=¯Û©ó·‹¼_}ð§ögÖ|q…iàÍGCÐî/“Lˆ¤öÖw{Öc’~õ°v€Ÿzwì«ãˆ¿¾x_Ç¿¡Š {\Žk–XbòPÛ<Ïög “ðìn¼ƒšõßø'Âß|1{àßééªèºAqm!`’ˆÝdPJ8  õçôúlß%Ü\c8S¹sÓ ýã_JRVXd‚B+¿+Ǽ5xÖJén»§£_5t\'Êî|ÏñSJñˆ5‡ß¾Y¯ˆŽ‚ïqöXäXÞîÇP„)hYÈ]Á sÛè¾6ñÇÆ·ø»âoÍám3KÒ²ì-nä‰î¦i$2I+¬Lꃒ nÏÔ×ÓÈ‹„@T`Àz uzQâ F‡°Œ‰ÅIß™E»µº]^¶êþUítµ™ÿl/ù7?ÿÛ‡þ—[צ|ÿ’9àOûéúKyŸíƒÿ&éâßûpÿÒëzôÏ‚ßòG< ÿ`/ÿIc¯‡üŒåÿ^×þ•#ôßòIRÿ°™ÿéªg¦RQE{§ç!E´”QK@ E´”QK@ E-%-%´”RÒPÒRÑ@ EPÒRÒPE´”QEQK@%´”QK@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQEQøRRþÿÖýû¢–ŠJ(¢€ )h ¢Š(¢–’€ (¢€ )i(¢ŠZJ(¥ ¢–’€ )i(¢Š(¢–’€ (¢€ )i(¢Š(¢Š(áßÙ[þKÇ¿û/þ•_×ÜUðïì­ÿ%ãßý‡—ÿJ¯ëî*ð¸oýÒ>²ÿÒ¤~â¿ü*‚þ™¦´Q^éùÈ”QERÒPE´”RÒPLó#äî/^z}iõùYuðƒã»øö«ÓÓGÔMïŒ5¦¸ðÚý¥7\ÛýªGÝn|ßݮ N8Åvàð±«~i¨íø»~˜V¬ák+ïùªydt4Wð¯NÕt†>ÒuèÞJËI±†å$mγG,ˆ$NOÖº­oQ“HѯõXmÚíì­å™aC†”Æ¥‚x±\²…¤âT´»4éÕ1¸“ŸZòß„ÿ¼;ñcáç…þ XcNOÀd‚ÒyÎYcܳCÁùÚ&F ·û¤×ÿ´‡Ç»?øMñ6•§_[Xx+â–›¤3BÞdº‰°mÓ#B ìcT'–Wf/©R§³jÚÙ˜ÕÄÆ1æ?MiÕ1¸““Œ“_0|>ý£5_üU~ xÿÀºÕöœú¦™ö»ˆ.£¼·‰‚È¥ $G*g%2ÃüÞ¼í!ã»}7Æ^ðÏ<«Í Éâ½ týjÃQ‚Þ©K2ˆVXwy­1>b•ÀiC/¨ê*r꯺ÛË]~ñ¼Dyy‘ösÉK¾F £¹8‘Ë£tLzƒ‘\¿|á_ˆ¾›ÂÞ3Ó×SÒ®7’fUf‰ƒ¡% žÖ¾Ký€CðWW³Fc¯‰õ˜bÌåcŽ`¨ ±' ˆaÓ£*—Õ5§­úü‡*MFÛŸoÑ_"ëµ·¨xŸÄšÁÿ†ú§Ä/NÖº®¡kq¬ wî’ÚÛÎ;®&AʸŒF}çá_Äß üað‘ñÁÒ¼š^±™‘vË)*ñȹ8t`A=88¥WRæ’Óúß·Ìp¯;&z WÉ_ÿhïü"³ñˆµ¿…×rø_Ag'TþÕ²Š9¢j:DíæeÉS‰8ÅF :²å†þ©~c©UA^GÖ´Wƒ·ÆÜü Ð~%è5mOVñÀÑhHðÅu¸•ç’VD@,O# mÏ…ðûöÕuü&ø“à›ÏøžöÆMFÂ9®a½¶½·‚ÍåO>x÷)eeŠ¿©T³vÛ}UôßMÅíãt»ŸI«£çkÚppzu~Y|øåã_kŸ4Ÿ øVñçöO5]Júk{˜¡ŽÖÑ¡8¼æÌ²“ Œ"AÀ“–öÿ´ßÇÔñGìÿàˆ^Öõ À¾4Ö¬­õÝbÅ ßiúcù‚eÈ bo1|¶a‘‘´¼gµäÕ=ª‡Gm~WÛNæ G.Çèµñ÷ìóðã஬Ÿüø©øžÈZ=µÝŒšãê¶Ìò”ešh¥g’†Ã¸0ÍòôÇxƒã§Ç‹_Ú×Lðý¯uCeÿÕÌ‹ .©h#ºÙw"­þwù`à Ç~;VqËœç(Á쯮Ÿƒe;WÕŸ¾;Ù|,øu£ø¿Æz äZî½46^µhîo®5ŒìµŒ¡òÙ¸åó´zä€w«–Ô‹‚Vn[Y¯ë籜1QwécÞ¨¯™< ûC꺯ÄK…Ÿ<{àZµšóJ0^[ßGm9hXÔ†d#¡àžÿM×-j¦í?óü©ÔRWAExÇÿš_À'Ú¾©¢Þk‹â-^ &8lv™•çV`ÊçåÀPrIðÛö‡ÕŸ¶.“¸”†,„œªüÊÈGZ¿ìê©Å5kù¯Ç·ÌŸ¬Ã[=©(¯e‰ÿüqðëÁ±ø×Â7Ñ[O£[ÌÞ º¾·œ^ɱq!‰XÊ ¹Ýó;××Õ†'éMÂ] )TSŠ’ +àÏÚæÊûÄŸ¾øûgRÒt¯꺌ßÙ—’YË$qÚùŠ7ÆAá”øÓ>j>8øûPY|Õ|S¨ø¿Á¾1ѧÕ4™5yV{Û «F"h~ÐZHÊ.@#‚ËŽCìŽ]zJjZ´Ý¼“iëò¹‹ÄÚn-i{_ÔûÚŠü¶øßðvÊËö¤øgá;/øžÛKø‘u®ÝêÅ­\¢«Å¹E·âWlÀ+пl-^ûáw~x|G«ø{À7Z¶“â rÚW—P†Â(vÇæ\áœ4¤|ò`±Á8cò#•©Jœa;¹«íë÷½6%âšRmmý~§è=ògìóðãàþªOão‚ßµ/éRÚ›K›)u¶Õí®Èë3,¬òE8 F †?(ë^ðËÁ÷¶&»ã¯‰_üI­[xgM×.ô_éZV£6Ÿ6ö!¹Û”i%•Žÿ˜:rÆKɹZ1µôÖ說ú—íÝ•–¯Ïõ?I(¯€ÿh|[áüýš|%âÝR'ñ¦°ºeî¶óƒª2Õi±0_õ¬˜]àœŒ&•áÛ´þðgGñ­¨ø⎅¨i¨ßËy-•ý’’òÛÏ.éz1»«ü(C-NÊzÙ´­º[¿ÁÙy,SNÖì¾lý¥¯Î¿€ŸõÏþË!ø¨I}âƒ×zîŸu<ò4“LÖÅå·bÎK0¸XòyP¸â¼?âƒk¿fOÙîÛâO‹µ]15­zÒãĺŠ^ÏжÔK›…2¡/¶$}ª8 08­a“ÉÔpæëe¦ú7ºßy2Æ¥kt¿éýz°tWçïìçyû+j¡>(ëž-ñ½¬ò ÝRúê;´w¨Jîç ò+âφQè—ÿ²Ïˆ>3x‡ã·áÏiRjocÿæÄ²ÚüÖÐ}ŠFbâVp£qÎAª†MvÓmZËX»ë~´&XÛ%¢{õíc÷VŠüìøƒ¯|]ƒàÂÏÚ¶Ø]§ˆü#ak¨x—Gš(¯ô»˜”߃!‘ ÊŒGÉóœV÷Ãjµíÿ #ÂÚµÔ ¾@¶ö"'’õbî5’W‘>RñÛ!Qµ¸åÅ`òÆ ê9h¯&º|ô·ükõ¥ují÷w>õ¤,ª@b< ž´êùöðgÄx«àÍç‚,®nít/Ú^ê­o(b±D`ï(.»2ï¥qa©)ÍE»yüªMÅ]+Ÿ[RJ‚ ^£ÓëYÐkZ5Ö§u¢ÚßÁ6£b¨÷É*´Ð¬£(dŒÊr¤žÕò¯Á?|CÐio>+ñ5•Í¿‡hý°äÜü[ÿnú]o^™ð[þHï?ì¥ÿé,uæ¶ü›§‹íÃÿK­ëÓ> Éð'ý€t¿ý%޼(ÈÎ_õíéR?F­ÿ$/û Ÿþš¦z]´•…Q@Q@½Q@Q@Q@´”QEQKE%Q@´”QEQEQEQE-%RÐRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PG4Q@ÿ×ýü¤¢Š(¥¤ ¤¢Š=袖€ J)h( Q@Q@Q@Q@Q@ IEQŠ( ¤¢–€ŠZ(¤¢–€Š( ¤¥¤ ‡eoù,_ÿì<¿úU_qWÿ²·ü–/ö_ý*¿¯¸«Âá¿÷HúËÿK‘ú7Šÿò<©þ ?úf˜QE-{§ç"QE-%Q@Q@Q@Q@ M 0*à ðsÞ––€?­<ñ_Ã:§ŠæÒô{Ö³ý›uÛÍgÃ𪺮§£¨¥ÌÑ/Êwf'å7ÉŒf½¨øÄZOì½ð2 ý6yuKÇZ>»©Æ!vt–þö[©^EÁ*¸Éo»Þ¿V¨¯z¦{)ZñÙßð«oæyñËÒ¾¿×ü6‡ÆŸ4ÍJoÛ[á© ¤Ïgo¡ë©,ë£fO•]ÀÚ¤öóÚ¼ŸöÄøÇà½CWðO„4øõ[½OÁÞ7Ðõ=Maѵ)"ŠÒÊa,ò$Ënb—jœâ&bzM~‘Ñ\”qñŒ¡)FüªÛÛ«}ŸsiáÛRIîÎáïÄ_ |PððñG„$¹—O2¼!®ì®l$ß û›¸â“ðvàö5ñWìã8>jž!ýŸ|}¥jºo‰.|SªÜXKýs%Ý­Ë™b™nãF…U•IùÙz× ~†ÑXSÄÆ1œ9t—žªÛtý %I·_T~5xkÀ~ø·Çžý¥´ŸYj:íæ§¥jzxÕå²Ô¬.ö´{³w/œ„Û”‹’TúKû:èÿ´…:d ô+ÿøvæK‹ˆ-5$’; d•‹HÉ3»¨üÊçd•íäÁ¥®Œng*ѳ½ýtù.Ÿy• *¦ô·Ý¯Þù]ñ/ã„þ"~Ñ/Æ‹m_LøwðÎð5†›‹¨ß&¹«DHûTæÖÞXͼ~ê6oŸ!ˆ9!Tik *4›n7vïkzhÍ+ÒsI&|ûVø÷Zñ¿ø»Áwí¿€5mjÙ¼Iq¤Áuk«&C†Ä{ê%.| oº:7>ð—Lð Ïí“à]sàÖ…¯§„¡Ñuˆ¥ÕõdÔ|««¦E$Foÿx¡PI ›Œã5úÙŒŒP08uQÍ):qF·ïßMZþ‘”ðœÓRo·NÇåÂ/_À?|q°ñ׆õµþÒñv¥¨èòÛiwSÇ©ÍåEZÇ$q²«ˆÁœª&wpkÑ> ߟÙoöLð.ñƒÃ:ü:¤³G¨ÛÚXµùÓã¼2ΦêÝA,.Ô+2³m*y¯ÐÌךZ+fpžœ›´Þ½••´Ó0†Çím{iÜü¶øieðÛÆµ×„¼qû1xbïBðÖ™¦êIâ‹ÈôË#N¹Yâ+iÁvû~ÁŸ5I~þÖ^ øÑâM3QŸÁíá»Íâ÷O²žÿì·Fs2y±Û$’á€Räç°8Åý¯ü6Ÿ|ðÃã‡tKľðÖ­¥¨ØZÅqk¨K¥ÜM,"¸YbÚ\+®IàGèu%gK2ä•9¨ëm÷ZýÏ]îTð·R‹z3ó§à½—ì­ü[ðõçÂÍÄš—ˆôŸ‚úöamtýдn%}D¢urT1Ï\`ý¤.p1žikŸ‰ö²O];»ÿ‘¥\ŠÚ|•ŒkÝ/QÔõß`³ší-~!è“MåFÒâV}ÎûAÚƒ»SxÃLÔ¤ý¹~j±ZLÖ0øGXŽKUÚxÊ«HÐOP Í}‘KZSÇ8ÅFÛ)/¼™aîÛ¿Tþãò·Àßm¿g?|Nø%ãÍX—Å«ëw4vºmÕÜZÌ:«³Ûx·Â?±•‡Â^ž#›Ã×ñ¼ À˜®u4Âzf30Œàã+ÁÇ5õæ ã‘EkˆÌ¹Ö‘³m7¯UùuîE0ý~+øzïáǃþ ê¶ã èñ[_Y_iW¶ñÅ%¢ˆäÅËÂ-Û'•L‘Û¨gÒÒW*¬jMÎ*×ù›ÒƒŒT[>ý¯õ¤ð‡Åï<Ôlo®´꺔÷¯ci5ãÄkå©1À®Ü³8¦xV'wá/ôߨó[ñß‹f©i¡]뗚ׇõ[=6æþÖîÒü‡ò ´IY&ˆ€„2€O ãúV (ÂŒj 0Ã5µLÑNñœo%¾ºlïmõí±œpœº§­ûi¯‘ù÷ûCjÚωô_ƒ_´ÿ„ü7ª\Øø;YöšÖ¤j‰¥Þ#A3ýœwª|Û3Üg$RðïˆGí'û\xKâO‚4ëôð?Ã}õdÔoìg±[CQ<˜Råcì]¬[`åXtÚ[ôF€àp+8æJ0åQÕ&“¾Éþ{½|ÊxfåvôÑ¿T~F~Ò~ñ ý 5„žÒ®¦ðçÇkÏ ]ê7PÄÍ¨Òæe½g`6©tŠ ‘¹çßm¦‹ÃËð_^þͺ»Ò<5ã=>òí,­e»x­-‘‹*%f (àcž•÷µk6w¦Ü~÷ékýÉ}ÄýOIk¿á×ó>høqûL|#ø‹âëOøNÃX·Ô¯VSÝhW¶Pâ$26éæ…xS€O'rE|û7~ÈÞøû-^ꦀúÄxïõ 4½a’[kÛk˜=«œí,” ¤ãšýŒ¢¦–fé&¨'´÷¾×òZ;•zýž>"k?> XjÞ5Ó%±ñ´sišÍ¥Ô?ôÛaåÊv8Ã$£.2¥_ yŸüïG½Ðÿe_ØjV2i×a®ÚX¦‰¡“s\9Ë+rF9#¥}¥I\õ1iÆpŒl¤ÓôµôüM#FÎ-½•¿/ò>7ñ¯íñ7ÿµ§„þé^ŽïÂ:åœwZ±‚áž nIQ*Ÿ$`ƒ 3ó{Šû"ŠZʽXÉEF6²³óó.œ½Ýį†~9Û‰?µwÁ_†úq2IwâíT¯Ý‚—ɳÝþÔ—Ý {WÜÕ‡ká Øë·Þ)²Ò­mõR8¢»½Ž[›ˆà‰%”î¨ Ú;V ضÊ(¢ÂŠ( ¤¢Š(¢ŠZJ( ŠZ((¢Š(¥¢€Š)h( Q@-%QE´”RÐÌÿ¶ü›§‹íÃÿK­ëÓ> Éð'ý€t¿ý%޼ÏöÁÿ“tñoý¸éu½zgÁoù#žÿ°—ÿ¤±×…ùËþ½¯ý*GèÕ¿ä’¥ÿa3ÿÓTÏK¢ŠZ÷OÎD¢EQK@ EPE-%QK@%PE-%-%PïE´RQK@ EPF(¥ ¢Š(ëE´”QK@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQEQøRRþÿÐýû¢–’€ (¢€ )i(¢ŠZJ(¢€ (¢€ (¢€ (¢€ (¥ ¢Š(¢Š(¢Š(¢–’€ (¢€ )i(¢Š(¢Š(áßÙ[þKÇ¿û/þ•_×ÜUðïì­ÿ%ãßý‡—ÿJ¯ëî*ð¸oýÒ>²ÿÒ¤~â¿ü*‚þ™¦´”µîŸœ‰EPE|™£üHñÇÄOÚ«UðO„¯þËà?†úr®¸V8ßíÚÅøÝ ·˜À²¬ í°‚a²q7ˆ¾4³øßñàWÄÍGûFöÁÓ]ðõÓGMq¡Ý¾^#U Ö²þé›$äðEW(®}[ETŒ(¯4øoñoÁ_t_XðĦ-PºÒ…íå·»³ÇœŽŽsסüëÃÿ| ⯅’ücðð¾Ô<7L¡²šK‰ÄvhmÕL’)pv•+ó9§`=ŽŠù§önñþñ?ëº'Š5OÛ~dû6«böi'ʾ‘È”LîÉËÚ©xÿöÂø5ðóÅZ§ƒõ u=Z÷Ã꯫¾•§O}˜®7µK•CŽJÌ£¨XW>£¢¼×TƒFøËðúÚçÂ^(¼°Òõ¸áºµÕ4[ í!ÁŽB­€Ã†sÔðØŸÄž*ñÃÿÂ_®^x†ïIñV·¦Åu}'›9·³ŸË‰Y¸ÎüÉ¢Ásì+åO~ØŸ ¼!¯k-õˆ/ ðìÒ[êz…¦‰y5…¤œH`ƒpCÕ£ ¾„×Oñcãð—þ7‡Zë[·Ö,.&Ò®´Ë)u ‹w–)fòAÙ@ÜÏ€:(åasèJ+ãßÙgöšOŒÞð½—ˆl55ñMþœno.¿²n-´Ç’3óyW ¾I 1Ï8ï]?kσ¾ñF§á —ÔõÍCAPú±ÑôëB-5H'ý*X”¢J]{¨£•ì>Ÿ¢¼Ä¿þxWáu¿Æ]K^ŽO ^¤-iun¯3]5ÁÛpÆ€»ÈíÀ@»È `ãå]ö•O‰ÿµŸÃÿ ø>ûVÒ´¦Ñuy5]Rµ–Æ_9^ÞIa•~a·%X޽ò(Qasô*Šüäøû/íàÿŒžðç†~.\Yi?õË»(­ÿ²l_û6á{„TgBÒà.̹Ïzú‡Å?<;û8ü:ðýÿÇ˨µÅÜZ[êÍhÏu2Ë*´‘[©X×dd8Í¡sÞ¨¯Ÿ>~Ó_ ¾,x¾ÿÀšÔ´ývÆÔ_-¦©§Ía%Í™`Ÿh€L ºn`pÃ##»¯‡?|ñO×þ(ð¥Û5–•{w§Þ}¦6·{{›&Û2J²`®'‚¤†‹1Üôš+ç¤ý§¾Ëð’Ãã]³ê7Õn>Ëcäé×2Ý]J]‘Dvè†L3#a˜ã’*ÏÂÿÚGáÏÅoÜø7KMGDñ%­¿ÛKÖleÓîÞÛvß:5m‘ ¬q‘œf‹1\÷Ê+ç_ˆ¿µ?Â…¾2¸øâ»Û±âlb¿ŽÎÖÊ{©ncžC$ ±y Jº ³5Ðxoãǃ|SðÎÿ➟c«Ç§i$W6’é· ¨Ç,EC µ dsó6nt=h³ žÕK_2~Ìþ;Ò~!Yxã\ѼYªxšøHïWÉÕlžÆ]'!\ié€1H`3œƒÈ¬ÏþÙ <­êÚV¥eâ ¬ô žßQÔíôKÉtûI#8}óù•{²+/½¬.}[ExŸÄÚá/Ã?x{â7Š5µñMÕµ­…ý²5Ä25Ü/ÒtsEÔmíþÖ"Ö4¹ì|Ø7߸(Ü‘ÆíÜôëX¿uÏ韾éþººƒMÔu¤ÔR ÞT±¨‡h›F[÷®Ü·/–&·±NÎÒzÿv._¬\#Ìì¤h¢ “ÐW"Ñ^h>3ü$##ÆZG?ôûÿ]¦‰¯èž%°]SÃ×ðjVnÅDÖò,±’¼¹IÕ[^šæ©—šh§·F½WÁ?¶ž‹gâŸü ð†®Ó+[ñY¶¼†å·3BÐ6P¼,ƒìEs%r÷µäúßüð†óÀß5if±_̺6ŽJI4FX"\RÌwfPKœ»q’kOÄ?ü!áŸxcá–¥<­â%ܶ0EH<«%VšIYF#A¸[Ž@Î è´µòÅÿŒž·ø«ào†Íâ]wÂZšx†ÅO‘¥NöÇœ‡mƒÝ²ˆ¶>àX«»}{¯ÅO‹^ø1áWñÄGìx•-â Í=ÅÄ¿êá†(Ã<’6Ž™'E‚ç£Ñ^ ð£öøkñ\Ô<' 5ö—â=.%¹ŸJÕ쥰½î@Y–9F2H©8$nÆF|âT^0øûX| ‹Jñ¶¹s£øÿP× þ“stÁÒÈIpÄmEgΠݢ¾Fð5ŸÅ_Š?³Ã_Bñõφ|Eu¤XÞÞê_d‚ú[¢ÖÙuuœœ†,x®ö×>5üOø{¤|fø•ñ]vÏ[¶¹ˆiÂÖÞ8eI¬¢hU]ˆTa´ñó{ v ŸyQ_ë?·_ÀMµ•j÷ÿðÞÜXê¦ÏJ¹, ³ùo-êíH·d+d–Á  {~§ñ¯áæ“àkïÚDøŒí‹qNöó¸‡ÏUi1ˆËÇ’±»u¥f=^Šó}o⿃t‰øO}q+x—Äð\ÝZAM"ˆ-´’Jãåx!K˜Œx¯×ÿm/ƒžÕ5;]J×_f‹s%¥ö°š%ãi–òÂþ\›æòòU[ º©^à‘ÍasëZ+Î|mñ[Áøkyño[¾ó|1emã\Ú!¸ßÅBTc¹¶¨Æ É– ž™Exo¯ÚáßÅÝ_PðÆ…öý+ÄZT+qs¤êörØ_%»¢a£âd, +ʵ۳àš¤žv¯~š äÖz£YéW3¦ž`p-Ã*íHËp§$¶ €håb¹ö=òÇí ûFé_ ¾¿<(·zÖ·¥\ßh×–š|×Ö@¤hñÉrè6ÅyŠA®FïCZ_³¿í§üdðÞ‡o>ŸªÛë­¢Ùß_Os¥ÜYXÉ3Çšmæ‘Dn ¹(ŽW‘‘Í®× ŸJÑ_(x›öÌø?áÏë>··ÖõæðÜÆßU»Òt››Û;“ïÇ,±® 'ñŽAõ-í¾£eo¨Y±x.£Icb¥IG”•`àô Þ†˜ËTQKH¢Š(¢Š(¢Š(¢Š(¢ŠZJ(¢€ )i(æÛþMÏÅ¿öáÿ¥ÖõéŸ¿äŽøþÀ:_þ’Ç^gû`ÿɺx·þÜ?ôºÞ½3à·ü‘ߨKÿÒXë‡üŒåÿ^×þ•#ôjßòHÒÿ°™ÿéªg¥ÑE…Q@Q@´”QEQEQKI@Q@´”QEQEQEQEQEQE-%-%RÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PG4Q@ÿÑýü¤¥¢€Š)h¤¢Š(¢ŠZJ( Š)h(¢Š(¢–€Š)h(¢Š(¢Š(¢ŠZJ)h(¢–€Š)h(¢–€ JZJøwöVÿ’ÅñïþÃËÿ¥Wõ÷|;û+Ébø÷ÿaåÿÒ«úû޼.ÿt¬¿ô¹£x¯ÿ#ÊŸà£ÿ¦i…%µîŸœ‰^MñÏ⾓ðGáOˆþ&jê%]ÙšIÇÚ.Ÿä‚ßç¨$t\·A^³^qñáOƒþ* /Á-忇585kxVH^êÛ/*“­‘ž¢šÍe?…ú¿Ã„VIââdñ‰æ—]×¥q‰RÔ›"·¡ŒmÜ+Í¿l-Vðgü"_µ„`2ê¿ ®Ëêq'ÞºÐ/1üg×ËSæ ð¸g䨯¶ª†«¥ØkzeæªB·6WðÉoñ ^-ÕõKˆõ¯ôû¸ÕíÚsºà:î*ˆþð e€¯¾ôï„? ´‹è5M'ÁZ%•嫬Ï›m±ºœ«#¬a•èAÈ­|8ø{ã ø5_xcLÖ¯mT,SÞÙÃq*($…W‘’N3ŒóÖŸ2œÿ`ÍWÐÿeoÛk6ïfó¥ÝÔÈ6´V—7RËl¤vÄL¸ö¯3ýŒ¾"ø‡Ç¼Q¯Yi3Ç:ñJ¼ º¸[›‚ñ4ÈU¥WQP0z_¡`Pª0ÕÌÝx'Á—Úü>*½ÐtûnÛo•}%¬OuÞlÅKŒ˜ƒc›aj»a·¶‰a†5ë„DTg°9 #ãOØgâ·Ã]wà€<¥ø§MŸÄú~–c¸ÒEÜ_Úweµ±o4(àîÛŒs‚+ãß‚šNáâ„~$|s¿øKâkoêWv]iö0ÞÁr¡¼¯¢c ‘:ír p]lü à­;^—ÅZ‡ôëmj}ÂKè­!K§ß÷·LªîÇ9<ÕOü7ø{ãk˜/|eá3]¸¶]±I}g Ë¢äªÒ+¹$ã¦ióÌ_ø[áÇÂÏ‚Ÿußø†oü.ð—Žã¾¼Ô®2$µÌk3lD_&ßh!q†ds^yñ+á¿Ä_ø(Gçø{­Xø€é¾Õ£»»ÓæK˜7J ÇdfUÉ 1Û»œf¿Cφü<ÚðËiv§G1y&ËÈO³¿¹åcfßlb±t‡¼/%´þðÆ™¥Kf$=­œ0¼Bly›ß»œ æŽ`±ð'íGñ£á~ÐáéK'„|M|u•7qgOÎHÚ~oÝ|ÿ/ÍŽx®çö»Ö¼-ãß|Ö´ÛmsDÔþ!èo ż‹5¼è#»W\« ŒqÜWÖZ‡Â„Úµôúž«à­öòéÚI§ŸM¶’YŽYÚ2ÌÄòI$šèÁÞ]7OÑ—D±~“*Ogmöh¼›icÎdž=»ceÜpÊ8êi\v>JñßÉûyü,tùZO kêÄue@@>Àó_%þкωþüJø«ðsÂHßðÑXMáð ˜ãÕo¦~¤¤Ž¼m½›øA ~¼K¡è·µ¾¿>Ÿo.©ià ÛDsÁ øªh7·w€tÍâçÅcgáýenoç–Ö{{w‘U¼ˆ%´D¾îé-°íƒ‘_«ZÞ…¢x—L›DñŸoªi÷ mî¢I¡pFäpTàŒŒŽµ‹¢|>ð†ÚÍü;á½7Lm?Òôë¿ø(Ö·{s I=Ãûy v˜Ýï¼¶+èJ±_¡5öoŠ<[á/iø‹ÆšÕ‡ô¸Ýîõ ˜­-Õä!P4²²¨,p'“À«©¡h‘ë/â8ôûuÕ¥„[=à‰Ã@pˆËås·8Ï4ÍwúŠ4æÒc?‹ GŠþ0èçÆZ7ö‡‰~"k7U·ö·¨C3)ŠKXüÍÓ#ãäd7bkÂ*x“â‡ÆÙ¼¤^ë:UŸ‚4ä²·¸há/ 4rÇ%ÍÏÚ² 2Œ[æNßÕ=7áÂþ WGð^‰c{jÁáž :Ú)cqÑ‘Ö0ÊGb ]¸økðêïÄâË¿ éskr­|öPµË»Ne)¼å~^½8éUÌ®+”‹:ìû'Û_ ž|s¢ÆÊü†\ߌ{cŠúûÆê©ûw|1•W— FaÕ•n-HØH¯«—Áž];NÑ×B°D«qen-bòmfLí’Û¶7]͆PÉÁäÖŒº&q«[ëÓØ[É©ÚFðÃtÑ!ž8¤ º$„nUb`zPä üZñg†¾þÝ>ñµ[ohZ‡ƒïì"¿¿•m­ Ìw&F¦ª) ëÕ‡Þ©õ?Új×àoÅ/ƒZbxïÇ6ÞÐ5}FÎ}Ä6×1xõÃËo*Oó@Pª¸%˜)•¶‘ô®¿áŸ ø®Äiž(Òm5‹0ÁÄ7GqáѶH¬2=qTîüàÝCÃñxNÿA°¸Ðà ±ØIkÚ Oº¥°8¥qØøsöyø×ñïãÕÿÀí_ǺGÆßF:¢x‹K†¥´Mä‹[“hÍnÌÀnÀù†FãÎ+ô*¹o xÁ^·–ÓÁš †… ä4‰cm°Ž…„j»ˆÏô®ª”˜!)® Fdàà†KHgÇÁŸvŒüðP?õö?ù¾Šøoa«i¾Š×[Ð4ÿ Ý $&ÏL“̶U'† åÇËu?/ã]å-{y†y,E?g(%­ôrýd×ài:—V øwö±ÿ’±û:Øæ?ô«îò‹Ÿ~üm·Ñ¡ñÔW…´ –¼±šÆúãOž ÙvoYmž7Žx¯.ÌÉ£Î?l¯‡š—þkW fbKI& Ð<7áï éˤxcKµÑì™Å½œ ofåˆH®IêqÍ5-,Ôüüý°>-|+o|Ò‡Œô_¶xgÇú|ú´Ú6ÞnŸ1Ê$’é<ÍТ4@ÈÉæ¬þÕ>:ð=ö³ðãE®¯g¯|<О–±-£ æ _,†<œ¯'­>d+¿Œ|!ñgöóð6¯ð§Sµñ¯…óÆ6ÿgÔ-l-âG c‘‹Û‚ò!ˆ»! ÊHùÓᎫ¡Ý~ß>°ð÷īϊ1Ùøjþ+RâKiâI[|†Þ mcHÝPfÁm¬ØÈ ú•'€| 7†G‚åðîœÞ\‘§H~Æ bäˆ6ù`ï%¸_¼sÖ“Gð|;%œº‡tí6M9+f¶´†‚9NdXÊ(*®y`1“Öš’°4|mñÆ~øiûsx_Å?u{_躟u> ëùVÚÐÝ BÞo)¦¬jňÜÃ<¤ÈþÚÓéÚæ¡ð;âm¿ŠeÒüi¬Ë-Ljt†Šå,þÛjE•êHD±Ëày›XbÜýñ…|1âËE±ñV‘g¬Û#nX¯m㸌7¨Y€>õ0ð燿°‡…ÿ³-±–n,¼”û(€ ¢?'6ÆÜc©)‚¾x;àÆ¥û@x[ÅöŸ®~&xÓJ²½K+Qwaz¿d–&‰ZÊ!µýêÆOÝÏ5óŸÂÿ‰ÿ >~Å?¾üLÔìôïi²x‹O¿Ñî¥A{}u½!há'Ì”9*<Å¥‰ ¹¯ÖO ü6øyૉnüá/Cžq‰$±³†ÙÜg8-©#=³Šv£ðçáî±®ÿÂQ«xcL½Ö6ymžÊ.vÙ·Íd/¿.3ÓŽ”ù‚ÇÏ?þ"xá¿ìŸðÃVøƒâYÜè|Op–ñÉ)¶"³  ÀçŠòoø&çÄ?_þÎþøueâ üS§Ãyqq¦%ÂÈa$oxs½Wç^HÇÌ=kî}GÀ~Ö4kOjÞÓ¯t› ¿f³žÒmàØ»Wˉ”¢m^Àâ«ø{áÇÃÏ _6§á_ éZ5ãÆbi쬠·”ÆÄ…ãE;IPHÎ2¥+«ÇÁ_³•œŸÿhçxQšçÅ^2ŽR@ùÑ`+z¸ñîkïÂ:ÏŠ¿àœ øçâ‡Æé¼9®9ÕôëiqÙ[ȤyÃhðIÝ̳dy’º¬Ùa´íý Ð|;áÿ ië¤xcLµÒ,Q™–Þη„3rÄ$aW'¹Åsò|2øo.»?Š%ð®”úÍÒºÍzÖ0™ŠQÃÊSqܤ«dò8ý•¿ä±|{ÿ°òÿéUý}Å_þÊßòXþ=ÿØyôªþ¾â¯ †ÿÝ#ë/ý*GèÞ+ÿÈò§ø(ÿéšaEWº~rQïEQE©i( Š( Š( Š( Š( ŠZJ(¯ øýñ#Äß |%¦jÞµ´»Ôµ=ZËLE½GÚØ cå²·úgŠ„fý£Ä6‰ã› Ç¡Ÿ3í `nÍÈýÛy{<Æ)þ³nìÿqÎ+âW?"Nÿæ{òZ ±RœT]íwfùw²ùžßE|±¦þÑŸðŒÇãí/âý´V·àrn;]Â-BÆl i-Čij±Ã8 FHù‚Þ¿øÑã|ÒüyñC€x«_žm?G´/ë‹ÂM¼Ò! Ò1‚0¨Xêv½öü:rá\j’/ÄÔcª÷®¹“Oªµ›{+«ØúfŠðÏÞ~Ñ_ð‘YˆV>:ÚHfþÌk•º´p„ c32J |¤¨\g=+Ì<=ñCö…øâZxOðÔzw†5‹,5ÿÚÄÏäà†>[•åHÏN{PñqVºzùO†êÉÏ–¤-›|ÊÚ»oÞÿç±öó/Å_‰ß>|8ðÖ§qe£Üx³YÖ-´©PyæÁ~Óæ•eùÖN6.rO~:UK‹¼+ñAø{ñgDÒ£ÿ„­nMÔ4©¥0‹˜¿•4seÀ#z‘Ç\.W~;ì:|1‰/k½ë{ÊíCâiuIkè}KE|U⿉ÿµƒüKáO êzw„¤»ñu̶¶ÍÛJ#ØÆLÈtÀ5ôÿ€ßâ$šD‡â\zlzŸšv /Íò<¬ gÎ%·g9íÒª–)NN)=<Œs‚¦”kN¤Z–Ö•ÛWjÿzhí¨¢¾uøÏñ‡Ä þ!ü&ð†gkskãíjm6òIÙ!Š;v”4;Y@lŒ|Á†;WRG†}ER¢¼WÆÿ´oÀŸ†Úøð·Ž|q¥èÚ¾µ¬÷ æÆeLª3åär7ãŽzU‰ÿ¾|.ðRøÇ]ñZ…¤×:Wt‰¢Éš‹ƒppà® ç†S°\ö*+æß€_´ïÃoŽ~ðëØkzl>/Õtȯï4;{µžâÍÊ)š6V>S6ÒJ ­ÿ~Ó_³ï„üM'ƒ¼IñFÓõˆ$ò¦·–î0a“ºÌÙÛáÈ"‹1\÷:+Ç~1ünð'Á¯\x—ÄÚÍ¥ÄÖ—Sé–÷W ¿šÞ/0Es’Ĩʃ÷‡Ì| ý¥¾|pðö†úN¹¦ÿÂS¨i±_^hÖ×K<önȦXØa[÷lÛI*>”Xw>‰¢¼Æ´Á‡~ ‹Â¾9ñ¾“¢êòí?f¹ºD‘ýÓ ÏîÔõöнñFÿâ¬~‚ÿàªè·z™‘dë&sjÖ»‹Fm˜1rvãœc>ÔX.z•ñoìñ‡öøå ÙüCñÆá» _}º×OûX¿W/lr%‘ãØZ6>¸Å{ˆio€•íüIãýOš+¹¬^9nã—6í²duÎWËnOR(°®{}Ÿ6¯¤ÛéO¯\^Á›&ᮚE,w „í ·ÙÆ9Î+Ë<ûB|ø—®ÉáxßK×5h•ŸìÖ×*Ò:',Ñùh rJd C=ŽŠñß~П¼â˜üâïé:F¸åGÙ..‘$Bø)ædâ=À‚7‘‘Ò»øïÁž O›ÅzÕ¦‘«:ÛZ=ÌËÏ3Œª#1’~”ìWEyÃï? þ+ÏmðßÅšwˆ¦Ó1ö„³ehÈ”$8ʞư¼mûGüøqâøE^G#~ÜŽzQ`¹ítW/¯øÛÁþðÄž5ñ³i§h$r5üÓ*[™•co0¸rÊ県W| ñ¾£ñÃÚÖ½yâmÅVŸÛ±Ø\èoæ@–*TÁ­ÎgE?>=EÛh¯ñ¿íð'Ὲ…|uã+FÕÀV{YîIq•2Ÿ,Èß·#ž•[ãWÇï|øw{ãMOYÓÚê]6îóFµšéb]Rkx ±Ã Û¼ÂUAPßx  žçE|ýð;öŽøkñ·DÑÿ°µí:O^i±jºE­ÒÏ=™eO5X`6#wIQÎ8®ƒÆ´Á‡þ$‹Â5ñÆ“£k2í?e¹ºD‘Œ©”û°Ã_h=¨°\ö;×%âOø+Áði÷~*×,ô˜5YD6²ÜΑG4… ›QØ…?"³uè éX¾3|)ø¯-ü üW§øŠ],¨¹K9ÖVˆ>B±QÎÆ …qòœ +ÏL¢¾*Ÿöäø+ÆDø~Þ(ÑG†¿±¤¾}lê å¥ò\,B̮ݛŠùßœ•ö‹­i>#Ò,õíî;ý;P‰f·¸…ƒÇ,N2®¬8 ŽA¦ÓB¹§EfŠ*ަu%ÓnŽŽ"7â'û8Ÿ>Q—iÙ¿o;sŒãœSJîÂl½E|%â‹?µ—†þ!øSá­î™àçÔ¼^—¯k"}¼Ä‚Æ?6O0™22>îæº?|Xý ü/ÿͧøj_øÛP¾´r>Öl"ŽÝHŠŸ0I¸©;³‘œb½ÿõrµà”âù“kÞè¯wéî³ÍþÕ§i7¦N®Ú~(û*Šù‡áÇÆOˆüV¼ø+ñ{C±ÓuåÓµl¯4¹ž[;»uq®ÉFôufèIȧ·ðsãGˆ¾#|Ö~%jÖV¶ú†.«pÀ$°°.#Ü™²ÛFì7ÓÍ_$¯M9;[ÝÕ4Óæ½­÷?Cjxúri-õü-ÌúNŠøbûö’ø¡yð—á‹|1¥éÄ_oÖÆHî„ÿc…™e ®É7”Énõï>ŸöŠ“Ä~'[øj-É›J7ió²6®b›qœ÷銬NGVŒªÊ*×V¾¯•ÙÙz¡RÌ!9(Á7·NúžÝE|Aã ý•¿ä±|{ÿ°òÿéUý}Å_ þË÷Ùüsøé¤]8ŠöçXûLq7ð­ÕÞçÐy±óþЯº+Âá¿÷D¼åÿ¥3ô×ü-Íôp£oüL(¢–½Óó(¢ŠZJ( Š)h¤¢Š(£¥´”Q@RÐIEòWíg£ðûÃz|òxi{gS•=·z«|™öXlòölpr«85Ïu§sZ×ni­µÑŸ&|lÇÆ?ˆ÷ß¼£[jú?Á“™¤MßÛ̳Ü[#¼ $ ˜±ï\ú§Ç¿èž(ðÂoŒzLÍ7†,üGa{räq 3+ǾP Úc‘¹àœWÖž ðW†>øzßÂÞ°];L¶.Égî‘‹»3ÈÌìʼn$³øV>‰ð¯á÷‡|1¨x/JÑbAÕ%šk‹)æšàæ@©+0E'©… Ô'ik¬·õ[[Ë¡èÿ­ØU*)S|´]¡µÜ$šŸ6ºI¿yZé6ÖÉÕ4ÆkDp–¿Û뙀]äÇÏÌóÆxæ¾øCð£Ã~?ñWÅMOZÖµ}2[Þ©§ê“ØÆË…mÌ‘0 Ù8ÜyÇ«éÿþο~x…|Uàÿ‹-N4tŽV¹¸›ËYײÈê28Î3ƒŽ•‹­þÊüE¬ßxƒX𿟩L÷‹ÛÔß,‡s6Ôœ(É=´¯B¥NW(­:]öôýL§6Á`ýµ:5ª%5IB7MJí[ÚZÖëÍò<›öŸðÊøá7€|-áÍNãt*Ób·½»‘¯¦GqpVGi[2ícаà+Ô<5ð'Äðžé_~(xÚêzJºtKgŸinó)G“ÊäÜûN,>œ w‘|øe…t¦B½MBÊÜÜ\*ê6fY7™7¶ · Åyé^©W ssší¥Ý´û¾G>+Š%,pØi=çy8Ç™©ùêÕÕù’vÖ×gÊŸä²üÿ°Åÿþ’×ÕUËkž ðljuÄÝ—Úoü94—ù’'“,©±Ûj0VÊñ‡Ü×S]©8Êr}_è—èxÙ†>pøzQNôâÓùÎRÓä×Ì+æŽÞÓ'ø£ñÃÁ:_„þ%ø7Ä72jzÝŽ®<­NØAÙí¡¸"H™Fv¾NNdò~ªŸTðÅ/Ù‚?i Vún§ág»°²žØÚ$¶d¤H0Uv ()Æ#ŒV_Šb¿Ù«Æž*¾ñ—ˆü .µ-R´Þbúö8.&ÎwIL#<òFÜrM}3i¦i¶l:5¬Vöñ-¼Vñ¢¬) .ÅP¡ð ÇJm’‘óìc¦xq¿fO…ºÞ›giöÇðí‚Ksqù¬ë ¬ŠÎ£q`À†ç#žkäc]ñÆ?|_ñ߇ì< àïi7ºÎŸyý¡¦›ÝVöKÑÍ5Ó‡‰#’FÉÎ\|¼p }Íà?ÙƒàGÃMñ À‹D×'ŽXšX..DB9ÈimÚST}ØÆ;b°5ÏØãölñ'/|}­ø*ÞëVÔe3Üæ{…¶šr0e{e@ÎrNJuù¾÷4Ô•Çc¾ YÛø—þ Õ¥M«ÚÇ©\ZøOUŠØÏÊѬ+qb=ÀíÚˆª¸èôîß²&—á³û9ü3Öô«;Aq7‡´ð÷Åvo%C†u'p!²s‘Ï5í ð„>x:Çáÿ„4å±ðþáw™V9”´ÌîÀ–'æc×+Î>~̾xºãÇ?<+…¬]G$Nö÷>HŽR•-ÚS@ÀHÆ;b“acó›ö|øQñS⟇~'ÜÁ«øI5 kÄzͧˆ Ö´gÔ5$LѪÉ7Ÿ,a0  (åyɯÐÏÙ÷Á ~hÖ3ýŸ¼7ðçÄ?³Wƽoâ…Ö½6µâcâïR6ž)£gòÕžO™pbéƒ÷k‰Õô«þÌß²&‰ñ ¾‹Tñ…ÌW ·ŸfK,k nH’¡½AëÞ¾÷ñ·ì{û8üDñ}ÇŽ¼]àÈ/5{ÖWºtžâ®1µ® †TŠSÇ%”–èÙë^%øaà?Â65í&9×Â7°jR#ÉZÜÛ Bê±2)8À§û4ùÔù÷@Ðü'ÿð¢øgO·Ò“Vð=â]%¬IL!º; ª€ª2{*Ž€c̼{à/‰5ߊ¿¼)¤xO‵ûûcZ´ÕV¶X×V±ÎÁáhãÚ@GËG»%¿C®þx2ûÇö?n´á'‰ôÛ)4ë{Ï6Q²ÖVÞñùaü³–9ÉBñ¯ ñìOû3x·ÅWþ2ñƒVïQÕnžúðÛÅ·¹¹‘‹´’[¬Â"Y‰$mÚrrNRXöêÞø‘ðËÞ ´ÒÒ-^Ólï ±¸‰ ŠѬ‘ÆÑãoÈÆWÍß±-´Vžø›m+QxÿÄHˆŠT,ʪŒL•ô7ÄOƒ¿ þ+xBøëF[ýÖH¥ŠÒ)¦´HÚ)Ólñ0 ¬@\ãÛ¥yO‚?cÙ«áÏŠ4ÿx3ÁçNÖ4¹LÖóGP—d„$¤·.Á?yM+« ù{ÇŸþ$üñÅ_ÞÒ¼'ñ;ÀºýíÆ¯­Ùê¤GªÚ¬*EÕ´S°xY#Ú@GËFXe¾ø£¨ø[â?ìeâ_éº4vöZ‡u ë y¡Bö‰.šò" ÇÈP` ¸è1VüKûþÌÞ/ñUÿŒ¼Càáu¨ê·O{x>Ûx–÷721v’Ha,Ä’6àääšún=/L‹L]+H—OHEºÛ„_$Bo—³vmãn1Ž)¹ #Á?e3Ëð áÞ³¤YÚ-ÄÞ°G¸‚8ûP8idëódõó_œŸ³ÂŠßþxæáuŸ ›ýw[Õ`ñ$:æŠ÷Ú¢]—+"O7ž…@ûÑ£aÎ9¥Ÿ ÿfOŸ|UuãO†ÞBÕ¯ ’ÚG†âäÅäÊë#"@ò´(7"‘±1‘X??cÿÙ×âw‰î|câïG.¯}·ísÚÝ]Y}«oC:ÛKÈÝ2ì7N¡H,|ñCáE¾ð¯ö`øEãM^ÛÆöVž0Óìæ»AºÞöÚ4¸hЂϹ<°±°ÜC}k×EѼ+ÿ Эü3ao¥Åª|?¹ûJZݤ¦;ì)e@ F @}S'Áo†i>п°£OðÌ7z$1É,ig=ºãeã~Õb1&às’ ­‹†þ ºø‡iñZãNâ«=ô¸o|ÙFË9$2´^P(åÎw-Û8âŽ`±ò-÷‡<#íï¦iwUŒp^x飅­â ,©¨ÄXª•Ã8^}qžÙ¯»-ííí K[X–bQBªÐ8{W’üVøðã|vIñGÃPko§nû<ÆImçˆ?ÞUšÝã)À%wm'’+Ñ<3á½ÁþÓ¼+á˲izL mm÷“ËŠ1µW|…°;±'ÔÒli멺¡ż—‘µ¼Á¡‡‰eR„NWæaÀärzŠü>ø?Ÿù6?‰ÿø?ùg_°”´'`hñ¯€:~¥ü*Ñì|?á}WÁ–1™ü½+Zm÷Ðfg'Í>lß|å—ç?):W²RÒT±ŸüYÿ“´øÿ^þ!ÿÒA\×íc¥êÚ×Å_ºV…«>…s«j+ ìQ¬Ï}ž3¸#ü­ÀÆ×Öú¯€ü'®x¯DñΩ`'Öü8· aqæH¾HºM“ ŠÁrñó©Çloˆ|á/뺉uûµê^šK6_6Dò%•B;mFU|¨Æ0ô¯¦ÂgTéÔ¡;?rŽÉêùí¾ëÞW¿žüšØ N5#ŠIü—/ã£>6øK£êj-cÿµWñGŠ|C¤Ð5éA€Men¸²[pÆ8äF]ùRw*’qÒªþÍ:¦›¦þʾ9Žþæ;vÒ®üD·BF ab]€pOÊH#ëšû7Å|ã=g@ñ‰4áu©x^àÝi³‰e…íå8É']ÊÛFQ÷)Ç ×˜x«öSøãOOãxJõ;¹<놎kˆ"¸—9ß,1H‘»Ë¹âÝ]¯>ÃV±ɾKÙ+^JÊé$Óò³ÙY˜gUƒ½;;sZíí+nìõM|×SữE­þÏÿ³‡uin,a¿Ö¢ŽI •­§D’9Èd‘HdlC_}ü9ø/á/‡züºæ‡â oT¹šÝíÌZ–±s¨BݰŠge ‘КÝñÿÁ_†t-3Ã>7У¾ÒôgY,íã–kXà*†5-ž<(C´/@: æ|û3|øaâ8ü[àofêÑG$K7Û/'ÂJ0ãdÓ:r;ã#µN??¥ˆ ãÏ(¶æùRN/šNJï™>¶Ù —N•DùSZ+ݦ¬­µŸæy7Æoù;ÿÙÿþ½¼Sÿ¤öõö…|Ýuð XÕþ;hÿ|Eã›ÝN}µt½­-"·³Kø–9QfŠ5š@v)ÌŒÇ#‚kéøö{h(¢–Â’Š(¢ŠZ)(¢€ (¢€’ŠZJ>”Q@Q@4~Ø?ònž-ÿ·ý.·¯Lø-ÿ$sÀŸöÒÿô–:ò¿Û"îÚÛöxñ,3ÈîžÆ8þ7È@÷ÚŒ~‚½gàìÛ|#ðEµÂå‡CÓQÔðC-¬`ƒô5áCþFrÿô©£b¸Júâ*éºg£QKI^éùÈQE-%Q@RÐIEQE-”´PQE-”Q@J( Š)h(¢–€ J( Š( ¤¢Š(¢–€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š(£ð¤¥ü(ÿÔýû¢–’€ (¢€ )i(¢Š(¢–ŠJ(¥ ¢–’€ (¢€ )i(¢Š(¢–’€ (¢€ (¢€ ZJ(¢Š(¢Šñ[ãM¯Ç]àN‹¥¾«©^i“êú•ÊJ-2ÑG ‘v±f™òª¹\pO (Û¨¯øSñš×âO‰ÿ„ïöîÿ¢s ÿßøÿùeGü'·wýþÿÇÿË*ûZÇRÓµ4itÛ¨®Ñk4.²Ã±*Nµ%þ§§ipý£SºŠÒ,ã|Ò,kŸL±ìYÿÐEO¾?üˆÄ@£ÿB¼7þSÿ–ŸÂwûwÑ9Ðïüü²£þ¿Û»þ‰Îƒÿãÿå•{wÇ‹÷? ´ßê:vŸªµí6Ú…ì“Cgsò[6ÉV7V1·£NÓìiÿbOþ‚*}ñÿäEÿý ðßø OþZ|Qÿ ßíÝÿDçAÿ¿ñÿòÊøNÿnïú':ýÿÿ–UgûmxÇR‡ÅZþðcXÖ<-àíJ÷M¾Ôl/­æ‘ZÁ¿zëlˇ ëÖ¾Ïø{ãï |OðFñÂ3›\¶K«wq±ö0èëÙ”ä0Ïhy×üÄTûãÿȇüDô+Ãà5?ùaòü'·wýþÿÇÿË*?á;ý»¿èœè?÷þ?þYWÚ¶Zž›©«¾uÚÄÛÄë VŽÒp}¨¿ÔôÝ*q©ÝÅg!CÍ"ƤžÙbhþÅŸýTûãÿÈþ"úá¿ðŸü´ø«þ¿Û»þ‰Îƒÿãÿå•ðþÝßôNtûÿÿ,«íϵ[+2§ïÿÕüÃçã?/¯”²O,‰4Š!Ú¡ˆ ÏSKûôSïÿ"ñ(ÿЯ ÿ€Ôÿå§Ä_ðž~ÝßôNtûÿÿ,¨ÿ„ïöîÿ¢s ÿßøÿùe^ÿñ#â}ç¼_ào [XÇu‹ug$ŽåZ|Ê9<ô5ì•×_†+Ó§N¬ëÎÓM­cѵü½ÐßQI?ì¼7þSÿ–ŸÂwûwÑ9Ðïüü²£þ¿Û»þ‰Îƒÿãÿå•}ÇErbÏþ‚*}ñÿäEÿý ðßø OþZ|9ÿ çíÝÿDçAÿ¿ñÿòÊøNÿnïú':ýÿÿ–U÷bÏþ‚*}ñÿäCþ"úá¿ðŸü´øwþ¿Û»þ‰Îƒÿãÿå•ðþÝßôNtûÿÿ,«½ý§~$|Dð'ü+Ýá­åžŸ©ø×Ävú+\ß[¨¡Iãvß凌œÄ+èßÁ®ZèVÞ&»†ÿVŽ[«ˆ"0E,À|î‘–rŠOEÜqëOûôSïÿ"/øˆèW†ÿÀjòÓã_øNÿnïú':ýÿÿ–TÂwûwÑ9Ðïüü²¯´¬õm+Pšk{ Ène·8‘"‘]ú0RHüi×zž›c,0_]Ão%ÁÛÉ"¡sè IúQý‹?ú©÷Çÿ‘üD ?ô+Ãà5?ùiñ_ü'·wýþÿÇÿË*?á;ý»¿èœè?÷þ?þYWÛÒM ;|çX÷°UÜ@ÜÇ ê}ª½ž£§êF°¹ŠäBÅÄêûXuS´œcKûôSïÿ"ñ(ÿЯ ÿ€Ôÿå§Å?ðþÝßôNtûÿÿ,¨ÿ„ïöîÿ¢s ÿßøÿùe_j\êšeÄ6—wpÁ=ÁÄQ¼Š®çÑTœŸÂ¯ÓþÅŸýTûãÿȇüD ?ô+Ãà5?ùiðçü'·wýþÿÇÿË*?á;ý»¿èœè?÷þ?þYWÚVš¶•<Ö¶7\MoĉŠìŸï($ƼKàÇÆØþ&ZxÂë[··Ñ†¼Qáès>EÀ³X™dùÂáßÌû£=:Ñý‰?ú©÷Çÿ‘øˆèW†ÿÀjòÓÆ¿á<ý»¿èœè?÷þ?þYQÿ ßíÝÿDçAÿ¿ñÿòʽ;ö†ý ï>Üø3KÒ<'7‹õ_j-¦YÚÃu¡óð¥rò+/Ì[àæ¨ü*ý¦Æÿ.>øóÁšŸÃÿÅeý¥ •ûE<7v¶4–÷¯±¸a´{‚þÄŸýTûãÿÈ‹þ" úá¿ðŸü°óÿøNÿnïú':ýÿÿ–TÂwûwÑ9Ðïüü²¯µ.µ=2Æhmïná·–àí‰$‘Qœú($~•~§ûôSïÿ"?øˆèW†ÿÀjòÓáÏøO?nïú':ýÿÿ–TÂwûwÑ9Ðïüü²¯´­5m*þi­ì/!¹–ÜâTŽEvCèÀGãVឈĶò,¨‰HaÇ^E?ìYÿÐEO¾?üˆÄ@£ÿB¼7þSÿ–ŸÂwûwÑ9Ðïüü²£þ¿Û»þ‰Îƒÿãÿå•}»Ä)æ[Ȳ¦q”`Ã#ÜTÔ¿±gÿA>øÿò!ÿý ðßø OþZ|9ÿ ßíÝÿDçAÿ¿ñÿòÊøO?nïú'Zýÿÿ–UÙÚ~Ñ^;×õŸiþ ø]y®Úx{S¹ÒåºQ‚%i­› "‚2=úõ®ÇÅu?xkÂþ+ñÏ„çÒ,5«µ³Ô˜Ü¤§H29H¤›báãn *FÜã“€yV -7õš–^Ÿü‰ô3zñœi¼§ Í-’»{_eZûw[ÙnÒ ø“ñ?Ä^!ƒIñ'Ã+¿ ØJ½ô·öó¤eT FÇŽ(Xåʱ5/òëÿn•,ú¢¤ëË*¨«ïu~]콵ݼ“¿Cÿá;ý»¿èœè?÷þ?þYQÿ ßíÝÿDçAÿ¿ñÿòʺ•ý¦¼Uuuâiô_†:ޝ¢øWQ½Óî¯-/!v-då]– Ç»ã=k±¹øå¯k^Ð|_ð«À÷~3ÑuËgŸÏK¨mÝãsE"I“½YX6 ƒÉëQ^ØšŸ‡ÿ#©½\×¹òŒ*¾l¯fýµ“kT›èy/ü'Ÿ·wý­þÿÇÿË*?á;ý»¿èœè?÷þ?þYWwð·öƒñ¿Å!¤êšgÃ;»oês4gR:„‘*1GsÕr”Œ úŸÚ¶£–{EÍ MK|¿Xž~iÅ/SØâ2¬*—d¤íÒÏ–³³òzŸÿÂwûwÑ9Ðïüü²£þ¿Û»þ‰Îƒÿãÿå•}ÄN>•âß~2Zütð,ž8´ÒßHŽ=FÿNò$”LÄØNÐÜFn@ÇÆMmý‰?ú©÷Çÿ‘<ßøˆèW†ÿÀjòÓÁ¿á;ý»¿èœè?÷þ?þYQÿ ßíÝÿDçAÿ¿ñÿòʺÏÚgâoÄßxŸá‡‚¾ßXé—¾<Õ¦Ó¥º¾µ7q©uaÉ<çÃ>µõöë-Ôx‚ê)¯"Š1s:/“Êʤ¶Õfè tÉ£ûôSï_üˆ¿â Qÿ¡^ÿ©ÿËOŒá;ý»¿èh?÷þ?þYQÿ ßíÝÿDçAÿ¿ñÿòʾøsâx¢ß\›ÅºhFÇU»´±Ùp·îÆ&¬¯ÝóG;:Šð_|Cø¿à¿Ú›áµm:÷Á_&Õ+D²hï-NÓ^ãæ¸2°¼Ëœ„\/ËÏZ?±'ÿA>øÿò ü@£ÿB¼7þSÿ–œçü'·wýþÿÇÿË*?á;ý»¿èœè?÷þ?þYWÜTRþÅŸýTûãÿÈþ"úá¿ðŸü´øsþ¿Û»þ‰Öƒÿãÿå•/ü'·wýþÿÇÿË*ûŽ’ìYÿÐEO¾?üˆÄ@£ÿB¼7þSÿ–ŸÿÂwûwÑ9Ðïüü²£þ¿Û»þ‰Öƒÿãÿå•}Å_<|ý l¾3|/Ö~&Zè²iQh÷š…¡¶y„­!°,*¿°Áǽ?ìIÿÐEO¾?üˆ¿â Qÿ¡^ÿ©ÿËO$ÿ„ïöîÿ¢s ÿßøÿùeGü'·wýþÿÇÿË*úàÅ«Œß ¼3ñB+#£GâX<èí%•dxÎæw€¡Ëž¯P²Ô,5šm:æ;¨ÕŠ‰Ã¨eê RFGqGö$ÿè"§ßþDñ(ÿЯ ÿ€Ôÿå‡ÅðþÝßôNtûÿÿ,¨ÿ„ïöîÿ¢s ÿßøÿùe_R|Hñͯ<â_Da¹Ô4-&ûSŽÍåó 8\`e‚’¸,ÅTø[ãñãï…žøªE—ý¹§ÛßK™˜áó1]팜dâìIÿÐEO¾?üˆ¿â Qÿ¡^ÿ©ÿËO™¿á;ý»¿èœè?÷þ?þYQÿ ßíÝÿDçAÿ¿ñÿòʾײÔ,5+qw§\Åut’'WCö”‘^U/ÆßEñ†?‚íæjÉ¢ÿmý«t?cý¡­ü¢þfÿ7r“˜Û›)ð†™â}SI¹ðõÅìi¬o€[‹Wþ(åÇR:þ4<Žkþb*}ñÿäEÿ‡ý ðßø OþZ|‘ÿ ßíÝÿDçAÿ¿ñÿòÊøNÿnïú':ýÿÿ–UöµŽ¡§êpý§M¹Šî•ß ¬‹‘ÔeIêÚTo R^B¯pæ8‘A‘ÇUQžHî4bÏþ‚*}ñÿäGÿý ðßø OþZ|[ÿ ßíÝÿDçAÿ¿ñÿòÊøNÿnïú':ýÿÿ–U÷’G 4²°D@K18I$ô«Yj”ëN¹ŽîHDâE$uåI¿±gÿA>øÿò!ÿý ðßø OþZ|Qÿ ßíÝÿDçAÿ¿ñÿòÊøNÿnïú':ýÿÿ–Uöþ«¥éaS¼†ÌJv¡šEŒ1ôˆÉ¯ø¿ñ’÷៌þøVÓLŽþ?k'K–W¡¶Qÿ1@1ö8§ý‰?ú©÷Çÿ‘üD ?ô+Ãà5?ùiãðþÝßôNtûÿÿ,¨ÿ„ïöîÿ¢s ÿßøÿùe_iÛêš}í¼×}ÌWiec«€ËÕIRpG¥~}h¿·—ˆ¯¼ßuƒ:Úx¦Žm^ÊòÚñ¢Kyš d6äDûQÔäätïBÈçÿA>øÿò üA¡ÿB¼7þSÿ–—ü'·wýþÿÇÿË*?á;ý»¿èœè?÷þ?þYWØþñ&‰âŸ éÞ.Ю–çHÕ­b½¶¸*½¼è$Gù°@*Açw­ BÃSƒí:mÌWp䮸\H¹FT‘š?±gÿA>øÿò#ÿˆGþ…xoü§ÿ->(ÿ„ïöîÿ¢s ÿßøÿùeGü'·wýþÿÇÿË*ûNûUÒô±Ôï!³©çH±î>ƒq5i® IVÝäQ+‚U ˜¸N)bÏþ‚*}ñÿäCþ"úá¿ðŸü´ø‹þÏÛ»þ‰Îƒÿãÿå•ðþÝßôNtûÿÿ,«íÖž•!yd“;T c®|WÍß¾/xŽÃã—ÃÿžŽ5hM«ës̅Ŧ‹j | ‰'›äF9 ´‚>`CY$ÿè"§ßþD_ñ(ÿЯ ÿ€Ôÿå§™ÿÂwûwÑ9Ðïüü²£þ¿Û»þ‰Îƒÿãÿå•};ñ3âÇ€¾h)â/jcO¶žU··ç¹¹Î+x" ,²ÑQI®'áÿíðûâŠÿá‚ßUÐüqñV•¬~Ð3YiÑ®RuÑ­ŠÈ³•;™q¸ Ã(ÎÒ³£œ×ÞööðZA­¬k 0ª¢F€*¢¨ÀU€àRÒ×f-†™Å·)nÛ»gƒÄ|YˆÌýœ*F0§MZ0‚åŒo½–®íîÛm‰EåÞ4ñ‹tøOFðŽ‹·gª_5¾¯1ºHŸN¶ ‘0ŒœÈKq´W |Áê4QERÕ+ÍGOÓѤ¿¹ŠÙw•ÕP@É,G gÞ€.QU"Ô,..^Î ˜¤¸‰UÞ5u.ªßt• ǽAs¬èöwqi÷—ÐAu6<¸¤•RGÏ*““ø Ò¢¢¸¸·´…în¥XaŒeØ*¨õ$ð*+{ë+µF´¸Žq" £† ‡£ G¿JµENóRÓôô2jQ[*ÄÊê€ œ±d@(ª°ßXÏq-œÉ<ZHÕÁt2¥”€GLõ¨æÕ4Ûk¸´û‹¸bºœf8žEY²¤äþzŠŠIà‰•e‘QŸ;A ´dã=p9¨¬¯ìu(~Ó§\GuJï‰Ã®GQ•$dPª*Œº¦™ìzt×pÇw(ÊBÒ(‘‡¨BrWY•³ª ’O@-FÇTÓ5EwÓ.á»X›k˜dY·¡ÚNµxWìïñÅ~5|.Òü}­Û[hWz¥åõ¤V¢}ÁÍËÀ6 X°@HŒÓ°AÑ^SñÃXñ÷‡>ø“ÄŸ c‚èöym ÌfH§ÿ¼’"ªAË `¸9ÝŠÖøSñEø¹ðãÿ¼> Xø‚Î;¥ˆf‰ØbH˜Ž E db8ÊœqE€ô (¯1øÀþ?·ð£¨ü7Õ¬ô}_OÖyE 3¼~ZÉ Ø6xô4€ôê+Áÿf/ˆÞ!ø·ð#Â|T"¶·m$·ByqîY¤Œm\œp£½{Å6€;QE€(¥¤ Š)h)h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(£š(À ÿÕýü¤¢Š(¢Š:ÑEQE-”Q@Q@ IEQEQE-%Q@ IEQE´”´”QE-”Q@þ!×´Ÿ è—‰õë…´Ót‹i®îfs…޼ŽO¢¨&¾FýŽ|9©ëºG‰¿i@ÑxâÍïÛ¢I~ý¦‹cÓ­†sÝü펻”ì½+ö•øSâÿž²øk jPiz6«¨Ú~YÖi4¨dK ¸Ta¾B |ÅFA5ïš}…–•am¥éЭ½¥œI 1 ¤q¨TU€UôSáÿBoŸ|ûIXžñÂ~,|« ËæÂñúäÍò=>ù¯º yÖ¸Šß4‹[O é‘ĶFÞcլбt¹Šc’'K– †bw ó #å}sáǃÿgÚËàͯÁ‹&ÐtÿÁ¬iÚΛ²µ½ÄV0ÄðÎÈìß:4„—ÎN\¾løKáÏ„iÚ_ã%çÆ«3â o]ØèÚ6“s+ý’ÖÞKa3ÜU‚´“9ݽã°ÇÐ? ¿eÏxÇ‘üOñеÿx¢ÎÕìlo(ø³áŒw÷W‘ë> ‚úÞΞ1k"߯bÌ­;Sòí‘@=A£˜,~Q|:¾ý¨´Ï„?oþ Á¢ÜèPø³ÄFñ&YŸX ö†³Aû‡u‹5~Kg†8Sèî,tßÙàGÃ/7W†0ø?ê~ÓÿôTµ7íËâ wIø?¥h&£6Œ|G¤è7×ÖïåËoc})•á.ËÏûuØ~Ò¿ ~!|OƒÀúŸÃ;í.Ë[ð^½µö¸­d0Fê–ßç#,2^;×¢ø—ᵟů…gáÿÆ«KMFMNÚ%ÔWO2źL7™jÎL©²Aº2NáœóX'±Ž;À?²·À¿…Þ"Òü[à ®«é0Kl·O3<ñL›\ov“ÁÜÙ`GóSÀ?ükûFOñâŠþY|B¼ÔõíOK†úÿ_’Â]. 6Çokn#q ;ÃçæÜ8Æw~Žü3ýœ¥øwâk?^üJñw‹¢Òá’ +fþ)­aYafH ˆÈáxVc€;gšÀñì‘áû¿k>/ø{ãü9ŸÄ³ VÛ@½Š+K»‚0×)¡”$Ì0 !õÛœšj@Ññ×ÇO|OÓ¿e_‚_ þ7êf?¯Žt­6îöÚ餑­^+ÔŒùãißå¹ëÀ$“Í{ ¿¼û=þØ t„GBÓ<{a¬Øk:|2ÈðN–uË$ŽÃÍI)~¤I9·ûUü kσÿ >hñkž(Ó­xOÁ~ñ¿…´»Ëé­<{¨ê:•óÍ$M$Sjq,R¬bP¨ª  urRÝ)sÇæŒuÿx£á7ìk¬é·0]x¢]ZÞ;yµ##ÂóÂ(Úá“÷ŒA¼˜òrO5îÿ ‡õÛVåÿiI--H£wYWlÖÓù±Iº ”muIióËü7ñ¿í~#xÿÅ l¼}}«kÚ¶™¡¯Ée6• ”¦­ímÄn°˜pp9ryùx¯føµsñ×á¿ìoà?‡?5–°ñ.½¯éþÕµ[k“4ÑéwLÁþÐ0w´1¤.ç’ É,I¯¥¼Gû$èWž+Ö|YðûÇ^(øs7‰'ûN©m ^Å ¥ÕÁk*heÙ3 È@8û¹É¯H½øàkàáøâ£{â/¼l’M¨]<×ï!”Î&7#k VC¹Yq·ÅH1<û+| ø[â/Å~ðÒèú¾•–Ësóš9Wk‰Ã» IÆw0,Bñ’|R›ö]ÓhφÝ.tyˆ¼+'|‘ø‰„K¿“zêä Ú¾¿øgû8ÉðëÄÖž!¼ø•âïæBðYXëñMi H»w2E fGQ³nôÿŠÿ²ÇÃ/Œ<#ñWÅo}¯ážTvÒĖ׉ Ââ(îÑâvtŽPYB2rNIã =u¥ýœ¾ƒ¿¼%ðús¾ûO²G¾|îß{pL×,©k°\ôPjöÊ(¨eŸ_¼ñ+ÄšÏÄÛßüBŸÂVqø¿UG¶‹O´» e&M÷ Ì  8ÏS_C|]Ôt?üÕm¾+^,–FÊVxÒ u©X•#ˆGgÁÉ‚ÿÂM`7ì¹£A¬k:ƇãÏhÛ·Óê7ööðÁçÜ6ç*¿f'É'rk­_€Þººð}׉5ÝcÄð…Ë5ŪjWL³ÜJìë5ÎØQ¤x· … ¼’|Š8z‘ƒ‚ޝ»ºÕö?GÌóœ|Uø…k-·Ä2¶—ÒÏs!•î´Ó %ºÆíü6ç(ËÙŽ;½çì”Q4Ú\‘ý«‹µo¶Žæ4™޼ޙí^×â…ÚŠt[t'ÈÃEZ£?læã½ºþNiæ˜ìèáaU^<ûÓM»½-&›üž‡Ë¿ !øå¨ŠzGÃ8ô84ë¿kq½Þ£,ÿh†W™•ŠCe‚2Þ£õÿÂ?†ðü%øa¥ø ;³~útS4·Bù³O#Í! Ùw9 :í<Ö§€~蟆ºº%ÅÌÿðê—Z´ÿhdm“ݶ÷Xö"aû¡·V5ß:‡VCÑ^È”¥¹ÇÄ|Gõ©Ê•6ÓÚÍ´¬›{éwnšì|¹ûɼøwþºßéTµõ-p ~èŸ <eàOsscbÒ²IvÈó4#n1¤kÁcŒ(ãó¯@­ð”Ü)Ft‘åq6œ}|E/†s“^¶†žAµóìðËÅ_ ¾Oá_[­® úæ³x%I‡‘u{,6èË ´eXŒägWÓ¬7)_^+Ⱦ üÑ>ø9ü ëZ¶»l÷s]ý£Y¸K›Óc(#ˆlùFÜZ龇Œ|õûTÉnýœ?ìh¹ÿÒq__xÃÁ¾øáËÏøÇOUÑï í¥ÎÉ8‘s´ƒÃ(={WÏ?´ŸÁ߉Ÿu¿‡~.øW¤Yë>ÔæÔQu¡pÖÒ´‘U+l7:‘¹~µëž/ð&¿ñÀ¶^Ö+|0?µGÄÁ< 6ûÿ$+ô“ösÖ4{á&‘ªxgÅú—Žôée»êú¼RCy9[‰•ÒXâp#`Qrƒ* ŒŽkÜ)j›BWæïì:D_²·Ž’C´Å¬øŒ>„„çÒ¿H«âÿ~Äž ÕõŸÜøsƾ)ð~‡âû‰nµG¿Ž :îiÆ&m ´~há¶1Â…¡>€Ï…“Ã>0ý•¿dOëWZiÚ߈íínŒ2´.öòÁv0èC5r™7ôþ‘àÿþÏ¿¶—†|ð’Äé^ñdž/æÔô˜dv¶óôò^  ŽÄ+¾^G]ÍžI5¯ûQ|Ó5Í+à?Ÿ 躄^ÑüM¶”³yšu¤67 Áž0Æ.M„JüoÆìçÝþ~Ížøeã-Câ>¥âMoÆþ+¾µzêzýÌw[Ù+ò!G*³Îv–b:ÅS‘6?:ü/ðsÀßc߉¿´_ŽÚ[߈º‚ø›S:»\J³Ùɦ™ã†Ú0­…„ÇVo(ûz ìZÞ£à ¯Ùà€üYàˈZlj#²þÆÐá»6QOsohÛ帛r(†$“, ’¼ÞÏâ/Ø_áν?‰,,üWâ} ž,žk½Cúv¡:\—sºe¡w_œ 6oØYTòÆÃÜø»öSð/Šþx+Àqk¾uðóËmZ²¸Ž-NÙãÊ,dùm½q¼y`0@¦ä‚ÇÉßô{á¯íÞ_Ú|<Ò|Iá›Û­NÔÛP²’{{¨’+­¥Ub”)tÂŒIïWoÿf¿2þÛpø&_X¶‡qàí7´!ü¶¼mFXÌØÝÛzWÓÿeü<ø¡ÆGñ?ˆ|KâÓ§M¦Ü]ê÷‘\}¢)U6˜öc*¡s•f;«¥ø±û;x_⯉ô¯ÿokžñFm%”:®x¶—&ÒfÞÐIæG4n›¹Lƒœ“K›QØùwöÓµÖl-þ ~ÏÞÒÁð׊uIm®´èïNŠîÛN‰-=®WsÇ»‰8Ÿ, ŽçÁ¯ÿ¾|yÓ|]ái¼}¦ÜØëš}޲×ÐÜJ¨ÒZ\¤ …•% …† FbzœýeãÏ€žø—ðïJø{ã;ýNýôF‚{=gí"=^ ËuÚ—ipˆMÉÜvmlTŽ+ á¯ìùqà/Çã oâO‹1—Åúï‹ü ž!ñ&´š¦•i9°¶Õl¾ÒÂ'–˜¶ÉÙæDT<~§ +˜V9«ÿøŸã_ÂÙKáÿÄ û–Ò>"]Î5÷Éš‚i $O*°vÞù9fº^óá/øsàGí§¤xá]¹Ò<5ão ÞÞê:TR;ZÇu§ÏÃr‘»6Æer‡nëŒæ¾øû8ü4øðïCøk,…Ú]ëK›ì÷ºdÖ‹²)-å`ÿ0\ƒ¼0n¬ æ³¾þÍþø]â­KâÿˆuŸø¿T¶K)5mvá.'ŠÑx·cŽ4Ž2Øb0I=ñÅO2–>|#ðí1âß‹?þ9XÂS«iþ)¾ðý•ÜÒùfŸb‘˜£†$uÏæ-׸!‹–_Ú³áO†moÿfÿ„:s\Ûx~?ýUne‹AÍœ7™Êe7nÝŽù澃ñì›áÍkÆÚ¯ÄxËÄŸµo”:°ðýäpÛߺp&’)¢” qÆôÛÜI&»Göwð†«Ãt¾Õuiäøav/´ùd¸Ieº˜&Âo$’&i7g'aCž‡SæÔ,|áðûáÿ…> ~ÙºÏ~Yþ!ðËö»Ǥ¿ÃnuhµG¶Ie×áÓßQ™o$†9·b n NzäÙY¾xrãâðøÒ÷WcZ]è>@xþÉöfœÏ¿o—æy»Ž3æmÇðçš¡ð§à‡ƒ~ü._„:;ÜêÚo¼Á©4RÉ*jÉ4ÑÉåG&VP6—’y'8Xø ö†ÿNðßìßðáoxÄð—ï~ÚrëVše¬/o÷H7*º±yoÌåp”cÓ~üø¯ðëãÌ~-Ñü§|<ð6§¥Ki«é–Ã_Á=Üm¾Öå!dPŽ9FÛ€AÎ2XŸ_¶ý¾Ãð“Iø?6§­\XønùõRk¸ÓUÒgv.>Éq(P“…tpsónÀÇYðËà ÇÃïë_|Uãkå·’Öµ«è䵆9 ’Ë 0ĦC°|ížø&ŽmÇÊßþü=ý¥uÿо9iÿð•ë0x«SÐmm®æ—ÉÓ4ý9•!ŠÑÔ#0o0¸çæHËëjÛüð?Âß‹^I’Ûথi¨Ò4’6ƒw¶Êæ6s–mªÉ–líMì{šõ?~É^Õüi¬x÷Àž5ñ7ÃKÄŒ«'‡ïc‚ÞöT|çŠhe 68Þ˜È'šöy¾xjóáL¿uI.µ-ãKm&i.¦ó®¦âò™ä•Ý)%ˆûÜãµ'-BÇË¿µøÓûdx³Ç–’ý§Ã_ ´¨t 9Ô“š–£þ‘{*ö,Š&FÐЍxUd‡þ ;ã_íE2ãÀ¶­§1?'Ù–æ8÷2ŒŒz5}!ðà?ƒ?gox"{ÛÛWºšö{­BH廸ž|ò¼QÄ„ª*¢áÊ£99'â_Á½WÄ>|fð]ì~¹áIf²Ô}Á/´[µ>l¢±ó#“FÐY³Î.>wý 8Õ|S¡übøGö)¼W ÚÜé·z^¢Y,µ­&솖ÎWòÜ:‡‰Êïer”üø[àý+â6™{7ìíqàMGM3\[ëþÖòÒÒCÆc€¤æEGYXU9ÉàÓO@gÛsø›Ã–ºÌ>¹Õm"Õ®W|V:-Ä‹Ï+;Èàòc[uð)øão~ÞRümñ&žÚw„ü¤Co¥NZ2u ©cu`±p‘ù²3 È@ã惘†-~s~Ñ? ¼ áOÚ?ào|9£Ãa®x“Å’hÝǸIu¶ß#ÌÉ àJý¯Ï/xƒÄ?¿h…ð_ˆtÍáî±}©jÚ¦³¥Ï¦Ú*?*$·iÂùÞcÁ@x*FA$8„Ð;ëë-2ÎmGR¸ŽÖÖÙIf•‚G(Ë33’O•«iZå„z¦‰y¡e6|¹íäYb|®„ƒ‚0pkÁ?kMÆþ%ý¼oá_‡Z;kºö¿cý ª2&èïa·HÊ£d,ì9ê­ïÙÇáQø'ðO $—θÒ-Ú_¨731–m¿ì‰€ö¥möÚüòý¤¾xCâÇíGà xêÇûGJ_øšëÈ2IùñÉj‘¾ce9Mä¯< Wèmy†¹ð›Ã¾ ø™¤|T¼º»MWEÒ¯ôxaãÍo¨¼O+:˜Ë™»p'*x˰4~[~ÊѼ!âïÙÆI­ñÃ>%:ìí4²ãhI;0ýÙ|.Ð8 :‰|oðÓKøA⟈>3ý¨þ]üCðî½­]ê+ã:õ®%°Ó§|ÃÛ,‘Íl-Ó Y6 Äè7‚ÿf/øþI¤ê:œÇá-†£§é~|°7Ú"ÔÖ4•®¶@»DCaË'!¸Çâ_ØÏKñtšŽ—¯üOñ­ß„õ{‰..´Ô¢6R,²Zßw‘æ‹rN6Î8 8«æW&Ç ûD[è~(þÏÿïî%›áï‹¡©\À’¼K¨ >Íf´‚FR¬TîÞGRèy®sà÷Ã|*ÿ‚€ø£ÂžŒØè¯à_µ%‚Èï œ’ßÛoH•Ù¶+æm¹À×¹~Ô^ øo7Ãï Åâ-Ä“AáÛØ—yàø$›UÒ$Ž2©4KÈû¨Fùr28ÈðïÙ?á~¡ÿ âÏ:}‰`ðäº Zð×lÿ´4Ø|â;¥„É$`\G$"93)Ü›‰_CƒÔ ú(þË~?´t´ñÔµOøJ#·6â×̓û?a¶6¹òü;;?ë±»¶8®ûÄü5âˆÖoî¯#ÔôíûDŽ(ž1nÖÚƒ#Èì­?˜ ¤8QÎTö”ì6~SþÌÚ=‡Ã¿þÏž6ðͼ‡Xñσ|G&°Æi$7ïb‹4ñ)UUÛ€£ Î|,ø;ñö€ø;¨üI¿ø{gâßxÞ{ûˆüWwâ9-u Kˆ§’M¼ %»F6ÆÖP0£õÁ²ï€¼ {ðÎÿIÔµI¤øW§ßéÚ`žXg‹QP²µÖØs¨!ŒÆpk‹¼ý|;g©jrü:øâ߇ú6µq%ÕÞ‘¡ßÅ72< ,´%ÏÞ Û{*¨â¯œžS玾ñ7‹ŸöWøuñÂèÿkßê3YëÏkrê.^==¼èüèʱv1Œäæ»/xoGøûhÜ|<øUdöñO‚åÖ.ô˜¤f¶ŽþÒäÇñ£1ØÌ«°ãƒ¿§LkþÒŸ$ñŠ¿gï‡öqëw‘©^ÇsªY¼Ïwb°iÎm®¥¼Ua‚dB¯'ÊÏ€C´û÷ÂÙËÃ_ xyðßÀm_öx²Ôu9<;­A¨Á5Ì’Ào•u7‘å(ë Ä ™ÌÄpÈnr”¬;ðÏY—YøGámÄ3yò^è–W7rËÎö’ÙWo©$šùoþ Ãâ~Ê^k€âÚK½A­‡'ìÿip¼ôûÁ³Žù¯wø‹ðÇÄ׿.þ|0ÕcÓ/_J‡F·¿¿%ŒVÁ $o)é !±…Q¸ÿoBøyà]á´?‡ÞŒÇ¦h‘ZC»˜F¸.ä »eœàe‰4¯ Îʹˆ?ò!x—þÁ—Ÿú%뮯.øÅ¤üL×| }¢ü(›GƒY¿}m.Ô[J¥%À¶e3ä<¨=A¤†xßì'ÿ&—ðçþ¼§ÿÒ©«ëZñ?ÙËá–¯ðoàŸ…~k×P^ßè6òC,ÖÅŒ.Ï4’e7ª¶0Àr5í”Kq-‚Š(¤0¢ŠZJ(¥ Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Š(ü)) ÿÖýû¢Š(£½PEPEPEPEPEPKIEQEQEQEQE´”PEPEPKI^i­üXðއñ3ÃÿîiüIâ;[›Øa‚=ë ­¶M;dyhÍ•CÎæRJôº+;|VðŸÄËXøtÏ9´FÚæ?*XnaÁÎÜœ£‚7 §#Šôš(¢¼Óá·Å üSM}|4gŽçÃÆ‘¨[]GåOձù?+uFÎr8 K¢Šó-_âß„4ŠZÁÙ^{kÖsêà {Ò Ksƒ5ÃäÕØCÎæuÆ@:ÿø§Ã~Ó¶Ћ ÿà²×ÿÒ™ø;ßÀºþ -øÝG6 ´¾õþEÛÞ?sÿ3Í¿á¯ÿgoú?òBûÿ‘èÿ†¾ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„]ÿv¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈVÅwÜÿÌóOøkïÙÛþ†ßü¾ÿäz_økÿÙÛþ†ßü¾ÿäzÃñ&­û'øcÅvž ºðÆ…u¬]\GjÐÚé6ÓyÊÁfaÔ9<®wâ½›þÇÁïú4üZÿñºoêkxÏï_ä ë]ã÷?ó<×þÿövÿ¡³ÿ$/¿ù“þûövÿ¡·ÿ$/¿ù½/þÇÁãÿ2.ƒÿ‚»_þ7^3âm[öPð·Šm<yá ëX¹¸ŽÕ¡µÒm¦òe•‚(™„{Päò¹Ü:‘Bú›Ú3û×ùú×xýÏüÍÏøkïÙÛþ†ßü¾ÿäz?᯿goúòBûÿ‘ëÒ¿áL|ÿ¡AÿÁe¯ÿ£þÇÁïútüZÿñº\Ø.Òû×ùØ®ñûŸùžkÿ û;ÐÛÿ’ßüGü5÷ìíÿCoþH_ò=zWü)ƒßô"è?ø+µÿãt˜ø=ÿB.ƒÿ‚»_þ7G6 ´¾õþB¶+¼~çþgšÃ_~Îßô6ÿä…÷ÿ¥ÿ†¿ý¿èlÿÉ ïþG¯Jÿ…1ð{þ„]ÿv¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈvÅwÜÿÌó_økÿÙÛþ†Ïü¾ÿäz?᯿goúòBûÿ‘ëÒ¿áL|ÿ¡AÿÁ]¯ÿ£þÇÁïútüZÿñº9°]¥÷¯ò±]ã÷?ó<Óþûövÿ¡³ÿ$/¿ùøkÿÙÛþ†ßü¾ÿäzô¿øS¿èEÐðWkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‡lWxýÏüÏ5ÿ†¾ý¿èmÿÉ ïþG£þÿövÿ¡·ÿ$/¿ù½+þÇÁïútüÚÿñºáüá?ž:çöwÃíìNm._7K´ùåQ™—~Rc<Ñ|òËï_ä+b»ÇîæfÃ_~Îßô6ÿä…÷ÿ#ÒÿÃ_þÎßô6ÿä…÷ÿ#Ññ#Kýž~[i×"ð™?ö¤æÞí4kY¤iîÆÝ€ô銣àû2|EÔ®t=ÁzE¶­fžl¶Wº,÷>í­Œàœdg¨ª¶×åŸÞ¿È_í;sGîæ]ÿ†¾ý¿èmÿÉ ïþG¥ÿ†¾ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„Mÿv¿ünøS¿èEÐðYkÿÆêy°]¥÷¯ò+—Þ?sÿ3Í?᯿goúòBûÿ‘éá¯ÿgoúòBûÿ‘ëÒ¿áL|ÿ¡AÿÁe¯ÿ£þÇÁïútüZÿñº9°]¥÷¯ò b»Çîæy¯ü5÷ìíÿCgþH_ò=ð×ß³·ý ¿ù!}ÿÈõé_ð¦>Ћ ÿà®×ÿÑÿ cà÷ýºþ íøÝØ.Òû×ù Ø®ñûŸùžkÿ }û;ÐÛÿ’ßüIÿ û;ÐÙÿ’ßüZ^ðŸÀ¿¦¶úoÃí¡êSé’ùº]§Í,K2áÊwŒgšîáL|ÿ¡AÿÁe¯ÿ¡¼û2û×ù±]ã÷?ó<×þÿövÿ¡·ÿ$/¿ùøkÿÙÛþ†ßü¾ÿäzô¯øS¿èEÐðYkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‚Ø®ñûŸùžiÿ }û;ÐÛÿ’ßüKÿ û;ÐÛÿ’ßü^•ÿ cà÷ýºþ -øÝð¦>Ћ ÿà²×ÿÑÍ‚í/½íŠï¹ÿ™æ¿ð×ÿ³·ý ¿ù!}ÿÈôÃ_~Îßô6ÿä…÷ÿ#×¥˜ø=ÿB.ƒÿ‚»_þ7Gü)ƒßô"è?ø+µÿãts`»Kï_ä+b»Çîæy§ü5÷ìíÿCoþH_ò=/ü5ÿìíÿCoþH_ò=zWü)ƒßô"è?ø,µÿãt˜ø=ÿB.ƒÿ‚Ë_þ7G6 ´¾õþC¶+¼~çþgšÿÃ_þÎßô6ÿä…÷ÿ#Ñÿ }û;ÐÛÿ’ßü^•ÿ càñÿ™AÿÁe¯ÿ£þÇÁïútüÚÿñº9°]¥÷¯ò±]ã÷?ó<Óþûövÿ¡·ÿ$/¿ù—þÿövÿ¡·ÿ$/¿ù½+þÇÁïútüÚÿñº?áL|ÿ¡AÿÁe¯ÿ£›Ú_zÿ!ÛÞ?sÿ3Íá¯ÿgoú?òBûÿ‘é?᯿goúòBûÿ‘ëÒÿáL|ÿ¡AÿÁ]¯ÿ£þÇÁïútüZÿñº9°]¥÷¯ò±]ã÷?ó<Óþûövÿ¡·ÿ$/¿ù—þÿövÿ¡·ÿ$/¿ù½+þÇÁïútüZÿñº?áL|ÿ¡AÿÁe¯ÿ£›Ú_zÿ!ÛÞ?sÿ3Íá¯ÿgoúòBûÿ‘é?᯿goúòBûÿ‘ëÒÿáL|ÿ¡AÿÁ]¯ÿ£þÇÁïútüÚÿñº9°]¥÷¯ò±]ã÷?ó<Óþûövÿ¡·ÿ$/¿ù—þÿövÿ¡³ÿ$/¿ù½+þÇÁïú4üZÿñº?áL|ÿ¡AÿÁe¯ÿ£›Ú_zÿ!ÛÞ?sÿ3Íá¯ÿgoúòBûÿ‘èÿ†¿ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„]ÿ–¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈVÅwÜÿÌóOøkïÙÛþ†ßü¾ÿäz_økïÙÛþ†Ïü¾ÿäzô¯øS¿èEÐðYkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‡lWxýÏüÏ5ÿ†¿ý¿èmÿÉ ïþG£þûövÿ¡·ÿ$/¿ù½+þÇÁïútüÚÿñº?áL|ÿ¡AÿÁ]¯ÿ£›Ú_zÿ![Þ?sÿ3Í?᯿goú?òBûÿ‘éá¯ÿgoúòBûÿ‘ëÒ¿áL|ÿ¡AÿÁe¯ÿ£þÇÁïútüZÿñº9°]¥÷¯ò±]ã÷?ó<×þÿövÿ¡·ÿ$/¿ùøkïÙÛþ†ßü¾ÿäzô¯øS¿èDÐðWkÿÆèÿ…1ð{þ„]ÿv¿ünŽli}ëü…lWxýÏüÏ5ÿ†¾ý¿èmÿÉ ïþG£þÿövÿ¡·ÿ$/¿ù½+þÇÁïútüZÿñº?áL|ÿ¡AÿÁe¯ÿ£›Ú_zÿ!ÛÞ?sÿ3Í?᯿goúòBûÿŒQÿ }û;ÐÛÿ’ßü^—ÿ cà÷ýºþ íøÝð¦>Ћ ÿà®×ÿÑÍ‚í/½­Šï¹ÿ™æŸð×ß³·ý ¿ù!}ÿÈô¿ð×ÿ³·ý ¿ù!}ÿÈõé_ð¥þЋ ÿà²×ÿÑÿ cà÷ýšþ -øÝØ.Òû×ùØ®ñûŸùžkÿ }û;ÐÙÿ’ßüGü5ÿìíÿCoþH_ò=zWü)ƒßô"è?ø,µÿãt˜ø=ÿB.ƒÿ‚Ë_þ7G6 ´¾õþB¶+¼~çþgšÿÃ_~Îßô6ÿä…÷ÿ#Ñÿ }û;ÐÛÿ’ßü^•ÿ _à÷ýºþ -øÝð¦>Ћ ÿà²×ÿÑÍ‚í/½íŠï¹ÿ™æ¿ð×ß³·ý ¿ù!}ÿÆ(ÿ†¿ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„]ÿv¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈVÅwÜÿÌóOøkïÙÛþ†ßü¾ÿäz_økÿÙÛþ†ßü¾ÿäzô¯øS¿èEÐðYkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‚Ø®ñûŸùžkÿ û;ÐÛÿ’ßüGü5÷ìíÿCoþH_ò=zWü)ƒßô"è?ø+µÿãt˜ø=ÿB.ƒÿ‚»_þ7G6 ´¾õþAlWxýÏüÏ5ÿ†¾ý¿èmÿÉ ïþG£þÿövÿ¡·ÿ$/¿ù½+þ¿ÁïútüZÿñº?áL|ÿ¡AÿÁe¯ÿ£›Ú_zÿ!ÛÞ?sÿ3Íá¯ÿgoúòBûÿ‘é?᯿goúòBûÿ‘ëÒÿáL|ÿ¡AÿÁ]¯ÿ£þÇÁïútüÚÿñº9°]¥÷¯ò±]ã÷?ó<×þûövÿ¡·ÿ$/¿ùøkÿÙÛþ†ßü¾ÿäzô¯øS¿èEÐðYkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‡lWxýÏüÏ5ÿ†¿ý¿èlÿÉ ïþG£þûövÿ¡·ÿ$/¿ù½+þÇÁïútüÚÿñº?áL|ÿ¡AÿÁ]¯ÿ£›Ú_zÿ![Þ?sÿ3Í᯿goúòBûÿ‘èÿ†¿ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„]ÿ–¿ünøS¿èDÐðYkÿÆèæÁv—Þ¿ÈvÅwÜÿÌó_økÿÙÛþ†ßü¾ÿäz?á¯ÿgoúòBûÿ‘ëÒ¿áL|ÿ¡AÿÁ]¯ÿ£þÇÁïútüZÿñº9°]¥÷¯ò±]ã÷?ó<×þûövÿ¡·ÿ$/¿ùøkÿÙÛþ†Ïü¾ÿäzô¯øS¿èEÐðYkÿÆèÿ…1ð{þ„Mÿ–¿ünŽli}ëü‡lWxýÏüÏ4ÿ†¿ý¿èmÿÉ ïþG¥ÿ†¾ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„]ÿv¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈVÅwÜÿÌóOøkïÙÛþ†Ïü¾ÿäz_økÿÙÛþ†ßü¾ÿäzô¯øS¿èDÐðYkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‡lWxýÏüÏ5ÿ†¿ý¿èmÿÉ ïþG¤ÿ†¾ý¿èmÿÉ ïþG¯Kÿ…1ð{þ„]ÿ–¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈVÅwÜÿÌóOøkïÙÛþ†ßü¾ÿäz_økÿÙÛþ†ßü¾ÿäzô¯øS¿èEÐðYkÿÆèÿ…1ð{þ„]ÿ–¿ünŽli}ëü‚Ø®ñûŸùžkÿ û;ÐÛÿ’ßüGü5ÿìíÿCoþH_ò=zWü)ƒßô"è?ø+µÿãt˜ø=ÿB.ƒÿ‚Ë_þ7G6 ´¾õþAlWxýÏüÏ4ÿ†¾ý¿èlÿÉ ïþG¥ÿ†¾ý¿èmÿÉ ïþG¯Jÿ…1ð{þ„]ÿ–¿ünøS¿èEÐðYkÿÆèæÁv—Þ¿ÈvÅwÜÿÌóOøkïÙÛþ†Ïü¾ÿäz_økïÙÛþ†ßü¾ÿäzô¯øS¿èEÐðWkÿÆèÿ…1ð{§ü ºþ -øÝØ.Òû×ù Ø®ñûŸùœ.™ûUþÏÚµÏÙ-|a oŒæx.m“¨~h‘{ôÏNz_@C470Çsm"˪eeaAG ŠòMgàÁ]rÂM:ëÁZT #}­¤V²Œ©\¬ª0Ær9ëƒÔ ùãöd“VømñSÇ_³½åëßiº(–œdùš8e1’20ôš&e÷[.Xj)Êt¼uiÛo+v׫ Æ5R³Òë¹÷5Q^QèQ@-%QEQE-%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEsEÿ×ýü¤£µQE´”Q@RÐIEQE-%Q@RÐIEQE-”Q@J( ¤¢Š(¢–€ J( .î­ìmf¾¼‘a‚ÝIŽQY‰=&¾$ý“4ûŸ‰¾'ñ·íc¯FÞgŽ.áäÛøwOb²ŽßicªÃ˜W¥~ÕžøŸãï†Cá‡Ã F2xÊî 3V¿ÇéúD͋ɈwVrÑå6 f*ÍŽq^÷á éðæ™á=o¦éÑZÛÆ?†(T"ƒŒsÉîyªè#ãˆ?ÀڷÿcO„>/¤^×HàÖ-Ô¶rÝ2 hIÏÝÜNp¢¾ì¯øùð®×ãGÂ?ü<™¼›Bß}”ㆷ¾€‰mfSؤª§>™ ©ðjÿÇúÂïOñOJ:7‹RÑaÔ­Ì‘Ì>Ñ 1´ªñ3¡Y¶ùŠ7d°ÀŠÀM¯…|a9øûZè¾9#ÉðwƘãÑ56éõ¢“c;q€gˆ®7¾ê¯ý¡¾Áñ«á¿à-ÞN¡q¸Ón8 m¨[-´ª{‘@>ªH<J,ì×Öze•Æ¥¨Ì–Ö¶‘¼ÓK! ‘Ç,ÌÄð€I>•ñgì§OñZñ§íW®ÂÂëâÑ´ÐÖQ†·ðõƒyváAû¦áÔÊ㾸4ߊz'í ñoöoð¯ÃwПBñ7ŒšÓLñdÿi·a¦Ø«m¿™JÊD¢eC±±*ûX)'cxsÃúO„ü?¦ø_A€ZéºM¼V¶ñ‰*{ð:õ=M=þË?òX~<ÿØyôªþ¾¤ø™àk/‰ÕüzB}¾!”ŒùS¯Íž¿+:ŒŽõòßì±ÿ%‡ãÏý‡—ÿJ¯ëíúô³‡lCôþ’Ž ³ø+Õþlñß~5¾ñ§€mλ”×´Y$Ó54c—[»S±‹uÉa†Ï|äq_&üRøÕâ¿ øÃ@ÐüA-‰®|7ª5á¹Ò¦hVHü¹Aw¤\äᘌ‚OÓv>ñ7„~9^x@´7ñ… :ž×Eû5ý°Äslb )Úvƒ’r~讎ëá…c—ÃcðE¢ZøwQ:‚Ú% <!ÞzäîëÉà•Â¥îv4Ú4>j¾"×¼,ºïˆõm7V—P•¦…´¬›Xa „HI22w1äãf½°´ øwÂÑ][øsNƒMŠövº™ A¼Î³•d…JÞ¬›Ô´|™ûDè:.‚î´»mf¿ñvŸ5Ä‘¢«Í#3ÎÀdœú×ÖUó—Å_ƒÿ¾$kV÷Þ3¶Ó46öÞþÆÕ´Ï9ážÝËKç¦ð_schàã¶kÒßž%Õ¾ê~ñVµþ«©ÙÞZI¯‘úJº#÷€#ÍŒäg‹•¬µ%nô= ¾Mý¢´ Hµð…Þ—a ¬÷þ.Ó¦¸’4Uy¤gbYØ ±Ï­zwÀß…3|ðkøRmMuf{™.<å„À?xÛ´¼1×?…rß¾üBø“¬A5·Œí´Í#O¼¶¿²µm3Îxg·@2Òùé¼ÜØÚ0;fœ,¥¸JíGRWžÿÂ+âmSáÖ¥ákQj:®¥iyj÷ñZýž0.UÑ€HßqX7üØíšæþ|'—àçƒåð¬Ú¢êÍ%Ô—rÂ`xQ·iy:c® ‹+nUÙìÕâ_|]â ø$ð¬Ëi«ë·öšU½Ã ¼!“þ3zöÚàþ$øLø—á+Ÿ êsIj%hå†â‰`ž‰î¤Q¯¨KmWÂ_'ðŽ·c®ÁãsQx•ÅÜ7×"x.÷ÆT›G–UÈeÚqÆ9ÈùZמ$ø¡ªø›Ä¤ž.X-¯ç²Òãðú*ÚZ¬ÃHIII#p }@_«<%á_ŒVÕ”þ1ñ¥®«¥Ø«††ÛNÒÝ1Œ¢´Îdp»IÝ„$zq\Âü"ñç„5­b÷á?Š­´3^ºkÉì/ì~×72q$²É€) çZÆJúây/ŒÚT’ÈÈÓíéˆqÛqÈô¯@¼ø/ãÝSñøeã(ô-#ÅS=ÍÍ´ö_i{k‰Wl’ÛH$M¥†8 ãžt÷Ÿ¼3?Â[?„ö×3[A§ä¶¼Lyñ]DþbÎ: ÛÉãÐâ‡(…™sÂäðv·i­Ûø»[ÔÙQÒê û‘<,Ë€û6®ÆVämã¶;×ÌÞð¯â›‹Ä'Ô´h´ÍwS{8,&'Û#‰$if–#P¹Øå²>›ð‡…þ/ØkV·3ñ•¦«¦Y£¯‘m§ i.Y—j¼Îd|m<áÉïŽ*o|0—ºGŒ´·Ô–èø³R½¿W”òÜIB7¶ý»3œ®sŒ ž{u-Ï”üWã?ø‡À?üg-³júËêŠþJ¸îeˆ2½¸ ûrOLšîüªë_ií¿i#Áú—…ô‡Ž6I<뛸®\ƒ9•UQ¢Bpç ~¸îì~\YøkáLJκ®|}öÆ“ìÄ}¬nfÚÍ>_Þë–úWoã†w%ñ§…|{¡êcHÕ|9+‡c ™n­&–ÝÀtÀ#8nv“3Ч5·¨¹^çÉ0ø£Å5O뺜¾.E¶Ô.l´¤ÐE²Ûªòr¦I }å8ã¾HÛéZ×ÄÏŠ:/ìû£^kpI¤xÏX¾ƒFóçŒ+FÓHÃí:dƧ·sí]â|$ñÿ„ubëáOŠí´/]ºkÙ¬oì~ÖÜËþ²HdB`eH##­uºÿÂëü4ñ¶µ&£¨îY†©I‰s†H¥Ž5ʮΠœ¯S“šã (²·„>ÍáfÓ[‹ÅúÞ¥"#¥Ü7×"x.‹¡¶m ·+´ã¶Zò=↫ðçÂß´?ÞÉwªø&âYìe¹mï-­øÝgÇ-µÎû;kØ üDÒ|qý¬lm X#Ô¬„;×PŽÖa%œ÷ëž/¼{ »»ƒÜùV²äkt8_:@TŽ@Îà1Á›À¿‰ZÇŠ¿á ñÜ6ž/¼¸¹¹†]<Éä øÝ ;K€¥yëuO€:ß¼=à Q›L½ð¤©u¦ê!VIc»V.Ò:ä–\ŽØ#ºãsy‡Ã›ŸèŸô{ÏÅSx[S†xõøHT8·™¼SE 9È*ËÓ$cÛgFÒµßÞ7ñεâMKHÐ|3¨&ÊËLŸì¥¤‰äšV Y‰.6þ=€ê¾ðׯ mv Cƾ0³¿Ó­Õ×ì–Zp¶óÙ†¤‘ä½v¨÷Åsº—Âh¾0Ö<]ð¯ÄðèÃÄL’_Ù_Z»vÑ4{dFF#ïCwè1.JåXä¾)Ïâï iøO£ø’ðÝx›Sk;^B¿mˆÛÈÜ<Í®«¸c;{"¬hvÚç¿z/`ׯõ¯x«Oº™bÔ¦ûLÖ÷VeK2I€Bº°à÷϶;|!Ö|eàí ÇVñ+xzè_Zë+lŠÈrÃ6á¶ùx*»wg 2O9_|,ñD>;_‰üC»«ÚÚ5•”V¶ÆÖÚÚ'l».åû“Np0s+Ï|iã/§Ã)/|o®/ü'‡TƒPÙw±Y•hÄ9S±ŽNæ9íŒwè¿ákxô|ð/‡í/ï¥Õ5ýZïM¸¾¶Q6 mmf ˆƒpÒ”eä—ÜšúŸî<5ô}uná}AØ‹bŸjûpc÷§ËÙö³íU-ÿg5ƒáö™ád×Þ oBÔ¦Õ4íR˜'’C ;N@a¸nÇn•n¤¯™*,ãþ]xÛCø¦Xh–ž)›Âš”%øñ ‡û= ÑË ää2úû•ñ†«öOˆ¾&o5§ý­cÑn´Ó$zb[XÉ:ù›³¸?å_LøSíµØux¾ÏPÓíÒEû–öa30¼’<’0Û×jãžõÊøËáÏÆ/&³ Eã{(ü=¬´Êc—Lu¼ÙÌHâP€p—>¹©RW.‡·h[Ë¡iÒÚ_N¶„ÇvX1¸Bƒ–^ñódpsZõáOÙxGÃ:W…´ægµÒ-aµœå™!@€·¹ÆMoÖ ÔøWD‡Æ_|ãŒ)âýSIÔ-®5Òí-¦ÙÛÛÚgdrG´ï.ŽqƒZòx¿Åð®þ_®©p.5­OJŽöO0î¸Iq½d?ľzÖ׊> xÓÂþñŽŸáǧxCV—óY=§™s •KËoQñ€J’£×­Iះ3üIýž>Å¥jØú¶Ÿ¨ØÝùbeŽxWr7)úõ¨È=[˜Ù~·¯k0þÓ>ðìW²¦™>ƒq<–Á”Ò‰\+ÐZò_ é¾+øËáŸüQŸÅú®{oy{“og?•kk§1‰#ÛûÂÜ'òF ¯[ðÏÁÿÛüO²ø©ã?E«ßÁa%“[ÁeöxUX’¢#æ± 7wI$ä ‹qð7ÇZA×ôO‡þ2Eð׉gšâkil¼ùíZçýp¶”H í¹~_z•$¶eY”5ˆ¾ ñì—/Ä$¸{-f}-]æ€ùl'ŽQºãÜTž:[ÿ >k·6^øâëWš¼°As4z ŽHÁòL:F[vâÃ9çØëŸ 4ûσr|Ю¿³í~ÅœWžiPŒ¬]”2n,A'‘ɯGðö”t-LÑ_8éöÐÛ™ÚÊ@›±“Œã8ÉúÔ¹«h5u>~øiâ-wPð§Å«ûù§—M×5ÈmÜ“ p“å¢g NõyNŸ/‰ü{§üðõï‰u=>?h—ó_ÍipcžvŠ8YK9 ’ç$zúרÞü ñ´:‡‹l|/ã$Ò¼7ã+‰îîíÚËκŠ[¯õ¼Š“ÉR@àsÍtÞø1?‡®~\>®·ð‚é÷V,¹Oµ•wÞ,.Ο6sÔUs-ÉIŸ7üÖüao­ü*ñ&«â]GT&«m}os7™nÍ@ƒËL NKI {çô¾qð¯ÀK Çðò7×ãþW¿f"اھÚýëy{1þÖ}«•×ôŸx»ö­Ò?³ä¹µÑ<'§%Åä€È°K,›öDÌw©=x Ü 'i=¤}qGÒŠ+@¢Š(¢ŠZ)(¢€ (¥ ¢Š(¢Š(i(¢€ (¥ ’Š(¢ŠZJ(¥ ¢Š(i(¢€ (¥ ’Š(¢Š(i(¢€ (¥ ¢Š(¢Š(i(¢€ (¢€’Š(¢Š(i)i(¢ŠZJøƒÀ¿ò}ìþ‹Ó«íúøƒÀ¿ò}ìþ‹Ó«ÔË~ßáš81ÛÒÿü™öý-%-ygx”QEQK@%PE´QE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@…%/á@ÿÐýû¢–ŠJ(¢€ (¥ ¢ŠZJ)i(¢Š(¢–’€ (¢€ (¥ ¢ŠZJ)i(¢Š(¢–’€ (¢€ (¢€ (¥ ¢Š(¢Š(¢–’€> ý–ä°üyÿ°òÿéUý}¿_~Ë?òX~<ÿØxéUý}‰«ø“BÐgÓíu›èìåÕg[[Q!ÛçNßv5í¸öëÔÎ?Þ¤ô”yùgðW«üÙ·EW–zEPEQ“TÓbÔ"Òe»…/§F’;s"‰]™S;ˆ `VgˆüU¡xN9õë³&¡u ”'k6éîdkòƒŒ“ÔñNÀt4QG4€(¢Š(¢Š(®jïÅþ±ñE‡ƒnn‚jú”2Ü[ÃµŽøáûí»÷5ÒÑ`"–.‰ãYB0eÜÄg¸õ©j9¦†Ú.n$X¢‰K»¹ ªª2I'€äš‚ÃP°ÕlâÔ4˘îíg£–FãÕYI} [¢Š(¢Š(¢Šç¦Ám®±¤‚À)Ç¢“øSå}…twtWá_ˆ>ñ¿˜<%­Új­Üé¡W¦Jýà3ÆqŠÈÕ¾/ü0Ðuñá}cÄÖ6š¦à† %•Ž0ôSÏr)r½‚èôz*–¥©éú=„ú¦«s²—–iX""Ž¥˜ð+ˆð·Å¯†Þ7¾ŸLð§ˆm5+«d2IOóRl2FH¢Ì.z%Ïiž,ðÞµ ¿Š4­B+&135Ê7îÀ€²ÈIÿd©éWtMoIñ•m­èWI{av»áš3”uÎ2ÔQaš”Q\ö·â­ ÷zU†±t-çÖîE¥¢O›6ÒûFÇʤóÅ$€èih¤ Š+_ñ‡á]*moÄwÐéÖøß4ìNÉîO@94³Er^ñçƒ|{i-÷ƒµ{}Z,†ÉBznS‚3Û#šëi´ER¢–ŠJ(¥ ¢Š(¢Š(¢–’€ (¢€ (¢€ )i(¢Š(¢Š(¢–’€ (¢€ )i(¢Š(¯ˆ< ÿ'ÕñþÀ0è½:¾à¯‡ü ÿ'ÕñþÀ0è½6½L·á­þù£ƒ½/ñ~Œû~Š)kË;Ä¢Š(¢Š(¢Š(¢ŠZJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(æŠ0(ÿÑýü¤¢Š(¢–€ J( Š)h¤¢–€Š)h(¢Š(¢–€Š( Š)h¤¢Š(¢–€ J( ¥PÒRÑ@ EPíE´”RÒPEPIJÏü–?ö_ý*¿¯£>2øþ#ü?Ôt &òµH€ºÓå ´Çyß ü9#i=ÏQ_9þËòX~<ÿØyôªþ¾à¯W7vÄ7åý%~X¿r½_æÏ3øCã¿øXÞÒüI2ùwì† Ø±´ÅwÙ2•þ¸ŽÀâ¾røû@xÁ¾$ðÖã=.ëÃ÷–º‹Mu‹¥Õ¶£a±Õ|—ʱ%ÊüŽƒ‚Hè=+Úv­ðëã–±¤ZÙO/…üuÔc–8™¡´Ô¡gWePL¸l·S´⨵ŸÙÏÂÒêZæ›ká·ÔÚóUŸSv»¹½…£uòÙäÝ ' ßÉ<+•=NÇ{h{uŸxƒEm[Åš*è3O+5½¯ž'[64ÅFÕœåT2s;:ä|àÀ:\º/‡Œëbó¼ñÃ4Ï2Àåż’±Œd.x$ú×_XË} ñ?Š<5ñ)¿h_ Ú¯Œ#[ë/R’Úãû><[À$MÑ߇'#æ$Žœ×QûCÃâ]3áß„!’æ=_]·ñ•²WÈŠ{•›1îE'j–À8í]ÏÄxêOˆø•à(¬ïî´kk«)ì¯eku–§rʨûYYGU5'Ä/ xÓÇþð˜–ÒÖÇTÓõ­7Q½€\bŽ;YƒÈ#”Æ¥ÎÞ™Uϵl¥³3¶ç-£x›âwƒ¾1è~ñ¾·oâ+ÙÜÍ‘Z-£[\[)vU ÌY ©ÆâO#ž9òíKãÞ³âx‰l<Ä'Ê:p1Ž@¹4EÄM3RøåãOà†|wáè µñ¥«[i³C"Èg0°¹T€A€}k©ÒbO#ž9Ùñ‚þ øãÁ>´Õmôû]jÃ\°Ônâ¶w[d‚ÚãÌ"6`K0@;Íž‚¶üWà]{YøÉàoÙù_Ù~‹PK­ÎD™¹£bãžHÏ#‹¯Ìvg³×‹|iñ¿ˆü-c h^ òS^ñ^¥m4ë¾;ue/$Å8ݵWÜšöšñ¿Œ¾×üc§hš·„%†=ÃŒZšÜ°Í°x€%C«uÀ¬¡kê\¶)|&Õ5ÛèšgŠ|?¬ßI¨[­ÕÙ³¸´–|F&) Ú1ŒýÈÝu2³$‹Åž0ø¹ðþí:ù<;xl¶¬ÞLrFèØ FCrG½/ìÉ¢øÖ/‡žÕ¯ÿãÞ·â¯x¬ ×ÿfíKâ¼+§k–vWLÆ·ä©`­œ©Æ@5KGðGÅ/…¾¿gà}LñO‡õ»é5êèÙÜZM8Dbb:|£ÁïÜŠôøkÇ>2ø-­xcV‚ÂÛÄšµ”ðùV¬ëh!;{e¸ÜØäòéU',J¹Ëø /Žž#²ðÿ5¿ØÚXßEÌÚDv9ÚD Ìå·‰ŠÜ ¡ŽÜ3^/¡|Bøïâð}Ì^(³·ƒÆ÷Úž=Ù‹“ƒ»çvX˜p£# œ“ö¯†4»­'ÂZN‹w·í6v0[É´åwÇ£`÷WÎÞø;ã-Óá¤7¢Ûw…5]^ò÷d¤*ôܼ¿”n?½\Ž1ÏZJkQ´ÎÏág‰Ôµ°.‡/gâŽ~5_ˆ7šˆm4‹/ê7ÚæÉf’äÁÈ rÇЏù€,Kö ¹â/ÚU²ø-àÿZ-µ¦½âùÕe™Y­­™ ,÷,Ê¥rxrqÏ¥øá÷ˆ|9¤|B²Ô„>g‰u{ûÛM¸y70Go8œŽp+ÎàøâÔø7à ím¼aà‹¯·[&[Y$»˜°×VÁ¶j¯ê-L߇ÿuñ7FðeÏ‹müq¦x‚9ÔÜEbÖRY\ĆE€­JŽ22kìŠñ _üb¿ñ¢ø—º>…¤Â$ûD°Þ5ÝÄ´„ò@Ž0ƒv2[w`kÚ+:–¹q<ã÷ŋφz>e£I¶«â+¿²Ãst¬ðZÆ y³º&Yön\(ç=°|ëáÏÆ=aþ'i^¹ñT8Óuèg+u‹YKgsv¸ÀVÔ6ÓÔÉõõ¯Œ¿5ZèZç…g‚øVõo¬…È& ±ñI· ÈÏLwÈ—ÂWßµ[ÿÂQám#AÒbGó¤†ñ®î$“O+Ĩ¹ë»q#Ò©5ÊK½Ë>>øeqãOøÅÐê hž¹¸¸xZ"æàN¨ ¸lÆÎ¤æ½nŠZɲì%|]ñâWˆü3ªk’Åñ[J‚ûOižßF‹OûB…L”†yT³¬Œ Êàž˜¯´kâý7á—Æ xsÄÿô+G¸µÖ®/¥\žà­Ç•xYˆ’-&h;ÀÏ8çJvêLînøÃãîþ|6ñ_‚ÒÞÓTñ…ý´°Ìžd;§V ™?0_0uãÞº/ ø‹âG†>1Ãð߯úÔ"³Ö4¹5kˆíVÑà’6<{T°+ŽA$žG¡Î,¼c€¾xq…·Û<«Y^_þôìò vgòÎߘàŒkÒµok—Ÿ4ˆù_Ù:v‘we.\‰|é¤ ¸L`ŒNj›ŽÞ¢Ôð›¿|^ø™àøÿÃÚÅ¥‡íN£ii¥µ¨ÜÚÛ†ŽI$Ÿpdv•ÛÀ=s^íû?Éð_ýƒ ÿÐkÇSáÏÆ xÅ |i¦]èÜ÷’ÙjÜ´RÚEy–xšo`I Á€î}+è?…ÔüðßÞÖv}»K²Š ¼¶Þ›Ð`íl •F­ Ejz|ðîÃâÕôß/¼ ¯[h¶zg‰5y#†KE¸k»#;+³0Ù6¨ÛÎrsÏÞuñÞ௞ŸÇV~Ót­FËÅÚ­ýå´“^—áí/MºÇik/´änœã"†Õ´ŸSå-;Æ_þ$h¾$øà½vÏGÒ4‹›¸´í:KE›íqÙç-<¤îS&8ÙÓÞ»«ÿ‰wÞ'ø;áÿˆ:o‰,<º¢Æ÷WWщÖ3†Y"…Y”3ùŠBç<™â¸Û/‡ÿ~é>$ø}à;3Pе«›©¬o®nšlRó龜ÛÌ)Ÿ”†ôíZ&ø#â#Áß´ÿ-®±wà–W´¾o. Òɉ8`­¸–\ƒ‚G\so”J? ¾1jÚ‡Åøwuâki×Ö-uüv†ÊXfˆÑ:à+‚£ Ü{×97‹¾.üQðŒ>#x{Xµ±ðý¿Û­ìô—µ5ͤ C´“îܲ:çnß”£ÜxsÀ?5?š_Å/ÙiÚ]…¶5šÙÚ\4ÍmÅAcff$œf¹Ø>üfðo†|Mð«Á6šeׇõ™nžËQ¸ºx泂ïïFЈÛ{¨8V y –ã}SCOø«xöoøx|3mæ»®ZéZfŸÇ ‹”UV~™Ó#œv¯Zð†¾/èúյ׊¼_k¯i²#‹›°-³$›~S£¨n¡Ç#œçŠáõOƒ^"Õ¾øGÁ°^E§x§ÂQé÷³ƒæB—–J8'*Hàã®8ÆEwÖ>5j:ͬ2ðö•£é°ÆÿišÞõM¸O%6 wrw8ãÞ¡ììR<Çÿ¼KáG^¸‡â¶’º†%ÃÛèÑiþ|a‰Š åBβ•Y² 1Î1]·Ä/Œž#±ø+ሠE¥ÍâCaö‹©bkˆ´ènÔ4“2/,·õ5Áè ~4øSÁz÷½LѦµÔ¤ºÙ®Ë;,æœçt2L¸à1|) üÀsí^…ñ#ÁŸ <)áJÓuOK´¶µ¿µ»£†DŽ-¬"”+ î‚;U>] W-ü%½×õ6º¿Ÿâ‡Ž´—DokQI ÙçsBÅJ‘ب ÷5áÃÏíýž¾3ˆj§Ão(¼þÎh·ûR'ÊߌîÃnÎxÆ+Ñ~|1ñN—ñ?Zø¯hÚw…bÔlVÉ4Í:_9]·£´ò¸HÓwÉ…ïùïøÛÁÞ?±ø¥cñKÀ6Ö:¬ƒL“Jº²½¸{\£J&YUI ¨*xÏÔ+¤Ý‡m|jñ¯‹þ|7Ó¯,¯"}Bk›[;½RKbÑ[$‡Ý4O Œíôæµ>ÞëÚ›Ýj|A°ñÖ’ñ¨F¶‚¤†ló–…Š•#±ƒÞºOÝüH>°¸Ðt=6ûT¡¿Óî®XA°©Þ‘O³’-ì+Êþ|1ñN›ñCXøŸ¯hÚw…"¿°I¦iÒùÂG2#´ò¸HÓwÉ…ï׎e[”}O§+óçã÷‡u½wöƒŠ÷ÂÒ%³°ñDzGÂøüiñù7¯si Fù3ÙÔ·Ê0Ùéózæ½7Â|KàÿÚøçMx?áAvðCæ$ xŒÏG·<Ö$`àŒqR|?ð¯Çï†úÞ“¢h:.§i©j·ZŠË>¡,Nû@R« ש­®¶DYõ1> k—>ø‘á_ˆ·zlz ·Õa6%‘ã†kóã„´xR7ÊWƒÚ¹mÆÞ#ø³ã„úwŒcŽßZÓu}R{ØbC©³…&…Š’ØÈ÷¯[øÉð›ÆŸ<5àÈõ;{K NÇPVÕ"Šáš%³”mbr€¹!T€@Íkè¿uM3öŽÕ~)·”tK½<ˆT9óùÄQ1òñ€¦$<ç“Ú’”mæ ;žð{Qñ%¥·‹#Ò>EãHOˆoɽ{›HJ6#ýÎÙÔ·Ê0Ù|Þ¹¯Cý u+]? õ_èÃG·ƒÄ6’Ýiè«r E†bñ…ˆ|z(çÒ¯x Âß~ǯizƒ¢êvz¦­u¨¤³êDàOµB•XXp½Mwž9ðo¼{§ü?½¾µ³²Ôô=rßRÔ!Žáž$Š$‘O”åsó/ô9.k‚Z1¤]é~4øï£xëág‡®4½AÓïµ/šÍ¬a¹/ ¬Qì!LŒ©ù†xÏE…¾ ð÷‰¿eïëÚÅœW:—ˆ“Y½¸¹•L'‚I’&Þ~aå˜ÃœIîs÷+¢ÊÊà‚=Aàׯpü2øßá/ x‡á„bÓo<9¬Ír-u+‹—Žk;[Óûä0;˜m¤0Ãyà§rœlpsêw^6ð?ÀëìgÓèãY!²p_=ê)îT¶7>8ø×Ç^Öü ¡xâÞÚãÄš„–’µÌ^l{v® ƒòäžÏJg€üGñFøµ©ü.ñƱˆ#}.=ZÒí-–ÕÐLO*zãõ§x£Áß|s¬ü:ñ«§Ùé—>Ô庾‚+¶V"©ÌI¼œr Œz×MÿF¹ÿ ßþ'?°?³~ÿï|ÿ´y¿sÛ·¾zöªÒÖ·=‚¾UøÝoˆþ1ü)ðn¨‚m*[›ËÙap)¤·Œ4{”ä¸#³Zúª¼;ã'Ãßø¢ïÃ>2ðD/ˆ¼%v×ñ\³$JK²‚W;F¦GzŠoQÉhqSÙYx_ö­Ò†‰ ZGâMãí‘¡I-¤ÊJÀócåÏ õ5|…ðÂxÃö€×üEñNµáí2++=:=óEöiÜ»\-ÁP’o`@ /.2­]•÷ƒ¾0ËñîÚë ¾D@ö_kD[ýï9ëïW8ëo!EŸEÑE‰bÒQERÑ@%´”P( ¤¢–€Š( Š)h(¢–€ŠZ((¢–€Š( ŠZ(¤¢Š(¢–€ øÀ¿ò}ìþ‹Ó«íúøƒÀ¿ò}ìþ‹Ó«ÔË~ßáš81ÛÒÿü™öýR×–w‰E´”QK@ E´”QK@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQEQøRRþÿÒýû¥¤¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢Š(¢Š(¢zZJ(ö¢€ (¢€ (¢€> ý–ä°üyÿ°òÿéUý}¿_~ËòX~<ÿØyôªþ¾ß¯S9ÿx~‘ÿÒQçåŸÁ^¯óaED÷E,pÉ*¤’çb’luÀïjHnmîT½´«*©*J0`ê8ï^YèQEQTïu-;MA.£uª1À2º 'êÄU˜¥ŠxÖh\HŽ2¬§ PE>Š( Š( Š+;RÖ4 }^ö%º™-â3ȱ‰&á#RÄeØôQÉí@8¢Š š¶–ó}/!is‚E-ŸLg9  ôRÒPE´”QYÿÚúI—Ȱy¹Û·Ì]ÙôÆsšÐ¢Š(¢Š(¢Š(¢ŠZJ*•Ƨ¦ÚO­ÕÜ0Í/ÜG‘U›ýÐNOáWh¢Š(¢Š(¢Š(¢Šäüa㯠xÂÓSñ†¥™ow…¹“s®®[lQ"¨ffcØ$à@YEŸ§júV®· ¤ÞCz-ekyŒ2,‚9“£}¤íq‘•<ŒŠÐ¢¹;OxFûÆz‡ÃË]N'ñ—k íÅ̲¥´ìV9F@ ¥©88Œõ”QEQEQEQEQEQEQEQE-%Q@Q@Q@Q@½Q@ (¢Š(¢Š(¢Š(¢ŠZOz(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢Š(¯ˆ< ÿ'×ñþÀ0è½:¾ß¯ˆ< ÿ'ÕñþÀ0è½:½L·á­þù£ƒ½/ñ~Œû~Š(¯,ï (¢€ (¢€ (¢€ (¢€ Z( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(æŠ0(ÿÓýü¤¢Š(¢–€ J( Š)h¤¢Š(¢–€ J( ŠZJZJ( Š)h¤¢Š(¢ŠZJ( Š( ŠZJ(¢ŠZJ( ŠZJZJ( ˆ?eŸù,?ì<¿úU_o×IJÇü–?ö_ý*¿¯Hý«þ+ê¿ >݃7ŒüW#à_í[â_…s1‡ÂÿROh@“åÅ«B?âej‘Gž c: ú;àw½3à¯ÂŸ|5ÓKýj©<Ã?¿ºžâcž~y 0Ï8À&¼«ö¾øk­x×á„~0ð8+ã‡WQø‡Dtûï5™ß-¿¸ž Ë·¡`›²2+ξ¶;¬}YEy÷Ÿˆº'ů‡ø‘áéK ~Î;¤Ús±˜bHÏûQ¸e#¨ ƒ^ƒPQùŸðƒáσÿk?‰_þ&|l°_麈.|9¡i·lïcei§áZDƒ;<Ù²®ÌA!‹cµ} ñÆ~ ý޾hÚ'ƒ4v¼ûV šNƒ¥ÍxQîúG‘#{©ËypÇ–%˜¨08ã>Oˆÿ²oň‰€u¯øÇš¤šþŸqáøá¸»±¼ºææÚ{yd‡ ¼üŽ¡z’ÁuÿhOøûöƒø5àÂW\𮹹/„uIci/l¢2Ã%¬,K$ ¼) òüÄ|Úuò$Üð/í;ã{_‹>øUñ‹NðüoãH®I¿ðæ¤o K‹U%µÒ8 ¬T’•‰ÀHæô¯ÚWöŒø…âÿ‰~øSà î†úÅÞŸ-Æ¡¨MÛRuŽ(Uc8™ÂÌNÅÊŽsSü$ƒá¦©ñAÂ?³&£à¹`i%¹ÖõM.ËNM=£‰™<–ŠI^gi fnÏW†|ø·ã†?¿hh´†úߎ,ujŸc—EHe ~’80Ý‘8Ø2 WÆ OXW>¡Ôÿl 5ÿgŸ üf𿇦Ôu¿^A£éš#Ëå±Õæ• kyeÚv¬RFûÛoEè;\ðoÇŠú'Æ à×Çß éºEçŒ-nnt]CFº–âÖYlÔÉ=´Ë2«,‹Ü‘_?Íû9üZðì­ðÎ=3LM_ÇüJž1¹Òc(¸’k‹‰îm"rJîT¸e^¹#ŒœgÐ,?á`þѴÿˆ7^Ö| á†0j7I¯ÅµÕåþ¡€C QK.Q6†/œ|¬7.K!ÜíõŸ|TðWƯ-¤þ"ñfµ5ÅÕ² Èí®"€Aum!— ¡ ¦„Ó:ÍãŸþþÉ_uÏŠZlú§Ä-tZi–³ Yf½¸Þaû\ÒÜ„…7ÊÎ21ƒóšÜøûLøâ?‹zÂo‹úv€’øÆ‡ÒujFú¸µC,–·(à26Á”pv±àšð wáGÆï‰?³¿ƒôÏxM¼Cãƒ%vŸ¨°ø›K³W„¼/!*Þt¥Kd–B$óì øm©üJÐS³.£à™-ÌÒÜ뚦™e§.žé ”òLRJó;¾#ÂíÀmÙ8"“H.v:ßÇÏ‹>5ø£â†?³Ï…ôÍQ<b‡XÕõ»©`´[É—pµ·Ž/#(;‘ÐŒ³ãŽŸ>xsáLj¾!xRËNÄ:äZ'ˆ7pšk]3Gmq %êmà kÇ^ ø~ºÝ¥ç‡¢†æêÒõ×ÛÜ[Ë,D)cò>ì`VÙî?<1¬~Ñÿ³>¿¡kžºð¶­â ¤µÓîÝîÖæ2Y³”ùRMè€NÂq“ŒÒ²¹âOŒZ¥—íá/¾Ó!¾þÒÓ/5bêIZÆÊ"€¢¨Ã4Ó€8É ~;éwŸìü/ñÃPø›àÄ>&ƒÄúÿö~³¥Ù]´6H"ˆÀ­ h¦Ý!ªÀ°*ÀÑØçÂ_u]oÆ_¾:h²è^-×ÓMÑá´J´VZ]²+:ƒÑ'ž\vbG8¼à—‹~/üƒâo…৉¼Owâ/êÚÅŒðG:|°\ÇQ‰&–MÊÂI"6HÆN@¥ ™èÞøõ­|ý‘>êþ$¸ƒÇ>1ñ(·ÒôÅKä]\ÎÎc^¶åT‚%W9 ®ÞZ»/‡?´·›âî‰ðƒâ%׋m®gÒ/ü9¨›ës5¢eµ¸G‘ü Y_;_i_3¿ì‡ñ/Dý˜~XêÚ§Š’Ñg–msTÒì´Ñe ‰‚¤&%y]ÉØq´s’8¤ì4ÙÔjßþ/øëâgо~Ï~Ó5Ë®­«ë—S[ÛÙwÙmâ… » ;“…8ãIßø•ñûÆß ¾xw_ñ?ƒ£“â'Š5´KÚðKo&£3Ȩ~Õ°b~am™…Æy¯ Ю>#þË¿>%Ësðÿ[ñÏ‚¾ êŸÛÖ¾†«›[¹cTžÞâ fˆ…áB>ìqžw›ß4/Š?¾|8øÁá¿\Ùx¯À¾!·ñ ðÝ̨o%´ä¡ÜA;FR]™8åfÁ%\ì<ñÓ⮕ñoMø5ñãÃ:n‘©ø“O¹¿Ñ¯ôk™n-. ˜Ý=´‹2‡IQàrCxóæûc~Ñþ'ŸÁŸÙ?4mñãT°ÑMJpc¹Ò܉%º>"eRUæãïsŠô-xûö€ý¤|ñ>çÀšÏü)ðßNÔʾ½V×wºŽ§ ¶òâ†9$ýÔq–%ËrÜ`pO ð«áWÄ}"ÃöqTðíå³xcWñ$Ú˜xñö8î„ÞKKýÑ&á·×4Yè?ðÚ’Û|ð¯ÄÏBþ8ñf«/‡ítav!´þÓ‚g†F{©#·]¡™ˆ$n ÏÞ®á¿í)ãI>-i?>.Xh)}â‹K›­&ÿÚ‰¾¶i-ÌšÖtq½$üÊÙÚá[b¾U»ýš¾!ê¼'¯\ø3ûsWðu^ ßCªiW—LeDe ²*20Î9À?Güƒá¶¥ñ'J—Áß³n¡à),¢žY5ÍSL²Ó~É!Œ§—“$¯#8vBAP› zÁÈKçgV‡È•‰ÈVÝœ =±ðÆþý~iþµÆ:߀uÝ3ÅwÚ|©/Û†ùåš+Y”“gÚ@VçTÈÉ Ákx—âæ¡¡~Ð ø5¶~(ÒuMJK¶vBú{BªŠ€`‡óNI#â¼ûö’øëâûïkŸü ¥êþø}}u§O>¥},š¥Îž3v¶il¨åUŸvâ8†7‡µ?‰ß?k|Rºøs­xKÁº?‡õ{æÖ#Š+–¹¡g2Å ’ˆ•°¢,¶_k:W)áWâÿìÝcñ7áM§ÂýsÆ'_ÖuMWÃZž•2ØHš»w®ò«Ba‘Žã†Ü½—²°î}%«~Ò¶_²•ïí5à½<]G.¥•Óñ4 c– æ9U²ädqY? þ)þÒŸ/ü3⻿‡úN‹à-yG3j26¯ D]. B1 Ûu’Á[%²¯1ºøãÿÁ<5_‚0Ú6¯âÃáûÕk[Aæ3^^Ì÷/x$?–ÒpÛsÞ¾Ùð•ΙàOi×±{‹]6Î)#a†GHUYHìA¤ìž%à:¯Œ¼ ñGÅ×:DÒü?ÕõÝ6’Ve¹M 1Gr@*dÇÌqÛ5ã—?µoÅMoÂßføá.óĬonD·’ÅkfÖvÆåŽõBΡU¸!I A9¯:¶“ã»_Žôß…ú߈îüm«kZž‰ªY¬?ÙO¯OÚ'y£h‰#j£–8\§ká_Â߈šCþÊGSðýÝ°ð†‘¬Ã¬oa’}5âfþéw!G½; ¹¹ðgöºø«ãï|=—Æ~ Ót_ üB¸Ôô«Y­¯dží5-!dóË#"¨…ÞV1ËaA'œV÷ÄmÿnŸ†Þ¿và]ûÅIû}$‚Ò)w1nFCÔ{Ÿ0ø_ð£âF“¢þÎðj~¼¶“Ã^/ñM ¥µÛj>D²ÿudóSiï¸W¨|uþ~Õ ¾=]e|?¬C?ƒµi¸ n÷¬d±w=•æ;I<  uaF—б|iqã _ ÞÜxÎÏP×Ð'Ù ¿™í휗Pûä]— ¸Œ)ÉpkçØÿİøŸÂþ<¸o XxKR²ñ†§k©A§\KsÍôq[™®wÊ·HX õï¼qqð÷ÿðZøcXñsùÉØ´8"¸»ÃƒóìšXWbãæ;ò285ñ/ì›ãxCYñW„üSð§ÆZøãÅú–µo=²ÙZZÞEOµ¿Ú·£ƒ d®ó‰[ ½N›ö›ó|ûD|ø³¤/•q¨jóxSP`Hóìõ$ ?¨•äQýì¾𯄾4Û¿ÅßÚËáGà 'sÚ|9ó|]®H¤„ŸÝiñ7ûrH­Áê…˜}Ó_vÐö@‚Š(©´”Q@ (¢Š(¢ŠZ)(¢€ )i(i(¢€ (¥ ’Š(¢Š(¢Š(¢ŠZ)(¢€ (¢€ ZJ(¢Š(i(¢€ (¢€’Š(¢Š(¢ŠZJ(´RQEQK@%PEPÒQK@ EPÒQEQK@|?àOù>¿ˆ¿öƒÿEéÕöý|Aà_ù>¯ˆ¿öƒÿEéÕêe¿ oð¿Ííé‰~Lû~Š)kË;Â’Š(¢–’€’Š(¢ŠZ(¢ŠJZ( Š( ¥¢Š(¯ËOÿ¶/í.Ÿ´¦«û2~ÈžÑüaâ éPêšÃêóyARq b!%ÕŒxE¹€’%rÅÈ 61®/þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËê?árÁdè„ø3ÿ ÿåõ~¿Q_?ð¹?à²?ôB|ÿòú“þ'üKþˆWƒ?ð2þ_Pëõ-~@ÿÂäÿ‚ÈÿÑ ðgþAÿËêåüMûqþÞŸ³”š7?kŸƒ~Ò¼©jir\i±‹ÄžtyC MBÿvÈ¢‘ö´Jnß1 €?i¨¢Š(ü)) ÿÔýû¢–ŠJ(¢€ )h ¢Š(¢–ŠJ(¢€ )i(¢ŠZJ)i(¢Š(¢ŠZJ(¢€ )h ¢Š(¢–’€ (¥ ¢–’€ (¥ ¢ŠZøöYÿ’ÃñçþÃËÿ¥Wõö´övwRC-ÌÌöí¾6u QñÊOCŽâ¾)ý–?ä°üyÿ°òÿéUý}¿Íz™ÏûÃôþ’?,þ õ› yµ-%yg W´´´°·[kRÞÉXãPŠ2rp$äÕŠ)h(¥¤ ¼¿áÇÂ? ü/Ô|]©ørK™&ñ¦¯>µ|.]VêãªíO@Kzõ (¸´”QEQKI@Q@´”QKI@´PQE-%´PQEGSÓ4íkN¹Ò5‹X¯¬ocxg‚tE,R¬ŽŒe`H ŒNÓôý?H°·Òô«h¬ì­#X ‚XâŠ4TDPU@ÀJ»I@Îé~0ð–¹6—¢ëv7÷¶ù2Áos²Ç´í;‘°Áàäpx®Ž€ŠZ((¢Š)’E˲T¹ 229¤¤ Š( ¢YU@À Àr@é“íO¥¤ Š)h(¥¤ Š( ŠZJ(ïE-%Q@Q@´”v¢Š(¢ŠZJ(¢€ + \ñW†<0°¿‰u‹=%n »ˆàW¨_1—8ïŠÙ†hn!ŽâÝÖX¥PÈêC++ ‚à‚:’Š+Æ›Zo E­Ø¾®¤ƒf·1TdƒmüO(¢¢–’€ (¥ ¢ŠZJ(¥ ¢–’€ (¢€ )h ¢ŠZJ)i(¥¤¢€ )i(¢ŠZJ(¢€ (¢€ (¢€ (ªVZž©yãN»ŠèÚÊÐËå:¿—*}ä}¤íaÜG¥]¢–¨ê¦›¤[ý¯U»†Ê ÊždÒ,i½ÎÕ]Ì@Ë€;ž(íW;iã jľ°ÖìnuX ‰-"¹‰îÆpá¢V.6žG袯ˆ< ÿ'×ñþÀ0è½6¾à¯‡ü ÿ'×ñþÀ0è½6½L·á­þù£ƒ½/ñ/ÉŸoÑEåáE-%QK@ EPE´”´Q@Q@ KEQTlu-;SYŸMºŠímå’ .²š#¶HØ©8t<2žAàÕêI¦®†âÓ³? > ÿÊd~;Ø™gÿ¢4ý¯Èƒò™Žßö&Yÿè¿_©ˆ(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¯’~?þØß¾ø»Døos¢k¾6ñ·ˆak›M Ãv"þøÛ©aæºL!(øÚY¾Rvà_Cø Åã¿hþ0}QðëjöéptýZ²ßÚïÿ–wå¶H;®MuÔ´Q@U ;UÓ5xçI¼†öb…à‘dPëÕIRFFyz–Š(¢Š()h®#âF½â¿ øWñ‚48§û9¦¨¦­¦hk®¶¢Ñ§ö{ÂÒÅ”¯¿›™AÆÌ`hÝ)h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ üÿ‚Õɬø[þÇ;ý7j5úý_?ðZ¯ù5Ÿ Øçcÿ¦íF€?_ih¢€ 9¢Œ ÿÕýü¤¢–€Š)h¤¢Š(¢ŠZJZJ(¥¢€ J( Š( ¤¢Š(¢–€Š)h(¢Š(¥¤ Š( ¤¢Š(¢–€ J( ŠZ((¢ŠøƒöYÿ’ÃñçþÃËÿ¥Wõöý|Aû,ÉaøóÿaåÿÒ«úö¿ÚCâò|ø?®øêÞ/µjÊ‹i¥[m.×:•Ñòí£9oœî*9*§â½Lá´?Hÿé(óòÏà¯Wù³Éü#â|`ý«|Ey¤j÷VŸþ[d½¼²[êZõØß?š‰Ö< ­®Tެ*O„þ(ñ7€¿h¿ü ñ¶¯uªYk‹ÿ G…§½™¦“ìr‘å’;’H¶—'}kÔ?f¯„Óüø?¢øCT“í:ì¡ïõ‹‚w<úãy×.ÏÎ㽊†î5åß¶G„µË h< ü#¼þØ×Za5SŽHx2ØêB²® f¼î¶;¼Ï²h®sÂ)Ñüqá]#Æ>˜\iºÝ¬7–òhæ@ëÓŒàó]%AGÉþ4ý¤|ká{[´Ò> ø§^Ðü=$‰uªB-¢IDC.ö¼»îv#i8>|5ý¾ø?Æ6ºÎ¯ñ3úö«—g©Gpº•åÄ—"y¼Æ+æ€ahÔsêkäÏ…?þ|ø¿ñçNøÊÂÍKÆw³ÚoÒç¾Ý ³)!¢†@£wbEw¿µ§Åx§à—‰Ÿ íeÕü=iñ K–+{{9a–Ak é•#·tGÝ•;FÑ“ÓÖ›Z‚>¤økûKEãˆðª¼oàÍ[áÿ‰®ìßRÓmõ_)’þÒ6Ú)!vQ,yâ<žN8¿þÙV:f½â«?|>×üq¢xWƒ^Õ´Õ„[ÛMïš8D®­;D¼¸s’¸'Ìì>#xcö–ý®¾x£áÜj¾øq§ê÷z¾¦ö—°Ç&£lÖ°ZqdÊKï#O9{ð»ãŸƒ?e]â§ÂO‹¶·¶¾(ÿ„‡WÔô»qcq2ë–Ú– »A,q²̾Y.TŽr*åòϼõŸÚán‰ð^?—:›Ká+‹X®`–(ËO9‚G q3H|½‡7&¹O†ŸøëÃÿ¼¢|@ð¬.•¯ZÇwnd]²rŽ U€$ ë_þά¾ý¤ÚHHi¼mâ2¼ºÿgÚãê98®ÿö Žh¿d‡QÜ+#­­ÎCÿLŸj$#¦ý•ô?øáþ©eãK[»K×Ö.å/7ù†H¶•ßÎÒAÇnµôµWni˜ýü#áh¼!ãŤZ&¿?ŠõëY5 §·[€DM.7”¹ÅzOÿiŸxCV×ZÓà׊uo ømå[½Z!kÉ#2Mk’‡ž03óez+”ý‚å‹þ‹"7ÿÂc¯¹çxçñOˆË¢¬&7#ÈüŒ±üÚZì‹è~ ø‡öŽøUá¿‚öŸ®õ'›ÂÚ”Mfa‰šæéîp"‚(ÌÍò”8ÚC*ˆÃøgñÛÆ>7ñE·‡¼SðŸÄ^ ¶Ô`’âÒúÿìò[‘ åÏåHZ X•9ÁäWÁv~ñv«û|ñO‡tyõÉ~ë–~!¼Ó!RóÜYÙÝÝ,«x%ÝD¶ÿp62p+ï?†_µŸÁ?Œ^'³ð‡Ã­NïVÔîm¤º•?³î KHâ"y&X“´-“í‚SˆÓ0|cûR›k>x Yø«øhGý¯&äÁgdòË O;Ómù¼µÙÎ@ê¼YûGøWÁ 4_‰~%Ñu{;¿K¥†€ÖÀë3ßÌJ­¢À¯™•9%öÎyü³ð÷âïƒ?e/‰ÿ|ñÑî¼?мGsâ=#U6w76º…¥äqƒ½¼r2»H#`g=cý®­í>.øáÇ{ 'Y¿ðF«ýV 4š×U‹L¹PŸlDˆ‰“ÊòÃåH`¤´n*ùUÂçÒŸ ¿iEñg£øYãïêß|Q}h÷ú}¾¦b’+ûxˆf…™|Èò ¡Œõ5æW_¶ô—Œ¬ü!ðÃÄž%·ø{©^Øk—V‹n!·[)HËH ¬Á ˆÀ& Ò¼÷à|±¯Š>3xvóá.­â/ø«I†î{k›¹õyílcx¶ÉæµþÔ_0aB€I`2ygÂ?Ú_á¯Á¯øh øñ.íuÿx–ãLXìn'MU¤™âòb’(Ù<Äpƒ•2œã89P\ûcâwÇ»¶øÄÿ„£âËiWv׺y‚3§¯Ùˤó¥Ã©[}ä˜2WƒÆ¾ Öck")åñÛÚ}–öP£çTŠO0y¹ÈÌkû=ü/ñ…ÿb-?á¾¹fÖÞ"½ÐµcöGù^5W¹¸Š]êŒFv¬Ø»ãÃíSá¿‚þÉqw§ø÷ÚðtòÛkΚ­¬S[ÿÇÄvÂWV¹hå @ìH®ãÆÿµÿ |2ð¿Ä½+¿Çã™- Ð,4èÃÞjC)#•WŸ3qH#°ægÿ ~Ï­uaÈÁ¯:ø!ñûözøG⟌šÆj×^>ñ Ì!ô›‹ÝÖíváH’8dd3NÂ>Âñ7Çÿ‡Ÿ³¿ÂkbÿVø…¡x†âN—P¼»¤ŽbìÊf-÷Wo'ï]ÃÚbÛÆß%øOãojß¼W%—ö•¦«å:ßY†*Ï °».ôÇÏäs‚v¶>aý¬>(x;\øðâ¿€lçÖ¼;oãm6öÚÚÖÒHçš+_zClê¿10EÚ2@í[Ú7ÄO~ÒÿµÿÃïü 7§†>i«êš¹µžÚ;SEŠPn#Œ—ùwmÇ*XŒí56Ыž—âÛ2ÃNÖ|Q‚¾kþ4ðï®%µ×5­5!û5¼Öÿñ𤎯9‡ÌÛ€0H$r}ƒÅÿ´WÂÿ|³øßy}%ï‡5XmäÓŤf[›Ù.Àò!‚#ŒÊäãi w5ðÂÿý—¼ ñCàÅ{{û&µ¬Üé¶±ØÜNuÈuþ‹-¼‘Æco7€K2ã¡ä0ºßˆ¿d€÷÷šÖ­¨ü*ÕôkLd¹û,S4ÓF‘ã,öêãáÚIà9P\û3á—ÇOøßÅøsÅ¿ |Cà˜ï­ä¸´½¿ò%·eŒc˜Äå¡”ƒ•F8<Œsñ/ÁÚk\øc៊ŒÞ ñŽ­´x‚óS½µ(ÐiöU ç|ÈQQ›ËŒaeŠæ¾ÙøcûWüøÉâko |6ÔîµB[i.¦Å…ÔÚG8žIãC18P¥²Aí‚~nýž-dOÙçöGˆ‡—Å3à¯, c_j>@ÏÐ øŸHñ¯…ôŸø~S6›­ZÃylä`´S u$v8<Šè+ç?Ù%OÙoáRN¬²/†ôÐÁ¸ ù ×<×ѵ ¤~axGÅ>ÖõÏÿÂÑøÉ¬x^úÇÄZµ­œzÃ["ÚG'îÊ¡ÏåF8ÀÅ}!ñÃĶ_ ¼;ã߃Þ$¾ñ ¾Û¨'™vÓ.·aÖhç#‰INQˆ$cåä×ü!ø¿ðcáÖ£ñGø”ë£qâ½Rxƒéó] HHt‰Æ2­Æ{OÅ/ŠøçÂ>øsðVr/þ#+ÅÂÂð‹*2Rêå‘‚”à2 s·‘šùÌ<áì¥y+ùo{é×½º´ç8lG×é(Ñ’¦šÖI{>^_{h­9oy»^Ú“|4ñžµû@|I_ˆzýî›ðóÃ0,6Öáží-JtW”Ì ñÛiÈ/Ó#vyo‡o‰¿isâˆzÿ‹u½BQ¸±Ñ¬4{¶°T‚ÜçJÑüÒ;‚àxÆ+GÀZ\_³GÅ;O…‘¤ð'Ž#WÒ¦•Œ†×U…&‰Øô ;o (Ævòÿ ¾#x{öc·×þ|_{!m5+›½&÷ì³Ïý•Á †6$Âàq‚vžA­#/…W}_7kéo•¶ÿ3޶Ú¬²¸]òÃÙY^^ÎòçvµÜù¬§mV¶÷NöŒ¸Õ|ெš ÿµ->ÕõË{-OX[“ms%¯‘1g–DÀì 8ÆFq]GÁÆø[uã |ñkSñ¥ü6ò¹°¹Õä^^UZC?„‘ƒØšâ¿hßøoUð¿Âˆ·ÐO'‰-oe[›v2­·Ùæ$¼*ò9Ç5ë¾;|ñ¯Š`ð߀§S¬]G!@ºtÖÙH×{ƒ#ÄŠ8Á<Ö‘”=»÷—K_}ºjqW¥‰YDZ£6ÿyÌâ—*|ÎüÞëÛÉ«#燺/ˆWqžiˆƒÚ3’Z%d¤ì´*Þ³-œî§ ±±v85òìâ¯xÓögð÷ˆ<_ªÝkZœ÷:‚Éuy+O3ª]Hª ¹$…PôõÆ©}—¦]êw*Ϥ2Lê£,V5,@©Àâ¾hýõ/†‡À­.„z†¬n¯!†=\'ÚüÃ3K!b„©¤;}¸®þ‡Êu<çö ðׇ¼aûB~Ï^ñ^™o¬iWšŽ¸&µ»‰f‚@škºïÁVÃFGP }iã jïÀ^{ÿ øbëÄO§ˆ!ƒJÓ<˜æhË,`F&xã Ä(ã°¯˜ÿhc‡öšýœä•Â"êZöYŽÿ‰dÍ}ñCâ÷Ãσ>ƒÅ¿5S¥iW)iËoquºiÝWe´r¿*Œs·ry>€x×ì{âO ø«á®³«xVnÚñ¦·ë× qw Øe3F­2ˆ”œ"ƒÇ5ç_<áMöÎøâ G´°ÔõÕñ+ßÝA G5ÛEknÌêr»Ž Œœu¯0ý‚><ü*K WáSk2/Š|&Ѿ7꿲„ß´§†~-x‹þÍ%5]A쵑¥Üæ\L'†ufPaª°ù«ö ïþ=&ÿq¿•~(|øû¦Z~Ç?³×‚t-_Åõxu­54ë; ŒpNæp²MráaDI¼¶âñ`d‡…#ôþÖ^Ó¾ø⦫¦Ý_k?†Ú=7BÓ\Þ]ê§ï`IPR74ŽTŒü̪w~~ÑqøãÆ× üiàýSÀ>1†ËûJ HÅ"^YnØÒÛO 2>ÆÀuÀ*O~qñÆï€Sü1øû<ÞxºÛPÕ|)ðÒ;‹O>‡$ésho¡B÷‘|L"Žeb줡A1Ǭ~Ïq~È^"øÏeªüÔüAâé:}Ó ëÙõYí-m娝½þÕ &àUTTçiò«Ù‹oûNümÿ†¦½ÐÃI¥'…uðÈ“O󒶪ÿhoóvì+û¬y¤äçozúëâGÇí á_‚<;âO躋kÞ*6öúw‡mcKR{ùãmB£ù{¢Î$}ûŽ’ üÓñ?Ç:ÀÛN/ŠßÖïNð~¹àìˆuH¬î.­Òú;ñ9…þÏŒ­±A/ñÙ#öÈÒt‚ÿĦµðßIšk­N] ÜÛÞÛéú­¼oò,A.#TÀi8V @ÉÁkØ¥þ~Ñ‘øßÇW |má SÀ0ŽÏûFÞËS1H—–{¶4–óBÌ®cl^ÏÁÇ)âïÚëMÐ~&ø—àç†|®x·ÅúÚI¦œ‘¸Šæ6‘æi]À†(~Dvq’Ò Ua¸"ýŸ£ý êž!ñG‰ô« §[ÛÙõií- ”*¼n÷ûT4™UAåNpEz'Á%ÿ†Ãý¡ïå‡ç[o $rü-ov\èJ®~‚“H.?Ŷ%߆mü/§·Â¿\ø£Å~£|š0[t¹µ\¦)Äå¤ÀBº:†Ü¥~P[Càçí‹áïŒ>0Ð<9ká gAÓ¼]§Üꥨ,K ,J-Ò"£±_,¸Á'æà3•ñR ö¿ðD "ø'ăv8Ékç€6³Å?ì…˜™<­ÄÁþR6å!àúf•‚çÐß~$éÒ||øoàßx_ÄúHƒ]hô}VÚæÞ-/P¸k|°S,‘('åd0ȯYøëûDxoà$þƒÄ>£¬?‹¯žÂÖ=65šo=TQe,Ò3*(_â<2kâÏÚ{ö˜ø'yñ³á<6Þ ‘ÛáçŠnŸ]ÆŸ}‹EŽ&…Ž|ŒK‰ßÝoõs^‰ñŸÆþøµã¿Ù“Ç^¸m[A¾ñUÛCpmæ‡&Þ6‰ŽtIl‘7(鑯 » ç¾|"ý££ø‘ãÝoáo‰üªxÅz=¤zŠÙjf'ûEŒ¯±fŽHY”Ü2ô€[ Ž [ý²¬âº×u?|;ñŒ¼á[‰­õMOHEª=±ÅÁ¶I^åaÁÞÈ$0MKÈfoø(t2(eŒü5‰7À?Ú·=úg½|ð«Ã_³wÂê? ?iÝKÄþñއuwöð]k)e©Û<‡Ê¹´K0дs!<îïE ³Ò>4þÒ>ÐÿjâŸÃ]jÿ 4Ÿ èÚö©¦Å+›[+Q¹–Úk¥ƒæF$GÉÁR · ¾§ºñþ¿ûxøgÃ:N»q/„µ/.¦¶qÌßcši.îBÜyyÚX PÀá< ðþÚi¼á­îßÀº¯Â{8a²ÔÖC8³¾½™ÚÄ…˜6ÙH*I+Óµy‡ìç¡x×Býµá7‹šo3áÿ„ï¼9g~r^}=n%¹²›$cp‚åßn{à6„³5oÛSN_í¯ø3áçˆ|]à_ O4ˆôøáû(6罬r:ÉsX;@®F ö ü}ð‡‹þ$Úü;Ñ¢–eÔü9kâ?Q>Ëycs!ó¼2 ZüžøU ~Í? | qð¯ö›Ô"¶Cd&ÞY„ϵ¶±;H"‡ Ÿ¢ž8øÇ x+Å~ð¶—T½øƒ}5›Z²2E¼&in$%†cA´¹9a_”ßüwðËÅ_ˆïþ8~Òÿ„|Aoâ RÞ;âG´T³ŠlBV&ɨ§t¯Výš|âM+ö£¶øU­$÷'À-R·Óo'äÜrå^=‡ú) :ÿ«úWû.~Ðÿ³‡Âx£Â¿A®Ÿë7_H¹¼>D“ ‡ÌŽ\3Å [`¹ö×hŸüð·Ã=uŸˆPø¾g¤ÞYº_^_¼vêÐ;»²^ä•ÇmÍ…É®»àÿícñ3ź×ÃoxgQð?4(ò]+SòÝ¥²”…[ˆ%‰™$@Ä+t*Hê9¯”ÿi/‹~ ³ñ¿ìÏñD·ŸRðª_j7¨-­%óVÅôöWœ[lo32íÈT'VÏÃÿi¿´í{ªüYøe%Ô¾ ðÿƒ‡ßW{i­¡¸Ô.nq"tGcI–àm*3Ã!*Úú®¡ûthC¬x»Bðˆ5χ»{KïÚÇÙÄÁeš(YIJÁ?4€@#ûÿÅžøWðþÃâ§$ú­®¶öðé6º|~}Þ¥qx3VÑ’¹gò@©ÎüÑðGÆ¿ ü'ý’Õ;ÊO%÷I‚Oçöøià¿ø_ÄÚ4°jױ躔6ñé:Œ¿c-/ŸÊe’4_ºšú'ãOÃ]3âÿ¿ü8Õ4Zå”°ÆÝ㟡{¤[ðÇJüøý¢¿iï÷¿´Áû«oÈðü<×µq®°Ó¯ñi›V·òïûßÞ¿ºßë÷y¯¼uŽžO‚š—ÇmPûg†m,./¢¢’/“¹BçXÜ3È6(`2Hõ¦Ö¹ÃþǵO‹Ÿ³ƒ|_¯Ê׸¶6w²¾KÉ=›˜LŽO%Ü(f?Þ&¼£^ðw„ü1û{xQðÞi¥ÝkžñÕü¶Ð¤Owpf¶&Y™@.ä±ù›'“Íw¿°Ï5o‡ÿ³ƒ4Ýy=KR†MNu¼ ô†dÝžwÙKÈ$ƒÒ°¼q42þÝ V7W1øOÄA€ àùÖ¼t£«‡Úµðg†µ­Aý¸~!^ë·öúu»è–ñ¬—2¤(\ŧ¡œœq××ÞUð“á_ øÇöÚøƒ¥x«MƒU³‹F·a¹A",¢=C…n7ÄgÐ×FÛ{:¾Æ×åë{n»TT}¥?o{stµö}ϲádü:ÿ¡§JÿÀè?øº_øY?¿èiÒ¿ð:þ.¹ÏøQ_ÿèLÒ¿ð?ð£þWÁ¯ú4¯üü+篎í½ÿ‘ïòåýç÷GüÎþOïút¯üƒÿ‹£þOïút¯üƒÿ‹®sþWÁ¿ú4¯üOð£þWÁ¯ú4¯üü(¾;´>ùråýç÷Gü΋þOïút¯üƒÿ‹¥ÿ…“ðëþ†+ÿ ÿâëœÿ…ðoþ„Í+ÿcÿ ?áE|ÿ¡3JÿÀXÿ‹ã»Cïä¹yýÑÿ3£ÿ…“ðëþ†+ÿ ÿâèÿ…“ðëþ†+ÿ ÿâëœÿ…ðoþ„Í+ÿcÿ ?áE|ÿ¡3JÿÀXÿ‹ã»Cïä¹yýÑÿ3¢ÿ…“ðëþ†+ÿ ÿâéádü:ÿ¡§JÿÀè?øºç?áE|ÿ¡3JÿÀXÿÂøQ_¿èLÒ¿ð?ð¢øîÐûßù._ÞtÌèÿádü:ÿ¡§JÿÀè?øº?ádü:ÿ¡§JÿÀè?øºç?áE|ÿ¡3JÿÀTÿ ?áE|ÿ¡3JÿÀXÿ‹ã»Cïä¹yýÑÿ3£ÿ…“ðëþ†+ÿ ÿâèÿ…“ðëþ†+ÿ ÿâëœÿ…ðoþ„Í+ÿcÿ ?áE|ÿ¡3JÿÀXÿ‹ã»Cïä¹yýÑÿ3£ÿ…“ðëþ†+ÿ ÿâèÿ…“ðëþ†+ÿ ÿâëœÿ…ðoþ„Í+ÿSü(ÿ…ðkþ„Í+ÿcÿ /Ží½ÿråýç÷GüÏÉ_|Bñ7ƒ¾3xÃÄžÖä²yõ›÷Ym¤ ñ›™7/ͨs2žµöOÂÛ[JÔÖ â²é÷ŒÁý²Ÿ³¶J*™–(yfvh€3ñOм­kÿ|G–Éï¬L7 w§jBÈÐH¹Û€¬@ ù\VHâOø(¸Ó~-xïà7…¾ê^)ñï‡5HtÝM°º uUhÞI¡ g W{/ß… C%ý¼>)ü&ñ>ƒ¥þ×_î¾x{Äw"Öß_µÕmõ‹&~Q.~θŒ’|ÍØ„!XŽ3ö@Òt9ÿà ßµ^¹q^Îm:ÞÙÈÖÞã{\=@-9õÀ¯|ÿ‚–iš.¥ûüJ:Ò#-¥½œð3J\%ì"§³;xꎄбþÒ_´¯f_AãEqª^ê—)c¤éV $¼Ôo%$P¯§vn€`Y•[äoößý¦>è±üFøÛû3êð4Msc­Úê¶ò<ÙìÒ5qŒŒùžPSÃk䯷5ˆß°ÊøZ×L¾ñ'ü"ÐÞØÁ¯<«§6¬¶6ÒJf0þð2ìB…yÞµ}Ÿâë?ø)_|)¬ø;\ðÇÂÉtívÊâÆå~Õ«ÐÜÆÑ8ÁÈû¬zŠú£ÇŸ´¿ÂO‡¿?á£5}TÜø6k+{ÛYm—|×bïo‘1±RdrÀmb»y.T+ñ^­ûuþÔ~ðø¿¯~˺ŸÃ…„]Ëtu¸Q†ÏšæK1šŠæ!Ñ@%Àæ¾Wø·ðcÇ_¿gÙWö}øÉ%•奿Ļxµi+ÜZµµÅäÒÆŒdHË*âPA\1_½÷Ö6Z•Æ›©[ÇwiwÃ43 ’9cdt`C+A`Ž q_ ~%xWãÃÍâw‚gkÄV©ulλ$PÙ Ž¹8xÜq’=k{Åž*ðÿ¼1ªøËÅw©§hÚ%´·——2gdPB¥ÝŽ2Nè' ÕÃÚ…t›}Âúm¶¥ÚÚÙ–öñbÌ8ªå‰'’Ië_žßðV íZÏö)ñZi™òn¯´¨®Ø1¼»v2*=è'@ý¸?h‹öÓx¯öqýœ5x&9]mõmWZµÑšþ8Î­à œá•ä@`T{oìõûaèÿæñ_‚®ü'¨x7âw‚âyu jl|tF‚rª$‰E.Qv—S‚¬¬ßJ|5Òtá×…ôO F‘hÖ]”k ‚Þ8UcÚÚ~o|J¶·Òà­ .´Ö;­oÁº‚êþ_Ê^£¾ò^Lu;ãDöU…|¯¡üiý¤äÿ‚‡ø‡Çƒö}šûÆÑx%-á>"³W³³7çíÂøÅå6IÙå ûÂs€k÷§A¼ÔµNÔ5‹¥jVÐËqfdY´Î¤‡ÌPö1+¸ dW毂ÿå-þ=ÿ²iþ–ØWêx?í'û@xSöeøE«ü[ñu¼×ÖÚ{E –åVk«›‡ J\€£9fnv¢±ˆ ~ø•ûg~Ø ¾Z~Ð4ø#¢Ãà›²Ë-¬:óË©ZÁxTBó²Ûùjº¯Ê¤«0 ¢¾Éý§> ü>ý¦~ê_|[¬&›¨j‘¥ý‘ŠDkËy-ŸäºH’5c±Ç†+¹I~pþÏ?¿hÙOã¿…¿bOÚtAâß x‰ÛÃ:äg{¬#r[¡f’-È#1È<ȉfŒ&@>µý±h¯Œ¿ f¹>&xÀèϨè¢]BþãS†áùïD1CˆYD—r '*¡ Ê àí¯’à›2~Õ> x+ú Ïx«RþÕºñ Úì\}’úDI.~ņ˜àAµz¾ÑÓ5èŸðXŸ^éß³§‡¾i{ßø†ÖØÆ<È-U¦+õóüŒWé—ÃÏ é|á_†–3F« iVÖ6ñäxl"Žê½H.N8,3ÔPÎÿ´í}áÿþ"ÐþxKB¸ñÿÅO•þÊðÝŒ‚hذóîgee‚±Žâ “€|cÃ_¶wÆ_ þÓ^ý›?hŸ‡:vƒ{ã›o´é—Ú6¤×±D›jL¯î;ádb¥vðpÊs_ þÍúoíñÏöÌøûñÏá.­áÛ=_CÔ¥ÑV[Ý]Ika4òÅn¶‘@UUÒ5GgìHæjý ø/û!K¡||ÿ…óû@üJÿ…›ñfÊÇ0¬QØÚé6“y‘n‚ј¦DG!S,çi“æQñsþ K⿆¾.Ñ53ðšîÛáV¡¯É ê÷?Ù÷72۾˙mìdO5"„†!åI´©1¶BÖø¿ÿø¡á?Kñ›á§Á«ཕêY·‰¯ïVɯ÷Iåy¶¶»Zd¤c™Ñ•‰…'æßðR-6Žßµìñû-É!ûìòjzš#ÆÒæ`ŽA†X-'ÛîÕìŸðU¿xwáÿìg'Âý*­¤ñ]î•£é–0(a±š;¼EŽ5·Tà`nQÜPè×ßèŸ<á߈Þ.t¿iöÚ°aÖ+˜ÄŠ®9Ã.pñ¼SöÒøƒÿ ¿öTøŸã“ÉžæÒÝó‚·:€p0÷L¤W_û4xQøaû=ü:ø¬§—¨èz…µÚråaS2ÿÀd,? üýÿ‚Äx²ú€žøW¢åõ/xŽÚˆõ°Ú£9_ûþðP¹Á0þÿÂûx Í•yâOµkSñ÷¾Ù3y øÛ¤Uú\€ü)aðÿÀžð>ž@²ðÞ›i§Dz*Î…O· ] Ž¥§jq´ºmÔWh‡ihdõÁ*O4Ã|Yøe£|bð«ðãÄ7úŽ™¦ë O6—tÖwF4‘]£®p’Ù"à†Faß#ñöˆø-ð·àüöbð·Â ÚxoMi4Ó$vÊCLé¨:‰&v%å“rX€8¿ *üLý·Èÿ‡”~Ì\ÿËKý9=~ÙW?âßCâÿ jþ¸½»ÓbÖ-'´k« ÚÚò:2A2󉜣s‚Áé[“Ϭ/qs"Åcs;ª u$ž¨í/,ïà63Çs dƒ©Ç‘‘@Ïü£ölø5û=øàŽŸð·ÃÑi·7:ÝÒ^_¹3_ßôv/upùyNâXv©'`PH¯èž¿?಄ ?ù8ÿ‰õßò¶¯Û:Jù‹Ký¤Sý­µÙdxxÆtŸ ¯ˆÎ¯ö¼‰7O ?gû7”1þ»vÿ4ôÆÞr>ž¯Ì ÿÊ[ücÿdÎ/ý.³ ßã7íù¤üøë¬|›À×þ!×cÓ,®´X´Ù¼Û^þõÕVÍ òq¢–w˜ÈØT?!8‡Ô¿oOŒ|OáÈ?jßw<%â›Ä²ƒ\¶Ö­µX-%”|«r°¦ÕÀË6]b³*>Ò+IдÝ_þ ù­ßßB²Í¢|=KËV` Žfš bËž‡Ë×#±#½w_ðUë[¿Ø—Æ y,¯4‰¢$gk›èb${ìv~Ž×Ìßµ§íì±ðŠ_‹èÄ‘CifÖ¢ëì˜[¦*dó<©¾î>îÎ}E{Ÿ‚gšëÁº ÕÖk Wv=K4JIüM~vÁ]?äÌõoû éú4Д¶—í ñí>*ý›ÿg›ïøÞY jÿW¶ÑßSH›kIgm:2AØã~î…C£èOÙoö©ð—í?áÍ^çMÒ¯<1â \‹-wBÔF.´û“¸Nr1GŠ«eX2©¯vð“¢h>ðæ‡á˜Ò-#OÓ­-ìÖ0Þ(Ub mù·ð†Ú +þ ½ñªËBc²¾ðu…Þ ±ð¢ø› „ÆæFf'¹$õ' ã¿ÛGâ6«ñ‡Äß¿f„³üPÕ¼åÇ®jjpiZ}Ä€â$À‡pC)îW ¬šëg?ÛRø¯ñ3Äþ-x ëáĿڭóé³ÝÇ{ouhJ©–Þá`ºœ(e*r®Øm¾=¥þÔÿ´/Ç߈^2ÐÿcoxuK˜í…kÆf_9“ÍsЮßJ÷ÏÁEÆ›ñkǼ-ðÇRñO|9ªC¦èúm…Ðcª«FòOu4K8` »Ø™~ø<(b/íáñOá7‰ô/öºø'uðËÃÞ#¹¶úý®«o¬XÁ3ò‰söuÄ|d“ænÀ$! ÄqŸ²“¡ÏÿýªõˈÐêösiÖöÎ@Þ¶÷Úà)êh¡Ï®{çü³LÑu/ØŸâQÖ‘m-ìç˜Rá/`=˜±ÛÇPÄt&€>’øÙñ§À~jß>!ݵ¾¥"ü±ó\K!Û0&Fù$c€2f!A#áwýµÿk¼.ÿäý–u!ðüCö¯;ûrÜj¢Óï} éþOŸŸ1Mœwíù«æÏÛ^ãÆºÏì¡û#YyV×:¶¡áss¢Ïö)uÓ“ÊK½¿1ˆ³Iæc¹Ç5öyÖà§ÅvŸ ü, ñµêø WÔk_^þÊz§í[ðòÒOè¶:t·éc$‚Îrð?—-¼­¶Q£ ãŒ®å ŸøoûsüTøñ¯øuþ|»ñƒf“M‹\ñÆ¥µ­Œ×IÞÅl% Ѳóv\d àúoì÷ñOöpÿ‚|~Ðþø¡ý“šÓêzÍœ<ÒÍmoÔ#D¾lhUU¢ùWž;æ¾öý†¼=¦øgöBøI§éq,1Oáë+× 0 ×Éö©Xû´’±'¹4õ]-PEPWäü«þMgÂßö9Øÿé»Q¯×úü€ÿ‚Õɬø[þÇ;ý7j4úÿEPGáIKøPÿÖýû¢Š(¢Š(¢Š(¢Š(¢–’€z(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¥ ¢Š(¢ŠZJ)i(¢ŽôPE-%|Aû,ÿÉaøóÿaåÿÒ«úúËÅð‡.tk¿ipêrøzö=FÀÌ {¸¾äÊ3ËÕI‘Í|›û,ÉaøóÿaåÿÒ«úû~½LãýáúGÿIGŸ–z¿Í…Eqo ÔZÜ¢ËÊQцU•†#¸"¦¤¯,ôgÁ¾ ðÇÃï ÙxCÁš|zV§[{X³åÄ®ÅÈPIÀÜÄÛ âºj)h›˜TnïƒÛ4ê( „EfuPñ¸ÉÇÐñÇ*”•C©ÆC Ž=©ÔPLH㌱B—98Éõ>õ%%xÁŸƒ ð’çÇW « cþO_øŒoä}”ÞíýÇúÉ7ìÛþ³åÎ~è¯p¢Š¦Z…ÜK dž§ë^1ðkàòü$Åñ¶ª5øJ¼I©ø„fßÈû1Ôdó<‘ûÉ7”é¿åÝýÑ^×INàQE ñÇ!Rê¡ÈÈΨ§ÑE"ª¢„@¨À€¥¢ŠñO‹ÿGÅmSÀZ™Õÿ²ÿáñ¦»³ÈóþÕö\þç>by{³÷ðØþé¯jUTPIü!°øƒ7Å{? iðøÂá™äÕV.™<¶%úä¯JõjJ(¢Š(¢Š)i)h)‰q±(@I8ž¦ŸERÒP8âR±¨PI8ž¦ŸE-%Q@Q@1ãÙYÔ1C•$gÔzSè Š( 4Q»+º†dåI‚})ôQ@RЋxÿáü'?>|HþÖûü+Û«ûŸ²ùgÚþÝg%¦ß3Ì_+fýÙÚùÆ03šöeEv  aÀ§ÒPJ!q!PXÇ8=FiÔQ@ HãLùjq$àc$õ&EW7áŸø_Á°ÞÛxWLƒJ‡Q»šúá-ÓbÉurÛ¥”ÆçnX÷5ÒQ@s~-ðw…üy¡Íá¯ikUÃFòZÜ®øœÄÁÓržƒÇÒQ@ÀyNƒð3á†=Ð$økqeà-ô?d¼ñEÍÌ“ˆØm’[X·1ŽR¹Á1pNTÆpËúE|9ñcö-±øŸû(é³®£âÛûÝsÃIms¦ø“Pw¹»]Nر¹g/±ƒ¼a“l“°W†kÚ?ü3ƹø7ªÚø'O}JÔé×~.‚îàLÖò/—,ÑÀ£+;®~e…@$•T8+ú­EyOÀÿ…ñ|øKá…ñj—Ùðý †Kë¦f–æfc$²ÌÄ‘˜ªî;W “нñ{áo…~6|4ñ¿BfÑüGjÖÓmÆøÛ!£š<ä "‘VD$FA¯H¢€?,|àÏø)ìïá»_„¾ ‡Áß ³ñGÅ?Û­ŒQéѲéÚEŠ‘û‹]áY‰  ¶Õ rîí÷­ñ_‡>øïKý¼üSûGÝ5§ü"Z¿ƒcР 17_l[›YNè¶à&Ø[æÝ×WÚ”Q@üwý‘~"üCý –š›Û™¤VVš.$ó›vUº)R ©xö7ñu×ÇMöŠý¢þ%?Ä_øbƒE´¶Ó!ÒtÝ?͈‘äi˜y„«1V‹m]¿zÑ@~Úß±åí}àÍEÄ’xSZðÅë^Øß,iAæ(YãçjÊà©^‡¥IðöKÔþxÙ¾0üfñýÿÅoˆëftë]Jö!km§Ù·/¥ª3ª3ÿËI Ës€»›wÙôPæ'ŠàŸÿøÅz£â½Kž(ñÓM>µ¯jLZK›™aò’Ü7— q&#C€¥ˆ Žö$ýáŽ<âÿÂ[ÿ wöö ·þØ>Áåm…bÙ³í¾îwnqŽõöµÎø·Oñ­áOM𖮚µso$vz„–Ëx–³0ÂJmÙJ󰲃Ó"¿(þ$Á5>:|Wø§£ügñ¯í'%Ï‹|Yüµñ/öüÖS^Κ‰²û(ßw#H?Ñüé¾á?óÓæÇjúzŠüŒø÷ÿßøéûKßé7ßhßí1 ù¿`† Ãi¹˜©‘•`Ô,ÛæbÄ`@¯¼>ü9øíðþ;»oŒSâl¢´Îƒ‘4 „îy$†y|âÃæñ’I&¾ƒ¢€ ø¯Cøã­?öôñípÖŸð‰j~ M %&ëí‹so)ÝÜÛ|ÛºãŠûRŠø¯Cøã½?öôñípm?áÔüš LMßÛæÞS˜¶à&Ø›æÝ×VïíÇðWÆ´'ìÓâ…6Ã\Õ¥ÓÞ¶Ja‡×Îûœ+‘ò!Çž+ëz(ÂÚuÆá#H»Úg±³·‚M§+¾(Õ[Ž28¯Î?ø+§ü™¦­ÿa}/ÿFšý:¯™ÿkOÙÎÚŸàåßÂ;y¼6·Wv·_l[avTÛ>í¾Q’,îéÜ{ÐÊþð¯üsàW†,þx?üFðæ”‚ÓEÖ5iîm/í¬c]°%ìHcW1 <²Ä€2ƽ¯öDý—¼GðJo|Mø·¯CâÏŠ¿®Rë[Ô M–ð¬yÙkm•Så©''j…P€"×ÚQ§–Š™ÎÐåO ÉO| ýµ¿døãÃ߳ޛáøÅúÅÆµe¯s%¥ÞqrdÚcªˆŠv³n l%–´þ~ÈŸ´–•ûbxwö£øËâ]3Ä—ZUôzºY4‘[ØM4rCmea¦æ‚(Ê’îÀ–,Nã–oÕŠ(ðWÀ~=k¿·íñ;öt×ôÍ;Å>Õ4ëi´Ýid:n«a©G+M Ïi‘íchÊÎrÊ:ýã€ß¶íw£ø+ö•>øð¿N¼ŽóRÓ´廿՚ݎ6‘‹*Fy îR¤†(ìoØ?g>|gø»ñ~-}µFø­u§Üµ‘¶‹`³.Ñ/˜þnÿ;9ؘÇ|ñôÕ|ËûOþÌžý¤¾ \| ¹˜h-hÖ÷=ä1þλ´atŒùv”ò1Á|­g7üwGðí¿ÃØôÿêwÑEöaâ¹în ÆÑ<–ÿ.fæ'È*Hæ3È?¨tPÁs~ËôÏØ§Åÿn|cqãxžÆù¦Ôµk™L-}zC4q<ÆŠÝÂŽçsíÈJ~ϾÖ¾ü øðïĆ#«xkAÓ´ë¿!ËÅçÚÛ¤Rlb²îSƒ‘^ÁEQE%-P_?ðZ¯ù5Ÿ Øçcÿ¦íF¿_«òþ Uÿ&³áoûìôݨÐëí-PG4Q@ÿ×ýü¤¢–€Š( ŠZJ(¢–€ JZ((¢–€Š( ŠZJ(¢–€Š( Š( Š)h¤¥¤ Š)h¤¢–€Š)h(¢Š(¢Š(¢–€>ý–ä°üyÿ°òÿéUý}¿_~ËòX~<ÿØyôªþ¾øÓñKFø-ð·ÄuÜ<«ËYÚgœü°Âc$…W=³“À5êg ý¡úGÿIGŸ–z¿ÍžG§üPñÇ?j‹ï‡> »Ko|=ÓVOIäÇ#]ê—à›kE‘)åF ŒSª·U5/Á߉ž5?þ"üø¡z—º¦‹,zÆ…r"Žu¡^|±±…VkiŠFÆKrx"®þÉŸ 5¿‡Ÿ -ï¼f xÇÆ7ëúìŒ6¹¾¿!Ê0<)6ÆøvàWž~׺f©à ï~Õ>…¥½øouåkQG÷®¼;|DwˆG(‘"ç…æC÷¯;­ŽãíÚ*Ž—©ØkZe¦±¥Ì·wÑ$ðʼ«Ç"†VÄjõAGÏ|}â|@ø¥ k·+=—†uh­lcD1ÂÈÄ©e·#«dÓ?i_x¿ÀÞ Ò¯<¨&—¨êZ͆Ÿö‰!IÕ#ºrŒvHã­y7Á¯x3Â?~5Åâ­vÇG{v‰nîc€¸XØ¡Ødø«ßµ†¹àÿü'Ð/­5MWC›ÄºdWE2IÍ"PΧ'w< ñÝwõiZ^ö½uÝŸ¥Ç'ŠÎèEÑýÛögÝw„[ÓgwVz—…<-ñ¶Ï^·¹ñÄÛMkOA'™i•oHJ0B aµˆo|c½x—Ä+ïÚKÀÞ-ðW†¿ádÛ]jY,ŸØÖËöpˆv9Ý×ȯMø}àÿÙ;Bñ}†¥ðãûd‘Â(‘‰ÊŽ™¬ßÚþJ¿ÀÿûÏÿ¢Eaû«ß[­¤Þíz_‰O0TÝ4âá'ïQ§cµd®·¶½vz#ÝþhÞ:Ðô{‹_ˆ"ÄÚƒÜ3Çq¢Y„„¢Hò ·uù±Ú»º(¯ZåVGçXŠî¬ÝI$›ì’_$¬—ÈùGþWö|3½ºëw,ãÌTÆŸuûéãq‚/Ýþò\²«žæ½ûÀ^<ðÇÄ¿ XøÓÁ÷FóKÔÜ«#ŒQÕ•°U•â¿8>A ÑfÖ*2Þ(ñcr>Ýý~Uõì“ÇÃ]f1÷cñGˆ•G þÑ›Šûn É0¸zRt¯mZjqì¿–ûõ±óyn>µI%RÖk¢ò‹îûŸP$œ\S|IøtŒQüS¥+) ƒ} øvn €]Ü=}«á+Ÿø¥®feýšü=(.ÇyÖ-|Ÿ¼GÙøÏZøMiBÜ«ðoòLûì‹+£‰çöÒµ­ö¡ÿÏ(ßå3ìI<{àh¬áÔeñš–· éÍwŽF±l1\Œ€xÈÍ6ÏÇþÔ.¢±ÓüI¦ÜÜÎÁ#Š+È]Ý@ª®I'ÐWÌú—„|E'‚´;8þè—sAqxÏ¥¾©l°Ùòöˆ¬gÁÜv 絇֧̕»t—ù¯ú½„öRŸ´Õs}º=¶žÒïäú_Cìê(ö¥¯Dø£àØ·Ã6š¶»ñKân«{¨^k–þ2ñ‹šúâKd±Kˆ¥T[f ·F ¸”+ïêø—öÿ‘sâ¿ý”èÈ«íª©n$y?ÅŸ~ 3þ«É­ŸZ3-œp[Kró<K(X•°~uÆp~µÈøö¥ø-ñÅg„<'¬Éu¨jñ<–Û­¦Ž'’(̲Aæ:…óQf\ðZå>6"¿í ð8ÈšéüEšú×Ê `†ÛDø ð(Bl-¦AÜT¡ ôb+Ÿ„Â7…÷c4¯+é¼—òî·~JÞf”«V^ÛÞM§¢·’}ÿ¦zoˆ~!x[Ã%ð÷„5k¦M[Å2ËŸFò4†2HNÐBª $³`WŽøƒö¸øá½KRÑïµ[™o4k™­¯£·±¹œÚ˜6‡’]ˆBÅ–?F …Î<‹ötñ\ÿ´Oŋv²[é¾Ñí4;å\mÔ.c[IÇl«{¨SÑ«àGÄ?„þñ'Ç«jv:uãxŽòâå.äD{›(*¢«eà FÅÉËôù†vÒ¦ç °”§ÅÉE­äöÙìšùó9Ï–P’Q“vo²^«w‘öf¹ñWáç‡<Ÿu}vÞ ËsEz t•eÿV#T›²¨,Oë\Ïÿ ~(ê÷ðÝäðjöЋ“e}k-ÃÛ“2$ʻМr¹ÆFq_™É øŽÏöjø«x‹PŸCðÝ—ˆ$¹º¼,ŸbŽæf6WN’Mƒ.Ae*Cï~Â🂼©|eð‹¯¾4Ÿø‡H†ôYY#X1–ÞxJÊìª`Ê¸ÉÆTc½k‹áÌ- Sr›m9Ù«ÛÜm$Ò‹ZÛ_yY4ýb†iZ¤ãh¤½ÛüתùhïcízJ(¯…>„+Á>4ø—^Щ¨j?¬õÿ'O½6ö±éÐ1ºòÈ}ÊIÂIµŠã  |%ö}óö-økâ?,éøÃeçœvÄm›Q½ï$Œ.Ìî9àWÝ? üû"xwÆVÚ‡ÂqáèüHRXàûärÜe>`T1?.sÇJõ³š4¨B§³§öêíN2VRÑ9=b’íÐâÀÔIGž_fi­Öºuùø1ûDøs]øqðÖãâµ>*ñÜRÇm’ÃíW6ò˜dÇ”†8òÀcqPIÀÍ|é©ü}øÓ«h+w¡x‚-µß‰RxcO¸{(%H4ÆeÖ\>Æ~XüÇi¯¼7ábßLñö¹onÂ_ÙîëÉҳʖƒZ’òá”g¯Ùã+ŸF«½Õ´ ?Ù£à,~1H†“âA}©}¡¶Dm5 Ée5‰/ÙÜ9 æ½5‘`èÖsŒT¹§k4®¥5eÛ——N÷9c^¥4›µ£{Ý«ê¢îýn}ùà |nÓñ%¸hÈÌPHÄŒ ž:WÎßþ AñKRø».¥ãhú{ø×]·ŸOÒï#¶µ¸CpÛŒ€ÂÒeÁ*ãŽÕóøwI{jµVQÖT¢º»ûªë^ûùžU?rmݽ¦ßNïò;©¿i¿øÃàÏÃ}GÁvVºoþ%jo££Ê¦kK7¶–H®®UO.ª#.ŠIÀ8;ˆÁìü㋾9iß¾+ëÐøÂÏÅt×ÚVª–qXΓÚäÍo,pâ2»FCžW“’Šøçá |¾ø­hÖcLðg‚u¶´¸a–ŽÖ+èÚ14¤äŸ‹;œ’I=M]Ö¼I¡|Rý±~'o Öìü£êWšÝ”©<}°yQ!‘ RÅ‚ü ôoc>¯‡©IÊ$©J5evµ‹Mò®mÕ­kë~·#ÚԌҜýôà­}v»·^ºÛKzo‹¾6ü~ñ_‹áŠàð„|)¨K¤Ár4øu ­Fê߉œ‹Œ¢DávÄ`ž¸ÑoãþÂñ®«-ü!#v‘­aßYp}ÏzúZ¿~×?m~è6úZ¦´÷ à$híÈBwÐ1Å}/ãÚ#ÀøÃá‚È»ÿ„‹Åèd²1BÜ¿ýd›†Óòǵ|îw‡£aý†Î½’oß’»I¾Ößdzy}IÊU}¦ê]ïöc¶Ç»ÑKI^é´”QKI@RÐQE´”´”Qô¢–€ J( Š)h¤¢Š(¥¤ Š( ¥´RQK@ EPÒQEQK@%-”QK@ EPEPE´”QE-%´”QE-%-%QK@|?à_ù>¿ˆ¿öƒÿEéÕ÷|?à_ù>¿ˆ¿öƒÿEéÕêe¿ oð¿Ííé‰~Lû~Š)kË;Ä¢Š(¢–’€’ŠZJ>”RÐEPRÑEQE%-PãçÆÿ„Ÿµ¯Á_Û;Å?µoìßàˈvþ;Ñ-´››[©Ò&³xÒÕä3@Ì1a Gï ‘ Iÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–ÃIÁU¿èÜ´oü ü°¯×ú(òþOþ ¯ÿFå£àHÿå…ðÒ_ðUoú7-ÿGÿ,+õþŠü€ÿ†“ÿ‚«ÿѹèßø?ùaGü4—ü[þËFÿÀ¡ÿË ý¢€? ?á¤ÿàªÿônZ7þþXQÿ %ÿVÿ£rÑ¿ð$ò¿_è Èøi?ø*¿ýžÿ#ÿ–äüÿý¹tïüø¿ð£Ið†,u›}bçTŽõQ‘ Š[wAþ‘vK2\1P"o˜~Pk÷zŠ(¢Š(ü)) ÿÐýû¢Š(¢Š(¢Š(¢ŠZJ)i(¢Š(¥¤¢€ (¢€ )i(¢Š(¢Š(¢Š(¢–’€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€> ý–ä°üyÿ°òÿéUý}ñ?á…~.Åáû?µÄ¶>Õ-õtµÂÁsqjI‰n©ßnJäg¡ãŠùÇöXÿ’ÃñçþÃÃÿJ¯ëíúõ3÷‡éý%~Yüêÿ6™­húoˆ´kíYn¬5($¶¸…ÆVHfRޤz$VÕåžÂ|2ø}¤ü*ððóAºº¼Ót(~Ïl÷’ g«ˆÎrHUã;@ÎO5ÞRQ@y©ü"øO­jêÚÏ‚´Kûë¦/4÷u´²Èç«;´e˜û“WÃ_‡'@>ÒŽˆ²ùâÃì0}”Kÿ=<›7µŒû×mEgìaÙ¯2Ä4“©+-µzz}¤ü&øW j0kƒt]:þØ–ŠâÛN¶†hÉ%]# §Ž uZ†¡j÷v7ú®›m{s¦He´–xRI-ä#âf£ÆWµ¨¦©Å+$gWZrçœÛ{]·p¢Š*Îc–³ð/‚4èôÈ´ÿiÖ©¢Ë4ö ¤(-%¸ÝçIU[I½·²`¶ãœäÖ®‘¡èž¶’ËAÓíôÛye’wŽÚ$…i˜¼’@wbY˜òÄ’y­JZÖu§-%&ÿ¯ø$¨El„¢–’²((¢Š(¢Šð†_³§„>x×_ñ§…5tŸÏywu¦Üj/&”.o¦Y¥,𱬹P«',+’+ߨ¢›`dßh§¨Øk–mw¥ÎâXQæ¶2®Ù .À´e׆ÚFG²­|àk°ŠÇúuºiSÉuf±ÚB‚Úâlù’Â.GÜw2àœœžMu”•¢¯4¬¤íÿþoïd:qzØçüGá? øÆÄi~.ѬµË5`â ëxîb :6ÉU—#×ÅøÛCñÖ‹à«o|³Ðô›¨œB‘ß,–Öv¶¬–‚;XØy‹!B¨T!²sŒú¥¥T ×Tìöû‰+ôoªÜòOß l¾ ü7Ó| mp/na2O{ta¹»‹Ë&; ªHUQÚ¼ëÀ³—‡í.x_T‹ZðׂtM'Q·Ï—si¦ÛA2n;dŽ5appyè”Vß]­gwg¾¯©ÂO•}Ç7ƒ|!Zµ¼ŒqkîòjH¶Ñ½ykµÀ ‰K/¾I¥ª|:ø}­èv^Ö¼1¥ê6˜m,n, –ÚÜF»E ¡D ¿(ÚŠìij&¢wRyN”Zµ<Ð~ü(ð®§·áèšF£aÍžmo:X,‘Ƭ2 ‘]V‘áßø|ÞLµÓN£q%ÝÏÙ¡H|û‰Ndš]€o‘Ï,í’OS[S«‰©?ŽMú±B”cð«ú®“¥kºuÆ®YèØ]©I­î#Y¡‘UtpU‡±‰á?x'Àv³Xø'@°Ð`¸`ò¥´vÂFüµ]Ä9À⺺*i¨¸&ìút)Â7æ¶§â߆Ÿü{$xÛÃf½-¨ÛßZEpñ©9!ZE$z€p{Öð_ƒ‚i1 À&€Û´ÑöX±dÛvæØmýÑÛÆSq]--_Öjr¨ó;-µ'ÙFíÛSïÞ¿Ö,|E}¥ÚÜjºbȶ—r@qn²ŒH"”‚èpÁHÈë^ÿ ékûP¿í#â=QuÓôˆô½Nò6‹;Ä×Bç{ºÈê¸U±Îx#éšJÍÎOFËQAET (¢Š(¢Š(¢Š)i( Š( ÞŠ( –’Š(¢Š(¥¤ Š( Š( Š( ŠZJ(¢Š(¢Š(¢Š(¥¤ Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾ ð/üŸ_Ä_ûÁÿ¢ôêû‚¾ð'üŸWÄ_ûÁÿ¢ôÚõ2߆·ø_æŽ vô¿Åú3íú(¢¼³¼)i( Š( ŠZJ)i)h)h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(£š(À ÿÑýü¤¢–€Š( ¤¢Š(¢–€ŠZ((¢Š(¢Š(¢Š(¢–€Š( ŠZJ(¢–€ J( Š)h¤¢Š(¢–€ J( ŠZNÔQE-|?û,ÿÉaøóÿaåÿÒ«úû~¾ ý–?ä°üyÿ°òÿéUý}¿^¦qþðý#ÿ¤£ƒ,þ õ› )i+Ë;ŠZ(¤¢Š(úQK@ E´”QEQK@ EPEPEP+âŸx3ÁC?ŒuË=.C˜äé“˾Ýän*È¢³´oŠkÚMŒ6Ú•ÇŒ|Am-ÊF«4ùsÂÜÅB(_cƒáê0м¦ùšo¥¿åå¿ôßâxuó:‘®é¤¬¼þÏÿ%ø¦Þ&ø§ðÛÁzŒ:G‹|O¦éÓ€R «¨â†èv³ÁÁø3࿇>2øSñƒÄ_,,﵋½s_MZîê8ÞâÙ-‰ò@w£LJLcîɓʫ¹K‘¨´´æ÷¥(ëÓNVüî–†ïVþÍ%Ì›ôÑ'úþgèl÷6ö¶ò^\Ê‘A —y‚¢¢Œ–f<$ô®Ã?þxÏS—Eð—ŠtÝ^þY ¶ºŽY6ޤ*’HÈàWçV³¬ëÚì3ð¦ÛÄWSE¥êÚÆ§k’Tÿc­Ìéfà„Äp.{®pkô[GøsðÏA½Òµ-ÃÚfŸwa Ce5µ¼QH°ºá•%H䌑ßÞ¹±Ù=,,%ídܹ§m½Ý.ý{tZšá±Ó­%È’V‹wßSƾþÑÂø·þ޽¥èM¥øŸPÑôñ4©ne·µXŠœ;eˆ2Ì0:t¯¨"–+ˆ’xI€2²ÊÊFApA+ó«á‡ÃOøÂ´¿â ÓQÔnuýv×ϸ…%’8`·I#XÙÙ‰¾Wv û£J~É÷—7ß³—€'ºÈë¦G,rvBÍÁTì+£?Ë(B2«FêÒŠk¦±¾Ÿsü̲Ü]I5 Siú;j} ^7ñ[ÇZçƒuχú~åy^%×âÓnüÅÜ|‡†W;FÖÊy¯d¯šhù~ÿØßoÿ¤Ó×ÅâäÔ.¼¿4}§P…LR…Eui~m~'ÒÔ„€ <ÍyÇÄ ‹7bÿ…cy¤ÚmßöŸíH¦—wMž_”ËŒsœûW#áË/Ú6-rÎOêž›EW&é--®Rvî3¹PsŽ£¥\«5.^VcG,S¥í}´ú6ïùué©ì:.»¢ø“N‹Wðýü•”ܤöò,±·Ñ”‘ŸjÕ¯Í=v/‡Zλqwû"é:êx™Ž÷ÃÇì:ñÈ?jftB¢lnÇ5÷gÂûˆÖÞ ±‡â½Í߈×wŸ%Š•„®~AÈQ¸¼B…ÏN+Ÿ ‹u¾kU÷ž¦{ñÂShÎ×û\µ›ŠoO7kö=¼—WøóðSÃþ">Öüs£Xë þ[ZÍ} JÓkß+{õšüšÖou/Œßþ,ø÷Áþ ð'‡üow­[]]êö¯qªßÍg½f¹•£TX¤gæ-ÌÌßAŸB(ùVÏÕÛ‹»[KY/®¦Hm¡C#Êìd±bpIxóW›@ð_Œ4­oR€k{KȦ— Ô…V$ÜŒ_˜~7Öõ Oþ ëð:ÏÄZŒÐèÞ¥áý7_¸ß‚4•‘Õ„Žz òãä‘Êßú{¢|)øGá½CEÕ|9á}'K¾Ò¢xtéím¡†TŠHöºFè2²rFH=O#48Ø.Oã_‹ß ~\AgãßéšÅÈ W—QÃ#)8 ˆm¹Î1],¾,ð´ÿ„ÆmbÍ4$\hãû'’Ý$nÙ´ä`ç¾ý™<àŸüPøñâ_ˆºU–»âø<_}¦0Ô!ŽæKm" %”Q$ªvC$XnzçÍþÙ^·þ7ÂC¥éÞ ¾ñ-/UŽŒò[ÄZÒÞá" ˜™òŠ€bBãSåÖ¹÷¿‚~'ü:ø“ÌÞñ.Ÿâ³ Oö+˜ç1ÈÂ@88'ƒƒŽ•ƒ©üxø+¢Þ&Ÿ«xçF³¹{™lÄrßB®.!m’FAl«#|­œ`ñÖ¾cð/ìíñGAý ¼;ñX¼ð¦„––Zmí—‡­'²:•»®c£6ÆhX3€áW3û;|6ø;⯠þÒšŸŒt;R¾“ž&†úk¸£’[{hå•âÚî F,à‚>až£ƒ•ÏÐ?‹ß|ð{Á—‹|C¨ÙÅ<6Ww6·7Iloæ¶…¥X"fÝ–rªÇ‘Áé\ÿÁ?Úá÷ÆŸ è—úF¯§G¯êv {q£Á{Íͦ@ÞŒV; ’‹ôò÷ÃKøLÿàœ6š‡Œ¬bÕõ OkÎ[¸–yV8a¸ŽÖD.¤‡0¤d0äñÉë^íû"xgÂ6¿~x‡JÒlaÔ.4;Q%ÜD³9(7†‘T1ÉäõÒiXiž³âŸŒŸ ü¬ÁáÏø¿JѵK¥-®ï"†Rî’ŒÀ€{€{WW¯ø§Ã^Ñ%ñ/‰uKm/JC=ÕÌË*îì@竦ë~ Ô|#6µö«F9¼ŽôÛí« r¤g5äþ øùû[üWÔ<_?Ã?xN]Ã:þ¡¢,—÷—pÎíe&ÐÅSpåJ’F9ÎRÐ<)à ÿÁGç·ðU•®œ×ž’âþ DH£[¦»rc@Y¢XØñÎrsœ×–~Ìß->(KñW^Ÿâ‹ü*Ðøë]ƒìº·.jÛf æ4H2Ø-Ü;S²ô“Kñ¬þð†‡{ñ¢÷IðÞ½~<¹Ò;­¶fä+;$LT°¥¹çž‚´üñ/áïÄxn§ðˆì<@–.seqþSãxBHÎ ààã¥|ûcø#ÂÖzìõðûÅú…γ GãM*ÂîçU¸7pˆdCö©Û—2ð²1ÆCÛ×|=áÏþߟ ­>Ø[hí¯xgT\¶°…!ìàVkY%Ž0ýò*†#øTRå Ÿax‹ã7Â_ x†/ x£ÆV“¬Í·m¥ÍäQMóŒ®U˜ÜFq‘ÍzÝõ–Ÿg6£qµ¥º4²M+„#Q–ffÀ $“€+ò£öpðȳÅÿ|\Ó,oüA©êþ!m~òö8ÚæÖHe + Ñy_}0FÖ9ÀÆŠu}kİÏìûcã빢мE¬xrÇÄS4m)¥#È0U*nlŒ{QÈÇé§„>2ü'øªO¢x'Åú^¹¨[Ò[Ú]Å,¡GVØ­’r8ó¿ÀßÚ³ÃÞ!ðÖ±}ñĺ7‡õ(üKªéI2Z™mìÙ’~l3p3_Dèß >xoVÒuø_IÒu: -ìf³µ†ÚDÔ j¤¡P ^Gã85ù“ðcá/ï~Ì?´?‰}u§› ¼8[K¹ÿhØï^iñËà%–¯àï†<á±wáÅ—ÖžnV=<¥ÇÚ$vš@ï–—-†g%²+ìZZíÃg¸ªr„¹ÛQÙ6ì´·~‰èaW/£5%Ê•÷i/SÇ>|øað³V»×ü#¦Jº¥ìBÝî®îî/gXåaG¸’B‘ƒÙqœ ç¹ï~ËŸüoâ;Ïkº¿lÔÙZùm¯n­`¼dèn!‚TŽCêJ忈‘_AÑXÇ7Å*޲«.g¥îïoSG‚¢ãÈà­ÚÇ/ªx'ÂZÏ„ŸÀz¦“mqáé-ÖÐØ´cÈòˆpàmÆ1Ž•æ¾ýœþ|6ñ~&ðµÚß[Äð[›Fîí-â“ï$QÏ+ªƒŽ¸Ï`q^åKXÓÌ+§ µn®ìýK–œ¤¤â®¶Ðá4O†¾ ðæâ 'G°0Zø¦êæ÷QC4Ïç\^ IœrSr¨B vÖ¯ƒ¼!áïøgOð„íM–‘¥Çå[Bd’R‰’Øß+3·$òÌMtÔ•LUY¦§&ï®ýV—ûŠ(GT‚¼ãn­ë~#ø]s¤XÍy—âx.®š$, mæS#‘÷TOs^ÿErU¦§VzYv:Xjª¬UÚM}é¯Ô(8<sÚŠ+C„­geg§ZÇe§Áµ¼#jG„E°Uô«4Q@ÛmÝ…|µ«þƳ®¹â}SÅ—†¤y5¹žæöÍ5 È´û‹‰3¾i,ã™`g$’ILnù±»šúš’šv2°ø7ðÏOø_Áxô(n<¯Ø×N¹i.c0Üšfwl7!™‹‚@® á¿ì¯ðcáO‰ ño„4ûåÔ,¢x- æ©}}¤R®×H#¹šDLŽ3Œã€qÅ}Eb±ó·ÄoÙWàŸÅIãOè÷ºõÄk Å]é³\ă'6’Ä%p ‚Àp®ÇVøð§]øgkð{Yðô7ž±†(mì¤i „B1G6ï5d^q }àäîɯY¤¢ìv>yøsû.|ø[âxügá› éõ¨#x`¹ÔuKíA ŠAµ’%¹šEL¯¸Ž¦¾aøQûø/ÅñWøÝák«mCXñ†¹u“©Olº†•qre·ûBYNHÛ%•dÆH Aý$¢Ÿ3Œ«- FÓtX<7ae U´ kª ¤ »a:m Æ:b¼{áÇìÕðkáˆdñ7ýmêH¤„Dšô–i¤3,vrNÖÑò6F1Û5îÔµ7šøùû5Øx‹Bø7ðÏÂ^“Wð_‡‹}§j6SIqmoUž,ªQÝàší⑘1Étnyêªú·À?ø‡ö‚ðÿÆoü@7º/…%»“KÐ?² ˆ[‹Û_³J¿mGI“óüêØè1Ö¾Ÿ¥£™…„¢Š) ù~ïàŒõŸÚEøÓâoˆRÒ¼2oÆ•¡ÿeA´P‰b‘>Ù‚I9ElÈŒxÀÆM}AEÛ¢Š)QKI@Q@Q@Q@RÐQEQE{ÑERÑI@Q@Q@´”QEQEQKI@Q@´”QE-%|Aà_ù>¿ˆ¿öƒÿEéµ÷|?à_ù>¿ˆ¿öƒÿEéÕêe¿ oð¿Ííé‹ôgÛôQEygxQEQEQEQE-%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEsEÿÓýü¤¢Š(¢–€ J( Š)h=¨¥¤ Š( Š)h(¢–€Š( Š)h¤¢–€Š)h¤¢–€Š)h(¢Š(¢Š(¢Š(¥¤ ¤¢ŠøƒöYÿ’ÃñçþÃËÿ¥Wõöý|Aû,ÉaøóÿaåÿÒ«úû~½LãýáúGÿIGYüêÿ6-%W–w…RÐË>9ý£õÏø¾ßÂ7?5k©5+¹m4Ù£–—í%¢ä9ù±Å[ñoí3¢xVðNã ïF¸ñІq3Æ~ÀL‹'ÁÁ°$ƒ€9¬oÿòX¾ÿØjóÿD­q¿´‚ôˆ¿´<®.lõ Z˜Z6d$PxÝa‡¸çŠñ«V­~Y]¦’Û­¿Ìý;,Ë2ÚßVuèòÆTêNM9_ÜçZ]Ûì§ë¦ÇÒž,ø¥¦øKâ„>]YK=׋ÍІd*/² vÞ' ñŠõüÃðß‹õ}{âïÁO xµóâŸÝëZ>¦ ,Îöð ŠlžXKÖ ~ñɯÓÊëÁb]^gÑ?Ñ~§ÍñFG¡Oí8¶ßGïÍ&¼œRkï<ö€øïÀ­/ÃWiáË¿ßx«W‹F³²²xã•®&ŠYS™^|²:ŽH¬¿„¿´Ç†¾&x¾÷᮳ jÞñ­…¿Û‘­ÛùÏk­5¼ŠY%EbÁ¸G—~ÙÿòøÿeLÿÒkªÄøøbÿ†Øý—G+ý±³\7ûÿbû)ûØçn<ÜgŒæ½•“¹õÏÄø£Âß𯅼7/‰_TÕììoR,fÆÎáöËxùûËä¨ä×¢’$ð~|þØ^ðÞ‘ã_„ÿ4Ȧµñ§ãï Ø\ÜÇs:¬¶ßhË1òðBŒü¼÷ï^Ùû]x#âGÄ/‚ZŸ†~4ªËsi,ÖÐÝ)o,¢™^âÙ.<¶•Pr=)[aÜúV+ˆ.0H²àí ãò¥–h`]ÓH±®q– þ5ùûû/ß| Ñ~)ßxCÃÞ×~øÿû%Úm+W–f†îÍeŒ¼ñ7Ÿ5¼Ì®óïÁnÙø}à-ö¢øçñ“WøÎ×Ň‚5Ÿì GûTðZZA ×(wI9PÁÛ8;±ÛiÊ?I‹ O0° sÛئ†u߬‹ê¤úWÅÿt_„Ÿ ~ øsáv·¥ë¾&ÓuZßOÑôkù…ýýÓ4“Çl×-,oä¨ ¸¼˜Ú 1=Ο !Öþ~×þðŽà›¯†z/Œt}P^i¬ NÞìÙÆÓEp" Ë ˆË´‘ÔêhQ Ÿ«¦D\†`02rzZD–)#ó‘ÃG×p Œ}kóƒâOÃ{o‹Ÿ·ðw‰u;øü1oàKKËÍ>ÒîkT¼eÔ.V4‘¡umˆf FìNÜ‚ßÚáÖðçNøQû;øKí Áßü^#ÕRˆsl±Ò9ÚHã™°J«º@ÀÈ'(\úßVøÄšwÇ ü‹O â-ÿVÂ^#û‘Çå„æÝæg;¸ÇJâ~2~Õž ø/ñOÁ õë+‹«ÏIµÄL¢+î.Ö gÏ;$”²ƒÛi&¾tðÿÂ/üý½|¤xMi¤jÕ§[¸–x­¥Y¡GhD®å )e\’käß‹þ;ðoÅOþÒ¶·§s¨Ï ®ƒái¬të›ÈcþÄs4˜žÙ#ónO, ïbp*”Q.Gîùe\n8ÉÀÏ­1%ŠRÂ'W(pØ àúJü¹øñãËÏŽ²÷ÀØj·e׋|U Ayqe#A*Lð\Åt‚1"¶ßÀ×}¡|=ðßÀŸÛ?Á¾øl.týÆžÕŸR´’î{ˆ¦¸Ó䢜‰Ï›† ¶rG^õ<£¹÷‰5¸¼?¡jZÁY,-'ºÚ_ÉBøzãÅ|‹}ûcA¦| øuñ¼w©ÝüE¿M´Ò¬çC*ÜL'(<É«gÉÇnXWÍ> øY¡|xøWñ—ã§Ä-FöOGªxŠ :ñon!<@d·Š(Ò@~MÒ>em§šóvÓXÔ?c/Ù^ÇÃúˆÒ5;Ø%µá…n¼Íöà’˜Ÿå}§§ƒÐÕ(¡s¡ÿ?j OüFƒá'Ž|¬ü>ñ]ý¤·¶6úªFÐÞÃýï‘~×_ u¯Žþ'âdÞ(·ÔtmRKdÒßH¸òLÒÿ¡Á˜\NƒË/Ã>•/‰~Y|aý¼|WájÇÂÖ^°»¹Ó­®æ¶†òA*¬I/”ÊÛbä7PÙœF™ú`’Ç$bTpÈy F)"–)—|.²/L©~•ù»ûEøIøgáÿ…_³¿ÃË«ýÂ?¼c¶§å^ÎÒ‹G¤´Šy¤%bPx#…$­3Á:'ìóû^øÁ¿ MÆáŸˆ:&­ý¥¥5Ì×É>–#’¤YÊ;o(ĸÉ&—(î~ƒµÅºH!yUdnŠHýHHPYŽä“_Ÿ4o‡ŸSâ_~ü)×ø=û{x/Iðg£ê^Õ®N{‰n"µ™eŠ99Ý‘fUL¨8Ü„õ&´~|4ðßí;ñ㉾45ιká|øwHÒêx-,­­íâ•§C"’f“ï6q´‘‚M(.~Ž3¢¡v`ª9É™â-"òY¡Ò4±¨­až)`1ÌÒ¢ž½@?a|_ð/Ÿ‹?³yñ‰õ½#LðÍÅþ‰r׫ùocºX‡VrʨO™“ž£9£”.zÅ—Æ/jŸÛã~™òiD—]Ky6%É‚8 ÆÆ™UÊŒ}âï^ð#ö¢ð_Çß…ºÇÄï YÏfº Ü%ÝŒì†xÚüÑÊü¸‘*¾iø]ðkádžÿbYþ#èºQƒÄŸÃ»Óqsö‰Ü9ŸNv“÷o!ŒdŽÊ1Ú¾lø>dø ðçáÏÅX±ƒ>,xbïÃzñG¯Ú_M»~ÀÈ»¡'¦7“°Ur¢n}Û{ûièü ðWÇ /Âz†¥ŽuVÑì´È¤ˆ]}¤\Ol ±ù÷€ãýáïZÖŸ´ÇÄh­5mSÅüCáí3GÓoµ®®®- {l iÌ#“¹Âí^:šüç´µÕ/`ïÙ¾ÇC¾^¥qãÅŽÚìij yßV¿Êbo•ö6i᱃Á¯ÓOü"ý mõKˆ~*übƾ½³¹µ¸ÓWÃÖzy“í˜Ãyð±q·9ÇCÐÐÒ@™í_ <{§|QøyáL/miâ(o)gˆJ¹(ÄpJœƒJùWWý·´+? Iâ-ÁÚŽ±$ž2›Á–v°K–òö0»$Œ¶lŒáTœõ5ù¯ð¿âŒt=áÿŠaÖ.-4OÙüYYx’Õ%q «k7V²+€BŸ. ¿x£Šô9¼1­§ì½û:QF½â¿ˆðêj%¹ò./µ~Ërb|,»Ê}ÃcàÓäcôÛá¿Æï‰6ñe¿‡¼Ið{]ð…ŒÉ+¶£}5³ÁE,ªDN[.FÑÔ×’Ëûkjrj>0:Â/ëÚ‚umCIÔ5-9­çQ&+G+¬%–B0»±Žä×°|0øsûCxcÅ ª|JøÀž5Ñ„2!Ó×@³Ó‰‘±±üèX¿ËéÐ×õ?ÚPÝüsðïÁ èwÖ:‡jÿ/|' ‡Y¸±–K;IÈ$À³\%™@ù‘2A㨠vrþÑ:}®©ð£FÔ¼5¨é·¿Zâ8­îÂÃ>%µ³\:\ÆyÝò•Àï^ÿä/ÿ kÄÃSÇöèñf±ý©Ÿ¿ö½é¿v~o¦{æ¨þÙ:OеÏÿ³æ‘àxxc]¹Õuu´ÔÚÖ;ÑlâÁ™›ìò’e.ñœõY^Á~§Ô>$øÛ¤øsã„~ϧM5ÿ‹¬o/a»VQ+fŽì®§æ%¶b°>-ü_ñ†~&ü9øAà;H/uÿ^I=ã\hì´k%ßupB•ùÏ NÒüH¯mü%ñWÂ_·§Â+oо=_Þ\hºÓÛκ\_ÙãóƒÈƒäó¸óÚ½¼nðQø™nÛÿ 忳ó÷3ý¤ŸiÇûXòúvÍ.P¹÷32¨ÜÇw5çÿüUâXê÷>)ðܾ–ÇT½²¶ŽYCuio!HnÔ¨ágP ÙñŸƒ<9ñÃ×>ñ]©¼Ó/6bYd„¶Æ ¿×¢³gÖt{Kè4««è!½¹æ(UYdÿq ÜÝ; Ò¤0¯8ñ§‹¼S kþÒü7á¹5ëMfüÛj71ʨ4Û}…¾Ðêya»åÀ¯G¯ÏÚáÿ…|#ûC|ñ§‡-¤²ÕüOâ⺌És9[…ÌB´eÌ`dgE8‰Ÿ¡ôTSÜAk—72,0Ä¥ÝÜ…UU$“Àu&«éúž«[ Ý*î+Ûv$ !‘dBGQ¹IRv£’X¢¥pƒùˆ½jJøöŸð&…ñCö’øðûņâ]T°ñc\Á ÌÖâS ½£G¸ÂÈNÖäsM!3ïUš'-KcvÁè~•óÞ«ñs_ð¿í!£|$ñ%¤ áßi3\è—¨JuºêÖ\±S˜H–28Ü?‡5ù­û*i+áï~Ëþ>´¾¾—Yø€ž,±Öešòy–êÛM†D´ˆ¤ŽÈ«–…Bȯ²?k}ÿð¶ÿgìÝßÚŸðš.Í~Ëä7Ú³ßVsúÕrëa_Cîš)h¨()(¥ ¢ŠZJ(¥ ¢ŠZJ(¥ £éE´”Q@RÐIEQE´”Q@RÐQEQEQEQE-”Q@RÐIEQE-'µ|Aà_ù>¿ˆ¿öƒÿEéÕöý|AàOù>¿ˆ¿öƒÿEéÕêe¿ oð¿Ííé‰~Lû~Š(¯,ï )h ¢ŠZJ(¢€ (¥ Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Š(ü)) ÿÔýû¥¢ŠJ(¢€ )i(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¥¤¢€ (¢€ )i(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠZø#áV½¤ü!ý¥>&øGÆ·Qéð™\¦¥§Ü\0H%Ý,Òª ¨ Þy Ád+Ø ÷½|«ûDÛü×Í®‹ñ6ÖkÍRÝ–[«hßœ—ʦq÷uÝ·¡¯Žÿá[þÏ~ïOñ‹­à_ºŸè§äýУ¯=eâ¼ÔåÄbT*+&·Õ+tzz'S>¡„œ¨9ÅÙ¾¶júÙŸ­´Wä—ü+Ïô4ø»ò¶ÿ?á^|ÿ¡£Åß•·ø×úÑÿÐt~çþd®8oæþÿým¢¿$¿á^| ÿ¡§Åß•·øÑÿ óà@ÿ™£Åß•·øÑþ´d?ô¹ÿ˜®8oæþÿûGÆß³uÇŽ<[oâëïˆZý´úuÜ·z|Q_.Ååàˆw@N ÄœWmoðjÌx»Â6ÕuëýSUðÅšKpbÍйûÒMµæ¶í¢¿>?á^|ÿ¡§Åß•·øÑÿ óàGý .ü­¿Æ¹–yé·õÕ¯“ÿ3ÛŸŠ“”#MÕ¢šZÅhÓMi©»úßsï-Wöyð~§ñ§Nøâ—W6ºÅŠðEåýžvXÚ ò¥·mlpà¯{¯É/øWŸèiñwåmþ4¼øÿCG‹¿+oñ­)ñÂü¸Ø«ë³ÿ3‡âq µE.D¢¯-’Ùl}ëñëà6›ñãKðå•çˆu Ýx[VY²¼Ó >z\Å‘'úøå\)?w¨³Yß ÿfÏ ü3ñmïÄ[ícVñŒo­þÆÚ¾¹sö›ˆí²à ª¤q!* £ò'? ÿ¼øÿCO‹¿+oñ£þçÀú<]ù[mþ´ä_ô¾çþgúá†þhÿà_ð¬þ,~ÊÚ§ÅÏìë?¼Keg§j–ú¾›§ÛG¦ýžÂòÔ†…á/fÒ6Æù€‘ÜyWyðkÅ·ßÁ3üSñö¼7‚ò=} œZ€Úr°²En–ïèU¢9y¯ƒá^| ÿ¡§Åß•·øÑÿ óà@ÿ™§Åß•·øÑþ´ä_ô¹ÿ˜¿× 7óGÿÿ€}­ðÏöpO|C›â×|eªøûŦŴË{½H[Å¥£¸wHa¶Ž4™FXäõõ5Ÿã_Ù‚ Wâ§ñKá§õŸ‡!×âŽ-U´¯³Ëmä±É-½ÔR •W€ëƒÜç'?ÿ¼øÿCO‹¿+oñ¤ÿ…yð'þ†~VßãGúÓ‘Ðt~çþcÿ\0ßÍü þö·¿f}'ÇŸ ¼=àMoŚԚυ¯“TÓüDÓ#j‘ß¡ßSË ‰vlØXý“l´?‹zÇxï^ñ‹t8.m„Úƒ[,CsE°C 1¬j›Ù†ÌÇ.Z¾Iÿ…yð#þ†Ÿ~VßãGü+Ïô4x»ò¶ÿ?Ö¬‹þƒ£÷?óúá†þhÿà_ðÑX¾h±|n¸øæ/nN­q¡E 5®Sì®äH>]þag üÛpæ«üjø/á¿þ¶ðî»wu¥]i—°êZn£`â;ËÛ|ùsBÌg A `H"¿<¿á^|ÿ¡§Åß•·øÑÿ óàGý .ü­¿Æõ£"ÿ å÷?óúá†þhÿà_ð­üû(Ã៊Pühñõïx¶=2çJk‹ój‘yí+²aEË •ÛÔ±-¸×­üø= ü øygðóÃ×w:”6óÜÜËwy°ÜÜÏw3Í$’˜Õ$¶p¯ÎßøWŸ?èiñwåmþ4¼ø?æhñwåmþ4>)È¿è:?sÿ1®oæþÿúÇHýŽ|¢xAøse®jcGð׊NJì˜3 ŠÒ2ÙƒåǺù§ïôù«ÙuÏ„z.»ñ‡Ãn/ncÔü-§ßéÐ[&ϳË P»I•/¹|±· ' ×çOü+Ïô4ø»ò¶ÿ?á^|ÿ¡£Åß•·øÑþ´ä_ô¾çþaþ¸a¿š?øüéÿ~Æz6««x°xsÇ~ ð·…üw4—Z惧=ºÙÝ\L¡%ti!yaó@Aà?/w^ý޼-¬|ðÁË?kT5õ-?R¶6ßm3Ä&_|-bxAÈùùOþçÀŸú|]ù[ð¯>ÐÓâïÊÛühÿZ²/ú_sÿ0ÿ\0ßÍü þöG‚?eÃÿt¿Š>7ñž¿ñ ÄZ sG¦K­ÜDb²ûB”‘â‚Þ(cÈJî*N=ñGÒþhzOƽoãŒ7×/ªëšU¾“-«yfH­Ü:ºaCï$s–#Ú¿ÿÐÓâïÊÛühÿ…yð'þ†~VßãGúÓ‘ÐrûŸù‹ýpÃ4ð/øÒ·_±f˜dñ6‡¢üDñ‹àÝ\ÞßøzÒKu¶iîÉi‚Nдé±Ë"¸¡àz«ÏÙÀw|1ðDêÚ”)à¹ÒïGÖa’8õ+KÈäyteAàd#iM¤c# òü+Ïô4ø»ò¶ÿ?á^|ÿ¡£Åß•·øÑþ´ä_ô¾çþaþ¸a¿š?øüëß~ʶ¾ø¹gñËÄÞ;×}1çÔM²ÂÖÒ•(‹ƱˆÈb¡1’ì͸œÕþÌßü@Õ¾&ü1ñγðã\ñFš·öP¶–Úý¡c–X.¢•ª2®'9ÉÏÇ_ð¯>ÿÐÓâïÊÛühÿ…yð ÌÑâïÊÛühÿZ²/úÜÿÌ?× 7óGÿÿ€}“ãÙkHñ¿ü!áÛïë‘ø“À÷m¨iž%3¤º¢]Håäy ¡ÕɦÀ¡UT£WÀŸ²¦›á?‹–?uÿëž-ñ}µ…Æ5Æ¢öþ\ÐM·jˆ¡Š5‰bÃlXö‚Y™·1&¾@ÿ…yð#þ†Ÿ~VßãGü+Ïô4x»ò¶ÿ?Öœ‹þƒ—ÜÿÌ?× 7óGÿÿ€}eã¯Ù{Å9¹×l.~1ø¦ÓÂÞ#–W»Ñã6oÅ1%à†âHxâ ãhsÀÀ¯£´ŸøgDð-§Ã}:ÌGáû94¨­É$ Hâ,y? Á'“×­~_ÿ¼øÿCO‹¿+oñ£þçÀÿ3G‹¿+oñ¤ø§!ÿ èýÏüÃýpÃ4ð/ø×^ý•ÁžÕ~Y|Fñçƒ/´‹ÍßGºk9`²·»ŒÄ 2ý˜NZ%c凑”wq[ÒþËž¼ý›ãý™uK«»ßÃh-c»“Êûb2ÊgŽe!bD|ù1Ç ó_ÿ¼øÿCO‹¿+oñ£þçÀú<]ù[?õ§"ÿ å÷?óõà üÑÿÀ¿àOÝ~Å~“àgƒ>Xx§XÓìü ª6¯a©Bm¾Ýö“<÷ [t&,#ÎváÝÏ9ï|ðÆ~ ñU§ˆµoŒ^+ñMµ¶ýú~¤l ¬Û”¨ßäÚÆÿ)9aȯˆÿá^|ÿ¡§Åß•·øÑÿ óà@ÿ™£Åß•·øÑþµd_ô¹ÿ˜®oæþÿúÛOýŽ>iÞ ø±à¨¯¯Z×âåýÞ¡+y>e£Ý1uKlG€¹-ðĤÕÏ~ÉþñgÂ߇¿ ¬üGªh1|5’Â}.þÈÛ‹¿?NŒ$2¹’'v@s„7·ñïü+Ïô4ø»ò¶ÿOøWŸ?èhñwåmþ4­9ý/¹ÿ˜®oæþÿûá÷À¯x+Å6¾"Õþ0x«Å–¶ë m?S6&ÖRêTòmc”à 9×að“àÞ‡ð¼ZÚ%õÍ÷ü&õþ¿qöŸ/÷SßÈdxãØ«ûµ' »-ޤ×çü+Ï?ô4ø»ò¶ÿ?á^| ó4ø»ò¶ÿ_ëNCÿAÑûŸùýpÃ4ð/øèoÆ¿‚¾øåá8|3â;‹­:{ ¸u ?P°“ɼ±½·;¢ž €Ê}Aˆq_ ¿fÛxîûâ§‹|YªøÿÆWvk§G¨êæ6¶JÛü˜!·Ž8Ð3rÇ$ž~fÏÄÿð¯>ÐÓâïÊÛühÿ…yð#þ†~VßãOýiȶúòûŸù‡úá†þhÿà_ð®ÐÓâïÊÛühÿ…yð#þ†~VßãGúÓ‘Ðt~çþcÿ\0ßÍü þúcñÂït¤xźƒ.Vd“íÚZ[<åW9Œ‹¨gkgŸ“ùš|]ù[ëNCÿAÑûŸù‡úã†þhÿà_ðlñÁÏüKý½ü5ñQÓg°ðwÃ}%†ù“j_j™q lzª 9 íþ.?@kòKþçÀú|]ù[ð¯>ÐÑâïÊÛüh|QÿÐt~çþaþ¸a¿š?øüõ¶¿:üã«Úö…øGáÿ‡>±5·€õÛ½OZ¿¼Ó.¬m-ã‚Ud¹HÄŒïò®ÀAÈ â¼›þçÀú|]ù[>?‡_Ý·ŠüZ€÷"ßòÉý(\QÿÐt~çþaþ¸a¿š?øüï?Úz?\þÏþ<Ó|¤Ï®k:–•=•½º%Ý%¶¨äíW-øV'ìð~ÿàoÀ/ ø YlêÑÄ×wÉDWWMæ¼KŽ!3žH$pkÈ´¿Ø»áö·§Áªi>9Ö®í.WtrÇ¿òoømà_Ù7Á¾ƒáE¾­j7#á#ëXŒ9º:Èq/Ú6Æ>æó³fÞƒ9®ƒTøEâ?~ÒZOÅÏÝZ¿†ü¤Ëm ÙFîÓFø•»»Z5EÄXŠ0®ç‰Û’+ÊᆼÿC޽ÿbÿãtŸðÃ^ÿ¡Ã^ÿ¿±ñºWÃÏïü•ÿ˜{zÿóëÿ&ÿ€}ÁE|?ÿ 5àÿúuïûûÿ¥ÿ†ðý:÷ýý‹ÿÒú®þä¯üÃëùõÿ“À>ߢ¾ ÿ†ðý:÷ýý‹ÿÑÿ 5àÿú5ïûýÿ£ê¸_ùýÿ’¿ó¬b?ç×þMÿû~–¾ÿ†ðý:ïýý‹ÿÒÿà x?þ‡{þþÅÿÆèú®þä¯üÃëùõÿ“À>ߢ¾ ÿ†ðý:ïýþ‹ÿÑÿ 5àÿú5ïûýÿ£ê¸_ùýÿ’¿óoˆÿŸ_ù7üíú+âøa¯ÿÐã¯ߨ¿øÝðÃ^ÿ¡Ã^ÿ¿Ññº>«…ÿŸßù+ÿ0úÆ#þ}äßð·è¯ˆ?ᆼÿCŽ»ÿbÿãtà x?þ‡ {þÿEÿÆèú®þä¯üÃëùõÿ“À>ߢ¾ ÿ†ðý:÷ýý‹ÿÑÿ 7àÿú5ïûýÿ£ê¸_ùýÿ’¿ó¬b?ç×þMÿû~ŠøƒþkÁÿô8ë¿÷ö/þ7Gü0׃ÿèq×ïô_ünªáç÷þJÿÌ>±ˆÿŸ_ù7üíú+âøa¯ÿÐã¯ߨ¿øÝðÃ~ÿ¡Ç^ÿ¿Ññº>«…ÿŸßù+ÿ0úÆ#þ}äßð·è¯ˆ?ᆼÿCŽ»ÿbÿãtŸðÃ~ÿ¡Ã^ÿ¿Ññº>«…ÿŸßù+ÿ0úÆ#þ}äßð¸(¯ˆ?á†üÿC޽ÿbÿãtà x?þ‡{þÿEÿÆèú®þä¯üÃëùõÿ“À>ߢ¾ ÿ†ðý:÷ýý‹ÿÑÿ 5àÿú5ïûýÿ£ê¸_ùýÿ’¿ó¬b?ç×þMÿû~–¾ÿ†ðý:÷ýý‹ÿÒÿà x?þ‡ {þþÅÿÆèú®þä¯üÃëùõÿ“À>ߢ¾ ÿ†ðý:÷ýý‹ÿÑÿ 5àÿú5ïûýÿ£ê¸_ùýÿ’¿ó¬b?ç×þMÿû~ŠøƒþkÁÿô8ëß÷ö/þ7Gü0׃ÿèq׿ïì_ünªáç÷þJÿÌ>±ˆÿŸ_ù7üíú+âøa¯ÿÐã¯ߨ¿øÝðÃ^ÿ¡Ã^ÿ¿Ññº>«…ÿŸßù+ÿ0úÆ#þ}äßð·è¯ˆ?ᆼÿC޽ÿbÿâ(ÿ†ðý:÷ýþ‹ÿÑõ\/üþÿÉ_ù‡Ö1óëÿ&ÿ€}¿E|Aÿ 5àÿúußûýÿ£þkÁÿô8kß÷ú/þ7GÕp¿óûÿ%æXÄϯü›þöýñü0׃ÿèq׿ïì_ünøa¿ÿÐá¯ßè¿øÝUÂÿÏïü•ÿ˜}cÿ>¿òoøÛôWÄðÃ^ÿ¡Ç]ÿ¿±ñº?ᆼÿC†½ÿ¢ÿãt}W ÿ?¿òWþaõŒGüúÿÉ¿àoÑ_ÿà x?þ‡{þþÅÿÆéá†üÿC†½ÿbÿãt}W ÿ?¿òWþaõŒGüúÿÉ¿àk^ÞÙéÖ²ßjÚÛ@¥ä–Wˆ£©fl©5ðßìÿÄÚKâ_Åí9C0G¦[Ï·lSòT2îä’–ÁñÔ€$ ײý†¾ ¨¤ÖõýkV¶‰ƒ}žYãXÜïv1‘ò•>â¾¶ð¿…<9à­xSN‡LÓíó²W'«Õ˜÷bI=ÍS«F9Æ”œœ•¶²JÿðìêÕœ]EdµÞ÷gAKIEy'¢QEQEQK@ KIK@ KEQE”´Q@Q@ K_/þÒ_µÿÁÙVÃNŸâ®§2ßjûžc ¹¼š8ÙUäÙ•DE-÷¤u È]Ä_ ÿÃädùóñ7þ  ÿäšý^¢¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþJ ÕÚZü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù&€?W¨¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’¨õv–¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþI Õê+òƒþ#û#ÿÏŸ‰¿ð]ÿ$ÒÿÃädùóñ7þ  ÿäªý]¥¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’hõzŠü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù*€?Wikòƒþ#û#ÿÏŸ‰¿ð]ÿ$ÒÿÃädùóñ7þ  ÿäšý^¢¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþJ ÕÚZü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù&€?W¨¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’¨õv–¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþI Õê+òƒþ#û#ÿÏŸ‰¿ð]ÿ$ÒÿÃädùóñ7þ  ÿäªý]¥¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’hõzŠü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù*€?Wikòƒþ#û#ÿÏŸ‰¿ð]ÿ$ÒÿÃädùóñ7þ  ÿäšý^¢¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþJ ÕÚZü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù&€?W¨¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’¨õv–¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþI Õê+òƒþ#û#ÿÏŸ‰¿ð]ÿ$ÒÿÃädùóñ7þ  ÿäªý]¥¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’hõzŠü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù*€?Wikòƒþ#û#ÿÏŸ‰¿ð]ÿ$ÒÿÃädùóñ7þ  ÿäšý^¢¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþJ ÕÚZü ÿ‡ÈþÈÿóçâoüAÿÉ4¿ðùÙ#þ|üMÿ‚è?ù&€?W¨¯Êø|ìÿ>~&ÿÁtü“Kÿ‘ý’?çÏÄßø.ƒÿ’¨õv–¿(?áò?²?üùø›ÿÐòM/ü>GöHÿŸ?àºþI Õê+òƒþ#û#ÿÏŸ‰¿ð]ÿ$×´| ÿ‚’þÌ«È9!ü§×_ÙZ_üùÃÿ~×ü+ðw‰<)ãq­Ä/ö£6™?› .fQ˜®3•ÃŒ>•àÿ†^#ð®µm«ÜøëWÖãòÝní¯š9a™™p¬€(1n@^à×ÌÞð‹uû/Šú®âËï[ézö§-´6%có/$vyØ©fM¢5xœ’0,=7}:qþSꟈ9øðÎÖÆëÅPˆ—Q”Á†ÔÎÏ ]Ø ŠOJ§à_‰ >"Þ\éžš)5 5ß-¬Öæ Ñ8ŠHªqÈÎ:dg¨¯–¼gã­{Ä^øã««7ÕµiuE‘ ƒjIs,A“ ü!œ®zc'¦+´ð&³©üKý£?á%ÖôŸøD/|/¤¼N¹$ß]¥Ë%o‘TÄ™ãá±Øñ_U…µBåö_qõ÷öV™ÿ>pÿßµÿ _ì­/þ|áÿ¿kþð|Hñ?õoj·zÏŠt˜¬¯®,ô¸4:Ò5·;D“¸½fo¼‡¨Ç¦j¾"éßt}sR´:gŒµ‹Øtu{ˆJšW+öƒÿ,Ô:nöâ¥á#Ù–¬?²´¿ùó‡þý¯øRÿeióçýû_ð¯*ð‡Ãø_XµÕîüy«ëJ#u»¶½hä‚feÀdP£Ê*ܧà×–è?µøWâ~—ã ç½Ö< s4¶ÒÜrÓZÞöYé» JŸövúÔýZ/d‡ÉöO©¿²´¿ùó‡þý¯øQý•¥ÿÏœ?÷í¸„v^'±øq¡/Œ¯f¿Öç¶Yþñd›÷žYô1‚ûŠôz—F ¢)R‡böV—ÿ>pÿßµÿ «{m¡iÖsßÞÛÁ½²4’9HTA–< ð|UâŸÞÇñ/Ä–¼k®øÎ+„Dk$h´÷·#ÞUÕ›w_7 ïØ{ÿÄO ]x‹áx–ÛÅwÊ,t¹$k«7QGß— T«ãwÉóqŠ·†‚µÒû‰P‡cÓ4Y|1â="Ï^Ñc‚êÃP‰'‚Uˆñ¸Ê°ÈõâÍsÃ^M.MGMiÆ­} „_g·m–`ÅYð>TN[·俳ƒo´ÿxOÄÒxR»‚ëH€®Ÿ4ˆlâóH¡ ˜ÂüÇ øØ|Oá_xOÄú/‰µ!Ö5ý?N›OŸdòdG/…Ù»-°gæîhú¼9­d/g_”ö_j:w„ô‘ª'‡§Ö ‘c0Ø[¤²€A;бQ´c“žâª|>ñ'…¾#øV×ÅÚ.äZ]¼È©q ,ŠÐJи`2>òý+½¸ÿyÝoå^û.ÉÓ?ëóUÿÒùé{rÞÅ{(ßcZëâo…ôÿØxSðÕåΩ<°Z]KkÚÎaPÎÈû²T9ÛÜW¯eióçýû_ð¯>0ÿÉjø9ÿ_Z§þІ¾š¢Taeî‰RŽº"öV—ÿ>pÿßµÿ Oì­/þ|áÿ¿kþ~¾Ò,¾$xòÃâf·/Žõ=.? k:¬:l6¬Šª-YD‡næ@P™À“’hŽ¢§öO´ÿ²´¿ùó‡þý¯øQý•¥ÿÏœ?÷í¾GÖþ)xÛ_ø]ð¶×F¿þÌ×> Íkis)xT(ÉT3u:Ž—G—Å¿ >2h>¾ñ%ç‰4Y]Iö‹,·÷6˜f+ Pv0eàð9ôþ«ÈžH)î¿Ú~ÿ„”x;ýûdڛ߳y_7ÙÕÄfLíÛÌ\óÒ·¿²´¿ùó‡þý¯øWÅÍðßSoÚ4h_ð™ëBfðÜ·?mó£ûHQwù!¼¼y|îÆ39¯´t›Ó4»=6[©ožÖâiç!¥”¢….ä 62p4§‡‚Ù!Æœ”?²´Ïùó‡þý¯øQý•¥ÿÏœ?÷í¯×Ä-ïÄŠñÅ­7ÆWº3Ø\_G¥iöâ?±¬c*&VR]œun ò1Ò”pñ}Ý8/²}£ý•¥ÿÏœ?÷í“û+KÿŸ8ïÚÿ…|‹ÿ ߌÂZ¨Õ§û_ˆ5]*ùr7\Ç1ìœr¾+Ò5Ïköÿ´—†¼)ô‰¤]h—7Ú‚<·•]¹Î@©ácÙ ’rþËÒÿçÎûö¿áGöV—ÿ>ÿßµÿ øãÃ#âÆMÅ_,ük¨hÊöò "ÊТÚǨÊЩ2—ãv}ñŽ+¥ñÅ뿲süL°¹m;ZžÆi ùJÌ·) ¬œp†ã°4Þ=¹!ü§ÔÙZ_üùÃÿ~×ü)?²´Ïùó‡þý¯øW…ü=ðާ]Ç^$ñÞ§u{<1Os`k`RXóå‚ç+‘óçq#9íX<]âmSáÄ]cQÔ¥¸½ÒõMn+Y\ÐÇn€Ä«ÇD=*~­È|þSéì­3þ|áÿ¿kþeióçýû_𯌓UñÏ´ï‚Ú x¯PÒ[Å-äúÕ£ªÏ4‘A«*pÛ˜òêkœøAâïˆgZø_â Å7º´~3­Ý¤ì†ÝVÉ@…‘UF<³õ=Ï\×Õ#m¹aü§Þ?ÙZ_üúCÿ~×ü)?²´¿ùó‡þý¯øV…%eì¡Ø¿eÅì­/þ|áÿ¿kþZ÷ú£g5…îŸÖ÷ QÐÆ¸eaƒÐf¶(¥*jÎ+⛿e»‰¦øuw®Y`Ô¦HÁ9 ¦(›Ûsõ5ô|Ñû+ÿÉ=Ô?ì)/þˆ‚¾—¯àÇÿ x{ÿ)âp«ÿ„ê>EµôçÐ E´”RÒPÒQEQE-%-%QK@%´”QE-%PE´RQEQE-%-%QE-%PEPÒQEQK@%PGÒŠZJ(¢€ (¢€ )i(¢Š(i(¢€ (¢€’Š(¢ŠZ(¢ŠJZ( Š( ¥¢Šüið7‡ô?Ác¾+Eãuˆü-á;K-.H¶³m!wƤcpS`œãy#œcõÛþ/ ÿÐËÿãÿâkòƒàßü¦Gã·ý‰–ú#A¯×úç¿áð¯ý¬¿ð?þ&—þ/ ÿÐËÿãÿâk ¢€9ÿøDü+ÿ@k/üÿ‰£þ/ ÿÐËÿãÿâk ¢€9ïøDü+ÿ@k/üÿ‰¥ÿ„K¿ô²ÿÀxÿøšè+ÀjuìúçÄ+{û›;¨–3i iðÉ3‰‹8|°Œ…ÚÎ3ŠõÿøDü+ÿ@k/üÿ‰£þ/ ÿÐËÿãÿâk ¢€9ïøDü+ÿ@k/üÿ‰¥ÿ„K¿ô²ÿÀxÿøšè( þ? ÿÐËÿ£ÿâhÿ„K¿ô²ÿÀxÿøšè( {þ? ÿÐËÿãÿâiáð¯ý¬¿ð?þ&º (Ÿÿ„O¿ô²ÿÀhÿøš?áð¯ý¬¿ð?þ&º ð/ÚSö…ð¯ìÃðºç⿌´ûÝSMµ¹·µ0X,m9{–Ú¤ ^5Àïóf€=sþ? ÿÐËÿãÿâiáð¯ý¬¿ð?þ&·Qƒ¢¸èÀΟ@ÿü"~ÿ 5—þGÿÄÑÿ—…è eÿ€ñÿñ5ÐQ@÷ü"~ÿ 5—þÇÿÄÒÿÂ%á_úYà<üMtP?ÿŸ…è eÿ€Ñÿñ4Â%á_úYà<üMtP=ÿŸ…è eÿ€ñÿñ4¿ð‰xWþ€Ö_øÿ]ÏÿÂ'á_úYà4üMð‰xWþ€Ö_øÿ]cøƒY·ðx‚íH4Ëi®¤DÆöHP¹ œ 8É[þ? ÿÐËÿãÿâiáð¯ý¬¿ð?þ&¼ßö{øÝáßÚ3á…ñÂv7zn—¯¡¢1p†Öæ[WÞ"wNZ"FðFpx¯h þ? ÿÐËÿ£ÿâhÿ„K¿ô²ÿÀxÿøšè( {þ? ÿÐËÿãÿâiáð¯ý¬¿ð?þ&º (Ÿÿ„O¿ô²ÿÀhÿøš?áð¯ý¬¿ð?þ&º (žÿ„O¿ô²ÿÀxÿøš_øD¼+ÿ@k/üÿ‰®‚Šçÿáð¯ý¬¿ð?þ&øD¼+ÿ@k/üÿ‰®‚Šç¿áð¯ý¬¿ð?þ&—þ/ ÿÐËÿãÿâk ¢€9ÿøDü+ÿ@k/üÿ‰£þ/ ÿÐËÿãÿâk ¢€9ïøDü+ÿ@k/üÿ‰¥ÿ„K¿ô²ÿÀxÿøšò~ÐþðWǯþÏš†Ÿ{>¹ñ ÞþæÎê%쥄2LâbÎ,#!v£sŒâ½þ€9ÿøDü+ÿ@k/üÿ‰£þ/ ÿÐËÿãÿâk ¢€9ïøDü+ÿ@k/üÿ‰¥ÿ„K¿ô²ÿÀxÿøšè( þ? ÿÐËÿ£ÿâhÿ„K¿ô²ÿÀxÿøšè( {þ? ÿÐËÿãÿâiáð¯ý¬¿ð?þ&º (Ÿÿ„O¿ô²ÿÀhÿøš?áð¯ý¬¿ð?þ&º (žÿ„O¿ô²ÿÀxÿøšü†ÿ‚Åøgþø%à/‰žÓmô¯hÞ.´†ÏRµa¹†7´»¸*®€qæÛÄã=AÉÏìÕ~@Áj¿äÖ|-ÿcþ›µý¢Š(£ð¤¥ü(ÿÖýû¢–’€ (¢€ )i(¢Š(¢Š(¢ŠZJ(¥ ¢Š(¢Š(¢Š(¢–’€ (¢€ (¢€ (¢€ (¢€ (¢€ )h ¯ ý£þ/ÆŸ„ï‚­˜A«ì[Ý&äpÖÚ¡óm¤SÛç[Õ‡BkÜé(Lñň~?üWý›<3à;/\h^,ñÙ´Ò¼K#KE›š„ìVR$YX"¡bÈøÂžŸ`xWÃ:7‚ü5¥øGÃÐ m3F¶ŠÒÚ1ü1B¡'¹Àä÷<×AINà|Ñð;þJÅ/û ýu_K×Í?ÿä¡|Rÿ° ÿÑ÷Uôµ|¿Ⱦ?âŸþ—#çø_ýÊ>³ÿÒäx™á_ø3ã–¡¬hv-qá_Û oÙBÚj6à !RÙÄ©ÁÚ9$gFv/~ øR; Ÿ ZÛèöÚ¤ÚŒ±GctÏ£brIÝÕ‹p1Ò½–’¾§žÿ*9ß øOÃ~¶¹³ðÆ™ow;ÝI µ Ò‚ô\…µtTQSqŸ4ü\ø[ñ[â.·jÚf¿¥YhÚMõ¶¡a ¶²¼ë4?Ö¸|2—Üpàž+ÔŸÃþ2Ö~j~ñN¥k.¿©YÞZµÝ¤O Ó«¤L³0ج¹ç’ è´•|ïarž5ð3᮳ð¯Áá­wT]^å®dŸÎMàmp_œ“Æ+ÌWà÷Æ{ˆš×ÄM7ÄÚ=Æ¡©3E—ÖsNö–›‰H!Ú꨸Àl ±$’IúΊ=£»båG&âÝcáÖ¡á¿jÒkº…Õ«Ý[Fñ@dtGT,Xgž£Šåþü3Ö¾x:_ 뺢ê÷]I8™w€€¿9'ŒW´RRæv°ìç¿>YüLð…dž.n^ÆVxç¶¹ïÁqŽEät#ÔW¡QI;j6ð–ñÎjÁ¼oâ "çI²W­œ‘Ü^1Œª™ZFdL1|µ\‘ºq\·ÂÖµ¹þëzdz6½tײYj¶ÒËök‰p$h^•87@ìIúFŠ®vO)ñßí£x±¾ø3Eñ¶·:ôþ$Óѯà…bT–C.ÇHÇ#äãšô/|2ñö¥ñKøñGY±¿—ðMm¦Á$y“©I'—Ìw%Š08Écûµî›§jKj6±],,±‰Q\$‰÷]w†ˆäUÊ~ÓK.§Ì3|#ø£ámOÄÐü+ñ%†Ÿ£xªy.åŽöÞI'²¹™vÉ%³Æár@Þ08ã'¤»ø ¢IðvÇáEô¶Í¥˜§µ¿ <Èï!1gÛœrÄü¹û§Ï5ït”½£Tx÷„4ŸðëVã{H¹Ò¬ãut°´’)îÜ®ÕiZGe]§æ"0¹>ÜRx+áÿ…ôi—±NÞ*Ôï¯âdV$»…#U`z•)’E{œØì|ÇaðW´ðÇÃ-µXO_ý®wظ]ÌÛPuæï]ߎ~jZï<)ñÃ7ÑiÚ¦#Åqæ+2ÝXÍþ²·¿R¤ä9ê+Ø©)ó½ÅÊ›í¾üPð±­Ið£[ÓE×nÞùìµki¥û-ÌßëZ†D%[á³ù䞳ğ u_ü0_øßZûF¶®· ©[±yWQHd‰ãŒ`a’¹ÉÉÍ{%-ì9Qã~Ò>8[ëð›ëúEÖ“f޲-¤‘Ü]±MªÒ´ŽÊ¸?1òÂäûq\_ÄïÙù¼ñKñ¾¤–zs%´Z½™B~ݤÂxÁ#ŽH Ùì£ëéz(çwºTQKPQóŸŽ<ñÇÅ0k>‹^ÐßÃú»HŠ÷625å½¼œlR®"b£…b¹ïœó^« xÃAø}gðõ&yí-tñ`do¾ëåìfÇ ÉÇAÒ»zJ§'°¹OøSàŸ‰~µ±ð¦±­éº¯†t¨ÞÛe¬±_íR< òy†"x8Lž9ëž{âß‹¾>Ôì…¦³¢ZéZ6§§`ir×d Ê‹;‰¶²üÇ;UONG£è§Îïqréc˜ð¤>0I1øêâÆëQ.ß6Ÿ°Áå6²É#në“»8¯)øoðÓÇÿ uYt};^²½ð\—W)k=³‹Ø~ÐYÊG*¸L Nå9çÍ{í-.aØñß‹? 5?Iáý{Ã:¢i ð½Ó\ÙÍ4~t,$dŽD6Õ9¶;äz…âñ<:%¼^1¸µºÕ:K(Þn;6¤Œì0¸-ÉÉ­ú(æÒÁn¡^5àÿ†þÑj3ü)ð‡„m5ˆík»aןN˜½öŠ®v.TxŽ>xÂ÷ÇÚwį‡ÚŦ«ZØÉ§O ý»Ïo5»¿™Ï–èà†ðàí}{I]U4»4×d†]EaAröêÉ M´o1«–eBÙÚ RzÖ%'+ ÷¯µï„ü øÏDðOˆlm𧉬l|-ã ›«¹¶²I{n׃÷±ÄÁÕ·ÝÜA!ynkêÚJJM Å3Àü;ðƒSÑn¾ÜK¨C ð&›sc8Taö†ž(£ ŸÝÆN­sþø ¬xr‡OªÁ1ðDÚ„“mFx½ÆÐ™é·ç­}9E?hÅÈž¼IðwÄ:ßÇ]âµ¶´išTÅ%‰n ˜;yóQÚ¾…¢Š—++QHgͲ¿ü“ÝGþÂ’ÿèˆ+ézù§öVÿ’}¨ØR_ý}-_1Áò*Ãÿ„ùþÿ‘u@¢Š+éÏ  )i(¢Š(¢–’€ (¥ ¢–’€ (¢€ (¥ ¢Š(¢–’€ (¢€ QKI@Q@´”QE-%Q@Q@Q@RÐQKI@Q@´”QE-%Q@RÐQE-%Q@´”RÒRÐRÑEQE%-PEPäÁ¿ùLÇoû,ÿôFƒ_¯õùðoþS#ñÛþÄË?ý ×ëõQE%-P_Ž¿¶÷Å_ |ý»>|Pñr\K¦hz/ˆYâ´ŒÍq4³ZÏ0Äœeå–DEÉ-É&¿b«òcö­Ò4Moþ =û/Yx†4–Õ!Ô®\¿h¶Y'¶8=Äè„{â€:ÿ~Úßµ/‚tø—ã/ÙkTÓüoÏsrší¬úµ·§–Á"óP*’Y.Üî _Nx›ö¢ð}¿ì¹¨þÔþ·>)Э´§Õ-í¼ß²<Â6Ù$.å$òž7 ¯ò¶Hæ¾”ºµµ¾µšÊö$žÞá9#pa•à‚5øû=³§ü¿öƒÓ %ô­?UñqÎA´Ú²íöÞÏøæ€>ÑøoûsüTøñ¯øuþ|»ñƒf“M‹]ñÆ¥­­Œ×IÞÅl% Ѳóv\e8@'о8þØ^#ðgÆ+ÙßàOËŸŠ_¾Âº•õº^ŧYéÖ®FÆžâUe AS†Ø¸tù‹0Zîÿa¯i¾ý¾iú\K SøzÊõ‚Œ5ò}ªf>í$¬Iõ5áÞ:ýª>.xÏöñ'ÀÙ/À:N¿âE zÿˆüA;Á¦Ù4Ü‹}°by0Üpù,¶2¨^€7>~Ùþ5¿øÛ§þÎß´ÂùþøÇ]¶’çH•uu+ @D¬î‰4Jª¯µNgÉ[kSÐ~Òÿ·…¿fŠð‹¼9s©Yx›L½¾ŽîÎ]÷h·ÊÁg  Œù²\I¶5&T XÆkáOÑ~Ô?µÏì¯uûFêšêO]¦œžKÔhÑÚÑnDæó–F`qóg­{·í5¡i¾#ÿ‚“þÌ~« ÏVzÍÚ«ÀMe×0¶u’5`{jµãÛÇöŒøG¥[|GøÙû5êøsq4)6¡µmy}eÌ$¸´HÃ#@Ù)ˆo! î ·>&~Ðÿ ¾|“ãïˆõ#7„µå´¶ËæIx·¡M²[¡#sK½vä€,Ä($r?¶­®£û$|^·»A"'†59€#?<4¨PGÒ¿+þ,9×doØÞ!ojž#ðÌ€~Q¢Hü•FSÁ_)¤=…};«~ݵ‡ü$>/ëß²î£gðáarÝnÔa³Ææ¹’ÌC梄ùˆtPÉp9®gþ Eñ+¿¿àž6ÿ¼;\hž"¾Ò.­‹®Ù4Ä2:ó‡Ã#ŒÖ¿[¯¬lµ;+7R·ŽîÒî7†heA$rÇ ÚèèÀ†V‚Áü¸ÿ‚«x{@ð¯ì;} x_L¶Ñô»M_MÚY–ðDvf aUrÄ“É$õ  Ù¿mÚÅšU×¾~Κ>Øù† ^ëUƒMºÔá‡!¦³±xÞw±˜Ê¬…Çð†Ê¬ÿf_Ú/Á¿µ‹Š~ †[(å–KKË+‚¦k;È0d…Êðxeun7#)!I*=“Âú›á i>Ñá[{ &Ò KxÑBªCb4P+ócþ ¡zm×í¡Y.ÊÃâV°Æ8T„àŸ*(ü(ôÇYÕôßh÷Úþ³8¶°Ó –æâVÎ#†.îqÎA5ù þÜß´wÄ Oñkàïìãw↼æ ërÖÓQ¾·¶,²M‘œí*À*y›ˆ*¤œãô›Å>Ó|aá_ÂZÊ—Óõ»;‹…S‚a¹¢sƒµjü ðO‚ÿoߨ‹ÂßðƒøCÑ>7ü1Оyl-㕬5Ø-^F•£áY·3ª· “„ã >ªoÛOÂ:§ì}¨~×^ÑfÔìtëVšM&âak:ÜE:ÛËÊ©(]¬wÁ—<}9ðÓÆCâ/à üAŸÙãÄúMŽ©öo3Íò>ÛOåù›SvÍøÝ´gÀé_œÿ?h? þÓßðMˆÿ¼i>ždm/,nó¬ï-®`óab¼0ƒ+ eH$)Ê·f)oÙ¯á4±0d h$Ѓa r_ ?iøŸñïâÏÁðñÓOÂçÓß›¿;íßo¤ÿSå'•³n?Ö>î¼t¯’|1ÿ(ñÄÛ­SÀÿ~ ê>-ø‹§jÚ…“éj)¥½“"-õÝü°Ç"gfTŒ©å2W7?dk«{ÏÛËö´–ÙâÜèQ’?½S#Á”ŠÁÿ‚Ti:ZwÇmvÓûbëÇ—ö×Þm­Ô<ž¸,ؤЫxöáñŽ—ño@ø'ûR|)»øO®ø±¼­ø_Ūi—×¶ˆDðª¢;ªi>fPÛ7.zÏÚcöãð§ì½ñKžñ§‡®o´ßi—×ßo´—|é5°a ¬V‚3æÉq XÔ™P)pO¯ÿ‚º[[§ìéázÕû{Kñv–ú[‰ÃG>Q[¯ÌHï´²¿jÍ'DÖÿࣿ²õ—ˆ£I-RJáUÀeûE²É=±Áî'D#ßÔkÿ¶·í[à_‰¾8ý–õ-?À0 žyâ×-¦Ôímx&y¬V/5v©Ë#ªÁÞê5ö<ñ£öqÔþ'øìÞèZþƒ=»²ìqˆdGŽEþÕ‘ÆNOZ÷K»[[ëY¬obIí7‘ÑÆX àƒ__ðNÑþÄŸôûr[J°×|OœÙ$Ae )ÛÙÿЫÿÁ:üiះ?ðNøïÆWɦèšíÝÝÄ™ÄqEªÞ€2YEP b@’PÑ?mßÚoâ>Žÿþ ~̺ˆ>üò[^Þëvº}ýô ŸÞÁfѼ„|¢1(nŠÄ×ÂZÍΫmÿBðúi¬Ë ú”‘Ýí8ÌðÜ6¨2¯ÐÞÁI4¿è:g†¼3ð­4‹K Xl•nµeQmJ±lÅ} ð_ö­ðGÇ_‚š×ÅÿØÜÁqá¸îÓTѯ1 Ýõ”>l–Ò¼m|`ƒÈ GÊÿ‚‡|Søùá›ßÙ¯à]ߌuxmüÝmgÕá²Ó´¹™ÜGj·—D·34j²P›C¯^qcàìûñëá-ßíñ'ã^²_‰Ö¨ M{‰`ŠònÌÌxÔ¨8±;˜–®Ëþ G¢éÚWìEà»ë(V9µ‹½^êå”d™u íÃ6:‘(¹=€¨‘ð¯üÆÇÄš=ï„áXjããmžªtuð4r«O,á]Úµ´j±ÛF¨Þlîø$!ë®ð_í»ñ'EøÕá‚_µ þ_xéš=PUƒU²¹¸ž¬Xª|®ÌÐ2*°jóÑÇü‡ã¶³ödûM¿†4¦¶‘®môñ#/3äŽN[ÔÖÇü¦$‡Výœ5˜†ÛË?‰Z:Å ê¡Ø9Áÿz5?…}yûJ~Ò~ý˜ü ‹ü] Χ{©Ü¥†•¤Ø¯™{¨ÞÉ÷b‰}VcÀàY•[ä}oößý¦>èÑüEøÛû3ê>ðhšæþÇ[µÔ/l æÏf‘«ŒgŸ3Ê xb ¯1ý¿Ä«¯Û7öi°ø{m¥_j°jÜék*é­¨FØËäüà¨HÊçx^Õí.³ÿ‚•xךσuÏ |,—N×l®,nSíZ±Ý ÌmŒºÇ¨ jøûû\ø[àçìõ¥~ÑžÓ—ÆÖfÓE°KŸ±†¶Ô~ìÅÌRQy(S9ùNÒ xE×íµûCxÃN½øƒðötÔ|iðâÌÊÐj÷Z¬:mÖ§ $†šÎÅãyÞ6Æc*²Â*>Hý¦¾øÿàüOøIñ&{KcAÖáìey¡òn5 ®#Ý#b@“ÛÇA_¹~Ð´ß øcHðÎ ÛØi6vö–ñ¢…T†Ö4P@%x#ö»¿øÓû8ÃñÏö}ð Ïõ£v,.|7&¡qmrŒ¾z½ÌŠñâ4e‘xË£)Â’@ø;þ ÃñßöŒŸáí¦“¢üŸÅ~×¼Yy.¡âíëkd³ûmÄfé¾Ç,fY²±|+ øÂà×ÐðM#Ón¿h Ìyv6µ„†! 'tùQGáNÿ‚DÉ¢§ýŒ¯óŽ€?P«åÿ…Ÿ´’|BøýñKöÕü,Â<'â¶•yàíI—£]BÂ[BßíI#ÀƒÚ3Ž”ìßmo ü$ý¨>þÍ:Žo'ñºCçêbèFº|—’I œf)¼ÆšXöŸÞ&ÐÁ°Ý+Ñ?jÿÚ2Ëö^øK'Ä©tFñ-ô×özm†–—ekË«·ÀE—Ë—iXÕÜ|¸ã9’>4ð­ûIü/ý¨¿kmØêZˆ­fð”ëÉŽÃÁzx}¦…œàËE>ùú Ç¿,lÚöSðv”š,:büJÖ¡S½"hÐ HœtÊ\Æð°?óÓÓ¨gíñ_Jø9ûo~Îÿüqe?•¤hZóÍeb Üò]\ZOVÐ`/˜òO"ć œ ×£øÃöðý£~évßþ6~Íz‡†þ\M M¨A­[^_YE;I.-0ÈÄ6JbÈBûˆ·í3¡é¾!ÿ‚”~̬+<1YëWj¬-”\ÂØ=ÖHÕìFG5õÇí«cm¨þÉ­îÐH‰áN`ÏÏ *ÁÔô »â_íð·áWÁi>>x“Tó<&Öv÷–²Ûò^­â«[%º»Þ`Ãh$2ÌUA#âçýµÿk¼.ÿåý–u!ðüCö¯;ûrÜj¢Óï} éþOŸŸ1Mœwíæ¾-ý¢&ñF«û þÇ:Nš¶×qÞêº>V XXËq¹ŽÖ;­Ÿ7“´¸|s·8æ¿C޳ÿ=#iðßÂÂû^¯@Iøãÿ‚>)ü ÿ…õðñŸSÑ_Oº½Ž 1 âK52ÞQóˆäWB‡ïâ]ÊA?xþ #ñ3ãç‡4éÿf¿·ž2×VÜÍ­¤Ú´6Zn“+Hë·Û®"‰n&hÕd*ª˜VÜCc¨ý”ÿg¿Š³ìÛñoÂ??²c—Z»Ö5›84y¥šÚÞ+«F‰|ØÐª«Eò¯ð]ßÉ~ TšÿE»¸[ öÎUDðL©åП“nÙ#dw ‘öµ~]ÍzwüÊ ,Ç”uO†¥î1ǘVñ”g׈“òú‰@Q@ KEWäü«þMgÂßö9Øÿé»Q¯×êüÿ‚Õɬø[þÇ;ý7j4úûKEQÍ`Pÿ×ýü¤¥¢€Š( Š)h(¢–€ J( Š)h(¢Š(¢Š(¢Š(¢ŠZJ( Š( ¤¥¤ Š´”Q@RÐQKI@Q@RÐÌÿÿä¡|RÏýþº¯¥ëæŸßòP¾)ØPèûªú^¾cƒ¿ä_ñOÿK‘óü/þåYÿér0o¼Sá½3ZÓ¼7¨ê–Öº¶°%6V’J‰=È„nÅ;œ 9lÖ“Ãþ(ðߊíîn¼1ª[j°ÙÜIk3ÚÌ“,Wq$NP®™ù”ò;ׯ³ýº|møëãŸÚr÷6–ÏáO “‚Ÿd´söÛ¸ÿë´ÙTaÎÐã£NÒÌ_ÿkûͱká/Žq5í·h ñ%Šÿ¤' 7q|ÿíKÇVõ<§¿sîŠ(¨®. ´·–êåÄpÂ¥ÝØà*¨É$ú)%}Éj)æ†Ú.ndX¡‰K»¹ ªª2I'€ä“_;ø?ö’ðߊ|E§è÷š=þ‰§øåFÔ¯R×Qx›aHص˜7ÞàJƒà¶Žt¿x/Âß <5ª¹±ñwŽô¿ ë’[NÞmÄ"BÉ@TI5éc2lVÚµ6ºùomÖ›éë¦åΜ£º>¶ð¿Ç/ƒ>7×ßžñÆ‹­ë1n-gg÷`Ë~í·“Åzyžð_á'‡ntKÝÂ^sá°F5½¤qKl&…‚:€ÃtlTòr95ùçâoÛ+Äþ.ñ׌´ÿ üIð§ÃMÂ7ÓiÖ0ëVïyy«ÏmĒɆQ ³? °ÀÉ9àyÉ_c;ØýX¢¿7|Aû`üBÖÿe|iøy¦X§ŠuïYhw3n–Õîé­eŽ7ÜRWPQòJ«ɯ@Ñ>&|zøUñÇÁ ~8êº?‰´¯‰1^-…æ™hö/ac•á*Îþd,*³|ù “ŒŠ9X\ûŠŠüñÒ¾*~Ô?/¼}âÏ‚:އáÿ x'T»Ò4ë-BÍîæÖ.lT4­4ÂDòRMÊ`Î 8&» Cö¼¸—öRðïǯèQÝx‹Åg§iú\’ Õ¯'û2ÆïÃyk $ô;F2 åasíúä|/ã¯øÖ}jÛÂÚœZŒ¾¿—LÔ<æÞö y¾@ù—#8È÷¯øqáÿÚ³HñF›}ñ3ÅÞ×ô+¸äþѳ´Ó¤´šÒO,˜Í¤¾cy‹æ­æŒ•$Œø«áJ~Ñw!ý¢¤ø/«i ž‘ãmjõ¤Ô-Zòkë±ÏÙTnT†0ˆ79 ĸÆÐ¤‘D.~Ÿø¿Ç¾ øc§ã­zÇÃö—2y1Mq´o.ÒÛ¤` mRp;i<'ãÿxòÞK¿x‚Ã^†{˜îBç¦LlØÏ½~]þÐtŸ‰¿¿fŸŒž9Óì:¯Šl¯5+8`k¤m–·K2Ç ÜΤ©*‡' “ÍnüÔ|ñWöÌ·øƒû9èËáÏ xgD¹²ñOîN7·¾ËØÁŒ¡Œ!\çù4ÏÕZ+ò“Ä?¶gŠ|cã_[xSâW„¾èþ¾›M°¶Öí仼է¶<ÒÊ!¶v;S`/ÃdžÞÇí—¨j²–…ñ»EСŸÅ¾#¿ƒ@´ÓLöS¬Í9·åó»ÈZN¹Ú1»ø©r1ó|Q_6|6ð÷íM¤xžÊóâ‹´hwpÉöÛ[=6KI­'Ù˜þÍ/˜Þbná¼Áœr1\7Ã/Ú?Uµð‡Åù>1½ðƒP¾[ÁjžBÜéÛ ö¢±8i”4kÏÌËžø¥`¹õ‹üwà¯XCªøç^±ðýÄ¢濸ŽÚ7”©`ŠÒ¶©8àUü#ñÀ>ŽY|âM;_X1æ ¨®vg¦ï-›~[ü{ñWÄ/þϳ·Ž~'èÐøƒÄçŒ4íAô«8’î"¸¶º’ tYœ¦^"¼»c'šè~½¥×í¿áMVûÀðBìFÞ:háWñY”=žëcöe\o/ÇL ®Qs«TWå¿ûfø¯Æ^+ñ‡ü"?¼%ðßKðµýƧØkvò]^j³ZpóNÁÔCò¦Á¸`äšöáûeÏ{û&hßt­9|Oâ ˜4{M-¤anuyæ0/÷¼‘ƒ'÷¶¹Ï4¹ù¼©+毆¾ýª4ŸØj<]áýwD»†O·YYéÒZKi6ÌÇöY¼Æó? æHäb¼Óâ¿íOñ~ÏÇŸþê…áj:†Ÿ¦i—ÖOu>°ÚVDï4ÂEò„Ž F#“˜œ¡sôRŠüæÖ?io‹_t¯€gà¬Úw†¯>.Á­}±µ+f½KI´¸¤ò¶²±Ö]„Œ7ÊXc"¹Ÿ?´‡íâü)Öþ"ÞhÓøgâ-þ± ýŽÊÑâž+­‰»2³žf–ÙÁŒ ª¬03ÍŒ.~ŸÖv¯¬i:™s­k·éÚ}š&¸¸‘bŠ4Y݈ >µùWâÛCÅÞ.ñŒ.¼#ñ+Â?4ï _Ýiúf™¬ÛÉuyª½™*ÒÜ8tÃ+‚#òþl “Þ©üVøÇí=àÙ¦ËQ²ZGÄO/öæž´NÚ\‹°ãtNÎYê6“ó->GÔ\Çé·þ(|8ø™mqwðóÅo‰aµm³>wÈŽFËfÇCÖ»ºüùñׇ|=ð³öÝø3à]: |s¦kšV§ œK WØÂ—6ìñ U,‡ 63Œ‚½cáWÆ?xÃÃ5mdÛ}£ÀÞ ÖtÝ;ˈªýžÆÞ9bóFã½·1ÜF2; N=‡sêê+ó¤þÔ߯?gïƒÚ–m§ÜüJø½xºu´“Fé§ÚŸ1Ä×-¾æ S°79'œmoFðÄŸžøñ¦| øÝ¨é¾&ƒÅzUÎ¥£ë}©±KdÊ.-§‡{)º²2ã‚3œ§+ ŸhQ_øËâ×í¯þÓ¾#øðž]ÂÆËE²Ô¿´õw›ì+!Ĭ|é$fUN~flãoǾ<üоé>3Õtk[ñGŽtíúòÖÍâŠm2öB2"/û¹öðHÊ‚8r…϶h¯š¼SñWÅzGíOà„fßþÿh:¦£uº2góí=’nW r6œúŠø[Á?´Wí‰ã3ðÎH5ß[ÅñjM^ÆÏ:\‡û5´§Ü7ïO˜î›€SòqÈÏ4(…Ï×êó‡¿<)ñBo4×þÔåÒ..Z=°Kw©•`|Ÿ1P°V`Ý3ƒ_;|6øóãþÆúÿÆ?ÇñO‡´¯Ÿ²¡H¤ºÑþЪ脜o+Ð1 q]‡ì_áëo þËÿ-aÚòÞi©}q"Æk›Æ3Í#7ñ3;’Iäš°\÷OøëÂ> ¹ÑlüQªC¦Íâ+ØôÝ=e$›ÉA) `³qô¥ñ׌4߇ÞÖ|q¬Áqq§èV²Þ\¥¬~tÞL+ºFDÈݵAb3œJù#öñ?ÅO|\øa-ÅÖ…ªøCÄþ-ÓtË{+,É}dò#ž;–”¨“åm¬¨ îïŠûvâÞ«ymnIÊÈêÃ!•† õVÂþ&Ñ|gá½3ž¹[Í/X·ŠêÚeèñL¡”ã±ÁäAàó[Õñìw<!xbïámõ¼&š„?b¼ñEÍÌ’˜ä–[Xò’2g£\gåòÎ úþ¡û"Aà¯Ø^ý–>ËΡy£ÜÚ¥Ýßîîþèï–yŠï(ÉÀù¶¨UÉ _sQ@?û?x#Zøgð/á÷ÿŽ­áNÓ®Ì ^/>ÖÝ"“c¥—rœ Šøg_ø ûV|ý¢~ üjý—­´hÚ [FÖg{IíïmÃbH¥]ªWt’0;º9R¤…zýF¢€? ¯¿d¿Ú÷â‡íðŸöøÓ®ho¨xgXŽ[L’Xì4.Ž«ndWyî&}æR[(W>©ø§ðÇ~1ý³~ üxÒ  ðÏ€ìu‹}DK1[’÷ֳÕRn‘s–¯µ( ý¡| ­üMøñá߆ÌCVñ.…¨iÖ†v1ÅçÜÀñ¦öˆ]Ì2p~•ò¦½ûÞ|Ký‡üû7øËP‹Gño„ìl%³Ô­‹Ož©b¤,ˆ~FdegˆÁ‰_¡´PåN½£ÿÁSG†êÊiˆç‰ãdmѶ†@Æ@5ùïá8?à§ |iðº-ÁÞ<ŸM€ÛZx¦÷S¹´cˆžòK,¨0 ó`n,Ùfý?¢€>ø%û[xöNñ?ìïã½ljÚŸ´®µ½BÙ Æ/õ$XÌ–êÁI„Œ®à72’Bƒ´xGÂßÁM> øËàG†,|¯é,mc¤ø–úêá^ÚÌ!ó-ÆŒJFÅòØàsúÑE~p~Åß²įٯâ÷ÅxÏÄx¢ÇÆñiS&¦ï‹Ë½AVIµ e„ XÔÜÍ'–71Ù·<ä×Â_±7€j /Qø­ñ§ökÕt[ã75Xðçˆ<Ø­nã´òî-® žYgF¹‘~b«·©n•ýWÌ¿²÷ìáìÕáïhkíââÏßøˆÈÖÂÔÀoRò0$—~Ï+;ò¹ÏÝäå{OÙËöŸý¥~+ø?Çÿµüº‡üà•Ô´ß èo%кÔåe½–BT…ÂðPP"îvo ý¾¾xâ¯í»û?x+Áºûx_ÄéºÅ曩„óµö›—ÖÌËÎPˇàü¤ðzÚù“Ç¿³„9ý¥~þÑm¯µœŸmu+eÓE°‘o¡°n3ùŠcÙænÆÆÝŒqÖ€>Zñ^›ÿDøá»¯…W–þð¼ZŒÎ÷Å67RJ`l™í pY%dÏ&0?!C†_«>~ΚÁÙ¿þ/$ûG“¦^[›¹”F×w·hþdòãvÝò7'b ¢¾”¢€>øû ÝYþÂ6Ÿ²gÆÑnnn-µ8.䱓ÏHd¹Ô'¼¶ž&e\¼%ãeq½pr+ɼáø)¯ÀZ|"𕟂þ"hú" =#[Ôn.-®"²ˆm.`툀¥ˆÆ ¾3_ªôPÅß>ü`ðGÂßÅñ{ÇRø×Çß>×qu‰¥MŒ³@ñG”NŽ1»çe2®Àg¤ýˆþ xÃöý™|ð—ǦٵÝêàÚHfƒý*þâå6¹U'ä•sÀÁȯ«h ‹>|ñ߆mÏŠ´¤m?áñ†‹¦iö!%-sçZEn’y‘íW16ãž8¤ý²>øïã«|&>6ƒþ¿éºíÿÚ¦1¡Úîó<¼+n~x^3ë_jQ@þØŸ²õïíá¯ê¾ ן<~5_êÌ›Ò+…ÚZp ÈÉ ÅYía•oŸuèàª>=Ð$økqeà-ô?d¼ñEÍÌ“ˆØm’[Xƒ1ŽR¹Á1 •1ðËúE~sþÓ?²/üyûi³o€õ÷ñ.¿¥Ë§–Ôõû©<˯³È^Y$”ù¬9b#O›b@p¢¿E"R‘"ª©( Šÿcÿ€ž;ø«|c½ñ«Züuã=G]Ó¾Ë)”ýŽå‰ÍÊ®×ÁåyÇ­|Ûû4|ý·e+ë߃þÓ<)âo†w }BRöîâ+èln]e&?x"MÊ¥HùÙHÇë5•ñwíÙû<ø¿öŠø3i¡ü5¹†ÃÆÞÖlu­æiL Å»s檱B#‘™x9u_¨ûJŠùëöoøað_ösð—Á-Jn¿³t¿³êjŸ43Ý]î’÷€YY$ÆFJâ¾Fý‚?boþÌ~7ñï‰þ!_Ûê‚xãѼ7$S4ÒE£Gs-à 7"ì21‰¶ à©ç¥~ŸQ@üSø ã¿þÙ¿~mÝ{WÚ”Q@ KEQE•ùÿªÿ“Yð·ýŽv?únÔkõþ¿ ?àµ_òk>ÿ±ÎÇÿMÚ~¿ÑEQøRRþÿÐýû¢–’€ (¢€ )i(¢Š(¢–’€ (¢€ )i(¢ŠZJ(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ )i(¢Š(¢Š(æßòP¾)ØPèûª­û[ø¯ÆÚ/‰<#ðÎÒ{Ÿxîê-O’r¶¦ôìšæW@|¤Š-ÇÌ$m8nqгð?þJÅ/û ýu_K×Ìpoû„Å?ý.GÏðÇû”}gÿ¥Èà¾|;Ðþü=ðÿß.,4 8­Q±´ÈÈ>y á¤l¹í“Åy_íWðžûâÇÂ;Û 7‘âï M¹ N¼ñÝÅŒúeέf’\ZÜÄðKÂü“FÈà0Û"°ç¨äd`×£º$¨ÑÈ¡ÑÁHÈ õzS¨ +Å?¼ã? ¯ƒ¼C¤Áq¤FE¯–°ùcbÙ‚…Gn8ã¥x'ƿٓNñ‡Á«?|.’/ kÔ-µ½á÷:G©YÉæ£ÌÇs6óÌCÁÁõ…-t¼ugOع¾[Þ×v¿{w“µº)ü:ñ÷íaâèš/Ä/…úW…th‡WÕ#ÖRûÏ+ lvÑ…x·Í´Ÿ1¤Ú™Rwa«Æ´?†ÿgoxÓMø{ðßLø¡àïj÷å‹I©[é—š}ÅàQ5¼ßh†@ñ‚£c'E?1 ~ˆQ\÷&ÇæŸígÄÛïÙãáòk:^™áÞxÓBòmm\Ëciw%é»ÝF\)(eeÛGA]Þà>;øâGÆ?XøBøe ì¶ÖÐêQêsj:ôb"êÑ¢ˆá@ªÀ7ÌÜÛ~¿ñ¯Ãï|DµÓl|c§F #PµÕ-TÉ$^]唂X%ÌL¤ìpÖÊž„]•ÚÎ=ÀÿµìõwñÁ?ü§xËÃþ0Õo5S›TŽÉ´©¯Ð+Çso"æeˆª#e ÞË•N²ÿöFÖíd |ðî³^.ð|¶Zµû†6ÇV³ŸíYl©o-œ²îÛœrGQ_xQG0Xù‹á·iÿx§JÒ~"|.Ó¼!£A‡SÔWZŽüÍ ˆùkiJ¬¥ÚO˜Ïµ2¹-†¬¿? ¼kàvøÔ|If–ÿð™ø³WÕtͳ$žm¥Ú*Äí´„~VÁÅ}cE+…Í= öqø·cðWömðtúdK«|9ñ¶¡­F.¡" xÖä3+†Û!Eá <û÷‹Ÿ `±ùÛáÿ‡?g_øÃFðÃ]/⃼M«ÜkZ|©Á¦^iòÞóm¦H.Q“ äœ¶Õô߉ß~&|iýŸôÝ/S´Òü!ñHÔmµë {V2Ø[ßYLÏ R8`ñ’’2Œe‰WØ´QÌ>høgãÏÚcÄÞ'°Ó>#ü/°ðvo†þýu¨µžp˜m¡‰¢—ù˜»6Ë’~cóí;û,üIø‘ñ¢ÓVø}åEàïˆ6Ú~ãŒÌ‘¸¶Òîã™%Ef òIòPvì3Ïé-)X>8ý®þüFñŽ…ðâçá·}à¯Yk b÷QXÆmí-ç@¢I0ª72¨Î0+”Ó>þѾ7ø âgÆi~Ð>=åÕµ®¡ý§{yuu „n–4HÒ%qïÆ0w¿yQG0XüíðçÃߎ¿³¶¹âÏøáž—ñKÂ> Õîµ6wÔíôË˾mÒÛ\ ˆdªà”dòyÚž£ñCàïÄOÿ³ÅŽ­[ižøƒ§ÞÁ­YChÆM>ÖþÊvx#g–Vˆì‘Ô}æ$ `WØ4QÌ>eøiãÏÚkÄž&Ó´Ÿˆÿ ì<#¤A ‡QÔSZŠüÍ0LF-`‰3òK³m\¯'æ?:iþýª~éþ?øUð³ÁÚŠü=âÍKQÔ4mf]V+&Ò¿µ‰iRâÙд¢%×i™láIh£˜,|àßÙ“Å õ¿Ù«LÒ|½OKøaˆFµx$T >«f@h‘ö»£\;Âä. b¹…ÿ³·Å_ h¿¬µ2(¥ð/Š|K©êÀ\ÂþM®£öï³²íc¼·ŸUrFyÆ ~QG; ¾ðÇÙßSñG…|ðÃJø¡áMgV»Õô»§ÔíôË»#~æI-nð¾õG$£/@pIήøÁðoâÿÅ…>ñ]¦¤h<¬G¯ÚéÖò7ök´R±6žiè^1.~S"‘ò£d}ËIG0XüÍøw®ü@øÉûiiú‡Æ §ÃýOá®q&“¢ý¡¯Úñµ&X®/í"H^5U@ä˜î#ZO‡ß´ïÃÍcâÿ€þxCM×|?ñ/Q½ÕluËQ-Fœú…²C2Km±¤”©\¦Ò£w$•?/èÖÄßæm±ŒãœzfG0XüÔ³GÅý7öxø+ÿõµ¢|Kø?x5Óînì×jÒ9ž×ÏBUL‹³ »oPHÎG§xÁ¾'~ÐzOÇŒ^µð.™àí*ïOÒ4˜µõ9渿eóî$š$EUÚ€À ÔkíêJ9‚ÇËþøgã /ö²ñ×Å[ÛTOkžÓ,-g¡w¸¶v2)Œê=HÁíUÿk?†=ø‰àÿ êß ·½ñ?¼G§xŠÒÎêA wfÅ›0ù„…BwËaHêA¯ª(¥Ì>ð—‚¿h¯~Ôþøéñ+ÂV^ðþ• êbXA©Ç¨Oló0`óH«v˜žjB*€Çq9ä¾~Î_<+kû;G­i‘B߯|E6°Ì/ä&¢¬-ÊícænÏ!3Žø¯ÒŠJ|ácäÿÙÇàÞ½á/€z§Âï‰6Io6­y® áYRUk]JyJüÈJüÑ¿#9 7ö3ľø@¿ üc§ÜYjÿ/n4’hž8ï- mÖ·P; ²$²ä© 8aÚ¾²¢‡ ±ùãûDé´ïÄ?xEü/ðšÚãHøwâ˜5ËKÆñ¬mªEj®ª†ˆ5¿™¿9&B¸û§<}NŸük£|Õ~"üCð\º»¥Û]ÎÚ…ÐÖ&”ûÉH¥‚%Þóap|¥°z^ÏE+…˜d†ºßÃ/ºM—Šãò|Gâ îµÝU1‚—šœ¦wB:æ0ÊœóòóÍ}=E64‚Š(¤EPE-%QEQEQK@3þÊÿòOuû Mÿ¢ ¯¥ëæÙ[þIö¡ÿaIôDô½|ÇÿÈ«þçøWþEÔ}Š(¯§>€(¢Š(¢Š(¢Š;QEQEQEQKE%Q@´”QEQE-%´”QEQE-%´”QEQEQE-%Q@-%QEv¢Š(Å´”QEQEQEQEQE-%-PEPRÑEW㞯ñÃâ?¿Œ>4ÕØojlöšÑóu!?»$$õhʱõÅ}™ðÃöÆø{ã'Mñˆÿ„[Tl.é›}”ê'Àòû’$ £¦ö5òYa+Ttj>I'mv~üì}žeÀØÚÕjkž-'¦ëÕo÷\ùàßü¦Gã·ý‰–ú#A¯×úü|ø+47ðXÏŽWò,±Kà«&GRYZßA ‚8 ކ¿`ëëS>1…Q@ KEQE”µ…âhøoÃZ·ˆDj:]¤÷BÛ žDlû7`ãv1œzWåÇ‚¿à£ßþ9hTß³‡À»¿kbØÜk‘Z;{%ÌòDZ»šÖi¤Š1.0›C€Øýd¢¾&øûûb7ÂÏèŸ>x÷âgÅMnØ^ Æt·ŠÎÜñæÝݲºÄ2 \c–d ¥¸ þÛß|!ñ#Ã_ ¿jÿ„7_ ¥ñŒËi¤êñjPjÚd÷nÁR eBÂI }ö YU `ú-K_(~Ó_µn…û;·‡<3§xz÷Ç>=ñœÍ‰áí4ížèÆWÌ’I6¸Š%Ý÷Š1'8UÙ~voÛÃâïÂïøwFý«¾]|5ð#±´×-µh5kX'›˜ÒãÈM«ÆKá€V"2ÀéÅòí7ûWøwöuÿ„ÃvZ ïü{ãZúf>ÓtS¤‘ö¿•'¶±'8RÊüõ©~Ý_þ^é:‡í_ðóáïƒõ‹•¶þÞ°Õíõ¨lÞ_¸.¢¶\ ÏR\1ØŒF(쌟t/‚ú×€t]gFÔuY> k–ú³ØF’%¬×,¨²Üu+-’W'ñ^ë_.~д½¯ÀÝoáF“o¡EñGÄ–^Žt¼%ªÞ:(¹ŠQ0Á nþðªþÓémû]Oû*x“ÃGJ{½kZ.°nüÅÔÕ@óaþJùl…gçÍ|ˆ‰ÀÈêêð¯|yмñËÀßo4mFëRñݽõŵô£Y[ žg[‡.KÈ]ªy#8­ŸôO€ŸéVßþ6~Íz‡†þÜM M¨A­[^_YE3I.-0ÈÄ6JbÈBûˆõ6Šñ/‰´'Âï…¿¥øûâ=OÌðØíïm¦¶_2Kļ m–$ni·®Ü9bª hß¶—í_®ø~‰Ö²Æ©'€¥‹í"eÖí¿µ¤´Æï>-=¢YŸ+ó*;Xƒšý9¥¯Š¿e?ÛKÂÿµŸ‹~ é Ðå±Ñ<4¦‚þiÉ–ôjQÌÄ=±‰<†…¡daæHò:ý«@ùñã_Û↵ñ{Å¿f„’|JÔ<c\ÔnuX4›k™+o™Ošù §æS¹[ T¯Lý™¿j¿ø_ç‹þx¿Áן¾ øXSVÑ®çK¥Tœe%†â5Q"u V€>º¥¯Î]{öàøƒãˆ^!ø}û%|"¹ø®¾­5]f]J 'KŽé&œm˜‚Èu'UdÃóà_í‘qñOÅ~'øAã¯_|>ø±á‹9/‡onc/`P½­àDG Ì£;q† ¥Ô1P·¨¯Á¯~ѵ+~Ü?¼AcðçVñ@Ð4«+¿ ZDt»tX¥ŽcvѦó¼ÍÛQA]ø9Á¯Üjºö¹á-ZñNŒ|;¬ßÙÛÏ{¦4étl®$Z[s<`$†&%7¨±0h£¥¯—ÿj/ÚoKý›<; Ë‡î¼aâ¯j)¥hZ%›ˆ¥½»|peeaT Ç, )‘óô_¶ÇÆ?†Þ6ð¯†ÿjo‚s|>Ѽk¨E¦iúÍ–³m«ÛEu?ÜŠåaQ³ž¬X|¡™T…4úC^ûDüxÐÿg†—¼E£ê:핵ͽ³[iq¤·%®h`²:.Ñßæ¯Ì_Ûã_Ç7ö£ø+áÍ;áDóYh~%º¹Ð ë6éÿ D‘E•UòÏØÄ^n3.ìîÈÆ }WñöÊø¥ðköpÖ>:ü_ø/'…õm;XƒM‡A}z “qo8m×Úà·uA½|³?&r úö’ý¡ü+û1ü,ŸâÏŒtëÝOM·¹¶¶h,Df}×-µH¼kßæ£ö’ý¡ü+û2|,Ÿâ׌4ëÝSL·¹¶¶0XÌå®[j%x׿Í_!Á\[ìaª>1»VÒŽ>²šgü·þL³Rÿ°®“ÿ£hôõ:+ŽŒ§T6ÿê#ÿt*Áñ‹¼;à jþ6ñuêiÚ.…k-ååÃä¬p@¥Ý°2IÀá@$œ PG^¥þÑÕ¿i-cöd·Ó¯WÄ:.‚ž –í„_ckw–„hC™<Ì̧”óÓ?$xoöÝý£¾)Ù7ŽþþÍ:—‰>o“ìú•î·i¦]ßB‡h–ÞÒd,G@Ç* ׈~Ê?tOŸðRxÿFÓ/´9óe{¦êQnìo­5 î-åSÝuF2² ö–ŠøGã/í¯7„þ,?À/_u‹,âê6vw ce¦Æè~Óy":+Êp@Q¸)pä-pöß·wĆ~/Ð|-û]ü¼øUaâ9ÖÒÏ^ƒRƒXÒþÒämIäBÂÉ'{°qP™@?Iih¯šjÿÚ.Ùsá3|TºÐˆaP³²{quöM«tûL¦O*o¸9Û·ž™héj+ó6ëöÚý¡¼a§^üAøû:j>4øqfeh5{­V6ëS‚CMgbñ¼ïc1•Y á •XþÌ¿´_ƒ?j?…?ü ¶QË,–—–WLÖw`É •$]ÈÊHRJ€ )kó»Åÿ·7Š|Añ3^øIû*ü*½ø¹ªøZF·Õõ·E¥i6w*Û _iY$`Á“;NÂÀ/|:ý·õÅø³£ü ý¥¾_ü"ñ_‰pš4²ÞE©éšŒÃƒwpª"ÈIPª Ì™X¨`Ð:+ñGößø×ñçMý¨þ øsNøQ<ÖZ‰n®tºÍºÂQ$Q@¥U|³ö1›ŒË»;²1ƒ_¨ß|{ñWâ…o5Ÿ‹¿ äøa«AzðC§IªAª™­–8Ùn<ëtE]ÎΛÈÙœàŠözZ( Š( ¥¢Š+òþ Uÿ&³áoûìôݨ×ëõ~@ÿÁj¿äÖ|-ÿcþ›µý}¥¢Š(æŠ0(ÿÑýü¤¢–€Š)h(¢Š(¢–€ J( Š)h¤¢Š(¢Š(¢Š(¢Š(¥¤ Š)h¤¥¤ Š( ¤¢Š(¢–€ J( Š( ¤¢Šù£àü”/Š_öú>ê¾—¯š~ÉBø¥ÿaAÿ£î«éjùŽÿ‘|Å?ý.GÏð¿û”}gÿ¥ÈZJøgá·Ú~:þÔÞ)ø³<Ï'„¾,žÐ1ògÕJèpÆ1ˆAÆÕ3R|2‘þþÔž+ø7u+'†¾&$ž(ðèv%"¿N5;4Ï8¢Žd¾§”÷î}ÅETŒZJü{ýž?g_ |\ð7Å?ÿjêºl|a¯[éú¾Ÿ¨\A%¿Ùü¹`5}ŽŠìIV^A#Ò¾Ëýœ¾.|Fø¥û)xâEžŸo­øÕí¥¶’Þâ²Cswepö²³Ê¨û7yeÎŒð8ªq°“>¯¶¿±½i’Îâ9ÚÙÌRˆÜ1ŽAÕXà ô<ÕªùöKñ>#ŠÜøBßÂõ§ŠîaÖ"µ¾{ø®oÄ™':&2QŽ3Þ°õÚ+ã/‹>,xËá§ÀizÔ_ÞÞJëWÔÍ›Ï<ñùž]¼HŒ@_»æ9ÚH= º…϶é+Ã~#|lƒáÁwø­ñF–Êö-ÄšE´«s1¿¹*‰iª¹26Ýàc€z;áÇÄ/Ú{XñVį†vƒáÍie/qi«‹›­0¬/,bæ#‡óD»#k0'ЫϨè¯>!|fý¦<+ªëºŽ‘ðÓE h²Oä}»_H5 RÞ}oŒ¬{ÀÊ£äýr+Ðn?h=/Pýš'ý£¼)§½Õ™ÑŸV‚Îå¼§%æ'e ‚H¦h°\ú.˜ì‘£Hä*¨É$à;“_|2ø÷ûC|Po øÇIøOogà}“Ì»¸ÕQ5áD—‘ÛÿPŒIE$¼‘…—vñWíEñCŰüDÔ~ü:Ä?¼%æ—©j2êÞöê[h³vö6û\@­¸+02 UŽåasï»ë-NÒ+ý6â;»i†èå‰Ä‘¸é•e$ô¬=Æ~ñ±­h§ ö¡áÉ£·Ôb‰·i¥A*Ç!(AÆr;â¾Gý”¼EÿOìáO,boì/ ÝÞ„èÛyÒüvâ¶ÿa/K¦þÎ:еBf×|q5߈5;—;¤¸¸¿ÝdcêbÙøä÷¡ ¹õÅæ£§éâ#¨]Ej'‘aŒÊê›äs…EÜFYM[¯…iø«Hñ¿ôÏ|>Ó5ÏÏã/Ûéz‰ÕeŠöBi£òî ªEÜÊ_ÈCÆxû®“Aq(¢ŠC ©ka~fW1Üy)|·WÙ"ýäl†ÁäV'Œo|U§øjþóÁ:l¾·fÖÒæãì°Êù<Á`ÆNvšù‹ö>ñ%‡‰l>&\/„`ð~±mãR f kÙ/£¸ÔÔ¯Úg:&9 `:â…sé¸üiáYwßø1ºÿã•SöVÿ’{¨ØR_ý}1_ ÂY>¦[Bu)E¶µm#â,öußËá_iŸ9Ã+üÿŸ;ïüÝñÊ?ᕾÿÏ÷þ n¿øå}K_Gýƒ‚ÿŸ1û‘ïÿ¬8ÿùÿ/ü Ÿ9Ã+|ÿŸ;ïüÝñÊ?ᕾÏ÷þ n¿øå}EØ8ùó¹úÃÿŸòÿÀ™óŸü2¿Áïùó¾ÿÁ×ÿ£þ[à÷üùßàÆëÿŽWÑ”Qýƒ‚ÿŸ1û¬8ÿùÿ/ü Ÿ:Ã+|ÿŸ;ïüÝñÊOøeoƒßóç}ÿƒ¯þ9_FÒQýƒÿŸ1û¬8ÿùÿ/ü Ÿ9ÿÃ+üÿŸ;ïüÝñÊ?ᕾÏ÷þ n¿øå}KGö þ|ÇîAþ°ãÿçü¿ð&|çÿ ¯ð{þ|ï¿ðcuÿÇ)?ᕾÏ÷þ n¿øå}EØ8ùó¹úÃÿŸòÿÀ™óŸü2¿Áïùó¾ÿÁ×ÿ£þ[à÷üùßàÆëÿŽWÑ´Qýƒ‚ÿŸ1û¬8ÿùÿ/ü Ÿ9ÿÃ+|ÿŸ;ïüÝñÊOøeoƒÿóç}ÿƒ¯þ9_FQGöþ|ÇîAþ°ãÿçü¿ð&|çÿ ­ðþ|ï¿ðcuÿÇ(ÿ†Vø=ÿ>wßø1ºÿã•ôe-Ø8/ùó¹úÃÿŸòÿÀ™óŸü2·Áïùó¾ÿÁ×ÿ¤ÿ†Vø=ÿ>wßø1ºÿã•ôm`àçÌ~äë?þËÿgÎ_ðÊÿ¿çÎûÿ7_ürøeoƒßóç}ÿƒ¯þ9_FRÑýƒ‚ÿŸ1û¬8ÿùÿ/ü Ÿ9ÿÃ+|ÿŸ;ïüÝñÊOøeoƒßóç}ÿƒ¯þ9_FRÑýƒÿŸ1û¬8ÿùÿ/ü Ÿ9Ã+|ÿŸ;ïüÝñÊ?ᕾv³¾ÿÁ×ÿ¯£(£ûÿ>c÷ ÿXqÿóþ_ø>tÿ†Vø=ÿ>wßø1ºÿ㔟ðÊß¿çÎûÿ7_ür¾Œ¢ìüù܃ýaÇÿÏùàLùÏþ_à÷üùßàÆëÿŽQÿ ­ðþ|ï¿ðcuÿÇ+èÊZ?°p_óæ?rõ‡ÿ?åÿ3ç?øeƒßóç}ÿƒ¯þ9Iÿ ­ð{þ|ï¿ðcuÿÇ+èÊ(þÁÀÿϘýÈ?Öüÿ—þÏœÿá•þÿÏ÷þ n¿øåðÊßÿçÎûÿ7_ür¾ŒQýƒ‚ÿŸ1û¬8ÿùÿ/ü Ÿ:Ã+|ÿŸ;ïüÝñÊOøeoƒßóç}ÿƒ¯þ9_FQGöþ|ÇîAþ°ãÿçü¿ð&|çÿ ¯ð{þ|ï¿ðcuÿÇ(ÿ†Vø=ÿ>wßø1ºÿã•ôe-Ø8/ùó¹úÃÿŸòÿÀ™ó—ü2·Áïùó¾ÿÁ×ÿ¥ÿ†Vø=ÿ>wßø1ºÿã•ô]-Ø8ùó¹úÃÿŸòÿÀ™ó—ü2¿Áïùó¾ÿÁ×ÿ£þ[à÷üùßàÆëÿŽWÑ”´`à¿çÌ~äë?þËÿgÎ_ðÊß¿çÎûÿ7_ür—þ[à÷üùßàÆëÿŽWÑtQýƒÿŸ1û¬8ÿùÿ/ü Ÿ9ÿÃ+üÿŸ;ïüÝñÊ?ᕾö³¾ÿÁ×ÿ¯£)hþÁÁϘýÈ?Öüÿ—þÏœ¿á•þÏ÷þ n¿øåðÊß¿çÎûÿ7_ür¾Œ¢ìüù܃ýaÇÿÏùàLùÏþ_à÷üùßàÆëÿŽQÿ ­ð{þ|ï¿ðcuÿÇ+èÊZ?°p_óæ?rõ‡ÿ?åÿ3ç?øeoƒßóç}ÿƒ¯þ9Iÿ ­ð{þ|ï¿ðcuÿÇ+èÊZ?°p?óæ?rõ‡ÿ?åÿ3ç/øeƒÿóç}ÿƒ¯þ9Gü2·Áïùó¾ÿÁ×ÿ¯£)hþÁÁϘýÈ?Öüÿ—þÏœ¿á•þÏ÷þ n¿øåðÊß¿çÎûÿ7_ür¾Œ¥£ûÿ>c÷ ÿXqÿóþ_ø>rÿ†Wø=ÿ>wßø1ºÿã”Ã+|ÿŸ;ïüÝñÊú2–ìüù܃ýaÇÿÏùàLùÏþ[à÷üùßàÆëÿŽQÿ ­ð{þ|ï¿ðcuÿÇ+èÊ(þÁÀÿϘýÈ?Öüÿ—þÏœ¿á•¾Ï÷þ n¿øåðÊß¿çÎûÿ7_ür¾¢ìüù܃ýaÇÿÏùàLùÏþ[à÷üùßàÆëÿŽQÿ ­ð{þ|ï¿ðcuÿÇ+èÊ(þÁÀÿϘýÈ?Öüÿ—þÏÀÿøxY|Mñ'…¼?o,ëk«^Ú[B»¦•–9ÝGVvÀ¤×ÒŸ ?cø£ÊÔüy7ü#Zsa¼œ /]}6}ØóþÙ,;¥~’øsáÇ‚|)«jZþ‡¤Ã©«ÜMsuvFùä’w2?ÎÙeRO ¤(ô®Þ¾G/ðþЍêbõÒ+E÷ïùm™x“^TÕ,"åÑ^OWòè¿ñWöcðf‰ð÷þ ÏñŸÁÞYOÓ¼l#ó_{“$zŽÌÝÉv'€ÎŠýª¯Èƒò™Žßö&Yÿè¿_ëôT£¨AY-ù­Z²œœæîÞ­ù‰KEfaEPRÑEq¿äŸxŸþÁw¿ú!ëà_ø$·‡´Ýö-ðÞ§e Ç>»¨ê·—, ’¥ÓÚ©c܈àAôúâÝ.ç[𦵢ÙmöW6ñî8]òÆÈ¹<àdó_6~ÿ¼eû>~ÍøQãóluÍMA§û¦hqs{4éµÊ©?$ƒ>ÛþÕß²†±¥ØxÎêÉ4Ý{GÖU×OÖmcÀVi"Ë,cqòñêC䮾þß¿´O‰t ;ãF«£|"ð&‰¡w…/nXÔZÜîH¾Ò²0Ž2GPé·!ŠHTmÈÓ!¹Õ¿à¬vßð™¤m{¡|3GÓÈù\=¤Í@ÆD× œdŒú×駉¼!á?Ø&“ã-Ë]±ŽU`¿¶Šê%• IU”2‚pØÈɯ‹¿jŸÙ“âW~%x3ö’ýu›ââ’ÌE©«SN”¶ëiÙ2íJåçÌ?22«¯ø£áíÓûQëžðßÇS |3ðƒ¨Ã©j1øzöâmGSx2$pͲ6ŽYvçqU@òO’|rŸþ •k7ÁûQñ&—àd}6?Ép–ijòH³¼FÛçn’@;/šõoŸà£?>x‡á/‹ü=ðÊ-'ÄP,2ËÞ©çDQÖT’3"º‡GEe%Né^ÕûWþË~=ø‘ã~гþ¿iáŠþY ¶’ù Yj62“ºÖå”3*€òí!H"F«§x¿Â?ðR¯ÚOÿ…iã<+ð“Â÷êšÆ‡sss©Í0.¶Ÿ¼m›ý ±Á“«y‡í)á|>ð¯ì7à/Ë Úç†ücá6îKgi"i,ͼ9GuV`BŽJ‚kÖÿà¤ú©à[?†¿µÿ„­ÚM_àö»o%òÇÃO£ßȱO¤Û²Êç"½cö—ý›|qñ/RýŸ—Á7‰{ið·ÅZV©¨Üj·n×sYX4[ä2c4ì#%‹YŽIæ¾±ø—àâŸÃßü7ñîÓ|Kaq§ÏÀ%Vâ2›×?Ä„†SØ€hóSöÚ׬¿iˆ¿e]ý³EñåÌ>-×%„j†H‰#¢Î¢R¹ÿ–‰®+ã´ZÿÁK¿f kXÖ(bÒКÒfmt‹NY2È›wˆ(Æ@S“óW¹üSøã¿þÙ¿>~Óz›â?ø)?ìÁ§ê°¬ðEg¬Ýªº†k(&¹…°{¬‘«Ø€këÛVÆ×Qý’>/[ÞF$<3©Ìùà¥Cø:‚>•Å|Søã¿þÙ¿>;éÐxgÀv:ž¢$˜­É{ëYá‹ÊŒ! 7H¹Ë ×¹~оÖþ&ü øðïÃF!«x—BÔ4ëO=ÊEçÜÀñ¦ö•]Ì2pq@Œÿ×ÿcOاÃ^$ý÷‡umÖú“˜Ú„¨ã¡_)¤àöû𪪡P`¯Í‰³/ƒ.ÿ`|øûâÍ+Àú—‡-4ø­u«‹¨ÒÖÓY¶V˜ä¡oS"1Fm¼€EM×þ ­káø>ÞÉàž8…±ñt’ÝKq³ûGÙª¼àr3BØÜ½MrŸðO½3CÑÿjÏÛMðâ$VxL‘€¨ŽfÕ ˆ pÈX:b¿YëñËþ sà8üñoöšÑìu™¼Kca­i:qÕçÛ¾úöÏíæòS´‘̲nÆæ ’O'ö6€?.ücû;þÕß¾8xããì}¡ø‡Iø‰)ÔŸP±³ñ&«u§¤y`ŽY‹!(vÈPÁP– Ù~Ìÿ³×Æ-+ã_ÿj/Ú.óJ_xÂÂ"ÛJÑK½žŸ§BѱF’QºIÃà0ÇqÜ>ÿ‚x\~ÚúwìÏ¥ËðCð߆¯µ BvŸZ¸Ô#ÔdºóŒrÅ¿îÉ!ì žkéMà'í•ãOÚïá¯íñ~ÃÁZL ¶¼Óî¿°n¯L×wPNYn#`åbTnP2OZ‡Ãÿ³§íoû#x»ÅIû&x¿á§ŠoeÔãðæ·,–“iWSm¤Bªc ª ³òªMÃ{{ÀŸ„µv»ñ“þçíAâËM5ìl$Óô¿ xjy×K&ûòÞob³?uÉómmë±Py§Á/ùJ?íÿbÿ‡ÿôŽÊ¿P«óâWÀ¯Ú¯áÏíyâ?Ú_öo´Ð‘ãÏ›GJ‘•Œ tÎÄ…~²Ä¥"D=T_þÈüuðCUøÉyãchÑxëÆzŽ»§ý–S)ûÓžh*»_•Ç­yüOLÑ-?cmSÓ•Mþ­ªê·”€|ïr· —n¤ù)^ÕÍÿÁ\í­àøàÏÙF¿ð‘i>2ÓJq/žñNJ#u¶ wE=…XÓ?fÏÚŸöPñ·Š5/Ù}Ä¿ü[w.¥'…u×’Ñ´ëÉpìRÆBìÚ¡FçP*”b¡ëOLý™ÿi?Ú#âç„þ'~Ø×ú™áŸÜ ý#ÂZ™<2ß 5ìÒýí¬ªpÃT ûÀ ý¶ÿäî¿cßû5Ÿåa_¨Uð?í¹û=ü^ø¯ªü,ø­ð&çNÿ„ÏáF¯.£mgª;ÇmyÁ„ÈŒé冷Q›¬~ŠøªüÖ<9}uûBèz&¬ý§–ú%Ä·1ý›Ë^eyIýç™»…8ÛŽôíÔQE%-PEPWäü«þMgÂßö9Øÿé»Q¯×úü€ÿ‚Õɬø[þÇ;ý7j4úÿEPGáIKøPÿÒýû¢ŠZJ(¢€ (¢€ (¢€ )i(¢Š(¢Š(¢Š(¢Š(¢Š=¨¢ŠZJ(¢€ )i(¢Š(¢–’€ (¥ ¢Š(¢–’€ (¢€>høÿ%â—ý…þº«µOÅKáÂ+ûÿ !¸ñwˆ%‹EÐ-ÓIu;óåBQOR™/ϨÍWøÿ% â—ý…þº¯¡/ô]UžÎëT±‚òm:_>Ùædh%ã, FÁÆF |Çÿ¸GüSÿÒä|ÿ ¹GÖú\3ø ð¢ÇàŸÂoü8´q<Úe¸7sŒŸ´^J|ˉI8'|ŒÄÎ1žkÊÿl/‡šç‰þÚüCð*ŸøM¾Þ'ˆ´‚ƒæ˜Úü×6¾ë< ¿ÄÁTü¤ƒõ#*²”`#„WÔ_[žýŽ#á§´OŠ^Ð>!ørA&¯ÙÅwvù‹–Cî•>⻚ÍÒt}#@°JÐì¡Óì¡,R xÖ(“{m¨€–$œ¤šÑ¤3ò?à¯~7ü*ð¯ÄŸ†~ø9âm[ÄúÿŠu«í:òæÐØé éH¡–K«‚€ª”.@+Œ0ÎG×ÿ <;qû#þ̾ðÎ¥¤jž1¾Ð¢îßÃö†þêk«Éšiš(²…‘d‘‰c”f¾±¢©ÈIš³Å-{Ãß|y§k¿ üw§ÇñÆjVWw: ÚÚÛ\ÇJ×’»Ò„¹€äôí;¦ü9ñ_Œ)xpiÄÏ*ÿ¥ßi²¬Æ $ÈBîÒÄà·9ç5ê? ¿i]Wâ7Š´ ¨ñ‡‡..VFÔnõ8ÙØXyp<˜óØþø¼c@KßÀWÔ´•7Åøüyo≚?ÅÏÚ÷Ä¯ŠšÞ±~Ú6¯=«Üèg(È‹·Cm\’åFà€‰ôW‚ü ã[_ø&éð%΃¨EâSᛸ?³dµ•oŒÌÎV?³•ó7œŒ.ÜŸJý¢©ÌJ'™|Ó.ô_ƒ^ѯí^ÆêÃ@Òíå·‘ o ‘ZÆ!«)# ŒWçÇ„µŠŸ³¯‡>(|ƒá‡ˆ|S©ëš®­}á­GN´óô»˜5TÄjº, È’Ç ߪ”´”‡cä?Ùáî§'ìmáo†^1±ºÑnï4:îÞê‚æ´c;â++làY¿°¦³yÁøYâ%6þ&øey jp7Uhfw—Ö6…×ct`7¯³j•¾™§Z]\ßÚÚÅ ÍéV¸•Uæ(¡TÈÀeЍÀÏAÇJ.>8ýµ³þ¦Fçøáp£¹ÿL+êOˆ^&oø Ä~/O,¾‹§]Þ ”â2ðDÒ(ls‚@âÓ~Ë~Õ¾)Y|SñwŠ|Iâ‰ôBMSLÒµ+ô“JÓoo1°ò³û°îÛp$^åã_ø{â„õOx²Ý®ô}j¶»…%’’'ûÊ$‰‘×>ªÀÐØûüiñÏÇÿƒpüKñÞq{}s¢ÛMkÔ®öb{æ!=2µõ-`ø[Âü7¦xGÂöi§é5¼V––ñçlP¡QA9'rI$žI'5½Iî4p_|x>øVo·‡µy2EØt+3}|Þkmܰ†\ªõcž5ð_ìñ'Ä>ñ—Žt|-ñƘ>!øÛTÖ,ï.´)a²µ´¿pÑÉÇ”F>|QÆ×é•% ŠÇôŠÇñ3ã×Á‚z^é§Ò5Uñ¦­³¥½Ž–-|ÏO:äá}ÐûgîJ¢š^™£&®–­ü±ˆžàF¢fNBÆâ ò8«Ô6;´”€)i( Š( Š)h(¢Š(¢Š(¥¤ Š)h(¥¤ Š( ŠZJ(¢Š(¥¤ Š( Š( Š( š?eù'Ú‡ý…%ÿÑWÒõóOì­ÿ$÷Qÿ°¤¿ú" úZ¾c‚ÿäU‡ÿ óü+ÿ"ê>EWÓŸ@RÒPEPEPEPE-%QE´”´”QERÒPEPEPEPE-%QK@ KIEQK@ EPEPE´”QEQEQERÒPEPE-%QEQEQE´Q@Q@ KEQEùðoþS#ñÛþÄË?ý ×ëý~@üÿ”Èüvÿ±2ÏÿDh5úý@Q@ KEQE”´Q@Q@ KEQE”´Q@á´OÇöqøiqñ;ÄZ6£®Ù[\ÛÛ5¶—KrMÃm Ý¿Í^ä­½C2怖ŠñÚö„øqû4ü?“â/ÄˉÒÄÏ¥µ½¤^uÝåÔ¡™!‚2Ê •Vo™•@’(_ãgÁo~Ð?5O…ß-ïFÕ’Ñ?—<FwG4/ƒ¶DnA ƒÈ`ÊH?Ù~Á¿´í|cûRøÂÅÙâ²X"ûdvàm­÷›¼(_”€Àq__üøß/Ç-ÿ^“À^'ð±aX)ñ‰<;ñã_Ô§Ô%ÓüMªÚÛ´Î\ÅA6F„ôUÉÀí_?Æov^øUâ-\½ÔîÓZÔõ+y&$_[é×Ïæ,™ÎvÀ¤Ø Ž‚¼Ê¹¤aɧ¯ù¥úŸuà:øŠõpñ©àÒÖöw„§ÿ¶Ûæ~¼Q_"øÃâeö¯ñsà”þÕæ_x¶+û‰b¶¥Ä~Bk;Z ½tÓšÚ_¾§Ø¿Z+àÚ?â§´?‰ËsàmJxto†Ö¶ž»m —böíȸ‡ ÏEr{f¢ý¥¾*|Cð·Åoßü:ÔgŸIM*]bêÆÄ7¶¶ìd”•èI„Ý{Vu38G›G£_ŽŸ™Ûà\MaË8¯iI_§*R³íx´×K3ôŠù/Å?ïõ_ŽWšĿðxªÓPº’(جW1ý•¤ˆÈ½Êœ†¹AãÚWTñ7‹gñ®­á/ i:”Úf“i¢Ì-$—ìØßqq _q`6ç1Œ¶›åŠ»½¿ïøœpáYÆ’¯^¢„9y›i¶¯9AFÉ^÷‹}­ÔûrŠøßö‚»ñ—ÃςްŸÆ—qßÿmØY^kq²ÚLÖ²ÈáÝÊü£lxÜO®OzÓø_cà¿øMôétOŽ·~4ºˆLÃJ}^Öé.”êKEÌÁ3¿Ž…A=)ýs÷žÎÚéÕu%pÕðín¯$­;òõ½´Nú^ÞgÖÔ•ùåðËIøÏñÃÞ3ñ¿†þ#ê–šÖ®êV–v7]Í„©jHâhå¨bÅIŒ+ë_ÿäø±ð»AñÝÄ ou¨BEÄi÷h˜£íÉ$)a $‚OZxlb¨Òµ¯ª#:á™àã)ªŠj-FVºqm6¯u³IÙ«ìÏX£éK_#øÏö«»ð†¥¯ãáGŒ5=ÂóOö¯oc·ÛlH–[u’Uiâ\½xqƒŽÔ™¹õÅ%xþ±ñãá~‰ðx|v¼Öø9í#»Šå‹Ê³±Æ‘ÊîB üs\OÃ_Ú*ÿÇþ'Óü?ª|3ñO„íµ˜džÆÿU³Hí¤XÓÌ+)Y¡‘—•G84XW>–¢¾[ñßíK¢xoÇ×ß < á-oâ‰4x£›SƒE·WŠÁe‘fšWD2ògÓïdWI­þÑþð¯ÁÛOŒ¾-±Õ4[;éE¬:UÍ£.­%ñ•à[Eµ“3:6?t$($¬.}I_3ü9ý¦4¯øî†^,ð–·àj’_iÖÚÔ ßÛÂG˜`’6u2FgŒá”rzómGöæð¬w¾*µðï€üQâ(¼ wso®\XZG$6QÛ1Ræ@0V`£ªY°>V>ᢾ`ø£ñøÙ|ƒâO½TñeŸˆt»›»+ý*l€‡|sÜ%Ã)P¤üÊUˆ*ÊË‘Šç¿e?޾8ø¥àOÁã?ë±^ÝèV·w>"»‚Ú->þ*=ÒEä¸#Î,]‰F3ÀàQË¥ÂçØWÈ>.ý°|;¢xƒÄZ?„<â/Yø5Ú-sQÑíKK)#]ÒÄGO6H×—Tû£’qÍvþ-ý§þø[á&‰ñŽÞê}oIñ;Áoa –óPº¹ÈKx¡8"@Uƒ«°«óaJåasèz+ó“Ãu_‰_¶‡‚¼?>®ø6}7Ã:Ãj.¬¦í$–ïop¡¢™HÞªã•ee8ïãñF—ã/ˆ£ø§ûJj æÑ¹|l-uÈ%‹R¸ºiÞYD$LU@ kåäáB€Mu¿ ÿim⎮>xƒÂú×|R¶Ú6Ö:Ü ÝÙnÚe…‘[iá×9S‘É ‰åc¹ô¥ñ·‹?lÿ h¼?àßx³EðLím®ëU¢Ëee4c2 fu2´#ý`Qò÷㽯žxKá$µ]a[ÂW¶÷V×£H÷+u·ÈH£sI!`às»h Af=~’¾løkûCj>>ñE—‡5†ž(ðŒZ¬\Y_j¶ˆ–Ò,K¼¬Œ’1ŠB¼ª8ÉÁô¯‘þ~Ôw.>2[jñ7Ž¿°hý•ÿäŸj?ö—ÿDA_K×Ͳ·ü“ÝCþÂ’ÿèˆ+ézùŽ ÿ‘Vü'Ïð¯ü‹¨úQ_N}´”RÐQEQKI@RÐQKI@RÐIE-% ZJ)i(¥¤¥ ’ŠZJ(¢€’–’€ )h ¢Š(£éE-%Q@JZ(¤¢Š(¢–€ J)h(¢–€ŠZJ(¢–€Š)h(¢–€Š( ŠZ(¢Š()h¢€ (¢€–Š(òàßü¦Gã·ý‰–ú#A¯×êü€ø7ÿ)‘øíÿbeŸþˆÐkõþ€–Š(¢Š()h¢€ (¢€–Š(¯i/Ú®óàÇ‹ü#ð—áß‚n~#|Hñ¸š]?H‚å,¢Kh3æO=ÌŠëŒ1.0ŒYOØUðgí[û2üUñïÄŸ~Ñ?³¿‰,´‰>‚{8âÕc/ae>íÐÈQ”þò@>Rü†FPÔKáçíñßã7‡¾þÒ¿ gøiâÇpú-ì”®zÐ Í™7ÇÜÇqPBïZØøãûaxÁŸ­ÿg?.~)|Bû êWÖé{g§Z¹{‰U”1 § ±péó`µÀü:ý²>3øKâï…þ~×ÿ âð^³ã)ÛE×t‹Ÿµi7×#D´bTs+0f]ȪwU~Õlõ¯¢ÿmÏù;¯Ø÷þÆ gùXPñöÊø§ðköpÕþ:ü_ø/'…õm;XƒMƒA}z “qop#Ûuö¸-ÝPog_,ÆOÉœ€ÃÁûO~Óº_ìáá¯ÜÁáë¯ø«ÆZ„z^…¡Ù¸Š[Û¹àÊÊÁªX+²€¤GÍðWOù3=[þÂú_þ5íµïìÍâÚÃÞ ñÃ_Eáoˆ?µ$Õô+Û˜üË_8-À+¬Q!|¸(Àœyd_¶ÇÆ?†Þ6ð¯†ÿjo‚“|=Ѽk¨E¦iúÍ–±m«ÛEu?ÜŠåaQ³ž¬X|¡™T…&¾Yÿ‚ŠüIøÍyñŸà÷…î~É.… øïOŸC¹mbا‰îQá)j!òói½˜Ç¾RÀg$cŠ÷ˆÿlÚ[ö{ñ‡ôÛSá­—‡5ËØôè¼_áË“-ŒsɆ{w2:îÁfËFv†)`¨Ðÿ‚…ÿÉVý”?ì¥é_úSo@Æ•ûiüEо+ü3ø_ñãàìŸ ÄÙo-lîä×!ÔDWvÜG ,ê­ç3Ī|ÀA|¤ Ÿº\Ãâ*h‡ïQ¬>k€§¯úÎêè•óçíkûBÝ|{ý’¾ø#ᜊž%ý¤n´ý5bBOÙ¡Ðêy#Î;l.sÅ}ãû)üxÕÿiOƒzÆ OÂgÁÖúÅÅÊÙZµçÛZ[XÊ—ò`Û¾EpiáC!¸å¿m?‰_¾üñ­ð×ÀÒxÀϦêk¨\&¡ˆÒ-RÎFkâ²+ÄxÏ”˜cŽ }àh ü |?ðÔ^N•áË}>Ù{ùVшԷ«2ǹ$ך~Ôÿòl_¿ìO×ÿôß=|3ÿíø½ñþÿáGÂßjg‡Àa™?á1}vÕÕ£ŒM"KýŸåùÿ< E·~FwtóOì[ûHøÇáæñ;á'Á¯†÷ß¼}©øû^ÕÎ+˜ôû<¥´)=ÕìÀÆžd‘ºÆœn*y.ïÒÏø'Çü™ÂŸûŸý%|åÿ¬ÐôØ<1ñ»Ä±B¢þûâ«i,¡Fæ†Ò8¤‰IêBµÄ„Û© ]øûhø“Æ¥ý›þ=ü6¹øYñ K&¿Óá{Øõ =FÃ3˜&UI ®Ãa‘HŽ@\2í¯Iý¦ÿjÿþοðønËA½ñ¿|a+C¢xwMÇÚnŠ`4’>×ò¢ãvÖ$ç B¹_š¿jÈ#²ý¿¿e ZÔywW/â iuh–ÝSíû×üÍx¿ÆÉ~9OÿJµ—àýލø“Kð2>›‰d¸K4µygx·Î&Ý$€v*_4îúŸíÕñŸàõ~Õÿ¯>ø?X¹[a¯XjöúÔ6o/ÜQ[.Pg©.ŒíF#úað]AÕ¬‹43(tt!••†C)GB+ò·ãgÃßø(ÏÇ…ž!øKâÿü2‹Iñ Ò[ÝêžtEeI#2+¨ttVRTàŠûïà„)üE»kMFE/å/™4²HÁ#†ÈÝ$Ž@QV!A#á‹Û?ö³Õô~&i²Æ©/€¤‹í+/öݰդµ#pž=<Ä&$§Ì±„;¸Ã`æ²à§ðëÁð ÂzâðÖ³ñJ‹RÌmÊmqЃ’p}+õ8 À¨âŸÙGöÔðÇím⿈W‚´¬4O (Áq12Þi$ÌÊöÆ%ò…$ ãî~þÒ ñ›âŸÅï†ká㤅:¥¾šnßÚ>ßö;÷ž_•“·Éû»Ÿ9ê1ÏÆÿðO½3DÑ¿jŸÛLðê$Vx“M‘€¨ŒfÕ ˆ p¹`éŒWIû²Ú·ö¿€œH\ÿ‚•]@š÷ìÛdÎÒüHÒdUîV7PÇð.¿Q¿¶·×?à¯xñLjÃAø|fÑüÀJó:» =l·#œ è„ÐÖµí ¿²ïÁ]Sã h'Ŀٷ}ˆ]}Ú¦X³æùScnìãaÏN:×ûM~Ø^ý˜4ÿ‡zÿ‹4V»Ñ¼m~-.î–à¡Óaò„¯0ŒC!œ€xA°œu¯ÿ‚¶]Aoûx‚)œ+ÜêzTqï0¹W ÀTŸÂ¸ø(f‹§x’ãöTðî¯ ÜXjž:Ñín#p^Ì(êAà‚¤‚(oÅŸ·Çí௠'ÆOþÍZ‡Â†hÜê2êöãTŽÒf.%°T2EAÚøPH@5ÛÜþÜ_¾'ÝÜ?ìyð‚ïâ·¦G ^ëW”5’Ï4K1µ·ûHÌóF®ª¤ßå*x'ê_ÚŽÊßPý™þ,ÙÝ x¥ðž¸#ÒÆb÷zñ/ø&Þ‘§èÿ±OÃ(ì"X¾Õius)òÍy331O8Éìèv¿²íW¢þÔ>×§mëÁþ+ð}ûiºî‡xâI¬®à¿>ØË+uù£FŽ¥xý__—Ÿ²dI§þÞ¿µ…§îíæŸÃ÷ƒ€ex%rß]Ò1ükõ€ (¢€–Š(¢Š()h¢€ (¢€¿ àµ_òk>ÿ±ÎÇÿMÚ~¿×äü«þMgÂßö9Øÿé»Q ×ú(¢€ ? J_€?ÿÔýû¢–’€ (¢€ )i(¢Š(¢–’€ )i(¢Š(¢Š(¢–’€ (¢€ (¢€ (¢€ )i=¨¢Š(¢–’€ (¥ ¢Š(¢Š(¢Š(æßòP¾)ØPèûªêÿh‹¶ßþëÿYÍý¬^FlAcs¨\.Ú £–Ì„¡°3\¯Àÿù(_¿ì(?ô}Õ{Œ~ø7Ç÷Ï‹ôᨷ†ïâÕ,UÞEH¯ ÿU)E`®Pò¡ÃyÆkæ87ýÂ?âŸþ—#çøcýÊ>³ÿÒäy§ìÅð¢óàÿÁý#úì†ãÄz“TÖî‚ójwÍæÜ3°ÎâìÏC·Ž+Ìl kzNƒáßÚ+Á™*ø#àDß¼ñ>õô]Voj:•¤RC+}²Öì ÀUHmÛ}G'œã…ø?áÝKþ9ø ¢xŠÑ­n¥Ñuù¤·™pÊ—SI*«¡îQÆAükôâKx%t’XÕÚ#”$TúŒô©kËþÎwå¢ÛO4õ×ËÈûéq¬9j¸QjU¼ù´¿³”ŠåV^óm6ïµÖçå–›£ê¿j¯|#ö6©ÞhÎÄŸô J"Â3ÔC":ç¿Ö½‹À^9ðïìáã߈ ø©pt=7]ÕæÖô{ù"‘í®aºåãŠq$XUaëžØÏÝuÐCq•qÊ„ƒµ€aÇ#ƒE,¹ÁÞÖ÷Ztµ­¸±¼kTU9x£Âß´ƒºö’>ߢj¾2µDY£X”ldr)ÿ<;á/ü}ø)q¢ivZ:Is¬4ímpX­ã9}Š3´Œôɯ¶@ÀV³Ás]·«iíÚߎ7ûSƒäŠ©snª)%}-xß¶¶è~j|8øwñWãO‡¼}ñDñ-–‘£üM¼½…ັrIe{h¶K¹vl‘p6ÎzsŸü@|Sñwàþ‘­…–ÿBÑ5½þÃâK/:ŒPcÛÁìkõ:–°YRN-Kmüõ¿}5üÏR§Êq­ QV’j6²qN íFò´ZÞß Øü³ðΫ|?ý­< ðŠñY´ŸÜê÷:4¬rN¨ZÍ*E“Éòœ:“ÜäúWeàígá÷ÂmÆ~>\Ï¡YÍ«Üj}Èk˜úÊgI¡žÛ ]>`uÚ{Šý¨å†ÀYãYÀ0t#=Ç­8eœ—å—WºÒÍ%mü…Šã¿¬rªÔ¹b›Œ­'8ÊRçO•Ù·'ug½Ó]?,õK†±ý‘< ¨øÀÌ,ÇŠín$kàîÍeö¹\3 2̆.G\¯L×Ö¾-þËZß‹¬4‡siC_¼.–Â×O0JØFgü¥Çȧ<ó_PQZQÀÊ 5%²Z®ß3‹1âª8ªs…JRMÊr\³²÷Ýì×#½­Ý_Èü×ø#ñã ¼+ãߤjž,ŸÄš´ö:]¼³Í;H#HyE*ªÒ)“Æ ÇLýaû4xVøkð[Þ×¢ò5(âyî"ÎLrNæM‡ý¥ïš÷Š*ðØ7œ¥{+--¿ÍœÙïÃÆ.Ny)Jò滊i[HÙ+¾ï]¿oþ)ø{Ä«ñB/>ñU¯Ä›-OT±Ò¼¦\\YÆmÑ1d°AoïV`rò;íÛ’Ffýª¨M½¹¸F$óÂìm¶ç8Ý×í^„]’hü€Óü5¯ø‡þ ÃðŸYðæŸ.¶¾ÔìuË»eóe¹²±Ô'3* åÊ«y›G]µ÷ÇßÚËà/ůi¾øwâdÖµJ .´0Ìâ]Îg,cÇÝÁ<·¾¨"µµ‚If‚ŽIŽdePÈîÄu?Z® šÿþ*xö^øÝñ—ÿnáÿ„Ë]:þ‘«]C!¶Ôlåˆ)‰&E ¼|ÉØ¹ïÖ_ÚîçLøÉð»áoÆß SPð6â8µ-JM9&‚ùtÀdïaP FWrºŒ„}ÿw$~‘ÜZÛ^Gå]“Gvº†y?µÝEcó[á*þǾ2øÓàýCÀµßøÇHwZp¸Ôo¯¡µG·t™¥ó"+ÆÅpÄe°1œW˜|ý¥¾ü²øóᯈ÷_ÙÚߌ5ÛÛ(ž jªñE‘…*ò+ VL¢D'ï_®0ZZZ™ ´) •·>Å ¹sާÜ׊üø4>Ùø®ÒïRZ>$ñ-ÿˆco³ù?f7¢0"y72lûãns÷GwÌ ü\¸}7ÆQk^ žÚÂ[yZ]b-VG{I-ö©‰ƒ…àõâ›âo„ßü)û |]KB¹Õo¾ëZw‰5m!#ó.~Ãö‹‰Þ³Ãê¬@ Ÿ»_¬ò[[Ë,sË<’Q™AdÈÁÚOLޏ©¨çSç_‡?µwÀo‹~$±ð滫_[ÉsäA Ù‚(†XÎÌcç圓À¯ž>$á—í.è/âïï#¹ ßÊý †ÖÖÞIe‚æ;¤* >¬GSõ©é\v>]ýŠ_Ìý“þ¾íÃûÛ9àdWÔTQIŒ(¥¤¤EPEPE´”QEQEQEQE|Ñû+ÿÉ>Ô?ì)/þˆ‚¾—¯šeù'ºý…%ÿÑWÒõóÿ"¬?øOŸá_ùQôŠ(¯§>€(¢Š(¢Š(¥¤ Š)h(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¥¤ Š( ŠZJ(¢–€Š( ŠZJ(¢Š(¢Š(¢Š(¢Š(¥¤ Š( Š( ŠZ((¢Š(¢Š(¥¤ Š)h)h¢€ (¢€–Š(¢Š(òàßü¦Gã·ý‰–ú#A¯×úüø7ÿ)‘øíÿbeŸþˆÐkõú€ (¢€–Š(¢Š()h¢€ (¢€¾øõð»ö«°øÑ¤|uý›üUm©ZÛiͧêžׯn¢Òî†âDöËhã¾\±à %˜3¡ûžŠüćà‡íWûF|køqñö“°Ð<áO…×ßÚö:F‘r÷×—šŠ•hÚYˆ1¬hñ¡àç%·-]à'íYðöŠøñ«ö^µÐ<[ |Sh.umYí'·½· ‰"•v©]ÒHÀîèåJëõŠü‚½ý’ÿkߊ´ÂÚCãN¹¡¾¡á^9nt=2Ic°Òt¸ :­¹‘]縙÷™Ilp 9\úöÜýžþ/|WÕ~|Vøs§Âgð£W—Q¶³Õã¶»ŽãÉ2#:{À ©)¹°êÀgïŠ(óö„ø7û\~Õ²®»ðïâ‰á¯xÊãY±¸²·±¾™í~ÅoµœÍ+¬˜—ylÆ;׺þÓŸÿhëžø“û8øÙ4MÁ“J×:§ss‰¬ÛÈ ¹Ž ‚ê*’™Ãä:2)¯²h Ê?‰?mÛx{áçÇ}Ã?¾iº¥¾§ªÿg^ɨjÿe'l0ðR5`Ç–Á wcc}ûXüñ߯| ñƒšÐZ|;ñ•Ž»©ý¦c;y¢wò@VÞøC…8õö¥ͽ½Ý´¶—q¬ÐLŒ’#€Êèà ¬Á¿?àœ¿mµÚ‹Ç^+ÓuWñÃo‚·º¶á NZÝgÔî$ghŒ9H ùŒ3“*08"¿Q?iÏÙçÇ?´–—¢øg⶯ðãHŠ+¸5Km* ÇS†ëËO22›F7àú‡Á‚~ýŸ>é ¾Ùµ®‘¦bòóÜÏ!Ì“Îà.ùõ8@Põšâ~&x2ˆÿÛØ¢H¼” Cò‡9#‹ûX~Ëž=ø‘ãŸ~гþ¿iáŠþY ¶’ù Yj62“ºÖå”3*€òí!H"F«§ÝÔPå—‹ü#ÿ*ý 4ÿøVž1 ü$ðÅãÆº¦±¡Ý\Üjs@Œ ­§ï`BclpdÁ*ߦú’š‡§hQÝ\_.m °¸¼•§¹˜B“Jß4’62îyf$žµ­E|ÓûY~ÍÚ'íKðzûᦥztB9â¿ÒµO1¬¯íóåË´%YYã`;XA×ËúAÿ‚ªEáôø{wmà/·C·ÿ„¶k‹—fÛçý•W 87ÍŒ·TÆE~›Q@Ž¿ðL‡óü+øíûPü?¼Õ¦×îômGÃñ\j²]Ü2ê4̹8ß#3I O'Ó>!~Ï´ÿÂÚgÅ´oì§ý‹â ˆ6ð&½áífg¶æÝB$ÐÈ¥AèXà©wYXcê?ƒ³„>0|_ø³¾Ú«|X½°¼k3l!&ÅgD¢Gówùýv¦6ô9ãéºüqø•û$þÙ´Žþ|kø»«h6ǃ¼K¦Ogá½6iFŸ§éËçÞÎÓº;Íy,‘Bƒ·jŸ›¢¯Õµì¯âÏŠÞ*ð—ÇOÞ!ƒÂ?ü$M>òê=ö—Ö²gu¥Ñ Ìç“iÃ:•;ƒ/ÜtPå÷‹t¯ø)ÏÆÞ|3ÕlüðçOÕPÚj:õ…ÍÍÕɵ”m˜ÙÂÌå7íaü.‡ =—\ý“-<#ûëß²ÏÂ#ÝÝh·pÜÞ$]_\òÏ;(m¦G$ô;Fp}¹Exÿìûà}káŸÀ¿‡ÿüHb:·†´;N»ò¼^}­ºE&Æ ]Êpp3é_"þÒ?³—Lj?hoþÖ?²åî—'Ь´Ó£jú6°ï®£d™q" Y0Q6AôjŠüaý£eïÛŸöÉø}uÅkŸx@èFô/ é—2I ÕóJ‰5Ρtâ@VÍ0‰c',y É­ÿi¯ÙçâÅÍWàç†È/Ãoézάg™£Íµ›Deò0‡{|‡h;sÇ"¾ç¢€<Ûã'…uO|!ñÏ‚4?/ûKÄ:§§[y­²?>îÖHcÞÀ.æ88«Ïd¯…Þ&ø+û9øáoŒŒ ­xzÉà¹û,†Xw´ÒIò9U$a‡aÍ}E|WðGàŽþþÕ>2kíh|?ñ éÙ‚‹Ïþ… Ž_9 €œž0Ç5ö¥PRÑEQE%-PEPRÑEùÿªÿ“Yð·ýŽv?únÔkõú¿ àµ_òk>ÿ±ÎÇÿMÚ~¾ÒÑEsEÿÕýû¢–ŠJ)h ’–ŠJ)h ’–ŠJ)h ’–ŠJ)h ’–ŠJ)h ’–ŠJ)h ’–¨Úêšeô÷6¶WpÜMdâ9Ò9Ú'#!\Jœsƒƒ@húRÖ~£ªéz<)s«^CeºÆ¯<‹³·ÝPX€Iì:š¼î¨¬îÁUA$ž¹5Ì7üŒQõý=YN7Qdÿ¯ñìZÅ‹ið´ÞKk éVËu|°V‘°® ÈýøÔ˜/-×ÔÜ~ÏŸ4ëV¹¿·xa„ òËv裶X– 2~•ò8Æ×©R8QqƒqnRjíod“ÑmsæÞi‹­9¬%4ããy6®ÖöI=»ž©ÿ Ï‚è`Ó¿ð.þ*øN|ÿCÿpÿñUæQ~Îÿ ¦&†ÆY#V[©`y܃Oÿ†røUÿ@ù¿ð&_ñªöùÏüú§ÿKÿ‘¶ÍçÜ?ð)‘é_ðœø'þ† ?ÿáÿâ¨ÿ„çÁ?ô0iÿøÿ^kÿ åð«þóàL¿üUðÎ_ ¿è7þËÿÅQõŒëþ}SÿÀ¥ÿȇ¶Í¿çÜ?ð)‘é_ðœø'þ† ;ÿâÿâ¨ÿ„çÁ?ô0iÿøÿ^K©|ø-£[ý¯Wì0gd÷¯döÜÌ;Oø ðcW¶ºT?m·b@’ Ç‘ Fåb(öù×üú§ÿKÿ‘mšÿϸàRÿ#Ö?á9ðOý þÃÿÅQÿ Ï‚è`Óÿð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ÿŠ£ë×üú§ÿKÿ‘m›ϸàOüÑþ"hß þ&x¶[üjÚWˆçqÜØH“m%äp¤n1ƒ ÙפÿÃPü9ÿŸ}GþüÇÿÇkoþËáWýæÿÀ™øªËÓþ|Õ¦¼·Ó"72éò˜.;¹Å((à7 ƒÒ¾o –q.žTÔ.ÚNî×mµ~^ìðpÙ~wA8Q”nÚNî×wÞÝÙü5ßù÷ÔïÌürøj‡?óï¨ÿߘÿøímÃ9|*ÿ |ßø/ÿGü3—¯úÍÿ2ÿñUÓ츛ùé~?ät{. þjù¿ðÔ?çßQÿ¿1ÿñÚOøj‡?óï¨ÿߘÿøímÿÃ9|*ÿ |ßø/ÿGü3—¯úÍÿ2ÿlj¿ž—ãþA츃ù©þ?äbÿÃPü9ÿŸ}GþüÇÿÇ(ÿ† øsÿ>úýùÿŽÖ×ü3—¯úÍÿ2ÿñU—Àß‚rëèÆ[R‚%žKqw'š±1Àr»²<G±âç¥øÿ{> þjùÿÃPü9ÿŸ}GþüÇÿÇ)?á¨~ÿϾ£ÿ~cÿãµ·ÿ åð«þóàL¿üUðÎ_ ¿è7þËþ4{&þz_ù²âæ§øÿ‘‹ÿ Cðçþ}õûóÿ£þ‡áÏüûê?÷æ?þ;[_ðÎ_ ¿è7þËÿÅQÿ åð«þóàL¿üUˉ¿ž—ãþA츃ù©þ?äbÿÃPü9ÿŸ}GþüÇÿÇi?á¨~ÿϾ£ÿ~cÿãµ·ÿ åð«þóàL¿üUðÎ_ ¿è7þËþ4{&þz_ù²âæ§øÿ‘‹ÿ Cðçþ}õûóÿ£þ‡áÏüûê?÷æ?þ;RÅð;à”ú¼ú1—Ô­£Ye·rR7áX®ì€{Õÿ†røUÿ@ù¿ð&_þ*cÄÿÏKñÿ ö|AüÔÿò1á¨~ÿϾ£ÿ~cÿ㔟ðÔ?çßQÿ¿1ÿñÚÛÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿ==/ÇüƒÙqóSüÈÅÿ†¡øsÿ>úýùÿŽQÿ Aðçþ}õûóÿ­¯øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*eÄßÏKñÿ ö\AüÔÿò1á¨~ÿϾ£ÿ~cÿ㔟ðÔ?çßQÿ¿1ÿñÚÛ³ŸÂ¢8ÓæÿÀ™øª?᜾Ð>oü —üiû&þz_ù²âæ§øÿ‘‹ÿ Cðçþ}õûóÿ£þ‡áÏüûê?÷æ?þ;W/>üÓÞ(ïâû3ܱ‰o ž8PÌ2y*K¯€lŒBöÝ óÜG™vé½Û¢®[–=€æcÄÿÏKñÿ ö\AüÔÿò3ÿá¨~ÿϾ£ÿ~cÿã´ŸðÔ?çßQÿ¿1ÿñÚ/¾ üÓ. ¦¥,V“€ Ž]@Æø=Á¢‚ÿî‘%¶’)RI*ɨ )~XŽ@ëŠ=‡ÿ=/ÇüƒÙñóSüÈ_øj‡?óï¨ÿߘÿøåðÔ?çßQÿ¿1ÿñÚÕ›öxøIm —rEJYÝî¤UUQ’I-€ä“XÓüøj#77Ä&@é¿Q+½F\¿ úŠ=ÿ=/ÇüƒÙñóSüÈ“þ‡áÏüûê?÷æ?þ;Iÿ Cðçþ}õûóÿ©,þ | Ôbžm=’ê;UÝ3E~α©ÉË•s´`¾†£²ø/ðSœ[iÒEu3tHµìrhöìÞ<ˆØôeb ljÿž—ãþAìøƒù©þ?äPÿ†¡øsÿ>úýùÿŽÒÃPü9ÿŸ}GþüÇÿÇ)u/‚ÿ4Y’ßX’+dTŸP13`î «¯ðàÌsÛÛI Y®ÁhPÞ8i%ì°®(öúýùÿŽÖ×ü3—¯úÍÿ2ÿñTÃ9|*ÿ |ßø/ÿG²âoç¥øÿ{. þjù¿ðÔ?çßQÿ¿1ÿñÊOøj‡?óï¨ÿߘÿøåmÿÃ9|*ÿ |ßø/ÿGü3—¯úÍÿ2ÿlj¿ž—ãþA츃ù©þ?äbÿÃPü9ÿŸ}GþüÇÿÇ(ÿ†¡øsÿ>úýùÿŽÖ×ü3—¯úÍÿ2ÿñTÃ9|*ÿ |ßø/ÿG²âoç¥øÿ{. þjù¿ðÔ?çßPÿ¿1ÿñÊOøj‡?óï¨ÿߘÿøåmÿÃ9|*ÿ |ßø/ÿGü3—¯úÍÿ2ÿlj¿ž—ãþA츃ù©þ?äbÿÃPü9ÿŸ}GþüÇÿÇ(ÿ†¡øsÿ>úýùÿŽÖ×ü3—¯úÍÿ2ÿñTÃ9|*ÿ |ßø/ÿG²âoç¥øÿ{. þjùŸðÔ?çßQÿ¿1ÿñÚ?á¨~ÿϾ£ÿ~cÿãµ·ÿ åð«þóàL¿üUðÎ_ ¿è7þËþ4ý—=/ÇüƒÙqóSüÈÅÿ†¡øsÿ>úýùÿŽÑÿ Aðçþ}õûóÿ­¯øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*—²âoç¥øÿ{. þjù¿ðÔ?çßQÿ¿1ÿñÚOøj‡?óï¨ÿߘÿøåmÿÃ9|*ÿ |ßø/ÿGü3—¯úÍÿ2ÿlj¿ž—ãþA츃ù©þ?äbÿÃPü9ÿŸ}GþüÇÿÇhÿ†¡øsÿ>úýùÿŽÖ×ü3—¯úÍÿ2ÿñTÃ9|*ÿ |ßø/ÿG²âoç¥øÿ{. þjù¿ðÔ?çßQÿ¿1ÿñÊ£©~Ô~ M>á´‹+Ù¯B7’’Æ‹“.â$$.zàg+¨ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿR¡ÄÍ5ÏOñÿ!J5nx~?äs_³õï‡ü%ðý`Öõ«[­BæK³ ÝD¯²¤jà›±Ôg {ü'> ÿ¡ƒOÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*»rÌ>o…Ãà N•;E[â—ÿ"uåô3<5P…8Z*ßÿ#Ò¿á9ðOý wþÃÿÅQÿ Ï‚è`Ó¿ð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ük¿ëÏüú§ÿKÿ‘;=¶kÿ>áÿKüJÿ„çÁ?ô0iÿøÿGü'> ÿ¡ƒOÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*¬g_óêŸþ/þD=¶mÿ>áÿKüJÿ„çÁ?ô0éßøÿGü'> ÿ¡ƒNÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_ñ£ëÏüú§ÿKÿ‘mšÿϸàRÿ#Ò¿á9ðOý :þÃÿÅQÿ Ï‚è`Óÿð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ÿŠ£ë×üú§ÿKÿ‘m›ϸàRÿ#Ò¿á9ðOý wþÃÿÅQÿ Ï‚è`Ó¿ð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ühúÆsÿ>©ÿàRÿäCÛf¿óîø¿Èô¯øN|ÿCŸÿpÿñTÂsàŸú4ÿü ‡ÿН5ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿâ¨úÆuÿ>©ÿàRÿäCÛfßóîø¿Èô¯øN|ÿCÿpÿñTÂsàŸú4ÿü ‡ÿН5ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿ>±œÿϪø¿ùöÙ¯üû‡þ/ò=+þŸÿÐÁ§ÿà\?üUðœø'þ† ?ÿáÿâ«Í᜾Ð>oü —ÿŠ£þËáWýæÿÀ™øª>±Ϫø¿ùöÙ·üû‡þ/ò=+þŸÿÐçà\?üUðœø'þ† ;ÿáÿâ«Í᜾Ð>oü —ÿŠ£þËáWýæÿÀ™Ƭg?óêŸþ/þD=¶kÿ>áÿKüJÿ„çÁ?ô0éÿøÿGü'> ÿ¡ƒOÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*¬g_óêŸþ/þD=¶mÿ>áÿKüJÿ„çÁ?ô0ißøÿGü'> ÿ¡ƒNÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_ñ£ëÏüú§ÿKÿ‘mšÿϸàRÿ#Ò¿á9ðOý :þÃÿÅQÿ Ï‚è`Óÿð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ÿŠ£ë×üú§ÿKÿ‘m›ϸàRÿ#Ò¿á9ðOý wþÃÿÅQÿ Ï‚è`Ó¿ð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ühúÆsÿ>©ÿàRÿäCÛf¿óîø¿Èô¯øN|ÿCŸÿpÿñTÂsàŸú4ÿü ‡ÿН5ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿâ¨úÆuÿ>©ÿàRÿäCÛfßóîø¿Èô¯øN|ÿCÿpÿñTÂsàŸú4ïü ‡ÿН5ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿ>±œÿϪø¿ùöÙ¯üû‡þ/ò=+þŸÿÐÁ§ÿà\?üUðœø'þ† ?ÿáÿâ«Í᜾Ð>oü —ÿŠ£þËáWýæÿÀ™øª>±Ϫø¿ùöÙ·üû‡þ/ò=+þŸÿÐçà\?üUðœø'þ†;ÿáÿâ«Í᜾Ð>oü —ÿŠ£þËáWýæÿÀ™Ƭg?óêŸþ/þD=¶kÿ>áÿKüJÿ„çÁ?ô0ißøÿGü'> ÿ¡ƒOÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*¬g_óêŸþ/þD=¶mÿ>áÿKüJÿ„çÁ?ô0éßøÿGü'> ÿ¡ƒNÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_ñ£ëÏüú§ÿKÿ‘mšÿϸàRÿ#Ò¿á9ðOý :þÃÿÅQÿ Ï‚è`Óÿð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ÿŠ£ë×üú§ÿKÿ‘m›ϸàRÿ#Ò¿á9ðOý :wþÃÿÅQÿ Ï‚è`Ó¿ð.þ*¼×þËáWýæÿÀ™øª?᜾Ð>oü —ühúÆsÿ>©ÿàRÿäCÛf¿óîø¿Èô¯øN|ÿCŸÿpÿñTÂsàŸú4ÿü ‡ÿН5ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿâ¨úÆuÿ>©ÿàRÿäCÛfßóîø¿Èô¯øN|ÿCÿpÿñTÂsàŸú4ïü ‡ÿН5ÿ†røUÿ@ù¿ð&_þ*øg/…_ô›ÿeÿ>±œÿϪø¿ùöÙ¯üû‡þ/ò=+þŸÿÐçÿà\?üUðœø'þ† ?ÿáÿâ«Í᜾Ð>oü —ÿŠ£þËáWýæÿÀ™øª>±Ϫø¿ùöÙ·üû‡þ/ò=+þŸÿÐÁ§à\?üUðœø'þ† ;ÿáÿâ«Í᜾Ð>oü —ÿŠ£þËáWýæÿÀ™Ƭg?óêŸþ/þD=¶kÿ>áÿKüJÿ„çÁ?ô0iÿøÿGü'> ÿ¡ƒOÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_þ*¬g_óêŸþ/þD=¶mÿ>áÿKüJÿ„çÁ?ô0éßøÿGü'> ÿ¡ƒNÿÀ¸øªó_øg/…_ô›ÿeÿâ¨ÿ†røUÿ@ù¿ð&_ñ£ëÏüú§ÿKÿ‘mšÿϸàRÿ#Ö,Ó§jp#I¬­"´Š2¡ƒn8Èê¤yç¡í~xÎÿÆþ·Ô5fó/l¥{IdÏ2˜Â²¹ôb¬3êr{â¶ÀfØŸ¬,.6šŒšn..éÚ×Z¤ÓW^¦¸,ο·Xl\dÓi§tíºÙ4ÕÏ^¢–Šú3Þ (¢€–Š(¢Š()h¢€?6?i¿ØÇâÏŠ¾3ÛþÓŸ²¯¡ðÄ“e|š‚4¶7°&9cÈFª«F`xßb7ÈÁ™üŸþßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ(ÿ„7þ #ÿC߃?ïÌü_¯ôPäü!¿ðY/úüÿ~`ÿä ?á ÿ‚ÈÿÐ÷àÏûóÿ WëýùÿoüKþ‡¿ߘ?ùøCà²?ô=ø3þüÁÿÈúÿE~@Âÿ’ÿ¡ïÁŸ÷æþ@£þßø,ý~ ÿ¿0ò~¿Ñ@ð†ÿÁd¿è{ðgýùƒÿ+.Çö'ý°¿h?‰žñ7í±ãíSð‚®Åä:>Ž»MܪÊådÛA¬…UË;„ ¨¶úý“¢€ (¢€ ? J_€?ÿÖýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¯ƒ|{#þÏŸµ—‡þ&¡ò|ñ‰"ðö´zGµn i÷ Ð"o9é½E¯¼ëÆ?h/…Ÿþø‹áäíå]_AæØÎ>õ½õ¹ÛL§±IU~£ ðM8±3Ùëá¨øûû[xᢓ?„¾Çˆ5•ùskWþ%ÖÍØ˜“÷ädž­¿‡µ>˜¿²…ïÆ‹[ð]µÆŸ­Ú3mq¬Ø~å¡9ÉwØËßl€žõÔ~Çÿ 5ß|+ÿ„£Ç@¿þ ]IârG\\^ñÂGðù1•]·ÀÒ°·5|/ÿ'?âïûÅÿ Y׿ø‡BÓ|O¡ßøwXβԠ’ÞeèJH¥I±Aìy¯ð¿üœÿ‹ÿìþg_K×Ëp«÷+ÿ×ÚŸúS<ø+×ÉÿéLù×öwÖõ+]UøWâI|ÍkÀw?af=f³ošÖP3÷Y8€c¨­oŠ?m>®«§jvwZd†ÆI4íNX|Ë îü¶+ tÎÇÜ ànç·'ø²ÏðÃâ†þ3ÛšMù]]ÇÝÌÙ·ºÿ«~§Ðzº‹_ 5?ˆÄ÷:ÝåÞŸöûnËvм®¸iAm¥UˆPzçŒ}n—»ê{šÚÈŸá_Æ[?ˆ‘i:~is©N4øeÔõ¡òì ¼1£<ÛŸq?*go¯§ºW‚|øS©|?Ó´Imu‹ËK6Ó¢Ú,̳ۥ뢙9h¿y¸²©*ÄçŽsïuµô*7¶§ŸxÓáo¾!ß隇ŒôÕÕ³ x¥fòAœ¦òèv1» sÅxÁm/F°øã㸾EöoZAoo£u´Ò.¡}¦Bg¸µ¶M¡’!¬’†;Y˜`ŒžšuÍ>ÊÒè~Öü%§X[´«>£n±Ç+‚¡·H–•óžG@zZ¿).×>ƒ¥¯šu¿|h·ø÷aá½3M•ü ‡Î¹enŒ—ýþ00Ü{t¯¥«9Fŧp¯…ü­|R²ñ§Å«o†úU…×ٵɮ§ŸP•ÕXù`,1$c%ÈBI,Êúñ÷E|ûð@ÖôŸ|Q¸ÔìfµS×fžÕ¤B¢xŒJÆOÞRxȪƒ²dÉlkøgâ?‰ü{ð‚ÃÇž Ò-ßZ¿R¿dºœÇrG3C1i“±J³2F;× £ühñŽ‹ã½Áþ?]úHöð\h·-#[Ü(RhÜ“†Èö⼬ø âý–4 ã^=Ý®§$ú†–Cqscö¹]â Ãà©À#‘œVtžþÜøðïVø}ðºÿ𑍡»¸¸´X&s”9uFvò£QñÁ$Ž£T¿29ž‡»xâ§Ä Sâ¶§ð³áŦ—Ɖm ÅÄú¤²4̪áaŽ,]rIëŸlûŸ….4§ƒuï_i~1øW­ë·6©XêºTÌÁ£9Y 9*Œ1œWªüÒ|u¢ü.Òì~!Ë+êÀÈÁnÌž8Y‰$rI,<’@!O œ’å¹qzžÏ_kú—Žlÿj-jÏÀ6·zï‡íU¥½w[{xÒRÅØF71' #¯ZûB¾Bñ6¡âÿþÑ:ŸŽ­ü'©ë¾¸Ñmí'—OƒÍu`å†ÀÌ¡È näšTú„Ï[øMñWñ©×téÑé~#ðÅßÙob…ËÂû—|rÄÇk©ÈÏ5å^ø»ñ³â9Öâð/‡4¨£Ðµ›).¯§•b”ÂøXãD·ìÁv$(Ü05×|ÐüIq¯øÛâo‰´¹t9<]{[Y\q!Ô®fCñv³ñ'âji—»ñœšn‹à](‚7Û4ª ØK•Ç<`òG%ò«ÝȼMñ—â×ÃÛ7ñ'l¼:Ö6̆çM³¾c¨Å MøYsÈ +Ò>"|Nñ¯øwÀßtû}G^ñR]$—ŒÉmoi¥“gÌrH sõ¯‹u_Oyðuü | ÔÓÆq@«¨jW6 åЃ,Ì]¤•ä=Á'9úkÆúoŠ<ã|WÓ4½vÎÃK“JÔ­,“Ì»‰$Uu‘#$gk.ãÜŒŠ§JlÃøMâ›ÿÚSÆGÆv0XjÐhÖ±J¶ÎÏ ]6É`kƒœGJêì>(üUøªk2ü%ÒtÁ è·Od/5Ie y<_|D‘•FGÌÙëÓ9'áHñv¿ñëÅž>Ö|3áý/RÒíá´ûl[–'@7à•W Sq"²|ªø·à kžÖ|¬x‡M—Qž÷L¾Ò K•–+“’‚ë±Á '“Æ–Mkæ4{·ˆu¿Šø?K¾Ðô: rà!¿K뢶¶Ca26ôÆs’Ep>øÅâïˆc៓K¸½º³kËKÝàÍná ŠÄ”p#“ÇÔW)ñËOñG‹ôx“Uð¥î¥¡ÛÜ}§[ÐmÛÍœ l «·Ì1äp3ÁÀ$ŽGÀ~¿ºý ´ø{áõ߃¼-s ™­–wÚù’uŒ°˜°U Ù*èi(®]A·sÑÇÅoŠ3ñߊ<+ðÒÇH† N-¦:¤²‰î$ä‰ÝMÀ€Ç=~=Æ×__À‘\hÖZl:œ–’F‰¤1ŘŽñ ¢åˆ9Á q_8|cµðïŠ5-u.~ëãÅð¼ÐéÚ¾#™âÊÛÜÑÐã…'(Ä .N+éßh¾-‡áF øÎsq¯¶žÐÜ»¾öóHßÌ €Í“’ ÉëJI$˜Õõ<{öc¸ø™'Ãß ‹ÈtÓáƒfvJ$”Þ•ù¶nR6g=yé_W×̳þ»âO xsCøOâk}ö‘–ï~`FÓXDX« Ãçç0§žõôýM_ˆ¨l|5û`h¾)ñOÃi×ÖïPº¾ŠGðHßfØŠÆñ¯døƒà„Åàòµ[_iÖ:”]â½·}²©Ï÷¸qþË ö?ÞñµñGá.§¤iÓÞZiZ•Ä—rÅ:[£5¾F ÓÉô5ãßþøÆÃão‡ui×Þ×5½7V¾H#iÖòÒeIe|±^7.IêwgîŠÚ Y#9-Y›ÀcöŽñ7ü'Þ½ñ]¿ö]“ •¤—mó—eF] Ž2j÷Ž,¼ Ÿ í|Oà ÞxNÛCñN™u47–¯g,ެ™9$®Éñ»¦A«µÖ5oxö€ñ7‹bðn³â 7TÓ,­¢—N¶óTðÕ߉¬¬¼g³²µ{©åZ<ÊcBçºÀw£Ä!£]Ùé>MWP²›!f¿eÆeR*ÄpF>ðèÄVŸƒ~Åð‡ã‡Ž¼%¢'Ùü7&ŠšäVJO— ± ?– ;Ca²m£¢]GÅ='Åø»áߌžÑ®|A§Ãg.™©ÚX¨’ébrY$D$näŒòË‚Fr økáßøçâ_‹¾+x«D¹Ð´½RÅ4› +Õ tÖà2Igfâ8ýâ d«éä;js¾xGâo_â_ÄÝ>/ë~+žæi&ºÜÞLk+D±ÃÏîÂì;và€¨>/]i_ þ+ü$kK˽?G¶Ô"ŽÚÒ6º¹(B®w>;œçÕ¯‡Þ!ñÇÀ]çáž¿à½cÄ–ÚuÄÇJ¿Ò`YḂfgE“,¾[d’Ãæ#'ŒOWâm7Å^$ø¹ð›Å²hw°ÛÛ_=ð dK7š$Û²9ã2{S¿½w°­¡Åéž<³ñ÷í7á[ë-+RÒV×F¾“SµkI³”V$²ûú×Úµó߈´ rãö🈠±šM.ÓF½†[•BaŽGpUú{õô%eQ­,i¡EVe KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE•óGì­ÿ$÷Pÿ°¤¿ú" úb¾hý•¿äžêö—ÿDA_1˜ÿÈ× þŸûaóøïùá½*í§ÒôQE}9ôRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PG4Q@ÿ×ýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š(ž—Â^žÞîÖ}ÎHoæûEÂ5¼e&›þzH¥pÏÇÞ95ÐÑE|Ïáù9ÿÿØ./ýξ˜¯™ü/ÿ'?âïûÅÿ Y×Óó-ü:ÿõö§þ”ÏŸáß‚·ý|Ÿþ”Ê·–Vz³ÙêGs˜ÝŠ#*rÈ÷« ŠŠ0T`ÀzS¨¯§>€(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š)ªèãr0aÓƒšZZ)¬Ê‹¹ÈQêx QE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEWͲ·ü“ÝCþÂ’ÿèˆ+ézù£öVÿ’{¨ØR_ý|Æcÿ#\'øjí‡Ïã¿äc†ô©ÿ¶ŸKRÑE}9ôEPRÑEQE%-PEy?Å¯ŽŸ¾èÖú÷ÅÏYxfÎñÌvÿireׄ0 idÛ¸اh ¶5óÇü<‡ö%ÿ¢¥eÿ€—ßü@oR×ÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}¿E|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛÔµñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oÑ_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöõ-|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛôWÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}½K_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöýñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oR×ÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}¿E|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛÔµñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oÑ_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöõ-|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛôWÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}½K_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöýñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oR×ÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}¿E|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛÔµñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oÑ_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöõ-|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛôWÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}½K_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöýñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oR×ÄðòØ—þŠ•—þ_ò=ðòØ—þŠ•—þ_ò=}¿E|Aÿ!ý‰è©Yà%÷ÿ#Ñÿ!ý‰è©Yà%÷ÿ#ÐÛÔµñü<‡ö%ÿ¢¥eÿ€—ßüGü<‡ö%ÿ¢¥eÿ€—ßü@oÑ_ÃÈb_ú*V_ø }ÿÈôÃÈb_ú*V_ø }ÿÈôöõ-|Aÿ!ý‰è©Yà%÷ÿ#שü(ý­¿gŽóx[ágŽìuí`E$ÂѰÎÑÅ·{*N‘– ¸gh ÚwŒ¼©!{¸‚³ØiGͼ¹Ãp UÚ2>a¼›ÆÏxÓà¿Æ?†Þ1¸Õ¦›á¿ˆdÿ„oY¶—iŽÒúàæÆ÷v2<ÇÌrã…eɷ쾇ã'Å~Õ—èϧêr7‡|-¼·F±Þ΀ôûTë¼û(kéß ´ÏŒÿ üGð×UÂ&µjñÃ)ÿ–7 óC(#R@­Ç'ü$·µñvbñŸƒ®%Ð5ø_E¿ÓÏ”Ò0ÿ¦ªžÛ‹(ûµôõKVòíKñSǾÔüðoá5Ô:oŒ>&ßÏk£<^riö6h¯wr#8 êƒÁÉä u¿ >|QøwãÕµï‹Z·Ž4+›9cŸOÕíí÷¥áxÚ9 –MˆªLdîwâµÕÇü+¯Œßh^7ÿ„[Â×ºŽ™¬Ü"4‚Î-V8Ö‡ ^2ã<ŠúÃß~øï^ ð/Œô¯kWS^¬uÔwe`…‘Ý¡.©†‘†9àWMÔò~Ùß¼1¯k:,Pk:ì”Á«êV™5冟"ýõšt%?Œ&â½ñ]çÄ?ÚKáÃøâ?‰urþñ<°Ec{k\Fâá ˆç`$!Qœã=±šüÄýœ¼;á{†ú÷>"|}Ô>kºV§ªÛkz×zu”~d“>ù‘nâ/"N¬°f6=vß4ÿ…Ÿ fÿÙõtr_xAñž|º…ÀÞÓX}ª[™ª¢îHÕŠª„å |ªäóy|1ý§¾üQñ„Þ°·Õt/-¯Û¡²Ö¬%Óæº´Î Ð >ú‚FFCê+Ç¿¶Âox¯Wðs[ë!¾ðê,š»hÚl·ÐéªÀŸô™ ¤’£sÔWˆxãÆþ ø³ûgü  5›/Oá«]nÿU¼ÓfŽê+{ í 1¬²ÄÌùhRr ƒŽköoø¡ðÇàæ¯ñçŸ5Ëx‚ê:­ÄzŒ±Ã%îŸqo¡Y3ÀDÜy8ÉÊ;Ÿxéß>ê¿Sâåž»lÞ{6¿þÑgÛ Û ;Ù‰åJC) ã E°/ÅkEùa‡âEÜH«ÀD°`/ âªÊâ¹õ׌õ¿Ž¿³Ä†2j¿æø‡áßkè·zf©gW‘4Ë‘=¼YS¸ÀÚvƒ¸?ËõgÅOÚ3áïÂmzÃÁú¤z†¹âmNs“£Y½õé·RA™Ñ0<‚;H gð§Äß‚šì‡ñÀŸ´–“q{â ÚÞE¥ë‘ëÒJ].ìGõ¤Ò¨x¶HpÇvrÀµˆ<_¥ÙX~Ü;¿ñÄûÿ…ÖÞ.Ñ´«R·šÖÞÚþÖ"Šh<û´xÃ$ÈX *NsüK•dÂçè矌¾øËáÛŸø.îFOžK[Ûk¨žÚîÎâ/¿Ä2ѰóÁ‚kÅöÚø;>£q‰e¯kº5Ñ´¸Ö´í"âçKŽU`ûõeV8.ŠÊ9çç þø|)ñºãàçÄÛ‰'ñU“Ã}xó[Ü"jÊHíÊËl‰9R¡°ÇFpsKû&|}ø á_Ù§Â×*Ÿƒm,ÑxéK¬Æ,3Y´†"ÑÉÑ™X|ËÔpOQ\§Š?hOøXñç†!3Ýjß´3®jAb?gŠFx£2ôó!!zàÚ¼7ö»™>ø£áoí9nZø3Uv²è¤Ÿì]`ffdˆŸd€u,kÎ~ øZø¹û4ügø“snÃ]øçýµqjĉcäÉi§ÃÇ?,kù’GŽUk…Îëĵû:ÚþÙ³|G¸¹Ôžu¹¼*öðû2y-— æ‰L “v{c#qý ·øãàiµ¿xq¤u‰6R_é*",VÂéüÇ!ž3Ôñ_šúïí ðßSÿ‚vZ|?³Ö­eñÕÖ‹iáÁ ,Šu?ít·hþËŸ4 ©!¶í9äâ½[Ç1i¿¾'~Éòøúú'IðÞ•{¢]ßÜ8ŽÚ…Òºù’·ÊªÎÜÄrN2E4+ŸuØ|UðŽ£ñOSø;m$ÇÄzF©:˜B-¦pˆVN…²yàºßíÇð;DÕu;oø›ê>‰uö-C\²Ó&¸Ò-n2¬—(0v’2†Që^5ðÛÆÞøûp|V¼ø{ªÁ«[Gà»k(¯-dY`{…•3åÈ¿+fÁ*HÈ<ב|)ø½ðƒÁ°'Œ~øÏY°ÒüY¤Zø›MÔ4‹©R;ù5›‹Ÿ(¬ ûÇÝæÇó…!0FG–p¹GÌ~ xç⟃¾ü6½ø³­]ü7ckã\Z/ŸæA1P_¾x ŽÇ5ãžý°>xÛÅZO†ü1g®_ZëS-µ¶®ºUÇöSNÃ"?´‘Ž£À)ž7WÎþ?ÐußÿÁ/äмOÁ©[xVÅeŠA¶HƒK$l!‘R ŒJû¿áF›e¥|.ð†™§Â°Û[i6*ˆª NÃŒç“ïSaÜ¥à‹ž ø†¾,o Ë3ê·š6£æÄÑí»±ÿ\?}FxaÁ¯ÔÿlσZo‚<ãå­õ‡Ä¹G†ÓO–{«‰-NÖO%~efn<¤“_4üøÅðãá ×í+¢üB×mtMZ뚌V7S$WWP^.ë³BÄ<ÆPÐ̹ûÃ>Ið.0|)û#Œí¾ñ'æ±ÊGëUÈ+ŸpøöÞø#ñÄžð΃ý­ž$¬­înté`´MAÈÖRLß(¸ *2?Ú¯ŸüEâoxÛö«ø«à ¿—? t Ǥ=„lÕ$7v0É(çáÉc‚yjòE? þ´*º‰õóoWùq^Éá„ß ~)þÛßaø‘ám7ÄÉa‡šÝuhîDEôèw´œ ã®)Ù!]ŸUø+YÑþü%Õ¼eñâƒxßEÓž[¹µ»Ÿ ˆ¢]©ä¯Ùr­†e‹6:â±>þ× ¾ x¯KðlvÚLJõFÒé?Û:t¶1j(‹¸ý™ß†;~`­µ±Ú¼‡öÒømáïþÉ÷ºï[é>еm3S½ÓôëuŠ/±Å}·Må ýcñÐ\§í ñ3á·Åÿ|ðÏÂ=vÃÄZÙñnŸ«FštÑÜ=ž›j…îZa>H1ü…_iÎF>R)Y1Üúïâ×íð¿àŽ©¡é_/¦±“Ä xö¯27Ø¢2ºŸ,½øXÔÎä*‚H¯=öÊøU„|Mã]wMñƒaáCbn×QÒ'·™ãÔf0A$(A2+:œó/uá?h-.ÃXý¯fÛ=J¸…nµû€®-®žóÄØ=ÒDV±×Yûu¢·ìéªîÕt}Çö¥µ+-³œ—þ ð×ík©Zø‚ÆM.â8uŸHOŠb¢+›¯ùç ›×iåŽ~í{wÅOÚ+áßÂ][LðÖ¯öíkÄZÌm5¦“£Ú=õì°'Þ—Ë…Œ7;('¡¯Î¯ÚÞ¶þذn:?„‰>¤[Aä?*½â2ßMý­fÔ|qñ&ÿáe¿‰¼¤ V‚[kx.ŲsjóÝ#ƬT±õ;A®D+Ÿ£? >=|6ø«á}[Å~¿{X<=$±j¶ú„Mgu§I —u¹†L4xP[wÝ  Á¯‚?joÛcÂ^$ø­ÿ«»×ôKëÙí—HÖZÆâÊÒûɺA8µº û°Ç ·rƒŒàÕé~xãá·í {ðsâ}×ÄÿëÚ+ªÈ&¶ºPñÇ1ƒiFò´i*áX·#$gÇÿhߟ|GÿÿðOƒ¼9­i÷:ÓÚèVðipË]ÙOe-Ç›;ãØÓq6àA!$R¸7¡ú[ûRüP¼ø9ðÆ?4¿ù ÙYˆlëòñÖÚÜþȦ·¿gÿ‡ðü/ø9á_¬ò]ÜZÙ¤·W±i'»¹Ì÷±bN^WcŽÃÀ¯!ý¼|5ªø—öXñŠè±‹Í YêËŒ—]6ê+©ÉŽ6Àõ¯¥<â-?žÐÒ¦DeÈ-Z÷ø¥ðÇľº‘áþÙ²–åŠê¶Þ8Ð¥²Ð/øgĺô¾v·b²éz‹÷’ëOÀe=9•e#±|v¯§«áïø'§‡uön±×µ41IâíOQÖãFqÔÛ!`:’8ÖEÇ𰯸h–à¶ (¢¤bRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%|Ñû+É=Ô?ì)/þˆ‚¾˜¯š?eoù'º‡ý…%ÿÑWÌf?ò5†§þØ|þ;þF8oJŸûiô½Q_N}”´Q@Q@ KEQEø£wà? þÑ¿ðVoxã5Ÿü$Þø{ák{½'Lºv{8å’ 0¶øI*ÊZöW+€ ';kô;þ§öGÿ¢CáŸüAÿÄ×Ä?ÿå2?¿ìL³ÿÑ ~¿PËÿðÅ?²?ý ÿà²þ&øbŸÙþ‰†ðYÿ_PQ@/ÿÃþÈÿôH|3ÿ‚È?øš?áŠdú$>ÿÁdüM}AE|¿ÿ Sû#ÿÑ!ðÏþ ÿâhÿ†)ý‘ÿèøgÿñ5õyGˆþ5ü:ð§Å? |Öõ‡Å¾5Šê}.ÔA+¬ÑÙFÒÌZUSaˆ Àœ`v 6ÿ†)ý‘ÿèøgÿñ4ÃþÈÿôH|3ÿ‚È?øšú‚Šùþ§öGÿ¢CáŸüAÿÄÑÿ Sû#ÿÑ!ðÏþ ÿâkê (åÿøbŸÙþ‰†ðYÿGü1OìÿD‡Ã?ø,ƒÿ‰¯¨( —ÿáŠdú$>ÿÁdüMðÅ?²?ý ÿà²þ&¾ ¢€>_ÿ†)ý‘ÿèøgÿñ4ÃþÈÿôH|3ÿ‚È?øšú‚¼«ã/Ư‡|?Ä_Š:‹éz¼Ðۼɷ-æNÛPl…]ù=ñÞ€<Óþ§öGÿ¢CáŸüAÿÄÑÿ Sû#ÿÑ!ðÏþ ÿâkéå`êzE:€>_ÿ†)ý‘ÿèøgÿñ4ÃþÈÿôH|3ÿ‚È?øšú‚Šùþ§öGÿ¢CáŸüAÿÄÑÿ Sû#ÿÑ!ðÏþ ÿâkê (åÿøbŸÙþ‰†ðYÿGü1OìÿD‡Ã?ø,ƒÿ‰¯¨( —ÿáŠdú$>ÿÁdüMðÅ?²?ý ÿà²þ&¾ ¢€>_ÿ†)ý‘ÿèøgÿñ4ÃþÈÿôH|3ÿ‚È?øšú‚³õmRËCÒ¯u­IÌvš|2\LÀ+J]ŽIÀÍ|ßÿ Sû#ÿÑ!ðÏþ ÿâhÿ†)ý‘ÿèøgÿñ5ë? ~,xã‡Ãý/âÃ{æÔ¼;¬yÿfá’ÝŸìó=¼™ŽUW’6œdq^@/ÿÃþÈÿôH|3ÿ‚È?øš?áŠdú$>ÿÁdüM}AE|¿ÿ Sû#ÿÑ!ðÏþ ÿâhÿ†)ý‘ÿèøgÿñ5õòÿü1OìÿD‡Ã?ø,ƒÿ‰£þ§öGÿ¢CáŸüAÿÄ×ÔPËÿðÅ?²?ý ÿà²þ&øbŸÙþ‰†ðYÿ_PQ@/ÿÃþÈÿôH|3ÿ‚È?øš?áŠdú$>ÿÁdüM}AE|¿ÿ Sû#ÿÑ!ðÏþ ÿâhÿ†)ý‘ÿèøgÿñ5õòÿü1OìÿD‡Ã?ø,ƒÿ‰£þ§öGÿ¢CáŸüAÿÄ×ÔPËÿðÅ?²?ý ÿà²þ&øbŸÙþ‰†ðYÿ^“â/|+ñSŸõ½EáñobºŸKµJë4vQ¼³•TƘTb0'«ÕèåÿøbŸÙþ‰†ðYÿGü1OìÿD‡Ã?ø,ƒÿ‰¯¨( —ÿáŠdú$>ÿÁdüMðÅ?²?ý ÿà²þ&¾ ¢€>_ÿ†)ý‘ÿèøgÿñ4ÃþÈÿôH|3ÿ‚È?øšú‚Šùþ§öGÿ¢CáŸüAÿÄÑÿ Sû#ÿÑ!ðÏþ ÿâkê (åÿøbŸÙþ‰†ðYÿGü1OìÿD‡Ã?ø,ƒÿ‰¯¨( —ÿáŠdú$>ÿÁdüM~dÿÁNþü&ý›~|<øõð'Ã6^ñ~…âÛ8 ŸKˆ[Fèmîn–$]–KdÚOð–Sk÷Z¿ àµ_òk>ÿ±ÎÇÿMÚ~¾ÒÑEsEÿÑýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€>gð¿üœÿ‹ÿìþg^Ýã Xø÷ÁúÇ‚µK‹‹[-nÚKI䵓ʘC2í#àí,¤Œã¡¯ð¿üœÿ‹¿ìþg_LWÌp·Á_þ¾ÔÿÒ™óü=ðVÿ¯“ÿÒ™ÊxÁ~øuàíÀ¾·û6“¡ÛGkl‡–ÙÆæ€òï ü"ð—„>#ø«âvö‹mOÆkmý¥“ýImSËIÄXùd)Ã0<òHÉ&½FŠ(–¡§iú½Œúf­kíÒå†tY"‘‚®Œ`GPF q¾øSðÃÀw‡|#¤øzêè–]>Æ Y ÎÒÑ"’¹Ûœ{WEyÇŠ¾|%ñΨšß<£kºŠQs}§ÛÜ͵~ê—‘ŠŒð ǵyÇ?‚Z§ÄDøicàôÓ´ý;Á~%Óõk›y÷C±´Èx Ž(Kp¨ÁTô,+éz)ÜV8Ÿ|6øwðûí_ðøcLðïÛŠ™ÿ³¬á´óJçnÿ)WvÜœÓxÃF¶ðçŠü3¦ëUÞÖîÒá‡jìR:•L/h í( gÂÞ ð´ã¤ø/C±Ðl‹o0X[Gkn›ŠÄª ÷<×7©üøE­xŒxÃWðN‹{®‡ýºm>ÞK“"ãk™Y \ r1Æ+Ò¨¢àdkºƒâ&ã@ñ.›m«i—@ ­náIà+ã2°  XÓ4½3EÓíôÒ 4X ··b†(ÔaQUP8_¢€>oøŸû-|,ø‘«Xx¶ÚÉ|+âý7P‹R‡]ÑâŠÛP3FÙo5ö(‘r­æë^ÕâŸxKÇ:AÐ|k£Yëúqe“ì÷ðGsõ "° 2p@ÈÉÅtÔS¸Xä´øÂ×BûÃÓt{•·[A-œ6î-îXCFŠ|µ<„û óŠÉÔ¾|+Ö|Ouh÷¾ ‰ÒEÔ'°·’ìøþãUðO„töè–{ -¥u$¥âE;rÛœgœf»ú(÷ÞÔµ}?ÄŽ—kuªia²»–{‹S2˜äòd`^=èJ¶Ò7ƒÅ?[Ð4/éí¤øN¶Õl]ÒF‚îž"ñ0tb’\«Êq@#‘ZôP%àê§VmOÚmÙ×Ò(õ5œ2}µ D·”ù¢0Pù ¥ñ7€¼ã]&-ÆÓõ½2 V×¶±\CUÚ $ŠÁH€+¬¢€9_ øÁ^°}3Á‡‡í$`Ï…¬V¨Ì8Ü˨cîy®Y¾ü{Fñü <ÚÁÍë¶—jÆä—“.c;ó wg,uæ½NŠw½Ý¥µý¤ö7±,Ö÷(ÑÉŒ«£¬¤wâ>|6ðßÂ?Xü?ð‡ž4m1¦û4w™š$šF—ËV<ìBÄ"ÿ áGWE <†ãö~ø u<—W_ ¼54Ó3;»èÖLÎìrÌÄÅ’I9$õ5£â¿„^ñ_Ã[ï„o`4 êýšKM/mЬ·4qˆ€¯È` d^›E;…ŠV—§èš]¦‹¤À–¶6Ço1©Q(DU€@Uú(¤RÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-P_4~ÊßòOuû Kÿ¢ ¯¥ëæÙ[þIî¡ÿaIôDóüpŸá©ÿ¶?Žÿ‘ŽÒ§þÚ}-KEôçÐQ@ KEQE”´Q@?ÿå2?¿ìL³ÿÑ ~¿WäÁ¿ùLÇoû,ÿôFƒ_¯ô”´Q@Q@ _’µ÷ü!ð³þ û=üBñ hz‰g»º“%Q>Ãr PY™˜…UPY˜…’~¸Wä'íàüDÿ‚…þÌžñ\Ýi’èÜÉ £1ÊÖ;îãGSÃ+I‚§†ƒ@©}ÿ6øo£ÚÛøŸÄ ~!é¹1„ñ%Þ€bÓ6Ê@I7™²c;Afèר>0øóð÷Â_oÿh8nd×üc§ QfÓM%Å±Æ "GKsÈf\ƒ‚¯Mñ‡ôoh:‡†3Ó> ]ñ׎µx>Õ…á«/·^,$n@ªv¶,Ø*×ý€| ¡ø ö@øce¢[$VÒ-õ{—QóMs©(¸wsÕÎg¢ª¨à òo‰Ÿ´—ÿþÒš¿„¾ü¸ø§ñ±,"ƒZºÓ ·³"6Ho5)U±ŒE÷”¢üª\0Ø=+àíÇðëãÄwø5­xkÄ_|z-Úæ#ÄÖbšê$Ù !›vÔRØ`„¨%Cb;_µÿÁ¿ÙûÇš7€þ(Ü]irkZeî«ÿ”e6*ÅÑÛÌ™d+¶$Ž',Ä ù©ñ›Æ¿üKûbþËž$øÇð¢Ûá…âk·vv’C­ÛkÞC1·YbÛªìùs|ÆÇzõÿÚãÁÄ/ø(¯ìÉáŸ[%æžÖÚ¥ãÃ(ÜŽÚrIy²ôeß äà‚3@…?üóá–‡q¦ê^?øiãßøGX™!³ñ«¢té<Ì•“p•˜¡Q¸yaÜŽv×Þ^3ø™à?‡Þ½ø¡âýjßNð¶Ÿl·rß³n‡É“YM›‹™ (EPK–A$ ðßÛ—AÓ|Gû!|[°ÕaYáƒÃ×·ˆd ¬“í0°÷Y#Rb+óã=íÏ?c/ØÇᦹ+ÿbxÓ[ð¾©áФ–Ä X¤#’6¾ïªÔ úºø*GÃ;K ßü-øƒiàK¢¢?Ë¢*é¬àH$ó°cäA,{.x®sþ âßxóö oxBþ=SEÖ¯ô{»K˜ŽRXe“r°Îàò‚WéÖ»á/ ø›Â—þÖôènt NÎM>âÌ®Ø^ÖXÌOT¡ÀÛŒ˜¯ËOø)ÃÏ|)ý€¤øðÿM]#Ãú>«§%¥ª¼’¬K%ËÊÀ4¬îrîÇ–=p8À Qñü“ᾓ.§u࿇ž6ñÿ…|<òC¨x“AÒ>Ñ£ÆÐdJRåäEu±Ú˜ä1\û'àÏÆ_||øy¦|Nøi~oôML0Rècš)c;d†XÛ”‘`ŽAᔕ!Žïïh¼áÿxnÖ;]+B°‚Ê‘p»"@¹>¥ŽKÉ$’I&¿:¿à˜6þ±ø÷à-:NƒñW†ÒXÑBDG`%{PêÕÕµ¬×·²­½½º4’I! ˆˆ2ÌÌx’OA_œrÁK¾ßÃâOü0ñ¥M,W>&Ó4_3L úÉQÚUcurá ŽH韾üqáxôkuDYy¨lŽE~Eü&ø›ûMþÁ¿ íþüeø+wão‡Þ7Koâ_ H—DØË3ÌÏ=¡à-)ƒ €ÀòÄô*÷ö¬øKìãqûRè÷:ç‚­­>Ößb‰~Ö@˜@ñyS<@I„«+8ŒgÚ¼âÝ7Çþ ðÿ4Xå‹Oñ&Ÿi©[$áVe†òš1 Ve€`€zÖ¿7>=øÃàoàš?ý¡|ñ'âçÄ/ƒ ¥ü:ßÃV³]Jkˆ¢KYMò3ÇöwY]ÛNíè˜=3_1Éÿ2ø sa4^Ñø‰ÿ ý™<-âËxîtÉ Ô.e†Q˜åk÷q£¯FV’%O €=:çþ sð÷Jµ‹Å%øQñDð=à ‹Ä—zÍ9’F%/æÿ«l‚¥w3gsÅ}£âOxkÇŸuÏx?Q‹VÑ5òæÒêÝÑ=³Àõ„9¯Dñ‡ôo臅üCiö•ªÛËkuo ÊK ÊQч¡RE~NÿÁ9¯¯ì;ñ/ÃÜIqeá]cÄú]ƒ;ne[Xçù=Œ“9㌓@ÍÿÌÕô½öð&»­ÝÅa§iÑë·78H¡†-Võä‘Ù¸UU’xVlŸðS?‡:Ûµ¯ü0ñÿ¼¥É"\ø‹HÐ̺h}÷GyPí^­¼!œWÀ÷Þ Ö4ø".‡ïÕ/g²¹tà‹y‹§ÛZÚˆ¼O¦*¢T6<¾­÷‰<’I<šû3áÏí ð·â×Â)þ6|>Ô›Xðí­½ÄóˆÓmÌ/k›5¼‘9fQ”œ‚ Rü¡üËá'‰t]"óáO‚|añVÔí õÆ•¡ékyw¦@%x”ßyR¼q3˜Ë*«9ÚTœnò/ÙWá¿ÆKûNëÞ=øwqðã¾:·“WÒ´É/m¯"·¹hn¾Ô‘ý°7oOàQµUGÝéðIoè~ýŒü;â-:Ù#¾ñUö§y{(<¯ä¶q†=p±À¸IêN@;þ Mû8ëÿ .|jÑê–ºŠhçÃˈ$Ôe c·ŠÒ7`åö¶~Ü©V*ÿ-iü*ý¾¾|Aø©að[ÅþñWÃk1´ºm¯Štß°‹Ð`±6ö!˜+crª±U‹§çƒ ¼(?à­_ü@lb3i õ Ø1 Þ£md³Î¾ŽàË“ÿMÖ»ø)-•½§‹?f@Š5=/â6—ãæXç‘$‘s×k4 ‘ß÷—ÆŸ¿ ÿgïÝüFø¥ª®•£Û2Ĥ)’içpJCkó<‚@,Ä($|cüËáΛö câÃøÂZ¬±GmâcC1i¤K÷]äI\…#‘å‰ ^%ÿÖüQ7íaû7økKð‹øöÞÆ]GW·Ðæ+4Ôo`ØÊ ³bÂ%нWâwÆ?Ú£âŸÃ¿|9ñ쓨K§ø’ÂâÆPþ'Ó/ž…UÀØ>dbH ‚ûã_í#ðçàg»/Œž#7:¿†µ ìa·›JX® ‹¨©™L’Ć"bÁºr¯™iK—‘Õ1Ë©ŽCÁ? ü}ðoŇ_ðH½À_tÉ´Ÿh½½³A<ÑÎâÛûBg¶âyjÂÊŠáT WîGÃè?~x{áÿ‡-cµÒ´,¡‰ ²$ Iõ,rÌO,I'$šñÿkŸ†úÇÀ[OÚáþ•®øï@¼• [O7zªL_ˑ՞=¦&ÿYóce,¥Iø#öýºuÿxRÓÃ;ð‡üe«x“Å1G®[é­¨i6PßN‹rÞ<àŰlÈré^£ÿÀ±·ðåÇ¿éh!Ò´ˆú¼‘(ÂÆŠ ª;±(Ú¬ÿÁ"?äÑþÆ WùÇ@¨5á?hoüLø§ñàî‘iaâ/†³ZŨ%ôQGéx¬ñMjRYã* ’ê„n^9¯x¯Ëïˆ ÿ Sþ mà/î4oÞºðý㟺ږ›¶H˜ÿ´Ê¶Ñ/~Obhê߈?µ_¿†ŸüûÙo%¿fÝ’øØÈW%Ì…” PK’‚Hð9ÿ‚ž|<]0øÑþ|Cê‘¥ÄÞ)Õmt#Mó<£q=ÀìÍ‚U#L’Ø#qPqœÔ>,øÙàOþ ø_â&8toZN–Z²JJZÛæk7B¼B mœ±$cåc^)àD_Ÿµ·ˆ~&Ïþ‘áOƒ©/‡ô^ñK­N?âcp½‰…r©'ª©¯]ý«¾j_>jÞ&/ørXµ½ d’=FÀù‘…ïûÁº2;†Á⾪Ëcß>’¥¯"øñ_LøÙð›ÃŸtÐ"mVÙ~Õrm¯"ýÝÌ'¿É*°õ\7B+×jE4Ñ[ÄóÎëQ‚ÌÌBªÔ’xzÖ‡âß øœÎ¾ÖlµcjÛe—Ïå·£ùlÛOÖ¾1ý´ÐøŸ_ø5ð“Z¹–ÛÂ^9ñ8µÖDr…ÌVð<ÑZ»)lÎ+žqŠúÂ_~|4×›Æ?ü%c jñØÉcæÙGäï·fW(ê¿+|Ȥ3ïN¹èÚ—‹¼)£júF±­YXß]œCo=ÌqK)?ÜF`Íø Õ»Ôtý?Êû}ÔVßhq~kªov諸Œ±ì5ø…ðcá7Ä¿? |GãíSᯅügªøÖÿR:εªÏ§o"Jñ$1¢Ã"À-¶…@Œ8ž8¯Yøóð÷Æv¿³Çìùð»ã® —ÈñŽ—¤êwV÷&ûW¸’Èœ„míjP3àÄ“žµ|‚æ?UtoxoÄbsáíVÓT¯åÍöYã›ËqÕ_c§ØóQk-𯇮mìõýfËLžìâî®#…å=0Šì ~ðˆþx+àOí‹ðU>éPøv?Zëf¯ab<¸nm,íLðÉ$c‚ѸܯÊFy9Âø ðoáŸí ®|qñ¯Æ­"kâ½CBS{󾟧ZC †83þ«c0eô‡+”w?O y®nÏÆ^Ô5‰|=§ë–7:¬ó-"º‰îwD­¼cÜWä^“ñSâ&•ÿÂÖu?Rº7úuÔú%¾¢$o=tó|°Y Ü0ŽaVÎåÁ_}øöLýœü'§øZÿÂþ±7¾k{ËV1þ—,Ñ€Dï2œÈdÉ, *rxÆ)8Øü}:·Åï‹ÞñŒº~‹¢ü<›FŠÖòi„>oö”ÊþkJÁ\bµÿi/Žð/àåßÅ}O‹Ä+ Ö‘ÅŸåÇ*ÝH±‡(n0Ù ù?áïÁ†ÿl/Úïâ6‹¿ú vö×@½ºµÝ”åòó´È«Ô|eC6ɯ–u©.mÿà9ð´s;Ùø[Ç3éVF-åZÁ"sØ3±Ç½W*¸®~€[þÓÿ|ã xOãïÃuð¶Ÿã[¡a§êº~£¡j/e!˜GBÞ¤tÉÚØû[ñƒá«3¨øR¶Ò­Çw2AOmÒ3ø×åwÄ;㠟ǃö¿µÆ¯c©xõE}}3mhšê'ú2êdÞÁ¾1“œÄmÇÿÿmoèkáÇ–>ÒôØ´Ý/Ä7²ÚÚ[%ìM-ÔQG«,ŒîÈÌËÀÚ30r…ÏÖK+Û=JÖ+í:â;«i†èå‰Ã£¯ª²’÷‰uã?Øëxv÷]°·Õgâ;I.¢K‡Ï÷b,þ¾ø-ðóãOì÷ðóã.§ªéšv¥Éos¬xwFÒï$½‚Æá-di¢ˆËG•UÂôÉ#µCû/~Ì¿|yû=øSÆ~0ðõ¯Š¼AâËhõmGX»Ý-ìײ¶öq9;Ô£ð#©²ÏÐdÔ4ù/¤Óê&¼‰u2ª7Šgp±# GOköÒÖê#z‰æ˜¯š#Î7”Îí¹ã8Å|EñÂ~~Óß ¾5Âg‰·ø'Y“¢âì™tçsÓå¸Êäô Ojwì›|Hø‰ñcö“D±x—UþÃÑe8?ñ(ѳºîÏ.ç÷{Ž].=Ѿ(k+û@¯ÂqmöciPó¾o;ÍÜËŽ»vàzWºW0|áƒâ±ãƒ§Çýº-þËö¾|Ï#$ìëŒdžÕÓ×va^ŒýŸ±­Ÿœº¿™¤Úv°”µ‰âWµÃÚ›ÞÍ5µºÛJd–Ûq™iËFÜ#œ×çêêÿ0?âá|@ÿ¿Z‡ÿ#W¡’ä2ÆFR\Úvƒ—å±Tés£´W›|$›F¸ð/‡õGU±&_.çU-ÛâFϘ%T~@ÊŽ1^“^>*‡²«*o£kkmåÓÐÎJÎÇÈ¿µW>$xNóᆾk©áÛ¿x’"âí­b¼) ±±ÊÇ(ÆAöú×Óþ²ÕôÍÆÇÄ—ö¾£o %Åç”°yò(ù¤òÓåMÇœ|û\Èùû?Øõgÿ¢žû~x—^ðÿÀ²ÑoeÒáñ±¦i7÷±I ±º—ã•ß´FHçEcm‘ÜúÏLñ—„5½F}F×,oïí×[ÛÝE,ÑãûèŒYSk~*ð¿†D'ĚŞ”.d_k¸Ž1eór}…xïƒeÏ€>¾Ðu¯ø6ÇLÔ¼:?Ðï M·2ؼ€æMÊNwg5ùÑáŸüEøáñ³ãOе‡þø‰&•â+½ñ¡4 §XÙ»ÇVð$R¢¬ˆ™ 6HêM !¶~Å]jZu•ŸöåÔPZaO$Š‘üäùÉÇ$€9ç"¨iþ(ðέ¨ÜéV¯g{eþ¾Þˆäš,ÿ}‹/â~NüWøgñ/áWüÇÇø£5¼ÓW²:jÚÝ=ÚYé³j¶¯ "Yù²®G@+¶øÅð‹á¿À‰>øëᕇâøJ4m%î-IYu-KÝ%ÁÏï·).Kgž”ùEsô·\ñ?†ü1w^%Õ­4˜emˆ÷sÇn¬Þ€ÈTì+ZÞâ ¨RæÖEš@dt!•”ô Ž>µù× ü?ðgÇÛ+ã_ôÈ|G‚¬´k Æôy¶öö÷–þ|Ó$GåÝ$„©lgå®O࿈uφiO øieÓ~kZÄùüϲÇöT›Éˆ±?,.Å”™£”w?Heñ—ƒíõ´ðÔúí„z¼ŸvÉ®¢-߈‹o?•x¤ŸnìiMOá®¶v^Ó¼*5澚O-Öcw¾×f!mrryÍx§ìëû+þÏÞ#ø àk^´ñ·¯ØXëwšÍÁio§ÔgT¹y¾ÐNõ+)ÀŒc'9ç|_ð¿Á¿?à i~<ÓÓWÒ´ß-ಛ&ÞiÖú8ã2¦vȨ˜+7ínª(I çØŸ>)éþ øCâŠþ6úý¶a-ì"Õ¡¸ò‡A*n>£5ò7ü6gÅ?ø?Âßþ-ü-]3áïŠÆUÕtÍM/d´‡Pd†IíŠ+€U¹8?.w‡iNŸà¿…¶Ã_Eö?hÓKaf¤ùVÂîÛ2$`“µrƒëšó¿ébýž~ Žº½ßÀ[Û. º f JÒÕíâû!¼iQÃD¿(•¢`Å¾è ³4¢„Ùûm«iZUƒêº¥ì6vQ¨fži8•OB]ˆP?n‘­húýŠjš ü•œ¿rkiRh›Ž„©üëó ö´ñWĿڷÀ? tmLñ_‡´ÏK®Zhº­ëÚiw—VˆLÆ9¢@¦4*@ˆÀ'>«û4üø»ðÃã/‰üW¨ø{Dðg‚üKa—EÑoå¹¶]Nnc‰âŒ!xò­Ž?LG.ƒ¹ö¾³ã/øvæ ?k–:eÅÉÄQÝ\ÅÈOeWe-øV³êzlw°éÏw ÝÜ)x¡2(‘Õz²®r@î@¯Ì¿Øóà÷Ã/ÿ õÿŒt_øÇÅZÆ¡£.¢¾sÚ,RmKHƒªH—î…Áé^ûWøGNøG¡ü+øßá o³Eð{Sµµ™T’F|Îæ2ܱXÇ–Ù'…êMºØ/¥Ï»åÔ4û{¸4ùêä1Š&uHïRrØÏ8UÚø7á•Ä¿l|Oˆ­×‡þéñx_Ir}sûûùPôܧ6;*žõ÷•&¬4xÇ?Šº¿ÂC¾Ð´ˆõ«ísU·Òá‚Y¼…ßp®T—Ãc•§|Ô~ñWÇmGÄV¶^2ð=†¤I¿Îº‡SG„%qA»-׌漿öƲŸRðï€tûk©,f¹ñn\C2t”LänRr21šõo|)ñ_…}ùÛvvVå|«æŠøßiu«xãÞ=µÃºŸË\N¦Bñͦ•ÝÜlÁr¤˜s´ äV~‡ñì'Á«ßž<ÒN…¤åå±·Wó..-Y‚Àä0Pbr«ýÜ6zãåÚžÖ_‰?î#ðŽu7ø{¦™¼M,R¼jµyRUÓþN‚£>ÒyäuP¨þÒ¾"ðÿŠgÏ xÏÃådðÁÕt{¹/È–‹&XϺñ®gŒ¨½¦¿ vóÿ†Ùžäxk ?ª?f×·”y•þ 6Z½j|Q¾ÊÉ_SÔ<-ñãæ­¨è÷úïË}?ÃÚ´‘«•ÔCßÙÅ/Ig„ªŒ ʤ°î2µŸ?î¾)x—á¿Ã¿ÚkgÃQÚË<×ÿe%n“rà=ò8=«ê(¤ŽX’h˜:8X‚B|×Á~× XN9¯¶+áÏŠßò}?¿ì âý•÷9t%-V½(¶s™Y‘m–Lî•Ç9«ñþëÅß³¹˜?ÅÏ‹JÁÛ!mõ ç1eÓÒˆÆàÙûE|éû0j ÔþMsàoxƒÄºpÔ'SuâD¹Ká(H÷ QDþPùq’Ø=kèºLhùÛàŸÇ+Ÿ‹~2øáYô”Ó—áî¶úBJ²™ ÈRãÌ ¨Ø~N€ž´| øóÅŸø³ÄݤƒÃ>!Ô4=Ï8) ³”D²38P¦BGËëÆMx7ìl¾_Æ?ÚVâAã'}½ö¹˜©Ç¡+ã_EÇì+ñì»b¾#\áÁãÛv¼ƒíœæ¯”‹ŸµúŠ<5«j:F•«Z^ßY¯·†â9%‹þº"±eëÜRë&ð߇M¸ñ­i¦·òáSÇšçøSy°¯Í/Œ¾ü_€ß>i±hþ!ÿ„ŸKÒžæÔí—S³Ô!Ü­Ãg3oÛælž¾µÓøGáÏ‚><~Öÿ_㓈ÿáM'JÒlo‡™ ­•Í šWŠ3Ài\î,9ùºð0¹zŽçÓß>/kSDzk–¶ökáOêz$Ø{{'Ú’9b~b98â½wHñ·ƒ|A{6› kÖ•å¾|Èm®¢šDÇ]ÈŒHükóö|Ô~|)ý˜~?Â[o6·àÝ Äþ Óä·W/5寘¶‘B$$Ò‚©¼ž§9Åy7Å ø«Àú/Ã_‰šoÂ|'Š?h‹au¥êm.®"»™C[Ê#‰#‘eˆ‘ ,p3ïMÇPæ?P~<þÐ^ø¢iÚ®§jwú¥–œmæ8f‰o€ƒdìM¹Ygôwü+ှ!ëß¼;gáëèt«»ÕM>‡Ì¸†òÜ…^Ù©²°î{‰ño…¶<2u«!¬‘eö˜þÓ_+vÿÒ¼OãÏÇÖøKyá¯ø[@—ÅÞ:ñ¤ï‘¤Ã*ÂD7K4Ò·Åä–#ð#ó/Á¿³Åoˆ³þ‡?…>xUu­vÖ×X¶ñ‰Ö®F¶×³m¸ûcIä²1'r!rGQšú‚äëú?í»ðVoŠ Öo¼ wddŒæ¬*‡ºXœÀI·€JžœâŸ*Ϩ¾xÏãÞ³â ý ã/€¬¼7vÂâÓPÓ5!k3o Ð8dŽD»•ÚGBpqë×>-𥖱‡o5«(5[õv’\F·ŸîÄ[yüRñö±¨xÀ¾#×t• §i·—6ë€Ù–YÓƒ×æŠü[øSû?üNøÇû=§ˆ-~x[_ÖUrFÀÀ‡['(\ûßQñw…4JßEÕµ«+-BëM´÷1Ç4¹ãä˜3~º@“€+òGötø/ð»ãÿÀ¯ˆßþ3iêþ,ñ&©­íFìæçMû8ýÔp¹?¹ã  p6€+?Ä?\꺭ʹÞ,¿Ò´=SQœiÒܼNæLîÖ5ˆ¶rCõ4r1úµ¥xËÂî¡>“¢k–:…õ¯úë{{¨¥–?÷Ѳþ"¼áw퉼Gñ7LñÕÆ Ùx/Ä¢YM,â:,jÊ\ÊÀ lak¡ðwìÁðÀ:ž‡¯x/Á¶N§áôd´»·B“…t1¶÷2nRs»<ó_|ø ð·â¯ÄÿÚCWø¡Aâ #ñEݺÝnu·  íÎØ‘—7gŠI!Üúãö˜øéª| ð6‹âŸ èÐøŽó]Ölt‹x$¸û%Õ­4˜em¨÷sdzz#('ž•±ð\—6Ò,±H#¡ ¬§Akò㿾3~ÔuI| áÿˆ)á;»MÎ×Ä—òÀšu«[‰A··H¥Cç’]¤ Œ•éz†>:~Ìß²ÅèudµÓ¦ÓþÝ}á»m>òKÁ¦ÙÜ¢ƒrHˆÁabÍgŒ`Rq*çè‡ü&^þÛþܱþØ9Å—Ú¢ûIÇ_ÝnßúVÄŽŸuuqemu·›|è‘Õž-ü®õ+œds_ü ý“ÿg‰~ø]o Ùë´Öºn¶ÚÔ™{ëA–;£tn3¿-/ÍŒãb°|{mð#öÎðïÄK¦[O üZÒ&ѵIOËjzXóí%r8ÜÑŸ)äîsÛ…`¹÷tZŽ=ìúlQIwlË º™#2¥”¨#‘‘Í|ÑáoŠ>0ñ÷í=â¿è ‚>éÐ[ê_ºV{½jø ‘„nT‚X/VlÁçÿ±‘uâ]Æß´n¹ÝOâÆµ=å¹nZ="ÉšÞÆ>yÆÙOt)éšÏýã{?‰Ÿ´n¨£.¨ž:»šBÇ–¶¸KLA \{S¶â¹ôÅÞ øM©i>»µ¿ñеàÍa¡èÖÿkÔ&3¾cTG àæG`88ÎÖÅO…Ÿ´‡ñ#ÄÚŸ€µ=Uðo‹´˜#»—JÖ`X¦’ÖSµn tgŽX· ¥•¸`Gc_ üRñ‡ˆ|+­þÐßt[Ã¥xš-oÃ>‡RuúVsä 'ˆ„RfwÎ1½C0ùk¸ðwƒí|ûmx3CÓ|u¨øÆ êÓ²êWk{=¹iíþo4|¹ÈܨI UŠà|ªÁsô¦–¹„ñ§„$ñ3ø.=f͵øãóZÀN†äG€w³» çÓÖex×üqâ{OøSAðš5ü:‘®¥ÕêGumk·†‚ ÛžMßÊöZüäý¦>xß´Àoh: †·­øÀ›ëÈaTžäˆýã–çÖª(Lý¥ª—÷öZ]Ƨ©N–¶–‘´³K#HãA¹™˜ð$šÍðï‰ü;âí5uŸ jvÚµƒ³ žÖUš2Ë÷€e$dwæ¤fífjzÖ¢F³k7öö¾ì5Ä©;³`¹}Íi׿µï„¼;㯉߳ç…|Ya§¤ßx¦ï϶™wG(‹MžU ;È;÷âšBgض^2ð†¥ugc§k–7W:ŒF{h¢º‰ÞxW¬‘*±.ƒ,¹ôíÆð …¼Q­Øèâà‘Û.b·óêÌeÉçµ~%øCá·‚|iàÿxoJŽË[Óþ=Ëáø.·ššTw‹@I8‹j·ëêsôÇǃ>8ðÿíâŽZ¿Â«?¾Ö´ûKxméûGKXÂÊ!·™dÌ0I'$©ÜZœEsôÞ »[«d½µ™&·‘C¬ˆÁ‘”ó¸0àzÁÓüiàí\gJ×l/A›ìÿ¹ºŠOߟùgò±ùÿÙëí_™ß¾"øTý4ëÿ€pÏá_kº…¾•,;|‰´ûI¯^Fà3í`em¬U•° SLý¡~|øIñ_öp½øq¢[x~î_éörEmòý®Y%”g÷ ÍûÖóG(\ýYª—Ú…†—no5;˜­ R’gXÐ8³2O¸ŸüXøið¶;~#xšÃÃi©»Gjo§H΀TÜFHÈÎ=kåÿø(¤:‡ì·­ÙLG>£¤#wᯡùÔ¤6Ï­“Çž‘`xüE§:Ý\5¤$]ÂD— ÷¡O›æwQÈô­c\Ñ|=dúž¿¨[é–qý鮥Hb_«¹~uøñ_àÂÏ \þÕ—Z‡-lÏl<9y¡ÔªéÓÍ’Í$8V‘¡qêBÒ½‹â‡¾/þ×¾_ é>6°ð‡„¬5-'_½–ÖÄËzØšðGr ¥ÉòþuÀ qï|‚æ?X,õm+QÓ×WÓï`º±u.·H¯QÕƒ©*@õÍeÅã?O¨Ûik¶_ÞF%‚Ýn¢3Kþ4@Û™}À"¾øGðkâ·Â]ãTþ(Ò4Ÿ øCÄz\—z~‹¤ßKymgx¶ò¥ËF%<´˜mb£ÀàWÌšoÀß…?ürÏã—§Åoãm?G‹\·×ŠÞà £ËT—9 ˜„1ŒÒåAsö§TÕ´½ÊMKY¼†ÂÒ^k‰(×ýçrüM7IÖ4zÆ=OC¾ƒQ³—îMm*Í} ó¯ÎØZümý£~x â­·Û<=?….üE&™1ÿGºÕQ!P%ŒpþPv`†kkᇥü#ý³þ"ü1ømh,<+¨xV×]—L€âÚÛRI’1ÇÑ ÈìH£ÐRåÏ»5øG÷PXøƒ\±Ó.n°"Šêæ(^BxØϵxßÅÏŒ:ÏÃïŠ? ¼§ZÛÏaãíFúÒòi‹†;[S:´d2HÁÝÆ+åÙà—ÂoŽŸ 5Ÿ‹_´;_ø»ÅºÎ®šœúˆûD–Ëou$[G“û•X‘YBã¸ãl~Õ¿ ¼)¬üDý™¾\[¸ðÚj—¶ n²8/ek`Ÿ¸g9I#Ë~rÊÌSNÊöô?At_xwÄËqáÝR×UŠ1Hö³¤ê’ªÆ2@aèy¯#ÿ} º`dÇÛØ×ÚUñ'í‡ÿ#/Àoû(Z?þ†kíº§²¾hý•¿äžêö—ÿDA_LWͲ·ü“ÝCþÂ’ÿèˆ+å³ùá?ÃSÿl< wüŒpÞ•?öÓéz(¢¾œú)h¢€ (¢€–Š(¢Š(òàßü¦Gã·ý‰–ú#A¯×úüø7ÿ)‘øíÿbeŸþˆÐkõú€ (¢€–Š(¯ÉïÛ{à7íñ3ö øGñ à=šA7‚l/oSºeI{4ðÚÎÛs°BHÄŸ15úÃE~]x—ö•ýº¼eá‹¿ø+öo¾ð¿o¡û#뺭»iV2Iò=ÌN vrÈœÞckt û$k¿¿àž^,ýüŸð’ø¿SÒ/d¸06î©xCIå™J|  m%P_¤´P†þ̾×¼û:ü2ðŠlÛOÖ4OiVw–ÎT´7ZÆ’#%r¬8$WÀá¿Ú#öDý©~,|PðWÂÛ¯Šþø¹-­ñ“K¹Ž;û «1¼¦Ã1MóH8]¥v0`U’¿\h Å_|;ý²~>þÓ¿þ?|@ððŸ…ü5­ªÁ¡GwÕÖ—d­“^ÞÌ ÷ TEÜ«Aåþ®øÇðŸâˆÿoO€¿4M[¯ xSN×bÔïÕ£ÛIugq*ÊÌ—gP6©ëÍ}ùEx_í;á}Æÿ³ŸÄÏøV͵gZðî§ggl…U¦¸šÙÒ4ˆPYˆ$ øŸÅ?²'ÄŠ?°7Ã/…Öçþ‰ÿíôÍSMûDDв˜¤x÷¹€ai<_©”PåN½ûHþÞþ%ð ÏÃ#öt¿Ðþ"j¦Éµ¿í;q¤ÚÍ"ì{È›8Êäº!•‚œeŸk?µ¯ìùñëÅ¿°.•ðu..~$üF·¸Ó¥¿¸'™s*ÌÒÎÁå1‚‘nØ…°J($g5ú›EE+ +uUý@¯‚?a¿…þk_®|{¢Ë£ÅâŸꚦ˜Ò4mö›)œ˜æ]ŒØV7`ûWß”Pñ'Kñ¦µài_5”ð÷Š®l¦M.þH’híï6Ÿ%Þ9Ed߀À£|¹ÀÎ+óß´·íµàÚxâgìí«ø»Ç¶0­§_Y /Quâ9åxH ŒQß$jþŸÑ@™ÿcè_±þüF¸¶Ó¼OñMWQ¸ŠÜ‰-tÛ»äE‚%*YY"0ÆÏ³#%‚“€ÇÎþ |cý¸~ ü/Ѿë_³­÷Š-|MøMà¤ø›àSÆZ¶›ªè°^Çg¨Ú]Ùš;Ës7Èë"\ìtUfm‹Ðkú¯c_ÙãÅ¿³—…|y ø¿P±Ô&ñO‹õ/[5‹JʖבÀˆ’y±ÆD€ÄwܼŒ1æ€>Uñ‚¿i?Û§â?‚"ø³ðîO„ÿ¼¨Å­\Ùj7‰>¥¬ÞC‘f8‚˜ÐT†UÚ®ä;1U^þ áÿ‰ž%ý¶¿gÛƒ—vö~4¶Óõ]CKk²VÞIôå{Ï"R1òN°˜›$ ?$ šý§¯~&þÏ-ñ¯íyðƒöƒÓu t/‡ÖZµµõ´Í(»™¯í¦†3¬m¤·:ð3Ò€<Å´oíÛâß Ý|=ðoìãyáoê WÖnõ{Yt«”l{˜¤L vrÈ¢BTà‘&6·½üýš›öxý”.> i“ c]›NÔe½¸Œ·zôM¼¦ü| í •PXM}‹E~^üý’üGãø&Å—ìÁñbÆO x‚îßSù&*ígwý«=픯å3PÞ[²†å ^ ¬?|xý¹¾øNøQãÿÙêûâ¹áè“O³×4R!e¨An¢8e™Œr´lPÌáKžJ!ȯÕê(â‚Þý©õ/„uÚ;P‚_xÉ/dÒü9d!htXÝãŽÕn™ÉIv‚s=Zÿ‚}|7ñ¿ÂOÙ#Àßþ"éRhž!Ó¨nlådgŒO©\ÍLlËóFêÜüó_gQ@|1øQñ Eÿ‚‚ücø¹ªh²ÛøGÄ:“ia¨3GåÜO6«"*†Þ ˜ØT>”Ÿ·wˆÛà©ø¢Ë¬ÿÂ7ãÝ+TÔ|¦~Ïeï2fÞË•\óŒŸjûòŠøGöÛýþ"|V·ð7ů·0[üNøS¨¶¥¥Gtþ\°É´Ïhí Èb@7¤nBʰò}{öý»|] Iào~Íw¾ñô?gmcPÕ-ÛJ±•Ææ2QVP¼²©àã‰>ë~£Q@˜¶?À¾;ý„´ï„ÆâO‰_à¸Óþæ†ÜÜÉ¥åeÉM‘‚1˜(f‰¯Ó¨T¬H­Á üªZ(à?Øoá?Ä?†×ÇkŸè²èñø§Ç𦫦4}¦Êw&9—c6‡MØ>Õóoìr¿µ'ì™myû8ëßõhçÄ’ÍkâK+ûxì–ÆñãF™ÃHES(ƒvR¼þÇQ@| ÿ ø;ñâwÂox§àµ“ÞüCøyâ?\ÑÒ‚VhßË‘TÈUp7,¬ ãØýóE|‘û+þ즧 dÏ üñ•žëGI›ûz!™îµ`ò^Fî2¡”Å»$£WÇ_ðNÙ_ãÂ?‰~2ñgÇ )# i±øKÃÊÑ¿Ú4¤»šâIc͵ ìÎŒuÏëåðÆ?…üGûz|ø«¢h²ÝxSšv»©~­ŽÚK«;ˆáVƒ’îêÕ=y¯¢?iß ëþ7ý>&x;¶m¨k:ׇu;;;d*iæ¶tbbɽҊü½ñìã_‹_°Ä7ÿ¯ÄŸZišŽšóÉ´Zj¶*˲W‡~#ºî]ÛX«àíÁK?Ú§öíÓ|;oá-[ö`¼Ô|x‘y ¨Eª[¦‹,àm—rªóüñÆq"öýC¢€>SøS௚oìÛ«è¼@ž-ø¬YêrÈ-ãŠ8­ÜMåYDѤbA8ÞÝØ¨bЦ¹ßø'×Ãü$ý’< ðÿâ.•&‰â 0ê†æÒVFxüýJæhòcf_š7Và÷çšû:ŠøPøOñoø)›ñ’=Vðd>m-õ-Éå Ów+ù;woݵû¸÷¯¿(¢€ (¢€–Š(¯Èø-WüšÏ…¿ìs±ÿÓv£_¯Õùÿªÿ“Yð·ýŽv?únÔhõö–Š(£š(À ÿÓýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€>gð¿üœÿ‹ÿìþg^ÕãÛ__x/[±ð Ä~"¹´–+ ‹­ÞL7)T•¶«°Àc’1Ð׊ø_þNÅßö ‹ÿ@³¯¦+æ8[à¯ÿ_jéLùþø+×ÉÿéLñÙ÷á —ÀÏ„šëyVêêÆ#%õÈÉûMôç}Ĺ8bÉ O;@Ï5íQ_NÙôÍ¿þ ë¿þ"üEŸJ½·oøÂù5‹Þ'°Ô&P/.6yR°Ü¸?(  2~’¢Šmåß¾x#ãƒäðgŽ­¤’×ÍK‹yíä0ÝZ\ÅÌsÁ(ÉIôÈ ô ŒŠâ>þÏQ|3ñ4¾,¿ñÿмkzm^Îñ¢.á·†GGmˆ±Æ7±EËœ’jú"Š.>>×co \x“Wñ€üsâ߇‘x†vºÔl<=©‹[)î$ÿY0‰â“dŽ~ñSE5柵'À«mKá÷Á¿„:N«xƒÃö>*Ó-¯¤Vžæé,Hušâââ!¹1¸–”í yȯÐÊ)©1XùáwìµáO†þ8?uOxƒÇ^&†Ñ¬,ï|G|·²YZ¹ËÇXã _ø˜‚q0³Ïø÷ö7ðGŒ|_¯xÇAñg‰¼?‹UW\·Ð5µµÔˆR¥æâ“çem¬T€F~\³õ幘XòÝ+à¿Ã=#áB|µÐâo%“X :¼2d¹g'yvb\É»~ó¸Ü׊xö>Ð< ®h·–Ÿüe¨è^¸K?B½Õ„ºl-ÌHc+¼qñ±Y¸Ç%¹Ï×´Qv<§Á¿<5àˆ~9ø—¤ÜÝ˪|@{ /ããkxŽÅ«ºå\–Þ`q^E{û|1¾øIâƒRêzºè¾%×%×îfYíþÔ·RÊ“¹Aä…±Ÿ›<×ÖtQv<£ãÁ¿üqø}uðãÆ sŒïÑÜÙºÅwm=³‡Šh$tp®¤u*x$ƒ^}ñö]ð—Ä-cHñ¿ˆµß xËG²]=uýñm/îmS‘Ö#1J»‰o¸¤p@$WÓP›øKð_LøQ§jÖŸð‘k~.»×$Yo//ðÇ…®®Zê_麷•¥nvè‘´L韾¡ùûNŠ9˜¬yGÆ_ƒ~øåðî÷á§Œæ :í Ohè—PInáÑâyEVãi%Oýkgáwà ü"ð‹ðßÂK(Òt(™ÙZWä³<ŒªªY˜–$(<8®úŠ.; KE€)0)h ¥¢Šù‡ö”øAãÿŠ_ð‚êÿ µ3OÖ¼­Ç¬Åý¬“Ék#E*£,9<€ËÇq^¥«ø‰Ÿ À_lìus«Z$:¬VbX­nšß{c à4d¶õ äW¦QNâ±ò·Ã¯Ù[Nø{âM+\oˆÞ1ñ–Ÿìý/VÕ{2†5Ìkؤ„ÉÂŽ9§øÿöPð§‹üqñ#¾*ñÃßë)zLJ/Å¢j% q¤ŠÒ*ªë´ôÎHõ5s0±ùûûLü—Âÿ±¿‰¾x;ûsÆ·W—úuÄvòêzÛKª[I;±‰¶v¨  ž&½áÿìwà ø£Ã~3Õ¼Iâ_ &íLÖõµXé.é·0Gå«nEù»(ä€GØTSæacæ_вï…~$øÕ>$é^%×ü â£h,.5Þ‹9níCnX§ ’Øs°Œzçð¯à§€¾ø ¾øVÖIôÛ‡ž[É/_íÓ\ÿ¯–åÈÞNÀ\pëTR» i¿±‚´ ųð¿Žüc¢øIn…ØðÕ®¯·JR$yJJ!,2Êd$ÿz¾ƒá†íþ1ÜüoK‹³®ÜèÃChKÇöAl'Y÷„Ùæy› ù›qü9æ½JŠ˜XùÖÙ—ÀwŸ¬ÞÿR ñmÖMT‰aÌc1²þçäàÿËO3šì.¾ ø/Pø'À=Mg½ðÌZ,:éY É··`Ž]á —b¸`€‚Œb½jŠ.>Zñì“ðÿÄ~ ðw…®õ­vßVð " Äv׉o­ÛD]ž|q,l¥LˆÀãšé¾ü³øY®_x¢óÆž%ñ®±oöVŸ_Ô>Ö±C¼I¶ÕP“‚}W¿ÑEØXøûÄŸ±§ƒµk~*ðGj‹5Æu¦eI¾Mó˜<¿/q‘ÃfLw~nHí^Eacƾ| ð_ìóàþxK»›!s=ä·Î’]O<äny^8ãBBª Â•FrrO²ÑE&Æy_ů„šÆÃDׯïôÕÓ/c¿‚}:T†už%eRã“ÞOã¹/ |‹Â>"³ñ_|]ªµŸ˜E¶¡©¥Å¬ždmï#òFí»·/?xÎ1_@ÑXK MË­O^Ž{‹§CêП¹®–]wéÔó_†¿ ü7ð¿E½Ñôi./äÕ.¦¼½»¾d–æêyÌò²")ô(V/…¾øÂÞ×ü‚ãRð׈.&¸“N¼t’ <åã·Øˆép@,ÅXn1$û%Ö %mŒçœâ¥)ÉÔw“Mú­Ÿ“]-±ó_…ÿf}ÂÚ¾“{Œ¼M}¦hs$özUÖ¢$±‰ãÎÁåˆÔ•Lü£?RjOþÍñ?õ_Úø·ÄžÔµ•….WI¾ŠÖ'X" y Çg–<“ŒWÒVR¥n^]Ïõ£í=·µ|Ö·M¯~Ýõ< ¿gÝëáνðË\ñGˆ5½?ÄKqyÅÜB&G ´;UIAPõ5íÚ^Ÿ“¦ZiVÅš(c… X¬j@8à ¿EkNŒcð£ÏÅæuë®Z²º»6’oîKî<ûâÃâǃ®¼â9î-ìnÞíYPaH¸.’.2£?/Jï£AkôPϵ>е~n§<±58ÑoÝM´¼Ý¯÷Ù}Á\?ÄŸi_| ¬ü?Ö¬uÈ ¼ÓYH±\¢’cwWPÜu*k¸¢¨Àâ~xLøeà]À-ÕÍ펅l–°Ïzë%̈GEEfõ!GÒ¾wø·ðcâþµñ÷Ÿ>ꚬº•6•5¾µÔ«$w‘[2ÛN–À=Aõõî<›â¯Â¹¾*éZ~›ÿ Ÿˆ¼ö3š ß-„³’…vJÍ»£Èàæ¼—à¿ìáÿš¾Ÿ}áOˆ>2½Ó4ãpË£ßêpË¥H×*ÁÚKxí¢ƒ6ðA8æ¾³¢‹ŠÇÈ7þ2xö¡ðçÆ_êºðǃâÕmtË[n’ýíõ¶/ÚZVxšE!I)°c<1õõPØÂŠ(¤RÑE|›ñöCð¼w©|Fðï‹?ðìsàO ø·ÃÞ3Õf<ßâ·ÂŸ|hð=ïÃïZ½Ö•zc|Å#C4RÂÁã–)å]Xdv# ‚¤ƒ‡ð¿àÝ¿Ã]VÐo|]âÚêäoÿ„šùuŠ=… Qac`~e äײQJã±ñü0ׄ,c¸Ðü-ñÆÞð•ÄŽçÃúv³åiñ¬„–Š Ñ<‰r»É9ûÃŒ{wÅŸÙûáçÆO é~ñTwVÒèº^¥e9ƒQ°š0 Iøl1 VVÀ$d=ºŠ9˜¬|óð“öz²øW¯j>(¾ñ¯‰|oªj¢ËÍñ ø¼Û‡ó6D‚4-Äç=±Íyœÿ±'ƒìn¯áð¼càMSžK‰ôMViÁææO&7ŠFŒHs¸#²…ö•s0±ùÇûQ| OxÏöuøua§ë^Ñ.o¬îo¬Ìí=Œ0ÚÀ¶óËv€ùRŒ2ÈäaÎyôwÂÙŸÂ_ üWª|@›^ÖügâÍVÝlßVñÚÞ]Eh‡p‚"‰ªdx$ž„+èê)ói`±ñw‹¿aÿ‡^%×|C¨é>*ñ?…4ŸÎ÷:Ö‹£ê+o¦_Í.|×xZ'#ÍÉó°<ë_@ë_~kÿ ྣ¢DÞû$vIb¬Ê#†,yeáШ`û·n²O5êR» -|7ý–¬~ø›Lñ¿¼câ{m Y4ý7XÕâÊÈcƱ&òŠHLœ(ã½;À_¼3ðïUñ¶±¢\ÞM7595[ápñ²Ç<ˆ#+HЪ`p±Ïzõz(¸Xù`þÈ¿ ÏŸ üþÒÕ¿±¼!¬Ã®ZMçAö—¹†i'T•¼†2ÒB¢¶1†Ï'Ñ~4üð§Ç-MÑ øsá炵_Ï©êž-¶×§žãQ¸ñ×ÛînÚâ$‚A#•Q°ÆŠ»B×¹$û ]…Œ|;ûxCÂZ…„>ñ¼)¦ÞG{†×W'JFŠa:E°Æe0‡ìi '«kØ~>üðOíà1ðÿÇRÞZÙÇw ì76GÔÂCFòÇ*Èì”?+`àm¢Žf9Ïx[Fð?…txz/#LÐí!³¶CŒ¬P D àsÅxþŸðwVð÷í©|dðÝô0èÞ)Òc²×,7™-훲]D@Û‘hÜ1HéŠúŠ.3ã/‹¿üo§üCÕ~*|/ÒtïØø¾Â3Å~ÕÉ·Õ"·Ü ¹ŠV‰xWBñl·ö¿ßáý…¬«©%ͭך'd@’C4’•!Ô(+í*)ó ÇÀ¿ >xÖóöÎø•ûExëN}+O†Ò<>…ã&êk-Ëf!vBÁ;ÎWå¾ú¢ŠMÜ¿: ø?aãÏ‹ÿ Ÿá¾•ñêÇÁZ¤Zö­ã¸î-ãÓ¬,Fètë$ž8H`ˆÄm]ÍŽŸ²ôSR›?hŸÙoáÿí5m ZøóPÕ,ÓËqntÙ`ˆ»Ê0“Άl”c~µè?>xoã_§ø}â«‹»]:â{[†’ÉãI·ÚL³ $r. òôÎ<שQJã±ó‡‹?fxÄ|Qþ§ü-«k ]WÉ–òSOGŽ#kºØÄHwyž`'¦ø£û4ø/âuî…âÕµ ø£ÃvæÒË[ÐîþÇ~¶Ä`Ã#ld’6<•)ÁÎÒ7ýEb±òî—ð%>ü<ññ´ñ7ˆ¼q­x‡L$¸Öï ýÃà‘cŠD@£,pª¤’q“Å|»û<~Ä~ñÁ‡Ïñ Vñ]Œ¶vz†§á ›É-ô¶ÔÞuœ±‰SsÏåù@8¯ÔJ)ó0±á?gÏ|hDºÔï5kž•¥Òµ}ãì—öe×k¬nUÔ£ŒR§ØƒÍ7àÇì÷á‚׿µ§j:ŸˆüIâg‰õ=gZ¹W÷"DQ— б¦NÕ ß’x¯x¢•Çcã¿~Æ> ¼ñ6µâ?xÓÅcñ,ÆãT°ðö¤-l®flï—Êx¤Øïœ¤.:(ɯXÕ¾x;WÕ>j³]_Ç'¢ÇJU™eÝl-1tdGy0ƒ9VB[’OJöÊ(»*o„>o ñÔÜÝÿoG¢6€ ߨþÊ×ä¾Ï/ÌówŒgÌÛáÏ5ÇjõþÑÚoÆ__Á>‹á]"K- O@Åâ¼»lÝÝÊXmÜÈ4 NÎFH?CQEÇ`¢Š)ò?Ưƒß~)üTð©iªh6žð^·¦ëžKÅuý­,ö™SÌ `(Ë÷ÀsÕ«ëŠ(¦ØX+æÙ[þIî¡ÿaIôDô½| ðÃu? j7>ñ­®‰§ÿiL<‰tèî[w—Îö ýÒ£Ùï_!œÕp̰’Œ\´©¢µþÇv—ây’ÂB®c‡æ«Z3ß›_‡kEþ6>ù¥¯œ¿áý£ÿè¦Xÿàš/þ*—þÚ;þŠeþ ¢ÿâ«ØþÓ©ÿ@óÿÉ?ù#êÿ²éÐL?òþ@ú2ŠùÏþÚ;þŠeþ ¢ÿâ¨ÿ„öÿ¢™cÿ‚h¿øª?´êÐ<ÿòOþH?²éÐL?òþ@ú.–¾rÿ„öÿ¢™cÿ‚h¿øª_øAÿhïú)–?ø&‹ÿŠ£ûN§ýÏÿ$ÿäƒû.—ýÃÿ'ÿä£(¯œÿáý£¿è¦Xÿàš/þ*øAÿhÿú)–?ø&‹ÿŠ£ûN§ýÏÿ$ÿäƒû.—ýÃÿ'ÿäŸ<'ûjÜèÞ!¾ðçĽ7íV–×SCý’…™Qªù°’ýÙ à¸Æ¾ãð޼!ãý0kÕ`ÕmxÜbož2z #l)Õ­¼Cðô\hq!ÀÕ^G¶„.~`¬>iAÆ ¨`z7ù¦EÆXÕWØJ¢ÿÉ—Ïüþóõn!àl£õˆMRvÿ·_˧Ëî:¯ƒò™Žßö&Yÿè¿_«ñ‹ös±×´ßø+—Æ‹êk¬ê°ø&Ì\]¬+n&'B9)*¸uÆp3Šý¯×á&â›Vò?©4üûýâRÑEQEPRÑEWÁž=ÿ‚ˆü ðˆ¼ià«ý;]¿ñ7ƒµK]t»;8§ºÕ.îã’Uûk1g3æ<‚üDÓ5†Ú%²\Iymâ+skyj°LÐ2¹‰•€ò•AgÜ¡A$ ú–¿3üóáâégÆð£âðoaì›¶ù¾g(ÿ{;»mÏ÷—‡~)ü<ñ_Ø>.h:ý­Ïƒ§³“Pžý–ëkc,Ž_iO/k P© PE~g_ÿÁP>½½ç‰<ðÏǾ,ðV,‘\x“NÑ7ixˆÎ’É*|£Ÿ3Ë#¸¯­ôÚgàŸˆ¾Ú*ÏİÃàHíÚy¯¦ †å´RE‚âa&Ffb†Ü¹÷šÏÕµK-J½Öµ1ÚiðÉq3X¬q)w “€šüß?ðSχ‹¥Ÿ?ˆcÀ=¼Mý„?³ nÛæùžv<£ýìîí·`U€ Š_„Ÿ< ñÃáþ—ñ?á½ój>Ö<ñm;Ã%»9·™íäÌrª¸Ä‘°äsŒŽ¯G¯<'ûn~Ï^ø_ð‹Å^ð¶§á¿üTÕn´m Caekk§]GxÖíöĆãd),¾dŠbó2¡Ù‚œŠûî€8¿ˆž?ð¿ÂÏkükrÖzƒ\Ý̱¼¬‘)‘†vëÐkCÁ¾-м}áÇ>œÝhÞ!²·Ôl¦dhÌ–×q¬±1G—r08`èFkä´ý±¾üAø_ñ{Æ“ø{SÖ¼ð¾æ};V{‹;Y­uG€Ÿ5l‘ç+2±¿|"ù]98õ;ŸŽß|û2Ù~ЖºMݧ‚-ü?e¬[éö¶ð¥Ô6Ãà @²,*ÈŽ«°I±q€Øô-ùÛ©ÿÁH~ÞH‘ü)ðŒþ'ˆ--®õ<7¤ý² 7í1, ™VM‚tVùÕK*°*_ ãé/ÙÓö—ø]ûPø*_|2¸¸ÙNmol¯¢^Ù\Ëš0μ©2;)äÈ`=þ–¾ø›ÿø[àŸˆú‡ÂxcÄß<]£ä_ÙøONþÐ[7VÚñÌûׇ‡Ø¬¾V!#Gÿ‚’|Õ5/ ø^ïKñ“âßkö^“@¿°Ž×TÓî¯ÈÍw Ó&-É#/‘‡Lè%WƒþÐ_´gÃÙ«ÁÐxÃâ$·2Bå,¬,4ø~Ó}tà•†Þ-Ê `rY•G¶HÝékàÿÁ@¼ ¨xïAø{ñ?À0øU¨x®ámt‰üO¥}’Òòi05”;aÙ™Wm€,3^©ûBþ× ¿g+íÃ^%Qñ‹|B3¦øBµûv©r™+½bÜP° `X†Ø«`êJ+áo†_·ÏÃOüDÓ>xïž'øUâÝlìëOi§O[çfÚ±Á&ö˱áC*†o•IbðOø)/ícâO„ÚnðïÁzW‹4NMgG¸›^Óíš:{RÒI%Œ‰*³ÜÈ#ô]Ùn¢€?XékçO€Ÿ´eÇÖÖÖËÀ>/ðGö¶,|S¥ 0\ý§ÌÀ¶"Y|×ûΛw/\ñô]yGÂ_Ÿ~8iºæ¯ðÛQ}J×ú­Æ‹zï¶û/­UTeBÀ æ©Ï­z½~þÅÿµo>xCâ_„.´}kÆ~2×þ#ø‚âÇÃþ²7úŒ–ñÃh­;F „ˆ0#q9$ íl~Š|ý¶þüxñÎ¥ð¡ôMwÀž<Ò ûLº‰,¾Åtñ h€wÎÐÊJ¶Ç*w* dÒ×È_ÿl߇|g¦|0þÄ×|uã­^µA¡xjÇí׋$ $]È­€ 6J…Á9n/‡_>#¿ÁkÃ^"øuãÑn×Pé&°û×Q .Í Û¶¢–Ã%A+¸+ö¥ò'Ç?Û'À?|s§|(²ðö½ãÿjvßlMÃV_mºŠÛ%Ÿ.í=70b¡H&o€¶/ï,Õ~G£k~ ñÖ‰n.îôÙÑ*¦D]ÌAtêCaÛŽ@Ö”´WÈ_ÿlï†ÿ|g¦|0þÄ×|uã­^µA¡xjÇí׋$ $]È­€ 6J…Á ^דø‹ãgß üUð¯ÁmsQx<[ãHn§ÒíDºMœo,ÄʪcM¨Œ@vÆ$Wüý¸¾|`øŽÿu¯ x‹áÏE»\ŤxšÃìS\Ä€³43nÚŠ[ •®à¤øûÿ)5ý™?ìâOý º ÓêZøÏâïíÕðCàüGðßÇk©Ã«xwH·Õÿso©z.æH!µ´a$—,ò”¢ PÌ\5è_ÿh7âïÃÝoâN¹áMoá¾™¡M2̾)¶|†Úᮆæ#Èü¾p ·8 ¢¨¯Í™?à¦u·k^øaãÿx;KyçÄZF†eÓ@‹ïº;ʇjõbá‘_e| øçðÇã§Ã«Šu˜ï´ C‰d“÷/k,JX®ðcxÁã0%H$Öékófoø)€ui5-Gá‡ÂßüBðÆ+Åu®èº)›Ný×.Ñ»H¤€9ùÄgœkì/>~Ñß-~%ü0¾{½*âG‚Dš?*âÚæ0 ONÙ0<¤ÊÅH$Ù+Ê>$üløsð—Wð~…ã­Eìo|wªÃ£i°K7Ÿ{;*"XF uœªŒõë_7üOý¾~øâ%ÿÂOxgÄ¿ü_¤ø˜YxONþÐ.­µÒwÞ¸u<0PÛ[åb­>ý£jo†¿´7ÅïÙ«Hð;© x“Ã_4a©èZõ›iú¥¢Í=¹ÞÌ ·L«nÆW ºµóïÂßÚKÀŸþ'|BøC£Ùê:_‰>]Go¨C¨ÅBá%.#¸µ1Ë!xX(`ÌáЕ«[ö‚øùàOÙ«áÿÅoˆbæ].ÆX Y"Iuq5Ä!I$‰€%Î\aUll¢²ÿ±ÎÇÿMÚ~¿×äü«þMgÂßö9Øÿé»Q ×ú(¢€ ? J_€?ÿÔýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Šù£Âÿòsþ.ÿ°\_ú}/_4x_þNÅÿö ‹ÿ@³¯¥ëæ8[øuÿëíOý)Ÿ?Ãßoúù?ý)œĉž øKái|eãÍ@iÚ\RÃý#¼×#4‰==ªÒ|UðLl>Ý^µ·‰õ==µK[Ya‘<ëdb®QÊì.¤ÉÀ ‘ŠùGÇÚ ö¶Ð¾ßx;àÒE¯jã¬wåÈÅ…»v&ÌÄz¤aÅu¶o„õx|#¡|wð]¹›Å_ /†±§ß¸Ó› ¨Z’?†H€lz íšú›ýϲ©k›ðwŠô_xSGñ§‡':^¹i å´ƒø¢®Gcƒ‚ ðy®’¤g‡üYý¢¾üÔ´â§5Ég—O*Û• G#nõëZŸ ¾:ü*øÕìß µøui4ÆT»ƒkÅqn[8ó!+®ppHëÅ|qûUø®óÁ_µ§ÀÏXxzûÅ3[iÞ!OÓcY.¥Ü¶êJ+ÜäóÒ©þÌÚÛ|_ý©¾$|vJo¦“¤[x~ëÃ÷ŠaÕštq?Úï (»2£brIž‚¯—Bo©ú]K_‰ö_´‡~/iÚÿÄ¡ñÅž»77cÚFƒáÙot¨à·b¶æêq‚妯\ÂgŽ˜çñ#ãÇÇoþÏ_¼[áiåðGümâ+ú9­v¢ÌóKi#Io:î³§šŒ”Ç$u9s§µóŸŠi_ ø3â>•ðÛÄ>×mgÖõ´»-E¬±§Ou2UIËóò‚OËØúW…éÅŸ€?´w€¾x³Ç×¾?ð·Äë{øwj‘B—:•„&2…WJ0ž_A¸ŒÝ?í…ÿ#?Àû(V?úGwJÃlûb–¾`ý®õo‰žø3}ñáV£%ž«àÛ‹}^âUd½Óí\5Ôb˱!Tàæ¼ßâGÆ|Gø…ðKá×Ábm:…ñF­uC$znòŸr¸_=ÛË$a–EQ ¡D.}ñËã‘ð?À6Ô¬¥Õ®{{+>Ý‚Ï}{u Š#' 31êGõ{9.&´†[¸…¼îŠÒFxG#æPØÁã8ô¯…ÿj)›Rý¤f¯ ^°]Ƶ«jN®2¯s§ÛF-×ëûç#é_s_ÛÍwcqio;ZË4n‰2^6a€êFTò21I­áô‰~Öþ#x‡á}¤7K«øjÚÒêåä–Ù£½Æ"˜ðì0wÒ½¾ýŸõO‰'í+ñ?áŒ9>PWøpyëT?dÿx‹Ç?³§<[âËçÔµ}OOÜÜH<¯æ8É zÐ vÐW>‡¢Š) Jù¿ãwÇ­oá_‹¼à_ x2ok~5ûwÙ Šö+ ŸaDwÜó)^Uøät¯¤k⎟òvŸ³Ï×ÄúK4&}Ÿg,óÙÁ=Ô?fšHÕž"Á¼·#,»‡iã#­Y¢ŠC¡¹¹‚ÎÚ[˧Ã3»Šª2Iú_“¾ñÇïi¿ õ™¾)êP‰Z¦­¤O·¶"ÒÚÅæ`ðüƒ÷Ì `Œ¨` +ß¼ Ä?‰¿uÍ?Xñæ¥e©ø?V×´¹¯í’;P†ÁÞ$‰‡ rWÜõ¯¬Æp«¡­Jѵìíͦ²nñkþãPÎ=¢÷`öºÛ²}û3ìï x—AñŽƒeâ ^ǨéZŒb[{ˆ‰Ù"Œ3Ž+v¾ý|âK…ñŒ¾7Ôî4ÉtíË¢º[ýŠ0û”*°ÍÂF_¯Zë|ñ+\ðWÅoŠ>ø›¬=Õ†‘ü$ÚUÌáf"“4yP2¶î¥rrp2O5ÇŒÉkÖ¥B|Üž·ø¹{+µ¥ÿzûÓ„êFÜßv×>²¥¯Îm_â_Æ;?ØçÄ?æÖ§µñ½¨Ã}¥«,cì67:„0Án£f6˜‰É`IÜNzc¥Ö®¾1|Ö¾xÏÄ>>ºñe‡‹µ[-'ZÓîà†;x¤ÔØå´òÔÄn@Æ~aÉ­× OUí#ÍÍ(¥®®)7m-ÖÊýLÿµ££äv²wÓDÛK©÷¥ùËñ_ã^»¯ünñÃøIµÏ xoÂ1[Æïáí1ï®ï/.#$ÉžTq«qÏ^Þßû+|Gñ¯ô_hž57·ÒxsPò,µKëÓåÔ,eRÑHñ:¨.ÖWÚ= ääãŠáºôpË6¶NÚí-µµžêé=/ëk£šS_d—uÓ¦þgÕT´Q_†¢¾3ñ?ÅŒß,VûâŒú7Œ´5ánáž-/Tp:ÿ£HDS>9Û¢šú{Àž2Ó~!xGKñžÐYê± ¢K„òå I2‚Gn9äsE,Tg' ŸfVa×ÃÒŽ"êTÛ²”]Ó}º4üšLÖñ»¥ø[AÔ|M­Íö};I¶–îæLÙ ]Û 8PN5Ëxâ—¾$xIüsàÝIu 3*¼¡YJ˜F\20 ã+ãÿü¯ˆö/jŸúK%|û9\·ÁË B¸fO |^ðÛÝÚ3’rÒY£ôóâÚG«…­}~Y’SÄ`§Zï=F’»ù¥wò>;˜J•xÂÞëZù;Ù~:|ϺuÚ?á—ðóHø§w¬?ü#zìÿf³¸Ky¥›çDj…Çú¶ê½¾•›á¯Ú‡àÿ‹ueÑ4KûÉ.Þ)æM:î%ÙoK!Üñ*ðˆHÉ<H¯Ï˜¤ÔáýþM¢ÛÇw¨'‹mÞ_ËŽID×{ßj–À'µ~†x#Zøý«kéañÁ&¡Ë¢[‹=Qî¦ ·åQ@€†<›^–eápð›Õµ)­gð»-¼¼ìsasÕd–ÚEü-î»ßCÕ¼ã?üBð¶Ÿã? Ý}³IÕÉo.ÖMÊ©;X‚9çZïíð›Ãpø¦ãWÖ(üsmiª0‚fÏw‘ ªw“´çnqßùËàߌŸ>x?ÀþðEï•kàÍCVÀb†W“KµÖ!°A™Š Ò¾â…[çŠ_B·ÿ³‡Æ?@¢Câ?ˆ/$ žÞ ˆ’>}7ïJë§Áp{U“ä””cg¯Çmtß—Uo'äsË=“§î/y+¾ß ô×¾‡ßžýª~ x£]Óü9£ê²_jsÇo¾›y™$;Tx‚¨ÉêH­øËöø9à?_x7Äš¬ñjºh®cŠÊæqʡԳÅ(Nsš£á/~Òw~"Óí¼]à-LѤ“76Ú³Ï4QàüÉ·PÇ8ãp¯œ­¾%‡µ—Źÿá×ÝðÇŇÞ1ð|¾>ðÞ½ky @²4×jûc‡Ê¤nÁB ä†Á¸¯9ðoíGðSÇž$µð¶®?ÛµÂÈÜÚÏk áN¢ÞY‘RSì¤ç æ¿?üqá?ø'ökøÍãmwF“Â|A×mîmtv*%´´yË Nɸ)^ È⾕ý´tM3ŸB]$rÈé×s¸Ï£‘^‡û`Û[Eû7øþâ(‘%šÎî`·íÉœdã=3^lr¼4jÐÂÍIÎ|»«.{;ZÏ£Þûô:ž2«…J±µ£Í¥¿–ýoßËc×¼Iñ?Á~ðøŸ®ßmgûG–ì|»ÆD„ìP_æicgœWsmqݼWP6èæEt=2¬2å_”ŸüAñîëöG‹Mñ/„t‹ ÿgèKöø57šëÊYíL ös ÎÁ ÿ('“ŽR¼;ÿ 3þ½aÿÐsf¹4pÔ#SšíÊKFš²Q¶ÝuwùhmƒÇ:µmd’{5«¿~šQE|ñé KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ _4~ÊßòOuû Kÿ¢ ¯¦+æÙ[þIî¡ÿaIôDóüpŸá©ÿ¶?Žÿ‘ŽÒ§þÚ}/EWÓŸ@%-PEPRÑE|Çà¯ÙOᯆu›Ÿk±jwN Úow,ÁÊœg«–õ¯¦Q4XãP¨ ÀtzSè®<_CZQ_ÖýÎìve_.zór~}=Ëä~@|ÿ”Èüvÿ±2ÏÿDh5úÿ_?ÿå2?¿ìL³ÿÑ ~¿WaÂQE%-PEPäGì­à]Xÿ‚þÓ>>¿¶Iµ µ…­›¸Âu(ó+¦z1[}»‡;Y‡F5sþ Ù¨k3x á7‚¬´Ù5«xÎÊ;­:9–ßíæ$a¡™¸O8¹›€@c÷kÛ¿fŸ„ÿüûZþÒ^?ñF‹.Ÿáÿ]è²inÑ”»[h§” ņÒÀÀu¯Cýµg Gö™ø4|)á}E4höúÖƒy#²Gý®à¡Ù`®ŽëÖ*Ø;q@Hÿ´OíY.’Ú Ÿ² ëi¶6Çĺ_’`+°Ååù{vmùvãã¥~sø¯ÂŸgïø&oÅoøÿמUñt#Lµ’î+¦·Ò/å·v„I¸Ù¾'FÎ7‘†5÷UŸíQûvé¾·ð–­û0^j>Âøsàïøá߇| áËxâÑôm:ÚÎÞ4!Š(‚ﻩ'’I'’kòþ +ð³Á¿>|,ø7ðwÂ>O…üUãÛ{«ÝÚêHÓP¹1ì[:w(OÀìR¡±Åzçƒ?hÛ³á‡ÃÛ…>'ý¯|Yã ÕtëMnÏS€éwÞBùPÝNy)• Î ©s“û¬ánxö9øûñö7³ð7Åß5÷Æ[]|Q¦ßÏ9xtëèþX¬ÄÑ)&5ˆ°,¡‚ÊÛ—r¨Èvÿ´OíY.’Ú Ÿ² ëi¶6Çĺ_’`+°Ååù{vmùvãã¥pŸ±7ËŸÿe?ŠÞ ø«á‹ Ä/u»ÝÆæê³Ÿud¤D¯ ¸Ú’+“œe™›ÕÛ?Ú£öíÓ|;oá-[ö`¼Ô|x±y ¨Eª[¦‹,àm—rªóüáÆx£§Òÿ <ñóNý›u}㷈ſu›MNY¼qG±»¼«(ž5ŒH#'Û» QTÐç‚oñßþï£ø[N„Ë®éQëΓ´~ð_iúµìª±ÿ·,~d#þºWÑZ×í¦§þ ¸Ÿ´T€x«PÑ×Giýçü$M‹º¯\¬®BÿÏ1š÷ø'×Ã|%ý‘ü àˆºTš'ˆ4ÆÕ Íœ¬Œñ‰õ+™£É™~hÝX`ô5ù÷¡~Äßm¿j¸>Ýè쿳¦ã9üym6èÌpöÈÐÚ÷ïÛ€@T w¶NyöíàxýŸà”¾'ø}w“¬·†¦Ô5lŒ9ÔoYf™X÷1dB÷PWKãïùD…¯ý“=ÿHmëêÏÛ Áž'ø‡û1üFðO‚ìT×5&X--c*¯,¬W •\œw"¼[Æ_ >!ê?ðMÛƒvZ,²øÍ<¦iGM žh½‚Òä‡v훕”ŒîÇhÑÿa?è^ý‘~éú²[®§ Ùj× ·Z”Ku+¹êÌZLdôÀ¾FýŸ%ÿmÛ"×÷†×LÖD¸ vö’Ý1 Ó-$îO©5ú û7øk\ðgìõðÇÂ'´k cDðÎeynåY¡¸·³Š9c%IRUÔƒ‚Gù£à—ÂxöÙý¡¾"ø›B’ßÂ~1µÑ"Óo$hÚ+ϳڤs*¨bÃiÊ(†ÿ‚LxgK³ý”­ü}Åψ£¡Í§jÁ¦ý ŒÛÌ’‡fªîÂ,Lò̃Âhihÿˆ¿´çìÏñ3ão‡íüc?ô»M‘Ý-ýä6ðÞZÏ{}wm‡c€Ê@>uµý³üâ/ø_áwíµð>ïáö±uÒnõ»Hu]ûE~D– ©#7Ëa]Ânù¤“óׇ>!üXÒ࢟üqàï„—µ+ "ÝcÔí´æÒ¬ŒHO´«ˆ³”Á6~ù¯MøôŸ´íÙ¦xgàÔ_u?†~ƒZ´Ôõ}{Ä0‡·†×r•´…0ï# ÖSÏCµIuôό߾?üý¨o?k?ٻñxïNñ…ŒŠü2n–Öæf·UXî­žO“!"ŒpƒoùHÅ@<ö¿Úûö£ø{¥øgNýš5 x‡Ãú­¶«¦kÄ:mĶ“CÛùMóƒÙÀܪØ%E{?ü&k» ./áû=ÌŸ|>ÒÅvHm¯ .A àäpjý×ÅÛ»ö‰ñ.áo|9ºø á«Kønuk÷w²[DrÖöö |¨êÜd\îôø(ÏÁ‰_¾éÖ ,WUñ/„üAaâ,‹¢5È´I£dC!U,Ûö–‚2Ä÷ÅóGìûñ›âÏÅ“©ÂËøA©|/[`hžþò+‘u4…„©¢#…î`7nè1_KÐäü«ÁZ½¿Ç/ˆ«l‡YÔ,|QðWÂÛ¯Šþø¹-­ñ“J¹Ž;û «1ŒM†b›æp6•ØÁVJò/ŒÞ5ø÷âoÛö\ñ'Æ?…ß .×]»³´’jÛXšòͺˆÝW`ˆ?Ëœƒæ6;çèÿ¿ò“_Ù“þÁ^$ÿÒ ªñOü;ý²~>þÓ¿þ?|@ð🅼5­ªÁ¡GwÕÖ—d­“^Þ͔¹V0 ƒËý]ñá?Ä?~ÞŸ~*èº,·^ð®®Ã©ß«F#¶’êÎâ8U•˜9ÞΠmSךðMSÀºŒÿà°×Úݲ]Â/à˜µ{uQsýš7Á,žye=˜^¹ÿXñ± þž,Hwˆj—zm•˧[ÉrŒàžÁÊ>¡ˆèk{JøOñø)³ñ–mUð]Ï€×KRÝ”×¢ê0íÝ¿vÕ'î㎵ôOí+ðKLý¢¾x¯àþ§?Øÿ·­€·¸Á" ¸f¶”ÉU•°yr;ÐÇ >9þÔø{á¿øOöH¿‹GÑtûk[Q‰ôÅSQ… þ¯«}âO$’O&¸ŸÙž5ø=«þÒ=ý ¾ÿ´øYⲚàÓo®íu ;t+sý¡KrÁĈê ùCr…@­O|wý¹~øNøQãÿÙêûâ¹áè—O³×4R!e¨An¢8e™Œr´lP ÌáKJ!È®úÃàíSñ»öcø±áOÚ+^´±ñGÄ…’ME€FÖšG‡‚Ù®!œ;ª‰>yB¨È%™Áä~~Ô=7ŸÂ…žý©`mKÄŸ <#ðÆçãÂ[ýF}G@¸ÒïR=CN-Í´ñJ˜ªI qgw^âÇÂÿÛSö†øõðWãŸ|žðçƒ<[¤›_E}íÝ‚]Csy©^Ì®±å¼¤@ˆ7¸(Ý ¯~ÑqÿÃ7~Þ ¿i+oôo |OŒø3ÄŽ>XÅÃmsJÝqÏd·n™©j8¿á¤?mO„_²ôé>ð0ox¡ÍòNÛ8%ü!ܹÏjúCöøø]¤|Xý”<¤jsÅe>‹c&·eu+X.´Å7wžz«D[²¹¯œ?à—žñŒ<%ãÚÓâ“ý¯Å߯R8çe#þ%ºRýš=€ä¨yÁÖ49ÿ±ÎÇÿMÚ~¾ÒÑEsEÿÕýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€>gð¿üœÿ‹ÿìþg^ÕãïÝø;ÁZߊl4»jïL´–x,mcigº™Tùq" f˶qÀ9<^+áù9ÿØ./ýξ˜¯˜áo‚¿ý}©ÿ¥3çø{à­ÿ_'ÿ¥3åŸÙᎿà…‡^ñâ·ü&þ:»›ÄëH¥d[«Ó½ae<¯“ÔÙü'*8¾žºµ¶¾µšÊò%žÞá9#p]a•à‚«WÔ6}GƲŸ†¼cð‡Xñ×ÀkO¼o øsPkÿ êrFæÚ]/Q&o²‰ˆÚd·‘™Yz’Xà.Úû2Š(nàš~!|ñG‹?hφtÛ›8ôoZjð^Å3È·R=úƱQcd` ÛqƬÿüñm¿í/áÿŽÿnlmìï´ù4ÙÝI,Oyh¿5´ÐùqH¯<-Óy@TmÝÏSQG0¬| á_„µOÀ(µ?ü ŸÂž ðEÍõÍæšºô—¶×šoÚä2¼ ötu–$f;[;Ž~èžCöÃÐþ%ßü;ø# ëZÕœ>;›ÆZBJÖ6qjfŽT…ˆc>ß”à9ëŠý)®w_ð…¼Túl¾%Ò­µGÑ²71,¦Þê/õsE¸².xaȦ¥¨Xù'Â~þÕíô_x;Zµ×t¹¯žÕî-–HÌS„‚:HÀ•ƒƒƒÒ¾¢—0XñÏ ßøæ×À:„Ÿ´£øjÆæI&ŠS¥Ï9ÓZÉãP|±¶ö;à mÛŒw¯ˆ?àœŸ ­´Õñ¿Å(o¦Õôy/§ðï…®gäÂáÝLyè’ÈÀŸïlƒÀýñ—¼ñDo øïD³ñ”î’µ­ô)q xÎUŠ8#*zVÖ‘¤iZ›m£hvqXXY ŽxcŠ4Q@})óh>Wý®¼ âgþø¡ðÿM“XñWÃ-jßYµ²…KMyoþªîÚ09,ñ1*?¼¢¾×®üQwàû»ïÃl5é­ –jÞt6Âv\Æ·"52¢ƒÃ…RÞ3]M®;œþøQûnxwã'Š~2$ä¿ñ}®Ÿew½Õü¨¢ÓÁUh¿ÑCn`Ç;˜Œã«ÿðPüJÑ<àOü&Õ.´¿ø×]†ÆÐØÊcfÄmæe±‘f]ÇO×èMSŸO°º¸‚îæÚ9gµ$Ã#¢³ÆX`”b2¹SæÔV!Ñínìt‹+ûƒwuoQË1àË"(V~¼FkJŠ*FxÇÇÏxGÁ¿ üeqâm^ÛOƉ¨2G4Ȓɘ@†vf¨’ÄÉ®'ö0°»Ó¿eŸ†¶·Ñ42ÿdÆûX`í‘™Ðý °#Ø×¨øÏàÏÂ_ˆÚ½–¿ãßé^!Ô´Ôòí®oì⸖Ý»j4ŠJŒóÞ½"a·‰ 8¢PªŠª¨Àt籠~r|ñÄÿ‰_·Å;­?]¼“áǃiÍfòh/ü¸áÛtݾ9d$t~ð¯Ñú§i§ØX´VæáÌ’ÑS{ž¬Ø,}O5r†Á ¯…¿j|Qá¯ÿ¾&é ×|a¥øcûoíÑh6_m¸íPE_)xÐn9ûÎ8é_tÑB`ÑVÊäÞÙ[Þd·3Ʋyr®ÙpÎ×\œ0ÎÏZµE†|QàÙÇÇ^Ð~izî›$¿µ_PÔ RÌË$Wÿjò…¹hT³9w ã>Áðá^±àŸ xÃþ$žÞ_øI5íkR­YÜ-¶¥3ð.‹âKÿé:¦»ªÝ_En‹s>qò¸ç©í]˜. tño5ïÙì’M»êþû»/¸Ã–)Qö~íÖý—cæïÛ+F³Ó?eMwúB‹;K_ì{[uQ‘i}mþèö¬‹_„Ÿ¾"kž·ø»¨èKáoÝÛêhú_Ú Þ§sj¿èÍ2J¡" pÎÍ20G×¾!ðÞâÝ"mÅt®›pQ¤·¹e‰ÌN$BQk¨aè@5²ˆ‘¢¢ª @AYáóéÒÃ*0K›šNí]®e§žðê]\¹N«œž–JÞ½~óåOü-ø¹áŠzßÅo‚7ZE×ü%pÛ¦¯¥k&h£i­ËŽx%X‡)…*À3’xÇðÊ/‹+¥ÞÜ|^›ImNâã}¼:8”ÛÛÛìP¼à;±pÌN1ÎJôª+‡™Î­5NqM¤•í­–ÊþKNöêtSÂFr‹~—Ó]Š(¯4ê¾gòw-ÿb‚ÿéi¯¦kÿ„OÃßð”ÿÂmö5þÛ6¿bûN[wÙÃù›1¸ÝÎqšÆµ7.[tw=L¯×™|Pq^®ÇEYö›q¬è—ÚM¥ôºd×¼Iu<ØÔ"nn^£#­kÑZµ}6q’’ÝáÙÃá…µ¯ÞØÉâ]x[RÖekû’ÃA—*¸<ª1Ú½âŠ*)QŒ ¬uã³ø™sâ&äüÞÞ—’8_Š¿ñ¯Ã_ø;I’(¯uÍ*öÆœ²Ä²ÜBñ¡rªÌ!IÇ@zWΞ!ýšµŸ~Ì›Û[oxf y´ëø¤“ìðêÄ•a'–$°%ùy·$ ûŠõðyµjŒi;ZJKÕ+}ÞG_N£nkuo‘ð ×ìÇñZÓö|øqðßB¾ÑO‰ü­Å«<—\‹ 2O"ª²Àe<ȹ¿=3îÞ µoü%âø4xwsý¯û2]A¯6ùm³ÊD±çÌÛÄ|¹Ç8¯¢(®¬NV´e±‹»“»Z§'wo™•,¶iÁµk-ûl|? ~Ê–¿ñ»RÕní$·ø‘ ¶úPå/kþd²™s„-3+|…þîzñY·²ßŽ¿á‘4ï€Öúiñ5½ÊÝÏs,³}ä7røBe<0Q˜ÇOƾò¢µ|SŒn2rÙÆ[uŠå_†ýÈYE 4–鯓wgÌšý±¹¦ÿÂNžþÇ0ý·ì“jFçìÛǛ䇄!“fvn!wc i6qkkc¤[yÓÉ"ÚïÓæ·’S½cgÁ6ßÝä’28òïÛ£Ç>,øaû7ÛÚx+P¹±ñ^«}¦iZ{X9Yä¸v’#ŒÊ¤Œšûª©Ýéöæ}mÁ·q,~b+ì‘z2ä0ìG5ÅWRtÕ9l¤åó•¯ù#¢hÆ\Ë{%òWÿ3ŽøY¤x§@øiá]Ç7§Rñ†—g¥r[yšñ!Q;î?{/ž{õ®öŠ+ˆÜ(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š+æÙ[þIî¡ÿaIôDô½|Ñû+É=Ô?ì)/þˆ‚¾c1ÿ‘®ü5?öÃçñßò1ÃzTÿÛO¥©h¢¾œú¢Š()h¢€ (¢€–Š(òàßü¦Gã·ý‰–ú#A¯×êü€ø7ÿ)‘øíÿbeŸþˆÐkõþ€–Š(¢Š()h¢€ (¢€–¼ŸÄ_>øW⯅~ kz‹ÁâßCuq¥Û%tš;8ÞY‰•TÆ›Q€ì3Œq^±@WÏÿj€¿³zéëñ‡Åqh—:¨fµµXg»¹™Tà°†Ú9&xÞÀ.xÎx  ékçö¦ø%â?ü=ð®®Iq¨|R±ŸRðúýŽåîÚÚ7–GfxÔDBFÇm'‘_CPEPRÑ^QðãgߎzF±®ü4ÔŸS²Ðu[íÞ mö^Ú¬o*™P°E;€*sÁàЫÑEùweûþÓÿü]âkÿÙâ^‘aá_É©ËáïÚI4wS}ö‚hVG#ÂeB«ï*½áOìñ*÷ã&ûBþÕ><‡Ç¾-ðä2A éö¢ÓHÒüåÄ’¢IO8b Ž–!6}Qâ/|)ñSŸõ½EáñoaºŸKµJë4vq¼³•TƘDb0'«Õ袊()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¯(ø—ñ³áÏÂ-KÂ:G5'±ºñΫ‹¤¢A,Þ}õà ‰X %‡Ìä(ÏZõz(¢€–Š(¢¾{øçûT|ý›ÓO_Œ>*‹D¹ÕC5¥ªÃ=ÕÌʧÄ6ÑÈá3ÆösÆsÅz·€¼uቾ Ñþ x*ðßèZõº]YÎc’$/÷[Ë•U×èÊ |1ûGþÌ?´·í)ãKïx‡âVŸáÿ——6³K¥éÖÍý±qÆ^ ¥(«L¬Ê|ÆQ•%¨÷·…|/ x#Ã:Wƒü+dšv¢[EggoÜŠ" Ï<(žORs[ôPEPRÑEQE%~@ÿÁj¿äÖ|-ÿcþ›µý¯Èø-WüšÏ…¿ìs±ÿÓv£@¯ôQE~”¿…ÿÖýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Šù£Âÿòsþ.ÿ°\_ú}/_4x_þNÅÿö ‹ÿ@³¯¥ëæ8[øuÿëíOý)Ÿ?Ãßoúù?ý)…Q_N}”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”µùÛ¡~׿|E†/t߆Ö/kã{›Í3Gcª`ÉyfìIsÙ ª>F ‡+Ó­ÿjáaðj?x‹ÃÌŽÚ-¯±ötrG4k,LVSAäGQRWÃ>ý >&ø^ßá̾1ðZWüY%†‘gwöÑ%üR\¢¥´·*íE—‚P1(-‘ƒ÷5ræ9eL3Jv³½šiììö¾«ª5ÂâãU^=<šü©iißÚÙ?j‹í¾_›äo_7ËÎÝû3»nxÎ1š»_3ÿtÇ¿ü" ÿ¥†¼Šµymæì{™vWö—vå‹—ÝÐúb–²ouí M›ìúŽ£mk.Ù,ȃÐáˆ5‹ªø¦)t›öðlÖzÖ³¼’ZÙ‹˜Ç*.U nùCÇœšÑÍ#’ž¤­e¿^Ÿ~ÇaE|Ñ¥~Ó>ÓîcÒ>-èz‡ÃÍQˆ_ø˜Fd°véû«ØÇ–Ã= Ïa_Eiú†­e£¥ÜÇyip¡£š©î¬¤‚+:Uá?…ÿ^‡V?(ÄámíàÒ{=Óôjéü›.RÑ_x÷ã·íAà¦ñŠ%ø_¢/…ô.Ý-ç×Õ5[ë+Waö˜WÊòÔÉù‹Üí]Ò¹æ¶}ÇE|Õã/Ú{Á~ø ¤üy´²ºÕlüG ‘Òtø€[«Ë­CÞÜu ÅŽó€ ô5¾øïöžÕ¯Å‹Þ}ĦàØ&ƒit—Mq|÷ olÜm ²p@ÅNÄnŒG'+ ŸPQ_ xö¢øáÏøGÂü¦hnEŽ›{¤j©¨ý’ýÀò­o(æF!VHÉ]ÄFY]âÚWã>©ñ·ÆŸ~|=²×u Egr×÷ºµµX'ˆ» FÂ|Ær«©äf //•…Ϲ©käÿ‡Ÿüsñƒá=ÿ‰~xBÞ/è:¤º6­¡jwÆÞ+[»bà\$nYpÀ¡Ø3’ šùÏö0ñ·í%©hÚ¤2xsNÕ´ñŽ­£w­L÷Vcí@\EMù‰ÈˆoPÜp´r…ÏÓÊ+ã‹4ÿŠ“|ø-áË/øŸK²PÕ®õ[á§éÚ|SãÉ› òË emª ÀäüÁSá÷ío£êÿ> ø›âVŽÞ×~4‰¯iðN·ˆv¡x^ÒeÚ²­Àá3˜à’0Äåasë-OUÒô[o¶ëØÛîTó'‘bMÎvªîbXœÜô«R\A Y è€þuøãûNühøáãßZ£ñ á½·‡¼'ã=cD¸Òîíµuqm‹¨î`KØJ.<蕊º•€V\·—í—'ÂÑû[|6‹ãE¥Þ£á3á½DÍogÝÌ7š|£åY†”áº0;ñO\ÇêäwM‘ ‹!v•M_~Ì­û1éãÅšßìù¡jmÕµ¤fûíöZ•˜‘{ĪoÑCr;2G~Õäþý¶>:xƒá¿í>['ÃÛ$ß©:꛵†7Û=Ŭ&0(Žxo™ðHÀæ—(î~›R×Êÿhûÿ\øÂ|>ž2ñoÄxžëK‚[kkŒQ¬’]ÜK†!:€£–'ƒ‘ƒoà§ÇøÇÇž#ø9ñcÃ1xWÇ>¶‚üÇksö»+Ûƒµn-ä!Xm••†A#œäÊ>¡¬ûÍ[KÓ¦µ·Ô/!µ–öO*Ý%‘Q¦“Ùb 6; šø±i|GâKÙÏáõŽ½á¿ _K¦Ï«júØ’úòÛý|V‘ª1Â’‘Ž3Ô/JóßÚ߯>$µ³‡Ž/ü+8ñx’Òå´§G™o§µ Y‰ÈT%e}†L€[©¨…ÏÒJ󉟾üÒìõ¿‰šì: –¡p-mä™da$åM€F¬sµôí^ð¯ãçÄÍKãEßÀï~³ð¾·>uÍ.m>øÞÛÜZ¤¢"rQ‘ºàÃp0 sÿµò«øÇö~GPÊ|}kFAÿA»ê j>Âð÷ˆ4è:‰ü=r·º^«oÕ¬êYa™C£€À yÕ»MSM¿šæÚÆî‰lŸË#‘]¢|gk€IVÇ885—â›iÞÔ.|§Á©k0BÆÎÒy¾Í ²º(Vا×iÇ¥|¯û(ø¤x›Äß—Tð]Ÿƒ¼McââÖ…•ô—ÑÞ^XÛÎÞè€a ®cŠV žÓâ_¿üãÛ†&ñ5½‡Š5F·KkIL’µÓl„T)—n[ë^Ã_þÙqGöì]ÿð°t!»8ó¯·(h¼wâ?Çÿƒßµ[ â?‰íô+ÝM<ËhæIXÈ»¶dFæã’+Ø«áø(ŒqŸÙºòB€ºêÚV#ý%z"®ÁŸs×'àÏxGâ‘&»à½R-ZÆ+‰íH³ò\[9Icu`YXrˆàƒ]m|ð¤øwûmüZøa§§“¡ø»L²ñl0äíKÂVÚåÔv39bÇý…¡ lûÞŠ(¤1)h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š(+æÙ[þIî¡ÿaIôDôÅ|Ñû+É=Ô?ì)/þˆ‚¾c1ÿ‘®ü5?öÃçñßò1ÃzTÿÛO¥è¢Šús襢Š(¢ŠJZ( Š( Èƒò™Žßö&Yÿè¿_ëòàßü¦Gã·ý‰–ú#A¯×ê(¢ŠJø‹â—íÑà|JÕ>x#Á^)ø£âÝ%—Tµð¾öØì€Ê³È]pä0áU€?)!¾Zûz¿$µO þÔ?±÷í ñCâ¿Âï‡KñwáÿÅ;ÈuKË{¡¯as˜J*0w‘A–L*Fà®Ñ˜ÎAûoöxý©¾~ÒvÚä^ ‹QÑõÏ Ì–ú¶¬Ú›=FÆI7mʆ F^ੌøoŠÿà¡Þ³ñ¶½àO…¼eñ^ïÂÓµ¶©uá(ÞY[L„«ÆeÞ e`Ã!6±k0溯كöŠýž>:kž2ñG„¼.<ñNT(¶Õtøl5•Ž!±^æe¥‰6m˶S2¦W>ðÇö©:¢ëº7ìû7Üx£ÂqêS½Î¬·v~ÒnoˆU–X|ä"BT'+ãEã _|ý¬~þÑ~ ×üYàõîü,Ï«£^Úù:­¤Ê®ËÀÔ´›&× °*H`@üÞðwü/[oÚã⣨øâN£áeÑ4ë{/ Ûé&kí>uÉ-ÕΞnAæï;d K)\õÀëÿc]WÇ·ðP¯/ñÂpxZÕ´-.öûG¶½Žþ(¦U¶ ž ¨Í*¹‘¸Èg9æ½/àü¥ö‹ÿ±ÃÿúGe@E\üøA}ñ‡àç…uÿjÞ9ø¤ÝjZÆ¡¦Û%Þ“ Ú=Äð]HòùöÒ˜Ã#Çq»*Ç4ÏŽ¶G€~ øçNøQeáí{Çþ<Ôí¾Øš'†¬¾Ûu· K>])Úzn`>b¡H'ç¿¿ò“_Ù“þÁ^$ÿÒ ª‹ãÃOÚCà·í]¨~Öß|#mñ3Lñ6‰‘­èt¶—Э±[;ç!„1ãb¹Îàc9 >‚ø+ûiü8øÍ­ëþˆ»ÿ„ëǺmåö‡ys§ÛyÚd1Z¼÷Ï4’yöò4jÈɸ-•cŒš›ö…ý®þ~ÎWÚG†¼I£âø‡7Ãú¯ÛuK”É]ëä …³Ä6ÀÛ[2|}ÿ”šþÌŸö ñ'þ]WÍ^ø‡ñcIÿ‚Š|{ñǃ¾\|VÖtX¬4‹uS¶Ó›J²1 >Ò¬Oåg)‚>lýóºþ~ß? ükñLøKã¿ xŸáW‹u°?³­üð%ïÄoŠ:²i:5™‚AygÁ)¯Í$¯´áG`X ‘ù;û`?í}ûQ|=Ò¼3§~Í…¼CáýVÛUÒõâ6â[I`Èm|¦Á Ê­‚TW©þÖÖçâWí›û*|-ñ½¶ÝgÔµ»»kyo­ G‹Ê¾Æ‡fAã€z-ü³á¬QÛkÞ5øiãÿx:ùâ[êÚ G¥˜…Y$–9$*„°ÚT>Glñ\§ü~î×Pø;ñFþÆd¸¶¹ø®K±°tt{k¬¬8 ƒGZý.ñ‡´_è…¼Ggþ•«[Ëkuo(Ü’Ã2”taèA"¿.?àºd/ìùãíÒO: ê°G']é•‚†ü@Í~˜|@ñŒ?¼¬xÖ}+P×#ÑíÚá¬t˜>Õpø-áÜ»Üö‡Ö¿?g¯ø(V¶Ÿþ3_x³À_¼_ayâø•iú~’oßD¶‰^?²\B× -f%rÑ.FàÜ’+öò¿/àŸ?òY?k/û(×ÿúQs@oí{ñ oÏÙëâ7uÒ4 Bñ-ÍÝÄ ‰ö*‚ÌìÄ*ª‚YˆP "½ø)ßÃ-ãNÔ¼}ðÓǾ ð†±2Cgâ-[D0éÒy™+&á+1B£pòñí¯>ý­¼¡|Aÿ‚ŠþÌžñ%²^iæÛU¼xd‘ÛOI/# ½y®AàŽ#"¾Âý¹t7IJŻ Vž(<={x†@šÉ>Ó u’5 ö"€=ÏÆ?¼ àÞüOñvµo§ø_O¶[ÉoÙ·Eä¾62Érå”"¨%ÉA$ð9ÿ‚žü<]0øÑþ|Cíâoì!ý˜Svß7Ìó±åïgwm¹â¾%ý¥µÿë°_ì“á5Óä×mü\ø?ðÅø©á‹ D1?à©¿rqŸ k€{ÿ£Þ×Òß·$‰ìƒñq¤` øzõrxå“~$àP³x⇅üð·EøÁdï¥ø{[ÒâÕÕµ/oi,^véÊ»¢lN\‡*0yÇ5ñTßðR†úÖ¥Â/†þ;ø¡¥iS´7:·‡t6¹°-åÈÒ#1èÊ€ŽA ‚~mý ¼Q¬øcþõá¢I$/«ø{ÃZ|òDJ•¶ŸÉ2‚GðÈ«å°îކ¿X> xÃ_ þxSÀ¾·ŽßIÑôÛh! Ä`´¤ÿHÄ»1嘒y4çÿ³×íOð‹ö™Òu ߇ÓǨè²yZ–“¨Ãö]JÅË2<$°Á*@df\‚¤îñïüGào€ÛFøUãø—Âú‡Å½fMNӯ죂êÒæ9ÄïIQ£‰‹V@ä©ÎÚúçTÔ Ò4˽Zè1†Ê&p£,V5,p9ÀãšüÍý¿H_ŽŸ²Q'þèç5®+ôWÇ’G¼E,¬N»fbp±$Е| ý¤< ñ«àhý <+e¨ÚøwÉ¿ŸÈ¼Š$¼Û§<‰/ɲG’c;yÈÆqÛæ(à¦_ .ÖcitÛ_é¿a Áb;؆`­·rª±U‹§çƒŸ ¼(?à­ü@lb3i õ Ø1 Þ£md³Î¾Žá¥Éïæ7­v?ðRK+{O~Í1‚5¦—ñK‚ qó,sÈ’H¹ëµšÈïŠýJ¯Êÿø)F­¦h0ý™uÝnê;;Nø¦\ÜÜLÁ#†e‰ä‘ØðTIè~¨WäüWÃ_¥ýŸ¼­œiÚ÷Žmtû“¸¯î.ŠE'Ì9+Ezn£ÿ2øk„Þ+ð÷È>"ðE¯˜eñ-Ž€²¼¸ÉT–Yc&1´–,¨ê3Å}¹ð§âÏ€>6xNø‘ð×VWе5&9Tduáã• ˆxe`úk¹±Ó4Ý7MƒFÓíb¶°µ‰`ŠÞ4 p¢íTTBŒ1Å~ü>Õ5„Ÿÿo¿xÉi¢øRöyt¸¡'˳:’]Á8‹û¦(¢A‘È;@qkðQ߆’ø›Uð÷ÂOxÏâÕ¾)‡PÔ¼+¤ë]~ðY·®ò0p@Õr9¯ ¾þÔ¿ ?hê¾6øm=ÝÃè;ÓRÒ§ƒÊÕ,æPÌ"–ßqÜ!ØU™X‚d0ž?±÷ÅÿÚ#áìÛàO ü:ý—ïuÍû=.£Õ ñŸnº‹Ýfgº1ºS!lírJŒ/a]×ìÏà¿ÚOۇƟüYð–ëá„üqáñonú¥ìRj–ò@c¸"Ý”—tGçËêÎIË@+[~Õ¾Õÿà¢7ßuß„¾<ÔbÒ|–:n‹ý‚²k–3™ãÍÛZÿwYfA(|æ@1óWêׯÛ+àïÀÃWþ8“ëÞ.‚ ôß ÙÚyúäÂp0¦Ô8ÊÇaÞàT³ WÍþ ÿ”·ø÷þɤ?ú[aX³Æ›gñþ MûBøïÅ‘ý§Tð%®—¤h©9ßöKkˆˆ‘àSÂîò‰Èç÷Ïýö JÓÿि 4ÍoMÒ>1øÆ¿ ­µ™|«MKÅ1³ÓÜ‘•Ý(ve'¿ÈUz³²¾*üTð׆ïÅ¿$÷Ú'‡ìÍôË`©,ÒÂ1þ¨;¢19eÀÇzóÿÚÛÁ>øû4|KðïŠ-ãžÓûPºFÜÜZ@óÁ0=š9XltÍ|ájþ,ÿ‚6\êÚô’KtžÔ­JK1†ÆîkX9<àEcÛô6‘ÿøcãøwÁ? |â¯6³ý•öÛí3NÙhÿÚË¢Þ̲0G…e}»•0Ãy*Ewm¿…ÿüsÂ{\øƒãé¢Ã'P¼‚&PêÓÊ*Cmœ) T+_ûxCðìðÆËD¶H­¤[ê÷,£æšçRQpî竜(ÏEUQÀù¡û|Wøááÿˆß~!x+àußÄý_Ä>0¾‚ûUX³ÓÞÕm¤fK/.áY¶§™œ© FÕþ@¥ß¿m¯…ß¼sqð¢ëH×<ãÛh|ÿìØÿgÞM©wh÷Fâ V+– T1cWâ÷ÆOÚÇãÇÆß‚Ÿ!ýž/ü ¨|<ñ½ÅÖ¦uÝ>í¥Òåš?´Û¸ŒÄÛ6+q–áœóþÐÐRÑEùÿªÿ“Yð·ýŽv?únÔkõú¿ àµ_òk>ÿ±ÎÇÿMÚ~¾ÒÑEsEÿ×ýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€>gð¿üœÿ‹ÿìþg_LWÌþÿ“Ÿñwý‚âÿÐ,ëéŠùŽþúûSÿJgÏðïÁ[þ¾OÿJbRÑE}9ôEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-Pæ‡ÂøÓOð¯ìéoáíFÖ]Ä^!šù%³™Ò)ï—$á•÷.Ö|ÈÆr+ Ä/­í}¬`“»}zÊRkÿ&hñ±!ÉìÛéo¹%úü5‹à«ãÝ?üÖ4­RÞS)Ô/4Y¬aÓÝÏ,Ä ùà*äœ×à‹Mð»ã×ǿ𾳮i·ºµžÙô‹'¾)r–ÃlR$2y€ü¬xÈÇZý!¯/ð?ÂÝ/À¾-ñ¯‹ìog¹ŸÆ×ÞÜG(]´1yAcÚÁÄÔÓÏhÉVöÑm8¤““mûÉïÓnÖ]4áÈÒ³né%Ñ­ºŸ‚õÙW]-£ImâÍcÄrxº-FÛ*ƒx.³g!ˆ”ÿÁéÖx³ÅÚ÷í#âφÞðïƒ5íßúݮ¹¬ÝêöØÃkö¸@&<ÉÎ0½¹Æ3¿¨¨ÿYääç:iÊòqw~ï2IéÖÖVóÞåd¤”T´²OÎÎÿ#âïIâ_ƒŸ´MçÆðÞ£â? ø£FƒN»“I·kË«›7gFxç1:’ €zûè|Wñ~£ñ£öfø“'†|)­ÚI%¤ö–V×¶Åy}òFþd6Ø2•,ÅWŒ’¤ŠûŠãŽsÒ©*wœ9u»ÕEéuÞÚ_·K›¼ ´â¥îÊúzŸütð׈µ_† ì4­&òòçOñ†¦¹Š y$’ a漪ŠJ$ÆÌ^äWÙTQ\8¬{«N4Úø\ŸÞtQèIÉ=íø _3ù;–ÿ±Aô´×Ó5æƒáÚÂÚ?þÜw i_eÙÆÆo3~}ñŒ~5äb åËnC“â©ÒöÞÑÛšKÕØ‡Åÿ~x÷VþÝñ†­u[ý‹0bÛ àŽkËà·Ãÿ‡‹yâ…þ°·ñ-½´ËfK4Jò:à#>[j±À'+Û(¦ðÔÛæåWïmL©çx¸ÁRö²pÛ—™òÛµ¯·‘ò}ßÁ?Š_!1ürñ¡‡I›—м<ŸeµaýÉ®tÒ©èTñÝH5ô…|+ x'ÃÖ^ð½ ±Òôäòà„3>ÅÎ~ó–cÉêI5ÐÑE,<`ù–ýÞåfÞ#Jm(-Tb”bŸ{.¾nïÌ+ðÃEðŒ'áÿ<3ñW࿉¼qñîö]H>£scuu§É,›¼›˜o ûhþ ø¡ãߊvW÷¾?`¹·'“ödrÆžVÐç’Û‰è1Š|ÈV<7ö/ð_Œ4}Ç¿¼m£Üxr÷âW‰ïõè4˱¶æÖÒá³™)!ˤnà@óïٻŠø¬x“àÏü↺Öý¦üYñ;âïÃ߈žñ½•‹[^iÖ2êsiWÖ‘ˆ¤Žh"ýâÇ*Þ`Gʽs]XøYà/‹ÿ³ßÅo üø[ðê}rÒíŸT±m*MV{cöˆ€ŽS¼ tîpÍžFkô’Š9‚Çã‡Æo‰¿þ-~Ïþ øE |'ñt> Ñ/´!¯ôk„·´tˆŒap§Îß VX E¹Øí¯|ý õ]À?¶ʉàÏx£AÒü9©Ú\I éW‘Žk‰Eo%J©ï‚Àãœý¢Ÿ0¬|ß௎–Ÿ,|G¥Ûø'Å^6:|“|A£O¦C.åeÛÌwJŽ@æ¿4þü]ñn£û ØüÐþø‹SñOˆt¹ôÝ&âßN’]*â ÉVåïW1F±†>hr ‘Ï\×íý¢j761U¹ã$uÁRF~µç?¾iþøán‹{>£eáø<ˆ®.B ¤‹e‚\óØP¤†Ññ‡Ž¼âÿ€ž6øñRÓ@Ô|]¢øÃ’ø[ZIï/ I!ˆGu —‘ÆC•ä ÅuŸâñ'ſڃÅ´<žÕ<3á8<;‡t¿í‹g±º¾wngœ[ɇXÔ¢ª³rqÞ¾ô¢—0XüÑø=ãíöJƒÅ?þ$øĺ¥¨×u KEÕt-.mRÚþ×Q™¦Tf„.tbT£òF zOÇ+oüA×ÿfÿXø_S· âK]KP¶kg–].9mK0¼1†X¼¶;˜… 1šû–Š9º…‹üMá¯Oûyx7Åpi7’hvÞ ¿¶—P[y œwvYbyÂùk!„-¸ŽqŠÆý´um+BñÀmg\½ƒMÓìüym$÷7R¤0D‚Æï-$ŽBªR@¯ºk–ñ_üã»8tïxOñ­´žtPê6ÝÇ»JïU™X+mb2pHïB`ы⿉Z†|>!ØZßx¯K‘ –ðý³ê³ÜÅpÊH#·ÜeL0bË‘·æÎ+àŸÙÓâÕ熾,|R¼ÖþøæÎÛâ'‰á½Óf—Ã7ép5¼Pn¹v@"Ôä“€¼æ¿J´½/LÑ4ë]E´†ÂÂÊ$†ÞÞÞ5ŠbŒmDUPàUú‚lx^_üð|zÅ“kÐøïA¹}8\Fo•€”Á»Ì“Æí¸ÏzûÚ¸SáŸÃsÄvþ1Ö¼)¤êý™ˆÁ¨ÜXÁ-äFº2“ºaåpÜEvô›%~ÁFÿ#7¥Oý´úZ–Š+éÏ  (¢€–Š(¢Š()h¢€? ~ ÿÊd~;Ø™gÿ¢4ý~¯Èƒò™Žßö&Yÿè¿_è)h¢€ üÕâ¯í»ðÇž2Ò¾!ü;¿ø×àíCR{júØÁukhÿrÖâÖ4ŽÅÛ™Îæua·ôªŠü¸øð+ãÅo?hÏŒ¾ âø‹á³ám;GûB]^¥«¤q½ÝËDB‰…vƒ†äŒ¡ŸÌ?go~Ø?²à ÙÆïöz½ñÔþº¼]+XÒµ(!Óï"»¸{€ÓÈÈþ^C†}§n•J’e¨ ÉÿØëàwí+àïÚóâÆÚt>w4KYöÍÙ%Ôý†…¬h"ÜFθct/Ú#ö}ý¹|iû@xáuçÄÿüHÑll4Û¨¡žÎæÊ(!ÄFyP¤H|¡Zýd¢€?6|Wà?‹ÿÿlOÙ³ã¥ß‚ntM'EÑ5ƒ®Ç$ðÍý“s{e:%¼®¬ ½Õw"Ií[¿ããÂnM³ÒžÊÛSÑgQóÇåìIn›vIßò”;Ô«+~„Ñ@—Ÿ <ñÏãïíáßÚâ/Ãù~øcÀz-Ö™§YßÜG6§©Mv²¡iR,yq ˆ 8*6î,Å=Âß þ!ØÿÁH|cñ–ïE–?j‹J·Ô‹GåIz.¬ä0…Ý¿!cs’¸ã¯JûòŠøãˆ~"ý½>üUÑtYn¼)á];]‡S¿VŒGm%ÕÄp«)`ç{:µOZóÿŒß¾?üý¨o?k?ٻñxïNñ…ŒŠü2×Kks3[ª¬wVÏ'ÈH£˜6ÿ‘„ŒWôòŠüÀºø£ûw~Ñ>%Ð|-àO‡7_<5i αâbâÞîòKh›-omhñ`—ÁꎭÀfs»Ó¿mÏÙãâ7Å$ð/ÆsAÄß…‹j:\7OåÁ› öŽÙUC¹•J—BÊpûÆŠüÌÔhOÛ›âW‡çð‚ÿg»ø¦þ1k>½«êÐ3M2uUY. Y=Äe_[kþ Ÿð7âGì÷ðwÆžø•§Icv|a¨]YË)LÞY›[Hc¹UG“jÈбŽGZý¢€ üyðD´÷ì™ûBün—Cø1ñ3Ãõç×ôÛí2ú u‰§’Y9L¶ÿ­Øwí*S :¸#öŠüúø¯ð·âOŠ¿nŸÙÿâæ›áù›Ã>Òõ¤Õ®ÃDzÊk»)Ò(Ü Ä»…TŽ}+éÚwÂÚÿÿg?‰žð­›jεáÝNÎÎÙ †šyí#E,B‚Ì@ WºQ@—¾!ý|kñköøqð‚á¿áø“à‹M3QÓ^y6‹MVÅY|¹^-ø Žë¹wmb­ƒ·–µGíÛ¦øvßÂ:·ìÁy¨øñbòP‹T·MYñ´\]ʨNÇçŽ3ûÅ?Pè ýš|5ñÃÂÿ l­hÇâo^O=ÝÓÃI šNÛ’Î6‰ÌX‡ñNIU%M{ÄðCs–Ó ’)T£©èÊÃb*j(ñ÷áV‡ûSþÀúˆþxWá…ׯ„÷ÚŒú‡î´»ÔŽÿN[–æÚxå ̱ qgw_ý£.¿i/~Ó³oÅ_Ž>ƒáö‡ÿ æ¦h>‰vŠ×¶Ó\Þ]MòÃÈV4U«‚«Ò~ÿWÇŸµìñâߎ5ø)âo ê6Vÿ |]e¯ß¥ãJ¯5µ¼±HÉ—ƒ!pªôË òOÛ_àïÆ©þ'ü+ý¨ÿgÝ"/ø£áœ·PÝèÏ*B÷Ö‹±Ö'| „iQ†KaÃ(%p|Wö€ñíûbü#ñÂß ü¼ø]¢½”÷z•Ö±y·šŒ–jg·Ól¡QâxÑVù6“œ•ÿb( †t/Ù²ãâìáßÙ»âm³èZ¤ÞÓìf UÞÇP³HÞ;†ò¦Y”7Ì\ó^ðãã'í¿ðÂÂ/ˆÿï>%]øv4ÓôÿhZœ?g¿¶B@ó‰Þ6Úi$ [« l–ýV¢€?8?fï_|]û@k?¶íAck x’}"éRhž!Ó¨nm%dgŒO©\ÍLlËóFêÜüó_gQ@ü1øQñEÿ‚‚ücø·ªh²ÛøCÄ:“i§ê ÑùwÁ ªÈŠ¡‹åLlT>”ßÛ»á?Ä?ŠmðTøE—Yÿ„oǺV©¨ùMýžÊ Þdͽ—*¹ä ŸjûòŠJüÌÿ‚‘|ø¹û@Zü%ð÷ÂKYçOñÚ.µ%eTÒã*ª—O– V6ù¾@[åàWé¥ùŽ?jÛ£FÑÁzŸìÏs«xîÞ&€j–ºœA¸˜ -Èc±±ÃŒ¡±‘¹OÝí¿fOØîÿÁŸ~ ø_ã¥êk1øÑ5ýߊ§¶rUN¡§“àb;oc¤¨S_ TPäÁï~Ú?±§ƒ‡À¯|ºø¿áÿ<‘x]Я£ŒÉi#³¤W:É"l,@,«° £z€çê¿ÙˆþÖþ-ñо*þÑ)ƒt b(mô?Bð]µŠÆrngºEÝæ°ãhq¸³6¢´h €ü-ð£âü‡Æ?nôYcðf¡àH´«}H´~T—¢êÎC]Ûò79+Ž:ô®'ãoÁ_?ÿiiÿk¯ÙEƒÆCĶ0éÞ,ð¼×BÒ[Õ€"%ͬ’º¤QŽì¤ÄŽé•ù3ñ_Ç¿¶Çí[àÛÏ‚^ ø1qð›Jñ25–µâj·‘düOD«#ù«”,ªû•ˆÂçzý'ñoà Þý†ÿ±ÎÇÿMÚ~¿×äü«þMgÂßö9Øÿé»Q ×ú(¢€ ? J_€?ÿÐýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢Šù£Âÿòsþ.ÿ°\_ú}/_4x_þNÅÿö ‹ÿ@³¯¥ëæ8[øuÿëíOý)Ÿ?Ãßoúù?ý)…òwí7ñWÆž¾ð?ÂO„—Im㿈Z¤vðNÑ$âÇNµýíõÓFá”…Œ7)ãq2Š‹âÿÄ¿|øÍðßPÔõsðÓÅ®|?¨$ÄÏW—-gtfU ¶s˜ÙIØàe†>£”÷î}kKE†WϾ=ñ׉ô?Žß ¼¦]´Áª½ì&4c#[$f"‚Ë´±û¤g½yçÆ‹^;ðÄ]{AÐ/ÖÞÆÇÀú¦µfßmõ³›™I á'iî+ަ6M¾Žß…Ï£ÁpÆ"¼©F {ñsWoe'tÞéü±)kã€|[㯄þ(È‘øÏÃvr]–òѶóÀÓZN#Q´ƒ‚§/=jíã{_ÙËÀÞ)E‡Vñ׎.M²óG ºšy#:FÚŠ 1ïÞ¢97.êÿv–õ»:ªðV6¥AÛš3PßMS’•íðò¦ïÛ¡öåà^ ð_Ç}ĺf§â¿ˆpxJq Ô,[M‚Ø)16Ãm,@?Ë.ÜïÎW=ê—ìõñÄ>9Òü_7‹o㸟Jñ-þmòG[h#a݂Ǔ’}khânÔe›ôéó<êù:•iUŒã ]Ç›í6­ïE>šùZÇÑ4µò×ìëñk\ñWÁ_âÄkõ´›½DÍ2Æ‘µ´Pÿu•sÏZã?fOüoªkš/Äé^^éÐëúB,IÛ Ýã)ò*îŸ/“’wrk8ãàùómÿí¯Â8ªkÛ_¹iKW­Ý½Ý5¶ïk#íŠ+á/ÙgöƒñÇŽüM¨øKâtÉ4ºŠÏu¢\,Q²Åi+CqîÀ ÈT0ãp'‚µgCý£¼G |,ø—ãϺj·^ñ æ—¦BaVÄ‚8#b€d)9f<•¾*!™Ò”Tú;þN+qÔ«Ï £”\~wem:;§{Y§ØûŽ–¾cðÇ„iu}#ÄÚ÷læ–g†[í´ÈRÖ8œƒ$QΟ¾ÞŠH X‚G9Ìx×Ä¿µÿÚóáƒ|e…´ËM K/couºW™£a™FyïŽ:V’ÆY&àõvéþg.ö•eNž" F.MûöVi?±w¾–M>çØtWÊþ2¿ø±ð»à¯üUªøÚ/ë6Âk…±·€[ QWÎsó+ƒºø‡ñÏá?…¼3ñ;ÆÚ勼'«íkgwf—á6ÈØá°A^P§ŽQv”^×{ië©®„êW5Ðw—,W¼¹¤’vWŠïmmw±÷-27YdC•`ú}vŸ*W|Eø·ðÏá&“ý·ñ'Ä–^µ9Ùö™@–R½V(†d•¿ÙEcí[Þñ~‡ã¯ i>6ðô­&“­ZÅym$ˆbf‚e ŒU°W ƒƒÍ¦¥¯ _Ú[àÏŽÃ]Äöú׈£‚æâ[m85à†;HÚYD’B5pYmùÀÛÍužø»àü2_Œ¦$𡵸¼k·Fˆиœº0 ¦3†diØ.zUã©ñ×áô¿ m¾6C-äÞ»D’)#±¸’áÒY¼„alˆf ±È;~ïÍ÷y®Göbñ|­ø‹Lñ•×mf×oÄWvoc%¢nR,¼©[€Äsš,+ŸGÒ×Ë_?l€¿ |WàíX¹¸¿Ñ¶M¬,n/`Ó÷ôûL°£*FW%‡B¯ZñƇð7ƒ¼[y¤Øë Õ®¯¢L‹3E¸8òäuuÚã†ùs‚G‹ç¥Ñ_~Ä^(ñwоë3xÓ]»ñö™â]cOŽîõƒÎÐZJ#Œ1µtž=ý°> ü;ñv«àj}RëRÐ6PØéWwP؉"Y”Í*G°«eK<‚Ê+ŸPR׎j¿~é¿ ôÿ‹Vº„Údž5C¶¸Ó-g½wós·÷0£H¸*CnPTŒ6óGìµûgi¿´=3HñÌwCÅš¦¥}k³Ò/ÃÊŽgò7OµâFò”oÝ Ãdr°¹÷Õà¿ÿi?„ÿ5{? ø®þâç^¿ŒÏ™¦ÚÍ}za^ ­*Ń‚ØÎ3ƒ]Oƒ>3ü2ñÿÃù>(xc^‚ Û¤­qu&èE±·•gI´lŸÄ¬ïЂKç¨R׿ÆïÛgÁ^-Ðü'¦üñ.¡¦êº‡Š´X–G³žÑu6K‘ÏÙÞhÂË!_0ÏJõÛS^øñðËáö·ñ‹ácÐôÝÞÑ_J}2ÚëÏ–{¨à2yó+2ñ(8Æ>_z|¢¹÷-ó÷¹<à†š‡Œ¾5xØx± ÕMÂiÑYý’Õ-IJ¦ËqûÌ`q“ÐW§þÝ_³f©ªhºu—ˆ¦x5É ‚ãcr¶1ÜÜ*´pMpSËI~`IýÙ8®‚ç×ÔµçñGÁ‘üOOƒÓ]´^(›MmZ+w‰Õ%´YLñÊFÆ!Ž ƒ»œ`|RðeÇÄéþÛÞ4Þ(´Ó“UžÝ"vH­dƬò±X°áIÝ‚0E+ ô:+㨿nïÙÒKÕVÕut§œ[ ^]*ò=3Î-³i¹h€_›‚X:“ŽkèŸ|JðÃO]üDñeï“ Ù,O%ÄHÓ“:¢2ˆÃ°äv9éNÌW;ºZù“µïÀ¯|A´øoá]bãPÔ5 f·µ¹ŽÊãìÏnŒòEÑATc¸„•ŽFyŠ¿¼''Æ?|0±ñ¶¥á=fË^¶ŠæÕt«—µÖʬ¶jhü­¬e•ŽÞý >V>É¢¼×â§Å¿|ðÒx¯Ç·rÚÙM:Z ·–æYn%ÉHÒ8Uس`ã8¦¸ï…_´§Â¯ŒÕß…¼1yugâ (E̺f§i5…çN<ÕŠe]é’2Tœdg¬;žõK_'ø¿öÕýŸüªë^Ôõ{»[Ã×2Û_YÙØ\ÜM’¨ÒLá……|À<ÂB’.J°“®||øk¢|;Ò>*-åΧá½tDÖwu•ÅëH³)e&(Q¤Q€AÜ£iààÑf+žÏE|û&~ØÖ?|)á­+Ç)r†¥®#áÏÄ? üUðnŸãß\½Þ©ù¢’-ßt¼«G*«©I#e9ã A®ÞŠ+'^×4¿ hZ‰uÙþ˦é6Ó]ÝLU˜Gd‘ö ,vª“€ =…jÒ×Éžý¶>x§^Ó4(u+ý3ûnQw©i·VVW’7ÝX®&S-Ûq\’R{/Å_Œ¾ ø~/üGÕÓKµ¹™míÐ+Ë=Ìì2"†Ã<œ(8œ vasÓ¨¯øIû@ü:øÓq©éþ{è5b{»=FÆ{ˆã›"7Û2UöœONq^}ã/ÛKà‚|I©xb÷V¼Ô®t'ÙªK¦é÷7ÖÖ¸¸šd]½)bAÌW>­¥¯ø‹ñãág¿ i¾1ñž´¶¶ÛDšrG“\Þ¼ÊLŽÄq·Žøªß ¿h†_#ÕcðUüÃPЊ CO½·’Îú×Ì¡’ B°Vá†AÁÍÏi¢¾5›ööý™áu#_º’Þ)š˘ôë¶·°e•ákG¶-Ì„®y+†û¤íh¿ÚBø5ðËQñ%Œ’Ýj·úMõÖ‹,SßY½ÄVæHy å furwcš9X®})K_*~δç‡>5èZ™*^¯Š§Ò"¾¿'K»´±óUPMäÍ*yL7¿È¶G# ·ñ öÃøð×Ä÷ÞÖõk«ÝKHPú’é¶7ñéê{ÝI2ÇŽë’ør°¹õåºÿƯ…Þøf>0êÞ"¶OIw_¡2$É/ˆ•gv'nÏÈ8øÚóö®Ò>'þÑ¿|/ð»\¿³Óïe×W]Òî­d²–T[He²yb™ÛçhÙNž¡D.~R׿ևâ¯Ú?âßÇ_‹þðÇÅ8<£xR³¶´ô‹+²ñÝDíò¨o”ÇÜœçÚ¾¡‡Æ7_¾_xÇãÿމaÓîSÎÕa°Kp±ÜËÇäZ‚$q–Ç|ž7¹ô%ó/?kÏ_¼gmà_kSGRÚsÝYÏko¨¬_xÚM2*Ê0~l|¹­Ÿ‹´ßÂOƒzí§…Ù×)±£áÒEpÞV޽5à‹ûoþÏ3xˆh6:Ååõ¿ÛO:¶w6˜·nâ5ˆÝ$e ,@ 2½óŽh³ Ÿ\Q_|Pý àøIûYZi4×î-|#?„x´èb{†¹ÔÞõcÉ‚$id”¦î€ “€ ¯gÐjŸ‚Þ#ø[¬|`ÓõyG‡¼;?ÙµBö³-Í”Á‘Y&· dw©8ƒ‘š9BçÑ4µÄøâG€~'hÉ⇾ ²ñžØ[9Ö]ŒFvÈ îGÕÀaÜW­þпÿ#7¥Oý´ú^Š(¯§>€JZ( Š( ¥¢Š(¢Šü¨ý£ÿg/Ú{ÀŸ´Ôÿ¶ì™â w\Ó"Òµ­Ut‡ÏXÄQ‡šKdhŠA `Ó¤ŠñåK«l—ÿ†“ÿ‚«ѹèßø?ùa_¯ôPäü4Ÿü[þÏFÿÀ¡ÿË _øi?ø*·ýžÿCÿ–úýE~@ÃIÁUÿèÜôoü ü°£þOþ ­ÿFç£àPÿå…~¿Ñ@ðÒðUoú7=ÿ‡ÿ,)á¤ÿàªßônz7þþXWëõùÿ %ÿWÿ£sÑ¿ð(òÂøi?ø*·ýžÿCÿ–úÿE~@ÃIÿÁU¿èÜôoü ü°¥ÿ†“ÿ‚«ѹèßø?ùa_¯ÔPäü4—ü_þÏFÿÀ¡ÿË ?á¤ÿàªßônz7þþXWëýùÿ 'ÿVÿ£sÑ¿ð(ò—þOþ ­ÿFç£àPÿå…~¿Q@ðÒ_ðUú7=ÿ‡ÿ,(ÿ†“ÿ‚«ѹèßø?ùa_¯ôPäü4Ÿü[þÏFÿÀ¡ÿË _øi?ø*·ýžÿCÿ–úýE~@ÃIÁUÿèÜôoü ü°£þOþ ­ÿFç£àPÿå…~¿Ñ@ðÒðUoú7=ÿ‡ÿ,)á¤ÿàªßônz7þþXWëõùÿ %ÿWÿ£sÑ¿ð(òÂøi?ø*·ýžÿCÿ–úÿE~@ÃIÿÁU¿èÜôoü ü°¥ÿ†“ÿ‚«ѹèßø?ùa_¯ÔPäü4—ü_þÏFÿÀ¡ÿË ?á¤ÿàªßônz7þþXWëýùÿ 'ÿVÿ£sÑ¿ð(ò—þOþ ­ÿFç£àPÿå…~¿Q@ðÒ_ðUú7=ÿ‡ÿ,(ÿ†“ÿ‚«ѹèßø?ùa_¯ôPäü4Ÿü[þÏFÿÀ¡ÿË _øi?ø*·ýžÿCÿ–úýE~@ÃIÁUÿèÜôoü ü°£þOþ ­ÿFç£àPÿå…~¿Ñ@ðÒðUoú7=ÿ‡ÿ,)á¤ÿàªßônz7þþXWëõùÿ %ÿWÿ£sÑ¿ð(òÂøi?ø*·ýžÿCÿ–úÿE~@ÃIÿÁU¿èÜôoü ü°¥ÿ†“ÿ‚«ѹèßø?ùa_¯ÔPäü4—ü_þÏFÿÀ¡ÿË ?á¤ÿàªßônz7þþXWëýùÿ 'ÿVÿ£sÑ¿ð(ò—þOþ ­ÿFç£àPÿå…~¿Q@ðÒ_ðUú7=ÿ‡ÿ,(ÿ†“ÿ‚«ѹèßø?ùa_¯ôPäü4Ÿü[þÏFÿÀ¡ÿË _øi?ø*·ýžÿCÿ–úýE~@ÃIÁUÿèÜôoü ü°£þOþ ­ÿFç£àPÿå…~¿Ñ@ðÒðUoú7=ÿ‡ÿ,)á¤ÿàªßônz7þþXWëõùÿ %ÿWÿ£sÑ¿ð(òÂøi?ø*·ýžÿCÿ–úÿE~@ÃIÿÁU¿èÜôoü ü°¯<ñ_Â/ÛÇöæñ‡‚¼;ûHø'JøsðÏì:–¡ JÒ^˜²²*F·,Ò4.ÑÆÌŠ‰½ŽæåOî”´Q@ÑFÿÑýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€>gð¿üœÿ‹ÿìþg_Kc€9$×ÌÚ±Ú~Ô$K–›Ý25ƒxÃß | ¡ü<ð¬m• ZÇküpƒæ‘Ê€ »eÜ€bNJíkê›Ôú›e/‹wß~é÷Þ$S‹<=,š.½nÃkÅ©X*RËÛÌIŽÅŠö5ô•yƒþ xoÀ¿÷*âœ>g(IΜ\:Y§7+÷½›V·g~‡À¾&Óï<ð‹áÿÇ]&xO‡F¯Eüµ±½„¬‘ÜÃ1_s•tϳY´OÙ¿à?Äibit¿k)u¨¼«i.%¥Ú;'ùë_¤ÒxOÃxpø>]&Õô#·û … ·’:'•»}±Š“NðLJ4<-¥évÖš•úâx[Ã~ÒÛCðö—m¦éÎÒ9··‰"„´§.J(,O›àí*ÛUWóæ;H–EsÕ”…áŽzŒ¿â_… |g©lx»Âš^³~bóï-"ž]‹’«¹Ôœ œzÖ¦ ¤îågªvÖÚ_ó80œU„Ãò‚œRŒã̹T×3‹Z«_–Û·w~‡Í<%ð‡Â³oÄ‹„qXÃm=Ÿ›t¶SùàÉ•U-ó¾+‹øÓã? ø³öxð·Â ê0jÞ(ñ45œ6v²,ÒǰDÒ¼ بä¶0û'LøKð¿FÒµ Iðž—g§jê«yo œIʯÝ¢¨læ®xgá¯ÃßL×>ðÖ£JÀ‚öv±@Ä£( óNx)ÊéY&¬íêöûÌð¼U†¤ã9óÔœ*{H¹5«åŠ\ÎíèãÓu¥ÑÖÙÂmìà·<˜£U?ðŠå¾!xw]ño‚5¿ xg]—î¥k$6Úœ‰d´•†UBT1_L­vTWª€nîç䇄>øÏöhןÄß~GñŽ@å¤ñŽ—q&««ªX4¶ƒR½rʈ8ž¾Áû@|ø—ûJYø_â—Ã}¿Â7|3ÉàÍa¯4«]AdÝ&餶t•de`¦)WfTdšý¢«˜ŽSâOÙÓÇžðƵmð_Pø/yð‡_¸ŠB‰ ´w:UØ·RÌSR·‘‚‚x f5ñ/ïõÿ†ó|Lý…´{{¯ˆ'±—Ãl7b-^2ø§ý3¶òØ7÷‰—ñý´Çé\~¡ð÷Àz·‹¬<ªxzÂïÄÚTF MN[hÞòÞ#»)ÅK¢üíÀ |ÇÔÓR Óàð—Ã_ i: Ü[èú6“o…§Ú%X‘c‚0‘ g ±>§×Ç?°ç‰¼8<1ã½9µkO¶^xç_xaûD~lªò©VEݹƒv `ö¯°üoðóÀô˜ôˆ~°ñ6› Ëp–Ú´wP¬È¬‹ IU”0W`2#½p¾ý›gÏ ëv~%ðÇÃoé:¶žâKk»].ÚáqÀd‘# §ž ÒOAØøÏöGñwßx7ãŽñkR±Óõ‹/ë—üWî‰,֒Ƥ;#üÒG m6I8ûÃ>Åÿõ±Öld¿Ǭ¤‘ù©s-²ÉÂÚIÝ£ëØƒx޵îþ2øðG⽊|sà]]Õâ ¢êòʦ*Ÿt32’Á{È«Ö ‚ X#¶µa†ˆ€*ª¨ÀPÀ›’>ý†8øªj–Ö~$Óük®,š|ò¤w'Ï”IX؆`Àð@êê q¶Ÿ|oñ7ÇÿΣñbËá'‡þê÷:rØÅijדÇh ›éæ»ä¬¡@ª~\ ð7}×{ð{á>§ãˆš‡ƒ´‹ŸZ:É©%Œ {¨Àeœ¡p@àk+Äþ øÃÅqøëÅ^ѵoE°­õÕ”2Ϙñ°–e9)´œ‘Û]™à›wÜþÌ6sA#I k:ɉ|²c7nW( Áû :PÁ?¼Wá«‚¯á ­VÖrËÄZä2ØÉ2%ÊHײJÊb%Çõ¯¶ü1á/ x+MmÁúE¦‹`óKp`³… ˆÍ;–MˆÜìK1ÆIä×'/Á_ƒóxÎ/ˆ²ø'FÁ/ªl ûjË‚7‰öo ‚Fsš·Ï(ô-qÿm¯Œ^|L“áÆ­©ZéiŒÖö²}»L[uFX^ì§Wܨyc’:U?‰¬|û,þÑ|=ñûüBÔ¼E2^êæn1¬or¡m~Uy`*Î1Њý"ñÿÂ/…ß`··ø‘á]7ĉhI„ß[G3D[©˜Lÿ²Ehø7áÇ€>hOáøvÃAÒ%fgµ²¶Ž]œft@œ’:ÑÌ.Sóö­ñ÷Âü)øcàýOO½šãÄÞ—JŠÕÑÞ+HÙL"œ¢…*¤0x¯mÿ‚†xŸÃv¿²çŽ|7uªZë]G§Dý¯à“Àwÿ ¿j<«ü>Õ¡¶ÖG-¢êø¶¸-ÁÎÇeÀè³v¬ßÙRæç[Ò~-þ×ö’_\xãP¼—J„Þ>¢#Aiqddeà|ÁQ»×Ý:ÿ‡%Ómõ]"ú3 Å¥ÔK4Æz£Æà«/±ýÃú…tk?xgOƒJÒ´èÖk[XÖ!~êGª£°—6–µ?¼uñ#ÅŸcâÇŽþ0ØYAâ¨-ü#¦XÙ¬*æF† #¼½Áo”9p¢üÀ»ÏÒŸ´B›¯ø&äqÈÇ÷¾Д‘ÔÖÊúÒ?Ù£öz‹VÔuÔøq }¿WŽh®æ:t¦K•)2¶W‘IýàyÍz&§àOk^Õô+ß,QB4é­ã’ÐE iä°Ùµ6£§Ì…bOèZ>ƒá-CÑìâ´°Óm ŠÞÐ*D‰U ÀÀô¯ˆ?kïøfO‰áW³gÓ¼sl×J.#&ƒ e²˜<ØÅ~‚GEÅ„DT€à+õoÙ‡öq×õ[Ýw\øcá»ýGRšIî®gÒ­dšy¥bÎò;FK31$’I&’z£Éj?šÏ…4ÿ‡šWÃwMÒ¿á:׆–Þ#¸Hï-4ØÖ'gu„fFÁEÜÀ›>ß7èS\YþÞßtK߉mñRµÐ5´ÍäÛB-UVHsj6åKíbJàz×èlŸ~IàQðÁ¼¤‡iWJ[(VÉdv.ΰ…¬Y‹n’sMð§Àÿƒž¹Óï|à­FºÒ„ÂÒk[(b–´€&Ù"¨`d 7œüØÍ5$&–ÿf-2ÂOþÓ7Ï5ÅÏ‹§¶‘ÊÏ v²¡=ÔþÑõ®£þ úY¿dßGÚ+r4‚G«Þ"ì+êÍÂðüڵƅ£Úiòë×ïPx!HÚîá!–b yŽQB–lœ*O xWÃ> Ñ`ðß„4«]J¶i+K8R¦‘¥¬h‚ò;3`rēɤä4‡ÿà^,ðÐýš¼5á5[T×,.õ+y¬^d[¨åRÈPÂH|…9Æ*O‹'Óþ5ø‹ãÁO‹:?ƒ¼w&Ÿoe­išÒÁqg41"¼òË-¶P).Á€;ý[Á_ƒðxÑ>#Áà?G#ʺªØ@·«$ŠQÜNÌ ÊÄœHïY>8ýž~|LÖá"ñÿ4}T*¨n®ìâ’vDUg#s($ŒSæÖⶇû&ühÖ¾=|±ñ÷ˆ´È4½E®®í&›¬ïm)C<²v9õ'æ É®‹á7í ðçãOˆ|]áÉt÷Þ¼6:ˆ¸€Â¢e’H–ÄãtMÈö¯YÓt]?úZ…¬môëK(¼»[h£[ÄùT"sè+æ¿Ù+öpŸövðV«iâFoÅž%Ô&Ô5mBeY¤v;w€Ø’x3)h=O«+Çh?ˆ¿ > øÏ⧦«s¢iÓO¼ ´NØÚ ª91®wH8ʃÈë^ÇP]ZÛ_ZÍe{\[Ü#G$r(dtq†VSÁzŠHgâ7í#âêÿ³ï„|OãŸ6þ-ÔüW¢ÝÁ ØÙYÅn’4±ÎûJnºåK’¹l+»m}uñïPÒü9û\üñÄ)Rß‹e«Ú[\Üñko¬L‘˜‹»|ˆî l'tàõ¯¤t¿Ù‡ötÑc½‡KøkáûtÔLmp«¦ÛâO*E™ "«¨èkÓüWàÏøëC—Ã>4ѬõÝ&|o´½. m½2Ž ñÛŠ®dMŠ6~+ðwˆ¯ïôß êö:†±kj¯*ÛMÒÇ…„eöB–õ9Å|=ûøÛáß„¿g™|7âÍbÃGñ.ƒ¨êkâho§ŽÖûÏs,³‰c½pCåp=«ìÿ‡ß~|(‚êßᯅ´ï ¥ñV¸û ºBf+÷w²Œ¶3ÆO«Ÿñìíð#Çú÷ü%>5ð‰­jÿ.뻫d™öýÝìW.`Ù®¶ñOí1h÷ÿµ?Á}u<`<ỽRƒIÖ£† ­âÔ$ØÊŠn…ZhJ„n×­ü.øO¡hß/þ#Þü_>:ñ|¾–Â{-–Q±±ó£t–EµÁýÛ¨ Hþ";×Öž(ð‚|máãá/h6:ÖŠB±^[Ç5¸ ÷q‚£ol;W1à_‚ ~%ë|4ð†™á¹ï¤ÒYZÇ ’ŽÁÝFæôÓæÐ,~l~Ï>=øE¤~Ä_ô}{SÓí/ ºñBêVóÈ‹,“Ü<¿fcÎZ3©ýÜvãèŸ é$¶ÿ‚nÉ£ë0Êu_øW7±ˆY[Í Út¾L{NNáUÇ\ñŽÕ{ö}ý¼áL|¡ë.ÒõVâ+¹má¼t†çPžæÜ Yrv¤€…9 Ý9æ¾áòãòü­£f1·cÓ”JBHù¯öNñŸ„üCð á݆‰¬Ùß]Ûè6I$1N*4,r@w ¬0r8¯‚ÿf? kw6ßô-Cãø Ä0x§TþÚÒ§µ°w“Í#ËÞñL’G$|J 6:œþŸx_à·ÂøŽx;ÁZ6‡®ÝFñK}ea½Ì‘ÈÊÎ,h«2© œ¥gxïàÁ/‰ÚœzßÄ/hþ ÔcP‚æòÎ)fØ:!r»Šÿ²N(æ ›>x/áÀ/‚¶:/ŠÇŒþxâ%•Þ£¨Éå½²[·YƒùYŒÇžj·yŠõ5ì¼Wà?þÝ?­ü1¨ZjZµ¦²×²ZÊ“·šÜ›UvBGÞY™A={ó÷š|?ð,~?cðý‚øa¡ks¥‹hþÄac’žFÝ›I9Æ:ó\„ÿgÿÞ»°Ô<à=G»ÒäškIí¬aŽh$¸@’´rÜ¥ÑB±•tùÂÇæ÷‚þþÎ?iïÚO¶ZMÜ–Æœ4ó©Ü‹r¢Heó¼¼È›³µ7uǵ굷†~ø/öñnƒð†|-k}¦1O˜O‘õKV”n üžã5ö7ˆ¿fÿÙûÅúÕ߈üUðßÃÚ¾«~þeÅÝÞ—m4ò¾Üîñ–c€9&¼Ÿöƒý›ì5ÏÙÃ_ø?ð+ÃzV&§y§Ý%¥´qXZ³A{o<ÎÂ4 ¸Çç8ŽmPXñÚÅßüg¤þÎZOÃ-BÏPÖ¦ñNu¥Ge"I4Û¿Úw$j…UÁÇ8Èùxê~k¾ðíÅñÊ?ßA¦jz½Ž‡s¥My"³i±ZF’ˆZBU™Hp§ï+u5õgƒ>|ð½/‹ü'à­#FñÒžúÒÎ(§mø2 ê €Äe±ŒžNMh|Aø5ðŸâ¿ÙOįéž%kùkï=B3@=À8=Å.d?>>Zê¾(ø+ûUê? 76âgÄïn—4¦Ì$’[ÁWŸvÒ¼Èë_DþËÿ>Áû3øÞÃ[Ò¬4ëM:ÒÎkYçŠ6Šða$ŠH˜ƒæ™³FKóšúÃÃþÐ|)£Úø{Ãu¾“¥Ø Ž [X–bAü)ª>‚¼¶ãöoøwâÕñÝÏÃÝO,Âãí¦Â;Ï"RÛyCŸ˜zÐä<P°³»ÿ‚‡h÷—,’ÙxéáfØï|ˆY}Ö+ŸBGs\‡Ã/è_ ¾-~Ô¾+ÖínntÍ?TѦ–ÚÊÝ®§™çµd++˳±öîH5÷™ðŸ…Ï‰×Æ§Iµ> KSd5%>Ô-YØD¸ßå–¶ççi^𾇫jºö¤ÚØêZëÇ&¡s )×o í¦u¹E$)bp:RæÊÝ_à_ľ*‡Æ_þÃû= uoøI.neµÖn"8eƒ¤c=Y'b­Ô>xC|eñNœ,t¿€ÂÝGã¿‹¬tËAqssg µwË77òå-ÞfBÄD3‚W#?vÑG0¬|û)~Î?¾ø–ÿÅ^/ñ:'‡/¢e¶ðV›qs§Ø—Ûµ…ÅÜŒÊɃòD6dœ60+ïz(¤ÝÆQE†%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-P_4~ÊßòOuû Kÿ¢ ¯¤n'†ÖÞK›†Å —v=TdŸÀWÎ?²ÌRGðîõÝJ‰u9™O¨òa\þ`Šù|ÇþF¸EýÚŸûaóøçÿ 8eå?ý´úN–Š+ê  (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š(£ð¤¥ü(ÿÒýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZù»Áßï~!Añ6ÿÁ>“Q°ð ÔÚ}”ÿj5›ÛXËÜE·–#m¨îÜÇ5ß|ø±¡|oøc¡|Lðômoo¬BZKg`ÒZÜFÆ9àr1–ŽEe'8ÈàŠmÏS¢Š­yqöK9îÊîò#gÇLíÅ&4®ì‹µâ>øÏoã_‚³ücI{H¡µ¿¹û˜;bÒ)_0(?—×oï^Akûdøpx‡ÀÚ.¯ ËalàºûQ¹VKO´K$(®6 Ãr ¶FéÇ<’ÇRŠMË}¡¡Â™…IÔ…:WtÛRWZ4›k}v{_Èû6ŠòüS·ð÷ÅO |/}9ç—Äö÷w t$ °‹P R›ImÙ둊àõÿZýÏ‹õü%ðLþ7ºðëµ+¶GaiÄgÉYdGß Ç*Áã¨5sÅSîúÛ®ûþG6‡ñuy\a£5ÛŠ\·q»m¤µVÕ­O¤ékÀ5ŽžÒ< {®øVçN¾ñ–ªšT–sÌ«%”¿.Ä)/ÉÆ6äxé]_Ž>'Åà¯x/ÁòiÍvÞ1»šÕf„þLFMÅJùÆ1‘Oë0µïÛñ؇‘â¹”y5|ÍjµP¿7^–~½.z¥ó—ÇoÚ'Jø{¡Ø\èòëRêÞl’ˆe›[h™Ìà«düt‘NøëûDèß­<5¨ßirjö^"™ÓÍ‚PžLQ„c [Êùœu¨ž6”y¹¥ðïó:0¼1­ì}•&ý¯7&«^]úéo;_¡ôU-xßÄoŒzg€­|}ofu{ojvz|E(EE¼#dÙÃn\p1ŸZÅñïÆû½Æið×áÿ…îbþÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯¦šX’D…DŽ U'– ×¾;Ô”ª±ÿ š¿ø1‡ú¹ùÿSÿgÌ_Ùµý4ßûæ/þ3Iý•ûQÐcMÿ¾bÿã5ô£ÞÙÇl÷²OÛÇ’Ò)ÁËg­C§êº^¯Ú´«Èoaà‘d\úeIª±ÿ š¿ø1‡ú¹ùÿSÿgÎ_Ù_µý4ßûæ/þ3Iý•ûQÐgMÿ¾bÿã5ÚGñÛÁÒ|k—àx N-ëÆô¼?c0‹…¶òwy›üÝÏ»1´{W°½å¤v¦öI‘mÂïó ›}wtǽê¤è"¯þ aþ®GþÔÿÀÙó_ö_íEÿA7þù‹ÿŒÒe~Ô_ôÓøÍ}!§êzn­Ú´«¸o!Éá‘d\ŽÙREy'‰>9øGÃ?¼'ð‚ñ$—QñmµýÌ7Q¼?f·zu˜™†|áp§ž¸£ýTýUÿÁŒ?ÕÈÿÏúŸø8¿ì¯Ú‹þƒoýóÿ¤þÊý¨¿è3¦ÿß1ñšúf ้g¶‘eº2ÊqÇqÖªYjÚ^¤óE§^Ctöçl‹Šå£'ëGú«ú «ÿƒ«‘ÿŸõ?ð6|åý—ûQÐcMÿ¾bÿã4ŸÙ_µý4ßûæ/þ3_OQGú«ú «ÿƒ«‘ÿŸõ?ð6|Åý•ûQÐcMÿ¾bÿã4ŸÙ_µýtßûæ/þ3_OU SL²Eyw Fà²HªHéœ8âõV?ôWÿ0ÿW#ÿ?êàlùÇû/ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú i¿÷Ì_üf¾”µ½³¾S%”ñÜ*œú ª¶ºÞ}u-…•ý½ÅÌë"ŽTwO÷”GãGú«ú «ÿƒ«‘ÿŸõ?ð6|íý•ûQÐcMÿ¾bÿã4ŸÙ_µýtßûæ/þ3]½ñ–ïDý¡ü=ðbK›«øPÖe¾y ¼-e4Q„Û»H’Iã쇉´-3÷~+¹½Œé6Vò]KpŒ1 JYØÈ zQþªGþ‚jÿàÆêäçýOü Ÿ?ÿeþÔ_ôÓøÍ'öWíEÿA7þù‹ÿŒ×–èµ§ÆOévþ;ðoÀ=WSðáW¶Ô©o¡=«Ÿ–xôã%Hù€ózw¯¦¾2ü]ðïÁ?‡:ÇÄC5å®R½­±í2 fHFÅ‘ÑNÁ9aÀ=ø¦øMÐE_üÅþ®GþÔÿÀÙçÙ_µý4ßûæ/þ3Iý•ûQÐgMÿ¾bÿã5ôâ 3Äze¦§§LŒ·pEp:³¢Ê¡€`¤€yúU†ÖôeÔ×öâøŒ‹s*y¤¹ß¥/õV?ôWÿ1ÿ«‘ÿŸõ?ð6|íý—ûQÐcMÿ¾bÿã4ŸÙ_µý4ßûæ/þ3^Ùãÿèÿ¼®x×ZÌ–Ú…Ö ðÆÈ&™-"iY"Ê ¸PH#$U‡Ÿt/‰>Ñ^¿0$qÞõV?ôWÿ0ÿW#ÿ?êàlùÃû/ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú i¿÷Ì_üf»}㟃µß‹>%øGø¯ü1cc5ÜÙfKã D‰„…˧–w  Œ\wí ûBj®<¦h“Æz§ŽuÓ¬í¡¼K?ÞªS½ÑÔ†Î;}iÿª‘ÿ Š¿ø1‹ý\üÿ©ÿ²/ì¯Ú‹þƒoýóÿ¤þÊý¨¿è3¦ÿß1ñšÐøeñ_ãGŒÑ¬!ÖMCN³Xä¤e/§H‹†@s€Ù …#ÿA5ðcõr?óþ§þÎû/ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú i¿÷Ì_üf¾ƒSÓ®®¦±¶ºŠ[›ly±#«%ø×á¦ûkß&%ÅÌ϶¸’Hè e±(#!÷*à‘ŒÑþªGþ‚*ÿàÆêäçýOü œWöWíEÿA7þù‹ÿŒÒe~Ô_ôÓøÍ}-qukin÷wS$0F74ŽÁQG©'€),ïlõ t»°ž;˜$Y"`èÃÙ ÑþªÇþ‚jÿàÆêäçýOü Ÿ5ÿeþÔ_ôÓøÍ'öWíEÿA7þù‹ÿŒ×Òêzm¬sÍuw)j™ÕD`ô.IùsïŠtZ…ŒâÌr •ßWÌ^»—‘î(ÿUcÿA5ðcõr?óþ§þÏ›¿²¿j/ú i¿÷Ì_üf“û+ö¢ÿ Î›ÿ|ÅÿÆkéy.má’8¦•å8Ef±€õü*z?ÕXÿÐM_üÃý\üÿ©ÿ³æ/ì¿Ú‹þƒoýóÿ¤þÊý¨¿è1¦ÿß1ñšúzŠ?ÕXÿÐM_üÃý\üÿ©ÿ³æ/ì¯Ú‹þƒoýóÿ¤þÊý¨¿è3¦ÿß1ñšç~9üjøÍð‡ÆÞ0xwE¾ð?ˆõýCŽéîçо¥*E#˜ByxB[oÎsžµõó2¢–rTd“ÐCáHÿÐM_üÃý\üÿ©ÿ³æOì¿Ú‹þƒoýóÿ¤þÊý¨¿è1¦ÿß1ñšú3NÖt}_Ì:Uô¢#‡ò%I6ŸC´œSµ[KÒ!:µä6Q1 y%$öË3Gú«ú «ÿƒ«‘ÿŸõ?ð6|åý•ûQÐcMÿ¾bÿã4ŸÙ_µýtßûæ/þ3_J ë#j/…Äf`K¼l ô;º`ö¯"øUñËÂ[Å1èË%‹xS]¹Ð%O7Ú¤Ne‡d˜›ÍIÁ$(ÿU#ÿA5ðcõr?óþ§þÎ'û/ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú i¿÷Ì_üf´þ6~ж¿ u½Àžðåç¼sâ“#iú5“¬La‡ýdóÎà¬1'v ý0 ?~$üLñÍÖ¹¥|OøoqðúÿH¯køµ+KĹóÔÜD‘å£1þñJñ¹y9áÿª‘ÿ š¿ø1‹ý\üÿ©ÿ³”þÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯££Õ´©oŸKŠò¼ˆe YÊ£Ô 9ð¢ïUÒôþ/ï!¶åW÷’*rç >b9cÀõ¥þªÇþ‚jÿàÆ?õr?óþ§þÏœ¿²ÿj/ú i¿÷Ì_üf“û+ö¢ÿ Æ›ÿ|ÅÿÆkéµ=6y.b†î)ÌâuWRb$dü¼sÎ*´šþ…sË&¥l‰k³Îc2™Êo9ùwvÏ^Ôª±ÿ š¿ø1‡ú¹ùÿSÿgÏ?Ù_µý4ßûæ/þ3Iý•ûQÐgMÿ¾bÿã5ô¼·6ðEçÏ*G30 ÏNOç |Mãox^MWÇÚ^ÔÖöà\«[E)X%Þ¼fDˆíœQþªGþ‚jÿàÆêäçýOü ž[ý—ûQÐcMÿ¾bÿã4ŸÙ_µý4ßûæ/þ3]¼Ÿ¼ÆH~ çRŸG}h^ !û Š9–o3˜Kd ¸Çzï|Yã ÂÔ|W¨Ê$µÓíf»ÚŽåH»,{˜)b=hÿU#ÿAðcõr?óþ§þÏ þÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯Zø_ñ/Ãß¼ áßh[­ ñ„„V³¼iŠ;„EœPpØ$g½v—º¶—¦—R¼†ÐÎÛcȱ—oEÜFOÒõR?ôWÿ0ÿW#ÿ?êàlùËû/ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú i¿÷Ì_üf¾$(,ÇrI¯žþ |o>ø“ð»Á:½®­eãýJúÂ{¥›q¶û¤—M™VbÉ´‚F3BáHÿÐE_üÃý\üÿ©ÿ³û+ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú é¿÷Ì_üf¾ž¢õV?ôWÿ0ÿW#ÿ?êàlù‹û/ö¢ÿ Æ›ÿ|ÅÿÆi?²¿j/ú i¿÷Ì_üf¾ž¯“ÿj/‹¿~ xj÷â‚ü?¢ëÑ­c–ù¯®§†ðJóy[bŽ4(ˆC’ÀçcþÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþËý¨¿è1¦ÿß1ñšOì¯Ú‹þƒoýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþËý¨¿è1¦ÿß1ñšOì¯Ú‹þƒoýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþËý¨¿è1¦ÿß1ñšOì¯Ú‹þƒoýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþËý¨¿è1¦ÿß1ñšOì¯Ú‹þƒoýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþÊý¨¿è1¦ÿß1ñšOì¯Ú‹þƒ:oýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>bþËý¨¿è1¦ÿß1ñšOì¯Ú‹þƒoýóÿ¯§¨£ýUýÕÿÁŒ?ÕÈÿÏúŸø>X½ø{ñçÆ¶çCñ—‰--t™Hó…º)‘ÀçHÓ#=Ap=Ž+èo xcHðv‡káý/.ÚÙ@ÉÆùøÈcÉ8À èh®ü·!£†›ªœ¥6­Í&äíÙ_eèvà2jXyº‰¹Iéy6Ý»k² (¢½£Ö–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ 9¢Œ ÿÓýü¢Š()h¢€ (¢€–Š(¢Š(+æ¿Ú¿âÖ¡ð“áõ߆Üx»Ä’Å¢hê7<š•ùòâ`½ü°L˜èJ…È,+éZùÄÿ|Qã¿ÚSÃ?|W%£ø/Àz|¯£Ú,Œóˬݲ\Í@бD1Øn6òЙÞüøUcðWá/‡>Y°–].Ø}ªläÏy)ó.%,y%äf9<žõó_ÂèìýûSø«àó~ãÂ__ø}xéG…Ô­S ²¨ à×ÞuóWí5ð{Äß¼3¡ë?nm´ÿx+U¶ÕôK›¶dƒÌ‚Í ¬Šì#–"ÊÀ)Ï€I 0gÒ•¬)m"ùW’`”ûäÕ«G¹’Ö/"XnȈûÕ™Cawx=p*Ç^µ  Y¦|-ð%€ýˆ/É?wJñ}¾{šù×À²ø“âø#Qù¥ðÞEŠNñN·lðʧ±G ÀûWØ7?²•‚[ê~ð÷µ½ÁúIJKs¢[IÙȘæX£wC"Fý †Ázצèß|-áïˆZ_ôy§¶mE]ÞÈl6ën¬1%w—ã®îzžkÂú…IrFQÒ)/SõÇÅØJÅVÃÕ|Õe9ÇF¹[NÉùÝôºÓ}OŽ< ãëÏüoø@š÷îüGáÛ=kHÕáo¾—–Q˜Ž¸‘pãÜ‘œƒ^×û¾ñµµé_íx|Uª Ñüa÷/ÞïÔ63ï^„?goÅñ¸|t³º¹¶Õ™–Ö?,[K!ŒÄd#nàÅqœ6 êNkø›ö~†÷ÅÚü âÍSÁ:ž··û@iæ'‚éÐ`HñLŽ¢Lã×®kJZ°—<•Úo欕ýt8s\ÿ.ÅRxZR䌡ZvŒ”ç'5å÷ÚM'²ùyïíyg©j|.±ÑïΕ}?ŠmÒ µe0Hb,åm§œ pÞ2ðÄ_ üpø='޼w'ŒcŸUº$š}µ—ÂÙ‹06ànÜ8ç¦+é ÿšf­£ø3KÕµíJúojIªGu<‰$÷S®ü‰™”ü§yáq€ÔøÃáž•ã?xKÅ××sÁqá ™n­ão—+MŒ‰7)8äm"´«ƒ”å)õn/~Ö¿‘É€âjzT°Ñ’qŒj¦ùï%%6¹•î¯f¼Ï…þ!xÛÂ^(ø×ñ>ÛÅZ–¡gi¡ ص…Œ·‰Ó6剌ޝ´ óÖ°!ñ¯Å? þÎV¾ ‹í 5+ÝQ†AÉkx’VAdÁõúð³á~—ð³HÔtÛ ÛNm_P¸Ô®®®¶y²ÏrÛœŠ£·æ_²ç…,ñÆ–¶2?Þ¹Ò.$k õØÂ €½}9ðyî øíñábŽ95¿?N’Ù&máû<žHÝ‚BnáˆúW¤|Lýž<ñ?Ç~ø‰©]\Xê¾–Ûù{n „È’ïV;CŒpÆ®xûàvâïCãïë·þñDP}™ïôÖLÜAÚ9ã‘Y$¶Fzz ]<XO™j“ÓÒÍ}êÿÍŒâŒ'¨ÉòÊp|îÎÊnq›Û^Y8½¶¿‘óŸŒþ*øÃâÁŸ^ñ¾‘g¥j>ŽY”Ï5¿ƒüªÇñntÒ†0Óÿ±¬J‹*6û?šW~ü›þ÷~µØé¿³o‡m<ã _kzŽ¥y㦪js´fâGS•*¡kŒ‘µ™¦þΞ%Ò,­4Ý;â׊!´²Ž8a‰ZÓjE…U¸èœpõy”æ›Ó£·W½šèe[9À{ áð³Œ=ýê|ëàŠn<Ñ“W’o£µŒÿÉå|Kÿ°&—ÿ ÇQü h¦ý¡¾8M¥:p¼ÓÑöýßµ,N%éÆC‡Ýß=k³ñWìéoâ/ˆz·Äm3ÆZׇou¨ ¶ºN’•âUUCÙÆvƒÙÏJõ†ÿ ü)ð«ÃçþÒ)ek‹‰¦Ë=Äîievå˜à{{VÔ°õ9ÕÕ’“}ÿÌóó ï õiû9¹Nt©ÓµšK“•¶ÛßXÙZûÜïëàOÚ·âÿí!à/Úé~ ÑdÑ>K 5ÿ‹l´ó®ÝÙ’™‹p# ûÉÓ=E}ùEz©ŸŸ´~yüð÷Â)ìõߌÿ |yuñ¯âŒ:MÚÂú¶¬ÑÌ]×z[}‘¶‹H¥•AxÎÏá;x¯Ÿágø§â‰ƒlˆº·ÁE¸‘Ò?YY¶euq–Ö˜Ê&B8b9ÀÁ¯Ô‹?„_ ´ïÂÎÓ|3ce⦂[wÔ-áXf’)Ê´‚B˜(¿3‚Ü WQâ_ xgÆZDÚ‹t«]gMŸýeµä)4·ýš>ËûrÝ|=“Á–Gëà6Ô…—ï<¯¶iE÷³»cëŒv¯ j o„>øuà…zׂn|] ö¡k§xwÃZ}ÃZ¬óZÂÞZ<¾b†(Á-½¶à|Ù¯Fø«û9鿼u¦|PÐüU¬x#ÅÚe“髨èòB[7s'“,wM(rHÊç8ô>'þζüá=ZñV«oâOËou§øŽ j"òü¶¸uˆY¥.¢0¹<8£›`±ñÂÍ'ZøUûhxúoaø_§øÇDÔÖûI²Õ>ßmx-#yaáHäFM¡€É¹ù›=ÅŸÙçà«þÙß <<Þ³:wЬ'ñÖ»âïh°\Û™õ·ËÄMA Q"Ʊ‡b¡1’rūо2üѾ/êžñ:kÚŸ…+xKâW€…㟃ðw⦯}ãH%ÜdÔï|˜¯Ú_9¥ŠPÖÑÅ—¥\ h××αªˆ‰‚…¢1Â$ÆÑ$m<ò¤Wèhô©hb×Ãß¶6•ð;KÓl¼Yã?Ûxëâ°#Ñ<9§9>ö໼Qaq m#<ÆÐNHÈ#îùCâ·ì²Ÿþ-éßl> k¾×4?û:ÐiâÎH ™ÚGn­æØòîòàª;QÁœ?Âσþý”?eï?ÄÛ¶™om/5?µ‘ehн½’‚6ª)ÇŒyÈã |LðÒxá—†>0ü7ø-ÿ ¶+-SGŸN×?·<ÝI¡º¹O³´«QÕtŸÚŸö#øpú¶‡â-+âGÂÿƃû>þÙ­5[m=X.Øgˆíb™ãx~8Ø~Ý>øoñöXÖ~4Ë¢Ås¬C¤éóé—²†ÁåÍ»í «rS]]ÿì[ªø›KÁÞ>øÍâïø-Z?7Gº’ÕEÌqV)îc…g‘8Ëd÷5õg¾xKÇÿ5/…Þ ´Ï‡õK?°¼0±Œ¤@ž[ºÉ€Tö QÍÔ,yÏÁ_ƒÿ >øZÛÅ^ ðý®ƒs¨éV­}qüȉ–Ë7BIâ¿0<_áøÛàߌ>(|'ø-®‰z…õŸŒ5 pÛêM5£»Ø#/$»D¨vÇ• Œ(Æ ~£|&ø%ª|1µ»Ñõ?ëž3Ñe´K+k cì­¤1å@á‚)[)„&Gn¯5àƒöÒ—Âڟþ&xž‡w¦áâðür[-¼;<  ¼“+"JÞ`›i`7ÓŠ3ðŸ†>3þÃzÄO‰ºl^ ñ›àµ {ÛÆT½m3{N 7³ sÆ3Ú»ÿÙàŸÂŸ |.ð?Ä_xnÖÇÄš–…mö‹è÷ù²ùñ«Iœ±1ž+ß|+ð¿ÃþøM§|‘åÔôK %4fk‚“Û,>Co(ndë´é^qðkö{½ø/uog¤üDñ³á‹ fµ³Ðõµ–ÖÙ 6Ȱ-Á(t„`ò:R¾–ÍO†¾ ñgíuñ âw¾Åñ&÷T×/ôÈ®nõã`ÚMµ®Ñ¥¬o%¢‘ 99÷ý,ý•|;ñcÁ_t |l‘__ьРMÀ¹f³F>AyGR©òû+’Ö¿e´ñv»âß„uï†çÅ›½RËLÓÙÏtß~á"º†O*Fþ"„éÓ©xsYø[ð^Ñô‹­[ÇZ¾›¥ßÉ Þ1»Ôoî]Õ>P2Ìä*ª€À mÜIäëz6½lךý¾£9C%´©2¥‘8÷©_þÀŸµ€³¾áïÆöÚæ½u&³m Úm¦¸Ž(R;2C aÿÛÝÚ¾Ô¨kR‘ó?í‰ã¯|8ýš¼wâÿ\5ž¯id±ÁpŸzÜÜJ´Ê{Õ˃Û®Káwìqð ÃZ/„1DÌqÿ߆ʩ<}ë¬*·ìS9¸›ã“‰<Åõ`§9–\Æ»ï‰?³:øÏâj|[ðŽ5Ÿø‚{Ó/åÒŒ ·¶‘±tW[ˆäUt$íp2+¨øû?øcöyÐuÿø[R¾Ôàñ¯6³3ê,²¬óÅL¡ÕT°>Pb[,XœšV€¿c/ÙƒágÆ?…šÇ‹~.ØMây%Öµ[;g»¸Ž d›çò,’JYšO½€ Ž}Oö×ð`¿Õ?g¿‡z=ýÖ•kqâXtÑqo1K˜­ü”‰™$ê$ç ×<ר_¾ hŸ|þðýýΣj÷×wÞmß—æo»ÈËûµEÚ¤àqœu4|Mø3¡üPñ/|M«_ÜÙÏà-TjÖ©—²yBíÙ.õc³ýÒ½Z…´< ǾþË? ¼{ñcà‡¿°üGcáÛ«d’+‹‰ƒ «,Œ“HêÒFÊ>2y‘Å|9áÿÙÇ≾ ø_ð/Ã$·ñö£޹kãsâbu o'Ùr× ¤gdªÌdü»$¸,kõLñ“y¡kVÉy§êÉoq ƒ)$R©WR=$WÆúwìy®xwM_ø;ã?‹´/DHƒH·–Ô›xIÏ‘ ÛÀn1Ðaò£î‘B4xçÆ¯KñGö¥øàÿвÿhxwR“[´µâŽib·2MxŠ–‰¥aÑ“#½tß¼ á~Óÿ~ørÏìþ½Òô«Ñ¥w·‰îàÙ: vb« vÈq…èªÕú¿Á- Yø·àß‹óê7‹©x.ÂîÂÞ ÈÐΗ‰å³ÌYK—C ž¹«Á½Ãÿ¼Qñ®Úþæ]OÅV6vÚ¿—öx’Ì>æÇ9b=1G0XøGÀß³/À]Kö½øŸà{ïØË¡i…sih|Ï.®ZàJëóç/µsÏn+¬ýµ¼7~Þ/ýœü'àmLøZéò(#¹ûËuXÝa›(ûWÈ=ë诈¿³e¯Œ~!·Å_ x×\ð‰®lcÓ®îtw·+um eŠê£% 0óNøËû7ZüfÓüš‡ŒuWð%À»´Õ,>Ì.¥¹,fY<È^0ǾUQ’xÇùµ ? ~üpðˆ¤Õ>"|_¸ñÖ˜Ð4ka.a§ªÊH"_6ÙÉPÛÐæ¿>¿gŸÙ«áOů„_¼]ã»i/õ£âAgv׫ébÚWxÞØ+…FÞw±ÇÍѲ¼Wß_ ¾xÃÀ~*‡ÄzÇÅïø¾Ú(äŒéú«YW.0ˆm£|¯Q†Ç­|=û8þÌzÏÄ_‡þ8˜øïÄž ²ñŠ5ëm[M²1$7ÖËrʬ¾|M$EÐí/ ËïÍ ù…ŠyðWÃOþÒŸ´wÆ=_ãU±ñ·5]FÒneZYÛµºÍ$þJ°Vy˜ðÄc‚qœÿÛ+ᇆ¾þÇÿð®~¬ºV—½§ýžW¶kÍKÍ;ؾ#y L¶@gŠ÷¿þ̾ ø…yñ[á×5‡>(Õ ŽÛRŸIò$ƒPŽõFâÞâ9º …pryäçKÄ_³nâÿƒV_üWâígWÚ„Z”ºµÌ‘Iq"øßÀ9Ôïîmn¾ß\_ZE—åN÷™fܬÛ@Jœ÷¯“ÿmo„ž6øõñ+àßÃI¸oÙêSêšÆª±¤pˆÂ)“î‡t€§’Jâ–á±ú ##¡¥¦GÅÄŸuQôSê ?1õß„¾ ø½ÿñfãûFÔôkØÜµ‰–HธN‰œˆËæ*nf ÜnÚ{W¢þÃÖ)áK¯Œ ´¹$>ð‡Œomô¸%‘¥û5´€0‰YË6ÕÀêNNXòI?IiŸ´=/ã~³ñÒû—Õu­" KVòþÌ‘A :áwï%prÄcµTðÁ+áåÿÄ=WAÖ/EßÄ=FmRyÊ&Îy§ú?ÉŒ&w.ðÜŽr8«oKcåü9ðOÃÛùôOé1è¶7¿îï&†åáõKpÒå¹ Åo~Øÿ ¼sã¿|=ñu‡„›âGƒ¼.×ͪøb;Ñg%̳¬b …ɦ ­ò3w ¸ŠÑÿ†4ñ'ü&£â?ü//ÂNº{ibÿËÒ|ß±4‚Sß°ìÚdPÙÛœŽ¸¯cøƒð_Æ/m ÷ÃüAá=GF´6’ÍgöY"¾Ý´™n`šˆÉ•ÈdUÆHqNú…*ý–åýŸ¼eáOø?á÷ƒ.ü^sgâ_jI4O ³Bc)å;°T’<ŒG´¸äø'Â_Ù“à&»ûP|rðn­à»­Ãöþ6¯æyvæî –ŸgÏŸœ¢“œôâ¾Üø#ðEø/½±âoøªéo5m_Stk›©Qv&DjˆŠ‹Âª¨~Êxïöc·ñ/Ä]Câ§‚|u¯|?ñµk¦£.ŽöÍÜvÙ"»‚t܃€@ó9W ;ûBxwMøŸûN|"øâÏ1¼ tÍSZ¸ÓÖGŠë›-‘ÛÅ)B¥– Ŷç<äqQüðæ—ðgöËñ‡ÁχÈö^ Ö<-o¯4Hò[Ù_Çr–äÄ›`™’£—°WÐÿgíâáðÖ³ý»¨øoÅ~v“L×4æná2¨I•–DhÝ%oB¸8íQüýŸ4¯…:ö¿ãWÄŒ¼câa wÚ¾¨cóŒåÁp¢GJI;Trq’qEô j|wá‚øÛûcüuƒâ2KªøAm_ì£4±ZÏw=¬ËÒ¬l¥Ì(²ª)8ýá$t®×ö”øU£üø[à‰ß4é-`ø3­PY¬²JÏ¥ê0Ô`!w*D§$"äôZú×Á?t_üMñ÷Å û›‹ïˆ'M7pK³Éƒû69#ÉÚ¡¾a)-¹›1Žk›ý¤~)x'á¿Ã«û/éW¾ '¶»Ó­´»+IîžþIa`Ð3BŒ±+©Áw*0x$ñG6¡cÁ<%¨Ù~Ðß¶øëJ¹Þ øI¢ÁŸ,lL¶¹™¤›ËµaŸBc#‘_sÞkº{o¦ê…½­ÝáýÄ2̉$¼ãäF ·>‚¾Vý‡>Þü ýžô?ë¶¿dñª_RÔц'¸ÆØßý¤Œ*yÚ¼¯âÏÂü]ýº~x‚]*æÏÁ¿4¡|ú›DV‹×•ßìñÈFÖ?ê²ÈèvlÇè•-T|Kûo0_ü+v8Uø‡á‚IàöÅë^“ûYxῇ~k?ðµ4ë½gDÔf³´m„ÕýËÜ#Ão£#.÷A¸† o¥wŸ~øWã®< âæ¸†Ùæ†æ ‹I<«‹k›vÝÑ>L‚+Ë5¯ÙnËÆõ/„ßüs®ø½.îa¼µÕ/žÙ/ìf·Á‰¡h!Òw«<Õ&‰±ð÷†5„¿~ ø“Ãÿ âø?ý·¯¦•p-5…¼kûKˆ™žÞêÞ<¡+´7™¸œðI;HöïÚsá/ŠõV!ðRÝ[Åo5ˆµžÐˆóûÈ㹆C„YHÏŠ®mBÇ)ðJýþ/ü—Ãðô‘øêòx®ô]DL$¶»Še–XdYvJ¡‚†Àà óŸìyû<üÖµO‰úΩá;‹ï |CÔ-ô¹[~ëHlã´–O›¤nK.sÏZû§à¯Á¿ | ðRø/Ã\Þ¬—^]Þ^Iæ\ÝÝܶ馕€³žxW™Ãû/á|GÕ~ |?ñ÷ˆ<%ˆ58õmWI³{Y,/nU”ÈYn ‘ÐL×òÙN`asn<¶ØÅüŠôêØ\|?„i¥‡%ó…LÿÝ-ŽØô¯¥ÿhOë>øãßøt²êz>‹}slWï,±ÂÅH÷‘XŸ?gÏ |c¼ÐüFÚ®¡áøeÝôÍoI‘b¼· Iή¼Ž¤¡9‹áÇÀ«ß Øø¢ÛâŽuŸ‰âËxm.¶Vð¬«åÁ qï7˜Ànl.OÊ)] üíøƒð#À_ bÝö…ðl³Z|KѬô¯/ˆ–æfº»½ºxša!.Uâ4ü„chÇrN¿í à_ |Tø±ñ__ñ†œe»Ð¾Å«iêd‘>Ç‹™ReÊ ÆÈÝ‘Œ‚&¾‘²ý…´²Ò¼®üAñ·ðãC¹K›O ]Inl€‰ËÇ’¬By Œð±³ãtÆ=»Å³ß†¼[â¯x²óS¼‚øgþk˜âò¼¸-¿}ûز„ù¿¾?x•àqë\ÄØüÅðׄ´_‡:ÆÏ $–£Æ?¯u-WtÒKö«ðÛËùŒÇÌ##É΂¿g„ú—Š~hWºKIeâ…2ëZ¤BæáVëTH +vàI÷Õ§r |«òà|£¤7?²„.nôû·ÖµÚwƒ&ðRäá¬æë9ýßúáÛ'û5¹¡þ;е¿k–ú­ô’ø3Âá+u+lÖ®±)šL >oîGÝ!y?/J|áÊy§ìÅá|lýŒþhßì_°ŸOˆ<7 Øcg3Ç %H? AŽ{U?ø'ÌZ|¸²·]Zø“_Š%É!cŽúEUäà¯KÒg[Ï üð·ÁÏ|A×<-oárBj6kd×—1“#åÛÉÝÒgåE?(ç®yï‚¿²¾¡ðFþØh?¼G¨èpÜÜÝˤ]¦ŸöK‰îË<®æ;T”ì_åp3íÅKkQ£æ«¿Ù›à7ü6½€ÿá ±þÀ›ÁwƒYþóËk¥¾HĸßÁIqŠú§ãÇÀÏ„š¯ÁCJÔ|1kqiàÝý´ˆÛ~,ÏNcÃgª/\ô­ß‹³Æ™ñ/Æ?ÄmÅ¿‚¼[¢ÚËc¥£¼"G´ƒ¼2Gq±º–PFWƒ^³¦xPÅà¨üâMJãÄaíÒêîóËYîÕÔ«´žJƘvªACXù7ö*ø'ð§Aø/ðÓâ–á«[_ê>²’ãPMÞtsn†brÄeÏ'Šøëžñoí ãŠÞ:ñÂÈ~%ºø‹QÐíû\6 ¤ÚX9Š;{{qŸ-ÂášU —É$çô7àçìåuðZúÎ×ÃÿmBÇȾ=Ò¾6x öDðGÁߊ7³i÷ž ñ†î.a¼óç]âi$Ž7¸M§q{Ž;×Gãÿ€¿ ~ þÖ_³ÍßÃ-8hPj—úœ6QK#Ã+[éÓç)#7ïfVqËgžkì cönðߊþ Éð_Æúö¯â8¤”Ý bòä6¦—‚cÑôyt«Ë} /ÛQ·Ó¦¼¶J ˜ñ¶RÁŠ€6ðŸÃ_F|SýšmüyñOøµájÞñu‰Ó%½Òü‰Õ‘3Ê–;ˆäC†ävÎXø%û2xkà‹|SãM'_ÕuÝKÆ loäÔåŽfyàS¾mêŠÛ¦rÎÀ œ(URº°[S䄳÷Ãÿ_¾=ê¿­¥×ôÍÅ&ÞËL’âhí"ž[HZ[‚‘:n‘“b Ù “‘±ûEüðwü-?ٛག\YøN uëfµŽê`òZE «›v˜?šQ€ÚÙl•ÈÏ5ö×Ão„7Ã?xëÄzUõÍÜÞ<Õ¿µî’}› —ÉHvE±TìÂórzâ“Æÿô_üEðÄBúæÞ÷À_Ék [<™Î¡q¸›r–ùD`®Ò¼“œÑͨrŸ!|6ð®ð'ö¾øà†– §øv÷Á6ZñÒâ‘Ú·GsqbW'iuŒ 2O­|ƒð§á/>=|ºø‹­|0‹Æ>+ñÃ]^/Šæñ¶»¶„_f‡ÈKvP0p@ÁÈÀ¯×¸>è°|n¼øæ·×'U½ÐàКÔìû0‚ 丗~òÒ~m¸Œó^¿² ׆îµ+_„¼Gðÿڵ̷RèúÙ%µ†YÎé ©¸…ÞÇøU°;MH\§ûDCñKÿ‚vëÖ][ÆZ¤:”‘È$\Eq ³ï\\ ÄŽä×¥ü ý‘þø^×Âu õ_Ù¤:…Æ»qwp÷7²Å‰Á“ËòÎö ݪ1¸n¯RñçÀÝâÀ«¯€úÞµ¨¾Ÿya„º‹Ê³j°27šòJ¬F(73)ÎM{e›§ÚéÑ1tµ‰"RÝHE Çâ§›B¬~JüPø^<ñgâGÄŸÚá÷Åo k÷Ksg­é÷FâM#NHö˜ZÏÌŽHü¼gÌŒp?ˆ_¦ µOjÿ ¼+©ü:•¤ð´ºe©ÓZBåŢĢ æB_r¨·äó^ ãÙÅÞ1¿×`ã7ŠtßxŽI^ëIˆÚIG7úÈ`šHZhãaÆÐÇŠõÝCÃöÿ>^øoᦑ-êø[Cš * ÓK<–ð0†>fi œd’M6î$KÒuÝ_®´-BßQ…k=´©2†ë‚P‘ŸjÕ¯‡¿àŸß5ߟ­tÿÁ%–¹â —Ô®­%]lŽ&^¡Â(fNWÜ5-jR–Š)QE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQE”´Q@Q@ KEQEQøRRþÿÔýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ+Ï>$üPðÂ}Ç^ñœò[Új:…¦— EJMÍëùq) Ⱥž‚€=Š( ¥¯=ø•ñWá÷ÁïÇ⟉:Ìz•,él³È’H ÒU1»d…=±Ås ¿h¿‚µ t‡>1±ÖuPÊÖˆíÏ–8.!™RB£Œ°RFO"‚ç´ÑE€JZ( Š( ¥¢Š(¢ŠJZ+Î>|UðÅ;VÕ|<—ú.¥s¤Ü™bhŠÝÚ…2( Ô ã 84èôW|Mø™¢|*ðü^$׬5-BÚ[…¶év3_άÈï¹£„3X𠸥øYñCŸücñÁRM.‘¨4«OC&è$hœ27# ¤sE€ô:Z­{w …œ÷×$¬6Ñ´Ž@É ƒ' ¯—öªøcðÏÿu¿'Ã>*wO¸¹·œ4®…Á_-™OÈÝ@éNÀ}EyÃOŸþ1Mqkð×Ŷ:íÕ¢‰&·‰Ê\"í d ž7mÆxÍzõ –Š(¢Š()h¢€ (¢€–Š(¢Š()kÏŸâo„£øÂžOøI$Òÿ¶/-¼¿±ù­ï3îîÞ¤mëÞ¸‰ßµ/À/ƒºÒxsâ'Œ-ôÝU•\ÚG÷“Æ÷ZXíc•£v žÿE`x_Åñ·‡´ÿxRþ-OHÕ"Yína9ŽXÛ£þ<Žõ¿H¥¢Š(¢¹ícžðö¡¥i:ö¯i§^ë“{n&H¤»™Fã*Äv®N( ¥¢Š(¢ŠJZ( Š+À¾,þÒÿ ¾ k–ñ…ýÔºö§\Á§iöW×Mn¤©”¤ÛW ¸Œààß)j–›«§ZêvÁÖ¸’T)G "†”ò§yv€ +ç_~Óß d´Ñ˜\˜œ£e‘È5è´€(¢ŠJZ( Š+…ðÄ¿xûQñ“á Z=FóÂׯ§êq*º=µÒ7«žTàŒƒŽ  æ–¸â?‚|OâÏø@Õó\ð¡€j–ȯ›St¥âÅBʤáX:â»z(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Žh£€?ÿÕýü¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š(åÿÚÄ·^Ð4[ŸøZö¿ ,&»hîn¤´Šòêðù µI»’ÊãŒs^!û0üzñˆ~7kß®¼j~#hQèÑêú~­q§:öYD3[Ì›Qd2²¸Q×é´wŸ‰ÚçÄ¿‡>iÚw‰/ü ý¡ Ú6§?Ù¢ž+õŒa˜¤Š’Çåõ+ÎF:W9ðŸá?ǹ?iëÿŸ-t2ÃPðÛi0iúeËÜ"·)"FÎÑÇæ–ݤG!BñšµkÔÃð¦µñãöžñWŽ5¯ü@o‡> ð¦±s¡i°ÙXAwu{qbBÏq<“yÚzàýÜe³m7×¼'û6x‰zÄ:ž§£ø§Ã²êšŒPùJ`¸-,â%û€¨,Ttè3]‰ðóöŽýžü_ã8¾è?޼ã Rmj kíE´»½6öïá y3,°³ ÀäóœWcñëá?Å_<á½ZÛK>(µ×4]OX‚9lDv³o¹HŒÙÀS… ÷»âõC'àÅï~Ó^8Õ>&ø{V]ág‡ç—N±Ó#mCTºAóÜÞ–íã8FðXŽTüÕ¬þÓþ!ø™ñÇ'ŦøW¡x_Q›IÒ--4WÔ¥½šÛ‰.î¦òÜË¢59àýÜeþ±ºø/㟇Ÿ´e¿Å¿ƒ‰j|5ã[iKä!’<uP=G¼\‚rÀ¯£ü6ý ¿gïxÊ?„>Ñ|yàßj“kpÛßjL¼Ó¯.•Dé¿É™e‰Š1‚9=Ih=O•þ/ür×¾/þÈžñ׎tÇ]kEñö™g¨Ç» »{6iBø'Ή”…o%G·“Å~ý¢?lo†mðÏÂ÷~¿øz·Z–±q«ÚÅ¥ÞÞÚJm÷fQ†FnŠ$9ÀäýñƒáGÇo <§xŠÃE°ñV™âÍ?Y½¶²¸ZCci3¶Ô–E-$¢2¹;T3g ì¿io‚þ1ñ®³à_‹Í´>>ð¤’Âne0Ew¦Ïò]ÚÉ"©8e9\ñ‚ê0_4] ÇË~>ý§5Ï|lñ¿ãøšÿ ¼3à[…Ó kM!µ+ÍJùr&‘Ûc¢E©AÉ#Ó~~Ö¾#³'¾(|A·]WZøs5Ý ¹Ž³ƒXòð¶w Žªc[†e Àä £hü/øïðWâ¯|wðsAÒ|gá߈·êWºV¡|tû› H.%x§ò¥I#±$ÀÇB_×&øñãWÀÿü?øëc¦xzÿÄéqo:4r–2!žYù²¤ ¹!UH`rhm \ó/†ý©üK¢øgâ—‰>-¤7ú°¶Ô.t(ô˜˜¶“íÚ«äÌDØórHnÇïÃKøão~×zÇÂ__ Ÿ x¯C‹XðØ1ª˜&´o&öÜ8¹ÏïNI 2\·ÃGý²ü¦xsá–µáo ÞØh¿g²—ÄcS—÷¶ä&ñcä‡óŒKŒq»æ=Åj~×ß¾$üMÑü5⯂wVö<ð¥ÕÏØç¸“ÉO²êïmr»ö¶ ®¼}åµ. jþÌßü_ñ‹Xø“ãÛÛñ?ƒ^—KðÜ*ˆìºzˆ¥¸gÉ—RI㎕õy'À†Ÿ¾xS᥮Ö}Æ(®>ì—L7ÜH3ÎVb3Ð+ä?ÙkâÅÿŠ?´ßÆ]Zû_ŸQøcáË«+N·'’—Þtl"ª 1D¿-ǘ½sÂjû ýªzƒJ–/ Éo"Æåe“î#8fÏ`y5r¸ßˆ~—ÆþñƒmîÍ„ºæŸud· Ö&¸‰£1ýÒÙ©ù9ãïÚCÅ_ |;yãïþÐðüFñ‹*½ÞŒš"&x« Y­â¸[É#'æ}£ûCü`øcsðÛáÁÉmôï|TºxâÔ.㦟eonnn&ȱ‚Tƒ‚:ÃåÏ| ý¯¼Oû3ŸÙš×ÂþÐm4Ë(¬Î« Ìu(튔T€[JFdvfÆ-‘õ_Ç‚>ñNŸðãÇ_ o--|}ðÆU¸³Šô·Ø¯K"æÚWQ¹D‰• :z£±'}ð³áׯ?x–æo|K“ÇZÕ£ÞŸ ­Ì¢D!ÒHxh™7‚„eN9lñù‘ð7ãæ½¡þΟ g¶¶øãOk’G}~1g¥Ù‹77LÂ9P®K$àc?§_ |CûDxƒÄWR|WðŽá- R±Ge©>¥u=áuÃoò¡HáT ÁRÌH9~Yðïì]¬jŸúÒ4½CDðœV«ªM­^ÚÛšúuH专iÝ!H¢äôUžµòÇìÿ&Å ×ö±ÿ§ ëèO…Oñ&óáÞ—Æ+KKO$>MÿØeómæuù|ä;Wo˜>b˜ùI dkçÙÛáçíð.ïþEÞ—¢ë_áÔoî­µ…¾–-B{¹d¸XÚÐÄÊì®ûr$P®*z ñÆ´í á‰| ãh|;¡hº•†… }‚àÔ-ôôùÍÜÎw†¸h*ß#GWÍsêö~ý“?dnòÎmB×Oñ\SÉooŸ4ÉÍÃŽ/ãv{ž+ë'àÏí?ð‹Møð‡á%†‰©ø?Æw×ךf±{{%½ÎŒº’í•Õb<Ä0c!”ù˜vÔOìµñZÏàÿìëà™c±]Ká—‰ Õ5€.Im£¸–Sä¶ÏÞ6×ayïWtIÈøĺÇÏÛoÃÞ-øiá™üÃí&ìk‡S‚=7PÔ>Ø<¸TY«8Ïkxƒ€u_~Óú×þ.xóÑüR“á_‡|&‘cžŽÚΡylJ\O<›%•J¬jAeÁàä×Õß> xÇZø©ðëã—Âd¶_øZèÙjqÏ/·úÎ|è™Â¶^#–ˆ ³sÈÇÃ/?¾$øëÄ\œŸü2ý£¿fßx«Jø¡hÞ6ð/‰uµk;;ýAôË&æìæhƒ¥YaÝ‚£†½°‘è_´¯ŽüOð·Ãþø_ðU,ô{ÇÚÜZ%„æÝZßN€«M<é±UUé–í\—†/ü#øËñ?áÇÃï¬ÚU—ÅêPkqĆA¥ÍpªÉ5®óºA£`?\€zV„>üqø•ñïÿþ6ég„ì< awm¤éZ}ëj2Mw|¾\×Ìb‰UDyUP¹ç9ê)t×|ø•ãëßl¼E}ö˜|#â{;MZ'“l–PJ©•vØå²y¯˜µÚ/ã4_°^‘ñ›OÕo]krÙ‰Ú‚J‹­Oh‘”($T${ç<צ†Ÿ´¿ÃO‰Óá^“¢k>ø›z58¯ï虜—Jº’Ý-å/Å!@@Ê.HÁ<ñËYþË_-ÿb |œØËâÍ+[÷ ö‚-Ú/í™o‹ 6}ヿ{zz§G.¿ñ÷à·Ç¯…º/ÄÇãMâl·Ö¶cOŠÎ;+¸-ÌÈöŒ„¿–…Ã’Jä·$çÿµ€¾#ÉñçàÝÄ?n¢‡[ñ=Âé‘ R4†òÝ#3p1‰r9¯©¾6|'ñw~-|ñ—‡ÖÜé¾Õ¯/u#,»$On"O)pwÑ‘Uÿi„¾<ñüþñ¿Ã²“ÄŸ5¡ªÃg¨HðÛ^Dјä„ÊŠå ùpÛOïŠIƒFÆ_ˆÞ2ý•?g=wÆ:þ¾þ=ñ$3,V–ÑZ†šé•"GŽßh+ÌÜ|ÍÓ#¨øòÚkÇ¿uOø•>,IñMµû{Oèƒ@’Î(`¸Iqc(‰`ûù T ¨ûWâ?Â߈´—À gÀŸtëøšêa-—Ø/[S·†[fY-åiL07ÌÀ«(L¨äx¬Ÿkÿ¶uÅÞ‘¡x‹À>³X¦…5-iuIfŽX‡›$K 2Èë’ªÒ¬Fw)«2ÿk_‰?|ã?„~ø;oc¨xÛT¼Óæû\BX0!R²8ÆìE¸¾Äx5ìŸ ¾ü`ðF¯©Oñ âSøóO¾… QM¦Ãbö×!‰vF‰›1²œ?wMsÿ>ø³Ç_~øËAXàRöïQ2˲Aöâ4ò×yÜ9ô½Kz (¯Ï_ÙwâÅŠ´—Æ­Vû[¸¿ømáË×Ò4Ëyvˆ’ö9H!‚B,m’IûëŒöý ¤Õ2)‘ä‰Ò7òÝ”€Àgi#ƒƒé_“š'ÂÏ‹³~Û!ðòü[½MvßÁpJÚÏö]‘™íÞî!ö$)Wx¸ ÄŒtÍ~´WÌz?ÂÙ~Öú÷ÆiÖßþ½GÂÐi.gûTw)+f=¼.Õ?6ŃGñÇÇeø›ðãöpøiâ$ÐõoÙÝê:¶¼öÑÏø.ôYÕÇ"2‚¤©œŠ§ðWáoÅ[¿&ý¡þ4ÚØhÚÆ©¦A¢išFŸpnÒÎÆ9Ò4·<’HüªñOK ©Ï|Vÿ“èøÿ`o脯¸»WÉ´ŠÏÄo‡¿~ >÷Ä^þз—NÔå{x/m5Dp&Es¦Ó‚Tƒ»ÛéÞx·Pð¶ŸyãÍ6ÛG×¥Bní,î ݼO¸€fH˸9(98¤ú &Á<¿äÕ<-ÿ]¯ÿôªJó? xƒö•ý¤âñÿÄŸ‡ž?OèþÕo´¿éqið\¥ëéàfK¹%$âW;0¿t‚q€zWìßðßã÷À[¸þ]iš6³ðÞÞþþ{]eo¤‹Q‚ÖròÃY˜Y]ƒ•R|ÅÀ$óžGø_ûS|xஓ¢x‹Âþ.Ô®õM*úúýìî4y¯Æ%Y!8P€É´®[9á°µÕˆ§ðëö¤ñÏŽ5ÙëÆÚŒÑØè_ÿµôMjÎ8Ôĺµ¢?‘*3ê$’,"nèø9#5ÓüPý«ì¼iðÒÆãà¨ñkš¿Ž,¼Ò»–u˜KrQxd{tmŽŒÁÅbxËö@ñ–“û&ø7áÃV øsâñ ë‚ÛÜ‹y<ؼÀÄü¯…Èç®x÷í¥ðâWÅýú×Á¹ím|W¢BÌ½ÔÆ};V³’Òé7…nv°+Ç †ê+êÐâdèµ}SàoÄ¿PêxsXøªêúÕ®¥~ŠðY[Gº )%W áTÉR@àç­xÏí-­ü9×<)­xcãŸü-ˆu=jÇNÕ4éôe¶´’ÞöQ ÍgsmC;‚ï`Þ½Û>ýžgÖÿd»¯ÙÏ·±Ø\¦‡m§ApÀˆž{mŽL€vLè|ÌŒxí_2xëáímñsÁ^ ð6­á_ ø?Lð~©¤]Ü-¦¢Ó¾¢¶'ú° E“nX³aAÍ4-Opý±>)|Uøn¿ í¾Ïjþ(ñ-¶•äÝ k{´ ¨’œX÷]— 85ÛxÀ¿~\k>&ñŸÄ“ñÒn4ÆÒ¡±Û©Gµâhb#e†2:rzý ~x·âG‰¾j¾Xø¦ÓWÔ<é|²-aûÞXÚw7¢ñõ¯¢õS©.ðè¢&Ô2}˜OŸ(Í´ù{öàíÝØ9ÇJ›èUËÙ×â—þ0ÝèºÎ·û@Á£xÝõmOÁSé¶Ñ,0Gq‰,„rì›ÌxFUɲã5ú±_™_¾ þÑ´=ï…ôü<ð·ƒ.4}RÒþëÄö:‰»½T¶pî¶iöt‘ ¸èò8Îz×¹þÝ¿5ÿ„ÿ³v»âêÒèÞ$»ºÓìôÉá dó¤ºäUÜË[¤£§OΛWbGØ4µåŸ-¼uið‡Â0|N¹{¿6ÜêRÉírÊ÷à¸gr+ÔêAEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE%-PEPRÑEQE~”¿…ÿÖýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZÆ‹Ä~žüéPj–²^‚WÈYÐË•ê6»#ñ[4QE”´Q@ÍÕµœFâîT‚%êîÁTg’x©è)h¢€±t/ xsÂö²ØøgJ´Ò-§šK‰"³‚;ty¥9’FX‚îyf#'¹­º()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¤¥¢€0ü?á øKOþÉð®•i£Xïi>Ïevños–m‘ª®æ<“ŒšÜ¢Š(¦³¢)w!UA$žÔÖNâX‘àÒu+kÙ#™`™$*:d…$@ô´Q@Q@ KEQE”´Q@Q@ KEQE‰¡xkþµ–ÇÃ:U¦‘o<¯q$VpGn4§/#,j »Y$õ­º( Š( ¥ªÒ^ZCq¤³¢M>|¸Ù€gÛ×jžN;ã¥Y Š( ¥¢Š(¢«KyiðÚÏe¬òÇo"Å8x]K…,£ í;©Úö qðGíâãñKIøEñ§ÀÍàgÄöÓÜè³Å}¡ixm”¼Ð"Ë™Áàãƒ÷wGãÚGÅ75o„¼ þ:Öü5 Sk72^Åaa`g¢…¥pÅåpÊ£#û­·ÃþGûøƒâ߆áO†µíGÄv=ÄÓÚjÑÚéŒ;3O&¡åª—ËC’ÅArÃÞ7µý”¿h?‹ ñcMÔ`ð¿Äkè5½'\´°¹¿·2,e&µ—ì±É":6‚¸àä€É¸°\úª/ˆ¿/¾Ùx–Ãᔑx©î¥¶»Ñ.õ(!òR't3%È ˆûU“oUaèkåïØcâwÇŸü/ð¼^#ðÔÚæ‹ssx³øŠ÷YY¯ÖCJÛwNG÷Ã_ˆzGÅ?Ûx×@³¾²Ó¯d™`…³ZO$qHщ|™>uI6îMáX©¨ö ¾Y`@<ä¨>óá½7POÛóÆ:«ÚJ¶Oà]:$¸1°‰¤¬JÆÒÀrFsŠvò œ‹|mªþÕÿ²×Å ßFºð—Ž|)%Õ¬úœ[}WJe¹hød” M¬qA<ŸÆŸŒ—¿aŸÜøbñã×þ+ @¶‘‰EÍã¹`WU"•Ž:b½cöZÒ¯­¾'þÑ ¨Ù˽÷ŒKÄe‘&ŒÚD !`/lŽ+ä‚^ñ„ß´¶ƒðXÒnaðÁ­oÄ~"´¹’6[{ˆïš#§ˆÜ¥¡yŸC†¬#ôÆ^!ø§ðÎÇÂþøAðñ¼a ¾ž±=寥­¢Z*EHÒ†‘ÝÇ#jžæ§ø!ñÒŠz¯Š¼âMá/ø*h#Ôôé&Žê?.íYíç†xþWŽ@Žy¯•¿jmJÆ×ö‹ðÕ¯Æù5ø~I¡9·þǦÚMoÏpëxºxi›÷;<°FÜ“ïT?aÝ3M¶øóñ³Sð·†5? øZúÛÃçJMR;„š{uƒÎÿHËäî NåR»€'6ÐwÔõ+ÚßÇÞ0ñ¿¾|%ø[sâ}oÁ­Åܯ’AÛ¯4 ~òv‰A !bq]ÏÄoÚGÄ~ðçÃëEðÕ×Ĉ“}–Ó@{¸bû4ñÇæN&º$Ƕ1Ýrr=ùŸÙI¼ÓücñúêöÎ[cyãíAâycdóaEµ°—$àŽ:ÖÇíi?À9tÏé_´½UìäžI¬5]*ÚîY4ë˜ÂÞuˆi¡gÚB•8äŒ 4½€õï…0ø«âs«Z|Sð(ðmÕ€ÛÉ üWö÷‹(}Æ6ŒSP:¼1‘Í{ ~|~ÅZ׌õ/üA±Óu¯x—áE‰²_j%ŠT¼yØ9¹HžtI¤‰~_šAŸ»êkÒ$µKÛ Fs¨Ãn²¥¤Œ‹öuuÝ$ÒvòÀà2K^í§~Ðþñìã{ûFx^ÖKÝ2×G»ÕE£°IwÙÆí$ ÃpV …sÓ¡èkà߀ߴ—„¾ø«ãîŸã]+U‘.IW¨|=øoã/ÿÁ:<[áÏéÒÚëúŸ‡üAzÖ K ^Ç4‘ÃåØ#äê Áç"´q%3Ô¾~Óþ)ÅáßxKàõëxY’}RãP¶†`’²\EjÇÌx#b~c‚ê7(Á«ž&ý¦¼o¨üBñW€¾|;—ÇgÀž\zÍÛ_Ãc ܺyŸe·ó3æJ8¦O´~϶o§| ð Œ5³Á¢X#FÊQˆ*Tòô5ñGþ%é?²wÅŒúÅ­7UŠÓźôþ$Ñ/¬´ë›øµ¯³#[£[Æûe‰ÛËÃí^ÌÉ_ =×Zý°¼)kû/\~Óz6‘qwedðCs¦Êˆ.î;IárA¢g'§ vÏšíSã+?ø7Nø­ðÞçÁ¾ø‡:Ú躋ßCtËs*yÃw`Âòº2yàó>=ñ‡„|aáOø'Ž5_é2iš¯Š¼DšôzlÀ‰ P×-å†R2‚21Þ½?ÇŸ¬¿i½WàßÃ/‡š&¯¯¢ø‡J×5ÿ¶é×6qéi$K$rK,jŒîãbylÀõÎÍr¡\ú_âíâ»_Šw¿þ xüqâ-ÊýYÞö+ K(î òci$´²HP:sÓ$t iO xóá7ˆ¾'j:m΃?ƒ$½¶×4¹ö½Í•Ý‚o–,©ÚùR¥`G½|ïsã‹Ù›öªøâ¿‰Ö:Œ^ø‘k¥Þiº­…Íü"âÂÜ[KhâÚ9€Ëòàç®HΟìÝeuoð÷ãGÆ_øbú= â·«ÛèïnZöm(@°‚m›iß0V!3’¸çš–´Ï@øWñÿãĵðÏŠ×á³ð?ŠŒ2A~5{i. ³¸¢º–Ûì*A*¤¸+Ò~'üAøÏ x’ß@ø_ð×þ«v·ϨÜêPiö‘¹b¢4Žø²ª@dŽÿœ~Ôü¢|Røy¦þŚόßk6É®hÖú€Ñm4ƒ&ë¶uÔ"U‘wm1Ééü$ú§í©øzÚ~îÓö“ÄKðßûݼ=”ºƒéòßn?j §w— Mãñùuϱ>üj²øã¥ø›EÖü?7‡'ý­|MãßÏ$=ð¼ž#³´y¢¶³ºÔÌ6jŠNÜ}šØ†Ç=O¬xá÷†>4üný¢´é7~ñ¦™àécó¢š×ψÁu2”,•Д$pÈØ â» ø\xwöê½¶Òté-t;‡6Î#aˆoÜ,Añ·r¨g8æ›}7Ã_µGů‰£VñGÁß„Íâ_é:„ö y&­oiwxÖÌG‚ÞAŒ`äažƒœì~=7Â4ð¶ ø~oøÏÆ÷Mi¤iJ J­<²Êÿ,qDw7©²GæÄ¯ü!²‹Qñ?ì«qãŸüXÔu–?ZØê1Y\]Ë2‰Åͬ‘½R…ÛtruÀ\–¾´ý¡m¼mை?¿h½gE¹Ö­<í—‰`Óbk™mN§mms1ä¼qÈvÞ~àÍ.QÜô¯†´·‰¼]ñ¢ïàG޼ 7„¼G¦é_Úw ×qÝ[º´c0”ùP®Ï¦´ßÚ§â&“ñKÀ¿ ~*ü/›Âú§Ž¦˜ZÜG¨Ãyl‘EHrÑõªWk§NAŠûv¿*üqñÇCø×ûPþÎóx?GÕmô?SÕîõ ¬7\½‹o·EUœÄ«—exc_L~Ö?´Î§û9Áà•Ñt8|A{âýSû=`’fÕ\È‹ΣãÖ“ˆÓ>¼¢£‰™âGuÚÌ ‘èOQRT|¯ñö‰×4¿‰Íð[á„Ç.²³MCR w–o/ú¡<ï¸ù’  \íe#98¥áÚž×\ø]ñ'ÅÚ¿†®4O|*†õµ­y‘Ý%µçAè 1[üXý޼)û2øWú¼Ÿµë]LûÓ®c·‚ÚÞh§7Æé£Z÷©W',20¢¾&ÜÏû?~ÔÞøÓâËÛÏjO ]êvÓ^› Ë[‡X GG(p…<‡'¥¢¹ô_ÁoñKTñO‚¼Wáé¼ã_MZ¦™4Ép¢;„ß ðÍ‹È8áåv_µ/Ä߈3kZ×ÀŸ…SxÇÂZÜÖÚsjVö'P–Ø•˜XÅ %°Àf![±Î@Äý[Pø«ñëâ×ÇÍ;M¼Òü#¯Ùiº&‘-õ¼–“_ s-ȆUWTÞûSp@ó_Ù×㟆ÿeŸ†'à7Æ#ZÒüGà뛸-¾Ë¤Þ_E«Û¼Í$ÚËo¡2‡dç“ÆH,;7í)ãkÃß´Oìõâ]/ÃWš¾±{i¯%¾ÇÍsq¾#’IDä™ð¡IÅ} ðWãψ> øßÅ ~!øIüâï Gku%°ºKÈ.,ï7ySE**C8?y×ÅïuïÚ›özñ¾r–Ÿañ²!9·3[[”YˆÜ¨ýF ê«~Óµÿo/êok*ÙKàÍ4œÆÂ&u¹˜•¥€<€r({S;ö½¼¾øKâ‰o®$¸‘|câ #—!VçA$œ½»ã§Ç-?ྕ£Ço£Üø›ÄÞ(½];FÑìÙVkË–Ž]ÈXãEåݸQÉÀÉ~Ë´€>ø;Å ø…§ëöÚ£ø«[¼U·Ðïî£hg¹&6E)ȵÙ~ÓÐè´Ÿ„_´FŸ jþ%ð¨ßbÂ{«MIl¦Y-ZámñãÉ–=ä ¥” eZ†µ è}ð÷ö‰ñ-ïÅK‚ßü|âm^ÊMCHhïc¿³¿Šù±¤±€VhÔ*ÕV9ÆÝÔüiñ³ö„ðýÿˆu ༚†¼=-È2ëÐ]_ÁkÓÛ[á›k(,Šä3€ägÇþÃûëŸô9þxo\¾ñ›Ìñjwº¬V¶¢du’MCˤV(U¹ë޵óWþÔ¥ø“§þÑ:?‹|Mñ‰µMF'J‰u6²–ÜŒXýìØ´Š».ÎÀàùPK!\ýñ7íoàM'à/‡þ9è67ZÜ^-–=Lˆ»ºÔ'vˆ[¡Y$GW<Œ¯$gæüDø‹âïÛ àƒñ7Áx/WÒ¿·îRò+ë[›{ËHÂæ‹ñ´,²!|¤òÝÁ¾4Óÿb?€ž=±Ð/µ þø kúŽ—,/ÊßV¹’B° YT)p§w@MzÆ»ñHøéû`| Ö>é:­Ç‡<>ºÚ˪Üé·Vp5Íͪ—„}¢(Îb™$c/€M4¬>ƒ×ÿik~?ñ/€¾|>“Çm੾±}-ü}¢^“iKŸ6eÁVû¡XÄ ê_ >:xã¹>&h–SÙ=—Ú¡¾Óî~Yí/lóç[ÈzeHàŽ ŽÈ›qxà×Â/‹ßôÏÚwÃúÊCâ rçZе»(õK‹K»;³¼ÀßÙá±4Dó½yb@8 »îºoÂÈþ ø†óàφõ/hz“ßȱjPÍ ÷RùX7 ìÒí“¥‚±ÁÊŠM!¦y—ÃoÚóâÇÆ_ i;øcðb÷Sðìæ4¼»›R¶¶"MÛg[HäÃÎ!9 ø Ä\Ew¾'ý­,¾ø»QÐ~6x+ZðއÔ‘Xø‰`7úM̈ŠI%¶Ð;Œ-×pç W¿a­6ëHý“þX_ZÉes˜¾dR£G"¹v'r°¨¬¿é_µßÄÿë^Óo4o†>‚ækh¯¼±«êú²’¢T‰ˆ·…%^@3ʸ¥¥Ã[³âOŒ1Ü|5“â7Ám>?ŠI‘b×G½€¼±3)Ù¶#R[Ë?1#h¸®3áÏíwð[â¯ÿ•Þ£7„ñ çì롟øßÄ·¦éõ{Èíbð3È#XâD‰dwòâU-ÑyÅr×ìã_†ÒûöµñŒå½³ù‘hZ¢YØ@Oð›ÉTÝÊ=Á=j,‡©÷çlÒÕk;H,- ±µ]°ÛƱ Îp¨0O°«5 (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€–Š(¢Š()h¢€ (¢€ ? J_€?ÿÐýû¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š(¢ŠJZ( Š( ¥¢Š)ªª£ ‚E%-PBªçh''ëC*°Úà ö4ê()h¢€ (¢€(0OZZ( Š( • ÁïN¢Š) °È=-€08¼WÃ_|ጞ)øëÝÞx£ÅAm3\È ´è‘„¶EJ®íÌÄ‘Á9öº(¸Q@!ðcàÄ?%ñĉ«[þ?_øˆ†€Cöf¾*|‘ó¾ý›~ÿËŸîŠöú(¡° i ØÈÎF}iÔPŽ|{øIÇ/…Ú§ÃIµC£.¥5”ÆéaóÊ}Žê+ <½éÞVß¼1œóŒZ´·¶Ñ[ƒ¸ÆŠ›±ÛF3Vh¢à4€Ã 2=éÔQ@ ªIPn¸ïAPØÈÎ9ê(¢Š()h¢€µwoÀÝÓ=ëË~-x'Ç>9Ðll¾øâçÀ:µê] ë{Ho–TXä – ÈG‹‡ç£"žÕê”PÍÿþ_xÆßÅøÂëÇÞ9×m¡°“R¸¶†Æ(,mغAokR5,K1ÉÜyÀ%‹}UK nÞE6À(¢Š@4¨8ÈÎ9úWñÃz狼¨øÃ^!ŸÂº¥Ò¯Ùõ;dYe¶tpá„o…pq†RpA"»:(å~Î>+³ø•¥|XøÏñëâ>¹á»{‹}[N¶Ò­,MÒ˜ç›É¶Èy MäŒ+ƒòíô_|ðOÄo‰Þ ø§â™.îoüç¾›f$Aeç\mÌÒFc.Î¥©Urj¢Ø¬%-R„0È>µÃ|MðR|Høoâ‡vtôñ.™y¦›šap´^`MË»nìãpÎ1‘Ö»ª(Šøqàäø}àx.¾ÜéÒcþ üHø­ñÏ¿<}¡ßÁ§è×——V> ͺÝÙ+GöÉYóyþè<RFC îeߌw¿~Zk~"‰mñÏìe}ñ«ÄÉâ‹}3^ºYRH¼Ý=îVå—D¹ÿÞøaûKxfËöøuñ3ã—ˆm4{ï©çȾL2]ì@  Ú¹ç¾eýœ|Qáí3þ µâtÔ5-ßGÒüOkv"«Áq3ܘ¡u'"I<ÄØ‡–,‘^Yã =Hýœ?d<}C¤§ˆt×½#‹yGËç¥XxÅiÊEÏÓ߆´'ÂŒ—×ÚWÃÏE©_骲OlÉ$¬Lp²åTb‡³G#žEpÿ¿jï‚ ¿×<uãXÁ$e@wŽÞíЈc–`¦(äg vðkÊ>%)ÿnÿƒ‘øLÄux´ qµŸ#†žÂ/±ùÛ„Éæù{»çâÿ³¤ž‡ö:øÒŸM°Ô¡Ô|L¾(ûIwÚØ2&ïŸ{7¼_…ù©(­Çsê?‚Ÿ´Ý+öMð?Æ_Ž>"ŽÕõ òþávù·3;"(H×–s€¯&½7á§íðoâæ­?‡¼ â(îõ{x¼÷²š9-®¼œãÌX§TvLÿWÂþø¯¯ü1ýŠ>iþ´ÓdÖücsa£ZÞkd°Óå—Γí2ªX¨Œ„‡'9ÀÁf±ŽtۋஉñÆúoеãk¬<‘iútz{ÙÛKlJ¬»Ù’VLÆû„Óå žÕñ3öÛðg‚>9ø?áͦ£júóêÖÞ#ž[K¦¸²–Î0,%p§t뵈I:Ö¾±ðÿÅø›ÀÇâV—«Ä<4«3µìá­ãD·vI¼Ð¥B²‘’>•ò—ÇÉ,4ßÛöp¿ÔZ;{ieñ$>d›UYt⑦OVguUÉrià¢VÚŒŸ³.¡žæÛNþÔÒ¿´¤HÚEŽÃíIç;Æ¿z5àºô+zÒ²Ð/¹ìþýª~|Lñ4ðo‹ ¼ÕoÞÖ&ŽXEÚF2ÍnÒ¢¬ NÂkç)|qûRüKøÿñSáïÂÏèÞÓ>¶–a‡QÓ~ÓçÿhC#í2¡  4G'üÜ*üñߌÓáæ¿âÿŒz Ò´ _LÔ49lô‹k!,ÊëäCm2O†Œ"¢ç~@ô¯0Ó>xÏâ·íUûEZx/â6¯ðöêÌh É¥•ÛrÓ[O³Îä7îö»OÎÜô¦’}wû/||Öþ.|?ñ£ñ*ÎÏCñµ[ÍY{y1b^ÍVCq;‘”o˜3';H­ß þÖ¿³ÏüUkàß xÊÖëRÔ$hlÁY#†îT8d·™Õc•³Æ‰ÏšüÚkét¯Ø+ãÂF/Ž| %¿‰à¶’ie½2N…õ ò3ÈËqrK1Z÷þÏž'ø™ðsÂÐê7|1øÇi¨Ýü>Ö“QþÇdKØÚ9 šØÊ '›ª¬»‚’29Á¯“>#6“oÿøpÿÊfKáK¨ô¹Èµ:ÏÚÍäóÌ$^b{m}ûCGø1ñ^ÓÁn|PžºûDv» 艠œÃ½cýç+æù9êwmç56*å=+öÀýœ5¯ÅàÝ7Æörê&ÎÄ‹m-Êœ£¹*!fÏ`ÿÌW±xãâƒ~ivÚßõH´›»¸,cšlì7/²$Șã'ÜŠüÂø™?Âïøuÿ‡ÿ² Ÿ˜úf‹ž#Ú%:êϘG›Í‰Œ˜çfüü¹¯cýµô‰õÏÙÃÀZ‹¤—Rñ…­5rC1žxãœù;˜ôùP®})à/ÚoàwÄÿKàOx¦ _XŽ)'XâIK$,Œ¡$H ´œf¼7ö‚ý²~è?~!è^ñ½¼^7ÑôÍA,Z4gŒjPB̆™JýÍÄœcõw‰¼>šoõ;_XCk¨Øéw0é‚• 2y%bXö€Td(Âã ¯Ê:_†Cþ ;|/¾Æ$]2æ'·ÍçژĤ}ïÝ|Yðή—þ²†êy®UYv-c8ep¬¬›A¥|Ÿðëš±ÿøÃâ]NÎ;›íBÐb´y7“öÈzÈ@b" ¸s´°ÎƒõGÆ? Oâƒ>🆭•/µ½T·†8”'™ssm".@—b=M&ÏýŒµOxçáïÆ_MqñRºÔàµy¡ÓìQÌÖСáB¤y$}ìî<’kêÝSUÒôK µ]fò +qºIî$X¢AêÎä(S_1þÄšõŸˆÿe_‡—Vcû>›ö7UÁ+-¬ n‰+»×=ëãß|øÉ£øûPñÏíá«ßÚ'Ãbá§²·±Ôš§E“…]lPÎBðÁ·ôòÉÉ.ÚŠú¥ÿ¾)ü>ø­e¨jõË}zËKºk)çµmñ-Â*» |aˆ2FG5㯠¾]Ziþ8ñ=Ž•}},pÛÚÉ2›™d”…@°®\î'ƒŒWÊ>#—Ä´gÀá¥þÈôŸ D¾k=CO’Å´{„1ƬÖyHËÚ¸Þ§|_B{W—ü#»ø_û9x‚ÊÏâ÷ÁMGÂ$¿ž;qâÉÉñ%½ÍÄÌ7öž “dy<ìhå ê|pø~%þÓš ֿоè‹w£ÇåÅþ‰1³ywgn[çáò+gKøåñßöOާQK¿G¢½çÚ¥‰arc c!ùxÆ+îÿ±Ún•ü”Ý8ćhËŽ˜o_ƾ]ýµbŠÙ;âD0"ÇédP½N€W±GJ­JtýšW”gÓ®çJS„e.nüÿÝ>뺇Š>øSĺ³+ßjÚMÜ쫵L·$ŽBŽÜǵs¾:|+ø<ÖP|A×¢Ónµ-ÆÚÕU繘/Þd†%y ]¸÷ªŸ5ý ?ƒž…õ+e‘t)J™~ËÆ3Ö¾¾Ð~!럷ÇÄK}Åö~ÖCÒÛE:Žœš¸Ó|”ó… •Óh[)}™ÉߨYa°P©V§;²Šoñ·gß³*­yFåÕ»]Ðo üYøsãŸÍñºý¶¡áëd•æ»FÂB Ê% …Ë^cáoÚïöyñ–½eáÍ Åð½Þ§ ŠÌÍÐCu!è°Í*,nO`¬sÚ¼3GðÝÏìÃàߎŸuŸXxÿRº…u=GJ´µŽÒ¯˜šÞM‚pUŸ*279¯š¿iMCâ½çìûá|Bñ燦¶ñ&¥¢]iº.•¥¬$HgŽ`-îZW—÷ Ÿ0Ž œnìÃåTjO•=I?•öåÖß$cW8Æöׯõó?Q¾'üløcðrÞÆoˆšäz[êŽÑÚCµå¸¸d弸bVv ‘’xgì÷ñº_‹¿>,A£ø5Ïih’i&0¾\_iŠsqƒ´6K¦7 Œq\á±ÿ‡h§Å;JÏà0º'Ÿ÷ Ȼ݈wqæù{Kc˜íšòiþ"~Ù?ð©Š5á±µ[sh~S¨‹+±&ÍŸÅçd6?=éQÀÓöR_iÅ;ôW’_Ó¿pys®×zuÑ3ì?k¯ÙÒ?Ÿ7lþÚ·?bi@Ú-Övù&ço’<`½zï>!x;á–ƒÿ 7Žu8ô0Í¿Ÿ b¾l͵åòx¯ËÏ…|UñCöAÑô(~)èZÃûí-ä-¢Û†³tÃÌ'º3)Yã“%äl6y=kÖ¿mŸKcûx{—گöÔ–÷>²—QÿK)¶&¸,?yýO^½éTË({xRŒŸÅgþ{/»_QÇSÙ¹µÒëú¿ùRxKö›øã¯·ð—Šíõ-yŒëQ,†9šØ0ŠR¾\…’ÛXð |OðÛâ‡í¡ñ#áf½ñ£Âþ*ðôöÚ%Þ¥è÷ºaC4zsËçÄÊAuQÇ=Xu¦ZG…¼7 ØiÚ^¦[YÚé¬VqǨ·EO,øù~BWŽ ‘Þ¿$?e?€^9øÁðWXµµ xCRÖõ{k½N¶·O5<í²ÿ¥°2(8eû¸È ‚A¬°äœÒI'‰sw¾Ë¯ôňöœÑ®Úvóþ» ß h øÃöxÒ?h/íðî—qhÓ_o%ã·’)šÞL 2)ÙÆv‘žsNÒj¿€:÷l¾h¾/¶½Öµ $K#Dó•Ü!òüÌì×’þÓßü3ðÃöñÃß[›MEÒ#·IÜä}¢6gsÝÝÉv8bM}ðwÁ¾ðçÂèºV™oµ–›dñªÄ£ùJÆAÇß,K×'5ÇR–ÙʲNÎM%}–t}Í£:œÊh•ÎSÇ_µOÀO†Þ%›Â>0ñm½ž©hÜĉ$ÂÔ?Ý3´HËÛ#¨¯d“Å~‹ÃGÆrê–É ­¿ÚÍñ•~Íö}»¼ß3;vãœæ¿>¿d |oàŽ¿ð³šÑu|W­¿‰~Û·p³(9›wÍ䑿ã=÷â¾p–/ø&GƒN¬³5›7Ô”îÝý‹ý ßxsÌØN~\s](¦æ©¦ÓRŒ[ïu}>ïÅýrIs>ÍýÝÏÒÿþÕ?þ#øš×Â>ñLwºþóh¦¢K¯,o"IRLIÚO´¾$~Ò_~kIáÏx–-U¢µ¬i%ÄÑB天 ¹;å°1ÏJïté¾fxz]5ôß°Üÿ±Ê¶?îÉO²úŸ/$lçn{f¿5> xS⿈>:|{ÃÞ:Óü3â5ñ=ÇÚ­u &=Bî]4’ldG–DqP"€T ¾¢¹°øJ¤ïÅu~vÝGô5«Z¤l–­ÿ—¯ê~—YüDð6¡à‘ñ"Ï\´—ÂæÙ¯?´D«öaŒ³—èÁÎy޵åžýª~|Kñ%¯„¼âU¼Ô¯Ãµª=¼ð­ÈK1…äEY0?)< ðÿ ~Î>ÓfO|Öþ!ÙÞé^"¿¾ß©Zˆ­í´û‹©Pµ¸ŒJÈ¡gãÊÜ¿{fgxOâÆ‚Ÿ¾üøÁoáßé¾)Ùi¦i{³ƒvùí\²•*—hçóqÀÑ’šƒm«Û¦‰^ú­|Ö‚x‰§ee×®¿ùŸOüMý þü Ô,ôkée©ß¡– 8¢–æåãü¨Ußn{‘ŠmßÇχàÖ¯ñÏBÔ—VðΓkupÒÄKµ©ex°À2¿˜»#ƒ^%ñKá_ã߯¿~*Ðãñt4Zf§£ëpý¢´Wib}кÍsŸ›îœgœb¼ËÄ;׿iߨkâUÅžƒ‘¯Ú.¥aqka –Ú{>@òÉo&ôsÎNFOZt°4¥rOªR×kù[ñ» b&œ“ó·ËÌéüÿ·Ä i?-|Uáÿ[ë õ·…¦ÓL©öˆtIïreYŒG8QŒ‘’¼…Ðø¡ã¿Ú^ý§—àÂ?iÞ´‹Ã']’[í<^nd¹KvQÈaŸ5HçÖ½Çà§ÅŸx“à?…|{o­YÚé ¦YÅq$³$Ik:F‘¼–8GGùv“’qŒägãÿ‹ þ|\ý»×ÃàûFZî5û\¶_éßDˆ|ÈdÉ#ü»°z㎌<”«MT‚J*_etòÒöó2¨š„ydÝÚëýXúoNöøsðÛ≾(x¿Lñ5öŸ¤Ïy¤µ–öE·–ÖÞyʤŸ03yx¶ŸZù'OøÇûdhŸ³î›ûP\xƒÃ¾%КÒ=FóF›OkYÅ«I帎x˜êyÀpØÚßLÏðkàÿÁÏ¿ì~Ûýž-OBÔ%»ožø–ŽÎeC™å”¯ôÆkå¯ÙÿöYñ/ÆÙÇÀ¶¾<ø«­Kà]BÊ Ï‡,à·µ„G…–¸Pd‘ß8<€Fk†•YT­Ì·ŽêÚ¤•ìþkÔšª¥Ôc{ÙõóóÜ÷ˆ¿þ#xãÅ_ þ|’ÛAÖ¾ hËâ;½OQƒíCLÓ=èòI+6Wæ;A :>õè¾ü`ø‘Äÿ|øåue¨xƒÂ6°j–šÅœ&Öý6p2òC÷cxÙ€;N>ðÇɹüëYµÑ¾þߎhÓLÐõÏM¡i9Â@“ÚÌmž¢…˪ŽHCOµ_‰¿¶ïÅøF'Zèþ‹@¹º…Ž;û–,±—\êÂÈÀò¸©t):|ª)G“šýoÍmÿ ~£U'Í{ë{[åý2wûRþÓ¶z§Ä¯„ž'Ò¾x*+Éí´HnôѨ\êqÛ9C=ÃÈG’²xŒ¼® ›ÒüQñWâ§…>6|øg¬Od§ÆZ£kËoèä¹²‚7y$w#­r¿ðOßè‘~Î^Ôn#°×<u¨Øk6“º¤¶’­ÜÒ“*“•PVà×ûQøOÀßÿiOÙûúîÝgÃZå®»!k[—Dž5†’{wVÚHnGµœ*te¡kh¯¤]ŸŸ}X”Ÿ²ŒÓ»vëæ¾îÇÑþ+ø¥âý'ö®ð?Â[9£_뺥¨\ÆbS#Ol걕òäµñ×í-ñ~?Ùâ—ÄÄñÑþiÒéÖº—†uyçYn®.‹HŽŠk‰$eã°8¯Ó/ìû%íþÏ”í¹“bí-œäŒ`œ÷¬jÖ¡CÙÚɨ½R[7~ûí¹¤!R§5Ý·_‚ôØÎðÍÜú‡†ô«û§ó&¹´‚Wn›™ãž=I­ÊÉ×5;Ã:¡âUü› *Ú[©ÙT¶Ø`BîBŽNO¼ÿà÷Ƈÿü+/ŒþÞ½þ•Ô–m$¼$M#°Úà»"óÒ¼«¹è#Õ«#_×t¯ h·Þ"×&û6Ÿ§DóÏ)¶GË6p;š×¦²«¬2•#>iøñ/Oøã/ˆLJ¼ymâ½nl…ŽŸ³Á&’¿gHåi ™dVp+éŠø“özDOÚ‡ö’HÀU¦…€8ò †½ëâ÷Ç_†ÿ-4{߈×òXï]‹+C/6ùÈÝ‚ wmý£¾2øGáׄµê4Àþ!Õ¬e};PšÖ[˜í˜¾sF\)ìMw—_|'ð÷áV•ã߈^#4”´°ûF¯"²C+ܪ"Ì@E•ØGæ¼çöÌŽ6ý–¾&»(,4KœsÚ´£ñß‚>þÍ^ñgÄE2hVú•Ñ stÓ4ðÅD!PÛË»ŒsÍU´ÏmðçŠ<7ã *-s𥶱§Í÷.-&I£¼ø]uâ‹_؈ZM>y„3‘:,‘”€û•†6“é_™’|ñÿÄßÃãOÙcáî£û?$²¬’kw—­¦-Ô@çAdFS×"Ry!Å{ïǯü)ñF¡Á?ü0½øÙñ I´µû_Øtõ·ÒM°•õ#å-˜À¤€`ã ñO”W?Akâ¯ø¿Ä?lÝkáNµ©Íá߉ZJëÚ|7Ô´ÍkÁ^ ²Òõ-Y¦¶ºŒÈgY'Ê‘šFrÏ”ã H8“^ýEc±ç¿þü9ø»¢§‡¾%x~×Ä:|OæGÊgË|cr0Ã)#ƒ´ŒŽ´žøQðçÀž_†>ðý­—…N¿Ù¥LÖån]¤™Ye/¸;»G5ètQp>dð¿ìmû3x3Å~3ð߀¬m5}:e¹µ—tÎ-æV $ÙƒA‚¯hÐþø3Ã^)ñt-.;MoÅ?g:Ê–/söEe‡p,TlÀm¯5ÙÑEجyô ¾[øËZøƒ‡öÿˆíÇR¹;›ívȪ‹Ñ’c`Bò¹ÀÆq^;aûþËš_‰"ñ]ÃÝ>-BÞ´ÆA”Ä“gvõ„¹Œôp008õ\,yßÄŸ„ß þ0hkΈžµñŸ‰#ŽáNèßûѺèHà•a‘Áâ³>|øWðRÂûMøaáè48uGYn¼²îóº»ÈÌÇŽp2}Mz½\v>nÒ¿d/Ù«DñšxÿJø¦ÛëPÎnc‘Uü¨ç';ÒÞJy@€T¯cñ—€ü#ñÂÓKñ–›©kcyo r.­I ƒi(Àž=A®ºŠ.¾gÖ?cÙ^ñ§â­_áî›q©jþwÚ\‰®¬®#V®À“½T0?0!¹¯¦h¡0±ç~*øMðßÇ ‹á׋¼=k«xrÞ8ãŠÎáw¤kÛÆÎõeV ŒŒòkø]û5|ø/«\ëß <'m£jwq%ºV’YšAò÷ÊîBåWŽƒÒ½ÎŠ.ÂÇ!¦ø Â?Œ5ŸiºlpxƒÄ1[Av¥·Ï + °$¨ {æºú(¤‘|*ø; | ¸ñRøZîãû3Äú¬º¸°“gÙ째Ï[pª$Ž7•bpN½vŠ(¡T@ž¾ô2«¬2„S¨ ¹ÏxKÞ<ðÞ¡áX¦¥£ê‘ùW6Ò *d¤©rC]ã&К¾Œù:ØgöN¶¸Žê‡ ,L[̸Èe9ýo­z¯Äßþ1[Y[üIðÍ®´tÜ‹Yd “ÀP’ÆUÀïØÈ¯\¢ºeޮ䤿î¶Õ™¬=4šQ_qäþøð“á†5øÂözf¬¶ÁL‰t<¶y…‹‚¤‚ #b¼ÛKý‹¿eí] ‡Ö nÊ,Ó;/—*Ì¢2ÎL`H ü„gr õ ,mdÛSzï«Ô;'¡å_¾ü+øË§Ùi¿ü;o®E§>ûc.ä’8ÎÉ#epFpp3ÐSþüøWð‰ïŸá¯†í|>u(íâ¹Á€•mC·Hʇnzœä’kÔ¨¬þ³S“Ùó>^×Óî+ÙG›šÚŸ0kŸ±ìÁâ/Oâ}[áöŸ-õÔ¾tÛL±E$™Îæ…c'?ìàä穯hñÃø÷ÃøCźDZŽo$ÇlÅ‘íŽb#aR6ÀÎ+·¢ªxº²·4Û¶Ú½=¨Á^Éj%q¾ø{à߆z#øsÀº\zFšóËt`‰”Í;n‘òìÇ,yëŠìè¬TÚN7оU{œ×‹ü!á¿xjÿÁþ/±MOGÔÐGsm!eY0l¥OPVÕ…•®™eo¦ØÆ!¶´"‰p© ª3Ïb­ÑG3·-ô +Üù÷Ç¿²·ìûñ;ÄçÆ~8ðU–§¬ÈKpL‘4ÛÊ"t`NÀ½ü5áÙ<>|&ú]©Ñ ¿Ù>Ãä§Ù~Ï·g•åcfͼmÆ1Æ+nŠÒxŠ’IJM¥¶»z©E6ÒÜùçÀŸ²—ìùðÏÅ1ø×Àþ ´Òõ›}âÕås˜-]ÙWvNp+SâoìÙð;ã«»ñÂ6š¶§yKtwÃ9ûŒñ23®2bp ¯r¢­ãksóó»÷»¹>ÂååV<¢ËàoÂ]7á¼ÿl|1iƒ®U–]5Cy/¹ƒ’Ç;‰,Îìäu®gá·ì½ðáºÞ'ø}àûM/V(b[ÒM*Fx+•ß`Ç.8Î+ßh¥õº¶qçv{뿨ýŒ.–‡‚|Ký˜~|_×—Å?<%o«êË Û›–’h¤hS8ŒN¹^Ow¯Uðƒ|+ðÿö~ð^—£Ø)Xm­Ób '$ú’O%ŽI<’MtÔTÏRQP”›K¥ô¥ù’Ôùž÷ö9ý™uÿÂmsðÿN:·ž.K¨‘#3ÜÉWç<ãn=«¥ø“û5| ø¿­ÇâO‰>¶×µ(aXižUa’BáGžÕîtVŸ^¯u.wu¶¬Ÿ«Óµ¹WÜx¿fßÿ 4í{Jð/„­´‹OÛý“RŽ'”‹˜6ºìmîÄ HãŒkÓ¼%á?øÃz„<%dšn¥D!¶·BÅbŒd…‰=û“]LEIüroÕ•q¬yÇÄÏ„ þ1h‘øwâ^m¯ØBþli8!£|cr:•u$ppF{Óþü&øsð{Bo ü5Ðm´>I ¯æIÆçv%Øã¹Ž½Š^Þ|žÏ™òöé÷³ù­©óÏÄÙGöyø¥â'ñw޼e©kíóny Í´`y¾S sŒrÀœ3^„~|::¿…õÑ¡@·Þ †K}EÜ¿b†dºF¡‚àªóÓŠôJ*Þ*«J.nËmX•^ö8ëßx?Qñ®ñ÷LŽ_é6ÓYÚÞmñ[ÎA‘nŽàŸC]V2“v»-$¶<ÇãG†|Wã_„þ,ðçµ¶×5½:{+io]ÒÝáLldhÒGi=óÚ¹OÙ«à†™û=|ÐþÙ:\^Z¡ŸQ¹Œ·7ógd· "dd"¨<Š÷š)_ ì%rž8ð7„þ$øb÷Á~8ÓcÕôMD*ÜZÊX$8©SÃx5ÖQH”4ÿØsöOÒ¯í5=?áÅ„7V2¤ÐH$¸ÌrFÁ•†eÆCk+öŒý›õÿÿþßêW¶pøÁwW†¥jï/Ú®îË0¢ ŒÆPy{K€í€x¯±(§ÌÅa¡B€ª0Ôê(¤3âÚ¦Ïöˆø™¡kß~xÚMÄÖ±ÚI⋽^Þ8mÒlyٱǞJcŒç°¯®|/ Ãᯠèþ¼èô‹K{Tr9aoÆñۚߢÅa)ª–*,rqÜã?€§QHa^A࿃^ðwÄ|UwŸˆmmod`(vcgpˆ8bTRCÄ gTRCÄ aabgÐ aaggÐ descDisplaymluc& hrHRØkoKR ìnbNOøid huHUcsCZ0daDKFnlNLbfiFIxitITˆesES roRO¶frCAÈarÞukUAòheILzhTW $viVN.skSK;L>@>289 LCD LCD æÑâÕàÙ_i‚rLCDLCD MàuFarebný LCD&25B=>9 -48A?;59Colour LCDLCD couleurWarna LCD 0   @ ( LCDLCD *5LCD en colorFarb-LCDColor LCDLCD ColoridoKolor LCDˆ³ÇÁɼ· ¿¸Ì½· LCDFärg-LCDRenkli LCDLCD a cores0«0é0üLCDtextCopyright Apple Inc., 2022XYZ óQÌXYZ ƒß=¿ÿÿÿ»XYZ J¿±7 ¹XYZ (8 ȹcurv #(-26;@EJOTY^chmrw|†‹•šŸ£¨­²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿparaffò§ YÐ [vcgtndin6®QìC×°¤&f\P T9333333mmod Rýbmbvcgpffffff334334334ÿÀF R"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÛC  ÿÛC ÿÝ–ÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¹OxëÁþÓXñ–±m¤YÆ /q Lã®ÕûÌ}€&¾0×?o¯Þê'IøGá-sÇÒ“µnl- ÚgÐÉ)B9õq¦ÞÈ—$·>ú¢¿<#ý£¿lÝP}£@ýŸD¶Þ·¬¿ýò^£“ö´ý£<,<ïˆßõ HcϘtÛˆ¯Xºùâ¯ØH^Ñ¢tWÈ¿?mþ=¼MR¾ŸÂ:ㆵ ZËžùlÇâõõ¬Áu \[H³E ®„2°=#‚+9A­ÊM=‰h¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šç«O¿cê:+Þ)ð÷‹lSðíôWÖíš3ÈÏbüEo×ÌÕ£:rp¨šktôg|dšºz|¯ûH~ÒÖà³ð¿†ìOˆ|w¯b=3K‹,ÌÎv¬’ç`?LôW»üAñŽðûÁ:×5V k¤[<îOL¨Âňñ'ìoðëWñ½ö¥ûU|PŒÍâoÉ)Ó£”dÙià˜âUÏ(ê¤S§nil)>ˆƒáßì®üCÕcø™ûVjÓx—Yœ‰bÑ•Ìv€òª`³/®G¾kïmÂþðµšØxoL¶Ó-Ð oÆ0=vŸÆ·h©G-Æ¢QE;ñ/àÂo‹Zlºw<;mròr· Šá³  ‘ï‘í_ ^Ùüjý†µí›{ëŸ|"’P'†|É{¦#67£Ôc5ú“U/ì,µ[ôÝJ¹µºFŽX¤‘цX#­k ­hö"Pês¾ñ¿†þ#x^ÃÆ¼Kí3PŒ/|ñ7ìǯËñÿötWJYkÞ" ÎL‘F>\§%·ŽÛZЮÒäG®Ñð\œz~˜W'〺ýïŒ.ükðëÄO ÞêŠVñI`¯ž¤óéŠô¿„ß ì¾èSiñÝ=ýõü¦âîáúÉ3uÇ·óêig8Ì¢Y\aAGžÑµ¢ÔÔ¾Û”¶iô -,JÄ7;Û[륺YSñâ/Äcâ!øYð°Aowmoö«‹™¾ðí*¹ùq’3Ç?Ï øñ3Å>(Ôu¿øæ×[ðûòÅÂșۓÛ9î01Ú‰?õx®?x]oë~OÙå`YVHóžJäç>ÜþÐ|"øBŸ †¡ªjZƒjÚÞ®Û®®[<ó žHÏ94bñyCÊ=œyùceÊùùïï7-¹m²*XŸ¬ÝÞ×}t·K.çÍ?·îštï ø âFžê~ñ-”QÉÑ–  âPõص÷Í­Â]ÛEuÜ™×>Œ2+óïöîÕWÄsü2ø-§’ú—ˆüAîÄäù6 ï c澕úQÁCíHÔ*Ð0~y?‚'µÙ%QX–QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(’I1´²°D@KÐÔ׿¾.x?Ç:¶…à=.õçÓN£Ô% Fè®>E àó_E|}¿»Ó¾k³YHb‘ãX÷ 3q^ñÃZ7…þx"ÓH·XK­>i/$›˜ŸRI¯Ò¸‡…JXºÉ¹Jn1¶‰5¹=íueß[žqVn2§’»ó×cÌ¿j[›Ï‹_>þÊþ‘¡Òç «ë^XÁpaaábX0>Õú+¤éZ~‡¦ZèúL mgeC H0©`*ô¿>ÿg«Qâ¯Ûã‹îròøf;-"&n«Áä {e+ôR¿>ĽyOfž× (¢¹Š(  ÷V¶÷ÖÒÙÝÆ%‚u(èÃ!•†?Q_š_´»€_´Œ¿gÍW/àïÛϬi17Ý]ÈZx×ÑW €OZý5¯ÎŸÛV3á|qf6ÜÍ®¦•#ç·œÀŸO˜×V ¾t“2ª´¹Û|×ü=âÍÄ¿5-唆à@^3´Û?ÊÀö0+„ýŽ5_ü.øãoÙS[“ûBÏÂÒí2ä6ï.Òèù©û€ùúñ^áðC^Ó<'ð;Rñn¢É:d·Ó4è 3îp+ãÙëãO~xSÅß´WÄû£/‰þ#j3˧é°6þK4¤0ªƒ*6– ŒWÕq¤éÏ2Ä{8Û[=oy-ÞË}Ï;+MP‡3éøv?^h¯Ì»Û?ãw…îañwÅo„×úuS›k‹M³\ÛÇÙ¦ŒTrwcâ¾Äðí%ðCâ]ª\xSÅö3;˜f“ìÒ««²`„x8Í||©I¢šg¸QP[Ý[]ÇæÚÌ“'÷‘ƒÌVv§âèª_YÔílz›‰’ ;ó¼ŠÎÅÊxÛÆÞøwá›ïx¾ù4ý/OŒÉ,®@àvõcÐæ¾eøûm|ð3¶á{™|kâ7â-;H®˜ð2àÆzá‰ö¯Ðþ |fý«5Ë/~Ò‰ÿïƒläÙxZ)7pr¯q·å;»äîÇVÑ¥ÖZ"ú"ßìÛáíã÷Æ]Wö®ñ¥«Úé6ñ› ÚJ:Aœ´ÿ\A>•úETtÍ2ÃFÓíô½.¶´µAQ Úª«À ½QR|ÌqQEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ãÿh¯ù$šÏÒ?ýW‹þÑ¥£øá{ˆØ«Ä–L¤`ùiÍ}Sãß ÛøãÂ:—…î_Ë[èŠþë1_øÓßµÏ iÿ u¿}¶ 2x’=FSlF@_”Fc‘šýW€ëR—ÕùªF.•W&¤Ò÷\Rº¾ú­VçÎç’ç´[敵Öç/û$x‚(?i_‹šl…Cø®=?Wˆó4q #%}²Ü×é}~.ɨ|!ñïÚ&Þ:u¤³x[]ÇXáܯ#ÐÄ“Çû3eyi¨ÙÁ¨XJ³Û\¢Éˆr®Ž2¬pAȯ†âLÕñµi-”šûŸù¶·=(ËȳEW†vQ@~sþÛò wâ?ÁÛ)’uñ Z„¨9ͼDn$Ãiæ¿Ee’8cy¥`ˆ€³1à9$׿§Ã‹¹¿hïÚ·ÅmPÍá‡vÓhÚ[žRk¦R“íÃ32‘ÛÕ„ÒjO¡]¬n]ü8ñÏÅÙ;Qø}ðúâ+KíWR’)žfڢظ#œ…®Ãötýˆ<ðjÖÓZñSÂYâ˜PuwóÃm÷ ˆä=òsÈÅuŸdø£á/Áº·„šßHË%óÍTÈFÜ bHãµ}m_IÆq”3 ÍI8ɹ.Vžõ³vôÜàÊìèÆéÝ+j¬W¹³´¼¶k;¸xmhÝC!…OWÍ>7ýŽ¿g¿ݶ©áh¬5;¾Õbò[H°‚û澞¢¾N2kcÒi=ÏÏé¿àžž2–°ø‡ã=>#Ò(5P¨>€ÄOëZ:güçá.ľ ñŠ#È&=KQi½ŽÅNÜWÞUûy÷'ٮǔøàwÂO†ˆ| ák-,®‘SÌ”é$…œ~½ZŠ+6ÛÜ´‚Š(¤EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(àßü$øŠÖ>6ð5·‡m5¿ xšc* d"Hø8dÚFšò/ücøû'xvÃá§Å[I.ììÓ.¯ÄDlß,m1nvŽ1ŠýO®;Ç^ð‡Ä¯]xSÆÚd:®›v¥Z9T¹ÜÕXve ŽÆ¾Â§³¾+ ½.ýäÝ•®í-ô<µ–¸ÿ£_wù+ø»ö’ñÓx.MSHðmÞ’—V=JG[®ïâ\}2q\N³ãÿŠ~ÐôÏ'Ä‹O¼ÍI¥¤Q"LŸ(Üqœb¾høÅ§/ì€.´ÿ†-/ôyIøKU+¨8ð‘¡ѯ¿úñTþþÒ>/½µ·Ö`ýœ®µ¨ö¼W}•Ì–æ =Æ+é2¾$Ë)R³Â%­Ú²—2¶×åûuœŒyKøñVù-Ìý¨±™îl­î$ZXÑÈô,4û›«k(êòT‚Áfw`ª u$ž~Yx¯öîøõ èíuÀ½^ÂHÁÌ·¶w+n¾œ€?yGÃïøŸöÚÕWJøñbßÃ:a£øoN?bº˜ç˜‹6Ö•OB2kóG‡zÉè{Ú­¤~;üzñÆmM¿gïÙ·~¥{¨?•«k–ùk[|áÀ”ew\ñŒcšûà¿Â? üø¦øÃi˜í3ÎGÏq;s$Œ}Y‰#=£ðÇáW~xf øK‹M²Œ ÅFd•‡ñI!Ë9÷bkÑk)Í[•l\cÕ…QY”QÔõM3E±›TÖ.á°³·¤žy(Œ³¹ N95t@ äÅñ'‡ôÏ臆µ˜üÛN -æ^ûd$Äu±Á¯&øâ N_ß|>ñ<›üCàiÿ³nXõšÜ Ú\yÛ,XäòH$×d0ªT%V/X½W“ë÷èýQŒªÚj/gùÿ_©î”QEq›Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ާ©éú6Ÿqªê· kij†IeíTUä’M~oø“ãGÅßÚ£Å7¿g @ðe«ùZ‡‰åR@8‘m”uî<÷ÛÙ>4x§ÄÿµGÅ—ý›¾Ý=·ƒ´6Y|O©C÷\ƒòÛ,ƒ×žç¯ðñú àŸøgá߆l|#á ôí3OcŠ(Ôƒ–cÕ™$žIäÖé(+½Ì÷ô<áìƒðáJ®¢4ÿøH<@ø3jZï¦g<¶Üä*“ÛŸ­}Aµµ²í¢H”t¡Gä*j+)I½ËI-ˆ¥‚„1Ïȇ¨`ü|ïñcöVø9ñv›[ÑÓOÕ¶‘£cˆ.b=ˆeÀ=»×Ñ´Q5°5}ÏÌ=?âÆÿØÛ^µðçÅÙdñ§Âû™6ºâ)ûMŽ~êΧ?.;g±çµ~“hZîâm"×^Ðnã¾°½e†h›rº0È ÕøgCñŽƒ{á¯Yǧ_ÆcšT2²ŸcÜv=«ówÁWšÇìIñ~×á~½w5ç¿\Ä¢êvgu̧ýAsÑwzö;95®“^d|>‡êÔt‘HÈe`#¡¥:°4 ùÃâwü[ˆÚÆ?w¥_lÑ5ìp«Íþ‹r݇•)ÚÌyÚÀ ú>¹ÿøkLñ†õ/ k)æYj<à8Àeôe8*{ weø•Jªsø^Ñïóê¼Ò0ÄÒrŽ›­W©ÐQ^!ð'ĺž¡á‹¯x÷xÁs.øž²¤cýàg³E‚ êA5íõ–/ èÔ•9tü{?šÔº5T⤂Š(®c@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ækãoü+ö´ð?ÆïøÃ7zIÓ|=â[M³GÓVI>Ïgw,1†mÙf؃'¹æ¿¦ZþU>Ê\î¿ìñ?þŒ¾ þÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åSþÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åSþÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åSþÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åSþÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åSþÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åSþÍûmÿJÿÁ@ÿâ¨ÿ‡³~ÛßÒ¿ðP?øªþªè åIÿà­¶Ìj]åÒ•TIÒçæ¯Ó/ø&_íñ³ö§ñ?Ž´¯‹3Ááû;)íEªÛóÉ"¾â ÈÂŒWß_µŸüšÇÆOû|Cÿ¦éëñ[þ{ÿ#ÏÅ_ûéŸú:jþЍ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+åïÚãã%×ÁÏ„w×ú ™¼G­§éq/ßk›2Žå7nÇ|WÔ5ò'Å‚ž-øŸûDøÅšÂÄþðz5ÐxóP]Íì'<6ÑÓ ­)Z÷dÊöÐê?eŸƒ|ø]e§^6¿«¦êwù¥¸”d‚O8\ñõ5ôcEL¤Û»VV (¢¤aEP^)û@üÒ>9|0Õ¼ ¨€—Æd³›¼7IóDàõÀ`3Ž¢½®Šqm;¡4|qûüVÕüwðîïÁ¾3b¾,ð=ÁÓ¯Ñþû"äE!÷`­ùWØõñ-ÂOø#öĹøàû3Áþ.ÓYuÞ*,wq¸+)RAbA ï_mUÕµî… ÚÌ(¢ŠÌ£çˆŸñm~(h?!ýÞ®yz»Žw·úÓv÷lÇ¢+èêæ¼eá]/ÇÕ<%¬®ë=VøÉ]Ãåuÿi ¾àWü ñV§­xN_ x³âOÎÚV¢ æFƒˆ§ä¬Ñí`ÝÎq^µoßa•Oµ §Ù-¿ð’åW’Õzõÿ?¼öš(¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠüÍý³ÿࣖ?±ÿÄí+á½×€dñSjš<:°¹MHY„Ü\Aåì6ógFíÛ‡ÞÆ8Éùþ“¤ÑŸÿ«ÿÈUó—ü³þN‡ÂŸö'YÿéÇP¯Ù/þ²£à¯êß ôy®.´ûIevòÎñ+3Ÿ©'4ùõÿÉÒ?èŽOÿƒÕÿä*?áù:GýÉÿðz¿ü…_¥¿ðÀ±·ý}þý¿ÿGü0ìmÿDŸEÿ¿oÿÅÐæ—ü?'Hÿ¢9?þWÿ¨ÿ‡äéôG'ÿÁêÿò~–ÿÃþÆßôIô_ûöÿü]ðÀ±·ý}þý¿ÿ@š_ðü#þˆäÿø=_þB£þ“¤ÑŸÿ«ÿÈUú[ÿ ûÑ'ÑïÛÿñtÃþÆßôIô_ûöÿü]~iÃòtú#“ÿàõù ø~N‘ÿDrü¯ÿ!Wéoü0ìmÿDŸEÿ¿oÿÅÑÿ ûÑ'ÑïÛÿñtù¥ÿÉÒ?èŽOÿƒÕÿä*?áù:GýÉÿðz¿ü…_¥¿ðÀ±·ý}þý¿ÿGü0ìmÿDŸEÿ¿oÿÅÐæ—ü?'Hÿ¢9?þWÿ¨ÿ‡äéôG'ÿÁêÿò~–ÿÃþÆßôIô_ûöÿü]ðÀ±·ý}þý¿ÿ@š_ðü#þˆäÿø=_þB£þ“¤ÑŸÿ«ÿÈUú[ÿ ûÑ'ÑïÛÿñtÃþÆßôIô_ûöÿü]~c'ü2ܸ|eNäxˆùgçW?áù:GýÉÿðz¿ü…_”_²‚|)ãŸÚûÀÞñv™© j:Ì–÷sb– ’¬õýEÃþÆßôIô_ûöÿü]~iÃòtú#“ÿàõù ø~N‘ÿDrü¯ÿ!Wéoü0ìmÿDŸEÿ¿oÿÅÑÿ ûÑ'ÑïÛÿñtù¥ÿÉÒ?èŽOÿƒÕÿä*?áù:GýÉÿðz¿ü…_¥¿ðÀ±·ý}þý¿ÿGü0ìmÿDŸEÿ¿oÿÅÐæ—ü?'Hÿ¢9?þWÿ¨ÿ‡äéôG'ÿÁêÿò~–ÿÃþÆßôIô_ûöÿü]ðÀ±·ý}þý¿ÿ@š_ðü#þˆäÿø=_þB£þ“¤ÑŸÿ«ÿÈUú[ÿ ûÑ'ÑïÛÿñtÃþÆßôIô_ûöÿü]~iÃòtú#“ÿàõù ø~N‘ÿDrü¯ÿ!Wéoü0ìmÿDŸEÿ¿oÿÅÑÿ ûÑ'ÑïÛÿñtù¥ÿÉÒ?èŽOÿƒÕÿä*?áù:GýÉÿðz¿ü…_¥¿ðÀ±·ý}þý¿ÿGü0ìmÿDŸEÿ¿oÿÅÐæ—ü?'Hÿ¢9?þWÿ¨ÿ‡äéôG'ÿÁêÿò~–ÿÃþÆßôIô_ûöÿü]|ûwþdzÃÙ3â¼ ðïKѵÝ.ÚÕínàGÄÏylT–#•b:t4ôßìOûfZþÙñ?ˆ-|&þº‚ÔÆ÷¢÷ÎóãgÜCÜmÆ0s_nWá¿üþIÏÅû X臯܊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSàWü¥ÎëþÇÿÿèËêþªëùTøÿ)sºÿ±ÿÄÿú2ú€?ªº(¢€ (®?Æ_¼ ðòÎßPñÖ¿c Áy2[ÛµìéŸ<„*Çb ¹$ª ö Šã~!øÿÂß <¬üDñ½Ù±Ð´ gº»˜#ÊR4ôD ÌI I5ø‡ñ‡þ ûXøƒ^ðĆž—áÇÂx‚ÓJ±ºÔí­¦Ô5Å•Ã;”dò¡1ƒ´Â1’q3ž÷ºŠ( Šøûö¦ý®´?ÙÐèþÑ|;ãˆÞ-W§Äì÷NÓ$²*¶ÈÔõÚÏeÚ /Ì?°·í ûQüKý¢>+ü0ý¤.-m®|/gkpšE¥½²E§MpáŒI4!¤p™,œ½ÔÐêýW!á?ˆñâß¿‚µûsû.æK;±eqíoq x¥IG ¤a€=úP_EPÏßµŸüšÇÆOû|Cÿ¦éëñ[þ{ÿ#ÏÅ_ûéŸú:jý©ý¬ÿäÖ>2Ø›âý7O_ŠßðCßù~*ÿØ7LÿÑÓPôUEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_8øëþ-ŸÅ­âd_»Ñ|SåèšÖ8T›?è7-ô9‰˜ðZú:¹OøCLñï„u_êãýT¢-Œ”n© ÿi⻲ìDiÔ÷þ£ôåºóHÃMÊ>îëUëýhutWŽ|ñv§âO¶‘âcxbwÒµE'%§·áe稕6¸l`’qÒ½Ž±ÅaåF¤©Ëuý_æ]*ŠqR]BŠ(®s@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ù†ÿ‚ÖÉÐøSþÄë?ý8êý$|:ÿ’}áûÙ脯æßþ Yÿ'CáOû¬ÿôã¨Wô‘ðëþI÷†?ìeÿ¢€;(¢€ +;WÖ4é—:Ö½}›§Ù¡’{›™VbAÕžG!TR@ª>ñW‡3¾·QghmOï!³Šäl–LŒ|êÄŒ•ˆ®$ ß²/ÄüTýš¾üBñÍàÔ5ísLK‹Ëp‰e,Ê[ˉU8誠 £h¢¼ã¯Ç/~Îß õŠçš-&Á’%KhŒÓÏ<§C ÎxЍêÌ4ìWáþûgþØž(ý«~i~*ðøøiðßâ=ä‰c¢\[Á-õåŒC{™%CvýÀ ŠäĿïíûâ˜í’ðéfâ1{öy ”BNò™SÈ sÚºú(¢Šþ8?aù>Ÿ‡?ö—ÿ@–¿±úþ8?aù>Ÿ‡?ö—ÿ@–¿±ú(¢Š(®?_ø…à_ kz?†¼G¯ØéÚ¿ˆ&iÖSΉsw!ÏÄN÷ÆHô߈~?ð·ÂÏk?¯õçI†kX†éš"‘«”AvÉ m€HÁýýž~9è_~xkãe„_Ù–šÝ¤’ÜC#-§¶w†å wT–7ÚÇ\6q@çErÞñǃ~!è0x£Àzå—ˆt‹¯õwv¥Ä,{ñ’àò:]Mðçü›þLâ—ýzYé}µ}Ç_ÁI¿äÈþ)×¥—þ—ÛPÃ_ðDù'??ì-aÿ¢¿r+ðßþÿ$çâ‡ý…¬?ôC×îEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSàWü¥ÎëþÇÿÿèËêþªëùTøÿ)sºÿ±ÿÄÿú2ú€?ªºñ¯Žþ~Ξ6ø·­®§K'‘É4×í,"Š8Õ™˜€O@åˆײ×Íß´÷ì·ðÛö¯ð-¿¾#=å´z}ÇÛ,®¬fMoq±£Ý†WGX‚¬§ÛšøŒþÔ¿¶gí\Mìðëþ\ð¾1ñb…gŒô’ÖÛ‡ê‹r=vžž“ð³þ ¿à};Åv¿h¿êŸ<}¤éuªÍ"XÛKoO&Ø;¾èw1ðzW™®ÿý@þ»‡ö‘øygÒÚà´"·…{#òHq€0nIí ú+à_ü'öyøÕ©'„/5¼ ãUA.…â$û ȸΠqHÇÊ‘‹p©¸H{Æ:PÚ:Þ…¢x›K¸ÐüG§ÛêšmÐkk¨–he Cñ¸*à Ú¿)¿à«¨‘è_ãBªøêÈúÝ_’ðVùü ÿ±êËÿ@4úÙEPCxB}uëcW²¶u†æ6‰àžÖf]Þ\ÑÈ Ž„eOUb9¯ÏÖµÿ‚Œþ׿ñ÷$?³_ÃË¿àMÓøŽx[ÔþîXÛñµ#ÑÅ}«û.þÊ ¿dïÞøKáô××ÒjÓ­Ö¡y0’k‰Õv†ÚŠ‘¢œ\ÿx±æ€6k?ù5ŒŸö&ø‡ÿMÓ×â·ü÷þGŸŠ¿ö Ó?ôtÕûSûYÿɬ|dÿ±7Ä?únž¿¿à‡¿ò<üUÿ°n™ÿ£¦ 誊( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( œÔ¼]âËø´½H‚K›»©Ž#††çv>€zs_˜ºïü;Ç5kŸ~Âÿ oþ!^Dæ),x®ÛKº’?2 >2gÔ. vÑî“i#Ø« øQ¿i?Ûsö±ÿDý•¾†> ºá|[âµâXI-mŠºò3‚‰p¹þ44õ7ìáûrüý¥59µãûSJzÞüRĪÎä ³e&ÿju wø ÿøøMðƒÅ°|WñŽ«©üKø•ùÇ_×.$‘Òr6—†ìàðei]{0¯¸µ½ Dñ6—q¡øO·Õ4Û Ö×Q,ÐʆãpU†@8#µ|¹ðöàýh׋JðOˆ†ŸâFÈ}UQg©+¯ÞU‰IŠãŸ%ßñ_\Päüu= à\q¨U_YÀ!À¿[«òOþ Áÿ _¿ö=Yè¿[(®6?‡_âñ¬ŸbðÖ›‹fƒì²jëi êyMrÍd¨ X€»*ùOöºý«|û'ü6ÿ„¯Y„ê¾ Õ­t="6Ä·×@ ä€JÅàd|d(Ë2‚ÂÁA¿ho | ýüI¦ßÌ—&ñ½Þ‹£iëóM<·‘d—`çË_{0NÔêâº_Øgà®­ð_öNð_Ã_Úˆõgµ¸ºÔ­ŸæòäÔf’àÀàñº4‘cp8ܧ¯Zù·öUýüyãOEû_þÙrÿlüE¿Ù>¢J¸µÐ`4?¹9 4yÌqó廜–OÕ'tI*¨$’pI4ù}ã/ø&Ý—ƒµëˆŸ±·5/ƒž's½¬ã–K­èŽBK–eBNpÞlcøb®jßöØý¤ÿf{ˆô?Û{á|Óè¨Â%ñ—…—í6“µ^xr OÍà|°õÿŒ?ðR/žÖáø_çÅïLÆ(4 ¡ºC(þºEtõD%e=TW‰]|ý¾ÿl[ybøåâ[ßµ+'‡´Oßj·Vî0c¹˜1Àt;\4›y!­ûPé¿‹~5ø2Ïâà f=wA¾,±ÜF¯$Utu=U”_0ÿÁI¿äÈþ)×¥—þ—ÛW¾üøà/ÙÃáµÂ߇ r4‹)$˜½ÜÞ|òÍ1Ì’;aW,GEUQØ ð/ø)7ü™Å/úô²ÿÒûjøkþÿ$çâ‡ý…¬?ôC×îE~ÿÁ?äœüPÿ°µ‡þˆzýÈ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSàWü¥ÎëþÇÿÿèËêþªëùTøÿ)sºÿ±ÿÄÿú2ú€?ªº(¢€ ñ?г—ÁŒ×úv±ñ#ÂZ¶©¤Í Å¥þÓ ì/‰-Ì%%ØP±CÝM{eäÿuŒºgíBïà™¥êþ5W€Z[ë.édÈeQ1Ç$M‘â¸qóc¯Jüý¡~ÿÁM?i;_ ZxãÁÞ°OêÑk‡N¼š3%ÄC ²ù×3f?P»OûUû•E|OðÄŸ·¾«ãÑkûEøSÁº?„~Ë3ô9nóí#o”¸’êeØ~mß/§"¸oˆ?~:h_ðQo…Ÿ-7I¤ùž5]/û/Þyß˰2yÆ(ái —䲯䂨$þ?ø“öîÒ¼s¯ìÛáOë>ûM$úì× wöÂÏæ(]B¾X]˜ùIÉ<öü?ø;ÿ4øqñÃÇŸ4Ox|CHRþ ‹ÉšÎ!ݾB%ÊH¹Ú3¾G¯Üª(Çþju?‡Ö×´™¤é2i§Ûè­#ÙCþä©’Y[q^[çëéT¾þÎ?¾kZ¯‰¼á+-;\Öîn.ï5&S=ô²Ý;I/úD¥äT,Ç÷jÁe¯m¢€ (¢€>~ý¬ÿäÖ>2Ø›âý7O_ŠßðCßù~*ÿØ7LÿÑÓWíOígÿ&±ñ“þÄßÿéºzüVÿ‚ÿÈóñWþÁºgþŽš€?¢ª(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùÇÄ¿ñl>4éž3OÝè>=é:—d‹Qˆ±Në¢æ"z ¯£«‰øà«/ˆ~ Õ|!|ÞX¿„ˆ¥ï èwE(Ç9G¸ôÅw师¥ª|2Ñú>¿'f¼Ñω¦åÇuªþ¼ö;j+Éþ xÖ÷ƾ …õÕò¼A£K&›«D~ò^ÚŽOûã;|ÕëωÃÊ•ISžéØÖ•E8©.¡EV…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Ì7ü³þN‡ÂŸö'YÿéÇP¯é#á×ü“ï Ø.ËÿD%6ÿðZÏù: Øgÿ§B¿¤‡_òO¼1ÿ`»/ý”ØÑEÂ|Bøaðóâχäð¯Ä¿Øø—J—'ȾfTb1¾2Ãtn;:ñ¾ ð‡¾xOðÛe¥h6¦ >Õå’U@¹($ŒÎFãÉf'Þ»ª(ñûâ¾›ÿRøÁðßÄ üIào‡ÖºW‰¬å±¹–Òêå.)F ¤¼‘Ä£jÑýŸ´ø)ŸÁ¿ ø/á*ø3À’xGÿg³–æ[›‰/¾ÅææWÊ]¬f@ŒÅqÇúáE~wÁJ¾ ütø]ðCHñŸÀÏÂ=wm®ZA*¬,¶÷A Ž(ÖxäÜL΄…àÐúGöˆÕ¿i=#ÂÚtß³‰¡kšûÞ¼_’T·K?-Éhü©¡&O3`åˆÆxî;ˆß¾|Z_ˆzOöÄ~Õ-õ>6žx¢Žþ×>L¯R"K³' (täåy¯H Ã_‰টþ1|>øÙâxß\ønò¾ ­äËi)˜‚ßhW¹wlcŽ•ú5û9kß¶N±ªkIûPøsÂúŸ15¼?$Ï$“>`›Í¸œ Œ`sÖ¾°¢€2韵 ¿€Zf—«øÕ^io¬»¥“!•DÆC‘6D{ŠáÇÍŽ½+Ö( Ã_ÚàïüÓö“µð¥§Ž<à{ð~­±htëÉ£2\D0«/s6cõ ´ÿµ_y|ñ'íïªøôZþÑ~ðná²ÌÆ}[‡¼ûHÛå.$º™v›wËéȯ¶( Îï†~:Mÿø—ð³Æ ûg€-<-¯¤i¨°¶i¦´†6vHÄÉ[ƒµÜðÀ÷ùãö›ýœÿmO~Ù>è>ñ6àÈíâðͦ¹t~Í +I3Û¤‘1™n]ÝX·UŒó±qú¡á¯„_ÿ‚ É9ø¡ÿakýõû‘_†ÿðDù'??ì-aÿ¢¿r(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSàWü¥ÎëþÇÿÿèËêþªëùTøÿ)sºÿ±ÿÄÿú2ú€?ªº(¢€ (¢€ (¢€ (¢€ (¯%øÅñÓá?À? ·‹þ-xŽÛÃú"!)-=ÃŽvA’Võ§“ÍzÕÆ|<ñ)x#Eø‡àéÞçDñ²]ÙÉ$m¼2r¤£€ËŸB+³ Š( Ÿ¿k?ù5ŒŸö&ø‡ÿMÓ×â·ü÷þGŸŠ¿ö Ó?ôtÕûSûYÿɬ|dÿ±7Ä?únž¿¿à‡¿ò<üUÿ°n™ÿ£¦ 誊( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( œu¿øµÿ¬|J¿»ðÿÄA{Ù"Õ SöYO§œ™Ž¬2M}\ÄÏ[|DðF©á9ßÉ–ê=ÖÓt0\ÆwÃ(#‘µÀ'‘‘Þ²~xÞçÇ~¶¾ÕÓÉ×4ç}?U„à4WÖ§d Ów£ õ±?¾ÃÆ·Xû¯Óì¿»O’îqÒ÷*8tz¯×üþlõ*(¢¼“°(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šþa¿àµŸòt>ÿ±:ÏÿN:…I¿äŸxcþÁv_ú!+ù·ÿ‚ÖÉÐøSþÄë?ý8êý$|:ÿ’}áûÙè„ ÆŠ( Š( Š( Š( ŠkºF$ŒI8¤šá| ñGáÏÄèõ)¾ø—Oñ,Z=Á´»“O¸K”†p2cfŒ•ÝŽzÐyEPEPñÁû Éôü9ÿ°ü¿úµý×ñÁû Éôü9ÿ°ü¿úµýÐEPEPEPEZòòÏNµ–ûPž;kku/$²°DE^K36¹4fŠùÛSý®¿eùtÝGâç…b¸c·hÖ-iÿl¬„'üŠöÏ ø§Ã>2Ò!×ü!«Ùëš]Æ|»»ˆîmßol‘3)Áëƒ@´QEðçü›þLâ—ýzYé}µ}Ç_ÁI¿äÈþ)×¥—þ—ÛPÃ_ðDù'??ì-aÿ¢¿r+ðßþÿ$çâ‡ý…¬?ôC×îEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSàWü¥ÎëþÇÿÿèËêþªëùTøÿ)sºÿ±ÿÄÿú2ú€?ªº(¢€ (¢€ ¥{©iÚj£ê7QZ¬¬ ®¨EˆÉ>•ÊüK¹ñí—ÃÿÝü-³´Ô<] ”í¥[ß9ŽÚ[°§ËYcŸR ž (Ëæ;þ û5|Møeàüeý ~ ]x·âw/çŠöÉŠµ„ ›äÛÀ1 Â0±üØEÀÜÀÕEe¶¹¢¦¨ºjãQuÞ¶ÆTóÊõÜ#Îì{â¿9|kûQxƒþ ÑðãNøn¿uK‹íú„æ9môëþ¾X’ rs&dä?íû,j?³íQðWÞ ñž¥â¿x¢ïNÔn¯nÀ2ÿh¾ "IcÁ.QÝ["FfùI,AÀýìý®þ$þÒ^ðÆ…¥~̾_xÄ÷rXµä­›}'äÜ“ÍÚ¥[æÄ’H±£( ¸º©þxà ³WÇo„-àÿ‰ÿ´Äñߊ~ý¬ÿäÖ>2Ø›âý7O_ŠßðCßù~*ÿØ7LÿÑÓWíOígÿ&±ñ“þÄßÿéºzüVÿ‚ÿÈóñWþÁºgþŽš€?¢ª(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùÃPÿ‹]ñÆÛV»ð÷Ķ—’fÝOþƒíå09g5ô}yïÅ?EñÀÚ—†<Ï"îTÙÎ pðHäaÀÎ9ÆGzô2Úñ…NZŸ•Ÿ£ëòv#ŸM¸Þ;­Wõç±èTW™|!ñÌ¿< e¬_Çö}^ÙžËS€Œ47ÖÇdÈGl‘¸î°¯M®\E R©*sÝ;Ó¨§%³ (¢±,(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šþa¿àµŸòt>ÿ±:ÏÿN:…I¿äŸxcþÁv_ú!+ù·ÿ‚ÖÉÐøSþÄë?ý8êý$|:ÿ’}áûÙè„ ÆŠ( Š( Šü4ý¬ïÿnÿŠžø©ñmVãàÃ_†¢åltø¦’ K]6¯´Ì×ì¬b”¡䜪ªÈCI]ßügâçÅïþÏï¼Sw¨xâo j4ˆ¯.Ì—3IöA)³K‹†;UŸ`]íµ “À Ù óŒßì> |-ñ'ÅMSK½Ölü5h×rÚiè²\ÈŠ@%C2€«ÎÄü¨¹ÆóçûZ蟷=ïÁ‡ý«¾5øþóÀ¿iÕ!¶Óüa5Í‘²‚vq0ŠE ( »¯,êFÁûƒûx§Æ>6ý–~x«Ç×]뺎‹o$÷±ygQ•Ši¹g’ ®Ìz±'½~*þÖž0ý¿ÿho!ø×âm(|0ø?§,2.€.ßP¿µ¸™!Yg³ 2)"_&2¿2FÝOÔðD¨Ô|ñä¸å¼I·ð[8õ¯±à¤òd¿¿ëÊ×ÿK­ëãïø"_ü?ØÌôŠÞ€?g袊(¢Šþ8?aù>Ÿ‡?ö—ÿ@–¿±úþ8?aù>Ÿ‡?ö—ÿ@–¿±ú(¢Š(¢¼?ö‹ñçÄ¿†ß 5¯ü!ðt¾9ñ\>TVZl\2w‰¤U!Þ8·nuO˜ÕW. áE*¶ž£ûmüñ‚üOñ[ã&£?Š|Sk.¦l4‹éí­t§†@¢ŠH`€J&Ö!.>fýÂý£¼kûX?Áïè_³ß‡Äþ3ñصµÔõ§ù–yn6Î2ŰûP)K´`€}Õ_„¿·¿ìÙûM~ÒŸµ¿‡üáäÖ“ámå¾÷›ä}"Å·9ºŸÉÞ#2ª ô›jç¦L×^rC:Ç’Öx—æä„,¹Á¯èÿã÷Æÿ~Îß uߊþ6” =&#äÛ† -åÓñ ´Yêò7>UËŸ•IƒðK^5øßûEkµÿŽá1é:Uö¡yÅJ¥þµ¨ùžhŒwHÌì{9Eù°ý$ÑEWßðRoù2?Š_õéeÿ¥öÕ÷|9ÿ&ÿ“#ø¥ÿ^–_ú_m@ Á?äœüPÿ°µ‡þˆzýȯÃø"ü“ŸŠö°ÿÑ_¹QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÔýü¢Š(¢Š(¢Š(¢¼›Æ¾ü=ÕN…㿈‡µ ¡­þ§mm8SИä‘XØ‘\§ü5¯ì·ÿEwÂø:²ÿã´ô%óßü5¯ì·ÿEwÂø:²ÿã´ÃZþËôW|'ÿƒ«/þ;@BQ_=ÿÃZþËôW|'ÿƒ«/þ;Gü5¯ì·ÿEwÂø:²ÿã´ô%óßü5¯ì·ÿEwÂø:²ÿã´ÃZþËôW|'ÿƒ«/þ;@BQ_=ÿÃZþËôW|'ÿƒ«/þ;Gü5¯ì·ÿEwÂø:²ÿã´ô%å> øíðOâ.¦tOøûAñ¢¿ÙtýNÚê}£«yqHÍÜã½Z€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ þU>Ê\î¿ìñ?þŒ¾¯ê®¿•O_ò—;¯ûüOÿ£/¨ú«¢Š(¢Š(¯çcþ {ây/¼yð¯Àp’Ía§êEêÆúh¡N;ÿdzõ5ý×óMÿ’‡UÐ?jŸ‡þ5¹³iôÁáÛ1l¬rËe¨]K4A°y ,e½Ž(ú ðô~ø+ðsIµñì:^‰àÚ›™[lP[éöÊŒÄúNß…~MþÈš¯ûg~Ø>(ý¸¼Se5¿‚|(ͤøB ‘‚ïÕÂò1;Êã'›å'Ë8âþ¯þ ›¬iöÖqð“à´Éq?,ÓêžQÈò$få‰ÎÂ#ñ¹¼ÉC~ÜøÀ^ø_àÝ#áÿtèôCmím¢"/$’ygbK;³1,Ä’MvøEÿÅÿ‘Sá'ý~êÿú.Ú¿^þ:|tøû:øo‰_ç¸·Ñ žfkhâO2rB ‹Î OüÔÿÁI¿mß~Ö·þÐþi·ÖÚ7„Íì²]jÉs=؉@Ž$wÚˆ±ub‹}Ð,ý ~ÅòhÿìYÓôB×ÔøMû ÁSÿg߇ß<ð¿â¾­¤k±NiàµV³ÅË£Fþ`%1¹Z1ƒœ+÷]dE‘~ëGÐШ¢Šùûö³ÿ“XøÉÿboˆôÝ=~+Áäyø«ÿ`Ý3ÿGM_µ?µŸüšÇÆOû|Cÿ¦éëñ[þ{ÿ#ÏÅ_ûéŸú:jþЍ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùÆ_øµß’õ~ø–oÙ!Ö­×å>ƒí1qêÎ3_GWœ|Xð7ü,?j·“ìúŠí¹Óç í¹ß€öù† ÂM üsÿ ÀÚˆ.#û>¢¡­¯à# í¹ÙUúþ:üÏG¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( Š( æþ Yÿ'CáOû¬ÿôã¨Wô‘ðëþI÷†?ìeÿ¢¿›ø-gü…?ìN³ÿÓŽ¡_ÒGïù'Þÿ°]—þˆJìh¢Š(¢Šüçÿ‚«ø¯þŸØ«Åö‘¾É¼Au¦éÈ{ü×qÎãñŽC\¯üÂðŽþÇ6:ÉM§ÅÖ§¨çûÂ7[ýµ®'þ J—Íû+øpÛ+4 âë9\ð¿a¾ »ÛqëŠøËàoíÝãèfÏ~ÉŸ²g‚µWâ‡Ùn-.u "ŒÛØ™îe‘§C0b›Œ³ùqÄy`ü€ôíï¯_~ן´O€a†w&{M&øjþ,»€K ‰·–éºÞ •<4²ÇC‚+ö@д¯ èZo†´+uµÓt›hm-a_»è#²ª€+ã_Ø“ö=°ý—|}ªxšðx‡âW‹_í> ÕÙšRò3ò"’OœÆ¬Å™Ûæ•Évãb¯ÚºŽ¡e¤é÷:®¥2ÛYÙDóM+œ,qÆ¥˜öM|[ÿ ÿ“%ø¥ÿ^V¿ú]o_ÁÿäøëþÆcÿ¤Võü/öîýš|_û4x³á_ïCâ¯x¡mmáŠÆ9^(’;˜¦’YgdXÀ ,XŽ1’>cÿ‚Q~׿¾ø7Æ>/x|3qªjqê6W3Å+Û̆D2DŽ—ËçÚ<ŒPôErñ÷ƒ>'øVËÆÿõ{}wAÔ|ϳÞZ¶ø¤ò¤hŸiãŸpk¯ Š( ãƒöÿ“éøsÿaùô kû¯ãƒöÿ“éøsÿaùô kû Š( Š( æ›þ }»âÇíûà„ññ­®…£ÇüõÔ¯]ØýJNƒðýøÇÅþøyá=Wƾ.¾KÐô+i.®î$á"†%É8“ÙT “€$ þeÿkßi_ à«·üsmq6‹áÍ[Úƒ¥º•­íôë6Ý»"± ¤ãpù¯©¯üAñãþ ¹ã _èšmïïÙ×F»Yo®fùnuf‰²pÊK0ÇËnŠóÈÒ8ŒAÿ‚~øC^ý£?hïˆß·ç¬¤µ°Ô®gÓðe„z^‡¡ÛÇkimùcŠ1€2rI=Y‰%‰$’I5øÿý¨nµ_XþËÞ¼hôýaÔ'ÿÁ\?h–;®¼5ð/À³àmmù£ßÝ(ÿimãõÿ–¿Ðw¼áO†¾Ò¼ à}6-#BÑ`[{KXF8×Üä³1%™˜–f%˜’I¯Á?¿ðUOÙ×ö~øa¢|+ðÂÝf7Gˆ+ÊnmD·W Ì·ÖI[,{a@¿ü—â?í9û[üð¯ÃXï¼á×- ¿³K±,š¢Oâ¿–/øt'í“é¡ÿàÐÿñº?áП¶O¦‡ÿƒCÿÆèú«¢¿‘ωðL_Ú£áG€uï‰,@Ñü9i%åуQ2KåD2ÛË>Ù¯ˆ¾xÄÿ|{ ü8ð«¡Öšþ ÿ£þ ûdúhø4?ün€?ªº+ùTÿ‡B~Ù>šþ ÿ£þ ûdúhø4?ün€?ªº+ùTÿ‡B~Ù>šþ ÿ£þ ûdúhø4?ün€?¢¯ÚÏþMcã'ý‰¾!ÿÓtõø­ÿ=ÿ‘çâ¯ýƒtÏý5|úà¶AЈ?õ?ün¿M¿à™ÿ±oÆŸÙWÄþ9Õ¾+&œx‚ÎÊ _±]} ï‚I÷ ‹† ×j(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùÆøµßžßý_‡¾%ƒ"vHu«eùÇ ûL\ú³Œ ú:¼Ëâ÷¥øàkÝO“ìú½«%î™88ho­ŽøXÓ$m'û¬kÑË+F3tê?vJÏ˳ù;3›ãÍÖ«ü¾kCÓh¯>ø[㘾"xLñ?—ä]J†+ÈÁ‚î²xÈ<®3Î0{× ×z2§9Sš³NÌڜԢ¥˜QE‘aEPEPEPEPEPEPEPEPòÉÿˆñw„üeûJøgQð†µe®ZÛøNÒ &°¹Šê4™of‰˜”•' qȯé£áÃ+ü=ð»¡[K² ŽA¯áKÄÐýŸÄš´<»¹×ò‘…~‘è¿ÿଖ:=އañtëx"ŽØGá©Ù Gµ¾Ær»@ÁÉÈ êöŠþU?ásÁ^?çÇâ'þ3ÿòð¹¿à¯óãñÿ ™ÿù €?ªº+ùTÿ…ÍÿxÿŸˆŸøLÏÿÈTÂæÿ‚¼ÏÄOü&gÿä*þ |kàoüGðåׄ<}¢ÚxƒD½Øf³¾…gÊ0t%•`=A•à… ~ØI¥ü3ð¦—ákiö™SM³†×Í+ÐÈcU.Fz±&¿™?ø\ßðWùñø‰ÿ„Ìÿü…Gü.oø+ÇüøüDÿÂfþB ê® ºµµ¾µšÆú¸¶¸FŽX¤Pèèã ¬§ ‚<_Êßü.oø+ÇüøüDÿÂfþB£þ7üãþ|~"á3?ÿ!PôYÿ û,ÿÑðgþÚwÿ£þ;öYÿ¢7àÏü'´ïþ1_Οü.oø+ÇüøüDÿÂfþB£þ7üãþ|~"á3?ÿ!Põá_øSÀº·…ü¢Ùx{F³Þ`±Ó­£´µ‹Ìs#ì†T]ÎÌÍËO$×C_ʧü.oø+ÇüøüDÿÂfþB£þ7üãþ|~"á3?ÿ!PõWE*Ÿð¹¿à¯óãñÿ ™ÿù ø\ßðWùñø‰ÿ„Ìÿü…@û Éôü9ÿ°ü¿úµý×ñ[àÿÿ¶‡€|caãï|0ñ®™â2cqmw‡o‹Ç)­Êçõõ?ü.oø+ÇüøüDÿÂfþB ꮊþU?ásÁ^?çÇâ'þ3ÿòð¹¿à¯óãñÿ ™ÿù €?ªº+ùTÿ…ÍÿxÿŸˆŸøLÏÿÈTÂæÿ‚¼ÏÄOü&gÿä*þ“7×n|QãO†~×õ›Ý†{ÝCF²ºº›ËAy“K;mE 2Np6¿ð¹¿à¯óãñÿ ™ÿù ø\ßðWùñø‰ÿ„Ìÿü…@Ñgü2wì³ÿDoÁŸøOißübµ´Ù»öwð®³iâ/ ü-ð®ªéò m¯,ôK{ˆ$9c…]z©¿›ßø\ßðWùñø‰ÿ„Ìÿü…Gü.oø+ÇüøüDÿÂfþB ꮊþU?ásÁ^?çÇâ'þ3ÿòð¹¿à¯óãñÿ ™ÿù €?ªºøsþ Mÿ&GñKþ½,¿ô¾Ú¿ ÿásÁ^?çÇâ'þ3ÿòr=ñGü'â„uø÷Ãß5VTK«I|5r©*£¬Š K5nTðGJýÿ‚ É9ø¡ÿakýõû‘_ÿðG†?~øâ5ŸÄ êÞžûS²’Þ=ZÂâÅæD…Ã4k:!` Á# ý Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÖýü¢Š(¢Š(ª÷v¶÷Ö³XÞF%‚á9º28ÃìAÅXª×—vÚ}¤÷÷’­í‘¥‘ÏED˜Ÿ  æÛâgü‡ã¯Œ5øU~ Ó/ü7$®öŸn•Ṋ&9XäÂ0fQÆá×®Jà?áÍŸµ/ý4ü þ7]oÄÏø,ÇÇ ÿêð·GÒô¯ ¤®¶i{\\¼@á^S¼Ì9*82z×ÿ‰ý­?¹¡àÿ㔥ÿlý©è! ÿà[ÿñº?áÍŸµ/ý4ü þ7Y¿ðøŸÚÓûšþþ9Gü>'ö´þæ…ÿ€ÿŽP—ü9³ö¥ÿ †ƒÿoÿÆèÿ‡6~Ô¿ôÐð-ÿøÝfÿÃâkOîh_øøåðøŸÚÓûšþþ9@_ðæÏÚ—þ‚þ¿ÿ£þÙûRÿÐCAÿÀ·ÿãu›ÿ‰ý­?¹¡àÿã”ÃâkOîh_øøåiÛ?j_úh?øÿünøsgíKÿA ÿßÿÖoü>'ö´þæ…ÿ€ÿŽQÿ‰ý­?¹¡àÿã”ëÿ¿à?×ÇzUïÅ{K´ðåœñÍr–R<óΑ°c‚ªv0I< þ­àŽÖÞ+hF#…UzüØ|!ÿ‚È|jOéVßt/PðÝÜñÃtlàkyáØ)•æ®s‚9ý'ÛOÕ¼WPœÇ2«©õ 2(j(¢€ (¢€ (¢€ +åÏÚcö¿ø;û'Úè7f½2xçKH, [‰X[2»+:‹æ(Îzžqè?>9øö‹øqcñGá¼óK£ßK<.c\E-¼…%@Ìðrr¬§½{Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@+~Ü+»öBø¸?ê\¾?”d×ò¡û6ßÚëáÿ©“Nœ Wõmûm.ïÙâèÿ©kQ?”,kùBýŠÛoíoð„ÿÔÏ¥ÎáEkôQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE|á§ůøãs¤Ýø{âH{»~ɳn£ÏOAöˆðù<³Œ ú>¼³ã‚.|wà{›!üsMxõ *aÃE}jwÄA=7r„ú1­†~7µø‹à/Å–éäÉwÛˆO ˜ÎÉ¢ ò6¸ g’0{×­Œýõâ:¯v_/…üÖž±}ÎJ>äÝ>›¯×ñüÎòŠ(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ƒ?ˆPýŸÇÞ%ƒ§—©Þ¯å3Šþæ>Íö‡~Ÿ¯™¥X·çþ2>(|øËwñGƧx _º…µ@Æñiwn®¦áðT¬dG ŠþʾÅu¯C}ÛÜÇ¢éË,r)GGцVS‚<y€=Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿ×ýü¢Š(¢Š(®?âüˆ&ÿ°eïþ‰zì+ÅZdú߆5}Ø…šþÎâÝ t ,lƒ>Ù4ü×ëìIÿþøcûMü ñwÅ/xÞãEÔ´»‹‹[h-Z Ÿ‘ Ê'½ó™‘ËpªS ¤î$á.üOá{Áž!Ô|)â‹tÝ[Ií®­¦R²E,g ¤Ðô#‘ÅU±Öõ2ÞâÓM¿žÒ ÅÛ4qJȲ¯£… 0úÐ[ÈÖî{eJ!v@ëÑ‚œd{ý]øçÿúøað·ö(пi-ÇSßøŽêÛLºšÞF„ØÝÿh´jÐZ*¨I˜X–vÊÆùUíù3ZsëZÅÖŸ‘s}<¶6Ä´P<¬ÑFOR¨NÐ~‚€='à/Ã}7âÿÆ? |3Ö5eÐì¼E¤×Ý#žHÜ@Üz.xÉõ§ü7ö>ðìãO èÿüGu¬Yø‚ÎYä¶Ô'»·xYWqhR0c“?.PTò{~yG$‘:Ët9 #¸5{TÖ5mnçíºÍì÷÷ æO#JûW€71'€3¨¢Š(¢Š±iÿPÿ¾¿Î¿½ßÈMÿ¯hô_Â?€ü%¯øóÆZ7ƒü/g%þ©«ÝEo1)fgv ì:“Øs_Ýö‘o%¦“ei0ÄÁ0÷UÐQ@Q@WÎÿµÆ›Ù÷ö|ñ§Å3"­ö—bñéêØ;õ ’!µî®¬Ãû¡jüAøÓ`Ÿ·ÿüÅ> ä“Á> Y´Û‰ bZéažö@GC-ãyÇQ°çIÿ‚JøóZøEñ›âwì}ã©<›¸n®.­#c…þÐÓ_ì׊€õó¢ =6ÄOzôoø#Á‰ôÏø¿ö†ñ4š‹îŽ›c4¹.Öv¾æPÇ’&¹;[?Å xüOGÕ?eOÛ‡á÷ígáKv[-zH.îÖ?”Ku§í·¾‡Ž\YºV,çÔÐôOEehZÞ—â]Oñ‡p·zv«oÝ´É÷e‚tFãÙ”‚+V€ (¢€ (¢€»Ú‚ؤuñš<‚Ú‹¸úRnÿ9¦>÷Z­qwmh†IäXÐ ’iÅ7¥‰uܹ¸úqõ¥ÝÎ+Â|cñÿáï„RAw©ÄeNƒwʾtÔmm=ÆÊX_3ÍvRËêÍ]#ÄÅq“´¤~€ç¶)7úŒWç5¯í» ÍäcØV‡ü6µ“r"N+G–ÔZ4sÇŠðnÏТØê(ÝÆkóŠëöÜQŪÂÄzŠåçý·üL²m¶·µ+ë´ÿZÉënL¸»ÛûÔRÇ4‰ŒWåì_·¼r%ŽÐ7û§üjSûqkc–Ÿ÷ÉÿŸìº·²Fë–m~ãôø6N(Ý_— ûrx‹8X­üÿ4~Ü^%Ï1ZcýÓþ5O'®ºã<êþãõ'4n¯Ìý¸uü|ÑÚß'ühoÛ‹\í¦Ý?ãJMWÐkŒ°]ßÜ~Ÿîö£u~TÞþÜÞ1P~Ç™=²‡ük$~ÜßHɶ±öͿƫû"©ëž wûÖÚ3_’öèø”åÖËþøoñ§ÿÃsüIÿŸ[ûá¿ÆšÉk4'Ƹ5ßî?[3íG8¯É/øn‰YâÒËþø?ãJn‰_óéeÿ~ÏøÔ¬¢°ÿ×<w÷­™>”™¯É3ûsüIòëeÿ|ñ¤ÿ†éø“ÚÖËþýŸñ¦òzÂÿ]0~qúÛº“8Å~IÿÃt|Lígcÿ|ñ®–Ëöãñ3D ì6‹' Sþ4ÞMYnRã,w÷©!…-~_ÿÃp뀪µ$ú)ÿ‚OÛ‡Äàâ8-ï“þ5)ªWúãƒóûÔlÒn¯ËømÿÿÏ OûäÿX_ÛÄxùà´Ïû§üi¼¦ªxÇú¿¸ý@Íã5ùÿ Áâ ó¯ýòƪ?íÅâ ØK{\ºÆ—öURŸ`×W÷©;©sšü¶ÿ†àñOk{_ûäÿXOÛ{ÄØùíí‰öSþ4<¦¨£Æ87ßî?PsIšü¾oÛwÄÀ|–¶ùÿtÿ@?n½ioÿ|ñ¥ý“To‹ðžqú‘š WåÒþÛÞ-Þ –Ö¡;ü‡?λ;_Ûf™ãˆHzàqCʪ•-ÁËDßÜ~‰n£wÅ~w\~Úð…Ì }ÅdÿÃm^ޱÁíòŸñ¡euˆrâ¼,wlý' ž@£&¿9-¿m‰Xÿ¤$ >ŸýzÑÿ†×µàmˆýK˪- ‡a%³?B·P[ùÑuûk÷µŠ#øõêˆý¶/‰?ºƒcþ5K*©¹œ¸·µ?IsJ ~sÚþÛ*ßñò¯Ðõêèý¶,H9Xÿ*_Ùµ WaºgèVê\Šü߸ý¶f ‹x¢+î?úõý¶®³Ìp~_ýzÙU ¥Å¸EÜý%ÝFkó²ßöØŒ kˆáØTíûkZíýÜqçéÿפ²º¬ÑqNìÙúšMÕùºÿ¶ÕÈn"„qÿפ¶ÝÈaº(qô?ãN9]V¶2ÿ[°‰Ù·÷¤{ŽqŠ7sŒWÁÚGí«áVûbh¡=ñÅzn‰û[ü%Ö'KdÔãYÕ¿úÕ‹Ëê­,vQâ,$þÙõoAK“Œâ¹=Æ^×aI´ëø¦WO­uHÀŒ©È5Í88èÑëÓ¯ «ÁÜvâqJFi¼úR磨¤Ö–€ (¢€ (¢€ (¢€8‰ŸôOŠß¼Eð×IJO•âkôû§¶eI–„(Æ6eu ƒÁ*Gµ| ð×þ Gû4|+ñÿ‡¾$xoRñ$º¯†o­õ U¹½¶xLÖÎ$A"­ª¹€Àã½}©û@øóZø[ð7Ç¿<9j¾Ñ/õ+T¹VxkXD*21RWwøYðþ ÉûK|Pøãà‡>"Ò|3•â}wNÓnšÚÊé&Xnî'1³Ýº†Ã¤g± èÊŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾pÑ?â×üm¾ðË~ïÃÿšd‹V„µD=<äÄœõ#¾¯'øÏà«ßx&eЛÊñ,zž“(ûÉ{jw Î2‡<|ÞÕée•b¦éT~ìô~]ŸÉÙú]u9±PvçŽë_ó_4zÅÄü9ñ­—Ä?i^/±_,_ ±w†t;eˆçœ£‚¿†k¶®Ô¥NnVkFo ©%%³ (¢³((¢Š(¦î^äRy‘ÿx~tú*?:ÿ-óß´[ùj¿÷Р ¨¨>Õj:ÌŸ÷Цý²Ðu?ï¡@hª¿n²ïqýö?Æ›ý¡aÿ?1ßkþ4rŠ¥ý¥§ùz‹þû_ñ¤þÔÓ[¸ïâÿ^¢³ÿµ´¡Öòûø¿ãIý±¤Ž·°ßÅÿâ/þ2|!Òîf³Ôüq¡YÜ[»G$sjv±º:œ2²´€‚Ák¿³¼³ÔlàÔ4ù㺵ºeŠX˜ ïâ ?ÿáÿâ©?á;ð@ëâ;ÿáÿ⨪¢¹?øO| ÿCÿpÿñTŸðŸøuñ›ÿÿñTÖÑ\‡ü,ù™4Ïü ‡ÿ‹¤ÿ……àþ&Ó?ð6þ.€; +Žÿ…‰ðüuñ>—ÿ°ñtßøXßG_iøÿ@ÅÿÂÈøv:ø§JÿÀè?øºoü,¯‡#þf­+ÿ ÿâè¶¢¸øYŸ ÇüÍzOþAÿÅÒÂÎøj:ø³HÿÀûþ.€;Š+…ÿ…¡ðÐuñn‘ÿöÿü]'ü-/†]ü]£ÿà}¿ÿ@ÝÁÂÔøaÿC~ÿƒ þ.“þ·Âïútoü[ÿñtßQ\ü-…üe£àÆÛÿ‹¦ÿÂÚøU߯z/þ m¿øºô+Ïán|)|i¢ÿàÆÛÿŽSáo|'ó:èŸø2¶ÿã”è”WŒ G_hø2¶ÿ㔟ð¸¾ùô?üZÿñÊôj+Íÿár|!|q¡àÎ×ÿŽRÂæø>:øçBÿÁ¯ÿ I¢¼Óþ?Ááÿ3ÖƒÿƒK_þ9Iÿ §àçýšþ -øåzeæ_ðºþ ùŸtüZÿñÊoü.߃¯¼?ÿƒ[Oþ9@ŸEyü.ÿ‚ÝüáÿüÚñÊoü/‚¿ôP<=ÿƒ[Oþ9@¥Eygü/?‚ôP|=ÿƒkOþ;Iÿ ×àý/ÿàÚÓÿŽÐªQ^Sÿ ßàxëñÿø6³ÿã´Ÿð¾~Ž¿<9ÿƒ{?þ;@¯Ey7ü/¿c¯Ä_àÞÏÿŽÒÂýø?æ£øoÿüv€=jŠòOø_ÿüÔ àâÏÿŽÓá ~¿|5ÿƒ‹/þ;@»Eyü4ÀA×âW†ðseÿÇi¿ðп‡_‰~ÿÁÍ—ÿ `¢¼{þàëñ3Ãø9²ÿã´Ÿðѳðÿš›áüYñÚö*+Æÿᢿgßú)ÞÿÁÕÿ¤ÿ†ŒýžÇ_‰þÿÁÕÿ e¢¼cþ;öyïñCÂßø;±ÿãÔŸðÒ³¸ëñG¿ø;±ÿãÔíW‹ÃI~Σ¯Å? ÿàòÇÿSá¥?gA×â§…?ðycÿǨÚè¯ÿ†–ýœ‡üÕ_ àòÃÿSá¦?g×â·„ÿð{aÿǨÛ¨¯ÿ†šý›‡_ŠþÿÁí‡ÿ¤ÿ†œý›_‹ÿÁí‡ÿ p¢¼7þ{öjóV|%ÿƒëþ=Iÿ Aû4ù«^ÿÁöŸÿǨÜè¯ ÿ†¢ý™Ç_‹~ÿÁöŸÿÇé?á©?fQ×âç„?ð§ÿñú÷j+Â?á©¿f1×âïƒÿð§ÿñúoü5GìÃßâÿƒÿð Óÿøý{Íà¿ðÕ_²ÿýáA§ñúoü5_ì¼:üaðwþwÿ |¢¼ þ·ö\óX¼ÿ…ÿÇé?á«ÿe¡×ãƒ?ð¡Ó¿øý{õóÿü5ì°?æ²x3ÿ ;ÿÒÃYþËþk'ƒð¡Ó¿øý}E|ùÿ kû+ÿÑcðoþÿ¤ÿ†¶ý•¿è±ø;ÿúÿ ¡(¯ž¿á®?e_ú,~ÿÁý‡ÿ¦ÿÃ]~Ê¿ôX¼ÿƒëþ=@CÑ_;ÿÃ]þʃþk„?ð{cÿÇ©¿ð׿²˜ÿšÅáüØÿñÚú*ŠùÓþûöRÿ¢ÅáüYñÚOølÙKþ‹„ðweÿÇhèÊ+çøl/ÙGþ‹„¿ðweÿÇi?á°ÿeú,>ÿÁÕŸÿ £è¯›¿á±dñÿ5‡Âø9³ÿ㔟ðØÿ²xÿšÁá?üÚñÊúJŠù³þ#öNÿ¢ÁáOüZñÊoü6Wì›ÿEƒÂ¿ø7µÿã”ô­óOü6gì™ÿEƒÂ¿ø7µÿã”ßølßÙ/þŠÿ…¿ðmmÿÅÐÓWÌßðÙÿ²Xÿš¿áoü[ñtßømÙ+þŠÿ…ÿðkoÿÅÐÓtWÌðÚ_²Oýÿ ÿàÒßÿ‹¤ÿ†Ôý’üÕï àÒßÿŠ ~ñ]þ³¥ø_XÔü9b5=ZÒÎâk;BÛEÅÄq³EnÛÜÏl×ó¿ð‡þ ñ«Àž;¾ðÿí9á”Õ4ß´´wÙÚÿgjz[†A „,:yrí“ÖNÇöƒþ[öHÿ¢½áŸüÁÿÅWÊ´ÇüCö£ÓY~!üDðŶ½{-µ» JÞ F”2D¨?眪ê9Úó@wüøõð—öðºø·á7ˆíµÛ Mgeͳ°û—>$‰ºà2€z©#šõúþ1> xcZý‘>"ÚøÇöøË¥xž![=[ú„kv«÷¼»Ë0ìB°0>l-Àc“¶¿Z?eø,O‡õÁiàÿÚŠÉt[ó¶4ñ „LÖrž€Ý[&焞ïd$ýÈÔf€?tè¬ø‡Añfiâ? êVú¾•–ÞîÒTž ôd‘ VàÖÅQEQEQEQEÿÐýü¢Š(¢Š(¢ŠçÖõ[&Ùqgcs4mŒá㉙N>¢€>AøÓñ{þ û¥øÊ}/ã•ï‚ï¼Ojs.¥ao¨ÝCމ#y2²ýÖ ûW‘Âëÿ‚MÏ?‡¿øOÛÿò)º†¡}«_Üjšœïuyy#Í4Ò1g’G%™™$’rI®ïÁÿ>(ü@Ñ5_x'ÂÚ†·¥è‹ºöæÖÝåŠã¹€Æ@äÈÐôïÿ ¯þ 5ÿ<þÿá?oÿÈtÂëÿ‚MÏ?‡¿øOÛÿò(DH#W¢ë~(x{ÁZÄ}sÂÚ…†5B×Q–ÝÖÚRÜ®×#68=ûPôñÿ ¯þ 5ÿ<þÿá?oÿÈtÂëÿ‚MÏ?‡¿øOÛÿò)úf™¨ëZ¶‘¤[I{{y"Å 0©y$‘ÎUG$“ÚºÏü1ø…ð¯VBøáëßßÍš8oah™ãnŒ¹ê>”ýBÂëÿ‚MÏ?‡¿øOÛÿòðºÿà“_óÏáïþöÿü‡_Ê ý_Âëÿ‚MÏ?‡¿øOÛÿòðºÿà“_óÏáïþöÿü‡_Ê ý‘|ø·ûêÞ0M/à5ïƒ,¼Mt¥#M2ÆßN»™{¢ ,þê¤ý+íªþ|9«ê:ˆ4ÝsG­o¬.bž PÉ$nXèEyú,Ò\hÖÒKo1õ,€“@tQEQEü÷ÿÁjþ8ý§Rðoìó¤\e,Ôëúª©ãÍ4q¶:O9ʞ·ҿ -OR°Ñ´Û½cUml¬b’yæs„Ž(”³»ÁTkù­ý¼-oûwþß^5øãñJ‹WðŽ×”–wÐ­Å»Ç 6zU¤Ñ¸em‘.ü‚a9â€>³ø+ÿBýŒ~ ü&ðŸÂ½ÏĦ×Ã:t{×M€yÒ¢æiˆûOYe-#{±¯žooÛ×öVýª~\xÃVšô^(Óo­µ &[«c…fFò¦Yn‚4IИ)Çûsÿ ­û0Ñ ðwþúÿ¤?²§ì¾À©øAàü?ä_ÓÇþÑ ?à’¿ái~ÌpøS¸óu¯†÷ÙnåÍ„¹–ÉÏ¢…ß ûC_¨ÕüÖ~É÷w?±'ü“Ä?uyZ xªêM!;Z+².tyŽ~óÉ 982¸ê+úS Š( Š(  }q®ÓEÔOÝö¥·”Űe¼Í‡nÑÜç¯å¥¾!ÿÁ]UI{oˆŠ;“£\ý¶¯ê¨“Ÿjù ö£øáà MM“¥Ò€qÒ¶£EÔ•¢pæøá©:“?köˆÿ‚—è2}›^×¼e§ÉýÉìÚ&üšk½øËÿñzùZ¿Œ5nËo1Ïýñ~§|%øIâïŽÚܺæªòIe¼—vÉÇ=}ó¡ü2Ð| ö\VI)‹€äsÇzõ>¡éÌî|”8†½kËØ®^ìþkSÀ_·î³ÚÂ~6¼GçwöeÓƒøùF”ü!ý¿1¹¼ãLz"çÿWõ[ᾘëk&<ŽƒÚ½¢ ¡º„K ¬+гœ¹ŸÞ{Ø(áëFþÎ7ôGñÀnû'Û„¼_ÇKœÖ*Ú‡ÃÿðP|aðçŒ=\¿üj¿¬øY.®í‡Î:×’´rC!IWc)­¡e~vqb«FŒ­*1ûæ[þ‡ü:rf ñ›–êF“??ù  ›ágü"-?‚ügú.p?ôUV~ñC[ºØÝ¶S±5ê„EuÍó£Šç›œ^­žŽzð¼aú#øÛo†?·;1gð—‹sß:|ßün§ƒáíßxÑüâéOû:|Çÿd¯ëÅ^k'kËUÌg“\m•ìö3‰à8ÚyÓ7©³Ë«‰…)òÔ£¸þ_ÇÀ¿ø(äxÆ_ø-Ÿÿˆª÷¿o{A›¯øÂ!þÕ„ãÿd¯ë×ÃÞ!‡T„!oÞ(æ¶5>ßQ·hf½s{J‰ÚRg®¨Q>ztâþHþ9áU~Üçþe?àßüM[¶ø/ûyÞÿǯƒ|]/û¶Sý–¿ª wF›F¶Œ§o¥géڅΛp³ÀÄ)ê+§ÙÉ«ÆOïÁH.×}¶ƒâù÷mÿÇjWýšÿà¤Ì7I῜zÝ7ÿ¯é³C×n4‹…VlÄÕî~¡£n²ÂrZʬe»;0U¨V¯è!S|ÿ‚…ÂÅ'Ñ|X„v7l?ö­:ðPÙE•âÍÇ Ì?öµZ$ðÔ:”&h†$•ã6ÓYNb”e<Öœ•ùÎ<]gFZÒ½æÅ?f¯ø)L‹º=ÅäâüÿñúŠoÙþ F€Ë7‡|\ê~ÜøýR~ñSDVÊó§@Mzª²Oz«V5âõlôpÓ¡Z7ŒUýü~7Àø(B±WÒÁDî›ÊµÒ¼XÇÐjDíÅUÞ)ð©`×–kÓ¨æhÓÚ>ø²$S[Æ KI3˯Šö3å8ÛÐþmGì·ÿ,qÆâòýDÏÿ$U;ŸÙwþ =j¦[Ũs©éq_ÕŸ†|QÜkkppê1]¼ÐEsŽA¹XW<”£+I³×¥5aͯ¸þ?¿ážà àÿÈ/Å@ú‰ŸþH«¶¿³üRõ¼»]/Ån}?µˆþw5ýNx“ÂoµÍ’üMpP»[ËçDJ²õ®¨ÒRø$϶7ÙJÓ§¸þm›öNÿ‚•:åô°÷Õ¿û¦±®?foø(…›ùw:7Š£aØê¿ýÓ_Ö†|N—±­µÓbQÀÍmkº­¹*1 æ|ñ•›g§xT‡=(ÇæäVoÙöû¸æãDñ$Ÿïê€ÿ;Š·eû#ÁAg>e†¼FHÿ¥5ý8j|ú}ËA8ãÞ¥Ó5K­._2 >_NÕÙõm/ÌÏ"9ŒT¹jB+ä0š§Àÿø(WƒWÎÔ-<]§þ$ÕdãþýÜÂ_þÞöŒ-ŒüomŽ]Æ?[+úñ°ŸFñ=°Šú™±‚f¼â×ìµàßÛËweÙ.Ô‚0“ïÅeJ¥.Z²híÄVÄÂÓµèïùŸÏ7‡þÁRüWj·¾×3ùTÿ†zÿ‚·ùmã¯ü*Oÿ'Qÿ ïÿm?òÛÇ?øTÿ÷uUtPò©ÿ çÿl?ò×ÇøTÿ÷}ðÎðV³ÿ-)ÿ¶Ûÿ–5ýUÑ@ʧü2ütÿËá[mÿË?áેþ]üOÿ…m¯ÿ,kú«¢€?•Oøc¯ø*Ùÿ—oá]iÿË?á¿à«Gþ]|Iÿ…u§ÿ,kú«¢€?•Oøc?ø*Éÿ—_ÿá]gÿË?á‹ÿà«þ]øaoø*)ëi¬ÿáYgÿÉõýUÑ@ʧü0Ÿüõ´Ö?ð«´ÿäú?á„?ਭ¦¯ÿ…]§ÿ'WõWE*ŸðÁŸðSÓÖ×VÿªÓÿ“¨ÿ† ÿ‚ž¶º¯þV¿ü›_Õ]üªÃÁN[]Sÿ ›_þM£þþ jzÚêøTÚÿòeUtPò©ÿ ÿ3=muü*-ù2øwïüÀõ¶Ô?ð¨¶ÿäÊþªè åSþõÿ.=m¯ÿð§¶ÿäº?áÞÿðR³ÖÚûÿ {oþK¯ê®ŠþU?áÞŸðRƒÖÚ÷ÿ koþJ£þãÿ&=m¯?ð¦·ÿäªþªè åSþÝÿ#=mîÿð¥·ÿäª?áÝðRÖÞëÿ [þI¯ê®ŠþU?áÝðQãÖÞãÿ KþI£þËÿ=mçÿÂ’þH¯ê®ŠþU?áÜ_ðQ“ÖÞoü(àÿäŠ?áÛÿðQsÖ ð£ƒÿ’+ú«¢€?•OøvçüPõ‚Oü(¡ÿãôöà¢'¬ÿ…?ü~¿ªº(ùTÿ‡kÁCÏXÿ øõðí?ø(aëÿ†þ=_Õ]üªóÿà¡G¬?ùpEÿǨÿ‡fÿÁAÏXWÿ ¿øíUtPò©ÿÉÿ‚‚°§þâÿã´ñ¿à G¬1ÿàþ/þ9_Õ]üªñ?oóÖ¿ð}ÿ£þû~ž°Ãÿƒèÿøºþªè åSþ{û}±AÿƒØÿøº?á×_·Áë¿þcÿâëú«¢€?•Oøu¿íêzÅmÿƒÄÿâ¨ÿ‡Z~ÞG¬VŸøeäñXŸðèOÛ$õ]ÿ‡ÿWô›ñ³Áº‹|×^ù×RµYï®aÜK,bE[V‚°Üs^+û[ü-ø‘¨þÔÿî´Ÿ j×¶÷>)ÕåŽH,g‘dº‘ƒ+*AÏWõMû Ú_Ø~Ë? ´ýVÚ[;ËO éPË ÈÑËÇlŠUÑ€*F9f€?Ÿ_øs?íiÿA? àÂçÿ‘)ßðæoÚÏþ‚žÿÁ…×ÿ!×õ)E-Ÿðæ_ÚËþ‚¾ÿÁ…×ÿ!ÒÿÙ?kú øSÿü‡_Ô•ü·ÿØÿkú xSÿü…Kÿbý¬?è3á?ü¼ÿä*þ£è åËþÃûWÿÐoÂ_øyÿÈT¿ðæÚ»þƒžÿÀûÏþA¯ê2Šþ]?áËÿµwý|#ÿ÷¿üƒNÿ‡/~Õ¿ôðþÞÿò Q4Pòïÿ]ý«?è`ð‡þßò;þ·ûUÿÐÃáü¾ÿä þ¡¨ åëþµûUÿÐÅàÿü¿ÿä _ørÏíUÿC'ƒ¿ð:ÿÿ•õýBQ@Ëïü9cö©ÿ¡—Áßø¨ò¾—þ¯ûTÐÍàßüÔ?ù__Ô üÀÕ¿jú<ÿÚÿ+©áÊ¿µ?ý > ÿÀíGÿ•Õý?Q@Ìü9Sö¥ïâ¯àv£ÿÊêwü9Sö£ÿ¡¯Á¿ø¨ÿòº¿§º(ù…ÿ‡)þÔ]üWàïü Ôù_Nÿ‡)þÔô6x;ÿ5þW×ôñE1ðå/Ú{¿‹|ÿz‡ÿ RÿÔ¿iÎþ.ðþêü_ÓµüÅÿÔ¿i®þ.ð‡þjüKÿQý¦;ø¿Âø¨ò NtPóÿQý¥ÿèpðþêüƒKÿQý¥èpðþßÿò NPó!ÿPý¤ûøÇÂ_øÿÈTïør‡í#߯^ÿÀ‹ÿþB¯é¶ŠþdÿáÉÿ´øOþÿßÿò;þŸûGÐçáOûýÿÈuý5Q@ÊŽ?à•~FÍãŠ^ ±˜ ‹qq%ËÓ¥™|\cÞ¼Ãÿ±å¥½Ùø‡íp£娯cëæKΧ–Ò¿§ŸÚ ötо1i­ªéþ^â‹DÄXÂL«Ò)ñÉ_î·U÷ñ’úÎm>öâÂà©–ÚG‰Ê:È›•;]IVH=AÅ~¥Áy[Чí'yMnžËîÝz¿‘ò™Þa‰¥.Xé³G1ð¿ösÓõ½I>ü%Ó¬´ÏêðÏ µíÖöòÜDÍæK0Y%  !G°Oþ›ûCwñ¿…¿ïå÷ÿ"×èïìá·øÇ^ñ¬é˜ô›UµˆžžuÓd‘î©Ù«õ"¼~>¯OëqÃRIF e¢»Õþ;x~ö.¬ÝÜŸåý3ùœÿ‡&þПô;ø[þþ_ò-/ü97ö‚ÿ¡ãÂÿ÷Ý÷ÿ"×ôÅE|)ïÌ÷ü93öÿ¡çÃ÷Ýïÿ#S¿áÉŸûøçÃ÷Õïÿ#Wô¿E0~)ÿ‚GüHð¡ð׆õohöú¯ˆo¥·‹QrÚ~í€Å€À¯ŽAòÈ ä;tðä¿ô=øgó½ÿäzþ‰~#xNø‹àýCš‹~Ô¡ ~ý½ÄgtS!‚ŽàŒŒŽ†¹Ÿƒ~8Ô|]á¹´¿¨ƒÅ>˜éú´?ôÞ/»2ôÊL¸u `äÀ¯N­(Ôê°VqÒ_¤¿Gçnç,fãQÂ[=Wê¿Uÿüÿ‡%üyÿ¡ïÃ_ù;ÿÈô¿ð䯎ÿô>xkÿ'?øÅKTW˜uÍ?ü9+ã·øoò¼ÿã¿ð䯎ü}áÏÊóÿŒWô¯E5_ð䟎_ô?xsþù¼ÿã4¿ð䟎ô?øwþù¼ÿã5ý*Q@Ígü9#ãwýÿßüjÿHøÙß⇿þ5_Òü×ÿÃ’>5÷øƒáïû÷wÿÆ©ßðä=þ!xþý]ÿñºþ“¨ æËþñŸþЇÿïÕßÿ¥ÿ‡#|eÿ¢‰ ß›¯þ"¿¤Ê(ùµÿ‡#|cïñAÿ¿7_üE/ü9㈺ýøºÿâ+úI¢€?›ør/Æÿ´/ûñuÿÄÒÿÑ~/wø¡à=×ÿ_Ò5üÝÑ>.ÑGÐÿðëü)ßðäO‹_ôR4?ü¹ÿ þè æ÷þ‰ñc¿Äÿ®ÂÿCø­ßâN‹ÿ€·5ý Q@Îü9⯉Z/þ\Ó¿áÈÿè¥èßø s_Ñíüáÿþ(÷ø™£àÇøÒÿþ'ÿÑLÑÿðãükú;¢€?œoørÄîÿtü¸ÿâ©áÈ?;üNÒ?ð ãÿНèâŠþr?áÈ?»üOÒ?ðãÿЧÃ>$wøŸ¤ÿà ÿü]F´Pó—ÿ@øÿECJÿÀ ÿøºwü8ÿâ'Š:Wþ çÿã•ýQ@Îwü8ÿâŠ:_þ çÿ㔿ðãÿˆ=þ)iø.ŸÿŽWôaE:?ðãï÷ø§¦àºoþ;Kÿ>ñïýM3ÿ³ñÚþ‹h çSþ{ã¾ÿtßüÍÿÇ©áǾ9ÿ¢«¦ÿà²oþ=_ÑUüëÿÃ|oßâ¶ÿ‚É¿øõ/ü8óÆÝþ+éÿø+›ÿ×ôOE;?ðãÏwø¯§ÿà®_þ?Nÿ‡xËþŠÅ‡þ ¥ÿäŠþˆÉN¯‚¾>þØ6>ûO„~Íö¦3úÃÁnzQ#ïrƒý£œzyVQ_WÙPßWÑzœ¸¼e:ç¨ÏÈÁ'!ðoŠ4Oj?ìg×5ë¨-¡´ƒI‘äŒNáD²ÿ¤€‰Ï~OðƒÎ=Sþwâîÿ¬ðS/ÿ$Wð›S¿Õ>7ø?UÕnd»¼º×lY¥bò;½Âd³I>õûÅ^Ïðü2÷Jœ]ÛWoÎý,§1–%JMY'¡üïÿÃŽ¼YßâÕþ %ÿäš_øq׊ûü[²ÿÁDŸü“_Ðýòg®<_ðã¯÷ø¹eÿ‚y?ù&—þsâú+¶ø'“ÿ’«ú¢€?žOøqωè®ÚàšOþJ§ÃŽ|Gßâõ§þ ¤ÿäºþ†h çŸþqâ.ÿíðJÿü—Nÿ‡xƒ¿Åû_ü?ÿ%×ô-E=_ðã}{þ‹·þ ÿ“)ßðã}sþ‹ ·þßÿ“+ú¢€?žßøq¾µÿEŠßÿoÿÉ´¿ðãmc¿Æ(?ðDÿü›_Ðüøõ~ÿàÿÁòm/ü8×Uÿ¢Éþ[ÿ“«ú¢€?Ÿ?øq®§ÿE–ü·ÿ'T7?ðCë‹+io/~5ÛÛÛÀ$’I¡DEff7à$ž•ûëâi¼5á_Äki. t«;‹±mÌÓ˜#i<¸Çw|mQêkùMø‡ûBþÙßðQÏ¿ÃÏé×GBg 4#t:}¼[¾Y5 —*<ìpýÚ)8 —?h?…Ÿ¾ëÃà þ)‰×¶îVîâÏJû3ÄWFêo=³Þ41㤄ñ^ƒû1þŸÿjK¨o¼#¥cøW~ÙµÝH46`‡ ¸qÏË*Éœ×ìÿì«ÿŠømðßì~0ý¡'‡Ç>#M².—a£Û?\8`èöÂÇÔÛ†¯Ø›;;=:Ò >ímm‘cŠ(”$q¢ *ª®p€>Uý’d?~ÈÞ ¸ðÇ…u}K[¼ÔÙ%¿º¼Ä2Ì£¡³Vòaà4„`<ŒÇÖtQ@Q@Q@Q@Q@ÿÑýü¢Š(¢Š(®?âüˆ%ÿ°eïþ‰zì*9¡Šâ·¸A$R©WV ¬0A¨"€?€zý2ý‘à£ZÇì³ðs_øQƒíõï·\ÏyctÒù^\׬l³¦Óæ ØÁªž1´>&Á'Ô¼_¨j_ ¼}o¥è7R¼Y_ÚI,¶ÊÇ"1*8Þ«ÐÇ\×ÿCø™ÿE3IÿÀÿøºüD»¹{˹¯$<îÒ8±Éǵ~—|bÿ‚kdÍ7öf“ÁöÖGk¦Ù^jBMÊðémÆa‡hòÝÚÜr@€ñôoü9âgýÍ'ÿgÿâèÿ‡!üLÿ¢™¤ÿà ÿü]~Güø£ðS⿆>*i–Qj7¼Žímæû’…à©=FA8#¡æ¾–ý¸¿mŸÛľÔÓÃiáË ÛKHeóæ•îZFwÚ (Ú¨÷'¯ckðEˆú‹¨kS|IÒäO·–á•l§Ë,H\—ê@¯Æ©¬ôhf’½˜˜Ø©?g^ÇóÖ€1h¯Ûèàˆÿæ‰&ô Ccì3÷þýKÿCø™ÿE3IÿÀÿøºü<¢¿pÿáÈ?è¦i?ø?ÿGü9âgýÍ'ÿgÿâèñÓþ>áÿ}{¾ÿ›ÿ^Ðÿè¿ ~ÿÁ¦ð÷Žt½â‡-µNš9ÞÆÆÑã{ƒÒHä*>l)$zWïLQGIKµ#Pª`  (¢Š(¢Šù«ö½øñSâ·ì÷âφ¿n,­$þÑ¿|ñoà}ø—F·6·Ò^Ï%±"ÚQ5”±4qI™ž@Iãfú¡ásâ&ðÎÞ/H#×M¹ÔÕ‹Û­ß–¾x‰˜)1‰7m%A#¥nÑ@Q@Q@ºÈ²±šéÎ hÍù ×âÄ[ýcãgÆë­ e…½Á‰=1šýqø¿©6“à]Fõ[iU?ïq_˜?ty/~%ê^)@HKƒó^ {™dTiÊgçü]^R« /côïág´ŸxfÓKÓãU-L„c– gõ®›Ä‹S‹ÌA‰ãåÚGˆnì.wI+4dò õ¯hÓ5KmN–#8®ÎiûCè°•JJ’>~½²žÂᢔm`x®›Ã~%›M˜CpÙ…º÷Åz‰tu;v’5Ä u¯¹¶šÊFŽà´ãšë¥R5W,FxZœÑØúBÖæÞú!$LXW ⯠-Ê5ݨÃõ WáÿϤ̱ÈÙ…ýkÚí/-ïàYc`Á‡8®)ÂT¥¡îÒ«OO•î|ÜÈðÉåÈ6ȿҽÃ)kf—‡å<kcÅ[…7v‹óõ8¯'xÞ Kà í\µb|üãSSM¤$K}BدŽ>µã¾%ðܺtÍsn¹¹©ü1â‰t÷×M¹ ÀÏjõ¶[mJ׿Ã#Šã‹•9k±íÊ,\.·>u²¼šÊaq !‡Q^×áÏCªÀÈȯ=ñ/†¥Óæ7VÀ˜Ï§Jåì¯e±™n-Ûišë©Õ2<Šš˜Zœ’Øú TÒàÔ­ÌR€r:׆ë:=Æ“rÈêLg¡¯^ðö½«†#ÌEiêš\»E"‚}}+’•WMÙžÖ3 D9á¹óí­äÖ3,ð1Èí^ÕáÏA©B±ÈØ”u¯&Öt["f;sÁ¬ÛK¹¬fYàl‚k²qUQáa1SÃÏ–GКž™o©Û´2Ï|W‡k:%Æ‘pÈT˜ÏC^±áßCªD#vÄ€r=kkRÓ Ô h¥PIâ§UÓvg»ŠÂSÅCš;ž ¦êw:lË'’Å–ÒäîCÜÖ•è_މϗfNÙU-ø§ÂíkËEÈê@¯=‚Y­åÞŸ+/á_J#Áwåã ò¿ø\Û±½³\)ê1\’c–ò¯kHè¼-â5½ˆ[Npàw®ÖX£ž3Œ†ó]½ÄÖ’‰al2šöo ø–-F1 §/ž# ã¬Nœ¯1S^Φçâ ÉjæêÐeO'ÄÛÜÏg:Í*ËÔt¯¥&…'ŒÇ È5ä~'ð»Ú»]Z)*y V¸|Bk–G6e—8KÚS:ï øšB%‚c‰@ç5Òߨ[ßÀbÈë_:Áq%¤ÂhNÖšö_ x’-F%‚VÄ‹ÅeZƒ‹æGV[˜*‹ÙÔÜó]AŸI˜°ÅX¶—sÙL·’9Å}ao C0ÆkĵÝ}&á¶‚bcÇ¥iC­fyÙ†]*röØôßxš×S‰b‘±(ê MâAªÀ]ŽAáÐO%¬¢{s±”ó^ËáŸŨF°NÃÍQÞ¦µ~'f ðöUw<~öÊkŒ+‚:ÕÐõéô›ÔÆzŠõ?èj–ìñ/ïkÅ.ašÚco:á—Žk¦”ãV6‘åâpõ0³ºØú#OÔ-õÐr9Îx—ðê¢\8çŠó ^¸Ò'X˜ûŠö½+V¶Õ­Ä±r9Ã(8;£Þ¡Š†&Ÿ,·>}š)­&17ÊÊkÐ|-⃠;Öàð ­¿xb;Ô7VˇœW‘M–òùR ¬½+²2HÛ©âT§<%M6>–VŽx²ea^k⟠³fòÌ{°WÂÞ)hŠÚ^·€Mz ):epÊÕÃïR‘ï©SÅS×sæ˜ä’Öã1åJŸ¥zï†ô%Y:ö¯\ð·‰ÖõµÑï5™âdµåéɼÝky·Fv:k¶\µ"|ú•L$íÐ÷}wC¶Õ­‰ÀóàŠñ ý:ãOœÃr¸ ð}«Ó|5âµ¹ kvv°à]¹¡ÛkÄ…gc\ð¨é»3ÑÄᡊ‡==ϱ¿ŸN¸Y b9æ½·C×­õh$y‹Ôg½x¶¡¥Üi“4 zðj;Ù¬$DÅHãŠè­AT\Èòð˜éáåÉ-Š¿´Â;/ˆ~žh¡Qwl¬áÀˆ¦ üÊø ñRøiñ™]måœ@Ñ’BŒ:Wìþ­[kÖ-gpAwR¬=A¯ÇoÚ[Âø#⯛§D#¶eR>`F+«;§NGÄTT%]ö³M¿‡R°†öePÀƒž£5«_4~Ë>$½ñWÂ{mFõ‹È³Ë'Ñ0}/^%xrÉ£ï2ìGµ¡Š(¬NТŠ(¢Š(ø‹à}'âoÃÿü8×¥ž 7Å:mæ—u%³*Î^ÂÐÈÑ3«¨p®J–Vã Ž+ó—áçü3öløiãï |EмEâËOÂÚž«j—7–-Ïc2OÊ©bŒP²ÁYIÁšýøÕã=SáÇÁ¿|CÐâ†ãQ𾃪j¶ÑÜh^{+Y'dÈÅ ”ã8 ó_ƒ?¿à¯_´‡ÄOŒø­xgÂvúo‰µí3K¹’ÞÒýgH/n£‚Fžý”8W%K+qGýQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÎ>ÿ‹cñ‹Wø}'îô?yºÎ‘Ùc¼\}ºÙ~¼J `žæ¾Ž¯øááOÄž]_Ã#%ð¼éªélKOoËEÇQ*nB½ #=+ÓÊêG™Ñ¨íéèú?“ßÊç.*.ÊqÞ:ÿšû¿Ù(®SÀþ.ÓV›s ªQÏ— êñ±ää––$çÚ|#á-À޳𿆭E¥…’mE–=YÜõgcËÉ5ÒW{ÍçJqú£qŒvîßwæûl–ž¼ÿSS‹öÊíïþHùëöfø[©ü'ømý‰¯Ä‘j÷w—!:ðÞTxaÕLh¬=7rÍ} Eç㱓ÄV•z›ÉÜè¡F4à¡QEÊjQEóÅ(eøcãM?㆘ý›(Mñ$H Ýf͈.ð:µ» Á%8×ÒKRÓlu:ëIÔá[›;Øž¢q•xäYO±Ù€Å*U/%x½îžÿæ»;3ô¹ãe¿ORÌ3Eq\@ë$R¨ee9VVÁt©+çƒ:•÷ƒõmOà_ˆæin|<‚ãGžCó]èò6"ç»[ŸÝ7NØ×Ðô±¸_cQÂ÷[§Ý=Ÿõ¶ÁB¯ß8ƒˆAä2æ¾ç…8–¶M'N*ïK;½OÍ÷¾—ìx9¾Y Î.þóÓüÿ®çäçÁæÙñoÁ é®i¿úS~ûWÌz÷ì§ðÒóÅÚOü1xsQÒï­ïZ;` ¬ÞD«!S ÀMÛp ©S_NV”žñ=ÆŸ*Á3ˆñ^¼ßeÕí1ãŠãW£+×îñtïÔùÿNÔ®4éÖXN<Š÷ \ƒU€@qÔW”ø‹A›M¸y?uÛ¦êtââ@EuU§Ç™V<<ùe±ïš®•o©Û´R¯$u¯ Öt™´{ƒŒ¡<öm[‡V·SœH úÔÚ΋mªÛ˜å^qÁ®:5œkƒ…xsÃsÁ-.ç±™g…°E{?‡üKo¨F±JØ—Ò¼‹UÒ¦ÒîL3 EØÕ+{‰¬åÂpGC]õ)ª‘º>{ Šž¥¥±ô.§¥Ûjp”OC^­èÓé7 ’‡¡¯PðωcÔ"X'8WG©é–ú³C*ƒ‘Á®NTÝ™ôœ,1PçŽçÏÖ7Óió¤ñ>98¯kмEoªD¨Ä 1‚+Ê5ÏͤHA\ÄzÈ´»–ÊešÆ+ª¥5Q]S .Iì{/ˆü7¥–Ä¢¼fâÚk9Ì3¬í^ñZœ"9$­3Äž‡R…¦‰G˜£5…*®•ž¦3Ñö´·8¯ x–K)VÖ鲄ã5ë`ÛêùáÑÅ|ß4ÛLÐÎ ²ší|-âW³[\±1ž•xŠ â‰Ï€Ì¹_²«°¾(ð»Ù³]ZŒÆz⸫k™­$Y¡m¥OJú<}žúß³Æâ¼“ÄþkêÑrÉöªÃ׿¹!æw#öÔŽÓÞ%Q‰a˜(àýk­šîc1È7)¯š­nf³˜Ka–½«Ã>!MRûÅýkN•ó#³-ÌÕHòOsŒñ?…ÞÖG¼µBsJâm®f´—ζ°ê+é9 Iâ1È2­^Aâ µ››«Eù'tk){²93,µÅûJgaáŸE¨Ä"œâQ].¡§[ê¦Ü85ó­µÌÖ“  ;YkÙü1â8µ(„S¶$µz6wGV]™*«ÙÕÜóMwA›G¸b€´mÞ°àž[YX[nx¯£/ì-ïàhe\îkÃõÝm&à°ÄOцÄ]r³Í̲×N^Ö¤xkÄÑjˆ&l8ãšwˆü7£OăœŠñ‹{‰-dÛ¶Ò {‡iG– 7FÛkÕšùfLu¼ËÅÞÁ7ÖKƒÕ€«>ñ@”-ÓsØšôFTljóí*r>’^ÏOMÏ™£i"”vHµê~ñBÈ«izØaК¡âŸ cyh£Ô^r­$RWSÓ½wË–¤Oœƒ©„©n‡ÐZÆ‘k«ÛÀnÇ +ĵM"çKœÇ*z…üP% itß0ã&»-WKµÕ­ŒrÈà×4*J›³ØõªáéâãÏϲ»–Îå&‰¶íë_0þØE¦¡áÛ¢þüº©üsþõv±£Ï¥LÉ"ü‡¡ö¯œÿhm=õ/ t9XØ1ü3^¦§4ÑñÙ—<(ÊœÏYý‹~ Ûú|¹þk_^WÈß±Šìø5 cîß]ÈŠúæ¼lwñ¤}ÞD­…‡ QEÊzáEPEP„þÔ‹¿öeø¸¾¾×ÇþSç¯ã«öbm¿´§Âfôñn‚ò~þÆÿiµßû6üW__ k£ÿ$&¯ã{ökm¿´g¶ôñ^†òzþäh¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùÃÀŸñlþ-k ¥ýÞ‹â3[Ñs¤Äÿ§Z¯Ñ±*¨à)>µô}x·Ç? êš×„áñ7…×>$ð„ëªéÄdh9–ŽJÍå+Üâ½ÁÞ*Òüoám/ź3n³Õ`IÓœ•Ü>doö‘²­î zØÿÞÓ†%nô—ªÙü×â™É‡÷$é|×§üøXéh¢ŠòN°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(øÒÿ‚‡Cä~Ú?S×SFÿ¾í¢oë_ÒÏü®o?ö+øVþštËÿ|]L¿Ò¿ßø(¯‚|]¨~ÚŸî4­úò nìÙ^ idFÝcnNTƒÍCðN[-GNý‹~XêÖ²Ù]Ám| èÑȘÔ.q¹X20FGJûnŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š+Äþ'üEÕ´ýB×áÇèÒûƺÊŒ70éÖÝîç®OÞn0zœ.u§Éø uoÉÕª¡fPø‘ã½{T×Wá'Âéø–î0÷÷ØÝhýe~Æfê£ëüGôxAøuáÈ<7áøØD„É4ÒÓ\Îü¼Ó?Vw<“Û€8U†ÿ´Ÿ‡i¶r=íýã›Bþnn/nŸ—–Fäõû«œ(õ9'Ы«Š‚°¡ð-ßY>ï˲éêÙNþÒ¦ÿ——ù…Q^aÔQEQEQEQExÆÏ j÷švŸñ Á±ïñOƒdkËTn­ÈÅÍ£c’%8w2kÒüâ½#ÇÓ|Y¡IæØêp¬±žëžÑ‘Vˆ5Ò×ÍZüYï‹ø>OÝøOÇ’Éy¥ž‰iª›‹aÙVaóÆ8²ª:ׯGý¢ƒ¥ö¡v¼Öí|¾%ÿosýÝN~GëÑþŸqô­Q^AØQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEyƯßø?Á2Gáñ¿ÄZìÑézTcïË£µ[ØF2äôùyë]WÃßX|>ðf“àý8îM„#IÞYOÍ,‡Ýܳ­yƒÿ;ãn¡â—ýæƒðôI¦XwIuI€û\£×ÊLEõ9ôuzØßÜÒ†oñKÕ­É~-£’‡¿7S¦Ëõûß䂊(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÓýü¢Š(¢Š(¯ÀÛ“þ ÃûFþÐ_´ÇŠ~+|?] èZ¼zzAö»Ó Ù¶²†Ü‚6Ç·ò9¯ßz(ùRÿ‡=~ØÜðÿþ ›ÿŒÑÿzý°?¹áÿü7ÿ¯ê¶ŠþT¿áÏ_¶÷%ý‘~ø÷à¯ì…iðSÆ‚ÔxŽ5xÛìÓ`Ý{4Ïï6¯gãŠü3ºÿ‚?~×Ó]M2&µÝ˜ÄɺŸùã_Õ5ñGíáðÇŸ´GìÙyð³áàµ:ä÷–¯Úæ0òÙ÷?ιÇN9¯ÂŸøs×íýÏÿàÉ¿øÍU´Pò¥ÿzý°?¹áÿü7ÿ£þõû`sÃÿø2oþ3_Õmü©Þ¿lîxÿMÿÆkìØCþ ×ûD~Îÿ´F•ñ7âéE³´»…þÉzf—|ɵp†5ÈÏ^k÷šŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šñÿ÷°Ø|:Ông? ?:üïýšµÙ4ÿê7ë¹$¸Î}¸¯¼¿i% ð§T_ö£þuùçû7Ç+øŽê@¹U“ŸÒ¾ƒ/Štd~sÄÕel9Ö]]·Õc8qÔUcG·Õ-Ú7_›±¯ ±Ô'Ó®Dð¶<Šöí ^ƒVH`Šó*Òp|Èú|:5£ìêîx¶§¥Üé3´S.c'ƒŠ“GÕ®t»•x›)ÜW·k:<­»FãæÇ¼7UÒ®4›ƒŠvv>ÕÓJª©3ÊÆà營´†Çºi:­¾«n$B7c‘X~%ðÜZŒFx/Jò½W¸Ò®VDcåžµîN¯mªÛ‰"?69Ë:r¦î_ ЧЇ$÷>|¹µ–ÚS•eëZú&·q¥N¿11“^Ÿâ_G¨Dgq íë^5qm%´¦Wk{×]9©­O…ž§7Cè]?P·Ô­Ã¡#‘\wŠ<,· níWç‘\&ƒ®M¥Îr€ò {n¨Á©[‰# är+žptß2Øö°õéâ£É=ÏœÞ9#Å"•q]o†üK6›2Á3fps]oŠ<.·ÝÚr@¯&tx™£•pÀô5ØœjÇÌðëR©„«~‡Ñ/¦±iÙ•ÇãZÿ‡®4ÉÚDº5kÃ~#—L•`³?•z܉g¬YàáÃÕÆ¹©=v=‹ÒÅÂësÀôÝBçM™g…ŽÑÔW¸hZä­¸!€qÔW“øƒA›L¸c“5¦jéÓ blGµZJkš'™…ÄÔÃT´ö=ÓYÑmõkv× Ø×‡jšeÆ•9†u%sÁ¯nÑ5È5hAyƒ¨©ußT•׿ìkž•yAØö1Ø(b!Ï Ï‚ym$Yâbs^ÉáŸÇ‚á± åž—>—rÐÌ8'ƒTàš[Y–XŽÖ^s]²¢ªFýO Œž|²>ˆ¿ÓàÔ`hfPAèkÄuý m&cò“+Ñ|9â¨o‘m® ÕßXÁ¨Û´SÀŽ pÓœ©ËSè+áébéÞ;Ÿ:Ú]Mep.-Ø‚¼â½³Ã¾!‡TˆDä äòísAŸI¸ÌdðkÒæ{9Ökw*ð®Ê‘Eu¹àañU0Õ9g±ë~(ðÔwÈ×p.$ñ ¾§Æí‰Tw­»û {øZ)”+ç‹Ù¬nѬ§‘^ßáïÁªÀÈ(äVŠ.C³/ÌXû*»žS¯h3é3³ª“ ®~))‘’¥yô~¡a ý»C*ä^!¯èWL¤¨&3Þº(âSV‘ææl©>xuŠxð@e#ë^W⟠4l×¶‹×¨¡á_,ÀYݱ¯D–8çˆÆÜ« áÖœ¼¤©Nªw[Ÿ3#Õe’ /i×zlòBTJ‘^Bл!`˸+’2ÏPkó ÀßðG¿Ù÷À^4ÐøßâW„ü¨x?Ã6özþ­a§Í$Q^ù‰ÕÂDì…®ˆÜ2ÏPhú0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯œ~ŵø¡¯|'›÷zF·ækšxU7úeªö[ü꣢’kèêñŽÞÔïü3kãO &ïø*qªYÖTŒ¤[œs¶h²H½L®iÉЛÒzz?²þý“g.*-%R;Çòê¿®¶=¾Šçü+â]3Æ>Ó|S£?™eª@“Ä{€ã;[Ñ”ðñWA^làã'+4tÆI« ¢Š*FQEQEQEQEQE`j+ð¾“;ÚêºÅœÑãrMql2225¥§êZv¯hš†“uí¬¹Ù,²FÛIS†RAÁz×ò7ÿP‡Ëý¹~!?üõ‹GoËJµ_é_¼?ðJ¹¼ÏØwÀIÿ<§ÖóÔî[úÐè…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@W˜|Oø“mðÿM¶‚ÊÙµoëmô­6#ûË™ÏsýØ“¬ŽxQß$VØ|<êÍS¦®Ù*(Gš[>(üJŸÂBÏÂþµ¿Œõì¦cŸ•Gñ\Ü÷!’IÆì`w+wá‡ÃX<§Ý]êGWñ.²âãUÔ¤¼¹›û«ýØ“îÆƒ€;dš£ð»á­Ï… ç‹<]tº·5ì>¡y’5ê¶ÖàýÈcèûÄdö×뿈…8}^ƒºûOùŸÿ"ºwÝôKž•7){J›ô]¿àÿÃz”QEyGXQEQEQEQEQEWžüPð¿Äo]øu¥û-à+qct¼=­ä't3)­ÁÇ%IëШ­hV•9ªvkR*AJ.2ÙžYð‡Ç—;ð “Y‹ìž!Ò%{ ZÛ¡Šò•ÈÝ~qÆ2pkÔëæÏˆJß ¾"Yü`´4 sÉÓ|FƒîÇÎÛ[ãÿ\ØùnºFy¯¤••Ô2At"»sMV¤½É꼟UòéäÓ1ÃMë n¿ÏçùÜZ(¢¼Ó¤(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+Ë~1øÞçÀžº¿ÒSÎÖõOÒá´·×Gd@×iË‘Ü)¯R¯œlÿâè|sŸQ?¼ðÿÃUkh{¤ÚÍÊþù½ÙãÂàò®r+ÑË(ÅÍÔ¨½Ø+¿>Ëæì½59±Sj<±ÝéÿäRøeà‹o‡~Ò¼'ùÒÚGºânIšæC¾iI<ÎIä ÕÞQEq×­*“u&îÛ»7„b¢¶AEVEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(æïÚ¢ÿû7àþ«uèñͱ_þÇ‘E©ëW^rýù3ü«ëÚøgà–¬?é¤ú|•ûþ×”±ÁJ÷pŽÔϳ”žcNçÜ>"ðôúuÃM Âþ}:áeˆ¨¯¡®ìὄÅ*†W‹x‹Ã³is4±ŒÄMsÑ®¥¤G0ÀN“ö´ÏRеë}R‡˜"­ë=¾©Fê7cƒ^ §ê鳋ˆŽy½ÃA× Õ`^q ê+žµÍKŽ…x{:›ž-©éw:Lí «˜ÉàÓ´fãJ˜¯>—8‘ Ÿ»ë^á¤ê¶úµ¶ô`IŠåœ9^;Ö L9*n|÷"É…$]¬8æºÄ×T¢;¢oZëüUáuºŒÜÚ®uÅy<ѼryR®k® UZž%ZÂÔ¼v>… g®Yg† +Æ|A¡Í¥\± Llj]Ä7Lë„´-×Ú½mã±×ìz† :×,[§/#Õ”¡‹†ŸáÚv¡>›:Ë£­{~‡®AªÀ¤>9ãúæ‰q¥NÈW÷ÃYÚn¥>™:ËQ[V¤¦¹¢p`ñSÃÏ–{ë­è¶úµ»#ŸðýOJºÓ&h¤.x5íš»­q ê)úæ§nT¨.Á®j5œ™ìãppÄCž™à1JÐ:Ë ÚýzÏ…üP·(-.Û:^_a=…Ã[Îâ«$’C –3µ‡LWuZJ¤n|Þ<5NV}}a£nb˜‘Ö¼O]Ðn4«‚Pf2z×uá¥Ò-¥ÙăŽk±¿°·Ô­ÌR¨`G¸iÍÓzŸKˆ¡OOš;Ÿ:AÖ‘Ìø_Äïk"ÚÝ7ÈzÚ½ZHíµ;R§Ž+ç "’7*ãî|1âg´uµ¹bTôÍèßÞ‰ž_˜Ùû:»~!Ðn4Ë—’5ÝŠê6»·\8çW‘²Ë…[äeõ®¨¸ÕV{ž,¡S Vëcé) ër§ ®+ƼKáÉtÙš{uÝþ•­áù,¶w•<5é×ö÷öÅ$‘Çé\‘n”¬Ïrp†.úŸ5£:8’3±Ö½cÂþ*ªÚÝ·Ì2k•ñ‡f°”Í f#\œr4D<)Ù(ÆhðhÕ©†©®ÇÒòEÔEVä^'ðËÙÈn­W(zûVï…üP³ªÙÝ6p ®þxbº„Æøeq\*ô¤}ã U;­Ïšá–KyCÆvºóšóÿÚ7ÄKð¿ì“¬,¤þF½·Äž–ÆFž˜IÏò×íqà€G\ƒÎ½L3Sš>32ö”iÊ öØÈðv: ë¯æ+ëzùö1;¾ ÂÞ·×_ÌW×5åcWïd}®Gþí@¢Š+”õ‚Š( Š( "ý WÀo‰+ëá­`䔵ü\|m¿þ7§ˆôƒÿ“‘Wöñáwüøˆ¾¾ÕÇþIË_Å_ÀæÛñ¯áûzx‡J?ù7uTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE’ÅWTÏL*JüÅÿ‚µxOáþ­û#k^+ñ]Ÿ]ðýÍšèSïdxno.àŽ` ¬}Áàd`€@麵$2ÚãV+ðöÿ‚t|øßû3éþ0i7·zLjo/žÖX/f¶òìíå6È! O™– ŽØ¯=±»ñ×üÓöüð§Áx·RÖþx¶çJFÒï¦3*Ùêó›Sº0 àI#Df+pÌô€H'€*¶ZÏtÿ¾‡øÕ]kGÓ>þÞ¾?ødÚl²ü4ðÕ׈.c¶ŠâXÿÐí®­¢ ռ̖61b’y ê¯í–ŸóÝ?ï¡þ5*I£tLtÈ9¯ÈŸŽŸðJÙžßàÿŒ5…>Ô­|_a¥ÝÝi^UõÍÓMyoILŒáüæQÏÍÇ8¯iÿ‚_üñ'Á_ÙZÃMñ¦‘s¡xƒÄ¥þ©yeyAs ,¶± #pwEnŽìÀ÷ Ñ ʾ×t=.x­µ=FÞÒiÎ#I¦HÙÉ졈'ð¯ÇßÛ÷öÕø•iñ+Mý¿e¹xï[– ]GQ·ÇŸk%àU­³ôŽMŒ$šoùd„m*Á™=á÷ü_à~i~9Üj øÿö^ý¹ÏƶÖÒÈ»q$âyÔ6ÔfS@½Ÿl´ÿžéÿ}ñ£í–ŸóÝ?ï¡þ5ü©þÓ_²ßÁýöñð7ìÇðsO¸²Ò/›D´Õ–K©ndó¯gin%ß!,»m6ÀÀ$kÞà£ÿðOï„_þxKÆ´{û}CS×cÑ®- Ì׫0º‚icqæ–e*Ðíà€wr:Pôt¬¬)È<‚)k‰økàÛ_‡_¼-ðþÇßÃZ]–› ÙÀƒøíÍvÔQEQEü‘ÁX!òÿm¯¿üõ²Òò±…¥~ÜÿÁ'%ó?b 'üò¿Õ—󼑿­~6ÁXô bóöÍ×g°°žå&Òô³º(Á"Ü/UÒ¿bÿà“V·ö?±Îe©[Kk4Z®¨6JŒ†›p8`hô²Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š+Žñ玴‡~¸ñ/ˆe+ D$Q Ý5ÄÏ÷!‰:³¹àÄàF”©JrP‚»dÎj)Ê[>#üDÒ>è#T¿G¼½ºqoacÍqytü$Q¨Éäõ8àsè#ðÇáÞ¯e¨ÜüJøŽé{ãM]°_š6ÛªÚ[õÀÆÃï6y<–¡ðãÀºþ±¯‹¿¢ÄW(SNÓóº-ÑÿåšúÎãýkõþ‘^û^ž"¬hAФîßÅ/ýµyw}_–ü´àêITžÝêüÿ/P¢Š+È;Š( Š( Š( Š( Š( Š( Š(  ­sEÓø)žúrrÓÞNwÏ!=N\ þy´¿ñt>:$÷žøh¢Gî“kW+òCöx¹õW<×ÑÕëbÿsB4:ËÞ—þÚ¾çû{Èä¥ïÔu:-ëøéò (¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(å¿Ú÷þHž­ÿ] ÿÐÅ|+û*êØë,ÐâÒ¾ëý¯ˆÿ…)«úiþ‡_ ~ËvS\êKÎå_I–Å:.çæ|I&±Ñå?T|7âˆï¿Ñ®NÙu×vpÞÂa˜¾qYf¶Ÿz|²!¯^ð·‰–ú5¶¸lHzò«ÑióDúœ»2S³ªpž ðìÚTí$c175‡§ßϧÎ'…ˆò+è{»Ho¡1J2WŠx‡Ã³iS4‘ŒÄÜÖÔk©®YyŽ[*OÛR=SA×`Õ`8qÖ¬ë=¾«G ±Á¯Óõ ´ë…šÇq^Ý¡kÖú­ºò˜äV©:oš'¡ÇF¼=MÏÕ4«.vŠuÀφ›¥j·:eÈš3òwîZÎoªÛ˜Ü ýxv«¥ÜiWTà÷®ŠuEfyxÜèOÚSØ÷ Z·Õ ‡æÇ"²}’€˜¤È`pG½t^ñúLáîŒö®ûÄÞKèüûEÚã­yÖòÛÌÐÎ60ï]œÑ«3Æ­†ž|Ñ=öH¬|Ac؆â¼o[Ðî4©Ø:þï<±áÿO¥Ì©#–‹5ëN–!±=pýk•IÒ•º•á‹§¦’é¯rÐõË}V*x"£ñƒ«nØqÍsS¨âìÏO‚†"ðÜð˜åx¥!Úã¦+Ô|1âÁ9[;³†è yµõ…̈́̓nªÆí×ïkЩJ3އÏáñu0ó´£ï¬mµv†`aÁ¯×ü;>™1‘GîLW_áo‰‚Ú_6p¤×}wkü)eaÖ¼øÎT¥f}-JTñtù£¹óŒÉo –3†ƒ^Áá¿èÀ-®ïÇ5Âø‹Ã’é³´.a5ÌAq-³‰b8*k²PUV‡…B½L4ù%±ê(ðÀ™ZîÐc¿嬒Bû†S^ÅáÏE¶¸?8㚡⠬ ÞZzœVT«¸{’;1x(Õ´¤bxgÄòYÈ-n›1ôê3Ci«Ú`á•…|ìñÉ”•…vþñ3Ù¸¶¹å(­‡ûQ1·îªlex‡B›I¹-݆±¬¯®,§Yà;H<û×¾ÝÚZk6xl0#ŠñMoC¸Ò§bGîÉàÓ¡]?vFxü ¦ý­-_Ð5Ë}VÝFïÞµ½<):ä\ƒ_9麌úlâhOCÍ{†…®AªB #Í‘\ØŠ/š'¯—f1«J›žm⟽ŒÍul>CÔWmq=¬Ââ#µ¯¤îíbºˆÅ(ʰ¯ñ'†æÓeiíÆc<ñ]41 K–G™˜å²¦ý¥3¼ð߉bÔbX%8z×Qyeô&)”0"¾rµº–Îa<uìþñ$Z„K Ä Ö©YóDíËóÎ>Ωç¾!ðìÚ\æX1µrñÉ$R‰#8a_HÝÙÃy ŽQ¼WÄ>›K¸3 -­pøŸ³#ÏÌ2ÇMûXlv¾ñ2ÞF-/p3G‰|.—jolÆ­y,RK ‰#l¢½gÂþ'K¸ÖÎñ¿x8¥Z‹‹æ‰¶ðöuO$š9!•¢”aÁ­3T¸ÒîUá&˜û9î|úU¡rŽ1"Ÿ¥zW…)ð²Ê å¢ü㜠ò×Á'?+Ž¢ºýÚ±ó<©*˜:šl}qÐl`Wx“Ãré³¢‰ºbº ø¨|¶W§5è×6ö÷ДVã§9R•žÇ»R•<]>e¹ódrKã;YMz·…üR&Å¥áç±®[Äž›M˜Ïn¹CÍrqHñ°‘ ×kŒf–"¦­žÇÒSÁä&7•…|%û]ékáÿ­ÒsHóÍ}eá_}¡­ÉÃkæïÛ|çádd ææ?äÕ– 8ÖHìÏgN®S[›ß±qÝð^Ý¿½{r2µõå|ƒûñðR×?óùqüÖ¾¾®\oñdzùû´QEÊzÁEPEP;âÿZøÇÂzׄoex-õË›dÑ.bh™— Œ€ÙÍ~OøKþÙðCÂ>)ѼWiãOq¢ÞÛÞÆö0ŽöÒ,Š­ˆ3‚WWêÇŽõû x#Ä>(³‰gŸGÓ®ï#ó±ÞÞ‘U±Î \WóùàŸø,ÏÆ¯xÓ@ðÍÏ|?¾¡ki#£]—D¸•cf\ÍŒ€Ù è¶Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¿?à¶ÿ?³¾ü<øa˜}wU¹Õ%PyòôèDJÙšë#Ô¯µ~ß×ó©ûucã÷üÛá¯Á$ÿH°ÐÛEÓï"ûÀ,òBñ±ëöW\öE}=ðþ ;ûüý¼à 3ÄW¾!Õ|7¢YÛOe§éwhòÞ¬@Üm{¨à‹æ˜±ÎüsÁ5åÿ?gOŽ¿µwíí¡ñûÃSø'ÂÚMÕ½ö‰¥ÞnŽêCdö|k"Ç*Í$ލ$¸»\ì÷¿ø+Àox‹ö[Ô~#hú¥®½à›Ë;´¹·#­'•mgˆ²J~õd ô(ïŸuÿ‚müZÕ~0þȾÖ üC? !kºOᇾÒc·‹ÃŒ¨íü³*F×’C)\_”)ÂãåÉýQñÇŠì<à¯xãUÿ/i÷zŒüã÷V‘4ÏÏo•M|ˆ¾'ý™ÿeoñü>дûÕøƒû@j©ÝÇkºëtÆY1qqæH—’b»,‡oú7í© üÿ²OÅäÓFfÔØÿ×$š_ü†€??à’Þ Ö>4þÓ?i¿ŸµÝèË<ÂVïªë¯!w\ç„f\vµýWãü29gÿÜE·¿‰Ùeþ÷’¶VÆ,ûni1ø×è§íQñóAý›>ø“↱A&]ÕnuIT|½:†öfºÈõ+í_·Pô1ÛÛ Ž(”"*ŒU@+ù×ý»âÿÁLþ|÷ö#hºuÜCæn&:…ãã×ì².Gû"€=×öôñìÏû%øsÀÀ¯h®¢[icW×ôé,t˜¯Þ ¯rѼÀÓ³:ÆÛ7äFkìø'/‹<}mð‹Ãþ ø­v×ZŸˆ4Õñ6“3ª ’ÂúFcªUòÉW G'¦+íï‹ <ñ¯áö¯ðÇâ%£_x{[X–ê•áfL“¦$Œ«.$Oµ|[áÏ‹ÿ ~-x:ç_ý›mnæOÙæòex‚[^XÁ†âÞÖ]îÒÄÖцÜ@$lÀù«ÓË9g'BoIè¼¥ö_ߥú&Î\UÒUO˯ùú¤}YûFüµøýð?Æ.®VÈø’Èà ì»Ö˜f·‘”rU&D$p8濞ßÙ‡ö©ø½ÿàñö£û>þоºo Ovnd…@k‹7“o,$ÈŽâÞP ²Á#*ÊûÕÿ Ï~Òþ!|F¼øGá/¥÷‹ôí. fæÀA:˜¬®I Í€œ\E”Y ÜŽ\»HOMÁHó¥ÓÜéM5t}WàoxOâW„t¯øR‹XÐu¨âÒêJIqÐà«)YXVX®®¿ÿàŠÚ§ˆ/?g?iÚƒ<šVŸâYVȹ$!’Öš4ÏCønv=IÏéïÆÏGð»à÷¾#Há†ôkûôÏñKo¼j=ÙÀQîjF~~Éÿñ?ðV/üToô› Ï­^Á!åZ`4‹3éŸ.Ee¶ät¯è²¿ÿàˆÞtð×Äÿ‹Šd“Q½³Ò!•¹ ÚÆ×7=÷ˆ‰ú ýÜ Š( Š( Š(  Z¦™bÛ/o!·lgH¨qø‘RYߨê1}£O¸Žæ Jï‰Ã®GQ•$f¿–ø,t>_íunÿó×Ãzs~RÜ/ô¯Ô?ø#lÞgì‘xŸóËÄÚŠþpZ·õ Õú(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¬­o[Ò|7¤]ëÚíÒYXXÆeši{ûžÀIàsU¹4’ÔM¤®Ê>,ñfƒàÞxŸÄ·Ki§Ø¦ùõ' UY˜ðª9'ŠñøS^ø…â;‹ÿ-ZÔÀ Ð4i9|-Òâeèn¤?ì ¸ CÂz&­ñ«Ä6¼ojöžÓŸÍðö‘0Áÿ ýÒt.Øð£Ÿvúj½z²XHºP¼zIöþêóþgò]oÇê¾wðôóó§ßèQEã¡EPEPEPEPEPEPEPEPEPÍ~ ?ð¨~&Ý|0Ÿ÷~ñSM¨è xK{Ÿ½wd=O™è#’kéJó?‹>oˆ>—M±›ìzÍ„‰{¥ÝÞúÜî‰Áì ù[ý’{âŸð§Ç«ñÁðj÷0ýU´w³Ôí m}nv͆~eÿdŽõëãIbWÅ´½zKæ·óO¹ÇC÷rtºn¿Uòü½I¢Š+È;Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼ÿ⇎!øwàmSÅ,ž}Ä#´€rgº”ì†0's‘œsŒžÕèóޝÿ?ã…—‡×÷š×÷}Ò]^u?fŒö>Dy“#£ô2ÚNjŸ Uߢéóv_3ŸQÆ6ŽïEýynwÿ<7€| g¥jOçëŒ÷Úœä妾º;æbG\´EéôQ\¸Šò«RU'»w5§MF*+dQEbXQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(åÏÚôÅ”Õÿžèuò7ìX¢MVhÝr7é_\þ×§õb9ýäú|¯ûZF.f½wÛ‡ÈW·†¿Õœ‘ùöooí8\û»ÅhÙ¯íWޤ à škYDÑü®¦¾’+ÄX8ujò¿xlÀÆòÕp§°®|>#ìÈö3»–Õh?†¼K¡Ã9 *ñ]Uåœ7ИePÁ‡ZùÂÚæ[Y±6ÖSÒ½›Ã^%‹QˆA1Û*ñõ¬ñ9_4Nœ·1S^Ê¦çø‡Ã³is4‘)1kÆþâÆQ4 @«è›Û8o¢h¥‚+żEáÙ´¹šhW1“[ÐÄs+3‡1ÀJœ½¤IðïˆaÕ í‰ZÒÕôx5KfÔnǼ Îö{)ÄöìG¨¯kðïˆáÔ Xä8—Ò¹ªÑp|Ñ;ð8ØÕ³¨y«¥\iw9T…íïLÒõI´»…š&;sȯtÖt{}VÙ‘Ôo#ƒ^ªéséW9—ä= uRªª+3ÊÇ`gF|ôö=¿FÖmõ[pÈÃ~9âo ǨÄgq*úw¯&Ó5;6ágŽÌò=«Üt}fÛU· „nÇ"¹jStåtz˜\T1ýMϹ·’ÖCªA^µ{JÕ®tɃÂß'q^±âO C©DÓÀ6ÊnõãWVÒ[Ja¸Y?Zì§V3Zž&+ <<î{Ñu»}ZØ2°Þ"°üOáxµšâÜm‘yã½y^›ª]i· 4,vç‘^á£kVú¥¸e ¾9ÉR›¦ïÙÂâáˆ%MÏ– `”Á0Ú¶´MvçLHbPpA÷¯Hñ?†Rú#qn»dëÇg†Ki 2‚¥MvÓš©=φ©‡Ÿ4Ox`ñ †ÒC’?*ñýoF—J¹1àùlx4í[ŸJ¸W º3ÔW«¼v%±8Ár:÷ÏéK]BñÅCûÇiº…Æ›r¯gš÷ \·ÕmÔ†ýààŠñ[E¸Ñç(àììj¾¨ÜéÓ¬Ñ7ÊJº´T×4N\&6xyòÏcÚuÿêÂJŒIí^#c>9Že*3Ö½×C× Õ V ;Ї_Ð`ÔíØªþðt5ÏJ«ƒ³=ln áí)ž ’[V÷…üRÈVÒù³ž5éWöú¾ÉeaÅpsJ“±ôµ)ÓÅÂësçyåµKE{‡›r³BÇÜz×½^ZZëG€ûǼ;YÒn4«†ŠU>^rªK’[™cðR¥5VžÇ³èZ徫ù‡˜:ŠÙ¹µŠê x¯´íFãO¸[ˆ[žµî:½« þó¸®zôÑìeùŒjÇ’{ž_âO K¦Ên!Rb&¹[{™­eÂpWµ}%um Ô- ˼_Äž›NÏn¹?¥m‡®¥î³ÌÌòçMûJg{á¿ŨB±NÀH8Åu–p_Bb™A +ç k™­%ÀÄ`äŠöo x’-B!í‰+†½´_˜r@¯?Ò5›"á][1“ȯrÒõKmVÜ:r9Å:r¦îsˆ§‰‡$÷>z+$2c•t¯K𷊸KKÑÐëV|Sáa(7–K†+ÊÊË™Iµu{µcg¹äµS SM¤n ‚úŽ+ x׈¼35„¯4 LYÎk¾*éez}5éCÜ;XVɺR³ØöêR§‹§Ì·>mŠY-eY¢$0í^)û\x€j m­dÿ[çÆOẾžñ?†¤±ÜÚ.än¢¾4ýªüPP)àù«ýkÓÃZs‹GÇfœôhΛ=çö,ãà­°ÿ§ËŸæµõí|‡ûçþ·¯Û.š×וåc‹$}¶D¿Ù)úQ\§¬QEQEyçÅÕßðŸÆ«ë¢jCÿ%¤¯âáClø¥àæôÖtóÿ“)_ÜÅUßð¿Æ ë£êÿ%Þ¿‡Ÿ† ³âW„ÛÓW°?ù0”ýáÑEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEâ&ñøSo %¬šàµ˜Ø-ó:Z5ÞÃä‰Ú%gX‹ãyE,8ñ_‡>ý?nß~Ô#ö°ºñ€5_µýÕô\]êŸdoµÃ%»Dª¶Õ#ŠM±üä®Õ$œsû¿EyÅÿ‡qü\ø?âφ:©ÄúEÖž\çËŠiáeIâ9qÁ<+ó›ö ý’?kÿÙZºðω5ÿjߵ˃w¨[[\jßAp°”YmÚAé Æ²‰ÈÁýi¢€?%ÿoÿØÿö¯ý¯H¢Vuˆ¾7”RÁs€Oø_¢þÀ?·Þ…ûHÜþÔöÞ+øqã[›»Ë¿ô‰õ)m#kÈ^Ü¢DlŽ'ÙÏÉ<ç÷¦Šü”ø›ðsþ ¹ñCÁú‚5ˆ_t]?V…íîŸKmBÞâHdxįa# aÁ)µ±Æy¯¥ÿ`ÿÙXý”þÿ·ñuÍŽ¡¯ê•Þ£¨M§´’Ú»L(Õhâv“9A†ÜG'íJ(ùüºý‹ÿj†¿¶—‰¼}û­§ëúMž¹¤Ì'²Ô!Žxd92ŸÄô±´Ô¡ D~ÖüK½YýýŽj´7ÓoOø}Çš| ø%àÙçáŽð«á앤¡&YHiîgî–y˜ ’7'0ª€Î·OÁÚOö„ørÿ¾ ê^Ñü=­ªlÜjóÞE{ †U–8`û=¼è#b È[æ8Ú8'?tÑ^iÒ~rÿÁ>¿fÏÚöUð¶«ðÇâeç…µ \O>¥k>‘=캀¾œCI<ûx"0ùq’ˆÀåz~QEQEQEQE.?ðYè|¿Ú¿Eùëá;ü¯/WúWèßüvo3öR×Óþyxºý;þµùûÿ§³šOÚ‹Â’Ã>ÿY”ÊêþŸZû×þ³Í·ìÍâË[˜ž.ºR¤†ÓìFFÝ Ø (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¦I$pÆóLá#@Y™Ž’I= /¯¬ôË9õFt¶µ¶F’Yd`¨ˆƒ,ÌÇ€äšù›J²½ý¡õØ6|/ø á |qñc_ƒ@Òco-]Ï,òžDpB¤•Ï]¨¤’p Õ(¯Ê ¿ø+€gÒnü]á_„^:Ö¼#`[ÏÖ“N,‘T‘¼È%tUãøÝO¨Šú«öUý³>~×–åßÃk=VÂo ý”_A©Û¤E ß™åìx¤•>SôlŽ2hëZ+ã¯ÚŸöÐðWìÚMÇÄx“UÒµp=KJµ·–Ê;ƒ¼‹i%žâLV6p¸9Qx8Ýý—¿jÿ þÕÚ&£âøCÄZ‡bËWú͵¼÷’ÊélÐÜMæŠâC€3œ€õE|Õâïø´m¾#Cû¿ øÁâÓõÀ8KkÁòÚÞÀ7ú¹pNIó'Æßø*?Á¯€?µ…Þ?ðO‹¡Õt‰ ù‘ÙÙ.b$ìž’ñ⠣⾴øoãöŸøCs¬j¾×<-£kÂ[e²ñ´vw’ÀUJܤi$¸F-˜œI]Àci=Ù~)RŸ¿¬^yšÝy£ E'(û»­W¯õ¡ï”WÆ1þÒ>ýž¾ëÖŸ´´4û¿ÉŸšT¼ú¤lžÚ1óK$ˆ»X í*YÊÄ`þÊÿµ7ÅïÚkÅ7Þ!? .|ð£ìNún±©\¦_Üù‰å‡ MrY<Å žEeŒÂº57¯ŸuѯU©Tj©ÅIvQ_“¿´‡í½ñÿÀ?µæ‘û-üð߇5õ»{#mq¬ý­sqt®ä;Á*…@ä'ßÓíŸía«/í£øSK³H¢:{xrk¹dyK7˜&$€¡víÛß9®cSé*+‡ø‰ñ/À? |/uãO‰:õ§‡t[1ûË›¹)ld"¼îØùQf<Mxì»ûcü7ý­/¼kÃ]?PƒOðlö›»èÒ%¼[±)WŠ5fuÉnÁR@9ëz(¢€ +࿌ŸðQÿÙ§à÷‹'øzo5/xºÖé¬fÒ|=dnî#ºF(bg‘¡„¸pT¢HÎs^5ü¯àîƒâ¸|-ñoáÿŒ>5Ë “jºr¨HÉÿY,"A8QÿLãûPêÍãüMñZø[Š¿fÁ¡øƒYÔÖÖçN}ZiF›=¤øf“Ì·!Îc9LŒ×å߯Û›öÒýž>(xá×Åïø 7Œç„Fú[ê•î$É:ÃvGPíEówí;ñ3ã_ÂÙx¿à·ÃÑñ"{[ÀuM='0Ü®ž#rÍlªÞ]û ’3ò£™ý–ÿlÏ…µN›y…ÌúŠôqÿ=Q;ÛRÆe%ˆ?˽pTà:¡ PÖôQEQEQEQEQEQEQEq?¼keðóÁZ·‹ï—Ì“]æÎØ¢ç.ä/ãšç~ x*÷Á^‚=q¼ÝX–MKU”ý罺;äýÁ„Ë\OŠ?âç|hÒ¼ï4/õ}SºÉ¨HØ o÷e#¡¾Ž¯[ûœF•¿á”+gv~^€šôyà·Ô­Ê0 ¬85ç'*r±ô•)ÒÅÂësçK{‰-eÄpË^»áßC¨À-®ÏÓšâµý ú¨¬iÕ•ùdub°q©mHÏð¿‰å´[]¶c< ô}BÆÏZ³ç ‘Á¯Ÿ 1•e®çÃ^%’Õ– ¦ÌGŠu°ö\ñÜXÇOePæõ}&}*fIËŸ”ö¨tÍB{ ÄÐ61Ôz׸jmž·gž ǼSWÒgÒg+ ;CWB²šå™Ž3è5RŸSÚ´rßV¶NuSÖ¶.­âº…¢˜dùãMÔ'Ó®h[iG­{~‡®Ûê° zãÄaÜ4Oo.ÌcZ>Î{žaâOM¦ÊgsÕË[\Íg(š,†ZúFæÖ+¨Z)Fåa^/â?M¦ÎÓÀ Dkz‹«Hós±Ó—==ŽóÃ~$‹Q…a”þðWOwiõ»C0 ½|ãmq5¬Â[rWiɯeðïŠ ½…#¸m²ôÅeZƒ^ò;rüÎ5²©¹çþ!ðüÚdÅÐf#Ð\ÌSÉlëÿ‚ÔüTÖ¼A¦hò|:ÑbŽúêÅÍÉ*%p¤ŽzŒÐôUEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP%Ž9£xfPñ¸*Êà ƒÁ ×Îÿ¥“À¾%ñÀû÷>VÇRÑÎLšUÓ“°Éû<¤¡>ã ú.¾~øï¦ßh¶ú?Æ.¥à‰Œ×§Þ¸Òæùo"÷Â~ñIáv’9¯W+jnXim=¼¤¾òôg&)rÚªéùuÿ?‘ô GLÔ¬u6×WÓ&[‹;Ø’xd_ºñÈ¡•‡±½^[M;3©;êQE!…Q@Q@Q@ gTœ…§ŠdSÁ8& @§ißüÙÁmâÇÆß‡óÿÃο÷Íܧÿf¯¨ÿàˆónø1ñîx‚&ÿ¾­#û-~×ÑEQEù¿ÿý¥¿h?ÙOºÄ¿†6Þ¿ðÍåÔz]Ü:¬Rß-ìÉ4Èñù7Æaòâ ƒ— Ï+œxßÃ??ðT¯Šß ´Œ> ð_ÃËý+[îm,åûu½ä±£²–KÕKíÊf@ œWÿÁm|o<úwÂÏ„:i3O¨]^jÓB¼±h•-mp;–2ÌÒ¿dþøkFø/ðkÃ>Ô.a±Ó|¢ZZOq+¬q"XÛªI+¹Àå,Ì}É †¿cø(=Çí ãícàwÅ |FÑrÐÄîÖ÷hû.bÈ7Á,Gª3>@bcúñ ~ ?‚õUøW&›Šü¡ýžÚÂLöhaŸ=`e“i\´äGü÷~ÃRÏñ÷þ sãOŽÞ†OøEôësT3•1©·¾Y,íŽÒJ$l<ü¬„×îÇí#ñþgÀˆ_VO*}C¾žÝ³ô¯%–Ügý©JÆ€??g/Ûçþ ûQø·Xðoà ø o4+Swu%ô„0*y‹Péy&Y˜ð1‚9â½GÇŸ·ïísû*xÇDÓ¿kß…Ú$ž×¬:—†f•w,dy¦?>yÕä@ÀùRy%‡F‘…ÿHøýðÃâÄéãÃ뺭¶—Ï—§Beb¾Ì×X>¥}«ÿ‚É|RмgqðÿönðYï‹×Sþйµ´ýôÐ<±›k[r'ÍœÊÌïªHÃ) º¾ñâßi~*ðõÊÞéZͬ¶“§Ý–Þá‘:ç³#>µñOíûy|>ý“â³ðÔvx»Çúº+Ùh–ϰª;lInd æ5f"ª³¹ °ö?¤³7쥠§Že~xJØêE_p2i¶JfXÛ¾] §¯WâüCÁš¿íUûXøãö­ø±ö”¾”^@&Õo™…² lå- „küÊ=@ µáñ×ü³Xð£üKµðw´¸Ö&¸‹Ã7èêrłʌ<ò‚R¸ùLñ¶N ©ùG´þÂÿ·û^h®™«éKá¿ødFurÐË„¨¸·ßó„Þ º6Ld¨,Û?}Wóÿæ½gÿ‚•|V}ÓnãñFå±Õ"xñF @ÐgÄ%ø€þ ÕWá\šl^+ò‡ö{k 3Øy¡†|õ–M¥rÓpyWáÿìåû|Á@j?k>øcá¿ ÍÔÝÝI}¡ žbÄ:^I–f9 ž+öö‘øÿ ³àÄ/ˆ+'•>‰¡ßOnÙÇúW’Ën3þÔ¥ã_–ðDŸ‡ßÙŸ ~ üOžV›šÅ}™î°}JûP¯‹?à¡¿µ'ì½ñEð·í•ðÇG·ÐuÞ`Ôü7,ÁLHʳK–k+E¸nˆ˜ŸzÏ쾕©Økzež³¥N·6WðÇq«÷dŠUŒ3Ù”‚+ùþÿ‚ÏüCÑücâ<&µ|Ma%ÍÝռ̒9/ü¨m r|Év»ë,ã +ösE¼´ýž¿f½2ïÆ’æ‡>·ûs‡Ü\éVJ$ Çï3È_RG­x'íûy|>ý“â³ðÔvx»Çúº+Ùh–ϰª;lInd æ5f"ª³¹ °ð¸|uÿlÖ<(ÿí|àm.5‰®"ðÍÀº:œ±`²£< ”®>Sý…ÿnMö¼ðþ­¦êúRøoÇ>FÁ´2Å!*.-÷üá7‚®“* 6àM¿Úão†îü'â_kZ„ºwÂ_øžê6ßëõ‹Á#Ó,H#p’FTwœn –?ޱv‰­xëþ ñWÞ ÔÛFѵ¶ñ"jAþ³û$ê‘Èc„ô ì#@Ý”“ÏCúKÿVøe|?b„Ò>iæ+Â:µ…õÕ½ºÛaM ±’I’Gcž…›¹¯O ^!íVµ:wÏ×·mûµ©Ê£ä_?/Nÿqçßÿh¯ÛóöžÑÄŸ³W|'ðóáÅ›µ®™>¼n§K|®ØÚ,‡ „¤ew’­‹ß³ïí«û\j_µä²WÇ¿h1jµÃ^^iË<- Ú5ÒNŒe–9#”Ùò!ùÀlèßðKÏÚkágÄo€žø1§ÝÃ¥xÓÁvmmq¥ÈB=ÌHìÂîß?ëUÃf\|ÈùÜ•fûö/ƒÿ áøŸ/ƈ¼?n¾6žÌX>©óyíj1û¿½·(Ûœ gæ¶Û»:’8ŸÚ{Å¿<ðWÄž>ø7ýŒÚ߆mgÕ&\Iä¶–ÊÊ&A$L&!FĄ̂lg#òÓöeý²¿à¢Ÿµ†›®êÿ ü=ðúOM¨Ã©[†’uf ˹7m –Î1•ëž>ßÿ‚”|@ÿ…{ûüB¹ŠM—Zí¼:48ßý£2Ã2ÿ߃)ü+çoø%}ÇÃ±„~6ñ—ˆtÝ/꺎«s5åÔPyqÂÂÉ—aŽ-‹(ﻎ´€óOÚWö¼ÿ‚‘~ÊžÒ¼Wñ7ÂÿæÒ5{£eΡp‰s±¤Xäy)tG+Áiæ¿a¾êÞ'×þxS]ñ´0[x‡RÒ¬nu­‘’ï&t]‚+’3É5òŸÃï|ÿ‚ü,Ôî5ï]ê^ Ðøgà­oâŒnÅ–‹áûIo.å=V8Wq ?‰›îªŽYˆ“@žðR?^&µðï‡ÿdï„ gø‰ñŽe°Ùa­t©ËšG#;oš2Ä`D³6FÐkóÇö"ø{§üÿ‚Ÿêÿt«—º¶Ò´ëí4M' ;%œW!·²ÛÐg¥t²íQû>ë¿´ÄÚÿöœñ­¶‰âÝNfÓ¼;¥Kowqý›§lº´0ºåm…H Ÿß3ÞW/ð£âwüwÿ´ñÿÃU5 ø†êåmîãI"Y|ϼo…•QÆÙÁ¨Éqƒ@Ò5õõž™eq©j-½­¤o,²¹Â¤q‚ÌÌ{$×ókðêÛXÿ‚¥þÛ÷þ$ñœ“©ð(kˆlX² Ó’]¶ÖØ_»5ë2vÎíŠê¬6&?^¿à¢þ<ºøyû|IÕ´ù;½FÊ-*2§œñÚ˃Û÷R9¯™à?-¼5û3jÞ;x×í¾1Öço0˜ÚØ*ÛÄ„ÿ³/žGûÔúµiá¿Øx~? Øé¶Öú$6ÿcKâD¶[`»<¡mùvŒqŠùSö9ý¼;ûøcÄÞÑ5·ñž$ÔþÜ×[‹fŠŒ$6ûD’nòþs¿#;¾èÇ?bÑ@‰?ð[oG§|$ø{ðÝ µÝf}M€ëåé¶æ,b×`û•ö¯ªâ÷Ã_ø'÷ìeðþËÆØm^ËDµ†ÏG…‚Ýj:¬± ®Uªyò3K! =ت·æüz×Sý¢à Þýžô áfÖVúf˜ó…Àµ›P•®ç¸1¡ö[,þÒ¾?Õ¾!Þ[ZêÓxvÁØXéËnQ4¨n*ƒ½\œ×ÎßðC€?áø¾éëEÿÐ.ëö—â¿ü’ßÿØPÿÒg¯Å¿ø!Çü‹Ÿ¿ëïEÿÑwtûÇEPÅ¿aï†? ?h?~Ñö÷·:׉|as{sÞ$^VœÚ„í<ÿfÚ¡mÞ^âwlÈΫå/ø,ý‡ƒåýš¼?©jé×íüCo™!PgÛ$”VûÂ2ª¬ã¡eLó¶¿V¼eã |>ð¶©ãoê1i:&‹ÜÝÝLp‘Dƒ$žäžŠ Ä€$ üð÷„|mÿ^ý¢GÄßÚÜè_¼3ZØ[ÈLr_aƒ¼JAÇq…k‡Sû¨ÂF¾€~Á5,µûØ“áŒ>#VK‡¶½– ã öYoî$¶?F…¯û$Wçwü‡þN£àÖý8Å_½Úva¤iöºN•o¥•”IÄ¡#Š(Ô*"(áUTà üÿ‚°ÿÉÔ|úÃÿ§¨÷ê¿¿à¥>Ô?e¯¾ý·þ ÄtÝJ÷R6šäQü–÷7"=éæ(/ Y£Ÿ×haó’Ç÷–¿:?à«:,Z¯ìEã[ÉÒn´‹¤Ïf:„=öÊhîŸx×Fø‘àoüAðë—Óz“jè8ë^ÃE†+*Õ%V[²éSPŠŠèQEsšQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(å¿Ú÷‚Z·ýt‡ÿC¯ŸÿaŒe_¹“ú ÷ÿÚððKWüÈô:ù«ö#ÕìôÝ>ñ.ßk4€~‚½¼1’Y¤ØûïÄÞŽî6¹·]²p;×˶“däBÀñ¹€¯¤áž¨¼ÈÎå5ø™ÿGñ‹¼l®<%©É`Ò©'gÍqÒĸ«H÷±yJ©.zlý]𿊣€‹[«ˆöz—_ñ¯J–÷Iºˆ£ÜÂÈÃûë_Ì~Òþ9ðݯ‰mü]{wQ¤Š?ˆéï^mû5~ÕlþYñÕükŽ1·ü*jÛXša*ª»©+Ÿ¸ž!С†F¹ÓåI#<¬?Zä­®&´›Ì‰¶²óšüZñ=—í‡û:ÛÝ\\x‡JkJÒ±^ü*ŸZûÛö`ý£4ߎÞiäQ£g„š1“µñœsÍtaê_Ý‘ææ9uŸµú-á¯Ç©Ä ™€‘@ük¡Ô4è5 v†eÎzùêÊö{ÖxþVSÓÚ½³Ãþ ‡T„#¶$ô¬káœ_4NÜc ‘öU5g”ëš úLíò“¬{+Éìf@Äc­}©iÐjíËœŽµáúæ‡s¥\± ˜JÞu8ò³ÍÇåò¥.hlz¯‡¼C¥ŽG`¥¬èöÚ¥»#®[Ö¼Êîk)Öx 5í¾ñ :œ!âAÅsÕ àïÑÀc©Ö‡²¨õ<{UÒ§Ón)Ú;ŸJ‚ÃP¸Óîh…E{¾±£ÛêålpkõM*ãJ¸hå_“9Å{v‰­C«@¬ äWŒjÚ5Æ•pÉ2þï?)ªú~¥q§N³Bp ô§V ¦±9pxÚ˜jžÎ[ׯh0jF+įìfÓçh®¿îZ·©nIÜTzöƒ©` H…*ΛÔõq˜W<7<HT†^= z†|Töò-¥ÛeOšã/ì.l¥0̸¥QÉ$0GzîšU#Ì›¡Z¥ è}%<Ú•¶Ó†Vâþ#ðìúlÍ,C1Õð¿ŠÙÖÎí²5ê3Ám©[pÈã­pBNœ¥©béó-Ï žKiVT8+^¿áïA©@-nˆÜF9®ÄŸK”ÍÝ®f å¶”MÚT×dãŠèñ©Vž¥¥±Þø«Ã-ë1•=EyéʶOv¯cðÿˆ!Õ wxÝÓžõÎø£ÂÎŽníÊÝ@ô¬éV³´ñx(Ï÷Ô¾e x¡¬ä×M”< ö¯EÔôëMnÈ2 x VC‚9Zî&ðºÝ!¹³_ÞuÅy4ö³[Èb¸\0í[Ó¨ª­O"½˜YÞÚAiz¥¶«l2G"¸ßø\H­yf¿0ä\‰«\iWJÊß»'¥{­…õ¾£l$B#‘\“N”®Zª|“Üù×÷H26ºšùoö¯Öû°[JrUÇ^½ëïÿxR+•k»Q‡@¯ÎŸÚ¦)`Òã†eÁ þµéàæ§%#ä3Ü%J¥è};û> Û×åÏóZúò¾Cý‹8ø-nüþ\ÿ5¯¯+ÈÆÿGÛä_îô (¢¹OX(¢Š(¢Šå|v»üâõÓ®ÇþAjþüÛ—á+™¯´WºòÞD€ÛÜù¶.‘†sD ð¤41àýzÿ‚¥ø’ïÿ±?ŽVÉÌrjÒé¶%‡Q·±ü ”ûâ¿à‘´ðçìs¦kðÆþ*Õµ+ù_3y2ý‰A>€[ð;dúšú‡öÁø/wûG~Í>1ø_ Ê‰©êö±\éÎÿ*µÕ¤©uØÚ%hÄlÇî†'µ~TþÃ_¶ßÿd¿†W¿³gíQcªx\ðíÜ–fm>yD°]9œÄV%g%g*Û|¶FR© ØßÚ#âÎð3àŒ¾+jNxNšh±ûË·]¬\ÿÏIÙñ¯ÆÏø"ÂÝFkŸˆ¿uUfK‘…i3ä´®Ynïã×[óÜ“éY_¼}ñ»þ —âí#ágÀ?ßø{àî‘v'½×µ(š{©Tìó¤ÇÊÞP-äÛ#3³ï·/öïàŸÂ|ø_ |(ð™f¨ €óT¥ÂÜ$› ±8'ŠùCþ ûFxSâŸÄÏü$ðMÿÛàøxÚ‚j®ªËê’ȱIÌæ·X0Yr¹r$B_¼iðãÇßü1¯|$ÔWTð¢YÇgc2Æñ –cìå HªÀ¡B¤Á€>^ý?àœß?gï‰sO¨xãÇÚeÕµ¹VV†i3æK J¡D“—Èàçk œÓÿ‚¥ø’ïÿ±?ŽVÉÌrjÒé¶%‡Q·±ü ”ûý ¯™?lo‚7ÿ´?ìÝã_…:4‰­©ÛG>žÏ€¦òÊd¹… JÑùlßžÔòßü#ÁöžýŽtÍ~ÀŸÅZ¶¥+ãæo&_±('Ð ~lŸS^­û`~Ñ—?¾øÒ\ù3xkOyõ}MOÉbeýݽ¬MÓíW2²Æ£ø3»±+ù[û1þÛÓ~Êÿ.ÿe?‹ÖÞñv©\Çý圲¥¥•ãäc a¤yÖGsËetmø5|f~(ÿÁA/4?_³7†õàæ‘zo5_jèÑRð²^Ý?I2ÞUº39'sìy~¦Ùч¶œ¾ÊíæÿEÕêôß–¯4åÈ´]_è¿Wúíé?ðDÏ…WÆoˆuÈŠe‡@²•‡26庼äö[ýI>•ûå}ce©Ù\iº•¼wv—q¼3C2 #–9WGF2°$FàךüøAá€ß ô…ˆÇ¥h0yJï6yX—–yH%³¶2pømû7ÿÁFo¾~ÑŸmÿiýCÄ0xWÆŒ·:Bß­ÝÂh‹Äí1YÉ—Š "”)òc@TŒ²ùgQ‰ÿý‚m¿f؇íMû6ß\hf—}·¶JѾ•4ò„†æÊPC¬FVT1’JN̪~Ã~Ä5Ú+öhðÄÏ„ôÉ5–¢cWº²• i@3*¬¥@— p~n~Øß¶Ž‰û_ü?Ÿö_ý4=WÇÚ§‹'µ÷ÑXÍoooko:ÎÄn…¤‰wÉ*¤kì±'åýAý‘>¿ìÝû>xOá-åÂ]êZd2M4y1½åÜ<Á ”F}ˆHª‚@4ùÿ¹ñù³ðÃ…¶òeµ}JëV™òÂ![½˜Ý>=×Ú½iÿà“ß³µ÷ìý™gᇵø'‡WR}NõSûsìƒ÷’DexV3s˨‹rWæ'í¯ûL|&øõûox;Ä«©½ïÃM¤Ø\]Å °žÚ³s,Q0ÙÑŽ>`€€Aýfñ'üïö@Ò4k«ý÷W×o¢F0ÙçI JøùTÉ>ÄPORIÀç¥}3ûüÕfÏÙËÃ_ Fï:Tç÷žZðÈ¿t/¬íõ+>ì‚ê7Š@¬ÈÅl2Êpz‚êkà×ÿ‚^~Âò;K'ÃRÎÄ’ÇZÖI$õ$ý·­}?ᯀŸ<'áÝ3ÂúG‚´c¤ÛCi™c’yp E.ì…ˆ3I9'“_ÏÿÅ­{Àß¿à°zþ‚lôÝ"ËYÐìî°Žh%¸²†ÚPvai“÷0wgkúDÓ¼?¤é>µð®ŸC¦YÚ¥”Q‰²ÁÕ|ÂÆBB€7Ýß9æ¾ø%çì/+´’|4.îI$ëZÉ$ž¤Ÿ¶Ðÿø)WÃÝkâ?ìmãÍ3öÏy¨éikª¤1ýæŽÆâ9nöÀ$`:’09Åx—üOâ÷€µŸÙKLøxšµ­¾¿àëÍA/m%•#›Ê¹¹’î9“YvïÆ2Œ;Wéÿ†ü7¢øKÃZ_„4 ³é:5¤6°³¼¥-íãF…åfwÂ(‹¤““_øóþ ­û|B×® ÿ‚u~Ç?ÑOÅ/ k:½­†­¨É¤\Z[ÜL‘IqBé$1+_adÝŒãpõ¯ÔoˆßðOïÙâ×µ_ˆ¿|ý«ânE–òçûSSƒÍtEŒ. ¨ã_•@ùTzõÉ®'þsû ÿÑ3ÿÊαÿɵçÝ:ί¡ßkέq ¼·%cÁgX¹ “Œ8çò/ìûkøöÆ·ñTÞ Ð¯ô6ð›Y-Â_´LdÂcO)›ä09§~Ð7ÚìŸû"kÂ/ ÞÞ[ØØ¶‰¢i– q|ñ\jE¡…¤gi%1¬²nbÌK ;™kÉÿà™_²‰?fOƒšŽ¡ñ?²ø¿Ç3Áy{gM•½º²Û@äg21Ýù—ÛÕI ŸßðP?ÚgÂ_¿i-'ö[×|h¾øKáKå%ÔãI¦ûMì¼Ñ…‚9YŒòbR›DåòHûÓ@ÿ‚ƒÿÁ>~ |4µðŸÃEý•áË3†—a¦êd»!¤·U2HÙ-$Ž2ij¶I5èz¯ü3ö"×5KÍkTøqçÞêIq<‡XÕ×|²±wl-à,IÀzUøuÏì'ÿDÏÿ+:Çÿ&ШþÅß´ˆ?iï:Å¿èöú%Ýõå帷µwx¶[JcVO›'û¿,ÿà¬?òu>°ÿéÆ*ý³øQð“áïÀÿYü9ø]¤ÿbxwOy¤‚×ÏžãkO#K!ó.$’C—bycŽƒ¿?à¬?òu>°ÿéÆ*ýú¯Ìÿø+gŠ-4Ø¿ÄEÄâ)|I©éV©<ÈñÜ­áQôKvo¿Jæšhd¸¸‘bŠ%.îä*ª¨É$ž©¯À?Ú“ÄŸü“ö¢ðçìáð^O´ü?øy<—æ½ oµf•‘.&ÆQÄj¦l¬‘œÝüàôcþ «áK¿~Å l¯Ð¤÷ö×z‰c1ßÞMq übt9ï_tÖN¡i^Ð´ß èVëi¦éÐÙÚ¿v(-ÐG öUPkPEPEPEPEPEP9âÿiž ð¾©âÍeöYép<òv-´p‹þÓ*ŽäŠó~Ôô Íâ¦%ÅÈø—áÿ„PþóIÒökšïuhâlZ[7cæÉó2žv¨"¾Ž¯Z¯îpÊŸÚž¯ü+á_7¯þrCߪåÒ:|úÿ—ÞQEy'XQEQEQEQEQEQEQEQYƒZÑŽ®|ܵ~ªjtú}Á†`qž8¯ÆŸø)­Ô‹¦[>W zàšáÄRVæG»•âªF^Êgê‡ìÏáá{ð7Ã÷6ß,‹g¸k¿•. ”ÆèÊÊqÏó¯Çƒ?ðPŽ>ð›áÍ#áâê6¶P¤qɾQ¸ø½¤J’kSO$`òàþ%g‡Ä[Fuæyg´÷©n~²|KÖtÙ¾êºw‰0öo €™H#ÇZü|ÿ‚wÅ+üwñMž’ Òšÿ¢ýÂ6-rÚßÇOÚ/ö©ÓWFðþ‚4ý.ä…yc‘É¿­~”~Å_³ýÀm3ÏÖO™¨ß‘$Ž@Îìcš*RmÞ!„¬éÅB»>Ëñn‚ta&¡ÒÝ2Ì{_žŸÿnŸ ü/ÕŽ‘áG]STL%>c¸œâ¾’ýº¾0¯Ã¯ƒz„Ö.ÅÒRÜGø×çÇìcû0iÞ-²¸ø±ã˜—P{ÙщpÿëUR­vDWÀS¦ÝX·‡?à§ñ%ñ§ƒåÓô²ÀÊ s׆&¿Nþ|hø{ñ÷ÃkáËè®eÚ<ÄæF#8ä ñM{àÏÃ}G›IºÒm„n¬Ú¿-<­ê²çí;cá-:톓­J_ËÏÊ 2§o­gV'¼Ž¼&aë‘£öËÄzSx|É-ÁÙn¹;@+à_‹·g†>j‡Kð‘]SSŒbNNAéÎ+éÛOâÔ^øw®Ù¾Û›ØA‡Q»oø×À±¿ìï£ø¯N›âOí¿´f¿o5 ºÿ…kN¯:³81yt(OÚ#¥Ð¿à¨b¼I|gàétí0°Ì쑷׆&¿Lþ|høyûAxmoü7{÷FôSó)#è+ƼUðWáψ´)t‰´h”:R#Šùö`øñ;àïÆkÆÓ#qáû™K¸í Àô¬¥‡týävPÇR®¹úñ ]Ò~ØÜj~&m-¡‡sÔ üãñGüSSÑõ©m¾i?Û‚&Á1¨`ï¢+öÿø™®xûâþðWH‘¡‹R˜A.ÂOUbôúçàçì¿à†^±[Ý&;‹¹cÜò8É$Ö´¦ê+3оysDò_†ÿðS‰.5{m7âÎŒ|>“°RÒP3þéjý0Ó5o üPðâx‹Ã)s‰¹Y{gð§Ç_ÙÇáÿ޼zmô¸íï2ctêÖ¾oÿ‚}ü]ñ'…|¬|(×ä/om/—rz`ô¬gMÒØô0¸¨b"á#Ò~-~Öž#øQñwNøm«h…,u Äb䪔çœç=«îO xˆÝXZk–-„¸PÜ+ãOø)‡Ã?‡bø“¦[fçHO3*9ʃÏZì¿dÏÿÂið¿Mó_3[®Ö˜âº)OŸFy9ŽØ¥(sxâm‡†üâ[·Vk(‹•éœ}kâ?€_´ÏŠ?hïkºCx|ÙXéo¶;€¨<νÁ'µywíÍñ çÃ/|5¦NVëR…•Bõ$ŽØúW²~À ³ð³⠰öñG'ƒÆMeR<®ñ=,5X×§j›ž…ñSâW†>és_ø¶ém<•$>Ÿ~ÜÿÁJ¼eiªIQŒž•è¿~xgÇŸuÂÎ8š+WUGCƒÓó¯ÏŸØÄvö^!×~^Éå˦“± þó°þ”©Vp|Œ×„j|ô÷?JIT_1Ýô¯”öÚ×´‹6ß t­(ÞÄÒìyÓ´óÉö¯{øÃªŸx7VÔoÃNÊ}ÀÍ~c~È~ ¼ñ§Åwâ&¢Í,Rb,2ßã]5Ôg±ãåêtrè~úÛ¼z¶×š {f¼·ÄÞ’ÊV¸µ£nH©|=ây¬eKk¶Ì}>•ë ÚêvÜaÑÅr.joÈö%*xÊ^gæßÇŸÚÿàséןjû[¨nŸ.[Íbx£þ ¢é^ µ—K·]C[¸û2€Ç8<~µåðS¿ \YèN%d™€B;eˆ®sö/ýžt„ð•§‹üiÔn\ü«'ð‘ŽZ¹µ7tsRK s×þ âÛmaâ'…[AÓî#¢(ÚN3ò“_ ~(xKâv±á›µ¸W§ÿÕZÞ>øð¯â‡€ïô[íîÖ&¸!¶œcñ¯ÉÏÙ—T×~|yÖþÜ;6Ÿaºs!~®œ]™¼:”ý­Ï·ÿm}juø[öYŽcT<ŸÆ¼'àíAð×á¿Àë]=ncŸ[…‡«´{Zô¿ÛňøM#FpZ"ߘ5óßü{à„u­þÛ ô˜áNŠr¯¥gZ=bu`k^-R-Gþ ñ'BÕ‹k^xtvn. hÜõÎsúWß¿i/ |W±‹RðõÒý¨ÒD§‘øW¾|CýŸ~xÇÁ—ºjèÐs „ rSŠü.ýŸ,î~~Ôºßlä1ZFØ ž1æcúUÓ¯u‘‰Á(®zgôogâ=ôÇÔo%¤ YËQ’JüïøÝÿð‡‡µ[ |?Ï¥xÝ^ÂP•$½pzë^qûþËðŽ/ˆ~)[Aq¯Îw3Nãpè{×=jN.èìËñj¬ye¹òõ×íÿñ‡J»óüCàY,¬XçÌ1F_^×Ùÿ¿j |`›r‘êH<@àƒéŠúâ5¯ìëk¡ÝÚøš+1”ë€T‘Á÷¯Â/…º¦ þ×ÖŸ® h’I… ÀÆÿ@MiJ³zHÖÆ1”éŸÑî™ã› -6[baVè]™Ž85ùïñ£þ ¥é­Ï‡~iÃÄ—q„CäŽß6+[ö¬ÔuÛ„“O¡»¤Ò[ì½pV¾Zÿ‚wkßgÐÌ:h¥ñ1`§àw'Ö§±/*¯í)¸ÔÔɶÿ‚‚üIÓ/¿<úE”Ë´h\õùX×èÁO~ø«¦Ç­xNõe“‚ȧŸä+Üü_ð7àçÄ¿ͦO§ÚÉÔeVDÁ+¸pF |Ëð›ö:±ýžõYo4ù.l¦<#þDúV˜z÷\²9s<¾0‡µ§¹öêxÿEÓôy5-rå-c·R\±ì¢¿0þ:ÿÁ@ô =^máEŠë÷ÈH"5ÓëŠò?Ûçã>¯á›_x~cÚŒ‹m88rþuôÇìCû1|<Ñ|oâ/ÙÅ{¬Ývi[‘Ïzδâvaeíé%TùcŸðPOÅ©Åeñ7çC‰Ø ¿ÊM~žü0ø­¢x›Mƒ[ðÝêÝÙÊ;Nzýk#ãçì·ð×â7ƒ¯ ƒL†Þî(˜¤‰€AŠü“ý“ü_ªü'øÁ}ðƒXºim¢fç…Àζ§YMr³‡‚•7ÍO¡ýXjúŒ $L#‘_Ÿ¿¶î¾‰Ò H}'áí~m:Pá³ €{ù»öÔ¾‚ÿÁö²Bw~ñz~5у¢áTós¬lk`Ú{£Öb¿ù"–ÄÿÏåÏóZúî¾Dý‹Q“൨aƒö»æµõÝyøßâ3è²?÷XzQ\§¬QEQEQÔìWSÓnô×b‹w –$R¹ü3_Š:oü3áÞm~>'j²5´©(`€QƒcïŸJý°Ô.ZÊÂæñ{AÈñ’ªN?Jþy­?à·>>»¼‚Ûþv˜‹4Š„ÿhÌpŸõb€?¢Z(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€+ÝÚÛ_ZÍeyÍopˆÃ*èã ¤wð÷W>¾×¾êò3ÍáY<í1Üå§Ò.IhOÞ0¶ccÐp+èzùãã…­Ï„ït/ºDlóøRO'SDiô‹’q÷Œ-‰Tt&½l±ûNl+û{‰m÷럑Ɋ÷mUtßÓ¯ùü¡èªö—V×Ö°ßYȳ[Ü"ɩʺ8ʰ=Á"¬W”Õ´gXQE€(¢Š(¢Š†Kky™^h•ÙzPHúf¥(ÂŒé_€ßð\i®ídø1-´Ïʾ!VÅAÚtò3޽M7þmu$­ñª)\¹ÿŠu†Nè"ô ßÊ(¢€ (¢€ (¢€ (¢€ (¢€ ¡¥iz¬k©g â#Uš5 ½_¢€ˆ‘ Ž5 ª0AN¢Š(¢Š+ž(|FÕ4›Û_‡¿¡MCÆÚÒ70ØÛôkË“ÎÔOá—n= ïŠ?¤ðtv~ðͨÖuý¥g öðnŸàx‹Xдh/’êò-"ä[›øU~Í>åuhË2¿*pTc‘Ö|øð§ö}ð’x/á7‡àÐôü‡™“/qu(ó.&rd•ý p .zõQEQEQEQE|kû~|h¼øû+xÓÅú5ÃZëwЦ“¦È‡kÇu¨7•æ!ìÑF^U>¨(à¿ÆÿÚãçü2_ìEuƒÃ,°ê~&àÿ¨âåâ‘•Ä6П”̪d‘ð"#+æj|dÿ‚z|PðwÁŸüDð÷íão⇴ùõ{Juµ»û,fY!C•üà|Uð3üø9û"|QxŒ7Wñê¬Ò‚|R+èýø1ëŠÌ£úè¯æþ 7ûwüCñÆùüð?Æ:§‡<)à–—Ok­ú{!¨êµHÒ[ºy‘ÆÀG$•x’¿]ࢴ¶§ðOá5·>3ÜüJø›/ö>…·Íq˜¬rÜ îÖ8±ÏšêFBµ~|Oý›´ï ~ÑÿcÛm—š„+¥ÌŸ6ýS[¸Y¯˜Ö8m(ž©ìeDÿ±·Â¿|1ø# ¿|[®ø»Å>"´µÔµ9µ½Bkãmq<*ÆÞÜLÌcŽ-ÛH,À±êø£þ çuã/øÁß>øûľñæ­†š~‘ªÜYÚ]A,W3´ ™™]QCçî¤t#ö  *ŒÀ¿ÿoË£ñ?ö×ý™>CûØ-u×oáïîPÃý˜l§Ç bh§ü×ãÄ_ˆúÅ?|Jñ>©â]CB¼Ó&‰õkÙïe‰.’á§w*¡ Éã'Þ¿O?hOÞýþø‹âÏŠØ5¾0[îÚ÷wr|¶öéï$„pv®X𦿿à˜@øöÝý þ݈N¤Btiz·¤~ñí]Oí â«oÛkö§ºøn&i~~ÏpÜk^*ž6ýÍõå¢;KáÁÎÆ·NáEÄŠHÅ|1û(xçö˜ý°?j»OøŸâÇ‹´m'Uš÷VÕãÒµ›Ë4¶¶ˆ4­´K'— "@`b¿©›¿Ãwáy¼*/.à†k6²ûLw·ˆ­—æ,ä—ó 3»wÍÖ¿?àŒ>»ñgÄÏ‹Ÿõh”\´PØ#ªáLš”ïwrÓ|z0¯Ýψž0³ø{ðÿÄÞ=Ô1ö_ é—š”¹8-!i˜~!hù¿ø£|høÍûtx›ö~µøÙãÉ| áMKZk‹˜üE|.ßNÓ.‡™æÝ$3…1`À¯¬m;ÿŸÿm~ËŸ³÷Žuæè1Üê×V…Õ´vë,ŽòÏr ‘ ¥ XBn9g.Fì×ÿRð}æ³­|YøÓ¬æk«¦´Ó#œŽ^IÝîîò}IÆ¿y"ÐôX5iµè4ûxõ;”Kt± žHÆ0­ ŠŒ qÀô æ›öúðÏÅÿÙ#Yð„üñÿ≵¯Aw=â]ë—hcx£·0¤R‚<Æ2‚›îŒµôí}û0üoý?g¿Œþý¢~ j®„ÚxÔm.õ˦…ܱÛ?’ÑÈŒ»e•HÝ»+‘ךóßÚGþ2þ éே+þ•aá+ÒE£C§FÚÅÚŸûîDo§°¯èOÄ>ð÷‹t‹ø¯KµÖ´»­žu¥ì so'–Á×|RFÚêdp@#‘@:~Å.±ý•~Íã½^û]×µ=&=NæïR¸–êé΢Ív‹$³39òã•P~P v¯’nïÚïâ‹ãmöDý—CÜ|Vñc—7q'L·wª¡`U%xó#ÈF!‡çû̬Ÿ©ªš~§‰#³±±‹ ¨¡#Š(—€`*ªŽà_€?ðKX¤øûû\üdý§|JL×¶žÙ_Ÿ%õ¹åòö“ÓÊ··hTu±Ú€?JÿdÿØwÀ_³=ÕÇŽï5;ï|JÖíŒ:®½q#´žk$“G dàFÒ"Òoàe±ÀüÆý©ü-áÏÁ\< áOi°jú6§“Õ¥Ê !š3 §k©àŒŽ•ý ×à/ÇïùLÃo¦‘ÿ¢f Ú†ÿ> |¹¾½ø[àÍ/ÂÓêH‘ܾŸl4É%ʤ’>µùÝñóöùø«â)|cà_Ø»Àþ)Ô<.£ÖüIyjbÓô×´ ç¤1O°K2ml É_–)A¯Öªó‰¶Vv? üj–PGn²éZœ®#P¤’ Üà ³I<“Ö€?'à_>$|XOŒž&ø—âmCÄ·òÞhîúáæXÙÒvFm@«…Pöª¿?à‡ò.|^ÿ¯½ÿEÝ×ïø'û?ø_öâý¹¼Uâ wãoÄ|,ðN… ,ôhdÑ&»–\Ÿ* GážI¼Ó’ªÉ)ûÙE6µf‰ûJÿÁ7þ xGÆ >/kÞ(ðŸˆ^r–ºÕÔ—Qùö¥ °]@ìa‘d×l¨¨ÿ †?¼:<¿iÏ‚^ÖükáëMgÃÞ1Ó4í`éר·©¸‰.X0ÃËc8ŠüNÿ‚„ø·Wýµ¿iÿþÉ”jŸð‰Mp5+äù­¡¼˜¢Ü¼Ž2VQ¦ç&FhÀ,Þß‡Þ Ó>ø ôRͧøgM³Ó-ÙþñŠÎ… {•@O½=ŸðQ¯ƒÿ ~þÓ4ß…ÞÓü-k¨K·éð, ,‰¨DªÎ 8¿q?h¯Ù«áoíCàuð/ÅIå·¶”ÜÙÜÚLÐ\Z\í(%Œò„íb1":œò½+òþ Ãÿ'Qðëþœb¯ßªü<øQñ·âÿìûEéÿ²Çí®\x£á‰]Ã!½,ÒÚÇ#áV$ùHäEÕ&7…Þ”–×r“––æÁÚÎiŸây!f>æ€> ¢Š(¢Š(¢Š(ÿÒýü¢Š(œ€+æÏŒ¶ž$ŠêÛY×d[¯ZK^ÚÛɆf¸$·Ÿ 6 ƾ^,$­}-ŠÅ×t;i7Ú¨¾ež£ –ó'Bc•J°Ïn rcpj½)Rm««];3|=^I)1ü×üDž'ñ®‡£Üê^Ô/æžÇRßk ò±k¥T’A$–æ\²HˆrÌÀ 5}fƒûœÖ^¢iúk¤iP­½•”I 1 ¤q€ª z1Z–_„ú½Qæo•Zïr±U•I¹%ai Öšçj“^aã‰Ú_…®—F†Æï\×$‹ÎMÓÑdœÆNÐîÒ4pĤƒ†–DnH"¶¯^¢çQÙ#AÉÙ ‘M¾µáúÆÛ)u }'Æ:¡àû›ÉVÿÚ_gh&‘¾ê-Ŭ³D»#²±<M{[>ôr§­g„ÇR¯iFjKº*­GFžõù5oŠÞ7Ôü e{q¥økÂ%Õ%´Ã=åÜé½m«‡H㌫ÊP‚Å•AuP¿ýž> Ãiö^Ó-ŒIÐÛGÂHC¬èPóÎ{ÕOk‡Cø¿ñÁ3æ+Ë˨5«}Ü ­g·ŠËë²HJ·ÔW´y¾h2IÉ5øO‰\[y}N1”jBW¾ÊÝ-ê{t(JñzYGð×ÄZu/¯_I©Çyo%î‹{?Í;Á *Ëo;ÿ‘VW<º›æŸ¥•‚ $×È·×Å´_„ì4T2Ú^ÝjR® ÃöÈÖ(b'ûïËcÐgÒ¾·©Éõ¯ÔøˆÅet«b~+}övOî8óZQD×THÓFƒ,p*¼wö’»G™1:Œ×‡øêúïÄþ3µøi§ÞM§ÙÅgý£ªÏnþ\Í ¹Š xä2y̲uÁ ˜n®p|ø[lÍu¥è6úmúä¥ý¢ùÈçÿiLJ[<Ìrz‚2+Ïâ~>Ãe“9®fï{=VÝ7Ù™ÑÁsÆ÷>œóã9§dW‰|#ñ>­}&µàÏÜ›Ý_Ã3Çܲª5Õ¬ë¾Þá•@PÌ#ཀ½¥ä9¯´Àâሥôã%trÖ¢á7KED’‡]˃R×I“ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>Zý¯þ,–­Žyþ‡_>þÂÄ:øtOè+è?Ú÷þHž®?é¤?ú|éûj±é>¿•ã ÞgþÊ+ÜæðÒ>2œc™ÂR?Bõ½ V¤(èkðþ ‰§Í¦]Y[\©Ú[õä×ô¦ê¶Ú¥¸–ç¸5øiÿm‚6m.SÁÿëšóWuŸS<,'j±>ªý“±°ð/ƒ/¯4¯,’5M¬9Çü´ú7û`ü(›âgÃKÛ[ó.!Óœ©úW„þÄ¿µÎ‡ðêÎO…?XX½‹ˆâg^¡G©ÇsS^•ýè—”â—'²žç†Þxƒöþµß<ÞÔ;áS ÿ‘+Ä.þ~Õ_þ&èþ(ñ?„.ák7Íu_”ÿô¯è›Qý¡þ [iO¨Üjð}œ¡lü„ôÏL×çe×íÕ‰~(|9ÑÆ«a#•iöÛÈ”Žþ´R©Í¤qT'Gߥ‡ý¾/5¯øgÍ'N¿VŠ[H!Y”ú€€þµ÷7ìKa¥jß³î‹@¨$zó^3ûQ|;»ø§ð†ê(¡ÿLhƒy~ã¼7öý©<7ðÖÞïágĉޛ5£„‡pà€sŽæ¢­'Ͱ8¸âbãQj~›ë:DÚTåYIRxª°_ÞYÆòÛ¿ð7óÒªxóöŠø3¤øfãX½Öb*#,„$Ÿ¦êüøañ/âWÆ/Œ÷WúÌßðŒÁ)QÁ AÆ?‘®š8…%fyXœ­Ò—=ô9/jº}Ïí£ ¶´žo†Æ~ áë÷ÿûÓTÐíãF»Xzb¿¿l¿…^!ð4oŒ-3iR _n{+Óë_¤?³?íðÏÇ ²±×55³Õ­$‘¾#݈®J”%t{jÑ­Íâ­.]"ÞxîW÷D ô¯Å…7ËoûT]\iÇ&;›Q_¦ÿµ/íkð£ÂÞ¾²Óu!u«ÌŒ°¤`7Ìzr ¯ÏoØ«á‰uj_|CÇô¾d[Á)Ï­m¼ïSÏ©ƒxu)#ö3âÖ‡§üTøW«è1‰%žP½NN+òÿö"Öfмq⯇š û0³”ˆU¸Èùúmky%•Æè›1“‚Jüqý¥¼Iwðâä>;Ò¢Ùñs)p;}iT§Éï#l:ˆ*s%ý¥%¾ø•ûCi ·bðX݈¥ Ós_¨Úœþ øxºU‚•hmÎBñÕ+óãö-ðUÿÆÏˆºïÅÍK,—´ðäq‚A&¿Pu]5ãó,.T¢ì+ùŒVôgÌ?1¥*rJ;#ùÄ:¿Æk?ÚsÄWÿ4ɵ=Eï\ƈläc« úãQñ/üø†oê*vŽv§L×JÂøÃÿ 'ìÓñò×â]Ÿ›¥Ü]y²¸\árIàé_¯ÿ ¿kƒ¿¼7k~5H¢ºdb8U ÏS\µ!ÊùsV œ‘ø§âËïÛ“^Òn<;¬x.ø$êQUèí¡¯¥àŸ¿¾0ü0Öu-GÇTºdwìÜžÄú×Ù?´GíðáÞ…qs£]E«D§Ë…[y²¤šç?gߎ^%ø×¥6±«èÃK…Ø@?0:¨­©ETÕžf-Õ¤½Ø®SáÛfêÛ\ý¥|³æ¾ {¹¯Ö=K—LðöŸ ®„ß"¿¿k ͧí3๾öÛèˆÿ¾Í~ðøN;x/Nn‹ =”U)òJè+ÓúÅ%}Î ¥µ¿X¦ùXzƒ_š´–ßÿj{}föhµû€„ô·õ¯Ø]KMŸJ£”¹À#šüáý¸¼ Yé~:·‹3i Òîz Ò½.uÌreø™Qª©ËcÙ¿o/ÙjÒt *\ϯƨ»O$¹+[?²ÿÃY~ü6²K¸¶\\e™±ƒ‚ùñðCÆ×_µ'޲TãŠÃóu>àšâK{yg¶aM~:iZ”z§í‡*¡Üæe Žÿ3WèÇÿÚ?áG€|3{q¦ê«=ÜÈÂ(Ó ’AÇBkó÷ö@ðµãŸ‹Z‡Æ Q`¸ÃC¸c£“ߨÖõ-5¡çá`ð×U6>Ýý¿ô%¶ø[ÄhÈÇN+Žý‡¡·¶øQgä Sžà+Ñ?mívGá ³›ï¬}ÝÍsß±^‡:ü³¾€FoËQBN.̼u%:<Ô¸tßÉea<g*ÈÃ>™øG¢ÂúíÙ¬‹gÜÆ?ïá¯Ú-H§\6pB¶*üLøu~¶Ÿ·.¡,¿wÍ?öÐÕW¤£%((Å)§Jgê/íñ|ü ða¿)¾íá5ã%Šàc>õùƒà«OÚ‡ö¹oØëwÞÓn¤Œ«´óü$×ÜßðRÏ ßêßôé𙢶Hû2xR Î=…pŸ±çí ðÆ_‡–ºåâYÞÁ…xØ9z‘ITR÷dM|<°ð禇økþ ÝãYµïŠþ#Üß°R^'šRò+â¿…_ ,~~ÙW>‚A¥öBt)h3–#¡õ>•ù?ðÄúïŠ?kGñoŠ­Íœ—3ŒnÎN[=À®zô¹»±êà1^Þ³V?|oà][B]+Pe´¹Wæéó.;×äwÅø'¶¥¦ê÷(øe­µÃ±`³_¨_µŸ.>GÀò:ÝÚÛ >N¤Ïô¯ÏŸÙ+ö°Ón´“áŠWím­À@a(îïbº)TŒÕ¤y˜¬$è^t^‡È°OûsüÔ>Ö’jz¶›hs†rÊT}\v¯ÔØëöÔ—ã…ëxÇ–‚ÇYpRLÝÀìO­v>øóðNðÅ寥¨Å2˜œªíSž>µù¿ûixÿö¡¿øáèš×G’¥A‚ÊzV5©¥ð¸ c«Zˆ£ÿ'ðþ³¦üOµm&/´º¼OÕŽÆkÂmý#K¶]—íl(ßÊûïþ 1ðC[ñ6¦xëÃQ™.tÆäÚ2H‹oøW=û9~ÖžÕü1oáïÝ-Ž©j:ºÓ¯\UQq–“2. ôÑólà :ª=µ¯ƒ5AY@N{ÏJó³¯íM®|z_ˆ~7ðÞ™󼊸OG>•ú¹¯~Ô? < §ËªÍ«EˆÔ°P©–ã u®ö{ýº/þ;xúO h~ §FHûQÜ3ÓžWýk:q—ºi†Å:𴕦ÖÒæÂÞ[¥Û$q¢·Àæ¾Lý§.´«Hæ#*ñøšý$×<;±“nÉ@ϵù¿ûWX\XZZÅp0VUõä×­€ª›K©ñ|C—ºPo¡õ¿ì¦Çð–Í`WÏ›–¾˜¯˜dòáRYžÿh›úWÓÕäã?ˆÏ´É_û4=Š(®SÕ (¢€ (¢€3µuߤޯ¬üt×ðA§6ÍBÕ½%Cÿ þùo×uÂúÆãô5ü [¶Ëˆ›Ñ”þ´ýûQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEU[ëMNÊãM¿‰gµ»â–6WŽ@U”B Z¢šm; hùïà]õ߆ß[ø-­ÊÒ^x:Qöü×MÆZÕóÜÆ3c…Àô%|íñ² ¼¬h4ÈÙ‡_ìzº ËM¤]0,`‰}IàWÐO ÔÝ[H²Ã2‡GS•eaAA+ÔÌÒ©ËŠÛßüKâûþ/™É…÷oIôÛÓ§ù|‰h¢Šòް¢Š(¢Š(ðGþ •í+àÜÿÜ›^_ûélOþË\×üÞmºßÆ?¿o¡·ýò÷£ÿf®ãþ ‰ïü%Ÿû—Ú²ÿßQ[ý–¼Óþy6ßüWƒûú~–ß÷̳ýš€?¢j(¢€ (¢€ «y{g§Ànoî#¶…Hå`Š éË*Õ~3üvžûöîý³l¿e>w_…_ uOÉ ·×é…û1aŒgòÈe?hq’‹€Õ¯Š^=µøaðÃÅŸ®-[Q·ð®‘}«=¼n§K(rŠä „À8 g5ñÂÛoãGÇÁñá_ìñ¨ëZ̲ÁÉñ&“m™ m².áã`÷+ƒÚ¾Žý«lí4ÿÙ+âå…„)ommà½r(¢B$q¦Ÿ2ªªŽ`ÀòÇü;þLÇDÿ°¶«ÿ£èôºÖI¦µ†k˜M¼®ŠÏ`ÅŒ•Ü88 Xü>ðìÇqªZÙjwñkòé·BÍR76ËlׇIC3FwÅrw?ðGÏÙªçá³øzK­V_½³gÄr]ÈÎ÷¬¿ë×>I‹ÌçËÆý¼ |ÔúË^gñ;âMŸÃÝ.Ý`¶mW_Õ¤û>—¦Ä{wpzöc\æG<(÷ À¯ø%÷íMã_†~)ñ·ÀŸß]ëšV›c$Ú>žYî%R¶ºŠÔÙÚd‰7›¸¨AMÀ[wî‡Ã/‡šÌ:¥ÇÄÿ‰L—^2Õcر©Ý—jy[[~£#?¼q÷Žy<–ôðxhFX¯ð­—ó>Þ‹«ù-YËZ£oÙÃ~ý—ùö/ü.ømyá™/øð÷Æ¿ üuªO­'æÓäÓ®n¦-oÖpm̬I)[æ0yPÅAÚ_Ò¿ÚgþM¿â¿ýŠzïþM@¯ì¡ûJiµOÂeø¯¥h“xzÜß\Ù}šy–vÜ)/½UF£,µ Jq§\Åu%KÄêë‘Û*HÏ5ù{ÿ}UoØò5auíLzˆ«Ë|4²Á>mØ>Ú·‘ð[ãäæ]>#òÃ¥k;„~Zgå $tŒôL±n$ÃÈíQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@EqPIksË ÊQцU•† ðARÑB`|áð¦y¾x¿QøªÈÆÅõM!É’ÁØ™m·¯näàg%pô}xÿÆoj~)ðõ¾·áR"ñ_…æþÐÒ¤þô¨>x¦RtÊ0Î ÆxÖ|=ñ¾™ñÂw‹t°cKÄýì-÷àÙbqýäpGNzô"½|zöÐXµ»Ò_âïÿo-}nqáýÉ:OåéÛåùXí(¢ŠòÀ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Æ_ø-•Ýü³÷‚,¢ ö9üN¯)åó#²¸ƒõ ä}+öj¾Xý±ÿf;ö­ø¨ü/žñ4ÝR9âÔ4«É4v÷Öá• ªòQãy#l@rÀ þÄz=ž‡û"ü"²± #“Ãz}ÑÙÓÌ»ˆ\Iø—‘³ïš÷wâ7€|1â]Á¾"ñ†™®øºé–71ÅsxcÆñlC>2:_’ØüÔ>Û|A¶Ð–úN«o­Ú¢Ø9(² ìY$G¸BÁ6©ªŸ³ßì×ûeüCý²´Ú¯ö³Ó,ô‹} ¥²°K»y…¸x%†Þ!·’`‘Æfi2òn-ó1,M}ÿ]¸¿ƒö$ñŠY†1My¤¥Á¤_n…†}·„rðHŸiý’tß[“Qñ]õü· :¢Z\Émgé°¿ü Ú¾ˆýª¼Çý6Ùm.ÖßÅp5ö¯s‡kKG…8šéSgºsÁ¯€gÿÿÁAa}YøM¡ü/²ø½àǽ’çK¼³Õ¡³hdœÄ,¬eXØ€ÍD¾â$ æºêR(&߯¶ò¿_VŒc8ÎMv¿àŸ~~Ý¿¬g¿Ù§Åž+[¤‡\ÕmßJÑã-‡’úõLjè;ù(Zcì˜î+áïø#7À«ÿ|)ñ'Æí~Õ ¹ñÅÄvºnñ‚tëÁ¥_E–vaÏQ#‚ ̹ýŒ?jÛW⦙ñöÖ¹µðgƒ46o°øWL¸Iæ³)hÃÂÒG´y³^S€ª¨ìý¡Ð´=Ã-‡‡<=e¥épGmkmŠbP‰(à*¨\†Çä×ü3Å—©ð?ÁŸ tL¾©ãÏÁD2Ãf„•Çýw– ò¯ø+ÏÂëþÊ¿­4¤×Á¶…`m=Õ~ƒýJ³ûa|&ý·>7~Ô^ ø‹á¿ƒ¢÷Á 5H¥Òà“^Ò£þÕ[{Ô¸’á·Ü«À.’Ô#!dP7|ÙQúñ‡à_„ÿjŸ„w¾3i—zDWfÏR¹²µºŒÏg{e¡ûB+ÆþYvBÊ6·QÁ ÌÏØÁÞ2ý¬~1ÜþÛÿívéþ¶‹Cð“’ñFö±ùs\&à7Ë9 Œä¦0‡þÊßñ_ðVo|Roô« ϬÞA!å@º=™ôÏ–êê;mÏjý’ñæ‰ã_?³Üý•ü ¾"Õt{4Ó4?í¶ÖIn lÜÒÝQy® _M”Ù\Â×W}žÞâi$[‰g q„r3@Ÿ¿´OÄOˆ³?ü+ãß -_xÂÓl Y2¤Þ!·µœK ¨%åY÷cþzc9Æßß¾ØþÃðLxaÝÆ^0¶†ÛX¹R ¸¿ÕÝ ž~­ÆDN͵›»Wè&¯û ü׿h»?Ú‡X±¸»ñ„q$"I³W†# sy;y‘ü¬[‚ |9ÿ6øUû\þÒ0é >ü7:‡ƒtk¨õ9µVÕôØ õגѤkÅÄR"B$Àïb(,è?ðH‡ÿð‡~È6^"š=“øÏV¿ÔÉ#æò¢aeú£ïg½wŸðT/ˆð€þÆ^4Heò®üLöš,ãwÚ¦Vxå¯Jý‡tŠÞ ýž<9ðçâ÷‚G‚5ĺlq­ý­úÞÆ`Ú¼‹vb‹nô"¾"ÿ‚šü!ý°?iCGøaðŸá©½ðG†îF¡ý¦ÚÆ› Ônä·¥m縊H–ÜI,0Ë’HÂàuÿ‚P|?ÿ„ö6ðö¥,~UÏ‹ï¯õ‰Aáåû,Dÿ½ º0ö5úDî‘£I#D’N©&¾\ýŒô¯‰þýž|)àO‹^ Öü#i޶Ë}m~·pÙÁ­à{WtO9·f2ŃzRþÖú—ÇxþjÞýžüÞ.ñ/‰-®tï7ûBÒÁtØçˆ¡º?j’1+.ã±ýìà`€~;ÿÁ4#Ž_·ÅÚ éL¶¶©©ÞÛ±êåÕï6[¨Ï@-„ª=…Eø­ÿÁøûP~Ëþ&ñ?†~*|,þÎÑ+ƒooo<²Ê%yB‚¸ØI'#§íMqÿ⸛À&†Óý|šeêÇï˜\/ë_ˆŸðC{‹VÑþ1Z¨å'ÐÝÏrŒ—¡"­ù×ïQV¨¯ç·ö ~Ê?ðP‰³/ˆñgaâq<:kI…ó£µÞœÀž?{g$œã!zñ@Ð~ü~ÿ”Èü6úiú&jýú¯À_ßò™†ßM#ÿDÍ@¿UÀ|Wÿ’[ãûjúLõß×ç?íCã¿Û·W‹Æ? ~ü´¿Ñu yl-|Gqâ <4°ÜÁ¶IRÊY`xÝK²¨‘˜ewÀâ€>4ÿ‚ȹñ{þ¾ô_ýw_¼uüñþÅßà£_±¶¥â6Ѿ Zø›IñBÚ›»[I¶‘d³ó<·Še»p¼JáFÏ1ÏéÏíAãÿÙþÂ^;ñÏŽtÉ>ø®MQ-•¾¢—2X^M †Òòßj»32`§vÛ@q×å—íûû[x¿Âºž›û)~Î &§ñsÇ! w´#ÍÒí®Á œ$ò&X1#ɈI_‘©?à•šÇÅߨûTñ_Š¥½ñ.§{­j—:@¿¹v–æ­à‰"Yî Â5Är¨líSŸC_œžý›à«^ øÕâ/ÚÃ^ ‚߯¾&{–¹¼š÷Ã×›éÃ:B·7R€ v`ˆÆÌíâ€?c¿c_ÙÀÿ±ßÃ9žúx/<]©Â.|C®JB©( ´Q»ãe´<œœ9‘ñÀ_³´íKNÖ,`Õ4›¨¯lîI ð:ɈÝIV±¿ž‰> ÿ‚Ô|Wðn§ài†] Y‰ ¼‚ÚëÃ6<-÷£ia™$Æ€ar ûIû*xÄ g‡_¼Yiö oCÑ­mï­üÈåò®BæTß:6‘•bbE~CÁXäê>}aÿÓŒUûõ_€¿ðVù:€Xôã~ýPçÇü2âÖØkâ\ZgÑÒ,ÿûVÕ¸ÿ€ƒT¿à•\Gûø¦û’\k ûŸÚWÿB _<Áf>)g|&ð‡ÀÍý'Zñ¶¨—R[Æ<ÉM¥»Ì ·G°ÿÆ88ý#ý—þËðOö|ðÂë°÷BÒ Žð /%k¬Ás¾q@ñEPEPEPÿÓýü¢Š(£Š(;ÓDNY‚sTµ=JÏJ±ŸR¿q µ¬o,²1Qf$ã^µñgâ»x‡Ã·vÞðüøk–ÔÜß]BFViFD…_9Uñ\T£Ï̳*XJNµ_…o¡½ .nÇÑÑ‘šù×á,ë¨èÚν{‰5-[XÔMÓ‘È×R[Ã=8£P£êz“U?á>ñÏÃÍbÆÛâ+ÛjÞ½u€j–°›y-&…O´Ä]×Ëbqæ«§P9^,Ñ|oð¯TÖüsàkKMcÃZ‹¾£¨i³\}–x&XÇ›5³•do0)wF+óò Üqð\Q^¦o•º™Dïߥ×U©éáðÞÊ|µ:ìÏBñÎ…£ë¾Ô4Í]WìÒÆÀ–åÜô*Fà{žÕwàWˆµ|&ð¿ˆui|û»ËÚI9ýé\§™Ï8“DZ¯ž|Y¨|bø“ Üipø}|%¤O<²Ëv—w+·p†%‡åŒK™ åA%FE}9ð³Äþñ_ƒtÛÿ§‘§E·[r¾[Û¼?#Âéü,„`Ç¡áxA…•u©Îj÷Wïo>Ú›æ°jŒnºžeûHx/KÕô [Ï>‘¯iw¶PZjvlâ¼ºŽ OÊêCçcdg¸™¾ü|šÑ´Ù>'§ÙJíiâÒ`[ÆSÁÃòÃc¸^µê?´ôiá} Ió5ŸèÖȽÎÛ´‡ýóôYUVY##¡#Šåñn5(ÊŽ*‹OGt›ºÕn‡€®Õ%7ú=|Ò´†_5?,ÒÌþ ³]J+‹·2\\ÜÛHb»gõ8x[h铊úä(+Ž•ò?ÆÛ;ýÓLø• Û4ú—„.MúÅÞžßiK¨~,÷”Üý9á½Kñ>câ aqe¨@“Âãø’A¸È×Ùøužýs-Š“÷á£ý|â—¾«tgÏïãøkñËân±òøgT°M+Qœƒþ‰4S-$l#Í"1ìvú×E¨|]øg¦èðÜøŸNû€RUº‰Ä¤œŒ;˜ž$ñT¾"êÑø—â7…~[4YyšÎ¤˜%‘¢·G#Œ¼Î)ê#'Óø’ü3ðþÂ"¹Óm"°»·.íE4L½FÖ^„Ž{Šûþ/Î?±0£BãgÞÖÓs‚“u¥*‹ÐÏÕü%¯ø"#âO†·^}Š–“K¸¹’kKèדóYü™xù$B¸<0aÓè/ø–ÃÅþÓ¼K¦9k]Nž<õÔB:ë^qãoè~ðíî·®Ü-½¥¬e»“Ù@îÌxrN«¿ô=OÃß t;V‹ÈºhåžHÏX̯?–}Ð8_¼o ³Œf&Hb$åð·þ}KÌi%MIïÀö*(¢¿b3ÿîá¥l;gçÙÆÔÌcØ÷ÝWŸJ]\²w_JðŸÚsöKÒjÛ{)¤ÕÍ€d€HîO`}kß¼A Ï¥ÎÄ ÆO\U}Y¸ÒgVŒü‡¨®z”UHû§¡†ÅÊ„½M‹Þø9iàOXøNÞo´µ„)¿\ìP¿Ò°¯¬®l'h.8Í{þ—©Ûjp,‘0'zÊñ‡`Õ!gQ‰¬hÕp|²=<~_ñç¦xµ½ßî$²œo‚e(Tò0x¯Œæÿ‚wèz·Å5ø¯áOeÊÒù²E2’Ù í_e^YÏa9·vã¡Å_Ñõyô«…t9^ëÚº*ÒRØñòü|¨KÙÔØéï<ö} MËÅGn¥ð0IÏ­~}übý‹¼ ñ*õï­b‹HÔ9”.'œä_©šf©mª[«ÄÑó ä|QáXîÞY®rG­rÒŸ+å‘êbð—ýýñ£Fÿ‚qê#RXu<úy#1<ŽWo¦6×é7ÁÙ/á7Â}1_HÓ­ç¼ n¸Ø ëœ[R+Fí™ k«ð߉n4é„719­ëaúÄåÀæ²rå¬)ð·ØòѦûWê;b¾ ø½ûxâuÓjÚjŤê.sç"í9=òkõL}“VµìÈ⼃Ğ›M•®"]ðž€v©¡Q?vEc0²¤ýµ¡ù“àÏø&š^]/ü$ž5mVÔþŒîì¸ôÁ\WéŸÃÏ€žøiáñ¢øgO†Õ¶€ÎŠ'Ís¶—ÓYJ³@ØÚy¯jðö¿©B@u¬ëPå|Ñ:ð9”kÇ’®ç‚øëÀz~£m>¯Û «IqA¯Ï?ÿÁ?´sR—Qðf®ži?¹ÜÝû5ªé6ú¥»E(ùˆàׇëEΕpÂEÌg¡­éUSVg6…\;æ§±ð·Âø'wƒlµ85k+âY¡!¶ÊKóÿZý1°ø{á½EFЬ£´†B.¼¢Òò[9Vx›kÚ¼=âµ(9HV©8»ÄìÁcã^<•7<{VÒ®t›†Y”ìÏZù;öýštoÚHO»»[Œ‚äÎ{_¤šÆ«G7cŠðÝWJ¸Òî r.T”Öô§«3ÌÆa'‡Ÿ<69ßÙ[྅ðCÀ¶~Ó.VêH"T’Aœ’?_AxƒÃñj‘ŒbQú×Xjéó¤ñ1À9#Ö½«B×-õx‡ H½Ea:rƒº=,2˜¸UÜùcâgŸxÛM›Dñe‚N“ È ã5ùó¯Á: –þK¯x èq;d$lÊ9ÿuköÏ\ÐàÕ-Üö>õ⎟s¦ËåLÁ8>Õµ9Fz3ƒJ®àØø‹áü£ÁV7ðj¾8Õ—Ä“DA"b_'þµú/oðËÞÑcÓ<3e”p.cP½½«‚±¿¸Óî<øŽrGc^× kÐêöꀑzÖu(¸kŽì.28Š^Î{Ÿ™?cè>#|HÒ¼o{ªý’M.u—ËÉí9ÇC_bøNæ_ ÙÛYC!híÀO®+Ú|Gá¨ušhFÙzýMxÅÅ´ö“´®Ö­mC–g™Ž…Z-.ˆöŸâ{ ÊTKƾ!ý¬!Òt/†zµ¶¼T‰c"=Ýþ•Ôü]ŸÆ1x/Pÿ„"fQò›fßïcŠü°ð¯ÀïÚ¯ãç‹á±ø‘q=¦‘i1'q8‘sŽàv”Û‹åèwPP®•Gñ#Ú?àŸß mü3öŸXÛùqÞé‘ê1Çå_µñ ¤;m‘G"¾fðÃko…¾´ð½¬`Ehøÿõ×]c{5ŒÂh›ió¬°éÆñ9¡™ÎWͱíúö«nX.%ƒ^+¨i·ZlÆ;…ã¶kÚ<=¯Á©À˜ GQWu Nøà×= Î.Ò=f éûHn~,~ßK–*J2¬êì~z¿ðóöOð?ņ:döF-#P—9¹Qµ³´wšíÿmï„þ8ñzd°7k ŠXóÐ6} {ßÀmRðÿÃ?LÕ¡6÷QŽWÐàWC¤¦îyrÄˤx§¿à™Z ¾©¯ã7ˆ`F ±ÌÌãôù–¾ÚƒáF‰ðëNŽÇÂÖimjƒQp8®@ñ-Î*ÛÜÑ1ÏJõèÞ×VµÈë ÂW§+ô=YT†2[ŸühøLŸ<<4»6 ¤‚G_ 5ë_³gÃm;áO‚bð9¼žWrIÏwÕßx—ÃSXJ×V«˜Ï_jäm.dµ”O!”óï]3Š’º7ã5Æx›ÃOa)º´”óJΜµå‘ՋõV‡S˜¹²ÐüS¢·…x=WÐuØu{`ÙÃŽ¯‡¿m«x“E²•WçiWùš÷+TŸL»I£?'që_;þج:§†ô÷O¼&LÄ×V‡-K£ÉÍs(ÕÂ8OsÞdõeøIf¯Ÿ7ô¯§kæ¿ÙdðžÈÿÓi¥})^n/ãgÔdÊØxúQ\ǨQEQE2DFÑžŒüëð?à‡~R |Z¾8ôÒ"ûs_»uüòÉÿÄñ:³*ü#³$s¬HöÖ€?¡ª*8dóaIqêó©(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ާ¦ØëuÖ‘©Â·—±<Fßuã‘J²Ÿb+Ã>êWº$ÇÁÝ~f—RðDÂyï\is|ÖrûíOݰ.Ð5ô |éñš)< âO|p°Såhì4íiPdÉ¥]8ÈŸ³ÊCîsÀ¯[-ýê–ý­¿Ä¶ûõ^­v91>ëUWMý:ýÛŸEÑLŠXæ&…Ä‘È+)È`yÔ}y'XQEQEF÷LÓu ƒQ´†èFIO65}¤õÆàqOµÓì,r,­¢·Ý×ËELþ@Wã?ü«PÕ4σÿn´ËɬØë³FÆ2Á­\àí##寘¿àŠzÖ§ñ£â ¾¡y5Î|?,Œø+wÈÜO÷¨ú@¢Š(¢Š(’ñ÷Š ð/[/‡Þ I4oZ\¡V¹hÙÖKöSÇÊ]Î@8‘‚†& ךüÉÿ‚™þÕ6¾]| ðœ†ûâÄ‹wÓ­- ËÍocu˜f¸(¹mÎ ŠÕ¤%—>[ úgö¶ý¦ü+û)ü ¿ø®ªÞêrŸ²i~í­{|êJ!#‘^Vì –*Ä?°ì£âßøž_Ûkö¥'Xø…â²/´{;•ùtÛy÷S˜Ï !ÇGã8ŒÚà›_²v§û/|žÆ!ñ§¤†ûT„~ÉJÂÚБÕâìç we…ýIûLÿÉ·üWÿ±O]ÿÒ «®Ñ>*|<ñŽõÿ†:»o{⟠ǺžŸ&kT¹Pñã2°<ŒŒâ¹ÚgþM¿â¿ýŠzïþM@ÿÁÿäÏ¢ÿ°þ¥ü¢®þ »ðæþȚω @5O^Ùk²Ž$Qæ‹i‚¿P<¹‹‘Ü îsŸðG¿ù3è¿ì?©(«éÛÞ[Xc‹/x3Ñ&Qþû²ªãäP®þÏþ=›â—ÀïüEºÿŸhz}ìþÓË4Ãð“p¯^¯‘?`ˆ®!ýŽ>%ÖKþã32ã¤W×tQEQEQEQEQEQEQEQEQEQEQEQEWÍMÿsâöÿõ^ø>hìõÌp}ݨü\v¾•®7â‚t¿ˆ~Ô|#«e"¾ *ýøfSº9Sý¤pzã ¯C.ÄÆq©ðKGþ~©ê¾î§>&“’¼wZ¯òùìvTWŽüñ¶©âmëÃþ,Ä~+ð¬ßÙúª~D»¸_T0àãç ö*çÅa¥F£§-×õGº4¥UN*H(¢Šç4 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ¥©j6Z>u«jR­½¥”O4Ò7ÝHãRÌÇØM]¯þ7\Oã S@ø!¥ÈÊþ&“íZ«¡ÃC¤Z°irG*fp#SÐò v`0¾Úªƒv[·Ù-[ûŒqy Úß§¯Buî·³ñ‹^‰¢Ô|o0šÞ7ûÖú\?%œ^Û“÷ŒG ¸Í}PÛÛÁio­¬k0ª¢"Œ*ªŒèàTÔ±¸¯mUÔµ—EÙ-ù-B—$«õ (¢¹ BŠ( Š( Š( Š( Š( Š( Š( ¿;?nŸØ}¿i84Ÿ‰ µ$ðÏÅì}6ÿsD—I ù±Ã,¨ Æñ¾^@;F+ú'E|û&~Ñ?oøûR|9ý¡ïu{ØëVrh±Ã,çUÓf°VY‹*ùm' r§ r+é ( ¾aý¨?eÿþÕ‡|ãêZO‡´M/ï¬lQ58ÑHXfb2 1Xgñ¸«'ÓÔP…ü1áÿøwMð—…,"Òô}Þ;[KXlpÃíDQèïÉêy­Ú( ¼Ãâÿů üð=Ïü]o}s¦ÚK Nšu«Þ\™Â.Ø“æ#'“Øs^ŸE5?¶×Æ-#öŸý¢¾ x“áw‡õ÷±ðýżo{¥\Zíw¾Ž@Få9G'µ~ãþÓüKð#Àözçƒ>êßµýfñtû ;J‰œ ‡äpÈ®éTl²£sÁÚáô}ù'û3þÇ?|}ñÂçöÅý³â·ÊÉ.…áèÛ̓Iÿ©i.Š`êc åX™do;§ëePEPEPEPÿÔýü¢Š( ÑExïÇWòþø’g ŠÜ<à æÝLÃóî;×K¥ö› ³pðHˆë"œ«)‚§Ðö®³SÓí5K ôÛè–{{”häÀ*èà ¤#ƒ_.Áðçã7ÖþÊø¨iÚÿ†£èÖz«Ëݪgˆ’â5HŠ>îõÜ:g½~{âS˜âpü¸ hÕ¤»£ÓÁN›\³vw:h‘|-ñ3xˆ)°k…0É!£`ÿ´N6ûâ¶gŠù¿g™F³¹odžœN2r$6‡p>ù¯œÿáø«ñ»ÃöÞ%ñ¹g£ÙÄÂæÇL±ˆÍÞ[¿Ê×Ï8ýðŽD!¢ ªyç"´ü[ñªûĵ?…ÆžtˆZ²Ç¥-ªäÃr·N"–îÕÈâX·HÃï'¹ Ÿ”ðÖt0T±*U¼ÕÛ]—žï¹ëb°³”aÉ­ž¶è}á5Yü!¤+Ù²·Îyëׇk&øQâûŸˆ¾ ´“P°¿ ëšTd–qul™Çž£ïùh8ûØ'è2Ù,´û{8ˆÙ^1Âp?•qŸüeáÏxjãÄ |¬|C s,ó(”Y˜ñë“Å~AÃùÖ# ŒUp›··{¾¦ðiÞÕ3Ì.µàŸ¼ ©h–|Wj¶Þ'ñl¢îú5Æ-ÜÛßÊ^§©bkÞÔaFz׿ÇÜe<Êq ¢”aÛ«9¥B•¡²F~£l·P02ÙÆFy¯‘-üiâÙ®÷Qð´6'RðÞ¹3>‡$’,0XßNß5¤ò9Â@Y·«ö® #fµÉx£ÂzWŠté´½VÝ.m®Ù#eX{þ]kÆá.*«•×çÂ÷GM'.JºÄäþø.o Ú^ëž ¾¿‰fÁÑQºzÕ×°ý©üH>ÃwáÏÛ7=´S\Ìr‹!ÛŸLþg¥iSšx§Ž–"-Þ÷nÏ~Ûþfð§Ë5$ÕC¸ý£|'eðÏÃþ+E{­WÄ0¤v:L ½Ô×`aáÚ*í€1ê@>Y¢|×%Y¼ªk÷þñ޵)ºÔ—(KE, $&WŠO-@ÝKI$ñ^…ðÛàO…¾^K¯}cÄW…šãQ¹ æ–v,þZ(Ù³ƒêN+×µv jî@Å}?x‡W(áðúAoæÿÈæ¡F•9µK«<'áà ?ê—þ-ñÖ­}â›Vš $½tû25² óE¼J‘oW-†ÛÆŒ×Ø(Jíè3^û;¯ŸðÝunâi©ê·`œr¯{0NŠ("½Ú¿ ¸ X:QJÏ•_N¶Üð3*Ò•i]ìØQEìœEPEPEPEPEP˵÷?µqÿM!ÿÐëÊ¿`Ý£Àz?òò?ô^§û_‚~ jøÿžèuó—ìg¯Ï¤ø;PvïÇh¯nŠÿe’>0¬¡™Bo±ú=cüM†ÏJñM{A¸Ògb´Dþ•ꉭµDä$ƒÖ·u /à1ÈÈàןN¬©³è±t)â¡Í ÏÑu©´™„ˆIŒžE{n­[j†Fù½+Æõí}&ᘠÑ1ÍPÒõYôË,LBg‘]•0ê¤yÑãaq“ÃÏ–{ÇâAªBΆ¼JöÒkÚÞuäµïz6±«nnÇ"±üOáÅÔb3Bsõ¬(Wåv‘èc°1­kLò]/W¹Óeóm؈ÁäW¸hºÕ¾¯l¸ŽEx ÄZÜf\«ºf§6™r&ÎÜò+¦µ5xžV0• òËcÒ¼QáTZîÉpã’zòibhä0²âEõ¯ѵ˜5{px ŽžµÌx«Âë*µå¢üÃ’sQ¬âùdwãð ¢öÔNWÞ$—N™mîÈÜ}+×ÿÑuK\:8¯œ¤ÑŠH»X~uÖøkÄ’éÓ-µÃ¥iZý蜹~`àý]…ñ/†¥Òäk‹uß9⹫;Élg[‹bWE} ®©mƒ‡FãÞ&ðôº\ÆxFbÒ•*×÷dk˜`mûÚ;‘áïêÂØ ­MWIƒT·1L8à×Ïö7³éó,ñ1W¶x{Ä0ê„fÄ‹YV¢âù¢và1ñ¯eSsÈu"m*èÄ똻¡iw5”Ë4 µïZÖ¯nQ†w¯Õt¹ô˃Ë…­tQ¬¦¬Ï/ sÀöx†-R%ŽFAúÖ–³£ÁªÀQ€ÝŽ x¥äÖ3,Ñ’0kÛ|=â58|·lH uï\õ¨8;£ÔÀã¡^>Î{ž=ªi]ÃA"ü™ëUì/çÓçY혀5ï:ÆmªÛ´r/ÍŽ x¦±¢\iå!<Ú•U%fyxì¾teÏOcØt~ ZÝFìH:Š“[РÕ-Ø7ö5âf¡.™x·Äê=kÝ4mfÛU·óc‘XÖ§ÈïÖÁb¡^<•7•pïÝ“Èë^£soaâk2<ÇãY^TžÇe¡Š×Äxå•ܺ|Â{v ޵í~ñ :­¸RØ‘x#Ö¼cSÓn4ˆ‚U v>µäÖ¤Ñ9R V•¨*Šñ8ðXº˜iÚ{ù¬ii¨Ûºcæ#Šðí[L¹ÓnLSƒ´ô=«Ø|=â }VBØ‘zæ¯ë:5¾§WQ¿±®ZU7iÞ+ D=¤7>{ŒuÇJêü?âIô¹V9Žb'ªisé—- ƒ ž fýMwû5R7>b§Jwì}’Zêö|a‘Ç5ä¾&ðܶ›‹eÌyÎWðç‰&ÒçJKFkØÑíukPxeqÒ¸5j}*tñ”ÿ¼|ñis5”â{s±”äŠö[µÖ­~ÍtAv港øfk Õ¸Ìlz ä­.籘M ©äWL ª.dy4kO S’¦ÌëüKá™,\ÜZ®cc\;er+Ût=n×\µû<øÝŒk‹ñ7†ßOf»¶]Ñ7_j)U·»"±¸~úŽÌw†|Q-¬‚Úäæ#À'µw:Þ‰g®Z™ Ç™Œ†áƒåäv®ïÃ>'{'[[£˜ÛSZ޼Ð/Œ—±¬r7ú}Ý„ÍÈ@Ze…ôº}È–Ü‘^ߪéšå§™70àŠñ}KMŸM™ ”`)ãÞ´¡YIrÌçÅ`çF^îÝÏhðÿˆ Õ­Ô3m”pEm^ØÅ}A2ä_;Ø^Íc:Ï m ó^ÛáÿëD8uç½rWø;ÄöòüÆ£ìç¹æ ðüúdåÑwFzW7¯o ’"CŠú:òÊèZ)Fì׊xƒÃÓi—%ãÄMtPÄ).Yfc–Ê“öØî¼1ℼEµºlH8É®¶þÂßQ·he†ùÎä‚`ðœ=kÙ¼1âH¯á[i›÷‰ÆOzƵ>h¹n=T^ήç›ëÚúLç Z"x¯ŠÿiÇeÓlÐ2œ~&¿Põ > F ޵ù¯û]h²i6öãø Ëƒí“]y}^iêxZsþ‘7ô¯§«ÎÆŸ[’»á ü‚Š(®SÕ (¢€ (¢€ þïeÜëèì?Zþý+øÕn§x¾“H?ñã@ßšÛ´ëVõ‰þ:*ífh¿F°o[xþ8+N€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ÎÖ4?^Òo4MVqe MtxäR¬?kFŠq“NèM_FxÀ}[PÓ´ýWá7ˆ¦2êþœZ$÷®4éë9¿þCŽ›yä×¾×Î_Ѿø»Ã߬Á[K"4t(ûÚmÓ“7ý{ÌCzqÒ¾‹GY:ÊÀAÈ ÷êf‘SqÄÇiïå%ñ/×Ñ£— ízOìþ]?Ëä:Š(¯(ë (¢€??à¶îøàYÿ¹â`¿÷Õ•ÁÿÙkãÏø"lÛh?Aýÿ »ß7ÖÃÿf¯µÿàµÐîýš|?÷<]l¿÷ÖŸ|öZøSþ¥6ßÚwŰÁ÷mÿ|êúÐôíEPEP'‰t ?Å~Õ|-«){bÒ{;…LW˜Ü~*Æ¿*à“ž)¾ðσ>!þËÞ0sŠ>x‚èwùOØîi(ÞQq¬Hã!þ Oë~P~Õ þ'ü ý¦ü/ûk~ÏÞ¿ñSêf=ÆZ—n÷7–lª¢â8£˜ìA8ÂÉLr ÐÛ?µÇüš¯Æ?ûõïý š¾Pÿ‚GɘèŸöÕô}}ÉñWÁÏñ‡à¿‹|gpÚSxÓA¾Ó£šâ lu g‰^H[cå7åí<`à×ß?dÏÚËöqøumðÃá×ÅŸ bÚÏ=Â}³Ã“Í6û†Þùqz¼g§÷?ÅäøÅ'¯àDš^0/Ù›ÄBäéÂ=ãÍßö@eݳ;0ÝŒñ_‰Ÿ´/ü¯öýý§|ko㯊~8ð4×¶6âÖÒKBÞÞÚí&ØÐiää³Y™˜ð À~üÚ­Â[B—n²N¨¢GQµYÀùˆ8ô©èâÙïAý¾|=¯iúgí¯øÄ>¶¶tžëJ[ÿí™fTÄLKÁ¶ s!Ø kòÓö–ÛÇ¿ðVï Íổ6¾}÷[ºC˜­`ÒX^Ý_ Äl±ûØFò¿¡«Ë»]>Òkëé’ÞÚÝId‘‚¢" ³3d“_œŸe¯…Wž-×üuá [êڜڒúGž÷^¹iZ@óI.\XÂäùQõ‡ç“sf»ðX5Rõ*;B;¿Ñy¾Ÿ{ÑõërÚ1ÖOoóô&²œÜ£.‹²þ·îÍ)AÆ6nìüzýŒ¿`ÿÚSà¿í#uñßãç4¿Ks¤ÜÙ5ÄZþ¡¨<ò´A ½å´YEDaþ°‘À>tÿ‚±éwÿjoƒ¼›ßËhQ¢ƒæx~Ýt<¦~˱by[8ÚŸ1Âà×ô!^àßÙà÷¾0ø«ãÆ£´Þ5ñs†¹¾º•®بéj$Ï’·,Ÿá Tÿ_:~Ö¿u_Úöuñ¯Â ê-O_µˆZËpXB.-n#¹‰d*‚3ĈR@9ÁÆ+èº(ðÏàwüëãV¡áýÀ?µÄe¸økᛇ»´ðž‡q3C<²ÊÒ¿Ú&hà ³¨‘ðÌã'5ûiáÿèžÐì<3á«tÍ+K…-­mmÐG 0Ä6¢"® [Pãçíÿû ~ÓŸµ¯Å=^ðg‰ü;¦øGÖKiu{ÄwR¶û™Ùa´™79 ‡û¨¼œø±ý†¿àªÌ¥?ᢠŒqâ}t~DYWïeùqÿüý‹>4þÌ~4øão>#ÒüI¨øÂDK›»»Ë‡–9%’g¸’îÞ%‹.X“œã¿Û?´Ïü›Åûõßý š½º¾.ø¿ð‹ö±ø‘iã i?ü7¦xCÄð^Ø%¬¾šk¸lo#hŠÅâ†#Ÿ`ç¨Á?àògÑØRþQTŸðV?ˆW_ì÷§|ðæë¯|VÖ,ô»+8¿×M $Òmòˆ">¾f:f¾”ýŒÿfëïÙCà¨øY©ë±øŽXõ «óu »@ Üùeä$¾¼úWÉßþüJý§l-[ö©øÙáOžøvN™àW´–ÊâVÛ¤‚eVà9—8ÿXÈ¡hô“á7m¾|.ðË2/ i6ZhaüfÖˆ·ÕŠ’}Íz PEPEPEPEPEPEPEPEPEPEPEPEPEPÎmçøqâÍ7㦑5¤ šˆ¡ŒeÓÝ€ŽçhêöÎAÎ2Pã _DÛ\[Þ[Åwk"ÍꯡܬŒ2Ár G}cg©Ù\iº„+qkwÅ,n2¯¬¤w |ÿðvúóÀÚö§ð+_™¤::}³CžC“s¤ÈØTÉêöÍû¶öÆzïý£·OñÿjÿÙéå/Ïþçê}EWvQ@Q@Q@Q@Q@Q@îîí¬-&¾½‘a··F’IáQe˜žÀ“^ð6ÒçÅ7ZïÆÍ^&KÊ#Ó‘Æ "Ø•·\ºe9‘±ÁÈ4·~#mྋ+Gyã)ˆ½‘Ío¤ÛáîŸ=Œƒ®xl‘^õcei¦Y[é¶,¶‘¤QF£ ‘ƪèÀ¯[ø8_ïTÿÒSýd¿ò_3“ã«åÍÿ’üËTQEy'XQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÕýü¢Š(¢Š(¨$LœTõäŸj=DÏ–¼£ÁßüWðþi?Ñõö˜§§•rÛ.£Qè—¹§˜+¦ñßÂß|AÓ?³|Gb— ­æFܬ‘¸èñȤ:7º‘ÅdþÐvA³Ñ¾1X@ò]ø&s%À@rúeÎíN?º6È; œ÷#×t?³kVpj0L$·¸$—£+€Àý#ü导'‹YÊúŠÖ¦«[m¹ô´q¿»UoäÏ™[á?Åí$‹O |Mº·±A…MFÒùÿ®Çc7üêOZê5 êfQ‚ì{ Ížlm°¯C^¥áϤȶ×Ç Ð5s^%ðܺ|, ˜›“\h,¬ ðGJN©JUjajYì}{em©[䕇¼O]Ð'Ò® c÷šÚð÷‹¥´u´¼;— 5é—Ùë6›xea\jR§+t=™ªx¸i£<'JÕ.4Ë•–>_z÷Z·Õ­ÔÆß8ŠñíA›Iœ•Rb5CLÕ&Ò§ÂI\ó]5iÆq¼w<Ì..xzœ•6=_ľPˆÏâQ^9=»ÚHa~a^ù¢ë0j¶áñȬoxn;èZâÄ‹ÏÏB»ƒåg~c€Ucí(žK¦jw:eÂÍô¯pѵ«m^Ømm͘WÍ °ÊÑL6•«Ú^§>™8’3òçšÞµ%5ty˜|¨¾YlzGŠ<,&Syf¿2ò@ï^O"21ŽAóÈô¯ 4mfßU·#v9Ëø«Ââe7–k†îcF·+å‘èãð©kHå<7âY´ù–Þቋ?¥zë Mbϳ«ŠùÒHž9 N6°®¿Ã~$“N”[NICZV¡z'6_˜rþî{ñ:œ+‰GZØÕt«mNÜÅ2‚{ùöÎò{ ÖhOJö¿øŠ NÎ%µË^‹ƒº=l»0Ucìç¹äÚ΋q¤Ü˜å£'å5_KÔ®4«4,vç‘^ñ©éVúœ)Gã^¬è÷]ÁGSåäà×E Šq³<¼vtgí!±íZ.³o«[F±È¨µÝßT·9_œ xž™ªÏ¥N²ÂIPsŠ÷Z·ÕmÕцür+–¥7tzØú]6ågˆð5îz.·oª[©V±È®zÔÜ4_‹†"Ÿ³©¹à—òÚÊñL»Y5¯¢ksi3¬Lg¨¯Nñ'†¡Ôbya\H9ú×\ÛKk)‚a´¯×F¢«=φž|ÑØö™à°ñ=†ôÁlp{ƒ^A©é“é·- ëòއ֮hšÔúTÊs˜ÏQ^¥=½‡‰l|Èñ¼Ç5ŒféÊÌê”c‰…×ÄxÝ•ìº|ëqÆJö¿ø†ßSˆ#7ï@äWjšmÆ›3C8ùsÁªöW²YL³BNWÒµ«IMs#“Œ –žÇ½kZ-¶­nÈëóv5áú®•>—;E(ùC^Ëáí~ R¬Ø”E\Ö4k}RF7c\tª¸;3ÜÆa!ˆ‡=3çÌîñï]W‡¼G>™:Å#nˆõÍbêzdú]ÃE:¹àÖpé·Ö½ ¯h®Ï–„çBwGÑñ½¶«k¸aÕÇó¯&ñ'†eÓåk«Q”5Sþ#›LœC9&.•ìˆöº­®FW Ý7ä}2tñtìþ#ç‹[ÉìæÂÛO"½“C×-u»_³\\Œ5Äx›Ã2ØHn-†c=k”´½šÂQ,‚5Ñ8ªŠèòiV©†©ìåª:ßøfK) öËû–çé\?C×$Wºhº­¦¹eäHA|`ƒ\‰¼2ö‹eÝä⦕k>Yãp ¯kKb_ x¢K9Öäæ3Ç=«¿Õt›]nÓzÌGÊÕàß09'»Ÿ xí$·÷~ôW£oz!‚Ç©/eWc–Ô´ÙôÉÌr­Wµ½º±‘f¶r‡={–©¥Zk–»ãÆò2¯Õ4‹*vŽ@qžjº“\²1Æ`%K÷”ö=kÞ$‡Q‰a™¶Ì:û×K}g ô-£9óµÌ¶r­Äî½§Ã~#‹S…c•‚Êr×ø»£ÖË3(Õ%MÏ6ñ‡¥Ò¥.ƒ÷MÞ°-®e³q, ôUí”ñePr+Å•½ êK•œ–\é¿i Dð׉#ÔbJx+ãÛ‡jxnÒQ÷„‹ƒøšö«[‰,¥I¢$sÚ¾ký¯µÕÕ|'m}ðëŸÖ·ÂÐåª?4ÌyðŽ=çö3¹{Ÿƒ²I×íwò+_[×È_±_ü‘[okËŸæµõíy¸Ïâ4}NEþë@¢Š+”õŠ( Š( ¿¯?à‡Ú]Ýä÷mñŠeóägÚ4%ãq'ûu~ò×à.­ÿ¾¾Óu;Í9~ ÆÿešH·|üÛ®qö3ŠýîÓí>Áamc¿Ìû.&RQµÛ˜›ýZÏÙcaó0ûä`z¯Ôööö–ñZZF°Á ªGªˆ£T€VÒô½;EÓ­´&Ý-,¬ãX¡†5Úˆˆ0Ø ¿OŒU- jÐŽËõ}ÛêþKD…B-å-d÷þ»Q\&áEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^)ñ«Áú¾­¤ØøÛÁÉÿgƒå7Öu¸¸´lrVxò1ýà:s^×Eta12£QTOǺ~MhÌëRS‹‹9_x¿Hñï…4ßhoºÓRˆH ýèÛ£Æßí#­î+ª¯š´¿ø³¿$ðûþëÂ_¦{‹#Ò;Mc–ÙVåFäßTu¯¥k|à sR§ðKUéÛÕ=¡ž«’´·Z?ëÏp¢Š+€è (¢€ (¢€ (¢€ (¢€ Ži¢·…î'qQ)gf8UU$“ÐRW€üxÕ/õK'á‡æ1j¾:œÚÉ"}ë}61¾òoû÷òzî8äWV ëUTïnï²Z·òZ™W«É"ŸÁXeñ®»âŽŠ5÷ûŽ®0bÒm…`+çÈ Œ>„pkèªÏÒt» K³Ñ´¸E½„)1¯DŽ5 ª>€V…V?«UsJËd»%¢_p°ô¹ “߯¯P¢Š+ŒØ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÖýü¢Š(¢Š(¢Š(ާck©é÷:uìK=½Ôm±¸Êº8ÚÊGpAÁ¯—þ ꯄeÖþkîºð¤ø³w9k.rÍjþþX#èSŸªä]èW‘ŸJùïâǃu˜¼K üQðeƒêΈZÖêÖ&D{Í:á—Í@_ÉHÁeäÎGÉq–S[ƒr¶ªÇXµ¿§Ìô0b¯ ìÿ?ëCӼƟ2±Ü[œÒבXüjð¾][Ô‡µVäYë¶Ÿ;gû‹8PÿT,­zlZ•‰-§IQ†AV~•ü™˜`±4ê?¬A©7Õ=Yí{6–ÚUCw÷ÇçGÚâþòþuæ¸>ÁÊËt•I¯íÐ|ÎãU%ÖlÐ}üŸ§Q¥'²)R“ÙiŽëÌì¯0ñ'ÅO øYø‡Y±Ò•‡ÉçÜ";ÿº„†o¢‚kÊÇøJCCðãÚ¿Œ%bBÍmn`±ý»™ö(÷À'Ú½¬¿†±¸–½•&סа¯yh}©ê^ahcoÝŽI¯#ÓôÿøN>8ið°—Mð5´—ÓàåWP»TÝ·,[߯^*éß þ=øê>2Öm< ¦È8µÒ³w|Tõ\H¡#>†0ßPkèŸ‡ß ü3ðçI}#ñHÏçO<ÎdžâfP­$²7,Ä× ~ÛÀÞWÂâ#ŠÅ5¦Ë}{ùXãÇf¡MÓ¤îÎþ0BxªºÜ66r]O¸Ç$*3·àª ?€«€`b™*«¦Öé_´5så‹ü]ñÅ~6¹“Æ?Sw‡¼t&pyŸSš&Ùw Ä2ȱ@d C4¥x ¹?ZøsÄ:?‰t[kB¸[»è’XeŒîFV?NÕâ~9øI¨xƒâŸ¬h—Ï¥hú”/¾–ìb’í *öÀ2ò¤üñÈãæ1±\Žö_ x?Ã>³6^Ó`Òàr£·PˆXwÀâ¼|º†2ª¼D“ƒk–ݶ= \é¸EA5Q^ÉÀQEQEQEQE|µû`ÉÕñÿ=!ÿÐëÊ?`æ à=G'¹ú¯Yý¯±ÿ OWÿ®ÿèuó샨^Ùø?PKgÚ<ìÿ㢽¼<9°Ò> 4Äû,Ê?Mn`Šî«Þµã>%ðäšd¦h1¿~» ø¢;èÅ¥Ñă¹®ÊîÒÛs £p5çÂNSé±iâióGsæÁÀÚ¿xw®³ÃÞ#›N˜G3îŒõ¦xƒÃÓiS4‘ŒÄÜ×, çŽzìÑò‰TÃÔæ>‚o°kÖGdÈü«ÇõÝ]g`¥¢sǦ)tvãJ¸ Ç1õÀÖ:ýŽxüEr^TÞ›ãöxÈ]üG‰éZœú\âh•=@¯mѵÛMR ýÅy½ Í¥JÛ0“œÖ^Ÿ6Ÿ*Ï t<Šè«F54w8°xÙá¥ìç±êž(ðÂ^)º¶_yÀï^E,2ÛÊÐIÁµîú»m«[ª³ øÁ‹â Gw\ÚŒH9úÖ4k¸>Vvcpª½¬0Ó5;2u’&;Aäâ½ÇEÖ­õ{pA±Ê×ÏòÇ$¢”me«Úf§s¥Ì³DÙò+zôTãÌ7ž|²ØôŸø]n®íçÇ W“ÉFå$áÖ¾€Ñµ«mVÝNFþ„W-⯠-Ê›Ë1†;×5Î>ìG—Æ¢ö´NWÞ%—L”[ܶèœ÷í^ ®©iÆWÎÆåmaë]w†¼I-„¢ ›(N+JÔ/ïDç˳MòTØw‰<76•)º·¢=@®jÊök–{vÚGZú MRל:8¯ ñ'†¦Ógkˆèš•:×÷d^a€iûjG£øÄê°ª» =jî·¢ÁªÛ²2þ󱯳¼žÂQsnØÚyížñ¤9Ä€t¬ªÒq|Ñ;2ütkÃÙÕÜñ½OL¸ÒækyT€ zU[+ÉlæY 8 þuï:Þ­nÊWçìkÄ5M.}2墕ptѬ¦­#ÊÇ`gB|ðØöøŽN%ŽR£±ï[¦—o©Û4R¨$ô5óíäú|âHM{W‡¼E©F H£Í^ƒƒ¼O_˜Æ¬y*ny6±£M£LQÔ˜Éàž•—©Üiw 4,Bg‘^ï©éVú¤ Ë’G¼;ZÑî4»–VS冶§UIYžf7*3öØöÍZ·Õ`WVñȨµÝ ßU_œ x–—ªÏ¥N²FÙNâ½ÇFÖ­õ[uto›¸®z´œÑêà±Ð¯J›ž¨i“é— A±=éúv§q§L³BÄ(ê=kÛuÍ Rpøàׇj:uÆ™pÐ̤(<ê£QKsÆÆà'B|ðØ÷M\·ÕíÃ!û¯zÊñ/†áÔ¡3ĸzw¯$Ó5K.áf…¾\ò+Ü´mjßU·VnÇ"¹ëStåÍÖÁc!‰‡%MϹ¶{i 2)Vô5«¢k—Lã˜óȯPñ/†ãÔ¡2Û®ÙG9¯¹¶šÚSo(ÚÈzú×d'±×sÅÄag…Ÿ46=¦âÒÃÄÖ!Ђøü«ÈõM"çI˜Ç*„±«š·6—v›÷dó^»soaâ0K¡¬9¥JVèwòÃO´ ³¼›N¸Yáb¼ô¯lðÿˆ`Õb±Ä£·zñý[F¹Ò§18Êö5NÎöæÂágºu­+QU=äq`ñSÃO‘ì{¾³£Ûj°2:㡯Õt©´™ÊJ¤.x&½Ãþ ‡S…UˆÈ­-OGµÔàhæPXŽ r´ ùOk„†*<ô÷>xÏËós]W‡|G6—0ŽfÝ vô¬ÝgG¸Ò. n¹Œž c`‘ʽ'J3Z5:/Øú=×UµÈ!ÑÅx÷‰¼?&Ÿ;O ‘½ªO ø‘´ÙVÞàå뎖š½¯8tq\Ò£.^‡Ò7OKûÇÏöói× =¹Á^¾õíF³i¯ÙùRcy ךx‡ÃÓiҳƹŒÖ …õÆ›:O qžEtN’šº<Œ."Xi{:›W‰|0ú{µÍ²–Fçµpàœíz½ßGÕìõÛQ˜/Œk‰ñ7…^صՒüñYÒ®âùdmŒËïûÚ;xcÄÏjÂÖá¿wÓ&½QÓ¬õÛ2F‘òšùüàí"»¿ ø™í\[]“8Í]j÷â,`—îêls:¾‘q¤\•e!O~ÕBÖâk9Vxyã½}wae­Z’@mÃ^-®hsé7H&3ÐÒ£ˆç÷fF?/•9{Z{£áÏ[êP¬S0YG=k¨»´‚öª5|Û ÄÖî²BØ õì>ñDw±‹[ƒ‡ ÷¬1wÍÒËó(Õ^Ê©Äø“Ãré³áRÑÒ¾2ý©¬â_ÃtÎd\þµúgsmÜM  2·Jø öÍÐŽ™àh¥ý_œŸÖ»2üG4â™âq&YÉFR†Ç¨~ÅòDí}ï.š××uòìTsðR×Óí—?ÍkëÚóñßÅ‘ôùÿe‡ QEÈzÁEPEP_Á/‹×g‹5¥ô½¹ù«ûÚ¯à¯Æë³ÆšúújCÿ"µuÞ mþ Ð[ÖÂÔÿä%®š¹/¶ÿøq½tÛ3ÿVºÚ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+Äz™â­PðÞ³c©Á%¼Ëßd‹‚AìGP{kjЍMÅ©Eê„ÒjÌð¯€úþ§&}ðóÄÒùž ð4ÿÙ·,x3[›K€8–,ry%I=kÝkç‰ÿñmþ"hàýÞ—y³D×±÷E¼ïþ‹r݇“)ÚÍ×k+èêô³H)8â!´õôi}ú¯&Žl,šN›Þ?—OëºaEW–u–ðXX|ÏØýßþyxƒMoÍf_ë_“ŸðGÙ¼¿Ûÿžº¦¿—”ßÒ¿]ÿà®ÐùŸ±¦ªÿóËXÒÛó”¯õ¯ÇOø$lÞ_훣'üõÒuEü¡Ýý(úÆ¢Š(¢Š(¢Š(¢ŠùëâŒüAâïIðáuÇ‘©YÕ—”Ò­ŸøT÷¹p‹œ¯^:¯V *ÓåZ%«od»¿ë]–¦U«(+¿ør’¢šŽ’¢ËG«AèA§WvQ@Q@Q@Q@ fTRîBªŒ’x ùÓàú·ü_â/×`µ¥ë'B ü:m«ò¯ýw˜õc¥jüy×5'Ðôÿ†¾”Ç®øîs§DËɂӯ.ôŽ,Ž9Ë:W°è›áÃÃÚLä—¿VÝ#ùÿÀ_š5袊òN°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿ×ýü¢Š(¢Š(¢Š(N)¥sO¢€9ígÃ:ˆ`{=wM·ÔmŸ¬w$«ÓѯÕ¿eß…—ÒÎïÓÚEìö }'ûâ¾¢°­…§QrÔŠ~¦ô±5!ðI£åtýœuí9HÑ>%ø† >èº{{à>¦xËΣ?þ. Ä¥Çý4Òmþk¶¾¬¢¼©ðÎ_/Š„~ävG8Ä/µø#åøP_.~]Câµú¡ê-´ë(Iú1V#ð¨‡ì¡¤^¾ïxÓÄÚØ=R]IâŒÿÀ" +ë:*épö¯’³|Cû_‘â^ýžþøI„ºG…lšqƒçܧÚçÈïæ\yúþìPÚÅoŠÖ4A…U@AVè¯^QøQÅR´æï7r0§ƒŽjJ(«lÈ)JZ)˜ö¥¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùoö½9ø%«ÿžÿèuòìÒøjþ(Á9“·û¢¾­ý¯0> jÊ;úm:uš#ó¯oÐuË}VÜnoœA¯/ñ‡%Ó&2B¥ ?¥`éú„Ö3  bžEm:j¢º8ðØŠ˜iòÏcÓüUá±p¦îÕFáÉä¯@ÅXaÁèkÞ4-nßU¶ Äo‘\çŠ|,’©¼³?qYQ¬âùdtã²õR>Ú™çzV§6•t³FÙ^â½×IÔíõK`èÀ’9óËÆÑ1ŽA‚:Ö¾«Í¤Ü‰‰¸­ªáù•ÑÅ—æ^Î\²ØôxUnU®í@ 9 w¯'–6ÌN6²×ÐÚ^«mªÛ ‚HäW#â ¬Ê×–‹óŽHÎ/–g¥˜`£íiœ§†¼K6›"ÛÜ6a<}+ØsmªZàüèâ¾q’"ŒcG^Ø®·Ã~$›M•mîX´g€}+Zø}9¢råùƒ‡îêì/‰|5&•+\[.bn¸®fÊò{–{s‚"¾…fÕ-°pè⼇Ğ›M•§·\Äj(Õ¿»!ãp/ÛQØôoxŠRV8”u«ºÞm«[²8€à׃Y]Ëc*\Û±<Šö¿ëðj…c‰üë*Ôœ4NÜ>5—²«¹ãZž—q¦LÑL¸^Þõ^Òök–X[iïZΫnQ׎¥xv§¥Ï¦Na¸SŒœê£UMYžV?*3æŽÇ±xÄ1jP,rH8>õ¯ªépêvÆ)Ø×ÏÖwsØL%…Êr+Ú¼;â(µ8DrJ+’µ ^'­—cÕh{:‡“k4úT쎹NÄtªú^©>•p³Dß!<Š÷SK·Ô 1J žÆ¼;YѦÒghäRc'ƒ]T«ªŠÒ<ìv_*3ö”ö=·FÖ-õ;uua»Н®èpê0* Á¯Òµ;&uš&%{Š÷-XƒU·Fß>9ÉV“ƒ¼OSŒ†">Φç„ê|úlíË€3ØÔš^§q¦L%„œWµëš­»)P±¯Ô,.4ˆ‚e8ìk®•U%fxØü J2çŽÇ¹èšÔ:­°e8~â²|KᨵZhT Wšò]/S¸ÒîVX[åÏ#Ö½×JÕ Õ-–HˆÜG"¹jSpw‰ëàñpÄÃ’{Ÿ?ÝÛIi)·”`¯­tñú\ê’6bnµz7‰<5¡M âQ^3qo%¤Ío2EvÓš«=Ï…©†Ÿ4Ov¸·²ñŽáƒ¸p}ëÆu}"ãH¸hÝNÌð{Uí_ŸI¸Xäba=«Ög·±ñ †Npà÷Τé;=A¨b¡§Äxe•íÅ…ÀšŒr@¯jð÷ˆaÕ TvĘç5ä:¾>‘;$™Øz£cw%Œë4 Aâ¶«IN7‰çá±sÂË–{@êšU¾§nÑL¹$pkõ­m&ãcåú׫xwÄÐjh!˜íVæ«¥[jvíŠ #ƒ\tê8JÌ÷1XHbaÏ Ï $Æ»O ø™ôùÒá³éXzÎq¤\pLdðkà’@úô'Ë4|Õ)ÎKl}$Vºµ¦Wãž"ðôºdÅãRb=ÅYðlje°™mî›1·ö¯]šmNÔ©Ã#ŽÖ¸¹'ä}©ÓÆSmn|õa¨Ï¦Ü‰­Ûó^Ù£k6ºÕ°G ¾9¼ÃÄ^—K™¥wFMbé—ói·"xXÜV󤦹‘åa±ÃÏ’{—Š<,ÖäÝZ.S98¯?ç8éŠ÷í'UµÖ­;Œ2šà|QáSjíyf3äJÊ•v¤oÀ)/mH‡Âþ&{IµÓ‡€kÓîí,õ‹]®+t5ó±V weJ×uá5«­µÑÌg€Mk^Ú€eÙÿwSc'^ðüÚDÌcˆžµÎÃ4H&€á—¥}=½¦­mµÀua^Aâ M§He€f?åJŽ#›Ý‘†\àý­#µðω£¼m®[tæ¾cý¸˜¬B:›ˆÿ“W{o+ÚÊ&ˆâ¼¯ö Õ"×~%Ç2$ŠÄûŒÖ´(Zªq91™Ÿ>p©¹ØþÅ'þ,­ ó÷qüÖ¾Á¯‘?cÂ|TýÛÛ‘úŠúî¸1¿ÄgÒdJØXQ\‡°QEQEø)¯Á¤×5íKZ?E¸Ô.f¸òLJ·ìó\¾Üÿh ã8Îk÷®¿ º[ÝÃúv›r€-µ¤0¸‚"ƒ‚:ó_ÿÁOu}oAý‹¼m¬ø{P¸Ó/­gÒJOk+Á*‡ÔmÑ€t!†C`óÈâ¿ÿà˜Ÿ¼y®þÚþÓ5ÿêZ•­ÌZ¸hno&š6+¦\¸Ê»pW#Ž´ý_ÑEQEQExÇʼnއuià/@š—µÅ?f…¹ŠÎyr†$ì.Üy®Œ.u¦¡ø uoÉÕª¡i>%øÿ\›Y‹áOóx·P}ÅÉ¡Ò-ƒq7mäÝGÔœÆwß¼¡ü8ðìzŠVfi®®¥;§»¹~dšg<³±ü†àU†Ÿ4ï‡:4¶É;êZ¾£'Ú5-Fng¼¹o¼ìyÂŒ‹ÑG©$ŸG®¼f* W¡ð­ßó>þ—M÷f4i6ý¥Mÿ/ë¨QEæAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPYÚ¾“§kÚU扫@·6Wñ<DÝ9Vê hÑN2iÝ «èÏžþ jÚ†¯µ?‚(¦Ô|.«&›<ŸzóGâ =ÚõOŽsɯ¡+¾6øgWû&ñ?ÁÐùž%ðcµÌq¯òɆ.­[w¦Jõ!‡œ×ªøWÄÚGŒ¼9§x§B—ΰÔáY¢nønªÃ³)ʰìAêæ1Ub±QûZKÊ]~Ouó]\3ån“é·§ü ¾îçAEW’u…Q@Q@W‡üwñ&§cá‹_xaöø‹Æ³.ÈŽ±$ƒý&àã±E’HèH5Ó„Ã:ÕcN=ïäµ3­UB.LÁø[Ÿˆß|Cñ’Þi–å´M?tÚÛ¿úMÊö>tÀ…nÕ ñ_GV…¼7¦x?Úo…ôhü»-.íâÈAÇÕ˜òOrI­úÓ0ε[‡Â´^‹oó~w#IÂ6{îýBŠ(®#p¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÐýü¢Š(¢Š(¢Šñ¯Ú#Çš·Âÿ?ø‡ :Ÿ‡´KëË]ãr â…Œe‡pŽý(Ù ©Å&ôþð¯à«ÅÞ1ñW|Ayâ¯ê×:Þ¯~æIî®åievcžY‰Àô€8Îb¥€8Ojþþ7§÷…Óû¿€ZqVP ÓÞ€?¿éýáFôþð¯àN$ÐARU†ê ;ÓûÂéýá_À-ýýoOï 7§÷…´P÷ö=ikø­ý~3xûàïÇÿÞø7UžÒßVÕ,ìo­VF÷V÷3,L’Ç­€ÙRFAŒWö¥@Q@Q@Q@Q@Q@Q@Q@2~ÖvÏsðcVŽ1’^Ñ«æïØ‚â ø.œ# xè+ëÿÖPøe©Û/$í<û×çOìë-Õ§Šo¬…TMÎ? ÷ð>õ Düÿ;«ìsM®—[ß@cpWŒx—@“L˜É-r=_ðߊ&µ“ì·­”ίS–MZÓiÃ+µçź2Ôú©âàí¹óÝ•äö2-Ä<×¶xÄ0jpª;(¯/×ü;q¤LÒD7Dưì¯%²™g€•Ç8­êSöŠèò0ø™ágË-¢ní"¼…¡”nÖ¼SÄ>¸ÒåyÕé¾ñ ¬ œJ?Zß¼³†ò†`a\p¨éËSè18Zx˜sGsæ0=MnèzÄÚUʰb#'‘W¼CáÙô¹ÚHFèÛ½rNÓ^•ÔÑòRŒèOSèh¦´Ö캆WJò_xvm2fž!˜ZªèZìÚLꉋ<ŠöXšÓ[³ÁÇÆ”©JýyJ8ºvàÖóió¬öìBƒÈõ¯mÐõËm^Ü+$Ç"¼ÃÄ^›J™¥„n‰åX6„Ú|é<$ŒEtT„j.hœ|ML4ý•MJñW…¼ôk«!ów¼©Ñ£>[¬½kÞtMvÛW· HßЊç|Sáe¸S{f0Ã’z•yEÙXìº#í(ž£ëéW"D?»Ï"½ÃJÕmµ[pñIŠùæDxœ¬«‚8"¶´-^M*à>~BzVøŠJJèãË3S—³žÇy⟠Ã^Y 7p;דʆ'1È6•þuô^¨Ûêp " ƒÔW⯠-›ËEÃHÏF»O–G¡˜eê¢ö´ŽSÃ>$›O”[Ü6c>½«ØZ궸á‘Å|å,O˜¥Yk®ð߉eÓæ[yÎcnçµm^Škš'60äýÔÅñ'†åÓekˆ1Ò¹›;ÙlfYàb6šú}—TµÁ!ÑÅyˆü5.™3Ol»¢5jßÝ‘YŽ_ËûêG¢ø{Ä0j±*3bQÖ¯kZ5¾«lÊËóãƒÞ¼Îò[)ÅͳmÇQ^×áÿCª@ªÄ VU¨¸¾h¸|kCÙÕÜñ½SJŸLœÅ ;sÁªÖ·sYL³Bqƒ^÷¬è°j–ì¥FüpkÄ5="ïI–EÌfºhUSVg•ŽËçB|ð=ƒÃÞ!‡S‰QØ ­mWL·ÔíÌR¨Î85óíܶS  b1^ÕáïC©Â±¹P:W5j<®ªÇÙÏsɵ}*r’垆›£jÓiW*ñœFz×¹êšU¾§nÑJ¼‘Á¯Õô‹&á£uÌ}tSªªFÇŽÀJ„½¤pÒu[}NÝe²ÄsTõÝ VÜŒaýkÆ4}b}*uxÏÉÜW¸é:­¾§É|Ǩ®Z”¥MÜõ°xØ×‡$÷<PÓ®4¹Ì3Ž;{UV¸Ò®áo”ž}ëÚuÝ ßT·`Àoìkõ-6ãMœÃ*ü£¡®º5TÖ§‰‹ÁO>hl{Æ•«Aª@²FFîâ±|IḵÄl«ÏëÊtZãIeR|¼ò+ÜôNNØJ‡$ŽErÔ¥(Kž;Î L9'¹óÝż¶²¼w# ÐWIá~].àE3~éºæ½ ľ‡QˆÜB1(çë^3sm5¬­Âíd®ÈÔXÙîx˜œ5L5Nhžõyge¯Øç²8>•âúÆ‘>“s±Ô•ìkcÃ^$—O•b™³úö¯Tº´²×-9ÃnXEÊ”µØô¥Nž.þÑàv·ÚJ'ˆí 潓Ã~$‡QŒC+ ¯0Öô)ô«†M¤Æzɵ¹’ÚQ,$£'?ZÚ¥%Qs#ËÃb*a§f} ©é–ú»G*‚Hàׇk:5Æ‘1IA1“Á¯Rð׉"Ô!Xf8Vö©¥Ûê–í G¸éÔp–§¹ŠÂÃOžŸ:õ;—­z…¼PÖ’-Û|‡€Ojæõ}çJ¸*T˜ùÁ¬<67ƒÚ½/v¢ÔùºU*aªùHÍ ¶§m†Ã«+ÅüI >•9™1koÂ^#1?ØîÛåíšô«Û+mRÐÅ ¬85çFR§+=¤œ)âá̾$x™ªO¦Ü,Ñ6Õî=kÛt^×Z¶ÚØ,G̵ä:öƒq¥\1vª^«s¦N³ÂO^EtÔ¤¦¯ÊÂb凗%S¸ñ7… fîÐH¯5 «úþuô‘«Úêö nˆù®+ÅÁ7–#¸¬èÖkÝgF;½­#;Âþ&{9­Ë|‡ŽkÖ™-õ|WÍeX6×]Mwžñ4–²-µÓ|‡¹ª¯‡Óš$å¹–¾Ê¦Áâ_ MlææÈH¯“¿h’WÁq†Èãó¯ÐÅx/¡Èã ùOö•ð±ðt÷qŽœ~u¦ùÒg7e_º•X=ýŒ1ÿ fÿO·_Íkëšù'ö5ãá¨ã…ØÿÇ…}m\XßâÈú‘ÿ²À(¢Šä=P¢Š(¢Š(¯àûâ’ìø›âåôÖ/ÇþL=x5ü"|[]Ÿ|f¾šÖ¢?òfJþÞþ¶ï…ž o]N?ù,•èç?›ÂO7®‡¦Ÿü–޽€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ æ¼cám/ÆþÕ<%¬®ë=VøÉ]Ãåuÿi ¾àWKE]:’„”¢ìХՙ⿛Ã(lø“¶•¨äó#CþªqžJÍÖ Üæ½ª¾qñßü[?‹:'Äèw¢øŸËÑ5¼p©)?è7Mþëf&cÀR=kèêô3:qrUà½Ùëèú¯“ÛÉ£› 'gN[Çòèÿ®·>ÿ‚£CæþÂß±Õ?±Ø~½žJþà—Òù?·GÃ&ìÍ«¯ýõ¤Þ þ…à¦yß°÷Åô·Ó›þøÔ­[úWó­ÿÕ›Èý·¾?­Õòÿßz}ÊÿZó£û¢Š(¢Šóo‰Ÿ,>éȶï©ëz¤ŸgÓ4Ø×^\žŠd\‚îxQî@;aèN¬Õ:jí‘R¢Œ\¥±CâÄ£à¨,ô?Úlx»]c—§©ûíüSJ‚ú»tÆG$;áÃQà{[½_\»þØñf¸ÂmSQq̲vŠ1üÇ÷QF9Àà  ¾ßøz{Ïxæá5?kŠ>Ùp?ÕÛEÕm-û±Gß¹=«Ùk¿^ ðô×Ú—ó>Ëû«§w«è—=*nRö“ù.ßð-½J(¢¼£¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+æ¯ ÅŸø§sðúoÝø[Æ-þŠz%µøù®¬ÇeWÿYàuQ’M}+^qñWÀIñÁ÷,3}S·t»ÓnÇ m}Ý  õðØþkÑË«Æ2tªüÑùv'ø]u9±4ÛJQÝVùžEyÂ_¿ÄǨÃö=oN‘ìu[SÃ[ß[²®;ûËìGpkÓ«“‡•)ºsÝS¨¥(ìŠ(¬K (¢€ ùÇáïü\¯Šz÷ÅY¿y¤h>f‡¡ç•b‡7·+ÛçÝ«ª®³ãŠõx7ûÃÍÿŠgM#Là¬÷_+K‘È&ç-Ð3Ö»¯xOLð/„ô¯èë‹M*…N0\ŽYÏûNı÷&½j?¹ÃJ§Úž‹Óí?ž‹ÿ9'ïÔQé_¯OóûŽ¢Š(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÑýü¢Š(¢Š(¯˜¿m?ù4¿‹ö-ê?ú%«éÚóÏ‹_¬þ,|/ñ_Ã;û†´ƒÅeޜӠËEö˜š1 ©BCc¾1@Â5~À| ý¦ÿcþÃzï¯xe'øƒ<ñΟçKys9o³\%æÒF ðÌ¥vÈ'Ã|Uÿ¸ý±ü=®Ýé:ƒ×\¶ÊÇyiu“2Ã(‘цGb2+žÿ‡iþÚôNgÿÀ›oþ;@ [¼QÜG$ñù±+)dÎÝÊ##¦G¯Ö¯ÛÛö”ý޾0üðG…~xq4ÿi÷PÍ#&œ,šÂÍ t{Y$ ÌÎÈ~Rëò»$gçÏøvŸí§ÿDæü ¶ÿã´ôÿm?ú'3ÿàM·ÿ 3ýþ!|&ø]ûBxgÆ4ÁªxRÈÌ'CÚV)6X¦hpwˆÜ† }®÷öþø³ð+ã'džñ_À -lt$Óà·¸-~ÅåÚ3–™`• ¦YT±RqŒþ§ûiÿÑ9Ÿÿm¿øíðí?ÛOþ‰ÌÿømÿÇhá*+îßøvŸí§ÿDæü ¶ÿã´ôÿm?ú'3ÿàM·ÿ „¨¯»áÚ¶Ÿý™ÿð&ÛÿŽÑÿÓý´ÿèœÏÿ6ßüv€>føÿ%¯Àö0iúUuüÁ~ÊŸðLÚ5þ3økÄŸ4Uð¯‡|=}üòK•1’!˜š²tíFãO¸Y£bí]5hª‹™^<4ùe±ô%Ýœ7°˜§\ƒØ×Šø‡Ã³iso‰s¯NÐjaßµu>ñšLâ&bÑ1ü/ˆ<;.™;J¼ÄOË.(µésª‘±ò¾þ¥Ï£?Ñ5‹Lðêâ¼{Ä~ŸMœË–„ÕŸ x…ìf“¶cn9¯\’ }FÔ«aÕǯIùEÉOK›í>i×÷uÀ¸Š‘×íÚ»o«ÛXyÅyˆ¼=6™3KÌgšÁ°¾ŸNg€‘ê+¦´â¥ÉÂ⧆Ÿ%M™é~'ð°˜5åšáÇ$õå2#¤…daØ×¼è:í¶¯lFð0Àú×7⟠‰ƒ^Y 8äYQ­ödvcòøÊ>Ö‘ÃhzìúLá‹/ºö¯pÓ¯àÔ­VXÈ!‡"¾suus®Ò½kwB×'Ò®†&"pGÖµ¯‡RW‰Í—f.œ¹fw^*ð¸™ZîÉpÃ’zòy"xÜÇ' ;WÒWêë,D0aÈ®ÅIÁ»µa׎orGfg—)/iHåü3â9tùÖÖv; ü+ØX[jV¸ 28¯›äG‰ü¹8u®ãÃ>%’ÉÖÒé³éWˆ¢Ÿ½ ¿1ä~Ê¡_Ä~“M™§¶RÑ¢¹kkÉì®°1V•ô^Ûkø>`W’xŸÃYHn­GÉJ†!?vAË\}Hí|7âTÔ£ÎØ”qÏzÜÕt¨5;fŽE»ùöÚââÖa4Gk^Ëá¯G¨D-æ8TWø¾hyvc±öUw<«Wѧҧhä\®x5BÖòk–â#µ}ªéPjVíŠ ìkÃum"ãI¸e`JÅmB²š³<ì~TeÏ \ð÷ˆ ÔáíûÁëZÚ®—o©Û´R¨ÜGÒ¾~²ºšÆq<,A^q^×áßÁªB#âEë\õ¨¸;Äõ2ü£É3ȵ}"àÇ ÊgƒK¤jóéщCÚ½ËUÒ­µH)TgðícFŸJ¸hÜe;WM*ª¢³<ìvT%í!±íúV­oª[¬‘‘œr*ž¹¡Ûê¨óCé^5£ëTêèÙNâ½ËIÕmõ;q,DnÇ"¹'MÁݦ L9'¹àšžŸ>›;Cp§nx5B×.4©ÔäˆÛƒô¯a×4;}Z݃¯ïàׇjuÆ›;C0ùAÀ®ªu”Õƒž|ñØúNÔ-õX›9ŠçüMáÈu( ¶ê`:÷5åú»q¥\(ݘ‰é^á§jêâhˆæ¹'MÁó#ÚÃb¡ˆ‡,·>wºµ–ÒSÀ«/ë]W†¼G.2ÛÌÄÄxç ®óľ‡Q…§„bQÒ¼fæÚ{ILRŒ<ŸzïƒU£cïBxJœëcè ›[MfÓ$ 8>†¼[[Ñ.4›†Þ¹±­¯ øžK­ÙÌg€kÔ®­,u›\6[¡®HÎTån‡©*pÅÚ;Ÿ=ÚÝIg š Šö x’-B†c¶Aë^q¯xz}*ft\Åž+Ÿ†îkYѬ§ ®º´cR<Èò0øš˜Z¼ŒúQÓ­õ+v†e#Šñ sBŸI¸?)1çƒ^áÏE¨B°Îq ®‹QÓ Ô­ÚW9à§QÁê{øœ41æ†çÎí`ààŽ†½?Â~%f+etùì ®?\Ðî4«†f>ưQÞ7!ÚkµÚ¢>nIá§f}ao©[åñ{A¸Òçc‚cÏ» x˜N‚Òñ°Ã€k·Ôtø5+fŠE#ƒ\p›¦ìÏ¡¯‡§Š‡4w<KÔçÓnX‰ žkÝ4­JÛX´WÈ$ŽEx®·¢ÜéWL1û³I£kéS«£|ö®ª´”ãÍÉÂce‡—³©±Ýx£Â±º›»÷¼µÑã}Ž0À×Ðúf§kªÛ‡Œƒ‘È®CľIÃ\ÙŒ7R+*8‡vGfa—F¢ö´Œ øš[YVÒéŽÎÙ¯-ý®«uó_{HãÄ{’U~OÓþü=ÿYÑ4oé“è¾!°·Õ4û ¶×Q$ðȆãpU°@##¨Írº7¯…þ¾‡Tðÿƒô}2òÜ“öº}¼2¡#«¢8=+àÛ¿ö«ð^—ð Ç^øy¬ÜÝxŠ{"‘ßir´IhÂD%…ÊK`ydú+ðãöøÅñ_[ý®¾éÞ ñ®·©Ù]kÇ,ZÌÑH®Ž0ÈòažÄW'Z/µ‹ÕÕûÒ¯ ß‘ÞÇõ÷EÉøÛÆ¾ø}á˯x–&ÒØFé%‘¾äQ¯ñ;žüN&°¥JS’„Û.RQM½Šþ èŸ ü<úæ®ydu‚ÒÒºâòåøŽ”rY·“\?Ã?‡Úßö¼ßþ&”¸ñ†§È Sº*Ðò¶Ðv݃ûÇx䌖¡ðóÁ^ ñ?ˆSãÅ(<­]Ñ—HÒØîHµPzÜÈ?Ö62>ïÐUê׫< NòÿÛW—wÕùoËN.£U'·Eú¿>ݽv(¢ŠñÎÀ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(æ¿ð‹â]§ÅKÝøoÄí›âr ³¶ÒøúO—!é‚$×Ò€‚2+ÄZ•â­ ÿÃzÜ"âÃR…à™tqƒƒØŽ õWŽü×õ[Ô¾øºc.½àâ±Å3u½Óþ=n© ò>3†'&½zŸíiöá£óŽÉü¶~VìÎ8þî§/Imë×ïßï=îŠ(¯ ì (¯&øÑãKïx"fЇ™â jXôÍ*!÷žöèìB?ÜsÛå­ðØyU©pÝ»V¢„\ŸCŠð¯ü\ߌú·Ždýæ…àa&¥÷Y/äíÓ¯û£8#$s_GWðëÁv?<¤ø>Àï]>²IÞY›æ–SîîY¿Wk]–"5*ZŸÃ¢ëóz¿6e†¦ãËw«þ¼¶ (¢¸€¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÒýü¢Š(¢Š(¬ø›Dð_†uox–él´Ò{Û¹Û$Eon†I’pªN'µoW̶‘#öLø·úõýÔù/â¯ø-ÕÔzíÜ^ ømÚB9ò_]²Îè ë*¤Žp ÇLžµÏÃï|iÿDËOÿÀÙ¿ +ô£áGüsÆ?eGö–µñ}•ƒAoyyi¤½»IçÛØ–.DŠ"s±¶¨óÆH'êÿø}ï?è™iÿø/øQÿ½ñ§ý-?ÿeÿ ü.·‚K™ã¶ˆò°EÉeŽ'_¢¿µßü§Æ²WÂß|LÖ<[gâÕ®¢°½¶‚Ý 6—sDò¨‰Ùßϸ/¶3œ|¸<}yÿ½ñ§ý-?ÿeÿ ?á÷¾4ÿ¢e§ÿàl¿á_”¿³/À=gö˜øÇ¢ü!Ñ58tyuQ,’]ΦE†É#ÁRí´|«¸d÷k­ý°eoöGø¬¿ µ]rAweý¥ì1v’ãÄ“ËpèÃØ‚$Òßø}ï?è™iÿø/øQÿ½ñ§ý-?ÿeÿ ü(¢€?uÿá÷¾4ÿ¢e§ÿàl¿áGü>÷ÆŸôL´ÿü —ü+ð¢Šþ•ÿfßø+Öñgâv‘ðãâ7ƒÓÃK¯Î–¶—Ö×hÒâC„YQÀ;Y°7Áí_´µü+ü $|lø~GñPiúUuQEQEWçÇÆø)‡ìßð#âf·ðŸÇëg\й6–),9žv9™Iù$\ð9ȯÐzþnü[០øÇþ Kqá¿i6šæ‘{¨B'³¾‚;›iBxy]CÅ(dl2†Ô ûÚ×þ û\J#šM~ÙOñɦ‚£þø™é_aüý¬ÿgŸÚžÛá7,õ}B5.ö -/•Wï0¶¸Xåe^îªÊ?½I«~È?²®·nm¯þøSk7C£Z@àGŠ4qøü ÿ‚ˆþË:ìOñÀ¿¿g{ë¯Yê·34ë<’I§_ÙìpÐÍ#4¨ärØÚÀ’®@?§ê+ϾxÑþ$|*ðgÄ9cX_Å.ª2&v¡½¶Žr£<àÀÍz QEQEq¾;Ò·á{ÛŸûäf¿|I=χ~$jg8{k¿•B·0ý¢ÚHOñ©˜¯ÃßÚÁàω·²*•MFF™Oc“Œþ•îäÒNðgçÜm‡iªè~Á|.ñ Ÿ‰<¦\[Ê‹x•ðsÈ@ëV¼OáˆîÐÝ[.×;×ÀŸ±oÅd¶ßà½^mÓHÄÆXöÉ=ëô×!Ç<‚+P¨{¹EXbð©KVšÊ}Ùý:W¥x_Å|‹+ÃŽÀš‹<0¯›ÛEùû^`Y¢“‘Oë]U£cÌn¦·‘ô…ͽ½üVãž$ðäÚl¦{uÌDæ·¼-â»gzßBkÑç‚Þþ€È¸UIR|§¹R…mÜȯHðŸ‰2,/^×?âK¥Î^-æ¹u‘âq"Ÿ˜¾•ß%£æéÊxZ¶>“¸´‚úÀ:°¯ñ‡n4¹šh×|MÓÚ» ø“í*-.ç3]ÅÕ¬7°42Œ«W*Ê”¬ö>’­c)Ýn|ó§êé· 4gîZ.¯mªÛ¸Ç"¼ÄzºUÉ‘´Dñì+;KÕgÓ'ÂNÜŒŠè©IT\Ñ<\6*xiòOcѼOáE¸Ýwd¸qɽyL±É˜¤Hëí^ÿ£jðj¶áÕnãÒ¹ÏxZ;´7v«‡@ïYP¬âí#·—Ƥ}µ3†ð÷ˆgÒgUæy¯l³»ƒPf…ƒ#E|Ý4O ­o(#¡®›Ãþ ŸKœF혛µi‰¢¥ïDçËsGìꯉ<(—[¯-©äóE%¼†9 ŸÊ¾Ž³¼‚þ,G õ®CÄþŽö6¹µ‘y w¬hV³å‘טåÊkÚÓ9O xžk VÎñ·DÝ¥zÙû>¡oÎWÍòÇ$äJñŠî¼+âVµqix~CК×A?z'>]˜4ýMˆüOá™,å76Ã÷GÓµq–×2ÚÎ$„íe¯£Ø[ßA´áãq^Gâo Ii)¹µŒö°ø‹û²+3Ëy_µ¤v^ñ"j'oÞùÖæ«¥Áª[¤·cé_?ÛO5¬‚X Bµì^ñ,7ð­¼í¶aúÔסÊùât`3Ö²¨y~±£]éSu%Zϲ¼šÆu¸€ô¯ õ 6 JÜÃ*ƒ‘Á¯×49ô›‚“ï[QÄ)+3ÏÇå΄¹á±ê¾ñ:œ œJ"µµM.ßS€Å(籯Ÿìïe²™g€ãš÷ë°j–ë–hê+›EÓ|Ñ=<¿Ñä™ãúÎs¤Ü”q˜Îpi4mbçIœãMœÛÜ.<Øðö¿>•pC˜š½c]ÐàÕ­Øf85â–›63E8!GC]”ª)«3ÈÆag‡©Ï ¡,ï ¿f…² r~(ðâ_Än uç½yÿ‡uù´Ë„†FýÛu¯p·¸†î,Gpa\sƒ¥+£Ú¥Zº|²Üù²X¥‚SÃi^ÕÝxKĆÖO±]𠮟ÄþŽú6¹¶\JxïŠò #šÖmŽ6º×oˆÔtë}F«œŽ xŽ»¡Üi3°`Z3ѽ+Òü5â8õÖ˜ \×G¨éöúŒ&@ ÷®*u7©îâ°°ÄÃn|é ‰#ùYy½Âž#[è —ýèâ¼ã\ÐçÒ® LyÈ5mu-¬âxÜλªF5#t|ö¬ðõ5ØúTÓ Õ-š)n x~³¢Í¤ÌÈêLYà׬øo_‡T€FÍûÅë[Zž•o©[´r®Iá§UÁÙŸA‹ÂÃxîxN¬]i ès5îzn££j&ˆ‚{Šð½oH›G¸10&6<¹ ë“iWY³ u®ºÔ”ÕÑäàñÓ¡>J›‡Ž4‹HôÛPB…óô¯Æ¿êóüEø±o<$¸Žà)ǦE}áûOüyÒô¾ƒ£Ü»™páOb+æÙ'áµßŠü]/‰.×6 âãÝ„‹§iŸ9Ÿ¸bqJu?R~Ø®Ÿá KUçhþ‚½*°t=,i"Ð6@bGã[ÕàוæÙúF_IÂŒ`úQXEPEP_¿ÇUÙñ»âúx‹Vù7-~Û~ÚŸðVgÃÞ$Õ>~Ìg ¦Èö×~&™åZd8u°‰ó*:@êÜìM»]¿5½gSñ³â jsu¨jwÝ\ÌÀ$Ó9y…ÌIà@܇ÀFßð/áÓzøsH?ù'zÍ/Ÿ²WüƒâoÂYtük‰|Yà{D‚Î+ˆaŽKN¶‰V4òÌaáG) ói8Áþ™ü3â]Æ^Ó|Yákèµ=W·ŽêÒêÝÐÊ¡‘Ôú~¾¼ÐåQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@uh¾Õ¼£ T·]ÑÙ™|’ã¹qvð[ 9Å~,üUøÙñGâeìÖ>3¾’ÚÚ:lJ`·‰ÐôhÏÌ̧¼…˜z×îµ~'ÁS><|øWucáÝM[ÿŠ÷Ѭò<, [CÂ=ð2;ã÷H6¾Ñ¹˜&Õ²á÷ ƒ¨Þ&ûKv¾_寯O8ÀV­ÝËåÑŸügMÿ ¼P?éÆSù ׯÿ°ÜÞGí}ðý|Ed¿÷Ûíþµã~%ø¹ñÅ©4Þ½röÓ‚ÛÉ…”ÿ Hö†Ç5_áOÄ=Sá/Ä¿ üNÑm⻾ð¶£m¨Ã û¼©^ÚA GÚCml`àƒXñvK0¯”¢ÒJÚú—“åòÃÓq›½ÙýÐx›Äº'ƒô+ÏøŽé,´ë2K+ö ©bpI kÂüá­oâˆí~.|E´{KB_ÃÚ4¿òìÒòáz‡*?å˜Çñ`Ÿ?fŒúíá üRÔ„v?„'Š!áƒ0™âÔLaþÕvp¢Hù"ßåá²WôR¼¯m=>ZNó’ÕöO¢óiü»|ޤ¯%î­—7ú}ý‚Š(¯ ì (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ 𚩤>™ñ—Âpµ¯n70'Þ½ÒŸ›˜©QûÈóЃ€IïÕówÆÚsáçÂ¥ŸJó¹¯(+ö fFÞ“É‚±ûŒÿg׫“R¯É®§&6tÕ6êJË¿™îZoŠt WÃVþ/µ¾ˆi6ët·.Á#2îÜÌØ ×=¹ßüOðwĦÕ[Á·†þ "u·–p¥cweÝû²pYGMØÁí‘Í~øƒâ‰uëðÿÚä´ðò\Ïso¥Äì--Ìò6"“ÈR~]Ù#œu9ûÓö»ß§xÖÃ?ꥰ—õÑf_ý’¾Ã8àˆàðUq2Új˲m-{¿ÃÔñ°Yë­^4”lžÿqú#_8èßñsþ7ßx‰¿y |:§Ù÷IuiÔ}ªAëä&#ÁèÇ ×}ñ‡Çø Àך¦˜ž~³zÉc¥À9i¯®ŽÈT×ç#¸SZ_ <ÿi~üéí£ßu7S=Ô§|Òy;œœgœ`v¯Ã~ç*Ýeî¯ý¹ýÚ|ßcÙ«ïÔPèµ§ùüŽúŠ(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ +ÅWºÖ›á_QðåÔµk[;‰líY¶‹‹”š(‹và.{f¿„ŸðWÏŽ|{áïÚwÃk«iÂ壹†ÚÐiº¦–ÀüȰ¾Õ'O.]¯ë/bý"Ñ^9ð[ãÿÂ?Úà âÏ„¾#·×-/Ÿ—V®ßÁqâH›®7 7U$s^Ç@Q@Q@Q@Q@Q@ÿÓýü¢Š(¢Š(¯¿kSñì½ñSFÑmÞîúëÚ’Å `³ÈÂ;UG%Ž8’x¯¢h à½WDøåñ‡Ã~¾ø[ øÇS°ð–¤Y®4Èn]md/÷ò€àþ 8nù¯ê“âìÿóÕ|W}ªxëþÓµ«Çin#þÔ}<³¹Ë1‚;ˆ‘I<œ Éæ¸¯øcø&/ý|;ÿ…ßü™@ÉÝz—Ž~6ü]ø›¢i>øƒâýOÄ^„¡lm¯n^hàv‚ªÇ¨^2rqÇJþœ¿áŒ?à˜¿ô ðïþSòeðÆðL_úøwÿ )¿ù2€?•ß ø»ÅñŸŠüª\hºÆžþe½Ý¬†)¢n™V\œZÐñçÄ?üQñ$þ/ø‰®]ø‡Z¹UY.ïeie*ƒ  žŠ£ °¯êKþÃþ ‹ÿ@ßÿáE7ÿ&Qÿ aÿÅÿ o‡ð¢›ÿ“(ù;¢¿¬Oøcø&/ý|;ÿ…ßü™Gü1‡üþ¾ÿŠoþL äîŠþ±?áŒ?à˜¿ô ðïþSòeðÆðL_úøwÿ )¿ù2€?šÙ×GÔµï4½&Ýîn¥×ôÒ± Ë—ìp=kû–¯~~ÍŸ±ÇÃoK⟈u¨¡»µ½:Œð«Œ$Ó· ]¤ŽõõQEQEü×x÷ƾøwÿ™½ñŸŽ5H4]L¿…®o.[d1+ø}#RÍÛ,Às_Ò3?~x3ãüTøkñ ɵkwÐ%ݺË$E‹AŽUH™p视1Ò€?g5¯ø(Gìc ÚµÝçÅ]*ePNÛQ5Ûœv nÙü+ñËöŽø‘ã¯ø*Ư |2ýŸü;{|+$¾n«{–ˆ×%ÅÝÉ R4HÐbÝæ¾[ÌMµø$÷ìM}†ÛÂWº{Ÿùi­zÌ?ïô²/é_~Ñÿ±—ÅŸØ+H—ö…ý’>!kx{H¹IµM*âUf‚9 Ʋȫ¶ ¸wŽ’C¹ÖËY@?}<á};ÁÐü£îûcm§ÛîÆï&Ö%‰3Œ íQœ é+娻ö“ƒö©ø ¤üL–Ùlµ˜e“MÕàŒz…²£HbÉ'Ë‘$PI*i$ŒŸ«¨¢Š(¢Š(´ÓÅmo%Íˈã‰K;¨$û_•?´ïÇÙ+âN‚×ú'Å?ϪÀ¤ [è‹øú×êf©du=*óNßåý®!ÝŒíó®qÆqŸZü ÿ‡\`“ñ¥xÿ©tÿòÆ·£YÓ|Ñ8±øbiòTØð] ãϼ%­Gªé,±[›w\H· ʃθ¯Õ¿…¿ðQŸÙçSÑíí¼Yã]3Nº‰B³K8]Ç×$×ç׈¿à‰>#Ò¬žëGø«§"|³¢4$ãÐýµëäÁ8þ#øjâXoµ)vDHóˆÚqÿmëÒujâ¹SùÿÁ>ZŽ —ÎþÚKäßäèÉmïÙx³'ů.GCø×šxŸö±ý’¥su¦üYðë¹êô\ŸÎ¿ž(c8ãÅ«ø½íºhþ·K]Îû x;VÌø¥-±Î>m ý¿¬>§^ü¿‘Ý[8ËëFÒ©ø?ò?lcý¯f8Èuø¡¡+ú|ükѼ9ûu~Ì*Úê4 £€Æö?ñ¯Ãyÿàœ¾Ž!,?Lö:þ—É?ðOo ùªÃÿ¿ýÛZNY«8´1x2æg÷?ò?¡)ÿmoØþòß¼8CzßGþ5å÷í[û'Å9ŸLø¯áÙöñÿ~-ÙÿÁ9¼p›æøÄ">ŸØYÿÛñVÿáÛÞÿ¢Ì?ðCÿß Â4kG¡ß_¯zŸù®ñ~Ø?³4 bøŸ¡+ú}ükÔ|;ûuþËf1¥ñKAOöšö?ñ¯Â›¿ø'?„­Ô´_Ö\Ôû|k/þíáÀÂÖ÷þÅÿîÚÖtêÍ|'¥/v³ûŸùЕÏí©û]Äc—â߇#þãÿòMsö¯ý”áœËañcÃÓ!ì·ÑŸë_ŒñÁ8<ñ,ñ”)=¿°süŸ§÷¼ÿE˜à‡ÿ»ë*tkEÝDíÄã05Õå?Ïüר?l/ÙšÞU–/Š•9ÿØÿƽ‡Cýºe)m–;N½Úö1ýkðB÷þ Ùá;VÄ_Vaê4LíõVƒþ çáye¿Å€€÷þÅÏþÞŠÚµ*ÒZÀãÂãðTeîÖsÿ#ú ¿ý´?cëø~-øsžÿoükÆõ_ÚÇöY¶¸aiñSÃóFOðßF­~;ø&÷‚qÏÆ`ý€û¾ƒÿÞðN8øÌ3ÿ`þï¬éS­¢tã1XÊòŸçþGìV“ûgþÍ:UÒÏÅ  <¶§ø×³YþÜŸ²<ð›âLJcb9 }þµüû\ÿÁ;ü/•O‹BAê4Líõ>Óþ Ýá;†Äß#×û?û|)Õ¡VdËš`¨ûªµþOüÝ¿~Öß±þ †{‹~ŽÂþ>Zóý®f¯ü,ý‡¨½ükò'þ½àŒgþ0ÿÁÿwÔrÿÁ8<¨Z/ŒÈìt,íù§N5£§)8©à*>ioëÐý¦ðÏí»û2éSùSüPÐLG¿ÛSŠõ4ý¸dI#ÉøµáÕÏPo£ÿþzßþ çá”b£â¸o¦‹ÿݵ¡aÿçðÞ|ÿ‹â Ôwþß Š¸j¯Wloƒ§î*·õOüÜïþÖß²%øûEŸÅ¯y‹Ø_ÅÏë^nk¿ÙŠ7ù~(hYÅìã_‘ðíïrás?êÿÝõZãþ Éàè“|_„žÇBÇþßšºj²Vå1ÅO'Íí-éÿ ~ßxköæý˜-1o}ñGBxɽõ¯@“öÛý'‹dŸ¼8CzßÇþ5üóŸø'¿† |VõÿîÚܵÿ‚qø2xD’ücÛû ?û~+:˜j·¿)цÍðp'µ¿ªä~Öx‹ö®ý’̆ãNø³á×Ïð­ü_ã\”?¶ìÍlâX~(hA”ÿÏìã_‘ðíïwøÎ?ðCÿß Ï¼ÿ‚søJߘ~/¬ß÷ÇþßÚ¯krœuç€æöžÒÞ‰ÿ‘ûÁ ~ÝŸ²»Â!Ô>)è°î×±ë[íûê042ü[ðç#¯Ûãÿþ}Sþ ëá†p‡â¸÷þÅÿîÚÞ‹þ Áà§@Ïñ”!=¿°sÿ·õŒ°õouº–s‚qäu/òä~ÆjŸµwì¯i;?ŠÞž<ÿ ôgúÔ:í—û4i·x>(hxî>Ûø×ãçü;{Àý¾3ƒÿpþ﬛ø'g…¡“b|[Y¯ö&?öú·Š®ÕœN “ÀB\Ê­¾Oüß½/öêý“®m×í|? ƒ¨kèÇõ¤Õ?l¯ØóT·xeøµáÌ‘Áû|ã_„ZüOÂW®‹'Æhà Üèdãÿ'««ðK/çð¾aÏý‹çÿ–ÌðÕSº‰ìQÌð³‡/=×£ÿ#õFÿö²ý–­§dƒâŸ‡æŒž ßF­iè_¶ÇìÑ¥N |Pм³×ý6?ñ¯É¦ÿ‚YxŽðçþÀÿ–ÌÝÿÁ3ü'm)~3Å ƆGþßVéVkXž\¾¥N|ê«_/øï5¿íÇû#Í»üZðò7po£Ö±5ßÛ ö>Ôíßþ.χ<ÌqþŸø×áî›ÿÉðmëíŸãdVã×û·þß Ýÿ‡YøŸø¿0ñÿRùÿå…sºbïcÔúþ´,çt~¤Kû[þËñ; ø¡ ¸ ÞÆ­užý¸ÿfm-ŽÏÄý ¢=þÚœ~µù /üßÀÉIÇh\ŽßØíù®^oø&¯…b}£ã$MôÑÿ&×G³­%ðž\j`èÎê«_'þGï’~ܲ¨¾-xtg±¿ŒZâ¼Kû[~Èwèf³øµáÏ3Ð_ÅÏë_‹ºwüÁW«ºŽ[ŸC –ÿÛñZcþ ià#ÿ5æü'Ïÿ,+ž4jÅèR¦/ Z”î~£Úïöc°¿4©à‹Øÿƽ+Ã?·Oì¹b×Qø£ ¦8 oSοo?à—¾ ·Œ¼?¡”ŽßØ%öü×;ÿ×ð¦qÿ Ž?üþM®‰Â´•œO2L ûµZù?ò?|.?moØúîß¼8Aïãÿò]sö®ý”a˜Í§|Wðìªß·ñÿ~GÙÿÁ.¼ s’OŽÄÇø° ÇþOеÿ³ðý˜ðŸ?ü°¬i¬]’;ë×ÂW޲?Rí¿lOÙ–ÒQ,?´ AÏü~Çþ5ëš'í×û)ÏKÿŠš.?½{þµø©Á1|cƒÆØnôЊÿíñ®zãþ ¿áX#ã>}4Oþí­jR­=âpáñXJþ3ûŸù¾Z‡íûêíßü8sßíñÿy«ûWþÊö²Ÿ±üVðôñÿ³}þµøä¿ðMÿ øÌ?õÿîúrÿÁ7|Çoü.qÏý@ûáSF•hí|^/]k?Ìý€ÓÿlŸÙ›L¸[‹‰ú^GÛSükÙ´ÏÛ§öN¸µV¹ø­áèdîú1ýkùÙñì!á½ Sôé£þw¦¸Ùcm8)û?䑽K ÿ·f®®´Ö°üàœ¸lÛ‡ÓÛ?¹ÿ‘ý%kÿ¶wìy}jÂ_‹vQÆÛØØþ†¾,ø¡ûuü† ´¿x·Nºf'lÉ8Æ>µøëÿ wq»Å@Ðý„óÿ‘ë·ðÇüÛÇ-¼Š×FÖU‘€Þ,xïþ¾¶£B½%~OÅ™ËÆeø·eY§åþG¸Ýükøâ~+cÅúxidÃI%„U'®O¥~²| ý¢¿d…Þ‡G‹~7,v7Ñg?¯Ï}þ‹¯êV1]jß"Ó¥c] ¥ÛžÄýµ?•oÃ.?è´/þÇÿ–5ÏŠÇÎk’Hô²Ž£B^ÖmùŸ«'öÝý‘Áø»áÏüüjoømÿÙþŠï‡?ð>?ñ¯ÊøqÆ3ÿ ¥ð?ü°¥ÿ‡ÜÑi_ü'Oÿ,+ͽϧ?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ã_%~Ü_·§ÁÍ?öhñnŸðKâ&•­x»\Ž=2Ù4ëµ–xb»`—(C•+ð¬0UŠž¢¾Wÿ‡ÜÑi_ü'Oÿ,+Åhø$Gˆþ|ñ/Å}âñ|ÞnåÓ“G6ŽöÊê'Kö¹±äÆZB6rªyøãEP_¿¿ðJ/Û'áÿ€þø“á7ÆÏXøvÓÃ÷±\è©\,!à¾g‚"ÇîÅ*y˜õ˜öéø_¥ß±güƒÄ?µß5ˆw^/ÿ„/J°¾6Œúi¾7Žˆf_ô‹}«ä\üÁ˜°à©ÈôOÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~ |jÿ‚eøKàë +þ2ëšû`›4-žRžwM'ÛØG‘È]¥s[à”üpÐõ=i~(.ýr¶þYÑMÆü }ÙûlxëŒ`ש,“7Öå Sï§å¿àr¬u'SØ©{ÝÝ_ømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü°¯,ê?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯ÅßÁ§ñn¯âÞü[]3TðÅ÷Ù¦„èF_2 =½ÂŸ·&eÉ`Œšô_øqÇý•ÿÂtÿòµ¯BtåÉ5gþz¯ÀˆTRW‰ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–‘gêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXPêïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXW˜h_ðIø£Çןü9ñÕ5CMµ{«©aðîëx¶H‘ù^göŽòü…È œñ[RÃÔš“„n–¯Éy‘:‘‹I½ÏÚÏømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü°¬K?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿò¸}kþßg xÏ@ðn¥ñ‰ã“Äiqö[¯øGGÙÌÖà1„Ÿí,‡e$®F02xR£)»A]êþåwø9¨«³öWþöCÿ¢»áÏüühÿ†ßýÿè®øsÿãÿü¢ÿ‡ÜÑi_ü'Oÿ,(ÿ‡ÜÑi_ü'Oÿ,+2ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° Õ[¿Û›öDµµšëþχæòQŸdwѳ¶Ñª3É=õ¯ãÏâ¿ÄŸ|`ø“âO‰Þ+˜Íªx’ökÉrK7Éç¢D›QeP;Wí¿ü8Îãþ‹Jÿá:ùa_†Þ?ðF¿ðׯú÷ÃÿÀmµoÞÏcr„ûÈ¡+žªØÊž„G€9 (¢€>üÿ‚mþÐvß³ÿí1¤Þø“WIð‡‰`›MÖd¸}–éF’Þg'€cSæ<…gïþ–á·ÿd?ú+¾ÿÀøÿÆ¿”¿Ùö_ñíiñv†:6¢t;8­./oõ3nn–ÒWL[âÜd•’07¯Þ-ÎÒ+õ_þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü° ÕßømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò+^ÿ‚)éžÒn5ß|vƒOÓít³Máý¨£ÿ<“ÐÉ<MxïÁÿø%v•ñŸ]ñ&Ÿá¿‹OŸ ýŸËº›@Ã\ùæAŸ+íù@<¾2Äy x®ºX Ó¥:ñ»ßEÐÆxˆFj êöGî¿ü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXW!±ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–ÃŒî?è´¯þ§ÿ–ú»ÿ ¿û!ÿÑ]ðçþÇþ4ÃoþÈôW|9ÿñÿ~QÃŒî?è´¯þ§ÿ–Äø³þÙiàíGö¯Æ'1øŽøiñ\'‡G•îŒÑ¬„ê@0®ÕÀ<õÇ})Q”åËvL梮ÏÙ?ømÿÙþŠï‡?ð>?ñ£þöCÿ¢»áÏüükò‹þgqÿE¥ð?ü°£þgqÿE¥ð?ü°¬Ê?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿÆømÿÙþŠï‡?ð>?ñ¯Ê/øqÇý•ÿÂtÿòÂøqÇý•ÿÂtÿò€?Wá·ÿd?ú+¾ÿÀøÿƾvøÉñ/öø¹ºƒüXðÞâ |š…½ìYr: ãÈ|†›WÅðã;ú-+ÿ„éÿå…ðã;ú-+ÿ„éÿå…uà±Õ°õZq’ìe^„*G’jèàîõ?nçFðÏŠô>a6•yÊU÷~GÎSÉ DkSwŠûÿàŸPëµGìåâŒúVµâŸˆº.á Y ­-®nÒ4Ô/¯SqcóÇ `ª¶1¼’¤×²ÿÃoþÈôW|9ÿñÿ~PÃÿ•?´í;â}ŸÅèôÛMVÖ!Šè&U²HWË6ÁþÜ›¼¦VRv®NNqKÿ3¸ÿ¢Ò¿øNŸþXWÃæòµgF;CÝ_-ßÍÝüÏ{½Îw¼µþ½‡êïü6ÿì‡ÿEwßøøÑÿ ¿û!ÿÑ]ðçþÇþ5ùEÿ3¸ÿ¢Ò¿øNŸþXQÿ3¸ÿ¢Ò¿øNŸþXW–u«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ãGü6ÿì‡ÿEwßøø×åü8Îãþ‹Jÿá:ùaGü8Îãþ‹Jÿá:ùa@«¿ðÛÿ²ýßà|ã_+þÒ:·ü;ö£Òšˆß¼7·{-µ«ø`Ô`ÇÝo"TóÎPè2Hó_%ÃŒî?è´¯þ§ÿ–^ïþ|–³__üo†ÚÚÝIe“ÃûFY™›Q9$œ üÍø‡á}OöJø‹iãÙóã.—â{u‹=WÃ×ëê/_.òÓs¬Ì?y 9;kõ—öXÿ‚ÅhZÂÚøCö¤²]"÷Ä:|,Ö²™ºµ@Ï=Þ ÊIû‘¨Í~3~Ð ¾ |(׿áø_ñLüO¼·r·w6ºGØ´èñÚ+“w1³Ý#òñÈž+Ñf/ØCãßíGs¡á]/û›öÍ®êA¢³À8aÆû‡ŒF ƒÃºg4ýŠx{Ä~ñn‹iâO jVÚ¾“¨F%·»´•'‚d=$BU‡¸5³_&þÉ_²do\xgÂz®£­^jl’ßÝ^NâfQÐÚ+yp „`<¬¨¢Š(¢Š(¢Š(¢Š(ÿÔýü¢Š(¢Š(¯ ý¦üe­ü=ýž>#øßÃS}ŸVÑtBæÒ\gÊ o-À=Õ°Ã=Å{•|ÅûiÿÉ¥ü[ÿ±oQÿÑ-@Åv§ªjZÖ¡q«kRÞÞÝ»K4ó9’IŽK36I$õ&“ªÜXMªAg4–Vä,“¬lbF=8A>æ³ëõÓà‡íåðKáÏìS¬~Ïþ ðL—ž&žÚþÙ"­nÞð±K‰d'ph÷ ðOÊ1Ç@Ⱥ¿w¥j–ÛÜßYÍm Úï…ä‘d_T$ÃÜUkiR ˆ§’5™cub¬ÉSŽpzý_ý»n‚ÿ´ÇÁü?øyàÉ4]SK¼†òifŠ(ÖÊ8à’#kï#œ„gü ··¸»-­bi¦”…DE,ÌO@äš’öÊ÷Nº’ËP·’Öâ#‡ŠT(ê} ¶üké/Ø÷ãG„~|~ðïÄÿè§]Ñôß9%…U^HÌѲ,Ñ«ðZ2w þ×wûz~Ñ~ý¦¾7¯þèO£i–ú|EæDŽ{¹#gc4Šœ É' ÉèÅQEQE}û(|@ñWÃÚÀzï„爐¸›X²´˜#Y`¹™#’7†R­Ð×öé_¿ÀÏù-~ÿ±ƒKÿÒ¨ëû¨ Š( Š( ¿kŸùMøÿ°ŒúŽ þŠkð¿ö‹ýÿk¿þØ>#ý¥~ëÚ.…%ÔöÒé—3ÞIÔ%4ø¬ä-¶•;\O<€ºùûÿ-øËà¿…ÿ²¯Œþû“ÿŠ£þ{à·OøG£ÇûòñUô%}b}Çý—‡Û‘=ÿÃ.üÿ¡z?ûíÿÆøeß‚½ü=ýöÿüU} ES¸–U‡þD|öeß‚¿ô/Gÿ}ÉÿÅSá—~ ž¾þû“ÿН¡¨£ë5;ƒÊ°ïì#ç¡û.|ïáèÿï·ÿâ©ßðË¿è^þû“ÿН è£ë3î +í ž¿á—~ öðô÷ÜŸüUö]ø.æ^þû“ÿН¡h£ë3î*Ãÿ">zÿ†^ø3ÇüSññÿM$ÿâ©á—¾ ““ 'ýü“ÿН¡(£ë3î5•Ð[DùìþËÿOüÀþþIÿÅRÃ/|ÎNýü“ÿН¡h¥õ™÷Êè=â|÷ÿ ½ðc·‡ãöÒOþ*“þwàÏý#ÿ¿’ñUô-þ³>ãþÌ¡ü§ÏCö]ø.:x}?ïäŸüUðËß{hÿßÉ?øªúŠkS¸¥•áÞð>zÿ†^ø5ÿ@ÿïäŸüUö^ø3ÿ@ÿ¿’ñUô-¾³>ãY]öOžÿá—¾ õÿ„}?ïäŸüUö^ø0æ_þþIÿÅWДQõ™÷–Pdùëþ{àÏm?ïäŸüU'ü2ïÁŸú'ýü“ÿН¡¨£ë3î ,¡ü§ÏðËÿqƒáøÿïäŸüU!ý—~ “ŸøGãÿ¾äÿâ«èZ(úÌû‰åxw¼žÿá—~ нý÷'ÿIÿ »ð_þ…øÿï¹?øªúŠ>³>áý•‡þD|óÿ ·ðS9>ˆý]Ïõ§Ùwà§ý ÿßOþ5ô%}f}ÄòŒ3Þùì~ËßUƒ/‡!ùy3ÿz†~ø[Â1ùZŸªûdÿ:íh¥,DÚ³f”òêwŒR#G|Ó¶šu‰ÚQEQEQEÕ­µõ´ÖW±%ŽÂ4rG"†GGee<AÁ¨©è æöÔÿ‚Z|Jøoâ]SÇß³Þ‘7Šüy#\ 2Ð4Ú–™¼äİò÷©ÿVÑîp¼:ü»Ûò–ÿÀ¾7Òµtðþ©áíFÏT¶“ZMÃÔÙCÊ¿½*(ù ý™à›ß´/Çý~Ú]wC»ð/„c‘~תjÖïo#GÁ"ÖÚP²Lä}ÖÀŒ¼àð«…ÿ |!ð{À'ÃOÙ @·[{xú±%¤vþ)$r]Ûø˜“Þ»Ú(¢Š(¢Š(¢Š(¢Š(¯Ï¯Ú;ö´ºðõå÷Ãÿ†Êðj0†ïQ• [¡Ktp oúhFðƒÃÐZó/‰Ÿ þøëE¸›â™o<p¼†í¿w5¼h 3,ˇPÉÚ{ƒ^ÖCŠÂÑÄ)⩹®Ë¿§_KœY…*³¦ãJV×Ü~ ÜÜÜÞÜKyy+Ï<ì^I$bÎìÇ%™ŽI$òI¯ÓÏØóàÿÇé üâÿëWÍ‘~ÉÞ9ñ?ƒcøƒàE[‹FIæ´Ó®\G{ö-ä@åÈXÝb>^ €sô÷ì;¢ë^Ó|i¢kö3é×¶÷v»á¸£‘rÕXƒŽzý[Œ3,=|¶¬hÍ7®º­WCä²l-Jx˜9­ÿ#îê(¢¿>è(¢Š(¢ŠùÓâü[ß‹~ø›îôÏcÃú¹þiNû)Û·Ë&Q˜ôR}\WÄ_YüAðF±àëÒu(Ïü³™~h¤ÿ€H¿ ç~ xÎ󯾲»ÖMoLi4íR6ûÉ}f|¹w{¶ýW­_÷¸hÕëuúoù¯’9)û•\z=Wëú?¼õz(¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+™ñwŒ¼3àMox³P‹N°ƒ«Èyf쨣,ì{*‚MtÕñíû2ø‡â½÷ü%>ñ ¯}mN½oôPä@Ê?t[îVÜz°éål=Zñ†&§${þž^½\eZ¦åJ7gÊŸ¿ko|DûG‡<æè^|£°;nî”õó~âü yþ& àtÿ°TYø‰âÿ¹¥mÿ¾§ŒÿJøÃÄ~Ö¼%®^xoÄ6ÆÏQ°.h‰V*ت’A"¾àý¢Ï‹|U?÷,`_ûêR¥~Ïžà0ø\ž¬0É(Ùm×U­úŸ€ÄT«„ª½n~ ÑEø)úQEQEW•üdðEߎ|qk£?“¯irG¨éS wÖ§|X'¦îPû15ê”VølD©THnÈ«MN./©Â|4ñ½§Ä_é~,¶O%îãÅÄ'ƒÌgdÑyÏ$`÷®î¾pÑâ×|l½ðÛ~ïÃß|ÍB˲C«B£íQÃÏLH3Õ†¯£ë§2ÃÆ9©üW^§ÉÝ?C,5G(Ú[­õç¸QEçEPEPEPEPEP_’ŸðPßø'<Ÿ´Øø»ðíì> [±^ZÎÂ(5x¢2tŽálVo•×j³(PÕú×E ~6ýŸ~9ü8¾¸Ó¼sàoF’ÙÌlÓØN!b2“1ȧ³#=‰¯@øEû~ÒÿõËmÁ¾Ôá‚ãµ FÚ[>Ï.÷3*¡Ç]©¹ÈûªzWö¹E|iû~Çžý¾I Z\&¯â­m’}gT °M"#†yX!ÜBËÎq»jý—EQEQEQEQEQEQEQEQEQEQEQEWŠü`øïàƒzo™­ÍöÍZe-m§BÃΓћ¨3Õ›ßhb1^Õ_$|fý’3)”匪o¯äÏÑš(¢¿>ü(¢Š(¢Š+˾3ø:ãÇ õOÊêqF.ì~ú^Z‘,%ObYvçКõ+l5yR©±Ý;ýÄU¦§³8†Þ1·ñÿ4O[àiÛ$’(è“–Tÿ€H íëç…ñEüFñ·Â™~KV˜kúRöû-ñÄ裲Å8 ö«èŠę́Ff¡ð½W£Õ“ó2ÃTr‚æÝhýPQEÀtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@:ü0ÿŠâwŒ>KòY^¿ü$:@íä]¶Û¨—°Î8QÙ³_EWÏ?b—Ã-ᯌV(L¾ ½ô(É}.÷ÜŒ¥r®3Ó×ÐPÍÄI<$ŽE ¬§!”Œ‚pEz¹—ï# JûJÏüQÑýêÏÕœ˜ouÊŸm½ô×È’Š(¯(ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€0|U­¿†¼1¬xŽ+9uÒ¬î.ÖÖ™§0FÒ£Ýñµ}Í)~>øõûhÿÁGüi'€‡æþÃÒwA¦ZÇ»å’þáÈF>üîpýÚ);kúÒ¬Í3DÑ´Q:èÖöêVšaoEæJßyß`˜÷cÉ ÈÙ[þ ðËáŸØü_ñþh|wâHöȺj5³õÃ#÷DzÈ3ÐÆxjýƒ´´µ°µ†ÆÆ¶¶·EŽ(£Pˆˆ£ ª£8p*ÅQEQEQEQEQEÿÕýü¢Š(¢Š(®+âOt‰ÿ¼Iðç^.ºw‰´û:v‡D¹£,¹þ%ÎG¸®ÖŠþZý­äÙ¾&Ø÷ÿEšþ<gù8o…ßö4èŸú] }»ÿ‚ý±¿çÏCÿÁšÿñà¿loùóÐÿðf¿üEWPòÿ‚ý±¿çÏCÿÁšÿñà¿loùóÐÿðf¿üEWPó¥û/Á$~3øsâï‡ükñ¢÷LÓ´/ÝÃ}äY\››‹™``ñ Â*¢î–-žÀw¯è¶Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ž~9ÝÜø¢}à®+%Ï‹æ/¨:4M±rÙ œF¹àäŠú i¡¶†K‹‡Å–vc…UQ’I=óÏÁ8fñžµâú’ÿ ŸbÒÆ ZE£BåLò#¡õrÅìù±Oìmþ'ðýÚËärb½ëR]wôëþ_3è+KKk Hll¢Xmí‘cŽ5TDUÐSàg8æ–ŠòÛ¾¬ë (¢Q@Q@|éoÿããì¶¿êô_‰Væhû$z½ŠáÇ ó¡Ãz³Šú.¼ㄵx â}@–=[K`2ÂîÌïU¿˜»“íW¥•ÕŠ¨éMû³\¯Êû?“³9±Q|¼ÑÝkýz£Ø(®OÀ¾-Óüyàý#Ægü{ê¶é0\çc‡B}QÁSî+¬® ´å 8IY­¼$¤”–Ì(¢Š‚‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼ÿ⇎­þx+Pñ3Çö‹¨À†ÎÜdµÅÜÇd1(ÎFqÈPOjô ùº?ø»_Zoõ¾øk!Uî—:ä‹Éô?eCUsé^†[‡Œ¦çSàŽ¯ô_7§ãÐçÄÔirÇw¢ÿ?–æß‚¾ x~/†ëáˆvkº†­$š†­$ꤿ¹ù¥uq‚¥8Ee ár1šƒá'ìÿáïƒ~$×u ^Í-޵(¶Ó€Í‰™Ž%ܧwŒŒrZ½öйç™*s|³ÝtÞûtù$âùuŽÌ(¢Šó ¢Š(¢Š(¢Š(Ê~2ø"óÇ ž üI’=KJ˜uŽöÔïö~Pû6kwá·ìþ"x'Kñm¢ùM{'„õ†â3²hˆ<‚Žç¨Áï]Í|á¡Å®ø×áwý߇þ!ùš•‡d‡U„µÄ=<äăÜ`W­†ýöT~Ôo%éö—Ýï|Ÿsާ¹QO£ÑþŸåóGÑôQEy'`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWümñ†§á¿ E¢x\çÄþ)t½-AÁYgáæ8è°¦\·@qžµì•óÃÿøº5Šó~óDð÷›£h9å]ÅíÚÿ¾ß»VTÚ½<²œTzŠñ†¾¯¢ù½ü“9qRvPŽïòêÿ®¶=oÂÐ|!à}?ÀVÐ%Îen u•,äó#ºœ‚dbY‡Lš¡à…~ øs¬_x2Ài£[h^âØù!¡ß´Æ‡;3¼ä·¦¯D¢¹eªùÓ“÷·óÖúüÍU+i¶ÞAEW1¨QEQEQEó¿ÆÏø£üEàߌ|èw¿Ùú›tÙÚ‘3¿¨Š]Œ©¯¢+™ñ§…ì¼ká-_Âzü{êÖÒÛ±ÆvRǺ¶{Šàþx¢÷Äß ì#ÖxÖt'“IÔTœ²ÝX·”Û½ÙB¹ÿz½Z¿½ÂF}`ìýÚüy¾ôrGݬ×IkóZ?Óñ=ŠŠ(¯(ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€35½ÃÄ:5öƒªGæÙê0Io2z9T«È׎~ÏÚÅûx>ãÀºì›õŸÝI£ÜÁ’(y¶”»^¸=ðkÝ«ç_žøõ£x™w¤ü@·Eá諨ڂönÞ­"n‰GµzØÞR©‡ëñ/Xï÷Æÿ4ŽLG»(Ôù?Gÿ߉ôUQ^IÖQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÖýü¢Š(¢Š(¯Œügÿý“¾ø³VðG‹ø¯Á¿¿°‡íuᯟ¼G®ü2ÔìôÝ+Ä:MÝÔî`Û]Å$ŽØœ*‚N=(ú{øÏñ÷áOìû¡ØøâζºŸ¨Ü}– 9$ß6Âûq±*“_8ÃÌbßú(Qà-Ïÿ¯"ÿ‚®|ø«ñÇá„4„Þ¸ñ.¡a®™á¶(8~Í"o;ÙF7?üÿ‡zþÙÿôJu_Îþ;@Ñÿü<Çö-ÿ¢…þÜÿñº?áæ?±oý(¿ðçÿ×óÿõý³ÿè”꿜üvøw¯íŸÿD§Uüàÿã´ýÿÃÌbßú(Qà-Ïÿ¯^ø7û]~Ïÿµû¯ ü)ñBkz•œæX–c+!wfDQÔ×òÅÿõý³ÿè”꿜üv¿O¿à•ß²ïÇÿ‚_¼Câ?о ½ðæ›w£µ¼SÜ˜Š¼¦Um£c±ÎzPïQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@R3˜àI=¨À¾<ê—Ú¥†“ð“@”Ūøêsk$‰÷­ôèÆûÙ¿ïßÈë¸ãšöý+K±Ñ4»MKˆ[ÙØÃƽ8Ô*¨ú^ðˆˆ3ñÆË¡ºÎ厡g Óí\ù³/ýw˜êã¥}^¶eû¨Ç ¾Î²ÿßîV^©÷90Þóu{íéÿp¢Š+É:Š( Š( Š( Š( >Åñ/Å eýÞŸzÇ_чE\¶Û¨W°Mʨç M}_?||²»ÑltO‹º#¼þø!Á=6“^ïc{i©Y[ê6‰í®£Ib‘yWÀeaìAÍzÙï# JûZ?ñ-þõgêÙɆ÷\©vÛÑÿ–¨µEW’u…Q@Q@Q@Q@WÈŸ¶×í;aû*ü Ôüw•?‰/Û쬿2Ë}($;¨ ˜áPd~Fp ° ®è¯ÎŸø&ÏíEñö¦øQâoüQk7Õô}m¬£6P}ž?³hdL®æËogç=0;WèµWåíÿ½Ñ~*¯ìéû*xD|MøŒfkIäfoìëK”ûñ‘V˜Åƒç7™q`îÁ|ã§Çïø*/ìÛàUø¿ñIð>¡áèå‰/-ì!¹œØùÎ?%Þ|6øSáojŸÙ²8»Õž¢še¼ åy&ûa;$ÆcP¥ÜtCƒ€ߊ+˜Xüd<"iìŃOÚeòå]<ê>V7ù{Ì¢7»Ëìãvy¯Ã¿~ÞŸ·ß¿hÍgödð¾ƒàè—š•œó½¶¢–YÒˬ²y‚ñ˜#Âå3–€s€Þº+ò_âÅø*‡Âo‡úïÄwÃ? ¯ô¿ÚÉ{x–#Q–u¶„n–@s!l6p¯£¿`_ÿ¿iOñWâ­†›§ÝÝê·vÖ)¥Ã40½²Æ›ØM4Ì_ÎóTÀaGÍ}µE~%þÙ¿·oíwû2||ƒáV‡¤ø?XÓüGWº&m/¤ºû5ÍÄ–ðÅrEÜKçoŒä¢ì9wÐþ&üsÿ‚£|ð÷Ä?ü=ðˆ¼=£Fnu£µé¸‚Ùd“l—a¶ b¨ûGÌFÐHýq¢¾JýŽ¿ko ~׿ &ñ®‡§I¡êºUÇÙ5M6I<ï³ÌWz2J<Èä^U¶©È`G>çñS⟾ øUø“ñSM+BÑãß4¬ 33©h9yˆTQÉ&€= Šüwðí‡ûkþ׺–£¨~Êô/ xÆg·]sÅrM!šE#|ƒ€ûN]#Š` œŒÍáÛçã§ÁOÚKýžÿm¯ èúY׾ζþ†ò%¡[—1G<¢i4- (ì<¦ˆ‚Yæ€?`¨¢¾ý±¿nχ²U•ž‹5›ø§ÇZ ,4;gòÛËfØ&¸—kùQ– ÎìUÀfP¹¨¯ÈoøXŸðVÏø&Šgƒüáëd®àðüñÜZXp]PÆò¸íþ’'Ï|£Ôÿàœÿ¶ÅOÚçÃÞ0Ô~$è:f–<15•¼7:jÍ\Ëp²´ªÑM$»Ljˆrý}£ñ‹Ç7~ðsË¢Gök¦¤ÁÔÉ{sò¡Á윻gŒ.;ÖÏÃ?Z|:ð^á[wóæ·B÷Sœ–¸º”ïšV'’YÉÆyµ~M~Ü?´ÏÆß…:Õ‡íðÖËAÔüáI¼/dš¼w3I&§Þ¢¿"ÿà¡¶oí3û xÇÃóxBËš—„|YâÁom¯%Ô"–Å û@œÇu eY¦2‹Ó†çýÓû.x—㯾i^6øÿc®x‚8¯ílôhn!Kk+ˆRH’ãíÊLü’áNÕáy šò޳èº(¢€ (¢€ (¢€ (¢€ òŒÞ ½ñ·‚fCo+Ä<±êZL£ª^Úñ€}el6{W«Ñ[á±¥R5!ºÔŠ´ÔââúœGÃY|DðV•âë%ò¾Ýó¢ï èvMÏ9GsÔsÞ»zùÃÃÿñk¾5jÝøâ™©éý’-R >ÙôóWq_G×Fe‡Œ*sSøeªô}>Néù£,5G(Ú[­õç¸QEÀtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Rc€9$Љ|sñ>«§øzÏÁ>“o‰¼i?öm‰a†nnN9Û Y9‚A¯Lð—†4¯øgL𦉗e¥ÀF;£–oVc–cÜ’kľƒñ+ÇÚ߯‹¡¿K·ß£øxºmaô‹¥ÿ®ò‚ðv©SÅ}^¶aû¨G ·ZËüO§ýº´õ¹É‡÷äê÷ÑzÁü¬QEy'XQEQEQEQEWÎÚ7üP´­¡ÝéŸlÆ¥l:(Ôl@Žåz¼Edcí_Dׂ~кuå¿„¬¾!èñ™5?ÞÅ«FïIoÛuºÐ–-þíz™KR¨è=¦¹~gÿ&Kås—­uöuÿ?Âç½ÑT´ÍFÏXÓm5m:A5­ìI<.:4r(eaõ»^d¢Ó³:S¾¡ERQEQEQEQEQEQEQEQEQEQEQEW–|gðmÏŽ>êšV˜JjÖ¡otéï%í¡óa*{#n}שÑ[á«Ê•HÕ†éÜŠ”Ôââögð߯Vß| ¢øÂØþÒ·W‘ü³™~I“þ eü+·¯~Å ñSÆ ¤ù,u6ÿ„‹I„w-²î%ìL2ª;15ôUtfT#N³äø^«Ñê¾íŸš2ÃTr‚æÝhýPQEÀtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ øó¯jIá뇞“Ë×üsqý™nì6äfîàÎØ¡ÎHäWº×Î? âä|LñÅÉ¿y¥i{ô- <«G fîå{6_•XsµH5êepQ”±Úú¿²¾ý_’g.)¶•5¼¿.¿×v{¯‡t7ÂÚŸáÍ?&ËL‚;xW¸HÔ(ÉîN2OsÍlÑEy³›“r“Õ)$¬‚Š(©QEQEQEQEVöÊ×R³¸Ó¯¢Y­®£h¥¹WGYO±¼àíÖ‰e®|!Ö%i/¼ uöxÏÍ6›>d³“þø%6_AWΟ?â€ø“áO‹q~ïO»a kGøE½Ón¶™»Ü3pÀW­–þò3Ã?µªÿÛïW^­˜ŸuƯmýùhϢ袊òN°¢Š(¢Š(¢Š(¢Š(¬ÊŠ]ÈUQ’OÜ×átpIÿ ý§=<øfOý¥_xÁCh+ßÙ×öd×üIáû³x“_tÑt™ÃÅqv¬d™qÈh`I@ásÖ¿4?à‡wïkâŒZ¹WšßE˜©ê x‡ÿFsW?พ(»ûWÂÆûm‚jº„«Ù¤&Þ(‰ÿty˜ÿz€>†ÿ‚BþΖø1?ÇzÓwŠ> <«m4ƒ/“›QW<>TiXÿˆnS~ ü>ðwÅ_êþ i©¬xYEŽîÑÝÑeTu‘FèÙ\a”UȬ߄^´ø}ð§ÁÞ±O.èö 1ƒ‹h<ŸrW$÷5è”ÏxKžð'…ôŸøJÉtíCµ†ÊÊÙ 2Ão9f8P9bIêI95üô|hÕ4¯ðWÝ IÖ/!‡@ð%æž’Oq"ÇqhvçSœHÎBªý§ÌC“‚N;×ô_ywm§ÚO{ †ÞÙY¸ ˆ73`kùŠý„¾øöéøùñ§âÆ=6}GE›Ï¾QĶÒA¨kW,2+ÄË“qÊ6äç•8÷GÇÞ>ýº|q{û(~ÈW¾GƒbàÐ\Àìu&XÁé_Ò%fkZ½‡‡ôký{T“ʲÓmå¹ÿ»(]Ïà šþy?à‘óØøËö…øÃû@xÂöÞÎííØq2GûírñîåeÞFv‹b ìÞ¿Xà¡?á[þÇ_u˜äòî57û"1mVE³m¾ë¬ß@M~iÿÁ:?àŸÿ³÷ÆŸÙ²ÛâƯ É­êºî§|l¦÷–,m™m•B[ÍßE)ÜA<ã8€?K´Ú—à§íñÅÿ²—‡-¤ñe¾…q&±©[Ié/o6Ëy­Vx¤ÞÎDáINÜeN>’ømð×Àÿ<§|;øq¥&‹áÝ$J-m#y$XüéZi>yYÜ–‘Ù‰f<ŸJøOöýˆ¯¿e/ˆŸüExÖÒi>$¾ŽßËÍ<ðiË<Š· ÈŸ¼`ñÁ<¦s_£——vÚ}¤÷÷²mí‘¥‘Û€¨ƒs1öf€?žŠcþþ áß ¯úN›à›Í>,žTG¡ÛJenØûNøñÜœw¯ØÛ/â·…þþÍ>?ñ?‰îcˆÝi7š}”/‚×7×°´D¨yl»øÎÔ Ç€kðGöøSñ/öµý¢~+üfðWÄ߆ºŒÏ}&¥gj·S×.fAµäŒ*ìú  ý8“þ o¢|@ñE—‰ÿiO‹¾)ø¬Úv<›[©E¥¸RrÑã|Α¾>a ÆO]ÔäðDßkÚ/Ÿˆuž7Äú¥­–ü(Óc—Í‘ê»çÙ¸ud#økçÿø)ïÄoþÐßµ_ƒ?cÿܰéWv6Ó"Qõ}Snd Û[È¿în—'Óúð·…¼;àéÞðŽ“£i0¥½¥¥ºŠaUT~§©<žkùÕý‰¬ÇÅø*ŸÄëO&‰wâ}Vã!qsö@û‰pú`w èKá·Ãß |'ð…ðãÁV‚ËDðõ¬v–ÑŒglc—r1¹Ý²îÝY‰'“_‚ð\9ìÿá2øM@ Èì5W‘‡_-¥·þL¯Šþˆëùžýªg¶7üïÃÿ töÍ×Vz Ùs½Þªøé”&hùêP ô èoTñ½¯~\üEñ´žTŠÚž Ãƒ‹{:lg¹ÁwVéöHœ‰î€õž@BžQƒÅz™e8§,EExÃñe~¯É3—&íN;¿Ë«þº´~@Áa¯ôÏü?ø7û<øcþ=­ÚïR–>3@«oŒ?½+MpIõÏ­~Êü!Ô¾|1øUàÿ‡Vþ)ÒxgH±Ó²·Öøfµ#fûü–e$žäæ¿?jíNý©à«^ø/ªÆu E:n•y ; –ÒÖÕo“|e]IY$™H`G ~Ÿîa?ú&ùYÖ?ù6¼ê•%99ÉݽNˆÅ$’ØüÉÿ‚–ëzíûiü!øá›øµ=9ÂÎI-¥YQ'ÖovÏó!#å‚8˜œñøWôc 1[–ð Ž(”*ªŒUÐ_ÌÏìcð³ÀZçü_³øi¥ ?ÁŸnµ»«;a,·±XçN…üÙÞItò¬ ³Ÿaý5TQEQEQEQEQEy7Æ_xÓÁRÿ`·•âXõ=&QÕo-Nä_¤ƒ(sÇ͓Һ?‡>5±ø‡à½+Åö+勸–.ð·l±ùG ×m_8xsþ-ÆGÁÏû½Çþf©¦öHµ(€ûdÓÌ\J;vëaÿ}‡•µÉz}¥ÿ·|Ÿs’§¹QO£ÑþŸåóGÑôQEy'XQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWƒüu×µ9t­7ᇅåòõÿÌÖQºõ¶²Qºòàû$Y¾Xc‘^ë,±ÁÍ3ˆãŒfc€ rI' ó¯Áø¥øâ­s㎠‡ì×û´ÍcËÓ-Üî”ÐÜJ z€85êå‘PrÄÍi ¼äþú¿$Î\SnÔ–ïòëþ_3Ý|= éžЬ<9£D ±Ó`KxSÑ#FOrz“Üó[Q^dæäܤõgJI+ ¢Š*FQEQEQEQEZòÒÛP´žÂö15½Ê4R#rme>ÄUš)§mP4|ÿû>]Üéz±ð¿T½ÿ/äÓÔ¿ß’ÊOÞÙÊ}DÛG²×Ðó¿Š?â„øñáß/îôß[¶‰zz(½ƒ2Ù¹õgâÕôEz™²æ¨«­¦¯óÚ_ù2+˜GhºoìéòéøQ^QÖQEQEQEQEQEQEQEQEQEQEQEQE|ûñâÞOÂ5ñzÉOàËåkÍ£,ú]áÝ®R «ŒôÚM{üRÇÒü%£®Û=*8Ám£ævÿiÛ,Þä׊ø3þ._Ækâ$Ÿ¼Ñ|$Ñ4Žê÷GúáH•‡ ¹î+èêõ±ÿº§ 2ßâ—«Ù|—ÜÛ90þü_’ô_æÿ Q^IÖQEQEQEQEQEÊxçÂZŽü!«øCTèú­»Â[(ÄeT`{Šêè«¥RP’œ]šÔ™EI4ÏøâÝCÅ>‚Û_8×ü?,šN¨¤å…Õ™ØXžþbí|ôù¸¯a¯.âÜ||†ð~ïEø•‚Nɯb¹Œú:®:³ ú.»óJqöЬ£5̼¯ºù;¯C ,Ÿ/,·Z—Þ‚Š(¯4é (¢€ (¢€ òߌ¿<ðáö¥ñ7â]ùÓôM0(fHÚY%–C¶8£E—‘¾UÎêÄ($z•üÓ~Ç_´w„¾?ÁGn~1ük™¬ïõkiì¼!hÈóEkrìZ[ŠÁX[4ß9 †Vg%XŒÿA¿þ/xàWÃýSâoÄ@éÚ’Í‘cy]žV hˆ 3;£·9$HôÊ(ùhÿ‚Q|xðÿÚZÑü[9Ó­þ$Û:ÀˆÞEþÒ–î6¶…¶T8g@Ä`62@9¢¿ðWÙ£Ç?~xSâWÃ6mkQð,—‰{ck–â[ÕšdEË?ðŒ¢‚vÈÍÑM~ÁQ@’¿à®_³:|*Òõˆ—:ž›ã;+(â¿Ò#Óæ–I.áM²y2Üìw§™"ÜÍyïì3ñ·ãoícû`xÇãgˆ×WÐ~éúÛéZO8Ó¤š$€òÃ4Û²;í'qÂ…ö^} D¹¼þѹÓíåº9âF“ ÜFkTPÁßðQÚ3Ÿg_éz…ÿ“âi·ÚV‡lŠÆId•å Ôé8–#œ’@¯†?à‰^/ø}ƒ¼{à8ïvøæîüjRZ˜ß-¤ÛÅ 1Ê$ÆÃ¶yJîÜ7Œ×îÅüÚÿÁa¡ðÿÃÿÚ[áçÄ¿jkgã¦Óâ¼»Š%"HN¸Ù÷ŒØÚYÈtä@û_û)þÔ^ýªþÛxÛÁò´zšC³bñº5ó&çˆ3 ²!䣩9\g •MÑ@ÿðYÚ3—º‹û5xrÿíõ–¥«®DŠÀ[F–ÄÚÄì@V2‹.¡œdgÔ?f¯ø(ïìið[özðÃK½sQ]CÃú5¬W‘G¥Ü0ûk'™t€Ús;>ì¹¯Ú (ùûøoûGêÿ·¿üOÀ—>ŽóJøuðê½JÖÎGÙ,‚Ý3ö«„Fd-Évä…EQ÷‹ú'ÿý£<)ðöuñ.—¨_ù>(ñÆ›}¥hvȬd–IQažPÀmAn“‰ b9À$ ûÆŠü#ÿ‚'xÓáŧƒ¼oðÿûESÇš–¥ý Öf7ÜúU¬Ç¢M»Y¥‘HÝ‘‘Ç5û¹EWóµâm+Å_ðNßÛÿ\øõâ÷RøSãÛC~¥e‘`‡W]<<«%µÂŒ#d‰w.IÀþ‰iUÔ£€ÊÃA€?þ7ÁQ¼-âïÍð÷ö4Òµo|A×¢h-f·Ó®=?Ì]¦qˆ$–HÉùFÏ,™›hÚÝ÷ü›öÕ¿g 3Rø­ñq–ëâoŠ£)*ÿÙÖ²8‘â2‚D“Ìà4ÎŽ©8foÓ}?IÒ´”h´»8lÑÎæXcXÁ'¹ MhPËŸ¶‡Á=Sö…ýšø!û*ñeÚh>ð¬)$»A{’”*vfvUKà~éRxšûWø×ª[}šObßH€¨_²èð1ò€_á3·ïXt<Ö¾®ìu7F_Wæ½·õê¾[}ç= s¯imöôÿ‚.?°GÇxŸþ '­üXø‘vtÛMª.Š®.5-^î4¶¶&0Ûpï±Âƒ€O5ý~Ó´‚ÿf„º¯Ä¯]ˆ ,–ÚlÚóRhd’ÞÙBƒ‚æ3–l*¨${õÀtͧüƒÇ>°øÍãÍ'ÅZ‰_x¶ÒÝ4”xÝÍÒBn.¯ÿxª° áˆÜÆHÅItQ@Q@Q@Q@Q@Q@y'ÆŸ_øËÁr?‡Ï—â- hõ=&AÕo-Nå_¤ƒ(AãæÉé^·Eo†ÄJ•HÕ†èέ58¸¾§ðïÆ–ü¥x¿OP„4‘ž±L§l±tpWðÍv•ó‡†â×ügÔ¼ÿ»Ð<{æêÚgd‹QŒ¶À?ë¢âP89¯£ë£1ÃÆKÓøeªô}>Néù£<5G(Ú[­õç¸QEÀtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@U{»«kY¯o%XmíѤ’G8TDf'°dÓJú <㦫¬Å¤üðäÆ-WƲ4W§ÞµÒâæîcèYv ýâÄ‘^á¤éV—g¢éP‹{+Rc^‰jT}¯ø)ksã-_[øå¬FÉ'ˆÏÙ4ˆÜa ÑíØˆø<ƒ;ƒ+¡ú&½\Éû5*û;ÿ‰ï÷mòo©É†÷›ªúíéÓïÜ(¢ŠòN°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ɾ7xFïÆ_ µ[++«ØÔ4ç_¾·–mæÅ·ÝŠìÿWSðÿÅÖž<ðV‹ã ,õ[Xæ*?‚B1"}QÃ)úWa_;|ÿŠ3Æþ5øI/Éoir5­)Oì:‰&DAýØg ¿V¯Vïp³‡X>eèì¥øò¿¼äŸ»UK¤´ù­WëøDÑEåaEPEPEPEPEPEPEPEPEPEPEPEP_:ë¿ño¾=é#_Ýé?­Æ•vz*êV€½£±þô‘î‰Gµ}^Uñ§Á·^7øw©éºQ)«Ùì¿Ód_¾—¶‡Í„¯¡b6gÑz9]XƯ$ß»/uüúüŸÈæÅA¸^;­Wõç±ê´WðçÆV¿<¢øÆÐºÈè?åœÃå–?ø—ð®Ö¸«R•9¸MY§fo ©%%³ (¢³((¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯#ø×ã-C ’/üþ!צKÒ£“wtv+û×.OO—žµë•óއÿ;ã~¡âfýæƒðìI¦Ø÷IuYÔ}®Qï b.z‘^–YJ.n­EîÁ]ùö_7eévsb¦ùT#»ÓüßÉ»ðûÁºÃïi>Ó~h´Øñƒ,§æ–CîîYÖ»(®µe9¹ÍݽY¼ ¢”VÈ(¢ŠÌ ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Ê~4ø2ïÆÞ¾²ÑÉMoNdÔtÉï%õ¡ó"Ûè[3èÆº‡>3³ø…àÆ6`"êP+ȃþYÌ¿,±ÿÀ$ ¿…vµó§Ãÿø·¿fŒ–ÏÍÞKcH´Ò%ñÕì0épÀ×Otò*À¶ê»Ì¦Bv„ óÎ1Îq^ÿ wû*Ñbðþl?øõ{üöVwVri×PG5¤Ñ˜žPÑ´l6”e<#‚Æ8¯æCö/ø5ð¿ö€ÿ‚ƒüKšóÃV7ÿ´IüA¨ÛéÒ@­bb–óìÖ‘ˆ±°²ïE ³Œ`Pï‡ü5ßì©ÿE‹Áÿø=°ÿãÕÙØ|uø'ªx3Qø¦xûA»ð¦‘2ÛÞêÑjvÏam3 · !™cY;×ÔWË_?`ÙËÅÿüa øᎋ¥ø¦ãK»þȸ²·ŠÒdÔV&k\J6€¦P¡·¥IЇö ý”¯Ù/‚ÿ|=iq}â «ûÍsMœÃy4Òùqd/0°*IVèr3@AØ~ÕŸ³©}o¦iŸ¼'wyy"C 1kvO$²ÈB¢"¬¤³1 Iâ½ñ™QK1’O@+ù”ÿ‚R|)ðuçí‰ã›i°j­à:ù´áuËä]C¨AÜ(`@‘pVÆFâF ~¯ÿÁJ><ê?þ¿‚|Ï/Ž>(Mÿþ‘ϲã u,`sŽ"R9J„t ³<ñkáoÅ!|ß ü_¤x°i†1vt«è/~ÎeÝå‰|‡}›ö6Üã;N:§ã¿_¾^Zéß¼q¢xRêö3,jº½”’ƧitY ( x×FñG‰ü!$µ™Òoíï^Þæ"Æ5Bíµf ñ€Øð¯rø{ãM?âƒ4¯i£dzŒ!Þ>ñJ¿,±ŸtpTý+æØ à§‡¾ þËÞ¶±Òá´×Ûn¿ïŒJ@25ëaÿ}‡•µyz}¥ÿ·|Ÿs’§¹QO£ÑþŸå÷º÷í9û8x[Y¼ðï‰~)x_JÕtéZ›K­fÎà•$å ¬P@"²á®ÿeOú,^ÿÁí‡ÿ¯Ãø(¯¼)ñ;þ -à?…^Òíí.u¸´K-eí£XÚk‹ëÙ^Y¦Øç[i³œ±QÉ ý²“ö"ý‘$F¾xl;tø”óè@~äg§øã¿Á/‰º¼šßè>(Ôá…®×LÔí¯'XQ•ZC23 ê c;Šõjü£ýƒboþÍ>2ø×\ÒSMðõüͧøQ…ÌWK¤Éw$ĸGgB©¸Ä˜bsÇú¹@þ½¢ø[C¿ñ/‰/bÓt­* .n®§p‘C*]äv<U“\ÃO‹Ÿ >2h—#ø[â;?i–— i-Å”ždi:¢Èc'ûÁ]OЊü³ÿ‚—|SñWÄý{Hýˆ¾M»VÖ }kÅ¡;m4Ë8ÍÊE)^›„~s)Á8‰F|ÜW?ÿFÔü߃4|ÿÇ®¿ øôóíQ?ö•~Òê…†‘asªê·1ÙÙYDóO<Î#Š(£RÎî퀪ª $œɯÿ†»ý•?è±x?ÿ¶üz¾ºµµ¾µšÊö¸·¸FŽHäPèèã ¬§ ‚<_Ìì[ðcáwíÿøq{á«ÿ‡Úúþ£o§Iµ—-ïÙìãc`%.ŠÕÙÆ0(ú(ðwƯƒ/?³¾øóAñ5ÞÒÞN™ªZÞK´rNÈdvÀïÅzm~3~Û¿ðN/„Z_Âsã/ìõ¥¿¼cà‹iuaŸq$v÷PZ:|)bbš8Õš&ˆ®Xm äõŸø%‡íAâïÚྭ |E¾}WÄþº†Ò[éN鮬®QšÚI›ø¥9˜òÁC1,XÑ/øÿÀ¿ ´aâ/ˆ~!Óü3¥Vw©ÝEgšà•O2fUÜ@8ÉÁ¯%ÿ†»ý•?è±x?ÿ¶üz¹ÏÛü#oû&üR¿ñžm©ZZè7ÆÝ.cY/e…¡µ‘7¶EšEÚë†SÈ ×åoüWöVøKñ3àß‹~#|Yðv›âv½Ö……‡ö•²\yQYÀ#E¼yʶ:”ç  ×_øk¿ÙSþ‹ƒÿð{aÿǫݴmgHñ‘e¯ø~ö KLÔ¡ŽâÖêÚEš á•CG$r!*èÊAV‚E~D~Þ?ðMÿ øóá·'ì±ðóKÒ|aeªÆ.Í Ó£—N’D¦O1’6)(ˆ¯VÁl“_¬^ 𵇼 ø'J²ðý…®ŸÆ?uiœº¢€<ŽÚÓö[µžKkŸ‹þŠhX££k–!•”à‚ Ù¢µt/ÚcörñF¡“Ὴ~Õ/§`±ÛÛkVRÌì{,k)b~‚¿bÄžI5úûr~Â_²ý§ìÝãxKÁ¶>ñ…téµ+KÍ5M°g·Ì2F§Ëu” œ®A ©À?Y¨¯Ë/ø$GÄïüEý—.¬u«js-½¥”O4Ò7 ‘Æ¥™°šðŸºu÷ˆ_XøÓâZ-CÆ.¦Ê'ûÖÚL‘¢™±ÍM ‰#VSÀò#¨5Êx×â>hÃÄ_üE§øcJiVw©ÝEg•Á*‚I™Wq HÉÁô ºŠò?|~øñZø}ñÃþ%ÕŒm(´ÓuK[»ƒcsùqH͵r2q^¹@åž9øåð[ᆧ‹ñ#Çš…µ ˜EÄVÚ¦¥mg3ÂX¨‘RiŠV€ÆAu> ñׂ¾"è‹â_kö$ÒÞ%¼Ónb»·2GÃ(’&eÊž£9ÕWÎßÿâñ‚¾.Åò[é÷_Øú«Ø5]Ï÷a›kv­}wöý›ü/¬ÞxwÄŸü/¥jºt­Í¥Ö³g ðJ‡ ’Fò†VSÁ+­×m<+ñ›á•íž‹¨ÛjÚ'‰¬\ZÞÚÊ—î~îhäBÊá\2+»-ÄFhÊ Ñú=àa‰¦å£¿OU±è´WŠü#øƒ¥ðŽßÄž1»‹NŸÃ°Ïk¬Íu"Æ–Ói¹K‰&v!PmO1‰ rj?þÓ³§‹µ»? xW⇆5_P*ÚÎÏX³žâi;cŽ9K1Àè5†+*U%J[§béTSŠ’ê{u…â_xoÁš߉ü_ªÚèš=‚‡¸¼½™-íáRÁA’Y ª‚Ä “Ô^C§~Õ?³&±¨Zé:OÅŸ ^ß^ÊA:Ý”’Ë,¬e%™˜€$àV‡½Ñ^IãOŸ~ë?ðŽ|Bø…áÿ ê¾RÍöMKTµ´ŸÊ|….i¶¶0q\—ü5ßì©ÿE‹Áÿø=°ÿãÔôEó¿ü5ßì©ÿE‹Áÿø=°ÿãÕîº6µ¤xH²ñ‡ïaÔ´ÍJî-n­¤Y žT2IˆJº2U ƒ‘@tQEQEQEW—øëãwÁ¿…÷öúWÄŸh~½»‹Î† SQ¶²–H·Þ‰3¡eÜÈÈ"¬xãÂOŠs^[ü3ñ¦âÉtåF¹]+P·½hBB.ûC8Î3ƒŽ”éQ^=ãÚà/Ãív_ xóâ7‡|;¬@¨òYê:­­­Â,Š E,ŠÀ2A#‘Í{ à¶µOìŪN¶ÚwÅÏÜLä5ÛìO`¾vOá^éoqÜÝZȳC2‡GBYXd2‘Ár  ¨¢Š(¢Š(¢Š(¢Šù×á—üP¿(Œ%ÏÆ]VîÄK'>\é—k(ÿrâñ[=Ê{Wéíýñ|5ý¾'kÉ/•q{¥¶•ɪºÙü¾ê²³qÐ)=«Ÿÿ‚ox <ûü9³(ãY´—X™ºo:ŒÏðTf OˆZ>¡yj®ZOevIZ;¢×}vgÖ¿eÿà˜ÿáýŒ¼çGåÝø“íZÔücwÛ&o%¿tŠ€>£øÿãñð¯à>"‰<¹|=¢_Þ@zfâ8X£ݤڣÜ×ó½ÿÊÔÿiÿ‡ñÏÄOßãø“e¯Ïm¦É}6­m§}–kyž%Iœ<ÅÌlÄ>P'8ý?ÿ‚º|A ýuÅ.ËŸj¶Z€~o.77²í‹p­þö;רÁ4þÿ½ýŒþÛÍ—w¯C>µ9Æ7ÿhLÒÂßøå€>Iÿ‚üMøýûBþÖßþ"üfžçH_ØG¢?‡¡–hôí>öK‚›‘”˵˜3œ’ÎN@Úí-|â¿>~Ô^ýž¾ø&ÞóÆ¿/¡¯\ií±·/¹ÅÕæšiLbiv’¤( œ85÷•=ðM8³?à¢_t~žM·ˆ“õÇ\¶_ë^ëðžCûiþß>'øï7úgÃQ3Ãùù ¹ÔÁ|N½›çó' 9[çµ~bxãµðö³ý¨Ï„㙼Kã9|GáÝ$À¤¼w¦·…×o!Ö“ËÇ>fÊþ‹d/Ùú×öiý›ô†­ cìï}¬H¸>n¥t¡§ù‡ÞáaCÝ#Züªÿ‚'kg‡¼-ñÏÄ:åÒYiÚjè·77œ$Pàò;ʪ 'Ò½Sö&ðþ©û_~Õ7ýº¼qjãúÃèþ¶œqJ ž3 –Çy •ùû6øÛÆÚ·Ã¯~ÊŸ bvñGÆÝkF±’Q‘i¶b᧠ìÒ)Œ K¸`Šþ·> |(ðÏÀï…¾øSáöéž´Kur´Ò}é§p8ß4…¤nÛ˜ãŠôúþw¿à«×—_kƒÿ³¾™!}±ZÂBÿË;zõalQ16} D5üé|+aûCÿÁbüCâ¯øúÓ<}¨MÇ(#ÐíÆ›©éƒså¸=ÉÏzþ‰,ìí´û8,,£ÛÛF±F‹ÀTAµT{1^Uñ¯Áš‹|×^ùÿ„ÿXÐî.ïÓH–ú[Kkq¥C#\NʃÊ/($Æ2GÖž*øáûb|jý¶~üø§áÙ¾ØÛêkréZeûH×ö6ûç•îníä)4e-¥Œ*íPK c‡ümmôÏxßÄzì çø©WNÓïî¼ö#íwg»²MŸE÷ú›û`|hø)û+é6´O<1k®øÞ=º6·ËQ–)Yžh£•š8QÝÎÉ ÕÅ*ôÜm&¬¥ªô»_ á$ô¾Çڵ㟾4xcö|øGâ?‹^,`Öš±x Ýµî®Ÿä‚Ý?Ú–B«ŸácÀ5èÞÕ¯5ï 鿣bÚ]Ö£gos5£¶ö·’hÕÚ"Ø]Å *Nqœ üSý¥u—ý¾?m/þÈ^¾|9ørój^(¸¶|y×6øIÕ[‘˜‹­¢6>Y%à€+Ïdý€¾x£Tøqã¿Ú›âꛟˆ_ ºE!­´™Ô˜Qû‹9Úê àD°Œ_8ÿÁ µ=ö?ôv?ê¤Ð§Qëæ Ôoýkìˆÿà”²¤1¬17ˆÒ4UWY˜WÀŸðE›¨tŒ?ü$’»O¶‘F~òÙÝ<[¿8~tû“ñÿÇãá_Àÿ|Eyrø{D¿¼€ôÍÄp±…G»IµG¹¯É?ø"7€MŸÃï‰n#Ëë:®“ ·]¶™äÁôct¹÷_jú+þ éñx7öAÔ<;».|iªØijù¼¸ÜÞÈG¶-·ûØï^©ÿ×øÿ ÷ö3ø{k4~]Ö»o6³1Æ7ÿhÌÓBßøb…} ûFkºw†gÿ‰:þ¬Wì–>Õ¤pÝ’a1ܱÂÜœWäüëAž |\ñ;nòo®ô{4þîëXî¤oÇ­tðUOÚ’ßÄe§ìoðrC¯ø³Å–Ñk1Xþù¢U•Z °ÿ¯š`ŒëÕQv°ýç¡¿±7ìêfÙë@øk¨¤×¤2jİœ£ßÝ]C‰! üAï@*ÿÁb> Â+û(Çá$Äþ4Ö¬¬ÙÁ6ö»¯¾‚Hbýá_+üøµûnþÌ߱ޗ‚¾ÚÏáÍ7L¹×WÄw:¤o´½g¾û\š|R¬øHd_”B , eÁ`õ«ï‰þ~Ïš›îÌ"@‹ÈûN¹v–±ø€·Èï_¾qx?ÉàäðÖ1Üè+`4Æ´•CÅ%˜‹É1:ž ˜þR:@Á-eñþ¿û3?Ä¿‰:íþ¿«xß[Ôužþy&d‚7¡#HDóa‘‚¨æàcú)©ê6z>u«j2m,¢yæsÑ#K3 ×ų?í)ðÏâÅ/þÎÿ<'àÿ…1$ ©ÙÉØI3ÊPÃonˆ§˜&ÚáÈ}…€Ã]íññ|4ý~'ø%ò®.ô§Òà á¼ÝQ–È÷Q1n:c=¨ð7öý¢tÿþÑÿ¿ix'Äþ0}f+öðéëöKVð]I%ô‘,cdlªrr q_ZüIý¤þ5ÁOôíGö}ý™<,žðœOm?ˆu=föçh›¡GŠ-ì‘yˆ„BWb «¸7ÑÿðFÏ ~ËÚ'0×.¦GÇÞ¶²TµAøJ“ƾ1´Ùû1ÿÁbM†›;Añž¦±Ó÷qKˆí•‚mVùÁ ((ößö]ýž|5û/üÑþørc|Ö…î/¯Y<¶½¾ŸYŠäí*"äíEPI “ô%PEPEPEPEPEPEPE•®kZw‡4kíX˜Ac§C$óH†8Ô³sÀïU¹4–âm%vxOÆYæñ׈´?š\ŒY#PÖÝ ZM³Œ¡#n$ö95ô4CkvÖѬP¡aUT`@JðŸZ.¥}cª|Xñ4&-oÇ­ÒÆÜ›m=Û8¦#ùÛ¦Kr2+ÞëÓÌä¡Ë†ŽÐßÎOâ¢òG.7z¯¯åÓüþaEW”u…Q@ûK|zð×ìÕðo_øµâT#LŒGghcÞ^ÌvÁžq¹¹bÚ›m~8~Æÿ³_Š?o?j?µ·í{wq¯h-s%¾‰¤³´V—&a DR vp?Ȩ„$dcµ¼È?à´ßum{Æ¿ ¾è„ÊLO¬Kn‡&k›ÉZÎÐê¢9€ÿ~¿qþ|8Ò>|.ð¯Ã BÙøgN·²R?å£Ä€I)ÿjGÜì{’M~_~Þ¿ðMß |Aøy¢ßþËŸôÍ#ÆZ]ôqK·Óa¹ÓÞ7´q4‰ Œ«Ÿœ‚Ã'<~ ø3Cо |$Ðü;{sžàÚÚ[‡;bŽßN¶TyžŠ2ÄŸ­z=~lÿÁW~']|9ý5½7N›È»ñ­õ®„¬ÍåM¾âàg†½›×ùÝ¡jß?à¬ß´Ö¡£jzîƒð3Á·OelÝâo åÞ×!˜0†0á2WçýAø«ÿõý•õÿ‚ú×<5ð÷JÐo¡Óæv§k]B ¨ã&èŸ:` ë#°qzæ¼çþ -ðއ_²…‡‹§Œ Sâäú¬Í™mãcmm{®ØÌƒþºûã÷­>üñïŽïjhz¡t£8-"@þZêïµG¹ùKÿ]ø×âø Æ¿|Kz÷¶¾ ’ÊëI2±y"¶½óVX“ÄQ¼JÈ;tÀ ÿ·3øFßöIø¥ã=:ÛR´¶Ð¯Mº\Ʋ黎 ´‘7¶EšEÚ놡¿.à‡žºK_Šþ<™1m3ézl ޝži†}ƒÅù×Ó_ðX_KáÿÙnÏÀÖ,^óÇí•—’¿yàµÝvØñ,PŒz° ÈÏø'®©ðËöàø_m¬~ê?é·2/lÁ¨i³Ë×.©_ÔÏĈ>øSà=wâ?.…–‰áëI.î¤êvF8TÛ‹Õ˜€95üý~Ð~_Ùãöôý–-á+kÂ\ò/MÖ—O§Nsß÷[I=ó_PþÙ^"×?l_ÚOÃ_°—ÛÉ"ð·,z¿Ž/íÎV$‡k7 ‚bVPÈ7FÀâ§Äsâ?íqûOh¾ ñ|oi{ñWS³‡Lƒ;…®Ÿqvl­£@z$a$ Ø2½“ýŽx+Á>øuá}?ÁžÒ­ômK‰a·µ¶cF3…,z³Xä’I5üò|ðæ‡ñWþ Õ¨'†ìãƒÂß ¤»†ÒÚ!û˜-<9hºM¨®N#p{“ž§5ý"Pòóÿ\ð¶‹âoÚÇX¶ð.™mgq០[êZëÛı™§k‰$ieØé w‚í–#8¿c¿à˜¾"ÿ„ö$øtîÛ¦Ó“P±“Ûì÷óªû÷²¿4< ?íñcöìø¶«ö¨lü;ªèz|‡‘'”ì¥?àd}:µéßðNŸÚCÂÿÿàŸ>:ñÿ‹ä‚õûØí­wm{©®­í¤¶=器ƒµC9àã¿à§?|EàïøÃöjøav¯Ž­àñ&¾°¾ÎX%’â# O êT*à‰0w?àŠß<=uáÿ|v×4¸nõ(õô}*âhÕÞÔAšé¡, V“ÏKp¥s‚Àü‰ñŸáˆ´OØÿUý¬¾2Îïñ;㯈íM°l«[èòG5ÆÕS÷RuH…„B£a_·¿ðM‡ÿð¯c?‡¶ÓEåÝë¶ókSœcöŒÍ4-ÿ€æ!øWN*³¨Ôäîì—Ý¢ü2¥TÒÛüõ>¼øƒÿ¢ø^¸ñÕ¾¥áë[)î/íîâI ’Þ2¸xäX¹äWóÇÿhø/á¿|Lñ¿Åïé^Gám-ôµ¸ŒJ^ÞÈò¢Þ$†8B«uQ&G<×ø(ÇÄøW_±·ÄF)<»^É4hFp\êr­´€}!yè xwüáÿü"?²<>(š=³øÓX¾Ô†0ÀVÉèÝØ¼Ozæ5<¶oØ—\ý¥¿oˆŸ?h/\Ûü8ÐͬE¬ïåÇ«µ¼Ix(ÛþÎ o3Œ©ftt°¯‰?iï‚ÿ |Gÿ(ð7ìõðöƒi&‡cªYØB"Ž`ìoîäuyþÉ RO$(ÍOUüéþÃñà§?~5¿ïì4&Öõ IO ,Ò:Ísëöiîš÷ø(ÿìáχ^ÔÿfO…ñÛøŸûv;+¨´+6Pln ™šYãˆm ±Æ<Â>]ØÏ5û/àß Xx'ƒ4¡‹-ÆÛO€cŠÖ%‰8ÿuEt”PEs^2ñw‡üá-gÇ>+¹û‹áû9ïïgØòyVöÈd‘öFÛ ¤áA' ×Âðõ_Øwþ‡ÉÿðO©ò5~ˆÑ_ßðõ_Øwþ‡ÉÿðO©ò5ðõ_Øwþ‡ÉÿðO©ò5~ˆÑ\Ç‚¼cáÿˆ^Ñ|wá;“y¢ø‚Î û)ŠþÎúd…öÅk ÿ,îuëÕ…²=DPÄÙô5ý YÙÛiöpXYF!·¶b€¨ƒj¨öb€,×óÁÿ­µðÝïÄ…:ƒ¥[ÿÂY¨A%ÔñF«sq²[ÁgŽgã”&âvò9þ‡ëùÙøâá¡?à°~ðBÿ¥iÞ »ÒíÈê†-"ÕîPú5¤Œúž;Šý.¼ÿ‚l~É:÷ý?Ášç,íõ]: GÕtâÖ—¦xãTk1W°,LŠà“ó8¯ËŸÙgâÅØöç—ö?ñ>¿q­xVÔãÒíàžBñFué·p!ÈŠI<ØÖdL),À‚QHþ«ù·ý¡…¯¿à±~Ò4ô}sÃv»ØGä¼î¨ÚOm§Ò€?¤Š(¢€ (¢€ (¢€ (¬ø£CðG†5oxžéltÒ{ÛÉßîź$cë…Rp94È|^ñÇÂïøSÔ~0k¶Z†ï"’Òyo¦X–A201ÆÌò2瀱ǿ7¾ÁQÿe/ x>ÇÁ~0ñeÜ×#IeôzeãÃqk•·”/Ì‹hÃ&î9ñWÀÿ x³þ ¡ûSë_¾/4ñ|*ðC)‡IYY#Xæ-ö[$*xyB.¥S¸íÚ †oé§íû8ü9¹2|8Ó¼=¦i^ø“£Oáø-’]?\·‰ŸJ»AüŽÈ àrN},•XOŸ÷—¬SÑvº¿­’9«µ*Ÿ/¿þ õßÂß >=ø^Oü#ñ>"Ñá­dš$–#ê‹!ã™Õ‚ºœ2Ž®Çž1ðŸ€|?wâ¿ëšb¡§¼½™-àŒ¹Ü’NêId×Ì?±ìã}û'~Ïöß xÞéì´=1-ÔÑÁ5ËFŒÁ@’HܰÎÕ8ž5òïÃßø(wì‡ñOÆšGÃßxåµ {]˜[ÙÛ¶™¨À%”‚Bù“[$kÞ`;WÔþ?ñ]‡< â?ê /i·šŒáº­!iœl)¯ã£à.“¨ü4øËðâÍéòìüGâ+[¤ÀÚ¢ MUmg í€ÃÚ€?´Zù×ã§í_ð öl¸Òlþ2ø¥tqe’Ò%µ¹¼‘Ò¡Ü¥¬R².X\Ç gÑâÿxÀžÕ¼iâ»ÄÓômÖ[Ë»‰>ìpÀ¥Ý½O€9'ÍÿµĈ?´Äø¾8ø‚ÞH-e´ 9Sò– Žq‚ z—Â?‡ÚoÂ…Þøk¤F‘ÚøkLµ±´1…y«;åØõ$’y5ùÿ¸Oxá'Á[5S«xÛÅЛc€\,5«mú½ìÊ€?D~~Ò?¿i#S×> kç^³ÑçKk§6·6†9]w¨ÛsLA^àïš÷*üÿ‚,ÞMáïünøkxÇͱ›M•Tõ m%ݼÇä¥}Ïÿý¥µO‚ íü ðí¤¸ø—ñ.S£èp[ü× ˆŽk”QÎõÞ±ÅßÍu#!Z€: þ MûøoÄ÷þÔ¾ ÕtÛ©,¦ŽßKÔ®Wí9–9!¶t“æƒv$WÙW~%Ò,|/7ŒnÞHô»{6¿‘Ì2y‹n‘ù¬|¾nà£îmßž1ž+ù{ÿ‚V|‹]ý´îeÕÖ-B†Öšé•~xí$[(Y ëóJÒFØ 9ýQPÀÁPb9îÖÂO%˸b]W2'B‹L–ÏÆs^Ïñ³öÀýž¿gkÍNøÁâ†Ðnµëvº³‹ì7—.ð¡ Y…¼2äã´“ž88üVÿ‚zhGÇ/ø(§ÄÏbÖ94ç\Ö¬ˆPQgÔ¯+cŽ™òe•þòç¯#ôãìSâÚ#öáÑþ,üW´´¼øSá= {[6›{ßÝBÏ0Šhâ/:fgÉìa!¨¶ÿ‡¦þÃôQ_ÿÚ·ÿ"QÿMý†?袿þ µoþD¯Íø+6¤ø«ö…ø;û;øM´Ó¦û2ŽÎâQ>¹x–±¡XÀè-ÔØ7½~~Ûÿ²¿ÅÏÙkSøoðŸÂ¶^)Ó[L:("ÞÙÓì×$¸žMÑ|ÀrÃwNIõ׿Š~øÓàM3â_Ã{öÕ<9¬yße¹h&¶2y¼.á#"0ÉQœdd`ÖǼqá†þ¿ñ¯5{m CÒÓ̹»ºGjNÉêÌHUQ–f ($\ìåðÒ_ƒ¿¼ðÊê5ŽóÃÚ5µØB~Ø# rAf.Aµøåñ×ÄZçüöé¶ý”týNk?…§ž}\[5ÌÚ~#¼•ˆãwœâÒ9 ¹¤– €~þο¶ßƒ¿jˆ:φ¾øO] èÖ²HÞ&¼¶ò4ù®’DU·Œd¶çG.»Ê¾å¼Çöÿ‚j_?h«Ù»Â_ ®¼sâJÞÖ[c©‘š[ Ì"U’ûÅÆ}=A|1áø+Ãöðž“£ép¬¶¶È#Š(`*¨ÿõ“Éæ¿þ?Êd~}4ý5~´|ø»ñ›âeþ­kñOàíïÃ,b‰í¦ºÕ-µvîÌ`E(PI=s_Aꚦ™¢i×:ƵyŸagK=ÅÄ‹1Fƒ,îîBªÉ$àUúüƒøÿû!þÑ¿´U§üIûGüLOƒôÔ®<;áÏ ¡KYcµYÖæøËÎB©eo1†X+ÅÒ€?@¾ ~Ó?h]KÅ:wÂ-wû|xBh ¾ž8eKr×"C†GU©òœn\Ž2 ïUø5ÿ8þï‹íÜÝh£òKºýå ¾Iø§ûuþÉßïßGñ¿Ä]=u(Éi`%Ô§‡U‘lÒo)½¤+_[WæGìYÿáð§ìÇ­ëž5ñÕ݇üO|êº}ËYíM>KùK+>%‘ŽA‚mR6@=[áüköBøµâ+_ xwÇ c¬_È"¶ƒSµ¸±;*¤Ó ‡s©pÌN&¾’ø¿ã/ø À÷>$ø{à©þ kPË G¤ÛÝÅe$©#…wLAóG=~Ái¾ü;ðµï߈žÓ­tërj·¿fE…¯"¶É®©€^&©|n!À$…\~Ü~ÎZ·ˆ5ïÙóឹⶑõ­CÃ:=ÅëJI‘®%³‰¤g-ÎöbKgœ“šüýñ§ü»â'ÂÏø[À_>_øJ÷ώŸ^·›1Ë2ÂÒ »›¡ šûcö›ý¢öhðULx»HkÁ¢Ú°ÿéÆ*ýùeVR¬2„P‡üý£>~Ò^>/øK®&©%RêÙÁŠòÎFÜBß2ƒ†å£0¯q¯ÀÛ#¿ðO¯ÚwÂ?µ—ÁH—…¼gu-¶½£@»-ZAµî!UP·Qî’5Æ#š2ãª?y<5â#ÅÞÒ¼W N.´½jÖ ÛI‡I ¹ŒI÷•  ª(¢€ (¢€ (¢€ (¢€?ÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠLŠZ)2) Æ)\vE&áI¸SbEFdQÖ°¦ñ6Ÿ×ÙdÝêíþuÏSN*ò‘¤)JZ$t4TI*H×FiÂ@{ÚRÕÊÇÑPOq¼fGΧù‰oâ}:æ³¢ÈÔ…Çó¬ªbiÁ¥'cXaç$ÜVÇEE1]X:]¶æ[˜Ž¢¨ÞjYFe›$LgùŠ£câ @í„:‘ýàò&°–.š—#–¦ªŒšºFåÐÀŒŠ 3[ÜÎ謋ýjÓN]³ýÐó"¤±Õmµß`?ÚÇô&¹–6—7/6¦žÂ|¼ÖÐÓ¢šX 7 érFC¨¬ ïØXMäL®[ý¸ýH­[k¸n£ÅÐý?¥a ]9IÅKTk*II­ TRdS^EKž‚·rKs;¢¹Óâm<\‹]²n=ð¸þu»É"†^†°¥‹§7hJæ“£(üH–ŠLÔ1ÛFd“8ŸäV³¨£»3QobÅÎÚøŸN»”ÃȤx(úo$Šã+QK ü.åΔ£ñ"J)»†3Š£}©[éñù³‚G ÆR*åR1Wl˜ÁÉÙV†¿e¨!WR?¼þDÖÖáǽM*Ш¯qÕ§(;MXu™±µ vËMÇž“ÙqýH¢¥xA^NçJRvŠ6¨ªz½ô~d9Ðã?¡«»Æ3Òª#%Í¡2ƒ‹å{Ž¢£ó±.|GckqögY z€¸þu±¦¯7b¡FRÒ(ߢ«Ãsè$85>áW ŠJéÓNÌZ) Åæ¬V¾Tý­ÿkO~ÉÇŒGâȾøR?Ý›ÿ.“b5K†"2ßy ö‡Ùª.é7ª €WË²ßÆ­öéøS«xßÇ¿tø<=e¯Ïk¥Új+¤&[XÑ’é’h¶$ƒÍ)òç6+î þx>%|xýºôoÛ‚OÙÁà¼Ôlíà¾}Kg†òÚ;Ç2D-˜Ÿ³Æç8˜.r¤=ãö¾ÿ‚™~Ê^ Ÿã$ßô‰^Ñ$ˆê6sh––’$rȱ«²C ;G¹‚¾ÉÕ”Œ•ðØXÂýÿ‚šüLøß'ïì4&Ö¯¬å?0 <£O³\úý•ßý“_gÁW?hßøöx×> A©Ãsã?}šÙ,"‘Z{[1*M-Äè2Q#1¦pYŸ+­€®?cïÚ>Ïö§ø¤üTŽÀiZƒË-–£hº8¯m±æylI&7VW\òrFOþÜ·o†?d½"ËÃÚ-Šx›â&¿m;KÜ|¸c'bÜ]l;öápÒ@*aÿÏøaªüýŽìo|iåÅï‰.¢J=µ´±¢FeSÈ&B1düÀý‰l5Û_þ â/ÚÇVæóHðÓË­Ç Ãtp²°·Òm±ÐT Õ¡$䓼<ð‡þ oñkÃqüAñÇÆëO†:ò ›/Zh¶ó­º¸"ºb£?ÞV3²ôo›*<çöxÿ‚„üaмoãÙ»öž´·¾ø‹á¡u•}i‹ûFú ¶òE ¤l0–)4Jr»ˆ'öÊ¿šøE·Œÿàµ,tÅY£¶ñ oÚ7úf’D¤ãº¼-ŸB+l<£‘”ÕÒjëº"¢n-EÙŸÐ'Â_7ÃÏØè×òyÚœ»ï5+‚rf½¸>dîÍß vƒýÕù]ñCöùøÓñÛã\¿³ì§Ú]]Z´‰â›ÄYíâH˜$·Ý ÛÄÇl‰!”#ŒåKý ÿCøí}ðOö[Õlü?tmuïκ«¡ÃÇ èÏw"÷¸FŒ0åZE#‘\—üwà‡Â¯Ù®Ïâ&¡h‹âOˆíý£,Ä~ñtô%,¡û¥s7åç Áˆ¯*µ%R{·p§MB*+d|ïûLøWþ 5û2|*¸øåíÿ \zLVÍ4›XRÝ'•aY"Y#‘%A#¨aåÆ@9Á×é§ìs㿈ßÿfŸ|Cø±4W&ñœ—“É+n“Èm›Ë_”ƒË'IÀ¯ õÍCñ>“s ø—N¶Õ´ËÅÙ=­Ü)<¨ áã2°ÈjÆ™¦iº.k£èÖ‘XXXÄ[ÛÛÆ±C 1¨TŽ4@QTª ijð?ö÷ý£lŸ€¿´æŸðÃá_Å9.¬ügom}¦éßÙ:fë&¾»šÖ+O2Ky@ Ck³n9äq“ûoð›Ã^<ð—€t½âo‹_Æþ&…Y¯uVµ‚ÈM#±m©º¢* !Œ2Ü“_Ï/Åï‰^ñoüÊßÄõËmÂÕm 7w¶_Bµó‚Ï-~¬ z·Ö¿k?á¼?c¿ú+Zþõ¨ò?âOÇÏÛ·Eý·ŸöGðWƆÔÖ§cm óèšRù^ÛEw# ð,zWˆOüOü14š>¤ É3Ù‚Lž¢DÚùèI8¯V¯ïp±ŸXhý«îw_r9#îUqé-~kÓñ=šŠ(¯(ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ù»Äñv>0Ùx2?ÞøoÀM£©÷Iõ7Ú[žÄD¹‘Ç#8R3ŠôÿŠ^;‡áׂ¯¼Iåý¦ðm‚ÆØrדD rw1çíöª_| 7€|Ÿ©ÉöoP‘ïõ[ƒÉšúäBœ"ŸEëàÿsJX—»÷cëÕü“ûÚ}:ÞüÕ.›¿Ñ|ÿ$z}Q^AØQEQE|ûj~Ç<ý¯ Òü5ÅSàßiÞ\òi)¢­÷Ú/ã2s%ÇÚíß9¬X*-É#o¿~ÍŸ ¼eð;á6“ð¿Æ^4::¥®Ÿ{ýœšiƒN‚(â·µ1¤³oò‚Hϸ‚é“ï4PÁß·ì5£þÙºo…QüMÿ†«áy®Lw‚Çíþm½Ò¦øY<û||ñ£+n8ù†>l±|á?áÿ¼;à=$î²ðæi¦Àq·1Y°¡Ç8ÈQÆk¬¢€?2ÿlØÇŸ¶Œ­u]gãÐ<1£ø•è‹ ­ÊÚ¼‘ƳÈ÷ö™ätÜ Ø§j÷-¡þĵ߆¼9§øKAý®5'Iµ†ÊÖü1n6öè#¾Û»åE玵ú‹E~]þÍðMûÿ¿´Ÿ´GŽþ*\|GמÞí]é­m9º»Q\=ÃÞ\³tÛ´pÝ@?¨”Q@–:üSOƒöξý©üSâx5]2Mbç\¶ÑE›#%ãå g˜ÈCd"A„e^Ù¯Ô™£ó¡’ãz•ϦF*Z(òãö ÿ‚l鿲wµo‰^&ñ4>0Öå´ûšÑÙ›U²II7†’MÒH¡P«¼s¿Ôz( âfãOøVðÿÃÏ/ƒf@'†ûzŠüRÑà™?>x]+áOíqZ5ܺ喟‡"ˆ¾¢"TÜ’µìŒ¬èŠ˜åNGzÌÒ?à›Þ0ý£õ/|kø³ûB_xîÚ¶ì¯t]¸†)C\Ù!ìîehÙ–,gæ*Ý+÷¾pð‡ü[ŒZ·Ãù?w¡x×ÍÖ4ŽËâcíÖËõâU ÷5éáéªÔgµW§ÚVÿɽ9jIÂiôz?^Ÿå÷ïÆ_øÿÇ¿µ/ ü4ñ¡ø­êc]a,VþH"'÷‚8šX€v^@ÙO¼¿0~rþËÿðLÏ~Ëÿ-¾)xkãsê 00j¶MáôOí7‘e’šKéŒe„Š¥ÁpH?­”W˜ugñÂ~<ñÇÃýSÂß ¼b|®ß„Hµ…±MAíÓp2l…ä‰w:eCîÊgpä üËýš¿à—>5ý™~-iŸüñÁå0²Ã©Y* GOiIí]ÞúPžfÁ‰F‡"¿`( Ì¿Û#öñçíã+]WYøÄt hãþ%z"è+r¶¯$q¬ò=À½…¦y7È6)Ú½Ës¶¿ðOïÚ9ü3gà}Oö°ñ xvÂÖ+,ôÍ)tÝ–° "ém¡\äps_ªÔPÅÿ³Gìðö_¼“Ä~±¸×wˆvª$!²rQw°á™…}¡EøÓãoø%§ÅŸüdŸâOØ‹ö¿ñnuáýoö¾ÕÞÆõ)’ÇlÎŽ0Ê^ ô|Á¹Wê=ðÿìCû鿱Ÿ…¼M¢EâoøKu^Cq5ïØ~Á¶xÊE—çÜgk<»xûøÚ1“—ûiþÇ~<ý¯mt¿ AñTø7Áö\òé)£-ñ¹¿ŒÈÃÜ}®ÝöˆÜ*Å‚ ‚Ù$¿zQ@~dzGŽeËðÏ[øŽ¾8ðÝ©-¤Ûc¦šÖ 4²Mq™âw˜K$›€b6ã àx¿í•ÿîƒö®ø›á?ŸÀšÆ…c“µ'•c¦@óÊÝö ÎîÌxQÜ+Ê~øsTƒC¾ø…â¨öxÆó FéOX-ÈÅ­°Ï8Š,pyzW­—~êÅ=Ö‘ÿëÿn­}mÜãÄûíR]wôÿƒ·¥ÏqDXÔ"ª Àvê(¯$ì (¢€ (¢€?œø(´z7ü³á&·âiz-Àð¼í#ü¨–Ðê’,ÙcÇ˵˜ú3_ÑÅ|‡û]þÆ¿ ÿkï Xé.¸ŸF×4C+izµª«ËnfÛæ$‘¶±>Õ,™S ²óŸ”µ?Ø_öÍ×< ÿ uoÚ¶ö_ y"×Fî[|),ër³°*0CNÛ†Aà‘@cüý­>|{ø¥ñáÃÛ{ËÏøWWz®":}Ä’³¦ÛwYۀŰRT‘‚~ ÿ‚×i×ìóàÝbÜ´°ñ4i883ÙÜlsèR¹õ`;ר¿±ŸìaáOØãÃzþ¡k·#¿ñ,öó^]ÜB–ãÈË$h[ <Ç<³·Zú3â·Âß|iø¬ü2ø…cý¡ ë‘§Œ1GX:IŽUãuWVì@ê8  ý‰µ 'Rý‘¾Üh²,–éá­:(A|¬WŽâepGb<×çwüßö‰<+¥þɾ’MKÅ^-¹µŸT¶´YÕ\5µ©TËngꃩÈÄ‹ŸEð—ü×öø3¦]ø+àí-áÏ^Ï$«aw¤Eu-ªÈr|©L¸z³D°î98ɯlýš?àž þø²_Š~%Õo>#|F¸w™µÍ\ÝM.|É`„³•‘òwI$’?÷YAl€zOì;û=Iû3þÎ~øy©ª ~ç~§¬2co»ÁtÈàù(ßÅåç½|!ûsËÅïÛûönøMi£LºÝìC•d’:DqëäX |÷¯ÚƒœqÖ¿#Ïüãl¿´?´íÏíæøî ¼Åº>‹ËXÌÔ°›óÇä1z~÷4ó÷ü³OÔt{à·ÄÍ Œ7ö2êP,ÁC𵏶 AÃo8#W×ß±§Á™¿dÏÙ«Å¿~+†¸øâk+¿xŽâèæxÒ(¤ºŽÕØó¹Ag—¿šì9 µú'¯øK¾*£Å5–°,dó­þÙoÇ“(þ8üÅm­î0kç¯ÚÏà?i‡ ðÇŸ€4­Hºjå4µÔd¿·Ê²BÜ@Шe%ö’\§åÈ`Êø"§„îõÏüZøÓ­5åÁ´ÓRsÕä¹’K»¼ŸvXOã_¶­û_þÎ^<ý§üÃ? üH>ÐnËb$Ò—Q}Eã’üÃqnФn„°R|Ì€x`‘?à’_ ÿ†B×u½`dø‹«jO,­Ë=´h¶X$õĉ1ú±¯É_Ø+ötñ§íñcþÿ‰¥˜|2ðV¨5ÏZŽ –îmá„2dŸkF2AX¼Ö\¿Ñìuû6xËöXøu7Âísâñ΃k&ý&/ì¤ÓNž%’Y®WrÏ;L%–Mãq pxúgAð—…|,nφ4k- ßÉç\ýŽÞ;:_ïÉåªïnO'&€??à³"ŸÄ~üðØUa–ð[§ _P™,íÑÐ/“(_°³(—þ“áü…ï~Ît@[†’ÎÎ\vV„¨_÷kóëâ§üÓâÅÿŒ|oñ_íÏâd¸‚{ISÃ(«h-5²D‹¨ >_˜å›$’~ç°ÓüAð·âÇ„o|U«®·7t¸ôm_Q[e³ŽëX°Mð\˜™b3.ôXÃ0^™ï^ž ¤*R¶¶ºõŽ­}×ùØåÄIÆQŸMŸÏþ οø-·Ä§|0ø{ð¾ q&»ªÜj“*ž|­:)C{3]dz”ö¯ÕÙ¯À#áwìýðóÀ?.mC°†àc¹0«\µ+9ükóëöŸÿ‚cøóö¦ø¡yñÆ¿ '™—§¯‡U£Ó¬<×’;pë˜Ë»ç•”3žH ?M~xoÆ~ðῈ>)ÿ„×_°£¹ÕþÄšy»ùØÆÆÞ7‘P¬ePÇqRÇày‡QÊ~ÑßWáWÀOˆľLÚ‰}qnÙÆn¼–[uÏ«LQGÖ¿)àˆÞ ~#|Nš?ŸZÕm´¨˜ŽBiÐùÏ·ÙšìêWÚ¾ôý±ÿeïˆ?µW…-þèßÿáð¤»_R±Mu¿–)—˜ÜÀȈÊÀ,'€+ö-ýÿÁ'þ,|ñ5÷Œ¾þÒWz·©Àö×7pøqZY¢–E•ÕÌš‹gs¢±=r:õ­ŠŸðKß’ÒŠÿµ¥âh,XÉZ 0Dì0]b]HFŽ7mÎ8Í~ˆ~ËßüUñ‹öyð¯ÆˆÚ}¦ƒ¨ëö³_K©qo°šA ƒ+3ðªÈrO_Jüdÿ‚\C7Æ¿ÛSâ÷í|†H¢Žþê2ÃýTúåéhG¶ ŽTÓé_°ß~ø«Ä߳Οû?ü(ñÉø}ok§Ûhòê §.¡$š\6¦ÕíÒ6š)¤Oš¯¹@ rÛ‡Á? à—?>Ûê–_ i‹Ï Ûëm _ ?F9ƒp.×ìFÐíŒzšýý¥¿hï‡ÿ³ÃGâŽ.ÓÎHÝ4í<8…Þ>Hb^¤d‚ì¹cÓŸÉ/ø%À¿üJø¯âoÛ“â¼9}bmCû%¤R¦âöúCö»¸û°Æ¥àLdÎÜçê¯ ÿÁ-~j^3‹âíãÏücÖ-ÙZ4Õç1Ú§pWt’2Ò1("¿Ntý;OÒ,-ô­*Ú+++8Ö( 8¢ÕDEUT é@(¢Š(¢Š(¢Š+ó þ ãñ"ëÀ¿²-î…a1Šãƺ­žÅxo o»—ŸF[}êëôö¿?à´¾ÕuoÙÃÂþ$°ŠI­´/ÂnöV8®m¦e@$Ø€žîz÷?ø%ŸÃo‡_±ß…µ‹QñŒ·ZÝÛc—ó¤1[óéöx¢ tŸROØ_¼s㟇z¦‘¦šµº­î"ýä½µ>l%ObYvçÑyì/âMŲÂ{ßN“Áiáû+¶캲Œ[Ü+z0•#ñéŠàüOûoøvßö»ðìðÿK‡Å:†«çÿm_Åw²=)á†IÌAV7J‘ÄÍ"î]¹UÎíÁvÃW•*‘© Ó¹)©ÅÅìÌßÚÛã»XþÀž1ø©£Hm¯µ­==BpÑ\ê3%„ꧨ1$Áê6äs_3ÿÁ~Úxwà_‰¾+ÏüLÕ¬¾üiøW¥C$°+ØxÓL†0NmEäK© €Óm¯[ÿ‚Iø¯A׿cOè:\è÷þÔ5;KøÁùã–k©.£,:ᢙ0zÔtæT#N³äø^«Ñê¾íŸš2ÃTr‡½ºÑú£ôgÄZö™áoê~'Ö¥iúE¬×—2‰ º$cŸERkùÛÿ‚@é:§Ä_ÚâwÆÛøJÇpeÇ*·zÕêÌ£wûË_ZÿÁUjí3Ã?¥ý˜>ÜÿjøçÇ-¥ügÍšÊÆFS䲦Ow‘ÇŒ˜Ù‰)»é¿ø'—ìÁuû0|³Ñ|KÅþ&—ûWY\†0Jè+]à ˆ#6 c9‚+€è3ÿà§_?áýŒ¼pa“Ë»ñµÑ`ç¾Ù2ùëøÛ¬µøãûl|.»øAû+þÈúÕ¤^Eö—¦ÝÉqÆ ]_›mP)÷Y\û×ÜðVÍY|uâ€ÿ³M¤ÿ½ñˆ£¸¹OÌ‚I#°¶l´n&Çû¦»¿ø,'€.uÿÙoÃ÷Ó丗þ"³eŠÞ6r–ÒZÜ@@U€ÇùP)ûnüG×jÏŠ^ ý…> Þ²[ëâ×Zñuô_2ÚiÁVâ8¤Ç$ege8 æ,E|ƒãßxkÇðT߆ß¼f±xSáö.•mò‚×D€ê· ç¹i ‹#Y‰$äæ¿Eàš_³MÇÀ¿ƒ÷¾)_xú¾¾žõ‰šËKEß2<Ÿ2³/ï¦É¨Ã1×ÂßðKÌülý·þ-|¿Rê°ê7°îëÚÍîbÆzd@;@ÑU~/~ÐÒ7Æ?ø*×Áo†pŸ7OøwbšÅÈê"ºO6ÿ$vÜ"µÜŠý¡¯ÅØ^öÛãOüöøðŽ.í´|höRçr˜$ŸìðH§ý¨4î=˜ÐŽþÇ Ñ>ÿÁG?hÍ/Å‘éZ¾$¾’YNÔŠ mF+Ävö[vsÇá[ õ½kã—‰~0ÁLþ#[½·‡¼¥jVž±¸$–È"”ÆävÆFTÜK!…xOísû3üJø¯ÿ'ñÃïÃwacãÏìÙ.¯£I´6-e½’WVUh]¶ó>ÕêE}óÿ.—Ÿ³¿ìeðWÀÑ.§ê—:ne‘æ}žÙ¾Ù4ŒF ò?zÿÄÒyjòOø"7€M¯>$üS¹/«êVšL.ÝBØÄg—ÑÊgÝ}«õWö¢øÿ ³ötøãä—ɸÒt;æ¶lãRDb·÷™ÐWƒÁ3ü ?c‡ñ s®Ås«ÎÃøìîñÂ- ñOø,GÄTðŸì©ƒ!˜-×µ›KC8f¶³ÍäŽádŠÞåðDχÿÙ_|{ñ.xöËâ-b:6#“™ü¯±{¦Uö¯Ûø¯þ ÝàHþþÆ¿ ´Ð€MªiÇW•»¹Õ${´'éˆ£Ø úËÆ'Òüá=kÆzÜ‚-;A²¸¿¹rpXÚW9?ì© çÛCÿŒ…ÿ‚Ë^_ô/ÁŒÄ¼"´ò­òƒõc_Ñ]=ÿðFm ïÆ¿¾0ürÖðúƒC©óº]ZæK»‚>n™ú×ô!@þ"Õ—@ðþ§®º‡]:Ök’ À"/Ò¿ÿà‰ºlšïˆ¾3üFÖÚ59Ε¸a—cu%Ü÷<ÿ´É>¤ ýÞñVÞ ðÆ± ©êVwÀž™š6N}¹¯Â/ø"¨Öÿ|û›ÈÎp!nšò)¸ëò’€ú@¿uø ñûþS#ðÛé¤虫÷ê¿~?Êd~}4ý5~ýWñ_þIoŒì ¨é3×^mñ–öÓMøCãBþe·¶¶Ðõ)$‘ÈUD[i $ž€?à‡ò.|^ÿ¯½ÿEÝ×ï~ Á/-N“ñOóWí"}_/#v·‹¸¸ÈÁ?Jý¹ø‹ãÿ |,ð6·ñÆ3=¾‰áûg»»’8ÚWX£êB ,ÇØPi\¯Ž°ÿéÆ*ýú¯À_ø+üGÀ¬?úqŠ¿~¨ógþ Ëá»}wö*ñF¥4+,ž¿Ò¯¢$dÆïw¡eô;.} ®ãþ ¡â‹¿þÄÿ ®ïœ¼ö^é䓟ÝÙ^Ï Cð‰PWÿ_×aÒ?b_ØK"£ëWºMœ`œe¾Šä…õ;a'è tðL/Üø{ö#øwâys_®£{ö./çxãÓøÐßtQEQEQEQEÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦ÔúJL_JLóN'²µ}B3Ožòá‚$JI>¢­hÂ.rèTa)I(œGþ(øoÀÖí>­8AÀÎ:WÆ~&ý²§ó¤_ Û ×$/Ÿç_/|Mñf¿ñ3Çw:e£±&(ª¤@b3_X|'øáßAoªÆ$À,dgñ¯Çñµ÷3|&ð6³hª-#ÁôQšäµOÙÏÁO<à7ÒºñYg8'—G.7Ëâù];3æ½/ö¾¸´QñdœVâþØãµ¶*ë¥ø+áHe1ËN>ï¥nh¿ | %ò®-מ9ZàÃÖÌ”•7RÇF&ŽÜñäÚíz÷víZ[é\JþÓsFÁÄEXûB/€–=ël§w|W=­þÎ~HüûhGP»qùkojê\à šåÑ|ŠžaûaOa$¶'Oñ­ûeCÚ{ô®ºO‚ÞWhÚ1Çݦéß³ïÃë¹Ø]Œ sùWY¢þοï †qó:cúÔá1¬•¹õ V/ŠçQÐñcö±P”c ô¦ißµ¡ÓäV}+Ýn?c/‡—4ÁÙwzgük†ÕÿcÿiŒJË&ÓìÆ–'/Íé¿k&i†ÌrÚ‹Ùr–´ŸÛ?B+ÿEÛô VÛþÙž`pÄqýá^~Ÿ²¿€ÙÔË+ã¿ük¸ÓcφZŒ`Ç+þÚ®Œ&37©îÅþF8¬>WMóI%÷íMዛǟvàÇŽGJØÑ¿kŸiͶFÝí‘]>§ûøm˜ØÊþhqÿׯ%ºý”4‹š9™‡§ùÍrb©fØiº²ÝXi帅ȶG°Û7Á@sÇâ*µçí“à¹m"˜ñÔW–ØþËžáRâBCþs]Ì_±o…çA$s§üú×^2Î+«Dç­‚Êè¿xåö¡ðÉ“ÌIÿ¨®ÃFý°ü1k—tÛ€÷¨~Æ1o·‘˜Žßä×'ì¯£Äæ6bŸZó›Í0®çjYn%XöølßøüEbëµÿ„/¡ò¡}§ê+…ÒÿeO ÜÊ"žR¤ÿŸZì“ö+ðÄ€:ÊÇ?çÖ½ͱPk¡Å<6WBi³“ƒöŸðÜmæG!È>¢»­/öÈð¤Q„Ÿ’8ÎEr:Ïìw¤iùxœ•ÿ>õËÙsDÆGùú×—Ng…•ŽÉQËq CÚÿá³|üGˆ®Sĵ¿…5,y…úŠçôŸÙ3·§ÊžR®Ï­uŸðÅ>ÆDͧÿ^½/o›âiÚ;^Ã+¡-w9‹?ڟÖR¬±ÉŸlŠïí¿lß, O÷þ¢¼ãVý4­:RÛË!ïþMaÃ-hd¼ñÔÿ“^mùéùV\9ÄÕ𵾫ŠÚö:sÌ‚…z_XÂzŸ«_zT­oaº…%ˆ‚®2?¸köMI]šJ-hÌ_jShþÕu{u/-¤ó¢’Z(ËŽù"¿Ÿø"F‡¯ñ'â¿/¿}¨YéÖžs¹ijKÿ}5ª“ô¯èšHÒXÚ)T:8!äzƒ_χß?à™¿´7‹ïtÿ†ú×Ä„þ*£ŽëB¯.#·†V’ÖIDjvM »Fé'–¯¸²±U"Oè:I#†6–Vˆ 31ÀrI' ‘Kñ¬Ð¸’7«)È ô ޵ø-ûX~Óÿµ7íYà™þ |øã-@×@MSPÔôÙ ¸¸€6~ά‘ oæ).2œ.íß±Ÿ³çÃçøSð/À?.#ò®|=¢XZ\€AÿIŽóÏs.ãÇ­0??à·:•ô…ß ,Ë-ýÕæ¯<+ÉÝ ­µ±u,f˜¥}âÿðOïÙ÷PýŸ£ø_7€ü?gâvðâiŸÛ£L€ÞǨ‹A½3¢¬¯ ›÷„ï˧šü‹ý©o¾8üwý³<5ñ¤üñý×€¼q¤Á›øvú;«« Ÿ´ÏòŠ+Lï P[îíÜAà~x“þ ñz=é¼!û*|I¸Õ¶0·[íêq!)‘¢†G*$“Ó#¨ú‹öOøiàßÙÛán‹û7iÞ(°×4ñˆ´ j¿Ùv¿ØwâÇÎŽÞå”˘äÞ7L¡{zýaøWÿÚý“þxŽ/[xjë–òyÑ]ë×O~RN»ü“¶`y ѳ)åH5ÀÿÁ-?·ü-û:Û|&ñw€|Ià½Ãw7²ëzTú}½ë_\Í$Mjóª™JD$ARxeÏé…|Óûeê÷ºì¡ñoQÓ‹-ÂøgSY>ò‰ hËM¡‰Ïlf¿:à‰³µø)ñÅÈÚµ/Çc!ïåØÚG*\féëõûâ‚ô‰ñÃÝxÓ¥Bþ½ë÷ƒá׆­<ðûÃÓÐEk¡ivV1 è±Û@‘(@µùçÿ>ý¼_ûN|6Ðë¾øcàøËÄž>±¼Ò mMžþ+(eTŠâ[§XÄLR°„c.àãX€ÊOø%—Âß þÒ~0üdø¹á;Å6Œ7›mZÒëq}¬ÝÉtò,s£§˜‹ ØÈGñWê·í'ðGö[øQðâÄH~ø6 D¾žÕLJôàEß”Ënò8&fAŸzù?þ ¢xÛᄼcðÃâ'ÃoxOY¾¾ma5-WH¸±°žÕ"··KušuLή]Âr„~S^ƒÿQñ_Å-á#~Ïß þx£Å—¾)6wz†¡¥iW–Y[ÎÒ ð#ÿ¤4С1ãˆÎIù”ÿ‚$ü?þÍøYñ âlÑáõíZßL‰ˆçÊÓaóX¯±{¬R¾Õûw_šßðKH¼CáŸÙ²ÛáwŒ<âkÞº¸’ñõ½6m>öÔ.gš9-ZeV“ˈ"Iò¤/P¿Jh¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯5/ø·?¬µû½âD eqÙSV³RmØöh³« šú.¼»ã'‚®>žU“FAçå‘XsÚ»ºã­FTæéÍY§fo ©%%³ (¢²((¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¼£ã'Ž/|àöþÁO?ÄZÔ©§i0õ/yqò£cû±Œ»gŒ µ¾*µ#N²*ÔP‹“èp¶ÿñv~4½áýï…þÈb‹º\ër/ÎÞ‡ì¨p;‡9WÒ5Âü5ð=—ïé¾´9íSuÄç–žæC¾iXžIw$óÐ`v®êºsDg5 t^þnïæe†¦Ôo-Þ¯úòØ(¢Šó΀¢ŠøGöý¼| ðÄöŸ |%¡^üHø¡¨„û?‡´ ÅãóWr}¢UI _˜Fˆï· UT† wQ^SðKÄ_)ëÉoy2´Òí±6£yŽ?u íÏc•Œ ƒ@FQ^9û?üfÑ¿h?„øÃáý>ãK°ñNñ[]”3GäO%»n1–^Z2FB:+Øè¢Š(¢ŠBBŒ±À÷÷ ¢ŠcH‰·{Üp2q’{z}Q@ø©ñÏÅ?üiÿ?ð¿ìóiñÄÞðf¹¤ g·Ðõ'²+$:}ÝÐdáÐxWq(r2:ò?Qþü? cÕ#>8ñG?µ '>%Ô†¢müü[‘{ïùúçjúP²Q^Cñ—ãÇÂÙÿÂÏâÿ‹>"·Ð¬~a HwÜ\ºŒùvð&d•ýB©ÇV d×û-þÓ^ý«¾ßüIðV—}¥i¶z­Æ–©¨ÄÒ#ŠO3;¨ ²¯²#‘‚@>’¢Š(¯øÛàíKÅþÒð×ËâO Lš®”àd›‹o˜Åî%L¡^„‘ž•ÔüDø—à„¾ºñ§ÄzÓú-˜ýåÍܶ2Þwl|¨€³&¼ö]ý±þþÖ—Þ5ᮟ¨A§ø6{HMÝôiÞ-Ø”«Å³:ä·`© Ñ…ÄÊHÕŽëúüLêÓS‹‹ê}à?é¾?ð~•ã '‹}N“frc“îÉ{£‚§ÜW]_8x7þ-‡Æ _áÜ¿»Ðüeæë:?eŽíqöëeúñ*¨à.{šú>·ÌpѧRôþj½ùlüÓ# QÊ>öëGëýjQEp…Q@ä?~<|'ýŸü,þ/ø³â+} Çæ¤‡}Å˨ϗofI_Ô*œubMq²ßí5á/Ú»áíÿÄŸiwÚV›gªÜijš€ŒM!‚8¤ó1º€Ë*ñ» ‚9$é*+Ëþ+ü0?t+]x·Äû-ȹûW‡/¿³î¤Â2yRI²MÑÛŠãï*œñ_’7¯ÄÿƒðSO†Ÿ4ߊ>.ñ'ƒõK{uk®jÏx&’K[ÓµÀXШ1#(+ÁÍ~ßQEQH0ÊœŽGÜZ(¤V )È<‚;ÒÐExÿÇ|VðgÃ-SÄŸ¼+⺚ùßà·üQÞ'ñŸÂ ~H4{Ïí=-zìíH™6 ô†mêO©¯¢+|ÇU¥|;¯GªüjŽPMï×ÕnQEp›…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@s3ðg…~!ø[SðO´Èu X„Áwip»£–3ÎB `AR tôP奿ü£á§†å¾¶øcñOÇžѵ9K7MÕ–;v' â Ì!‘°X×­~ÎðNÏ€?³/Œãø‹àéµ}cıC4)yªÝG)E¸d+1B™##$‚yæ¾ñ¢€>rý¢4¸´ý;Dø¡öU»_ ݨBÈ$["ø}žö&CÃcÁòŽ¡ÿ»ø&5ëïüñ¿‹~tšßú †Íáq¨ fM‡$…2² áT ý*Õô«wI½Ñ58ÄÖz„2[̇£G*•aø‚kÅÿgÝVú? ^|=×$2jþ»}&RÜ-ãù­%û¯ P=všõ¥ûÜ"}i»Û¯ü¥üä^åo)~kü×äyìÿûþÏ?³Î¾|m¡X]ø›ÅìÎã[×§·ˆò³DGnrx±ù˜$ œý±EägçÅ?ø&gÂŒn¾2x»âŽ¿á$–ëíV²A©Ù¢éÁ%3C –ëxbî”9+×%²Oè?‡´–Ð4 7BkëQ´ÛhmÝë‰n® (ͨi_‚€X“Òµè  øåðwOøïðò÷ᶯâ=kÃ:v¤@ºŸB¹ŽÖêh6²½»¼±L 2Ä‹·æã ü­û<ÿÁ8>þÌŸ`ø‡ðׯž0(ŕΡhloce`ê(,áiïQ¼a€=2èEÁ|Oðø›àM[À¯ê¾M^4‰õá-uQ]]„39>9øiã/HóÆÑ]é÷·ö­§Þ®ÇDûL0YÂd1/-ò·=2ß4P_þÒßðO߆?µ_Œ!ñoÄßø¶$³ŒGi¦ØßZG§Z|ª²40Íg1W—h26âXØ><övø¡~;_†þñ&»â-" Œ–¿Û·Q]IiUg·1C ¤ ‚Á6ðÌÜã|ýûHÁ<~~Ô¾9ÿ„çâo<^¦Ö;M:ÎþÑ4ë%ˆÿf†k9ŠJï%›Ø(}Q@%û?| Ñÿg‡|3ðïˆõ¿iVr—µ}vê;¹ía(ˆ¶Ð´QB©lÊ&ß”³sŒ›ûE~ÏZ'í)à•øâøƒÃz4ŽZê=îSz„cɹ3A8xçf'®zW¿Ñ@~ÍŸðO¿†_²¿ŒÅ¿ ¼kâù#¹R·zeíý£é·˜Ò6¸†8K´^c4gpÚÞ£ ýãEWóÛãX%ÿ‚uÿÁEÅ­ZÞh¾|U7~mÌ+”/ÝeºŒ¨ïkv] gÈ .NEBUæ_~ü9øëà{߇Ÿ4hµ­÷ ±ò²C*çdÐȸxä\œ2pH9RAô+ û-RÆßSÓn#»³»&†h˜ ñl·z­ú[¶Ë‹ /áŽEs¸ ¢†(É8ÂÉ‘ƒŒŸ?ðEoÙi‰fñWŒÉ<’o´ïþWWëõùÿTý–èiñŸþißü®¯Ô/…ßô?„Ÿ¼9ðËÃRÏ>•á‹4ûi.™wŠ­+""—8ËEèw•æô¿‹Çîl~ ëZ‡üTÒÂa»ÔíÚæÙc ¡£^Ieȱ ÅOø+üGÀ¬?úqŠ¿~«ñŸâ¯ìûZ||ø—ை~$øbþ_Ï B–VÛ¡K…×pX•àšý ý¦þüNøÙà{?ü5øsðÔ\^)Õ/làón.,<·N7‹”;•׌ƒ‘òÊø(wŒ5oÛö‚ðWìSðH¶¤þ¾’ïÄqüö¶·%DLÒÆ,ai<Þ^O(~ðm?¸¾ð~‹ð÷Áz€ü9‹Jðí¶Ÿj§ÚƱ&qŒœ(Éîy¯ý˜ÿdO„_²§‡ît߇ֲÝêú S©kÌ%¾½e$€ÌTŒHÛseÔTQEQEQEQEÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦“Ž:“ÞÕó×í)¯Ï¡|1Õg¶m²´L>•ô7­|Yû^jsÃá·Ó—îM Ïë_=Äõ•<CÙÈisâ¡xŸìðÞÏÅ‘\ø¿P\Í63Üäרš”¶W ®#ÏÃþÉz"é>ÿ®Çwæs_Jë:4„ •ùñÅ|ÖKÆXM/{]~g¹šæíc%ü:~G™èšÜÚt¡Xå ¯Z³»KØÄ‘œ‚+Å/ì&Óå1H:w­\›N”F[ä5Ù•æs¡?eWc“‚XóÓÜíõÿ%äf⇭y{Ç,l™6²š÷[[¸.áWC»#šæ|Gáèîã3ÛŒ8¯C5ÉáU{Z[œx {ƒöu60|?âŵÉÊž†½)LsÅw+W‚I¶ÒÜma]‡‡üDЕµ¹o”ð3\ÙNnâýSlÃ.ÓÚS4µï ‰s5¸ÁïŠó—F†S‚®§ƒ_@#ǵ˕g1ö5õ7Æå×ýå=Å×ô´vžÊ{W+‰äÚ÷UkmFÜŒ‡ +̵ÿ½¤†âÈ¥›e<²öÔ¶]ŽRýÕS¤ð÷ˆÒu[k–Ãt®²îÎè HÈà׃Æï ‚HÎW¥ø{ÄKp¢Övù‡­veyœ*/cXçÇåîž‘Çëz4ú|§ º2zÕ?P›N•ZžE{]Õœ°äpkȵ­]:bTezãÌòÙЗµ¥±ÓÆÂ¬}]ÏOÒ5xu??J§¯hI} ’%ýèé^[§êió #'ë×ôZJÜß7zôòül1TýMÎfxyóÃcÆ. šÚSªUÆk¬ÐŸ0ec´ö¯'šÔÃTö5ºzxÜkGÚCs¶ñ¿ƒ¬¼Q¥\ZÏs"çøû㯠^ü4øŒŽÞ)wÐw¯Ú­3QƒP€H­’G"¾&ý²<÷‡-õ[ÂMŒ]‡\m®.0ɩԦ±”wZ\1šNG†«³Ðö?†^2:މgy+îI@?N+è{y’h„¨r¾ý™¯.uŸ [éC$Û ?S_vé¶Íkh±ÎÚõx7R­xóø— uš‰§E4g4êû4Ϙ (¢˜Q@Q@Q@rÞ7ñn›àO j¾/ÕÎ-t¨fÁv"öˆQîEu5óÄ/ø¹?4…‘~óHÐ6kºÞ9V(qelݾwÌŒ§ª€k».ÃF¥_áZ¿Eþ{/6Œ15\cîîô^¿Ö§Sð;ÂZ—‡<uŒø“ž­©±+5Ï̱`òIµôu¯e¢ŠÇ‰•j’«-ßõo‘t©¨EEt (¢¹ÍŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( <ÿóã/ˆþÉû½+ÅŠÚþ•ÙVà—Я¾í²…&¾‹¯øý£ß§†¬~"è™5¯].© ¯Ym”m¼‡?Ý’“Ž»E{.‹¬XxƒG±×t©ÖzŒÜBãø£•C)üzÙ‡ïi÷z?UþjÏÍÜäÃû²•/šôäïøtQEy'XQEQEQEQEQEQEQEQEQEW; ÿ‹­ñcQø‘7ï<=àó.“¢g”šèñ{v¾£¤Hà €OQ]7ÇjÚ^ƒgà¿ >ßxÎoìë:ÂŒ3qrqÈXcËdt%Mz?ƒ¼)¥xÂúg„´DÙe¥À°§«cï;cø²Ì{’k×£ûŒ;«ö§t¿ÃöŸÏá_öñÇ?ÞTQé_¯EúýÇKEWvQ@8~ÖßâýœÿgßüW@’j:m°‡NðD—÷L!·‰UÜ;î+WÁŸðJžgµOÚÛâ+K¬x÷â-Ýá†úðï–;˜¤Ž¹ä=Äè固*À'vGü¿^¹³ýŸüáØ_lZŸ‰iþ!ki8ûfP~ Wèïì«¢ÅáïÙ“á>…û?…tmûzÎ&‘¸õrOã@•ñGñgˆ< ¯èžÖÿáñí”Ñiú‘…'—L„G)Ã+ldÓ§5øûvþÂ~ øû1ß|^ñ'‰5oüR¹Õ¬þ¿¨Ü¹ó¾ÐY$D€³1½ÆÑ† ò×ôO_˜?ðWù3mKþÃ:_þŒjõŸø&¸ö"ø]ùõ¾ÿÓ…Í}Ë_ ÿÁ5ÿäȾ×¥ïþœ.kîJ(¢¾\ý³þ1xÓà'ìÛ㊟¬b¿×thíÄ ÿ°$¿úfÔ+öÊö‹‹9൜ÚÏ$l±Ì\Æä`8VÊ’§œƒÞ¿|wÿ)¯ø}ÿ`IôͨWíÝ~~Ó?°›áïÙ×â_Ço^8Õ~&üU³ÓÚê JiZÚÎÏdªV+{U$lÁ+µŽÀÉšöÏø#HöKÔ}üS¨é5¥}Oû}ÿÉ›üXÿ°,ŸúWË?ðFŸù4­GþÆCÿI­(õ’Š( ÊO‹_ðO»_ˆÍñâÏí+ãý[âåµ®­7‡lÍ…Ž‘l±ÈöàEÃJ€&⡈;ÕÉÍ|íÿ8þß‹çþž´_ýî¿i~+ÿÉ-ñýµý&zü[ÿ‚ȹñ{þ¾ô_ýw@°¼©x›Áë«xdmñ/†'MWK`2Z{~Z.:¬©¹ ô$Œô®ßÀ¾0Ó|}á +ÆAÿFÕ Yvç&7èñ·ûHà©÷ÖWÎ ÿ‹cñwXøq/îô?ùºÖž;‘·[/ã‰UF©=ÍzÔ?}‡•/µyz}¥òø¿ð#’~åE.GëÓü¾ã蛋‹{;ynîåX Y䑨*"(Ë31à9$ô¯Ï Oþ —û!X|Hµøog¯^ê¯sv–GT³´2iqÊî&fug@Ç™#Ó‚G5ö÷ÄŸ‡žø³à=oῌá’ãDñ³ZÝ$R¼ߟ–D!èzFAþ~ÿo¯†þƒöÇýžÿg‡¦‹¤Z[é¾E¤A¦¡ªi咨勲êÖlFÑÎ7Ͷ?vý'Wã7üàyÑü7¡þØŸ š7Ž| eåí¯ÈÏhÒl·øæH'1¢“Õ«d*úðÇzÿÄÿ‚þ ø‰â=t­_ÄzM¥õÕ¬jè‘M5Z~пüñrÝõË1öÈPü°ÞÀÆ”‰‘¶ç’¸=ëÞëòþ¹¯\ê?³ˆô[‡ÞºGŠ.–ýا´µ“oýö\þ5úý@Q@Q@Q@|ßã_øº_´¿†ÐþóAð‰‹XÖû¬—'›+Výee<¸¯^øƒãM;áïƒuOjc|Zt%’1÷¥•¾X¢_wr}k“ø+à½GÂ^7¾$>g‰|E3êz´‡¯Ún>o,z,K„p0qÖ½|îiK÷øcê÷öêüZ8ëûòTºný;|ßà™ëÔQEy`QEQEQEQEQEQEQEQEógíEûR|7ý”~xúGº¹»s›¦[•ûUýÀ*ˆ ˆ2HxAŽ¬Ê­ãÿ²wÄÛ3âÿˆn~&|nðæ“àO‡zþÈÑ;j¯$Ãs3±bFH(’Àˆ€æ¿8¾ÿoø)_ˆ|_âØ¿µþ|,>ÖB^Ñ£²›ìö`¯Cö‰Ë]2ž)FʵýPâïíAûL~Ô6·w‡ÿf/ƒþ5µðž‘â }=#–m.ÖøE5ÂHï#yÈ]³´ ¡Àýsú!ð#ÀŸ´Ÿƒïõi¾;üO²øksKe®–m¤Vc#3EˆL{×ä¿ÇïùLÃo¦‘ÿ¢f¯ßªð?´ßÁ٫ßðüXñZsʬֶ~úþðŽÐ[©ÜÃ<;QN72׃þÄŸ¶¿ü6E×Ä»? Â5¥xNâÆ;#%ÏŸqqâÎwL¡UQ‡’°ù±“ŒžWÅ¿°Àm.Ëâ§ÅAyñÅ:õ¦­w߈'kÏìøÚ)(mPð<‘¶ç]£a^•ñÏüãþEß‹ßõõ¢ÿ軺ý[øµÿ_ÄüX‹ä¶Y΃ª°à}ŽüþåÜöX§ÿÀ«èšâ¾#x>ÛÇþÖüu€5Kg‰ôIGÍÿÀ$ ß…`|ñ…Ͼ鮥•ÕmѬµo¾—–„Å6áØ³.ìz0¯V·ïp°©Ö•ú;¸ÿíËî9!îÕqï¯èÿBO‹~ü ð¬¾3ø­â+_iqä#NÙ–w>\®d•ñü(¬qÏLšø¿öOÿ‚„鵯Ç?|8ðw…eÒ¼3¡é/¨Ûj“µÜ²\CÝHÕ„¹Ìb6ó÷°¾âØGàŸ>7ë?~+­ï//„Kc¥k›/LDU– sÃ+°gØå8Ló_”?ðIM6ËGý±þ/é:lB K/Q‚ÇDŽ=VUÀ+Ê:ÏèÇšo‹µë_€µˆü=â+«gŽÃP–Ýn£µœ’F…þYŸá<üPý³¾,þß_±×†<;âmSãVâtñ ä¶‹>±µ1£ó7É&sÓû¹_ˆ_ð[Ïù%_ ì5wÿ¤Ôúa㛿Žþ'ýì5÷úm·Ä;í?M¼Š]R<ÚÊÅcšâ2ª¥TÊ»‘N07u^|¿û9þÞÚ·‰>'¿ìÛûRøU~üV‰–8X;Rw‘`fgØòcd‰'ðI¸„?x|*ÿ’_àÿûiÿúN•ùŸÿrøgâ¿‚ß¼9lmü_ðâêÚO·[’—Mš]Ž»—ù3Šá>xΈ>Ñ|_ú…º™qåÜ'É2cý™‡á]ÝqÖ£*stæµNÏäo ©EIlŠ(¬Š (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šh=(Î3_~Ù:¤Ñ5®œ‹•–sø‘_7ZüÖýµ5‰tËÒÚçÿ5ñœqS—Ï£áxßèÏÙëT´Ó¼ ™±…ôMŽ­k¨.øA¯>[Íÿm¼©’Š‹ü«ÖtíFkhËžEyÙs*4aNKC»4ËUJ³šÜõý[G‡Q…²>lW_éóXLcqž+Øt^ B Õ¯¤C©DÃz÷ó¶Š|ô·<|.T'Ë=3Ñu¹´Ù‚HÙŒú×®Y]Åy‘CWˆêXÌc”Á­=\—O˜$§1漜¯6 {Û†;‹ž™ÛøƒÃñÞFg·\J:â¼¶Hä·Å' µîÖ—ÞÄ$Œ‚ s>!ðìwq›˜W£še*Qu¨ï¹É—ã7ìê>ñ [0¶¹?!èMzdf)ã ¼« ð)#’1¸Úêk±ð÷ˆž÷'å=ë «7åýÕc\Ã/R^Ò™¯â/­À3À0¼ÒTxÝ¢q‚§š÷ô’9Óx;•«×¼8·[§„aºñZæÙBšö´ˆË³._r¡ÊhzÜ–,r6c5êŠÖÚ•¶FXW…Ë HÑÊ0Ë[ú»6Ÿ(ŽC˜yùVjàý}Žœv M{JE@{'7—­r‘ÈÑ8’‡îË%¶£i»†Wæzÿ‡ä³”Ü[Œ¡«Ìò—í(½˜Ý{*‡Iáß‹…[{“†ÔÞÙC ŽAzW„Ç$±>øÎ¯Lðt÷ 8æ»2¼ÕM{ ÛœØüƒö”Î?[ÑfÓe;FSµgé÷òØL$°;Šö»»8/á(àGZò=kE›M˜¶Ü¡é\y–[:3u)ltà±Ñ©gPõ#VƒRJ˜}ï­cøAKØÌ°®s^o§êéò‰Pð:ŠõÝ'VƒR€~lr z,l1Pöu78ñYá§í!±âòÅ5¼ž[ñ"×máßJÚÜœÖ׈<<·hn-ÆW—K¶óÛåu¯Têàê]lzPœ1P×sߊæ0Õjó¯ø}£swl8ïŠg‡"xyòËcÂa—Ê“ÌS·+Ó|?â¸E·¸<ö5ÅëZ,ÚlÇ1šÆŽYc!Ðão¥|ÖW VÌ÷+Q†&ÑÜ÷-GNƒP€£€Ié^Aªésé“2°Ê•Üø{Äi:¬'kbº]B J¬Èë_E‹ÃRÅSöÜñpø‰áªrKcŬ¯g±•fˆzö­C¨Â8“½yn­¥Í§JFÓ³µT²¼šÎUš3jñ08ê˜iòTØõ±x8W<Z×4hõK(ýàäW‘]ZÉi)†aóŽ•ëÚ6µ ü “‡ïQëš$:ŒÔaýkØÌ0ÄÁT£¹åa1s¡>Iìy~™ªO§N’F~AÖ¼«ö¦×Cx ·9ó™ÿ¾s^™wi=¤¦) úWÉÿµV¥siá›ä<„ûæ¾+˜Ô£†uº>«ƒ…lL*C£GWûj :ÀM~ö÷¯«à‰EàS[ž&±’N¦`æŸ_f|Ó (¢Q@Q@Q@^'ñ™áê>'Ödòì´¸$¸”÷ÛΫ€w$ ò¿€þÔí<1uãG³Ä>6¸:¥à=bŽA‹kqžvÅI‡ñ\Ÿˆž<ðïÁ‹s¿Ou­{>ÅlãÈ·nß¿˜ Ž .Gôh õª~ç ¡ö§«ÿ Ù|Þ¿(³’>ýW.‘Óç×îÛï (¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠŽX£ž'†dG *ÊÃ!à‚Pk翲ÉáCÄ¿¯œ“ák“q¦–92iW¤É åŒLZ6=¸ôE|éñ˜ø§Â¿­ÆØ4©†•¬‘Ðé—Ì;{A6×Ô×­–~ñO þÖßâ[}úÇærb½ÛUí¿£ßüþGÑtRAïK^IÖQEQEQEQEQEQEQEQE×t‰I" %‰8¤šuxÇ-_QÕãÒ¾øfc¯ãGh®%O½i¥ÇÍÜÇвþí3÷‹Eu`°®µEM;w}’Õ¿’Ôʽ^H¹ÿ þ$x×YøÝ|¤éç~•áäaÀ²…ÈšäÞâ@px!F:b¾¬ÝHÓ¼?¤YèZD"ÞËO… †5è±Æ¡T~B´ªñø¥V£”U¢´K²[ÁîîÉÃÒäžý}BŠ(®#p¢Š(ñïþ IáK½_ömðωíPºè#ƒÏÀûÝ[Oò{0F¿V÷ìmâ‹OþÊ_ uË9ÅÀÿ„kLµ‘Ôç÷öP-´ãê²ÄÀûŠí¾>ü Ñ>=üñ_Â-}¼«oÙ´)63ä\![Ì*dGÇ|c½~LÿÁ6~9_üñ6½û þÐj¾×ômBy´I®¤Ùæv %¬nûT‰[3Û°âPìÍ´0î-~IÁ`<}àËoÙq¼úÕ£kº¦³`ÐX¬È×,—y$òÁ,@Áb0 9 Wë%í¶£g>Ÿzžm½ÔmˆI‘ÆÖ<ƒÚ¾ ÿ‡\þÂôLÿò³¬òmfÁ0|yàíoö8ð‡ôÝfÒmWFKëkÛ5 ż¿nÔIw.äuuÈåX_IþÓ´‡ÿf/„÷ß¼M¥Ýë7Öíofc–¹F§22®<ó^¦ÿÁ2aý'QµÕtÿ†ÞUÕ”©4Nu]¶ÉSµ¯ œ8 ƒÜW‹ÿÁPÄŠ~ðoì§ð£Â×ÚιãËô¿šðDËamg§·ÍæÜ‘‘ÑÜ“ò äeнgß:íðƒÃßü3g>Ÿ¦ø‰n+{’¦h͵ĖΡ+ñ‡¥{ ÐÃs –÷¬±J¥]VV ƒÁu㟳¿Áë€?<#ðƒO¹ûjxnÈC%ÆÝ¢k‰¦¸/eyØÈI潞€?hïø&Ƨ¢ø«þïìCª·€|y§»\& ½•ËX[1ù /Ñ¡pmܤF¹Î¿ìéûøoâ­õÏìÇûføjxúSý™qo©ÛlÓ5W|(ŠH¦C,™FÌR mó*Wë}|õñ³ö^ø;ñö÷CÖüy£íï ]ÛÝéú­©ß@ÖÒ‰V3&ø˜š7 ¼’¡[ ?2>1~ÀŸÿfÏûA~À:½Å¤±fKï »™DЃ¹¢€HH¹ˆÿÏ O˜§˜œ¾Õ_«?dø(O€?hû•øuãKOøAþ)ZoŠãFºÜ‘ÝK>i´i0Û—i-âTÁûáK×è…|ùâ¯Ùàß‹~1øgãåÞŠ,¼qáyšXµ 2!k¥h^.í@"eU|«:àÁr¤è:(¢€?/ÿ‚ŒxÿÁ~ý‘~#é:Þµik¨júØìí^dÜO,¨¡#‹;ØŽ§…œM|¹ÿhñ߃¿áœµ¿K¬ÚE¯YøŽîáì^d["{ko.Q!Š1GPÀc*Gjú£_ÿ‚jþÅ(×u/ëŸ~Ó©j×3]ÝJ5}Z?2yÜÉ#lKÅEË18Pè“ÿ¹ý„ÿè™ÿågXÿäÚú×ãÄÍ3àçÂï|TÕìæÔ,|1c-ôÐ[módH†HBä.~¦¼¿öQý¨<)ûZ|6¼ø•á .ïG´²ÔæÒä·½1™D°Å ų2à¬ËŽs^ÿ Õõ¿~Ìü øMá-C]Õ||mü-¥[ØÃ%ÄV°"©o6BN?q"olž\¨æ½_öý›îÿeÏÙïHøu­N—ýÜÒêš³DA‰on•FŒ>òÅ${¿ˆ©a€@£ûCxÿÁ~ø9ã]SÅúÕ¦•h×ê¿h™#i­Ü$h¬Awr@UPK¯Åïø"_Ž|!¢ŸŠ^Öu{[ [Q“H¸´·¸™"’â8…ÒHbV ¾ÂÉ»Æáë_¨ß¿àŸß²/ůj¿~ ø ûWÄ:Ü‹-åÏö¦§šè‹>\QÆ¿*ò¨õë“\Oü:çöÿ¢gÿ•cÿ“hîgWƒFÐïµçV¸†ÆÞ[’±à³¬H\…ÉÆHsŠüáø]ûTxgöéðO‰üAðŸA¿Ð¼QðªæÃS³Æ&y¥˜LÂ5ò™²²¤#) W°¯fý o´?Ù?öDÖ4„^½¼·±±mDÓ,âùâ¸Ô‹C HÎÒJcYdÜŘ–8@w2דÿÁ2¿dÿ~ÌŸ5CâeñŽg‚òöÏ ›+{ue¶ÈÎec»ó…/·ª’z0¸™Q©‘ÝVô}LêÒS‹‹ê}÷àiž=ðŽ•âý ÿ¢ê,¡s’Ñãoö‘ÁSî+ùÿøïão hðX½+_ø›ªA£xoÂÃOw»ºmÂ#ÑþÓ'Þâ@ûGkösÀÿñl~-ë? %ýÞ‰âÏ7ZÑsÂ¥Çü¿Z¯ÐâUQÀR{šæþ.~Ãß³'Ç_ˆ1üNø£á¬kÉVï ¼º·Žd‡>_›¢¹Pq’9)È ó4iÔ÷>ªôå³óLŒ5W(ûÛ­¯õ©ð_Œ øqáÝ7K¹½µÐ´‹¢±´W *CØã3eˆEîI d×âßÅ {Cñü/á&¡áýFÛSµ2¡–Ödš=â×Q%w!####4û¡EPåÇíeÿÒ>øŠOß³ž–~"üX»™¬„6±½Å¥…Ï £ˆ¾içBaC„ ùŒ¥J0ýŸÿàœ>"ñ§ŠÏí ûyk ã?Þ¸]iD¶vÁ~e[¶LFá;[ň êvÑ„_²÷Áß‚¾(ñG޼£‰bV;¨ž&+ÔR¤Œçžhñ×ãçü3_ñ_‰Sölý€´OøK|U*ý”ë«ØXÇýH2ÇÆn$ź1¼G¡~Ê¿ðM_ü>ׯoÚOP~'ßJ.ݮٮl¬î;Çó\ÎüµmSˆ ‡?m~Ïÿ³WÂÙŸÂ+á…Z2Ù,¡MÝôØ–úúE~â|Ç’B€¨¹;A¯y ¾Oýº¼QiáÙâÖ©y8.4 ½=XœfME~ÆŠ=Ù¦}kë ü2ÿ‚‡ü]Õ¿j¯ˆý…g_/ÄŒ·Ñßk×ÐK¾ÒØÀ,2Ê›—˃w›pyÚâ8×2nJöOø#O….ô/ÙOR׮Ъø“Ä—·P0!·µÈõýìR ýi¯0ø-ð¯Aø#ð§Âÿ <5–°ðÍŒv«!i¤4Ó0ìÒÊZFŒ±Åz}QEQEQ\ÄßÚü:ðN¥â©ÓÏšÝZÀ2Z{©NÈbP9;œŒã2{V´hʤÕ8+¶ì‰œÔbå-‘åž ÿ‹©ñšÇÂ)ûÏü?1êZt›T²@}DK™¶x"¾¯,ø;àk¯ø.]aþѯj’I¨j³žL·×'|¹#¨Nc²æ½N»s*ÑsT©»Æ/>ïæõô²èa†ƒIÎ[½É|‚Š(¯4é (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ æMô2A&:í•Jœ~€?ÿà‡:t`øÅ­»oži´89䀋zäçý¢Ãò¯ß þ}à·ò|=øÃñ«àwˆÉ·×cKg07}æâÖä ÷ pœzjþ‚¨ðã÷ü¦Gá·ÓHÿÑ3WïÕ~ü~ÿ”Èü6úiú&jýú â¿ü’ßÿØPÿÒg¯Å¿ø!Çü‹Ÿ¿ëïEÿÑwuú1ûO~Ö³ÇÂïxçÃ^'ñÖ– KñÂß|Oiái¼@ÚeÅŒ×ïäÛÊ-…ÂL¦fù—ÌB¸ކ€?¥*ùÛÂ?ñB|tñ?ƒ[÷zoŒ¡]~ÄtQv˜†õ«>Cí^¨ü3ý¬üwã‰Ì~Ñ|gc¨Giuv Ä“M}Ìi+®Dy[æo— ò+ËjÚgõ1_ˆ_ð[Ïù%_ ì5wÿ¤ÕûàŸøâV†uøuÿÅ¿|3“äÓõ¯ø¨´¡Ø Ï—yöe•GE$⾊¯Ÿ>>ÛO Úxâî›{¯Þ¬×ZM6ëÞ þRg¦Òkßm®`¼¶ŠîÖA,3¢º:œ«+ ‚¡×­˜þòñ?̬ÿÅ?gêÙɆ÷\©öÕz?ø7DÔQEy'XQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦c"ŸH(7Éé_˜_¶ÉÿŠßF–Ÿû9¯Ô0 ¯ÌÛgþG}þ½?ös_Ç¿îgÓðŸûäO©ÿg«X®ü RŽª+­ÖtY4ÙÎÐ|¶é\ïìßÏ‚ û¢½çR¶†{r. ¨©#ÄÖ™vY øw¶âÇceK5Ðñ› Bm>a$Mòç‘^¿¤jÐ_Âa¼ExÖ«&c;!Õ-qŸ»çÆ?Læ›a®Zé×1È·°®ñ ‹ÏÓšãËñµ0•=œ×ºm‹ÃÓ¯h½OcÖ4ˆuHÛ–¯"¾Óå°˜Å( •ì>±o©Â¬Œ ÇnôÝ_H‹Q‰¸Ã×µ™eÐÄÃÚRÜópxÉQŸ%M3Ñu™´Ù‚³f#úW®Ú]C{ÉÏZðûû ´ùÌ2‚cZº&µ>"£¶c¯'-ÌçB~ƦǡŽÀª±ö´ÎÓÄ>[Å3À˜qé^a$rA!ŽA†S^ëiy ìHÈlŠäüEáá:››qó¢»³|±M*ÔŽL¿àýCÃþ"kw·-•=+Ó"–9:œ‚+çöWŽMŒ6:Wa¡x®ÒÅ–ÛTºŽÇ ½ÕsùšŒ«5j^ƧÈ×3ÀFÞÒI¯è z, óûW—K–ò˜fí^÷Ð]E¾Tn…H#óËkþŽî#5¸ÃŠèβŽx{JKS »0äjØâô-rM>aͺ,óì+Ô£–×T¶ÀÃ+ ñ axÅ0à ØÑu©ôé‚3f2kË˳7Mû»~G~?ûô÷.ëþ’ÊC=¸;O\W-‚HÎz׸Å=¶©n1†Ü9¯7ñ„ö2ˆTúUf™g+ö´¶'ŽºöUw:_xŠ9Ð[Ü0 8ä×S{g ü$èkÂ"¹²­ÆñS’Xàν?Ã~(ÓõE¸ºŠY®ÇVþF½ «2Ucìjœ™– ÙËž™ÆkZ,Út¤…;3T4ýB[ ƒÆpjöË»8o¡1Ê3‘Ö¼‡[Ò$Ó'Q@Š( Š( ³5­_Oðþ‘{®êÒˆ,´ød¸šCÑc‰K1ü…i×Οä“Ç>#ðïÀû>^²ãQÖŠžcÒ­„ŽGŸ(±Ï»0eVªŒ£»}’Õþy˜â*òAµ¿OSCà.‘¨^i:ŸÅ?DbÖ|w8¾(ßz l Ï¢ÅóÀ¹¯{¦GpƱD¡UUtŸSĺÕeQ«_eÙl—Éh:¹"¢QErš…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@axŸÃÚw‹|;©xcWMöz¥¼–òŽûdR¹„g ö<ÖíP›‹R‹Õ QMYž!ðÄ:Ž¡àÇ𧈟v½àÛ‡ÑïsÕþÍÄ2ŒòVH¶ÝÎkÛëçOÿźøç£ø±w£øþ5ÑïÏE]FZÊFÿjDÝúWÑuèæ°Nj´“Wô}WÉÞÞV9°òò=ã§ù~EW˜uQ@Q@Q@Q@Q@Q@Q@5 û=*ÂçSÔf[{[HÞi¤s…H㙉ôkÁ> X^x®ÿXøá¯BÑÝx¤ˆt¸¤5¶ ýÂûï_±Ê‘Q|d¸ŸÇ~"Ñ~é22¦±‹ýrHÎ :L2™†¸Ó95ô-µµ½¼V–±¬0@ª‘¢Œ*¢Œtpzï÷k}ªŸ„SÿÛšû—fqÿ¯”?ø óò&¢Š+È;Š( Š( ¾jý¢dŸ¿µ• ÅM ο³B–š¥£}ŸQ¶Rs¶9€;“$Ÿ.EtÉÎÜó_JÑ@WðOátþh¿ á×õ/Å¢,±Ç«MçÞIÊò";ð1°Pê”Q@Q@Q@Q@Q@Q@=ã_ÙëàGÄq¼MñáîƒâM]£HÞ£¦ÛÝNc*3m\œ ñ[Ÿþ|*øN—Ñü1ð†“áEÔÌfèivPÙ‰Ì;¼³'”«»föÛž™8ë^‹EQEQEQEQExÏÇ j~!ð”zï†%ð¬ëªé„ —–^ŽJÍä+Мg¥w¾ ñn™ã¿ é^/ÑÛ6º¤ 2Œä£ý¤`Tûƒ]M|áàOø¶_5¯†2þïEñG›­è™áRlÿ§Z¯û§ªŽ“ë^µßa¥KíCÞ^Ÿi|¾/ü㟹QK¤´~½?Ëî>¢Š+É;Š( ÇŸ þüSÒàÐþ%xgNñNk0¸ŠßSµŠî(æ È$T•X Ì29Á#½qýš?gköž*ðgÃOhzΞY­ïl´»k{ˆK©F)$hIV*pz+Ûè Š( Š( Š( !øëðŽßãŸÃ-Wáçˆ5? [jæ5î‘7‘v!ŽUy" Ae@c`À‚zWãïÅÿÚàGüãÄö_gO‡ö~!ñ%Õ ¹ñ¡vÆõD„5´2ΨX³(2”$j¥.\šýuý¡þ7xkövø=â?‹^(!áÑ­É··Ýµ®ï$ù-íÓ¾d€Hjå kùƒý¥~ ø³Ký›<1ûR|Yg¸ñÿÆOÏ©NÒ¦ :[w’Ö0§;DœÈàFbL ”ými—Ñêzu®¥ú»¸’UúH¡‡óª~"ñƒá ÿÄþ(Ô Ò´.¸ºº¹q0ÅË;»`q¿µ?í¯ƒžÖs»íú—qŸ_6Ö7Ïë_Š?ðV/~.øñK±WÃ9]¤Ôf²—TŠ6#íW÷ò*ØÚÉŽvF ÌÀäèÝPPÒËÿ-ñ_Åßj>ý> jâÒÎÙõ[«•Ó,Ô±!Œ$Á)çI ÉÁÇ?à¿ø)¯ÄûoÚìçñ¯à…Ç…¼K®j6Zx0jBCÛdTY¼·‡d‘Û‹¤Ä`_¢ÿ³×À¯~Î? ôO…~ …~ϦD ÍÎÀ²ÞÞ8Ì×2ã«HÝ'j…Aò¨ã?Ù{ᇎþ>øCö×–ìø¯ÁV­id‘Ê‹i";iš3vhÚwd!× ±ñ Xñ‡‡üªë>ðòx¯Ä‘´Òä»Kº}ÀûC«¬.H%H$c¾kò·öoÿ‚ž|Ký¦>*Ú|/ðOÀôŽUÄú•Ôž Âiö)*G5ëX®â…ÀçbzÓ?Žj¿Ö}¶ÚÆÚÖ«ý¥ï‚5©F[Ÿ½Yýï—8b8ý—¯çOàB'í ÿ€ñŽ˜ ­?Á÷š¥Ð=Q¢Òa]"ÕÇ·˜cu÷ÅAž¾ñ§á/Qñv—‰­\ÛE%åŒWî;YÙAx–pˆ%xÞŒŠ»­Üêvz5ýÞ‰fºŽ£¼¯mjÒˆy• Ž#) ;aKvç88­:­{yk§YϨ^È!·¶¥•Û€ˆƒs1öf€?4oø*oÆü\¿øáÿÙÕµi—7–“éÑx‘ ,¶þÐ<Ãd#*»æÝ†ãi9îoÿ਺·ÂÿØh?´ÿÀ¿ü0´Ô‰_ Æ£m8f\ÁlW?7–ÎÀcå9ù7þ ;cqñwö±øÇûDj1ýÕÌ ·ð\ë÷Íp>¢8$_¡¯ØoÛáw‡þ/~ͼ#¯Ú­ÉM"òúÉŽC}eOm*·U"E±ÕK)á{?€|àߊ^Ó<{ðÿV‡[Ð5ˆ„Ö·pQ×8 ‚+)YVX®Â¿ ÿàˆ>5Ö5/üPðÜÏ&¡_i—ö¨Ä‘ê1ÜG0^À²¡ÀîIîk÷B€ (¢€ (¢€ (¢€? ?m…doÚwLý¾> i'UðõÔ¿ñTXħlO2.M¹ÛÒù˜";¹¾òƒúßðOãïÂÚÂ0xËáV¿¯jȆâ`·Vr8Ï•s ;âqÈäa±•,¸'×®-ໂK[¨ÖhfRŽŽ+« `x Ž=kç‡?²_ìÿð‹ân£ñoá—„àðÞ¿«YËcsö7‘-)¥ŽfÙk¸ÃÝÿ«UÈÇ&€?$þ?Êd~}4ý5~ýWÄÿ¿`O€ß~%‹~8mb¬0@“i÷æÔ"ÛgËd „« õ½#àGì»àÙê÷W¿ðf­¯jRkQÃÃXÔå¿D–*cWÀRwžüPYâOÙ×ö}ñ–·uâoü1𾹬_0k‹ÛíÆææfU ’Ë ; I<+þ;öYÿ¢7àÏü'´ïþ1^ÿE|¡ûMÚxÛáïìͯxKöjð,z†­wmý¦éš\pÚAc L/pˆåö¨À?3a‘æß°×ìggû3| Ô<ã #Ô|IãLͯ´LZ‰-b$ É3Øù˜—h|QN2iÝ «èÏŒþüø?ñCŸDø·àx¯Å>ô o5]&Òöåí-Xµ¡O¸ŒÄã`Î3’;צÿÃ'~Ë?ôFüÿ„öÿÆ*®¥ÿí §ê£÷zgÄK#e9è£RÓÆø«ÂZ5ȯ¢kÓÍ¢EZ;Ms|Þÿt“ùØGhò?³§ù~9oøÁ_taáßxOðΔ²4¢ÏL´†ÊÜHøÜþT ‰¹°2q“Þ¿ÿà·ŸòJ¾ÿØjïÿI«öCÇž Ó>"x7Xð>µ=ÍµŽ·lö³Kg1·¸D`˜¥^Q½é_kðJß٧ıGˆõOj±ÂÅ‘nõÉgTb0J‡F㸯,ê>Üø{ªiš'Áï êúÍÜ66zŒ³Ü\H±Ckl…ÝÈUP9$~.~Õ_µÏø(ÿÆM öPýœ.VóÀ^¹MO_ñ£5§š£ó¾]ñB®ËÈóåo—äA%~ÅüGø ðÛâÇ„ø-ã» u "YFaÉ Œ¶2ÄUÆ|°‚ ôÍt_ þü7ø7á¨üð»Ã¶~Ò#mæ HöïsÁy\åär8.ìÍŒ ЇÃÏxwát‡~ƒìÚ?‡l¡±µN­åÀ¡C1îíÌÝKO&»*( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  ®™e­i—z>¥šÒú ™FŽU*Ê~ šñ?Ù÷S½·ðÅÿÃnC&­à;·ÒäfûÒZ¯Ïg.;+Â@î×½×ξ+ÿ‹ñÏÃÞ2_Ýé^7‹û üôQ{d±ú³ÑA^®_ûÊu0ý×2õùÆþ®Ç&#Ý”j|Ÿ£ÿƒoÄú*Š(¯(ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(­Ò¿0?mùôcéiÿ³šý@a‘_˜¶ÂçÇ:"ö6¸?÷Ù¯‰ã½pLú~vÆDúGà>·›à«xÁM«Å|éûr~Ó~&øiàçÒü"€j7ŠUH'pÎG¯aÓlõ ü(M{A‹Ï¹H•zó´WàgíSñÇã7Œ|Oåø«I[Xlä+Ê«¸ì)pôëªp‡Ù°g0§)Î]n}uð»öNý¢>)Çgñ_ñ-ô w²a·Ë†Ãc•÷¯pø£û?þÐqxÓ÷¼¸m2Æ0·Î‡ÏÕü<‹ •¡QHU@Èí_PþÐ?µg‹>üIÑ| §X¤¶úšnw;r>}½Å}&? ÇÜWg‡ªén}1àÛCEѬ"¹cçÅ >  þµï:V«£(r¾t¾¿½³ð”~!•?{-¿€8ÎÍÕðO€ÿo[›kv>/mí´Û.0€`Š3_5•b+R›Œ–‡±¥N¬–çëæ±£Ã©DC/ÏÚ¼‡PÓî4ùLR©Ç¯µ~fx¯þ ñºöiï~xpÞiÑ1ô` õÃ-w_à ZGÄýn?|Jµ:V¹!ڨɰvêkÕÌòØW´§¹ÁÇÎŒ½œ¶?Bô]j]2eŒ±1µzÕäW°‰ExeÄB4Fs Èßìžzý+ä/Ÿ¶ÿ†>;hZk5î¯(>\J ò8Ç÷¯3&ÅÕ„ý•]C1ÂÓœ=¤7?A|AáŹæÙ0ÝM~-~Þž%ñW‡¼c¡ÛèÚ”ö(s¸Fp Þ=«wÃðPoW:„z§ˆ¼.ÉáòAi1»aö šðßÛ;ヾ.ë~Ô¼9!8ÌÈÀ© ¼v8¯^¶UNsö°<ÚÙ(òHýˆýšüWu/Ã=ûVc<’Ads’IQ_SÇ"J›”åM|5ðŒ_ ´I_÷j–Ñžx袼cãíé§|6ÔG„ü$§QÕy7ÃŒgw®\§3Ÿ<©TÙãpJINèþ¿áô¼§q äW—MnÖÎðÊ0G­~U¯íõûSÙÌu WÁøÒ>öåD'gÐ.k럀¿¶~=³hwy³×c 4,…!UœdѨZ{†_˜ºo’GÖZ6µ>›*!9ºûW©G%®±m¼¯»¶’ÖFŠ`@ÿ]khºÔšlЬįMyng*_¸¯±ßÁ©¯iOsÉ¿i]>ÿAøk­ßi®at…Ù]x#¥~tþÁž/ñN§âÍû_Sšõc—å6BýêýOý¢æ·Ô¾ kò`ögþ•ù5ûÚ8ño‰J ¨›Ÿüz½jGéÞ©¤M§K–C°œ ©e{5Œ¢X›¡é_™¾ÿ‚„xîÇS¶Òþ8èGH‚á‚—¿2­~ƒøcÅ:4h&rÂÚï€8×âý·¿hÍ.çû_ÄÞòtSóoXT¾¸ šû»àOí%àïz>›sA2Fß» ôèqÞ¾v1Ióô=‰Ê–!yŸ w6°j6û\n 85äÚÎ61p?vO¬üF»ðW‚µ-b5ó&±‰š0z‘×é_Ÿ7üe5]:ÿNÖ¬ÖrÛ¤jçÏp ö¯v®–6—ãÈÝÇ €kÉ:Ϫh¯æþ ¿ûV|dÒjO xGãŒõÝwAñõ”¶ñë:Íä)<¿¼´ž%žGžX|€Ë×Ì"¿z¿k­êß³Ÿ/Sĺ¿„¦Ð4›Ý^-CE½{¤—O·’tS$d1™@tÈÜ8È<ÐÒ4Wãßü{Ÿ%økÆš÷ƒµÏ ÍjÖGEÔf°K·½º‚Ýâ¹XYLª#fdù†Ã–ädÑZ+ðsö#ýš~.þÒßì¾/xûö‚ø£\j·×‘ZCa¯ÜšÖÕüçÍgbÆT`3šóïÚËà'íð+âÏÂO|>øñãÿiu¦»Öï|ëIÖ{xÙ™â‘Sc%À •ØÙ8ÅD”Wãïü«Gøà¯†º/Ç_†Ÿ5xê/ Þêv‰$¿ð^=Å´2Y%õêÆÂ@‡ÊS L¦pIý'Ö¿àÞ:m&ðx{öœø›§å?Ùšë]šHÛNÏ1P£ÝŒáÇJý=¢¿ ¿à÷ŸüQðûâŽ~-ø£]ñ ŸV·ÒìãÖ¯îo<ƒc –vˆ\;íÞnP1^»íU¿à¯zо|4ð¯ÅOü@ñ„õÇÕbÑ…–•ªÜYYÜÛI ÍÃË$02fTdP$ÏÝ;Hè@ì5ø­ðö2øÛñkö~ð‡Äí[ö•ø‰áÿø£MMEcMbê{8’ç/oû³2HÀÄQ›÷ƒ98ljû þÕ´…ûSxƒö3ý õãâù¬$Ô-luÈk”¹ÓCJß¾Úh¦…YÇšK® ŠýÄ¢¼[ö‚øíàŸÙÃá^±ñ[Ç’‘e¦¨H-ã#μ»“"hþ9ëÑT3•I¯É?ºgíÿ¹¾ø­ãψš§Â„ëq$:n›á¹ZÒkÏ,”uIT«H‘‘‡šo04€ªF €º´WóóûNX~ÐðMOx7âOÊ^ ñÏ€u«‰,î´^5ò,±)‰³„taŠIqº9$ÝxÏIøà_üAÐ77ÄÚu¦§l$8†ò%™+Gc@uùyû~Þ·ß³ÅÆŸðoàÕ¢küF¨#R†tÓ£¸;!cÏ›q+ÜÄr:3‚ «ðÞ ÿ‚|üzø“áâ¯ÚWö€ñ„3ÔÐÏöP1ØéҸʡ_õnWøÖ‰ÈV o`×Ê+ñ#þ ¥í#¤~ÑŸ~üVñƵâo|:ŠM3ËÔ.g¹¶’öKͰ\Cö†vUh­åe Ç澡ÿ‚Ÿ&¯¡þËúÇÄ¿ xÓ^ðv¹á9­ZÈèºŒÖ v÷·P[¼W+ )•DlÌŸ0ØAnFAû?âׂÄj¾¼«çA5” í1^@D8nß:€HìM;áGÇÄ/é>'•|«É£ò¯"ÆÓÜ'Ë õu8±ù¡ÿ¤ð‡ÄÏ|1“ö…ø«ñÄþ'º×&¼±Ó,5-^êïOŽÎÞEçòfv 1š97EQÀËùÓþ –|wðW‡ÅŸ|mâ? §‹/ÒâÞßHÕ.,¬^FŽ_·™"…ÓtÛÖÙX©‚¾¥ *˜yÒ{ÇÞ_”—äþL娹j)®º?Óõ_3÷þŠøþ ÑðïÇÀâ_Ä¿ø‡ÅÞ#ñý”‹¦³©Ü_[ÙÚ¹wµ[hæfØ^G‘³–$Š+¸ý¶kmöHøNÞ'’$Ô|S­´–šƒgl×* ´²àƒäÀ3ã’J  ¶áåGÖš–µ£hÍjº½ý½‰¾™-íÄò¤Fiä8HãÜFçcÀQ’{ Ó¯ÈßÙ_ö!ñ'Ä OAýª¿lÝcSñGÄ{‰bÕ4Ý*æf‚ÛH â[mÐÇ·lªB¸…vG;YÇ×ßµíoáoÙ®=AþÀÕöëGëýjQEp›…Q@Q@Q@WçÿíýûdøsöaøQ©iz©üI×àk}"É^{a0*×Ò ÉHâ1–yPTóöãý¢|+ñÏö°ðçÁý^ÇWñÂ?…Z€›Ä0èV†ú[ýMy‘” ƒbû.âÀ®éÊçŠäÿॿµ·Ã¯Úெ<)àï xŸÃ÷6» Ï™¬é"Â×È—ãq+üùe*¸('Ó?f&ñ/‡µK}Wž%Ônn¼DVU’êÞhå’h'Þ£ÊO5w˜Èì¹5ç¿ðYïx*ßà…ðÞmVÙ¼Sy¯YêiË"›•´Š ¤k†ŒËæ€  ààôö?Ôÿµÿe?„¹ÜáÑ¢cêÐÙÇ~ªkñö[„üpÿ‚¶xÇÆú±óâðö¥â B%o˜¬‹iÖ™?ôÌ(üCý–|ãÿŒ:̚爼Cö»³<‘C o´Éºí…#\Ð08ÏÍÖ€ x_UƒP“Ãm¾ÕÒÞE•`»œ¤0Ã!RBˤ…üÀ:ä ×ëÏŠ5‡vÿ²NàïMiâ½ Ä¿…tt´‘&‚ûÏ·û *2’¤*†,3ÆÒ m‡¡*µ#N½©QB.RÙ.ÿÁ<þ ]þÒß ü«jöÉm§|ÓÓHh*Ç>¯ùQO±p0–‚7è1#’µú¿_Ëçüƒãï‡ÿeÚgÆ~ x’Í<¯\Ï¥¶ª&S§®£¦NñÛ] ²Q`™|Åó3·æ˜…\éöÞâÞòÞ+»IVx'UxäF ŽŒ2¬¬8 ŽAjñ™TQ‹Ú*ËúówdÓ¤£wÜš¾)ý®üqûgx>o 'ì•à3ÅñÝ-áÖQhÁ·d0ý˜&ûË_¾ ¹Æÿº:wûZŠæ5?¿áuÁd¿è‹xkþúƒÿ–ôÂêÿ‚ÉÑð×ýõÿ-ëö†Šü¾øñGþ k⋞Ò><|/Ðô\<ãS¾´hLðªÛÈÑÛ©N~i‚)ÄmÁ=:Ô( â‡m¾ü5ñ_Ä+Ìy>Ò¯u& ЋH\~;qŽõø“ÿMð]Íü¿¾1jÙšâî[=*)Ûï3’÷Wy>¤´¾¹ÿ‚¬|nðçïÙs_ðê°Gâ}šÆÖÈH¿i60{™Œ@îò¼¸ž2øÛ¹€Îx¯ÿ‚l~Ñ_²çÀŸÙKIÐümñIÑ|Eª_ꞣg4Œ'ŠF—ìñ†P¤’`‚6ìF(öξNýº> ³ý‘þ(x¡$ò§}m> &ÔÊÙFËî­0aôÍ~dø“ö˜³ý·¿oß„_>_݇ž¾“Všé<ËuÔ'²_µI+!ÚþNaŽÀIw8ë°ÿ‚Îünðå‡Âà^‘ªÁ7ˆ5}V+ýFÒ)å‚ÊÒ&d¢œ§›,‘´{±¸!# f€=þÏðÿþ¿Ù›XñÅÄ{n<_®Îñ¾>õ­Œinƒß ÿ:ûöâø¡§ü#ý•>#ø¢öaÅÖ“q¦YÏy©!µ„(ﵤÞG÷U@käïÙ§öÄýgoÙWáïƒuψúö†‘¢Á-å’Mwp·×Y¹¹‹d±Ü'•Ôç¹ s_üHÕ>8ÁY~(hþø}¢ÞxCàw…î|×Ô¯ÓbÊä”’äã)-Ï—”†Ù„y%Ü$}ÿWøs©ø{à‡Œ~#jù0ø¿VŽBz˘Œ†AþÏ›,ˆ=Ñ«ör¸†ßü'ð—Àš'ÃY‹ ÃöÉkkw«Égoâwb]ØòÌI=k· Š( Š( Š( Š( Š( Š( Š( ø÷á«ï|8½½ÑüN¼;$ZÆžÀd‹›ó¹d €µ^‰áØøÇÂúWŠ´Ó›mVÚ+”É_1A*}Ôäq] ŒA¯ž>á-cƧ;cðåñ¼ÓTô:n¢LѪúùRV>¸êÓýîQë“ÑýÏ—ïg$½ÚÉô–Ÿ5ªü.}EW”u…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@yŸÆËã߇š¾dvj"1sa 8hï-ˆ–¶]B“èMze¶¼©THnþâ*SR‹‹ÙœÂÿEñ À:/‹Pl–úöˆñ.æ2RdÇQ¶E`3Ú»êù×á÷üP_ü[ðÞOÝéÞ ð‘icøCJDw±ÜH…“_EWVgB0¬ù>ªôzÛå³óFXZŽP÷·Z?Uý\(¢Šó΀¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÑýü¢Š(¢Š(¢Šù›öÃøÕ«þÏŸ³¯Œ>)øzî5}2ã²YFèÖââE…Çp›·c¾0x4ôÍüXê·/ík«_ϨÜüPÖIرX¦ ÏeU;Táµ?jïú*:çþŸð írŠþ(ÿáµ?jïú*:çþŸð£þSö®ÿ¢£®àIÿ þ×(¯âþSö®ÿ¢£®àIÿ ?áµ?jïú*:çþŸð írŠþ(ÿáµ?jïú*:çþŸð£þSö®ÿ¢£®àIÿ þ×(¯âþSö®ÿ¢£®àIÿ ?áµ?jïú*:çþŸð írŠþeÿàž·ÇýCö†ðçÂÿˆ¾%¹ñfâé™KâKy™IŽX¤ÀaÈÃG½MQEQEQEQEQEQEQEQEQEQEQEQEQEQES1‚)ôÓÔP4)=«óö×8ñÖ‹ÿ^¿û9¯ÓóŒf¿/ÿm~|u¢ÿׯþÎkâxëLÌú^_í‘>ÚøEi ߀l ¹A$m dŸtWä'üŸÂZ6‘âM2M>Ê8ÃŒ¶}Æ¿a> ‘ÿ>ŸŸùâŸú¯Êïø)Ьšö’¹àÿñF½¼ª„g„§ÞǘÕqÄOÔúÇözÒ4{o‡z-íµ”qÜ}š<¸ýů„j½H^þÑ^Y1íûÊý%ø) IÂæsi?÷íkòÓö¯f‹ö„ðö8p?ö¥y8:µ0õå}ŽüDiΊ¶çíÞ™£ð’5ÓÔûö+ùÜøcðÉ€ÖoìãðWÀŸü!l|MojÚ¨ËLÏ÷²y®×öçý¤¯>xkûʲêÚ’ˆ`d1ÈÏÒ¿5¼û*|zøÛl¾4ñ§ˆ/´»Kó¹R;‰T`óÑz׋À:^ô]¢v᱊¢´Ö§êÅ¿Š¿³lžÔ,¼K¤®ñ2€0H$}kò‡öÔm¯>>x¡|'&Ý!äýÚŽ˜Þý1_Bø'n‡ è×Z¾¯â›»ö –i˜qþó^ÿýѬü?ûGøD€†‚Ý‚¯¿Îâ»)W§‰¦é9_ÌÂ¥)P©Î¶>²ý¿üy¢xb/iò´êq˜ò§{"»ÏØ?àg‚ü%à{}w^Ó¢¼ÔµZiWæõþµå?ðR­1-¼Ká›°¿!‘sè>s_dü•$ø]£µ» …ê?ÝæâðJ<®Èï¥lMÓ=sâÿÀ¯‡ß¼¨YɤAö¦‰¼¹‚üÈqÆ9¯çóöuðÇÃÿÚŸXðá%ã†EÚOûïþýéþ!hl&¶º?.Ò2kñŸÀkiuûc뮮鞙ûï^½,Lq”Z‰åʃÃÔ×cìoÚïâuÏÃ?…·÷Vyw2[¶Ò0vž•òwü¿àdžþ"]]üVø‹zÕÛf!8Îlÿ&¯nÿ‚—xvâ/‡P\Y¦äh¹»×æoÀ½+ö¾O A/Ã?:~æòÊH€cq\:O 7ÔëÅTUåô­¬|3øo®ih·zU³Á*Û· #øñk‰û)þÑÖwÞ¸h4Íjà …xA÷›µuVOÿ˜‹x´ÂÍÓ>býš¼óÆ?²÷í­ñgÅ:>¥ã}ôùÄža–2z`ôl×·QCJÑw<ØJTefÕÿˆz¼z÷Á=CVR Ëf[óC_—ÿ°Âø›ã³­xžy Mº4eTï=+õ Å>Ö¼û6ÝiÚš¥CfÂAœà„jøƒö_/Ä»†Ï?÷Ý|ÆË imOj¬cˆ”W‘ûE€|äF°i @½>•ñ¯íuáŶðý«)ýÒ9 ø-}qáïì+mrxéšùçö½+'‚í™yÌŒñÚȧK‚”Öé?Èß"U(c#µÏÿf™-(¢Š`QEóÃm —7"Š%.îÇ ª£$’z*Zð:ö«c¤|#Ð%1jž9œÚË"}ë}6!¾òoû÷òzî s]X,+­US½»¾ÉjßÉje^¯$Š¿ ›ÆZ¿ˆ~7êHAñŸcÒUÆ ZE£Œ€yS<€ÈÃèGZú&¨iZeŽ‹¦ZhúdBÞÎÆ$‚×¢G…U@1Wê±øŸmUÍ+-’ì–‰}ÂÃÒä‚O~¾½B¿5¿à¬_?áýõí*)<«Ÿ_ØhñyÁ“írìÑ[ºŸc_¥5ø·ÿ5?ðµÿhÙÇöh‡÷k¸Ôuºÿ£Mqº¹‹wGÞ¸Í?o_€úÏìýð×öføÉáhÍŽ«áÍMÒ/gUÇ•ªÙ¨ÔmÜÿ´ÒµÁçœ OÒ?Û¿ö‚ÒµÏø'5ÇÄJ"_‰ÖZU­¨ ‡PdšâþÒÃÈñÒ¾Œý¼¾Âïý•|uáH<ýVÆÌêÚh/ö½;÷ê‰þÔ¨¯ÑÍ7øŸ©üyøCð;ö/•ä•­üu.à3µ,/Z€çÕêðœ}ÕÚ€?¦ßØÏáÿü+Ù_ᇃ^?&{}Öæá1·7Ãísƒî%™…~{Ák¾ c|ðOÈ$Ù7‰õ©/$òÖúd$2‘éæ\Ä~ª+ö~(¢‚$‚Ç ª£T  ¯ç_þ BÍñßþ ð³à¹3YÙ.“arƒŸ.MRëιs诔Ǿz·Â_ø)gìËû;þÉðƒe¾ñŒô- (¿³ÖÊX`´±™fóf—bù"åÛsFYˆåAÍ~ž~Ç~"üaýœüñCâšZůø’îÞ;(ZVÜÏ"ÛáYÜå¡TrsüUÓ~Ð?³÷‚hŸ„úÇÂO°°Õ¾ÎEÕ¤q}¦Ù­æI•¡2#*“³i8û¤Žõ_àþ ê~“áGÁÍ~-rßádV¾»+ÿ£½¤fF¢Ç#b“##=Å|¡ÿj³ûWìYâ9ñŸ²jZT¿LÝ$û=UýŠþ*øwàÿü?ŸüW&Ý3Ã:^­q"‚JÑjWI(Oå“liþÓ í?à¨Ölý†~$2Ð"QÿÕm3ÿŽ“_õÍ[öŽøû4þÁž ºtƒS¸ÔõÏO æÞÂZùãF=XƒËµ¸.`îEyodžþ#½ýŽ%ý¬þ&§™ãO?‚î6`A‡JŽÓQ(¨*’ËʯO*8qÅ~èjŸ´¿Ù§þ åொw…þ×Áš ¶—ný.5+>%·Ô6dp9òÑjù“þ #¢i>ý’¼áÍÕ,´Í+Äö–°F0‘A™{h£ÑT>•á>µ“öçøÇðOöy´&ëáwÀÿ h—¾$e9†ëQ[(Kv#‚K¶ÁÃ(,½(íÏø&ìóª|3øEyñŸâ ½Ç¾,Ê5{ÉîgK)Y¤·F'óiäé’ê¬2•úlHPYŽä“Hˆ‘"Ç„D ¾{ý¬þ ­ýš>%øå%òn4ýñmŸ8ÅÕÌfÞÛÿ#H”üìþÌ´÷Áï þÞ¾<ý¤þ3ê²Øé7ÒkwT±[KvÞuíÂÅm‰Y‚­£HŽÏ5úð³þ +{ûJ~×¾øCð"ÁWáêYÞÝkWú•«-äþDNÊmÔKû¨üÏ)uÜKœ¨ÀÏœÁ>C¦üñÇÄ‹Ûu3x“ZŽÆ"ê h4ÈCìd¹‘~«ô¯°µÿ‡ß³_ìµñï_ý¬,›Äß>üÓî)D3ê‡`©¿Q--ÙÉàò%äöc_Ð}7?ü5¡þ×?ðWø–ÛûWÂzß`»€»ª›mŤž6hʰw½>RÝÖ€?d¼cûNþ̳Gà (5ŸimeáË,¬ì,®¡º¾¸[h„QG¼,ÎI â/Ve×åüCáÏ~;þÕž2ýµüY¦>—¡ÉqªMfÄ.kýM™¹xí wVqß`뻨úü×ö0ðÝôzŽð§K–X›pq}}⺖T#Ø®+ëý;MÓ´{ }+HµŠÆÊÑ(`(¢U€À çsþ Ýã¯ü\ý£>þË^v›ìbÔ›pp²êÚÌ‚(CסfÒz _ןߟ†žÐþü>ðçà Æ#Ó<7aoa)eo`?‰È,Ç©bIä×óó5‰ñüÑm¼F1äë+4aÏüùhb[R>¾\d~ýÐâWüëÄ6vß>øMäëQ×g½Dþ#•«Dä{r¹úŠý ý•$O‡ÿ±¯Ã]GÅ5¼:G„,o®™þôP­ ¸lö㱊üQýµõkïÛöýðÏìùà~Û¢øbDÑ¥¸€‡HØ?Ÿ«\îäbYõhp2XgöÃö¿‰¼3û|S±ðÔ>DV^Ô-aŽ><»qlÑ0Ë@ŒðMýQý«?m~Ô>Oµ¯‡^MN8åýâÅ{¨³ÃcÏm­ãGÝLhGL×ô‰_‹ŸðD­Î>=×Ð/Úï|H-d#ïyvÖpôR kc¬Ý‚=eBçÑG¥~ÃüfðO‰|SðVäí‡F˜êz8?Å¥ß1m‹ê ›rêE~7|8u³ÿ‚ÙkpêC3OyªIì[Dw\Û0EvfeJ«Œ~×£Õ~‰–«œ{õõêCuZK+9®¡¾–ÞæØ:Å+(/ÉáXŒ€ÛFq×=*ÍÄl;¿µOü¥÷áýø[ÿFŠþˆ«ùÝýªå/¿ ëÿÂßú4WôE@=ÿ±«¯ìåÿ@ø“û?éRy~ñCj0ÛÛ}ÔO)?µ,øèZ+s$`ú15ýWóÉ|ë}ÿÅC¥ l¼ˆ9Ì^oý ènŠ( Š( Š( CǾ2Ó>xCTñ†¯Ìt%†–CòÇÿ´îBs\gÁOê~ð¼Ú׊~ox¢vÔõW#•šnRè°¦.pqÁ®C^ÿ‹±ñŽÓÂiûÏ |?hõ Gº\j® µ€ö"ÌŒ9;Xt¯¤k×Ä~ã¨ý©Ù¿O²¿öçÿnö8éþò£ŸE¢õêÿO¼(¢ŠòÀ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ž9xSTÖü)‰|.¿ñRøFuÕtâ24?ë`8ä¬ÑåJ÷8Ízƒ|W¥øãº_‹´fÝgªÀ“ ÎJ–27ûHÙV÷ºjùÃáÿü[?Šzß©¿w£x‡ÌÖô,ð¨ÌÓmWýÇýâ¨è¤šõ¨þû éý¨j½>Òù|_øÉ?r¢—Ihýz—Ü}EW’u…Q@Q@Q@yGŒ>üø…¬Ÿxÿá߇µê”Pào… ~­êü4ðvá!©yfèi}½€¸0îòü߳ƛöom»³ÇMZñÇÇŸtÔѾ#xcLñEŒL^85;8o#G#‘fV Øî0kµ¢€>vÑ?d_Ùk×){£ü&ð¼É!Òmdt>ªÏ~†¾´´´°¶ŠÊÆ··BGjG*Œ`*Åâ¿ìËû7kú­æ»¯|(𞥩j3Iquus¡XM<óÊÅä’YÎîÄ–f$’I'5çß<%á_üH—þ-ËGðÃ/?LÒ,,-ã¶²mNä—½–8bUŒ÷áp]‹ƒ’kÕ>4ø×Pð‡ƒþËáÁæx“Ä&™¤Æ:›«Ÿ”Iì±.\“ÀÀ­u<§ü<ðn—á 4ïM> ¯)ûÓLÇt²·»¹,~¸¯_ûŒ<«}©^+Óí?ý·æû•=úНô_¯Üy§ü2wì³ÿDoÁŸøOißüb½ÞÒÒÖÂÖRÞÚÝ8¢B$hƒ ª£(«Wu…Q@Q@Q@aã_‚?>%j‘kŸ¼áÿj0B-ã¹Õt«KéÒfqÉ(ñ·ÃO øƒY¼Ùçßj5•ÝÔ¾ZÓ|ÓDÎÛQUW'…¯_¢€Iû\itoÚÅúwì÷p.< ñ}ˆ£´‘+˜PÜ$.ùf‰f.’xà0Oœÿ™øÃÿB.½ÿ‚˯þ7Gü)ŸŒ?ô"ëßø,ºÿãtæ´W¥™øÃÿB.½ÿ‚˯þ7Gü)ŸŒ?ô"ëßø,ºÿãtæ´W¥™øÃÿB.½ÿ‚˯þ7Gü)ŸŒ?ô"ëßø,ºÿãtî¿°'üž?Âû Eÿ 5gµü«ÿÁ6ÿezçí'áˆÚÇ…uÃ^¸ûuÍî¡k-¬r:© >j©‘ØžBçh屯ªŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š)­ÖMn´1¡O¿/ÿm\Âu¢çþ}¿ös_¨ 2+ó öÔÿ‘ÿBã#ìÃÿC5ñ“…±qùŸW|=×$Óü¦Áàù1œÿÀE~ZÁD5“y®ékrvF3×5úã¢hqÍà­ktÚëm8r‚¾Døçû/èõnõ»Ç¶šÔü¡Y—¾{V8L]Jß —äm^„+)Û{³êoÙÞ8åøC¡+ †´‹¯ýsZüŽýµtØì?hÿù_òÐíûÊýwøOemàÚxLJd†Î5X’I ÷úW•üXýü+ñsÇÖ?¿¾x§±U Ø?6îÜWÔR«Gá¹áÊ3£$™.éðÆÁSö?ý§_œ_±^ ‹ûAké)ùáSµkõß]ø{Ÿ‡B€³[¤^Vüœ·oÖ¾Cøeû+ø{áŽ/|m§j,÷sy¥Inïô¯›ÃW–rR=ªÔV*)ÀýÖôˆµw¨bóšüTÿ‚ZÉkâ=!%ùXtñ¾¿b´‰ñk{Áì}kÁhOÙ[Ãîlõ-Fé­¥³.Æažsü5éºPÄ%^žç›³£zSØóχšBx“àN¢Ê %Å ]§¡Ê üÆø_ã}[ö3øã¨[ø«zèZÍÁ“wEP0¾ÃÖ¿e|3áð‡iáˆ\¼Vh¨¬sÎÐ¥p_¾ ø⦞ö>!²‰å`GšP\úf¼Œi*5%›ž/ªRR‰¥¨þÕ¿µ ÂDšªye7 äœtë_„µÇÛÿ‹_tø´xÏö]¬›UŽy]àû×駇ÿà˜~]TjQx’æ[`ÁþÍçK³¯M¹Çé^áã¯ø'ïÃéÖ6öJ,e±P7 *ÌAÏ%pM}5L=)¯m¹ãG(µ lYøe.¯yû?ÙiÚTí Ä–k±—¨ùF1_|ý«|KðOâæ³áOwÓ‹;™ÏÙÞSÀ\cŒã¹¯ÔÏ x./‡ÚŸ…bc46‘¬`œä…êkÇ~)þÍ_þ.¸þÛµŽÞqÀ™Tÿ´kÁ˳gJ¬©ÕZ®+êCž™é:÷í)ð^÷D:âjñùAKà2äŒ}kðãâ>£gûCþÒz=Çû<ÚiòŸ6U~u9ã# ¯Ð#þ aá¿‚x²ò[rÙòMÄå1é´¶?Jû;á÷ì±ðÿáNš£@²ˆÝ(æ]ƒ{©¯k†½­­7_ÞP¨vŸ ¯'ðχ¬4[ÂÍä"¡cÛ½Ö£¸ŒH‡ Šð)c– 7Y{êôIhƒ”Ñ®uK-P\βF…H'ð9¯%ýš?kˆÿüKrÃH[m#„—æ‡>¼UÏÿÁ/>è·Q_êúôúÔ`ƒ²ie‘qôrkìá‡>XÇaá«àF3üð+LÃ+ÚaÑž æýœÙùiÿ"Ò5I|EáïÄ%®›"Ë*ö–&¾Âýÿkƒ'øe§hׂ[ÞZǶDb£@sí^ßãh9ÒeÑüCiÄ2©C½Cc?Q_]ÿÁ0>øƒZmkBñÆ—¸†1C,±¯¸Â+«*Ì#‰ƒ¥W~ÆyŽ Ñ—´[þѵ—„¡Ðï¼#ðöc¨ê—*ѦÞrÇ÷M~|þÂºŽ±¤|uÔõ£[ÝÝÈ CÁå˜÷ú×í¿ÂÿØ»á§ÃÛ%K¸U»QÄòì®\^7qûøgÃåø…cxð¼¿ËV`¼qÇzŒM(a¢ùèUöÒQ‘·ûl|*ºø·ðÖ]OEŒ>©¦@Z5K ‘ë_þʵw‡<%¥´ø›+XjZsøÏ8ïô¯ÕkY|€Uó™[Ãñ¯ž>.~Ä? þ:9Ô­‚èÚƒòòA˜›?TÔ`ñÅAÓ­¹xŒ<°òæŽÇ›|cý¯¾øWÃ1h·ÂãP™„)±éÐú׿7ìsãcÄÿ´ÅçŠ5½Èn¥)éìG_­~žø/þ §àoH//u)5¶‹œ\3ËÓÓÌ&¬xkö3ðgƒ>!Éã½.é ™ˆÄ °Q´“Ó§zÎ3†ÙuÓx”}?ûPü*O‹ß u"Ðî¹û3ˆäçiÅ~JþÌÿ´'ü3¾¥sðŸây{H휈œŽ'ñØWíÆƒ®˜1mvA@6€GWŽühý’>|o·7W–‘Z^H?ׯ¡[óQšôiΞ2—)Èá<5K½??µOÂ{-?û_û`,J»ñ•Ï󯜬ÿà£ZÇŒ>&Zxᦊš­«Êç}Ü‘Ÿ”ãµfêðJX]}º?ÝÉoœ˜ŒÓǦ7b¾–øIû8ü=øE徑e—‘ã3‰òFkÊHà'Ë«;Ü)^'пgº¿ø¬]jqç–ÍÙ•zå·­~_þÁú[ɯkrÂ3ŽñúýYÕíìüyá‹ßê.bŠò&ˆ°8 0#·Ö¼ãà7ì¹á¿×W·šEÛ]›Î»Ù›çø«Û©ìñ”Z‰çAË U6uºÉò²óùW…þÒ·óÞø:'a?ú }]â/”cuj1Áâ¾UøðŸñJ2È>až¿Jüçˆ(TÃÓœ:YŸc”T…j‘—[žIûÿ Žª=þ‚kõÇg£·ˆô}ñY}škpLâäüâIÞ|Æã·§Í_»´P@#¿ÿgø'gÄÿ…·üÿ/|<ü+ÐoõKýô]Ú°u¸ŠE³‡ìë)¸S ˜ ´J3s‚3ûÁEcxŠÿTÒü?©êz˜ÚÖ¥ik4Ö¶ ,p=Üñ¡hàYe"8ÌŒrs– _ÏV•ðþ iûd\þØ:¿Á }_Q’öêê.oé+I%«ÙÛÇç ¼“oL6ÎJÛú.¢€?%>'|Tÿ‚ªxËÁÚ†|ðNð}ö¥ Áý¥ˆ´Ë«‹uq‚ðw¤€}×!¶ž@ÎöOø&¿ìããOÙ»ö¸Ð¾&é£Jñv¿«Ýj7Ðá¹xÓ e·y#l¤^`ÚçÎprè=ò§í½ðóÅ_ÿeoˆ?ü§6­®êÖp-¥ª2#K$WPË´*£„'’+ãoø%‡ìgâïÙïCñ?Ä‹Ú)Ñüi¯¸Óí­dxå’×M„«±Ý2ƒq. ?,H{‘_®”PæoüCà‡ÅO?|7á_„z¾#Õ¬|Ko{5¼2E%ºYÝÄÒfg@@y`óÓ¯Gÿ‚{þËû/|³Ò&kÞ5ðÇumwá×…ÛÆž$µ~Ť-Ü6j‘ÝPƒqpV8Õ—bNHR,@?†±—ìãûnüýªuŽ~jÇã¶[jw+®i(lßT¼Šâ{ÕUº•äòö¶cQ¹•ˆ^pôEQE~FþÚ±/ÅßütÐ?k¿ÙjkSãÝí$¼Ó.¤Xì¶XŽ)RIc;¡ ±»Æ­ðÛ‰ñ¿Åø*ÅïIðóÁŸ¬~ß_/Ù¯|Cq¬Û\†í€bÑäpv^ªCa—õÊŠøö ý„¼%û#h—zΣxž$ø®FPÕv޲ÛZ†ù„[Æ]Û )°PWí¯ø_EñÇ…uŸø’´é:ý•ÆŸyJù–÷Q´R®G#(ÄdWAE~|ø!ûrÁ>ü]â­áwí>2|:׿ŽxÄZŒ6 ,yXäÙ+oŽR„,ÀE"6Õ!Æ+ãßÁÏø(ÇíËâM Bøàk?‡4Û¸¦6'Tµn'l“Êc’I¦•cf-AÀ,Ç÷ÒŠçõ‰o|=ákéü1¤VïL²•¬´Ø¥ŽÜÜÉ dÃn’JDqù„ ä*ç$€ ?^ýŸo»Ûe?lü]MÛSží´±âDvòZ½œ1¬Æé²ÖñÚÛ>bƒž?¢Z(‚ø¯øßÂþյ߇>>4ñ-´iö- ^Acö™Õn. ÆŠŠK¶NHR,@¯Ã¯Ø›ömý¶~ ~Õ·Ÿ>)|)öÞ3û]®«{ýµ¥buKÈ®'¾X㹕äòö6cUÜÊÄœgú¢€>vøážÕ|3ñªÉOüSWÓT 2dÒ¯˜G) rÞK•u¹5øñûrCsû3ÁD¾þÕQ¦|7âg±–îåFS6È4ûädgì-©îX㡯߽sFÓüG¢ßè´~u–£–ó'÷£•J°úàñ_ k¿¼/ûQüÖÿgв4Zç‚nÍ•¾ ŠâÖX›ØÕº¬6ÖRFôÜ2 zÒýöK­=?í×·Üïÿ#‘{•mÒ_šÿ5ù3ïK{‹{»x®í%Y ™Uã‘2:0ʲ°à‚9u¯øíã_~ðå÷Àï†éñ+V¸¹ò§´“W¶Ò´;¼ï2ç哿v)œô¿;ÿf¿þÖ¿²>»á¯Ù‹ã§€/¼oà©oaÒô/讣µ†âA)rNÁ?òÔÄñ (°5ägóWñCöpÿ‚˜|Ný¥ìj ¿…v Ò/tû»Hõ}%íaaF†6ݽԔ̇p,Y±´`ÛÙ÷âOí=ã[ûí?ãÿÁè>Ákl²A}o®Újiu9`1¹wˆËng#·Zú’Š«}}e¥ØÜjZ”ékiiÍ4Ò°HãŽ0Yݘð@$“ÀøÿíÓõ/Ú?öêø§ûY\Û²hzl—¦ÉÙq‰µ0ZÅ“ÔÇdŽ‚W8Ü+ß?hŸ~Öß¶–±®þÍÿ<}ðïáìòéúçŠuÐÖÆö+ILrǯßÝ~ìFF”£Bàþþϼû6|/Ó>|?·Ùif<Ë›—Ͼ»p·3ÕßÑT*/Ê P·QEQEWüTñܼ{â‹í7í¶ÚÂØr׳DäåŽN9Ú í^‰_7YÅÙøÑ.¦{á†îÐAÝ.u¹÷¯èE²£<‡9½¶„e7R§Á_Ÿeóz~=lMF—,wz/óù‰ðÀ’|?ð]¾—¨KöfõÞûT¹<´÷×'|ÎOpʧûª+Óh¢¹1åVr©=Þ¦Ôé¨EF; ¢Š+Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼W㟅µMcÂÐx§ÂéŸø>uÕtüuÂ?}Ç%f*W¹Û^ÕEta12£R5#Óñî½Ìε%8¸³›ðŠt¿ø_KñnŒûìõHtç%w™ý¤l«{ƒ]%|áðóþ-ŸÅ sá<ß»Ñõß3[г¦óþ™j½†Çýâ¨è¤šú>¶Ì0ѧSÜø^«Ñþ«gæ™j®Q÷·Z?P¢Š+„Ü(¢Š(¢Š(¢Š(¢Š(¢Š+0kZ1Õχ…ý¹Õ?h6žj}£È Ìò³¿fâìc$ ×åÇíÝû]üBÑ|m¢þÈŸ²è{ŠÞ,xRæî ¤é–ó®õT, ¤¯dyÄ0üÿy•“Oáÿì•áoÙ'A:ƃªßx¿ãÄ…þÅ}vúâGr÷EêH“?,1l“|›‚îlŒ.UªFœwÕýìέU¹3Ü­¾0|%Ö~7ê^,ñß´=ÇÂún‰m¨êVÖ²M4‡måêÇ4ŠÅI_%¬Ç ×Óžø¡ðÓâ×6þñn‘âIlÕ^tÓ/íï%rB—»•ƒ‚qœWóíñÃáG‚!ÿ‚¢|0øKªéjÞz=¥Å­Ôaâ¹_&Vs* wË·¹¯ÞŸ†ÿ> |¹¾½ø[àÍ/ÂÓêH‘ܾŸl4É%ʤ’>µ¶a‰Jžçâý^ïͲ0ôœcïnõ~¿Ö‡¬Ñ_’¿?oŸŠ¾"—Æ>ý‹¼âCÁ‚ê=oÄ—–¦-?M{@ÞzCû³&ÖÀŒ•ùb”óÿø#×ʼnã'‰¾%ø›Pñ-ü·š;‡¾¸y–6t»ßå#‘ƒ…P*áT€á7?j¨¢Š(¯ÎoÚöÛñv‘ãí[ö}ý–¾ßüFø¡¥ª‹æ’‡KÒŒ¨âG1ïÈe#çŽ3‘‰IÊ×ÿðK¿ßþ2þÖoþ2øŸPÕ¯¡ðôë%œò”µ¶š;ûdÛªb¶|Ê6(êÝÉ$÷§XÖtiw:Þ¿}™§Y!–{›™V!z¼’9 ª;’@¯(ÿ†’ýè©øWÿ–?üz½7ľÐg–¡cž=ÅTH @Þj*–›¨Øku®­¥\%Ý•ìI<FÁ’H¤PÈꬤ{Š»@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@;xþ(o~.ðüšŠ|E§á1ß ÿh¸WÀè}ëèšùïãô2ø~×ßìP´þ Ô[£,úmÞ »@;ü¥[Ûi5ô3Eq \@âHåPÊÊrXd}¯W1ýä)â;«?Xéø«?VÎL7ºåO¶«ÑÿÁ¹%Q^QÖQEQEQEQEQEQEQEQEQEQEQEQEó¯‹ÿâ€øßá¿'îô¿Çýƒ¨Š.Ó2XÈ}Y¾h³ØWÑUæß|þ?øy¬xrÔì¿x„öR†Žò܉``ݾuŸBkÐÊëFyj|2÷_£ëòßäsâ Ü/Ö«åþ{“Ey÷¿§Ä/hÞ+#eÅÜncÆ wQèGQ‰±žØ¯A®JôeNr§=Ó³ùÓš”T–Ì(¢‘YXS{ŠÈ±h¢Š(¤, €N à{ÒÐEPEPEPEPEPEPEPEPEPEPEPEPEPEPÿÓýü¢Š(¢Š(®sÅÞ-ðç€ü1©øËÅ÷ñéz.ÜÝÜÌp‘EÉcßè$ð9®Ž¿>ÿà¨sKìWã‘”óÅ[SuÛŠðÍWþ Eû,ØjÙØøÅz”0±U¹†ÊÍ#”âA-âHí¹TúYÿðúßÙ“þ…à&ŸÿÉõüÅWØZŸì1û@é³ü_´æ“øRHì¨MÒZ9ÂÎÑvCœõÈâ€?lÿáõ¿³'ý >0ÿÀM?ÿ“èÿ‡ÖþÌŸô(øÃÿ4ÿþO¯æ£Â¾×·ödÿ¡GÆø §ÿò}~~ϳ§Ä¿ÚkÇ/à†‘\_Ál÷“É<‚(aT»±õfUrI®KâïÂo|ø…«ü0ø‡f,uÝÑfE`èË",‘º8á•уïë@Ñgü>·ödÿ¡GÆø §ÿò}ðúßÙ“þ…à&ŸÿÉõüÅQ@Ó¯ü>·ödÿ¡GÆø §ÿò}ðúßÙ“þ…à&ŸÿÉõüÅQ@ØìÕÿýžÿj¿‚<Ú–‡â,6ZÄB÷(ƒ.ax&ž6*9*X69€M}Û_Æ7ì4°þÙ ŒNP¾±œeYô5ýœÐEPEPEPEdkž Ð|1¦M­x—R¶Òtûq™nnæH!AêÒHUGâkÂWöÀý”žûû9~0xLÌê5g³þþy»3ÿ £h¬}Ä^ñV•¹á}N×XÓnFbº³™. ë$e”þ¶(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦ŸJu0BÞ¿0mS·ÇºÀ¶çþû5ú|xùÃûfZFúö›rÃæK~?ï£_ÆëýŒúNÿ{‰ößÃ+Èõiez xÔþ7Ä^ºêØ|ÝN+à܈žÓ· è"½d„‘á¯SBð‹ìpâ+JŽ&Mw>}>j>Êêk¼ðïˆöâÖsȧx—A976ËùW7FÜpâ¾[ßÁÕvG¶ùq4îÏ b¹‡iÃ+Šó/xtÛ±¸·^¼UxˆÆÂÖèõèkÑ ÃuÓ‡VõR™p¤œ¡¯_Ó585C¡Ëc‘^#4[Ê`˜r¾µ§¤j’é÷ ÀáOZáʳ)açì§±Ù˜`#V<ðÜôÍoE‹Q…F$ä—v’ÚLc¸ô5î7ÐßB$‡=«'\ÐâÔ"fUù…{9®WÐö´·<¼»éKÙÔØóÝ[›N™Vc˜ó^·iyìBXÎA¯ »´šÎc«ÀéZº&µ>Ÿ0Žc5ååy›¡?eWc»€UW´ÛxƒÃ©x¦âÝq çŠòù"x¥hÜmd<×¼YÝÅyhˆ9Ëø‹ÃñÞ!¹vȽ‡zïÍòµQ{Z'&_˜:oÙOc‘Ðõùìd 1ÌD׬ZÜÃw–3kÁ%ŽHd1Ê0Gjè´=r]:a¶èÏé\YNléKÙÕ:ó ²3^Ò™ÝkÚwñ´ˆ>q^Kso5¤¦7H¯x¶ºŠî%’3¸0Í`kÚ wÑpàv¯O6Ê£]{H9~`é>Iœw‡uö´u†sòz\Ñ[êû[æ8¯ š)-¥0È0Ã¥uþñÛ•·ºo”ô5çå9Ÿ/îk™†›÷ÔÌíwDšÂRñ.c'µbYÞKc0–2p"½ÆX`Ô-°pÁ…y^¹¡Ëa!•äjË4Ë¥F^Ú‘x z¨½•CдMf BÈÝéWµ+/¡(ã>•â–²ÙL&ˆœwë:6¹ôAY†þ˜¯c-Ìa^ΦçÀÊ”½¤6<ÇUÒ§Ó&!Æc= Wӯ气Kùs^ϨiÐßÂÑÈ3žõä¶“6›9È;3^&e—N„ÕHlz˜,tjÇÙÌõ]'VƒS·#v9¬oxu.c3B>q^{§ê3ió‰cû¹é^»¥jÖúŒîQ^® U?gSsÎÅa§‡Ÿ<6—hg„|Þ+Ìe‰à”Ç0!×¥xuèUÁUæ†Ç­ ÐÅSå{žõ˜o-ÿ¼¬+ÌõÿKµÅ¨ù{Šf…¯ÉhÂ+ƒòW¦G%½ô;— ­_ERÇÒ×sÆpž§‘àË#Äá—†«Ñ|?â(÷UxlÄÍuj¹’q+¾Ê’¬+ç©Ê¦ ³¾Ç³(ÓÅGMÏeI£Áä5|ûMè«…¤º„c³ùW¾è%RÞé±ÛšòÚ]‘ü+/;ƒ*ô³ú´ñ8 Éogù~SÑÅÂ/ºüψÿdèîmüQv‡ü~©ønõ¯,Ç™Ô üçý‘tˆõ-Bôç,äkô«L°O€CnõãpJŸ?Kž·b#*¼½KÿÅO¦ž´êý,ø†QE (¯øëâOFðŒ^ðËcÄ~/t?”iø–sŽBÃæ-Øâº0˜iV©qëý_å¹jªr}oáŸü\‰^!ø½7ï4½7~‡¡g•h¡lÝܯcæËò«v©¾Ž®oÂÓ<á}/Âz:í³Ò HŒÚ9vÿiŽY½É®’¶Ìq1«U¸|+Eè¶ÿ7æÙjN1÷·z¿P¢Š+„Ü(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+çOÿÅ»øÑáïGû½'Æ º©ÙVèeìforwDIà.+èºà¾'ø&ˆ~Ö<%#yrÞÂM¼½ W1ðÈäm‘TœvȯC,¯UµO†Z?G×åºóG>*›”=ÝÖ«úóØïh¯1ø=ãi¼{ðÿMÖµòµXYê1+Ûcå̤vË Àz^\¸Š¥RTçºv5§QN*KfQEbXQEQEQEäß|o}àϘ¼>¾w‰5Ù“MÒaî×w*¹ÿf!—bxã­t?<cðëÁšg„¬[Í6qæyÞžáÎé¥by%Ü“ÏAÚ¼›Áñu~*ê_¦ýç‡ü(fÒt yIn·kë“û¤aÁP{ŠúF½|wîiG ·Þ^½ýº¿Î:ü^›/Nÿ?É ¢Š+È;Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( øéá}SU𽿋¼.™ñ'ƒg¥†:Ê"¿·8ä¬ÑeHNÑ^“ái~5ðÆ™â½÷Ùê$ñú®áÊ·ûJr¬;Etuó‡ÃŸø¶Ÿuß„“~ïGÖ|ÍoAÏ «#¦Z¯aåÉóªá$šõ©~û éý¨j¿Ãö—Ëü䟹UK¤´~½?Ëî>¢Š+É:Š( Š( Š( Š( ¡¸¸†ÒÞK«—Å ³»ªŒ’}€©«ø‡ÄÞñ46ŸëäÓ/V<|ÂáZü$ÿ‚ZÅ'ÇßÚãã'í;âRf½´ öÊüù/­Ï/—´žžU½»B£û­ŽÕú÷ðçþ.Ľcâìÿ¼Ñ´o7FðþyWTl^]¯oÞ8òÕ‡ð‚ ~ÿÁ*üWyoàŠŸ<&Û\¶ ºB?Þ¥OmÙ¯­>É/ðý´ÿý'JóOÚêâÖÛöUøÃ-à3áqp{³ØÊ©ÿ@3ÿÁ)þ&ÞüGýt-Nc=߃on´v9c¾ÉíÔû$ÇöQ_¤øÙÿOŠà~Ξ4™¿Ô?ŠæTÿ}lm ~…köN€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€2µÍÇÄZ-þª'™g©A-´ËëªQ‡äkÈ?gÍfúãÀá m÷ë º›Eº'« Sˆdóµ¡)ƒß½Ò¾v¸ÿŠö†‚çîiŸì|—þèÔôÅÊØo·%GrEz¸ÞQ©C¯Ä½c¿þKwòG%vq©ò?ø6ûÏ¢h¢Šòް¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ç_ÿÅñ“Å$ýÞ›âUÿ„‹LÂ$r#¾ˆ{ù\(褚ú*¾~øÿgs¤iZ/Å}*3%ÿ/VòEO½&Ÿ6"½Œ}c;³Ûm{Å审gý”‚k{˜ÖXÝyWG•‡±5ëf?¼…k?íIt½râ,E)à²ÿjÿòÍr¡ÿ„ú}áù©û;~ÑzÿÁ=zO†Rxô(çh˜L¬gÓ&-ó§“<ºŽŸy{†ýx¯”¿i?Ù«JøÇ¦6¿ ¬v>.³L~T»Eé Çײ?ðô?/OÏòÜCœq˜I{ðéÑ®Öþ¯ëcöÏ x¯,… ¹wI}^³¿:^ô%²wÞËð»Ý6©lï-5 Hoì&K‹k„Y"–6ŽŽ2¬¬2#Erþ;ñ߆~øfïÅž,»–ƒêò9û±Æ¿ÄíØ~'?+¾~Ñþ#øª]ü8ø›iu&‰hò!€®n¬'$F¬@1¹ê¹ÆNõ<Üλ¯|Pý²~(Ã¥iq]2Ø“ “m§Û†šR>ôÜõc…QŒçÔã8J‚ö1½g§/gçåýi­¾“à."Žc7¬£‚çun­(tKyõè·Wº¾ô>8øÑûRüjÓï|<º$,¾u¡‘¦Aœe#‡‘ÇóýÀ6týƒ·I£·Š;‰<éUT;íÛ¹€å°:dóŽÕç? ~ø[á…!ð¿†"ÏG¹¹p<ë™±ƒ#‘ÿޝESé•íd9]L<%:ór©=_oEý~Áø‘Æ8\ε,>]ESÃPN0ÓÞk«o}^¶}Ûz¶QE{ÇæÁEPEPEPEPEPEPEPEPEPEPEPEPEPÿÔýü¢Š(¢Š(¯Ï¿ø*2ËûøèÄ…Äm`Íœ/Ú£>Ü×è%s¾.ð—‡’-¬Py—h’V˜—}ª Î8à ýÚÿ‡/~Í_ô1øþÿÛÿñŠ?áËß³Wý ~#ÿ¿öÿüb€?ŸŸÙïöŒø›û1øåþ |-º† B{g³ž+˜¼ëyàvW)"eI•XÀ‚r_~,øßã‡Ä=_âÄKÑ}®ëN3ªãU8ã ˆŠª£Ðd’rOôgÿ^ýš¿ècñýÿ·ÿã׿f¯úüGÿíÿøÅ0tWôùÿ^ýš¿ècñýÿ·ÿã׿f¯úüGÿíÿøÅ0tWôùÿ^ýš¿ècñýÿ·ÿã׿f¯úüGÿíÿøÅ~#~ÀË7í‘ð¨D…Êk±ÀÎQ‰'ØWös_ þÍ¿ðOO€³'ŠÇ†óWñ–ÑCy©J²µº8ÃyJŠˆ¤Ž c8ã5÷UQEQEá¿´Ç þÍ¿5ß‹>,tZ\a-mU¶½åì¿,èpq½¾ó`í@ÌA kÜ«ðþ oãÝJâûá =ÜÃ(¼Ö. ^D³3-­©Çª<÷èÂ~ü ý¤ਫ਼6Ô>-|cñLÚ€të§ŠEf‚7êÖº]«6Á±v‰&rNq¸Èû€ýðF¯Ù3û$XCÄßj ƒwöø<ÒØë·ì¾_^q²¿Bþü,Ò~ üð‡ÂÍ$ŽiÐ[HPæÜÝq1Ç¥˜¼Œ}X׬Ðóñƒà¿í ÿªø‰¤|TøIâ™5ßê÷Ü´ÈR ¤Áse¨Ú«%‘IŽT ¤¯–Ãý|ø¹á¿Žÿ <5ñkÂ{—Nñ¢Î#~^ T˜æ…Ïh¥WŒ‘Á+‘Æ+¯ño‚ü!ãí¼=ãËÄ[IÍioÌHXñ Öò-µÛîSÜ×’ü Ó×Uð N3ª/×]l°Éo!ŽO”£b¾{Š­F”*ÇTÏgF•YÊ“Ý÷ˆ®âìʼëÄ>1±žØqMðïˆ -®ËØæ½#1\Gž[½}‚öXêWê|ÛçÂÔ·Cçì:7¦»ï x€’-§<ôæ™â¼dÜÛ:œW ¥¡“r|®µòñö¸J·{ó•€tŽâ2­ó+ ó/øq­ä76Ãä=«Sþ"óÛ\uõ®æDŽæ-‡•5õS…,m?3À‹©…©®ÇÏÀÉîCµ”×£øoÄlZÜ}ë7Ä^{rn ’q±Èð¸x¾WZùEV¦ v–Ç¿% U=7=g\Ð!ÔË€ÏëË.íe³”Ç8Áèþ×…Ú {“‡V޹¡Å¨Ä]ß^Æ/OMU¢µ<Ì6*xyû:›q£êóé³[1šõÛ B+øD‘ž{Šñ;Ûìeò¥Z·¥ê×t·es\Yfk<<½•]ŽÜv5ãí)îzf¹¢E uq^Oyk-œ†F= {F›©Ã¨À=ÅPÖ´D¾…Џt¯W1Ëiׇ´¥¹æ`±²¤ýœÏ;Ñ5¹´ÙnÛþ•ëvwÞÂ$C¸ðë»I,äh¦­mZ—M˜+’c5åå™´°òö5¶= vU´§¹Úø‡Ãñ^!šÝq&+˦ŽHÃ0Úë^ëkw übXˆæ¹ÍÃÉw¸€ öë^Žm•Æ´}­N<=Ó|•CÃú䶈el£ʽj£ž0èr xÐMm&ÉÆ÷®«@ñZH Ÿ%OÍqe™«¥îUgVc—ª‹ÚS:h v†xFW–Ͷò”“åe5ï°ÍÂŒîS\‡‰t¹ŒÏÇ=+¿7ÊcQ{Z{œ™vbâýœö2¼7âL[\·k½¸·†ú ¬8¯u– Ÿ•Ó¥z†¼@Oú5Éçµråy²þslÃËûÚG?­èSiò´±.PÖ%­Ü¶“ `c‘ÔWº\ÛÃ{ V†¯'×´9¬%/ùIëXf¹\©KÛRØÛŽU³¨wÚ½üa$8qZšŽü-‹ÔpkÄ-®e´›Í…±Žµë:¹ü!á…z9^kBöUw8±Ø R—<6<ÛVÒfÓee+˜û¯§êXJ²DÇÅ{>¡§C¨Bcp9ñýWI›M¸eÇÉëÚ¼ÜË. ûZhïÀccR>ΡêúN­§änÇJÊ×|;ê4ÐŒH+Í´ýB{ DˆH潇JÔâÔ VSócšô°8èb©rÔÜàÅa§†Ÿ<6 ’|þ"׿KÒ1»º;Uý„k—'§Ê3ֺ߇þ Óþø7Ið~™óC¦@±—Æ ’šI»¹,}ÍzÔ?s†•_µ?uz}§ù/›9*{õ:-_¯OóûŽÂŠ(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>tÒâÜüyÔ4#û½â4-¨ZŽŠš­¢rƒÞX¶ÈIêF}^/ñãÃŽ»àfÖ¼<¿ñ?ð¬ñëq$Íió4x‘${—oBHÍz/„xbc¹ã™ZÓÌ^¶Ö7^\÷cùGL–ã‘]X<+­USN×Ýö[·òZ™V«É#ðÇþ Ï}áŸÿ·ß~ÜíþÌñZ¦u(Ùæ¬ý¢ÍÓw¾µWî!GR+úH¯ÍÛ3öÓþ7ø{Ã^+ø5xžø“àa‹G» bKˆ-X<0Í*èñ¸ß  ¬X0!·/oû&~Ñ?ø$2‚Ùü(øUào‚~Ò~ü9ÓWKдxöE%ÙŽç–G<¼’1,ìz“é@>~п?l? k²è?³ÏÁkOX=šIµy®ÙÚGË– YM$2°L)Ü$³€x5øùû.þΟðROÙ‡ã©ñ{IøEmâ ~Þâ×RµºÖô˜–t¹™.‘ã½ýÛ‰#RÖÈÛÏÒUå|UñOÆ^ Mgã—áçˆLòÆÚZjPj£É\l—íà'Ï“òòF9<×äÏüóþIWÃ_û ]ÿé5~ÈxóÆšOïë9×c¸›OÐížêt´…§£Œdˆâ^]½ë_ÏwüÃöðŸíKà_xáG‡¼I=Þ‰©Our/4k›uÉÅÚH99í@Яù%þÿ°6Ÿÿ¤é_ ÁU~.Úü2ý’uÝ)‘uoM‹k#y‰Ûͺp½Jˆcd'¢³¯¨èÏ|Q¼øû2èÞ=‡Âš¯‹ï4Ý'I†='J„Éy4ó¤P"íÁ`¡Øo!Y”d…8Å~}ü>ý–~9þØ¿´ÿÚGöÓÑ"ð×…4%Bð[9vÚŒ~ÕÎÔfùå‰&`‘" ´õoükáÿÁ¿ÙÂ:^·[jÞ"óµÛ¸˜mdkò *èan±Ù¥}ßHªQ€8v¥ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼Oãÿ‡ïõo‡“ëzçZð¤ñkV'¿›bw²àrwǽqÜ‘^ÙMeWRŽ+ yéÁâ]±ªº;ÿÀù™Ö¦§ÔÅðLj,#=Ï*T—5Yl¿Wåýz~·áׇo6”ñ˜Éû,-g7¦ßf>oð¿VÒ|ÿˆfø…û]üZ»Ô<5£Ç¢ˆƒ µ¬YØn&ÇÌäœn<’v¨ ¿ìÙñ¶ëö|ñv¥àˆZq²Ó/nBÞ;E‹›+”±sÅŽ«ÎÎHoÔ…_ ¼)ð‡ÂÐøcÂð`p÷7.s60dÑz(àWŽ~Òß³f›ñJm@Hìü]c!”áRí¤3_î9û§ƒòôù§ÃºXês½{Ý®Žû¯ëåmÖaâîK˜T—â°ü™sJ–¼Ñki¿+Ù÷[»ÝŸSYÞZjߨL—ײE,leYXpA б_ÿ³Ÿí®|×_áÅš-9Ú&Sçi“îù¾^¦"ye>ò÷ úçð]AÍ´‹,3(tt!••†ApAƒ_[’çTñ´¹£¤–ëªgâ\À8¬ƒìj¾jrÖ[Išê¾z¦›–Š(¯dø0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÕýü¢Š(¢Š(¯”?mߌ&øû1ø×â/ƒb×m Ž 9™Cˆ%º•aóv¶A1†, ‚7FE}__žÿðTù2Ïÿ×Ký*Ž€?–-Oã·Æjþ}SUñεsurÅä‘ïç,Ìy$üÕCþÅŸúµüŸÿ‹¯7¯×¯xÿþ ÷7ìkámH°_‹bÎ$luuÕ$²^yctdãÌ(W #ó7þÅŸúµüŸÿ‹£þÅŸúµüŸÿ‹ª? .¼eñ#Â÷Ÿ­¤¼ðœ£êÐß2K•Lê¸ äÇ‘ÁЃÍ~ÁG|kûx¾ïÁ'öR°Ómîí œj’é §Û4,ìèñ˜â 2û›np@bOà¿ø\_èrÖ?ð:þ.ø\_èrÖ?ð:þ.¾žÿ‚~x›ögð¯Ç5/ÚŽÎÒëÃͧL–M¨Ú›Û¯‹¡Vž’Xp¥€Äê<›ö´Ö> kß´‹µoÙæÑl¼ qçv]ˆöU£>Çű‹Mç@ÔfÍÒ»§®ûļCsâAÎ=kòŸÁ^!¸ø?ñŽáµb³óäQžˆúÑáßi¾$Ó Ôl&Y`Áõ¯ál]*¸oªOâÿ6}7áêQ¯õškÝ•¿#ÈgŠH%Ù *Êk°ðÿˆ&IÒs•è+¨Ö¼=ú!“­r:o‡/…æéPª©ë]qÀâ0õ—³zÒÆQ¯KßÜõ"‰q:« ó½î§Îµ_s^ùqªz(eYÖæ¾¯‚UéÚ[Ÿ?†ÆJ”´>~dR÷GZô kÍpËk?ÞÅ.·á´nšÜ|þ‚ªøo@¹¶¹ܮҽ+æp¸LNºŠøn{XœM*´›{žƒ,I2qk̼Cá×·o´[ Žõê@qÏZŠh„Ècaké1ØÖ†»ž6*Rºz ’ÛÊ%Sµ”ò+Õü=«B HpÉÅsz×…î<ß6É Üy­ï èÓXÆZq†=«çòÌ=zuœ~ÉìcñªRMnjêº4Œ_0ÃõäÚ–—>!I”õ¯vâ²ïôøoâ(Às^¦i•*±æ‚Ôó°9ƒ¦ìÞ‡ŽéºœúdŠñ6T•ì:]òßÀ²ƒÉכ߸Rö9À¶åI®ÿDÓ[O¶Ç,z×I ð›§=ŽÌÎ¥)ÅJŒÕô;}B2@Ã×’_ØMe)†UùAà×½€+ VÑâÔ"l›±®ÜÛ+öžô79²ü{ƒ´ž‡šèzÌÖ,d“5ëÖò‹ˆVAЊò¤ð­ø» ßêíÁ¯R°€ÛÛ¬G°¬òl“M‹Í% >hnsúöƒìM$c+Ê®-ä¶Ç0#oC_@0+•×<>—щ~qéS›e.iʘòìÅÃÝ›Ðåü3­¼-¤­Ý+Ô>WQ‘¼«Iðåìw‘½ÊzתơT)ì+|Úû>ZÈË5äs½#ˆñ‡’Tk‹qó óOÞÛËÝ] }"«®Ó\¿á¦˜ùÖªK¸®,×*wu)£¯-Çí BÏ…õƺQo?$W]wiÜmƒ ×á½æÑüÛƒ^€"½\±MÑå¬;Ò«ÍȵÏÉbÞl#)Ô×;ku%¬žl$®Óȯzš§C£ ×™kœM¾År é^i•T§.z(õpY„fœjŽƒ«JýáÖ´u:øLrMbxsJ“OŒ4ƒk7Q]e}œ¥E*ËSÈÄÍB¥éžªéWl̬»¢= &—©Ë¦È[å&½RÓ¢½…£qœ×’jz-ÍŒ¥q¹ â¾c—T¡SÚRZî °ä¨zÖ¨E¨@žõ­è0ßD]Fw¬¯ Y\ÛÆÏ7Ý=+» }ké0Éâ(Z´O´½•Oqž{i=”Æ ×OZètzM>A §1šîu­ =B&*>~ƼžòÆâÆCãè{WËWÂÔÂÖs†Ç¿F¤1På{žåo,7‡ Œ+á/ÛBú='ö_c <Ò²°›kèí;Å–¾‚Iõ+…ŽÝ$±ã_˜ÿ´wıãÏoc?›§À~CÔnÉʼî'ÏéÏÉö™ÙÃÙEE‹¿DzOìµrísqb~R~‡¯Òï Mq-’¬Ã€8&¾1ý’¼èƒ\’< A:õ÷•­´vшâºø/© >Ö[3ŸŠñ”åUÁnY&–˜:Óëô4ŸSãBŠ+˾0øÞãÀ~¼Ôô´óµ›æK .ËK}tvB ¸?9šßBUjFœ7nÄT¨¡'²8-þ.wÆíGÅ ûÍáà“L±î’ê“ö¹GýrLEõ9ôup_ |oðïÀúW…":kX÷\ÍÔÍs)ß4„žNç$ŒóŒÕÞ×VeˆŒêrÓøc¢ô]~nïæe†¦ãËw«þ¼¶ (¢¼ó (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùOâíwðÏáô÷:>™æx‹X¶fà¶ù!ŽE8+$ì1ÁàìŽõùyñÇâ'ö5ß…|?ªM¡xz{»›˜¬m˥̆C ÊÆ ?.윊ý5ý ?f]âÕ¼¾ м½+ÅQ¯Ë>1ÖÑÂN~Â@2:À?"|Má}ÁºÝχ|Me&Ÿ¨Ú6Ù"`ûz2‘È`H#köžÃå•(þé^z9)jµµÑ¥}u>'=©ŠŒý÷îô±ú5û[‘ xÂõ¹3]Z¡'¾Äsÿ³×è%|5û[løk¯Þcýn®Ñçþ¹ÛÄözû–¿<ãófuŸšü‘ôY*¶×P¢Š+æOP(¢©j:–‘§Ü꺔ËoigÍ4Žp©jY˜û3M&Ý6x_Ç[PÖÿ²~ ønc«ã6dº•>õ¦•7SBëû´Ï IäW¸iNŸ éVz&“·²°… †5è‘Æ¡T átûßÝë¼A E{âÂ#Ób|Öº<'ý¡”þõñÁÊšú½\ɪj8Xýüä÷û¶ù7ÔäÃ.fê¾»ztû÷ (¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+›ñW‹ü1à"M{ź”:]Œgo™3csHUy˜€pª 8àWIXÞ ðî‡â½"çAñ”Z†Ÿv»e†eܬ;PAä‚ ƒZÑäç^Òü½m½¼‰Ÿ5Ÿ.çæǿڳLñ~©¢ËðÎÎ[;ß ÝµÍ®¯0Q/̆7Há!‡— #vÿ¼|‹®x»Äþ5×ãÖ|W©Ïª^;¨ó'rÛFﺣ¢¯¢¨vÑþÞ>ðŸì†µ¦j)©§ˆÞUÓ´©$ÅâÀ.Îpsd3ïd…Á?5~+ø‡ãÿÅoÜÉ7öäºlLr±XŸ³¬cÑYyø–&¿_¥Ä¹F„9;|׫{kÑ}ÇÆÏ+Æb*IÕv_‡Èþ娯ãOá×ü'öÁøk{oq¦üIÔµ›x3ZëOý©ª:£›ò…=>GVˆ¯è×öýº¼%û_ørîÂêÍ÷¢Š(¢Š(¢Š()¦†Ú..bŠ%.îÇ ª£$’z:×Ï?á›Çž$×>9jhDZ¶tí `Å¥[9ýàn%Èô³ñËR¾×ÿ±þ xzf‹Qñ›²ÞJŸzÛI‡›©O¡qûµÏ IkÝ4½6ÇFÓmt2·³±‰ †5û©jT{1^´q†¿Ú©øE?Õ«z'Ü㼫n‘üÿà/̽EW’vQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@yGįþ¤kãHÃu:!µŠ6–yTd*ŒFX¨÷¯W¯5ø¥ð«ÂŸü5'‡|M̹kk”ζ”¾‡ÿBSýˆíËý‡¶ÖoÉÖÛ˜â=§#öVæó?5~ þÖ—ú‡ÄEñßÃ=4è×+§>™$·efiá2 Qš%ùãlã,ý}8¬‚Þ4ñÏÅOÚ ã-jçVhîÞåRW>Rxž\¤KˆÓîÿ ŠòOŠÿ |Wð‡Ä x’ÑI–µ»@|›˜ûÈ{Æå<©ö Ÿdý‹4ß·|qµºÆ³ìnçúnQþÔ¯ÜñXL¶¥l4S\ŽÒÝìí®çÁR­^x˜Â«ºÿ€~ÄÑEüþ~†QEQEQEQEQEQEQEQEQEQEQEQEr¾5ñ~™à? ßx¯X†â{==7ȶ°´ÒíîBŽ€u,HP9$ üºøáû\ß|MÑgð‡tDÓ4¹%ŠQs;—¼Ý‹"<{0±® œgf¿[™UÔ£€U†<‚ ~nþÒ²Q‹í^=øQi”ù¥¼Ò¢;´–Ê;w1ŸÁýÑöÜ_/Ž!,\}ëû­½/éú½=<†!ÓýËÓªê|âŸxŸÆ×ÇQñn«s«\áî$i6ƒÙáG²€+öÿàÃß|8øu£é>³Ïsm Å܇™&¸’0]ݺžNèâ¿ !‚IîÙï$`€{“ŠþŒ- K[h­bû¢¢ý`W³âfaêFšR´•ì¯m4ôÔ\-›âþ¯W íeì›Rq»årÕ^Û^ÝI¨¢ŠüÀ÷Oñ·À†?|e¥øçÄÚXŸQÓÜÐ_õkr˜ýàCÊò3÷[rüµìÊ¡@U€jZ+ Xjp”¥¤å¿™èc3lV":5ê9Fš´Sm¨­ì»]QE¹ç…Q@Q@Q@Q@Q@Q@qÞø‰à}·þ_iž#þÌ—È»þͽ‚óìòÿr_%Ûcp~VÁ­?èÞ+ðƯákÉ¥·ƒY³¸²’Xd¨—´lÈݘʞƿ–/Œ?±Ÿísûx­¾+|,Õo5 O%“Äexa;u O›b|á„lœPõwE~~ÊßðX_ x—ì~ ý§lÓÃú›m5û(ØØLݺ€nx÷tÝI$F¢¿l´=wDñ6‘i¯øsP·ÕtËøÄ¶÷V²¬ÐMthäBU”úƒŠÕ¢Š(¢Š(¢Š(¢Š(¢Š(ÿÖýü¢Š(¢Š(¯š?l‚š¯í û;xÃá_‡î#¶ÕõKt’ɦ;b76ò,ÈŽÃ;UÊì-Ž3œb¾—¢€?‹Oöý¯t›ùôë„Úü²@Å ³yâ$wI#ÜŒ= ’ Pÿ†*ý­¿è‘ø—ÿÓñ5ý®Ñ@Åü1WímÿDÄ¿ø.›ÿ‰£þ«ö¶ÿ¢Gâ_üMÿÄ×ö»E¾/ý—?h¿ø~ëÅ~6øq®hš={»»)b†0ÇhÜì'å>ðŠ7²†‡â gµº„ðJ?FSü.Œ#U€aȮҊþkî¿gÿÛ‹þ »ñ#Tñìÿi?<¨6e[{g¾ŠâÞ2J.¡e&ŠX?¾‹ ŒáÀ,•ìIÿ~øáyn4M?ö}™üB ‹›Év1Ÿ²­  öóO¦{×ïuüäxköXý²ÿà¡t¿‰?µb\ø'ÀÖ@ùPóâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ÿðû¿ŠßôM´_ü ¹¯Ýoøf¿ÙÏþ‰_…?ðGaÿÆhÿ†jýœÿè•øSÿvüf€? á÷³ŸøVº/þ\Òø-ÏÅf? ´_ü ¹¯Ýøf¯ÙÏþ‰_…?ðGaÿÆhÿ†jýœÿè•øSÿv?üf“@0ÿà¢~'ø‰¨RïÁuŒäç1\LyüEhxþ qñƒÀ„.£Y\D½#–Yp?"+úgÿ†iýœ¿è•øSÿv?üf«Ëû/þÍ“ÿ¬øSáSôÑl‡òмGÃØE?ijþ­·öÞ%ÁSu=ÕÒÈü·ÿ‚Õüg‰%ð‰.3ç\‚ñêµÿ²øÁÛáîˆ?íâçük÷DþÊ?³ý àžÓÿÒÃ(þÌ_ôIü-ÿ‚{Oþ7^Œ0ê*É~,áuo×ðGá‡ü>Ïãýíÿ.Æ—þiñƒþ‰î‰ÿ?ã_¹ßðÊ?³ý àžÓÿÑÿ £û1Ñ'ð·þ í?øÝS¥~Ÿ‹'wüø^à¶_üÓÝÿ.Æ…ÿ‚Ù|`_ù§Ú'þ\ÿ~èÃ(þÌ_ôIü-ÿ‚{Oþ5Gü2ìÅÿDŸÂßø'´ÿãTý“íø±ûO?Á†þ gñ„ÿÍ>Ñ?ð"çühÿ‡Ùüaÿ¢}¢àEÏø×îü2ìÅÿDŸÂßø'´ÿãtÃ(þÌ_ôIü-ÿ‚{Oþ7G²òüX½¢ïø#ð¼ÿÁl~0ù§Ú'þ\ÿ(ÿ‚Ù|a>hŸøsþ5ûŸÿ £û1Ñ'ð·þ í?øÝðÊ?³ý àžÓÿRö>_‹´]ÿ~ÿÃìþ0ÿÑ=Ñ?ð"çühÿ‡Ùüaÿ¢{¢àEÏø×îwü2ìÅÿDŸÂßø'´ÿãtÃ(þÌ_ôIü-ÿ‚{Oþ5MSkeø±s®ÿ‚? ?áö__‡º'þ\ÿðû/ŒôO´Oü¹ÿýÏÿ†Qý˜¿è“ø[ÿöŸünøeÙ‹þ‰?…¿ðOiÿÆé{¶üXý¢ïø#ðÃþgñ‡þ‰ö‰ÿ?ãJà¶èŸhŸøsþ5ûÿ £û1Ñ'ð·þ í?øÝðÊ?³ý àžÓÿÓtŸoÅ‹wüø_ÿ²øÁÿD÷DÿÀ‹Ÿñ£þcñƒþ‰ö‰ÿ?ã_ºðÊ?³ý àžÓÿQÿ £û1Ñ'ð·þ í?øÝJ¡m—âÇíÁ†#þ iñ„ ½Ñ?ð"çüiüÏã òO´Aÿo?ã_¹ÿðÊ?³ý àžÓÿQÿ £û1Ñ'ð·þ í?øÝW³vµ¿.ußðGáIÿ‚×|d?ó h`×kŸþ*ø}wÆoút?ûýsÿÅWî·ü2ìÅÿDŸÂßø'´ÿãtÃ(þÌ_ôIü-ÿ‚{Oþ5YýYvüY^×ÏðGáOü>»ã7ý:ýþ¹ÿâ¨?ðZÿŒÇ¯€t?ûýsÿÅWî·ü2ìÅÿDŸÂßø'´ÿãtÃ(þÌ_ôIü-ÿ‚{Oþ7MáïºüX{_?Á…ðúߌ¿ô è÷úçÿŠ¥ðZïŒËÓÀ:ýþ¹ÿâ«÷_þGöbÿ¢OáoüÙÿñª?á”f/ú$þÿÁ=Ÿÿ£êË·âÃÚùþü*ÿ‡Øüfÿ¡Cÿ¿×?üU0ÿÁk¾2Ÿùt?ûýsÿÅWî¿ü2ìÅÿDŸÂßø'´ÿãTÃ(þÌ_ôIü-ÿ‚{Oþ7CÃßuø°U|ÿ~ø-oÆQÓÀ:ýþ¹ÿâ©ßðûŒÿô è÷úçÿНÝ_øeÙ‹þ‰?…¿ðOiÿƨÿ†Qý˜¿è“ø[ÿöŸüj—Õ—oҵóüøRàµÿÏüÈZýþ¹ÿâª'ÿ‚Ôüa|ÿô6ÿ¶×?üU~íÿÃ(þÌ_ôIü-ÿ‚{Oþ5Kÿ £û1Ñ'ð·þ í?øÝ§ºüX*ÖÙþü(‹þ [ñŠ<Å¿ÐÈ„÷#ÿf©ÿáöèŸhŸøsþ5ûŸÿ £û1Ñ'ð·þ í?øÕðÊ?³ý àžÓÿÕGed¿'Rý~ø-§Æ?äžhŸøsþ5ËkßðXï‹úä&àmß=Òk‚V¯ßoøeÙ‹þ‰?…¿ðOiÿÆèÿ†Qý˜¿è“ø[ÿöŸüj±¯€…EË8Ýz³J8—N\Ðv~ˆþcµÿø(¿Å_³}«IµŽ7ÎQ%“o?Záí?m/ÁwÍdž¬î[qFš@Øšþª¿á”f?ú%ÿÁ=§ÿ«Vÿ²ïì×jsŸ Œúè¶GùÄkÊÿU0-ÞTWÞÏIq-&£UýÈü ðü3â‚ôx´]#ᦊ!„`uq“ùWT?à·‡üÓmÿ®k÷Wþ«örÿ¢WáOüØÿñš_øf¯ÙÏþ‰_…?ðGaÿÆkèiQ8¨AY#Å©QÎ\ÒwgáOü>ïâ¶sÿ ×EÿÀ«š_ø}çÅoú&º/þ\×î¯ü3WìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3Z~Ãï>+Ñ5Ñð*æ¸þ ýñ+Äž3ð÷‹5Oi†ÄÒ[Y‹›(ÜÌ»<÷=K"ðƒøIÍBðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍkF´©¾h;=Wߣü œ•™øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍdQøUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹¯ ø¹ÿOñÆM),¼Kð¿F‚öÛþ=¯ º¸[ˆ{•É2êÙÆ5ýÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã5¾RŒÕZR´—TgV”g«£ùÐø3ÿaø‡ðg³øWJð•¨Eqw%ÛK5ÄêÛ¤DLaxÀŠõ¯ø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3F':ÕZ®ò{°¥J0Š„Vˆü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf°4? ¿á÷Ÿ¿èšè¿øs\/ÄOø,'ň¾o ^øJ²Ó®ÄWç‚6Ðî?u\€ŽqÇs_Ðü3_ìçÿD¯ÂŸø#°ÿã5]?fÙ¶;™nÓáW…D³V?ضD™Æ•×’(ZÁµ·Ã-(aPˆ‹srUFtt©á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍdÙgáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã4Ã5þÎôJü)ÿ‚;þ3@…_ðûÏŠßôMt_ü ¹£þyñ[þ‰®‹ÿW5û«ÿ ×û9ÿÑ+ð§þì?øÍðͳŸý¿ àŽÃÿŒÐáWü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4üyþÓ¿´?j_Š·¼ivRI6–¶034vÐ/Æ\–ùœ¼ŒOVsÐ`ž«êŸÛká5×ÁÚâƒZÅl4ùuIõ68£Â4û÷7ë¨ ²5/ À(WŒb¾V ½‡à'Æ¿þÏ?ô‹^ *ú†‰)-…„7PH¥&‚]¤²!#Øá‡ WW°þÏß µ_ŒŸ<ðÏG€ÜK¯j–ÐÈÜØ8{‰Xv8Uݽ”Ðêßü>óâ·ý]ÿ®hÿ‡Þ|Vÿ¢k¢ÿàUÍ~êÿÃ5þÎôJü)ÿ‚;þ3Gü3_ìçÿD¯ÂŸø#°ÿã4øUÿ¼ø­ÿD×EÿÀ«š?á÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍ~Ãï>+Ñ5Ñð*æø}çÅoú&º/þ\×î¯ü3_ìçÿD¯ÂŸø#°ÿã5ß³/ìáqÁ/¯ ”#ûÄpxê!Èü(@=ú/ü÷âV—ãsâ ×€4‹íOXŠX÷ÜÜ´´‡‘ @s†É<·øP*üHìOë ?þ¯ösÿ¢WáOüØñšÖµiTw“¾Ëä´DB *Èü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf²,ü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ çÓâüçÅ¿|7?…ü[ð¯D¸µ—æG[»…–âHŸkÈŽ y߆¿ðP´ø%oÄø~×WÖu8¤Óî¬/¦t6©¹%.­Ã2­€Î@<è·þ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ¯[ W£‡©…‹÷'Ó·š8ë`iΤjµª? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ¯$ì? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ æŸÄ?ðP»/xé|g­|?²ðõÈ>|ŸÙ÷<7êe‰×#Ì d#ûÅIɯ ÿá÷Ÿ¿èšè¿øs_º¿ðͳŸý¿ àŽÃÿŒÑÿ ×û9ÿÑ+ð§þì?øÍwâó:ÕéÂY]Böôg=,)ÊR‚µ÷? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ® ü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍðûÏŠßôMt_ü ¹¯Ý_øf¿ÙÏþ‰_…?ðGaÿÆhÿ†kýœÿè•øSÿvüf€? ¿á÷Ÿ¿èšè¿øsGü>óâ·ý]ÿ®k÷Wþ¯ösÿ¢WáOüØñš?ášÿg?ú%~ÿÁ‡ÿ ¯ø}çÅoú&º/þ\Ñÿ¼ø­ÿD×EÿÀ«šýÕÿ†kýœÿè•øSÿvüføf¿ÙÏþ‰_…?ðGaÿÆhð«þyñ[þ‰®‹ÿW4Ãï>+Ñ5Ñð*æ¿uášÿg?ú%~ÿÁ‡ÿ£þ¯ösÿ¢WáOüØñšü*ÿ‡Þ|Vÿ¢k¢ÿàUÍ#Án¾*2•o†š!‚ ÕίÝoøf¿ÙÏþ‰_…?ðGaÿÆkâÚƒãüëö\·ŸLñÃß kþ.UÌZ›¢éÒ]#*n_ÊÙl‡ƒ—;ÈåèùÏý þ4ø#ã~¼jóH^ìèóÊ-n·g,Ö®<´“8;£Ú;•‰ÈÖýlŽÿ²ö¦n~k§û.gßq£ß¸Ó.rÐnRŒxËÄÈä nÇÞx£]ø¿ûvüG‹Âÿþi5´2oƒKðÖ•mg ¬m•Þß,q“Çåu?qœWì7ì­ÿ†øwðûì~0ý¢n!ñ·ˆl‹¤Ã¸ií×n ÷D´> £Œûoö8ý­-kO‡òx¹Šk"7ÞP~¢›åEýÅüªJ(?*/î/åG•÷ò©( ü¨¿¸¿•9Qî¨AN¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ñŒ_³×Á¯ÚÓL±øÃá˜|IŒòÉh³K4B'˜(r<™ÁG\ô¯g¯Éø*ÿíñ›ö}ðÇû&—Ãsë7š„wm0Mæ¤1ÂP>9ÚXôÇ^hï_ƒ¿³ÀŸ€šž¡ðÂPxnãXŽ8®Þ®%2¤D²çHà`±é޵ï5ø£ÿ¡ý§þ<~Ð^,ø‡§üañdÞ$·Ñ¬¬%´I`·‡Êy¥•\ƒQ“£®zWíuQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Âø.ü‰¿ ÿëÿUÿÑVõû¯_…ð\?ù~ÿ×þ«ÿ¢­èËà‡¿ò<|Vÿ°v™ÿ£¦¯è¦¿oø!ïü¿ì¦èé«ú) Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ø×û7üý¢lô«Œ¾_Ûè’K-šµÍÍ·”óYm¥ˆœ…{=8¯p¯ÉŸø*·í%ñ¯ösðÏÃÍCàω×Ýæ¡ã [[¯5!ŽŒbê)Bà±û¸Îy ¸þ ~ÊÿÿgkÝSQø5áDð寵Q]ºÝ]Üù© fA‹™¥Ý¯5ô~"ÿÁ/ÿl¯Œ¼QãÛÚ ÇjVšUŒ–ê ’K$¢B¦¡/«Iǵ~ÅÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaEqÿð°üÿC6™ÿ°ÿñtÂÃðý ÚgþÃÿÅÐaECmsoyoݤ©<¨xäF ŽŒ2XpA‚:ÔÔQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Æø-ÃsÄ¿|)ñÂktÓ|¨J/#‘œLÿÚ^L1y*«a”—ÜËÓ=+öb¿4¿à­òež"ÿ°–•ÿ¥+@„²÷üÓã?ígà-Câ/ýk@ÓtÍ;S—J’=Vâê)Ìðà ìʰZλ6ΠÀä0?IÔÿj/úüÿºÿ+ëïÏø"¯üšÇŠìs¾ÿÓv_¯ÔüÂÿÔÿj/úüÿºÿ+èÿ‡)þÔ_ô5ø7ÿuþW×ôõE0¿ðå?Ú‹þ†¿ÿàn£ÿÊú?áʵý ~ ÿÀÝGÿ•õý=Q@Ì/ü9Oö¢ÿ¡¯Á¿ø¨ÿò¾ørŸíEÿC_ƒð7Qÿå}OTPó ÿSý¨¿èkðoþê?ü¯£þ§ûQÐ×àßü Ôù__ÓÕüÂÿÔÿj/úüÿºÿ+èÿ‡)þÔ_ô5ø7ÿuþW×ôõE0¿ðå?Ú‹þ†¿ÿàn£ÿÊú?áʵý ~ ÿÀÝGÿ•õý=Q@Ì/ü9Oö¢ÿ¡¯Á¿ø¨ÿò¾¾/ýª¿c¯‰²§áí'âF©¤jrø–‰íΓ5ÄÊ‹lȬ$óíà ’ã ß8¯í*¿¯ø.üŽŸ ?ìªÿèØ(öƒöGÿ“Uø9ÿbvÿ¤×еó×ìÿ&«ðsþÄíÿH!¯¡h¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Í/ø+_ü™gˆ¿ì%¥éJ×ém~iÁZÿäË&ø?Iñ÷¯Æ© ë-ÍÈŽH„±7¶J¨ëÓ£(#¸®¶¾?ý€¿ä;ÿØ?ýõë?µºGÃ-CSýž´½?Zñ­¬¶ò[Ùjm²Þâ•Lñîó"Ú=ÁIuî(Ùè¯Ìÿ‡ðRÏCâþþÔÔ¾ xÌaY5x´ÙŽp+­ŠUòDÒVë_£ÚF³¤xƒL¶Ö´ è5-:ñ\ÛJ³C*Œ’!*ÀúƒŠÒ¯çkþ ‡ÿ#§Âûê¿ú6 þ‰kùÚÿ‚áÿÈéð£þÁú¯þ‚€?h?dù5_ƒŸö'húA } _=~Èÿòj¿?ìNÐ?ô‚ú€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Í/ø+_ü™gˆ¿ì%¥éJ×ém~iÁZÿäËEŒ vä n‘ÏwrX÷4ÐøjmjãÚTþ$…mõi- kÈÓRäÆ ª0X`>@Á?S_ϯüþGO…öÕôlý×óµÿÃÿ‘ÓáGýƒõ_ý~Ð~Èÿòj¿?ìNÐ?ô‚ú¾zý‘ÿäÕ~Ø é5ô-QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Í/ø+_ü™gˆ¿ì%¥éJ×ém~iÁZÿäË"¶Ðl~a Hw\\ºŒùvð&d•ý•N-“@»E~#ü.ÿ‚¦ø³ö€ý­¼ð›á¿†íôOë“[Ï. ¦mNå%û·A÷>àó«œà~ÜPEP_Î×üþGO…öÕôlý×óµÿÃÿ‘ÓáGýƒõ_ý~Ð~Èÿòj¿?ìNÐ?ô‚ú¾zý‘ÿäÕ~Ø é5ô-QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Í/ø+_ü™gˆ¿ì%¥éJ×ém~iÁZÿäË;ÔdH¤Ú‰d`‹H.¦˜³ýÂIàcší>8ü@Ö?à¥ôßÙ—àµÔÃà÷ƒ®ÒûÅZô#ݼlT,.rxd·$´»Lq†¯øKÿ‘ý¨íÓQðWŽ~%YøOÀzÄÑ6«i£^]\¶ -óåî·1Á }ãµ¥c´ó°à ýÍøðáŸìéà+O‡ ´µÓôø>y¦|=ÍääÓÜË€d‘±×(¨U@¥xsÃÚ/„|?¦xWÃv‰a¤èöÐÙÚ[Æ0ÁãEöU Ú¢¾ý¢¼#ÿñ‹î£ý›üiá øN[h–#ªE3jI>Ò%lýŽê-¹å{úŠügøo,Cþ !vå†ßøM5ÅÏû^MÒãó⿨þm´¿ø$×í¿¢øþ/ŠzgÄO ÃâØoΦº—öŽ¢×õ¤2´ÌͦÌÎImÙ ’ ‘_¯?³‡†¿nÍÅ3Ú{Å~ñ†…„‰ Ñ#ž;ó}æGå¼›­-¢òü± ls¸®3@hQEWóµÿÃÿ‘ÓáGýƒõ_ýDµüíÁpÿätøQÿ`ýWÿFÁ@´²?üš¯ÁÏû´ý †¾…¯ž¿dù5_ƒŸö'húA } @Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Í/ø+_ü™gˆ¿ì%¥éJ×émyÇ‚ý¡¾Ýü1ø‘óhw³A<‹m1‚Möî0àG>´üýÿÁ;à _e/‚š×ÿ‰Z~·w©ê>!¹Õcm6Ö ¡Mii†in!!÷@ÙHÆ9ä÷¯ü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›Gü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›Gü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›Gü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›Gü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›Gü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›Gü>wöOÿ 7‹?ðÏÿ“k¯ÿ‡CþÆßô Öði'ÿGü:ö6ÿ n³ÿƒI?øšä?áó¿²ý¼Yÿ€ü›_“ßðR/Úûáwíoâê¿ lõKH|9k{ ÈÔàŠf¸x™<±ÓdarGnµû!ÿ‡ý¿è¬ÿàÒOþ&øt?ìmÿ@Ýgÿ’ñ4õÏìÿ&«ðsþÄíÿH!¯¡k•ð7ƒ´_‡~ Ð<áµtÒ|7ak¦Ù¬¯æH-í"Xb Çï6Õ=ÍuTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÙapache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/pic/protocol-spec-mck-bug2.png0100644 0000000 0000000 00000717162 15051152474 032606 0ustar00rootroot0000000 0000000 ÿØÿàJFIFHHÿá@ExifMM*‡i  , HÿâÐICC_PROFILEÀapplmntrRGB XYZ æ5+acspAPPLAPPLöÖÓ-appldescPbdscm´œcprtP#wtpttrXYZˆgXYZœbXYZ°rTRCÄ aargÐ vcgtð0ndin >mmod`(vcgpˆ8bTRCÄ gTRCÄ aabgÐ aaggÐ descDisplaymluc& hrHRØkoKR ìnbNOøid huHUcsCZ0daDKFnlNLbfiFIxitITˆesES roRO¶frCAÈarÞukUAòheILzhTW $viVN.skSK;L>@>289 LCD LCD æÑâÕàÙ_i‚rLCDLCD MàuFarebný LCD&25B=>9 -48A?;59Colour LCDLCD couleurWarna LCD 0   @ ( LCDLCD *5LCD en colorFarb-LCDColor LCDLCD ColoridoKolor LCDˆ³ÇÁɼ· ¿¸Ì½· LCDFärg-LCDRenkli LCDLCD a cores0«0é0üLCDtextCopyright Apple Inc., 2022XYZ óQÌXYZ ƒß=¿ÿÿÿ»XYZ J¿±7 ¹XYZ (8 ȹcurv #(-26;@EJOTY^chmrw|†‹•šŸ£¨­²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿparaffò§ YÐ [vcgtndin6®QìC×°¤&f\P T9333333mmod Rýbmbvcgpffffff334334334ÿÀH ,"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÛC  ÿÛC ÿÝ“ÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùã—íoá_…ºÌ~𕌾1ñÝÞ&Ë2:³}ß7fJõ*£Ý›¶ç×D€2kœ¾ñ„t¶)©ë–6Œ½D×1FGýô¿?Óàçíyñä GâÇÃíäîNŠ.Oý7Ã:œu k½ÒÿàŸ_Ñþóªx¶Py5+ùËIïynƒò­9"·dó7²>¶‡â€._Ë·ñ6™+ú%ì, õÔÛ]ZÞD&´™'Œôd`ËùŽ+ãK¿ø'÷ì¸ñãIð´šTßóÖÞúì?þ=3~•ç×ß±Wü pu€¿5í>eµÔ䶃QÔŸ|Ñ˳ ˱ú'E~th¿µÄσÕŸƒk 2—òmüAd¥¬å>²c* êvà ýÒ5}3_Ó-µê;ÛÄC4LTލ7Ƥ™¥ETQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¨æšx̳ȱ¢õf øšñ‰´ïjøSÃÖ­®xšçˆí"Ël>²män+Ï%øaã¿ÂÞ"øÍâ‡Ò´äS#XY¸†(s‰¸õÉȯ¦ÂpãöQÄcj*P{_YKü1ZÛÍÙyœqÞó…(ó5÷/V{–±ñgᾂÅ5_ZÄËÔ2tÿpç"ý¡~ Ï Š/[–=>IGóJù/Wøõû ü+›ìÝÚj÷(vŸ±«êEœz\ šÎ¸ýµÿeíGÂsÁmÁyŽ@UõÈŒßÙdëE²ó÷cøZ_™Ÿ>'¼WÞÿÈýѼqá¨mW¶ºÏ@²ß÷ÉÁý+ª¯ÎÏ øÏö/øÉp±ø'ÄqxZŸˆ¶ÎÖWDžÉÏàµë,>3|mûCxßÂéË3u c¾áËqÎNEZÈpx0Uí>‘¨”[òRMÅüì\«OZ°Ó¼uü7üÏ®¨®#À|7ñF]c÷EFx’&þë/Q]¹8¯–ÅajQ¨éV‹Œ–éîzêFqR‹ºgÆ?µ—Ç]{ÁVúOÂo…©ö¿ˆ>3“È´Uçì°c÷“·¦1õ'¶]û:~ÌÞø#£Rïþ'2ÕG©ê“ÒÉ3ü̪NHPN¯^3ŠðÙMƒâßíñ3ãþ¬>КuÏö6ŒXîXíÕ‰v^Á†Ð2;_xx³Æ^ðF˜uo_GeoÑK ÙGsì+XQœä¨Ò‹r}íä’s“Ðéè®Á_¼ñeÿ„[RK¹!åãû²(õ*yǽtºÿˆ4_ é’ë:ýÜv6pãt’°Uô=ÏaEl¾½:¾Âpj}¬ï÷5á(ó§§sfŠó|ZðÄ ™¬¼1ª%ÍÌsD~Y ô,ò@=O¸®çSÕ4ÝÆ]OW¹ŽÎÒºIe`ˆ w$ð(ÄåõèÕö5`ã>Í;ýÁN´'x»£#ÅÞðÏ4¿ ø³O‹RÓ¯P¤‘J¡¸ÏB:ƒ_œ~¸ñìAñnÃÀºÝô—ÿ|cq·O¸”“ý—q#cË|ô@y8ìs׊û»ÂŸ¾øÓU:&ƒ¬G-éÈHÛä2c®Ìýï¹OÚwáUŸÆ‚¾%ðŒ°‰/ZÚI¬[º]D¥¢ õûÀgÖ´Ä`«á§ì±0qo£VùêL*¢æ¦î{är$¨²ÆC#€A=)õò÷ìqñëâOìÿáÍcP”Ï`­§ÝHNKÏk…r}ù¯¨k†Q³±²wW (¢¤aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ øÍñ'RðÌV~ðj ¯ëge´cŸ)XíóÐßJö­Bö 6ÂãQ¹8ŠÚ6‘Ïû(2kä¿…—v7“x¯ö‹ñ¼Â%ó¾Í$Ÿr +a†‘sÓ89¯¨áÜ%(Ʀ?¬¿šOá¦íù#ÏÇU“q£g.½—Vfx—ÄŸ¿d¿IâßKý³ã-_ ‘¯ï.¯.Ÿø#¹ê®ñïüøÏûRI¿i=f}Ã7Meá« „ò¦wà+Ïäc *oÙÏÁw´¯Ä}Cö¦ø™lfÓ •í¼5§MÌPDþ¼©à¶Áèsž ú\8åæ9ZõeV¬¯7»íä»$oB„c­#ðÀ„ í’ßÁž²ÓÊŽdX”ÈÇÕ˜ŽIõ¯M:V˜F œ$×5ÿ ¿EyŽMît¤|õñ+öZøñN ‰<5oëçm岈nPŸî¸¯õ=?ãÇìQtºžuqñá2œOm&ZûO‡¹@r ¨ä`óßú‹P]ZÛ^ÛÉiyOÊQã‘C#)à†SÁÐÖ‘ªÖTC‚>!–MPÒ¬¿hßÙþä\YNêQp²Æy}ÉÙÔõjúçÁ¾.Òüsá›Oiº¸òWº8á”ûƒ‘_œÉoqûþÐöº$…?åx„.wCa|Ü€¹û¡²TâÎNqÇÒžŠO…_u–#Dñ:›ëOÊ’¾£Ó$` û>í,¡QÞ­%x¾²‚Þ/»ŽëÊë±å¥ì*§†NÍv}Ìó/ø'c,?5Ý.ìîÕ,õëïµ7v.ÿ&sƒÐµÜ~Ó6RÛø§Áþ&Ö¬%ÕÙoù,JðW“^?ð×X_Ùãö»ñgÃwýÃÿœêšD¯Äbå -þËœû_¤ÛÛÝDÖ÷Q,Ñ8Ã#¨e#Ü+ÈÉs¨c#ŠåæVz^Ú5ggÑögN+ í©:w·üá†Ù¾*øòž(øk¤M£øvÚÓmÎèü”‘º$N^Õèÿµv—©ÞxsBÔ!²—PÒôûß2þº´D =n+ê+; :/'O¶ŽÖ2s¶$T\ý ±$qʪ`« ‚b+Ø­ÆwÌhã¡OJiE&îÚ³Z˾»ôÐæ†Uû‰ÑrÖNûi÷v?=ü5/‡(Ó›Z'ÓÏCÉÍ ¥É³kó;¯ÛvÞׯ6øEð“E‰gñ]ö®—‰uÿ-aµµRÓe‡#~A÷Å}ë¬øÛÁž û™â}v×OžãlP‹™UFéœÜ÷xhø—\µ…´&ykKQÀk§Æœ N9ç5õÿ…a7ÅÖ÷Þ&ý¤u»¯ø³VCó ž8, –ëP þ+žq_-R*Ë™ž„dîì~†Ã4W,Ð:ÉŒ«)È ÷u©+ózÛà/ígð.BŸ6ø¨ês/Úù£'§ÊO™Åcì¯ð²ùû£ïú+àGý®~6_âÛHý¼UopLJ»–!õ"5ïïYwwß·§Å´6z~•ð¿LŸåk³þ‘vªO9ŠFlqè¢bú°ç]§¾4þÐ_¾è2ê~)¿Yumtè’êâS÷QPtÏ©ÀÇ©â¾Yøð¯âÆÏˆÐ~Òÿ-ÍŸÙN|9¢·"Ò9I\“ó~}«Ó~þÆ^ð&²¾8ñííÇ<`~cª9™"cÉD~@3Ó ‘Ø×Ù*¡@U€jÔU¢ 7«Š(¬K (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ä?†Úæá|Jð¦¿2Ù_]ÜIsn%;VX¶¸Ü¤àwù±ñ.=^/ë¾#ÑÓͼðž§¹l÷c›>}#Í~ÎøÛáG¾ Ilj4åšæ¹23G …‚GÖ¾\ñ×Ã|>ñ÷‡´«ˆ x†ÆçK¼IܘØN\1fcžë_°å¹Ö‡ÄЂ—¶«¥•¯N=ïï[kw>_„«Jp›k–-úÚO¯¡õç€üU¦xßÁº?ŠôiDÖz´sFÀõ  ×[_™²‡Ä˜¾ x³Uýšük¨Çq¥[LóøUÞ Àíþ§ÝÜ0£¦ ~—[ÞZ]‚m¦YvõÚAÅ~Q‰ÂT¤ýøµê£§V2Z2Íàzÿí-ð“Ú­Æ©È÷ÌQü˜ZT :Ë‘^¥àïøsÇš2kÞºVŽJ“Œ2°þ^ ûìÅä8Ü=%^½FfÓK_3:XÊS“„$›õ:ª(¨å–8"i¦`‘ %˜œs^IÒ|Ãûcxöüñ%¿üL5h×O²¼³Ü0]£þ¸×ˆüð½§ÂÏxÃz”Éh,ü.$ÊÁÉpd™Ô–Ç!œŠã®/'ý³ÿhÍ;û 4Ÿ þNÓK7&ý@p˜=ܤvÏ­}Õã‚ÞñÞ¯µâ[¸žÄ@,²F¦0IÛ„aÇ5ôü?ŽÃá¥8â[Qœ%d›Wòm~gŒ£:‰:vºiëå÷g…¼àï C1ðŽ—k§ÅxæY ²3rX‘×5ÕÕ7N³Ò,-ôÍ=<»kdƹ-…X’½_7RÜÏ—cÐŽÚ…QP0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®OÅžð·Ž- µñFŸüvÌ^1 ÎÆ<>µÖQ[añ)MT¥&¤¶kFLहd®˜¼mû*|6ñ&‹uk¡Âڤ˛{¨yòœtÊ÷_Qšüñ6Z·À¯Íá¿Úgúփ¸‹oh²9·1žÂ5Š£UU¹N¥Kè¶ÆÁEVey_Å¿ƒ>ø×á™|-ãÝ5/``ÞT¸hŒo‰ú«Q^©E4íªÌ¿†¿|wû&|@³øñžâ]WÁ:¬ž^¯¾JÅýØ&'•ãŒv=22GéŒrG,k,LVSAäGc^Sñ¯á'‡~5|>Ô¼â‡úJn¶œ¬·¸^RD=ˆ<bkæÏØß⯈dZýŸ~&ÈG‹ü)·G“‡»±ë ¸=~S ÖÒ÷—2Ü…£±÷eQXQEQEQEQEQEQEøOûxÁFh/ÙÃöŠÕ~ü=ƒE}ÊÊÆâ3{g$Óo¸„;åÖdÉãå¯ÝŠþU?à§Ã?·ìÀÿÏÿEÇ@ÿáòµÏüúøkÿÓòMðùÚçþ}|5ÿ‚é¿ù&¿©_ìûùö‹þø_ð£û>Ãþ}¢ÿ¾ü(ùjÿ‡È~×?óëá¯üMÿÉ4Ãä?kŸùõð×þ ¦ÿäšþ¥³ì?çÚ/ûáÂìûùö‹þø_ð å«þ!û\ÿϯ†¿ð]7ÿ$Ñÿý®ç×Ã_ø.›ÿ’kú•þϰÿŸh¿ï…ÿ ?³ì?çÚ/ûá€?–¯ø|‡ísÿ>¾ÿÁtßü“Gü>Cö¹ÿŸ_ àºoþI¯êWû>Ãþ}¢ÿ¾ü(þϰÿŸh¿ï…ÿ þZ¿áòµÏüúøkÿÓòMðùÚçþ}|5ÿ‚é¿ù&¿©_ìûùö‹þø_ð£û>Ãþ}¢ÿ¾ü(ùjÿ‡È~×?óëá¯üMÿÉ4Ãä?kŸùõð×þ ¦ÿäšþ¥³ì?çÚ/ûáÂìûùö‹þø_ð å«þ!û\ÿϯ†¿ð]7ÿ$Ñÿý®ç×Ã_ø.›ÿ’kú•þϰÿŸh¿ï…ÿ ?³ì?çÚ/ûá€?–¯ø|‡ísÿ>¾ÿÁtßü“_[~ÿðR?Úö‡ý¤ü5ðŸÇÐh‰¢jÐê1²³’ómi,éµÚwçAŸ”äf¿wÿ³ì?çÚ/ûá¿•ïø'Ðþ ]jÀþ'ÿÒkºþ«(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Ãø-ÿü“…ÿö¿ÿÑ _¸õøqÿ¿ÿ’qð¿þÂ×ÿú!(îOø&Ïü™Âïúô½ÿÒûšû޾ÿ‚lÿÉ‘|.ÿ¯Kßý/¹¯¸è¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ Íÿ'¶ÿö Ò?öjþªëùTÿ‚³Éí¿ý‚´ýš€?ªº(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€<Ãã?Ä+/…Ÿ üGã»×4»Gxòy2·É¿1óÏìAð¾ç ¥ø‰âD'Å.$Õ¯YùdI˜˜£ yÛ°+ï_Wø·ÁÞñÞ‹/‡|]§Ç©é³•/¹ÚÅNFvx5¹cei¦Y[é¶m­#H¢z$q€ª£ØŠÑNѱ6Öåª(¢³((¢Š(¢Š+ó›ö¼°ƒ_¼ûShq´qi·éÚï–1æYJøËÔÅ~€WèÍy·‹|;àÖü ­4:Æš³5ôQ¾LSÆb¼«¨`}EkJéó[N¤O±ßX^ÛêV0j®$†áDe90ÈÅ[¬ CÓ<5£Yè4?g±°‰a†=ÌûQFÜıüMkVL°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ {ÿ'ý/ýqÐô\uýU×ò©ÿ=ÿ“þ—þ¸è?ú.:þªè¢Š(¢¼[ã'íðWà‘ý±ñoÅ–Zº—†ÞGó/.ÿž6цšNx%PÜŠöš+/CÖ,¼C¢éúþšKZjvñ\ÂXmcÈ Žâ¿iÚ?ö»øãà?ˆ%ø¦Ý|.øSà(o$ºñÜmªë/bÅ^;-eÔÑžÇtÊIŠ€?o¨¯–?b?xƒÅ¿²‡Ã/x«S¹ÖumCII.o/&{‹‰œ»ÒK!fcÔ’kêz(¯ ý£>7Åû?|3¸ñâxvÿÅš„—0XiúVœ»®//n˜¬1Œ!I1Uf¢±â¿!›âí¿gûf| ¼øýâ)cJGBWî(ï}Wi?µÀ_âž³ðM|]ieãmám¦Ó/ÚË,Ž‹"ýœËµ'ʸâ6fä ÷Ú(¢€ þU?àŸò’ûoûxŸÿI®ëú«¯åSþ ÷ÿ)/¶ÿ°‡‰ÿôšî€?ªº(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Ãø-ÿü“…ÿö¿ÿÑ _¸õøqÿ¿ÿ’qð¿þÂ×ÿú!(îOø&Ïü™Âïúô½ÿÒûšû޾ÿ‚lÿÉ‘|.ÿ¯Kßý/¹¯¸è¢Š(¢Š(¢Š(¢«ÍwknÈ—$M'Ý ÁK}3Ö§$“À´T÷V·jZÖd˜)Á(Á€?…O@Q@*ŸðVoù=·ÿ°V‘ÿ³WõW_ʧü›þOmÿì¤ìÔýUÑEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÏ:üQß´?ˆ43òXøãN‡Uƒ²‹Ëä\"ï4edo¥} _=|~ÃÑøKâ¬Ckx;V…®\u}ÿú5ÐÿÇþêåõIPþt×ÏxÿäɘÍ"ª+¿Ë¯ásèZ)A¥¯(ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ þU?à§¿òÒÿ×ÿEÇ_Õ]*ŸðSßù?é뎃ÿ¢ã ꮼsã×Ç/þÎ uOŠÿËèú[DœBiä’wƈ¬È¹f e™@îkØë‡øð×ÀŸßÏâØ_â•ÖƒlÎf“Â~ sy¤ÎO,±¹VHàŒ¿?ë—­jxkþ /®|/Ö­¼ ûp|7Ô>k·—³kÞèwL:²<~c(öç«2ô ÔÔD8Ô" 0ò¯íÆþÈÕFðíè¹_Pi÷öz­…¶©§L·—‘$Ðȼ«Ç"†VÄE|ÁûrÿÉ ü\ÿ±z÷ÿ@  ¯Ø þL×á?ý“ÿF=}_þÀ_òf¿ ÿì Ÿú1ëì B ‘œr=«òSöÈÿ”~Ê_õñ}ÿ¡¥~¶WäŸí‘ÿ)ý”¿ëâûÿCJýl¯—¾=þÆß³Ïí#’üJð´-¬2íMbÇýR |ô¼ >êÊ÷kêø'ãïü á§ÂÜ| ðN…«|LøE¶‘Œrº‡Už}ŒU>RJÃ?2Šðsðoöüý’?Ò>x¾?Ž>µäx{ÄM³U‚ü÷ ãvÕávÊ£8 nzWÓ²‡í­áÚŠ÷^ðˆðÞ§àßøIGö¾“¨ "ó LQ´¶×X ÇhWP‚ݘAs À„º…,€²²ºnm„»Ôý®?bëïÚsâWÂÿZx®?§ÃÛ¦ž[w³7&éZâÞl+‰cØrG õµ}©âïù5¯úò¹ÿÑm_›ß·‡í]ñsö}øÁðgÁ®,áÓ|ovðêBæØNì‚êÚ!å±#aÛ+tïŠý!ñwüŠš×ýy\ÿè¶ ÆÏø"/ü‘öƒÿIV¿m+ñ/þ‹ÿ${â/ý‡ ÿÒU¯ÛJ(¢Š+ùTÿ‚³Éí¿ý‚´ýš¿ªºþU?à¬ßò{oÿ`­#ÿf ꮊ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¹øfÛÆ~Ö|)w­i5¶Oð™…oªœî+§¢®G )ÇtL¢šižCð#Ä×>(øW¡\êY•„m§Þ«}õ¹²cî÷m›¿õêùçáçüR?¼{à&ù-µ¯'Ķ+ëöÜ^ûüŠú»ójiW”£´½åé-mò½¾FI7M'ºÓîÐ(¢ŠóN¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ {ÿ'ý/ýqÐô\uýU×ò©ÿ=ÿ“þ—þ¸è?ú.:þªè¢Š+Äžðߌ´[Ÿx»JµÖô«ÅÙ=¥ì q«èñÈ[ñ¹Ef=m4ì­ %€†&Ø".«¶=©Ójq…Æ01_”Þ:ý•ࣼ«ø ÆŸ´6‡¨èZõ»Ú^Ûÿ`ÚÃæÃ Ã/™¢H¹Õ÷¯Öº(ò£àßì±û~üÓ|+à½#ãÆˆ<á¹ û5tKvg²ŽPòB&’ØË—]À1}Ã<]í·Ž-¿hÿÙzïÂ.¼ÒâÔ;ømâ Æ3qyhæßÔ\ÅûÈá"­Ið§ÅãÇŸ|=âÆ9šþÒ3?µÂ~îaøH¬+Õ¯ûÌ,'Ö-Åú?z?ûwÜrS÷jÊ=õýèz Q^QÖQEQEQEQEQEüªÁOäÿ¥ÿ®:þ‹Ž¿ªºþU?à§¿òÒÿ×ÿEÇ@Õ]Q@Q@Q@Q@Wñ âO€¾x^ëÆŸ5ÛOè–c÷—7r×v2Þwl|¨€³&€;j+Ê> üiøñÿÀ–ÿ>^Ë ÝM4Í,[³=»”’P­€GŽkÕ袊(¯åSþ ÷ÿ)/¶ÿ°‡‰ÿôšî¿ªºþU?àŸò’ûoûxŸÿI®èú«¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Ãø-ÿü“…ÿö¿ÿÑ _¸õøqÿ¿ÿ’qð¿þÂ×ÿú!(îOø&Ïü™Âïúô½ÿÒûšû޾ÿ‚lÿÉ‘|.ÿ¯Kßý/¹¯¸è¢Š(¯†¿f/زÙÃâçÄߊÃÅíâ>#\ÉqöO° E³]KrWÌóåóÖÎÔéœs÷-WÃbȾþÖ^?ý¨ÇŒTÛÞAý“öµûeŽÁo´ùïæmò6ãÊ\îÏÁûžŠøkö¬ý‹ ý§¾"ü4ñüž/o‡w-pm…€»û`iàŸnÿ>/+ýN3µþöqÆÚÚ­‚êš]æ˜îc[¸d„°*$R¹Ç¶jýñOìKûÅûx;Ä^Ňŧ_¿Kã9±VÈ„A6yóîéœî˜ï_kQEQEüªÁY¿äößþÁZGþÍ_Õ]*ŸðVoù=·ÿ°V‘ÿ³PõWEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_<üÿŠ_Æ?ø`ÿ,Zv¢5[è>Ǫ/›±÷c”:Ÿs_CWÏ>>ÿŠCãg|r¿%®¾“ønõ»n—ý"Ïñ2«¡¯W,÷ÕJÌ®½c¯åuó91>ëNÏðz“ùCQEåaEPEPEPEPEP_ʧü÷þOú_úã ÿè¸ëú«¯åSþ {ÿ'ý/ýqÐô\týUÑEQEUKÛû6Ù¯5ˆí`O½$®}Yˆ‹ö¾[–ÒÒ9/n`Y˜¤m.Ó°;(b¶2@$€ô¯æÛöâýœ~=Íð'Tý¦ÿk»øÑõk{]/Ã,²i:u´ò2˜Ð†eQw‚™8_Þ<ŽÄ¨ô£‘ËËG«)È ô ŽÕy®hº}徨[Û]ÝœC ²¢I)é„V ·à ~QþÀ·Ÿþ ²'ø\ö÷¾3Ý«§†×R“"¥ËF, üŽ%1«~ï~Õb$~\þÞ_²Oˆ~øCÀ>#øïPñÅ_Þ\[Í2$’(ã‹iï[ÊwTÉ89QÅF_´ïÄ_‹¿ þÝx—à‡ÛÇþ*{›{H,› ‚Sí/|ò¤o·z«&—gUV5üú~Þ³oíW¦ü+Ói?ڃǰë:åî§žëI¦djzZ.§dÀe–âÅ„ë·Ý‚•Zö*FUu*ÀF=®Œ&!Ñ«±Ý4þã:´ÔââúœçƒüIkã i*²Ç“«ZÃr s·Í@ÅOº“ƒî+¤¯ž¿gæmÏÅ '$?‚õi¡·SÉûáûM«¨v…} Zæ8uJ¼¡ºz=Wáb0Õé©=úúõüBŠ(®#p¢Š(¢Š(¢Š(¢Š(¯åSþ {ÿ'ý/ýqÐô\uýU×ò©ÿ=ÿ“þ—þ¸è?ú.:þªè¢Š(¢Š+ñWþ kâ³cðSÀ WÚu~KÒ;²ØZ¼d}3r§ëŠýª¯Áø.F‘«M¥|×¢…ßL´›\¶–P>DžálÞ%cêë …Gû&€?N¿a¿ Ãà?ØëáV›>ÛxÎo¨ÈX… ý  ó–'¦ Äœôï_:K/ü[öò´ñŽœ­wð_àaQíþ£Q¿Y<Å1ƒDó"± ¼+¸)äþø‘û]~ßÿü-ðá/†ßá¯ÂÍ7M±ÒõïHÏ"ÝÇi E*,ÅbÞ·‹hAfÈÈ#cÛÿ€ß<û:ü3Òþ|<´ò4ý=wM3çÞ\¸mÌì1ºIç²€@UP=Ž¿(¿à²òhÖ¿ö3ißú&æ¿H¾'|Gð¿Â/ë_¼k,h~€Ü]¼Q™dXÃùQyc’8üíÁFÿ࡟ ÿi†:_Â_„º~£$0ê‘jWz…ü)mX"–4Šû±c.æf € Ù%@?P?à“ßòdÞÿ¯Ý[ÿKe¯Ò þs?àŸ?ðRƒ> [üø·g©ÚM¥ÞÝOk{gn·PI Óù¥\YÕÙº+0rýxÆ:ÄOè^>ðÌ6‘â;mFÍäCµ½Ôk,eò¤« ƒÒ€:š(¢€ þU?àŸò’ûoûxŸÿI®ëú«¯åSþ ÷ÿ)/¶ÿ°‡‰ÿôšî€?ªº(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÔýü¢Š(¢Š(ù­ÿ‚ÏüHñtß¼7ðÍ5 bðýŽ‘ðµG+—Ȭî B =9¯Å€ò$“_­ŸðYù:]'þÅË?ý=|û.|QðŸÁo~øãëº.ƒvf¸µUWs˜ÙDW—‰˜H ‘ÊŽEx<«q áãuà«d}Á¥….®¤ ò0ˆPIÀ'ŽÀu¯»à¡_´ÏÃ/Ú—ãã†ú]Ž›”÷WqG Õô«#¸y6q„V¤±b8èðNOÛá죫øºëâ~ƒwzúì%µõ„1Oq‹% "GäƒÝ@ÈÇ4ùæ?÷çRºhZà+˜”…/ƒ´ÐÓ&»ï‹¾.Ðü}ñCÅ>5ðÖ”º•­ê7vÖHG+–UÂð9 qžœWé—€ÿn߀¾ý‚oÿfKÁwøª{;«S¶MÅÅÖKÙ%.H™î•tü‹óûÇó£ÌïΙE?ÌïÎ1ÿ¼:eñ,ªC+G|×õÿoø—âÿþÏþ#ð׊u u(|)¬}žÁæbòEm< '“¸òQ_q\ôÜG@þ_+úEÿ‚"ÿÉ"øÿaËý%ûoEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_‡ð[ÿù' ÿì-ÿ¢¿qëò¿þ “û4üfý¥<àM#àÞ„ºåÞ‹¨]ÏvwmiåÇ,Jªss$a²AáI4í¿ðMŸù2/…ßõé{ÿ¥÷5÷(š_üßþ S¢XE¥èºF¡aenŽ YEI!Q/BŒ’O­hÃÁOçÓVÿªÓÿ“¨ú«¢¿•Oø`Ïø)ïüújßøUZòuðÁŸðSßùôÕ¿ðª´ÿäêþªè¯åSþ3þ {ÿ>š·þVŸüGü0gü÷þ}5oü*­?ù:€?ªº+ùTÿ† ÿ‚žÿϦ­ÿ…U§ÿ'WÇ_Ÿö–øã»ÿ†¿|I­i>"Ó¸·]bK€‹qÍï ™ã9GSÃgšþÜ(¯ä+á'ìÓû|sð-ÄŸ†3êú·‡µ'™ ¸oÅnY­äh¤\÷hã¤r£=Gé_ðÁŸðSßùôÕ¿ðª´ÿäêþªè¯åSþ3þ {ÿ>š·þVŸüGü0gü÷þ}5oü*­?ù:€?ªº+ùTÿ† ÿ‚žÿϦ­ÿ…U§ÿ'Qÿ ÿ=ÿŸM[ÿ «OþN ꮿ•Oø+7üžÛÿØ+HÿÙ¨ÿ† ÿ‚žÿϦ­ÿ…U§ÿ'W7uÿÑý¿uÍrßYñ7„_S¹GŒ4÷Zî<»ð7=Û6è3@Ö}Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@<ëñG~Ñ:&®>Ké’éÓv_¶éçÎ…Øú´LÈ¿Jú¼#öŠÓnßáÙñf”›õ/Þ[k–àwûæPO¡„¾Gzö/R´ÖtËM_O2Öúç‰ÿ½ªOâ¯WûÊ«zÅü¶ü_#’‡»RpùýÿðWâ_¢Š+Ê:Š( Š( Š( Š( ¿“Oø)OŠü5­þÞ®¥£êp^Zéo¥Ú]K‡Xn,‚Çq‘Ñâu*㨠ƒ_Ö]Ÿ¶4>Gíañ…=|Y¬·ý÷w#ZþÔü=â Åšâ ÞG¨é:½¼Wv—0¶èç‚uˆ{«)C[üü:ÿ‚z~Ûü xçÁQÆÚ»eå‰:ÒB~Ï2÷eÁO”—WgÿÃý¿ÿçŒ_ø>ÿŽPõWE*Ÿðì?ÛÿþxÅÿƒèÿøåðì?ÛÿþxÅÿƒèÿøåUuNûNÓõKsi©ÛEw ˜æE‘ Öq_Ëü;öÿÿž1àú?þ9Gü;öÿÿž1àú?þ9@ÕJª¢„@T`ÀS«ùTÿ‡aþßÿóÆ/üGÿÇ(ÿ‡aþßÿóÆ/üGÿÇ(ú«¢¿•OøvíÿÿËã×ýÞÿÉÏþ?Gü>Ëã×ýÞÿÉÏþ?@ ÿœýµÿèU±ÿÁµŸÿ£þ9ûkÿЫcÿƒk?þ9^÷ÿ²øõÿB7†ÿòsÿÑÿ²øõÿB7†ÿòsÿЂç?múlðmgÿÇ(ÿ‡N~Úÿô*ØÿàÚÏÿŽW½ÿÃì¾=Ðá¿üœÿãôÃì¾=Ðá¿üœÿãôàŸðéÏÛ_þ…[üYÿñÊ?áÓŸ¶¿ý ¶?ø6³ÿã•ïðû/_ô#xoÿ'?øýðû/_ô#xoÿ'?øýx<ðI¯ÛUÝU¼/`€œ:µž¹Ä„þB¿}?`Ù>÷öJø57„üE£â]zñµNK|˜#ƱÇE€,±ªòÄ ³7Å~CGÿ³øîL¾ðã <€nÁ#ØùÇ‘¯Ú¯Øçö©Ð?k„ÿð°4»£êVMc©X3ù‚ …E| Ѻ¸*pPyõ…Q@Q@Q@W̾ ý²f?ˆ~=·øa࿈:¯‰î¥šlãY’Kug‘UÚ5ŒáQˆÃsŽ3Å}5EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_È×üiqûoxØÿz×H?ùO€WõË_Éü}qûmøÀÿzÏH?ù# Ü/ø% gö#ðpþíæ®?ò~c_£•ù¹ÿœlþÄþݾՇþNÊkôŽ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€*_ØÚêv7môb[k¸Þ)Pôd`~ ×‡þΗ×QxêrÔ<u£JOVKwÌ÷LL€ø¯{¯žlâý£u #òXü@Ò’î?C¥þîEQê`eb{ãšõp^ý´|¹—¬wÿÉ[#’¿»8Oå÷ÿÁ±ô5Q^QÖQEQEQEQEüT~Û°ùµßÅÔõñ& ß÷Ü¥¿­jõü›þÙ²Ÿí%ãÚ§âoˆü!ðÇÄ:¾‘©k3Omwm¦Ï$#€wG M¬3Üþ‡¿aɼÿÙáúxvÅï„Ûý+êªùƒö.ð׉<û+ü4ð·‹ôÙôgLÒc‚æÒé sBèÌ6º7 ãú~€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÖýü¢Š(¢Š(ùrÿ‚ÌÉÒé?ö.Yÿèéëà?Ù³àË~Ð_|/ð„j‰¢¯ˆ'xÞî@ËH£y[h$eÊ¡;±¿Fà´>Ö­?h_xª{W]/PТ·†|,HdLôȧõù¥ê𖉍Ûêú5Ô¶7ÖŽ²Ã<.c’7SÊÊAàÐÙ?·?ì‘kû üLÒ¼¦ø‹þ+ kO𼈱\ʼn&YQI%r¬1‘‘Ž v¿°oì=§þØw^)“YñKxvÏÉ k-IJϻiÚÄÛÉîx¯…¼WãøïY“Ä>3Õîµ½NUTk›ÉšiJ¯Ý]ÎIÀì:UÏ|AñÏÃÛÙõë׺ ÍÌfd²àgŒÿ ##ë@_|7¿‰~&øsq{¥'‡oç²70ýÉ|¦Àa×FxxßCø•à_üBðÔžn—â; }BÙ»ùw1‰6:2ç ;Evøïÿoøáÿ ·ÀÝcàÖ­q¿Søv^ÕXüÇMÔ¥@3ÉòçƒÙUqÅ~ÄPEPEPŠMÔŒQíë@êMçÒšOlÓr¸Ï¥MÙêI¼ú~´»½k–Öü] x~#6«yvf¾yñ/íWà]é­lÇ_›ü tÒÂTŸÂ7œaèÿV>¯ÝH_«àéÿm=9ü¸ ·dõÞßãWWöÏðù^b„žà1ÿÙåµVèààßÚ>åÝÆG4Í|-7í¡ F¹H!Éé–?ã\Õ÷í¾Öšm´„z»ÿTršÏ`—àÖòüÐìÓwœãùÁíÑxOït‹DõÑÿÆ®ÃqIÔi¶Ÿ÷ñÿƇ–U] ÿÖüó~è®êRq_œ’~Üó¯ÝÒíý´ñ¨áº.ú&Óþþ?øÕd×ÞÂÿ[°Ky~éê7Wç$·5Ë}í.ÔÛGÿ›þŽA×L´ÿ¿þ5 ,­{Xk‹ð/í~è©j7Wç ÿ·EÊ&aÒmûÈÿã\ä¿··‰D˜‹ÃÖE}L²U¼¦²èKâüûOî?PAÏju~\ÛßÅ*qÿí‘úI'øÒÛÛÅŸøGl¿ïä´,¢³è'Æ8/æqúŠOµ>•ùuÿ ïâqÿ2í—ýü–—þãÅ?ô.Yûi'øÒþɬºã ó?¸ýCÍ.O¥~]ÿÃ|x§þ…Û/ûù%'ü7ÏŠ?è^²ÿ¿’SþÉ­Ø?×ó?¸ýDÝAlu¯Ë¯øoŸžž²?öÒZÐÓÿo-ry Þèp¯¨’OêhþǯkØk‹°_ÌþãôÛp¥¯Îøn'Î?³-1ÿ]üj7ý¹¦1éV§þÿãRòº½Šÿ[p_Íø£YÇZ3_›£öç¿où„ZßÇ©#ý¹oüúE ÿ¶þ4eV]þ·`¿›ð?GÍ.E~sÛŽaÿ0›_ûíÿÆ¢—öç¹A˜ô›Rßñ£û.¯bßà¿›ð?G  üÝ·Eó ¶j?í£ÿHŸ·5éë¤Zøÿ?쪽‰\]‚þoÀýÍ&kó˜þÜ“Ð*ÜÿÀßüj»~ݪØ]Ø]ïþ4¿²«ø³ÝýÇéhÝ_›ßðÝä|š5±ú»ÿnèÿ¶åµÓcSÓííÿÝv?ÌÒyenÁ,Á=9¿ôwµ.îqŠøkþ;@Úq ?‹ñ¬™ÿmk$}±YÛ²úïoñ£û6®Ö-ñ6o#ï°Ã8£8¯€öÙ³,Ø ÇûÍþ5±ퟢÜð@ûÇüiK-«ÐC‰ð’ÚGÜå°3JFkáy?lý fR‹zn?ãXïûkY«al¡Ç»55–UbŸá#£gßû¨Í| Ÿ¶½Žà&³€þÓjÛ?Ãã¡„gÑøÒyu^ÅG‰°’ÚGÜÄâ€s_ ÜþÚ:$k˜ ‰ÏûÇük'þfÌÿË”þßãOû2©2âœ"ÒçèlRf¾ƒöÖÓÂIi»›ükH~Ù¾=`‹?ïñ¡eµ_AÇŠ0oírî£u|uûjiqŸÜÚBÙõfÿ©ÿ ±eŒýŠÿoñ¤²Ê·µ‰—á#¼¿ïÒÞœÒîÀɯ…´ÏÛCA¹-ì½ÎãýMzV›ûW|-ºQö½N(›Ópþ¦¢X ©êéq{Lúu r{Wø{₼P´JsÛxÍw°Í ÃtN¿×<éJI­U:Šði–7ô 1' Ð=j ê)»©Ô†QEQEQEùûIÿÁ1~~Óõ/‹¾'ñv±¤ßêpÛBööknaQk¤#fÉ ç­~˜×âÿí¥ÿ7ø•û1üyÕ>xkÂN¯cakgp·7op²±¹„HÀˆÝWœ ý&ýšþx{öeøM§ü#ð¾¥u«iút÷3¥ÅàA3™L¬–ª¸°8é^õ_'~Å´ˆ?iï€Úoůi–ºEýíåå³[Ù—0ªÛJQH2l‘Éæ¾± Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾|ý¡£}EðÿÄûU&oê¶÷’•ce9û=Òª8'Ùkè:ÂñFgâ¿ j¾¿ÿ}VÖkY8Îd)‘î3‘ï]¹~!R¯ Ëkëèôkæ®cˆ¦ç¿ëÐÚŽD–5–&Ž+Aäiõâß³ö¿y®|-Ò­u^5Mɤ^©ä¬ö a;©UV?ZöšË‡tjÊ“èÚ*E8)®¡EW9 QEQEQEW…ø¯öžýœü ®ÞxcÆ_<;¢ë{¹³»Ôí¢¸…Іâg ¤‚èkÝ+øäÿ‚‘ÅäþÛ“ÖòÍ¿ï» vþ´ý|øOžñ߇¬¼[àÍRßZÑu/myi"ÍʬP”uÈ 2‘Çq]|3ÿÕ›Ïýˆ~?¥­òÿß…ÊÿJûš€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (£ PE7r÷"“ÌŒuqùÐ証èç¢þb“íãþZ¯ýô(j*µZùlŸ÷Цý²Ðu?ï¡@hª¿n²n#ÿ¾ÇøÓ´,?çæ/ûíÆ€.QT¿´´áÿ/Qßkþ4ŸÚš`ÿ—¸ïâÿ^¢³ÿµ´¡Öòûø¿ãIý±¤·°ßÕÿÑ¢³?¶´qÖþûúŸãIý¹¢ùˆ[ÿßÔÿÔ¢²¿·´?úÛßäÿoü$ë©[ßäÿ×¢±ÿá"ðÿ}N×þÿ'øÓá#ððëªZÿßôÿÚ¢±?á&ðàëªÚßøÿÆ“þ ÐZÓþÿÇÿÅPåƒÿ O†]^Ïÿ#ÿâ«ñÆ?†¾ÒßUñ¿jªÉ 2,ÓÊ»HK®0;+Z4'RJÕÛ艜ãÍ'dzmÎxT€N³d¹ìnbãÿ£þÿ ŽºÝþÅÿÅVEÎÂcá×\±ÿÀ˜¿øªoü&~ó°ÿÀ¨¿øªéh®gþ_ùXàT_üU7þŽºþŸÿQñTÔQ\·ü'> ïâ ?ÿáÿâ©?á;ðGéßøÿ@UÉÿÂ{àa×ÄZwþÃÿÅRÂàOúôßü ‡ÿŠ ¶Šä?á`øuñ.™ÿÿñtŸð°ü:ø›LÿÀØøºì(®;þ'Ãá×Äú_þÁÿÅÓácü=|Q¥ÿàlü]vtWÿ #áßý :WþAÿÅÓáe|9|U¥àtü]vÔWÿ 3á¸ÿ™¯IÿÀè?øºOøYß G_iøoÿÅÐqEp§âÃ1×ÅÚ@ÿ·ûþ.“þ—Ã.þ.Ñÿð>ßÿ‹ îŠà¿áj|0ÿ¡ÃGÿÁ…¿ÿIÿ [áwý:7þ -ÿøºï¨®þÇÂÏú´oüÛñÊoü-¯…C¯Œô_üÛñÊô+Ïán|)|i¢ÿàÆÛÿŽSáo|'|k¢àÊÛÿŽP¢Q^uÿ ƒá(ÿ™ÛCÿÁ•·ÿ¤ÿ…ÅðŒÌï¡ÿàÊ×ÿŽP£Q^oÿ “áëã ÿv¿ür“þ7Áñ×Ç:þ íøåzMæŸðº>¾:ÐðikÿÇ)?átüÿ¡óAÿÁ¥¯ÿ L¢¼Ëþ_Á¡ÿ3îÿƒK_þ9Mÿ…Ûð`ÌýáÿüÚñÊôú+ËÿáwüïãÿÿàÖÓÿŽSáxü~ x{ÿ¶Ÿür€=JŠòÏø^ÿè ø{ÿÖŸüv“þ¯Áú(^ÿÁµ§ÿ T¢¼§þ¿Àñÿ5ÿø7³ÿã´Ÿð¾~Ž¿<9ÿƒ{?þ;@¯Ey7ü/¿c¯ÄoàÞÏÿŽÒÂýø?æ£øoÿüv€=jŠò?ø_ÿüÔ àâÏÿŽÒÃ@üÿ¢“á¯üYñÚõÚü©ÿ‚‹þÜÿdŸø'Nøuá› Ý3[Šk‹«ýN¥†G‰Âý’#‘p¿;Xá—`çïøh?€ƒ¯Ä¯ ÿàæËÿŽ×+ã?‰ß²ŸÄO^xGÇ~2ðv½¢ß®Ùí/uK ¡qØíiuV*yhçOÙ_þ Qð'ö‘û'†µKàϵ?²µWɹö³º!R\žˆÁ$=šý¯ækö©ÿ‚}ü‹í~3ý•þ-xeÔn‘¼7¨ëöA‡}¶WrMϲN}Ošxæ³ü÷ã¯ìíy¾%ñÿ„¬$6íow>íBÌFv‘myóoUÇɽpFAÍV”Wο³ïíWðGöšÐ¿µþø‚;«¸P=Ö™qˆ5LñûërIÛž<Ä-<5ôUQEQEÿ×ýü¢Š(¢Š(Ǿ6Ú| ¹ðsÚ= ü6²ù>Íæž|þ7úæ¾'þÉÿ‚NϿïÊÖ¿3à´~+×o~?xgÁÓÝ»if‰Ô6ù>ZÍs,‚GÛÓq£=q_Žövwz…ÔV6=ÍÌì8¢RîìÜU\’O`(úºþÉÿ‚NϿïÊÖìŸø$çüûü:ü­kùT×|=¯ø_R“Fñ6›s¤êཽÜ/˸dnI°Èé‘Sø{Â~)ñuÔ–>Ñï5«˜PÊñY[ÉrëõfXÕˆQÜž(ú¤þÉÿ‚NϿïÊÖìŸø$çüûü:ü­kù?’7‰Ú)T£¡!”ŒGPA­è<#â»o[h·²è–ïåI~–Ò5ªIýÖ˜.ÀÞÄæ€?ª/ìŸø$çüûü:ü­hþÉÿ‚NϿïÊÖ¿“Ê(úÃþÉÿ‚NϿïÊÖìŸø$çüûü:ü­kù<¢€?¬›}+þ AçÇå[ü9߸mÜ-1žÙÝÇç_¢^‡ÂÖþÓ¢ðJÚ&‚!Cd,6}—É#*bò¾M¤tÛÅ•ý5ÿÁ¼_â oà‹|1ªÝ½Í‡‡µÍ¶)#ò#¹$xÓ=x.÷™zý‘¢Š(¢Š(®sÆ*Ñ| á=gƾ$œ[iZÅýܧø ¶¤ýB©À®Ž¿$à°_á^þÏV_ ´«/VøwäÊá—M±+5Á㑾C í+8ìhóGö$øíð‚ûöÃñŸíOûKx¦ßÃ×Oö˽2)ãžro5'hÀÊŽL%µ¶øÆq÷“ ~ÞÿÃÈb_ú*V_ø }ÿÈõò·ìÿÉýœuOÙãÁž!øá࣭øÇ]³]JîW¿¿µ1¥á2Á•oq)Ž@Ù\ïÝ“Ø}ÿ¿ý…ÿ蘯þ õoþL ÆO¿þü ÿ‚–^kß 5øuO†>8Ô¤ÓÚxRHaŽÛ[)*¦Ù‘ ­¥Û Éq<šþ¥+ùàÿ‚šþÁ_> üѾ.|ðÓxz=Q[meîîè=½à äÝM)O*eTùÉ—œàcõçö.øÞ¿´ìÙ࿈—3‰µv´:¯9a¨Y~ævaÛÍ*%û®(êj(¢€ (¢€Õøµÿý¢¿lß„?ôþÎsê1xzëÃð]\‹=ßSO¶µÝÔo™eµœ«yiÈ0qÉ'ö•ÉŠçüI¯ØxkF¹Öµ'òà¶Rì}…TcÌùQ*(EÊ[#ùh»ý»?à§Ö(f½Õ5›dZO Ø ‹iâ¹+¿ø(Gü6ùM½Ç‹oÈn]NSÿŽÙ_¤ÿ¾-ø¿ã'›Ã¾•þÊÒlDRpã=ë蟆Ÿ³$^°·×|MŸq. Dã!‘¯Z9w*¼¥câçÅ3«7Ty’êßüð:ûã¿íÃã9šk›­Svë³HVû%¶+õÛ"v,úˆžÿØÿ‘kú¡Ðc±ðÜË&“j°ÁP1_@hz;«np$‘X×öÚLôp/ˆþ-(§èŸèͬ~×öÎM ^FÿkA#ô6Õ¯kãßÛ>Ì£éÈ㿇"oý Ð×ö+­è°j–åa‡JðÍWK¸Òî 2'Ëž´R”¦µ¨ÅŒ£FŽ¿W½ùË4þ1ý¶µ@ è:ä tÙá¤ú˜¨N³ûlžO‡ƒCém_ØÇ‰|9£ Oí•}+ÆeŠkiŒr|’!ÅmEÊKãg.24hJÏz/ò?•…‹öÎa”𿉈öÐ%?ûmOòÿmÁcá¨÷Ð%ÿäjþº<-â“-/O°5ê$Ç<ÞV¬jV­¬ßÞÎü. Z(ßÑ‘ü]›ÏÛ Ä`ÿØ ÿùœ³þØÒ¶Ôð÷‰žÃBÿíµ^^)ð°B×Öƒ¯ÞæáäY \«Æ{VÐs’º›ûÏ2¼¨Ò—,ðñû—ù˱ýµXe|)â’=´ ¿ù•íÿmXW2x[Å(=N(þvÕýwx_ÅaÀµ½l×¢ºCyV„êÕ‹ÖOï=Jl%XÞ£EþGñ‚eý²3χ¼KŸûÉÿÈÕ$q~ÙóœCáŸHÙЦ?ÊÚ¿®oøUí”ÝYd÷ 1\o$ïRQ‡¥tÞJñ¨þóÉÄT£JV©‡½ùËÑÿmâ2<âÒ?ì_¸ÿäjŽ[ÛbÜf x®1þÖƒ8þvÕýzx_Å+0[+³‡è ï]µþŸo©Û¤Ü8®iT©ZSyëQÂájÚ£÷#øÎò¿lãÿ2׊ýÀæÿäzž-#öÙ¸‡Â^-“ýÝàÿ+jþ®µí ãHœ€ ˆ÷ªºV©q¥\,‘§q].3jê£<¯mB3ä©B+ä¿ÈþULJnAÓÁž0ÿÂ~çÿ‘ª´ºoí± Û7…áùÁÿÒjþÊ-î"»ˆKnW%â_ ǨÄeˆbEÏ ÓnÒ“ûÏW £Í 1%þGò~ÜŒv§…|XÄö˜ÿíµ_MöøoOxÍî<7röÖ¿¨k‹y­f0Í”eï]¿…¼Nö®-/ìè ®™ÒšWRg•‡Äa¹ù*QŠù/ò?”ðçíð€¼žñ¢Sá»?ô–ª%—íÓ ü¾ñxaëáùÏó¶¯ì™¨w™Z¼ÓÅ^?5õšò9 W=:²nÎOï=^”#ÍNŒ_É‘üšDo‡ýÜ^ñsØxnbô’´†—ÿ‘~_øÕ”úx^ãúY×ôØ ‘>áòȵè¾ñL‘°´½<5µHT¶’g‡“ä©J+ä¿ÈþP§Ñÿà –ã}Ç‚|kõo \¨ým*—Ùo3Ïü"^0?÷.ÏÿȵýŠÜAo¨ÛÛ ®8¯×ü;>•;:s 9¨£)KNfuciQ¤¹£F-z/ò?–(,à 'm¿ƒ¼e)ô_ Ε­]ÿ„oþ "ãð‚xßþ¥›ŸþD¯éÊÞæk9EŹÁ«Ù¼7âHu(V)HŠu¡8ê¤Ì°Œ5m%N+ä¿ÈþFgð÷ü Û›ŸxÒ÷ü;r¿ÎÖ«.›ûz©ÈðŸŒÿ°ÿü_؆¡§A¨@a”‘Á¯×tô™Ø€Z"x4©7/Šl¼u(Q÷£F-z#ùs‡ÃÿðP«¥ oà¿J=WóŸåkR¿ƒÿà¢RŒ?|nGý‹·#ÿm«ú„ÐõÉô›…`KFzŠ÷ /SƒR€KëÖ¦ªœuRf˜*˜ZÊΚ¿¢?©ü7û~Û—>ñ”GÑô+üíê4Ñ?op~O x¿'þ “ò=_zö…«q‰C^!¦Ï§ÜgR<ºo™|næÕ þÆ6ôGó„?ࢮè|ãGºè3ŸåoK'?à¢.3'ümû\ý·¯êkÃ>&“O—ȹɌœƽ–Úâ+¸„±‚+:ª¤ììÁ¬5hÝSWôGñÙ'…o¨[Ë—Â^0‡fѧÿDR§‡?ov§…¼]“ÿPy¿øÍ\þ#ðÌñ4Ю%ö¯¼°–ÎSÀ(á­iûÛMÜáŸQ–´coCùÀŸðQ×1x/ÆŽ§¸Ñg#ÿDUiüÿ·ùî|ã8‡«hóý£_Õ†üI.Ÿ(¶‰Œ÷=«×1iªÛvtaYÕö°zÉXg‡­Æœoèã¾O~Þs.Ç𯋈ôþ˘í*¤¿ ?n‰Û àï»M6ã?ú.¿«Ïør}6VšÜn‹­rqÉ$L%„ìojè…)I]Íýç›[ råýÇóƒ?à¡Ú~tñÍ„j3¹l.ã}Dbˆ>4ÁA¼$ÆÙ5ÿiì¼t˜ÿ}¥WÚˆá½aÕ• \ÖW> xÆÖE5’C$ 5,3ß“Y8®nZ²g|%'|,#÷Xþ`ô?ŽðS/Köø‡ÆšŒŸÝ‚9¿ •Ü/ˆ¿à®² ¬ö³¸ÿãuúAñ[àŠþÞ¿Š<u,–‘0rÀ`Œžœf¾¤ý›?i{_YÃáßȰêq€‹“ËûœÖ˜œRæ¥+£“Äru=–"<¬ü=þÜÿ‚¼ùeñÿ.?øÝYÿ‚¼‘üEÿÀ[þ"¿ªee‘C)È<ƒRŠó>©;ŸÊ¯ö·üäÿÄ_ü¸ÿâ(þÓÿ‚¼Ÿàø‹ÿ~.?øšþªè¤3ùTûüäÿÄ_ûóqÿÄÑöÏø+Éþˆ¿÷êãÿ‰¯ê®ŠþU>Óÿy=¾"ÿßáG›ÿy=¾"ÿß7á_Õ]üªnÿ‚¼Ÿú(¿•Å|'ñä|qníý«ÿ ¡‚?öÎï¶y;?s»;výßjþåëù:ÿ‚¹®?lÝdÿ{IÒÏþAÅyŸÀUÿ‚ƒ¿Ã»oøgcãøC󈱚QgçîýöÝœnÝ÷½ëÙ¾Ïÿy=þ"ÿßwã_­ÿðH†Ïìk¥îë ÿÈ ×éå*Ÿdÿ‚¼Ÿâø‹ÿ.?ưÁ^Oñ|Eÿ¿×üUUtPò©ý›ÿy?ÇñþÿÜñTeÁ^OñüEÿÀ‹þ.¿ªº(ùTþÇÿ‚¼Ÿùiñÿ®?øº?°ÿà¯'þZ|EÿÀ»þ9_Õ]üª`Á^Oüµø‹ÿ—ürøGà¯'þZüEÿÀÛþ;_Õ]üªÂ5ÿy?òÛâ/þÜñÚ?áÿ‚¼Ÿùmñÿüz¿ªº(ùTÿ„Oþ òå¿Ä_üÜñê?áÿ‚¼Ÿùoñÿwüz¿ªº(ùTÿ„3þ òåãâ/þ .?øýð„ÿÁ^Oü¼|EÿÁ­Çÿ¯ê®ŠþU?áÿ‚¼Ÿùyø‹ÿƒ{þH£þ/ø+Éÿ—Ÿˆ¿ø8¸ÿäŠþªè åSþÿüäÿË×Ä_üÜòMð¯?à¯'þ^þ"ÿàêãÿ’kú«¢€?•OøW?ðW“ÿ/ðyqÿÉ4µÿ‚¼Ÿù|ø‹ÿƒÛþJ¯ê®ŠþU?áYÁ^Oü¾|EÿÁõÇÿ%Qÿ ¿þ òå÷â/þü—_Õ]üª«ÿ‚¼Ÿù}ø‹ÿ… Çÿ%Ñÿ £þ òåûâ/þWü™_Õ]üªÂ¥ÿ‚¼Ÿù~ø‹ÿ…Çÿ&Qÿ ‹þ òåÿâ/þ·ü™_Õ]üªžÿ‚¼Ÿùø‹ÿ…5Çÿ&Ñÿ oþ ðåûâ'þÓÿòmUtPò©ÿ _þ îå÷âþÓòmð¤ÿà®Çþ_>!áO7ÿ&×õWE*Ÿð¤?à®§þ^þ áO/ÿ&Ñÿ 3þ æåïÇÿøSÉÿÉÕýUÑ@ʧü(Ÿø+‘ÿ—¯áQ'ÿ'Qÿ þ âåçÇ¿øT?ÿ'WõWE*Ÿð à®þ^Þ·I{(3AÑnž9%WÌ.„aÎýÕWòÛÿ ý>>xïöËñçŠ| ðÛÄž ÑïÓI0ÞéúEÝͬ¦-2Ö' 4Q²6ÖB­ƒÁEx—ÁOÙCöòøðËFñ¿Á˜5)|¨ý X´!·²‹÷7C6Ø$»‰“#ç(2~ar}Sþ?þ €zÚjÿøUÚòu~âÁ7|!ã~Ç~ð—Žô[Ïk6ê¾m•ümq—Q¸• G V•à ŽA¾ç åSþ3þ zzÚjßøUZòuðÁ?ðS³Ö×Uÿª×ÿ“kú«¢€?•Oø`oø)ÁëkªáSkÿÉ´ÃÿÁMO[]Oÿ ›_þL¯ê®ŠþU?á€?à¦g­®£ÿ…E¯ÿ&Qÿýÿ‚˜¶º‡þßü™_Õ]üªþ¿à¥Ç­µÿþößü—Gü;ßþ VzÛ_áOmÿÉuýUÑ@ʧü;Óþ PzÛ^ÿáMmÿÉTüà¤Ç­µçþÖÿü•_Õ]üªû¿à¤g­µßþ¶ÿü•Gü;¯þ BzÛÝáKoÿÉ5ýUÑ@ʧü;£þ ÿÁŒßüKÿoý®çëÃ_ø1›ÿ‘«ú ¢€?–øswíqÿ?~ÿÁŒÿüKÿmý­ÿçóÃ?ø1Ÿÿ‘kú¢€?–?øsgíoÿ?ÞÿÁŒÿü‹Nÿ‡5þÖßóÿáü\ò-STPòÍÿjý­è!áü\ò%;þÓûZÐKÂßø0¹ÿäJþ¥è å§þÏûZÐOÂßø0¹ÿäJwü9›ö³ÿ §…ðauÿÈuýJQ@Ëgü9—ö²ÿ ¯…ðauÿÈt¿ðæOÚÇþ‚þÿÁ…ßÿ!×õ%E-ÿðæ?ÚÃþƒÿÁ…ßÿ!V~«ÿyý§4->}[Zñ/ƒ¬,­—t³O©]G/«3Y€+ú ¯øÇðcÃôô}rââÎ{RÏkq œE# e¢'cƒß#8û¬¹®œ$)J¬cZMG«Jÿg%à®ÏåWñÜz>·(ñÞ¹i¬ZÛ>4¦›ÉŸ̳Š{` '¨a_`øsÀ_m‚×á÷€ímôûQ–ÆÉ1å·'ÊŒ¹Pǘlß“^Áñoá'‰>øŸþ¿I Ç›Ÿo4•–ÅC?2‚aס#šÚýœ4ÏíoŽ^µÆv_ ÿðoÓe~ù€ËðX<ë`Õ×+|Û·§ÓCóìF"½jê»­ÿáËÿµwý|#ÿ÷¿üƒNÿ‡/~Õ¿ôðþÞÿò Q4WóÑú)ü»ÿ×jÏúþ1ð—þ_ÿò;þ¡ûH÷ñ—„ÿð"ÿÿ«úf¶¹·½¶ŠòÒUš ÑdŽD!•цU”Ž#jzòZ;æOþŸûG÷ñŸ„ÿïýÿÿ!Ó¿áÉÿ´wý~ÿ¿×ÿü‡_ÓU€þe¿áÉÿ´gýžÿ¿×ßü‡KÿNý¢»ø×¿÷öûÿ‘+úh¢€?™Ÿørwí߯Þÿ¿·ßü‰KÿMý¡»øßÂß÷òûÿ‘kúe¢€?™Ïøroí ÿC¿…¿ïå÷ÿ"ÒÿÓh?ú5÷øƒáïû÷wÿÆ©ßðä=þ!xþýÝÿñºþ“¨ æËþñŸþЇÿïÕßÿ¥ÿ‡#|eïñ@ÿ¿7_üEI•ùsÿý¼|Oû&Á¢x'áæ‡߉üOi5Ìzè-ig ?—”ˆcΔœœ ˜RÁÃb€?(þ0Á./¾xYüañgã?…ô ¯5Ìæ D†3Ü–m±í…w6çãä98æ¿IþþÇ?µßíõâµø«ñ?U»°Ð¯È/âp12ÂNvéöŸ&ô;Bà€Àñ_Ð?ìÑûüý–´ôèâûÄOËsP 6¡.F#à,1ŸîDÛˆÍ~A~ÅßðJ¿Œ ¯é~1ë·ß ¡±d¹³±Ò® :ãž¿¼™>[E#y,¬±žkú/†!1¬Ì#P ¹,Ç$òO©5-QEQEÿÐýü¢Š(¢Š(ùrÿ‚ÌÉÒé?ö.Zèéëà_Ù£ã¿ÀŽø»w£®»‡®ZY,Ù‚HÚ&(Ä®¡·!#†¿£ÿø(üâÿö°ÔôxZ·ÑüS¤Û›9c¼ ö{«}ÅÓæ@J:qÆ<â¿1¿áÌŸ´·ý4ûÿ/ÿ ˜ÿoÚÏDý®þ*iž5ðï†ßÃÚv§.ŸÚîã÷)yŒ(¾ÔP[Ÿ›¸ý€mß þÇÚ‡ŠÇŠ|+6¿iâ8¢Ù5›Æ—1Kí¨|Ìn眩ç Ò½“þÉûKÐsAÿ¿òÿñº?áÌŸ´·ý4ûÿ/ÿ Ìo‹><ÿ…¡ñ3Äÿ’hSÙX-ô—ÞSé¶¹¹ºaÊd s)’5;3‚›†ºôPQ4XãPª ÀtS¨¢€<Óã'Ã=ã/¯|,×p,üM§Od\ŒùRH§Ê˜ïE W_uùÿ®ø7ûMþÏW^:øoñÂèÞÔŒz•…Ó\ÛM¿ˆˆ&EX¥wýü[ $ý‹¢€ (¢€ (¢€Õð7íµñ&ïÞ‡ÃVgþBDÆØô#?Ò¾ù»_‘_µTÒø¯â ¦‡¸¹Šè¢Ž½¯O*¤NnÇËqf*PÃr®§£þÇßmÖ3ãMvÎÇt‡<ç8Ͻ~Ž^X[ÞÛ$Q‚?*ù_ÀÞh>Ól s€kè_ø–ø„?ïG\Õc\¥'%ÐÇ(ÑTú½Ï9ñ‡æÓ%. ˜ÏzÉÓµ;6ežp§8õ¯ ïl­ïàh¥PÁ‡ZðÍ{C¸Òî›ËË'Štq ~ìƒ1ÀJŒ½¤_Ñ5ÛmRÃ.9.µ¢Áª@U”ìkÁìo®,¦Û±zŠöÏx† Z ŠçÄaÝ7xž–  ðöu7<_RÒî4»†Še; àÕÍY¸Òg ŒLdóé^Ï­h–ú­¹V0ðíOJ¸Ó'0Èßzé£QT+Üò1˜9á§í!±ïf©m©Û‰"a’9Êx£Ã xun¸sÅy®—ªÜé—+,-û¾â½ÃGÖmµ{pË׊åœ%NZµ ôñTùe¹óì‰42˜äO^õèžñQÅ•éã³ÙñG…ã¼®mFsÅyÇ%¼¾\¹W^•ØœjÆÇ‘5SSM¥JÅsèÊâ¼£Å>{f7¶CƒÉ¬øSÅ ³¼n:kÓ™"¸‹ †Vœ©ËSßq§Š§~§Í™[ •a^‡á_4n-/[åè ¦x£Â¦7v£å=@¯>ùչ࡮þuUXù¸óájXúc1\ÅŒ†Få>*ð«ÀÆúÍr:)ÞñKDEëqÐ^©ˆ®¢ìÈÕÀù©Hú5ìñtõÜù©$–)/Êÿzõ ø±_máÁk;Å^hßí¶c Ô^~¬ÈûŽU—§Ö»tªö•0u,ö>‹¾°·Ôíš9`Ã^®hw:Dí…Ìg½v>ñI8²½l€“]õõ…¶§lc 85Çá-On­*x¸)ÇsÁô­VãK”K½Å{ެ[ê–áÔÀr+ÅõÍ ãH¸lcj­¤êÓé— 2“¸®ª´”ÕÑæáq“ÃO’{½â/C©ÂÒF¡dµxåœÖ3˜¥*z׿húž­‘HÝŽEføÃ°j3Ƹ•yÏ­aF¿#å‘ècpª½µ-ÎÃ>#–ÆaopÄ¡õ¯d†Xî#'!†kæÛ›iìç0Êe5Ûx_ÅjÂÖí²§€Mi_z'.Y˜8?gTë|Ká¸õšx ÏNµã7÷’˜¥YzWÒÑMñŒåMr$ðÜZ„M4# Ï]¯vG^g—s¯iLä<-â—µ+kzNÞ€šõÄxnáܤ2°¯›g¶–ÒSÀ«)ã5Ûx_ÄïhâÖí²§šºô/ïDåËs쪚^)ð¶I¼³^Ü^b|ÈßhHµôº4WP†\2°¯6ñW…‡7¶C¸4+ÛF^g–ß÷”ÊžñX€‹;Öàp 5éwö𭮬+ç&]¬V@C î"ÓTµÁëŠñïxr]6s, º×Úxš}6A Á܇ô¯]ìõ{L:8æ¹}ênýEJž.ê|왌îS‚=+Ó<+â“…³½>ÀšÂñ†'Óæ3Û‚a<šäA(û£È=«®QXž-:•0“³=ïÄ&âMãM¾‰g†t# #ŠüLøÙà]OàÏÄ1q¦»[­Ä†XÙ20 ãúõáqixùSÀ&¼öÄð5޽àfñD1‰n¬Ð…úrhÁIÂ\fVyBŠÞ=#öløŸÄOÛÍ4›îm³’rNÌ.kéèkñãö5ñ}þ•ã« # ãH]{g×ì<|õ®|Æ’Gc×áŒs­†÷·C袊ó£ (¢€ (¢€ üËý¨ÿà™žý¨þ,ÜüYñŒõ-îæÒÚÔÛZÁ ‘…¶R¡·?99æ¿M+ñïöÖÿ‚—øÛöXøÝ?ÂÁZ~¹kŸix.nnfŽB׉]¨1·Šûçö[ý4OÙoáD <=«Üëvp]Ü]‹›¤Hä-pA+„ãWÑuò7ìOûHk_µGÁ(þ*ëú=¾‡vúÕ‘¶¶‘äŒ-¾Ì6çç'w5õÍQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEpß<#>ëþp jv’Ç=`7DßðŸÂ²þ øºO|1ð÷ˆ®I7s[,W[¾ð¹·&²:ƒæ#ôÚùçá?üRŸþ!|6’xšõ€=àԇËÊGã^®÷˜Z”úÅ©/O…ÿí¿qÉSÝ«wÓõ_©ô5Q^QÖQEQEQEäž/øûð'áþ³/‡|yñÞÕ Ty,õ^ÎÒåA¹ ŠiUÀ`r9ŠõºþLÿà­°ù¶Ž¾ÿó×KÒ›ò· ý(ú¤ðüñ C‹Äþ×l|G£ÌΑÞi×Ý[»Ævº¬±3)*Fƒ]E~dÿÁ#fó?c?ç–­ª¯ç>ïë_¦ÔQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW/ãOèÞð¾£âÝ~O.ËNˆÈØûÎÝ4ÙØ…Qêk¨¯™›þ/wÅŸë|ðþçžñêÒž¨?‹žã§~_…I9Tø#«ÿ%æö_~ÈçÄUqVŽïoëÈÝøSà «Í3Wñ¿ÄË(î|Cãp$¼µ‘ÚÙc÷A\cj. ‚9n¹#5Ìøgö\ðŸ~.iÿ¼;YÙÛ­À“O|º£ÍF'!~c•lû8¯¨¨­ÞwˆR¨á+)«5ÒÖµ­ä´]Hú;Fêík~· (¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š*µå®£i>Ÿ}Ü[\£E,n7+£¬¬PAÁfŠiÛT 7|0¼ºøaâé¾kÓ4št‹%ç†ne92Ùƒ™,ÙY-Éã¹Nx úF¼ÃâÇÃöñÿ†Dlÿ`×ô©V÷I¼5½ä<¡'û÷\r0s‚@©>|@_ˆ~÷}ƒZÓäk-Rɾõµì?ð¶—âS¤ËçÙÿiYCyöyN2ñyÈÛàr¸è=+¶¢€ª¨¡Upê( Š( Š( Š( ÿÑýü¢Š(¢ŠñÏŽ?>~ÎÞ ÿ…ñFö[ íÛyÁ%Ãy²çhÙ-ƒŽ´ìtWæ¿ü=Ÿö*ÿ¡ŸPÿÁMßÿGü=Ÿö*ÿ¡ŸPÿÁMßÿ@¥Wæ¿ü=Ÿö*ÿ¡ŸPÿÁMßÿGü=Ÿö*ÿ¡ŸPÿÁMßÿ@¥üÄÁhìï.?j_ =¼J£ÁÖ#*¤Œÿhjq_µŸooÙ·ö…ñÔ~kwwÚä¶ó\¬SX\[§•Îù.FzgšOÚ#öÇý˜g¿Yx/ãMÓî^iñê…Ó$¼e’YbCæ"0µj/š'£ÇƬ}•SÇõ=2}*á¡™NÜðiÚV§q¥OçDÿ&yíÚÖoªÛ° oǼ;SÓ'Òî9—åº)WS\¯sËÆ`%B^ҞǹèÚ½¾­j$R7t"¹¿ø].ã7V«‰—tŽâ"­ó+ ò_ø]­˜ÞÚr‡’+OÂÞ)exÄÐúšôIaŠæ"†VÁ:r=éBž*•ÖçÍÛ—‚?z/†‘{<]=w>iRá²>V^•é¾ñXlï_>„Ô>&ð“Fí{d¹H¯9ÚÑ1þSÒ»}Ú‘>~õp•cè›Û+]ZØ«€C xž»¡\éS“·1†ºo ø¥¡e³¼lƒÀ>•é–vºµ©FÃ85ËJœµ=™Ò§‹§Íˆð/U¸Ó&Iao“<ŠöýX·Õ-Õ‘†ìr+Åõímä¨ËcÁªÚ^«>™p²FÇoq]i)Ç™f<<½œö=_ÄÞMB3=¸Ä£Ò¼j{y­f1J6ºŸå_@i½¾©Èóã‘X^&ðÌZŒFâÄ«Ïë*Ü_,Žüv_«ÛR9_ x¢KWw”Î5ëËñ‰#;¯šg‚kYLrŒ½«ºð¿‰ä·•lîÛå<W‰ÃÝsDË,Í~Î¡Öø—ÃQê —­xÍÅ´¶s˜e]O¾–GYT2ò qÞ%ðÔ:„FxW÷‹XQ¯öYÓ™eªkÚS9 ø¢[iÒí²‡¡¯\C Ü9RWÍsÁ-¤¦)†×SÅwñ<¶²-¥Û|‡¥k^†—‰Ë–æN/ÙU4¼SáAƒ{eÇry™ ‘µÖ¾—V†æ ÃæFæ¾*ð¶syd9êE,6#—FVk•s~ò™•áÉfâÖíò¤ãšôûËK=fÓ ùÝÔ£~VZî<5âw²qovÇgAÞ´ÄQOÞ‰Ž2²öUv1uÝ}*và˜Éâ°áš[y°pËÞ¾ƒ¹·²ÖìøÃ†Jñ}oC¸ÒnÌDðj¨WOFa˜`]'í)ìz/…üOìbÚäâQë]&«¥[ê–ìŽlpkçȧ’ÞUš‚=+Ø|¼´šÎc ë‚§Œ×Eáï\é³,36øz?ˆü;§Hƒ/5âWVÒZÌÐÌ6•‡S™Ty€u¯½²žÂso0 އµvÓœgOŸÄa§†©Ï¡"šÏUµÈĊÚò¿x^K9æÌf3Î=+3þ!›L˜G#,šö‹k‹mNØ8ë•̹©;­Z3§ŒžçÎ'(r8"¯ëJž'ð߇oÎíèpOÓÙxŸÂÆÙÍÝšîCÉ•ÀÊ’°á‚‘Ší§5/y;ˆ¥:”Ççïìçhºwík§§çQønû>ŸtWä_Áí1ôïÚjÉä3M;~a~º§Ý¨ÎW¾CƒîdüÇQEãŸdQEQEü¦ÿÁ`W¶çûÚ˜ôh¯êÊ¿•?ø,2ãö¿'ûÞÓOþ=0 Õßø#Ógö?AýßjCÿ„×ê}~UÁ[?²îø‹Rù_ª´QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÏ?â’øµðÿâ|–ú„²ørý½Rôy–¹ô :rO­} ^OñóøÃáf¿¥Xä_ÃÚìÙ~ø¹´"x¶žÄ²mükÑʪÆ5â§ðËÝ~’Óð½Îl\[¦ÚÝj¾Zž±Er>ñT7ðN‡âÛ|mÕm!€è®Ê7§ü²§é]uqU¥(IÂ[­ á%$¤º…QY”QEQEü£ÿÁ_aòÿlkÇÿžº˜ß’ºÿJþ®+ù¥ÿ‚¯üøÁãÏÚ¦-sÀ~×|I`úŸ¸ÓtË«ÈD©$á¼1ºîŒädPè‡üúo3ö†¿L¨¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÂñ?‰4ø~ÿÄúôâßOÓbi¦sýÕìvc€£¹ ¨AɨÅ]±JI+³Ë~3xÃY²·Óþx"Lx·ÅÌÖöÎ9û°éŽ‚5ÎÞ屌àŠô_ø?Fð…´ï h1ì³Ó£ ûÒ1åäs݉f>¦¼·à׆õ}JãPøÅãX > ñR¯Ùíß“§éjsol3јbI:eˆÈ÷Êõ1óTâ°°z-dûËü–Ëæúœ¸x¹?k.»y/ø;¿—`¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾lø“mqð§Æq|mÑbfÑïV~'¶ŒºvÃ|ª:¼áû”=¹5ôV¼³µÔ-'°¾‰g¶¹FŠXÜWGe`x ƒ‚+³‹öS»W‹Ñ®ëúÕvvf5ésÆËGÓÔu­Í½í´W–r¬ÐN‹$r! ®Œ2¬¤pA ÔõówÃ˯…þ/›àv½+>*Éyá›™L–€æ[6cÖKrxîSž¾‘£„ö3²w‹Õ>éìÿϳº yãw£ëêQEq›Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÒýü¢Š(¯€ÿा"ü|ýž~iÃSÖµ-.<¦‘b\E·Î@ã5÷åü†ÿîlïú#ÿÀØ?øª?á×?¶wý ‘ÿàlüU^TPòÿ¹ý³¿èLÿ`ÿâ¨ÿ‡\þÙßô&Gÿ°ñUýyQ@Ï×üŸöý£~~ÒvŸ>&øq4Í-.úݦ[˜¥"I•B ¨IçÖÁNÿc/Úöˆøù¡xÛáO‡ÓUÒ,¼5k§Ë+\G[ˆï/%eÚäT9÷¯Ýš(óþ û<|Uý~x¯Ã¿4¥Òoµ=R;˜eIwD±m'(H×䝯ïø&ßíq㌞9ñ^ƒá(çÓuoP¼¶Ý»ážáݲ2¤þ¨¨ ä7þsûgЙþÁÿÅQÿ¹ý³¿èLÿ`ÿâ«úò¢€?ßøuÏíÿBdøÿGü:çöÎÿ¡2?ü ƒÿНëÊŠò€¾Ö<ð3á׃|CÙõ]ÚE…Ü@† qkgR®G¤du¯X¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šcô¯ÈŸÚ R·“âõ¨„îx/NáôÍ~ºÉ¹5øÁñXüi¹Uäµóãõ¯c(ŠrgÅñœíJî~|3ñl7:•µÀòÎÀzô‘Cu×Õ«äß o‡A²#åp¢½»ÂÞ(«ex~aÜÖXœ;R¼NœŸ3¼U:¦‰ü3-Œ¦êÑr‡­q±O$  %{WÒ’E Ô%_ ¬+ǼOá™,¤k«a”<œQ‡Ä).V,Ï-p~Ö‘ÖxcÄë{Û]IØšêu-2ßR¡•AÈë_;Å<°¸–&!ׯøgÅÞÆ¶×M‰0eZƒ¼Žœ»1Xû:»žq®h“éS”*|®Æ²-®f³•fˆ#­} ©iÖú•¹ŠAœŽ x~·¢Ï¥Ü0e;;Þ…nefpft©KÚCcÓü5â8µD30+[YÑmõ[vVQ»ð[k™,æY£m¿JöoxŽ+ø’ °²(Á¬ëQq÷¢wà³Õ³¨y©¦\iS´3)Ùž 7OÔgÓ&Yâ$J÷MkFƒU·e`ö5áÚ¦›q¦\y3Tt8®Š5£QZ[žn?<<ý¥=kÑ5«}fÐ ¾0¹_x_~nì— Ô^y¦êé×{vÀÏ#=kÛ´MjÛWµˆÜ5Í:r¦î¶=,6*–*’ÜðOßÛÍÁ+"õ? ø¤JÎñ¾n€šgŠ|2¬¦öÌ|ÝÀåÿ¾‚`Ã+"šÝ¨ÔÖç–Õ\-]6>•š®¢(ãr°¯ñ7†fÓåk«UÝrk ð¿ŠCªÙÞ7=‰¯Bš®á(à2µqÂr¦Ïz¤)béÝn|ÎNTíÅw^ñ<–Ž-n[(N4Ïxj[76Ã1žHÅœW|Rªµ>mN¦vgÒË$WQ\:8¯4ñO…væöÉ}È›á¯=‹-µÛf3Кõ¸g‚ö ñáÕ‡O­p8Êœ¤„ébéò½ÏšH*N>F½ÂÞ)h ÙÞ:jÏŠ|-´›Ë%ùz+ÍY •+úWwÖ‘áoìÛgxÜv&½AH¡—æV¯še†M­”u¯Fð·Šv0³¼o¡4ñ8e¼IË3K?eTè"ªv{Ÿ9ÞZMe1†u+‚@?JèÛgÿ=ãÿ¾…Y¢ K«i$r£1èjz+Îü]ñá7€5Ò|yãmÃwÒÄ'K}KR¶³•¢bT8I¤F*J° ŒdØ×¢Wóÿª‡gíGáY‡I<d?Ô5þµJñç¾!é’ë_üE§x›O‚f·’çL»†ö™U]£i gPá]IRrŒ]e~<ÿÁ¦Ýû1x¶îxÂí¿ï­>ÀJý† Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾g×?âö|M‹÷¾ ð<é6¨Ýc¾ÕWæŠ×Ñ’½(än‘Ð×iñÇ:§‡4«/ x< ¼_â©Mž™ÿ–\f[§ë„>bpyÇÍuÿ| ¥ü:ð•…4¢d[e-4ïþ²âw;¥™Ï$³¶O^@+ØÂÿ³ÒúÃø¥¤|»Ëô^w}:¿¼Ÿ³è·ýêÿà­Q^9ØQEQEQEQEQEQEQEQEQEQEQEQEy‡Å‡íñÃ"ßMŸìþ•*Þé7ƒ†·¼‡”9þãýׂpH'Ÿˆ ñ Ââúò°kZt­eªÙ7 m{.9ùOÞCÎTŽr z]|Ùñ*ÖçáWŒâøÛ¢DϤ^¬üOm'u¸;a¾U^pÝICÛ“^¾ ûzU–ûÇ׬íîž~¬ã¯û¹{U·_óù~^‡ÒtT·V×¶ÐÞÙʳAp‹$r! ®Ž2¬¤pA ÔõäµmÀ¢Š)QEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(9~á¯Åo‰š_|»¶Çê.§ð&¿j&û‡øyãQÿ«/c©ÉüÍzùVíŸÆ? =OÔû ù¾²¹µ8@JæH– 9$ZöÏ*·†,üÃË_åXÞ)ð¿šîÍpÝp*'Þ³7¯–óRZ}ˆü+âàZÞ¶@Mz4±Cy GÃ# ù¨‰!—«¡þUéÞñH-•Ûr8Ôb(}¨^eÝÖ1¼Qá©le7V‹˜ÏP+‹Šá ”MÊë_IË7qpXWø›Ã2ÙHnmW1±çÚª{®YfYo#ö”ΣÃ>)KÅ×G;šëu :ßT·1J äpkçtw‰ÃÆûYç^±á%Ð[;£‡dÔW Ó扶]™*‹ÙÕ8 oC¸Òne-èk&Úê{9DМ?}©i¶ú¥¹Ž@G¼G[Ñ.4©Ø;3Á­¨VRVga€t¥í!±é^ñL7Ȱ\¶Ù­nkZ5¾«nrìpkÀ#šX&Yᇥz÷†|N—ж· ‡{ÖU¨r¾hÙ~`ªGÙV<¿UÓ&ÒçhdS´ž 3OÔgÓ¥’íë^é¬höúœ Fìpkõ-.}*gŽAò“ÅoF²©îÈó±Ù|¨Oš™ìzˆ­µˆ<¹0ƒ\׋<3o-§$ ó‹+Éì%çyÇ­{N…®Ûk6Â)HŒ5„àéÊè†&Φ燩hdÞ¿+©ýkÔü/â•-ë|݉ª^)𹄛ë%È=@í^rà›p%Yky(ÔŽ‡S SSéi¡†î’a‘…xï‰|15„­sj7FyÀí[þñRHÊí¾n€šô9aŠî†VÅ Ê›>‚¤)â©Ýn|ÏŽ~qÏjì<;âY´é–Û1ž(ñ>.1žýÙükÆëÑV©åeφ©séX'‚ú$d20é^q⟠}ëËEã©°<7âYtÙ–Þቈ׳A=½õ¸t!•Çó¯=ÅÓ‘õÌΜË.S^Òžç#áMk"Úß6Aîk×Hnáܤ20¯šî šÒSªU…vÞñCZ:Ú]±(xÉ­1t×4N\·2qýÝSOÅ>'uíÆ9 W˜ÛˆtÚÃ_KÇ$WP‡RWšxŸÂåw^Ù¯Hžã£+4ÊÓ^Öžæ†üK6›(†s˜JôûÛ-~Ëzà–ð&U†Ö¥uþñ$Út© Á&2*Þµûð9pïùuWcVÒn4ˆI gÁ4–ò  bW½_XÙkÖYP°àúW‹êºEΕpb|¤ðj¨ÖR÷YŽc‚•7Ï Lðlj㾌[NØ‘{šéu]& VÜÆàŽ |÷ ²[¸x›cœŽõë>ñdwj¶—'kŽ2k Ô_4OG/Ì£R>ʦǜêúEÆ“rѺæ3ަѵ¹ô‰ÕÕ‰‹¸¯jÕt»mVØ£N85áš¾™.—rð2¤ðkZu•EË#‹ƒ• œôö=ãMÔíõ;u’&‘È®WÅ^[Ôk«a‡ñ^s¢ë7LêÛ²‡¨¯pÓ5+mVØIÈ®zt¥u±ëaëÃO’[Ÿ:ÍÄå&M¬+§ðïˆgÒ¥ÊÛ¢5Úø§Ã)p­uj¿?p+ÈäŠHd1ʤ2×Z”f µ˜jžîÇÒVw°^Â$‰ƒ+—ñ/†ã¿‰§q ã½yC.™8ŽV&3^×gwô ,D"¸jRtÝÏ¡¡ˆ†&²ÜùÂæÚKiLS.6×S xŽKx. 1…${Wi⟠­ê«a‰oZò á{už)~V sí]ª¦µ>wFxy;l|ðâHî?j[Kˆú<óŸÑ«õq>è¯Èÿ„ ŸÚfÁi§þM_®ýÚY´m$ŽÎ•éIù¢Š+É>¼(¢Š(¢Š+ùfÿ‚Ê®?k]<ÿ{ÂúyÿÉ‹±_ÔÍ-ŸðY¥Çíe¤ŸïxSO?ù5x(ô¯þÌÙý“5Qýßjÿ%lÍ~´Wäü…³û(kCû¾,¿ù'dkõº€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?˜ø)ÁïkŸ·Ï…~ü<±û§ãÔÒ¿µÌnÎ$Ôµ[ùciˆbÛXÄcfÆ8$þŠ|Sÿ‚K~͉à[QøTÚ¯„Ÿø*î¿ñ“ã¶4¯ xKX¼·IþÏ=Ð2i­aj-£•ðfŒI¸êI¯¼~?ÁHü?ñƒÁú¿ÁoØËDÖ¼}ãZKf·vÚ|ñCcm8òæœ,e.ªØVdXÐìÿ.Öwüö’ø‘ñƒÁž4øqñ'U¸×åð[ØÍ§ß]»Msökß9Z eo™ÄmP¹-‡+ª z×ü§ÂõoÙ[ñ_ŠìV}sÃ÷6k¡O½•๼»‚9¶€B°x÷‘‚Gü“ö=Õÿdÿ…z›øÞHŸÆž2š J8ÌŠÖeaolpï™#;/ÊYö©ePÍòüßâöwÇŸ a“®ê·:¤ª>^‰C{3]dz•ö þ ûÿîøñ§ökÒ>)|cÐîµ-_Ä·Ïlñß\ZªÙÛÊm‘vDêÏ’2CqŠç?k?ø&‡¼7ñ_á-ìïá^ãÃ~'ÔE—ˆ‚ï Ó¡YíÀº’fÜÑ#E,»·6?w‘Îk³¶ý½*ý—|ã‹zÄÚ牼ChúŒ×3$q³Cu3Éjƨ¸æ>Üõ ¢Ù5ñÏü_Ãwzoìãâ¿Ü©HõŸÌ°çø£¶µ·RÃÛ{2ýTЋÿÁn<+ðþÏMøwãAb±øÛU¸¹²ûPvÝ.™džcFɧd× U±‘¸Œà×èçìû9øKö}ýŸ<2º^œ-¼Kâ­6ÇR×®X±–{¹c2ˆÛ$€¶âS`AbIü©ÿ‚˜É'ÇOÛÃáOìùhÆ[[TÓ,gPÕͬ]ï¸n:l"bzàWôSqÃÅ„DUT`8A@Îgüá—ƒõÚá•·‚´õ‹Ç^=G‡R;ŸµfKk-8²B•Û"e@È9"¿w>üðìýðëMøgðæÀXévs’ÌÒ\\2,ò³KÈFOaÐñâÿ…÷ÿ”ðׇ¿ããLøn¶™nª‡Jµ}K§µäÁ½~ûPóÿ"ø=àsöúð§Ã¯‡¶?bÔüzºWöÁÙ„š–«,m1 [kŒlØÂãäŸÕ™?à“Ÿ±;£"øRý  5kÜq™üÅ~Kë§×àª^%ø»„5¿ˆ6^ ÕîÑtßZ5õÙH·þ͆dx­Â¤…>¬+ì_~Þßþ7~Õ ¾|<ð¶·ð’ÊëW‚}b-bÝcÕ/ì÷³)†h“‚)yRÅŽFÜ*ý—ÿbï|ø‰ñ߯×Z&£aÃÙ‘<+}}ĺµ® ×-| Õ%ѯþ'Z[ßÜßÛ¹ŠäÚ_Ëö{H"•NèÄŽʈªƒ‚ÀþÔ\[Áwo-­Ê !™Y[ÊÃb+ùîýµ?e/‰_|c㯆Ϫø§à­ÕÞ…{go“}.•í{awnæcšÃbåÈ ¨ùN}Xþó×X;ü¥£ûš_yÈýÚßâ_Šÿ€ÿì]þ û.AàGÑ<\5M{Å—Vä\x¯¦ŠavêwMº¿’ó•YVC€3“óìðã¯ìµû/~Òšïtb PÙI¦è,²¾÷H§Œ][[©l«ÉqQÒ3ίsý„?঺'Æéô¯ƒŸ‚h¿\-µ¥ø,õyT`).Ÿsî;}¤¬uúù^QÖ??²÷ü+áìë7Ç_Ú–ÓP¹¿ŸM¸Ö¿²ÒâK±Óà‰¥O7fÙ ÏùŒ €…+¸1?:ÿÁ/¿c…¿´óü@×¾0i“ßèú±¶²Hne¶ÍÍÏšòÑ2“±>q_´¿ðRˆð¯c?ˆW0ÉåÝk¶ðèÐŒãöŒÉ Ëÿ€æSøW”ÿÁ"¾ÿÂû Øx†XöOã=ZÿT$›Ë…”cé‹bÃýì÷ ?dÏÙGHøQÿEñ—†ü9orÞøm§M¨XMs—Ãj––é>fvD½× “Í@”Q@2ÿð[vþÑ^ Ÿûþ‰ï›ë³ÿ³WôÑ_Ïü7á‡Ä_üið6£àŸ ê¾ †/´2I§ØÏv¨âîV Æ`8<ÐÐßðDÙ·~Ï~7ƒûž(vÿ¾¬mGþË_³5øýÿsðG޼ð{ÇZg޼;©xv{v9¡R´šÍäCk–E™T²åq‘Ækö€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ÌÖµ3ÃÚE滬ζ¶6<ÓJÝdŸË·SÐV|Ñã6oŒßáu¡/áO ›2Éyá›™L–€æ[6cÖKrr½ÊsÀWÒ5æ?þŸˆútÿ`×´¹V÷J¼5½ä<¡Ï?#}×ðsŒNøSñ|BðÀ½½ƒìÞ+Yj¶G†¶½‡‰Ýo¼‡œ©ëkÖÆ¯oOëQßizô—ý½×ÎýÑÇAû9{'·ONß/ÈôÊ(¢¼ƒ°(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š()¾á¯Ã?ˆr4_µ©¨Ô¤þf¿seû†¿ þ ¡—ãþ³ò´¤þf½¬™^NçÂñ«j·sõ7áÏ‹¤ƒC°†ëî5ïÍÜ+$gr0¯“ü9ÖښȸùF zo†|K-„‚Úäæ:χ\׉Փf|±P©±Ðx§Â¢U7–c 9 W—:8ùdZúR)¢º„:á•…yçŠ|,%òÐr¼àVx|GÙ‘Õ™eœëÚÒá_î gxyè ¯G–(®¡(À2µ|ÕûÈ%Ç)"¥zw…|R-¥éÁib0ö÷¢NY™s~æ©‹â_ Ia)¹µ\Äy>ÕÅ$¯ ‰!?2ž¾µô¬±ÃwGÃ+ŠñÿxfKk«q˜ºàUQ¯Ì¹dg™e®/ÚR:_ x©.lîÎ$ ×c¨é¶ú¹ŠU ‘Áô¯a‘ãN§k-zÇ…üP.Um.Îp ¬ëÐqÖ&Ù~g¯eTàu½}&å‘1žXÑJð8–#†¥}¨éÖúœ$äpkÄu½ãIœ«©1òšÚdՙǘåΓö”ö=Ã&[¸ÖÞèáú{×C¬è–ú­¹ £~85àË, °Ãž+×|1âu¼Œ[]6$ Öuè¸ûÑ;09„'eTóKJ¸Ó.LRð¹â«YÞMc8¸¶àòkßu=&×R€«¨,G¼CYÑî´©ŠÈ¹Œž tR­‘å{œ8ܺTeí#±ëz½m¬Ûy2ã~0Aï\ŸŠ|,Ñ1¼²L©äÚ¸;;Ë>užǵ{6…¯[jöâ ÈߎA¬' RwG^ L9*|Gˆ®èdʬ¦½KÂÞ*…³¼<ð5GÅ^hɼ²_”ò@¯;V’ 7/ µ´”jÆës†Ó RçÒ–ß@a”d5x¿ˆ|?6—1–1˜õ®§ÂÞ)Y”Z^€šô ›h/ 1È+­q©J›±îT…<\.·>kÁÛµ‡µÖh>&›KuŠBZ#Å7Ä^›K¥ˆr‡?ÅÒ½Æ3ó/Úa§cé;K«}FØKÎŽ9çÞ'ð¦í×vK×’®_ÃÞ!›J™bbLlyÍ{U¥Õ½õ¸’& ¬9¯>Q•'séhÖ¥‹§Ê÷>otx˜£uEv¾ñ3YH¶× ˜5Ðø£Âé25å ÃõÀ¯)d’*ãk íRUccœja*yíªivºÝ¡x°XŽ x¾¥¦Üi× £å®ŸÃ%{9E½É; ÆMz§¥Úë¶{ã’2¬#7F\¬ôªÒ†*ÑøÒuk.e’&ù{jöÝ+WµÕ­Æ'ŠðÝKL¸Ó§0Ȥx©4½Vm.ádC•ïZU¢¦¹‘Å‚ÆJ„¹$wÞ*ð¶äkÛ5Éî+˾hØ/ñ'C_@éZµ¶¯oÁ#W ⟠2»²^:‘YQ­Êùducð*qö´I¼)âsòÙß¿=5ê U—#•5ó߀ýÖZõ ø¤HáÆ8ÒÄáþÔMr¬Óþ]Ô5¼QáÈïá3Â1"׌ÍÐÈaa”õ¯¦ÆÙ#ƼÿÅK¤7V£H°Ø›{²/3Ë9¿yLç|1⇴qivÙ^‚½r7Šê-ëó# ùªXä†SŸ+§Jîü1⇶uµº?/Lšªôo¬N|·1q~Ω³â $¡®¬Ær@¯+•61¿x¯¥£’+¨·) ¬+ϼUáo9îÐr TQÄ8»3LÇ+æýå#—ð߈æÓeX&;¢jôíBÂÏ_²Ê`’85à®ydmeëšë|9âIôÙÖÛ17ZÚ½ ®h˜ Á?ÜÕZº¾•q¥Lb•~NƲ㑢a*äW¾^ØÙkö[†# û׊êÚLú\í—Ö®neicrçN\ôö=Âþ*YÂÚ^­Øšêu­ßW¶a¿ðähÈtá—¡ÍzÏ…|H—¶w-ûÁÀÍsÕ¢âù¢z|geTó]KK—L¹0̦¯è:ܺEÈ*I‰"½ƒZÑ-µkv Øë^#©isé“%Rt5µŠk–G3 <4ý¤6=öÂúÛQ·ÂÁ·kñ?†Ò5Ý¢þðs\ƒ¯Ï¥\.ã”<öûûmJØK r+–Q•)yÍ Ôñtí-Ïœe‰ásGzéü=â+.EŠRLdþ•Úx£ÂÑÜ¡¹µaÎy$‘Ë †)Fk¶3Dx©ÔÂTºØú>Òî øhˆea\W‹¼3Õ¤÷vãkì9Çzã<=âtÉÖ)Pšö n¢»ÓÞXÎC)®9AÂZ÷Ö)â);ïcò_à½ÈŸöµŒ}ègOàWëôv¿>ÿÉÕ¾:}®ëù½~ŧݮœÙÞkÐòøCøRõEW’}€QEQEWæGí‰ÿÛÒ¿kŠ–_ï¼{7†^ÏJƒKû,zrÝM4¾g˜gùØÆÞ1œóÇé½~X~ÛðR GöBø±¦ü3´ð ^(]CFƒVûSêMhTÍqq—å‹ys#;·s»ã$êØëöZ²ý‘~ß|2°ñž&Ž÷VŸT72Û VS<0CåìY$'9Ï|cŽ~¯¯Ž?bÚªïö¾øQªüK¼ðÚx]ôÝj}$ZÇtnÈm­§ó7˜âÁ>~6íþ眱袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®?â‹ì¾xľ=Ôqö_ é·š”¹8-!i˜~K]…|mûm|*ý¡~8|(ŸáGÀ½GÃúE§ˆÒX5Ë­fk¸§Á£dŠÓìÐN¿½Ã¬ÅÇ €¼±*ùÿdøyŒu‹|_i¥-ÃÚiÑÉqÊ[‡’êìáÈ„þ5Ã~ÓÖ–¿±·üÓÁüxwþ+:eõô€Ajm®çk-J-‹…ÃÌÄcØ0Ðÿø'¯ìµûG~ÉZf·ðÿâ÷…5/ê·êk6—=ôº’_¼p@ùöÐD`1Å“üAºd1ÄðQŸØoÆŸµì> Ôþj:N•®xe¯!¸}VYáŽk[¡(V‚ Ø´o Üö Óªþt¿nïø¿ßðS/†ßcýý†ˆÚ.y丘ê_²È¹ìŠýšø)¤þÔ>ø1u¡|a¼ðεñL¶’ßJº°š÷ìWf+u[y5–‘]æÎ0ÆFß™Fã´~U蟰íõ¡~Òµ5¿Š~\øÚêêîì™õ9mbk¸^ܪEöBÇ”,J€2I Ú?‹ <ñ«áö¯ðÇâ%£_ø{[X–ê•áfJ“¦$Œ«.5<Õäß³'ǃ_ôMgÂ_Ré´†“C ,ÒE¶ÖE¶C_e“{c à N9¯Œ¾&üÿ‚®|Pð~£àCâÃÝOÕ¡{{§ÒΡoq$2 ‰t?ø¼ß=úÙþç}ÖœöïêÁY˜ÌC¸Ábªß{q?ч.5+¿iwZÌ~V¡5¬pƒ³2ãðlŠü®ø{û|Qø¹ñ›Hý£ÿm/iþ1¿ÒD3i:.$Œ°+<‰1#þXBdnd‘ÆU¿Z«Ëhë? à·>?ûÃÿ†ÿ ­äËë•Ö­2)ä-„"·{1º|{¯µ~´þÏž þxáÛGåMáýÂÒqŒfá!_=ˆõiw1÷5ù#ûWþÀ¶·íGñ>'k^!ð=…¦‹k¤Y%Þ¥²HeiSÍÁÃJå³!Î à| WìßöøˆÞ ÓńңñfÇûxÑwÓ·ïmžC\ªËƒÒw d`Ò´¢Š(®;Äÿ>ø"X`ñŸ‰´½K•/ê°Z´Š§¨•ÔR+±¯çOþ ƒß|+Ÿûún¤¿÷ÌПýš€?  ø÷ÀÞ;Šâø‹Mñ Vl©;é×ݬLÃ*®af Hõ®²¿ ¿à‡“nðOÅ{îj[ßPÎ?öZýÕ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š*¦¡e¥XÜjz”ËmiiË4®p© ÌÌ{M4›v@Ùæ_¼}yà¯ÁeáØ…ßŠ|A(±Ò-ºï¸“¬Œ?çœ#çrxè Íl|2ð ŸÃ [xzMÝÛ³\_]¿2]^LwM3“É,Ý3ÈPjó/„ö¿üOwñÛÄP´PÝFÖž¶`Û鹿à©é%Éù³Ô&%H¯£kÖÇ5F ÷—¯oHþwò8è.y{Wòôïóü¬QEy`QEQEQEQEQEQEQEQEQEQEQEQEQEQEWÍŸín~xÊ/Ú$LúMØŠÏÄöÑ‚K[ƒ¶ÕQÕà' Ô”8ã“_IÕ{»K[ûI¬/¢YíîQ£–7•ÑÆX àŠìÀâ½”îÕâôkºþµ]™z\ñ²ÑôõÖêÚúÚÛ9V{{„Y#‘etq•e#‚9§¯›~]Ý|-ñ„ßõÙYôÉÖKÏ ÜÈrdµt¶LǬ–äå{”瀯¤¨Ça}ŒìâõOºèÿϳº yãw£ëêQEq›Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š()³åœWáï>_Ú+T=s©IüÍ~áË÷ ~"øªÜÞ~Ò¤#Œêr3^ÆT®åè|GíNýÏÖ 'éªxBËbíuŒùWwg-”æÞ`C/­{_ƒbðõœ*ƒùRx‹ÃÐê0<‘.%õ¬iâ—¼w×ËTè©ÓÞÇá½£‹;³ò5ë±ËÌ[—çFóeÄÚ\¤ùYOZî#¤3L¾ÿ¼¦QðÏŠØ:ÙÞ¶{kÓÞ8nâÚØta_58–ÚAI×¢ø_ÅM-¥ãqëSˆ¡öâN[˜ÿ˺…xb[æÕ ŒòqÚ¸ˆäxœ:6Ö^‡Ö¾”u†ú :8¯!ñ?†d²‘®­—tdçµiB¿7»#,Ë-å~Ö‘Òx_Å)r«ivØqÆMvZ–§nÑÌ äp}+çHåxŸ|'k õ¯ xnZ]¶$dÖUèr¾dm—f qöUO?ÖôIô©Ø:bÃ3ÛÈ%ˆ•u¯¢u6ßT·1ÈÈà׈ëzúEÃRcn†º(×RVg?.t¥Ï ŽûÃ>+[‚¶—­‡ìOzëµM.ßU¶1È3‘Á¯ž"‘à`ñ˜r z¿…|R.Z]œ8èMcZŸ4N¼bª/gTóÍgFŸG¸eq˜ÏBk>Öêk)VXX©>•ô§¦Ûj¶åAÈàׇk5Æ•pD‹”ìk|=e5Ë#“€•9sÓØõx†R³]°Œ`õ5Îx§Ã"×ÖƒŽ¤ à-®f·K a…zö¯Ûj¶ÂÖïïã=ë)Ótåu±Ñ‡ÄÇJ›ž8ã"T8eæ½WÂÞ)„´¾l1èMcx›Â²[»^YŒ¡ç¸UycpÈpËZÎ*ªº8á:˜jžGÒV°_ۘ䕅x·ˆ¼9>›)’5&6æºÏ x HÊñ¾n€šï®­`¾€Ç ¬+Ž2•7f{µ!OOš;Ÿ4îÈ®¯ÃÞ"›K˜E!ÌG×¶iþ"ðôºlí,K˜Ï5ɯ#šôHê|»SÃTÐúNÒîÞúÜM ¬+…ñG†t7V‹óx®C×®ô¹Â–Ý¡¯mÓõ }NØI#‘\ƒ¦î¦¥Zž*’ÜùÍÒHÜ£ðË]dž¼M%œ‚Þàæ3Ç5Ðx£Âë*5åšáº‘^U$o ˜Û*º¡ËUk¹áÕŒð•è{¶©¦Zk–›ãÃ1ä^+©iÓi“´3xÍuñ,–.¶÷M”c Ê@ YÒuk.á]1šÒ­uÍ‹•rOc´ñG…J¼³SÏ$Õçž7,ÖCÍ}¥ê–ÚͨäG"¸xq s}hŸ'p+:5­îÌḛ́<ËÛQ/ø_ÅAÂÙÞ8 cÞ½,luÈä5|ÄŽÈùCµÿ•zŸ…|R-•ã|ÀM,FÞò:2ÌÑ¡oÅKµ76«‡àW‘È’A!‰Æ×S_M†YpäàY™´ý•CoÅ>wf0èå2ÄbcœyÍ}.’EuäÃ+WŸx§Ââd7v‹óH¬ðõÜ]™®i–ó/iHå¼9âI´é„6èOö¯M¾±²×ì·. #ƒ^ èÈå\muí]w†üM6›(‚àëQ¿¿“Žÿ—Uv1u&ãK¹1J¤&x5› ÒC(žµÖ½êúÂË_²Ü0I¼_TÒfÓ&tpBçƒUJº’å–æ8ü©KÚCTzo†|Qê-­Ó õï[ÚÖ‹o«[²7ö5à0Í4²ÂpËÍ{…üOôbÚàâAÇ5ÏZƒ‹º=>5#ìêž[©é“é“´3)3Z:»q¤Ü$˜ÏjõýkE·Õ­È oǼCSÒî4éÌ1Ñ«jUTÕ¤qbð“ÃÏžý§êú°’È®7ÅK¥7V©óŽp+ƒÐµû*u Ùˆš÷ ëmJÝeˆ‚t®YAÒ•ÖDZBµ<]>Yn|ã5¼°JÑÊ6²ú×S¡øì –Þäþì!äô®ïÄžŠú6ž¶EýkÆ®á–Þ+ˆæR¥TŠìŒã5¡óõèÏ &Ï„~8Ÿö¤/F»¹?«×ì\v¿¾gþnCqqÿ³Wì²}ÚY¼m4tð[½ ?1ÔQEyÙQ@Q@/ÿðZ•ÇíQáƒýïØŸü¨j¿¨ þaോÿCáFõðu˜üµB€>ùÿ‚)·übçŠ×ÓÆW§óÓ´úý¯Çø"“Æ2x¹}<_t=>Æ¿a袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯¿mÙ3Aý¯>'‚//Æ‹­éW"÷JÔL~h‚m¥$@T´R¡ÃA+s·ëÚ(ñ§áwüÓö£¸ðæ‡ð“ã߯ãsð§Ãwˇ ´«5ÌP?™ 2^§'ó5ídí'+ö>q¦—sôÂþ"½±¶¶,å“hÏÒ½ãLÕ Ô Yb`}}«ç¦ÒfÓ,áóAÚTmü«SEÖgÒg,õZÏEI]`1ó£%ìzg‰|1ümqn¿¼×Oo-´†)FÖZúMÕ Ô­ÖHˆ$õÎø›ÃQßFn­×Hõ®zUœ}Ù†?‹ÚSÜå¼/â‰-\Z]’cìMzÒK ÄAЇF¯šç†KYÚ9 :WuáoV[[¶ù=«JØ{®h˜å™—/îêìkx¯Ã+ 7vË‚98¯+!£|te¯¥Õ¢»‹Œ20¯0ñW…Ìe¯,׎¤ 0õþ̃4ËoûÚC<+â—F[+³Çc^¢éÜ%[ Ž+æ’Y_<£­z7…¼PÈVÎí¸è ¥ˆÃý¨-Ì¿åÝB‰¼2ö.n-W÷g“ŠâbgŠO:2C‡Ö¾“t†ò0¬>µä^'ð̶2›Qû“ÛÒ®e/vFyŽ[gíi'…|N.ZÞ6p3]†¥§ÛêvÍ Ž |ì$N$Œ•uïë^±á¥Ò-­ÛbAÀÍEz/š&Ùvf¦½•SÖ4I´©Ê²á àÖHé(’3†_Jú'QÓ­µKs ô>•â:Þ‡q¥ÎÑ•>Wf­hÖRÑœ9†ZéKžžÇ{áoý¥Eß 8×]ªi–Ú­¹Gœpkç¨ÚD`ñ1V^•êÞñJ\ªÙݶ$Yâ(4ù và35ìê}­hÓisËžµg[\Ik"ÍÚÀæ¾Ôô»mVؤ€Ž x~³£O¥\•IBx5µ êk–Gc€tß=3Óü?â5k³]ýò0s\·‰ü2֌זk•<=+‡·ž[iHX†Sšö Äj° KÀ7‘Žk)ÂTåxìo‡ÄCgSsÆ–FŒ‡Œí õ¯Tð¿Š<ÌYÝ“‘ŒY(ð¹´-wh3äÚ¸gƒ#PÖ³JªºÜã§:˜Jšl})umü7•…x·ˆ¼=6™3Kæ6=«ªð·Š|ж—‡p ®úêÖ ø rÁ…q+Óg½R<];Çsæ³É {×C¡ë³é7 ªIŒõkÄ~›K™¦ŒŒúW)œœž+ÓRHØùiB¦wGÒ…¾§n& ¸r+‰ñO†ÊníWæq\.­Í¤N0Ä£E{u†£o©[‡Œƒ¸r+Ï©MÓ–‡ÓR¯KO–{Ÿ:H…\a…vÞñ+Ú:Û\cTeìç±ÔøŸÂík!»´\©ä\¹ò ŸÖ½ûLÔm5›LdF¯<ñ7…ÞÑÚæÑrN*(bùfmËÓ^Þ™³á†+gzØnÄô¯Iq‘È5ó9WÜ OÓ½SÂþ( Êñ¾aÐúÖx¬/ډוfŸòî©kÄþKµk»UÇ›9†`@Ï·¥R5#Ë#‹…ž|ôö=ûO¿ƒR€Mg=k‘ñŽ‹hú]Õà0Fæ¸ë·LãqÌdò+ѼC}£á{¹an dþ•Ê©Jœ´Øô劧ˆ ù·±ùAû?d~ÓP¯¥ÍÏþÍ_²ë÷E~2þÏÿ3?óñqÿ³WìÌtWVpýõèy¼­FKÌ}Q^AöAEPEP_™¶§üŽ/Û âv‘ñþ Ÿ ÿehñi?eWÛ¼Ï*âyüß3íPc>~Ý»OÝÎyÀý7¯ÌŸÛcþ 1ÿ wñFøÿ ÷þïí}%5O´ÿk}ƒËß<Ðy~_Ù.7cÊÝ»pëŒq’î±_ì›ì{ðÛXøyŠ‹­«Iªý¤Ù}‡Ë2[×åùÓçNíÛ‡\cŒŸ°ëã?Ø—ö¸?¶'ýoÇ­áQá#£ê­¦}˜_}¿ÌÛSyžg‘ßõ»ví=3žp>Ì Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ž~<ÿÅ7qàߊÑü£Âš´qÝ¿÷týG×ý B>•ô5r^=𵿼­øJçu[I­Á=ÝNÇÿ€¶}+“øâ™ü_ð³@Ôïò5 ûâ·ß6dÁ.ïBY7~5êÕýæ2ëo“Õ~<Ç$}ÚÍwWù­èzÍQ^QÖQEÏëþ-ð¯…|Q¬ÙhépXF×—Û‡+ÛLŒ¹ÆFqÓ5ÐWáüÞøK?÷/uuÿ¾¢¶?û-~ÙøwÇ> ñ|—øOÄ~·% S2ØÝÃrb¥ÄLÛsƒŒõÁ®¦¿¯ø!äÛ|gñbïØiMÿ|Ëp?öjþ‰h¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯Äoø-w„þ… ñÕÕÿ„ÕµˆôÛ[ ìû4As4ñ”΢SÎÝÀ‘ƒ‚Aý¹¯çSþ 9ã;_üiø[ð‚âý,¬ôË¿º™îá:­Èƒ|˜ÿžij[Ô÷ |ýœà–_³—ÄÙ¯ÀÞ(ø‡a©Ûø»ÄšD:Åí¥ü¼fôáÛ‡…JDè0Pò9ÉÍyìñ7âŸÀÿÛsÄ±Ž¿â›¯øJ9õ[5»‘æòéÑÉsІfò|È‘„‘©ØY½@5÷7Ä/ø)ìyðoÀ)mà§Œo´ËdµÓ´*9X¸‰F¯;¢Ã.âX¶>ê1â¾Mÿ‚fþÎ_=¼ø£§ÜßøkÃ6–©QÜÉlßj½•Ìe¤ˆ©m±ÂàŽ™`Hé_½¿·ÏÄøV¿²ÄÿG'•qu¥>—  æj®¶@¯º‰‹{c=«ò?þ £û\~Ë?³ÀßXüOñL–>-×µ™n¤´‡N½¸²CqÛ¯™- %„Œ?yÆîq@ÿn/ÙµàŸoàÿÿ²Ç‹õŸ ǨêFÆæÅï ÑùËO@Äˬ‘Ì$SÇbE~õ|ø?Ń~ø™wn¶—>)Ñl5)¡\ìŽ[¨IsÉPÄ…'¨Á¯ÃŸ7?૟<-¡ü&ðΡáoƒ–WmV„ÃÄ®Á'@,²Hо\0ÆÌÀ–22;?\>>ø§Mý–?d?êÞÖør=;HÞÙ1ʱ¥Žãܬ}hóGö¨ý«¾3~ÒŸ´L_±gì—«I¢[Gs%žµ®Û;E#Éo“w¶xþx­m‚•r˜y\©PÿDÙÿÁ!ÿeaàI´ hjÚ§Šnbo3Är_L·BåÁ&U·Vû67…xÜã‚äåÍŸðE/„&=Ç¿õdó.u)ÓA±‘ù.-·7“É;B3ë¯ÝÉeŠžyÜGjY™Ž¨$“Ð@†ÿðJ¿ì>(xóö@ø«K®'ƒãº“LžyZF¶m2élîm‘Ÿ.bmêñ©8Œ#0p?c~)øKÀ¾9øw¯ø_âmšj¼µs¨Ã#:+AïI-+ ¥dx¯çËþ q$Ÿÿo/‰_ìÕŽŸ%ž·¨ùÇÍ©j”VVcöM~Æ~ß??áZþÈüAžUÅÖ”ú\$7™ªºÙ¾ê&-íŒö ÁOø&7ì‹ðËö¢ño~)i×7þðÍ¥ªAw2[7Ú¯es"*X¬p¸#§Ì +Ömow¿ðN_øâÇì¹âý_B²×®'Š}:{¶–!5§— VQ´OªÄ“³ÿ‚CþÊÃÀ“hÐÕµOÜÄÞgˆä¾™n…Ë‚L«n­öln9 ñ¹ÇÉËÿ‚Uüxø—añCÇŸ²ÄíZ]q<ÔšdóÊÒ5³i—KgslŒùsoWIÄaƒû‘,±AÏ;ˆãK31ÀU$’z+ù·ÿ‚\I'Ä¿ÛËâWÅ;5c§Ég­ê>cqójZ„F%#Õ•˜ãý“@¯?·çí­~ÌŸ³Ž¯ã¿ #Ô®`Ò4¹$Pé ÍÐv3<1Ž(äu\.àW"¾ý¿àŸ>>ü+Ò¿hOÚ§PÕ~ x§ÇQ½ò%Ρs[Û;0‡2E"ÊîÃ÷Ÿ|"†å%¾ûý»?g Cö¡ýž5‡ž–8|AeñoÄŸŒú}Æ¥§Új)¥é°Ãu5¨ÅÍq#Š–âXÕyÇÞï^±ûxÿÁ1|à‡‰ÿe_k:†¿ý«¥îk%Ƥ^Òheo?kotòäDRA ‡ç ªÿ²¿í¬~Ê?²•¢Áð'Æz´¶ÑÝê³k2XIk¡Ü}²V–)þÙ±ÿr 1ûpÁr5úÿÛø•ñ‡ã?À;ï‹?5§Õï|G®_¾œ¾TpÅoaÈ„Q,j¿(eq' ñY7rì}—ðÃÁ–ÿ>xSáõ¡ éV:bЋ8þ;s]ÍR¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ª÷v–·ö³XÞijÛÜ#G$n+£Œ2°<AÁbŠiÛTÍ¿ ®î¾øÂoúä¬údë%ç†nd$™-AÝ-“1ë%¹9^å9à`WÒUæ_þÿÂÁðÇÙtùþÁ®é’­î•z8kkÈyŒçû­÷\`ä™…?á`øcí—Ð}ƒ\Óek-VÈðÖ×°ñ"ãû­÷ózäõ±«ÛÓúÔwÚ^½%ÿouó¿tqÐýܽ“Û§§o—äzmQ^AØQEQEQEQEQEQEQEQEQEQEQEÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š()~éúüZ¾ÿ“¦¼ÇÕùšý¤›îþuø©ªÈ°~Ô—²ÈpƒT|ŸÄ×±”«¹_±ñ\\ÿ‡ê~Á&“oªè°G*‚ÁÓŠò=_G¸ÓnŒL¤®~÷µ{'‡5;+Í6Ýmä „_å^_ûCOs§ü/Öõ==ü›«xÑÇPF+–ÜOcׯ—B´#(nUÑ5 í.áY(zŠöÝ>õo­Öl`‘ȯåSÁ¾=ý§~.xÏYÑ|1â)PXÌW€ÄIÇñ{W¾Að÷öڱܶþ,›$÷W?û=U[M] P|µ$èľŽý źâQùšò «k‹9 \)F^¹ë_Ž_ð‹þܺ»k)âu5€n1*0'ãïô?¶Ÿˆ¯ü^>|Z°}?Vy!¥þ&ééOQ¯vdfY|j~òëï†|Nö®-.›r`Ÿzõ"ž<ðèâ¾cŽhäHæ·9GPÀýFkÓ|-â’¡l®ÛÀ&«†¿½·2P^Ê«Å>*ÆòÍxêEy³oI3÷]ké "¸‹ûȼ·Å^d-wd½NHañ?e†e–ÿËÚBx_Å--oÀ&½Aã‚ö­‡GóKyˆüü®µèþñAŒ-¥Ùã &¦¾ÞôC-ÌWð«2‡‰ü4ö25Ű&.¸ì+ˆVx[z²/§ZúRD†ò 0 Œ+È,IQm/N×k:ô,ù¢teÙ”j/eTáu­ ëJ›, Œw¬«k™­¦ÂÄô¯¡¯ì-õ;bŽñ-oBŸK°?t{Ö”±)«3“0ËåN\ðØô}Ä6Ú´d¼ûä`ç½r¾%ð¹µf¼´CÉ¥q×3Û¸š‚¦½ƒ@ñ¾«oö[7c÷©tܹµ†öÀ2šñxvm2S$`´D“]G…|P&Åã|ÃŒ×ukü)ea\Šôå©îT§Oe¹ó^åì+sGÖî4¹ƒ#e;ŠÒñ‡¦Òç2B›£<×%÷ru¯B3XŸ/R¡SM¢4­RÛU¶„ŽEr)ðÀZêÔ|Ü óíX¸ÒgWFÊwíúf§m«[B #‘^|¢á-¤¡^ª|“Üùåã–) Ã/Woᵓ Kƒ˜Ï¯jèW±Ûˆ£ L=¤>#Ê´­Z}2ád°3ȯkÓu+MnÓ‚HÁà÷VSÙJRuÁ«šV©q¦N¯¹äVµi)«ÄæÁã])û9ìt(ðä–SíP˜Xä€+’Ë%@AjöK­R S×’ÆGš°9Ä ü]ðoÄ߈—Ÿ´Ýï…®µ&—KV‹œ}æ÷ö¬éVkÝ‘¾/-»öÔö?c<+âálï[ Ð1¯H ²¯Lƒ_1Àò$*à|ü^§áo‰XÞ¶tcQ_öâteY¯5éÔ$ñ_† Æomy W”º²±VÊyõ¯¦Æ×OPÕæþ)ð°‘ZòÍpz(Ãb5åó<«›÷´÷9Ï x’]:QÁýÑãŸzök{ˆnáÆw# ù­â‘‰Fí]†¼G6 ·¸;£>µXŒ5ýèœÙnlâýC¤ñO†Á®íæôå.eq†JúZÞhná ¬+€ñW…„¡¯,׿*hbZ÷Ñ™åœËÚÓ0|1âwµu·¸?!à^‘©évÚÕ¦HÈ5àO±K‡]¬µè>ñCÂâÊôü§¡5¥z÷àa–ã#%쪜†«¤\éw ²/Ê:V\S¼ ¡%Hî+èSLµÖ-ˆà’2 x¦«£]é²²:~ïÖªŽ!OÝg.;:2ç‡ÂzW†hž–]˜F´}CÉõ-:}6塘v5­K§è·QnùºWКƋkªÂUÔìkç_è÷N™t޹P§ºhVU7<ÌÇ/ ÎF|û<°—ö–¶”óŸÑ«ömx¯ÅÿÙÇöв~þ|ÿÉ«ö>íNoñ£^ wÃÊýÇQEäfQEQEüÌÿÁlWþ2#ÁMëáX‡å}w_Ó53ÿð[5ÿŒ€ð3zøaGå{s@bÿÁþ1÷Ç éâ‡?•µ~Í×â÷ü-¿âÂøí}…~G{}Iq 9ì±Ì þ5êå~ûæNÞ«U÷Úß3“§-NÏðz?óùCQEåaEP_†ÿð[øw|9ø_?÷5[õÿ¾ Cÿ²×îE~0ÿÁhü7®ø‹á7ÃÅÐtÛJh5É÷%´/3*µ³rB@È ™?àˆ3mø›ñ6ïéMÿ|Ü0ÿÙ«ú9¯ç_þÃá/xgâ÷åñ…¥ÛÜèQ’êÖXÝn“åV‘Tƒœ þŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ç 3Ký¨?à°º­¦·i¯á¿ Þ\Eqm­r×0Zø.bžÞW@¦]¦ÕrªªÛf(ÛCÇ"·¼!ÿäø‡ñgâ´?nÛøëQ³e6º˜®º\j¹"f‘"ýÀêÐÇïn^GË·ÿ†ýžõ…ŸõŠž&µû6±ñ"hn-ÑÆ4«`ÂØœôó™äc‚†3ôâÿൿ?±~x+áÄìŸÄúÓÞH òöúd$2‘éæÜD~ª+övb·‰-íÑbŠ% ˆ *ª¨À ¯Ä¿Ûcö ý±?kŸŠçÅsxƒÁšw†´EšÏC³7zŠJ–m+8–à í¾fÆ(6…\’÷'ìÇû+üð§À‡:o‰¾è7úüŒ··WzU¬×-w‘?½ýú캸À‚$7A!Œ¯ÿ‚C~Ïz‡ÂÏz‡ÅOZý›Xø‘47èãšU°alNzyÌòH1ÁCúTð‡ü“âÅŸŠÐ|lýº-ê¶‘[x¯Á2Y5ú¨Y¥Šêî+i-d`2ñ‘)uS÷]Aóà¾ý†oÙÏâïˆþ-|ñÏ„õ©¼S,òêVÚ‘»Ž+ß:V›÷°y,WbUÒpÃ$gk0>ñ öDýµÿk«½3Jý¬2GjðÙÃâoìý¤x£]ýþ[Ý¢|%€¥É*e³‰¥›t‘-Ë»;Ê&‘ÆæW8¯ž|IàOø+_ˆ4k­"ßÇ¿ ´fºFŒÝX® ·†&6–ÆEVô`¹Ašêÿàž±§Ž?dÝÆ÷_õ];Yñ?Œ/­ä{:iîìÖÈå7És æ4³J[å §q<ì?G(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®[Ç4ðçßë><ñ}ÚØèº¤··s7ðE –lìq…QËä×äì'ÿ ø·ûP~Óºç€|Woacá)´íFÿLµ† ·0ù7ù $Û˜V`Ç3r1Ò€?j(¢¾ý±¿nχ²U•ž‹5›ø§ÇZ ,4KgòÛËfØ&¸—kùQ– ÎìUÀfP¹ëæß‰–·? ücÆý&}*åc³ñ=´`’öÀí†õTu{rpÝÊqÀɯ†?áaÿÁ[|Mà™¾(ižðo‡­–»ƒÃóÇpuiaÁuCÊàK·øHŸèwZ¿ÂÍQ”µFÿSòŒf¼Ïâ_ÅO ü&Ó¥Ô¢—þ ”Ö_aÐ]­÷Š’z zR«‡ûH¬eû¦ÙÏøep5“wµ£RyQ´žŸZ(×oÝfXü¦ö¨úf_ø*si·aм+&¯igY†>¡±_W| ý¹¾üeœhZ´‘iZ‹ü¾D‡k{`“ëX~ý›>x[F‹LMY¼±¼œIJø“ö‡ý–o4/é5øEjð\E*É"ÆHèÙíŽÂ”ðÏâEás*M{6~ÂxÃâÕN¥eóÛ¸ÜHû uÍ|Sñ{ö°ðÂwksu Ö£À€6_?@kÐ|sñg_øuû,¶·âXš=SÈ(7ª¯ø×æßì›û>ÂûÔ§øÅãÅ{Ûk¦i#‰ÀüGðÕÓ®åî³,VYNÚÄú@ÿ‚§ÞÇ2ÛjÞ’8n=r[úðö‹øeñóH_ìKø^í×ç„0,§Ó5ãWÿ~j[hÍ£"©]½cô¯Ê߈©û#ümÒõ¯¼‘i:•Ò+Ǹ• Ϊ}ºf¢­ÌŽŒc ¿g#õÏö‹ñÕßÀ¯Í⸴ÖÔà„eœô"¼ÿö~øû§|vðêx‚Æßìr’I‹¡]¤ú“é_Dkpéÿ>Ý4Ь­}iÌ@5øùû'ë³|0øÙ®|4ºa‘ÂÓ‘ž?:ºU¹´g6;+P‹”7?r|-â“òÙ^8ÇbÇšùö‡ý³tŸ†9Ó¼i¢fãPš8rª[g˜Ê¹àŽ›³^¯®ê‰¢hWZÓ¸U¶Œ¾kòcàý­×ÆÿÚ¶ûVÕÜÍce0xÉ䀿)Ö¤“º[‹ö䩲?_ÚHM~úm ¨ Ÿ­|gñKöâð?ÂÍI¬tG‹UÔ"8ò•²Ùú^¿ûw|T? þÿcéOåÜß¡†"§¹þ•ð¯ì{û&ØxJÿ…›ñ"¨MzîãÌlõ'oÞϵªÚåfxŒº4Ÿµ{Ùáø*5¦£{Œ¼0ÚMœ„+O*²®=rZ¾âðÿ‹üñGŽŽúÞa’#`p{ôô¯ ñßìÕð¿Åž¹Ò’‘ÈÈDl¥ASëÀ¯€gëß³¯Ç럆:Ñ—D¸}‘£…óxÉ>´%ìÝËçŽ*Ÿ-µGé§ÅˆËð·Ã“ø 3ˆT¶Þ{}+Å<7ÿ#ðÏ‚ßV’Õ$ÔbgE·\–b„Æs]ßí·£}àö¡©Y|ñ´ F:t¯ËÏØྋ㋛¿xžsM6Õφ=¨ªý¥‰ÂCêéÎgéÂÏÛ¾óâ³ßØ^xB[W¶ˆ¸V†qV®kÀµÖ‘ñâMßÃÙ4Á§ÝÚ“ž$sޤúWؾðífŠËKK{‰ScI…ç?…~EþÐ>¶øûRÛøÂÙ~Ïi©É)Ýýk8ÉÑv;¥x¨9-ÏÖåU<|Ã>õå¿><ÁðFOMÚC’~§ð>õë––â÷C²Ö¬Ï™o,`îõ¯ÈÏÛ+^¿ñçÄ7áî‹9Ä2ÂΣ'† Ÿå]Ó”eçp¸j”«;Ÿ°?³ÇÆóñÏÁ©â†ÓΞ$ÇÈÀŒäg¹5é^$ð¼7Ñ5Ū„‘y8ï_8üÒäøwá?M³Pˆ±© t¯ªômjÛU·a¿¸®NPÕõ,]©.Ÿ¥ÞÍF‚Oå_?¼káOþÕZ†­â˸í-Æ i;ŸÖ¿H>x¨øÇáhÕå?¼û/Íõ*kðÛUðe×ÄÚ¶MÍd€È¥À$eK7S¬”•ÑžSRPR…MÒŸŠ_·¦‘á›ÖÀºñ¬G ð†p?ï“Z_¿n¿üIÕ"Ò5xSE¾$b7ù[?BM}¥ðÛöjø/§x^ÞÎ!$–XÇš[kqƒÕkò7þ û>è?5í;â‚a6 $Ã;@ÏlzÔRÄ4­#ª¶_NKš™û¯àïÇ}oR¾õp 7¨5Í|bøÿðóàÎ%÷‹5(`®R'`¿ ù‹à‰/µ…v:³¹’á-Ô¯=ö×Ã~$ø=ñöý£-Žâ”xZÝ”mf*¤Ã×=ªq~Ò ¿ù½œÏCñ·ü^y/ž_ ø!õ E?ë£ØëÕ³ðËöõðuhôZ&‡pį•bO±&¿Aô¯¿<¡C§6Ÿm)¹BOö¯ÇOø(¿‚~ø^ÂÛÄ? ®#¶Ö‘ùH› î⦕ylÎŒFYNOš+SöŸÁž3â†Xfóm¥©ÏjÇøÏûK|>ø;£½æ»¹*YafŸÃ5ò—쩯ê÷ÁÛ{»æ/46ÃõÈN9¯ÎOkÚw‰¿jË='âÕÙ]2®Ç$.7\Ž•­h&®Ž<¯/héMè}3â¿ø(õüú™:'€ä¸±?iXÜ©¹ Šõ„?¶€>&ßǤÞÉ—¨»!c†û_dx+áWÀ wÃ[øzÎÚêÔÆ3ÈúWÉßàžÔ¼ymãÿ‡w_Ù/¢RNH$ÿ ³Ã×kÝ‘ÑË)Ê.qÑŸzøOÅ ¢Aq&øÜe\ôÇjóŽÿ´Âï…:D‡Äš„w´•„°ÉÇldWœø·T—á¯ÃÉg½™´ûr7g©UÇô¯É/€Þ¹ý¯þ:]jþ7•åÑl.|°…ÈR¹×ý*ñù}èœù]yNMz¿ÿ‚ܦ¦æÛÁ¬ú^ì Â>Ý¹ëØ¯¯>þÓÞ ø¨‘¾“y¾ ¤a¸¦s_JEû0|‹ÃÿØè±<½€ü¤çëŠüXý¨~ Ý~Ê¿¬¼{ਗ਼×L•·ñ,Z”B)Ø ­Wø…§Cá›Íê2¨H?…|¯ð{ÇCž ÒüAi&e’ÙÎ{•¾Œ¾ñZ—†¯#™°áÇáT¨8Í4sÇÍFTªïcòïöxAí'mçmÅÇþÍ_³‹÷E~3~ÏŠOí1Ÿh¸ÿÙ«öe>è­3oÜ¿s/QÔQEy'Ø…Q@Q@~d~ÜðO ¿Ûâƒã{§…EÓ?³Œ ¦›Ó!óä›~ñqß¿Œ`ôÎkôÞ¿8¿m?ø(U‡ìuãMÁ×~—ÅM®éí~&MElÄaeh¶0K»îç9zP£~Ãÿ²ÿ±×€õÿÏâ¥ña×5!¨ ÖÈÙ± E³ašmßs9ÈëŒWÚõñìUû`Úþؾ ñ‹­|*þ úؘ^ð^™wD²ïÜ!‡oÞÆ0}s_hPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP^iñÂ/㟆^!ðݸ&î{V’×x\ÀD°àõ¼EäW¥Ñ[aëÊ•HÔŽé§÷R Qq{3…øcâäñçÃíÅÊA}JÒ9%ÇE˜ ³/ü@Ãð®ê¾yø%ÿLjüyð¶O–=S:…ŠôÇTz*zˆäÞ¹¯¡«§3¡uä¡ð½W£Õ~ Ë QÊšr߯ªÑ…Q\@Vf«­èÚ u­ßÛéð»mW¸•"RØÎrp:V~9ÿÁkáÝû5ø:îx¶Ýï« Óÿ²ÐëN‘ãø†éìt rÇR¹‰<ÇŠÚæ)P7F$ 3êk£¯æ+þ¥6ßÚoÅÐÂMÿ|êÿf¯éÖ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùã㯩xÃY±øá9Ú½n??Yº­–’$í%ÁýÚBI ר|DñÖ™ðçÂWÞ*ÔÔÍöpýeÍćlP ä–vÀà ž€×)ðwÀºŸ†4‹Ïø¹„þ.ñL¢÷T”Ë6#÷VÉ×À‡`#9ÁÆ+×ÀEQƒÅO¦‘]åßÒ;úÙw8ñû%óôÿƒþg¨húF› iVz&ÚØØDÃ}ÔŽ1µ@üiQEy2“nïs­+h‚Š(¤0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠðOÚkãχ¿fÏ‚þ"ø±¯í•ôØ|»Vl»ù¾[x|3òär¨º) ÏۯŚ÷í3ñÃÁÿ°Ã;ÇŠÞòhuÞAÏÙl!ÄÉ öʦ&ÚÜ4n¹É"¾[ýôáüû[øá‹DÓ´‹x/´û;tác‚-)&®1Éäõ5{ö+ñwí-ðƒþ¿ŒúÇìíâ/ˆ¾,ø©:jrkÂî;E{Àž4Š7…ÙUÝËœ ù`(+̾xçÅ:÷üÿDñ·<#uà_Y¼+6y*Í5·Ÿ µ²î‘Uyƒ”`0Fhú:ñ׌to‡ž ×¼{â)<­/ö:…ËwÚÆÒ¾3Ü…ÀÏüúÁ9< «þ׿µkO‹°}¾?\¥å¬o–…uK’~Én딃ª·”ÙÈçôÃþ â‹¿ ~Å:/åÍ«>Ÿ§îóÎ{ȼÑÿYà¿à¾´ðçìwa¯B˜›ÅZÆ¥+ÉòdJ3è·æM~¡×–|(ø'ð·àv•©h ´¼?c«ÞɨÝG’ÉæÝJªŒäÌîGÊŠ‚v'>§E~'ÿÁ]¾2'„µ/…_ źê6šÄšÖ«i€Z[GÛF«ß,^R¼™u~+ý¯î¾|ø{û.þ;¸Ö¾1Ýéðè6V^W™œlSìÒ^K!ýÔ¬|¶‘~bŠ3$Ä*áþGý«´};ö¥ÿ‚¬ø_ྫÔ4éºUä(î‚[Kh_U¾Mñ•e%d’6e!@5ÖüNø¦þõ¿‚­üÐxVwþØðÀ–V•£ýXÑüéX³‰bbñ—cµ¤%˜ ö0OÛÓú¬·Þ>½cÿotó·vq×ýܽªÛ¯§—ä~’~Ʊvû7é·¾9ñ­àñ_ÅŸïŸ[×&&VW¼É ·wù¶oåä8i[æl¨¿vU3R°ÖtÛMcKnl¯¡Žx%NVH¥PÈÃÙ”‚*õy [FvQHŠ( Š( Š( Š( Š( Š( Š( Š( Š( ÿÑýû=+•ñxñKhW?ð‡¥«j»O’/Eƒv7lg?utÆ\€=)J7*.Îçç´ZÈÐõ x‡AŽóWøk}u«¦Kµï®b˜F/‘Šb4Ž!åIÊÆB¢Œ—ÉûÛI¾:•Œ¢müÕ c™vH¹ìÊz\îŸàé¾-Õ¼omþÕÖRî%#$­ºí@=8Æ}p3Ðc¶U 0:W•”e ÁÔr¼›×}NÌf*5-e°ê(¬mc[Ó´ ­WWž;;(ÚYæ•Â$q¨Éf'€z­ÛS‰+›dWˆ7Ç/¤-©&­I¦¸^.›1‰“ûÊ€yÄØŸ¡½WBñ“â].ßZÐîRòÊí‘M܎Ѓ\¸lu×T¦»=jPœUÚ9/‰>4ŸÁZÝ鶇PÕu ˆll-·lÝܶÈÃ7ð¢ç{°„R@'Šó5ø§_Úk|JÔ.¼W¬ÊÆIâVKXYù1Û[¡TŽ4è¤‚ä ³1æ¯||Õ ð姃¼S~Þ]Ž•â g¸“´iqÖªçÑC̤žÕé%Ú¨r6°c¡¡¯Î¼EâšxJ50³Œ¹¥u­¯~þG¡ƒ¡5Îw>y×ìµÏ‚ÀxËÂwww¾´µ=&âwVصţHY£’0KݱÔ`y¯¬4ûë}FÖ û9¶÷¬‘ºòXdõàß¼A¥øsáÞ½¨j­û¶µ’^òK*”HÔwgfä×uðsHÕ<=ð³ÂZ´¾°Ò¬à˜¯J¥s߯{ÖgX¬^¥“e üú‡…5륳0]ÈóÍau(ýɆg,í Ž6rJ³)R+_L!Ê]?ŸP̰ËCgø>Äb0ò¦Ò—Qùµ”ÚÖ¥žµí˜4-Q@‚Š( Š( Š( Š( Š( fû§èkñcQ;jKÂzQÿ™¯Ú‰¿Õ“_‹÷ö¬ºi†ä£ÿ3^ÎSö½‹âë~í¾çì&Ÿk Ö S Êѯ_¥|—ûNønm3ᦷ<#t>K=+ê= ÄV7) ù ª€~+Ì¿i8’oƒþ VÁͳàút®i(6{±ÃÓ¯M8Ÿ?°G‡tâw‰Ž³l·!'èÃ=Ú¿x4ï„_¼IbÚuöjûþRO?­~ ÿÁ=­äOŠ>.ò—r¬üþm_·PÜËm0šÝöž¢ºaF3އ›ŠÆÊ…UØüŠý±¿eÍKö|ñ¿ÅoƒÐ›+(ßÌž(WŒdB}kêïÙCöÒ¾3ø6Ö+‡HõH£_9s†ÝŽr+îOišWÅ?ßxW[®ce]À@5ü𵦻û~Ò2i÷!£Ñ/ï6«t@„ÿõ«Rtß+= U(bé)Aê~èxóR»Óü «5£&&à} = ~!üTðwÇ_j¿´;fò[—.!FråþíAZf¡eãÿÇjâXoá c‘÷úõù  ë7ÿ²‡í-¯jÖÛ´mbð³;.T) gœúVµé©+£ƒ)­ì¤éÔ:ýSö„ý²ïßÏÿ„RFþ÷‘.qùW|Nø¡ûSüBðÍç…µ¯j]ÂÙZ:ô¯èÁ¿þxÏF‡S°Ôí[ze8à‘ÏjùÛö‹ý«þ|-³K­=íµ=A¹FªYEczX[C/lakÜàžöš3üÒÒÁTÄS嫿o|?ñÅ?ÙÞHüLí.¨Ñç9Îú×”þÀÿ´]‡Á»¹~üD˜éñÛ9HÞ^ôþuŠM>hy~&.<’?aüKá‰lå76‹˜ÏU¯È¿ø(ØüÎ_1qëõúÿ¨ügø]’ú¥Î¹oö]¤îÏlWàWí1âéi–øxZóK°¹RÏ;v‡ROoz#ˆrVfurÞJŽª?^c¿ùŸt½.ÿÃ>›E~wþÕÞÿ…GûDé>7Ómñ¯u»‡tS_¡ |4þðV¤©Ù, 3ù ñŸÛÉã‡É©¼Aî´•2«ÎP–þU(itg‚ÌÔ¯Nc?i¿.ƒðLê6rý«–…O|¯ýƒ<>›áçñ¦¤¤Ü]»Í×’q_Kñ‹\øé§øGá%±a5æÙ³ÏÉåíçñ¯Ü|9“áß‚4ý*8ø!$ rFjéVæ'1Ã{8~ëf~\ÿÁO¼Câ tí;ò®‰ ëòó¿†ÿjÍ3ÀVzG‡¼ ¨]éñ Ù$PÈTþ WØß¶×Ã+ÿx*=oJˆÜ\i„ÊT ž?­^ý†ÿkÜøR‡Þ;xt½GO-ÙpN;TW¥­Î¬»§MBZØùçöý±-É–çÀš”q‘Îa”JðX4¿ÚÇÿ´ÏêÞ¼³+ƒÅ~ Õ4¹×Ì ¨îTŠÚiUÏ2“žk±ÂþÊ4¿þÎ×…åÀy4ø3Éäas_|ðßÅOÚTñÜŠn4ø$dRyÄÌ+ç üV›àtÚ§ÁàÍÔB Ÿ_–¿ebO„öþø^5 øós¨Í4Ù=q#nμå.WcêªÒzw[ž¤dh«ÀQŒzV–›©O¦\,Ñ;Ö׈¼;.›3\F7DÜjå;g×µzÊJq±ðóS£RÒZžã-j WáζêG˜,åÈü+òsö4Ðlõ/ˆÚãÆ\­ÌÇþûWß>5½¸±ð~¬!|,–Òd~ø³û4~аü.ý¢õõ¥1éÒÎÊÎ~è%Û5çÖ¦©»ŸW®±TÚ‘ûŸ4R[Ìк•pxü+ó÷öÿÖqðúÎÎí÷4ã=Ë_ ú·Äß…:掾"µ× XÕ7Ïã_ŠŸµ‡Äx¾=xâÓá×%û\6ò©wAÇ\å[ó©Äóh`çB¯3Øý,ý”tª~ÏÏüb ƒÿjüÙøa¦ZÚÖñæˆoFòg¯ÖÚxøwð xvqò‹C»×!M~dü5Ó×Tý®oM¯V*Üwž°§xËS¾Ja'OsõëAÕfÓZ„â0E~xÁT5xµo…ºsÂøa1ÀE~€$·Û£i½~[ÿÁJätð ’d‘çÀVøŠIÇ™nS‹p¨©ÌúÃöU†m;à}®¯uÌ0ÂŒOÑI¯‰¾7þÖ?üYã§økð‚9`¹gŸä6HÎpGjûÿö]…|Aû.ËaoóKö#€:çcWåWÀ¿xoá‡í©éþ8Ŭ’Ý6É&ãqõš«öYèâ0 •X+žÑá_ÙãöÆø”±Ÿ|H¼²Ž^±¼‘Œg·1×…~Ù?²‰¾ øßÅž*×›[º2YÙI8\ÿû(~-|;²²]Tk0¤J7§=«ò'öýý£Ûâµ¥¿„ü5_é‘ÈU§O»œj+ÐQW‰¦Y˜Jo’jÇé·ìqáÓuðBÎîÔgtI•õæ¼GöœýЬþ1Ïý¹£Ê4ÍN pë€Ùüsë_N~ÃÌdø ¬ «m´{6ÓÖ¾ñ§í ñá?í<=ãÉÞNùW`B€XûžÂŠ“÷dN7/p“©KsæïþÚ_\Eà­vþòÖ€°áè•ô?Áø(Åoë¾øß¥N¦VÓÜ«/Ôç_£šOŇÎôZ¥¬‘ÈŽ@=«òƒöðñ?Ãÿj^ð0ŽëX‘ó¾ävÖª­¥x“—æS“p¨ÓïÚ¶[|¸ño†Ê´w0«ü§ûã'ù×à×ÀˆŸ¾]jqü<ðõÍò™Éf¾lJþ>ü5Ô5ïÙ~/ jæ\Kb09 #ãõ¯Ë?ÙëÆgömøŸ«ø'â$i­ÝÙhå•26ð½N}+R¾“;1TSpF½‡íƒûb[Æ"oêLO ˜¤íøWŒ||ñ_ímû@hQhšÏ€u.[a‚NœûWí5—Åo†×vÑê6Ú“FFìì^xçÄ?ø(Ão‡:·‡¬m_¼”í mµpsŽêjëÒQÖ,ÁÏ÷n6%ý“|ã/ü,´²ñu„–s¤j»%X`ƾ€ñ Ä–úEÓÄpÛM{G€%ð]‡‡¿foKðæ¯©^¬Ž¥¨£<–v Œ^kP©”0UðÙHÜ&øåûW|ýœÅ”?|S¨j,‹oa µÕôŠí·Ìû ý¡|ûNx3ÁŸþ.xKãåÞc iº¶µe£ÿÂ9£ÍQÚA%Äv¿h–&•” ½²ØääÒÿÁ:>5|Iøñû3ÚüDø¯«nkÒjwöíp¶ð[ ¡GmiÀ=vä÷5îÿµWüšÿÆû¼Aÿ¦éëãŸø$/ü™¾Ÿÿa­SÿCZã6>Íø#ûG|%ý¡mõÙ~ê²^O᛿±jV·6ÓYÝZ̓$ª8«(8ÆåaÕHç_ŒŸµ«þÄÿ¶'‚ÿko £YxâMÇö' Œ~àO'?ieodxÇ&H$$þñ³û2Ž’¢ÉG© ƒÐƒ@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(­õí¦›eq¨ßÊ ¶µå–Fû©ÌÇØšþ_ÿlïÛOá¯íAûJx ׳ÿÂŒð†¯hoeHýd·~HfÁc‰vùw°]ϰQTP‘­èÞ²ñ‘4Ø—qÝÛʪc쯑)ªì àÚ¿•ÏŒŸµWÃm_þ O¦þÑþº’óÁzF­£o»X]kKH"·º•"p¯ÐI° 8¯ëŠùöªømímû"øƒÃîÒíüU¦YêÚäÒá¢x¯­‡ï” ¡2Àm’8¯ÌŸØSöäøwû,|+¹ýœ?iû]OÀú÷ƒï®ÚØO§ÜKæCu#NcdZD‘ewÁ*•”†ë_¾•Ÿ¨iV¬¨º¥”‚#¹ѬO¨Ü ~øóöÍñçí­ûN|1ø_û=ǯh_t­zÒMSPµ3ÚÏ}Ȧw¸0Ý@‡£·Ì[s€vªþäüLø—࿃ÞÕ¾$üCÔWIðö‰ÉurÈòl:Ä€$a™™äuU’+¶†-£ÛF±F½P>€T´ü¸~Á?|âø(žµñcâ=ÑÓn¨º*º<¸Ôµ{¨ÒÖØ” ·÷.ñ«—8ó_¦?ðXM Á÷?³‹5‹åÓ¼IáírÍô'¼Ùî'ÊÏ2ƒ·÷!§$àfç8õzŠiÛTäoüö½´ø§à›…>+¹Æ½¦îKläî´…!ZHóÇß@~UõʼÏâ·ÃÿøX>ûŒÿ`×4ÙV÷J½5µì<ÆÙþë}×䙓áGÄñÃ?jÔ û»¦JÖZ­‘á­ï!áÆ9ùï!çƒ× ×­^ÞŸÖ£¾Òõé/û{¯û£Ž‡îåìžÝ?Ëåùz›EWvQ@Q@Q@Q@Q@Q@Q@Q@Q@ÿÒýü¢Š(Å!éA u¯!ñ§µK]b?øOWñ$±}¡Öwhíl ' -ê±ùŽB œƒÈ‘•j±§)NNÈõÍÙÈáß!‡XøƒáOêd¾›wš£Á“²â{Cp¬«Ñ‘ Æ@§è§µr¨þÐþݪßO¡x’$æ[kk>m£’!™çY½7"îîEK­ù¿<3 üDøs1²ñ‡îdx¡¼ÌC~Ó ÍÒ€Jî€8eWå+g”ó,-jYe[ÔKѧó=:x'NJS·/sÖ¤Š7 íNxö¯"ø{åxcã»á %‚iÚ­‚êí'lw+7‘+Ñ|ÐP0 Vln$œø½ãws ‡ºœºÕ‰Xî+‹6µGt¿¿3µ”‚Ìûdb—àÕÆã¿ZüC’ñ®· 70,lZÓcÈBXÆẎïSÐ_‘xs€Åáóy}b\²iÝ7«ù~'©ˆ¦ý„ݯ§ôÏ¥üO὞¾ðïˆ-RóOÔ"h¦ŠNŒ®0y‚:‚9‘Í|Ià]'ã–›¨ØøÅŽ—¤j—útúݳ½Ä1Ú\Ñ ‚ãhæ\ƒÆq_|1YŽ+åÿ€ÒCÂzî¸9MKĺì¨{ûdФ{|µú‰øY<¹Ö„"Ü5Õ'£Óó<üª·,d½ñÏ„~!X­ŸÄ‰Zô:Äþ¹‚ú=6ÖØA§$ÊŸif ^Idî(XüŒP2M}÷k470%Å»ŽE ¬:kÍ|A¥ZêúmÅÔBX¦Ñ”ô*ˆØŠóßÙÃÄMo¢ßü(Õîž}_ÀÒ 0Ó6d¸°#6“ûå>CþÒœõøñ­ ˜I¤šÕ%¦NœÖ>Ò”f¾Ïêz/Æ=WñÃÍsLÐ%ÕM¸šÒ78Y'·a4hÇ‘†e Ó½r~ø‹áŸéIy§Ý$W)ò]ZÈê'¶™xx¤NªÊxçëÜW¦øçÅšw‚<+«x«Vm–š]³Îç'`ÈP:’Çäâ¾qðÿÂ/ x—Ášn­ñÃÖwºýÄfòâGˆ Vk†iš2àn—*A8ã¥O‹Ôp®ÕmNí-žŸŽWw¥µË^.Öl¾!xÓAøwá™~×6™¨ÚjÚœñÐÚÁa*ΈÒ”K,ŠŠ©×næþ}+Æž+ñ-ÿŠ-¾xt´Ô¦·ûeõóƳ-…£ªBM3"åY˜¡[ös‡F¶ðžµ§hÖPØG¦ëš©ŽTeÃ2gh!FOlVcjé¡~Кö•xDRøF°šÉÏ`±yÒtSê‚Ul{ç¥}&Y YJ«Q‹šÒO£Ô+ûõ&¾†Ïð}´ÉÞòÇÆ~#±Ã›©5)eýíŸ6»ÙŽ•Ø|5ñλ¨jºŸ ÿ„cÅ:ËkPÄEŠ¿M W;ñÃözðŸÄ»9´ïX¬ÒV9qŒ÷ÎE}uáŸÅ{·¹;dQ€OzÜÖt[}^جŸë\I¸JÒ=Êô!ˆŠ«Kt~ jðO‰Z]Óÿ ãÒìÙ‰ò’Dû©¯¡¾ ÿÁ:4/íX5ÿŠzŠø†ê"Áòà8¯¼õ-6}*s Àž§½G§êw:dë$ ÆyÕ:*K™u,Ö¬*rÔØìOà Ãz)ð¸¹Cwd“©ëXR©Êùfvâðœÿ¾¢ÏÅ$ÿ‚{|K‡R6þñ«é:k7  §¶M}•ðþ ÿàï_øÔ¦¹ªî³HC¦;Šú)–Ki duõ¯Cð·Š •³½löÕb0éûÈœpäý•c3ľ ŽÊl#Ô.ß,¯€>4~Ç~ ø›;êúe¼vZ±9•ƒzõ¯ÖH®¡ÚpÈâ¼—Å’ÕÚêÉr„äÚŠWÃ!ã°r§ûÚ[ŽZüóâ¥æ ,5Ÿ»èùÇ’eLmôÆÜþµúcðöDøaðgLY4í>)õ ß9bqÉÎk  Êù0í^áo4n,îŽåìOj+Ðëp9³Ÿ¹TÏñ7†%³´Z/îÁÎy7Š4hš%À¸Fžƒp#ú××°^ÀAãŠòøfK)ZêØnŒõS¡U?u˜æ9'ï)š_³Ïì%oðßã-÷ÄKRKˆ&!¡‡*@mÙã•û5…µÝŠÛº‚»@€â¾vGt`Éò²r½zw…üR¬«ez~cÆI¥Zƒ‹¼MrüÑT~ʪ8ïø=vMiwk.w8 ׿×ůØCþ1Õ¥ñ¦]þC’èBœþ9¯Ù™­¡¼ƒË™[¥xïˆü96pÓ@¹‰²xíE:ŠZ2qx9Ðn¥-ÇßÿÁ>>,j׫§ø«ÇOu¦çQ–2ôáE~“|ý~ü!ÓÖk->)õ&Sºr2Ä‘×9®±^E‘¶²ž zg…¼TÙ[+ÓžÀÓ©†qÖåù¢éÕ>vý¡¾ ê|!yáKYþÌ·²+ðÏÖ¼ö]ø ¨þÏžþƹÔ>Ù+K,€‚7±nÀz×éEîm©Û˜äƒÐ׉ëÚÆ“pß.èFô¢Œ”™ÏŽÃN”}Í™Ýx_¶Õ û AqÇ5Êø«ÃMg×Q ÛKc¦+2I"m®9R=kòÛö‘ø™ûXGãIü3àˆ&ŸL¼0è§ ¸c?xzÕUN›÷E…œqöu7]OøµðçÃÿÿj·Ñ£-´åÉNF ýÚø_¨ÅáíÃÃÒ¨`†5] WçŸì©û-xÃÁv¿ð±>#IöÍjð Ä‚Ï'©>•÷;8ÛNTÕú†#:5VÈúFâÚÛP€Å&Hë^5â \éÓ4)hox[ÄÅH³¼lú5zl±C{V‘…rÆnœ¬z’…<\9–çÆ¿Ž<«ñôy>½+ñ÷ö{øSቿµ?Ä6©;yÒífêæÇB+ö÷ã„.×ÂÚ£iÑ™L8T^¤‘_–ß²o¼aáÿ‰º­î½¥Éi Í!Wa‚Í]u"ª=(Ë >§Osÿìñî³{åxwÆRXèòœ›TtÆßNA?­}oðƒöð'ÂKÔRÙ.µ¥Ë4íËùý{W»éúÖ™0šÙŽÑÔ{W²èZí¾«áûƒXÕ¦á¬OO †&Ÿ$·<P²²\i¬¾[IF§Ó#òwÂÙ‡TðoÆÙ>(^êÞdí"W€ L÷¯Ñ¯øb;ØšâØms^A4RÁ1†`U…tEÆq¹äÕ§S &ÖÇ­kÞƒRµK«07¨ÎGzüõý©¿fíS㾇k}ö)a·$ÓÜû³Âþ%ò²»<=1]ˆ¼9o©Ào-ׯF+·i®œkGÛRÝ+|ðEïÁÏÃá{›Ÿ¶*(W9JòïÚöðÇÙO‰¼5³KÖúù«…;½y'Ö¾ž¸Škg0H6•=ûÕý'U¹Ó.‘1ÚO"µ©†RWG&2©JMU{Ÿ–:gü‡â†î#ƒÄÞ-}CJÏ1³¦6ç§ ?{Oˆ?c/Üx ¼'áØÎîBI”wb1œ’kõ*ÎòÇÄ6>S€Iƒ^]â/M¥JÒ®L]ˆíXaå¯,ŽÌ|dŸµ§±âŸ²ÃÍ[েå𮽩Ñ+e[ €p8Ö»?ÚöVðÇýÆ­eÔ ‘øÃ/§9­Y0AÇqõ¯Jð¯ŠŠí³¾>€1§‰Áý¨•–æÍË–©øýªÿÁ2>-é³?ü"Þ6–ÞÀt‡|`ôû¤ô¯KøIûxcÀº²kÞ2TÕuhHýãÇ#œñŠý…B’Ç•åX:ó¯x\87–‹‚9 w5 $õ:³ZRäö”QËxS]:E¦¹"ÑÀ;Ð ò¯Ÿ²OÃ_– }qez¡˸QÈ'¾sŠêåIü¹ ž•ØxgÄrióyÑùWN&Š’¼O?/Ì¥Z«?%5ø&?Æ+9$å‚Ç´"Hñ·Óî“һ߄߰w…< ª¦¹ã º¦¥ ds¸äwãû%ÐÝÆ%ƒ+ à&ZMt’Ax–Â! ѾsœçŠÙý„ÿcKö9ðŸŠ<5©x¢/7ˆoa»Y"´kQŠ//i $›³×9÷m|WûþÙZWíáÿëúW…æðÂønê VŽk¥º2™Ñœ0+{q·æ¾Ô Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( IJŠÀŒ‚PE|ÿû=;èš?ˆ~\±ó¼ªÜZBåÁûE«Ÿª9Ùkè:ùçPÿŠ7öŒÓoÇÉcãý.K9= þ˜|ØÙ©™@ïŽ+ÕÀ{ôªÐî¹—¬uÿÒyŽLG»(OÎßüCQEåaU®ïlì!7ÓÇo8/#\ŸsVkó þ ï™ûêoÿ<µ-¿9­~‘Ùø“úбÓõK[«’ E/ÕuMJw?yš)ÍŠó×mAú“ë_NþÙßòiÿìVÕ¿ô™ëÿà–zįì?àbei4é5kiBÿ JæEv;Oãš÷Û;þM/âÿýŠÚ·þ“=|ëÿœÿ“'ðŸýjßú[-~‘WæïüsþLŸÂõý«élµúE@ÎçügþKŸÂúð—ÿK¿¢:þs?à³Ú®ŸÇ…P<éæÙio<ÈÝoyò³ \þ‹â–)âIàu’92²U”Œ‚à‚:’Šù‡ÆÿµÿÀtOÙóťͿŒüBöqÚ@¶’¼,÷ïåÀ Àl›Ž¼w¯§¨ùÜÿ‚ÌÿÉsøAÿ^ÿéb×ôG_Îgü{UÓâøñðª<Û--癣ï>VaÔ±±ëƒ_Ñ|RÅtýºgÇíKYy-ÔWòìÎH/i>~‹o^¯ô_3ùÜýªÿå/Ÿ ?ëÿÂßú8WôG_ÎGí]­iQÿÁ^>Ì÷Q„±Õ<) í¸b)T`®„áÔó؃_ѽyGY࿵WüšÿÆû¼Aÿ¦éëãŸø$/ü™¾Ÿÿa­SÿCZûöªÿ“_øÁÿbwˆ?ôÝ=|sÿ…ÿ“7Óÿì5ªèk@—ÿ4ðe—Œÿbïˆå3>‡®«lØÉI-n#,šEú5{_ì‹âËß~Ëÿ |Q©Iç^^xwNÉÝ冉Øû³!'Ü×ÿÖ Ðÿc/Š÷— ¨²éØéºîhàQÏrÒ=ëWöÒåÒ?cß„¶“¬ú­ÀÈÇËr Êãõ•Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|Ûñ6ÒëáoŒ!øá¡DϦN±ÙøšÚ1“%¨;b½Ud·' ܧM}%P]ZÛ_ZÍeyÏop‘¸ ®Ž0ÊÀðAWfìgv¯£]×Uú®ÎÌÆ½.xÙhúz‰iwki õ”«=½Ê,‘È„2º8ʲ‘ÁƒV+æß†—W? üe7ÁnV}&èIyá‹™ %íÝ5“1êð•êJsǾ’£„ö3²w‹Õ>ëúÑöwAB¯3üMÓô¶?Ù¡4¹¥ÚO–·Ò$«&ÞÛŒIl{f¹CÆ¿ïu×ð·†´ïêóÚ›¤¾º¾7VþP)Þâˆ4ŒŒAÚÅ1¹I8â²ü®ßþÏÚLJ¾!¸Ô4}z宇ˆ–=’-ä»C­ð*7'u@Fè¿ðû<k%Ž©ËRKáêýzzõ\< F·k­¡âŒþ=…ìû6ŽBö˘qøYÿü 'ˆá´ÔôiΛ®é2}§N½ ¸a¸;£|²!ÈaÔp*·Âɤñ‰¼qñ ›e®jB=ù!¬´ø–t= ¼¾iàõï^Ï0ŽD)66‘“ž˜¯‘âÌÒt³ÊøŒ3³Réä’.Œ¹-~ÖkäxD´ž•càv?ĺŒq³×®?i-kM×m÷ZxBn#…ÊùÚôG*eŒRð˸ € gèÉÁkYÎ0¿^Õóoˆ~ ø“C×gñ—ÂXxV»%®ídO7N½lçtЬ®NrèsÌ,~0ý¦lñeqàM#P¸éö›}LÃnÚÊž`õÇ>•ôÜIŽ©›â>´ªÆÚZ-¥Ë÷Úþ¨Ùᢿ…k[_Â=ZËŸ|{á=BU¶”ëÖþa4rD°Ü‘Ð|’E–ô¹®'Ä÷¶ß´gŒ­-ü=$–¾ðŒÒH5»CåÝ\_•(#²—GÉ•¹Y•²®>øÛâv¿m¯üfÔìáµ¶ŠH—LÑÖXÑÒR¥£šâCæ:6>t) cçêmFÓt .ÛGÒmÒÒÎÎ11F»Qz(‚¾“1ñTòÊx óµ›Z«.ÝÌ«Q¤ªûTýç÷47…>)èžÔ>#]ͦêÓM òìm¢»Xâ·’SûüÉ(|¾æ¾®ð'Ãÿü;ЗAðìN±ïye–Y ³Ï4‡/,²6YÝRkÆc¸[¿ÞÓÐn1Zj·®1<± Oýþ8úWÓÊ0+ô¿ ðÑYr¯(¥)7ªVÐó3ºÒçŒ:XQÐRÑE~ŠxAEPEPEPEPEPS}Ã_Žº8ðØNê,ÿÌ×ìLÿêÍ~1 ·°ý¬®®ÇXõW#ó5ìå?kÐøÎ,vöoÌý˜ZÝTŒ©På^eâ u½²^:*ö‘ã8e‘-îF7Ïn•èb¸‹‚¼õ)AÜ÷oKK•n|ÙÒÚÎ2’!þUëÞñT7ˆ–×9¬oøXóyf:u¼ád–Þ@ÉòȆ»ÔhÜð#:¸ZžGÑ÷¶vú„9`¼GÄ>—Hž0Zk¸ð¿‰Òå­Óã¥v:ŽŸ¥lѸ#Šä§QÓ•™ìW¥ U>xn|ç ÃÁ ’VNÕë>ñP¸Îõ¿x8ɯ=ÖôK&fܹBx5Y"l8ï]ó„jGCÀ§^xiŸ@kZ-®¯lw_)¯Õ4ÉôÉÚ+… Œ×¤x_ÅK:-ÑËŽ3]F³¢ÛêöÇ nǸ¡7MÚ[Ö"„1Tý¥=Ï ÓuôÙı’5íú&·o«[ÝÜW‰jZtúuÃA2œv=©šv¥q¦N'…ˆPy½Jjjèòp¸ÉП,ÏQñO…’íMå Äƒ’y##Å#Fनz×½hšÝ¾«l¬=Esþ)ðÂ]Fnì×N;ÖTª¸û²=,n5cí(™~ñS.Û;Ãì ¯OeŽæ,pȾhe’)H9VJôŸ ø«fÛ;Æã 4V þ(‘–æ6ýÍbŸŠ|.֌׶ƒ õÀƒ°†_•Å}-"CwÖù‘Åx÷‰|3-„sn»‘¿Jt1÷dNg–rþòžÆ‡…¼PБgvxè ¯Pd‚ò\:ù nFÊžEzG…|PcÛkvÙ^ƒ4WÃ5ïD¬·3M{:¥øb[7–˘Ïa\@%:ü®+éi#‚î"­‡Fä¾$𤶮×vƒ1÷Q­}–VãûÊFŸ…|TNÛ;æç &½kxoa*à20¯›øÎô8 ñê+Ö|)âXå‰l®›÷‹ÐúÒ­KíDÛ-Çó/eT墾‡¾°¶ÕmŠ0#ƒ^#®h“é7HÌyàýkJ•EË#›1ÀN”½¥=CÐu«]bØÚ]Œc®Cľ{ Ͳîˆó\”ZʲÄÅJôæ½{C×-u›aiwƒ!¨”%MÝliB¼1 ’{ž4™Èpv°é^•áo²exÞÀšÈñ7†äÓå7VêZ#é\b—S´Žõ´£±ÓsŠ.®­úJË7p•8eoƼľk^îÎ% zí5¥á_+gzùì ¯O’(o ÚØda\1æ¤õ>†Q†.[Ÿ4ƒƒÇåWl/®4éÖX9溯øeì\Ý[)1ç ®#žýÿJô`ÕU¡òÕá:3±ï>ñ¾­V8EexŸÃ1ÞF×Vë‰{×”Y_ϧL·žäW·è:ôµ¸V#~9® ”¥N\Ý¡Âbቇ²©¹àòÃ$yr©WCÁ¯@ðljÞ&[+Ó•ìMt>&ðÂ^ÄÓÚ®$×K ÖÒ”t8®žeUhy•!S Rý]ñ‡mõH~Ûi÷±ž+É. –ÝÙ&R¥N+½ð·Š|–W§ ð+¡ñ‡aÕ ûUŸßÆxïQNn›³:1xX×¶¥¹åúF¯q¥Ì$„œdd{W³Ù]Øx†Ä‰pKA¯ ž'†f‰×k/V†•ªÜi— $MòŽ£µiZŠ—½—ŽöO’®ÌÕñ‡.4¹šH”´MÓÚ¹C”ùÎUÖ½öÂþÇÄ{N ȯ6ñ'†æÓæk˜†èÉüª(WiòÈ×qýå-S6|-⬳¼nB}«ÓÁIWp;”×Ì ²¾AÁÅzW…¼Såí´½löZœNíDìË34¿uTµâ ‰]Ù¯ÍÔ^Zñ´m±Á\u¯¦I£Ĭ+Î|Uáu‘ZîÌa½*pø›{²g•_÷”ÎwÃ~%—O”A;f#Ç5ìPÍì"D!•…|Ù"4\8!éô®×¾#{VÞé¿vÜj¬Eõ‰Ï–fN/ÙÕØ×ñO…ykÛ!ÏRx·ˆÌè×L2Œªs_YÇ$Wpî\20¯ø£áp4K»ËA”’Ò–³æQeçjpu)ŸŸß³ÙøÑ†êÓÏüÚ¿]¥~Aþ̪GÆÔà¬óŒßUúøŸv´Í—¾Œ¸:W ýGQEäŸ`QEQEüåÿÁo×þ.7ÂöõÒoÇå:Wôi_Îüâ¾øXÞºf¢?)¢ aÿ‚·ü[ÿŠKéªiÇó‚ZýͯÂÏø!ëÅñY}50þpÍ_ºtQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW‚þÑv7Qø /é‘™5_ZëPÕ’Ùñ:“ýÓ 9#¾+ÞªŽ©§ZkmÞ‘~že­ô2A*âŽU*Ãñ×^ìkB¯g÷®«æŒ«Óçƒqú}õ®©am©Ø¸–Úî$š'$PÊjÝx?ìé¨ÝŸ‡Ÿðˆê¯¿RðeåΉpOqhø„èa)ƒßµ{Å,vØÖ.Ïï]Í œðRîù­ÿh‡ÍýмLÿóËQÒ[ó»EþµúS_¿ðUKInÿb?ˆPÈñÜé ŒŸùÛ¯Aõ®SSðçþ 77—ûkøU?ç­†¬¿•œý+úدä—þ a¦jö¶×.'²ž8 ]ZF‰‚ é·$d‘ŽH¯ëj€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ †âÞ »ym.£Ya™YeYX`‚;‚855øçÿåÔngÏŒßaϹŠçGԤ״ñwc2F·=Ì?g”('“.~鯽?lïù4¿‹ÿö+jßúLõãßµ×ì•ã‹^6ð/Ço€Ú½‡…¾+xé<«Ëó*ZÞXe‹[Ü#‘Ș´†I$Fáex—ÂOÄ?jøƒc柯ؽž§kÒ¬R$ñìš4•<©vœllsÁ ƒ?à“Ÿòdþÿ¯í[ÿKe¯µ>/ü1‹âÿ®ü 7‰µÏ¥Ü‘HuÞ‹ E<§µ')&ñ‡NW"¾sÓÿà_²“j¶Z_‚îìíÐ’±Ãâ n4œœ*ßɯµ¢!‰!Œa9àp9<ÐäÞ¹ÿsý¼O©Í­x“Ç~<Õµ ƒ™nnõKær?½$š{1üM})ðö"ð§ìëâKsÂ_|u¬XéðIo«ë1Üid]€›H­¡\ å0p§œWÚ”PàE‡…®ÿmoø)Øø±àX$‡Ÿ nôõ¸ÕÞ6XgºÑÛÍH¢$ Ï%ÖvŒÿªS!ટڟ‹ÿ bø¿àk¿MâmsÂ)w$RC÷¢ÃQO)ÃíIÊI…|aÆÓ•È®ûMÒ4­)¡Ò, ±Žâi.$X#X•晋É#v%™,NIÍhPä¾¹ÿsý¼O©Í­x“Ç~<Õµ ƒ™nnõKær?½$š{1üM})ðö"ð§ìëâKsÂ_|u¬XéðIo«ë1Üid]€›H­¡\ å0p§œWÚ”PàGí§ákÏÛ‹öåð‡Ào†ðIw¥ü9ƒÊñ>¥å·Ùì|ùÄ·JÍ€ ,hˆƒ?<¬P`)aûïYöšF•aw{¨XÙAmu©:Éu,qªI;¢Õ¥`r¨¡AbHPà Р1øÍðŸÃþø‹á7ŒŒË¤x’Ûìó=»š2¬²G$eƒ.øäUuÊ‘‘È#ŠüÇпàž?gÍ{EѼ%ø…ñc}¬ÉÑhðGÌ÷ñÛÃQ«F?Õ4›ØIƒZýfñ_‰ôøsPñN¿7Ùì4Øši[¾EQݘáTw$ òƒ>Ö.dÔ>-øÞ‰|X’çìróoj¹èq‡“¦Xò2 z¹}8Â/Q]GeÞ]>Kwò]NLDœš¥ÞþKþËþê^ ð–àO iÞÐ"ò¬tØ„iŸ¼Ç«;ììK1îI¯3øïð2×ãLJ¬|9yã_ø&+ƒpgð¾¤4ÙçÊò¦s¡ãç;p>`kܨ¯6¥INNrwléŒRV[‘—¿ðF?ÙŸR½—RÔ|eã{«»‡2I4ºŽžò;“’ÌͧI=ÉÍ}Ÿû>þÊÚGìñ}y£|Cñ§‹­ï-–Õl¼I«­ý²«)A¹çåȯ©(¨(ð_Ú«þMãý‰Þ ÿÓtõñÏüþLßOÿ°Ö©ÿ¡­}ãØköcñö»¬x“ž¼¾¾×¦šâôÿnëE,—™?sâDªÄŸ‘P(Ž+Ù>üøsðÁkà„º0Ñ48¦–á-~Ñ=Àó¦åÉ’á哿 w vù½ÿBñF§ñ"_†ÿ±g€œI≚µ½ÕÞ>am¦Û9 $ª9Ød )=–Ýý«õ[ÃÓ|#á­'š2yZ~‹i•ºvhÖ4‚¨¯‡?fÏÙ;Çžøãã¯Úsö‰Õ4ß|Bñ,†ßKM1æšËIÓ6åB×BÁö…‹!8E'q2½}ÿ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@gñ[áøø…áƒee?Ø5½6U½Ò¯G m{1¶yù[î¸çå=27áGÄñÃ&çPƒìö—+Yj¶G†·¼‡‡þã}ä<äg ×§WÍ¿m.¾xÂŽLúdËŸ‰­£™-ÄWŠ£¬–äỔ〠¯_ý½?ªË}ãëÖ?ö÷O;wguìåíVÝ};ü¿#é**½¥Ý­ý¤7ö2¬ö÷(²E"Êèã*ÊGr X¯%«hÎÀ¢Š)QEQEQEQEQEQEQEÿÔýü¢Š(¦°ÈâHzPΟëÃGÃ_-‡î|9{å_¨êtëí°Ì~‘¿•)ÿr»][CÒõûl5kxîíæS¤ŠO2ž=Åv~'ÐlDÀŸ˜‰"ÚrrrNA5øo‹\?RS¥Ã­tŽÞßä{Ùv&ôüãùoü)oø6饸Eâ©ô;$ÿeÜÇöí=I礌$‰sÔ#èX¸øñ³ÅÊ,KzÚÉVÒ"=”Çñ¯¦+û/…0~Ã.£O¯*üu<<Ú§5yyhQE} æ…Q@Q@Q@Q@Q@M÷õ¿ç†KŸÚªê×q:£ÿ3_´ýÒ}~:éÃ`:žú³ÿ6¯_+•œŸ‘ñ\aû$»Ÿz\Ç5´«Œ8ð⺿ø¢}>QʺŸøh]Â/m—üW“K;$ƒk÷ö«ƒEcÓ©†šhú6ÖîÓRƒ|LHäWx¯ÂÅw^د=H®?G×®´‰C!&>⽟JÖlõ‹pÊFHää”%Iû§· E,Tye£<4¶ÒnŒí•z׬ø[Å p‰kvØ|qYÞ(ð¶Ý×Ö‹Ïp+Í£i “xÊȆº½Ú‘ÛSÈç©„©ä}©i¶ú¹Ž@áÁ¯ÖtK*à†_Ýö>ÕèñR](´º8aÆI®ÃRÓmõ8 rr:×$&éËSØÄІ&Ÿ==Ï’gŠE’ßå>µêÞñBÌ‚Îñ°ËÀ>µÀkzLúMËFW÷g¡¬twÖHÏ#šì©Q\ð0õ熞§¿ë5¶¯nrüpkÃõ-6ãM¸0N¸¡õ¯Ið¿Šu·G 8É®£ZÑ­µ{bÀà×&é»3Ý­††&Òž§jSés‰ 8äW·èzÕ¾«l °ßŽEx–§¦Üi³¼S/ði4ÝB}.u ÛßÕV’šº<œ2T%É#Ñx¾ñQȳ½8ì ®þþÆ×Tµ1ÈJùÔ‘"kÕ<'âe‘Óa‡Lш¡ozÙn`¤½•S‡Öô´{‚œ´dð¥dÛÜIm(š#µ”×ÐÚŽŸm©Ûùr}=«Ä5ÍãI¸bT˜ÉëW‡¯Î¹fsf9{¦ùéž‹¡k¶ÚÍ·ØîÈÞF9ï\w‰ü7%ƒ››UÝv«’¶ž[I–hN¯\ÐõË]j×ì—xó1ŽiN.œ®¶*hâ!É=ÏQ´åN{פø[Å;qgxØÇBk'ľ–ÂSsl7FÜœv®(¸[5¨àŒêaêKɰí`Wø›ÃXÈ×V£r£Ò´ü+â¤Y]·5éÒÅä;[ ¬+JTÝ¤š§Š§u¹ó@ùI\sÜUÛéôù„ð7NÕÕx£Ã2ØÊnm—1ž¸í\@À9ZôUER6>R½Ñ™ï:½­lÈc‘Y^'ðº_Fn-†$•å7óéÓ‹ˆIÆyÅ{nƒ¯[êÖê ~ðA®*”Ý7xŸEƒÅÃJ»žVuâ0±¯kO~ǘi:´ú\ë$GÖ½¦ÂþÏ^³ðr9S^ 42ÛÈÑH»XuÍhiz¼úTâHÉ#Ó5­Z*Kš'& *OÙËc{Äž’ÆVšýÓsô®5Kx+Þ½öÆþÓ]²Û& #‘^oâO Me!¸€ ¨¡^ÎÒ7Æeé¯iGT[ð¿ŠåŠAk¨6S 5ë1ÉÄaÔ‡S_2ò3´Šïü+âv·aev~N™58œ7Ú‰®WšÙû:¦¿Š<,% wh9⼬£Ç&$áü«é…x§‹ †Vç^)ð¨uk»%Ãu w©Ãb-îÈÛ3Ën½¥3Ã'’ÖAkvrv¾.’+¯ Þ:üÊÑ·ò¯häI6°*éZóø˜Ááë»K–$yd~•«¡ï)#‚†bãJTæ~{þÏ7mí¢}Ƹ¸ÿÙ«ö>?º+ñ—ö{*ÿ´µ¼‹Ñî.?öjý›A…¥›+MÁîô¥n㨢Šò± (¢€ (¢€ üÍý¾`Ï~ؾ ð~³ x®ÏÉá«[»yæÞI̦áãpWc caÎ}kôʾý²?nß ~Ç:Ÿ…ôÏxZ÷ÄMâˆn¦­g޵hÔ†óÎ|ÁŒzP_ì ûkÿ±Î‡ã '^ñ5·‰Ä÷s£[@ð…²H¤6ölîÞ1Jý¯Šc_ÛOß¶>Ÿâ­CÃÞ»ðâøZ[H¤[©ã˜ÊnÖVv¾QÎ}kíj(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùçHÿŠ;öŠÖt£òXøóL‹P‡²ý»N>LÊ£Õ¢evõÅ} _=~ÐJÚ †>)À“Áz´Ü0å¾Áv~Ít£ê®¤û úY]C¡ ¬2äkÕÌ=út«÷\¯Ö:é<§&Ý”áç“ÿƒqi®é—‘‚¨êIÀ§WÂ_ðS(|ÿØwâ‚zA§7ýñ©Ú·ô¯(ë>ßMSM’e¶Žî™óµŠXãžsÒ¯Wñ×ÿÔ›Èý·þ?­ÍúÿßzuÊÿZþÅ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠñŒž6Õ´-:ÇÁ¾ Äž/ñdi§/üð\fk·ë„>lãïc‚3]\4«TTã×ðîß’Z³:µT"äÎ7Tÿ‹Ûñy[ýdó1Ý$®{³±$út]•tf˜ÎJ¾è¿VüÞÿrèg‡¤âœ¥ñ=ÿËäQEyç@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQETV¶×ÖÓY^D³ÛÜ#G$n+£Œ2°<AÁ=Ó¶ |ÛðÖêçáWŒ¥ø%­ÊϤ]‰/<3s!$µ¸;¦±f=^r½ICÛ_IW™üVø~>!x\ÙYOö oN•ot«ÑÃ[^ÃÌmŸîŸºãœ©é)¿ > ˆ7Œ`×´¹ZËU³<5½ä<8Ç÷ï!äqœƒ^¶5{zZŽûKפ¿íî¾~¨ã¡û¹{'·Oòù~^‡§QEäEPEPEPEPEPEPEPÿÕýü¢Š(¢Š(¯•¾$Ä~|]Ñ>&Ú÷Lñ?—¢ë 9†MËÿÀ³ >޽Å}S\_|¦xïÂz¯„õ|›]R…™~ôdŒ«¡ìÈØ`}@¯77ËÖ+:=ZÓÉôg^ ·%DÞÛ?AÓ_IrªŽF#V¼Â?!ð­•·ƒ¾,K&‹®éÊ`{»ÈšKá*³Apߺmê߸F85í¶:®™©B.,.¢¹¹ «=r3_È\GG±xû¹-.û-z”W»±~ŠÍ×õ¥¡ôüëçš}ŠE4ËëÇãUÞúÍZAüèIôE$ÞȵKÏZÀ¹×­`Fu!€êÄ…Qõ&¼Å|á¦òµ-z9ÈÖ§í7ôÿUæ÷ zìÃeuë¾ZPmù+›C 9t=®úþ;h‰sö_Zñ¿‰~)—Ã>Õu¨3& còmb–’æ|G ($—aô'¥q–ž1ø·ãä-ðÛÀ÷¶ò GÄ ö(F‰-ù–Aî0=ûW¥x;àn¬ºí‹þ(x‰¼EªéÌdµµ‚/³éÖ²@u‹,d‘AÀ‘ÎGP€?Ná qµ*B¥uË ë}ý,:˜Š8x»ËÞ=OáW„#ð€t/ ¢üÚu¬i#zR7Hݾó–?z-1P'¥>¿¥!¢º9¹IÉõ<_â/Å_1ð׆"]kÅ—Û£µ±²¨øÿYrà IÕ‹rz($Y¿´šv£>™p²Äv®y®š””ÕÑå`ñs¡>Ylz‡Š<,·ˆ×Vƒlƒ®;×’¸’)vÈ Éï:&µ©n2Fþâ¹ïøY.ãkË1‰$ ÆV½ÙŽ;ñö´Œß x¥”‹+Óס5é’EäGókG,2ì’Eõ¯Eð·ŠŠ0±½=8ÑZ‡Ú‰fdÿƒTÊñ/†åÓæ7P)haÚ¸­Û_wCÛô¼Éì&6Ã+ ñÏxj[k›e% íWCuË#,Ï,q~Ò‘§á˜™lîÎTð ¯R>MÜ\á‘…|Ìw.O+Ú½Âþ*òÙmnÏQˆÃ[Þ‰¶[™ß÷uH|Sáf‚V½´'RpK#Ç ’3µÒ¾”"+¨°pÈâ¼£Å>6¥®ì—*y QF²Ù“™eÎþÒ‘¹á_-Ê-¥ÛbAÆMwWÐ^@cnVó\o,/æ!ÚëÔW¬ø[Å)t«itØqÀÍMj-{Ñ5˳%%쪜Ÿ‰|5&›)¸€çµrqHð¸t8q_I\ÛÃ{E(ܬ1^3â_ ͧJnm×tgÓµ] ×ќٞXâý¥3§ð§ŠVU[+Öĉ®ßPÓíõ;s€Ž |ç #8eæ½c¾)*-/p ¥‰£o~&ùvd¦½•S„Ö´94›–RÓÈ8ãó¬‹{™m&Y¡8"¾‡Ô4û]NÝ£•CW‡ëš%ΓpÛ—1ǵmBºšå‘ǘeÒ£?kOcÑt-rÛZµû%Þã=ëñ'…¥²ÝZÑžp+·¹šÒAÀÕ?ø]ídkË!˜P+‚br¼[MF¬t8bªa*h}+*AyבÅy‰|0öR5Ͱ&3É´ü-â¦l¯°&½1ÒȰØd"¸”¥IØú û<]=>#æ¥';{¢®Y_\XN&¶lyÙø£Â†ÑšòÅ~NàWŸò9ÆB+Ò§Ë8ÜùšøiÐzÐ5ë}ZÜ)`$A¬_xn;¨Õºüãžyv}6›r³¡Â÷¯qÑu«mZØa¿ŽõÅV›£+£ßÂ⡈‡³©¹àl’A'Ï•e5éžñH;l/²“V”±oµòÜ×—÷UN£ÄþKˆÚêÍv¸äã½xÖ§cæÙÜ[L ‡­}A ÄWP‰b!‘«ƒñg†¢¸´¸»·m‡5|KøY¦i•ÅÅΙù[û= ö–·ŒtIî?öjý–O»_ŸÕ£ý§bº­ÍÈÿÐëöQ>ïÓœükÐäàµjO¸ê(¢¼c삊( Š( ¿žOø.2ÿÅKð‰½m5‘ùIk_ÐÝ=ð\…ÿ‰ïÁöõ¶ÖÇäöt×Áþ$ÒëD?š^Wï5~ ÿÁ ›þ%×Ò}þk{_½”QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÎx¿Ã–ž/ð®¯ákìyµ¬ÖÌq¾j =Ôœq^}ðÄw~#øW¢S#SÒUôËÕ<²ÜX±·{°PÇë^É_<ø þ)¾9ð;|–ž ŽY/mÒ£Þ~&UVúõp¿¼ÃU¥ÕZKòƒOärU÷jÆ]ôýWõæ} _ÁF¡óÿbŠiéanß÷Åä ý+íšøëþ ŸûüWOM ·ýñ4mý+Ê:Ïæsþ ×7‘ûj|+]Feÿ¾íf_ë_ÙEÿ°’?ÛáDʤ¨ÖQIº7_ë_ÙÅQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEdëÚæ—á÷ÄÜëka§ÄóM#tTA“õ>€rOšñ/ƒš©âMJÿãwŒ h5OÆ"Ó-dëa¤©Ý ~Ï7úÉ>£§"²¼XOƈëðâÛ÷žð”±Ýk®>åÝèù ±ÏB¨FùG=”à_K€í^ÅOöjŸnk_(î—«Ýù[»8ãûÉó}˜íæûü¶õ¸´QExç`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÍß­.¾ø¾Ž:Lúl«Ÿ‰­£™-ÄWŠ£¬–äỔ〠¯¤j «[kÛilï"Yà9#p]a•à‚®Ì/ØÎí^/F»§ºÿ.ÎÌÆ½.xÙhúz‰gyk¨ZAc*Ïmr‹$R! ®Ž2¬¤pA ÕŠù³á­ÍÇŸËðKZ•›H¼ÞxbæBNëpwMbÌz¼å:’‡·¾“£„öS²w‹Õ>ëúÑöwAB¯~ÑîEŸÄ-6åC>ŽQ¿“‘ù ûŠðªpž[?Š„~ã¶ÍxõüñÏü*¿ÚRo–Oè°Xô©IüšP*ä>0]ý½ñVTC÷“OÒmàoÁä2ŸÒ¾º¢¦ŸåwýÅK:Ä>«îGËþÊ^¿q'Œµ]sÅMq¨jÿâòÕG°ìÞø_à@¶þðõ–– `´0 ‘±ýé1½¹$× Ñ^Þ J’µ8¤¼Ž:ØÚµ>9\hþïéNc¥:Šè¹ÊQE7ðGZPè1KEQEQEQEQEQEE/Ý#Ø×ⶦ̿µóŽÚ¤ŸÌ×íDü!>€×âÕûcö¦»î?µÄ×±•6›±ñ<`¯ìãæ~…:H©ÈÜ3ô®Ó¾$’Êål®˜Ž9ô­ýGÃÑj%¼ð¯ÎˆÇÒ¼ºHä†B­ò²ði· hgìêaŸ2ØúUdŠx·.XWø§Âêá¯¬×æ‘YžñCA"Ù]©èMzÐh挆V®¥MŸG SÅR·SæfF†BÃzSíî§´˜M *V½?Å>n½³_˜rEy[G(Ë‚:×§‡ª¤µ>W†žg²xwÄqjýžèøÇ5âo $¨nì—æÆN;×™A4Ö²,ð60y¯að߉"Ô¢û4샎{×Jnæ‰ëaqP¯gSsÆJËm'9WSø×§ø[ŶØÝ·N©¼Oáe™ZúÌ~ó¸åCu´˜æ9m¥TsrÔÂÔò>‚Õô«mVÔ©œpkÃ5-2m>àÅ0Úzô x£v-/‘À&ºígE¶Ö-0ç„Ý7fzðñÅCžžŽŒ$Cµ“§½zï…ÖSÒºåIMhx¸lS¡>V{îµ¢Ûê¶ÄûðíKKŸJ¢™IàתxcÄñß ¶¹lHǽnëZ-¾«nU”ǹ)ÎTåil{øœ,14ý¥=Ï Óu+2àO ;Š÷ \·ÕmÏÍŽEx–§¦Í¦Ü´3  t>´Ý7Q¹Òîð1Ú~𮚔TÕÑä`ñ’£>Ylz‡Š|*—ˆo-F$^p+Éd†b­”‘ò¯zÑ5¸5k`TüØäVŠ<.—hníHÏJ«O–G~7«ÚÑÜÊð¯Švgzxè ¯M‘!½€«á•…|ØñËÆ9?w*W£x[Å6Ù^6{iסö¢,¿3³ö5Œ¯xj]>cqj»£nOµq@”lŒ‚½ëéY"†îŒ#޵ä&ð̶››eÌ'œÕt1ÒFYžZâý¥= ø¡£+kx~^€×©~êî#Ñ•ÇÖ¾fRP‰b8a^áo´L¶—òö&£‡·¼2¼Ïþ]T"ñ?†ÝÚòÕ~Cɸ%y!%ðܺl¦âÝsrjèV¾ŒæÌòÉEóÓ:¯ ø¤\ivpÀMvz†Ÿ§nc”q_:¤ÌŽ%ˆíe¯[𧉖î1ivØ‘zZŠôlù¢o–f kÙU8 oBŸIË Çž+*Æò{¼èn;WÐzŽ›§ŽuÈ= xŽ»¡Í¤\1 ùYàÖøzÊK–G&c—J›ö”ö=OCÖíu«A Ø.G ×⟠=«›ÛEÊ Wgy=”ÂæÙ¶‘Ö½—B×-µ«_"\oÆ5”ã*r¿C|=xb!ÉSsÄ7:Êvº×¤x_ÅL…lï&ªx§ÂÍlæòÉvy W’!°Ë]Hé¹ç§S Rý¦Eu8eaõ¯*ñG…ͳ5å˜Êž¢—Ã~,6ûm/Tð z ò/ Èã àÖœµ>{<];­Ï™ù‚9EhišœúeÂÜBHò=«±ñG…¤·v½²\¯Ryóc#žâ½Î3ާÌV£:, tmfÛW¶9b9¹x]eV¼´_˜r@¯6Óu;2àIÅ{ž‹«A¬ZR cæÅ8:o™l}´10ösÜùý•à“ººŸ§Jôß ø§xw­Ï@jÇŠ¼,³†¼µ\0êyK,ÈT’!­ÒUUÏ#–¦zl{gˆ<=©Ú È ú׌\ÚËe+C2AÆ+Ô<+âe•VÆí°Ý5±â? ŪÄf€™z{Ö¨àùYèbp°ÄÃÚSÜò]/VŸK¸YŽÎ⽿KÕmµ{U9 Häð;«YlåheR5{KÕ§Òg §µtÔ¢¦¯Î >T¥É#¸ñ7„× {d9+̤VFÛ ØÂ¾„ÒµkM^Ô2N0Er~$ðœr‡»³\7RcG×»#·—*‘ö´ŽkÃ$–ÆQmpsõ¯Yšâ½>I#!•”×γC$2¦Ê•®³DñYÃ%­ÉýÞí]\:oš'6 3”béÕ?9> 0ÿ†¬“µÜù½~ÆG÷~Eü$²Š/Ú–âé5ÍÃ~{Í~ºFZ3Kó+—ÂK÷skk¢Š+Ê>¼(¢Š(¢Š+óGþ û xÇöƾð5×…šÐýŠ9cò¾Ä.oóÎï8c†¿FëâØïöÛðwíŽ('Ë™©.ùº±ê‹åosýØåÃÜ×ÐÕçÿ< Á¯éRµ–­fxk{Èxqî?ÞCÈÁÆI½>¾nøguð¿ÅðüqÐbgÓ¥XìüMmÉ–ÐExª:ÉnO=ÊqÀׯ‚~ÞŸÕe¾ñõëû{§»³Ž¿îåíVÝ};ü¿/‘ôZÎò×P´‚þÆUžÚåX¤C¹]nVR:‚A«5äµmؘQE€(¢Š(¢Š(¢Š(¢Š(¢Šÿ×ýü¢Š(¢Š(¢¿™oø*Çí=ñv?Ú"ÿá‡þ.€?»Z+øJÿ…Åñgþ‡=cÿçÿâèÿ…Åñgþ‡=cÿçÿâèûµ¢¿„¯ø\_èsÖ?ð>þ.ø\_èsÖ?ð>þ.€?»Z+øJÿ…Åñgþ‡=cÿçÿâèÿ…Åñgþ‡=cÿçÿâèûµ¢¿„¯ø\_èsÖ?ð>þ.ø\_èsÖ?ð>þ.€?»Z+øR´øÙñ†Ææ;ËOk1M GóåXt#ç¯ëþ Õñ³ÅÿeÍÆ=¸7ºíÍÖ™stxkŸ²¸Ù+cøÊ2†=Øß÷5Q@Q@Q@Q@Q@Q@Q@L2†¿õˆ×ö¢¹š^µ79?S_´N2+ñËㅭΑñ˜ßÇòy—¬Àƽœ¡ûÏÐø®1Òçæ~¹èrÁ6—ƒ ƒùW'⟠,è×¶£ 9 w¯%ð?ˆµ-"ÆråÃ¨Ü } ¤ê¶Ú¬Vž¢¹ªÁÓŸ1éáq4ñPP–çϸh¥Û®¿‡JôŸ x ©w§§šŸÅ^/›Ë!†+ËÎøäÂ’-tIª«CÊqž¥ÖÇӤɑ†Vç^)ðÇ™›»1‚9 UO ø§a[+Öàð ¯Q%L‚Z¸]éÈú(û<]3æGGY Éò•©-ç–ÚQ,D©ñ^¡âŸ C^Y¯#’y\‚H˜¬£qŠôiÕSGÊb°³£3Û<7¯Å©['aæ9ïY>)ðºÜ+^Zœr@¯1´¹–ÊužÆ+Ù¼;âµHRÞ(ÃgÖ¹*S”4Oc Šx{)îx¡A!¬¨zt¯Mð·ŠwgxÜô MO⟠,êo¬—Ô^W‰ — :×MãZ>gïpsòg½ë5¶¯lHÆâ85⎛>›tñ\)±¯Bð¯ŠC…³¼nzk®Ö4kmbÜð ‘Á®XT•7fzXŒ5–ÈxÃ#Šò¿x]às{d¿'REὤ‹ivÙSКõ•ho ÈÃ#ŠàiÓ‘ï§ ]=w>i8åOPk[HÕçÒn‘“³¸õ®¯ÅkwkË4ÊžHçì¥qŽ ×¥ Æ¢³>bµ¸iÝCéz¥¶­l®„G"¸ÿøa%F»´aÉWžèúµÆ“r­|¤ò=«Ý´ëû}FÙdF ‘È®±•){»IF½Ÿ1‚áxÉ«ZN«q¤Ì&%3È®Š˜u5Ì?•òMhzŸˆü/ê4öãlžÕã·v³Ù â¹R)æ¾Ò5‹}NÙ]Xo=EføÃ¶Ú¬¬m<×5î/–G¥˜e±­:Lü¸ø>Å¿i«zyÓ&¯×4ûµùaàm)tÚ{K€ ,ßú Wêz}ÚéÍÝæŸ‘ÉÂjŒ¢ûŽ¢Š+È>¸(¢Š(¢Š+ùýÿ‚æ¯ï~ ·¨ñü¿³«ú¯ÀOø.jüŸÐøŒ~Ù´üɾokê<8ôå_¿õüüÿÁ Ûý+ãBú§‡äu þ¨¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(矃ÿñJøïâÃ'ù!¶¿]jÀÙu5Þêƒû±Ì¬>¦™û^CçþÊ“ÓÂëß2·ô§üEÿŠGã'€¼|¿%¶°fðÝóz‹ŸßYûü¤~5£ûQÃöÙ›âä|Ïkëùéó õs_}¿ó¥U£ûÚ¿ÌäÂh¥O³ü«ü¾Gñùû$Íä~Õdéÿ†€¿ƒ_Â?­oü9þÌs}Ÿö“øO?O/ź~Wðšþã+Ê:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š+˾-xþOødI¤Áöïêò­Ž“f93ÞMÂdq>óž2 ¶*³Tá»"¥E¹KdyßÄeø½ãȾi®áÑŒW¾%™Œï·°wòz(àç ý#QAC ãŒUQ…Uà^wð¯Àü;ð¤zTóý»V¼‘ï5;Æåî¯gù¥ŸLð¿ìß5éÙ˜b"íF—Á¼ßY|ÿ$‘Ž›Wœ÷‡eòüîQEy§HQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQET6Ö÷¶ÒÙÞD³A:4rFà2º0Ã+ÁpEOE4í¨6|6¹¸øSã9~ kR³h÷¢[Ï \ÈIÝ;¦±f=^rICÛ_Iך|Uø~¿ü.Ö“ýƒZÓä[Í.õxkkØyóýÒ~VåIïŠá?Äñÿ†Œúœ`×ô©ZËV³<5½ä<>÷ï!ä`ã$ƒ^¶5{zZŽûKפ¿íî¾~¨ã£û¹{'·Oòù~^‡§ÑEäEPEPEPEPEPÿÐýü¢Š(¢Š(ùÿ‚¦ÿÉíxçþ¹iŸúC y·ì-â/ÙëÂß´—«þÓp^xE-n?¶[›«8ïX/“%Ä_ÌŒ ãX*HÀ¯Ñ?ø)çìAñÏÆŸî~4ü,ðå׋´¯Û[Gs ‚y×6·шpМ£"©  àäqŸÌøb¯Úßþ‰‰ÿðY?ÿ@?¶¾¿ðÄß´.¿¬~ÍÖqYø2dƒ`¶€ÛZ½È@'{xJ©Ž6n‹´ 䀾·ÿ‚~øÿöð—Ã/Y~Óúf›sâ+§>Cêz{_4–~^vŒ±ÉåK¿<‚œÜd|oÿ Uû[ÿÑ!ñ?þ 'ÿâhÿ†*ý­ÿèøŸÿ“ÿñ4ó¶»&“.·¨K Æðéq)µI]`.|µcÝ‚àzýgøñ þ û}ûèþð^‘aÅHí­Tyv š¬z‚‘ö‰n/<±æDß7c)@QŒˆ኿kú$>'ÿÁdÿüMðÅ_µ¿ýÿà²þ&€>a¢¾žÿ†*ý­ÿèøŸÿ“ÿñ4Ã~ÖÿôH|Oÿ‚Éÿøšù†Šú{þ«ö·ÿ¢CâüOÿÄÑÿ Uû[ÿÑ!ñ?þ 'ÿâhæþ¬ÿàßòg–ÿöÔÿö>VŸ±íw{s¤_üF+M>XýçpG¹ WõûüñìÛû7h?ü`èué&¸Ô/Ò6ÍvÛ¼ Ã†òÐ*’8, b€>Ç¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(­Ò¿ ¿jÖÓâD3¹Ú«tI?~¾?Jüeý±òþ,,Ò·,3ôÍzÙB¼ÙñÜeüê}Ëák)Ç„4ËìïŽXÁÈ®«IÖ'ÒçÄß ŽTè´;ݤê¶ú½¨u ’9Æø§Âáƒ^Ù¯#’pÚ³&‘r²ýÛE{­…å¾§l³DCŠá”\2Øú!‹§Ë-ÏœˆxÜ‚ ºW¨øSÄèè–WGæ4x§Â¾fëË1†+ËÕç‚Cü.½ë­¨ÕZJU0µ<¦~YÕXWœx§Âêá¯-æ;šÃ~-\%•ãrx×¥’¦G*¸Z•6}©âé2¼n„£Œ0õ«7’ØÎ."b¥#Ö½+Ńwd0ÝN+Ë^?*L?ÞEzT«)+)ˆÃN„ÏxÐuÛ}bØ) 8 ×;⟠Q®ì× :Šó};Q›NºÆp28¯tÑõHµkU•NsÔW é¾d{¸ZÐÅÓösÜùô¬I´‚ާšõ ø§~Û;×ç &¥ñW…Ä ÞZ.ä⼩„LC|®†º}Úó<«ÔÂU¿Cß5ÛW¶è7TŠñON¸Ófky—ŽÆ½ÂÞ* ‹+Ö瀦ºýoF¶ÕíŽWçǹ©Ôp|²=LF˜{Xnx5µÔ¶’¬±R;׳øoÄqjQˆ%lJ¿†kÈu->ãL ¸^Ý5ZÖæk9–h[cJë©F3އ‘…ÆN„í#Þõ­ßU·`ʘà׆êzeÆ›;C(Àƒ^Ãá¯&©ŠFýêñõ­ oEƒU·!×çƒï\tªºR´s„Ž"Òž§j3és¬ñ1 žkÝ4M^ZÕ[ ¶9áúž›>™9†QòÔšF«q¥Î²DNÞ⺫RRWG“‚ÆÊ„¹g±é>)ðº]«^ZŒH¼àW“H’Û¹ :š÷ý#VƒU·!ÉÇ"°°(ü¬½«½ð¿ŠZÞU´¹9N™5£â_¨Ýyb¹=ñ^hÁáÆÃkõ×˱ÓsǨªa*\úT4q0ÊÃë^_⟠-yd¼ UO ø¥íZ]§@kÖ’Hnâܼ«W­9Xú$éâ©k¹ó;)S±†®ƒA×fÒ§_œ˜‰äWU⟠´{¯l—ŽàWš²•%Ák¹TSV>j¥*˜j—>²º‡R¶) ¬9çž)ð¹P×vKÁäŠæü;â6ác‘³u¯l¶¸‚úÜIܯڸä¥NZANTñt¹e¹ó`2ÛÉ‘•u5ë^ñ »Agtß8àY~-ðËFZòÌqÔ^y Ó[Ê$_‘×Ò»9UXèxñœðu5Ø÷kÃöš¬•ýæ8"¼KQÓn4ÙÚ)W z·†|MìbÞé±(àVÞµ¡ÛêÐÃŽ sR«*nÌõq8Zxª|ô÷mPÎu;÷‚øZìòü˜äû¾CnÎ:Œw¯Òšøãö¹ý´¼û[x^ëÇZ&§¬¯Šžñ-Æš!&3f"/æyÒG×ÎÆzãŠñ/ø'ì?ãߨêçÇ“ø×_Ó5±âÄÓV§ùߺ6&ä¶ÿ:4ûÞpÆ3Ðæ¿M+âÙöãø}ûaÞxžËÀú©¢·…£´’s¨ˆ^Bìòd“§”sœu¯¶(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(È~;øbçÅ_ õÛM;#R±ˆ_Ù2ýõ¹²a<{}Ø¦ßÆ»?xŽËÇ> Ò!Ö[â÷æ¹Ô.% ®éŒwÊíÒê3Ü×÷A^ èQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@]][XÛM{y*Áon$’9 ¨ˆ2ÌÄð$×ο mn~(øÆãㆷ.™ Éeá›y;Líšð©è÷azƒ‚ /Å»¯‰¾.¶ø¡JÉ`;ßÜÆpb²Îc´ :IrG#¨NpA"¾ˆ³´µÓí °±‰`¶¶EŽ(ÐmTDUP:¯cýÚ‡÷æ¾èÿœ¿ôŸ)Å©ýØþ/þçèX¢Š+Ç;Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾nøŸgwðÃÅÐüqÐbi4é;?[D2e³Þ*޲[“Ï)Ç_HÔ6Ö÷–ÒÙÝijA:4rFà2º0Ã+Á]˜_±Ÿ3W‹Ñ®éî¿Ë³³1¯Gž6Ùôõgyk¨ÙÁ¨XÊ·×(²Å"Êèã*ÊGPAÈ5f¾løosqð£Æ’üÖdfѯD·ž¸“˜Ý5‹1êð¹:’‡·¾Œ¹¹¶²·–îòT‚T¼’HÁQFK3êMÅ |éû3~Î^3ý¨þ(Áð»Á7öwMm5íÅÍÉ>TÐVr,Çsª€:’: šý£ÿ‡àø7þ‰]ÿþ ¢ÿãTÃð|ÿD®ÿÿQñªüPý£¿gÿþÌß5…^6– «ÛH¡¸ŠâØ“öó®ä‘Aät ƒÈ öäû·ì³û|Uý«|¯øãÁÚ†—c£Jm¢…÷\ܪo1®ÀvË^”úsÿÁðoý»ÿüEÿƨÿ‡àø7þ‰]ÿþ ¢ÿãUüõêúUö…«^èšœ~UæŸ<–ó&AÛ,LQÆG_vøÓþ ÛñÀÿ³M¯í+©ßØI¦Ëmôúr3}¦ [‚69b61ù—rŽ™êhôŸþƒàßú%wÿø2‹ÿQÿÁðoý»ÿüEÿÆ«ùÚ¢€?¢_ø~ƒè•ßÿàÊ/þ5Gü?Á¿ôJïÿðeÿ¯çjŠþ‹m?à·þ{˜Ò÷á~¥€wŽþe^ä)A>ÙZýø7ñwÁŸ~hÿ|rntmj2ñïdÑŠI‹ü.Œ ‘íé_ÂVŸðGöcûÛI ®ê`{ Æhõ"Š( Š( ¸_‰ü ð‡Á÷¾>ø‘«G¡øN1-ÅܪäX£F¬ß3²¯½wUùßÿTÿ“ñïýwÑÿôçm@Çü6þÊÞø‡ñ?ÀÉ­x‡Q¸Ô£žèê…¹u‚òX£-î#AµtÉçšô¿Ž?ðGŸÙÿÄþ¾›à“^x7ÄÐE#ÙÇ-ä·š|ò•ŽqqæÌªÇåÞòç%_ ØsÈ¢¿ ¿à?´'õKÅ¿³Gof¿‹ÃÚP¹}òÙÇëmsjåŒa匢ç óÁ~ìÐEPEPXdWäí¹á÷ÒµÛ+æéy9?˜&¿[š¾ýµ¼.»á¼E'û1ŒÐÆOç^–U;U·såø· ç…ºèq?°ÇŒÖ:ž} VV_,Ø_ º¶©$ƒçå>•ø9ð[Çw>ñņ d)f_lƒn¤ Äð׊f´YÝS±=«×a¸†êèC+W›§-vœéâ¡g¹ó[©Š8Ã/5µ¡k3iWAÔæ2~jíüSáT”5í¢ò:^XË$dÆãiAï]ñšœOš¯†ž¥Ï£¬¯aÔ­„ÑÊÃ¥q)ð¨”ËÃXz×'á¯ɧL°ÈߺcùW¶AZªësĵLKt=÷[Ñ-5{c¸|ØùMxž§¥\és´s‡ƒë^•á­Ò­¥ÓaÇ=ë©Õô{m^Ø£¯Í”×4déÊÌõ1hbáÏ Ï³½žÆužóÕí¾ñ ­º«0ȯÕô©ô›‚®ëUlo&±˜\[¶rkªµ8Í]V:å–Ç»kZ¾­nQÀŽ x®­¤\iSä/c^Ãáý~RÀKéZ¾‘mª[´r¨ÝØû×:ŽÌöñxHb!Ï Ï Òµ{­*a<äæZ÷#X·Õm–D#$r+Ä5"ãI¹häð{MVŸKºY"?)ê+®­Î7G‘ƒÆÎ„ù%±ê>%ðÔw±5ŪâQÏã×Ëk'—0!¯¡4­RßS·ÄÀ±Šç…rbéŸ3°ØØe þ¢»O ø‚M>ám'Ý7sZþ(ð¹B×ÖƒŽàWš°a‡Ïü«ÐŒ£R'ÍNL=K­¥Õệ<:¸¯,ñG…š öÐdu RxOÄ­-¥Û|§¡5êûb¹‹nVçÞTä}'¹‹¥g¹óTRÍo(–«©äW°x[Ä«¨D¶÷/Ïx«ÂæÝíšü£’p6—sZÍöˆN×S]²Q©Äêá'g±ô­¦[êVíª3Ø×ç/íaâá1¼&²|× °ÇQÔWÛ·¿´ÃójZ¼Â#§ q_Ž¿<_ñ“â@ŠÌ™v˲>ÿ.k\¾Œ¢îö8¸§J­8Ƴë?ا@–ÚÕ5 †iôÍ~—/JùÿàçÓá/i'bVz–Q_@/JãÌj)OCÞáŒ,©aí!ÔQEyçÒQ@Q@~ ÿÁrþ$›Òç\šY×­èÞÒ.õÿßA¦iš|M5ÅÕÌ‹ ă,òHä*¨I8¯æCþ £û^|ý¤o¼á/„w—:ºx:}A®uƒÉ³œÝ,*¢Ü¹>ÓËÕNARÀæ€=«þpßñQü^_[MþR]×ô3_ÊOüö²øMû/øßÅÐüZ–òÊÃÆØA í¼h‚Ù­^ff¸Uo4)ó hçÔÍR¾ñg†Y[;.5÷ÈÙ¢‚p½¿Ýj¿wó}#óüßC›Q«B¿Ã»ù~v=á'€'ð'†ÜëS ßëRµö¯wÔÍw/, ÿr1ò dשÑErbq«7R{³jtÔ"£QE‰aEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEɺ2(X¸uî3Þ€>^ýª¿-Ÿ6ɪ´Wçgì­ÿ(øûHýÃZ­Àð?çÚŸÙz„«äÜÈ{YÝ©.OD`’ѯÑ:ñNࢊ(¢Š(¢Š(ÿÒýü¢Š(¢Š(ùÿ‚ªØÝÚ~Ú¾1šæ&.í´Ùbf‚Ò4ܾ£r°úŠø»á_Åüñ§þjòèºÝšº$ÑàîŽA‡GVÈeaÔèz_Øçíû'~Ï_´JZê|?ÝΚ»!¾K‰,ç ÎÃ,L¤®…²3Ðf¾Uÿ‡]þÁôŸÿ³ÿñÊþa¾&üOñÏÆ/ßü@øªË¬ëºŽß6â\–5 Šª0U@Šô?ƒÿµ'Ç_€Ú.±áï…~)ŸDÓõÁþ“*8ß·o˜›ÁØøãp¯èÛþwûÐ>üÏÿÇ(ÿ‡]þÁôŸÿ³ÿñÊþSgž{©ä¹¹‘¥šf.îÄ–fc’I<’O$×ÐZçíYñïÄŸ¬þë>-¹¹ðmФqÙ°\˜¢9Ž6 ì‰ÙIô¯èÃþwûÐ>üÏÿÇ(ÿ‡]þÁôŸÿ³ÿñÊþR(¯êßþwûÐ>üÏÿÇ(ÿ‡]þÁôŸÿ³ÿñÊþR(¯êßþwûÐ>üÏÿÇ(ÿ‡]þÁôŸÿ³ÿñÊþR+ú½ÿ‚CØÞYþÇt·P´Iy¬êsBXc|{Ñ7Q¹~>ÿ¼ýƒVú‡F–ñÃ!mnáÃ‘ØªÊ Ù¯Ñ øWþð퇄ü%§Ã¥hú\Kµ¬ 8£^@ÿ&€: (¢€ (¢€ üïÿ‚ªÉŽø÷þ»èÿús¶¯Ñ üïÿ‚ªÉŽø÷þ»èÿús¶ Gþ mã?迱_ƒ¬µvÂÆâ+X¼sÝE¨7óJ³29éÒ½«ö‚ý½¿g?€Þ¿Õ¥ñv›â_,2}‡GÒ¸¸¸äI A÷¤“v†l)ü˜ý‹?à˜ßÿi?ÙãÃÿ¼câ_iÚ®­5ôrAa-¢Û¨µº’*%¶‘òU9cÎqŽ•ë~6ÿ‚(éº}°Ö> |J¸‡[²‘fµ‡\´Ž[wt;”<°W|—Šó@ ÿxø!ã…×ümûPxÊÉì-|Mlú~˜ÒÆc7‚âán®§Œ~è¯f‡ò2Öwˆ~2þÏš¥»mø•á"Ž1¬Ùuÿ¿µü©MûxêÎ_+RÔí­rñÉÇáŠè4ÏØ¦ïPuŠOéöîÜ|ÖÓúW4°Uâî£cÖyÆ ¤ygU?¼þŠgøÓð^ Ú&øƒá⣣ ZÐý]…ûH|Ò¦¾ ø}£èâkiÿÇ+ùø³ÿ‚skÑy°|JÑG³ApòªsÁ;¼C ”oˆ:;c¸Šð­_µ’å”.yÑx8Kž5ÏéÆÓöøyvø‘á¥Èä6±f?œµÈx“âßìûzqkñ+Ã"_A¬ÙñÚþp-àšõÌžYø‰£Gîa¸ÿ Ùÿ‡kk_ôS´?ûñqþΨՋÒ'«,à ZŽ¢g~ Äûâ‡ò½Æ­h”µÖx{ö—ø9§H°OñÃæ/}VÓÿŽWó÷/üc]EÜŸôGöònô¬I?àž ‰Êˆ:9Ûéÿá[Z¤•¹2ÁÒ•ã^Çôñíû?ÜÄüJðÈ 9X²ÎZáüKñsàÒ‹/‰^Þ; fË?ú6¿œËø'.»zHoˆÚ,8þô7úV‡ü;_[ÿ¢›¡ÿß›ð®uB¢wå=Z˜ìhrÊiŸº²|nø/m?<=‘éªÚñÊîü+ûJü³ak{ñÃÛBu[N?ò%=7ðMÍv̉ñ+D“„7ÿ*Ä?ðOM|gþ.ŽqÿLgÿ ê’«(ë˧SFwUÏêþö~š>~%xdƒÔfËÿŽ×™xŸâßÀVcwcñ'Ã,ººÅ‘þR×óÇiÿß×.b‰:$yìa¸ÿ µÿ×Ö¿è¦è÷âãü+š*±wQ=:øÌhZUûªŸ> DâHþ ø}]Oý­?øíz÷†i“Û‹{‰Ó»jöc?œµüà]ÿÁ85ûeÜŸôI~‘\éYkÿò×ÙöÿÂÁÑÇ¿•?øVÕcVkX~„¡+Æ¿àMº¯ÇÙçT€Å'į dô?Û6_üv¼gUøÃðJÆrüDð䩞««ÙŸå-~ EÿÚÖåPÇâfˆ¹íäÜ…IÿÖÖÿè¦è÷âãü*)´>ÉÓ‹©‚®¯íû¿cñûàå„é<íkOþ;^Ù¤~ÒßomÔÍñÃq8X³ÎZþgî¿àœþ ¶m¿ð±4g¢+ðªÑÁ< Œ~ hç‘Oþ¡eÿã×nÆ[â>‹ûÐÜ…EZ5¼–6Âf˜H.Ol™ýx—â·ÀY®ì~%xd“ü+¬Yý«\øåðb6~ ø}]O_í[Oþ9_…Gþ ­¬ƒøYúýù¸ÿ ­sÿÞ× ]éñ'D—é ÀÿÙkZn²Vå¹Í‰Ž ¾uVÇôYáŸÚ{à˜Öÿ⇗nmZÐíJîŸö‚ýŸ§ˆ¤Ÿ¼0Á¿ê3eÿÇkù‘´ÿ‚qx’ðŸôUÇ÷¢¸û-vV_ðK/ÝÀ'?ü={4w9ÿÐk ”*7~SÑÁæ8{rª©Ÿ¼þ%ø³ð9ZêÃâG†œ«¬YŸå-rV¿~ ÙÎ&‡â‡ÔúŠÚñÚüQ?ðJùªÞÇýr¹ÿâk3Pÿ‚\xªÅw‰þ˜³Ïÿ[EÕµœnpâ°øG.uRÇôG þÓ_îà wñÑÈ?½«Ùç-mÝ||ýŸo!0Ëñ/à ýFl¿øí4éÿÐñCÈ#ÿ…‘¡ ÷1ÜcÿA®¢/ø%g‰eŒIÿ [ëžÆ;Ÿþ&° —ºF†a‡œy=¥ÏÜ¿|_øiv^Óâ'‡$ºíÕìÏò–³´ïÚ àÞ™r³ÁñÃáGQý«iÿÇ+ñ ÿÁ*|LßøZþÏýs¹ÿâkÿþ ƒâ«*~%èû¬wüMoU¯„ó+RÂÂ\þÖÇôy¤~Òÿ/à /Äo ÄýÃjökü庯ÇÙëR£“âW†7cƒý³eÿÇkù¸´ÿ‚fx¢êAü, <÷hî1ÿ ×L?à•^% ñ_ã?ôÊçÿ‰®wF¢wå=Hcpõ!Êê&~Üêþ YNËÄ?JŠêömü¥­öŽø7¤Ü€ß|>bcÏüMm?øí~?ü«Äȹ¼:íÏÿ\å×ü+Å6²˜ÛâN‚þëÁú t¯jÕ¹)ÒÂÒŸ¹¥Zý¨üFѧ_D†ã?¨¯2¸ýŒõ8\¢øÆÅÈÿ¦]§VkXmøL<®«þõy?íû?O$¿¼1µ‡ýl¿øí|ïñ ö‰ýŸ¼.¯ycãÝP''e¾§k)ú|’þl®?dD؃_²™}DréN¶ý޼ozÁ,µ[[‡=‘$'ùTáðµá¯%ÊÇæ˜ Dy}²_&~š|Tý§4/Í%¥§‰,bÓÉ8U»‹8íÑ«Ñ?eI¾ͯEâïøãAÓã²`wZ¬N烜<€×Á ÿà”ÿþ#Eöˆ5}?Iƒû÷i8ü‚©¯mðDŸŒ„düCÐAÿ®7_üEk_1¨“ƒŽL¿†hJj²ªåo+¼q~Ð?bA|Kð¨ý³eÛþÚÔéûBüÁÿ‹™áŸüÙñÚüðDŒ‡þj&ÿ~n¿øŠ_ør?Æ?ú(šýùºÿâ+Çr¹öñ*±û×ÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄRûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@—ücöÈ“âÏŽ×àOÃmf;¯øoÊ–úâÊe– SQeþ²2Uâ·*€qæïc¨GãÕvŸ<©ü3ø…âo‡:Ó¬—þÔ¯4ÉÝòYÌг¨nv±\®{\]ú‹ÿÃý¯ï~|[¶øgã-PCðóƳgûL Ó¯Üm†ì3±«#˜’ÒîÅ~]V¶ƒ£^x‹\Ó¼?§.û½NæXWÌ“8EäP÷ ÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚŠÚ3ö|¶…î'øá„Ž0Y‰Öl¸þÚ×ó³ñþ +ã?…z kþ/ø£ AȆ†é§¸qü&Ѹúžub|šcßdí׬ˆíòJ?¥{ ƒ‰´¡I¸÷ÿ‡8ñ…O–¤¬ÏêïÂÿµWìíâÍ(k6?ôhYcE»Ôí-åa”ß弡‚¶2¹A t®‹þàýÏ àæËÿŽ×ó›ð§þ ñOâǃmüg¥øçE²·¸’XÄSEr\˜©$ªÎ+Ò?áÈß?è¢h÷æëÿˆ¯?EÓ«*ršv·c¢ŒÔ ¤î~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿ\>ÿ‚ø£¯ø¯Ä^ _èÖš—‡5f‚à ¢¸MñÍU9CÈ=#œ(ÊJRŠÑjþû~l©M&“êD_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üEfQûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPOø,_í ¥|Añ—þøÄ6z߆´«Ö.dÓ®£º·’úæY Ev‰™wÃDœ1õ¯ÅŠû£ö½ý‚¾&þÈg‡µÿj¶~!Ò5ù¦¶V *¥½Äjb—ÍUÁ‘72c9Øý1ÏÂôS‘Þ7Y#b®¤AÁt ÓiÈ#¬q©gbd’z(ûý”¿ko†¾=ý<∾>ÐôÿϦG£ö©kɹµf·’Y#’Ee2˜üÎ@ûÙb¾„ÿ††øÿE3Ãø9²ÿãµüûø[þ¹ñË_ðÖ•®j~2Ñtk½FÖ‰lgŠå¦µyP9†B©·|dílqpHæ·¿áÈß?è¢h÷æëÿˆ Þ¯øho€?ôS<1ÿƒ›/þ;Gü47Àú)žÿÁÍ—ÿ¯Á_ør7ÆOú(šýùºÿâ(ÿ‡#|dÿ¢‰ ß›¯þ"€?z¿á¡¾ÿÑLðÇþl¿øíðÐßè¦xcÿ6_üv¿áÈß?è¢h÷æëÿˆ£þñ“þŠ&ÿ~n¿øŠýêÿ††øÿE3Ãø9²ÿã´ÃC|ÿ¢™áüÙñÚüÿ‡#|dÿ¢‰ ß›¯þ"ør7ÆOú(šýùºÿâ(÷«þàýÏ àæËÿŽÑÿ ðþŠg†?ðseÿÇkðWþñ“þŠ&ÿ~n¿øŠ?áÈß?è¢h÷æëÿˆ Þ¯øho€?ôS<1ÿƒ›/þ;Gü47Àú)žÿÁÍ—ÿ¯Á_ør7ÆOú(šýùºÿâ(ÿ‡#|dÿ¢‰ ß›¯þ"€?z¿á¡¾ÿÑLðÇþl¿øíðÐßè¦xcÿ6_üv¿áÈß?è¢h÷æëÿˆ£þñ“þŠ&ÿ~n¿øŠýêÿ††øÿE3Ãø9²ÿã´ÃC|ÿ¢™áüÙñÚüÿ‡#|dÿ¢‰ ß›¯þ"±|CÿiøáM"ã^ñ'Å/ éÚ}ªî’i£ºUƒîd“Ð’Ofªrj1WlM¤®ÏèþàýÏ àæËÿŽÑÿ ðþŠg†?ðseÿÇkùÌø_ÿšñ÷ÅÝóÄ^øƒ¤­•ä–E®mnc2ž^¡QK=¶}šÙ?'·“³èrâ£j‹¦þŸðÜÏøho€?ôS<1ÿƒ›/þ;Gü47Àú)žÿÁÍ—ÿ¯çëßðF‰¾,Ðl|K üKðýΟ¨Ä³C †ë•aМx ò ó[ðäoŒŸôQ4ûóuÿÄW88·+4tÆI«£÷«þàýÏ àæËÿŽÑÿ ðþŠg†?ðseÿÇkðWþñ“þŠ&ÿ~n¿øŠ?áÈß?è¢h÷æëÿˆ©ûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚ?᡾ÿÑLðÇþl¿øí~ ÿѾ2ÑDÐ?ïÍ×ÿGü9ã'ýMþüÝñûÕÿ ðþŠg†?ðseÿÇhÿ††øÿE3Ãø9²ÿãµø+ÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄPïWü47Àú)žÿÁÍ—ÿ£þàýÏ àæËÿŽ×à¯ü9ã'ýMþüÝñѾ2ÑDÐ?ïÍ×ÿ@½_ðÐßè¦xcÿ6_üvøho€?ôS<1ÿƒ›/þ;_‚¿ðäoŒŸôQ4ûóuÿÄQÿFøÉÿE@ÿ¿7_üE~õÃC|ÿ¢™áüÙñÚñ_Š—?±çÅÈOxûÃÚ \G¨[k61Ü®:>n$Qèàã¶5øÿÿFøÉÿE@ÿ¿7_üEðäoŒŸôQ4ûóuÿÄWFV„ÕJ2qkª3«F3,ÕÑú—û8ëÿ ¾ IâUø¯á=CL½šÚ{+˜µ›%/…‘dFf% žP=ÛÆ_?e/ˆž¼ð޼gàí{E¿]³Ú^j–Bã±ÚÒœ0ê¬9Aæ¿¿áÈß?è¢h÷æëÿˆ£þñ“þŠ&ÿ~n¿øŠÓ0ÇÔÄÖuêüN×ù+‡¡PP†ÈÅýªàŸ¢ûgŒÿeo‹>u¤o êý–áßm•Ü“s쓟Sæž¼Ãöbÿ‚žütý¯"ð/ÄÍþ?𕄆ݭîæÝ¨YˆÎÒ-¯2ÛÕqÄro\sZ??à—ß<-'Œ>,|gð¾§Â!,wm=èϗo !’Wÿeàrp9¯ËÈt‹S\'†¢ŸW–âs¢C î 6Øö»›sñò œœs\fÇö¹û?~Õ?i­ ûcáWˆ#»º…Ýé—‡Q´Ï¾·$¹àH…£'…s_D×ó•ûÿÁ+~1·ˆ4‹?5ëï†Ù2\ZYiS˜µ×=y*åmŒdeä ²²Fy¯è„Çfq…Üä³ d“É>¦€%¢Š(¢Š(ÿÓýü¢Š(¢Š(ù`ÿ‚¶|`ñ߈?iÝGá¤ú¤ñxs–ÖbÖÊ9a2ÜÀ³I3(8g;öäô½~W ý@ð.eÿ¾Ûükôþ ›ÿ'µãŸúå¦é 5æß°·ÅƒŸ?h=3Æÿ´ŸíOGkq¹·bÎæM¾]É€çx@xÛ€$@$ûñÁ¹—þûoñ _ê¥Ä¼¶ßã_R~Úÿ>|`ý¡µÿüÒ?²<3t–ñ§î¯ÚgËp òÄÐ`ÌÖÿðOßÚƒöNøðËÇøñáÑ{¬ênd†S§-ÿÛm¼½¿dÙ?9T9ÉaŠü¤þпÿŸ™ï¶ÿ>ߨc?h—ï·øÕvïO¿Öõ í&×ì677ɾwy1;’‘ç¾Õ gÚ¿Yþ'þÕ±ÿˆÿ`Ý#à׆<&-þ Ckk•ýž±µ¥Ü$y×fóq&0ÅÛvE~Gÿh_ÿÏÌ¿÷ÛÚÿóó/ýößãTè  ŸÚÿóó/ýößãGö…ÿüüËÿ}·øÕ:(üZ®©‹47“G"ÊË#èAƒ_×wü3â·Œ>/~ɺ¹ã«é5=WJ»»ÒÍÜÌ^Y¢µaå4ŒyfÁKN2y&¿*þ¬ÿàßòg–ÿöÔÿö~¤ÑEQEWçüSþLwÇ¿õßGÿÓµ~ˆWÅðPŸ†>:øÃû'ø¿á÷Ãm%õ¿ê2éoiÇH ¿‚YiYmDfäöãšó¯ø%'ü™‚ÿëëWÿÓ„õú3_ÎÏÀËø+7ìõðÛNøUðûámŸö–÷Cö±e4Û®eiŸs‹ÕÏÌçp+¶ñÿ¢øÍ¦Í jIôËã²I-n´Í>@§ƒ‰­ä–ñ®Ò ´âµv¿§üMÿ‚®xËá|‰}¢ë±¹šØî_¶X\‰®°ã6éòɃòùl Šþ—+óö"ÿ‚nøsö`×âuˆü_ñX™"™#e´Ó¼àVcndùä’@J™œ)ÚJ…˜ŸÓê(¢Š(¢ŠCžÔ€S¨ m5JëM³¼]·P$Ãý¥ùÖ…JMlg*QzI\ð?~Íÿ <])—V°“sþîSò®!c/‚‹ÒÂä{ý¥ó_YÑ[¬eUöŽ äøi;¸#å1ûüA„·½_¥Ü”ßøc¯ƒä’Ö÷§þß$¯«¨£ëu;’òL3ûÊ#ö:ø<:[Þû|’—þïàÿüð¾ÿÀÉ+êÚ(úåNà²\2Ú'Êcö<ø@ßø%ðÇ»ÛÞÿà\•õe–*}Áä¸oå>T±ßÁÑÒ Ñô¼’“þëà÷h/¿ð2Jú²Š[©Ü¿ìŒ?ò)ÃüÿžÇþß$¥ÿ†:ø9Œ}ž÷ÿ䯫(£ë•;“ý†þCå?øc¿ƒà`A}úü’“þïàÿüð¾ÿÀÉ?ƾ­¢­ÔîØØoå>Sÿ†;ø?ÓȾǽ䔿ðÇ_:ýž÷ÿ䯪è£ëu;‡ö6ù”¿á޾u_àd”ÃüÏú‹ïü “ükêÚ(úåNãþÇÃÿ)ò¡ý޾·X/ý¾IMÿ†:ø<E½ïþI_VÑG×*w²\3ûÊ_ðÇŸ É‚ûÿ$¥ÿ†;ø<9_àd•õe<]NãY>+rŸ(ŸØëàñ96÷§þß$¥±×Áìÿǽèú^I_VÑG×*wö.ù”ÿᎾcE÷þIIÿ wð|t·¾ÿÀÉ?ƾ­¢…Œ©Üo'Ãÿ)òü1ÏÁÓÖÞ÷ÿ$§Øëàèéðÿ·É+êÊ(xºÄ²\2ûÊGö;ø=Ú ãÿo’Qÿ wð|Œ/¿ð2Jú¶Š>¹S¸ÞO‡dùH~ÇŸîÁ|>—’S¿á>mÚ#Ôö¾—ükêº(XºÅ›¶‰ò·ü2Ân˨ÿàt¿ãM?²ÂVdÔ÷¾”ÿZú®Š_ZŸqË( ÷‰ò¯ü1÷Â,ª¿ÿÀÙ?ÆþÈ ¹¨àt¿ã_UÑOëu;‚ÉðëhŸ*Ãü$'%5ú~—üiOìðºÇ¨­ì‡ú×ÕTP±uÌo(ýO•ìðË+ñô½“ühÿ†@øMýÍGÿ¥ÿú®Š2£êJɰëhŸ+à |%Ç)¨ÿàt¿ãHcÿ„g*üýodÿúªŠ_[©Ü§”aÿ”ùSþÿáéøú^Éþ4¿ðÈ ¿¹¨ÿàt¿ã_UQGÖ§ÜK'í¢|ª?d„Ãø5ü—üiì{ðˆõŠÿŸú}“ükêº)ýn§py>ïå!ûüï÷þIGü1ßÁÿùá}ÿ’}[E/­Tî/ìl7òŸ(·ìsðyÆ×·½aèo$5ýŒ¾ ÷±¹ünX×ÖTSúÝNây&ïäóû|ëö¯ü zé<5û/ü*ð¥Ò^iZ|¾bÃ̘¸ÏÐ×Ñ”QõÊ›\qÉpËUQ´±·±…`´‰aE€?•[ÁÏJ}ÎÛêzQŠJÈ@1KEŠ (¢€ (¢€ (¢€?›ïø+‡ì­øwÆóþÓþ±{¯øƒÊM}!Rßa¾E­ËÒ… º AÜs"×â%~w–vš…¤Ö„ skr±J¡ã‘a••H8 Œ_šÿÿà“²?ÄFãXÒ´íGÁw.dq¡Ü¬VÅ¥½ÄsÅÿ³@;@É¥~¹ÿÁ*¿d kâ¯ÅM?ãß‹ì^x&ä\Y<ªBê¬'0¬YûÉlø‘Øq½Q9ËmýDømÿ‰ý’¼ ©¯Á«øÚhY]cÕ®Ô[n^Fa´Ž Ëž«#8=#Šý3Ñôm#ÃÚ]®‡ YA¦éÖ1¬6öÖѬ0à *Gª t(JŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ™ ÆÂ& 䥆àbFFG¶GÖŸE~K~п>=I¯]øÏZüijÙÄöjwÁ9 ö^Lj98zެÙ$×Å•û¥ñ×Åžá¼3á–Ljü_:é:~(Óñ,ç…Š=Ì[±Årúïì¥ðw^𵇇%Ò…•ÆnGiˆnX¨Áydf9'z·$ãúÞKÇÇ®GG¢åVÑukmôÓÏMÇd<õ%ì^½oßÔÂý‹ßwÀË%þåíØÿÇóýkêúò/‚Ÿ ÛáƒÁßÚS.æž9¼¿)¶K‚.[‘ŽppzñÐzí~sžb![V­7x¹6¥ÀÓ”(ÂÝ ¯>,ÅñŸaýÝ‘q¡kG ûãæ ˜öÏ‚O\6+èºæ|gám;ÆþÕ|%ªŒÚê¶ò@Ç(X|®=ѰÃÜ Ã.ÄÆ•Tçð½£Ñÿšó4ÄÓr…£¾ëÕ5ã_¼S¨ø‹À‘éž!8×ü34š>¤¤ä›‹3°>{ù‰µóÐ’q^ËXâðÒ£RT¥ºeÒ¨§%Ô(¢Šç4 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€<«ã_Á¿ü|øk¬ü,ø…hntb=¥“ky”îŠxXƒ¶HÛ §Œ ’òÓûCÁ2ÿiŸ‚:ÍËøw@¸ø…á­ÄÛj:$q1Lð'³MÓDà}ìÑÏ5ýuÑ@Ãn‡û7~о$ÔF“¡|3ñ%åÞðŒ‰¤ÝüŒ¾L` õ,@ëöÓö ÿ‚Yë¾ñ^•ñ£ö’Š¿Ònt¿#­Ç•r¼Ç=싘ËF~d‰ Øfn Ýê(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šk(u(s†àyô#‘@ ñ›ö†ðGÁËF·¾“ûK^‘wC§@ÃÌäpÒ·"$÷#'øTóÉŠ_¼oñwWþÒñUçú2ø}«µÃ7™sqi©Í—=YÙ.œóÿmO¹s_w—V¾_Ú¡x|ä&õ+¹£.zƒØŽ+÷ ˲èÒöØysÏ«{¯$º~7î|&y‰Ä¹rTVO3õ³ö‹Ëø5tÿó×W¹oü… ÿJû"¾IýŠbòþBÿó×P»oÕWúWÖÕùgÊù…ñ3ë2µþÍOÐ(¢ŠðNð¢Š(¢Š(¢Š(ç/Œþñg‡þ8X©Ö$iZèQ÷ô˧% uû<Ä7©ÐWÑhé*,‘°tp äzk7[Ñ´ïh÷º¯žËQ†Ky£?ÅŠU‡äz׌|Öu=3TøUâILº×f{Ûï\X8Ýg8ÿz?”úç“^´ÿ†RûTôáoO¹éó]ŽEîU·I~ðWäÏ{¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( Š( ™t?ø²á—÷~ ñµÃË¥±â; Qþi-=9þôC l¨M}5\oü¤|Dðÿ„õœ¬Wk˜åOõL‡tsF{20zô<\WÁÏêÚåÿ‚ükˆüaáG[]Az „#÷7qú¤ÉóvÃgÅ{ŸöŠ_X_t—ŸE/ÑùÙõ8é~î~Ï£Ûõ_ªÿ€{=Q^9ØQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEùoÿý¼¼Qû'E¢xáæ‡߉üOi5Òj7 µ­œ(þVR!:brpÌ0¥ƒ†Å~¤Wㆿ¾"=¼ám/ħI—ϳ:•”Ÿf”ã/œ±Ž+ƒÀô åßáìqû]þß>+_Š¿5[» ü†k‰–s·O´ù7 ÉÚGäŠþ¿fØŸà?ì·§£øGÞ"xö\kš€Yµ r0ÁaŒÿr  ŒnÜFkëuUE€*¨À€¥ Š( Š( Š( ÿÔýü¢Š(¢Š(ð[þ ?ÿóøÇñ{㿾 ÚÅ®G­[[ÅdÒ¬SÃ=²•Óy‘‘W€r>µùÅÿÌý³¿èA“ÿ`ÿâëû¢€?ŸøvgíÿB ŸøÿGü;3öÎÿ¡Oü ƒÿ‹¯ìŠþ>áÙŸ¶wý2àLü]|½ñ'à׎>xÆ÷À?!ƒF×´áOk,è]ñ¬±ä®GÌŽ§¯zþéëùÿ‚©ÿÉñøûþ¸èÿúl¶  ø]ûþÑß<*ž6øgá‘®h¯4 ˜®" e‹׿`xÈí^ÿÌý³¿èA“ÿ`ÿâë÷þ ÿ&wgÿa½Kÿi×ê?ðìÏÛ;þ„?ð&þ.øvgíÿB Ÿøÿ_Ø5ü€ZÁ1?l뫘íÏŒ>cß%Ô ‹žìwð+úQýŠÿg«ÏÙ‹ö}оj÷i}«Ç$÷º„±gÊûUÓîd<•E €÷ÆxÎ+êê(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šòž2Ô<%à™-ü=óø‹ÄG¥éHÝÝ¡ý„k—'§=k|.UªF”wfuj(EÉô8ÿÿÅËøÁ­üF—÷š/„|ÍGÏ*÷'úåD¬8e¸¯£«ðƒtÿ‡þ Ò|¦|Ðé,eñƒ$‡æ’CîîKs]…tf8ˆÔ©û¿†:/Eþ{¿6ÌðÔÜcïnõ~¿ð6 (¢¸€¢Š(çMGþ-ÏÇ»=X~ïEø‘²¸ì©«Y©01ì<è²€K šú.¼»ã'‚®ÍwE.Ç µ¶¶8¯æßVý”?iY5KÉ#øi¯2¼ÒEŒ¸ ±Áé@Ö/ü6×ìÿEwÃ_ø1‡ühÿ†Úý‘¿è®økÿ0ÿ%ðÉ¿´¿ý=ÿeÿ ?á“iú&zÿþËþýjÃm~ÈßôW|5ÿƒÆøm¯ÙþŠï†¿ðcø×òWÿ ›ûKÿÑ3×ÿð_ð ~É¿´¾ä™ëÿø/øPöꎲ"ȇr°ЃNªšz²X[#Œ2Ä€ƒØ…n€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùÇAÿ‹ñ»QñK~óAøz$Ó,;¤º¤à}²Qÿ\“}NEw¿¼oqà?ÞjZZyÚÕó%†—奾º;!»N\ŽáMj|1ðE¿Ã¿é^…üé­cÝs7S5̇|Òy;œ’3È«ÖÃ~ç*Ýeî¯O´þí>o±ÉWߨ¡ÑjÿOóù#½¢Š+É:Š( Š( ¾tð?ü[ÏŒž$øy'îô¯†×ô¡ÑVrB_B¾û¶Èpšú.¼ãö‘†¬>#h5Ÿ].©¯ -ª·çû¯IõÚz™T”¦èKi«|þËû÷òlåŦ’¨·Ž¿.¿ï4Vf‹«ØxƒG²×t©DÖzŒ1ÜBãø£•C)üi×™(´ìÎ”ïª (¢ÂŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾pø±#üGñ¦ðBÁ‰°`š¯ˆ]Oݱ…Á†Ø‘Þâ@22Qž†½¯Æ*Ò¼á}Oźܞ]–—M'«cî¢çø°ª;’+;øWVÓ4ßxµ6øŸÆs Fø°FÃöÃ<…†, A$W­—þêÅ=Ö‘ÿûukëc“ïµIußÓþÞ—=ª8Ò$X¢Pˆ€P0”ú(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>røº­ð÷Æ^øÛf Ù[•Ò5ཟrãÊ¿ëÞbzØé_E«+¨t!•†A‚ eëÚ&›â]ûÃÚÄB{F-æCÝ$R§‡ƒØó^9ð[Ô¡Ò5/†%”É®øq`îÜ‹"7YÜg‹×+ÏZõ§ûì2—Ú§£ÿ ÙüžŸ5Øã¹VÝ%ùÿÁZü™ï4QEy'`QEQEQEQEQEQEQEQEQEÆøÿÁGÄO ßøOYb¼_ÝÊŸë ™>hæŒöd`õèx&¸Ÿƒž7Õõ»ÿxÔˆüaáG[[ñÐ\ÆGînÓÕ&N}›< ^Ñ^ñú¾}añŸÁPµÏ #%í²pu(Ó@}^?õ‘úx'½lÕX¼,ÞúÅö—ùKgçgÐäÄ'ícÓ5ÿüÏ¢±<7â-#ź ‡‰tÅΟ©D³C î­ØŽÌ :‚<ŠÛ¯.pqn2VhꌓWAETŒ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šó¿Š¾9_‡~Ô¼K~}ê(†Ê2g¼œì‚0'.A s´ZС*“8-[²"¤Ôbå-‘æ°ÅÏøé%Éýç‡þ)‰;¤ÚÍÒüçÐýž.=UÎE}^kð“ÀïðûÀº~…vþ~§&ë­FrrÓ^Ü2w'¿ÌvƒýÐ+Ò«³3¯Tä¦ýØ«/–ïæîþf8X5inõåòZQ^qÒQEQE±E7¸itƯâ%§¬‘É[ßš§Ówú~?‘êÔQEy'XQEQEQEQE…âiÞ-ðî¥á]7Ùê–ò[Ê;…‘JäzÔÄf¼»à'ˆuGÁoáO>í{Á·£ßg«›n!”g’²E´†îs^Ý_:x‹þ-ÏÇMÅkû½âk£ßžŠº•¸-e#µ"nˆ}+ÖÀþö”ðï‰z­×Í_Õ¤rW÷eŸ'èÿàþ§ÑtQEy'XQEQEQEQEQEW‘·Ç¿ƒ+ñ=~ iÇÇ,vL Þ|Ÿ´` è|‘¿¼×7ûOü|ðÿìÓðWÄ?5ݒ˧Ååiö¬pnõ ²¶ð s‚ß3‘ÊÆ¬ßÃ_Íçìå¤|IðWüáŠ~.Nòx§Çsǯ]y¹¯öôW€ôfG Wfí˜ùhúâŠü%ý¾ÿiߊ_¾8iÿ°ŸìÕy%½Õüéc®^[HQ§šTß-©•9ŽÞÚ,½ÑS–Ã!Qƒ€~ªx·ö´ý™< ªË¡x¯â‡‡´ýFÝü¹m›Q…æ‰ÇU‘™û6+¼ðÆo„UÏÃOèþ(h”¼‰¦ßAu$jt‹(Éx£Ö¾Rø=ÿÜý•~ø ë~ °ñ¦§,;/µ]^žây|íl‹u„a”c,Í–>û ~Ã)ý™ÿi‹>8Ö4å´ð´é&›áiZæ+‰fÓî.üò\#3£F¤È±'èô»ÇŸþ|,‚Îçâg‹tŸ C¨3¥³ê·ÐY,ÍÄfgPÅA¦Ebx'ãçÀï‰ZÉðïÃψ‰µU‰§6šf§my8‰ þ\23m€'ñüü#mûø’çÄuµæ§-Þm¤Í4jÒÛÜÉw’4.Fä&¤ ´Ãƒ‘‘\Wü“á7ƒ¾~Ê6ÿuÛ[K _Åo¨]ê—!#’ 2ÚC q´í€ÌFBüÛl}ûâÿÚà'Ãívo øïâ7‡|;¬[ª<¶Z†«kkp‹"†BÑK"° ¤‘È9Ðø â×Âߊ‘ÞËðËÅúG‹M1­ÓiWÐ^ˆ »ŒbC ¶ÝÛ[nqœt¯Áïˆ_æÿ‚¨þÕ²k¿4ñ ü&ðJ&›⟳î56FÜþQ` ’8¯Ÿ* $p ¬g÷;áÁ?…ÿü%‚¾ø~ÛBÓbUó (<ë—Q6âcóÍ!îÎIì00(—Œ?ho€¿uÙ|1ãψÞðî±£Ég¨ê¶¶·².ä-²+ÊAŽG"¹økÏÙSþ‹„?ð{cÿÇkñŸþ ao᫯|*Ñ4-*øJïíõ n§Š%[›ˆe’Þ Häp8 ¡7·¸É¯Õ¿þÂ?²¾…áBÖ>ø{R¾Ó¬m­®.氉帚•Wr¹fv‰<’s@»á_Ú?ö|ñÖ¿ká_üKðÞ½­_öV:µ¥ÍÌÆ426È£‘¶¢³€ è+©ñçņ ³¹ø™âÝ'Âê élú­ôK3Fq™Ô1PF@é‘_™¿`‹ÿ„ðPOübð÷‡ ÑþÙiÒË ˜î"}š…å¼0Ï C¼Êˆ¾eÎÒÊ (ãé?ðV7ð·ìkâKŸiÖךœ·zu¶“4Ñ«Kos%ÜRHй˜"6Ò7FE}F¿µ×ì¨Ä(øÅàüŸ]zÀ~¦jö øÛÁž=Ó?¶ü ¯éþ"Ó·lûN›uä;‡8ó!f\ûf¿#?àœ¿±‡À¯þÉzŒ>,øLñ±â«›ëÑq{{ˆí–co ,œ2©X|Á´ÿ{×Ç–¿ìmÿRÐþü»¸´ð¾¿¨é67:wšÒªÚk)’ÞRä—XYüØËÊÄ‚Hô»X&ñ_…ü£Oâ/kz•l3-ÝýÄv¶ñÿ½$¬ª?\ÿÅO‰>ø=ðëÄ?¼e?ÙôYÉw9ÜûÉ€d‘Ê¢ìÀw¯Á_ÙÏá×?à¨ÿµ¿Ž´EÕÊ|.ðÑ·°Ñ ™ã¶iœ[(™ #Œ£ÜÌ1$…ù?i|9ûY~Ì~-Öcðï‡>*xnûRÖ(­ÓT·4Žp©.<Æ' L“T®¿âí|iŽÄ~÷Âÿ ¤Y¦î—:Ü‹ûµô?eC¸÷pká_ø(oì“û/x[öZñˆü1à+Â^"ÐR9t«.Ù-f–XÛ|‘Lc ç+B²dÜWÈÖÁ!þ'_ü@ý—®ôe–]GºåÕ“NIi®!š8®c–f$–qæ4`ÿr5ï^†³£T¶­5Û£v‹×Èç«v£}ÿ¡úŸ^Kñã×ÁO„“%¯ÄßhÞ¹•w¥½ýô0Ü:yafó{…5ñOü£öϾý—þÙxOáüÈ¿|d²-œ¤+:Ñ0²]” ‚ä†Knn|²§Î¿cOø'‚4ï Añsö¥Ò?á9øâÀ/î­õ¼ÝÅcçá%Ž\‰®fiwmo‘@Ágó΃ïÿþÔ³ŸÄmB-Á¼?«ê3²¬Vê0}¦Fc€a#xáMzω¼Qá¿èW~'ñ~«k¢hö âòöd··…KId*ª 2OR~BxÛþ ½›ûu|7ø¯ðÂöº?Ã; ­µ]b8fŠ(mu+’XÄ6¥ƒì•’¶5*¬Xð3ÖŸˆðŠ/õëÙ[ê>µ²žâþÞî$š -àC+‡IV.yæZwíUû1ë…®‘¤üYð¥íõô©C­ÙI,²ÊÁQRY™ˆI8éÞ5øà_†º0ñÄ?iþÒšU€]êwQYÀepJ§™3*î!I985üðÿÁ> øsÇñWÇmcK†}jóT:^›s4jòÛÛZ­3@Äe<眣•äùxé×÷6€ (¢€ (¢€ (¢€ (¢€ (¢€>qø°§á׎4v£fž èúø>Ãpãɸoúá1=J¶:WÑ †”äAâMñF…¨xsY‹Î±Ô ’ÞeõI©ÁìFrcÍyÀ}{S&¡ðÛÄòùšÿg|ÌzÏkÖwuÄ‘`sÉ*Ië^µOßa”þÕ=ø^Ïäôù£Ž>å[t—ç×ïZü™îÔQEy'`QEQEQEQEQEQEQEQEQEW€ücðæ¯£ÞØ|gðLmwÃ(ÉylœGJcºh«§úÈý<Š÷ê+«Štj)¥~ëº{§êeZ’œyY‡á¯i.Ð,š¢Šà>$|TøsðƒÃ’x·âˆì¼5¤£lÞ̱‡|#OÍ# ˜ààPE~wÛÁT¿bPi¿ðOdà\ɤê =¹ò ©P=H¯¼ü5âxÏA²ñG„µ;mgGÔ£Û]ÚJ³A2ŒŽ„©¸=x  Ú+Ë>0ühøsðÁÏãïŠZ”šN…ñÛµÄv·{d”1]Ém®Ê~b»Gsȯ’¿áé¿°ÇýGÿÁ6­ÿÈ”úE|Ûðö¿ýšþ=êGDøSãÛgTÁe²ušÎíÕA,c·»Žd X¢&¾’ Šà>$|TøsðƒÃ’x·âˆì¼5¤£lÞ̱‡|#OÍ# ˜ààWÅÖßðU/؆çToü'SÅ82i:‚ÀOn|‚ãêTR(ôBŠÂð׉ü9ã=ËÅÔíµRŒKmwi*ÍÈz2:¤vàõâ´uGOÒ,.5]Zê+++HÚY§Ö8¢YÝØ…UP2I @(¯ÏÿÁQÿb¿ j²i ãy5iam&Ÿauq?ìÌ#ãÝ C^×ðCöÇýœh­RMá/‹ãÕµˆa{‰,d·¸µ¹X…gÙqnPYrT‘È §h¯ øåûHüýœ4½7YøÅ­É¡ÙjòÉ ´©ewx¬ñ€X7Ùb—g 1»íœù«þû ÑD“ÿÚ¯ÿ"PèEùð?੟°Ó«ñBO VÿäJýV¡ÆpÃ<‚>ÇšuQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šå#ñ<ï«jŒFžçæsÐD›P/@AÇZö:ô3J‘çT`ýØiêú¿›ÛÊÇ>.ÎrÞZÿ’û¿¢Š+Ì:‚Š( Š( Š( Š( ¼ãâׂÄj¾·o*ýOc(;LW2 ÛçP ô&½ŠÛ^T§Ý;¢*SR‹Œ¶gü(ñ°ø…à'Äò/•y4~UäD`Åw òçB½FNíƒ^‰_9ø_þ-×ÇoÁÏû½Ç‘¶µ§ŽŠº„ -ôKêλe>‚¾Œ®¬Î„aWšŸÃ-W£éòw_#,,Û¥ºÑÿ^{…Q^yÐQEQEQEQ_ÁEÿjeýš~ÝEáÛ¯+Ç2iº*¡ýì;—€uýÂ0Úç«GÁ •¼o)ÿ‚þܶŸ íOÛ> |—í:»šßRÕƒm18ei ÿË(çe?8¯ýµßþ¿ø*÷Á=Sî%ܾbzªKlÇèšú3àGü_ཧ¯ÝüY“Z>6¿´ŽãWû¢ö±Gq6dò çÉ å–$î`[Œâ¿3´øsðïÅoÀko éwºœŠN-œ/1ŽÜWà§üÇÀ×_þ0üNý£<_›ÝRÉE¼W ï¾Ö%’{¹GûacŸIO­~Ä~Ùºf¡«þÉß,t¼›ƒáMÀQ’Ë»Hêrʤ­~{ÿÁî´·ø ã»(dS©EâS$ÈܰIen!b:€Y%÷Áô4ûAE|ÛûP~ÔŸ ÿdÿÁ㿈bâïí×IigaeåµÝÔ‡—1¬Ž‹¶4Ë;p>ó(?@hÚƒjúEŽªÖòY›Ø"œÁ6‘6ÇÚHܹÁÁ#=èðçþ sãÇ¿ ¾Y¹‘õ;ëÍ^h—“þ‰Û[ñßq¸”÷M|wñÃâçÇ]"o„ÿ±oí¢Íð{ᦙ‰gªAa‹c!|±ø·¤[ùš¿Ã«Ÿ2b£,úeéX§r|¹DRs¨÷4ú{ðçáß‚¾x/KðÃÍ.@Ò"[[Â8©fc’îä–wbY˜’I'5ÛWÂðN?CãÏì¹á»íJçÏñ…û SÜrí%š¨†VÏ$ËncbÇ«ï}Óqq¥¼·WR,P¬îìpªª2I=€hùžý±ußü_ÿ‚¡éZßÿÂoªx]* ='íÚ¥çöTÚ·0´Ò‘®ö•\±ì@É Wªþ×´Oí×ãü<øâïÉðVˆÅ­µµÆ™©‹«ËÇG ¼µ“jGLŽèoºIÛrà—pMñ·öÙøµûC_¡’cÔ.â,?ÕÜkw„Â=±L z}+ö?ö¥ñÁŸ„_ÿá~üZÐìõ›Ÿ¸Ñ<ô®†¡qµcŠÑÜ’JÊ ²ƒµT¹S@I[[Çim¬Y) *.æ,ØQ–9$ûžM~Ánü~mü'ðÏá]´›Ž£}y¬\F#ì‘­¼öĸÿt×ìÀωÒ|høEá_ŠÒhÒhÅI}”²‰ž(¥$ÆLT6ôôp¿ ÿk'_Úþ Áà…j~Ó§ø^}ÆxÇ*Ñ[çV¼™òädcÛnJúWáïüƒàŸÞøeð»Á¾)ñ—ˆ|7¢Øé°Y[éos]AFÆIX³(y%’7$ž×û~Éÿ~+þÓwÿ¶×íI¤Éá뇹’÷KÒnÐÇr÷ ‚Ð9ó!‚Î «ÈŒU0 oÜê(ñkþ Uñ:ëÃß¼ð²Æo,øÃT–îäËÛiH¤#²fž7ú ÇC_t~Áÿàø/û)ø¾XKûëÕ¯Î0Íw©´¸oSºÅŸDùQÿÅÒ5×>kİ’ÛW¶•&G¶s“ز°Àï´úWìw> ­Â¿Û|7‘'Õ|m­ž‚c`T%ÌJÂäþa;÷tnx5Ñ…ÃJµHÓŽïú¿¢Ý™Õ¨¡'Ðøgööñ½®»ðWâ¿îŸ‡ü-dÞÑ“?-Þ¯ªÈ–ד¯fÄ죱Áe9ÍSÿ‚1xFëDý˜µßݦÑâ?ÜÉÇÞ‚Ö ?õÕd…|uÿñü,O|:ý€gå}rãÃ÷Hu!Ì·ÄÊUUÙr^Ynü¨\ç6ÇïÀO„šWÀŸƒ~øI£8š XGnó*íÜÉq6ÞÞlÌïŽÛ±[æ8˜Ô©jt^‹¯«wo͙᩸ÆòÝêÿ¯-ÀÂ?kŸø+šèÂý·ÃÞ՚Ѣ?4kiá¨ÞI#oTšî7 ëæŸ­JµüãÿÁ?m£ð÷üó⾋âYj`ø¦wü¦[•Ô£vÚ\IJ8ÇðóÒ¿ ¿xãà <¬øûÆw©§hš ¬—ws¹ácŒg1ªŽYˆQÉÀtm|Gÿøÿ ëö6ø‘¨Å'—s«Ù&Î NU¶¤/#}¯dý›þ>øö˜øWcñoºMö¥j3ÜÁZ‚ij8¶ÄîO"í.¬Íœƒ_—?ð[oˆ#Nøað÷á|âMwU¸Õ&Pyò´è|¥ ìÍu‘êWÚ€<_þ ߪþÚß þ=ÿÁo¶>5ðï‹õ+JVë[²°–Q[3“5Â8Dxi*3¸‘‘‚_ÿ ý¢¿lKŸ€Íð÷ã¯Â=?áæ‡âëëx–îßZµÔ%¬Ü]yB+y¤`»£V,@g$Wí¯ì×àð·ö~øyàÊŸEÐì!¸ÇúI…Zàãý©YÏã_ŠðWýfóâ_í ðö|Ñ¥ÍÇ’¯µyç\»KXÃï(·G÷ Õø'×ÃÿøVÿ±ßÃ-Hü»‹ý0jÓda‹ê®×ƒwº¤ª¾À_eVn¤ØèEŽ…¥ÇäÙéÐEm‹*ðPiPEPEPEPEPEP_8üVáÏŽô6ÃfœÛtm>Çpãȸn߸”ŒžIVÀ⾎¬_ègŠ´CÃzÌ^uާ–ó/}’. ±Aìy®Ü¿©UNzÅèדßü׌1œãe¿OSdÀrzZð¯þ¦ÚÿßËæxƒÀÓ:áYí±›K€8–,ry%I=kÝk‡&!8¿k›ù¯øæ{õ‡á¯é.Ð,mƒ9 ×£|;ý­hïØ¯öJðn‹?ìó-·‡m-·ÁâÍLKm<šœ¯t’Íooø„†\*¼ ô\ç½_öUý•|IûW|AÐÿnÏÚ[ÅÚwоÛäÞhÚ.“¹­-šÕÏ•ÆõÄkk 9·]ÄÉ––F%À~Åø›Æ:W< ©xóÆÓ.§èv_ê÷–(àŒÉ.?½Œäœɯç¯övð¯‰ÿਟµ.½ñ{ãa˜ü5ð;#[èâF*ÌÇì–+ƒÑÖ6’êEÃ9|¡×oߟðWˆ—^ ý‘î5šã\»b9"fò­À=v‹x£ tË6:ÐÓß>|,ñßÁ=sá-ÿ†ôûoɧO ¬ÛEvN"")mÕTž& ÊÊ«òOþñ#[¾Ñ~$ü)Ô.žm3J’ÃU°‰¹½×›ÖÓØ1Ž"¦wd“_®´÷Ä o…Ÿ³ÇÄO\:£i:ëA¸à5Ì‘­Ó?íÌè¿~IÿÁ<umᯊ?.‹}BïOÒ­Û´Igœß‹ˆ¾”úyûnxºÇÁ²GÅsPUhäðý튇©—QO±D<ÞL¼w¯Ïßø#oÂ?gÿøÿÄZU½äÞ(ÖÚÞq JÓO…UHÞYf{Waÿ™øÿßìÑ£xÞ]·0× I?zÖÂ7¸þyæ³íñÛàŸì×àï…žý˜|]¯^éö/,wÓ#ÚØ\Ë{+Ü™Ãù.LlÒä ‚W#­|½ÿ2ð—…¾~Øß¼SðGO‡Ã~!½µ²Õe·ÓZÆ×‰}$qL0´Û ¾ ·$ÌOôã/hð–³ãÜ‹=@³žúîcÎÈ-ÐÈäç€9'Í~5|ýˆh?´Œµ¯í¦–ÚU͕Ė$§ÍµÚm–6–8m¡Àa‘¤wÌ,[Ý?à¯.¼û#Üx~ÂSÞ4Õì´§ÛÃ}ÞIÏ¡û:¡õ Ž„ÐÀ³·…|OÿDý©uï‹ß Çá¯ÙßG0Vf?d±\ޱ´—R.Èå»r~,|øYã¿‚zçÂ[ÿ éöÞ“NžX!¶Š(ìœDDRÛª¨)ð×Âü:Ñg–ÛJñ…ý亓FJ‰“MHLP9g›ÌÚz´j†¸Ÿø"î­¼5ñGâEÂo¨]éúU»c‚Ö‰,ó€{ñqÒ¿L?lÙOõ¿Âi<ª]ÿek:|ßmÒ5¥ÅµÐR¤H€‚ÑH¤«ŒñÃTP¿ÙGàŸÁü!¥ü>Òl.,µ]"Òææý"IR’æ’Iå‘g1ÈRvªáTý˜a[/ÙÏöŽø›ñ›M»±/‹H4m:Ò7FÓí®næh˜(dE@™F8é_“?ÿi?ÚOþ £ñ"´©õ/<¥ÖØ·š±[Èçuæ“qÀhÉË4'¶AX¤,kú_Ð5ÝÅ:âo]¥þ•«ÛCwiqÌs[΂HäSÝYXìhóÇþ Åâë þž'Ó.•Zo_éšm¾ðïÊÞ1÷Û>=:Ô?ðKï„z…ÿc jzΕm5ÿ‰æ½Õæi¡Gm³LÑBw0'£oƾ9ÿ‚ÚxÚâðü+ø=¥“4÷s^jÓ@¼³?ÉkiÜ’ÓŠgí¤ÿÁHÿgïÙ"}TÖü§xÂÚUžr|=öƒª Ø®^ê\êâ*ü–u ³?jØ{Dý¥>'|$øày4M;O𖣿_edÔtõ¸·•aˆÁ£º™FöP<ξŸ¤õò/ìðïþ‡ì‹ðÇÃREå\ÜiQêwŒ0›Tf½`ÞëçöÛŽÕõÕQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùçÅ¿µ§ìÉà]V] Å<=§ê6ïåËlÚŒ/4N:¬ˆŒÌ‡Ù±@CQ^kàŒßþ*«Ÿ†ž3ÑüPÑ)yM¾‚êHÔ2éQ’>ðG­[ñçņ ³¹ø™âÝ'Âê élú­ôK3Fq™Ô1PF@é‘@ýó¿ü5çì©ÿE‹Âø=±ÿã´Ã^~ÊŸôX¼!ÿƒÛþ;@DQX>#ñW†| ]x«Åšµ¦¢Ø ’{ÛÉÒÞÚ$b3Ë! ’$õ WŠÿÃ^~ÊŸôX¼!ÿƒÛþ;@DQ^oàŸŒŸ¾%O%§Ã¯h~)žÞñéz•µë¢ú²Á#>¢½"€ *9¦†Þ¸¸uŠ(”³»ªª£$’x¦¾u¼ý°e]?V:çÅ¿ Åx¬Q”êÖ¥Q‡<ö)‚†(èêùÇÇÿñr~+è? ¢ýæáÝšî·ŽUN,­›ýçÌŒ§ª€kØ5?x_NðUÏÄ$Ô!½Ð ´kÕº¶‘fŠhBîS¡*ûø ‚rHÅyïÀjv>¹ñ§‰£Ùâ/ÜVô±$ƒýÜg±E€èIëeÿº§Š¢Š+É:Š( Š( Š( ¿¿hø%ÇÄÚ;âeßÄ¿|}’[¢Å,-LJ@O´W/„ԙ嶆vË7ÌM~ÄÑ@Wí ÅÞðN‘ øóÄŸð˜köPùwz·Øã°ûc†$Hm¢gHÎÜŽqžõùYûEÁ-|ûK|P¿ø›ãߎÌóɺ aá´Ùab²<ÚÆRþ0Â=ç.T3’Y¹5ûEyÇÂßø»Â? ô|HñGü'šÕŒÞ­-šYÕ.Û Û£È ˆŠ£Ì\‚Ç–"¿8OüOÅ¿ þ!ë?dŒW¿ £Ö€éSX¦£iÅ‚ƒ$Y“嬑HÉ’òkõ–ŠüsÔ?à•þ2ø¥ñOø‰ûIütÔ<{sbð‘nºjÛ§•‡0¡iäŽ8ß2Ç ç$õ9¯×?Yë:‡‡õ;j+¤j×6³Egzð ”µ¸t+ÍdˆÜ†(YCcÍlÑ@ŒÞÿ‚WüVð·Æ¤ý¡4ßÚÛÇBú}Aïäð¼r&ºWI÷FÚk¤Œ›Bà)ÂÓ¯ u¿Œ_|Uð¯AñxbãÅ6O§É¨Idº€ŠÚ|%‹v’ZHK mà¡mã•ëôPåWìÿæø‰û&xøx£Âd¾ÐïÚ1¬hߨ :ŒQn(¦G½›Êu,vÈ©¸GF"¾áý¢þxóã/ÂýCá×€|r~O¬n‚÷P]=u$°–)#šÝ¦ƒËi7/ïU·(–Ü=ÒŠüpøAÿÁøÝðRßáí/qá”Öš¼þ…¼ãnG¸Ë~çåÞØÆ:šƒâÏüëãÇAfŸ¿iýCİØ1{xn|>; XSQXÃÆí¹ÇÅ~ËQ@-Ÿƒ§ðßÃH>øùtY´½!t½.íà)hÐ[ùò´H#!XÆYCcnFs_”>ÿ‚XüXðŸÆ¥ý¡t¯Ú·ŽþÛs~×óxZ9wÏv¯û¢m@ǵÒF]¡@ p¸ÀÇìÅ ,pÆ“IæÈª>6î`983éRÑExWíû<|8ý¦þÜü6ø—bÒ-͵ͻˆîlî£VTž`Ê+0!”«) Šü£øGû þÑúþŒÖžý£µ 'Á—z>…rúg™uö•™­í!¡›(…%R@ m-~¬üs×õI´ý7áo…e1kþ7•­EäÚØ¨ÍÝÉÇM±åW¡%¸äW¯øw@Òü-¡XxoEˆAc¦ÂBž‰ÀÉîOR{žkÓTÕ,?´öÿÍüÞŸ'är¹9Ôå[-ýz}ÛýÇÈß²·ì)ðgöS׆’x¾ý.µÍKkÜ”|ŽPØŒ2íünÀ }¥EæGç/íÿóо/üX³ý >øÖ÷áwÄ‹gŽIu8~ÓÃÔŽÑ !d ì«§ ’OŒ|Dÿ‚t~Òßí,ôo¿´µÖ¹¢ÙÈ$6Ú,pBì£ Å"ž(ËÑÞ7#'M~ÀÑ@Mð/áðá/†þøbyn´ï Û´1Í0Q,­$,’8@‘ÙŽzüåý§ÿà˜þ<ý©¾(^|Eñ¯Ç æA¥éëáÕhôë5äŽÜ:ßÇæ2îùåe ç’Â×:(€ø_á¿xGÀ:G†þ ø§þOXFÑÜëbM?í_;ØÛFò*Œª1ÜT·à~Sx×þ añ[Çÿãç‰?hv›Æ°ÞÚßÁx¾VÞk†ÛˈjZ¬^Zí]¸8Ëd’OìÅáË-gNðö—§ø‹RÆ«kkW—ËÛ «„@²Î!RË‘Ámˆ\àŒÖÍPEPEPEPEPEPEPÎ?øqãíãE·É¦Ë³F×ñ÷E¤ïþrÝ¿s)'k8¯£Adr bø—ÃÚg‹¿~ÿyî”QEy'XQEQEQEQEQEQEQEQEQEQEó.ÿ?âWü"ò~ïÁ8¸i4Ö韪¿2Zú,wz1Ð6T¦¾š®;ÇÞ Ò>!øRÿÂzÐ" Äù%_õJ¿4r¡ìÈÀù ®àçµ}fÎÿÀþ6!kÞ%×<3¥ßHîMæ+[‹¨6:5´Ï,3+A&üºmŠ€NÜ‚ø‘ÿ‘µ¸ø±ûSübý 5dßp`šC»’—ýë\ú…·‘~„×ôC_Ÿß³·ü§á_ìÁã„ñÇÃ/øÅYÆÛ½>çP´:}ò…uEº†(ZAréó‚­ìH? 4Wó¯ÿcÕ›âïígðöyÓn7y1ÚÛ¸SþªïĉÒ?¼"Š&ú0¯è¢¿-5ïø$ÏÁOøÞ_‰Z÷Ĉ¾*–æ;éɪ؛±q )_ìýÊÑí]˜#hP ûöŸøcgñ öaøƒðÎÂÑX\ø~ê;d ‹X¼Û@û2Ç>•ù—ÿKø¥ðÇÇß nnA¸ÐµHu[x™¾o#P‹Ê}ƒ®Õ’ß-ŽqýêýŸð®‚þðÖ—áÉ5;Íi´Ûh­Íö£"ÍyrcP¦YäUEibA'€ùûðßþ ‡ðWáÅ~+ü9ñ§ŒôN+¿´5µ¦£i œ°™„Íg*%’»Ú¶´fL•æÜPÁh¼#«k_³o†üO§C$öþñ-y°±Auo4K+ú/—>²Þ¾Ïý…1m:0êI8=ˆ#‚ }%âÏ øoÇ^Ô|ã 6_EÕ¡k{»K„Ñ?U`0G à‚¿6‡ü£á>‡>£ÃO‰>:ð>‹«ÈÏu¥ézº¥£‚FÌ£Íi$â€>[ÿ‚£þÑWuýö)øŸkz†£ šÀ°q k¨‰Xd¹¿}9$Ê&â ¸_ÕÿÙGà&Ÿû5|ðÇÂ{YâóO„ϨÜ/Iõ “æ\8ࡎÄÏ;Aæ¹ÏÙÏö-øû/¬·Ÿ ´G›]¹O.}gQ“íZŒˆz¨“j¤Jßı"ãp8úº€?ø+¬ßk?„³Î›q»ÉŽÖÝŸõW~ ¼HJ‘ýï*(›è¿¡û+;]:Î >Ê1 ½´kH½mUÀ Wåî½ÿ™ø)âËñ+^ø‘ñ÷ÅRÜÇxu95[v.! Å"ËýŸ¹Z=«³m à_¥žÐ_ÂþÒü9&§{­>™m¹¾ÔdY¯.LjË<ˆ¨­#ã,B¨$ðJ߯Çßø-„umkömð߉ôèdžßþ"…¯6V(.­æ‰e@%òãÖ@;×ìsÞ,ðŸ†üuá½GÁþ0ÓaÕô]Z·»´¸@ñMõVór €hæßØSÅzŒ?d?…7þ'ŠÇA²Ó§rcºÓãÓ£¡„‘“ƒØ‚8 ׿GüöЏøË¯è±OÀ|ø›[Ô5dÖƒ‰]DH‚à íÌmûéÉ FQ7UÂýH?à• ô9õ>|Iñ×ô]^F{­/KÕÕ-0¸0î`mkHx'ôÇìçûüý—Ö[φÚ#ͮܧ—>³¨Éö­FD=TIµR%oâX‘q¸ l~ÌôOÙ_öyÐ~KpŒú%¬—šµàû²ÞK™®¥Îا*™òÕAäWûþغ'íàÅ6ZøbÿÃú™²—N7¢ö_³¼I$,Â(v‰I‘í81·Ì{}_âŸiž1ðƯáh9Óõ»;‹‘˜ä0ÜÆÑIµ× ­µŽr"¿1,à’ü!­Ëâ?„Ÿ˜¤ûðÈ!Raox³°ÿ»^ð—þ Ãðá¿Žáø§â‹Ýkâ_Œ-¤I¡Ô$ŸÆV?¾ Ûk÷2K4º„Z½’]É$ä™]§[!i Ķ['9Í_ñüwàï‹­‡Šþ)üHÖ­Uƒ¯uË[˜Ã„,¶,3ïŠö_Øsö§ñOíeáßxâûÃöº†4môÍÉßçËq‰OÚ71Më‘g` ’F8÷-xoìíû>øödøcið§áì·—:]¬÷&}Aã’êin_{4 p¡ÀÂ.|ªÉÉ>å@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šâ¾$øÎÓáÏÿ|A¿­¼3¥Þêr)8 ¶p¼Ä~;q@‹_·ßí;ñKã7Ç ?öýš¯$·º¿,uËËi 4óJ›åµ2§1ÛÛE—º*rØd#j0¸þÁ7?e_…þ‡Âz߃,8Ö4å´ð´é&›áiZæ+‰fÓî.üò\#3£F¤È±'ëÐÿà¬oáoØ×Ä—> Ó­¯59nôëm&i£V–ÞæK¸¤‘¡r7!0E m¤nŒŠý*¯Âoø-×^/ |2øShåßR¾¼Ö'z²F¶ð;î7þé Gÿ‚mþÇŸüOû(xwÆÿü¤ø“Zñ5ÕõêϨÚG<Él³xP3‚B‡xŸ=ê·ígÿÑðÿŒ¾+ü%ÖþxOÑ|?¢"ñrÚ<6¥‚Ïnë2Àî»ÜFgF¥›åµyoÄŸ·ìÑû%ØøE>[øCðÆi¢Éâ3ªÛê3[†DµK´™Ši%`Á›r«°È5úgûèž'Ñ¿d¿‡xÏS¼Õõ­cNþÕ¸¹¾žK‰ÜjR5Ô!žRÍ„†DP3ÀôWÄøEÀÚõÇŽl­õZÙOqouM–ð!•ÃÇ *ÀÏ#µ9_ðHïÙÇáÏÆ¿|Gñ‡Äÿ YxƒEÑmììí-o¢À·’I+2ÏÍB'œ?¹¯ØŸø(ÇÄøW_±·ÄF)<»^É4hFp\êr­´€}!yè ~UÿÁ:¿l€ÿ³À+ï ø¯O×õkz½Î£,Zf˜×ãò↎FtFÀŒœn,hþ ‡û<|4ý–5^Æ ïˆ¯ÓI´$r–– $©ô–YpG¬B¿hüGâ 'Â~Ô¼Q¯N-tÝ&Ú[»™OD†.íï€ãÝ68_ðOy|ioû@øÇö)Õµ‡¾ð>•«]ß\C)8gÐnš'Ž$É —LQ¥QýÀGCŸÝßÚWã׆¿fŸƒZÿů ¸]21 py{1Û yÆæåˆjlµøYÿ˜†ÿâoí•ñ#ãÄ>\ÙÚ•äö\ê÷Ѻ&Üóí]÷ü›â­®ø×á—À™I‰õ‰mÐäÍsy+YÚ z¨Ž`?ß­ªW”¡oeÇëÉi7.¬Ÿö7ýšüSûyø¯Pý­¿kÛ«{Ak™-ôM%¢´¸ò™„H1ÙÀÿ"¢d9‘ŽÖó>€ý½?à›Þø…ðóE¿ý—>éš?Œ´»èã– o¦Cs§¼n$óC´q4ˆâ2®~r7 œñúðƒáÆ‘ðƒáo…~hHÏÃ:u½’1æèú¤>´Õá—P³‚ŽÖA¤ié5âɸšâ&À1rO$šþ™5-FÏHÓ®µmFQ ¥”O<Ò‰jY˜û ¯å—öñ7í+â/Ú+âOíðC᥿Ä}NïíoŽóQ·Ó’ÎMrìÝ,Š÷ǽȂDÂîÀ'8ÈÈõ)‹£Ã£¯‡¡°‚=) û0´X[ˆ6ìò„@lÙ·åÛŒcŒb¿™ïØ{áÃÏŽ_ðQë« ÙÜxÂ׺î±g`УXí{ÓŒ~Q6¨—ÌEÆÐcWèÅ/ÚŸþ !à‡>#ñ‡‹gÍ'ÃÚF—c4·ˆñŒÆÕv•ˆã¸gvRA  –8W”ÿÁ¼l¾üGø£qϬêvºL.ö™äÛìÍt¹÷_jýÇÀ´Q@Q@Q@Q@|íñÂ9<ªøgãU’œxjàZjG/¥_0ŽR@å¼—*ê>¦¾‰¬­wEÓüG¢ßè´~u–¥–ó'¬r©V\+³‰Tª©É]l×tôkî1ÄRçƒK~ž½ (äŽXÖX˜:8 ¬AAô§×„|Öµákχþ “~·à[–Ò§'ƒ,3i0vÉÜÉÚM{½F3 èÕ•6ïn½×GóZŽ^x)Q\Æ¡EPEPEPEPEPEPEPEPEPEPP]\ÛÙ[Kyw"ÃÒHîp¨Š2ÌIè5=|ññ®úóÅÚ–ð;B•£¸ñ6n5Yc?5¶~øç±¿t½Ì uàp¾Ú¢…ì·o²Z·÷׫É/êäm®è¶ÿÑ®[°òe 3rv°Q_GÖ‰¼;¦x·ÃÚ†5˜üÛ-N·•{íqŒFAì@5Û—âU*©ÏX½£ßü׌1œ£e¾ëÔÝ‘ExoÀjw½ðŠ$ßâ/Ïý™tǬÐ(Í­À²ÅŽO$‚M{•gŒÃ:5eMôüWGóZ—FªœTQEÌhQEQEQEQEQEQEQEQEQEà_ü9«é7–|›^ðÊ2ÝÛ'QÒØîžÜúºs$}pÀàE{íÕƒÅ:5Ò¿uÝ=Óõ2­IN<¬Ãðψôè&Ð':~¥Í Žê݈ìÊrukr¾eÒ?âÇüKÿ„fOÝx#Ç7 &œÇˆôýYù’ÛÑc¸ûÑŽ² u5ôÕiªrNð–©ùv~kgçäN«’´·[ÿ^aEW ¸QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯™ÿlÝ3PÕÿdï‹–:^MÁðΦà(ÉeŠÝ¤u¹eRÖ¾˜¨åŠ)âx'E’9«+U”ŒAà‚:Šücÿ‚%ÝioðÇvPȧR‹Ä¦I¹`’ÊÜBÄu²Jïƒèkô;ö ý©>þÉþƒÇÅÅßÛ®’ÒÎÂËËk»©.cYli–v,à}æP~,?ðM?ü'ø‡¬|@ý>1^ü.ZO¥Mbš¦7 ’dBO–²E#&HÉ®cPÿ‚WøËâ—Ä?â'í'ñÓPñíÍ‹ÂEºé«nžTNÂ…§’8ã||Ë+œ“Ôæ€?^´mAµ}"ÇUky,ÍìN`›HŒŠcí$n\àà‘žõüñþÖ¤~ПðUßü(ý+OðÄú-…Äc•h­ó«^{gÊ‘‘m¸=+úñž³¨xS°ð梺F­sk4Vw¯¹K[‡B±LÐA(Èb…”61‘œ×㾑ÿªø¹ |Xºøç£þÒÛxêöâêæ]Pxb6”Ëx¬³0VÔ .åv\)€(õËǾðG‹|ªh?,lõ/ Í™ ¨Ö†(›t¡þ]¨P>Oí_6þÈ_µ…?i­Åsü>ð̺„ü¨®‹¦Ü3©Šö“(ÑĨ¾HX¼³°“€àgŠùâìûRüRðÝׄlÛvD‘§ß9Ûž3€ñü¢ÿìÿ²ï†,R.K¿Yæ0Ø2G…ñn;…m„úWè—ì×àð»öøyà•6‹¡XCp1ô“ µÁÇûR³ƾ8ý²?`~Ø2µÕuŸŒG@ðÆŽ?âW¢.‚·+jòGÏ#Ü ØZg‘Óp,ƒb«Ü·Þ_ <7ãŸøMð÷Ä/õû0ë>¬¶ ¦ý¡w/6Ñ¼Š¥S Hc¸‚ÜgЫó«þ ­¤j:¯ìKãI4ðXX\éw3*Œ–…o¡VéÙK'°×è­bx“ÚŒ<=©øSÄÖqêF±m-Ý´£)4¡I½™IPçÇüwPÒo?bo ÛéÒ+ÜXßjÐÞ •œÞË*†ÇCäÉÁìAéŠò?ø+—í)kðçá?4¼x‹Ç+ºå#ož5 X@•ÁUõÚݳVtïø'ÄÏÙîïÄšÏìÇûA_ü<𮣾êëN¿ÓbÔb#BYüÇ!dQ€þJÈ]ˆÉ±û&þÀžÔu«ÚoöƒÖ¯þ&xÛTœÞØÉ« °G-®Ò“j†ZF®ÔÈuÐŒãY-Ÿ7{Ÿ‘•F›P}O@ÿ‚\~ͧÀÙüëÞ/´k/|@š=Jî ¤¶Öˆ…líäáYå`pTÊTŒ©¯ÏOø(¥´z/ü¯á&·âiz,ãÂó´¯ò¢[CªH³e.ÖcèÍGòíwûü7ý¯¼)c¤xºâ}\Ñ ­¥êÖª¯-¹›o˜’FØÄûT²eN@*ËÎy O¯+å¿‚ÿµ¯Ã_|ð¿áíµåàøzÑEy«b#§ÜK#´{mÝdgoš9b¡X!*HÁ?j°¿í›­øAþêßµmì¾òE®Œܶø RYÖåg`T`†· ƒÁ5ôÏìeûøSö8ðÞ¿¤hZõLjïüK=¼×—w¥¸ÅªºÆ‰ƒ#ž]Ž[­hþßGÃOÙâwˆ_*âïJ}.ÍÕl_u㦠í_(ÿÁ<<5û/j>4ž<\xÃ\º™zÚÉÙá*Mù×¼þÚ±ß?kÛ]/Ã|U> ð}‡—<ºJhË|noã2p÷k·}¢7 ±`¨ ¶I#omû~Í3ý•¾Üü2Öþ"Âs A “Iƒû%4Ó§ù²K-ÊïYçy„ÒHn#a ÀÏðWoˆ#Áß²ï†â—eÇ5k 0~cLod?îÿ£…o÷±Þ½cþ ±àðÿö3øyk${.µËyµ™Ž1¿ûBgš#ÿ~ cð¯3ý²?àŸþ<ý°ëôÞ/ó_$rS÷*¸tz¯×üþlôê(¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠËÖõ;ú=L-ìtø^y¤=8Ô³Ètï^+ð7FÔuHu_‹þ'„ŬøÙÖhb½k¦F1iô%>vÆ2XddVwÅGo‰^8Ñþ Y1m5jÞ!e< 8œ-Iõž@ Q‘Å}ˆ‘¢Ç…U ëÏ÷eµSW勿õôK¹Æ¿yRý#ùÿÀÛæÇQEäEPEPEPEPEPEPEeë–z†£¢ê~“~ÚUõÕ¼±[Þ$i+[JèU&È 9ˆ`¬0qƒÅ0ZÇüCö»ø9ûF_ø;ÆŸ[ÅÞðgŠeÓui:t ecxb˜°xÚHшÚùSМd€R4VDÒnhO.…~ þж-kyYBy©˜æ@ÙVÆCx=ø¯Ä/ÙÇâWíµñ[öÇñ§À½GãT·^øc}tu;åÑ4´{Ø¬î„ n[)ç9 C\©$ ýÔ¢¸¿ˆš'Š'ǤOây¯ÛM±”jn.a³0´>L[íÚ-²,{· Ï ÔúcE~Á3~7þ×_µ/µï|Hø©$ÞðIµ:jéZr6£-êͲ34Vñ¼I•¹ŠÄ•&¾ñý¾ÿi‰¿fÙïUñ^rx»[uÓ4=ʲº˜÷2°‚ Ò|ÊT¸Ea† ¶(¯Îoø&Ç?ŠŸ´ìù«øÛâö¸|A­[xŽîÆ;ƒoolVÚ+[I6[G4ŽrW<òp|áÿ>øõûMþÎ~6ðn£ðsâcØØøÑ'‚==*ÆâXe³òTÊ’Í ÒH'i€Úqµ†vNÐÑŸ‰ÿñm¾"h?àýÞ•{³E×ñ‹yŸýé»&Sµ›®ÖWÑõòGìýðËãïÁ;­ö®ñ‰ñˆ.ø?ã¿Ã-â·'i´}~65,2+–T‘H¬Œ## ‘‚@=BŠø;þ #㌠¿g­Oâ÷Áÿ7ƒo|%$qØm/QK˘-V2×1Éå̅Ԫ݋dÉ?à¢_µ7ÂHþ-CûBEá{[›ë›K{y¼7¦Ü´©mµZPë `å—?w9ç÷ŠþÿiÿÁH¿g‰|7ÆØ¼MÄ«Áa§ÜÛèZd —BxahÞ&¶ùøŒ© ódŒ WîWÄ-Å>!ðF«¡ø3ÄÏár涺ºÛCxÖ²+2y)ò0ÆFÛQ_Î?ìsñËþ û`xÏÄ~Ñ>7Çáû_ Y­ÌײhZeÌnòJ#Š5U¶Œà;OEéÍ{Ç?Úþ ûx‹ÃºÿÆèßü ®Ü4ÖÂ>@ñ€Ï kh¡hedËFÇÎCƒpE~ëÑ\Ÿ€üg¢üEðG‡þ xq™ô¯éöº•©qµü‹¸–X÷͵†GcÅu”QEÇø÷Á:GÄ? _øOZAzŸ$‹þ²W˜åCÙ‘€#ò<\/ÁÏjúÅÿ¼lBx¿Ân¶×Ý…ÔD~âñ=VdÁ>œ+Ú«À¾1øoXÒ¯,>2ø"6¿á„e»¶N£¥±Ý=¹õdÿY\08‘^¶j¬^o}bûKü¥³ù>‡&"./ÚǦþkþù÷=öŠÂðlj4x~ÃÄúâçOÔ¢Y¡qýÖìGfSè ƒÒ·kËœ[Œ•š:£$ÕÐWÄ?¿kËßþÑ>ý˜þxb?xÃÄD\j¡î´EÃyÓ:G/Í凩… Œ™«¾!xÓKøqà/üAÖòtÿ i×z•ÀÑZDÒ²‚{¸õù“ÿ¹ð­âÍÆÿ¶OÄ ·>.ø·©Ý$#?gÓ­çehâ'•GJíè±Ó#=·þ <ö¿°ÏĹí¤h¤_ìl2¬3¬Y‚9é^Qû~ȳ¿Š¿g¯…¿¼Aá?¶ø®æÆßP’ù¯¯ƒ5ÔrY ,â> Ž6ãŽEz—üþLSâoýÁ¿ôñe^ƒûÉ›ü&ÿ°,_úÐÕ滢xcH»ñ‰5 }+K°ŒËquu*Á1¯V’G!UG©5ùq¨ÿÁU~ø‹ö‚ðWÀÿ‚Ú3ø²ÏÄ:Õž•{­Í#ZÛF·3,E­#(d›fìîpŠq…Üaõ§íû$øöœ×|)uñ3UÕŸÃþy¤›Bµ»x4ýFG(ck•_›1á€d*øb7×ñ7ã_Ãoü*ÿ‚®|.ðŸÃ×ÃÚ2j^•m,ãB$iY‚Ž2ÛAcÔœ“’I ÒÕQ@Z滢xcH»ñ‰5 }+K°ŒËquu*Á1¯V’G!UG©5ùq¨ÿÁU~ø‹ö‚ðWÀÿ‚Ú3ø²ÏÄ:Õž•{­Í#ZÛF·3,E­#(d›fìîpŠq…Üaõ§íû$øöœ×|)uñ3UÕŸÃþy¤›Bµ»x4ýFG(ck•_›1á€d*øb7×ñ7ã_Ãoü*ÿ‚®|.ðŸÃ×ÃÚ2j^•m,ãB$iY‚Ž2ÛAcÔœ“’I ÒÕQ@WçÇí;ÿø]ûþÌ_>(x7ÂÙž(±†˜oRþù™%žò‘‚<í̆ ãž;Wë•|?ÿ"ÿ“$ø¥ÿ^vŸú]o@ß°u÷‰Sö ð¡áÛxõMv="ùìà»™¢Šâån® QÉ6¢³¥¶£œbºßØÿö´°ý§ü;¯Ûë)ð‡Ž|-†·¡K)–KVWeŽ@̨Å[k!Ê‚²#)ãi9ðN?ù2…Ÿõãsÿ¥³×È¿´í³~É_·—Ã?ÚkÃà[xkâ«ÿÂ9âxSäŒÊÆ8þÐøÀû†)€êZ݉?1 ÙZ(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠŠâx-`’êæEŠT»»*ªŒ’IèëB@|ûñÆæãÅwšÁ=&FI¼W/©:4:E©9Èû¦SˆÔô9"¾€µµ·²¶†ÎÒ5†caUaTÀ_?|·ŸÆ§ˆ>7jq²¿‰¤û.”®0ÐéŒV,Ê™œt<_DW­™¿gË…_cñ=þí#ò90¾õê¾»ztû÷ù…Q^IÖQEQEQEQEQEQEQEQEóž‘ÿçãΡ¡Ýè¿¡mBÔtTÕm ”òʼn =HÀ¯£+Åþf’$ríèI¯O*©hèÍû³\¯Ë³ù;?Kœ¸¸¾^u¼uÿ?ÀöŠ+œð‡‰ôïx_Kñ^’Û­5[xîg%wŒ•?í)ÊŸpk£¯>¥78ÉY£¦2M]QP0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®cÆž,Ò¼ á]OÅÚÛì³ÒáiŸ\Žý§b}È®ž¾mñü]o‹ZÃÈ¿yáßµ]dõI¯›;Võ™\‚0"»òì4jT½O†:ËÑ~¯eæÌ1\cîîô^¿Ö§QðCÂz®‘áû¿ø±1âÍý¥¨dsaˆ-†y xPBZ½ªŠ+ ^&UªJ¤ºþ—¢Z"èÒPŠŠ (¢¹ÍŠ( Š( Š( Š( Š( Š( sÆ>&°ðW„uÏj§Z Íüç8ÄV±4¯ÏûªkùnÑ~êÿàœÿ¿hkè<ÿ¯[\2—–Æ[{¬î‰.¥‘½¢Î{Wî7ü¯âü+ߨÏâÄ2ywzôh° ãö„ËËÿ€þiü+[ö?ø/¤i°Ï‚>x’×u—‰<6ï¨ÂF—]W¸?ÄÁ^zc¨‡ÿ‚u|{°ñ÷ìY¡ø‹Ä7Y¸øymq¤jnNJG¥F&9äÿ¢‰'«f¾ÿ‚?è÷þ$ðÇÅßÚ[üLêÌÜ“öp×R'±–ñ=ÊûWåwÂ_‹šßìáoÚöoñMÁ†ïSÒît«pxݨÃt4Ù CþšÛ\É.GUˆ¿ Ïø'wÃÿøW?±¿Ã]*Xü»RÀë0Xê’5Úî"‘èzwíkñþwìÏñ/Ç /“q§èw‰lùÆÛ«”6öçþÿH•ø×ÿ™ø¿û>|øQãïüRñÖ“á½k]Õ!‹ì—W .šËOƒtr,+™4—€I%OõŸüCâü#²Ý‚à—lþ2×-mÝ3Ö¶J÷n}ñ,pþuÝ~DZ‡Àe‡câ7Ãm\×u­=Bòö÷M·–õÿ´‹]"´ìžh1Ç*¢áP  b€;oÙ§ö¦ðÇíÁ«|RðÚø2Öïádž.m--.u «‰šG %¤ÑmE_%d Ùa¹2g]ø×C°‹á~½á­*Ö+K$Ñî­ ·…8¢ˆ[´i"€ªª¸@À ùgö.ýž|!ûøFóà¥ÏŒ,5¯ø‡P¸Ö„yKk¹mÌi ¶iFHÖËŽ3»§5öÕí²ÞYÏfÿvxÚ3ôaŠüÿ‚O»EøÅmýË ÿï´½û-rßµ ÓþÙÿ>1jѱ¹øiû6ø;_òXÃs¯}Ž}¬Lý¦<‚2-G÷ùùWö"ý¡ìÑû9þÑž+±›Êñ6¦|;¤hˆ>ÿö…Òêj$QÿL^_BP)ê+õ‡À³Ã~Î_ðLˆÞÖ`1ø«Ä×µqŸý`¼»Óä"'œÁHÈÉÃ0ûÆ€1?àŒ“Coû&xŠâáÖ(¢ñf ÎìBªªØØ’I<S^_ðÂ_ÛÏöìñí3¬Fn>ü%‘4ÿ ¤€ùWP–kwõ!‹Þ7ñ+4*r+â¿‚ÿuÏÁ=çøðà=Ïþ3xÒÿF±‚ûå³–Ú–†RËç}˜¡Çô7û/|Ñ?fÏ‚>øO£ì–}:7P¹Aµj|÷3sÎ ü©žDj«Ú€>€¯æŸâgŠ´OˆÿðWÈ5}Xnðÿ‚u­>¥ƒû(ÔÉ!þêÞõ=†HÚέc h÷Úî©'“g¦Á-Ìî†(T»·à šþvà•~‹ãïÆOŽüioçG«YMk1nH¸ñÛÝHèOñ§ÙΨÜ=kl;‡´´øn¯nÝH©ÍÊùwèGWÈÞý¡<ðá•Úü}ñÏ‚¯£Ð®în·žì¥¬åDEwo>Ü ;Yé´OÄTøeû?xûâL˜¤Ñô+Û«V9Rn ,-Ç8 ´¬ƒÔf¯†tjÊœº~=Ÿ£Z¡Qª§$?Ÿ²ÿÅï…úÿü³Ç¿¾)ø§Oðî‹e6¹u¦]j7 r$±#9°´‘”×ë‡¿à¡ø¡ûRøWöyøkŒt½JÞî}W^IdŠ 3m ’ì…L¾ûK‚. ƒƒàŸ³GÈŸ üsñ⟃´¯¥Þ­›`5koDÊ6f„L·Ì7*¯]€v¯½¾~È?f_ÚoÅ_´”Îá?xšÆ-KÑÙ#°¶²¹˜@f1I$ LÖìË(ÀvŠæ4>éð¿ƒ|!à‹ 4¯èv:”Ó5ÄiöÑZÄó8¤d‰UK°P ““ÅgüEñÿ†>ø]ø‹ã+¡g¢øzÒ[Ë©:‘ íAÆçs…EêÌ@ší+ðûþ 5㟈´×ÅöýŸâ]JúÕ?¶ ×oÿøÿÇì·cà¸%Û?ŒµË[wLãu­’½ÛŸ|K?}Sû|?ÿ…mûü0ðü‘ùSÝiIªLÃyš«µé î¢`¾ØÇjç?à¤z.™­þÅ_bÔÑH´´¶º…˜Rh.át*OBHÛ‘ÙˆèM|åÿa¹Ôçý”µ¨¯‹{ߥ®ãÒ#ifìvc9ú“Y?ðXÏŽšgƒ¾Y|±¸V×<{u ³ÂÍ™a(™¤lr7Ü$H¹á€“t×Ö?ðOƒ7ÿ¿e/x_[‰àÖuHŸX¿‰ÆÖŠ}@ù«)åZ8¼´p‰OÒ€>Mÿ‚Ðü@ÿ„övð߀måÙqâÝqEÏßµÓ¢icÚg€Ö‡ìûû`þÊÿ²ïì[àëÇn«¯hú¹“FÓæ’jW¯%¶+o-¼ùYß §$œ ù;þ Ÿs?ÆŸÛ7áìñ§HdHâ±µp§ýUλzCíˆc…‰ôúWé§íÿ÷øñ_á>±àox3þñ ßÙÍ–³e¤AÖ­ÈïŸ Dî5hÈ-›=EzWìåãM;ö¨ø5àoŽß< ¦Øj³Ésy¦C2%ü–UÃEðM4JѼ‚pP¼ñ]íkñþwìÏñ/Ç /“q§èw‰lùÆÛ«”6öçþÿH•Ô|Ò|ேz'ÂOkvZÃ|?°´ÑnŬÑÉ$SÚÄ"o>4f1HìŒÅ[œæ¿=¿à²?áý–ì|»gñ–¹knéœnµ²W»sï‰c‡ó 8ÿ‚/øCOðŸÀ¿üQÖ%ŠËþ=e,ÖYcSm¥À[s1æ\J?à&¼wþ sñ§Mý©¼oà?ÙWöv+ãZÓP{ËÙtÒ. l†¢I”"(ÚWó²0F[*ázßÿÁ,þøÛö/Ñ|d¶—Öµ_ f¤ºÆ÷sÂn­à’ÝÉŒ)VH›h 99Ý[ÿðEoxK[ð/<<;¦ÙxŸÃSA)Õa¶D¾¾±¿2óc{ˆ$ˆÈY2 ¯~¯Â„~ ød&[–ð¶c¦¼ÈYdµ"y<€ì¥±ï^•EQEQEó.“ÿ?â_ü#R~ïÁ9¸i4ö韫?/mè±Ü}èÇ@ÙP:šúj¸ÿø+Gø‡áKÿ ëjD©…‘ÖC*ó¨{20~G‚k…ø9ã]cW´¿ð/È_øMÖÚ÷°ºˆÜ^&z¬ÉÉôlä ^Æ'ý¢—·_t—šÙKô~v}N:_»Ÿ³èöýWê¿àÿ,Ö'Ñ?b‰÷vÎÈó[XZåx%nµ h} 9Ú½_ö:Ðm¼5û)|#Ò­¢øEô›‡UéæÝ[$òŸ©’F'ÜמÿÁE<5wâÏØ¯â¦—ešH4èoÊŽÑé×PÞHßð…á]_ì=âË/~ÈŸ u‹<ÔƒÃÖZ{·ý5ÓSìRƒô’㇕ÁQäÅ>&ÿÜÿOUè?°7ü™¿ÂoûÅÿ¡½y÷üþLSâoýÁ¿ôñe^ƒûÉ›ü&ÿ°,_úÐ×µüî~Õò—φõÿáoý+÷›âÅ/†ß 4Èu¯‰¾(Ó|-cs'“ Ú•ÔV©,¡KlŒÈ˽¶‚v®N3_´í/ð›ÄßðRo |nðæ­ý£à¿ j~:„1¹GŠÆDk™b\uL°_›oËA ÕäŸ ~=üøÇ$°|-ñ¾‘â{‹x„òÁcw·DÄ(y!ÌA’YG´úO_ÎçíWÿ)|øaÿ_þÿÑ¿y¾ |Rømð£L‡Zø›â7ÂÖ72y0Í©]Ej’ʶÈÌŒ»Ûh'jäàWó5ûHþÒÿ ¼Mÿ&ð§ÆïjßÚ> ðƧáñs¨C”x¬dF¹–%ÀwTË…ù¶ü¹ýQQ^Ið×ãßÁ_ŒrKÂßé'¸·ˆO,7qËqLB‡’|Ä e”sÇZøÏã_íå®|%ý±üû/Åá{ý?ÅsèÐ6¦×N³Eý«p`È„!S°óËsí@¤õùÁÿÓ<ðŸö@øÅâo h¶zN³ã§±†þâÞŠ[éîn`šWQ–"çžûVbGëòsþ +=ì_²VŸ¨cÞ(ÓÒ|t‹{¦öÞ©øâ€:Ÿø&ßÁÿ ë°G†|9ãÍ×ZÒü_q¨j7vW°¬ÐËþšé dpA; Ôö8#‘^B4›Ú£þ g«øn5—Á³Æ› µÍÄXû<š¬o3Åex “É‚:bÙýExÿÃ/Û Æþ=øðçöFýŠt+Ëï ÊËY×fˆÛÙhº u*ÈAùƒïĤybI úÓû,~;ý—>Øü9ðä†úúG7z¶¤ë‰oïåÌ™º£c\¨ImÌ@>¯çsáü¦ŸÄõÿ­ÿé®JýÏø•ñËàïÁÕ·?üg¥xYïä·P»Ž gHðÅòm$´HüØ|-ý¦>é¿ðT]Gãî§«}Àšž«ªÇ£$R\ÙËmÌ›wª;í'* «e€ÁÀõG_ÿÁH¿äÉ>)×§þ—[×Ó¿>-ü.øµcs©|0ñf—â«k&D¸}6î+¯%¤ªÊ#bP°€Ø'¾bÿ‚‘É’|Rÿ¯;Oý.·  àœòdÿ ?ëÆçÿKg¯ ÿ‚ÁhPjŸ²jÏ3hZþ›uôd/æÛ’¸–½Ïþ Çÿ&Oð³þ¼nô¶zùûþ ¯­·ìäø.еOx’ÂÒÚåäò’YŽ eEú° ÒÿêòøƒÀ¾×§bÒjZmËêZhUÉ>ù5Ö×=áøsš/‡Ž3¥Ù[Zü½?q§ÜWC@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢˜i(‰(¦‚ià¶Mó8@;“Š—4•å (ßDZ¢¼ŸÄß|ág)¨_)eê†þµæ‘þÕŸ 'ºkXîŸpã”ãóÍyŒû N\®jç¡K*ÄM]AŸQÑ_#ë?<#¨L®—#hèxÿßÐhÆ‚ÚæëïÇø×-Ã:œ—üNÊœ=ˆŒ9¹O¦h¯üœ›Ñú2ãã§€–6 |½=¿Æ½gøD¯íIJºíÛ‘žßE|suñ³ÂÒ^4Ér VÈéþ5Ýhÿ´ƒ —Wj¬>Ÿã\n,ÃT—+i|Îúü;^1RŠ¿Èú2Šñ_ø^~#p¾_Óük#Xøïà¸íXÅx¤‘íþ5ÝW?ÃF.\ëï8éå‰;r³è+ã›þ‚ãí?hûÇ=¿Æ½+Nøûài¢_2ìøqa8¯ QÚM/™ÓˆáüE5~[žùEx±øéà.@½§ø×/⎞6Æ;{¥b}Çø×]~ ÃB<Ñ’3žŽO^nܬúFŠùHøåákƒIp±öÿõM/ãoõ&ÇzªÇÔýkaënÒù›bòE'ðÜöZ+ÏÏÄo muyÿmƼóÅ_ü=-ÂÛÇ{ÁÔ†ã[ã3ê æ¤¼ÌpÙEj’Qåkä}E|ñ |Vð唂 ¯£1žû‡ø×£'ÄŸ:ƒý£?í¯øÑƒÏ°õc~d¾aˆÊkS•¹[=ŠàÄ „cý¡ýµÿò}kâNqxì—±•FãæãYãø†…Ó¿™X\šµVÕšùKÑ^áÿ‹^p ¸½qþÐÿíGÄcþB1ßKþ5ӄΨUù’ù™WÊëAÙÅíæ·ÿü)³´wñì7¯ø×’OñD7­r/#È9ûÃük‹Ä´(Ë•´þføl’µE{Xú–Šñ½â·…§EYïãVíñ®Ÿþ?ƒóÆ£?í/ø×m ë8§Ì¾ó–®[ZÎ,ïh¯&×¾&x^6òoãbÚã^giñ'B¶¸ý®3“ýáþ5ÅŒâ|=)rÝ?™Ù†ÈëT‹v·Èú–Šòý3â—„nà¯ãR9aþ5¦~#øD©Û¨ÄqþÚÿzóŒ<¢¤¦Ž)eõ”¹\YÞÑ_8ø¿ãW„íɵŽí[=pAþµÌh¿<+§Ê¡îG–zôëùד[‹0ЩÈßÎç¡O‡ëÊŸ=¾V>µ¢¼J/ŽÞuȽ Ç×ãIÿ ÏÀ˜%¯W§ø×¤³Ü+WSV8–Wˆë{uñçŒ?h¯C.ß´oU?Â3ýkGÁ_´oƒµ9ã²Y¶ ñ·úדþ¸á}§%ôïs½ðî#ÙûKYQY¶…ž§¹³•eFÊEhuõPš’æŽÇ…(¸»=ÇQMÖUrBŠ(¦EPEPEP_?üxÔ¯µ‹Máƒ)‹SñÌæÞyï[é÷’þ)ò~öâ5ïÌÊŠ]ÈUQ’O_:üVñ÷‹¼Gñºìµ¿c¤è{¿‡M´rUÿ®óÞØô¯W+JX™m ¼äþúú&rbŸ5©/µùuÿ/™ïÚf›c£i¶ºF™·´²‰ †5è‘Æ¡UG°zŠ+Ëm·vu%mŠ(¤0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çO„ñAøçŦù,áëz(è>ÁzçΉe‚|ßæÍ}_=|x·ŸÃMádl× ºÿMTit«¼Et¸Je]sÀÁ5ïö×]ÛÅwk"Ë Ê®Ž§*Êà ƒÜȯ[3ýâ†%}­ø–ÿz³õläÃ{­Òí·£ÿ-¾DÔQEy'XQEQEQEQEQEQEQEQEÁüLñͧßê>*¹O>[t mÉk‹©NÈbP9%œ€qÈ=«àç®üàä\´x‡X•õZ~ ’öçæq‘ü)Â.8ÀÏzàäÿ‹µñ¥a½ð¿ÃY·t¹×$_”z²¡Ïª¹ô¯¤k×Å~â„h}©ZRÿÛWÝ«õ]Ž:^üÝN‹Eú¿Óþ(¢ŠòÀ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ðÓþ Ѫügø¼º'À… ¼]¯iºòêš–©g¢^\X\NÖÛmã¶šÝe¬òy§€¯…*qû ð‡Äöþ1øiáïÚx{Tð¤3Úª&—­Zµ–¡h°I nPü™_U!‡½Šþqট±§ÄÿþÕ:g~xGT×4ßÛÙ‹û>Æ{¨,¯áak#Ü4(Ë„Rr ç±5ýè%‡†´7ÚRyVZU´6'÷bh?QZôPóÿDÿ…ëûFüZÑ<%àO„^6¼ð¯Ãó{j×±è¯íÜó*ÜMlÑÆÉ$!O*LÙ$ ¤÷m¿íëã@·Ò|5û(üP+§Û¤°I¢Ïa"Pˆ¥Ö) € ~•ú‰E~#þ¾ý ¾/þÙž<ý¨ÿh¿êþšßIû•o©é÷61Cö™8áµûLh]a·ŽEr¼–“{`¿?·Q@Ëì1ûø÷Æÿµ‘ºøáOIðWƒ/¥ÖgþдšÞÞöâÖf[(“ÍUY3#o=A\Ïô)ûUiÚ†±û2|YÒt›Yoo¯<)­Ã#I,²Ée*¢"(,ÌÄ€“À¯|¢€?ž¿ø$¿ìƒâËj¾.hWºL~c Yêvò[Èo.3ܬS*±FûPãäbèëú¢Šøcþ ñ Ǿø®øáüCã/xöÆóJ´M6{øl¡•R+‰nžc1J‚]ÁƱ™°OÅ‹²Ã-{Â!ýš¾"ëšÆ·«5óÝYèwi°EQ$!²¬²ãæü+ú¢€?šŽZ߯ïÚ»öŸø{â?ˆÿ|KðËá•–­¤Å©>§¦ÞÇÅÆ$¸¼¸’¡cvŠ?Xšýÿ‚§ø³âŽ»ðŠOÙÿá?ÿx²ûÅfÒêþÿIÒn¯,-ì­î žIžô‡–&!k_´ïíÉûUü'ÑügðwÅøYá­b ·‡SÒï"…–ÜMwq$ÄHâòc^‹» K9ÏôEyçAó‡íAñ§Åÿ¾Ük¿¼­üAñ=þûm:ËG°šù"¸(JÍyähà^øc…Éeüªÿ‚Wé¼!ñ§â&«ñÛá—‹¬'Ó|%àwÇZß‹,ot[;M Â[ö†[Ëi\\ˆƒ3ÉÁ$á@ç#òóþ !oñ›à·‹ê7Ž™çèº%Ýⵌ—âÎæHÄRª°ÀgæWô}EG žtI.ÖMêk 0ÈÎìGz’Š(¢Š(¢Š(¯øÇá½cK¼°øÉàˆ Úÿ†–êÙ8:Ž–ÇtöçÕ“™#높+ßh®¬)Ѩ¦•û®é•jJqåg þø³àØWRð÷Šl$†Eéæ[ÜÆc’6Ç à²°êGQ_–?ðNï꿳ÿň_°_Ä{’—¾¾ŸUð´Ó ¿nÓ¦ùäô)²à*äå¦Ïú³ºt¯ø±ÿÿá“÷^ñÍÃI§ž‘éú³òöÞ‹ÇÞŒt •©¥øËû)ü6øÕñÁ?5‹½OAñw€§Xj:DÑ[Í"+‰XeC»wËøÎÓ…Täœ5„µO˳ó[?ò'UÉZ[­ÿ¯3ƿਿòbŸî ÿ§‹*ó/Øö»ýšü=û9|+øe­|@Óm|$¶Õuï„ÿ´MU’ÕÁFÓ¬ôÙï<°] icHÆÀyÚ ÉÇZüêý‡¿e_‰’ümñí§ûDéŸØ.ñd÷o¥he³&Ÿ ÛaÞltaÄ‡æ ¹œ Öê(Šñ¿Ã_‡_,môωÒ¼Wgi' µŒÑE. ïD+m$d àâ¼×þ;öYÿ¢7àÏü'´ïþ1^ÿEy·‚> | øgys¨ü7ð6…áK«ÈÄSͤ閶2K;‚;[Æ…”pIæ¿2¾~Ëÿ>8~Ûº÷ísñû@oèÞ½6¾Ñ¥‘d¸´ñä[Ü9Œ‘ä©pÙùäa·1 ·ëõW)ãoø3âG†î¼ãýÓÄ%îÓ5ì+<.Qƒ!*àŒ«TõdWWEq¾øwà†šAÐ>øoNðÆš[{[é¶‘ZDÏŒoe‰T3ÕŽIõ®ÊŠ(μqðá/Äéí.¾%x'CñdÖ Énú¾›m~ЫYc7¹@Ä@Æp3\/ü2wì³ÿDoÁŸøOißüb½þŠá¼ðÃá¯Ã+k«?†ÞÒ<'o|ë%ÄzM…½‚Lè0­"Û¢ r@¯Ì¯Ûãö»ýšüqû-|Jøqá?ˆn§â[¸`·ŠÆ&s3Ë ì-"P ¨F'žÕúÝU …‰96Ñ’Ø_ð ‹ÿàœòdÿ ?ëÆçÿKg¯‘7]øfåü3£Kþ’Ê7<ŒŠû‡[½v™qvN([ò¯ÇeÓµ?ßnŒD¿“3)ÿuä+à¸ã1¨¡5-ä}w ``êJ½M¢Šžøqã/‰×kªêwI˜ÜOCéÖ¾«ƒö8ðíÅ’ÍÒ H†ïßµ{.ákoéðéVñˆö(Q]Ëc(†vÊõó>OF.ص«=ÜÏ5­-pÛ#ã›ßÙ U‰ÁtåW§'5ÆËû4ê¹®% ?Ú5ú™ÔQŒî \gˆü<'êØa½{¹‡aÔ=¥ÌÁq^#›’«?=ôÿÙ¢úâQ·²¯ü×V?d]BR6ÞÉ·®wú_çŽLQÖ»ßx‹i·GèkÌÊr|$§ì룣šâ"¹é³ámGöHÖmË̇„×,fÍMh7ûÆ¿T~IãÇUa^wâº1¸·zWfkÁ¢½¥Œ0|Vv—RŽ}Oø×ë÷ˆ<5o{–Ô8ç¥y[Ú%¼…Jdãšó1Ü èOIèzX>&Uak+Ÿž û?üguÜ—Sovÿ‚_ÙïãH]å”ׯø×êg‡u¨c"ÞéG±"½[Ú\FF¥Z½L'Я *]žv#‹+Q•œ4?Á¿ŠQ9âU#ëþ5nßàçÅ‹— ܹ>íþ5úËâ/ ÅÍÍ´céŠàK€… ëÚ¼lW :,æÒ=\?ûX]E\üñ?³÷ƉVæb>­þ5Fëà7ÅÛ!æM4¤~?ã_¬¾Õ­nBÛ̪¥u×:m¥ÜF9"\Á¯nQ¯Oš»<š¼]Z”ùe ‹àÿÅ%}Ëq(?Sþ5zÓàÇÅË×Ûä ý[ükô÷\ÐOœ”Œl=ñXзu’oZð%Ãn•NYIØö!Ÿ:æŒQùÜg¿Œì0÷8>íþ5—{ð/â݆ieñÿýxÐu{;È„2¢‡·¤YßÀPƤž‡ï.¥VŸ=9Ýž4¸º´'Ë8$Åñð‡â¢¶êUúnÿÔ³øñ†ýsäÃñnZý/ÕtOìéÊÉÆxâ¡Ó®…ŒÁ‘AçŸJð£Ã’¥>Z²±ìK?æ‡58¦~zAû,übÔWÎyØýàÇ?­fÞ~Ìÿ­dÄòcþ5û¥ßÁ}n®˜VÇ TZΗô˜s_EW€èÎôæÙáÑã¦È4µZÚUž5‘NAf¿XRRWGç’Vv (¢¨AEPEPEP„|z×5&Ðtÿ†Þ—Ë×|wqý L6¸ÝyqޏŽŽ9+Ø4 MðΉaáí/&ËM‚;xSÑ#P£>§“Üó^ðÇþ.7Äo|`Ÿ÷š]ŽýBÏ*`³ur½›/Ê­×jkèêõ³ÝBUºÖ_â}>JËÖç&ßn¯}¢ÿ=ý,QEy'XQEQEQEQEQEQEQEQEQEQEGTÓlµ6ïGÔ¢Ú_DðMtxäR¬§ê âu+Û Uø[®JdÕ|tl77Þ–ÅÆû)±èÑ|£ýÚ÷ÚùÓâü[ï‹>øŸîôÍwÕÏð4ï²» ²eE W­—~òÃ>º¯ñ/óW^¶91>ì£W¶Ñÿ“·â}EW’u…Q@Q@Q@Q@Q@Q@Q@ywÅÿ\x Á³^i1ý§]Ô¤M?J·™¯®N؆P¼»{)ê5ów†?âëü_¾ñÄŸ¼ðßZ]3JÁ>¢à »‘؈Æ"CÈËæÿ ¾‡6&m%îôÿ7òüÏQø_à[‡^ Óü2’}¢éšòàò×sóJÄòw98Ï!@«Ð(¢¸«Ö•Iº“woVmN 1QŽÈ(¢ŠÈ°¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùóã¿íMð+ömÓâ¼ø¹âˆ4«›¤/mcµÅõÀf;xƒ>Üñ½‚ <ôËxÆGÄh^;Ы¦ø†ÆßP¶Æb˜EsÈ‚D<«€ØaØÓ|kã¯ü8ðíÏ‹|}­ÙøF³Í»¾™ …I軜€Yº*Œ’xšêè¬ÝYÒ¼E£ØøƒB»ŽÿMÔàŠêÖâÐLã‘pU”‚pkJ€ (¢€ (¢€ (¢€ (¢€ +òËŸ¶‡íñcöŠø™ðáü3,Ÿï/"k­_R»¶óයû2·î`›ç'Œï_¡Ÿ îþ(^ø]'ø½§iZ_ˆL²‡F¹šîÐDîÈ’xâ}Ä}á·±4è4VV·®h¾Ò.õÿßÛéze„f[‹«©V!z¼’9 ª=IÅeø/ÆÞøá‹xV·×4-H;[^Z¸’DnѾÖvº²ŸB ¦¾pøÿÓâf…ñrÝéÇ—¢kØáU$oô;¦ì<¹>Fcü$_G×;âï i~4ðΧáMi7Ùj<z®áÃ/ûJpÊ{ weø•J¥çð½£ßæ·^ib)9GÝÝj½NŠŠðß‚~/½¹ðep±ëÞ•´íJIhhà\ÃrK°€ÛR×uà/‰þ)é7:ïÃÙx—M³º–Ê[› Ö⹄)x÷¡*H §ƒ‚#‚+,^TjJœº~=Ÿ£Z¢èÕSŠ’;Š(¢¹Š( Š( Šåx×XÕ­/ü ãv âÿ ºÛ^öP‘û‹ÄÏU™0O£g d öªð?Œ~Ö4Ë»Œ~€Íâ #-Õºpu-ŽéíÏ«/2G× $W­€š«…›ßX¾Òÿ)lþO¡ÉˆN/ÚǦþkþù÷=òŠÁðljtx~ÃÄúâãOÔ¢Y¡q× ÔÙ”ä0ìA¥oW—88ÉÆJÍQ’jè(¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(§ž©1Î)ôßâ¥}yoÅÝY4]Èçj”ý+áoØóFÏŽu}[nUä“ñjúãö޽·²ð#µÁÀg }q^ûö]ÊpK!þkólꢞmIK¥¿3í²µË–U’ëÈû_[Ðb¿ˆº€Ö¼–æÒkIZ)†0x5ïÂhŸ…`çu­+ø‹ ÃŽkéslª5#ÍOsÂÀf2§îÏc…Ð5籕a˜æ:õk{ˆï!ócû­^wg5œÆ)Azèt~[) Žc5æåY£¤ý…S³€HûZgEâ?‰TÝÚ¨Ýí^qûÈŸò²š÷¨'Šî0P†S\oˆ|6&Vºµ#’+¯9ÊÓ^Ú‘–0åýÝR·‡|GÒÖäû]ùÏ8e5à$"šÞE†äå±Ë3{?c]èm˯ûÊe-sC›O‘¤ˆ|™¬g’Ýı0y¯tx­õ+npC ò½sCšÂVx—1šÇ3Ë'í)lÍp‚šöu7;_ëñÝÆ!˜üã¥njt”&9@ÉèkÃášH%ÂH+Ú½WÃþ Šö%†c‡÷¯K-ÍcZ>ƾç?/tŸ´çº¶“6›1V'jf—ªO§LOÉЊö KN‡P€¤ƒ'òWI›N”«©+žµæætðÓö´¶;°xèÖ³«¹ëºf¥£eêG"¹ßøy.”Ü@¸+‚ÒµYô郡ù äW¯éº„¸‘H'¸¯_ Š¥‹§ìênyµèO >xlxƒ¤¶òáÆÖS]ï‡|C¶³žœUïh Æ2ëÎGÖ¾gý–õéÞí&ø‘$n㯾>#ê6—^Ôî¤ÃEäÃò¯ÏÙÚK9~$Ü¡;VGl ùþ#ÂB8êu)}¯ø·‘bg,HTû'é·…äZmœvÏ¥uÛªbH£ƒTÃëõ<%7I=ÏϫϚNH}Q]f!EPEP^'ñÛÄúž•á8|)á—Ljüc:é6ë›ýtç…Š-Ì[±Å{e|ãàø¹uω2þóFð “Cѳʼüë•úœD¬8* zy]8©:ó^ì5õ}Íïä™ËŠ“²§å§Ë«û¿Ùá/ iž ðΙáM6Yép$úƒ›ý¦9f=É5ÑQEyõ*9IÊNí1ŠJÈ(¢Š…Q@ãß‹ þCgqñ3ÅÚO…"ÔYÒÙµ[è,–vŒâ33®â¡†qœdg­ø±ð¿âœ7— ü]¤ø²-9‘n[J¾‚ô@ÒPHavÚX)Æqœt¯„?à¬ïáoØßÄ7> Ó­®õI¯të]&ycV–ÞæK¨å‘¡r7!h"6Ò7FE~bÿÁ2|Câ/Ù³öȽøã£öAã­*ž2J »6É©X¹Ïs ’F=Z@(ú-ñßÅ/†Ÿ m-u‰~+Ò¼)m|æ+yu[Øl’Yn*3(b$ÕGÀþüS¸»´øiãmÅsØ*½ÄzV¡oz𣒤»‚=q_ÿÁSŸÂ–ß±‡Œï|C§Û^ß , ÓxÕä†êkÈAx\Èâ ä•#*9Šë?à¿ñ—Å„ºßÀ/éú/‡âÔD^.[G†Ò°YíÝfX×{ˆÌàˆÔ³| ö®ƒþ #ðãºÇìáeñ"]2øH¼?«ÙAøEÇÙ.¨ð1¸Æ]•ö“€Ã#©È釀þ3ü!ø§qwiðÏÆÚ/Šç°E{ˆô­BÞõ¡G$+H!v*‚=q[9øðû᎕»ñĺo…´ÙæñÜê—pÙÂó2³ˆÕæeRåUˆPs€Oc_Á-ü á/ þÇ^ ñ‡¦[ÛêÞ%KËJñ#UžêD½¸Ž?6Lna(EàÀÉ9ü‰ÿ‚´üvÔþ/|c›À^/?ƒ¾ÈšuÜÑœÂÚÝò»Êެ‹ ‰Aä4SÁ4ý<éZ®™®évzæ‰wþ¨Ãŵ̲Ã<3(xäÔ•du!• ƒ‘^wûXþËÖsXß|\ð•½Í³´rÇ&¹b®Ž‡ ¬¦\‚Á¡«¿²ÿü›GÂ_ûtý7Ã_…ÞýŸþ~ÚŸðQßk^Ñmí~ø6ò)uAg†ÛP¸¶EƒË_/ þ›s’3.7Ä®ÙÞÛˆô;¥øÛÁºß…Çš>¹c}á·î—S‚æ9,¼Y/(XDZvÍœ ž+Ç¿á¯?eOú,^ÿÁíÿ¯p²ðî§hQø_OÓ-­th û2YE %²Á·o”"P&Þ6ããüÏþÅÿ¾þÐðP‰s^xjÆÿáþ‰qâ JßN’k·Ÿf´ŒE€›z(WgÀ ßøkÏÙSþ‹„?ð{cÿÇkÔü ñ'áïÄý.msΈ´ßéÖÓynt»¸o!I•UÌlð³(p¬­´œàƒÞ¾<øçûþÎ^/ø?ã À? tM/Å7]ßöEÅ•¼V“&¢±3ZâQ´2… ¸í*Héþ×|§&¡›¤º­š›k渎%F¹FAi ç~àÇï î[ û VÊ KK¹ŽòÒå‘M ¬‘ÈŒ2]I èAÅrÿügñÁ:ǃ¯ˆTÔíÚ4sÿ,å4Rtp­øWáü·Ç~*Ò>,üJøu9u/ XÙ˨ۂsw6×qÛ";<ô“,`'‘_§·ícmû&ü“ÄšbÅuâÿHÖ´¼¯Ÿ·t—2/ñGn¤1ÄÅà6F”jÊœÔàìÖ¨™ÁI8½™ÞxWö‘ømáφún§ñ›ÅúW„õ‹7—M¾T½†ÖF½²>\»VFË`>½áçÇï‚®ŸOøgãÍÄ·±£HöÖ7ðÍr¨§Úo0.HùŠãÞ¿ÿbïÙ>ÏÆž1ðßÇ¿ÛM4Ô>.[Ý_é)ª’%™H’9.¢â77ð£ ás¯ü7ö|ð'ìéÃïÚ+àÃýr\ØJ4dp™¼§¸·š8£ÛnžTŠÛT 9êÇÃßöŠ6ŒõKÊïõFXy{¼­Ý­ýQ^Oðâß¾ øâmÒ$w^'Ñl/î,ùiq<*Ó"ç'jÈYF{ ü}ý¾ÿiߊ_¾8iÿ°ŸìÕy%½Õüéc®^[HQ§šTß-©•9ŽÞÚ,½ÑS–Ã!Qƒð›Ÿªž-ý­?fOê²è^+ø¡áí?Q·.[fÔay¢qÕdDfd>ÍŠï<ñ›áÅUsðÓÆz?Š%/"i·Ð]I‚]#bÊ2GÞ¨õ¯”¾Á7?e_…þ‡Âz߃,)ÍyoðÏÆš7ŠåÓ•å4­BÞõ Y d»m Tã=pqÒ¿'¿à¶á+‚ž[:ÙüM{¯mxc_´¥•½´æxÖLoòÌ’ÄJgnpq }±û|ðÿÁÙ{ÀÖÖ:\6šçˆtËmWV¸XÕn'¸½_´*ÌøÜÞJÈ#PN/òî/ý¡¾|>×fð¿Žþ#xwúź£Ëe¨j¶¶·²(d-²+ÊAƒ‘]_~&|:ø¡§\jÿ ¼Q¦xªÆÒ_"iô»Èo"Ž]¡¼¶xY€m¬ Î5ø'ÿ­µðåïÄ_…Z¥[ÿÂYm%ÔñF«ssÒ[Áf’8œŽP›‰ÛÈÉÏîWÀÿ„>øð¿Ãÿ |#aoco¤ÚAïkººHÕf¹”€ É+.æcÉú(Ökå/þÚß³Ÿ~(èßï¼N5?ë:…¾š4ý6'¼kiîd¯ÚdŒyqm, «6õíÅ|Ãÿ4ý¬üQðWÂz'Á„’0ø…ñtK ýý•“¸‡Ì‹Òiäo.&þ; 2©¯rýŒÿbÿ~˰ØÚêŸo"ó5}q£;M//¼Ž7¤ ÷@wã{ œ¶k‚ñWÅ?‡×t/ ø¿Äº~‘¬xšá-tË;›„Žâòij¬1¹²Ä.@Æâr@¯ŸÿjO~ÖqÍáÿþËžÓÍωâûÄÚ”Ëö}bØvS½Ü6Pí—î‘å£ðCSøMñáüÏោþ)xÊ_ø‚}Ã×óê³ Èn&GÙ‰ÈXØP6…_º?ª;Û¸4û9ïî‰Xm£i\€X…A¸Iàt×ÌÿlßÙçö‘•ôφ¾&S®B¥¥Ò/ãk=Aýâ!“‰Bÿ‰œ/r+êZü±ÿ‚…~ÆúWŒü{ûD|·_ |Tð0“Yûnœ>Í6£ªù²‡h°MÊ*ï†NX‘哆@>èøÛã W@ðÝ¿†ü(ÙñO‹gn˜æ7~öàã°G—-ØíÏÝøÂW€|%¥øCF\Zé¬AˆÃHý^FÿiØ–>æ¾ ýŠ|yⱿŊ–³øÇÂvz,ÖVü}’y Iç¹xú,—;‡ÝàaÐp~…W¯˜~æ­ֲÿoûuiêÙLJ÷äêü—§üÊÁEWvQ@Q@Q@Q@Q@Q@8~Ö´.“û0ü×þ+ê%åí¢¥¶›hìT]jl1’9Ú0d|s±â¿>àžÿ²ïü-n?moÚ^8¼aã_ÎךLwñ‰b²¶G*—'¶Ñä1J›0X…òoø-ß‹u-þü4²f0ÞM¨êSD4b{sP$˜À«÷#Á~Ó< àí ÁZ,B?@±¶°·Eè°Úı EQ@'ÇüSðOÃWÄ<"ž7ñlFìôÉ.º;M"Ædf8 ±nÞ˹2 üëÖ¿œø(ßÁÿÚ‹Ãzƒþ,þÓ>>·ñ©âK«‹tÑ,UÖÃGÛÈ©"6%IWeL’£2IÕýPWâüóþIWÃ_û ]ÿé5~ªþÍè#ýþ¢ðº …Œ5íã?³—ü›×Âÿû´Oý!†½š€ (¯•¿kÿڣß²7‘ñ]Ò®5Ë«ûµÓ´û8cYnäŠIWÍ•³åÆ&,ÁXôNx÷üCðGÂÏ ßxßâµm hZrîžêéö ôU]Øð¨ ³Å~a|,ý·¾2~Öß´†‡áÿÙçÂ×ÁÏ Þ³øƒ[»‰·‘,oµ7I”„;•+n†Š.åð¿ö]ý¢?ࡾ Òþ<þØ:ÔÚéÝèž²cŸm( †òÞD2)™ËO*ô!J8ûöµý¨üÿôøgះŸ ¼ êšÌ7èzt äéÖâÜ ’k‚‡Ì‘‹H§hùål–u?1û3ãOÇo…¿³çƒfñÏÅmrNL¬(ß=ÅÔ dEoùåsè£rÄ($|1û%þÕ_ÿk÷Þ5Ó<'/…~i67vöÒ\"¯ïËÆ"w™¹wU”ƒ÷qçk³¶Ò|/àìñWöˆñŒ´7íý«\j·³â[ ™6$1¹#¹Xð°D:ýš,y•·oCûO¤éV¥Úèšœ:vcÃomoÅ 1 ¤h€*ªŽ@4QE~"~Âò‘ÿÚƒþ¾u?ý:Šý‰ø‰®xŸÃ>×¼CའþmwM²šâËK syåª×²]Cg<ÖP‹›„š(™ü±#€J©| ž3ƒŽ¸«4PóuûixöÕø‡ðßÄ¿´GÅØ­¾xfàCm/…téÞYå·„·ÙßRhÛd„’' ·÷Qñ_oÁ/왪ü^*Ô ÿÀ[Aý+êOø(1Oû|XI”:˜ýä•Oà@"¾Oÿ‚1j6w²Î·¦Ç 76ž'¼i#ÏÌ©-µ®Æ#ÑŠ°êkz•ÒÓTµ~]>í¾ã8Ç•¿?ëþ úçEV\g‡þ#ü=ñf­y x[Å^³©éãuÍ­•ìÀ3·2G³ ÉÇÌ5ã_´ßÀ¯þОѼ¥øæïÁžš÷wˆ¢°Œ ½KOØÑ£Ÿ?º ø ÃRwÖüUýŠ~xoÁßðUø?árÍiá.»‰åi›É· fÈîÄ–áÁ³ÈÅB~0øƒà/‡¶pê>ñ.™á«[†ÙÚä6q»ÿuZg@O=® ãO>"x{áïŒþxjßâˆf[s¦Ù­ÚGo:ܺ œJ׼®ä:×äçüöOðÎð·â÷í-ñ¯Åמ1ñuåݬ‰­-´›Y®¢H¬á]–VDi “Ã*™6‰ 1ýý…<%âþÈŸ |;â‚ÿÚ)¤%ì§çŽ;Éæ›= qJ©·øqŽÔø%ÿø?ûQxo@ðÅŸÚgÇÖþ"ÕÿÁC¾ x{âí„Ú]æ·§Gw£ëIE©Ù¤£rÇ0¬±†Èh™Šõ(ÊNúüððÇíûOÁ3ü_¦|!ý¦-äñ÷ÂËÖhô}^Ýüˈ­âÀ?f’B òÕ—u¬äãËp˜,û¥ã¿‡ÞøŸá«¯|CÐíeÒÿâÇüKÿ„vOÝxÇW-%ƒtOÕŸ—·ôXî>ôc l€É¯¦«ñç‚´ˆ^¿ð–¸§ì÷©…‘~ü2¯1ʇ³#ÃéƒÁ5Áüñ®±ªÚßøÆìÅþu¶½=®á#÷‰ž«*àŸFÎ@ÈìbÚi{uñÇIy­”¿GçgÔã¥û¹û>oÕ~«þítQExç`QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¢¿›¿Û—þ [ûBxoãlj¾üÖ“Âz„/$Ó¤–;[{‹‹»ˆ>YÞæ9B¨|…T’NxøÇþ]ûpÑP¸ÿÁ~›ÿÈÔý‰Q_Çoü<»öàÿ¢¡qÿ‚ý7ÿ‘¨ÿ‡—~ÜôT.?ð_¦ÿò5bTWñÛÿ.ý¸?è¨\à¿Mÿäj?áåß·ý üé¿ü@Ø•üvÿÃË¿nú*ø/ÓùøywíÁÿEBãÿúoÿ#Pö%E¿ðòïÛƒþŠ…Çþ ôßþF£þ]ûpÑP¸ÿÁ~›ÿÈÔý‰Q_ȇÿAý¶ô}VÛQ¸ø„u8 pÏmu§XfQÕeº¸ÕYHìkú”ýþ-'ÇO‚žø°-E‹øŽÅ.%€¬sRERy :œ{P´QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEƒ½#R´À4í­‡mO•?kK¨mþÁçœ+\cÿ5à³Ôöú\¯Ýúzd×­þÚDŸ‡6c8ÿLú ¯>ý‘l£½ÒåÆHÝÖ¿!Íc)æêÜý+´rÙI÷> Óõ»›+1œºÎkÖ´ÍFøÆÃ5ä:®•6›;‚¹F4iš¬úlÀƒòÕôX,Φ§³­±äb°P­jg¦ëº$Wñ³ ùëÈï-f´Ã(Á½³MÔaÔ"!äõ¬íwD†þ6up+ÔÍ2¸×µ¥¹ÃÇJ”½œö8_ëÏc ‚fÊžæ½Z¢¹ˆ:ÀŠð‹«I­&1È9 t½-„¢Îc=ëÎÊsWN^ƾÇ^;ª/i¢ñ‡<å7Vƒæ+Îq$NU†kÞàž+¸w¡Ü¬+Šñ‡VE7V£:ŠëͲ®oßQ0Àf ~ê¡_þ#ÆÛ[–úô"RdeXWÏøxdÃ|®µßx{ÄEH´º?BjrœÙ¿ÜÕc—ËÊc¼EáÝÇíÞx¯>exßË~W¿ƒÈäâ¼Eáä—76à )ç2’u)!`36¿w3AñYÈ œåL×¥*¦§$¾Û¯Bš|ñÜöíOL‡Pˆ†žÆ¼ƒUÒ§Ó§!ÁÙØ×£è:üW±,R8­KM‡P·(ãžÆ½ì^ž.Ÿ<7<|6&xyòËcÄmîÝ÷ÆNSðþ¾—±ea¼q^uªi“鳕+”'­Q·•à—̈íÇ5ó˜Llð“´s†…xs-ÏiÕ´¨5 °ù»òGM›N•£qÚ½Ãþ![ -çoš·uM* BR>cÐ׿ÁÃiHñð¸©á§ÉScÈ4ÝJm>Q$Mò÷¯^ÒuhurnEy¡§Ï§ÊѲü¤õ£OÔf°”ÒùƒLcߌ$ûÒH}ÝÉcîkÉ4I[âWÇ C_Ç™ ü;I4ëCÕdÕ®7RxcÄX= +èªõ±¿º¥ ?_Š^¯eò_‹g%~n§É~¿{üQEäaEPEPãü‚æ_ˆ~/øû4عvñŸˆÖâåò‹¾+(\ûbæcžÛMxü£ÁZ¯ÁŸŸ ?jOÄ-¥ÞdQ/ôYV{bøÿž°@:mˆ¯¹kRÇñ¯þ ¤é¥…ÆŸð@ÞÊ9O5-ÚPÞK‹ôú¦;WØðQ߃Ÿð¹¿dŸi¶yú¯†c]~Ã,%Óƒ<¡GRÏlf@vJø£þ ñ×ã‡ÃoÙçᯮ Ãñ‡W´ÔívòÞT‘E¸`=Zÿ§÷“ÔW톕¦Yhš]ž¦Æ!³°†;xPtHâPˆ£èü¯þÀ¿ˆþ?þÔ_ü!â3é?´ÍBhÉ%ŽÈf¹»…ϦÙç‚ ;,kô¯ê¾€? ¿à·^=x¼)ðËáM£—}JúóXž5ê>ÉÛÀHï¸ÜJû¦²þ ||ý¼fÙ.ÇÂ)ðBßÀš†4{MOVßQšÜ2%¨ºX-¤ÌSI+ Û•]†A¯>ý­Hý¡?૾øQúVŸá‰ô[ ˆÇ*Ñ[çV¼öÏ•##ÛpzWïÿ|7àø?TÐ~$XÙê^š/2þ AQ­ P6éCü»P |ž3Ú€>týƒôOèß²_É~Ж_³üoÃät:¥½®©e¤Äø>v¥>©yºí?x!WÜF¯‚?hÏ€×ÿ?à›ž ×gÓµ¶†F?6R7Þàò%’J‹ö@ÓuÛUøû6Ï·Ãÿƒvú—ˆöK‚‘”Ê‚Ö`ÎI%œœ´Újø?Äÿ~ü ý¨ü9û=ü<ðU½ß>,]¶¡¯\iÆ+v·-¹ÖæóÆi²%X Ýœ8¯º®. ´·–êêEŠUÝŽUFI'°­2¿¶ÅêßðTUñ»Oqá_†·Z-½Àµ…®dhìÔ&E‰yaçÊèÃÓ5÷ÆOø)n«ñ—Þýþø¯Ä¾.Öá’Ú-I´É"†ÀJ»Lñ¬~c¼‰œ®ðˆ§ Ì@*~hÿ‚\Å/Æ¿Û_âïí ~†HãŠþî"ØçÖïwD=¶Á¨§Ò¿¢Zü¿ÿ‚hþÅ^ ý—¼¬øÇâjÆž:ñ€…&µGYFg fX ˆJ´’3o”©*0€‚OçgüN}Oö’ÿ‚„ø;öu³¹)c¥+E;yÉ©”º»Ÿ¿Ì°È™ÿ®`uÍJuüäÞÁ…à¶PIâ· î®Éò«µö‰²Ð)<Þº Çñ u Ü‹F¡ðµ4ŸÀ¶š‡„…½îˆ‘õSiÀcAèÈ `{×ã7ü ã.‹ãoƒÿ´%oMu­É90‹h–ßc{ù“HŸTjýûÔu "ÂçUÕnc³²³æžyœGQF7;»¶ª€I$à þ\¼á+öóÿ‚5§…Œð¿@¾’õVã„=­Áâ0/'vؘܩ!$|^‚¯aý”·Nëѯ{òOï9ý›U9ÖÍkú½ %³ïìUáO^Œ+øÁ0ßÞFß(óm,>Ñ2zýðÃÖ¿#àŽ>ºø‡ñ‡âwíâüÞê–J-â¸g}ö±,“ÝÊ?Û úJ}kö#öÌÓ5 [öNø¹a¥öƒáM¨ÉeŠÝ¤uu,Š@õùïÿKºÒßà/Žì¡‘N¥‰L“ #rÁ%•¸…ˆêd”ßÐמt´WÍ¿µíIðßöOð ;ø†..þÝt––v^[]ÝHysÈè»cL³±`ï2ƒô¨6¯¤Xê­o%™½‚)Ì`I‘Cl}¤Ëœ3Þ€?Ÿø+ö­âŠŸ´×Àž ·:¦©maÛZ«ªoÔ5˯)b,쪥–If` 6I&¾ú³øÙÿ)Óí °²ý—ôˆmí‘bÅpTDUGúW@+á…n¿´7ü/Ä>*'íZgƒoµ F9O/C¶l ½±ö-ÁîN{×ôKssoem-åÜ‹ #I#±Âª(Ë{M1·ïÅÚ«þ à ã'†mü3®xZöÊ Í&Úé/ ·ƒEjn=| žyÖc©»læmTû4y÷Ü8úWô_Ï7üØKâ¿ø)—Ç/Û©6ˆ|Oq¼}Ñöf!þ*IJþ†kÌœœ›”·gRI+ ¢Š*FQEQEQEQEQEQE=¿ðZû[½âÁϬe¡[}F5=¼ËIíå#Ø‘ ÿ"¿ Ëkˆní¢»·mñLŠèú°È?•~oÿÁRÿgÝKãìÓs«øbÕ®üEà+í‹x£RÒÍj¨Rò’|²%rÆ £“]Ÿü›ö…Ñ~<þÍ·ûxŸÄþ µƒFÖ cûä{eòíæ`yežVßиuꦀ>ó¯Äø-ë¨ø[ðÒ2Fã¬Ý;-†˜¯Ù?hþ&ñƒµÁšùð¶»{m$Vz¨µŽôÙLà 7Ùå!%ÚyÚÄ_’?à–Ÿþ?j6z§ÆÚ^ïijiÊël³øm#†&7ùpŨ$h[jî* œ ç€?L¿fçI?g…²Fw+xWD Ž„pkâWÆ…ß4ëM_⟉ì|/g)‚Þ[é„+,¡KRz£5ñìùû~Ñßnü5¤EûI^kÞÐ$Œ>ƒ?‡ Ù5¢Ë´w3]Ï, ŽgÝ(òüZâçÂÙËÀÑoÇ:®©%êéöØy£I#û<Çð YÜ‚x 1À Ûm#WÓ5ý&Ë^ÑnR÷NÔ ŽæÚxŽèå†e¡U”‚¡¯!ý g¯†´ÇÃé¾üQ²’æÀÊ.m¦‚CÍ¥Ò«"O òª» 2²He#Šë~ø6ãáÏÂü=º˜\OáNÒä•I!ÞÊÚ8  Lò+Шùû†OÚßþ W¬ùwK/ÅO€3ÏÃ.àÖ #u|ÙK“Ðî·”ž‘²¿røÛÀ¿²§ü+á ž·¡ku!\Z^BDzžq8ẵfÃ#•²®1IÑëôVúÆËS²ŸMÔ­ã»´º¢šI‘¸Ã#«XF¯Íí/þ Ûáï…Ÿ´ß†~?~ÏZÜžÒ£¹“û{Ãáä[K‹Yc}ËlS8S&Ò`“1ƒó!Mª¤ã|yý©àšÞ"±ø]ûMi÷=øE4‚ßK×­ M%´Cî¬2Ƀò¨æÒr¬ ~éö/ÍûiðÏâ—ÃÿŒ~²ñçÃ=rß_ÐïÇîî-Û;XZ9áã‘só#€ËÜV÷Šü%áxzûÂ^2Òíµ­SŒÅsiwÍ ¨{2°#ƒÈ=AÁŠø3àìoû0þÑZ—ÄO„~'¸ƒáν§\CwáË™¥s á’6£nVhÐyL¿¼Œn}Ì@è¥Q@ˆŸ°Ÿü¤ö ÿ¯OÿN¢¿nëñöÿ”þÔõó©ÿéÔWí…ü7W7öWd¸–7X¦ÚËv+í<6ÓÎ¥~eÁ^]Wö7ÔUˆõ­,s½òëðMGI?b…̇p×ãQ¨Ü‚?_5üdÿ‚mþд •®™ñö¡¼ñ…”¾|V­áˆmí–` ‰<›{ø£.ˆ T òk¥ø!ûþÑß³ý…§‡~þÔ–^·¹¶˜Þ‚xgß*F.ofyœä ’Ø$šý$ñ÷Ä_|-ðä¾/ø‹®Zø{Eãîï$D¯)ÚŠX÷cÀwÁ¾3ð§Ä/ XøËÁ­¾·¡êJÍmyjâHeTrŒU‡\2}Á¯Ìø,t-3ön²øZ’‹ŸøÓW²[ †û‰#µ2IV1–*$c%œ8úûö'øSâ‚_²ÏÃ߆¾-O'YÓ,¥šî"ÛŒ3_\KvГÓ1¶dex$s@¿·ëª~ÆßÈPti>¦Dó5ù¡ÿ®½›áïÁ«‰ë)þÄÔüO{áýisòB$†ÖK;ƒé²G*Ìz+Zú§ã¿ìûFþÐPêÚ?i»Ãá=FíçGÃPEpù¾d0ÈÖ÷°™ÄX\ÉPØÍyGÃßø%Æ/…^Ö<#àÚRçKÐõòúÇþ˜æµ™ÀHb›PtY0«ó¨ ò¯< và11¥RóW‹Ñú?Õn¼Ò1ÄSr»¿OSõ«Çß| ð·Ã’ø¿â.¹káíŽ7»¼E¼§j)cÝUßøÏŸ¼5cã/j¶úÞ‡©+5µå«‰!•QÊ1VpÊA÷¿à _1ûXüñçö—Å×,´Ym‘ssxtÒ$7É䔘y@9y0¹ç¥ß±?Ÿüý–~ü5ñjy:Ιe,×qÜašúâ[¶„ž™ˆÍ°ã#+Á#šÇ‡•*Žœ·_×âU*ŠqRGÔÕüøÁ'u ;Ä_´íñÇ_ºŠÒ4…åžæâEŽ8£Õ/¦»•ÞF!UGÙbH5ý×â7†¿à:M—Œu¹5‰ð.«xf“EÓ­¬·Vé#IW3´Ï1Æ|–Ï,» ãC{ZÕ¤ÿ‚˜~ÑzW‡|9’~Ïï…æ£zèV-W@BCï½Ó·¦DLìÄböiUUB¨À:\Wï‡øMàí;À?ôˆ4=JË‚ÚÀÙ™ŽYÝ.ìK1$±$æ­xëGñ7ˆ<¬hž ×Ï…µÛÛi"³ÕE¬w¦ÊfY¾Ï) .ÓÎÖ ülÿ‚Þº…¿ #$n:ÍÙ¹ØgùŠýWý›$ýþÉÜ­á]‚:laÁ¯Ìߌ?ðKOŒÿµ=Sãí/wâY´åu¶Yü6‘Óü¸bÔ4-µwPNs_AþÏŸ±×íðïÃZD_´•æ½àHÃè3øz “Z'ü»Gs5ÜòÀ˜áv}Ñ€1@¢õø‰ûÿÊUÿh¿úðÔô¾Æ¿nëñö?ÿ”«þÑõá¨ÿé}~ÝÑEAr’Ém,p>É+z1ÀÐÅÿµ¯íÓðƒöOÒZÏZ›þ\ǾË@´}¡·–K—ù…¼GûÌ 7ð#`ãó¿À²wí/ûx²ËãGí›©ÜøSÀ‘7¤øfØ5¼­ rp¶M´n>ôÒî¸p6®Æ\~Ê_ðN/üÖGÅŸ‡ü,¯Š—R›©uÂóÚÚ\1Ü^Ÿç–\óö‰Fü€Qc9ÏÜ_ü3âÿ|%ñ„|ª ĚΗui§ßdƒì×3FR9|ȃH›IÎä‡QÍ|ûEþÜ¿?cíNøðG‹Å^7Ó¢‹MÓt ó[iÄa"K§BÒ<™Æ Res÷Ên |à·üïâÇíã8h?ÛûX¸Ô.çÄ–~Y<±9Ü‘\yD-¼C¯ÙáÃs#†Þ§í?Ù'öøGû,Ú§ˆá*ñýŸµk÷¨ ˆÎ>u´Œ–!ÉÉÈÙ;œŒ(ûª€!¶·‚ÒÞ+KXÖ(aUDEUUÀ~:Ákõhàýœ¼¢g÷—ž*†që¶ÞÆíOë*×ì>?¶åôß·/íµà_Ù{á­Ãê‚L©®]À Ck,’©Ô¤,>SäCq)$~ü˜Ç'ØoÙH¹Ðe¯„šUâç‡ÂÚ9‘[ª³ÚFåO¸'ôEU±±´Ó,­ôÝ>%‚ÖÒ4Š(ÐaR8ÀUP;­PEPEP^ñÃZÆ›uañ‹Á¼AátasnœGKcº{cެ¼¼]pÀàE{åÕƒÅ:5Ò¿uÝ=Óõ2­IN<¬Áð¿‰tøzÃÅâãOÔ¢Y¡q× ÔÙ”ä0ìA½_2éŸñcþ%ÿÂ>ÿºð?Ž®YìOHôýYù{EŽãïF:È šúj´ÇáU9)Sw„µO˳ó[?ò'UÉZ[­ÿ¯0¢Š+„Ü(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÑýü¢Š(¢Š(øý´?äí~/ÿØÑªÿéK×½ÿÁ8¾~Ï~%ø‡Cøù«-¬z~ž³é¶Mt,ÅÜŒåeo0ã&ÁØNâÝÖ?ü+àOÄ¿þÔž=×5-êm#Å:¥Æ«a{.ðKãy»C¨ 2(Àó‘ž„WÿðxþW÷âOð Ný¢¼%à|oñ—„>jŸÛ>Òµ a±º$ã H8p­•Ü8lg½~ŠþÌŸ²ÿì{ñö3ñOÅ?‰¾*[X¦ Ò9½X[O{u&ÙVÜó/›… `—-µyù9ÿ׈ÿèwÿ~$ÿ ?áñý®ÿïÄŸá@–‘Á-Ü1]IäÂî¡ßÚ¤òØïÍ~º~Úß³ì‰ðŸölðo>xnüQ¨½ª¨[åº:ŒRǺi `›9îžM~OÂ5â?ú]ÿ߉?ÂøF¼Gÿ@«¿ûñ'øP%·ÿ׈ÿèwÿ~$ÿ ?áñý®ÿïÄŸá@”Vßü#^#ÿ Ußýø“ü(ÿ„kÄô »ÿ¿…bWöGÿèÿ“/øaÿ^éD•üƒé^ñ¶¹¨A¥húýååË„Š(­¥wvn-fÿ±¿Ã|&ý™>xÅ‘}ŸXÓ4ÕûT]LRÊÍ)Œû®ìq@MQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÜsÍfHxæ’ãÛTcáÝ—ý~ý×ûÿÇ›öå¿™®ÇöÓçáÕÿ§Áÿ šâcùR>I$8·õ¯Ê±’QÏ#sïðQ¾Q?SîíOO‚úÝ£e‡~õäZ¦™5Œì¥ICÐö®‡âÅO ü>ð½ß‰uy¶Cm~™'øÓâø(/Æïˆ> žÓàþöí>)^?1“ Äu+í_s™åðÄÆðÜùL6T,×}3T¹ÓeR§ä‘^½§jßÀ®§“ÔWã‹¿i^ øq§x‹UѵK©QdSv2z/µ}·ðâN¹âÿZø—XˆÁ<ã,„mÇNØãà15p’¯ÂzXš1Ä.h-O¨5Í è‹¢áÇ¥y5Ý´¶“¦px¯mÓµuU?1+3]ÐâÔa,ƒ+»3Êã^>Ö–ç&*Röu68m_–ÆAÄ”ÏzõH&†î 邼*êÖ[)L2Œ=k ÐuéleXç9ºW”fn“öU¶;³½I{JF÷ˆü>»eé×ç_21áÐô¯|†x.âea\ˆü6 7–ƒ¸Ñ™e©Þµ¿0åýÝ@ð߈ 0µ¸<öÍzEäd_ž´·ÅÍàÏ‚[ÄšAîVD\6\újí¿d?Ú _øÉàïí¢Ã2¹@±è=ë|›Ôkt0Ì0‰ÏšÓþ!ðïžsÃz ófÒBŽ0Ë_A’.z©®Ä>YÁ¸¶n¼Tæù2•ëS/.ÌÜo œÖ‡â leNÄ¡õ¯LakªÚà†áϤ›$ÅtZ»%„‚;‚LyÅyÙViËûšû3³€æýí=Ækšº|ÆX†c5…ÃÛH'‰¶â½±Å¶«iƒ¸Wø£L›F†âæ0HE,8£0Ë%Njt¶+R^ήç§hÌwÑùNß:Öµ5 6B€nÇZü{ð'í…ã½[ã‘ø{-²%Š»ÃÂ~úÛáýz=BݧG~+ÙÁccRƾ瓊øIΙç:¾“6›9>Lðiº^«60(NÓ×ëš®›¥lcaÏc^=¨é³éÓ´.>Pz׉ÀTÃMT†Ç©ÅÆ´=œ÷=~ÃP·ÔàX1Ç5Çø‹Ã„wj¹>•ÉiºŒÚt Æ~Ry¯ZÓ5KmRÛŽ§¨5éPÄÓÇÃÙÔÑœU¨Ï >hlx®%†N>WßxwÄ{¶Û]7#Ži§¥\iÒ”uÊž†»½ Ä‘L«mpØ~ÕÑj0jP• gúV–6Ÿ´§º<\>"t'Ë=²½žÊU’ ÈëZ6³¡ ùÇQ^]ªéré“•Á*MV³¼šÎa$'§&¼L>®§³’Ðõq˜x×\Ñ"ý£ôø%øU®\m£·cúŠü™ø;“ñN'³·óú±ñ»XQø=¯;M³?_–ÿà´¸_¦öþb¾[Œ9*bàáÔú^r†¯?Cö7Â3\™J„Ïá^•Ž=ê…••½¢-qšÐïŠýk-úT’“?6Æ×U&ä…¢Š+Ò9Š( ¼Ëâ÷Ž%ðoµ‹óõk’–Zle¦¾¹;!P;áŽâ=צ×Î'þ.Ç\¬ðÿÃEú¤ÚÍÒþGìñ~*æ½²ŒeQÔ¨½Ø«¿;l¾nËæsb¦Ôyc»ÑŸÉjzoÂßÅðïÀº_…Ãù÷P!’î|äÏw1ß<„žNç'ç«Ðh¢¸ëÖ•IʤޭݛS‚ŒTVÈ(¢ŠÈ°¢Š(¬_YëZ‡µM?ÃzŠèú½Í¬ÑYÞ¼é-n Å3@Y¢7!ŠPØÆFs[TPåOÀÿø'_ÅO‚ÿ´ß´*|x}kYÖ§‘µèåðäqRÖæt¸¹€»^È!ó5Úè™hÀÅ~¨Ï70Émq˪QÑ€*ÊÃx Ž¢¥¢€?9ÿcÿø'Ÿ‡dŠ^.ø“¥ø­¼@¾ µ’ÆÆÑì>ÌÖ’\,æ37Ú%óŽ#wlLíÎ9ÀûÿÄVzΡáýNÃÚŠéµÍ¬ÑYÞ¼å-n Å3@Y¢7!ŠPØÆFs[4Pâö‘ÿªø¹ |Xºøç£þÒÛxêöâêæ]Pxb6”Ëx¬³0VÔ .åv\)€+Ð> ~Àµ/Å/ ÝxCÇ_µ¦«©h·Ë²âÔxy Štî’ˆ5÷©î­{Šýa¢€>^ý¿fm7öNø7oð¦ÇYÿ„†¶Ý_ÝjfûÚ&¸ äù³mÙFŸ|çnxÎaûJ|›ãïÀïü ·ÔSH—ĶÉ]É™ad™% P2–å:dW¸Ñ@~ß±¦“ûxYÐ_VÄ^ ñ ฽ÔRnWe¼ …œí.Ù'–v특ûrþÊZí}ð³Gøu¦xŽ/ Ë¥ë0ê¦âkfºWX­® òÂ+ÆA&`sžØÇ5öà³/À/~Ít„¾]63%íæÏ-¯/¦;§—$ÍÂITU\¹¯|¢ŠüËý²?`~Ø2µÕuŸŒG@ðÆŽ?âW¢.‚·+jòGÏ#Ü ØZg‘Óp,ƒb«Üµ ö%ý¯<;áû è_µ¾¡c¥iv±YÚÁ…í”CoãD"÷p Šç/ÙÏ⮯ðTÔâ“UðïˆC´ÖvÒÈ¢âKWv´«%YZÄ(u“ ·þ;ü>ø½ñÃÖ:WÁÿ‰Ïðºþ ƒ%ÍìzM¾¬×+䄸xÄ1 ½NxÇzüªñ'üïÇþ0ñÄ¿üMûFÞê+–â+£©É ·Ú„Ðmòd˜(cÚ»6ãh.0+÷²uíoMðÖ‹}â faoc§BóÌçøR1¸ýO犨Åɤ·i+³ó£FøQûDh&´øñãÏÅ [,—±I¢Ûé²éÚe¼€Ë+\Å4²È×y ã–ÏjúGö¨øçá_Ùsö×¼oq4V7vmc¡Z€36¢ñ2ÚCwU 3ãîÆ¬{VÏÁ}\¿Òõ¯‹ZÔK‰&Á?¼mà_‰÷Ÿ?aïEðçÄÚ-¨h—JN‹z·º…TFŽÃ>SFèæ3~©Ñ@¿‚_Åòx7B“âZÇâw±¶:¢Xîû"Þ˜Ôΰo,ÞX“pBI$c5ÔQEç±|'ømĉþ/Çá»!ãK‹4°}[ÊèÛ&pûppXaŠ€¤•UШ Š( Š( Š( Š( ΛØRð§Æo|nøañ[ðvµãÛ«›‹ä¶°ÓîP%Ìÿh1/Ú¢“åWÆã­}«ðÏÂ~+ðg…ÓDñ—Œ.üq©,²Hu+Û{kY™ü±˜íR8ðŽÜžõè4PEPžë¿ þxŸÇÄ¿ønËQñG†c–-7Pž óÚ¤ÄòÉã9RFS-´ÍŸB¢Š(¢ŠøëâçÃxkãï‚?hs@³½‘ö-Íä𫽌··ˆÇî•a峟º§ŒkìZæÕ<#¬®ëMR…Î2PŸºëþÒ0 ¾àWŸ| ñ^©®xR_x¡¿â¥ð”í¥j@ž]áâ)Æy+4x`ÝÎq^­T«a•EñCGæ¾Ëù|?ø ÉÉQÇ£Õzõÿ?¼öŠ(¢¼£¬(¢Š(¢Šðß‹_ ¾%øöÿOºð'Å]Oáä‘:M……ÚÜ»0!ÜÞC+)QÀ @õ¯ ýŸ?bk|UñîÿÇZ—Œ|Kã )­/ÞöÚÚÝ]¦žÚP-Õ6a‚x¯¹( Š( Š( Š( jï~Ö?áÐüû9ø»Kð6‹¨%À×uiÄŸÚP¨("K2ŠØÞ ä®ÇF$\ó¥û'þÈ ÿd¿ ]hþ’]c_ÖeÕµ«µQsy"ôP|¸T’R<¶ %™›šúŠ(¢Š(¢Š(¢Š(¢Šä‡&"./ÚǦþkþù÷=îŠÀ𿉴x{OñF8¸Óõ(–hœuÁêve9 ;Eo×™88ÉÆJÍQ’jè(¢Š‘…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÒýü¢Š(¢Š(ã?Úöãý™¿g¿Gàωú»\k{W±´¶ûd°#Œ©”p¨Xr9Ç8Áó¿ü={ö!ôÔ¿ðP¿ü]?¶ÝíÕÿíuñv{ÉZiÄšŒA˜ä„†SkôTP£Ð à~ ~Ï¿¿h¯\øSà÷‡¤×õ ( ÍÀÅo1´4“NñÆ» e@phúBÿ‡¯~Ä>š—þ ÿ‹£þ½ûúj_ø(_þ.¿—¿øCľñ>§àÏéòiZÖ;Û]ÚÌx¥C†S‚Aö G â½›Àÿ²wíñ#án­ñŸÁ^¸Ôü#¢ MÅâI ±XéLP»‰eŽ[ËFÇàhúÿ‡¯~Ä>š—þ ÿ‹£þ½ûúj_ø(_þ.¿•”F‘Õf 9$žÕôOÅÙ7ö‚ø1àmâGįO£x{^Ø-®ZX%*Ò.äY£ŠGxY—$Uôë‘@Ðïü={ö!ôÔ¿ðP¿ü]ðõïØ‡ÓRÿÁBÿñuü«Ñ@ÕGü={ö!ôÔ¿ðP¿ü]ðõïØ‡ÓRÿÁBÿñuü«Ñ@ÖÿQý‰5^ÖÆ;ëÍ-¦p¢æãK1E@Ú€>ТŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¤=)iÙÅ >/ýµ>_‡Vl{^ýוþÍ–w¶ÞmT1X¹b=5ê¿¶·ü“«0z}¬è&©~ɶ±ßü>kI×1ÈdSø’+ò¼~Ú竱÷¸Î[~gçßíµûX|:oÝü<´¸í!¹w.ÓÔsÞ¹Øwöªý›¾øN øžÝW¼¹“ vã2?ÉÏzõ¿Ûïöfðnƒá«Ú¿j™ŽNÜžA5埱OìÝðûÅþƒÄºµºIsopäe?#ñü«íðõaƒ§iŸ5R.¼ÕÕOŠ¿~xgÂV:猭­ÛN¼+än¶qŒÒx;YðÖ½ Ç«xN%Kq” ¸öÅ|Sû}=•¯ÃÝI·,71…èêûö`Ñâ¾ø§)bÓŽ€VY†ëT•H1áq¡QÆfU¯í/àøëþ;«ÍºŠç(X``Óñ®Ÿâí™ðká©X¼E¨¨¨bÔpF{×âíbÞ"Ò?ie¡)WŒÑ–C0÷/ŸØËÃÚ–m¯øòöKû»ØÕÝ%,áC ñ’Gzà UáŒÙ¥zKÛ‚>¶ðOímð3ã\æÃÃ’}«°gRIü+Õ®mÚ)`Èß2°éƒ__µÇ앦|µ°ø›ðÇRšÙ㸌¼q³ ÆNxÕúiû+ø¶ãâ7Á;=cS}÷vjÊÌzŠ¢«4Êሴ¤ÉÀceEòÔ=µ ¾…ž c»Ð~øáØ“ïÉýk\3ÙF4ê°Åáy›•3éÏ|gø_ñ’?µx3QG—ÚëÚº™¡xÉ—–Îx¯Í{öøƒðã╇Šþj2-\"32® à¶:f¿V´ï êSxbÈê‘ãPŠ`õ`9æ¹³œ•M{ZFÙvbãîÏb¦…­Ëc(ŽvÊ•Üë©k«h7<†Ý¯%– v†A‚+ZÛ[k-6î)ÛäòÈÍy™VgÊý…}ެ~ ™{Z{Ÿ…ßíM¯ío<@|¢YëöêÆâ[DŽPøÍ~7|+‚=GöÁ•:«¼Õú+ûX|J_ÿîµDlNêÂ?Ê»s\%E8ΊÐÇ‹ŒbÔ÷=»Æß´×Ã/†v^oŠõ`É€<}kÃt_Ûãörøƒ|t{ Hyîväºõ¯ËoÙëö{ñGí'®Ü|Bø…¨\&žÒ³$EØ¡RÇ.qÓÚ¿A|Yÿøøaâ/ ͆%6„(J<@£íÊk֡ЧQ{ŽìóëR”´Š>¾†æÎòÞ;ý>e–ÞPJœðzV•†¡>Ÿ*Ê„$õùû/üMñ—ÂOŠÒü øƒró¤Ò:A$¬XíBvýìöÅ~Ž|qñ%ÏÃßÞkvq<îÊ|°€·ol×Íc2¹Ñ©ÍOcÚÃc£RŸ,÷=7Ç´OÃ_‡–oêÅòä®àOzøßUÿ‚†~ÍWZ˜±·¿"rvŸ1šø3à7Àï~ÑÞ2Ô|cñòê g}¹p»w°) tÇjýÔ?`ïZîˆm“m6ÌxV×<öéº5â©Tjç“(΄¹ÒÐõ_|SðwÄ+A¨ø^õeaþUô/‡|D[[¦äñšþw47Týš¿j;/ø{W{í*òS•ó ‹‚ ©Å~È|Bñêø'áäž,ùr˜·}p¼‰R©ƒ¯hìÏIΚwg¶üTøÕðëá–”÷¾0¿ŽpN N>µñç‡?mo€¾=פÒ<7¨4ärë´þUùÉðgᯋlŠWúçµ9áÐí'pˆ$`…C( tÅ~’ø‡þ ÕðÆãÿeðôͧÝÄŸ,ÑVfêA¯ƒ¥Š“÷‘äS­<4Ÿcóö³¼¶ºý¦ü%¨YÈ$I$Œ‚¾› ¯èá·×th¤?¼ò¿˜O‰ ñw€?iMÃ~'‘çŠÒeŽÝÜ–%T0'=…B~×ÓÂÿ l5©_jÛÛn'é“\XŒSÁ¸SèuÓ£Mä} ã¯x[ÂúL×Þ$ºŽÞ8×qÜpq_¾ ý½g]}íìî[nC¦=+áOxËÇ¿¶gÆóà'Pš×FµœÅ+FÌ£j–^pG¥~hðN…>M2ÿý"铿™/¸Ž¹9?­zœøçö+ø©e­xvòk ÜLÃÈÅNOBHô¯ÖŸøÞx ßÄV¬ \[’qØâ¾vt稹uGµxbcÊ÷;ý¤þkž!½ðÔ’;ëFhß,8 â¾oñíðCAñLž{ãö¥pœ2à“_’1Òtø§ÿÄo |]‡÷v°Ðu£Ð [¦Í´Íاêzá±_E×-ã éþ:ðŽ­á P£j¶ïld£0ù{£a‡¸®àg‹5x?kþšM#SRr~Õfv'¿˜›_= 'ëVýö5:ÃÝ~X¿“ºûŽH{•\zK_Ÿ_Ñýç±QEäaEPEPEP_7üPfø™ãÝ#à­¡-¥[õ2ôû4M›kF>³È0à„‡ìþ7ñv•à? êž/Ö›m¦™ JÀ3·DÚv!WÜŠà>øGUÐü9uâŸ/üU>/ŸûKR$sqû›qžBÁÇv8¯_/ýÌ%Š{­#þ.ÿöê×ÖÇ#ß’¥ó~Ÿð+žÎª¨¡ª0à)h¢¼ƒ°(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+çŲø¯¢üP‹÷z/‰¼­\Ç ’“þ…tßî¶bf<"¾®_ÆÞÓèýë-༯ˆòh¼’<˜Ú÷é·üEüɾ·Ûk|/NV~ÎW–ü\ø¹á_ƒž—Äž$—|¯”´´B<ë©€áv«1áG© O|øuà‡P|G:„z•ž¥tè aæ^I¸ ò»OúÂÃäèFì)üÊðdž~&þÙç×5ÙÚÛI¶`..³ØÛç+ož ‘Ðu'.ÞýyÖì¹haW5Yl—O7ýyìx|á¯Öý¦eœ·GE¾vôrkx.·¾­ôZí÷÷ìÉñÿWøã¥êÇ]ѾÁy¥J3< ßd‘%$¢bH‘Þ9n3õ%rÞ ð_‡>ønÏž´[=>ÉvªŽY˜ýçvêÎÇ–cÔ×S^Ö]Fµ:1…ysK«>Šq¸,FaV¶]GÙÑoÝïeÿ{t½ºQ]§Ï…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÓýü¢Š(¢Š(øŸý´‘“ö¶ø¾?ð”j‡ŸCpäÄUßÙOö»ø—û"ø£Vñ'ÃëkF=vÙ-¯,õÝáDÅ¢pbxÝY 60ØÁ ƒÛúý©?à— ¿i/ˆ7-uëÏëÚOím \Át衦6d+!PØ8ÉÉ?1ÿÃ|ÿEZÿÿQòE~ |Wø›âŒ¿uï‰þ2xßXñ Ë\Üy+²%b ‹“…UI8kꟄðPŽ?~jŸ|%›&‘|·IåÄ.×–kxœ!+"ÆrX•.´’}1úyÿ=ð_ýkÿüEÿÉÃ|ÿEZÿÿQòE;Ö÷Ú\EunÛ%…•чfS:û‹öÿ‚üký£~è¿|ik¦Ziz[C$óYÃ"Oy%ºí¥/#ªã©«–ç§úÿ=ð_ýkÿüEÿÉÃ|ÿEZÿÿQòE;WôOÿ=ð_ýkÿüEÿÉÃ|ÿEZÿÿQòE;WôOÿ=ð_ýkÿüEÿÉÃ|ÿEZÿÿQòE;ý’ÿÁ:•“ö0ø`NŸ!çÐÏ!ð®‹ÿEøikª[\k¿µMBÂ7[x¬a·yuQ)’M¹õÚkösÁ¾ð÷Ãÿ i> ð¢Øhú%´v–/Hâ‰v¨ÉëÀä÷4ÒÑEQEQEywÆïˆ±|#ø?ãO‰Ò¢È|1¤^ßÇœ,³A 4QŸ÷ä ¿~h~ÝßðR-Sàç‹À?ÙæÊ=sâwmºŽÆ{€<«{x>}ÑÜ(„…*ìYSç_þÆŸðSï¶Kâï‰ÿ¯<÷ê²®Ÿ>¯y©»‘ºÓO m éò‚teb²?àßÓâgÄO~ÔŸTêúŽ“tÖ¶\aËj— Ïytsÿ-R7@§þš±êÑ 9?¼ÿHýˆl$ø’Ÿ®~ øONP׎דëVðEŸùx¶¿Q4Iýé!áGWZýgý‰ÿlo ~×ÿg×m­âãƒYÓCïXä‘IŽxIùŒím›¾eee9ÀfûîÒÖþÖkèRâÚå9b‘C#£Œ2²ž àƒÔWÈ?³oì3ð7öYñ.¹ã†Ñê2jÚêÍÉyvÒG ¤’‰E´Q HÊ!U ò+ÉÇßä‚ö5Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ =)HÍ ÒÒ€±ñ‡í¬âÝYú{ú £ö=ÿ‘%ßý “öÔÉøuiÿ_cÿA5KöN½‹OðË)ÇÎÿο2œùs»½’>â„[ÊZ]ÎSþ o„Rdt'ùñOØL¸ …Ê)+çÍŸûî½3öú× ¾øO,Jy9#ò5ü’¹ø;åÈyÿô:ûŒe8bi>Cåðõe‡ªœÿ‚‚JÃÃzdƒ·)Çç_q~ÇZ¼süÓà‘°È­ÇÔ ùþ O£=‡ôÉcæ6¹Cÿ¡WºþÊ÷RZü!±’#† ýxžÒ¦*ûŒ©,LÛGÂ_ô{}oöÄŠ ÆJHÄ£-~¶éöc¥X[8ÛˆcÇÓh¯Éoê-yûc[É7Þÿ-~àI¢Å¨h6sD1"œÿÀEuæ˜?¬ÁU¦c‚Å*tÙùçûiÎG·†ô ¼øn~Ã7ÿÃ>j- a•®xôéX_¶ý»Ûü2‘Á†ZÅý…ç™~^ÚÇÈ™ægÔŠåË1¾Æ—,ûì2©4â|­ðÃvz÷íW¨ßH7Ü'ŸÉëÕkö"{y-‰Æ~í~+é¾!Ô~þ×âÿWC“zÒ'šÃ —(~ìG}áÿè–ÚÞ™y‚hÕÁ/qŸZêÍòÿ¬%V‘ŽìféËcáÏÛFT½ø?5­òùˆ®‡ŸQ»ÐÁ:’)~Ï išEÇ·Ë_:Á@>0øwà o ‹”šúI„!¸É¦}kÒàž#{?„s\Æ gp=ÎÞ*2ÌTèÒJ®ÂÆaÕI·Ló¿ÛcöñW‹|Mÿ ;á1’=^»jqO@Ojøz×ö–ýªÿgï+JñVŽ×)lvî>p?_§¿·>£cñÝ~øªÑm-%Þ®¸#¾}kìÿøá׌¬¿´æ³²½3.ì°‰Ç?PkÓÇÁJ qÎJ3’|ØüÞýÿà§žñn¥oáïˆHºuÔ¤ ÜqóSë_¯ú6±a¯iÖú®šâ[k” ¤tÁ¯ç[þ ðÓáׂ¤±Ôü°ZjßjƒÛ ̃'ä¯ÕOØŸÄzØøE¤Åâ6bÆÚÎNqëÍgK0„iÇ›AÕÁÊNð>¹ñ‡–éLöë‡ïŠò v¶Ó®ÒO•‚ú:9d §!…p6ÐëF½–‡Øk‹4ÊGí©X ÅÇܨ~ |¾[?Û´§*dš½Ïþ ɬÎþµ†Üæ#céÐ×Î? Qâý­dLaÒI¿Jú›þ à_Å¿ ÒþÆ3;Úäx kJ8þY*S"¦ Ê.¤“¾ ~Þš'ÃOZxzßMbcH÷¯e²ÿ‚£ØØ±xìܸÿ&¾¦ýŒ_àᆖz= KOb—ΆåÔ`òËž¢¾•×¾|ÓÜ·‡´¯ rIŠÿ|Ôcpñ£j©^ýƒ‰sýÜ´?|EñÍ>1þÒš´kI š7@ÛG Ü×ôY­ë°øEe®øö5kT¶ âO^}käm3â§ì±áßAáÍ?J²m\>Õò­£lî©T?࡞(Ô_à,—ÊDTü¨1´öÛ…ÇB´y$¬sWÁU§.dô>;×m_j"ºð×ìÿá¸n-c•ãÜ¡°pHþíMþÖý»Ào¸Hmô ^¤e ±^ÛFq5*mòì|ÃûiööžšêV‹w$šý8ðÿˆüÃökÃÓšüqý„>'iºtú¯Ã½jU´½´•âU”ìÎ×#¾=+õóUÓtÈ$¾¸¼…5'w˜¸þuóxŠÕpÕۊѳׄiU¢“ÞÇåŸí½ iÓþѾ¼ Ò'#ýÃ_lüb°»Ò?gwhÄ(¶ÆGÑ«òŸö„øÝeãÏÚsAÓ4—Åc:#·QÀeëøWîGˆ¼.Þ3øº, ¦µÊø ¯¦«‡†"œg-ÏyQ“Q?œÙ³ö’Ó>øÓ]ÔÞ¹»žgrÞ™r}E~ZÁY4,-Æšá€üÿñêù×ö^‡Á>ø½â|PÑ­šY.¤XZæ$ÁaÇ.§°¯× _…¿¦ u‡´§B8ÄPÏü¼ìFcõjܲ‰ÙKí©ó&~7þÖ¿¶þ‹ñ÷ÀÒø~ÏOh®Üm€ç<{šûãö:ÝïÁ Ú„n© $ ÝyV¯ñ§‹?e?†0B<[¤iîpªÖ Ÿ¢¡5ô?5_xŸÀòê¶Ž6xX¨H„C¡ÇébððÅÓæ‰ÇB¬¨OSðÓáõ´RþÖ7GïE|Øÿ¾š¿{<3â6ñÚÜ7!TøWá·ƒì^ÚÓPŽ!òý¹Çï5~ÂBíÆÊpÀ ùüV.¦²Lö(a¡^“gUñ¥#Ÿáæ°ÄdƒøŠü€øo×Äû[g9-!Çç_©ßõ¿;áÖ©o/ß10þUù™ð}CühÓÕ€ »v÷ñ&£Œ}ÝÞ‹×þçiðWÁ—þ ðD'^>gˆ5¹dÔõYÞk˳½Ôÿ×1„¾Zõª(®|V"UjJ¬÷n攩¨EEt (¢°4 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùÎóþ-ÇÇË}@~ïEø“¶—²G«Ù.bcØyÐå@³ šú2¼«ã?‚î¼qðþÿOÒIZ°dÔ4ÉïG}h|ÈJžÅˆ)ŸF5èåubªrTvŒ—+ùìþNÏäsb Üy£ºÕ^kCÕh®#á¿-~!xGñ…  ý£´±ùe:ü“Gÿ2þÛ×jR§7NjÍ;3xMI)-˜QE™AEPEÀ|Nñշßj(–?´\D¢+Kq’×rÄ rw9Ç dö­hQ•IªpWoDLæ£)l-ñWü]‹¶‹÷žð;EªjçªO~Ã6v§Ô Ì®9€y¯¤kË>xçÀž ŠÛX“í:þ«+êµÁÁ2ß\Òr:„áasÞ½N»s*ÑrT©¿v/>ïæÿ .†h;9Ëw¯ù/—æQEy§HQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE󇎿âØüZѾ&Åû½Å^V‰­öT›þ\n›ès1à)Í}\¯ü#¦xóÂZ¯„5›]R…›(Ç”‘ÚF‡¸Áüñ~¥âI¢ø¥¶ø““¾—ª<´¶ü$Ù=Vhðẜt¯Z·ï°ñ«ö¡î¿O²þ_þqÃܨãÒZ¯^¿ç÷žÍEy†‘ñ‹áçˆ|l~ø{UMSVŽ'—ìß¼†%ˆ€CJ>BÙlaI#¾+ÓëÏ­‡©M¥R-7®½Ž˜TŒµ‹¸QE‰aEPEPEPEPEPEPEPEPEPEP^ ñ“Ã:ÆŸs§üaðD_x]X\[¯QÓîžØã«(ËÅ× 8‘^÷Eu`ñNE5¯uÝ=ÓõFU©)Ç•˜ñ6ã/éþ(Ð'~¥Í÷Áê¬;2œ«Ä[õó.›ÿ?âgöþëÀþ;¹g²=#ÓµwåàôXîz è€É¯¦«\~S’•=a-SòìüÖÏü‰ÃÕrV–ëëÌ(¢Šà7 øßöÑѾÝ|5}[Ʋ‹]zÜ2èòB¹–n¾V8Ý ë!+x÷öTñýç‚ÞŸžý,~£Å™•.=ˤ²Š’…j ËØÉ¥Ï’_Þ¶Ý›q{©?ÙÚ+ÃÞ!Ñ|W¢Ùø‹Ã·‘ßé×ñ‰!š3•e?¨ ðAÁ €El×é±’’M=äZ´§NnVkFž5Ñ…QTfQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÔýü¢Š(¢Š(ùvý¼ÿo_Ú¿h|>ø}â»ÏøwÁ÷òéç¿“$Ò[’Ë,‹ó6ç ´gqÆy¯Šá´¿kú+"ÿÀù?Æ™ûhÉÚü_ÿ±£UÿÒ—¯{ÿ‚qxöRñÄ¿ÁûQMf°E§£ijR¬¤”¹ó˰ 6ù`œ`±êx?ü6—ícÿEcÄ_ø'øÑÿ ¥ûXÿÑXñþÉþ5Æ~Ñv_ ´ÿþ2²ø-7Ú<¡*é’Êaã!¹(! ä®+ôWödðŸì©~Æž)Õþ2ÞZGñ PóYYudU?cHÍ»åÆ Ćâ€>ÿ†Òý¬è¬x‹ÿäÿ?á´¿kú+"ÿÀù?ƾj³Íw½,¶å×Ì+ËÏÌG¾:Wë§í­á?Ø+Gý›<¨|º±oÊö¡~Å3Is4&?ôƒz¤œz†àq@Ãi~Ö?ôVÒ…W ³™1óßícðãż Ú„­üËÁœƒŸCé]?ì-àŸ|,ðð׌`÷fi[¿!›=ëë¯ø}âÝÙäz\OŸ$ry ïXªØ'ËmyP§‰2Üùÿöåø/ãŒÓl<n.+…wêxô®·àÏÃMÀ ítmr Ê.ç®­}/áϬª°\7#ší¥† ÈJ¸ ¬+ߩɣu¹ãBsÃT×cñ3Æ_þ&ßþÑpøîÊË:b±>`¦à}=«õ÷Â:êɦÚi×D,ñDŠsê+]Ц°v– ”<ú×,’6èì®ÆÒþ#¹&Œ û‡#®kám#àí©ðþWðïƒ&›QÑØìIe-•_m«Ž•ûñ¼RaþVSÒ¶´vk Õ]ŽÌþçeyĨËÙÔØïÆåêkÚS?üWÿ÷øŸã¯ ·Š%ƒÆÿo§–h8·lÎAîê+ô·EÐlü/§Á£YD#ŽÕD`Œ¯¤¢š¨C¸7¥q^#ðà—7Vƒ :×nq—º´ù齎L»Ë.I™Þñ”EµÃ|‰®ëQS{¦L|Í"+Ä^*FÖ^ÕØh>%kb¶—'+Ú¹2œç•û:ÇVg—'ûÊgåo…¿eO‹Zoí?.ìBi%å!ðÙÃtí_ Þ#ðņ¹¥>…«Û‰c‘pU‡­}5 ±ÜFàÕÉøƒÃñÜ©ž‡ß›à[V¤qeØÎOÝÌüAñçìiñÀ^&ŸÄ¿u”iœ¹·L÷1Ï`OSY|2ý½üeÐuèžÖɸiTÉœ~+Šý…‘f³”«eXìü=â7B-®[å'½qeyËŒ*§^;.N>Ò™ù…ðö O†Z²xׯ—_ê®KæPÒÜžp=kì_ˆŸ´ˆ~¸ð®¢6Ã*•21_ZÝZ[êV¥̬8¯&Ö4ytÙI#)YfØ:”ä«Sz-ÄBiÓ©¹ø…qû"~Ó_¼G6£ðJîk«¤fòØ0I8ùWÞ½ËÃ?²ÿí#ñ•ãO‹ºÎ‘2Fx#¸ùÖ¿P4ÝVçO™dŽÐzk×tV-FC žµëåÙ¤1ù%£8±¸)Ñ•ãðŸ…?ÿaŸxâ&«|=±ÁmåùÓw1 2N:×êg[P°ðžŸ¥k<Ä„$Éú;TÓ"¾”›±¯Ôôù´ùÌr)Æx5åfôªÂJ}Ý–Ô§8òËsó·öý„î|I¬¿ÄŸƒ—Òiú¾L8Ÿ©Ïõ&¾r´ø ûkxš3áÏùÖšj†hËï+ëÊâ¿hô½Rm6ä:7ÉÜv¯]Óµ }Nà‚qȯC§‰‚„¾$qã0Ó¡+ô?ž ÿØ;ÇÞñèQ6 Ñ²5̲› sÐz×îÃ=B}/ÃvƒªŒK AƒÖ»={ÃÅÃ\ZåO\f¼äù‘JU²®Zò±xÌFv–Ç¡‡ÃÒ¯ 7>Zý¨ÿaý'âÄÿð™x2åôíj"]L Í¡õ¯.> þÜÞ Œé:D^Y/Ê%bû±Ð+÷_þ!10´¸õ5èÅ"»„¯ÞVôjÒÅSæŽþgRÃK—¡üúxö#ø‘ñÄ–Þ'øË¨\%ÛvÁN>£5ûaà +Fð·ƒâð–”‹A LœTÞ!Ñ&´ÍJuÅs0Í$.³!à ùå™WÃÕåžÇ®°TëSæŽçÃýšþ'iÿ´}ßu+][Æ•_ ÷I>دÑ-wïj«qn22+¤Ð|D·(°Ìß8®µãŽâ"®2¦¾‚¥:XÊ|ËsɧR¦V{+xâ5ŸÃ„l1ò+óãáB*|q±Œ*»1_§¿ôoáÛë‹qÁBM~ZøWƒã-´¨9IOóù{‡–OŸ¿ù¤dµc[S—·ùŸ¸1‘娊q\¿‡õíòÜüËÅtç®köÜ"5 ¥Ê«Qp›Œ‡Ñ@¢ºŒNcÆž+Ó< áMWÅÚÃbÓJçqœ*>Tí;aG¹çÿ<)©è>}{ÄËÿ'Šç}[R$`¤·¤<òQí@½Î+˜øÿ#⇇þÃûÍ'FÙ®ë˜åYblYÛ7cæI󲞪¯£«Ö­ûœ4iý©êý>Êùêÿð’ýW.‘Ñzõÿ/¼(¢ŠòN°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šù§ö”ñgÆø[ûgáu¥¼¶Q#ëšîØùh‘6P yˆb½H$v`0rÄV´›îìŒq•89µ{v3ô?h>%x¯Â^*Ô!Ò|9¯â 2k‡IJÈv^Û©=[Ìâ/8'©iµ·ƒ¼[ñFø}àkõ$Ô®<©/¥ýÄJKŽ2 ¿ üA1ï_’¾#ñO‰<_©>¯âNãT¼ùiq#HÀz.~êú€; ö¿Ù:Ûí?ü*¤d#]Èà“úâ¿ZÆp] xz˜¬C暃òWQß¿O›>BŽwRU#Jš´[ùÙ³öÊŠ(¯ÆOµ (¢€ ù¸ÅÚøÓ»ýo…þÉÞ;rEüÙPýUÏ¡®÷㎮¼ àé.4hþѯêÒ¦Ÿ¤À0L··'l|¡9vÏ\w­o†^µøsà½;ÂðÉö‰áS%ÕÁå®.¥;敉äîrqž@Àí^¾÷%]üR¼cÿ·?»Eêûu}ùª}¯ô_¯ËÌ諸ŠòÀ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ç/ß´…þ :iWVZžµq› %OšwqžÀäùIñ㋾ ø‹Wñ ÔÃI]i"ŽîÚÅä† £·Gç.ãæ•eóìâ¿l¼{ðóÂ_ô<;ã ¼¶|”o»,/Ž'ê¬=¸=# ÿ0ðQYeø%ãùþxgZé%¼W—“Ç”š8n2bµ”•ßiÃ#'@Ì£ô^βÌu'íRë¯6½:-ÏSæó\*µE/sòõ=çà÷í½ðCöyø‘s¯ø’kÝtEe=±‡G…'6FBùdŠ, §8s¯öÿ?à±?²ŠïÓOñï„7°h¿²I­ÆNZÎYäùÖ¿•Ú+äs¼â¦:»¯Q$öÓ±ì`pQ¡OÙÅŸÞׄü]áoø~ÏÅ~ Õ­uÍPO2ÞòÎUž W§ÊèHàðGPx<×E_È¿ü£ö¿ÖÿfÏŒׯݾx¾æ+MRÞF&+I¥"8¯ã„hΤ}è³YSoõÑ^AØQEQEQEQEQEQEQEQEQEQEQEr>:ð^ñº‡„µÅ&Úù0~üR/1ʇ³#ÃéƒÆkäËŸÚ®…ž¼ð‡Ä y5OøzV±x¢ÊÅx¨¡¢»iˆ*«"Xrû³ò€xÝýªüiñÇÃ3Eð÷Lh4'7z­«n¢õ]€ ÖA»Ù×ä\ÓKq+Ï;´’ÈřؖfbrI'’Iêkõáxâ°üØ™' Ý$õO­ßK­Öûmc峌ÕÒ©jJÒîöÿƒê~¨þÊ?|eñ_ƾ/ÿ„¾ìIºÞÚk[hÆÈ-’7uaòyÞ»˜’Ç'¹ëòö Õ>ÁñšK"p5-2æ=J4s(Í~¼×ÏqÆ|{8Ú-+%éoÐôr*ò©‡NNîìüûøÑû*øÿâ¿Æ¸5ëÝwÎð¥Úò;(–Â8ñ›xbèÛÎJ0É%ùÛž ðo‡<áÛ? øVÍl´ë%Úˆ¼–'ï;·Vv<³I®¢Šü÷”P¡Vu ½é=[×伿®ÇèÙïf9Ž€ÄM{**ÑŠVZuiní¥ÿVÛ+À¾>|ð÷Æï}ž}–Zýб°¾ÛʾT˜å¢cÔuSóàûí׊ÂÓ¯MÒª¯x™6s‰Ëñ0Æ`æãR.é¯ëTöièÖçÈŸ²WÁŸˆ <=ª'õÔ&ÌZZ:Ë mã#|ƒ)ÆÐ7e¸_®è¢³Ëð0ÃQ w²îuq/â3\uLÃny»¾UeÛoN÷o«aEWaáQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@R¼Ô´í8Â5 ¨­~Ó Š/5Õ<É¢.â2ǰÕÚüÿ‚Ÿ~Åÿ?iH¼7ãß„·É¨]øJÖx_B–_$Íæ8“εv"?8à++•ܪ¸l¤õŠþ]¿g¿ø)‡íû0kŸð«h=.ûÅZ6”âÞ{]P<$³ÒˆçÎFÈ‹_ЇÀoÚcàÇí'áßøH~x†I¢Uk«?sfOiíÛç^x2ŒGÊÌ9 y¢Š(¢Š(¢Š(ÿÕýü¢Š(¢Š(ù@ÿ‚€~ȼ?ûIøÓÆš„5OxwÅúŒú¥¥î›i-â/ÚÛÌx¤«drWæÆ@WÃÿðÏÿè›ø—ÿ÷Ÿüj¿¹ú(ø`ÿ†øóÿDßÄ¿ø'¼ÿãTÃ?üyÿ¢oâ_üÞñªþçè áƒþÿãÏýÿàžóÿW¨xSÅMìún«£ÞYÞ[9ŽXf·’9#uà«#(*GpFkûݯâ›öÚÿ“¹ø»ÿc.£ÿ£š€<§Kø)ñ—\°‡UÑ|¯êWtSÛéwRÅ WHÊ‘‘Ž hÃ?üyÿ¢oâ_üÞñªþ²ÿàòe ÿì7þ•M_jPðÁÿ ÿñçþ‰¿‰ðOyÿƨÿ†øóÿDßÄ¿ø'¼ÿãUýÏÑ@æ‹û2þÑ>!Õ-ô}+ៈ乺pˆJº=ÙÞ0ª£¹bîkûý“¾k_g_ü3ñ+jº&ž‰tåViÈêp¬Äg¾+èŠ(¢Š(¢Š(¢Š(ù°ý¨~übÿ‚~~ÑÃö§ýŸlžÞÜÉpém-½’ÜntûÔ_»m#ä¾@_”"±ûóáü÷öRñnßÄ CÀº¸AçÚÜYÏ}™ŽD3Y¤¥Ó<éõQ_©·ö÷vòZÝijA2”’7PÈêÃX#‚ |âØ;ö>ñŽ¡&©­ü*Ñ…ÌͽÚÒ7±Vn䥫Ĺ=øç¾hó³ö—ÿ‚¾xFç×>ý•¬oµjéöxu‹›c 6­)ÛºÞÝó,Óÿp:*«~|¯\ÿ‚_~Åž%ø¡ê¿>/Úox¾³×{°•Äùû·iä E†.£ï߆Ÿ³GÀƒ·¿ÚŸ ¼¤xPÛ³ívö¨n¶žª'}Ò€{Ü÷¯q Š( Š( ¿3?m/ø(Ý·ì}ñ3Høs7€ÅgUÒ"Õ~Òº ²%¸ž/Ë6ÓgFíÛ‡ÞÆ8Éý3¯ø›û4üøÍ®[ø—⟂4ïj––ËiÅäeäKtw‘c#å ì~¤Ðãïü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ!ÿ‚åØù£2á@?ùø~]‡ý‰?ð ü_§_ðÁŸ±Öä“h÷àÿñTØ3ö:Çü’mþüþ*€?~6Á[¬>0øn/ÿ¬“Kò¥ó|í ³ÁÛö$þt|ÿ‚¸[|(Іˆÿ äÕf;Ʋ!êsÓìoüëôköŽý?e¯ ø.=CÃß 4{ ƒ. ‘BAÆz¸_ÙÿöQý˜5¯5ÿ‰>iÇ™ ,$œÀï_‰Ç`¨æõ7í-½ôû¯úK‡Ãbª`ß,×%öþ‘àRÁqtéT«üƒÿSÿä âï¿à²ÚUÔÆh~I =GöêŸý±úg¤þDZåÄæ¿„º‚x>Aÿ⫺ÿ† ýŽ¥)ðŸCÿâ«ßýÆ*7µÏ!û\<­{ëÿµI©ðžE#Ó\ü…]•‡üþH„Rü’P;ÿoÿ¶¿BµßØ/öW³c-¿Ã g „ÿñUÈÃþÊÙÿ’c£|½G’øªñ¾¹†ÂÎÊ›_?ø'¤°Õñ¿:סñuÇü L¹ŒÅ/Á‰?õ0/ÿ Wsÿ‘Óe”ÉÂ9#çÛ ÿ툯ӽö2ýn± ß °î`?üUw-ûþÇR'ËðŸCÿâ«ÖŒpØÈs8Üàn¶VNÇä<_ðYhdÇð¢E#ž5±ÿÈUÚYÿÁp-íâMðzI}ÿ·€ÿÛ_ :÷ìû,éò`øa£˜Ïo$ñÿW-ÿ ]û*«gþŽŒqÔOÿ^JÆá°³åTÚùÿÁ;þ«[g4ÿ¯Cà[þ ?¤jRy©ðvH_þÃÊöÀV)ÿ‚ÆYÚ~È}ÿ¶Çÿ!Wê^û~Çw Esð£Cßïÿâ«CZý?dƋϰø_£FGa Çþ…]u0øJÑöŠùÿÁ0UñŸ²r·õè~^i_ðZhô·>Hëéý¸þØ×X?à¹=þ ÉŸûÿ WÚS~Äß²Ìc—ᆎ¤uýÉÿâªþ—û~ÉQN#¼øW¢H§¹€ÿñU†9ÃS~ÉE¯_øs\FYZkÚ9&~}êßðZ]#S˃’DÇ¿öòŸý°®þgŒ„ò}·ÿ!W륧ì1ûÝÄ$‹á>„íÿâ«3Xý?d—„½—ÂÝ7„'ÿŠ®üV_†’ö®ôoüÎ:ÚÑ~Ïšß#òÿHÿ‚צ–6Ÿ„’Lž‡]ÿlMt_ðü‹oƒ2ÿÔÀ?ù¾Ó›ö%ý–-ä1ÍðÇGtýÉÿâ«OGý¿dt˜Gð«DuÏSÿâ«‹ áàý+^¿ðçM|¶«\÷LüìÕ೺N¡7Ú!ø=$ÿaåoý±“ÿ޳ÿ¢M&Oí±ÿÈUúëoû þÆ÷Q‰#øO¡éÿ⫟×`?Ù=c3Ø|/Ñ“Â!?üUuâ²Ü5½¯³¿£æc‡Å×OÙóÛÔüÏÒà¶Qé‹å·ÂI&OC®ÿ¶&·Ïü&ÀŒƒ2á@?ù¾ÍoØ«öWF(ÿ 4pËÿLOÿ[š/ìmû"‰„7ÿ ´GSÜÀøªçÂgt½’‹^¿ðæØŒ²«^Ñ´ÏÎíWþ E¤jM½~Ézÿo)ÿÛXCþ f¼„òg±þÜü…_¯PþÂ_±ÄÈ$O„ÚÏÿÅW;®þÀ¿²vÃ5Âí1ØBøªêÅ`pÍ{W ú7þg>Y?f¥cóWMÿ‚ÜÅc†_„2LýG€ÿÛV¯à·ZUôf9¾ IÏíõÿä ûnOØ«öXŒmðÃG =a?üUuÞýŒ¿céÈ‚÷áN†ÏÓ&ÿÅW.7ÃÔýÏ+^¦Øœ¾¬?ytýÌ)?à±Ús9hþHª{pý±sNÿ‚ÏE¦ËæCðžB=?·ÿÛ*ýgŸö ý'„ù_ tD$pDÿН3Ö?a_ÙkM˜‘ðÃG1ÿ×ÿÅSÅapØwíU7òoüÂ…zÕ—#šù¯øÂÉÿȳ þ ÈÇ×û|òfê_ð[=#RˆÇ'Á™=ÿ·Ôÿí…}Íìcû)Dêïð¿Fuî ÿНLÑÿboØÓQ„ðŸBßùàøªß ™añK‘¯¼Ë‚«Aó_î?'¿áñ–!‰_„Ò`ö:àÿä*ÒÓà´1i²ù‘ü'‘‡§öàûe_«÷¿°7ì4DCð¯D±Õa?üUyv£û þËztÆ9~èøì|“ÿÅW6#†Ã¾uMü›ÿ3jëV\®kæ¿àø.=‰M²|‘½â~?ù¹=Sþ ;¤êæGðzH[×ûyOþØŠý²ý?døe ?ÂÝTï˜ÿ^¡¦þÃÿ±¦£tøQ¡î=pøªßŒÃb×+ÞgW [ ù“?%áñÖ˜øO #¿öØÿä*ë4Ïø-Äz|b)>I2úŽÿ¶&¿Jµ¿ØöOØe°ø_£!àBøªóÉ?b¯Ùb1¿Ã 0ã˜Oÿ\u'†ÁÊê›ûÿà4éÖÄ«¹¯»þñLßð\2t)'Á‰ê>¿ü\UßüKMžS,¤ˆ7QýºþØŠý'ÑcoÙfj ´GÏsÿâ«Ò#ý„c{˜÷Gð£CÁî ?üUz–csû|4´v?"áÿ‚É[A –?…?ê6?ù »[/ø. ½´B)¾I.;ÿoÿ¶¿CuïØöUµk?†>ÏA ãÿ®(þÅŸ²²œ±ѷÆÿÅWŸõÜ>|Š›_?ø'ZÃVÄÙÍ?ëÐøWÄðZÝ?ÄMÆ–~Iž¥wÿoÆ}¾Â+äOÁB,t?¿€Z˜°‹ûL.9é»ìÇùW톫û~ÈñøfîAð·EŠê8ÉWAõûÕñgŸٟà·ñQôOÀúuÍŠ¹SDJcó¯'9ÆeÕkSUé9Ií­»y£ÑÊðøÚtê{¨¥¾›þ+£ÿÁiì4©L­ð‚II9Çöð_ý±5ÓŸø.Uÿš3'þÿ+ôÝ?`ߨïhÏÂmŸú`øªü0gìwÏüZmþüþ*¾ç F ¬•«VR•äõ?1¿áùvôFdÿ€òðü»ú#á@?ù¿NÇìûcþI6‡ÿ~ÿGü0gìuÿD›Cÿ¿ÿŠ­ÌÊ»ø-W…´ÍSQÖôÿžEþ®bk¹—^PóWdeÏØ9Ú¼ Ùÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñUR›“»bI-ÌOø~]‡ý‰?ð üGü?.ÃþˆÄŸøPþ@¯Ó¿ø`ÏØëþ‰6‡ÿ~ÿGü0gìuÿD›Cÿ¿ÿŠ©ù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñTù‰ÿ˰ÿ¢1'þÿ(?ð\»Áø1'þÿ+ôïþ3ö:ÿ¢M¡ÿ߃ÿÅQÿ ûÑ&ÐÿïÁÿâ¨ùîø§û|xÆþ oxKádž{œµÍºjë<çøãQg–Op2§°æ_ƒ_ðQO„þ>²ñ¼¾}WìqÌ‚©ˆrfŒÇÿf|`7¥AŸðÁŸ±×ýmþüþ*²eÿ‚{~ÇRk0k+ðÇKɉâkuFû<ˆ!š2Än\pF:œçŒ}âœ{ ðÒ«îY­Rzv½®y¿ÙXuSÚ¨k{õÿ†?6¿áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*¾tôÌOø~]‡ý‰?ð üGü?.ÃþˆÄŸøPþ@¯Ó¿ø`ÏØëþ‰6‡ÿ~ÿGü0gìuÿD›Cÿ¿ÿŠ ËÁUí4ˆñ÷‰¾½ÝͶ–òÚh£X 4ù.Î>Ñ$ÆÐï–H†yk°w$æ´ÿáùvôF$ÿ€ò~¢Þ~Ã_²>£pnõ…º=ÔìL’Äîä  ³18=ÅUÿ† ýŽ¿è“h÷àÿñUÙŽÅ*²\ªÑJÉy›wo͘ХȵÝêÿ¯ÀüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªã6?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€òðü»ú#á@?ù¿Nÿáƒ?c¯ú$Úýø?üUðÁŸ±×ýmþüþ*€?1?áùvôF$ÿ€ò~2~Ñß'ý ¾6ø¯ãÆžÚQñ-ÂL¶7Ú ) GæìvÕŒv¥ZßðÁŸ±×ýmþüþ*¿¿à¨¿°V‡ð×MÓ¾9|ðÜzg…í"[MwO²FÙhÁ•z’÷yr‘‡3øyEP_¾ÿ‚ÜÇ£xLÒ/þËsckÜx)šH+HWì-äg8ÏS_Ÿ_°'ì“«þÔ¿¬aÔìŸþ? KÞ½rA¼jw%š°Æd¸#i‚±ï~ ý,ÿÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈÃòì?èŒIÿ…ÿä ý;ÿ† ýŽ¿è“h÷àÿñTÃ~Ç_ôI´?ûðøªüÄÿ‡åØÑ“ÿ ÿÈò¿ÅOø(—‰'TÒþ Má]VW[‹=i$†LŸ˜½·Ø£RÝNU“÷‰¯Þ?ø`ÏØëþ‰6‡ÿ~ÿGü0gìuÿD›Cÿ¿ÿŠ®ìe_ ?iBn/úß¹†# N¬yj+£ñgà÷ímá/]¯Æ [ÿoÏá¨'ž}"IÅä‘4LŠK€¡÷nUuÈÆ}=¯þ—aÿDbOü(ÿ Wéê~Áÿ±ìgr|'ÑFDpFûÝÇ›ÿ ûÑ&ÐÿïÁÿâ«»=Ï'œ*ÔŠRJÎÝuoå¹Ï€ÀG½¹ù‰ÿ˰ÿ¢1'þÿ(ÿ‡åØÑ“ÿ ÿÈúwÿ ûÑ&ÐÿïÁÿâ¨ÿ† ýŽ¿è“h÷àÿñUáçæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠ£þ3ö:ÿ¢M¡ÿ߃ÿÅPæ'ü?.ÃþˆÄŸøPþ@£þ—aÿDbOü(ÿ Wéßü0gìuÿD›Cÿ¿ÿŠª:ŸìAûèšuί¬ü1ðí……œm,÷"†(ÐeÝÜ*¨’N~"~ПðQ߀_´æ‡ý•ñOöz7°¡K]RÛÄ £iŸùå8ÓÉ+“Ÿ-ÃFO%I¯ËO øÛÄ>ñt^0økªßxrþÆf{;ˆ. ]D™áZXÂÊðÿ(VäÁÅ~´~Õ?´_ìà¶x;ömø9áïkiº7Ö®mœip7B`MÊ÷,;6R>„4ƒŠùsögý€~>þÕºŠø¢ÏNÂ^¼•¥“Z½ƒÈ¶evË dfÇ!DabÚ]xú+û$ÿÁ_®µ;½7áïí1¥ÉqwrñÛÛëúM¹w‘Ü…_µYB ,OñÛ©ÉÀõjýí†d¸†9ãÜE 7)FÁVƒêw¯ÿf/ØKà'ìµo£á,ë+hÂO®êXšððwý]ºDJ†³àû6€ (¢€ (¢€?ÿÖýü¢Š(¬ëí_IÓR½‚ÐÉ’¢Y2ÀuÆâ3Z5üòÿÁnïï¬üeð¤Z\Ik S;®q,ph÷ïþ¿ ÐbÏÿ#ÿâ¨ÿ„¯Âßô³ÿÀˆÿøªþ ¿¶õ¯ùÿ¸ÿ¿¯þ4më_óÿqÿ_ühûÉÿ„¯Âßô³ÿÀˆÿøª?á+ð·ý,ÿð"?þ*¿ƒoí½kþî?ïëÿÛz×üÿÜß×ÿþö¬uM3SÚmÜ7a0Å"É·=3´œWç·Äø'Ÿì[ñÇš÷޼mm#kÚíä×—Äk2 ĬYÿv$y=â¾Dÿ‚#^Þ^xOâ‘»žIÊßi¸Þűû©zf¿"?mm_U‡ö¶ø·7³¢/‰5d`󛀠ìáW€|ðá¶à?þçÃ: ©yÌáb.Ò12±%€f<“Åu?ð•ø[þƒøÿ_œ¿®.þ Kö—•šoøA?øji(µkGw!UVxÉ$ð¹&¿ƒ?í½kþî?ïëÿzïìû¬jò|yøn};+x“HX‚ ä\hû‹¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(£©¥=(mý¦ôæÔ<° ™ÿÇM|ÿû0Å6¥¢Mb +’ôöc_^ü_Ðe×üumÃD þ˜¯‰d?ù~.Õ<50ذË/^2w5~gŸQK5§}¥e÷³íòŠê9wZŸUÊ’[ÊÈù½ wÞñ Àµºo¦joh`×6£æÇ"¼äù·]¬µÙ(VÁUºØç&*­©ï³E ÔE_Z¼¿Ä>ki{q•ö­ø”ª­­ÙÉõ®ñ’;¨pÀ2°¯£©N–:ã¹âÁÔÂÎÏcÁäŠQ*í^¡áïÇs‚s†s\æ½ 5³µÌ íô®N)d‚@ñ¬;WÍÒ©SW•ì{u! M;½Ï}ž®a) W”ëÚ ¶r4Ñ)Ùí]G‡üB—(°NpãŠën Žê#Œ« ú\F–2•Öç…J¬ðÕ,ö< ^ Ѷ žkÕ´GÁ!ÇׯhYHÓÄ>L×9os%¬¢hx*y¯™ÃN¶µžÇ»^`Er:ÿ‡c¸FžÝpþÕÙšäêKÛR9òüÅÁû:†G‡¼BвÛ\7³^’Œ“F|ÊkÀdŠH$Øÿ+¯Jí|5â #aitÙk,¯4qŸ²ªi˜å÷^Ò™§â?,ªnm†¯æ Ëœ|²-} Oª‘^â/ä››eÆ9­3Œ®ÿ½¤FY˜%ûº… ø„NÚàüÝ9®¶öÊ èJ8"¼)Kiw/ÊèkÓü;â¼Qo1ÃŽ)e9‡:t+“À¸?kOcˆÖti´éYÀ>_¥gY_Ëe*M@î+Ûoìa½ˆÇ ÎkÉu}>FuÆ:Wg•T¡/iGc³ŽXòT=+FÖaÔ!‘º§Õt¨u ¸»ñËÙì&ÄÜwëº.³¡çæ¯[/Ì!ZŸ²«¹æãpn”¹éìy6¥¦Ï¦ÎQÁØ}j]/T—Nœ2œ.zW­jº\:”$0ù€à×j6iÓ´s)Úzñ³ ¶¦~ҞǩƒÆF´y*ɧj0ê0 Œž¢¹ïøy.”ÜÛŒ0êpú6«68ÉÊW®Ø_Å}‘9íáqñå–ç—^„ðóæ†Ç…ËÀÅ%2ž ušˆdµqopr­Ó5ÓkÞKÕ7®; òùá{YJJ ¸<ðkЭƒ­Íb•Zx˜Z[žò¯ Ü]™XWøƒÃe]®mxúUoøí\Atr§¥zb´7Qn2µ},užŒñ>§‘òÿŒƒ§‡¯¿…–3šù#öxû+|Oº’àÁÎ }Ãñ²Æ=ÁZ¦®˜‹ùWæÿÁ-rCãã4,îNüÃ;§<.6Ÿ6¶ä}öUR5ðµw²ýOÙØØ犚á¼1¬Kw˜.̼ îqÒ¿cÀb½µ5PüÏAÓ“‹:QEÚs…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Aukm}m5•ì)qop‘È¡ÑÑÆYOpAàŠžŠü¯øÃÿŠý˜¾%ë·$𬚗€n®²Ïm¥³ðWÃM×@Ь‡î­­*dõv<³»¹,Ç’I®â¾Zýˆ“Ëý‘>/¯†ôóùÄ }K@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@~HÁMÿfÚö‘½ðf•ðbåo¼'n’&¥¦=òYF—F@Rîe‘•fEN î„«óšýo¢€?%?eoø$çÂ/ƒÿcñoƇ‡â‹"Û"ÛÉþÇ´sò@ã7ã˜m<‘šýgŠ( ‰!…qƪ¨ÂªŽp©( Š( Š( Š( ÿ×ýü¢Š(¬ûÝ#JÔÊ6¥eÙ!L±¬›s×ÅhQ@?ðŠx_þ€öøøQÿ§…ÿègÿ€ñÿ…oQ@?ðŠx_þ€öøøQÿ§…ÿègÿ€ñÿ…oQ@l´½7L ºm¤6¡ðXEÇœtÎÐ3Tçð߇®f{‹.ÖYd%™Þfb{’FI­ª(´vVqZý‚(#Km¥<¥Piê6Ž0sÒ²¿áð¿ýìÿð?ð­ê(þO ÿÐÏÿãÿ ?áð¿ýìÿð?ð­ê(þO ÿÐÏÿãÿ t~ðÜ2,±i6ˆèC+,‚èAƒ[”PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÍ-7¦Mâ€(ê6«{i-«ô‘JšüŽÖ^ãàÿÇ6ŠaŠæef#€C¶­~¿NõðíUð¾ÿ^»:þn^æ5rîŠø^3ÂMBš{Åßî>·…1ö’¡Si+phšµŽ·¦Çyi –99°µÿ -Æn-@ é_™ß?ho|9tð爡‘íÐ…,àñ­}ÑáïÚ7áÆµ hu(Òvê‡?áQÏðغJ8‡iŒÉñZÎTu^D¢Öx®V-§x5í:2Ê–)æýêáO¼Î&7q<çüŠº>&ø9FÑ|€}úÕéå”haäåíÇu«¥î3¼šçRŽ2 yÆ¿á¶W3Ú/Njàø™àâä Ÿ5¾%ø9Æþ<{šìÆÔÁ×V”•Î\-8ü:A·ûV>>¿áKÿ ÏáÑ?òõÿ õ©æ¸^NGQt²úÜÜÜŒÕñ€ÊZêàu®sD‚àß©E ƒW_ãÃiWkêq°>¹ÿ ©ÆO†0¶ôÔbR}ÿ ñkG *ªq¨RÄ{/fàÏi‡>Z†ëŠ‚îÎ+ÈÌrŒƒ^P>9|:óøS¿áyü8ÿ ¬¯øW¸ó\,£É*ŠÇ”°Óº‹®h3iÓ4°Œ¡«>†ãí"UÎÎàÕ9¾5ü5˜m“S‡¡ÏøTq|gøkj1'Ðð¯p‘¯íUdz®UÝ>G{WjÇÕ´¸oáeuãƒ^qÿ ÏáÇ}V?×ü(?þôþÕõÿ ÷ªæ˜J‘å”ÑåÓÀâ#.edj|Ú|Í©ÙØÖß…n®’ímÆJ~•yñᆠ¥%Ôãý›¤üRøgm ŽÓRœûŸð¯š…:­ÍNªHöªN¬éòÊ續•æ¹-BŽú=è 2ƒƒYcâwƒð1~œûÒ‰¾ =uÀú×Òâqxj”ù%4xÔhW§.eyôÐKk)†A´¯C]g†õùm¤ó¡õ®GƾÙ[<÷„aÀãÿ ùÅ_´ÅµƒK‡”\cÏðX¼Ò– k’W>·—ËKXØö¿ÚËâE¾áy<9mM `Fyþ•ñìáá‰õ¯ÅsH‰‰#ês^Uâ¯xƒÇú±¹ÔIÙȤ“Œþuú?û*|#ŸÂZSëšš‘-Ø ¡»q^S5Çó[D{u¡ ·Èþ&}] èCO&Wå›ô®¤ðiæë_¶áð°§ô?*¯^U%ÌÇQEÐdQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEüL~ÙOæ~Ö_ÓÅZ¸ü®¤ý³×ñû_1Ú³ã úµÁù_J(ú×ý‹ÓËý’¾/¯…ô£ùÛ¡¯¦«æÿØí<¿ÙCàòúøOE?œf¾ Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(1I¶EÍS¹°·»R“¨`ÜFjíZQœyf®ŠŒšwG‹x·à_ƒ|WÇ,BÕŸ«"Zðÿøb Çtn­|EwŸ–$ãõ¯¶h¯· åõåI}ïüÏVŽ}‹¦­ Ÿ—ùÿÃ!Ùuÿ„ÂÿþüÇþ4ŸðÈV}üe~íŒã_cÑQþ©eÿóëñæiþ±ãççà¿Èøãþ Ëþ‡ÿûõøÒÿÃ!ØŸ½ã óÿl£ÿûŠo„ò÷½/Åÿ˜¬xßùùø/ò>8ÿ†B±ïã óÿl£ÿ?ᬳŸøLoÿïÔã_cÑGú§—ÿϯÅÿ˜¬xßùùø/ò>9ÿ†C²ÿ¡ÆÿþýGþ4Ã!ÙÿÐãÿ~£ÿûŠ åëþ]~/üÄø‹ÿåçà¿Èøãþ Ç?ò8_ÿߨÿÆød+-ÛŒoþžLã_cÑGú§—ÿϯÅÿ˜ÿÖ,güüüùÙ ÈÌãúåøÒÿÃ!Øöñ…ÿýùükìj(ÿTòÿùõø¿óÄxßùùø/ò>9ÿ†C²Îá1¿ÿ¿1ÿðÈv]ücÿ~cÿûŠ?Õ<¾÷ö_‹ÿ0ÿXñ¿óóð_ä|qÿ ‡gÿCÿýùühÿ†B±=|c~í”ã_cÑGú§—ÿϯÅÿ˜¿Ö,güüüùÙ Ä(ÿ†-Ð?èi½ÿ¿1ÿ/ü1nÿCMïýùükíj)®Ëç×âÿÌo‰ñÏzŸ‚ÿ#âûhŸøªoyÿ¦Qÿ:?Ø¿A‰÷Çâ«Õ#þ™Gþ5ö­ªwüúü_ù’ø“ÿåçà¿Èøãþ È ßãþ¸Çþ4Ù Äù/ÿïÌã_cÑWþ©åÿóëñæë&7þ~~ üŠ®ÿbý üÿ¦xªöQèbüjý‡<?æaºÿ¿)þ5÷œø7-“¼¨þ/üʇc¢­Ÿ‚ÿ#ä¿~Èþðޝ«ý©5÷’Aò剜} }Uoi¬K " 0*ÕéåÙ>šÃÖþ¿©ÁÌëâ]ëJãvûÒ‘šZ+ÒJÇQEÀ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š)U†=ékø}ý§î§ÿ†™ø·$r2îñ~¾x$uÔ& î 0-x7ì°f/„!É,<áü“É'û> ÷š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯á·ö“3ö‹ø§'÷¼U®Îújþ䫸bý¡Ìøùñ.Oïx›Y?ì´ý—~Ì©åþÍß #þï„ô!ùXC^ß^7û:'—û>|0û¾ÑGåc {%QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¬Ét]v-=„3rKD„’~¢´ëùùÿ‚”~Ú¿´ßÀ_ÚDøá7Aﱬn¾Ì,,.?}3J·Ü[ÊüínÀì(úDH‘cB"€è§Wò©ÿ ‡ÿ\ÿŸÿá%kÿÊê?á°ÿ૟óñâü$­ù]@Õ]üªÃaÿÁW?çãÄÿøIZÿòºøl?ø*çüüxŸÿ +_þWPõWE*ŸðØðUÏùøñ?þV¿ü®£þþ ¹ÿ?'ÿÂJ×ÿ•ÔýUÑ_ʧü6üsþ~"ø÷Ãß | ¯|Eñl’E£xrÎ[Û¶‰ ’¡]͵,}vuç?>.ü1ø5 7Š>)xšÇÃZjçl—“iYy+ |¼¯þÄjÍí_™6µWí‘ûcDéû x&‡þšG‡þ²<¯±Š±¶€ rT„K€`ºž«ðËþ §ðÆË__ˆ¿´n½¨ülñ»á¤¹×dv°F…ŽÐ»îA’Êî˜è‹Ò€>ðøoñÂÿ< £üFðUÃÝhZô?h´–HÚ’-ÅCpsŽ„ê+ñãö—ý£ÿk¿Ž>øâ_€zm×Âï…>†òK¯ÝÈÖÚ®²ö,Uã°hòÑÆ]HÝìwL¤˜«öËNÓ´ý"Æ /IµŠÊÊÕ8`8£EUDPT€ ùöãd‹j£xvô?Ü  b?xƒÅ¿²‡Ã/x«S¹ÖumCII.o/&{‹‰œ»ÒK!fcÔ’kêzøÿöÿ“5øOÿ`dÿÑ_`P„þÑŸâýŸ¾Üxñ<;âÍBK˜,4ý+N]×—·LVƤ˜ª³ÑXñ_Íñ öß³ý³>^|~ñžÓþ!_JñxGH¼–+;K(8^ÇysHÅÃ1¥#¡+÷÷¸€H$gjü”ý²?å _²—ý|_èi@­•à~#ý§þx7âºüñ—‹m¡_Õ]*Ÿ²Oü¥~/ûüYÿ¤ú…UtQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ ÿ'€ì_Óô)«ú«¯åSþ ÿ'€ì_Óô)¨ú«¬Oxo@ñލxWÅ6êšF«Û]ÚÜ x¦†Aµ‘ÔõVÝù[âOø'.¹ð»[¹ñçì?ñ#Pø[¬JÞdš5ܯ{¢]0莲 /¼©pðªÕ;öóøÓû?_AáÛ£ámÖl\C‹|>†óH˜“…iY¶9!$/Ïú…é_¬OPÓì5kôÍVÚ+Ë;¤1Ë È²E"7]ÀŽ ŒP‚¼iሾÒüsà­A5] Znlî£ «,OÑ€p¬=ÃAàŒ×ÏŸ·/üšÅÏû¯ô ú#KðÆ“á GáoÙ[h66P46PZ‘[Ûd»"P(c b¿-üuû*ÿÁFþ%x?Wð?hmQеëw´½·þÁµ‡Í†A†_2+D‘r;«ï@UþÀ_òf¿ ÿì Ÿú1ëì ü¨ø7û,~ß¿ôß ø/Høñ¢xnH#þÍ]ݙ죔<‰¤¶2å×p _pÏWGÿOÇü ðæ©ðûÅמÖ"ñ6Ÿm VÛ\ê/z¶ ubWw›´ä‡¸úi_’¶Gü¤ öRÿ¯‹ïý +î¿Ú#´Œ|/¦Øþξ<´ð·à’îêòÆôš×Ëpb 4R…;ʶBƒ€F{ÎOþÂÿ·Äˆ>ø§âï÷Š<Ò>wý‹ _fiH.|¨­Ö)3þ±[Ú€?g+ãoÚKöáø=û6jöÞ Ö"Ô Ôõ©¿hï‰úÄ+¨a]>+=2 ¶•YŒŒÍ ï¤ ãcœýž ð„~*—ÇI¢Y/ˆæ·KGÔ…¼lkxËˆÏ·ÌØ 8ç¥~[þ /û^|Úíä?³oÃÛ¿ùv¶-?ˆ§…»;‚’FÝAË[Þ6õìåûüý™îÛÄ^Óî5oÜ#¥Æ½ªÌnoåóy“iÂÇc×bG Í__Ñ@*Ÿ²Oü¥~/ûüYÿ¤ú…Uuüª~É?ò•ø¿ìkñgþ“êýUÑEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ ÿ'€ì_Óô)«ú«¯åSþ ÿ'€ì_Óô)¨ú«¢Š(¢Š(¢Š(®s_ð„¼Y.™?ŠtKfMî;û½¶Šá­.âÿWq‘XÇ*gåtÃƺ:(¢Š(¢Š(¢Š(¯åSöIÿ”¯Åÿc_‹?ôŸP¯ê®¿•OÙ'þR¿ý~,ÿÒ}B€?ªº(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ ÿ'€ì_Óô)«ú«¯åSþ ÿ'€ì_Óô)¨ú«¢Š(¢Š(¢Š©{c¦Û5æ£q¬ ÷¤• ϫ1Pº)‘ɱ¬±0tp ²œ‚BíYך拧Þ[é×ú…½µÝÙÄ0Ë*$’ž˜Eb ~Ð¥bxľð~‡yâojvÚ>“§Æe¸»»• ‚Äò9 £êkŒøÍãøáo‰9?üdOð¹íï|g»WO ®¥& EK–Œ.XùJcVýÞýªÄ&Hü¹ý¼¿dŸüð‡€þ&|GñÞ¡âÿо;¼¸:·šþdI$QÇ!ÒÞ·”î©’pr6¢Šþ€¿a¿ Ãà?ØëáV›>ÛxÎo¨ÈX… ý  ó–'¦ Äœôï_:K/ü[öò´ñŽœ­wð_àaQíþ£Q¿Y<Å1ƒDó"± ¼+¸)û9àµø<á¸|Y!—[M³[÷a‚×B=Kæ¾Pÿ‚ÿÉ“|Sÿ¯+_ý-·¯·kâ/ø(ÿü™7Å?úòµÿÒÛzøóþ—ÿ$Çö3ý"·¯Ú ü_ÿ‚%ÿÉñßýŒÇÿH­ëö‚€ (¢€ þU?dŸùJü_ö5ø³ÿIõ þªëùTý’å+ñØ×âÏý'Ô(ú«¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯åSþ ÿ'€ì_Óô)«ú«¯åSþ ÿ'€ì_Óô)¨ú«¢¼÷þߟú4_üÛñÊ?ámü)ÿ¡ÓEÿÁ·ÿ B¢¼÷þߟú4_üÛñÊ?ámü)ÿ¡ÓEÿÁ·ÿ B¯Áø.F‘«M¥|×¢…ßL´›\¶–P>DžálÞ%cêë …Gû&¿k?ámü)ÿ¡ÓEÿÁ·ÿªwßþ ê–æÓSñVw ˜æ¾´‘ Ör8 ¿ |Hý®¿oÿ‡~øð—Ãoð×áf›¦Øéz÷ˆ$g‘nã´…"•b±oFÛÅ´ ³d d±ÇíÿÀožý~é ¾Zy~ž»¦™Àóï.\6ævÝ$„sÙ@  *¨ëñgá:(Dñ–ˆª£ FØoõ”ïø[ ètÑðcmÿÇ(­×u›hš‡ˆuF)e¥ÛËu;(,DP!w I øûsÿÁN¾üaø â/ƒ lõMKPñ?Ùâ’öêÝmmmà†â9Ü€Îdwo/h,[€§÷þߟú4_üÛñÊ?ámü)ÿ¡ÓEÿÁ·ÿ æÛþ ±ûy|1ý•ü;âŸüU°ÔŸ®_G¨[^ØD—9B)hÙÑ‚ŠU“qÎAƒ_ÑßÁŸŒ øóðçJø©ðîy®4 dÎ-Þxš ¶™íäÜÈÃÆÃÜsW¿ámü)ÿ¡ÓEÿÁ·ÿ£þߟú4_üÛñÊô*+Ïámü)ÿ¡ÓEÿÁ·ÿ£þߟú4_üÛñÊô*þU?dŸùJü_ö5ø³ÿIõ þšámü)ÿ¡ÓEÿÁ·ÿ¯æ?öA¸·¼ÿ‚«ÚÝÚJ³Á?Šmmod`(vcgpˆ8bTRCÄ gTRCÄ aabgÐ aaggÐ descDisplaymluc& hrHRØkoKR ìnbNOøid huHUcsCZ0daDKFnlNLbfiFIxitITˆesES roRO¶frCAÈarÞukUAòheILzhTW $viVN.skSK;L>@>289 LCD LCD æÑâÕàÙ_i‚rLCDLCD MàuFarebný LCD&25B=>9 -48A?;59Colour LCDLCD couleurWarna LCD 0   @ ( LCDLCD *5LCD en colorFarb-LCDColor LCDLCD ColoridoKolor LCDˆ³ÇÁɼ· ¿¸Ì½· LCDFärg-LCDRenkli LCDLCD a cores0«0é0üLCDtextCopyright Apple Inc., 2022XYZ óQÌXYZ ƒß=¿ÿÿÿ»XYZ J¿±7 ¹XYZ (8 ȹcurv #(-26;@EJOTY^chmrw|†‹•šŸ£¨­²·¼ÁÆËÐÕÛàåëðöû %+28>ELRY`gnu|ƒ‹’š¡©±¹ÁÉÑÙáéòú &/8AKT]gqz„Ž˜¢¬¶ÁËÕàëõ !-8COZfr~Š–¢®ºÇÓàìù -;HUcq~Œš¨¶ÄÓáðþ +:IXgw†–¦µÅÕåö'7HYj{Œ¯ÀÑãõ+=Oat†™¬¿Òåø 2FZn‚–ª¾Òçû  % : O d y ¤ º Ï å û  ' = T j ˜ ® Å Ü ó " 9 Q i € ˜ ° È á ù  * C \ u Ž § À Ù ó & @ Z t Ž © Ã Þ ø.Id›¶Òî %A^z–³Ïì &Ca~›¹×õ1OmŒªÉè&Ed„£Ãã#Ccƒ¤Åå'Ij‹­Îð4Vx›½à&Il²ÖúAe‰®Ò÷@eНÕú Ek‘·Ý*QwžÅì;cвÚ*R{£ÌõGp™Ãì@j”¾é>i”¿ê  A l ˜ Ä ð!!H!u!¡!Î!û"'"U"‚"¯"Ý# #8#f#”#Â#ð$$M$|$«$Ú% %8%h%—%Ç%÷&'&W&‡&·&è''I'z'«'Ü( (?(q(¢(Ô))8)k))Ð**5*h*›*Ï++6+i++Ñ,,9,n,¢,×- -A-v-«-á..L.‚.·.î/$/Z/‘/Ç/þ050l0¤0Û11J1‚1º1ò2*2c2›2Ô3 3F33¸3ñ4+4e4ž4Ø55M5‡5Â5ý676r6®6é7$7`7œ7×88P8Œ8È99B99¼9ù:6:t:²:ï;-;k;ª;è<' >`> >à?!?a?¢?â@#@d@¦@çA)AjA¬AîB0BrBµB÷C:C}CÀDDGDŠDÎEEUEšEÞF"FgF«FðG5G{GÀHHKH‘H×IIcI©IðJ7J}JÄK KSKšKâL*LrLºMMJM“MÜN%NnN·OOIO“OÝP'PqP»QQPQ›QæR1R|RÇSS_SªSöTBTTÛU(UuUÂVV\V©V÷WDW’WàX/X}XËYYiY¸ZZVZ¦Zõ[E[•[å\5\†\Ö]']x]É^^l^½__a_³``W`ª`üaOa¢aõbIbœbðcCc—cëd@d”dée=e’eçf=f’fèg=g“géh?h–hìiCišiñjHjŸj÷kOk§kÿlWl¯mm`m¹nnknÄooxoÑp+p†pàq:q•qðrKr¦ss]s¸ttptÌu(u…uáv>v›vøwVw³xxnxÌy*y‰yçzFz¥{{c{Â|!||á}A}¡~~b~Â#„å€G€¨ kÍ‚0‚’‚ôƒWƒº„„€„ã…G…«††r†×‡;‡ŸˆˆiˆÎ‰3‰™‰þŠdŠÊ‹0‹–‹üŒcŒÊ1˜ÿŽfŽÎ6žnÖ‘?‘¨’’z’ã“M“¶” ”Š”ô•_•É–4–Ÿ— —u—à˜L˜¸™$™™üšhšÕ›B›¯œœ‰œ÷dÒž@ž®ŸŸ‹Ÿú i Ø¡G¡¶¢&¢–££v£æ¤V¤Ç¥8¥©¦¦‹¦ý§n§à¨R¨Ä©7©©ªª««u«é¬\¬Ð­D­¸®-®¡¯¯‹°°u°ê±`±Ö²K²Â³8³®´%´œµµŠ¶¶y¶ð·h·à¸Y¸Ñ¹J¹Âº;ºµ».»§¼!¼›½½¾ ¾„¾ÿ¿z¿õÀpÀìÁgÁãÂ_ÂÛÃXÃÔÄQÄÎÅKÅÈÆFÆÃÇAÇ¿È=ȼÉ:ɹÊ8Ê·Ë6˶Ì5̵Í5͵Î6ζÏ7ϸÐ9кÑ<ѾÒ?ÒÁÓDÓÆÔIÔËÕNÕÑÖUÖØ×\×àØdØèÙlÙñÚvÚûÛ€ÜÜŠÝÝ–ÞÞ¢ß)߯à6à½áDáÌâSâÛãcãëäsäü儿 æ–çç©è2è¼éFéÐê[êåëpëûì†ííœî(î´ï@ïÌðXðåñrñÿòŒóó§ô4ôÂõPõÞömöû÷Šøø¨ù8ùÇúWúçûwüü˜ý)ýºþKþÜÿmÿÿparaffò§ YÐ [vcgtndin6®QìC×°¤&f\P T9333333mmod Rýbmbvcgpffffff334334334ÿÀš b"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÛC  ÿÛC ÿݧÿÚ ?ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠB@“€(h¯’þ/~Øß þêgºx›Å~(b4Ý4yæ‹#€Bg×¼y<]ûy|XÜøgúOý_ºu$×êC€È§þùª¢÷zæÑZ+óÅ?g¯ÛWPh¼ý ÿ³d8ÌPé1ȃñ2ToðÏöùðpóô‰:WŒš.‘êVfSM?d¿™ ™ö?D¨¯Ï _ÛâOÂÛˆtßÚkáÝΉ ¸Oí3tÖLÝ2#`H\÷ßÀ¯·¼ñÁÿ4(|Ià­RWOŸ¤°lB;Q*mjƤ™ØÑEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@UkËÛ=>ÝîïçŽÚÆZIX"(÷f ³ExŠ?j_ÙóÁÒuÿéÑ8ÎD.×=9?êëƒöñý“n'[x¼w8´½QŸ©€V©Ë±<˹õÝä~ø÷ðgÇ!G…üc¦Þ<˜Ú†uŠFÏ¢Iµå^¶¬Syw©i­Æ˜´QE!…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢‚qɯ ñ·Ç_xoQ>ðý´¾!×›…µµä+¶üãò®ü»+Äbçìððr—›{%æÌkâ!MsMØ÷:+å!áÿÚÇ™»×uËØ?>E¼~d»}³p}ë2óàç€-J·Œ~"j pùÁKøá½òíoj÷e`éiˆÆG›´¾z/¹³c*Ëà¤íæÒÿ‚}E|{kð‡ÂÓ>ø•y ¨F ·qÜ€Þã šÓkÚ'áÈ7 oã&1–dSÑÐW§±¢<;†­¦.ÒN ú7xÿäÀñÓ‡ñ)´»­àþÕôW“|=øÇá?ˆ[¬í¬5H²%²¸ùeR:ã¦G¾+ÖkçñÙ}l5GG.Œí£Z#ÍtùÙñ«ã/ÄŒgý›?gÙÍ£Ûý¿®¡Ävq·(ÜJóéžqôWíSñ^OƒŸ|Eâë&Æ¤Ñ K >÷Ú.ˆ†6QÜ£0o±¿d¯ƒ–ÿ ~Øôx^Ú¥ËròM?ÌO;@è=Íc’æeKWcsà_ìÏð×à6šÃvBëZ™IºÕ.{©Ý¾ù.r@c“´WÐÕ ÜA’EV= SVrmêÊI-QQI<ãΑS=73ùÔ ç‘JÏq”u-3NÖlfÓ5khï-.¤Ê¡ÑÔõ §‚ ~p|Lýž<]û8ë7?¿f–;To7XðÚ±û=Ì+’ÏtÊóÀvêkô¦Y¡„n™Ö0{±ùÐDsÆTáãpAî5p›Ž½ ”S<—à‡Æ_ |tð Ž|.ûVa²æÝ¿ÖÛN¼;8$çêTøôòü›Šÿv¥)z->oda[NŸÇ$|¢¾`?´îŸœ¯‚ëÃö§ø´×ôvëV0ü±êyýñe –b} ë_hü*ø‹¢üXðãÝÃZê°‡+Ý$:؃úQ8«sG`‹èÏC¢Š+"Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šò?^9“Àþ žâÀçTÔZÙ§s,œgƒ½veø˜šðÃÒø¤ìe^²§9l?ø‹ãßøÇÄïð—ጾ]Þ1¨ê –±ã,¡‡ñãŽ:zó_|JøQû#ih:=£ø“ÇšÀÄ6VÊf½¹‘¿‰ñ’©ŸR3Î3Î&ñ‡Šô¿Ù#àsë—q›ÿëäEgæ–çR¹û«êUäŽàzÔŸ²÷ìã?†üÏŒß$:ßÄo>Ygù–Ê'åaˆ„w?@䟥Î3:tàð87j+wÖ¤–ò~]–Éyœj”½­_‰ÿä«·¯sÍì~þÖ_´B¦³ñoÅÒü7Ð.0ñèú<˜¹1·!et GP͸zf»+þ Ùû7Z«O­é·šÝü¼Ësuy33Ÿ\n ¥}×E|£­.š"ê|%ªÁ:ÿg)ñsáûíþ?õw·“Cêp®#Røwû[þÎjÚßïIñ7Ùit½M·_‡hÝóÐt Ù>•úIE ¼ºêÍt>ð‹¾þÕö¬øUäðÄæ{Y—ȼ†xú‡^ ¦F22@ëƒÅ{ÿÂ/Š޳yuà ³ñV“ÃÀ¸Œp$NÇßã^ûS~Ïš£Ëǯ‚Ò<}á£ö©ˆõ"ù¤ŠT\n%Aý:œÕH|ieñÃá^û@ø/°ø§Ãrl¿„rêñcΉÀäã ¯Ôõ¯²Êq°ÆÓYv)èô„žð—Eä{5ÓtyxŠN”zk^«ºÿ4Sý¿î,¾hräXêÞ2Ó ºnv¬~bœ·lgÖ¾øÀ±Ò±l7‹x~@;ì^?•|û_Ásñ_öc³ø•ájžÏZŠ1ÎÖ·‘pqýÅ ùW×_¼s¤üIømáÿhÒ‰­µHÛ=pê6¸>ù¾SBTŸ³¨¬âÚkÍ9©k¹ðÍ…—ü,ËoøãÆ2—GÖ4Y® ¥¡˜!O(EUf ÔùGë_aþϾ&ÖüWðÇOÔõöin‘ž/5óºTLaÉ=sž¾Õ‰?go†(Öß^¿±’+‰Ÿ|« ›#•³’\`õï‚+Øt'Nдè4&¶´¶]‘ƽWÝñ_`ñ˜HÑ îšM$ ’³ŒZÕÝë©äe¹uZU\æûõzë»>¼´_‹ß|c¼^ú ^žh¬íüÏ-Âű2qÉ<×Ð_³?Šuïx q®\½ùÓ¯%¶†éÎL± rz’3Ôú×Kã_€ß¼w«oX³’×I%»ù~h|ã+Ò¼=áÝšLj¶vVã ~¤ú“ëKˆ8§‰Ëㆥvɤ”9UšÕó>á‚˪ӮêI÷Öï[íuä|cã+sñCã6¹àÿø–Oé4½´>h‰$r£-‡ AÝŸOjï?fkz„$ðÍþ úµ†‡p‰itä±u}À€Ç¨G³^«ãß‚¾øxš—ˆ-]oPóà}’2ŒA~×øCÁ~ð.º'†mÖØÇ»;¬Ç¹£3â¬\©a!Íh%+EÇâ’{·/>á‡ËªÇí[Òï[»»ì­ä|9ûxƶz§Á}~Ùö_ÚxÆÁc†‘Y×)Ç8=Å~ƒÀí$1Èã Ê „Šüëøùp>/þÖ ¾iD\[ø6u×õÚ aXÇ÷”.=Î+ôf¿>©ðÅÔwaEV%…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@x_ÄߌQøZö?øN×ûkŃ÷vÉ’±Ài1Î=¸«¿þ$ÍàM 3sâ iŽŒ+ÉÞüo>Ëëë^+¬k¾ ý”>\|CñäŸÚ~-Ö (ßuupàb»àq“ÀúWÖeyu?dÝ¡ækvûEuîôîy¸Šòœý'nï·üüÿ  xïöŽñ@h¢kÛ4¢ 8W®ÖÇ-Ž™ÎkÊ[öÉø-¥Þ¿†~x÷Æ×–ß(]:Ø$mé¶b¯¸{Ö'¿g~Ó÷–Ÿÿj{™¡Òå>~›áˆÝ’(#?pÌìuÉþ,Šýð¯ƒü/àÂZ]¾“§ÛŒ$6ñ¬h?f¹3> Äb=Ú³÷Vѻ薆˜|!¬V½Þ­ŸÃKþÓ!–ÛöÔá²R9&ùÕ{XçÕÏ_þØ^±acñ÷àþ«áeùZ{ˆÊœ÷ÊÆ„Æ¿Gª¥õ…–§i-Ž£\ÛÎ¥$ŽE ¬§¨ õäRÅ8>hèü›GT©ÝYŸøsÂÿ~ ÙŸþÌ~0[;¨Žæ¶†o2 ÝJuD•âÔ~×3ÐòñøÊð­Ë¦–Ò÷×]zXû^×4Ï iZî³0·³³C$Ž}aêO@;šùÓCýªü«ké—vW:}•Óì†òQû·nƒ#h÷Íz—Æ_j=øo¬x_Je[»”FqÀf‰Ö@¹õm¸¹æ¾>¸µø«ñCÐ~O᤮‘4m5ëácUˆõR8ç<íÉ5!‘eØœ,ªbŸ½ÌÓ÷Ôy#kó$þ-t±Yž2½:Š4ö¶š^îûy ÷×¶Úm•Æ£xâ;{hÚYôTA¹à|ÃíeàwÕűÓî×Ki<¥¾+òÓîã§ã^ëã Éâê^¶”‰nmÝŽI}¸]ÇÜõ¯…¦ƒâÞ¥àKOMàÓZÈÞ’6lV'xcòçæõÍGdy~*œå‰Õ©%¬Ô9cgyëñ[°óL]jrJŸn×»íä~†É®io .ìoá*êI«‚=Ã)¯€¿bK»xÏâßÀiµ¿„5u»·øSPRv/û+åð+îÏ è¯áÏ i>’A3iÖ°Û—Äsøâ¾ýšü$_µ·Ç¿Z`éñM§ZC"ò²²¤›ÿ8Í|4ãç;Ålþz²mò·¹ú#EW!¨QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Äþ$Òü# Þx‹Y“Ê´²Bî{ŸEäð=ëáxçÆ>6ñŸ‚|_¬øwì~KÖ[%š@f||î™ÝÐ qŠ÷ŸÚ'ádѹ%Ý¢°õdà×1ñÊ{M?Løwæ†4»„ã "ÿ~¥ÁXzT¡J¯'4꺑»¿º”:[«¾ïd|öm9IÊ7²Š‹õmõû×´ø¾>~ÛöÚö.|7ð¢Í/3þ®Ké(qÓ|R0ü«ôl p+óãö1AªüSøûâ; Ô^+¹±þ™DÍ´gþ+ô¿3­½»ô6¸QE‰aEP_—¾ÏìóûWø×áÝ´`x_Çú[jö“ˆ’ê2ÊÑ(ì[v[Õú…_šŸ¶²&—ñÏàv¹Ù$÷:¥¼Û~óÆ#ˆŽàs]ùjæ­½›F Ú=‹ö]¶ñ&¥àíGF×tˆ—š¢Îöä¸pÉ3’™'o'ïEy7ì>Ú†ƒñâÏß ]=ï€|?©Ft÷~‰4ûÌ«whvãÖ»x~)Züýnüo4¾UŽ¥ÌV‡<ýªwhá>ø‘”ŸjùàÇ-ß ì¾þÏ#ø£â½,º†«©:Ÿ°ÙÏppŘrû@¨Ç¾ké8Æ««™â¥Ê—¼ÖžN×woW×ÌáË"¡Bš½ô¿ßÐý§¢¿1¡øûu|xük¤xžÛâC^b]OH¸ˆÆUÛï-±V䎀ð;ãµz‡û}ø"ÊèhŸü+¬xVN9¡3ÀêL  ‘¯’t_ÙÔô½¢ê}óE|Áiûg~Ì70‰_â™lOðM!GQƒ\æ¿ûv~ͺ,lÖ~#:ã@]6#pXNGZŸe.Ãç]ϰ«æ_Ú?öðïÀílíâmã [÷:f•Ï<’? ì‹’¨s×·C_=ê_´gíñɲ?gË i×!×µ¬ª*‘Ë$`.צKkÖ¾~ÉZ/Ãí]¾"üGÔåñ¯î¹—Qºº‡<í‚.výr}±ÍZ¦£¬þây›Ø‡öHøâ/iÚ·Äß‰Ò ¯xÙÍÕó““o‡zÛƒØ/€xWÙTQYNM»²Ò²°QE# (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ d’Ç %`Š£$“O¯”þ%ÿk|Jø¯eðod°Ñ ³ûmï”ÅZl±2;`q^¾K•}n«Œ¥Ë§)=ì–úu}‘Í‹Ä{8Ý+¶ì—™Í¿ÄŒ^!ø©ú_…·ÙØî?"²ðòb9¯ž~éw?µwí®|bñLføàéú«ŒÃ-Üg2ÊÀðYI_bãŠ_]Ø|ðgÆÓá°ÐÛØX›hw¹fStË;q¿Šú£öLðLøá-!# qqmö»–ï$×±võ%p? ÷¸ÎQ†)F“÷ £ÒÑåOÏ]nüÛ8²´Ý;ËvÛ~·>À¢Š+âX(¢Š+óëöÍøk©xh韴çÃDû'Š|"½ñˆÇÕ†râ@>öÎ~¹ç ¯ÐZÈ×ô›]wC¿Ñ¯bY ½†HdÊG"®œù]Å%t|eâoiwÃÀ?´·ƒÈŽËYX#½Lõ7*ßïqõÂþÕ6·ß >'ø#ö·ð¼FæÆÍMÖÖ>KXHÛѸã«6OÒ¾mø/a þ‡à~²ï=¯ƒ¼gq¤¸ÜÁ¼·yN7ué_\~Ù…Ã_ÙòÓá…#2_x¾uÒ,¡'tœÊàž¥7/ç_sœÆ›Ëðr“n§¼¶û »k}×M6~G…röÕR^îçcíïx‡Gñf…câ]ånôíJž På^9e#ê mWÊ“üFøKû"|(ð׃üm®Å ºM•½¢C2\Nñ Râ4€8ÎHÞ½‡á¿Åï‡?´ˆõŸë–Úœn¹hÑñ4~¡âl:þ#ðÒƒÝl{ ]J¢Š*Q_8üiý¨¾ü±hõMAu]z\­¶•b|û¹¤ì»S;r¼GµTbÛ²vÜÓý£þ6hÿþj^)¼Ntk}6ró^J6Æz¬w`k„ýŒþj_ ~G¨xš=¾#ñ]ÌšµùlîV¸ÁXùì d}kÅþüø‰û@øòÏã÷í!hlm,K¡xuÉ)j),ªxÜ:ŒóžN ~Žªª¨U`éZNÑ\¨˜êî-QX–QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ç/Ú“åøY4¤|‘ÝÚ3Àyéɯ1ý©îQ>xBö¸G,N¥yèˆxü«ëßxoJñv‡wáín/:Îñ :ô#Ðpyõò>¿û7xâîÆoý·@ÒÙå¶·šº ¿w~î~¸¯Ô8'7ÁCêÿY©ÈéNRÕ?yJ)YY=S]t³>6ÃU|ü‘¿2KÒÌòØï[M#ã×Å/\·—'ˆÅ¿‰#Rpíª’;ùïë_¥ÕøÓâ'ñÃ+o~Ó -+øBåôoEÉ6!‡yÇR¨@œývð¯‰ôoøvß[­?R‰fŠE9Xtúކ¾/ˆð?WÆÕ¤¶ŒšûŸù¦·=(˺: )’IHd•‚(êXàÆ«G¨éó8ލÏ@®¤ŸÀñTWHë¹rŠ(©WæOí7vÞ9ý¬~ø2À‰Áz}öµ8_àIÂGÉît5úãx{áï…µx¢ém4í2šGcŒ… ¾¬Ç€=kàÏÙ?ÀºÿÄÛ¿þÑÞ1ŒÛê:/i¥,Šq§¦@eÏðÈHãÕk»/’…HÔ–É£ êñåG_£üðçí û9ø[Â>*¾¹³Óí®šåÖÜàͲCò7#~ÕõwÃ_…¾øIá¸|+à=*2Æ 7Ô•¿¿#uf>§&¼ßà÷Â|:– ]WÅ+}¢Ú£¢X¥¾ÅËäî߸œ‚s_D×­Å•iOZ¥ ªq”œ“WêÛ¶©køeÑ’£8Ù¤—Ü—«hš>½llµ»(oíÏü³ž5‘&V¥ógqâ÷³À ¹Z{¯‡>–FêϦ۱?‰Jè¼?ðƒáW…$Y¼1á +Jt9 kg $èQEz5No¸¹PQE# (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ù‚ÏþN–ëþÀ«ÿ¡½}?_%ü@Ô$øeñÂÃâ6±áíFÇìS\"–Hœ°;pÃë_[Â0u'ˆ¡Štä’îôv^zndùT&öRW>5ý«ïnfðÇÇ»îb³™1Á>Uü Gýð ¯Ô…—¶Ú‡Ã_ ]ÙœÂúe cîÄ þ¢¿3¾! /â/ÄßA¥\-ÖâD¹°‰ÆU^ià$óŽ’m5õì3ãñâ‚ÖþÔåÿ‰ï‚n&Ò¯cn$ýÓŽAçk€{â½rÙQ©Bm[šœ/äÒ³_‚92ZêJjû7ùÜû:Š(¯Ît(¢Š) 1ÀšZó?Œ_4¯…ÿ 5ÿkƒNµ‘€Ï.䪾¤ž‚šWv~Uü,qwñÃǺ³i7ĉå\æ_œz×ý¦ã¿ÅÚãJоè¦tð\-î.cckos9%î }ߺ ñÅXø?á¹üð›á׎¼f~Ëâuµ«Ùùs’68ûÓ¯Õï x¯@ñž’ºç†®…哳 +/̽FúWÜgTê,» YE¸¥5{i~gÔñð:Õ Þº?Àù;àÇìu xKPo|]Ôâ®2ÝßÐD_¨Š•qÎG [âì5ðç^ÕfñWÃ]Fûáÿˆ$;ŒÚd¥ ‘úæHŽsô ûvŠø¿m+Þç­È­c󲇷πì¾ñΑãx"â3ª[ý¶ŽŠLn>•uõø)%ÂÃþ ·ǘ“NY}À3šý¢Ÿ¶òBäó?:§ø ûc|If‹â7ÅX|;¦Ëþ¶ÏH¶enª²–,¾Çš÷„¿²?Áï„·Û6v2kÚû`¾§ªÉö«’}‰Fq…Ͻ}=E'ZO@PAEVE…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¤e ¥[x4´PɺïìÏ}{ý½g¤øª{M+_–ig±* &'r2ô+ƒŽ{WÈþ4‡ãìgema¡Ëssàß-wjŸhŠÅ]ðœ´kÜ•]¿~µW?⛯ YèW“ø¾Kxô•Cç›­¾VÎû·pkë#Æx×¥UúÂ/ñ±æ¼ª—ÙºôoüÏÏø¯âgį†1x‡Gñ‡Œ<=;F×i”¹HÉëc ²pÃê+&m#ÁgPðéø?¤k^#[˜¼Ù$ÛÆòK𿇥|ÙñSþˆüY;þÅšWˆ%ñ8ù’hïåé$“Ëíupþ§=«Ú¼á/ø)riQæóCˆ}@…¹_c±T}kê° J…MÒJÍ»EòEÝ[ߊV•¾G›[&SŸ75ößW§fÞ‡ëuyÇį‹_þhSxƒÇšÍ¾™JYQÜyÒc´qŒ»Ÿ 5ùµã­ þ xº3,:–”v©éNZá³ìêFkÉ>·Á­ÆÐ¿íy§köþ6Þd×åótÅlðê!'±$WæQíïCßu:÷ ‡Ä/ÛÓÄÖZ†»cqáƒZEÂÏú»DÙBW®ÆÀ9éŽG8¯ÓÍ+JÓô=6ÛGÒ [k;8Ö(£A…TQ€ƒs¡Ýé6·x$Óž50}¾VÂ8Û·Œbµë*“¾ ŒlQEfPQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEUKÛ -JÝ­5æê’(e?«tSŒšwBjûŸ+þо ÑôOZx›ÃúlV²èZ…µáò+²í×­|Wyã«…´‡Ú¸ýcö“Ò¤ñðçÃÝÎ#ó¬¤@Žõòoÿf‰_øõ¯W*£NRsSÙÛû·óä\ßqψž1%koÓ•ô?H~|b‡â¯©ønûF¸Ðu)wËm;`™ œí\rG¯h¯†>|Iø!áê>-ñÅ; o[ÖbHL­.D*9éÐué]?nŸÙ÷ÂÁ¬ôfOêÜùv:]¼³Ë!•¶„ÿÇ«æø†ŽëM`¹eÞ×¶¶æÖ×Úç~ u=Ÿï·ù|¶>Àžxm¢yîG`³3RM~`|Cñ%ÿí¥ñ‚×á‚ÙÃ_ܥι¨F~[¹Ô°£tÀÚqîNjÄÐ~Ó_¶1Z…¤ß >L|’’šô9û¥̹èÊØkï…ß |ðÂ6¾ ð=ŠÙX[|ÍóË!4ŽÝYŽ'Øv¯Z§K÷½’çž¼°µÒî´Ø%´²PÄÈ Fª0ƒÓŠÕ²°±Ó`º}¼vЯD‰B/ä1Vè©u¦ãÈäíÛ¡\ª÷¶¡EVEQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š§¨êšU…Æ¥ †ÚÕIŽªŒ“@ñkâÇ„> x&ûÇ>4ºöv‹ò æI¥<$q¯RÌp=S“_xkáwÅOÛ+R‹Çßæ¸ðïÃâ]3ÃÑ9‰îPà)žÛ¹ôàÓ| ¥]~Ú¿gø›âTøU¾ ¸1hÖm—÷0¶î:Ü7ã¼õ¯Ó¸aŠÞ$‚Ç ª£Ð] òh·3·7¡Íx?Áð‹‡¼¥[é| †Ú5I© X÷'“]MW;f\wŽ<àï‰ Þñ¶•­§Î1Îö“Æå$|­î9®ÆŠiØËmÁÿa{ãã†S]x«áKÍCF‘ši´èݾi!ÎNÕÎIVã&¿D>üDð§Å? XøÓÁ·©{§_ ©ù‘±ó#ŽªËÜk±»´¶¾¶–Êö%ž Ô££€ÊÊÃz‚+óW±Õ?b_Žúö“¿þŽç][ä”Óo˜ðË謿<ñ[ßy™ü>‡êÃuw6î$ŠU Œ§!•†AЊ–¹ÍŠ( Š( Š( Š( Š( Š( ¼ÏãGÄ ~ü"ñŸÄè,—R“šE¬æ%œÚB҈ˀÅCmÆpqé^™_7þØ¿òiÿìSÖ¿ôŽJüuÿ‡ãx—þ‰§þdÿäZ?áøÞ%ÿ¢Eiÿƒ™?ù¼ëþóð‹ágÅ|Pƒâw„t¯ǦÚéMjº¥œ7b•îC˜ÄªÛK\㮥~çÿÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹Gü?Ä¿ôH­?ðs'ÿ"×ìWü1×ì¡ÿD{Âø&³ÿãtÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹Gü?Ä¿ôH­?ðs'ÿ"×ìWü1×ì¡ÿD{Âø&³ÿãtÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹Gü?Ä¿ôH­?ðs'ÿ"×ìWü1×ì¡ÿD{Âø&³ÿãtÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹Gü?Ä¿ôH­?ðs'ÿ"×ìWü1×ì¡ÿD{Âø&³ÿãtÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹Gü?Ä¿ôH­?ðs'ÿ"×ìWü1×ì¡ÿD{Âø&³ÿãtÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹Gü?Ä¿ôH­?ðs'ÿ"×ìWü1×ì¡ÿD{Âø&³ÿãtÃ~ÊôG¼'ÿ‚k?þ7@Ž¿ðüoÿÑ"´ÿÁÌŸü‹U®พ0tÏá5„OžL𬲠}º:ý“ÿ†:ý”?èxOÿÖün¿(?ட~ |+øá ká§t_ _Ýø-æ¸Ó, ´–HMÃùlñ"’»”2 6ßþ ‰âÕˆ ¯„¶2Iܦ­*/ämØþµ?ü?Ä¿ôH­?ðs'ÿ"׿ÿÁ0go€¿?dý+Å>h#Ö%Ôõ(ÞóPÓmîgdŽ\"™$Fbpx¯Ð¿øc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖünøc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖünøc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖünøc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖünøc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖünøc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖünøc¯ÙCþˆ÷„ÿðMgÿÆèñ×þâ_ú$VŸø9“ÿ‘hÿ‡ãx—þ‰§þdÿäZýŠÿ†:ý”?èxOÿÖün¿?ø+GÇÿ ¿i=Ãß ü9§ø_K›ÂöW/k¦ÛGk N÷—¨Ò‰UK•Eã8v ê§EÔ­£Xj¬žQ½·Šb€ço˜±žøÍi×7àßù4?úñ¶ÿÑK]%QEQEQEQEQEQEQE“®kºG†´›s^»ŽÆÂÍ ’Í+DUêI5nþþÏK±¸Ôµ–ÞÖÖ6–YáQe˜Ÿ@~`7ü%·‡Ä;»C5Æ•ð[Ã3ùd)1¶±r§æäu@1Éàgަ´§ êö&R±Õëß´—Æ?ÚV¸ðì¡¥-¾“˜î|M~§É_Sn¿tŸLäj½ Á=¼­ê+⿎Þ"Ôh%°œ”G ‡${÷Õ*´×Q¸.Çæ{øëö£ý“Þ3ñ>!ñ?ÀQ²j–‘¯­c'T²í_B2}Fs_w|3ø£à¯‹ž·ñoõ¿²Ÿ‚â~èëü,=+¾š®!{yÐIªU•†C+ G¡ùŸñcá‹?eÝ~Ð_³õ¹“Ã÷$7ˆ|="'ŒLÑ ã#'€2#©«Vž›2u¡úiEyÿÉ~ø·à½;Ç>º:~£qýèß4n;2žõ¯@¬Z¶†‰…QHŠ( Š( Š( Š( Š( Š( œ¿k‹&øû:x×⿃¢¶ŸYðí´3[%â4–夸Š#½Qãb6¹èÜWóùÿœý¬èáOüÝÿòm~ÚÿÁG?äÉþ)ÿ×·þ–Á_ŸŸðD/LÔ< ñI¯í!¹)©i¡L±« 2ôÜ(äßø|çícÿ@ à¾ïÿ“hÿ‡Î~Ö?ôð§þ îÿù6¿§oøF¼;ÿ@«OûñøQÿׇèiÿ~#ÿ þbáóŸµýü)ÿ‚û¿þM£þ9ûXÿПø/»ÿäÚþ¿áðïý­?ïÄáGü#^ÿ U§ýøü(ù‰ÿ‡Î~Ö?ôð§þ îÿù6ø|çícÿ@ à¾ïÿ“kúvÿ„kÿô ´ÿ¿ÿ…ðxwþVŸ÷â?ð æ'þ9ûXÿПø/»ÿäÚ?áóŸµýü)ÿ‚û¿þM¯éÛþ¯ÿÐ*ÓþüGþÂ5áßúZ߈ÿ€?˜Ÿø|çícÿ@ à¾ïÿ“hÿ‡Î~Ö?ôð§þ îÿù6¿§oøF¼;ÿ@«OûñøQÿׇèiÿ~#ÿ þbáóŸµýü)ÿ‚û¿þM£þ9ûXÿПø/»ÿäÚþ¿áðïý­?ïÄáGü#^ÿ U§ýøü(ù‰ÿ‡Î~Ö?ôð§þ îÿù6ø|çícÿ@ à¾ïÿ“kúvÿ„kÿô ´ÿ¿ÿ…ðxwþVŸ÷â?ð æø,·ík0=;Âðcûš}ÉÏ×uÛT±ÿÁf¿k$@­¥xUÈþ#§Ýdýqxé_JÁoôÍ7NÓþ H­¼ÉuíÞTj›°¶8ÎÐ3Œ×èÿüóBÐî¿c_…w:u¼²¾–K;ÂŒÄùòõ$dÐâü>sö±ÿ ?…?ð_wÿÉ´Ãç?kúøSÿ÷ü›_Ó·ü#^ÿ U§ýøü(ÿ„kÿô ´ÿ¿ÿ…1?ðùÏÚÇþ€þÿÁ}ßÿ&Ñÿœý¬èáOüÝÿòmNßðxwþVŸ÷â?ð£þ¯ÿÐ*ÓþüGþüÄÿÃç?kúøSÿ÷ü›Gü>sö±ÿ ?…?ð_wÿɵý;Â5áßúZ߈ÿÂøF¼;ÿ@«OûñøPóÿœý¬èáOüÝÿòmðùÏÚÇþ€þÿÁ}ßÿ&×ôíÿׇèiÿ~#ÿ ?áðïý­?ïÄá@ÌOü>sö±ÿ ?…?ð_wÿÉ´Ãç?kúøSÿ÷ü›_Ó·ü#^ÿ U§ýøü(ÿ„kÿô ´ÿ¿ÿ…1?ðùÏÚÇþ€þÿÁ}ßÿ&Ñÿœý¬èáOüÝÿòmNßðxwþVŸ÷â?ð£þ¯ÿÐ*ÓþüGþüÄÿÃç?kúøSÿ÷ü›Gü>sö±ÿ ?…?ð_wÿɵý;Â5áßúZ߈ÿÂøF¼;ÿ@«OûñøPòüÿðY?ÚÝåóÇü|ƒN¸ÛÇÖèž~µoþ9ûXÿПø/»ÿäÚó¯ÛúÒÖÛþ EâkKhR(¥á #E €:ÀŸ”qÎkú»ÿ„kÿô ´ÿ¿ÿ…1?ðùÏÚÇþ€þÿÁ}ßÿ&Ñÿœý¬èáOüÝÿòmNßðxwþVŸ÷â?ð£þ¯ÿÐ*ÓþüGþüÄÿÃç?kúøSÿ÷ü›Gü>sö±ÿ ?…?ð_wÿɵý;Â5áßúZ߈ÿÂøF¼;ÿ@«OûñøPóÿœý¬èáOüÝÿòmðùÏÚÇþ€þÿÁ}ßÿ&×ôíÿׇèiÿ~#ÿ ?áðïý­?ïÄá@ÌOü>sö±ÿ ?…?ð_wÿÉ´Ãç?kúøSÿ÷ü›_Ó·ü#^ÿ U§ýøü(ÿ„kÿô ´ÿ¿ÿ…1?ðùÏÚÇþ€þÿÁ}ßÿ&Ñÿœý¬èáOüÝÿòmNßðxwþVŸ÷â?ð£þ¯ÿÐ*ÓþüGþüÄÿÃç?kúøSÿ÷ü›Gü>sö±ÿ ?…?ð_wÿɵý;Â5áßúZ߈ÿÂøF¼;ÿ@«OûñøPóÿœý¬èáOüÝÿòmðùÏÚÇþ€þÿÁ}ßÿ&×ôíÿׇèiÿ~#ÿ ?áðïý­?ïÄá@‹Ÿ°?ü_ãÇí7ñú/†ôý ÛH}2òð¾Ÿiq þe¾Í£t—2®ß˜ämüköö¿•Oø$OüžjÿØUþq×õW@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯„?mÏëWZG†þø*b¾ øwöVØpÑÙ¦<ÒHû»· c_w×Ï|³¼ý¡#øõªê¯y-¦ö K÷pe·yŠÙûß…iI¤îÉ’mXôφô/…¾ÑüáÈÄv:D 0]€ù¿Úc’O©®îŠ*¾¬ ¢Š)QEW˜|cøg£|]øs¬ø[ŒŠiÙÝGÿ°ïÄMwWðF©ð—Çñ?Û¹4¹ËŸšH!b‘8ÏU ƒÞ¾â¯š4o€×žý¤µ_º§¦›®Ø-µþž"$Í*( áü€Çå9ö¯¥êê´ÝÑ0M+0¢Š+2‚Š( Š( Š( Š( Š( ¾oý±äÓþ0ÿا­é•ô…|ßûbÿɧüaÿ±OZÿÒ9(ñ×þsÿ#/ÅßúôÑ¿ôeÕCUüòÿÁäeø»ÿ^š7þŒº¯èj€ (¢€ +Ÿâ­¼ekðîm~Å|Q{ ÜE¥ý¡ ãAËI䃼 ï½xÿí3ûNxö`ðm§‰~ÈþŸÆ4µVâ-šV„ª…Ù¤i6¤’ªä€ÄG»y­˜èôΊøKþ Ïñ£âGǿ٪ËâÅmXkZôÚý»\ x-³./—oiÀ=vä÷¯´|Kâ]ÁÚÿŠ|S—¤iq4÷WW (bNYÝGrzPå—¢ëš/‰4«mwú…¾©¦Þ ’ «YRx%CÑ’HÉVàâµ(¯Æø-—ü›×‚?ìiÿHn«öb¿ÿà¶_òo^ÿ±¦?ý!º aÿ‚FÉ™hßöÕ?ôu~›×æGü3þLËFÿ°¶©ÿ£«ôÞ€ (¢€ +ðÇÄxÚóVÓü¯Øëw Ëo~¶S¥ÇÙ§a¸G)Œ¯ŽJ“‘ÜWÇß´×í‘©|3ñ”>øBëâÆVÝgƒOŽ7[eû—“eFÁ×heP>ü‘Œ÷ù¡ÿÝøññÓãnñ::¼Z¦³á=xiˆ°Û[[Ço±XK›hÐ:‡S†bÇëô¾€ +‚ø£ñÃÿþxâoŠK+Ã63ßN±€du…KÐw8U€XŽkò‡áN§ÿý³<>ß´ˆšoÁjr9Ñ4Ø4è¯'š(]K#Ë£Í Ae‰P®@?fè¯Ê¿Ùßö”ý£¼ûNØëö¹’Ë]Öu‹9oü?â+RÝoc$—$i{ C(DŒ²FPï ~’ø«âü .—5ë- µ»²YéÒÝn.6—FÒ •…ÎOlšëè FEWòñÿ£ÿ“¬ð÷ýŠú]_Ô=/ðZ?ù:ÏØ¡aÿ¥×ôý1ø7þE þ¼m¿ôR×I\߃äPÐÿëÆÛÿE-t”QEQEQEQEQEQEQEfkZ¤.‘y«ÜœEgÊØð£=4ð7íwãüFñ‡ÿeO‡W ÷ˆÝ&Öî"äÛiÉó²’>ép3ÔqÞ¾Úø}à_ü5ð~™à¯ Û­µ†™ ÆŠ£ñ1õ,y5ñWìK¡_øãTñ¯í1â‹vSñ¥ìÐY WlXC!  eWÕú[Uv÷B!®¡EV%…Q@Auko{m-Ük,3)GF §‚©è ÌŸ øcïÚQü !h~|J”¾š ÌvZƒœù`ž³euç$šý6‘_+~ØŸ ÿáj|ÕWO]ºç‡ö¶›(ûñÏiûóý¦E*>µØ~ÌÿÇÅ‚Þñl¼^˜~Ív‡ïGqÈÊÞøÁ?ZÚ~òæ":;óEV%…Q@Q@Q@Q@Q@Q@ÁG?äÉþ)ÿ×·þ–Á_Á¿äCø©ÿa-7ÿDÍ_zÿÁG?äÉþ)ÿ×·þ–Á_Á¿äCø©ÿa-7ÿDÍ@ºQEQ\~ñÀÚÇ‹5iý÷ˆ´˜V{Û.[›Xݶ«M’cÉàÁ4ØQ_7þÓ?´çfÚx“Å–·š¾¥­\}‹HÒ´øZ[­BðŒ¬H@Úƒ¦Y¿à!› ?ÿgOÚ“öÀñ·í·gð·ã½Œ> еãY·ðÌp[´–Е?f3\kŸ7å%Õyë}Ðû!EPE~f~Ñÿ¶Ç£ñ‡ˆ>~ÈþŸÆ4µVâ-šV„ª…Ù¤i6¤’ªä€ÄG»y­˜ëÑ¿àœÿ>$|{ýš¬¾!|VÕ†µ¯M©ßÛµÀ·‚Û1Bà"ùvñÆœ×nOzû¶Šå¼aãü=Ñ¿á"ñÖµi iBh 7wÓ,,³¸HÕ¤rw1@ÍtV×6×–ñ]ÙÊ“Á:‡ŽHØ2:°ÈeaA‚(z(¢€?¿à¹òø3ÿ]uÿý¿I¿àž¿òf ìßú>ZüÙÿ‚åÿÈ?àÏýu×ÿô ý&ÿ‚zÿɘ|)ÿ°Sèùhìº(¢€ *)ç†Ú.ndX¡‰K»¹ ªª2I'€ä“\σüuàψZ\ºçu»=N†ymZæÆd¸ƒÎ„âD!*Jž æ€:º+ó3öý¶<}Œ.ë6Æý´ÄH¬ìÕ¶«Ë†Áoá]Çs‘’1úŠü|ð‚¿à§¿|%gñzÿã—ðÉuÈ–ÿKÐ!Òa™"·™CD'2Dî¡”† #LÀ» •µû~Ô<[ñSÆß²·í9ckį@.ÖþÉpj6{£S+*í@ÄOˆÈˆ7å©ÈéMÇêß|  ø£KðV¹¯ØéÚö¸’Iaeq:E=ÚÄB¿ŽA©a¹<ôÅvQEü™ÁAå%>'ÿ°Ÿ†ôÛ§×õ›_É—üþRSâû øgÿMº}Y´QEQ\ß‹|eá/hW'ñ¾³g iƒ2ÝßN–ð' /!UÉì3“Ú­Iâ-/·‹í?²ÔÞ›‘’Ÿf æùƒ$lçœPÕø;ûLÿÁFhoxUø‘û)øv ü0ðíÜVÓø¿S´…¤Ôg–O)c³·ºVO/qùŽÇp1»Ê?+~Ù|=Õoõßøk[Õ$ó¯u 2ÎâwÚ|²ÂŽíµ@,IÀÔØQE~yþÚŸµ/į†>*ðGìùû=ivúŸÅOˆîße’ì·°´¡¸d$ÙW`XUÙ•¸ô2Šü‹ñoÂø*7à ^|IÑþ9iž6ÕôÈ åÞ€Ú=´p\¤+¾HmÜ@¥˜€B…XºRE}Ÿû~Ò6ßµOÀ½'⑱]3Sóe°Ôíc$Åõ¾ÒþQb[Ëudu I¶’HÉú–Šãô_ˆ>ñˆu h:ýþ¹á÷êÎujÌªËæÂô0 ‘ƒž vQEüªÁ"äó—þÀº¯óŽ¿ªºþU?à‘?òyËÿ`]WùÇ_Õ]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯/øÑe¯ß|/ñü"דØêÖ¶ÆêÚKwd½©lHÈ!B:pkÔ) °È<kl5ogR5½šqaÍç7àßZøÇÂz?Ь°!Õ­!¹s´ÊŠýT’¸®–¾zýŸÉðý·Š¾ÌpÞ Õ¦ŽÙOQ§ÞŸ´Ú“õ Ãð¯¡k|Ç©W”#·OGªü3ÃTs¦¤÷ëë×ñ (¢¸ÂŠ( Š( Š( Š( Š( ¾oý±äÓþ0ÿا­é•ô…|ßûbÿɧüaÿ±OZÿÒ9(ñ×þsÿ#/ÅßúôÑ¿ôeÕCUüòÿÁäeø»ÿ^š7þŒº¯èj€>@ý¤?mïÿ³-ì>ñuÕÞ±âûè’[MJ·k‹Ù–RV2IÛ+0ÀÜû;UºWÊ"óþ 1û^qc ?³_ÃÛ¿ùi(iüG<-è>I"nã‹R?¼â½ûöšý…¼9ñçÇ6þÕÿ?i ?ŸÛêÉ™6›7ú>£x½´˜rªN  dÏF5ôU~J_ÿÊb4ßûùK_­uù)ÿ)ˆÓìDoå-~µÐ^wâŸè:‚|c6‰§[ØI©Úß]Ý´$Mqpð0ie*w Y²pÏè•ÉøûþD_ÿØ6óÿDµ~vÁ!¿äÍ´ïû jŸú×éм-áïøoRð‡‹,#Ôômb -nífŽhe]8#Ðæ¿7ÿàßòfÚwý†µOý kôËRÔ´íO¸Õµ{¨¬llãigžwX¢Š4gwbTI$@–zßüÛâÁmRçÆ_°·Äëï\HæY<7«J÷Ú%Ëwçã€eŽgç‡^´Ýþ ñàž©màïÛ§á…÷g‘ÄQx“H¯t[–þö¤+Ç$E$¯Ï1§Jì>$ÁK>'ˆáÏìÏáÍGão[*°è±¿ötD’ïcnEÈ%£Fּ̋ÒçöQý±¿k´þØ~>Oø.á’Càß íË*°eK™É’2U€a½®@ ôýSð‡‹ü5ãï i¾3ðv£­¢k­Å¥Ô$˜å‰ú0Èê5ùÿ²ÿ“zðGý1ÿé Õ~°|5øyá„ÞÐþø.·Ñ<=l–¶©$+ˆÓŸ™Û–$’Iü°8¯Éÿø-—ü›×‚?ìiÿHn¨Øà‘ŸòfZ7ý…µOý_¦õù‘ÿŒÿ“2Ñ¿ì-ªèêý7 ÎO‹¿ðQï‡^ñm×Â~Õ>0|C·–KvÓ´x$¶óDÛOs±Ž¾ñŠ7Q‚×å£öoý·¿kô¿Ú—Çëð»ÁW\·„¼(À\ËëÕÈg^F2î#ýZWGã¯ø'ßü ãÍsã?ìeñ6÷á÷еë‰/5 /Poµéò»´…dʹUÜì@’9ö–;6f²ô¯Û÷âÇÀFß·WÂëÏ «¸†?èqµîp{3*3í$rDr;óþ¥:PÞ?go„Ÿ³o„äðwÂMû*ÎåÖk©Wžâêe]¾dÒHI'ÂŽŠ q^¯káýÇW¾ñ–o©©¬Iuv‘"Ï:À IJHç !rqÔ×/ðãâ§ÃŸ‹þÅŸ |Geâ]&Bϲ™døÎÉ|ѸQ°î+¿ ÉOø%·ü†hÏûî¿ô9kõ®¿%?à–ßòý£?ì{ºÿÐå¯ÖºÄñ†|9ãïÃ>.Ò­5½"ýB\Yß@—6Ó(!€’)#@8 ò¥Ðô/ø?A´ðÿ‡lmô}J…a·¶·`··†1¢( ª£° ÚéɯÅߌ¾)Á@~'êß²ÿìɨ6‡ð»Eq‹¼\€•º%^Ú݆7FøeTR ø$•€1`ð‡ˆ­¿l?ø)­‡Ä߇Š/~üÒ¥±›TLù7—²­Ê ‰ÇÞYاð²BÎgôëã—ìÿð£öð€ðOŽkt2ùöåe’­§ÚTK‘²²°Žr§£8«_þ|:ýž~Ø|5øe§ .Ëç’F!®.®%ÅĘå| œ ¡T*Ž“âÄß‡ß ¼9/‹~%x†ËÃzD'i¸½™bV|9w "ǰ4ù–f/ÛOöP?lý“~!ˆÞµå|!âÆ ,qŽ‘ÚÜ’ˆ8èí—=UÍzW¯ø)€uÛ|+ý¡|/ª|ñüÒ$ g«C#Y\K#lO&ä"ýÓ"*t #u®Yÿ‚‚|Jøß©Üø?öø_{ãy#s ¾&Ö#k-Ý¿¼˜Ëpr’Düqô«ÿ‚}|@øã=â×í±ñ6óÇzþ:ÝXèº[}“H±‘]_jˆX‹»ËŽÛFæzýS¯åãþ Gÿ'Yáïû,?ôºþ¿¨zþ^?à´òužÿ±BÃÿK¯èúcðoüŠýxÛ襮’¹¿ÿÈ¡¡ÿ×·þŠZé(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦Ir£E*‡F*à piôPkK+=>µ°‚;hW¢D¡gÐ(¬ÑEQEQEQErx»BŸÆR|9œ?ö“éßÚY?u%³Iä¶<ÜŽµ£hZ7‡lF›¡YEaj›Ë….æäœ¤úׇ|eÿŠ[Æ>øŸË¨&ùº±ê‹å‡sýØåÃÜ×ÐÕÝŠÃÆ4éÔ†ÒZú§gøYüÌ)TnRŒº~_×äQEp›…Q@Q@Q@Q@Q@Q@ÁG?äÉþ)ÿ×·þ–Á_Á¿äCø©ÿa-7ÿDÍ_zÿÁG?äÉþ)ÿ×·þ–Á_Á¿äCø©ÿa-7ÿDÍ@ºð—íÿø;ðGÅ3|/Ñ,µ/ˆ?–$ðö‡nò̳:‡D–]¥T• íŒI à”Å}Û_œ_ÿ`9¼SñKQý¢g߈z§Ã/Š€âáÜi÷›UdÑpÊå¦á—ŒíÊ$P—‡ŸðP¿Úë÷¿üCìùðþëŸì™µËˆOðÍ2°d,¹VÌ‘Ž~ksÒ¾Ñýœ¿dO‚²æŸwà 2oíMM/õKéÚâöð!Ü<Æ;QFNvƈ¹äŒó_Aûe~Ô¿³ ©¤~Úß dÕü=ÇŒü(¢{VÀ{ˆ>TBÇž|†Àùajýø=ñûàçÇÝ ëÿüUgâ+x™¢…Ê\Ûîè'·,ÑÛz öÍzU÷‡ôSRÓõON·»¿ÒG²žh’ImšUÙ#Bì BËò’¤8?þÓw¶ž#ýº~&Iq¦A(¹·ð‡†È·Óíß(’m»K¨b¤„y0HYù¯Õý#J±Ð´›-LʳÓàŽÞ,[lQ(DbI€2NOzüÿ‚åÿÈ?àÏýu×ÿô ý&ÿ‚zÿɘ|)ÿ°Sèùkógþ —ÿ ÿƒ?õ×_ÿÐl+ô›þ ëÿ&að§þÁMÿ£å ±n. ´·–îêEŠUÝŽUFI'°“_˜~5ÿ‚“é~+×çøuûxSøËâ”ùZꤶÑí‰à<³¸Vd©ag´µúq}em©XÜiשæ[ÝFñH¹#r8*Ã#dÕù7?ìñÇökÔ/¼)ÿ¼øy®[xöØøw¨ü$×ä>Zj‘Å%æ‰tÂñÉöUÏüóiÔugúOá/øOǺ¯Š|¬ZkÚ=èÝ åŒéq€uÚñ’2g ðy Å>Ðt/øÆmN·°“Sµ¾»»h"Hšâáà`ÒÊTî@³dàž+àOø$7ü™¶ÿa­SÿCZýñ÷üˆ¾#ÿ°mçþ‰jüìÿ‚CÉ›ißöÕ?ô5 Óúä¼kà|Iцþ!x{Oñ6’dY~É©ZÅw‘ â9U”:‚pÀddà×[YÚÆ¯¥øI½×µ»¨ìtí6 .nn&`‘C *^IUA$ž€P}OSÐ|# \êÚµÌN¤[´³M+,P[Û¹fbpªˆ£è¯ÈŸØ²êïö”ý¶þ.þÙ:E“ÛxEðÖ‹4ªÑµÜ‘‹e2…#òÁ–b22#F#½~xÜþÚŸ´¿í3<š'ìEð¾k]F17Œ¼R¿g±A¥íáÉW*yi›æ€P¹üÿ‚„|!øÇâ¸>ø«MÔþüI‘ü†ðþ¹nñÈó¸¤3mŽ: '=×Þµùµð[ö¿Ñþ(é_´7í#ñSø›ñ3Ka%«î6Úm“ ØHb3"bª<¨òIò²kô–€?“/ø(/ü¤§ÄÿöðÏþ›túþ³kù2ÿ‚‚ÿÊJ|Oÿa? ÿé·O¯ë6€8‰¿<ðsÁ—ÿ~%ëhz›·Ï¹”;€]‚¢ªF¬îÌÄURIè+ó^ëöãý¡?i ©¼?ûü-¸ŸK.Ñ?Œ/±øƒ¨xsM¹ñN—Ai«Kg ßÛÂáÕ£Šå”ʈDŽ «C71®¶¼®O¿ "ø¯Àé|C øê{SzšYI­n¹pÛ<¿º Æìà84ð÷ůø*WìéáýïGø9}sñÇWy¶Ót« µY.äù#I,H †ê±ïsÐr;ÿø'ìñâïÙ¿öm¶ð¿¢ž"ñ£q­^Ú öF¸Ž(c…™IRÂ(Ÿb¼ã'Ð~$þÂß²‡Äý÷CÕ¾hºT·hÁo´‹(tÛÈd í‘&¶T%”œá÷)èÊÊùƒþ [ñÆú¿>"|!ñ†¯/ˆ ø]¯¾•§_ÌÌîÖŸ:,AØœ¢4D ÏÊ®|¡@ö_ÿðOß„¼Y7ÅOê:ŸÃ¿‰LË*x‡D¸xåi‘B#Íà­…f6‰ÏwÅxø›ÿý‘¿sñoÃ1|~ð¯ÛZ"˜µ«xGñO®çÚ¼¶baýëŽõõ÷ÇÿÛ_övý›Ä¶?ñ*\ø@Ù¢i€^jnÌ2ªÑ) àr¦fObkäñþ û]~çá_‡"ýŸ¾Ýqý³­.·q þ(ae ›—æ\Fƒû·­}©û9þ×?¿j=:êãán«+êZj#ßi—°µ½í sæ!Ê0Ï£w\ñœñ_MWÆß²¿ì[à_Ùvç\ñ=–»ªx»Æ~*Qý¯¬ê“{—Þdb± íÜä±.Ò9=\×Ù4üªÁ"äó—þÀº¯óŽ¿ªºþU?à‘?òyËÿ`]WùÇ_Õ]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çkþ(ïÚ#BÖGÉcã­6m2nËöÛçÀì¼Ñ–EúWÐÕá?´V™w/×ñV”›õ/]Ûk–ß['Ý(>ÆùëÙô­NÏZÒìõ=üË[øc¸‰¿½ªOâ¯WûÊ«v¼_ËoÁ¥ò9(ûµ'Ÿßÿ~%ú(¢¼£¬(¢Š(¢Š(¢Š(¢Š(¢Š+æÿÛþM?ãýŠz×þ‘É_HWÍÿ¶/üšÆûõ¯ý#’€?à‡?ò2ü]ÿ¯MÿF]Wô5_Ï/üçþF_‹¿õé£è˪þ†¨¢Š(ÄôßÙËàŽ‰ñQ>5è>²Ò¼f°Ïn÷öJmŒÉq0ÍEb•Î>û¡zàhíwöÃÑït%ý—<;ávÖXç:£x†I‘ãòD>UÄoÝœô;ýUE~Íðwþ i7í)íHÞð8ñU¾’tuµ“g›s»æ1ý§Íó>cÏ›ökõöuÕ¿i½_Ãڬߴö‹ hzÊ]±Ãï+ÂöÛ-)–iÎýùqÛ½}E~wþÆ?~:x£ãíàÏŒ~ þÚ³ð~¹c,U¤Fî)ZÓq˜,T®:ç<çÄ{þ ‹©ê>%Ð<5à‡óxnî[Ëk)¦žénšÆFt‰ß¡D†2 | níŽ+îχŸ >ü*Kà=$i²ø³T¸ÖµI Ó\Iuu6VyÞFǤ"óµFN}€?¿g…ÿðS¿Ù‡áœ? üàÏêLSݬº•ÜòÜo¸ °- Ô)´ÇÉŸRkõûÄÞ Òþ&|:½ðÄ«$»²ñ‡Ù5Khd’$q22$ˆÂE\çi œw®îŠóÿ‡ ~ü ðúx[á†ì|5¥¦ †Ê‹Ì`1¾V<êîYs^EWã?üËþMëÁö4Çÿ¤7Uû1_Œÿð[/ù7¯ØÓþÝP°ÿÁ#?äÌ´oû jŸú:¿Mëó#þ ÿ&e£Ø[TÿÑÕúo@PÕ4­/\Ó®4jÎBÂí s[ÜF²Ã*7UtpUîÅ_¢€<‹áOÀ„¿κ>xr CâK•¼½‚Ô¸¦DØ q)Ûü1…_jù‹âߊ¿à£Ö5«?‚þ ð6¥à¸äA¦Üj³\­ì‘˜Ô¹˜%äj“pAÀZûêŠü5øðwþ iû:ÝxÖïÁ^ð=óxïV“X½…äÒî$,JÃå\Ŷ?˜à6ãþÕ~Ÿx®÷ã¼ß²ö³¨]>áÿ‹Ã×2³aö MLBÄ4mpeZrå‡澌®wÅÞÐ|wám_Á^)·k½]µšÊò–H ¶÷c‘<ÈY$]ÊH%XzÐçÃgö˜ý à™Öz‡†µtÔ~*øÊÂòÜ_]´v… ›T– 4j‰‹B0‚ûÜׂ~Ïß ?঳OÃ{O† <ðù4Ûye¸–{™æ’îêy›-,ò%ÜaßP UP0+ö[ÁÞðßÃÿ iðuŠiš…k•²a(D]ÎY˜€9f%˜òÄ’Mt”ùÏðÛÄÿðS»Ÿè6ÿ¼#àK_Éyê²ØK9»ŽÐ¶$hC]¸.#*yí__|Xøð£ã•¶‹cñcÃÐø’ÓÃ÷Ÿo´·¹iø+à»?‡Ÿ ´•Ñt šH­Ä²ÎCÏ#K#4³¼’»3±9w'€ø;â½ÿEÔõèð?Ãù¼7w-åµ”ÓOt·Mc#:Dï‹Ð¢C¾P7vÇóÏìáð¿þ wû0ü3‡á_€üà]CI‚ê{µ—R»ž[÷¡º…6‚8ù3êM~ÜÑ@Uñá?„>7ü<‡ÀŸ´ˆµ{ “gs{f’Ë-slË(£ur‚Eè[ps]‡„|áèVþð>‹g iƒZXÀ–ð'© Q“Üã'½t´PEPàwü/þAÿ뮿ÿ ØWé7ü×þLÃáOý‚›ÿGË_›?ð\¿ùüÿ®ºÿþƒa_¤ßðO_ù3…?ö oý-}—EP=âŸøWÇ:φ|i£ÚkºEàÛ5¥ô qƒý¨ä §¸â¹_…>üð©ðGÂí!t= ÜÏv-RYeE–á·HTÊÎÁIè í€¯K¢€?3> ëßðT]OQñ.á¯ü?›ÃwrÞ[YM4÷KtÖ23¤Nø½ $1[åwlq_<þÎ ÿà§³Ã8~øÁžÔ4˜.§»Yu+¹å¸ßpA`Z¨Sh#“>¤×íÍñ¿í«â¯Œž ý’üQão†º¬>ñމik}qu–R(át{Å‹ÏY–PÊ€‚N@'5â´7ioÚ3öð„~ÝAsâ¿éz Þ%–êd´k‹g±Ýa€ ¦K‘õPRˤŠûÏâÂßügðMÿï‰ZiÕü;©˜šæÐ\Ol%ò$Y£ öòG&¢¶7`ãEwvöðZ[Åikà *¨ˆ€*ª¨ÀPÀøóð»Aÿ‚¤üø¡ü3ð'Ãï‡Z‡í’ÚÝ<Ûí·–’F€4’1/#`nf'½}=ð+Ä?ðPËÿˆÖV¿´†<¦ø)âŸí3èÒLoQ0ìs( É€ß/BzWÝÔPŒüDýž¾ |Zñnão‰~´ñ.©áˆæOûpi­â²3“nÇÉåÑŠöÅ{ AkvÖ±¬0¡UU@à8T´PEPòeÿÿ””øŸþÂ~ÿÓnŸ_Öm&_ðP_ùIO‰ÿì'áŸý6éõýfÐEP’|\øð㾉ýñk–>#¶U+ÜG‹ˆ7u0\!Y¢'Ö7\ÖÏ‹lüWᆎðzÆÒãÄVšaÑ-u²4ðǶÞ9Ø:¾Îc¼wÏ5èTPâwíðãþ }ûKü/½øMã¯øÃH¿žÞw—M»ž+ÖÒ +Mu2`‘ÎPñÓô×ÀCþ 5¤ø‹Â>ø§àÿØøNŽ+;ë« î_P[h!ØnÝ ’«ŸÝã“Àíú/E~wþØ¿~:xã·ìëiðïÄÙ~ñWŠ-´rÑ’ñf¸…Ê‘$m AJ‘—‡r 3öÈý¾ |Sñç„¿h¿ÙçÄPøcâ¿ãò {¢Eµõª³ºÂì°2H¸e)"HQð¸#ìÏ|$ø{ã/xSâO‰´‘â/¦Ñîi‚Z5êça¸…ٔъà ó^@“šÏˆ¿à®3ЦðT ðoƒnoìòx‚Ðïl,1ý¦à£c'&#øTWÖ_±¿ìµ¤þÉß !ÔN·­êwRj:¾¢T ¸»”Â++hª«“– ¹ÁbÖ4P‰èÿ³—Áân³ñ–ËÂRx×]™'¸ÕnÜ\,‰Ä 1qʃ>PL÷Í{ePEPò©ÿ‰ÿ“Î_ûê¿Î:þªëùTÿ‚DÿÉç/ýu_çUtQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ŒÿkÛ?ò ѯ|sàísXÑõ¼Ç£¦‹V·K‘¸ýüÙãq&ÅÞ>]¤t$‚Ù•øsÿuº¸ø‹ñ à?ì᤹ûW‰µVšUTÝÍ ³cêóõô §uø)>…á¯򿧮ÿ¾#h»He¼Ú<-f‘Oƒ ¸#äç€M}Ïð¯â§>4øKø‘ðßTM_AÕд3(d`ÈÅ^9€dt`U•€ Lð/ÛCâWŸ²ÇañTÖÐ[êæ¦iî˺î{›v·†£?3Ü Ä¼þ )ðëÅŸ¿d›{[Ëh|U¬]ë6PÌ6ºÙMDÛO H`i=Uƒ wÿ´ÏíùáÙkǶñÇ€¼G¨6³¾›{`–mz~@ëû…“tnáYçÈ œ_~ߺτ´;ïx—övø•§iZdOqus6•Å1Ï$çáQ@Ë1àOñwí«¬hŸ?ি¾ë7¶öº/ƒ’Æúùî¥H¡WI¨ÌŽîB€ðAäó»µ~•êß´çÀ‰WöNÒ/›Åš—Š´{ö¿ŸI’ › +_)ÒHî®\¤®¹ ¨ŽAeÝ´0$·ý™ÿhoþÓÿ áø­áMÿFÒînî-b‹QX–Y>ÌB´‹åI"ìÜJŽs•i÷SÚÃq.ù'¸¸™¤Ž&“-=ÌÁìXzWÀßðIß„w~-Ó¼Yûe|P «øÓÆÚ•Ô·#{Gmm¹š"~é–bÐñ÷R-‹…,ÑÖðQOè>7Ñ< ñãáçŠ>MâVÙ§_k¶±‹ NB€óFä¡Ë(oªdG5÷Þ½¨Ýhú£«Xéój÷6VÓO•±A=ÓÆ…–Œ¬‘‡«½•rFX šü™ÿ‚ÒE¥Ù{Ã“ÝÆ†õ|Wh–Ì@Þ»¬ï €»HQžÙ žÕ÷¯ÃÜx?öOð¿Ä?’gÑ<cªj%˜ç}¶š“Ï–<õVäÐÎß¿à¢^ý¢~*Ið›Á+ñ„uwCÕ'6­{¦ fŠÚà‚ÑÅ(šhÜX© Wå Hàø"ÿ„.çøñ7ã>®»ïü[®%Ÿ˜G,,£3ÈËþËIvAõ+í[ðWíJïÅzÁ¯ÙóHø˜xóÅ "*òÙVΓa é:—‚ËN‚+h#(T"(ú(¿`Ö~üEÿ‚™j~+Öïí4Ë…šœð™neHb‘ÛGH2îä(d»WÛÏUõ$W«÷èÕ£å̽c¿þJÛù•ýÙÂ/¿þ ¢|oÿo†¾ŸÅ¾>øñ@ÑmYkÛ½6Þ+xŒŒ7Èn6®æ!FHÉ u"¾ÏøKñwAø±ð‹CøËok>£k–GPTÔŒqI°,|ÉY]£U(»ó»H'×Ï׿?g¯ÛÄd­:k¯iÖz\3jÚ¦$-¦óFV.ã‘Ù¦VÁ;c(6°Ý¹H«?µÃ]SIý†ükð¯á ´Á´ ¦amç™ìl–4’·æv{hÝ09bqƒœW”už`ŸðQÏ x×ZÕ4ÏÙóáw‹þ,Øè²®µM"Ä&ž íŽY[s9PÈ¥‡*æ·>ÿÁE¾ü\ð—ÄÃáÝ{Ãó|,Ó'Ôõ«]FÞuX‡É–fÌÌbeááŠðßø$‡Ç¿„Ú÷ìÿ¤| Óï Ó|qá¹o¥º±ˆå¾Ž{‰'[¨sþ´*:Æø%“g !B~¾ñGì]ðoÄ>ø± i¢ÿÃòüh¸‚çÄ7¶7 ö‰ ŒßºóÒXãW.áÆÂvèp@Ì?ðU_j~½ø¥aðkdzø;Nm·ÂØ[›Ûxãí^C°SƒÁ#8«RÿÁR|/è¾.Ëð_Ç£Á3}ÝgìÆÃýiƒ&qq°4lÉ8ÝÇ^+Ï¿à©÷>øûøwà‚af§¨iº=½ºà±iÈ×LÌF72‹·VfÜy&¾Èð·ìÑ£kÿ±7‡ÿf_Í6™owá« é`Uóc¹)Óºwyû и|ø¯¥|røWáß‹:w¤éþ$®`¶¾· ‘‘KˆÝÓç ¹pÇå#87´V‘,JÏ€f –=É5ÙPEPEP_7þØ¿òiÿìSÖ¿ôŽJúB¾oý±äÓþ0ÿا­é”øëÿ9ÿ‘—âïýzhßú2ê¿¡ªþyà‡?ò2ü]ÿ¯MÿF]Wô5@Q@Q@Q@Q@ÂøÇâ߇—ZM—޼M§h:õÂZXE{u]Nä*¤Jì œ8dg­wTQEWã?üËþMëÁö4Çÿ¤7Uû1_Œÿð[/ù7¯ØÓþÝP°ÿÁ#?äÌ´oû jŸú:¿Mëó#þ ÿ&e£Ø[TÿÑÕúo@Q@Q@Q@W㈟þZGãÿiž¶—;%Ôï`³FÛ× 3 8ïŠìh¯ðßíOû5x¿U] Ã?¼5¨ê28Ž;xµkS,®ÇF¾fd$ð6f½î€ (¢€ þ^?à´òužÿ±BÃÿK¯ëú‡¯åãþ Gÿ'Yáïû,?ôºþ€?¦?ÿÈ¡¡ÿ×·þŠZé+›ðoüŠýxÛ襮’€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€6ñO€ü=àýÃ:—ƒâߨb!òfóL?gdŽWu”2¶U”ckÈÅr6_´·ÀÙà/Ã/êä>#Õí¬´ 'AžÖ÷PžèıÉ/•ç Tóv`7X€~Ÿøqðáןø÷â„me‡Yøyö¯$²ïW–0¯–¸2¹#œ“@üuý¡¾þÎÿ„ÏâθšU¤¬c¶A–êîP2c·…~g##qûªX¨æ¾¼ýµaOi´^«ð£Æ>ð=¾—=Æ¡©XÅ_ÙÈÞ|- ~n3S. üñî(N0~jø<–_ðP?ø(Š>$ø”gá—ÂdÛ¤ÚÉûË9Z)<›2Pü¥n$InÈ?{b£eF+õÛö¨ðÖ‰âÙÿÆÖºû$vv:t×Ìò}ÕjfaÿU)ôjô²š‰WQ–Ò÷_¤´¿Ë‘Í‹‹tÛ[­~íOSð|#ñCÁºOÄêQêú¹¸´ºŒ0Y’U€ee`U•€e`A‚+â¿ÚWþ àÏÙ{â5¿ÃŸxÄ—ój1Ç.yb–’[ß«… p²nIËedS¸p •'†ÿ‚Mi:÷‡f½wÃÑcâíZÖȱ<Û,ví¸z•¤—ý¬?lÿ ~ȃF½ñσµÍcGÖóZŽš-ZÝ.FãöwógÄ›xùv‘Ð’kûzx‚ÿF‡Ä6?³—ÄË:溊x´˜]d…Ô:ºíœ’ F:Šøïþ ¬Ããü øÚZÿnjR]]I#ª,+s46Vò36¨Ý9$ð1õ¯Ñÿ~×?üãß|Ð5Dñ&»âû¨ôË+= [{Ñaª…šð‰—É…W§Þr*„)À?dßÚóÁ¿µæ¯ø—Á©¢ÙhQYÈÚ’À¾l²!ˆü™dû‹ÙÇÞÏ8ãþ.~Þ >üK‚žðö¹ñCÇðnt¯ Z‹£i´ÂâRʪT¸(}i¯}øðá×ìõáÍWÂß mfµ±ÖuK^äO/šíur¨®C`aBÆ¡W°ø‘ÿñø»áOÙÿö«øÏðÇö‚¸]Æ^.Õ(5+üÆ’]CspòBó?.¼ä–6$,˜1&<€~†ü<ÿ‚ˆx_Å?4¯ÙëÇ? †¢Š(âOø(çü™?Å?úñ¶ÿÒØ+à¯ø!÷üˆ?ì%¦ÿ虫ï_ø(çü™?Å?úñ¶ÿÒØ+à¯ø!÷üˆ?ì%¦ÿ虨÷BŠ( Š( Š( Š( Š+ç}Sö­ø¥|cÒ~?‹-î¼s¬HðÇah²\˜eDg1ÜI´p¹ pŽÁ½@ú"Š( Š( Àïø._üƒþ ÿ×]ÿA°¯Òoø'¯ü™‡ÂŸû7þ–¿6à¹òø3ÿ]uÿý¿I¿àž¿òf ìßú>Zû.Š( Š( Š( Š) dð-ògįۧöKøK¨K£ø×âV›¡D–Ö^n¥,l?†E²ŽmþËàþÇøþ Aû|EÖ`ðþñ BêAI©Û\éñ»Ÿ¿¸‰!<dž1’(î:)•Ô:Êà ŽA–€ (¢€?“/ø(/ü¤§ÄÿöðÏþ›túþ³kù2ÿ‚‚ÿÊJ|Oÿa? ÿé·O¯ë6€ (¢€ (¢€ (¢€ (¬Ý_XÒ”ý~ü ýþ.ø«FÐ|Qc«ñ¦ì^è:Æc’ÞÇOIeh &IZDO5ܾmò×Ü âŸ '‰Á¯«Ù¯ˆ$¶ûjéÆâ1xÖ¡Šyâ Þa‹x+¿nÜŒg5ù_û'~Î5OÚzÿö—øµâKkOðÆ›sá}Éd”ÞéÓÚyvæ7ŒÂ‘*,fc¹dfs.ö™«¡øéû,þÖ×µì?µoÀ?xm^Ê 2/[k¨Õ¬Ö,M¾LNd•Æ ’9Ì;£VTŸFÑTjsÁMu>iÿ‚œþÈ>øU¢Ûà|¯à¿i¬š„6-äE$·„ŽòÜ&<©ÖR»Âa\Ä _õóötñþ±ñSà/Ãïˆþ!ŒGªøC°½» »ÜK ™Y²³å”vWÁß¿fÛö»—Hð—í7âO x;áÖ›uåÖ™á?µÏw¨Ip$–ì@;HbªNã2©§úƒ¤øSÃúw†<9j–Zfk ºd$P[ Ž(ÇS…Uu®cCñ+þ Aÿ“öÒýž?gýý¢O õôc‘äêгnÊ[Ù»{7½~çWãT?±ßí¯yûfØþØ>!½ðÕ휅#Ò—PÕ<ˆmÍìDq±Ó÷oXݤ p œàû+@Q@Q@Q@|ßûbÿɧüaÿ±OZÿÒ9+é ù¿öÅÿ“OøÃÿbžµÿ¤rPã¯üçþF_‹¿õé£è˪þ†«ùåÿ‚ÿÈËñwþ½4oýu_ÐÕQEQ_-~Ö^<øÿà¯ØY~ÍÞ>(ñ‰/£Ób¹}­k¤¬ªOÛ.ˆÊ®0 ~íN „Œ#€}KE,?"ñö£ã­#KµÔáÔ¥»º¸š ÛËhîV)¤“d¢R±“’Fæ]Æ¿¡:þ<¼qá?¾ý»|-á_Ú#]>%ñœ#ðû\Þ}¥î¢xî§‚xÖ&uM±ªÉ´ EU …ÅaµüÁ~ÛŸò•mþÃ>þV•ý>ÐEP_Œÿð[/ù7¯ØÓþÝWìÅ~3ÿÁl¿äÞ¼ÿcLúCu@ÃÿŒÿ“2Ñ¿ì-ªèêý7¯Ìø$gü™–ÿamSÿGWé½QEQ_žÿµ%çí“ñâ^Ÿðöy¶>ðÍÕŠßjÞ9›™ ­¡\”˜c1)$ÑF °èEüâÿÁ2þ)|s?¶wˆþø«ÇZ¯‹ôx-5hïþÝ{qyÉa2Æ—Q‰Ýʱ|Ã«àæ¾³ý§ü/ûo~Ð>0ø‘¥éZìÿ~ü:¶ž[[˜ätŸÄ -7›ç[:¹…‚ò7„Œ¬*¸PÖO LøK[&ÿícsöo+ýgå7—³ý­ØÇ½?¿±—ü³Çüo¯|Pý¸tírh¬(ílu{™ÖëSòÏ$ó󼘆 Ã{7Þ ·Ô_ðG/ˆÿ<}ðoÆ–Þ<Ö/5Û YŠ->âúy.eO6Ý^hI ->FUÎvõ¯Ø:üÿ‚—þÀ?þü—ã‡Á}xZçÃWÑj6É,¶×Vד%º>Ù]ü¹#•Ó•Àe'p'}Iÿ‹øÑâߊ³Ž¡áßßI©Ýø#T:}­ÄÄ´†ÂHc–ÝÛ%ÌldPOD½«Æ¿à°¿´†™eà?öXð›ý¿Ä~'¸µ½Õb‡÷œ2 - *¹>lóª:¨ä*dŒ:“ö7üŸöjÕÿfÙÚÏDñt~GŠ|OrÚÆ© ëjóF‘ÅlNqº(ÑwúHÎ @zQEWòñÿ£ÿ“¬ð÷ýŠú]_Ô=/ðZ?ù:ÏØ¡aÿ¥×ôý1ø7þE þ¼m¿ôR×I\߃äPÐÿëÆÛÿE-t”QEQEQEQEQEQEQEQEøÿ‹Ö¯üX~ ~ÏZî¾ñv¶÷&1Î$]•©#¾æº—îšðÿø(ÏìmàÙ³áç‚>,|8›RÖ4Xµ±¿Ñµ»éµ #òÚHÆed0˜U€!Ô.ÜdýCñßö:ý±¾-þÖ¾ý¤-u®Ÿà[ËS¡i—7ÚÍ…Ë\Eö’– Ò3r„…8 X('îoÚÃàV±ûJ~;#øJ$²±ñ­ok-´²¼Ÿd‚þÚXç¼XÚO,”([Ë-µËž(Ô¾ Ýø'RøIá oáÆ“k¡xkWÒ¬ïìl¬âH!‚È–uUŽ0ªßÎ\çšüvý¿í-þ=~ßÿ?g;„ÚVœ!¼Ô ê¦+˃5ädz›K59ôjý ýо~Ñþ[ü&øÙyáÝZ×Ãq,%Þqw,ÏiÅv.-¡P"ʬl€’¼0Êå¾%‹ö<ý¼í¿k Gö»‡Pøosâ{¥’+{;Û½bk;HZØY¢Æ#³…Ë,#níØ$³ç€ ¾,~Á?³†üMàúU¾—ðËIø]© wUh­È‚ú VIcŽf2ªÆHÁݱɨ×ן¼kk©þÏ>,ø‡à;Á¨[Üx_PÔô˘Ve6O4FN2å*}Å~rþÑß³‡üŸöŸðz|9ñ¯Š~xÃRÏ·–ú$º¼FïÊ`È&k‹i™ÕT°²@#õwFð–…¢x:ÇÀ–ÖÊú5…„Zj@à6±D!á È¿ø"w‡ímgïx¡Bý§Rñ3Ú9ÝåÙÙ[º~·ïï^¯ÿdøÝcðÓöc»øgp¿ð|Fž=6ÞoÞ‹8f»”/R»Ua>òŠùïÄ¿ ?iOø&…>!|Iø©è~0øQss ìºFµÓ_éï4‰É£YD‘üѹ1@W#©ý’þ§íIªè_·¿í7ã;o^òi4)ö}+E6sºªJ$<˜d•wïå-º€>Çý†muÿ ü-›á×­Š´F³ºÔaîR³Š`[ý ÁÔÿ»_~ÂÐ7Å/ø(×íñ‡Pýïö·ºm¶îJ¬·¿f‡¸·³+ôc_¨~ ÿŠ;ö…ðî¼>K/iÓé7”]ÙŸ´[»´È^5úWÅw_±ÿí'ðöñ§ÇÙ/[ðåö—ñ I®5] Äßj‰âyLìb{e;‚ÌÎèwG±\¦×׫›ûÕ#_ùÒ=¥ÿ“&r`ô‹§ü®ß.Ÿ…Žãþ ·ñ/þÇ^"Òc˜E}ã+»-ÜóÒ‹‰ð= 0:Ÿ÷½Å}Aû)|9—á/ìÝðãáõÔ?g¼Ò´[OµÇŒm¼<û‘ÿ¤zøþËö;øóñóâÿ†¾-~Úž&Ñ/4¿H.4Ÿ xi.?³~Ð8’áî°Íó*—_ŸxP¥‚e[ô§Äoâô Iü% ­Æ¶¶ò›ï¤xm^çiò„òD’:Æ[Ê£0À&¼£¬ü*ñ÷…ü?ûUÿÁ]Àþ,°‹]ð—Ãý Aym0ß ±[ZJ°ô[ëÀ­ëŒWÙ>$ý‘fÙÓãw†?kA}¥ü5ð߃¬ç³šÇÉ1ÚÜ^Þ¬–ðÎd2á]VfP«-€Ùkçoƒ±×íýðKã7þ;hz§Ã=gÄþ=7ùõ;fHâW"êEa¶„ª—U%€UP1Šëþ+~Éÿ·oí3ãÏÉû@x›À¶þðƱi¨^hþ—SE¸H¤VÙslÆIŒ[£Mò„]Çnl€~¯ë~,ð·†`±ºñ.±g¤E©ÜEijדÇn'¹˜1ùŒ»åpÔcƒÅ|Gûr~Ä_ ?ißê~%khôˆ5”²iú¼@#Kä¡e¶¼ÿž±üÑç*q¹Z/Ûïö[ø»ûOøsÁV |K§øzóÂ:”š·üLdY.‘Um$†9J˜¿xyC÷‡½pÞ*ðOüçâ‚®>x‹Yø}á+-VÜÙê:î–uu·‘vJ`‰ÓËW‘IÉ1Ÿ¡Á  ¿ø$oÅÿüPýš/4ÝË©Ià½Yô«+©‰wk/")¢‰‰,b.ʹéÅíWÿஞ?ÿ„;ö@ÔÛý>ÿ¯ý˜>x%ãòn,ô;InSÛuvŸi¸÷úW¯£ë“ð7ü&ð‰iƒâ¶iâ%‹‘iË5‚¸$!çŽ) íÁùr9'¬ Š( ‰?ࣟòdÿÿëÆÛÿK`¯‚¿à‡ßò!üTÿ°–›ÿ¢f¯½ࣟòdÿÿëÆÛÿK`¯‚¿à‡ßò!üTÿ°–›ÿ¢f Ý (¢€ (¢€ §£§Ïw.Ÿ ÔR]@’%u2 = (9ûׯŸµÏ€jO‹W>øcð?Äþ ðv´ÓÿÂO¯Å)´1íÙ º‰A#1°bÃk4iÿ…³÷Ùþ ÁS´o…u˽NßD×®l付…žâÙ-[µ›f¸Œ¹Æhú¨’D‰YX" %˜œRMQÓ5}'Z·7z5ìð(d·‘eMè܄ŒŽâ¿9ÿiÿÙ{ãïí_ñ|⟟üÓ¬"œG¤Ì ö©~Üψ?àŒÚ^±¤üWøÑ¦è—²]øCOŽÚßÌa„šánf[Yp8 aYIרPÕ_ ý¿?iOŠ~&ø)ðöÕ~ |2ÑnÖçÄ®î×z¬ ¡•ídM®Ë"vC°!ݳç)_ß²'ë߅ðRÿ ü0ÔïWR¸ð¿ˆµ]=î•J ƒmor¢]¤±]ø ‚N3Œžµýk×óðÛþS%wÿcž»ÿ¢.¨ú}¢Š(¢Š(ð;þ —ÿ ÿƒ?õ×_ÿÐl+ô›þ ëÿ&að§þÁMÿ£å¯ÍŸø._üƒþ ÿ×]ÿA°¯Òoø'¯ü™‡ÂŸû7þ–€>Ë¢Š(¢Š( 1À’j­–¡a©Cö:æ+¨rW|N®»—¨Ê’2;×åOíWû:þÑ´o‹¼v>(øÐxà„tén´»=Q$Ú¼‘Û™š[Õ$q‚dF‰yiÇÿðCé¼@þ.ø©oÄŸØqXé­,;—ö¹%˜DûzØ’ õ#é@Ж¥ªéš5«_j÷ØÛ)Ë<‹zÌ@¯ý§¼â/‹?³Ÿ< à/.çXñ&‘5µŽfXã‘åoïIÚŽùÆ+ñÏö¼ý“~7xãáÅOÚkö³ñ³Yê^yáðþ•*Ï¥[Úý¡!‡—}à *ÊxyX±1¹ÿà“‰âuý‹ü4þ!‘ÞÞKýLé¢Lå,…Ã.ÑŸáó„¤{8 .ýÿà›^ø7áËß~Ñ>Óuÿ^ܺ[[܈µ M>Í cV šFÜÌø%Wj®Ó¿wÌßðX_Ùáo€ü'áOŽô;O 꺠Ñu,"ŽÚÞäK×LaU|Äò]K– 7gÐ}7ŸðPÿŒÿí£ûCxcöNøöí§‡/$ŽY¡lÛÜêŽ6O+8ÈYFL2e#+´ÔOø&/|Eñö6ðeç‰î%¼¼Ò÷K[‰Žç’ÞÒáÖžâ8ŠÄ;á9çšûþ¼_öxø/¢~Ï|-ðƒA—í0ø~×d·v›‹©Y¥¸˜ŽÞd®ÌNÐBäâ½¢€ (¢€?“/ø(/ü¤§ÄÿöðÏþ›túþ³kù2ÿ‚‚ÿÊJ|Oÿa? ÿé·O¯ë6€ (¢€ (ª—í|–7-¥¤r^ÜÀ“1HÚ]§`vPÅT¶2B’@zPÞ_YiÖíw¨\Gk}é%pˆ>¬ÄVÒTY#`èà äzkù¬ý¸ÿg?³ü Õ?i¿ÚÃÆîÞ2}ZÞ×Kð½‹,šVm<Œ¦4!™”]à¦N÷#±Û÷/ì uñÉÿà›+qð¹­ï¼g]<6º”˜.š0»˜ùJbVýÞðªÄ&Hý]»Öôm>òßO¿¿·¶º»8†%D’SèŠH-ø üçÿ‚™þÌ¿ÿi߆¾ðÏÂK{{»½#V{»¨înVÙ|¦‘XáˆcÓ¯5øçûy~É^#øá?|KøãÍCÆ|wwpu_5üÈ’HcŽB-¤?½>SȨ@9¿¨¿G¯Ãà_Câ¹ ºÚi¶k~ä`µÐ…DÄRù —g¯Øwà/Á…zGƒ5/hÞ Ö¬Úú†¡cì×—l ÌwÌŒDAò#ŒaU@à¶Iþ}à¡? ü?û'þØ6·øuÒÃÄVPÚ± §]´²!Hº•Bðy:û@Û€?Y?h?ø+·Âo„ž!ñGÃÏøOVñ‹<5}y¥ÍöŸ&ÏN[«9Z>h’Y]C©àD»‡ñ ññ'ìYðÏÿ·?íûCþÑ:Òu]z Ÿ¶ÇáYã¹—ì¸XE P,àUL$m)|5†[xô}¤]\ßi6W·‘yÜA’GýÇu—ð'£EQEüªÁ"äó—þÀº¯óŽ¿ªºþU?à‘?òyËÿ`]WùÇ_Õ]QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š dÐE~M|]ÿ‚¾~Ïÿ üg¨x;Ãz>£ã¥ÊöóÞÚ4pÚ´±®"i2dPAÀ z©#ùoü>ãá?ý­cÿ ÿ ý·¢¿?á÷ ÿèkøøQÿ¸øOÿDëXÿÀ¨?€?mè¯Äø}ÇÂú'ZÇþAþÃî>ÿÑ:Ö?ð*ð Ûz+ñ#þqðŸþ‰Ö±ÿP…ðû„ÿôNµü ƒü(öÞŠüHÿ‡Ü|'ÿ¢u¬àTáGü>ãá?ý­cÿ ÿ ý·¢¿?á÷ ÿèkøøR¯üßá)`áÞ°ªO'íP~ Ûj+ç?Ù«ö¢ø[ûTx._ü5¹”ÖWH#º´”Œ€ê Xr®¤‚3ЂÑ”QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWÏ¿´¯ìÙðïö¦øm/È‹40¬éug{jUn¬îcÈD]]~efGVRXô8aôðgìýûø?௉tOx£ÆÞ!ø‘¬øRÝítí»¶’ËG†Dò˜YZå–2cù>ñP>ê©æ¾ó¢Š(¢Š(¢ŠùçÁÿñGüyñ‡„Égâ»[}~Ñz(™?Ñ®Àõf`ŽG¥} _<ürÿŠgYð7ÅHþTðö¨¶—¯Ð P}žVoP°€{×ÐÕêæ^üiWî¬ýc§ågó90Þë•>Îÿ'¯çp¢Š+Ê:Š( Š( Š( Š( ¾oý±äÓþ0ÿا­é•ô…|ßûbÿɧüaÿ±OZÿÒ9(ñ×þsÿ#/ÅßúôÑ¿ôeÕCUüòÿÁäeø»ÿ^š7þŒº¯èj€ (¢€ (¢€?š_ø\ðY«+%ýìG‰,B/P£ÃÖI<ƒþþ[;©¯ß¼!û?ü+×¾*øÚq†‹hâÎ$º¹n!·ˆwy_ =9c…æoÂÿ¼1û6ÁK¾#üZøa}ye¥xÅ‹åYF’\oº–æ8J,¯í`àX|§=+î¿xãüóâV‘ñS㆕?‚þøn?JÐKº>¬ã£n! ÁÛ%ÆBf80ÆGËÿ³ø5âì~×á+âϋד\ÛM…tù&iÞU•[™Îå^ž\q°á«õ«YYYé¶piÚ| mkkÅ Q(HãÕEQ€@Àãÿi?n-lþ-øÊËÃW7дðCp]¥–$;K$q«3xàu çËöÜÿ”«höð‡ò´¯éö¿oÚ—ö“ðOįۑþ?ø(O{á/TÑg¶wŒÅ-ÌZJÀÄoµ”HÑ1@ø;HÜäé—á?í§û1|mÕôÏ ü9ñ垣®jêæßN‘&·¼cm,‹åLˆrˆ¬N20 ŽhêJ(¢€ ügÿ‚Ùɽx#þƘÿô†ê¿f+ñŸþ eÿ&õàûcÿÒªöø$gü™–ÿamSÿGWé½~dÁ#?äÌ´oû jŸú:¿M袊(®{ÅÞ ·ð—„õ¯]àÁ£Y\ÞÉž›-âiôZèkÁ¿j„½“öcø¹š¥î›Â:ðW%‹ €ÚOzü?ÿ‚(xzã\øÅñ?âEæežÃH·³yS&©tgcõ?d5ú+ÿ3ý ÓáÀÛ„Þv¼ñ÷ÅPtM6Æó.>ËrD7R„üÈþL}Ì’¹ÚØüdý¿m ~Éžøƒ£ÅákÿøÓÅ·ZxÒ-m‚‹y^™Ï &Q†“!cË“·¨ýZýe/Š~0øŸ7íûbsãíAChZ, „Ñ `v3Br"tV"(y1ežLÎÄ ׿±ìûìÑû<øká¥ÀS­lkýbEÁ¨ÝᦎD­Ýc¸OÛŸöËðçì‘ðÛíV¾V£ã­}$‹DÓœåwêpD8àÈØA™“ì/ø§Cð7…µxšäYé œ÷ד7";{hÌ’6' §É¯äf?ÚßÂ>?ý°®iÚÃ×¾+Ѭæy´)#À°7ú >C*LŽ£‰%ù˜Îë'üßö&ñî½ÿ ‰ûP ux†S¨éVwã2Z™¾e¾¸B°Ç‘…0phöŽ¿oà·ß ’ÖGÓ¾jÓ\…>ZK}HÍŽ:£3Ü)úW¨Á+><üYý¡.>2xßân»s©Ã&©börJÏm§,ÿj–Hm‘‰Ù Š¢®zPëµQ@/ðZ?ù:ÏØ¡aÿ¥×õýC×òñÿ£ÿ“¬ð÷ýŠú]@ÓƒäPÐÿëÆÛÿE-t•Íø7þE þ¼m¿ôR×I@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿŠü-áÿxgUðwŠì£Ô´mjÚ[;Ëi~ä°L¥N0FAê#¨ ׿wà”¿ <u>‡ñÅšÏÃɯùü'%÷‘¦\Ì…HûbBgû«ÈXÛå5~¨Ñ@ûAè“·Ã 5Í ·þ žÛZ²U tö àÛÉÞ1^Í£j¶zöc®iϾ×Q‚+ˆ[Ö9T:ŸÈеwkoi5Ú `¸FŽD=aö ׄþηw~ ½ð£!{ßj7ZCûÏ o¾ÝñýÖ‰Ô/Ò½UûÌ]`ÿ “Kÿ9»[üKñ_ðà{õQ^QÖQEQEQEQEQE|Iÿÿ“'ø§ÿ^6ßú[|ÿ>ÿ‘â§ý„´ßý5}ëÿÿ“'ø§ÿ^6ßú[|ÿ>ÿ‘â§ý„´ßý5~èQEQEWóKÿïð¶ÿà¥^:øžÿ½[Sâ]q¨mºÈÿrèì+úW;FÂ3µˆ8>‡µ_±‡íãïÙ+â·Œ,ü=à üaãmjÎ] ßNÌ¢X/£¹RwÃo$»Yh×k1¸uзüö¡´ý¾ ]é^¥øãxåÒô [šád˜yr]…6 6`Ò”\`’5¿`Ù™ÿfÙóLðÖ¹ §‹5÷þÕÖÈ!Š\Ì %¾Gh" ‡‚ûØpÕó§ìŸû|RñWÅøkÿÛFàjŸ'Úú6ˆáZ ýS²)1£Æ òb\ˆ‰293§ë=üÁ|6ÿ”É]ÿØç®ÿ苪ý”ý¢¿à¡¿³÷ìËâû¯x÷ûZïÄ6ÖÑ]}–ÂÌH'RÑâY8ùÇ?7üÖxCö£‹Aý´Çí[£»ZÍâ;Íb]>)•moZEx‘Û Î‘H@'˜s€xþÏ(¯Œ?fÿÛÇà/íKâ‹ü2“SMjÒÁõ)­ïìü–ñÉM™䌰yT`1Ï8é_gÐEPàwü/þAÿ뮿ÿ ØWé7ü×þLÃáOý‚›ÿGË_›?ð\¿ùüÿ®ºÿþƒa_¤ßðO_ù3…?ö oý-}—EPEPÊŸ·ŠÏƒ?dO‹:Ú¿–Ï ]Ù+t!µ,Ô|Ì1ï_ŸðD ‹ƒ?¼nSkkô6î˧ڬƒðÍÑüs_`ÁLô[[ýˆ>&ÙèнÄÑC§\Ȉ2ÞE®£m<ÍÇdØÿuM~)~Æÿµí'¥üŸö^ý–<&§âíKS¹¼“_ÍŽÆ ¤7tXae)ÄÓHPp6# ?ðQ/ˆš×íñŸ°ÁyþÓ¬kWÐ^øžæ?š XGš‘ÎGhÇúLƒ‚ Ä£,Ûkõ£á×€ü=ð»Àzï Â`Ò<9e²ž[ˆsÝÛ˜÷bOzù+ö(ýŒtŸÙsú‡ˆ¿¬Ú(¢Š(¢ŠüUÿ‚Úø¬Øüð‚Õöc_’ôŽì¶¯LÜ©úâ¾öý‡<3€ÿc¿…Zlåmãÿ„~ßQ± û@ç,OL‰9é_˜¿ð\#V›Jø=¯E ¾™i6¹m, |‘Ïp¶o±õu†B£ý“\_…>$~×_ðP‡žøð›Ãoð×á^›¦Ùiz÷ˆ$g‘nã´…#•b±oFÛÅ´ ³d d±À¬hì¿ðQ_ÛÊÓÆzrµßÁ…D·úGPY<Å1ƒDó"± ¼+»i ýʯø ð3À_³¯Ã=+áwûO#NÓ×t³> ÷—.›s;7I!öP¨ ªçgü¦÷ö‡Ò¼à WàElj­ ´ºÔ_W›ÃxžZlƒÈk¦´ „Îý¥þ\ç½~¥ø‡Àžñu¤¶+ðö¬ÛNIí¤Wáþðe‘X÷õ¯åÏþ )ð;JýŽiŸxÃà\²xnÏW‚=oNŠ?ñ.¾¶˜¤‹ bÇËʬ[ o)€ ý(ø=ÿ‡øwð÷NOVÚ¦‹ã;VB;k?´[ÜÏᦅ•†Ñ!(áJ1+–qøcâF•ñþ ·ûIéšÿ€ü/}ῆº41éñê·Ñb+k%•¤žwõr\ÈÌvÁ60 œ’€?£„þ/›âÂßxöâ?&_èÚv¦é»ZòÙ&+ŽØ/ŒWXþÐtÏ hg†4X¼;Hµ†ÎÚ<çd6è#sß  VÅQE*ŸðHŸù<åÿ°.«üã¯ê®¿•Oø$OüžrÿØUþq×õW@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÔýü¢Š(¢Š(®_Çñø+ÄFJ²é÷dÔ`×Q\×Œíæºð~»kl†If°ºDU,Íõ&€?ƒ]H“¨]É2¿þ„kè¯Ù‡öQø§ûYxºÿÂ_ ¾Ånt«qs{y¨ÌðÛA¶Ô cId,ç!B¡èIÀ×ÎÚ¢xþÙ-5íeŠá#q$d:,±º8ê’FêêpÈŠóšëü{ãÏ|Oñ†«ãïjRköµ7Ÿwu.7Hø 8PUTUP¨ÈPEPEPï'üI?á"øŸã³ì¶-·#ñ¤¿|Cðë@Õü2¶º²ømá=#Âv÷β\G¤Ø[Ø$Σ Ò-º bœ+ùÿ…{ÿyÿŸ¯ˆ¿ø:¸ÿäš?á^ÿÁ^çëâ/þ®?ù&€?ªº+ùTÿ…{ÿyÿŸ¯ˆ¿ø:¸ÿäš?á^ÿÁ^çëâ/þ®?ù&€?ªºþ^?à´òužÿ±BÃÿK¯ë›ÿ…{ÿyÿŸ¯ˆ¿ø:¸ÿäšòo~ÈðP¿‰úÄ~ øàÏx£T†¶K­Nä]̰#3¬a啘 gb8‰îhúàðoüŠýxÛ襮’°¼/m=—†t‹;¤1ͼn§ª²Æ ƒô"·h¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ž`ÿŠ7öž¹eñ JYWѵ 'å`>¶î úWÐÕó÷í ºW†ôo‰6hZçÁ¥¶ ûGÌÖŽÞMÔcÙ£|Ÿe¯S(÷ªº/í§›øòdŽLfçþ]Ïð¹ô 3Eq w8’)T2²œ†VB*JòÚ:Š( Š( Š( Š( Š( ˆ¿à£Î‹û|Q À±¶'©ûd øþ}<#Á?팊&mCNp™Šˆeã® gÞ½gþ Q™û,xfAÖ/ØŸÀéú€þ¢¿ŸŸÙ×à'ÅOÚ/Ç7^ø@":Õµ„·ò ®…¢ýš)"þsÔî‘>_ǵpTWò©ÿ©ýº?ç‡þ—ühÿ‡TþÝóÎÃÿKþ4ýUÑ_ʧü:§öèÿžvø:_ñ£þSûtÏ;ü/øÐõWT`Ó4Û[©ï­m"†æçl©«ÉŽ›˜ ·ã_Ëü:§öèÿžvø:_ñ£þSûtÏ;ü/øÐõWE*ŸðêŸÛ£þyØà鯸uOíÑÿ<ì?ðt¿ã@Õ]üªênùçaÿƒ¥ÿ?áÕ?·Güó°ÿÁÒÿUtWò©ÿ©ýº?ç‡þ—ühÿ‡TþÝóÎÃÿKþ4ýUÑ_ʧü:§öèÿžvø:_ñ£þSûtÏ;ü/øÐ×_ð\¿ùüÿ®ºÿþƒa_¤ßðO_ù3…?ö oý-~ëðJ_Û‰Ö#.‹eªàœÖ-ÎΜþùÓ¯¶kBËþ Oûs-¤K¢ãˆŽ³Ùíò1_È‘@Õ­üªênùçaÿƒ¥ÿ?áÕ?·Güó°ÿÁÒÿUtWò©ÿ©ýº?ç‡þ—ühÿ‡TþÝóÎÃÿKþ4ýTº$ˆÑÈ¡‘AêªÖ:}†™n-4Ûh­ RHŽXÐÉ!TÍ+ÿðêŸÛ£þyØà鯸uOíÑÿ<ì?ðt¿ã@Õ]üªênùçaÿƒ¥ÿ?áÕ?·Güó°ÿÁÒÿUtWò©ÿ©ýº?ç‡þ—ühÿ‡TþÝóÎÃÿKþ4ýUÑ_ʧü:§öèÿžvø:_ñ£þSûtÏ;ü/øÐõWE*ŸðêŸÛ£þyØà鯸uOíÑÿ<ì?ðt¿ã@ïüþRSâû øgÿMº}Yµü—ÞÿÁ)?nÔI“ÃvWlJæäk„9yü½>îxãµm‹ËxÉ+o{®bù¶‰Šnä¡SÞ€=îŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾yø£ÿŸÅ?‡ßSä·ºž_ß°ïøßm¸öT2Iãšú¼¯ãg…&ñŸÂïh–`ý¸[››B¿x]Z‘4;Ob]Ï¡¯G*«׊ŸÂô~G÷^ç6.-Ómnµ_-OT¢¸ß‡ž+‡Ç>м] ñ5´Šg¢ÊW/üÁ_»*â«JP›„·ZÂjIIu (¢³((¢Š(¢Š(¢Š+óƒþ Á™ûø½ÿç•îßôKýkô~¿<ÿàª1yŸ°×ÄÿžRèíùê–«ýhð«þ G7—ûnx1?ç­¦®¿•„íý+úá¯ä+þ e7•ûsü:OùêšÊÿå&í¿¥^´QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEV7ˆ´;?h—‡5›]RÚ[iÜ™ {àñ[4UBn-In„ÒjÌñ/Ùë\¼Õ~éú^¬âká©&ÑoW©Y´÷1 û˜Â1ú×¶×Ï>ÿŠ?ã狼(~K?ÙÁ¯ZŠ'‹ýíG«1çÚ¾†¯G7‚öîqÚV’ùëo“ºùØ6ýŸ+Ýi÷W (¢¼Ã¨(¢Š(¢Š(¢Š(¢Šü˜ÿ‚ÌÃæþÉš[ÿÏ/iíùÚÞ/õ¯Ìßø#TÞ_ík¨'üõð¾ ¿•Å£JýFÿ‚Ä@fý•€É‹ÄZk~i:ÿZü®ÿ‚>Cwoû]«¼.±ËáýI7!~ô-×§jþ©(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¢ÀÓ5²È¦dPÅ27<G\u  h¢Š(¢Š(¢Š(¢ŠÿÖýü¢Š(¢Š(®WÇò$x‡þÁ×ú%«ªª:¦Ÿ­¦ÝéWYòo!’ÇlŠTãð4üê?ò¹ÿ®¯ÿ¡ûûþ Ññ_ömøKñWXÖh½.+«{«‹K¼¸¶û\6“‡Ì…£Ã`Șðq‚?ŠºOŒŸðKOÚÃÁ~7Ô¬üásã=Iä{;û‹u/1*%†YDp>÷sÑy?ü;§öÕÿ¢W¨ÿßëOþ?@Ÿí¡ãƒh?x«à6˜º_….VŠ‘}ž9®šd‹øÛœqž¸æ½ãþ ¿ñ{öføGãïj?´F› ¦òÑM½¹µû\6왦iqŒ6;b¼ÏþÓûjÿÑ+Ôïõ§ÿ£þÓûjÿÑ+Ôïõ§ÿ =ý®|eð‡Çß|OâŸÚXÒ|%y"#X¼„y,©ð+¶H^={×ÔðMŒ¿²×ÂøÂãö‹Ó!–ïQ‚Õt«ë›_¶EFeûD[0ÛMÑØ9 ŒŽþ=ÿéýµè•ê?÷úÓÿÑÿéýµè•ê?÷úÓÿБ~ÓÞ)øYão^1ñOÁM(è¾ Ô.Õì-vy`,®±ÿË0y?„0Àðjû[þÓûjÿÑ+Ôïõ§ÿ£þÓûjÿÑ+Ôïõ§ÿ Šh¯µ¿áÝ?¶¯ý½GþÿZñú?áÝ?¶¯ý½GþÿZñúø¦Šû[þÓûjÿÑ+Ôïõ§ÿ§'üŸöÖv>êcŽg³ó3àPèOüþFo‰ßõçcÿ£¿¡ºü´ÿ‚eþÆ~>ý—¼1â/üS0ÚøÅFû¬âÖ2@’D%FcÈB@©íú—@Q@Q@`x¯ÄÚ7‚¼/«øÇÄS‹]+C´žúîSÒ8-£2Hß‚©5¿_“ðWߎ?ð®gK†UÇ—«üGºû3…8eÓlÊÍtÃÞsDwWjø7öø;/íÍñ§ã¯Ç‰Ð°êö:Œ ãÙ¯µÕx¢1×ìvªU{ÈkÚ?à¿õxÛâWì“ã‚mu-6êmFÖ?êîìÜYê0Œõ?,LìŽØë_¢_ðO/ÿð¢eoèWÖþF·âλ©‚0ÂçPUtFš(Q0õC_•?·5÷ìwÿðGí?áø=ÅŨ],CI!ÓUG÷¥·u“'øå'PôeESÓµ _OµÕtÉÖæÎö$žPå$ŠE Œ§¸`A®PHsÚ–šÝ(°ÐsšnM.xÍ5ŽØÑ¾€—A3èI¥'ÜÖeö±§iˆÒ^Ü$J£'süëæÿˆµGÃÏ—µ[Ÿ´]6¨ÈÏÖº)aêOáG#3¥I^R±õlqž}éŽçùñ/öë–Ýfx4Ë"ûiçHU˜‚B†lH㯼ÿöÞµ¼u#Äö)ÏmB!ÿ³×|r¦þ)$Ïœ©ÅÑo÷tÜ—s÷ð¶FwRoìM~éÿ¶TsšŠñtC*Mk4WúÝ?ú‘ýyŠ?Š13ËWóasûMio½¼y{ MGþÔ¨ö‘ÑxÛãèGýÄÔÿíJÙKùÑ ‹ªÿÐ;þ¾Gô±æÆ?‹4žb_Í0ý¥4a×ÇðÿàÈñÊCûKè½¼}þ ‡ÿNY<-üD/õ¾¯ý¿ÇüéozzÈŸÞüÒÚSFÿ¡þ/üþ9Kÿ )¢ùŸâÿÁˆÿã•+*‹ÿ—ˆëu_ú—ãþGô³æGýñFõþõ4gö—ÑGüÏÑàÄñtÏøi}ÿÌýþ ?û:?²£·´@¸¶¯ýËñÿ#ú_Þ½š Ç&¿š«OÚsF¶˜L¾>‰ˆìuÿÅ×Kÿ ‰¤(øÎÜãþŸ×ÿЧ,ª þ^"—Ôÿ y~?äF"UîhóûÕüí/ím¦Î™>9±QØIÿÐê¹ýª´¬îoÙ~šñuÙpoãE>+«ÿ@ò?¢Ýê{ÒoµüêÚ·GS¸xòÏÿIÿÅÕûYé'§Žì‡×ROþ.›Ëb¾ÚñdúáåøŸÑ'˜¾´†EìkùÙ“ö²ÒÈÛÿ Ýá©'ÿUíY¤¡çǶg?õSÿ³ÒYl?¸®¯L<èÀH¿Þ¥2 þ*þt£ý«´‚ßò>Y®¤ èu®Ÿµ.šë•ø‡¦.?½ªÆþ‡Måqmõ~Eûo骢1ã]3#¹¿Oñ©¥ý¶´ÙiñÆ”?î ŸãSýŸ³Fßë$íuF_s?zÄŠ:°§yŠz5?~ÙR’ÇÇškg°¿_ñ¦Gûhé(Á¿á8Óøÿ§áþ4ÿ³áüèÅñE[ëB_sÿ#ú Þ=hÞñWàt·”x_é {ß-EsûkéWði‹ô¾_ñ¡å±·ÆÖJ]P—ÜÿÈýöó³Q¼õüù·í¡¤/Ê|q`}Åðÿ³iûoi6/çØI··ÛÍ?ìÔׯŒ—T½ }Ïüègh»×ávŸÿ4Òt’ë3ßÏúW±x;þ •ð¢á•|Y«ÙÛ©êVLãòZç–ô’ûÏBŽ i*r_&~¶ó»98§àõÍ|- ÁF¿d]_ sñO°c×Ï,ƒó"»AûxþLJþjÖ„?íàÿñ5É8µ£=ºuTÕÑõ¦sš’¾Eÿ†òýŽçâÖ†í¹ÿâiíéûÿÑYÐÿïûñ5,Öç×tWÈŸðÞ߱׋:ýÿoþ&øoØãþŠÎ‰ÿ›ÿ‰¤×tWÈðß±Çýþþ¿ÿMÿ†úýÿè¬h¿÷õÿøŠúúŠùþïö6ÿ¢±¢ÿßÇÿâ)?á¿coú+7ýü“ÿˆ °(¯¿á¿¿cQÿ5cFÿ¾äÿâ)?áà±§ýþú—ÿÐØTWÇððØÐÍWÑÿï©øÝ'ü< ö3ÿ¢¯¤~rÿñºûŠøçþ ûÑWÒ?9¿øÝ'ü<ö2ÿ¢¯¤ÿäoþ7@cÑ_ÃÁÿcú*ÚOþGÿãtŸððØÃþж•ùOÿƨìª+ãOøxWìaÿE[Jÿ¾gÿãTŸðð¿Ø¿þж—ÿ|Üñªû2ŠøÇþûÑVÒÿï‹þ5Iÿý‹¿èªéŸ÷ÅÏÿ ³è¯‹ÿáâ±oý]3þýÜÿñšOøxìYÿEWMÿ¿w?üf€>Ñ¢¾,ÿ‡‰~ÅŸôUtßûõuÿÆi?á⟱_ýM;þüÝñšûRŠø«þ+ûùªšwýù»ÿã4ŸðñبÍTÓÿïÅßÿ µ¨¯Š?áã±OýM?þü]ÿñŠoüÙ¢¾%ÿ‡þÅþj‡þÞñŠOøxçìOÿENÇÿ¯øÅ}·E|Gÿý‰¿è©Øÿà-ïÿ¦ÿÃÇÿboú*v_ø {ÿÈôöõñü<‡ö%ÿ¢¥eÿ€—ßüIÿ"ý‰?è©Yÿà%÷ÿ#ÐÜWÃßðòOØ“þŠ•Ÿþßÿò=7þKûÿÑR´ÿÀ;ÿþF ¸è¯†ÿáäß±ý+OüÔ?ù“þQûÑQµÿÀCÿ‘¨îZ+áŸøyWìB?æ¨Úÿà¡ÿÈÔßøy_ìAÿEFÛÿ5þF º(¯…¿á忱ýoüÔù“þ]ûùªÿø/Ô¿ù€>ꢾÿ‡—þÃßôT-ÿð_©ò-7þcûùªà»RÿäZû¶ŠøGþeûÿÑPƒÿºŸÿ"ÒÃÍ?aÏú)ðÿà·SÿäJû¾Šø?þkûÑO‡ÿš§ÿ"RÃÍÿa¿ú)ñà³TÿäJûÆŠø3þqû ÑO‹ÿz§ÿ"RÃοa¯ú)Ñÿà¯UÿäJûÒŠø+þwû ÿÑNÿZ¯ÿ"RÃÏaú)Éÿ‚[ÿèïj+àøy÷ì1ÿE9?ðS«ò'ü<ÿöÿ¢œ¿ø(Õ¿ù€>ù¢¾ÿ‡ þÂßôSWÿ¿ÿ!ÒÃпaoú)£ÿú¿ÿ!ÐßtWÀ_ðôOØWþŠhÿÁ>±ÿÈTŸðô_ØPÍMÿÊ6±ÿÈT÷õðü=öÿ¢™ÿ”mcÿ©?áé°§ý¿ü£kü…@ Wç÷ü=#öÿ¢”ðM¬ò7þ•û ÑI?ø&Õÿù€?@è¯Ï¿øzWì-ÿE!¿ðM«ÿò'ü=/öÿ¢Žßø&Õ¿ù€?Ah¯ÏŸøzgì1ÿEÿðM«ò%7þ›û ÑDüjßü‰@A| ÿŠkSñ¿Â©>EðÖª÷6IÙtýLˆU}v±pHï_CWåtðPÙ>ûã_<#ãG½ÑâÑ'µ×Øiš„mo2,–Óùon¯/ÎÆ3°1PA kÑÿáéÿ°×ý)?ðMªÿò-z¹·½8×þtŸÏi~)¿™É„Ñ:ÊíòÝ~èUùéÿPý†¿è Ëÿ‚}Sÿ‘i¿ðõØoþŠ¿ø'Õ?ù¼£¬ý ¢¿<áê_°çýóàŸSÿäjOøzŸì9ÿCôßø'Ôÿù€?Cè¯Îÿøz§ì;ÿCìÿø'Ôÿù›ÿUý‡¿è{Ÿÿú—ÿ#Pè•ùÙÿVý‡¿èz¸ÿÁ>¥ÿÈôŸðõØþ‡›üê_ü@¢•…â_ øgÆš%dž¼c¤YëºEæÏ>ÊþÞ;«i|·&ø¥VFÚꬹ0r|ÿXýˆ?èx¹ÿÁF£ÿÈõó—íkÿ0ý|kû¢¿0?áï?±·ýu¯ü¿ÿIÿzýÿè!­à­ÿøªý@¢¿/áï±Çüÿëø,þ.“þùûÏö·ÿ‚ÆÿâèõŠü»ÿ‡¾þÇ?óù®౿øºOø{÷ìuÿ?zçþ ÿ ÔZ+òãþÿûÏÖ»ÿ‚ÃÿÇ)?áð±ßüüë¿ø,?ür€?R(¯Ëoø|ìyÿ?÷þ ?ûm'ü> ö<ÿžú÷þ ?ûm~¥Q_–ðø?Ø÷þ{kÿø-üv“þ ûÏMÿ£ÿŽÐêmùcÿ…ý¿¿âüþ=Iÿ…ýÿ½âü¯ÿ Ôú+ò¿þû zø‡ÿ«ÿÇ©¿ðøÙþ¦ü§ÿ Õ*+ò³þû úx‹ÿ©ÿÇé?áñ?²÷|Eÿ‚Ôÿãôú©E~UÃâ¿d/îxÿ±ÿñúoü>/öCÿž^#ÿÁlü‘@ªôWå?ü>3öDÿž>$ÿÁt_ü‘Iÿý‘?燉?ð]ÿ$PêÍùKÿŽý‘çßÄ¿ø.‹ÿ’i¿ðøÿÙþ}¼Kÿ‚èù&€?W(¯Ê/ø|‡ìÿ>¾&ÿÁt?ü“Iÿ‘ý‘ÿçÏÄßø.ƒÿ’hõzŠü ÿ‡É~ÉóçâoüAÿÉTßø|Ÿì‘ÿ>>'ÿÁtü•@mütÿŠgTðGÅTùWÃZªÛ^¿eÓõ1öy™½v±Bï_CWãoÄø+?ì…ã¿k~¸±ñ2ZÒXÛNƒjHÃ÷nqtOÈøn«­ƒþ Éû=øNÓ|'ãû˯ØYÚ­Ô¶¶pKo3˜”ù±»\©e|îåG'«Õ«ûÌ$'Ö—äõ_1Év³]Õþíè~²Ñ_“ðùoÙ+þÞ(ÿÁ}·ÿ%Óáòÿ²_ý|Sÿ‚ûoþK¯(ë?Y¨¯Éø|Çì™ÿ@Ïÿà¾Ûÿ“)?áó?²gý¼Uÿ‚û_þL ÖŠ+ò[þ5û&ÿÐ'Å_ø/µÿäÊOø|ßìÿ@ÿà¾Óÿ“(õªŠü“ÿ‡Î~Éÿôñ_þ í?ù6“þ;û'ÿÐÅŸøgÿÉ´úÛE~HÿÃç¿eúø³ÿ,ÿù6“þ?û(ÐÅ¿øgÿÉ´úßQ ˆ žP‘KŸáÈÏå_ΟíÑÿ$øûJ~ÏZ¯Â¯é~ µÖo/,®#“P´¶†Ü-¼¡Ü3Gu+d®qòž}+óËöøùàÿÙ«ö‡Ò>+xòÚöïF²´¾·–=>8å¸-s F›VY"Rœ¸ã×¥gÔWägü>‹öRÿ ‹¿ðËÿ“©?áô²Ÿý þ/ÿÀ þO ×J+ò'þIû*нâÿü±ÿäúOø}/ì©ÿBïŒ?ðÃÿ“èõÞŠü‡ÿ‡ÒþÊ¿ô.xÃÿl?ùaIÿ§ý•¿èZñþéÿü° ך+òþQû+гãüÓÿùaMÿ‡ÔþÊÿô,xËÿtÿþXPëåùÿ©ý–?èWñ—þéßü±¤ÿ‡Õ~Ëô+xÏÿtïþXÐëõùÿ«ý–»xWÆø§òÆ›ÿ«ý–ÿèTñ—þißü± Ø+ñ÷þYû.ÿЩã/üÓ¿ùaIÿ¬ý—ÿèSñþéßü° Ø:+ñïþYû0vð—ŒðOÿå…7þ[û1Ð¥ãüÓÿù>€?a¨¯Çø}oìÉÿBŒ?ðOÿäúOø}oìËÿB‡‹ÿðOÿäúýˆ¢¿¿áõß³?ý /ÿÀ]?ÿ“©¿ðúïÙ£þ„ÿà6ŸÿÉÔûE~9ÿÃë¿f¯úü]ÿ€ÖüIÿ¯ý›;x;Å¿øaÿÉ´ûE~8ÿÃëÿfßú|[ÿ€öü›Mÿ‡×þÍÿô&ø³þüXòm~ÈQ_ßðûÙ÷ƒËöxÿ¡'Å?÷îÇÿ’¨ö^Šügÿ‡Ù~Ï_ô$x§þýØÿòU'ü>Ëö|íàß?ü•@³4Wã'ü>Ïö}ÿ¡Å÷Å—ÿ$ÒÃìÿgÿú¼Oÿ|ÙòM~ÎQ_Œ_ðû?€?ô"øŸþù²ÿäšOø}§À/ú|MùYòE~ÏQ_‹ÿðûO€Ÿô"x›ò²ÿäŠOø}§À_ú#üìÿøõ~ÓQ_‹ðûo¿ô xþú³ÿãÔŸðûoý¼â/ûêÏÿPíEø­ÿ·ø#ÿDÿÄ_÷ݧÿ¦ÿÃíþ Ñ>ñý÷iÿÇhö®ŠüSÿ‡ÛüíðûÄ?÷òÓÿŽÒÃî> ÿÑ=ñýü´ÿã”û[E~(ÿÃî> ÿÑ<ñýý´ÿã”ßø}ÇÁžß5ÿûûiÿÇ(öŠüNÿ‡Üüíðï_ÿ¿Ö¿ü]'ü>çàçý½{þÿZÿñtûeE~&Ãî~Ñ:׿ïý¯ÿMÿ‡Ý|íðç]ÿ¿ö¿üU~ÚÑ_‰?ðû¯„?ôN5ßüµÿâ©?á÷_¿èœkŸøkÿÅPí½øÿ»øIÿDß\ÿÀ›oñ¦ÿÃîþvøo­ÿàM·øÐíÕø‰ÿ»øOÿDÛ[ÿÀ«oñ¤ÿ‡Þ|)ÿ¢k­àUµ~ÞQ_ˆ_ðûÏ…]¾ë_ømIÿ¼øYÛᦳÿvÔû}E~ÿÃï~Ñ3Öð2ßü)?á÷¿ ÿè™ëøoþûƒE~ÿÃï~ÿÑ2Ö?ð6ßü)?á÷ß ÿè˜êÿøoÿÄÐîøuÿ¾øiÿDÇWÿÀëþ&“þðÛ·Ã [ÿ­ÿøŠýÆ¢¿?á÷ÿ ÿè˜jßøÿIÿ¿øsÿD¿UÿÀø?øŠýÈ¢¿ á÷ÿÿè—j¿øÿ¤ÿ‡à|<íð»TÿÁ„ün€?rè¯Ã?ø~Ãïú%º§þ ÿãtŸðü‡ÿôK5?üÁÿƨ÷6Šü1ÿ‡àø·ÂÍOÿ0ÿñªoü?À}¾ê_ø2‡ÿPî…ø]ÿÂð'ý­KÿPÿñšOø~¿è•j?ø3‡ÿŒÐî•øYÿÂð?o…:þ áÿã4ßø~‚è”j?ø4‡ÿŒPî­øSÿÃð_ýCÿ‘ñŠOø~ƒ?è“êø5‹ÿŒPî½øOÿÄðwo„×ÿø5‹ÿ‘é¿ðüOÿÑ&¿ÿÁ´_ü@»UüÎ~Ú?³íð ãçŠÿj¯†š®¥«iz¦£s©&¯¤Ë#^iÐLåŵÜ-äD˜ <&5Pûs°}ÿÄðý[ïüEÿÈô‡þ ‰á?ú$—¿ø7‹ÿ‘¨¿²·ü'HÕ¾ÇàßÚ’Ét˳¶4ñŸ6ÎzfîÕ2ÑÝá ¹?êÑFk÷ Ã~&ðïŒt;?xOS¶ÖtA–÷vr¤ðJ‡ø’D%HúþBjOÚö`øÿ-ÏŠ<ðŠïáçŒ'%ÞîÃQ…¬nœœ“qf-ÑwòFÈÄœ¾þ•䟳×í_ñÃöc×?µ~kïoc3‡ºÒ®s>›wùë  cÌB’À`(ûm¢¿9ÿc?ø(¿Ã¿Ú¶æ?^é7>ñìp´ÒX„{›9Ò!óËÊ.¥fFv©~µú1@Q@Q@ÿ×ýü¢Š(¢Š(¦I"D,¬ÄðI§×+ã¢G‚WfRG;Ey?ü?\ÿ¢Coÿƒ—ÿäJü"Ôä!uÿ]_ÿB5õ—ìwûø»öÀñž­áŸjöº¦ƒj—W—w*Ò±HÑ#^Y˜ƒÜ'¦@?JÿáøÚçýü¿ÿ"QÿÆ×?èÛÿàåÿù¿$¿iÙûÄ¿³'Å­Oá7Šo-õ«áž;›lùsC:ïFÃr§Aè:õ/Øßö1ñoíƒâ-kJÐu«]Ç@…%º¹¸V•³)!#\NI ýÿ‡ãkŸôHmÿðrÿü‰Gü?\ÿ¢Coÿƒ—ÿäJüƒý¡þx“örø±¬|'ñUÔךQF[‹b|¹b•w#€Ü®GPy½Ãö2ýˆ¼cûc_ø‘trÓÃúw…Òßí7(Ò³KwæyH‘§'ˆ˜’HÜÐègü?\ÿ¢Coÿƒ—ÿäJ?áøÚçýü¿ÿ"Wã—ǃž$ýŸþ-x‹á‹f†ãTðìÑÇ$¶ìZ)Rh’xs‚G"œ‘œEy~ôÿÃñµÏú$6ÿø9þD£þ®Ñ!·ÿÁËÿò%~ Q@½?ðümsþ‰ ¿þ_ÿ‘)ËÿÇÖC ÿ-Êç5§_²ü¢€?´oÙöÁøû^x*ëÄ~´ŸFÕ´‡Xµ-2å–I-ÝÆQ’EÀ’6ÁÚÛTäTqŸ­«ùåÿ‚ “ÿ /ÄåÏd±?ùëú Š( Š( ¿”ïÛ›âW‹¿i?Û’÷LøsᛯÙ|;’=6ÓH²¶žð]G¦Kæ_ŠØ1ÉpdFuë^zWôOûY|jƒö}ýžüiñKÌT¿Ólš-9[~¡rD6£oñ+«0þê±è+òÛþ¹ð^x4þÑž ¤½×§þÆÓ¦—%ÚYg¼—q儳×?Þ‰¨!û~ÁHÀ~Ì7`ú—õÏþ*¾Jý±¾2~Ú´ÏÃK}+â¿ìõ¨xgLðµÃêçV‡CÕ¡kX¢…Öo2Y÷F”;¤-€6‘Šþ£j†©¦iúÞ™w£jÐ-Õü2[Ï Œ¤‘J¥w ¤ƒ@œ¿ðJÏŽ?ð·e/ÚǛ­ü=”è—Ž\Ú¢‡²|v_$ˆ‡©‰«ô¦¿›/ØwTÔ?coø(O‹?fÏÎÑè¾&¸—F‰¥8H¤Üé þÔ±?–£Özþ“h¤#4´Öé@ <{בüQø»áŸ†zL—šÄáeÚv.y'µwþ!Öm¼?¢Ýj·N;t'$úWâ/Ä?ø«ãÇÄ£§Ûn’1)ŒF§åÙ¸xâ½ Ú>w±ó΃±üÈø$7Ž‘Š?Ä=8×ýoþ.´4ßø#׌oæ?ÄÍ6=3a1ú2¿¡øSÌV¼´#’yˆß …*éøVÊ)/u~'1ÄQ¦ôôGãjÁü|ê|SÒðê?ÿ¬=_þ·ñ IO1þ$iÒ§ªéóñÊýøð¯Š6…²½o¡5éRÅ ä%[Ž>µÊéò»IÕ,O¶¥ÍNVgó-ÿ†ñ—ˆöÿ°|¼äZëtŸø"÷‹õtÝÅM9O¡ÓfÏþޝÝ?øzM6á§…wDÜñÚ°tÝB}6a<㸮§‡„£x£ÄY­zU9j»¯CñœÿÁütájé§þá“ñêãõ_ø#gŽt‰¼©þ$X°þðÓ¥Çþޝè×A× Õ­× <Ìr:UÝWK‡R·h¤POc\Š .ÒG¹Vs>j2?šÿ„ñno‰6 Ø:_þ=]Ž—ÿXñN«’‹x'·öd¤ÿèúý²Õ´›.줠„üé4­RãJe‰²™é]o¤¯Ã¥œÔ„ùk~Gã$ŸðCÿ"OŠº{Oì¹Gþ×®+Pÿ‚8øÏM”Åqñ&Èc¿öl¿üz¿£_¶Õ¡6$î*ί£Ûêv1ïŽkš1Q•¦j­IÔ‡5 ÍŒðH/³…—âmš)î4Éþ×ÚiŸðE?ê‘y¶ÿì~ŸÙ2òM~ÔjºLúEÁŽU%sòš“IÕ®tÉÃÆß.yÕ,$Z¼OŽqZ9+‹×ðDO[ÂeOжR‘Øi2ý¹®_ø#”Å/ÄËEe8çK“ÿ’+úKÒukmRØI{Šç0[“ÿ`7ÿäÚý{²¼žÆàOÚGQ^ÛáßêBØ QN¾—T,»4ö¾ìôgóË«ÿÁuýM“üR…—ûÃFoþK¬ÿÔ·ß!÷þÆoþL¯éSÒíõ(9W$Ž xn³¢\i3²ºæ2x4Q9nŒ³ ¸ªošÓÑ‘ø¹¤ÿÁ®µeÌ_àVôþÂcÿ·Õ­?üïXŠ2ñüa†F¿°˜gÿ'«öN¾›Nf…^FkÛô-zßU€a± àŠUpܺ­°9¢ª¹dìÏçJçþí©ÚLÐOñR%eÿ¨+òe%¿üîêI'ø±A»ÿbÿ·¢¿¡ïxj-F#<+‰Wž8ÍxÝÅ´–Òµ½Â•`kZT©Ém©ÃŽÇâhÏW§¢ÿ#òÏþ…s{šŒÑêßüŸEïüûT¶ˆÉÆæ#·ö ý¿5û5áßÏ¥Ì"‘‰=ëÛ,ï-ïàBÛ•«ž­.W±ë`ñÊ´lž§óy?üâöÞS¿”2ÿÔ ÿòuii?ðFßí„ü`[v=?âB[ÿoÅ@>'ðÌwÑ››eăҼ†H糜£åº#Nœ–ˆòñÜM ûîëÑ‘ù*¿ðCyØPƒÿRñÿådjßðDCL‹ÍâúÜé ÿÛó_¹¾ñFqizüŽõ¯IeŽâ<2°®iRä—¼^Ž/ÛÂôÝ™üÚŸø#äªp~,Goì/þï®›Eÿ‚/A«‡ã8…ÏðÿÂ=»ÿr"¿t|QáS 7–k•êEyü7Ù\yñ1;WThÂkEcÆžcˆ£RÕ]×Èü™ÿ‡7ý±ÿ„çÿ|«™ÖàŠWzGÌ>.ùéê·Ú¾s²ý‰l!ÖÆâK¥(m­ ÓD€~å[zV§e®Za°Å‡*Ex?ÆÙûÃ?t‹‰-mc¶¾£F¡w623Œw¥‡ òÕ†¿3lÁc}®­×k/ò?|ÿrð—Žô¨õmãk˃8’?ò~»oøq¶ŸÿEšSÿrúÿò}v~ñÿŒgÉ¡ê¥Íª¾$F$¨PzŒñÚ¿a<ã­#ÇÚ¾·¤J$IgÔõ–+áªØÓ&Ï"𩤑ø”?à†ÚqÿšË/þÿ“éßðã]3¿ÆY¿ðB¿ü_½ÊA8©ÕÀ}!ø#ÿ5Òûüd›ÿ+ÿÉÔ¿ðãm'þ‹ÿø!_þM¯ÞÚ(ðOþm¤ÑcŸÿKÿÉ´¿ðãmþ‹ÇþÓÿ“k÷®Šüÿ‡èþ0Üà?ù2ÿ7Ðÿè°Üÿà?ù2¿z( Áøq¾ƒÿE‚çÿ‰ÿÉ”¿ðãÿÑ`ºÿÁ"ò]~óQ@ƒ_ðãwø¿uÿ‚Tÿäºwü8ãßôW®ÿðKÿ%×ïø;ÿ9ð×ýë¿üGÿÉT¿ð㟠÷ø»yÿ‚hÿù*¿xh ÁÿøqÏ…ÿè®^àž?þJ§Ã޼-ÿEr÷ÿñÿòM~ïQ@„_ð㯠wø·{ÿ‚ˆÿù&—þuá?ú+Wßø(‹ÿ’k÷rŠü$ÿ‡øG¿Å«ïüÅÿÉïøq߃ÿè¬ßÿà¦/þH¯Ýš(ðŸþwàîÿ/ÿðUÿ$RÿÃ<ßâÆ¡ÿ‚¨¿øý~ëÑ@…?ðãÏÑWÔ?ðWÿ¥ÿ‡x'þо£ÿ‚¸øý~êÑ@…ŸðãßÿÑVÔðYÿ¥ÿ‡øþŠ®¥ÿ‚Èøõ~éQ@…ßðãßÑUÔ¿ð[ÿ¥ÿ‡x þЦ§ÿ‚Øøí~èQ@†?ðãï÷ø§©ÿàºþ;Kÿ?øÿEOSÿÁtüv¿sh Ã?øqÿÃïú*Z§þ àÿ㔿ðãÿ‡þ(êŸø/ƒÿŽWî]ømÿ@øuÿEGUÿÀ?øå/ü9áÏý Wÿ ÿâë÷"Šü8ÿ‡ |7ÿ¢Ÿ«à ü]/ü9á·‰ú·þÛÿñuûE~Ã~ÑNÕÿðßÿЧÃ~ÑMÕÿð ßÿНÜ:(ð÷þ…ðÇþŠn±ÿ€VÿãKÿBø_ßâf±ÿ€vÿã_¸4Pâü9 áoý½gÿíÿÆ—þ‡ð³þŠ^³ÿ€–Õû}E~!Ãþwø•­à%µ/ü9áOý­kÿm«öòŠüDÿ‡"|'ÿ¢“­ÿà-·øS¿áÈŸ {üHÖÿðÛü+öæŠüGÿ‡"|$ÿ¢‘®à5·øRÿÑ~ÑG×?ð×ÿ‰¯Ûz(ñ'þ‹ðƒþŠ6»ÿ€ö¿üM;þ‹ð¿ÄmwþüZÿñ5ûiE~&ѾÿÑE׿ïůÿKÿFø9ÿE^ÿ¿6¿üE~ÙQ@‰ßðäoƒ_ôQ5ÿûókÿÄS¿áÈÿ;üC×ÿïÕ§ÿ¯Úú(ñGþð_¿Ä/ß«Oþ7KÿGø+ÿE Ä÷îÓÿ×ímø§ÿHø)ßâˆïݧÿ¥ÿ‡$|ïñÄ?÷ŧÿ¯Úº(ñ[þ‘ðGþŠˆ¿ï‹?þ5KÿIøÿCÿˆ¿ï›?þ3_µPâÇü9'àoýÞ#ÿ¾lÿøÍ/ü9'àgxò³ÿã5ûME~-ÿÃ’¾ÐûâOÊÏÿŒRÿÃ’¾ÐûâOü“ÿãûGE~(ë?ðI_…ÿ ¼)âOxKźæ­yo¥Ý$–wfòn-Ù32.lì®ÞƒÒªxwþÍû<ø—@Ó|E§øóÄ­kª[Es6_rd¿òï׿m&†+˜d·‘J¥Xd2°Áz^û;Í.•á­cáµã–¹ðF©s§.ï¼öŒÞu¬‡Ù£|e¯V?¼ÁµÖÿ)hþæ—Þr?v²þòüWüþãó‡þ—ðþ‡¿~v_üKÿKøÿC׉¿;/þG¯Ùê+Ê:ÏÆ/ørgÀú|Oÿ}Yò5/ü93öÿ¡çÄÿ÷Õ—ÿ#WìåøÉÿMýŸèxñGý÷eÿÈ´ïøroì÷߯þ(ÿ¾ìù¿fh ÆøroìõÿC¿Šïåÿ"ÒÿÓgú¼Sÿ,ù¿eè ÆŸørwìïÿC¯Š¿ïíÿ"Wƒ~Ô?ðJ?‚þxÓâdž|Uâ+íSÃVbæ.ä´0;y¨„8ŽÙbxaÍAõòíñŸû|YOMfÿ¾[úPòqû0ü,Ð~6ü|ð_Ÿ]\Øé~%½û,óZYÑLnÀ¡‘]s•T×ïü9?örÿ¡ÓÅ÷úÃÿëñ3ö ›Èý±~¿®»n¿÷Øeþµý¢Ðãwü9?öpïã?ßûþC§Ô?fÿú¼Yÿì?ù ¿cè Çør‡ìÛÿC—‹ð"Ãÿ©áʳ_ø·ÿ,?ù ¿ch Ç?ørìÕÿC‡‹¿ð&ÃÿißðåÙ£þ‡àNŸÿÈ5ûE~;Ôfúü_ÿZÿ ÒÿÔ¿f^þ.ñþiÿü_±PãÇü9Köcïâïà^ŸÿÈïør—ìÅÿCoŒ?ð/Oÿä ý…¢€?ÿáʳøÇÿ4ÿþWÒÿÔÿeïúÛkÈšú kÛéµ"Öád#,#Rì3ùÙ‘Àpr>¥øýûTëßšãÃ2iÉWÛqx=e#î¡í<ÿ<ó?ƒdò|_¡ËýËëfü¥S_«ä¡Mׯ­m¤{zÿ—ßåòY†y{:wÿ#ì¯øs×ì}ýÏàÈñš_øsßì}ÿ<µÿüþ5_©´WäÇ×–Ÿðçߨ÷þxëÿø2üj—þûûÏ {ÿýª¿R¨ Ëoøs÷ìyÿ>ú÷þ ÿûUpðÿÁ6fŸüU×|ãX5i¦éòhòÇ|c’]4«FÂRÑJ oêAé_°5óÏÅOø¤þ'ü>øŽŸ$ÜÉáûöáÔërDz¤è =9¯W+÷ý¥æNÞ«U÷Úß3“îòÔìÿ§ü‘ñ÷ü:ö:ÿŸ]wÿgÿÓ¿áÐ?±×üùëŸø3?ün¿Qh¯(ë?.ÿáÐ_±ÏüùkŸø3oþ"—þûÏŽ·ÿƒ6ÿâ+õŠü½ÿ‡B~Çóá­ÿàÍÿøŠwü:ö7ÿ ~µÿƒGÿâkõŠüÀÿ‡C~ÆßôÖ¿ðhÿüM/ü:ö6ÿ n³ÿƒI?øšý>¢€?0¿áѱ¯ý5ŸüIÿÄÓ¿áÑ?±§ýµüKþúwE~þÝßðNßÙ·àìÉâŠtýFß]ÒfÓÖ'¸¾’xÂÜÞEå`ü®qèkòëöø=ào´ï…~üF†k X‹Qi’Þf‚BÖÖSN˜uä|ÑŒúŽ+ú;ÿ‚¥CæþÂÿ¼M£7þU¬Çõ¯Àoø%̾WíÓð×Ñÿ¶þ:Eæ?\Pî/ü:3ö2ÿ >¯ÿƒI¿ÂÿýŒ¿è «àÒjý6¢€?2¿áÑß±ýuoüÏKÿýŒ?èªÿàÖzý4¢€?3?áÒ±‡ýµ_üÏþ4ïøtì_ÿ@Sÿ·ã_¦4Pæü:Kö.ÿ ©ÿƒ[ñ¥ÿ‡IþÅßô/êø5¹ÿâ«ô¾ŠüÑÿ‡J~Å¿ô/jø5¹ÿâ©áÒ¿±gý º—þ n¿øªý-¢€?5?áÒß±gý š—þ n¿øª_øt¿ìWÿBÖ£ÿƒk¯þ.¿J¨ ÍoøtÇìUÿBΣÿƒk¿þ.—þ3ûб¨àÚïÿ‹¯ÒŠ(ógþ5ûÿЯ¨àÚïÿŽRÿæÿbú¯ÿðmyÿÇ+ô–ŠüÛÿ‡MþÅô*ßÿàÚóÿŽRÿçbúï¿ðm{ÿÇ+ôŽŠüÞÿ‡NþÄßô)_àÚ÷ÿŽÒÿç¿boúoðo}ÿÇkô‚Šüàÿ‡OþÄ¿ô(^ÿàÞûÿŽÒÿè?bOúï?ðo}ÿÇ«ôzŠüâÿ‡PþÄŸô'^àÞÿÿS¿áÔ_±ý ·ø7¿ÿãÕú4HPYŽä×Áÿm]+B{Ÿ|-·•üe£{û…+o—ÃHÀ÷m«ŸïŠôò¼Ÿ©ìðñ¿~ËÕœ¸¼m:æ¨ìyg¿àœðO/‡ZCk~2ðüÚu°ÎÍúÆ d•‡ðÇÌYÛÙAõ^ØhÚ)¶ŽÞIµ@Ï;Íæ–fUœªp ±ç9ì<Å>/ñ7µyußjSjwÒõ’fÎ÷U~ê¨ìª…~š~Áºg‘ðß^ÕˆÁ»ÕLCÝa‚2?Y }¶qÁôptëT|Õ—’×§};ýÈððyÌñ•«G_VqŸðê_؇þ„›¯üêü~œ?à”ß±çþ‹Ÿüj?ü~¿E¨¯Í¦?)¼ÿõý”¾(\ø®ÿâ„çÔ ÿŠö‚ñç‡ÛåÄvµö0Ö“‘þó*“_Aשœë]Ôþd¥ÿ$ßãs—¥5×_s±ùÙÿ©ý‡¿èE¸ÿÁÆ¥ÿÉïøuWì=ÿB$ÿø8Ô¿ù&¿Dh¯,ê?;ÿáÕ°ïýSÿàãSÿäš_øugì9ÿB ßø8Ôÿù&¿Cè Ïøuoì9ÿBßø8Õ?ù&ÿ®ý†ÿèŸËÿƒSÿ’«ô2Šüôÿ‡WþÃ_ôO¥ÿÁÆ©ÿÉT¿ðëØkþ‰ìŸø9Õù*¿B¨ Ïoøuì3ÿDîOüê¿ü—Nÿ‡Y~ÃôNŸÿ:·ÿ%×è=ùóÿ³ý†?蜿þuoþK¥ÿ‡Z~ÂÿôNÿ:·ÿ%×è-ù÷ÿµý…¿è›·þuþL¥ÿ‡[~ÂßôMþuþL¯Ð:(óûþoû ÿÑ5?ø9Ö?ù2—þqû Ñ4ÿÊαÿɵúE|ÿ¹ý…?è™ÿågXÿäÚ_øu×ì(?æ™üëü›_Q@ÿîÿa_ú&CÿÇÿ&Òÿïaoú&Kÿƒ_ÿ“+ïº(à_øu÷ì-ÿDÉðo«ÿòe/ü;öÿ¢bŸø7Õ¿ù2¾ù¢€>ÿ‡`þÃôLSÿÚ·ÿ&Rÿðÿaú&)ÿƒm[ÿ“+ïj(à¯øv'ì3ÿDÆ?üê¿ü—KÿÆý†‡üÓÿðiªÿò]}é^=ñãoƒ~ èÿm׿ûF£:“ka|äqŸö®xôÉÀ=\-JÕ*Q¼ŸDgV¬a)»$|—â¯ø'ŸüãÁLjüSðþÛOÓíF^I5]S“ÙT ¼³Ê'°®+àïì=ûüaðí÷Št¿„‹eeü¶¬º®¦dt‰#`îÞ¶ÿº Ç©¯˜~+|añ—ÅýpêÞ'¹Ûm?e³ˆ‘on§²¯v?Äç“ôÑ_ØNMÿ5DÿžzÝÀüííÏõ¯¸Í¸=`rç^«½F×¢òóõ<,rëâ}œ»gó0¿áÙ_°çýðg©ÿò]/ü;/öÿ¢aþ µ?þJ¯»è¯ÏÏ¡>ÿ‡fþÿôKàÿÁާÿÉTïøvwì=ÿD¾üê_ü•_vQ@ ô?aïú%öÿø0Ô¿ù*—þ£ûù¥öÿø0Ô¿ù*¾ê¢€>ÿ‡iþÄôK­¿ð?Qÿäªwü;Sö ÿ¢]mÿúÿ$×ÜôPÃ?ðío؇þ‰u¯þjü“Kÿ×ýˆ¿è—Zÿàv¡ÿÉ5÷-ðßü;gö"ÿ¢[iÿ·ÿü“Nÿ‡m~ÄôKm?ð2ÿÿ’kî*(áïøvßìIÿD¶Ïÿ/ÿù"—þ»ûÑ-³ÿÀ»ïþH¯¸( ˆ?áÛß±/ýÛ/ü ¾ÿäŠ_øwìL?æ–YàUïÿ$WÛÔPÄðîØ›þ‰eþ^ÿñú_øwìOÿD²Çÿoøý}·E|KÿäýŠ?è–XàEçÿ¥ÿ‡rþÅþie‡þ^ñúûfŠø£þÏûÿÑ+Óÿïýßÿ¥ÿ‡t~Å_ôJôÿûÿwÿÇëíj(â¯øwWìT?æ•éß÷úïÿRÿúÿb¿ú%zwýýºÿãÕö¥ñoü;·ö,ÿ¢U¦ÿßÛ¯þ=Kÿîý‹?è•i¿÷òçÿWÚ4PÅÿðïØ´Í*Ó?ïåÏÿ¯<øŸû%ÿÁ9¾ øZ|QðVƒáÝß:êkd|gdQ‰KË!5f=…~‰×ó»ûBÿÁ=¿lÿÚWö¬ñ.©âýRðD×ó¾—¬ÝÞ¤ÖÖzSɺk{$s2È‘áY<´VpY¤ù·…ÿi¿ÿ³oŽ/dð?ìµðfÇöH!]^å&›Tºbvmnexá x!ÈÇ–ÜWÒ²¯üCâ‡Å±ø¿ãÜÓx Ã2m‘tð£ûfé8(à­¨>²†Ï jý ý—ÿ`¯€ß²å´—‡4ßíÿĺö¤«%Ð$a…ºce²F#ÈáÝú×Úôä?¾ü&ýŸü,žøMáËmǃ3Æ Ü\¸þ;‰ß2JÜðYŽ À¯^¢Š(¢Š(¢ŠÿÐýü¢Š(¢Š(®{ÅÖw‡…5« 42Osesj:³¼Lª?k¡¢€?}nÒæÃZ¿²½‰ ¸·¸–9#pU‘Õˆe ò=Evß >0üNø%âCâï…>#»ðÖ¬Ñ45« ILr#I ®¤dÔ_××ÄÿØWöZø¿â[x×ÀÖ³kŒ^ââÝžÙ§sÕä Ç»’y<×›ÿðcúÿònþ.€?’ÿxëÆ¼Q}ã_ê÷湩0{‹»§ß$„=0Ó|(øÙñ[àn½/‰¾x–ëÃZÄ^L²[+,yÎÙ#pÈàFå8<Šþª¿áØ?±‡ýÿù7?ÿGü;ö0ÿ¡ÿ&çÿâèù(ñ‡Œ|UñÄ·þ0ñ®©q­kZœ†[›»§2K+ž2IìÀ ìþütø¹ð'W»×>ø¢ïÃ7—ñnØ©I£*$Ã#m$•%I\œ“_Õ'ü;ö0ÿ¡ÿ&çÿâëàŸø(Ïìiû:þÏŸ³Ññ÷Âÿ ¦¬ÿjZ[y²M,ëåK»pÚìG8ë@‚¾%ñ/ˆ]B3sÄ‹é^?4o!Ž@Bç]”§ ±´·< Ôç…©u±ô”A}n2Wœx«Â»·^ÙŽGP+Ã>#—N™`œ–ˆœ šöKk˜/áÜ„2°®'R•úô*RÅÕî|Ù—ðß#¥z?†¼[å´¾?/@MXñW…ƒwj¿P+ÌY[c¬†»m±ºÜð_µÂU³Øú>{[}JÛ #Ž+żEáÉôÉÚh—1Ùð¿Š^ÞU³¼o“±=«Ô'†ÛS¶*ØuqÖ¹#)RzžÔ©ÒÅBñÜùîÃQžÂu¸·l`ò+ÜtvßUN@uëÊüEáÙ´¹šXFb'·¥a麌ÚuÈšqžEtÔ¤ªFñ<œ&*¦§$ö=ßYÑíõ[v‰×æìkÃuM*ãIœÅ0;3Á¯qѵˆu;ee#~9ºÎ©#¿±®ZU¥f{ü+ÞƒXjtës Õíú¿«ò¸äWŒjÚTÚ]ËG(;sÁíPX_\ió‰á=+®t•HÜñ0xÉП$yÖ4{}RÝ¢‘Fâ:׆êúDúLÆ9Ùž {>ƒ¯Aª[¨'¢®jú=¾­lR@7†¹)Ôtåf{x¼IìzŸ‰¼1ò5Ͳí ñ^A$RZÌa—(ëÒ½óEÖíµku(@|r+ÄþŽõ ͲâAéXѪâùdzXìjGÚÓ9ÿ ø¡íÙlï*xת2ÃynGól±Ik)I §­wÞñCÂÂÖñ²½ª¯CíÄÇ-ÌZýÕMŠþ(ð¼–Œ×V«”<œWks%¤«,LU”ò+éßAÎWŽx§Ãmc1¹·\ÆÇ·jt+ó{“'3˹?yKc¾ðæ¿£Å#~ôu®–âÚ+¨ŒsÀ×Î6W³X\ `b¥{g‡|A©FlH½EeˆÃ¸¾hÙfa‘ä™çž$ðÌ–›‹uÌgÒ¹[ÉôéÄð1Nq_GÜ[ÅsŠUÜ x߉|7&Ÿ+\À3_¥mG¤¹YØe²§/iLôøŠ-NŽC‰GQZÚ¦—¥ŽUçµ|ûg{5Œâh kÛ|=âu+p²0XÖ£ÊîŽÜ¿Ñöu7Q<¥a¨\铬ö¬H«ßµ=6 JÙ¢‘zŽ xv³¤O¤LÁ‡ÉØãµmFº~ë<Ì~_*2çë~ñ$´A$ J:Š­â_ C©Df‰vÊ?Zñ»K©¬g[ˆ ½¯Ã¾"‡S„E+(zŠÔ%MóDîÂciâcìªnx¥Å´ö³µ´Ãk-tñ Æ•8I´Gô®óÅ>[èÍոăӽxü°Ë4S)VtÂq©w<šôªá*]l}e{ü"XX0#šãu+$$£©¯Mð¿ŠCbÆôàŽ5?Š<.²†º´_›©ÅyQÁ&*è~•ØœkFÈñ\jaji±ôÃ,wò+ òødÚï½³\«rÀv«ÞñH;lïO=×£¼q\ÄQ°ÊýkŠò¥+3ßjž.Ÿ™óe¥ÌÖ² "b¬§¥{7†¼I£Á9EýkñG†d³ÜÚ®S9ãµq–×3ZJ&ˆá”ç+¶P…Hž³ÂÔ³ØúNhc¸Œ¤ƒ בøŸÃ2[;^Z.W¸ÖxsÄð_İNÛeô5ØÉs¡À*¸båJVè{õ(ÓÅBèù¢ÞâkYD1WSȯgð߉a¿…mælJ¼sÞ¹¯xTÁºòÅr;ŠóèešÖQ,,U×­vË–¢ÐùøÕ©„©g±íÞ"ðì¼%ÐbEä^/yk-ŒÍ Âì â½_Ã>'ŽíÚé± ïëZº÷‡­µx‹¨AÈ"°§QÓv{†+L=¥=ÏÒµ;.àK?JöÍ\µÕa_çÇ"¼?P±¸Óç0N0AÀ¢ÃP¸°¸Y¡<)®ŠÔ—2<Ü>teÉ=m„Ëw¥Eâ½ßuÁsæíÃŒóø×û|O—JÔ®ü/«Í²Ü²¬`ž7`ç_|x¾K?xU±u +BF1È—ªi¾Õn­n­dhg‚hmdt’9†VR2 ƒ@ìãû<øSU´×¼/ð¿ÂÚ>§a –ÞêÏD±·ž£Ç$p««ÄkÙëøÿý–iÏÚ3Ä_´×ÂÄ¿2hzŸŽ~1Ø¿öÇŠÊKi¹It›ìÑD{>Öß&F̹º gþ/oÄááhÿyà¿Ü$º“uŽûV^cµôhíþôƒ¡l) }3^ê­<4©»T•›kt·KÕîü¬»œ ¼›’¼Vž¯«ùl¾gÃÿf_üøÍs®Os§ g\-­ÐÂJ%y"9c'!¶o;—*qØœWÜ”Q\™¦k[QU®ï+%÷ap£J{|ùáŸø§ÿhßèLjüQ¤iúºÛí¬äǹùIü }_>|Kÿ‰Æ†^-$7sßh— ýï¶Cæ[¯ýüŒÕe^ó©Kù¢ÿy~1/EvkñÓõ>ƒ¢Š+Ë:‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¹OøÂ4vÐ|c¦E©Ù¸$€†GÁ‘Ô†FÁ#*AÅutUÒ«(INÍuDÊ*JÒWGåÇ?ÙBËÂ:Ö…cðÖþ]B÷Ä·O¶•q·ÍQfI$Yò«åÆ1À;|·wáx Åöo‹t»*î+˜˜$èT0W27ÝuÿiIõúçðÛþ.7ÄßüZ›÷š^•»BÐû«G fîå{2_•XsµH5îZÿ†ü?⛦xNƒRµ$7—by :·©Mòë§oéþFÝQ_˜RQEåß|'/~ø‡B´í¦ØÜZûÂêØ‰¡Úz‚]ÈìkÔh­°ÕåJ¤jÇtÓûˆ©MN./©Å|9ñd^:ðƒâèˆÎ©gÒÑe+‰Wþá—ð®Ö¾yøÿαã…r|©áÝU®ì—°°ÕÚ"UõåÁ#½} ]9Ó¯(ÃáÝz=WàÑž£•4Þý}VŒ(¢Šà7 (¢€ (¢€ (¢€#–(¦CÈ$CÕXdsÐÑQD6Ä¢€+âÏø(Çœ?b¿ŠM´n–6ÌIm½€ðGÒ¿›ø'mýÉýµ¾¼ó<™Ô'_™‰ûö“¯­cÔQEQEQEQEQEQEQEQEQEQEQEò'í;ð[áf»á-[âµ V°ˆ¸½·AºæO»RÇÀ•ˆU9 ’>l W×uó<ñ{¾(ùÇ÷¾øs„ï¡­ å½;Pxì\÷=¼Š­JU¾± 8¨jÚíÛæôüvG>œ=›Wooóù“>6ø]ãï‡BÍüe£O¦ÇÉ ŽFÛ†í»”¼„†Å~­~ÇgØ>i8ÁÔ./'>ø™¢¤uôv¹ èÞ&Òçч¸êPyÕ/ xSEðG‡¬ü-áØM¾›`a˜¹Uwg#sdžXõ$×»žñ‹ÇàÕ ÂÓROMš³ÿ€p`2U‡¬êEÝ[çÐè袊øsÝ>|ñÿüH>9|9ñ:üêÑê:%Ëzù‘‹‹uüdŒ×Ðuóÿí&gðöÛÆ©i¼#«iÚº×N¨ÿ†É>ÕïÈé*,‘°dp#AèkÔÆ{Øz5;^?s¿å$rÑÒ¤ãèþý?AÔQEygPQEQEQEQEQEQEQEQEQEQEQEWç—ÆïØëÄÞ'Ö/¼gàïI«ß]±’K]QÇš})À ÑQ•BŽ7WèmxÏÇ?j^ð`Ѽ4ßñQø¦tÒtÀ Ísò´¼rI¹÷t õ¯w‡³N¾ªÒrÑßkyùugc†¥R›ö«D~_éz†˜û/­Ú YÑXŒ£˜˜£ìq•`J’+õ3ö “? õØ¿»¬Hß÷Õ¼úWÓšo¿ZxMøq¨ivúž‘§[¤";ˆÕÃ04¼ý×f%‹ NA¨~ü(ðŸÂ{]ONðrÍ –§sö£ ²y‹ì µüÛp£ï>õõ\CÆtqØ9áùZ•Õ»4Ÿàü¿É˲IЭ—º±étQE~p}(QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÑýü¢Š(¢Š(¢Šþtÿjø)÷í7ðƒöƒñÏÃOÿbcø{Q’Ö×í-$¾ZcÜJ¹>øýQ_ʧü>ö¾ÿ©{ÿÏÿÇèÿ‡Ãþ×ßõ/à¹ÿøýUtWò©ÿ‡ý¯¿ê^ÿÁsÿñú?áðÿµ÷ýKßø.þ?@Õ]|Mû|~Îþ5ý¦þ†ÞžÖßSþÒµ»Ývå#òáݸeCóÇøûðKþ ³ûTxûゼ¯afëÚÅÏ•`é'“q2Æû[Î88'¿]ÿà ¿´ÿf¯€‡â7Ão²kÿiZÚ¦Âg‹Ë›vï”2sÇ4ùû6ÿÁ+h?„Ÿ| ñ3Äz–.—á½VÞöá`¸v”ÇÉ Oã_Ðå8ÿ³7üöžø±ñÿÀ_ ¼Wý‰ýâ=ZÞÎëȱhåò¥l6Æ2œ|ý2ÿ‚þÒße¿ƒ:޾ý‹ûOQ×áÓ¥ûtxü‰-nf8Pé†Ýóž™ Ðj+ùTÿ‡Ãþ×ßõ/à¹ÿøýðøÚûþ¥ïü?ÿ ꮊþU?áðÿµ÷ýKßø.þ?Gü>ö¾ÿ©{ÿÏÿÇèú«¢¿•Oø|?í}ÿR÷þ Ÿÿ×î·ìñëÇ´‡ìógñ7â7Ù¶gÔom[ìqbòà`Y¹ç“šûVŠ( Š( Š( Š( Š( šÇëN¦8È¡ùwûfóãË?_*.:û‡áö“oªü?ÓÑÀ.a\¾;ý´´ØÆ¥¤cAùWÕÿ³åô—ž³óŸ,Gé^ÍYKØDø\¶1xúПS7VÒæÓ&hfRS<±¡ë7DÊU³<Šö]oH‡T´d+óÁ¯ Ô´ë6àÃ0ÀƒS þÓFo‹ÁÏ .j{ù§j6ú”HØŽErÞ'ðÂ^ÆnmW;WèºÕÆ‘t¬[1žÕîv£o©[ b är+–­'Mݾ½Yn|é4A#E(*Ãí]W†¼K&™(‚bLgŽkµñG†ùæÔms^A42A!ŽA´§¯zí…ER6g‡V„ðÓº>‘·¹‚öé†V×ø«ÂÛ³yd¼õ W1áÿϦÊ#‘³ãšö[;Ø5+pêCס*RºØöéÖ§‹§Ë-ÏœŽèß®k¿ð·ŠÕ…­ë|§¡­ø[vëË5äH¯/"DbŽ6º×dyjÇS© ˜Jšl}46º­©C‡Gâþ!ðôÚdå£\Ä{Õÿ øš[½Ëe­z•Õµ¶±dW!ƒ®8JTån‡·QSÅSºÜð'T¸Ò§ÄÇoq^å£k6úºº7ÍÜWŒëºÆ•1ã÷F©éZ¬ú]ÂKü£¨®šôÔÕây˜,\ðóåžÇ¸ëZ=¾©nÈê7ãƒ^¨éóé×Mo2à×¼húž©n®Œ7ã‘Pkº$¥».Ð$ìk–gfz¸ü+Cž™á¶:„ÚuÂË Ç­{>â8µ8‚HBÈ;WŒ_iói³5¤ËÀ<QZ]Oc2ËT×mZ*¤n ‹–§,Ö4ˆ5[vÔnìkÃ5m&ãI¹1̧iè}«×¼;â8u8V9$±ªé6Úœ%\žÆ¸iÔtÝ™ïc0pÄCžž ¦j—]À’œŸzö­ _·Õ¡°wãúÎ>“pÈÊLg¡ª7óéó¬°¶=k®¥5Q^'‰…ÆO?g=Yñ7†"¾®mÆ$äE%´†WCÁ¯tÐ5û}ZÜe†ð0A¬øi/!7V£N+U\_,K‚XûZF†ežA‘^ë kP궪Càr+›G‘݆]˜*±ösÜóOxrM>Cql¹ŒúW)iy-”âh R:ŠúFæÚ¨Ú@ Šñøjm>V¸·\ÆÆ¶¡YKG¹Á˜åÒ¦ý¥3Ðü=â58U$lH+_SÓ-õ+vŠTž†¾{³¼šÆešÁ‘^ááïCª@¨ÍûÁÅc^“‹º;rìÂ5£ìêny³¢Üi3•pLg¡¬ËKɬeçkèMSLƒS·h¦QžÆ¼;ZѧÒge+˜óÁ®œ>#™rÈòñùt¨ËÚS=[Ãþ"ƒS·X¥lHª>(ðÊÞÆ×Vˉ¥y-¥ÜÖ3 àlöxž-M>Ïp@qÏzÊ¥7ͳ Œ…xû:‡ŒM ‘HÐÌ •­Ý\ŸH~lÄzסøŸÃß!º¶p3Çzò ¢xd0J6²œs]¨ªFÇ“^L5K­¢ôûë}NÜI0äW⟠‰C]Ù¯ÍÔ\>‡®O¤Ü©,LgŠöû û}NÜK7Q\’„©Jñ>Š…zx˜rËsçB’A.*ëßÒ½;Âþ*È[+Ö碓Sx£ÂÂUk«Aóu W•b[y~l«­uEƪ×sÄq©…©ä}/"Ew W‘…y‰ü.öN×v£1žH­án+exÜô½"H⻈£ÊÃë\Nôä{²iŠY-¤Å”e¯_ð׉ã»-®çõ5Çø£Ã²éó¨˜Û°í\”3½³‰#8#Ò»¦•Hž*õ0Õ--¦T*Ø!…yŠ|.öò5å¢ü­ÉºO x™.ã[[–ă]ıGqG”ŠàMÓ‘ôu! ]=š"•áu‘>Ió^½á%Ú-­Ñï=ë›ñ?…ä¶vº´/R+…†Ym¤óc;X•Û>Z‹CçiÕ©…©n‡½kz¶¯ oìÕâšž™q¦N`•HÇC^áÇu·¹l8âº-gG¶Õ­Ê2üÝC çISvg«ˆÂCMΞ§Ý½¬‚>‰/ =E~r|H´Ž×ö…±kuÛÞÚž=Ýs_¤Z†›q¥]˜åB@<ø7â®ô]A~ô—Ö¼ÿÛT¯c $ù½ŠÌc(ºq’ê~»ÛñÕ=AÜ_¥O_2÷gêtW¸‚Š(¤hQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE›¬é6zö}¡ê)æZê0Ko2úÇ*”aùZTSŒšwBjú3Ã?gmZòëá­¿‡5fÝ©øFæãCºÿ~Åö'àbØkÜëçŸÅûBøA?%—´ø5{~Ê.ìÏÙîÚd)#WÐÕéæñ^ÝÔŽÓ´¾ý_Üî¾G6 ûœ¯u§Ýÿ(¢ŠòΠ¢Š(¢Š(¯Ÿÿk|ÿÙkã ]Kx;Äú>b?Zú¼Oö•‡í³—ÅH:ùž×ó°˜PñÕû*MöÚƒàüÿÜñ‡Éú B þàkøjýœ&û?íðº~ž_ŠtFü¯¡5ýÊÐEPEPEPEPEPEPEPEPEPEP^5ñǯ‡´Ë/ ø8 |_â¹ žšŸóÄc3]?\$ óƒÎ8#5ê:Þµ¦xsG¼×µ©ÖÖÇO‰æšV訃$ûûÉ< ð.§âVûã‹ h5F!Òmdëc¤ƒº%Çi'ÿXçÜc"½L¾”bž&ª¼c²ï.‹Ó«òÓª9qmªqÝþ ¿ù£ðûÀúWï XxSI&DµRe™ÿÖ\Nçt³9îÎÄ“éÐpv”Q^}j²©79»·«:!¢¶AEVexí)o4_ %ñ=¢—ºð­ý†±wZλá5{ís^2ÐSÅ>Öü5 j¶W¼ö3FÈàNk·.Ä*XŠuÉ«úu0ÄÓs§(®¨ß·¸†îÞ+«vÊ®Œ:aSWüןğ<'¨ÌIš;µ—=|ËBmß>û£9¯^¬±t*³¤þËkîeÒ©Ï%Ô(¢Šç4 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ñŽ~+ÔôÃMÿЧM'Mଷ<<¼rQîrÝ5ìõó—…¿âåüjÕüo'ï4O‰4m3º½ü€é×ÝØèFkÓÊéÇÖš÷`¯êú/›µü®râ¤ì¡å§ù¿»ñ=›Á~Ó| áM+Â:BâÓJ·HãÊ™ÏûNÙcîMtôQ^}J’œœäîÙѤ’AETQEQE|óãø£þ=x?Å£ä³ñ]¤úÛtQ<é6„ú³è=«èjñ?ÚB¼ÕþjZŽ’?âkáÇ‹Y²n¥fÓÜMÀõ(GÖ½KÃz퟉ü=¦x“O9¶Õ-¡º¿É2ûŒà׫‹ýæ•^ªñ-Wàíò9(ûµ%úþòüMª(¢¼£¬(¢Š(¢Š(¢Šøßþ ŸûüWOM'wýñ4mý+ùŽÿ‚Íä~Ù ×XUÿ¾â‘­P¿·|>ìyñi=4–ÿ¾0ßÒ¿–oØZo#öÀøHþ¾ ´_ûí¶ÿZþÓh¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢°cW©GßÂTò´þNéþ<§,ô­Ý5ú¯ÔúŠ(¯,ê (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùËÂÿñrþ5êþ5ÞhžY4}3º¾¡(úe÷EÄ@ô#&»¯Œž6ºð/.ïô…óu½EãÓô¸G-%õÑÙõÚräw kgá§‚m~øIð³y¯eóåï5ć|Òy;Ü“ÏlÕëaÿs‡•n²÷W§Úu—Íö9*{õ:-_éþ$wTQEy'XQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÒýü¢Š(¢Š(¯Í_‹?ðKOÙçãÄmâwŠ5n-WÄW-up¶÷1¤AߨE1¦¿J¨ ÉøsOì·ÿAOÿà\_üføsOì·ÿAOÿà\_üf¿[¨ ÉøsOì·ÿAOÿà\_üføsOì·ÿAOÿà\_üf¿[¨ ËÏÿÁ&?fï‡Þ4мu¢êZëê½·¾·Yn¢h̶î$@ÀD ó_dþÑŸ³¿‚?i¿‡¿ð­¼=ݾ—ö¨®÷YH±ËæC£,¬1Ïx3öiøoÂÿMuq¤Ás=Òµã¬’ï¸ °,ª£qÅ{•QEQEQEQEQES[¥:‘ºSAsó#öÕÕ¥þßHQò˜clýkÕ~kºw… ‘d- Åx¯í©†ñ¤ ‚!üëÙ¾i“Ûx& pJRkéSä~YVSþМ }‘¢ë0j¶áÐüØäTZnU‡Î:ñ=/U¸ÒîXIØO"½ÇEÖíõH•†ür+Ç«NTß4O¶Àãaˆ‡³žç…ê:uÆ™9·œe{UýZ›I™YX”'‘^îhvú­¹þµáúŽq¦Na¸\.x5Ó Æ¢³‡»´´Ölöœ0aÁ¯×t ´™È ˜Éë[^ñ3ظ·¹$Äzf½BêÚÛX³#å`ãƒ\‰ºR³Øöeìñtïˆð½+UŸI¸W‰‰Nâ½ËHÖ-õXFß8"¼[\Ðn4©[#1“Ö«iZ¼ú\ë$Díã#5¥j*Kš'‚ÅO >Iì{F½ ÛêÖì âAÐ׈ßé÷|í À¯J÷mY·Õ-ÕѾ|r* wB·ÕmØ`ìkžYEÙž¦; DyéîxU­ÜÖs‰á;qÚ½§Ã¾!‡Sb‘±(à׎_XO§ÌÖ÷Œ Ckw=Œ¢h =+º¥Î:&:å–ÇÐzž™§na•A$pkõçJ¸duÌgî×§øsÄñj¬7lƒŠèuM2ßS·1L2{â§QÓvg¿‰ÃÓÅSç†ç€X_O§L³ÀÛqÔW¶è:í¶¯lïÞ†äZ΋>•;+©)ØÕ é´ùÄ‚ W\骪èðp¸º˜yòOcÓ9Í^ƒƒº=<»1hòT<Ïľ}:F¹·Œõ®gNÔ&Ónæ‚;WÑwÑ]DÑJ¡ëÆaÎy„Ík"Ë Áñ^Áá¯ŨÂ-®Èó÷¬ëPp|ÑØéÂc!‰‡³«¹ãNŽ®RQµ‡•nh:åÆp ±h˜ò+·ñO…Ö`o-ž¤ òÇW‰¶¸Áµºš©Ë«‡©B¦›Eéú…¶§n$ˆ‚äW⟠‰ÔÝÚ 0ä\6ƒ®Ë¤ÜŒѱ潾ÂþÛQ·FCŠãœ]7t{ô+ÓÅC’[Ÿ;’ v¿Êêzצx[ÅYÛexyš›Å>IÔÝÚ 0ä^X|ØeÚß#¡®¸¥Z>g8ÔÁÔ¿Céà†ö€Ê¼kÄ~—M‘§€f"k¡ð¯ŠzYÞ¶bkÑn †ú’ÊÕÇ J”¬Ïv¤)ã!u¹ódRË‚XŽ×C^Áá¯GuÛ\Þq^$ðóés4Ñcj壞Xe„àŽx®ÙF3GJ½L-^W±ô¬ÑGqÆåjñßxpÙJ×Vãä'¥u~ñDW±‹[–ă׽v—6ðÞDc•CÃ:r>޵ x¨]n|Ù¯ ‰#;XW®xgÄñÝ*Ú\·Î8®Gľ–ÂSqÌgÒ¹¥’ÞA,d©^•ÚÔjDùÊu*aªr½¡õ*×R„¤ª2G¿/>9iͦüRÑ-Ÿ Ô-Hÿ¿©_£þñ_@ ±(â¿7¿hIfÿ…ó¥DÌJ}¶ÐÛýbSÁ)FM3\þtêS„£½ÏÕkõcéSÕ{õkô«äOsëè|(¢Š“P¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(篠øwþб|§Â´_jM?PŵÏþ„‡ð¯¡AdW-ã [xÓÁÚׄîð#Õ­&·Ü…¤RþªØaî+‹øâ{Ÿü+ÐouFÎ#ax­÷ÖæÉŒn÷b›¿õj~ó uƒ·ÊZ¯ÅKï9#îÖkù•þkGú»EW”u…Q@Q@QÕ4Í;[Ó.ômbÚ;Û ød·¸‚e°Ê¥O ¬¤‚½\7Äû«ë†¾,½Ó'{kË}"þHeмr¥»”uaÈe {ó'öQý˜t+û]WGøMá[KÛ)Rx'F³Å,lÊܬ¬‚2+ßëøÉøuû^~ÔzÄ ÚꟼSqi6­b“DÚÅß—$m:V_3Xdx"¿³j(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¼›âÿo| A§øn!wâ¯Ê,t‹cÎë‡Êãþy¿;“Ç@qœÖøl<ªÔTá»þ¾åÔέE¹Hà|bÍñ›â*|2´;ü'ái"»ñ¹utë{ ÷òŽz8`3ô²ªª…Q€8t¸†~²øqá+oÛÊn®‰k‹Û§æK«ÉŽé¦ry%›¦rB€3ÅwõÕ˜b#&©Rø#¢óîþ‚²èe‡¦ÒsŸÄÿ«|‚Š(¯8é (¢€ (¢€>}øÿ{ßøþS¡x†æXSû–º‚­Ì#ÿjú ¾|Ó¿âžý¥µ{AòÁâíÞó=šçN˜ÀWê"¥}^¦o­USù”_ÎÚþ790zC—³kñÓð (¢¼³¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šò¿Œ¾6»ð7n¯tuóuÍIãÓ´¸‡Þ’úììˆzíåÈôS[ |iðïÁO„m[ÍkG/y®ïšBO?;–<öâ¼®ßþ.oÇinÏï4†ˆaºK¬Ý/ï¡ûÖà|–?´¦½PÒ¾t$ûÛ¹×êà=úUhys/Xÿö®G&#Ýœ'òûÿàØúŠ(¯(ë (¢€ (¢€ (¢€"ž.¡{{˜ÖX¤dpXÄ R·Ñ´‹Fic,¼‚‘*‘ôÀ¯ ý®tý–>/Mm#E,>ÖäGBU•£²•rÇZþJ?dßk²þÕÅÞ¥s2Éâý <ÎÀ‡¾…pr}èû^¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯™µoø½¿džãý p²ê¬wúºó·£Go÷¤ aHèk³øÉã}[@Ól|!àÌIâÿHlôåÿž ŒÍvþ‰|ÙçœpFk´øà'áß„¬<'£åâ³OÞJÿë'™Îé&s݉'Ó à ö0ßìô¾°þ)]GÉlåú/;¾‡_ÞOÙô[þ‹õðNÊŠ(¯ì (¢€ (¢€ ùóBÿŠ{ö’ñ>™Ò/è–Z˜ô2ØÈÖŽ¾ÆR}«è:ùóâŸüH¾*ü/ñÝŠKÛ½sýïí „¤‘äW©•{Ò/æ‹_w¼¿£—¢Œû5øéù3è:(¢¼³¨(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¼ûâ—Ž"øwà]SÅ;<ë¨#ÚCŒ™®æ; ŒÉÜägã'µkBŒªN4àµnÈŠ“Q‹“Ùgü\ߎÒ\ÿ¬Ð>!‰;¤ºÍÚüçÐýž.=UÏômyŸÂËàXh·ïçê³ï¼Ô¦'-5õÉó'b{áŽÐ}W¦WfgZ2©ÉMû±V_-ßÍÝüÌp°j<ÒÝêÿËä´ (¢¼ã¤(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦·Ju5úSB{‘¶­äÿð·-¬Äe€þ$šû§à=”7ÿ­â•s¹ùø7öÖÀøËj}- ÿÙ«ïOÙßPµÚD¬UQ^ífý‚±ùæÇûF¢—RÞ¹ M¤ÌÌ ˜‰¬ý+T›KœM½Å{íý„„ £9WˆëúúLìÀf#\Ô+s^2=<Ç.•{J{âkVú­ºº‘¿¸¨õÝ ZÝ”Œ8èkÄ´½RãL¸Bß'q^ã¢ëVú¬!‘†ñÔW=Z.œ¹‘êà±ÐÄCÙÏsÃ56ëM ”„Õ­Z¸ÒfWWÌyäW²kš¾­nêß+ãƒ^%¨éóé× £àt÷®ªUEfx¸Ì$ðóæ†Ç¼iZ¤¥ºËÉ#¥s^(ðÄw‘›«eÛ äâ¼ÓHÖ.t«€ñ·î»Š÷ +T·Õ-Ä‘r9É:nœ®_ ˆ†&Ÿ³žçÏSÂð9ŽPA¹¢ë×:LŠ7Œö¯Cñ7…ÖõæÑq +È&ás ¬§œ×ljFkSÃÄaêaç¦ÇÐúv¥mªÚ‡B#‘\7Š|,_7VCž¤ â´]nãKJœÇéí^ݧê6º¥°xÎr9É(ºr¼Or†"ž*³ÜùÛkÇ!‰ÆWWáÏ\iÓn1Þº_ø_vëËEç©Åyk+FÅ%eí]°q©3©N¦¥Ñô%Ԛݗc¸q^)«é]ËFÊJǧáÏM¦J°\6ø‰ü«Ôï-,¼Ac•ÁÜ8#µrÂN“³Øõf¡‹‡4~#Åt­V}*åd‰¾Ry¯qÑõ‹}RÝ]oî+ÄuãK¸1ºü£¡õ¨ôZëK¸D~\ò+JÔT×4NL2XyrÏcÚuÝ ßV€å@t5â:†>›pbÎ2kÝô}bßU·!ù±È¬ïèê–ìè1"ô¬(Öqvg§ÁF¼}¥3Ä žKyD’y¯aðω¢¿mîX Ö¼†æÒk9L ¤ GÒÛÊ%¶²Wejqš¹à`±SÃÔå{Cêze¾§nc•A=x~µ¢Ï¥NÈT•n‡µz_†|N—Ь'Ž{×Gªivú¥³E ‘Á®*u%MÙŸEŠÃÃhn|ûis5œ«$-µ”ç5íñ¾«†b€`ç½yV³¢\é3”a•=µfZÝËm2Ïl+]•))ÆësÂÂbg†¨ã=QñG…–åZîÔaúñ^PÉ42“‡Oν³Ã¾"‡T€Aq àç½aø·Ã‚@n¬ÓÎ+Uœ_,ÎìnT¶¤Qð¿ŠL$YÞŽÄפ^Ú[jÖmÃùÔ‡‰ð~WZô? ø¡¢t´»?)ã&ŠôÆ…—ãÿåÕ]Žc[ЧÒ'cƒå·OJ£§j3éÓ,Ðê=«Þï¬m5{R¬nñ-oB¸Òç+ÕCUFº’呎a€•){Z{ák–ú´*AÇQÞ¶®m¢¹ˆÅ*îR+çm7R¸Óg[‹s‚§æµî:&¹o«@¬­‰;­sW âî[/ÌcV<“Üóxf[ MŰ&3ÉÅrÖWrØÌ'·bšú>âÚ+¸ŒR®A¯ñ?†¤°•îm—1“œº0ø…%ÊÏ?3ËÝ9{Jg øwÄpê‘ä H:Ö¾§¥Ûêp¦QÏJùòÎî{Dð><Šö¿øŠR²$¬kÐqw‰Ý€ÇÆ´}MÏ&ÖtK*å‘ó垆² š[Y|è[k-} ªivúœ9@,G¼KZЮ´™ŸpÝ<߈ºå‘åf9s¥/iOcÒ|9âHµEµÙ`ãžõ›â ù¯,מ¤ óy¤µu–Ã^Ãá¿C©@-nN$=êjSp|ÑØêÂâ£^>Φç2²9ŽA‚;VO¥\rÙ‡¸®ßÅ>IÞÙŒ¤ òÇFV!†Ý½A­á8ÔV<šôg†©sè­?P¶Ôí„‘CEq>*ð²N¦îÌa‡$ àt}jëK˜2·î³Ò½¿LÕ-õ[`ÈG#‘\S„©»£è0ø˜baÉ=ÏŸK ›NUÓ½z_…¼U¶w­Ï@I§x³Ã/ 7vcȼ¼ ð ºú×Zå«w“¹¶†î kżMáçÓeó¢ÄOÕÚøcÄÑ^F-î›3]}í¤7Ð4R.à¸a)S•¢¯Fš|Ësçk™­n’h[ §&¾ø½¨¾£ñ§I–S–Öƒÿ"%~ƒkž—Jºæ"z×çÄçÏÆ!—öë\ûh•ìàÚ“oÈøLÆœéÊ—sö:ßýX=Wƒî¥X¯œ–ìý:‡À‚Š(¤jQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEóÏßø¤~1øûÀ ò[jæX¯¨ºýÍáü&Aù×ÐÕóÏÅÿø¥|{ðûâb|°Ûß¶‹~GÛjk¶6sýØæU?S^®Uï¹Ðþtíêµ_}­ó91z(Ôìÿ£ÿ?‘ô5Q^QÖQEQEÇüC‡íñ4|Í2õ8WaX~'‡íÕ ëæZN¿œl(øKðßgñχgéåê6ùL¦¿½ þü=7Ùõý2~ž]Ì-ù85ýòPEPEPEPEPEPEPEPEPEP=GP²Òl.uMJu¶´³æšW8Tæb}5óÿÂm>ûâ‰.þ;x’‰/ckOZÈ0m´Üó9SÒ[“óÔ&%MWø‰4ßüsÁ-Ûû Kò¯|M:Lï·° ?Šb7?BpzŠúBa·…-íÑbŠ% ˆ UQ€A^Ŀ٨[íÍ}Ñÿ9é?â8×ïj_ìÇñð??BJ(¢¼s°(¢Š(¢Š(¢Šùóâ÷üHþ!ü.ñ¨á"Õ¦Ñå>©ªÂQ7{ H÷ú×Ðuá´†qyð[Ô,Gún†`Õ oÉ1?÷µ{.•¨Ûëež­hwA{ sÆ}RE ¿¡¯SïaiO·4eÿ¥3––•gö§è_¢Š+Ë:‚Š( Š( Š( Š(  x£ÃÞ ðæ¥âïjéZ6‘—7wS¶Øá†1¹™°ì9=5ùµáŸÛ«ãwí©j²~È¿ßÄþÒ&hÄö¢šUµÄ«ü0Â˹²9ÀrÊ|Å ÿ‚±|BñOXø‡Cñ»O§^ÿk¼?N²À-ŸË(îáØ2àä¨ûÓà—Ęþ1| ðoÅ8íEñV•i¨=²¿š ’xƒI|.àŽJîÀÎ3Ò¿o}!¾;þÕ<7 ùÖ¿~ê›/UK×QpOûƘð=5õoì}ûCøWáü7Ã_|g6ëo Yê6¦ÀIqq üñZÛGŸã—1¨ì3“À&€:Ûƒþ 5¡þÈþ+Ñ|¡xv?øŠúݯ/ kÃj–VìvÁ¹–)I’RíÀ€džZúgEøÏã¨g˜~-|H𽿆ðý³uÿ¥-¥ÀSí4,[Ów½~±xö­µø+û|>ø©j£Pñgˆ¼7£Xhv@yu¬ÜÙ¢rë†wÂí3-râ+Ê­ITžíÜÖ5¨­‘æÿµ/üúß“àWÃ_Çãí^³ÛÝ¿ÛͨMFé€KHÂÃ.÷“qÈÃ6ÌeM~˜hú‡®|§­hÖV^0{&‘ôØï^[5¼ØJBnü•m…°ÄG­Ÿæ£à§ìù¨iÿðQ?† ¼]3j~+Жx¶êFóXë-ë ùÏ–^ÚA!¤ ÿÄkúcñÏŠ¬| à¯xßTâÏÃÚ}Þ£>N?uiLüýÖ%Ÿ›³Oíóñ‹ö‡øí«|‡áFŸ¥GáI§]{PþÛyc´ŽÞ³¿” ;IÄj †œ€ ü)ûyþПþ'xëÁ?¾CâýÁ:¥ÖŸ&¯6º–ò,ç$€B( ŠõŠ(¢€ ð/Ú.Îæ×Á6ž<Ó£/}àF×W@¿yá…ö\&ºbv-ô¯}¬ícK³×4›ÝQO2×P‚Ky—ûÑÊ¥~ šëÀb}hT{'¯šê¾hÇOž%‹;ËmBÎ û9¶÷1¬±¸èÈãr‘õ¬×…~κ¥äÿ âðάûõ?Ý\hw_ïY>ØÈö1Ç­{­,nØÖ.Ïú1ЫÏ>áEW)¨QEQEàÿµÛþðœ‘]kÎ>åÝà;­ìsЪ‘¾QÏ@§ {pUV~û´V­ö_æö^lÃUÁi»Ñ¿tMSÄÚÿÆÿÀÐj~!ŒE¥ÚÉÖÃISº$öy¿ÖH}ÇNE}HPFàKQÅ:ÕÚ²Ù.Él¾EP¤¡_êáEW)¨QEQEW‚þÒv“·Â‹ízÉw^xjæÏWƒÙ¬çGcÿ~÷W½VŠtH¼Má_ÓãËÕ-'µlôÄÑ”?λ2üB¥^ɧø˜â)óÓ”{£VÎî BÎ ûVß Ìk"7ª¸Üâ Y¯ýŸ5¹uïƒ^¹¹È¸µ´Rƒ÷„–Lm›w¿îó^ËQŒÃºU§Iý–×ÜÊ£Sž }ÐQEÌhQEQEQEQEQ\Å_ˆºÂ/†Þ%øâR³|3aq2©ä¡e‰3Æù¿í@oÆÏÚGà—ìí¥A«|`ñU¶‚.Ãkv5ÝÆÞ ŠÞy\@,hÈÉó.ÿCýnµ‹}Vñ¥ Ëw´Àú†{H®pŒ¥bsµF R+ä/ø'_Â}Oö¡ø…âÛ«öƒ‰uÍNãQ{OÚÜ&ûkV·ÚÆhQ²6[äCoýÖYæL0ý"ý¤eþÒÚŸ€uo\=•Ç€õˆµ8ž(‘Úæeim›•ŽR‹¸ŽF:PÔÕò_ůۃökø3âƒàOx¤Þø¡[kéZM¬ú•ÜmŒí‘m‘ÄmŽv»+cœb¸ø(¿í'{û6~ÎzŽ­á{ß±ø¿Äò®“£ºàÉ ’‚ÓÜ(?óÆm­ÐHÉžµÿëý–´?ôëVBãâŽmcÕ5kû…Ýu]4vÛ,¢5aæ üòîfÎ(½| ý®>þÑ7—Ú?Ã-Öµ¦)k­2î l¯¢PpÍä\"3ª’2nU$ ‘^Åãÿè? <ªxïÅiþÉÑ¢ó®M¥´×“,{‚–X`G‘‚ç-µNÕŽ$~(~ÖÒÁðßþ ½ð/Ä^ ì:Ÿ‰ãÑ¢Õ>Ïò…½¿¸Ó¥y1÷·[üž¡~kõßöñÌ_ þüBñä¬hº¡<@ôiÄ !Oø…Wñ 4øûnþÎ?´wŒ.< ð—ÄSjšÍ­”šƒÃ-Õ¨ûy$·šÕ–á#ŽR»'Tb6J¤0<Œäê>+üWðÁ/j?~%ꋤhW—çNQ¤mÒ¸#Œ3»30Tß &¿'àãþk¯ *<)â8£‘¸ÜÛ©ÿE×ÍðTŒú·Ç[Å^ð•Û'ß‚ÒÛè\!ýÖ£âké|„·SЛh¼ãÓ.oï!¯$ë?k¾þÕ¿ÿisª„æ­³í3˧]ÚÛ«?DOFÏŒ·`çæª|yý®¾þÍ7únŸñƒS¼Ò­–ÚXôÛË›wÃ)çCÆ$ÉMÛ€Á#æßðN?‡Qü7ýþX´"+½rÍõ»†Æ ©È×3{ˆ%ú(¯ˆౚ…ÏŒo¾ üÑNýOÅ:̳…•v1Y[ñß{\I÷M~†k¶ßìñáÿƒzgǽs[¼°ðf³|4ë+©ôËØä¸‘ä_.ˆJñ”ˆ)Cƒóf¼†_ø*‡ì[\ÍâÛèá¸Ýå»hÚˆWÛ÷¶“23Ž•èÿco |wá'…µ-Ml< ðÎín.4O³y«©ÅopÛ@d ‰•þGÜ®@ÛÖ¿=¿à¢6–¿ÿmÙÏöeÓ¡A¥Ø½½Õͼ*8í¯nÕ%]‹Àòí¬˜ªãO`híCÿ>ýŽÓP¶Ò§ñ>¡ÝÛ¢Eš&¤ŒÌçjà2rx¯Ð:ùö›ý•¬ÿh¿ü%ñEÞ¡Œ_ õäÕ®#’+Þ[‡ŠF¶V 6hWq9ãµ}}@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¤a‘KHÇš? mÿ†?çÎæÕôwÁ«‹_ A$NAP¼WÎ?¶©ÏÆ[eìm þm_V|%ðü«à8/!ämR*úˆ8ý]\ü¡ÆoQÀúoÃ>$‹P„ApØ”WKa£nÑL ‚8¯-®%¶•fˆ•e<Šö¯ ø‚=JŠVÄ«ë^-j./š'ÛåùŠ©J¡æ:öƒ6•30\Ä}=*†—©Í¥Ü,°±ÛžE{ýöŸ£E2ƒž•âþ…q¤NX.èš·¥YMrÈàÌ2ÙQ—µ¤{­[ê–ᕾ|r*=wB·Õ­Ê‘‰ Šñ-/TŸK¸Y¡c·<Šöí]µÕ!R¬<Ìr+š­'ÍO…xrTÜðýOO¸° vÓÐÕ#X¹Ò&V‰²„ò+ÙõÍÛU€‚ H9¼CPÓ®4ˆ†å{ðkªœãQYž>3<<½¤6=ëKÕmµ;u‘æî+˜ñO†c½Cul»dqÞ¼×HÖgÒî¨ÙLò+Ûô­VÛT·Y# œr+–pp•ÖÇ­…ÅCO’¦çÏ“C$4S ¬ã[&·6“8`~SÔW¤xŸÂézwh¸˜sŠñéàky r):×]9©­O†ž¥ÖÇÑv£iªÛC»päW â y¬×vK–ëŠâ4Mn}*ä0$ÆO"½¿NÔíµ(ÄÁ‰ŠåœeJW‰íQÄSÅÇ’{Ÿñ$ÚtË Í˜›ŒÒ»øV;”{«A‰98ä’)‰Ì2®MvóFª‹Ô4ë}R£9èkÅ5Ý}*c…ÌdðEwÞñB] ´º8qÀ'½vW¶PßÀÑH¡· yð©*r±ô•èSÅSæŽçÎÖ·SZL³BØ*kÙ4AªÀ-îόכkÞŸJ™™A1“ÅaZÝËg0ž ×l鯢æêxx|EL<ù%±è¾(ð°ÝÚ/N+ÍHhةʰ¯kðÿˆ-õXDߌs\ÿŠ<-÷®ì׎¤ ÂwðÌìÆàã8ûje_ x¥à+itr½zE핦¯hQ°C¥|ìÁ£r¼«­z†9£skÜ-ƒ!½kçÝ+UŸL¸Ycc³¸¯qÑu›}VÝdB7c‘\•è¸;Äöp„kGÙÏsÈ|IáùtÛƒ,kû¦ô¬ ;ɬ&Û¶0zWÑW¶PßBÐJ¹ Þ¼OÄ>ŸK˜È‹˜«ªŽ#™ZG•˜e®”¹éìzo‡¼A¥¬ûÊÝ¿ÓàÔ h¦\ƒ_yf¾ä ò×G…Š?v®…%Q]L4ðóº>…Ó5Km^Ø2à’9Ãx§ÂäfîÍ}È®3FÖgÒn©Ìly¯r°¾¶Ô탡äWâ黣ÜÃÖ†.³ÜùÜ<±¾ï¸èkÕ|/â‘([;³Ï@j§ŠîÖ È J¡øññ…Ïö±Ó¿åš^Ú‘øºý\ðψP`‘¿x9ï_•?ÿÃOÚ±ä}²ÓÿBJ¬½8¹/"xŽ´'Ssö&ßî¥X¨ û€{TõãKv}½h ¢Š)Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@yÇÅï7Žþx‡Âñ ÜÝÚ»[c¨¹‡÷°{~ñV½ŠÛZTêF¤wM?¸ŠR‹‹ÙœÂϯ>øŹ̺…¤m6;NƒdÃð‘XW_<üÿŠ_Åž>ø_'Ë—©jX¯Aö-Q|Ј?»¡ÔûšúºsJ1§^J Õz=Wà̰³r¦œ·Ùú­QEÀtQ@2HÒTh¤PÈà‚BQO¢€>d·ý‹¿d»VW‡á†)Ó-Ø‚?ÞC_M×ñ_ªþÚ_µÂßÜÛ7ÅߨŠGO—R™>ëü,+ûAÒ§7Z]ËÆhcr}w(4~Š( Š( Š( Š( Š( Š( Š( Š( ¼ÇâÇÄøá´é°}»^Õ%[&ÌrדpƒÜ_¼çŒäŠô‹››{;ynîäX`Yäw!UFY‰<$×Î -î>*xÒÚÄlºM –ËÃ6ò6Ûçl×¥OGœŒ/„çƒ^–_B:Öª½Èþ/¤~}|“9±†ïð]_õÔô…_×áç…SNºŸíÚÍüyª^ZêöošW'ЕپkÒ¨¢¸ñåVn¤Þ¬Ú5¨ÇdQEbXQEQEQE•¯i6úþ‡¨èWê5i­¤ïòL…èkÉÿgMZãUø7áÈïx»Òâ}6e=Uìdk|ø ø×¶×ÏŸ?âIã/‰Þn޶58‡a­ ÌûVüs^¦ßÂÕ‡g~qúR9jiV½×ëú3è:(¢¼³¨(¢Š(¢Š(¢Š(¢Šü!ñ¥›kð[O ÚøƒÒÖÊ9lƒò‡Cžhöç¦.7î=kö³âÄü*ðf«ñ â©áí/:îîP̱¦BŒ*wfbQ³1A$ ùGö£ý‰ô_ÚÆøµá?Ýü<ø“á‹a­ÙÀ· Ç™&š=û›i•ƒ©xÅ/Øöý¢¬ì´ÚöŽ—WðõŒ‚Q§iž·°ŽYI<©Ñ]ÇU2#…$í' uüFñ]§¿f¯øßáõÃ^[k¾¾Ô4©¼¹ iRæÁå¶p’ªH›ƒ)”0Ï üáÿ‚(iÖþÎ^2Õbp×—^*š@ê±ÃchÑçêd|úëõß@Ð4ß øsMð¶›]?K´†ÊÈ`ŒFŠGOº¯ÍÝ7öø‹ðcÆ~$ñ/ì…ñŽo†ºGФÝè—šE¾¯g†$4fn!Œ°_”¹3þ ßñÊ? |°ýž¼,æ÷Æ.­âû½ÀÓá˜1!W'3αƒ8óû¦¾Ýý‘> ?ì÷û:x+á]â¨ÔôË?;Q(wvíqr¼©$…÷Uã_?`ß |:ø™?ÇŸ‹ž+¾ø±ñBvÜš¾§Á£`®mmc!NÕ˰A¥}mñOÃ^5ñ‡€u ü=ñBø3]Ôbò`ÕšÈjÔ1c$XAr™ÅþV!°qŠü°ýŒü:Ÿ|Yû^|^˜ «oj—¾Óä<ÿ¢[Á2ÏûPËmŸ÷E|#ÿîøyã_ÚžO ü ñt|øIªÜxR…Ù¨êW{E¥¤ ðB”‘ŠãýY”L~ÐþŲ?‰¿cÿ k~Ÿâ~1Ð5;³ØãN–ÞîDHås/Ú® Šé¤ ¤8¯¢´Ï…>ð‚µ¿ ü°Ó<uªçŠ{]:9-ã¾™ ™m‘¡0Ú¹×pgø¨ž1Ó|gÿ[øƒñOQÅÆ‹ðgHº¡?)žÎÚ=5a¦æ½ºr£±ç±¯Ø_‡oü*o„šŒ5øíð£Tø[àß/S^V¶Ôo?³†£$Ö£$Öè¦{/ÌÈ ù'nT»#Ô«'K©ZÎ^óôû?«ûŽX.zŽ}‹×¯ù}çägÀO‡—þ2ÿ‚]üyø®Gæêßî5ïÉ'Rë¦2Ⱦ'¶˜sT¿àš|gûIê~ø£ñFøà=“i´l˜®µy$3IvAà´ÑŒôܰàæ7ÏêŸì¿û6jŸ¾€þ3ñM¿ü?º‚Ù_J[[;Öy'·™<û0w•ÎX•¶@®ÿÅ_'´ø9{ð—à&£eð°KÁisg¦%Ì6I;–¡µY`_1÷1»åc»ñ^YÔ~OÿÁ9HøÍûm~Ðÿ´cþþÒ¦°±ò<›ûÆ0m=ÊÛÙ*’;7½}«ÿ5øƒÿ ÿö2ñÛÃ/—wâ$¶Ñ`ç¾Ý2¬ëÿ€â_ʲÿb_ØsÄÿ±¶¡â`ø—‹<=â$GšÀè‚ÆE»‡ˆæ[¶Npº²lÁÈ9y¿ûkþÆ5ý±EÐâ‚øCÂz3 ‘¥®Š/škð$O´I?Û 8¾ÄM˜\±É-ÀSÿîðü+¯Øßᦗ${.5M<ë`±Õ$k´'ÜE"/Ð ùçþ ñþ_ÙF?Ã.Ùüg­YY²‚míw^;}Äû¿A>øÄÿ ~xwáç‹|I‹/|=kŠjØ8Ion¢8 Yf’5Ufßó»8¯‰?lŸØ Ç_¶Œì5kâêxÚ°Ò´ˆôsöf™#É%ÇÛ¢3< ”P«…«0×ÿ³'ÃÿøUŸ³×ï<~UÆ¡ØÇr¸ÇúSD²\{ÌÎ÷:弦x¯Fð®¦xã[‹Äší¼{nµ,ÆŸËî8ql$˜GòàŒ‚F3ÔÐEPEPÏ:üQß´?ˆ43òXøãN‡Uƒ²‹ËäN‹þÓFVFúWÐÕó×Çà|=„þ*Â6·ƒµhZåÇQ§ßâÚè~L‡ð¯¡A¯W0÷éÒ¯ÝYúÇOý'”äÃû²>Îÿ'ÿâÑEåaEPEPsC Ä/op‹,R©WF••† ðAE`[x?ÂVN²Yè–0:TÇmGBZÉøž%? #øVK½P™WV±,êV Ó9Ëþç(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢©jZާÜêÚœëmggÍ4®p©cs1>€ ÓI·d žcñƒÇ·ÞЭôÏ Æ.¼Wâ9~ä[žs;šg£…~w'Ž€ã9­ß†ž±øqá+_ZÈn®2ÓÞ]?2]]ÊwM3“É,Ý2Iž+Ì~é×Þ>ñßÇoÀÑ øÍ¯‡ídÚé€ÿ®#´·'æ'¨\JœWÑ•ëcš£…ŽëY‹·¤võ¿‘ÇAsËÚ¿—§Ÿå`¢Š+È;Š( Š( Š( Š( Ÿ>Ä›]øàVàéü·q'÷-µ8Öâ%Ùߊú¾|·ÿŠ{ö˜»ˆ|–þ/ðôsgû÷ZlÅ1øC&kè:õ3zª«üÑ‹ùÚÏñLäÁéͯÇOÂÁEW–u…Q@Q@Q@Q@~_ÿÁ^|M} ~dž›hÅSÄZÖ™a>;Ĭ÷x>Û­×ùWê|Ãûa~ÎñþÔ?µï…^&©Ü4Wzu̹ò¢½¶mÑù›A;nG ‰Výˆ|#gàÙá.‰d›_Yj?鶤Ÿm—ÿ"LÕõ=~K|(ø‰ÿø)ðwGø/sû<Çâ½oÂÖ)¦iºÌ^!°ŠÂ[keò­žXKo>Z*‚¾dlàdì&½Söø ñËá¤ßþ)~Ò+ø÷â^©oup±ÜGrRÞÕË]Ñ3FŠ­3¢¢± Š p|OÿE¶—â/íû;ü¿O;J¿–Ì4m÷û[TKi³Ûî@¹ö¯ÞÅUE€*¨À€¯Í¿ÛçöHø…ñ·Rð'Ư·6ñ|Føiv—Vv×n#Šò8¦K˜‘]¾E’9ÞU3aÅrþ>øÿøÉá)¾x7൯­KT‹ìš‰/¼Akw ²H»ekHíÁ•Y²vÈ¢Rƒ Ý‡)xFcûYÿÁ\o|_¤f󠔨ŸïGÿ¤0G±¾ïϨJÒ&>òá5ö?ü‡Å—Ú_ì°<£eõ?ˆšî™¢AŸÿxnˆÅ T?ïc½{×ìsû#x7öEønþÑn?µ¼A«º\k:«'–×s !-²ƒäõf<±¯’¿m‡¿µWůÚá^­à?„òëþøQ¬Ûk /öΕluy„¶óH)îQãXÖ#ùŠ fs¸$çOÛV_~ɵ§ìÍñ6xä:„<;ŽþBæGµÒ<ÈYQ{±Žëžã³¼ñe؈åË<‡¨’Î,g¦ü÷¯§¿boø‹Á_4KÏèè~%Ôíâk‹)%†gŽÞÖ!oe¼Nɰ[¢RÁ—q ‚ÉüûW·í÷âÚ?âŸÂ9´o x³KmÕÿ¶´›–Òm£6Æ)^8n]ä.-Îñ’CŒ¨5ߘÕãF ¾}_ßø$sá õœ·—OÃñ>¿øÑâßÙö¹ý§üðâÂ{ÏüMÔÏðÀ¹+¨ê—Bxåô8îÝ£ÏöƒòäÛ{à´?>üý<32ßxŸÆZ亾µr¹-y«ºÇh²1?3!{¦Ž,ó²1Ÿ›$þÊÚþÆß ¡ýªuÚâíî¯|W{m1[Lc6VÓGm ¸‰Bó|˜ö圹ˆÁß´7ÁÿÛâ—í»àï–¿f¿ðgË»8ôûS®hñËyËÜìyo+¶äV ¨8óŽ“ö¯ÃzŸá_é^ÒS˱Ñí`³»¼b4‚¨¯Äψßñ|ÿ౞ðÇúý3ám…¼²ã•I--äÔU½2.nbB{ŽÕûa¯jZ¶™á»ý_HÒ%Ö5+kY&ƒNŠX¢–æeBÉË3$HÎØPÎÁFrN+ñ·ö8ø!û[øöÎñ¯Ç¿_ ÚÂÇâ,z„R]ǬiwJW1Ý òâ¹yeV„l]Ý1º€?këðÇàŸü_ø+÷ÄÞéÿ m.í`=R9­"‹H(=ÙäǸ5û-ñ#_ñ‡†<ªkžð¼ž2ñ´kö=&+«{#s#0\î"E@K±-ªB‚Äù?ÿÚøûOü ø£ñYøíðâK3ñ¡¹“[]SL¸Ki k™¥G† ™faq$Ë‚ªv•¸ä~ÌQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¤=9¥¦±À¢âgäí¬?âñZöÿEƒÿf¯¾ÿgècŸáí²J2 (9úø#öÐÁøÕd¡µƒÿf¯¾> ëmŸ„lm2”\þUîâ`ý„?<Ëg˜ÕSØÞñ?…ä¶ÜÙ.èÏ&¸»[©¬gY¡b¬"¾Že†ê§ Œ+Ç|QáÙ,¦k‹tÌdçŠæ¡^땞Îe—¸KÚÒØï¼;â(u(–9[º†Ÿ¡nÐÊÈà×Ï—“ÙÌ'€•"½«Ã¾"ƒT€$ŒQÛÖ±¯G•óDíËsÕ³™åšþƒq¥LY1žþ••a>Ÿ2Íp:×зú|ð4R¨9¯×ü?q¥N΋˜oF²šåg›ÀJŒ¹á±ë:»oªÀ™`$ÅI®èPj¶çþ}:eš@¥{~ƒ¯CªÂ`$«ž­'xž–  ðäžç‰êZtúlíËÓ§¸«Z>¯q¥Ü«¡ù;ŽÕìÚæ…o«BT€ñ CM¹Óçh&R uSœjFÒ<Œft'Ï yÒukmVÜI±È®gÄþK´76£9#Ö¼ÓGÖ¦Ò.U£$ÆzŠ÷+U¶Õ-ĈrHäW$¢éÊèõ°øšx¨rOs癡– R 7|Ö¦¬\iw ðœ¨<ƒ^â G{¹µ]²/K¤7V‹‡‘ë^s¤jÓèó¬ˆß»'‘^á¥jj¶âDnHäW$éÊèöðبbaÉ=ÏžeŽHXÇ Û"ö®A×eÒîXîО•Üx£Âépyh1 ä^K,R[¹I—q]±’«uÕ£ ëÓéSÉ1ÕêWvv#²0\Œƒé\ñ“¤ìö= (â¡uñ#Ç´}R}*e–6ÈÈÈÏj÷-W·Õmã ýÅxf©¥Í¦\´©ÛØÓ´­V}.ád…ŽÎõuh).hœ¸L|°óäžÇ®ø‹Ã°êP4‘ŒJ=«Åo,î,gh%\`õ¯~Ò5›mRÝdFù±È¬ÏørN#,cl€W= ÎÌõqøÖ‡´¦x„2K‹$m‚µë>ñd3 µ»m²½yeÕœÖ3´3 ñUÁ1È%S‚½1]“„jl|þ:³>޼³‡R·) 0à׉kÚ údìBæ#Þºÿ ø¤I‹K³ŽÀšï¯lmõ+c€0aÁ®'NVgÑÕ£Ohî|ïku5¤¢h˜‚+Ù<=â5h´øßÓë^q¯h7\Å•wFMaÚÝKe0šA×l©Æ¤n Š–nØôxoËÍåšçÖ¼ØoS“ò°5íz¿o«ZýšëñŽ{×-â <$Ýٯ˞‚±£U§É#³„RJ­Þñ9……¥ÛqØšô-GM´ÖmNpw¯ž`Üü®¦½Âþ(h]m/å'šxŒ?+¼G—f þê©Éêú=Æ•pÉ*þìýÓQéZ­Î—p>׳oªÀ²+Är+’½t{Y~cðå–ç‘x‹ÃÓéwãÂÜæ°-/&ÓæÂÄyôUí”Wд3Aé^+â/O¥Ü4‘ÑèÃ×S\²<¬Ë/•{J{›áÿéBÈï[zŽŸ¡C*ç#ƒ_ B†UÎGZñwA¸Òn˺6èkl={®Yfa–Ê”½¤6=Ã~"‹QˆZÜ‘¿úÖoŠ|,²)¼³0ä¨é^_òÚÊ&·m¬+Ù|3âõ(~Íp~p0sÞ•Jr¦ù‘Ó…ÄÃgSsÅÙ$1È0Gc[š¹>•8!³y×oâ Ý{h¿P+Êž?-Ìr.Ó[Âjª<ºÔg†Ÿ‘ôeõ¶©l`Ñ\Š|,A7–cŽâ¹ \¸Ò.$˜jöûëmNØ:Á‡"¸§NWG»F½â½\Wï0ÔªõW‹ü×àÚù”½Ú²}Gù~'EEW”u…Q@Q@Á‹!û?Šu˜:yw· ùH¿»ÍöhsÿÏKfüâS_Â×Äx¾ÏñÅtòõ[Õü§q_ÜÃ)¾ÑðÛÂsõó4›üíÐÐqEPEPEPEPEPEPEPEçŸ<}mðçÂsë†y¨Lék§Ù¯/w{1Û J''“ŽŠ ­hP•IªpWlŠ“Q‹”¶G™üT½»ø“â»ozÍ£¢^x’æ3ƒ†r–¡‡I.OêœM} eei¦ÙÁ§XB¶öÖ±¬QFƒj¢ Úª t ^iðÀ>ðä³k³ ßk’›í^ë©’ê^J)ÿžq‘ã â½V»³ ñÒ…'xǯwÕüö^Iy˜áàõ©=ßàº/ë¨QEæ!EPEPEPEP_>Kÿ÷í1o'Ü·ñ‡ž?÷îôÙ÷gð†Jú¾|øßÿ_ü7ñÒñý“¯Çe+vK}R6·‘°;3^¦QïT•/挗Î×_ŠG.3H©viÿŸà}EW–uQ@Q@Q@Q@Q@WžüMø±ðãàÏ…§ñ¯ÅZøsF€…3Ý>7¹ä$H ¼®@8DVcƒ@…Ex‹ûK|1ñÀ]CöÒ¤½“Áš}¥íóHö’Cs%½‹8wŽv±tdà0#8ç=ð¿öòøsñ†óBOøÇWºgˆnÒÒßV˜éjÍ/’ÒIvŒÑ,Q8>cä„ÚÙèE}ÁX>(ñ™áj^'Ö˲Òà’âSܬc8¬ÝîHºHPYŽêkãïüHð'íã}7à÷ýzÓÄzV“xo|O%Œ¢xcŽÉ•£³wL£f*•ÛõÛ—áãVªŒÝ¢µ~‹Wóíæaˆ¨ãÖý=ORøáÍJÇ·3ñ*mñnV½¬I(ÿG€g±E´zE{…0+?UÕ´­ NŸWÖïaÓìmT¼×,0Æ£«;¹ £ÜšÏ‰uªÊ£ëøv_%¡ti(EEWÌ?ðÚŸ²IÕ×D|4n\à©BaÏOõἯü~½»Åž4Óü)à»ïÇgy¯YÙ[}©`Ò 7×W1œ-¢ŒæRÀä<Šæ4; +á-'þ ð†ûâO†>k^ñ…üAâû˜­tèµÍM9diŸËWý󆨂ÁN+Ú<[ûSü øñZƒ>>ñ,~ñå¬WV§QG¶²¹Y™”$WrpWKƒ’ÉÈBÑH##ih¢Š(¢Š(¢Š(˜ñ¯†m¼gá g—˜jÖ“[yÚdB¾ªpG¸®à?‰®|Qð¯B¹Ô²5+ÛO½VûësdÆÝîÛ~5ëõóÏÃÏø¤~3x÷ÀMòZë^O‰lW×í¸¼?÷ùTþ5êáy†©O¬m%ù?Í?‘ÉWÝ«wÓõ_×™ô5Q^QÖQEQErÞ8‡í ñ¿üõÓî×þú‰…  ›ìþ/Ðçéåß[7å*šþðõØ~Ñ¢jÏKyWóB+ø(Ò&û>«e?O.hÛò`hû좊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯š¾!K/ÅÏÃðoLvÿ„Hò¯|M2\ï·°w”òz(àç"½âÏäð†DÚ\nñ­*Øé6c“=äÜ&G÷ï9à`c ‘Sü+ðü)—q?Ûµ{ÙóS¼n^êöšY ôÏÊ¿ìß5ì`ÿqOëOâÚ>½eòéçèÎ:ß¼—²[uÿ/Ÿåêz,QEI#Ž5 ª£ ªp%WŽvQ@Q@Q@Q@Q@>|eÿ‰'~xÜp,µ¦Òå=„Z´-[Ø:¯ãŠú¼CöÒ®5?ƒ~"šË‹½*8õ(º=Œ‹9#þ„~5ëz&«o®èº~·iÌ…¼W÷ù&@ëúõ1>þ”û9Gÿn_úS9iéVk½Ÿéú#RŠ(¯,ê (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (ªW–iskewu7ÌÉoº«ÌÈ¥ÙcRrÅT gÒ€.Ñ^{­|[øSá½NmÄ^3Ñt­FÛh–ÚëQ¶‚h÷(aº9$V\©dt ×[¢kº‰´¸5¿jú®s»Ê¹´™'‚MŒQ¶É*Ø`TàðAEj×Î_‰øƒãŸ ü·ùì]—Z×qÓì6®<˜Úy€usÒ½ûUÔìt]2ïXÔåYØÄóÍ#tHãRÌÇè5âtËíSNÕþ-kñµ_\ ¸Ñþôtce”?÷ïç$uÝÏ5ëe¿ºŒñOìéñ=¾åwê—s“ïµK¾þ‹üöûÏ~ÀQEy'XQEyÖ­ñƒá&ƒ¨Ï£ë¾6ÐôëûVÙ5½Î¥m Ñ·\:<”û@‹EaÚxŸÃWúñMŽ­is¢˜ÚQ}ñ½© þr±MªAÉÎ m«+¨t!•†A‚ -Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¤n”´Ö8$ ü†ý´°~3Úg¯Ù`Çþ=_Qü>߆¬ÉʶÅþUò÷íŸÿ%®Ã=>Íoÿ³WݾðиðE墿O)xü+éUìa~_K)cj´v¾ñVloN;5é2Åô\Vós,–òm“‡Sϵz7…üPT­•ãdt×›ˆÃëÍéòÜÃOePÈñ/†äÓ¥3À»¢nx®ZÖîk•š&*ð¯¢å†ÞöÜÆà:0¯ñ/†æÓ¥3Û®èÏz(U¿»#<Ã/p~Ò‘è~ñz¤B';esÞ·5>BÙ¢”g= |õgy=Œ¢x[s^ÕáïêD±ÉÄ£¨¨¯G•óDíÀf1«eSsÉõÍm2åƒ/ÈOŸc}>:Ï3Î+ßu].ßT€Å"óØûׇë:5Æ‘;$ƒ(O½ÊjÌó1Ù|¨Kž±hZõ¾©± íRkº¬c÷ƒ¡¯ °Ô&ÓgY¡8µ{†ƒ¯[ê(݇šç­EÓ|èõ0XØ×‡$÷Y<Š÷ 'U¶ÕmƒÆr{Šã)S—2=¼.&ˆrOsç·íÜÇ"á‡jèt ~m.`Ÿ,õÜøŸÂËryh1 çä²£ÂJH9ë]±©ŠÝO½ áªsD÷;«[XoÈü«Æõ=2çL¸h&\ÓÞ´ô-z]*u]ÙŒžEz•Ý¥ˆìw& ‘Áî+ž.TžÇ£% T9—Äxî•«M¦N“Fä àŠ÷-X·ÕmÕцîâ¼+TÓ.4Ë–†á0àúÓô½VãI¸Yco—<Šº´×4N\:t%É=^ñ‡aÔ¡2Ä Jx­å¤Ö2˜%kßt}b RÜIù»ŠËñ‡`Ôáib”V+8;3ÕÇåñ­kÄ722º¤tÅz…¼T®VÂìóÐ^iwg5”Íÿy B$o½8oZîœ#V7GÏaqSÃÏÈú:úÆÛR·) à׉kÚ ú\ì@&3]w†|R0,ïžÄ×{ygmªZ”p0à×J›±ô•)SÅÚ;Ÿ<ÚÝÍg(š"AS^Å¡kÖú½¸·¸Ár1ƒÞ¼ë_Ð&Òæ,£1šÂµº–ÒU–Ú»¥Î7[ž ÓÃÉÆGuâ <ÞÙ §Vç¸ äpE{f¯Á¬[ýžç‰Á½rž)ðÑšîØb3ÉÖ±£7~YXÜgkDw…¼Så²»$¯bk¹Õ´»MnÛåÁ$p¼ ¹xÛÞ½ Ã'òm.›)ÐOJÞô€Ç©¯eScŒÔôë6á£/CRéZµÆ—2Ë gæ{F­¤ÚkV»” ‘kÄu-:}2é¡”aWîŸZÒU8òÈǃ•óÓØ÷}X¶ÕmÖHÛæÇ"¯^ÙC}C0È#­x•«Üi“¬±p3ȯoÑu›}RÝZ6±È®Et{¹~aðä™ä^!ðìÚ\í"ŒÄzÁ³½šÆU¸šú*úÊèT0jñhé–OõLx®Š5¹—+<¬Ã/t¥í!±éÞñ¤*Žß¼Enj|Œ)@;‡Ò¾y²»–ÂešÆ9>õì~ñΡæšî…q¥Ü—1g­cÚ^Mcp'€à/?Zúþ F†ewzñ@¸Òg8ˆž+|>!IrÈó³,¶TåÏOcÓ|=â5XRà?¡¬Oø`H­yf¼õ W™ZÝÏe2Ï·¨¯iðÿˆ Õ`Xeûøç55):RæŽÇV D=MÏ‘dCµ×{WA¡ë³iW‹“WÒ»Ox_ÌV¼³7Ryc¡Œìa»×DZ¨*½)açt}a}oª[B0äWŸø§ÂÛw^Y/^HÈhZ寑râc'‘^ßakª[Œ† 9Å(ºrº=Ú5aЧË=ÏÁ–ÞL•‡Q^©áo¤ø²»o› '½TñW…˜föÄsÜ ód/»—åu?•u8Æ´|Ï"¦¦»FßÞÖF`5ø½ñ«~Ѷ¥xÿO¶ÿÐÖ¿Wô?‰¬ÞÖí¾`¸÷¯É¿ˆ¤Ú2Ñ×£_ÛèkZåÑqæO±ËÄuáWÙN=ÏÛc=Aoþ¬}*zðå»>îÀ‚Š(¤jQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE5•]J8 ¬0AäkçßÙõ›AÓüMð¶àŸ3Áz´ð@§–ûÙûM«ª»ì+èJùçVÿŠ;öŠÑõAòXøóL—O—²ý»N>t,ÇÕ¡fEõÅz¹¿N­ë™zÇ_ý'˜äÄ{²„üí÷ÿÁ±ô5Q^QÖQEQE|¹uû~Éw×÷¥÷ÂæîîWši%´Wg’F,Ìwg’I&¾˜°°³ÒìmôÍ:·´´!†$HãŒUQØ¿ßÿ¶Ÿíe¡|cñç‡tÏŠz奎—¯j–°CÉEŠ(.¤DEÚ  _Õ_ÀígPñÁ_‡þ ÕîòûSðö•uq<‡sË,Ö‘»»嘒O­z•Q@Q@pî¼_aðë〝ìtÍzÎÒK‹[J¸³F„oo68äÊ”V 6’O_&~Ý_?áYþÈß ÿöý ?fOÙ§ö4ÒåñïÄ-Ã[Ô®õ _PÓbºKEdyL1!µ„¼ÛÚ" Fxæ°?aß xßö’ýµ.|+ºÑ×û2î{Û]VÒ[†¹[·Hâò)¢ÑÌÁƒn^„mù¼áÇÄ_ø)ÇăZ'ÆŸ 'ëȵ˨Ûh÷6ÚŒrÂÙ1¨a0ˆ<Š\È«‚2¼»þ _ãßìï„ßþC0ŠOk3jóÜé°ùxoöKÝ+}WÚ½ÏTý¿¿f„ ´o‡?õ–ø“âí+J·ÒtE´¹¸{™í Xa ËUA´—۪NJõØcöȃö½ð>·{ªèƒÃ¾+ðÌVº­”lÏïÃfˆ¸ ¡Ìr)FÉR‡’5Õ~×¶€ÿd¯Újºå´šï‰µÆht}ÙÂÍw"ã.í†1¤¨g Ç$V'ž¿à—ß²ÿþü7ñ?Œ¾+Û¶Ÿâ߈—p]Obøóm­­D†6ÞWyävOáAÃnQògÉ_öÍÿ‚©ë¾0Õâ^ø4³Çe˜x‹i’›{b:ŒÉxït¾Êé@YE¯ÿÁRu¿OñR /èW"ݯm¼#=­ä·¯]ë Ò‰p· : uù¸b‡ {ÇìKû[é_µç©¼_ýœº'ˆt[Ÿ±jÖæH㘠t–&`•*ä¨<© ¤¶ÝÍõ®µ«ØxG¿×µYD6ZlÜÏ!è‘B…Ý¿×áïüÏQŸCøÇây”¥Ž¥¨iQD?€M ]I(_p³GŸlPÓ¿ðQïÚ‹ãŸì¡¢xWÆ¿ ®4)ô­já´ù¬µ+)¦¹ 7œ’Çqù{R¥rœp¿7j¿à àˆ?k½kFð_Š<=à«iç]0E}©Pnï Í Æ% ¥ÚC€¤¬dŒ×/ÿZñ'‡<ûKü ø âjÛFÐí¤KÝbîâxá‚Ö Nê8ZIžB PÛ;Äpã³ ÷Û3öÖø§|Õ¿gÿÙëV¶ñ׊üe§·‡´í?Ãù¿Š k¤û<¤Éåfò™–4RÎÎFF2k®eNkâz|ï÷íés)Ù«íúô>íý•hö£ø/¤|ZѬI’íå¶¼²w›k»vÛ" ]êrh%Xd>sý¤ÿnM_Á´ÿÙ£öpðÄ~,êED±Jåtí0:ïÍË#+3*~ñ×r*!ÜÎËPþÊžÔa¿ØJ÷Zøo³XÒ,µ/jvA†Rw|v»‡fÈâ» ?7ÿÁ!¼ªx²?‰µŸŽÀ¼ñµY¬à¹uùöûMë§\$³H‹Œñäã¥rž‡ñ£ã_üöRðm¯ÆoŠø/Ç~¶¸·‹Y°Ò`¼´¸³K‡©Šy6—eO1‘ðÌ¿!‘úYð¯âG‡>/ü9ðïÄï HϤø’Ê+È7Œ: ÍŽÏe7ŠøÛþ •â  ö#ñí½Û6¯&™en§øåkø% {ˆãvÿ€×QÿÝÒµö%ø[i©î󤳻¸]Ý|›«ë‰áü<·\{b€<«ãíñwÄ¿´l¿²Wì iz§‹4¨ úÞ·­¼‡NÓP.pÍåù‘«7ÍûÆòÄd‚kË~"|}ÿ‚€~Οþxâžñ®•ñ7T‡K·žÊÊæ Ï4QʇFP¢ÊX«©Pr2 |Õñ‡Xø“ÿðý¼üEûFê^ŸÄ?>%¼é5Ä$óóÇq4 !ù#¹‚h·ÆŽ@’1€FX§íÿŠ ¿hi?¾^[ëÚS¹0Èñ:Òå߈ãt3&FGJHÎÿ·ïÆÿ‹Ÿ³ŸÁQñsá]ÖŽ¿Ù—pÛÞÚê¶’Ü5ÊݺGÑMFŒîf rô ¯Íãÿ<]ÿ#øËð»Ãô½cáÖ•gâ{4½·¶¼°ÔüôŠB|²þ\¬¿2ᆠàŠñø-gŽ'µøSð÷áVŸºK¿kRߣžHôè|¥M£“ºK¥ w+ÇJÒñ'ü;Nø{áÏ…Ÿgoê‡Ä×óhþ²“ÅLö6 m—iòGæÅ4²d pªKnýŠ?hÚkâ÷ÆO‹? ~:ZhVðü1’->Y4‹y£_K4Ь’K+…Ø|ŠØe'+ô²³,tMLº¼¾Ól-í.u]K IÎà`4¬  t-“ZtQEWŒþк,ÚïÁ¯Ák‘sgköèHûË%‹-À+ïû¼W³U[ë;}FÊãO»]ð]FñH¾¨à«È×Nì«Bªû-?¹™Ö§ÏèÍðƵ‰<7¤øŠßV©iÒã¦Ù£?nWƒ~Í——|&Ó´KæÝ{áÉîô™ýžÎwE÷ïm{Í^a‡T«ÎšÙ6¿põ9éÆ]ÐQEÆlQEQEQEQE5Ý#F’F Š $œRM~'|Ó­ÿ࡟µoŒ~;|HƱð·áMáÓ<)¤Jì.frÄ\Ke\ìEš@Àîi"RvG´þ§~ÑšÌþýŸ>'x‚ÔŸLðƵs^»á²•׈¯…¿àZ]½‡ì~—pã~¥¯êWc®åB3ï¶1øPÕß¶|qÃû"|\† x_SUU v:^/ÿ±ÿ“øwÿ]5Ÿý:Ý×µþÚòi?¿ìXÕ?ô«Å?à–?òc?ÿ릳ÿ§[ºöªø ñ«ã\þ.OüRÿ„/àÖ—¥¼ðéº F-FúHíËÌu+‰2<•F™WLUa¸ü©ÿFÓlãøEñXXÇÚç× Ÿ¿—ª²ÎF5ú+ûHÞÜø¢ÒÇàΔäI¯Å=ö¨Êy‹J²äÉ:@±©úƒÖ¿>à‰?òCüÿcÿéUÙWéÒ„›ÖWÓ˧ÞÓûŒaSšMvüÿ«µñwí¡û"/í}áïøVïÄÍáÍ?Ãú¨¿» ngk¨JymŸ1m´®C`ÿ }£MwHѤ‘‚ª‚I'Ô“\fÇæŸí­û/~Í>ýül¶> Ѽ=ÿ–÷Uí½¤PÝCw<…û@W3ɶ7ÞÍæËe°i?à’Ú§Šµ?ØÛDÿ„•å–ÞÓSÔmôÇ”–ÍŒr 'øc*( ¸1_?ü]ñŠ?à¦?[à‹É4ï~½Žox†»ª]ÆNØ-O*à`ù]W9 oØø7Ã?<'¤øÁ¶éš&‡m¥¥´v8¢'$“Õ˜’X’I$“@·ü¤gö\ÿ¯‹_ý8Wê?Ç¿?hO‡º—߈úlw¶WÑ:Ã>Å7S‘„¸¶‘1ʇÁ+¤ƒùqûpÿÊFeÏúøµÿÓ…~ÓPå_ü‹ã'íõˆ?±çÅÝLê^'øCtöúmÔ ‰®ôˆ¤0É%–#å”'Ÿ.T\µú©_‰š\ßðÿÁk5Xl‡Ëâ},áz.Í")2Øõ6Ê~¤WíQEQEQEóÏÆø¥¼qðÿât$6šƒh÷äp>˪.Åg?ÝŽUSõ5ô5y×Å¿ü6ñ…c¸½´soê.býäð‘V½ ®´a^. Ñú=àÎ|T©»oºõZ£Ñh¯>øSâñãχ>ñc6f¿´ŒÏíqîæ„ŠÂ½¹+Ñ•9Êœ·NÏäkNjQR[0¢Š+"Š(  ƒÈ5ÄÃðÏáž>ÏáM&,vÆþI]½ :ÿÄωƒX¿¶›Åº»ˆ§•0×÷î¹Þ€?ºê+#Ãó›M¹'&[h_>»× Š( Š( #ø…ñóàŸÂ}NÛEø›ãÂ÷÷pý¢5Ø­¥’Å<ÅY»”ŒôÈ5Êi?µÇìµ­Ý¥†—ño³\ÊÁR?í‹Egf8 ¡¤‰=†M~bÿÁgæƒ_Ó>ü.Ò­£ŸÄ>#Ö.^ݶƒ*ª,Vê›±¸,’\:¾Õôïísû,~Í>ý¼j÷~ Ñ´û¿ xngÓõH,`¶¾[ÛH1jMÄj²1–`ŠêX‡ÜAÉ4úDŽ’¢ÉG© ƒÐƒ_?øƒö°ý™¼)®_xkÄ¿|;¦jºdÏoukq©AÐMÚñÈŒùVR0AäùWþ 3¯ø«^ýŽ4Qâi¥¸‹MÔ¯ìôç•‹°Ä˱A?Â’G@(À|Eðw…þ<Áb´ï \éV·ú7„lmæÕ£xRHnÒÅ®™+'阮&ÜL˜ ØŸþÒ?³ïÄ=V=Àß<=®ê“«´´Õ-¥¸÷bW.߀¯j¯Ã?ø+Á¯~ øgâ‚|7¦xCÆ©¯ÛÛÙÜiñXM<>LÒK¼[„Ýå²# ,€ÜAý[ø{㻽ömðÇÄ¿‹W&ÖîÖ:®»<£ ’Çb“ݳ>`Á‰¼P¡x×â~h¯â?ˆ> °ðÞ–‡i¹Ôncµ‹qè¡¥e`9=…yßÃïÚoö{ø­¬ÿÂ9ðë≯jÌ­µìMráX¤D‡p£’T+ò?öAøy©ÁC¾.øŸö¯ý¤a:¿ƒ¼=|Úw†ü;1/§Ç U«D~WŽÚ2übiX—ÈR§ªÿ‚¶ü øyðëá'‚þ4ü8Ñlü#âíÄv––÷šTÙHb’ æP|•]Í#FO+óÔÐíÍ|Ó?í•û'ÛK$ü]ðÂIe:­¾C)ÁßÔW¦üñ}çăþññ.…¦js»@’òÖ9›åíËž;WàÂÿ„z/íKÿ.ø»$Zm³øCD½¼[ãä¡‹e´ÑÙŸ,ch{†‰ðÝpÌüàÖØzJsQ“²îEI8Æé\ývøñ?á·íñ+Sø¤ø¯JÖ[GYlô-.Úú î­ms¶kÙ­ÑÌ‘½Ááw(!8äkí*þyÿà¢ß|ð ö˜ø â_ÙßFµð—Œ5¶iítˆÖÒ9<«›h­Ã ÅæÈ_Þ(!³ƒ_«ß¶ÿíÿ ½û>ëôãxŠéãÓ´X¦‘ïîsµŠÿ†5yHèvm=kLf)Õ’v²JÉvKú»îîÉ£K‘[¯S×þ$|{ø+ð}â‡â‡ô Ïp»¢‚úò(§‘¼±æ2û…Åj|9øÁð¯âöŸ>©ð»ÅšgŠm­J¬í§]GpagQ*¡- pkó£öý޼8þ³ý£¿h;ñ×Ä¿ˆ‘®¬×:Ò Ógir7Àf ²FUÝÈÊ‚#]ª§wÍÿ´½ ö`ÿ‚§ü#¼øAi‡l~"C¦ÛjÚ}”k´¿Úw³iòŸ%EV ˜DßÖ¹MOÞêù¯Å¶7ì±àÝj_x“⟇í5w1˾ŽV‰ÇdòËaÜ1w¯Ÿ?à©¿¼eðãöD×.¼q-•λ}g¤Ý\ÀJÉ És6WÌ"'ÑÈïš_سöný—µOÙ7Á7_„4O'Š4X%Õï®­!¹¹¸½š1öÈä™Ã:ySoŒ `#Û‚2@>ßð'Ä¿‡4¦×>ø›MñE‚¯6›wÚ#®bfÚØþÁö¯4ñíaû3xS\¾ð׉~(øwLÕtÉžÞêÖãR‚9 š3µã‘ò¬¤`ƒÈ5濱7ì—oû!ü>ñƒ?µ£×.5½fmGí)‹m¹Š8 €†f$Æ’sÉc_›?|ÿ‰þ(ø$ð°jñjñTÕ¡6ß`èßz˜o{ V¹eøò¿ý)•t« wºýCè:(¢¼³¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šù+öÒý§¬?e‚w¿Õ5zþeÓ´k7?$·Ó+2´€Æ(•Yßœxßì£û_èÚ†•ûI~Ó:çŒ>6j×F[Û†k}gRµ´)¶5d¶¿’±€£s|Ñû{º|Cý¿?fƒZ¡7:E¼Öú¬ö„þîA=ñ߸wÜ–Eyíœu5ûa@„ÿ>xâwüÃãw‡þ#øoNñF™„nR×SµŠòJEG2²‡ ìc 1Í~ºx¶÷Fýžþß^|5ðÚ‡b͇<5irHóÍÊÁEd†I © o}¬x?—ß?å0ÿ¿ì\å¤WìýÅİIus"Å *]ÝŽUFI$ôu¡ ?›¯Úÿâí“â/ŒŸ ¼ñââxcÇZ”°x[E½.ðÛÈá#Q–0<éHn›Œyª!WôÛAµº¢‰B"(ª¨À€ þqmë½OÅ¿´¯À/Šš‰tƒÅ~"#N…ò<½2ÊþÊ+s´ô2òþÖGZþë³EÑ›¢Ýí¿­•þç§ÈÆ„Ôâ§mÿ._’¿¿fÚãï§âïø&GÅß ›}cPñìá㛄ÓMž£t×2ønôå‡ÙÞND!7H}ôYfDWo¯¿àœòdß ?ëÆëÿKn+˜ÿ‚¢xJÇÅ_±OŽæ¹„Ks¡6Ÿ©Z±ë±^E°÷0É"ýÐè:H‹$lX9„u|§ûxÂïDz?ÂÏê urÚ,²Êçs;Ø–µbÇ»f.Iäž¼×Õ”QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦¾1Í:˜ã"7¡ùûh>6Xgþ}­ÿ›Wéo• à­?=<¥þUù¡ûiŒ|g²~ëknqù×Ý¿ük ØZ\/–‹Çé^ö*-áà|Y^Ì*óõ=ÅÞˆÂú„_»XÆç#Ò¾$ñí[ð7ÂZ”šVµ®˜.àl2…‚?àB¿@u»ˆ.ü=y$d:›5ü³ß|0Ó~(~Ø^ÔyÃ0#ƒ·ã^t1-{¬úZù]9>tÏÛïþßß³Œ ök¯»qœ”_þ*º—ý»ÿf-A~ÊÞ#Ý¿ŽUøºøÜÁ<¾¬HÞùFr¼ÿ*ÔÓÿàžn‡ÙZ8-Ý &Üžœ…¢¥ýä,.>Ÿð®}•¦|BøwãR.|ª¥ÚKÈPF}5ÔY^Mg*Ï Á_~µøcñóá'Äo؛Ŗ~+ð®·5þƒ#cVm€rqƒ_¯Ÿ¼^ß¾éÞ%‹æßvb:WMÑk–G›˜`9´ö‡uÈuKT q*ŒÒÕ4»}N Ç¼ OÔgÓ®Г€yíú½m¨Û†y\A"¹ëÐä|Ñ=cñösGŽkZ,úT쬧Ëê R°¿›N™gˆñ^ó©ØYêð4E”’:ƒšñgFŸI¸1º„ðk¢eQYžN;.•sÇcؼ?¯Áª[®Hcš—\Ñ!Õ-ÈÇÎ:W‡é×·tâHNH¯mÐõÛ}FØ$Uuê ¹ªÒäwG©ÇSÄÃÙÍ'¨éó铘&_ǵ.™©Ï¥N%„ü ó^Ó¬é6zÍ» *d9¯Ôté´ÛƒÊTgŒ÷®ªUUMÏ#‚t%ÏÜ´MnÛV·VVùÇQY¾$ðÜ”M4C/5äZf¥q¦Î³@p½Å{†­Zê6‚C"î å­fù‘ì`ñQÄÃ’Hð;›im¤kyT«-ii½Î•:Éùz^©â/[ê™mÀó1œŽõãSÛIg)‚e!Ç5ÛN¢œu<,^¦\èúJÕmµK`èA$r+šñ…a¼®-†sŠó-7TºÑîÂù^êkÚ´MnÛW¶¿­qN.œ®s‰Ž&“ÜðI ’ LS ¥kA×®4¹À/˜PkÐoc+-C’¨ó/ˆñý+U›L¸Y"'o~kÜt^ÛS€›+:cÏ\Ž+z”•¦yÙŽ_*2祱ê^Ö­õ[a·ïÁ¹ïxY$Ýyh¼ã+Îl¯gÓnâ騵íš&¹m¬[€Hó1Ê÷¬ªÅÓ|Ñ;èV§ˆ§ì§ñ ñ¿kX|Ú£ã}ü&:ùFÔ&#ù×õãû"ÍçþÊŸ_ÓÁú ÿ߯ô ¢(¢Š(¢Š+ñ¿þ Aã¹tÏ~ øc§±7Þ0×D¦5ûÒÛéñÊývžõû!_~ÑðO/†´ßÄ$ø‹ñÆþ/ŠêÕ;;ë(¬¬ä[G%”Ž…Ùw»,XõÀP=§áì±ð;À^ ðƆÿü;>©¡é¶V’ß6•h÷3Mm #Jó·³³)bÄ’IÉ9¯£c"EŠ%ˆUÐéXÐ'ð·†´ï\ëÚü¶šÿRx伸#øæh£‰ žåQGµt¥Ñ”1BAÈ÷È ÂÏÚ]-¾<ÿÁX~|%•VïKð-µ¥ÕäÇæÀ²êòSÁF°!SסëPÿÁS|'iû>üBø5ûRü(Ó Ð5]'S’ÒöKÖÙnZ—é(ˆ(mñ­Äo»;ã;NTb¾Âð×üWá·…¾0§Ç­?âg%ñ·ÚZêkùµ =Þá¤$Iâ\3¡(T`áq¦ÿi/ÙÃÀµ/ÃsðÃâ4×¶ºjÞAút‘EuðUdi¢™UÙ[(xcŒhÛt}ZÃ^Ò,uÍ*Q=–£W0H½)”:0ö*A¯Ãø#íŒZ'Å¿ÚBñ ù~'´»±‰â–M°Üß%É yùeòÃz¾µúíðà–û?xßᾉâ}sÄúU‹fúõÌ7SÚ@¨¨–м0@((¥I\Üáÿ`¯…Ÿ>&Ïñ§Â~ ñÃ/Ý£%Ö©á{ñd÷{€RÓ£#‚H6›ú¾ãÍy_üö±øUð*÷á/†î ÏŽþ&Æt«;+Þ\%”ì#º”¢üØ‘ ‚>ìïòƒ±±ÖþÉ_ àý‰cí6ÓÅöÿñRß³jš…¬ëfÕo¬6jItb…±‘”fWeðwö ø+ðŸÇMñgVºÕþ!øôë®øªóûFîÑ|ˆŠÊ åYÔp¬k¤þÈ›öŠñ^§®®¥u¥øKÃksa¢ÝÙ2,òjn¦9µ ©$y·Î-Ù‘×xÜW¡—á£6êUø#«óì—›üßCŸQ¤£‰íþ#òöxð@ý¡à¨>7¿ø¯giâh¼e{.¡my \Ùýªâ°ksÑ„Ná,ÐWïw…>|1ðÍsàhþ•×k>§ÛÚ1_BaE$WÇ?ÿà_ g¿‰­ñ_Àž<ñœºÅÑqíýŒÐj)+ùŽ—AlQÜ4€9!ÃnÝ×? UÉ^³©77ÔÖœb¢Œ¿à¡šn©«~Åÿmtug¸M-'` “äÛÜE4ç°‰ŸAɯ9ÿ‚TÉ£?ìCàtÒ¥Y'ŽãV[Ð,—P¶°”c#ý’zý ½³´ÔlçÓõRæÖê6ŠX¤Pé$n6²²ž °$x"¿:—þ ©ðçÃz†ªßþ%xëᎮÌf¼Ò#É}’.â½ §Î-æB®¨w2:“µ”Œàö ã\Ñ|ñ—áËéÞ&°¶×ü'â{•á¹Uxf¶ˆýÀ Êêr§ ¤ ~7ÿÁ´ëÇ?to ÝËàNÖ :á›tsé/Öîc°Òµ¿j6×SÊÛ"û)…ëJìxb/Éì¥|ÃûÿÁ@¦ý­þ2øãÀV~‡HÐtYo´Ë¯5ÚêæÙnVi‘†ÔfWV t'=kÔÍ}éB¯óE?¹r¿Å3“ ¢”;7þ“?Lh¢¿8¼û~Ë©|Zºø û+ø ãâ÷ŒtÒë¨ÏÒXé6&#¶A%Ó«+laµŽQ7ªìÿ-ygYú;E~Vi·OíáÚ3Á¿³¯Ç_ƒº6£ãiàKKÝ7Wóá[y\«Ü(òdbé½c= çé_Û?öñŸìµðº?ŠÞðŸŠô«[„ƒQKA¬e€Né ˆeîv!ÆT¨ÁŒàëê+åÙ/ãÄÚ#á4|ÓµY¼9y-¶§ªC¯¹Òí’Z#7Ú~džG*LAAy*¤€׊(¢€ (¢€9/ø]ñf¿¨Oc}ys \Ag©Ë4ó¬’mb‰ Ê%bQKp=J*¥HÁ».¯²êþFugËÑôgŠ¿h]w§Æ~<Ó~xßÇš÷ެ$M MDšóN·Ó£2ÛÛG=ÒgÊy Ò¢£¬ŒÜ+ã_ø'ÄOŠŸ²‡…ü+Ñþ|=ý¼Moe¤Ã†íuC%ÕÓæÜÏ·N]ÒHÜžxU¨ô;þ ÿâÏÚÇ? ¼Uâ¿ÚOOÕ4ê>)¿–ÎÇT²–Á­tç‚Ù⊧D“ÈIUŒœô#q ×ÝuÌxÓÅ à¿ jž+“KÔ5µÒàiÍ–•l×wׂƒ#žÊ4ùûpÿÊFeÏúøµÿÓ…~ÓWáÆ=[Çÿ´OíÁðâ7…>øïDðÿ„ïm"ÔnµÏÝØÅûgšdgâƫՙ€÷íOã?Ú«Äz¬ÿ¿f¯Kg.µdŸmñ¾¡p¶Úv› Édqm·2IpªJ‚ñ’c9 >=ýš#—ãŸü+ã?Æý=þðE³hÉ>w+]¤qiÈô"Amq #¶=kö¾¾hý”¿fO~Ê¿ ­>øeÍö¡;}«VÔœb[ûçPB2v¢€4ÏÊ£’X³7ÒôQEQEQEQEóÏÁ¯ø¥¼cãÿ…ïòŧj#U±^ƒìz¢ù…v9C©÷5ô5|óãÿø¤>5øÇ+òZëÉ?†ï[¶é¿gø™U‡Ð×ÐÕêæ¾û…çIüÖïjÿ3“ ¢•>ÏðÝQEåaEP_ÁW¡û?Œõø:yz…ÒþR°¯ïV¿ƒïŠPýŸâo‹ éåëëù\8 îWÀS}£ÀÞŸ¯™§Z7ç šë+ÏþÍö…ž Ÿ¯™£iÍùÛ!¯@ Š( Š+ÄzÜ>Ð5/ÜZÝ_E¦[ËrÖö6ïuu0‰K‚ƒ<²60ˆ –8€?ÿkoøX?¿à¨¾øyð¥´ÆÖþXYÝÁý²&m6+»e}\½À·ýî4)…êÛAÀɯª>&þÈ_¶íK§…iOŠº‡àh¦Ž{­#ÁöWíPòÞa²Ê—ó[ å’¯šbïøZ7_·ïÄŽ>xËÃöÞ=ŽúÛH¹¾Ð/£·³YîahVêgˆ$[m¡ï'nr3ȯ޺â>ü=ð‡ÂOhß¼ dºnáûu·µ„íEË33Y݉wcË1$òkùóýŽ<=ûB|~ý¢¾<~Ò_¼S¤øfúâúâÖ)õ‹&½Š{}Nåî"†23åùImfÚÇikö·ö¸ø…⇟¡4wwPH±Ï*À¬c†#ó3œ €£æa_ÿÁ#<1âO†ÿ¼MðÿÇ^ñ„¼G&±.§4ºÆ‘uaoqo$0CÃ4ñ¢»)²ƒ‘’zùÏö>ð¬?¶—ÇÞ~Ùúæ£âŸˆ_oLV¾”Ao¢ÅLb•„*ùÄAd\*°ò÷™A!~Êÿ‚µxÎûÂ_±¾³§ØHaoêšv–줩ò‹µÓ®GfûXwRAâ¾[ºðïÅ?Ùsþ mâ/Ãx§áç­›í·zw¨ª.£SJÀÃ#jÿ´ïìß­ü=ð»,~ ŠK}OLYÈ$ºµ$ˆ]›<ÈÙÐ1 +0'€h`Øü>ý~ivp[RÒ!Õæ?Äòê™»,ǹĠEv¯ÏOø,‡Œ.üa}ð›ödð§úf½â Ljmj¿{Ì”› ÿ×G–p?ݯVø)ûcüOø=ðGÃ<}û>ü@¿øƒá :-Þ åÓï–Áòý°…dU:G ,»«ÿ²·ì—ñsƵ/ÛSö»†+?\³6… +‰SKiŽ6“•LQ° bÀ“$„KÐîøÇÃ_²Çììú½ë,–^Ñ"´´‰Ž Ä–VÂ8cþm™8è žÕø©ÿÐð/í‡wðóÆŸþ_ø:Êêÿg¾¹ñ4Wó^É-Š™<د÷N>|’àðúGöøñwÄ‹¿uË†Þ ñŒ,¯DÚ>‚4].æþغIÔu —*˜‹[Û’ij®65};ÿÒÓ¯ü-û*øsÀï„õÏ kž’íuu½2ãNy§º¹–àIž‰æ¦ÇQ¹z`)äW~; <´ßż¼¯ÓÕ-üݺô*¹Þ]:™Éüý‚5è~7ÅûLþÔ^:?¼dQ´èa·ÚfžñäÄcCËùD“ £åYðÃãïø*ü—_¿i€?³üŽÿÙú¬ð´‘£Þú½üv[Ž;ªÂÁOmÍŽ¦¿{+òþ 1û:üXñGþ~Óÿ4vñ7ˆþ^C5Γ<û›{[”¼…¢_½&ÙÕã\¹ ©Á® ýX´´µ°´†ÆÊ%‚ÞÙ8£AµQaU@è¿õ»øiÏø,6—ŒßlÑ>$k4ËÈû^géýÝJ+ëÍ}WãïÛ‡ãoÄ Mà¿ÙÓà'Žm> ê‘}˜ÜëúOö~Ÿ¤Ë"á¥iå}Žñ“ò <µ' ܇Õ`ïØÐ~ÊÞ Ôõß&·ñƳëWÊÍ*Ff[x¤p€f/# ŽrxU ®þ'ü3ðgÆ/ë? ¾ ië©è:ì&ˆIÚÝC#£VHØF«kùöñ&ñËþ ñ‡JÕt joüñ}éZIÿ-1æÇ$|$7±ÆsÑáe ó ¡£_®âý¤?io‚ÿ¶'Å WâwÃx›áFµ µÑÆ•aq¨[Y ±Gqm"— 3È«Áˆ$\§í ¦ü_ÿ‚’ø«Á_ <)ðçÄþx{PþÑÕõßÙÿg\LÛ<²-mÜ’åcg ´¶æ`_ËU$€~ÖÚÞZÞÙèZÈ$·ž5•tda¸7Ѓšþt¿bÏ…Þ-ý¬ÿh¿Žßü5ñXø~¥$v×Ú@ˆÍqo©\Ë2ÀZUa²8íáéÏݯÙÚ³ÆúÿÂßÙ÷Ä|;ð®µâ}P°›HÒ,ô->}BXn'·tŠiVc0ãs9ã (ù˜Wå×üóâoˆ¿d¿‚ú‚|kðâ­ÿˆum^}Fâm;ÂsKŒÅ0Ư4±Ú#$å ½hëØ{öTø¡ð?ã?ÆŸü\Ô¯Ûcw¦_Ma2ÞçÉ·VxKÆÒJFÙVÈã óWü3MÔü5û3\~Ò?|q«êëâöfŸ[Õ®f²±Ò¬e1Ï#F¬Ï¹“Šw~¯Q_Ÿ>|Yý³þ?øËã>ãÏø'à º¬Òi¨ºÕôW:¡S‰Í^CäÀò†v;qa ÊÆ?Þ-#L·Ñ4›-Ñå’ #·§•ç™’%¦Id,ò9æv%˜òI$ÐQ@Q@Q@‰_·á÷ü#öiø»©æ×K»’×Kšé†#Qû 77û){“í_¶µñçí½û-ÁûW|¸ðMÔzw‰t›…Ô´[¹È—‘#/•#˜G2±V#;N×Ãmù×ì›ûbÿÂbÚoÀOÚÖãÁ_´ÈÚÚâÃQíÓVû8?éV’åÈdEÞÊ­‚w4{“|ÅðþSñËþÅÇþZE~ŒþÑ~$EÐl>A›7‹¤xï.ä‘bK="Ù|Ûû‡v!UV!·,@;ø·ÅºŒ:N¤éw—ww(bKÛ‚ÌÌýdð9¯’|uq®ÁQ~1xsÃ> ³»±ý|x/õ jæÞH¹PÅj$ Çäfx5yA“¿¿a? Üø+öAøU Þ@ÖÓÇÆÖV¿f»9{È<×Öu0ÅoA,qF¡UTª `ÐT”QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢£gÚ ëŠðO|o¶ð¤vzuͲk— I[(®ÜG‚_7.$`s¶0I2šÂ¾*(óT•‘­*›´QïôUxçWMù«¹QEQEF϶€$¢¼Å<3á½q¼-m Ö¹­Æ‹$–Zl&âX‘þëJÙXâ Û{®{f©éŸ<3qª[hzõ÷†ï¯[Ë·]JsHFv$ÈÏ |~Oa\U3ª¨JiIìÖ£2ZÑEEÉ"+ƒÃcK]¦ Q@Q@Q@Q@Q@Q@Q@Q@5ºS©®@Ó@ÏÈoÛHŸø]` æÖÜ~¦¾ªð5´¶þ²2‚ Æ¸?…|«ûg–ÿ…×§ÿ>öÿÌ×èG‚t+}[Àv@ó<¤ þôS¨•\üžÕÆÕktdGâI´ýú ÎäòŽ+ðŸàûÅuûy¼ªê¡…Ç.p?‡Ö¿müSg6Ÿ¥êN»XF@Ïzþl*øUãÏ_é—º„,QŠdçœ}+æ/ø&7Ä›ÛOkþ º”›ušs'¨ÎF? V5¡ÈýÓ·‰öñµD}qûS|p³ø¤K´-Æ à˜£'þ\׿…ÿíeñ¾wÖt[«ÆR]6AGP+¾ý§µ'øÁûbé¹o6ÍRGúÂ+õ£ÃÞÁZ†•m“Q¨à`}Ñ[Ó’¨¬Ï3N8isSGä&‹ñëö‘ý—õÛi> yºÎ’X %‘À ý©øyãÿ|jð,³uŒ¼"GÇ;HPMxGÅÿ„ú7ÆIáígj‰zHÑšÆÐümû9| ×-´íOÎsˆÀ'ŒçØTU¢éë« §]rT>Hý¥k=cCñ;ü6ømiý£¨ÈL[П•³á¼Nøsû`j¨æìÂÒ 6)ãñ¯×=BÆçH›ì¬ qƒô­(¥QZG.&/ï¡ùkð?öÑø§ð»ÆVÿ~3ذ¶•­ԅ¹û ~µú¡ñ+Kÿ…‘ðÎãWðmÏ•=Ź’9Sû»…~m~Þßlõ7‰¬ãÞÚr²ÏsÖ¾žÿ‚xÞïÆÓIÔfi^ÊÜsœµÏRÝ= &&8Šv™ð_ì£ñ[Ǻ_Å]wÀ?µºµí„ÉýÀ qøšýrÓµ)´éÄжä¦qÅ~EþÔ~ Ô~þÓz/‹tÔ1XݱiàdÈ?€¯Ó½\†ãÁkÒ`¤–ûóÿÍuÑš’³<|Ë *5à|…ûvü}ñ2^èþðªú~¥z91rxlç_c|+ðî¡¢üÓ5¿ß´÷±Z$’É ±Ø3_úKIñ›ö¦ƒSÿ‹}c/_âV¯ÔOÚëâ<ýžï¾Á'ÙäŠ œ`|¢°œ]ÑëÓ”*ÅS©»?9~4~×~:ñ§ŒgøsðŽÉ®e‰š'™7 §#ÐÞ¹[/~غ¯ü% ®]JÈ<Ãkž=qœf½_ö Ð>Ûé:¯|A} êwr‰¹ù³ƒíí_¡“ü\øz¬Ð¾¯Í‘÷¸þUPqžûžu~l4¹iÄùgögý±ïüu©Ÿ…ÿm›¬ÂDq3“—õ?0^äWןÖóEð–¡qm!GcŽzwÍ~=~Õw¾ðïŽÇ~ ¸ŒJ™Lg’K¯_Ê¿`í5‹o‰³rkjCÎÖ!˜õ%¶Œÿ:gÊm_ ªÚ}OÅŸ~ÕŸlüi¯ø/Ež]F{‰¶EÉù8#°>µõÁMß,µíg\¸}>æ]òÄØâ¾dý‹<'i¨|sñMýÊn{+¼G<ŒÿJýÓÓµ‹2áZ!ÎÑÐýiÇÏv*™°’§Ðü„øÙão‹ hNÕµ9eÒuù³÷~òë_©ºêjz%®¦§zÜÀýE|‰ÿðŠkÞ³ø£Å¾ëJ*NÑÏ,ô¯mý޼QÄÿ‚šRÏÅý²o^ùjh×q—,‹Ìp1ÄCš™ûIüD_‡Ÿoµn µÖ–îy®3öñŽüE¦ÝxÆz´—–—ã|û)Æ+ç/ÛwÄ3kþ5Ñþ[Ÿ1nk¨ÿyƾèø!à«oü;Ò4hE$¢à tN*£Ðó)Õ– ¹÷T‘[jl:0¯ñW‡åҼˈ1œœc¥iøgÅÚ8´¹;ô&½>òmRÁã8tW5åJG¬ýž2 õGóÃâ?ÚsÆ þ3jvÒêRL‚LCoÛ©ãŠìuûUþÑ׳kìúƒ±1*C)é÷€¯4ñW›?þÕ÷"æ!$·FAù¿úÕûKáÛhü+gŸ¤¯‘ ¸UxÆ* ¹õG5LJÃÚ-úÆï³—Œ ð×Å;).¬.*ÝH[Ï_»Ö¿\¼ã hV~"Ód‡}ëÅ¿n? h_þ]êW*£PÓÐØõÏùäðO­GT¿øom¥ÜÈg60…byéNWÔŒñøZu"«R>:ý©þ#?ÃÿŽúf»³íÛÍ•CÜ×µêúÏí9ûLX­ç†¬¤Ð4étSFÍÈíÁQ_=þÔþ>"ý£tKI¡ó"70<ƒ‚+÷{àÅݧ…ü'§ø~8„0Šª:qQR›¿4Núx¨¨ÆÁ½KÆŸ´¿ì»â+fñÔ³êzc¸ËÉœ~‚¿c~|c·ø…áM;Åvµ gQÛ5ÌÿÁC<)¤kßµ^u_:Ù7#ô¯”ÿà“Ýü.¶·-Ÿ.<Ö´¥SŸÝg.>Š‚ö°>¶ýª?l7ྞ…¿×5 ÇY îÀôϯ¥~~[ø7öÀý ìSÄÒ_\x~ ¡½#SØôûÀWÑ¿ÿgïj~4²ø“ãír;{M6O5c‘Ž8ëØ×¸Ûëöbðv‰•eâkRö¨D®xÇo»\µ©ò;£ÒÀb•XYŸ•Þ%oÚ§öf»‡\ñL³êúTo󻣿×êìãñ¶/‹¾²ñ%¡ pè Š=yÿ øƒöµý¼<3ñGáÞ¡á X®£o2‘æ¨nqÎx®ƒþ ©Àð6÷Ï–T‘íÖ·¡W£8sL">xè}aûLþاáf‘káÏD.õÍEš(Ð’ìdtÏò¯‡áGí‰ñ¢Ñ'—â?í/û3øžÊÃÇ6“jºuÔ‚13n<sšýø[ñ¼Sá›/Ûƒ™UGóúWU?gÏÚÒ;h¯l5õŒî?9ñÆ@¬¯øfÓá·ƒ¯ßC·Y[ÆÅ ('Šè¡^êÒ<ÌÛË(Î’>qý¨naðÚÞ/ x6ÔjÝá1„RItÒ¾áWí‰ñv&ñ¹¿¸Ò<ÜÈÆ:È•âß5+ÆŸµ&©â\±ØH!)ã!Øq×±¯èk@øåð£û>ÞÚ×Y·…bE]›º`}+šªå•âÏV“Œâ”÷?¼ ûGü[øã ü`³’K{‡ ‡Üyî~î:ZýøuñÓUÓíµ}:_:ÊádE|Iû~ÃðÇæñ—}m>§cºUd?6qOzã¿`Ýø¯áÄzUÓ—6åÀÉ쥱]tê{EiF7 *Rö±?cî ý5äB:t¯Æ/ˆ(#ý¢m#·ÛèůÓm+_›K‰â“&2¸úWæ—Œ “Xý¡­n-†å¶äŸL:šíÁRäR><ÇF»¥ÞçíD1¯Ò¦¨ ŒTõàÏsô:? (¢¤Ð(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùçNÿŠ7öŒÔôóòXøÿKŽò?C¦*UQê`ev=ñÍ} _>~Јú“áߊÊ|ïê¶÷Sccp~ÏtƒêŽ öô :H‹$l9„õqþý*Uû®Wë?ôžS“îÊpó¿ßÿ㨢Šòް¢Š(å¿þŲŸ‹ñwˆ†U·/–·bà°Áîòý±_lkšî‰á"ïÄ$Ô-ô­.Â3-ÅÕÔ«ƽZI…U¤Ð­áŸhß„ÿ´M·ˆ5„š«ë6½ûÅד$0É6Àù„Ⱥ`ýì{d`×£xëÆz?Ãÿ j-׋k÷l_¿,‡ˆâA݈Qîyâ´¥JS’„ÛÑ9¨§'±å¿|A«kWšÁNaÖüNŒ×·)×OÒ”ížsèÏþ®?RO àײøsÃúO…4+ èP k :%†ÇeQŽOrz’y$’y¯’¡ø¡ð»öpÐ.þ)~Òž,°ð犼j~Ù43¹{”·Œb+KkhÃÏ"À¤؇æ<“Á®Ãþ ƒûÝk¶º%ÿ‰ï´u¾Ømîõ *òÚÖUs…píU÷ÝUGR@¯C0«%†¦î£»ï.¯Ñl¼µêÎl<n¬·‚þµðÐz*µå¦£g¡§Î—6·Q¬±KŽHÜnVVXAVkË:Š( Š+㟈ÿ·Ïì«ðË_—ÂZ¯#Ö5øYÑì4[yõY‘ãûèæÕ$H!•œ0ÁÈàÐØÔWÊ_ÿmÙËöÕæðÏÃ/ùÚô´­¦Þ[ËgtcO¼È²ª¬›‹Ëf*9 ÜøÙ¡üuŠßWñÒô½6[§M Å¨3ËoÈÍö‡¸Œ€øÜgšúFŠø/ö'ý¤üañoöM“ã¯Åƺ…„Ú£ÜÿfÙíobI+x²ÎûAÀ\–=+êÏ„Ÿ¼ñÃÀ:gÄ¿†ÚÕ4T?•.Ó«FÅ9#l2:° ©ïЃ@‘EPEPEæ_~+ø[àwÃ|Uñœ¾^•áëW¸ušO»çóHV4ÿi†x ÏŸÛûÆÞ øƒã þÊŸišWX‚[íVþ׿:VrËf<À8ýùðÄ *ƒÄ‹_?~ÈþÐ~ÁU~-ü7ðÅ¢éú5§†M²}ÔŠ%ÒäŒz“°“ÉÉ'š£ðGöhý£þ(ø;Å¿¶‰|qk£jmŸT½ÐåÑÒîyô«W3Ù[Ã{$¡í‘Ö8Œb5?"ÄX±û1_ø²Çþ ¬·~8ñx£Xñg‡ÅÄ—ëkš³èÐ\Ç‚"P4)ˆûå "½JÞþœ¿•µòvkñæ9!¥i.é?Ñþ‡éŸü{ãΡðö[ñµáë“iâË…¦Ê§·Ší4ªG!’Þ9J0û¯´Ö'ü?ö~Ó~~Ì~Ön-$ñ§‡þ"뺽ï‰<(· ¥ßH¤Íh.“˘Fsx9°kòþ Sãñ¡üð‡ÃØ$Ù?ŠuÃpã?~ÛM…‹Œ×Yá?…~¢|(øÕðÇ㆕ªë¿ µÄñ¢ê3iW7C4q ËuF‘#yQªÔ‰#,ŒÚÆ¿ lEãŸüïàwÁ¢ïMð¬v·÷0Ü¥„’j7(æ׶¶ˆP~”»Çë¿:o„`¯Ø§^ŠÚÓJðýµ¯ˆüa†‰-4»(¢¶¸|o²YerÁL€[å •?S~üøqû9|;±ømðÏOš}¯Ï<¼¸ ¸¸¾GǰP¨UÆOÛ'B·ýˆÿn‡µ?ƒlNðŠßÊÖ-í# tßPQ Ëm"Jƒ4ÊÏÔ_¿6wvº…¤ö3%ŵÊ,±H„2:8ܬ¤pA бEPEPEPc€:šùÓá? Ô­ü3iàO I³Ä8¸]©b‰Æn®íŠäŽA ׬xoÃúo…4 ;ÃZ<~U–™vñ/}‘¨PO©=IîrkÖ§ûœ+ŸÚ©¢ÿ ÝüÞŸ&ŽI{õRéϧܵù£nŠ(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€$iPøÛ\ŽÚÏĺÄ1ĺ•ÈHÒ;ÉUUTI€  8€?´øa†ÚííãX¢‰B¢( ªª0€è*JñßÙâúãTøðÏR»•§žïÃ,ÒHìYÝ䲉™™$’rIë^Å@Q@Q@Q@æ>?øÕðáUÍŸÄßèÞŸPF’Þ=Rþ 6™€Ì‚WRÀ#¥ixâ—Ã_Š–7:ŸÃ?éž*³²C<ÚeÜWqÅ!‚;D̰sƒÎ(¼¢Š(¯Ÿ~1뺯ˆµ‚^ Õ¼I“Q¹­†’§lÒû<¿êã÷'§½GÇÞ6Ò>xOPñn´I‚É>H×ïÍ+±Äƒ»;æx¸ƒ~ ÕôM>ÿÆž4ü_âÉïP?óî€b DôHåÇ÷³Éâ½l(¼TÖÚEw—ùGwçeÔäÄ77ì£×Oø?æzŽ¡i^Ñl¼=¢@¶¶|I 1¯EDSÜ“É<žk^Š+Ë”œ›”ž¬êI%dQEHŠ( Š*¦¡c¥X\êšÄv–vq<ÓÍ+Ž(£RÎîÇ€ª$ž  tW€i_µoìÇ®ê–z&‹ñ_Â÷Ú†¡4vöÖðk’K4Ò°HãBY™ˆ É'½þ€ (¢€ (¢€ (¢€ üvý¹¾?¯~6迲τüAa¥XxvÒç_ñL÷—1@“¼vÌlôÄó|Éd2#yk’Yã ~í«ï/Ú·ö—ðoì¹ð“Vñÿˆn`“V0¼Z>šî·÷Ì1*ƒ¸Æ¤†•‡Ý@O\ðçüÇöyø1ñkàTŸ>,éZ7ÄxûV¿Õ5KÝBÞ Ùì¦32, ]XÂØ_4€æOî„®Œ%wJ¬*¯²Óû™j|ðqî|óâ_ø?Sÿ‚©|ñ‚¯lf²›Ã–6’Ca,o”ÒZßÛ5£¬dˆž%eSÃ( 8¯ÐÏø)wÄÛÏ…ÿ±×îô¹L7þ"Ht8XqòêàqÜÛ @÷Å~4üLÕ¼ðöãøuäYè“xjúÚÜÅ[«HšÝÜ/urÊçûw»e¶¨ÉÆ+õƒþ £à kâìu©_x:#ªßYk¬¶ÇÌólâY#–DÛê‘Ìd8þ$t­3:¥^¥5²nÞ ÃTç§>¨ë¿à™_ ìþþǾ ‘`j.I5ûÇï#_ÛŸ µXGàOzûò¿;eÛöj³ý“¼¨kß4]_ ø{OÓu +»È¡¼‚ãN¶H$Uµ-ç>ã1V.Û“Åoþŵ¯‰¿kmk⇈£Òm4ßøwS·°ðô‹«}r…$yžížVq_)Õ`r¤±±áðYÿÂ3û.éþ ·“2×-`tÏÞ¶²Vºsï‰Rξ ñ ïÅâ‡Àاö·±Â bƒLˆÙh· ±jîÀÅ ·×$î9¼OØ# X ì’½‡þ 1ãÿ|Uý·¾|»ÖmCðÖ¡fu™ È!·›R¾ˆO ͪëºîïÇ5ôüá6ãÙËOø¹g4vúÏÃÛøe†]á^[MAÒ cŒäe¼Ï&QƒÀFÇZý[Ñ´m'úMžƒ Ùçiºt)ommn‹0à ˆˆ UÀ¥_#~Å?´žƒûK|ð犗R‚ÙZÇk®Ú+¯Ÿ ôË’GŒªNGš‡¦ÖÆr\ÐEPEPEPYWº‰¨ê¶£§ÛÝ_iNïgq,HòÛ<¨cv…ØŒ²1V*FTx5«E|aûKüø1¯Ã£|:ðÖ©ã߈qéðê7z=Åà@žéæxŒ‡ÈxmÙ_—+ê/ø¿ 'ËûÍ#Ûôº³©ÍíÊöùŸ«ª¯£kÕÌ"©B{jµ—«éòVùÜä÷7*‹Ñ›ü,QEyGXQEWx‡ösýŸ<[¬ÝøÅ_ Ý$²BÎÇ ’kÙh RÇÀžÒü$<¦øwN´ðÀ…íÆ• ¤1Øy2^?³*ˆ¶1bYvàäç­tVvvš}¬V6%µ´ 8¢PˆŠ¼U\èY¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÑýü¢Š(•ݤ7–ÒÚÜ.ø¥XzƒÔWÇ^/ð>µá[þg‚ì–÷AñºÏ! Ï›†Sv[8vÕÁ¸à¸Qö‰Ô~TyÉA‘í^~c•añpä¯êŸÍ8lL©»£…ø{á+ÿxn×÷šÅε”kSÝ„ó¶(dqûÄgÖ»ú`  `S²+½+hŒg'&ÛqQ—9Ås~-ñ&á=óĬ¥Œ{ß`Üìs…DQÕ™ˆU’H¼}cø½­«jz®·†DŸ46vñ\˜ýÑq<Á¼Çþÿ–¨£ ûÕâçyþ/¥í±2´MhaåQÙCäûWœüYñUç‚~xÅšz‰.t»§‰[•2*ü™öÝ‚kŒð?¼Imâ5ðÄ?!µ9Qå³½¶S½ìq‘¹|¶whæ@FåÉ >e=U{߈¾9ð&¿á-û³g5°î´ˆB·Ý<×^1§Š «á¥u-Št}H©˜¿¼!¥ø+ÂÖ8¹½|ûÛ¦Á–îòA™f‘±–flàtU€QñW…t_hW:¹n·6—hU•‡#Ñ”ŽU‡U`räW–|-ø«cªi‘x[Æ·qé4Ñ€¶Ôl®Dí$|y±+¾98eeÈÁ®ƒâ_Æ/ü=Ð忺¿Šæù”‹k(d¸žNÁ#q«Š95üÃŹÆ?„'K’pÑZú»ï~ǽ‡ÂJÑÜÜýžüM«ë¾ ¸Ò¼C?Úµ ê7zD³Ÿ½:Ú>ؤoVhö=Û&½Ùå+Ú¼;à'ƒ5øfÖæYõÜͫ޴| šñ¼Âˆ{ª) |f½¦òo"” •ÀWõ aáí~++úÛSÂÆ(ûi(msÎüYñc@ð¶«€±\jÚ¼Éæ +æu§™ _–$'€Ò2†<‘MðçůëÚ¿ü#·pÜ躹Mék¨D`y”pZÉŽP§¯–ÌGp+Îþ O­ð÷OñUÒ,šŸŠê—²àe¥¹%ü¼õÛ‘÷U@«ÿôk}KÀúŒ«"Ú]éÑ5å¥Ùu­Í¸ó#•I邼ú®Aàšü“â§³Ìc‡§jwIïtög|2è´£Ôú7/žžÕ'5ÆxÄÄ> ÐüOv¢ÝµK k¶B~áš%®}³ë^¨þÐ ìµGÓ"½ºÔ D‰f°°¼¾¶ŒŽ¡§¶ŠHÁÿqßû«Â眒]Þ‡”©I¶’=ÒŠç|;â¯ø¯OM[úŒ:…¤ Aç Ž„Þº AäV‰¦®ŒÚkFQE1Q@Q@Q@Q@5úS©­Ò3òöÐñzìõ6öÿÖ¿L~Ã$> ÓR@0¯ò¯ÍÛ4ø]ºq··?ú}Ùàß]éú6–ó” ƒÓ ¯{*?>˱Q¥«sÓ|y Ûê~¾;@˜DØ5üÒ|=íÿmY­Û¦~O¶+úkÔu+}G÷“BÙQÉüØxÞ=CöéžÖ>úAÈéÆÚò£Q§Ë#ì+Qƒ‹œ:Ÿ¿Þñúdp¤ 4e@®'ö•øáŸ ï¬^Úõ#2FàÁ­k}škX£‚Uå@çÔV括M¦M´óœ=0x5Û[¥£ÀÁfr£S’¦Çà—ìÝñSÅ?³×Åë„~0wà¬fBp8~•ûo}§£µå³[ˆX®~ò׿Ïüwö|k«UøËàX ^ÚÏåNÜœñõ¯Pý‡~9Û|Rð@ðõô»µ‹˜‰ù°€ƒü«UZÒGfeOÞÓÜüÚøËðÆëÆ¿µtºÕ[D7L \Ó.GR@ýkï;_ø%—‰u®Äû¹TVŒ†ê™ûc~ÎÚî»xŸ|j˱ŸŒŸækŸøKÿ×¾é‘xGâÖ–öRX¯—æÍ€NÞséYÖ§ö¢våøÅRÍô sþ cã ?ñpî$†åQü~½_örý‹‡ìñ­Íâñöœò$ƒiûÀç£ZóߊðQ}wâ&Œ|5ðƒF}JòïåI!Гӏ¯^ý–|ñÖÓMüV¹ºj%’ É«dãCZRÕ¥¹ŽaJ­?~”´>'ñtïá¿Û›GÖ¯FØ¥`K7ýg½AÑÙZøƒÃö“®ø©õQ_¶'À-wÄò§ÄBïªØÁbœdÖOÂoø(ö£ðóD·ðÅ}-´ùôõo”[`Ú;ŸJÆ­'xX:ñÄÒåžçéG‹f´ðd2êì¢ÚÎ>¬qÀ¯˜¾-øçÃ~>ø[¬Çàëó{å#nq•ëПJù'ãí_ã_Ú–ø}ð§B–îÚ놸…A&Òº~0GÞîßZñÏüaÓbý”n¤¹¸ ¨éVâ“ó]¨x¯µ|a¥mxbûI#|s); WóÉââû?ŒW_‘äû±vG—ÎÜo-Óð©«tw`±‹¹g¹ú5û ü=8Õ¼s¨G¹õ „±±¶ãúW§~ݺ=æ½ðkS¶‰VLNV¾ÚøIð’Ç ô6Æ! ë-Œžk‘øà‹ønûÚ LŒ2G9?þªè§+ÇSÈÅBT«{F~7~Éÿ°æ§ñÂ3jzæÑÚ `Œ+OMà×ÔŸðJ?ÚTø“y$Fcÿg¯™´Mkã7ìYñ"òòËO¸Ô<9y/˜xÊ£Þ¾Ôƒþ ­àytŸÞ@£SÛþ£#vïLf¹&¹ÑôTë*õ<çþ qªÍ}ºïŽ^vƒm“hèsÝëï½?ÃMð£àôþ‚çí0Û[Ëg®§Ò¿=ÇÅÚcöžø“e¨ø7N¼Ñt›÷ÚŽ 'öÍ~Šø¯JÔt?…Wvä†[¸ "F¼Hº©¨¸Ýž&.5©I^ZŸðNí5uïŠ~>v?ÚÁ÷Á¯ÖmNÆ[ †‚PFZüyÿ‚wëO¦|]ñ“#5ØãÛi¯ÞË;Ù‡\vëšQ¨âÍqT#ZßÌ|±ñCÃéâjz+0L…€<ýÐkàÏØoâ…Çïx¯Ázçú=½¼ÒEs€ÃÍ~ŸêZd¶2Kkv˜RëȯÂ_Û6ÇÅ? ¾!&½áXÝ´&.Å2MV&š·22Ê12…GNGÔ?<-?Ç¿Ú/X×î#2A£Mû³Ûž¥~›]X6˜æÍ×h^ŸJð?ØÂPØx¼W{ËýX+¹#žÿû—ćT…¥@<ÑÜu®|=^W©ÛšàUhóÓÝÓàŽõÜøoÄíf~Íxß»=뎺µ–ÊV‚uÁ½S—>IÀäsšô%5v|Å EJ3?0<3â*Ëö³¼¶»eÙw>âs_®>%Ð$±˜ÝÛ|ÐH{søWó…ñv7í.±àÁ,×63îÇÔóŸé_£:üF_hiáÏŠºA°ÔmcÙûàv‰¯:5ed}v# L#mÏHý°µû} á.£ç8F‘p<ŸÂ¹Oø%ü&ïÁ2ßÝ.Ø®£sÆA¾1øãÏŠ?µÿŠ Ó<=¡M†‘ÆùQ~B¹ëÁö¯Õ¯€Þ·øIàý7@Ó“c[FªvŒt­ç}:5–rHüÙý¤4ÿìÿÚƒJÚ>F¸Èã¥~¯heŽ›nÄà…ù[ñ¯S‹Wý§tng{ŒÃûqá©tÝ: ˜ø¶ƒÇJt%ovFy¥tªSØùOöÊñ­ð'[±¸ù‹ô¯ÿ‚eè±]ü$k´4q?#^ûj²ƒ:¸^£¯7ÿ‚Xk0Áà¦]6“¥MXZMÄêËñ ¥ §Ê?´F¿ã_Œ?SᎣ%••¼ÛdQÀ*Iñé_lü7ý‡¾øf y¼Ecý«)˜±<ŸÌ×ÊŸ¶ÃO‰>2/Åß is_iòÈÊ.FIÏOZô ÁF´í-,®í5TP¦Û‡lfŠU#'i™ãpõiòº[F~Õßþø/à.±q¡hðØÞ˜~LsÅp¿ðK+(u†òE:çå`=º×È?¼Gñ»ö’ðî¡0Ó.t]Í7£ã ('‰íÍ}oÿ·Ö,4Ý.óÁÏ(7V€‡ïdÿrÕƒ‹º=l-xÕ§j›ŸV|K×~Ë«¿‚¼_&üs×ü1k¯>‡snrzIr;‘_gGÿÜñ´ÅñànÆ8üºÏÚ«ösñ÷¯ˆò|iøW§K|%róÇçånzw5‡ ÿÁD´í*Á4ÿX7Pv²Ê9^=MkJ¬6‘ÍŒ…eïÒ5ÿáÖ:ïˆ4×·Ô¾'\ºJ0ñ”R?ô:ûOörý4ÿÙóÃïa¦êͪ9æ /\úë_™Þ:ýµ>0üTºƒÃßtë‹q+àÝ[ñÁïÕúùû'Øøö×áõ«|EÔæ¾Õ¤q3FsëX84ù–ÆôåíaËQêhjPMl“C *Ê9¯Ï»Iö|sŒ÷põÿxWëw‹<;o¨ØM,jª Èë_’2ÛIiñî8d"òýWµ„¬§cáó¬ £Z§í ?pTÕp}*zùéî~“Eû¨(¢Š“@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Å^´ñg†u_ _ÿǾ«k5«œgT+‘î3‘ï^oû?ø‚ï^øY¤Aªñªh~f“z¤å–âÁŒ'w¹UV?ZözùçÀÿñGüsñ·‚ÏÉiâX ñšöóú=çÕ™Õ•êáy†«Kª´—ËGø;üŽJ¾íH˾Ÿªü¿èj(¢¼£¬(¢Šþ0¿oÈ|Û'âÂzë27ý÷7õ¯éçþ ý7Ÿû|(MWþø–Eþ•üÍÁDáò?mOЉë¨Äß÷ݬ-ýkúQÿ‚sÍçþÅ? ßÓOï‹É×úPÛQEQEQE~W~Ó‘IûO~Ù?dy “Á^ µÿ„ÏŰçä»òØ%¥¤Š:®YÕgÜ9A_N~ÝÇìuñb(”"'‡îUUF óÏì½k?ðP?Ú·ÄWx7zxðå„E¾ð…­X¹è§ìèOá_DþÝŸòg¿ÿìuü…Rý?ä;ÿØ?ý ëŽý¡?cm/ã߉õÿ|cñ¶³­ø>ÎÇv‘áKy>çÙ\ÅâW„‡¹¸.…ðS%IdÂŽÇöÿ“6øOÿ`Xÿô7¯¨i=;Pp§¡sHÍ~?ÿÁ+üQ­¯À¯ü2ðD†?x¿Ä†œr,l’δݶ:lS„äÇŽF+÷ÓÁþѼ á;Âz^MŽ›Š1üLz³±îÎijäšö(³ÑößnWQò[9|ö_7Ñu?y>N‹^‹õ#ç¯_²ÂÏŸ¼ ñ[âÝ]Íà"ÆÛM#atYÄ ]#ÆÌê® í  ã $WÊŸðXàHÿd—"ŽíÏí{Ћ*ù«q»3ùÄìË&ìq÷sÎ+ô“âü%ð³ÁzÇÄ/êéZ…n×7WtT^P9gf!QFK1 $ üø5ðÏÅßðPµgÇ}6M;á/†fxüá«•½ã7W+÷YÔ:‰YDy1G‡ñÎÃïØcDñO‡d_…šGŒ£’-RhåI3InŒ ¬ ŠAäc b¾¯ óíûY|-ýším-üV/µ¿jQKqg¡èÐ}¯Ržig1åDp V-#²Œ+c%HWÂÏÛ·àÄŸÙãWý¥/å¹ð·†|?w5…ôz‚©.¢XÝbagóZUš?,/$¶00k´ý­.)ÿgÚ¯ˆl|%ªXÛêûR¤ñ0Š67ùfvV)œgœf¾Fÿ‚gx7Á^ý‚t¿|D¶±}QÔ5?]¶£r[Â,nLipâPULBÑdVê¸ @ wýœ?nŸþÑ¿üCð’Úÿ„ÊŽ T çê}M~ÑEQEWçïí¡û|Aý¯ŸNÐÛâÑð‡ƒtÒ³=^ ¯2ý¢iþÙ|+af“É9 TP“|ø}âO† Ñ>Åâø|#e¥j?ÚRÊÚ_öŒ²Ê‘ðKXxpîñ‰&IÒÔ´Ý|¾f{“sç  g­tapò­R4£»3«QB.O¡Éü?ÿ‹‘ñ_Äeýæ‘áíú‹žU™onW·ÌøXuPE}\Ÿ|!§x ÁúOƒô¡þ¥À±Æ Õä>îä±÷5ÖVÙŽ&5*ûŸ Ñz/óÝù¶F›Œ}íÞ¯×úÐ(¢Šá7 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùçYÿŠ;ö‰Ñ5qòXøïL›M›²ý·O>|.ÇÕ¢fEúWÐÕá´V›xÿÛÅšRoÔ¼ym®[ßìo™A>†ùûש”4ë{'´Óß·ÜìþG.1{œË¦¿vÿîôU +R´ÖtË=cO2Öú牿½ªOâ«õæJ-;3¥;êQE!…_µ,?gý¦þ.Áÿ<ü_¯¯å¨N+û…¯â;ö¾‡Èý«~1'¯‹µÆÿ¾ï¥oë@ׯì©7Ú?eïƒó÷x|Ÿ©ÓàÍ{å|ßûÍçþÊŸÓÂz*ÿßq¯ô¯¤(¢Š(¢Š(¯Î¯|\ø™ûFþØšÖ‰ðë\›EøCðdµ–°öëþÞ×$$}ì¬|˜ Û?)ÉĪWëÏ^9¸øeðGÇßìÿãëÚ£¼ööîñÅÀógükáí·€?cïÌ@}GÅQ˯ßNyyæÔd/¹<–“×m|ëûfÁ×üöW·¹f‰æ½ Ž)bðAà×êvµ©xgá÷†u_Äš~“¤[M}xöð3†Ú3$Ž#…K¹¤áT±è5ùmûcÊB?eOúíyÿ¡­~¸ Œƒ@€¿¶güGö•>ÓüOð[ÁºÃ¯‡zýÁµ±ñ>© .£©0S 6ÐH[ìñ:Uʳ8WC‘_ºÞ ¸º¼ð~…w})žæ{ W–FûÎí–cîO5ù ÿ³øàP?èf_ý"¸¯¿|Yâbx;á7‚&ò¼KâÍ:ÙZuçìrÄ¢âé±Ðã)L±àäbºpxWZ¢¦´îû.­ú#*ÕT#ÌËoü^߉ç]Þø'ÀW ëþ°œ<þ°;Pô.r ñwÃëÛãö¥ñ玽âÛ^ðÖ¡>Ÿ§Çg¡(¼Ôö»•$¿M‚0¬Ò£„fl"õ+ú“á? èþ ðÞŸá]"ÃM‰b‰{:³ìÇ,ǹ$×Î_´ïíñ7àužš~üÖþ*ÝjÌïý—¸Chc*JcŠy }ĨTä)äV¸üTjIFžŽ‹Ó»ó{¿ò#IÅ^[½ÿ¯#àχ?´Wí-û=~ÛºOì“ñÛÅñ|Jðߊ’ìÝVK8­oa[ˆä6ò‚I• R¬Œø:°ö†¿aŸxöý±õ¯ŽŸ¼AöŒÖ†h´½œ¶PÙGo €˜ÞV&Y!‡wîNIiNâ Oߪá:Š+ùâý¾4ßÚŸáˆ~xÃÄŸ5ñÏuû¨lt}i¬´=>ÚÚHEºÁUå|ÊžcÊ mÛNBî`èv¾ ý¸¾+þÒ ¼á{/Ù›Ã_Ûþ"ñF¨4é§63_-„NŸ,äGû¸Æò’`c9Ç›~ß?þ,øÁ~4ñÝßÄûŸ|7ðo†.¯í4ýGµ¿ÔuÄY i{9À6Å„H‘£eÙŽB¬}“öÔ~!Éûü;×>&]^k~"¹Òæ¼i.]¦»žÚYå–È3¹,ìmŒ@9IøóðÏöíø%ðkWøã¥þÑ×ÿˆ<1göíKM—E°‡O–ðÓˆÆPc\•-.ð}‹ûþЗÿµìñ¢|Mñ„6ZÄÒ\ØjPÀ¬-ÚâÙÊ–Œ9'dˆUö’v–+“Œ×å'í³ûf|oøƒáXþøëáv·ð?áïŠ.¢µÕ|AªÙO}<–ÊÂC(‘Ã$¨Þ‹+³7('?°²?ƒþøöyðvð3U]{ÁélÒÛj!·5ä²»4óI »J[td06à|ÿ!Ò´½;ã?ì Ú}œ6¦OC¸Å¦ì]Øc;@Í}Oûok_>xKøíð[U•“áÝÏö†½ •Co«èù_´†bŒê𪖠¤a ·,WÌÿðRßù,ß²wýé^Ÿ_¬×öZ¥Æ™©@—6—q¼3E ’G *ÊÀðCAÅr? ¾!økâ×ÃïüJð|Æ}Ä–q^Û3`:¬«’Ž8tl£ŒðÀŽÕÜ׿'ü&kÏøkâçìùw+O¿jZ}“3”ÎLjè ‘É'ü×5úw@Q@Q@uãƒÿ >'Oiuñ'Á:‹&°VKwÕ´Ûkö…\‚˸ʆ 3ŽkKÁ?>|4ÓçÒ~x_Kð¥Ô¾|¶úM”1I.оc¤ŠÍµ@ÜFpí]ñV‡ð7৉þ8|IÒ¾!|?ð÷‰.žK-RÎ}SJ´½˜EzŒgUyâv ' ؘž¤×Ùv›§húu®‘¤ÚÅeaeAoo,pà j#TU*€ŠðŸÿÅ?ûGxGWéŠt{ý%Ïo2Í–î<û\ÄWÐuêf¾ó§Wù¢¿uþ19pš)C³Ž¿©óÖ§û%~˺Ϋ&·ª|%ðµÍìÌZIG´&F'%œyxf'© “^ÇáxKÁbèž Ñ,t 9[p¶Óí¢µ„6ÈŽ%UÎÇj訯,ê<7Sý˜¿f½oR»ÖuŸ„Þ¿Ô/æ’âææãA°–i¦•‹É$’<%݉,Ä’I$œ×¡x³áç€<{¡EáøgLñ¤‘ØêVP]ڣĥQ–‘RB2 WaEyÏþü#øesuyðÛÁ„î/‘c¸“IÓmldC•YÞ4,9ä^EQEQEQEWüoñ~£áo½—‡NïxŠhô­-ÁûUÑÛæg°‰w>zz×°WÎZü\ÏŽ—Š÷šÃÅ}.ǺIªN¼”{Ę‹êr+ÓÊéGÕš¼`®üû/›·Êç6*O—’;½?Íü‘ìð†àé>Ò¹·ÒàX·cGë$„z»’ÇÜ×[EÁV¬§'9;·«7ŒTRK`¢Š+2‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÒýü¢Š(¢ŠkP[ã¥e_O[•³7‰Øqñ¿ò¯)ø…©ëúψôφþ¼};ûB o5 èÎ%·³‰ÕDG"Y™¶†þAÜqš‡ìãðIfv—ÂV²|Íy,^eë9þ3tÙ›vyÝ¿5ò¼QÅË)*Õ åÙÙ£» …RøÝ®t´<^‹ZXŒÖº.©¥êJ£v-¬îâ–g#¸ERçÐ.k¶‚î+Ût¹…ıÌ+©Ê•<‚Lc½xç‚ïõ? xÒoƒþ*¸›XÑuËiît©îšê‘`Og3Ÿ™ö‡ ŒÙ%r $W+â?†x_Åš'„| âý_Ã^± ä‚Ò)!¹Š²yD$?iŠV1!C`€+óÎ;Ê¡›àašáê%µ¿o—U©éáé*rö2z¹ã§:ïŇúŒ‚]CMÔS¹e'0XDZHÍŽÒ`¹'øN>®Œ¶ß~kâÝSº—Á{˜þ"xX\k×q±þØ7.f»¾²8 åžIÐÈŠq¹@ŠúÏÂÞ%Ñ|[ Yx‡Ã·i{§_F²C2td>ÇЂƒÍ}'†Upï-TèNönþ¯­»gÚp—Ù±ó¿‹üàÏü\Ö´趺ÄvÚ6›4fxÕš&yîÔí|nã±wMø)ð×Á±MqáOÚXÏ:–TŒ4­pFöËc³ÍOà»ó®ü_ø¥ª§0i§JÒ‘½d‚q ÿ€´ø?JõæÔƒ_—øŸ:ølÏšWi«Û]:mò;ðØ™%žÉ~GžþϾ$¸¿ð|¾Õˆ¯ƒndÒ'^rcƒÚCž¾e¹‰õ&½ÚeÜ¡}r+ãoj“|ø­eñy<5¯ˆ´½\ 2?cºoe,bcØ×·Òþ8ñ®‰àŸ ]x¿Z¹YÙÆ\FéXŒ$h?‰œI¯ÛøW<†;/§ˆOegä×sÊÌp’[Çil|»¦ê¾-øaâOxCš ¾)ðΛr&Dµš$¹±{Ðn^ßdì‹" pW ¸µÉ/|iñºõü &‹/…tË«}ºDkË›YY×ɉ w$1²³³}Ü€3ƒ]ÿÂ-Y´ðäþ ñ4×[ñ5Ôº¥ä]âkŒá''˜¡TŒãŒ©ú–xBñáøñ¬X»–ŽûA´–<ÿzÞêàH?*ŸÆ¿È^Är^Érݵ«Ýuìz³n4¤ÖñE¯‹.µü&ƒ6šgˆånü–1‘§ØÁ½à]¸*%%à“pxô•²Òì¬mô½Ú;{Kd¤q DEQ€ª£8ÅyGí·>Ò´O‹V %›À×Mu4;Õk+ˆÌ7(…ˆRûHd‚WHªú_ÇŸ„z†œ/#ñE•»/ Ì¢ÞtaÕ^)6¸`x#Í{~)æ„ÙiÂô¦—K»¯>‡ ƒœ£¹W[Їß<=â½$›x¼My•©ÂŸêçy”‹iŠö‘U ¨ÜçjãêˆÉ+“_ èwó|jñî“}¢Âdðw…®ù¯ÙYc½½Ue†82ä„’îãÁ@?z¾¾ˆ¼t¯´ð능–ƻߥ÷±Íš¥Ì’ß©%Q_vyaEPEPEPEPHÝ)i­Ò„ ü‡ý³?ôå^ÞÜèUöÕž‘qa¡é"å? ¯‰ÿl“ŽºY ·þf¿P4m6 O¶Ì “x'ýÑ^ýjœ´`ÙùÎí±u­º<ÃHÖdÓä8ßðÊzb´4‚ß Çˆ?á8ÓtXWVlæaß7^ÕŸ­è—MçË= I¡k“i©1ž£°®z´UEÌ_ Œ• û:»›â ÛêåâF+Æ.­e³• sÔûWÑ:v¡£n²ÂAÏZÀñ'†¡Ôá2¡däu®zù_,ŽüÇ/hûH#5¶—ªÚ¶—¯Û-匼4mÈ"ºÿ‡¾xBVÔü¤Gg,¹ÞWÕº×+sm-œÍo:•eã>µ±¡ë·Dë´“꽫z´yÕÑååÙŒ¨¾J›‹|(“Ä÷ê#”Æs_+xà'Â_Ü<šÿ‡ãžV?1}ømðsàïoôŸEnàŒ9ù°_]­µå’Å iåÀÀq_:Hާc¬:ûí¼/âY,d× LgÖ´­‡ë›-Ì-ûº¥øe¬¾O2ÞN¹éŠð=kàWÂß}·ÄŽîL䟻ŸÈWÜrÅg«Ùí`9x߈¼=>“)’ ZÓÚ ©û²; *_¼¢ô øeð³áG…e𦉠ŒÊÀŸÏ쾋m©Û˜ÝB°Ú¼+OÔ.4éÅÄàuÅ{vƒ®ÛjÐ.yÅaV+æ‰èàqñ®½]Ïñ7„í'Y4íjÕg0í^(gój)©Ýxf)fvwc'ò¯»µ­ßV·)"øà÷¯Õt«*àÁ"å3Á®šUTÖ§™ŽÃT¡+Áèw?´?èV eỬv€6¨λoA·ÕmÙXaÀë^a5…ÂË Î+Ût ~ V bAÔW5ZNèô°XØW%E©âš–›q¦Ü<äW™Eð£á·ü%PxÆãCŠMNÝ· O\óÏOzú÷\ÐíõhJ3±¯Ô´éô˃ê@ÞºiTŒÕ™äc0Õ0òç†Ç¶øS²¼³H­p¾XÆßJ¥®ør Mh¾IG^:בiº¥Æ•0’!Iɽ¿DÖmµ[udaæc‘\ó¦áª=l*ž&—³«¹ó_‹| ¡ëEôÿiéuàn§×ävß³?À‹}@jmáxšT9'?Ó÷?ˆ|?©åH½ x•ýŒÚuÃÃ:ãž tR”fµ<¬e ¸y{¯CÒ~øÁz%‚ÚøfÆ+Ep f§ñƒìµÛ9VX„ŠAÜŸÞ¯2Óµ ôÉÒX2<^ã¢k–Ú¬ ‚7ã‘XV¤àî¶=L6*˜(Ot|— ü+ðW¯î®¼=¥%ÕÓn•ÇREz–­\iWE9‹¸¯Jñ7†"Ô#7Ãl«ÏëÇ¥ŠKY9—l‹ÛÖº¨Ê3‰äbèÕ£S˜¿ãÏ‹¿ ô[p#¿øw§è:~›cò-ºW Ïô¶‡®Áª@¼€àr=ëÄu ì&0L¡¥°ÔgÓgY£$c®;Öõ¨)Fñ<ì&>tgižÑ¯ønßVŒº²^#¬XO¦oŽpp:÷m ]·Õ REGâÛ뮘B>÷¥sQ¬ãîÈõñ˜(WŠ©Lþ|´7í?t®¡Õ¦~³Iðoà÷‹/ÄÞ+Ðb¹™Û%ÛŽ¿…|êcù<;ñfoˆO©–ó_~ÂúÕõÆU¸cô5Õ )êxõ±³¥Qrô=O ¼ákmá2+X˜cåù⹟xz}6á®-Çîó‘J›Ã~%šÆQopÅ“¦O¥zÛ-¦«k‡GÌ›§-v=i(báýãã¹þxó[‹Ä·ÚbK¨BÛ–CÔúoBÔ¬µ9l¥JŒb¸xjm2c,tdçé\õ•üú|Þ|Œ@ï]"§¬O&•iáêòOb_ˆ_4­^Ò[bÐ^XM÷”úWáxGÀÖ¦×ÂV+aàÿ"¾šÒuk-zËÊ›ñŒóïxr]6S4K˜Û·jšUÒ5Ì0w='¡N¡øšÐè^,µK»gÔ¾õÆ_~È_æÎ©¦ønºÎàÀõ5¡‚¸ÁÁè~ñCDëgvÙ^€šXœ?Ú‰®0RN•M&“Áº&•bú X$V¬6öŒ`{â³¼àxS“U𮞶SÌs#/ñ×Òšöo«[›‹`¤dÞ¼nêÖ[IZÞq¶E= iFQ’åg.•J/G¡ívºg‰´óov‹"È6²0¼wÄ?²§ÀÿN÷·†á–Vþ.çè*=;QŸJœM 3Èî·«Ø“ŠäÄa¹ÑôfeÏ n|¿ið+À_ î økFKXÇÝaÎ+¨‚y-$BÛ6òö¯¢5>ßP€Ã*‚OL׉kºú]Áf3žk£ UlyŽ ¤&枇u¤êº‰,ÛMÕbW.¸e`9ú×™ëß²ÀŸÜ5Þ©á¨e–L’Ý9?…V·¸’ÚUž&+·Ó½{'†¼K¡Á;bQëÞ°Äaìù‘èeyŒd½œÏœíþxáÍÉ>Ñc¶Oá`3Ò»]+R¸ÓgW‡å¦+ß5+u R¨>•ášæ>•rC)òÎpkjT—+83$éOÚAè{¦«§¦4±NÜï_“ÜF_Ú0ú-Ü'ÿZûïOÖ'Òã“c,Žy¯€n.V÷ö*x7Pgþú׆£Ê¥cÈÍ1þÚT“Ý3ö"¸*z†…5xSÜý Š÷PQE&EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEP_<ülÿŠcÄžø¤Ÿ,z.¦4ûæèލ<–wõɰs_CW ñ?Â)ãχÚÿ„Xú•¤‘Åž‚p7BßðŸÂ»òÊñ§^. Ñú=àÎ|U7*mG}תÕÝæŸ|\þ9øeáï\n§µXîsÔ\ÀLSdu¼Fë^—\ØŠ¥RTåºm}Æ´æ¥%³ (¢±,ùwDZgì·ñ'Åú|uðòÃY×õgY.®æy÷ÊÈ‹’@¼*À+ݼ à_ |4ð¦Ÿài‘hÚ”¬–¶gˉ]ÚF ¸“Ë1={×ó—ÿý³?j„ÿµ·>ü?ø{¢x{K:aµ´†;r‘ 6ÖwÃ4EŽd‘$õô¯Ù/ø'ÇÄo|Xý‘< ãψ:¬ºßˆ5ª «Éöù’˜5;¨£ÎÐË*Œ‚€>΢Š(¢Š(¢Š(òëÀ·#á/ü↵fY|fð͆«¦³p%»Ò‘axPô-²+‰ê¢¾“ý»?äÏ~-ÿØëù ämï€.ø«áO|Lø<ÂŠß /†±áæ%T]m*g²rÄ.Ù•€Ä)e Ä+1¯\ñgƒõÚKön¿ðO¬n< ªxÛGû-ý´‚;™´éåJ£cì+´î—àð8OØþLÛá?ýcÿÐÞ±ÿk¯Œ´Ã_ kv¿ ¾§Šô©4—2k÷兽ĿH¶9™g™“ä`¨ráTäÖøû:ü[ø£ø[Á6ÿÿ¶<áˆÅºi [C$Ð ØCt&iîlîÁ+µñZÛOÀñ“§ÝÛ"©%6K4„¬™RÊÁÎ0>ÿ‚“|løÓà| ð_Á/]èž!ñŽ£uÍ…¡IáL–qÄ’ ¬J‡‘ÔÁùºãÖªñû<|-Ÿã”Ÿ´N£§I¨øÐXE§ZÏu3M Œ1o­!|¬/ r—¯8Áy òNm³d¬~ÁN~~ÖŸfðÿ‚~  ðFSTvÔ¬¬Òîü;$VÒÇ=ͼ¥"ŒÊðÆQ‚<ݼ]ÿ”·ðÊøWÃÿ |3 ZAj--M„úZ5¤Hž\b}Uã]ŠÐPÅ~ÜQP3óÓáN›ñOöxÿ‚uj1üW½¸ÑüwáøŽî{‹‹Èïg‚饼žÐ‰ÖI£vâØ°Î¯?ü;Çß~'þÌVÞ8øÉª]kZž§«_+»°’ØÇ寄aT¬ ;WÕ?þ xãÿ‚áßÄdºŸB–îÖîh-nd¶óͬ‚EŠS¢b0Ê}Š•uV¡èz7†tk/øvÊ7KÓaK{[[tX¡†”*""à*¨PÎß¶wÃ_ü_ý—þ!|:ð$ï_ÕìZ@]có¤†hæòƒ¹ „* 2FH×ç7À¯Ùö¸ø§ð»Á¿¿i'¶øyðÁÊƉ¦N’jž +3N‹w4ʑù¾`NF|½ÛdOÛz(3EÑ´Ÿi^ÐlâÓôÝ6í­­ APÄHÑG ª A_ÏÕ·ÂÏø)öûLx«öÒ~éZ‡‰5ß6ÞÈjÚž—vše‰!#†Ô&¥B°¨Œ¾2Fî›ß?ÐÅøŠº/ü[ãÅŸ…ñ“Áöžð_†t_ ¬õ/êÍÍäyÊ©l²vRd†!Ï8”⾕ýž¾7ì}û=Üü>ðÄ÷^<ŸE7ú¼iV—“K™VÝHcVfC3ÎN+Ïb¿^=ðƒxËãÿLj/Šÿ/>Ó¨[†W]2ÂVÒÆ6Rà €ÃjrS$ï (¢€ (¦»¤hÒHÁQA$“€êI ?ø›ñcáÇÁŸ Oã_Š> µðæ gº|osÈH‘Ay\€pˆ¬Ç¸-ö–øc›‡í!¥I{'ƒ4ûKÛæ‘í$†æK{pï2íb&èÉÀ`FqÎ?2þéÖÿðPÏÚ·Æ?¾$cXø[ð¦ðéžÒ%@ö39b.%²®v"Í `w4‘);#ÚDÿløã‡öDø¹ (4𾦪ª0ìt€<ÿáíåðçã æ„žð'ޝtÏÝ¥¥¾¬<;1ÒÕš_%¤’í¢X¢p|ÇÉ µ³ÐŠõÏ‘áßÚÀ¾ ÎȼC§j:4îxöò~¬ ùïþ cÿ&3ðïþºk?úu»®OöÔøKñ¯â^%ÕüQ⻄öÖöÖ:flÃSio¶[ÜÝ\Üɦ"î#TÊ27Π©-êdÚ×Tÿ™8ý饸ØäÆéO›µŸÜÿÈûßÁ~|K“Wá÷ˆ¬¼Cýƒsö;æ±™gH.6î1³¦T°œއšïkñ[þ’ |ø€Ýψ~V‘WíMygYŸªêÚV…§O«ëw°éö6ª^k‹‰cQÕÜ…QîM|ãÿ ©û$]tAñwÃFåΔ&ôÿ^ÊÿÇëŒý´?dEý¯¼=á Ýø™¼9§øU÷a-Ìíu O-¢Sæ ¶“µÈlá¯"ýµ¿eï٧ñ¿–ÇÁZ7‡¿áÒãJ½·´Š¨náÇ¿hJæy6ÆûÙ¼ÂÙl¶ }ýâÏiþð]÷Žc³¼×¬ì­¾Ô°i뫘ÎÑFs)`ržE|…¤ÿÁA¾ß|IðÇÂk¾1ð¿ˆ<_s®¹¡É§,3ùjÿ¾pÛpX)Åyïü[TñV§ûhŸð’¼²ÛÚjz¾˜ò’Ù±ŽA€¤ÿÌeE·¦+Â?nùHÏì¹ÿ_¿úp ÒþÔÿ>üVƒàϼK†¼EykÕ©ÔQí¬®Vfe Ü€@\ÁRàä€2rЀ‚29¼‡ãÀ߇Ÿ´'ÃÝKáÏÄ}6;Û+èaŸb›‹)ÈÂ\[HÀ˜åC‚à•RAø+þ Åñ“ÆöúÇÄØóâî¦u/ü!º{}6êPD×zDRä’ËòÊÏ—*.NÚýT¢Š(¯œ¼?ÿ3ã~§â·ýæ…ðõdÒ¬;¤š¤àÉG¼Iˆ¾§"»ïŒ7ŸÀ>¾Õ´Ô󵋲–:d#––úèùp¨® ÜGp¦´~ø‡~Ò¼)ùÓÛG¾ên¦k©Nù¤$òw98Ï8Àí^¶÷8yVë/uz}§÷Y|ßc’¯¿QC¢ÕþŸçò;ê(¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š*¥ý®§cq¦ÞÆ%·»â•FIVê [¢šm; hðOÙÒúêOàNC%ÿ‚oî´iKuhíß08Ý12€{â½î¾y±ÿŠ7ö¿²?%Ä ).£ô7úYòäUð2±=ñÍ} ^žn“­íVÓJ_~ÿt®¾G. Úìé÷møX(¢ŠòΠ¯3¾ø-ðsSÔnuKÀš Ýýä4÷iv²M,ŽrÎîÑ–fcÉ$’Mze ¶çÆoŒºí_ñGAÒ|y¯ØéÖšÝÂAm©u1FpB¤k UQž ëÃOÓ´ý&Æ 3J¶ŠÊÎÕ8`…8£EUDPT®WÊß°þ³©x‡öJø[¬ës_ß\è°§žF–YRÎîK18䓚ú¦€ (¢€ (¢€>dý´4Û[öKø»gj¤ÔåFIÀÒ‘p¦£ýŠukMköGøCydÊÑÇá2Ø•éæZÀ°H>¡ã û澎Ö4› {I½ÐõX„öZŒ[OèñL¥OÕIù›ÿõ×õ„:·Žÿa¿NS[ømq{¡<£aÔt éLÉ4c£m’Mh^¨ØäloùHGì©ÿ]¯?ô5¯×üqý»/é—º‡ÃOiÞ*±´“ì÷é·)p‘ÈË»c4díb§8ëŠü•ÿ‚Ûjúd< =ÔcQŸ_k”·Ü<Ɔ+IQä œí "‚qŒ+ô?ö[ðü—¾Óþ-ë“Ç{­xÊÂÒdxÜI¶œ±/Ùm£aÆaŸ\ó’3_i¿ðOoÙâgÅ­@é“þ? ´¾µZfÔµ&²IxÌ#¶x¡R\㕯¯~~Èß³ßìÕy©xïà·‚nlu˜ì."Aª_Ü›„;d0¤W·oy5 Ìø€&½ª¦.“µä•û¥½Ÿ®¯O4sG–£SíøÐè~~Ô¿þ*ü\ñ‡ÁÞ]KâŸ×RŠkgŠ%û<âÞCÃ#‘ÔÖ/ì×û^ü'ý¨Ç‰m¼ö½?Sð­ãZÝØjK WE qE,¡¡f ¡²e ¨Êçæ¿Ø#öløƒáüGý©~3iÿØ/ø¯yqq‹»tšmÍÃ]œã‰Š QPnùتzÿŠà±÷ŠõÙüM7€—JÕ.K4“iW׺p%ó»÷vÓ¤C9ç ÍyÇIù»ûSÙé^2ÿ‚±ü ´øAåÜø‡M“E—Ä2Ya¼¶²»–[´þ4°U“÷6!Á¯×¿µ/Â?Ùª_ ÅñRòêÐø¶ymìM½³Ü)x<°ûÊýÀ<ÕäûúUÏ‚Ÿ²ßÀOÙà]Ið‹ÂÚ%Ýò츼g–êòTÎâ­spòK°žJ 8ñ¯íeû<üIý­j‡>¿ÐäѾü5_ímGZ™“n§-ëÆÍih€±,¸F,ÍÎÌ1å ?Rkñþ ¬h÷·—ìËáÿßA§h5Ŷ©<÷R,6è­¨«Iæ;ª¤Z¨$‘Å~ßWÍßdÙÿöÖt]{ã…Æ»{ Ç$6Î.®mu#(ÿf–=ë¸dÎ2qÔä⿈~2ø(ŸÄ[oŸ ÞY~xRú¿ø‰$ÌöÌ$‡K±rôÞ<ƒÙ×åXÌ¿_þÐ_µ?ÂÙ7þm3Çö—vÚgŠîÿ³-f±Š²Ø$R™.ÉHcYýÚ¹ § Àèø3Â_¼9gáèöº‹§®Ë{;8–cN'–'’I$’I®'â÷À„ô[ü^𽯉l¬ÙÞÜO½$¤9Šh™$Œ°%g=(Ëlýsáº~É?o¼uui.…¨è‰jÒ::Ow,,l|ƒÈi þ[DW8`p3_+ÿÁôèß²L×~!ŽH¬õßÝéb@Flü›xK¦…§Ž\{ä÷¯eÓ?àš?±ž™}kx| %ôV ºÞÖ÷UÔn­#9ÏKpÈÃÔ0`{Šûc„<>v%¾¢èÖÄíEX-­m­Ó<(¤q¢ôJü§ÿ‚–ÿÉfý“¿ìx‹ÿJôúýq¯ÃÏÛ§ãÁÿ‹¿?eË…Þ2Ò¼U.™ãkgº]6î;“ Ëwb¸ŽÐÅN3׿P¿jŽú7ìãðKÄŸuGF»²€Á¦[?[½JpVÖQÉËüÏŽB+7@häoØ!×VøçûXx¦Ðî±¼ñËZDëþ­žÉ®Êž‡;ÔŸ¨=ëôê¾0ýþk_?gJñxqâÏO7ˆu±(Ä‹}¨ícŽÏK?ûjØâ¾Ï Š( ¾;ý«¿k­ öp·Ðü-¡éxÏâOŒf[m ÃÖ®YÞFòÖi˜Rÿ(8˶Bð“ìJüTý‡Ø~Ò¿·ÆÿÚ{Ä‘¤ðøJEÐôäþÏ$ÉeqolCÔÎýrM~|»ý£­¾k>!ý£N‰Š'iï-ltTÛéöÂÙo$Žï溺³3aÎ0ÿ±WÄßÛköÁøoªüF_Œšg…cÒµ‰4¦µ>µ½2yPA9IçC·"m»vœc9çöÅ?ò,jÿõçqÿ¢Ú¿%?àŠ_òl¾.ÿ±¾ëÿMö4öçíOã_ |?ÓüâíkR‚ÖûH×­î ¶2(º»¶Dqw¼D†ˆ‰f çŠñÿØëöðµý°~#øëAðç…ßCðß…­­f³¸º˜=íÉžGBÒÆ™Ž.!UŸÞ=½ÃãÏìïðÃâjÿõ?[ßxëLÐnl4ÝBMÍ$¢I‘cBv.Ì7…Þ™C`.ÿà“ÅÛöý¢fx„|ñL¬··N ÀXb½J¾þþY5òvkñæ9a¥i.é?Ñþ‡ïxOíû@øGöeøW¨|Xñ­æ¡§ÙK ¸‚ÅQ¦’k†Ùc¢…ÏÞ$ð:x¯v®OƾðOÄ O ü@Ь¼G£Êé#ÙêñÜÀÏÊ1Ž@ÊJžAÇåGçÿ¶Gí•yðÜ|rÐÿfø¿á­¨ˆ§×ãþÕ—O ¼\, u>}¾[6Þ@#ýCû0~Ôý¯~]xóáúKáýNÝåÓîín•g{ ñ¸#Vh¾udl.ñB° |çûMÿÁEÿgŸ†^ñÇzÂx›ÇÞTú5–—c‹{[Æ n¾|ò"Û¬p¿ÞUfn6à ‘³ÿÌý—¼Kû5ü»“ÇÂ|Gã[¤Ô§‚ÞTž;[uˆ%¼^llÑÈØ,ìÈJüûA;wkŸ‹¿¶¯ìŒžÔ5‹Úo‹àñn¨l$ðÅ­‰cØÌÛ¼Ù÷n Žƒs_~Ò>$ý¨ü tßüÐôèšL¶µá릒 JíA ²XιA"*°ØÊKg…vÚÀ_ðYùüÿ±Žoý*ý¦ ž?fÚWá÷íGðâøF·ž&ò5-6vjÓ®Àù¢” õG¼ðC*ý_ж.?fø+3øoB-ü/ñÛJK©­¢#ŠîA1…Íݬ‡ØNÕûU@Q@Q@cñÆóxÀ—ÚÆœžv¯rRËM„ ´××GË…@=pNâ;…5áwáøwà]+ªþuÅ´{î¦êfº”ïšBO's“ŒóŒÕæ/ÿ7ã²Åþ³@øhÛºK¬Ý¯Ê=ÙâçÕ\×ѵëbÿsB:¿z_?…|–¿öñÉGߨêtZ/×ñÓäQEy'XQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¦8$qO¢€>nø‰­|Xðÿ‰u,C£ëv2iRÝ9Äv÷K –Ü;t]ιÙnbèG#ë_-kçÄŸXñÏ„<»¿ É›OF¹”ı»ìeÁì~ômò±Î0ÄWПÄé[kz޹eobƒ&g¸@„c<ò}ÎkÁQï¿hOØ_¤Úü;Ñf™Æu›˜þáòÛìè~a¸ì:pøÌ1x ßZƒqŠÞý|¼îk{¼•»æzoÀÍ'QÒ~Å«Áåjþ'¹ŸXÔ20Âk·.©Ž¿»Œ"sýß\×± Q¢*a=*jðóìö¾aˆxŒD®ö^HåkÞo¹Èø³Ã:w‰ô[Í'V¶[»k¨ž)#~ŽŽ0G_Ë¥|] ¿Ã¯iÇmNãRð†ŒR?ÞÎìQ\n>Y¼ÂäÏ€‘»’  €Z¾ý##ƒ«øwJÖ­e³Ô­Òâ Æ×I20ô*ÀƒùW^MÄup°©‡»äš³¶ÿ#ªh¥Ë?øb厣imÕŒÑÜ[Ρã’6 ާTŒ‚èE|×ñ{ÄvŸ |_áO‰“ßÇoœòÙ]ÀÌ—6W|ß-~ó´R"Iµ2p¤ã®ÜþÊÿ –i%ФԼ>&%¤M¿šÞ&'“û½ÌƒþuÞøðÃÁ÷««Ùé_nÕ†ûmüwq¹zò–ÚÝ»2ŒÇ €ÄGF¤¥%Ó–ß{¿â\8ß[§Ðä4û|u×í|Qâí>] Áº\Âm;I¸â{éî\Þ'@ªyHŽyŸ{?ü7áÉ4)ï.´ËIïîç³µIä‚7”5ÅÄpðÌ èÆ¾ŠŠ5$W‡üQPÖü¢ÿ‡‰4ܯ\¥«5Ûþ‡õ¯O â³lÞ•J®×’ÑtW#Ôn¢¬’>¥¶·ŽÚ5†ت1Ê­(Ú1J)kúÁäÞáEP ¢Š(¢Š(¢Š(¢Š(¦·Ju5†E4)l~DþÙ,Ç=3?óÂßùšýKðŒðÉáÝ=õô?ìŠü±ý³”ÿÂî°ppVÚßùšûgÂÚÆ¡g¢iŽ’t…r=xîbaz±ùî¨cj³è­GLƒR·hg\œpkÄ5½ãIŒšõ]źŠ,R¶Ù}ëkSÓmµ8 s(9ó£RTåf}>/Oi ÏÑõÛ­.@U³¥{f“«A©Û«ÆFqȯÖô[&r¥IŒô5¬O¥N¯Ìdó]uðñ”y¢y8LdðóäžÇ«ø›Ã±j04Ñ®$QšñKˆg¶• ~úIÕ­µKu’3–Ç"¹ŸøZ+Äk›eăž+—vG~a€Eí`y–‘«O¥\,‘1òûŠ÷+U¶Õíƒ!‘ȯžå†[ghf\qÍji:­Î•:´gjÍtW ¦¹¢yxÂt'É-ŽóÅ> öÌ|ÝHµå쎉2®µô&‘«ZêÖÛ•ƒ„W⟠n&òÈ{+žgvGva—F¢öÔŒ_ øì¤·gä<jõy"´Õmv°¬+ç7FF*Ãuö5ÚøcÄÒÙH-.ÎPôjºÔ/ïDÃ.̹}Ê›¼CáÙô›ƒ4C0ðÔ&Ó§ÂØé_@K®­jQ°è¼WÄ:Ú]É`?rzQJ¯7»!ãð.ÛS=gB×mõ[uË ÕgG·Ôí™ üýx>¨Ï§Ü ­ÏC^á¡k°j(ÝûÐ9…j.š'¡€ÇCgSsÄu>}6á­¤àÓ,/æÓçBØÇZ÷sC¶ÕaaæŽ^¨iói÷ ë´‡ÖºhÔU™äcps¡>xl{n¯Ûê*“‰æ¥×4;}VÝ”¯ï1Á¯ ±¿¸Ón±Ÿ¥{~ƒ¯Ûê°*îĪ9ÏZ‹ƒº=Œ6ãÉPñMGNŸL •O^¤Q¦êSéwh㸯tÖ4K]Z²¯Í޽xŽ­¥Í¥Ü43)Uþ­éURVg“‹ËçF|ðØöÝX‡W¶/ÞEU×ô }VBâ@85ãÚ6±q¤N¯Âw÷¯qÒuk}RÝe‰²qÈ®z´Üâ{ï­I§j3i³­Ä qžE{N¿áø5HKÄ pkÄïleÓçk{…ÆZê£5-Ï ž§e‚vÌGŠæµ ôÙÌ®1ÐÕ1†Êô=z£ÆÈùªUªPÏ£Ý-µ[^Ì®+Ç|CáÉôÙšhFc5/†|K.Ÿ"ÛÝ6cñKÂÂÒí²¨­BÏš&¸tjGØÖØâolfÓæ0È~”ý;Q¹Ó&YbcÁçé^ͭ薚űš,o#*ExµýŒú}Á†e+Öµ£YIrÈåÄá'B\ËcÜô=rßVX?¥hj:t„ ÀGZð ;R¸ÓfÛœz×¶hšý®¥àÊ:Žù®:ô4Ow˜B¼}MÏ$×t9´©ÏÆOV%½Ì¶ò‰¢8e=«èBÂBÝ¡•A$q^#®èSi>(OÓC¤¬Ï+1ËeIóÓØô¯ ø’=F5·œâQë]§¦[êp¥PN85óÕ¥Ô¶3,ПœW¶x{Ä1jP¬r%« Ô\_2=¿‘öu7<ƒÄZ4ÚHœ8&2 ~qÁ#GñÊþõÜú¯Ø¿ij:lñH9*pkñïSH´ÏÑÁ'éÿ}­z —„®|¿`½jn;6~ÏC÷EMPA÷O^÷?E£ð ¢Š*MŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ž~Å)ñâÃWù [Ô×lã6ú̪ƒ²Ç2‘õ5ô5|óñ+þ)/‹¾ø„Ÿ%¾¥$¾¾oU¼m¦}së_CW«š{îÿ™+ú­ßküÎL.œÔû?Áê¿ËäQEyGYü‰ÁTáòÿn??üõ‡GoËL¶_é_»_ðJ‰¼ÏØÀ©ÿ<®5…üõ+†þµøsÿc‡Ëý¶ÚNº~’§t1{<¿ë$õ$pE}^¦>j”V5“ï/òŽËÎï©Ë‡NoÚˮޟðÈ(¢ŠòN°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯ý£5™ü9û>|Nñ¨->™ájæ0½wÃe+®?^Ë\—ü.ž7ð'ˆü#*&¿¦Þi囕ꈓŽÃw4ù½ÿzÒíì?cô»‡õ-R¸“w(ŠŸ}±Â¾±ý´ÿäÒ~/رªé;WÃ_ðGÏÜi? >"|ñ&ÏÄßüI9»·só"](ˆ®=R{iƒcÔz×Ü¿¶ŸüšOÅïû5Oý'jñOø%ü˜Ïÿúé¬ÿéÖî¾ÀøÅ£'ˆ>ø»IuÞfÒîÊúh‘3Æ×ÇÿðKù1Ÿ‡õÓYÿÓ­ÝzWí5ûGjß ,µ?xkáW|}­jT’ZÍ h³^é‹,âH’9îÓ!JîuÌ©ÁÍkB³§8ÔŽéßî"¤¢âúŸ$ÿÁ#ô_øG|ñ{E µ-|_ ŒÓ3m'þ:E~ºWáÏìkñ³ÅßÄš/Œ~øó[ofê73èzÅëXê h¶÷·‘-¢‘Àåˆl•ý“ÿý¤> þÍ_4¿ˆß ¥¶µÖoõ»; ·öþp0Ïoq+¯–JáÁˆ}0Euf´UÝ9wI#ryàaW   ýÒð_ƒ|3ðóÂzO¼a™¢hvÑÚZ[G÷cŠ!€2rI=Y‰%‰$’I5ùûpÿÊFeÏúøµÿÓ…}wÿÿñgí 㟅^*ñ_í'§êšGˆõßËgcªYK`ÖºsÁlñES¢Iä$*ÆNz¸käOÛ‡þR3û.×ůþœ(öš¿4¹¿áÿ‚Öj°Ù—ÄúYÂô]šDRe±êm”ýH¯Û:üPýš#—ãŸü+ã?Æý=þðE³hÉ>w+]¤qiÈô"Amq #¶=hö¾Š+ƒø›ã{‡~Õ|Y2yÒÚE‹xz™®d;!ŒÉÜäŽq“Úµ¡FU&©Á]·dLæ£'²<¹¿âæüv þ³@øh™=Ò]fíxö?g‹ñW5ômywÁß\xÀ–zf¨þv³zϪLyio®™3:à€÷ +Ôk³3­TTé¿v ËÏ»ù»¿™† Gš[½_ù|–EWœt…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@>~бɢèšÄëU&j¶÷’•f²¾ÏtƒêŽ öZú9#š5–&Ž+AäkÅŸŠ¼7ªxgPèÚ­¬Ö²qœ,ÈP‘î3‘ï^gû?k÷šßÂÝ*ÓU8Õ4.‘z¤ä¬ú{NO©UV?ZõgûÌ}`íò–«îiýç"÷k5üËñ_ð-÷ÓEW”u…_ðPX|Û7âºzêÛ¿ï¸coë_Ù­ßðQÈ|Ûcâšzß[7ý÷eZþ•¿àž³yÿ±‡Â—ôÒ™ï‰å_é_e×ÄðMé¼ÿØ—ákúY]¯ýñ}p¿Ò¾ß Š( Š( ¼oÅ¿~xÓâ…~4k|±øÇÁ©$Vö×ÛÉäKðÌ#`&ˆîo‘Á{cïöJ(´öv—D˜R½7¨l~u⿼S¡ÙX|>ø~©‹<^íohQ@°ûûÇÛÑbOº{¶0¯Xñ'ˆtŸ è7þ%×gÖtM4Î{*Ž€w' I kÇ> x{VÖ.õŒÞ4€Ã®x¡líß“§éJwAôgÿY'«5êeô£ñ5Ôv]åÑz-ß–Qˈ“mRŽïð_Ö‹þê^ðnðÿº„´$+kaÝÍ÷åsËÈç»;ÇÜúWYEçU«)ÉÎníêΈÅE$¶ (¢  ¢Š(¢Š(¢Š(¤eVR¬2„RÑ@WKÓƒ¥¤*ÊrA~埾|4ø»â_ø§â&«sàkÆÔ4ÈZâUµK£´¬²Û«åd( S·œpH>ÃEQEQE!É)Áìkñ3þ¸ÏÂ4]CVË^µLz’b‘9ÿ£×í¥~&ü/h?aïø(‡Œ¼âúWÃŸŽØºÑodZI¥óRsò¯—,ÓÀr³a[4û+âŸù5úó¸ÿÑm_’ŸðE/ù6_Øßuÿ¦ûýkñOü‹¿ýyÜ趯ÉOø"—ü›/‹¿ìoºÿÓ}~ò«)V‚B+ñÇþ ïá¸<5ûT|r†Ø††êÃKu+È&‹›gÇü #Ÿs_¢_¾ürñõ¾mðoâºü0XRu½ì+}fK¯3g–Q®&É1€ÿtÛ³ÆÑŸÎ_…ÿ±Æß…ÿ¤ðÇÃïRè%Ó4sqsªËáø¯×Y´½»yšÚâí• ÷;6âANAõ0~öµ>Ö—ÜíùIœµ´© z¯½_ô?bµKNÒ,¥Ôµk¨¬­!É4î±Æ€œ ÌÄÉ“\Ÿ‰~'|7ðo†áñ—‹|U¥hºÄ‹Z…åì0Z<¨³Há §œJøþ 1âK¿ þŲü'ñNª|YñÇ'LÑl­Ÿ“>­ÌO,Vq6 ¨NÕ,™XUý#ö??`ÿ~Ì_5{SÑ¢‚þi¬ü¹š ÒóMä°qµÒ?´!HÉ^|³¨ú;âçìƒû9|kðÖ«£ø«ÀÚDwZªHÃU³³‚ßP†i"xîcU°c¼†b®~ø`H¯Îø"×O—~ýÀqœq_Z~Í?³GÃÙgáÔ>E,‰,¦æúúè«]^ܲ…2HT*€ ˆ *Ü–bù»ÿÿ/Á_ûæÿÐ"¯ÚjüYÿ‚ÈÈà¯ýŒsèWìõåݦŸi=ýüÉmml,²ÈÁ8Ðnff8@$ð~)~Ú+.¡ÿ;ýštÝ,쾂; ‰Xu6ãQ¸fM‘È?ýµ¯ÅOÙºÝÿl?ø('‹¿k+h¦ŸáßÛìo]HŒ‘ÜܬFcÝ÷—Ü\Õ ±nšý« Š( ¸?‰ž7¶øwàm[ų§›%œX·‡©šæC²ÀÎ@8ç=«¼¯œ¼Kÿ/ãf•àäýæ‡àWÔ{«ê2‚,¡>ñ®eôìkÐËpñKÔøc«ô]>nÉy³ŸQÆ6ŽïEýynw<sàOYéÚ«yÚÕû=þ©1奾º;æ$Ž»Ip¢½JŠ+—ˆ•Z’©=Û¹­*jQ[ ¢Š+Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÔýü¢Š(¢Š(®WÅþ°ñ_†µ/ êQ‰muKy-äìÈ¥Iãjø-â;ÝsÁÃK×AMoÃsˤ_†êÓÙ·–$¢T àÿµí^«ø{C×­ÚÛV³Šîá–HÕÔýCã^YâÈ.~|q°×˜„ðÿb[+Ž~Xõ[e>Cô3ÅòVPlýcµû UÇ ÁÏ=+ù—6à:±Çâ}EÁs+õ¿D}±vQœzžað/á&™~5+? i‘ÜŽC‹Hsõ/…zÄE Kh¨ª0Œ@*gij±(Å 9¯Íó,Cœìª9¤–®ûýçC«9|L03œsKEæ`QHN)ÜÒ«éIº—pÆh»z ŠfFÎNŒ×Îó™¼CûCø#J-e©jó 3lµ‡?÷Ûã>ø¯mÖ/8ñIù¾•åßã]â‡|Z IƒÙè–Î:bÙ óàÿ×iJŸ÷E~¡áf^êfp“_ lÒ¯¹‡œ»ŸUQEFÒ$ÕýDÚ[Ÿ$IEsšGŠ´msPÔ´Í6a5Æ‘:Ûܨ ùr4k(ì¸üxê ttÆÓ[…Q@‚Š( Š( Š( ˜ý>šÃ"šò+öËA'Ç5 À6öàþm_r¦-‡‡ôÉc‘ BHíÀ¯†?l“Žšgýp·þf¿R|=i ç…´ø¥\†·ÿAîU¬ãJ瘶Å×¹âM%¼¨Ð± ¼æ½KÞ/YJÚ߀×)â/Ë¥ÊÒD3wô®L¸`p{TÊ0¨¯Ôè£Z®V>‹Ô,-µkb†¡¯ÖôIôyŒes=kÃ~*žÍ…µëe;dפÜÛYkvg6áÁ®(9S—‘ìN4ñp¼>#Ä4b}"exÉ)žE{–•ªÛê–êèAb9âzÞ‹q£ÎÛÔ˜z‹IÕî´©ÖTcå“ÒºkRU2<ü6XzžÎ®Ç¦x›ÃÞ©¹·\8ä⼆x%‰Ú †Ò¦¾ƒÒu[}VÙdB Ç"¹_ød]Æn­Fu°£ˆq|¬ìÌ2õQ{ZG›é½Æ“p²DNÞã5íúV©o«[‚$ŽE|÷"‰ŽA†SƒZúF­q¥Ü+#ü‡µtV¡Ì®?˜:RäžÇu⯠îÝ}d¹=JŠòéÑŠ7~•ô.“«[jöÀ©‘ó â¼QáL–½³^{XQ¯gË3¿0ÀFqö´LO xšKÖäåzõ+»[MjÏiÃùà¡¶7Êâ»o øžK'[{¶ù:UÖ¡ö¢aÌWðêldkº úLìU Œž¢³4ëùôù„ð±ïòAiªÛa”:¸ë^;â/Í¥Ìdˆf§J²’åc0ƒö”v=GA×`Õ`S$G­?]Эõ[v  8ðû éôé’æÙÎÜò+ÛôrßUNáæÈ¬*Òp|ÈïÀãa^>ʦçˆj}Æ3C2à†›a6Ÿ:Í ë^ã®èPj°7Êú׈j:tÚmÁ†ápsô®ªUTÕ™äc°S¡>hì{f¯Aª@H*æ±£Ûj¶í«Éè{׃X_\鳬±1àçíº½« q(ê+’µt{ tkÃ’gêšUÆ—;C*ü£¡ö¥Ñõ‹"á^&% äW·ë<¥³Fê7v5áÚ¾—>‘9†e;C]TêÆq³Üò±˜П==uÒu[}N’6ù‡QYž!ðü¤ váÇC^A¤k:UÂȬLg¨¯qÒ5h5Kq,L Ç"¹'MÁݾ L9'¹àÖ3éó˜fR¡MM§ê3鳋ˆXãÒ½£_ðü:¬ …ÃŽ†¼NúÎm:v·¸R1Ò»(ÖRV‘âcps£>hì{®‡¬Ã«[ §5‡âŸ ¥ä-sj J¼ð:טèúµÆ—r&%Q^á¤jöú¬¢`[Šä« sDö°˜¸baÉ=ÏŸ§†KiŒ2Œ0õ­í _ŸJ¸Ts˜Ïé^…â G{¸·\H9â¼vhÞSÊC-wSš«u<l8/Ž¢¹ã7MÙ“§L9¡¹ä~£>›t$‰ŽQ^Û ëÐê° œH"¼CP²ŸNœÃ2‘ކŸ§êW:lë ‚+ÎüGáÉt¹ZxFb<Ö™¨Ï¦Ý,ð±>¢ºeOs#ÈÃמ|•> ñ'†äÓÍ —‰?JäÜ^õ¦ê6^ ²Úø,F>µæ~$ðܺd†h†`'¡§Á¨[˜¦\šñ-oÃ×T¥€Ì'<×E*êk–G™˜à{Hž‘¥x†-ON‘]¿xšüƒøˆÌ¿´e¨ßÛèůÑX¯.,ÃÉnON­~møÆY§ø÷c,Ãoí¿ôb×f…¹—‘âføçSÙ)osöòßî¥OPÃ÷éSWÏIêÏÒ(«AQHÔ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠòoŽ^¸ñwÂÍM°ÈÔ-áûm›/ß[›6ŷЖM¿užñM¿¼¢x¶Ûu[Hn ŽŠî£zÀ[*~•Ö×Ï?⛟Æ_ dùG„õiÑ?»§ê9¹·èYÁúW«K÷˜IG¬þOGøòœ’÷k'Ý[æµ_©ô5Q^QÖ|Ãñ;ö3ý™~2øÂüMð-¶½¯ÜÇR]Kqt…’؃dS"|ª1÷~µë¿ ¾ü?ø5á8| ðËF‹AЭä’híagtY&mÎÙ‘™¾cÏZü+ÿ‚—þØŸ´¿ÀÿÚQü ð»Æóè Ñìn–Ú+kI–S vß,.üínÇ¥~†Á2þ0üEøßû4ÿÂiñG[—_×Z¾µ72¤hÞTK"â%EÀÜ{PèMQ@Q@Q@|Ïâ’~4üH_‡¶ÿ¼ðƒåŽç[q÷//ÇÍ–z2Ç÷åŒáNÝ|añÞ¡á-×E𪠟øš_°é0q+žwâ8ïbF:Á®á¿€ôðÍ‹›‰St·W/þ²êêSºiœœ’]½IÀÀÏìa?Ùé}eüOHþ²ùl¼õû'_ÞOÙô[þ‹üü½NèRÑExç`QEQEQEQEQEQEQEQEQEù©ñÓöWø¥àŸŽÐ~ײ´.¸G‹Äž½˜ÛÙëÐ0(ü"NÛT̪]VL‡ $õo‹:÷>6~Ãÿ/n¼«xgÅzׇµ{OøGgí7ÂéHBD°‚Ó,Œ¹ˆ…Ô«Áö­ùÿüø·â?„¾þÏÞ9øAñO×b½¹¶šõü3tš]¿öŽ£4±Ë5Ì›6D‰2™\®<_®Q@>xKþ)ÿÚÇâ?éºv³ö ÖsîHRkãgì«àïÚâG‚#xSá/ŽôOøNöÒ-Fë\ðíÝŒP¶y¦F|:,j½Y˜_ºPÂ_µ?Œÿj¯ê³üýš¼-œºÕ’}·Æú…ÂÛiÚl7%‘ŶÜÉ%¨9* ÆHeŒä0õoÙKödðwì«ðªÓáï†\ßj·ÚµmIÆ%¿¾uä#'j(cLüª9%‹3}/Eó—ˆÿâæ|mÒü Ÿ¼Ðü±êÚu“R˜e ÷wKúö?x·Mð„uoêÇýJ·yŠçØpˆ=݈QîEpß¼%©xkÁCSñ ψüM<š¶¦Ä`‹‹¯˜GƒÐD›So@AÇZõ°?º¥å46~ŒýùG#v€pkéŠâ>xLøsá+/ iŒfòyîýeÍ̇t³9ä–vÉäœ €Wo]†&3j/‚:/>íù¿ÁYt3ÃÓi9Oâ{ÿ—È(¢Šó΀¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯!øÙð'áí à©<ñ[F]_KiñíöÓ¨!f‚T!‘À$ppA*À© úõóïÁoúχڷÃCǺ¿´ùå™tÉõ­“]ØYI Æ–Æe Ó„`̶àT/?üý‚hOÙ¯Âמ øIûDÃ¥é…ójE/ƒ­®K\…$9ö¯S&שÿ2qÿÀ“KñhåÆéM˵ŸÜî{æ“¥j6w—öPÜÜiÒ­d–5w‚FFŒ¼LÀ”bŒÊJàí$t&´)ƒÊr ŽôµåAU5nî,.`°¸—RDëÅ‚) ¯°iÁÁ#=*Ýùcñ—öøïûB?†ãøÁûAÃ­Úøb÷íÖ±Eá k2$;we ¾Rrsޏ¯©¿iÙ‰ÿiitmÄž=ÖôZ,¿Úzénš³3)Aq> ùj2¬AXdýQEq>ø+áGƒ´ß|<ÒaÑ4 "?.ÚÖv¨$³ÌK;³ÌìK3I$æ»J( Š( [Æþ-Ó| á-WÅú¹Å®•ÌÃ8.ÄAþÓ± =È®ào„µ/ ø,jÞ$ñ‰ç}[Sb0D÷_0‹"M©·  ã­rßâãüRðy¥h›5ío«›vÍØï“çe=T_F×­[÷8hÓûS÷Ÿ§Ù_=_þrCߪåÒ:/^¿å÷…Q^IÖQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÕýü¢Š(¢Š(¢Š(Ë~/øˆ¾ Ô<4\ÃzʳÙL¤‚îó •IèUÀ϶kŠøWãFñσíuK¨¶¥lïg¨[¿Þ†òÙ¼©”ÿÀÁ#؃_B8ܯŸõï…þ+ÒüU¨øËáέojÚ¾É/´ÛÈ‹ÚO4k°LŽŒ¯¬U› *åNÞ<ñ „ªft#,?ñ#çk®Ç©ÅF1ösÚ÷=+Ž´¹á7Ÿ¾!xZçìþ2øy¨4ãíº<‰¨Û‘ë°ys/¾cÏÖ¡?´o˜&û6©¬ÿe\w‹P‚k7Ó¢Wó®;„3,;µZ ~+ïG±R’¼uôÔ÷Ê+Èí>4|4¾_2ÓÄz|ƒÔ]ÅÿÅV›|PðB'˜Úݘ__´Åý ¼yex…£¦þãOªTìzNE5«Å5 4àÆãÄúbmÏü½ÆÄcÔ+øWsûHxÿ0øaîüIr:E¤ÙOpçèûD›ŠëÃpî6³J&þL¸àçÔúbI¢ˆfFÅbÝk…1ÛòOzùîÃ_øéãGò¼7ðüèо6Þk÷K¨õ0CºSôú×Ccû>ü@ñ$Æ_Š.æ´½§è¨4ø=Q¥_ß2žÿ0>õöù_…Yf¥V<«Ï¸ÎUpôÝç;²·Œþ*x{‹%” c]‘X[i–‡Íº¸—*mL•êÍ€=kÚþ øOøNÑ/äêr.¯d$»¹s,¤rx ÛG=«ÞøIðóá¼Mƒ4m6G]¯:¦éäpò¶]†yäצ(À¿qá. ¥•B\²æ”·ä2Ì•eËd-y?Å{Ÿˆø~é~C¼Ø Hä´È™ùÌ1±¼›~àynÆAX¨¤Rù\u¯¯©Oš.7±åÓŸ,“>ðF»¢ø'Åz¹ðå/üKg¯iÀkpÆ’]j ‰d’;û•"_6I£‘'!Tùd/Ý6wæ›C¨`aŸPpAö"¸ÿxÃÞmY´QnÚÞ¡q©]0ê÷'.O·aè+º¯7&Ëe„ ¨Ê£›Õï«:1•ÕIó$QEz§ QEQEQER7CKHÇ=ÈÛ/㞚Ǡ‚ß?™¯Óßk2è:l) Ü`ôQ_˜?¶ü–Ëz}šßùšû#Ãw[èúlð9"_Ð ÷ëÑæ¡~s„Ç:8ꯡõݬ±4¨`Ex—ˆt)t«—‘FèÉàסxgÄÑj1,'¯¯zê/ì Ô`h¦PAW™Nnœµ>¿Bž&ÐÜù¿ÑÏZë¼?âY´×X¥ËÆ}úUM{A¸Òg%´g¥s~F®ý&•‹žgÐEe¯ÙpÁ‡×ãzæu¤ÜuÌMÒ­è!›H•QÛtYä{WªÈ¶> ²à‡Ü?*å59_¡íÊ4ñTïöÒµ‹*à:1+é^Õ£kvÚ½¸Æc‘^;­hZTÄì-zÕ7T›M¸BHäVÕiF¢æ‰Ç„ÆÔÃËÙÏcÒ¼Qáu™êÔ`㑎¦¼žHÚ1H0ÃÖ½ûEÖíµkn£B+˜ñO…ÖåMå¢áÇ'C‹å‘×ËáR>Ò—SÏt^ãJ¸Y¾B~a^á¥j¶Úµ¨e ’9ó̱¼O¶eÆÞ­]#VŸJœM|„Ž+|E%tyùv>TeÉ-Ž÷Å>.òÍrGjò׫ðW¯Ö¾†Òµk}ZØ:sÔW⟠«î½²\ɽaF³O–Gva—ÆkÚÒ0ü/âil¤×g1ž¯X’;MV× ‡Wó‹«#˜È"E®ßÃ^'{)VÚé²­Ç=ªëÑûQ2ËóÝÔØ¥â/O¤ÎÒń֨M§Î'…ˆëècµÕ-J6WŒø‹Ã³hò™c]б¥J¯7» Ì0¾¤z¦ƒ®ÁªÀ0p㨥״5X ÀÁ¯°¿›M¸[‹f wíú½«àáÀäVU©8>hž† ðöu7•rÐÎ8ÎìkcÃ> “KŸÉ›ý[W§_ØYxŠËzà±)®xÊTå®Ç¡*tñpºø‘âšmýÆ?› mÁ¯oÐõØu[e 'q^+ªisés˜æS·<‹N¿ŸLf¸šÒ­És#Œžv–Ǹkš$¤ ìkÄu-2çM¸1ο.x5íš¹oª@§pßÜU­gH·ÕmÌr(ÝŽ rÒ«(JÒØöqX(b#íb|ôrN;WgáŸK§Ì¶³ÑkŸÕ4›.á¡•NÒx5˜8ÇQÞ½9BD|Õ:“ÃÌú6h­õ[•pÌ«˜Éàâµ¼3â‰lä[[Ôcí^£ukm«Zl8`Ã^lg*rå{GR0ÆS¿SÁ4ÝRãK¹Y¡byí~£câ =’`± וkÚÚdäí-5™¦jséWXIÚ"ºªSŒÕâyX\[ÃËÙÕZþ#ðÕÅ„Æxèa\vYáÊ÷Ý/S³×l¶¹˜r+Ï‹rz(ºµ?h¶fõgãô5xwí£Þ_|2»×t•ΩáIàÖí¤–æ7çñø×§”I{uN[NñûôOäìþG.1>Neº×îÿ¡î4VV…¬Yø‡DÓõý9·ÚêVñ\Ä}c™¯èkV¼éEÅ´÷:Sºº?–/ø,”>_íqfÿó×Ã:s~SÝ/ô¯Ó¯ø#TÞg앨'üòñF ¿½£ZüØÿ‚ÐÃåþÕº ÿÏ_Ø7å{|¿Ò¿Dÿà‹SyŸ²§ˆ“þyxÂù; þµ#?]袊(¢Š*†©ªiú&›u¬j³­­•”O4ҹ¤qÌÇØWëæ¯I'ÅÿEðƒOb|9¡˜¯|I2–VÎû}<ÝÈß'¢Ž ‚n ªÏÞvŠÕ¾Ëüú/6Œ+Õ䎛½½K´½CÇ:ýçÇ_@ÐɪFm´+Y6šP9Yí%ÉùØÿwWÑTÈãŽÖ(”" ª£À€Sêq¸§Z§5¬¶K²[/ëw¨èRäºõõ (¢¹ ‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ý¥"’×á¼~-·R×Ôôýb0:ÿ£Îªÿù Û>ÕïqKñ$аx䕇B ŠævSÕ@5ômx_À]R‹Ãw¿_í¿ãwÿž¶ÚC~ZuºÿJþ¹«ù-ÿ‚´Ãå~Úž$ùë§i-ùZ¢ÿJóΓößþ G7™ûø1?ç•Þ®¿üíýkôn¿4?à’syŸ±o‡Óþyjzªþw,ßÖ¿K袊(¢Š(;XÕôíJ»Öõ‰ÖÖÆÂ'ši_î¤qÌOÐ ðŸ„N¥ãj÷㟊àh.µ¨þÏ¢ÚÈ9²ÒAÊv’àþñÏ¡+?Çÿþ Çð¢É‹x_Ã爥SòÜMöú~G\‘¾QèÈa_J¢$h±Æ¡U@€è¯bìÔ9~ÜÖ¾Qè½e»ò·vq¯ÞÎÿf?‹ÿùú¢Š+Ç;Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¸‰^ÿ„·áï‰<4{ê:}Ì1úhÑŸ,þ ƒ]½¥®SŽéÜ™ÁI8¾§™üñü% |)¯3o’ãNe>²Ä¢9?ñõ5é•óçìóÿÅ^o”x[Ä…´)ék3ý¦ô"S_A×fkICQGkÝz=Wàc„›•(·½‚Š(¯<è (¢€ (¢€ Çñ»¦ø_BÔ¿e_ÿWé^ÙEO³cuŠª¶“ûÏ2Ó~ ü)Ò>á&\ËeAvç5ÞÛé––ˆ±ÚD"ŒE  {ZU(¥±œªÉîÈ„J)ûëN¢™ŸÐ¸Ï½:Š(¤Ç9¥¢€½-PEPEPEPEPLsO¦¿Jkplü‡ý³Àÿ…Ù`Oüû[ÿìÕö·‡4[ŸøCl/ÀÜ<¤à}+âÛCþKNŸŸù÷·þµúQðƺð5„RŒƒ Ò½ùÍÂŒ[Øüç„U±•“<ÒÞy­¦Žh›c)æ½Ãþ!‡S€Fǃšóø~]6á¤Q˜Xç>•ÎÚ]ÜY\,ð±q\²Š«£Ð¡ˆž§,¶>ˆ¿°‚þÜÃ2äÖ¼C^Ю4©¾U&2z׬x]‡S·P[÷£µk_ØA†qœŠã„Ü%cÝÅáa‰§ÏÏ›ˆÍoèZìÚMÀÜÙŒš“_ðüÚ\ÌÀ~ç±®tí ‡æ½W%R>Ró£RÇÐÖ:õ—bW‘øƒ@—Jœ•]Ñ±àŽ‚¢Ðõéô©×$˜º^Å–:õeqϱ®7ä{Éà ?ˆðÝ7S¸Ó.HŽ<ú÷ #Z¶Õ­ÀnÇ"¼“ĸÒ%gæˆò>•™¦jsi· 4G¸­§EMs#‹‰ž^Î{ÿŠ|1’×¶‹ÏR+ËØ2Hw®ãïÚ.µk¬[ŽAl|¹ø_qkÛ5ÁêEeF³O–GFa—ÅÇÚÓ8Z›H¹ c=E{žŸ¨Zê–¡ã!·E|èCFÆ7jÛÐõ»*áHbS<ŠÞ¶™]¹nfàùg±Úø§ÂÁƒ^Y®äŠò×GF!ÆÖS_FiÚ®«n$Œ†r+…ñO…·fúÍyTV4+8¾IÙŽ_uí)~ñ4–R­Ó|Кõ©#´Õ-J¶XW΄¯ÃÒ»o xžK)E­Ûf3Кºøµ cËûª›|EáÙô¹ÚX†èZ±,/çÓnâp³DHçð¯q×t+}ZÜåx:ñFÂ}:f·¹ÅuѪ¦¬Ï‚ óDöÍ^‡S„ÃÌÍ[Ö4kmRIçǼÂúk ÖXX½½kÛ´~ V¤â@9®J”9^'µ—ã£^•7¿±šÂá¡™H\Òµk*áeV;;Q^ÁâÁªÀHt5âWÖ7tæ ‘Œt5×Jªš±áãpSÃÏž'¾é:¥¾«n$Œ‚qÈ®wÄþŠíæÙxA^i¢kè÷+"ŸÝ±äz׸隕¶¥š&‘Èô®Z”Ü2=¬."š|’ÜùÞh$·”Ç2•e5ÕxsÄré“,œÄÇòÜx›ÃQßF×6ˉWÒ¼~hdž+Ÿ•—¨®ØJ5cn§‰ˆÃO Sš;éacâ-ÈAb8"¼_TÒî4™ÚÁÛØúÖ׆üE.™*Ã9&=ëÓõ >ËÄ6!—# úW•p'ŽÞâ½·KÔ­5ë,6 #‘^K¯h\ìÁs ªz>­>—p²B~NãÚºjÑN<Ñ<¼&&T'ìç±ÐøŸÃRX»]ÚŒÆÝ}«‡Èá‡zúN¿³Öí1Ã>e¯7ñ/…¤´‘®-î?JÊ…k;HÓ0Àiíh–|/âƒ-¥ÛqÐôkË+]ZÔ†ƒŽ |ìK©8+Þ½ Âþ)h ÙÞ6S 5XŠ?n%eØøµìêÞ·¡\éS°U&3Ðö¬kk™-&ÆÅJö¯¢n-­uKm®«+Ç7éWRó#^ÚŒÿÛD®Ì- 95ØñóL{© tå½Ï×Û_õb¬Ô°}*zðg¹ú%h ¢Š*MBŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¡¹¶‚òÚ[K¤C:2:7!•†>ÄTÔSLýngÓü#©|<¿r÷žÔî´¼·Þ{uo6ÚO£Dà/²×¿×Ï#þ(ßÚ<¹eñIÏûÚ†“þ6ïú~_CW§›®j¾Ùm4¥ó{ÿä×90zC“ùtÿ/ÂÇÍ?cßÙÇö€ñM¯~/ø6?ëVVi§Ãp÷—mµŽI%Xö[Ï<®rT·8οø=ð;á_À/ \ø?á‡t{Ë·¾šÞ9¦˜=ËÇM!i䑲R$tÎkò+þ ™û\þѳÏÅï xgà÷‹ÛÃÚN§¡ ¹á[++÷jš2ûî`•ÇȪ03ŒäŸ£?à–þ-~Ðü]⌠jšn»öX&x €¤ÖÞ8ÔÅŽHÏ=kË:ÏÔ(¢€ (ª÷wv¶“_^ʰ[Û#I,Žvª" ³1<É4Ò¾ˆ4ø·ãù¼á¤þÆ€_xY•lt‹N¦k¹xRG÷#;“ŒŒŠ»ð·Àü:ð¤Z;Îou;§{½Jñ¹{«Ù¾iebyÆx\ôP;ך|.´ºøâ럎ZäLšz¬–^¶b²Î%»*zIpG¨N2A¾‘¯[ý…?ªÇ}åëÛþÝüïÙt?y/jöééßçùQ^AØQEQEQEQEQEQEQEQEQEQEQEQEóçÀ/ø”øþ_øG¼Cyä'÷m/qs罫è:ùóGÿŠ{ö”ñŸ÷añn…i¨Ù§ÓäkfßËu'Ûé^¦Þ£ZŸ’kÕ5ú6râ4œ%ço½ è¢ŠòΠ¢Š(¯œþ01ñ÷Œ<7ðNÔî´½a«ë˜è4ÛG8›Úy€_Q·Ò¾€Ôu ='O¹ÕuV K8ži¤n#K3`5á_ôûÍj×YøÁ®DcÔ|q8žÝïA¦CòYÅí”ùÉ6àO5ëe¿ºŒ±Oìè¿Äöû•ߪ]ÎLO¼Õ.ûú/óØúUUB¨À:KEäaEPEPEPEPEPEPEPEPEPEPEPEPEPÏ?â’ø³ðÿâ|–÷òËáËöõKÑæZäö :rO×ÐÕåü+?Œ>øƒI±È¿ŠµÙ²ýñshDñm=‰d ø×QàAãè~.·ÆÝVÒØ‹#(ÞŸðÊþêây…§S¬où¯Í¯‘ÉOÝ«(÷×ô§Þuõå^*øðCÇzÛø—Æÿ<;âbTHÞ÷QÒm.îY#E2Í9 88«Õkù«ÿ‚­|qøßðïö¤ðÄ/xkH“B°œY隵ݿ˜ï2³ùPȉ¹¶Œœdâ¼£¬þŒ¼-àÿ xHOø'D±ðþ—3­¦Ÿm¥º»œ³áUPIêqÍtUùÿ ñߌ¾!~ËRëž:ׯüGªGâ ø Ö¥u-äþZÇ*y“36Ѹàg5úc@Q@yOÅß]xñ[è‹Ïk²‹"ׯ™u'ØÏ8‡Îäñ€#9¯L¾¾³Ó,®5Be·µµ¥–G8Tff' d×Ï ¬nþ$x¦ë㯈!híeG³ðÝ´ƒ ?8{’§¤—'œõ ’¤W§—ÐŽµê¯v=;¾‹õ~Iù¸‰½)Ãwø.¯ü¼ÏKøaà_‡>ƒBYåü®×Z…Ûr÷W³ÓLÄòrxä(½Š+†½yT›©7vÍéÁF*1ÙQYQEQEQEQEQEQEQEQEQEQEQEQE|ùá¿ø§ÿhßèý#ñFa« í¾ÍšÎL{PŸÀ×ÐuóçÄÏøü_øcâáòCuq{¢\7÷¾Ù™þDkè:õ3/yR«Þ+ÿ%¼$Ž\.Žpìÿ=P¢Š+Ë:‚Š( Š(  z…ýž•asªj2¬¶q<ÓHÜ*G–f>ÀkÂ~Ø^k–úׯn&Pñ¼âkd½—RÎ?mÉûÂG ¸µÇ;«Üèt©'ñlÞf¢èpÐé6¤=Ãd}Ó)5Ï$W¿ÚÚÛÙ[Ceiú,q¢Œ*¢ *Ø0+Ö¹Âÿz§þ’Ÿë/ý'ÌäøêùGóä¿2z(¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šÿ×ýü¢Š(¢Š(¨å–("yçqq‚ÌÌpª£’I=©+çŸÚÎòêÃögø™we+C4zî×S‚3èhæOÁV¿cÏxŽóÃC\Ô5él]¢–ãK±i­¼Ä8`’Èщ?Ä›”öb9®Cþû zø‡ÿ«ÿÇëùSëɯ¡¾~Êÿi955øCáÖÖ#Ñ›©žh­áFa•O2fU.@ÈPs@Ñ7ü>ö@õñþ WÿÑÿ‡ý=|Cÿ‚Õÿãõü»x¿Â>#ð‰µ/xºÆM3XÒ&{{«iF9àƒþ5í¿dï´…¶©{ð‹Ãm«ÚèØ3<Ñ[Ä®Ã!ÊÊÈÚ2húÿ‡ÃþȾ!ÿÁjÿñú?áðÿ²¯ˆðZ¿ü~¿–ïøo\ð~¿¨x[ÄÖRiÚ¶•;Û][Ê6ɱ¬¬=A¯{ø!û!~Ð?´V‰ªxá7…ßWÓ4‡1OpÓEfP¡ÌQ™Yw¸R UÉ´ý Ãáÿd_ÿàµøýðøÙ×Ä?ø-_þ?_Ë&¯¤êZ«{¡ë6Ïg¨iÓImqƒkÅ4,QÑèÊÀ‚=k:€?ªÏø|?ìëâü¯ÿ£þû zø‡ÿ«ÿÇëùS¢€?ªÏø|?ìëâü¯ÿ©­¿à°?±äó¤2ϯ[£ ¦eTz’³`{}«ùM¢€?¼_‡_< ñkÂ=øs¬Á®è:šî‚êÜ’§ ¬¤GSÃ#Êx íëñ{þ¡yu/ÀïÙI+4úê4hOʦKuÜ@í£?Jý¡ Š( Š( Š( Š( Š( šÝ)ÔÒ„åí£¦ñ:ÏTc…H!ý3_o|ñ¶sáK lãlj¹ü+å¯ÛWO€ß%óýá ý+¨ø*êþ …•¹ùy«èÜðñGækðù…KuÜ[ÛjP|:°â¼KÄÆ•pÏ·1“Ú·ü/↶e´»o“ &½2îÖÓWµÚà2°à×—Ê“·Cë%béÝnx …üºtëqzW¶è:ô¬€t¯%×´ô‹‚Ê ˆ÷íYv:„ú|Ë<Œu­çEM]V<5NIl}a£ ‚e5â~ Ð'Ò® íÌG¥zÖƒ®E«[?|kRúÆ øZ”Ž+ŽgcÞÅaaˆ‡47>nÆyõí]/‡õù4«œÆÆ™¯ø~m.veÆÇƒÚ¹ÁÓ’+ÑiM'ïP©n§Ð¿èZÝŸfW•xÿˆ4ô‰ËF»¡c×ÒáïɦN±ÊÙ„šö6ŽÓY±ù€uq\žõ_¡ï¨ÃOÌðm3RŸLfŽÐzW·hÚŶ±l9±È¯%×¼=>•;<`˜³Ç¦++LÕ.4Ë4,BçšÞ¬EÍÏÂ⧇Ÿ³©±è)ð®I¾²0ä^bÊê̤a‡Z÷íY¶Ömƒdn=W½r>*ð°m×¶KƒÜ ÆgÊΜÑöÔ¶8½\ŸIYNb'š÷ >þ RÕeBaȯœÚ7RcIlu¾)ð¨`×¶KÏRyk#«”aµ–¾±½¶ÔmÄ‘ÊÑ\м*5åšòzXЮÓå‘Û˜eêkÚÒ1<3âi,d·$•éÍz¼‘Úê¶›XWó“£FÛ_†Zí|1ây,¤·gtmОն"Ú‰†_˜Yû9ìSñ‡¦Òf2Ä»¡cX–÷tÂâp:×Ð2GiªÚm :0¯ñ‡®4™ÌÑзjš5T´f9{„•ZG¨èôµºò˜äSõí Rò0µáÚ}ôú|ë5³`µíšˆ-µ8@,¾†°­EÅóDïÁcá^Î{ž)¨éóé³´S)Æx4Û éôÙÖx˜í¯sÖômZ‘óãƒ^'ªiWDæÛž ®º5”ãfyܾT%Í fÐuø5H@9sXÑíõX r»±¯°¾žÂq4$®+ÛS^)ªi7Z\Íëòçƒ]'†|NÖ2 k£˜ÏCé^—}ag®Yäa‹ aJ“³ØôçNž.Ÿ4~#Á­/f²™e€œÖ½§ÃÞ"‡S…câQÔò]_F¹Ò¥!Ûžj¡k{5”©<$«µµJJqæG—…ÄÏ>Sß5ßU·1Ê£v85áÚ®—s¥\´2/ÈO½ÃÞ"‡R…c ­-cFƒU·(ãæìk’•gf{x¼10öÜùìepêpGJôO x¬ÆE•éã &¹ [E¸Ò¦1Ì LäÈ݃¹8Åz–¤mÔùÜ5Z˜jšŸGÝÚÛjvÅWðí{FŸJ»)·÷M’ vžñB2­Û`Ž5Üê:u¶«lR@áÁ¯> Ó•™ô©CKš?áZF¯q¥Î2v¢½»NÔm5›0NG ׋k:ú]Ã!ËÏ¢Ò5‹*äKe{­tU¤¤¹‘æ`ñ²£/e=Ž·Å>{v7vK•<+Ï0ÈØk úMÔíu‹\¦ #‘^â ´l×V*pyÀ¨¡ZÞ쎌vëÚÑ x¤ÂÂÎìñØšõ)"¶Ô-ð@uaõ¯šØ27Ì ¸¯Cð¿ŠM¹wg ð ªÄáíïD2ÌËþ]U ×ü*ö3ýªÐe$ ü×ø¡o:|dÒ&‘p¿nµÿ×E¯ØLÁuxtjüÃý¡­mm¾-h‘[o­I¨ýêVù}fù“ìy¼C—BcÜý?ƒî¥OUíþàúUŠñ§¹÷4~QEI QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEóÿío6áM/â-Š»ðF§k©¿yí·yW1ý7$û-{Õ½Ä7vñ][8’•]y ¬2ö"³õíÏÄz¡áýEwZêvòÛJ=Rd(ß¡¯%ýž5›ÍCá–‹«6uO M>‰x=%°-8öƽY~óŸX;|¥ªûšyȽÚßâ_Šÿ€ÿð‡þ qßgþÿ‡å_ûæîCÿ³WÓðDi·|ø‹÷5èþúµQÿ²×Ïð[ØvüNøe?÷ô{Õÿ¾nÿìÕîðD ·|7øŸ÷5kÿ¾ qÿ²×”uŸ¸ôQEówÄû«ŸŠ0·ø¢JÉ¦Ä±Þøšâ2AŽÓ9ŠÌ0è÷eºƒ<‚EzWÅ?ÅðïÂ’jÐÁöíVòD³Ó,×—º½ŸåŠ08Ï-è ÷ÅUøKà |á–M^·x‡W•¯µk³ÉžònXýÄûˆ8Àɯ_½…?­K}£ëüßöïO;vgÞKÙ-ºúvùþG¥ZÚÛXÚÃegÁon‹q  ¨ˆ0ª pTôQ^KwÔì (¢Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@|ùñcþ$_¾xÏîÇý£s¢Ì¼58ˆ¤‘‚=þµôxGí'eq7ÂMOX±\Þøzk]Zýײ™$cÿ|¯O&í0‹ÚWþ¹S—¿tßm~íOw¢ªi÷Öú…¶¥hÛà»%Œú¤ŠOäjÝy­4ìԊ*9¦ŠÞ'žwÇ–fc€ªI$ôRçÏŽW7*»Ð> iR2MâɼÝEÐá¡ÒmHyÎGÝ2#SÐäŠúÚÚÞÊÚ+;HÖ EŽ4Q…TQ…vp+çß‚qKãMkÄ_õ x†_±i ã-&ÍŠ¡ò¾|¤aô#ƒ_D×­™¿f£…_gñ=þí#ò90¾õê¾»ztû÷ù…Q^IÖQEQEQEQEQEQEQEQEQEQEQEQEQEóÏÀø¦¯¬ã|cÚ½\»ß…ZÕ׬uü¹—ÌäÄû²NÎß'§çcèjþZà³ùµ¦˜ÿó×ÂÚ{~W7ký+ú–¯å÷þ O—ûTxjOùëàëø‹ý@Jò޳ô_þÉ7™û'jéÿ<¼W¨/çifßÖ¿Zëòþ«6ÿÙoÅ1±øÆ÷ò:~Ÿýs_¯ôQEy§Åoˆáï…›P³ƒíÚÖ¡"Yivc–¹½Ÿå1ýÐ~f?ݾ+l= Uš§«"¥E¹Kdy¿Ä«‰þ*øÖ‚z<Œº=Š÷Ä×’1wAbtyÈÜýA‘žE}oo¥¼V–±¬0ªˆˆªªŒp^oðŸáùøá²ê3ý»]ÔåkÝVðò׳s!Ï÷Wî ãœdšôêìÌ+ÅÚ'îGñ}eóé䑎›ÖsÝþ—õÔ(¢ŠóN¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÀÿiKy£ø]7‰lн߅¯¬ux@ëºÒu,ËW»[\CwoÝ»ŠeWF °È?ˆ¬/è)⟠k^“u[+‹^{yѲøg5ÁüפñÁÏ êçEd–²çïy–dÛ¾}÷Fs^¤½üË'ÿ“-?ô–r-+¿5ù?ø(õú(¢¼³¬(¢Š*9eŠžyœG`³3QÉ$ž€T•àµkûÝ'KøWáéLZÇŽî ŽõûÐX ß{?Ñbùà\WV ëU;Úû¾ËvþKS*õy äQø)¾5×à†1Ñc‰B¨ü­¬~%Uªçe²]’Ñ/¸Xz\Iï×רQEÆlQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¯œ¿kÄy?f‰É–c¡^p9þ_FÕSLÓõ­6ëGÕ­ÒîÊú'‚xd’H¤R®¬;‚ €?û;öPý¸þ,þȱkš-4ý[L×ÊK5®£Œ©qíYchpÀ’¡æ¿f¼iÿ\ø¯ø‚ïVðÇ‹uY\»:Ù"Ãq[Žv£¸Ý´v8ë“ÿ‡"|.ÿ¢‘¬ÿà5¿øPóÿñ?â?Š>.øÿ\ø“ã9’}gÄ/urѦÈÿð¢öUdð:×Óß²Ÿíáñ{öHÒu¿øËMÕ´½nE¸k}F9b¸UÚ$FŠHÛ‘€ÊNCÍ~±Ñ>ÑHÖðßü(ÿ‡"|.ÿ¢‘¬ÿà5¿øPóçñÇ^#øãmoâ‹§:οu%åÔŠ¡É)É £€£ €õÇì·ÿøÓû(x?Xð'‚-4ÍWGÕ.ö8µ¤smvè±´‘´RFHeEÜ‘•mËgõSþ‰ð»þŠF³ÿ€ÖÿáGü9áwýgÿ­ÿ€?ž/ø›Yñ¯Š5x޵jÚíåÅýäØ æ\]HÒÊø ÎÄàVGŸðäO…ßôR5Ÿü·ÿ ?áÈŸ ¿è¤k?ø oþüáÑ_Ñçü9áwýgÿ­ÿÂør'Âïú)Ïþ[ÿ…8tWôyÿDø]ÿE#YÿÀkð©­ÿàˆß Òt{ŸˆºÔ‘ (··RG¦qÅiÁQÇÁoHT…mr bE¸Ïó¯ÚŠñ¿>~Ο,þü4²6še³´ÒI#ožæâLošWþ'lè öJ(¢Š(¢Š(¢Š(¢Š(¢Š)–šÝ(ó—öÝ.ÔÍ×lk]?ì³^)øTg‰}/éšæ?mÈœÙÊä|¾JóÛ5…ûø¼F$ðŽð ‚û}”õëè¿æ4~gUEæŽ3ê}34rÛÎc”mdãßøWį‚Êðä­¯øe.TÞZ®d+ÉÝe·©áÐñ\iûHžµHÔÂÕºØú&öÎÛT¶1H+µâzöƒq¤\´F»/ xŸÌAez~aÀ5ÞßXÛêVæ9`Ã\ñ›¥-Ob¶ª|ÑÜð-+S¸ÓnÂß!<Š÷ Y·Õ`VFÃŽ¢¼]ð寗;:ŒÅ؊˰Ԯ´Ù–í€"ºjÑŒ×2<¼.2xiòKcè;û/á1L½zWˆëÚ úTìÀw¯Xеû}RËbLsZ·ö_ÀÐÌ ƒÒ¸©ÍÁžÖ/ Oxî|Ù¼‘]¯†¼E6:ÛÜœÆÝ=ª†¿ Ï¥LÛS1u¹Á“‡Ï5èɪ‘>UNxyŸEÜCi«Za°Ë à׌kþŸJ˜²ŒÄy­ï x‹Èqcxß)à^›ygm©[äƒ q):Lú9Ò†.•áñ§js铬Ñ3ȯnÑõ›]fØsóc‘^I¯èis—UýÑïÚ³4ÝNçK¸Yà<Òº+Rç\Ñ<¬.*xyû*›⯠î {d0G$ òöG« 0í^ÿ£kÚ½¨ççÇ ×⯠›ë%ù‡P*(×ÖÓ:ó ’ö”NW@×çÒî\þäžE{eí¶£oæDC+kæâ¥>V7|ö®«ÃšôºUÈGlÄØâ®½%Í ¯0ä—³‘Óx§Â»·^Ùr+Ì£|¬+é;yྀI ­^y⟠îV¼³^G'–ödtæYeÿyLÊð§‰ZÑÅ­Û|¤àWªÏ¶£nU€uq_8íhØïʲ×á›f[KÖùBi×¥z&8 Åÿ¡—â O¦HÓB DÜ×3ew=”¢h[iSȯ£eŽÞþØ£aÑÇãž$ðÌ–4ð¯îÏ¥:‹û² Ã-ö¼¤z‡|C¥ŽFÄ‹ÇÖ´u­ßT€«ŸðK[©l¥BÄ2ž•í^ñ:” ͉G=ë:ô4NÌ¿0h{*»žAªéWTíÃåÏ«Ø_\iÓ‰á?-{Ƴ£Ûê–æ6·c^ªi3éw £ ž tP¬¦¬Ï/–\ðØöm_ƒU€ âAÚ®k5¾«nÉ"üøà׃XßϧL³@ØÇQ^ß kÐê–ê ~ðpk–µÌ_˜B¼9&xÆ©¥Í¥Ü4S/ÉØÕ\¸ÒgR1W´k=¾«nÑÈ£v85áÚ®•q¥Îb•~\ð{WU*ª¤lyxܰóçïn¥£Ë ã‘YZÿ‡¡Õ¡bò][ŸIX6c'W¸iúŒŒ ,MÉê+’p•7t{\D10äžçÏwÖ77ÞuÚðks@×æÒ§UvÌF½OÄ: ­¹!q èkÄo¬fÓçky†8ÍuB¢šÔñ1Xiá§Í ¡ìoá¾€I ®SÄÞŽýæÜbQÍy÷‡µéô¹ÄNÙŒžõíö—PÞ²Äw+ äœ7Ìoˆ†*’Üù¾xe·•£”me5ÛxWÄ’Yȶ—M”< ×Uâ Ç|un HµãÒÀöò4M•e=k½8U…úžZsÁÕÓcèCN´Öm œà ׉ë5Ε9‰”´`ðk§ð¿Šdµu³»;ð zeí•®¯hUÀ!‡¹!RTÞ»´èÓÅÚ;Ÿ>Ù]\ÙÎ&…±Šö¿xŠR!¶$«Êõí }*bT/®kÒòk;…¸€•ÅuV£Çš'ƒÅTÃTå–ÇÐz®•o©ÀÑJ9ìkõ}*ࣞ zχ¼C¥Ç#b@+cUÒ­õKfŠP7Á®u\§½‹ÂCh;#¼m½8+Ð׬øSı΋itß?bkϵãJ¸(ã÷}cÇ#Ã"É k±ÇÚ#ç°õ熩iDêzm¾©nb‘Á¯Öôiô«‚¬>Nƽ/Âþ%KØÅµÁÛ"ñÏzèõ}*ßU¶1¸ˆà×-:Ž›³=ìNž*ñÜñ WŸJºI³<Š÷; û]RÔ4ø—þ)|=&,Øí-<•»~hŤ0†Üb_½œcŒs^dàâí%c©4õGëµ2Yc‚7šgÇ,ÌÇ@ä’O@)õó§Å½JÿÇž!³øá™Ú©F.µû¨Ï6šX81Ú[“òÙrHÚs]8,+­S–ö[·Ù-ßõ»Ðʽ^H߯O6Uø¿¼y/Æ-Eü#Ú1–ËÃP¸À“—äò²?E3ƒ_JÕ3M°Ñ´ë]#Km¬ì¢Ha‰#ŒmUÐнUŽÅ*³¼U¢´K²ÿ>¯»lT)rG]Þþ¡EW¸QEQEQEQEQEQEQEQEQEQEQEQEQEV/‰4h|GáÝSÃ×8òµKYí_=6Ì…èkjЍMÅ©-КMYž/ûÿ»½¢¾|ø%ÿ_üJð3qý—¯=ôKÙmõX–á{_AסœA,LÚÙ»¯Ij¿sàÛt¢ŸM>í¼ãÖ«¥i_ ¼?)‹WñÝÁ².¿z ïfú,_/¾î+ß+ç/…?ñpüâOŒ³þóOBÚ&…ŸºlíŸý"á{4ÀàõÂàÕåqPrÄËhkë/²¾ý}'î•5ö¿.¿åêÏ|Ò4« J³Ñ4¨„vÇ1މJGàhÑEyr“nìêJÚ ¢Š) (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+Ä?hmóQøc{­i+SÂÓA­ÙŸIlÌoÎ=ãñ¯o¨n-ỂK[”E2”unC+ }ˆ®œ%Ñ««£Lʵ>x8w3ô fÏÄzâ 9·Zêvñ\Ä}Rd¿¡¯?ñÏÀŸ‚Ÿµˆ|Añ#ÀzŠu;hÚ+SN·¼• Vg«ÌŒBv`£Œ±=ë•ý§›Mð®«ðêùËÝø#SºÓo¼ö»¼Ûi>€=–¿ÿà¯?þ4ü,øÇà½/ᯎµ¿ é÷Úšh4ÍB{8ä™n¦]ì°º‚Ûp2yÀxü2¥ZtÖÉééÑüаõ9à¤ÏÝ/|:ø}ðÏK—Døqá3ºuÄÆâKm*Ê^fUC#$ŠÎUUKœ3€+²¯Èïø$ň¿>xßPø“âSÅWÖZòÇ ú­ìײÇZÄÛæg*»²vŽ2I¯×ã6"žxm¡’æâEŠ(”»»ªª£$’x¦¾pøsß|q7ÆUhzo›cá˜$ÌyÛq~TÿÄmN„ ät5?ÅB÷≭>ørfŠ+¸ÖïÄWQœm7<[†%¹?(BdTšúOÓì´« }/M…m­-#Ha‰#AµTÀ^Âÿf¡·5÷Güåÿ¤úœo÷µ-öcø¿øŸ¡rŠ(¯ì (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ùóàOüI¯þ xþS¡ø†æhSû–ºŠ­Ì#ÿjú¾|°ÿŠ{ö—ÕmGÉ‹¼?owŸï\éÓHúˆ¤½L½Jµ?+¯Xµú6râ4œ%ço½ è¨..­­Iu*B„…Ø($ðOsS×™c¨(¢Š@ó—¿ø¸ »ñ¥ñT´{—fX,æ6öð©9 qà«“ÿ†µý¦è¦kßø'ø×Ïú£ÿÛ°ý‰nÇŒö¦m0ë+å55§e³û.Óæ˜ùfÝýï˜ lï@ÿÃZþÓ?ôS5ïü “ühÿ†µý¦è¦kßø'ø×-ñò…öÿü[Ágy<·ò,¹cþž6—ùŠç;wsŒf¿A¿àœì3w¡x¾OÚ´³â" 5Ö’ØYlùÌ!L»ºÿÛÞ€>"ÿ†µý¦è¦kßø'øÑÿ kûLÿÑL׿ð6Oñ® ã_àø©â¨~<’x55¤´¥‹MçËåþr1÷K|ØÆîs_¦_ðNí;öºøkâÉi÷ÓÅßkuEÖd‘#]7ÊM†ÓaÍ2y›Šþóîíâ€> ÿ†µý¦è¦kßø'øÑÿ kûLÿÑL׿ð6Oñ¯'ø‚ž‹Ç¾$À,žMJðiM?úÖ°7Ù‹çøŒ[sï\…}ÿ kûLÿÑL׿ð6Oñ£þ×ö™ÿ¢™¯àlŸã_Ê7Õ뿲Wü6?Šßìþ þÐOøÆú±[\Û‰c´–ø¶Õµ–Þ}ÒÛHÍò®çpÍò§ý„¯æþ í¡éGímàMSÀ°ÅoâÍWK²žçÈ^[¨ï$ŠÒY6òd!n<íE érŠ>´PHN)i­Ò£ãÛ3CK߆7·ðǺx”ò=+ó—öqñ¬^ø‘k©Ý9X¦g»àWí?Ä_ Úø«Âz†“r»„Ñ8Ï=kð+Å-ÿ…+ðàxÚîÍ~n¤ ùÿöUøÅkã/ E¡êwj *¡?1Q€+ìB‹"mnAãÖS£S”û|-ZxÌ:hù ‘1*JJµê>ñG˜ë`Ž5KÅ^t/}h:s´Wœ¬îÎÖS]œ±«w<r©…«ä}!ukü&9T2°ë^/â/M¥ÎҢ«ð·ŠVE[K¶äð 5ß\Û[ßÛ˜äƒ+…NP•žÇ»R,T9£¹óµ•ôö2‰­ØŒv¯hð÷‰ ÔâXä8”v¯8ñ‡'Ó&i!RÑç5Í[ÜËk2Ü@Å žk²¥8Ê:%õ0µ9g±ôeý…¾£E*‚ë^!¯èWLäªæ#Ò½Þ(†ú1 ÌÇ÷®¦öÊ B 0aÁ®:Spzžå|5•ÊÚ]Ëg*ÍÀó_FËÖåŽ+È|Eáy¬$k‹Q˜Ï$S¡ˆ¿»"3,»’^Ú‘Ýø{Ä0jP¬R0¯ëZ:ÆmªÀR@7ÿ ô¯µ»’ÆešC-{‡¼Mo©Æ°ÌBÊ?ZŠô\_4N¼>5£ìêžS«è×MÁI”õªÚ}õÆ:Ï)Î+ßuM.ßR·hÜÄpkÃuçHœ«Œ¡<Þu5ÊÏ7€• sÃcØt vR–ýàê*æ­£Ûê¶ì’/ÍØ×„išŒÚuÂÜD~Pyîz.µ«n®Œã¥rפéÊñØõðÈ×$ÏÕt™´©Ú)”ìÏ­hšÔúU°bÑžÕí:Æ‘oª@Ѻ޵áÚ¶•>•;E"½µtÓ©‹•žV; <<ý¤6=ßMÔ¡Ô`Yb ç­eøƒÃÖú¬ TbQÐדè:Ôº]Ò‚ÇË=kܬ¯ ¿…f…Ï5Ç8ÊÐõðX˜b!Ë-Ïïlg° ¹Ð×Gá¿K¥Ì ˜îˆô¯Kñƒ§nÌ£â¼JúÆ[ ü‰”€½ v¤g3ÅÄaêaªsGcè«k¨oaÄC×⟠­Ò5Õ ÚÃ’zå¼+â'Ó¦û=Ñùàf½š)¢¸ŒH„2°®9EÓ•ÖǹNPÅÓ÷·>h‘$…Ìl 2šôŸ xŸj­ã`­xa.WíV£3‘޵䮒ÛIµ¾WS]ŽQ«çêÆxJ—[EßXÛê–Å 85âZÞƒu¦Lß)0×_á?aexþ½îÒÞþ€a\Ñ›¤ìö=º´¡‹¥ÏÏ,™fˆaÚ½¯Ã¾"‡Q„G#bQÁ¯5ñ‡n4ÙÌЩh³Ö¹ûKɬ¥Yá$`溫RŒÕâxØ\EL4ùe±ô©¥Ûê–æ9”Ø×‡kZ-ΗpÈÿtž zׇ|C£‰ÎÙ@æµµ=2ßR·h¥PIzä§QÓ•™íc0°ÄCšŸ>Aq%´É4$ïC“Šößø‚NŒq ¯'Ötk­&f‡ÊǃíYö7ói³ á'ƒÍuUŠ©£ÃÁ×–¥¤{毤[ê–쎣~85áÚ¾“q¥NŅ̃Ë'ƒ^Õ kPê–ÊÁ¾|r*Ƴ¤[êvÌŽ£v85ÉJ«ƒ³=Üf 8ˆsÃsÅô jm&è.âb5î×p_@²ÄÛƒ•óþ¡¦\i×O ªvçƒ\§ˆ¾/Ø|5Ò®îpf v!<çW\ðþÑÞ'…Í^¸ÕzSûføÒËBÑâÓt·êV"@ðãükÍdO É âI£Ìw2.;çò÷‹¼_¬|eñìO.æŽòm9ã95ûðsᾟàÏiú[D<ÅPçêy®êïØÓå–çÏàpÏŒu¢´Lö¸úß-F¤gÒ¤¯›–çêq•‚Š(¤0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(†øŒ|zž¿—᩵þßw—h]$ª.B¹þÙ\ðFGâ—ÄOŠß|euu¤øÿZ½“ɤÖ/þ I}Ö·@‰¹Hî¹z×ï5~IÿÁL_¸àÌ÷ …«É‰‚³ÚVÕ~¶þ½<,ïV¬oJOÓ¹øŸûe'üŠ/ÿ_ãÿDWè×üÎlMñ¦ï/‡X~Qù×ãÅOŒïÅiìΫiogo§4¦Ý! X vç{±;È9~•õ/ìûjÙþÇ^1×ç×ü;&½áÿ-œWÍm(K»afÒ”’’OõÍ”fLñ‡9ñ¸£0¥ŠÇT¯Gávü_¡Û•aåJ„iÏuÌþ°>%xòÃáÇ„®üKy¹™JÃijœÉuw)Û ($³uÀ88â¹ÿƒþ¿ð~‡s«ø¢AuâÏKöí^qÏï˜|°¡í ò(HÀ8¯øAã-ö¨ñ§Ç àÝø Ãå¡ÐÔ¡žøö‹©bnU¢Ï–ŠÃ å¸ çìæÄMQ¢¨Aë+9?Ê?-ßž iÅÎ~Ñì´_«ÿ//P¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¾cø­ûSø'á>½?…µm+R»Õ"Ep±Ä‰ #Œ« ÆTôÊ©äÔúr¾zý¢>Øüdð‘KEH|E¦+Ia9ãqêÐH¸øëü-ƒÓ úÙ'Õ^"1Æ'Èû;[Ïӹǎö¾Íº?ð^¥û_x†×â±ãÏhVÚ{k6Vö“Cxïr Ú³›1ù?6ÖÛ·=Mz×ìãñ§ãÆ?‹ÙøƒXEÓm§¼¸µ·‚8bl)`»ÏÏ"¶ »õ¯ÎíGN¾Ò/î4½Nµ»´‘¢š)×Iá•èA¯Ó/Ø+Ÿeðωañ/í}á|Pñ'Žü §µþ—­éֱݵëýŽ?µÙ—U¸èä „…!¶}üAý¿ÿhy¿h¿ˆš‰%¾Óîÿ²ô÷µUÓ¹Š%33…,YËœçqü+çOŒ_µ¯‰ZÔÐ[Ï%¿‡àr-­*)âI@ûÎz€~ïAÜŸ¯o?ÌðR½ -+Ú˽]´Ó¢Ñ[ÐáËðµ×¿V]Ý–ÚŸÑßüúÝþüJÓ¨g‹X´˜Ç¸o %¾ÐÅzà” jýø›ãë?‡¹ñ ñ»¶e·±´Ndº¼˜í†’Yºã œq_Ãßÿ‰>;øMâË/|8Öît nÁƒEsj壯ÝxÛdpU‡ ¯êgö'øáqûkZÚ|bñ‚[Ú]ø NþÊ…·"ê’Ækò‡’/¿0²>œù«?uknþK×ðW}CåËhnÿ3í/„>¼ð^=÷ˆå~)ñ ¦ûW¹ëºáúD§þy¿"ÇRÎ+Öh¢±Äâ%V£©=ßõ÷.…Ò¦¡…QXQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@(~Ó~(üµ²Õ<)¤Y]h÷Ÿº’òu–F‚ãœ+"2r¬I‚éŸÍüø±ã=rÇÄ:–»%­ö˜“Gk%’­£B— ^ ®CyŽ;b¿oGø3³ý4ýkâWÇo6¿yq©>VÔ&’æW™€µbd¹'™Æ¿jkóköð¦ëŸxâdÿV‘iÐ7ûÇΘ~büëô–¾wñj¦?ÙGh$¿_Ôôxz.ï'Ð(¢¼“ãWŒïüày†‚7øƒ[–=/JŒ™¯.ÎÄaÿ\Æ_Óå¯ÂáåV¤iCvì{5j(EÉô8ßÅÊøÃ®|E“÷š7„š‘Ý^äàß\/㈕†C(>•ômq¿|aðûÁzGƒôãº=6F~†YOÍ,‡Ýܳ­vUјâ#R¥©ü1Ñz/óÝù¶g†¦ã{w«õþ´ (¢¸€¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ª ªé’ê2iÞB÷ð ‘íÄŠfHۀ̙Üö$b¯×óOûjþÆßµwÁ¿Ž^(ý©¾êZ†»§êz•άº†‘,£TÒÖw.ašùÚ”ùa“zyJý,Q_€Ÿ²·ü-[ì~ ýª,¶Ÿ–4ñ&›¯m#œAåujýÖð‡Œ¼'ãÿYø³Á½®»£_®ø.ìæYáwá# ðGPx éh¢Š(¢Š(¢ŠÿÒýü¢Š(¢Š(¯3øÏà9¾(|'ñoÃËiÖÖé·6qÊßu$•BØç±œv¯Lª—÷öZ]Ƨ©N–Ö–‘¼³K!Ú‘Æ€³3Ð2MÞ8ýÿi‡Þ#¼ðψ>ë†âÒFO6ÚÆk›y@8ЫÆêz‚¬}ùâ¹øg¯_ôN|Eÿ‚«¯þ7_Ñgಳ7…üAw¡èš.¿âX-£ûm¤ñÛÈTà˜üùÑÊú€£Šä¿áö³ÿýÞ'ÿ¾l¿ù&€??áž¾=Ñ9ñþ ®¿øÝðÏ_¿èœø‹ÿW_ün¿ÿáö³ÿýÞ'ÿ¾l¿ù&ø}ŸìÿÿB7‰ÿï›/þI Àøg¯_ôN|Eÿ‚«¯þ7Gü3×ǯú'>"ÿÁU×ÿ¯ßÿø}ŸìÿÿB7‰ÿï›/þI£þgû?ÿÐâûæËÿ’hðþëã×ýŸàªëÿÑÿ õñëþ‰Ïˆ¿ðUuÿÆë÷ÿþgû?ÿÐâûæËÿ’hÿ‡ÙþÏÿô#xŸþù²ÿäšüÿ†zøõÿDçÄ_ø*ºÿãtÃ=|zÿ¢sâ/ü]ñºýÿÿ‡ÙþÏÿô#xŸþù²ÿäš?áö³ÿýÞ'ÿ¾l¿ù&€??áž¾=Ñ9ñþ ®¿øÝMoû8þÐ7s¥µ¯ÃoË,„*ªéWD’{å×ïÏü>Ïöÿ¡Äÿ÷Í—ÿ$ÔÖÿð[ÙæIÒ;ø¢Ø€Î#²m£×iæ€=Ïþ ‰û6øÿötø{oñ6Ûû7^ñMÿöƒXV’ÒŒ$K)R@‘€,Ê Ú ¤uå?>4ü<øýà+/ˆÿ µ!©h÷…’¥%†dÆø¥CÊ:ädÄA¯V Š( Š( ¿“Ÿ†Ÿ~#üý½>0x×áw.>!ëê¾&³m6Ö9ä‘`—TÞÓbÞ9 QFvãæäô¯ë¿oØþR‰ñ£ýÿÿéâ*èþ!ÁO?më &qcðO´‘°z†™©Íåï¨u2:ÀŒõq^-ûÏðgã¿í+ƯÚâ`Õ~(Ëx·Z^‘}¶‚{ØHXÜ•[vhð¦ÞÚ=˜!pÝ•ý7×âGüÛöbøl>ÿÃDøcIƒDñVmüÖ‘¤ oxþ^éÕß2HP¬Ÿ{i`Û€] ¶ôWÇ?°Åühý“<ã]5î¶`¸±¼þüÒX\Él²9îïhÎ{±&¾Æ šÃ S©¬2(AŽ¡”†ä_žµŸÀõ¸[Æ~·ó.P€Ñ äƒÔþ•úy⢸‚;ˆÌ ta‚5ÑB»¦î34ËiâiòÏsùãðω|Mð×Äk¨Y»ÙÞBBº¶z)¯Óï‚ÿµ&‘╊ÇÄWÕÔcsŸ¼q]7ÅïÙK¾?g¿Ò¿Ð/˜“¹äþ&¾ñ¯ì©ãLfÓΠуgÿA¯¡„è×…¤õ?;Ž€©û´Ú>Óý±?l'öZøO¤|M´ðâøÒ-[W‡Jû2ß}‡goq?›æyçFÝ»Fwg<`þMj_ðXË+Û´Að} ÏoíðÃÿHzˆ<ñKÆ%¿…GýGû†»}/þ w-„B)þ€?ê?·ÿq溋π¿ ¤|Ûü´=†šßáU¢øðù$¾Ú0ŽšßüM)aªMk$sœ-7îR—õó3î?à¸V—Q˜åø(H?õ1þWWŸÝÿÁb-'™¥ƒáBø·ƒ퀯o_‚¿ •BŸ€¶ŽÿÙmÿÄÑÿ Sák‚À[ÿp¶ÿâk8áªGi#ª¶o†¨¯:mÿ^§…Cÿ‡H$‹áC+úŽÿ÷ wöð\´„E?ÁÃ1ÿá ÿ¸ó[×¾Hù‡à}¢ÿPÖÿ [?€Ÿà—|ßìå‡Mb?ô©áªIk$sáóœ=7xR’þ½LK¿ø-ý•ìF)þ þ¦!ÿÊêà.à°Ö’ÊÏÂ&OoíàöÀW¹7Á_…Ù8ø aÿ‚¶ÿâiðSás®ßøP¶#>š[éJž¢ÚHéÄfØjŠó¦ßõêxŸü;ì3,ð|'!‡ýGzÿäzð\qa%ø2d uÿ„„ýǾÿ³ÿÃöbWà¢OìÖÿ ½að+ὺŸ´| ²”ÿµ¦1ÿÙiÕÂÔ–òLà áé¿r”—õêrúüÞÃR„Ã7ÁCÏøHÇþS«ƒø,%¹v+ð•‚žßÛ ÿí{©ø-ð»þˆ%‡þ Ûÿ‰¨gø!ðÂhʯÀ{>£Laÿ²ÑO V;Ibs\-MgI¿ëÔòþ <ú4Þd? YÓºÿocÿl+´ÿ‡åBWk|cÿsÿåuZoÙûÀ·/Á+Lzf·ÿZÖßþÀ¡%øc!õ:cý–”°S–®HX|úŒ=ØÒ’_×™æ:ÏüsOÕ›|»úÿÜx®{þ0~1ÿ¸ïÿp×¼Ÿ‚ß »|°ÿÁ[ñ5FóàOÃ9ÆaøgöÓJ¨aê­9‘ñøI{ΓשÀè¿ðZùt„?Â&@uý¿û`k¢?ð\¨Oà³`ÿÔÄ?ù]VGìÿðû9?íðZßüMo'Á?…ÑĨ߬‡s¥·ÿQ<Û»’6Ãq ®UNKúõ<›Wÿ‚ÌiÚœžt_ÚÝ_ø¨îÏŽoƒfR;ÿÂC·ÿqæ Ô?à¶ú~¥E?ÁC’1ŸøH‡ùN®ŽËà?Øtÿ¬åúéŒGò­sð[áo€vø+oþ&±ú¤ïu$vÃ?£(òºr·õæx\ŸðXkv‘š?„…ÿ ×AÿÛ ¹¦ÿÁe¤Ó&Áð°;nÿ÷ {$¿~J…GÀk>«¦0þ•mðá¢MºÖŒ‡·ökÿ…mì*ÛY£‰æXHJñ£/ëædGÿÉE@àÁcÜÿÂBþã«/Uÿ‚ÙXjИ¦ø*U±ßð‚ôÝ^»¥üø!jŒ/?gÛ)Ï«i’éZƒáOÀ29ýlÿ°\ŸáX}NkTÑë,âŒágcåƒÿ„‡$„¬oøžþ@®ƒFÿ‚ÓI£· Tþïöö?öÀ×¾^| øqXg«(›ÕtÙô®kþoÂr>ÚcÓû:Oð­½…F­Ì7ûC NW…'ëÌå?áù0ãà³áD?ù]\Ö¹ÿ¥±Ö—Ÿƒ- ãÄÿî#£KÝ…_3oÅÿðZüGdÖÚÂQ§ÊÙýá׸ü>ÀŸÎ¾7ñ_í嬸Æéîõ 3³€/øù¾¹Ñ?bëZðYÉðü[!8óчó÷7ÂOø&gÁ+2š§<7g¨«Œˆ }vkIF¥éQ_#4ð¸êžþWóm~Lü‹øCÿðÿÃMqõÍ_á™×å1/ö¸·ÀõæÎLþ•ö|ð\kx‘Q~ ¶`ÅD:û‡WêD_°ì}ÿ…M ¶;›POó©ì)ûgð©tüã^elDæï'sêð9mµ(Øü¸ð\¸GüÑVÿˆòºÿ͇þˆ«áD?ù]_¨ÿð±öä’hø ?Æ—þOö>ÿ¢I à(ÿÀî¹ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üã^sñ/ögýƒ>è-®ø·á‡‡â ‘ºZ«\\8þÓ#>äáGr+Z'Vj5vöHŠ•#å'dÏßø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«è¯Ù³à?ìÇñ«Å~1¹Õ¾xvËL±ŽÏì6qÚ)òUÚmŤáÛ “Àã€9¯¯á„ÿcïú$šþñ®¬Ë.©„¬èVø•¯óW2ÂâcZ ¤6?.?áù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯¶þ~ÇŸ²ÝÕ߉üã/…úî½á[ö_=ìÑ^æÆë2ÚLBm\•%HQ·Ö½·þOö>ÿ¢I à(ÿÆ•Žœ¿áÓÕ?šÔÒ•U8ó#òßþ›ýVÿˆòºø~l?ôE[ÿ !ÿÊêýHÿ†ý¿è’hø ?Æøa?Øûþ‰&ÿ€£ükœÐü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿü·ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿Q%ý†c‹xžyþøz8ãRÌÍlªŽI$œzø ㎃ûhæ ü#øOáÛëÕÊIª5š¼‡ÈSÄþÙ=ƒ‘êeY6#SÙÐû¾‹Õœ¸¼m:æ¨Ï8ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¾ýø7û~ÊÞ#øYá}{Ä t+ÍFþ¦™­gÎqè+Òÿá„ÿcïú$šþñ®,MN¤©·³kî7¥>h©w?-ÿáù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯ÐoŠ_°¿ìïµ+ï‡? ô[-rÉEÌqÇj ݤ'|–Ò)Îå•A\{ÖÏ‚dØ«Ç~Ò¼_£ü&Ð ¦©Ì£ìÀ”cãs÷‘Sî \°¯ØªÉÝ^ÏÉôûúz2U_}Áúÿ^‡çü?6ú"­ÿ…ÿåuðüØ芷þCÿ•Õú‘ÿ 'ûÑ$Ð?ðð±÷ýMÿGø×)©ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ4ùoÿ͇þˆ«áD?ù]_Œ¿´'ÇþÑõÿ‹>/b—:ÄÇìö»Ì‘ÙZ'[FH\¬i€Nѹ·9c_Õ÷Œ¿`Ù_Qð†¹§øsáfƒi«]XÜÅg2Ûh®&XœðUÈ9¯ã†òÎëN¼ŸO¿…­îm¤h¥ÁWGCµ•äF  ÔQE}óû~Þ^*ýŽ.¼Aaý†Þ/ðξ‰#izl„±æ9 3€Z<¤ŠoÂß ô+þ›ýVÿˆòº¿3àŸ_ tϵƒ<â-"-o@ŒÝÞj–ó¦ø ­½´‡÷ƒÐÊcQþÓ þž¿á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ëø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+«õ#þOö>ÿ¢I à(ÿ?á„ÿcïú$šþñ Ãoþß¾ý¢þ"i÷W>Ô/—ÈšïûP_Es À‡ÌO²Ûìl|»÷ü Œ @пà­6Ÿ<{ðÃ_ Þê],ÞÚǬ.´"i&y$ÿI}‰ñ‚rªd<Í~Äÿà ~ÇßôItüãAý…cö%›á.€Iä“j?ƽÊÙýz˜Ha&î¢îŸä¾_ðeôãZU–íXü‡ðükHø{àí+ÁÚWÁvx4ÈDfCâ­,‡æ’VÙçÜ–#'“]‡ü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ5ãÕ«)ÉÎníêÎØEE(­‘ùoÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãYßìeûøkM—XñÃ? i¶0 ¼×$h=²Ä žÃ©íS¹;E]´•Ùùÿ͇þˆ«áD?ù]@ÿ‚æDrGÁVã¯üTCÿ•Õé?mÿb-7ÎÒþ|Ð5[•û}Ý™ŽÝO¬qed«lÄUŸÙövø?ñ;Åž#ƒÅÞÒ/4;kDw³[(¡·72J<¦)¨%U\ ú×Ôª¨ag‹Ä{‘Jö{¾ÚtùýÇ•ý±JUcFž­ýÇ–ÿÃóaÿ¢*ßøQþWQÿ͇þˆ«áD?ù]_©ð±÷ýMÿGøÑÿ 'ûÑ$Ð?ð|©ë–ÿðüØ芷þCÿ•ÔÃóbÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ5ãÞ0ý’?f üRðv™ðãF—ÁÞ!ŽçOKG· ]S‰¢•3Ê´ê¦2¨$gšêÂa}«”SÕ&×µ·ÝÈʵ^DŸŸæ|1ÿ͇þˆ«áD?ù]Gü?6ú"­ÿ…ÿåu~¤à þÇßôI4üãGü0Ÿì}ÿD“@ÿÀQþ5Êj~[ÿÃóaÿ¢*ßøQþWQÿ͇þˆ«áD?ù]_©ð±÷ýMÿGøÑÿ 'ûÑ$Ð?ð~[ÿÃóaÿ¢*ßøQþWQÿ͇þˆ«áD?ù]_©ð±÷ýMÿGøÑÿ 'ûÑ$Ð?ð~[ÿÃóaÿ¢*ßøQþWQÿ͇þˆ«áD?ù]_©ð±÷ýMÿGø×š|Býœ¿àŸ ­ ÇŒ~ønÖf]ÑÚ¥¨’æOM±)݃ÓqÂú‘[PÃÔ«% qm¾‹R*TŒ4‘ðü?6ú"­ÿ…ÿåuðüÈ€þ«`ÿÔÄ?ù]Tþ%Z~ÍúÜcðÛàχ¼?f3ÙÇ=Û÷[tQç¸ÃŸF¯·¿fߨÿözñ·Â ÄŸ~éÍíÓÜyql¹ŠÚ9Z4ÀTʳñÞ¾‡1á\F õœKJí$·>‹o3ÎÃfÔëUöTµó>-ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº¿R?á„ÿcïú$šþñ£þOö>ÿ¢I à(ÿùƒÔ?-ÿáù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯Ôøa?Øûþ‰&ÿ€£ühÿ†ý¿è’hø ?Æ€?-ÿáù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯Ôøa?Øûþ‰&ÿ€£ühÿ†ý¿è’hø ?Æ€?-ÿáù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯Ôøa?Øûþ‰&ÿ€£ühÿ†ý¿è’hø ?Æ€?-ÿáù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯Ôøa?Øûþ‰&ÿ€£ühÿ†ý¿è’hø ?Æ€?-ÿáù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü®¯Ôøa?Øûþ‰&ÿ€£ühÿ†ý¿è’hø ?Æ€?-ÿáù°ÿÑoü(‡ÿ+«Åÿhø+Å÷Ç‚Þ+øK¤|5o Üx¢ÔY¶ 5¯µyP¼ˆf_$YC¸KhÏÎ1»<ãöÃþOö>ÿ¢I à(ÿá~&ÿÁ;ÿe¿|=ñ†<+ðûFðæµ©XÏ Ž¥oo²KK¦CäÊ9¾ ë‘Þ€?Žê+©ñ¿‚¼MðçÅú¿þsÆÃõýÁ/ax«án§ñ‡ã÷„muȼRñ&…i¨ÄXGeí÷AN1ö‡l!?À›‡ÊàÐ_ðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÕåßÿஞøÅá9Sÿ‡æÃÿDU¿ð¢ü®®+]ÿ‚ÂhºßŒ¼7ñ ÷á„—¡EuGöÆØá¸› ö³sö2$&<¢Çå „ÝÈúûÿ 'ûÑ$Ð?ðx®•ûþËzWÆícÀÚ÷Âý}7YÓáÔô}Öh¢#ò®  0Xçl€ ¯?/çý䩽T_Ý¢vó³+8Ž_uKkÿÃ~?‰ñü?6ú"­ÿ…ÿåuðüØ芷þCÿ•Õú‘ÿ 'ûÑ$Ð?ðð±÷ýMÿGø×œt–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ4à þÇßôI4üã@–ÿðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWWêGü0Ÿì}ÿD“@ÿÀQþ5åŸ?gÿø'_ÀŸ Iã/Šþð·‡´ÔÈŒÏn ÓºŒùpB¹’gÿeœrxæ€?ŸÚ“öøûGKsâ]#à‹x ÆS’ï©éºâ4z›«?°"JÇ’]Z9 ûÎÀb¼Kà'í9ñ§ökñöÿÂ˧G+º°—÷Ö€q‰íØícŽ®£î²×¼þÒŸ´ÁOŠš™ðì¿ðOIð®›u(‚+ã`³ëw¬Ç °Æ»Ò 瀩¾CÆyZú«öVÿ‚BüAñÿØüaûF\Ëà½öÈšDN¯p½q)!’ÕO£“¨(‡€?Mÿbø(ǃÿjëµð.±áû¿øêÞ4ÑA·Zlȃæ‘.O?ÙŸh…Y$c_¤µæ ¾ |/øáX¼ð§Ã¶¾ÒãÁes,î<ÉæbÒLøþ9Ž8Î8¯O Š( Š( ÿÓýü¢Š(¢Š(¯?k©d‡öbø›$,QÆ…{‚1‘_E×Îÿµ½µÅßìËñ2ÞÖ6–VЯpª2N#$ñô â.¾ºý˜?b¿Œµ„ZÕïÄ´·Óô"±Ïu{)Š33Ë`1-ŽOŠù¾›ýž?k¿ÿ²ùÕ£øQ«Emk­76×P-Ä-" ,N6¸‘Ô ñ¯ˆÿ|Qð§ÇÏÿ[ =kA¸{k˜ÃPéÝXpTŽAî+èÿÙöøÓûVizλðé,í´ÝÄ2\_LbG—p0“Œq‘_2øãÆÞ'øâÝWÇ>2¾}GZÖ§{›«‡À/#œ“À€^óû=~Øßf-[LøS«Ãmc­óÛÝ@· Tm lm|q‘Áã â€<Ç^ ñÃëñu¯Øµ æKK¨²l‘œÁ¨=Á¾§ýšÿ`µ/…5o|<†ÊßGÒçkA5ôþP¸¹DY(ÀV'j²åŽHó“¼[â¿xëÄÚŸŒ|Wzúޱ¬ÜIuuq'Þ’YYŽ0°Àâ¾øûj~гG‡µ_ |,Öâ¶ÒuiMÃÛÜÀ· à Fš-ßuʪƒÔ£Šù·ÄþÖ|âM[Â>"·6z®‰w=Ü-ÉŠâÚC¨qÝ]H¬:Õ×u½WÄÚÞ¡â=zåïu=Vâ[»©ä9ygÌ’;嘒}ÍePEPEPô«ÿM–Fø)ã¸Y‰D×"*¹à· {àWí=~-ÿÁí®#ø#ã›§–µÈÕŽ¥ºîûdgë_´”QEQEWó5û#|Pøyðþ Kñ—Å?+ÿ‚cþÇ~4ñF±ãøNî}W^¼¸¿»‘uKÔqu#K+YB¨.ÄàA@æ­ûþÆš,i¼ø±£Èž–Í-Óß0#·é_Œÿ·Oí¹/í¯w¡~Í¿³F…¨jÚ]Ö£²Ìöû.5;”ÊÀ°Å’ÑÛ¦âìòí$à°EB[õ/Oÿ‚VþÄ7+q'&»ÛÑ&Õµ™õ!n?Cǵ}cð§àÁŸÖsÙ|&ð~Ÿá¥ºÿ]%¬CϘ‚Ißt®`Ì@í@—ì™ðMÿgÙëÁ n%Ô4{B÷ïF¾º‘®.v1”YdeC•Ò¾‹¢Š)Í-€F¥¢€´SLJzóRQ@šOB/"?J<˜ñŒT´Us2=”{}ž,ç¾D^•5s±{v!ò#ô¥ò#ô©h£ØÇ±‘¥8CíRQG3J=ˆÌ1žÔžD~•-s0öQìEäÆ;QäGéRÑG3eÄ>D^”yúTÔQÎÃØÇ±Š1Ú*?J’Š9˜ýœ{ýž,ô§y1ŽÕ%s1*QìD`Œö¦ýž/JžŠ9˜{v"òcÆ1G‘¥KEÌ=”{yúRùQú ’Š9˜{(ö!0F{Qöx½*j(æaìcØ‹Éû´c=ªZ(æaì£Ø‡ÈÒƒoíSQG;±‡b³ÅéKäGéSQG;cÄg‹Ò—ÈÒ¦¢Žv5FˆM¼GµÇjšŠ9Ø{v!ò#ô¤û<^•=s±{v û<^”¢ÇjšŠ9Ø{v"ò#ô¤û<^•5s±ûv!ò#ô¤û<^•=s±{v!ò#ô£È‹Ò¦¢Žv?cÄ>D~”"ªj(çaìaØ‹ÈÒ&?J–Š9˜{(ö! §ùkŒv§ÑI¶÷‚[!»E(¥¢‘aEPEPEPEPEPEPÌÿi¿ ü#‚]JÙ¬x¡—åµVýÕ¾G pËÓÔ ùû †¯ÈßøßÅuéüIâÛ÷¿¾ŸÌp¨½’5"Ê;õÍ~ÖüJø ðËâ¤o/‰4¥‹Pa…¿µÄ7CÓ.ŽÁÃØWå®­û6øÒù¼C«ü6…üM hº„Ö)2…K‰ÌyY>b«€¡%ˆÈ^ ~½ÀØÜ¶'ov§W/>‰ì½7õ>;>¡‰”»Ç¢_ª=ïö|kž1ÖÚÌþO'ø×é~dþÁð]XxËÆ}ì/o¿£ûÏ`¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ±¼C­EáÍ÷\žÚâñ,£i 6‘4óÉáŽ5å‰ü»’MlÑU“M­ïmůŽ´·¾,\O¢Å¿Cðê1Qcòí=n_‚Ç?ÁÂBFêù¢¿\j_†žñE¾‘ce¤ÆÅä|,ðl?óÏFÓ—ò¶A]ý~˜Jõê?7ùŸw‡V§Dó‡?âÙ|XÖ¾Kû½Åf·¢g…IIÿNµ_÷[ªŽ“ë_G׌|qðž©®øN/x]â¥ð”ëªé¤]àæH9+4yR½ Æk£,©'BoÝžž£ù=ü›3ÅEÙTŽñüº¯ë­g¢¹ø³Kñ×…4¿hͺÓT&Qœ”'†Fÿiopk§¯>¥9BNVh茓I ¢Š* (¢€ (¢€ (¢€ (¢€ þvà¦?ðOO§ŒõOÚàV‹.±¥ëL×Zö•f†K›[³Ì·pÄ£t‘L~yB‚Èû› ÙýQ@À+£FÅe$F#±®Â> ñgüCiá?iZî³~Û ´³…§šCìˆ ÀêO@9$ þܼmû:üø‘|uOü;ðþ¿~Ç&æóM¶–à÷æVMä{n®³Á? ~ü5´k‡~Ò¼1lÿz=2Ê 5o÷„(¹>æ€?5?à˜¿³û:Yø²×ÇvÆ‹7kn5œ£-®›"¬°Åo")“&wSê«ÑU›õ’¼ ãG‡õ]*m;ã'ƒ 3k¾VûL ÖÿKc›‹sêÊ?y\08‘^Çáßi>+Ь|S:•ÏbÕ®üW ¡i Ns.ž§’HyCûè ÷ÇêxÅŸkzÖ­kðgáüæ{ZŒÉxœÿeé¹Ä“t–O»ë“œŽ } æ°Ø¥:Iyßdº»î¾^šìy¹®irÏånçâ ~¦~ÁzÙ< â?2áµA-ÁõKXƒôÌÌ*ŸÅÿاB¾ÑâÔ~æËS²…Qìç´wžZãpv?$­ŽOÜc×o&¾„ýš|¨xàÖ…¢k¯g©q5ÌR.×Y%™È =Bm…}ÏñF–Z„µrI§ºëúS•U£ŠýâÙ=zv=âŠ(¯É° ò/Ž^¼ñoÃmN+¬iE5=9Ôe–îɼØöû¶ À«×h­ð¸‰R©±Ý;™Õ¦§Ôäü â»?ø;Fñ}†:µ¬sí;‡Î‡Ý*}Åu•ó¿Á¯ø£|[ãO„2üi—ÚÚZôÙú‰.Q÷a›rŸs_DVù–4ëJ0ø^«Ñê¿0Õ ›ß¯ªÜ(¢Šá7 (¢€0¼M£Ýkú öe©\hóÝÆQ.íJ‰¡'£!`GõÇB#ñ3ãgÁïˆ |C,ž0i5;{ùêe¤K“×çfÉY1ÕX穇5ûœÌ¨¥Ü…U$ðò´zu·í'â—ÔõhŒÿ |;,‘YBIUÕïÔÞà‘‚`‡%cÁù›'=T}§gu0RœÚ^ÏíwòI÷}¶Ýô¹âg8×QŠøº~·ò?«÷óáFÿ·Ã? ø}—d–Zu²H?驌4Ÿ›’kóûâìeªè^6Ò/<Tð½ýý¼W1Ýqe²ª¹oùé‚Náó(ûÂÇõ§½n;ÏhbèÐXy];·å²×ñ92 èΧ´Vz ¢Š+óS邊( Š( Š( Š( Š( Š( …?kŸØàïíd£_ÕšO øÖ„PëvH®Ò"Œ"]ÂJ¬è½¾ep8Šü‡×ÿà‰ÿ´ ®¨°øcÆÞÔ´æly÷Myg(¦·œ~Rë_Óøwð[þ·à¯K.­ñ³Æâ[Ÿ)„:t&ÚÒ)вK#³I8RC $|Û”•¯Õo¾)»Ô<97|Evž$ðK&™{j8×óÆ #š0 àpÅ{u|éñ~ÆóÀž Ó¾;x~“û)ž½oɹҲdÀêöÍóös“^¦_ÕRÃ=åð¿ïvô{zÙ÷9q Áª‹e¿§—åsèº*¥…õž©co©ió-Å­Üi,R!ʼn+Ür*Ýy4ìԊ(¤EPEPEPEPEPEPEPEPEPEPEPEPEPEP_8þÐ_´‹ðkFûžËïß!6¶¤åcSÇ69AÕÏ$}_?|Jý™¾üO¾¸Öu›9¬µ{œo½´™’F `nWßÀ}ÌãŒ×«“K DeŒ¿"íúùz˜ÕYÓj…¹¼ÏÅßø‡ZñVµwâݽö¡|æI¦å™è8 ý ý€µ}Öž2Ð]¿Õ½Ê÷ĈçÿZðGöOñn©âÏh_/aÖ­|-,¼—[I4ÓGæ4QýèËEÀrÌ£$qéë?²'„SË ±¸Ù(Ê7˿Øx—…ÄåµiQšºI¥³KF´ßcã2Êiâ£)§»WûÖþ§é|ùû@ÛÏ¢išÅ}= ÝøPK©B ³é÷‚ò1õF í¶¾ƒ¬ícJ±×´›ÝSÍ³Ô ’Þdþôr©Fˆ5ø®ìkF£ÕuóOF¾jèûŒE.x8¯éô-ÛÜAwoÕ³‰a™UÑÔä2°È úSW„~ÏzµñðLÞ֤߫x"îmrx/¹ÿGî´%p{à×»ÔãpÞÆ¬©^özèþkQЫÏ.áEW)¨QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEWó×ñÛþ ¯ûYþÑ_µ_Š@ý™¿b€ÿ²ÕŒsø#HþÒñ+&ÙõÝ@,×ï¸a„g`ŒôÙ\Œo,Fkëú( Š( Š( Š( ÿÔýü¢Š(¢Š(¨nm­ï-å´»f‚ud‘VF*Aà‚8"¦¢€?1üiÿ“ý‘¼aâ ¿[Ùjº¼v‘í´ëЖÁ˜äìIc“`Ïð©v¹?øsgìŸÿ?Þ$ÿÀøù¿Y( É¿øsgìŸÿ?Þ$ÿÀøùøsgìŸÿ?Þ$ÿÀøù¿Y( É¿øsgìŸÿ?Þ$ÿÀøù¿8¿à£±?ÁÙÂ>×¾ CP¸ñõÍ´ë©Üù¨© JêPB°ryÉ5ý@×å—ü#ökø»ûHø'Àº?ÂM%5k­Qºžé^xáÙ°ª©Ì„g$v Á¯Ø›à߀ÿhïÚ EøUã{I,ôF ©d’ÂgŽpÐD]v´†EÁ#œ©¯ÜïøsgìŸÿ?Þ$ÿÀøù¾Gý€`ÿÚ[àwí1 üCøá¸ôíÊÚò9f[¸e!¥ˆª ¨Äœ“_Ðå~MÿÛ?dÿùþñ'þÃÿÈôÛ?dÿùþñ'þÃÿÈõúÉE~MÿÛ?dÿùþñ'þÃÿÈõ5¿üÇöK†t–[ŸΊA1½üaXz°+cèA¯Õê(Ï~|+ðÁØü?øm¤E¢èzx>\d–våØåØòÌÄ“^…EQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEä¼c¨øSÁOiáÏ›Ä~!š=+J@p~Õtv‰=„K¹Éè03Ö»x;Nðƒ´Ÿé|Á¦@±—Æ ’}é$>îä±÷5äÿ‹›ñ·RñkþóBø~$Ò´þé&§0l˜{Ę‹ñȯ£«ÖÇ~ê”0ËŠ^¯eò_‹hä¡ïÍÕù/×ï’(&—¦G¨¾°–‘-ü±ˆžà"‰Z0r¾7 À«ôQ^S“{I|ésÿçãä7ƒ÷z/ĨvHõ{ÌgÐyÐåqÕœWÑuå?|yão_Yh䦷¦´zŽ™"ýä¾´>d[} `¦}ס•ÕŠ©ìæýÙ®WóÙüŸÈçÅA¸óGuªþ¼Ö‡«Q\WßÙüBðFã0u(äAÿ,æ_–Xÿà_»Zâ­JTæá5f™¼&¤”–Ì(¢ŠÌ ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦»¤hÒHÁUA$“€êI§W€ürÖuR+á†&1k>5v†iSïZi‘ów9ô%>EÏRÇ"º°XWZ¢¦»¾ÉjßÉje^¯$\Œï…hÿ¼q¬|m½RÚjoÒ|<¬8q>'º÷ž@@<£ŒWÑõ—¢hÚw‡t{-G„[Øéð¤Æ:,q¨UëÞµ*ñø¥V¥â­¢]’Ûþذô¹#g¿_Q¨‰,q¨UP`:N¢Šâ6 (¢€>pðü[/ŠÚ×ÂÙ¿w¢ø“ÌÖô>Ê’þ›j¿î¶$U&¾¯øåá]S[ð¤>%ðºçľu]8ÌúØ9+4yR½Î3^‡àßé~8ð®—âí·Yê°$È3’¥‡ÌþÒ6U½Á¯[ûÚqÅ-Þ’õ[?ûy~)œ˜rN—ÍzÀ…ŽšŠ(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ (¢€¼ù§Âñf¾&KðêÝøOÅòKy¡±û–·¿zæË=_ýdCÕFI5ôµy÷ÄÿZüFð…ׇ¤”Ú^#-ÍÒðö·Ð̤r6·•$w¯G/ÄF-Ò«ðKGåÙü¿+®§6&›iJ­¿Ëæz å?|{u㟠T^2@Àã>èz¯ó5[i!V=Lf7ÿ€8Vü+šø'âû¯ü7ÒuW+«Y«Xjß}/,ÛÊ—w¡b»þŒ+Õ­ûÜ,'Ö•ú;¸þ<Ëî9!îÕqèõùìÿCÕ袊òް¢Šñï‹_u ÚÙxWÁÑ-JÍo¦ÛžV,Þ]M×B>cž§Ž™ÇF :ÓTá»þ®ü–ìέU¹Hä>$jú—Äß7Á\<q¢Kâ]B#ƒmhü­œmÿ=®û©ž {þ‘¤iº—i¢hÖéiccà H0¨ˆ0®Cá§Ãí;ádžcÑmekËÙÝ®oïdæ[˹y–g''$ôà9ë^]XìLThüü_Y?^—ÌËIë9üOðòþº…Q^iÒQEQEQEQEQEQEQEQE żvòÚÝF³C2²:8 ¬¬0TƒÁpEME ó«‰þxÂÿàn¯#5€Y5 Í!$ÉbÌL¶»W·bp3’‡<+é ò/Œ~Ô<_áØ5? 8·ñW†æ†‘7ý7ïBÝ2“/ÈÀœ àWIðãÇZÄoØxªÁ p¥.-ÛïÛÜÆvË ƒ‚ 0#20zõñëÛAbÖïI‹¿ý½¿ª~GÜ—²/Nß/ÊÇsEWvQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@cx‹\²ðÆ©xRm¶º]´·2Ÿö!Bçä+f¾|ý å“\Ò¼=ð²ÑˆŸÇœ6³m8e°¶?h»qôEû5veøeV´iËn¾‹VþJæ8ŠŽr[þ½ _ÙóC½ÒþZk:ÂãVñLÓkw§¡3_·˜¼F#ظíŠöêŽ(£‚$† `*¨ÐT•ÌK­VU_Wr¨Ó䂊èQEs;ßÿÅûBÙjC÷zgÄ[i1è£SÓ†èY@^QGr+èŠñž¾×~Ýê: ÿ‰×†¥‹YÓØ ‘qb|Ìܲo\{×£øOÄv>0ðÆ•â0æÛU¶ŠårTH¡¶Ÿu<q^®3÷”)×ê½×òÛÿ%ÓþÝ9(ûµ%šùïøëó:(¢¼£¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÕýü¢Š(¢Š(¯çÇ¿ðY‹>ñLj<+kà xt}BêÍ$y®C:Á+F°øÉ'ýWâ?Œ¿à‹¾ñ‹µ¯ËñNöÙõ›Û‹ÃÒ£a¸‘¤*Ü ã8Î9 ›ÿá÷?èh?÷úëÿ‹£þsñ‡þ‰Öƒÿ®¿øºöÏøqׄÿè­_à¢/þI£þuá?ú+Wßø(‹ÿ’hÄÿá÷?èh?÷úëÿ‹£þsñ‡þ‰Öƒÿ®¿øºöÏøqׄÿè­_à¢/þI£þuá?ú+Wßø(‹ÿ’hköRÿ‚ª|Ký ¿hü×|¤iv>#–æ9nm¥¸icZÍp ‡b¼˜À9 }}ÿý±çãý­þÿ]ñuíŸð㯠ÿÑZ¾ÿÁD_ü“Gü8ëÂôV¯¿ðQÿ$ЉÿÃî~0ÿÑ:Ðïõ×ÿGü>çãý­þÿ]ñuíŸð㯠ÿÑZ¾ÿÁD_ü“Gü8ëÂôV¯¿ðQÿ$ЉÿÃî~0ÿÑ:Ðïõ×ÿ_º_³/ÅOã§ÀüZÖlaÓo|Ih×ÛÛ–h£++Ç…/–Æ<×äÏü8ëÂôV¯¿ðQÿ$×ëçÀ/„–ß>xcឤúÄ>¶ku»’! LG“qŒ3ûøÆãÒ€=‚Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼³ãî| à[»ý%<íoPxôý.ËK}tvD=vœ¹šõ:ùÆ×þ.ÇI¯OïËæì¿›6£ËÞŸð~Hõ?†~¶øwà}+ÂP?%œY¸›©šæC¾i <ÎIç«»¢Šâ­ZU&êMÝ·vo(ÅEl‚Š(¬Š (¢€>tøÿ÷âç‰þIû½/Ä øƒH­!Ù{öùdÃªŽŠI¯¢ëÀþ?iwÖz™ñ?BˆÉ«x èj*«÷¥²#eì9ì±ÿv½³IÕ,u½.ÏYÓ%ÙßÃðÈ:%ð|ëªéøë!„~úŽJÍT¯s¶½<²¤\ ¿vzz?²þO&Î\T]•Hï˪þºØöª+›ðwŠt¿ø_KñnŒûìõHtç%w™ý¤l«{ƒ]%yõ)Êq’³GLdšº (¢ aEPEPEP^Gñ ãçÁ?„ú¶‹ñ7Ç:?…ïîáûD0j7±[K$%Šyв0%w)ék×+ðûþ ?4þ™ðwáv•mþ!ñ±röí´UQb·TÝÁd’àqЕö Ó­'ö¸ý–µ»´°Òþ-øVk™X*Gý±h¬ìÇT4ƒq'°É¯¡QÒTY#`èà äzkówö¹ý–?fŸ~ÇÞ5{¿èÚ}ß„¼73éú¤0[_-í¤µ&â5YË0Eu,Cî 䚯ÿ™×üU¯~Ç(ñ4ÒÜE¦êWözsÊÅØbeØ ŸáI ˆ£  ` ¯|kûK~ÏŸáÛÒÝŽXÃÙÍë£üÿàþ~§îÃïÚoö{ø­¬ÿÂ9ðë≯jÌ­µìMráX¤D‡p£’T+Üëñþ Ûðƒáç øÓðãE³ð‹´ÚZ[ÞiPGe!ŠH'™AòUw4R@<¯ÌS_®¼_yñàÿ¼{¨€.üK¡išœÀ.Ð$¼µŽfù{rçŽÕæG™Mûe~Éöó=¼ÿ|0’DÅYN«ne8 üýz7>6üø£<–Ÿ ¼s¡ø¢æó$‡MÔmîæDé¹ã‰ÙÔ{+ñKödøyàðS¯Ž>0Õ´ SÂþmB%·žÚ)­ñfŽÁÄêPïX§62[æëÍcÿÁF¼àO€ÿ´ßÀþÏz=¯„üe©Þ;Ü[i%¤ryW6ÑÚ±‚ª ÆI£r÷Š0ÙÁ èV¼ïâÅß…¿ l"Ôþ'x³Lð½´äˆ›Q»ŠÜÊGQ»r;…ÖíñƒFøðkÅŸ5À²CáÛ'š(XíOˆí Ïo6fDÏlçµ~Z~À_³©ý¤ÿí«ûTÄž6×üQu:hvzŠyÖ6––ò´fT¶“(:@„WxœýOøkñûàŸÆ9§µø[ã#Ä÷6©æMoew·Ç»Þ|Å\ñ¸®3Þ½z¿?à§^ð¿ìÅñ¯àíð{M·ð·ˆ$¼¼‹§B–ñÝ%ƒ[2ïŠ0ªÅÒi"“#çF x¿V¿lÿx¯á‡ìµñ#Ç>‘à×4Ý)Å´Ñÿ¬§u…§OF…\Èb¹  ÿ~Õ?³‡ÂífOxûâ>‡£jМKg-ämq=¤‰ :À€®Ëá߯„!š†2Ò|R-€i—N¼ŠâHA8HÑ‹¦OMÀf¿4à–ßÿgß~Ìv?oü?¥x¯Æ:Ýæ ºíÞ¥oõÌS¥ÄŠ0Ʀ.B7ïÞÙÈÇÓ²·ì[áÿÙâ_Å_h7pK§øòò4Ë!h†—e“Jm,w Ò¨À ‹@çã_Ú[ö|øqâü%ã‡õ«UF–ÎûP† ãYT:Ø0ܤ‘È ×)ÿ ¡û%Ñ_ð¿þ m¿øºü³ÿ‚Œh?ÆÛÏàÁX­"šY…¤º›ª)w³º¾&Dã-² i)à÷¯Òehú‡Á›Ÿ ø;FñOŒ´»ø´»sec Ì!`nm£UYÏ~rñò»s‘ŠúÀüñGC>&øqâ é+3Àn´ùÒâ*Y7¡#p â»Jù—ö@ýŸŸöcøáÿ„Ww°êz†œ÷sÞ]À¬‘Ï=ÍÃ˹Cs…B‰Ï÷kéª(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+åû²~?øå´ÄùþøBä}¥‡ú½_SˆäD;5¼»;qÈÁßüM­x“Zƒà§€n ¾«›ªßGÏöfšxwÏi¥,c¯;¸á«Øü/áÁ¾±ðLJ­Å®Ÿ§D"‰ êIîÌrXžI$šöhÿ²ÓU_ñ%ðù/æõ{GæûSýì¹>ÊßÍöÿ?»¹¼€)h¢¼c´(¢Š(¢Š(¢Š+ç ÿÅ ñßÄžoÝé¾6·]vÈtQy!½AêÎ6J}«èŠùûöƒ´¹Ò´=â–™{ï_Ç|Á>ûØËû›ÈdzF۲׫”¾iºi«|÷â—Êç&/Hª‹ìëòëø@ÑUí.í¯ía¾³KÂ,‘ºòeHö æ–êêÚÊÚ[ËÉV F’I…TEfbx’My|®ö:®rÞ<ñ¾‰ðóÂ÷ž*לˆ-@ s$ó7Å÷wnüOšóÖẽø¡ñ ñˆÕCEÕtÛ sœYé·¬„}çëœdòÞ¶¹øÝã(~)ëQ2xGA‘×Ö’>Ó2¯¨È§ÜbGæÀ<·ÓUìb_Õ©¼<~7ñ>ßÝýeç§My)þö^Ñì¶ÿ?òûú…Q^1ØQEQEQEQEQEQEQEQ^mñ†-RO…¾(}ÄW½¶°žâ=VÖ8e–ÔÛ¯š\%ÂûD±êÖ1, wÊZÏæF–’7•*Ä2‚+¯ÿ‚‡~ÔšÁÿÙ|Ñ41âÿˆ_€¶Ò´¿˜ˆ„’yq]8O™›ÏŠAw×~_‰9¸Ôø%£ôïêž«ÐÃIÉ^;­Wõç±ú=E~;xáíåã øŒjÿ´ æƒñSª¡4¦XI§`ÄÙZb¤J³´ÌU°Ù$?Vúþ ßû\kßµoÂNçÇVÑ[xÇÂ7ie©5ºypÜ$¨Z €™;öºº”2’ y«Ð•9rÉOU÷£JsRWGèùáÿ.ø‹ñ/à÷ìú>(ü-ñµ×„um&þÞÜC³Å~/P¤ŸhŠFS«:”#¸`x+ã? ¾ÿÁ@>!~ϾøÇ ~Ñ3[xŸÄBj¶ú5ï‡ôÙ-\\/›Fà¡+æFTïòŽ éŽkÏ×Z+ó¿þ Éû^ø§ö±ø_­]|@µ·ƒÅ>¼ŠÖî[D1Ãuè^¼²NÇ;]\·+•£öÀý´¼eà/‰:ì½û5éx“âï‰Ì`µÀÝk¥Ç2ïF‘rHcS¸„Ž0Ã~–Q_“þ~Ýßþë_ì?h©üCâ¿ ÙK«_éO£Y&•<Êf¸ŠS#ljÛ[b—ÆBA\þÆ?´wü5/ÀMâåœz~±æÍaª[ß%/mˆÞbÜY¶:2H ’T6ÒN2@>«¢¿$¾$~ÑŸ´í û[k²?ìÍâ oé>æñ‰$´KÛ¼ÂcI’Þ)FÁ²IR%QµÙƒ7˜W1ñ;Âÿ·ìÿñ“áF‘à¿‹ú¿Ä_ xãW‡OÕóG¶”X/šIˆŽ@‘4NÌ® ìa“Æ@?e(¯”ÿmx¿Á¿³OŒüyà_\ø;Yð½«j0][Eo7œñ«m"ÜÇ"ì•™A*‚2Êß¾~Ù> ø?↻ûKêš犴Ø5/±Çáý2tŠ;‘æC‡! nŒ«thõVŠüÀý†µ¯ÚºûãwÆ_ü~ñ…çŠ4?O—§M=„PÞK,Ò°º‹Ê‰ ýÔK•Þʾf2x5ú@Q@Q@Q@Q@Q@Q@Q@Q@|ñ¡ÅkûBkºñùôÿXG¥[žªo¯}r꼑…¾µíÞ#×,¼1 j^#Ô›m®—m-̧ýˆP¹Ç¹Šò¯ÙóC½ÒþYë:ÂãVñLÓkw§¡3_·˜¼ubãÚ½\îèU­Õû«ç«ü¿íã’·½R0ùýÛ~:ün¢Š+Ê:Š(  0 ŒƒÚ¾xøO„õO|œíO ß­8úf¢LÑ*úùn]Xö8ôE|ïñ;þ(ŸŠ^ øŸÉg|íáÍQ»y7‡}«·¢Ç8äžÍŠõrßÞ*˜æW_⎫ïW_3“î¸Ôíù?é?‘ôEQ^QÖQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Î~+øä|<ð.¥â8SÏ¿ °XÀL÷“FS— ?„oÂo‡¾Óü?rþ~¢Á®u ÉËM{p|ÉÜ·˜àî^s}ÿ?㾘?yáÿ†ª·S÷Iµ›•ýÊú³Å—Èå\à×ÑÕëb¿sB4:ËÞ—þÚ¾ííï#’—¿QÔè´_¯ã§È(¢ŠòN°¢Š(¢Š()à†ê -®PKÊQцU•†#¸"¾~ø<Þ›Ä_u-7ƒî·X3œ´ºUá2ÛŸ¼cË#c¦¯¡«çO‹ÿñBx׿àù--$6´GOìû×\®»øoøzÙgïðÏímþ%·ß¬~g&+Ýj¯mýÿvÿ#èº(¢¼“¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+ÄZþ—á]ÿÄšÔ¢ 6žgôHÆNrzÜàVÍ|ßñ$ŸŠ4Ÿƒ–Ç~¥ùZ¿ˆˆû­6m,Ûþº¸ÞË×`Wn_†UjZnÑZ·Ù-þ}›FŠ®Ów¢õ6>h¤Ú~¥ñGÅQüAãyVñ£nM­ŠŒZ[ ôÛº·<Š÷Š@KYã1NµGQé~—EòZF’„TBŠ(®cP¢Š(¢Š(¢Š(營ñlþ(kŸ &ýÞ®ùšÞ…ž7ŸôËUíò?ïGE$×Ñõâ$ðlãT°ÇY|¡ûûsŽJÍT¨êvפøCÅ_|1¦x³F}öz¤ <~«¸r­þÒœ«Äõ³ÞÓŽ)nô—ø—_ûykê™É‡÷$é|×§üøXè袊òN°¢Š(¢Š(¢Š(¯À/ÚÛþÇ/ø*/¾|)m1µ¿‡wpl‰›MŠîÙ_W/p-ÿ{† az¶Ðp2k÷“ÄzÜ>Ð5/ÜZÝ_E¦[ËrÖö6ïuu0‰K‚ƒ<²60ˆ –8¿ bïøZ7_·ïÄŽ>xËÃöÞ=ŽúÛH¹¾Ð/£·³YîahVêgˆ$[m¡ï'nr3È ¥¾&þÈ_¶íK§…iOŠº‡àh¦Ž{­#ÁöWíPòÞa²Ê—ó[ å’¯Ñÿ†ÿü%ð›Àº/ÃX® èëmk%ˆPIff<³»îÇ–bIä×m\Å¿ÃOjþ6‹AÕ øƒñJïã'ÃßxwÄž;–Þö-CRÑ/-,Yb{‰nÜMª;É0!Iù°1È«_µç>+ü%ÿ‚…ü;ý©>ø_ñ¦ŒöP `hZmÅü‹å¬¶7(|”eÖr!Œ9]Ì1@¾ÿÁUµøþþÄwÞðÄI¦XëÚV‡VãÊH­c&)pl¶Ù´q´‘Ò¶ÿf_ré?±Â[D¶^/Ò´Ø|Ka14·Z7rÃ!ã):ø{à¿Àÿ)›^ñN£ý¡q§õ¶÷…Žk ƒ f–áG8Áû[yq¤|ø5É!´Ï‡žfËp MŸªÅ_’g‰þý¥ll¿ÚëCµÒï.ñ"û ¶‹¥ÜßÃij’Æ×-rð#ˆüȉŽ5<±b@µi‹£fªAZ½ºé}½Wù>¤ÑýÖõ[Ÿ›?ðMþØwuŸðM-:ÿÂß²¯‡<®øO\ð–¹áé.×P‡[Ó.4çš{«™nùèžjlu—¦žE}ÿ\fÇâ‡üÏÆWúo‡þµ‘’k7s$yƒN€*¡ªî¹ U¨úÕð‡ÀÖ ¾øGáæ›‚ßúUˆQýè!Tf>¬Ì 1îI5ð÷üÛöYñ§í+ðsI¹øg½ñ_‚ï^öÚÏzÆ×vó&Éâܨ ¨ê ¶•êEsÉû~üZºð%®‡£~Ξ<¸ø®ö±Å%Ö-¾áZgº$8·ÝóŒÆ¹)eûôòwüãþ3öùø3û4hMöȼ<ð6¢«ÿ,Mü©wyœrÆÞ9?Wï¿ èÞ)Ðõ xŠÎ-GJÕ`–ÖêÚeÝÐL¥$GSÕYI¿7aoØÓÆ¿ ¼Oâ/ÚGöмM[âçL¯*+¬É¦ÃpÁåMëòœ€­å’‘¢ˆÐ•-ž7ã¿ÆïÚsà÷íÃmâÛ_øÃÅŸm4htÛ‹mÖ{ËYe™ ïyQæ#øŸÃÚN¯{k>³¯ø³Om"ÒÚ góG¹›y›i/…Ú¨Û²?H"‡Oý–ÿgm7Hдm[ÅÐøH´°¶²Òlä¼ÔoäÙ!›2?Ìçî ,Äí€?´¿kÿµ·ü+â~¥á¿j O‡°M¾«¦ÚâÞK‹Kh£2Qæ»ÎÇŽ›±_c~Ïÿ±ßÅ߇_¶÷ˆ~/üKñ6§ãßXxxZhšæ±q—MutÑù°–FhÒó€Äh‡ÌÊŒç?~À~4øû5êÿügñ‡àOÄí[ÄÞ>¼·ŸÍÒü+s4hˆÓË.æ¡`ÒK6HŒ(澄×~*þѵí_ðÚgÃü2øSámOû_P¹ÖtÛ½4ßMiN«vÊ<•Œ˜Ä1Åæ6ã#Î@PÙz(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ ó/ŠŸáö–6ÿÚZþ­(´ÒlïÜÝ¿ =‘3¹Û€|‘]Ÿˆ¼C¤xSC½ñ¿r¶š~ŸK4ÑU}rOI Mx—¿jþ2×äøßã›f·½¾ˆÅ¡ØIÿ0í9¹ GA<ãæsÔ)Û Qéà0ð³ÄV^äz3è¿WÙy´sW¨î©Ãwø.ÿåævŸ >¿€ô[‰õ‹í/k’ý¯W¾=g¹oá_H¢lkÀœ šõ:(®üeñçí1ãOÛ“ãÎ7„.<@³Å¢iJcºHæT…d’6Ñ!¶ŒB»ÕZBKí ïÖ_xNËÀ^ðï´Ü}“úu¦—i ¼}WY@–?ðWÿˆð‰~Éø^6ÏãMbÇO*ÃnZõÛèÝÿ¼zñ„ßðP/…³¯À‡¼;à¯jÞ=‡J³²µÓgÓÿ³aºÕn2~úᕼ§¹vÒ7'#ŽõÎÁN/­>.þÖŸ³çìÔ&W¶’ö AÈ «^EÎ?¼°Û»×î+Þ¿à°ß"ñWì¯ííÃ_xX´º(ùãµ¼&ÒU¨V’HXû ô ƒöý“>'ü-ñgŽÿhßÚ¡‹â7ĉei,mäYVÊ ‰þÕ2»¡dß$¡0ˆÌ¨ˆâI òWìË-Çíeÿ?ñ÷Æp‹ áŠÝC¥FNøÔ@íac€znuÏ´œo×ÙOâzüdýœ¾üE{±{yªhö¢ú@A&þ݃Žât~+ò‡þ 9ÃO¿´ÂH,¼cö»Q $ÓŧÏx· €ýïõ±?Ëœ« ÐêOÇŸøqå~ÑZ-,¾¶—ûjàÝè &ÿA5 à`ç€~eÁô=E¼%ñsÇw@ýŸZÕ4ÛEoá3YÅ<ÒàtévŸ¥zçü×ö”Œx=dƒ®Þ!ø“ñX´û›-=„³YÙÈÊÍ~ì—# ±±ˆ‹»mK{gìQðÓRý”<9§~;0x¤¼Õ-ÿ¶íobŽâöXÓûBØ7ñ4 £aêÑÇ ôS©ˆ¦£Ö úµ}¼í«ô¿Ds>Zr¿ó?Çþæ|ÿ­ñÂ/ƒþü#KŸ$kš­Æ©p@fØ–q­¼lÊ€³nœ€$¯8¯YñÿüáÔ WáWì ëÿ¼XtÕÒ4”³Ñ¯b‚Ëd"æ—Ί7>P‚ªHŠŒšñ?‹÷öŸ¿à°Ÿü²¬ÚwÃ{kwêÆ µlVó^$oB¸= ~ëלtŸ›ŸðO_Ù³VýgkVø¤RÓÄZÛ¾³ªÂ޲-µ¬?ºIVtPîåI]ÎTqùþ [¦êß¾=üaý°¼n¢MJòscf¤ï½óyÒªÐAp‡®Æ#ëû7ñÃÚ‹¾xÛÂzF~ß­hš••¾0sm$IŒñÌ:×å?ük]Ð-¾ xïáìò-·Šô¿Íwye'Ép¶òÚÛCΖPñò°ç@ ¶o‹l¼û(üX×ïßbÂ9¨ZFOO>þiüe•E|‰ÿuðÝî…û!6©t¤Eâ/jWðÞ4Ž 2G¶ûgâðQŸŒZ—í-ã~Âÿ³¬‹â-gQÔ#¹ñūﵶÄí·šEÊí„þþàÿD^_rº¾#øëÁ¿ðOÙIžÇL}~ÇÁiúMµ¨•m$¾¸šEYe/²@ŒäË;aNN@Ær?5ÿjüvý…?jýgöÓøQ¥/‰<â·s¬Ã†+¼dk›{¢™h’I‘e†p «íVdýzý™¿i‡?µ?Ⱦ!|>–H¼©>Ïaqsct1Š@  £¯ÊËèÁ•{½?âß|&ƒâ=ö¡a'‚õ,^Oqs$meö9ãÌ‚f“h«†ïFr+òþýáv‹Åÿ¼wàûym~ꚤz1‘X,Ëo5Ô‘…Ü2L0KlóûÁžs@ïÿøÿ—ì‘/…á“lþ4Ö,tò áŒ6å¯]¾­ÑOûÀw¯šµÚ§ö­ð/ÃïÙëökð§ÃKᎫ­xu'øãIÒõÿh²–ð½…ÚD×GQló-Ë+É¢¸¿†úö»âŸ‡¾ñ?Š,SKÖ5}2ÎòòÒ&,–÷¬’D¬Ø$#1\ž¸®Ò€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>|ý å“\Ò¼=ð²ÑˆŸÆúœ6³m8e°¶?h»qôEû5{üQGI *8ÀUP0 ¯Ÿ4ø­hM{^?>Ÿà[´«cÕMíïï®]O÷’0±·Ö¾‡¯W0÷)Ò¡Ùs?Ykÿ¤òœ˜zR©òù/ø7 (¢¼£¬(¢Š+‚ø£àÈþ ü?×<"ØYoí˜@Ç— óÂùÿfES]í­ Ò§8Ô†éÝ|‰œ¢âögš|ñœž=øs¢øŠï+~ðù¨xd»·&)Á¾u$B+ÒëçoÅ ñ£Åþ“Oñ2/ˆôáü"W"äÔÈÀ&¾‰®ÌÒŒaY¸|2Õz=mòÛäc…›p´·Z?—ùîQEyÇ@QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®âO­>ø#VñuÒù­eòbï5ÃÆççr²{Ws_8ø—þ.oƽ/Á©ûÍ À"=_Rî²j2‚,¡>ñ®e=C^†[‡Œê^§Ã_¢éóvKÍœøšŽ1´wz/ëËs¹ø5à›¿øÚÓYo;]Ôä“QÕf?zK룾\‘×o¢ŠõZ(®lN"UjJ¤÷næ´©¨EEt (¢°,(¢Š(¢Š+žño†´ïxgT𮬻­5[y-äîT:à0ÿiO ö WCE]9¸ÉJ.Í QMYž+ðÄºŽ³àqáÿ6uÿ O&¨yi-~T“žH’=­»¹Í{U|é­Źøï¦ø‰w£|D‰t˳ÑSTµRÖŽ}åt@zŒšú.½ ÖšöŠ´»5GÕ|þV9°’|¼xéþ_€QEæAEPEPEPEPEPEÉ$ŽÚi˜" ,ÌÇÉ$ž€PɶŸí sû>|žûÂÑ›ßø®tÑ<1d‹æK>§wò#ˆÿˆBò1‚ÛPãx¯„àž"ñÍÛ|mðwÄ bïWÕ|7ªéñÊ×WR]•¾Ù¡^F<€ò:õ¥ø_ñá/í5ûdkŸ´'Ähš?~™4oXêz•µ«]Þ?ü|j‚)¤RTŽQ¶Œæ,Кçÿà—¾ÿ IûPišEÜW¶7º±¹·ž Xf†FõRHÝIVFYAV‚AÅ~¶üsøËáÙÿá^¿ñgÆÎÃMСßåFG›q3Á<‘Ø(ì3¸àkò×ö}øuûBþß–W?h/ë¾ ø¨Í*hðÕÛé©41¶Ó4ÒYâÈeVpd‘·2´i´7/ÿ…ñ.·ã/üý™ü=)Yž•ûQàï hžð–‹àŸ Áöm'@³·°´‹û[F#Œ{ª2{Ðå·À¿Ù“ãÏÀÿÛŠk #ÅÞ*Ö> ÛèójSQ–æÒK©“È[9T°G’)ʇË*®I “ÁÁb|A¯øÃÿüEà/뺊5{Ùì:f©uk öp¡‘‹[Ã"£H%–0°v’FÜ~ÕWàOü6ëÅÿÿo¯ƒ_>ØÛkš¯…m¡Ôã±½”Ãk-ËL×ÓÅ3¨%Q­¬ãÜ@Î }ýãmWÀ±§ìq>™ñcÄÚÆ±yq¦µ¬²O«\É«êzÅÜ^+;ƒ!š¾JyDPìIù¿öý‘þ=[Zé_ioˆ+7ÌÑÝi~›Y½ ™%ÔQ¤;™¸"Üðä’‹ä_±ÿŠuOÚ[öâñœŸ¶¿›ñáÌ,|9¡ 'L{iÌwm$°’hɉ⻖É“²6OÞZüÃÿ‚°ê—>ý™ãñ¾‡â-[Þ!Óõ[[]:]/Q¸±µÛfd™ u&&+¸¤eHËgšýbfñ÷ÀŸøçâ/ňñø‹ÄzE®¥t¶¾'¸†$7ˆ&ETer»cuyé^/ÿÖ5ê??g qªø£U–é`-´‡hìl³þóO0ÏlT¿´Ž?nø»¥é <9âý{OÓ#¼ðž§p÷¦Fd‰î#ÇÂÛÈDËlÁleHп°×ÀÏŽ¿ ~7|eŠ!ñ&·áK áÓ¼16»5ÚÞZ<ÒÊ× ®å7ª$JΪ¹,Øq_.ÿÁIׯ_ðÖŸ >ü.ñ·ˆ¼?ªüE6¶ú½†±w ²$×QÙÁ4p$‚8ŽÕ¹PÜFwûÃ_ÏÅO‰ÒëðV‹Áá {âŸð¢É-×Nð嘿½ B¦Ct 7ׇsnàÜâ€=ÓöÅýŸþ!~Ê?/þ>üøéãÈo|/sd×Ö¶Ú•ÔwWÚŒC"*W•X‰Õ”´WéGì­ñcUøåû<ø⮽[êzþž²]¬jR3q´2²)è®èYFN'­~k~Ѱþ×?·í¾™ðoÂ? õ/„ÿ ä7Z¶­â‚–÷Sˆ‰(¿eV.U Þ7î.çA5÷—Äë3ö?ýŒµÕð)1Çð÷Ã-k¦I( Ív±ˆ`–AгÎêïØ’zPÄ¿hO޵¿í9¨þÉ¿³ˆ%ðO„¼$Ò/Šá u-VÖö[[ó.Ÿl$–ä]DÊáÙâ.Ç8lÙRAú¾ÿ‚”|@ÿ…{ûüBº†O.ë]·‡E„gÿ´eXf_ü2ŸÂ¼ƒ°øÇþ áïˆt|vø•ãOkæÎý´}.ÖûV¼šÑv@²\ÊðI!IIó‘P°!J±7#'þ Õã-â?ÂM;á?‹õí Å>7’çO–×OÕ®íídD–Þ+V6ñÈ\É;Ê`ìàWGûhŸ·ÃÙ«ÁÚGÃø*ïÃÚ­»kצ¡wìé©1¸G™"ùnªþ3ÍxQ¾øÅûAÁT>xGã–™¤éÚ·Ã8¡¸¸·Ñ%–âÆ5³…õXdß/Ìdie… Ž ¼ üið»Àz?,õMCZM*-{ª]Ë{yq#±y$–i™Ø–v$.v¨Â¨ õPEPEP_8|9ÿ‹iñ;\øK7îô}kÌÖôðª²7úeªö\Ÿ:¨è¤“_G׉|tðÆ©©xbÛÆ>MÞ$ðdãT±ÇYV1þ‘nqÉY¢Ê:¢½L®iÉЛ÷g§£û/ïÑù6râ¢ÒU#¼.«úëcÛh®wÂ^'Òügá3Åz+ù–Z¤ <~ 8å[Ñ”åXv Šè«Î©MÆN2Vh錓WAET (¢Š(¢Š(¢Š(¢¸¿|Høwà¶‹Ç^)Ò¼8÷¡Ì ©_AfeãqA3®í»†qœdg­v”W'á?xÇ\]xÄzoˆ¡´`“>y ÚÄÌ2Ì,ÁI€k¬ Š( oÅþÑüoáGÂzü^uާ E î3ʺžÌŒ)ì@5æ¼U¬<ÃËæx£Áì°K+qöÛ6è×kžNôÀ~¤0瓊÷:ð«¤Ï§|eðtmsÂjßj:ßéLsqn}Yï#ÎpÀàEzÙ|ÕH¼,Þ’Ö/´¿ÉìþO¡ÉˆN-UMý?ào÷÷=úŠÆðî¿¥x«B±ñ‡8¹°ÔaIáwGˆèGPr5³^\àâÜd¬ÑÔšjè(¢Š‘…Q@ÖeE.ä*¨É'€îkÈ¡ý¡>Üͽ¿Ä¯ Ë,¬5›&ff8.I'  _¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šùë⧈µëÑ|ð-Ã[ßê u«øÿæ§7ÚyÇËêÝÀ!‡VëO•;-Ûè—Wýo¶æU«(FÿÓ1üdŽvÞ|:ð…Ï=ãÖ58»z5½¹ÿ€»ÿxtú‚±<9áÝÂZ†ô u´Óôè–(c^ʽÉîIä“É$“É­º×‹U5hGD¿Wæ÷vÉB“Šn[½ÿ®È(¢Šà7 (¯+øñ¿á/½Ãâ_Šl¼;sâ>+©6½Ë¡E`Š'Ôдê”QUo¯¬´Ë9µJâ;KKd2K4Î#Ž4Q’ÌÌ@¤œPª+å«ÛwöEµÔΓ7ÅÏ}¡[i+Dqþ¹Iÿ¯}²ñǃ5/ 7tÍvÆ÷ÃKo-ÙÔ๎[/³ÀÉ(ÆQ6¶æ ƒž”ÔQ^áÏÚ³öiñ†»cá |Oðî««êR¬6¶–Ú”M4­÷QX–cØMnxûö…øð¯[Ã|{¢øgU–¹K]BúyšfU#°;K#}A c®âOÄ 7ádžd×o#k»©]mìlãæk˹xŠÀÉ%RÀÉÁÆ* |]ø_ã_^xûÁþ)Óµ¿iæU¸¿²¹K‹xš"³ÆH ªÀ‘בê+ÌþiZŧÆßÛ¼|*ñxgO”`Álü5ì‹ÿ=§wû©Ž¼èàpÑiÖ­ðGñ}"½zöZö¿5z¯HC⇟õÔâµïƒ  õÏk2-ßÄÉå‡^ûJÂÞâÀù°ÚAé hÀæ'9#ú‡Á¾'±ñ§…4ŸiÇýV¶ŠáFs´º‚È}ÔåO¸®–¾wø%ÿ†¿ãƒó|‘h7¦ÿLSÀþÍÔI•U=DRïR}H®Ê¸™b¨MÏ⃺ÿ ²iy'ËeÑ\Æ•*‘QÙéóZþ:ŸDQEááEPEPEPEPEPEPEPEP_$|Vý…e¯þ4»ø…ñOÁÒëÚýêÆ’\I«ê‘ DTŠ´Š5tDPNXüÄ“õ½ÎøKº'ü5§xGÑË™¥D ·Iî&»‘c^¦¸y%sîîÇÞº*( Œ þÅ_±¿‰#Ó>/hºÓ¦Xz§n·ÿ?˜WÇ¿`ÙOãŠ.¼mãG½|w\Þi÷WZ{ÜrÆT¶–8ÝŸø”»wjûŠóŽƒÅ> ~ΟÿgÝ2}+àÿ„­<:—@ æ|·S…åD·33ÌàH ä œ[ß> ü3øçáQàŸŠú >!ÑDér¶ó<‘…‘dW‰‘Õ‚»C¦½6Šüþ¶ÿ‚_þÅ–Ò¦ßܽ¢8Z>³©½±aÈ% Ï?‰9ï_oøOÂð‡l¼%à½&ÛDÑtÔòí¬ìâXa‰záQ@'$ž¤’NI&º*(âÿÁ;c߈~,ÕhWºWÃK=cXÕ¼S4ÚÝéèL×íæ Ž£ì\{W¶×åÞ&ÿ‚£jß /~-ÚZx+ÁÉke%ý¯…$µ¹—P6Ñ!q®ÌÈ“”*o^NÖòÎT}#û||øûJü´ø«ñ7M°Óu ÍBîÚÜi©,pÍmjV?4¬²JCDŠpØùF¨Æb]j²ªú»•FŸ$WCìÊ+ñÂ_µíâÏÛ[Vý“´ëïÏm \K-ö¬ºUÞÔ±†4•›Êûf|Ãæ$XÝ!ëŽkÝà¡¿µOÅÙ²ÛÀvßçÒu?x®ú[1¡ÝØÏ{{t¤.É MdÄEJ’í"í?+ æ4?Iè¯?f©¿i{ÿköœ:¦»¨ˆ¥·Ó´h%ŒØÆA-̯4«$§+‘#sç#èº(¢ŠùëãìRxv |]²BfðV ’]mfÓo1Ú€:àaé´šú)bž$žG ¬§!”ò>†³uíÃÄz&¡áýQ<Ë=JÞ[i—Ö9T£~85ä³îµ}uà/øDõ·Ý¬x2æmï=[쇸Ï$<%=ÎkÕ—ïp‰õƒ·ý»-WÜïÿ#‘{µšé/ÍÀür¢Š+Ê:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(“ñß‹ôßø?Vñ†¬Ñô¸R¹Á‘ú$cÝÜ…港‚Ô¼/౨ønñ‰&“UÕŒsuóy~Â$Úz:×%ãÏø¹_´/†Q~óGðÇ—®ë]Õå6ÍþófVSÁP+èêõ«þç _j~óôû+ç«ÿÀNH{õºGEë×ü¾ð¢Š+É:Š( Š( Š( Š( 3ø¿à™|}ðÿSЬ[ÊÔÑVëO”4W¶ÇÌí–Iô&®ü-ñ´_ü¤x¬/•=Ü;nbÆ WQñydSŒöÁ®þ¾tð‡ü[¿zÿ_÷zGµÝ0tU¼L%ô+îß,¸šõ°ß½ÃNXûËÓi/ºÏäÎJžåE>Gú—Ìú.Š(¯$ë (¢€ (¢€ (¢€ (¢€ (¢€ ü’ÿ‚¤þ×_Ã_‡ðÀúÔø×Ç Z_ºJ3¥éRñ3ÌW&6œ-AòË¿\þ¶×x‹ösýžü_­]ø—Å¿ ½Óì<5â3«A ­³GœÞF¥A  L!Svˆ'åãûé„<'oá_øAmôK¼6- ‡öZÛD¶?cdòÍ¿ÙÂù~QC´Ç·nÞ1Šó]öiýœ¼7«ÙëþøUáM/TÓåIí®ít;. –3”’9#…YO!”‚Jü¢ÿ‚é7¿j?Ù÷ö™Ö­%ŸÁþÔ4û}FxÔȰ6Ÿ¨‹Ü:4‘»”þñB;WÞ_ÿo¯Ù·á?Âëßi^7ѼU¨ÉlÏ¥ézmôWW7—¤ÄŒ³öý®èŠt‹ŸøŸM¶Õô»ÕÙ=¥ä)qo2g;dŽ@ÊÃ#¡¼;Ný‘¿e*ûûGOøGáX®CoWþƳmê€ÆBûmÆ(Ùk⿌~,~Î>øÁñZ?GÕµë9µ ”²Y-ì¡¶ódò\yòÊÊ ‹9$ð+ò[öFñÇ„þ:ÁU>)|U}VÞk{++øt&2¯úRÛ5¾›–àŸ™ZÙ]þ^pÙÇZýݽðׇ5Má-GJ´ºÐî-ÚÎ[ `í$¶eØÐ´, f2¿)B»Hã¯6ðçìéû>x;[µñ/„~x_CÕìX½½íŽ‹cms *LrÅ º’¤‚A(ñ—þ  ý“¿n?…ÿµ—†BkàkÚDÀK0± kvväq=œªŠHÆô,y¯ÝxÏŸ|5aãê¶úÖ‹©Ä³[ÝZÈ$цzŽ„t*pAà€k™ñ·ÁoƒŸõ5oˆþÐ|W}k‘ ú¶—k},qn-å£Ï²®âNÐq’Ozèü)àxC_ xÃú‡tegqe§ZCilC—"US,~ñÇ=èðgâ?Å_†_?à­žÖ|IâÍ&ÃÁ ­£€j7—Ð[Ù5Í…¼×ARi]c.·³„89;3Šû§ö¼ÿ‚‰üø7ð¦þûá/Œ4?øîø4«]:îJ($½qrmÝ•5É Ì ¶ÕŠýOÿ û,ÿÑðgþÚwÿ§Åû)þ˰ȓCð{Á±É ¬¾ÓR9PUáßÞxSàÞãO•®™c£ZÝë·O‹kh®|…k’8U ¹ÏAɯÇø$~·¥|IøÛñóãF¯wøƒÄw‘K³È¿hò5 ››»‚9(bp1Jý¿ñW„<'ã½ãÃ7Ñ,|C£]”3Yj6Ñ]ÛHc`è^•‘¶²†\Žr+Šð‡Ào¿u•ñ€~øsÃZ²#D/4Í"ÎÎàFÿyD°Äµ»Œà÷ W¯—¿mO†ú÷Å¿Ùcâ?€|/]êú†˜eµi­%K¥‰}ZCÐ;“Šú†Šü…ÿ‚i~Óß<+û%èŸüsã#Âÿ‚n5+}BÓV¼†ÂSçÞMt’"NÈ\”)Û’J8ÏÇß´·uOø(_Æ­:OXÝKðGá¤Þ@¿xä…u}Fé40çi2ÜHâLoŽ ó0¶Wîg‹¿fÙãÇºãø›ÆŸ <9­êò¾ùnîô»ig™±ŒË#FZN?¾MpþÐt?üTŽÇÃzu¶à_…lm¬ím!H-dÖ\~ð¤q€€Z¡ÚkœŠôrÜPŠ0ëÂàš´×ÀŸ~ÈÞð'Œ|g¤xW\ð\ÚŒ…®«{ Œ€O{5ÒJ‹;¡u+0®pà©ÁÅ~¹W…ø¯öaýœ¼s­Ëâ_|1ðæ¯«\9’k».ÚIæsÕ¥s黓\˜Šò«RU'»ÔÖ5¨­‘ó'ìÝûlÝþÔ´Ç޼ ðæÎÎ…žÒ£’-U¡o¯u &DVRÒ*%»/›±L[ÎÀņí£ãoø-'Å=ïBø{ðOÕ`÷z›êÚŒBA›Xá‹È¶iÀ9U´HÃ=BgÒ¿i|ðãáïË'Ó¾øcKðŬ»wŦYCf·8ܰ¢ƒŒœgÔ×!âoÙãàu˯øËá—†5ífø©¸½¿Ñ¬®®f(¡É4°³¶ÕP£$à: ij¬ðóø;Á?4±c©ZÁá M‚8ohÖÕlí¢TI ¹Ø"ƒ»8ÇzüEÿ‚mx³ÃßÿnoŽÿn/á7zšÜ®“®ÒY^^îV |¨-¢V pœ_¸3ü>ð׃GëŸ i“xQ`Ka¤=œ-§# ¤_e)ålR ·¹/ üøà-nxῆ¼;¬[«¬Wºngir‹"•p²Ã8 ¤‚ä(×(¢Š(¢Š(¢Š(¢ŠùÃá¿ü[O‰zïÂ)ÿw¤jÞf· ç…T‘¿ÒíW°òä;ÕG;X“_G׈|uðΩ¨xjׯ¾w‰<?ö¥eD¸·8çlÑd:z…ð<ÄÔ|Uºýôç—žmFBñ»“Éa‰ =v×Ï_·Güžïì‰ÿa›¿ý(³ ÓYí~ü$ð¾µâ[].Ó@ÑôÛi¯ïÚÂÌ'î­cgw1[&ù  8K€•øûfÁDiSàM?Äÿ¼¨ü:øw¯Ü[ê¢ê:“2m…¾Ï %\«3•t9ûô@ ‚2 ~1ÁlÆ>øú—ÿH®(õçÁ—WžЮï¥3ÜÏajòÈßyÝ¢RÌ}ÉæºZå¼ ÿ"O‡ÿìiÿ¢Vºšü¯ñ‰?nÏŽ´ÿŒ>ø+U?¾xE!MÏŸQ•v¤ÞŽY‹|ДTUËdà7Οþ8~Ö°Wíà |IøŽß¾xÕ×'P²†Þå#$W 4ae€H®¤HcppPvýÔºº¶±¶šööd···F’I$`ˆˆƒ,ÌÇI<_…Ž›{ÿ1ý³ô¿ø~'_ÿfXޤÊU5K”‘g‘!Èçí.ˆ§vÝ®ê¤õ#Â$üø—/Ã{ÝøOÅÒK{¡1û–·ŸzæÇ=¶|ȇ’£$šúZ¼÷⇀­¾#xFç@iM¥ôl·6kÃÚÞÂwC2‘ÈÚÜrT‘Þ³>øöçÇ^u×"~$Ñ&k ^סŠî€þäƒçB8ÁÀ'½Œ_ûE/¬¯‰i/Ò_=Ÿž½N:?»—²éºýW˧—¡ê´WóÅû|i¿µ?Âÿü6ñ‡‰>0jãŸë÷PØèú ÓYhz}µ´‹u‚,«Êù•<ǔ۶œ…Üßzþß?þ,øÁ~4ñÝßÄûŸ|7ðo†.¯í4ýGµ¿ÔuÄY i{9À6Å„H‘£eÙŽB¬|s°úöÄø£ñsàÿÀÝOÆ_ü8|Sâĸ¶··´wä$ï¶I|‹b®Û‘ÎÐy`GñÝ÷Àßø(nµð¾'ê´ Æ—ãõ±mM<;o¤YÅay~bØ¼Š ™ùK´l¡þ\0ùÏÕŸ°eÏÄïÙᦥñBþçSñöž÷/sy#Kq%¬÷Éfd‘Éf?eh¹'8Æk‘ý¼¿j;ÙïáLÞðÞíKâGŽc}3ÃÚm¸2\®?tn|µËˆ·È0w˵劀pßðM_Úׯ?µWÂp|LX'ñ?„nâµ¹º†5‰/ ¹Œ¼2¼J,™GVœÉàßðU_x#¾ øIyáiÚ=ÄÞ6²ä³´†ÝÙ<™NÖhÕIàñšúþ ×û)ê?²ÏÀó§ø»oü&,u-V5!–Ô„  a×%ÈÈóÀ%B“á_ðW?ù>Øñeÿ¢e ¦?míkã‡Ãé¾ j²²|;¹þÐ×´¨mõ}+öÌQ^RÁ”Œ!v傊úƒá—Ä? |Zø}áÿ‰^˜Ï£ø’Î+ÛflU•rQÀ'”qžÚºëû -RÆãLÔ K›K¸Þ¢I#e`x! Žâ¿3ÿà™3^xGÃ_?g˹Zx~øÛRÓ왘,¦rcP@dŽI?à~¹ Óº(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¬/ø—Fð~}âÜ ]?NˆË4‡°ÙŽÉ$ÍT äÔb®Ø¥$•ÙÅ|VøˆÞÑ ‹I¶þÒñ&µ/Øô›÷§¹oâoHãtÀŒŒŠ¿‡cáþƒ(Ôn´¼C«Ênõköû×7OÉÁíglkÀ°$×ð§ÃZωõ¹þ6xöÜÛ꺤^V“c'?ÙšiåW¦˜|Ò ¼rµôz¸Éª0ú­7¯Ú}ßeä¿®Ö9(ÅÎ^Ö_/Nþ¯òù…Q^AØQEQÔõ=?EÓnõZâ;;¤žâyX$qE—wv<U’z þS?m/xßãœöß¶¦¤óYø:ëăþ²‘J–Ó4Å’Y.È= Ó)'¾ÿ1IÂ-~¨ÁN~9êóYh²WË}OT×,ø'Äß²ÿ€>øáw¼ ¥x7V´ŽÖ_èO¦Y›x¬®",ìí¾w8rËáØœƒé29TYc;•À Žàô¯Âo¾,ñoüöËÙ7º¼úgÂ?‡ÒÉ7ˆä´r†ùìdT¹,FU±9ð ©Ý6€?\ŸÇ ~ΧârbFÓ¼*u»£l~ÑϱÅ~ZÿÁü ¯ðÛâWÅ«ö7§ˆ5ØôךOšB¶P-ËÇŸï2Þ¥FzPÛ?a€þ8ø«|ð7ƒtO ]5¨]+PK$ÚÝŃïp£ÏrJâVg,êX195í>Xüø á?…7w0ÜÅá}1a¼¸UÛ ³ÒÝJr¤gn{kÜkå¿Ûcâü+ÙC⇋’O&tÑn,íß8+q¨âÎâI”¥~OÁ+4¯]øçã·ícâ¸ìômN¹‘,®®U"‚ƹf¼º*[ ÈÄ*1‚ŠŽ©]ü0»ÿ‚¤|nÓ>*ø‡M›Bøà#=ž“,ˆmïüI+H¦áÐàb‡!îÚw~è|.øémñãàß üŠ ûÅ4'Q6H#·ðý¥¿ú5ÒF«€¬%GŠ8Ã|¿)®¬U§È´êßDº·ýyVª¡¿øs½Ó|-Ὲº½¿Âßi–ú?ÂoÊ‘ÝÃeÃm¨ßC‚¶‘ª 0œ4§ø›Ñ«ëäD8Ô*¨0ÏxKš'‚<9aá_[‹m?OŒGõ'»3ìÇ,ǹ$×GZã±Q›Tééíú·æúü–ÈŠœo)|OòôA_;|UÿŠ+âG‚~*ÇòZË)ðþªßôë|w[»ËàÚ¯¢k‡ø•àè> x\ð|øR¶t‰D~x_þ Vü(Ë+Ædçð½£ÑýÛú†&›”.ëUêŽâŠòÿƒ>1ŸÇ? ômoPÊêIµ¿FûÉyjLS‰e-B+Ô+›BTªJœ·Nßq­:ŠqR]BŠ(¬K (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€îâC¶+xUˆäoÀ ±Â©#‚ø­ãÿjÿ±ÏŽþ&ZØ_ø+[ŸÁ:Χo’ùZ†Ÿ0Óæ–^Ìs&¾RŽWÉ¿íÿá¥ÿà¡^ ø©büÓ‹u[cóGsªÌPZG(?)²Bê9C(ƾÓý«ÿäÖþ1ÿØ›âý7O@ þÀïþ%~ÏŸ>9xãâu]½ÝÌéqâNKÚÓPžÖ[FœÄèR% Œ¤79šûãâ‡Å¯†ÿü+qã_Š> µðîoÇrø2>2#Š1—–B ³¾Pÿ‚cÉ|0ÿ®Z§þ.ë®ø­û|ø…ã_üfø•§ÜøÃU›Lh,ìµ[—¹Ó4ÕŠ Œlí6¨v'wÏ–P¬I ¿²×íeàÚÏHñGˆþé—¶:O‡u!§¤·Áî³Éæ¬hͱHnú€xTWâ¿ü'þHÿìb_ý#Š¿j(Áh?ÚWá/ìÅá;|ZÔ¥²µ¾•­í!··’â{™ÕK˜ãTAÚ Ë²¯«WÈ^!ÿ‚—é¾ Ðí<{ãOß4?_y~V³u§@‰‰îÙ‘§ ªùI“æÏËž+íŒ?¾|v‹@OŠú(Ö ð½òêVHóË ipƒï:Äè$\ |©E|Uû~Ô$øâ¯‚Þ Öl¼sãßÁý‘§èz;®§r&™Ô$ŽÜ¹ŒÆ¹t †g Hû@ñÃ?ÚWáZï†oS]ð‡‹­\E:¤€Ååp%ŠE ‚2®¾¢±þ x£WkmGáŒåó­úØóЃÁ8¯[/’©…ŸÚÖ>Réò{?“èrb+UWMý?ào÷÷>¢¾eøÍûLèß ü1á{ÿøPøâ?±Ãº>–„=ë$bWyg`c·Š4`Ò;ò£'i¶üíû.ÁA-þ5øâÇŒ¾%øEü>l›QKi_P߬䪨˷u`2Tä ãË”Zm=ΤÓWGé~|ëðQO†—¾8Õ~|ðoŠ~,øƒC2­òè:wú5³Âæ7ÍpÑ‚¡‚'î±ã<ÂoÛßâ¿i ü øð†ëÀ¶><ÓfÕt;‹ËÁ%ñ³ŠæŽk›uLGæ‹w]…ƒFØÎàA¯§|1àŸÙ—öC´ñµiy¥øß>¥¨Üêš—–n®9$‡¼”œ)cµåŽMHÏ1ø û}|)øÙñ*çধ¢ë^ø…j:7ˆ-–ÞYž$2È‘2;e’1¼¬‹ù”0yøðCGø•ªG¯j)ñV‡-­¨·Xt/ßé6Ìü6’Æ!-‚än*gWägƒt½CöÇÿ‚•Ø~Ñ? ì'ᇀeñ†Khu¬¡uÙ 8W’Y6Á.[U?»wñï/û­ü¨ò³þ ñ[âWŠÿdøïÆ:†±ñ_ÒõÍSìñÝÞ=ÝõÊÛÙÚɬRÝ9Ææ$ ,3Ü×Ü¿³¯íà¯ÚWᥧį‰m¢y¥´¼±º ·V7pI꤀ÀaÏ*Ê{â¾ÿ‚2ɧjÿö5êúIg[z¿ü37üŽ i8·ðgí¦M¨ýŸîE¿§,’JÉÛ2(foï<à ŠýP¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Í~0üTð·Á/†^"ø©ã9¼­'ö¯s •þìPÇž ’ÈV4Þa_‚_þø¯Rýƒ~'þÖÿ!ÏÄ‹š¦ª•ps§è¿m‰,mcÝÊ©FWÆyAo™ }OûPÜ?íÓûTi±_†µiì| à8¥Ö|c}dÉæ5Üj#ŠÚ2Áд-*& <ŽJ“ sÿ·7ì‰ðóöOñÏŠ¦ø×ñÅ6ú<6.ºV·«Ås¦Ì>ÝnŠ²Â¶é™Ü˜a‡U=±@£¿±Æ§ý­û'ü ¼Îâ¾Ñá'Þ Xâ?ªWÀ¾j?Û¶ÿSñžðUÝÕôò Ñm_°X&:šúI¯ž»pxÀ¯§?`¿éö_°WÃïßÉ›MF¼óH?çÜÜFÊ=ÿwï_%ÿÁ-q¶êù~ÉnG¸–U¯(ë?Ÿ¿ÙÛöÊðßÁ¿‹?j½{ú—ˆu]Mk¡a+b õÜ—MÍÙâEŽ ( 죅ã#õËö'ø|j,mÌ7ø“ãË8î´ùcØè:|ÊvYÙFßrDV)+uS¹$Èòxoìû2èßÿàšúÏ5Ø’6ø©6«|’ȹ0O‹[9½u%¢J£üMGÿø¿©¯„üeû,øÜµ·ˆ~ßÏ=­´§÷‰k,Æ;¸@ÿ§{¼–÷˜Ôû=EPEP_;Kÿíßêô¿‰>[tjšbåIì7Ûœìý}^%û@hú§Ãɵí s­xNxu»ßͲ;Ýxäï‹zã¹"½<¦kÚû)m5Êþ{?“³ù¸´ù9Öñ×üÿ £Ûh¬O kö*ðö›â]-·Zj–ñ\Ä{í•C}Æp}ën¼éÁŸËttÆI« ¢Š*FQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®Å~%Ó<á½KÅ:ËùvZ\<‡¹3´z³;’tóÅ/ø¸ßü;ðnÞi¶Åu½{©¶·ôkvì|é°YzíPEweøeVªSøV¯ÑoþKÎÆš®1ºßeêt<5©é¾¸ñ‡‰“oˆ¼g;j·Ùë”~âžBÅÐèIíÔQYbñ.µYT—_òù- £IB*+ QEÌhQEQEQEQEá_´MBO ÚøïÃÑï×<rºµ°eŠ..¡$s¶Hwd¤^ëMtI£‘C## ƒÔ]8—â{kb"¸}.úʼnœÚm¤Œ Ò¾>»ÿ‚bþÃ÷óµÍ÷é.fn¯&¹­;©7ÄÐ׿ u];\øoám_H¹ŽòÊïK³’)¢`èèЩXd^[ðãö¥øGñWâçŒ>øFòê_øκ”S[1ÿ‚s~ƾ>ñf±ã|>:†·¯ÝÏ}{pu}Z?6âáÌ’>ÈîÕ,I ªt çáÿ‚_~Âöó$ñ|3ÃÆÁ”gX<ƒ‘Á½Á oø÷ûS|$ýšßÃüUºº²>.ž[{"Ù®™—¿Ì+Âæ¯'¯>•Äè'ø]ãKOúLlt«‘‡‰aŒºÔ°^m^ÝŽòJ šùö²ýž~$þÖ¿µß_èrhß ~¯ö¶£­LÉ·S–õãf´´@X–Ü#fçfò„Ÿ§×öz­Æ™¨Â·—q¼SDã*ñ¸*ÊGpAÁ®Ì+ÙT»W‹Ñ®éïþk³³1¯Kž6[ôõ?¿à zþƒªþݳ“âMJÚÏÃzdÖº¼—W¤v¢95wf‘ˆ@Œ¶ª '5ëÿüdÿðQ?ˆ¶ß>¼²ü ð¥ô7~1ñH5™í˜I—bä é¼y³¯Ê±™}#Tý•¾|QñݧÂÚçÄW~‚Y¼-v÷w6ÆçF™ÃiÞXüƶqŒ7E9P9ûßÁþ ð—ÃïYøGÀú=®…¢éë²ÞÎÎ%†ÇS…@Iå‰ä’I$’ic°¾Æ§*w[§Ý=Ÿõ³Ð(Uçúõõ8Ÿ|û8|×>&x‚%‡Hð½˜ò-!ÛšN"¶µ„tS#•Eã O×äGìoãß'ñΣûfþÕß<4ÿu÷‘t&çR·Tðý‚’ˆ© 91ÊW!ÞD$±2Høý€øÑð+á_í á8<ñE:ö‰mwò[‹«›L\D9{Yas…‘†ÒÅyÉ—?á×?°Ÿý?ü¬ëü›\†Ç­|ý­>þÒ^8ø‡á_†BKý7áüš|GW ­ù¿IŽmÇ ¶6Ô±nx ׯðW?ù>Øñeÿ¢e¯½>þÌŸ¿f›}i> ølxj-xÀ÷äÞÝÝù¿eyD›¹¦ÚHÿwçœàWæOüâçˆ^øM¦xÆš'‰¯-¼ie4°éšµì±ÅåH»Ù!‘Ê®H# Pí~bþÁº·Ç?ÚÃÅ6‡uçŽZÒ'_õlöMp®Tô9Þ¤ýAï_\þÔ?ôoÙÇà—‰>(êŽweƒL¶~·z”à­¬ £“—ùŸ„Vn€×˜þÀÿ5¯Ÿ³Ž¥x¼8ñgŠ'›Ä:Ø”bE¾Ôv±ÇgŽ%ýµlq@gÑEQEQEQEQEQEQEɯ—íÁý ÌŒ¬—ÓÉíYF;Ë÷ó_¥¿ ü7¬ø³]—ãomšßRÔ"1hÖsý™§7##´óšCÔ·•BW«Œš¡ªÓzý§Ýö^Kñzì‘ÉF.rö²ùŸ«üÌ(¢ŠòÀ¢Š(çÿÅ ñ¿Å~oÝéÞ-‰|C`:(¸È†ù«3“…}_>~ÐM¡éþø³§¡k¯ê s0Q–}:ç^ ú£öÛš÷è'†êîmœIÊNC+ ‚¡êæ?¼…)ñ-šYZisÉÕÊÇÉ(ŒFUÝ<ÙbLwf^3@«7Ú][Icz¢ºV‘Ž7«Œøƒ_“?µWüsöwÒ~ø§âGÁm&o‡þ3ð~u¬Y^i÷·!$kšfŠD–WUިʯÆV!‰ >ûM~Æß¾<|#ø3eá_ÛxwÇß `µ¸ûUà‘ÖmF;{u2yñyŒŒ“C»vÇÉ?žWÄ_†ðR_n¾ø»Xø}á}#Z·6Z¾³¥>£-íÅ«“$PÉEóW!ÇÉHR€â€;ø&ÇŸ||ý™†¯ñòMSZðέq¢µüÜÍw0Áq’¿ñÈpŒÝ[hfËOè£*º”p °ÁA¯€õ…ß?b?Ø?ÇÑ5W··Ó4 UäÔ¥aÅæ­}nÑG"í?$’JÑÇ©%@A’FãÿµÑþ#éÿ²n—«|K¹¼¹»ñ£w©Y5ü,ÿ`”FœÈKmsH™ê¬à‚@=‡L×áýžµ¿x+Vù|/5ö»á÷o¹Ùãi¯,í³DÝ'$“Šø[þéig¡~Ï~-xªî+8õoÎ÷ww±Æ¶ö±JóHí…U 4¤’p0M~—þÑbøÙð£\ðL2‹MV{yŽtxò.ŒlˆIí`Åý’N2~?þÉ?°‡ÆŸü4á—ÆOˆÑiÿ ô½bâmCš)t¼¼¾ÔIo¨\¢dŒFØL‚ ì|2ú˜Ù{x¬BZí/^ç×Í>ç-ìß³éÓÓ·Ëò>®ý–4éÿiÚoÆ·ý¬‘xNÊÔøWÀât(óÙÛ»}ªü‚ä2,~ÒH„e+ì_Œ¿³7ÀoÚ)´ù¾0xZ>’’Åk#Oq ³2kyc#%W<çŠö]@Ñ|+¡ØxkÃvQiºV—vÖ¶Ð H¡†% ˆŠ8@Àù3ð_öTý¹?d­wÅp|ñ'ƒ|eá/ÞÉ~Öá¾³’+—ÈócXÂ1P¨ÿ½`ÁG€kË:šn4ÿÁ=à þü$×ïî¾üM›MY4K¹Úx¡MNíì]v’2ðÊ<Ø¥>Ó±‹üå¿ Ûø÷—ýÖþUù•ðÏö*ø¯ãÚFÏö¬ý®|K¥k>$ÐÑEÑ4›û6ÇÉ.a>eÀY‰¤UÁ&S¼¹ÆÓúkqÿòÿºßÊ€?&¿àŒŸòiÚ¿ýz‡þ’Y×eûsªÁûJ~ÈÚ„Þ'‹î`P:ù3ýL~€šãàŒŸòiÚ¿ýz‡þ’Y×KñRãþ‡ü£áoÃí$›­+à–•{âaÓîÁ{¨¢­¼Lßßȶî³c£`õŠ( Š( Š( Š( Š( Š( ¹ÿè3x£ÃZŸ‡mõkÝ MF€_éÏw–ÛÆ<ÈT‘EþQ°yÅtPçïÀOø'OÂÏÙÓâxø±à?xÆãV™fKØoïìæ¶Ô™–íRÊ9$ñ ;ÁÞ ç¨>ÝûLþÌ^ý©ü'gàoø§Ä…­-Ŧ‡uomóe1t'·ŸÌX™7F£h wHR¿JQ@œº7ìÇáÙÇáþ—û9ü;ñ>¿­iµ¨ÛkPN,¬í‰¹¿{_³ÛÛìóÈ$äc'=‡Ä¯ØáWŒþ&ÏñŸÀÞ"ñÂï^!K«ï _-˜ºÎ£hÜe‚Á #sl“ìzüV¿´¿¯·Ï§øÂ-&ØõS{{ûû—Sýä@±·Ö¾‡¯W0÷)Ò¡Ùs?Ykÿ¤òœ˜zR©òù/ø7??þÁ:~|?ø¹añ×_ñ_м}ãm6O:ßê)w‰f%c¶vØ­ò†v Å{í;û+øSö¬ðæàÿx§Ä:&‡c+M%ž‹smo äŸ)®Dö×ü¢¤ÆP A;HúzŠò޳Á?gOÙ÷@ýš~'Ã? ø‹Z×ôKiž[EÖ§‚áíC¹¢„ÁF\³à‚w1ÁÆù‹ÃðM?…ÞøË/Ç­â'Ž-¼as6¡srš…‚-Ä—2'ŽUM=wE)$:g‘èpkôbŠ(¢Š(¢Š)®‰"4r(e`AdzƒN¢€>xøÏáyüUð~åŽ|#~ÏbòÚeþg·Á=vê}0}_;üEÿŠ#â÷ƒ>$'Ée­nðÞ¦{bàù¶n{ ³¥@qšú"½\×ß”q í«¿ñm/Ç_š90š'Où.Ÿ†Ÿ ¢Š+Ê:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠðÚâUÏÁÿÙëâÄ›ò¯´=î[6ì·Ž†;b}¼çLÐçÅÏÚ;ãwícûJÞ~É?²–¾þð߆̣Äþ,¶RÓ¯Û'[y‚в{ ¼’ä‡Xc“ûR~Á¾|1ƒÇ³Ä/ˆ:çtÛ¸ÄRë“O=ô"ù~II#b­ÁÆÍÀ‚pGeÿmø_má¯ÙÛXø§uíWÇš´ß¿nY¬´ìÁäóÄæà“ß#Ò¿^hçïxoì?³£áÿˆ:æ£æèÞRÕmoeµ¿i´ûQ$·ê&WÏf9Ãd†Iñãþ Çð/ÅÿµÂÿ|Eø±ñWǰÇi«fØ.Ÿâ;«pÂ#–fmþfì™T1Œ_£¿ðRˆð¯c?ˆWPÉåÝk¶ðè°ŒãöŒ« Ëÿ€æSøWæÃo~Û³WüêcÀðõ‡…fÓdÕÿ¶Íä’jéo«É½n–Û)Ër¦ÂK`(b­Ò€>öJøÓðßöÖøc'üqãáBÁ&£¬M«kWpÁwj³2[ÈC"ºLÂõl’8Zþßχì›ãß6±¨èWš§ÛlnôËé¬&[Üù6êÏ!xÚI´m•lŽ2…û%é^,Ñÿf¯†öþ:Õou¯\h¶·—×Z„ò\Ý4÷«ö–I%”³±ÌòÆIÀPWßðYŸ þ˺‚­äÅÇŒµËX?ï[Y+Ý9÷Ä©ç@Uû~ʾ-ø÷û5èß¼{ñ›â.â/MzöréÞ#¸Š8`‚g·‰Œr-–˜üÃr‘‚:×{ÿàøýñ—Zø¹ñgöaø¿âY|m'ë‹”²Õî>{†7e2¼Ç-"»mt238ù†â0ÏÃÿÚ'ãŸÃ¿ÙÿÂ_>þ;6›Å:i¦Gw¯ØG¦i‹vªËrÎÒ¶õ2î}¬cÎye¯Yÿ‚{þÇ>,ýœ4üBø½}£ñ#Çó,ú‡”âU´ˆ;Êb2Œ–I¼Ì¿&B…$.æÊý¼kÿ|-ñ…¿gÙêÕ/¾,xüưLȲ®Ÿoq!†96SÍwV ¸)#;‚6ÕH¿àÞ'Ô| =ÿŠþ:xâûâ¥Å»J5˜5«ˆl­ïŠåRqó uo”ÁŠò»8QòWì@eý¥¿à£_¿hoÉö«ýª=)Hâ%šF°²ÆxlãuvÝ×5ûƒñÆšÇÞ%øƒ«,ü7¦Ýê2î8-biHÏ©Ûï@œðMÚçÅŸþøÂ?Z½ÖþL©w¨É·ÍžÆXÝâiBºT0È…º¸ N_q>Mñçö­ø‡ð¢=7Àÿ4õÔ>;üv¹Šö0ȳdi“±ƒNŒ+|¦Rƒ($Pnysó'ü»Âz§Œ´x_æ6~5Öl§ÖN›¡£ÌÊO¥Ä÷k#º°õ¯Uý†íSö‹ÿ‚‹üaý õ„6^k˜t®8‡Îéö8Û%\ç5ëO÷8UµSWþôûÞ¿%Üä^ý[ôçÿ~lú^÷þ ÏâÝgá–£ªxçã_5ߊóÙË<+ñß³†ŸmûAßj×~,¼Ô/g1kWsykl¬!†ò3°\DdPXà?½}»Ey'Yüÿüzð÷‹|kÿ?Ñ~x⊴¯ëbßQ×mmuËÔŽäË}t. GP"µþP~°þÕºV‘¥~Êž=YõmOH‡Ãžººµ¼²Ô.-ï’âÂܽ±ûJ¸•Ù¤D ½›Ì'œšügøª|}ø±û}|qøûðDÐüIs¢Ý\页뙭í㵚_²ÚIB  " t Oµz§ü/âÿí©á¯Ù»Sð÷Æ x?Dð÷Œ¯-t–¸Ño®§½ÞÞmT— ±–Ù•Ï<6;æ€=£þ #á_ø‡áV¥ñçâG‹µÿj:ÕåΛ§Á¨ê—wV‘XÛ˜÷ȰK#!•çW]䪘R76^ëæØ»áÿü+ÙSᇃÞ?&xtKk«„Æ ÜêÞN§ÜI3_OPEPEPEPEPοÕ¼¯ø_ã] "=ìý_hûúUóflr|™vºRI¯¢•Ô:Êà ŽA¸¬ŸèZw‰ôCÚ¼~m–§–ó/r’©SB3{kÉ>kºÇ„n<â7ë¾¹}"èž²Gü{LçlíÁ=H&½iþû ¥ÖžŸöëÛîw_4rGܪ×I~kü×äÏs¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼K㇊µ]7B²ðG„äÛâLtë:Á¹¹8ä,1där ½ªI#Š6–Vˆ 31ÀrI'°¯œþÆÿüg¬üo¿Rl>•áäa÷l!r%¹ô7ƒ€BŒt"½L²œb刚¼aøË¢ý_’g.*MÚœw‚êÿ®¬ö¿ø[JðG†4Ï è‘ùvZ\ ~­·ï;z³¶YrMt”Q^uJ’œœ¤îÙÓ¤¬‚Š(¨QEQEQEQEQEQEQEó‡Ã/ø¶ßµïƒ³þïJ¿ß­èáV ›ý*Õ{*S¹TdíbM}^ñÛÚׇlüyáx÷ø‹ÁÿiÚÖhTbêÜã²Å@äz¯†Œ:Ø‚+ÖÌ?{â–ïI‰uÿ·–¾·8ðÞä.Ú¯Oø•Ú(¢¼“°(¢Š(¢Šñ¿üøiãOŠ>øÑ¬iòÇ㤑X_Û\Ko'‘.wÃ0€š#¹¾G í¼kÑu¯ x_Äø‹G³ÕZÜ0ŒÝÛÇ9@ØÎß1N3œuÅoÑ@º/†¼;á¸å‡ÃºU¦—Ä4‹kp#€XFH÷­ª( Š( Š( Š( øÉàmOÅ:®¿á6ø·Âòý¿J”ÿŠ?ynÝ2“§Èà gàWWðïÇ:gÄ_Øx¯LS¹R³@ÿë-îí–ÈÀŽ@ÈÁèEvÕóF©ÿ_âªø?uàßÜ,7ävZÁŽsÙRä ®¾7ÐW±…ÿh£õwñFî?¬UçuÔã«û¹ûNÑþþô½Q^9Ø!V‚ qü1øm¬±xOIGB °°·Ђ­wP|JøðÓâï‰|⟈tš­Ï¯PÓ!k‰VÕ.ŽÒ²Ën¬#• (]NÞqÁ û PEPEPEPEPEPEP^UñcâÞÒ-¬t+q¨ø§^“ìšE—yg#™Ò(‡Í#pÀ$g5Úx³Å:'‚|;âŸ\ m?NŒÉ+ž§°UÙŽGr@¯!øOámo\Õî~3ü@·0ëºÌ~^düÿeé¤å"ô–O½)ëÎ0>a^ž„^"²÷VËùŸoN¯ËÍ£–½Fß³†ïð]ÿÈí>ü<‹á燚Öêàê:æ§!»ÕoßïÝ]É˶{"ýÔ^öI¯K¢ŠâÄWYº“wlÞ5¨Ç`¢Š+Š( ŠøëöÑýª­e߇·š=×|uâÉÎá½,+?Ú/ÐduL3GtʯÌìȃ·.ì·û<|[ð¦ª>7þÑ?µ|D×,´Ï´yz.“Ë$¯m¬º.ŒŠ× ‘ò©ûì÷üÿ~Åÿ<ûI~Ðß´½·Æ5Ôµ¸¼5â24õMZþÌB.oµ(ÿEž-Àˆ“³ŒqŒšýø«ñWáïì¡ð›I¼¿Óµ;Ý2Ãìz¦é°O¨ß]N")kl…‹3;$xß+Œ‘Ë  ¨¯ÂÍ'öÇý­¼gûwü4øKñC“ág†µ9¾Ô<<¦9nnlå·™â{ËŽY˜˜ÎQDaHÃ&ášýÓ $($œÞ¾_´öñ²êr ÿ¼'r~̧îk”GSÙ­à‰ûZø£ö2ý£µ¨|_y§-Ïö~¬°Gûí£[„Ï–ˆ9­Ï˜7‚êØˆéÇÁLf„¿þj_~ÚêZOŠ/5ËU’ëûgR™JÝ´0ËrñǦc ÅxÇiû‰E|£¤ü.·øÝû&xÁzŸˆ5Kyáý æ=OG»k[ø.`¶†XäYFs‡sŸc‚>hø+ûAü^øûB§ìƒûUêÇħ^_ø´À°J,¶÷A8ór¥rrâAµ™Ä‘½~¢ÑE›¬é6:þ‘}¡êqù¶zŒ[ÌŸÞŽU(Ãñ¼oö{Õ¯ŸÁø+[“~¯à‹¹´[‚x.–Çý@:íhJ`÷Á¯v¯ïâƒý¡m/ÇîôψÖ&Ö_îOM¢bz ð€w"½\ï)T¡ÖÜËÖ;ÿä·û‘É_Ýœj|ŸÏþ ¢(¢Šòް¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ä¯Ûöpºý¢>ÚÅáåÑ> ø>ò=cÂú¡bŸeÔ !‚³¨b#—h @;X#àìÁö ?ÃZïÄOƒ'Á_,-£ÔüE£Ë¦köútîÖîn¡h.|‰pŽ«"±+Ñ“8É#'Õh ”¾~Æÿ ¾I£'€õ?éš~ƒ0žÓM_j§MR$2•k3qä2;’] bNAɯ«h¢€ àõ?…ÿ5¯é?uÚ^x³B¶–ÎÇQ–0ÓÛA9Üë< œá±¸`À÷”PEP ñៀ>+èpøkâFƒkâ-* ¨/RÚñ<ȅųnö÷ÁÈ ðÊJ°*H=´qÇkJUQ€à@)ôP_5xÇþ,÷ÄÛ‰P~ïÂÞ-x¬5å%µßݵ½=€9òå<A9&¾•¬Ox{Jñfƒá­rqa©BðL‡º¸ÆAìGPz‚Ý€Å*S÷ÕâôkËüÖëÍ# E'(û»­W¯õ¡·Ex'ÁOêÖÚ_|a1—_ð~ÔŠfàßiŵÈÏRä“Ó“^÷Yã0®GM»ö}ÓÙüÑTj©ÅIx¯ÄŸ>ø£¬Á®kzï‰t¹míÖØE£x‡RÒmÙÙ÷<6sÅ9.Ar 'íTW)©óßÃ/ýš>ë~ ýžtd‰äk­FÎÏP¼™á—Q’DÎþd‰6ÀÉ5çß±ßìã¯| ðLjŢ€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€î‘£I#U’N©&¾5ðWí·ðÛâ·Æˆþ|ÑõoÛZÉ"jÞ"°·ØzvÈÝ—uË0ó :„ÖÎägó¿ü'ã‹4Ox?öpøcu$.øÅ©&š|–*ÿÙåÒpå~Ñ,±ÆHê‚EèM}õð;àÇ‚ÿgÿ†'¿Û4Ý ­!ͺ¸nf¸˜½$­–=‡ ¸PùÄ·gŠ?᣼[û4ü.øAã¯øF¹áÕ¬ìíÌp3HÐUZá˸Æqö‡¼{¯…‡âÄŸ Ëà½FÖÖêê÷Išê Ù-–ÝŸh3Û“™#Uq´œn y¿*¾Êaþ9ظÿËH¯ÑÚf=OÄþÒ>ø~÷ì§/VÌO³Ì0ZÛ¯Ÿq6Ì®ðW+‘¸®Ì¿ ªÖ9m×ÑjßÉ\ÇS’K×§âx÷†¿h߃?³ß…<1¢üTñPøçâEúê ¦[)¸½{bPby!L˜£È»¤Ú\‘ŠûÖ¿šŸÛö^øwû5þѳÄ> ¹ÔµMOĺÄ3júŽ«v÷wW×_Ú4…°ªÇÌl…cÉäÿJÕÌK­VU_WqѧÉÐ+㯈߷¿ì¯ðÓÄxCSñšë:ý¹u’ÃD¶ŸU™>­QãGR0ÊÎä û¾XýŸ?dŸ…³N±ãOxNK›í[Æ—²_^ßj&–ÙšCoÇ{!ÌÄ’pX£ƤÿÿlŸÙÛö‹ÔîÅâ_ü Ô5_øg÷®Ç®éÑG4(óE±f¸Q¹±´¡nøÅ~uEk¦ünÿ‚»iþ4ýŸcŽçDðzC7ˆµ?kYÉ,VÒEpÍ*üŒfÞ¶¹RKÌ2aúcÿÿ“1ø­ÿ`‘ÿ£ã Óá×í7ÄÏÙžÏöŠðσïï¥Ô4û›ë}ÖDžöf·–H¼˜Û¬ìcÈã¾'‚¿?jÿ„´rêï.lÕõk‡2Mws¥ÛI<Îz´®cÝ!÷rkÐ|ðãáïË'Ó¾øcKðŬ»wŦYCf·8ܰ¢ƒŒœgÔÐâÿüwân“©é?~išµªj†ªuMAdAh‘Çö{g¹$-í0-„,x×èv£ûZþȾC?¼;©i¾Ób¶·²Ó5;KëÉÒÒ!QCo®ÌÌ(à(êÄ ‘ì~&ýž>x×\ºñ?Œ¾xc^ÖoŠ›‹ÛýÊêæbŠL“K ;mU 2N ¬/ødïÙgþˆßƒ?ðžÓ¿øÅyoì!ñ·âoíðRçâÿĘm­\Öµ£ÛÚÅå¤:d"‰&FYVU.qœ+óSþ +ãß|Wý·þ|¹Ö-DðÖ¡hu‰ È!·›Q¾ˆO ¬NÕu‚Ý2¦ðS_»þ𿆼¡ÚøgÁúM¦…£Ø†[{+#µ¶„3aQ*¢‚ÌXà ’OS^U©þÌ_³^·©]ë:ÏÂo _êóIqssq ØK4ÓJÅä’IÎîÄ–bI$’NhÛã’9cYb`èà2²œ‚ ‚:ƒOªö––¶°ØØÂ–öÖè±Åj#DUU@pX À_ø'§Š|qÔí|!©j—6²é×z¤ËkmsŒ·EJÜLU?}ÂIHܼ1]÷íûûSÛ~ÐzjþÈß²ÅÜ~0½×'ñ­`þne§[2ÊU®£Ì~X`¯< ”D_/,ÎU\¾ üøIña O‰Þ ÑüTmT¬/©XÁu$*Ç$Fò+2dõÚEx?~|>ðéÐÿgÏ„^Ó|)‹¦ûV°ºM¤V{t«Bf“ÉU%¦lF¬ÙÏ ×f íª¨7e»}’Õ¿¸ÇW’K~ž½Wþ ýðOø)ðFÂEBךÊ,‚GR®mfFÁ'oœìòàa” ~yÁ:ø“ÿЯ?h¿†³Ïìãw¤xÒóÄš´0ëúˆ/wgg`HiRÖ[yQ$¸rÛ#Ú•‰!>Éý¡>,h¿~ ø»âV³} ‘Ñôë©-<çUóïDLmà@ßyä“jªŽ¿LÕï|ø#ðÖñu‡¾Ð|7z¹ÅÆŸ¦[[OÈÁýìhq×¥tÞ6øuðûâ^›ñÃ_Š´ûi…ÄVÚµ”ÐÇ0VA"Ç::‡ Ì7œ3‚k”Ôü˜ÿ‚*é:¿À¯ø…/a¸×u²ÝD$ ø'Dðœ·ê‰rúF›m`Ó¬d”xиRNg8ë\ιû6~Ξ'Ö/hý_“³õG>*›”oÖ«úóØô*+Í~xÙþ xKñÚùZˆSoh¯-Ï—:•íó‚@ô"½*¹q%J¤©ÏtìkNjQR[0¢Š+Š( Š( Š( Š( Š( Š( Š( Š( Š( Š*ž£¨Yi6:¦¥2ÛÚYÄóM#œ*G,Ì}€¦“nÈ“§è:Už‰¤Â-ì¬!H!z$q¨U€á´ûßÞëõøZ+Ï‘›ƒæµÑá?¸Oc1ýëフ5ô-z¹“TÔp±û;ùÉï÷mòo©É†\ÍÕ}vôé÷îQEy'XQEQEQEQEQEQEQEQEuà×Î? ÿâÛüC×¾ Ü~ïK»ß­háE¼Ïþ“l½¿s),«×kkèêðÏŽþÔçðý—Ä Ç¿Ä^ŸûJÕGY Q‹«rG;e‹<I õ2¹©9aæôžžì¿¿GäÙËŠ‹IT[ÇòëýwH÷:+Ã>!Ó<[áí;ÄÚ4žm–§w7}² àú0èGb­Êógã%ª:c$ÕÐQE# (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ æüaáMÇÔ|'¯ÅçXêq4RëžUÔöd`Ob®’Šºu% )EÙ¡J)«3Ã~ ø¯X–ßQøiãYwø§Áì¶óHxûm›ôkµÏ$:`?$†àœW¹W€|hÐu]çNøÏàø Úß……ݺu¿ÒœæâêÈ?yzp "½ŸÃÚö•âÇÄZâæÃQ…'†AÝddv#¡r5éfã4±4Ö’Ýv—Uè÷_wC—'éKu·šþ´ðMŠ(¢¼£¬ŽYb‚'žwXãK31U@É$ž©¯‡ÿfŸÛ¿áÇíEñWÆ? <¥]Å…"’â=JgCü p Ä‹ó*±`Ë»¤dÅq?·çÄÿjv~ý¾\mñÿÆIMœÒ&IÓ´!Ÿ¶ÝIŽBº+§¼k.>eñ‡ì9àOüÿ‚™|Wø=áth´}+ÂæÒØ9˹ˆé’yŽ{»åÝrÆ€?zkâOŒÿ·—„þ=á‡ôý_âGÄÖм1j/®!` ™·*£*‚YgQË(Ö÷íÑû@\~Í¿³o‰¾ i,~ º ¥èû±ÅýæUdðL1‰&ð|¼ |ùÿ·ý->ü·øÓâ˜ZïÇê—W·?¼¸K ˜½´bFËbe"âCœ»8Ý‹€ Ï ÿÁH<9wñ›Aø ñ#á_‹| â¿Ü[[ÙEo!ûSìŽW>j7•rè®Öô5ô_íEûLé?²Ç‚m¾!x›Âš¿ˆ´'›Èº¸ÒÅ» &r«˜O4G³mR¡†F\õþ1øðÛǼñ¯ÄVrÍâ%Êi²,›bé ·š˜ùön,œ¬s_¿ðYϧ‡ÿf½ Àð¸>,×aܾ¶Ö1<ÒÂSç@vþÍ_´e—í1ðúO‰~ð†³áÝY,dÕVÙ øŒ²;À"šCµ]JûA= ÁÇÊ~ÿ‚xSÇŸ®> ø_á'ŒõØÝÍgwg VY-dò§inü´HÜaœ°L÷äWñïVðçÃï~ß±”Ö¾&øŸ…gc¨êöÒ,ºg‡¡†ŽîöyÓr·±!Fí®Ã ¹HßíÙ[öSðì¯àcáÿ çTñ¨V}k[¸_ô½Fç’Y‰$¬JIòãÉ ’IgffúŠŠ( Š(  ³É&–¾sø¬êŸüJ>x2á­ÖHÖoßÄy³±~–èÝ÷€;&N'x,#­>[Ù-[ì»ÿ[½ kÖPúôógÁn¿ÚÃõŸ ëž)°Šç:>—¤Ç¿Ú׿åímî]¥–5h‹£y(¥²A.W•¯×=6ê{í:ÖöæÒK ®"IÞb†HYÔ˜Ùв“ƒµ™r8$s_…ÐtŠðWëûmÕÿ´£ ¼J>HþÁh–˜>éyvÌ9É+“Îk÷’¯‹U$£hGD¼»¿7»ÿ+ …yjÞÿ×cæoÚ‹ö™ÒeÛ|Bñ7…5hO7‘uq¥‹vLåV#0žhŽ%fÚ¥C Œ62¹ù‚Ûþ uáSàÛo‰ZÁoˆ¶þ ºŒÌºÔzDXC24ËrPFKvã5ç¿ðYŸaþÍÚ€íNnü]¯@¦1ÉkkÞgÀîD¦οI~ |>ƒá§Á|3’%#Ãú%ŽŸ2 ¼À©)aÐïpÄúä× ¹Kà—ÇÏ…?´G„?á6øI®Ç­iÉ'“8ÚñOm6ÐÆ)¢+£GQ‚9RG5ìUøûZZüÿ‚£|^ø?àÐxSQ·Ôqg ³Àcx.áùz~ã|§uGs_¾”QEø¥ñ:FøÇÿ~ð'uÇ7:/Ã=-/á·q.–Õ¯ÒSþ×$žá{×ím~$„ÁðZ©f½a x×BS ~ÒUR}ZÌïÅ~ÛPâ×üóþN3ö´ÿ±Žý/Õkö¨8ÈÎ9ø·ÿ¼ÿ“Œý­?ìcƒÿKõZý¥ ÿò˜Ïƒßö µÿÑz…~µ|Yø…{á ?øV¿ñ‡ˆÜÛiV§=ľ‘B>f=ø2Gã/í1ãEÿ‚µøÄžwÛmü+¤@/–Û¼R$²yDò¹§xg­~Á|&ðf¹-õçňqñ_ˆ,vç•Ó,3º+HóÑ¿ŠSÆ[èIõ04!¼Mex­—ó>Þ‹y}Û´r×›oÙÃwø/óíÿì>ü=²øsá±¥¬í©ÞH×ZôŸëo/%æIXõÆxQØc¾I¯ñ‹âïþü;Ö>'|C¾:>vèešSÄpB¤òÈØT_^I<÷à'íWðwö‘´ñ-ïÃ;û‰að”±Ã¨5å»ZùF@åOÏÕqdöÅ~Bß|nø5ûpþÓ×z÷ÇoiøðÂçn‡¡ê7ÑZ~ð>Õ,nÀ´M·s8Œ¬J2Ó5pW¯:³u&îÙ½:j1QŽÈ÷ø'¿ÁŸ|UøÏâïø(ÅÛ¤ÜxÍ®ú{grÚO¶?´€|µIæEÞä`¡oPÿ‚ÀÉÜÿØwLÿÚ•îúGíÅðÄ?| û>|,Ôíü_yâ˜o˜ÝéÇ%Ž›…³Ü"ÈËò±‘buUBvàgŠðø,ü™ÝÏý‡tÏý©Y}×û>ÿÉømÿbÖÿ¤QWæ¿ü7A“LøIðïãNƒ#Zx—Á(l….¢y‹ê³ÚÂGã_¥³ïü_†ßö-hÿúE~vÁfõëm;öWÑ´fuûF¯â{$Hóó†ÚæW`=…ÿ´=hõ'Á>"Ox3@ñdx ­iö·£oÝÅÌK(Ç·Í]=yÏÁßËá/„~ð¬à¬š6‡¦Ù0o¼ÞÖ8È9F ¼[ãï‡/õχWZ¦ˆ?âuá‰bÖ´òHžÄù„ßt{×ɯi¤ 0*à ðA®Œ&%Ñ«±èîgZšœ\_Sž"°ñw†´¿i‡6º­´W1ó’ªi÷\àûŠß¯þ“áMGÅ¿®Õð½ñ¸ÓÁï¦j9ž¾¾[—V=Ž}Zæ8eJ´¡·^Uø20Õà›ß¯¯P¢Š+ˆÜ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠðO~Õtó¦ü_ð|&]ÁûžXWƒ}¦?76ÇÔ…ËÇœáÀɯaðçˆ4¯è6$Ðæ”)<.;«ŒàŽÄt#¨ ƒ[]x5óWƒOü)ÿ‰· î?wá<·ú Úëï]YП21ÀÁ dšõéÿ´Pön¯8î×Ëuå#Ž_»©ÍÒ[úô=¾ãéZ(¢¼ƒ°(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠüKý°%}Cþ Ÿû8èú€Ûakecs †s}xØû¢Ž¿m+ñ‡þ “£jß ¾'| ý®ôè Ýõ˜,õ(Ôdùk:ÝÃÈþ :p*:µ~Æé¾™¯é6Zî‰uÃÍ´ñ0xå†UŽŒ8*ÊA¸ Æ¿€ò˜Ž_ö.?òÒ+ô{@ÿŠ×öƒ×õóóéþ°‹I¶=TÞÞ~þå×ý¤@±·Ö¿1¾k¶^ÿ‚³~о$ÔŽÛ]/Âw2úì…4— {œ`{×é¯Âk?øGàuÇŠìôv×üY­ÅyâÓÖhí^ööð¢ƒÎ˜ˆâ,¾\{œíSÉàW«ƒýÞ­n¯Ý_=_à­ÿo•½ê‘‡ÏîÛñ×ä~aÿÁS¿äæfû ý8XWî=~þÖÿ ÿà ¿´×Åo|FÓ~EᛇγXZÉâ="ñåœOìòÈ.a&$UxüÇ<~»|ø™ñ¯Å¶:­ÏÇ_…_ðªÆ™ R¤Ï®XêÐ\ä9˜ƒjsÄdê‚pMyGYô5|©ûLþÈ jËm.Ïâ6­®iði)2E“z¶Ñ?œT––9"–7eØ6–^2}käÏø%gÆ¿ü)ñÅŸuûÏiPêÖöº=ÅÚ¨ÆÄ‘î ª‚xIë‚kø(ÿðQŸÙ£\ñ^‡ã\|nðÆ­{%å…âx–sfÍÇ—_¹˜ÂÈ÷f5Ù*~bÏ uo‰_ðMÿÛŸ²êë¿ð”|+øqfÖ‹<Ç<-ªNÖ‘Ê]+43®$”’<¨Älý;ÿ‚ƒÿɘüVÿ°HÿÑñ×Ê>ý˜?h?Ú?ö·Ð?jŸÚoA´ð‰à…·þÂðä7±êeìäyíÌÓA˜°³¹™Û!˜…@yWÁAÿäÌ~+Ø$èøè/þ Áÿ&Mð³þ¼n¿ô¶â™ÿ"µµ»ý‰>)Ex@lí$þüwÖïþ>¢Ÿÿàÿ“&øYÿ^7_ú[q^!ÿnøšìÑÂ}, ¯|LÕl´ë[D9áµ™.¤tQÉýäpÄxë(ïðN»ûÝGö+øWq¨)ISNšüó‚êh¢?Œj¤Wڕ㟳×ÃI¾|ð7Âû©[¯ é–—Ÿq®R0geÿdÈXjö:(¢Š+çŸ(þ›Â¿-Tîð…ú¥éQ’Úeþ ¸àuÚJ0ôÁ>õô5bx—A°ñO‡µ/ j‹ºÓT·–ÚQßlªT‘î3‘ï]¹~%R­ËmŸ£Ñþ ˜â)9ÁÅoÓ×§âl£¤ˆ²FÁ•€ ƒAèA§WˆþÏúõþ§ðö\lë>žmøw2YˆÜòCű³Ü“^ÝYã0ÎYR}ôþeQª§%Ô(¢Šæ4 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(›ˆ,í建‘b†gwc…UQ’Iìä×Ï¿í§ñv¡¯ünÕce—Å2ýŸKGht‹V+ò¦fF§øï¨^ëphß´)Z-GÆó˜n$O½o¥ÃóÞKí¹?v ðÛˆ׺iÚ}–‘§ÚéZlK¥”I 1¯ÝHãPª£ØzË÷8[ýªŸúJ¬—þJûœŸ_(þoü—æ]¢Š+É:Š( Š( Š( Š( Š( Š( Š( Š( 4ø·?µ_ ·îôoˆQ6«d:*êvÀ-äcÞXöÊOµ}^'ñëÃšŽ«à‘â_¦uÿ\G¬Xc«=¯Í,\rD‘n]½Î+Ó|+â=;ÅþÓ«aç¨dye¶ól$_є`r ¬,õ=7P,,.á¹)ÞTŠøÏLí'ε£ÙJmï/à‚Q‚RIQXg§ƒ_ž_ðK¿žøYû-øgÆQé‘Câ@u=Bð¯ï¥·’W6qî<ˆÖŒq¹™»×ÂßðSO†þ ø‘ûnüøm¤iÅ®øÃìk­\D»e¸´¸¼[hžR½|˜`—¨QŽ€`÷¿þ_ÿÐVÓþÿÇþ59Öôan/ ý¸›`“ÍM…‡8 œgÚ¿?þ#ÿÁ3d}kÀ^!Ò<ðò×Kñ ÖŸtšmÔw—ŠðÞ›Èo™”…“i!”©E|Ûâ?ØÖÓá¯üÛÄ?¾(iv²ø¿ÃÚŸ‰L°¸—ìÚ„r´ˆÑJ¸ÎmQ"~Ä(ö^ÏSÓuÂÂî’˜ÝåH¯ŒôÎÒqRÍ{go4VóÏrÎq3ÎG÷AäþùSÿ€øwàÍ ö]ƒâ¦Âž#ñ5ýò_ßmÌÒEk1Š·DjpQ¸“Ô×çWÅŽ:¿íÿ$øYã‹ ïà]'Æšg‡ü=8?¹¸]6þÜÝÍ<ù²Ìv9FEä©Àô×uygcŸ{>ü€±$l ÷mε£ÙJmï/à‚Q‚RIQXg§ƒW¡ž˜–{yXÜeYe#ÔÁ¯çßþ uðÏ¿m‚ÿ t]>+]ÆKi³wíšk[›ÅµŠIHêaŠpO;F:+÷§Áþð¿€<3§x3Ázd>‰¤Ä!µ´·]‘E9ÂrI$òI$’I4·uyicŸ{ ñî›g´×ôûë°ßn†0Yby=¯ ËìU|•Åq›§õBïUÒô÷Xïï!¶fY  1ù[ÿµý¦>%|Zðï~|d¼“TñGÃk˜b[û‰ —W6ó¼Ñ²LÇ—x$„0œ²ºƒÊ’Þÿ­Ñü/y£ü+‚ÏJ†okZ…Í­µÊ¨ûKÙ@ª ¾îèg¹FôbqÔäöïþ_ÿÐVÓþÿÇþ5zÏQÓõÍasÈL1:¾3Ó;IÅ|£ÿÁ3¿cK_ ØhÚßÃË[Ëèí"·¸»—±É4ËW”œÌÀ·`ô›ÿéý”ñŽãt„júö»$Ñ42¬Û´Ûx’;]ì  ä™XŽÛ…}ó6¿¡[ÊÐ\j6ÑH‡ ­2+èA9nÓP°¿Rö71ܪõ1º¸‘5üò|mø#ðËãOü»Oø[…ö!‚ïÃè’æu³’þg”ÆUƒLvR -’rI®Ïþ %û.ü#ý’¾øsãïìØ.þx¶Ï\¶±Âþä¥ÊMÒýÙ¤r áHV]ÁÕ¸ ÷Ίó_ƒ+ÖÌW7ÃPº½Šúúæ&,±È!´š1gkª†%œ8Ú¢¹q8yR¨éÏtmN¢œT£³>Pý›¼=ûlüVø‡â?ÛŸÁZƒ^_ˆ‰%žŠ®uŸOÑíå)vÑÚFURA‚äåö— nOà ÇÄý#þ ï¯ÃñŽ&ןÞ"ÞG¡¼ï§È.ÞXü–¹S˜âRû‡ßÝŽ1_³³Ï„ü{à/‚þð/Ęô˜õÏXC¦¿ö#Ìö- šˆad3džhÕK€$ ŒWæ$¿±ŸíÅqûeCûaI«x -V;…ÙÉ}ªqd->ÀaɰÜXÀI,Oúø0£Îgþ w©êCÀ ü; "ÆÿV¿¸”“„ó­áŽ8·÷f“Ù¯Úï è6ðî•á)vZE¤p(è±[Æ#Aø*ŠùwöÑý“ôOÚïá0ð5Ö 4]kK¹ÚUùŒÊ°Üdd‘Å*±VÁÈ;XWç¯ÿÁUn>'»o|>²š;U±oÇ6¢u'‰T'š7@Ѭåxgòzò¸l5~’xkÆžñ¢_ÉàívÃ]]*êKÃau滇e¼Æ&o.TÈ܆ø¡û}ÚÛ|yÿ‚|ý.#ÚNš!½Ô <«Ewpg¼‡½¥š“ìÕú7û~Íz‡ì«ð2†š®¥o¬ësß]êW×VáÒn.6ªà¸Þ@Ž4RÄdã8¯‹<+ûþÙ¶ÿ¶¼µÿ‹õÝÏ-ÁŽ}>íL¬Zý‡e¹kÌ‘Àr¥ˆùÝ€Ç9|]ð®…ÿëÿ‚ƒøâƒ,ƇðÇÇÑ}šîÞ2E½´R²Û߯2IÙ ˜nÂç Wô2`NAä_Ÿ?ðQÙSâ'íiðçÃ^ ørÚ擪5ü·ºÅÅÌZ!ÞÚãp¾dÝ´ ‹Œžžëû+øOã÷€~é¾ý ¯ôM_VðüPÙY_hÓ\Ìn,á@ˆ×FæOœ , †qù‰ ¤«ãoÚ_öoøÉñ³ÄzF³ðÓãž±ð¦ÏN´kyìôÛy&K© …ÄÎRîÜíÆÓÀëھɢ€?(ÿájïú<àÿü³£þö®ÿ£ÈñWþOÿË:ý\®kÆ-Ðü á»ÿøŠ³Øiñ—sÕ˜ôTA݈UÉ«§NS’„UÛ¤’»ž6¤iCê´Ýÿ™÷}½âîû[–„\åíeò]—ù¿øÏÌø$yãïüsý¤5˜ÏÚ¼U¬-¼ny!ç’[ëµÏ~fƒò¯Ö«ïŸ´ÿŒZoÀiµjšlš´vqÃ#ªYF̦IePR<²¡ˆ$ôŒþhþγOüGö\øk'Âÿ†×¿ gÓ¦¼žýî5 5ÉnšyÕ˜´pÅÂÆª¿»è9ÍzÏìû'|øoñßâíûJx—FñO‹|Wa…œš\·‘Dd,ef¶€Eˆ`HÂà6p~÷’uŸ0~ÝÇþWü;ö}øúMŽˆÖú•äCâäÜÝ!¿e²SŸF¯ÚxÓß|¬øóÅ×kc£h6’Þ]Lä±B¥Ž3Œ±Æz³$Wä~ÇŸ·µ·í]©~×6ÚÃk]¤ÛYÞÝkYZBÖÂÑ1¤.Y!wnÁ%˜¯­æ–BïŒ3ò‡n—þsû ÿÑ3ÿÊαÿɵ÷ýÄn|‰ð«öý”¾ xâÃâGÃÿcxLY–ÚëûKR¹òÅÄM Ÿ»¸¹’3º7eåN3‘ƒÍ|åÿ€ÿ“;¹ÿ°î™ÿµ+õ"¾&ñü¯ö=ñf¡{ªxÀÓ_\j3Éu9}oXØóJÅÙ¶ Ћ–$€ªèìŸ{m‘TÕO„3|9Ñ"xB} ¾ÇªZº^i·c†¶¾€î†@{sòOzôŠ+jåJj¤šÔŠ”Ô¢ã-™æ? ||ÿ<&—ºŒ?c×4Ù^ÇU´<5½ì,‹î·Þ^¼ÎA¯N¯šü|­ðâ-¯ÅË@Súù‡Nñ»ge­ñìåȺGœ×ÒjÊêH Œ‚:]™¦«R^äµ^O¬~]<š}LpÕ°–ëñìþÅ¢Š+Í:BŠ( Š( Š( Š( Š( Š( /â'ÃÏ|WðV­ð÷ÇÚlz¾ƒ­Â`¹·”pÊy ¤r®Œ#© ¬ñ?ìãðOöžý—ø<׫Žýåuúü/Ö;ä¶_&rP÷g*5óßñüÍj(¢¼£¬(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢õ¦äЀ}ÌñIÓ©¥qØ’ŠÉÔu?K„Ü_N°Æ;±¯;½øÓðêÆAÆ·18ÁcþLjÌ(ÒøäoK R OZ¢¼WTø»á9­s§jhûº29­eh¿48f {©‡Vþû–þuæTâLtq¢¸dø…áG\®¡ýõL—â…Õ øø÷¯EæT7æGÀÖ½¹YÞQ_;ꟴ†»>F§±PôY¥u:/Å ÜÆ}E7V5çQâ<<§Èݎʹ-xÇ™£×è® |Cð‘ÿhÅÿ}U;¿ˆÞŠ&uÔ#8š»çšPJêHㆫvQg¡Ñ_8·Åm,^—“y`ôó?*ô]3âo…/#Qý¡óêÕÃ…â$ã{xŒš½8©Xôš+ /i3'™ʰ##šÅ×|YacbÒ$à1à`ó^L}(«Ü↤¹NÞŠð½;ÇkâK‹ã"7P\œ~f½BÏÄúEÜBXîPƒï\xîu£±ÓŠÊêÒvhé(¬Q®é™í ù×âoÚZŠÚëk7ub?•tbó:T`æÞÆ8| J“PHôº+Æ´OÚÆâ ˲ä÷w'ùפEâ &T.SÞ£›Ñ®´v+—Õ¦ìÑ»Ec{JÿŸ”üëÍÒœûÖølÎXsFH¶¤%ÊѱEpúçtM*Ù¤šíPöçå1|WÒÒìÌÚ™dÏÝó?*âÆq 2Q“:°ÙMZ‰´£¨¯:±ø™áȕΣ>›ªÛ|Að°lB0>µÙO5¡(©Fhæx©´âÎêŠò|Uð¶hHÔ‘N:† ×¢üxð‹]ùjâcžCHN?:ó±Nkt²Zó2GÓ”V&­Øëv‰wa(‘AÍlJ÷(ÖH©AÝeJn.ÒÑŽ¢›ÎiÕ¥É (¢˜‚Š( šî‘£I#U’N©&^ ñëZÔfÑ4ï†^”Ç®xîàéñºýè,ÀÝy>=,\°ÇJêÁaj±¦¯×²êþKS*Õy"äf|Gñ犼Gñ¾ñI·ÔØéz a÷4»G!¤\ôûDÀ¹õ}YZ‹§xoE±ðþ‘‚ËN‚;xP q¨UúœOsZµy†%Uªåhì—d´_†þbÃÒä…žý}BŠ(®#`¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š( ƒ_:|'À¾0ñWÁyþKk N±£Ðé·ÎKÆƒÒ ÷/ü ¾‹¯ž>:Ã/…n¼7ñ£OBdð•Ï•¨2iW¤EpxÆJºŽØ&½\­ó¹aŸÛÛüKáûþ™ÉŠ÷mU}ŸË¯ùü¡è¨¡š˜c¸·q$R¨de9 ¬2=Á-yMaEPEPEPEPEPEPEPEPEÇxÿÆzoÃßêž/Õ~ht苬`üÒÊß,q/ûNä(úÖ”©JrP‚»z"g5äöGüDÿ‹¥ñ'JøCoûÍDòµ‘ʸS›;6ÿ®Œ<ÇSü_HôàW|ð^¥áo I«x£çñG‰¦mOUr9ÍÊÂ=ÂÎkØk¿2«ªÝã =_Wó{y$sábìç-ßàº/ë­ÂŠ(¯0ê (¢€ ç(üNð_Á¿jÿ~!j ¥è:$BYæ`IË0DDQË<ŽÊˆ£«+ùéÿ‚pþÕŸ>xÛãÅÏþ-Mľ;¿Š[xÍ•íÛåš{›–ßmªK" ÉœPÛß>þÔŸðP=CÆÿ´–ªþ(°ð7†¤]Wµ°{-(˨”C²ìXüÅGŸÍùø]ÌFÐ>øý«ìþßû.ü_´-'„5í£ý¡a1_Ô üç×ofý¥¿jÿ„e½vò OûCÄ¢Ûµ±Ô-­#k‰-Q.eH|¨Ü;2©gu Ð[õã­ŸöÁˆZ~3ö¯êÑc×}¤«ýhðÇà‡ÆÍsá¯üEð/€wOãÿ‰þ!Ô|/ AÄÞeìág™;-j°û²%Í—‡áeýÑÕõBe¹˜Ax¡a¸õËBG)]ßüÓQñ–“ûBü Ô>¤â¨¢¸þÊ.é~Ü×Q-¿–;¿˜Wo¾(è‹;ømŸÛÊ]^_ô¿…?³“ù0̇‰²Ì;0ŠHòHÈÄ)ü3súã_7~ÉŸ³þ™û4ü ðÿÃ+b“êq¡»Õî—Ÿµjw 5Ä›º°О|´\ó^¥ñGâw‚þ ø Wø“ñ P]/AÑ"Ï3NY‚""ŽYävTEX@Žÿ‹çÿñ?ˆúF™ð³Ož8³Ê£ÙÛ%ƒ/¦EÕÔ®¨Ïc_¹ÕüÒÿÁ9k/Ùÿá?~0|\øÿâ¯ì/øòú9­ã7׌c’Yîn[}¬¨ß,ˆ0Ä“8Å}máOÚÑÿmÛÃá÷„> ê:•ŸÃO‡Ö·úÆ¡"´¶Ú²¬aæ„&™ DIFNç%Fà(¡ÿ‚ÎxòMözðÇû&óÆ:ôe£^L–Ö4Ž“ç<ã_´Gìçû}xGöZðï Æ~%ð¸]3BŸCÐ4wŽ÷ì¥B†žtY]¢GAç°(_*H¯0ý¾¿h?„_¿n…~Ö¼Að/ÂÛèW]»Mð͇‚ü%¢x;Jÿ- ÆÚ ÿÏ+X–$ÿÇTWÈðPÚŠÙ{à&¡­i ž1ñ.ý7BN $ºÇ÷mïÎÞcSÃWSû1øÏ_ð¿ìቿ´‰žæõôS®êÚ¥ù£·»-uðŠ1åÀè˜ ž1Œ×å÷ÀoÚ/áßí‰ÿ%OøÊïÉÐü1§]ZøMºFÛ=ʺwL&x_İDž¼eáýF(ü[û?µÝΧ„Ç娴÷ɹ1ÏgódgæB£‘^–Zá):56šµû>ïÑù6sbSIN=?.¿×sŸý©ü.ïø*·Á„‘þþÃÀðÚjQžUf„˪˻° P.\㸯Û}sZÒ¼7¢ê"×nRËMÒí庺žC„мŽÇ°UŸa_ÍŸìÝû[üööø­ûO|^ñèúV©Õ¶¾Êîêi•å† s²Ú)J2ÚA´†Àùð Å}qû@þÐ_ÿn½_ÙóöFð^¯má]yÖwÅÚͬš}€µVÜÐÄ̤„pùjë”X°ÄŸ4é8ø#®™©x»Çÿ¾7Ý[´všÕäC)WšæyîçAî¢'ýñXß·§‹¾?ÁI~üø}¨[Øê~±·¼Žk¸Åµµäf]QÞhîFî™æ¿XþüøoûüƒÂV÷Ék£xnÚmKXÕn>A4á7Ý]Ë×já~Uçj*®N2?gÚÇà$·÷ÅoÚkâÿ‰ÿ±tmFËmIlîîTia··}¶ðÊÈEœ8!€ûø }©üý°¼Sûo|ƒã߈—ƾðÇÚ|A擦=Ž—eymùi1TÚgy’-›äc´¶Ààe«ñ/ö—ÿ‚hŸuo|ýŒüKw'ˆ|e­ØY]kÑZKl-!šeŒEÝÄŽ]ÙwÙµQHÉÜvþ~Öß|5û:üñ7õÍHYêY\YhÊÄ™.uY`“ìÑ®9Ü»˜ôUVcÒ€?f-ö‚øûbüyý¥?gÿ é2š ‹»M^õ¬Ñ-¯nI´ò`<žE˜C–PrFEzwÁm'Å?ðT_‰Z…ÿí3â(t=7á-øGð—k-¸w•2Ï<²;ÏE& 8 Bù;ÁoOÿ‚*j¾ௌ4 J)|_6¶÷—Öœ‰£±C³G(_ÌÁ©"¼_HøƒáØÃþ ±ñ ãÇ÷ƒFðÄ&•ïXÁÕDWË3……I$%±…ÜIÂä€è&ÞÞÞÎÞ+KH– UHãE ˆŠ0ªª8*jdr$¨²Æw+€AÁéO Š( Š( Š( Š( ›~&Á7ÂÏÚ|nÒ£c¤Ý´ÿÃÎëbvÁ{´uhícÉ(p02kèØ'†æîm¤Yb•C£©YXdGÐÔ†ŸeªØ\éz”+qiwÃ4N2¯‚¬¤zpkÀ>j¾ñ ÿÀŸLÒÿf!»Ð.d97:[6Dž²[îà´f½‡þÑBÿn ïùÇòòGýÝKt—àÿàþ~§ÑtQExç`QEQ^ueñwá~§ñ…:gŠ´ëÏZÛÉu>•ÊKu Q2£™cBJ]~VÃ`ä kÇüûkþË_ ¼]¨øÇ߬´}IdK«I#¸w‰¤E‘A1ÄË’Œ§ƒßÖ€>¥¢¸Ï‡¿üñ[Áúþê‘ë^ÕD†Öî%uID24/"«pèËÈ=+¥Ô5=7I€]j·pÙÂα‡šEK¹Úª 2Äà¤ð(õQ@ wXÔ»ª ’IÀw5óŠñïÇø¶èð….iq7ÜÕ5‰V»aüPÂr±z¶Nz­\ø­_|Gñ#üðµÓZØÃÏâ}F3·ì¶MȵGè%œuþêdàŒîÞƒÃöÞÓí¼(`:<0¢Z}™•áòTav2’À듚öaþËKŸþ^Iiýؾ¾¯§e¯TÎ)~öVû+ñ}½_?™¹E|§ãoÛƒöSøsâ½KÀþ6ø‰e¥kš<¾Mݬ‘\³Å&ÚJDËÐŽ„×®éÿ>êŸ ¿áuÙxŽÙ¼öi/?µ[tp"fGs½U€ ¤tÉ=+Æ;OO¢°ü7â øËEµñ'„u[]kI½]ð]ÙL—J¾©$e•‡ÐÖåQEQEQEQ\'Äoi_/ÙþÑ:†½ã= Ø_ÝhözE´v2"à͸T%Ú0X«G˜<~ÀÑ_5þÍž6ñÍ×ì¿áˆÿuMzmµm^îhÌG o¸W–(Õ?.Ü®ü(äŠø'ÀŸ´íEû||A×­ÿg}q>üðÍÇÙeñÙG{ªj2õ N6£2åWi‰HÞìX!ýŠ¢¿>$|lý¤¿`ÏŽßô?Šÿ_â¯Âψ2µ³Mc¶¡`ñINêð`±ŒMä«®å ¬7Wìýó¿€¿â†øÍã‡ÏòXxWÄzpì$ˆ¯P_0+…Ͻ~|Dý°¿jO„¿¶Tÿ âÿˆ|áÿiºmÓÜÙiéç[Ë,o=´º`…ó#%6Ÿ—#iÅ~áþв'„,ü=ñ¥O–< z&¾ú…] æÝR¯Ïi5êeMJR öš·Ïxþ*Þœ¸½¨¾Ïå×ð>Œ¢¿œ_þÛ´ïí_ûgØ|4ø5ã{¿xÄZ„‘X¤66’É•cÉ5Ñ7»y²Ç ¾•W`?¼ßlµ˜~ø† 3Å—~Ôl4É&]r(­ä¸…­Íiš9#0áñå†;vœåGªQ_Ï×ü[öÈý¤~-~ÑðøãW‹®5ÝYÐ/®l žÚÖçÃ,l³)†(Øá#•pNIÇC_±´Çǯ~ÍŸ|AñcÄ[fm:/.ÆÔ¶òþl­½º÷ù›—#%P3c hÞ¨¯Âø&¯ÆÏÚ×ö£ø­âMwâWÄ‹ÑáO,2ÜéÑXØÄ—WW¯ ŠÝ›ìûÒ%X¤-µƒð£pÎkìŸø)wÄ_‰¿gÑñGáo®¼#«i7ööâ µž+ñxê…$ûDR2˜•YÔ¡ÃÁPÐú+òÇÁ¾8|/ÿ‚vx‹öŽøµã›¯xÃ\ðÿö¦š×ÖŦɨ'M,P§˜XÍ ænü ß1xãSý¼þ~DzþÓŸ>>]ézÕÄV3Ùøz=OvÛðäôÐÔ±Gxþ2ü@ýœ¡°ðM²¬÷G¯Æú­«°tЈIAË)@WøöHòŽ£õŠò¿‚Ÿüñ÷ញñ[áüï6­ÆYPx$ŠK Ê¥‚É‚¬#¸%H'åÛ›öÆñ×ìycáÿXxËÅ>×$6fâMM­."½äòü‘o 1˜Ó!ÃõÈ*8,úE|×û$~Ð_ðÓßôOŒi)¡\js^C5ŠNnµ¸’…¶äUº1»ã5䟷íÃáïØçÃÚ G¤'‰üOâ)›ìúa¹û0KHGïn$p’Š¢ ¿1'äjû¾Šù—öOøÑñöøOcñWÇ>¶ðm®¹ûí.Þæ½–kN@šPaˆG¼Œ ·) qÆ~ý«à§¿eŒz½wáu†§F·–7qkN Å”Ìë ’'Ù—!Øw&NÓј`ØŠ*Ž™¨[êÚm¦«fwA{ sF}RE §ò5ùQûRÿÁO øñÉ>|5ðL~>Õàû=½Ûý¼Ú„Ôn˜´Œ,2ïp7Œ3lÆTÐë-Åhú‡®|§­hÖV^0{&‘ôØï^[5¼ØJBnü•m…°ÄG­ŸÎOÙ§öùøÅûCüvÕ¾ Cð£OÒ£ð¤Ó®½¨m¼±ÚGo?ÙßÊÐ ¤â5CN@€Õ +óÆ_·7Æ[ÏÚoÅŸ³‡À?ƒ©ñ ãÂ+ º¿mYl"MðÇ$†V–a$sMÎTí¥yÏÃÏø(Gí5ñO⯊þ x'à6}⟕Õ!>&Ž8akYÅ´Š'kq0àlœØÐëõùéû$þÙ¿ho‹~=øO㯆±øïáô{5]Kí¬—¾’ BˆA #W …ã ä~…ÐEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š( &8§RdqRû€ÖÀ5æ_¼smà? Í«\08ÏéŒFkóoöÈñ”×úŸ­[÷ó`zäŠùÞ)Í^ åÙídwÖ11‹ØùóÄ¿þ |O×^ÃE–O(¹A8ÆkÓ¼9û-ø‹ÅVI¨jZ„‘Ü> \ôýkѾ|#o èñëú´n&‚GnÕõ.•¬M§L ŸÝtÅ~_–e®¤ù±—>û1Ì#É…KCâ¿Ù‡Çºi0Y_I,c¦Oÿ^¹[Ÿ¿ìä1Íw"â¿YtýFÞþ*0ÏqYZæ£ x¸qÍ}F'¨NÖŒ ŵã% ¨ü±ƒàÿÄV FUÓGû?üJž!"jR0>ÿýzûFâíe0ÊJ°<èô-vK9W'rõâ`rZ×ÙVlïÅfÕy}¥4€î¿g?‰j»šöFaÏ_þ½sR|ø… •’îEe¯×8fŠâ1"r¬+“ñ…ö€g€|ÝMzùÒåö”$pá8¾·7-DË+…íãÝoy+Lš|>2Ì»’I?íûþUžÞO-†ÂµÒè¾ – <ƒÞ¼LCJRå­Q£Ò¯›Tåç„QùŸqðSâýº4ŒƒÔäšÆÿ…eñN7á¤R=Í~ÌG$p|¸`¼ÿÄ:±“sù{ס˜p$azSlåÁñtå.J±Gå¬ ¾,NûyGâjåÿÁÿq[y°%ÏmÆ¿AÖIblƒ´Šï¼=âr-®ÿk‡Ãôj5NUføÜæp÷éÁ‘— ¾8[&÷´“Üÿ…TÁ!#ˆUeÿh×íÄÐCuZ¼»^Ñf±”ʼ¡®¬Ã‚½’æ§6F ‹ÝOv¤QùoeáÿÚa¶ßÍ´jÜž ý£f]³$®½¹?á_¤šÕ¤¡¢?(ë^­¢ëVÚŒA ÇZœ¿†á[Ýf˜ñ¼AR—½ JÇãö…ñ»L.î9Uþ§Ê›ü.¨F#’eöɯ٭sÃv´mçBûWŽ_x~ÞÆs°€{qÚ°Ìx>­ ^ÐéËøšhÚqÔü؆×ãìÊ%…ç }i“hßgmó ˜þ?á_¨šÍ”Ë ± †½RÞÇJºŒIJG°®œ ûxë[^Ç>+‰½„´¦~)ɧ|gñ •X}jÒ7Æá„IfãÜ×ëö»àÝ>ê&šWÒ¼Â]ÖÚCBǵpcxV¶ZOC· Ä´«-`®~p-ŸÇÙr4ä~5R]ã³2e˜ãëþú³á닜[ÝD=z;húdñàÀ¥Xzz×¥…ሇñnÎ G{YS±ø Ö¿ðeVZ¸’|pb#ŠI¿3_«ž#ðu•«5Ì‚½pq‰§ÚFwˆ†áÒ¼ŒG ס>NsÑ¡ŸÓ­nD~r›ÚÇYŠŸ¯øV|Ú/ÆøCM"MÏ^¿á_­~¼Ó[mµÜKº»y´M*î¦e#Ò½z<õˆsû[³Í­Å^Ê\¾ÊÈüSŠŒñÑ´ª~¦¯B~;Lå"–bݹ5ú•®øFÚÆBñÀgœâ°m­mm¤Y  ~µãχkÑ©Ë)Y¤3ÊU!Ì ›?7$ð·ÇÍg÷3¤Òrqüª•ÇÃo¶‹¶k9õÜOô¯ØÏ ÞX\E±c ®ŠöÆ+¸Ln£=«Ý\ í©óª·gþ¹J•N_f¬~)YøãȰ´rƾ¹5ÛØ|#ø»|u<¡{üÆ¿F5‹tÛ‚§îžõ›«Üi²«îÜ„×öU9*NÇ¡_:•Z|ôà…nÿg‰W6~eÍÜŽÄgnúõáž%ø]ñÁÎ÷Vò,KÕò{Wí¾™©C¨D².3éYž*ð¶›â]:K[øDˆÀðE{ØÞ§:\ôg©æ`¸Â­)¨Uбù!ðwãψ¼­ÃkªÜ4¶,Áv·lñ_®ÞñŸŠ4k}^ÉÃ$êƒê+ò‹ö„ø2þ½:®› [lä€8×µ~ÊnM“I½˜”„mE'° W—¹Õl%w…®ô;¸‹,¥Š£õª Sôk"ŠËÓïVö"Ö¥~ÃN¢œT‘ù­H8»0¢Š+BŠ( ¾qø[ÿ∾2OûÍ6ض‰ ç•6Öïþ“p½6B·]ªA®ƒã¿‰u=;¶þðËíñŒç]*ÇcßÎqÈX¢ÜI ½;žÓ<á½7ÂÚ2yvZ\ c¹1¸ú³’{’MzÔ¿s†sûSÑ…nþoO”‘É/~ªHëóéþqÐQEäaEPEPEPEPEPEPEPEPEPYÚÆ•a®é7º&©šÏP†KyôhåR¬?MhÑN2iÝ «èÏøªßÛø{Qøi¯JdÖ<tt×fûÒÚc}œØþëÀ?ݯz¯]?Ä(¢ŠòΠ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯›µ¿ø»-|0Ÿ¼ðÏÃÖŽúÿº\jÒ ÛB{æFÿhíaÒ½/â·Ž×áß‚¯5ø¢ûV£![]>Ør×·dÔåŽHí¢øKàGø}à»m"ú_µj÷n÷ºÉå§¾¹;ær{àü ÿt õð¸£,KÝû±ý_É;/7~‡%o~jŸE«ýÏôó=.Š(¯ ë (¢€ (¢€ (¢€ (¢€ (¢€ k*º”pX`ƒÈ ö4ê(­¥••„^E…¼vÑç;c@‹“ß Í$ú}…Ìñ]\ÛE,ÐÆîŠÌ‡¯ÊHÈü*ÝcøƒÄ^ðžwâ?jvº6“`žeÅåìÉoo d ÒK!TQ’I€6(¯ðßí ð ÆZݯ†¼!ñ/Ã:æ¯|Ymì¬u›+›™Š©vŬìB©c€p={QEQEQE„Xd¢–ŠùÇáI?|u¯üº;4â[YÐ éö+‡ýýºÿ× ‰ÀêU²x¯£«Â>;è:˜Ñ4ÿ‰>‹Ì×ü 9Ô!EàÏi·–äõÄ‘dñÎTÖ½ÃÚî›â ÃÄz4¾uŽ¥w¿ªH¡†Gb3‚;+ÖÌ{â—]%þ%×þÞZú߱dž÷¥ÛoOøzXØ¢Šò¯|vø!à e¼;ãψ~ðÞªˆ²5ž¥«YÙÜ~UŒSJއCŒÕ䇪Ñ\ç…-|B‡gŠ|@a¶<3OtV©žŒ~ô§-Æ2}L F/Y^+eüÏ·¢Ýùi»G-y¶ýœ7‚þ¶ÿ€~"þן ?lmöañ'~%xŠ×Á^ƒQ‹í>Óæ77º³ÞÜZëS¾‚ÈÌ̬°‚Ñ„*® ?°ß°Ú*~È?ÕFðõ‘üJd׆ÁWÿäÉ€ ùãáö›ñkÅ‘ühñDd? XJ0R&á¯åSÿ-&ÿ–ÝLŸ•ª¯ˆ%“ã×eðEƒà/ \)ÖgS…Ô¯£!–ÉuŠ#†˜Ž§Ž ún(£†4†Ç ª£@à@+Ù—û-._ùy%¯÷búz˯e§Wn%ûÙ_ì¯Åÿ’üýÑEã¡EPEPYÚÆ«c¡i7ºæ§'“g§A%ÄΆ(T»ŸÀkF¼çã©x¯áü/£u cCÔ¬í‚õó®-dŽ~ËÇÂ$¾MÿÄ+ôÒ¤`pËa4÷E}wíH˜vF¯©?bŸ„6ÿ?føcT¾:|w÷äZöÿý&lŸâØÏå©þê-~vÿÁm<)¬j? ~øÎÚ 'Ò´=VòÚð ùPßE‰œŒíÀÊL°Hõ•¾1|(Ò>Û|aºñ•ÚÆ;Èõ•V³º‚HÎXýЃ,[å·øÍÿˆ¹ÿ„ëâÇÀ‚º9/«ßOu!A÷¿âkskimŒr2ðÈ+÷ŽþúÓL±¸Ô¯äÛZFòÊíÑ#Œf>À ×á?ì¯á~Ü?¶Î³ûjx›NžÇá߃æû?‡c¹ëæµCª(?óËs]JT³²¨$g¥·Äh¾~Èÿ¦EšìõeóKñÐ)= çâo‡uOþÆš¿íOuE«xÃâÝåòÈ~ðµšÖfVÏû7!×:Wë×íñ;^ý¡õ~Åÿ®Ì:§Äû[]cÄ÷±|ÃKðé 3n#£M×i#p ‡‰¯1ø•ð#S¸ÿ‚?xcÁžÒçÕµˆ4­_†ÒΞy'Ôo#»œ"F˜ª]HNàzWiû!ü+Ö?e/ÙgÆÿµ'ÆS!ø­hO1¿¸³Óôû]ºmG`‘îNLq‘˜Å|»ÿÄð:ßí¥ñ;Å‘¤ré¿ ôàټ@l ’%…´€Žïmi)'«,NsŸÔ_ø(ÄøW?±ÇĽZ)<»OO< 36©"Ú8_qŽß@kó—þó§àí'Pñ± {/Š÷vqHNXÝ豤©½BââlÉ#½}ÿEÔíø²yß?eí:þêØç6úÏ‹’ò ‘Å0P>ðÚ£øn+þ §ðãŒ>(üñ?À½'PºñÉ}¦­ÞŸ „XHe¶6ÒK2)X2H|Æ (RsÅQý¹|á_Øïþ É¥~Ͼ™ ߉/쬮§Q¶[Û…·^Ý÷¶³À©Îv£$yÀÊjz—ü»À'òþ£ã{˜ÿÒ|g®]N’­md«jƒ=ñ2MϽyÏüO_ÔxL³iÿ-íÞ@(.¬`›V$z·˜ñ#c¡^zý»ðç‡4Ï xgJðž›~i”@ÀŠÙ4éÀQ__ðYïÞÏðßá§Á­2j0מåbSóH,c¢f–íõ+í_´uøMûI_Yüpÿ‚³üøUË=Òââ<‚¢êÕ&Öe·Íp©Ø4ûKàßèþø{ ü:Žî4Í M´Ó7@ÈÑZB°¨*r v1E1¤0 Ž8ÀUU :O¢€<×â·Æ†ÿü(|qñS[@ÐÄñÛ™#–Uó¥ÎÅÛ ;s´öÅ|Íÿ&ýˆÿè©Zàÿÿ#××~.ðG‚þ iÃþ<Ð4ÿifE—ìš•¬W–þbgkùS+®åÉÁÆFkË¿á•ÿfú$ÿÂOÿãâ¿ðòoØþŠ•§þ_ÿò=} ð{ãÏÂ_Ú-øŠ/éÚuÇÙn&Š)¢ϱdØDé?+æ°ÿá•ÿfú$ÿÂOÿãé øwðÿὄúWÿ i~²¹—Ζ *Ê ¤—h]î"+6Ðâ3€jìkðËþ ÿ£öòýž¿g¤ÿH²ÓäƒP½Œrw—aîTQme»ž½Í~æ×áWìïqÇø+WÅŠ(âãKð¥å­¤ îŒKl‘i ôÄ€Ï #ƒÉh÷V¾0ÿ‚…|@ÿ…qûüLÖ#“˹ԴïìˆppÌÚ¤‹hÛ}Ö9]¾€×ûV~×—Ÿ ¾!ü5øðjïNÕ>!xç^²´ºŠaö”Ó´Ùd $³$n¥]‹ÝWb8ùgþ 9ã;‰þü8ø%¢¿™ªø¿\7~Bã³ÈXvW–éqž¥8èhê¯ø&?Ãÿø@?c/¬Ñùw~":ÌücwÛfc ~6ë|ÿ=—ûgöÔýœü+¡Ÿ+\óìdN\}«VHà8ÿeâr+õ‹Ä?¾ þÉ¿´};Çþ'²Ñ4¿ iv¶ð¼ŠngK(V$K{p|É\…USêp2Gåßìyð×Ç¿µßíg«þÞ4¹ôiÓ2xVÊíNg Alcƒ²í"ü­pr¤áðûEPEPEPEPEPEPÍ>ÿ‹9ñRoËû¿ xæY/4“Ñ-5/½qiè«(ùã åTšúZ¼ÿâ€í~#x>ïòJmnÁ[‹¥áío!;¡™Hän¸ä©#½TøOãi|oá(®5@"×t©OÕ ÌWöß$ì~uÇXW¯Œ~Þ’Ä/‰i/Ò_5£óWêrQ^ÎnŸM×ê¿ËËÐôº(¢¼ƒ¬(¢Š(¢Š(¢Š(¢Š(¢Š+çÿÅkûAxƒÄ'çÓü c‘lz©½¼ýý˯ûH#o­{g‰uë/ x{Sñ.¤qk¥ÛKs'bV.@÷8À÷¯.ýŸtÝ#Ꭻ¬ø›øšYµ«æèL×íæŒŽÄFQHö¯Wûºku~êùêÿoûxä­ïTŒ>vßä{eQ^QÖQEQEQEQEQEQEø‰ÿbø…âŸx×áwì_à;Ÿ*ïÇ–·z€€ââçì¶ !òÍdYeî#qŠý»¯ÂY¶±ÿ´ð­¯ˆ0m-l£–È? ˆt9ænzbãqãÖ€?h>ü5ð¯ÁÃ?Ú‹=ÃÖ©kó6Þ^G=ä‘Ë;·vb{ךüCø འâ¿áÛJñ_‹¡‚êïQŒlš{½0´wrp»ÆF8'9¯VøƒñÁ¿ ¼ªüBøƒªG£x{D‹Î»»”3,i£ Ù˜…TE,Ì@PI¼ûâ7Ší_±Ä1 ÍdÔÌ{z*xmРî(ö¿á׃ìþ|?ðÏ€tü}—Ãzež›KHV?ˆZþl¿lý!¾5xÿö°øÈ‹æÚ|1¹ðׇ훨ý)m.Ÿi¡bÞ›½ëúdñ6¿§øSÃz¯ŠugòìtkIïnû±[Ædsø*šü'ø ðòÿÆ_ðK¿?µÈüÝ[â=ƽâY$ê]tÆY÷ÄöÓîhíoþÕ¶¿`O‡ß-Tj,ñ†ôk È1îµ››4@]bpÎã¸]£æe¯É‚Ÿ³æ¡§ÿÁDþ|2ñtÍ©ø¯BXY{hI†3ÿ¯£?àš|gûIê~ø£ñFøà=“i´l˜®µy$3IvAà´ÑŒôܰàæ7Ïeÿå#ã7íµûCþÑûûHfšÂÆCÈòoïÁ´÷+odªHìÞôûKãŸXøÁ^ ñ¾©ÅŸ‡´û½F|œ~êÒ&™ùú)¯È?ø#…/n~üMøÑ¬ú‡ŒuäµiXrâÊ3;ºû4—l©_jú³þ kñþÿìeã·†_.ïÄIm¢ÁÎ7}ºeY×ÿÄ¿•tŸðNïºýþirG²ãTÓα1Æ RF»B}ÄR"ý iÐþ|&ø Ä/‰þÒ†™qâ9®|Aâ ¶šY¤¹–’WbfwØŠ •6¢äíQšü¶ÿ‚6臈ôïŒ?uÔßâír;_0ÿÏHƒÞ\à÷ ÷iŸ÷E}³ÿøÿ ëö6ø‘¨Å/—s«Ù.Î NU¶¤/#}¬ø&ÃÿøWß±—€aš?.ï_ŠãYœã¾ß3I à?”? úoÀ_þü5ñ—¼}á+¶Ö¾!]Cy¬Lò¼žt¶áÖ=ªÄ„Qæ7 ÆI5ëQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¥4qÖŸMn”\.\E’7ER!šü—øµ|}ÓdŒùˆ$Û«õƒW`ºeÑn‰ÿô_•šµ¾øß ¡ªœÿãÕùßTæTé=®}Ÿ«:“ê‘úmo¢ÛÜhV–Áí‚01þè¯4Ôô©ôÙ¶8ù;öÛ,-œq•SÕ4Èu™X|ÄWÑ㲸U¤œw±âa3S›OcÈ´V]:`A%Zö ?P·¿‰LG$ŽExî«¥M§LUÁÙØÓô}^m2PAÊW‡–fÃOÙÕØôñ˜X×´§¹é:öƒ¡‘ ¼žx&µ™¡•HÁ¯mÓµo¡#d‘YZ惡’!‰1Åzù¦Y Ðö´Ö§é?gScÐ5÷²A3e zŒ3Åuä;•«Â.m¥´˜Ã0*Êz×I ëòYH!²‡¹¯;)Í%쪽¬~^¦½­3«×¼?äEájòù –ÑÌRŒ0ï^óÑÜÄ" µrþ ðú^Æep»s¬¡V´¤Žl¿âýMŽ7E×f°uIäÍz”SÛêà©Ü®+Ã'†H$0Ê6•­­]¸Ó¤Xóº:ó²ÜÞTš¥[c·—©®z{›#ðëBææÝrjâU¤FÜ>V^ßJ÷K[«}Rß#æWâµ»››eãÚµÍ2´Ÿ¶£±9~>ߺª[ð爳‹k¦ö®êx`½‡cʼ 3£e×S^‡á¿©Å­Ñäq“[eYÂkØÖ2Ìræ¿{]Ð$ÓäiaF9¬K¹¬åÆ5î“Á ìnVåæ…-„­$`˜Ísæ™T©?kGclb¦½S¾Ðõ¸uÂ1ùý)uÝJUx;בÚ^Mg0’åzŠõ½Z‹RŒ)?¼¥z ˆ{:ÛœX̨ÏÚSØòKË9l¥heR1Ð×Gáýq¬œE)ùMwÞ‰ü,È0õä7VÒÙÎa—‚½ xøŒ,ð•}¤6=Jˆb¡Ë-Ïy‚xîc‡ ×#âO­ÔfxW =+œðÿˆ$´•mçl©¯RŽXîb „2‘_EB­&Ssn¸aÏæøxß•Ôÿ*ù–ª`«]l{Ñ•K †ŽP@íFŸ=„ë"1ëÈö¯/Ì*aj8T~éìbðTëÇšžçUñ;ÀÖž3ðÝÕ„‘‚ò)Á?C_•ÿ'ŸÀ߆)Îcbxÿ…~¾iÚ¼Z¥‘ üàr+ò£â¬v6ÿìðö’\^kËã<5)rb©hÛHôxR½Eφ©µ®~‘xBús:ª’Ñ0ë«…ð%”ø~ÎeÃ3 9ü+¹¯½È©Éa×1ñùœÔ«JÃÍ- ÇjZö#±çW|oñ~¥á;ÃgwˆüI4zV– à‹›¯—Ìö&ç'  g­tápò­R4£»3«QB.O¡Éxþ.WÅ­wâl¿¼Ñü3æhz.yW”ß\¯û͈ՇA¯£«“ð'„4ßø?Ið~’?Ñô¸ ØÁ‘ú¼‡ÝÜ–>æºÊß1ÄF¥_sáZ/Eþ{¿6ÈÃSq½»ÕúÿZQ\áEPEPäÏüâÇÇo‚º€¼Cð7ÇwÚ.§âKæÒ?±mím.#¹!ZO= ÐÉ 3$eCm ®9-ïßðNßÚZý£ÿf­/Å>0¿þÑñ^ws¥êóHÚYâa,R@ª7Á$yÀ°lW†||eø­ÿ2øðÁ€¹Ó~iþ%¼^ª“N$†¡ííÏ=ž¾}ý‚e—öcý»~0~ÉZ‹4yä¼Ñ‘Þû.n­BW°‹‘Þ ;pë¾ñ'í-âïø(¯Œ~i¿õCðÿÁPA®^Gö-;ÌÙp–ó&œ$û7Ý-pv7yjÃ;þjûSöØñ‹üû4øÏÇžñeσµŸ Ú¶£Õ´VóyÏ*¶Ò-Ìr.ÉY” 08 ã ü›ÿÛâ7ÄOÚ3ö‘—÷±xÃÅÒiÖH³Ó÷ÈOpc¸„Ðì•¡ÿøÿ—ì‘/…á“lþ4Ö,tò áŒ6å¯]¾­ÑOûÀw 5ýü=ûh~Ó?ôÏŒZ÷í©øgû^æî;kh´-6åZ YLföHÎLˆãíÖ»Ÿ€~ؾý¹5~#_ø×À>ÐÛT[Ét»k(/ZåaHc&(¾Y#yXáe9òÉ<µó¿ûTþÑß²¿ìiàï‡~ø/«ø2úÞÊÏKƒÄ·ïoqoíÇ烙+UI<îd1¬¸Hb6ŸÝ[x‚ËÂ:Ÿ‹.ÿ´5È,m£¿¹Ú‰çݬJ&“laQw¾[ Œð Ä?‹ŸkÍþ £þÌ:'ÆmFÏÃ>.¸‚öÚìý9§µ±¸I&’%;£ò¤HÙ», ÎM?kÿpþ̳—ˆ>!Éuör+uÓôƒ0M÷¥Â”…™@Tm˜i¤P*Œ üíý¦!þÏÿ‚½ü »óF²$û™µ8¿Ýüf”þÖŸµö§áHÒ¾~ÍÚ]Ö¥¨c˜nüMAAÁ¥^@Çÿ¼øÛñGã×ìñ«øÇâÞºþ!Öm|GycÄ‘C -´v¶’*m#S†‘ÎHÏ8ί™~%øÓö¼ñ—íÿ©~ÍŸ>1^Úøv8£Õ5INŸa2hVÒ¨–Hò3&Åx’-͸´Š®Ü3W?ÿáøÏ ~ÏŸðOˆßüGµàмI¨¼ÚnnÞÊÂ;x¾d••I-Ðû#þ ÓðCÄøe©üoøžˆÿ®N½ªK*âH­§-%´<¯eeãip„~ìP×´ÝvËྷeeâûÍUÓt–íôŠÙî’KHÄpѼFÜ™6ê# †;vðGãïì-«þÚß¶|Eã½cãþ¥ák=Q]6‹¦Ýyò,+4¤–Ž=»‘ôÎs_¡ŸðP¯ˆð®?cŸ‰šÄryw:–ý‘›T‘moºÇ+·Ðü¯ýž¿h²gì$—>ø#«\EsÖª9ðˆ “T¿»}ÎÒ4ŠÍfymfh¢p»ü¤ ÁÕ˜ŒŸoÿ‚—|Eø—ð{ö}~øÚëÂ:¶“on!‚ Yâ¿ލRO´E#)‰UJÜ0<úCöWÖ>!ø“ötø}âŠÚ‘Õ¼U®iúíËC»1½|jc#JDè„wžrkòëþ [ã„_ü/øF—>H×5[Rà€Í±,ã[xÙ•fÝ9I^q@Ÿð»á¿üâìûáߌzí5·‰üA¤&«o£^øM’ÕÅÂù°Dn ¾deNÿ(àž˜æ½—þ Éû^ø§ö±ø_­]|@µ·ƒÅ>¼ŠÖî[D1Ãuè^¼²NÇ;]\·+•£É<ÿøu•øUûè:ÿÄ/5t%,ôkØ ²Ù‚9¥ó¢Ï”`ª„b£&½#þ ëû6jß±ïìó­jߊZx‹[wÖuXQÖE±¶µ‡÷P3©*ΊÜ©+¹Ê‚Bî íãûRjëâm7ö5ø¡E⯉Þ3ò¦bDR9E#`ŒÊLÙf `;†æþý¸4?Ùï[øÿ¢~Ð7š§‹üíÆ± ]i"Äfk«eùNbRÊÛq€‘ÁÁ+tÝ[ã·Ç¿Œ?¶ÔI©^NllÔâ¾o:UBz ŽPõØÄ}~½ý¯|UkðÁ¿¯/•¡üHð¦¦-¸ù#×~Ìm6ã ó–TrO,ý,$=­)Ò¾«Þ_/‹ð×þÝ9ª¾IÆ]ôütùžïûþÑßðÔ¿4oŠ7–qéúÇ›5†©o|”½¶#y‹qfØèÉ"‚IPÛI8ÉðÛö×ñWÃ_è?³Gìç¤Câ‹þ)h‘Va¾ÛLIù¥\€Ò2fL3Ž1æI• þ `< ûê/טÁ§j:Þ­«‰ ¶¶†i{µʼþ O¢ß|pøéñ‹ö¿ñ¬fmRîé¬ìKüë šƒ4óªÓÈ…a…1Ò6+À¯4é=ƒãGÁÛçá·ÁMcâýŸí¨kÞ3Э…ýÖg¤[Gc". Ñ[…B]£б„yq±sÇßÿ²ž½ñ3Å_³·€üQñŠàÝx¿YÓRöúF·KV?if–ÐÆ¨¨Ë `r¯ ªµíå®g>¡{ †ÞÚ6–Wn" ÌÇØšü4øwñö®ñïüÄß³V•ñ§TŸÁ~–{ÛÛ¯°iŸi6¶ëh2-vnûDËm½2ØÏ÷¯ü/Æ^=øeû2x‡â‡Ã]x;YðĶ’ÆÖðÛN·Ÿi¸Ž×È‘n"“÷»M¤år+òCöø§ñvŠŸÿh¯üÕ>&?u)#k‹+ÛkE³k‹‰/f‰ŒùÜXI xP¾âºïø)wí7ñ“Ç_ü?ð‡ÆµO†ïâm^;˜e½¿·¼ûrX) $ ‰g…²O`9àô¯þ á©|pñ¯À]?âçÇ/]ø£Qñ˜7vs[ZÁ…¬2ËeLÆÎó€$bÌ@]€wWöØý« ¸º¼ð~…w})žæ{ W–FûÎí–cîO5ù…ñçHø÷ûjþÐ |S«xûádº“]xŸÂzÄ¢òKnä-%ͅ˃$q‡sòŽˆgóqOÓŸÿÈ“áÿûZè•¥ñ¿„tø3]ð/ˆ#ótÏØÜé÷Kë Ômãßk{ÐÃ_ˆžøµà- âO‚.æ‡âT»µ‘—kì~ªëü.Œ ºö`Gjî+ñïþÕã-fÿàwŒ¾ë3™á ×Ý-Ðõ†ÄÞÑö|ô•þ¬kö€ (¯$øÏã{ÿxH[xu|ïëó&›¤ÅÜÝ\|¢Cè±.]‰àÖ·ÃaåV¤iÃvgV¢„\ŸC‰Óÿâìüg›XoÞøcáýµ·t¹Ö¤_ßIèE²£=äúF¸Ÿ‡^°øwàÍ3Â:{y¢Ê?ÞÌ~ôó¹Ý,­žrîIç§NÕÛWNcˆŒæ£OàŽ‹Ó¿«woÔÏ MÆ7–ïWýylQEyç@QEç¿þ'øOàÇÃâŽ.ÛDðí³\Ü2 Ò7!R8×#/#²¢@,À5ù¹û›hÖìŠ! ‰]‹`²|€ð›!>×Å´÷üöî{5ÇÂ?ÙäLðÜ_§­¤lVHÁS󃂬Š|„;ƒ¼Ì¡kõOà_ìûð§örðd^øS¢Ç¥Ùü­q;~òîòUónf 4×AÂ*®{M|oÿÿ“1ø­ÿ`Ÿý¯}‘_ÿÁAÿäÌ~+Ø'ÿkÇ@ý†ü/ᨿe_„zÔzM¢jÃöOö‘o›sG‚Þf7d‚rs_;þÔ_·wįé~<Ñ¿f‡·þ"¸ø}öˆ¼Aâ]BÙ Òt©-¸™aILmu4}H(p%C_RþÃßòh_ÿì\±ÿÑb¬~ÚŠöHø¿ŒøgS?ù¨Ìàœ¼ñƒö[Ñü}ñ3Z›^×µ GSÝLX¬w ˆ¡cUUU+îêü×ÿ‚LÉ•xgþÂ:·þ•½~”PδGí!¢ü±Ñl—Ú·Œ¼Wâ¹&·ÐôM"Ùæšöh™7JÇ u.Í– –T`­Î/Ùcö«ý¨þ-þÞºÇÂÿ‘/„lt="ùßâ3´«ä4M,À»M&Éß¿o?*¨8¯Ú½ Är:ü9ø/ÿ)—ø«ÿ`™¿ô’€?q袊(¯†ÿlß|hñw†5}K@ø'Ã߇>ðö¥ªêo£ï\½¾´ŠIc‹í,V3ì`ìw)WåØëö—ø¥ûêÖþñ¿Ù|]¨ø†æ×LÖu¶–ùì´Ïôe¸1ÞÍ"´4!²»Èhä~Ȳ†Xd=ëñâíŸñ/àœ~þÍ^“Ävþº¿¹mkPà³Ñ­¢ ÞCymrÖÒ¹ÛµŠàïĉ]‡ìM üBðgíÉñ·áÅ·ÄÇžð–•g Õεz÷eõ«±m.ã–Ø®¥n£àv¶HÍ}=ÿð;ëß³x_ÃZÂOꚆ“iuxžÔöÏ-¼o(Ùi&Ü;ÜvçŠùþ 1ÿ&UáŸûêßúVõöÇÇù"Þ?ÿ±UÿÒI+€è>/ÿ‚OÉ“xKþ¿uoý-–¿H+óþ =ÿ&Má/úýÕ¿ô¶Zý  ‘¼Gûgü!ð'í ?ìëñ"i<+ªMmis¦jW¸M:ÿí+Ÿ-f8º°*7üŒFnŸ{ø¥ã‘ðÏᯉþ"6mdxoMºÔ~Çnq-À¶¤1¡Ã`°¸¯Ÿ³—Â_ÚSÁ¯à¿Šº2_»šÖîþÓ^_ü+ÖVóÊ /,&ÄWÖ27ð\A’W¸ #`ìfÁ¯qÔ,¢Ô¬.té™Ò;¨ž&hœÇ WR¤«® °‚9‘_•¿¿bí+â@ÑkŸØwÄ ðçǺŔÅ©M¶¬A{΢x â™YK†Cž%$¸ôOÙöÚñÅoê?³—Ç¿ ÏàÏŒ>…ÞîBlîãˆ!iS–ò˜««¹£u!ㆠ%øÏÿáñg‡µ­cÅ·ÄßÙãX¸ûÒj›nµoË!ýÑKœ–,6€cÚŽQŸõ¿IÕtíwK³Öô{„»°Ô!ŽâÞhÎRXePèê{†Rö¯œ¿miß¿e?Š~Ô¢ó@Ð/o`ÅÖŸ¼·#é,IŸl׆Á-<©øóö5ðšê÷êçÃsÞhÁÏÞÚɺÝýs…Ñû*(ôBŠ( Š( Š( Š( kÆ>ÑüsáGÂzô^mާDãø”žUÔöd`ObyŸÁkÛj? ¼k.ÿx=–Þyl´aþv¹ä‰úÜŠ÷ð«¢]iß<›Zðª²Þ[§[ý%Žn >¬Ÿë#ÏBâ½l¾J¬^oâÖ/´¿ÉìþO¡ÉˆN-U]7ôÿ¿ßÜ÷úüMøŠ¿ð¨à± ñޏ†×Iø•¥Gg ÇðIröo`‘çûÞt0©@u=Å~Íø^Ò¼Q¢Xø‹Dœ\Øj0¤ðȿĎ22;ЃÈ<käOÛgöR´ï€4é¼3¨ â‚®RðÞ¤ Žèmc Œ2Ê’˜Ðï^QÕd­åÎ.-ÆKTu&šº>үů€ò˜Ž_ö.?òÒ+ìÏÙƒö”ñçŽ5þ |~ð6­à¯Šš>žnnškmÚ^§»$RÝÚ]Eº ºî@v‚ØFnƒó‡ÃŸ~üÿ‚°ükñ—Å­v?h×Z7Ø£¸–)¥V¸’=.E¤’±¹Î1Ç^•#?v|UâÁ>Õ|câ›ÅÓômÖkÛˇ Ë ½º$r!T€ ôüÙÁAÿkÿ‚ÿÿhƒž$øS©MâM+áýÊÝ^ÜEm4+#½Ü˜¡YÖ7v ]¡I`<ãú4øsñÁ_ü§|Bøwª&³áí[Í6·h’F²ù¼2aeTq¶DeåGN8Á¯$ø‹©_üUñ[üð´ï—l©/‰ï¢81[·)cùk8ûÿÝLõù…v`°Ž´ìݒվ˿ù.®ÈƽnE}ßEÝžgðßâw‚?jo‹ÚŒ¶šÜhß Å­Å¾’êË3ÝÝ«¯.‘€ ´+£'p#sÎì+ûcx‹ö¸¶ñäúÿ†í¼?ÿ…íµ¬Mk3Ì“¬âRIg•òÇNÌ+êψ¾ñeÏÂ]kÁ5 _ k²iÏg¤ÝM=½›íر9Wîœ6ÖÃl>oû)~Ì> ý”~Ûü:𤯨]Ï)»Ôõ)T$··Ž¡YÊŒìE,i“µG%˜³3ÇbÕY%h­ì¿ÍîßqP£ÈµÕ½ÿ¯ÈüÕÿ‚¢~Øÿ¼Yð3Äÿ|â/íŸ^ÞÖîÖk…K6Ó®ƒÏæÍ$i*ñÂ3NqŽkÝÿàž¿¶/À|øaðßÄŸfñýŽœšiÒ¦¶¸W’[HÝ™£›Ë0²´h\~ó8à€x¯Ôª+ˆÜøÛþ ÿ&cñ[þÁ#ÿGÇYðNù2o…Ÿõãuÿ¥·áŸðPÚ¿ö|ÿ…ñ[àOü&ÿÂwö1gý—ö{Ÿ3ídRló<¯+;yÎü{×¢þÃþ6ÐþÿÁ<¼ãß™—JÐ4{ëËŸ³ÂóËåEypͲ8Áf?Aîp2@ø*·Ž4ïþÅþ-Ón¥òîüUs§iVkýù Ê\È?ïÄšúö4ð.£ðÛöXøcàíbÜÙßÙè–ÒÜÂà ÷@ÜHŒ;2´„7¾kàø>üUÿ‚‡|lðçÄŸŠÞÔ|ðÀ² ­#FÕPA¨k—™ çO%"p$å|±²&&Guý‰ Š( Š( ¼âÇ‹õÍGTµø?ðò/ÄšÜfK»µäiZvq%Ãc¤÷b'9g°ø£ñ‡~[¸-Σ­jR M.Á?Ö]ÝÉ ”uvì=ȇsø'KºÕX£ôŠò éÔàgÖÁÓ(}j¢¿ò®ï»ò_‹Ó½¹+IÎ^Ê?7Ù›þºƒ|!¡øÃV>ðìE„{YØòÎç»»e˜÷&ºz(¯2¥INNrwlêŒRVAET (¢Š(¢Š(¢ŠøÛâWìû(|Uñ]ߎ|Mà•·×µ-uu¦Þ]éÆáŸï™RÚXãf~®û71ä±5Ø|ýŽ¿fÿÙçW“Ä_ <‹«ÍÛ½ãÜ\Ýܤ*Ìîe¨%FBã¥}3EsÞ,ð—†KÜ`{«eO¥}ñEehz‰áÏÃÞ°ƒKÒôøÖk[X–!#UQØŠñ¿Ž³/ÁOÚ>ßK´øÍ Kâ }¤{XF¡}g<¡C1KIáWl(œ1Q¤sï4Pžü/øYàoƒ> ³ø}ðæÂM3@ÓË{i.®o aÎJ¬—RK&Ðz.ì(à8¬OŒß¾~Ð>‡ÁŸô™u½ÖäZ¥íå’4¨RæÒhKœ…rTd=vŠø_Qý‘þüøo¬êß³ï‡dÐ5-.æ×]HŽ¡}y“i¥˜á.ç˜)xšEm€ùCgj¹âØëöSý¢µË?Ž^2ð¬úÖ·­[Ú]E{ý³ªÀѢƦ[ÝÇ[F«†Ë}âIû)Ñ$FŽE ¬ Œ‚PE|÷ðßÃx§àýÓÞ¿f² r[L¿ÌöÇ'®Ü²ŸL^¬?{„që“ÑýÎß{9»Y>’ü×ü ýÇ¿YÚCai°"tXÐ33¶Ôf%˜àu$“Ü×ÍcÙÏö‡ñ¯Š¾1øZ_jVP hµMJÚ8¢ [ µÌQIù˜&æã$àcéÊ+Ê:ÎáÏÃ?|&ðM‡Ã¯Ù˧xKWKky.®nš$‘‹•\É,¸ŽÐ_ 0>gÐ?àž?²†CC íJ(R§¡ãƒƒùŠøŽÇþ Ïûé¾&Æ–^ º‹^Š´­ÿöþ¶n|ìä¹ß,{’NyÎkíÚ(¢Š(¢Š(¢Š(Äz™â½RðÆ´²>Ÿ«[Ëkp°Í-´ ÊQÂÍ$±’¤á‘•‡PAæ¾?ðK¯ØU‰føg’zŸí­gÿ“kïê(㟆¿°ìð‡Æú_ć¾]/Ä:+¼–—-©jW"'’6‰•qu$Lv¹rF^ø­û þË_üiwñ ⟃e×µûÕ$¸“WÕ"P‘ DTŠ´Š5tDPNXÄ“õÅñ„ÿàž±‚õu]án5Ä ½>ß5Ö¤›½Lw³Lø©¯²­­­¬í¢³³‰ ‚XãŽ5 ˆŠ0ªª8€JšŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+毃ð‡âE·ÅkQåøkÄŸâr ³¶ÖøúO—!ô à“_JÖF½¡é~&Ño¼=­@.l5^ £oâG?CÜ ò+·ŠT§ï«Åè×uþku摆"“œtÝj½M`CAÈ=ékÀ> 뚦6¥ðcÅÓ™µ¯ û$ï÷¯´§8·œz²ÝÉŒà’I5ïõÌ+£QÁ»ö}ÓÙüÑTj©Ç˜(¢Šå5 (¢€ (¢€ (¢€ (¢€>{ý ¤“^Ó¼9ðªÕˆ›Æúœ6ó…8a§Ú‘qvàû*¨ú5}qÃÅ„DU@Àpó߇¿âµý ¼Câóéþ±‹H¶=TÞÝþþå×ý¤@‘·Ö¾‡¯W0÷)Ò¡Ùs?Ykÿ¤òœ˜zR©ço’ÿƒp¢Š+Ê:Š( Š( Š( Š( Š( Š( ¾ý¨ÿb}öƒñ‡†~-xOÅw>$øD¢Økvp-È1ÆæDI f~ÆfÚDƒ†e`ê@pQ@”?¿`?Ú?öг²Ð?hÚ:]_ÃÖ2 F¦x~ÞÂ9d $ò§EwTÈŽ“´ œþ¢h›á¿i¾Ób §évÙC ˆÑHé÷@µE~3üEý†|Qû0ø¶ûãì³ñ:iW÷Ñ¥ö›s¦Á©Ûiðß?•,Ðù¬Çe FX ~óåõÀ¿Ø;Âß>%Íñïâ׋/¾,üO¸;ãÖ58Ö-IAµ´VuŒ…;T—`ƒ5JûcÅž±ñ†u_ jc6º­´¶Òd¨‘JîêNG¸¯7øâ;ýwáͦ›­ŸøxfYtm@’.,O—’{–Mžù¯IÓS©­âìý«ñ½ýQÌ¥ËW—£_ŠßôûNñ%¦¹áýJÇÃŒzF¯qo,v—“[ý®;iÙHŽVƒ|~hFÁ)½wcð¯ì“ûxÿöbø…ã?j¿ׯÐxúOµëòèke4÷ÊòÈ—à^LW 4¥&wl ý¢¼Ó¤á4†žð~«âø+@ÓôMoÅ2yú•í½ª$·—±Y.v´¤3±å»žFs_~ÿ‚z|Vðí:?j}KãŒZω®îš]B7ðÊÅͬª"–Ù?â`Â!älŠaN?©ôPÏÿ´ÏÂüvøQª|-ðo—À©¯+[j7ŸÙÃQ’k Q’ktS=¿—æd|“·*Ý‘É~Ëÿ³f©ð àøã?ÛøÿÃð ¨-•ô¥°U³½g’{y“ϸy\åˆù[i ú¶Šñ| žÓàåïÂ_€š—ÂÁ,O¥Íž˜—0Ù$îZv†Õe|ÇÜÄ>ìÅ|ÁûþÞ'ýµCĸüYáï"<ÖD2-ÜÑ$ÿl€àFû6arÇ$·Süð‰þü,ðïÃÏø’?^øzÖ;Ô#±p’ÞÝDp+@²Ì$jªÍ¿æ#vq^¯E|ûjþÇ~9ý°,´Ÿ §Åðw„4ÇK§ÓSEÒ\_§š‚y.>ÙÚ#“jÄå‰$¿F~Ïß ý¼y{¡Ý=œ÷ÛZ´‘±Vò§Er7) Ð}¬þÝ?²&©O¤jŸôhî­˜¤Š³4€0àȬ§ð5™ÿýè«i÷ÔŸüEõé~ ø3ñoâN“©k¿ü«øNÑÆo.,,¦¹Š ØvHã®9Æ(úíÿ‡~Æ¿ôU´ûêOþ"øxìkÿE[Hÿ¾¤ÿâ+øÈedbŽ ²œx ŠôŸ |øµãßê~.ðOƒu}{DÑCÛÛ)® ·Ú»›|‘©Qµ~b3ÀäñÍ]ßðð/Ø×þж‘ÿ}IÿÄQÿýè«i÷ÔŸüEõéžø1ñsÅþÔ<}á_jú¿†ôÂïRµ±škHJ ϺTR¿ ·?( œ@×gü< ö5ÿ¢­¤ßRñÃÀ¿c_ú*ÚGýõ'ÿ_Æ=ýœÃÀ¿c_ú*ÚGýõ'ÿGü< ö5ÿ¢­¤ßRñücÑ@ÙÇü< ö5ÿ¢­¤ßRñ¡¥~Ý¿²µ¨C¦iß4g¹¸`ˆ­+F ƒs¨Qøšþ-è ïÞ àº‚;›iXfPèèC++ ‚à‚9¥¯Îßø%‰uÏ~Åþ}vòKÙ4ëFÊ•‹ºÛÃrþT{$"ª;( 8Wè•QEQEQEQEQEQEQEQEQEQEQE!ê(<ŠQM­cx¶èwäôòÿè¿#þ\¯ü-!<v9Ïã_­)?ñNêxÿŸYÿô_¿TÏñ#ç{cõùw·õšQó>ç…#ûºÏÈý6Ä7ÃÉ‘\…U_—ð¯JÑ5¨µÀ'ÜWžjÚ¶±Ã<+”عü«"Îî[)¼ø>\‘^–0­†šUv8ªá)Ö…á¹íZ¦›¡Vwc^?ªi“iÓ22üzv‹­Å¨Äª[+CTÓ!Ô (ëóz×¹ÀSÅÓö”÷<Ì&&xyòKcÈ´}V]:pr|²zW°éú„Wð‰†kÆõ]*m>bŒ2„ñOÒµyôÙC|¼ò+ÅËs*˜zžÊ®Ç£ŒÁƼyàzV½¡Ç¨DdŒbA^M=¼Ö²˜fH8¯nÓuoáW‰&²µí -B6‘$׫še‘­VÃÇJ“öu68Í^’ÆAÍ”÷¯T†â+¨C¡Ü x5Í´¶Ò˜¦R ÷®—A×ä±q ͘Íqåy´©¿eWc«€S^Ò™Ökú ^BÒÂ0¼¶x%µÇ"íÇïOÌbD9 +Ÿ×4o .ƒ9®Ì×(Uí)îràs)S÷&yö¬Í¦Ê9Œõë×VÚ¥¾åÃ:WˆÝÛ½œÆ)ÔàV¦‘¬K¥Ì§9ˆö¯'+Ì¥EûÛØÌ ¨½¤77ÖtYtÙ˪å dZÝIm(š´­xxleL%NIìzøŒ=§~â­:cµ-z—8¾qð×ü\ßz§ŒŸ÷š€DšF›Ý$ÔeÞÌ=ã\D;¢»ŸŒ¾6»ð?®nôeóµÝNHôí*÷¤¾º;"À=vòçÙMn|6ðM§Ã¿i>µo5¬bt½æ¸s¾i <üîIç·ëaÿs‡•oµ/uz}§÷Z?7Øä©ïÔPèµ§ùü‘ÜÑEäaEPEPE‹âOi~,ðþ¥á}m$“Nխ嵸Xg–ÚFŠe(áfÒXÉá‘Õ‡PA ÊïØûQ²ø½û~þÓá™n ðà±ð½“© †cþë6ž#ƒ¿#­|ýÿfð÷‰> üdø]û\øýP‹~“u*‚Ï·$Èä™ ’xÉë¶0+õ3à×ìgû7þϾ&ŸÆ<)'‡õ[˜ZÞY©©\¤‘±Î›™bbBS+ÎɯMøÃðWáÇÏ7€~-苯èMq×ÙÚiíÈžìu’ÞH¤Rà ‚AÈ$PÌßðMo‡ÿð¯c?‡ÖÓGåÝë°M­LqÿÚ4°·þ˜‡á_ ÿÁM'ã7íkû?~̶Î'…ÔcS±êwqÄÅÀïÒ>:ílô"¿o<= hþÐ4Ï xzØYéZ=¬6v)fX­íÐGı ŠI'ŽM|ã?ø'oì{ñÅš§Ž|iàYu]wZ¸’êîê]kW$Ò6â@[ÀªEUTaT©ûMxûà×Äš§¾6YiÚž¢m¼µ±¿Ž)Íü5´vÑÊ̘Àe9bB‚Eه⯉¾8| ðŸÅŸhðè:‡‰ ’ëìp;Ip``Î>dJ¯Óø±_??üóöæO†¥¾µ¬Ÿý½¯·ü-á Á^Ò|á{E°Ñô+H,l­Õ™„6Öш¢@ÎK¨ e‰'©$Ðà·üâïÁOÛGá7Æ2Ô]ßhž‘íc?u®"žìC»ÕD’©aÔŒãšý ý–þjd ZÛÅåñ·‹¬5ÄSÍÌï¨_ÀÎcžwB›Q¹Æðì>ñ¯xø«û-|ø×ãÏ üHø“ _[ðs#iÎ×Gys ÔI 8ŽUÞ!Ô‚8<+ÝõM:ÓXÓ.ô‹ô/k} J •&9T« ŽFAê(ùmÿ‚}ø'^ý§®<-ûx]¹ñ¯‰Iæ+ë‰b†ÚÆÑÇL7’ã)êŠkúŸ( £p¯ ø û6üýšt GÃôOì{MZä]]ž[™e‘P"î–fwÚ |« – e‰>ë@‹?ðYß\KðëáÇÁM¼ÝSÅÚá¼òüïœ~Djðyn—êSކ¿M5á/€xOâ“ißðø[D¶°¼]N8ä³’ÖÆŒ+Å e“;ÔÚK6àWœüVý…e¯þ4»ø…ñOÁ²ëÚýêÆ’\I«ê‘(H"*E ÚE€:"(',FâIó·ÿ‚^þÃcÌøj[3­k'ÿohÐÿbïÚÿö”øMyñþ˜|+¡G«]éÚ%´,X¾›f"‘Æ£n,…Så8¯Î‹÷öŸ¿à°Ÿü²¬ÚwÃ{kwêÆ µlVó^$oB¸= ~Ì|4ø[àOƒÞÓ¾ü7ÒÆ‹á½%e[kTši|±<4‡Í™ÞV-#³eœœž½+æ}þ Ýû øwÅ–þ;Ñ|smâ[µ¥ð×µ£?ŸÅÙÚô–,IÝ»!!²  µëÎþ/ø{QñwÂoxOHÏÛõ­R²·Æó®m¤‰1ž3¹‡ZôJ(ñþµ®èß¼wðöyÛÅz_‰f»¼²“ä¸[ymm¡Œg Ë ¨xùXs‚@¯?ÿ‚š|`“öŽñ7‡?c?€ãÅÚôÇSÖ¦²"XíZÖ9[´‹•ýØf’~~B¨œ¾å£¿`ÙOãŠ.¼mãG½|w\Þi÷WZ{ÜrÆT¶–8ÝŸø”»wjõŸƒ_³§Á?Ù÷LŸJø?á+O¥Ðy£ß-ÔáyQ-ÌÌó8’9'UBVwW<ÃÀß4H¿b8>|---´þ ¹Ól›I%ÔörFìàp$k†bøãy=«â/ø"ο¡Gð+ÆÞy–éž'žîîÍÎÛ„‚{KXcr‡ ™ ˆxá”ç“_¡Ÿ ÿâø‹â¿„2þîÂf:öŠ; [¶ÅÌ+اÎ\6kø•û~Ê|WwãŸx%mõíA‹]]i·—zq¸gûæT¶–8ÙŸ«¾ÍÌy,Mvf8xÒªÔ>ªôz¯ò~f8jŽP÷·Ùú¢;Û3Â:‡íg¡~Ê~Ó£ñ5ìö·—:Þ«ê¬C[Dò,5ŽO:RÈEß–]A,Û•uÿnψÑ|-ý’~&x”Î ¹¹ÒfÓ-NpÆãSÅšõeówñÐ)=­ß‚¿±×ìßû<êòx‹á'ƒ!Ñuy {w¼{‹›»ƒ…Y=̲¨È\t®‡ã‡ìÏðWö¶Òì¾3h2ø‚ÛEi^Ö¨_YÄ0PÌÉi<*í… Td)›<&çÊ¿ðJAàÿØÇÃZš'ñ]ö£«MïÍ¢dúùVéôé_$~ÙWöà§> –{ 5•ÝäDŒ ÞVÔg‰‡möÖðûßJý{ø3ðágìûá©üðH“DÑ®'k–¶{Û»Õ°Š¹¦(2URrq’Mx4¿ðNÿÙ·¦ðMËxî¾Úoλ­} ÜÝæoûnsŸÃ·Jû^¿bTÁßðR¿ÚcÁ7l"ŸV{ýR%~–MA.Üò~K ~œ×íµ~DþÛ_½øcªéÚçà júEÌw–Wz]œ‘MGF…H*à Šìnîíl-&¾½•`·¶F’IáQe˜“Ð2M|×ðgö9ýœÿgÏK⯄>“@Ô§·’ÕßûOQºŒÃ++°òn®e‹$¢üÛw `Ÿ˜¿o/ÚŠÖ? Þ~Êÿ¤OüZø†D6 _L¶¹R—Ü2DþY!C2”ÊØTù€<+þÃc6£á/ŒÝ Çâ[Ä;.ëx¤€í‘ö¡ŸÂ¿k«æïÙ+à ŸìÑðÃ? ã’+BÆ7¸Ô®beÅýËy“¸$UI¡ ˆ¹¾‘ ¾nð/ü]OŠZ—Å9¿y x`ͤh ò’Íœ^Þ/ûÄyH ÷Ðürñ>«i¢ØøÂRlñ7f:}£¶ðc7WGâ²r9‚:W§øOÃWƒ<5¦øWDʱÒàH#ÈQË7«1Ë1îI5ëÒýÆÔûSº^QêþþqÏ÷•9zGWëÓîßî:(¢¼ƒ°(¢Š(¢ŠüMÿ‚Å[çà7Ä·[øs^»Iò£Í6·=ºZ·ë_µÑKñ$ð8’92²œ«)Ô_1þØ¿³Í¿í=ð Ä? H­µi|»Í*æ`vA¨[Ñ Ô´N@$#±šùçöý©¬µ¿ YþÍ?$O |`ø~©¢O¦ßÈ"—QŠÑC=¹c‰£xFbØóW(à€Ò)eŠžiœG`³3*¨ä’O üÿ‚ZøŸÃú—ígûF›-B¿¶ïg¼²Û"Ÿ´[®£rÆHð~eD9`kõ³ãoìÉðOö‹M./Œ~“^F-ª.¡}dˆ.67-œð‰3å®7†Æ8ÆN~~‡þ û ÛʳÛü6h¤C•eÖõ•`}A¹ôgÇßÚá¿ìÕàˆ~ üR¸¸¶Ñ罊ÁZÖ¸>d’E6ÆÜý=kÔ¼1â-/ÅþÒ|[¡ÈeÓu»H/­†ÒÐ\ƲÆH= VùûmþÎ~?øÑáÿ…?²/ÂK¥øÆâ-CQñÌí=¾g¦ÆÖ±[n•Þy§)1erä/Ì@•“ôÃÂÞÓ|á#ÂZ2”ÓôK;{eb ÛF±F …QÚ€>,ý¬a_þÑZ…¿Ä¯ ê·ø«£ª6Ÿâ t,ЪK•B¤…è²!YŽYFÃó?‚?kÿü9Ôfý•ÿा‹O:ͼ–þ'1‡Ñµ›Y–ßid`ØiP(\5" ±ýˆ¯8ø©ðáÏÆÏ]øↅo¯h×|˜§_š7ÁHd\þÄšÕÇÆOØsQ“Æÿ õ/5?ÜÊ×[¡`Ͳ`I›äÆÇO߀=r+ï_ÙöÅøEûThO7ƒ®›Kñ5‚ÄË@½!/í¬BñæÄ" r„c´} à?é¼á߇Þ2/Ãu¦—hf`òýžÊ‚-ì Ûdàdöæñ~Î?¬þ6EûAé:"iž5û-ÅÍÕ¦"KØî6å®ckÈ»F$áû1`ç_ÿÁAÿäÌ~+Ø'ÿkÇ_dWÆÿðPù3Šßö ÿÚñÐAûÉ¡|#ÿ±rÇÿEНûtêúf‹û!|XŸUºŽÖ;öÚ3#5Äf8£\‘–w`“^gû üsø-{û<ü$øggãÍ .…inttÔ­›PEi#û07zª’Ë· OJô?Š_°Ÿì±ñ«Æ7þ>øàÉ5½sR1´ó6­ª@„ÅÄ»a‚î8“Š>DÆNI$€xü7UÓ¯¿c]ÂÒæ9®tíSSŽâ5`^'yÌŠT”`Ã=A¯«~>þÔ¿ÿf©|3ÅKË«CâÙå·±6öÏp¥àòÃï+÷óW“ïé^_á/ø'?ìqàOiÞ,𗀤Óu]*â«y£Öus¶hI*×…Pv°*zGâ?µ—ìóñ'öµý¨~ø:ÿC“Fø[ðÕµµjfMºœ·¯5¥¢İá°7;0Ç”$ýI¯Áþ,ðÕ×üCâMݾ¥o$:…廬ªV[˜m­âBÔà sòŸJýŽøÅð7á‡Çß [øC⾓&³¤ZÜ­ÜpÇyweûåGŒösBì6ÈÃk1^sŒ€GËkÿºý…Uƒ/Ã<È#ZÖrþÐÛÞ,ñ6•௠ë>2×YÓMÐl®/îš5.â XÚY ¨åˆU8¯JóO€Ÿþ~Ò^ˆŸ ®gºÑã»–Åšâ·O £0(Üã¤zñ/Žÿµ„?²?ˆ~~ÌÞ »Ön5«yôK ¼’àÚiž)®¥žög“Ë‹Íg9v q±†eê¿bÏÙÅ¿e¿€Z7à 봿ÖZIu RhsäµíÎ7¬Y”U#V Û»8_ðP rãÃß±§Å{ûRCË£µ¡Ç÷/eŽÙÿñÙ ~~þεG…~~ÄŸ ¾|"XüsñŸÅPÞ/@°"y"¼¼½¸“Ï¿Øq P«Uʳ*ÿ n‘?e|à/ |Pð^±ðûÇ6#RÐuëwµ¼·fdßú:e`@*ÊAEyGÁÙGö~ý>Ó7Â[h··‹²k×yn¯ O´\¼’*(¬ f€0¿dÙéÿg…í¤ø‚ôk>6ñ5ܺ׉µO¼o5[³ºR¬@&8þêp3Ëà"©þÝ:¾™¢þÈ_'Õn£µŽã@½¶ŒÈÁÍqŽ(×$eØ$×Öò7Å/ØOöXøÕãÿ|NðdšÞ¹©Úy›VÕ Bbb]°ÁwI„E" ã'$’@>1ÿ‚<üG¶¹ø~^Îsguy}h|·›lÑ0ìÈÛdòVLô¯ÓŽòE¼ÿbþ«ÿ¤’WÞ8ÿ‚{þÍ ´û‹| &Ÿâ_[kUÕ%ûLr %ˆ‰nœ}ѸqÎݧ ‘^óñ×öŽø gð—Åz>«ñAÓ5]kÃ7“ZX^jVÖ÷’­å›˜1’ 2ùF2OšïÄÒ½8ÖŠ²Ùú¤¾ëï÷˜R¤àÞ»üŸù3ÿžÿ“&ð—ý~êßú[-~Wæÿü{þL›Â_õû«élµúA\çÈ¿µ'íð—öYÒc‡Äs6¹âýEGög‡¬{ë§sµ Ÿ&"Üoa“ÈEvká~Ë¿l]jŽ?·¾ªÞð—ºóMðM¼ÆÒ( PXÉ|凒6¬fo<Œ‚Ш _¥ÐþÎ?—ã^¡ûB^艨øâö {x¯.±(³ŽÞ?,}•Û0ûÏËò@`¤­wÿ¼¡üPðˆ>ø”̺O‰lgÓîÌ _&á >Æ!€lƒƒŠü©ñ·í{ã¤³üƒAŽ[M´û¿4~V‘¤ZD¢$[BÊP*"íI ±!O‘ü®>¥ý“a¯þÍSÝxïXÔn»â{˜ŸÄš•´žeŽ“ci*ÊÉ4ñåsæ 2à¡|¬4’¨ ~è_þøgᆗw†l ²±´ÊbPVøå}Îßí1 C¢Š(¢Š(¢Š(¢Š(¤ ƒÈ4´PÍ>'àçÄ™¾\~ï¾+yot?rÚëï\Øç Ÿ2!Àä–&¾–¯;ø£à>#xFãBóž¡ %ÖŸv¼=­ìt2©Œ9ÚHª¼}qã¿ 1Ö¡~"Ñ¥k ^סŠòýÉ>ú‘ƒ€N zøÏßÒX•ñ-%úKç³óWêqÑýܽ—M×ê¾]<½S¢Šä¼sãMá÷…ï|W¯ÈRÖÍxEæIdn(ÇwvÀñ8šó)R”ä¡vö:¥%äö8¿‹5 ZXøWÁ±­çŒ¼Jío¦ÀyX€¼º—®"…~c‘ÉÀÆ3Ž‹á¯Ãý;ádž"Ьäk»©]®/¯$æ[»¹y–i É%L“€ÏÅü$ð^¶·Wßþ Æ‹¼FŠ<ž«¦Øƒº+8óн!ãsõädûz8Ú±§ªÒwKâ}ßù.õ}U¹¨EÉûY|—eþoþQEåaEPEPEPEPXÞ!ñ‘á]÷ÄzõÊÚiú|M,Ò·EUôîI<9$€95³_/ þÐ^6Ü~‡>¹ãþyë:œGòkhüÛûÃî÷àpŠ£r¨íêßè¼ÞËïÙ3 õœRQÝíývFÏÂïêþ7ñ |oñÕ³[ÜÝFÑh:|¿óÓßþZ0è'¸3ž¡NÜàà}Ež3ëO™«%¢]è¿­Þ¥Q¢¡}þl(¢Šä5 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€>sý¤¿iï†ß³„m¼Gã—ž÷PÕ¥6ºN‘b‚[ýFçÝÃGÊ Üð¹–eVù럾1oBý >-ü>_‡K®Iq ÿe½çÚ®>Å,B}>[¼"åûBȬ ©T* ©ëóìØ°~ØðPŸ‰ßùzO‡-ç%¢†äJñCpˆN2|›‰ùW‘‚«ÕO޾O|"ñ_‡zO-„ÒÛ6pRæóa`{|ê3íšïËkÆW?ÂÓðßÕøšnP÷wZ¯‘ùÙû/þÖŸ¶¿ícà»ÿ|:ð§€l4½7R}.eÔîõH§ó£Š)™•aI”®É—°9Ž„þ£ø¿Æ^øáÛÏøßWµÐ´m=7Ïwy*à c¶YÈ'€:“Àñ_“ŸðE/ù6_Øßuÿ¦ûûÏâÇì¡ðã—Ä=Çß ¼ñ>¶0Zè—7Nt_4»7Ú^Ìa^l6ÂXíe2£AÆþζ×ÂÏÚâ‹üð®ÖöãOðŒLÚ¥Äbnü÷dLL|УnA‘TŸîŽÿc×á?ü¯IÓtÚö•дkd³ÓôëómmcjE :…Ú"(쪠=~ìP_ üeÿ‚†|øKñFøOczþ0ñv­¨ÛiÒÚiL’Eb÷¬Y¹¸$F¬¥¹KHÃ*õ¯ª~+ü8Óþ.|>Ö~꺮¥¢Ùëq¬S]i7&Òõ]]–9@l ±Á23)5ø3ûu|øCðãçìÉá¶ðýµÆ§Ü´[šk£ýžÇžW,ò¸Þß3yÇLýjGPuÑÒV&¾?ÙÄĈŒ»NÀåA`»±¸œt¯É¯ÚöÀý³?eÏøD®¾(øSÀ—vž,Ô>ÁöMÖ§<ˆË´¹q:BÃq‚yí_®Uø³ÿÿ/Á_ûæÿÐ" ¿¿iŽ>6™âýá¥Ï<3ɯ\iw ý§§ ¬ÑÚ8ýôJ‹á†:³*‚O¬|øÇðÿãÏÃý7âgÃMHjZ.¦¤FÙ`•8’ ãÉ)*O±©úyV‚ ~*ü&ò¿dø)ž¿ð+FˆØøã-‚êºmŒdˆ-o I"²&p«æÁs ª…hÇEûWEPEPEPEPEPEPEPEPEPEPEP„|kðÖ¯ZwÅ_Ãæø“ÁÌÓyKÁ½°oøùµluÊe“®p2kÖ|/â]#Æ>ÓüQ ÍçØjp¬Ñ7|7Pó)ÈaØ‚+z¾jð¿üYÏŠSøoÝøSÆ’K{£‰k¨}ë›1ÙVOõ‘r Mzô¿Ú({?· µçÚù|KË›ÈãŸîêst–þ½Ïo¸úVŠ(¯ ì (¢€ (¢€ (¢€ Äñ.½eáoê~%ÔŽ-t»indõ+ {œ`{ÖÝ|÷ûAI&½§øoáU«7µ8­ç pÃO´"âí‡ÑUGÑ«³/Ã*µ£ m×ÑjßÉ\ÇQÂKס±û>è7ºGÃ;SXñ6ñ4³kWÇ¡3ß·š2;E#Ú½²™qÅÅ„DU@ÀpúŒf%Ö«*¯«¹Ti¨AAt (¢¹Š( Š( Š( Š( Š( Š( Š( Š)®é4’0TPI$à:’hÏþ&üXøqðgÂÓø×âˆ-|9£@B™îŸÜò$P^W "+1ÁÀ¯Ž|/ûK|1ÐîõÏÚK’ôü8ñ¶{¬ÆòZI ïwáýñܘ`—i"hH¤ãÇ8ùSàoÿ ý«|cñÛâF5…¿ o™áM"Tas3–"âXÛ*çb,ÒsI“²=§ìÿø(ƒ_ý’HM­ž„WÜùïÿ±ÿ“øwÿ]5Ÿý:Ý×§~Ñ>>ülñŸá |G_‡ß ¥³?Úï¥DÃÄ—%Ø"¸oÝÃo̸|î ®­ç¾h?þø§ÆÏ€<5â;SÄ>D}JÊÚe–[A!*¢`¹ăò“¸wßWà‡ü3ÂÖÞ ý ¿h?ÚN÷Pø~hôøæ—d‰k{uwǘ 'ë÷¾€3õm_IÐtéõrö :ÂÕKÍqs"à h:³»ª=ɯ ý³¿dÛ`hpü]ðÃ]þÔ·“œ`L[Ê'Ø5q_¶gì‹íy xCÂ÷Þ%oi¾Õ†¡x‰ngk¸Jlx”‰#±Rv¹ ‚~íxíéðö`ðìiâ÷h¾mÊ4Ñ'µµ†Þí5ê–ËÀ \»­˜ºofÎ  Ñ/ø¾ÓÁÞÔ|fֺݶ¸û6•n×·— Ƽ1ó#äÖ¾>Òà ßo¾$øcáFµá_ø_Ä/¹Š×N‹\ÐäÓ–F™üµß8m¸,â°¿à— ã…ýŒ|ÐÍþÍ3±ƒ;ù(Não+fß—óíÃÿ)ý—?ëâ×ÿNúWñ_ö™ø3ð;Å^ðŸÅ}wþ¹|P’µ•åÔ2-†èYAIn‚˜¢c»#y’G÷ ûRÆßSÓ.#¼³»e†h\I±¸Ü®Ž¤†VAÁ+œñ×€¼ñ7Â×þ ñö‘m®hšœmö·Q‰Ü3ʺç*ë†SÊFkò£ö.ñ‰fÏÚ‡Æ_°'Œµ‰uO ÚÄú§ƒ..ÿ× wArÖÊßÄ Nì@DÊT Ø ØZ(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÑýü¢Š(¢Š(¯Ïoø*Gü™o¿ë¥‡þ”¥~„×™|bøQá_Ž uß…Þ3Gm+^€Ã#DBÉuI@d`ÈÇü(Wê?ì{ÿ+Ô?e_„z‡ÂÇð<>"V¸žîÊè]}˜Ç,àdN¾[ùŠÈÁSŽ3éïz×üâtzÂxwâNq§†>KÝ[\C1NÛÒ1"ƒŽ¸cYðä?ôP¼?ÿ~îÿøÝ~7xŸ^¹ñW‰5_^FÏ«]Mw"D6¢¼î\…€'Šý7ý“?à§Z·ì½ðF_„1ø ~KYîn4ûϵprÛȸŒFÆP¬x!”íùsÀ¯Zÿ‡#üiÿ¢…áÿû÷wÿÆèÿ‡#üiÿ¢…áÿû÷wÿÆèñS¿—UÔ®õK…D–òi&uB i± £€<Õú¡û;ÁPõ€¿³·ü(Õð%¶±scÔVÿiòbUºfsöˆ<¶ó ³žŽ»‡^äú‡ü9ãOý/ÿß»¿þ7Gü9ãOý/ÿß»¿þ7@Š÷5ÍÄ·iYœÀËñP×íwü9ãOý/ÿß»¿þ7Gü9ãOý/ÿß»¿þ7@Š4Wíwü9ãOý/ÿß»¿þ7Gü9ãOý/ÿß»¿þ7@Š4Wíwü9ãOý/ÿß»¿þ7Z:Oü✚„ ®üHÑ­ìK5í­®f”/}¨â5'ê€?C¿à’_òeÞÿ°ž«ÿ¥-_¥õäß~ øCöø[¡|&ð2H4­ &Q$Ä4ÓË#%šB07É#3p õš(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠCÚJu€çü ÿ’³ç_°>,?ñNjõë?þ€kñÿà_ü”Xÿßο0ãm1T_™÷œ$¿s[ÐýŽŽÙ.¬"Žac_å^a®h¯a!’,”ÏcqŒ+í±˜xŠ+¹ò¸\\¨Mßcê[;¹¬¥ó¢È¯YÐõÈõT9ÃȯÊO†¿¶‡ˆü{ þ£«x>M& HšA½w ã,}+Óÿg?Ú­>2ê·vvaû#x#8ǹ¯!ðìwÑ™áW§™e±©ûê'éKÙÔØä¼?¯Éfâ ÛåÍz¬S%ÄAÔäùÏ_»“@Óîo§yµBøõÅ|Éð'öÜÿ…‡ñ‡Ï¥˜&p% ãäÇ¿½a“f‹tê½ Ìð±“S¦~ƒëzWñ–pã½y=Õ¬¶’˜%9¯y‚dž1$g úW?®èPßÄdEÊîÍòˆÖýí3,»2tåÉ=7Òµ‰ô¹Ý˜Ïjõ‹+ÛmRߌ6Exµå´Ö’fåW´½Rm2eÚvz×…–æn”½•o‡ò=f V´†çAâ/´%®­G¹Åq`º0eù]M{eí®§mòÙ×âKy ͸žµÑ™åvýõ-™žÓöUMŸ ëþ`[{—äœ ínm¡»£nVWæ'ÇÚº?:å¶–t϶<Í÷€'ϸô¯¸>|FOxvËV|£u¾ßLŒ×¡–æ³Äxü-§ÏL—^Ðe±Ëæ3XV—RÙJ²ÆÄb½Îâ¯!1¿Ì¬+ȵ½m6c" ÆkÎÌòÙÑ—¶¥±Õ—ã•Eìꇣkqj0€ìúUè {šzW™Ú]Ki*ÜFqŽÕêú» üb)ï=+¯ §Š‡°©÷œø¬,ðòö”ö<Žhd…öH¥Jñ]O‡üC%«ˆ.å5Ôø‹A[ÈÌ–ëóöÅylðÉùS ®+É­F¦¥âô=*5!‰§in{ÔOÜ92µy÷ˆ¼9µÕ¨ã¸ª×ä´qopÙ\÷¯OŽH®áÜ>ejú(:xÚVêx®50³ÓcÁupë”e=+Ñ|9âO3ýäàŽ•WÄ^*ZîÔcØW ¬Èãø]kç“©¬¢Þ‡µ%OOMÏv¹¶†ö®kÊ5­k YâC^ñïö¡OÙÿÀ²x–îÈߘÈ@Ïcî=+Ñ?gÏŒöß´añI±6‹:ƒ´\{ŸZúJøjxÊ\éjx”«Ï>W±l¬=+»ðÿˆÝH´ºn;Î×t ,XËn¹ˆöô®[%NïʾRjá*rì{ó…W±ìb)Cã¹í¾Ÿo}e6áóm8?…~|S„ÛüGÖ­ÇD¹qÿWí¾“¯‹Ë9-æ8‘Tÿ*üQø¿øYÚÙ_âºý¼Ž::´áR;ž¿©Â¬éËk¨of¶ð¦±ç:ú’2F¬ÝÅxŸÁ]Ý|¤\¸ËyB½ÄaTé_kÂØyÓÂÁÉécäóêðy(®¬:Šó‹>8?| ¨x‚Ù<ýE‚Ûið–šöàùp ^ÿ1Éݾ³BUgpZ·dx&¡)l9±ÿ‹ŸñÎãS?¼ðÿÃUkX;¤ÚÍÊþù½ÙâÂ`ò®r+èêóŸ…< ¦ørgóï´÷Ó“–žòs¾y u9r@'ø@¯F®¼Ê¼eS’ŸÃeèºüÝßÌË £ynõ×–ÁEWžtQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@>|{³¹Ðm´/ŒLm%ïî¼ë…AóM¦\b+Èñß C‚xI¯z³»¶Ô- ¿²‘f·¹E–7S•tq¹XBE6þÆÓT±¹Ó5„ö·q¼2Æßuã‘J²Ÿb á?¯®ô;ZøI¬ÊdÔ< uöH™þôºtÙ’Ê_û÷ò`tÚz߯ÂyÓÿÒ[ý%ÿ¥Ÿ_)~kü×ä}EW’u…Q@!V‚ -æ ø-ðŸáljµïx¶Õ¼N!]JkDçìåÙ F˜Œ6db̪ “–'ŸEW“|<øð{á>¥«k¼#§èZž»4·×B>ÓpóÈepó6é6o$„ݱ…@¯Y¢€ ŽYb‚'žgÇ,ÌÇTrI' %|ûñÃT¿×ßIø/áÉš-KÆ,ÂöTûÖºL\ÝJ} ƒ÷Iž’:×V ëTP½—WÙ-[ù#*õy"äSøGŸü]¬üqÔPý’è6™áôqšt|ÉÀ=â@O¨QŒà×ÑÕCJÒì4M2ÓFÒ¡[{;’c_º‘Æ¡UGÐ ¿UÅ*µ¢­¢]’Ûþذô¹#g¿_P¢Š+ŒØ(¢Š(¢Š+ËüyðSá7ÄíWF×|}áK kTðõÔš}äð´ÛMm –2“.$ A)»cte#Šõ (¢Š(¢Š(¢Š(¢Š(¨.m­¯ {[È’xdd‘C+B§¢€1-|5áË„º²Ò­-çîÉ£®F8 8­º( Š( Š( Š( Š( Š( ²«©G•†<‚ |¹ð¿@мã|×4ëkˆ-óªèRO ;6pçÌ€3Ñæ$œá³Ò¾¥¯øï¢ê6ºV™ñKÃQu¿ÌoB/[‹o >ÍÌ=׎Mz¹\”ܰÒÚ{yI|/ô~Mœ˜¤Õª­ãùuÿ?‘îv6Zt k§ÛÇm 䄉(Ï'p9«U•¡ëZwˆôkH”Oe¨ÃÄ.?Š92ŸcƒÈíZµæJ.-§¹Ôšjè(¢Š‘…U¾²´Ô¬®4ëø–{k¨Þ)ca•xÜe>ĵEy‡ÂÏ‚ß > hmáß…±ðÍŒ…L«iY&eVšS™%`8 #1÷¯O¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+æß‰Íð¯Æö¿t¸Øé‚-?ÄРÎmÉÛîVŽÖ<’‡šúJ©ê:}–­as¥jP­Í¥äo Ñ8ʼr¬¤zq]˜W²©y+Åè×t÷ÿ5Ù٘ץÏ-úzûeŸØÿ´<ôû/—æù»‡—åãvýÝ6ãœôÅ|ÙáXføçãh¾#êhÃÁ^™—@·p@½ºBUõSÕTåaݸ9ÏhÿÛúçŠ.?d¯ítoèÓÉ-ÅúÍþ•>Ž‚9N\ËE2yrxEÆ0?{XXYiV6úf›[ZZF±Ecj$h0ª t õ±45yÍhûAõõ’û•ûéÉJ§·z«%¿¯ù/Çä[¢Š+çOD(¢Š(¢Š(¢Š(¢Š(¢¼›âÇÄ;i–šO‡-Æ£âÏÈmt›?ïËši="„|ÎztÈß ‡YªpÝÿ_rêgV¢„\¤qÿ5ý_Ç>"_þ¹kyîcYuýB/ù‡éïÿ,”ôÜu wc#Ü|? iÑ,¼; Û-¦Ÿ§Ä±CtU_Ô“Ô“É9'“\wÂÿ‡–ÿü<ÖRÜGYÔdk½RýÿÖ]ÝÉ˹=BƒÂ/aîI>“]xìD,¨Qø#×ùŸWþ]—›fT)»º“Ýþ ·ùùü‚Š(¯4é (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ Cœ½{f–ŠüKÿ‚-³¿ƒþ/>¥ÿ!v×­ÞzçÊ“ÿï¯Ùÿȱ«ÿ×Çþ‹jüjý•š/Ù+þ ñ_öyñ<âÇAø£³Xðìóü©q/™$ÐA;'žxÝ$!G$ ý•ñOü‹¿ýyÜè¶ Å_ø"wŒ•>xÛÀ?)“W}JØãï‘omã=Ê(ãÜšýǯçÇþ ’¯áÙµþ0[„¼}qñôÍCO°‚ã uØJ0ôÁ5ûñÁ¿ié­tÔýš¿áÝ2Oöé¼S%øØNÏ!­–Å7Ëï }Ü3^†.„U:ua³V~«ÂÏæsÑ›æ”%ÓòÓGå¿ü þNÓö¢ÿ°´¿ús¼¯Üzü<ýbø('ìÙñÄßü+â_‡šÅÿŒwVJãThgw˜ÎdEŒLŽ›m¸b ž1õ_üÇþ#ð'ì)¯Ïã]NÛEñα—d¿Ø×Gêou “¥”²bV€cõyçAú1_‡ðTïù9ŸÙƒþÂÇÿNúû+x{ã„¿cOhڦɾ"Ç¡Í4K®I7–.îZIíc½tß0T‰.uŒdb¿>ÿhߨ£þ ûK|Cð×Äoø—áæ‘yàý§JƒM¸ÕeY¼ÃçXÊÎåÑs–Û…(ç ¸uø³ÿÿ/Á_ûæÿÐ"¯Ò¿ðÔi¡í"¾f 2øUõ2?Í焽ñ³nÂyÝ+ócþ !ÿ _‚¿ö1Íÿ E@´Õø•ûi´«ÿ9ýš‘Ρ³Oÿ×±Ôg ÿŽyµûk_‰¿ Ú/Úçþ ­|^ÐçÞø)§¦Ÿi{ÍÍÒ¤±¢+މîeV†X”ç (öÊŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼ó‭þ#x>ë@2›KèÙnl.—‡µ½€î†U#‘†àã’¤ŽõètV´+ÊœÕH;5© ¥lÏ-øEãÛxWÌÖaûˆt‰^ÃVµèa¼ƒ‡ qþú‘ƒŒœõ*ù³â2?ÂÚ|e±Rº­äéÞ%G „í¶¾ w‰ŽÇ=vêkét‘HØ:8r=5Û˜ÐÕjKÜ—àúÇåÓÉ£4Þ°žëñìÿ®·EWšt…Q@Q@|ñáïø­høˆüö±‹G¶=TÞÝþþé×ý¤@‘·Ö½³ÄÚõ—…¼;©ø—R8µÒí¥¹“±+ {œ`{×—þϺ îðÎÇSÖüMüM,ÚÕót-=ûy£#±”R=«ÕÁþïV·W¯ðVÿ·ŽJÞõHÃç÷møþGµÑEåaEPEPEPEPEPEPEPEP^5ûFk3øsö|øâ PZ}3ÃÕÌazï†ÊW\~"½–¹/ø]ûc…}cûiÿɤü^ÿ±cTÿÒv¯†¿àž#¸Ò~|Dø â8MŸ‰¾ø’swnçæDºQ\z¤öÓǨõ¯¹m?ù4Ÿ‹ßö,jŸúNÔò÷ü?Åðk_²áVÂÜøròýv÷0]^O29ú¹‘à5ú}_ßðNB| ð'à—äÓ¼\5¿_žŠ.F­y5‹ŸVf { ýøåñëþ¾œñü=ñgÕvŽ? èòj‚Î.YD;Ëü™Îà­ò×~:„b¡8m(¯½hÿߣG= óF[§ÿ~å¯ü þNÓö¢ÿ°´¿ús¼¯Üzþrÿb_|møñ×âwĉ>!Ë¥|HšK’4ß^Ï5¤Íu%©Yc„:bVƒd0§<~þÝ¿µg¾~ÌøÏð¾Ù´-_Ä¥…¸´×¬J]C ÕµÄïÖÎÀÇ2˜ÆàNWWÐ~ˆ^C-ŤööóyeFT•@%†x$kð[ö¹ý„¿h_ øzoÚçâÌÿÀBMZm Å6­%°¶wÎñ@g’Ý .ñlŒ2©Ác…?_þÓŸ?m #á'Á‰ß4;­_Qš]Gź]•¤-:Éoo1·1:I:«¹•3n7¨=s¶¹?¶W~+ü×¾|'ø ñx߯lú[EªhrYÙéÿm‰¡šInY¶›Ë$*“‚Åyõ'ìCûIØ~ÔŸtïÃ¥C¡jlòi:…·Ð]Z¤m‹pI"&ŠHÙTò¹Û–Û¸ü)ûpÿÊFeÏúøµÿÓ…}•ÿþý™õ¿Ùgö|µð?‹düI¬_M¬ji #‚ââ8âXÇ åÅ #å/»i#ükûpÿÊFeÏúøµÿÓ…~ÓWâgí7ü#_ðT_ٻĶc7…½ŽŸ _½²[ë¨ m· “è+öο<}¿ÿà¯~Òtüí;àî º¤¹Ü‰$+-È ôÝç^Aˆ>†€?k袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÒýü¢Š(¢Š(¯ý¦~6ÚþÎÿüMñjæÏûAôX‚ß;D³ÊÁ"V=—q>•ï5ùíÿHÿ“-ñ¯ýt°ÿÒ” Ã]gþ Íûhêz”÷¶%°ÒmåbRÚßKµxãSÑCL’9Ç©cYðõÛwþ‡koüØñŠüê¯ÙØ/ö0ý–~=|×|sñoÄï¿Ìð41ß% Ó¢AI™ïîÉnxÀÅxü=_öÝÿ¡ÚÛÿ6übøz¿í»ÿCµ·þ l?øÅ|â½7LѼQ«é-èÔ´û+¹á·º âʤƒýå×ë÷ìCû~Êg-Kâ/Å=Ÿˆ„÷qNú;UÒcƒˆÝã~râL·ËƒŽÆ€>vÿ‡«þÛ¿ô;[à¦ÃÿŒQÿWý·èv¶ÿÁM‡ÿ¯ÏVÚÒËT¼³°¹¶ÐM$qNQ,jÄ+€y‡8>µû7û*þÄ?²WÅoÙ6ëâ·Ä/5¯‰š;׸™oRô§€°Dx/ò…c»ïgåãóWü=_öÝÿ¡ÚÛÿ6übøz¿í»ÿCµ·þ l?øÅ~x\ÇWÅ ù‘£°VþðƒøÔ4ú+ÿWý·èv¶ÿÁM‡ÿ£þ¯ûnÿÐímÿ‚›þ1_TPè¯ü=_öÝÿ¡ÚÛÿ6üb´tŸø+'í§§êÞ^x¢ÇS†6­çÒí9pÆãqŸf¿6h íÃöPøûkûL| ðïÅØl³.55–»\îX®­¤h¥ÝЕܹçiç5ôe~hÁ$¿ä˼?ÿa=WÿJZ¿K袊(¢Š(¢Š(¢¾ øõÿý—¿g­VãÃ^*ñš×ˆm iš,Böâ&•Ë$¸î*¸î¸¯•ì¿àµ³<÷†Ï ø²ÚÜŸ–_³X¹ú²‹Î?ÔûE|õðö¨øûJió]ü#ñ<:¥Í¤k%ÕŒŠö÷ÖÊÇ’ B¶Üñ½w&x x¯¡h¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢äP€ç ™ü~ßd\´|þ¢¿`üVâ›Õý:Íÿ ü§ý˜p>&L¸ÎAþb¿4ã(sâè/3íø^v¡]ùOüUøÁá?‡žºÿ„³U3¼.Aë·Šü\øã‚Þ ý£u|M¿GÓÛyG“æSÊž‡5ú÷ûYþÊúÅýçV½ýžø>ª¹þ•øYð[ögðߎþ.Þø6ù#A»ôàñ¯©Ëé,-ÝVxX©ªÖ²?£_ Cðž›Wð¯ÙçÒŠyT ª0{WœxEø¦êW ð±£i²|Ï/ŸÂ¸ß |!ðßÀdžteDˆÁ36;—×ÍðN}>½Z  €ÌGä+l](ã(¹SzRœèTIì~„ë_ü;àæ¿~¶1ÊÛAnçüšôè¾0xÛA]sPÔãŠÔ‡,9Í~_ÁJlfÑ<mtªYa—zú“•8óÀ‡?ÿhßÂÚ†«6Ÿ C´y p¬1‘Áß½y¹|*aUæÎÜc†!Ú;Ÿ³ÿðÒüKwý‘oâ8èœÀçó­‰!‹`º²‘f¶“£©¯Jü¤ø—ÿê´Ó<5qâÏjbÇV°C)hÈJŒúî?àŸüeâW»øgãÛÉ/nì·*¼‡$쇭væb¡ÏOs“‰ òÏcô¿IÕ%ÓdÜ[1ƒ““Ž)þ%ý ~x4¬~&×!´“¦ÒAþµñ§í™ñ¡¾ xNH,¤òõ ¼¤x8<ãξø3û%xÛãò7þ)ë\Û\ÉÌ8VçÒ¸²©Ë œjìvf0uÍMj~Êë¾;ø}ñCÁš¥Ï‚õH®ÜBÿu€í_²l~Oí©§ÝØÓå^ûãÙ7ÇŸ|1y℺܋f°Ÿ2Ò uÈÆ{ú×Í?°#ê>+øÓy.¤Œ—±ù¢MÃ’ÜW¥‹ÃBTe:{´pÐÄJ2Q™ûç¡xˆéùK—Ä@u' ¬/þÒü&þ^¿âm˜Nyüëàۛㆳð‡Â+c¢–‚úíÌJëÁkçoƒ?±Tÿ´(¼[ñKU:˜¾EYH;wóè:WO‰•iVz3«‡ŒåÏ ÏÖ‹_>"7•á½z ç~:Ó¹µ’ÝòØhñÇ5ùEñOþ óã?‡Zx«àF«,7Ví‘ ±ãð&¿BÿfK‰:ÇÃűø£m:j–áT<À†lgžk|Û(hûJ{™eøùAòËcÖô½N}:eØO–O5ëvwvº­®Óƒ‘^;}c=Œæ9AÀ=ûÕ½#S›O¸^IBGóùve*öU~ÓÆ`ÕhóÀü‚ÿ‚iBÇǺk ù_û5~‰|’H~iÆØ#çþ+àø(ÕÔ7þ4Ò^2£þ_¡ß,£Ð>iºÔ§Ef²1íò¦kÖÍ𶦧E\âËñ“ö‡¼ÙxÛKÒ, Æ·tFYˆ ó=[öžø.ÿ²o¼KŸ8Ûžÿ~*|_ø»ñGö‡ø¨ÿ ~_Ïcb¯åË$'åëŽx5ô§„?àš¾Õô…¼ñ òO®ºgÎb #¯O_jïÀâÓ„iÖÜåÅÓ÷Zgé-®«¡kðKÃ7‰{lüîR;ûU«{©­åó o˜Wã[_ü[ýŠ~'[h^#Ô¦Ô¼%4€üÇ(8Çvú÷£ê¶ºç…­¼YfÀÚÏ›Ž€Ý^g•:Sö”¶=\=N<µ\µñ–•g§}§X¸H!ó W‚xÏöø§Îðê%†Þuì9çð5ùAñÿâÏÄŸ‹ßaøQðïP›O³‘¶Jñ[†½ÿŸðM¯x—EŽ_\­î§"‚Ò> n#ð¯S (U¦©Vz³Í­NTæç ³ü5ñ[á猜/†õˆ®‹ôÁþuî×ÞÕü©Û1ö¯À¯Ú#öyñÇìc¨YøÛÁšÜ‹¥îÈFq“ÁÀö¯Ö~;“Ç_4ïÞÞ›t’B{€šó1XIàª)Aèz4ëÇ =ϵï5}.ÖůofHà%˜€ükäïüø§j¿Ù§Ä°Çx[<“ìkóCö¡ý¡¾%ø÷â·Áo†—³Y%Þc’HOO›<ZõÁ0ôÍGD‡^ñ>§öÍvâ5“Îb +‘ž¸ ¯~T)â©%/ˆò–¥ÓÐ¥ÿÕ´½àœ÷šMÂÜBHmÊzðÕï_ðMÝaá ­¤ÇF¤~8¯ËoÛ+á_Å¿> júœÚ¶‰tÛ‘ÉÊÆƒ+Žú×èìÆ?…ïÀ¡þUÅ.l %êuÞ8©\ý\¹û,¶å§ GŽI8òÄ?Œ| zb×ø¹ðãÆä ëÞä85ï^×ÞÝÒ ƒ˜ÏÎkñ#ã—ì‡ñö]H¾ ü*Ôæu¯2[ÂxÆsƒè=kí¿Ùgãš|\ðlMòjV¨`O͸`þ5óµðu02烺=ˆW†"6–ìû—Å_¼ áéíô¯ê1ÚÍv3¹<ãú׉øóâŸÃIú湺Ýs9‘Ð×å§üoÄz®â þÀ3Ü +ÜäeÅv_¿cOþÐÓüSñ;U{ˆ¦Ex#ƒ°1Ôz÷åFž.š›ÜòUia§Ê~‘xGÇþñ…¬—þ¿[ÅÙ+ô¯ËŸ‰ÎòxÿV‘úý¥‰ÿ¾«ôcá'ìßoð/I»±Ó\Ío*’1ü8WçoÅ G·ñަퟞv<ûµ~QÅØiÒœSØý…j£”–ö?b~ßý»áÞ˜1ƒ`W²óŠùÃösÕb—Àöp1ÁGµô~ìŽ+õn®ª`ánÇæùÅLTýG×Î7_ñsþ:Cd?yáÿ†ª'—ºM¬Ü¯î×Ðýž,¶G*ç½Oâgí¾øUñléçIg-áêf¹ì†0's9ÆOjÇø9à‹ŸxÒÃV;[Ôõ RcËK}twÊIvœ =Šû<îhOÕû±ù¯yü–Ÿöòìx½ùª}¯ôü#Ô袊òN°¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯>&Å¿øá_‹~ïNÔðþ²„CrÛ­&nÀG7 Ǭ}\—[žcTp{Šî˱§U9ü/GèôvëÍ# M7({»­Wª:Ú+È~x¾ÿÅž¶M{å×´)dÒµD',·–gc“þúíøzõaŠÃÊ•IR–éØºU⤺…QXQ@Q@Q@u=JÇFÓ®µ}Ne·³²‰æšFáR8Ô³1öf¼'à†›}â;_ãgˆah¯ü\Ul"“ïZéŸôhÇ¡—ýkã‚H=j·Åù¦øƒâàf˜ì-ïÂêZôˆH1é8ÛGF¸õŒú&a¶†;{tXâ‰B¢(ª¨À€•ëË÷k}ªŸ„Wÿ$õôK¹Æ¿yVý#ùÿÀ_Ÿ‘%Q^AØQEQEQEQEQEQEQEQEQEQEQEQEQEQEQE×D‘92° ‚2=Aê(ç/„.ÿ¼_₌VÒ̶­ –?{M¹sæBý;ÌJúsÐWѵàŸômFÏLÒþ*øn#.µàYæÅû×6Þ@Þæ…xä׳èšÎâ-Ë^Ò%Ùj0Çq ƒ£G"†Sù•ëf_½ŒqKíi/ñ-þõgë~Ç&Ün—m½?àm÷”QEy'XQEQEQEQEQEQEQEW|\ñö«áøl|à„[¯ø™šÏ+oÿ[y7\G äò>fàƒ]§|q¢ü;ðµçеÆ&`8“™g™øŽ×»»pâx×ðÀúÕ¤×ßü~¡¼câUS*u]>Ðs”^ „}çëœdú˜1„^&ªº[/æä·%Ôå¯7'ì¡¿_%þo§ßÐäuïCÃßtÃðþBþ2ð´ïªÛ_KÌ·×®3t³±9atÒ ãåÀ¯iø{ã}/â'„tÿi@Æ—i‰aoõ–ó¡Û,.:†Ftç¯B+´¯š5ø²ÿ—Z_Ýx7â ÂÅv:Gc¬‘„˜öT¹kŸïŒ’tB´±•:ŽóWq}ú¸þ«Î멜 ¨ÉJ?Ïô£ÿ€}/EW†wQ@Q@Q@TrË<Ó8Ž8Áff8UQÉ$žÍøÏÆ€ü5}â¿MäÙX&æÇ,ìxXÐwwl*Žä×—|'ð~¹©Ýü_ø‰—âmr0–¶ÈÒôìîŽÙsÒFûÒœXãœó^Š_>5‡Çz‚7ü ~†‹Œ.£{*×Χ¬qœ¬ ÷ËqÈ?OW³ˆÿe¦è/Ž_’þ_þKîèïÅO÷²öá[yùÿ—ߨ(¢ŠñŽÐ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(æ¯Ú[öZøyûOxjÃJñl—:F· Ü ½[Ó™bÔ4ë• †ŠBQŠ©d=HuVü ñÇÃuŸ|hñ—޵%’{}3UŠÐÙO-‹B«½@Y|àåÁ)œ¨³18úŠühý?dÛ‡örøEâ?ƒ ¿ µ½Å—wR_Þk&uk›x­™PÅhŠXƒ)*HnsÒ¿J¿gý~ÿUøy‡®6u¯ O.‰|;ù¶'b¶O'|[=É5íµó¼ñAþÐÓ[ýÍ3â=šŸÝ¦˜¸p; öäݘw¯WûÊ5(u^òùoÿ’Ýü‘É_Ýœgò=¿Ìú"¸?|/øyñUðö·ãoÚkWÞº7ÚT·Q‰ ¥Ë.Ó"Æq‚2 ªÃæU#¼¢¼£¬(¢Š©¨}¼X\(Do|§òÅ„^nÓ³y\¶ÝØÎ9ÇJü‡ý¥ÿe/Û—ö«´øuðçH´ð–¢oÐéwzÇ™&ý•¼ûG…ã穯Ø:(äïÚoàÿÇ‘ižøsñ‡¾ ½ŠxüA5½«M«Ü£• ¼…•cÓpv ¬>ú’µéß>|:ýœþXü3øgbm4ËBÒK,¤=ÍÝÃã|÷»älœ  *€=ŽŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÎÕô;^Ò®ôM^¹²¿‰àš&û¯ƒkõ¼3àÆ­¨ø_QÔ¾ø¢v›PðÒ t»‰>õîŽç¿»Btøà`uä×Еáÿ¼'¬]Xéÿ|ÿx9ÚêÙk¶#6ŽH•3·¾à1Œ“^®]R3O Qé-Ÿit~gäïÐåÄÅ«UŽëñ]ÍáEs^ñf㟠i¾-Ðdólu8VTþò“Ã#z20*ñºZójS”$á%fŽ˜É5tQE@Š( žÿh)^°ðß›V"ojqA8S†}¡l>Šª>_@ÇD‹J  @|÷áßø­hxˆüúìbÑíOU7—¿ºuôd]‘µ}^®aîS¥C²æ~²×ÿIå90þô¥S»·ÉÁ¸QEåaEPEPEPEPEPEPEPEPEPæ§ÇOÙ_â—‚~;Aû\~ÈbÐxºá/xröcog¯@Àd£ð‰;mRw2©uY20“Õ¾,ëÞ<øÙûü@½ºð­áŸë^Õí?áž?´ß ¤Y!  L²2æ"R¬WÚ´Pá÷ìÇâ¯xsöFð¯ìË⟄tßZÜÜm@øfê=:Öò}N[«Y¤º“g—‰Jå>EÜyŸØ¯†Þ1·øàMÆøÚvÉ$Š:$ËòÌŸð/á]½|ïð§þ(¯ˆþ6øU/ÉjóiJz}–øâtQÙbœûUêÐýîtúÁó/Ge/ýµü™É?vª—G§ê¿SèŠùwãgì«àïÚâG‚øïDðÿ„ïm"ÔnµÏÝØÅûgšdgâƫՙ€û¡E|ûN|Pý ü'™àÙËáÄÞ-ñ7‰!œ.¯q,Pi@B«æÜ3°ß ݹ#%wHß‚‡ýŒÿd{oٛî±â­Oþ‰~3˜Þx‹Zfgó¦fgÂÒÞZ³3`G%˜µSí:(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÓýü¢Š(¢Š(¯Ž?o¯†^*ø¹û*ø×Á¾ µ7ÚÄ‘Cs ºró}šU•‘v!Ns_cÕ-GQÓô}>çUÕ®c³²³¥šyœGQ Ë;³@$œ þõ=/SÑ/çÒµ›Ilom˜¤°ÎˆÃ¨e`?QU£¸žt†FE”mp¤€ÃÐ㨯ë[ÄðS/Ø_NÕ®,gñ Õ$Ê4öúdÓDÄ®ÑÃ=Ǩ$V7ü<ÿöÿ œ¿ø'“ÿˆ äò¥Iç(ädI8e€ÀzŽõý_ÃÏÿaú Ëÿ‚y?øŠ?áçÿ°ýåÿÁ<ŸüE'•*OK5•Ä{ó›W|¯èKÂÚ†³«xgIÕ|E¥¶‰ªÞZA5݃J“5¥Ä‘†–$e‘ÌnJîRTã#ŠÞ¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ héN¦ çüWdžµOúõ›ÿ@5ùSû0ÉN›éýE~ªø´ÿÅ7ª׬ßú¯ÊŸÙ‡åø™9<óù·¿öê§ÛpÒÿf¯è~¡xíx+SàÛ?þ€kðOöe±y?i}`@¼‚çõZýÑñÞ½h¾ÔâÞ î?ñÓ_‹?²%ý¨µ€Få%ÿš×ÝUöu鸭O•5)©Hý!øÆ^?‡š¤u¾Îùÿ¾M|5ÿÚÔÖ/ê¶òn,?A_£ÿ´.‚Wáέwl¹"ÝóŽ¿t×å‡üð´^%ÕF¯œ£¸X·ÐõëÍbZ±ô¿üÒ®ü§EÆ$˜ ÿß5×~ÉMá5 …0®“ïƒ^GÿÖžïÁz]¬ã¥ÇñZûöQÓmõ/‚ú|l0Li® z8¨}r‚”79(OêµZ‘»âYž jX9 çò¯Ì/ØÀ[¯í'©¬»ÏœHÿ¾kõ#ÇÚt¶:¥œm‰ñùWäoì£s-¯í#}+&1Ñ„iÌÛ†ö²•H?²gǯü'ñv¹¬xµ7^Æ*[æ8ãØ×è¼ðQ¿†ó‰br»O§ÿZ¾Hýˆ>| ñwuÿ übÐí﵉nØB.ÎÓ€1‚=ëõYÿ‚~þ̱´Zø.ÑŒà#ñU¶; ~†8lSþËïÛWö·øyñÓÀ ¥i_.£’ùº@ ~~ÂZËx·àl6šÁ2G*„·÷@Áëí^KâoÙ‹ö2ð,ž*Ð,4ÞàJ óøµ}cᛯ‡~øM¨„ñC¢[HP[ô8CÓ“[àsU%ŒqX:±|ñ>*øóñƒàWìóâé.ü;¥&«âËmM¥ƒgýÓé^ qûuünñï<'à+¸ÕøVÜ1\m®öUð†¾8þÑšÕçÄhò{[‘Ç/9oZý¸´_b-Ê8m£m^ áÌ0Ñ ÕHÇšç^çîMŸÎGíKãÏž8ðüßà’ÇOgÊqÛž+öwöhÒKþÍ^Û 0±ãxŒWÂ_ðSˆÞ}* ÛNtO*¤duúû\¦¯û=Ûégåv²Ú¹ïû°+¶…hצ•UnÇ=X:3~ÌüÏý›tÅÖÿhÝRóSýì¶÷eTžHû¦¿v4 }¬Yl¥? àWáo/—àÿíQw¤ø€ý˜jw&Dfã#*µûIlÂòÚ¸>håƒCÍx™”§†ª§ OO©Õ§in|ÏÿÐtÏ|¸¸tVtþ"G \ŸìA Éiðiäµ;öAǶ® þ ñsJð×Ã)|5qp¯ur~TÏ'¨é^íÿû’=KàªHtDõ½è(ã(^g‘ÍõzžéøËñËÆ6:_íGi¯ø¢MÖºt¬»Î~u>õú‘á?ø)wÂ]+I¶Ò¦1nŠ‹Æc²×Áÿ´×à x{ö¥ÓÓÇúZÜhÚ›³³Jß¾£Ô{×èw‡¿c_ÙkÄ5¶©aáI£n ÝÆ½\x¬JÂr©'c¦•ˆ»¹ÉüMÿ‚…üñ§ƒuIûÃs‘8#ºû×Å¿ðO^_üQñ¦Ÿ¹ìn®dhÆ „Ò¿L¡ýŽ?dÂK­Áš|P¯&IU‚€9;ëÔ>|;ý–tJY>Yi±]DpßdmÄÞ=ëÒ¨éã(ûšpç¡S^‡æíóf·~?ðõ¤Éýõ¯ÔÙËSM'á·b@ˆ º+ó¿þ ¥¥¯ÄßM}à/1k—áæŸ•¼„?øè¯ŸÅT©„PG±†„qÌúþå¡»²|a•Ô×âoǘRˆ7ñ¯Ê¾{3_­z&ºÑFöó·Êãò¯Éoò$¾>½ †œŸüz¾c1¯B-ot}Q•òOk ß´û‹? érÄÙ•õ¼ ˆÔ·Zò‚Ú}°ø}£É°kµñ÷Œtïx;Vñ~§Ìd &ÌàÉ'ÝŽ1îîBs_qÂYt¡†„ ®åk|Ï’â Z•i·Ñ³È¼Cÿ;ãn™á4ýæ…ðüGªêÒMN`EœGÞ$Ì¿Ž }^?ðGÁÚ…<—~"ù¼Eâ¤ÕuW#íW_1ØD»PƒkØ+í3:±çT©»Æ ËÏ»ù»ÛÊÝœÂÁò¹Ëw¯ù/’ (¢¼Ó¨(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠùÒ_ø·Roõz'Ä«-»$z½Šü§ÐyÐñêÎ+èºò~ ½ñ§€/-ôRS\Òž=KK‘~ò^ÙŸ2-¾ï‚Ÿð*é~xÊËâ‚tØ€©©Û¬ŒƒþYÊ>Yc>èá”ý+ÖÆ~ö„+õ^ëù|/æ´ÿ·NJ>äåO£Õ~¿Ž¿3³¢Š+É:Š( Š( °^±Öêºc÷ô˧;âõû<į©ÐWÑ•óíað¾—¢ü\³ž+}[Á·k*,’,fòÒãÝZ©b73§Ì£“•àd×­•~ñ¼+û{‰|/ô~Mœ˜¿u*«ìþ]Ïä}E~v|Nýº-"I4Ï…:ižB1öûå*‹ïäû#ª‘_hü&Öµ?ü2𾿭Oö›ýGN¶žyHUß$ˆŽ2O@­1ü?ŠÂÑ|DyT’ë÷tüÈÃæ4ªÍ›½¾ãШ¢ŠñNࢊ(¢Š(¢Š(¢Š(¢Š(¨n.-ìíå»»‘a‚g‘Ü…TEfbxI©«æÞ]üdñŒ¿4 ž? é,’xšò"G™ÎäÓ£qüOŒÌGÝ^3œ©ìÁa=¬õvŠÕ¾Ëüú%՘׭ȴսÏ[Ü|mñ¤_µˆÙ<# JéáËYêu;_Q‘O¸+=Í€y?MUk;;M:Ò RÞÚÙ8£@aU@à«4ñØ¿k%Ê­¢]—ùõo« <‹][Ý…s>2ð–㯠ê>×£ólu(ŒO¼§ªºžÌŒ)ì@®šŠå§RP’œ]š6”SVg‡|ñn±sk¨|8ñ¬»üUà÷[k‰lµ#ýísÉ&7w pN+ÜkçÿŒú©¡]éß|!›Xð²²ß[§Þ¿ÒXæxO«Gþ²<ô ðNuž"øÓðÛÂþ ²ñÖ¯¬Äšf¥ Íhæšà0ÈXâ1npÝ”ýâ+ÖÅa]wØxߟK.’ê­Ùî¼´èqÒª©§ n¯·õ£ÿ‚z¥ä_þ,Ùüeð½ßŠôûÓíᾚÑ#‘ÃÈV5F øƒä:dõ¯]¯7†Ž•Ei-ÑÓJ¬g8½QE QEWÍ_5,—àdž'xtK’xžþ#‚#nWO‰Çü´—¼#î®AÏ̵Öü[ñö­¡­| «sã?ŽÉ)iÿ[y7\GÉ3pÁ×|:ð“ðãÂðxsKfžMÍ5ÕÔœÍwu'2Ï!ä–sêNA^Æ,55‰—Äþÿ·|ºwzô׎«ö’öke¿ùŸ—©ÖéÚuŽ‘am¥i%­œk1F6¢Fƒ ª;]¢Šòm݉QHŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ¼/öƒÑ¯®|<[¢&ýcÁwPëv uo²̇íxK‚;ñ^éP] f¶•o6ùI¿6ónÏÇ\×V èÕD¯g÷ù|ö2­Ož/©GBÖl xwWøy­^ɬ?‡5›}0XâàO`íæÂLÛ„`¦òŒ7äc8ÅzOÀ?Ú'XøáâýnÉtxtKµIoi§i$“jî•@Úå ×¹¯cÃ8ªq©YC÷qêôº{~g Δœ`ß¼úyõ>°¢Š+çOH(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šù§Fÿ‹7ñbO Iû¿øþg¸ÓÏHìõ|fkqÙVà|ñï ª:šúZ¸?‰^±øàûß ]Èmæ mnWïÛ]DwC2È*ØÎ$dgšÁø;ã»ïøj[?Æ-¼Qáù›OÕàé¶æ.<ÅÜ™~u#ŽHÅzø¯öŠ+¾(ÙKô—Ïgæ“êqÒýÜýŸGªýWê¿àµEp·_|iãK‡ªE'ˆu!KHÎ÷EŠ6•Œ˜ÈO•NOaŽkº¯2¥ÂÜê×W^k¹Õ§{=‚°üM¯YxWúŸ‰u‹].Ú[™9Á+ {œ`{Öå|÷û@Èúõ‡†þÚ±ó¼m©ÅáNiö„\]°ú*¨ú5teøuV´a-ºú-[ù+™âj8AÉoúôüMŸÙ÷@½ÑþØêzÀÿ‰¿‰d›Z¾l`´÷íæŒŽÄFQHö¯k¦GD‹J  @>£ˆuªÊ«êîUjP]Š(®c@¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯þ6Å!âü`‡ä‹B½~¦Ýön¤DLî{ˆ¥ØÀzšú"¹Ÿø^ËÆ¾Õü'¨ÿǾ­m-»gau!\{©Ãq]Ùv!R­OáÙú=àa‰¦ç–ý=VÇME|¡àÚ'ÀÞøg§Åñ/[ŠËÄZ/™¦_Zfº7,a-å -ó… ¸€¹'ž w¿¾9èÿç×¥ðþ›qe§èÍn‰5Ë(yÚa!?»L„ 'wAŽz19*”gRP|±vo¦öÓ¿ÈÊ–:”œb¥«è{•Q^IØQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÔýü¢Š(¢Š(¯€ÿà§:•þ™ûxîM>v§9C‚ÑËpŠê}ˆà×ß•ùïÿGRß±o¶ŒâKÿ“)@È]{ïÂßÙoãÿƯ ê>.ø_à«ÝGÒ‹,÷ìTÜ£,‰æ2™ªµx~‰~ËðQÿŠß²×Ã-Gá‡43[°šinl¥¼óRKIæy>[*d´í9þ,q@ž—6×W2ÙÝÆÐϲHŽ ²:œ ò<^ñðßöXý þ/x?PñïÃoßëÚš]eºS£u‰YƒJÊ:¬aˆ¯ñ¹¨xŸ]Ô|G«8’ûT¸–êvQ´fbì@Iâ¿A?f¯ø)_ÅïÙ›áÏ hZ^­i—i×W‚Q%œ—$»îXÙD«¼–à‚H$ŒùÌèñ»G"•e$F#¨5ô‚?e_ÚâGÃûߊ~ð5þ­á{!’ö%@¬!ÿXcF`ò„ÁÜcVÆzW„ê÷z­ýΩ~þmÍä¯4¯€7I#cÇ$ö¯Ñ¿ðS_Œ~Ið?BÐô½B d¸‹NÔ.‚{D¹,Ì‚Kµ˜”'þ-ÊüÝe*ÅX`Ž4•,óIq4—œ¼¬Y©c“QPEPEPõ·ÿ Ôõ Kö+ðª_Îó‹+ÝRÞä±H–éÙPØ8‡Šý!¯Íø$š‘ûøx‘€ÚžªG¿úK ý. Š( Š( ¿œ/ÚfdðWüÁ¾"Ôó ¶§ªø]÷ÝÙ4PÙoÿt2œžØ5ý×àïükà†ºßð†~ÓžŠ]ú®‘ªMC['œf°ŸŽT ¤‘ vfŒwûÅE|£û~Ô>ý©þi~2Ó.¢OÙž»§©Ä–—ʸs³¯•)ân…NÜîVêê(¯ÉïÛÛþ !¨~ξ1ðÏÂß‚pØx›Æ²],ºÅ¤é%ÂAnë¶R!taq;8e‹"¨%H‘sú{àëÿj¾ÑuOé±èÚåݼ×Ö1Mö„µ¹’0Ò²í]á•ÝœPIEPEPEPEPEPEPM^´êhâ“cG=âÁŸjŸõë7þ€kòËö[ÒåÕ>!_,o‚±±ýE~¦ø°ÿÅ5ª׬ÿú¯Ìߨñ‘~%ê9ÀÌL?Q_žqD/¡ê}~A&°µÚì}aã‹;£áíNÐ+<ͪÜàŠüÂý“¼ ãß þÑZ†·â&Kkwmôä¯5ûYâ-n¢3Á÷Åy|‘´Lc•È;ãšês«›} cb¡n§UñnÎë[øqªÛi°ý¢imÜ*Žä­~\þÅ ßµ×ìà_IÐmçñ—‰#az¼Æ¿lµ :M>VŠPyèjΕ©>PeExÉËs9áß²©±ëâòøÕ´ƒÔüY×l?kOÚm#ÐõK9ü;i1Û qž_ºE}ùû:~Ζ?|2ºk%ôØie·9ëÏzýÓ.,¯ Bˆ¤öVf½ Ç¨@dýbô¯o2Âýb“•'¾§™ƒÅ:så™ÂèzãØJÉ1·c^³kuÜbHÎAá7ÓZÌc•pãŠÛÑ5é4é6ÉÌGŒñr¼ÖTeìjìz¹†_‘öÜüÎÿ‚‚|)ñçüe¦]xWG’ý™1ÇZû'ᗇ﴿éú^· ŠQn¨èݲ¸"¾¼´¸´Ô"Y+ý@5CYÑ ¿€€6°ç^æiƒXŠjT·G“€ÅºSqžÌü]ý¡dÿÇâañ7áùÓu¾mªæ#'øJ×—Ù~жO‡m¿áo Ýj{G–.9=3É&¿f/­'²›Ë—¢ôÍjhºÊYʱË4g®TW‘—æî”ý•mK—F¤}¥=ÏÃÝ7öfý¢?hAâ/‰w“iúz^Ý·;ôl~•úÅà¯Øx? á;rv|¦'œü¸5õ±´½·Ý®×€+Îuÿ5£´Ð §ZÛ7ÂͯmEèa—b#Íìê‡ÿÿfoŒ þ Íñ3àÔìîîfhbR 9ϨÓxwâ¿íñ Í|#}¡ÜiS>®\dÙàƒú×ëœR[I¾<Ü+Ô4^Îõ1"H8à êÊsXÔ‡²©º#0ÀΔ½¥=™ø1ñŸöøˆ|7Œ*ý©e~9Û/‹<#:é¾$²S±À9'%¿‡ëà«?þÙ_þ‰ô{U"ýÒ\¯À<’{Wí¥ÜÖr,жàšõmR±Õ#UxÓÍÇ< ÕåØúu¡ìªnì$©>xl6fo"µ{&ÚÇäö¯½µÏÃvžd €ìymÔÚÊaœm#¦+›ˆ¯†vKÝ6ÂR¥^:îyí?û3è¿´†LÖl–Ú¼|©qÈ<žØ=}ëòu´ÏÚóö~¸“A±µ¹ñ·”ÈùGï1ì+÷GD×e²™RfÊ9é^£Øï¢ìGÜ9àõ°XŠXºzîyõèO /wcù¼Õ&ý¬þ=2ø~î ŸÚHq&üò;ýÖµú¡ûü´ø¡mÕ®ZóRº’F$òN‹=ëë¿øuc-sg¯Ðý+"¶Ü•e¯¶:¶¯*VG£G O õ>ý¹¾xÛÆ?¼7á­*Kûh”‡t<.dSü«íïø6òÃáÞј®!¡êQ]þ¯! mvã¡=kÑ#hæå©ï)ÒÆÓ×sÌýænÇ€0dÌo•` ~cüz·þ-vÎL’ÿZý~ñ‡VE{›qÈŠüý¡“Åe$o3ú×å\Y¯µÏÐxg²vÞÇêïÁn>hùç÷B¸oÿÅÊø»¡ü7‹÷š7…<½sYî¯?"ÆÙ¾§2²œ†P+?á¿Ä3Âc×u¦Ûk¦Ù¼ïÏÌBÚc€=IÙü ðÆ§¥xJox™1â/NÚµþzÆfÿSÏ!b‹j…ìs_µðŽ& ±+t¹WøšÕü—ÜÚ?2ÏèËën”º¶ß¥ÿWø\öê)«N«±€QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEËx³Æþð.žuOêÖúU·;L¡ï9öPM~fÚÁ>ê~.Ñþئ¡¢êº‹_éó^‡mštáV‚PÉ–@Yv÷Sšú?ö”ý˜âkMãoÈañ,q%¼Ž|›µAÀRÇÉŽ‡…?Å‚KWäö§¦j:6¡q¥jÖÒYÞZ¹ŽXeRŽŽ½C)äýg‚²<z2”¥Ï'nh½³ºÓwë{ZêÛŸ#žcñæ’V]ë±ú%û%|Køñ;âæµªxÓYŸPK}"_.B[ÄÏq D˜E8g#©¯ÑÊüÁým·ø«Å—˜ÿUeo뤄ÿì•ú}_)Çá ÂTéÅ$’ÑiÐõ²)JXu);¶ØQEò'°QEq¼k§|<ðv§âýHoK‰Ž!÷¦™ŽØ¢_wr}sÚ¹_‚þ Ô|#á6¿ñ)ó|Mâ9ŸSÕ¤ïö™ùò‡¢Â¸@ÀÁ#­qš¯ü]ŸŒÖúþ÷Ã//;¥Æ± >D^„[¡.qÑŽÖôzøÜPT~Ô­)z}•ÿ·?UØã§ïÔsè´_«ý>ð¢Š+È;Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  vßW»Ñï-´ İÔd–ÞâX¼äŽB8cWp™ÿ üGøï¢|_Ñ|c$®./¯q·¹v/m,yäÛ*ôÊ*©^ê ~çW-ã/xcÇúþñm„z…Œüíqó#vtaó#̤¾§…øeõo()EïüËÑþ‡•šå¿X…”¬×Ýó?žÊýçøÿ$kÁ_ö³ÿÑK_˜ÿeÏü)yµýÌÖ¼/’|ð¹žÔ‚áW·o0 §¸R@?§¿äø'þÀö_ú%këøó1£ŠÁQ«BWN_§^ÇÃøiÒ¯8TVv=RŠ(¯ÉÏ® (¢€ (¢€ (¢€ (¢€ (®+⎴‡>ºñ>³ºE‹Á|Ësq'Ãõ.çÇ$ð iJ”ªIB íìL梜¥²8¿‹ž<ÕôqaàŸÆ~&Ý <­œ‰o&넌gn~ópÁÙ|<ð‘ðçÂÖ¾Ò Kå“%ÅÄœËus'2Ï)ä–sêNà âþøXÒÍÿÄ/m—Æ^&Ú÷Xål­Ç1YE× Æì}æä“€kÚëÑÇU8ýZ“º[¿æä¶_7×Njr~Ö{ô]—ù¾¿pQEåaEPÂß?l _^ßx/À6FëZ¶- ÅÕÜlÛ¿B&¤aêpÞ+òÒâêâí÷Îû±œŠ¡˜± £FI8k÷kâÁ?|\°û?Šl@½vÃ}#º‹Óƒ¹Ù`WÛ<×凯/ÙƒÇÿ Ú¤QsÃé’/mæ%ÿ¦ñrcÿ{%?ÚÏû/æÙliû k’£Þïâ~OôÐø¬÷ ‰rç–±]º|¿SëŸØ'Pó<âM/?ñï©,Øÿ®Ð¢ÿí:û¿6` ý·^5ÒØÿ¬K”¸fVÿÐ…~“WÁñ­.LβôzGÐd’¾?Ì(¢ŠùcÕ â>!xóGøsá{Ÿjá¥òÈŠÞÞ>eº¹“ˆ ‰y%ÜñÀ8'€k¬¿¾³Òì®5-Fd¶µµ¥–YTHÐe™‰è&¾sðçÆ/Eñ‹ÄP<^Ó §†len‡ÔeCürc÷W‘Îú8 ,ezÕ~ïæúEy¿Á]œÕêµhC⇙Ö|$ð±¥5÷Ä•›Æ~% ÷Xå,­Ç1YC× Æì}æä“€kÚ袹±X™V›©/ø tKÉÒ¤¡TQEsšQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@~iþØß>+Ã,þ/ƒ\½Ö¼ íºK=ÛRÁ³Æè£ ÷d*XtsÑ›ô²¿=à¥?´Åïìåû<Ü È#ñWe}#Nr6èñ–¹¹ x&8þUô‘Ð@"½Œ7– «Æ)÷OôìüΗã?êéZÖqÕ¥Ô'±œ«Äv äAŠþÓÿeŽö´—À_ üYŠ4·½Ôà1j'݆þÙŒW  ä….¥Ó<ìeÍ|™ëŸEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÊøÃÆþð&½âýJ-2Í8 !ùºíYÛý•×äßÅ¿ÚN÷\ñî£â„ÆëÃ)¨Ù­…äá”Mz‘¶RFP”ê>UemáxÜ9úñ+á7‚>,iIñ…ˆ¢ ä\Æv\[³u1ÉÛ8S•8ùYñ›öXñ×ÂÃ>±¦+kþL·Ú CæÀ¿ôÞ!’ }r½É\â¿GàHåÜíW¼zYü-yw~½vGÍgïoq{«ªßúô9/ÙÓ_‡Gøåá­{W»XbYçk‹‰Ü*ªËŠï#±ÀbY‰÷5ì¿à°³¯Ã­jëÃÞÓoþ!\Ú­ufÑÚéÌààª\K—pûé!«0¯ÅÚ³â]å¬|9Ñæ1,±‰ïÙ +gË„û70ï•íœü5\> c©ÖÇrÓû ÏÖíþ7áê… Ë«¹ýx7þ }ðöþìEãÿ†ž‹n[n¢B“Ô¤±Útï†5÷ïÀŸ‹>ý§>'ê~j‹«xkÃ:e¾›dÅZ7[»ïßܳŠ¢…Œî#‘‘ÍõoìoûNxöXøÓ¥xæÂyÃ÷’Gi®Ù)%.¬¾s·¡–ù‘¡†Üífãðø—MNËV­é{_ðÓæ{5)sZýÏí>Š­gyk¨ÙÁ¨XʳÛ\ƲÅ"«£ÊÀ÷г\Æ¡EPEPEPEPEPEPEPEPEPEPEP_•ßµ'h¯ ø‚]_ÕÚË÷¥¾Å.–­mñÿuÜ.ð>ú4„wS“ú£\ÇŒ<áßø~ëÃ)³[Ý>ìa‘º«ŽÕ]z†Š÷x{6§ƒÄ*•i©GÍj¼×™Á˜á%ZŸ,%gýn=ŒÌì]Éfc’O$“_ªÿ°n›ä|4×uB0nõVŒ{¬0DGêæ¾&øïðÄ_õíºÿ÷ŽE¥ð_ÇÊ›,€~ 9^áE¿cm7ì?4«œcûBæò®&h¿ö~ŸÆÙ•*ÙR©FWŒšý_è|®E†œ1n3Vi3êj(¢¿>ä(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+ò£Ä?ðVŸþ øûâƒ^2Ðu=3Hðö¡>•6¼6ÍÝÚÈb˜½ª4B®¬©v å€xýW¯ÎÿÚÇþ »ðKö›–÷ŶJ|ãË€XêÖH+©Àûe¶Ue÷u)'L³€>éðw<#ñ ö~-ð6±k¯h×ë¾ »9–h\w‘‘ЃÈ< tõü‘jþý·?à™^7:Ö4ö:%ÔÁ~Ùkº÷Ãú˜*ÎŒ«‘‹Àd¡æ¯ØÙ[þ ¯ðkãgØü'ñ[Êøyã vƦâOø•]ÈxýÍËãÊf=#›B¬ŽhõZŠj²º‡BXdÈ Ó¨¢Š(¢Š(ÿÕýü¢Š(¢Š(®âWï |Zð6³ðëÆöŸmÑuÛv·¸;[ktdoáe8*{]ÅøC¬ÿÁü/.¥<ºÄË»{bbŽ{yOE,®c×>•—ÿ?Óÿè©Kÿ‚õÿã•ûåE~ÿÃôÿú*Rÿà½øåðãý?þŠ”¿ø/_þ9_¾TPàoü8ÿOÿ¢¥/þ ×ÿŽWæÿí¯û!hÿ±÷Œü=á¼G?ˆÎ»`×ÂU!òöÊÑmÁcŸ»œ×ö%_Ïçüà×ʼnü ©|=ð†©â;[MH¦–ÂÒ[„ŽCrìŠÁÎjüäý‹ÿdÍöºñæ¯à˜|Cqáã¥Xý·Îhmÿ8M¸ 1×9¯ÒÏøqþŸÿEJ_ü¯ÿ®cþ ð[âçÃož)Õ|àíWöw7•×ö’ÛÆòyÊv«:€N9ÅC4øÿ?Óÿè©Kÿ‚õÿã”Ãôÿú*Rÿà½øå~ùQ@¿ðãý?þŠ”¿ø/_þ9Z:Wü Ãqê¾µñ6êk%`dH,Q$eî33냊ýᢀ<÷áOÂïüø{¢|1ð—Ø4-&ÉÜÌY‹É#±ûÏ#³;žìON•èTQ@Q@Q@axŸÃñ¯‡µ ø³O‡VÑõhÞîÖáÅ4R 2²Ÿ_ÌG5»E~üAÿ‚SüløGã¹¾#~Å_ßH&;;«¹l¯¡Gl´ s´wp>Y‚p0ÛÈÉ/~ÿÁgügfþ×ý›£½—Ä–Þ!·º¹:”Z\Ÿa[K¤|Ë,ð+/˜ñü‰'??á‹ÿà«óéâ/ü+¬ÿùe_Õ]üªÃÿÁV?çÓÄ_øWYÿòÊøbÿø*Çüúx‹ÿ ë?þYWõWE*ŸðÅÿðUùôñþÖü²£þ¿þ ±ÿ>ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ßWMP˜C¯[ÀØÿ|ݨýkûñ:-ÿýp—ÿ@5ù‡ðAÖ/‹³ÈÝÿßB¾G?Ϊa«Ò„bš}Ö¿-O¡ÉòØW§RRm4º•#ö0ÿ‚ª²‚-Õçn¯a†¯›â(O–P½ùž½ ¾hÞ2×Èþu“ö4ÿ‚©\&äµñ ©ÿ©ºÏÿ–5Rçö*ÿ‚¢À¦[;_ǯü%vGùj¿¦/ø‚KYµÃd™¯OG†êðÊz×µƒÇÃv×<ÜNT%ïl&göJÿ‚•£`ÚëÁ‡ýMŸüŸVíÿe/ø)ÄŒ"(´ÿäúþŸü;â"„[\ž=ë©Õô{mZÜÉñÕêGíióÓIËÐâžÙO–£vò?•Hÿeø)|\žŸOÚý¿­«OÙþ “|3iˆdö7Zç¨×ôuwe5”†9AïëV4ÍRm6Ue'ozòhq HTä­½?áÏB¦M Cš”›õ?œIbŸø*tƒ3Xxþ¾-²?û‘¬;¯Øûþ gfv\ØëÈìi´?Êþ¿«m/T‡P„2·ÍŽ•­¤A¨BÀ®Ö¾†¼Ü©ûJ)?SŦ”gÉU´)qþÊðS¿ÕEâúxªÔíýmÚþÇßðT»ÕÝkˆdÞ.³ÏQ¯èòÿOšÆS‹…õ«:N¯.—:ª’P×ÏÑâ)§%h¤¼¿áÏ^®Sjm³ù¾—ö&ÿ‚¦ÉóM§kï_YýÈÖÿ²ü¾ÝŒwzò°ì|QiÿÉõý_Xj0_®‡'“®èê1™cJ¼æ½üD¦áí(¤ýQåPŠSåªÚô?•ˆ¿e?ø)”XÅâÏ§Š­GþßÖå·ìyÿM¼@öðx…ÔÿÔÝgýuþŒî-å³”Ã:àÖ¦­K¦L$Æz­x8n"šš…h¥èzÕòx8óR“~§óu/ìQÿMfm?Äõñm‘ÿÜaÜ~ÈðS+FÙqe¯!ÿ±¦Óÿ“ëú¿±¾†ö’3»=k']Ñ£Ô!,‹‡ïâg'z)7æxôRSå¨Ú?•h¿e?ø)¢ÇâÏLx®Ôý8Vì±§ü>é2–¾!‘OýMÖ×Q¯èÆæÖK)<©† ž t¾פ´”C;|‡¥x˜LþN§²­½W”ÅCÚRmŸÌÝ×ì]ÿ@´îtíy}ÿá*³?ËP5”¿²wü¶#”·×ÐOÚý¿¯ë$ýžúH ­^_â KG3À>\ç»ó õ©¥R”S^ŸðN<*s|µLþfí?d¯ø*çËkˆ[ÛþÛAüõZ/ûÁT¤_ÞYx…Áõñm‘ÿÜE–÷2ÛH%„•eçê>ñwˆ!™¾qëYeùäk>I«3LnXé{ÐwGò×wûÁNìFë­?^AÿcM™þZ…PöOÿ‚–§Íúã¸ñM¨ÿÛúþ±ï-"½„Ç È=+Èõ­]:rÊ3«Ìqè»ÓŒZôÿ‚N •*žíI4ýOæ²ÓöFÿ‚¢ßqkˆ_þæÛAüõ ±7ìOÿKa¾m;_lw>,²?û‘¯èÖÎòk)ÖXI¯[Ñu¸u(‚;aÇ\ÔåÙ¯îÍ$ÇËeKÞ‹º?•)ÿd?ø)}³ìžÏ^F¾(´ÿäú|_²ŸüÑH£ñ}ž"ÿºÏÿ–UýT¯zuwœÇò©ÿ _ÿXÿŸOá]gÿË*?á‹ÿà«óéâ/ü+¬ÿùe_Õ]üªÃÿÁV?çÓÄ_øWYÿòÊøbÿø*Çüúx‹ÿ ë?þYWõWE*ŸðÅÿðUùôñþÖü²£þ¿þ ±ÿ>ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº+2åSþ¿þ ±ÿ>ž"ÿºÏÿ–U¥£þÅßðTFÕ¬×\¶ñ2iædûA‡Å¶M ‹pß´H ã8¯êfЍÊÍ15ucùwñìÿ=ÕüM©j^ðþµ£iÓÌL6öÞ)°…B•YÂj]ŽI$ó\çü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ} ªž-²UP:¢Jþ«h§Ìía[©üªÃÿÁV?çÓÄ_øWYÿòÊøbÿø*Çüúx‹ÿ ë?þYWõWE!ŸÊ§ü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeAý‹¿à«`Úx‹ÿ ë/þYWõWE,IûÿÁRbÑ£Ñí¼9¨Ú´Ry‚êßÄzlL¸ Ç$Ñê Ò.N~|GZ£ÿ _ÿXÿŸOá]gÿË*þªè­«â'UóT•ÞÚ‘ qŠ´UåSþ¿þ ±ÿ>ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº+Ïæ?Ã_²üŸIðöµ.± kÚγt‹og ç‰ôùìâGÉ’vI5"àp Ï=¸$ý‹àªÑ¨D³ñªŒ<]d°ÿ‰•U´WE\T¥Ó{GõÝúþ‰Æ’RrêÏåSþ¿þ ±ÿ>ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº+œÐþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}$ü=‹ÅŸ³ýž°|%qs2#ÙëÖúd/ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}iï¨x§Â–ÆÛR² M{¦+âUåå·fc´ º1Ç(ª™R ’¬0Gr“nìI[D%Wè_üßö5׿iߊ¶:ïˆ4ù#øqወî5[©ˆ®Þ","'‡iN<Ü}ÈÉ$†( ëü?ûÿÁP¯´6÷@±ñ z]Å´2Z*øªÒ°:6 ¬€))PW¡­øbÿø*Çüúx‹ÿ ë?þYWõTPFàK@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1ücþ}ž"ÿºÏÿ–UýUÑ@ʧü1oüI™ ún»rˆÊÞ\þ*°š&*rG& ÊÃ#£zÑÕ¿cÏø*~£¨K{g¡ëLRà‹[ØÁmåÆšˆUäàw&¿©ª+o¬OÙû+û·½º_¹Î<ÜÖÔþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªº(ùTÿ†/ÿ‚¬ϧˆ¿ð®³ÿå•ðÅÿðUùôñþÖü²¯ê®ŠþU?á‹ÿà«óéâ/ü+¬ÿùeGü1ücþ}ž"ÿºÏÿ–TÃÿÁV?çÓÄ_øWYÿòÊ¿ªºü[ý³¿à¬gÂok¿þiK¬x«Gž[íZùOØl®ab’Ç †žHØ%ŠÆ¬:H(ò[ãwÀoÛ»á7‚î5oŽ·úŽ•áë°bhµYN.ºv¢þI'=Ê"1Ç$b¾Ó´ÝGX¿·Ò´‹Yo¯näX¡‚ie–G8TDPY˜ž“_¦ÿcÏÚ÷öüñZüTøªÝØèWäñ¸ù“š} Ù½NÑŽÈýþÌÿ±'ÀÙnÂ9| £‹ÿ2l¸×u³_É‘†¶ÁþäAAÜXŒÐÈßðM‚¶—ÂÍ >3x‹û+Àm,¼/©)¼Ô!Èùpû ”Yû†Š6ù«õΊ(¢Š(¢Š(ÿÖýü¢Š(¢Š(3[Õ#Ñ4kýfd2%„Îʼ)b{œWâÏü>ëá7ýoÿ-¿øªýžñ.™.µáÍSG‚I}k< ÍÐ4¨Tí“_Í×ü9Oã¯ýzýó?ÿ@SÿÃî¾Ñ8Öÿð"ÛÿŠ£þuð›þ‰Æ·ÿßüU|±ÿSøëÿCž‡ÿ|ÏÿÄÑÿSøëÿCž‡ÿ|ÏÿÄÐÔÿðû¯„ßôN5¿ü¶ÿâ¨ÿ‡Ý|&ÿ¢q­ÿàE·ÿ_,Ôþ:ÿÐç¡ÿß3ÿñ4Ôþ:ÿÐç¡ÿß3ÿñ4ú“û$ÿÁF< û[|J¿økᯠj:Õ†•6ªÓÝË ÆÉ ÐBPÉ;‰œL[µ÷íÿà¿ÙꄼOámC^›]±kä’ÎX‘QVV‹k 9ÊçŠùûöÿ‚xüJý“¾3jŸäWCÿý‚>"~×>>ð¿‹<¯iúM¾‡¦=”©x$,ÎÓ4»—`#lPªþÈÿðP¯þ×5Õ|ᯠê:ú]ŸÛ[¹ateÞh’sÍEû[ÁD<û$øïNð‰|)¨ë·’ެ֒ˆªÎÉ´‰9ùkÈ?`/ø'ïÄÙ7âV¹ãOëúv«mªißcHìÄÕüÀù;ÀÀªÿ·×üãâGíañKHñ׃üA§iVš~š¶Oà¹u‘Ÿ#`#jóÏø}×Âoú'ßþ[ñTÃî¾Ñ8Öÿð"ÛÿН–?áÊèsÐÿï™ÿøš?áÊèsÐÿï™ÿøšúŸþuð›þ‰Æ·ÿßüUðû¯„ßôN5¿ü¶ÿâ«åørŸÇ_úô?ûæþ&ørŸÇ_úô?ûæþ&€>§ÿ‡Ý|&ÿ¢q­ÿàE·ÿ_²¾ ñ$2ð†‡âûXZÞrÆÚù#r "ÜIJ…b8È ƒŠþpÿáÊèsÐÿï™ÿøšþ‹¾xrëÁß¼1áÙkK²±•Ó;í H™—<à•È ÊŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( =E-&3Fr)\ q šEòðÈ?55ùUðüO¤|\–Û¼ ÿ} ýa¾ŒËi~ÃÇ&:0¯AðçˆTgt~nÆ™â/ÆæØsÖ¸š'ÏÝu?ʾ[’¾ª²ÐúÝâ©êÏgÕ4¨5;rÊìpkÉ/¬'±™£|µÞøoÄ+* kƒ† èõ2ÛQˆå~b:×¹ŠÁÃi©åPÄTÃO–[G¦êréò««|¹¯\Òµx5ƽy>¥¥\iÒ•*Jg­V±¿žÆu–#òçšòpê¸iû:‹CÐÆa)×<7=WÒa¿·q^G¨iòØJba•ÏëZFµo¨Dx:Š~­¤Ã©BxÃú×¹ÀCMNžV:å™å:V©q¦NNS¸¯_ӯῷFzŠñ½CL¸Óæ+0ÊzÔšV«>Ÿ0ÁÌdŠñ²ì}l4ý•E¡éã0P¯x=OH×48µX Ä£½yEÍ´Ö’´2˜W·iÚŒ7ð‡B ¬ÝkBƒQC(\H;ׯšeQ¯kHó°8éR—$ö<ÿ@ÖŸNFNP×­ÚÝGw’3kÃ/,¥²• ˜`g­lèZìútÂ'9«ÎËsÑš§Qhwcð*¢ö”÷;ísB‡PˆÈƒ+Én-嵜Ç/Oï×1ÝD3ÕÌxƒ@KØÌ±œzW£›eЬ}­-Î,·éËÙÔØæ¼;âŵÉù{W¥ÕÔæR‹ö5–‡Fa¿ï)¼Aáù-d3[ r‘LöòoŒ•uë^÷$q]@Ca•…y~½áé-¤2Û.Tòjsl¦Tß¶ ^_R^ΡÓx{Ä1ÞF±L~lWMwg ì&9TŠðˆe–ÞA$_+)é^¥áÿGv¢Žq]™Vf§e[s›ä~Ҟǭè³iÓŒ|†²¬ï$´™eˆGZ÷ËXo­Ú ^G­è²é²– ˜¥yù¦W:oÚÑ:ðåR>ΡèÚ&µ£ £ÞF·¢C¨ÂYF$ëɬ¯$²fˆü§µzþ‘«ÃŒØäW¥—ãc‰‡²©¹ÅŒÂÊŒ½¥=»³–ÎS £ŠÓÑu¹té•Y³<פëZ4:”lBíw¯&¾Óæ°”Å0Àõõ¯#‚«„¨§OcÓÃb©â!Ë5©ívw‘_@$B=k•ñ‡RåLöãkõ®?EÖ¦ÓçUc˜É¯[µ»†ú’"µîa«ÒÆÒpžŒñêÑž§4v<*he€´rðâ¿?>'»ø£eÑÊùÿã_ª~ Ð#¹…çˆÀ_‘¿õ˜‡Åª7=¤ä0údWç\Yƒ©‡qOk£îxwN»vÞÌýuð$qCá{áû¢5®Ç¨¯øcâY_CÓà›îHƒÒ½ÕuÜ:ý7"ƪÔ"Ö–GÀf˜yB¬¹‡ôêAÒ–½³Î (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€<'ã_ìÏð3öˆÓá°ø¿á+]y­F ¹;༄g;c¹£™TžJÚ{ƒ_ÄìqÅyü2ø/à›/ ü/ð埇töŠ'•m“M&ÑóÍ+fI_ý©›gë5VÅ VVñ°ÁHÐé€*ÕQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEù ÿ<ý•~ÙÿÇŸ´Ÿ„m´ïi‹bé¨Y³Ûy¯qo<1²Ã+²HÀ»¡~Ÿ7¿^kâø(÷‡|EâÏØ¿â7‡¼'¥ÝëZ­ÚébK$¹¸—f©hí²(ƒ;mE,p8“À4üÍ~Â_ üñoö±øðû⚺¿‡õK‹Ãuhîñ¬¢ÞÊ{„ Ѳ¶7Ƥ€p@ÁÈ$WöSáÏ xwÁú%Ÿ†¼'¦[hÚNž‚;{K8R!Aü)ª>‚¿–ø'/ÁŒÞý´>kÞ+ð¿¢év’jfk»Ý.îÚÞ=ÚeÒ®ù$Uw1 2y$¦¿«š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÅñˆt èWÞ%×ÄÓO!íE< ’{Iâ¾ø%ûQx/ã>­áûxFÕ wk[{‡Vk«eèè@x¼|àr HúRæÚÞòÞ[K¸–x'VI#u ŽŒ0ÊÊx Ž=kò_öý›5„Z¸ø¥ð°Í‡ Ë;, Þv™(9 ¬>o+?uº¯FãüæÅá¹qW4ĺۿËþM¿Uð×!É3e[-ÇÍÓÄNÞÊw÷oü­w~{ì¬÷ýn¢¾Cý™i­?âæŸ…¼RñÚx¾Ò>G—ȃ™bË ÿy~\…±ûKþÒúoÁý5ü7á·ŽóÅ÷‘åá’Éq,£»¨‡¯Sòà7_öîê¿[æ÷?ö·#Ã~fë7þÄö/Û_åoæ¿òùü·ÐÝøáûOx7à­ýމqk:½Ã£Okªµµ±<Èä‚7‘Ê!Æî¤¨Á>ùá¿é^-Ð4ÿhr™ôýNž*T²8Èʰc_–ß³oìá«ü[ÖOů‹[iŒñÇpI—S›9.äóäƒ×ûýËšý`†­âH EŽ(Ô*ª€T à: äÈq˜¼O6"²åƒøW[wùÿZoíx‘‘d¹R£–`fêb!k;û·þT»ÇËmÞÒQEôgåEPEPEPEPEPEPEPEPEPEPEPEP_Iû~Ë?µŽZ‚`ÔšÌǽã%—'¥|·åî¾VÝ÷cT\¶&øãxüeà;+¹å2„wÂà õsA‹Pž1‰+ò'á¯Äÿ|ñÙêhëb€‡8Æzô¯Ò/ü~ð7Š-i/¢µyJ»…çñ5ãdÜCGKÙb]š=<ã%­‡­í(ê†ÞX\YHÑÊ¥@é]ÿƒ¤¸dÚùØM.µá]Æî Ô8ÿ»k®øjÑAuþÐÿôð9|)Öö‘Ÿºyø¼\êÓä”5:—UuÚFA®^ðäs!š†ö­¯øJ4ȽþúãHÞ'ÐHÚo"?ð!þ5íâþ¯Z<³hòðîµ9]&xü‘Oo(ŠE*Àðk×¼4gþÎO?ïÅeÜj¸I%ÄE‡ûCükR/øzÂ%ä@Û‡ø×•—`aB«—:±ß¯*°QågBÈ®6¸àלø‹ÃÀsj¹#°®£þ­ñöØÿï¡þ4Çñ/‡¤Rwïñ¯K¨V¹‘Ç…UiJé3ÈíüÕºE@CƒÈ¯pӼƶŒÉ×Ê­ç„oµäZ¦—.! ÏZõOøJtÿ/‘Ÿøÿ£u¬øbèm–ê#ÿŽgƒ£Y]I\¬z´´qg1á 'óøÊìkÕ1\}®±á‹>!¹ˆÀ‡øÖ€ñN…ÿ?‘øÿìË%N•%Iã#:’æQc5½B3ó×’ÞÙÍcqäȤzõãâ?ñûýô?Ʊ5-kÁ²‘%åÜ#Ëýkƒ5ÀQ«ï)$μ*­=[Bø9îM³ AÇjî@ϸ+ox2Î=êVá=¤_ñ«ð°<#ù AÿükÐÁbhÓ§É)£‹‡œç΢ÆxC[µ2¿?µyƒE,7 ) zü'¾aójpßÅÿÇ“Ä~’_4ßÛîëþ±ƼŒ~Œä¥ Šç£„ÄT„\%uú JlQfêzÖš$•J8Íq±øï¨TÔíÀôÑƦ>ðÿ˜¤÷ñƽºº1‚„¦™åÏS›™E˜!ðÛÂMÍ¨àœœW)aö¶(‹*êpEz<ž:ðt¨UµKrý4_ñ¬„ñ'€¢—ÎKë}Þ¾bÿ|þ+/¢ê©B¢±êÐÄÔPå”=ı¶Bÿ{Óol⻄Ç*†Ís)ãß*íþÔ·öÑÆŸÿ ÷„ÿ¤÷ñƾ‹ë”9ÑäºT¹”YÃk:3é³@Lf¨i·ÓY\‡Œ¤Šînügà«´1É©[œÿ¶¿ã\Ü:‚!¹óN­¶õÿùlFœj©R¨v–*NŸ-H³Ölåi WaÔVn±¥AO˜Ž¥T¶ñW‡L@Å}ß÷‡øÔÿð”h'¥ì\ú°ÿúmJTùg$Ï S©óF,ò[û tÙÌR´ô­ ZŸOTœ¡í]~·¬xN{f{›èW÷¯ø×Ëþ1ø½á »¬QÝíÏ ÀŸÐ×ÃãêÃ.xÌúœ ..2ާÓþ0ñu–‡á[­nBD‡#ð5ø§ª\·‹þ$Ý_ØŸøýœ²ý &½Oâí «xÊÍ´M0µ½‹pËÏ?Aû9ü3Õ¾ðÿƃÞ-Õ Ð|+ã­YÔî·y6¶Z­ÌòlRí²8äfmª¥ŽxéUüƒÁ-WwíÓðÜÿuu“ÿ”‹Á_×ÍQEQEQEQEQEQEQEQEQEQEQEQEQEðAu–·1¬ÐÌ¥VV*ÀðAjZùö˜ý¦tï„:søgÃ/ç‹ï#ùTá’Éq,£¡r9D=~ó|¸ ÇÇRÃRuk;%ýX÷xo‡q™®2, 9§/¹.­¾‰wýOƒj‡Þø/ñFÒçá®±öYçÿMû.Âm2PC&×ó¹w(ü¥I‹öfð'†þ5|W¸Ÿâf³ö™ãÍïÙ'v3j’ä–ÏU\nqÄt WÒ?gÙ¯Xøµ«ÂÖø°f›F¸”Ü$s–óµ9IÉw'‘zž¯ÐqÍ3ö–ý›u_„ú¿ü-_…žl:,S,òG"]2`Ù„säîè€ðxÁ¯É^YVÿÚ^Ç÷\×äòïoëÒÇö´x³Ëþªý}ýwÙò{{/Žÿ6÷éßûÞÐýe‚-`ŽÖÚ5†T""ªª£T€K_!þÌŸ´Í‡ÅÍ:? x¦D´ñ}œ0áùs,C p9tï/Ë¿^WëX u,M%V‹ºÕâ®%áÌfUŒž YÇîk£Oª}ÿP¢Š+°ðBŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¦í§Q@ Š‚âÖ;˜ü¹€e=AUš*g$âÊRiÝ-âïÞ ñpy/,¢óÛ£~•ó·û_]^ô]rÞÊ<ä)Gãò¯ÐZ+ç1%ªï(}Í£×ÃgøªJÑ—Þ¯ùŸÚ~Ë,­ÖÚßÅö‹ôýÜ¿ãVìÉñSþ‡L×9ƾ袳\JÖøÿ3£ýgÅÞ÷_ø ÿ#á_øf?ŠŸô8Ú߹Ɨþâ™á¼ciÿ~åÿû¦Šoƒð6µŸþÿÌ_ë>/ºÿÀWù Ã1üSŸð˜Ú߹Ɨþ“â éãOû÷/ø×ÝR\]þÿÌbŸU÷#ácû2|T<ÿÂckÿ~åÿCû1üT+ãAÿlåÿûªŠê~³ÿÀŸù‹ýeÅw_r>?³Å<äxÆÓþýËþ4¿ðÌ»øÂÔÿÛ9ƾ袗úìÿð'þcÿY±}×þ¿Èø_þ“â¦?äq´ÿ¿rÿðÌŸ?èq´ÿ¿rÿ}ÑE7ÁøÏÿæë6/ºÿÀQð·ü3ÅAÓÆ6Ÿ÷î_ñ£þ“â¯ý6Ÿ÷î_ñ¯ºh£ýPÁ^öøÿ1¬¸®ëîG¿ðÌÝñ¯ýû—üißðÌŸ3ÏŒmHÿ®r×ÝPø?µŸþÿÌ?Ö\Wu÷/ò>ÿ†cø¨:xÆÓþýËþ5RëöVø™{•qâûB¿õÎ_ñ¯½(¤ø;÷Oÿæ5ÄØµÕà+üÏqûøôÂ[j@íåËKÿ }ãã×Å–Ÿ÷îZý¢³ÿR2ÿåøÿ2ÿÖ¬guÿ€£óãþÿÇßô6Zß¹i?á|{ÿCm¯ýû–¿B(¡pNù_þ/óõ«Ýà(üøÿ†=ñæ?älµÿ¾%£þûÇ¿ô6Zÿß¹kôŠ?Ôœ¿ù_þÿÌOŠq}×þÏøcïÿÐÙkÿ~å£þûÇßô6Úÿß¹kôŠk‚pÊÿð)˜ßã;¯ü‘ùïÿ }ãßúmïÜ´Ãx÷þ†Ë_û÷-~„QIpF^•¹_þ/óú׌µ®¿ðþGç¹ý|{á-µÿ¿rÐcßäâÛlö%¯ÐŠ)ÿ©8åøÿ2bßUÿ€¯ò>‡ö]ø¡n‚8¼ahÓ9Ƥÿ†bø©’á1´ÿ¿rÿ}ÕEh¸;´³ÿÀŸù‡úÏ‹î¿ð|yû+üK¾„Á7‹íJž¿»—ük“öñ¬d—Ķ®Ç¹ŽJý"¢²«Á|þ(¿ü ÿ™T¸« òµ÷#óŽ×öÖ¡¹If×ìä@A#Ê“$WÛ¾é^ÑbÓ4øã(cªãqõ¯D¢»2ÎÁ`çÏB~m¿ÌæÌ8ƒŠŠ…i]zX`SNÇ¥¢¾‚ÚÜñD´QLŠ( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(  «lÒïû°È%5üØ.ûëdþôˆ?2+ûÝÖÖFÑu‰K¹·”*¨É'aÀw¯âGKý›?hˆõ[37ÂßF¢hò[D¾ Ã$“ pTQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEùáÿSm¿°çG÷¦ÑÇþTí~‡×À?ðS¯øƒÅ?±§Œ´? é—ZÆ£qq¥¶³…î&p—ð;Ž0Ìp'€3@ŸðJõÝûrü=?Ý‹X?ùJºýx×ò©ÿÇøMñKÂÿ¶‚u¯ø;YÒ4ûx5o2âóO¹·… éס¤‘FIdòN+ú« Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( sÅðøšãÂú¤> žmqíärÜ)h’b>RÀ{û$ÁüÚøû'x«Å¾-¼ñ×Ç{yÒ.¤citÛ¦¿¸Vù¤”äþç=yÛäå¿R(¯#0ÉhâjÂ¥kµª>ㆸÿ”àñL ¢êÚó·¾’Ý'Òÿ†êÏR8aŠÞ$‚XâBª(U@À ¦Ü[ÛÝÛÉkuÍÊQÑÀeua‚¬Á¦¢½kt>'™ÞýOÊß²Œ¼ã{| ·¸žÒöí 0Û1i×%²¤6x‡<‡' ÑŽ0Oé¿…!ñ·†´È<_q Þ·¼ky-º”‰æç*lû õAEyvIG V¥J7J]:/D}ÇxƒŽÎ0xl.9):WJv÷äžÊOËñÝÝêQE{Â…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(’˼Ó8Ž8Áff8 $’z^?íû>ÜH±[üNð¼®ä*ªëVLI<›’k¾ñ£lðvºÿݰº?”M_ÂO„—ŠôTþõí°üäZþö袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+^ñ‡ü-¦É¬øŸSµÒ4øŠ«ÜÞL–ð©sµA’Bª ''“[ùµÿdm¿±GŠG÷¯ô‘ÿ“‘šû«Bø¥ðËÅŒzG†|]£ê÷ò†)ogoq3˜„ÙŽ$àp+»¯ä“þ <»¿mŸ îÙjÇÿ$¥Ö¿­º(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(‚ø©«éþøaâý{WmltÝP¹¸•¾ìqCnîìq“…PI¯áƒÂw6Ö~)ѯ/$[Á{o$Žz*,ŠXŸ ¯íoö³ÿ“WøÇÿboˆ?ôÝ=_þø·âgˆ-´O h÷úš´ðGs%¤·_fŽw æH#hŸ˜€py ëßþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôsþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôsþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôsþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôsþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôsþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôsþ ûÑWÒò7ÿ£þ ûÑWÒò7ÿ¯Î?øqχ?è¯]ÿàš?þK£þsáÏú+×ø&ÿ’èôB÷þ 'ûØD&Ÿâ®œÊ[n"ŠêfÉû±ÂÇuÆ)ÖŸðQضöqÅ]5T’1,w17ìÉ ·é_„Ÿ·üIý~é?¬ “ıɥÛj?j’ØZ°7*lØ$qåç;¹ÏJþδÿøð¶ÿ®Iÿ Š·EP_˜ŸðW]WOÓÿcMVÎòu†mOWÒí푎 ²¬¦r‹êDq;}×éÝ~?Ák?ä×<)ÿc•—þ›µ üwÿ‚l|Dð7ÂÏÚÃÃþ3ø‹­[xDµ²Ô’K»·Ù¼¶Îˆ õbp+úUÿ†îý?è­h?øÿÖ¯å»öFý“üEûR|NÓü%Íç†t[û{¹¿¶†œ÷–Êö¨[Ëûð¡,Fßõ™±é_©ÿðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëQÿ ÝûÑZÐð'ÿ­_—¿ðã;_ú-ÿ„ðÿå…ðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëQÿ ÝûÑZÐð'ÿ­_—¿ðã;_ú-ÿ„ðÿå…ðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëQÿ ÝûÑZÐð'ÿ­_—¿ðã;_ú-ÿ„ðÿå…ðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëQÿ ÝûÑZÐð'ÿ­_—¿ðã;_ú-ÿ„ðÿå…ðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëQÿ ÝûÑZÐð'ÿ­_—¿ðã;_ú-ÿ„ðÿå…ðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëQÿ ÝûÑZÐð'ÿ­_—¿ðã;_ú-ÿ„ðÿå…ðã;_ú-ÿ„ðÿå…~¡Ãw~ÇŸôV´ü ÿëVeïüö2°”C?Å}%Ø®ìÅçL¸Éz8ØÇLæ¿4?áÆv¿ôZÿ áÿË ü»ý¶e8ÿcÿŠšWÃHüN|X5= _íFÏì;<ë››+Ëó§Î>Ï»váØÇ Ô*þÞ±Û(añkBÁæãò"—þ»ö<ÿ¢µ ÿàOÿZ¿%¼ÿU¶ñƒtŒ/hu» [ï'û?•ö˜–]›¾Þ»¶îÆp3׺øq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ¨ÿ†îý?è­h?øÿÖ¯Ëßøq¯ý‡ÿÂxòÂøq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ¨ÿ†îý?è­h?øÿÖ¯Ëßøq¯ý‡ÿÂxòÂøq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ¨ÿ†îý?è­h?øÿÖ¯Ëßøq¯ý‡ÿÂxòÂøq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ¨ÿ†îý?è­h?øÿÖ¯Ëßøq¯ý‡ÿÂxòÂøq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ¨ÿ†îý?è­h?øÿÖ¯Ëßøq¯ý‡ÿÂxòÂøq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ¨ÿ†îý?è­h?øÿÖ¯Ëßøq¯ý‡ÿÂxòÂøq¯ý‡ÿÂxò€?P¿á»¿cÏú+Zþÿõ«ÁC¿bãvlájéžb±Lì¹òò:âO+a‡8=~kÿÃŒíè´?þÃÿ–ù) þÎñkµÄŸ²óëÍqøªïÃGUÀ’-n¤¶ûGÙüÎ7lݳÌã8ÜzÐõGÿ õûÿÑXÑïëÿñÃ}~ÆÿôV4_ûúÿüE~hôŸú,sÿà‰ù6øq¶“ÿEŽü/ÿ&Ðéü7×ìoÿEcEÿ¿¯ÿÄQÿ õûÿÑXÑïëÿñù¡ÿ6Òè±Ïÿ‚%ÿäÚ?áÆÚOý9ÿðD¿ü›@¥ÿðß_±¿ýþþ¿ÿGü7×ìoÿEcEÿ¿¯ÿÄWæ‡ü8ÛIÿ¢Ç?þ—ÿ“hÿ‡i?ôXçÿÁÿòm~—ÿÃ}~ÆÿôV4_ûúÿüEðß_±¿ýþþ¿ÿ_šðãm'þ‹ÿø"_þM£þm¤ÿÑcŸÿKÿÉ´ú_ÿ õûÿÑXÑïëÿñÃ}~ÆÿôV4_ûúÿüE~hôŸú,sÿà‰ù6øq¶“ÿEŽü/ÿ&Ðéü7×ìoÿEcEÿ¿¯ÿÄQÿ õûÿÑXÑïëÿñù¡ÿ6Òè±Ïÿ‚%ÿäÚ?áÆÚOý9ÿðD¿ü›@¥ÿðß_±¿ýþþ¿ÿGü7×ìoÿEcEÿ¿¯ÿÄWæ‡ü8ÛIÿ¢Ç?þ—ÿ“hÿ‡i?ôXçÿÁÿòm~—ÿÃ}~ÆÿôV4_ûúÿüEz¯ÂÏÚ+à‡ÆëÛý;á?Œl|Os¥Æ’ÜÇhìÆ$•Vl¨àE~üVÿ‚5é ~xÃâ4&Ôº6¡ª‹c¢¬BscnóˆËý±¶ïÙ·vÓŒç¥Wÿ‚ ÉFø¡ÿ`«ý%FtQEQEQEQEQEQEQEQEQEQEQEÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯Åø!ïü?ì¦èé¨ú)¢Š(¢Š(¢ªß_YivSêZ•Äv––±´³M3ˆãŽ4gwbª’IÀñ·ÂÿÛãöøÏñÊ€ß ÍN'œjpÀƒJ[dÌÒ‰_ÀaFê¬W€>Ó¢Š(¢¼_ã—íðŸösð|ž4ø¯®Å¥ZËoýåÝäª3å[@>i¦qò®rì«ÍRý?hO~ÓŸ aø§à Kû-&{™í=J(¢¸ó-È JÅ,Ë´ç›>€=ÖŠ( Š( Èø-_üšÏ…¿ìs±ÿÓv£]ŸüóþLöûêÊ*ã?àµòk>ÿ±ÎÇÿMÚvðGÏù3Øì?©ÿ(¨õ.Š( Š( Šà~%üRø}ðwÂ7ž:ø™®ÛxD²=ÅËcsHŽ4y$l|¨™»^;û1~Ö¿ ¿k-_×~ÚjvÖž»K9›R‚( "VˆG4¤©ÞÚ}¨ê (¦I"D,¬ÌN©'Ò€E~gx§þ •ðBÓÅ:‡…>x[ÅdÒ[WžÓÅÅ’Hb²<ŠìGå¶2®G5ïÿ³Wí£ðKö¦}GLøsy§x‡GV{ÍV€[_øŒÉµ^Hݤ£±R@p¤Œ€}eEPEPòÁÿÿ“¹µÿ±kNÿÑ×5ýJiÿñámÿ\“ÿAüµÿÁd?äîmìZÓ¿ôuÍRšüx[×$ÿÐE[¢Š(¯Çïø-güšç…?ìr²ÿÓv¡_°5øýÿ¬ÿ“\ð§ýŽV_únÔ(Ö?à’ÿòe~ÿ°Ž­ÿ¥o_¥5ù­ÿ—ÿ“+ð×ý„uoý+zý) Š( Š+ø—ñKá÷ÁßÞxëâf»máýÈ|÷-ÌA"8Ðe䑱ò¢fì wÔWËÿ³íkðËö²ÑõýwᦧmiáÛ´³™µ("€Èò!uh„sJJ‘ýí§Ú¾  Š+ãŸÚSöìýŸ?e«›}â©>¥¯Ümoì!#º¾Ž&̯$QħŒyެÝUX@ØÔV?‡µ«Oèoˆ¬ÒÛU¶†î%â9ÐH¡€$ó‚F{šØ Š( ¿˜/ø-_ü7…¿ìL±ÿÓŽ£_Óí0_ðZ¿ù:o Ø™cÿ§F€?£/‚ßòG< ÿ`/ÿIc¯K¯4ø-ÿ$sÀŸöÒÿô–:ôº(¢Š(¢¾hý£¿k‚?²Ö‰©ñKX+}v3i¥Y*Ï©] à´p–@s™$dLŒnÝ€@>—¢¼çáÄÿühøkáÿŠ~†æÛIñ%°º¶Žñ;…‰‘cy;9õèÔQ\OÄ_ˆÞ øMàÝOâÄMZ-@Ò#\ÝM’ª ªAgvbUAf$ ¯ÍÙ?à­_^Þoi ³@Q@&>ÿ”³\ÿÙSÕ¿ôç=YÕü˜ø#þRÍsÿeOVÿÓœôýgQEQEQE|Cñþ û8üø¦|'Õu+¯øžþê+9­´XâºÌâ4R<±"6ãÊ+4Š9dûzŠ( Šùö’ý®~ ~ÊúUçÄëùäÔumß`Ò´ø~Ów°€Å#,ˆª tŽŠO“Å|·¦ÿÁU~ Yk6wÅ/øËáÍŽ©&ËmKZÒ„v…z†r’4¸ÿ®qÉŽ¤ãšý@¢©éÚ†¯§ÚêÚUÄwvW±$ðMŽX¤PÈèÃ!•”‚àŠ¹@Q@ûSɱü^ÿ±?_ÿÓ|õø‡ÿ@ÿ’ñCþÁVú>Jý¼ý©¿äØþ/ØŸ¯ÿé¾züCÿ‚ ÉFø¡ÿ`«ý%FtQEQEQEQEQEQEQEQEQEQEQEÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯Åø!ïü?ì¦èé¨ú)¨®. µ‚K«©aRîîBªªŒ’Äð’jZüWÿ‚‘ü ý¤ügñ3FñU®‘®øïàu…¬Úžðö¥ö[§ž6Èïn#‘¤ÎP†X¥!AÄNàôÏÅßø)7Ákð|%¶½øÅ㙘Ç•á¤71GgºEt#×ÉY˜wQZß³Ã~Ý^?øÄßÚi>ðBZΖ¾²Ä×$ ¦º›÷„ãÍõ‰ qŸ±7ÇŸØ>}2?|²²øuâ)1 ƪÄ,õy¥Sq39»pAÀHêô¯ÒÊù‹ö™ý™ôïÚLðç„|Uâ]GGðž›z×z®§¹ˆêʪPM&p±«üÇåcýÝ­†Ÿžøsào…?ðUÝÁtK_èva­¢@H“.Ç–wle‰f<±'šý ¯ÉKÿùLF›ÿb#)hõ®Š( €|]û|4Ôüñö†ø­uqãÿ]ÛÝÉ£Úê¼Óô[d¼¨­íÙYåƒ7ÊîTVË>ÿ‚CÉ›ißöÕ?ô5¯Ñ?È‹â?ûÞè–¯ÎÏø$7ü™¶ÿa­SÿCZýøŸ¢xÃÄŸÖ‡‡TBO÷¾Îp>XÚ¿Zk‘ñǼðÿ×^$ø‰­Xè:$*VkBhàƒ~Bd 1nFKtÐ ðoö‰ø+ñÿHþØøIâË-}QCÍoùw–àÿÏkiÍ<ÈìM{M6ŸSàíñ ø'Ã~Oˆv7*Òx¯Ã¬únÉwV €°ÏÎ~ÊÄÛ¤)ýðø ¢üIðïÁÏèuÕ|geaz¥ÒÉæù·9&M«¼€[NORù¹ÿ«ÿ“Yð·ýŽv?únÔk³ÿ‚>ÉžÃÿaýOùE\gü¯þMgÂßö9Øÿé»Q®Ïþùÿ&{ý‡õ?å~¥×5âïøGÀÇŠôñ_ü~ãâ»sðÿö)øy©|\ñgË}Mâ{=Õä–MŒW<~ðÀ§ªÈkêÏهß´Ö‘ kZÏíEâ=;Xñ·sö¶:\a-t»uL}X"omܱùùþ6ëV?f¯Œ¿³'ÄŸ[iŸ³~©¤&—coì›’Æ{@p™dV9ž7”ÚÇ£µôµ|kñö+ø{ñ›ã¼_~2j^,Òô›h!Ò<5rÄi6’ ̳Iâ&i[®q Æß•ÿà•–¶ÖZ‡í ggAoŽ.cŽ8Ô""#JUG0à ýr¯ÉOø%·ü†hÏûî¿ô9hõ®¼«ãíSâçÂ/|0ÑõÓ᫟XK§ÿh,i0G8Ù."E»te—ï®3žØ¯U¢€>}ý˜?gß þÌ¿4O…ZEw5‚4—×é÷÷’ÒNë¹ÎO  ³m@ª¯Î¯‰z~‹cÿ…ø^ÿãŠZï@¸›Ä‚³k|\ÿ–‹ôˆúú«öÅý´ôÙÞÞÏá?á1ø»â}°èÚº´Ì1Û÷+Ì?r1‡”Œ ª×ö#ý’|Oð~}ãŸÇ]KþŒyª\YÂ`ÿd‰×‚I æýØØ‘Æ6 f÷?Úsô–¹á]'Qý˜|Ka¡x›F½ûLöÚœaí5+o-•­œ”“i,A ÈûéÖ¾GðÏüoUøg­Ûx öÝøs¨ü)Öåo-5{xž÷CºaÕãxüÆUÿ®m8Y–¿S«çOÚ7ãìÛðÏÁ·6_´f­¤.‘?ÙzŒi{-âŽÑÙm’IFxÈBõ#­{ƒ|sàψº¿Š|®Yx‡HºÿWwa:\BǸÝ 0î§x ê«ù¶øcðãÆ¿h âgü×À¾'øYà¶Dú®§©j-i¤êë*—[IæM»‡–²\™p‘mçúI åƒþ !ÿ'skÿbÖÿ£®kú”ÓÿãÂÛþ¹'þ‚+ùkÿ‚ÈÉÜÚÿص§èëšþ¥4ÿøð¶ÿ®Iÿ Š·EP_ßðZÏù5Ï Øåeÿ¦íB¿`kñûþ Yÿ&¹áOû¬¿ôݨP¬Á%ÿäÊü5ÿa[ÿJÞ¿Jkó[þ /ÿ&Wá¯ûêßúVõúS@׋¼gáhW(ñƵg iƒ2ÝßN–ð' /!'°ÎOjüÚñ_ü~ãâ»sðÿö)øy©|\ñgË}Mâ{=Õä–MŒW<~ðÀ§ªÈk࿌ÿ |}ðûö‰×~(~ÞÞñ?Å_†úyt‹Í'Qk3L´y™£Zǰ¢Ø64–ëÙn¯ÚÙ«ã/ìÉñ'ÁÖÚgìߪi ¥ØÆû&Â$±žÐæYŽDçå6±èÇ­Wý˜|9ûMiµ¬þÔ^#Óµkw1Ïkc¥Æ×K·TÇÙÕ‚&öÝËŸŸãnµÉüEýŠþüføïÆ_Œš…׋4½&Út \±M¤ˆ3,ÒE¸‰šVÁ+…BH1·ìª(ò7þ Ykme¨~ÐÖvq$ðxâæ8ãB""4¡UTp¯×*ü”ÿ‚[ÈgöŒÿ±îëÿC–¿ZèÄ:}ö­ jz^™zÚmåå´ÐÃtƒ-²!T”A% 9:Šü2ýµ?c„ÿ³gìQâ­wLY¼Oã­ORÓRñ>ª|ýFæIn”˱˜·’ŽI%T–n<Çr¯ÞZüÔÿ‚µÉ–xþÂZWþ•%}×ð›þI_ƒì §é4uå?´ç†ÿi-sºN£û0ø–ÃBñ6{ö™íµ8ÃÚjVÞ[+[9)&ÒX‚‘÷Ó­z·Âoù%~ ÿ°.ÿ¤Ñ× På†à£z¯Ã=nÛÀ_¶ïÃGáN·+yi«ÛÄ÷ºÓ¯Çæ2¯ýsiÀê̵úIàßø3â.oâŸk–^!Ò.¿ÕÝØN—±î7FH ;©ä¼{öøÃû6ü3ðmÍ—í«i ¤_ÆOö^£^Ëx£´v[d’Qž2…=Hë_ˆ? ~x×âoí£|Lÿ‚zøÄÿ <öÈŸUÔõ-E­4BÝeRâ+i<Âé·pòÖKó.-¼€I5üÁÁjÿäé¼-ÿbeþœuþŸkù‚ÿ‚ÕÿÉÓx[þÄËý8ê4ý|ÿ’9àOûéúKz]y§Áoù#žÿ°—ÿ¤±Ó>4é?5.Ѿß®—ã Ý6æ-*éßËݲoÚûpz§šËøÁû@üø £n|[ñe—‡¡u-S>û©ñ×ȶŒ4ÒûìCŽø¯Ï™¿lßÚƒö™ô؟ᄚf+ÏŒüV¢ÞÍFp^ÞQÊžx3¶Í¯…¾ Úü!ýœ~#½ÏükáˆfñÝýÓ4>,פ}E˜î%J¢F*¸Ëfé¿v>QýøÇ~ø‰á«_|5Öì5í Ô$3éÓG4*“÷d„*8(pW¡€(|'Ð|sá‡Ð~%ë«âoÙÛ*ê:’ ‰.n %ÝPyÀùG é_˜¿¿b/†Ÿ ¿gïŽ_|_uqñâF¯¤êwG[Ö?zÖaòR;8œD#L |³àaYP쯵òŸíËÿ&ƒñsþÅëßý€3?`?ù3o„ÿöÿF=}_ ~Àòfß ÿì þŒzúþ€>:ý±?e ¿k_øKÂ7¾+oèZ¯§h¶¦ãûEQ|±ñ4F"¤°ü¶qÅ}_¦h:&‹¢[xkI°‚ÓI³m¡´Š5X#bƨСxÆ1ŠÖ¯ÉÚwö«øñÏÇW±Çì]KÄWíüGâhÜ‹=×;'Xç\€ë’²J2Tþî Óšÿ‚cÚiVj[‡aáý¿ˆ`MXú:•¸¾ -ñÇ–#du_/Ú¾£ý£“öåðOÄ%ø›û9¾“ãO­”1^øGP Éš&s$ö³,îd cÎû·¯jý—¿g/þËŸôß…Þsvñ3\êΡ%¾¾”,ÌvŒˆ¹;QUI$?CÐçOÂoø)OÁëŸð€|h±¾ø3ã˜HŽm7ĈÐ[ù‡û—n¨¡} Ë{g­~‡ÚÝ[^ÛEye2\[Ρã’6ŽŒ2XpA‚+ó§öÖøóû iÚ<¾ý m¬|}®G˜ Ñ4è…ÆÔ–&F³rq÷¦`zWÌ¿ðNoƒÿ´w„þ-ê^0³Ñ5ï‡ß/mî ‡UÑ4/u~ÈÑ¡ =¼ç=³_2x{âoüö£×ôÍGÁ·øðÑ.¡š[½hyÚÝýª8gHátÝ•ºŒ`ü³5|ÿû|Oý…þë°x/â€ï>|Y·ÂÜ^øÕ ÄÒJÜ‹P–8ÒÝ[®|«tnźŸÜ«+Û-JÒCN¸ŽêÖå‘K‡ŽDaÊÊH Ž„à¾/x#Xø“ðÓÄ>пiÚ¯Æoâ(ý¢­<¿R›Ãþ*ñ¤Â»¤òc[ÙVBª ÷Æ m|ì`ÒOø'D~#‹ö*øXž)GKçÎcìw9³?7;M±Œ¯m¸Çá^-ø‹ÿý™þÓŸ ¾jWí+s5Ì÷Q¾i}|/î쬄x1É /´©½¶Ž0½(èÿÚ›þMâ÷ý‰úÿþ›ç¯Ä?ø"ü”oŠö °ÿÑòWíçíMÿ&Çñ{þÄýÿMó×âüþJ7ÅûXèù(ú3¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯Åø!ïü?ì¦èé¨ú)¢Š(åÿ_±¿ìñûGÁ$Ÿ¼)ë »SW²ÿDÔ£ aOŸÌ{,¡Ðv¼§öwýœhßÙïâ,z$ŸdñïÁãk:Ça­!mZÆq³¬Sm}ñŽAÄ‘¨"kïj(矎µgÀ?Ù¾ëH²øÑâ‘áÙµÔšK%6W—~jÀTHskÁv—_½ŒçŒó_Ž÷Ÿ¶‡ìÓ/üžÇãâxÈÅá3¦6¥ýŸÅÙû¿#ìþqóy{}ë÷«^ðo„ëÚ¾ðgükMñn¿ÄOÛÇZŸÆ_'ζ“Jöº5©<”Š*ÅÅï~Ÿ"$H±D¡  `:)ԃᯠxgÁš-·†ü!¤ÚhšMší‚ÒÊ·‚5ôHã £ð½Eùÿ«ÿ“Yð·ýŽv?únÔk³ÿ‚>ÉžÃÿaýOùE\gü¯þMgÂßö9Øÿé»Q®Ïþùÿ&{ý‡õ?å~¥ÓYUÔ£€ÊÃAE| ñ£þ Íû?|QÕá4ð|? %øÈhÑø³Å—ž˜³ïî>Ñhí!Y?ÑíäÙÃå}­í_´zßÇÏéÿµÚ3F–mwÂ6šDºÔOmG5ͬH_äŽàDÊÍŒû}ë§ÿ…Cð›þ„­ÿ¶ßünx_Q½ø[âøËM‚þçK¹³Ó`½MšlsI$"dŽ7Ä*ÄUCÀÀæß ¿i߇g;Úzån|;áµ¾½Ÿí±ù“ÛA§ÜKo+¼vÞinaf7ãñ^—k¬éW«²{KÈRâ WÑã2°úŠüÝñ¿ü_@ðïˆ'ø‹û!ø×Sø/âÆùŒ6’Éq¤\r[wbÁ þ¼kÚ#Ò¿O( 6øh> h_ ôcñ£P³¾ñe…:ÅÝ‚‘k$Ñ‚^HÔ$g à"óœ(é_™µgü7ö=ø“û8|Eð‚¾ G]×tk«K+oì½N/6i »å´D\ú³ï_¯n‰"4r(e`Adz‚+ÏáPü&ÿ¡+DÿÁm·ÿ ̯؛öîý”ü%ðáwÁÿøäZxºÚÒßM’Çû7Q}·rÊU#óRÙ¡9,àûyä×Ú¿µoí]à¯Ù#Áº?Œüm¤êžµ¨®ZpˆºÈbyw7$cnŽ 9=+Ù­þü.³¸ŠîÓÁÚ4ÀÊñÈš}ºº:œ«+È òé^ûa|ñ?íðÿAøo Í¦Úiï¯é·šÅÅþÿ=4ËY<É–È$R´9 âŠWp-ƒ@7ÿÁS¿iý[à?Á½;À~ ½“Kñ?ĉ'³Šý7)²Óíü¿¶L®€°‰R5Ú7ìËó(¯!ý•kø&ÿì­ðºÏÀ^ñë\jS*M«êm¢j‚mBónF?eÈrDQ羬Y›õÿ_ð‡„üWäÂQ¢XëeÝå}²Ú+/~7ló¶îÚ3ޏ•Îÿ¡øMÿBV‰ÿ‚Ûoþ7@Oð7öÅýÿi {PðÏÁß6¹©ivßl¸‰¬o-6A½cÞæÔüÌ'ž˜¯-ý£¿gŸÚCãÿÄô+âÃü>øD,¡Vº:«ÞÝ=`©²"›@ýë)çtF¾ÆÐ¼௠ÜIwáŸéúDò®Ç’ÎÒ+wdÎv–T‘ uò·ÀOØ»övýœ#Šçáß…¢}mYÔ1y©9<'qˆ·wXV5?ݯªh¢€ þL|ÿ)f¹ÿ²§«éÎzþ³«ù1ðGü¥šçþÊž­ÿ§9èú΢Š(Ì>)üøSñ·A>ø­á{Ø`ìQ,%º´3.%…¿Ú”û×ÀÚGì#ñwö|ñe޹û|W¼Ñ¼0÷±I¨xSÄ o4æ·iŸÈ“cì}™ L^a8ÌÕú‘EyÇÅ‹~ø&óâ/Å[ûÃÖ s]yÜliÜGòíã’C¹Øã©Àæ¿?à¡?¶‡ìÓñ¿LøSoð¿ÆC[“Ã~,µÔõ4ûû&Ò5!¤ÿH·v?º››ÐWïÞ¯¢hÞ!°}/_°·Ô¬¤*Z ˜’h˜©È%8##Ž qßð¨~Е¢à¶ÛÿÐŒüý¶¿f/~0øMãA®ëÍo-жþÏÔ-¿s7¶û›h£ãpãvOaSkßµw‚´ڣß²mÆ“¨Kâ?iÒj1_ ‹ìQÇ73•rdó7¶aÂ’9êG»è¾ð'†¯?´|9áÍ7J»*S䳆 662»£Ppp23_<ø—à'‰üUûaø;ãíôÚm¿†< _YÚGó©Üjz1HÓ~èGöt·$'ïKψ$|añKöˆøû~Úš¾¿ñºûYÕþøþÒ5Òî̹¶Ò.‚Æ\,J0 H²~s«®â¥kºøóÿ;ý™ì¾ëoÂ_IãŸk¶’Ùizu•Ø&æé q¼­4Q€ªÌ A™¢¯9¦ú¶‘¤ëÚ|ÚN¹e£cp’ ˜Öhœ<Œò+‹ðçÁÿ„¾Ô­á/èz%ð$‹‹6ÚÚ\ž§|Q«s“Þ€>Yÿ‚qüñOÀÙ_ÃÞñ½«éúö­=ίwi&D–Æð*'S‚®!Dޘʹe<ƒ^Sâ_Øgãíã]WWý¬~,Þ_ø-5 ßL𧇘ÚY}e&ÛíR@ﳿtÎ ;fúƒEyWÂß >èCÿ ü+cá»2ÛGûéÊô3Îû¥™‡÷¤v>õê´Q@ûSɱü^ÿ±?_ÿÓ|õø‡ÿ@ÿ’ñCþÁVú>Jý¼ý©¿äØþ/ØŸ¯ÿé¾züCÿ‚ ÉFø¡ÿ`«ý%FtQEQEQEQEQEQEQEQEQEQEQEÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯Åø!ïü?ì¦èé¨ú)¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(òþ Wÿ&³áoûìôݨ×gÿ|ÿ“=‡þÃúŸòŠ¸Ïø-_üšÏ…¿ìs±ÿÓv£]ŸüóþLöûêÊ*ýK¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ù`ÿ‚ÈÉÜÚÿص§èëšþ¥4ÿøð¶ÿ®Iÿ ŠþZÿà²òw6¿ö-ißú:æ¿©M?þ<-¿ë’è"€-ÑEWã÷ü³þMsŸö9Yé»P¯Øü~ÿ‚ÖÉ®xSþÇ+/ý7jëðIù2¿ ØGVÿÒ·¯ÒšüÖÿ‚KÿÉ•økþÂ:·þ•½~”ÐEPEPEPEPEPEP_Ìü¯þN›Âßö&XÿéÇQ¯éö¿˜/ø-_ü7…¿ìL±ÿÓŽ£@Ñ—Áoù#žÿ°—ÿ¤±×¥×š|ÿ’9àOûéúKz]QEQEQEQEQEQEü˜ø#þRÍsÿeOVÿÓœõýgWòcàùK5Ïý•=[ÿNsÐõEPEPEPEPEPEPEP„~Ôßòl¿ìO×ÿôß=~!ÿÁ?ä£|Pÿ°U‡þ’¿o?joù6?‹ßö'ëÿúož¿ÿàˆòQ¾(Ø*ÃÿGÉ@ÑQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@ÿ×ýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯Åø!ïü?ì¦èé¨ú)¢Š(¢Š(¢¼‹ãoÇ?†Ÿ³×€îþ"üRÕWLÒíŽÈÐ ÷S°% ·ˆs$ƒÀbª Ê=#þ öß‹ú‚µ¯„—š‡5Ë«XRêòô¥ø¶½uXnþÌmÂ*Á¶¬Œû²à·4WÉŸµWíðïöVÐôö×­®xãã´Ÿ´7¼?ý¿âW¶¶`¾;NGµcœ[0ÚÒìÚ¿9dC*ËÂÛ?N°ÓàªZ4Z}´vÑÉ­øAÙcPŠX­ '8€?¨ª(¢€ (¢€? àµòk>ÿ±ÎÇÿMÚvðGÏù3Øì?©ÿ(«Œÿ‚Õÿɬø[þÇ;ý7j5ÙÿÁ?äÏaÿ°þ§ü¢ Ôº(¢€ (¢€ +äÚ‹öÏøeû0Åa¢jV÷>*ñƶög‡4±æ^Ün%UäÀo*6a´1Vf9ص±óGìmÿ.oÚ‡ã ïÁÿxøBÿì·V’%ë]e­Hó ™^J¸RHaÝH*:Ðêµcø‹W_øSמ#2é¶³\˜ÁÚ\B…öƒÎ3Œf¿;jø(׆þx²ûáwÂÏ ÝüNñ¾“ÜjvöEŦ—.÷7ŬY†uU ƒïºž+¿ý‰¿k­7öØøgâ=CYðêh:†sýŸ¨X¤íq °\Ź$W*Œƒz•9#nwr0ùÝðóöâý»ÿmï^xCöeÒ´o‡ºN˜‹5åýÌbøYÅ!*Ÿh¹¸ŠDgr–‘Ûl1ÆÕb¹ß´_Ä/ø*ìU™ãï|FÓüuámBáa–Xôûy-¢Ÿï,7m`–%¢pʶÐlþü øEð'N¿Ò~øb×Ã6º¤«=Ò[o&Yv©fvf;GAœ ž95ð÷üÇÆžðïìw¬økXt:ŸŠõ:ÓMŒ‘¿Í·¹K¹d9Ú±BÊ[ . ýá@E~Æ?µ›ûYüµø‘ˆÒµ{;™4ÝZÍ há½…RBbfäÆñÈŽ¹äd©$©'ëüÿ‚2øÄýœuÿk=½§‹5É&°WÈ[ÚÄ4Êf”:g¾Ï¥~¿PEPòÁÿÿ“¹µÿ±kNÿÑ×5ýJiÿñámÿ\“ÿAüµÿÁd?äîmìZÓ¿ôuÍRšüx[×$ÿÐE[¢Š(¯Çïø-güšç…?ìr²ÿÓv¡_°5øýÿ¬ÿ“\ð§ýŽV_únÔ(Ö?à’ÿòe~ÿ°Ž­ÿ¥o_¥5ù­ÿ—ÿ“+ð×ý„uoý+zý) Š( Š+äÚ‹öÏøeû0Åa¢jV÷>*ñƶög‡4±æ^Ün%UäÀo*6a´1Vf9ص°õýùSûÁK›ö¡øÃ{ðÄž>¿û-ÅÕ¤‰z×YkR<È&W†®’wR ޵Ö~ÔÿðQ¯ üñe÷Âï…ž»øã}&¸Ôíì‹‹M.$]în%Š9X²) êªßuþËÑ\è—÷Ÿð•xÒ5;4=:Ei#|qö¹¾d¶^™ ™0AXØsWaÛÛÀ߈®´ðÞµáËÕ´»µŽsq$±‰!™‘ ‡R¤ÎNk­ø)ûþε½_Äþ ðÊ^kšÅÍÄï©‘{qwÌmíÙ×D¡¶£{.<ÇsÍ~lþÁŸ·WÇßÚ—ö»¿Ñüi¨ÛéþmöæÎÖÞ†HDmæ¸iÞA¼îbø<áT`Ýšþ_?à’pCkûnx†ÚÝqC¢kŠ8 «unÀWõ@Q@0_ðZ¿ù:o Ø™cÿ§F¿§Úþ`¿àµòtÞÿ±2ÇÿN:F_¿äŽxþÀ:_þ’Ç^—^ið[þHç?ì¥ÿé,uétQEQE~`~Ó¿ðS¯|×õoü1ðÕ×Äßxy ë g!‹MÒ°F)ĺ1¦Õ'kH§ôWıOퟤþÖ¿ õïêZ*xFóÂ×FßP‰î„ÖˈJ³¬Ì±íB»·.ÒrAÍ|eñ£þ ÿiá­Jöçà§Ã{¯øCJ»SøŽîI­,&¸9>\ap7JY‡>^9 ¡ÿµïí'ì·ð?Tø¹†ÿ”³\ÿÙSÕ¿ôç=YÕü˜ø#þRÍsÿeOVÿÓœôýgQEQEQYšÖµ¤xoH½ñˆ/aÓ´Í:¸¹¹¸uŠaŒnwwbª’OJü`øÃÿ‘Ð|/©™>|6¼ñW…’åíS^¿žM:Òîh°dKeò%'å ‚ì®Æ(öÊŠù’Ïö°øYoû4i?µŒ.[ÃþÔôèoLRþöq4¿/ÙcUÇ›/˜ .Î7«’?7,¿à°š›üRð·¯|"»ð÷‚¼Osn–Ú…ý̑ݽ•Ä¢!z‘ù'Eä”G`pTIžhèoø(Gíãâ¿Ù ø[ÀÞ´Öµ¯Û\\Ew},†ÞØBë ¼[^RŲ?z˜ÇCž<ƒMøuÿxøàÛÝüRÐü}wº·ÐZÎÞã »–9Ýl¥Øä`lioG*réÇÄ_€_þ-kúŠ~$xRÏ_Õ|4ÛôéîC·;ÖN`ÞŠpÀŽ=Íz7ˆ|E xKD½ñ/Šu}'IÓ£3\ÝÝJ°Á kÕÜ…Qõ4øOû+ÁH>=hí¿f¿ÚÞÖ»Û½Lhfü[Åkyg©<žTK0· °É!UÞ¨¼03/ûã_Ë…4MCöäÿ‚–^üEø_c'ü"v^ °Õ®ïY qǦi&–gÎ Ét Z›sò0¬Gõ;@Q@ûSɱü^ÿ±?_ÿÓ|õø‡ÿ@ÿ’ñCþÁVú>Jý¼ý©¿äØþ/ØŸ¯ÿé¾züCÿ‚ ÉFø¡ÿ`«ý%FtQEQEQEQEQEQEQEQEQEQEQEÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯Åø!ïü?ì¦èé¨ú)¢Š(¢Š(Íþ"ü øeñmtTø•áËO/‡o“Q°[´Þ°ÝF Œ€Àƒó#erœ =_µlkñsþ Ùá"‰­t½_Ã:|€ ¬7·þ$}EKµüÒþÏÿà±>&ÖµWRšGˆ|Të¸ðVÆ«X?Ý[Ûýxá½CÆú_Æ_hV2x›Â–“Åi«Ü(Ú[8-/ÎN¨,AlìÜûJïlþ<ÿÁ;¼='ÇŸÛãOíˆÈ¾[ÛÛ Io›%äŠTÿ½š qØÌµØ~×ßµ&½ûNø­?b?Ùåu«ÿ±‡Äšý³“ckb§³¦AëåRTܦöúð à—„g„ú§ú%” .îŸæžâLgç‘É8ÎaGÊ P±×óûnÊU´?û øCùZWôû_ËŸíÁ©ØEÿNÓ®žtXë>óÛ#ìKGmǶäÐõEPEPäü¯þMgÂßö9Øÿé»Q®Ïþùÿ&{ý‡õ?åqŸðZ¿ù5Ÿ Øçcÿ¦íF»?ø#çü™ì?öÔÿ”Tú—EPEP˜]|'øUmñOŽwž²_ÛéæÅµwLΖi– ò‚ ¸ö|›¶ñ_Ï¿üFÒ_ˆ¿¶?ÄŠ·‘ü°ézîq÷nuKØŠÿãžm~öþÒZäþýþ(øŠÕ¶Ï¦x_Z¹ŒÿÓH¬¥eý@¯Åoø#F­àχ¾øÕñOǵ®‹¦Ù>÷wr,QAktç,ÇøÙÔÕˆx ÓÚf/ƒÿ²ÿÀߌÿt=ÏD×üac2]\ÛÆmCT»­­wzþöMîùi!‹ã?ðI‚ºÂÏÙ~/ëÐ5¾¥ñ ñµuGe±±Zdz:«L§û² ùž÷Tñü_ö±Òô›k­3örøgx&ºš]ж³v:8!å_•WïC 3±Y$T¯Ý;;M:Î >¶µµbŠ(Ô"G ªª£€ @§ÄOˆ~øQà­_âµ(ôD§¹¸“ QÀU–wbe™ˆP WóÕá¯üLÿ‚´þÒRüCñD7>ø1àé~Ë çm÷û,$eZòäaçdD¥y!bVÒý§þ-jðPÚ¾Çögð§‰`ðç¯ÝJ×ú„·Å ïhÞ]Õî]‚ÈAo&Ñ2A-¿!]¶þØ|<Õ¿fÏ?tÏx;Äú…á¯[ìŒ>§j ó<²ÈÒ Îç.îܳM{o‡<9¡xCÃúw…|1c›¤iñÚÚZ»c†T""@µ_þÊ¿¶7…k sâ%—ƒ4i¬´¯_Aiü“ RŽá®N‘„S•€6Ö$áÆpA¯±¨¢Š(ù`ÿ‚ÈÉÜÚÿص§èëšþ¥4ÿøð¶ÿ®Iÿ ŠþZÿà²òw6¿ö-ißú:æ¿©M?þ<-¿ë’è"€-ÑEWã÷ü³þMsŸö9Yé»P¯Øü~ÿ‚ÖÉ®xSþÇ+/ý7jëðIù2¿ ØGVÿÒ·¯ÒšüÖÿ‚KÿÉ•økþÂ:·þ•½~”ÐEP^auðŸáU·Äy>9ÞxzÉ|co§›ÕÝ3:Y¦X€OÊ‚àoÙònÛÅz}x¯í%®OáŸÙß∭[lúg…õ«˜Ïý4ŠÊV_Ô üÿ‚HÚKñöÇøñVò?–/Q½Î>íΩ{_üsͯ׿Úf/ƒÿ²ÿÀߌÿt=ÏD×üac2]\ÛÆmCT»­­wzþöMîùi!‹ù‘ÿhÕ¼ð÷Á¾)øãVµÑtÛ'ÑàžîîEŠ("nœå˜ÿ:€:±OÜÞêž#ÿ‚«þÐ6:^“mu¦~Î_ ï×SKºÖnÇ@<«òªýèafv+$Š”ôÇü_ணð³ö_‹Åzô o©|B¼m]QÆlB,V™ŽªÓ)þì‚¿Pªµ¦gŸa [ZÚÆ±Ej#ÕUQÀP©Ù•»ª£$ž€?˜Oø$ßüŸ‰ì ¬ÿé]½OµüºÿÁ&u&ý·õ©e+}£ë"Ÿõ„ÜC Ûëò©?A_ÔUQEüÁÁjÿäé¼-ÿbeþœuþŸkù‚ÿ‚ÕÿÉÓx[þÄËý8ê4ý|ÿ’9àOûéúKz]y§Áoù#žÿ°—ÿ¤±×¥ÐEP_šß¶ï‚>þÏ?±oÆGáLJlü??ŒŠ}´Û&ÖººÔîâ‚F$’p«#² Â ÎÅ9ý)¯Éoø,ι>—û'ézt-¬x¢ÂÚAë[]Üèq-sŸðGO‡Úl¿²—‹/uû(¯mDFa <ÇwŸhÌd€~™üøeÁŸƒ> øY ‚cá.ÖÎY¤“¢:Aìòaõ¯(ý°?k~É &ñ†ºRÿ_ÔÁ¢éA±%íÐ[¬d4¯Ø£çeÞ¾$üAðÏÂkß„*¸7þôWÍjÿ~ë>9Ô|O¤Þ.jïk¦Ú_Û›‹É•q ´«1Üí…ÈReʤ×Mû8üh·ý¡¾ xkã%¦‘&…ˆÒáÒÎYDï‚æ[~dUPwy{º zöê(¢€ þL|ÿ)f¹ÿ²§«éÎzþ³«ù1ðGü¥šçþÊž­ÿ§9èú΢Š(¢Š(›ñ‡„<3ãÿ jž ñ–­¡ëVïkyk0;%ŠA†S‚=à œA×àÿüJ ü8øaðcà·ôë}FµŸQ¹‚ÎÙGVqà |uËÜ–9,A,I$Ÿè¿šïø,…Ô¾"ý©¾xäÙnº©û¯¨\Äçþù‰M~ÉüýŸ¾ø£öGøYð§â‡-|A§iº.w%­ÚnD¿Ž•¤"Gpyù•™[*Ìæÿí/¢ÚþÕ?ðS?‡<.ž~‰ð¶ÒÚã\xÔyP-¼ßmž<Ž*míý¤}¸à×Ùß¶gí¿áÙ§ÃËðÏá ÄµTŽÇIÑ­ÚÉå!–åqÊùpýùNÐBXhþÀŸ²†«û=øRñ¯ÄÙN¥ñSâ¿ÚõܯçKö2-¯›Îæ Ìó0á¤'–TC@×à‡ÇOø%·ígñJ–ëRøù/Äk›U2Ác­µå¼%ÇðÄk˜£cØíQž¤Géíûs|ýš~(øOá_Äk]L]ø®8gKëxàk8&¸6þeËÉ28Tef}ˆØQžOöZ:H‹$l9„þqÿ`ÏÛ3^ý™þ$Aû"|qð†ŸáÛS© .KëkXí/mu|¨Úù¢.’FÚ¾yù‚ûÝ1èê¿”ïø(Mµ·Åø(¥Ç…~¿Úµ›»­Hw·;ö˜H¢#=E”FþéBCêÆ€ (¢€<#ö¦ÿ“cø½ÿb~¿ÿ¦ùëñþÿ%â‡ý‚¬?ô|•ûyûSɱü^ÿ±?_ÿÓ|õø‡ÿ@ÿ’ñCþÁVú>JþŒè¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÑýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(çïÚÏþM_ãý‰¾ ÿÓtõø¯ÿ=ÿ‘çâ¯ýƒ´Ïý5~Ô~ÖòjÿÿìMñþ›§¯åŸö$ý´nc]sÅ:Í¿„SŇÄÖöÖåøÙy?fw}ÙÍ»vücï@ØÍüùÿÃòµ?ú#Pÿàù¿ùø~V§ÿDjü7ÿ ÐôE>ðü­OþˆÔ?ø>oþA£þ•©ÿчÿÍÿÈ4ýWâ‡Åßø#í¿ÅŽÞ*ø¡gñ)´ÅZΧ5šiÆ{¨å½c%Ìk/ž‰µÝÜ‚W…!J¶2|‹þ•©ÿчÿÍÿÈ4Ãòµ?ú#Pÿàù¿ù€?a?g?Ùsàÿì¹á7ð¿ÂÍ,Ã%ÙV¾ÔnXMzè0­4¸^¨Š¨¹$($“ôE>ðü­OþˆÔ?ø>oþA£þ•©ÿчÿÍÿÈ4ú«ûI|øßñ“RÒçøWñÃPøQcim$7VÖ:x»7R3eeóEÍ»ÆT|¸õȯ̽_þ—¬xƒUº×µïsêZô5ÅÕ΀ÓO4¬rÏ$©–f'’I$×=ÿÊÔÿèCÿƒæÿä?áùZŸý¨ð|ßüƒ@¡?³÷ì‘ûD|ñ¦‡«x¯ö“Õüyám%fIt;Ý0ª\«ÀñD¦æ[Û‰Dì²ÎÀ¼šý¯çÏþ•©ÿчÿÍÿÈ4Ãòµ?ú#Pÿàù¿ù€? Ê+ùóÿ‡åjôF¡ÿÁóò ðü­OþˆÔ?ø>oþA £¿àµòk>ÿ±ÎÇÿMÚvðGÏù3Øì?©ÿ(«ò3öÄÿ‚‰êß¶Ãá4?LJgµ×-µ(¦‡Qkù&‘ ¸¶X/³CË›Œ‚œŒ`ç#KöEÿ‚™ßþÉß×á,“Ä‚;û›Ótú¡³lÜm<¯²Ë÷võÝÏ ¥~ƒ³µÏ겊þ|ÿáùZŸý¨ð|ßüƒGü?+Sÿ¢5þ›ÿiˆþƒ(¯çÏþ•©ÿчÿÍÿÈ4Ãòµ?ú#Pÿàù¿ù€?w¾ x7Løà?|>Ö™“Oñ>›y¥Ü4}a½…¡r¹îÎ=ëñWáÇü/ú^¼—_~%ÜkzR‡m?M±û \Îß2y&›gRXÉÁ;\šâÿáùZŸý¨ð|ßüƒGü?+Sÿ¢5þ›ÿh÷sÀ?üð»ÂZw>éhz“ŠÚÖÝv¢¤’rÌìrYØ–f%˜’I¦|FðÖ¯ã?x“´þÔµ½:êÊßSŠ?:K)n"hÖtMɹ£-¸ ëÈê:×á7ü?+Sÿ¢5þ›ÿhÿ‡åjôF¡ÿÁóò ^ÿ‡ÿÕlÿËoÿ¾TÃŒê¶å·ÿß*£ÿÊÔÿèCÿƒæÿä?áùZŸý¨ð|ßüƒ@¤Ÿ°÷ìWìg ø«G/ÿ„¾OÜÛNeþÏþÏòE²:Ûö‹ÙÞNr1é_tWóçÿÊÔÿèCÿƒæÿä?áùZŸý¨ð|ßüƒ@ÐeüùÿÃòµ?ú#Pÿàù¿ùø~V§ÿDjü7ÿ ÐÍŸðYù;›_û´ïýs_Ô¦ŸÿßõÉ?ô_Å¿í…ûNMûZ|Z‹âLJWà ›o§}‘nà"Ýä}þaоf1·Œuæ¿´?þ<-¿ë’è"€-ÑEWã÷ü³þMsŸö9Yé»P¯Øü~ÿ‚ÖÉ®xSþÇ+/ý7jëðIù2¿ ØGVÿÒ·¯Òšþ\?dŸø*4_²çÁM3àû|4>%:uÍÝÇÛF±ö=ÿj”Ë·ÊûØÛœgyÏ^+éoø~l?ôE[ÿ !ÿÊêýù¢¿¿áù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü® ßšä~ x7Løà?|>Ö™“Oñ>›y¥Ü4}a½…¡r¹îÎ=ëðÓþ›ýVÿˆòºø~l?ôE[ÿ !ÿÊêè~Áü;¥ëÉuñGâ]Æ·¡Å(vÓôÛ°µÀLíó'’i¶u …ŒœµÁ9¯Úü?ðgÂï iÞø¤A¡è:Lb+k[uÚˆ:’IË3±ÉgbY˜–bI&¿ ¿áù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü® ßšüÚø÷ûþÐßפ/A·Ò‰† wP »KôT<ä2€AÁ¾+ÿ‡æÃÿDU¿ð¢ü®£þ›ýVÿˆòº€6|3ÿYñ/‚õÛ?x?öºÑ5=÷ÛÞYh/oq `‚RHõ5eÈ$A#¥~ŸþÍ¿>2|]‹âÏÆ;ï‹+¨‹QeöÝ=l‘æù¸aq;HfÞ™ÉØ:äãòŸþ›ýVÿˆòºø~l?ôE[ÿ !ÿÊêýù¢¿¿áù°ÿÑoü(‡ÿ+¨ÿ‡æÃÿDU¿ð¢ü® ßšþ`¿àµòtÞÿ±2ÇÿN:} ÿ͇þˆ«áD?ù]_˜¿¶íE?í•ñFÇâ…§„¤ðÂhzd¶Ëvu²¹åóÚQµÒ¦ ð@ù¾lØÒ¾ÇõÑð[þHç?ì¥ÿé,uéuüðx+þ U„< øHük¿ìK [;þ›öh–-û~ÀÛwmÎ2qÓ&ºoø~l?ôE[ÿ !ÿÊêb?~h¯Àoø~l?ôE[ÿ !ÿÊê?áù°ÿÑoü(‡ÿ+¨÷æ¾Jý´e»_ÚçàØødÚÏü#÷ÖZ¾©exÐý¡xHŠÉd%Z)xa‚Aç?—ðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWP½~Ï_ðH„¿ µÛ?ü`×åø‘y§•{[¶û&—+ýìFI^p¬ËÉßf¿_#Ž8cX¢Pˆ€*ªŒ:_?ðüØ芷þCÿ•ÔÃóaÿ¢*ßøQþWPê?í“û3jÿµ‡Âˆ>iþ5ZF ÛÉRËíÂî(öÀñùöø_1’Lî?2;Ë?øqýVÏü¶ÿûåRÿÃóaÿ¢*ßøQþWQÿ͇þˆ«áD?ù]@ÃŒê¶å·ÿß*ý•ý¾Gðà·…~Eªm/†mš¶ùfóËÊò—ò·Ë³—cÊ<ØUÙƒ9åeN8÷¯ž4ø'ÿí‘á_Aà ~Õº•¯†-¡6ð °™&†`$Gílèª8P²€£…À¯ÿ‡æÍÿDUð¢?ü®£þ›7ýUÿˆÿòº€>øý”àœßÿf?ˆw:…×üvÁÂêÚ‚ˆÒÜÊ•­àö<€Îï#àÛ¿B«ùýÿ‡æÍÿDUð¢?ü®£þ›7ýUÿˆÿòº€? *+ùýÿ‡æÍÿDUð¢?ü®£þ›7ýUÿˆÿòº€?bÿjoù6?‹ßö'ëÿúož¿ÿàˆòQ¾(Ø*ÃÿGÉU>)Áeåø—ðËÅßÂӇд{ý'í_ÛÆo#íÖï›åý7ìß»nåÎ1‘Ö­ÿÁ?ä£|Pÿ°U‡þ’€?£:(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€?ÿÒýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(/\Ñ4h·þñœZŽ—ªÛËkwm:‡Š{yФ‘ȧ†WRUàƒŠù×þ«öHÿ¢CáüAÿÄ×ÓÔPÌ?ðÅ_²Gý à²þ&øb¯Ù#þ‰†?ðYÿ_OQ@0ÿÃ~ÉôH|1ÿ‚È?øš?኿dú$>ÿÁdüM}=E|Ãÿ Uû$Ñ!ðÇþ ÿâhÿ†*ý’?èøcÿñ5ôõóü1Wì‘ÿD‡Ãø,ƒÿ‰£þ«öHÿ¢CáüAÿÄ×ÓÔPÌ?ðÅ_²Gý à²þ&øb¯Ù#þ‰†?ðYÿ_OQ@0ÿÃ~ÉôH|1ÿ‚È?øš?኿dú$>ÿÁdüM}=E|Ãÿ Uû$Ñ!ðÇþ ÿâhÿ†*ý’?èøcÿñ5ôõAusoem-åÜ‚( F’Gn¢Œ’}€¤ÝµcI·d~q|zø û2|5¶ÒbðWÃOé>#–as Õ­„QMm_Ƭ Y¸SìOP j|ýeoˆ¾¹“Ä? |9{¯YÎÆòi´ø^Yüâ]ebTò܃î¤÷¯)ø‘ã+xÇPñ¹Êû-Ðÿ ®9>äÖ×ÁÏøæÏRöé÷_è×c·•!7ü°ß@GzüJ7:íþéû¿öïG÷ëé¡û½~K#Xt¿z½ÿû{ªû´õ³>®ÿ†*ý’?èøcÿñ4Ã~ÉôH|1ÿ‚È?øšútÀ2œƒÈ"–¿n?>aÿ†*ý’?èøcÿñ4Ã~ÉôH|1ÿ‚È?øšúzŠù‡þ«öHÿ¢CáüAÿÄÑÿ Uû$Ñ!ðÇþ ÿâkéê(æøb¯Ù#þ‰†?ðYÿGü1Wì‘ÿD‡Ãø,ƒÿ‰¯§¨ ˜኿dú$>ÿÁdüMðÅ_²Gý à²þ&¾ž¢€>aÿ†*ý’?èøcÿñ4Ã~ÉôH|1ÿ‚È?øšúzŠù‡þ«öHÿ¢CáüAÿÄÑÿ Uû$Ñ!ðÇþ ÿâkéê(æøb¯Ù#þ‰†?ðYÿ_N*ª(DUvEQEçŸþ|6øÇ¡Á៊>³ñ6•kr·‘[^ÇæF—È÷‚Hã>ŒkÐè “á„ÿcßú$šþñ£þOö=ÿ¢I à(ÿúÊŠù7þOö=ÿ¢I à(ÿ?á„ÿcßú$šþñ¯¬¨ “á„ÿcßú$šþñ£þOö=ÿ¢I à(ÿúÊŠù7þOö=ÿ¢I à(ÿ?á„ÿcßú$šþñ¯¬¨ “á„ÿcßú$šþñ£þOö=ÿ¢I à(ÿúÊŠù7þOö=ÿ¢I à(ÿ?á„ÿcßú$šþñ¯¬¨ “á„ÿcßú$šþñ£þOö=ÿ¢I à(ÿúÊŠù7þOö=ÿ¢I à(ÿø×ãÁoÙ×Â2¿ðßßé]§ÙE–¦°C„» ÛÚû«À;pwu?*ãõâ?Œ­üàíCÄràÍl·Cüs¿¯Ó<Ÿ`kò~êæâöæ[˹ ³Îí$ŽÜ–v9b}É5ùˆùó£NJNÒ–¯É-¾÷ù«øeÃêµIã++Æ:/6Ö¿rüϰ¼û~Åþ.ðÎâ-;á.ƒäÞÄ­´Ûn(ÇBsÉV¥t¿ð±ïýMÿGø×!û,øïì·÷~¿“÷wy¹³Éé*Þ ÿyFà?Ù>µ÷ }Ÿ ç ƒ…~»?U¿ùú3â8Ÿ%x lðÿguèöÿ/T|›ÿ 'ûÿÑ$Ð?ðð±ïýMÿGø×ÖTWº|ùòoü0Ÿì{ÿD“@ÿÀQþ4à þÇ¿ôI4üã_YQ@&ÿà þÇ¿ôI4üãGü0Ÿì{ÿD“@ÿÀQþ5õ•òoü0Ÿì{ÿD“@ÿÀQþ4à þÇ¿ôI4üã_YQ@&ÿà þÇ¿ôI4üãGü0Ÿì{ÿD“@ÿÀQþ5õ•òoü0Ÿì{ÿD“@ÿÀQþ4à þÇ¿ôI4üã_YQ@&ÿà þÇ¿ôI4üãTbý€¿ch®þÚŸ ôc bØd‘£Éÿ¦lå1íŒ ûŠù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü(ÿ†ýÿè‘øsÿcÿ ú¢Šù_þ{öCÿ¢GáÏüü+Òþüø/ðròöÿágƒ4Ï ÜêQ¬W2X[¬-*!,ªåz€I"½vŠ(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÓýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(®/â…o¼kák¯ ØêÙ_mÚ²Ì"󉈲½1»€Nzdcší(¬q#Vœ©Ïf¬úiò6Ãb'F¤jÓÝ;®º¯]Î/ŠÿGÃÛëÿÛÚ^}Ò[yfòq½÷nó_¦ÌcúÖ‡Ã_Ùì|Cð¤'þßû$‰å}—ÍÇ–Ûs»ÍN¿J÷/Ú»þIÞŸÿaXôDõÐ~ÍòJlëâçÿFüžŸ à^u,#§ûµ Úï}:Þçì5x¯0YqЧïí{GmzZßêþÑ/¼7á« Q¿þÓ–Â1¸òü¢è¼&Wsò9ç®’Š+õª4£(Ge¡øåjÒ©7R[·wÓ@¢Š+C0¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(Å>/|)Õ>(ÿgÛG­®›cc¹Ì^A—|­Æò|Å輎çÖ¾ø©ðæO†>!·Ðd¿‰¸µKŸ0EåcsºmÆæþæsžõú©_ŸŸµwü”=;þÁQèùëòÿòL4pÒÆ¨þñµ­ß¦×·à~¯á¶{Š–&8/ݤݬ½wµÎ×Ã?³¯¦Üéž%Ó|T]@b¹ý¬0Àß ŽÇÔWÙ£8º÷Åfè¿ò°ÿ¯x¿ôZuöù.I†ÁA¬4mÍkêßæÙð™æ{ŠÇTO+òÝ-üQEìžQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEÿÔýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(æOÚ»þIÞŸÿaXôDõÐ~ÍòJlëâçÿFçÿjïù'zý…aÿÑ×Aû5ÿÉ)±ÿ¯‹ŸýkàhÿÉG?ú÷ú£ôZßòLÃþ¾~Œ÷º(¢¾øüè(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+óóö®ÿ’‡§Ø*/ý=~×çßí]ÿ%Nÿ°T?ú>zø_äXýQú†ò4_ágÞ:/ü¬?ëÞ/ýVfh¿ò°ÿ¯x¿ôZuöÔ~|%oŽ^¡EV†AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÿÕýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(æOÚ»þIÞŸÿaXôDõÐ~ÍòJlëâçÿFçÿjïù'zý…aÿÑ×Aû5ÿÉ)±ÿ¯‹ŸýkàhÿÉG?ú÷ú£ôZßòLÃþ¾~Œ÷º(¢¾øüè(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+óïö®ÿ’‰§Ø*ý=~‚Wçßí]ÿ%Nÿ°T?ú>zø_äXýQú†ò4_ágÞ:/ü¬?ëÞ/ýVfh¿ò°ÿ¯x¿ôZuöÔ~|%oŽ^¡EV†AEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPÿÖýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šü1ÿ‚šéÚóþÑß<ð×ÅÞ Ñµï‰‹g©[Ùê÷qZ, =­¥´‘Û¬‚83(;K6NM~çQMEŠ‹œ(d’x÷<šuQEQ_,|}ý«¼ðÇ þêš]Þ¹®üLÔ—O²·²h÷@X¡ó¥2á7ÌõÃtÐÔôQ_•´/í—ñcÅÿâý?c[kKŸF\k^ ¼A5¦’#˜*d-#ÌwWȉQ¤<~«Ñ_ŠŸµ'€¿oÙ·áŒüñûXñÞ£gw:Žžt[QÙÎÅ–Î¥Uö«!^CnÈÆì€Ç‰‡ü:‹ãõÚ^ü@6îú´ˆ¶è¢i%wHÀ´ÝÆQ2ƒnrI$ù÷†?jïx¿ö£ñ'ìµ éwwZ¿…4Ѩ_êJÑ›(Ïî3Þß¼„SÇ ‘Øàêz*½ÝÝ­…¬×ײ¬öèÒI#¨ˆƒ,ÌO2I¯š?eoÚõ‡ƒµ¿x7E¾Òt­#U—JI/|¼Ü´QÇ)’0ŒH]²¯ ‚3õÀÓôQEQPÜ@—6òÛHYVUd%£Ã†\}95øuûXxƒSÿ‚„|cÒôëú¿>G}cko©j×wè.žá-•\Ï#o åÜm-“òƒ’Fk÷€ (¢€ (¢€ (¢€ +ñ—áÄ¿Úóþ a®ü—⎥âÏø6Ö{ûÈ®­¬£2+ÙEˆœÛÁú›«¤\¤ìÇ­~ÍPEPEPExnûE|-Õ¾;곆4þ7ѬR»·[y 0ÀË Ó°6Ù£8'ø‡zöùeŽÞiœG`³3’I= Ñ_”“þÛÿi‹¿ÂoØoúUî“áÆÛªøÃÄVÓPå€ò#€†`åHˆí& Õzç×ö„ýºþþÕß?gß‹#¾+Ò~ Ì¡§ÙÜ@ÂÒ&&ð§ïdÐÆ¥°ÈêASžN?^袊+†ñ?ÃOøÊþ=OĺR_]E…]žE"5bÁpŒV'ñ®æŠÇ†§V<•b¤»5Ìߊ«F\ô¤âû§gøÃvñ$®ØãPª=¤¢ŠÙ#Š( AEPE|Qû_|iý¤¾_xFýœ¾Ýx–îxµ+™ì//-4èP‘¼­i$^^ó+6çlmFú×ÚÄ…±ÀM-áß¿hŸ…ß´v‰¬xáMôÚ–›¢j¦Ï<–ò@q,‡Ë.uÚêr;ê+­ø¯ñOÁ>kþ!^ CD{™U Œ<Ù$UEå™Õ@Íz%ÈøÆúįè|,òI£øŠÎû7š&†F‚áÆÅ\©šë¨¢Š(¢¼söMi~ x·RÐtÏ„Zf” ±b—wêºb¿lëùýý‚þ3|/ð¿ÅÚKö¥øÇâm uMn++Q1/pRþòâcPFi>ä*+`!'…&€>ðÿ‚~Ö,ø áï ü5ø; Þ|Qø“t,t€Qeû*3¬Fq­#I"G ¸ÚX³Boœ~?þÌž:øû-ø‹ã¦©ñÃÆ—ßü=ok}6¥ý·pºs\És=¬v§ƒó n§i .c®3öœÕô«oø+gÁKïÜÇgáû}*Åí&¹m ¯Ì —©7EF}qšçடµ‘â¯[ü øex5>ËS¶“囉lá¸Ù,–zsJ¤«JÆ7™Ô“ÊPyÜßÿðMX|quû'x{Æ5»ý{[ñ…æ¡«É>£q%ÌÁ&¢ˆ±É(ã.N2M|3ðWÅw_¶í=ñkÁþ*x“ÂRxsRšÏþÒ5VÑ¢–ÚÞyãºÄÚ'€"çy%˜åï=ö˜ýg_‚÷^O.´> øwDW·ÒT\¼k0ŽÎY2°<²I‚Éæî²ØÍ|Ùûv~Ì?þ1üÔ¿ljIáOéšBx†ÓY³•aMMbˆMsll‡$2£ …T—ïß²ì÷ñëàwĉö_¼y«x¯ÀóKo„ÿ´µ'Ô&[vi^W‘$ÈŽTZ®w0P1_–úìçqñ‡þ mwðro‰¾0ÕàðVš·—^!›QŒë6²L‹i:B±À±Ü\ĸXø%ñó×ø'çÄÿ|_ý“¼ã‰SIy®N·vÏw(Ä—qZ\ÉS?÷™‘fþ&ºšüæÿ‚||Gð þÓ?µÆÿ‰þ!±Ð/õÄm:[ùv3^ÝI0a¬~D ’>Py# ¡Íàøÿb€¾ ø£ÇóØi³ê0ÉâÍLêOÅ´.-á„”@‰$¬¡°2Iè|ƒÿoøböÿ |cñÿÄEîüCã½Z[qu7Ï#ÚÙÒ>óÎe¹’O3ÔÆ¤ò8ú7öžñ†™ûOÁ>¼ã„Ñ^ÜØk\·V‚kY-çš6íd•–)b®9B3¹HÇÒñ ‘T¨Ì%FÁ‚¼oügã>áoÙþO‚ý¿Æß®-llôë|ÉuöT$–Qå°ì‚ù™ÎÐv¶>±ý—~¯ì×û0x?áÿ‰'Hæð¾—%Χ û‘ÜLò^]àŽª’Hà7p3ÇJüiø[ãoÚ“Á_¶‡ÄïÙsà¿õoK¨Åö%Öáv¢ m¾a 1ƒ"þ›þš•û>þÀÞñV­…F{âÍAþé‘.ï›él#\ú(¯ÿàÞ¹»øeñãv¸›õø…ây˜|ÒGh‚f`{šæ@}JóÐPš|¶øµðþ w¥|²øŸâxkVÓ&ºÕWZ½’èút×*Ò+3"ȳ$e@;\)8';ÿðUy$ø×ÂKý¬,-µ Uû%ýþý×7ús´Ï‡Ýù‹F?^~?üxð·‹¿bˆÿþêGUÒ¯4 R +ÄŠX²í¾É¤U•QÆÇ$‚F229¯ÿ`/Ž?fߨ;Ã^'ñ‡ˆ ŽûZÕuŸOµ"ãRžþ[ƶŽ­ù®þLq7ÝáH9Æ ï¿o¿†ðËö$¼TøŸã/Â&ÀÔÑæ×åÔîbˆ.©+DdP3|¨È<½ÊAǘ~İk]þÏÿüwñcÇÞ¸×áMjmD־å2Ï'™î"ß½·ù‡~[' wþ IâÛ»_ÙûÁ>´/ ·ˆ<@³Î̬¿»´¶ª8##-(m¤g+Óƒ_~øWã·Àøá÷ìåá~-kX¿ÒÒ->ßKmH,l-†Én^ ËnˆÀcíÍ|ßûj~Ô<;ñÁÿ²gìßäÂÍøƒþ·P—4{7$ ‚`¯˜Q%–Ë7bÊGÂß¶Ÿ„¼Wû)Kðݾ|qñ—Š>5k:’Cugs«Év×±H¹ó €,#§Ø‘Âû•ÕˆùÊâo‡_~+ÿÁY~"øSö¿›H°›M‰ô†ŠùôÓ4ÉedÇèÈØ{s1*­ó0a×"¾Þøq¥Á>g¿ŽÞømð—Cƒ^ø—âI¦D»°i|Au§ïšêêYf6Š@*Û0À΢=Ï@Ïûn~Õ-û'ü Goç‹õ‰OÒm_-½‘ I+†1B¡˜Ž7©•Ý‘òDŸ²Å‰¿güyøóñŸÆ|RB½ñ‹ ^[ ?G–f¹ŽÜZÄ0›@œ&ÅÎåM¸Þ|ûþ ÍukÇ/Ù©<\âǪÏ%ä’œBí–i/ž0°€yìMz§üûö°ðî‰ð;Ä?>_E¯ø‡Ä‘^K x´i£‰ä¸xÉUk—‘ %d-ÀÛ¸þÏàûøþ øóâî¸ïqªxßÄ q)-$ñØÇ»Íf<’ÓÜM“ê ~ÇWçgìã„^øðöÓ!üXø÷ðÿã;xKY{ѧ\ëshvÚœš²Ü9žLE;ª[ªˆá`‰*Œ*Œýóÿø—ãÿÙçö_x‹ÅÏâxºXü?>´¶ÑXÉqÇ™5Ü¢Þ’56è`ÂôÞsÍUÿ‚hê_þþÁ:_Äïkvš“®êšŽ£{ws HÒ_´ÿgÆ„òK¶L(Éç§Zñ?ø-½µÝçÃÿ„úÄ dÑ“S¿Ži#ù”¼ðDÐG(’õí@}ÿÁ?>iÿÿe¯èël±k>!µ[ÕdÛ‰ëPA*£ûÃH}>L÷5ö ΋£^jVzÍÝ…¼ú†ž[\I4Ð F×òä#rn6Ò285^ÛTÐ,ü9µow:$6‹p—ÕmÖÕcÞ$ÞNÐ9ÝœcšñßÙßöŽðí9á-[Ç? íµô]/V¹ÒV{øc„]½²Ææ{uI$c Òá9ò?íEûX|W?]ëÝÉ}ª*6E¬wA<›rTÅmó0ë°©‘ÐÚSRý§5È4­#öz¿Ó<#áÛ>ëRÕ¼_z#¼’Ñ!MÑÛÛÙ8;žPwyŒ¥Bƒ÷X ßœ_±÷íYûh~ÕŸ _áW‚u;kOéWs>±ãFÒ)#°Ó$>Ç VÈ‹÷²È&eÀ6Y·/èÿíÝãØ¾þÇ¿õ»FòI¤6“j‚¯©²Ø¦Ïtîè=«Èà›þ ѾþÚ‹µhÅ«ë6÷Þ(ÔåÀÄûš&ú H¢ëïë@ |ý±?mhþ-üBý’žâÛâWíµ+7KÖ/"Š/ìÉ ííÑ…QžÝTD`[~’Dmëß¾2~Öÿ ¿à ÚOì¿ñ›â?tØMw4‰cªAþƒ5ÜrD±"4E^,©b¬­œn+…ÿ‚=xb_é¿i zÚÏ5ù-­ËOôÛ„ó¶InWqîc•ÏþÌÞ!о ÿÁEÿh¯Ú?ÆZ¾Ÿá߇6òéIyw*ÃolE°Žc#¨¦9$}òzУ~Þ¿¿m¿†þñÅ_ xƒOøkàoߨYé6ÐÅo¨ê> k‰5Ì’J޶ñ©cˆ¶‚B0`FòúÆÏÛwöŽýœu_ µË?…^🇚ýæ’Ö9¯üE§Z™u'„Ȭ–¶‹2I$ ¹\±Úp²ÿÁ`üW?Š<ð‹à…®æëâ¼·P˜˜:È"ÛÁÊðQä¼ ¸àíÈé_E~ßš¦û?þÀ~"ðg…À´¶:nŸám:1˜fhíÝxïöU”ŸS@*~Ëÿ´¿í³ûfü-´ðOÃÝJÏÂzŸ‡L±ø‡Çw¶qKç;1k[k;E_(Îc9ö 63.ÿkÿ‚bþÐß>0?ÅoülÖÄ·Ÿu Kk}HElþ{]G,e¢TWUk`ÈÅwaŽN6è_³6Ÿgû-Á94ßÇŠ÷Oð­ßŠ®7š[»¸ö5ö€hâèÒ¼_þ !¦øoáÿìß‹Õ:G5÷Ø—ÉXbCJáâÈ\œÅfxgâ·í/üêÙÙ~'^kþÑR]VþÕìlaò ’Ä\¥³<0+If…7n Tòwg?IÁHuoŠžý›üIãxö?iš]”°jP.ž—WZÛå†Öa¹yÚœ»èŒØl‚¥A¯Œÿ`?xwÄ?´÷í;ûUxëU´Ò4›[ó¦A}{:AV×·²V–BmKkt#$€=+Ò?à³¾6›Iýœ¼1àÛÿ„«_„ÊÛ¾V·³†IHÏLy­gÚ€8Øö_ý¨ôßÙûÁþ+ðÇ!àOx¢f֦ГÖWÎVY<½ßk·–žƒ·dóðW¸¾2M¨ø/Àøé/|7ñVXôÏ ÛéénñËhEæ\^ KŒÏ8*…An„ 5ûáMgá¯Á­+áÏÀ‹½zÎËV›L‡MѬž@³^&—lªæ$ê@DÎO^ƒ'Šü±ý£å‹â·üŸàÃ[÷Q§x>ÎD#ž Ü s©äݼˆÔöçìõû=~ÒuÓÇ?‡Œü Ø 4ü9e¦¨H¡Û´Æï.Ø@­“×>7ñ;öŸøÅñ«öоý“d«‹} ãÃ9øÂNTÂÉ ¬ o*»y?- eUJûâ?Š~ ÕµxKº½®±âÛ¤º†›¡§·iãg$¡“iã¨ïŒŠü ÿ‚dþÎú×ÇOøûâ5·ÅïxT¿ÖþÉ©Ãá똭§ºT…n#ži@Yî& Œ ‚y=>«ðÅïÚ7áü@ý•¼Kñ-þ+øgÄ:{ÞÞ5Ý­½ÖŸ!´¸œÐQ”´¶Óƒ ƒ_@~Úßµï‹~øƒÂ>i°k¿> ȉcÇÍŸo+ùKq*äeƒlÜv(GwùT+îþËÿ dO„?¼aàï„ÚÏü$ÿ,`Ž_ßê7o¨êâ9œ‚%¸*"V2ß$A[vÏ4}Êø³áÃGãø,÷ޝ/iŸ¾,|4ý¼wö¢|Ajš´ñÑG©ÝºZAlpe‚;†iG;|Ä Üï k¾ ¾ûo†¼%©YÝ·†=>îÚÖd•¬dHÕ’•I1°Œ© Ø8 ¦Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( ÿÐýü¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¯“<3û ~Ëø£uñ“Gð·ü%W7¨-ÄóOqR1v–ydhcm䲕Oý͸õóÏÇÙWàGí+šŸ¼0šÔÚFï²\$óZÜD®AtÛ¼lÈØå•Ï Ícj?±ŸìÏ©ü‹à4¾µ‡ÁP\¥êYÃ$ñH/J‹†¹Iï6ÒT»ÈX¯ÊI^+éú(ç +öDý›ô_…7¿tßXÃàÍJDšîÇ2“s4d2I4åÌîêTmf‘€WŒÙÁ3cK9`VðUÅÕ£™`°¸Ö59¬âœ–µÉSžá²q_zÑ@ÚF¤øK´Ðô(tí:Â%†ÞÚÚ5ŠbA…Hã@UG_x‹þ ãû x¯â-ßÅáüZÕýË^\¡ºº[9î]·4’Z¬¢Y¹eÛµ‰%”’kíJ(¥­…Œ:e•¼vövñ¬1É"AµQP€0Šø‡Å?ðM¯ØÛÅšåæ¿uà?ìëI™î£Óoïlm¦,rA·‚d‰W<áGµ}ÓExGÁ?Ù—à_ìëü?ü)‡N¨±­Ô«,÷N°–([‰$r± gÕÏŸ³·ÁŸÚ'E´Ð¾0øf ~ =ÚKI Ëͳ¾7n hå@ÛWr†ÚÛFàp1ítPÌ? ?coÙÇà¿ˆÓÆ~ð‚'ˆÑS¾¹¹Ôo8ÁÙ-ܲ˜É_—)´à‘Мý â_é/ðæ«á?@nt½jÒ{+¸„’Þæ3ª$•в1•ƒ ƒƒ[TP•|ø%ð¿àƒÿáøG¢ B7]›q<÷$Ï0Pîd¹’Y !TrØ1X¿ ÿgƒ|Sâ¯|7ðïöN¹ãiþÓ«Ý5ÝÕÓÝJd’bعšU/+±…##…Ç·Ñ@3üiý¿goÚÅ:OŒþ/xI5í[Fƒì°Jn®mós"Ç"Á,bEWf`8Ü{IñöiøñWá¶™ðƾ†ãÁº4°Íg¦ZÍq§Á Û£Çß±I mU‘¾Rv’rA î”P¬|1ð>½ðÚ„:ž™¿Â7:hÒÅ&š lB$K¬ ycnC†Çz©ðŸáÃÏþ³øsð·H‡¬i!¶MpUç‘¥™nIX–b~f8=Šù9?a¿ÙcþÅïÆËZÝø¶þñµ'¸šy ûc¶öm^Cn¿ÏŸ/†ù†5âïìcû6|wñÍ—Äoо\×l`Ke”Ý\À’E3"Ë2¢I´±ÆàI¨¾¢¢€9kßø?QðlßîôkFðÅÅ“iϦˆ•-~ÆÉå˜Jªlù@Àé_2ü'ýÿe‚ž/Çžð,Pk–;ÚâîææøÚ·÷ [™dTaÙÀÞ;5}‡EyÆŸ? ¿hO|\У×t”nbC$É è ¬‘K $ˆØb ‘\×À¿ÙoàGìÝiwoð°h³ê-ÍÛ<·7s(9 ÓÎÏ&ÀyØLóŒ×Ð4PÍ¿dÙãö‘»³Õ~.øJ-WT°É‚ú§´ºX²XFÒÛ¼fD’«&ऒ Mnüý˜~~ζ·6ÿü#k Ëz¡n.·Iqw2ƒ¯s;I)LòvÐz ÷ª(Ç>6|øIûDxZ?|_ðüzöo0¸ƒ2IÐL]ñM $ˆH8 6pÀŠàü/û~Ìþø[®üÐ< k…|N¨º¤-$òOyå°xÌ·M!¸&7£Äƒaå6×ÓôP„|ý™¾~Îz}݇Áß Ûè'PÛö©ÃËqu>Ϻ$žw’B ’BnÚ $š÷z( Š( Š( ¼÷âÇÃ/ |føqâ…Þ3IFñ%£Ú\X,¨d˜0èJ ‚8¯B¢€>$ý›¿`‚Ÿ³^­oâ}ãTñOˆ,-ÞÖÆû\¸[–Ó ‘™¤ŽÊ$Hã€9c¸…,rÃp Àð¿ÿà™¿þ!|]Õþ/Ûë¾#ð­ï‰ÛX³ÑoÒÖÚøÌCL˜šEYØfe†<€§šý¢€??~'Á6?gˆº¿ƒõ}*M_À§ÁzpÒm#ðíÔvbK Ò¾Éâ–BìÓI¾@ÁÜ;o,NF§Ä¯ø'oìýñÂ~ðŽ’Ú·‚ øs涉6v¶Ó[Í;Ç$“³Ë¥æg‰Êy‘Ù¯»¨ €­?àžþ× øÃ_<®|;ñ±»Ñü,ñŸ¬ê®½ãK-äÞiºN·wºeµÉäMöhb‰$z·Ê{©â¾ÖøÇðkáçǯ_ü6øŸ¥WD¿Úåw4rÅ4|Ç4R.$CÐpARAõ(ó&ëþ á;ÿ §Ã{ÿ?î< ¬qèm¬Âm$ ¬E ±C6®Ì/ny¯µ¾|ðìíðâÇáwÃxg‹F°’i¹—ΞIgrîò>$“Žâ½‚Šøâ/ì#ðïÅÿ¯~8øÅž%øcã-^3¥wá›ÔµKå`¡ŒÑɃslRv•RÀ;)š½àOì‰ð‡àµ«øÇÉ}âkìZûÄ:íÏÛõYÃ`²™Ê Ub2ÛwnÎÕÇÔPÏ¿´·ìáàÏÚ›áÊ|0ñizJßC~Ï¤Í 3Èð+ª£´ðΦ<¾âƒ¹TäAéõƒ>Õ>Oû?Ãsy§xnmxpKjñ¥Üv"ØZüŽÑ²ò†2c#ýšõº(Ãÿg€ý™¾Xü'ø5åÖ•e5ÅÇŸ¨I—RÉs!‘Œ p¡ÆB®|ªÉÉ?-KÿÄýœï~,x‡â~¯q­_Zx›R:­ï‡šñ#Ñf¹2™ÿ{q#ËÊÌËÈTdŒâ¿Eh ”¾&~ÇŸ ~*üiðÆÿÞê–ú—Ãu¶V›i-¼zX6“™ãi!kv“!ÊýÉPa0s¯ûPþË>ý¬ü¦ø â&­«éš^—¨.¤ŸÙA É:E$+æàœ +`'­}-Ep~&ømá?|4Ô~jöż9©érhò‡k-¤ùFÁÚÊ¿u±Á×ɳÇüÇàOìï¯Xx«K¹Õ¼Y«èÂUÒå×nRâ-3Ï9•¬àŽ8¢‰ÜòÏ´¶I ŒšûŠüäðßüûönÐ~$j^>º—YÕôýGR:±ðíÝÜgD!Ýн´q!•b.|µ‘ØʶàN~†ý§¿eo†µw€í<ñ ®ì¢Ó.VòÊïO‘"¸·”!C·ÌI£+a•”ƒÁ ô­ñßìëûü"ýœõ‰<_¥\ê~-ñt–âÑu­~ä^]ÛÚ¨À‚Û ‰ xãå]ØùKm⹟ÚGöøOûHüEÒþ,jšî¹á/éGmöÝæ8X¢f([ÌŠB² bЩÇ }ÕExÀÙëá‡ìåá)<#ðÓOxîSq}{u'Ÿ}pzÍs9»rp ¹;Td×Ǿ,ÿ‚ZüÖñ÷þ ñðãßÅ;oŒ×æ¿àßÇCq{áû¸íd¹HÓÊVc$RGˆ÷¡A†Œ}áE~~xûþ ­û9xÓÀþ ð>”5O ]Ï{g¨i)£5ÅшÏ5ÅÄÑJd•ÚØIÃ!U BµõÁ€ÿ ¿gÏÿÂðÛOkh'™®o.®$3Þß]I÷纾i$o^(Šö:(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(ÿÙapache-zookeeper-3.9.4/zookeeper-specifications/protocol-spec/verification-statistics.md0100644 0000000 0000000 00000006313 15051152474 032310 0ustar00rootroot0000000 0000000 # Verification Statistics ##### Experiment configuration Our statistical results include: diameter of the system states that have been traversed, the number of states that have been traversed, the number of different states that have been discovered, and the time spent in the experiment. The machine configuration used in the experiment is 2.40 GHz, 10-core CPU, 64GB memory. The TLC version number is 1.7.0. ##### State space constraints in model checking Due to the state space explosion in model checking and the complex actions of Zab protocol, as well as unlimited number of rounds and unlimited length of history, it is impossible to traverse all states. We try to let models can tap into larger state space. See CONSTANT *Parameters* for details. ##### Verification statistics of model checking | Mode | TLC model | Diameter | num of states | time of checking(hh:mm:ss) | | ----- | ---------------------- | ------------- | ------------------ | ------------------ | | BFS | (2 servers,3 rounds,2 transactions) | 59 | 7758091583 | 17:28:17| | Simulation | (2 servers,3 rounds,2 transactions) | -| 6412825222| 17:07:20 | | BFS | (3 servers,2 rounds,2 transactions) | 19 | 4275801206 | 09:40:08| | Simulation | (3 servers,2 rounds,2 transactions) | -| 10899460942| 20:15:11 | | BFS | (3 servers,2 rounds,3 transactions) | 22 | 8740566213 | 17:49:09 | | Simulation | (3 servers,2 rounds,3 transactions) | - | 9639135842 | 20:10:00 | | BFS | (3 servers,3 rounds,2 transactions) | 21 | 7079744342 |14:17:45 | | Simulation | (3 servers,3 rounds,2 transactions) | - | 6254964039 | 15:08:42 | | BFS | (4 servers,3 rounds,2 transactions) | 16 | 5634112480 |15:42:09 | | Simulation | (4 servers,3 rounds,2 transactions) | - | 3883461291 | 15:52:03 | ##### Verification statistics with parameters (count of servers, MaxTotalRestartNum, MaxElectionNum, MaxTransactionNum) | Mode | TLC model | Diameter | num of states | time of checking(hh:mm:ss) | | ----- | ---------------------- | ------------- | ------------------ | ------------------ | | BFS | (2,2,3,2,termination) | 55 | 10772649 | 00:02:21| | BFS | (3,1,1,2) | 45 | 9602018536 | 31:01:57| ##### Issues Besides, we have found several issues related to the ambiguous description of the Zab protocol. Details can be found at [issues.md](issues.md). apache-zookeeper-3.9.4/zookeeper-specifications/system-spec/doc.md0100644 0000000 0000000 00000004110 15051152474 025657 0ustar00rootroot0000000 0000000 # ZooKeeper's System Specification of TLA+ ## Overview ZooKeeper's system specification of TLA+ focuses on the implementation of the Zookeeper Atomic Broadcast(ZAB) consensus protocol (or, [ZAB1.0](https://cwiki.apache.org/confluence/display/ZOOKEEPER/Zab1.0)). As is shown by the informal description of [ZAB1.0](https://cwiki.apache.org/confluence/display/ZOOKEEPER/Zab1.0), the implementation of ZAB that used in ZooKeeper production differs a lot from its original design. We make this system specification to grasp the core logic of the ZAB's implementation especially. The [sysetm specification](zk-3.7/ZkV3_7_0.tla) written in TLA+ is precise without ambiguity, and testable with existed tools like the TLC model checker. (We have done a certain scale of model checking to verify its correctness!) The system specification can serve as the super-doc supplementing detailed system documentation for the ZooKeeper developers. It can evolve incrementally without high cost as the system develops over time, continually ensuring correctness for both the design and the implementation. We have also made a formal [specification](zk-3.7/FastLeaderElection.tla) for Fast Leader Election in Zab since ZAB 1.0 depends on FLE to complete the election phase. If you have any question or find any problem of the specification, please contact us. apache-zookeeper-3.9.4/zookeeper-specifications/system-spec/zk-3.7/FastLeaderElection.tla0100644 0000000 0000000 00000104164 15051152474 031732 0ustar00rootroot0000000 0000000 (* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *) ------------------------- MODULE FastLeaderElection ------------------------- \* This is the formal specification for Fast Leader Election in Zab protocol. (* Reference: FastLeaderElection.java, Vote.java, QuorumPeer.java in https://github.com/apache/zookeeper. Medeiros A. ZooKeeper's atomic broadcast protocol: Theory and practice[J]. Aalto University School of Science, 2012. *) EXTENDS Integers, FiniteSets, Sequences, Naturals, TLC ----------------------------------------------------------------------------- \* The set of server identifiers CONSTANT Server \* Server states CONSTANTS LOOKING, FOLLOWING, LEADING (* NOTE: In spec, we do not discuss servers whose ServerState is OBSERVING. *) \* Message types CONSTANTS NOTIFICATION \* Timeout signal CONSTANT NONE ----------------------------------------------------------------------------- Quorums == {Q \in SUBSET Server: Cardinality(Q)*2 > Cardinality(Server)} NullPoint == CHOOSE p: p \notin Server ----------------------------------------------------------------------------- \* Server's state(LOOKING, FOLLOWING, LEADING). VARIABLE state VARIABLE history \* The epoch number of the last NEWLEADER packet accepted, used for comparing. VARIABLE currentEpoch \* The index and zxid of the last processed transaction in history. VARIABLE lastProcessed \* currentVote[i]: The server who i thinks is the current leader(id,zxid,peerEpoch,...). VARIABLE currentVote \* Election instance.(logicalClock in code) VARIABLE logicalClock \* The votes from the current leader election are stored in ReceiveVotes. VARIABLE receiveVotes (* The votes from previous leader elections, as well as the votes from the current leader election are stored in outofelection. Note that notifications in a LOOKING state are not stored in outofelection. Only FOLLOWING or LEADING notifications are stored in outofelection. *) VARIABLE outOfElection \* recvQueue[i]: The queue of received notifications or timeout signals in server i. VARIABLE recvQueue \* A veriable to wait for new notifications, corresponding to line 1050 in FastLeaderElection.java. VARIABLE waitNotmsg \* leadingVoteSet[i]: The set of voters that follow i. VARIABLE leadingVoteSet (* The messages about election sent from one server to another. electionMsgs[i][j] means the input buffer of server j from server i. *) VARIABLE electionMsgs \* Set used for mapping Server to Integers, to compare ids from different servers. \* VARIABLE idTable serverVarsL == <> electionVarsL == <> leaderVarsL == <> varsL == <> ----------------------------------------------------------------------------- \* Processing of electionMsgs BroadcastNotmsg(i, m) == electionMsgs' = [electionMsgs EXCEPT ![i] = [v \in Server |-> IF v /= i THEN Append(electionMsgs[i][v], m) ELSE electionMsgs[i][v]]] DiscardNotmsg(i, j) == electionMsgs' = [electionMsgs EXCEPT ![i][j] = IF electionMsgs[i][j] /= << >> THEN Tail(electionMsgs[i][j]) ELSE << >>] ReplyNotmsg(i, j, m) == electionMsgs' = [electionMsgs EXCEPT ![i][j] = Append(electionMsgs[i][j], m), ![j][i] = Tail(electionMsgs[j][i])] ----------------------------------------------------------------------------- \* Processing of recvQueue RECURSIVE RemoveNone(_) RemoveNone(seq) == CASE seq = << >> -> << >> [] seq /= << >> -> IF Head(seq).mtype = NONE THEN RemoveNone(Tail(seq)) ELSE <> \o RemoveNone(Tail(seq)) \* Processing of idTable and order comparing RECURSIVE InitializeIdTable(_) InitializeIdTable(Remaining) == IF Remaining = {} THEN {} ELSE LET chosen == CHOOSE i \in Remaining: TRUE re == Remaining \ {chosen} IN {<>} \union InitializeIdTable(re) IdTable == InitializeIdTable(Server) \* FALSE: id1 < id2; TRUE: id1 > id2 IdCompare(id1,id2) == LET item1 == CHOOSE item \in IdTable: item[1] = id1 item2 == CHOOSE item \in IdTable: item[1] = id2 IN item1[2] > item2[2] \* FALSE: zxid1 <= zxid2; TRUE: zxid1 > zxid2 ZxidCompare(zxid1, zxid2) == \/ zxid1[1] > zxid2[1] \/ /\ zxid1[1] = zxid2[1] /\ zxid1[2] > zxid2[2] ZxidEqual(zxid1, zxid2) == zxid1[1] = zxid2[1] /\ zxid1[2] = zxid2[2] \* FALSE: vote1 <= vote2; TRUE: vote1 > vote2 TotalOrderPredicate(vote1, vote2) == \/ vote1.proposedEpoch > vote2.proposedEpoch \/ /\ vote1.proposedEpoch = vote2.proposedEpoch /\ \/ ZxidCompare(vote1.proposedZxid, vote2.proposedZxid) \/ /\ ZxidEqual(vote1.proposedZxid, vote2.proposedZxid) /\ IdCompare(vote1.proposedLeader, vote2.proposedLeader) VoteEqual(vote1, round1, vote2, round2) == /\ vote1.proposedLeader = vote2.proposedLeader /\ ZxidEqual(vote1.proposedZxid, vote2.proposedZxid) /\ vote1.proposedEpoch = vote2.proposedEpoch /\ round1 = round2 InitLastProcessed(i) == IF Len(history[i]) = 0 THEN [ index |-> 0, zxid |-> <<0, 0>> ] ELSE LET lastIndex == Len(history[i]) entry == history[i][lastIndex] IN [ index |-> lastIndex, zxid |-> entry.zxid ] RECURSIVE InitAcksidInTxns(_,_) InitAcksidInTxns(txns, src) == IF Len(txns) = 0 THEN << >> ELSE LET newTxn == [ zxid |-> txns[1].zxid, value |-> txns[1].value, ackSid |-> {src}, epoch |-> txns[1].epoch ] IN <> \o InitAcksidInTxns( Tail(txns), src) InitHistory(i) == LET newState == state'[i] IN IF newState = LEADING THEN InitAcksidInTxns(history[i], i) ELSE history[i] ----------------------------------------------------------------------------- \* Processing of currentVote InitialVote == [proposedLeader |-> NullPoint, proposedZxid |-> <<0, 0>>, proposedEpoch |-> 0] SelfVote(i) == [proposedLeader |-> i, proposedZxid |-> lastProcessed[i].zxid, proposedEpoch |-> currentEpoch[i]] UpdateProposal(i, nid, nzxid, nepoch) == currentVote' = [currentVote EXCEPT ![i].proposedLeader = nid, \* no need to record state in LOOKING ![i].proposedZxid = nzxid, ![i].proposedEpoch = nepoch] ----------------------------------------------------------------------------- \* Processing of receiveVotes and outOfElection RvClear(i) == receiveVotes' = [receiveVotes EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] RvPut(i, id, mvote, mround, mstate) == receiveVotes' = CASE receiveVotes[i][id].round < mround -> [receiveVotes EXCEPT ![i][id].vote = mvote, ![i][id].round = mround, ![i][id].state = mstate, ![i][id].version = 1] [] receiveVotes[i][id].round = mround -> [receiveVotes EXCEPT ![i][id].vote = mvote, ![i][id].state = mstate, ![i][id].version = @ + 1] [] receiveVotes[i][id].round > mround -> receiveVotes Put(i, id, rcvset, mvote, mround, mstate) == CASE rcvset[id].round < mround -> [rcvset EXCEPT ![id].vote = mvote, ![id].round = mround, ![id].state = mstate, ![id].version = 1] [] rcvset[id].round = mround -> [rcvset EXCEPT ![id].vote = mvote, ![id].state = mstate, ![id].version = @ + 1] [] rcvset[id].round > mround -> rcvset RvClearAndPut(i, id, vote, round) == receiveVotes' = LET oneVote == [vote |-> vote, round |-> round, state |-> LOOKING, version |-> 1] IN [receiveVotes EXCEPT ![i] = [v \in Server |-> IF v = id THEN oneVote ELSE [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] VoteSet(i, msource, rcvset, thisvote, thisround) == {msource} \union {s \in (Server \ {msource}): VoteEqual(rcvset[s].vote, rcvset[s].round, thisvote, thisround)} HasQuorums(i, msource, rcvset, thisvote, thisround) == LET Q == VoteSet(i, msource, rcvset, thisvote, thisround) IN IF Q \in Quorums THEN TRUE ELSE FALSE CheckLeader(i, votes, thisleader, thisround) == IF thisleader = i THEN (IF thisround = logicalClock[i] THEN TRUE ELSE FALSE) ELSE (IF votes[thisleader].vote.proposedLeader = NullPoint THEN FALSE ELSE (IF votes[thisleader].state = LEADING THEN TRUE ELSE FALSE)) OoeClear(i) == outOfElection' = [outOfElection EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] OoePut(i, id, mvote, mround, mstate) == outOfElection' = CASE outOfElection[i][id].round < mround -> [outOfElection EXCEPT ![i][id].vote = mvote, ![i][id].round = mround, ![i][id].state = mstate, ![i][id].version = 1] [] outOfElection[i][id].round = mround -> [outOfElection EXCEPT ![i][id].vote = mvote, ![i][id].state = mstate, ![i][id].version = @ + 1] [] outOfElection[i][id].round > mround -> outOfElection ----------------------------------------------------------------------------- InitServerVarsL == /\ state = [s \in Server |-> LOOKING] /\ currentEpoch = [s \in Server |-> 0] /\ lastProcessed = [s \in Server |-> [index |-> 0, zxid |-> <<0, 0>>] ] /\ history = [s \in Server |-> << >>] InitElectionVarsL == /\ currentVote = [s \in Server |-> SelfVote(s)] /\ logicalClock = [s \in Server |-> 0] /\ receiveVotes = [s \in Server |-> [v \in Server |-> [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] /\ outOfElection = [s \in Server |-> [v \in Server |-> [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] /\ recvQueue = [s \in Server |-> << >>] /\ waitNotmsg = [s \in Server |-> FALSE] InitLeaderVarsL == leadingVoteSet = [s \in Server |-> {}] InitL == /\ InitServerVarsL /\ InitElectionVarsL /\ InitLeaderVarsL /\ electionMsgs = [s \in Server |-> [v \in Server |-> << >>]] \* /\ idTable = InitializeIdTable(Server) ----------------------------------------------------------------------------- (* The beginning part of FLE's main function lookForLeader() *) ZabTimeout(i) == /\ state[i] \in {LEADING, FOLLOWING} /\ state' = [state EXCEPT ![i] = LOOKING] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = InitLastProcessed(i)] /\ logicalClock' = [logicalClock EXCEPT ![i] = logicalClock[i] + 1] /\ currentVote' = [currentVote EXCEPT ![i] = [proposedLeader |-> i, proposedZxid |-> lastProcessed'[i].zxid, proposedEpoch |-> currentEpoch[i]]] /\ receiveVotes' = [receiveVotes EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] /\ outOfElection' = [outOfElection EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0]]] /\ recvQueue' = [recvQueue EXCEPT ![i] = << >>] /\ waitNotmsg' = [waitNotmsg EXCEPT ![i] = FALSE] /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = {}] /\ BroadcastNotmsg(i, [mtype |-> NOTIFICATION, msource |-> i, mstate |-> LOOKING, mround |-> logicalClock'[i], mvote |-> currentVote'[i]]) /\ UNCHANGED <> (* Abstraction of WorkerReceiver.run() *) ReceiveNotmsg(i, j) == /\ electionMsgs[j][i] /= << >> /\ LET notmsg == electionMsgs[j][i][1] toSend == [mtype |-> NOTIFICATION, msource |-> i, mstate |-> state[i], mround |-> logicalClock[i], mvote |-> currentVote[i]] IN \/ /\ state[i] = LOOKING /\ recvQueue' = [recvQueue EXCEPT ![i] = Append(RemoveNone(recvQueue[i]), notmsg)] /\ LET replyOk == /\ notmsg.mstate = LOOKING /\ notmsg.mround < logicalClock[i] IN \/ /\ replyOk /\ ReplyNotmsg(i, j, toSend) \/ /\ ~replyOk /\ DiscardNotmsg(j, i) \/ /\ state[i] \in {LEADING, FOLLOWING} /\ \/ \* Only reply when sender's state is LOOKING /\ notmsg.mstate = LOOKING /\ ReplyNotmsg(i, j, toSend) \/ \* sender's state and mine are both not LOOKING, just discard /\ notmsg.mstate /= LOOKING /\ DiscardNotmsg(j, i) /\ UNCHANGED recvQueue /\ UNCHANGED <> NotmsgTimeout(i) == /\ state[i] = LOOKING /\ \A j \in Server: electionMsgs[j][i] = << >> /\ recvQueue[i] = << >> /\ recvQueue' = [recvQueue EXCEPT ![i] = Append(recvQueue[i], [mtype |-> NONE])] /\ UNCHANGED <> ----------------------------------------------------------------------------- \* Sub-action in HandleNotmsg ReceivedFollowingAndLeadingNotification(i, n) == LET newVotes == Put(i, n.msource, receiveVotes[i], n.mvote, n.mround, n.mstate) voteSet1 == VoteSet(i, n.msource, newVotes, n.mvote, n.mround) hasQuorums1 == voteSet1 \in Quorums check1 == CheckLeader(i, newVotes, n.mvote.proposedLeader, n.mround) leaveOk1 == /\ n.mround = logicalClock[i] /\ hasQuorums1 /\ check1 \* state and leadingVoteSet cannot be changed twice in the first '/\' and second '/\'. IN /\ \/ /\ n.mround = logicalClock[i] /\ receiveVotes' = [receiveVotes EXCEPT ![i] = newVotes] \/ /\ n.mround /= logicalClock[i] /\ UNCHANGED receiveVotes /\ \/ /\ leaveOk1 \* /\ PrintT("leave with condition 1") /\ state' = [state EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN LEADING ELSE FOLLOWING] /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN voteSet1 ELSE @] /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch) /\ UNCHANGED <> \/ /\ ~leaveOk1 /\ outOfElection' = [outOfElection EXCEPT ![i] = Put(i, n.msource, outOfElection[i], n.mvote,n.mround, n.mstate)] /\ LET voteSet2 == VoteSet(i, n.msource, outOfElection'[i], n.mvote, n.mround) hasQuorums2 == voteSet2 \in Quorums check2 == CheckLeader(i, outOfElection'[i], n.mvote.proposedLeader, n.mround) leaveOk2 == /\ hasQuorums2 /\ check2 IN \/ /\ leaveOk2 \* /\ PrintT("leave with condition 2") /\ logicalClock' = [logicalClock EXCEPT ![i] = n.mround] /\ state' = [state EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN LEADING ELSE FOLLOWING] /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN voteSet2 ELSE @] /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch) \/ /\ ~leaveOk2 /\ LET leaveOk3 == /\ n.mstate = LEADING /\ n.mround = logicalClock[i] IN \/ /\ leaveOk3 \* /\ PrintT("leave with condition 3") /\ state' = [state EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN LEADING ELSE FOLLOWING] /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch) \/ /\ ~leaveOk3 /\ UNCHANGED <> /\ UNCHANGED <> (* Main part of lookForLeader() *) HandleNotmsg(i) == /\ state[i] = LOOKING /\ \lnot waitNotmsg[i] /\ recvQueue[i] /= << >> /\ LET n == recvQueue[i][1] rawToSend == [mtype |-> NOTIFICATION, msource |-> i, mstate |-> LOOKING, mround |-> logicalClock[i], mvote |-> currentVote[i]] IN \/ /\ n.mtype = NONE /\ BroadcastNotmsg(i, rawToSend) /\ UNCHANGED <> \/ /\ n.mtype = NOTIFICATION /\ \/ /\ n.mstate = LOOKING /\ \/ \* n.round >= my round, then update data and receiveVotes. /\ n.mround >= logicalClock[i] /\ \/ \* n.round > my round, update round and decide new proposed leader. /\ n.mround > logicalClock[i] /\ logicalClock' = [logicalClock EXCEPT ![i] = n.mround] \* There should be RvClear, we will handle it in the following. /\ LET selfinfo == [proposedLeader |-> i, proposedZxid |-> lastProcessed[i].zxid, proposedEpoch |-> currentEpoch[i]] peerOk == TotalOrderPredicate(n.mvote, selfinfo) IN \/ /\ peerOk /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch) \/ /\ ~peerOk /\ UpdateProposal(i, i, lastProcessed[i].zxid, currentEpoch[i]) /\ BroadcastNotmsg(i, [mtype |-> NOTIFICATION, msource |-> i, mstate |-> LOOKING, mround |-> n.mround, mvote |-> currentVote'[i]]) \/ \* n.round = my round & n.vote > my vote /\ n.mround = logicalClock[i] /\ LET peerOk == TotalOrderPredicate(n.mvote, currentVote[i]) IN \/ /\ peerOk /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch) /\ BroadcastNotmsg(i, [mtype |-> NOTIFICATION, msource |-> i, mstate |-> LOOKING, mround |-> logicalClock[i], mvote |-> n.mvote]) \/ /\ ~peerOk /\ UNCHANGED <> /\ UNCHANGED logicalClock /\ LET rcvsetModifiedTwice == n.mround > logicalClock[i] IN \/ /\ rcvsetModifiedTwice \* Since a variable cannot be changed more than once in one action, we handle receiveVotes here. /\ RvClearAndPut(i, n.msource, n.mvote, n.mround) \* clear + put \/ /\ ~rcvsetModifiedTwice /\ RvPut(i, n.msource, n.mvote, n.mround, n.mstate) \* put /\ LET hasQuorums == HasQuorums(i, i, receiveVotes'[i], currentVote'[i], n.mround) IN \/ /\ hasQuorums \* If hasQuorums, see action WaitNewNotmsg and WaitNewNotmsgEnd. /\ waitNotmsg' = [waitNotmsg EXCEPT ![i] = TRUE] \/ /\ ~hasQuorums /\ UNCHANGED waitNotmsg \/ \* n.round < my round, just discard it. /\ n.mround < logicalClock[i] /\ UNCHANGED <> /\ UNCHANGED <> \/ \* mainly contains receivedFollowingNotification(line 1146), receivedLeadingNotification(line 1185). /\ n.mstate \in {LEADING, FOLLOWING} /\ ReceivedFollowingAndLeadingNotification(i, n) /\ history' = [history EXCEPT ![i] = InitHistory(i) ] /\ UNCHANGED <> /\ recvQueue' = [recvQueue EXCEPT ![i] = Tail(recvQueue[i])] /\ UNCHANGED <> \* On the premise that ReceiveVotes.HasQuorums = TRUE, corresponding to logic in LFE.java. WaitNewNotmsg(i) == /\ state[i] = LOOKING /\ waitNotmsg[i] = TRUE /\ \/ /\ recvQueue[i] /= << >> /\ recvQueue[i][1].mtype = NOTIFICATION /\ LET n == recvQueue[i][1] peerOk == TotalOrderPredicate(n.mvote, currentVote[i]) IN \/ /\ peerOk /\ waitNotmsg' = [waitNotmsg EXCEPT ![i] = FALSE] /\ recvQueue' = [recvQueue EXCEPT ![i] = Append(Tail(@), n)] \/ /\ ~peerOk /\ recvQueue' = [recvQueue EXCEPT ![i] = Tail(@)] /\ UNCHANGED waitNotmsg /\ UNCHANGED <> \/ /\ \/ recvQueue[i] = << >> \/ /\ recvQueue[i] /= << >> /\ recvQueue[i][1].mtype = NONE /\ state' = [state EXCEPT ![i] = IF currentVote[i].proposedLeader = i THEN LEADING ELSE FOLLOWING ] /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = IF currentVote[i].proposedLeader = i THEN VoteSet(i, i, receiveVotes[i], currentVote[i], logicalClock[i]) ELSE @] /\ history' = [history EXCEPT ![i] = InitHistory(i)] /\ UNCHANGED <> ----------------------------------------------------------------------------- (*Test - simulate modifying currentEpoch and lastProcessed. We want to reach violations to achieve some traces and see whether the whole state of system is advancing. The actions below are completely not equal to implementation in real, just simulate a process of leader updates state and followers get it. *) LeaderAdvanceEpoch(i) == /\ state[i] = LEADING /\ currentEpoch' = [currentEpoch EXCEPT ![i] = @ + 1] /\ UNCHANGED <> FollowerUpdateEpoch(i, j) == /\ state[i] = FOLLOWING /\ currentVote[i].proposedLeader = j /\ state[j] = LEADING /\ currentEpoch[i] < currentEpoch[j] /\ currentEpoch' = [currentEpoch EXCEPT ![i] = currentEpoch[j]] /\ UNCHANGED <> LeaderAdvanceZxid(i) == /\ state[i] = LEADING /\ lastProcessed' = [lastProcessed EXCEPT ![i] = IF lastProcessed[i].zxid[1] = currentEpoch[i] THEN [ index |-> lastProcessed[i].index + 1, zxid |-> <> ] ELSE [ index |-> lastProcessed[i].index + 1, zxid |-> <> ] ] /\ history' = [history EXCEPT ![i] = Append(@, [zxid |-> lastProcessed'[i].zxid, value |-> NONE, ackSid |-> {}, epoch |-> 0])] /\ UNCHANGED <> FollowerUpdateZxid(i, j) == /\ state[i] = FOLLOWING /\ currentVote[i].proposedLeader = j /\ state[j] = LEADING /\ LET precede == \/ lastProcessed[i].zxid[1] < lastProcessed[j].zxid[1] \/ /\ lastProcessed[i].zxid[1] = lastProcessed[j].zxid[1] /\ lastProcessed[i].zxid[2] < lastProcessed[j].zxid[2] IN /\ precede /\ lastProcessed' = [lastProcessed EXCEPT ![i] = lastProcessed[j]] /\ history' = [history EXCEPT ![i] = history[j]] /\ UNCHANGED <> NextL == \/ \E i \in Server: ZabTimeout(i) \/ \E i, j \in Server: ReceiveNotmsg(i, j) \/ \E i \in Server: NotmsgTimeout(i) \/ \E i \in Server: HandleNotmsg(i) \/ \E i \in Server: WaitNewNotmsg(i) \/ \E i \in Server: LeaderAdvanceEpoch(i) \/ \E i, j \in Server: FollowerUpdateEpoch(i, j) \/ \E i \in Server: LeaderAdvanceZxid(i) \/ \E i, j \in Server: FollowerUpdateZxid(i, j) SpecL == InitL /\ [][NextL]_varsL \* These invariants should be violated after running for minutes. ShouldBeTriggered1 == ~\E Q \in Quorums: /\ \A i \in Q: /\ state[i] \in {FOLLOWING, LEADING} /\ currentEpoch[i] > 3 /\ logicalClock[i] > 2 /\ currentVote[i].proposedLeader \in Q /\ \A i, j \in Q: currentVote[i].proposedLeader = currentVote[j].proposedLeader (* ShouldBeTriggered2 == ~\E Q \in Quorums: /\ \A i \in Q: /\ state[i] \in {FOLLOWING, LEADING} /\ currentEpoch[i] > 3 /\ currentVote[i].proposedLeader \in Q /\ \A i, j \in Q: currentVote[i].proposedLeader = currentVote[j].proposedLeader*) ============================================================================= \* Modification History \* Last modified Sat Jan 14 15:19:45 CST 2023 by huangbinyu \* Last modified Sun Nov 14 15:18:32 CST 2021 by Dell \* Created Fri Jun 18 20:23:47 CST 2021 by Dell apache-zookeeper-3.9.4/zookeeper-specifications/system-spec/zk-3.7/ZkV3_7_0.tla0100644 0000000 0000000 00000335064 15051152474 027504 0ustar00rootroot0000000 0000000 (* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *) ------------------------------ MODULE ZkV3_7_0 ------------------------------ (* This is the system specification for Zab in apache/zookeeper with version 3.7.0 *) (* Reference: FLE: FastLeaderElection.java, Vote.java, QuorumPeer.java, e.g. in https://github.com/apache/zookeeper. ZAB: QuorumPeer.java, Learner.java, Follower.java, LearnerHandler.java, Leader.java, e.g. in https://github.com/apache/zookeeper. https://cwiki.apache.org/confluence/display/ZOOKEEPER/Zab1.0. *) EXTENDS FastLeaderElection ----------------------------------------------------------------------------- \* The set of requests that can go into history \* CONSTANT Value \* Replaced by recorder.nClientRequest Value == Nat \* Zab states CONSTANTS ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST \* Sync modes & message types CONSTANTS DIFF, TRUNC, SNAP \* Message types CONSTANTS FOLLOWERINFO, LEADERINFO, ACKEPOCH, NEWLEADER, ACKLD, UPTODATE, PROPOSAL, ACK, COMMIT (* NOTE: In production, there is no message type ACKLD. Server judges if counter of ACK is 0 to distinguish one ACK represents ACKLD or not. Here we divide ACK into ACKLD and ACK, to enhance readability of spec.*) \* Node status CONSTANTS ONLINE, OFFLINE \* [MaxTimeoutFailures, MaxTransactionNum, MaxEpoch, MaxCrashes, MaxPartitions] CONSTANT Parameters MAXEPOCH == 10 ----------------------------------------------------------------------------- \* Variables in annotations mean variables defined in FastLeaderElection. \* Variables that all servers use. VARIABLES zabState, \* Current phase of server, in \* {ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST}. acceptedEpoch, \* Epoch of the last LEADERINFO packet accepted, \* namely f.p in paper. lastCommitted, \* Maximum index and zxid known to be committed, \* namely 'lastCommitted' in Leader. Starts from 0, \* and increases monotonically before restarting. lastSnapshot, \* Index and zxid corresponding to latest snapshot \* from data tree. initialHistory \* history that server initially has before election. \* state, \* State of server, in {LOOKING, FOLLOWING, LEADING}. \* currentEpoch, \* Epoch of the last NEWLEADER packet accepted, \* namely f.a in paper. \* lastProcessed,\* Index and zxid of the last processed txn. \* history \* History of servers: sequence of transactions, \* containing: zxid, value, ackSid, epoch. \* leader : [committedRequests + toBeApplied] [outstandingProposals] \* follower: [committedRequests] [pendingTxns] \* Variables only used for leader. VARIABLES learners, \* Set of servers leader connects, \* namely 'learners' in Leader. connecting, \* Set of learners leader has received \* FOLLOWERINFO from, namely \* 'connectingFollowers' in Leader. \* Set of record [sid, connected]. electing, \* Set of learners leader has received \* ACKEPOCH from, namely 'electingFollowers' \* in Leader. Set of record \* [sid, peerLastZxid, inQuorum]. \* And peerLastZxid = <<-1,-1>> means has done \* syncFollower with this sid. \* inQuorum = TRUE means in code it is one \* element in 'electingFollowers'. ackldRecv, \* Set of learners leader has received \* ACK of NEWLEADER from, namely \* 'newLeaderProposal' in Leader. \* Set of record [sid, connected]. forwarding, \* Set of learners that are synced with \* leader, namely 'forwardingFollowers' \* in Leader. tempMaxEpoch \* ({Maximum epoch in FOLLOWEINFO} + 1) that \* leader has received from learners, \* namely 'epoch' in Leader. \* leadingVoteSet \* Set of voters that follow leader. \* Variables only used for follower. VARIABLES connectInfo, \* Record [sid, syncMode, nlRcv]. \* sid: Leader id that follower has connected with. \* syncMode: Sync mode according to reveiced Sync Message. \* nlRcv: If follower has received NEWLEADER. packetsSync \* packets of PROPOSAL and COMMIT from leader, \* namely 'packetsNotCommitted' and \* 'packetsCommitted' in SyncWithLeader \* in Learner. \* Variables about network channel. VARIABLE msgs \* Simulates network channel. \* msgs[i][j] means the input buffer of server j \* from server i. \* electionMsgs \* Network channel in FLE module. \* Variables about status of cluster network and node presence. VARIABLES status, \* Whether the server is online or offline. partition \* network partion. \* Variables only used in verifying properties. VARIABLES epochLeader, \* Set of leaders in every epoch. proposalMsgsLog, \* Set of all broadcast messages. violatedInvariants \* Check whether there are conditions \* contrary to the facts. \* Variables only used for looking. \*VARIABLE currentVote, \* Info of current vote, namely 'currentVote' \* \* in QuorumPeer. \* logicalClock, \* Election instance, namely 'logicalClock' \* \* in FastLeaderElection. \* receiveVotes, \* Votes from current FLE round, namely \* \* 'recvset' in FastLeaderElection. \* outOfElection, \* Votes from previous and current FLE round, \* \* namely 'outofelection' in FastLeaderElection. \* recvQueue, \* Queue of received notifications or timeout \* \* signals. \* waitNotmsg \* Whether waiting for new not.See line 1050 \* \* in FastLeaderElection for details. \*VARIABLE idTable \* For mapping Server to Integers, \* to compare ids between servers. \* Update: we have transformed idTable from variable to function. \*VARIABLE clientReuqest \* Start from 0, and increases monotonically \* when LeaderProcessRequest performed. To \* avoid existing two requests with same value. \* Update: Remove it to recorder.nClientRequest. \* Variable used for recording critical data, \* to constrain state space or update values. VARIABLE recorder \* Consists: members of Parameters and pc, values. \* Form is record: \* [pc, nTransaction, maxEpoch, nTimeout, nClientRequest, \* nPartition, nCrash] serverVars == <> electionVars == electionVarsL leaderVars == <> followerVars == <> verifyVars == <> msgVars == <> envVars == <> vars == <> ----------------------------------------------------------------------------- ServersIncNullPoint == Server \union {NullPoint} Zxid == Seq(Nat \union {-1}) HistoryItem == [ zxid: Zxid, value: Value, ackSid: SUBSET Server, epoch: Nat ] Proposal == [ source: Server, epoch: Nat, zxid: Zxid, data: Value ] LastItem == [ index: Nat, zxid: Zxid ] SyncPackets == [ notCommitted: Seq(HistoryItem), committed: Seq(Zxid) ] Message == [ mtype: {FOLLOWERINFO}, mzxid: Zxid ] \union [ mtype: {LEADERINFO}, mzxid: Zxid ] \union [ mtype: {ACKEPOCH}, mzxid: Zxid, mepoch: Nat \union {-1} ] \union [ mtype: {DIFF}, mzxid: Zxid ] \union [ mtype: {TRUNC}, mtruncZxid: Zxid ] \union [ mtype: {SNAP}, msnapZxid: Zxid, msnapshot: Seq(HistoryItem)] \union [ mtype: {PROPOSAL}, mzxid: Zxid, mdata: Value ] \union [ mtype: {COMMIT}, mzxid: Zxid ] \union [ mtype: {NEWLEADER}, mzxid: Zxid ] \union [ mtype: {ACKLD}, mzxid: Zxid ] \union [ mtype: {ACK}, mzxid: Zxid ] \union [ mtype: {UPTODATE}, mzxid: Zxid ] ElectionState == {LOOKING, FOLLOWING, LEADING} ZabState == {ELECTION, DISCOVERY, SYNCHRONIZATION, BROADCAST} ViolationSet == {"stateInconsistent", "proposalInconsistent", "commitInconsistent", "ackInconsistent", "messageIllegal" } SyncMode == {DIFF, TRUNC, SNAP, NONE} Connecting == [ sid : Server, connected: BOOLEAN ] AckldRecv == Connecting Electing == [ sid: Server, peerLastZxid: Zxid, inQuorum: BOOLEAN ] ConnectInfo == [ sid : ServersIncNullPoint, syncMode: SyncMode, nlRcv: BOOLEAN ] Vote == [proposedLeader: ServersIncNullPoint, proposedZxid: Zxid, proposedEpoch: Nat ] ElectionVote == [vote: Vote, round: Nat, state: ElectionState, version: Nat] ElectionMsg == [ mtype: {NOTIFICATION}, msource: Server, mstate: ElectionState, mround: Nat, mvote: Vote ] \union [ mtype: {NONE} ] TypeOK == /\ zabState \in [Server -> ZabState] /\ acceptedEpoch \in [Server -> Nat] /\ lastCommitted \in [Server -> LastItem] /\ lastSnapshot \in [Server -> LastItem] /\ learners \in [Server -> SUBSET Server] /\ connecting \in [Server -> SUBSET Connecting] /\ electing \in [Server -> SUBSET Electing] /\ ackldRecv \in [Server -> SUBSET AckldRecv] /\ forwarding \in [Server -> SUBSET Server] /\ initialHistory \in [Server -> Seq(HistoryItem)] /\ tempMaxEpoch \in [Server -> Nat] /\ connectInfo \in [Server -> ConnectInfo] /\ packetsSync \in [Server -> SyncPackets] /\ status \in [Server -> {ONLINE, OFFLINE}] /\ partition \in [Server -> [Server -> BOOLEAN ]] /\ proposalMsgsLog \in SUBSET Proposal /\ epochLeader \in [1..MAXEPOCH -> SUBSET Server] /\ violatedInvariants \in [ViolationSet -> BOOLEAN] /\ msgs \in [Server -> [Server -> Seq(Message)]] \* Fast Leader Election /\ electionMsgs \in [Server -> [Server -> Seq(ElectionMsg)]] /\ recvQueue \in [Server -> Seq(ElectionMsg)] /\ leadingVoteSet \in [Server -> SUBSET Server] /\ receiveVotes \in [Server -> [Server -> ElectionVote]] /\ currentVote \in [Server -> Vote] /\ outOfElection \in [Server -> [Server -> ElectionVote]] /\ lastProcessed \in [Server -> LastItem] /\ history \in [Server -> Seq(HistoryItem)] /\ state \in [Server -> ElectionState] /\ waitNotmsg \in [Server -> BOOLEAN] /\ currentEpoch \in [Server -> Nat] /\ logicalClock \in [Server -> Nat] ----------------------------------------------------------------------------- \* Return the maximum value from the set S Maximum(S) == IF S = {} THEN -1 ELSE CHOOSE n \in S: \A m \in S: n >= m \* Return the minimum value from the set S Minimum(S) == IF S = {} THEN -1 ELSE CHOOSE n \in S: \A m \in S: n <= m \* Check server state IsON(s) == status[s] = ONLINE IsOFF(s) == status[s] = OFFLINE IsLeader(s) == state[s] = LEADING IsFollower(s) == state[s] = FOLLOWING IsLooking(s) == state[s] = LOOKING IsMyLearner(i, j) == j \in learners[i] IsMyLeader(i, j) == connectInfo[i].sid = j HasNoLeader(i) == connectInfo[i].sid = NullPoint HasLeader(i) == connectInfo[i].sid /= NullPoint MyVote(i) == currentVote[i].proposedLeader \* Check if s is a quorum IsQuorum(s) == s \in Quorums HasPartitioned(i, j) == /\ partition[i][j] = TRUE /\ partition[j][i] = TRUE ----------------------------------------------------------------------------- \* Check zxid state ToZxid(z) == [epoch |-> z[1], counter |-> z[2]] TxnZxidEqual(txn, z) == txn.zxid[1] = z[1] /\ txn.zxid[2] = z[2] TxnEqual(txn1, txn2) == /\ ZxidEqual(txn1.zxid, txn2.zxid) /\ txn1.value = txn2.value EpochPrecedeInTxn(txn1, txn2) == txn1.zxid[1] < txn2.zxid[1] ----------------------------------------------------------------------------- \* Actions about recorder GetParameter(p) == IF p \in DOMAIN Parameters THEN Parameters[p] ELSE 0 GetRecorder(p) == IF p \in DOMAIN recorder THEN recorder[p] ELSE 0 RecorderGetHelper(m) == (m :> recorder[m]) RecorderIncHelper(m) == (m :> recorder[m] + 1) RecorderIncTimeout == RecorderIncHelper("nTimeout") RecorderGetTimeout == RecorderGetHelper("nTimeout") RecorderIncCrash == RecorderIncHelper("nCrash") RecorderGetCrash == RecorderGetHelper("nCrash") RecorderSetTransactionNum(pc) == ("nTransaction" :> IF pc[1] = "LeaderProcessRequest" THEN LET s == CHOOSE i \in Server: \A j \in Server: Len(history'[i]) >= Len(history'[j]) IN Len(history'[s]) ELSE recorder["nTransaction"]) RecorderSetMaxEpoch(pc) == ("maxEpoch" :> IF pc[1] = "LeaderProcessFOLLOWERINFO" THEN LET s == CHOOSE i \in Server: \A j \in Server: acceptedEpoch'[i] >= acceptedEpoch'[j] IN acceptedEpoch'[s] ELSE recorder["maxEpoch"]) RecorderSetRequests(pc) == ("nClientRequest" :> IF pc[1] = "LeaderProcessRequest" THEN recorder["nClientRequest"] + 1 ELSE recorder["nClientRequest"] ) RecorderSetPartition(pc) == ("nPartition" :> IF pc[1] = "PartitionStart" THEN recorder["nPartition"] + 1 ELSE recorder["nPartition"] ) RecorderSetPc(pc) == ("pc" :> pc) RecorderSetFailure(pc) == CASE pc[1] = "Timeout" -> RecorderIncTimeout @@ RecorderGetCrash [] pc[1] = "LeaderTimeout" -> RecorderIncTimeout @@ RecorderGetCrash [] pc[1] = "FollowerTimeout" -> RecorderIncTimeout @@ RecorderGetCrash [] pc[1] = "PartitionStart" -> IF IsLooking(pc[2]) THEN RecorderGetTimeout @@ RecorderGetCrash ELSE RecorderIncTimeout @@ RecorderGetCrash [] pc[1] = "NodeCrash" -> IF IsLooking(pc[2]) THEN RecorderGetTimeout @@ RecorderIncCrash ELSE RecorderIncTimeout @@ RecorderIncCrash [] OTHER -> RecorderGetTimeout @@ RecorderGetCrash UpdateRecorder(pc) == recorder' = RecorderSetPartition(pc) @@ RecorderSetFailure(pc) @@ RecorderSetTransactionNum(pc) @@ RecorderSetMaxEpoch(pc) @@ RecorderSetPc(pc) @@ RecorderSetRequests(pc) @@ recorder UnchangeRecorder == UNCHANGED recorder CheckParameterHelper(n, p, Comp(_,_)) == IF p \in DOMAIN Parameters THEN Comp(n, Parameters[p]) ELSE TRUE CheckParameterLimit(n, p) == CheckParameterHelper(n, p, LAMBDA i, j: i < j) CheckTimeout == CheckParameterLimit(recorder.nTimeout, "MaxTimeoutFailures") CheckTransactionNum == CheckParameterLimit(recorder.nTransaction, "MaxTransactionNum") CheckEpoch == CheckParameterLimit(recorder.maxEpoch, "MaxEpoch") CheckPartition == /\ CheckTimeout /\ CheckParameterLimit(recorder.nPartition, "MaxPartitions") CheckCrash(i) == /\ \/ IsLooking(i) \/ /\ ~IsLooking(i) /\ CheckTimeout /\ CheckParameterLimit(recorder.nCrash, "MaxCrashes") CheckStateConstraints == CheckTimeout /\ CheckTransactionNum /\ CheckEpoch ----------------------------------------------------------------------------- \* Actions about network PendingFOLLOWERINFO(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = FOLLOWERINFO PendingLEADERINFO(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = LEADERINFO PendingACKEPOCH(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = ACKEPOCH PendingNEWLEADER(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = NEWLEADER PendingACKLD(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = ACKLD PendingUPTODATE(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = UPTODATE PendingPROPOSAL(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = PROPOSAL PendingACK(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = ACK PendingCOMMIT(i, j) == /\ msgs[j][i] /= << >> /\ msgs[j][i][1].mtype = COMMIT \* Add a message to msgs - add a message m to msgs. Send(i, j, m) == msgs' = [msgs EXCEPT ![i][j] = Append(msgs[i][j], m)] SendPackets(i, j, ms) == msgs' = [msgs EXCEPT ![i][j] = msgs[i][j] \o ms ] DiscardAndSendPackets(i, j, ms) == msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i][j] = msgs[i][j] \o ms ] \* Remove a message from msgs - discard head of msgs. Discard(i, j) == msgs' = IF msgs[i][j] /= << >> THEN [msgs EXCEPT ![i][j] = Tail(msgs[i][j])] ELSE msgs \* Leader broadcasts a message(PROPOSAL/COMMIT) to all other servers in forwardingFollowers. Broadcast(i, m) == msgs' = [msgs EXCEPT ![i] = [v \in Server |-> IF /\ v \in forwarding[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v]]] DiscardAndBroadcast(i, j, m) == msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in forwarding[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v]]] \* Leader broadcasts LEADERINFO to all other servers in connectingFollowers. DiscardAndBroadcastLEADERINFO(i, j, m) == LET new_connecting_quorum == {c \in connecting'[i]: c.connected = TRUE } new_sid_connecting == {c.sid: c \in new_connecting_quorum } IN msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in new_sid_connecting /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] \* Leader broadcasts UPTODATE to all other servers in newLeaderProposal. DiscardAndBroadcastUPTODATE(i, j, m) == LET new_ackldRecv_quorum == {a \in ackldRecv'[i]: a.connected = TRUE } new_sid_ackldRecv == {a.sid: a \in new_ackldRecv_quorum} IN msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i] = [v \in Server |-> IF /\ v \in new_sid_ackldRecv /\ v \in learners[i] /\ v /= i THEN Append(msgs[i][v], m) ELSE msgs[i][v] ] ] \* Combination of Send and Discard - discard head of msgs[j][i] and add m into msgs. Reply(i, j, m) == msgs' = [msgs EXCEPT ![j][i] = Tail(msgs[j][i]), ![i][j] = Append(msgs[i][j], m)] \* Shuffle input buffer. Clean(i, j) == msgs' = [msgs EXCEPT ![j][i] = << >>, ![i][j] = << >>] CleanInputBuffer(i) == msgs' = [s \in Server |-> [v \in Server |-> IF v = i THEN << >> ELSE msgs[s][v]]] CleanInputBufferInCluster(S) == msgs' = [s \in Server |-> [v \in Server |-> IF v \in S THEN << >> ELSE msgs[s][v] ] ] ----------------------------------------------------------------------------- \* Define initial values for all variables InitServerVars == /\ InitServerVarsL /\ zabState = [s \in Server |-> ELECTION] /\ acceptedEpoch = [s \in Server |-> 0] /\ lastCommitted = [s \in Server |-> [ index |-> 0, zxid |-> <<0, 0>> ] ] /\ lastSnapshot = [s \in Server |-> [ index |-> 0, zxid |-> <<0, 0>> ] ] /\ initialHistory = [s \in Server |-> << >>] InitLeaderVars == /\ InitLeaderVarsL /\ learners = [s \in Server |-> {}] /\ connecting = [s \in Server |-> {}] /\ electing = [s \in Server |-> {}] /\ ackldRecv = [s \in Server |-> {}] /\ forwarding = [s \in Server |-> {}] /\ tempMaxEpoch = [s \in Server |-> 0] InitElectionVars == InitElectionVarsL InitFollowerVars == /\ connectInfo = [s \in Server |-> [sid |-> NullPoint, syncMode |-> NONE, nlRcv |-> FALSE ] ] /\ packetsSync = [s \in Server |-> [ notCommitted |-> << >>, committed |-> << >> ] ] InitVerifyVars == /\ proposalMsgsLog = {} /\ epochLeader = [e \in 1..MAXEPOCH |-> {} ] /\ violatedInvariants = [stateInconsistent |-> FALSE, proposalInconsistent |-> FALSE, commitInconsistent |-> FALSE, ackInconsistent |-> FALSE, messageIllegal |-> FALSE ] InitMsgVars == /\ msgs = [s \in Server |-> [v \in Server |-> << >>] ] /\ electionMsgs = [s \in Server |-> [v \in Server |-> << >>] ] InitEnvVars == /\ status = [s \in Server |-> ONLINE ] /\ partition = [s \in Server |-> [v \in Server |-> FALSE] ] InitRecorder == recorder = [nTimeout |-> 0, nTransaction |-> 0, nPartition |-> 0, maxEpoch |-> 0, nCrash |-> 0, pc |-> <<"Init">>, nClientRequest |-> 0] Init == /\ InitServerVars /\ InitLeaderVars /\ InitElectionVars /\ InitFollowerVars /\ InitVerifyVars /\ InitMsgVars /\ InitEnvVars /\ InitRecorder ----------------------------------------------------------------------------- ZabTurnToLeading(i) == /\ zabState' = [zabState EXCEPT ![i] = DISCOVERY] /\ learners' = [learners EXCEPT ![i] = {i}] /\ connecting' = [connecting EXCEPT ![i] = { [ sid |-> i, connected |-> TRUE ] }] /\ electing' = [electing EXCEPT ![i] = { [ sid |-> i, peerLastZxid |-> <<-1,-1>>, inQuorum |-> TRUE ] }] /\ ackldRecv' = [ackldRecv EXCEPT ![i] = { [ sid |-> i, connected |-> TRUE ] }] /\ forwarding' = [forwarding EXCEPT ![i] = {}] /\ initialHistory' = [initialHistory EXCEPT ![i] = history'[i]] /\ tempMaxEpoch' = [tempMaxEpoch EXCEPT ![i] = acceptedEpoch[i] + 1] ZabTurnToFollowing(i) == /\ zabState' = [zabState EXCEPT ![i] = DISCOVERY] /\ initialHistory' = [initialHistory EXCEPT ![i] = history'[i]] /\ packetsSync' = [packetsSync EXCEPT ![i].notCommitted = << >>, ![i].committed = << >> ] (* Fast Leader Election *) FLEReceiveNotmsg(i, j) == /\ IsON(i) /\ ReceiveNotmsg(i, j) /\ UNCHANGED <> /\ UpdateRecorder(<<"FLEReceiveNotmsg", i, j>>) FLENotmsgTimeout(i) == /\ IsON(i) /\ NotmsgTimeout(i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FLENotmsgTimeout", i>>) FLEHandleNotmsg(i) == /\ IsON(i) /\ HandleNotmsg(i) /\ LET newState == state'[i] IN \/ /\ newState = LEADING /\ ZabTurnToLeading(i) /\ UNCHANGED packetsSync \/ /\ newState = FOLLOWING /\ ZabTurnToFollowing(i) /\ UNCHANGED <> \/ /\ newState = LOOKING /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"FLEHandleNotmsg", i>>) \* On the premise that ReceiveVotes.HasQuorums = TRUE, \* corresponding to logic in FastLeaderElection. FLEWaitNewNotmsg(i) == /\ IsON(i) /\ WaitNewNotmsg(i) /\ LET newState == state'[i] IN \/ /\ newState = LEADING /\ ZabTurnToLeading(i) /\ UNCHANGED packetsSync \/ /\ newState = FOLLOWING /\ ZabTurnToFollowing(i) /\ UNCHANGED <> \/ /\ newState = LOOKING /\ PrintT("Note: New state is LOOKING in FLEWaitNewNotmsgEnd," \o " which should not happen.") /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"FLEWaitNewNotmsg", i>>) ----------------------------------------------------------------------------- InitialVotes == [ vote |-> InitialVote, round |-> 0, state |-> LOOKING, version |-> 0 ] InitialConnectInfo == [sid |-> NullPoint, syncMode |-> NONE, nlRcv |-> FALSE ] \* Equals to for every server in S, performing action ZabTimeout. ZabTimeoutInCluster(S) == /\ state' = [s \in Server |-> IF s \in S THEN LOOKING ELSE state[s] ] /\ lastProcessed' = [s \in Server |-> IF s \in S THEN InitLastProcessed(s) ELSE lastProcessed[s] ] /\ logicalClock' = [s \in Server |-> IF s \in S THEN logicalClock[s] + 1 ELSE logicalClock[s] ] /\ currentVote' = [s \in Server |-> IF s \in S THEN [proposedLeader |-> s, proposedZxid |-> lastProcessed'[s].zxid, proposedEpoch |-> currentEpoch[s] ] ELSE currentVote[s] ] /\ receiveVotes' = [s \in Server |-> IF s \in S THEN [v \in Server |-> InitialVotes] ELSE receiveVotes[s] ] /\ outOfElection' = [s \in Server |-> IF s \in S THEN [v \in Server |-> InitialVotes] ELSE outOfElection[s] ] /\ recvQueue' = [s \in Server |-> IF s \in S THEN << [mtype |-> NONE] >> ELSE recvQueue[s] ] /\ waitNotmsg' = [s \in Server |-> IF s \in S THEN FALSE ELSE waitNotmsg[s] ] /\ leadingVoteSet' = [s \in Server |-> IF s \in S THEN {} ELSE leadingVoteSet[s] ] /\ UNCHANGED <> /\ zabState' = [s \in Server |-> IF s \in S THEN ELECTION ELSE zabState[s] ] /\ connectInfo' = [s \in Server |-> IF s \in S THEN InitialConnectInfo ELSE connectInfo[s] ] /\ CleanInputBufferInCluster(S) (* Describe how a server transitions from LEADING/FOLLOWING to LOOKING.*) FollowerShutdown(i) == /\ ZabTimeout(i) /\ zabState' = [zabState EXCEPT ![i] = ELECTION] /\ connectInfo' = [connectInfo EXCEPT ![i] = InitialConnectInfo] LeaderShutdown(i) == /\ LET cluster == {i} \union learners[i] IN ZabTimeoutInCluster(cluster) /\ learners' = [learners EXCEPT ![i] = {}] /\ forwarding' = [forwarding EXCEPT ![i] = {}] RemoveElecting(set, sid) == LET sid_electing == {s.sid: s \in set } IN IF sid \notin sid_electing THEN set ELSE LET info == CHOOSE s \in set: s.sid = sid new_info == [ sid |-> sid, peerLastZxid |-> <<-1, -1>>, inQuorum |-> info.inQuorum ] IN (set \ {info}) \union {new_info} RemoveConnectingOrAckldRecv(set, sid) == LET sid_set == {s.sid: s \in set} IN IF sid \notin sid_set THEN set ELSE LET info == CHOOSE s \in set: s.sid = sid new_info == [ sid |-> sid, connected |-> FALSE ] IN (set \ {info}) \union {new_info} \* See removeLearnerHandler for details. RemoveLearner(i, j) == /\ learners' = [learners EXCEPT ![i] = @ \ {j}] /\ forwarding' = [forwarding EXCEPT ![i] = IF j \in forwarding[i] THEN @ \ {j} ELSE @ ] /\ electing' = [electing EXCEPT ![i] = RemoveElecting(@, j) ] /\ connecting' = [connecting EXCEPT ![i] = RemoveConnectingOrAckldRecv(@, j) ] /\ ackldRecv' = [ackldRecv EXCEPT ![i] = RemoveConnectingOrAckldRecv(@, j) ] ----------------------------------------------------------------------------- \* Actions of situation error. PartitionStart(i, j) == /\ CheckPartition \* test restrictions of partition /\ i /= j /\ IsON(i) /\ IsON(j) /\ \lnot HasPartitioned(i, j) /\ \/ /\ IsLeader(i) /\ IsMyLearner(i, j) /\ IsFollower(j) /\ IsMyLeader(j, i) /\ LET newLearners == learners[i] \ {j} IN \/ /\ IsQuorum(newLearners) \* just remove this learner /\ RemoveLearner(i, j) /\ FollowerShutdown(j) /\ Clean(i ,j) \/ /\ ~IsQuorum(newLearners) \* leader switches to looking /\ LeaderShutdown(i) /\ UNCHANGED <> \/ /\ IsLooking(i) /\ IsLooking(j) /\ IdCompare(i, j) /\ UNCHANGED <> /\ partition' = [partition EXCEPT ![i][j] = TRUE, ![j][i] = TRUE ] /\ UNCHANGED <> /\ UpdateRecorder(<<"PartitionStart", i, j>>) PartitionRecover(i, j) == /\ IsON(i) /\ IsON(j) /\ IdCompare(i, j) /\ HasPartitioned(i, j) /\ partition' = [partition EXCEPT ![i][j] = FALSE, ![j][i] = FALSE ] /\ UNCHANGED <> /\ UpdateRecorder(<<"PartitionRecover", i, j>>) NodeCrash(i) == /\ CheckCrash(i) /\ IsON(i) /\ status' = [status EXCEPT ![i] = OFFLINE ] /\ \/ /\ IsLooking(i) /\ UNCHANGED <> \/ /\ IsFollower(i) /\ LET connectedWithLeader == HasLeader(i) IN \/ /\ connectedWithLeader /\ LET leader == connectInfo[i].sid newCluster == learners[leader] \ {i} IN \/ /\ IsQuorum(newCluster) /\ RemoveLearner(leader, i) /\ FollowerShutdown(i) /\ Clean(leader, i) \/ /\ ~IsQuorum(newCluster) /\ LeaderShutdown(leader) /\ UNCHANGED <> \/ /\ ~connectedWithLeader /\ FollowerShutdown(i) /\ CleanInputBuffer({i}) /\ UNCHANGED <> \/ /\ IsLeader(i) /\ LeaderShutdown(i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"NodeCrash", i>>) NodeStart(i) == /\ IsOFF(i) /\ status' = [status EXCEPT ![i] = ONLINE ] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = InitLastProcessed(i)] /\ lastCommitted' = [lastCommitted EXCEPT ![i] = lastSnapshot[i]] /\ UNCHANGED <> /\ UpdateRecorder(<<"NodeStart", i>>) ----------------------------------------------------------------------------- (* Establish connection between leader and follower, containing actions like addLearnerHandler, findLeader, connectToLeader.*) ConnectAndFollowerSendFOLLOWERINFO(i, j) == /\ IsON(i) /\ IsON(j) /\ IsLeader(i) /\ \lnot IsMyLearner(i, j) /\ IsFollower(j) /\ HasNoLeader(j) /\ MyVote(j) = i /\ learners' = [learners EXCEPT ![i] = learners[i] \union {j}] /\ connectInfo' = [connectInfo EXCEPT ![j].sid = i] /\ Send(j, i, [ mtype |-> FOLLOWERINFO, mzxid |-> <> ]) /\ UNCHANGED <> /\ UpdateRecorder(<<"ConnectAndFollowerSendFOLLOWERINFO", i, j>>) \* waitingForNewEpoch in Leader WaitingForNewEpoch(i, set) == (i \in set /\ IsQuorum(set)) = FALSE WaitingForNewEpochTurnToFalse(i, set) == /\ i \in set /\ IsQuorum(set) \* There may exists some follower in connecting but shuts down and \* connects again. So when leader broadcasts LEADERINFO, the \* follower will receive LEADERINFO, and receive it again after \* sending FOLLOWERINFO. So connected makes sure each follower \* will only receive LEADERINFO at most once before timeout. UpdateConnectingOrAckldRecv(oldSet, sid) == LET sid_set == {s.sid: s \in oldSet} IN IF sid \in sid_set THEN LET old_info == CHOOSE info \in oldSet: info.sid = sid follower_info == [ sid |-> sid, connected |-> TRUE ] IN (oldSet \ {old_info} ) \union {follower_info} ELSE LET follower_info == [ sid |-> sid, connected |-> TRUE ] IN oldSet \union {follower_info} (* Leader waits for receiving FOLLOWERINFO from a quorum including itself, and chooses a new epoch e' as its own epoch and broadcasts LEADERINFO. See getEpochToPropose in Leader for details. *) LeaderProcessFOLLOWERINFO(i, j) == /\ CheckEpoch \* test restrictions of max epoch /\ IsON(i) /\ IsLeader(i) /\ PendingFOLLOWERINFO(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) lastAcceptedEpoch == msg.mzxid[1] sid_connecting == {c.sid: c \in connecting[i]} IN /\ infoOk /\ \/ \* 1. has not broadcast LEADERINFO /\ WaitingForNewEpoch(i, sid_connecting) /\ \/ /\ zabState[i] = DISCOVERY /\ UNCHANGED violatedInvariants \/ /\ zabState[i] /= DISCOVERY /\ PrintT("Exception: waitingFotNewEpoch true," \o " while zabState not DISCOVERY.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ tempMaxEpoch' = [tempMaxEpoch EXCEPT ![i] = IF lastAcceptedEpoch >= tempMaxEpoch[i] THEN lastAcceptedEpoch + 1 ELSE @] /\ connecting' = [connecting EXCEPT ![i] = UpdateConnectingOrAckldRecv(@, j) ] /\ LET new_sid_connecting == {c.sid: c \in connecting'[i]} IN \/ /\ WaitingForNewEpochTurnToFalse(i, new_sid_connecting) /\ acceptedEpoch' = [acceptedEpoch EXCEPT ![i] = tempMaxEpoch'[i]] /\ LET newLeaderZxid == <> m == [ mtype |-> LEADERINFO, mzxid |-> newLeaderZxid ] IN DiscardAndBroadcastLEADERINFO(i, j, m) \/ /\ ~WaitingForNewEpochTurnToFalse(i, new_sid_connecting) /\ Discard(j, i) /\ UNCHANGED acceptedEpoch \/ \* 2. has broadcast LEADERINFO /\ ~WaitingForNewEpoch(i, sid_connecting) /\ Reply(i, j, [ mtype |-> LEADERINFO, mzxid |-> <> ] ) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessFOLLOWERINFO", i, j>>) (* Follower receives LEADERINFO. If newEpoch >= acceptedEpoch, then follower updates acceptedEpoch and sends ACKEPOCH back, containing currentEpoch and lastProcessedZxid. After this, zabState turns to SYNC. See registerWithLeader in Learner for details.*) FollowerProcessLEADERINFO(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingLEADERINFO(i, j) /\ LET msg == msgs[j][i][1] newEpoch == msg.mzxid[1] infoOk == IsMyLeader(i, j) epochOk == newEpoch >= acceptedEpoch[i] stateOk == zabState[i] = DISCOVERY IN /\ infoOk /\ \/ \* 1. Normal case /\ epochOk /\ \/ /\ stateOk /\ \/ /\ newEpoch > acceptedEpoch[i] /\ acceptedEpoch' = [acceptedEpoch EXCEPT ![i] = newEpoch] /\ LET epochBytes == currentEpoch[i] m == [ mtype |-> ACKEPOCH, mzxid |-> lastProcessed[i].zxid, mepoch |-> epochBytes ] IN Reply(i, j, m) \/ /\ newEpoch = acceptedEpoch[i] /\ LET m == [ mtype |-> ACKEPOCH, mzxid |-> lastProcessed[i].zxid, mepoch |-> -1 ] IN Reply(i, j, m) /\ UNCHANGED acceptedEpoch /\ zabState' = [zabState EXCEPT ![i] = SYNCHRONIZATION] /\ UNCHANGED violatedInvariants \/ /\ ~stateOk /\ PrintT("Exception: Follower receives LEADERINFO," \o " whileZabState not DISCOVERY.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED <> \/ \* 2. Abnormal case - go back to election /\ ~epochOk /\ FollowerShutdown(i) /\ Clean(i, connectInfo[i].sid) /\ RemoveLearner(connectInfo[i].sid, i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessLEADERINFO", i, j>>) ----------------------------------------------------------------------------- RECURSIVE UpdateAckSidHelper(_,_,_,_) UpdateAckSidHelper(his, cur, end, target) == IF cur > end THEN his ELSE LET curTxn == [ zxid |-> his[1].zxid, value |-> his[1].value, ackSid |-> IF target \in his[1].ackSid THEN his[1].ackSid ELSE his[1].ackSid \union {target}, epoch |-> his[1].epoch ] IN <> \o UpdateAckSidHelper(Tail(his), cur + 1, end, target) \* There originally existed one bug in LeaderProcessACK when \* monotonicallyInc = FALSE, and it is we did not add ackSid of \* history in SYNC. So we update ackSid in syncFollower. UpdateAckSid(his, lastSeenIndex, target) == IF Len(his) = 0 \/ lastSeenIndex = 0 THEN his ELSE UpdateAckSidHelper(his, 1, Minimum( { Len(his), lastSeenIndex} ), target) \* return -1: this zxid appears at least twice; Len(his) + 1: does not exist; \* 1 ~ Len(his): exists and appears just once. RECURSIVE ZxidToIndexHepler(_,_,_,_) ZxidToIndexHepler(his, zxid, cur, appeared) == IF cur > Len(his) THEN cur ELSE IF TxnZxidEqual(his[cur], zxid) THEN CASE appeared = TRUE -> -1 [] OTHER -> Minimum( { cur, ZxidToIndexHepler(his, zxid, cur + 1, TRUE) } ) ELSE ZxidToIndexHepler(his, zxid, cur + 1, appeared) ZxidToIndex(his, zxid) == IF ZxidEqual( zxid, <<0, 0>> ) THEN 0 ELSE IF Len(his) = 0 THEN 1 ELSE LET len == Len(his) IN IF \E idx \in 1..len: TxnZxidEqual(his[idx], zxid) THEN ZxidToIndexHepler(his, zxid, 1, FALSE) ELSE len + 1 \* Find index idx which meets: \* history[idx].zxid <= zxid < history[idx + 1].zxid RECURSIVE IndexOfZxidHelper(_,_,_,_) IndexOfZxidHelper(his, zxid, cur, end) == IF cur > end THEN end ELSE IF ZxidCompare(his[cur].zxid, zxid) THEN cur - 1 ELSE IndexOfZxidHelper(his, zxid, cur + 1, end) IndexOfZxid(his, zxid) == IF Len(his) = 0 THEN 0 ELSE LET idx == ZxidToIndex(his, zxid) len == Len(his) IN IF idx <= len THEN idx ELSE IndexOfZxidHelper(his, zxid, 1, len) RECURSIVE queuePackets(_,_,_,_,_) queuePackets(queue, his, cur, committed, end) == IF cur > end THEN queue ELSE CASE cur > committed -> LET m_proposal == [ mtype |-> PROPOSAL, mzxid |-> his[cur].zxid, mdata |-> his[cur].value ] IN queuePackets(Append(queue, m_proposal), his, cur + 1, committed, end) [] cur <= committed -> LET m_proposal == [ mtype |-> PROPOSAL, mzxid |-> his[cur].zxid, mdata |-> his[cur].value ] m_commit == [ mtype |-> COMMIT, mzxid |-> his[cur].zxid ] newQueue == queue \o <> IN queuePackets(newQueue, his, cur + 1, committed, end) RECURSIVE setPacketsForChecking(_,_,_,_,_,_) setPacketsForChecking(set, src, ep, his, cur, end) == IF cur > end THEN set ELSE LET m_proposal == [ source |-> src, epoch |-> ep, zxid |-> his[cur].zxid, data |-> his[cur].value ] IN setPacketsForChecking((set \union {m_proposal}), src, ep, his, cur + 1, end) \* Func lead() calls zk.loadData(), which will call takeSnapshot(). LastSnapshot(i) == IF zabState[i] = BROADCAST THEN lastSnapshot[i] ELSE CASE IsLeader(i) -> LET lastIndex == Len(history[i]) IN IF lastIndex = 0 THEN [ index |-> 0, zxid |-> <<0, 0>> ] ELSE [ index |-> lastIndex, zxid |-> history[i][lastIndex].zxid ] [] OTHER -> lastSnapshot[i] \* To compress state space, \* 1. we merge sending SNAP and outputing snapshot buffer into sending SNAP, and \* 2. substitute sub sequence of history for snapshot of data tree. SerializeSnapshot(i, idx) == IF idx <= 0 THEN << >> ELSE SubSeq(history[i], 1, idx) (* See queueCommittedProposals in LearnerHandler and startForwarding in Leader for details. For proposals in committedLog and toBeApplied, send . For proposals in outstandingProposals, send PROPOSAL only. *) SendSyncMsgs(i, j, lastSeenZxid, lastSeenIndex, mode, needRemoveHead) == /\ LET lastCommittedIndex == IF zabState[i] = BROADCAST THEN lastCommitted[i].index ELSE Len(history[i]) lastProposedIndex == Len(history[i]) queue_origin == IF lastSeenIndex >= lastProposedIndex THEN << >> ELSE queuePackets(<< >>, history[i], lastSeenIndex + 1, lastCommittedIndex, lastProposedIndex) set_forChecking == IF lastSeenIndex >= lastProposedIndex THEN {} ELSE setPacketsForChecking( { }, i, acceptedEpoch[i], history[i], lastSeenIndex + 1, lastProposedIndex) m_trunc == [ mtype |-> TRUNC, mtruncZxid |-> lastSeenZxid ] m_diff == [ mtype |-> DIFF, mzxid |-> lastSeenZxid ] m_snap == [ mtype |-> SNAP, msnapZxid |-> lastSeenZxid, msnapshot |-> SerializeSnapshot(i, lastSeenIndex) ] newLeaderZxid == <> m_newleader == [ mtype |-> NEWLEADER, mzxid |-> newLeaderZxid ] queue_toSend == CASE mode = TRUNC -> (<> \o queue_origin) \o <> [] mode = DIFF -> (<> \o queue_origin) \o <> [] mode = SNAP -> (<> \o queue_origin) \o <> IN /\ \/ /\ needRemoveHead /\ DiscardAndSendPackets(i, j, queue_toSend) \/ /\ ~needRemoveHead /\ SendPackets(i, j, queue_toSend) /\ proposalMsgsLog' = proposalMsgsLog \union set_forChecking /\ forwarding' = [forwarding EXCEPT ![i] = @ \union {j} ] /\ \/ /\ mode = TRUNC \/ mode = DIFF /\ history' = [history EXCEPT ![i] = UpdateAckSid(@, lastSeenIndex, j) ] \/ /\ mode = SNAP /\ UNCHANGED history \* txns before minCommitted don't need to be committed again (* Leader syncs with follower by sending DIFF/TRUNC/SNAP/PROPOSAL/COMMIT/NEWLEADER. See syncFollower in LearnerHandler for details. *) SyncFollower(i, j, peerLastZxid, needRemoveHead) == LET \* IsPeerNewEpochZxid == peerLastZxid[2] = 0 lastProcessedZxid == lastProcessed[i].zxid minCommittedIdx == lastSnapshot[i].index + 1 maxCommittedIdx == IF zabState[i] = BROADCAST THEN lastCommitted[i].index ELSE Len(history[i]) committedLogEmpty == minCommittedIdx > maxCommittedIdx minCommittedLog == IF committedLogEmpty THEN lastProcessedZxid ELSE history[i][minCommittedIdx].zxid maxCommittedLog == IF committedLogEmpty THEN lastProcessedZxid ELSE IF maxCommittedIdx = 0 THEN << 0, 0>> ELSE history[i][maxCommittedIdx].zxid \* Hypothesis: 1. minCommittedLog : txn with index of lastSnapshot + 1 \* 2. maxCommittedLog : LastCommitted, to compress state space. \* 3. merge queueCommittedProposals,startForwarding and \* sending NEWLEADER into SendSyncMsgs. IN \/ \* case1. peerLastZxid = lastProcessedZxid, \* sned DIFF & StartForwarding(lastProcessedZxid) /\ ZxidEqual(peerLastZxid, lastProcessedZxid) /\ SendSyncMsgs(i, j, peerLastZxid, lastProcessed[i].index, DIFF, needRemoveHead) \/ /\ ~ZxidEqual(peerLastZxid, lastProcessedZxid) /\ \/ \* case2. peerLastZxid > maxCommittedLog, \* send TRUNC(maxCommittedLog) & StartForwarding /\ ZxidCompare(peerLastZxid, maxCommittedLog) /\ SendSyncMsgs(i, j, maxCommittedLog, maxCommittedIdx, TRUNC, needRemoveHead) \/ \* case3. minCommittedLog <= peerLastZxid <= maxCommittedLog /\ ~ZxidCompare(peerLastZxid, maxCommittedLog) /\ ~ZxidCompare(minCommittedLog, peerLastZxid) /\ LET lastSeenIndex == ZxidToIndex(history[i], peerLastZxid) exist == /\ lastSeenIndex >= minCommittedIdx /\ lastSeenIndex <= Len(history[i]) lastIndex == IF exist THEN lastSeenIndex ELSE IndexOfZxid(history[i], peerLastZxid) \* Maximum zxid that < peerLastZxid lastZxid == IF exist THEN peerLastZxid ELSE IF lastIndex = 0 THEN <<0, 0>> ELSE history[i][lastIndex].zxid IN \/ \* case 3.1. peerLastZxid exists in committedLog, \* DIFF + queueCommittedProposals(peerLastZxid + 1) \* + StartForwarding /\ exist /\ SendSyncMsgs(i, j, peerLastZxid, lastSeenIndex, DIFF, needRemoveHead) \/ \* case 3.2. peerLastZxid does not exist in committedLog, \* TRUNC(lastZxid) + queueCommittedProposals(lastZxid + 1) \* + StartForwarding /\ ~exist /\ SendSyncMsgs(i, j, lastZxid, lastIndex, TRUNC, needRemoveHead) \/ \* case4. peerLastZxid < minCommittedLog, \* send SNAP(lastProcessed) + StartForwarding /\ ZxidCompare(minCommittedLog, peerLastZxid) /\ SendSyncMsgs(i, j, lastProcessedZxid, maxCommittedIdx, SNAP, needRemoveHead) \* compare state summary of two servers IsMoreRecentThan(ss1, ss2) == \/ ss1.currentEpoch > ss2.currentEpoch \/ /\ ss1.currentEpoch = ss2.currentEpoch /\ ZxidCompare(ss1.lastZxid, ss2.lastZxid) \* electionFinished in Leader ElectionFinished(i, set) == /\ i \in set /\ IsQuorum(set) \* There may exist some follower shuts down and connects again, while \* it has sent ACKEPOCH or updated currentEpoch last time. This means \* sid of this follower has existed in elecingFollower but its info \* is old. So we need to make sure each sid in electingFollower is \* unique and latest(newest). UpdateElecting(oldSet, sid, peerLastZxid, inQuorum) == LET sid_electing == {s.sid: s \in oldSet } IN IF sid \in sid_electing THEN LET old_info == CHOOSE info \in oldSet : info.sid = sid follower_info == [ sid |-> sid, peerLastZxid |-> peerLastZxid, inQuorum |-> (inQuorum \/ old_info.inQuorum) ] IN (oldSet \ {old_info} ) \union {follower_info} ELSE LET follower_info == [ sid |-> sid, peerLastZxid |-> peerLastZxid, inQuorum |-> inQuorum ] IN oldSet \union {follower_info} LeaderTurnToSynchronization(i) == /\ currentEpoch' = [currentEpoch EXCEPT ![i] = acceptedEpoch[i]] /\ zabState' = [zabState EXCEPT ![i] = SYNCHRONIZATION] (* Leader waits for receiving ACKEPOPCH from a quorum, and check whether it has most recent state summary from them. After this, leader's zabState turns to SYNCHRONIZATION. See waitForEpochAck in Leader for details. *) LeaderProcessACKEPOCH(i, j) == /\ IsON(i) /\ IsLeader(i) /\ PendingACKEPOCH(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) leaderStateSummary == [ currentEpoch |-> currentEpoch[i], lastZxid |-> lastProcessed[i].zxid ] followerStateSummary == [ currentEpoch |-> msg.mepoch, lastZxid |-> msg.mzxid ] logOk == \* whether follower is no more up-to-date than leader ~IsMoreRecentThan(followerStateSummary, leaderStateSummary) electing_quorum == {e \in electing[i]: e.inQuorum = TRUE } sid_electing == {s.sid: s \in electing_quorum } IN /\ infoOk /\ \/ \* electionFinished = true, jump ouf of waitForEpochAck. \* Different from code, here we still need to record info \* into electing, to help us perform syncFollower afterwards. \* Since electing already meets quorum, it does not break \* consistency between code and spec. /\ ElectionFinished(i, sid_electing) /\ electing' = [electing EXCEPT ![i] = UpdateElecting(@, j, msg.mzxid, FALSE) ] /\ Discard(j, i) /\ UNCHANGED <> \/ /\ ~ElectionFinished(i, sid_electing) /\ \/ /\ zabState[i] = DISCOVERY /\ UNCHANGED violatedInvariants \/ /\ zabState[i] /= DISCOVERY /\ PrintT("Exception: electionFinished false," \o " while zabState not DISCOVERY.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ \/ /\ followerStateSummary.currentEpoch = -1 /\ electing' = [electing EXCEPT ![i] = UpdateElecting(@, j, msg.mzxid, FALSE) ] /\ Discard(j, i) /\ UNCHANGED <> \/ /\ followerStateSummary.currentEpoch > -1 /\ \/ \* normal follower /\ logOk /\ electing' = [electing EXCEPT ![i] = UpdateElecting(@, j, msg.mzxid, TRUE) ] /\ LET new_electing_quorum == {e \in electing'[i]: e.inQuorum = TRUE } new_sid_electing == {s.sid: s \in new_electing_quorum } IN \/ \* electionFinished = true, jump out of waitForEpochAck, \* update currentEpoch and zabState. /\ ElectionFinished(i, new_sid_electing) /\ LeaderTurnToSynchronization(i) /\ LET newLeaderEpoch == acceptedEpoch[i] IN epochLeader' = [epochLeader EXCEPT ![newLeaderEpoch] = @ \union {i} ] \* for checking invariants \/ \* there still exists electionFinished = false. /\ ~ElectionFinished(i, new_sid_electing) /\ UNCHANGED <> /\ Discard(j, i) /\ UNCHANGED <> \/ \* Exists follower more recent than leader /\ ~logOk /\ LeaderShutdown(i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessACKEPOCH", i, j>>) \* Strip syncFollower from LeaderProcessACKEPOCH. \* Only when electionFinished = true and there exists some \* learnerHandler has not perform syncFollower, this \* action will be called. LeaderSyncFollower(i, j) == /\ IsON(i) /\ IsLeader(i) /\ LET electing_quorum == {e \in electing[i]: e.inQuorum = TRUE } electionFinished == ElectionFinished(i, {s.sid: s \in electing_quorum } ) toSync == {s \in electing[i] : /\ ~ZxidEqual( s.peerLastZxid, <<-1, -1>>) /\ s.sid \in learners[i] } canSync == toSync /= {} IN /\ electionFinished /\ canSync /\ \E s \in toSync: s.sid = j /\ LET chosen == CHOOSE s \in toSync: s.sid = j newChosen == [ sid |-> chosen.sid, peerLastZxid |-> <<-1, -1>>, \* <<-1,-1>> means has handled. inQuorum |-> chosen.inQuorum ] IN /\ SyncFollower(i, chosen.sid, chosen.peerLastZxid, FALSE) /\ electing' = [electing EXCEPT ![i] = (@ \ {chosen}) \union {newChosen} ] /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderSyncFollower", i, j>>) TruncateLog(his, index) == IF index <= 0 THEN << >> ELSE SubSeq(his, 1, index) (* Follower receives DIFF/TRUNC, and then may receives PROPOSAL,COMMIT,NEWLEADER, and UPTODATE. See syncWithLeader in Learner for details. *) FollowerProcessSyncMessage(i, j) == /\ IsON(i) /\ IsFollower(i) /\ msgs[j][i] /= << >> /\ \/ msgs[j][i][1].mtype = DIFF \/ msgs[j][i][1].mtype = TRUNC \/ msgs[j][i][1].mtype = SNAP /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) stateOk == zabState[i] = SYNCHRONIZATION IN /\ infoOk /\ \/ \* Follower should receive packets in SYNC. /\ ~stateOk /\ PrintT("Exception: Follower receives DIFF/TRUNC/SNAP," \o " whileZabState not SYNCHRONIZATION.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.stateInconsistent = TRUE] /\ UNCHANGED <> \/ /\ stateOk /\ \/ /\ msg.mtype = DIFF /\ connectInfo' = [connectInfo EXCEPT ![i].syncMode = DIFF] /\ UNCHANGED <> \/ /\ msg.mtype = TRUNC /\ connectInfo' = [connectInfo EXCEPT ![i].syncMode = TRUNC] /\ LET truncZxid == msg.mtruncZxid truncIndex == ZxidToIndex(history[i], truncZxid) truncOk == /\ truncIndex >= lastCommitted[i].index /\ truncIndex <= Len(history[i]) IN \/ /\ ~truncOk /\ PrintT("Exception: TRUNC error.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.proposalInconsistent = TRUE] /\ UNCHANGED <> \/ /\ truncOk /\ history' = [history EXCEPT ![i] = TruncateLog(history[i], truncIndex)] /\ initialHistory' = [initialHistory EXCEPT ![i] = history'[i]] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = [ index |-> truncIndex, zxid |-> truncZxid] ] /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> truncIndex, zxid |-> truncZxid] ] /\ UNCHANGED violatedInvariants \/ /\ msg.mtype = SNAP /\ connectInfo' = [connectInfo EXCEPT ![i].syncMode = SNAP] /\ history' = [history EXCEPT ![i] = msg.msnapshot] /\ initialHistory' = [initialHistory EXCEPT ![i] = history'[i]] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = [ index |-> Len(history'[i]), zxid |-> msg.msnapZxid] ] /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> Len(history'[i]), zxid |-> msg.msnapZxid] ] /\ UNCHANGED violatedInvariants /\ Discard(j, i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessSyncMessage", i, j>>) \* See variable snapshotNeeded in Learner for details. SnapshotNeeded(i) == \/ connectInfo[i].syncMode = TRUNC \/ connectInfo[i].syncMode = SNAP \* See variable writeToTxnLog in Learner for details. WriteToTxnLog(i) == IF \/ connectInfo[i].syncMode = DIFF \/ connectInfo[i].nlRcv = TRUE THEN TRUE ELSE FALSE \* See lastProposed in Leader for details. LastProposed(i) == IF Len(history[i]) = 0 THEN [ index |-> 0, zxid |-> <<0, 0>> ] ELSE LET lastIndex == Len(history[i]) entry == history[i][lastIndex] IN [ index |-> lastIndex, zxid |-> entry.zxid ] \* See lastQueued in Learner for details. LastQueued(i) == IF ~IsFollower(i) \/ zabState[i] /= SYNCHRONIZATION THEN LastProposed(i) ELSE \* condition: IsFollower(i) /\ zabState = SYNCHRONIZATION LET packetsInSync == packetsSync[i].notCommitted lenSync == Len(packetsInSync) totalLen == Len(history[i]) + lenSync IN IF lenSync = 0 THEN LastProposed(i) ELSE [ index |-> totalLen, zxid |-> packetsInSync[lenSync].zxid ] IsNextZxid(curZxid, nextZxid) == \/ \* first PROPOSAL in this epoch /\ nextZxid[2] = 1 /\ curZxid[1] < nextZxid[1] \/ \* not first PROPOSAL in this epoch /\ nextZxid[2] > 1 /\ curZxid[1] = nextZxid[1] /\ curZxid[2] + 1 = nextZxid[2] FollowerProcessPROPOSALInSync(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingPROPOSAL(i, j) /\ zabState[i] = SYNCHRONIZATION /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) isNext == IsNextZxid(LastQueued(i).zxid, msg.mzxid) newTxn == [ zxid |-> msg.mzxid, value |-> msg.mdata, ackSid |-> {}, \* follower do not consider ackSid epoch |-> acceptedEpoch[i] ] \* epoch of this round IN /\ infoOk /\ \/ /\ isNext /\ packetsSync' = [packetsSync EXCEPT ![i].notCommitted = Append(packetsSync[i].notCommitted, newTxn) ] \/ /\ ~isNext /\ PrintT("Warn: Follower receives PROPOSAL," \o " while zxid != lastQueued + 1.") /\ UNCHANGED packetsSync \* logRequest -> SyncRequestProcessor -> SendAckRequestProcessor -> reply ack \* So here we do not need to send ack to leader. /\ Discard(j, i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessPROPOSALInSync", i, j>>) RECURSIVE IndexOfFirstTxnWithEpoch(_,_,_,_) IndexOfFirstTxnWithEpoch(his, epoch, cur, end) == IF cur > end THEN cur ELSE IF his[cur].epoch = epoch THEN cur ELSE IndexOfFirstTxnWithEpoch(his, epoch, cur + 1, end) LastCommitted(i) == IF zabState[i] = BROADCAST THEN lastCommitted[i] ELSE CASE IsLeader(i) -> LET lastInitialIndex == Len(initialHistory[i]) IN IF lastInitialIndex = 0 THEN [ index |-> 0, zxid |-> <<0, 0>> ] ELSE [ index |-> lastInitialIndex, zxid |-> history[i][lastInitialIndex].zxid ] [] IsFollower(i) -> LET completeHis == history[i] \o packetsSync[i].notCommitted packetsCommitted == packetsSync[i].committed lenCommitted == Len(packetsCommitted) IN IF lenCommitted = 0 \* return last one in history THEN LET lastIndex == Len(history[i]) lastInitialIndex == Len(initialHistory[i]) IN IF lastIndex = lastInitialIndex THEN IF lastIndex = 0 THEN [ index |-> 0, zxid |-> <<0, 0>> ] ELSE [ index |-> lastIndex , zxid |-> history[lastIndex].zxid ] ELSE IF lastInitialIndex < lastCommitted[i].index THEN lastCommitted[i] ELSE IF lastInitialIndex = 0 THEN [ index |-> 0, zxid |-> <<0, 0>> ] ELSE [ index |-> lastInitialIndex, zxid |-> history[lastInitialIndex].zxid ] ELSE \* return tail of packetsCommitted LET committedIndex == ZxidToIndex(completeHis, packetsCommitted[lenCommitted] ) IN [ index |-> committedIndex, zxid |-> packetsCommitted[lenCommitted] ] [] OTHER -> lastCommitted[i] TxnWithIndex(i, idx) == IF ~IsFollower(i) \/ zabState[i] /= SYNCHRONIZATION THEN history[i][idx] ELSE LET completeHis == history[i] \o packetsSync[i].notCommitted IN completeHis[idx] (* To simplify specification, we assume snapshotNeeded = false and writeToTxnLog = true. So here we just call packetsCommitted.add. *) FollowerProcessCOMMITInSync(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingCOMMIT(i, j) /\ zabState[i] = SYNCHRONIZATION /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) committedIndex == LastCommitted(i).index + 1 exist == /\ committedIndex <= LastQueued(i).index /\ IsNextZxid(LastCommitted(i).zxid, msg.mzxid) match == ZxidEqual(msg.mzxid, TxnWithIndex(i, committedIndex).zxid ) IN /\ infoOk /\ \/ /\ exist /\ \/ /\ match /\ LET writeToTxnLog == WriteToTxnLog(i) IN \/ /\ ~writeToTxnLog \* zk.processTxn() & packetsNotCommitted.remove() /\ LET committedTxn == packetsSync[i].notCommitted[1] IN /\ history' = [ history EXCEPT ![i] = Append(@, committedTxn)] /\ lastCommitted' = [ lastCommitted EXCEPT ![i] = [index |-> Len(history'[i]), zxid |-> committedTxn.zxid ] ] /\ lastProcessed' = [ lastProcessed EXCEPT ![i] = lastCommitted'[i] ] /\ packetsSync' = [ packetsSync EXCEPT ![i].notCommitted = Tail(@) ] \/ /\ writeToTxnLog \* packetsCommitted.add() /\ packetsSync' = [ packetsSync EXCEPT ![i].committed = Append(packetsSync[i].committed, msg.mzxid) ] /\ UNCHANGED <> /\ UNCHANGED violatedInvariants \/ /\ ~match /\ PrintT("Warn: Follower receives COMMIT," \o " but zxid not the next committed zxid in COMMIT.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE ] /\ UNCHANGED <> \/ /\ ~exist /\ PrintT("Warn: Follower receives COMMIT," \o " but no packets with its zxid exists.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE ] /\ UNCHANGED <> /\ Discard(j, i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessCOMMITInSync", i, j>>) \* Assuming that everytime committing two txns, node takes snapshot. ShouldSnapshot(i) == lastCommitted[i].index - lastSnapshot[i].index >= 2 (* There are mainly three places where calling takeSnapshot(): 1. zk.loadData() in lead() when node becomes leader; 2. syncRequestProcessor.run() tells when to snapshot; 3. node processing NEWLEADER in learner.syncWithLeader(); *) TakeSnapshot(i) == LET snapOk == lastSnapshot[i].index <= lastCommitted[i].index IN \/ /\ snapOk /\ lastSnapshot' = [ lastSnapshot EXCEPT ![i] = lastCommitted[i] ] /\ UNCHANGED violatedInvariants \/ /\ ~snapOk /\ PrintT("Exception: index of snapshot greater than" \o "index of committed.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE ] /\ UNCHANGED lastSnapshot RECURSIVE ACKInBatches(_,_) ACKInBatches(queue, packets) == IF packets = << >> THEN queue ELSE LET head == packets[1] newPackets == Tail(packets) m_ack == [ mtype |-> ACK, mzxid |-> head.zxid ] IN ACKInBatches( Append(queue, m_ack), newPackets ) (* Update currentEpoch, and logRequest every packets in packetsNotCommitted and clear it. As syncProcessor will be called in logRequest, we have to reply acks here. *) FollowerProcessNEWLEADER(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingNEWLEADER(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) packetsInSync == packetsSync[i].notCommitted m_ackld == [ mtype |-> ACKLD, mzxid |-> msg.mzxid ] ms_ack == ACKInBatches( << >>, packetsInSync ) queue_toSend == <> \o ms_ack \* send ACK-NEWLEADER first. IN /\ infoOk /\ currentEpoch' = [currentEpoch EXCEPT ![i] = acceptedEpoch[i] ] /\ history' = [history EXCEPT ![i] = @ \o packetsInSync ] /\ packetsSync' = [packetsSync EXCEPT ![i].notCommitted = << >> ] /\ connectInfo' = [connectInfo EXCEPT ![i].nlRcv = TRUE, ![i].syncMode = NONE ] /\ \/ /\ SnapshotNeeded(i) /\ TakeSnapshot(i) \/ /\ ~SnapshotNeeded(i) /\ UNCHANGED <> /\ DiscardAndSendPackets(i, j, queue_toSend) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessNEWLEADER", i, j>>) \* quorumFormed in Leader QuorumFormed(i, set) == i \in set /\ IsQuorum(set) UpdateElectionVote(i, epoch) == UpdateProposal(i, currentVote[i].proposedLeader, currentVote[i].proposedZxid, epoch) \* See startZkServer in Leader for details. StartZkServer(i) == LET latest == LastProposed(i) IN /\ lastCommitted' = [lastCommitted EXCEPT ![i] = latest] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = latest] /\ lastSnapshot' = [lastSnapshot EXCEPT ![i] = latest] /\ UpdateElectionVote(i, acceptedEpoch[i]) LeaderTurnToBroadcast(i) == /\ StartZkServer(i) /\ zabState' = [zabState EXCEPT ![i] = BROADCAST] (* Leader waits for receiving quorum of ACK whose lower bits of zxid is 0, and broadcasts UPTODATE. See waitForNewLeaderAck for details. *) LeaderProcessACKLD(i, j) == /\ IsON(i) /\ IsLeader(i) /\ PendingACKLD(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) match == ZxidEqual(msg.mzxid, <>) currentZxid == <> m_uptodate == [ mtype |-> UPTODATE, mzxid |-> currentZxid ] \* not important sid_ackldRecv == {a.sid: a \in ackldRecv[i]} IN /\ infoOk /\ \/ \* just reply UPTODATE. /\ QuorumFormed(i, sid_ackldRecv) /\ Reply(i, j, m_uptodate) /\ UNCHANGED <> \/ /\ ~QuorumFormed(i, sid_ackldRecv) /\ \/ /\ match /\ ackldRecv' = [ackldRecv EXCEPT ![i] = UpdateConnectingOrAckldRecv(@, j) ] /\ LET new_sid_ackldRecv == {a.sid: a \in ackldRecv'[i]} IN \/ \* jump out of waitForNewLeaderAck, and do startZkServer, \* setZabState, and reply UPTODATE. /\ QuorumFormed(i, new_sid_ackldRecv) /\ LeaderTurnToBroadcast(i) /\ DiscardAndBroadcastUPTODATE(i, j, m_uptodate) \/ \* still wait in waitForNewLeaderAck. /\ ~QuorumFormed(i, new_sid_ackldRecv) /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED violatedInvariants \/ /\ ~match /\ PrintT("Exception: NEWLEADER ACK is from a different epoch. ") /\ violatedInvariants' = [violatedInvariants EXCEPT !.ackInconsistent = TRUE] /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessACKLD", i, j>>) TxnsWithPreviousEpoch(i) == LET completeHis == IF ~IsFollower(i) \/ zabState[i] /= SYNCHRONIZATION THEN history[i] ELSE history[i] \o packetsSync[i].notCommitted end == Len(completeHis) first == IndexOfFirstTxnWithEpoch(completeHis, acceptedEpoch[i], 1, end) IN IF first > end THEN completeHis ELSE SubSeq(completeHis, 1, first - 1) TxnsRcvWithCurEpoch(i) == LET completeHis == IF ~IsFollower(i) \/ zabState[i] /= SYNCHRONIZATION THEN history[i] ELSE history[i] \o packetsSync[i].notCommitted end == Len(completeHis) first == IndexOfFirstTxnWithEpoch(completeHis, acceptedEpoch[i], 1, end) IN IF first > end THEN << >> ELSE SubSeq(completeHis, first, end) \* completeHis[first : end] \* Txns received in current epoch but not committed. \* See pendingTxns in FollowerZooKeeper for details. PendingTxns(i) == IF ~IsFollower(i) \/ zabState[i] /= SYNCHRONIZATION THEN SubSeq(history[i], lastCommitted[i].index + 1, Len(history[i])) ELSE LET packetsCommitted == packetsSync[i].committed completeHis == history[i] \o packetsSync[i].notCommitted IN IF Len(packetsCommitted) = 0 THEN SubSeq(completeHis, Len(history[i]) + 1, Len(completeHis)) ELSE SubSeq(completeHis, LastCommitted(i).index + 1, Len(completeHis)) CommittedTxns(i) == IF ~IsFollower(i) \/ zabState[i] /= SYNCHRONIZATION THEN SubSeq(history[i], 1, lastCommitted[i].index) ELSE LET packetsCommitted == packetsSync[i].committed completeHis == history[i] \o packetsSync[i].notCommitted IN IF Len(packetsCommitted) = 0 THEN history[i] ELSE SubSeq( completeHis, 1, LastCommitted(i).index ) \* Each zxid of packetsCommitted equals to zxid of \* corresponding txn in txns. RECURSIVE TxnsAndCommittedMatch(_,_) TxnsAndCommittedMatch(txns, packetsCommitted) == LET len1 == Len(txns) len2 == Len(packetsCommitted) IN IF len2 = 0 THEN TRUE ELSE IF len1 < len2 THEN FALSE ELSE /\ ZxidEqual(txns[len1].zxid, packetsCommitted[len2]) /\ TxnsAndCommittedMatch( SubSeq(txns, 1, len1 - 1), SubSeq(packetsCommitted, 1, len2 - 1) ) FollowerLogRequestInBatches(i, leader, ms_ack, packetsNotCommitted) == /\ history' = [history EXCEPT ![i] = @ \o packetsNotCommitted ] /\ DiscardAndSendPackets(i, leader, ms_ack) \* Since commit will call commitProcessor.commit, which will finally \* update lastProcessed, we update it here atomically. FollowerCommitInBatches(i) == LET committedTxns == CommittedTxns(i) packetsCommitted == packetsSync[i].committed match == TxnsAndCommittedMatch(committedTxns, packetsCommitted) IN \/ /\ match /\ lastCommitted' = [lastCommitted EXCEPT ![i] = LastCommitted(i)] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = lastCommitted'[i]] /\ UNCHANGED violatedInvariants \/ /\ ~match /\ PrintT("Warn: Committing zxid withou see txn. /" \o "Committing zxid != pending txn zxid.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE ] /\ UNCHANGED <> (* Follower jump out of outerLoop here, and log the stuff that came in between snapshot and uptodate, which means calling logRequest and commit to clear packetsNotCommitted and packetsCommitted. *) FollowerProcessUPTODATE(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingUPTODATE(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) packetsNotCommitted == packetsSync[i].notCommitted ms_ack == ACKInBatches(<< >>, packetsNotCommitted) IN /\ infoOk \* Here we ignore ack of UPTODATE. /\ UpdateElectionVote(i, acceptedEpoch[i]) /\ FollowerLogRequestInBatches(i, j, ms_ack, packetsNotCommitted) /\ FollowerCommitInBatches(i) /\ packetsSync' = [packetsSync EXCEPT ![i].notCommitted = << >>, ![i].committed = << >> ] /\ zabState' = [zabState EXCEPT ![i] = BROADCAST ] /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessUPTODATE", i, j>>) ----------------------------------------------------------------------------- IncZxid(s, zxid) == IF currentEpoch[s] = zxid[1] THEN <> ELSE <> (* Leader receives client propose and broadcasts PROPOSAL. See processRequest in ProposalRequestProcessor and propose in Leader for details. Since prosalProcessor.processRequest -> syncProcessor.processRequest -> ackProcessor.processRequest -> leader.processAck, we initially set txn.ackSid = {i}, assuming we have done leader.processAck. Note: In production, any server in traffic can receive requests and forward it to leader if necessary. We choose to let leader be the sole one who can receive write requests, to simplify spec and keep correctness at the same time. *) LeaderProcessRequest(i) == /\ CheckTransactionNum \* test restrictions of transaction num /\ IsON(i) /\ IsLeader(i) /\ zabState[i] = BROADCAST /\ LET inBroadcast == {s \in forwarding[i]: zabState[s] = BROADCAST} IN IsQuorum(inBroadcast) /\ LET request_value == GetRecorder("nClientRequest") \* unique value newTxn == [ zxid |-> IncZxid(i, LastProposed(i).zxid), value |-> request_value, ackSid |-> {i}, \* assume we have done leader.processAck epoch |-> acceptedEpoch[i] ] m_proposal == [ mtype |-> PROPOSAL, mzxid |-> newTxn.zxid, mdata |-> request_value ] m_proposal_for_checking == [ source |-> i, epoch |-> acceptedEpoch[i], zxid |-> newTxn.zxid, data |-> request_value ] IN /\ history' = [history EXCEPT ![i] = Append(@, newTxn) ] /\ \/ /\ ShouldSnapshot(i) /\ TakeSnapshot(i) \/ /\ ~ShouldSnapshot(i) /\ UNCHANGED <> /\ Broadcast(i, m_proposal) /\ proposalMsgsLog' = proposalMsgsLog \union {m_proposal_for_checking} /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessRequest", i>>) (* Follower processes PROPOSAL in BROADCAST. See processPacket in Follower for details. *) FollowerProcessPROPOSAL(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingPROPOSAL(i, j) /\ zabState[i] = BROADCAST /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) isNext == IsNextZxid( LastQueued(i).zxid, msg.mzxid) newTxn == [ zxid |-> msg.mzxid, value |-> msg.mdata, ackSid |-> {}, epoch |-> acceptedEpoch[i] ] m_ack == [ mtype |-> ACK, mzxid |-> msg.mzxid ] IN /\ infoOk /\ \/ /\ isNext /\ history' = [history EXCEPT ![i] = Append(@, newTxn)] /\ \/ /\ ShouldSnapshot(i) /\ TakeSnapshot(i) \/ /\ ~ShouldSnapshot(i) /\ UNCHANGED <> /\ Reply(i, j, m_ack) \/ /\ ~isNext /\ PrintT("Exception: Follower receives PROPOSAL, while" \o " the transaction is not the next.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.proposalInconsistent = TRUE] /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessPROPOSAL", i, j>>) \* See outstandingProposals in Leader OutstandingProposals(i) == IF zabState[i] /= BROADCAST THEN << >> ELSE SubSeq( history[i], lastCommitted[i].index + 1, Len(history[i]) ) LastAckIndexFromFollower(i, j) == LET set_index == {idx \in 1..Len(history[i]): j \in history[i][idx].ackSid } IN Maximum(set_index) \* See commit in Leader for details. LeaderCommit(s, follower, index, zxid) == /\ lastCommitted' = [lastCommitted EXCEPT ![s] = [ index |-> index, zxid |-> zxid ] ] /\ LET m_commit == [ mtype |-> COMMIT, mzxid |-> zxid ] IN DiscardAndBroadcast(s, follower, m_commit) \* Try to commit one operation, called by LeaderProcessAck. \* See tryToCommit in Leader for details. \* commitProcessor.commit -> processWrite -> toBeApplied.processRequest \* -> finalProcessor.processRequest, finally processTxn will be implemented \* and lastProcessed will be updated. So we update it here. LeaderTryToCommit(s, index, zxid, newTxn, follower) == LET allTxnsBeforeCommitted == lastCommitted[s].index >= index - 1 \* Only when all proposals before zxid has been committed, \* this proposal can be permitted to be committed. hasAllQuorums == IsQuorum(newTxn.ackSid) \* In order to be committed, a proposal must be accepted \* by a quorum. ordered == lastCommitted[s].index + 1 = index \* Commit proposals in order. IN \/ /\ \* Current conditions do not satisfy committing the proposal. \/ ~allTxnsBeforeCommitted \/ ~hasAllQuorums /\ Discard(follower, s) /\ UNCHANGED <> \/ /\ allTxnsBeforeCommitted /\ hasAllQuorums /\ \/ /\ ~ordered /\ PrintT("Warn: Committing zxid " \o ToString(zxid) \o " not first.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE] \/ /\ ordered /\ UNCHANGED violatedInvariants /\ LeaderCommit(s, follower, index, zxid) /\ lastProcessed' = [lastProcessed EXCEPT ![s] = [ index |-> index, zxid |-> zxid ] ] (* Leader Keeps a count of acks for a particular proposal, and try to commit the proposal. See case Leader.ACK in LearnerHandler, processRequest in AckRequestProcessor, and processAck in Leader for details. *) LeaderProcessACK(i, j) == /\ IsON(i) /\ IsLeader(i) /\ PendingACK(i, j) /\ LET msg == msgs[j][i][1] infoOk == IsMyLearner(i, j) outstanding == LastCommitted(i).index < LastProposed(i).index \* outstandingProposals not null hasCommitted == ~ZxidCompare( msg.mzxid, LastCommitted(i).zxid) \* namely, lastCommitted >= zxid index == ZxidToIndex(history[i], msg.mzxid) exist == index >= 1 /\ index <= LastProposed(i).index \* the proposal exists in history ackIndex == LastAckIndexFromFollower(i, j) monotonicallyInc == \/ ackIndex = -1 \/ ackIndex + 1 = index \* TCP makes everytime ackIndex should just increase by 1 IN /\ infoOk /\ \/ /\ exist /\ monotonicallyInc /\ LET txn == history[i][index] txnAfterAddAck == [ zxid |-> txn.zxid, value |-> txn.value, ackSid |-> txn.ackSid \union {j} , epoch |-> txn.epoch ] IN \* p.addAck(sid) /\ history' = [history EXCEPT ![i][index] = txnAfterAddAck ] /\ \/ /\ \* Note: outstanding is 0. \* / proposal has already been committed. \/ ~outstanding \/ hasCommitted /\ Discard(j, i) /\ UNCHANGED <> \/ /\ outstanding /\ ~hasCommitted /\ LeaderTryToCommit(i, index, msg.mzxid, txnAfterAddAck, j) \/ /\ \/ ~exist \/ ~monotonicallyInc /\ PrintT("Exception: No such zxid. " \o " / ackIndex doesn't inc monotonically.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.ackInconsistent = TRUE] /\ Discard(j, i) /\ UNCHANGED <> /\ UNCHANGED <> /\ UpdateRecorder(<<"LeaderProcessACK", i, j>>) (* Follower processes COMMIT in BROADCAST. See processPacket in Follower for details. *) FollowerProcessCOMMIT(i, j) == /\ IsON(i) /\ IsFollower(i) /\ PendingCOMMIT(i, j) /\ zabState[i] = BROADCAST /\ LET msg == msgs[j][i][1] infoOk == IsMyLeader(i, j) pendingTxns == PendingTxns(i) noPending == Len(pendingTxns) = 0 IN /\ infoOk /\ \/ /\ noPending /\ PrintT("Warn: Committing zxid without seeing txn.") /\ UNCHANGED <> \/ /\ ~noPending /\ LET firstElementZxid == pendingTxns[1].zxid match == ZxidEqual(firstElementZxid, msg.mzxid) IN \/ /\ ~match /\ PrintT("Exception: Committing zxid not equals" \o " next pending txn zxid.") /\ violatedInvariants' = [violatedInvariants EXCEPT !.commitInconsistent = TRUE] /\ UNCHANGED <> \/ /\ match /\ lastCommitted' = [lastCommitted EXCEPT ![i] = [ index |-> lastCommitted[i].index + 1, zxid |-> firstElementZxid ] ] /\ lastProcessed' = [lastProcessed EXCEPT ![i] = [ index |-> lastCommitted[i].index + 1, zxid |-> firstElementZxid ] ] /\ UNCHANGED violatedInvariants /\ Discard(j, i) /\ UNCHANGED <> /\ UpdateRecorder(<<"FollowerProcessCOMMIT", i, j>>) ----------------------------------------------------------------------------- (* Used to discard some messages which should not exist in network channel. This action should not be triggered. *) FilterNonexistentMessage(i) == /\ \E j \in Server \ {i}: /\ msgs[j][i] /= << >> /\ LET msg == msgs[j][i][1] IN \/ /\ IsLeader(i) /\ LET infoOk == IsMyLearner(i, j) IN \/ msg.mtype = LEADERINFO \/ msg.mtype = NEWLEADER \/ msg.mtype = UPTODATE \/ msg.mtype = PROPOSAL \/ msg.mtype = COMMIT \/ /\ ~infoOk /\ \/ msg.mtype = FOLLOWERINFO \/ msg.mtype = ACKEPOCH \/ msg.mtype = ACKLD \/ msg.mtype = ACK \/ /\ IsFollower(i) /\ LET infoOk == IsMyLeader(i, j) IN \/ msg.mtype = FOLLOWERINFO \/ msg.mtype = ACKEPOCH \/ msg.mtype = ACKLD \/ msg.mtype = ACK \/ /\ ~infoOk /\ \/ msg.mtype = LEADERINFO \/ msg.mtype = NEWLEADER \/ msg.mtype = UPTODATE \/ msg.mtype = PROPOSAL \/ msg.mtype = COMMIT \/ IsLooking(i) /\ Discard(j, i) /\ violatedInvariants' = [violatedInvariants EXCEPT !.messageIllegal = TRUE] /\ UNCHANGED <> /\ UnchangeRecorder ----------------------------------------------------------------------------- \* Defines how the variables may transition. Next == (* FLE module *) \/ \E i, j \in Server: FLEReceiveNotmsg(i, j) \/ \E i \in Server: FLENotmsgTimeout(i) \/ \E i \in Server: FLEHandleNotmsg(i) \/ \E i \in Server: FLEWaitNewNotmsg(i) (* situation errors like failure, network partition *) \/ \E i, j \in Server: PartitionStart(i, j) \/ \E i, j \in Server: PartitionRecover(i, j) \/ \E i \in Server: NodeCrash(i) \/ \E i \in Server: NodeStart(i) (* Zab module - Discovery and Synchronization part *) \/ \E i, j \in Server: ConnectAndFollowerSendFOLLOWERINFO(i, j) \/ \E i, j \in Server: LeaderProcessFOLLOWERINFO(i, j) \/ \E i, j \in Server: FollowerProcessLEADERINFO(i, j) \/ \E i, j \in Server: LeaderProcessACKEPOCH(i, j) \/ \E i, j \in Server: LeaderSyncFollower(i, j) \/ \E i, j \in Server: FollowerProcessSyncMessage(i, j) \/ \E i, j \in Server: FollowerProcessPROPOSALInSync(i, j) \/ \E i, j \in Server: FollowerProcessCOMMITInSync(i, j) \/ \E i, j \in Server: FollowerProcessNEWLEADER(i, j) \/ \E i, j \in Server: LeaderProcessACKLD(i, j) \/ \E i, j \in Server: FollowerProcessUPTODATE(i, j) (* Zab module - Broadcast part *) \/ \E i \in Server: LeaderProcessRequest(i) \/ \E i, j \in Server: FollowerProcessPROPOSAL(i, j) \/ \E i, j \in Server: LeaderProcessACK(i, j) \/ \E i, j \in Server: FollowerProcessCOMMIT(i, j) (* An action used to judge whether there are redundant messages in network *) \/ \E i \in Server: FilterNonexistentMessage(i) Spec == Init /\ [][Next]_vars ----------------------------------------------------------------------------- \* Define safety properties of Zab 1.0 protocol. CheckDuringAction == \A p \in DOMAIN violatedInvariants: violatedInvariants[p] = FALSE \* There is most one established leader for a certain epoch. Leadership1 == \A i, j \in Server: /\ IsLeader(i) /\ zabState[i] \in {SYNCHRONIZATION, BROADCAST} /\ IsLeader(j) /\ zabState[j] \in {SYNCHRONIZATION, BROADCAST} /\ acceptedEpoch[i] = acceptedEpoch[j] => i = j Leadership2 == \A epoch \in 1..MAXEPOCH: Cardinality(epochLeader[epoch]) <= 1 \* PrefixConsistency: The prefix that have been committed \* in history in any process is the same. PrefixConsistency == \A i, j \in Server: LET smaller == Minimum({lastCommitted[i].index, lastCommitted[j].index}) IN \/ smaller = 0 \/ /\ smaller > 0 /\ \A index \in 1..smaller: TxnEqual(history[i][index], history[j][index]) \* Integrity: If some follower delivers one transaction, then some primary has broadcast it. Integrity == \A i \in Server: /\ IsFollower(i) /\ lastCommitted[i].index > 0 => \A idx \in 1..lastCommitted[i].index: \E proposal \in proposalMsgsLog: LET txn_proposal == [ zxid |-> proposal.zxid, value |-> proposal.data ] IN TxnEqual(history[i][idx], txn_proposal) \* Agreement: If some follower f delivers transaction a and some follower f' delivers transaction b, \* then f' delivers a or f delivers b. Agreement == \A i, j \in Server: /\ IsFollower(i) /\ lastCommitted[i].index > 0 /\ IsFollower(j) /\ lastCommitted[j].index > 0 => \A idx1 \in 1..lastCommitted[i].index, idx2 \in 1..lastCommitted[j].index : \/ \E idx_j \in 1..lastCommitted[j].index: TxnEqual(history[j][idx_j], history[i][idx1]) \/ \E idx_i \in 1..lastCommitted[i].index: TxnEqual(history[i][idx_i], history[j][idx2]) \* Total order: If some follower delivers a before b, then any process that delivers b \* must also deliver a and deliver a before b. TotalOrder == \A i, j \in Server: LET committed1 == lastCommitted[i].index committed2 == lastCommitted[j].index IN committed1 >= 2 /\ committed2 >= 2 => \A idx_i1 \in 1..(committed1 - 1) : \A idx_i2 \in (idx_i1 + 1)..committed1 : LET logOk == \E idx \in 1..committed2 : TxnEqual(history[i][idx_i2], history[j][idx]) IN \/ ~logOk \/ /\ logOk /\ \E idx_j2 \in 1..committed2 : /\ TxnEqual(history[i][idx_i2], history[j][idx_j2]) /\ \E idx_j1 \in 1..(idx_j2 - 1): TxnEqual(history[i][idx_i1], history[j][idx_j1]) \* Local primary order: If a primary broadcasts a before it broadcasts b, then a follower that \* delivers b must also deliver a before b. LocalPrimaryOrder == LET p_set(i, e) == {p \in proposalMsgsLog: /\ p.source = i /\ p.epoch = e } txn_set(i, e) == { [ zxid |-> p.zxid, value |-> p.data ] : p \in p_set(i, e) } IN \A i \in Server: \A e \in 1..currentEpoch[i]: \/ Cardinality(txn_set(i, e)) < 2 \/ /\ Cardinality(txn_set(i, e)) >= 2 /\ \E txn1, txn2 \in txn_set(i, e): \/ TxnEqual(txn1, txn2) \/ /\ ~TxnEqual(txn1, txn2) /\ LET TxnPre == IF ZxidCompare(txn1.zxid, txn2.zxid) THEN txn2 ELSE txn1 TxnNext == IF ZxidCompare(txn1.zxid, txn2.zxid) THEN txn1 ELSE txn2 IN \A j \in Server: /\ lastCommitted[j].index >= 2 /\ \E idx \in 1..lastCommitted[j].index: TxnEqual(history[j][idx], TxnNext) => \E idx2 \in 1..lastCommitted[j].index: /\ TxnEqual(history[j][idx2], TxnNext) /\ idx2 > 1 /\ \E idx1 \in 1..(idx2 - 1): TxnEqual(history[j][idx1], TxnPre) \* Global primary order: A follower f delivers both a with epoch e and b with epoch e', and e < e', \* then f must deliver a before b. GlobalPrimaryOrder == \A i \in Server: lastCommitted[i].index >= 2 => \A idx1, idx2 \in 1..lastCommitted[i].index: \/ ~EpochPrecedeInTxn(history[i][idx1], history[i][idx2]) \/ /\ EpochPrecedeInTxn(history[i][idx1], history[i][idx2]) /\ idx1 < idx2 \* Primary integrity: If primary p broadcasts a and some follower f delivers b such that b has epoch \* smaller than epoch of p, then p must deliver b before it broadcasts a. PrimaryIntegrity == \A i, j \in Server: /\ IsLeader(i) /\ IsMyLearner(i, j) /\ IsFollower(j) /\ IsMyLeader(j, i) /\ zabState[i] = BROADCAST /\ zabState[j] = BROADCAST /\ lastCommitted[j].index >= 1 => \A idx_j \in 1..lastCommitted[j].index: \/ history[j][idx_j].zxid[1] >= currentEpoch[i] \/ /\ history[j][idx_j].zxid[1] < currentEpoch[i] /\ \E idx_i \in 1..lastCommitted[i].index: TxnEqual(history[i][idx_i], history[j][idx_j]) ============================================================================= \* Modification History \* Last modified Tue Jan 17 21:21:28 CST 2023 by huangbinyu \* Last modified Mon Nov 22 22:25:23 CST 2021 by Dell \* Created Sat Oct 23 16:05:04 CST 2021 by Dell